Stephen G. Kochan Patrick Wood著
UNIX系统只识别三种基本类型文件:普通文件(系统中包含数据、文本、程序指令或其它内容的文件)、目录文件和特殊文件(对UNIX有特殊意义,通常和某种形式的I/O相关联)
文件名字符数不能超过255个
echo *
#部分替换,限制匹配
echo chap*
匹配单个字符
echo a?
# 列出以小写字母开头且不易数字结尾的所有文件
ls [a-z]*[!0-9]
不在chars中的任意单个字符
两种解决方法:
cat "my test document"
cat my\ test\ document
Ctrl+d
终结标准输入# 内容覆盖
who > users
# 追加形式
echo line 2 >> users
wc -l < users
I/O重定向
写法 | 含义 |
---|---|
< file | 标准输入重定向到file |
> file | 标准输出重定向到file |
>| file | 标准输出重定向到file,file内容清空 |
>> file | 追加 |
<< word | 标准输入重定向到随后的行中,知道某行只包含了word |
<& digit | 标准输入重定向到与文件描述符digit相关联的文件 |
>& digit | 标准输出重定向到与文件描述符digit相关联的文件 |
<&- | 关闭标准输入 |
>&- | 关闭标准输出 |
<> file | 打开file进行读取和写入 |
$ cat <<\FOOBAR
> \\\
> `date`
> $HOME
> FOOBAR
\\\
`date`
$HOME
默认同终端或终端程序相关联
即使标准输出被重定向到文件中,错误小心仍然显示到终端
command 2> file
将标准错误重定向到文件中
另一种写法用于确保所有的错误信息都出现在终端,哪怕监本已经将其输出重定向了文件或管道:echo "Error message" 1>&2
(将应该输出到文件描述符#1的错误信息重定向到文件描述符#2)
过滤器:从标准输入读取数据,将结果写入标准输出
# wc为过滤器,cat/sort都可以作为过滤器
# who/date/cd/pwd/echo/rm/mv/cp都不算
who | wc -l
只要系统允许用户登录,UNIX系统(init
程序)就会在每个终端端口自动启动一个getty程序,getty是一个设备驱动程序,能够让login
程序在其所分配的终端上显示login:
等待用户输入
login
比对/etc/passwd
中的条目验证登录名密码,/etc/passwd
中有验证成功后要启动的程序(每行最后一个冒号后面),没有就默认使用标准Shell,即/bin/sh
。
登录Shell会在系统中查找并读取两个特殊文件:/etc/profile
会检查新邮件、默认的文件创建掩码、建立默认PATH及其它管理员希望登录完成的工作;.profile
位于用户主目录下,其对环境做出的修改会一直持续到登出Shell
Shell的职责:解释型编程语言、程序执行、变量及文件名替换、I/O重定向、管道、环境控制
Shell首先进行变量和文件名替换,查找文件名替换字符*、?或[…]
Shell在磁盘搜索命令之前,会先判断是否为内建命令(cd/pwd/echo)
I/O重定向:wc
程序在wc -l < users
中只接收了-l
一个参数,他会转而去统计标准输入的内容,Shell会将其的标准输入重定向为文件users
管道:将上一个命令的标准输出连接到下一个命令的标准输入,然后执行两者
环境控制:能够定制个人环境
解释型:Shell有自己的内建语言,这种语言是解释型的,Shell会分析所遇到的每一条语句,然后执行所发现的有效的命令
Shell先进行管道和I/O重定向,然后是变量替换、命令替换,再是文件名替换,接着将命令行解析成参数,Shell会从命令行中删除空格、制表符和换行符(空白字符),然后切分成参数交给所请求的命令
Shell的命令搜索次序
记法 | 含义 | 例子 |
---|---|---|
. | 匹配任意字符(Shell 则认为? 能匹配任意单个字符) |
a… |
^ | 匹配行首 | ^wood |
$ | 匹配行尾 | x$ |
* | 重复之前的正则表达式零次或多次 | xx* (一个或多个连续x) |
+ | 重复之前的正则表达式一次或多次 | xx+ (两个或多个连续x) |
[chars] | 匹配chars中的任意字符 | [a-z] (小写字母) |
[^chars] | 匹配不在chars中的任意字符 | [^0-9] (非数字字符) |
\{min, max\} | 重复之前的正则表达式至少min次,至多max次 | [0-9]\{3, 9\} (连续3-9个数字) |
\(…\) | 将括号中保存下来的字符保存到接下来的寄存器中(1-9) | ^\(.\)\1 (行首前两个相同的字符) |
字符 | 八进制值 |
---|---|
响铃Bell | 7 |
退格Backspace | 10 |
制表符Tab | 11 |
回车换行Newline | 12 |
换行Linefeed | 12 |
换页Formfeed | 14 |
回车Carriage Return | 15 |
取消Escape | 33 |
一行中输入多个命令,使用;
分隔
向后台发送命令:
# 返回[job number] PID
$ sort bigdata > out &
[1] 1258
chmod +x
使文件具有可执行权限
没有参数的echo
会产生一个空行
不想看到命令结果,可以将输出重定向到系统的垃圾桶:/dev/null
,任何写入这个文件的东西都会消失!
要强制写入或从终端读取,可以利用/dev/tty
,该文件总是指向终端程序
调试:sh -x
跟踪执行过程,这启动了一个-x选项的新shell执行指定程序,命令在执行的同时打印到终端,前面加了一个+。
Shell的两个特殊操作符:&&
和||
,根据之前命令成功与否来执行后续的命令
如果文件中的第一行的前两个字符是#!,那么该行余下的部分指定了该文件的解释器:#!/bin/bash
显示日期和时间
$ date
Thu Dec 27 19:31:25 CST 2018
获取当前已登录到系统中所有用户信息
用户ID、用户所在的tty编号(UNIX系统为用户所在终端或网络设备分配的唯一标识数字)、登录时间
$ who
root pts/0 2018-12-27 19:41 (10.131.253.92)
[root@CENTOS-MINION-2 ~]# who am i
root pts/0 2018-12-27 19:41 (10.131.253.92)
$ echo one two three
one two three
\c
不输出换行符(\c
是由echo解释的,而非Shell,必须将其引用起来交给echo),有的系统不会显示这些echo转义字符echo命令的转义字符
字符 | 输出 |
---|---|
\b | 退格 |
\c | 忽略输出中最后的换行符 |
\f | 换页 |
\n | 回车换行 |
\r | 回车 |
\t | 制表符 |
\\ | 反斜线 |
\0nnn | ASCII值为nnn的字符,nnn是1-3位的八进制数 |
列举文件;
-C
强制以多列形式输出
-l
输出文件详细信息:
$ ls
anaconda-ks.cfg
apache-maven-3.5.2-bin.tar.gz
docker-ce-17.03.2.ce-1.el7.centos.x86_64.rpm
docker-ce-selinux-17.03.2.ce-1.el7.centos.noarch.rpm
get-pip.py
go1.9.2.linux-amd64.tar.gz
jdk-8u131-linux-x64.tar.gz
nohup.out
openscap_data
privoxy-3.0.26-stable
privoxy-3.0.26-stable-src.tar.gz
$ls -l
total 314124
-rw-------. 1 root root 1089 Aug 10 2016 anaconda-ks.cfg
-rw-r--r--. 1 root root 8738691 Apr 12 2018 apache-maven-3.5.2-bin.tar.gz
-rw-r--r--. 1 root root 19529520 Apr 12 2018 docker-ce-17.03.2.ce-1.el7.cento s.x86_64.rpm
-rw-r--r--. 1 root root 29108 Apr 12 2018 docker-ce-selinux-17.03.2.ce-1.e l7.centos.noarch.rpm
-rw-r--r--. 1 root root 1780465 Apr 13 2018 get-pip.py
-rw-r--r--. 1 root root 104247844 Apr 12 2018 go1.9.2.linux-amd64.tar.gz
-rw-r--r--. 1 root root 185540433 Apr 12 2018 jdk-8u131-linux-x64.tar.gz
-rw-------. 1 root root 28908 Nov 30 11:01 nohup.out
drwxr-xr-x. 2 root root 39 Aug 10 2016 openscap_data
drwxr-xr-x. 10 root root 4096 Apr 13 2018 privoxy-3.0.26-stable
-rw-r--r--. 1 root root 1741772 Aug 27 2016 privoxy-3.0.26-stable-src.tar.gz
(concatenate)显示文件内容
$cat dingtest
if (( i == 100 ))
then
echo "$i == 100"
else
echo "$i != 100"
fi
如果没有指定参数,$*为空,cat也接受不到任何参数,这会使它从标准输入中读取
cat $* |
while read line
do
echo "$line"
done
统计行数、单词数、字符数;命令选项必须出现在文件名参数之前
$wc dingtest
6 17 69 dingtest
$wc -l dingtest
6 dingtest
$wc -w dingtest
17 dingtest
$wc -c dingtest
69 dingtest
复制文件,saved_names
如果存在会被覆盖
$cp names saved_names
# 将文件collect从目录../programs复制到当前目录中
$cp ../programs/collect .
文件重命名,hold_it
如果存在会被覆盖;
在目录间移动文件
$ mv saved_names hold_it
#将三个文件移动到../misc目录中
$ mv wb collect mon ../misc
删除文件
$ rm hold_it
-f
忽略不存在的文件,强制删除,不给出提示。
-r
指示rm将参数中列出的全部目录和子目录均递归地删除。
-i
进行交互式删除。因为一旦文件被删除,它是不能被恢复的。为了防止这种情况的发生,可以使用“i”选项来逐个确认要删除的文件。如果用户输入“y”,文件将被删除。
rm -r
删除指定目录和其中的所有文件(如果没有使用- r
选项,则rm不会删除目录。 )
rm -rf *
删除当前目录下的所有文件
显示工作目录
$pwd
/ding
更改目录,参数-
表示上一个目录,无参数表示回到HOME目录
创建目录
删除目录,要先删除目录中包含的所有文件,否则会报错;
文件链接,一个文件有连个不同的名字,不会占用两倍磁盘空间;
对于普通链接,被链接的文件必须与链接文件处于同一个文件系统中,否则会报错;
可以随意删除两个连接文件中的任何一个,另外一个不会因此消失
$ ln wb writeback
$ ls -l
...
-rwxr-xr-x 2 steve DP3822 89 JUL 20 15:22 wb
-rwxr-xr-x 2 steve DP3822 89 JUL 20 15:22 writeback
要在不同文件系统的文件之间创建链接,使用-s选项,这叫做符号链接(ls -l文件类型为l),指向原始文件;
例子中文件大小为15,内容其实就是字符串/users/steve/wb;
如果原始文件被删除,符号链接无效,但符号链接本身不会删除->悬挂符号链接;
$ ln -s /users/steve/wb ./symwb
$ ls -l
...
lrwxr-xr-x 1 pat DP3822 15 JUL 20 15:22 symb -> /users/steve/wb
$ ls -Ll
...
-rwxr-xr-x 2 steve DP3725 15 JUL 20 13:30 wb
为特定目标目录中的多个文件创建链接:
ln <files> <directory>
给出系统中所运行进程的信息:PID、TTY、进程所使用的计算机时间、进程名称;
-f
打印更多信息:PPID(父进程ID)、进程开始时间$ ps
PID TTY TIME CMD
4581 pts/0 00:00:00 bash
9255 pts/0 00:00:00 ps
$ ps -f
UID PID PPID C STIME TTY TIME CMD
root 4581 3875 0 19:56 pts/0 00:00:00 -bash
root 10717 4581 0 21:38 pts/0 00:00:00 ps -f
从每行内提取字段:cut -cchars file
-d
指定字段分隔符;-f
指定待提取的字段# 提取前8个字符
$ who | cut -c1-8
# 提取前8个字符,然后提取第18个到行尾的字符
$ who | cut -c1-8,18-
# 从/etc/passwd文件中提取每行的由:分割的第1和6字段
$ cut -d: -f1,6 /etc/passwd
将文件中每行合并在一起,由制表符分隔
-d
指定分隔符;-s
将同一个文件中的所有行合并到一起,默认制表符分隔$ paste names numbers
Tony 123456
Emanuel 55555
# 粘贴ls命令的输出(paste将来自标准输入`-`的所有行进行合并),使用空格作为分隔符
$ ls | paste -d' ' -s -
过滤器,转换标准输入中的字符;从标准输入获得输入,结果写入标准输出,不改动原始文件
-s
压缩多次连续出现的字符;-d
删除输入流中的个别字符# 将:换成制表符,11为制表符的八进制值
tr : '\11'
# 小写换成大写,加引号避免Shell将其解释为模式
tr '[a-z]' '[A-Z]' < intro
# 大写转小写,小写转大写
tr '[a-zA-Z]' '[A-Za-z]'
# A-M转成N-Z,N-Z转成A-M
tr '[A-Z]' '[N-ZA-M]'
# 所有:转成制表符,使用单个制表符替代多个制表符
tr -s ':' '\11'
# 删除换页符
tr -d '\14'
在一个或多个文件中搜索指定的模式
-i
忽略大小写;-v
获得不匹配的行;-l
得到匹配指定模式的行所在的文件;-n
显示行号# 在当前目录的所有文件中搜索单词shell
grep shell *
# *放在单引号中避免Shell误解
grep '*' stars
字母顺序排序,特殊字符按内部编码排序,空格32,双引号34
-u
消除重复行-r
逆序排列-o
指定输出文件-n
将行中第一个字段视为数字,对应的数据进行算数排序sort -k2
从第2个字段开始排序-t
后的字符被视为分隔符,sort -k3n -t: /etc/passwd
对第3个以冒号分隔的字段进行算数排序uniq in_file (out_file)
查找或删除重复行(连续出现的重复行,所以一般需要先sort才能被判断为重复),可做过滤器
-d
只把重复的行写入输出-c
统计出现的次数sort names | uniq
sort names |uniq -d
# 找出数据文件中词频最高的单词
tr '[A-Z]' '[a-z]' datafile | sort | uniq -c | head
数学等式解算器,操作数和操作符之间必须用空格分隔!!!
:
操作符,针对出现在第二个操作数中的正则表达式匹配第一个操作数中的字符,默认返回匹配到的字符数$ expr $i + 1
1
$ expr "$file" : ".*"
0
向左移动位置参数,同时,$#
的值也会自动减1;
如果$#
已经为0的情况下使用shift
,会发出错误信息:prog: shift: bad number
(prog
是执行不当的shift命令的程序名)
shift 3
:一次移动3个位置
测试单个或多个条件
test命令的所有操作数和操作符必须是独立的,用空白字符分隔的!
将test的参数放在双引号中是一种良好的编程实践
# test命令的所有操作数和操作符必须是独立的,用空白字符分隔的
4 test "$name" = julio
$ test 1 = 1
$ echo $?
0
# =的优先级高于-z,所以test会将命令作为等量关系测试来处理,希望在=之后还有一个参数,所以报了错
$ symbol==
$ test -z "$symbol"
$ echo $?
1
# 下面的写法可以防止这种情况发生
$ test X"$symbol" = X
test的另一种格式:[ express ]
$ [ -z "" ]
$ echo $?
0
test字符串操作符
操作符 | 如果满足下列条件,则退出码为0 |
---|---|
string1 = string2 | string1等于string2 |
string1 != string2 | string1不等于string2 |
string | string不为空 |
-n string | string不为空 |
-z string | string为空 |
操作符 | 如果满足下列条件,则退出码为0 |
---|---|
int1 -eq int2 | int1等于int2 |
int1 -ge int2 | int1大于等于int2 |
int1 -gt int2 | int1大于int2 |
int1 -le int2 | int1小于等于int2 |
int1 -lt int2 | int1小于int2 |
int1 -ne int2 | int1不等于int2 |
在使用整数操作符时,将变量的值视为整数的是test命令,而不是Shell
操作符 | 如果满足下列条件,则退出码为0 |
---|---|
-d file | file是一个目录 |
-e file | file存在 |
-f file | file是一个普通文件 |
-r file | 可由进程读取 |
-s file | 不是空文件 |
-w file | 可由进程写入 |
-x file | 可执行 |
-L file | 是一个符号链接 |
逻辑否定操作符!
:[ ! -r /users/steve/phonebook ]
逻辑“与”操作符-a
:[ ! -f "$file" -a $(who > $file) ]
,(如果-f
测试没有通过,就不会执行who
命令)
逻辑“或”操作符-o
:优先级比-a
低
括号:改变求值顺序,括号两边必须有空格,且要将括号本身引用起来,因为test要求条件语句的每一个元素都是独立的参数,且括号对Shell有特殊含义(新开一个子Shell)
[ \( "$count" -ge 0 \) -a \( "$count" -lt 10 \) ]
-exit n
:返回退出码n
:
什么都不干,可以用在谋改革判断分支中,因为每个匹配的语句分支都需要对应的命令
sleep n
:挂起程序执行n秒
希望某个程序在登出后继续运行,使用nohup
命名运行该程序
退出循环,break n
退出第n层内循环
for file
do
...
while [ "$count" -lt 10 ]
do
...
if [ -n "$error" ]
then
# 当error不为空,退出while和for循环
break 2
fi
done
..
done
跳过当前迭代中剩下的命令,continue n
跳过最内侧的n个循环中的命令,继续往下执行
内建命令,专门用来在循环中执行处理命令行参数
getopts options variable
options中单个字符表示选项,如果选项后还需要参数,字符后要加冒号。mt:
表示程序支持-m``-t
选项,其中-t
还需要一个参数
如果减号后的字符没有在options中列出,会在返回为0的退出状态之前将问号保存在variable中,向标准错误写入错误信息,告诉用户指定的选项有问题
如果getopts没有在选项后找到要求的参数,会将问号保存到变量中并向标准错误输出错误信息。否则,就将选项字符保存在变量中,把用户指定的参数放在一个叫做OPTARG
的特殊变量中
特殊变量OPTIND
初始值为1,随后每当getopts返回时都会被更新为下一个要处理的命令行参数的序号
while getopts mt: option
do
case "$option" in
m) mailopt=TRUE;;
t) interval=$OPTARG;;
\?) exit 1;;
esac
done
read variables
从标准输入中读取一行,将第一个单词分配给variables中第一个变量,将第二个单词分配给第二个变量,以此类推。
碰到文件结尾,或用户键入ctrl+d
,read
会返回为0的退出状态码
cat $* |
while read line
do
echo "$line"
done
read -r
避免解释反斜线。(可以修改变量IFS保留前导空格)剥离参数的所有目录部分,得到其基础文件名
格式化输出信息,格式化字符串中的每个百分号(格式规范)都有一个相应的参数,除了特殊规范%%,他会显示一个百分号。
不会在尾部加一个换行符,能够理解转义序列
printf的格式规范字符
字符 | 功能 |
---|---|
%d | 整数 |
%u | 无符号整数 |
%o | 八进制整数 |
%x | 十六进制整数,使用a-f |
%X | 十六进制整数,使用A-F |
%c | 单个字符 |
%s | 字符串字面量 |
%b | 包含转义字符的字符串 |
%% | 百分号 |
%s不处理转义字符,%b强制解释转义字符
%[flags] [width] [.precision] type
$ printf "%+d\n%+d\n" 10 -10
+10
-10
$ printf "%-15.15s\n" "this is more than 15 chars long."
this is more th
$ printf "%*s%*.*s\n" 12 "test one" 10 2 "test two"
test one te
printf的格式规范修饰符
修饰符 | 含义 |
---|---|
Flags | |
- | 左对齐值 |
+ | 在整数前加上+或- |
(space) | 在正数前加上空格,负数前面加上-,对齐 |
# | 在八进制数前加上0,在十六进制数前加上0x或0X |
width | 字段最小宽度;*表示使用下一个参数作为宽度 |
precision | 显示整数时使用的最小位数,值左侧用0填充;显示字符串时使用的最大字符数;*表示使用下一个参数作为精度 |
导出变量,只要子Shell已启动,导出的变量都会被复制到子Shell,而局部变量不会
export -p
得到列表,包含了导出的变量和值,这些是在用户登录后由登录Shell所导出的$ export -p
declare -x CLASSPATH=".:/usr/local/jdk1.8.0_131//jre/lib/rt.jar:/usr/local/jdk1.8.0_131//lib/dt.jar:/usr/local/jdk1.8.0_131//lib/tools.jar"
declare -x HISTCONTROL="ignoredups"
declare -x HISTSIZE="1000"
declare -x HOME="/root"
declare -x HOSTNAME="CENTOS-MINION-2"
declare -x JAVA_HOME="/usr/local/jdk1.8.0_131/"
...
. file
:在当前Shell中执行file的内容
exec program
:使用新程序替换现有的程序,不会有进程处于挂起状态,由于UNIX系统执行进程的方式,exec所替换的程序启动时间也更快
还可以用来关闭标准输入,然后使用其他你想要读取的文件重新打开它
exec < infile
exec > report
#将标准输入重新分配回终端
exec < /dev/tty
$ set one two three
$ echo $1:$2:$3
one:two:three
set -x
打开跟踪模式;set +x
关闭跟踪模式;
无参数的set
会输出一个按照字母顺序排序的变量列表,是存在于当前环境中的局部变量或导出变量
--
:告诉set对于后续出现的连接符或参数形式的单词,均不视为其选项;是考虑到可能会出现以-起始的行、全部是空白字符或者空行
set -o mode
:启动行编辑模式,mode可以是vi或emacs
指定在程序随后的执行过程中,值都不会发生改变的那些变量,如:readonly PATH HOME
,如果之后再视图给这两个变量赋值,会输出错误信息
变量的只读属性不会传给子Shell
只要将变量设为只读,就没有“后悔药”
从环境中删除谋某个变量;不能对只读变量、IFS/MAILCHECK/PATH/PS1/PS2使用unset,
$ x=100
$ echo $x
100
$ unset x
$ echo $x
$
-f
:删除函数eval args
:使得Shell对args求值,然后执行求值结果;常用于从变量中构造命令行,实际上可以实现对命令行的二次扫描;$ cat last
eval echo \$$#
$ last one two three four
four
#得到最后一个文件
$ last *
zoo_report
$ x=100
$ ptrx=x
$ eval echo \$$ptrx
100
$ eval $ptrx=50
$ echo $x
50
wait job
:暂停执行,直到job所标识的进程运行结束,job可以是进程ID或作业ID。如果没有提供job,Shell会等待所有子进程结束trap commands signals
:告诉Shell只要接收到signals中列出的信号,就执行commands(如果不止一个命令,要全部放入引号中),信号以名称或编号形式指定#exit是必须的,否则程序会一直停留在接收到信号时的执行位置上
trap "rm $WORKDIR/work$$ $WORKDIR/dataout$$; exit" INT
信号编号与名称
信号# | 信号名称 | 产生原因 |
---|---|---|
0 | EXIT | 退出Shell |
1 | HUP | 挂起 |
2 | INT | 中断(如按下DELETE键或ctrl+c) |
3 | QUIT | 退出 |
6 | ABRP | 中止 |
9 | KILL | 销毁进程 |
14 | ALRM | 超时 |
15 | TERM | 软件终止信号(默认由kill发送) |
不使用参数,trap会打印出当前的信号处理方式
如果第一个参数是空串:trap "" signals
,当Shell接收到signals指定的信号时,会将其忽略
如果忽略了某个信号,子Shell也会忽略;如果指定了某个信号的处理程序,子Shell自动采用默认处理方式,而不是指定的处理程序
使用trap signals
,会将signals中列出的信号处理方式恢复成默认行为
Shell在执行trap时扫描命令行,在接收到信号列表中的信号时还会再扫描一遍:
trap "echo $count lines processed >> $LOGFILE; exit" HUP INT TERM
#为了防止count被提前替换,将命令放入单引号中
trap 'echo $count lines processed >> $LOGFILE; exit' HUP INT TERM
return n
:n为函数的返回状态,可以用变量$?访问它
接受一个或多个命令作为参数,告诉你这些命令是什么类型
$ type echo
echo is a shell builtin
$ type ls
ls is aliased to `ls --color=auto'
$ type cat
cat is /usr/bin/cat
$ type nu
nu is a function
访问命令历史记录
$ history
....
$ history 5
885 type echo
886 type ls
887 type cat
888 history
889 history 5
为历史记录中的若干命令启动一个编辑器或将历史命令列表写入终端。后一种,使用-l
指定命令列表
-n
忽略命令编号
fc -s old=new first
执行选中的命令,无需事先编辑
fc -l 50-53
#最近20条命令写入到标准输出
fc -n -l -20
#将最近的sed命令送入vi中编辑
fc -e vi sed
#将编号104的命令中的abc替换成def,然后重新执行
fc -s abc=def 104
重新显示上一条命令并立即执行
#运行上一条cat命令
$ r cat
cat doc/planA
#将cat命令中第一次出现的planA替换成planB
$ r cat planA=planB
-i
:声明整数类型$ typeset -i i
$ i=hello
ksh: i: bad number
alias name=string
:自定义命令
alias ll='ls -l'
unalias name
:删除别名
unalias -a
:删除所有别名
打印出尚未完成的作业状态
终止后台作业
ctrl+z挂起当前运行的作业,fg在前台恢复执行当前作业
在后台恢复执行当前作业
if command-t1
then
command
command
fi
# else
if command-t1
then
command
...
else
command
...
fi
# elif-else
if command-t1
then
command
...
elif command-t2
then
command
...
else
command
...
fi
command-tx
命令的退出状态会被测试,如果为0,则执行then和fi之间的命令case value in
pattern1) command
...
command;;
pattern2) command
...
command;;
...
patternn) command
...
command;;
esac
;;
类似于break
模式匹配:与文件名替换相同,?
任意单个字符、*
零个或多个任意字符、[...]
中括号中出现的任意单个字符,符号|
用于两个模式之间,效果等同于逻辑“或”
for var in word1 word2 ... wordn
do
command
...
done
for var in "$@"
一样:for var
do
command
...
done
&
将循环放入后台,甚至是作为命令管道的一部分# 放入后台
for file in memo[1-4]
do
run $file
done &
# 重定向
for i in 1 2 3 4
do
echo $i
done > loopout
# 数据导出
for i in 1 2 3 4
do
echo $i
done | wc -l
列表最后一项后面加分号,循环中每个命令后面加分号,do后面不加
for i in 1 2 3 4; do echo $i; done
while command-t
do
command
...
done
until command-t
do
command
...
done
只要command-t返回码不为0,就会不停执行代码块
内建整数算术操作,算术扩展:$((express))
,(express两边和括号之间不必有空格),出现在算术扩展中的有效元素只有操作符、数字和变量,未定义的变量或空串值被视为0
$ i=1
$ j=$((i+1))
$ echo $j
2
express中可以包含常量、Shell变量(不需要美元符号)及操作符,按照优先级从高到低的次序,操作符如下:
符号 | 含义 |
---|---|
- | 减号 |
~ | 按位取反 |
! | 逻辑反 |
* / % | 乘、除、求余 |
+ - | 加、减 |
<< >> | 左移、右移 |
<= >= < > | 比较 |
== != | 等于、不等于 |
& | 按位与 |
^ | 按位异或 |
| | 按位或 |
&& | 逻辑与 |
|| | 逻辑或 |
expr1 ? expr2 : expr3 | 条件运算符 |
= *= /= %= | 赋值 |
+= <<= >>= | 赋值 |
&= ^= |= | 赋值 |
$ x=10
$ ((x = x * 12))
$ echo $x
120
if ((i == 100))
then
...
fi
不同基数的数字:base#number
,尽管被设置为按照八进制显示,但分配给该变量的值默认基数仍然是十进制,除非另行指定
$ typeset -i i=8#100
$ echo $i
8#100
$ i=50
$ echo $i
8#62
name () { command; ... command; }
如果函数体和花括号出现在同一行,{和第一条命令之间必须至少有一个空白字符,最后一条命令和}之间必须有一个分号
函数仅存在于所定义它的Shell中,无法传给子Shell,因为函数是在当前Shell中执行的
在函数内部使用exit,不仅会终止函数的执行,还会使调用该函数的Shell程序退出;如果只想退出函数,可以使用return n
命令,n为函数的返回状态
可以将函数写进ENV文件中,启动新Shell就能直接使用
bash和Korn Shell可以只用局部变量:typeset j
Korn Shell在变量FPATH
中搜索匹配函数名的文件
$ typeset -i array
$ array[0]=100
$ array[1]=50
$ (( array[2] = array[0] + array[1] ))
$ echo ${array[2]}
150
$ i=1
$ echo ${array[i]}
50
$ array[3]=array[0]+array[2]
$ echo ${array[3]}
250
Shell能够识别4中不同的引用字符,单引号、双引号、反斜线和反引号
出现在单引号中的特殊字符,Shell会将其全部忽略
传参时,引号会被Shell删除,并不会传入程序中;
$ t='* 233'
# 执行变量替换后,引号没有了,会执行变量名替换,sad!!!
$ echo $t
dingtest dingtest26787 petalk 233
$ t='* 233'
# 执行变量替换后,引号没有了,会执行变量名替换,sad!!!
$ echo $t
dingtest dingtest26787 petalk 233
# 加了双引号就好了
$ echo "$t"
* 233
作为前缀使用,在功能上相当于单个字符周围放置单引号
作为输入行的最后一个字符,Shell将其视为续行符
双引号中使用反斜线去除特殊字符(反斜线、美元符号、反引号、换行符、双引号)的含义(删除反斜线),如果后面没有特殊字符,Shell忽略该反斜线(不删除反斜线),继续往后处理
$ echo The date is `date`
The date is Sun Dec 30 21:49:19 CST 2018
$ echo $(who | wc -l)
1
变量名以字母或下划线开头,后面跟上零个或多个字母数字或下划线
count=1
等号两边不能有空格
Shell没有数据结构的概念,都是字符串
对变量赋值时,Shell不执行文件名替换
没有值的变量叫做未定义变量,值为空(null);Shell执行变量替换时,为空的值会被从命令行删除
${variable}
结构,在变量名最后一个字符后面跟着的是字母及数字字符或下划线时使用,使得变量可以正常替换
即便是执行自己的Shell程序,Shell也会进行正常的命令行处理,这意味着在指定程序参数时可以利用这些处理步骤,进行文件名替换和变量替换。
但是,参数传递过程中会丢失引号,可以在自己的Shell程序中视情况给变量引用加上双引号
$1
表示使用程序的第一个参数进行替换${10}
参数 | 含义 |
---|---|
$# | 传递给程序的参数数量或者由set语句设置的参数数量 |
$* | 引用所有的位置参数$1、$2… |
$@ | “$@“以”$1”…的形式引用所有的位置参数 |
$0 | 所执行的程序名 |
$$ | 所执行程序的进程ID |
$! | 送入后台执行的最近一个程序的进程ID |
$? | 最近执行的非后台命令的退出状态码 |
$- | 生效的当前选项标志(参见set语句) |
变量$#
变量$*
变量$@
for arg in $*
中传进来的参数会丢失引号,可以使用"$@"
代替,那么传入程序的会是"$1"、"$2"...
,关键在于$@
两边的引号,如果没有了引号,该变量效果$*
无异for arg in "$@"
do
echo $arg
done
参数 | 含义 |
---|---|
p a r a 或 para 或 para或{para} | 替换成para的值 |
${para:-value} | 若para已设置且不为空,使用它的值;否则,使用value |
${para-value} | 若para已设置(可为空),替换为它的值,否则,替换为value |
${para:=value} | 若para已设置且不为空,使用它的值;否则,替换为value并将其赋给para |
${para=value} | 若para已设置(可为空),使用它的值,否则,替换为value,并将其赋给para |
${para:?value} | 若para已设置且不为空,使用它的值,否则,将value写入标准错误,然后退出。 |
${para?value} | 若para已设置(可为空),替换为它的值;否则,将value写入标准错误并推出。 |
${para:+value} | 若para已设置且不为空,替换成value;否则,替换为空。与${para:-value}效果相反 |
${para+value} | 若para已设置(可为空),替换成value;否则,替换为空。 |
${#para} | 替换为para的长度 |
${para#pattern} | 从para左边开始删除pattern的最短匹配,余下内容作为参数替换的结果,不会修改变量的值。 |
${para##pattern} | 从para左边开始删除pattern的最长匹配 |
${para%pattern} | 从para右边开始删除pattern的最短匹配 |
${para%%pattern} | 从para右边开始删除pattern的最长匹配 |
: ${PHONEBOOK:=$HOME/phonebook}
p a r a : ? v a l u e 和 {para:?value}和 para:?value和{para?value}:如果忽略value,则向标准错误写入parameter: parameter null or not set
模式匹配:pattern中可以使用Shell文件名替换字符(*、?、[…]、!和@)
$ var=testcase
$ echo ${var%e}
testcas
#变量的值不变
$ echo $var
testcase
$ echo ${var%s*e}
testca
$ echo ${var%%s*e}
te
$ echo ${var#?e}
stcase
$ echo ${var#*s}
tcase
$ echo ${var##*s}
e
#没有匹配会输出整个变量名
$ echo ${var#teas}
testcase
除了Shell内建命令之外的其他命令通常都是在一个全新的Shell实例中执行的,我们称其为子Shell
子Shell执行完毕时,它所创建的变量无法再被访问
局部变量与导出变量
可以通过将赋值语句放在命令名之前,将Shell变量添加到该命令的环境中
PHONEBOOK=$HOME/misc/phone rolo
#等同于
(PHONEBOOK=$HOME/misc/phone; export PHONEBOOK; rolo)
变量名 | 含义 |
---|---|
PS1 | 命令行提示符的字符序列 |
PS2 | 辅命令行提示符 |
HOME | 主目录,用户登录后所处的位置 |
PATH | 搜索用户命令的目录列表,以冒号分隔 |
PWD | 当前目录 |
OLDPWD | 前一个当前目录的完整路径 |
CDPATH | cd命令没有给出目录的完整路径时在该列表中搜索 |
TERM | 终端类型,ansi/vt100/xterm等等 |
TZ | 决定当前时区 |
IFS | 内部字段分隔符 |
ENV | 当以交互方式启动的时候,Shell会在当前环境下所执行的文件名 |
HISTSIZE | 可保存的闲钱输入过的命令数量 |
HISTFILE | 保存命令历史的文件 |
FPATH | Korn Shell在变量FPATH 中搜索匹配函数名的文件 |
一旦登出,对PS1等做出的改变会失效,除非将修改保存到.profile文件中
HOME:主目录,用户登录后所处的位置,不带参数的cd命令将该目录作为默认目的地
PATH:不将当前目录放在最先搜索的位置,避免特洛伊木马攻击
echo $PATH
/bin:/usr/bin:.
CDPATH:不会在登录时自动设置好(echo $CDPATH
为空),需要明确将其设置为一系列目录
TZ:最简单的TZ的设置由时区名(长度至少3个字符)和一个数字(指定了小时数),现代Linux也可以指定地理区域来指定时区
IFS:当Shell解析read命令输入、命令替换输出以及执行变量替换式,会用到;在执行变量赋值时并不使用
命令分组
(...)
:小括号中放入一条或多条命令,这些命令会在子Shell中执行
{...;}
:命令在当前Shell执行,如果花括号中的命令全写在同一行,左花括号后一定要有一个空格,分号必须出现在最后一个命令的末尾
{ echo ".ls 2"; cat memo; } | nroff -Tlp | lp
#三个程序被放入后台,标准错误重定向到errors文件
(prog1; prog2; prog3) 2>errors &
旧式行编辑器,交互式
# 将所有的p.o(.匹配任意单个字符)修改成XXX
# 1,$(范围限定符,表示从文章的第一行到最后一行)指明在全文范围内应用替换操作,替换操作格式为s/old/new/g
# s表明是替换操作,斜线用来界定替换内容和被替换内容,g表明执行全局替换,而不仅仅是替换某一行
1,$s/p.o/XXX/g
#打印全文,查看修改结果
1,$p
流编辑器,非交互式,不会修改原始输入文件,结果写入标准输出
-n
默认不打印任何行,与p命令搭配使用,打印出符合指定范围或模式的所有行
d命令删除文本行
# 将intro文件中的Unix替换成UNIX
# 单引号避免Shell解释其中的反斜线
sed 's/Unix/UNIX/' intro
# 只打印前两行
sed -n '1,2p' intro
# 删除行1和2
sed '1,2d' intro
# 将包含jan的所有行中的第一个-1改成-5
sed '/jan/s/-1/-5/'
# 在制表符的位置显示\t不可打印字符显示为\nn(nn为字符的八进制值)
sed -n l <file>
行编辑器,set -o vi
开启
基本的vi行编辑命令
命令 | 含义 |
---|---|
h | 向左移动一个字符 |
l | 向右移动一个字符 |
b | 向左移动一个单词 |
w | 向右移动一个单词 |
0 | 移动到行首 |
$ | 移动到行尾 |
x | 删除光标所在的字符 |
dw | 删除贯标所在的单词 |
rc | 将光标所在的字符修改成c |
a | 进入输入模式并在当前字符之后输入文本 |
i | 进入输入模式并在当前字符之前输入文本 |
k | 获得历史记录中上一条命令 |
j | 获得历史记录中下一条命令 |
/string | 搜索历史记录中包含string的最近一条命令,如果string没指定,使用先前的搜索内容 |
行编辑器,set -o emacs
开启
基本的emacs行编辑命令
命令 | 含义 |
---|---|
ctrl+b | 向左移动一个字符 |
ctrl+f | 向右移动一个字符 |
esc f | 向前移动一个单词 |
esc b | 向后移动一个单词 |
ctrl+a | 移动到行首 |
ctrl+e | 移动到行尾 |
ctrl+d | 删除当前字符 |
esc d | 删除当前单词 |
erase char | (用户自定义#或ctrl+h),删除上一个字符 |
ctrl+p | 从历史记录中获得上一条命令 |
ctrl+n | 从历史记录中获得下一条命令 |
ctrl+r string | 搜索历史记录,查找包含string的最近一条命令 |