Mee's Notes
也許因為編輯工作的關係,書寫成為一種習慣。
highlight.js
星期一, 5月 10, 2021
星期五, 9月 11, 2020
在 linux 下複製資料夾內的所有檔案但不複製資料夾
同事問了一個問題, 他想要只把 A 資料夾下包含子資料夾內的檔案通通複製到 B 資料夾下, 但是不要在 B 資料夾下建立各層的子資料夾, 例如:
$ ls -R test
test:
a.txt t2/
test/t2:
a2.txt
若希望把 test 下的 a.txt 和 test/t2 下的 a2.txt 複製到同一個資料夾下, 例如 dd 下, 但是不要在 dd 內建立 t2 資料夾, 這只靠 cp 指令的 -r 選項是做不到的。根據估狗大神幫我找到了這一篇討論, 找到了使用 find 指令的方式如下:
$ find test -type f -exec cp "{}" dd ";"
$ ls dd
a2.txt a.txt
find 指令可以使用 -exec 選項在每次找到一個檔案時執行指定的指令, 指令中的 {} 會被取代為找到的檔案名稱, -exec 到 ";" 之間就是要執行的指令。利用這個選項, 就可以為資料夾中的每一個檔案執行 cp 指令, 複製到同一個資料夾中了。上述指令中之所以要將 {} 以及 ; 用 "" 括起來, 是為了避免被 shell 執行 shell expansion 而出錯。
星期二, 5月 26, 2020
Arduino Keyboard 程式庫 println 沒有送出 Enter 的問題
同事有一個程式模擬鍵盤輸入裝置, 使用了 Arduino 的 Keyboard 函式庫, 程式大概是這樣:
Keyboard.begin();
delay(2000);
Keyboard.press(KEY_LEFT_GUI);
delay(500);
Keyboard.press('r');
delay(500);
Keyboard.releaseAll();
Keyboard.println("NOTEPAD"); //輸入文字 NOTEPAD
delay(500);
也就是模擬按了 Win + r 開啟 Widnows 的『執行』交談窗, 然後輸入 "notepad" 開啟記事本, 但是卻發現執行後只會停在『執行』交談窗, 少了最後按下 Enter 執行所輸入指令的動作。後來使用了新版的 Arduino IDE 重新編譯執行這個程式卻又正常, 可以開啟記事本, 經過查證, 才發現 Arduino IDE 1.8.5 (含) 之前的 Keyboard 程式庫有問題, 1.8.6 就修正了。這個問題主要在於 Keyboard 是繼承自 Print 類別, Print 的 println 會使用字串版的 write 輸出字串, 程式如下:
size_t Print::write(const uint8_t *buffer, size_t size)
{
size_t n = 0;
while (size--) {
if (write(*buffer++)) n++;
else break;
}
return n;
}
在 while 迴圈中, 如果目前輸出的字元是 0, 就會當成是字串結尾, 跳離迴圈;否則會叫用字元版的 write() 輸出單一字元, 而在 Keyboard 類別中重新定義了這個字元版的 write(), 程式如下:
size_t Keyboard_::write(uint8_t c)
{
uint8_t p = press(c); // Keydown
release(c); // Keyup
return p; // just return the result of press() since release() almost always returns 1
}
這主要就是模擬按下並放開鍵盤上對應該字元按鍵的動作, 不過 press() 中會把 ASCII 碼透過 _asciimap 這個陣列轉換成對應按鍵的掃描碼, 而 '\r' (CR, 0x0D) 會對應到掃描碼 0:
const uint8_t _asciimap[128] =
{
0x00, // NUL
0x00, // SOH
0x00, // STX
0x00, // ETX
0x00, // EOT
0x00, // ENQ
0x00, // ACK
0x00, // BEL
0x2a, // BS Backspace
0x2b, // TAB Tab
0x28, // LF Enter
0x00, // VT
0x00, // FF
0x00, // CR
......
使得字元版的 write() 傳回 0, 導致字串版的 write() 認為字串結束而返回。由於 println() 在送出字串後會再送出 "\r\n", 也就是 CRLF 當成換行, 因此處理到 '\r' 就被當成字串結束而停止, 最後的 '\n' 就沒有機會送出, 因此不會模擬成按下 Enter 了。
為了修正這個問題, 從 Arduino 1.8.6 開始, Keyboard 類別就重新定義了自己的 write() 版本, 如下所示:
size_t Keyboard_::write(const uint8_t *buffer, size_t size) {
size_t n = 0;
while (size--) {
if (*buffer != '\r') {
if (write(*buffer)) {
n++;
} else {
break;
}
}
buffer++;
}
return n;
}
你可以看到當遇到 'r' 字元時, 它會當成沒看到, 繼續處理下一個字元, 這樣就可以避免上述問題了。
判斷 Arduino IDE 版本
由於同事遇到一個怪問題, 需要根據 Arduino IDE 版本使用不同的程式, 查了一下才知道, Arduino 在編譯時會定義一個巨集 ARDUINO, 格式為類似 10812, 表示 1.8.12 版, 只要利用 #if 判斷, 即可區別不同版本的 Arduino IDE 了, 例如:
#if ARDUINO<=10805
Keyboard.press(KEY_RETURN); //按下 Enter
Keyboard.release(KEY_RETURN);
#endif
就可以讓程式只有在 Arduino IDE 版本為 1.8.5(含) 之前的版本才會執行, 教新的版本就不會執行了。
星期日, 4月 12, 2020
在 WSL 1/2 執行 GUI 程式
WSL 很好用, 不過預設的情況下你只有 Terminal 跑 Shell 可以用, 如果想要測試一些圖形化界面的程式, 就要借助額外的程式, 好在已經有許多善心人士提供了免費的 X server 軟體, 可以在 Windows 環境裡執行 GUI 程式。我自己習慣使用的是 mobaXterm 軟體, 預設安裝好並不需要其他設定, 只要執行就會啟動 X server。接著, 就可以在 WSL 裡設定 X server 的位址與螢幕編號:
如果使用 WSL2, 它會像是 VirtulaBox 一樣幫你建立一個虛擬網路界面, 你可以透過 Windows 下的 ipconfig 指令看到有一個網路界面名稱裡面有一個有 WSL 字樣, 就會標示這個界面中 Windows 這一端的 IP:
172.31.80.1 就是 Windows 在這個虛擬網路中的 IP 位址。或者你也可以在 WSL2 中使用 ip route 指令查看, 就可以知道 Windows 那一端的 IP:
export DISPLAY=:0
上例中表示 X server 的位址就是 localhost, 或者 127.0.0.1, 螢幕編號為 0, 也就是第一個螢幕。設定好之後就可以執行 GUI 程式, 例如 gvim:View post on imgur.com
如果使用 WSL2, 它會像是 VirtulaBox 一樣幫你建立一個虛擬網路界面, 你可以透過 Windows 下的 ipconfig 指令看到有一個網路界面名稱裡面有一個有 WSL 字樣, 就會標示這個界面中 Windows 這一端的 IP:
D:\wsl2 ❯❯❯ ipconfig
Windows IP 設定
....
乙太網路卡 vEthernet (WSL):
連線特定 DNS 尾碼 . . . . . . . . :
連結-本機 IPv6 位址 . . . . . . . : fe80::2d93:534a:4692:bc2e%53
IPv4 位址 . . . . . . . . . . . . : 172.31.80.1
子網路遮罩 . . . . . . . . . . . .: 255.255.240.0
預設閘道 . . . . . . . . . . . . .:
172.31.80.1 就是 Windows 在這個虛擬網路中的 IP 位址。或者你也可以在 WSL2 中使用 ip route 指令查看, 就可以知道 Windows 那一端的 IP:
$ip route
default via 172.31.80.1 dev eth0
172.31.80.0/20 dev eth0 proto kernel scope link src 172.31.86.214
上述設定 X server 位址的指令就要改成:export DISPLAY=172.31.80.1:0
才能正確連到 X server。WSL2 與 VirtualBox 不相容
WSL2 因為使用了 Hyper-V 虛擬機器, 所以比透過 API 轉換層執行的 WSL1 快得多, 不過也因為這個原因, 和 VirtualBox 並不相容, 即使使用了支援 HyperV 的 VirtualBox 6.X 版也一樣, 如果你在啟用了 WSL2 的系統上使用 VirtualBox 安裝例如 Linux 作業系統, 就會遇到到一些靈異現象, 像是我自己就遇到安裝到最後不成功, 或是好像安裝成功, 但是使用時執行 git clone 老是說什麼 hashcode 不對、或者是下載 .deb 檔但要透過 dpkg 安裝卻解壓縮失敗之類的。目前若要同時使用 WSL 與 VirtualBox, 就必須改用 WSL1, 而且要確認沒有啟用『虛擬機器平台』功能:
另外, 也要確認 WSL2 要求你要安裝的 WSL2 Linux kernel update package 也要移除, 才能正常使用 VirtualBox。
View post on imgur.com
另外, 也要確認 WSL2 要求你要安裝的 WSL2 Linux kernel update package 也要移除, 才能正常使用 VirtualBox。
星期六, 4月 11, 2020
VIM 的眾多版本差異
在 Linux 上安裝 vim 如果沒有特別指名套件名稱, 例如:
因此, 建議在安裝 vim 時, 可以選擇有圖形化版本的套件, 例如:
sudo apt install vim
那麼安裝的會是 vim-common 套件, 這個套件只會安裝文字模式的 vim, 不會安裝圖形化版本的 gvim, 而沒有圖形化版本的套件, 邊一時是不會加上 +xterm_clipboard 模組的, 也就是無法讓 vim 複製資料到系統剪貼區 (clipboard), 也無法從剪貼區貼資料到 vim 中。因此, 建議在安裝 vim 時, 可以選擇有圖形化版本的套件, 例如:
- vim-gui-common:通用行的圖形化版本, 如果沒有什麼特別需求, 或是面對以下版本不知道該選那一種, 就可以安裝這個版本。
- vim-athena:採用 X Athena 圖形元件程式庫的版本, 如果你執行這個套建中的 gvim, 會注意到他的圖形界面長的很不一樣, 那就是 X Athena 程式庫。這個版本體積小一點, 如果你不介意界面長的怪怪的, 或者根本就不會執行 gvim, 那安裝這個版本根本沒差。
- vim-gtk/vim-gtk3:搭配 gtk 同行界面程式庫的版本, gvim 的界面看起來會正常許多。如果你的環境本來就會用到許多使用 gtk 建制的軟體, 那就可以安裝這一個套件。
訂閱:
文章 (Atom)