Linux系统知识大分享!

Linux系统知识

一、inode

1.1 什么是inode

理解inode,要从文件储存说起。

文件储存在硬盘上,硬盘的最小存储单位叫做"扇区"(Sector)。每个扇区储存512字节(相当于0.5KB)。

操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个"块"(block)。这种由多个扇区组成的"块",是文件存取的最小单位。"块"的大小,最常见的是4KB,即连续八个 sector组成一个 block。

文件数据都储存在"块"中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为"索引节点"。

每一个文件都有对应的inode,里面包含了与该文件有关的一些信息。

1.2 inode的内容

inode包含文件的元信息,具体来说有以下内容:

   `* 文件的字节数     * 文件拥有者的User ID     * 文件的Group ID     * 文件的读、写、执行权限     * 文件的时间戳,共有三个:ctime指inode上一次变动的时间,mtime指文件内容上一次变动的时间,atime指文件上一次打开的时间。     * 链接数,即有多少文件名指向这个inode     * 文件数据block的位置`

Linux下可以通过stat命令查看文件的信息:

stat example.txt

1.3 inode的大小

inode也会消耗硬盘空间,所以硬盘格式化的时候,操作系统自动将硬盘分成两个区域。一个是数据区,存放文件数据;另一个是inode区(inode table),存放inode所包含的信息。

每个inode节点的大小,一般是128字节或256字节。inode节点的总数,在格式化时就给定,一般是每1KB或每2KB就设置一个inode。假定在一块1GB的硬盘中,每个inode节点的大小为128字节,每1KB就设置一个inode,那么inode table的大小就会达到128MB,占整块硬盘的12.8%。

查看每个硬盘分区的inode总数和已经使用的数量,可以使用df命令。

df -i

查看每个inode节点的大小,可以用如下命令:

sudo dumpe2fs -h /dev/hda | grep "Inode size"

由于每个文件都必须有一个inode,因此有可能发生inode已经用光,但是硬盘还未存满的情况。这时,就无法在硬盘上创建新文件。

1.4 inode号码

每个inode都有一个号码,操作系统用inode号码来识别不同的文件。

这里值得重复一遍,Unix/Linux系统内部不使用文件名,而使用inode号码来识别文件。对于系统来说,文件名只是inode号码便于识别的别称或者绰号。

表面上,用户通过文件名,打开文件。实际上,系统内部这个过程分成三步:首先,系统找到这个文件名对应的inode号码;其次,通过inode号码,获取inode信息;最后,根据inode信息,找到文件数据所在的block,读出数据。

使用ls -i命令,可以看到文件名对应的inode号码:

ls -i example.txt

1.5 目录文件

Unix/Linux系统中,目录(directory)也是一种文件。打开目录,实际上就是打开目录文件。

目录文件的结构非常简单,就是一系列目录项(dirent)的列表。每个目录项,由两部分组成:所包含文件的文件名,以及该文件名对应的inode号码。

理解了上面这些知识,就能理解目录的权限。目录文件的读权限(r)和写权限(w),都是针对目录文件本身。由于目录文件内只有文件名和inode号码,所以如果只有读权限,只能获取文件名,无法获取其他信息,因为其他信息都储存在inode节点中,而读取inode节点内的信息需要目录文件的执行权限(x)。

1.6 硬链接

一般情况下,文件名和inode号码是"一一对应"关系,每个inode号码对应一个文件名。但是,Unix/Linux系统允许,多个文件名指向同一个inode号码。

这意味着,可以用不同的文件名访问同样的内容;对文件内容进行修改,会影响到所有文件名;但是,删除一个文件名,不影响另一个文件名的访问。这种情况就被称为"硬链接"(hard link)。

ln命令可以创建硬链接:

ln 源文件 目标文件

源文件与目标文件的inode号码相同,都指向同一个inode。inode信息中有一项叫做"链接数",记录指向该inode的文件名总数,这时就会增加1。

反过来,删除一个文件名,就会使得inode节点中的"链接数"减1。当这个值减到0,表明没有文件名指向这个inode,系统就会回收这个inode号码,以及其所对应block区域。

这里顺便说一下目录文件的"链接数"。创建目录时,默认会生成两个目录项:“.“和”…”。前者的inode号码就是当前目录的inode号码,等同于当前目录的"硬链接";后者的inode号码就是当前目录的父目录的inode号码,等同于父目录的"硬链接"。所以,任何一个目录的"硬链接"总数,总是等于2加上它的子目录总数(含隐藏目录)。

1.7 软链接

除了硬链接以外,还有一种特殊情况。

文件A和文件B的inode号码虽然不一样,但是文件A的内容是文件B的路径。读取文件A时,系统会自动将访问者导向文件B。因此,无论打开哪一个文件,最终读取的都是文件B。这时,文件A就称为文件B的"软链接"(soft link)或者"符号链接(symbolic link)。

这意味着,文件A依赖于文件B而存在,如果删除了文件B,打开文件A就会报错:“No such file or directory”。这是软链接与硬链接最大的不同:文件A指向文件B的文件名,而不是文件B的inode号码,文件B的inode"链接数"不会因此发生变化。

ln -s命令可以创建软链接:

ln -s 源文件 目标文件

1.8 inode的特殊作用

由于inode号码与文件名分离,这种机制导致了一些Unix/Linux系统特有的现象。

    1. 有时,文件名包含特殊字符,无法正常删除。这时,直接删除inode节点,就能起到删除文件的作用。
    1. 移动文件或重命名文件,只是改变文件名,不影响inode号码。
    1. 打开一个文件以后,系统就以inode号码来识别这个文件,不再考虑文件名。因此,通常来说,系统无法从inode号码得知文件名。

第3点使得软件更新变得简单,可以在不关闭软件的情况下进行更新,不需要重启。因为系统通过inode号码,识别运行中的文件,不通过文件名。更新的时候,新版文件以同样的文件名,生成一个新的inode,不会影响到运行中的文件。等到下一次运行这个软件的时候,文件名就自动指向新版文件,旧版文件的inode则被回收。

二、Shell

2.1 Bash的模式扩展

hell 接收到用户输入的命令以后,会根据空格将用户的输入,拆分成一个个词元(token)。然后,Shell 会扩展词元里面的特殊字符,扩展完成后才会调用相应的命令。这种特殊字符的扩展,称为模式扩展(globbing)。其中有些用到通配符,又称为通配符扩展(wildcard expansion)。Bash 一共提供八种扩展。

    1. 波浪线扩展~: 自动扩展成当前用户的主目录
    1. 问号扩展?:代表文件路径里面的任意单个字符,不包括空字符
    1. 星号扩展*:代表文件路径里面的任意数量的任意字符,包括零个字符
    1. 方括号扩展[]:文件名匹配,即扩展后的结果必须符合现有的文件路径。如果不存在匹配,就会保持原样,不进行扩展。方括号扩展还有两种变体:[^...][!...]。它们表示匹配不在方括号里面的字符,这两种写法是等价的。
    1. 方括号简写扩展[a-z]:表示匹配一个连续的范围
    1. 大括号扩展{}:表示分别扩展成大括号里面的所有值,各个值之间使用逗号分隔。内部的逗号前后不能有空格,大括号也可以与其他模式联用,并且总是先于其他模式进行扩展
    1. 大括号简写扩展{a-z}:表示扩展成一个连续序列
    1. 变量扩展$:扩展成变量值
    1. 子命令扩展$():可以将子命令的运行结果作为父命令的参数
    1. 算术扩展$(()):扩展成整数运算的结果
    1. 字符类扩展[[:class:]]:扩展成某一类特定字符之中的一个
  • [[:alnum:]]:匹配任意英文字母与数字

  • [[:alpha:]]:匹配任意英文字母

  • [[:blank:]]:空格和 Tab 键。

  • [[:cntrl:]]:ASCII 码 0-31 的不可打印字符。

  • [[:digit:]]:匹配任意数字 0-9。

  • [[:graph:]]:A-Z、a-z、0-9 和标点符号。

  • [[:lower:]]:匹配任意小写字母 a-z。

  • [[:print:]]:ASCII 码 32-127 的可打印字符。

  • [[:punct:]]:标点符号(除了 A-Z、a-z、0-9 的可打印字符)。

  • [[:space:]]:空格、Tab、LF(10)、VT(11)、FF(12)、CR(13)。

  • [[:upper:]]:匹配任意大写字母 A-Z。

  • [[:xdigit:]]:16进制字符(A-F、a-f、0-9)。

  • [![:digit:]]:在第一个中括号之后加!表示否定

    1. 先解释,再执行:Bash 接收到命令以后,发现里面有通配符,会进行通配符扩展,然后再执行命令
    1. 不匹配时,会原样输出:文件名扩展在没有可匹配的文件时,会原样输出
    1. 只适用于单层路径?*这样的通配符,不能匹配路径分隔符(/),Bash 4.0允许**匹配零个或多个子目录
    1. 文件名可以使用通配符:文件名包括特殊字符,需要把文件名放在单引号里面

2.2 Here文档

Here 文档(here document)是一种输入多行字符串的方法,格式如下:

<< token   text   token

开始标记由两个小于号和文档名称组成,结束标记是顶格写的文档名称,中间的是文档内容。

Here 文档内部会发生变量替换,同时支持反斜杠转义,但是不支持通配符扩展,双引号和单引号也失去语法作用,变成了普通字符。如果不希望发生变量替换,可以把 Here 文档的开始标记放在单引号之中。

<< 'token'   text   token

Here 文档的本质是重定向,所以,Here 字符串只适合那些可以接受标准输入作为参数的命令,对于其他命令无效,比如echo命令就不能用 Here 文档作为参数。

Here 文档还有一个变体,叫做 Here 字符串(Here string),使用三个小于号(<<<)表示

# 对ddd进行md5计算   md5sum <<< 'ddd'

2.3 环境变量

环境变量
# 查看所有环境变量   env   # or   printenv   # 查看指定环境变量   printenv PATH   # or   echo $PATH
自定义变量

自定义变量是用户在当前 Shell 里面自己定义的变量,一旦退出当前 Shell,该变量就不存在了。

set命令可以显示所有变量(包括环境变量自定义变量),以及所有的 Bash 函数

创建变量:var=value

读取变量:$var

删除变量:unset var

输出变量:export var/export var=value

用户创建的变量仅可用于当前 Shell,子 Shell 默认读取不到父 Shell 定义的变量。为了把变量传递给子 Shell,需要使用export命令。子 Shell 如果修改继承的变量,不会影响父 Shell.

特殊变量

$?:上一个命令执行的退出码,0表示成功,非0表示失败

$$:当前Shell的进程ID

$_:上一条命令的最后一个参数,比如上一条命令ls a.txt b.txt c.txt$_表示c.txt

$!:最近一个后台执行的异步命令的进程 ID

$0:当前 Shell 的名称(在命令行直接执行时)或者脚本名(在脚本中执行时)

$-:当前 Shell 的启动参数

$#/$@:脚本的参数数量

变量默认值

machine=elk01为例

${machine:-elk01}:如果变量machine存在且不为空,则返回它的值,否则返回elk01

${machine:=elk01}:如果变量machine存在且不为空,则返回它的值,否则将它设为elk01,并且返回elk01

${machine:+elk01}:如果变量名存在且不为空,则返回elk01,否则返回空值。目的是测试变量是否存在

${machine:?"变量machine未定义"}:如果变量machine存在且不为空,则返回它的值,否则打印出变量machine未定义并中断脚本的执行。

上面四种语法如果用在脚本中,变量名的部分可以用数字19,表示脚本的参数。

filename=${1:?"filename missing."}
declare命令

declare命令可以声明一些特殊类型的变量,为变量设置一些限制,比如声明只读类型的变量和整数类型的变量。语法如下:

declare OPTION var=value

-a:声明数组变量

-f:输出所有函数定义

-F:输出所有函数名

-i:声明整数变量

-l:声明变量为小写字母

-p:查看变量信息

-r:声明只读变量

-u:声明变量为大写字母

-x:变量输出为环境变量

declare命令如果用在函数中,声明的变量只在函数内部有效,等同于local命令。不带任何参数时,等同于不带有任何参数的set命令。

let命令

let命令声明变量时,可以直接执行算术表达式

let命令的参数表达式如果包含空格,就需要使用引号

let可以同时对多个变量赋值,赋值表达式之间使用空格分隔。

字符串

字符串长度
var=hello   echo ${#var}   # 5
子字符串
${var:offset:length}   # offset 默认0   # length 默认字符串长度

用法和python的var[3:3]的用发类似,省略了python的步长参数。同样也支持负数,但是表示的意义不一样。

$ foo="This string is long."   echo ${foo: -5:-2}   # lon

offset-5,表示从倒数第5个字符开始截取,所以返回long.。如果指定长度length2,则返回lo;如果length-2,表示要排除从字符串末尾开始的2个字符,所以返回lon

搜索和替换
# 如果 pattern 匹配变量 variable 的开头,   # 删除最短匹配(非贪婪匹配)的部分,返回剩余部分   ${variable#pattern}      # 如果 pattern 匹配变量 variable 的开头,   # 删除最长匹配(贪婪匹配)的部分,返回剩余部分   ${variable##pattern}

通过下面的方式可以截取文件路径的文件名

${path##*/}   # */可以匹配路径前面的部分,删除后只剩下文件名
# 将头部匹配的部分,替换成其他内容   # 模式必须出现在字符串的开头   ${variable/#pattern/string}

上面是字符串头部匹配的模式,下面展示的字符串尾部匹配模式,用法与头部匹配一样

# 如果 pattern 匹配变量 variable 的结尾,   # 删除最短匹配(非贪婪匹配)的部分,返回剩余部分   ${variable%pattern}      # 如果 pattern 匹配变量 variable 的结尾,   # 删除最长匹配(贪婪匹配)的部分,返回剩余部分   ${variable%%pattern}

还有任意位置的匹配模式

# 如果 pattern 匹配变量 variable 的一部分,   # 最长匹配(贪婪匹配)的那部分被 string 替换,但仅替换第一个匹配   ${variable/pattern/string}      # 如果 pattern 匹配变量 variable 的一部分,   # 最长匹配(贪婪匹配)的那部分被 string 替换,所有匹配都替换   ${variable//pattern/string}

算术运算

算术表达式

((...))会自动忽略内部的空格,但是这个语法不返回值,如果算术结果为0表示命令执行失败,所以如果执行(( 3 - 3 ))命令行会提示失败。如果需要获取运算结果可以通过(( v = 1 + 1))或者v=$((1 + 1))(等号两边不能有空格)赋值给变量。

((...))支持的运算符号:

  • +:加号

  • -:减号

  • *:乘号

  • /:整除

  • %:余数

  • **:指数

  • ++:自增

  • --:自减

嵌套的写法:$(((5**2) * 3))或者$(($((5**2)) * 3))$((...))圆括号的变量可以省略$符号。

进制

默认是十进制

  • 0123:八进制

  • 0x123:十六进制

  • base#10101:base进制

位运算

$((...)):支持的位运算:

  • <<:向左位移

  • >>:向右位移

  • &:位与运算

  • |:或运算

  • ~:否运算

  • ^:异或运算

逻辑运算

$((...)):支持的逻辑运算:

  • <:小于

  • >:大于

  • <=:小于等于

  • >=:大于等于

  • ==:相等

  • !=:不等于

  • &&:逻辑与

  • ||:逻辑或

  • !:逻辑否

  • exp ? a : b:三元运算符

求值运算

,$(())内部是求值运算符

 `⚡ echo $((foo=2+4, 4*9))   36    ⚡ echo $foo   6`
expr命令

expr命令支持算术运算

expr 3 + 2   # 运算表达式中间需要空格
let命令
let x=2+3   # 运算表达式中间不能有空格

行操作

快捷键
  • Ctrl + a:光标移动到行首

  • Ctrl + e:光标移动到行尾

  • Ctrl + d:删除光标位置的字符

  • Ctrl + w:删除光标前面的单词

  • Ctrl + t:光标位置的字符与它前面一位的字符交换位置

  • Alt + t:光标位置的单词与它前面一位的单词交换位置

  • Alt + l:将光标位置至词尾转为小写

  • Alt + u:将光标位置至词尾转为大写

  • Ctrl + k:剪切光标位置至行尾

  • Ctrl + u:剪切光标位置至行首

  • Alt + d:剪切光标位置到词尾

  • Alt + Backspace(删除键):剪切光标位置至词首

  • Ctrl + y:在光标位置粘贴

历史记录

bash会保存用户的操作历史,保存在~/.bash_history文件中,默认存储500条记录,环境变量HISTFILE指向这个文件。

history命令能显示操作历史,该命令会在所有命令前面加上编号,通过定制环境变量HISTTIMEFORMAT可以显示每个历史操作的执行时间。

export HISTTIMEFORMAT='%F %T  '

通过history命令返回的历史记录和编号,可以通过!编号的方式快速执行指定编号的命令。

history -c可以清楚历史操作记录,设置export HISTSIZE=0不保存本次操作历史。

!感叹号命令的用途

  • !!:执行上一条命令

  • !n:执行历史文件里指定编号的命令

  • !-n:执行当前命令之前的第n条命令 ⚡echo 1 1 ⚡ echo 2 2 ⚡ echo 3 3 ⚡ !-1 ⚡ echo 3 3

  • !string:执行最近一条以指定字符串string开头的命令这种模式只能匹配命令,不能匹配参数由于这种模式会扩展成匹配历史操作,所以需要正常使用感叹号!的时候需要转义一下

  • !?string:执行最近一条包含指定字符串string的命令

  • ^str1^str2:执行最近一条包含str1的命令,并将其替换成str2

Ctrl + r快捷键会进入一个交互模式,会根据输入查找开头匹配输入的历史命令

 `⚡  echo 3   bck-i-search: e_`

只输入e,会按最新的顺序查找立即操作中以e开头的命令并展示在操作行中,回车即会执行。

脚本

脚本第一行通常是指定解释器,即这个脚本必须通过什么解释器执行。

#!/bin/sh   # or   #!/bin/bash   # or   #!/usr/bin/env bash   # env命令在/usr/bin目录下,返回bash可执行文件的位置

如果没有第一行的Shebang,就不能直接通过./script.sh的方式运行,必须通过将脚本传给解释器执行:

/bin/sh script.sh   # or   bash script.sh
参数
  • $0:脚本文件名

  • $1`$9`:对应第19个参数,第10个参数用${10}引用

  • $#:参数的总和

  • $@:全部的参数,参数之间使用空格分隔

  • $*:全部的参数,参数之间使用变量$TFS值的第一个字符分隔,默认为空格

getopts命令
while getopts 'lha:' OPTION; do     case "$OPTION" in       l)         echo "linuxconfig"         ;;          h)         echo "h stands for h"         ;;          a)         avalue="$OPTARG"         echo "The value provided is $OPTARG"         ;;       ?)         echo "script usage: $(basename $0) [-l] [-h] [-a somevalue]" >&2         exit 1         ;;     esac   done   shift "$(($OPTIND - 1))"

-l-h-a三个参数,只有-a可以带参数值,所以a的后面必须带有冒号,对于输入的lha之外的命令将会匹配到?,匹配到的参数中,$OPTARG保存参数值,比如-a foo$OPTARG保存的就是foo。

exit命令

exit命令用于终止当前脚本的执行,并向 Shell 返回一个退出值。0表示正常,1表示发生错误,2表示用法不对,126表示不是可执行脚本,127表示命令没有发现。如果脚本被信号N终止,则退出值为128 + N。简单来说,只要退出值非0,就认为执行出错。

exitreturn命令的差别是,return命令是函数的退出,并返回一个值给调用者,脚本依然执行。exit是整个脚本的退出,如果在函数之中调用exit,则退出函数,并终止脚本执行。

source命令

source命令最大的特点是在当前 Shell 执行脚本,不像直接执行脚本时,会新建一个子 Shell。所以,source命令执行脚本时,不需要export变量。

source命令的另一个用途,是在脚本内部加载外部库。

#!/bin/bash   source ./lib.sh   function_from_lib

source有一个简写形式,可以使用一个点(.)来表示。

$ . .bashrc
read命令

有时,脚本需要在执行过程中,由用户提供一部分数据,这时可以使用read命令。

read [-options] [variable...]

如果用户的输入项少于read命令给出的变量数目,那么额外的变量值为空。如果用户的输入项多于定义的变量,那么多余的输入项会包含到最后一个变量中。

#!/bin/bash   echo Please, enter your firstname and lastname   read FN LN   echo "Hi! $LN, $FN !"

如果read命令之后没有定义变量名,那么环境变量REPLY会包含所有的输入。

#!/bin/bash   # read-single: read multiple values into default variable   echo -n "Enter one or more values > "   read   echo "REPLY = '$REPLY'"

read命令除了读取键盘输入,可以用来读取文件。

#!/bin/bash   filename='/etc/hosts'   while read myline   do     echo "$myline"   done < $filename

-t 参数

-t参数可以设置超时秒数,如果超过了指定时间,用户仍然没有输入,脚本将放弃等待,继续向下执行。

-p 参数

-p参数指定用户输入的提示信息。

-a 参数

-a参数把用户的输入赋值给一个数组,从零号位置开始。

$ read -a people   alice duchess dodo   $ echo ${people[2]}   dodo

-n 参数

-n参数指定只读取若干个字符作为变量值,而不是整行读取。

-e 参数

-e参数允许用户输入的时候,使用readline库提供的快捷键,比如自动补全。

-d 参数

-d参数定义用户输入的结束符,而不是回车符。

-s 参数

-s参数使得用户的输入不显示在屏幕上,这常常用于输入密码或保密信息。

IFS变量

IFS(内部字段分隔符,Internal Field Separator 的缩写)默认值是空格、Tab 符号、换行符号,通常取第一个(即空格)。

比如读取/etc/passwd文件内容

root:x:0:0:root:/root:/usr/bin/zsh   daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin

文件中每一行内容的字段由冒号(:)分隔,如果需要将一行内容映射到各个字段变量中,指定IFS为冒号(:)即可

# 假设行内文本读取到变量file_info中   IFS=":" read user pw uid gid name home shell <<< "$file_info"

IFS的赋值命令和read命令写在一行,这样的话,IFS的改变仅对后面的命令生效,该命令执行后IFS会自动恢复原来的值。如果不写在同一行,则需要在命令执行完之后恢复IFS为原来的值。

OLD_IFS="$IFS"   IFS=":"   read user pw uid gid name home shell <<< "$file_info"   IFS="$OLD_IFS"

条件判断

if

if条件判断的语法结构

if commands; then     commands   [elif commands; then     commands...]   [else     commands]   fi

ifthen写在同一行时,需要分号分隔。分号是 Bash 的命令分隔符。它们也可以写成两行,这时不需要分号。

* if后面可以跟任意数量的命令。这时,所有命令都会执行,但是判断真伪只看最后一个命令,即使前面所有命令都失败,只要最后一个命令返回0,就会执行then的部分。

test

test命令的三种形式:

# 写法一   test expression      # 写法二 方括号和表达式之间必须有括号,因为方括号本身是一条命令   [ expression ]      # 写法三 支持正则判断   [[ expression ]]
参数说明备注说明备注说明
-a文件是否存在-b是否块文件-c是否字符设备
-d文件是否目录-e是否存在-h是否符号链接
-N自上次读取是否被修改-r是否可读-s是否不为空
-S是否socket-w是否可写-x是否可执行
f1 -nt f2f1是否比f2新f1 -ot f2f1是否比f2旧f1 -ef f2是否引用相同的inode
-n字符串长度大于0-z字符串长度为0s1 = s2字符串相等
s1 == s2字符串相等s1 != s2字符串不相等s1 ‘>’ s2字典顺序s1在s2之后
-eq整数相等-ne整数不相等-le整数小于等于
-lt整数小于-ge整数大于等于-gt整数大于
=~正则匹配
case

case结构的语法:

case expression in     pattern )       commands ;;     pattern )       commands ;;     ...   esac

匹配模式:

  • a):匹配a

  • a|b):匹配ab

  • [[:alpha:]]):匹配单个字母。

  • ???):匹配3个字符的单词。

  • *.txt):匹配.txt结尾。

  • *):匹配任意输入,通过作为case结构的最后一个模式。

Bash 4.0之前,case结构只能匹配一个条件,然后就会退出case结构。Bash 4.0之后,允许匹配多个条件,这时可以用;;&终止每个条件块。

循环

while
while condition; do     commands   done
until
until condition; do     commands   done

until和while相反,while只要条件满足会一直执行,until是一旦条件满足就停止。

for…in
for variable in list; do     commands   done
for
for (( expression1; expression2; expression3 )); do     commands   done
break

break命令立即终止循环,程序继续执行循环块之后的语句,即不再执行剩下的循环。

continue

continue命令立即终止本轮循环,开始执行下一轮循环。

select

select结构主要用来生成简单的菜单。它的语法与for...in循环基本一致。

select name   [in list]   do     commands   done

举例

#!/bin/bash   # select.sh      select brand in Samsung Sony iphone symphony Walton   do     echo "You have chosen $brand"   done

函数

函数的定义:

# 第一种   fn() {     # codes   }      # 第二种   function fn() {     # codes   }

通过declare命令查看当前Shell定义的所有函数: declare -f(输出函数体)/declare -F(不输出函数体),通过unset命令删除函数:unset -f functionName

在函数体内通过参数变量$1~`$9访问传入的第1-第9个参数, 0 ‘ 表示函数名, ‘ 0`表示函数名,` 0‘表示函数名,#表示参数总数, @ ‘ 表示全部参数,用空格分隔, ‘ @`表示全部参数,用空格分隔,` @‘表示全部参数,用空格分隔,*`表示全部参数,用IFS的默认分隔符分隔。

局部变量

函数体内声明的变量属于全局变量,整个脚本都可以读取,通过local关键字来声明变量为局部变量,仅在函数体内访问。

数组

数组的定义:

ARRAY=(value1 value2 ... valueN)   # or   ARRAY=(     value1     value2     value3   )   # or 通过指定位置赋值   ARRAY=([2]=value3 [0]=value1 [1]=value1)

通过通配符赋值:

mp3s=( *.mp3 )   # 将当前目录的所有 MP3 文件,放进一个数组。

通过参数声明数组:

# 声明   declare -a ARRAYNAME      # 将用户输入读入数组   read -a var

访问数组元素

array[0]=a   # 不指定位置默认为0   # array=a   echo ${array[0]}

@*是特殊索引,返回数组的所有成员:${array[@]}/${array[*]}

一般配合for...in遍历数组,注:一般把${array[@]}放在双引号之中

activities=( swimming "water skiing" canoeing "white-water rafting" surfing )   for act in ${activities[@]}; # <<-不加双引号会有问题   do       echo "Activity: $act";   done

数组activities实际包含5个元素,但是for...in循环直接遍历${activities[@]},会导致返回7个结果。加上双引号可以避免这种情况。

拷贝数组

# 拷贝activities数组并添加一个元素   hobbies=( "${activities[@]}" diving )

数组长度

${#array[*]}或者${#array[@]}会返回数组的长度

非空位置

${!array[@]}或者${!array[*]}会返回数组中有值的元素位置

数组截取

${array[@]:pos:length}从pos位置开始截取数组array长度为length的子数组返回,length省略则返回从pos开始的所有元素

数组拼接

+=符号可以对数组进行追加:

foo=(a b c)   foo+=(d e f)   # foo = a b c d e f

如果是拼接两个数组遍历:

foo=(a b c)   bar=(d e f)   foo+=${bar[@]}   # foo = ad e f b c

正确的拼接方法:

foo=(a b c)   bar=(d e f)   foo=(${foo[@]} ${bar[*]})   # @和*可以相互替换使用   # foo = a b c d e f

删除元素

unset命令可以删除元素:

# 删除角标为2的元素   unset foo[2]   # 删除数组   unset foo

关联数组

declare -A声明关联数组,使用和其他语言里的hash或者map对象相似

declare -A colors   colors["red"]="#ff0000"   colors["green"]="#00ff00"   colors["blue"]="#0000ff"   echo ${colors["blue"]}

set

Bash 执行脚本时,会创建一个子 Shellset命令用来修改子Shell环境的运行参数。

set -u

Bash会忽略不存在的变量,而不是报错提醒,在脚本头部加上set -u,后面遇到不存在的变量就会报错。

#!/usr/bin/env bash   set -u   # or   # set -o nounset      echo $a   echo bar

即使脚本报错也不会影响脚本后面代码的执行,开发中需要在脚本报错后立即终止执行,放在造成严重的后果。通过command || exit 1只要commad返回非0就立即停止执行脚本。

# 写法一   command || { echo "command failed"; exit 1; }      # 写法二   if ! command; then echo "command failed"; exit 1; fi      # 写法三   command   if [ "$?" -ne 0 ]; then echo "command failed"; exit 1; fi

set -e

代替上面的冗余写法,只要脚本发生错误,就终止执行。set +e表示关闭此功能,或者通过command || true关闭,|| true可以使该行命令始终返回true而不触发异常。

set -e   # or    # set -o errexit

set -o pipefail

set -e有一个例外情况,就是不适用于管道命令。加上set -o pipefail只要一个子命令失败,整个管道命令就失败,脚本就会终止执行。

set -x

用于调试复制的脚本,每行输出的行首如果有+表示脚本的执行,在执行脚本时加上-x参数(bash -x test.sh)也有相同的效果

set -x   # or   # set -o xtrace      set +x   # 后续的脚本执行不再输出调试

常用写法

# 写法一   set -Eeuxo pipefail      # 写法二   set -Eeux   set -o pipefail
shopt

shopt作用和set类似,区别是set是从 Ksh 继承的,属于 POSIX 规范的一部分,而shopt是 Bash 特有的。

临时文件

临时文件通常保存在/tmp目录下,但是存在这一些问题,首先这个目录时所有用户都可以访问的,里面的文件所有用户都可以读;其次临时文件一般在脚本运行完之后需要删除,但是如果脚本意外退出会导致临时文件的堆积。

所以,应该使用mktemp命令来创建临时文件,mktemp命令生成的文件名是随机的,同时只有创建者有权读写,再通过配合使用trap命令来保证脚本退出时临时文件被删除。

#!/bin/bash      trap 'rm -f "$TMPFILE"' EXIT      TMPFILE=$(mktemp) || exit 1   echo "Our temp file is $TMPFILE"

mktemp命令的参数:

  • -d:创建临时目录

  • -p:指定临时文件的目录,默认值是环境变量$TMPDIR的值

  • -t:指定临时文件名的模板,X表示随机字符,比如:mktemp -t mytmp.XXXXXXX表示有7位随机字符

trap命令格式:trap ACTION SIG1 SIG2,其中ACTION表示bash命令,常用的信号有:

  • HUP:编号1,脚本与所在的终端脱离联系。

  • INT:编号2,用户按下 Ctrl + C,意图让脚本终止运行。

  • QUIT:编号3,用户按下 Ctrl + 斜杠,意图退出脚本。

  • KILL:编号9,该信号用于杀死进程。

  • TERM:编号15,这是kill命令发出的默认信号。

  • EXIT:编号0,这不是系统信号,而是 Bash 脚本特有的信号,不管什么情况,只要退出脚本就会产生。

trap命令一般放在脚本的开头位置,否则它上方的命令导致的退出都不会被捕获,多条ACTION命令可以通过函数封装:

function egress {     command1     command2     command3   }      trap egress EXIT

Session

登录Session

登录 Session 是用户登录系统以后,系统为用户开启的原始 Session,通常需要用户输入用户名和密码进行登录。

登录 Session 一般进行整个系统环境的初始化,启动的初始化脚本依次如下。

  • /etc/profile:所有用户的全局配置脚本。

  • /etc/profile.d目录里面所有.sh文件

  • ~/.bash_profile:用户的个人配置脚本。如果该脚本存在,则执行完就不再往下执行。

  • ~/.bash_login:如果~/.bash_profile没找到,则尝试执行这个脚本(C shell 的初始化脚本)。如果该脚本存在,则执行完就不再往下执行。

  • ~/.profile:如果~/.bash_profile~/.bash_login都没找到,则尝试读取这个脚本(Bourne shell 和 Korn shell 的初始化脚本)。

==Linux 发行版更新的时候,会更新/etc里面的文件,比如/etc/profile,因此不要直接修改这个文件。如果想修改所有用户的登陆环境,就在/etc/profile.d目录里面新建.sh脚本。==

如果只是修改个人的登录环境,一般是写在~/.bash_profile里面。

非登录Session

非登录 Session 是用户进入系统以后,手动新建的 Session,这时不会进行环境初始化。比如,在命令行执行bash命令,就会新建一个非登录 Session。

非登录 Session 的初始化脚本依次如下。

  • /etc/bash.bashrc:对全体用户有效。

  • ~/.bashrc:仅对当前用户有效。

对用户来说,~/.bashrc通常是最重要的脚本。非登录 Session 默认会执行它,而登录 Session 一般也会通过调用执行它。每次新建一个 Bash 窗口,就相当于新建一个非登录 Session,所以~/.bashrc每次都会执行。

注意,执行脚本相当于新建一个非互动的 Bash 环境,但是这种情况不会调用~/.bashrc

登出

~/.bash_logout脚本在每次退出 Session 时执行,通常用来做一些清理工作和记录工作,比如删除临时文件,记录用户在本次 Session 花费的时间。

三、SSH

端口转发

SSH 除了登录服务器,还有一大用途,就是作为加密通信的中介,充当两台服务器之间的通信加密跳板,使得原本不加密的通信变成加密通信。这个功能称为端口转发(port forwarding),又称 SSH 隧道(tunnel)。

(1)将不加密的数据放在 SSH 安全连接里面传输,使得原本不安全的网络服务增加了安全性,比如通过端口转发访问 Telnet、FTP 等明文服务,数据传输就都会加密。

(2)作为数据通信的加密跳板,绕过网络防火墙。

端口转发有三种使用方法:动态转发,本地转发,远程转发。下面逐一介绍。

动态转发

动态转发指的是,本机与 SSH 服务器之间创建了一个加密连接,然后本机内部针对某个端口的通信,都通过这个加密连接转发。它的一个使用场景就是,访问所有外部网站,都通过 SSH 转发。

动态转发需要把本地端口绑定到 SSH 服务器。至于 SSH 服务器要去访问哪一个网站,完全是动态的,取决于原始通信,所以叫做动态转发。

$ ssh -D local-port tunnel-host -N

上面命令中,-D表示动态转发,local-port是本地端口,tunnel-host是 SSH 服务器,-N表示这个 SSH 连接只进行端口转发,不登录远程 Shell,不能执行远程命令,只能充当隧道。

注意,这种转发采用了 SOCKS5 协议。访问外部网站时,需要把 HTTP 请求转成 SOCKS5 协议,才能把本地端口的请求转发出去。

本地转发

本地转发(local forwarding)指的是,SSH 服务器作为中介的跳板机,建立本地计算机与特定目标网站之间的加密连接。本地转发是在本地计算机的 SSH 客户端建立的转发规则。

它会指定一个本地端口(local-port),所有发向那个端口的请求,都会转发到 SSH 跳板机(tunnel-host),然后 SSH 跳板机作为中介,将收到的请求发到目标服务器(target-host)的目标端口(target-port)。

$ ssh -L local-port:target-host:target-port tunnel-host

上面命令中,-L参数表示本地转发,local-port是本地端口,target-host是你想要访问的目标服务器,target-port是目标服务器的端口,tunnel-host是 SSH 跳板机。

注意,本地端口转发采用 HTTP 协议,不用转成 SOCKS5 协议。

远程转发

远程端口指的是在远程 SSH 服务器建立的转发规则。

这种场景比较特殊,主要针对内网的情况。本地计算机在外网,SSH 跳板机和目标服务器都在内网,而且本地计算机无法访问内网之中的 SSH 跳板机,但是 SSH 跳板机可以访问本机计算机。

由于本机无法访问内网 SSH 跳板机,就无法从外网发起 SSH 隧道,建立端口转发。必须反过来,从 SSH 跳板机发起隧道,建立端口转发,这时就形成了远程端口转发。

$ ssh -R local-port:target-host:target-port -N local

上面的命令,首先需要注意,不是在本机执行的,而是在 SSH 跳板机执行的,从跳板机去连接本地计算机。-R参数表示远程端口转发,local-port是本地计算机的端口,target-hosttarget-port是目标服务器及其端口,local是本地计算机。

远程端口转发要求本地计算机也安装了 SSH 服务器,这样才能接受 SSH 跳板机的远程登录。

rsync

rsync 是一个常用的 Linux 应用程序,用于文件同步。

它可以在本地计算机与远程计算机之间,或者两个本地目录之间同步文件(但不支持两台远程计算机之间的同步)。它也可以当作文件复制工具,替代cpmv命令。

它名称里面的r指的是 remote,rsync 其实就是“远程同步”(remote sync)的意思。与其他文件传输工具(如 FTP 或 scp)不同,rsync 的最大特点是会检查发送方和接收方已有的文件,仅传输有变动的部分(默认规则是文件大小或修改时间有变动)。

常用参数:

  • -r:递归目录

  • -a:可以替代-r,除了可以递归同步以外,还可以同步元信息(比如修改时间、权限等)。

  • -n:模拟执行结果,并不真正执行命令。

  • --delete:rsync 只确保源目录的所有内容(明确排除的文件除外)都复制到目标目录。它不会使两个目录保持相同,并且不会删除文件。如果要使得目标目录成为源目录的镜像副本,则必须使用--delete参数,这将删除只存在于目标目录、不存在于源目录的文件。

  • --exclude:指定排除模式,可以多次使用。

  • --include:指定必须同步的文件模式。

  • -i:参数表示输出源目录与目标目录之间文件差异的详细情况。

  • --ignore-existing:参数表示只要该文件在目标目录中已经存在,就跳过去,不再同步这些文件。

  • -m:参数指定不同步空目录。

  • -P参数是--progress--partial这两个参数的结合。

  • --partial参数允许恢复中断的传输。不使用该参数时,rsync会删除传输到一半被打断的文件;使用该参数后,传输到一半的文件也会同步到目标目录,下次同步时再恢复中断的传输。一般需要与--append--append-verify配合使用。

  • --progress参数表示显示进展。

  • -z参数指定同步时压缩数据。

    ---------------------------END---------------------------

题外话

“不是只有程序员才要学编程?!”

认真查了一下招聘网站,发现它其实早已变成一项全民的基本技能了。

连国企都纷纷要求大家学Python!
在这里插入图片描述

世界飞速发展,互联网、大数据冲击着一切,各行各业对数据分析能力的要求越来越高,这便是工资差距的原因,学习编程顺应了时代的潮流。

在这个大数据时代,从来没有哪一种语言可以像Python一样,在自动化办公、爬虫、数据分析等领域都有众多应用。

更没有哪一种语言,语法如此简洁易读,消除了普通人对于“编程”这一行为的恐惧,从小学生到老奶奶都可以学会。

《2020年职场学习趋势报告》显示,在2020年最受欢迎的技能排行榜,Python排在第一。
在这里插入图片描述

它的角色类似于现在Office,成了进入职场的第一项必备技能。

如果你也想增强自己的竞争力,分一笔时代的红利,我的建议是,少加点班,把时间腾出来,去学一学Python。

因为,被誉为“未来十年的职场红利”的Python,赚钱、省钱、找工作、升职加薪简直无所不能!

目前,Python人才需求增速高达**174%,人才缺口高达50万,**部分领域如人工智能、大数据开发, 年薪30万都招不到人!在这里插入图片描述

感兴趣的小伙伴,赠送全套Python学习资料,包含面试题、简历资料等具体看下方。

👉优快云大礼包🎁:全网最全《Python学习资料》免费赠送🆓!(安全链接,放心点击)

一、Python所有方向的学习路线

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。

img
img

二、Python必备开发工具

工具都帮大家整理好了,安装就可直接上手!img

三、最新Python学习笔记

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。

img

四、Python视频合集

观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

img

五、实战案例

纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。img

六、面试宝典

在这里插入图片描述

在这里插入图片描述

简历模板在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值