MoreRSS

site iconszqp修改

偏旅行、个人生活记录的博客。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

szqp的 RSS 预览

让NAS自动复制存储卡上的文件

2025-08-30 19:31:32

早期用过群晖的NAS,有个很好的功能是自带SD读卡器,可以直接插卡,然后按一下按键,就能自动把SD卡上的文件复制到NAS的硬盘上。群晖上有个USB Copy的套件,也可以实现自动插卡后就复制卡上的文件。

前一段自己组了个NAS,用了飞牛系统,飞牛的备份策略还是比较弱,虽然能备份外置USB读卡器或者外置USB硬盘的文件,但没有自动插卡后执行备份的功能,必须手工操作,于是就打算结合AI编程,自己写个脚本实现这个功能。

飞牛还有很多NAS系统的底层实际上都是Linux系统,所以基本上方法是类似的。

其实核心是利用了Udev,它可以管理挂载的设备,相关说明:https://www.reactivated.net/writing_udev_rules.html

通过它,实现插入USB设备后,能自动执行脚本。

实现起来也很简单,就3步

  1. 编写一个脚本,复制SD卡内的文件到制定目录
  2. 把这个脚本配置为一个Systemd的Service
  3. 编写Udev的规则,当插入卡时,执行Service。

为什么要把脚本变成Service而不是直接执行脚本,是因为有个坑是Udev调用脚本有个默认超时时间,而如果复制文件的时间较长,超时了,脚本就会自动中断,这个也是我一开始测试的时候遇到的坑,AI编程工具没有意识到这个问题,而是遇到自动退出问题后反复的增加了各种监控和测试脚本。看来AI也不是万能的。

文件复制脚本

文件复制脚本其实内容可以很简单,插上SD卡以后,看一下SD卡自动挂载的路径就可以了,飞牛的话,是会固定挂载在  /vol00/MassStorageClass  目录下,每次都一样,当然,如果你的NAS同时插了好几个USB设备比如U盘、读卡器、USB硬盘之类的,那么可能路径会不一样,自己用SSH登录飞牛系统df命令看一下就可以了。

而目标路径,一般飞牛的话第一个储存空间是/vol1/1000/ ,第二个就是/vol2/1000/ ,以此类推,看你想把SD卡的文件复制到哪个就选具体的路径就可以了。

脚本里可以用cp命令直接复制 比如 cp /vol00/MassStorageClass/* /vol1/1000/backup/  ,也可以用rsync命令  rsync -av  /vol00/MassStorageClass/ /vol1/1000/backup/  ,rsync命令还可以加 --exclude-from 参数,后面跟一个文本文件,里面写上你希望排除不复制的文件,比如 .* 表示不复制 .开头的隐藏文件等等。

当然,还可以加各种功能比如统计复制时间,统计复制了多少个文件,复制完成以后删除卡上的文件等功能,建议再结合 Server酱或者pushover、gotify之类的工具,在复制完成后发一个推送消息到手机,这样就能及时知道复制完了。以上功能都可以找一个AI编程工具,说清楚需求自动生成即可。

比如可以用类似如下的提示词(等待5秒是怕刚插入SD卡时还没完成挂载就运行复制命令,会报错):

写一个Linux下的BASH脚本,脚本能够使用rsync命令将/vol00/MassStorageClass/下的文件均复制到 /vol1/1000/backup/,需要在复制前检查源目录是否存在,不存在的话等待5秒重试。需要检查目标目录空间是否足够。统计复制了多少个文件及耗时,复制成功后将源目录下的文件均删除。

以上提示词可以根据需要修改。

Systemd的Service

这一步比较常规,就是在 /etc/systemd/system/ 目录下创建一个Service文件,比如usbcopy.service  内容类似如下:

[Unit]
Description=功能描述

[Service]
ExecStart=/usr/bin/bash /root/usbcopy.sh (替换为你脚本的路径)
Restart=on-failure

[Install]
WantedBy=multi-user.target

然后执行 systemctl start usbcopy  试一下能否成功运行,可以的话就OK了。

编写Udev规则

这一步也比较简单,就是在 /etc/udev/rules.d/  目录下创建一个规则文件,比如usbcopy.rule ,写入类似如下内容:

ACTION=="add", SUBSYSTEM=="block", KERNEL=="sd[a-z][0-9]*", ENV{ID_BUS}=="usb", ENV{ID_FS_TYPE}!="", RUN+="/bin/systemctl start usbcopy.service"

这里我是匹配了所有设备 

KERNEL=="sd[a-z][0-9]*"

当然你可以根据需要直接写你机器上SD卡对应的设备,比如sda 就是 KERNEL=="sda"   这个通过df命令也可以直接看到。

其实意思就是当机器上增加了sda设备后,执行 /bin/systemctl start usbcopy.service 也就是刚才增加的Systemd服务。

添加后,刷新一下规则

sudo udevadm control --reload-rules
sudo udevadm trigger

然后插个SD卡测试一下就可以了。

注意事项

如果脚本加入了删除文件的功能,记得多测试几次后再正式使用,避免复制没成功又把有用的文件删除了。

文件复制的示例代码

以下示例代码仅供参考,实现从/vol00/MassStorageClass/vol2/1000/photos/sdbackup的文件复制,在/home/szqp/autocopy/exclude-list.txt中写入了排除不复制的文件列表。统计复制文件数量和时间,完成后删除源目录下文件,并通过Server酱发送提醒信息。

因为基本是AI生成的,虽然在我的NAS上能用,但不代表你的也可以,要自行修改相关配置,不能直接运行:

#!/bin/bash

# USB自动复制脚本
# 功能:从固定USB挂载点复制文件到指定目录


# 日志文件
LOG_FILE="/var/log/usb_autocopy.log"
SOURCE_DIR="/vol00/MassStorageClass"
TARGET_DIR="/vol2/1000/photos/sdbackup"

# 信息发送函数
sc_send() {
    local text=$1
    local desp=$2
    local key="你Server酱的key"

    postdata="text=$text&desp=$desp"
    opts=(
        "--header" "Content-type: application/x-www-form-urlencoded"
        "--data" "$postdata"
    )

    # 判断 key 是否以 "sctp" 开头,选择不同的 URL
    if [[ "$key" =~ ^sctp([0-9]+)t ]]; then
        # 使用正则表达式提取数字部分
        num=${BASH_REMATCH[1]}
        url="https://${num}.push.ft07.com/send/${key}.send"
    else
        url="https://sctapi.ftqq.com/${key}.send"
    fi

    # 使用动态生成的 url 发送请求
    result=$(curl -X POST -s -o /dev/null -w "%{http_code}" "$url" "${opts[@]}")
    echo "$result"
}

# 记录日志函数
log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}

# 检查USB挂载点是否存在
check_usb_mount() {
    # 等待挂载点出现,最多等待10秒
    for i in {1..10}; do
        if [ -d "$SOURCE_DIR" ] && [ "$(ls -A "$SOURCE_DIR" 2>/dev/null)" ]; then
            log_message "发现USB挂载点: $SOURCE_DIR (第${i}次检查)"
            return 0
        fi
        if [ $i -eq 1 ]; then
            log_message "等待USB挂载点出现: $SOURCE_DIR"
        fi
        sleep 1
    done
    
    log_message "USB挂载点不存在或为空: $SOURCE_DIR"
    return 1
}

# 检查USB挂载点
log_message "检查USB挂载点..."
if ! check_usb_mount; then
    log_message "错误: USB挂载点不可用或为空"
    exit 1
fi

# 使用固定的挂载点
MOUNT_POINT="$SOURCE_DIR"
log_message "使用USB挂载点: $MOUNT_POINT"

# 验证挂载点的可访问性
if [ ! -r "$MOUNT_POINT" ]; then
    log_message "错误: 挂载点 $MOUNT_POINT 不可读"
    exit 1
fi

# 创建目标目录(如果不存在)
if [ ! -d "$TARGET_DIR" ]; then
    mkdir -p "$TARGET_DIR"
    if [ $? -ne 0 ]; then
        log_message "错误: 无法创建目标目录 $TARGET_DIR"
        exit 1
    fi
    log_message "创建目标目录: $TARGET_DIR"
fi

# 生成唯一的子目录名(基于时间戳)
TIMESTAMP=$(date '+%Y%m%d_%H%M%S')
COPY_DIR="$TARGET_DIR/usb_backup_${TIMESTAMP}"

# 创建复制目录
mkdir -p "$COPY_DIR"
if [ $? -ne 0 ]; then
    log_message "错误: 无法创建复制目录 $COPY_DIR"
    exit 1
fi

log_message "开始复制文件到: $COPY_DIR"

# 检查源目录是否为空
if [ -z "$(ls -A "$MOUNT_POINT" 2>/dev/null)" ]; then
    log_message "警告: USB设备为空,没有文件需要复制"
    COPY_RESULT=0
    SOURCE_FILE_COUNT=0
else
    # 计算源文件数量和大小
    SOURCE_FILE_COUNT=$(find "$MOUNT_POINT" -type f 2>/dev/null | wc -l)
    SOURCE_SIZE=$(du -sb "$MOUNT_POINT" 2>/dev/null | cut -f1)
    log_message "检测到 $SOURCE_FILE_COUNT 个文件,总大小: $(echo $SOURCE_SIZE | awk '{printf "%.2f MB", $1/1024/1024}')"
    
    # 检查目标目录可用空间
    AVAILABLE_SPACE=$(df "$TARGET_DIR" | tail -1 | awk '{print $4}')
    AVAILABLE_BYTES=$((AVAILABLE_SPACE * 1024))
    
    if [ "$SOURCE_SIZE" -gt "$AVAILABLE_BYTES" ]; then
        ERROR_MSG="磁盘空间不足。需要: $(echo $SOURCE_SIZE | awk '{printf "%.2f MB", $1/1024/1024}'),可用: $(echo $AVAILABLE_BYTES | awk '{printf "%.2f MB", $1/1024/1024}')"
        log_message "错误: $ERROR_MSG"
        sc_send "USB文件复制失败" "$ERROR_MSG"
        rm -rf "$COPY_DIR"
        exit 1
    fi
    
    # 记录复制开始时间
    COPY_START_TIME=$(date +%s)
    
    # 使用rsync进行文件复制
    if command -v rsync >/dev/null 2>&1; then
        log_message "使用rsync进行文件复制"
        rsync -av --exclude-from='/home/szqp/autocopy/exclude-list.txt' "$MOUNT_POINT/" "$COPY_DIR/" 2>&1
        COPY_RESULT=$?
        COPY_METHOD="rsync"
    else
        log_message "使用cp进行文件复制"
        cp -r "$MOUNT_POINT/"* "$COPY_DIR/" 2>&1
        COPY_RESULT=$?
        COPY_METHOD="cp"
    fi
    
    # 计算复制耗时
    COPY_END_TIME=$(date +%s)
    COPY_DURATION=$((COPY_END_TIME - COPY_START_TIME))
fi

if [ $COPY_RESULT -eq 0 ]; then
    log_message "文件复制成功完成"
    
    # 发送成功通知
    if [ $SOURCE_FILE_COUNT -gt 0 ]; then
        # 格式化复制时间
        if [ $COPY_DURATION -ge 60 ]; then
            DURATION_TEXT="${COPY_DURATION}秒 ($(($COPY_DURATION / 60))分$(($COPY_DURATION % 60))秒)"
        else
            DURATION_TEXT="${COPY_DURATION}秒"
        fi
        
        SUCCESS_MSG="成功复制 $SOURCE_FILE_COUNT 个文件,耗时: $DURATION_TEXT。总大小: $(echo $SOURCE_SIZE | awk '{printf "%.2f MB", $1/1024/1024}')。目标目录: $COPY_DIR"
        log_message "复制了 $SOURCE_FILE_COUNT 个文件,耗时 $DURATION_TEXT"
        
        # 删除USB设备上的源文件
        log_message "开始删除USB设备上的源文件..."
        if rm -rf "$MOUNT_POINT/"* 2>/dev/null; then
            log_message "成功删除USB设备上的源文件"
            sc_send "USB文件复制成功,清理完成" "$SUCCESS_MSG 。文件清理完成"
        else
            ERROR_MSG="删除USB设备上的源文件失败"
            log_message "错误: $ERROR_MSG"
            sc_send "USB文件复制成功,清理失败" "$SUCCESS_MSG 。错误:$ERROR_MSG"
        fi
    else
        sc_send "USB设备为空" "USB设备中没有文件需要复制"
    fi
else
    ERROR_MSG="文件复制失败,错误代码: $COPY_RESULT"
    log_message "$ERROR_MSG"
    sc_send "USB文件复制失败" "$ERROR_MSG。复制方法: $COPY_METHOD。源目录: $MOUNT_POINT。目标目录: $COPY_DIR"
fi

log_message "脚本执行完成"
exit 0

© 2025, QP. 版权所有.

尝试AI编程

2025-06-23 20:11:12

这两年最火的可能就是AI了,从ChatGPT开始,各种AI应用层出不穷。趁着周末的时间,我也试了一下用AI编程。

我原来以为的AI编程还是让AI回答一些编程中的问题,或者给一些参考示例代码,结果没想到现在的AI编程已经进化到了另一个层次了。

我试用的是字节出的Trae,分国内版和国外版,主要区别是能使用的大模型的不同,以及国内版目前好像还没开始收费。我试了一下Claude、Gemini、国内的豆包还有deekseek这几个模型,总的来说Claude的效果最好。

现在的AI编程,你只需要输入需求,就能自动生成完整可用的全部代码并且可以执行,我已经很久没碰Python了,Flet框架更是以前没用过,但花了几个小时就做出来了 机柜图生成器 这么个小工具,而整个过程中我只做了非常少的修改,99%的代码都是靠AI自动生成的。

而且AI对需求的理解力也很强。很多时候我不需要说的特别精确或者特别详细,AI就会自动补充完整。比如我只是说我要增加一个能输入机柜高度的选项,AI就会自动在页面上生成机柜高度的输入框,提示文字,并把页面上输入内容和程序处理逻辑中关于机柜高度的部分关联。

当代码出现问题时,AI还能自动读取输出结果和报错信息,去分析错误原因并自动修改。很多时候代码产生了错误,让AI自己纠正就可以了。

当然,AI还并不是完美的,也会出现一些比较傻的错误,比如把变量的大小写弄错,或者弄错了代码的缩进格式等等,但基本上是非常可用的了。整体能力我觉得替代一个初级的程序员应该完全没问题。

以后可能遇到问题,真的不需要找一个工具去解决,而是可以自己做一个工具去解决了。

© 2025, QP. 版权所有.

我的摄影器材

2025-02-26 19:54:52

今年偷了个懒,没有去做年终总结,不过趁有时间,把手上的摄影器材做了个初步的整理。

增加了个统计展示页面

虽然我是个器材党,不过不算不知道自己买了这么多器材,虽然有些已经出了,但大部分还在手上,特别是去年喜欢上了胶卷相机,收了不少老胶卷相机。可惜胶卷没拍多少,攒了3卷冲洗了一次,还大部分翻车了。

为了统计器材,还试用了一下各类线上的多维表服务。老牌的Airtable功能还是最强的,可惜很多功能要付费,国内的很多一般用也没问题,但要共享视图嵌入到网站很多都不行或者效果不好,最后还是用了Teable,功能不多但够用,而且视图嵌入网站的效果比较简洁。

以后试试写写我用过的这些摄影器材吧。

© 2025, QP. 版权所有.

复活 Bose Soundlink Mini 2 音箱

2024-07-09 13:50:45

家里有个Bose Soundlink Mini 2的蓝牙音箱,之前有一段时间没用,结果就无法充电了。接电源,电源灯一直红灯闪烁,也无法开机。

一开始以为是电池过放坏了,看了一下淘宝也有电池卖,不贵,但是要自己焊接线,有点麻烦。

无意中网上搜了一下,居然找到了个解决方法,不用换电池。

用数据线把音箱接电脑上,注意要用能传输数据的MicroUSB线,有些MicroUSB线只能充电不能传输数据是不行的。

接好后,去Bose的网站升级音箱的固件:

Bose Software Updater

如果提示已经是最新的固件了,顺序按“A、D、V、上键、下键” 这五个键,就会出现可以刷新固件了。

刷新到75%左右的时候,应该已经可以听到音箱的提示音了,然后电源灯也会变成长亮的红灯。等待固件刷新完成。

刷新完成后记得不能马上拔掉数据线,要让音箱继续插在电脑上冲一会儿电。接电脑充电比较慢,可能需要半个小时以上,电源灯变成黄色长亮,就基本说明修复成功了。之后可以接充电头充电,这样比较快。音箱也可以正常开机使用了。

© 2024, QP. 版权所有.

台北见闻

2024-05-27 08:13:51

因公到台北出差,上一次还是2013年自由行来台湾玩了一圈,在这样一个时期,还是比较难得的。

记录一下见闻。

1 申请的商务签注。台湾那边申请入台证比较快,大陆这边申请赴台签注比较慢,要先准备很多材料到当地的台湾办公室申请,大概经过一个多月会拿到赴台批复,然后凭借这个批复去出入境那边申请台湾通行证和赴台签注。

2 给台湾办公室的材料里除了邀请函和公司简介,还得列明赴台的日程表,批复里会根据行程表写明允许你赴台的时间。不过真正的赴台签注又不会规定的这么具体,只有三个月一次,一年多次等几种类型,还不清楚如果没有按批复里写赴台时间去了会怎么样。

3 10年后再到台湾,感觉物价变化不大,日常吃饭和购物的价格感觉跟大陆一线城市差不多,甚至还更便宜一点。比如写字楼密集的地方,中午一份快餐基本在100台币左右,也就是20多人民币。奶茶大概六七十到100台币左右一杯。热炒店,类似大陆的大排档,一个菜大概300到500台币左右,比大陆要贵。

4 街上有共享单车,但没有大陆共享单车密集,更类似以前政府运营那种单车,有固定的桩,取车还车都得到固定桩,有app可以查固定桩的位置,也可以扫码或者刷卡借车。

5 公交车和捷运,10年前来的时候,基本上博爱座一般人是不坐的,经常捷运上满人博爱座也空着。但这次来,好像也是经常有年轻人会坐博爱座了。

6 实体店和夜市生意看起来不错,游客挺多日本人和韩国人。欧美游客好像不是特别多。

7 有外卖,Uber eat和foodpanda,据说后者还要被前者收购了。但我注册Uber eat一直不成功,刚注册完账号就被锁定,foodpanda可以使用,点外卖感觉送的比大陆还快一些,但选择不多,很多餐厅还是不支持网上点餐。另外就是除了夜市之外,很多餐厅会比较早关门。

8 加班不严重,大部分人5点半到6点左右就下班了。我去的地方科技公司比较多,鸿海,英伟达,亚马逊,英飞凌之类的都在附近,晚上也看不到很多加班的人,基本上7点以后就都走光,街上没什么人了。

9 庙很多,不少还在闹市区,很多人路过庙门口也会拜一拜。

10 本地人特别喜欢排队凑热闹,不管卖什么的,都喜欢凑过去排队,好像不排队买一个就吃亏了。夜市特别明显,有些摊位的队伍会长达几百米。

11 支付宝和微信有一些连锁超市和便利店可以用,台北故宫的文创店也能用,但总体上能用的地方还是不多。本地人用apple pay,line pay,街口支付什么的,用悠游卡和信用卡的也多,现金也占很大一部分,很多小店和摊贩基本只能用现金。

12 网上购物也有,但比如虾皮,街上能看到一些虾皮的自提点,但总得来说没有像在大陆一样那么夸张的网上购物比例,大部分还是通过线下买。

13 公交捷运主要是悠游卡之类的实体卡,没留意能不能扫码,好像没见到有人扫码进捷运站或者坐公交。

公交上车刷一次卡,下车刷一次卡。在站台等车需要招手,不然公交车不会停,下车也是要按铃,不然公交车到站也不会停。

14 还没去吃什么特别高级的餐厅,就普通餐厅和夜市来说,也还好,口味跟福建差不多,但有些食物会跟大陆不太一样,比如很多地方夜市都有的臭豆腐,炸的和汤的都有,也不辣,汤的臭豆腐更类似于大陆的卤豆腐。还有麻婆豆腐也是完全不辣,类似于肉沫豆腐的做法。

另外就是大陆很火的烤串,台湾比较少,吃过一次也不太好吃,可能适合当地人的口味吧。

15 台北的建筑,高楼大厦不如大陆一线城市多,10层左右的楼挺多的。建筑整体比较偏老旧,可能老城市都差不多吧,类似大陆很多城市的老城,不过倒是比较干净,这方面感觉台北有点像日本的城市,当然发达程度台北比东京大阪还是差不少。

16 台北对宠物还挺宽容的,狗狗是能上捷运的,只要放在类似婴儿车的宠物车里推进站就可以。

17 台湾的发票可以抽奖,所以绝大部分地方买东西都会打印发票给你。你也可以选择提供“载具”,也就是一个电子发票账号,会给你开电子发票存进载具,自动兑奖。



更新一些图片,近期都是台北的雨季,经常下雨,而且我也没去什么特别的景点,大家凑合看吧。

© 2024, QP. 版权所有.

用电视看视频

2024-04-26 10:27:12

以前其实比较少用Youtube和Bilibili,觉得手机上看视频并不太舒服。后来试了试在电视上看,意外发现很完美,特别是YouTube的4k,很不错。有很多评测、旅拍之类的视频,在手机上看没什么感觉,在电视上看还挺惊艳的。

YouTube推荐使用Smarttube这个APP,非常适合在电视上使用,能同步账号收藏,还能去广告。

SmartTubeNext

Bilibili推荐使用BBLL,而不是官方的APP,也很方便,不过不开视频不能播放4k。

xiaye13579/BBLL: 一个第三方哔哩哔哩客户端,A third-party bilibili client。 (github.com)

另外,如果想看电视台直播节目,可以试试myTV,自带直播源,没有广告,速度也很快,就是得经常更新。

lizongying/my-tv: 我的电视 电视直播软件,安装即可使用 (github.com)

© 2024, QP. 版权所有.