ubuntu 免密操作

sudo免密

1
2
3
4
sudo visudo

# 文件末尾添加
YOUR_USERNAME ALL=(ALL) NOPASSWD: ALL

pkexec免密

要取消 pkexec 命令在 Linux 系统中的密码输入限制(实现免密执行特权命令),需要配置 PolicyKit 规则。这通常通过在 /etc/polkit-1/rules.d/ 目录中创建一个 .rules 文件来实现,允许特定用户免密执行特定命令

1
2
3
4
5
6
7
8
9
10
sudo nano /etc/polkit-1/rules.d/nopasswd.rules

# 添加如下代码
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.policykit.exec" && subject.user == "YOUR_USERNAME") {
return polkit.Result.YES;
}
});


Ubuntu 25 下 Flameshot 快捷键截图失败解决方案

问题背景

我在 Ubuntu 25 上想实现这件事:

  1. 截图后自动保存到文件
  2. 同时复制到剪贴板,方便直接粘贴

命令行手动执行 flameshot gui -p ~/Pictures/Screenshots -c 时可用,但绑定快捷键后执行却报错:

Flameshot Error: Unable to capture screen

另外,Ubuntu 25 默认 Wayland,会话里基本不再走 X11 方案

环境

  • 系统:Ubuntu 25
  • 显示会话:Wayland
  • 工具:Flameshot 12.1.0

结论

核心不是快捷键本身,而是 Wayland 截图通道(portal)和启动方式。

最终稳定方案:

  • 不使用 QT_QPA_PLATFORM=xcb
  • 使用脚本封装 Flameshot 命令
  • 快捷键绑定脚本路径
  • 重启 xdg-desktop-portal 相关服务

解决步骤

1. 安装/确认依赖

1
sudo apt install -y flameshot xdg-desktop-portal xdg-desktop-portal-gnome

2. 重启 portal 服务

1
2
pkill -x flameshot || true
systemctl --user restart xdg-desktop-portal xdg-desktop-portal-gnome

3. 创建截图脚本(Wayland)

1
2
3
4
5
6
mkdir -p ~/Pictures/Screenshots ~/bin
printf '%s\n' \
'#!/usr/bin/env bash' \
'/usr/bin/flameshot gui -p "$HOME/Pictures/Screenshots" -c' \
> ~/bin/flameshot-shot.sh
chmod +x ~/bin/flameshot-shot.sh

4. 先在终端验证脚本

1
~/bin/flameshot-shot.sh

如果这一步能正常截图,说明后端链路已通。

5. 绑定快捷键

设置 -> 键盘 -> 自定义快捷键 新增:

  • 名称:Flameshot
  • 命令:/home/zjz/bin/flameshot-shot.sh
  • 快捷键:Ctrl+Shift+Alt+S

通过网线传输数据

今天需要将本地电脑A的虚拟机环境配置到电脑B上边,但是虚拟机很大,需要200G,在没有合适的硬盘下,使用网线进行传输

  1. 关闭电脑A和电脑B的防火墙

  2. 使用网线连接电脑A和电脑B(电脑B的WiFi关掉,只保留以太网)

  3. 配置共享文件夹

    1. 电脑C盘或者其它盘->属性->共享->高级共享->勾选共享此文件夹->点击权限->选择完全控制

      image-20260205163457476

    2. 配置网线传输速率,电脑B打开控制面板->网络与共享中心->更改适配器设置->双击以太网适配器->点击属性->点配置->点击高级->找到 连接速度和双工模式->配置为1G。注意,电脑B不要连接WiFi,否则速率可能会慢

      image-20260205163912560

    3. 然后电脑A也要配置,只不过电脑A选择 自动检测

  4. 传输速率配置完以后,我们还需要配置IP地址

    1. 电脑A 打开控制面板->网络与共享中心->更改适配器设置->双击以太网适配器->点击属性->Internet 协议版本 4 (TCP/IPv4),然后进行如下配置

      image-20260205164225233

      只需要配置IP地址和子网掩码,其它不需要,留空就行,这个主要是为了配置同一网段,最后保存即可

    2. 电脑B也需要配置IP地址,地址为192.168.10.2,子网掩码也是255.255.255.0,然后其它配置不变,保存即可

    3. 看一下两边能不能互相ping通,能ping通就下一步,ping不通就关闭防火墙

  5. 传输配置,为了以防万一,电脑A和电脑B都进行如下配置

    1. 打开控制面板->网络与共享中心->更改高级共享设置->打开公用文件夹共享和关闭密码保护共享

      image-20260205164828108

​ 如果电脑是win10,也差不多,只是名字不太一样,看着勾选就行

  1. 然后就可以查找电脑B了,电脑B之前我们已经共享了C盘或其它盘

    1. 电脑A 输入快捷键 win+r,输入 \\192.168.10.2

    2. 如果还是需要输入密码

      1. 用户名:192.168.10.2\用户名
      2. 密码:电脑B的登录密码
    3. 输入以后,资源管理中心的网络出现这样就表示成功了

      image-20260205170542613

    4. 然后将电脑A需要传输到电脑B的数据拖拽或者复制就对应的电脑B位置就好了

最后,如果是克隆虚拟机到新电脑,需要确保vmware版本相同,然后拷贝下面三个文件到新电脑即可

image-20260205170542613

ctypes封装C++接口给Python使用

ctypes封装C++接口给Python使用

一般步骤

  1. 设计 C API 边界,新增 *.h/*.cpp,用 extern "C" 暴露纯 C 接口。
  2. C API 只暴露基础类型与不透明句柄,避免直接暴露 C++ 类型。
  3. 约定错误处理,统一返回错误码,必要时提供 get_last_error
  4. 约定内存管理,C 层分配的内存由 C 层提供 free 释放函数。
  5. 生成动态库,Linux/macOS 需要 -fPIC-shared
  6. Python 侧用 ctypes 加载库,并为每个函数配置 argtypes/restype

最小示例
目标:封装一个 C++ Calculator,支持 add、返回字符串、回调通知。

文件 1:demo_c_api.h

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
#ifndef DEMO_C_API_H
#define DEMO_C_API_H

#ifdef __cplusplus
extern "C" {
#endif

#ifdef _WIN32
#ifdef DEMO_C_EXPORTS
#define DEMO_C_API __declspec(dllexport)
#else
#define DEMO_C_API __declspec(dllimport)
#endif
#else
#define DEMO_C_API __attribute__((visibility("default")))
#endif

typedef void* DemoHandle;
typedef void (*DemoMessageCallback)(const char* message, void* user_data);

DEMO_C_API DemoHandle demo_create(void);
DEMO_C_API void demo_destroy(DemoHandle handle);
DEMO_C_API int demo_add(DemoHandle handle, int a, int b);
DEMO_C_API char* demo_get_name(DemoHandle handle);
DEMO_C_API void demo_free_string(char* str);
DEMO_C_API void demo_set_callback(
DemoHandle handle,
DemoMessageCallback callback,
void* user_data
);

#ifdef __cplusplus
}
#endif

#endif

文件 2:demo_c_api.cpp

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
#include "demo_c_api.h"

#include <cstdlib>
#include <cstring>
#include <string>

class Calculator {
public:
int add(int a, int b) {
return a + b;
}
};

static char* copy_string(const std::string& text) {
char* result = static_cast<char*>(std::malloc(text.size() + 1));
if (result) {
std::memcpy(result, text.c_str(), text.size() + 1);
}
return result;
}

struct DemoWrapper {
Calculator impl;
DemoMessageCallback callback = nullptr;
void* user_data = nullptr;
};

DemoHandle demo_create(void) {
return new DemoWrapper();
}

void demo_destroy(DemoHandle handle) {
if (!handle) {
return;
}
auto* wrapper = static_cast<DemoWrapper*>(handle);
delete wrapper;
}

int demo_add(DemoHandle handle, int a, int b) {
if (!handle) {
return 0;
}
auto* wrapper = static_cast<DemoWrapper*>(handle);
int result = wrapper->impl.add(a, b);
if (wrapper->callback) {
std::string message = "add called";
wrapper->callback(message.c_str(), wrapper->user_data);
}
return result;
}

char* demo_get_name(DemoHandle handle) {
if (!handle) {
return nullptr;
}
return copy_string("Calculator");
}

void demo_free_string(char* str) {
if (str) {
std::free(str);
}
}

void demo_set_callback(
DemoHandle handle,
DemoMessageCallback callback,
void* user_data
) {
if (!handle) {
return;
}
auto* wrapper = static_cast<DemoWrapper*>(handle);
wrapper->callback = callback;
wrapper->user_data = user_data;
}

编译动态库(Linux 示例)

1
g++ -std=c++17 -fPIC -shared demo_c_api.cpp -o libdemo.so

文件 3:demo.py(直接 ctypes 调用 + 再封装一层,仅提供接口)

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
import ctypes
from pathlib import Path

_CALLBACK = ctypes.CFUNCTYPE(None, ctypes.c_char_p, ctypes.c_void_p)

class Calculator:
def __init__(self):
lib_path = (Path(__file__).parent / "libdemo.so").resolve()
self._lib = ctypes.CDLL(str(lib_path))
self._lib.demo_create.argtypes = []
self._lib.demo_create.restype = ctypes.c_void_p
self._lib.demo_destroy.argtypes = [ctypes.c_void_p]
self._lib.demo_destroy.restype = None
self._lib.demo_add.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int]
self._lib.demo_add.restype = ctypes.c_int
self._lib.demo_get_name.argtypes = [ctypes.c_void_p]
self._lib.demo_get_name.restype = ctypes.c_void_p
self._lib.demo_free_string.argtypes = [ctypes.c_void_p]
self._lib.demo_free_string.restype = None
self._lib.demo_set_callback.argtypes = [
ctypes.c_void_p,
_CALLBACK,
ctypes.c_void_p,
]
self._lib.demo_set_callback.restype = None
self._handle = self._lib.demo_create()
self._callback = None

def add(self, a: int, b: int) -> int:
if not self._handle:
raise RuntimeError("Calculator 未初始化")
return int(self._lib.demo_add(self._handle, a, b))

def get_name(self) -> str:
if not self._handle:
raise RuntimeError("Calculator 未初始化")
ptr = self._lib.demo_get_name(self._handle)
if not ptr:
return ""
raw = ctypes.cast(ptr, ctypes.c_char_p).value or b""
self._lib.demo_free_string(ptr)
return raw.decode("utf-8", errors="ignore")

def set_callback(self, callback) -> None:
if not self._handle:
raise RuntimeError("Calculator 未初始化")
if callback is None:
raise RuntimeError("回调不能为空")

def _bridge(message_ptr, user_data):
text = message_ptr.decode("utf-8", errors="ignore") if message_ptr else ""
callback(text)

self._callback = _CALLBACK(_bridge)
self._lib.demo_set_callback(self._handle, self._callback, None)

def close(self) -> None:
if self._handle:
self._lib.demo_destroy(self._handle)
self._handle = None
self._callback = None

文件 4:test.py(使用 demo.py 提供的接口)

1
2
3
4
5
6
7
8
9
10
from demo import Calculator

def on_message(text: str) -> None:
print("callback:", text)

calc = Calculator()
print("name:", calc.get_name())
calc.set_callback(on_message)
print("1 + 2 =", calc.add(1, 2))
calc.close()

路由器桥接网络

一、交换机

  • 工作层级:数据链路层(OSI第二层)
  • 核心功能:基于MAC地址在同一网络内转发数据帧
  • 特点
    • 用于扩展网络端口数量,实现多设备有线连接
    • 所有端口平等,不支持网络地址转换
    • 即插即用,无需配置即可工作
    • 不分配IP地址,不管理网络流量
  • 可以简单理解为做数据转发操作

二、路由器

  • 工作层级:网络层(OSI第三层)
  • 核心功能:基于IP地址在不同网络间路由数据包
  • 特点
    • 连接内网与互联网,是网络的出口网关
    • 执行NAT(网络地址转换),将内网IP转换为公网IP
    • 提供DHCP服务,为内网设备自动分配IP地址
    • 内置防火墙,提供基本网络安全防护
    • 通常集成WiFi功能(无线路由器)
  • 像小区的邮政局,负责处理对内和对外的信件收发

三、路由器接入

WiFi:现代路由器通常集成WiFi功能,但路由器本身的核心是路由和NAT,路由器能够提供网络服务,必须通过WAN口连接上行网络(如光猫、入户网线)

特性 网线接入 无线接入(WiFi)
速度 通常更快 受环境影响
稳定性 极高,不受干扰 受距离、障碍物、干扰影响
延迟 极低 较高
安全性 物理隔离更安全 需加密协议保护
适用场景 台式机、服务器、游戏主机 手机、笔记本、物联网设备

四、网络扩展

4.1、路由器桥接

当主路由器信号无法覆盖全部区域时,可通过桥接扩展网络

4.1.1、有线桥接(推荐)

配置方式:路由器B通过网线连接路由器A的LAN口
工作模式:将路由器B设置为AP模式(接入点模式)
优点

  • 性能无损,稳定性极高
  • 设备在同一局域网,互访方便

配置步骤

  1. 用网线连接路由器A的LAN口和路由器B的WAN口(或LAN口,需关闭DHCP)
  2. 登录路由器B管理界面,关闭DHCP功能
  3. 设置路由器B的LAN口IP为与路由器A同网段的不同地址
  4. 设置路由器B的WiFi名称和密码(可与路由器A相同或不同)
4.1.2、 无线桥接(中继模式)

配置方式:路由器B无线连接路由器A的信号
工作模式:WDS桥接或无线中继模式
优点:无需布线,灵活方便
缺点

  • 带宽减半(需接收和转发)
  • 稳定性受无线环境影响
  • 不同品牌兼容性可能有问题

配置步骤

  1. 将路由器B放在能稳定接收路由器A信号的位置
  2. 登录路由器B管理界面,开启WDS/中继功能
  3. 扫描并选择路由器A的WiFi信号,输入密码
  4. 设置路由器B的WiFi信息

4.2、电脑桥接

4.2.1、电脑网络共享

当台式机无法连接WiFi且无法连接主路由器时,可通过另一台已联网的电脑共享网络。

适用场景

  • 临时网络扩展,无多余路由器
  • 网络故障应急方案
  • 台式机无WiFi功能且无法布线

配置步骤

  1. 物理连接:用网线连接电脑A和电脑B的网口

  2. 电脑A设置(已连接WiFi的电脑):

    • 控制面板 → 网络和共享中心 → 更改适配器设置
    • 右键点击WiFi适配器 → 属性 → 共享选项卡
    • 勾选”允许其他网络用户通过此计算机的Internet连接来连接”
    • 选择连接电脑B的以太网适配器作为专用网络连接
    • 点击确定

    image-20260201221104233

  3. 电脑B设置

    • 确保以太网适配器设置为”自动获取IP地址”
    • 稍等片刻即可上网
  4. 网络变化

    • 电脑A的以太网适配器IP变为:192.168.137.1
    • 电脑B自动获取IP为:192.168.137.x
    • 电脑A充当临时路由器和DHCP服务器
  5. 注意事项

    • 电脑A需保持开机状态,电脑B才能上网
    • 共享后电脑A的防火墙可能阻止某些连接,需相应调整
    • Windows ICS默认不响应ping请求,这是正常安全设置

五、局域网通信原理

两个设备要直接通信,必须满足:

  1. 同一IP网段:IP地址的前三部分相同(如192.168.1.x)
  2. 同一子网掩码:通常为255.255.255.0
  3. 同一广播域:连接在同一交换机或路由器的同一VLAN内
1
2
3
4
5
6
[路由器] (IP: 192.168.1.1,DHCP服务器)
├──[交换机] (扩展更多端口)
│ ├──[电脑A] (自动获取IP: 192.168.1.100)
│ └──[电脑B] (自动获取IP: 192.168.1.101)
├──[手机] (通过WiFi连接,IP: 192.168.1.102)
└──[智能电视] (有线连接,IP: 192.168.1.103)

所有设备均在192.168.1.0/24网段,可以互相访问

现在有这么一个场景,电脑a要向电脑b的服务发送数据或请求,我们只有在同一个局域网且同一个网段才能发送数据,可以让电脑a和电脑b使用网线连接到同一个路由器或交换机上,这样就能处在同一个局域网,一个网段内了

六、虚拟机额外补充

虚拟机如VMware里边的网络桥接和我们上边的桥接不同,VMware中的网络模式一般有以下几种

  1. 桥接模式
  2. NAT模式
  3. 主机模式

在电脑A中运行虚拟机时,网络模式决定局域网其他设备能否访问虚拟机服务:

模式 虚拟机IP获取 电脑B能否访问虚拟机 适用场景
桥接模式 从主路由器获取,与电脑A同网段 可以直接访问 虚拟机需对外提供服务
NAT模式 从电脑A的虚拟网络获取私有IP 默认不能访问(需端口转发) 虚拟机仅需上网,不需被访问
仅主机模式 与电脑A的专用虚拟网络 不能访问 完全隔离的网络测试

ubuntu GNOME插件推荐

安装GNOME 插件管理工具

1
sudo apt install gnome-shell-extension-manager

GNOME插件推荐

  1. Caffeine : 点击图标即可控制电脑不休眠

  2. Clipboard Indicator : 剪切板工具

  3. Compiz alike magic lamp effect : 最小化动画效果

  4. Dash to Dock : dock 栏工具

    • 让 Ubuntu Dock 支持点击最小化/恢复
    1
    gsettings set org.gnome.shell.extensions.dash-to-dock click-action 'minimize'
    • 回复原状
    1
    gsettings reset org.gnome.shell.extensions.dash-to-dock click-action
  5. Blur my shell 美化工具

Tweaks 工具

支持设置终端剧中显示,双击窗口放大或缩小

1
sudo apt install gnome-tweaks

ubuntu 安装中文输入法

方案一

安装中文语言包

1
2
sudo apt update
sudo apt install language-pack-zh-hans

安装 Fcitx5 输入法框架

1
sudo apt install fcitx5 fcitx5-chinese-addons fcitx5-config-qt fcitx5-configtool

手动启动:

1
fcitx5-configtool

手动创建 fcitx5 自启动文件

1
sudo nano /etc/xdg/autostart/fcitx5.desktop

写入

1
2
3
4
5
6
7
8
[Desktop Entry]
Type=Application
Exec=fcitx5
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
Name=Fcitx 5
Comment=Start Fcitx 5 input method framework

方案二

  1. 安装 IBus 和拼音引擎
1
2
3
4
sudo apt update
sudo apt install -y ibus ibus-libpinyin
im-config -n ibus
ibus restart
  1. 设置 -> 键盘 -> 输入源 -> + -> Chinese -> Intelligent Pinyin (libpinyin)

拓展-语音输入

开源项目

VocoType-linux:高性能 Linux 离线中文语音输入法,基于 Ali FunASR(VocoType-cli). ~0.1s 瞬时上屏,输入法级稳定性, 极高中文准确率、低资源占用(CPU Only).支持 IBus / Fcitx5

对着文档就能安装,只需要安装语音输入法就行,里边的中文输入法不太行

1
2
3
4
git clone https://github.com/LeonardNJU/VocoType-linux.git
cd vocotype-cli
./scripts/install-ibus.sh
ibus restart

mvnd安装和配置

一、mvnd 安装

方法 1:通过 SDKMAN(推荐)

1
2
3
curl -s "https://get.sdkman.io" | bash
source ~/.sdkman/bin/sdkman-init.sh
sdk install mvnd

验证版本:

1
mvnd -v

方法 2:手动安装(备选)

到 Apache 官方下载 mvnd:

1
2
3
wget https://downloads.apache.org/maven/mvnd/latest/mvnd-1.0.3-linux-amd64.zip
unzip mvnd-1.0.3-linux-amd64.zip -d ~/mvnd
export PATH=~/mvnd/bin:$PATH

验证:

1
mvnd -v

三、环境变量

mvnd 对 Java 路径十分严格,必须设置正确的 JAVA_HOME。

正确示例:

1
2
JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
PATH=$JAVA_HOME/bin:$PATH

将其加入 ~/.bashrc

1
2
3
echo 'export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64' >> ~/.bashrc
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> ~/.bashrc
source ~/.bashrc

验证:

1
2
echo $JAVA_HOME
which java

四、配置 Maven 镜像(mvnd 与 mvn 共用)

编辑 ~/.m2/settings.xml

1
2
3
4
5
6
7
8
9
10
<settings>
<mirrors>
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>Aliyun Maven</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
</mirrors>
</settings>

验证镜像生效:

1
mvnd help:effective-settings

看到 aliyun URL 即成功。