0%

引言

深度使用 Cursor 有一段时间了,老实说,一开始我对所谓「AI 颠覆编程」的论调是带有几分警惕的,但当它实实在在地帮我完成了几次繁琐的代码重构后,我发现了一件令我头皮发麻的事:

它不仅理解我代码语义层面的逻辑,它甚至洞悉了我为什么这么设计。

那一刻,我突然有种高山流水遇知音的感觉:有了这样一个懂我的神器,我离传说中的 10 倍程序员,真的只差一个 Cursor 么?

代码风格评价-01

实战派:它是如何融入我日常工作流的?

经过这段时间的磨合,Cursor 已经渗透到了我的日常开发中,成为了名副其实的提效利器。真正让它封神的,绝不是常规的一问一答式聊天,而是它对代码上下文的深度介入。 接下来,我结合几个高频实战场景,给大家分享一些常规对话之外的深度使用技巧。

局部手术刀:内联聊天 (CMD + K)

这个功能极其适合处理局部代码的“疑难杂症”。选中一段代码,按下 CMD + K,就能直接对这段代码下达指令。我最近高频使用的场景包括:

  • 骨架填充:我非常看重对代码主导权的把控。面对复杂的代码逻辑,我习惯先用伪代码搭好核心“骨架”,再让 Cursor 去填充实现细节。这种“我定方向,AI执行”的模式,让我在享受极速编码的同时,又始终牢牢把控着代码的架构与灵魂。因为我深知:初级使用者被 AI 牵着走,而资深开发者始终把控着代码的架构和灵魂。
  • 精准“微创”修改:很多时候我只需要调整某几段逻辑,最怕AI“自作聪明”把整个文件全改一遍。通过选中特定代码块下发指令,它就只在划定的范围内“指哪打哪”,绝不误伤其他无辜代码。
  • 自动补全注释:选中那些“当年只顾着爽,忘了写注释”的工具函数,一键生成清晰的说明。
  • 代码瘦身:把几个过于冗长、复杂度极高的函数扔给它,它总能给出极其优雅的重构方案。
  • 边界防御:写完一段核心逻辑后,让它帮忙检查是否有遗漏的边界情况和异常处理。

谋定而后动:计划功能 (Plan)

在做稍微复杂的模块改造前,我会先用它的“计划功能”。反复和 AI 沟通重构的步骤,这不仅是让AI清楚我的意图,更是一个帮我自己理清思路的过程。思路清晰了,写代码不过是顺水推舟。

cursor-plan

双剑合璧:IDEA 与 Cursor 的无缝切换

虽然 Cursor 很强,但作为一个重度依赖 IntelliJ IDEA 的开发者,我深知 IDEA 在项目工程化管理、复杂重构以及对开发工具链的深度集成方面,依然具有不可替代性。我无法、也不想完全脱离 IDEA。

如何让这两者共存?我找到了两个堪称神器的插件,完美打通了我的工作流:

1. Switch2Cursor (IDEA 插件) 在 IDEA 中安装后,只需一个快捷键,就能瞬间在 Cursor 中打开当前正在开发的项目或特定文件。遇到需要 AI 强力介入的代码,一键穿梭过去。

Switch2Cursor-logo

Switch2Cursor-overview

如下是快捷键

image-20260402142822290

2. Switch2IDEA (Cursor 插件) 同理,在 Cursor 中利用 AI 快速生成、修改完代码后,利用此插件可以立刻切回 IDEA,继续享受熟悉的编译和调试环境。

Switch2IDEA-logo

Switch2IDEA-cursor截图

这两个插件,让 Cursor 真正成为了我挂在 IDEA 旁边的一个“最强外挂”。

守卫控制权:与 Git 的天作之合

AI 写代码速度虽快,但我绝不会让它脱离掌控。为了防止AI“夹带私货”或产生意料之外的修改,Git 成了我最重要的一道防线。 每次 Cursor 替我大面积生成或重构代码后,我都会习惯性地利用 Git 快速识别出它到底动了哪些文件、改了哪几行逻辑。这种基于代码 Diff 的审视,本质上就是我跟 AI 之间的一场 Code Review

边界拓宽者

这些年微服务的兴起,让前后端的分工越来越明确,作为一名擅长跟服务器和数据库打交道的后端开发,以往面对非后端领域的实现细节时,总免不了有些底气不足。但 Cursor 出色的跨语言理解能力,给了我跨越边界的底气。AI 帮我拆掉了不同技术栈之间的那堵墙,让我偶尔也能顺畅地客串一把“全栈工程师”。

杂活终结者:告别“体力活”

渐渐地,我开始把那些看似简单但极度消耗精力的小活全部外包给它:

  • 环境与命令:帮我快速生成繁杂的终端操作命令。
  • 质量保证:生成完善的单元测试用例。
  • 快速上手:面对一个庞大的老工程,用它帮我快速定位代码片段,解释我不熟悉的祖传代码。
  • 文字校验:校验技术博客的标点、错别字。

灵魂拷问:我是进化了,还是被驯化了?

Cursor 确实好用,但有时又觉得它好像一点点把我“干废了”。

看着屏幕上一行行由 AI 飞速生成的优雅代码,我的确感受到了效率的狂飙。但静下心来,一种隐隐的担忧随之而来:我好像得到了一些,又好像失去了很多。我究竟是在进化,还是在被逐渐驯化?

当一个 AI 模型能以专家级的水平、极低的成本处理掉 70% 的知识型和体力型编码工作时,我们必须要重新审视一个问题:在 AI 时代,程序员不可替代的核心竞争力到底是什么?

如果敲击键盘写下 for 循环不再是门槛,那么审查代码(Code Review)和掌控全局架构的能力,就成了关键。当代码并非由你亲手敲下时,你需要极其敏锐的嗅觉,才能在一大片看似完美的 AI 代码中,捕捉到隐藏在业务逻辑深处的致命 Bug。

结语

回到文章开头的那个问题:我离 10 倍程序员真的只差一个 Cursor 么?

我的答案是:Cursor 可以给你提供 10 倍的产出速度,但如果你没有驾驭和审查这些产出的能力,它也可以给你带来 10 倍的 Bug。

AI 可以帮你干活,可以为你提供无数种方案,但它永远无法替你做决定。你依然要依靠自己的专业认知去拍板,依然要为最终合并到主分支的代码负全责。

五年前,我们需要能看懂代码;五年后,面对汹涌而来的大模型,这个要求依然没变,甚至要求更高了。

大浪淘沙,工具永远在变,但技术人的内核不该变。


微信端的朋友也可关注我的公众号

qrcode-12cm

引言:Git 命令行里的小烦恼

大多数人使用 Git,都是在 IDE 里完成的,而我偏偏喜欢在命令行里敲命令,通过命令行可以更深刻地理解 Git 的操作逻辑,但随着时间久了,我也不得不承认:Git 的命令和参数实在太多、太难记。每次切分支、合并、提交都要手动输入一长串命令。

所以我一直在寻找,看看有没有一种方式,既保留命令行的纯粹,又能简化 Git 命令的繁琐。一个偶然的机会,我发现了一个恰到好处的插件——Oh My Zsh 的 Git 插件。那一刻的感觉,简直就是相见恨晚

Zsh 的隐藏宝藏:你不知道的效率神器

用过 Mac 的朋友都知道,系统默认的shell是 zsh,而 Oh My Zsh 是一个让 zsh 更强大的配置框架。它内置了很多高效插件,其中最让我惊喜的就是——git 插件。启用后,原本冗长的 Git 命令,立刻变得简洁、优雅、高效

更妙的是,这个插件是 Oh My Zsh 自带 的,无需额外安装。你只需要在 .zshrc 文件中添加一行配置即可启用:

1
plugins=(... git)

一行命令的魔法:别名让 Git 更顺手

Oh My Zsh 的 Git 插件,其实是一个集合了上百条别名(alias)的配置。
它把常见的 Git 命令浓缩成简短易记的缩写,让命令行使用变得轻盈又顺手。

github地址:https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/git

gitPlugin

下面是我最常用的一些命令缩写:

常用别名 等价命令 功能说明
g git git
gst git status 查看仓库状态
ga git add 添加变更
gco git checkout 切换分支
gcb git checkout -b 创建并切换分支
gb git branch 分支操作
gcmsg git commit -m 提交信息
gp git push 推送代码
gf git fetch 拉取更新(不合并)
gl git pull 拉取更新
gm git merge 合并

写在最后:简单就在身边

回想起来,我曾经在各种效率工具、终端主题之间反复折腾,想找到那个能让我“更高效”的完美方案。结果发现,好用的工具其实就在身边,而我却一直在寻找!Oh My Zsh 的 Git 插件就像那个被忽略的老朋友,安静地待在 plugins 目录里,直到有一天我真正启用了它,蓦然回首感慨万千。其实,没必要挑来挑去,用好身边的工具,先把事干起来,才是正道。

如果你用 Mac、用 zsh、也用 Git,那我真心建议你试试这个插件!


微信端的朋友也可关注我的公众号

qrcode-12cm

引言

本文将介绍一款用于迁移 Elasticsearch 数据的实用工具 —— elasticdump。它专为在不同 Elasticsearch 实例之间导出和导入数据而设计,非常适合小规模的数据迁移、备份以及测试等场景。

elasticdump

elasticdump简介

elasticdump 是一个命令行工具,主要用于将 Elasticsearch 的索引数据导出(dump)为 JSON 文件或从 JSON 文件导入到 Elasticsearch。它支持迁移 Mapping(映射)、Data(数据)。

GitHub 地址(官方文档):
https://github.com/elasticsearch-dump/elasticsearch-dump

安装方法

elasticdump 是一个基于 Node.js 的命令行工具,因此在安装时需要使用 npm 命令。

1
npm install elasticdump -g

具体使用

本节将通过一个具体示例,演示如何从 es1 实例中导出数据,并将其导入到 es2 实例中。

定义变量

1
2
es1_input="http://IP:PORT/index"
es2_output="http://IP:PORT/index"

步骤1: 从 es1 实例导出数据

导出列类型

1
2
3
4
elasticdump \
--input="${es1_input}" \
--output=./my_index_mapping.json \
--type=mapping

说明:在 Elasticsearch 中,导出”列类型”相当于导出数据库中的表结构。如果不迁移这些字段映射(mapping),Elasticsearch 会自动推断字段类型,这可能导致类型不准确,进而引发查询或索引异常。因此,建议在迁移数据时一并迁移字段映射,以确保数据结构的一致性。

导出数据

1
2
3
4
elasticdump \
--input="${es1_input}" \
--output=./my_index.json \
--type=data

说明1: 导出的数据是 jsonline 格式,一行一条,每条是一个 json 格式的数据。

说明2: 上述命令适用于 Linux 或 macOS 系统,使用 \ 作为续行符。如果你使用的是 Windows 操作系统,则在命令提示符(CMD)中执行命令时需要将续行符改为 ^

1
2
3
4
elasticdump ^
--input="${es1_input}" ^
--output=my_index.json ^
--type=data

步骤2: 将数据导入 es2 实例

导入列类型

1
2
3
4
elasticdump \
--input=my_index_mapping.json \
--output="${es2_output}" \
--type=mapping

细心的读者可能已经注意到,这里的 input 和 output 与步骤1中正好相反:此处的 input 是本地的 JSON 文件,而 output 则是 Elasticsearch 实例。

导入数据

1
2
3
4
elasticdump \
--input=my_index.json \
--output="${es2_output}" \
--type=data

总结

Elasticdump 是一款简单却功能强大的 Elasticsearch 数据迁移工具。通过几条简洁的命令,就可以轻松实现对 Elasticsearch 数据的导出与导入。无论是数据迁移、备份,还是测试,它都能成为你的得力助手。


微信端的朋友也可关注我的公众号

qrcode-12cm

引言

对于开发人员来说树形结构是一种非线性结构,与数组、栈、队列等线性结构相比,复杂度更高。要深入理解和掌握树形结构,亲自绘制树形结构示意图是一种有效的方法,正所谓想要做到心中有“树”,需要自己动手实践。本文介绍一款高效的绘图工具Graphviz,并演示2个使用 Graphviz 绘制树形结构的例子,同时文章还会推荐一些相关的工具,帮助读者更容易地入门。

Graphviz简介

Graphviz ,是Graph Visualization Software的缩写,是由AT&T实验室精心打造的开源工具包,它在生成各种图形表示方面应用广泛。Graphviz采用DOT语言描述图表,并通过布局引擎解析脚本,自动完成脚本的布局, 此外,它还支持多种丰富的导出格式,包括SVG和PDF等。

我对Graphviz的喜爱源于其自动布局的特性,它真正实现了“Graph as code”的理念,即像编写代码一样绘制图形。Graphviz的强大之处在于其“所思即所得”(WYTIWYG,what you think is what you get)的工作方式,这与传统的“所见即所得”(WYSIWYG,what you see is what you get)工具截然不同。普通的所见即所得工具依赖于鼠标拖拽操作,而使用Graphviz时,我的主要职责是编写DOT脚本,只需关注图形中各元素之间的关系,无需分心于布局问题,这让我从排版的繁琐中解放出来。在日常开发工作中,这一点尤为重要,我们希望在构思代码的同时,能够专注于数据结构本身,快速根据我们的思路生成示意图,而不必时刻考虑排版问题。这样的工作方式让我们能够更加专注于工作本身,将注意力集中在问题的核心上。

我的实践

本节演示2个用Graphviz画树形结构的例子,让大家对Graphviz先有个直观的认识。

实践1 树形结构示意图

我接到了一个关于树形结构的开发任务,需要将数据按照省,市,区县的结构组织成树形结构, 借助Grapihviz可以很方便的绘制一个树形结构示意图,方便开发者沟通交流,效果图如下。

OrgTree-preview

树形结构图对应的源码如下,可以看到只需要定义节点及节点之间的关系,不需要关心布局。

OrgTree-codesnippet

实践2 双向链接树形结构示意图

一般的树形结构从父节点可以找到子节点,但是从子节点找不到父节点,我需要构造一个双向链接的树形结构,也即从子节点也可以找到父节点,例如我想知道市辖区属于哪个地市,则需要通过子节点不断向上遍历直到找到合适的父节点。 下图是用Graphivz绘制的示意图,可以看到父子节点之间的箭头是双向的。

OrgMultiLinkTree-preview

如下是对应的源码,这里箭头的指向为双向。

OrgMultiLineTree-codesnippet

工具推荐

前几节介绍了Graphviz以及使用它的原因, 并且展示了我用它画树形结构的实例,本节介绍一些具体的工具,方便大家入门。

推荐1 Graphviz 命令行工具

Graphviz其实是一个命令行工具,使用它之前需要先安装,不同的操作系统安装方式不同,具体可查看官网 https://graphviz.org/

  • 如果是mac可通过homebrew或者port安装
  • 如果是windows可直接下载安装包
  • 如果是linux根据不同的发行版用不同的包管理软件安装 如aptyum

如下是用Graphviz命令将dot源文件生成图片的命令示例

1
dot -Tpng input.dot -o output.png

推荐2 vscode插件

Graphviz 是一个强大的命令行工具,但使用起来可能不太方便。幸运的是,VSCode 提供了一些插件,可以显著改善用户体验,使使用 Graphviz 更加直观和友好,下面介绍一些好用的插件。

  1. Graphviz language support for visual studio code

提供语法高亮

vscode-Graphviz-language-support-for-visual-studio-code

  1. Graphviz Preview

可以快速预览或者导出graphviz文件,这样就不需要记忆具体的命令了。

vscode-Graphviz-Preview

推荐3 python接口

很多编程语言会集成 Graphviz 绘图工具,PyGraphviz是Graphviz的Python接口,允许开发者使用Python代码与Graphviz进行交互,实现图形的创建、布局、可视化和分析

官网 https://github.com/pygraphviz/pygraphviz

总结

通过掌握基本的 DOT 语言和 Graphviz 工具,用户可以轻松创建和自定义自己的图形,满足常见的可视化需求。正所谓一图胜千言,不论是在学术研究、软件开发还是日常工作中,Graphviz 都能够提供极大的便利和帮助,赶快实践起来,体验它的强大功能吧!


微信端的朋友也可关注我的公众号

qrcode-12cm

引言

我们知道shell中的命令都是串行执行的,如果想要充分利用服务器资源,减小等待时间, 让程序并行运行就需要些技巧了, 本文分享一些shell并行运行程序的技巧,下面会用代码演示并行执行3个任务,并等待所有任务都执行完成后再执行后续的逻辑。

代码演示

准备工作

在做整体的演示之前,先写一个程序模拟任务运行的过程,之后演示时会用到该程序。

code-task

该程序名叫task.sh,其会休眠几秒模拟任务运行的过程,其有2个参数

  • 第一个参数是任务名称
  • 第二个参数是要休眠的时长(单位秒),相当于模拟程序运行了多少秒

调用方式如下

1
2
# 启动一个名为task1的任务, 运行10秒后结束
./task.sh task1 10

串行执行

shell中的命令都是串行执行的,下面这段代码比较典型,在启动程序的脚本中非常常见,大家应该比较熟悉。

如下依次启动3个任务, task1 运行10秒, task2 运行20秒,task3运行30秒

code-test1

test1执行结果如下

run-test1

可以看到任务是串行执行的, task1运行了10秒,task2运行了20秒,task3运行了30秒,总共运行了60秒,这个符合我们的预期。

并行执行

之前讲了shell中的命令都是串行执行的,如果想要充分利用服务器资源,减小等待时间, 让程序并行运行该如何做呢?

这里并不希望大量改造程序,希望用最小的代价,让程序并行运行, 下面提供一种思路 使用&和wait改造启动脚本即可,不需要大量改造程序。

基础知识铺垫

知识点1:**& 让程序后台运行**

在shell脚本中当我们需要把一个任务放在后台运行时,通常我们会使用&符号, 此时主进程会继续往下执行,而子进程会在后台启动运行。

知识点2:wait命令 在shell脚本“多进程”执行模式下,起到一些特殊控制的作用

× wait命令用来阻塞当前进程的执行,直至指定的子进程执行结束后,才继续执行。如果wait后面不带任何的进程号或作业号,那么wait会阻塞当前进程的执行,直至当前进程的所有子进程都执行结束后,才继续执行

× wait命令一个很重要用途就是用在shell并行编程中,可以在shell脚本中启动多个后台进程(使用&),然后调用wait命令,等待所有后台进程都运行完毕后,主进程再继续向下执行。

并行改造1

有了上面的知识铺垫,下面再看一段代码(参见 test2),其和test1的区别是增加了&, 让任务后台运行。

code-test2

执行结果如下

run-test2

可以看到3个任务虽然是并行执行的,但总运行时长是0秒, 也即主进程并没有等待3个任务运行完就结束了,相当于失去了对子进程的控制。

并行改造2

下面再看一段代码(参见test3), 其在test2的基础上更进一步,在末尾加了wait命令,其会等待上面3个任务都执行完才会执行后面的命令

code-test3

test3的运行结果如下,可以看到总运行时长是30秒,从时间上可以看出其是等运行时间最长的任务结束后才执行后面的逻辑,也即等所有任务都运行结束后才会执行后面的逻辑。

run-test3

这就是我想要的结果,比较贴合实际情况,在子任务运行完成后,主程序还要做一些收尾工作,所以要等所有子任务都执行完才能执行后续的逻辑。

结语

通过上述的改造, 可以大大的提高程序运行效率,在完成业务需求的同时,还可以充分利用主机资源,减少等待时间。

该技巧抽取自笔者真实项目中的实践,在几乎没有改造主体代码的情况下就能让程序并行运行,也不失为一种好办法。笔者就是用这些朴素的技巧,硬是将运行时长从十几小时降到十分钟内,达到了优化目标,小伙伴们快点实践起来吧!


微信端的朋友也可关注我的公众号

qrcode-12cm

引出问题

做数据加工的同学有时需要通过shell脚本执行sql语句操作MySQL, 对于下面的命令应该并不陌生。

1
mysql -h${mysql_host}  -P${mysql_port} -u${mysql_user} -p${mysql_password} ${mysql_dbname}  -e "source ./sql/sql1.sql"

如果直接将密码写到命令行将出现如下的警告

Warning: Using a password on the command line interface can be insecure.

warning-insecure

那么用shell脚本操作MySQL时如何避免将密码明文写到shell脚本中呢? 本文提供2种思路

解决思路

思路1 defaults-extra-file 选项

defaults-extra-file 是 mysql 命令的一个选项,它允许你指定一个包含额外配置信息的配置文件,对于在执行mysql命令时提供一些额外的配置非常有用。

具体命令

1
mysql --defaults-extra-file=./my.cnf  -e "source ./sql/sql1.sql"

可以看到这里并没有在命令行中指定数据库连接信息,而是将这些信息配置在配置文件./my.cnf中

my.cnf

1
2
3
4
5
6
[client]
host=YOUR_HOST
port=YOUR_PORT
user=YOUR_USER
password=YOUR_PASSWORD
database=YOUR_DATABASE

思路2 MySQL 安全登陆工具 mysql_config_editor

mysql_config_editor 是MySQL自带的一款用于安全加密登录的工具,可以在一些场合避免使用明文密码;另外如果使用mysql命令登录数据库也可以避免每次都输入一堆参数。

具体使用方式

步骤1: 先用 mysql_config_editor 添加一个login-path

1
mysql_config_editor set -G test-login-path1 -h ${mysql_host} -P ${mysql_port} -u ${mysql_user} -p
  1. 该命令用mysql_config_editor创建一个名为test-login-path1的login-path
  2. 其会将登录MySQL的 username、password、port等信息加密存入一个隐藏文件 ~/.mylogin.cnf
  3. 该文件可作为连接MySQL服务器的认证凭证,以后在命令行或者脚本中可免于输入明文密码,安全方便

步骤2: 连接数据库时指定login-path即可,不需要再指定数据库信息

如下演示2个使用 –login-path 参数的命令

登录mysql的场景

1
mysql --login-path=test-login-path1

连接mysql 并执行sql语句的场景

1
mysql --login-path=test-login-path1 -e "source ./sql/sql1.sql"

是不是挺方便? 这样既避免了敏感信息的暴露,也避免了重复输入登录信息,方便了运维人员的工作。

总结

本文详细探讨了两种连接 MySQL 时避免将密码以明文形式写入shell 脚本的策略。通过采纳这些最佳实践,我们在完成工作的基础上,进一步加强了对敏感信息的保护。这些技巧的应用不仅提升了系统的安全水平,还展现了对隐私和敏感信息负责任的态度, 快点实践起来吧!


微信端的朋友也可关注我的公众号

qrcode-12cm

引言

本文分享一个shell脚本中处理命令行选项的小技巧,我看到项目中许多shell脚本没有处理命令行参数这部分内容,这也是导致我们写的shell脚本不够灵活,不能抽取成可复用脚本的原因之一,所以分享下自己在这方面的实践。

引出问题

在命令行中通常有两种类型的选项:短命令行选项(short options)和长命令行选项(long options)

  • 短命令行选项通常用于提供快速且紧凑的命令行选项 如 -s csv
  • 长命令行选项通常用于提供更具可读性和描述性的选项 如 --file-suffix=csv

现在需要写一个shell脚本, 可同时支持短选项和长选项,写好后使用方式如下:

  • 短选项方式 x.sh -s csv
  • 长选项方式 x.sh --file-suffix=csv

在shell中getopts可以很好的处理短选项,那如何处理长选项呢?

基础知识铺垫

在解决这个问题之前先做一个基础知识的铺垫

如下的代码,只要写过shell脚本的同学都知道,是引用变量的值。

1
${value}

再进一步理解如下的代码,其使用了一种叫变量扩展的知识点

1
${value#pattern}

其返回的不是value变量的值,而是一个替换后的值。那又是如何替换的呢?删除value中与pattern相匹配的部分,从左向右匹配,举个例子

1
2
3
value=www.skygroup.com
echo ${value#*.}
输出 skygroup.com

解决办法

再回到最开始的问题,我们要写一个shell脚本可同时支持短选项和长选项两种风格的命令行参数,这里介绍其中一种思路,通过getopts命令结合上面讲的变量扩展的知识点解决解析命令行长选项的问题

有了刚才基础知识的铺垫,下面看一段更完整的代码片断

getopts-long-options

假设执行的命令是 x.sh --file-suffix=csv,我们需要从命令行选项--file-suffix=csv中解析出具体的值csv,这里就用到了${value#pattern}的知识点,关键代码如下

1
file_suffix="${OPTARG#*=}"

结语

本文从实际工作场景出发介绍了笔者在写shell脚本时处理命令行选项,特别是长命令行选项的思路。能够处理命令行参数才能使我们的脚本更灵活,在工作中也更容易抽取出可复用的核心脚本。

其中涉及到变量扩展的知识点,因为篇幅限制没有展开细讲,笔者在博客中专门写了一篇关于shell变量扩展的文章,有兴趣的读者可参考。


微信端的朋友也可关注我的公众号

qrcode-12cm

1. 基础知识

1.1 变量引用

shell引用变量的基本格式 ${parameter}

  • 如果parameter是数字,则是参数扩展
  • 如果parameter是字符串,则是变量扩展
  • 如果parameter是数组,遵循数组的扩展规则;
  • parameter还可以是@ * # ? -等特殊参数,参考特殊参数的引用

1.2 变量扩展

1.2.1 模式扩展

1.2.1.1 ${value#pattern}

删除value中与pattern相匹配的部分,从左向右匹配

1
2
3
value=www.skygroup.com
echo ${value#*.}
输出 skygroup.com

1.2.1.2 ${value##pattern}

也是删除value中与pattern相匹配的部分,从左向右匹配,但是#与##的区别在于贪婪模式上

  • #是非贪婪模式,也即最短匹配模式 lazy
  • ##是贪婪模式,也即最长匹配模式 greedy
1
2
3
value=www.skygroup.com
echo ${value##*.}
输出 com

1.2.1.3 ${value%pattern}

删除value中与pattern相匹配的部分,但是是从右向左匹配

1
2
3
4
value=www.skygroup.com
echo ${value%.*}
输出 www.skygroup.
注意因为是%是从右向左匹配,所以要写成.* 而不是*.

1.2.1.4 ${value%%pattern}

也是删除value中与pattern相匹配的部分,从右向左匹配,%与%%的区别在于贪婪模式上

  • %是非贪婪模式,也即最短匹配模式 lazy
  • %%是贪婪模式,也即最长匹配模式 greedy
1
2
3
value=www.skygroup.com
echo ${value%%.*}
输出 www

1.2.1.5 总结

  • #表示从左向右匹配,##表示从左向右贪婪匹配,删除位于#右侧通配符匹配的字符串
  • %表示从右向左匹配,%%表示从右向左贪婪匹配,删除位于%右侧通配符匹配的字符串

记忆的方法为

  • #是去掉左边(键盘上#在$的左边)
  • %是去掉右边(键盘上%在$的右边)

2. 案例

2.1 长命令行选项

背景: 在命令行中通常有两种类型的选项:短命令行选项(short options)和长命令行选项(long options)。短选项通常用于提供快速且紧凑的命令行选项,长命令行选项通常用于提供更具可读性和描述性的选项

现在需要写一个shell脚本, 可同时支持短选项和长选项,写好后使用方式如下。

  1. 短选项方式 x.sh -s csv
  2. 长选项方式 x.sh --file-suffix=csv

在shell中getopts可以很好的处理短选项,那如何处理长选项呢?

这里介绍其中一种思路,需要用到上面讲的变量扩展中关于模式扩展的知识点 ${value#pattern}

完整代码片断如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 解析命令行选项
while getopts "s:vh-:" opt; do
case $opt in
s)
file_suffix="$OPTARG"
;;
-)
case "${OPTARG}" in
file-suffix=*)
file_suffix="${OPTARG#*=}"
;;
*)
echo "unknown option: --$OPTARG" >&2
exit 1
;;
esac
;;
\?)
echo "unknown option: -$OPTARG" >&2
exit 1
;;
esac
done

假设执行的命令是 x.sh --file-suffix=csv,我们需要从命令行选项--file-suffix=csv中解析出具体的值csv,这里就用到了${value#pattern}的知识点,关键代码如下

1
file_suffix="${OPTARG#*=}"

2.2 获取不带后缀的文件名

假设获取到的文件名是 x.sh,我希望从x.sh中提取出不带后缀名的文件名,这时就用到了${value%pattern}的知识点

1
2
3
4
5
6
SHELL_NAME=x.sh
SHELL_NAME0=${SHELL_NAME%.*}
echo ${SHELL_NAME0}

输出
x

微信端的朋友也可关注我的公众号

qrcode-12cm

引言

Vim是笔者最常用的一款文本编辑软件,几乎伴随着我学习linux的全过程, 是一款经过时间考验的工具

本文介绍一个vim小技巧,临时加载配置

当时也是让笔者眼前一亮的技巧,因为笔者在实际工作中定义了很多快捷键,快捷键多了之后就会出现难以记忆而且相互冲突的问题,实际上这些快捷键不需要立即加载,只在某些场景下临时用一下即可,该技巧正好解决了笔者的痛点。

背景

笔者在用vim处理文本时会临时定义一些快捷键,但不希望将这些快捷键定义在全局的配置文件.vimrc中,只是希望在某些场景临时用一下,但是因为命令多又不想一个一个执行,有没有办法一次执行呢?

1
2
3
:map ,y :Bill<CR>
:map ,l A 漏记<ESC>
:map ,c :s/\s*漏记.*$//<CR>

解决办法

步骤1: 创建一个名为 commands.vim 的脚本文件,将你的命令逐行写入

1
2
3
4
" commands.vim
map ,y :Bill<CR>
map ,l A 漏记<ESC>
map ,c :s/\s*漏记.*$//<CR>

步骤2: 打开 Vim 执行以下命令加载脚本文件中的命令

1
:source /path/to/commands.vim

是不是挺高效? 比起很多“开箱即用”的编辑器,Vim 有一定的学习曲线,但是依然觉得值得。


微信端的朋友也可关注我的公众号

qrcode-12cm

简述

在拼接字符串的过程中有时需要拼接分隔符, 如果你只会用 + 或者 StringBuilder/ StringBuffer 拼接字符串?那你就 OUT 了,建议使用 Java 8 中的这款字符串拼接神器:StringJoiner,你值得拥有

代码片断

原来的字符串拼接代码

1
2
3
4
5
6
7
8
String logStr="\n---------------------------------------------------------------------" +
"\n请求ip:" + clientIp + ":" + clientPort +
"\n请求url:"+clientUrl+
"\n请求方式:"+requestMethod+
"\n执行时长:" + stopwatch.elapsed(TimeUnit.MILLISECONDS) + "ms" +
"\n入参:" + (args == null ? "[]" : Arrays.toString(args)) +
"\n出参:" + (result == null ? "[]" : objectMapper.writeValueAsString(result))+
"\n----------------------------------------------------------------------";

使用 StringJoiner 之后

1
2
3
4
5
6
7
8
9
10
11
StringJoiner stringJoiner = new StringJoiner("\n");
stringJoiner.add("");
stringJoiner.add("---------------------------------------------------------------------");
stringJoiner.add("请求ip:" + clientIp + ":" + clientPort);
stringJoiner.add("请求url:"+clientUrl);
stringJoiner.add("请求方式:"+requestMethod);
stringJoiner.add("执行时长:"+stopwatch.elapsed(TimeUnit.MILLISECONDS) + "ms");
stringJoiner.add("入参:" + (args == null ? "[]" : Arrays.toString(args)));
stringJoiner.add("出参:" + (result == null ? "[]" : objectMapper.writeValueAsString(result)));
stringJoiner.add("----------------------------------------------------------------------");
String logStr=stringJoiner.toString();

不需要自己处理分隔符了,是不是轻闲了不少?

进一步研究源码就会发现,其实际上是对 StringBuilder 做了进一步的封装

stringJoiner-01


微信端的朋友也可关注我的公众号

qrcode-12cm