shell
Shell脚本:Linux Shell脚本学习指南(超详细)
Shell 既是一个连接用户和 Linux 内核的程序,又是一门管理 Linux 系统的脚本语言(多条命令写到一个文件中)。
真正能够控制计算机硬件(CPU、内存、显示器等)的只有操作系统内核(Kernel),图形界面和命令行只是架设在用户和内核之间的一座桥梁。
第1章 Shell基础(开胃菜)
Shell 是什么?
由于安全、复杂、繁琐等原因,用户不能直接接触内核(也没有必要),需要另外再开发一个程序,让用户直接使用这个程序;
该程序的作用就是接收用户的操作(点击图标、输入命令),并进行简单的处理,然后再传递给内核,这样用户就能间接地使用操作系统内核了。
在Linux下,这个命令行程序叫做 Shell。
Shell 除了能解释用户输入的命令,将它传递给内核,还可以:
- 调用其他程序,给其他程序传递数据或参数,并获取程序的处理结果;
- 在多个程序之间传递数据,把一个程序的输出作为另一个程序的输入;
- Shell 本身也可以被其他程序调用。
由此可见,Shell 是将内核、程序和用户连接了起来。
Shell 也支持编程(.sh文件)
在 Shell 中输入的命令,有一部分是 Shell 本身自带的,这叫做内置命令(shell启动,内置命令常驻内存);
有一部分是其它的应用程序(一个程序就是一个命令),这叫做外部命令。(运行外部命令要开启一个新的进程,所以效率上比内置命令差很多。)
应用程序就是一个文件,只不过这个文件是可以执行的。既然是文件,那么它就有一个名字,并且存放在文件系统中。
用户在 Shell 中输入一个外部命令后,只是将可执行文件的名字告诉了Shell,但是并没有告诉 Shell 去哪里寻找这个文件。
为了解决这个问题,Shell 在启动文件中增加了一个叫做 PATH 的环境变量,该变量就保存了 Shell 对外部命令的查找路径,如果在这些路径下找不到同名的文件,Shell 也不会再去其它路径下查找了,它就直接报错。
Shell 虽然没有 C/C++、Java、Python 等强大,但也支持了基本的编程元素,例如:
- if...else 选择结构,switch...case 开关语句,for、while、until 循环;
- 变量、数组、字符串、注释、加减乘除、逻辑运算等概念;
- 函数,包括用户自定义的函数和内置函数(例如 printf、export、eval 等)。
Shell 是一种脚本语言
(1)编译型语言 : 在程序运行之前将所有代码都翻译成二进制形式,也就是生成可执行文件。如 C/C++、Pascal、Go语言、汇编等
这个过程叫做编译(Compile),这样的编程语言叫做编译型语言,完成编译过程的软件叫做编译器(Compiler)。
(2)解释型语言或者脚本语言(Script):一边执行一边翻译,不会生成任何可执行文件,用户必须拿到源码才能运行程序。。如 Shell、JavaScript、Python、PHP等
这个过程叫做解释,完成解释过程的软件叫做解释器。
我们在使用 adb shell 的时候,ls 会显示出目录???是怎么实现的
它不是内核显示出来的,只是因为使用了ls命令才会显示出来
倒序的推导:应该是:显示一定是使用了显示驱动, printf 打印出来的,怎么封装的打印?有时间可以去看下
大致是ls读取目录和文件的名字,然后将它们打印出来
常用的Shell有哪些?
常见的 Shell 有 sh、bash、csh、tcsh、ash 等。
sh
sh 的全称是 Bourne shell,由 AT&T 公司的 Steve Bourne开发,为了纪念他,就用他的名字命名了。
sh 是 UNIX 上的标准 shell,很多 UNIX 版本都配有 sh。sh 是第一个流行的 Shell。
csh
sh 之后另一个广为流传的 shell 是由柏克莱大学的 Bill Joy 设计的,这个 shell 的语法有点类似C语言,所以才得名为 C shell ,简称为 csh。
Bill Joy 是一个风云人物,他创立了 BSD 操作系统,开发了 vi 编辑器,还是 Sun 公司的创始人之一。
tcsh
tcsh 是 csh 的增强版,加入了命令补全功能,提供了更加强大的语法支持。
ash
一个简单的轻量级的 Shell,占用资源少,适合运行于低内存环境,但是与下面讲到的 bash shell 完全兼容。
bash
bash shell 是 Linux 的默认 shell,本教程也基于 bash 编写。
bash 由 GNU 组织开发,保持了对 sh shell 的兼容性,是各种 Linux 发行版默认配置的 shell。
bash 兼容 sh 意味着,针对 sh 编写的 Shell 代码可以不加修改地在 bash 中运行。
Shell 是一个程序,一般都是放在/bin或者/usr/bin目录下,当前 Linux 系统可用的 Shell 都记录在/etc/shells文件中。
/etc/shells是一个纯文本文件,你可以在图形界面下打开它,也可以使用 cat 命令查看它。
在现代的 Linux 上,sh 已经被 bash 代替,/bin/sh往往是指向/bin/bash的符号链接。
如果你希望查看当前 Linux 的默认 Shell,那么可以输出 SHELL 环境变量:
$ echo $SHELL
/bin/bash
输出结果表明默认的 Shell 是 bash。SHELL是 Linux 系统中的环境变量,它指明了当前使用的 Shell 程序的位置,也就是使用的哪个 Shell。
Linux Shell命令的基本格式
Shell 命令的基本格式如下:command [选项] [参数] []表示可选的,也就是可有可无。
短格式选项和长格式选项
Linux 的选项又分为短格式选项和长格式选项。
- 短格式选项是长格式选项的简写,用一个减号
-和一个字母表示,例如ls -l。 - 长格式选项是完整的英文单词,用两个减号
--和一个单词表示,例如ls --all。
参数是命令的操作对象,一般情况下,文件、目录、用户和进程等都可以作为参数被命令操作。例如:
[mozhiyan@localhost demo]$ ls -l main.c -rw-rw-r--. 1 mozhiyan mozhiyan 650 4月 10 11:06 main.c
选项附带的参数:有些命令的选项后面也可以附带参数,这些参数用来补全选项,或者调整选项的功能细节。
如果我们只是想读取固定长度的字符串,那么可以给 read 命令增加-n选项。比如读取一个字符作为性别的标志,那么可以这样写:
read -n 1 sex
1是-n选项的参数,sex是 read 命令的参数。 -n选项表示读取固定长度的字符串,那么它后面必然要跟一个数字用来指明长度,否则选项是不完整的。
内置命令后面附带的所有数据(所有选项和参数)最终都以参数的形式传递给了函数,外部命令后面附带的所有数据(所有选项和参数)最终都以参数的形式传递给了应用程序。
命令后面附带的数据都是“原汁原味”地传递给了函数,比如getsum -s 1 -e 100要传递的四个参数分别是 -s、1、-e、100,减号-也会一起传递过去,在函数内部,减号 -可以用来区分该参数是否是命令的选项。
Linux Shell命令提示符
[mozhiyan@localhost: ~]$
各个部分的含义如下:
[]是提示符的分隔符号,没有特殊含义。(有的没有)mozhiyan表示当前登录的用户,我现在使用的是 mozhiyan 用户登录。@是分隔符号,没有特殊含义。- :
localhost表示当前系统的简写主机名(完整主机名是 localhost.localdomain)。~代表用户当前所在的目录为主目录(home 目录)。如果用户当前位于主目录下的 bin 目录中,那么这里显示的就是bin。$是命令提示符。Linux 用这个符号标识登录的用户权限等级:如果是超级用户(root 用户),提示符就是#;如果是普通用户,提示符就是$。
Shell 通过修改PS1和PS2(环境变量)的值就能修改命令提示符的格式。
- PS1 控制最外层的命令提示符格式。
- PS2 控制第二层的命令提示符格式。
Shell脚本
在 test.sh 中输入代码:
#!/bin/bash
echo "Hello World !" #这是一条语句
第 1 行的#!是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell;后面的/bin/bash就是指明了解释器的具体位置。
第 2 行的 echo 命令用于向标准输出文件(Standard Output,stdout,一般就是指显示器)输出文本。在.sh文件中使用命令与在终端直接输入命令的效果是一样的。
第 2 行的#及其后面的内容是注释。Shell 脚本中所有以#开头的都是注释(当然以#!开头的除外)。
#!/bin/bash
# Copyright (c) http://c.biancheng.net/shell/
echo "What is your name?"
read PERSON
echo "Hello, $PERSON"
第 5 行中表示从终端读取用户输入的数据,并赋值给 PERSON 变量。read 命令用来从标准输入文件(Standard Input,stdin,一般就是指键盘)读取用户输入的数据。
第 6 行表示输出变量 PERSON 的内容。注意在变量名前边要加上$,否则变量名会作为字符串的一部分处理。
执行Shell脚本(多种方法)
(1)在新进程中运行 Shell 脚本
1) 将 Shell 脚本作为程序运行
Shell 脚本也是一种解释执行的程序,可以在终端直接调用(需要使用 chmod 命令给 Shell 脚本加上执行权限),如下所示:
[mozhiyan@localhost ~]$ cd demo #切换到 test.sh 所在的目录
[mozhiyan@localhost demo]$ chmod +x ./test.sh #给脚本添加执行权限
[mozhiyan@localhost demo]$ ./test.sh #执行脚本文件 ./表示当前目录
Hello World ! #运行结果
2) 将 Shell 脚本作为参数传递给 Bash 解释器
你也可以直接运行 Bash 解释器,将脚本文件的名字作为参数传递给 Bash,如下所示:
[mozhiyan@localhost ~]$ cd demo #切换到 test.sh 所在的目录
[mozhiyan@localhost demo]$ /bin/bash test.sh #使用Bash的绝对路径(命令可以用绝对路径来找?)
Hello World ! #运行结果
通过这种方式运行脚本,不需要在脚本文件的第一行指定解释器信息,写了也没用。
更加简洁的写法是运行 bash 命令。bash 是一个外部命令(应用程序), /bin/bash在PATH环境变量中
[mozhiyan@localhost ~]$ cd demo [mozhiyan@localhost demo]$ bash test.sh Hello World !
(2)在当前进程中运行 Shell 脚本
source 命令:是 Shell 内置命令的一种,它会读取脚本文件中的代码,并依次执行所有语句。你也可以理解为,source 命令会强制执行脚本文件中的全部命令,而忽略脚本文件的权限。
source 命令的用法为:
source filename
也可以简写为:
. filename
两种写法的效果相同。对于第二种写法,注意点号.和文件名中间有一个空格。
Linux 中的每一个进程都有一个唯一的 ID,称为 PID,使用$$变量就可以获取当前进程的 PID。
Shell配置文件(配置脚本)的加载
与 Bash Shell 有关的配置文件主要有 /etc/profile、~/.bash_profile、~/.bash_login、~/.profile、~/.bashrc、/etc/bashrc、/etc/profile.d/*.sh,不同的启动方式会加载不同的配置文件。
~表示用户主目录。*是通配符,/etc/profile.d/*.sh 表示 /etc/profile.d/ 目录下所有的脚本文件(以.sh结尾的文件)。
(1)登录式的 Shell
Bash 官方文档说:如果是登录式的 Shell,首先会读取和执行 /etc/profiles,这是所有用户的全局配置文件,
接着会到用户主目录中寻找 ~/.bash_profile、~/.bash_login 或者 ~/.profile,它们都是用户个人的配置文件。
如果三个文件同时存在的话,到底应该加载哪一个呢?它们的优先级顺序是 ~/.bash_profile > ~/.bash_login > ~/.profile。
如果 ~/.bash_profile 存在,那么一切以该文件为准,并且到此结束,不再加载其它的配置文件。
如果 ~/.bash_profile 不存在,那么尝试加载 ~/.bash_login。~/.bash_login 存在的话就到此结束,不存在的话就加载 ~/.profile。
注意,/etc/profiles 文件还会嵌套加载 /etc/profile.d/*.sh,请看下面的代码:
for i in /etc/profile.d/*.sh ; do
if [ -r "$i" ]; then
if [ "${-#*i}" != "$-" ]; then
. "$i"
else
. "$i" >/dev/null
fi
fi
done
同样,~/.bash_profile 也使用类似的方式加载 ~/.bashrc:
if [ -f ~/.bashrc ]; then . ~/.bashrc fi
(2)非登录式的 Shell
如果以非登录的方式启动 Shell,那么就不会读取以上所说的配置文件,而是直接读取 ~/.bashrc。
~/.bashrc 文件还会嵌套加载 /etc/bashrc,请看下面的代码:
if [ -f /etc/bashrc ]; then . /etc/bashrc fi
(3)给 PATH 变量增加新的路径
当用户登录 Shell 时,PATH 变量会在 /etc/profile 文件中设置,然后在 ~/.bash_profile 也会增加几个目录。
如果没有登录 Shell,PATH 变量会在 /etc/bashrc 文件中设置。
PATH=$PATH:$HOME/addon
第2章 Shell编程(Shell 脚本编程,重点讲解变量、字符串、数组、数学计算、选择结构、循环结构和函数。)
Shell变量
字符串可以由单引号' '包围,也可以由双引号" "包围,也可以不用引号。
下面我们说一下三种形式的区别:
1) 由单引号' '包围的字符串:
- 任何字符都会原样输出,在其中使用变量是无效的。
- 字符串中不能出现单引号,即使对单引号进行转义也不行。
2) 由双引号" "包围的字符串:
- 如果其中包含了某个变量,那么该变量会被解析(得到该变量的值),而不是原样输出。
- 字符串中可以出现双引号,只要它被转义了就行。(双引号需要被转义)
3) 不被引号包围的字符串
- 不被引号包围的字符串中出现变量时也会被解析,这一点和双引号
" "包围的字符串一样。 - 字符串中不能出现空格,否则空格后边的字符串会作为其他变量或者命令解析。
脚本语言在定义变量时通常不需要指明类型,直接赋值就可以,Shell 变量也遵循这个规则。
在 Bash shell 中,每一个变量的值都是字符串,无论你给变量赋值时有没有使用引号,值都会以字符串的形式存储。
可以使用 declare 关键字显式定义变量的类型
Shell 支持以下三种定义变量的方式:
variable=value
variable='value'
variable="value"
如果 value 不包含任何空白符(例如空格、Tab 缩进等),那么可以不使用引号;
如果 value 包含了空白符,那么就必须使用引号包围起来。
注意,赋值号=的周围不能有空格,这可能和你熟悉的大部分编程语言都不一样。(其它的都需要空格)
Shell 变量的命名规范和大部分编程语言都一样:
- 变量名由数字、字母、下划线组成;
- 必须以字母或者下划线开头;
- 不能使用 Shell 里的关键字(通过 help 命令可以查看保留关键字)。
单引号和双引号的区别
#!/bin/bash
url="http://c.biancheng.net"
website1='C语言中文网:${url}'
website2="C语言中文网:${url}"
echo $website1
echo $website2
运行结果:
C语言中文网:${url}
C语言中文网:http://c.biancheng.net
以单引号' '包围变量的值时,单引号里面是什么就输出什么,即使内容中有变量和命令(命令需要反引起来)也会把它们原样输出。
以双引号" "包围变量的值时,输出时会先解析里面的变量和命令,而不是把双引号中的变量名和命令原样输出。
使用变量
使用一个定义过的变量,只要在变量名前面加美元符号$即可,变量名外面的花括号{ }是可选的,加花括号是为了帮助解释器识别变量的边界
将命令的结果赋值给变量
Shell 也支持将命令的执行结果赋值给变量,常见的有以下两种方式:
variable=`command`
variable=$(command)
第一种方式把命令用反引号` `(位于 Esc 键的下方)包围起来;
第二种方式把命令用$()包围起来,区分更加明显,所以推荐使用这种方式。
只读变量
使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。
删除变量
unset variable_name(不能删除只读变量)
Shell变量的作用域:全局变量、环境变量和局部变量
Shell 变量的作用域可以分为三种:
- 有的变量只能在函数内部使用,这叫做局部变量(local variable);
- 有的变量可以在当前 Shell 进程中使用,这叫做全局变量(global variable);
- 而有的变量还可以在子进程中使用,这叫做环境变量(environment variable)。
Shell 局部变量
Shell 也支持自定义函数,但是 Shell 函数和 C++、Java、C# 等其他编程语言函数的一个不同点就是:在 Shell 函数中定义的变量默认也是全局变量,
它和在函数外部定义变量拥有一样的效果。
要想变量的作用域仅限于函数内部,可以在定义时加上local命令,此时该变量就成了局部变量。
Shell 全局变量
全局变量只在当前 Shell 进程中有效,对其它 Shell 进程和子进程都无效。在 Shell 中定义的变量,默认就是全局变量。
全局变量的作用范围是当前的 Shell 进程,而不是当前的 Shell 脚本文件,它们是不同的概念。
一个 Shell 进程中可以使用 source 命令执行多个 Shell 脚本文件,此时全局变量在这些脚本文件中都有效。
Shell 环境变量(也是临时的)
如果使用export命令将全局变量导出,那么它就在所有的子进程中也有效了,这称为“环境变量”。
创建 Shell 子进程最简单的方式是运行 bash 命令
Shell 进程每次启动时都会执行配置文件中的代码做一些初始化工作,如果将变量放在配置文件中,那么每次启动进程都会定义这个变量。
注意,两个没有父子关系的 Shell 进程是不能传递环境变量的,并且环境变量只能向下传递而不能向上传递,即“传子不传父”。
Shell命令替换:将命令的输出结果赋值给变量
commands 是要执行的命令。commands 可以只有一个命令,也可以有多个命令,多个命令之间以分号;分隔。
使用 date 命令的%s格式控制符可以得到当前的 UNIX 时间戳,这样就可以直接计算脚本的运行时间了。
如果被替换的命令的输出内容包括多行(也即有换行符),或者含有多个连续的空白符,那么在输出变量时应该将变量用双引号包围,
否则系统会使用默认的空白符来填充,这会导致换行无效,以及连续的空白符被压缩成一个。
注意, $() 支持嵌套,反引号不行。$() 仅在 Bash Shell 中有效,而反引号可在多种 Shell 中使用
Shell位置参数(命令行参数)
运行 Shell 脚本文件时我们可以给它传递一些参数,这些参数在脚本文件内部可以使用$n的形式来接收,例如,$1 表示第一个参数,$2 表示第二个参数,依次类推。
同样,在调用函数时也可以传递参数。Shell 函数参数的传递和其它编程语言不同,没有所谓的形参和实参,在定义函数时也不用指明参数的名字和数目。
换句话说,定义 Shell 函数时不能带参数,但是在调用函数时却可以传递参数,这些传递进来的参数,在函数内部就也使用$n的形式接收,例如,$1 表示第一个参数,$2 表示第二个参数,依次类推。
这种通过$n的形式来接收的参数,在 Shell 中称为位置参数。
注意,如果参数达到或者超过了 10 个,那么就得用${n}的形式来接收了,例如 ${10}、${23}。{ }的作用是为了帮助解释器识别参数的边界,这跟使用变量时加{ }是一样的效果。
Shell特殊变量:Shell $#、$*、$@、$?、$$
| 变量 | 含义 |
|---|---|
| $0 | 当前脚本的文件名(不是脚本文件名返回的是bash)。 |
| $n(n≥1) | 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是 $1,第二个参数是 $2。 |
| $# | 传递给脚本或函数的参数个数。 |
| $* | 传递给脚本或函数的所有参数。 |
| $@ | 传递给脚本或函数的所有参数。当被双引号" "包含时,$@ 与 $* 稍有不同,我们将在《Shell $*和$@的区别》一节中详细讲解。 |
| $? | 上个命令的退出状态,或函数的返回值,我们将在《Shell $?》一节中详细讲解。 |
| $$ | 当前 Shell 进程 ID。对于 Shell 脚本,就是这些脚本所在的进程 ID。 |
Shell $*和$@之间的区别
当 $* 和 $@ 不被双引号" "包围时,它们之间没有任何区别,都是将接收到的每个参数看做一份数据,彼此之间以空格来分隔。
但是当它们被双引号" "包含时,就会有区别了:
"$*"会将所有的参数从整体上看做一份数据,而不是把每个参数都看做一份数据。"$@"仍然将每个参数都看作一份数据,彼此之间是独立的。
如果使用 echo 直接输出"$*"和"$@"做对比,是看不出区别的;但如果使用 for 循环来逐个输出数据,立即就能看出区别来。
Shell $?:获取函数返回值或者上一个命令的退出状态
所谓退出状态,就是上一个命令执行后的返回结果。退出状态是一个数字,一般情况下,大部分命令执行成功会返回 0,失败返回 1,这和C语言的 main() 函数是类似的。
exit表示退出当前 Shell 进程,我们必须在新进程中运行 test.sh,否则当前 Shell 会话(终端窗口)会被关闭,我们就无法取得它的退出状态了。
return 关键字用来表示函数的退出状态,而不是函数的返回值;
Shell字符串
获取字符串长度
在 Shell 中获取字符串长度很简单,具体方法如下:
${#string_name}
Shell字符串拼接(连接、合并)
在 Shell 中你不需要使用任何运算符,将两个字符串并排放在一起就能实现拼接,非常简单粗暴。
Shell字符串截取(非常详细)
Shell 截取字符串通常有两种方式:从指定位置开始截取和从指定字符(子字符串)开始截取。
(1)从指定位置开始截取:2个参数(指定起始位置、截取长度)
从左边开始计数:${string: start :length}
从右边开始计数:${string: 0-start :length}
注意:省略 length,直接截取到字符串末尾
(2)从指定字符(子字符串)开始截取:这种截取方式无法指定字符串长度,只能从指定字符(子字符串)截取到字符串末尾。
使用 # 号截取右边字符:${string#*chars} chars 是指定的字符(或者子字符串),*是通配符的一种,表示任意长度的字符串。
*chars连起来使用的意思是:忽略左边的所有字符,直到遇见 chars(chars 不会被截取)。
如果希望直到最后一个指定字符(子字符串)再匹配结束,那么可以使用##,具体格式为:${string##*chars}
使用 % 截取左边字符:${string%chars*}
最后,我们对以上 8 种格式做一个汇总,请看下表:
| 格式 | 说明 |
|---|---|
| ${string: start :length} | 从 string 字符串的左边第 start 个字符开始,向右截取 length 个字符。 |
| ${string: start} | 从 string 字符串的左边第 start 个字符开始截取,直到最后。 |
| ${string: 0-start :length} | 从 string 字符串的右边第 start 个字符开始,向右截取 length 个字符。 |
| ${string: 0-start} | 从 string 字符串的右边第 start 个字符开始截取,直到最后。 |
| ${string#*chars} | 从 string 字符串第一次出现 *chars 的位置开始,截取 *chars 右边的所有字符。 |
| ${string##*chars} | 从 string 字符串最后一次出现 *chars 的位置开始,截取 *chars 右边的所有字符。 |
| ${string%*chars} | 从 string 字符串第一次出现 *chars 的位置开始,截取 *chars 左边的所有字符。 |
| ${string%%*chars} | 从 string 字符串最后一次出现 *chars 的位置开始,截取 *chars 左边的所有字符。 |
shell数组:Shell数组定义以及获取数组元素
Shell 数组的定义
Bash Shell 只支持一维数组,不支持多维数组。
在 Shell 中,用括号( )来表示数组,数组元素之间用空格来分隔。由此,定义数组的一般形式为:array_name=(ele1 ele2 ele3 ... elen)
Shell 是弱类型的,它并不要求所有数组元素的类型必须相同,例如:arr=(20 56 "http://c.biancheng.net/shell/")
Shell 数组的长度不是固定的,定义之后还可以增加元素。例如,对于上面的 nums 数组,它的长度是 6,使用下面的代码会在最后增加一个元素,使其长度扩展到 7:nums[6]=88
你也无需逐个元素地给数组赋值,下面的代码就是只给特定元素赋值:ages=([3]=24 [5]=19 [10]=12) 只给第 3、5、10 个元素赋值,所以数组长度是 3。
获取数组元素
获取数组元素的值,一般使用下面的格式:${array_name[index]}
使用@或*可以获取数组中的所有元素,例如:
- ${nums[*]}
- ${nums[@]}
Shell获取数组长度
利用@或*,可以将数组扩展成列表,然后使用#来获取数组元素的个数,格式如下:
${#array_name[@]}
${#array_name[*]}
其中 array_name 表示数组名。两种形式是等价的,选择其一即可。
Shell数组拼接,Shell数组合并
所谓 Shell 数组拼接(数组合并),就是将两个数组连接成一个数组。
拼接数组的思路是:先利用@或*,将数组扩展成列表,然后再合并到一起。具体格式如下:
array_new=(${array1[@]} ${array2[@]})
array_new=(${array1[*]} ${array2[*]}) 先获取所有元素,然后合并为一个数组
Shell删除数组元素(也可以删除整个数组)
在 Shell 中,使用 unset 关键字来删除数组元素,具体格式如下:
unset array_name[index]
如果不写下标,而是写成下面的形式:
unset array_name
Shell关联数组(下标是字符串的数组)
关联数组也称为“键值对(key-value)”数组,键(key)也即字符串形式的数组下标,值(value)也即元素值。
array_name["index"]
获取所有元素的下标和值
使用下面的形式可以获得关联数组的所有元素值:
${array_name[@]}
${array_name[*]}
使用下面的形式可以获取关联数组的所有下标值:
${!array_name[@]}
${!array_name[*]}
Shell内建命令(内置命令)
所谓 Shell 内建命令,就是由 Bash 自身提供的命令,而不是文件系统中的某个可执行文件。
可以使用 type 来确定一个命令是否是内建命令:
[root@localhost ~]# type cd
cd is a Shell builtin
内建命令会比外部命令执行得更快,执行外部命令时不但会触发磁盘 I/O,还需要 fork 出一个单独的进程来执行,执行完成后再退出。
而执行内建命令相当于调用当前 Shell 进程的一个函数。
| 命令 | 说明 |
|---|---|
| : | 扩展参数列表,执行重定向操作 |
| . | 读取并执行指定文件中的命令(在当前 shell 环境中) |
| alias | 为指定命令定义一个别名 |
| bg | 将作业以后台模式运行 |
| bind | 将键盘序列绑定到一个 readline 函数或宏 |
| break | 退出 for、while、select 或 until 循环 |
| builtin | 执行指定的 shell 内建命令 |
| caller | 返回活动子函数调用的上下文 |
| cd | 将当前目录切换为指定的目录 |
| command | 执行指定的命令,无需进行通常的 shell 查找 |
| compgen | 为指定单词生成可能的补全匹配 |
| complete | 显示指定的单词是如何补全的 |
| compopt | 修改指定单词的补全选项 |
| continue | 继续执行 for、while、select 或 until 循环的下一次迭代 |
| declare | 声明一个变量或变量类型。 |
| dirs | 显示当前存储目录的列表 |
| disown | 从进程作业表中刪除指定的作业 |
| echo | 将指定字符串输出到 STDOUT |
| enable | 启用或禁用指定的内建shell命令 |
| eval | 将指定的参数拼接成一个命令,然后执行该命令 |
| exec | 用指定命令替换 shell 进程 |
| exit | 强制 shell 以指定的退出状态码退出 |
| export | 设置子 shell 进程可用的变量 |
| fc | 从历史记录中选择命令列表 |
| fg | 将作业以前台模式运行 |
| getopts | 分析指定的位置参数 |
| hash | 查找并记住指定命令的全路径名 |
| help | 显示帮助文件 |
| history | 显示命令历史记录 |
| jobs | 列出活动作业 |
| kill | 向指定的进程 ID(PID) 发送一个系统信号 |
| let | 计算一个数学表达式中的每个参数 |
| local | 在函数中创建一个作用域受限的变量 |
| logout | 退出登录 shell |
| mapfile | 从 STDIN 读取数据行,并将其加入索引数组 |
| popd | 从目录栈中删除记录 |
| printf | 使用格式化字符串显示文本 |
| pushd | 向目录栈添加一个目录 |
| pwd | 显示当前工作目录的路径名 |
| read | 从 STDIN 读取一行数据并将其赋给一个变量 |
| readarray | 从 STDIN 读取数据行并将其放入索引数组 |
| readonly | 从 STDIN 读取一行数据并将其赋给一个不可修改的变量 |
| return | 强制函数以某个值退出,这个值可以被调用脚本提取 |
| set | 设置并显示环境变量的值和 shell 属性 |
| shift | 将位置参数依次向下降一个位置 |
| shopt | 打开/关闭控制 shell 可选行为的变量值 |
| source | 读取并执行指定文件中的命令(在当前 shell 环境中) |
| suspend | 暂停 Shell 的执行,直到收到一个 SIGCONT 信号 |
| test | 基于指定条件返回退出状态码 0 或 1 |
| times | 显示累计的用户和系统时间 |
| trap | 如果收到了指定的系统信号,执行指定的命令 |
| type | 显示指定的单词如果作为命令将会如何被解释 |
| typeset | 声明一个变量或变量类型。 |
| ulimit | 为系统用户设置指定的资源的上限 |
| umask | 为新建的文件和目录设置默认权限 |
| unalias | 刪除指定的别名 |
| unset | 刪除指定的环境变量或 shell 属性 |
| wait | 等待指定的进程完成,并返回退出状态码 |
Shell alias:给命令创建别名
若直接输入该命令且不带任何参数,则列出当前 Shell 进程中使用了哪些别名。
使用 alias 命令自定义别名的语法格式为:
alias new_name='command'
使用 unalias 命令删除别名
使用 unalias 内建命令可以删除当前 Shell 进程中的别名。unalias 有两种使用方法:
- 第一种用法是在命令后跟上某个命令的别名,用于删除指定的别名。
- 第二种用法是在命令后接
-a参数,删除当前 Shell 进程中所有的别名。
Shell echo命令:输出字符串
echo 是一个 Shell 内建命令,用来在终端输出字符串,并在最后默认加上换行符。
echo 命令输出结束后默认会换行,如果不希望换行,可以加上-n参数
echo 不会解析以反斜杠\开头的转义字符。比如,\n表示换行,echo 默认会将它作为普通字符对待。
添加-e参数来让 echo 命令解析转义字符
Shell read命令:读取从键盘输入的数据
read 是 Shell 内置命令,用来从标准输入中读取数据并赋值给变量。如果没有进行重定向,默认就是从键盘读取用户输入的数据;如果进行了重定向,那么可以从文件中读取数据。
read 命令的用法为:
read [-options] [variables]
options表示选项,如下表所示;variables表示用来存储数据的变量,可以有一个,也可以有多个。 options和variables都是可选的,如果没有提供变量名,那么读取的数据将存放到环境变量 REPLY 中。
| 选项 | 说明 |
|---|---|
| -a array | 把读取的数据赋值给数组 array,从下标 0 开始。 |
| -d delimiter | 用字符串 delimiter 指定读取结束的位置,而不是一个换行符(读取到的数据不包括 delimiter)。 |
| -e | 在获取用户输入的时候,对功能键进行编码转换,不会直接显式功能键对应的字符。 |
| -n num | 读取 num 个字符,而不是整行字符。 |
| -p prompt | 显示提示信息,提示内容为 prompt。 |
| -r | 原样读取(Raw mode),不把反斜杠字符解释为转义字符。 |
| -s | 静默模式(Silent mode),不会在屏幕上显示输入的字符。当输入密码和其它确认信息的时候,这是很有必要的。 |
| -t seconds | 设置超时时间,单位为秒。如果用户没有在指定时间内输入完成,那么 read 将会返回一个非 0 的退出状态,表示读取失败。 |
| -u fd | 使用文件描述符 fd 作为输入源,而不是标准输入,类似于重定向。 |
Shell exit命令:退出当前进程
Shell declare和typeset命令:设置变量属性
declare 和 typeset 都是 Shell 内建命令,它们的用法相同,都用来设置变量的属性。不过 typeset 已经被弃用了,建议使用 declare 代替。
declare 命令的用法如下所示:
declare [+/-] [aAfFgilprtux] [变量名=变量值]
其中,-表示设置属性,+表示取消属性,aAfFgilprtux都是具体的选项,它们的含义如下表所示:
| 选项 | 含义 |
|---|---|
| -f [name] | 列出之前由用户在脚本中定义的函数名称和函数体。 |
| -F [name] | 仅列出自定义函数名称。 |
| -g name | 在 Shell 函数内部创建全局变量。 |
| -p [name] | 显示指定变量的属性和值。 |
| -a name | 声明变量为普通数组。 |
| -A name | 声明变量为关联数组(支持索引下标为字符串)。 |
| -i name | 将变量定义为整数型。 |
| -r name[=value] | 将变量定义为只读(不可修改和删除),等价于 readonly name。 |
| -x name[=value] | 将变量设置为环境变量,等价于 export name[=value]。 |
Shell until循环用法详解/ while循环详解
unti 循环和 while 循环恰好相反,当判断条件不成立时才进行循环,一旦判断条件成立,就终止循环。
Shell until 循环的用法如下:
until condition
do
statements
done
condition表示判断条件,statements表示要执行的语句(可以只有一条,也可以有多条),do和done都是 Shell 中的关键字。
while 循环是 Shell 脚本中最简单的一种循环,当条件满足时,while 重复地执行一组语句,当条件不满足时,就退出 while 循环。
Shell while 循环的用法如下:
while condition
do
statements
done
condition表示判断条件,statements表示要执行的语句(可以只有一条,也可以有多条),do和done都是 Shell 中的关键字。
Shell for循环和for int循环详解
Shell for 循环有两种使用形式
(1)C语言风格的 for 循环
for((exp1; exp2; exp3)) // for 循环中的 exp1(初始化语句)、exp2(判断条件)和 exp3(自增或自减)都是可选项,都可以省略(但分号;必须保留)。
do
statements
done
几点说明:
- exp1、exp2、exp3 是三个表达式,其中 exp2 是判断条件,for 循环根据 exp2 的结果来决定是否继续下一次循环;
- statements 是循环体语句,可以有一条,也可以有多条;
- do 和 done 是 Shell 中的关键字。
它的运行过程为:
1) 先执行 exp1。
2) 再执行 exp2,如果它的判断结果是成立的,则执行循环体中的语句,否则结束整个 for 循环。
3) 执行完循环体后再执行 exp3。
4) 重复执行步骤 2) 和 3),直到 exp2 的判断结果不成立,就结束循环。
(2)Python 风格的 for in 循环
Python 风格的 for in 循环的用法如下:
for variable in value_list
do
statements
done
variable 表示变量,value_list 表示取值列表,in 是 Shell 中的关键字。
in value_list 部分可以省略,省略后的效果相当于 in $@,本文末尾的「value_list 使用特殊变量」将会详细讲解。
取值列表 value_list 的形式有多种,你可以直接给出具体的值,也可以给出一个范围,还可以使用命令产生的结果,甚至使用通配符,使用特殊变量
给出一个取值范围的具体格式为:
{start..end}
Shell select in循环详解
select in 循环用来增强交互性,它可以显示出带编号的菜单,用户输入不同的编号就可以选择不同的菜单,并执行不同的功能。
Shell select in 循环的用法如下:
select variable in value_list
do
statements
done
注意,select 是无限循环(死循环),输入空值,或者输入的值无效,都不会结束循环,只有遇到 break 语句,或者按下 Ctrl+D 组合键才能结束循环。
#!/bin/bash
echo "What is your favourite OS?"
select name in "Linux" "Windows" "Mac OS" "UNIX" "Android"
do
echo $name
done
echo "You have selected $name"
Shell break和continue跳出循环详解
在C语言、C++、C#、Python、Java 等大部分编程语言中,break 和 continue 只能跳出当前层次的循环,内层循环中的 break 和 continue 对外层循环不起作用;
Shell 中的 break 和 continue 却能够跳出多层循环,也就是说,内层循环中的 break 和 continue 能够跳出外层循环。
break 关键字
Shell break 关键字的用法为:
break n
n 表示跳出循环的层数,如果省略 n,则表示跳出当前的整个循环。break 关键字通常和 if 语句一起使用,即满足条件时便跳出循环。

while 循环通过 read 命令的退出状态来判断循环条件是否成立,只有当按下 Ctrl+D 组合键(表示输入结束)时,read n才会判断失败,此时 while 循环终止。
continue 关键字
Shell continue 关键字的用法为:
continue n
n 表示循环的层数:
- 如果省略 n,则表示 continue 只对当前层次的循环语句有效,遇到 continue 会跳过本次循环,忽略本次循环的剩余代码,直接进入下一次循环。
- 如果带上 n,比如 n 的值为 2,那么 continue 对内层和外层循环语句都有效,不但内层会跳过本次循环,外层也会跳过本次循环,其效果相当于内层循环和外层循环同时执行了不带 n 的 continue。

Shell函数详解(函数定义、函数调用)
Shell 函数的本质是一段可以重复使用的脚本代码。
Shell 函数定义的语法格式如下:
function name() {
statements
[return value]
}
对各个部分的说明:
function是 Shell 中的关键字,专门用来定义函数;name是函数名;statements是函数要执行的代码,也就是一组语句;return value表示函数的返回值,其中 return 是 Shell 关键字,专门用在函数中返回一个值;这一部分可以写也可以不写。
由{ }包围的部分称为函数体,调用一个函数,实际上就是执行函数体中的代码。
函数调用
调用 Shell 函数时可以给它传递参数,也可以不传递。如果不传递参数,直接给出函数名字即可:
name
如果传递参数,那么多个参数之间以空格分隔:
name param1 param2 param3
不管是哪种形式,函数名字后面都不需要带括号。
和其它编程语言不同的是,Shell 函数在定义时不能指明参数,但是在调用时却可以传递参数,并且给它传递什么参数它就接收什么参数。
Shell 也不限制定义和调用的顺序,你可以将定义放在调用的前面,也可以反过来,将定义放在调用的后面。(就将定义放前面)
函数参数是 Shell 位置参数的一种,在函数内部可以使用$n来接收,例如,$1 表示第一个参数,$2 表示第二个参数,依次类推。
除了$n,还有另外三个比较重要的变量:
$#可以获取传递的参数的个数;$@或者$*可以一次性获取所有的参数
Shell 函数的返回值只能是一个介于 0~255 之间的整数,其中只有 0 表示成功,其它值都表示失败。
如果函数体中没有 return 语句,那么使用默认的退出状态,也就是最后一条命令的退出状态。如果这就是你想要的,那么更加严谨的写法为:
return $?
$?是一个特殊变量,用来获取上一个命令的退出状态,或者上一个函数的返回值
如何得到函数的处理结果有两种解决方案:
- 一种是借助全局变量,将得到的结果赋值给全局变量;
- 一种是在函数内部使用 echo、printf 命令将结果输出,在函数外部使用
$()或者``捕获结果。
(2)shell 语法
$ : 作用是取变量值,PATH 是变量名,那么要获得变量的值就是$PATH
比如echo $PATH 就是 打印PATH变量的值到标准输出。
https://www.cnblogs.com/chengd/p/7803664.html
$? :
https://blog.csdn.net/wlovh1989/article/details/51113488
$1:
http://www.cnblogs.com/fhefh/archive/2011/04/15/2017613.html
^ :linux中的^表示行首 $:linux表示行尾
& :后台执行
linux重定向:改变标准输入输出到其它方式
https://blog.csdn.net/fenxinzi557/article/details/52279520
在 bash 命令执行的过程中,主要有三种输入输出的状况,分别是:
1. 标准输入;代码为 0 ;或称为 stdin ;使用的方式为 <
2. 标准输出:代码为 1 ;或称为 stdout;使用的方式为 1> (默认,可以省略)
3. 错误输出:代码为 2 ;或称为 stderr;使用的方式为 2>
> 输出重定向到一个文件或设备 覆盖原来的文件
>! 输出重定向到一个文件或设备 强制覆盖原来的文件
>> 输出重定向到一个文件或设备 追加原来的文件
< 输入重定向到一个程序
管道 |:它仅能处理经由前面一个指令传出的正确输出信息,也就是 standard output 的信息,对于 stdandard
error 信息没有直接处理能力。然后,传递给下一个命令,作为标准的输入 standard input.
管道与重定向的区别:https://www.cnblogs.com/pengliangcheng/p/5211786.html
(补充点小知识:linux中怎么结果一页一页输出
用管道命令(|)把一个命令的结果作为另一个命令的参数即可。 sudo apt-cache search php | less 这句命令的执行流程是这样的,先执行sudo apt-cache search php,把结果存放在一个文件中比如A, 然后再执行less A
for循环:
https://blog.csdn.net/babyfish13/article/details/52981110
Shell for循环语法
for 变量 in 列表
do
command1
command2
...
commandN
done
| [:blank:]包括TAB等,[:space:]就是空格吧。 |
if语句:
http://lovelace.blog.51cto.com/1028430/1211353
http://blog.51cto.com/yangshufan/2054138
(3)shell 命令
http://www.cnblogs.com/zhangze/articles/1832542.html
export命令
用户登录到Linux系统后,系统将启动一个用户shell。在这个shell中,可以使用shell命令或声明变量,也可以创建并运行shell脚本程序。运行shell脚本程序时,系统将创建一个子shell。此时,系统中将有两个shell,一个是登录时系统启动的shell,另一个是系统为运行脚本程序创建的shell。当一个脚本程序运行完毕,脚本shell将终止,返回到执行该脚本之前的shell。从这种意义上来说,用户可以有许多 shell,每个shell都是由某个shell(称为父shell)派生的。在子shell中定义的变量只在该子shell内有效。如果在一个shell脚本程序中定义了一个变量,当该脚本程序运行时,这个定义的变量只是该脚本程序内的一个局部变量,其他的shell不能引用它,要使某个变量的值可以在其他shell中被改变,可以使用export命令对已定义的变量进行输出。这个过程称之为变量输出。
export:
export 功能说明:设置或显示环境变量。
语 法:export [-fnp][变量名称]=[变量设置值]
补充说明:在shell中执行程序时,shell会提供一组环境变量。export可新增,修改或删除环境变量,供后续执行的程序使用。export的效力仅限于该次登陆操作。
参 数:
-f 代表[变量名称]中为函数名称。
-n 删除指定的变量。变量实际上并未删除,只是不会输出到后续指令的执行环境中。
-p 列出所有的shell赋予程序的环境变量。
source命令: https://blog.csdn.net/wangyangkobe/article/details/6595143
当我修改了/etc/profile文件,我想让它立刻生效,而不用重新登录;这时就想到用source命令,如:source /etc/profile
对source进行了学习,并且用它与sh 执行脚本进行了对比,现在总结一下。
source命令也称为“点命令”,也就是一个点符号(.),是bash的内部命令。
功能:使Shell读入指定的Shell程序文件并依次执行文件中的所有语句
source命令通常用于重新执行刚修改的初始化文件,使之立即生效,而不必注销并重新登录。
用法:
source filename 或 . filename
source命令(从 C Shell 而来)是bash shell的内置命令;点命令(.),就是个点符号(从Bourne Shell而来)是source的另一名称。
source filename 与 sh filename 及./filename执行脚本的区别在那里呢?
1.当shell脚本具有可执行权限时,用sh filename与./filename执行脚本是没有区别得。./filename是因为当前目录没有在PATH中,所有"."是用来表示当前目录的。
2.sh filename 重新建立一个子shell,在子shell中执行脚本里面的语句,该子shell继承父shell的环境变量,但子shell新建的、改变的变量不会被带回父shell,除非使用export。
3.source filename:这个命令其实只是简单地读取脚本里面的语句依次在当前shell里面执行,没有建立新的子shell。那么脚本里面所有新建、改变变量的语句都会保存在当前shell里面。
Local命令
local一般用于局部变量声明,多在在函数内部使用。
(1)shell脚本中定义的变量是global的,其作用域从被定义的地方开始,到shell结束或被显示删除的地方为止。
(2)shell函数定义的变量默认是global的,其作用域从“函数被调用时执行变量定义的地方”开始,到shell结束或被显示删除处为止。函数定义的变量可以被显示定义成local的,其作用域局限于函数内。但请注意,函数的参数是local的。
(3)如果同名,Shell函数定义的local变量会屏蔽脚本定义的global变量。
cat 和 echo命令
Cat
https://www.cnblogs.com/zhangchenliang/p/7717602.html
Echo:
https://www.cnblogs.com/my-blogs-for-everone/p/7138013.html
cat命令是linux下的一个文本输出命令,通常是用于观看某个文件的内容的
echo命令的功能是在显示器上显示一段文字
两个区别:一个是输出文件,一个是输出字符串:默认都是输出到显示器上的。
seq命令
https://blog.csdn.net/nyist327/article/details/46958157
用法:seq [选项]... 尾数
或:seq [选项]... 首数 尾数
或:seq [选项]... 首数 增量 尾数
以指定增量从首数开始打印数字到尾数。
-f, --format=格式 使用printf 样式的浮点格式
-s, --separator=字符串使用指定字符串分隔数字(默认使用:\n)
-w, --equal-width 在列前添加0 使得宽度相同
--help 显示此帮助信息并退出
--version 显示版本信息并退出
如果省略了首数或者增量,则默认其值为1,即使这样尾数仍小于首数。
首数、增量和尾数均以浮点数形式解释。当首数小于尾数时增量一般为正值,
相反在首数大于尾数时增量一般为负数。
指定的格式必须适用于显示"double"类型的参数;当首数、增量和尾数均为指定
精确度的定点十进制数时默认为"%.精确度f",否则默认为"%g"。
sed命令:
grep命令:在文本中搜索指定的字符串
https://blog.csdn.net/onlyou930/article/details/6582409
默认情况下,‘grep’只搜索当前目录。如果此目录下有许多子目录,‘grep’会以如下形式列出:
grep: sound: Is a directory
这可能会使‘grep’的输出难于阅读。这里有两种解决的办法:
明确要求搜索子目录:grep -r
忽略子目录:grep -d skip
sort命令
https://www.cnblogs.com/kimbo/p/7263344.html?utm_source=itdadao&utm_medium=referral
uniq命令
read命令
https://www.cnblogs.com/lottu/p/3962921.html
ping命令
http://aiezu.com/article/linux_ping_command.html
fdisk命令
mkdir命令
linux软件版本管理命令update-alternatives使用详解
linux 文件夹重命名_如何在Linux中重命名目录和文件夹?
realpath命令
浙公网安备 33010602011771号