Linux命令行与shell脚本编程 3e

# 第三章 基本的bash shell命令 ## 3.3 bash手册 man **keyword** / **keyword** -help ## 3.4浏览文件系统 cd 相对路径,绝对路径,当前目录,父目录 ,根目录 .. . ./ ~ - -返回进入此目录之前所在的目录 ls - -d仅展示目录名,而不展示目录下的内容列表 - -F 区分 文件夹和文件 - -R 递归子目录和子文件 - -l 展示详细信息 - -a 显示隐藏 文件 - -i 显示系统为文件分配的唯一编号 inode 编号 - script_name:过滤,可以配合通配符 * ? 使用 - \-\-time=atime:显示的事件为访问时间而非更改时间。 ## 3.6 处理文件 #### 创建 touch - -a 只改变访问时间,不更改修改时间 #### 复制 cp source destination - -i是否覆盖已有文件 - destination可以是地址+文件名 也可以只有地址。但注意只有地址的时候,目录的最后一定加上/。避免最后一级文件在复制前其实并不存在的情况,在这种情况,将会复制一个与文件目录最后一级同名的文件。 - 单点'.'在复制文件这里很适合用于将其他目录的文件复制到当前目录下 ```shell cp /etc/NetworkManager/NetworkManager.conf . ``` - -R:递归地复制整个目录 #### 链接文件 - ln -s data_file ln_data_file 符号链接,ln_data_file为独立的文件,拥有不同的修改时间,并且有不同于原文件的大小 - ls -i data_file ln_data_file 硬链接,拥有相同的编号,文件大小,修改时间 - 硬链接只能在同一储存媒体创建。 - 不要创建链接文件的链接文件,应该选择对原文件创建链接文件,一面连接链错乱。 #### 移动或重命名文件:mv - mv a b a和b在同一个目录,名字不同则为重命名。 ab目录不同则为移动目录,移动目录的文件名字也可以不同,此时既移动了目录,也更改了名字。 #### 3.6删除文件:rm - -i确认是否要删除该文件 - -f不受提示影响,强制删除文件 - -r 递归 ## 3.7 处理目录 #### 3.7.1创建目录 mkdir - -p连续创建多个父子连接的目录 mkdir parent_floder/child_floder #### 3.7.2 删除目录 - rmdir:删除空目录 - rm -r 删除文件夹内的文件后,再删除文件夹 - -rm -rf 强制删除文件夹及其中内容,且无提示 ## 3.8查看文件内容 #### 3.8.1查看文件类型 file #### 3.8.2 查看整个文件 **1) cat** - -n 显示行号 - -b 只给有文本内容的行加上行号 - -T 不显示制表符效果,使用^I替换所有制表符 **more/lsee** - 空格翻页,回车下一行。 - 可退出形式的查看部分文件:more/less(less提供更多功能) #### 3.8.3查看部分文件:tail/head tail/head file_name:显示最后/开头10行 tail -n num 显示最后num行文本 # 第四章 更多的bash shell 命令 ## 4.1检测程序 #### 4.1.1 探查进程 ps - 默认只展示运行在当前控制台下属于当前用户的进程。 **ps的3中命令行参数类型:Unix, BSD, GNU 单破折线/无破折线/双破折线** - a 全部进程 - l 长列表 #### 4.1.2事实监测进程 top #### 4.1.3结束进程 **kill命令** kill [pid] **killall命令:通过进程名来识别需要结束的任务** kill process_name 可以使用通配符 ## 4.2监测磁盘空间 # 第6章 使用Linux环境变量 ## 6.1 什么是环境变量 **用于储存有关shell会话和工作环境的信息** **分为全局变量和局部变量** #### 6.1.1全局环境变量 - 所有的shell以及其子shell都是可见的。 - printenv/env 不带参数可以查看所有的全局变量 - printenv env_var / env $env_var 可以展示指定环境变量的内容 #### 6.1.2局部环境变量 - 没有单独变量用于展示局部环境变量,set用于展示包含全局和局部环境变量。 ## 6.2设置用户定义变量 #### 6.2.1 设置局部用户定义变量 - 赋值 my_variable='zz' 注意整个语句不能有空格,否则会将第一个空格前的内容解释为shell命令。提示command not found - 引用 $echo $my_variable - 局部变量一般使用小写,全局变量使用大写 - 自定义变量只在当前shell可用,不可在其余shell(包含该shell的子shell或父shell) #### 6.2.2设置全局环境变量 - 先建立全局变量,再把它导入到全局变量中 ```shell $ my_variable='zz' $ export my_variable $ #此时的变量将可以在子shell中使用 ``` - 更改子shell中的全局变量,不影响其他shell中该变量的值。 ## 6.3 删除环境变量 uset ```shell unset my_variable #注意不需要$符号 ``` - 在子进程中删除了一个全局环境变量,父进程中该全局环境变量不受影响。 ## 6.4默认的shell环境变量 **参见本书的p111表 6-1** ## 6.5设置PATH环境变量 - 作用系统会搜索在PATH环境变量包含的目录,这些文件夹下的可执行文件,可以不指定路径而直接以文件名执行。 - 查看 echo $PATH - 添加PATH环境变量 ```shell $ PATH=$PATH:/home/ubuntu/Scripts ``` - 若当前的工作目录需要加入PATH环境变量 ```shell $ PATH=$PATH:. #直接以.代表当前目录即可 ``` - 本节定义的变量只能持续到退出或系统重启 ## 6.2定义系统环境变量(让环境变量持久化) - 启动文件/环境文件: 当登入Linux系统启动一个bash shell时,bash会朝招几个文件中的命令。这些文件被称为启动文件/环境文件 - 启动bash shell有三种方式,下面为三种方式的启动文件的介绍。(登录时作为默认登录shell/作为非登录shell的交互式shell/作为运行脚本的非交互式shell) #### 6.6.1 登录shell 以下五个启动文件 - /etc/profile - $HOME/.bash_profile - $HOME/.bash_login - $HOME/.profile - $HOME/.bashrc ##### 1)/etc/profile 系统上默认的bash shell的主启动文件。系统上的每个用户登录时都会执行。 - 使用for循环运行/etc/profile.d目录下的所有文件。 ```shell if [ -d /etc/profile.d ]; then for i in /etc/profile.d/*.sh; do if [ -r $i ]; then . $i fi done unset i ##### 2) 其余4个$HOME目录下的启动文件 - 提供一个用户专属的启动文件来定义该用户所用到的环境变量,大多数Linux发行版只使用这其中的一到两个。 shell会按照顺序运行前三个文件,如果其中一个被运行,余下的则被忽略。 - $HOME/.bash_profile - $HOME/.bash_login - $HOME/.profile #### 6.6.2 交互式shell进程 如果不是登录系统是启动的,则称为交互式shell。 则不会访问/etc/profile启动文件,只会访问HOME目录中的.bashrc文件。(这就是为什么在配置Jupyter notebook的时候,将相关路径加入~/.bashrc而非上方的3个文件) #### 6.6.3非交互式shell - 系统执行脚本时使用的shell。它没有命令行提示符。 - BASH_ENV用于这种情况下,运行一些特定启动的命令。 - 如果没有设置BASH_ENV,shell脚本可以继承并使用父shell中已经设置并且导出的变量。 #### 6.6.4 环境变量持久化 方法:将变量放入环境变量。 - /etc/profile 可能随着发行版的升级而更新覆盖,自定义的变量将失效 - 在/etc/profile.d中新建一个.sh文件。把所有修改/定义的变量放入这个文件。系统启动运行etc/profile时将运行这里的sh文件。 - 大多数发行版中,使用~/.bashrc储存永久性bash shell文件。但如果设置了BASH_ENV变量,如果BASH_ENV没有指向~/.bashrc,那么将不会生效。 ## 6.7数组变量 ```shell $ # 括号中 空格分隔 $ myarr=(one two three) $ #使用索引来引用对应的值 $ echo $(myarr[1]) $ #查看整个数组 索引值改为星号 $ #使用unset来删除数组的值。 ``` # 第7章 理解Linux文件权限 ## 7.1 Linux的安全性 - uid 登录名-密码 #### 7.1.1 /etc/passwd 包含以下内容 - 用户登录名 - 用户密码(默认显示为x) - 用户账户的uid - 用户账户的组ID - 用户账户的文本描述 - 用户HOME目录的位置 - 用户的默认shell #### 7.1.2 /etc/shadow文件 用于管理用户的密码以及密码有效期限的文件 - 登录名 - 加密后的密码 - 自上次修改密码后过去的天数密码 - 多少天后才能更改密码 - 多少天后必须更改密码 - 密码过期前多少天提醒用户更改密码 - 密码过期后多少天禁用用户账户 - 用户账户被禁用的日期 - 预留字段 #### 7.1.3 添加新用户 useradd - m 新建HOME目录 ```shell $ useradd -m test ``` #### 7.1.4 删除用户 userdel - r 删除用户的HOME目录以及邮件目录 ```shell $ userdel -r test ``` #### 7.1.4 用户修改 **1. usermod** - -l 修改登录名 - -L 锁定账户 - -p 修改账户的密码 - -U 解除锁定,使用户能够登陆。 **2. passwd和chpasswd** passwd:更改单个用户密码 - e 下次登录必须更改密码 ```shell $ passwd test $ #会让重复输入两次密码以更改该用户密码 ``` chpasswd:使用标准文件批量更改用户密码 文件每行为 userid:passwd **3. chsh, chfn和change** ## 7.2使用Linux组 #### 7.2.1 /cat/group文件 - 组名 - 组密码 - GID - 属于该组的用户列表 **当一个用户在/etc/passwd文件中指定某个组为默认组时,用户账户不会作为该组成员再出现在/etc/group文件中。** 当一个用户同时是多个组中的成员时,在/etc/passwd文件中记录的是用户所属的主组,也就是登录时所属的默认组,而其他组称为附加组。 #### 7.2.2 创建新组 **groupadd 创建无用户新组** ```shell $ groupadd groupname ``` **usermod 往新组添加用户** - g 更改用户的默认组 - G添加该组到用户的属组的列表里,不会影响默认组 ```shell $ user -G/g shared test ``` #### 7.2.3修改组 groupmod - -n 修改组名 - -g 修改组id ```shell $ groupmod -n new_name shared ``` ## 7.3 理解文件权限 #### 7.3.1使用文件权限符 ls -l 展示的第一个字段就是描述文件和目录权限的编码。 第一个字符描述了类型 - - 文件 - d目录 - l链接 - c代表字符型设备 - b代表块设备 - n代表网络设备 之后有3组字符的编码。分别为属主/属组/系统的其他用户。 每个组内的权限包含3类rwx,若某个位置为-则表示没有该种权限。 #### 7.3.2 默认文件权限 umask 查看新建文件的默认权限。 umask 返回4位数字。第一位为黏着位 后3位分别代表3个属的文件权限 ![885b5c1b9ee80b0c0861743963821e44.png](en-resource://database/16687:1) 但注意umask是返回的掩码,新建文件的默认权限实际上是使用全权限值(文件666目录777)减去umask值(8进制的计算)。比如若umask 为026,那么文件的默认权限为640(rw-r-----)。 ## 7.4 改变安全性设置 #### 7.4.1改变权限:chmod - 格式chmod options mode file - mode分为两种:八进制模式和符号模式 **1)选项option** - -R配合file的通配符,递归的将权限操作应用于多个文件和子目录。 **2)八进制模式** chmod 760 file **3)符号模式** [ugoa][+-=][rwxXstugo] 3各参数分别表示 - 用户组 - 增减等于 - 要应用的权限 ```shell $ chmod o+r file #对所有三个属 增加 r(读取权限) $ chmod g-x file #对属组用户移除执行权限 ``` #### 7.4.2 改变所属关系 **1) chown 改变文件属主** *chown options owner[.group] file* ```shell $ chown dan newfile #改变文件属主 $ chown dan.shared newfile #改变文件的属主(dan)和属组(shared) $ chown .shared new file #只改变文件的默认属组 $ chown test. newfile #如果Linux采用和用户登录名匹配的组名,在属主后加.,可以同时改变属主和属组 ``` - 只有root用户可以改变用户的属主,任何属主都可以改变用户的属组,只要属主属于更改前后两个属组的成员。 **2) chgrp改变文件的默认属组** chgrp shared newfile # 第9章 安装软件程序 - PMS(package management system):曝光李系统 - 基于Debian的发行版(如Ubuntu和Linux Mint)使用dpkg命令 - 基于Red Hat的发行版(Fedora,openSUSE)使用rmp命令 - 书本介绍了两种,仅总结dpkg的aptidue命令。 - [aptitude 和 apt-get/cache的比较](https://lvii.github.io/system/2011-11-30-aptitude-vs-apt-get/) ## 9.2 基于Debian的系统 #### 用aptitude管理软件包 **aptitude** - show pkgname :查看包信息 - install pkgname:安装包 - remove pkgname:移除包 - search pkgname:不完全匹配方式搜索包 - 展示所有匹配的包 - 包名前的i表示已安装。vp表示可安装 - purge pkgname:移除包以及相关的配置文件和文件夹 - safe-upgrade:安全升级全部包 - full-upgrade/dist-upgrade:稍激进的升级策略 **dpkg** - -L pkgname 展示这个已安装包的全部文件 - -search absolute_file_name 展示这个绝对文件路径属于的包 #### aptitude仓库 清单地址:/etc/apt/sources.list # 第10章 使用编辑器 # 第11章 构建基本脚本 ## 11.2创建shell脚本文件 **基本脚本实例** ```shell #!/bin/bash date who ``` - 井号后的内容为注释,不会进行运行。 - 在脚本目录下直接以脚本名运行,如果该目录不在环境变量PATH内,将无法找到该可执行文件。可以通过添加至$PATH或使用./script_name的方式来运行。 - 新创建的问价你如果用户没有执行权限,需要新增用户对文件的执行权限。 ```shell $ chmod u+x file_name ``` ## 11.3显示消息:echo - 如果需要展示双引号/单引号,那么使用单引号/双引号来划定字符串。 - -n 将文本字符串和命令输出显示在同一行 ```shell $ echo "who is running this:" $ who ``` ## 11.4使用变量 #### 11.4.1使用环境变量 - 使用美元符号引用 - 可以在双引号中引用 - 如果需要展示美元符号$而非引用变量,可以使用反斜杠进行反转义。 - ${}也是常见的变量引用方式,花括号用于帮助系统识别变量名。 #### 11.4.2用户变量 - 组成:字母,数字,下划线 - 区分大小写 - 系统会自动决定变量的数据类型 - 变量在对应的shell脚本的结束前将一直有效 - 与系统变量的引用方式一致。 - 定义举例 ```shell var1=10 var2=testing var3="more params" ``` #### 11.4.3 命令替换 将b命令的输出赋值给b变量。 - 反引号符和$()格式 ```shell $testing=`date` $testing=${date} ``` ## 11.5 重定向输入和输出 #### 11.5.1 输出重定向 - >指定输出的接收 ```shell $ date > receive_file ``` - \>> 两个大于号,表示追加而非替换,当receive_file存在时 ```shell $ who >> receive_file ``` #### 11.5.2 输入重定向 **将文件的内容重定向到命令。** - 普通输入重定向 ```shell $ wc < test6 2 11 6 # wc 直接接文件名,将会多返回一个字符串--文件名 $wc test6 2 11 6 test6 ``` - 内联输入重定向:使用命令行而非文件来输入字符。开始和结尾使用相同的符号囊括内容。 ```shell $ wc << EOF >wuliking >jung un >EOF 2 2 16 ``` ## 11.6 管道 - 使用管道符号 “|”拼接多个命令,前一个命令的输出将作为后一个命令的输入。 - 下方的命令会以more的方式展示sort后的file_name文件的内容 ```shell $ cat file_name|sort|more ``` ## 11.7 执行数学运算 #### 11.7.1 expr命令 ```shell $ expr 1 + 5 6 $ expr 1 * 4 expr: syntax error $ expr 1 \* 4 4 ``` #### 11.7.2 方括号 ```shell $ var1=$[1 + 5] $ echo $var1 6 # 不赋值或不是用echo命令,直接写方括号表达式将报错 $ $[1 + 2] 3: command not found # 因为算式的结果直接输入,将3作为命令解释 ``` - 星号也不会被解释成通配符,可以直接使用 - 但是不能进行浮点计算 #### 11.7.3 浮点解决方案 **使用bash内建的bc计算器:bc** **基本用法:shell下使用bc命令行进入bash计算器** ```shell $ bc #进入bc计算器 $ bc -q #进入bc计算器,且不显示普通进入时展示的bc欢迎信息 1+2 3# 进入后shell输入后,shell输入符消失,可以进行运算输入输出 ``` - 在bc内可以设置scale符号,指定输出结果的小数位数 - quit退出bc **脚本中使用bc** - 基本格式:variable=$echo "options; expression" | bc) ```shell variable=$echo "scale=4; 3.44/5" | bc) echo $var1 ./test .6880 ``` - 使用内联重定向 ```shell $ cat test12 #!/bin/bash var1=10.46 var2=43.67 var3=33.2 var4=71 var5=$(bc << EOF scale = 4 a1 = ( $var1 * $var2) b1 = ($var3 * $var4) $ #这一串输入的最终输出是a1 + b1 a1 + b1 EOF ) echo The anwser is $var5 ``` ## 11.8 退出脚本 - 退出码:范围0 - 255。0表示正常执行退出。 #### 11.8.1 查看退出状态码 - 运行中结束后,立刻运行 $? 命令可以返回退出码。 #### 11.8.2 exit命令:指定退出码 - 脚本最后一行 exit 5/exit $var_name。 - 但是如果后面指定的常量值或变量的值大于255,那么将会返回整除255的余数。 # 第12章 使用结构化命令 ## 12.1 使用If-then语句 ```shell if command then commands fi # 例子 if pwd then echo "it worked" fi ``` - 只要command运行成功(状态为0),commands就会被执行。 ## 12.2 if-then-else 语句 ```shell if command then commands else commands fi ``` ## 12.3嵌套if:elif ## 12.4 test命令 - if-then语句无法执行状态码之外的条件。 - if test variable - if [ condition ] #### 12.4.1 数值比较 - -eq/ge/gt/le/lt/ne n2 等于,大于等于,大于,小于等于,小于,不等于 - 无法测试浮点数 #### 12.4.2 字符串比较 - str1=/!=// str2 - 等于,不等于,小于,大于 - 大于小于号必须进行转义,否则会解释为重定向。 - 大于小于的顺序和sort命令的顺序是不同的。比较命令认为大小小于小写,而sort相反。因为比较使用![7f55d776031c419152890b05a67a6c4f.png](en-resource://database/16727:1) ASCII顺序,而sort使用本地的设置,本地设置的小写字母在大写字母前。 - -n/z str1 长度为非0,长度为0 #### 12.4.3 文件比较 ![0b22ac682d375b9f946b6691110d334a.png](en-resource://database/16725:0) ## 12.5 复合条件 - and:[ condition1 ] && [ condition2 ] - or: [ condition1 ] || [ condition2 ] ## 12.6 if-then的高级特性 #### 12.6.1使用双括号:数学表达式 ((expression)) ![52d58212ee86764e041f4ea99b684b24.png](en-resource://database/16729:1) 除了表中的高级表达式外,也可以使用上方的大于小于等基础比较符号,且不需要使用反转义符。 #### 12.6.2 使用方括号:高级字符串表达式 [[expression]] 可以使用匹配模式 ```shell $ if [[ $USER == r* ]] # 如果用户名是r开头 ``` ## 12.7 case命令 ```shell case variable in pattern1 | pattern2) commands1;; pattern3) commands2;; *) # 在上方列出的模式外的所有情况,执行默认指令 default commands;; esac ``` - pattern是可以使用通配符的。 # 第13章 更多的结构化命令 ## 13.1 for命令 基本格式 ```shell for var in list do commands done ``` #### 13.1.1读取列表中的值 for test in hunan hubei shanxi - 使用空格分隔 - 如果需要使用单引号,或单个元素中间需要使用空格,可以使用双引号囊括 #### 13.1.3 从变量读取列表 ```shell list="hunan hubei shanxi" # 在已有字符串变量后新增字符串的方法 list=$list" henan" for state in $list do echo "Have you ever visited $state" done ``` #### 13.1.4 从命令中读取值 ```shell file="states" # cat获取文件states中的内容,再使用for进行遍历 for state in $(cat $file) do echo "Visiting $state done ``` #### 13.1.5 更改字符分隔符 - IFS Internal field separator,内部字段分隔符 - bash shell默认空格,制表符,换行符为IFS - 修改IFS:IFS=$'\n - 备份当前IFS并在之后恢复:IFS.OLD=$IFS;IFS=$'\n';IFS=$IFS.OLD - 指定多个字符为IFS: IFS='\n:;'#换行,分号,冒号为IFS #### 13.1.6 用通配符读取目录 for file in CHAP13/* #file遍历全部文件和文件夹 **可以在一个for语句,连续遍历多个文件夹,以空格分隔即可** for file in CHAP13/* CHAP14/* ## 13.2 C语言风格的for命令 **基本格式** for (( variable assignment ; condition ; iteration process)) ```shell # 并不需要使用美元符来引用变量 for (( a = 1 ; a < 10 ; a++)) do echo "The next number is $i" done ``` #### 13.2.2 使用多个变量 ```shell # 同时迭代a和b for (( a = 1, b = 10; a <= 10; a++, b-- )) do echo "$a - $b" done ``` ## 13.3 while命令 #### 13.3.1 基本格式 ```shell while command do commands done ``` #### 13.3.2 使用多个测试命令 - while后进行多个测试命令。最后一个测试命令的退出状态码会决定是否退出循环。 ```shell var1 = 10 while echo $var1 # 每个测试命令为单独一行 [ $var1 -ge 0] do echo "loop is not ended" var1=$[ $var1 - 1] done ``` ## 13.4 until命令 **格式,多个命令测试与while一致;跳出循环的方式与while相反** ## 13.5嵌套循环 **(与其他语言一样) while, for, until都是可以互相嵌套的。** ## 13.6 循环处理文件数据 **使用两个for嵌套循环,每层分别指定不同的IFS,来遍历/etc/paswd的每行,每个冒号分隔符的内容。** ## 13.7 控制循环 #### 13.7.1 break命令 **1.一层循环时,跳出循环** **2.多层循环时,break:跳出最内层循环 break n 由内往外在停止n层的循环** #### 13.7.2 continue循环 **与break终止一层循环不同,continue只会提前终止某次循环(在这次循环中,不执行continue后的命令)** **continue n 可以指定往外跳多少层,继续命令。** #### 13.8 处理循环的输出 在done命令 加一个输出命令实现for循环内的所有输出到屏幕的内容输出到文件。 也可以像管道一样,将整个循环作为管道中的一节(一个命令)。 # 第14章 处理用户输入 $0 程序名 - 在程序启用语句后,以空格分隔参数 ./test3.sh param1 param2 - 如果单个参数内包含空格,以双引号包围该参数 $1 - $9 第i个参数 参数序号大于9时的参数引用方式 ${10} - 如果不是在当前目录下直接调用脚本,那么$0将传递包含路径的脚本名。可以使用basename命令引用不包含路径的脚本名 ```shell $ name=${basename $0} ``` - 判断参数是否存在 ```shell $ if [ -n "$1" ] $ ... $fi ``` ## 14.2特殊参数变量 - $# 返回参数的个数 - 花括号内不能使用美元符,而需使用感叹符或赋值给其他参数 ```shell $ #使用叹号引用 $ echo the last parameter was ${!#} $ #使用参数赋值方法引用 $ params=$# $ echo the last parameter was $params ``` #### 14.2.2 获取所有参数 - $* 将所有参数作为一个整体引用。 - $@ 将所有参数作为多个独立的单词,可以使用for等方式进行遍历。 - 注意使用时需要在$* $@ 加上双引号[why?] ## 14.3 移动变量 **shift :所有参数往左移动一个位置,第一个被删除。理解成python的list pop(0)** - 常用于传了未知个数的参数,使用一个while [ -n "$1"]的循环,依次遍历$1这个变量,再shift,以此遍历全部参数。 - 可以加参数n来移动n个位置: shift n。 ## 14.4 处理选项 ## 第X章 零散 cron 周期性调度 crontab -l - date +%Y%m%d 返回加号后指定样式的日期 日期加减计算-d ```shell date -d"2 week ago 1 day ago" +"%Y%m%d" ```

你可能感兴趣的:(Linux命令行与shell脚本编程 3e)