分类 默认分类 下的文章

在index-tts2中看到了一个新的包管理器,uv
https://github.com/index-tts/index-tts/tree/tts2dev

pip install -U uv
uv sync --all-extras
uv sync --all-extras --default-index "https://mirrors.aliyun.com/pypi/simple"

uv sync --all-extras --default-index "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"


# download IndexTTS-2

uv tool install "huggingface_hub[cli]"

hf download IndexTeam/IndexTTS-2 --local-dir=checkpoints

# or 

uv tool install "modelscope"

modelscope download --model IndexTeam/IndexTTS-2 --local_dir checkpoints

我懂你遇到的情况了——你用 snap 安装的 Docker,结果系统里找不到 systemctl 命令,没法像普通 apt/yum 安装的 Docker 那样用 systemctl start docker 来启动服务。
这其实不是 Docker 本身的问题,而是 snap 安装方式systemctl 管理方式 的机制不一样。


1. 为什么 snap 安装的 Docker 没有 systemctl

  • systemctlsystemd 提供的服务管理命令,用于管理 /etc/systemd/system//lib/systemd/system/ 下的系统服务。
  • snap 包 是一个沙箱化的应用分发格式,它会自带运行所需的依赖,不会像 apt 安装那样往 /etc/systemd/system/ 注册 systemd 服务。
  • 所以用 snap 装的 Docker,默认不会生成 docker.service,自然也就不能用 systemctl 来启动/停止。

2. snap 安装的 Docker 怎么启动

snap 应用有自己的启动方式:

① 查看 Docker snap 服务状态

snap services docker

可能会看到类似:

Service         Startup  Current
docker.dockerd  enabled  active

② 启动/停止/重启 Docker(snap 方式)

sudo snap start docker
sudo snap stop docker
sudo snap restart docker

③ 设置开机自启

sudo snap enable docker

④ 关闭开机自启

sudo snap disable docker

3. 如果想继续用 systemctl 管理 Docker

那就不要用 snap 安装,而是用官方 apt 仓库安装 Docker:

# 卸载 snap 版 Docker
sudo snap remove docker

# 更新 apt 索引
sudo apt update

# 安装依赖
sudo apt install ca-certificates curl gnupg

# 添加 Docker 官方 GPG 密钥
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# 添加 Docker apt 仓库
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo $VERSION_CODENAME) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 安装 Docker
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 用 systemctl 启动并开机自启
sudo systemctl enable --now docker

# 验证
docker --version
sudo docker run hello-world

这样安装的 Docker 就会有 /lib/systemd/system/docker.service,可以正常用:

sudo systemctl start docker
sudo systemctl stop docker
sudo systemctl restart docker

4. 总结

  • snap 安装 Docker:轻量、隔离,但不走 systemd,所以没有 systemctl 管理命令,要用 snap start/stop/restart docker
  • apt 安装 Docker:会注册 systemd 服务,能继续用 systemctl 管理。
  • 如果你习惯用 systemctl,建议换回 apt 安装;如果只是临时用 Docker,保留 snap 版也行,只是启动方式不一样。

如果你愿意,我可以帮你一步步把 snap 版 Docker 换成 apt 版,并顺手帮你配置好 Docker 免 sudo 运行,这样以后就可以直接用 docker run ... 而不用加 sudo。
要帮你换成 apt 版吗?这样就能用 systemctl 了。

WSL搭建的服务,通过母机端口提供给局域网访问

netsh interface portproxy add v4tov4 listenport=8080 listenaddress=0.0.0.0 connectport=80 connectaddress=127.0.0.1

要获取标题数量对应的列名称(如 A、B、C...),可以通过标题数量计算对应的 Excel 列名。Excel 列名采用字母编号(A-Z, AA-AZ, BA-BZ...),可以编写一个转换函数来实现数字到列名的转换。
以下是结合标题数量获取对应列名称的示例代码:

// 数字转Excel列名(如1->A, 2->B, 26->Z, 27->AA)
function numberToColumn($number) {
    $column = '';
    while ($number > 0) {
        $number--;
        $column = chr(ord('A') + $number % 26) . $column;
        $number = (int) ($number / 26);
    }
    return $column;
}

control/interface.php

function formSubArray($data): array|string
{
    if (is_array($data)) {
        $result = [];
        foreach ($data as $key => $value) {
            if (is_array($value)) {
                $result[$key] = formSubArray($value);
            } else {
                $result[$key] = formSub($value);
            }
        }
    } else {
        $result = formSub($data);
    }
    return $result;
}


/*表单提交数据整理和防sql注入*/
function formSub($data): string
{
    $data = trim($data);                              //消除两边的空格
    $data = htmlentities($data, ENT_QUOTES, "utf-8"); //字符转换为 HTML 实体。
    //对单引号(')双引号(")反斜杠(\)NULL进行转义
    return addslashes($data);
}

/cmd.php

#! /usr/bin/env php
<?php
$_SERVER['HTTP_HOST'] = '';
$_SERVER['REQUEST_URI'] = '';
require_once __DIR__ . '/shell/baseCommand.php';

class CommandRunner
{
    /** @var string 控制器名称 */
    protected $controllerName;

    /** @var string 动作名称 */
    protected $actionName;

    /** @var array 命令行参数 */
    protected $params = [];

    /** @var array 控制器映射 */
    protected $controllerMap = [];

    /** @var array 颜色定义 */
    protected $colors = [
        'reset'  => "\033[0m",
        'red'    => "\033[31m",
        'green'  => "\033[32m",
        'yellow' => "\033[33m",
        'blue'   => "\033[34m",
        'purple' => "\033[35m",
        'cyan'   => "\033[36m",
    ];

    /**
     * 构造函数
     */
    public function __construct()
    {
        $this->initControllerMap();
    }

    /**
     * 初始化控制器映射
     * 子类应重写此方法来定义控制器映射
     */
    protected function initControllerMap()
    {
        // 示例控制器映射
        $this->controllerMap = [];
        //遍历shell目录下的所有Command结尾的类文件,加入控制器映射中
        $dir = __DIR__ . '/shell';
        $files = scandir($dir);
        foreach ($files as $file) {
            if (is_file($dir . '/' . $file) && str_ends_with($file, 'Command.php')) {
                $controllerName = substr($file, 0, -11);
                $this->controllerMap[$controllerName] = "shell\\{$controllerName}Command";
            }
        }
    }

    /**
     * 运行命令行脚本
     */
    public function run()
    {
        try {
            $this->parseArguments();
            $this->validateCommand();
            $this->executeAction();
        } catch (Exception $e) {
            $this->error($e->getMessage());
            exit(1);
        }
    }

    /**
     * 解析命令行参数
     */
    protected function parseArguments()
    {
        global $argv;

        // 至少需要 CONTROL 和 ACTION 两个参数
        if (count($argv) < 3) {
            throw new InvalidArgumentException("缺少 CONTROL 和 ACTION 参数");
        }

        // 获取 CONTROL 和 ACTION
        $this->controllerName = strtolower(formSubArray($argv[1]));
        $this->actionName     = strtolower(formSubArray($argv[2]));

        // 解析其他参数
        for ($i = 3; $i < count($argv); $i++) {
            $arg = $argv[$i];

            if (str_starts_with($arg, '--')) {
                $param = substr($arg, 2);
                if (str_contains($param, '=')) {
                    list($key, $value) = explode('=', $param, 2);
                    //参数安全处理
                    $this->params[$key] = formSubArray($value);
                } else {
                    // 无值参数视为布尔值 true
                    $this->params[$param] = true;
                }
            } else {
                // 位置参数
                $this->params[] = $arg;
            }
        }
    }

    protected function getControllerName($controllerName=null)
    {
        $controllerClass = $this->controllerMap[$controllerName?:$this->controllerName];
        $controllerClassFile = __DIR__ . "/{$controllerClass}.php";
        $controllerClassFile = str_replace(['\\','/'], DIRECTORY_SEPARATOR, $controllerClassFile);
        // 检查控制器类是否存在
        if (!class_exists($controllerClass)) {
            if (file_exists($controllerClassFile)) require_once $controllerClassFile;
            else {
                throw new InvalidArgumentException("控制器文件不存在: {$controllerClassFile}");
            }
            if (!class_exists($controllerClass)) {
                throw new InvalidArgumentException("控制器类不存在: {$controllerClass}");
            }
        }
        return $controllerClass;
    }

    protected function getActionName()
    {
        return 'action' . ucfirst($this->actionName);
    }

    /**
     * 验证命令是否有效
     */
    protected function validateCommand()
    {
        // 检查控制器是否存在
        if (!isset($this->controllerMap[$this->controllerName])) {
            throw new InvalidArgumentException("未知的控制器: {$this->controllerName}");
        }

        $controllerName = $this->getControllerName();

        // 检查动作方法是否存在
        $controller   = new $controllerName();
        $actionMethod = $this->getActionName();

        if (!method_exists($controller, $actionMethod)) {
            throw new InvalidArgumentException("控制器 {$this->controllerName} 中不存在动作: {$this->actionName}");
        }

        // 检查方法是否为 public
        $reflection = new ReflectionMethod($controllerName, $actionMethod);
        if (!$reflection->isPublic()) {
            throw new InvalidArgumentException("动作方法 {$actionMethod} 不是 public 的");
        }
    }

    /**
     * 执行控制器动作
     */
    protected function executeAction()
    {
        $controllerClass = $this->getControllerName();
        $controller      = new $controllerClass();
        $actionMethod    = $this->getActionName();

        // 获取方法参数信息
        $reflection   = new ReflectionMethod($controllerClass, $actionMethod);
        $methodParams = $reflection->getParameters();

        // 准备传递给方法的参数
        $callParams = [];

        foreach ($methodParams as $param) {
            $paramName = $param->getName();

            // 检查参数是否在命令行参数中提供
            if (array_key_exists($paramName, $this->params)) {
                $callParams[] = $this->params[$paramName];
            } // 检查是否有默认值
            elseif ($param->isOptional()) {
                $callParams[] = $param->getDefaultValue();
            } // 必需参数缺失
            else {
                throw new InvalidArgumentException("缺少必需参数: {$paramName}");
            }
        }

        // 执行动作方法
        $result = $reflection->invokeArgs($controller, $callParams);

        // 输出结果(如果有)
        if ($result !== null) {
            $this->output($result);
        }
    }

    /**
     * 输出信息
     * @param string|array $message 要输出的信息
     * @param string $color 颜色名称
     */
    protected function output($message, $color = 'reset')
    {
        if (is_array($message)) {
            $message = json_encode($message, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
        }

        echo $this->colors[$color] . $message . $this->colors['reset'] . PHP_EOL;
    }

    /**
     * 输出错误信息
     * @param string $message 错误信息
     */
    protected function error($message)
    {
        $this->output("错误: {$message}", 'red');
    }

    /**
     * 显示帮助信息
     */
    public function showHelp()
    {
        $this->output("命令行工具使用帮助", 'blue');
        $this->output("用法: php cmd.php CONTROL ACTION [--参数1=值1 --参数2=值2 ...]", 'blue');
        $this->output("", 'reset');
        $this->output("可用控制器:", 'blue');

        foreach ($this->controllerMap as $name => $class) {
            $this->output("  {$name} ({$class})", 'yellow');
            $this->showControllerActions($name, $class);
        }
    }

    /**
     * 显示控制器的可用动作
     * @param string $controllerName 控制器名称
     * @param string $controllerClass 控制器类名
     */
    protected function showControllerActions($controllerName, $controllerClass)
    {
        $controllerClass = $this->getControllerName($controllerName);
        $reflection = new ReflectionClass($controllerClass);
        $methods    = $reflection->getMethods(ReflectionMethod::IS_PUBLIC);

        $actions = [];
        foreach ($methods as $method) {
            if (str_starts_with($method->getName(), 'action') && $method->getName() !== 'action') {
                $actionName = substr($method->getName(), 6);
                $params     = [];

                foreach ($method->getParameters() as $param) {
                    $paramInfo = '$' . $param->getName();
                    if ($param->isOptional()) {
                        $default    = $param->getDefaultValue();
                        $defaultStr = is_string($default) ? "'{$default}'" : $default;
                        $paramInfo  .= " = {$defaultStr}";
                    }
                    $params[] = $paramInfo;
                }

                $actions[] = "    {$actionName}(" . implode(', ', $params) . ")";
            }
        }

        if (!empty($actions)) {
            $this->output("    可用动作:", 'cyan');
            foreach ($actions as $action) {
                $this->output($action, 'purple');
            }
            $this->output("", 'reset');
        }
    }
}

// 创建并运行命令行工具
$command = new CommandRunner();

// 检查是否有 --help 或 -h 参数
if (in_array('--help', $argv) || in_array('-h', $argv)) {
    $command->showHelp();
} else {
    $command->run();
}
    

shell/baseCommand.php

<?php
namespace shell;

require_once 'control/interface.php';

use control\interfaces;

class baseCommand
{
    use interfaces;

    /** @var array 颜色定义 */
    protected $colors = [
        'reset'  => "\033[0m",
        'red'    => "\033[31m",
        'green'  => "\033[32m",
        'yellow' => "\033[33m",
        'blue'   => "\033[34m",
        'purple' => "\033[35m",
        'cyan'   => "\033[36m",
    ];

    public string $date;
    public string $time;
    public function __construct()
    {
        $this->date = date('Y-m-d');
        $this->time = date('Y-m-d H:i:s');
        $this->init();
    }

    /**
     * 运行例子测试
     * php .\cmd.php base test --name=zhangsan [--age=32]
     * @param $name
     * @return void
     */
    public function actionTest($name, $age=23)
    {
        $this->output("hello world: {$name}, age: {$age}", 'green');
    }

    /**
     * 输出信息
     * @param string|array $message 要输出的信息
     * @param string $color 颜色名称
     */
    protected function output($message, $color = 'reset')
    {
        if (is_array($message)) {
            $message = json_encode($message, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
        }

        echo $this->colors[$color] . $message . $this->colors['reset'] . PHP_EOL;
    }

    /**
     * 输出错误信息
     * @param string $message 错误信息
     */
    protected function error($message)
    {
        $this->output("错误: {$message}", 'red');
    }

    /**
     * 显示帮助信息
     */
    public function showHelp()
    {
        $this->output("帮助信息", 'blue');
    }

    // 自动加载类
    public function loadClass($className)
    {
        $className = str_replace('shell\\', '', $className);
        if (stristr(PHP_OS, 'LINUX')) {
            $className = str_replace('\\', '/', $className);
        }
        $paths = [
            "{$className}.php",
            "control/{$className}.php",
            "shell/{$className}.php",
        ];
        foreach ($paths as $fileName) {
            $path = serverRoot . $fileName;
            if (file_exists($path)) {
                require_once $fileName;
                return;
            }
        }
    }

}