命令都是在终端会话中输入并执行的,打开终端的时候会出现一个命令提示符,一般形式如下:
username@hostname$ / username@hostname#
$ 表示的是普通用户,# 表示的是 root 用户,root 是 Linux 中权限最高的用户。上述命令也可以简单的配置为 $ 或者 #
一般情况下不建议直接使用 root 用户执行命令,因为很容易对系统造成严重的破坏,更好的方式是使用 sudo 等命令获取临时的 root 权限
shell 脚本通常以 shebang(sharp-bang) 开始:#!/bin/bash
shebang 是一个文本行,其中 #!位于解释器路径之前。/bin/bash 是 Bash 的解释器命令路径。bash 将以 # 符号开头的行视为注释。脚本中只有第一行可以使用 shebang 来定义解释该脚本所使用的解释器。
脚本的两种执行方式:
将脚本名作为命令行参数:(这种方式实际上不需要指定 shebang 了,因为已经指定好了解释器)
授予脚本执行权限,将其变为可执行文件: (使用 shebang 之后的解释器路径来解释脚本)
chmod 755 script.sh
./script.sh
echo 是最基本的终端打印命令,类似的还有 printf 命令(类似于 c 中的 printf() 函数)
登录shell:是需要用户名、密码登录后才能进入的shell(或者通过–login”选项生成的shell)。例如:
用户通过输入用户名/密码(或证书认证)后启动的shell;
通过带有 -l 或者 --login 参数的bash命令启动的shell。
例如,系统启动、远程登录、使用su -切换用户、通过bash --login命令启动bash等。
非登录shell:不需要输入用户名和密码即可打开的Shell。 例如:
默认情况下,直接命令“bash”就是打开一个新的非登录shell
使用su 切换用,注意,这里没有中划线
在Gnome或KDE(桌面环境)中打开一个“终端”(terminal)窗口程序也是一个非登录shell。
两种常见的进入登录式 shell 的方式:
exec -l bash:这种方式会用 login shell 直接替换掉当前的 shell
bash -l:进入 login shell,不是替换,使用 exit 可以退出 login 状态
判断登录式 shell 和非登录式 shell 的方式
echo $0:如果第一个字符是 - 说明是登录 shell
shopt login_shell:如果是 login_shell on 说明是登录 shell
以上两种方式只需满足一个便可
登录式 shell 和非登录式 shell 的区别
最主要的区别是启动 shell 时所执行的 startup 文件不同
主要可以参考一下两篇文章:
最重要的一个点是:当需要对当前的 shell 进行一些设置的时候(e.g.对 bash 进行一些设置、设置一些环境变量、设置一些全局变量),可以根据当前 shell 的状态,知道应该去修改哪个文件
大多数脚本语言不要求在创建变量之前声明其类型。用到什么类型就是什么类型。在变量名前面加上一个美元符号就可以访问到变量的值。shell定义了一些变量,用于保存用到的配置信息,比如可用的打印机、搜索路径等。这些变量叫作环境变量。
变量名由一系列字母、数字和下划线组成,其中不包含空白字符。常用的惯例是在脚本中使用大写字母命名环境变量,使用驼峰命名法或小写字母命名其他变量。
env # 显示所有环境变量
printenv # 同上
cat /proc/$PID/environ # 查看指定进程的环境变量
cat /proc/$PID/environ | tr '\0' '\n' # 以一行一条环境变量显示
# 以名称为依据从运行进程队列中查找进程,并显示查找到的进程id
# -o:仅显示找到的最小(起始)进程号;
# -n:仅显示找到的最大(结束)进程号;
# -l:显示进程名称;
# -P:指定父进程号;
# -g:指定进程组;
# -t:指定开启进程的终端;
# -u:指定进程的有效用户ID。
pgrep(选项)(参数)
可以使用等号操作符为变量赋值:varName=value
- 如果 value 不包含任何空白字符(例如空格),那么就不需要将其放入引号中,否则必须使用单引号或双引号。
- var = value 不同于 var=value 。把 var=value 写成 var = value 是一个常见的错误。两边没有空格的等号是赋值操作符,加上空格的等号表示的是等量关系测试。
# 输出变量的值
echo $var
echo ${var}
# 输出和添加环境变量
echo $PATH
export PATH="$PATH:/home/user/bin"
# 输出字符串长度
var=123456
echo ${#var}
# 获取当前环境使用的 shell
echo $SHELL
echo $0
# 更改 Bash 的提示字符串
cat ~/.bashrc | grep PS1 # 查看定义当前提示字符串的环境变量
slynux@localhost: ~$ PS1="zjl>" # 提示字符串已经改变
- PATH 变量列出了一系列可供shell搜索特定应用程序的目录。$PATH 通常定义在/etc/environment、/etc/profile或~/.bashrc中
- 使用单引号时,变量不会被扩展(expand),仍依照原样显示。这意味着 echo ‘$var’ 会显示 $var 。但如果变量 $var 已经定义过,那么 echo “$var” 会显示出该变量的值;如果没有定义过,则什么都不显示。
- 可以利用 PS1 环境变量来定义主提示字符串。默认的提示字符串是在文件~/.bashrc中的某一行设置的。 【①可以使用特定的转义序列定义特殊颜色的提示字符串(\e[1;31);②某些特殊的字符可以扩展为系统参数(\u: 用户名;\h:主机名;\w:当前的工作目录)】
环境变量通常保存了可用于搜索可执行文件、库文件等的路径列表。
将指定路径添加到指定环境变量开头:
# 最基本的方式
export PATH=/opt/myapp/bin:$PATH
# 通过使用函数实现【可以将如下函数定义在 .bashrc 文件中】
prepend() { [ -d "$2" ] && eval $1=\"$2':'\$$1\" && export $1; }
prepend PATH /opt/myapp/bin
# 改进的函数添加方式
prepend() { [ -d "$2" ] && eval $1=\"$2\$\{$1:+':'\$$1\}\" && export $1 ; }
- 第一个函数工作原理:函数 prepend() 首先确认该函数第二个参数所指定的目录是否存在。如果存在, eval 表达式将第一个参数所指定的变量值设置成第二个参数的值加上 : (路径分隔符),随后再跟上第一个参数的原始值。
- 第一个函数的添加方式如果第一个参数为空的时候,则会在路径的末尾多添加一个路径分隔符 :,解决方法是使用
${parameter:+expression}表达式,当且仅当 parameter 有值且不为空的时候才会使用 expression 表达式
Bash shell 使用 let 、 (( )) 和 [] 执行基本的算术操作。工具 expr 和 bc 可以用来执行高级操作。
基本操作:只用于整数
#!/bin/bash
# 赋值
a=1;
b=2;
# let:只用于整数
let c=a+b; # 等号和加号前后都不能有空格
let a++;
let a+=6; # += 前后也不能有空格
# [] 操作符:只用于整数
c=$[ a + b ] # 等号前后不可以有空格,[] 号里边可以有
c=$[ $a + $b ]
c=$[ $a + 6 ]
# (()) 类似于 [] 操作符:只用于整数
c=$(( a + b )) # 等号前后不可以有空格,(()) 号里边可以有
c=$(( $a + $b ))
c=$(( $a + 6 ))
# expr:只用于整数
re=`expr 3 + 4`
re=`expr $a + 4`
re=$(expr $a + $b)
可适用于浮点数
#!/bin/bash
# bc 可用于浮点数
echo "4 * 0.56" | bc # 2.24
no=54;
result=`echo "$no * 1.5" | bc` # 81.0
# 设置答案精度 scale
echo "scale=2;22/7" | bc # 3.14
# 进行进制转换
no=100
echo "obase=2;$no" | bc # 1100100
no=1100100
echo "obase=10;ibase=2;$no" | bc # 100
# 平方和平方根
echo "sqrt(100)" | bc #Square root
echo "10^10" | bc #Square
文件描述符是与输入和输出流相关联的整数。最广为人知的文件描述符是 stdin 、stdout和 stderr 。我们可以将某个文件描述符的内容重定向到另一个文件描述符中。
在编写脚本的时候会频繁用到标准输入( stdin )、标准输出( stdout )和标准错误( stderr )。脚本可以使用大于号将输出重定向到文件中。命令产生的文本可能是正常输出,也可能是错误信息。默认情况下,正常输出( stdout )和错误信息( stderr )都会显示在屏幕上。
文件描述符是与某个打开的文件或数据流相关联的整数。文件描述符 0 、 1 以及 2 是系统预留的。
如何判断一条命令是否执行成功
当一个命令发生错误并退回时,它会返回一个非0的退出状态;而当命令成功完成后,它会返回为0的退出状态。退出状态可以从特殊变量 $? 中获得(在命令结束之后立刻运行 echo $? ,就可以打印出退出状态)。
# 使用大于号将文本保存到文件
echo "zjl" > zjl.txt
# 将文本追加到文件
echo "sdy" >> zyl.txt
- >:如果目标文件中已经存在,会将文件中的内容进行清空
- >>:不会将原有文件的内容清空,而是追加到末尾
# 将标准错误输出重定向到文件
cmd 2> out.txt
# 将标准错误输出和标准输出重定向到不同的文件
cmd 2>stderr.txt 1>stdout.txt
cmd > output.txt 2>&1
cmd &> output.txt
- cmd:表示执行的命令
- 注意 &> 和 >& 顺序不同
- 如果不想看到或保存错误信息,那么可以将 stderr 的输出重定向到/dev/null。
我们在处理一些命令输出的同时还想将其保存下来,以备后用。 stdout 作为单数据流(single stream),可以被重定向到文件或是通过管道传入其他程序,但是无法两者兼得。
使用 tee 命令,可以将数据重定向到多个文件,还可以提供一份重定向数据的副本作为管道中后续命令的 stdin 。
command | tee FILE1 FILE2 | other_command
需要注意的是 tee 命令只可以从 stdin 中读取数据,并且默认情况下 tee 会首先将原始文件的内容清空,使用 -a 选项可以在文件末尾追加内容。
借助小于号( < ),我们可以像使用 stdin 那样从文件中读取数据:
echo "-e" > zjl.txt
# ps < zjl.txt 是无效的,zjl.txt 会作为标准输入,但是 ps 不接受标准输入
ps $(< zjl.txt) # zjl.txt 作为标准输入,$(...)从标准输入中读取内容作为参数
# 以下均是 ok 的
cat zjl.txt # zjl.txt 作为参数,cat 会查找这个文件,然后读取,显示
cat < zjl.txt # zjl.txt 作为标准输入,cat 从标准输入中读取数据显示
标准输入和命令行参数的区别:【为什么上述 ps 命令需要使用 $( … ),而如果是 cat 命令却不需要】
标准输入就是编程语言中诸如scanf或者readline这种命令读取数据的地方;而命令行参数是指程序的main函数传入的args字符数组。
管道符和重定向符是将数据作为程序的标准输入,而$(cmd)是读取cmd命令输出的数据作为参数。
对于某些命令来说,不接受标准输入,只接收命令行参数(eg. ps 命令);而有的命令即接受标准输入,也可以接受命令行参数(eg. cat 命令)
参考链接:
文件描述符是一种用于访问文件的抽象指示器(abstract indicator)。存取文件离不开被称为“文件描述符”的特殊数字。 0 、 1 和 2 分别是 stdin 、 stdout 和 stderr 预留的描述符编号。
exec 命令创建全新的文件描述符。
< 操作符可以将文件读入 stdin 。 > 操作符用于截断模式的文件写入(数据在目标文件内容被截断之后写入)。 >> 操作符用于追加模式的文件写入(数据被追加到文件的现有内容之后,而且该目标文件中原有的内容不会丢失)。文件描述符可以用以上3种模式中的任意一种来创建。
截断写入模式的时候,有以下特点:
- 创建一个截断写入模式的描述符时会将该文件的内容清空
- 在该模式下,会从上一次写入结束位置开始,截取指定长度(新写入内容的长度)进行覆盖
# 创建使用只读模式文件描述符
echo this is a test line > input.txt
exec 3<input.txt
cat<&3
# 创建并使用截断写入模式文件描述符
exec 4>output.txt
echo newline >&4
cat output.txt
# 创建并使用追加写入模式文件描述符
exec 5>>input.txt
echo appended line >&5
cat input.txt
- 读取写入文件不需要 &,但文件描述符需要
- 只读文件描述符只能使用一次,如果要再次读取,我们就不能继续使用文件描述符 3 了,而是需要用 exec 重新创建一个新的文件描述符(可以是 4 )来从另一个文件中读取或是重新读取上一个文件。
重定向操作符( > 和 >> )可以将输出发送到文件中,而不是终端。>和 >> 略有差异。尽管两者都可以将文本重定向到文件,但是前者会先清空文件,然后再写入内容,而后者会将内容追加到现有文件的尾部。 默认情况下,重定向操作针对的是标准输出。如果想使用特定的文件描述符,你必须将描述符编号置于操作符之前。
> 等同于 1> ;对于 >> 来说,情况也类似(即 >> 等同于 1>> )。
处理错误时,来自 stderr 的输出被倾倒入文件/dev/null中。./dev/null是一个特殊的设备文件,它会丢弃接收到的任何数据。null设备通常也被称为黑洞,因为凡是进入其中的数据都将一去不返。
数组允许脚本利用索引将数据集合保存为独立的条目。Bash支持普通数组和关联数组,前者使用整数作为数组索引,后者使用字符串作为数组索引。
普通数组
# 使用数值列表定义数组
array_var=(test1 test2 test3 test4)
# 通过索引的方式赋值
array_var[0]="test1"
array_var[1]="test2"
array_var[2]="test3"
array_var[3]="test4"
array_var[4]="test5"
array_var[5]="test6"
# 打印数组的值
echo ${array_var[0]} # test1
index=5
echo ${array_var[$index]} # test6
# 打印数组的全部值
echo ${array_var[*]}
echo ${array_var[@]}
# 打印数组长度
echo ${#array_var[*]} # 6
关联数组(Bash 4.0 开始支持):类似字典
# 首先需要声明关联数组
declare -A ass_array
# 添加元素的方式
ass_array=([index1]=val1 [index2]=val2) # 一行初始化
ass_array[index1]=val1
ass_array[index2]=val1 # 按索引赋值
# 例子:定义水果价格
declare -A fruits_value
fruits_value=([apple]='100 dollars' [orange]='150 dollars')
echo "Apple costs ${fruits_value[apple]}" # Apple costs 100 dollars
显示数组索引:
echo ${!array_var[*]} 或者 echo ${!array_var[@]} ;对于普通数组和关联数组均有效。
# 输入输出
echo
cat
printf
# 权限管理
chmod
# 字符串操作
# 对来自标准输入的字符进行替换、压缩和删除。
# -c或——complerment:取代所有不属于第一字符集的字符;
# -d或——delete:删除所有属于第一字符集的字符;
# -s或--squeeze-repeats:把连续重复的字符以单独一个字符表示;
# -t或--truncate-set1:先删除第一字符集较第二字符集多出的字符。
tr(选项)(参数)
# shell 相关
echo
exec
shopt
# 环境变量
env
printenv
# 进程
# 以名称为依据从运行进程队列中查找进程,并显示查找到的进程id
# -o:仅显示找到的最小(起始)进程号;
# -n:仅显示找到的最大(结束)进程号;
# -l:显示进程名称;
# -P:指定父进程号;
# -g:指定进程组;
# -t:指定开启进程的终端;
# -u:指定进程的有效用户ID。
pgrep(选项)(参数)
# 重定向
tee 命令
# 创建文件描述符
exec
除。
tr(选项)(参数)
echo
exec
shopt
env
printenv
以名称为依据从运行进程队列中查找进程,并显示查找到的进程id
-o:仅显示找到的最小(起始)进程号;
-n:仅显示找到的最大(结束)进程号;
-l:显示进程名称;
-P:指定父进程号; # -g:指定进程组; # -t:指定开启进程的终端; # -u:指定进程的有效用户ID。
pgrep(选项)(参数)
tee 命令
exec