Linux学习

第一章 Linux的由来

  • 开源协议
    alt text

  • POSIX接口规范

    POSIX接口规范,全称为“可移植操作系统接口”(Portable Operating System Interface),是由IEEE(电气和电子工程师协会)和ISO/IEC联合制定的一组操作系统接口规范。其主要目的在于实现不同操作系统之间的兼容性和互操作性,使得开发人员能够跨平台编写和运行应用程序,而无需进行大量的代码修改。

第二章 linux基本命令

期末考试方式

使用exam命令,输入数字选择选项或题目,答案默认保存,若想修改之前的答案,输入对应题号就行

1.连接到学校主机10.188.2.251的帐号密码

账号:s223174
密码:welcome

2.nano指令操作文件

查看和编辑文件

3.环境变量

  1. PATH环境变量: 当shell执行时去哪找可执行文件,依次查找
  2. PS1环境变量: shell命令提示符的样式
  3. LANG环境变量: 与shell交互使用的语言
  4. HOME环境变量: 当前用户工作目录
locale -a 指令
列出所有系统安装的语言

image-20240314232303614

修改PS1环境变量---shell提示符样式
PS1='HAPPY: \u $ '
\u是特殊参数,表示用户名

image-20240314232634341

添加当前目录(.)PATH变量
注意:PATH变量以冒号分割(:),修改PATH环境变量只对本次生效,想要永久生效,可以修改.profile文件
PATH=$PATH:.

image-20240314232909211

4.echo命令

类似于打印命令

image-20240314233218047

5.alias命令

alias可以起别名或者修改命令默认的执行方式

alias 别名=源名
例子alias list=ls

alias命令展示所有已取(默认)别名

unalias取消别名
unalias list

image-20240314233930938

6.Linux中的源字符

  • $美元符: 表示取值
  • 双引号: 对引号里面的源字符进行处理
  • 单引号: 不对引号里面的源字符处理
  • \ 反斜杠: 最常见的转义字符,用于转义其他字符
  • *字符: 任意字符
  • ?问号: ? 可以用来匹配任意单个字符…命令 ls a?.txt 会列出所有以 a 开头,后面跟着任意单个字符,并且以 .txt 结尾的文件
  • [限制条件]: 限制范围,例如 ls /tmp/[123].tmp(区间写法)等价于 ls /tmp/[1-3].tmp(列举写法),表示取出tmp目录下的所有1到3以tmp为后缀的文件,注意变通
  • ^或者!: 表示否定negative
  • ~字符: 表示工作目录,等价于echo $HOME

image-20240315000305427

7.基本命令

  • clear清屏命令

  • cp 源文件路径 目的路径

    例: cp hello hi

    image-20240314234136513
    alt text

  • type xxx命令

    查看指令类型

image-20240314234306905

  • rm 文件路径

    例: rm hi (hi文件已经存在)

    image-20240314234422632

  • ls指令(参数 -l等)

    例: ls -l hello

    • -l参数,显示更详细信息

    image-20240314234557355

  • history命令

    显示shell命令的历史记录

    image-20240315000404594

  • !!直接执行上条命令,!加编号执行history里的第几条记录

    alt text

  • susudo超级用户(管理员)
    su : 把身份切换到超级用户(su之后就切换到root用户,要求输入的是root用户口令)
    su 用户名 : 切换到其他用户
    sudo : 以超级用户的身份执行某条命令(sudo要求输入口令时,输入的是当前用户的口令)
    值得注意的是,只有被指定的用户才能使用sudo命令,在/etc/sudoers文件里的才被允许
    alt text

  • whoami查看当前用户

  • id命令或groups显示所属组

  • hostname查看主机名

    1. hostname
    2. hostname 想要修改的主机名
  • uname查看操作系统内核
    alt text

  • free查看内存
    free -h参数表示human readable 便于人类阅读的
    alt text

  • date显示当前系统时间
    alt text

  • vi命令查看/proc系统信息文件夹下文件
    vi /proc/cpuinfo
    alt text

  • passwd修改口令命令
    以前存放在/etc/passwd下,现在存放在/etc/shadow文件

  • wall广播命令
    您可以使用wall命令来向所有登录用户发送消息。例如,要发送一句话给所有登录用户,您可以在终端中输入以下命令:

echo "这是一条消息" | wall

这将向所有登录用户显示消息“这是一条消息”。请注意,您需要具有适当的权限才能向所有用户发送消息。

  • wwho查看所有当前登陆用户
    alt text

  • seq生成数字序列
    alt text

8.shell命令执行的优先级

  1. alias别名
  2. 内置命令
  3. 可执行文件

9.源字符使用实例(第一次小测)

  1. list files whose name start with a,b or c
    • 解答 : ls [abc]*
  2. list files whose name don’t start with a digit
    • 解答 : ls [^0-9]*
  3. list all files whose names only contains two characters
    • 解答 : ls ??
  4. list all the file whose name contain a $
    • 解答 : ls *\$*
  5. Which file stores the user information?
    • 解答 : /etc/passwd
  6. Which file will be executed once when user login
    • 解答 : .profile.bash_login
  7. Create an alias called “la” for the command “ls -a”
    • 解答 : alias la='ls -a'
  8. The names of the directories that a shell searches to find the file corresponding to an external command are stored in the shell variable named
    • 解答 : PATH

10.链接命令

  1. 软链接
    类似于Windows下的快捷方式,当一个源文件的目录层级比较深,我们想要方便使用它可以给源文件创建一个软链接,方便文件操作
    alt text
ln -s 源文件路径 软链接名字
命令 说明
ln -s 创建软链接

11.解压缩命令

  • .gz和.bz2的压缩包需要使用tar命令来压缩和解压缩
  • 不加z参数,就没有压缩,只是打包成一个文件
压缩格式 说明 压缩包后缀
bz2 常见的压缩文件格式,由bzip2生成,具有高压缩率 .bz2
gz 常见的压缩文件格式,由gzip生成,广泛应用于Linux和Unix系统 .gz
tar命令选项 说明
-c 创建打包文件
-v 显示打包或者解包的详细信息
-f 指定文件名称, 必须放到所有选项后面
-z 压缩(.gz)
-j 压缩(.bz2)
-x 解压缩
-C 解压缩到指定目录

12.关机和重启命令的使用

命令 说明
shutdown -h now 立刻关机。这个命令会立即关闭系统,并断开所有用户的连接。
reboot 重启。这个命令会重新启动系统,相当于按下计算机的重启按钮。

13.远程登陆,远程拷贝命令

13.1 ssh命令的使用

安装步骤:
① 假如Ubuntu作为服务端,需要安装ssh服务端软件,执行命令: sudo apt-get install openssh-server
② 客户端电脑如果是macOS系统则不需要安装ssh客户端软件,默认已经安装过了,直接可以使用ssh命令
③ 客户端电脑如果是Windows系统则需要安装OpenSSH for Windows这个软件

13.2 scp命令的使用

scp 是基于 SSH 进行安全的远程文件拷贝的命令,也就是说需要保证服务端和客户端电脑安装了相应的 SSH 软件。

scp命令格式:

  1. 远程拷贝文件:

    • scp 本地文件 远程服务器用户名@远程服务器ip地址:指定拷贝到远程服务器的路径
    • scp 远程服务器用户名@远程服务器ip地址:远程服务器文件 指定拷贝到本地的路径
  2. 远程拷贝目录:

    • scp -r 本地目录 远程服务器用户名@远程服务器ip地址:指定拷贝到远程服务器的路径
    • scp -r 远程服务器用户名@远程服务器ip地址:远程服务器目录 指定拷贝到本地的路径
    • -r 表示递归拷贝整个目录
      alt text
  3. ssh命令是远程登录主机电脑,相当于直接操作的是远程电脑。

  4. scp命令是可以把本机文件拷贝到远程主机,也可以把远程主机文件远程拷贝的本机,注意:拷贝文件夹需要加-r选项。

  5. 大量的文件上传和下载可以通过可视化工具FileZilla来完成。

软件安装和卸载
安装方式 说明
离线安装:deb文件格式安装 从本地存储介质安装软件,使用Debian软件包格式安装软件
在线安装:apt-get方式安装 通过网络下载并安装软,使用apt-get命令从仓库安装软件
  • 更改镜像源
    因为使用 apt-get 命令默认是从国外的服务器下载安装软件的,会导致下载安装速度很慢,所以需要更改成国内的镜像源服务器
  • 镜像源就是下载软件来源的服务器。
  • apt-get 方式卸载命令: sudo apt-get remove 安装包名

第四章 文件和文件系统

使用文件系统的三个步骤:分区、格式化、挂载

  • Linux系统中把一切可以用字节流传输的都看作一个文件
  • Linux系统里面的文件系统类型ext2 xfs jfs ext3 ext4
  • Windows系统里的文件系统类型: ntfs fat32
    alt text
    第一块SATA硬盘./dev/sda
    第二块SATA硬盘 /dev/sdb
    fdisk /dev/sdb  分区指令
    mkfs -t  ext4 /dev/sdb  格式化指令,-t参数表示要创建什么样的文件系统

Linux系统下的重要文件

  • /etc/passwd: 每个用户在该文件中都有一行记录,其中包含了用户的登录名、密码(通常是一个指向加密密码的指针)、用户ID、组ID、用户信息、用户主目录和默认shell等信息
    1. /usr/bin/chsh可以改变默认用户登陆的shell
    2. /etc/passwd和/usr/bin/passwd是两个不同文件,一个是记录用户口令等信息的文本文件,另一个是改变用户口令的可执行文件
  • /dev/null是Unix和类Unix操作系统(包括Linux)中的一个特殊文件,也被称为“空设备”或“空设备文件”。
    1. 这个文件的主要特点是会丢弃所有写入到它的数据,就好像这些数据进入了一个黑洞一样。同时,当你尝试从/dev/null读取数据时,它会立即返回一个文件结束(EOF)标记。
    2. 在命令行操作中,/dev/null常被用作丢弃不需要的输出。例如,当你运行一个命令但并不关心其输出时,你可以将输出重定向到/dev/null,这样命令的输出就不会显示在终端或写入到其他文件。这对于清理不需要的日志信息或避免在脚本中产生垃圾数据非常有用。
    3. 简单来说,/dev/null就像一个数据的“黑洞”,任何写入其中的数据都会消失无踪,而读取它则不会得到任何实际内容。

mount挂载命令

  • mount [-t vfstype][-o options] device dir
  • vfstype :vfat(window fat32 format)、ext3、ext4、iso9660(cdrom format)
    mount /dev/sdc1 /mnt
    umount .mnt
    umount /dev/sdc1
    alt text

Types of Files文件种类

  • Simple/ordinary Files(普通文件)
  • Directory Files(目录文件)
    目录文件只包含两个字段:一个包含目录下的所有文件名,另一个是索引节点号
    alt text
  • Link Files(链接文件)
    A Link File is created by the system when a symbolic linkin created to an existing file.
  • Special(Device) Files(设备文件)
    A special File is a means of accessinghardware devicesincluding the keyboard, hard disk, Cb-ROM drive, tapedrive andprinter.
    1. character special files(字符设备)
    2. block special files(块设备)
  • Named Pipe(FIFO)
    Tools that enable processes to communicate with each other
  • Socket(网络插口)

动态库和静态库文件

image-20250214101357322

  • widows下

    1. 动态库.dll,不把代码打包进可执行文件,只是链接
    2. 静态库.o
  • linux

    1. 动态.so
    2. 静态.a

Linux下的目录结构

alt text

和文件操作相关的命令

  • df命令 显示分区使用情况(挂载)
    alt text
  • du命令 查看文件分配的存储空间大小
    alt text
    du -s /etc
    # 查看目录占用空间
  • mkdir命令 创建目录
    mkdir -p dira/dirb
    参数p表示创建路径上的所有目录
  • rmdir命令 只能删除空目录
    mkdir dira
    rmdir dira
  • ls命令
    ls -a 目录或文件名
    参数a表示显示出所有以.开头的隐藏文件
    ls -l 目录或文件名
    参数l表示以列表的形式列出详细信息
    alt text
    第一个字段第一个字符表示文件类型,-表示普通文件
    第二个字段: 链接数,有多少个条目指向了这个文件的索引节点
    第三个字段:所属用户
    第四个字段:所属组
    第五个字段:大小(以字节为单位)
    第六个字段:文件最后修改时间
    第七个字段:文件名
    alt text
  • touch命令 创建文件或修改文件最后修改时间
    如果文件不存在,则创建文件,否则修改最后修改时间
  • rm命令 删除文件或目录
    rm -i file1
    参数i表示交互式的
    rm -rf dirb
    参数r表示递归删除子目录在删除所有文件
    参数f表示强制删除
  • mv命令 移动文件或者文件重命名
  • file命令 查看文件详细信息

alt text

逻辑卷管理(好处:可以动态扩充)

  • 挂载: 把一个硬件设备和对应的目录关联起来
  • 硬盘变成可用存储空间要经过三步
    1. 分区
    2. 格式化
    3. 挂载
  • 逻辑卷管理软件工具:用多块硬盘上的分区虚拟成一个分区

逻辑卷管理软件工具,如逻辑卷管理器(LVM),允许用户将多块硬盘上的分区虚拟成一个分区,从而更有效地管理和使用硬盘资源。以下是使用LVM将多块硬盘上的分区虚拟成一个分区的基本步骤:

  1. 准备磁盘并标记物理卷:首先,你需要准备要进行逻辑卷管理的硬盘分区,并将这些分区标记为物理卷。物理卷是LVM管理的基本单位,可以是一个硬盘分区或者整个硬盘。
  2. 创建卷组:接下来,你需要创建一个卷组。卷组是由一个或多个物理卷组成的集合,它提供了逻辑卷管理的框架。
  3. 创建逻辑卷:在卷组中,你可以创建逻辑卷。逻辑卷是最终提供给操作系统使用的虚拟分区,它们可以跨多个物理卷或卷组。
  4. 格式化逻辑卷:创建逻辑卷后,你需要对其进行格式化,以便操作系统可以识别和使用。
  5. 挂载逻辑卷:最后,将逻辑卷挂载到操作系统的文件系统中,这样你就可以像访问其他分区一样访问逻辑卷上的数据。

此外,LVM还提供了许多其他功能,如逻辑卷的扩容、缩小、迁移和备份等,这些都可以在不中断数据访问的情况下进行。

第五章 文件安全

1.Protection based on AccessPermission

  • Types of users
    1. User/owner(文件拥有者)
    2. groups(同组用户)
    3. others(其他人)
  • Types of Access Permissions
    • Read, write, and execute(执行)
  • Access Permissions for Directories
    • Read: list the files
    • Write: create or remove directories and files

所以,对于每个文件,三个身份和三种权限组成九个权限位
alt text

  • 目录也有权限
    1. 目录的x执行权限:所有要进入到目录里的操作,包括(cd命令,touch命令,删除目录里的文件)
      目录文件只包含两个字段:一个包含目录下的所有文件名,另一个是索引节点号
      # 想要查看目录权限要加参数d
      ls -ld dira
      alt text

2. 改变权限或者所属组的命令

  • chgrp 组1 文件或目录路径: 改变组
  • chown newowner 文件: 改变所属用户
  • id user: Find the user id
  • group user: Find the user group

3.Changing File Access Privileges

chmod 权限语句 文件或目录对象命令:改变文件权限位
权限语句由三部分组成:

  • 例如 u-x
    第一部分是要修改谁的权限:a表示所有,u表示owner,g表示group,o表示others,ugo也表示所有
    alt text
  • 也可以用八进制数来代替权限语句,能够一次性设定所有文件权限位
    如要给一个文件设置rwxr-x—权限,每三位(111 101 000)转化成八进制数就是750,所以命令就是
    chmod 750 hello
    alt text

Default file access privileges 默认文件权限

  • 通过掩码值umask来设定默认访问权限
  • umask命令
    # 显示掩码值
    input: umask
    ouput: 0022
    # 设置掩码值
    input: umask 077
  • umask怎么决定文件访问权限
    1. The access permission value onexecutable file or directory iscomputed by(针对目录或者可执行文件): file access permission=777-mask
    2. The access permission value on otherfile is computed by(对于普通文件):file access permission = 666-mask
      alt text

alt text

三个特殊权限位

  • SUID
    alt text
    If this bit is set for a file containing an executable program for a command,the command takes on the privileges of the owner of the file when it executes.

    <!-- 设置suid位 -->
    sudo chmod u+s /usr/bin/passwd
  • SGID
    执行命令时,会把身份临时切换成同组成员(groups)

    <!-- 设置sgid位 -->
    sudo chmod g+s /usr/bin/passwd
  • STICKY黏着位(对于目录)
    设置黏着位意味着在对于一些共享目录(所有人都能创建文件)时,不同用户只能删除自己的文件

    <!-- 设置/取消设置黏着位 -->
    chmod +t /tmp
    chmod -t /tmp

第六章 File Processing

view complete file

显示文件全部内容

  • cat命令
    1. 参数n: 显示行号
    2. cat命令不加文件时,默认从标准输入中读取数据(等待键盘输入)
  • nl命令

分页查看文本文件

  • moreless命令
    1. 输入/s22会在文件中查找s22相对应文字
    2. 按Q退出
  • od命令 显示不可见字符
    alt text
    1. 参数-c表示以字符形式显示,例如空行会变成\n
    2. -h以十六进制显示

Viewing the head or tail of a file

  • head默认显示文件前五行
    1. 参数-n5表示显示文件开头前5行,数字可任意改,n可以不写
  • tail默认显示文件最后五行
    1. 参数-5表示显示文件开头前5行,数字可任意改
    2. 参数-f实时刷新 ctrl+c退出
  • wc 查看文件大小的命令(Determining file size)
    alt text
    1. 参数-l表示只显示行数
    2. -w只显示单词数
    3. -c表示只显示字符数
      wc -l /tmp/hello.cpp
      
      # 查看系统里有多少个用户
      wc -l /etc/passwd

比较两个文本文件

  • diff命令
    diff [options] [file1] [file2]

查找文件命令

普通文件

  • find命令
  • find 后面跟一个目录,表示在此目录中寻找,
  • expression:
    • -name pattern
    • -size +/-N 加号/减号表示 大小/小于 大于N的文件
    • -exec or -ok CMD 表示对find找到的结果每一个都执行一条命令CMD,exec和ok的区别在于ok每一次都要询问
      find /tmp  \( -name "*.txt" -a -size 0 \) -exec ls -l {} \; 2>/dev/null
      alt text
    • \( 组合 \) 使用括号来把多个条件组合在一起
      find /tmp  \( -name "*.txt" -a -size 0 \) 2>/dev/null
  1. 通配符的使用,通配符是一种特殊语句,主要有星号(*)和问号(?),用来模糊搜索文件
通配符 说明
* 代表0个或多个任意字符
? 代表任意一个字符

alt text
通配符不仅能结合 find 命令使用,还可以结合其它命令使用,比如: ls、mv、cp 等,这里需要注意只有find命令使用通配符需要加上引号。
alt text

命令文件

  • whereis 查找命令文件,会找出可执行文件的位置和帮助文档的位置
  • which 查找命令文件

对文件内容操作

去掉重复内容

  • uniq命令
    uniq命令只能除去中间没有其他行的重复行
  • 参数
    1. -c 统计连续重复出现的次数
    2. -d 显示重复的行
    3. uniq 源文件名a 文件名b表示把去重结果输出到文件中
      alt text

sort对文件内容进行排序

  • sort 排序
  • 参数
    • -k3 表示按第3个字段来排序
    • -n 默认按字符串来排,看编码,想要把字段当作数字来排序,加参数-n
    • -r 逆序排列
      sort -nrk4 /tmp/student_record
    • -t"分隔符" sort命令默认按空格分割字段,参数-t可以切换分隔符
      sort -nrk4 -t":" /tmp/student_record
    • -f 表示不区分大小写

cut命令剪切文件内容

  • cut -blist [-n][file-list]
  • cut -clist [file-list]
  • cut -flist [-dchar][-s][file-list]
  • 参数
    • -f参数1,参数2... 表示切割出第(参数)列字段
    • -d"分隔符" cut命令默认使用TAB制表符来作为分隔符,加-d参数可以修改分隔符
      cut -d":" -f2 /etc/passwd | head -3
    • -c参数1,参数2 表示切割出第(参数)列字符

拼接命令

  • paste 文件a 文件b命令 简单的把文件a和b按行拼接,第一行拼第一行…
  • join命令 实现按关键字拼接

查找文件内容

grep命令: 查找或搜索文件内容

  • grep 查找内容 文件名
  1. grep命令及选项的使用
选项 说明
-v 显示不包含匹配文本的所有行
-n 显示匹配行号
-i 忽略大小写
-l 只列出包含内容的文件列表,不关心内容
-c 只计数,对每个文件进行计数操作
  1. grep命令结合正则表达式使用
选项 说明
^ (行)以指定字符开头
$ (行)以指定字符结尾
. 匹配任意一个非换行符的字符
* 代表前一个字符可以出现一次或者任意多次
[] 代表集合的意思,eg. [Hh]ello
[^] 方括号里上箭头表示取反,匹配除了括号里的其他所有字符
\< 表示一个单词的开头,或者可以用grep的参数-w
\> 表示一个单词的结束
\{a,b\} 表示前一个字符出现a到b次,a为0表示至多出现b次,b不填表示至少出现a次以上
| 表示或,要用grep的升级版egrep
? 出现一次或者不出现
+ 表示前一个字符至少出现一次
() Grouping
\ 转义斜杠

alt text

alt text

  • 通过 grep 命令可以在指定文件中查找指定搜索内容,这里扩展一下,grep 还可以查找管道中的内容
    比如:ls/| grep ‘lib’
  • 在使用 grep 命令时,还可以省略查找内容的引号比如:ls/| grep lib , grep hello 1.txt

对文本批量修改

  • sed流处理
    对文件流水线处理,读一行处理一行sed ‘[address]command’ filename(s)
    • 根据行号选中行处理
      1. d 删除操作对1到3行进行删除操作sed '1,3d' filename
    • 根据正则表达式选中包含匹配项的行处理,sed '/正则表达式/' file
      1. s替换操作 对计算机系CS的学生替换成EECSsed 's/<CS/EECS/' file
      2. s///g全局替换操作 把数字替换成*屏蔽成绩sed 's/[0-9]/*/g' file
      3. p打印前三行sed '1,3p' file
      4. a在匹配行后加一行内容sed '/\<CS/a =====High Salary=====' file
      5. i在匹配后前加一行内容sed '/\<CS/i =====High Salary=====' file
      6. !否定
    • -n参数 表示只执行操作,不默认输出缓冲区
    • -i参数(或 --in-place)是一个非常重要的选项,它允许你直接修改输入文件,而不是仅仅将结果发送到标准输出。
    • sed格式
      sed '/正则表达式/s#原文#替换文本#'
      sed '/^[[:space:]]*$/d' output_sort.txt严格删除所有空行[[:space:]]这是sed中的一个字符类,表示空白字符。它匹配任何空白字符,如空格、制表符、换行符、回车符等
  • awk命令
    • awk命令里的正则表达式格式为awk '/re/' filename
      alt text
    • 在awk命令中,美元符号表示字段的意思$
      alt text
    • F参数指定分割符
      • 默认情况下,awk 使用空格或制表符作为字段分隔符
      • 如果你有一个以逗号, 分隔的文件,并希望使用 awk 来处理它,你可以这样指定分隔符:awk -F, '{print $1, $2}' filename
      • 如果有都多个分隔符,用[]括起来,例如-F'[ab]'
    • $1~字段匹配
      • awk '$2~/^D/{print $1,$2}'表示对字段2进行匹配,如果是以D开头的,就打印字段1,字段2
      • awk '$2!~/^D/{print $1,$2}'表示对字段2不进行匹配以D开头的,就打印字段1,字段2
    • 过滤条件
      • awk -F: '$3 >= 1000 && $3 <= 2000 {print $1, $3}' /etc/passwd要显示用户ID在1000到2000之间的用户名,您可以使用awk来过滤/etc/passwd文件。$3 >= 1000 && $3 <= 2000 是一个条件,它检查第三个字段(用户ID)是否在1000到2000之间(包括1000和2000)。
awk '/\<CS/{print $1,$2}' /tmp/student_record

awk '$2~/^D/{print $1,$2}'
# $2~/^D/:这是一个条件,它检查第二列(由字段分隔符分隔,默认为空格或制表符)是否以字母 "D" 开头,~ 是 awk 中用于模式匹配的操作符

awk -F: '$2 !~ /916/ {print $1}' /tmp/donors
#这条命令将根据冒号分隔符(:)来解析文件中的内容,并打印出第二个字段中不包含916区号的人的名字。

awk '{print NR ": " $0}' /tmp/donors
# 请将"文件名"替换为您要处理的文件名。这条命令将打印出每条记录的行号(记录号)后面跟着记录的内容。记录号由NR内置变量提供,表示当前处理的记录的序号。

 awk  -F'[: ]' '$7 <= 85 {print $1,$2,$4}' /tmp/donors
  • tr命令(映射替换)
    • tr 被映射域 映射域(可用区间表示)
      alt text

文件解压缩

  • gzip 只能保留压缩或者未压缩二选一

    • 压缩命令格式gzip filename
    • 解压格式gzip -d ziped_filename
  • bzip2 .bz2后缀

    • 压缩命令格式bzip2 filename
    • 解压格式bzip2 -d ziped_filename
  • Linux下文本文件和Windows下文本文件不同:
    Windows下文本文件会每行包含一个\r
    alt text

第七章 vim的使用

7.1 vim的三种模式

  1. 命令模式
    命令模式下按i进入编辑模式(插入模式)
  2. 插入模式
    在此模式下编辑文件内容,按esc退出,进入末行模式
  3. 末行模式
    在此模式下输入:q表示直接退出,:q!强制退出
    w表示保存,wq保存且退出

7.2 vim的常用命令

快捷键 功能说明
yy 复制当前行
p 粘贴
dd 删除当前行
v 普通视图模式
u 撤销
ctrl+r 重做,反撤销
g 跳到文件头
G 跳到文件尾
数字+G 跳转到指定行
/关键词 向下查找关键词
:/搜索的内容 搜索指定内容
?关键词 向上查找关键词
n 查找下一个
N 查找上一个
:%s/原字符串/新字符串/g 全局替换
:开始行数,结束行数s/原字符串/新字符串/g 局部替换
快捷键 功能
. 重复上一次命令操作
> 跳至基地
shift+6 切换成蓝色主题的外观
shift+4 回到浅灰色主题
ctrl+f 回到黑暗主题
ctrl+b 上传一屏

第八章 File Share

硬链接

  • ln 源文件名 链接文件名
    修改链接文件的内容,原文件也会改变

  • 不能跨文件系统(设备)创建链接,也不能对目录创建硬链接

  • 创建硬链接文件,链接文件与原文件分配同样的索引节点号(文件系统)

软链接

  • ln -s 源文件名 链接文件名
    分配不同于源文件的索引节点,当访问链接文件时,先去根据索引节点号找到链接文件所在硬盘位置,访问链接文件内容(源文件路径信息等)找到源文件,在根据源文件的文件名和索引节点号找到源文件索引节点,从而找到源文件路径,对源文件进行访问要访问两次硬盘

第九章 重定向和管道

1.文件描述符表

文件描述符表(File Descriptor Table)是操作系统中用于管理打开的文件、套接字、管道等文件类型对象的一种数据结构。在Unix和类Unix系统(如Linux)中,每一个进程都有一个与之关联的文件描述符表。

  1. 文件描述符:这是一个非负整数,用于在进程中唯一地标识一个打开的文件或套接字。例如,当你使用open()函数打开一个文件时,系统会返回一个文件描述符。
  2. 文件描述符表:这个表是一个数组,其索引是文件描述符,数组的元素是指向打开文件的指针(在更底层,这通常是一个指向文件表项的指针)。
  3. 文件表:每个打开的文件在内核中都有一个对应的文件表项(也称为v-node或inode)。这个表项包含了文件的详细信息,如文件的大小、访问权限、偏移量等。
  4. 关系:文件描述符表是进程级别的,而文件表是系统级别的。一个文件表项可以被多个进程共享(通过不同的文件描述符),但每个进程都有自己的文件描述符表。
  5. 操作:当进程执行如read()write()等系统调用时,它会使用文件描述符作为参数。系统会根据文件描述符在文件描述符表中查找对应的文件表项,并执行相应的操作。
  6. 关闭文件:当进程使用close()系统调用关闭一个文件时,它实际上是在文件描述符表中删除对应的条目,而不是删除文件表项。只有当所有引用该文件的进程都关闭了该文件时,文件表项才会被释放。

alt text

  • 有三个特殊的文件描述符,是系统自动打开的
    1. 标准输入描述符stdin 0
    2. 标准输出描述符stdout 1
    3. 标准错误输出描述符stderr 2

2.重定向

2.1标准输出重定向

  • 格式: command < input_file
  • 例子 把/tmp/studnet_record 里的所有小写字母变成大写
    alt text

2.2标准输出重定向

重定向也称为输出重定向,把在终端执行命令的结果保存到目标文件。

  • 格式: command > output_file
命令 说明
> 如果文件存在,会覆盖原有文件内容,相当于文件操作中的’w’模式
>> 如果文件存在,会追加写入文件末尾,相当于文件操作中的’a’模式

alt text

  • 可以使用输出重定向合并文件
    1. 按列合并 cat filea fileb > newfile
    2. 按行合并 paste filea fileb > newfile

2.2标准错误输出重定向

  • 格式: 2 > output_file
    2表示标准错误输出
  • 使用pipe出现错误输出时,2>/dev/null放在出现错误命令的后面

alt text

  • /dev/null 空设备,一般不关心的错误输出就丢到空设备文件中

多种重定向结合

find /tmp -name "*.cpp" 2>/dev/null >result 
# 即丢弃结果也保存正常输出文件

find /tmp -name "*.cpp" >&result 
# 把标准输出和标准错误输出都保存到文件result中
  • Redirecting stdin and stdout in OneCommand
    Command out-file

  • Redirecting stdout and stderr in OneCommand
    Command >out-file 2>err-file
    Command >out-file 2>&1
    Command >& out-file

进程间通信的方式

  • 匿名管道:是一种半双工的通信方式,只能在有亲缘关系的进程之间使用。管道可以实现在一个进程中生成输出,而在另一个进程中读取该输出
  • 命名管道:是一种有名的FIFO文件,在文件系统中以文件形式存在,可以在不相关的进程之间进行通信
  • 消息队列:是一种进程间通信的方式,一方发送消息到队列,而另一方则从队列中接收消息
  • 信号量:用于进程间的同步与互斥,可以实现进程对临界资源的访问控制
  • 信号:是一种异步的通知机制,用于通知进程发生了某种事件,比如接收到了某种信号或者错误
  • 共享内存:是一种高效的进程间通信方式,多个进程可以共享同一块内存区域,以实现数据的共享
  • socket:是一种网络编程的通信方式,通过网络套接字实现不同主机的进程间通信

KISS原则

Linux设计时,遵循KISS(Keep It Simple Stupid)原则

(匿名)管道命令

管道|相当于一个容器,可以把命令执行的结果存储到里面,在释放出来

tree /bin/ | more
# 统计当前登陆主机人数
who | wc -l 

du /tmp/* 2> /dev/null | sort -n -k1 | tail -3| awk '{print$2}'
  • tee命令
    接收上一条命令的输出,拷贝保存到一个文件中,并把输出传入下一个管道命令的输入
    alt text

命名管道FIFOs

  • FIFOs(命名管道)可用于系统上两个进程之间的通信。它们允许两个进程在系统上独立执行时进行通信,而不像普通管道那样只能在同一个进程内进行通信。

  • FIFOs(命名管道)是在磁盘上创建的,并且有一个类似文件名的名称。这意味着像操作文件一样,必须先创建并打开 FIFOs(命名管道)才能用于进程间通信。

  • 可以在独立执行的程序之间使用 FIFOs(命名管道),这使得它成为进程间跨应用程序通信的一种有效方式。

FIFOs的使用

  • mkfifo [option] file-list创建命名管道命令
    • -m 八进制权限数 参数用来设定管道文件的权限
      alt text

第十章 进程Processing

  • 进程就是程序的一次运行
  • A process is a program in execution
  • A process is created every time you run an external command and is removed after the command finishes its execution

进程调度方式

  • 先来先服务(FCFS)调度算法
  • 短作业优先(SJF)调度算法
  • 优先级调度算法
  • 多优先级队列
  • 高响应比优先调度算法
  • 时间片轮转调度算法
  • 多级反馈队列调度算法

Linux下的进程

Linux系统采用多级优先队列的方式

1. Processor Scheduler(处理器调度器)

  • 定义

    • 处理器调度器是操作系统中负责实现CPU调度算法的代码部分。
  • 功能

    • 确定下一个要运行哪个进程。
    • 考虑多种因素,如进程优先级、等待时间、I/O需求等。
  • 目标

    • 确保系统资源的公平分配。
    • 提高系统的吞吐量和响应速度。

2. Dispatcher(调度器)

  • 定义

    • 调度器是操作系统中负责将CPU控制权从当前进程转移到新调度进程的组件。
  • 功能

    • 停止当前进程的执行。
    • 保存当前进程的上下文(如程序计数器、寄存器状态等)。
    • 加载新进程的上下文。
    • 将新进程置于就绪状态,以便其可以开始执行。
  • 要求

    • 上下文切换过程需要快速且准确,以确保系统的稳定性和性能。

3. 进程优先级值计算

  • 影响因素

    • Nice值:一个介于-20(最高优先级)和19(最低优先级)之间的整数。
    • CPU使用情况:进程占用的CPU资源量。
  • 调整方式

    • 用户可以使用nice命令或renice命令来调整进程的Nice值。
    • 操作系统根据进程的CPU使用情况动态地调整其优先级值。
  • 目的

    • 确保所有进程能够公平地获得CPU资源。
    • 防止某个进程长时间占用过多CPU资源,影响其他进程的执行。
  • 怎么改变Nice_Value值

    • nice -n nice_value命令

进程相关命令

  • ps命令
    • -l参数: 显示详细信息
    • -e选项: 显示系统中所有进程

alt text

# 修改进程优先级(普通用户只能降低优先级--->给nice_value赋正值)
nice -n -20 ps -l 

alt text

Shell命令可以是内部的(内建的)或外部的。

  • 内部(内建)命令:这些命令的代码是shell进程的一部分。内部命令直接由shell解释和执行,不需要创建新的进程。您列举的内部命令包括bg(将后台暂停的任务继续执行)、cd(更改目录)、continue(在循环中跳过当前迭代的剩余部分)、echo(在终端显示文本或将文本写入文件)、exec(替换当前shell进程为新的进程)。

  • 外部命令:这些命令的代码存储在文件中,文件内容可以是二进制代码或shell脚本。外部命令需要shell创建一个新的子进程来执行。您列举的外部命令包括grep(搜索文件中的文本模式)、more(分页查看文件内容)、cat(连接并打印文件内容)、mkdir(创建目录)、rmdir(删除空目录)和ls(列出目录内容)。

在UNIX系统中,一个进程可以通过使用fork系统调用来创建另一个进程。这个系统调用会创建一个与原始进程完全相同的内存映射。

  • 父进程:执行fork系统调用的原始进程被称为父进程。
  • 子进程:通过fork系统调用创建的新进程被称为子进程。

可以使用type命令来查看是内部命令还是外部命令

  • type命令

alt text

shell环境下执行命令

  • 命令执行

在Linux下,Bash执行外部命令的过程涉及多个步骤。以下是该过程的大致描述:

  1. 命令输入

    • 用户在Bash shell中输入一个命令,例如ls
  2. 词法分析

    • Bash首先对输入的命令进行词法分析,将命令分解为单词或标记(tokens),例如命令名、选项、参数等。
  3. 路径搜索

    • Bash需要确定输入的是内部命令(如cdecho等Bash内置的命令)还是外部命令。
    • 对于外部命令,Bash会在环境变量PATH指定的目录中搜索该命令的可执行文件。PATH环境变量包含了一系列用冒号分隔的目录路径,Bash会按照这些路径的顺序进行搜索。
  4. 创建子进程

    • 一旦找到可执行文件,Bash会使用fork()系统调用来创建一个新的子进程。这个子进程是当前Bash进程的副本,它将用于执行外部命令。
  5. 执行外部命令

    • 在子进程中,Bash使用exec()系统调用来替换当前进程的映像为外部命令的可执行文件映像,从而开始执行该命令。
    • exec()调用会导致子进程完全变成另一个程序(即外部命令),执行完毕后不会返回到原Bash进程,除非遇到错误或外部命令执行完成。
  6. 等待命令完成

    • 父进程(即原始的Bash进程)会等待子进程(执行外部命令的进程)完成。这通常是通过wait()系统调用来实现的,它允许父进程挂起执行,直到子进程结束。
  7. 处理命令输出

    • 外部命令执行过程中可能会产生标准输出(stdout)和标准错误输出(stderr)。这些输出通常会被打印到终端上,除非用户通过重定向(>>>2>等)或管道(|)改变了输出的方向。
  8. 命令结束

    • 当外部命令执行完毕后,控制权会返回到Bash进程。如果命令成功执行,Bash通常会返回一个成功的退出状态(通常是0);如果命令执行失败,则会返回一个非零的退出状态。
  • 脚本文件执行
    alt text

  • top命令
    动态实时的显示系统中进程变化
    alt text

    • d: 按D键切换刷新时间
    • h: 显示可选项
    • q: 退出
  • pstree命令
    用树的方式显示进程之间的关系

对进程进行控制

前台进程的概念:
前台进程是与用户直接交互的进程。以下是对前台进程的详细解释:

  1. 定义:前台进程指的是那些当前正在与用户进行直接交互的进程。这类进程通常位于屏幕的最前端,接收用户的输入并显示输出结果。
  2. 特性
    • 交互性:前台进程能够直接响应用户的操作,如点击、输入等。
    • 阻塞性:当一个前台进程正在运行时,它通常会阻塞用户终端,意味着用户无法进行其他操作,直到该进程完成。
    • 单一性:在任何时刻,通常只有一个进程(或进程组)可以在前台运行。
  3. 与后台进程的区别:与前台进程不同,后台进程在后台运行,不会阻塞用户终端,允许用户在同一终端启动新的前台或后台进程。后台进虽>然可以向终端发送输出,但通常不能从终端接收输入。
  4. 操作系统中的表现:在Unix-like操作系统中,用户可以通过特定的命令在前台和后台之间切换进程。
  5. 实例:以Oracle数据库为例,当用户运行一个应用进程时,系统会为该应用建立一个服务进程,这个服务进程可以被视为前台进程,用于处理连接到数据库实例的用户进程的请求。
  6. 其他系统中的应用:在Android系统中,前台进程也是极其重要的,它代表当前正在与用户交互的应用程序。这类进程在系统资源分配中通常享有优先级,以确保流畅的用户体验。
    总的来说,前台进程是计算机系统中直接与用户进行交互的进程,它具有高度的交互性和实时性要求。在不同的操作系统和应用场景中,前台进程的具体表现和管理方式可能会有所不同。
  • 执行命令时指定其为后台进程
    /tmp/loop &
    # 此时后台进程loop不断输出数字,按CTRL+C无效,因为是后台进程

jobs命令是Linux系统中的一个内置命令,它用于显示当前shell中正在运行或已经挂起的任务列表。

  1. 基本功能

    • 显示当前shell中所有正在运行或挂起的任务。
    • 通过任务编号、状态、进程编号和任务名称来展示任务信息。
  2. 常用操作

    • 使用jobs命令直接查看任务列表。
    • fg [%n]用于将后台执行的进程调到前台。
    • bg [%n]用于将后台暂停的进程继续运行。
    • Ctrl+Z可以将当前正在运行的前台进程暂停,并将其移动到后台。
  3. 任务状态

    • 任务的状态可以是停止状态(S)、运行状态(R)或已完成状态(D)。
  4. 命令参数

    • -l选项:显示详细的任务信息,包括进程ID(PID)、状态、作业编号、命令和进程组ID。
    • -n选项:只显示上次显示过的已经停止的或已经退出的作业。
    • -p选项:仅显示选定作业的进程组的进程ID。
    • -r选项:仅显示正在运行的作业。
    • -s选项:仅显示停止的作业。
  5. 使用场景

    • 当你在终端中运行了多个命令或脚本,并且想要查看或管理这些任务时,可以使用jobs命令。
    • 特别是当你需要切换任务的执行前后台,或者查看哪些任务正在运行、哪些已经暂停时,jobs命令会非常有用。

alt text

  • fg
    把后台进程变成前台进程
    • 默认把带+的进程调到前台
    • %1: 指定把哪个(进程id)进程变成前台进程

alt text

  • bg
    挂起前台任务使用CTRL+Zbg命令来把后台挂起的任务唤醒

命令的顺序和并行执行(Sequential and Parallel Execution of Commands)

  • 串行: cmd1 ; cmd2
  • 并行: cmd1 & cmd2

alt text

如何终止进程

  • 终止前台进程
    • CTRL+C
  • 终止后台进程
    • kill命令
      alt text

kill命令

kill 命令在 Unix 和 Linux 系统中用于向进程发送信号。其基本语法是:

kill [-信号编号] 进程ID
  • 信号编号 是一个可选参数,用于指定要发送给进程的信号。如果省略,则默认信号是 SIGTERM(软件终止信号,通常为编号 15)。
  • 进程ID 是要发送信号的进程的 ID。

kill -l 命令会列出所有可用的信号。

目的:将 信号编号 指定的信号发送给 proc-list 中指定的进程ID(PID)对应的进程。作业ID通常用于 shell 作业控制,必须以 % 符号开头。

kill -l 命令返回所有信号及其名称的列表。在某些系统上,信号编号可能不会与名称一起显示。

常用的信号编号包括:

  • HUP(挂起,通常为信号编号 1)
  • INT(中断,通常由按 Ctrl+C 发送,信号编号 2)
  • QUIT(退出,通常由按 Ctrl+\ 发送,信号编号 3)
  • KILL(强制杀死,信号编号 9)
  • TERM(软件信号,如果没有指定则为默认信号,通常为编号 15)

请注意,向进程发送某些信号,尤其是 KILL(信号 9),应该谨慎进行,因为它可以在不给进程机会适当清理资源的情况下突然终止进程。

如果你想向多个进程发送信号,可以通过空格分隔指定多个 PID,如下所示:

kill -SIGTERM 1234 5678 9012

此命令将 SIGTERM 信号发送给 PID 为 1234、5678 和 9012 的进程。

信号的处理

命令和进程的异常终止(续)

kill命令的主要目的是向进程发送信号(软件中断)。进程在接收到信号时可以采取以下三种行动之一:

  1. 接受由UNIX内核决定的默认操作。
  2. 忽略该信号。
  3. 拦截信号并执行用户定义的操作。

由进程内部事件引起的信号被称为内部信号或陷阱(trap)。由进程外部事件引起的信号被称为外部信号。

程序怎么通过代码处理信号

让我们详细探讨一下这三个策略:使用 sigaction 设置信号处理程序、使用信号掩码以及使用守护进程。

1. 使用 sigaction 设置信号处理程序

sigaction 是一个系统调用,它允许你为特定的信号设置一个处理程序。虽然 SIGKILL 不能被捕获或处理,但你可以为其他信号设置处理程序,以便在进程被 SIGKILL 之前,有机会执行一些清理工作。

#include <signal.h>
#include <iostream>

void handle_sigint(int sig) {
    std::cout << "Caught SIGINT, cleaning up..." << std::endl;
    // 执行清理操作
    // ...
    std::cout << "Cleanup done, exiting..." << std::endl;
    exit(0);
}

int main() {
    struct sigaction sa;
    sa.sa_handler = handle_sigint; // 设置信号处理程序
    sa.sa_flags = 0;
    sigemptyset(&sa.sa_mask); // 清空信号集

    // 为 SIGINT 设置信号处理程序
    if (sigaction(SIGINT, &sa, nullptr) == -1) {
        perror("sigaction");
        return 1;
    }

    std::cout << "Running, press Ctrl+C to interrupt..." << std::endl;
    while (true) {
        // 主循环
    }

    return 0;
}

在这个例子中,我们为 SIGINT(通常由 Ctrl+C 产生)设置了一个信号处理程序。当 SIGINT 被捕获时,程序会执行清理操作并优雅地退出。

2. 使用信号掩码

信号掩码是一个进程用来阻塞(忽略)特定信号的机制。通过设置信号掩码,你可以暂时阻止某些信号被处理,直到你准备好处理它们。这可以用来确保在执行关键操作时不会被打断。

#include <signal.h>
#include <unistd.h>
#include <iostream>

int main() {
    sigset_t mask;
    sigemptyset(&mask); // 清空信号集
    sigaddset(&mask, SIGINT); // 将 SIGINT 添加到信号集中

    // 阻塞 SIGINT
    sigprocmask(SIG_BLOCK, &mask, nullptr);

    std::cout << "SIGINT is blocked, perform critical operation..." << std::endl;
    // 执行关键操作
    // ...

    // 解除对 SIGINT 的阻塞
    sigprocmask(SIG_UNBLOCK, &mask, nullptr);
    std::cout << "Critical operation completed, SIGINT is unblocked." << std::endl;

    while (true) {
        // 主循环
    }

    return 0;
}

在这个例子中,我们使用 sigprocmask 来阻塞 SIGINT,这样在执行关键操作时,SIGINT 不会被处理。操作完成后,我们解除阻塞,允许 SIGINT 被正常处理。

3. 使用守护进程

守护进程(Daemon)是一种在后台运行的进程,通常用于执行长期任务或等待某些事件的发生。守护进程可以监控你的应用程序,并在应用程序被 SIGKILL 或其他信号终止后自动重启它。

创建守护进程通常涉及以下几个步骤:

  • 从终端会话中分离。
  • 关闭标准文件描述符(stdin、stdout、stderr)。
  • 改变当前工作目录。
  • 设置适当的信号处理程序。
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>

void daemonize() {
    pid_t pid;

    // 创建子进程
    pid = fork();
    if (pid < 0) {
        perror("fork");
        exit(1);
    } else if (pid > 0) {
        // 父进程退出
        exit(0);
    }

    // 从终端会话中分离
    setsid();

    // 关闭标准文件描述符
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);

    // 改变当前工作目录
    chdir("/");

    // 设置文件权限掩码
    umask(0);

    // 重定向标准文件描述符到 /dev/null
    open("/dev/null", O_RDWR); // stdin
    open("/dev/null", O_RDWR); // stdout
    open("/dev/null", O_RDWR); // stderr
}

int main() {
    daemonize();

    std::cout << "Daemon started..." << std::endl;
    while (true) {
        // 守护进程的主循环
    }

    return 0;
}

在这个例子中,我们定义了一个 daemonize 函数,它执行了创建守护进程所需的步骤。然后在 main 函数中调用 daemonize 来启动守护进程。

第十一章 Shell脚本编程

  1. Shell Script (Shell 程序):

    • 一个Shell脚本是一个包含一系列Shell命令的文本文件。这些命令可以由Shell(如bash, zsh, sh等)解释并执行。
    • Shell脚本通常用于自动化任务和简化复杂的命令序列。
  2. Shell Variable (Shell 变量):

    • Shell变量是用户或程序员在Shell环境中用于存储和引用数据的机制。
    • 变量可以存储字符串、数字或其他数据类型(取决于Shell)。
    • Shell变量通常用于在脚本中传递和存储数据,以及动态地构建命令。
  3. Program Control Flow Commands (程序控制流命令):

    • 这些命令允许在Shell脚本中执行非顺序的命令,以及重复执行命令块。
    • 常见的控制流命令包括ifforwhileuntilcase等。
    • 这些命令使得脚本能够基于条件执行不同的命令序列,或者重复执行某个命令块,直到满足某个条件为止。

总结:Shell脚本是包含一系列Shell命令的文本文件,用于自动化任务和简化命令序列。Shell变量用于存储和引用数据,而程序控制流命令则允许在脚本中执行非顺序的命令和重复执行命令块。

Shell脚本也是一种编程语言(解释型)
编程语言分为两种

  • 编译型: 如C++这种,通过g++先生成可执行文件
  • 解释型: python或java,读一行命令,解释执行一行

alt text

编写shell脚本

vim first.sh

date
echo "Hello world"

shell脚本有三种执行方式

  1. bash first.sh 指定运行shell来执行脚本
  2. first.sh 直接执行shell脚本
  3. ./first.shsource first.sh
  • 在脚本文件第一行指定shell环境#! /bin/bash表示把/bin/bash程序加载进来,由它来解释执行脚本文件
    alt text

shell变量分类及操作

  • 环境变量(子进程无法修改父进程的环境变量)
    使用env命令直接显示环境变量
  • 用户自定义变量
#! /usr/bin/bash
# 定义变量,不能加空格
nam="Jack"
# 不能使用双引号
echo "Hello, $nam"
  • readonly: 把一个变量变成只读
  • export: 把一个用户定义变量变成环境变量
  • source: 允许脚本修改环境变量值(使用source或者.执行脚本文件时,不会再创建子进程来执行,使用当前shell进程来解释执行脚本)
    #! /usr/bin/bash
    # 设置父(当前)进程环境变量值
    PS1='Study Hard/$'
    # 执行时使用 . first.sh  或者 source first.sh

内置的环境变量

  1. PATH

    • Purpose: 包含目录名称的列表,shell在查找外部命令或程序时会搜索这些目录。
    • Default: 通常包括/usr/bin, /bin, /usr/local/bin等目录。
  2. EDITOR

    • Purpose: 指定默认文本编辑器,如vi, nano, emacs等。
    • Default: 根据系统配置和用户偏好而定。
  3. ENV

    • Purpose: 指定shell启动时要读取的初始化文件。在某些shell中,如ksh,它可能用于指定环境设置文件。
    • Default: 通常不设置,但在某些shell配置中可能使用。
  4. HOME

    • Purpose: 用户的主目录路径。当用户首次登录时,shell通常会切换到这个目录。
    • Default: /home/username(其中username是用户的登录名)。
  5. MAIL

    • Purpose: 用户的系统邮箱文件的路径。当有新邮件时,某些shell会显示通知。
    • Default: /var/spool/mail/username(其中username是用户的登录名)。
  6. MAILCHECK

    • Purpose: 指定shell检查用户邮箱以查找新邮件的频率(以秒为单位)。
    • Default: 通常设置为60秒,但可能因系统而异。
  7. PPID

    • Purpose: 当前进程的父进程ID(PID)。这不是一个通常设置的环境变量,而是可以通过shell命令(如$!$PPID在某些shell中)或程序内部API获取的。
    • Default: 由操作系统在进程创建时设置。
  8. PS1

    • Purpose: 主shell提示符,显示在命令行上。
    • Default: 通常设置为类似$#(对于root用户)的字符,但用户可以通过修改.bashrc.bash_profile等文件来定制它。
  9. PWD

    • Purpose: 当前工作目录的路径。
    • Default: 由shell在每次更改目录时设置。
  10. TERM

    • Purpose: 终端类型。它告诉应用程序(如文本编辑器、终端仿真器等)关于终端的功能和特性。
    • Default: 根据用户使用的终端仿真器而定,如xterm, vt100, linux等。
变量 描述
$0 程序的名称
$1 - $9 命令行参数1到9的值
$@ 所有命令行参数的值
$* 所有命令行参数的值,单一字符串"a b c d"
"$@" 所有命令行参数的值;每个参数都单独引用 "a" "b" "c" "d"
$# 命令行参数的总数
$$ 当前进程的进程ID(PID)
$? 最近命令的退出状态,执行成功返回值0
$! 最近后台进程的PID
Study Hard:$cat first.sh

#!/bin/bash
echo $0
echo $1
echo $2

Study Hard:$first.sh a b c

./first.sh
a
b

Study Hard:$vim first.sh
Study Hard:$cat first.sh

#!/bin/bashecho $1

Study Hard:$first.sh Tom
Tom

shell变量的数据类型

变量声明

语法:

  • declare [选项][名称[=值]]
  • typeset [选项][名称[=值]]

目的:
声明变量并初始化它们,设置它们的属性。在函数内部,会创建新的变量副本。使用+代替-关闭属性。

输出:

  • 没有名称和选项时,在当前shell环境中显示所有shell变量的名称和值。
  • 使用选项时,显示具有特定属性的变量名称和其值。

常用选项/特性:

  • -a:将每个“名称”标记为数组
  • -f:将每个“名称”标记为函数
  • -i:将“名称”标记为整数
  • -r:将“名称”标记为只读(无法通过+x关闭)
  • -x:将“名称”标记为环境变量

shell变量被默认为是字符串类型

Input: a=1
Input: b=2
Input: c=$a+$b
Input: echo $c

Output: 1+2

# 正确输出加法公式
Input: declare -i z
Input: z=a+b
Iuput: echo $z
Output: 3

算数拓展

  1. $(): 这是命令替换(Command Substitution)的语法。当你想要将命令的输出赋值给一个变量,或者将命令的输出作为另一个命令的参数时,你会使用它。$() 的结果会被替换为括号内命令的输出。例如:
files=$(ls)
echo $files

在这个例子中,ls 命令的输出会被赋值给变量 files,然后 echo 命令会打印出这个输出。

另外,$() 是反引号(...)的现代替代方式,两者在功能上是等价的。例如:

files=`ls`
echo $files
  1. $((...)): 这是算术扩展(Arithmetic Expansion)的语法。当你需要在 Bash 脚本中执行算术运算时,你会使用它。$((...)) 的结果会被替换为括号内算术表达式的结果。例如:
a=5
b=10
sum=$((a + b))
echo $sum

在这个例子中,算术表达式 a + b 的结果(即 15)会被赋值给变量 sum,然后 echo 命令会打印出这个结果。

命令替换(把命令结果存储成变量)

  • 变量名=`cmd`
  • cmd1 "$(cmd2)"
    • $(...): 这是一个命令替换的语法,它允许你执行一个命令,并将该命令的输出作为另一个命令的输入。在这个例子中,$(sort "$filename") 会执行 sort "$filename" 命令,该命令会对 $filename 文件的内容进行排序,并输出排序后的结果。这个输出随后被用作 nl 命令的输入。

alt text

变量的输入输出

变量的输入

read 是一个在Linux中常用的命令,用于从标准输入或其他文件描述符中读取输入并赋值给变量。它的基本语法如下:

read [选项] 变量名(列表)

其中:

  • 选项 是可选的,可以用来指定read命令的特定行为。
  • 变量名 是要将输入赋值给的变量。

示例:

echo "Please enter your name:"
read name
echo "Hello, $name. Welcome!"

在这个示例中,当用户输入其名字并按下回车后,read 命令将输入的内容赋值给变量 name,然后 echo 命令使用这个变量进行输出。

常用选项包括:

  • -p :用于显示提示信息而不是使用echo命令来显示。
  • -a :将输入分配到数组而不是普通变量。
  • -n :限制输入的字符数。
  • -s :用于静默模式,输入时不显示在屏幕上。
  • -t :设置等待时间,如果超过指定秒数没有输入,则读取终止。

alt text

变量的输出->echo

echo 命令用于在终端中显示文本或变量的内容。它的基本语法如下:

echo [选项] [字符串或变量]

其中:

  • [选项] 是可选的,可以用来指定特定行为,比如 -e 用于解释转义字符。
  • [字符串或变量] 是要显示的文本或变量的内容。

示例:

echo "Hello, World!"

在这个示例中,echo 命令会在终端中显示 “Hello, World!”。

常用选项包括:

  • -e :允许使用反斜杠转义字符。
  • -n :不在结尾添加换行符。
  • -E :关闭反斜杠转义功能,将反斜杠字符输出为原始字符。
  • >> 文件 :将输出追加到指定文件。
  • -E :关闭 -e 选项开启的转义字符解释功能。

对数值进行处理

因为shell变量被默认为是字符串类型

整数

  • declare
  • expr 表达式
  • let命令
    alt text

小数

  • bc命令: 默认从标准输入读取小数
    a=1.1
    b=2.1
    echo $a + $b | bc
    # 从标准输出中通过管道变成标准输入在交给bc命令
    
    Output: 3.2

Homework

  • Write a script that take a directory as a command-line argument and output the names of all directories within that directory.(1.sh)
    dirname=$1
    ls -l $dirname | awk '/^d/{print $9}'
  • Write a script that read a text filename from the keybard, sort the file and display it with the line number(2.sh)
    read -p "Please input a file name:" filename
    sort $filename | nl

shell脚本的注释

#用来注释一行,类似于python(除第一行外)

编写shell脚本

shell表达式

alt text

  • -eq: 两个整数相等

  • -ge: 大于等于

  • -gt: 大于

  • -le: 小于等于

  • -lt: 小于

  • -a: AND

  • -o: OR

  • !: 非

对表达式求值有两种方式

if test $a -eq $b

if [ $a -eq $b ]
# 要有空格

if结构

#!/bin/bash  
  
num1=10  
num2=20  
  
if [ $num1 -lt $num2 ]  
then  
    echo "$num1 is less than $num2"  
fi


#! /usr/bin/bash
read a
if [ $a -eq 0 ]
then echo 'zero'
elif [ $a -gt 0 ]
then echo "is Postive"
elif [ $a -lt 0 ]
then echo " is Negative"
fi
# if.sh

循环结构

alt text

for循环

alt text

# for循环格式 in可以不写,表示从命令行取参数列表
for variable [in argument-list]
do
   command-list
done
# 实例
for name in tom jack marry
do
   echo "hello $name"
done
  • 题目: 输出命令行参数之和

    #! /bin/bash
    sum=0
    for i 
    do 
       sum=`expr $sum + $i`
    done
    echo $sum
  • 题目: 从1加到100,输出结果

    read n
    sum=0
    for i in $(seq 1 $n)
    do
            sum=`expr $sum + $i`
    done
    echo $sum
  • 题目: 批量的给文件改名

    #!/bin/bash  
      
    read -p "请输入目录(默认为当前目录): " dir  
    if [ -z "$dir" ]; then  
        dir="./"  # 如果目录为空,则默认为当前目录  
    fi  
      
    i=1  
    for filename in "$dir"*.txt; do  # 注意这里使用引号包围变量和模式  
        echo "重命名 $filename$i.txt,y 或 n"  
        read choice  
        if [ "$choice" = "y" ]; then  
            mv "$filename" "$dir$i.txt"  # 确保重命名后的文件路径正确  
            i=$((i+1))  
        fi  
    done
    
while循环
while expression
do
   command-list
done
  • 题目: 输入密码检验
    #/bin/bash
    passwd="007"
    read -p "Please input your password" password
    # 变量要加双引号,防止用户输入出现空格导致异常
    while [ "$password" != "$passwd" ]
    do
            read -p "Please input your password" password
    done
    echo "Congradulations!"
  • while循环中的break和continue
    alt text

case分支语句

# 格式
case test-string in
pattern1)   command-list1
            ;;

pattern2)   command-list2
            ;;
patternN)   command-listN
            ;;

PTA习题

alt text

  • 解答
    read a b other  
    sum=$((a + b))  
    echo $sum
    
    # 解法2
    read a b other  
    sum=`expr $a + $b`  
    echo $sum

alt text

  • 解答
    #!/bin/bash  
    # 提示用户输入密码  
    echo -n "输入密码password: "  
    read -s password  # -s 选项使输入不回显到屏幕上  
    echo  # 打印一个空行以分隔密码输入和后续输出  
      
    # 检查密码是否正确  
    if [ "$password" = "pta12345" ]; then  
        echo "密码正确"  
    else  
        echo "密码错误"  
    fi

alt text

  • 解答
    read n
    if [ $n -eq 0 -o $n -eq 1 ]
    then 
        echo "$n indian boy."
    elif [ $n -gt 1 ]; then 
        echo "$n indian boys."
    fi

alt text

  • 解答
    read n
    if [ $n -lt 0 -o $n -gt 100 ]; then
        echo "输入整数小于0或大于100"
    else
        echo "输入整数在0到100之间(含)"
    fi

alt text

  • 解答
    read a
    # 算数拓展
    if [ $(($a % 2)) -eq 0 ]; then
        echo "$a是偶数"
    else
        echo "$a是奇数"
    fi

alt text

  • 解答
    #!/bin/bash
    
    # 读取用户输入的文件名
    read -p "请输入文件名:" filename
    
    # 判断文件是否为目录
    if [ -d "$filename" ]; then
        echo "Yes"
    else
        echo "No"
    fi

alt text

  • 解答
    sum=0
    
    while true; do
        read num    # 读取用户输入的数字
        if [ $num -eq 0 ]; then
            break   # 如果输入为0,跳出循环
        fi
        sum=$((sum + num))  # 求和
    done
    
    echo $sum   # 输出求和结果

alt text

  • 解答
    chance=3
    while [ $chance -gt 0 ]; do
        read -p "Enter password: " passwd
        if [ "$passwd" = "zust" ]; then
            echo "Success"
            exit
        else
            echo "Password error"
            chance=$((chance - 1))
            continue
        fi
    done
    echo "Login failed"
    exit

期末考试exam程序源代码

#!/bin/bash

paperdir="/tmp"
select_first=True
command_first=True

main() {
        clear

        while true
        do
                cat <<- MENU
                        1) select
                        2) command
                        3) script
                        0) quit
MENU
                read -p "please input your selection:" section
                echo
                case "$section" in
                        1) selection;;
                        2) command ;;
                        3) script ;;
                        0) exit 0 ;;
                        *) echo "input error!" ;;
                esac
        done
}



selection() {
        paper="$paperdir/p1"
        grep "^#" $paper
        select_ans=~/1.ans
        if [ "$select_first" = "True" ]; then
                snum=`grep -c '^[0-9]\{1,2\}\.' $paperdir/p1`
                gen_ans 1 $snum
        fi
        select_first=False
        curr=1;
        while true
        do
                echo
                pid=`sed -n "${curr}p" $select_ans | cut -d'.' -f1`
                sed -n "/^$pid\./,/^$/p" $paper | sed "s/^[0-9]\{1,2\}./$curr./"
                youranswer=`grep "^"$pid"\." $select_ans | cut -f2 -d" "`
                read -p "input your answer($youranswer): " answer
                case $answer in
                0)
                        main
                        ;;
                [1-9]|[1-2][0-9])
                        if [ $curr -ge 1 -a $curr -le $snum ]; then
                                curr="$answer"
                        fi
                        ;;
                [a-d]|[A-D])
                        answer=`echo $answer | tr "[a-z]" "[A-Z]"`
                        sed  -i "s/^$pid\. .*/$pid\. $answer/" $select_ans
                        if [ $curr -lt $snum ]; then
                                curr=`expr $curr + 1`
                        fi
                        ;;
                -)
                        if [ $curr -gt 1 ]; then
                                curr=`expr $curr - 1`
                        fi
                        ;;
                "")
                        if [ $curr -lt $snum ]; then
                                curr=`expr $curr + 1`
                        fi
                        ;;
                *)
                        echo "input the correct problem number or answer!"
                        ;;
                esac
        done
}

command() {
        paper="$paperdir/p2"
        cmd_ans=~/2.ans
        grep "^#" $paper
        if [ "$command_first" = "True" ]; then
                cnum=`grep -c '^[0-9]\{1,2\}\.' $paperdir/p2`
                #gen_ans 2 8 $cnum
                gen_ans 2 $cnum
        fi
        command_first=False
        curr=1;
        while true
        do
                echo
                pid=`sed -n "${curr}p" $cmd_ans | cut -d'.' -f1`
                sed -n "/^$pid\./,/^$/p" $paper | sed "s/^$pid\./$curr./"
                echo -n "ANS: "
                grep "^$pid\." $cmd_ans | sed "s/^$pid\.//"
                echo
                read -rep "input your answer: " answer
                if [ $curr -eq $cnum ]
                then
                   echo '============  Last problem. This is my bottom line. ================'
                fi
                case $answer in
                0)
                        main
                        ;;
                [1-9]|1[0-9])
                        if [ $answer -le $cnum ]; then
                                curr="$answer"
                        fi
                        ;;
                -)
                        if [ $curr -gt 1 ]; then
                                curr=`expr $curr - 1`
                        fi
                        ;;
                "")
                        if [ $curr -lt $cnum ]; then
                                curr=`expr $curr + 1`
                        fi
                        ;;
                *)

                        #answer=${answer//\\/\\\\}
                        answer=${answer//#/\\#}
                        answer=${answer//&/\\&}
                        echo $answer
                        sed  -i "s#^$pid\. .*#$pid\. $answer#" $cmd_ans
                        if [ $curr -lt $cnum ]; then
                                curr=`expr $curr + 1`
                        fi
                        ;;
                esac
        done
}

script() {
        paper="$paperdir/p3"
        grep "^#" $paper
        curr=1;
        scnum=`grep -c '^[0-9]\{1,2\}\.' $paperdir/p3`
        while true
        do
                echo
                sed -n "/^$curr\./,/^$/p" $paper
                read -p "input your selection( n(nano)/v(vim) to edit the file ): " answer
                case $answer in
                0)
                        main
                        ;;
                [1-$scnum])
                        curr="$answer"
                        ;;
                -)
                        if [ $curr -gt 1 ]; then
                                curr=`expr $curr - 1`
                        fi
                        ;;
                n)
                        nano ~/$curr.sh
                        ;;
                v)
                        vim ~/$curr.sh
                        ;;
                "")
                        if [ $curr -lt $scnum ]; then
                                curr=`expr $curr + 1`
                        fi
                        ;;
                *)
                        echo "input the correct number"
                        ;;
                esac
        done
}

makefile() {
        paper="$paperdir/p4"
        cat $paper
        read -p "press 0 to quit or any other key to start edit the makefile...: " choice
        if [ $choice = "0" ]; then
                main
        else
                vi makefile
                main
        fi
}

gen_ans() {
        ans_file=~/$1.ans
        if [ -f "$ans_file" ]; then
                return
                echo -ne  "There is a answer sheet in your home directory.\nDo you want to recreate it? (y/n): "
                read ans
                if [ "$ans" = "Y" -o "$ans" = "y" ]; then
                        shuf -i 1-$2 | sed 's/$/. /'  > $ans_file
                fi
        else
                shuf -i 1-$2 | sed 's/$/. /'  >> $ans_file
        fi
        if [ $# -gt 2 ]; then
                seq `expr $2 + 1` $3 | sed 's/$/. /'  >> $ans_file
        fi

        #seq -f '%g. ' 1 $2  >> $select_ans
}

main