SHELL编程基本知识点一

SHELL编程基本知识点一

1,起始点

在每个脚本的开头都使用"#!",这意味着告诉你的系统这个文件的执行需要指定一个解

释器.#!实际上是一个 2 字节[1]的魔法数字,这是指定一个文件类型的特殊标记, 换句话说, 在

这种情况下,指的就是一个可执行的脚本.在#!之后接着是一个路径名.这个路径名指定了一个解释脚本中命令的程序,这个程序可以是 shell,程序语言或者是任意一个通用程序.这个指定的程序从头开始解释并且执行脚本中的命令(从#!行下边的一行开始),忽略注释.

如:

#!/bin/sh

#!/bin/bash

#!/usr/bin/perl

#!/usr/bin/tcl

#!/bin/sed -f

#!/usr/awk -f

2,条件表达式

条件表达式用于 [[ 复合命令以及内建命令 test 和 [ 中,用来测试文件属性,进行字符串和算术比较。表达式使用下面的单目或二进制操作构造. 如果某操作的任何 file 参数的形式是 /dev/fd/n,那么将检查文件描述符n。如果某操作的file参数是 /dev/stdin, /dev/stdout 或者 /dev/stderr 之一,将分别检查文件描述符 0,1 和 2。

[ -a FILE ]

如果 FILE 存在则为真.

[ -b FILE ]

如果 FILE 存在且是一个块特殊文件则为真.

[ -c FILE ]

如果 FILE 存在且是一个字特殊文件则为真.

[ -d FILE ]

如果 FILE 存在且是一个目录则为真.

[ -e FILE ]

如果 FILE 存在则为真.

[ -f FILE ]

如果 FILE 存在且是一个普通文件则为真.

[ -g FILE ]

如果 FILE 存在且已经设置了SGID则为真.

[ -h FILE ]

如果 FILE 存在且是一个符号连接则为真.

[ -k FILE ]

如果 FILE 存在且已经设置了粘制位则为真.

[ -p FILE ]

如果 FILE 存在且是一个名字管道(F如果O)则为真.

[ -r FILE ]

如果 FILE 存在且是可读的则为真.

[ -s FILE ]

如果 FILE 存在且大小不为0则为真.

[ -t FD ]

如果文件描述符 FD 打开且指向一个终端则为真.

[ -u FILE ]

如果 FILE 存在且设置了SUID (set user ID)则为真.

[ -w FILE ]

如果 FILE 如果 FILE 存在且是可写的则为.

[ -x FILE ]

如果 FILE 存在且是可执行的则为真.

[ -O FILE ]

如果 FILE 存在且属有效用户ID则为真.

[ -G FILE ]

如果 FILE 存在且属有效用户组则为真.

[ -L FILE ]

如果 FILE 存在且是一个符号连接则为真.

[ -N FILE ]

如果 FILE 存在 and has been mod如果ied since it was last read则为真.

[ -S FILE ]

如果 FILE 存在且是一个套接字则为真.

[ FILE1 -nt FILE2 ]

如果 FILE1 has been changed more recently than FILE2, or 如果 FILE1 exists and FILE2 does not则为真.

[ FILE1 -ot FILE2 ]

如果 FILE1 比 FILE2 要老, 或者 FILE2 存在且 FILE1 不存在则为真.

[ FILE1 -ef FILE2 ]

如果 FILE1 和 FILE2 指向相同的设备和节点号则为真.

[ -o OPTIONNAME ]

如果 shell选项 “OPTIONNAME” 开启则为真.

[ -z STRING ]

“STRING” 的长度为零则为真.

[ -n STRING ] or [ STRING ]

“STRING” 的长度为非零 non-zero则为真.

[ STRING1 == STRING2 ]

如果2个字符串相同. “=” may be used instead of “==” for strict POSIX compliance则为真.

[ STRING1 != STRING2 ]

如果字符串不相等则为真.

[ STRING1 < STRING2 ]

如果 “STRING1” sorts before “STRING2” lexicographically in the current locale则为真.

[ STRING1 > STRING2 ]

如果 “STRING1” sorts after “STRING2” lexicographically in the current locale则为真.

[ ARG1 OP ARG2 ] “OP” is one of -eq, -ne, -lt, -le, -gt or -ge. These arithmetic binary operators return true if “ARG1” is equal to, not equal to, less than, less than or equal to, greater than, or greater than or equal to “ARG2”, respectively. “ARG1” and “ARG2” are integers.

3,特殊字符

#

注释,行首以#开头为注释(#!是个例外). 注释也可以在本行空白的后边.

;

命令分隔符,可以用来在一行中来写多个命令.

;;

终止"case"选项.

.

.命令等价于 source 命令(见 Example 11-20).这是一个 bash 的内建命令.

.作为文件名的一部分.如果作为文件名的前缀的话,那么这个文件将成为隐藏文件.

.命令如果作为目录名的一部分的话,那么.表达的是当前目录.".."表示上一级目录.

.字符匹配,这是作为正则表达是的一部分,用来匹配任何的单个字符.

"

部分引用" " 可引用除$、` 、\ 、外的任意字符或字符串," "中的变量能够正常显示变量值.

'

全引用' '与" "类似,不同在于shell会忽略任何的引用值.

, 逗号链接了一系列的算术操作,虽然里边所有的内容都被运行了,但只有最后一项被

返回.

如:let "t2 = ((a = 9, 15 / 3))"

# Set "a = 9" and "t2 = 15 / 3"

\ 转义字符,如\X 等价于"X"或'X'.

/ 文件名路径分隔符.或用来做除法操作.

` 后置引用,命令替换.

:

空命令,等价于"NOP"(no op,一个什么也不干的命令).也可以被认为与 shell 的内建命令

(true)作用相同.":"命令是一个 bash 的内建命令,它的返回值为 0,就是 shell 返回的 true.

如:

:

echo $?

# 0

死循环 如:

while :

do

operation-1

operation-2

...

operation-n

done

# 与下边相同:

while true

do

...

done

在 if/then 中的占位符,如:

if condition

then : # 什么都不做,引出分支.

else

take-some-action

fi

在一个 2 元命令中提供一个占位符,具体见 Example 8-2,和"默认参数".如:

: ${username=`whoami`}

# ${username=`whoami`}

如果没有":"的话,将给出一个错误,除非"username"是个命令

使用"参数替换"来评估字符串变量.如:

: ${HOSTNAME?} ${USER?} ${MAIL?}

如果一个或多个必要的环境变量没被设置的话, 就打印错误信息.

"变量扩展/子串替换"

在和 > (重定向操作符)结合使用时,把一个文件截断到 0 长度,没有修改它的权限.

如果文件在之前并不存在,那么就创建它.如:

: > data.xxx

#文件"data.xxx"现在被清空了.

#与 cat /dev/null >data.xxx 的作用相同.然而,这不会产生一个新的进程,因为":"是一个内建命令.

在和>>重定向操作符结合使用时,将不会对想要附加的文件产生任何影响.

如果文件不存在,将创建.

注意: 这只适用于正规文件,而不是管道,符号连接,和某些特殊文件.

!

取反操作符,将反转"退出状态"结果.也会反转 test 操作符的意义.比

如修改=为!=.!操作是 Bash 的一个关键字.

在一个不同的上下文中,!也会出现在"间接变量引用".

在另一种上下文中,!还能反转 bash 的"history mechanism"

需要注意的是,在一个脚本中,"history mechanism"是被禁用的.

*

万能匹配字符,用于文件名匹配(这个东西有个专有名词叫 file globbing),或者是正则

表达式中.注意:在正则表达式匹配中的作用和在文件名匹配中的作用是不同的.

*

数学乘法.

**是幂运算.

?

测试操作.在一个确定的表达式中,用?来测试结果.

(())结构可以用来做数学计算或者是写 c 代码,那?就是 c 语言的 3 元操作符的

一个.

在"参数替换"中,?测试一个变量是否被 set 了.

?在 file globbing 中和在正则表达式中一样匹配任意的单个字符.

$

变量替换;在正则表达式中作为行结束符.

${}

参数替换

$*,$@

位置参数

$?

退出状态变量.$?保存一个命令/一个函数或者脚本本身的退出状态.

$$

进程 ID 变量.这个$$变量保存运行脚本进程 ID

()

命令组.如:

(a=hello;echo $a)

注意:在()中的命令列表,将作为一个子 shell 来运行.在()中的变量,由于是在子shell 中,所以对于脚本剩下的部分是不可用的.

用在数组初始化,如:

Array=(element1,element2,element3)

{xxx,yyy,zzz...}

大括号扩展,如:

cat {file1,file2,file3} > combined_file

# 把 file1,file2,file3 连接在一起,并且重定向到 combined_file 中.

cp file22.{txt,backup}

# 拷贝"file22.txt" 到"file22.backup"中

一个命令可能会对大括号中的以逗号分割的文件列表起作用[1]. file globbing 将对

大括号中的文件名作扩展.

注意: 在大括号中,不允许有空白,除非这个空白是有意义的.

echo {file1,file2}\ :{\ A," B",' C'}

file1 : A file1 : B file1 : C file2 : A file2 : B file2 : C

{}

代码块.又被称为内部组.事实上,这个结构创建了一个匿名的函数.但是与函数不同的

是,在其中声明的变量,对于脚本其他部分的代码来说还是可见的.

注意: 与()中的命令不同的是,{}中的代码块将不能正常地开启一个新 shell.

{} \; 路径名.一般都在 find 命令中使用.这不是一个 shell 内建命令.

注意: ";"用来结束 find 命令序列的-exec 选项.

[] test.

test 的表达式将在[]中.

值得注意的是[是 shell 内建 test 命令的一部分,并不是/usr/bin/test 中的扩展命令的一个连接.

[[]] test.

test 表达式放在[[]]中.(shell 关键字)

具体查看[[]]结构的讨论.

[ ]

数组元素

Array[1]=slot_1

echo ${Array[1]}

[ ]

字符范围

在正则表达式中使用,作为字符匹配的一个范围

(()) 数学计算的扩展

在(())结构中可以使用一些数字计算.

具体参阅((...))结构.

>&>>&>><

重定向.

scriptname >filename 重定向脚本的输出到文件中.覆盖文件原有内容.

command &>filename 重定向 stdout 和 stderr 到文件中

command >&2 重定向 command 的 stdout 到 stderr

scriptname >>filename 重定向脚本的输出到文件中.添加到文件尾端,如果没有文件,则创建这个文件.

<<

重定向,用在"here document"

<<<

重定向,用在"here string"

<,>

ASCII 比较

\<,\>

正则表达式中的单词边界.如:

bash$grep '\<the\>' textfile

|

管道.分析前边命令的输出,并将输出作为后边命令的输入.这是一种产生命令链的好方法.

>|

强制重定向(即使设置了 noclobber 选项--就是-C 选项).这将强制的覆盖一个现存文件.

||

或-逻辑操作.

&

后台运行命令.一个命令后边跟一个&,将表示在后台运行.

&& 与-逻辑操作.

-

用于重定向 stdin 或 stdout.

cd source/directory

tar cf - . | (cd ../dest/directory; tar xpvf -)

- 之前工作的目录."cd -"将回到之前的工作目录,具体请参考"$OLDPWD"环境变量.

注意:一定要和之前讨论的重定向功能分开,但是只能依赖上下文区分.

-

算术减号.

=

算术等号,有时也用来比较字符串.

+

算术加号,也用在正则表达式中.

+

选项,对于特定的命令来说使用"+"来打开特定的选项,用"-"来关闭特定的选项.

%

算术取模运算.也用在正则表达式中.

~

home 目录.

~+

当前工作目录,相当于$PWD 变量.

~-

之前的工作目录,相当于$OLDPWD 内部变量.

=~

用于正则表达式,这个操作将在正则表达式匹配部分讲解,只有 version3 才支持.

^

行首,正则表达式中表示行首."^"定位到行首.

4,控制字符

修改终端或文本显示的行为.控制字符以 CONTROL + key 组合.

控制字符在脚本中不能正常使用.

Ctl-B

光标后退,这应该依赖于 bash 输入的风格,默认是 emacs 风格的.

Ctl-C

Break,终止前台工作.

Ctl-D

从当前 shell 登出(和 exit 很像)

"EOF"(文件结束符).这也能从 stdin 中终止输入.

在 console 或者在 xterm window 中输入的时候,Ctl-D 将删除光标下字符.

当没有字符时,Ctrl-D 将退出当前会话.在 xterm window 也有关闭窗口

的效果.

Ctl-G

beep.在一些老的终端,将响铃.

Ctl-H

backspace,删除光标前边的字符.

Ctl-I

就是 tab 键.

Ctl-J

新行.

Ctl-K

垂直 tab.(垂直 tab?新颖,没听过)作用就是删除光标到行尾的字符.

Ctl-L

clear,清屏.

Ctl-M

回车

Ctl-Q

继续(等价于 XON 字符),这个继续的标准输入在一个终端里

Ctl-S

挂起(等价于 XOFF 字符),这个被挂起的 stdin 在一个终端里,用 Ctl-Q 恢复

Ctl-U

删除光标到行首的所有字符,在某些设置下,删除全行.

Ctl-V

当输入字符时,Ctl-V 允许插入控制字符.比如,下边 2 个例子是等价的

Ctl-W

删除当前光标到前边的最近一个空格之间的字符.

在某些设置下,删除到第一个非字母或数字的字符.

Ctl-V 在文本编辑器中十分有用,在 vim 中一样.

echo -e '\x0a'

echo <Ctl-V><Ctl-J>

Ctl-Z

终止前台工作.

5,命令行处理

命令行处理解释了Shell如何处理一个命令的内部机制

Shell从标准输入或脚本读取的每一行称为管道(pipeline),每一行包含一个或多个命令,这些命令用管道符隔开,Shell对每一个读取的管道都按照下面的步骤处理:

1、将命令分割成令牌(token),令牌之间以元字符分隔,Shell的元字符集合是固定不变的,包括空格、Tab键、换行字符、分号(;)、小括号、输入重定向符(<)、输出重定向符(>)、管道符(|)和&符号,令牌可以是单词(word)、关键字,也可以是I/O重定向器和分号。

2、检查命令行的第一个令牌是否为不带引号或反斜杠的关键字,如果此令牌是开放关键字,开放关键字指if、while、for或其他控制结构中的开始符号,Shell就认为此命令是复合命令,并为该复合命令进行内部设置,读取下一条命令,再次启动进程。如果此令牌不是复合命令的开始符号,如该令牌是then、else、do、fi、done等符号,这说明该令牌不应该处在命令行的首位,因此,Shell提示语法错误信息。

3、检查命令行的第一个令牌是否为某命令的别名,这需要将此令牌与别名(alia)列表逐个比较,如果匹配,说明该令牌是别名,则将该令牌替换掉,返回步骤1,否则进入步骤4。这种机制允许别名递归,也允许定义关键字别名,比如可以用下面命令定义while关键字的别名when。

alias when=while

4、执行大括号展开,比如h{a,i}t展开为hat或hit。

5、将单词开头处的波浪号(~)替换成用户的根目录$HOME。

6、将任何开头为$符号的表达式,执行变量替换。

7、将反引号内的表达式,执行命令替换。

8、将$((string))的表达式进行算术运算。

9、从变量、命令和算术替换的结果中取出命令行,再次进行单词切分,与步骤1不同的是,此时不再用元字符分隔单词,而是使用$IFS分隔单词。

10、对于*、?、[…]等符号,执行通配符展开,生成文件名。

11、将第一个单词作为命令,它可以是函数、内建命令和可执行文件。

12、在完成I/O重定向与其他类似事项后,执行命令。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏安恒网络空间安全讲武堂

PWNCTF部分复现

根据readData和writedata函数的逻辑发现数组是char [22][12],主要是判断越界的if语句有逻辑漏洞

2012
来自专栏运维一切

模拟linux内存管理代码 转

这个代码模拟实现了linux内存管理的三个算法ff、wf、bf。这三个算法都是连续分配的方式,这种方式的缺点就是内存碎片很难被再次利用。

871
来自专栏happyJared

Python中的ORM工具:SQLAlchemy

ORM全称Object Relational Mapping, 翻译过来叫对象关系映射。在Python生态中,目前较为流行的ORM模块有SQLAlchemy和p...

1782
来自专栏青玉伏案

iOS开发之SQLite-C语言接口规范(二) —— Prepared Your SQL Statements

  在《SQLite的C语言接口规范(一)》中介绍了如何去连接打开数据库,本篇博客就介绍如何操作数据库,本篇主要给出了如何执行数据库查询语句(Select), ...

2196
来自专栏SDNLAB

Open vSwitch系列之openflow版本兼容

众所周知Open vSwitch支持的openflow版本从1.0到1.5版本(当前Open vSwitch版本是2.3.2)通过阅读代码,处理openflow...

56613
来自专栏哲学驱动设计

优化OEA中的聚合SQL

    之前写过几篇关于聚合对象SQL的文章,讲的是如果设计框架,使用一句SQL语句来加载整个聚合对象树中的所有数据。相关内容,参见:《性能优化总结(二):聚合...

2437
来自专栏蓝天

Redis基于eval的多字段原子增量计算

一些应用场景需要对多个值进行原子计数,Redis的eval+hincrby可以达到目标,但如果计算的字段比较多时,效率会是个问题,它的时间复杂度为O(N),而...

802
来自专栏刘君君

Validator 使用总结

2016
来自专栏Java帮帮-微信公众号-技术文章全总结

Mybatis_day01

Mybatis_day01 前言 Jdbc演变到mybatis jdbc jdbc编程 publicstaticvoid main(String[] args)...

4237
来自专栏osc同步分享

mybatis 的一些常用功能

1. association 查询结果的一对一关联: <resultMap id="blogResult" type="Blog"> <id propert...

2868

扫码关注云+社区

领取腾讯云代金券