您的位置:首页 > 运维架构 > Shell

shell 知识点补充(1)-提示字符的设定/read/declare / typeset/变量设定功能/别名/万用字符与特殊符号

2012-10-17 17:10 543 查看
1、PS1:(提示字符的设定)

这个比较有意思,可以定制自己的提示符;

当我们每次按下 [Enter] 按键去执行某个指令后,最后要再次出现提示字符时, 就会主动去读取这个变数值了。

预设的 bash 的 PS1 变量内的特殊符号代表意义:

o \d :代表日期,格式为 Weekday Month Date,例如 "Mon Aug 1"

o \H :完整的主机名称。举例来说,鸟哥的练习机 linux.dmtsai.tw ,那么这个主机名称就是 linux.dmtsai.tw

o \h :仅取主机名称的第一个名字。以上述来讲,就是 linux 而已, .dmtsai.tw 被省略。

o \t :显示时间,为 24 小时格式,如: HH:MM:SS

o \T :显示时间,12 小时的时间格式!

o \A :显示时间,24 小时格式, HH:MM

o \u :目前使用者的账号名称;

o \v :BASH 的版本信息;

o \w :完整的工作目录名称。家目录会以 ~ 取代;

o \W :利用 basename 取得工作目录名称,所以仅会列出最后一个目录名。

o \# :下达的第几个指令。

o \$ :提示字符,如果是 root 时,提示字符为 # ,否则就是 $ 啰~

OK!所以,由预设的 PS1 内容为: '\[\u@\h \W\]\$ ' 就可以了解为何我们的提示字符会是: [root@linux ~]# 了吧!好了,那么假设我想要有类似底下的提示字符:

[root@linux /home/dmtsai 16:50 #12]#

,那个 # 代表第 12 次下达的指令。 那么应该如何设定 PS1 呢?可以这样啊:

[root@linux home]# PS1='[\u@\h \w \A #\#]\$ '

[root@linux /home 17:02 #85]#

# 看到了吗?提示字符变了!变的很有趣吧!其中,那个 #85 比较有趣

2、read

要读取来自键盘输入的变量,就是用 read 这个指令了。这个指令最常被用在 shell script 的撰写当中, 以跟使用者进行对谈。关于 script 的写法,在后面章节介绍,底下先来瞧一瞧 read 的相关语法吧!

[root@linux ~]# read [-pt] variable

参数:

-p :后面可以接提示字符!

-t :后面可以接等待的『秒数!』这个比较有趣~不会一直等待使用者啦!

范例:

范例一:让使用者由键盘输入一内容,将该内容变成 atest 变量

[root@linux ~]# read atest

This is a test

[root@linux ~]# echo $atest

This is a test

范例二:提示使用者 30 秒内输入自己的大名,将该输入字符串做成 named 变量

[root@linux ~]# read -p "Please keyin your name: " -t 30 named

Please keyin your name: VBird Tsai

[root@linux ~]# echo $named

VBird Tsai

read 之后不加任何参数,直接加上变量名称,那么底下就会主动出现一个空白行,等待您输入。 如果加上 -t 后面接秒数之后,例如上面的范例当中,那么 30 秒之内没有任何动作时, 该指令就会自动略过了~如果是加上 -p,就可以增加提示信息了;

实练补充:

作用

从标准输入中读取一行。

语法

read [ -p ][ -r ][ -s ][ -u[ n ] ] [ VariableName?Prompt ]

[ VariableName ... ]

描述

read 命令从标准输入中读取一行,并把输入行的每个字段的值指定给 shell 变量,用 IFS(内部字段分隔符)变量中的字符作为分隔符。VariableName 参数指定 shell 变量的名称,shell 变量获取输入行一个字段的值。由VariableName 参数指定的第一个 shell 变量指定给每一个字段的值,由 VariableName 参数指定的第二个 shell 变量指定给第二个字段的值,以此类推,直到最后一个字段。如果标准输入行的字段比相应的由 VariableName 参数指定的 shell
变量的个数多,把全部余下的字段的值赋给指定的最后的 shell 变量。如果比 shell 变量的个数少,则剩余的 shell 变量被设置为空字符串。

注意: 如果省略了 VariableName 参数,变量 REPLY 用作缺省变量名。

由 read 命令设置的 shell 变量影响当前 shell 执行环境。

标志

-p 用 |& (管道,& 的记号名称)读取由 Korn shell 运行的进程的输出作为输入。

注:-p 标志的文件结束符引起该进程的清除,因此产生另外一个进程。

-r 指定读取命令把一个 \ (反斜杠) 处理为输入行的一部分,而不把它作为一个控制字符。

-s 把输入作为一个命令保存在 Korn shell 的历史记录文件中。

-u [ n ] 读取一位数的文件描述符号码 n 作为输入。文件描述符可以用 ksh exec 内置命令打开。n 的缺省值是 0,表示的是键盘。值 2 表示标准错误。

参数

VariableName?Prompt 指定一个变量的名称和一个要使用的提示符。当 Korn shell 是交互式时,它将把提示符写到标准错误,并执行输入。Prompt 包含多于一个的字,必须用单引号或双引号括起来。

VariableName... 指定一个或多个由空格分隔的变量名。

退出状态

这个命令返回下列出口值:

0 成功完成。

>0 检测到文件结束符或一个错误发生。

示例

下列脚本打印一个文件,这个文件中每行的第一个字段被移动到本行的末尾。

while read -r xx yy

do

print printf "%s %s/n" $yy $xx

done < InputFile读取一行,把它分成字段,并使用 "Please enter: " 作为提示符,请输入:

read word1?"Please enter: " word2系统显示:

Please enter:

You enter:

hello world变量 word1 的值应该是 "hello",变量 word2 应该是 "world."

为创建一个共同进程,用 print -p 写到共同进程,并用 read -p 从共同进程中读取输入,请输入:

(read; print "hello $REPLY")

print -p "world"

read-p line变量 line 的值应该是 "hello world."

为把输入行的副本保存为历史文件中的一个命令,请输入:

read -s line < input_file如果输入文件包含 "echo hello world," ,那么在历史记录文件中将会把 "echo hello world" 保存为一个命令。

3、 declare / typeset

declare 或 typeset 是一样的功能,就是在宣告变量的属性。如果使用 declare 后面并没有接任何参数, 那么 bash 就会主动的将所有的变量名称与内容通通叫出来,就好像使用 set 一样啦! 那么 declare 还有什么语法呢?看看先:

[root@linux ~]# declare [-aixr] variable

参数:

-a :将后面的 variable 定义成为数组 (array)

-i :将后面接的 variable 定义成为整数数字 (integer)

-x :用法与 export 一样,就是将后面的 variable 变成环境变量;

-r :将一个 variable 的变量设定成为 readonly ,该变量不可被更改内容,也不能 unset

范例:

范例一:让变量 sum 进行 100+300+50 的加总结果

[root@linux ~]# sum=100+300+50

[root@linux ~]# echo $sum

100+300+50 <==咦!怎么没有帮我计算加总?因为这是文字型态的变量属性啊!

[root@linux ~]# declare -i sum=100+300+50

[root@linux ~]# echo $sum

450 <==瞭乎??

范例二:将 sum 变成环境变量

[root@linux ~]# declare -x sum

范例三:让 sum 变成只读属性,不可更动!

[root@linux ~]# declare -r sum

[root@linux ~]# sum=tesgting

-bash: sum: readonly variable <==老天爷~不能改这个变数了!

declare 也是个很有用的功能~尤其是当我们需要使用到底下的数组功能时, 他也可以帮我们宣告数组的属性喔!不过,老话一句,数组也是在 shell script 比较常用的

4、额外的变量设定功能

刚刚我们提到了两种变量取用的方法,分别是这样:

[root@linux ~]# echo $HOME

[root@linux ~]# echo ${HOME}

那么,在那个 ${variable} 的使用方法中,其实,我们还可以将变量进行一些修订的工作喔! 只要加上一些字符标志,后面再接着使用比对字符串,就能够修改变量的内容了! 我们取底下的例子来说明:在底下的例子中,假设我的变量名称为 vbird ,且内容为 /home/vbird/testing/testing.x.sh。

1. 完整呈现 vbird 这个变量的内容;

[root@linux ~]# vbird="/home/vbird/testing/testing.x.sh"

[root@linux ~]# echo ${vbird}

/home/vbird/testing/testing.x.sh

2. 在 vbird 变量中,从最前面开始比对,若开头为 / ,则删除两个 /

之间的所有数据,亦即 /*/

[root@linux ~]# echo ${vbird##/*/}

testing.x.sh <==删除了 /home/vbird/testing/

[root@linux ~]# echo ${vbird#/*/}

vbird/testing/testing.x.sh <==仅删除 /home/ 而已

# 这两个小例子有趣了~变量名称后面如果接了两个 ## ,表示在 ##

# 后面的字符串取『最长的』那一段;如果仅有一个 # ,表示取『最小的那一段』喔!

3. 承上题,如果是从后面开始,删除 /* 呢?

[root@linux ~]# echo ${vbird%%/*/}

/home/vbird/testing/testing.x.sh <==都没被删除

[root@linux ~]# echo ${vbird%%/*}

<==被删除光了!

[root@linux ~]# echo ${vbird%/*}

/home/vbird/testing <==只删除 /testing.x.sh 部分

# 这个例子当中需要特别注意,那个 % 比对的是『最后面那个字符』的意思,

# 所以啰,第一个方式当然不对~因为 vbird 这个变量的内容最后面是 h 而不是 / 啊!

# 至于 %%/* 则是删除『最长的那个 /* 』,当然就是全部喔!而 %/* 则是最短的那个!

4. 将 vbird 变数中的 testing 取代为 TEST

[root@linux ~]# echo ${vbird/testing/TEST}

/home/vbird/TEST/testing.x.sh

[root@linux ~]# echo ${vbird//testing/TEST}

/home/vbird/TEST/TEST.x.sh

# 如果变量后面接的是 / 时,那么表示后面是进行『取代』的工作~而且仅取代『第一个』

# 但如果是 // ,则表示全部的字符串都取代啊!

这里您稍微留意一下就好了~反正就是变量后面可以接 #, ##, %, %%, /, // , 而他们存在的意义并不相同的啦~ 另外,几个不同的变量内容还可以进行判断呢! 举例来说,目前我需要用到两个变量,分别是 var 与 str , 那我想要针对 str 这个变量内容是否有设定成一个字符串,亦即 "expr" 来决定 var 的内容。 那我可以使用什么方法来进行判断呢?如果您会写 shell script 的话, 直接用 shell script 就好了,如果不会写,那么我们就透过简单的变量判断吧!

Tips: 底下的例子当中,那个 var 与 str 为变量,我们想要针对 str 是否有设定来决定 var 的值喔! 一般来说, str: 代表『str 没设定或为空的字符串时』;至于 str 则仅为『没有该变数』。

变量设定方式 str 没有设定 str 为空字符串 str 已设定非为空字符串

var=${str-expr} var=expr var= var=$str

var=${str:-expr} var=expr var=expr var=$str

var=${str+expr} var=expr var=expr var=expr

var=${str:+expr} var=expr 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

根据上面这张表,我们来进行几个范例的练习

范例一:若 str 这个变量内容存在,则 var 设定为 str ,否则 var 设定为 "newvar"

[root@linux ~]# unset str; var=${str-newvar}

[root@linux ~]# echo var="$var", str="$str"

var=newvar, str= <==因为 str 不存在,所以 var 为 newvar

[root@linux ~]# str="oldvar"; var=${str-newvar}

[root@linux ~]# echo var="$var", str="$str"

var=oldvar, str=oldvar <==因为 str 存在,所以 var 等于 str 的内容

范例二:若 str 不存在,则 var 与 str 均设定为 newvar,否则仅 var 为 newvar

[root@linux ~]# unset str; var=${str=newvar}

[root@linux ~]# echo var="$var", str="$str"

var=newvar, str=newvar <==因为 str 不存在,所以 var/str 均为 newvar

[root@linux ~]# str="oldvar"; var=${str=newvar}

[root@linux ~]# echo var="$var", str="$str"

var=oldvar, str=oldvar <==因为 str 存在,所以 var 等于 str 的内容

范例三:若 str 这个变量存在,则 var 等于 str ,否则输出 "novar"

[root@linux ~]# unset str; var=${str?novar}

-bash: str: novar <==因为 str 不存在,所以输出错误讯息

[root@linux ~]# str="oldvar"; var=${str?novar}

[root@linux ~]# echo var="$var", str="$str"

var=oldvar, str=oldvar <==因为 str 存在,所以 var 等于 str 的内容

# 上面这三个案例都没有提到当 str 有设定,且为空字符串的情况喔!

# 您可以自行测试一下哩!

虽然猛一看,觉得变量没有什么奇特的地方,但是,如果仔细瞧一瞧,嘿!一堆环境变量与系统资源方面的变量, 可是会影响到我们在 bash 里头是否能够顺利作业的呢!例如 PATH 啊、ulimit 之类的~

特殊变量:

$* 这个程序的所有参数 如命令ls -al dir 即代表 -al dir

$? 执行上一个命令的返回值 为0表示执行成功!

$! 执行上一个后台命令的PID

$$ 这个程序的PID

$# 这个程序的参数个数
如命令ls -al dir 即代表2个

5、别名

增加别名:

Loong:/home/yee/shell# alias vi="vim"

Loong:/home/yee/shell# lm

bash: lm: command not found

Loong:/home/yee/shell# alias lm="vim"

Loong:/home/yee/shell# lm kkk.txt

取消别名:

Loong:/home/yee/shell# una

unalias uname

Loong:/home/yee/shell# unalias lm

Loong:/home/yee/shell# lm

bash: lm: command not found

Loong:/home/yee/shell#

6、万用字符与特殊符号:

!在 bash 里头还支持一些万用字符喔 (wild card) !多了这些万用字符, 我们利用 bash 处理数据就更方便了!底下我们列出一些常用的万用字符喔:

符号

内容

*
万用字符,代表 0 个或多个字符(或数字)
?
万用字符,代表『一定有』一个字母
#
批注,这个最常被使用在 script 当中,视为说明!
\
跳脱符号,将『特殊字符或万用字符』还原成一般字符
|
分隔两个管线命令的界定;
;
连续性命令的界定(注意!与管线命令并不相同)
~
使用者的家目录
$
亦即是变量之前需要加的变量取代值
&
将指令变成背景下工作
!
逻辑运算意义上的『非』 not 的意思!
/
路径分隔的符号
>, >>
输出导向,分别是『取代』与『累加』
'
单引号,不具有变量置换的功能
"
具有变量置换的功能!
` `
两个『 ` 』中间为可以先执行的指令!
( )
在中间为子 shell 的起始与结束
[ ]
在中间为字符的组合
{ }
在中间为命令区块的组合!
组合按键
执行结果
Ctrl + C
终止目前的命令
Ctrl + D
输入结束(EOF),例如邮件结束的时候;
Ctrl + M
就是 Enter 啦!
Ctrl + S
暂停屏幕的输出
Ctrl + Q
恢复屏幕的输出
Ctrl + U
在提示字符下,将整列命令删除
Ctrl + Z
『暂停』目前的命令

[root@linux ~]# ls test* <==那个 * 代表后面不论接几个字符都予以接受

[root@linux ~]# ls test? <==那个 ? 代表后面『一定』要接『一个』字符

[root@linux ~]# ls test??? <==那个 ??? 代表『一定要接三个』字符!

[root@linux ~]# cp test[1-5] /tmp

# 将 test1, test2, test3, test4, test5 若存在的话,就拷贝到 /tmp

[root@linux ~]# cp test[!1-5] /tmp

# 只要不是 test1, test2, test3, test4, test5 之外的其它 test? ,

# 若存在的话,就拷贝到 /tmp

[root@linux ~]# cd /lib/modules/`uname -r`/kernel/drivers

# 被 ` ` 括起来的内容『会先执行』

上面几个例子相当的有趣!尤其是最后面两个!需要注意的是, [1-5] 里面『代表只有一个字符』但是范围可以由 1-5 ,这样来说的话,那么我们如果允许『只要档名里面含有至少一个大写字符』时,就可以将档案 copy 出来的话,可以这样做:

cp *[A-Z]* /tmp

很有趣吧?!也就是说『 [ ] 谨代表一个字符,而这个字符的定义可以是范围(-), 可以是指定项目,也可以是两者并存。 』举例来说,我想要找出在 /etc/ 底下所有含有数字的档案, 可以这样:

ls -lda /etc/*[0-9]*

但如果我只想要找出含有 3 及 5 的档名的档案呢?就会是这样:

ls -lda /etc/*[35]*

如果是『不想要』某些范围或者是单字呢?就使用 [!] 即可!例如不想要有小写字符为开头的档案:

ls -lda /etc/[!a-z]*

很好玩吧!至于那个 ` 是啥?在一串指令当中, `command` 内的指令会先被执行, 执行完的讯息再回传到外部指令来处理!也就是说:

1. 系统先执行 uname -r 找出输出的结果;

2. 将结果累加在目录上面,来执行 cd 的功能!

很棒吧!!另外,这个 quot (`) 的功能,也可以利用 $() 来取代喔!例如:

cd /lib/modules/$(uname -r)/kernel

这些基本的功能需要特别来了解一下才行

内容摘自:《鸟哥私房菜》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: