Cygwin 建置

Cygwin 安裝

Cygwin 是在 Windows 中一個 POSIX 系統 (例如 Linux),將原本 POSIX 系統中的軟體重新編譯移植至 Windows。

Cygwin Installation 下載 setup-x86_64.exe,將 setup-x86_64.exe 放置於 c:\apps
Cygwin Mirror Sites 得知下載套件的網址 (要複製連結或點選才正確),如 Taiwan: ftp.ntu.edu.tw 實際網址為 https://ftp.ntu.edu.tw/pub/cygwin/

列出 setup-x86_64 的說明,可執行下列:
c:\apps\setup-x86_64 --help

setup-x86_64.exe 參數簡要說明

-B 不以系統管理員身份執行
-R 指定安裝目錄
-l 本機套件儲存目錄
-s 指定下載網址
-q 無人值守安裝模式
-P 安裝套件、-x 移除套件;多個套件以逗號 (,) 分開。

-D 只下載不安裝
-L 由本機套件目錄安裝

-M 跳過其他頁面,直接到 Select Packages

-t --allow-test-packages 安裝測試版。
注意!當現有套件安裝測試版時,在沒有指定本參數時,同套件亦會更新為測試版。

先測試本機套件儲存目錄 (c:\apps\cygwin.pkg) 及指定下載網站。
c:\apps\setup-x86_64 -D -B -l c:\apps\cygwin.pkg -s https://ftp.ntu.edu.tw/pub/cygwin/
  • 第 1 頁為版本說明。

  • 第 2 頁應該為 Download Without Installing (只下載不安裝)。

  • 第 3 頁 Local Package Directory 應該是 c:\apps\cygwin.pkg (已預設好)。

  • 第 4 頁 Use System Proxy Settings (預設值,不用改)

  • 第 5 頁 Choose A Download Site 應該是 https://ftp.ntu.edu.tw (已預設好)。

本步驟僅測試,並不需實際下載,確認參數合乎預期後,可離開安裝程式。
安裝或下載後,本機套件儲存目錄中會有 下載網址 的資料夾,如果變更 下載網址 會再建立一個,這表示;如果要重複使用,那麼 下載網址 不應變更。


再來,指定安裝目錄 (-R) 安裝至 c:\apps\cygwin

c:\apps\setup-x86_64 -B -R c:\apps\cygwin -l c:\apps\cygwin.pkg -s https://ftp.ntu.edu.tw/pub/cygwin/
  • 第 2 頁應該為 Install from Internet (下載的檔案會保留至本機套件儲存目錄)。

註:setup-x86_64.exe 選項 -L 由本機套件目錄安裝,如果沒有安裝過 Cygwin,那麼本機套件並不完整,安裝的 Cygwin 無法執行,第一次安裝不要採用該方式來安裝。

安裝或更新時,可採用無人值守安裝模式 (-q ) 及指定安裝套件 (-P) 一併安裝 openssh,ping,wget,curl,zip,unzip,nano。
註:scp ssh 的套件是 openssh。

c:\apps\setup-x86_64 -q -B -R c:\apps\cygwin -l c:\apps\cygwin.pkg -s https://ftp.ntu.edu.tw/pub/cygwin/ -P openssh,ping,wget,curl,zip,unzip,nano

Cygwin 加入 Windows 終端機設定
{
    "name" : "Cygwin",
    "commandline" : "c:/apps/cygwin/bin/bash --login -i",
    "icon" : "c:/apps/cygwin/Cygwin.ico",
    "hidden": false
},

Cygwin 版本

Cygwin 的版本可由 cygcheck -c cygwincygcheck -V 得知套件的版本,uname -r 得知 dll 的版本,「套件」跟「dll」的版本應該一致。

得知 Cygwin 的版本
執行下列得知 cygwin (套件) 的版本:
cygcheck -c cygwin

Cygwin Package Information
Package              Version        Status
cygwin               3.4.5-1        OK

執行下列得知 cygwin (套件) 的版本:
cygcheck -V

cygcheck (cygwin) 3.4.5

執行 uname 得知 (dll) 版本:
uname -r

3.4.5-1.x86_64
註:C:\apps\cygwin\bin\cygwin1.dll (/bin/cygwin1.dll) 的版號為 3.4.5

cygwin 是安裝測試版:
cygcheck -c cygwin

Cygwin Package Information
Package              Version                       Status
cygwin               3.5.0-0.140.gbf915420042e     OK (1)

執行下列得知 cygwin (套件) 的版本:
cygcheck -V

cygcheck (cygwin) 3.5.0

測試版執行 uname 得知 (dll) 版本:
uname -r

3.5.0-0.140.gbf915420042e.x86_64
註:C:\apps\cygwin\bin\cygwin1.dll 的版號為 3.5.0
1 當尾碼有長編號時,並非表示該套件一定是測試版,執行 setup-x86_64Select Packages 頁面,在 New 欄位下拉「向下按鈕」,在列表中會顯示 版號(Test),如 cygwin3.5.0-0.140.gbf915420042e(Test) 即為測試版。

更新 cygwin 核心套件時,應關閉 cygwin CLI,如果沒有可能會發生「套件」跟「dll」不一致。

「套件」跟「dll」不一致時解決方式

執行 setup-x86_64 至 Select Packages 頁面,在 Search (搜尋) 輸入 cygwin,在找出的項目 cygwincygwin-devel 按下「向下按鈕」選取 reinstall,按下 下一步 前要關閉 cygwin CLI。

Cygwin 環境說明

在 Cygwin 的根目錄 (/) 為安裝 Cygwin 的路徑如 (c:\apps\cygwin),在 Cygwin 中的 /home 即為 C:\apps\cygwin\home
~ 表示使用者主目錄,如使用者名稱為 user~ 即為 /home/user
在 Cygwin 中的 /usr/bin 比較特殊,Windows 的路徑是在 C:\apps\cygwin\bin

Cygwin 讀取外部目錄 (指 Cygwin 安裝路徑之外),可直接採用 Windows 路徑其前後加上單引號 (') 或雙引號 ("), 或者以 cygpath 轉換成 cygdrive 格式。不過,大部份的命令並不支援 cygdrive 格式,當然也有部份命令不支援雙引號只能採用 cygdrive 格式。

比較好的方式,先切換至工作目錄 (不在安裝路徑中,如 D:\MyProject),在工作目錄中以相對路徑存取子資料夾,建立的指令檔也以相對路徑來設定輸入或輸出。

cygpath 簡要說明 (cygpath --help)

命令格式:
cygpath [選項] 檔案或路徑

選項:
-a 列出絕對路徑
-w 列出 Windows 路徑 (c:\Windows)
-u (預設) 列出 Unix 路徑 (/cygdrive/c/Windows)

cygpath 轉換範例
以雙引號切換路徑:
cd "C:\Windows"

可以看到命令提示列為 Unix 路徑
user@MyPC /cygdrive/c/Windows

目前路徑轉成 Windows 路徑:
cygpath -aw .

C:\Windows

主目錄轉成 Windows 路徑:
cygpath -aw ~

C:\apps\cygwin\home\user

取得 Unix 絕對路徑:
註:-u 列出 Unix 路徑為預設值不需要指定。另外執行 cygpath 時;一般情況不會為「相對」,雖然並不需要 -a 的選項,但建議加入。
cygpath -a "c:\apps\setup-x86_64.exe"

/cygdrive/c/apps/setup-x86_64.exe
cygstart 命令 (相當於 Windows 的 start)
以 cygstart 執行 Windows 檔案總管開啟目前的資料夾:
cygstart .

啟動與 .txt 關聯的編輯器(例如記事本):
cygstart my.txt

註:檔案在目前的資料夾。

貼上 Cygwin 會顯示成反白 (Highlighted),非常不習慣改掉它。

查詢設定值:
bind -V | grep enable-bracketed-paste

enable-bracketed-paste is set to `on'

暫時取消:
bind 'set enable-bracketed-paste off'
永久解決的方案(只需設定一次):
echo "set enable-bracketed-paste off" >> ~/.inputrc

再來重啟「終端機」。


修改使用者

進入 cygwin 產生 /etc/passwd 檔案。
mkpasswd --local > /etc/passwd
編輯 /etc/passwd
開啟 c:\apps\cygwin\etc\passwd
或
nano /etc/passwd

nano 操作簡易說明
^O 儲存,^X 退出,^W 搜尋
註:^ 為按下 Ctrl 鍵。

修改前
winuser:*:197610:197121:U-W10T#winuser#,S-1-5-21-2503564773-1016231635-2146039180-1002:/home/winuser:/bin/bash
修改後
user:*:197610:197121:U-W10T#winuser#,S-1-5-21-2503564773-1016231635-2146039180-1002:/home/user:/bin/bash

如把目前 Windows 的使用者 winuser 改為 user。
有兩個,一個為帳號 (在前面的位置),一個為使用者主目錄 (在後面的位置),中間的 winuser 不要動

存檔後,將主目錄 c:\apps\cygwin\home\winuser 改名 c:\apps\cygwin\home\user。
可採用檔案總管或在 cygwin 內執行下列:

mv /home/$(whoami) /home/user

再來重啟「終端機」。


改為英文介面及修改命令提示列。

編輯 ~/.bashrc
開啟 C:\apps\cygwin\home\user\.bashrc
或
nano ~/.bashrc
修改 ~/.bashrc
# 本行為備註。前置 # 表示為備註。
export TIME_STYLE=long-iso
export LANG="en_US.UTF-8" (1)
export PS1='\n\[\e[0;32m\]cygwin2 \[\e[0;33m\]\w\[\e[0m\]\n\$ '(2)
1 改為英文介面,原始為 zh_TW.UTF-8
2 「命令提示列」預設為 使用者名稱@電腦名稱,如果有多個 Cygwin 可修改「命令提示列」,本例為 cygwin2
套用 bash 環境及配置:
source ~/.bashrc

檢查一下設定是否正確:
echo $TIME_STYLE
echo $LANG

Cygwin 的 PATH 預設會納入 Windows 的 Path 環境變數。在執行命令時,可能執行 Windows 的執行檔,造成混淆,建議將 PATH 限制在 Cygwin 的環境中。

在 Cygwin 檢查 PATH:
echo $PATH

/usr/local/bin:/usr/bin:/cygdrive/c/WINDOWS/system32: ...

PATH 是以冒號 (:) 來區分,主要路徑為前二個 /usr/local/bin/usr/bin

~/.bashrc 重新設定 PATH 及加入其他路徑 (如 ~/bin)
export PATH="/usr/local/bin:/usr/bin:~/bin"

export 同等於 Windows 中的 set,如將變數 myUp 設定為 Windwos 環境變數 %USERPROFILE%,在 Cygwin 中以 $USERPROFILE 取得。

取得 USERPROFILE 的 Unix 路徑:
cygpath $USERPROFILE

/cygdrive/c/Users/winuser

設定 myUp 變數:
export myUp=/cygdrive/c/Users/winuser

切換至 C:\Users\winuser:
cd $myUp
在 bash shell 中可直接由命令回傳 USERPROFILE
export myUp=$(cygpath $USERPROFILE)
cd $myUp
實際上以本例而言,並不需要 export
myPath=$(cygpath $USERPROFILE)
cd $myPath
那為什麼要 export 呢?
進入一個子進程:
bash

查詢 myUp 變數:
echo $myUp

回傳:
/cygdrive/c/Users/winuser

查詢 myPath 變數:
echo $myPath

並沒有回傳值,因為 myPath 為臨時變數。

PATH 要以 export 來設定,因為子進程共享環境變數。

以 alias 建立指令別名

建立 cup 別名將目前路徑切換至 %USERPROFILE%:
alias cup="cd $(cygpath $USERPROFILE)"

執行 cup:
cup

可以看到命令提示列的路徑已切換
user@MyPC /cygdrive/c/Users/winuser

Cygwin bashrc

修改 .bashrc 將安裝路徑作為命令提示列標題及設定 NODE_PATH 的路徑
cat > ~/.bashrc << "EOF"
export TIME_STYLE=long-iso
export LANG="en_US.UTF-8"
export PS1='\n\[\e[0;32m\]'$(basename "$(cygpath -aw /)")' \[\e[0;33m\]\w\[\e[0m\]\n\$'
export PATH="bin:~/bin:/usr/local/bin:/usr/bin:~/node_modules/.bin:/usr/local/npm:/cygdrive/c/apps/nodejs:/cygdrive/c/WINDOWS/system32"
export NODE_PATH="$(cygpath -aw ~/node_modules);$(cygpath -aw /usr/local/npm/node_modules)"

alias code="'$(cygpath "$USERPROFILE\AppData\Local\Programs\Microsoft VS Code\bin\code.cmd")'"(1)
EOF

# 套用 bash 環境及配置
source ~/.bashrc
1 加入 VSCode 的別名。

註:上述的 .bashrc 雖然會依據 Cygwin 安裝路徑自動設定 NODE_PATH 的路徑,但 npm 的 prefix 並未變更,若要變更執行下列。

設定 npm prefix
設定 prefix 為 /usr/local/npm (由 cygpath 轉成 Windows 路徑):
npm config set prefix "$(cygpath -aw /usr/local/npm)"

檢查結果:
npm config get prefix

Cygwin ACL

執行 setup-x86_64.exe 以參數 -B (不以系統管理員身份執行) 安裝 Cygwin。

icacls 列出 Cygwin 根資料夾權限清單
先設定暫時 PATH:
PATH=$PATH:/cygdrive/c/Windows/System32

列出 Cygwin 根資料夾權限清單:
icacls.exe $(cygpath / -aw)
正確的 ACL (Access Control List 存取控制清單)
%USERDOMAIN%\%USERNAME%:(F)
%USERDOMAIN%\None:(RX)
Everyone:(RX)
CREATOR OWNER:(OI)(CI)(IO)(F)
CREATOR GROUP:(OI)(CI)(IO)(RX)
Everyone:(OI)(CI)(IO)(RX)

剛好有三組 ACE (Access control entry 存取控制項目),上方為檔案或資料夾權限,下方為資料夾繼承權限。
檔案或資料夾權限,F:完整存取權,RX:讀取和執行存取權。

getfacl 列出 Cygwin 根資料夾權限清單
getfacl /

# file: /
# owner: $USERNAME
# group: None
user::rwx
group::r-x
other::r-x
default:user::rwx
default:group::r-x
default:other::r-x
可以看出 getfacl 跟 icacls 的對應關係。
getfacl icacls

user::rwx

%USERDOMAIN%\%USERNAME%:(F)

group::r-x

%USERDOMAIN%\None:(RX)

other::r-x

Everyone:(RX)


觀察 Cygwin 建立根資料夾之外檔案時 ACL 的情況。

在 Windows 建立一個資料夾 C:\acl-test 並觀察權限清單
mkdir c:\acl-test
icacls.exe C:\acl-test

BUILTIN\Administrators:(I)(OI)(CI)(F)
NT AUTHORITY\SYSTEM:(I)(OI)(CI)(F)
BUILTIN\Users:(I)(OI)(CI)(RX)
NT AUTHORITY\Authenticated Users:(I)(M)
NT AUTHORITY\Authenticated Users:(I)(OI)(CI)(IO)(M)

C:\acl-test 資料夾,Cygwin 的 ACL 跟 Windows 沒有對應,缺少了 group (%USERDOMAIN%\None),在該資料夾建立檔案則 ACL 會出錯 (註:寫入已建立的檔案並不會錯誤)。

在 Cygwin 內建立 C:\acl-test\acl-file 檔案:
cd "C:\acl-test"
touch acl-file (1)
1 該檔案為新檔,如果已存在應先刪除。
觀察 acl-file 權限清單,已出錯:
icacls.exe acl-file

NULL SID:(DENY)(Rc,S,WEA,X,DC)
%USERDOMAIN%\%USERNAME%:(R,W,D,WDAC,WO) (1)
%USERDOMAIN%\None:(DENY)(S,X)
NT AUTHORITY\Authenticated Users:(DENY)(S,X)
NT AUTHORITY\SYSTEM:(DENY)(S,X)
BUILTIN\Administrators:(DENY)(S,X)
BUILTIN\Users:(DENY)(S,X)
%USERDOMAIN%\None:(RX)
NT AUTHORITY\Authenticated Users:(RX,W)
NT AUTHORITY\SYSTEM:(RX,W)
BUILTIN\Administrators:(RX,W)
BUILTIN\Users:(RX)
Everyone:(R)
1 權限清單雖然不正確,但擁有者還是有完整的存取權。
R - 唯讀存取權
W - 唯寫存取權
D - 刪除存取權
WO - 寫入擁有者
檢查 acl-file 的 ACL,已出錯:
icacls.exe acl-file /verify

acl-file: Ace 項目的順序不標準。
目前的資料夾不在目標檔案,則情況不同,ACL 並不會錯誤。
切換至使用主目錄後,重建檔案 (註:沒有重建時 ACL 並不會變更):
cd ~
rm "C:\acl-test\acl-file"
touch "C:\acl-test\acl-file"

觀察權限清單:
icacls.exe "C:\acl-test\acl-file"

BUILTIN\Administrators:(I)(F)
NT AUTHORITY\SYSTEM:(I)(F)
BUILTIN\Users:(I)(RX)
NT AUTHORITY\Authenticated Users:(I)(M)

檢查檔案 ACL 是否正確:
icacls.exe "C:\acl-test\acl-file" /verify

已處理的檔案: C:\acl-test\acl-file (無錯誤)

以 getfacl 列出檔案 ACL:
getfacl "C:\acl-test\acl-file"

getfacl: C:\acl-test\acl-file: Not supported (不支援)

noacl 加入 /etc/fstab,解決 Cygwin 在根資料夾之外建立檔案造成 ACL 錯誤。
不應讓 Cygwin 造成其他資料夾的 ACL 錯誤,剛安裝 Cygwin 就應該加入 noacl

noacl 加入 /etc/fstab
cat << EOF > /etc/fstab
none /cygdrive cygdrive binary,noacl,posix=0,user 0 0
EOF
在沒有重啟所有相同路徑的終端機時, cat /proc/mounts 如下:
先檢查原狀態:
cat /proc/mounts

C:/apps/cygwin/bin /usr/bin ntfs binary,auto 1 1
C:/apps/cygwin/lib /usr/lib ntfs binary,auto 1 1
C:/apps/cygwin / ntfs binary,auto 1 1
C: /cygdrive/c ntfs binary,posix=0,user,noumount,auto 1 1
重啟後應為下列
檢查重啟後的狀態:
cat /proc/mounts

C:/apps/cygwin/bin /usr/bin ntfs binary,auto 1 1
C:/apps/cygwin/lib /usr/lib ntfs binary,auto 1 1
C:/apps/cygwin / ntfs binary,auto 1 1
C: /cygdrive/c ntfs binary,noacl,posix=0,user,noumount,auto 1 1

noacl 測試
cd "C:\acl-test"
rm acl-file
touch acl-file
icacls.exe acl-file (1)
icacls.exe acl-file /verify
getfacl acl-file (2)
1 跟資料夾權限一致 (Windows 權限繼承)
BUILTIN\Administrators:(I)(F)
NT AUTHORITY\SYSTEM:(I)(F)
BUILTIN\Users:(I)(RX)
NT AUTHORITY\Authenticated Users:(I)(M)
2 getfacl: acl-file: Not supported,「不支援」才是正確的。
noacl 回復為 normal (只是備用,如果回復則 ACL 會出錯)
cat << EOF > /etc/fstab
none /cygdrive cygdrive binary,posix=0,user 0 0
EOF

將 Cygwin 根資料夾 (以 Windows) 複製至另一個資料夾 (c:\apps\cygwin-c)

觀察權限清單:
icacls.exe  "C:\apps\cygwin-c"

BUILTIN\Administrators:(I)(OI)(CI)(F)
NT AUTHORITY\SYSTEM:(I)(OI)(CI)(F)
BUILTIN\Users:(I)(OI)(CI)(RX)
NT AUTHORITY\Authenticated Users:(I)(M)
NT AUTHORITY\Authenticated Users:(I)(OI)(CI)(IO)(M)

檢查 ACL 是否正確:
icacls.exe "C:\apps\cygwin-c" /verify /T /Q

已順利處理 0 個檔案; 0 個檔案處理失敗 (剛開始應該無錯誤)

進入 cygwin-c 建立根資料夾內的檔案時,Cygwin ACL 跟 Windows 沒有對應;已知會出錯。

建立檔案:
cd ~
touch acl-file

觀察權限清單:
/cygdrive/c/Windows/System32/icacls.exe acl-file

NULL SID:(DENY)(Rc,S,WEA,X,DC)
%USERDOMAIN%\%USERNAME%:(R,W,D,WDAC,WO)
%USERDOMAIN%\None:(DENY)(S,X)
NT AUTHORITY\Authenticated Users:(DENY)(S,X)
NT AUTHORITY\SYSTEM:(DENY)(S,X)
BUILTIN\Administrators:(DENY)(S,X)
BUILTIN\Users:(DENY)(S,X)
%USERDOMAIN%\None:(RX)
NT AUTHORITY\Authenticated Users:(RX,W)
NT AUTHORITY\SYSTEM:(RX,W)
BUILTIN\Administrators:(RX,W)
BUILTIN\Users:(RX)
Everyone:(R)

檢查 ACL 是否正確:
/cygdrive/c/Windows/System32/icacls.exe acl-file /verify

acl-file: Ace 項目的順序不標準。

修正 ACL

cygwin-c 在持續使用時,ACL 錯誤會一直增加。
若需要修正 ACL 錯誤,可執行下列修正,將 Windows 權限配合 Cygwin 的 Owner、Group、Others,並移除其他權限。

(在 Cygwin 中) 執行 ACL 修正 (可能需要十幾分鐘)
/cygdrive/c/Windows/System32/icacls.exe $(cygpath / -aw) \
/grant \
  "$USERDOMAIN\\$USERNAME:F" \
  "*S-1-3-1:RX" \(1)
  "Everyone:RX" \
  "CREATOR OWNER:(OI)(CI)(IO)F" \
  "CREATOR GROUP:(OI)(CI)(IO)RX" \
  "Everyone:(OI)(CI)(IO)RX" \
/remove \
  "NT AUTHORITY\\Authenticated Users" \
  "NT AUTHORITY\\SYSTEM" \
  "BUILTIN\\Administrators" \
  "BUILTIN\\Users" \
  "NULL SID" \
/inheritance:r /T /C /Q (2)
1 S-1-3-1 為 Creator Group ID 即為 $USERDOMAIN\NoneS-1-3-1 參閱: Security identifiers | Microsoft Learn
2 inheritance:r:移除所有繼承的 ACE,/T:目錄下所有檔案及資料夾,/C:發生錯誤時繼續執行 (仍會顯示錯誤訊息),/Q:(安靜執行) 不顯示成功訊息。
相關命令
檢查 Cygwin 根資料夾內所有檔案 ACL 是否正確:
/cygdrive/c/Windows/System32/icacls.exe $(cygpath / -aw) /verify /T /Q

Ruby

安裝 ruby 可採用 setup-x86_64 手動安裝或建立 cygpkg 按下列安裝

cygpkg install ruby

gem 安裝路徑

顯示 gem 環境,執行 gem env 如下(省略跟安裝路徑無關的訊息):
RubyGems Environment:
  - INSTALLATION DIRECTORY: /usr/share/gems
  - USER INSTALLATION DIRECTORY: /home/user/.gem/ruby/2.6.0
  - GEM PATHS:
     - /usr/share/gems
     - /home/user/.gem/ruby/2.6.0
     - /usr/local/share/gems
  - GEM CONFIGURATION:
     - "gem" => "--user-install --bindir /home/user/bin"
套件會安裝至兩個目錄
  • 一個是安裝目錄 INSTALLATION DIRECTORY: /usr/share/gems
    註:實際上還再附加 gems,如 /usr/share/gems/gems

  • 另一個是使用者安裝目錄 USER INSTALLATION DIRECTORY: /home/user/.gem/ruby/2.6.0
    註:跟上述的註解一樣,要附加 gems。

gem install 會安裝至「使用者安裝目錄」,也會在 /home/user/bin 建立指令檔。~/bin 應納入 PATH
採用 bundle 將安裝至「安裝目錄」,並不會在 ~/bin 建立指令檔。
更新套件時可能會安裝在 /usr/share/gems


gem 常用命令

找出可安裝的套件及版本:
gem search <套件REGEXP> [選項]

「套件REGEXP」表示找出前置合乎「套件名稱」的所有套件,除非加入 -e 選項 (全吻合)。
註:套件名稱不可含版號。

search 有那些可用選項:
gem search -h

-a, --all    顯示 gem 所有版本
-e, --exact  套件名稱全吻合
--prerelease 顯示預先發行版本 (註:可簡化成 --pre)
列出已安裝的套件:
gem list
顯示已安裝的套件的詳細資料:
gem list <套件REGEXP> -d

如詳細資料中顯示
Installed at: /home/user/.gem/ruby/2.6.0

實際安裝的路徑是在 /home/user/.gem/ruby/2.6.0/gems/套件含版號,如果有更新,會顯示多個版本。
找出套件指令檔路徑:
gem which <套件>

一般情況,套件的指令檔會在套件的安裝路徑之中,套件的「資料」可能在指令檔的上層。多個版本時應採用 which 來得知目前是用那一個版本。
安裝 gem-path,找出套件路徑:
安裝 gem-path 套件:
gem install gem-path

執行 gem-path 得知套件安裝路徑:
gem path <套件>

Node.js

Cygwin 的套件中並沒有 NodeJS,但 Windows 平台的 NodeJS 可在 Cygwin 內使用。
安裝 NodeJS 由 Download | Node.js 下載 Windows Binary (.zip) 64 bits 解壓至 c:\apps\nodejs

取得 Unix 路徑:
cygpath "c:\apps\nodejs"

/cygdrive/c/apps/nodejs

檢查 nodejs 下載的版本:
/cygdrive/c/apps/nodejs/npm -v
npm 安裝全局 (-global) 模組時,先瞭解 npm 的路徑關係。
先設定暫時的 PATH 變數
PATH="/usr/local/bin:/usr/bin:/cygdrive/c/apps/nodejs"

顯示 prefix 的預設值:
npm config get prefix

C:\apps\nodejs

顯示執行檔 (bin) 路徑:
npm bin -global

C:\apps\nodejs

顯示模組路徑:
npm root -g

C:\apps\nodejs\node_modules

由上述可知,在 Cygwin 中 npm 執行檔 (bin) 路徑跟 {prefix} 相同,而模組路徑是在 {prefix}/node_modules,這兩者跟 npm-bin | npm Docs 上的說明不同。

Cygwin npm-bin | npm Docs

packages

{prefix}/node_modules

{prefix}/lib/node_modules

bin

{prefix}

{prefix}/bin

設定 prefix 路徑為 /usr/local/npm,由於 NodeJS 是 Windows 平台,需要以 Windows 的路徑來設定 prefixNODE_PATH
在 Cygwin 一定要設定 NODE_PATH,命令 node 才能找到全局模組,另外;執行 node 的命令是子進程,要以 export 來設定 NODE_PATH

設定 npm prefix
設定 prefix 為 /usr/local/npm (由 cygpath 轉成 Windows 路徑):
npm config set prefix "$(cygpath -aw /usr/local/npm)"

檢查結果:
npm config get prefix

C:\apps\cygwin\usr\local\npm

顯示模組路徑:
npm root -g

C:\apps\cygwin\usr\local\npm\node_modules

顯示執行檔 (bin) 路徑:
npm bin -g

C:\apps\cygwin\usr\local\npm
npm ERR! bin (not in PATH env variable)
提示執行檔沒有納入 PATH 變數。

列出 .npmrc 的內容:
cat ~/.npmrc

prefix=C:\apps\cygwin\usr\local\npm

在指定的路徑 (locally 本地) 中安裝模組時,如使用者主目錄 (~),則會在該目錄中建立兩個檔案 package.jsonpackage-lock.json (註:全局沒有這些檔案),模組儲存在 ~/node_modules,執行檔路徑為 ~/node_modules/.binnpm rootnpm bin 剛好就是主目錄路徑,但那不重要,主要是以 NODE_PATHPATH 為準,如果只是引用函數庫並不需要將 ~/node_modules/.bin 納入 PATH

在「本地」路徑安裝模組之前,先在本地執行初始化 npm init -y,如果沒有初始化;則 npm 會自動找出上層的模組設定檔 (package.json),並將模組安裝在上層。註:主目錄 ~ 上層並不會安裝 npm 模組,可不需要初始化。
npm 維護模組時如 installuninstallupdatelist,區分「本地」及「全局」,本地安裝 (install) 時需將目前路徑切換至「本地」,本地安裝則以本地移除 (uninstall),本地移除時並不會移除全局。

node 在執行 .js 時,會先找出該檔案路徑或 (所有的) 上層路徑是否有 node_modules 子目錄,如果有則會載入 node_modules 內的模組 (不需要設定 NODE_PATH)。
.js 檔案在不同的路徑中則要設定 NODE_PATH (該設定值有順序,本地路徑要在全局之前)。

何種情況,需要在本地路徑中安裝模組?
如安裝舊版的模組時,最好不要以全局安裝,全局模組應為最新的模組,舊版的模組應該安裝在本地,避免全局模組在安裝或更新時,自動更新。

~/.bashrc 如下新增(或修改)
export PATH="/usr/local/bin:/usr/bin:~/node_modules/.bin:/usr/local/npm:/cygdrive/c/apps/nodejs
export NODE_PATH="C:\apps\cygwin\home\user\node_modules;C:\apps\cygwin\usr\local\npm\node_modules"
npm 常用命令

npm install <模組>[@指定版本] [-g]

安裝模組。

npm uninstall <模組> [-g]

移除模組。

npm list [指定模組] [-g]

顯示已安裝的模組。

npm update <模組> [-g]

更新模組及相依模組至最新版本。

npm view <模組> version

檢查可安裝模組的 (最新) 版本。

npm view <模組> versions

檢查可安裝模組的所有版本。

npm view <模組>

查詢可安裝的模組的資訊,如版本、相依模組等 (JSON)。

npm search <模組>

搜尋可安裝模組。

npm config list

列出組態。

npm config ls -l

列出所有組態。

相關網頁:
npm Docs

cygpkg 套件安裝移除及更新

在 cygwin 主控台 (CLI) 中執行套件安裝移除及更新,交由 setup-x86_64.exe 來維護, 下載套件時於指令碼指定下載網站及指定儲存於本機 (共用) 套件目錄。

cygpkg (進入 cygwin,貼上下列指令碼)
bind "set disable-completion on"

bash -c 'cat > /usr/local/bin/cygpkg' << "EOF"
#!/bin/bash

usage="Usage: ${0##*/} operation [package(s)]

OPERATIONS:
  i, install	Install package(s).
  r, remove	Remove package(s).
  u, update	Update package(s), display the installer, and update manually.
  f, fix	Update packages to the current release version."

ExecuteFile='c:\apps\setup-x86_64.exe'(1)
params='-l "c:\apps\cygwin.pkg" -s https://ftp.ntu.edu.tw/pub/cygwin/'(2)

UpdatePkgs=0
case "$1" in
  i|install)
    params="${params} -q -P";
    shift;
    pkgs=$@;;
  r|remove)
    params="${params} -q -x";
    shift;
    pkgs=$@;;
  u|update)
    UpdatePkgs=1;
    params="${params} -M";
    shift;
    pkgs=$@;;
  f|fix)
    UpdatePkgs=1;
    params="${params} -f -M";
    pkgs='';;
  -h|--h*) echo "$usage";  exit 0;;
  -*|--*) echo "unknown option: $1" >&2; exit 1;;
  *) echo "$usage";  exit 0;;
esac

pkgs=${pkgs//,/ }
pkgs="$(echo $pkgs)"
pkgs=${pkgs// /,}

if [ $UpdatePkgs -ge 1 ]; then
  if [[ -n "$pkgs" ]]; then
    pkgs="-P ${pkgs}"
  fi
elif [[ -z "$pkgs" ]]; then
  echo "$usage"
  exit 1
fi

if [ -f /var/log/setup.log ]; then
  logsize=$(stat -c%s /var/log/setup.log)
  let MaxSize=10*1024*1024
  if [ $logsize -gt $MaxSize ]; then
    echo "The /var/log/setup.log ("$(numfmt --to=iec $logsize)") is larger than 10M, it seems abnormal,"
    read -p "Do you want to delete it? (Y/N): " yn
    if [ "${yn}" != "Y" ] && [ "${yn}" != "y" ]; then
      exit 1
    fi
    set -e
    rm /var/log/setup.log
    touch /var/log/setup.log
    set +e
  fi
fi

show_size() {
  if [ -f /var/log/setup.log ]; then
    logsize=$(stat -c%s /var/log/setup.log)
  else
    logsize=0
  fi
  echo -e "\nThe size of /var/log/setup.log is "$(numfmt --to=iec $logsize)"."
  exit
}

if [[ -n "$pkgs" ]]; then
  cmdline="'$(cygpath -a "$ExecuteFile")' -B -d -n -N --no-write-registry -R \"$(cygpath -aw /)\" ${params} ${pkgs}"
  echo $cmdline
  trap show_size INT
  echo -e "\033[1;33mNotice! When specifying a package, do not select Cygwin core in the installer to update.\033[0m"
  eval $cmdline
  show_size
else
  cmdline="cygstart CMD '/K (\"$ExecuteFile\" -B -d -n -N --no-write-registry -R \""$(cygpath / -aw)"\" ${params})'"
  echo $cmdline
  echo -e "\033[1;33mWhen updating the Cygwin core, you must exit the Cygwin CLI.\033[0m"
  eval $cmdline
fi
EOF

bind "set disable-completion off"
chmod +x /usr/local/bin/cygpkg
1 setup-x86_64.exe 的全路徑。
2 本機套件儲存目錄及指定下載網站,注意 -l "c:\apps\cygwin.pkg" 要雙引號。
cygpkg 用法
安裝套件:
cygpkg install zip,unzip,nano

或以空白區分套件:
cygpkg install zip unzip nano

移除套件:
cygpkg remove zip,unzip,nano

顯示套件更新(未指定套件,以 cmd 進程執行):
cygpkg update

套件更新 (或安裝指定版本):
cygpkg update gcc-g++=7.4.0-1

在執行 Cygwin CLI 時,執行 cygpkg (setup-x86_64.exe) 更新 Cygwin core (核心),更新時會出現下列訊息:
io_stream_cygfile: fopen(/usr/bin/cygwin1.dll) failed 13 Permission denied
Failed to open cygfile:///usr/bin/cygwin1.dll for writing.
running: c:\apps\cygwin\bin\kill.exe -TERM -W 10124

[處理結束,代碼為 4294967295 (0xffffffff)]

因為更新 Cygwin core 會終止 Cygwin CLI 進程,為正常現像。但有時不會執行「終止」,則可能會造成 /bin/cygwin1.dll 的版本不一致,在更新核心時,應退出 Cygwin CLI。若版本已不一致,解決方式可參閱 Cygwin 版本
有指定套件時,如 cygpkg u nano 不應選取 Cygwin core,在指定套件的情況下 cygpkg 並未以 cmd 進程來執行,執行更新後 Cygwin 將會崩溃。

進入 Cygwin 如下錯誤:
bash: /usr/bin/locale: No such file or directory
bash: /usr/bin/tzset: No such file or directory
執行下列修正錯誤:
cygpkg f

JavaScript 套件

JSHint

JSHint 檢查 JavaScript 錯誤,如以建議 === 或 !== 以避免轉型、變數沒有以 var 宣告、陳述式結尾沒有加分號。 剛學 JavaScript,範例以瀏覽器測試是正確,可是採用 JSHint 檢查後才發覺函數不存在 …​。

把 JavaScript 貼上 JSHint 可即時檢查出錯誤。

安裝 JSHint
npm install -global jshint
cd $docs
jshint $InputFile

如出現 Something is available in ES6,表示 JavaScript 使用了 ECMAScript 6,那它會不會太新?
ECMAScript - 維基百科,自由的百科全書

jshint 是否支援 ES7
jshint -v
# jshint v2.12.0 (1)
1 JSHint Blog 只會在某個版本說明, New Release: 2.10.0 : support for the three most recent editions of JavaScript: ES7, ES8, and ES9.

那如何設定讓 JSHint 檢查的 ES 版本;可參考 JSHint Documentation ,實測順序如下。 另外選項可參考 JSHint Options Reference

設置 JSHint 選項的順序
  1. .js 加入 /* jshint esversion:5, maxdepth:1 */

  2. --config 參數指定設定檔

  3. .jshintrc 設定檔 (跟 .js 相同的目錄),可按照 jshint/.jshintrc at master · jshint/jshint · GitHub 來設定。

修正出現 'jQuery' is not defined.
{
  // https://github.com/jshint/jshint/blob/master/examples/.jshintrc
  ...
  "yui": false, // Yahoo User Interface

  // Custom Globals
  "globals": {
    "jQuery": true // 加入 jQuery
  } // additional predefined global variables
}

陳述式結尾沒有加分號時採用下列自動修正

fixJsFile
cd $docs
cat > bin/fixJsFile << "EOF"
#!/bin/sh
for i in `jshint $1 | grep -i "Missing semicolon" \
| sed -e 's/\([^0-9]*\)\([0-9]*\)\(.*$\)/\2/'`;
do
  echo 修復檔案:$1 行號: $i
  sed -i $1 -e $i's/\(\s*\)$/;/'
  wait
done
EOF

fixJsFile $InputOutputFile

terser

GitHub - terser/terser: JavaScript parser, mangler and compressor toolkit for ES6+
JavaScript 壓縮,程式碼縮小化 (變數名稱縮減、去掉註解、空白字元、換行字元 …​),去掉 console.log …​

安裝
npm install -global terser

terser --version
# terser 5.3.4

# 測試,結果顯示主控台
terser $InputFile -c drop_console,passes=2 -m (1)
1 參考官方網頁:-c drop_console 去掉 console.log,passes=2 執行 2 次壓縮,-m 變數名稱縮減。