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
这些基本的功能需要特别来了解一下才行
内容摘自:《鸟哥私房菜》