前言

本文按照作者个人使用习惯,记录自己在 Windows 11 中搭建 WSL配合XServer 的开发环境。作者基本使用过所有的Linux DE (Linux 桌面环境)Hackintosh 也使用过四年,桌面体验最佳的一直都是 Windows,个人观点。

当下的 Linux 桌面环境弊端:

  1. 界面丑,虽然自定义程度都高,主题多,但是最诟病的是风格统一问题,即便是默认主题,不协调的地方也让人难受。
  2. Wayland 极其不稳定且软件生态差,尤其是远程协助类的软件。
  3. 缺少国内毒瘤软件的支持,即便是QQ出了NT、微信出了正统Linux版本、钉钉出了半成品,但还是差太多。

本文目的

  1. DBus不正常工作的问题,以及多会话 DBus共享问题
  2. 中文输入以及 Fcitx5 自启动问题
  3. 解决WSL在网络变更时导致的X Windows消失的问题
  4. 按照我的个人习惯,配置 WSL2开发环境
  5. 记录自己遇到的并解决的坑
  6. 不是新手向的教程,需要有 WSL2的安装经验更好阅读

Windows 下的配置

更新 WSL 到最新版本

1
2
3
4
5
6
7
8
9
$ wsl --update
$ wsl --version
WSL 版本 2.1.5.0
内核版本 5.15.146.1-2
WSLg 版本 1.0.60
MSRDC 版本 1.2.5105
Direct3D 版本 1.611.1-81528511
DXCore 版本 10.0.25131.1002-220531-1700.rs-onecore-base2-hyp
Windows 版本 10.0.22631.3296

修改 .wslconfig

  • C:\Users\xxx\.wslconfig
  • 修改后需要重启 WSL
 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
[wsl2]
# 是否强制 WSL2/WSLg 子系统使用 Windows 代理设置(请根据实际需要启用)
autoProxy=false
# WSL 的 DNS 请求,由 Windows 代理转发
dnsTunneling=false
# WSL 网络流量是否经过 Windows 防火墙
firewall=true
# 启用 WSLg GUI 图形化程序支持
guiApplications=true
# 启用 IPv6 网络支持
ipv6=true                           # 启用 IPv6 网络支持
# 启用 localhost 网络转发支持
localhostForwarding=false
# 启用 WSL2/WSLg 子系统嵌套虚拟化功能支持
nestedVirtualization=true
# 启用镜像网络特性支持
networkingMode=mirrored

[experimental]
# 检测空闲 CPU 使用率后,自动释放缓存的内存
autoMemoryReclaim=gradual
# 新创建的 VHD 将自动设置为稀疏
sparseVhd=true
ignoredPorts=22
# 会允许容器通过分配给主机的 IP 地址连接到主机,或允许主机通过此方式连接到容器
hostAddressLoopback=true
# 和 dnsTunneling 配合使用,决定是否使用 Windows DNS 缓存池
useWindowsDnsCache=false

配置 Hyper-V 防火墙

  • 允许入站连接,不设置此项会导致其他机器无法访问宿主机中的WSL的端口
1
Set-NetFirewallHyperVVMSetting -Name {40E0AC32-46A5-438A-A0B2-2B479E8F2E90} -DefaultInboundAction Allow

配置 Hyper-V 动态端口范围

  • Hyper-V 会保留占用一部分端口,我要在WSL里装docker,避免端口冲突
  • 遇到过的问题就是 npm run 的随机端口提示被占用
1
2
netsh int ipv4 set dynamic tcp start=49152 num=16384
netsh int ipv6 set dynamic tcp start=49152 num=16384

安装 ArchWSL

下载 ArchWSL 的根文件系统

  • 下载链接:rootfs.tar.gz

  • 创建一个文件夹用于存放 WSL 的数据,我这里是 D:/WSL

  • 导入 Arch Linux 到 WSL

    arch-dev 是这个WSL实例的名称

    1
    2
    3
    
    $ wsl --import arch-dev D:/WSL ./rootfs.tar.gz --version 2
    正在导入这可能需要几分钟时间
    操作成功完成
    
  • 设置为默认实例 (可选)

    1
    2
    
    $ wsl --set-default arch-dev
    操作成功完成
    
  • 启动并进入 Arch

    1
    
    $ wsl -d arch-dev
    

创建 Arch Linux 的普通用户

  • 注意替换为你的用户名
1
2
3
4
5
USERNAME=rayae
echo "%wheel ALL=(ALL) ALL" > /etc/sudoers.d/wheel
useradd -m -G wheel -s /bin/bash $USERNAME
passwd $USERNAME
su - $USERNAME

配置 sudo 无需输入密码

  • 看个人喜好,我不喜欢 sudo 输入密码,只用来提权
  • 注意: 此步骤在普通用户下执行
1
2
sudo chmod +w /etc/sudoers
echo "$USER ALL=(ALL:ALL) NOPASSWD: ALL"| sudo tee -a /etc/sudoers && sudo chmod -w /etc/sudoers

修改默认用户并开启 systemd

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
echo "[user]
default=$USER

[boot]
systemd=true

[network]
generateHosts=true
generateResolvConf=false

[interop]
appendWindowsPath=false

[automount]
enabled = true
options = \"metadata\"
" | sudo tee /etc/wsl.conf

修改 DNS (否则不能上网)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
sudo unlink /etc/resolv.conf
sudo rm -f /etc/resolv.conf
sudo touch /etc/resolv.conf
sudo chmod 0666 /etc/resolv.conf
cat<<'EOF' | sudo tee /usr/local/bin/boot.sh
#!/bin/bash
/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -Command '(Get-DnsClientServerAddress -AddressFamily IPv4).ServerAddresses | ForEach-Object { "nameserver $_" }' | tr -d '\r'| tee /etc/resolv.conf > /dev/null
EOF

sudo chmod a+x /usr/local/bin/boot.sh
sudo sed -i 's|systemd=true|systemd=true\ncommand=boot.sh|' /etc/wsl.conf

重启 WSL:wsl --shutdown

解决开启 systemd 导致的 WSLg 无法使用的问题

开启 systemd 之后 $XDG_RUNTIME_DIR 会由/mnt/wslg/runtime-dir 变成 /run/user/1000

相关问题链接:https://github.com/microsoft/wslg/issues/43 https://github.com/yuk7/ArchWSL/issues/357

解决方案

  • 启动时将 X11-unix 连接到 /tmp/.X11-unix 下以解决 systemd 覆盖 tmp 目录下内容的问题

增加一个 系统级的 service 去挂载 X11 Socket

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
echo '[Unit]
Description=Bind WSLg X11 socket to /tmp

[Service]
Type=oneshot
ExecStart=mount -o bind,ro,X-mount.mkdir -t none /mnt/wslg/.X11-unix /tmp/.X11-unix

[Install]
WantedBy=default.target
' | sudo tee /etc/systemd/system/wsl-x11-socket.service

sudo systemctl daemon-reload
sudo systemctl enable --now wsl-x11-socket

增加一个用户级的 service 去挂载 Wayland Socket

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
echo '[Unit]
Description=Symlink WSLg wayland socket(user) to XDG runtime dir

[Service]
Type=oneshot
ExecStart=ln -sf /mnt/wslg/runtime-dir/wayland-0      $XDG_RUNTIME_DIR
ExecStart=ln -sf /mnt/wslg/runtime-dir/wayland-0.lock $XDG_RUNTIME_DIR

[Install]
WantedBy=default.target
' | sudo tee /etc/systemd/user/wsl-wayland-socket.service

systemctl --user daemon-reload
systemctl --user enable --now wsl-wayland-socket

使用 tmpfiles 自动链接(另一个方法)

1
2
sudo mkdir -p /etc/tmpfiles.d/
echo 'L+  /tmp/.X11-unix    -    -    -    -   /mnt/wslg/.X11-unix' | sudo tee /etc/tmpfiles.d/wslg.conf

重启 WSL

  • 注意需要在 CMD 或者 PowerShell 下执行
1
wsl --shutdown

检查基础配置是否 OK

包含如下 3 个文件,则成功

1
2
3
4
5
6
# 检查 WSLg 的
$ ls $XDG_RUNTIME_DIR/wayland* /tmp/.X11-unix
/run/user/1000//wayland-0  /run/user/1000//wayland-0.lock

/tmp/.X11-unix:
X0

配置 Arch Linux

配置 Pacman

重启之后重新进入 WSL

1. 配置 pacman 密钥

1
2
3
4
5
6
7
8
sudo pacman-key --init && sudo pacman-key --populate

# 备份原有镜像
sudo mv /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.orgi
# 使用校园网联合镜像
echo 'Server = https://mirrors.cernet.edu.cn/archlinux/$repo/os/$arch'| sudo tee /etc/pacman.d/mirrorlist
# 刷新数据库
sudo pacman -Syyu --noconfirm

2. 配置 pacman 加速镜像

1
2
3
4
5
6
7
8
# 选取 6 个速度最快的镜像
sudo pacman -S pacman-contrib --noconfirm

## 有镜像备份(mirrorlist.orgi)执行
rankmirrors -n 6 /etc/pacman.d/mirrorlist.orgi | sudo tee /etc/pacman.d/mirrorlist

# 刷新数据库
sudo pacman -Syy --noconfirm

如果你没镜像备份

1
2
3
4
5
## 无镜像备份执行
curl -s "https://archlinux.org/mirrorlist/?country=CN&protocol=https&use_mirror_status=on)" | sed -e 's/^#Server/Server/' -e '/^#/d' | rankmirrors -n 6 - | sudo tee /etc/pacman.d/mirrorlist

# 刷新数据库
sudo pacman -Syy --noconfirm

3. 配置 ArchLinuxCN 镜像源

1
2
3
4
5
echo '
[archlinuxcn]
Server = https://mirrors.cernet.edu.cn/archlinuxcn/$arch' | sudo tee -a /etc/pacman.conf
sudo pacman-key --lsign-key "farseerfc@archlinux.org"
sudo pacman -Syy archlinuxcn-keyring --noconfirm

安装基本工具

  • 注意:此处从 ArchLinuxCN 源中安装了 yay
1
sudo pacman -S --needed base-devel git curl wget vim net-tools yay openssh --noconfirm

配置 bash-completion

  • 全局生效
1
2
3
4
sudo pacman -S bash-completion --noconfirm
echo '[ -r /usr/share/bash-completion/bash_completion   ] && . /usr/share/bash-completion/bash_completion' | sudo tee -a /etc/profile

source /etc/profile

图形配置

1. 安装 XServer

我用的是 X410,你可以选其他的或者就用 WSLg

可选 X Server

  • WSLg
  • X410
  • VcXsrv
  • xming
  • XPra
  • X2Go

X410

  • 优点: 美观,在Windows中不突兀,网络变更后不会导致X窗口被关闭
  • 缺点: 收费比较高,独立版似乎免费但是会弹购买窗口,可以自行寻找 X410 Launcher,或者使用我提供的开心版

开心版下载链接:开心版

安装

  • X410 中勾选 Allow full poblic accessVsock 中的选项 (用X410的原因就是为了这个,这个选项可以避免网络变更时,导致X11 Forward的窗口断开连接,突然消失,可以自行在控制面板禁用网络适配器测试)

不与 WSLg 共存

  • 设置 .wslconfig 中的 guiApplications=false
  • 重启 WSL 后生效

与 WSLg 共存

手动设置 DISPLAY变量,加上 IP地址就会使用 XServer,不加就会使用 WSLg 需要注意的是,WSlgWaaylandXServerX11 而且要语句要加在

  • 镜像模式 使用 XServer
    1
    
    echo 'export DISPLAY=127.0.0.1:0.0' >> ~/.bashrc
    
  • 非镜像模式(NAT) 使用 XServer
    1
    
    echo 'export DISPLAY=$(cat /etc/resolv.conf |grep "nameserver" |cut -f 2 -d " "):0.0' >> ~/.bashrc
    
  • 使用 WSLg
    1
    
    echo 'export DISPLAY=:0.0' >> ~/.bashrc
    
  • 立即生效
    1
    
    source ~/.bashrc
    

2. 配置 鼠标光标大小

1
2
3
4
sudo pacman -S xorg-xrdb --noconfirm
echo "Xcursor.size: 12" >> ~/.Xresources
echo 'test -n $DISPLAY && xrdb -merge ~/.Xresources' >> ~/.bashrc
source ~/.bashrc

配置 DBus

忽略这一节

1. 检查用户 DBus 是否运行

- 注意:启用了 wsl.conf 中的 guiApplications 并才有 $DBUS_SESSION_BUS_ADDRESS 变量

1
2
3
$ stat -f $DBUS_SESSION_BUS_ADDRESS
stat: cannot read file system information for 'unix:path=/run/user/1000/bus': No such file or directory
# 可以看到 /run/user/1000/bus socket 文件不存在

2. 增加 DBus 自启动

- 在 .bashrc 中加入了 dbus 自启动,且无论是否启用 guiApplications 都会启动一个同一用户共享的 DBus 并且确保同一用户的 shell 会话共享同一个 DBus - 参考链接:https://x410.dev/cookbook/wsl/sharing-dbus-among-wsl2-consoles/

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
cat <<'EOF' >> ~/.bashrc
set_session_dbus()
{
    export XDG_RUNTIME_DIR=/run/user/$(id -u)
    export DBUS_SESSION_BUS_ADDRESS=unix:path=$XDG_RUNTIME_DIR/bus

    if [ ! -d "$XDG_RUNTIME_DIR" ]
    then
        sudo mkdir -p $XDG_RUNTIME_DIR
    fi
    sudo chmod 700 $XDG_RUNTIME_DIR
    sudo chown $(id -un):$(id -gn) $XDG_RUNTIME_DIR

    if [ ! -e "$XDG_RUNTIME_DIR/bus" -o -z "$(ps aux | grep -v grep | grep dbus-daemon)" ]
    then
        /usr/bin/dbus-daemon --session --address=$DBUS_SESSION_BUS_ADDRESS --nofork --nopidfile --syslog-only &
    fi
}
set_session_dbus

EOF

source ~/.bashrc

中文以及输入法

安装字体及语言包

1
2
3
4
5
6
# 中文字体
sudo pacman -S noto-fonts noto-fonts-cjk noto-fonts-emoji noto-fonts-extra --noconfirm

sudo sed -i 's/#zh_CN.UTF-8 UTF-8/zh_CN.UTF-8 UTF-8/' /etc/locale.gen
sudo sed -i 's/#en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
sudo locale-gen

安装 Fcitx5 输入法

1
sudo pacman -S fcitx5-im fcitx5-chinese-addons fcitx5-qt fcitx5-gtk --noconfirm

1. 配置环境变量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 配置环境变量
echo "
export GTK_IM_MODULE_DEFAULT=fcitx
export QT_IM_MODULE_DEFAULT=fcitx
export XMODIFIERS_DEFAULT=@im=fcitx
export SDL_IM_MODULE_DEFAULT=fcitx
export GTK_IM_MODULE=fcitx
export QT_IM_MODULE=fcitx
export XMODIFIERS=@im=fcitx
export DefaultIMModule=fcitx
" | sudo tee -a /etc/profile
source /etc/profile

2. 配置 Fcitx5 自启动(方式一:service 里启动)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
cat <<'EOF' | sudo tee /etc/systemd/user/fcitx5.service
[Unit]
Description=Fcitx5

[Service]
ExecStart=/usr/bin/fcitx5 --disable=wayland --verbose '*'=0
Restart=always

[Install]
WantedBy=default.target
EOF

systemctl --user enable --now fcitx5

3. 配置 Fcitx5 自启动(方式二:bashrc 里启动)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 自启动 Fcitx5 (每次启动bash都会判断 fcitx5 是否在运行)
cat <<'EOF' >> ~/.bashrc
function restart_fcitx5() {
    kill -15 $(ps aux | grep -v grep | grep fcitx | awk '{print $2}') 1>/dev/null 2>&1
    /usr/bin/fcitx5 --disable=wayland -d --verbose '*'=0
}

if [ -z "$(ps aux | grep -v grep | grep fcitx)" ]; then
    restart_fcitx5
fi

EOF

source ~/.bashrc

3. 检测 Fcitx5 是否启动

1
2
3
# 重启后检测 fcitx5 是否自动启动
$ ps aux|grep fcitx5
rayae        287  0.1  0.0  47748 25296 ?        S    01:38   0:00 /usr/bin/fcitx5 --disable=wayland -d --verbose *=0

4. 检查 DBus

忽略此节 - 检查 Fcitx5DBus 是否和我们前面创建的 DBus 一致,否则部分软件不能通信 - 最直观的就是会导致你的 fcitx5-configtool 提示 Cannot connect to Fcitx by Dbus, is Fcitx running?

1
2
3
4
5
6
$ cat /proc/`pidof fcitx5`/environ| tr '\0' '\n'|grep DBUS
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus

$ cat /proc/`pidof dbus-daemon`/environ| tr '\0' '\n'|grep DBUS
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
EOF

5. 添加拼音输入法

1
fcitx5-configtool
  • 添加一个 Group 名称为 ENG,其中只包含 English 输入法
  • 添加一个 Group 名称为 CHN,其中包含 PinyinEnglish
  • Addons 中禁用掉 Wayland
  • Global Options 中修改 Group Forward 的快捷键为 Ctrl-Shift-Space (因为X410无法响应 Super+Space)
  • 切换到拼音测试下输入效果

6. 使用某大佬的 fcitx5 的优化脚本,值得推荐

https://github.com/debuggerx01/fcitx5_customizer

1
curl -sSL https://www.debuggerx.com/fcitx5_customizer/fcitx5_customizer.sh | bash -s -- recommend

测试 VSCode 测试中文输入

1
2
3
4
$ yay -S code --noconfirm
$ code
# 测完之后就删掉,因为 Windows 下的VSCode打开 WSL 更好用
$ yay -R code --noconfirm

安装基础软件

安装 wslu

https://wslutiliti.es/wslu/zh-CN/install.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
wget https://pkg.wslutiliti.es/public.key
sudo pacman-key --add public.key
sudo pacman-key --lsign-key 2D4C887EB08424F157151C493DD50AA7E055D853

echo '
[wslutilities]
Server = https://pkg.wslutiliti.es/arch/
' | sudo tee -a /etc/pacman.conf

sudo pacman -Sy && sudo pacman -S wslu --noconfirm

注册 wslview 作为 WSL 的浏览器

1
2
3
4
5
6
echo 'export BROWSER=wslview' | sudo tee -a /etc/profile

source /etc/profile

# 测试 xdg-open
xdg-open http://www.baidu.com

安装 rofi 应用启动器

记得加上参数 -normal-window -steal-focus 否则 rofi 里不能输入任何东西(无法获得窗口焦点)

1
2
yay -S rofi
rofi -show drun -normal-window -steal-focus

创建 Windows 中的快捷方式

注意我这里的脚本会以 login-shell 执行,从而能够 source 我的 bashrc 并且 sleep 3 不能删除,否则rofi不能正常启动应用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
cat <<'EOF' | sudo tee /usr/local/bin/launch-rofi.sh
#!/bin/bash

nohup rofi -normal-window -steal-focus -run-command "/bin/bash -c -i '{cmd}'" -show drun -log /tmp/rofi.log &
sleep 3
EOF

sudo chmod a+x /usr/local/bin/launch-rofi.sh
yay -S imagemagick --noconfirm
wslusc --name Rofi --icon /usr/share/icons/hicolor/apps/rofi.svg -- /usr/local/bin/launch-rofi.sh

安装 OpenJDK

1
2
3
4
5
6
7
8
9
# jdk 17 & 8
$ sudo pacman -S jdk17-openjdk openjdk17-src openjdk17-doc jdk8-openjdk openjdk8-src openjdk8-doc --noconfirm 
# 查看已安装 JDK
$ archlinux-java status
Available Java environments:
  java-17-openjdk (default)
  java-8-openjdk
# 切换到 JDK 8
$ sudo archlinux-java set java-8-openjdk

安装 Docker

 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
32
33
34
35
sudo pacman -Sy docker docker-compose docker-buildx --noconfirm
sudo groupadd docker
sudo usermod -aG docker $USER
newgrp docker
sudo systemctl enable --now docker.service
sudo systemctl enable --now containerd.service

# 配置加速镜像 (可选)
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
	"iptables": false,
    "registry-mirrors": [
        "https://mirror.iscas.ac.cn",
        "https://docker.m.daocloud.io",
        "https://dockerproxy.com",
        "https://docker.mirrors.ustc.edu.cn",
        "https://docker.nju.edu.cn"
    ],
    "insecure-registries": [
    ]
}
EOF

# 配置 HTTP 代理 (可选)
sudo mkdir -p /etc/systemd/system/docker.service.d
echo '[Service]
Environment="HTTPS_PROXY=http://127.0.0.1:23450"
Environment="HTTPS_PROXY=http://127.0.0.1:23450"
Environment="HTTP_PROXY=http://127.0.0.1:23450"
Environment="NO_PROXY=127.0.0.1,localhost,127.0.0.0/8,10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16,198.18.0.0/15,198.19.0.0/15,169.254.0.0/16,*.aliyuncs.com,dockerproxy.com,*.daocloud.io,*.aliyun.com,*.huaweicloud.com,*.cn"
'| sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf

sudo systemctl daemon-reload
sudo systemctl restart docker containerd

完善工作

链接 Windows 的个人文件夹

1
2
3
4
5
6
7
8
# 获取当前 Windows 用户的个人文件夹
export WSL_HOME="$(wslpath $(/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command '$env:USERPROFILE')|sed -e 's/[[:space:]]*$//')"

# 软链接到 WSL 中
ln -s $WSL_HOME/Downloads $HOME/Downloads
ln -s $WSL_HOME/Documents $HOME/Documents
ln -s $WSL_HOME/Pictures $HOME/Pictures
ln -s $WSL_HOME/Desktop $HOME/Desktop

自用 bashrc 分享

功能如下:

  • 在主设备 MAIN_DEVICEblake 时,PS1 不显示 hostname
  • 当前为远程登录时,显示 hostname
  • 展示 git 分支
  • 上个命令执行失败时,展示错误吗并将用户标识置红
  • 历史命令无限制保留时间戳记录
  • 上下键搜索历史命令(类似ohmyzsh
  • 其他的自行体验

bashrc

  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
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# If not running interactively, don't do anything
[ -z "$PS1" ] && return

MAIN_DEVICE=blake
is_main_device=$(if [ $MAIN_DEVICE = $HOSTNAME ];then echo true;else echo false;fi)

prompt_command() {
    local last_ret=$?
    history -a

    # terminal tab title
    local cwd="$PWD"
    local title="${cwd/#$HOME/\~}"

    if [ $is_main_device ] ; then
        echo -n -e "\\e]0;$title\\007"
        local HOST_FLAG=""
    else
        echo -n -e "\\e]0;$HOSTNAME > $title\\007"
        local HOST_FLAG="${COLOR_YELLOW}\h${COLOR_NOCOLOR} "
    fi


    export COLOR_NOCOLOR='\[\e[0m\]'
    export COLOR_RED='\[\e[0;31m\]'
    export COLOR_GREEN='\[\e[0;32m\]'
    export COLOR_ORANGE='\[\e[0;33m\]'
    export COLOR_BLUE='\[\e[0;34m\]'
    export COLOR_PURPLE='\[\e[0;35m\]'
    export COLOR_CYAN='\[\e[0;36m\]'
    export COLOR_WHITE='\[\e[0;37m\]'
    export COLOR_YELLOW='\[\e[0;33m\]'
    export COLOR_GRAY='\[\e[0;30m\]'
    export COLOR_LIGHT_WHITE='\[\e[1;37m\]'
    export COLOR_LIGHT_GRAY='\[\e[0;37m\]'
    export COLOR_LIGHT_RED='\[\e[1;31m\]'
    export COLOR_LIGHT_GREEN='\[\e[1;32m\]'
    export COLOR_LIGHT_BLUE='\[\e[1;34m\]'
    export COLOR_LIGHT_PURPLE='\[\e[1;35m\]'
    export COLOR_LIGHT_CYAN='\[\e[1;36m\]'
    export COLOR_LIGHT_YELLOW='\[\e[1;33m\]'
    export COLOR_LIGHT_GRAY='\[\e[1;30m\]'

    # PS1
    case "$TERM" in
        xterm*|gnome*|konsole*|screen*) color_prompt=yes;;
        *) color_prompt=no;;
    esac

    if [ "$color_prompt" == no ]; then
        for name in $(env|grep ^COLOR_|cut -d= -f1);do
            unset ${name}
        done
    fi

    if [ $UID = 0 ] ; then
        local USER_FLAG="#"
    else
        local USER_FLAG="$"
    fi

    local SSH_IP=`echo $SSH_CLIENT | awk '{ print $1 }'`
    local SSH2_IP=`echo $SSH2_CLIENT | awk '{ print $1 }'`
    if [ $SSH2_IP ] || [ $SSH_IP ] ; then
        local HOST_FLAG="${COLOR_RESET}${COLOR_YELLOW}\h${COLOR_NOCOLOR} "
    fi

    if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then
      local DEBIAN_CHROOT=$(cat /etc/debian_chroot)
    fi

    local GIT_BRANCH='$(git branch 2> /dev/null | sed -e '/^[^*]/d' -e "s#* \(.*\)# ($(git config user.name) @ \1)#")'
    # last exit code
    PS1=''
    # chroot
    PS1="${PS1}${COLOR_RESET}${DEBIAN_CHROOT}"
    # show hostname if current not main device
    PS1="${PS1}${HOST_FLAG}"
    # current directory
    PS1="${PS1}${COLOR_LIGHT_GRAY}[${COLOR_LIGHT_GREEN}\w${COLOR_NOCOLOR}${COLOR_LIGHT_GRAY}]"
    # git repository info
    PS1="${PS1}${COLOR_PURPLE}${GIT_BRANCH}${COLOR_NOCOLOR}"
    # color by exit code
    if [ $last_ret == 0 ] ; then
        PS1="${PS1}${COLOR_LIGHT_WHITE} ${USER_FLAG}"
    else
        PS1="${PS1}${COLOR_LIGHT_RED} ($last_ret)${USER_FLAG}"
    fi
    # root/user flag
    PS1="${PS1}${COLOR_NOCOLOR} "

    export PS1
    export PS2='> '
    export PS3='#? '
    export PS4='+'

    for name in $(env|grep ^COLOR_|cut -d= -f1);do
        unset ${name}
    done
}

export HISTFILESIZE= # unlimited
export HISTSIZE= # unlimited
export HISTCONTROL=erasedups:ignoredups:ignorespace # Don't put duplicate lines in the history and do not add lines that start with a space
export HISTIGNORE="pwd:l:la:[bf]g:exit" # ignore such commands
export HISTTIMEFORMAT='%Y%m%d-%H:%M:%S '
export PROMPT_COMMAND='prompt_command'
export PROMPT_DIRTRIM=3


shopt -s cmdhist # combine multiline commands into single history
shopt -s histappend # append history
shopt -s checkwinsize # re-update LINEs and COLUMNs
shopt -s autocd
shopt -s extglob
shopt -s dotglob
shopt -s cdspell
shopt -s dirspell
shopt -s progcomp


set -o noclobber

stty -ixon # replace Ctrl-S for forward-search-history


export EDITOR='vim'


# Use bash-completion, if available
if [ -f /usr/share/bash-completion/bash_completion ]; then
	. /usr/share/bash-completion/bash_completion
elif [ -f /etc/bash_completion ]; then
	. /etc/bash_completion
fi


bind "set completion-ignore-case on" 2>/dev/null
bind "set bell-style none" 2>/dev/null
bind "set show-all-if-ambiguous on" 2>/dev/null
bind "set show-all-if-unmodified on" 2>/dev/null
# search history with arrow
bind '"\e[A": history-search-backward' 2>/dev/null
bind '"\e[B": history-search-forward' 2>/dev/null



# alias
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l='ls -CF'
alias la='ls -A'
alias ll='ls -alF'
alias ls='ls --color=auto'


if command -v dircolors > /dev/null 2>&1; then
    source <(dircolors)
fi

结束