Yu's Blog

do something!

python

环境搭建

pyenv

1
2
3
4
5
6
7
# Python 版本管理工具,能让你在不同 Python 版本间轻松切换。可以结合 venv 等虚拟环境工具来管理虚拟环境。
brew install pyenv # macOS安装pyenv
pyenv install --list # 列出可安装的 Python 版本
pyenv install 3.9.7 # 安装指定 Python 版本
pyenv versions # 列出已安装的 Python 版本
pyenv global 3.9.7 # 设置全局 Python 版本
pyenv local 3.9.7 # 设置当前目录局部 Python 版本

venv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Python 标准库自带的虚拟环境创建工具,用于创建轻量级的虚拟环境。它只能管理虚拟环境,无法管理 Python 版本
python3 -m venv myenv # 创建名为 myenv 的虚拟环境
python3.9 -m venv myenv # 创建使用 Python 3.9 的虚拟环境
python3 -m venv --without-pip myenv # 创建纯净环境

source myenv/bin/activate # 激活后前缀显示(myenv)

pip install -r requirements.txt
pip install requests==2.28.2 # 安装指定版本包
pip uninstall pandas # 卸载包
pip list # 查看已安装包
pip freeze > requirements.txt # 导出依赖清单

deactivate # 退出虚拟环境
rm -r myenv #删除虚拟环境

uv

install

  • linux / mac : curl -LsSf https://astral.sh/uv/install.sh | sh

创建项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
> uv init tmp
Initialized project `tmp` at `/Users/yzhyi/test/tmp`
# uv init my_project -p 3.11.
# uv init <项目名> --python <Python版本>
# uv init --python
> cd tmp
> ls -al
total 32
drwxr-xr-x@ 8 yzhyi staff 256 7 30 19:23 .
drwxr-xr-x@ 6 yzhyi staff 192 7 30 19:23 ..
drwxr-xr-x@ 9 yzhyi staff 288 7 30 19:24 .git
-rw-r--r--@ 1 yzhyi staff 109 7 30 19:23 .gitignore
-rw-r--r--@ 1 yzhyi staff 5 7 30 19:23 .python-version
-rw-r--r--@ 1 yzhyi staff 81 7 30 19:23 main.py
-rw-r--r--@ 1 yzhyi staff 149 7 30 19:23 pyproject.toml
-rw-r--r--@ 1 yzhyi staff 0 7 30 19:23 README.md

操作环境

  • 同步项目依赖

    1
    2
    3
    4
    5
    6
    > uv sync
    > uv sync --index-url 源
    Using CPython 3.10.18 interpreter at: /Users/yzhyi/.pyenv/versions/3.10.18/bin/python3.10
    Creating virtual environment at: .venv
    Resolved 1 package in 3ms
    Audited in 0.05ms
  • 同步之后,会自动查找或下载合适的 Python 版本,创建并设置项目的虚拟环境,构建完整的依赖列表并写入 uv.lock 文件,最后将依赖同步到虚拟环境中。

  • 既然使用uv管理项目的话,就使用uv的命令来运行代码,不要像以前那样使用python xxx.py来运行。

    1
    2
    > uv run main.py
    Hello from tmp!

管理依赖

1
2
uv add pandas
uv remove pandas

区分开发和生产环境

  • 把pandas安装到开发环境中,而把requests安装到生产环境中。
    1
    2
    uv add --group dev pandas
    uv add --group production requests

管理多版本 Python

1
2
3
4
5
6
7
uv python install
uv python list
uv python dir
uv python install 3.10
uv python uninstall 3.10
uv run --python 3.10 ./main.py
uv python pin 3.10

基础语法

基本数据类型

Number(数字)

  • 数字是不允许改变的,这就意味着如果改变数字数据类型的值,将重新分配内存空间。
  • 可以使用del语句删除一些数字对象的引用(del var)

String(字符串)

  • 字符串用单引号 ‘ 或双引号 “ 括起来,同时使用反斜杠 \ 转义特殊字符。
  • 字符串的截取的语法格式如下:变量[头下标:尾下标] (索引值以 0 为开始值,-1 为从末尾的开始位置。)
  • 与 C 字符串不同的是,Python 字符串不能被改变。向一个索引位置赋值,比如 word[0] = ‘m’ 会导致错误。
  • 字符串可以用+运算符连接在一起,用*运算符重复。
  • 不支持单字符类型,单字符在 Python 中也是作为一个字符串使用。
  • f-string 是 python3.6 之后版本添加的,称之为字面量格式化字符串,是新的格式化字符串的语法。f-string 格式化字符串以 f 开头,后面跟着字符串,字符串中的表达式用大括号 {} 包起来,它会将变量或表达式计算后的值替换进去。
    1
    2
    s = 'hello'
    print(s[1:2]) # 输出 e (左闭右开)

bool(布尔类型)

  • bool 是 int 的子类,因此布尔值可以被看作整数来使用,其中 True 等价于 1。
  • 布尔类型可以和其他数据类型进行比较,比如数字、字符串等。在比较时,Python 会将 True 视为 1,False 视为 0。
  • 可以使用 bool() 函数将其他类型的值转换为布尔值。以下值在转换为布尔值时为 False:None、False、零 (0、0.0、0j)、空序列(如 ‘’、()、[])和空映射(如 {})。其他所有值转换为布尔值时均为 True。
  • 所有非零的数字和非空的字符串、列表、元组等数据类型都被视为 True,只有 0、空字符串、空列表、空元组等被视为 False。

List(列表)

  • 列表截取的语法格式与 String 相同。(左闭右开)
  • 列表中的元素是可以改变的
    1
    2
    3
    a = [9, 2, 13, 14, 15, 6]
    a[2:5] = [] # 将对应的元素值设置为 []
    # a 变成 [9, 2, 6]
  • 列表可以使用 + 操作符进行拼接。
  • Python 列表截取可以接收第三个参数,参数作用是截取的步长。(list[1:4:2], 2 是步长)
  • 更新列表:append()
  • 删除列表元素:del list[index]

Tuple(元组)

  • 元组与列表类似,不同之处在于元组的元素不能修改。元组写在小括号 () 里,元素之间用逗号隔开。
  • 虽然tuple的元素不可改变,但它可以包含可变的对象,比如list列表。
  • 创建只有一个元素的元组,需要注意在元素后面添加一个逗号,以区分它是一个元组而不是一个普通的值。
  • 元组也可以使用 + 操作符进行拼接。

Set(集合)

  • Python 中的集合(Set)是一种无序、可变的数据类型,用于存储唯一的元素。
  • 集合中的元素不会重复,并且可以进行交集、并集、差集等常见的集合操作。
  • 在 Python 中,集合使用大括号 {} 表示,元素之间用逗号 , 分隔。
  • 添加元素:s.add( x )
  • 删除元素:s.remove( x )
  • 计算元素个数:len(s)
  • 清空集合:s.clear()
  • 判断元素是否在集合中:x in s

Dictionary(字典)

  • 字典是一种映射类型,字典用 { } 标识,它是一个无序的 键(key) : 值(value) 的集合。
  • 键(key)必须使用不可变类型。

数据类型转换

  • 不允许整型数据与字符串类型的数据进行相加
  • 显式类型转换: int(), float(), str()

运算符

  • /: 精确除法
  • //: 向下取整除法(若分子或分母中有小数,则得到的是取整的小数)
  • **: 幂运算
  • 逻辑运算: and、or、not
  • 成员运算符: in、not in
  • 身份运算符: is、is not (判断两个标识符是不是引用自一个对象)
    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
    #!/usr/bin/python3

    a = 20
    b = 20

    if ( a is b ):
    print ("1 - a 和 b 有相同的标识")
    else:
    print ("1 - a 和 b 没有相同的标识")

    if ( id(a) == id(b) ):
    print ("2 - a 和 b 有相同的标识")
    else:
    print ("2 - a 和 b 没有相同的标识")

    # 修改变量 b 的值
    b = 30
    if ( a is b ):
    print ("3 - a 和 b 有相同的标识")
    else:
    print ("3 - a 和 b 没有相同的标识")

    if ( a is not b ):
    print ("4 - a 和 b 没有相同的标识")
    else:
    print ("4 - a 和 b 有相同的标识")

条件控制

1
2
3
4
5
6
if condition_1:
statement_block_1
elif condition_2:
statement_block_2
else:
statement_block_3

循环语句

  • while 循环 (没有 do while 循环)
    1
    2
    3
    4
    5
    6
    n = 100
    sum = 0
    counter = 1
    while counter <= n:
    sum = sum + counter
    counter += 1
  • for 循环
    1
    2
    for <variable> in <sequence>:
    <statements>

推导式

  • 列表推导式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    [表达式 for 变量 in 列表] 
    [out_exp_res for out_exp in input_list]
    或者
    [表达式 for 变量 in 列表 if 条件]
    [out_exp_res for out_exp in input_list if condition]

    names = ['Bob','Tom','alice','Jerry','Wendy','Smith']
    new_names = [name.upper()for name in names if len(name)>3]
    print(new_names)
    ['ALICE', 'JERRY', 'WENDY', 'SMITH']
  • 字典推导式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    { key_expr: value_expr for value in collection }

    { key_expr: value_expr for value in collection if condition }

    listdemo = ['Google','Runoob', 'Taobao']
    # 将列表中各字符串值为键,各字符串的长度为值,组成键值对
    newdict = {key:len(key) for key in listdemo}
    newdict
    {'Google': 6, 'Runoob': 6, 'Taobao': 6}
  • 集合推导式

    1
    2
    3
    4
    5
    6
    7
    { expression for item in Sequence }

    { expression for item in Sequence if conditional }

    setnew = {i**2 for i in (1,2,3)}
    setnew
    {1, 4, 9}
  • 元组推导式(生成器表达式)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    (expression for item in Sequence )

    (expression for item in Sequence if conditional )

    # 元组推导式和列表推导式的用法也完全相同,只是元组推导式是用 () 圆括号将各部分括起来,而列表推导式用的是中括号 [],另外元组推导式返回的结果是一个生成器对象。
    a = (x for x in range(1,10))
    a
    <generator object <genexpr> at 0x7faf6ee20a50> # 返回的是生成器对象
    tuple(a) # 使用 tuple() 函数,可以直接将生成器对象转换成元组
    (1, 2, 3, 4, 5, 6, 7, 8, 9)

迭代器与生成器

迭代器

  • 迭代器是一个可以记住遍历的位置的对象。

  • 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

  • 迭代器有两个基本的方法:iter() 和 next()。

  • 字符串,列表或元组对象都可用于创建迭代器:

    1
    2
    3
    4
    5
    6
    7
    >>> list=[1,2,3,4]
    >>> it = iter(list) # 创建迭代器对象
    >>> print (next(it)) # 输出迭代器的下一个元素
    1
    >>> print (next(it))
    2
    >>>
    1
    2
    3
    4
    5
    # 迭代器对象可以使用常规for语句进行遍历:
    list=[1,2,3,4]
    it = iter(list) # 创建迭代器对象
    for x in it:
    print (x, end=" ")
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 也可以使用 next() 函数:
    import sys # 引入 sys 模块

    list=[1,2,3,4]
    it = iter(list) # 创建迭代器对象

    while True:
    try:
    print (next(it))
    except StopIteration:
    sys.exit()
  • 创建一个迭代器: 把一个类作为一个迭代器使用需要在类中实现两个方法 iter() 与 next() 。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class MyNumbers:
    def __iter__(self):
    self.a = 1
    return self

    def __next__(self):
    x = self.a
    self.a += 1
    return x

    myclass = MyNumbers()
    myiter = iter(myclass)

    print(next(myiter))
    print(next(myiter))
    print(next(myiter))
    print(next(myiter))
    print(next(myiter))

生成器

  • 使用了 yield 的函数被称为生成器(generator)。
  • yield 是一个关键字,用于定义生成器函数,生成器函数是一种特殊的函数,可以在迭代过程中逐步产生值,而不是一次性返回所有结果。
  • 跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
  • 当在生成器函数中使用 yield 语句时,函数的执行将会暂停,并将 yield 后面的表达式作为当前迭代的值返回。
  • 每次调用生成器的 next() 方法或使用 for 循环进行迭代时,函数会从上次暂停的地方继续执行,直到再次遇到 yield 语句。这样,生成器函数可以逐步产生值,而不需要一次性计算并返回所有结果。
  • 调用一个生成器函数,返回的是一个迭代器对象。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def countdown(n):
    while n > 0:
    yield n
    n -= 1

    # 创建生成器对象
    generator = countdown(5)

    # 通过迭代生成器获取值
    print(next(generator)) # 输出: 5
    print(next(generator)) # 输出: 4
    print(next(generator)) # 输出: 3

    # 使用 for 循环迭代生成器
    for value in generator:
    print(value) # 输出: 2 1

with 关键字

  • with 是 Python 中的一个关键字,用于上下文管理协议(Context Management Protocol)。它简化了资源管理代码,特别是那些需要明确释放或清理的资源(如文件、网络连接、数据库连接等)。

  • 优势:

    • 自动资源释放:确保资源在使用后被正确关闭
    • 代码简洁:减少样板代码
    • 异常安全:即使在代码块中发生异常,资源也会被正确释放
    • 可读性强:明确标识资源的作用域
  • 基础用法

    1
    2
    3
    4
    5
    with expression [as variable]:
    # 代码块
    # expression 返回一个支持上下文管理协议的对象
    # as variable 是可选的,用于将表达式结果赋值给变量
    # 代码块执行完毕后,自动调用清理方法
  • 工作原理

    • with 语句背后是 Python 的上下文管理协议,该协议要求对象实现两个方法:
      • enter():进入上下文时调用,返回值赋给 as 后的变量
      • exit():退出上下文时调用,处理清理工作
  • 创建自定义的上下文管理器

    • 类实现方式, 可以通过实现 enterexit 方法创建自定义的上下文管理器:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      class Timer:
      def __enter__(self):
      import time
      self.start = time.time()
      return self

      def __exit__(self, exc_type, exc_val, exc_tb):
      import time
      self.end = time.time()
      print(f"耗时: {self.end - self.start:.2f}秒")
      return False

      # 使用示例
      with Timer() as t:
      # 执行一些耗时操作
      sum(range(1000000))
      a = 1
      print(a) # 正确,with 与 if 这种语句块不同,并不会限制变量的作用域(a 的作用于未被限制在 with 中,而是在外层)
    • 使用 contextlib 模块, Python 的 contextlib 模块提供了更简单的方式来创建上下文管理器:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      from contextlib import contextmanager

      @contextmanager
      def tag(name):
      print(f"<{name}>")
      yield
      print(f"</{name}>")

      # 使用示例
      with tag("h1"):
      print("这是一个标题")

函数

  • 定义函数使用 def 关键字,一般格式如下:

    1
    2
    def 函数名(参数列表):
    函数体
  • 参数传递

    • 可更改(mutable)与不可更改(immutable)对象: 在 python 中,strings, tuples, 和 numbers 是不可更改的对象,而 list,dict 等则是可以修改的对象。

      • 不可变类型:变量赋值 a=5 后再赋值 a=10,这里实际是新生成一个 int 值对象 10,再让 a 指向它,而 5 被丢弃,不是改变 a 的值,相当于新生成了 a。
      • 可变类型:变量赋值 la=[1,2,3,4] 后再赋值 la[2]=5 则是将 list la 的第三个元素值更改,本身la没有动,只是其内部的一部分值被修改了。
    • python 函数的参数传递:

      • 不可变类型:类似 C++ 的值传递,如整数、字符串、元组。如 fun(a),传递的只是 a 的值,没有影响 a 对象本身。如果在 fun(a) 内部修改 a 的值,则是新生成一个 a 的对象。
      • 可变类型:类似 C++ 的引用传递,如 列表,字典。如 fun(la),则是将 la 真正的传过去,修改后 fun 外部的 la 也会受影响
  • 不定长参数

    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
    def functionname([formal_args,] *var_args_tuple ):
    "函数_文档字符串"
    function_suite
    return [expression]

    # 举例
    def printinfo( arg1, *vartuple ):
    "打印任何传入的参数"
    print ("输出: ")
    print (arg1)
    print (vartuple)
    # 调用printinfo 函数
    printinfo( 70, 60, 50 )
    # 输出:
    70
    (60, 50)


    # 还有一种就是参数带两个星号 **基本语法如下:
    def functionname([formal_args,] **var_args_dict ):
    "函数_文档字符串"
    function_suite
    return [expression]

    # 加了两个星号 ** 的参数会以字典的形式导入。
    # 举例
    def printinfo( arg1, **vardict ):
    "打印任何传入的参数"
    print ("输出: ")
    print (arg1)
    print (vardict)

    # 调用printinfo 函数
    printinfo(1, a=2,b=3)
    # 输出
    1
    {'a': 2, 'b': 3}

lambda 函数

  • ambda 函数是一种小型、匿名的、内联函数,它可以具有任意数量的参数,但只能有一个表达式。
  • lambda 语法格式:
    1
    2
    3
    4
    5
    lambda arguments: expression

    lambda是 Python 的关键字,用于定义 lambda 函数。
    arguments 是参数列表,可以包含零个或多个参数,但必须在冒号(:)前指定。
    expression 是一个表达式,用于计算并返回函数的结果。

装饰器

  • 装饰器(decorators)是 Python 中的一种高级功能,它允许你动态地修改函数或类的行为。装饰器是一种函数,它接受一个函数作为参数,并返回一个新的函数或修改原来的函数。
  • 装饰器的语法使用 @decorator_name 来应用在函数或方法上。

函数装饰器

  • Python 装饰允许在不修改原有函数代码的基础上,动态地增加或修改函数的功能,装饰器本质上是一个接收函数作为输入并返回一个新的包装过后的函数的对象。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    def decorator_function(original_function):
    def wrapper(*args, **kwargs):
    # 这里是在调用原始函数前添加的新功能
    before_call_code()

    result = original_function(*args, **kwargs)

    # 这里是在调用原始函数后添加的新功能
    after_call_code()

    return result
    return wrapper

    # 使用装饰器
    @decorator_function
    def target_function(arg1, arg2):
    pass # 原始函数的实现

类装饰器

  • 类装饰器(Class Decorator)是一种用于动态修改类行为的装饰器,它接收一个类作为参数,并返回一个新的类或修改后的类。
  • 函数形式的类装饰器
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    def log_class(cls):
    """类装饰器,在调用方法前后打印日志"""
    class Wrapper:
    def __init__(self, *args, **kwargs):
    self.wrapped = cls(*args, **kwargs) # 实例化原始类

    def __getattr__(self, name):
    """拦截未定义的属性访问,转发给原始类"""
    return getattr(self.wrapped, name)

    def display(self):
    print(f"调用 {cls.__name__}.display() 前")
    self.wrapped.display()
    print(f"调用 {cls.__name__}.display() 后")

    return Wrapper # 返回包装后的类

    @log_class
    class MyClass:
    def display(self):
    print("这是 MyClass 的 display 方法")

    obj = MyClass()
    obj.display()
  • 类形式的类装饰器(实现 call 方法)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class SingletonDecorator:
    """类装饰器,使目标类变成单例模式"""
    def __init__(self, cls):
    self.cls = cls
    self.instance = None

    def __call__(self, *args, **kwargs):
    """拦截实例化过程,确保只创建一个实例"""
    if self.instance is None:
    self.instance = self.cls(*args, **kwargs)
    return self.instance

    @SingletonDecorator
    class Database:
    def __init__(self):
    print("Database 初始化")

    db1 = Database()
    db2 = Database()
    print(db1 is db2) # True,说明是同一个实例

内置装饰器

  • @staticmethod: 将方法定义为静态方法,不需要实例化类即可调用。
  • @classmethod: 将方法定义为类方法,第一个参数是类本身(通常命名为 cls)。
  • @property: 将方法转换为属性,使其可以像属性一样访问。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class MyClass:
    @staticmethod
    def static_method():
    print("This is a static method.")

    @classmethod
    def class_method(cls):
    print(f"This is a class method of {cls.__name__}.")

    @property
    def name(self):
    return self._name

    @name.setter
    def name(self, value):
    self._name = value

    # 使用
    MyClass.static_method()
    MyClass.class_method()

    obj = MyClass()
    obj.name = "Alice"
    print(obj.name)

模块

  • 一个模块只会被导入一次,不管你执行了多少次 import。这样可以防止导入模块被一遍又一遍地执行。
  • 搜索路径
    • 当前目录。
    • 环境变量 PYTHONPATH 指定的目录。
    • Python 标准库目录。
    • .pth 文件中指定的目录。
  • import 模块名:并没有把直接定义在模块中的函数名称写入到当前符号表里,只是把模块的名字写到了那里, 可以使用模块名称来访问函数.
  • from … import 语句: 从模块中导入一个指定的部分到当前命名空间中
  • 给模块起别名: 使用 as 关键字为模块或函数起别名
  • 模块除了方法定义,还可以包括可执行的代码。这些代码一般用来初始化这个模块。这些代码只有在第一次被导入时才会被执行。
  • 每个模块有各自独立的符号表,在模块内部为所有的函数当作全局符号表来使用。
    • 包是一种管理 Python 模块命名空间的形式,采用”点模块名称”。
    • 目录只有包含一个叫做 init.py 的文件才会被认作是一个包,主要是为了避免一些滥俗的名字(比如叫做 string)不小心的影响搜索路径中的有效模块。
    • 最简单的情况,放一个空的 :file:init.py就可以了。当然这个文件中也可以包含一些初始化代码.

错误和异常

1
2
3
4
5
6
7
8
9
10
11
12
try:
runoob()
except AssertionError as error:
print(error)
else:
try:
with open('file.log') as file:
read_data = file.read()
except FileNotFoundError as fnf_error:
print(fnf_error)
finally:
print('这句话,无论异常是否发生都会执行。')
  • 自定义异常
    • 异常类继承自 Exception 类,可以直接继承,或者间接继承
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      >>> class MyError(Exception):
      def __init__(self, value): # 类 Exception 默认的 __init__() 被覆盖。
      self.value = value
      def __str__(self):
      return repr(self.value)

      >>> try:
      raise MyError(2*2)
      except MyError as e:
      print('My exception occurred, value:', e.value)

      My exception occurred, value: 4
      >>> raise MyError('oops!')
      Traceback (most recent call last):
      File "<stdin>", line 1, in ?
      __main__.MyError: 'oops!'

面向对象

  • 类有一个名为 init() 的特殊方法(构造方法),该方法在类实例化时会自动调用
    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
    #类定义
    class people:
    #定义基本属性
    name = ''
    age = 0
    #定义私有属性,私有属性在类外部无法直接进行访问
    __weight = 0
    #定义构造方法
    def __init__(self,n,a,w):
    self.name = n
    self.age = a
    self.__weight = w
    def speak(self):
    print("%s 说: 我 %d 岁。" %(self.name,self.age))

    #单继承示例
    class student(people):
    grade = ''
    def __init__(self,n,a,w,g):
    #调用父类的构函
    people.__init__(self,n,a,w)
    self.grade = g
    #覆写父类的方法
    def speak(self):
    print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade))

    s = student('ken',10,60,3)
    s.speak()
  • 运算符重载
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Vector:
    def __init__(self, a, b):
    self.a = a
    self.b = b

    def __str__(self):
    return 'Vector (%d, %d)' % (self.a, self.b)

    def __add__(self,other):
    return Vector(self.a + other.a, self.b + other.b)

    v1 = Vector(2,10)
    v2 = Vector(5,-2)
    print (v1 + v2)

命名空间和作用域

  • 当内部作用域想修改外部作用域的变量时,就要用到 global 和 nonlocal 关键字了。
    1
    2
    3
    4
    5
    6
    7
    8
    num = 1
    def fun1():
    global num # 需要使用 global 关键字声明
    print(num)
    num = 123
    print(num)
    fun1()
    print(num)

多线程

  • threading 模块
    • threading.current_thread(): 返回当前的线程变量。
    • threading.enumerate(): 返回一个包含正在运行的线程的列表。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
    • threading.active_count(): 返回正在运行的线程数量,与 len(threading.enumerate()) 有相同的结果。
    • threading.Thread(target, args=(), kwargs={}, daemon=None):创建Thread类的实例。
      • target:线程将要执行的目标函数。
      • args:目标函数的参数,以元组形式传递。
      • kwargs:目标函数的关键字参数,以字典形式传递。
      • daemon:指定线程是否为守护线程。
    • threading.Thread 类提供了以下方法与属性:
      • init(self, group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None):初始化Thread对象。
        • group:线程组,暂时未使用,保留为将来的扩展。
        • target:线程将要执行的目标函数。
        • name:线程的名称。
        • args:目标函数的参数,以元组形式传递。
        • kwargs:目标函数的关键字参数,以字典形式传递。
        • daemon:指定线程是否为守护线程。
      • start(self):启动线程。将调用线程的run()方法。
      • join(self, timeout=None):等待线程终止。默认情况下,join()会一直阻塞,直到被调用线程终止。如果指定了timeout参数,则最多等待timeout秒。
      • is_alive(self):返回线程是否在运行。如果线程已经启动且尚未终止,则返回True,否则返回False。
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        # Code to execute in an independent thread
        import time
        def countdown(n):
        while n > 0:
        print('T-minus', n)
        n -= 1
        time.sleep(5)

        # Create and launch a thread
        from threading import Thread
        t = Thread(target=countdown, args=(10,))
        t.start()
    • 线程同步:
      • 使用 Thread 对象的 Lock 和 Rlock 可以实现简单的线程同步,这两个对象都有 acquire 方法和 release 方法
        • threadLock = threading.Lock()

json 数据解析

  • json.dumps(): 对数据进行编码。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import json
    # Python 字典类型转换为 JSON 对象
    data = {
    'no' : 1,
    'name' : 'Runoob',
    'url' : 'https://www.runoob.com'
    }

    json_str = json.dumps(data)
    print ("Python 原始数据:", repr(data))
    print ("JSON 对象:", json_str)
  • json.loads(): 对数据进行解码。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import json
    # Python 字典类型转换为 JSON 对象
    data1 = {
    'no' : 1,
    'name' : 'Runoob',
    'url' : 'http://www.runoob.com'
    }

    json_str = json.dumps(data1)
    print ("Python 原始数据:", repr(data1))
    print ("JSON 对象:", json_str)

    # 将 JSON 对象转换为 Python 字典
    data2 = json.loads(json_str)
    print ("data2['name']: ", data2['name'])
    print ("data2['url']: ", data2['url'])
  • 处理的是文件而不是字符串,你可以使用 json.dump() 和 json.load() 来编码和解码JSON数据。
    1
    2
    3
    4
    5
    6
    7
    # 写入 JSON 数据
    with open('data.json', 'w') as f:
    json.dump(data, f)

    # 读取数据
    with open('data.json', 'r') as f:
    data = json.load(f)
  • Python 编码为 JSON 类型转换对应表
    • dict object
    • list, tuple array
    • str string
    • int, float, int- & float-derived Enums number
    • True true
    • False false
    • None null
  • JSON 解码为 Python 类型转换对应表:
    • object dict
    • array list
    • string str
    • number (int) int
    • number (real) float
    • true True
    • false False
    • null None

requests 模块

  • Python requests 是一个常用的 HTTP 请求库,可以方便地向网站发送 HTTP 请求,并获取响应结果。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import requests
    # 发送请求
    x = requests.get('https://www.runoob.com/')
    # 返回网页内容
    print(x.text)

    # 每次调用 requests 请求之后,会返回一个 response 对象,该对象包含了具体的响应信息,如状态码、响应头、响应内容等:
    print(response.status_code) # 获取响应状态码
    print(response.headers) # 获取响应头
    print(response.content) # 获取响应内容

queue 模块

  • queue 模块提供了一个线程安全的队列实现,用于在多线程编程中安全地传递数据。
    队列是一种先进先出(FIFO)的数据结构,queue 模块提供了多种队列类型,包括 Queue、LifoQueue 和 PriorityQueue,以满足不同的需求。

Queue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import queue

# 创建一个队列
q = queue.Queue()

# 向队列中添加元素
q.put(1)
q.put(2)
q.put(3)

# 从队列中获取元素
print(q.get()) # 输出: 1
print(q.get()) # 输出: 2
print(q.get()) # 输出: 3

LifoQueue

  • LifoQueue 是一种后进先出(LIFO)的队列,类似于栈。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import queue

    # 创建一个 LIFO 队列
    q = queue.LifoQueue()

    # 向队列中添加元素
    q.put(1)
    q.put(2)
    q.put(3)

    # 从队列中获取元素
    print(q.get()) # 输出: 3
    print(q.get()) # 输出: 2
    print(q.get()) # 输出: 1

PriorityQueue

  • PriorityQueue 是一种优先级队列,元素按照优先级顺序被取出。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import queue

    # 创建一个优先级队列
    q = queue.PriorityQueue()

    # 向队列中添加元素,元素为元组 (优先级, 数据)
    q.put((3, 'Low priority'))
    q.put((1, 'High priority'))
    q.put((2, 'Medium priority'))

    # 从队列中获取元素
    print(q.get()) # 输出: (1, 'High priority')
    print(q.get()) # 输出: (2, 'Medium priority')
    print(q.get()) # 输出: (3, 'Low priority')

logging 模块

配置日志级别

  • DEBUG:详细的调试信息,通常用于开发阶段。
  • INFO:程序正常运行时的信息。
  • WARNING:表示潜在的问题,但程序仍能正常运行。
  • ERROR:表示程序中的错误,导致某些功能无法正常工作。
  • CRITICAL:表示严重的错误,可能导致程序崩溃。
    1
    logging.basicConfig(level=logging.DEBUG)

记录日志

1
2
3
4
5
logging.debug("这是一条调试信息")
logging.info("这是一条普通信息")
logging.warning("这是一条警告信息")
logging.error("这是一条错误信息")
logging.critical("这是一条严重错误信息")

日志输出格式

  • 通过 basicConfig 方法自定义日志的输出格式。
    1
    2
    3
    4
    5
    logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S"
    )

将日志输出到文件

1
2
3
4
5
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
filename="app.log"
)

高级用法

使用多个日志记录器

  • 在大型项目中,你可能需要为不同的模块或组件创建独立的日志记录器。
    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
    import logging

    logger = logging.getLogger("my_logger")
    logger.setLevel(logging.DEBUG)

    # 创建文件处理器
    file_handler = logging.FileHandler("my_logger.log")
    file_handler.setLevel(logging.DEBUG)

    # 创建控制台处理器
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)

    # 设置日志格式
    formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
    file_handler.setFormatter(formatter)
    console_handler.setFormatter(formatter)

    # 将处理器添加到日志记录器
    logger.addHandler(file_handler)
    logger.addHandler(console_handler)

    # 记录日志
    logger.debug("这是一条调试信息") # file
    logger.info("这是一条普通信息") # file and console

日志过滤器

  • 通过过滤器来控制哪些日志需要被记录。
    1
    2
    3
    4
    5
    class MyFilter(logging.Filter):
    def filter(self, record):
    return record.levelno == logging.ERROR

    logger.addFilter(MyFilter())

日志轮转

  • 当日志文件过大时,可以使用 RotatingFileHandler 或 TimedRotatingFileHandler 实现日志轮转
    1
    2
    3
    4
    from logging.handlers import RotatingFileHandler

    handler = RotatingFileHandler("app.log", maxBytes=1024, backupCount=3)
    logger.addHandler(handler)

并发编程

多线程编程

  • 使用 threading 模块可以创建多个线程来并发执行任务。然而,Python 的全局解释锁(GIL)可能会限制多线程的并行性,尤其在 CPU 密集型任务上效果有限。
    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
    import concurrent.futures
    import threading
    import time

    # 定义线程要执行的函数
    def worker(index):
    print(f"Thread {index} is starting")
    time.sleep(2)
    print(f"Thread {index} is done")

    start = time.time()

    # 使用线程池创建多线程
    with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    futures = [executor.submit(worker, i) for i in range(5)]
    # 等待所有线程完成
    concurrent.futures.wait(futures)

    end = time.time()
    print("concurrent cost: ", end - start, " seconds")
    print("All threads are done")

    ### print
    Thread 0 is starting
    Thread 1 is starting
    Thread 2 is starting
    Thread 0 is done
    Thread 3 is starting
    Thread 1 is done
    Thread 2 is done
    Thread 4 is starting
    Thread 3 is done
    Thread 4 is done
    # 有任务执行完才会轮到下一个任务

多进程编程

  • 使用 multiprocessing 模块可以创建多个进程来并发执行任务。每个进程都有自己的解释器和独立的内存空间,可以有效地利用多核处理器,适用于 CPU 密集型任务。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import concurrent.futures
    import time

    # 定义进程要执行的函数
    def worker(index):
    print(f"Process {index} is starting")
    # 模拟耗时操作
    time.sleep(2)
    print(f"Process {index} is done")

    # 使用进程池创建多进程
    # max_workers=3 表示创建一个最大容量为 3 的进程池,即同时可以运行的进程数量不会超过 3 个。
    with concurrent.futures.ProcessPoolExecutor(max_workers=3) as executor:
    # 提交多个进程任务 创建一个包含 5 个任务的列表,每个任务都会执行 worker 函数,传递不同的索引值作为参数。
    futures = [executor.submit(worker, i) for i in range(5)]

    # 等待所有进程完成
    concurrent.futures.wait(futures)
    print("All processes are done")

异步编程

全局解释器锁(GIL)

  • 计算机程序设计语言解释器用于同步线程的一种机制,它使得任何时刻只有一个线程在执行;即使在多核处理器上,使用GIL 的解释器也只允许同一时间执行一个线程;所以当GIL存在的时候,即使电脑有多核cpu,单个时刻也只能够执行1个,相比并发加速的c++/java 所以慢;
  • 多线程 threading 机制依然是有用的,用于IO 密集型计算,因为在IO期间,线程会释放GIL,实现CPU 和IO的并行,因此多线程用于IO密集型计算依然可以大幅度提升速度,但是多线程用于CPU密集型计算的时候,只会更加拖慢速度;使用multiprocessing 的多进程机制实现并行计算,利用多核cpu 的优势,为了应对GIL 的问题,python 提供了multiprocessing。

asyncio 模块

  • 协程是 asyncio 的核心概念之一。它是一个特殊的函数,可以在执行过程中暂停,并在稍后恢复执行。协程通过 async def 关键字定义,并通过 await 关键字暂停执行,等待异步操作完成。
  • 事件循环是 asyncio 的核心组件,负责调度和执行协程。它不断地检查是否有任务需要执行,并在任务完成后调用相应的回调函数。
  • 任务是对协程的封装,表示一个正在执行或将要执行的协程。你可以通过 asyncio.create_task() 函数创建任务,并将其添加到事件循环中。
    1
    2
    3
    async def main():
    task = asyncio.create_task(say_hello())
    await task

基本用法

运行协程

  • 要运行一个协程,你可以使用 asyncio.run() 函数。它会创建一个事件循环,并运行指定的协程。
    1
    2
    3
    4
    5
    6
    7
    8
    import asyncio

    async def main():
    print("Start")
    await asyncio.sleep(1)
    print("End")

    asyncio.run(main())

并发执行多个任务

  • 使用 asyncio.gather() 函数并发执行多个协程,并等待它们全部完成。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import asyncio

    async def task1():
    print("Task 1 started")
    await asyncio.sleep(1)
    print("Task 1 finished")

    async def task2():
    print("Task 2 started")
    await asyncio.sleep(2)
    print("Task 2 finished")

    async def main():
    await asyncio.gather(task1(), task2())

    asyncio.run(main())

超时控制

  • 使用 asyncio.wait_for() 函数为协程设置超时时间。如果协程在指定时间内未完成,将引发 asyncio.TimeoutError 异常。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import asyncio

    async def long_task():
    await asyncio.sleep(10)
    print("Task finished")

    async def main():
    try:
    await asyncio.wait_for(long_task(), timeout=5)
    except asyncio.TimeoutError:
    print("Task timed out")

    asyncio.run(main())

logging

  • 默认只会打印 warning 以上的信息
  • logging.getLogger() 的基本功能是获取一个指定名称的日志记录器对象。
    • logging.getLogger()(无参数):获取一个根日志记录器(root logger)对象。默认的根日志记录器名称为空字符串 “”。
    • logging.getLogger(name)(有参数):获取一个指定名称的日志记录器对象。如果指定的名称在当前日志层次结构中不存在,它会创建一个新日志记录器。
    • 每个获取的 logging 是不同的

pydantic

BaseModel

  • BaseModel 是 Pydantic 库的核心基类,用于创建具有数据验证、序列化和类型检查功能的数据模型。它是现代 Python 开发中处理数据的强大工具。

数据验证和类型转换

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
from pydantic import BaseModel, ValidationError, Field
from typing import List, Optional
from datetime import datetime
from decimal import Decimal

class Product(BaseModel):
id: int
name: str = Field(..., min_length=1, max_length=100)
price: Decimal = Field(..., gt=0, decimal_places=2)
tags: List[str] = Field(default_factory=list)
is_available: bool = True
created_at: datetime = Field(default_factory=datetime.now)
discount: Optional[float] = Field(None, ge=0, le=1)

print("=== 数据验证和类型转换 ===")

# 测试各种类型转换
test_data = {
"id": "123", # str -> int
"name": "iPhone 15",
"price": "2999.99", # str -> Decimal
"tags": "electronics,phone", # 需要手动处理
"is_available": "true", # str -> bool
"discount": "0.1" # str -> float
}

try:
# 大部分类型会自动转换
product = Product(
id=int(test_data["id"]),
name=test_data["name"],
price=Decimal(test_data["price"]),
tags=test_data["tags"].split(","),
is_available=test_data["is_available"].lower() == "true",
discount=float(test_data["discount"])
)

print("✅ 产品创建成功:")
print(f" ID: {product.id} (类型: {type(product.id)})")
print(f" 价格: {product.price} (类型: {type(product.price)})")
print(f" 标签: {product.tags} (类型: {type(product.tags)})")
print(f" 可用: {product.is_available} (类型: {type(product.is_available)})")

except ValidationError as e:
print(f"❌ 验证失败: {e}")

序列化和反序列化

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
class Order(BaseModel):
id: int
user_id: int
items: List[Product]
total_amount: Decimal
status: str = "pending"
created_at: datetime = Field(default_factory=datetime.now)

# 创建复杂的嵌套对象
product1 = Product(id=1, name="iPhone 15", price=Decimal("6999.99"))
product2 = Product(id=2, name="AirPods Pro", price=Decimal("1999.99"))

order = Order(
id=1,
user_id=123,
items=[product1, product2],
total_amount=Decimal("8999.98")
)

print("=== 序列化功能 ===")

# 1. 转换为字典
order_dict = order.model_dump()
print(f"转换为字典: {type(order_dict)}")
print(f"订单ID: {order_dict['id']}")
print(f"商品数量: {len(order_dict['items'])}")

# 2. 转换为JSON字符串
order_json = order.model_dump_json()
print(f"\n转换为JSON: {type(order_json)}")
print(f"JSON长度: {len(order_json)} 字符")

# 3. 从JSON字符串重新创建对象
order_from_json = Order.model_validate_json(order_json)
print(f"\n从JSON重新创建: {order_from_json.id}")
print(f"商品1名称: {order_from_json.items[0].name}")

# 4. 部分序列化
order_partial = order.model_dump(include={"id", "user_id", "status"})
print(f"\n部分序列化: {order_partial}")

# 5. 排除字段
order_without_items = order.model_dump(exclude={"items"})
print(f"\n排除商品: {order_without_items}")

Field

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
from pydantic import BaseModel, Field
from typing import Optional
from datetime import datetime

class User(BaseModel):
# 基本字段配置
id: int = Field(..., description="用户ID", gt=0) # ... 表示必需字段
name: str = Field(..., description="用户姓名", min_length=1, max_length=50)
email: str = Field(..., description="邮箱地址", regex=r'^[^@]+@[^@]+\.[^@]+$')

# Optional 字段配置
age: Optional[int] = Field(None, description="年龄", ge=0, le=150)
phone: Optional[str] = Field(None, description="电话号码", regex=r'^\d{11}$')
bio: Optional[str] = Field(None, description="个人简介", max_length=500)

# 使用别名
user_name: Optional[str] = Field(None, alias="username", description="用户名")
# 允许你在代码中使用一个字段名,但在JSON序列化/反序列化时使用另一个字段名。

# 默认值工厂
created_at: datetime = Field(default_factory=datetime.now, description="创建时间")
tags: list = Field(default_factory=list, description="用户标签")

# 测试
user = User(
id=1,
name="Alice",
email="alice@example.com",
age=25,
phone="13800138000"
username="yzy"
)
print(user.model_dump())

CamelModel

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
from pydantic import BaseModel
from typing import Generic, TypeVar, List, Optional
from datetime import datetime

# 使用你定义的基础类
T = TypeVar("T")

def to_camel(string: str) -> str:
parts = string.split('_')
return parts[0] + ''.join(word.capitalize() for word in parts[1:])

class CamelModel(BaseModel):
class Config:
alias_generator = to_camel
validate_by_name = True

# 定义具体的数据模型
class User(CamelModel):
id: int
user_name: str # 会转换为 userName
email: str
created_at: datetime # 会转换为 createdAt
is_active: bool # 会转换为 isActive

# 使用示例
user_data = User(
id=1,
user_name="alice",
email="alice@example.com",
created_at=datetime.now(),
is_active=True
)

print(user_data.model_dump_json())
# 输出: {"id": 1, "userName": "alice", "email": "alice@example.com", "createdAt": "2024-01-15T10:30:00", "isActive": true}

Ubuntu配置


常用软件

  • apt-fast 多线程下载
    1
    2
    3
    sudo add-apt-repository ppa:apt-fast/stable
    sudo apt install apt-fast
    sudo apt-fast update
  • ssh
  • net-tools
  • tmux
  • git
  • vim
  • htop
  • gcc
  • make
  • neofetch
  • zsh
    1
    chsh -s $(which zsh)
  • clash-verge-rev
    1
    2
    wget https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v2.3.1/Clash.Verge_2.3.1_amd64.deb
    sudo apt install ./Clash.Verge_2.3.1_amd64.deb
  • lazygit
    1
    2
    3
    4
    5
    6
    7
    # ubuntu25
    sudo apt install lazygit
    # ubuntu24及以下版本
    LAZYGIT_VERSION=$(curl -s "https://api.github.com/repos/jesseduffield/lazygit/releases/latest" | \grep -Po '"tag_name": *"v\K[^"]*')
    curl -Lo lazygit.tar.gz "https://github.com/jesseduffield/lazygit/releases/download/v${LAZYGIT_VERSION}/lazygit_${LAZYGIT_VERSION}_Linux_x86_64.tar.gz"
    tar xf lazygit.tar.gz lazygit
    sudo install lazygit -D -t /usr/local/bin/
  • yazi
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    apt install ffmpeg 7zip jq poppler-utils fd-find ripgrep fzf zoxide imagemagick
    # 方法一: 下载官方编译过的包
    wget https://github.com/sxyazi/yazi/releases/download/nightly/yazi-x86_64-unknown-linux-gnu.deb
    sudo apt install ./yazi-x86_64-unknown-linux-gnu.deb
    # 方法二: 手动编译
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    rustup update
    git clone https://github.com/sxyazi/yazi.git
    cd yazi
    cargo build --release --locked
    sudo mv target/release/yazi target/release/ya /usr/local/bin/
  • nvim
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # use ppa
    sudo apt-add-repository ppa:neovim-ppa/unstable
    sudo apt update
    sudo apt install neovim
    # use source code
    sudo apt install ninja-build gettext libtool libtool-bin autoconf automake cmake g++ pkg-config unzip git curl doxygen -y
    git clone https://github.com/neovim/neovim.git && cd neovim
    make CMAKE_BUILD_TYPE=Release
    sudo make install
    nvim --version
    # 安装nodejs
    sudo apt install curl
    curl -fsSL https://deb.nodesource.com/setup_23.x -o nodesource_setup.sh
    sudo -E bash nodesource_setup.sh
    sudo apt install nodejs
    node -v
    build avante.nvim

卸载软件

  • 卸载单个包
    1
    sudo apt remove example-package
  • 卸载包及其配置文件
    1
    sudo apt purge example-package
  • 卸载并自动处理依赖
    1
    sudo apt autoremove

dwm

必备软件

  • feh
  • compton (或者xcompmgr)
  • xorg-dev (可能需要 suckless-tools libx11-dev libxft-dev libxinerama-dev)
  • ubuntu-restricted-extras (基本包含所有基本的媒体编码器)
  • tlp tlp-rdw (电池管理工具)
  • acpi acpitool (电源监控工具)
  • timeshift (系统备份工具)
  • light (背光灯调整工具)
    1
    2
    3
    4
    # 为背光灯调整工具设置sudo免密码
    sudo visudo
    # 在文本最后加入如下代码
    {登录系统的用户名} ALL=NOPASSWD:/usr/bin/light
  • preload (默认记录使用率最高的软件,添加如内存,打开该应用会提速)
  • vim-gtk (将vim中的文本内容复制到系统剪切板,~/.vimrc set clipboard=unnamedplus)
  • flameshot (截屏软件)
  • rofi
  • fcitx5
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    sudo apt install fcitx5 \
    fcitx5-chinese-addons \
    fcitx5-frontend-gtk4 fcitx5-frontend-gtk3 fcitx5-frontend-gtk2 \
    fcitx5-frontend-qt5

    # 添加到 /etc/environment
    GTK_IM_MODULE=fcitx
    QT_IM_MODULE=fcitx
    XMODIFIERS=@im=fcitx
    SDL_IM_MODULE=fcitx
    GLFW_IM_MODULE=ibus
  • vmware-tools
    1
    open-vm-tools open-vm-desktop
  • 更改登陆界面
    1
    2
    3
    4
    git clone https://github.com/thiggy01/change-gdm-background
    ##修改登陆界面颜色
    sudo ./change-gdm-background \#130c0e
    sudo ./change-dgm-background ~/xx/xx.jpg

DWM启动方式

  • 用gdm3做为display manager
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 配置完成之后可以在登录界面选择dwm作为桌面启动
    cd /usr/share/xsessions/
    touch dwm.desktop
    # 添加以下内容
    [Desktop Entry]
    Encoding=UTF-8
    Name=Dwm
    Comment=Dynamic window manager
    Exec=dwm
    Icon=dwm
    Type=XSession
  • 使用startx命令从文字界面启动(推荐)
    1
    2
    3
    4
    5
    6
    7
    8
    # 修改grub配置,打开文件/etc/default/grub,将GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"的改为GRUB_CMDLINE_LINUX_DEFAULT="text"然后执行命令
    sudo update-grub
    # 将启动等级改为多用户等级,执行如下命令:
    sudo systemctl set-default multi-user.target
    # (目前使用 xinit 启动会导致在浏览器中 fcitx5 输入法使用不了)
    # 如果想改回启动图形界面执行下面
    sudo systemctl set-default graphical.target
    # gdm 会读取 ~/.xprofile 而不会读取 ~/.xinitrc,所以要添加 ~/.xprofile,内容和 ~/.xinirc 一样

dwm配置

  • 安装字体
    1
    2
    3
    4
    5
    6
    # 当前用户安装
    cp *.ttf ~/.local/share/fonts/
    # 系统范围安装
    sudo cp /path/to/fonts/font.ttf /usr/share/fonts
    sudo fc-cache -f -v
    fc-list
  • 为软件指定打开标签
    1
    2
    3
    xprop | grep WM_CLASS
    # 鼠标会变为十字架,用十字架点击想要被指定的软件的打开窗口, terminal 就会显示该软件的 instance 和 class
    # 将信息填入 config.h 的rule中
  • use slock
    1
    sudo addgroup --system nobody
  • 触控板
    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
    # 实时配置
    xinput list
    # 找到触控板设备的名称或 ID。
    # 使用以下命令来启用轻点功能(假设你的触控板 ID 是 12,可以根据实际结果更改):
    xinput set-prop 12 "libinput Tapping Enabled" 1

    # 添加配置文件这种方法更方便
    sudo apt install xserver-xorg-input-synaptics
    sudo cp /usr/share/X11/xorg.conf.d/70-synaptics.conf /etc/X11/xorg.conf.d/
    Option "TapButton1" "1"
    Option "TapButton2" "3"
    Option "TapButton3" "2"
    Option "VertEdgeScroll" "on"
    Option "VertTwoFingerScroll" "on"
    Option "HorizEdgeScroll" "on"
    Option "HorizTwoFingerScroll" "on"
    Option "CircularScrolling" "on"
    Option "CircScrollTrigger" "2"
    Option "EmulateTwoFingerMinZ" "40"
    Option "EmulateTwoFingerMinW" "8"
    Option "FingerLow" "30"
    Option "FingerHigh" "50"
    Option "MaxTapTime" "125"
    Option "PalmDetect" "1"
    Option "PalmMinWidth" "8"
    Option "PalmMinZ" "200"
  • picom
    1
    2
    3
    4
    sudo apt-fast install libconfig-dev libdbus-1-dev libegl-dev libev-dev libgl-dev libepoxy-dev libpcre2-dev libpixman-1-dev libx11-xcb-dev libxcb1-dev libxcb-composite0-dev libxcb-damage0-dev libxcb-glx0-dev libxcb-image0-dev libxcb-present-dev libxcb-randr0-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-shape0-dev libxcb-util-dev libxcb-xfixes0-dev meson ninja-build uthash-dev
    wget https://github.com/yshui/picom/releases(不行就去网页下载)
    meson setup --buildtype=release build
    ninja -C build install
  • yesplaymusic
    1
    sudo snap install yesplaymusic
  • caffeine(熄屏不进入休眠)
  • 向日葵
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 修复依赖 libgconf-2-4
    下载依赖
    wget http://ftp.debian.org/debian/pool/main/g/gconf/libgconf-2-4_3.2.6-7_amd64.deb
    wget http://ftp.debian.org/debian/pool/main/g/gconf/gconf2-common_3.2.6-7_all.deb

    安装依赖
    sudo dpkg -i gconf2-common_3.2.6-7_all.deb
    sudo dpkg -i libgconf-2-4_3.2.6-7_amd64.deb
    sudo apt --fix-broken install

    安装 SunloginClient
    sudo dpkg -i SunloginClient_15.2.0.63064_amd64.deb

加载系统

  • kernel.ld 描述可执行程序或库在目标内存中的布局方式。它定义了不同的程序段(如代码段、只读数据段、可读写数据段、未初始化数据段等)应该如何在内存中排列,以及指定了一些特殊符号的值,这些符号可以在程序的其他部分被引用,以了解内存布局的相关信息。
  • 内核text段加载到物理内存0x80000000处;随后是trampoline.S(用户和内核的跳转代码).这两段都是按页对齐的;随后是rodata,data,bss段.
  • entry.S 为每个核心配置内核栈,然后跳到start处.
  • start.c 执行一些只允许在机器模式下进行的配置,然后切换到主管模式。
  • main.c 初始化几个设备和子系统之后,它通过调用userinit 创建第一个进程(initcode)。

main.c

  • kinit(): 将所有空闲内存添加到freelist.
  • kvminit(): 设置内核空间pagetable.(除了trampoline 和 kernal stacks外,其他内存是直接映射)
  • kvminithart(): 内核页表指针存到satp中,开启页表.

  • -S:synchronize 同步
  • -R:remove 删除
  • -Q:query 查询本地

安装

1
sudo pacman -S 包名 # 安装软件包的同时会安装该包需要的依赖

更新

1
2
3
4
5
sudo pacman -Sy # 更新软件源,如果前一段时间已更新过,则pacman不会再去查找更新
sudo pacman -Syy # 强行更新软件源
sudo pacman -Su # 更新软件包
sudo pacman -Syu # 更新软件源并更新软件包
sudo pacman -Syyu # 强行更新软件源并更新软件包

搜索

1
sudo pacman -Ss 包名 # 搜索支持正则表达式

删除

1
2
3
4
sudo pacman -R 包名
sudo pacman -Rs 包名 # 删除该软件与该软件的依赖包
sudo pacman -Rnsc 包名 # 删除该软件与该软件的依赖包并删除全局配置文件,个人配置文件不会删除 (推荐使用这种方法!)
sudo pacman -Sc # 删除缓存(-R删除包之后不会清除文件,只是放到了/var/*目录下)

查询本地软件包

1
2
3
4
5
6
7
sudo pacman -Q # 本地所有软件包:系统自带和用户下载的
sudo pacman -Qe # 本地所有软件包:用户下载的
sudo pacman -Qeq # 本地所有软件包(不显示版本号):用户下载的
sudo pacman -Qs 包名 # 查询本地所有软件中有哪些带有该包名称
sudo pacman -Q | wc -l # 查看一共有多少软件
sudo pacman -Qdt # 查询不再被依赖的软件包(孤包)
sudo pacman -R $(pacman -Qdtq) # 删除不再被依赖的软件包

安装系统

确定启动模式(UEFI/BIOS)

1
2
3
4
5
# 设置终端字号
setfont /usr/share/kbd/consolefonts/LatGrkCyr-12x22.psfu.gz

# 如果提示目录不存在,就是bios,否则是uefi
ls /sys/firmware/efi/efivars

联网(有线网不需要进行这步)

1
2
3
4
5
6
7
8
$ iwctl   //会进入联网模式
[iwd]# help //可以查看帮助
[iwd]# device list //列出你的无线设备名称,一般以wlan0命名
[iwd]# station <device> scan //扫描当前环境下的网络
[iwd]# station <device> get-networks //会显示你扫描到的所有网络
[iwd]# station <device> connect <network name>
password:输入密码
[iwd]# exit //退出当前模式,回到安装模式

更新系统时间

1
2
3
4
# 更新系统时间是有必要的,因为下载软件时,服务器会验证系统时间,
# 如果时间不正确,可能出现下载失败的情况。
timedatectl set-ntp true
timedatectl status # 查看一下系统时钟状态

分区

查看系统磁盘

1
fdisk -l

进入图形化分区工具进行分区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cfdisk /dev/<硬盘名称>
# 如果提示你要选MBR还是GPT: UEFI启动选择gpt,BIOS启动选择dos,选中后回车。
# 1. BIOS启动只需要俩个分区,根分区和swap分区

# 2. UEFI启动需要分三个区:
# 1. boot分区,用于存储启动信息,类型为efi system
# 2. swap分区,也就是交换分区,类型为linux swap
# 3. 系统分区,用于安装系统和软件的分区,类型为linux

# 分区操作:
# 1.选中new
# 2.输入分区大小后选中primary回车
# 3.选中type,修改分区类型(根分区是Linux类型,swap分区是Linux swap/Solars类型)
# 4.确认分区无误后选择write 回车,输入yes,选择quit退出图形化分区界面
# 5.命令行输入lsblk,查看分区的初始状态

格式化分区

BIOS格式化

1
2
3
mkfs.ext4 /dev/<硬盘名称>   # eg:mkfs.ext4 /dev/sda2 格式化系统分区
mkswap /dev/<硬盘名称> # 格式化swap分区
swapon /dev/<硬盘名称> # 激活swap分区

UEFI格式化

1
2
3
4
mkfs.fat -F32 /dev/sda1 # 格式化boot分区
mkswap /dev/sda2 # 格式化swap分区
swapon /dev/sda2 # 激活swap分区
mkfs.ext4 /dev/sda3 # 格式化系统分区

挂载分区

BIOS挂载

1
mount /dev/sda2 /mnt  # 将系统分区/dev/sda2挂载到/mnt目录

UEFI挂载

1
2
3
mount /dev/sda3 /mnt  # 将系统分区/dev/sda3挂载到/mnt目录。
mkdir /mnt/boot # 创建boot分区的挂载点。
mount /dev/sda1 /mnt/boot # 将boot分区/dev/sda1挂载到/mnt/boot目录(需要在/mnt下新建一个boot文件夹)。

修改镜像源

1
2
3
4
5
# 很重要,不然等到天荒地老
vim /etc/pacman.d/mirrorlist # 加上一个国内源
Server = https://mirrors.tuna.tsinghua.edu.cn/archlinux/$repo/os/$arch

pacman -Syy # 更新一下

安装linux

1
2
3
4
5
6
7
8
9
pacstrap /mnt base base-devel linux linux-firmware linux-headers
pacstrap /mnt vim bash-completion iwd dhcpcd net-tools openssh man
# vim 命令行编辑工具
# bash-completion 命令行补全工具
# iwd 无线管理工具
# dhcpcd 有线管理工具
# net-tools 网络工具
# openssh ssh服务
# man 使用手册

写入分区表

1
2
genfstab -U /mnt >> /mnt/etc/fstab
cat /mnt/etc/fstab # 检查fstab文件是否正确

进入系统进行相关配置

进入系统

1
arch-chroot /mnt

设置时区

1
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

同步硬件时钟

1
hwclock --systohc

本地化设置

1
2
3
4
5
6
7
8
9
vim /etc/locale.gen
# 找到下面两项,取消注释
en_US.UTF-8 UTF-8 # 英
zh_CN.UTF-8 UTF-8 # 中

locale-gen # 生成locale信息
locale -a # 列出所有启用的locale
echo LANG=en_US.UTF-8 > /etc/locale.conf # 设置系统语言
locale # 查看一下

网络配置

1
2
3
4
5
6
vim /etc/hostname # 编辑hostname文件,输入主机名(可以自己命名)
vim /etc/hosts

127.0.0.1 localhost
::1 localhost
127.0.1.1 主机名.localdomain 主机名

安装grub引导

BIOS 安装 grub 引导程序

1
2
3
4
5
6
7
8
9
10
pacman -S grub
grub-install /dev/sda # 安装grub。
grub-mkconfig -o /boot/grub/grub.cfg # 生成grub的配置文件。

若报错则执行下面
vim /etc/default/grub
GRUB_DISABLE_OS_PROBER=false # 取消GRUB_DISABLE_OS_PROBER=false注释(让grub识别别的系统)
# 建议修改GRUB_TIMEOUT,加快启动时间

grub-mkconfig -o /boot/grub/grub.cfg # 再执行一次

UEFI 安装 grub 引导程序

1
2
3
4
5
6
7
8
9
10
11
12
13
# 下载grub和efibootmgr软件包
pacman -S grub efibootmgr

# 安装grub
grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=Arch --recheck
# --bootloader-id=name 其中 name 可以更改为自己想要的名称,建议简短明确

vim /etc/default/grub
GRUB_DISABLE_OS_PROBER=false # 取消GRUB_DISABLE_OS_PROBER=false注释(让grub识别别的系统)
# 建议修改GRUB_TIMEOUT,加快启动时间

# 生成grub的配置文件。
grub-mkconfig -o /boot/grub/grub.cfg
  • 如果出现错误,请回头检查命令是否输入错误或者配置是否出错,安装时的一点错误都可能成为你无法开机的原因或者以后使用时的bug.

设置root密码,新建用户

1
2
3
4
5
6
7
8
passwd
useradd -m -G wheel <用户名>
passwd <用户名>

# 加入sudo,打开 /etc/sudoers
如果EDITOR=vim
执行visudo
找到%wheel ALL=(ALL) ALL 把前面的注释去掉

配置启动项

1
2
3
sudo systemctl enable dhcpcd
sudo systemctl enable sshd
sudo systemctl enable iwd

安装一些硬件设备

1
2
3
4
5
6
7
8
9
10
11
12
13
# CPU编码
pacman -S intel-ucode (intel的cpu装这个)
pacman -S amd-ucode (amd的cpu装这个)

# 显卡驱动
pacman -S xf86-video-intel(Intel核心显卡驱动,用Intel核显就装,否则不用装)
pacman -S mesa nvidia(-lts) nvidia-settings nvidia-dkms nvidia-utils nvidia-prime(nvidia显卡驱动,用nvidia显卡就装,否则不用装)
pacman -S xf86-video-amdgpu (AMD显卡驱动,用amd显卡的就装)

# 这里举两个例子,我的笔记本,i7-11代,搭配intel核显以及3050显卡,所以安装前两个。我的台式机,e3-1230垃圾CPU,搭配HD6950显卡,所以装第三个。nvidia-dkms 与 nvidia-lts 不兼容,如果装lts驱动的话无需安装dkms 。注意:nvidia驱动的安装与前面选择的内核有关,如果你安装的是linux-lts内核,那么需要将nvidia更换为nvidia-lts,linux-zen不支持nvidia显卡(务必对号入座),如果你选择安装新内核,则需要修改一下ibt=off ,否则无法进入系统

# 声卡驱动
pacman -S pipewire (alsa-utils) pipewire-pulse pipewire-jack pipewire-alsa

使用cn源

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
vim /etc/pacman.conf  # 删除Color前的#号

# 在/etc/pacman.conf最后添加
[archlinuxcn]
SigLevel = Optional TrustedOnly
Include = /etc/pacman.d/archlinuxcn

vim /etc/pacman.d/archlinuxcn # 写入以下源
## OpenTUNA (China CDN) (ipv4, https)
Server = https://opentuna.cn/archlinuxcn/$arch
## 北京外国语大学 (北京) (ipv4, ipv6, http, https)
## Added: 2020-05-18
Server = https://mirrors.bfsu.edu.cn/archlinuxcn/$arch
## 腾讯云 (Global CDN) (ipv4, http, https)
## Added: 2018-11-23
Server = https://mirrors.cloud.tencent.com/archlinuxcn/$arch
## 网易 (China CDN) (ipv4, http, https)
Server = https://mirrors.163.com/archlinux-cn/$arch
## 阿里云 (Global CDN) (ipv4, ipv6, http, https)
## Added: 2020-07-03
Server = https://mirrors.aliyun.com/archlinuxcn/$arch
## 华为云 (Global CDN) (ipv4, http, https)
## Added: 2020-10-31
Server = https://repo.huaweicloud.com/archlinuxcn/$arch
## 清华大学 (北京) (ipv4, ipv6, http, https)
Server = https://mirrors.tuna.tsinghua.edu.cn/archlinuxcn/$arch
## 中国科学技术大学 (安徽合肥) (ipv4, ipv6, http, https)
Server = https://mirrors.ustc.edu.cn/archlinuxcn/$arch
## 哈尔滨工业大学 (黑龙江哈尔滨) (ipv4, ipv6, http, https)
## Added: 2021-01-09
Server = https://mirrors.hit.edu.cn/archlinuxcn/$arch
## 浙江大学 (浙江杭州) (ipv4, http, https)
## Added: 2017-06-05
Server = https://mirrors.zju.edu.cn/archlinuxcn/$arch
## 重庆大学 (重庆) (ipv4, ipv6, https)
Server = https://mirrors.cqu.edu.cn/archlinuxcn/$arch
## 重庆邮电大学 (重庆) (ipv4, http, https)
Server = https://mirrors.cqupt.edu.cn/archlinuxcn/$arch
## SJTUG 软件源镜像服务 (上海) (ipv4, ipv6, https)
## Added: 2018-05-21
Server = https://mirrors.sjtug.sjtu.edu.cn/archlinux-cn/$arch
## 南京大学 (江苏南京) (ipv4, ipv6, http, https)
Server = https://mirrors.nju.edu.cn/archlinuxcn/$arch
## 莞工 GNU/Linux 协会 开源软件镜像站 (广东东莞) (ipv4, https)
## Added: 2018-11-03
Server = https://mirrors.dgut.edu.cn/archlinuxcn/$arch
## 南方科技大学 (广东深圳) (ipv4, ipv6, http, https)
## Added: 2021-08-17
Server = https://mirrors.sustech.edu.cn/archlinuxcn/$arch

sudo pacman -Syy
sudo pacman -S archlinuxcn-keyring
1
2
3
4
5
6
# 想简单点就直接在/etc/pacman.conf最后添加
[archlinuxcn]
Server = https://mirrors.tuna.tsinghua.edu.cn/archlinuxcn/$arch

sudo pacman -Syy
sudo pacman -S archlinuxcn-keyring

使用aur

paru

1
2
3
4
5
6
7
8
9
10
11
$ git clone https://aur.archlinux.org/paru.git
$ cd paru
$ makepkg -si

注:archlinuxcn源中有paru,直接sudo pacman -S paru

$ paru # 约等于 sudo pacman -S Syyu 对软件进行更新并升级
$ paru 包名

sudo vim /etc/paru.conf
取消BottomUp的注释 # 使最常用的软件显示在下方

退出系统重启

1
2
3
exit
umount -R /mnt
reboot

系统软件

使用debtap将Deb包解压并打包成archliux package

1
2
3
4
paru debtap
sudo debtap -u
debtap xxx.deb
sudo pacman -U xxx.pkg

桌面环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
sudo pacman -S xorg xorg-server xorg-xinit xorg-xrandr
sudo pacman -S feh # 壁纸
sudo pacman -S udisks2 udiskie # U盘
xrandr # 查看有哪些显示器

cp /etc/X11/xinit/xinitrc ~/.xinitrcS
vim ~/.xinitrc # 删除最后几行没用的,添加以下几行
fcitx5 & # 开启中文输入法且后台运行
xrandr --output Virtual-1(显示器名称) --mode 1920x1080 --rate 60.00(显示器配置)
feh --bg-fill --randomize /usr/share/backgrouds/archlinux/* # 先设置分辨率再设置壁纸
picom -b &
exec slstatus &
末行添加 exec dwm

systemctl enable udisk2 # 识别U盘
startx # 进入dwm

ctrl+shift+pageup # 放大字体
xrandr -q # 查看显示器设置
xrandr --output Virtual-1(显示器名称) --mode 1920x1080 --rate 60.00(显示器配置)

电池优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
yay -S tlp tlp-rdw tlpui                                       ## 安装电源管理及图形界面
sudo vim /etc/tlp.conf ## 编辑配置文件,防止Btrfs文件系统损坏
SATA_LINKPWR_ON_BAT=max_performance ## 更改内容为
sudo systemctl enable tlp.service ## 设置tlp服务开机自启动
sudo systemctl enable NetworkManager-dispatcher.service ## 设置开机自启动
sudo syatemctl mask systemd-rfkill.service ## 屏蔽服务,防止冲突
sudo syatemctl mask systemd-rfkill.socket ## 屏蔽,防止冲突
sudo tlp start ## 启动服务

# 使用TLP显示系统信息
sudo tlp-stat -b ## 显示电池信息
sudo tlp-stat -d ## 显示磁盘信息
sudo tlp-stat -e ## 显示 PCI 设备信息
sudo tlp-stat -g ## 显示 GPU 信息
sudo tlp-stat -p ## 显示 CPU 信息
sudo tlp-stat -s ## 显示系统数据信息

声音

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 -S alsa-utils

# 查看有几个声卡设备
cat /proc/asound/cards

# 查看声卡的card number和device number
aplay -h # 查看帮助
aplay -l # 列出所有的声卡和数字音频设备

# 设置默认声卡
# nvim /etc/asound.conf
defaults.pcm.card 1
defaults.pcm.device 0
defaults.ctl.card 1

# 开启声音
amixer sset Master toggle # 交换开启状态
amixer sset Master Capture # 交换开启状态

# 关闭自动静音
# 方法一
amixer -h # 查看帮助
amixer sset Master unmute
amixer sset Speaker unmute
amixer sset Headphone unmute
amixer set Master 10%+ # Master增加10%的音量
amixer set Master 10%- # Master减少10%的音量
amixer set Master 70% # Master音量设定为70%

# 播放音频
aplay -D hw:1,0 /usr/share/sounds/test.wav # hw后的的数字分别代表卡号和设备号

# 在终端中输入alsamixer打开设置
在 alsamixer 中,下方标有 MM 的声道是静音的,而标有 00 的通道已经启用。
使用 ← 和 → 方向键,选中 Master 和 PCM 声道。按下 m 键解除静音。使用 ↑ 方向键增加音量,直到增益值为0。该值显示在左上方 Item: 字段后。过高的增益值会导致声音失真。

背光

1
2
3
4
5
sudo pacman -S light
sudo usermod -aG video <user>
light -U 10 # 屏幕亮度降低10%
light -A 10 # 屏幕亮度升高10%
light -G # 得到当前亮度

蓝牙

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
// blueman更方便
sudo bluetooth on # 手动启动蓝牙
sudo bluetooth off # 手动关闭蓝牙
sudo bluetooth toggle # 手动切换蓝牙状态

sudo pacman -S pulseaudio-bluetooth pavucontrol # 安装蓝牙音频
sudo pacman -S bluez bluez-utils
sudo systemctl enable bluetooth.service
sudo systemctl start bluetooth.service

# 使用Bluetoothctl进入蓝牙控制台
$ bluetoothctl --help 查看帮助命令
$ bluetoothctl -v 查看蓝牙版本
$ bluetoothctl 进入蓝牙管理工具环境
[bluetooth]# power on 打开蓝牙
[bluetooth]# agent on 开启代理
[bluetooth]# scan on 扫描蓝牙设备
[bluetooth]# pair xx:xx:xx:... 配对该设备
[bluetooth]# trust xx:xx:xx:... 信任该设备
[bluetooth]# connect xx:xx:... 连接该设备
[bluetooth]# disconnect xx:xx:... 断开蓝牙
[bluetooth]# help 查看帮助信息
[bluetooth]# show 查看本机蓝牙信息
[bluetooth]# discoverable yes 设置蓝牙可被发现
[bluetooth]# info xx:xx:xx:... 查看该蓝牙设备的信息

pulseaudio -k 或者 sudo killall pulseaudio # 确保没有pulseaudio启动
pulseaudio --start # 启动pulseaudio服务
pavucontrol # 管理声音输出

触控板

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
sudo pacman -S libinput xf86-input-libinput

libinput配置文件位置
1.libinput默认的配置文件在/usr/share/X11/xorg.conf.d/40-libinput.conf,可以设置鼠标加速、额外的鼠标按键、触控板、触控屏等。
2.由于同一个设备的不同驱动程序可以共存,如果你打算为一个设备使用 libinput 驱动,请确保它在其他驱动中 /etc/X11/xorg.conf.d/ 拥有优先级。
举个栗子:
如果你同时安装了 libinput 和 synaptics 并使用其默认配置(即 /etc/X11/xorg.conf.d/ 中没有属于两者中任一的文件),synaptics 将因其在默认安装目录中拥有更高的数字顺序 70- 而获得优先级。为了避免这种情况,您可以将默认的 libinput 配置文件(40-libinput.conf)软链接到目录搜索顺序优先于 70-synaptics.conf 的 /etc/X11/xorg.conf.d/ 中去取代它。
sudo ln -s /usr/share/X11/xorg.conf.d/40-libinput.conf /etc/X11/xorg.conf.d/40-libinput.conf

# 更改libinput配置
# vim /etc/X11/xorg.conf.d/40-libinput.conf
MatchIsPointer “on” # 小红点
MatchIsKeyboard “on” # 软键盘
MatchIsTouchpad “on” # 触控板
MatchIsTouchscreen “on” # 触控屏

常用选项
1.当检测到 USB 鼠标时,它将禁用触摸板。
Option "SendEventsMode" "disabled-on-external-mouse"
2.允许单指和双指触击分别调用鼠标左右键,而不用按触控板的物理按键
Option "Tapping" "True"
3.防止打字时误触触控板
Option "DisableWhileTyping" "True"
4.触摸板不再拥有区域的区分,与之代替的是双指代表右键,三指代表中键。
Option "ClickMethod" "clickfinger"
5.轻击后手指按住会使单个按钮关闭,此手指的所有动作都将转换为拖动动作。
Option "TappingDrag" "True"
6.自然滚动(反方向滚动)
Option "NaturalScrolling" "True"
7.启用鼠标加速配置文件。这有助于使鼠标手指的速度更自然一些,迟钝感更小。建议使用 Adaptive,因为其会根据您的输入更改。您也可以尝试“flat”选项。
Option "AccelProfile" "adaptive"
8.更改鼠标指针的加速速度。使用 -1 到 1 之间的值。数值越大,指针移动的速度越高。大多数人倾向于使用 0.2 以获得较慢的响应速度,使用 0.5 获得较快的响应速度。
Option "AccelSpeed" "0.3"

# https://wiki.archlinuxcn.org/wiki/Touchpad_Synaptics?rdfrom=https%3A%2F%2Fwiki.archlinux.org%2Findex.php%3Ftitle%3DTouchpad_Synaptics_%28%25E7%25AE%2580%25E4%25BD%2593%25E4%25B8%25AD%25E6%2596%2587%29%26redirect%3Dno
9.在synaptics上启用自然滚动(触摸屏那种滚动).只要将VertScrollDelta和HorizScrollDelta的值设定为负就行(翻转滚动方向):
Option "VertScrollDelta" "-111"
Option "HorizScrollDelta" "-111"

# 举例:
Section "InputClass"
Identifier "touchpad"
MatchIsTouchpad "on"
MatchDevicePath "/dev/input/event*"
Driver "libinput"
Option "Tapping" "True"
Option "TappingButtonMap" "lrm"
Option "DisableWhileTyping" "True"
Option "TappingDrag" "True"
Option "NaturalScrolling" "True"
Option "SendEventsMode" "disabled-on-external-mouse"
EndSection

通用软件

输入法

1
2
3
4
5
6
7
8
9
10
11
12
# 安装fcitx5
sudo pacman -S fcitx5-im fcitx5-chinese-addons fcitx5-material-color
# 有些应用仍然不能使用中文,去archwiki搜fcitx5查看解决方法

# nerd fond
sudo pacman -S ttf-jetbrains-mono-nerd

# 英文
sudo pacman -S ttf-dehavu ttf-droid ttf-font-awesome otf-font-awesome ttf-liberation ttf-linux-libertine ttf-opensans ttf-roboto ttf-ubuntu-font-family

# 中文
sudo pacman -S ttf-hannom noto-fonts noto-fonts-extra noto-fonts-emoji noto-fonts-cjk adobe-source-code-pro-fonts adobe-source-sans-fonts adobe-source-serif-fonts adobe-source-han-sans-cn-fonts adobe-source-han-sans-hk-fonts adobe-source-han-sans-tw-fonts adobe-source-han-serif-cn-fonts wqy-cn-fonts wqy-microhei

zsh

1
2
cat /etc/shells  # 查看zsh的具体地址
chsh -s path # zsh的详细路径

wps

1
2
3
paru wps-office-cn wps-office-mui-zh-cn ttf-wps-fonts
$ 解决粗体黑块
paru freetype2-wps

software

1
2
3
4
5
6
7
8
9
10
11
sudo pacman -S unzip git neovim tmux joshuto python xclip
paru iwgtk # iwctl的图形界面
sudo pacman -S capnet-assist # 有些网络需要网页登陆
paru google-chrome
sudo pacman -S clash-verge-rev
sudo pacman -S scrot # 截图
sudo pacman -S flameshot # 截图
sudo pacman -S sioyek # pdf
sudo pacman -S rofi # dmenu
# rofi -dump-config > ~/.config/rofi/config.rasi
# rofi options -theme ~/.config/rofi/powermenu/style.rasi -show drun

GDB

启动

  • gdb
  • gdb
  • gdb

基础操作

  • starti: 启动程序并在第一条可执行指令处暂停, 通常停在 _start 函数或动态链接器入口点
  • start: main 函数入口
  • l: 看当前行上下文/ l func 看func上下文
  • p: 打印变量/调用函数. p $寄存器名
  • bt: 打印栈回溯
  • f stack_id: 切换栈(只是能打印该栈的信息,并不会重新执行指令)
  • up/down 改变堆栈显示的深度
  • b: 打断点(b main.cc:5)/(b func有函数重载时会给多个函数下断点, b func(int)只会在这个函数打断点)/(b func if 表达式)
  • i b: 查看断点(info break); d num: 删除断点
  • disable n: 暂停断点; enable n: 启用断点
  • clear 行号n:清除第n行的断点
  • delete breakpoints:清除所有断点
  • tb:创建临时断点
  • s: 进入函数
  • f: 执行完当前函数并返回
  • n: 下一步
  • si:执行下一条机器指令。
  • ni:执行下一条机器指令,但不进入函数内部
  • c: 继续运行
  • watch 变量名/if 变量名 == ?(写入时打断)
  • rwatch 变量名/if 变量名 == ?(读取时打断)
  • awatch (写入和读取都会打断点)
  • tui enable/layout src
  • layout asm/src
  • r 参数(表示传入main的参数argc, argv)
  • finish: 继续执行程序,直到当前函数完成并返回到它的调用者
  • set logging on: 记录过程
  • save breakpoints file: 保存断点信息到指定文件
  • source file: 导入文件中保存的断点信息

多线程调试

  • info threads: 查看线程信息
  • t thread_id: 切换线程
  • 正常情况下多个线程会一起执行,不会因为在一个线程打了断点,另一个线程就不执行了

调度器锁模式

  • 理解调度器锁模式
    1. set scheduler-locking off: 不锁定任何线程,所有线程可以继续执行.
    2. set scheduler-locking on: 只有当前线程可以执行,其他线程暂停运行.
    3. set scheduler-locking step: 当单步执行某一线程时,保证在调试过程中当前线程不会发生改变.其他线程也会随着被调试线程的单步执行而执行;但如果该模式下执行continue,until,finish命令,则其他线程也会执行,并且如果某一线程执行过程中遇到断点,则GDB会将该线程作为当前线程.(只有线程创建后这个命令的暂停效果才有效)
  • 查看调度器锁模式:show scheduler-locking

调试coredump文件

什么是coredump文件?

  • coredump文件又称之为核型转储文件.进程运行时,突然崩溃时的那一瞬间的进程内存快照,会把进程此刻内存,寄存器状态,运行堆栈等信息转储保存在一个文件

怎么产生?

  • ulimit -a: 查看core file size
  • ulimit -c unlimited: 取消 core file 限制

调试coredump文件

  • gdb [binfile] [coredump file]
    1. 查看调用堆栈,寻找崩溃原因.
    2. 根据崩溃点,查找代码,分析原因.
    3. 修复bug.

  • 前缀键: ctrl + b
  • tmux ls: 查看所有tmux会话
  • 新建会话: tmux new -s
  • 分离会话: ctrl + b + d
  • 接入会话: tmux attach -t
  • 杀死会话: tmux kill-session -t
  • 切换会话: tmux switch -t
  • 重命名会话: tmux rename-session -t oldname
  • 窗格操作:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    Ctrl+b %:划分左右两个窗格。
    Ctrl+b ":划分上下两个窗格。
    Ctrl+b <arrow key>:光标切换到其他窗格。<arrow key>是指向要切换到的窗格的方向键,比如切换到下方窗格,就按方向键↓。
    Ctrl+b ;:光标切换到上一个窗格。
    Ctrl+b o:光标切换到下一个窗格。
    Ctrl+b {:当前窗格与上一个窗格交换位置。
    Ctrl+b }:当前窗格与下一个窗格交换位置。
    Ctrl+b Ctrl+o:所有窗格向前移动一个位置,第一个窗格变成最后一个窗格。
    Ctrl+b Alt+o:所有窗格向后移动一个位置,最后一个窗格变成第一个窗格。
    Ctrl+b x:关闭当前窗格。
    Ctrl+b !:将当前窗格拆分为一个独立窗口。
    Ctrl+b z:当前窗格全屏显示,再使用一次会变回原来大小。
    Ctrl+b Ctrl+<arrow key>:按箭头方向调整窗格大小。
    Ctrl+b q:显示窗格编号。

操作

basic

  • .命令可以让我们重复上次的修改(理想模式: 用一键移动,另一键执行)

    • 重复上次修改: 从进入插入模式的那一刻起, 直到返回普通模式时为止
    • x, dd, >G 等普通模式中执行的命令
  • G: 增加从当前行到文档末尾处的缩进层级

  • $: 使光标移动到行尾

  • S: 删除光标下的字符并进入插入模式(我禁用了)

  • A: 使光标移动到行尾并进入插入模式

  • I: 使光标移动到行首并进入插入模式

  • o: 向下换行

  • O: 向上换行

  • f{char}

    • 使用;键可以跳到下一个
    • 使用,键可以跳到上一个
  • /: 在文档中查找下一处匹配项

  • ?: 在文档中查找上一处匹配项

  • *: 高亮光标出的词并移动到下一处

  • u: 撤销(从进入插入模式开始, 直到返回普通模式为止, 在此期间输入或删除的任何内容都被当成一次修改; 在插入模式中移动光标会重置修改状态)(此功能可以让你控制撤销粒度)

  • 模式下,r可以修改所有列选中的位置

  • !{cmd}: 运行shell命令

  • ea: 在当前单词结尾后添加

  • m{a-zA-Z} `{mark}: 添加标签,回到标签(大写字母是全局标记)

  • ``: 当前文件中上次跳转动作之前的位置

    • %r]``r[: 修改包围的符号,change surround
  • `.: 上次修改的地方

  • gi: 跳转到上次插入的地方

  • ^: 移动光标到行首第一个非空字符处

  • $: 移动光标到行末最后一个非空字符处

  • =: 自动缩进(格式化)

  • dtn: 删除字符n前面的内容

  • zz: 将光标所在行移动到窗口中央

  • 撤销在普通模式下使用u,反撤销使用Ctrl+r

  • 对比两个文件:

    • 在终端下: nvim -d 文件名1 文件名2
    • 在nvim中: :diffsplit 要对比的文件名
  • ctrl+z暂停vim返回shell,shell里做完想做的事,然后fg返回vim

  • %命令允许在一组开、闭括号间跳转,可作用于 ()、{}以及[]

  • daw/caw: 会删除当前单词以及前一个空格

  • /: 对数字执行加/减操作; 10: 对最近的数字加10 (有进制的区别: 0xff + 1 = 0x100)

  • 插入模式下

    • : 删除前一个字符
    • : 删除前一个单词
    • : 删至行首
    • : 切换到普通模式
    • <C-[>: 切换到普通模式
    • : 切换到插入-普通模式(可以执行一个普通模式命令, 执行完后, 马上就又返回到插入模式, eg: zz 调整屏幕显示位置并继续插入)
    • {register}: 粘贴某个寄存器的内容
    • =: 表达式寄存器, 可以做简单的算数表达式
    • r: 修改一个字符
    • R: 进入修改模式,直到回到普通模式
  • 普通模式下

    • J: 把当前行和下一行连接在一起
    • K: 查看处于光标之下的那个单词的手册页
    • : 运行跳转命令时, 会在跳转列表中添加一条记录, 而命令会回到跳转列表的上条记录
  • 可视模式下

    • v: 激活面向字符的可视模式
    • V: 激活面向行的可视模式
    • : 激活面向列块的可视模式
    • gv: 重选上次的高亮区域
    • o: 在可视模式时, o键可以切换光标的端点
    • .: .命令可以重复上一条在可视模式执行的命令
    • r: 在可视模式中使用r键时, 会修改选中的所有字符
    • 块选中时,按c删除选中内容并输入新内容, 在返回普通模式时会对所有行都进行修改(即: 快选中时, 对当前行进行修改时, 会对所有行都进行); 按A/I可在行末/首进行修改
    • : 可以在可视模式及选择模式间切换(选择模式中输入任意可见字符, 此字符会替换所选内容并切换到插入模式. 几乎不用)
  • 命令行模式下

    • tabnew, tabnext, tabprevious, tabclose: 操作tab
    • badd filename, bNext, bdelete: 操作buffer
    • /: 删除至上个单词的开头及行首
    • {start},{end}+action: eg:3,5p (.代表当前行的地址; $代表文件末尾行; %代表当前文件中的所有行)(.,.+3p 会对当前行至下三行进行打印)
    • {range}copy{address}: 复制
    • {range}copy{address}: 移动
    • 在高亮时按下:后执行命令时,会对所有行执行该命令
    • 在行选中模式下,按{:normal 指令}将会对高亮区中的每一行,对其执行普通模式下的指令(实际上就是选中范围{range}再输入normal)
    • @: : 重复上次执行的Ex命令; 在运行过一次@:后,后面就可以用@@命令来重复它
    • 在命令行上也可以用键自动补全命令
    • vim会记录命令行模式中执行过的命令; 也会为查找命令单独保存一份历史记录(使用上下方向键或者/遍历)
    • q: : 可以调出命令行窗口, 此时可以对窗口内的命令进行编辑
    • 正在命令行构建一条Ex命令,做到一半时,意识到需要更强大的编辑能力,此时可以按打开命令行窗口
    • 在命令行模式中,给命令加一个叹号前缀!,就可以调用外部程序
    • read !{cmd} 可以把{cmd}命令的输出读入当前缓冲区中
    • write !{cmd} 把缓冲区内容作为指定{cmd}的标准输入

选取范围

  • a)或ab一对圆括号(parentheses)
  • i)或ib圆括号(parentheses)内部
  • a}或aB一对花括号{braces}
  • i}或iB花括号{braces}内部
  • a] 一对方括号[brackets]
  • i] 方括号[brackets]内部
  • a> 一对尖括号
  • i> 尖括号 内部
  • a’ 一对单引号’single quotes’
  • i’ 单引号’single quotes’内部
  • a” 一对双引号”double quotes”
  • i” 双引号”double quotes”内部
  • a 一对反引号backticks`
  • i 反引号backticks`内部
  • at 一对XML标签tags
  • it XML标签内部
  • iW 当前字串
  • aW 当前字串及一个空格
  • is 当前句子
  • as 当前句子及一个空格
  • ip 当前段落
  • ap 当前段落及一个空行

标记

  • m{a-zA-Z}命令会用选定的字母标记当前光标所在位置. 小写位置标记只在每个缓冲区里局部可见, 而大写位置标记则全局可见. `{mark}命令跳到位置标记所在行, 并把光标置于该行第一个非空白字符上
  • `` 当前文件中上次跳转动作之前的位置
  • `. 上次修改的地方
  • `^ 上次插入的地方
  • `[ 上次修改或复制的起始位置
  • `] 上次修改或复制的结束位置
  • `< 上次高亮选区的起始位置
  • `> 上次高亮选区的结束位置

移动

  • j: 向下移动一个实际行 gj: 向下移动一个屏幕行

  • k: 向上移动一个实际行 gk: 向上移动一个屏幕行

  • 0: 移动到实际行的行首 g0: 移动到屏幕行的行首

  • ^: 移动到实际行的第一个非空白字符 g^: 移动到屏幕行的第一个非空白字符

  • $: 移动到实际行的行尾 g$: 移动到屏幕行的行尾

  • w: 正向移动到下一单词的开头

  • b: 反向移动到当前单词/上一单词的开头

  • e: 正向移动到当前单词/下一单词的结尾

  • ge: 反向移动到上一单词的结尾

  • W、B、E 和gE面向字串移动

查找

  • f{char}: 正向移动到下一个{char}所在之处
  • F{char}: 反向移动到上一个{char}所在之处
  • t{char}: 正向移动到下一个{char}所在之处的前一个字符上
  • T{char}: 反向移动到上一个{char}所在之处的后一个字符上
  • dt{char}: 从当前位置删除到char之前
  • 可视模式下也可使用/进行查找

在文件间跳转

  • 用[count]G命令直接跳到指定的行号也会被当成一次跳转, 但每次向上或向下移动一行则不算. 面向句子的动作及面向段落的动作都算跳转, 但面向字符及面向单词的动作则不算. 用一句话来概括, 可以说大范围的动作命令可能会被当成跳转, 但小范围的动作命令则只能算移动.
  • : 后退
  • : 前进

寄存器

  • Vim的删除、复制与粘贴命令都会用到众多寄存器中的某一个. 可以通过给命令加”{register}前缀的方式指定要用的寄存器. 若不指明,Vim将缺省使用无名寄存器
  • 想把当前单词复制到寄存器a中, 可执行”ayiw, 或者, 可以用”bdd, 把当前整行文本剪切至寄存器b中. 在此之后,既可以输入”ap粘贴来自寄存器a的单词, 也可使用”bp命令粘贴来自寄存器b的一整行文本, 两者互不干扰
  • 当我们使用y{motion}命令时, 要复制的文本不仅会被拷贝到无名寄存器中, 而且也被拷贝到了复制专用寄存器中, 后者可用数字0加以引用. 复制专用寄存器, 顾名思义, 仅当使用y{motion}命令时才会被赋值. 换句话讲, 使用x、s、c{motion}以及d{motion}命令均不会覆盖该寄存器. 如果复制了一些文本, 可以确信该文本会一直保存于寄存器0中, 直到复制其他文本时才会被覆盖. 复制专用寄存器是稳定的, 而无名寄存器是易变的. 尽管diw 命令仍会覆盖无名寄存器, 但不会波及复制专用寄存器. 可以通过输入”0P, 安全地粘贴来自复制专用寄存器中的内容.
  • 用小写字母引用有名寄存器, 会覆盖该寄存器的原有内容, 而换用大写字母的话, 则会将新内容添加到该寄存器的原有内容之后
  • 系统剪贴板(“+)与选择专用寄存器(“*): 如果在外部程序中用剪切或复制命令获取了文本, 就可以通过”+p 命令(或在插入模式下用+)将其粘贴到Vim内部. 相反地, 如果在Vim的复制或删除命令之前加入”+, 相应的文本将被捕获至系统剪贴板
  • X11视窗系统支持另一种被叫作主剪贴板的剪贴板, 它保存着上次被高亮选中的文本, 可以用鼠标中键(如果有的话)把它们粘贴出来. Vim的星号寄存器对应主剪贴板, 可用*号加以引用
  • p命令旨在将寄存器中的文本粘贴到光标之后. 作为补充, Vim也提供了大写的P命令用于将文本插入到光标之前

  • q{char}{changes}q记录宏
  • q键既是“录制”按钮,也是“停止”按钮
  • :reg {char}: 查看指定宏内容
  • 用@{register}命令执行指定寄存器的内容, 也可以用@@来重复最近调用过的宏
  • 输入qa时, Vim将开始录制接下来的按键操作, 并将它们保存到寄存器a中, 这会覆盖该寄存器原有的内容. 如果输入的是qA的话, Vim也会录制按键操作, 但会把它们附加到寄存器a原有的内容之后
  • 当动作命令失败时, 宏将中止执行

模式

  • 通过使用元字符\c与\C,可以覆盖Vim缺省的大小写敏感性设置。小写字母\c 会让查找模式忽略大小写,而大写字母\C则会强制区分大小写。
  • 正则表达式使用\进行转义

替换

  • [range] [substitute]/{pattern}/{string}/[flags]
    • range:
      • 使用可视模式选取范围
      • %: 表示整个文件
      • 1,20: 第1行~第20行
      • .,+2 当前行~往后2行
      • .,$ 当前行~文件尾
    • flags:
      • g: 全局范围内执行
      • c: 可以确认或拒绝每一处修改
      • n: 抑制正常的替换行为, 只是报告本次substitute命令匹配的个数
  • substitute命令要在整个x轴与y轴上执行, 即凭借标志位g处理横轴字符的同时, 使用地址符%处理纵轴的文本行
  • 替换特殊字符
    • \r 插入一个换行符
    • \t 插入一个制表符
    • \ 插入一个反斜杠
    • ~ 使用上一次调用:substitute时的{string}
  • :substitute命令的查找域留空, Vim将使用上一次的查找模式(:%s//{new_word}/g)
  • 通过输入{register}, 可以将寄存器的内容插入到命令行

自动补全

  • 通过这两个组合键, 不仅可以在插入模式下触发Vim的自动补全, 而且还可以用它们在补全列表中反向或正向选择
  • 通过来终止这次自动补全, 其效果是下拉菜单将被清除, 并恢复光标前的文本, 即调用自动补全前所输入的内容
  • 文件名补全

文件

  • :ls 列表的开头有一个数字, 它是在缓冲区创建时由Vim自动分配的编号. 可以用:buffer N 命令直接凭编号跳转到一个缓冲区, 或是用更直观的:buffer {bufname}格式实现同样的功能. {bufname} 只需包含文件路径中足以唯一标识此缓冲区的字符即可
  • :bufdo 命令允许我们在:ls列出的所有缓冲区上执行Ex命令. 不过在实际应用中, 发现:argdo更加实用
  • :wa! 把所有改变的缓冲区写入磁盘
  • :qa! 关闭所有窗口, 摒弃修改无需警告
  • Explore缩写E :edit 只需输入:e
  • Vexplore/Sexplore: 水平或竖直打开目录树
  • :write !{cmd} 命令会把缓冲区的内容作为标准输入 传给指定的{cmd},{cmd} 可以是任何外部程序
  • :w !sudo tee % > /dev/null 普通用户保存root权限文件

install vim

ubuntu

1
2
3
4
5
6
7
8
9
vim:
sudo add-apt-repository ppa:jonathonf/vim
sudo apt update
sudo apt install vim

nvim:
sudo apt-add-repository ppa:neovim-ppa/unstable
sudo apt update
sudo apt install neovim

config nvim

1
2
3
4
5
6
7
8
9
10
11
12
13
sudo pacman -S unzip(要解压一些包) git neovim
# 安装pyright需要安装npm以及python3,这里不需要nodejs
sudo pacman -S npm python3
# copilot需要高版本的nodejs

# telescope
sudo add-apt-repository ppa:x4121/ripgrep
sudo apt update
sudo apt install ripgrep
npm install -g fd-find

# 清除nvim配置
rm -rf ~/.local/share/nvim ~/.cache/nvim ~/.local/state/nvim

.gitignore

1
2
3
4
5
6
7
8
9
10
11
12
# no .a files 
*.a
# but do track lib.a, even though you're ignoring .a files above
!lib.a
# only ignore the TODO file in the current directory, not subdir/TODO
/TODO
# ignore all files in the build/ directory
build/
# ignore doc/notes.txt, but not doc/server/arch.txt
doc/*.txt
# ignore all .pdf files in the doc/ directory
doc/**/*.pdf

patch

生成 patch

git diff

1
2
3
4
#只想 patch Test.java 文件
git diff Test.java > test.patch
# 把所有的修改文件打成 patch
git diff > test.patch

git format-patch

1
2
3
4
5
6
7
8
git format-patch HEAD^       #生成最近的1次commit的patch
git format-patch HEAD^^ #生成最近的2次commit的patch
git format-patch HEAD^^^ #生成最近的3次commit的patch
git format-patch HEAD^^^^ #生成最近的4次commit的patch
git format-patch <r1>..<r2> #生成两个commit间的修改的patch(生成的patch不包含r1. <r1>和<r2>都是具体的commit号)
git format-patch -1 <r1> #生成单个commit的patch
git format-patch <r1> #生成某commit以来的修改patch(不包含该commit)
git format-patch --root <r1> #生成从根到r1提交的所有patch

应用 patch

git am

  • 直接将patch的所有信息打上去,而且不用重新git add和git commit,author也是patch的author而不是打patch的人。
    1
    2
    git am --abort                                 # 当git am失败时,用以将已经在am过程中打上的patch废弃掉(比如有三个patch,打到第三个patch时有冲突,那么这条命令会把打上的前两个patch丢弃掉,返回没有打patch的状态)
    git am --resolved # 当git am失败,解决完冲突后,这条命令会接着打patch

git apply

  • git apply并不会将commit message等打上去,打完patch后需要重新git add和git commit。

检查 patch 情况

1
2
git apply --stat 0001-limit-log-function.patch  # 查看patch的情况
git apply --check 0001-limit-log-function.patch # 检查patch是否能够打上,如果没有任何输出,则说明无冲突,可以打上

冲突处理

方式一(编辑冲突的 patch 文件)

  1. 根据git am失败的信息,找到发生冲突的具体patch文件,然后用命令git apply –reject ,强行打这个patch,发生冲突的部分会保存为.rej文件(例如发生冲突的文件是a.txt,那么运行完这个命令后,发生conflict的部分会保存为a.txt.rej),未发生冲突的部分会成功打上patch
  2. 根据.rej文件,通过编辑该patch文件的方式解决冲突
  3. 废弃上一条am命令已经打了的patch:git am –abort
  4. 重新打patch:git am ~/patch-set/*.patchpatch

方式二(编辑冲突的源码)

  1. 根据git am失败的信息,找到发生冲突的具体patch文件,然后用命令git apply –reject ,强行打这个patch,发生冲突的部分会保存为.rej文件(例如发生冲突的文件是a.txt,那么运行完这个命令后,发生conflict的部分会保存为a.txt.rej),未发生冲突的部分会成功打上patch
  2. 根据.rej文件,通过编辑发生冲突的code文件的方式解决冲突
  3. 将该patch涉及到的所有文件(不仅仅是发生冲突的文件)通过命令git add 添加到工作区中
  4. 告诉git冲突已经解决,继续打patch: git am –resolved (git am –resolved 和 git am –continue是一样的)

强制打 patch

1
git apply --reject xxx.patch

一、标题

在想要设置为标题的文字前面加#来表示
一个#是一级标题,二个#是二级标题,以此类推。支持六级标题。

注:标准语法一般在#后跟个空格再写文字,貌似简书不加空格也行。

示例:

1
2
3
4
5
6
# 这是一级标题
## 这是二级标题
### 这是三级标题
#### 这是四级标题
##### 这是五级标题
###### 这是六级标题

效果如下:

这是一级标题

这是二级标题

这是三级标题

这是四级标题

这是五级标题
这是六级标题

二、字体

  • 加粗

要加粗的文字左右分别用两个*号包起来

  • 斜体

要倾斜的文字左右分别用一个*号包起来

  • 斜体加粗

要倾斜和加粗的文字左右分别用三个*号包起来

  • 删除线

要加删除线的文字左右分别用两个~~号包起来

示例:

1
2
3
4
**这是加粗的文字**
*这是倾斜的文字*`
***这是斜体加粗的文字***
~~这是加删除线的文字~~

效果如下:

这是加粗的文字
这是倾斜的文字
这是斜体加粗的文字
这是加删除线的文字


三、引用

在引用的文字前加>即可。引用也可以嵌套,如加两个>>三个>>>n个…
貌似可以一直加下去,但没神马卵用

示例:

1
2
3
>这是引用的内容
>>这是引用的内容
>>>>>>>>>>这是引用的内容

效果如下:

这是引用的内容

这是引用的内容

这是引用的内容

四、分割线

三个或者三个以上的 - 或者 * 都可以。

示例:

1
2
3
4
---
----
***
*****

效果如下:
可以看到,显示效果是一样的。



五、图片

语法:

1
2
3
4
![图片alt](图片地址 ''图片title'')

图片alt就是显示在图片下面的文字,相当于对图片内容的解释。
图片title是图片的标题,当鼠标移到图片上时显示的内容。title可加可不加

六、超链接

语法:

1
2
[超链接名](超链接地址 "超链接title")
title可加可不加

示例:

1
2
[简书](http://jianshu.com)
[百度](http://baidu.com)

效果如下:
简书
百度


七、列表

无序列表

语法:
无序列表用 - + * 任何一种都可以

1
2
3
4
5
- 列表内容
+ 列表内容
* 列表内容

注意:- + * 跟内容之间都要有一个空格

效果如下:

  • 列表内容
  • 列表内容
  • 列表内容

有序列表

语法:
数字加点

1
2
3
4
5
1. 列表内容
2. 列表内容
3. 列表内容

注意:序号跟内容之间要有空格

效果如下:

  1. 列表内容
  2. 列表内容
  3. 列表内容

列表嵌套

上一级和下一级之间敲三个空格即可

  • 一级无序列表内容
    • 二级无序列表内容
    • 二级无序列表内容
    • 二级无序列表内容
  • 一级无序列表内容
    1. 二级有序列表内容
    2. 二级有序列表内容
    3. 二级有序列表内容
  1. 一级有序列表内容
    • 二级无序列表内容
    • 二级无序列表内容
    • 二级无序列表内容
  2. 一级有序列表内容
    1. 二级有序列表内容
    2. 二级有序列表内容
    3. 二级有序列表内容

八、表格

语法:

1
2
3
4
5
6
7
8
9
10
11
表头|表头|表头
---|:--:|---:
内容|内容|内容
内容|内容|内容

第二行分割表头和内容。
- 有一个就行,为了对齐,多加了几个
文字默认居左
-两边加:表示文字居中
-右边加:表示文字居右
注:原生的语法两边都要用 | 包起来。此处省略

示例:

1
2
3
4
5
姓名|技能|排行
|--|:--:|--:|
|刘备|哭|大哥|
|关羽|打|二哥|
|张飞|骂|三弟|

效果如下:

姓名 技能 排行
刘备 大哥
关羽 二哥
张飞 三弟

九、代码

语法:

1
esc键下三个点就行

示例:

单行代码

1
`create database hero;`

代码块

1
2
3
4
5
6
(```)
function fun(){
echo "这是一句非常牛逼的代码";
}
fun();
(```)

效果如下:

单行代码

1
create database hero;

代码块

1
2
3
4
function fun(){
echo "这是一句非常牛逼的代码";
}
fun();