学习bash

shell的简单发展

基本上我们常用的是bash,它的全称是Bourne-Again SHell,而sh的全称则是Bourne shell,但是在系统中的/bin/sh其实是一个软连接,指向不同的shell(在ubuntu18上指向了dash,centos上指向了bash),在脚本中还是比较推荐写#!/bin/sh来指定shell的。

Bash的功能

  1. 可以查看历史
  2. 利用tab键进行补全命令或者文件,如果有插件的支持,还可以补全命令的选项
  3. 可以设置别名alias
  4. 控制jobs,这个比较推荐使用tmux
  5. wildcard的使用 ll /etc/a*

如果要查看一个命令的执行顺序,可以用type -a ls来查看,不仅可以告诉系统查询命令的顺序,还能告诉你指令是shell内置的。

变量

由于bash是非常古老的,所以它对变量的定义也是非常老的。如果我要看一个变量的值,推荐使用如下的命令:

1
echo ${PATH}

而定义的话则更加简单了,直接一个a=test就定义好了,这里的test默认是字符串类型的。有一个重要的规则,就是如果用双引号引起来,则$PATH代表的是这个变量的值,如果是单引号引起来,则代表了这个$PATH字符串。还有一个用法是$(ls)来执行命令,然后用该命令的结果来代替。同样如果要对变量增加一些值,直接加在后面就好了,PATH=${PATH}:/home/test/bin←这里的冒号是PATH里面的分隔符,并没有特殊含义。

如果不想要变量了,可以直接unset a来取消,如果希望变量成为环境变量,则用export a即可。

之前说了默认是字符串类型的,那如果希望是一个数字或者是一个数组呢?可以用declare来宣告某个变量的类型,比如declare -i a=100+200 这样a就会是300,而不是字符串的100+200

环境变量

刚刚说了用export可以让一个普通的变量成为环境变量,那么环境变量的作用是什么呢。

举个例子,直接输入cd会回到用户的家目录,那是什么决定的呢?是由HOME这个环境变量来决定的,所以你只需要修改HOME=/etc,这样你直接一个cd就回到/etc目录下面了。比较重要的环境变量有

  • HOME
  • SHELL
  • HISTSIZE
  • MAIL
  • PATH
  • LANG
  • RANDOM

这几个就是比较重要的环境变量,所有的环境变量可以通过env来进行查看,而所有的变量(包括用户自定义的和环境变量)则可以用set来查看。set显示的比较重要的变量应该就是PS1和PS2这两个提示符了吧:

  • PS1:就是用户平时的提示符。
  • PS2:第一行没输完,等待第二行输入的提示符。

语系问题

一般来说,出现乱码无非就是编码解码的问题,这里大部分都可以通过设置系统的语系来解决。可以通过直接file命令来查看某个文件的编码:

1568255628837

可以看到一个是utf-8的编码,另外一个是ISO-8859编码,而且还说了是换行符CRLF,说明是在windows下编辑的。

如果要设置语系,只需要设置LANG这个变量的值,然后让它成为环境变量即可生效。

变量内容修改

可以直接给变量进行一次新的赋值,当然也可以为变量进行新增,修改和覆盖,但是我看了语法,比较难记,所以感觉还是算了,毕竟可以通过最简单的、最暴力的方法:直接覆盖进行操作嘛~

这里真的用到过的是以前在装zsh插件的时候使用的一个

1
git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions

一开始我是没理解ZSH_CUSTOM后面的那个:-是什么意思的,为什么不是直接下载到目录里呢?其实这一步的目的是,如果ZHS_CUSTOM这个变量已经设置且不为空,那么就下载到这个变量所指示的目录里面去,否则就下载到后面那个目录里面去并且设置ZSH_CUSTOM为后面的那个路径。

变量配置方式 str 没有配置 str 为空字符串 str 已配置非为空字符串
var=${str-expr} var=expr var= var=$str
var=${str:-expr} var=expr var=expr var=$str
var=${str+expr} var= var=expr var=expr
var=${str:+expr} var= var= var=expr
var=${str=expr} str=expr var=expr str 不变 var= str 不变 var=$str
var=${str:=expr} str=expr var=expr str=expr var=expr str 不变 var=$str
var=${str?expr} expr 输出至 stderr var= var=$str
var=${str:?expr} expr 输出至 stderr expr 输出至 stderr var=$str

上表摘录自鸟哥的网站。

同一个用户同时多次登录

不少时候会使用root同时登录一台服务器,那么当所有这些root退出之后,这个历史记录看上去似乎怪怪的,会丢失一部分,这是为什么呢?

每当root登录的时候,会读取之前的~/.bash_history的内容,写入到内存之后,然后他开始自己的工作,每次敲一个命令就会在在内存中增加一条新的记录(如果记录达到上限值还会删掉最老的一条),当root退出的时候才会把这个内存里面的数据写回到~/.bash_history中,所以理论上只有最后退出的那个root的命令才会写入到文件,才会被之后的history看到,因为它把之前的都覆盖掉了。

bash配置文件

这部分是最重要的,理解了这部分就理解了为什么用户刚进入bash就能有一些环境变量,就能理解我应该在哪里设置我的命令别名。

首先简单的把shell是否需要登录分成login shell与nologin shell,只需要查看一下/etc/passwd就能知道哪个用户对应的哪个shell。

login

如果是login的用户,则会去读取以下的两个配置文件:

  1. /etc/profile
  2. ~/.bash_profile ~/.bash_login ~/.profile 这三个文件依顺序读取,只要读取到了一个,就不读取之后的了。

但是你可以在这两个文件中写入一些操作,让shell再去读取别的文件,当然也是OK的。

nologin

这个仅仅会去读取~/.bashrc这个文件而已(rc的意思是run command)。

stty(setting tty)

其实一直好奇,为什么ctrl+d就代表了eof,为什么ctrl+c就能终止程序呢?一切都在stty -a这个命令里。

1568258936164

  • intr=interrupt,会发送一个信号给程序来终止。这是为什么ctrl+c能够终止程序。
  • kill在这里是清除tty内容的意思,所以ctrl+u能够清除所有的内容。
  • eof=end of file,所以在文本输入的时候按下ctrl+d就能够结束。

要设置的话,就可以直接stty eof ^h,这样你就可以用ctrl+h来输入文件的结束符了。

管道符

管道符的作用是,把前一个命令的标准输出,作为标准输入给后面的一个命令。所以要求就是后面的那条命令需要接受标准输入,否则就不能使用。但是有个命令是xargs,可以将这个不可能化为可能。