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 变量名称缩减。