AOP
@Aspect 切面注释@Pointcut(“execution(* cn.zp.…controller..*(…))”) 切入点@Around、before、after等等 执行以下是一个不常用的案例,将不常用的一场进行切面,@AfterThrowing注解的使用 /**凡是注解了RequestMapping的方法都被拦截 */ @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)") private void webPointcut() { }
/**
拦截web层异常,记录异常日志,并返回友好信息到前端 方法只拦截Exception
将未做处理的异常统一进行处理 */ @AfterThrowing(pointcut = “webPointcut()”, throwing = “e”) public void handleThrowing(Exception e) { logger.error(JSON.toJSONString(e.getStackTrace())); //通过response和writer将信息输出到浏览器 }
IOC、DI
各种注解Configuation、Bean、Service、Controller、Repository、Component Mybatis 半ORM(对象关系映射)框架一对多、多对一、多对多使用RowBounds对象进行分页tk.mybatis 通用Mapper插件 Shiro 与 Spring Security 对比 shiro四大核心功能 身份认证,支持多数据源(api简单)访问控制(api简单),支持粒度(粗:角色;细:权限)的授权会话管理(session),会话验证(验证器、定时任务等验证是否过期)加密(api简单) shiro其他功能 支持一级缓存多线程支持,即如在一个线程中开启另一个线程,能把权限自动传播过去测试支持“记住我”功能支持授权支持,允许一个用户假装为另一个用户(如果他们允许)的身份进行访问不与任何的框架或者容器捆绑,可以独立运行 shiro核心组件 Subject 主体,即登录用户SecurityManager:安全管理器;即过滤器FilterRealm:域,Shiro从从Realm获取安全数据(如用户、角色、权限) SpringSecurity 比shiro难,如果不使用spring则不考虑shiro的功能它都有,并且支持Oauth、OpenID 一般数据库结构(经典5张表) 用户表、角色表、权限表(资源表)、用户-角色表、角色-权限表 Shiro的实现过程 springboot整合redis 自定义Realm(安全域)继承AuthorizingRealm,实现获取授权信息方法(数据库)、获取身份验证信息(url拦截后身份信息验证)2个方法@Configuration配置,将自己的Realm验证加入容器,将容器加入权限管理,增加拦截器工厂,增加启用注解等配置controller内使用,通过注解:@RequiresRoles(“admin”)角色、@RequiresPermissions(“add”)权限等 Netty特点
一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持使用更高效的socket底层避免一些问题,简化了NIO的处理方式对TCP粘包/分包进行自动化处理可使用接受/处理线程池,提高连接效率,对重连、心跳检测的简单支持大量使用了volitale、使用了CAS和原子类、线程安全类的使用、读写锁的使用序列化
xml - 数据全,全平台支持json - 移动、web端thrift - 完全rpc支持Protobuf - 跨防火墙,多语言支持IO 一对一(一个线程对应一个连接)
NIO 一对多(一个线程对应多个连接),非阻塞IO
Channel:表示 IO 源与目标打开的连接,是双向的,但不能直接访问数据,只能与Buffer 进行交互。Buffer:与Channel进行交互,数据是从Channel读入缓冲区,从缓冲区写入Channel中的Selector可使一个单独的线程管理多个ChannelPipe:两个线程之间的单向数据连接,数据会被写到sink通道,从source通道读取使用
服务器端 public class NettyServer { public static void main(String[] args) { ServerBootstrap serverBootstrap = new ServerBootstrap(); NioEventLoopGroup boos = new NioEventLoopGroup(); NioEventLoopGroup worker = new NioEventLoopGroup(); serverBootstrap .group(boos, worker) // 创建分组 .channel(NioServerSocketChannel.class) // 创建频道 .childHandler(new ChannelInitializer<NioSocketChannel>() { @Override protected void initChannel(NioSocketChannel ch) { ch.pipeline().addLast(new StringDecoder()); ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) { System.out.println(msg); } }); } }) .bind(8000); // 监听8000端口 } } 客户端 public class NettyClient { public static void main(String[] args) throws InterruptedException { Bootstrap bootstrap = new Bootstrap(); NioEventLoopGroup group = new NioEventLoopGroup(); bootstrap .group(group) //组信息 .channel(NioSocketChannel.class) .handler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel ch) { ch.pipeline().addLast(new StringEncoder()); } }); //监听url和端口 Channel channel = bootstrap.connect("127.0.0.1", 8000).channel(); while (true) { channel.writeAndFlush(new Date() + ": hello world!"); Thread.sleep(2000); } } } ```Nginx
特点 占用内存小、宕机概率低负载均衡非阻塞高并发(官方:5w并发,2-3w没压力) 异步非阻塞事件处理机制:运用了epoll模型,提供了一个队列,排队解决不使用多线程:切换线程占用cpu 正向代理:异步非阻塞接受请求后统一发送到服务器(降低服务器压力),代理客户端反向代理:代理服务端动、静分离MQ(Kafka) kafka常见问题
分布式发布-订阅消息系统 削峰填谷、缓冲解藕、扩展 名词解释 Producer :消息生产者,就是向kafka broker(服务器)发消息的客户端Consumer、Consumer Group(CG) :消息消费者,向kafka broker取消息的客户端Topic :主题,消息以topic为类别记录,内部包含多个partition(队列)Broker :一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic;一个非常大的topic可以分布到多个broker上。Partition:一个topic可以分为多个partition,每个partition是一个有序的队列。partition中的每条消息都会被分配一个有序的id(offset)。kafka只保证按一个partition中的顺序将消息发给consumer,不保证一个topic的整体(多个partition间)的顺序Offset:消息id,kafka的存储文件都是按照offset.kafka来命名,eg:the first offset就是00000000000.kafka 主从复制(leader follower) topic的分区拥有若干副本,这个数量是可以配置的,并且是各个节点均匀分布的zk保持连接,如果是follower并且同步延时符合配置要求,则说明是同步节点只有当所有同步节点状态都是committed时,消息才会被发送到consumerleader选举: broker选举:谁先创建了zk内的controller节点,谁就是ISR,同步状态的副本的集合(a set of in-sync replicas),在这个集合中的节点都是和leader保持高度一致的,所以任何follower都可以被选为leader,相当于随机 幂等性 原因 先commit,再执行业务逻辑:提交成功,处理失败 。造成丢失先执行业务逻辑,再commit:提交失败,执行成功。造成重复执行先执行业务逻辑,再commit:提交成功,异步执行fail。造成丢失 Producer重复提交问题 单会话 Kafka增加了pid和seq 1、Producer中每个RecordBatch都有一个单调递增的seq; Broker上每个Topic也会维护pid-seq的映射,并且每Commit都会更新lastSeq。2、当RecordBatch到来时,broker会先检查:如果baseSeq(新消息第一条) > lastSeq(维护的最后一条消息)则保存数据 有序性处理: 1、设置enable.idempotence=true后能够动态调整max-in-flight-request。正常情况下max.in.flight.requests.per.connection大于1。2、当重试请求到来且时,batch 会根据 seq重新添加到队列的合适位置,并把max.in.flight.requests.per.connection设为1,这样它 前面的 batch序号都比它小,只有前面的都发完了,它才能发 多会话 原因:当应用重启时,新的producer并没有old producer的状态数据。可能重复保存。方案:通过事务隔离机制来实现,所以事务引入了transactionId和Epoch原理:同单会话 Consumer端会话 通过msgId自己进行操作 命令 配置文件:监听地址、序列化方法、group-id(消费组)方法:producer生产:
@Autowired private KafkaTemplate<String, String> kafkaTemplate; @RequestMapping("/send") public String send(String msg) { kafkaTemplate.send("group-id", msg); return "success"; }consumer消费
单消费组: // 如果是单消费组,这里的group-id就是topicName @KafkaListener(topics = "group-id") public void listen(ConsumerRecord<?, ?> record) throws Exception { System.out.printf("topic = %s, offset = %d, value = %s \n" , record.topic(), record.offset(), record.value()); } 多消费组配置:@KafkaListener(topics = {"${自定义}"},groupId = “xxx”)Zookeeper 常见功能
分布式的协调组件,功能: 发布/订阅负载均衡命名服务分布式协调/通知集群管理Master选举分布式锁和分布式队列等 崩溃恢复模式(正常时为消息广播模式) 当刚刚启动、Leader服务器宕机、重启、网络故障等导致过半服务器与Leader服务器没有保持正常通信时,所有进程(服务器)进入崩溃恢复模式首先选举产生新的Leader服务器,然后集群中Follower服务器开始与新的Leader服务器进行数据同步,当集群中超过半数机器与该Leader服务器完成数据同步之后,退出恢复模式 Leader选举,过半策略=3台服务器,投票选举
数据同步,过半策略 直接差异化同步(DIFF同步)先回滚再差异化同步(TRUNC+DIFF同步)仅回滚同步(TRUNC同步)全量同步(SNAP同步) 事务 顺序一致 采用了全局递增的版本Id(ZXID)来标识,zxid记录ZooKeeper状态的改变zxid实际上是一个64位的数字,高32位用来标识leader周期,如果有新的leader产生出来会自增,低32位用来递增计数当新产生(事务)提议的时候,会依据数据库的两阶段过程,首先会向其他的server发出事务执行请求,如果超过半数的机器都能执行并且能够成功,那么就会开始执行 分布式事务、分布式队列 临时节点:每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点;eg:new ZooKeeper(**).create(**)获取锁:判断有序节点中序号最小的一个释放锁:将瞬时节点删除即可过程:创建临时节点 - 队列中序号最小的获取锁 - 执行 - 关闭锁 - 重复 扩容 水平扩容,全部重启或者顺序重启(不是很好),3.5+会支持动态扩容Mysql
使用多版本控制协议实现强一致性优化 索引 explain 命令获取语句执行计划 explain extended 会在 explain 的基础上额外提供一些查询优化的信息;show warnings 可以 得到优化后的查询语句,从而看出优化器优化了什么explain partitions 会显示将查询的分区(如果分区的话) 热备、主从、分库分表 配置Nosql(Redis、MongoDB等)
Nosql完全不适合交易场景,Nosql主要用来做数据分析、ETL(数据抽取、转换以及加载)、报表、数据挖掘、推荐、日志处理等其他非交易场景对比 HBase 实现的语言为Java,依托于 Hadoop 的 HDFS(分布式文件系统)作为最基本存储基础单元。 java编写,需要java环境存储量大、分割简单,但占用内存大api不灵活适用于大数据环境 redis 实现语言为c: 存储数据结构灵活事务保证业务原子性执行速度快适合数据变化快并且容量可预见集群方案在3.0-版本效果不好单线程、支持事务,所以在单个处理和事务处理均可保证原子性 mongdb 实现语言为c++: json格式在服务器端可执行js脚本非事务单个文档大小限制为16M,32位系统上,不支持大于2.5G的数据对内存要求比较大,至少要保证热数据(索引,数据及系统其它开销)都能装进内存适合集群 LevelDBCouchbase(memcache的升级版) 数据备份 redis:主从配置 分布式 非动态 余数算法:通过数据的key进行hash计算,根据数据库数量取余将数据存储到各个数据库上 动态增加 hash槽 一共16384个槽,各个服务器分管一部分新增机器,将数据通过key进行迁移jedis会协调数据的获取 分布式锁 redis锁:setnx,不过这个不是分布式的,是单库的锁;如果需要分布式则建议使用zk的临时节点Redisson(java框架)实现了redis作者出的RedLock算法 分布式部署 修改配置文件Elasticsearch与Hbase的分析
有es为什么还需要hadoop趋势:相互之间会越来越模糊,现阶段侧重点不同,hadoop是一个生态,es只是能替换其中的一些功能Elasticsearch 优势: ElasticSearch擅长建立索引和搜索索引基于索引查询的快速文本、结构化、统计化查询,以及聚合类数据分析基于json进行查询,开发效率简单且高 劣势: 只能查询索引列,无法搜索和合并未索引的内容;无法运行创建新数据集的复杂分析,所以并不适合做数据分析类操作经上述国外论坛内的讨论,当文档查询结果破万后效率会下降复杂关联查询会降低速度 Hbase(Hadoop) 优势 量大、批处理,复杂计算如果需要专门的数据分析,需要建立团队并使用hadoop 劣势 较es开发复杂,需要人工较多,投入大Elasticsearch
特点
是一个分布式,高性能、高可用、可伸缩的文本搜索和分析系统Lucene是单机的模式,es解决了分布式问题场景
全文检索、结构化检索(同义词处理,相关度排名)复杂数据分析近实时数据日志
logstash持久化
路由分片查找文档位置优化
查询的时候,操作系统会将磁盘文件里的数据自动缓存到 filesystem cache里面去,所以cache建议是全部索引数据内容的一半大小所以一定要符合业务,只将需要搜索的字段进行索引,减少索引也就减少了内存占用提高了查询效率数据预热、冷热分离;可以建立一个系统定时对流量较大的索引进行提前刷入内存进行预热复杂数据提前计算,es的关联对象一定要简单或者是已经处理过的数据再插入es分页性能优化:分页也还是要获取之前的数据,所以无论是一次性多取,还是和微博、淘宝一样的下拉等,都无法满足用户自定义页码跳转,建议还是一页页前进;基础概念
索引(database):含有相同属性的文档集合 分片:每个索引都有多个分片,每个分片是一个lucene索引,减少索引压力备份:拷贝一份分片就完成了分片的备份,用处:分摊搜索压力、容灾启动后自动创建5个分片,1个备份;创建索引的时候指定分片,备份可以后期处理 类型(table):文档必须属于一个类型,索引可以定义1+个类型文档(rows):文档是可以被索引的基本数据单位API基本格式:http://<ip>:<port>/<索引>/<类型>/<文档id>
json格式可以创建索引,指定分片和备份,并指定索引结构(put方式) put请求:http://\<ip\>:\<port\>/<索引> { "settings":{ "number_of_shards": 3, // 分片 "number_of_replicas": 1 // 备份 }, "mappings":{ // 索引结构 "man":{ // 类型 "properties":{ // 文档 "name":{ "type":"text" }, "country":{ "type": "keyword" }, 插入,根据结构使用json传递即可Hbase(Hadoop database)
介绍 是一个高可靠性、高性能、面向列、可伸缩的、建立在hdfs上的分布式存储系统是Google Bigtable的开源实现只支持单行事务,可通过hive执行复杂join语句所以一般是配合Hadoop、ZooKeeper、MapReduce进行搭建 特点 主要用来存储非结构化、半松散化数据KV基础存储基于列的模式(其他关系型是基于行)存储大数据,可以支持千万的QPS、PB级别的存储null和空不占用存储空间,表可以设计的非常松散动态列强同步 使用场景 对象存储:1b~100m海量存储(新闻、视频、音频、论坛)时序数据:高并发、海量数据(k线图、监控数据、传感器)推荐画像:用户特征,非常松散时空数据:高并发、海量数据(轨迹、气候数据)CubeDB([kjuːb]):全称是多维立方体,用来实现实时报表,底层数据存储在hbase消息/订单:强同步、海量存储(聊天、订单等)Feeds([fiːd])流:feed是将用户主动订阅的若干消息源组合在一起形成内容聚合器,帮助用户持续地获取最新的订阅源内容;订阅源不再是某个内容,而是生产内容的人/团体。订阅中通常夹杂非订阅内容,比如热门推荐,广告(今日头条、朋友圈、知乎)NewSQL:之上有Phoenix的插件,可以满足二级索引、SQL的需求,对接传统数据需要SQL非事务的需求 语法抽象、封装、继承、多态、组合
io
字节流:多处理视频、音频、图片等字符流:多处理带有编码格式的文本装饰器模式泛型
eg1:public static T max(List list)分析:静态的是标示范型方法eg2:public static <T extends Comparable> T max(List list)分析:如果是排序方法,需要参数继承自Comparable类的子类其他:K - key,V - value,E - element, <? extends T>可以赋值T以及T子类的集合(null可以),元素会向上转型为T,取值会有范型限制 <? super T>可以赋值T以及T父类的集合,元素会范型丢失,例如不记名投票,结束后无法查看投票记录数据结构
汇总、总结、代码、动图数据结构:栈、队列、链表、树、图(散列、最短路径、拓扑)常用排序算法 稳定性解释:若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面时间复杂度 O(1),一次就找到,哈希算法就是典型的O(1)时间复杂度O(logn),2为底,eg:当数据增大256倍时,耗时只增大8倍O(n),就代表数据量增大几倍,耗时也增大几倍O(nlogn),eg:当数据增大256倍时,耗时增大256*8=2048倍O(n²)/O(n^2),2为底,数据量增大n倍时,耗时增大n的平方倍 空间复杂度:临时占用存储空间大小的量度推荐 1、基本算法使用插入排序2、快速+稳定+占空间:归并排序3、快速+不稳定+少空间:堆排序4、23之间有个快速排序,简单,不稳定,时间和空间均也介于23之间5、纯数字:基数排序 基本算法,时间换空间 插入排序,稳定,推荐 O(n²),空间:O(1)对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入通过代码分析大数据量比冒泡速度快 冒泡排序,稳定 时间:O(n²),空间:O(1)从前往后依次的比较相邻两个数的大小;如果前者比后者大,则交换它们的位置 选择排序,不稳定 时间:O(n²),空间:O(1)在未排序序列中找到最小(大)元素,存放到排序序列的起始(末尾)位置 希尔排序/缩小增量排序/Shell排序 不稳定,时间:O(n^(1.3—2)),空间:O(1)插入排序的改进版需要定好步长(间隔>1),所以需要对数据非常了解 归并排序,稳定 时间:O(nlogn),空间:O(n)空间换时间使用分治法,递归一分为二,在分组后的数据顺序性较好的情况下使用 快速排序,不稳定 时间:O(nlogn),空间:O(nlogn)选择一个基准数,通过一趟排序将要排序的数据分割成独立的两部分;其中一部分的所有数据都比另外一部分的所有数据都要小 堆排序,不稳定 时间:O(nlogn),空间:O(1)二叉树 基数排序,必须是确定范围的整数,稳定,快速桶排序 算法思想: 分治算法(分组计算后再通过算法合并)动态规划算法(类似递归)贪心算法(最多人满足,eg:先满足最容易满足的人)二分法(折半搜索)搜索算法(eg:导航最近距离)回溯算法(eg:乘阶、ip划分等) 领域算法:大数据算法、分布式算法、加密算法、负载均衡算法、推荐算法等多线程
锁: 悲观锁:锁表、锁行;乐观锁:增加version字段线程中断 使用线程池ExecutorService的话,shutdownNow是立刻停止,shutdown是执行完现在没执行完的再停止 线程间协作,join(在start后使用join,其他线程会等待)、yield(让出cpu重新竞争)线程池工厂 private static ExecutorService executorService; public static ExecutorService getExecutorService(String groupName) { synchronized (ExecutorFactory.class) { if (executorService == null) { executorService = new ThreadPoolExecutor(4, 8, Long.MAX_VALUE, TimeUnit.NANOSECONDS, new LinkedBlockingQueue<>(1024),// Executors.defaultThreadFactory(), // 默认工厂 new UserThreadFactory(groupName), // 自定义工厂,可以console线程组和一些上下文参数 new ThreadPoolExecutor.CallerRunsPolicy()); } } return executorService; }
// int corePoolSize, //常驻核心数,0:直接销毁;需要合理分配,太大浪费资源,太小频会繁销毁创建 // int maximumPoolSize, //最大数, >=1 超过会进入缓存队列 // long keepAliveTime, // 连接最大时间,这里使用了最长时间 // TimeUnit unit, // 时间格式,一般使用秒 // BlockingQueue<Runnable> workQueue, // 等待中的任务队列,超过则执行抛弃策略;队列有三种策略,不过阿里云推荐这样,所以有其他需要上网再查 // 该队列为单向链表,使用锁来控制入队和出队的原子性,是一个生产消费模型队列 // ThreadFactory threadFactory, // 创建线程工程,线程池命名是通过给这个工厂增加组命名来实现的 // RejectedExecutionHandler handler // 拒绝策略,超过缓存队列长度以后执行,阿里建议一般跳转固定页面并将数据记数据库or日志后期削峰填谷再处理 //抛弃策略 // ThreadPoolExecutor.AbortPolicy() 抛出java.util.concurrent.RejectedExecutionException异常 // ThreadPoolExecutor.CallerRunsPolicy() 重试添加当前的任务,他会自动重复调用execute()方法 // ThreadPoolExecutor.DiscardOldestPolicy() 抛弃旧的任务 // ThreadPoolExecutor.DiscardPolicy() 抛弃当前的任务 // 不过默认的抛弃策略和线程工厂功能简单,一般不满足使用,实现自己的更合适,实现见 ``` * 使用 ``` while(线程池数量){ Future<Integer> future = executorService.submit(() -> { ...... }) } boolean isDone = false; while (!isDone){ if(future.isDone()) {// System.out.println(future.get()); isDone = true; } else { System.out.println(false); Thread.sleep(50); } } service.shutdown(); ```
反射
因为效率问题,使用场景不高,一般会在三方或者获取其他项目私有属性的时候使用;或者自己写框架时使用class类:获取类加载器、类名、创建类实例、包名、父类名、接口名、获取注解、所有方法、返回值、类型(匿名、内部、接口实现方法)、构造方法等几乎所有能用到的Field类:成员变量/类的属性Method类:等Constructor类:构造方法使用:Class<?> c = Class.forName(“package path”).method;(eg:newInstance())动态代理,框架等底层都是通过该方法进行切面编程的:eg:HttpServlet的dopost方法调用
method: private void testInvocation2(){ PersonServer server = new PersonProxy().getPersonProxy(); server.login(); server.submit(); } // 代理类 public class PersonProxy { // 业务接口 private PersonServer server = new PersonServerImp(); public PersonServer getPersonProxy() { // 返回一个代理 return (PersonServer) Proxy.newProxyInstance( server.getClass().getClassLoader() , server.getClass().getInterfaces() , (proxy, method, args) -> { if (method.getName().equals("login")) { // 前置业务 System.out.println("login_begin"); // 主业务执行 Object object = method.invoke(server, args); // 后置业务 System.out.println("login_end"); // 返回执行结果 return object; } else if (method.getName().equals("submit")) { // 其他方法 return method.invoke(server, args); } return null; }); } } // (proxy, method, args) -> {}的原文 new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { } }异常
finally 即使发生oom也会执行finally执行顺序:先保存return的值,再执行finally,最后将保存的值return,不再考虑finally改变后的值综上:finally是处理诸如清理资源、释放连接、关闭管道流等,所以不要有赋值、return等操作ps:lock需要写到try内,小心造成finally内的lock.unlock方法在加锁失败后仍旧执行造成报错测试
类注解 @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = xxxApplication.class) 初始化,使用mockMvc,MockMvc实现对Http请求的模拟,可以方便对Controller进行测试,使得测试速度快、不依赖网络环境,而且提供验证的工具,使得请求的验证统一而且很方便 private MockMvc mockMvc; @Autowired private WebApplicationContext webContext; @Before public void before() throws Exception { mockMvc = MockMvcBuilders.webAppContextSetup(webContext).build(); } @After public void after() throws Exception { } 方法 @Test public void testIndexFail() throws Exception { String responseString = mockMvc .perform(get("/api/***") .accept(MediaType.APPLICATION_JSON_UTF8) ) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(); assertNotNull(responseString); JSONObject json = JSONObject.fromObject(responseString); assertEquals(json.getString("code").toString(), "1"); assertEquals(json.getString("msg").toString(), TrainConstants.ErrorMsg.PARAMS_ERROR); }日志
日志门面:门面模式,只提供了接口规范,slf4j、commons-logging日志库:具体实现门面,log4j -> 升级 -> logback、log-jdk日志适配器:因为log4j日志库比slf4j日志接口先出,所以没有使用logback的项目需要增加slf4j-log4j12适配器综述:推荐slf4j + logback模式运行 ./xx.sh,如果不用./则会去path里面找路径
变量
基础
与其他语言类似,name=“params”,不能有空格变量以 开 始 , 如 果 是 拼 接 等 使 用 开始,如果是拼接等使用 开始,如果是拼接等使用{}包裹双引号内部可以有变量,单引号不行,所以拼接等建议使用双引号echo -e 激活转义符(mac不需要)字符串长度:${#变量}字符串
长度:${#变量}提取:${变量:开始:个数}(从开始+1截取个数)数组
array_name=(value0 value1 value2 value3)array_name[0]=value0valuen=${array_name[n|@]}传递参数
echo "执行的文件名:$0"; echo "第一个参数为:$1"; $ ./test.sh 1 2 3 Shell 传递参数实例! 执行的文件名:./test.sh 第一个参数为:1 $@全部参数$*全部参数作为一个参数命令-关系与逻辑
算数,加减乘除:echo `expr 2 + 2` 注意空格必须要有布尔运算 eg:if [ $a -lt 5 -o $b -gt 100 ] !非-o 或-a 与eg:if [ $one == $two ]; then xxx elif xxx else xxx fi 关系,只支持数字,eg:if [ $a -eq $b ] -eq 相等-ne 不相等-gt 大于-lt 小于-ge 大于等于-le 小于等于 逻辑运算 eg:if [[ $a -lt 100 && $b -gt 100 ]] && || 字符串运算符 = !=-z 是否为0-n 是否不为0 eg:if [ -n “$a” ]$ 是否为空 eg:if [ $a ]命令-文件测试运算符
-r file 文件是否可读;-w 是否可写-f file 是否是普通文件(非设备、非目录文件);-d 是否是目录-x file 是否可执行-s file 文件不为空;-e 文件存在命令-其他
read 获取console的输入
eg:read param;echo $paramecho
\n 换行,\c 不换行,\f 换页,\r 回车,\t 水平制表符,\v 垂直制表符-e 转义单引号:原文输出反引号:输出命令结果输出到文件(覆盖):eg:echo “Im’ OK” >xxx/test.shprintf,同c语言
eg: printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234 # %s %c %d %f都是格式替代符;-10:宽度;-4.2f:保留小数等 无论多少个占位符,多少个参数,都按照参数数量进行格式输出;注意:如果是%d,但是参数是字符串,则报错 printf "%s %s\n" "参数一" "参数二" # 参数一 参数二 printf "%s %s %s\n" "参数一" "参数二" # 参数一 参数二 printf "%s\n" "参数一" "参数二" "参数三" # 参数一 # 参数二 # 参数三test eg:if test $num1 = $num2
测试、调试等使用,可以快速获取表达式问题流程控制
if:if [ $(ps -ef | grep -c “ssh”) -gt 1 ]; then echo “true”; fifor:for var in item1 item2 … itemN; do command1; command2… done;while:while condition do command done无限循环:while true …… 或者 for (( ; ; ))case: read item case "$item" in 1|z|\*) echo "case 1" ;; *) echo "default" ;; esac breakcontinue函数,注意参数当n>=10时,需要使用${n}来获取参数
function name () { echo "name函数->$1" #a } name a b c d输入/重定向
输出 覆盖:command1 > file1;eg:who > test.sh * 追加:command1 >> file1;eg:who >> test.sh 输入 将xxx的内容做cat处理:cat < xxx.xlsx如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null;/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃