diff --git a/java/spring/spring/RestTemplate.md b/java/spring/spring/RestTemplate.md index 5390450..fe6b399 100644 --- a/java/spring/spring/RestTemplate.md +++ b/java/spring/spring/RestTemplate.md @@ -3,7 +3,7 @@ 在我们的项目中,如果借助 [RestTemplate](https://so.csdn.net/so/search?q=RestTemplate&spm=1001.2101.3001.7020) 发送带参数的 Get 请求,我们可以通过拼接字符串的方式将 url 拼接出来,比如下面这种方式: ```java -String url = "http://127.0.0.1:8080/rest/get? + id; +String url = "http://127.0.0.1:8080/rest/get?" + id; ResponseEntity forEntity = restTemplate.getForEntity(url, RestVO.class); ``` diff --git a/java/springboot/工具类.md b/java/springboot/工具类.md new file mode 100644 index 0000000..75a7bda --- /dev/null +++ b/java/springboot/工具类.md @@ -0,0 +1,415 @@ +> 本文由 [简悦 SimpRead](http://ksria.com/simpread/) 转码, 原文地址 [mp.weixin.qq.com](https://mp.weixin.qq.com/s/CKA2wANH_3YiMSyEwKOG-g) + +[TOC] + +[![](https://mmbiz.qpic.cn/mmbiz_jpg/JfTPiahTHJhrVCBjpkzyu9AsDHmKqDiaBO5AYRKwKoULyOb9GHSsR11hjXIzSDqsMulDm0VlcEw8j6kbehG4E7XA/640?wx_fmt=jpeg)](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect) + +* * * + +最近发现同事写了不少重复的工具类,发现其中很多功能,Spring 自带的都有。于是整理了本文,希望能够帮助到大家! + +[断言](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect) +----------------------------------------------------------------------------------------------------------------------------------------- + +1. 断言是一个逻辑判断,用于检查不应该发生的情况 + +2. Assert 关键字在 JDK1.4 中引入,可通过 JVM 参数`-enableassertions`开启 + +3. SpringBoot 中提供了 Assert 断言工具类,通常用于数据合法性检查 + + +```java +// 要求参数 object 必须为非空(Not Null),否则抛出异常,不予放行 +// 参数 message 参数用于定制异常信息。 +void notNull(Object object, String message) +// 要求参数必须空(Null),否则抛出异常,不予『放行』。 +// 和 notNull() 方法断言规则相反 +void isNull(Object object, String message) +// 要求参数必须为真(True),否则抛出异常,不予『放行』。 +void isTrue(boolean expression, String message) +// 要求参数(List/Set)必须非空(Not Empty),否则抛出异常,不予放行 +void notEmpty(Collection collection, String message) +// 要求参数(String)必须有长度(即,Not Empty),否则抛出异常,不予放行 +void hasLength(String text, String message) +// 要求参数(String)必须有内容(即,Not Blank),否则抛出异常,不予放行 +void hasText(String text, String message) +// 要求参数是指定类型的实例,否则抛出异常,不予放行 +void isInstanceOf(Class type, Object obj, String message) +// 要求参数 `subType` 必须是参数 superType 的子类或实现类,否则抛出异常,不予放行 +void isAssignable(Class superType, Class subType, String message) +``` + +[对象、数组、集合](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect) +----------------------------------------------------------------------------------------------------------------------------------------------- + +### [ObjectUtils](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect) + +1. 获取对象的基本信息 + + +```java +// 获取对象的类名。参数为 null 时,返回字符串:"null" +String nullSafeClassName(Object obj) +// 参数为 null 时,返回 0 +int nullSafeHashCode(Object object) +// 参数为 null 时,返回字符串:"null" +String nullSafeToString(boolean[] array) +// 获取对象 HashCode(十六进制形式字符串)。参数为 null 时,返回 0 +String getIdentityHexString(Object obj) +// 获取对象的类名和 HashCode。 参数为 null 时,返回字符串:"" +String identityToString(Object obj) +// 相当于 toString()方法,但参数为 null 时,返回字符串:"" +String getDisplayString(Object obj) +``` + +2. 判断工具 + + +```java +// 判断数组是否为空 +boolean isEmpty(Object[] array) +// 判断参数对象是否是数组 +boolean isArray(Object obj) +// 判断数组中是否包含指定元素 +boolean containsElement(Object[] array, Object element) +// 相等,或同为 null时,返回 true +boolean nullSafeEquals(Object o1, Object o2) +/* +判断参数对象是否为空,判断标准为: + Optional: Optional.empty() + Array: length == 0 +CharSequence: length == 0 + Collection: Collection.isEmpty() + Map: Map.isEmpty() + */ +boolean isEmpty(Object obj) +``` + +3. 其他工具方法 + + +```java +// 向参数数组的末尾追加新元素,并返回一个新数组 + A[] addObjectToArray(A[] array, O obj) +// 原生基础类型数组 --> 包装类数组 +Object[] toObjectArray(Object source) +``` + +### [StringUtils](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect) + +1. 字符串判断工具 + + +```java +// 判断字符串是否为 null,或 ""。注意,包含空白符的字符串为非空 +boolean isEmpty(Object str) +// 判断字符串是否是以指定内容结束。忽略大小写 +boolean endsWithIgnoreCase(String str, String suffix) +// 判断字符串是否已指定内容开头。忽略大小写 +boolean startsWithIgnoreCase(String str, String prefix) +// 是否包含空白符 +boolean containsWhitespace(String str) +// 判断字符串非空且长度不为 0,即,Not Empty +boolean hasLength(CharSequence str) +// 判断字符串是否包含实际内容,即非仅包含空白符,也就是 Not Blank +boolean hasText(CharSequence str) +// 判断字符串指定索引处是否包含一个子串。 +boolean substringMatch(CharSequence str, int index, CharSequence substring) +// 计算一个字符串中指定子串的出现次数 +int countOccurrencesOf(String str, String sub) +``` + +2. 字符串操作工具 + + +```java +// 查找并替换指定子串 +String replace(String inString, String oldPattern, String newPattern) +// 去除尾部的特定字符 +String trimTrailingCharacter(String str, char trailingCharacter) +// 去除头部的特定字符 +String trimLeadingCharacter(String str, char leadingCharacter) +// 去除头部的空白符 +String trimLeadingWhitespace(String str) +// 去除头部的空白符 +String trimTrailingWhitespace(String str) +// 去除头部和尾部的空白符 +String trimWhitespace(String str) +// 删除开头、结尾和中间的空白符 +String trimAllWhitespace(String str) +// 删除指定子串 +String delete(String inString, String pattern) +// 删除指定字符(可以是多个) +String deleteAny(String inString, String charsToDelete) +// 对数组的每一项执行 trim() 方法 +String[] trimArrayElements(String[] array) +// 将 URL 字符串进行解码 +String uriDecode(String source, Charset charset) +``` + +3. 路径相关工具方法 + + +```java +// 解析路径字符串,优化其中的 “..” +String cleanPath(String path) +// 解析路径字符串,解析出文件名部分 +String getFilename(String path) +// 解析路径字符串,解析出文件后缀名 +String getFilenameExtension(String path) +// 比较两个两个字符串,判断是否是同一个路径。会自动处理路径中的 “..” +boolean pathEquals(String path1, String path2) +// 删除文件路径名中的后缀部分 +String stripFilenameExtension(String path) +// 以 “. 作为分隔符,获取其最后一部分 +String unqualify(String qualifiedName) +// 以指定字符作为分隔符,获取其最后一部分 +String unqualify(String qualifiedName, char separator) +``` + +### [CollectionUtils](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect) + +1. 集合判断工具 + + +```java +// 判断 List/Set 是否为空 +boolean isEmpty(Collection collection) +// 判断 Map 是否为空 +boolean isEmpty(Map map) +// 判断 List/Set 中是否包含某个对象 +boolean containsInstance(Collection collection, Object element) +// 以迭代器的方式,判断 List/Set 中是否包含某个对象 +boolean contains(Iterator iterator, Object element) +// 判断 List/Set 是否包含某些对象中的任意一个 +boolean containsAny(Collection source, Collection candidates) +// 判断 List/Set 中的每个元素是否唯一。即 List/Set 中不存在重复元素 +boolean hasUniqueObject(Collection collection) +``` + +2. 集合操作工具 + + +```java +// 将 Array 中的元素都添加到 List/Set 中 + void mergeArrayIntoCollection(Object array, Collection collection) +// 将 Properties 中的键值对都添加到 Map 中 + void mergePropertiesIntoMap(Properties props, Map map) +// 返回 List 中最后一个元素 + T lastElement(List list) +// 返回 Set 中最后一个元素 + T lastElement(Set set) +// 返回参数 candidates 中第一个存在于参数 source 中的元素 + E findFirstMatch(Collection source, Collection candidates) +// 返回 List/Set 中指定类型的元素。 + T findValueOfType(Collection collection, Class type) +// 返回 List/Set 中指定类型的元素。如果第一种类型未找到,则查找第二种类型,以此类推 +Object findValueOfType(Collection collection, Class[] types) +// 返回 List/Set 中元素的类型 +Class findCommonElementType(Collection collection) +``` + +[文件、资源、IO 流](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect) +------------------------------------------------------------------------------------------------------------------------------------------------- + +### [FileCopyUtils](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect) + +1. 输入 + + +```java +// 从文件中读入到字节数组中 +byte[] copyToByteArray(File in) +// 从输入流中读入到字节数组中 +byte[] copyToByteArray(InputStream in) +// 从输入流中读入到字符串中 +String copyToString(Reader in) +``` + +2. 输出 + + +```java +// 从字节数组到文件 +void copy(byte[] in, File out) +// 从文件到文件 +int copy(File in, File out) +// 从字节数组到输出流 +void copy(byte[] in, OutputStream out) +// 从输入流到输出流 +int copy(InputStream in, OutputStream out) +// 从输入流到输出流 +int copy(Reader in, Writer out) +// 从字符串到输出流 +void copy(String in, Writer out) +``` + +### [ResourceUtils](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect) + +1. 从资源路径获取文件 + + +```java +// 判断字符串是否是一个合法的 URL 字符串。 +static boolean isUrl(String resourceLocation) +// 获取 URL +static URL getURL(String resourceLocation) +// 获取文件(在 JAR 包内无法正常使用,需要是一个独立的文件) +static File getFile(String resourceLocation) +``` + +2. Resource + + +```java +// 文件系统资源 D:\... +FileSystemResource +// URL 资源,如 file://... http://... +UrlResource +// 类路径下的资源,classpth:... +ClassPathResource +// Web 容器上下文中的资源(jar 包、war 包) +ServletContextResource +// 判断资源是否存在 +boolean exists() +// 从资源中获得 File 对象 +File getFile() +// 从资源中获得 URI 对象 +URI getURI() +// 从资源中获得 URI 对象 +URL getURL() +// 获得资源的 InputStream +InputStream getInputStream() +// 获得资源的描述信息 +String getDescription() +``` + +### [StreamUtils](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect) + +1. 输入 + + +```java +void copy(byte[] in, OutputStream out) +int copy(InputStream in, OutputStream out) +void copy(String in, Charset charset, OutputStream out) +long copyRange(InputStream in, OutputStream out, long start, long end) + +``` + +2. 输出 + + +```java +byte[] copyToByteArray(InputStream in) +String copyToString(InputStream in, Charset charset) +// 舍弃输入流中的内容 +int drain(InputStream in) +``` + +[反射、AOP](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect) +--------------------------------------------------------------------------------------------------------------------------------------------- + +### [ReflectionUtils](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect) + +1. 获取方法 + + +```java +// 在类中查找指定方法 +Method findMethod(Class clazz, String name) +// 同上,额外提供方法参数类型作查找条件 +Method findMethod(Class clazz, String name, Class... paramTypes) +// 获得类中所有方法,包括继承而来的 +Method[] getAllDeclaredMethods(Class leafClass) +// 在类中查找指定构造方法 +Constructor accessibleConstructor(Class clazz, Class... parameterTypes) +// 是否是 equals() 方法 +boolean isEqualsMethod(Method method) +// 是否是 hashCode() 方法 +boolean isHashCodeMethod(Method method) +// 是否是 toString() 方法 +boolean isToStringMethod(Method method) +// 是否是从 Object 类继承而来的方法 +boolean isObjectMethod(Method method) +// 检查一个方法是否声明抛出指定异常 +boolean declaresException(Method method, Class exceptionType) +``` + +2. 执行方法 + + +```java +// 执行方法 +Object invokeMethod(Method method, Object target) +// 同上,提供方法参数 +Object invokeMethod(Method method, Object target, Object... args) +// 取消 Java 权限检查。以便后续执行该私有方法 +void makeAccessible(Method method) +// 取消 Java 权限检查。以便后续执行私有构造方法 +void makeAccessible(Constructor ctor) +``` + +3. 获取字段 + + +```java +// 在类中查找指定属性 +Field findField(Class clazz, String name) +// 同上,多提供了属性的类型 +Field findField(Class clazz, String name, Class type) +// 是否为一个 "public static final" 属性 +boolean isPublicStaticFinal(Field field) +``` + +4. 设置字段 + + +```java +// 获取 target 对象的 field 属性值 +Object getField(Field field, Object target) +// 设置 target 对象的 field 属性值,值为 value +void setField(Field field, Object target, Object value) +// 同类对象属性对等赋值 +void shallowCopyFieldState(Object src, Object dest) +// 取消 Java 的权限控制检查。以便后续读写该私有属性 +void makeAccessible(Field field) +// 对类的每个属性执行 callback +void doWithFields(Class clazz, ReflectionUtils.FieldCallback fc) +// 同上,多了个属性过滤功能。 +void doWithFields(Class clazz, ReflectionUtils.FieldCallback fc, + ReflectionUtils.FieldFilter ff) +// 同上,但不包括继承而来的属性 +void doWithLocalFields(Class clazz, ReflectionUtils.FieldCallback fc) +``` + +### [AopUtils](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect) + +1. 判断代理类型 + + +```java +// 判断是不是 Spring 代理对象 +boolean isAopProxy() +// 判断是不是 jdk 动态代理对象 +isJdkDynamicProxy() +// 判断是不是 CGLIB 代理对象 +boolean isCglibProxy() +``` + +2. 获取被代理对象的 class + + +```java +// 获取被代理的目标 class +Class getTargetClass() +``` + +### [AopContext](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect) + +1. 获取当前对象的代理对象 + + +```java +Object currentProxy() +``` diff --git a/linux/assets/v2-3b235c3ce05833cf24f0a540f85e0876_720w-1682328791181-3.jpg b/linux/assets/v2-3b235c3ce05833cf24f0a540f85e0876_720w-1682328791181-3.jpg new file mode 100644 index 0000000..3b39b37 Binary files /dev/null and b/linux/assets/v2-3b235c3ce05833cf24f0a540f85e0876_720w-1682328791181-3.jpg differ diff --git a/linux/assets/v2-3b235c3ce05833cf24f0a540f85e0876_720w.jpg b/linux/assets/v2-3b235c3ce05833cf24f0a540f85e0876_720w.jpg new file mode 100644 index 0000000..3b39b37 Binary files /dev/null and b/linux/assets/v2-3b235c3ce05833cf24f0a540f85e0876_720w.jpg differ diff --git a/linux/crontab.md b/linux/crontab.md new file mode 100644 index 0000000..84a6c32 --- /dev/null +++ b/linux/crontab.md @@ -0,0 +1,552 @@ +![一文精通 crontab从入门到出坑](F:\Users\admin\Desktop\文档\learning_record_doc\linux\assets\v2-3b235c3ce05833cf24f0a540f85e0876_720w-1682328791181-3.jpg) + +> 本文由 [简悦 SimpRead](http://ksria.com/simpread/) 转码, 原文地址 [zhuanlan.zhihu.com](https://zhuanlan.zhihu.com/p/58719487?utm_source=wechat_session) + +> 这篇博文较长,涵盖了几乎所有 crontab 的坑。阅读时间会比较长,建议认真读完。 + +此篇技术博文主要介绍的是 crontab,Linux 下的计划任务管理工具。涉及内容包括 crontab 使用配置、常见坑的分析和个人总结的错误调试方法。 + +我的理解,后台任务通常分为两种:常驻和定时。之前的文章《pm2 进程管理工具使用总结》主要针对的是常驻任务。今天来谈谈 crontab,主要针对的是定时任务。我的实验环境:centos7。 + +**介绍 crontab** +-------------- + +crontab 的服务进程名为 crond,英文意为周期任务。顾名思义,crontab 在 Linux 主要用于周期定时任务管理。通常安装操作系统后,默认已启动 crond 服务。crontab 可理解为 cron_table,表示 cron 的任务列表。类似 crontab 的工具还有 at 和 anacrontab,但具体使用场景不同,可参见附录《让你学会 Linux 计划任务》一文了解更多。 + +关于 crontab 的用途很多,如 + +* 定时系统检测; +* 定时数据采集; +* 定时日志备份; +* 定时更新数据缓存; +* 定时生成报表; + ... + 等等任务 + +当然,更多使用场景是要以视具体情况而定了。毕竟是工具通常都是常用规则总结而成的产物。 + +确认 crond 服务已经安装与开启之后,下面开始具体说明 + +**简单示例** +-------- + +先来个简单示例体验一下。目标是每分钟向 / tmp/time.txt 文件下写入当前时间 + +**新建 crontab 任务** + +```shell +$ crontab -e // 打开crontab任务编辑 +* * * * * date >> /tmp/time.txt +``` + +静静等待几分钟工作如下命令查看文件: + +```sh +$ cat /tmp/time.txt +Do 29. Dez 22:45:01 CST 2016 +Do 29. Dez 22:46:01 CST 2016 +Do 29. Dez 22:47:01 CST 2016 +``` + +* 从上面结果看出,每分钟执行了 date 并写入到 / tmp/time.txt。 + +简单示例演示成功。下面从细节深入说明 crontab 使用。 + +**使用选项** +-------- + +上面的实验中使用了 crontab 命令的 - e 选项。我们来看看 crontab 命令中有哪些选项? + +**-e 选项** 表示打开当前用户的 crontab 任务列表配置文件。当然也可以直接打开,路径通常是在 / var/spool/cron / 下,文件以用户名命名,如 / var/spool/cron/root。不过,采用 - e 方式打开,福利是可以帮助我们自动检查任务配置符合规则。 + +**-u 选项** 指定某用户的任务列表,很好理解。比如我当前是 root 用户,想操作 poloxue 用户的任务列表。如下: + +```shell +$ crontab -u poloxue -e +``` + +**-l 选项** 列出某用户的所有任务列表 + +**-r 选项** 删除某用户的所有任务列表,这个选项使用小心为上,估计也只是自己实验时玩玩而已,正常不使用。 + +crontab 命令的选项中,主要使用的就是以上几个,理解比较简单。 + +**任务配置** +-------- + +说完了 crontab 的命令选项,下面开始正题,任务列表文件如何配置? + +首先,看下 crontab 任务列表配置格式,示例文件如下: + +```shell +SHELL=/bin/bash +PATH=/sbin:/bin:/usr/sbin:/usr/bin +MAILTO=root + +# 更多细节 man 4 crontabs + +# 计划任务定义的例子: +# .---------------- 分 (0 - 59) +# | .------------- 时 (0 - 23) +# | | .---------- 日 (1 - 31) +# | | | .------- 月 (1 - 12) +# | | | | .---- 星期 (0 - 7) (星期日可为0或7) +# | | | | | +# * * * * * 执行的命令 +* * * * * date >> /time.txt 2>&1 +``` + +从上面的示例文件可看出,crontab 的任务列表主要由两部分组成:环境变量配置与定时任务配置。可能大家在工作中更多是只用到了任务配置部分。 + +**环境变量配置部分** +------------ + +理解环境变量配置这部分可以帮助我们减少去踩一些不必要的坑。简单说明上面涉及的环境变量。 + +**SHELL** 为 / bin/bash,表示使用 / bin/bash 解释执行命令 + +**PATH** 表示到哪些目录路径寻找命令程序,此环境变量的值说明了为什么我们在 crontab 中执行命令时,尽量要写命令全路径才能执行的原因。 + +**MAILTO** 变量作用是当任务执行有输出时,内容发送到哪个用户的邮箱。禁用可以设置 MAILTO=""。 + +当我们在使用 crontab 时,发现某些定时任务不能顺利执行,但 shell 控制台执行成功,环境变量是否正确是我们需要首先关注的点之一。具体详情可以看后面关于环境变量坑的说明。 + +**定时任务配置部分** +------------ + +这部分是 crontab 配置核心。 + +**基本配置** + +如下所示配置共 6 列,前 5 列是关于执行时间配置,最后 1 列是具体执行命令。 + +```shell +.---------------- 分 (0 - 59) +| .------------- 时 (0 - 23) +| | .---------- 日 (1 - 31) +| | | .------- 月 (1 - 12) +| | | | .---- 星期 (0 - 6) (星期日可为0或7) +| | | | | +* * * * * 执行的命令 +``` + + +第一列单位为分,表示每时第几分钟,范围为 0-59; +第二列单位为时,表示每天第几小时,范围为 0-23; +第三列单位为日,表示每月第几天,范围为 1-31; +第四列单位为月,表示每年第几月,范围为 1-12; +第五列单位为星期,表示每星期第几天,范围 0-7,0 与 7 表示星期日,其他分别为星期 1-6; + +**时间配置段类型** + +根据时间列中值的不同设置方式,编者总结出以下五种类型: + +**固定某值**,指定固定值,如指定 1 月 1 日 0 时 0 分执行任务 + +```shell +0 0 1 1 * command +``` + +月日时分都指定了固定数值。 +注:* 在 crontab 中表示任意值都满足条件。 + +**列表值**,时间值是一个列表,如指定一个月内 2、12、22 日零时执行任务 + +```shell +0 0 2,12,22 * * command +``` + +上述日指定多个值,2 号、12 号和 22 号,以逗号分隔; + +**连续范围值**,时间为连续范围的值,如指定每个月 1 至 7 号零时执行任务 + +```shell +0 0 1-7 * * command +``` + +上述日期为连续范围的值 1-7 时 + +**步长值**,根据指定数值跳跃步长确定执行时间,如指定凌晨 1 时开始每割 3 个小时 0 分执行一次任务 + +```shell +0 1-24/3 * * * command +``` + +上述指定从凌晨 1 时每 3 个小时执行任务,如 1 点 0 分,4 点 0 分,7 点 0 分等。 + +**混合值**,支持以上类型的组合,如指定每小时 0 至 10 分,22、33 分以及 0-60 分钟每隔 20 分钟执行任务,如下 + +0-10,22,33,*/20 * * * * command + +这里的分钟值采取了多种类型组合指定,包括连续范围值 (0-7),列表值 (22,33),步长值 (*/20)。 + +说明:这几种时间配置类型是编者自己总结,希望能帮助大家更好理解。 + +**定时语句解析工具** +------------ + +通常在使用 crontab 添加任务时,我们会依靠自己已有知识编写定时语句。当需要测试语句是否正确时,总需要一定时间等待证明其正确性。作为一名牛逼的程序员,这种方式就太不酷了。有没有一款工具,只要我们给出语句,其就能告诉具体执行时间呢?下面介绍一款老外开发的 crontab 在线解析工具。 + +工具地址:[https://crontab.guru](https://link.zhihu.com/?target=https%3A//crontab.guru/),下面是工具的截图 + +![](https://pic2.zhimg.com/v2-b9c9d6301f824594b33f408273436ae9_r.jpg) + + +从上面看出,我们输入的语句解析结果为每天的 04:05 执行任务。下面有这样一行文字 “next at 2016-12-31 04:05:00”,告诉了我们最近一次的执行时间。 + +注:百度搜索 “crontab 在线解析” 获得的工具有坑,某些语句解析结果错误。为避免大家受骗,这里提供具体地址:[http://tool.lu/crontab/](https://link.zhihu.com/?target=http%3A//tool.lu/crontab/) + +**使用有坑** +-------- + +crontab 使用中常会遇到各种坑。下面列出编者在使用中曾遇到的一些问题。此处介绍两种坑,一种是基本功不足导致的配置错误,而另一种是多数人对 crontab 配置都存在的一个理解误区。 + +**整点时间设置错误** + +其实这个错误不用单独说明,但是我在刚开始接触 crontab 时犯过,单独拿出来说明一下。 +如设定每天 3 点执行一次某任务 + +下面列出错误方式,当我们听到每天 3 点执行一次某任务时,很多人会把重点放在 3 点,而忽略了执行一次的需求。下面是个错误的例子 + +```shell +* 3 * * * command +``` + +这里会导致在三点的每分钟都会执行一次任务,也就是执行了 60 次。正确方式如下,每天 3 点 0 时执行任务: + +```shell +0 3 * * * command +``` + +**日与星期的关系误区** + +这真的是个大误区,很多人都不知道的大误区。直接开始说明吧。首先做两个练习 + +设置任务一:每月的 1-7 每天零时执行某任务,答案如下: + +```shell +0 0 1-7 * * date >> /tmp/date.txt +``` + +设置任务二:每星期的星期一零时执行某任务,答案如下: + +```shell +0 0 * * 1 date >> /tmp/date.txt +``` + +上面两个任务的设定都是正确的。下面提出第三个任务,设置每个月的第一个星期一零时执行某任务。 + + +分解任务要求,首先,第一个星期就是每个月的 1-7 日,而星期一就是星期一。所以我们理解的 crontab 任务配置如下 + +```shell +0 0 1-7 * 1 date >> /tmp/date.txt +``` + +下面直接使用前面介绍的在线解析工具分析此语句,如下 + +![](https://pic1.zhimg.com/v2-3685380c1c54a36717aa6f17e16778b0_r.jpg) + + +解析结果显示语句执行时间为每月的 1 至 7 日和每星期一。可以看到最近执行时间是 “next at 2017-01-01 00:00:00”,这个时间也并非星期一。这是 crontab 的一个特别容易误解之处,下面直接给出结论: + +当日和星期任一列包含时,日与星期两者为并且的关系; + +当日和星期列中不包含时,日与星期两者为或者的关系; + +请注意,前面提到的那个百度搜索出来的工具分析结果显示的确是每月第一个星期一,这是错误的。如有朋友持怀疑态度,可自行验证,如有错误,随时告知。 + +**环境变量问题** +---------- + +当我们刚使用 crontab 时,有人会告知所有命令尽量都使用绝对路径,以防错误。为什么?这就和我们下面要谈的环境变量有关了。 + +首先,获取控制台环境变量看下 + +```shell +$ env +XDG_SESSION_ID=10 +HOSTNAME=localhost.localdomain +SHELL=/bin/bash +PERL_MB_OPT=--install_base /root/perl5 +USER=root +MAIL=/var/spool/mail/root +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/php5/bin +PWD=/var/mail +SHLVL=1 +HOME=/root +LOGNAME=root +XDG_RUNTIME_DIR=/run/user/0 +_=/usr/bin/env +``` + +考虑篇幅问题,上文输出有删减。 + +然后,获取 crontab 环境变量信息 + +```shell +* * * * * /usr/bin/env > /tmp/env.txt +``` + +输出结果,如下: + +```shell +$ cat /tmp/env.txt +XDG_SESSION_ID=732 +SHELL=/bin/sh +USER=root +PATH=/usr/bin:/bin +PWD=/root +LANG=de_DE.UTF-8 +SHLVL=1 HOME=/root +LOGNAME=root +XDG_RUNTIME_DIR=/run/user/0 +_=/usr/bin/en +``` + +对比 crontab 与控制台输出,我们发现两者的环境变量差异很大。如果命令在控制台执行成功,而在 crontab 执行失败,我们需要考虑是否命令涉及的环境变量在 crontab 和控制台间存在差异。 + +明白 crontab 使用绝对路径执行命令原因了吗?我们知道命令默认查找路径是由 PATH 指定的。从上面输出结果可知,控制台的 PATH 值为: + +```shell +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/php/bin +``` + +crontab 的 PATH 值为 + +```shell +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/php/bin +``` + +crontab 的 PATH 值为 + +```shell +PATH=/usr/bin:/bin +``` + +/usr/local/php/bin / 下面存在 php 命令,在控制台执行成功 +$ php index.php +因在 crontab 的 PATH 变量无 / usr/local/php/bin/,其执行 php 命令则会失败。 + +如何解决?已知哪个环境变量导致问题,可以直接在 crontab 配置中加入变量配置。 +不知哪个环境变量导致问题,终极大招是引入控制台环境变量,如下: + +```shell +* * * * * source /$HOME/.bash_profile && command +``` + +当然,对于某特定环境变量或有特定的处理方式,如 PATH,命令使用绝对路径亦可解决。 + +**特殊符号 %** +---------- + +% 在 crontab 是特殊符号,具体含义如下: + +第一个 % 表示标准输入的开始,如下示例: + +```shell +* * * * * cat >> /tmp/cat.txt 2>&1 % stdin input +``` + +执行成功之后,查看 / tmp/cat.txt + +```shell +$ cat /tmp/cat.txt + stdin input +``` + +我们看到标准输入写入到了 / tmp/cat.txt 文件。理解上面示例,首先需知 cat >> /tmp/cat.txt ,作用是将标准输入重定向至 / tmp/cat.txt。 + +其余 % 表示换行符,示例如下: + +```shell +* * * * * cat >> /tmp/cat_line.txt 2>&1 % stdin input 1 % stdin input 2 % stdin input 3 +``` + +查看输出 + +```shell +$ cat /tmp/cat_line.txt +stdin input 1 +stdin input 2 +stdin input 3 +``` + +可以发现这里有三行输出。那么如何解决?既然是特殊字符,自然而然就想到了使用 \ 进行转义,如下: + +```shell +* * * * * cat >> /tmp/cat_special.txt 2>&1 % per cent is \%. 2>&1 +``` + +查看输出: + +```shell +$ cat /tmp/cat_special.txt +per cent is %. +``` + +执行成功了。自此,你就顺利爬出了 % 特殊字符问题的坑。关于这个问题的具体说明,可以参看附录中的《Crontab and %》。 + +**关于输出重定向** +----------- + +当我们不做输出重定向时,如任务有大量输出,或许有些无法解释的问题。 + +输出写入邮件,crontab 任务输出默认写入到执行用户的邮件中,如下演示: + +```shell +* * * * * date +``` + +命令输出当前日期,下面查看当前用户的邮件 + +```shell +$ cat /var/spool/mail/$USER +... +Sat Dec 31 17:45:01 CST 2016 +``` + +由此可见,任务输出的日期信息写入到了用户邮件中。如任务有大量输出,会占用磁盘资源。但编者测试显示,如磁盘容量不足,任务也会执行,但输出不会写入邮件; + +我们可以关闭邮箱功能。设置 MAILTO 环境变量为空。如下: + +```shell +MAILTO="" +* * * * * date +``` + +是不是关闭邮件写入就好了?附录《Linux 中的 crontab 与 sendmail》博文表明,关闭 mail 功能,输出内容将写入到 / var/spool/clientmqueue 中,可能占满分区的 inode 资源,导致任务无法执行。inode 资源使用情况可通过如下命令获取 + +```shell +$ df -i +Filesystem Inodes IUsed IFree IUse% Mounted on +/dev/sda1 512000 378 511622 1% /boot +/dev/sda2 92672000 185351 92486649 1% / +``` + +抱歉!这种情况编者并未测出!但在公司的生产环境发现过未重定向则任务不执行的情况,加上后解决了问题。百度也搜索到了类似问题,如有朋友了解,欢迎指教,万分感谢。 + +当然,为了避免此类问题发生,建议任务都加上输出重定向,如下: + +```shell +* * * * * date >> /dev/null/ 2>&1 +``` + +输出到 / dev/null 中,标准输入和标准错误都应处理。如大家对重定向有疑惑,附录中的 Linux 重定向的解释不错。在技术的世界,当我们不按常理做事,事情也不会按常理犯错。 + +**调试大招** +-------- + +最后的福利,编者根据自己的总结而梳理出一套快速定位 crontab 错误的思路。两个角度: + +一是任务是否执行 + +二看命令是否正确 + +**任务是否执行** +---------- + +调试思路:首先,通过日志确认任务是否执行。然后,如未执行则分析定时语句。最后,定时没有问题,检查 crond 服务是否开启。 + +下面说明具体分析步骤。 + +调试错误,日志通常是个利器,crontab 也有日志。我的服务器中 crontab 日志文件位置为 / var/log/cron。 + +**查看日志** + +日志中包含任务执行记录,配置错误提示,任务配置编辑重载记录,服务开启等记录。 +下面是日志的部分内容: + +```shell +$ vim /var/log/cron +... +Dec 31 19:17:01 localhost crond[1455]: (CRON) bad day-of-week (/var/spool/cron/root) +Dec 31 19:17:01 localhost CROND[4409]: (root) CMD (date) ... +``` + +这里截取了对调试比较重要的两条记录 + +```shell +Dec 31 19:17:01 localhost CROND[4409]: (root) CMD (date) +``` + +显示 12 月 21 19 时 17 分 1 秒执行了 date 命令。 + +**配置错误** + +```shell +Dec 31 19:17:01 localhost crond[1455]: (CRON) bad day-of-week (/var/spool/cron/root) +``` + +上面显示 / var/spool/cron/root 的任务配置有错,也就是 root 任务配置有错。错误原因:bad day-of-week,星期配置有错。语句是这样的: + +```shell +* * * * date >> /dev/null 2>&1 +``` + +明显缺少了星期时间段。 + +确认定时语句,通过上面的日志分析,如任务没有执行,使用定时语句在线分析工具分析定时是否正确,非常简单。 + +确认服务开启,如果定时语句也正确,检查服务是否开启。检测命令如下 + +Systemd 方式 (centos7 及以上) + +```shell +$ systemctl status crond.service +``` + +SysVinit 方式 (centos7 以下) + +```shell +$ service crond status +``` + +查看命令输出,如未开启,执行如下命令开启 + +Systemd 方式 (centos7 及以上) + +```shell +$ systemctl start crond.service +``` + +SysVinit 方式 (centos7 以下) + +```shell +$ service crond start +``` + +确认任务成功后,如问题仍未解决,继续往下看。 + +**命令是否正确** +---------- + +确认命令成功与否,这里总结步骤大致如下: + +获取命令执行输出,crontab 中的命令执行出错,多数人都不知道如何调试。我们知道在控制台执行命令时,可通过输出获取错误信息调试问题。这种方式在 crontab 同样适用,方法就是利用重新向获取输出,进行分析。示例如下: + +```shell +* * * * * php /root/index.php >> /tmp/debug.log 2>&1 +``` + +这条任务总是执行失败,我们把输出重定向到 / tmp/debug.log。查看 debug.log,如下: + +```shell +$ cat /tmp/debug.log +/bin/sh: php: command not found +/bin/sh: php: command not found +``` + +显示 php 命令没有找到,很明显的可以确定是环境变量的问题。这种方式定位问题非常有效。 + +具体问题具体分析。有了命令执行的输出,下面就是具体问题具体分析了。或许是前面提到的各种坑,也或许是命令本身所独有的问题。 + +调试的方法到这里就说完了。但还是实践为王,需持续总结,同时也希望大家不要在同样的坑中重复犯错。 + +参考附录 +---- + +[让你学会 Linux 计划任务](https://link.zhihu.com/?target=http%3A//os.51cto.com/art/201001/176402.htm) +[Linux 中的 crontab 与 sendmail](https://link.zhihu.com/?target=http%3A//www.server110.com/sendmail/201311/3125.html) +[Crontab and %](https://link.zhihu.com/?target=http%3A//www.hcidata.info/crontab.htm) +[Linux 重定向](https://link.zhihu.com/?target=http%3A//www.jb51.net/os/RedHat/1120.html) \ No newline at end of file diff --git a/ubuntu/Linux 下解决 Java 输出文件,中文变问号和中文乱码问题.md b/linux/ubuntu/Linux 下解决 Java 输出文件,中文变问号和中文乱码问题.md similarity index 100% rename from ubuntu/Linux 下解决 Java 输出文件,中文变问号和中文乱码问题.md rename to linux/ubuntu/Linux 下解决 Java 输出文件,中文变问号和中文乱码问题.md diff --git a/ubuntu/MediaWiki搭建以及pivot皮肤配置.md b/linux/ubuntu/MediaWiki搭建以及pivot皮肤配置.md similarity index 100% rename from ubuntu/MediaWiki搭建以及pivot皮肤配置.md rename to linux/ubuntu/MediaWiki搭建以及pivot皮肤配置.md diff --git a/ubuntu/Ubuntu netplan 网络配置.md b/linux/ubuntu/Ubuntu netplan 网络配置.md similarity index 100% rename from ubuntu/Ubuntu netplan 网络配置.md rename to linux/ubuntu/Ubuntu netplan 网络配置.md diff --git a/ubuntu/Ubuntu20.04 设置开机自启.md b/linux/ubuntu/Ubuntu20.04 设置开机自启.md similarity index 100% rename from ubuntu/Ubuntu20.04 设置开机自启.md rename to linux/ubuntu/Ubuntu20.04 设置开机自启.md diff --git a/ubuntu/assets/01e4de9a92894823926700f8d7527f5b.png b/linux/ubuntu/assets/01e4de9a92894823926700f8d7527f5b.png similarity index 100% rename from ubuntu/assets/01e4de9a92894823926700f8d7527f5b.png rename to linux/ubuntu/assets/01e4de9a92894823926700f8d7527f5b.png diff --git a/ubuntu/assets/1643267257621244.png b/linux/ubuntu/assets/1643267257621244.png similarity index 100% rename from ubuntu/assets/1643267257621244.png rename to linux/ubuntu/assets/1643267257621244.png diff --git a/ubuntu/assets/1643267285121077.png b/linux/ubuntu/assets/1643267285121077.png similarity index 100% rename from ubuntu/assets/1643267285121077.png rename to linux/ubuntu/assets/1643267285121077.png diff --git a/ubuntu/assets/1643267320249485.png b/linux/ubuntu/assets/1643267320249485.png similarity index 100% rename from ubuntu/assets/1643267320249485.png rename to linux/ubuntu/assets/1643267320249485.png diff --git a/ubuntu/assets/1643267361627502.png b/linux/ubuntu/assets/1643267361627502.png similarity index 100% rename from ubuntu/assets/1643267361627502.png rename to linux/ubuntu/assets/1643267361627502.png diff --git a/ubuntu/assets/20201223103738935.png b/linux/ubuntu/assets/20201223103738935.png similarity index 100% rename from ubuntu/assets/20201223103738935.png rename to linux/ubuntu/assets/20201223103738935.png diff --git a/ubuntu/assets/20201223103800889.png b/linux/ubuntu/assets/20201223103800889.png similarity index 100% rename from ubuntu/assets/20201223103800889.png rename to linux/ubuntu/assets/20201223103800889.png diff --git a/ubuntu/assets/20201223103837439.png b/linux/ubuntu/assets/20201223103837439.png similarity index 100% rename from ubuntu/assets/20201223103837439.png rename to linux/ubuntu/assets/20201223103837439.png diff --git a/ubuntu/assets/20201223103852538.png b/linux/ubuntu/assets/20201223103852538.png similarity index 100% rename from ubuntu/assets/20201223103852538.png rename to linux/ubuntu/assets/20201223103852538.png diff --git a/ubuntu/assets/20201223103909625.png b/linux/ubuntu/assets/20201223103909625.png similarity index 100% rename from ubuntu/assets/20201223103909625.png rename to linux/ubuntu/assets/20201223103909625.png diff --git a/ubuntu/assets/20201223104001940.png b/linux/ubuntu/assets/20201223104001940.png similarity index 100% rename from ubuntu/assets/20201223104001940.png rename to linux/ubuntu/assets/20201223104001940.png diff --git a/ubuntu/assets/20201223163227878.png b/linux/ubuntu/assets/20201223163227878.png similarity index 100% rename from ubuntu/assets/20201223163227878.png rename to linux/ubuntu/assets/20201223163227878.png diff --git a/ubuntu/assets/2022012713362496593.jpg b/linux/ubuntu/assets/2022012713362496593.jpg similarity index 100% rename from ubuntu/assets/2022012713362496593.jpg rename to linux/ubuntu/assets/2022012713362496593.jpg diff --git a/ubuntu/assets/2aa05b0bdc0f4fe6b6e87cb6f5be4132.png b/linux/ubuntu/assets/2aa05b0bdc0f4fe6b6e87cb6f5be4132.png similarity index 100% rename from ubuntu/assets/2aa05b0bdc0f4fe6b6e87cb6f5be4132.png rename to linux/ubuntu/assets/2aa05b0bdc0f4fe6b6e87cb6f5be4132.png diff --git a/ubuntu/assets/5f05716ed04f48c080981fbe3a2f76ce.png b/linux/ubuntu/assets/5f05716ed04f48c080981fbe3a2f76ce.png similarity index 100% rename from ubuntu/assets/5f05716ed04f48c080981fbe3a2f76ce.png rename to linux/ubuntu/assets/5f05716ed04f48c080981fbe3a2f76ce.png diff --git a/ubuntu/assets/634f96ea28654d0eb785870dbff30e66.png b/linux/ubuntu/assets/634f96ea28654d0eb785870dbff30e66.png similarity index 100% rename from ubuntu/assets/634f96ea28654d0eb785870dbff30e66.png rename to linux/ubuntu/assets/634f96ea28654d0eb785870dbff30e66.png diff --git a/ubuntu/assets/6b9f1fd3cbae498f81bc734c0e1e513a.png b/linux/ubuntu/assets/6b9f1fd3cbae498f81bc734c0e1e513a.png similarity index 100% rename from ubuntu/assets/6b9f1fd3cbae498f81bc734c0e1e513a.png rename to linux/ubuntu/assets/6b9f1fd3cbae498f81bc734c0e1e513a.png diff --git a/ubuntu/assets/b3bae3ffc05d4eeb90f411e63b0bd770.png b/linux/ubuntu/assets/b3bae3ffc05d4eeb90f411e63b0bd770.png similarity index 100% rename from ubuntu/assets/b3bae3ffc05d4eeb90f411e63b0bd770.png rename to linux/ubuntu/assets/b3bae3ffc05d4eeb90f411e63b0bd770.png diff --git a/ubuntu/assets/bc18570392904484b79e6643af4d2495.png b/linux/ubuntu/assets/bc18570392904484b79e6643af4d2495.png similarity index 100% rename from ubuntu/assets/bc18570392904484b79e6643af4d2495.png rename to linux/ubuntu/assets/bc18570392904484b79e6643af4d2495.png diff --git a/ubuntu/assets/c40ba4901cf64719a342fec89de50329.png b/linux/ubuntu/assets/c40ba4901cf64719a342fec89de50329.png similarity index 100% rename from ubuntu/assets/c40ba4901cf64719a342fec89de50329.png rename to linux/ubuntu/assets/c40ba4901cf64719a342fec89de50329.png diff --git a/ubuntu/assets/dfdd550aa39645fbb6e4af83dad2b173.png b/linux/ubuntu/assets/dfdd550aa39645fbb6e4af83dad2b173.png similarity index 100% rename from ubuntu/assets/dfdd550aa39645fbb6e4af83dad2b173.png rename to linux/ubuntu/assets/dfdd550aa39645fbb6e4af83dad2b173.png diff --git a/ubuntu/assets/f1217a97bebf47af847b262e8c6af524.png b/linux/ubuntu/assets/f1217a97bebf47af847b262e8c6af524.png similarity index 100% rename from ubuntu/assets/f1217a97bebf47af847b262e8c6af524.png rename to linux/ubuntu/assets/f1217a97bebf47af847b262e8c6af524.png diff --git a/ubuntu/assets/fd6ca3f8abcc4cd99496126c49125d1c.png b/linux/ubuntu/assets/fd6ca3f8abcc4cd99496126c49125d1c.png similarity index 100% rename from ubuntu/assets/fd6ca3f8abcc4cd99496126c49125d1c.png rename to linux/ubuntu/assets/fd6ca3f8abcc4cd99496126c49125d1c.png diff --git a/ubuntu/assets/fe128a2af10140af9a57262bc83636a5.png b/linux/ubuntu/assets/fe128a2af10140af9a57262bc83636a5.png similarity index 100% rename from ubuntu/assets/fe128a2af10140af9a57262bc83636a5.png rename to linux/ubuntu/assets/fe128a2af10140af9a57262bc83636a5.png diff --git a/ubuntu/开机自启动.md b/linux/ubuntu/开机自启动.md similarity index 100% rename from ubuntu/开机自启动.md rename to linux/ubuntu/开机自启动.md diff --git a/ubuntu/怎样修改 linux 时区.md b/linux/ubuntu/怎样修改 linux 时区.md similarity index 100% rename from ubuntu/怎样修改 linux 时区.md rename to linux/ubuntu/怎样修改 linux 时区.md diff --git a/ubuntu/手把手教你在 Ubuntu 20.04 系统上添加 swap 交换空间.md b/linux/ubuntu/手把手教你在 Ubuntu 20.04 系统上添加 swap 交换空间.md similarity index 100% rename from ubuntu/手把手教你在 Ubuntu 20.04 系统上添加 swap 交换空间.md rename to linux/ubuntu/手把手教你在 Ubuntu 20.04 系统上添加 swap 交换空间.md diff --git a/ubuntu/设置SSH密钥登录.md b/linux/ubuntu/设置SSH密钥登录.md similarity index 100% rename from ubuntu/设置SSH密钥登录.md rename to linux/ubuntu/设置SSH密钥登录.md diff --git a/linux/常用命令.md b/linux/常用命令.md new file mode 100644 index 0000000..f871a80 --- /dev/null +++ b/linux/常用命令.md @@ -0,0 +1,44 @@ +### Linux 常用命令 + +#### 文件操作命令 + +学习优先级最高,Linux 一切皆文件 + +- pwd:显示当前所在目录 +- ls:查看目录下的文件 +- mkdir:创建目录 +- cd:切换当前目录 +- mv:移动文件或目录 +- cp:复制文件或目录 +- rm:删除文件或目录 +- zip:压缩文件 +- unzip:解压文件 + +#### 系统信息命令 + +后端开发重点,可使用这些命令进行异常分析 + +- top:查看进程及资源占用情况 +- ps:查看进程信息 +- free:查看内存占用情况 +- df:查看磁盘占用情况 +- ifconfig:查看网络接口信息 +- netstat:查看网络状态信息 + +#### 文件查看命令 + +后端开发重点,可使用这些命令快速定位项目日志中的异常信息 + +- cat:查看文件内容 +- head:查看文件开头内容 +- tail:查看文件末尾内容 +- grep、sed、awk 三剑客:灵活查找和处理文件内容 + +#### 用户权限命令 + +一般是给 Linux 运维(管理员)使用 + +- useradd:添加用户 +- userdel:删除用户 +- chmod:修改文件或目录权限 +- chown:修改文件或目录所有者 \ No newline at end of file