#!/bin/bash # 工作区文件操作脚本 # 用法: # zq-file.sh read <文件路径> 读取文件内容 # zq-file.sh write <文件路径> <内容> 写入/覆盖文件 # zq-file.sh append <文件路径> <内容> 追加内容到文件末尾 # zq-file.sh prepend <文件路径> <内容> 在文件开头插入内容 # zq-file.sh replace <文件路径> <旧内容> <新内容> 替换文本(精确匹配) # zq-file.sh replace-all <文件路径> <旧内容> <新内容> 替换所有匹配 # zq-file.sh remove <文件路径> <要删除的内容> 删除特定文本 # zq-file.sh insert-before <文件路径> <标记行> <内容> 在某行之前插入 # zq-file.sh insert-after <文件路径> <标记行> <内容> 在某行之后插入 # zq-file.sh delete <文件路径> 删除文件 # zq-file.sh new <文件路径> <内容> 新建文件 # # 脚本会自动执行: # 1. 从NAS同步到本地 # 2. 执行操作 # 3. Git提交 # 4. 同步到NAS set -e ZQ_DIR="$HOME/work/zq" NAS_REMOTE="nas-webdav:/home/Drive/工作区/张青/工作区/" EXCLUDE_ARGS="--exclude .git/ --exclude .gitignore" LOG_FILE="$HOME/work/zq-file.log" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } usage() { cat << 'EOF' 工作区文件操作脚本 用法: zq-file.sh read <文件路径> 读取文件内容 zq-file.sh write <文件路径> "内容" 写入/覆盖文件 zq-file.sh append <文件路径> "内容" 追加内容到文件末尾 zq-file.sh prepend <文件路径> "内容" 在文件开头插入内容 zq-file.sh replace <文件路径> "旧内容" "新内容" 替换文本(精确匹配,首次) zq-file.sh replace-all <文件路径> "旧内容" "新内容" 替换所有匹配的文本 zq-file.sh remove <文件路径> "要删除的内容" 删除特定文本 zq-file.sh insert-before <文件路径> "标记行" "内容" 在标记行之前插入 zq-file.sh insert-after <文件路径> "标记行" "内容" 在标记行之后插入 zq-file.sh delete <文件路径> 删除文件 zq-file.sh new <文件路径> "内容" 新建文件 注意: - 文件路径是相对于 ~/work/zq 的路径,或绝对路径 - 内容支持多行,用 $'\n' 表示换行,或使用 $'...\n...' - replace/remove 操作会精确匹配文本(包括空格和换行) - 操作会自动:从NAS同步 → 执行操作 → Git提交 → 同步到NAS 示例: # 读取文件 zq-file.sh read 10.项目/README.md # 写入内容 zq-file.sh write 00.收件箱/test.md "Hello World" # 追加内容(支持多行) zq-file.sh append 00.收件箱/01-速记本.md "- 新的速记"$'\n'"- 另一条" # 在文件开头插入 zq-file.sh prepend 30.资源/notes.md "# 标题" # 替换文本 zq-file.sh replace 10.项目/README.md "旧内容" "新内容" # 替换多行文本 zq-file.sh replace 10.项目/README.md $'第一行\n第二行' $'新第一行\n新第二行' # 删除特定文本 zq-file.sh remove 10.项目/README.md "要删除的行" # 在某行之前插入 zq-file.sh insert-before 10.项目/README.md "## 章节" "这是新插入的内容" # 在某行之后插入 zq-file.sh insert-after 10.项目/README.md "## 章节" "这是新插入的内容" # 删除文件 zq-file.sh delete 40.归档/old-file.md # 新建文件 zq-file.sh new 30.资源/new-note.md "# 新笔记" EOF } # 确保在正确目录 cd "$ZQ_DIR" || { log "错误:无法进入 $ZQ_DIR"; exit 1; } # 解析参数 ACTION="$1" shift || { usage; exit 1; } # 处理文件路径(支持相对路径和绝对路径) resolve_path() { local path="$1" if [[ "$path" = /* ]]; then # 绝对路径 echo "$path" else # 相对路径,基于 ~/work/zq echo "$ZQ_DIR/$path" fi } # 从NAS同步到本地 sync_from_nas() { log "步骤1: 从NAS同步到本地..." rclone sync "$NAS_REMOTE" "$ZQ_DIR/" $EXCLUDE_ARGS --log-level INFO >> "$LOG_FILE" 2>&1 || { log "警告:从NAS同步失败,继续执行..." } log "同步完成" } # Git提交 git_commit() { local message="$1" log "步骤3: Git提交..." git add -A >> "$LOG_FILE" 2>&1 git commit -m "$message" >> "$LOG_FILE" 2>&1 || log "没有变更需要提交" git push origin main >> "$LOG_FILE" 2>&1 || log "警告:Git push失败" } # 同步到NAS sync_to_nas() { log "步骤4: 同步到NAS..." rclone sync "$ZQ_DIR/" "$NAS_REMOTE" $EXCLUDE_ARGS --log-level INFO >> "$LOG_FILE" 2>&1 || { log "错误:同步到NAS失败" exit 1 } log "同步完成" } # 执行操作 case "$ACTION" in read) FILE_PATH=$(resolve_path "$1") REL_PATH="${FILE_PATH#$ZQ_DIR/}" sync_from_nas log "步骤2: 读取文件 $REL_PATH" if [[ ! -f "$FILE_PATH" ]]; then log "错误:文件不存在" exit 1 fi cat "$FILE_PATH" log "完成!" ;; write) FILE_PATH=$(resolve_path "$1") REL_PATH="${FILE_PATH#$ZQ_DIR/}" CONTENT="$2" sync_from_nas log "步骤2: 写入文件 $REL_PATH" mkdir -p "$(dirname "$FILE_PATH")" printf '%s' "$CONTENT" > "$FILE_PATH" git_commit "写入: $REL_PATH" sync_to_nas log "完成!" ;; append) FILE_PATH=$(resolve_path "$1") REL_PATH="${FILE_PATH#$ZQ_DIR/}" CONTENT="$2" sync_from_nas log "步骤2: 追加到文件 $REL_PATH" if [[ ! -f "$FILE_PATH" ]]; then log "错误:文件不存在" exit 1 fi printf '\n%s' "$CONTENT" >> "$FILE_PATH" git_commit "追加: $REL_PATH" sync_to_nas log "完成!" ;; prepend) FILE_PATH=$(resolve_path "$1") REL_PATH="${FILE_PATH#$ZQ_DIR/}" CONTENT="$2" sync_from_nas log "步骤2: 在文件开头插入 $REL_PATH" if [[ ! -f "$FILE_PATH" ]]; then log "错误:文件不存在" exit 1 fi # 创建临时文件,先写入新内容,再写入原内容 TEMP_FILE=$(mktemp) printf '%s\n' "$CONTENT" > "$TEMP_FILE" cat "$FILE_PATH" >> "$TEMP_FILE" mv "$TEMP_FILE" "$FILE_PATH" git_commit "开头插入: $REL_PATH" sync_to_nas log "完成!" ;; replace) FILE_PATH=$(resolve_path "$1") REL_PATH="${FILE_PATH#$ZQ_DIR/}" OLD_TEXT="$2" NEW_TEXT="$3" sync_from_nas log "步骤2: 替换文本 $REL_PATH" if [[ ! -f "$FILE_PATH" ]]; then log "错误:文件不存在" exit 1 fi # 检查旧文本是否存在 if ! grep -qF "$OLD_TEXT" "$FILE_PATH"; then log "错误:未找到要替换的文本" exit 1 fi # 使用 Python 进行精确替换(只替换第一次匹配) python3 << PYEOF import sys with open("$FILE_PATH", 'r', encoding='utf-8') as f: content = f.read() old_text = """$OLD_TEXT""" new_text = """$NEW_TEXT""" if old_text not in content: print("错误:未找到要替换的文本", file=sys.stderr) sys.exit(1) # 只替换第一次出现 new_content = content.replace(old_text, new_text, 1) with open("$FILE_PATH", 'w', encoding='utf-8') as f: f.write(new_content) PYEOF git_commit "替换: $REL_PATH" sync_to_nas log "完成!" ;; replace-all) FILE_PATH=$(resolve_path "$1") REL_PATH="${FILE_PATH#$ZQ_DIR/}" OLD_TEXT="$2" NEW_TEXT="$3" sync_from_nas log "步骤2: 替换所有匹配 $REL_PATH" if [[ ! -f "$FILE_PATH" ]]; then log "错误:文件不存在" exit 1 fi # 检查旧文本是否存在 if ! grep -qF "$OLD_TEXT" "$FILE_PATH"; then log "错误:未找到要替换的文本" exit 1 fi # 使用 Python 进行全局替换 python3 << PYEOF with open("$FILE_PATH", 'r', encoding='utf-8') as f: content = f.read() old_text = """$OLD_TEXT""" new_text = """$NEW_TEXT""" new_content = content.replace(old_text, new_text) with open("$FILE_PATH", 'w', encoding='utf-8') as f: f.write(new_content) PYEOF git_commit "替换全部: $REL_PATH" sync_to_nas log "完成!" ;; remove) FILE_PATH=$(resolve_path "$1") REL_PATH="${FILE_PATH#$ZQ_DIR/}" TEXT_TO_REMOVE="$2" sync_from_nas log "步骤2: 删除文本 $REL_PATH" if [[ ! -f "$FILE_PATH" ]]; then log "错误:文件不存在" exit 1 fi # 使用 Python 删除特定文本 python3 << PYEOF import sys with open("$FILE_PATH", 'r', encoding='utf-8') as f: content = f.read() text_to_remove = """$TEXT_TO_REMOVE""" if text_to_remove not in content: print("错误:未找到要删除的文本", file=sys.stderr) sys.exit(1) new_content = content.replace(text_to_remove, '') with open("$FILE_PATH", 'w', encoding='utf-8') as f: f.write(new_content) PYEOF git_commit "删除文本: $REL_PATH" sync_to_nas log "完成!" ;; insert-before) FILE_PATH=$(resolve_path "$1") REL_PATH="${FILE_PATH#$ZQ_DIR/}" MARKER="$2" CONTENT="$3" sync_from_nas log "步骤2: 在标记行之前插入 $REL_PATH" if [[ ! -f "$FILE_PATH" ]]; then log "错误:文件不存在" exit 1 fi # 使用 Python 在标记行之前插入 python3 << PYEOF import sys with open("$FILE_PATH", 'r', encoding='utf-8') as f: content = f.read() marker = """$MARKER""" new_content = """$CONTENT""" if marker not in content: print("错误:未找到标记行", file=sys.stderr) sys.exit(1) # 在第一次出现的标记之前插入 result = content.replace(marker, new_content + '\n' + marker, 1) with open("$FILE_PATH", 'w', encoding='utf-8') as f: f.write(result) PYEOF git_commit "前插: $REL_PATH" sync_to_nas log "完成!" ;; insert-after) FILE_PATH=$(resolve_path "$1") REL_PATH="${FILE_PATH#$ZQ_DIR/}" MARKER="$2" CONTENT="$3" sync_from_nas log "步骤2: 在标记行之后插入 $REL_PATH" if [[ ! -f "$FILE_PATH" ]]; then log "错误:文件不存在" exit 1 fi # 使用 Python 在标记行之后插入 python3 << PYEOF import sys with open("$FILE_PATH", 'r', encoding='utf-8') as f: content = f.read() marker = """$MARKER""" new_content = """$CONTENT""" if marker not in content: print("错误:未找到标记行", file=sys.stderr) sys.exit(1) # 在第一次出现的标记之后插入 result = content.replace(marker, marker + '\n' + new_content, 1) with open("$FILE_PATH", 'w', encoding='utf-8') as f: f.write(result) PYEOF git_commit "后插: $REL_PATH" sync_to_nas log "完成!" ;; delete) FILE_PATH=$(resolve_path "$1") REL_PATH="${FILE_PATH#$ZQ_DIR/}" sync_from_nas log "步骤2: 删除文件 $REL_PATH" if [[ ! -f "$FILE_PATH" ]]; then log "错误:文件不存在" exit 1 fi trash "$FILE_PATH" 2>/dev/null || rm "$FILE_PATH" git_commit "删除: $REL_PATH" sync_to_nas log "完成!" ;; new) FILE_PATH=$(resolve_path "$1") REL_PATH="${FILE_PATH#$ZQ_DIR/}" CONTENT="$2" sync_from_nas log "步骤2: 新建文件 $REL_PATH" if [[ -f "$FILE_PATH" ]]; then log "错误:文件已存在" exit 1 fi mkdir -p "$(dirname "$FILE_PATH")" printf '%s' "$CONTENT" > "$FILE_PATH" git_commit "新建: $REL_PATH" sync_to_nas log "完成!" ;; -h|--help|help) usage ;; *) log "错误:未知操作 '$ACTION'" usage exit 1 ;; esac