<<<作用
* cmd <<< word
把word字符串(而不是文件word)和后面的换行作为输入提供给cmd。
例如:
[root@snow zc]# cat <<< "hello" > 123.txt
[root@snow zc]# ls
123.txt perl5 word zc.sh
[root@snow zc]# cat 123.txt
hello
[root@snow zc]# : >123.txt 冒号后面有空格,: > 代表清空123.txt文件
[root@snow zc]# cat 123.txt
[root@snow zc]#
冒号作用
{str:=expr}
如果变量str不为空,${str:=expr}就等于str的值,若str为空,就把expr的值赋值给str。
例如:
[root@localhost ~]# ${abc:=t1}
-bash: t1: command not found
[root@localhost ~]# : ${abc:=t1} 冒号后面有空格
[root@localhost ~]# echo $abc
t1
[root@localhost ~]# echo $?
0
注释:
在第一条赋值命令中,若abc为空,则将t1赋值给abc,同时将t1作为命令来执行,但是并没有t1这个命令故报错。
在第二条赋值命令中,若abc为空,则将t1赋值给abc,同时将t1作为参数传递给:空命令执行,且返回值为0。
作用一: 占位符
#!/bin/bash
var=0
if [ "$var" = "0" ]; then
:
else
:
fi
作用二: 空命令,与内建的true相同
#!/bin/bash
while :
do
echo "dead loop..."
done
作用三: 与>结合结合使用,用于清空文件
[root@localhost ~#] : > data.log # 清空文件
[root@localhost ~#] cat /dev/null > data.log # 等同于于这种
示例二 清空文件
[root@node56 ~]# cat <<<"Hello" >123.txt
[root@node56 ~]# cat 123.txt
Hello
[root@node56 ~]# : >123.txt
[root@node56 ~]# cat 123.txt
[root@node56 ~]#
作用四: 注释
单行注释
: your comment here 冒号后面有空格
等价于
# your comment here
多行注释
: 'comment line1 冒号后面有空格
comment line2
more comments'
例如:
: this is single line comment 冒号后面有空格
: 'this is a multiline comment, 冒号后面有空格
second line
end of comments'
示例三 脚本注释、占位符
脚本test_colon.sh
#!/bin/sh
: this is single line comment 脚本注释
: 'this is a multiline comment, 脚本注释
second line
end of comments'
if [ "1" == "1" ]; then
echo "yes"
else
: 占位符
fi
点号作用
想在当前shell脚本中,调用另外一个shell库里面的函数,可以使用点号(.)或者source命令。
例如:
[root@snow zc]# cat lss.sh
#!/bin/bash
ls -al
echo
echo
. /home/zc/zc.sh 点号(.)后面有空格,且是绝对路径
[root@snow zc]#
source命令的作用就是用来执行一个脚本,那么: source a.sh 同直接执行 ./a.sh 有什么不同呢?
比如您在一个脚本里export $KKK=111,假如您用./a.sh执行该脚本,执行完毕后,您运行echo $KKK,发现没有值,
假如您用source来执行,然后再echo ,就会发现KKK=111。因为调用./a.sh来执行shell是在一个子shell里运行的,
所以执行后,结果并没有反应到父shell里,但是source不同他就是在本shell中执行的,所以能够看到结果。
经典问题:
shell脚本(test.sh)如下:
#!/bin/sh
echo "export monitor=1" >> /etc/profile
source /etc/profile
调用后(./test.sh),执行echo $monitor,没有输出任何值
手动source /etc/profile后,再次执行echo $monitor,输出了预期的值
可见脚本中的source /etc/profile没有成功(提法本身就是错误的),请问这个应该怎么解决?
A:在执行test.sh的时候,不用./test.sh
用source test.sh。
alias实现命令别名
1.显示当前设置的所有别名:
[root@snow zc]# alias
alias cdp='cd /root/zhangchao'
alias cdz='cd /home/zc'
alias cp='cp -i'
2.只显示某个别名代表的含义可输入alias name
[root@snow zc]# alias ll
alias ll='ls -l --color=auto'
[root@snow zc]#
3.为命令设置别名可输入alias 新命令='原命令 选项/参数'
[root@snow zc]# alias cdz='cd /home/zc'
4.取消命令别名
[root@snow zc]# unalias ll
5.一次执行多个命令
a.首先使用命令 type 自定义命令名 ,查看自定义命令名是否被系统占用。
[root@snow zc]# type ll
ll is aliased to `ls -l --color=auto'
[root@snow zc]# type loo
-bash: type: loo: not found
[root@snow zc]#
b.使用命令alias创建自定义命令:alias loo='cd /root;ls;cd /' 。需要注意的是命令的使用格式,分号与分号之间是没有空格的。
c.如果希望删除这个自定义命令,可以使用命令 unalias 自定义命令名 来完成
6.别名永久生效
那就是修改rc配置文件,把设置别名的过程加入到系统启动后初始化用户的脚本中, 用户只需要修改 ~/.bashrc文件, 加入你要设置的别名命令即可
想要添加一个命令 oo 输入oo就能直接进入/mnt/hgfs/D/work/project/ASID/5.code/trunk/ASID/ASID/wms目录
方法:vi /etc/bashrc
在文件末尾添加alias oo='cd /mnt/hgfs/D/work/project/ASID/5.code/trunk/ASID/ASID/wms/'并保存退出
执行source /etc/bashrc 使配置生效
完成!
bg、fg
bg %num 即可将挂起的job的状态由stopped改为running,仍在后台执行;
当需要改为在前台执行时,执行命令fg %num即可;
builtin命令
用以执行shell的内建命令,既然是内建命令,为什么还要以这种方式执行呢?因为shell命令执行时首先从函数开始,如果自定义了一个与内建命令同名的函数,那么就执行这个函数而非真正的内建命令。
$ umask
0002
$ umask() { echo "umask function"; }
$ umask
umask function
$ builtin umask
0002
$ unset -f umask
$ umask
0002
caller命令
返回当前活动的子程序调用的上下文,即调用堆栈信息,包括shell函数和内建命令source执行的脚本。
没有指定expr时,显示当前子程序调用的行号和源文件名。如果expr是一个非负整数,显示当前子程序调用的行号、子程序名和源文件名。
caller命令打印出来的堆栈信息在调试程序时是很有帮助的,当前栈帧为0。如果shell没有子程序调用或者expr是一个无效的位置时,call命令返回false。
caller一般做调试用,打印出函数调用的行号和源文件名。
例如:
foo()
{
echo "foo called"
caller
}
bar()
{
echo "bar called"
caller 0
}
test1()
{
echo "test1 called"
caller
caller 0
caller 1
caller 2
}
test2()
{
echo "test2 called"
test1
}
test3()
{
echo "test3 called"
test2
}
foo
bar
test3
command命令
类似于builtin,也是为了避免调用同名的shell函数,命令包括shell内建命令和环境变量PATH中的命令。选项“-p”指定在默认的环境变量PATH中查找命令路径。选项“-v”和“-V”用于显示命令的描述,后者显示的信息更详细。
例如:
[root@snow zc]# ps
PID TTY TIME CMD
5826 pts/0 00:00:00 bash
7019 pts/0 00:00:00 ps
[root@snow zc]#
[root@snow zc]# ps() { echo "function ps"; }
[root@snow zc]# ps
function ps
[root@snow zc]# builtin ps ps不是内建命令,是环境变量命令
-bash: builtin: ps: not a shell builtin
[root@snow zc]# command ps
PID TTY TIME CMD
5826 pts/0 00:00:00 bash
7020 pts/0 00:00:00 ps
[root@snow zc]#
disown
后台挂载,在后台运行用户进程,不受shell退出的影响,弥补没有使用&和nuhup。
disown 使已经在运行的用户进程 不受用户退出限制
disown -h %1 # 1 为jobsID
如果已经在当前终端运行 ;可以ctrl+z 挂起 ;bg jobsID 放入后台
disown -h jobsID #使已经在运行的用户进程 不受用户退出限制
用disown -h jobspec来使某个作业忽略HUP信号。
用disown -ah 来使所有的作业都忽略HUP信号。
用disown -rh 来使正在运行的作业忽略HUP信号。
需要注意的是,当使用过 disown 之后,会将把目标作业从作业列表中移除,我们将不能再使用jobs来查看它,但是依然能够用ps -ef查找到它。
disown命令可以从当前shell的作业列表中移除全部作业,也可移除指定的一到多个作业;正在运行的作业也可以移除;也可以标记作业,使得它们在当前shell退出后也不会结束。
该命令需要set选项monitor处于开启状态时才能执行;查看作业控制状态:输入set -o查看 monitor行;执行set -o monitor或set -m开启该选项。
disown命令该命令是bash内建命令,相关的帮助信息请查看help命令。
语法格式
disown [参数] [标识符or进程ID]
常用参数:
-h 标记每个作业标识符,这些作业将不会在shell接收到sighup信号时接收到sighup信号
-a 移除所有的作业
-r 移除运行的作业
参考实例
删除全部作业:
[root@linux265 ~]# disown -a
删除运行状态的作业:
[root@linux265 ~]# disown -r
根据jobId,移出指定的后台任务:
[root@linux265 ~]# disown -h %2
一、键入不同
1、HUP中断信号:HUP中断信号是当用户键入<Ctrl+X>时由终端驱动程序发送的信号。
2、INT中断信号:INT中断信号是当用户键入<Ctrl+I>时由终端驱动程序发送的信号。
3、KILL中断信号:KILL中断信号是当用户键入<Ctrl+Z>时由终端驱动程序发送的信号。
4、TERM中断信号:TERM中断信号是当用户键入<Ctrl+ >时由终端驱动程序发送的信号。
5、TSTP中断信号:TSTP中断信号是当用户键入<Ctrl+T>时由终端驱动程序发送的信号。二、二、对应操作不同
1、HUP中断信号:HUP中断信号的对应操作为让进程挂起,睡眠。
2、INT中断信号:INT中断信号的对应操作为正常关闭所有进程。
3、KILL中断信号:KILL中断信号的对应操作为强制关闭所有进程。
4、TERM中断信号:TERM中断信号的对应操作为正常的退出进程。
5、TSTP中断信号:TSTP中断信号的对应操作为暂时停用进程。
三、启用不同
1、HUP中断信号:HUP中断信号发送后,可以重新被用户再次输入恢复启用进程。
2、INT中断信号:INT中断信号发送后,不可以重新被用户再次输入恢复启用进程。
3、KILL中断信号:KILL中断信号发送后,不可以重新被用户再次输入恢复启用进程。
4、TERM中断信号:TERM中断信号发送后,可以重新被用户再次输入启用进程。
5、TSTP中断信号:TSTP中断信号发送后,可以重新被用户再次输入继续使用进程。
enable命令
用于启动或关闭 shell 内建指令。
若要执行的文件名称与shell内建指令相同,可用enable -n来关闭shell内建指令。若不加-n参数,enable可重新启动关闭的指令。
eval
eval主要用在对参数的特殊处理上面的,一般的命令行,shell处理参数就只执行一遍,像转义和变量转变;
但加上eval后就可以对参数经行两遍处理;网上有说是对command-line处理两遍,我认为是不合理的。
一个eval只能使shell对参数多一次处理,因此有几个eval就可以多加几次,即eval eval command-line
这样就能对参数进行三次编译,但此时应特别注意参数的转义,下面有例子说明。
eval命令会计算(evalue)它的参数,这些参数作为表达式计算后重新组合为一个字符串,然后作为一个命令被执行。
eval最常见的用法是将动态生成的命令行计算并执行。例如:
$ name=woodie
$ cmd="echo Helllo $name! "
$ eval $cmd
Hello woodie!
[root@localhost simon]#vim a.sh
#!/bin/bash
echo "$$#" 进程号+#
echo "$$#" $+参数个数
eval echo "$$#" 最后一个参数值
[root@localhost simon]#./a.sh w s d
20621#
$3
d
exec 命令
命令代替shell程序,命令退出,shell 退出;比如 exec ls
这个命令还可以作为find命令的一个选项,如下所示:
(1)在当前目录下(包含子目录),查找所有txt文件并找出含有字符串"bin"的行
find ./ -name "*.txt" -exec grep "bin" {} ;
(2)在当前目录下(包含子目录),删除所有txt文件
find ./ -name "*.txt" -exec rm {} ;
shell 中的 exec 两种用法:
1.exec 命令 ;命令代替shell程序,命令退出,shell 退出;比如 exec ls
2.exec 文件重定向,可以将文件的重定向就看为是shell程序的文件重定向 比如 exec 5</dev/null;exec 5<&-
shell的内建命令exec将并不启动新的shell,而是用要被执行命令替换当前的shell进程,并且将老进程的环境清理掉,而且exec命令后的其它命令将不再执行。
因此,如果你在一个shell里面,执行exec ls那么,当列出了当前目录后,这个shell就自己退出了,因为这个shell进程已被替换为仅仅执行ls命令的一个进程,执行结束自然也就退出了。
为了避免这个影响我们的使用,一般将exec命令放到一个shell脚本里面,用主脚本调用这个脚本,调用点处可以用bash a.sh,(a.sh就是存放该命令的脚本),
这样会为a.sh建立一个sub shell去执行,当执行到exec后,该子脚本进程就被替换成了相应的exec的命令。
source命令或者".",不会为脚本新建shell,而只是将脚本包含的命令在当前shell执行。
不过,要注意一个例外,当exec命令来对文件描述符操作的时候,就不会替换shell,而且操作完成后,还会继续执行接下来的命令。
exec 3<&0:这个命令就是将操作符3也指向标准输入。
另外,这个命令还可以作为find命令的一个选项,如下所示:
(1)在当前目录下(包含子目录),查找所有txt文件并找出含有字符串"bin"的行
find ./ -name "*.txt" -exec grep "bin" {} ;
(2)在当前目录下(包含子目录),删除所有txt文件
find ./ -name "*.txt" -exec rm {} ;
先总结一个表:
exec命令 作用
exec ls 在shell中执行ls,ls结束后不返回原来的shell中了
exec <file 将file中的内容作为exec的标准输入
exec >file 将file中的内容作为标准写出
exec 3<file 将file读入到fd3中
sort <&3 fd3中读入的内容被分类
exec 4>file 将写入fd4中的内容写入file中
ls >&4 Ls将不会有显示,直接写入fd4中了,即上面的file中
exec 5<&4 创建fd4的拷贝fd5
exec 3<&- 关闭fd3
1. exec 执行程序
虽然exec和source都是在父进程中直接执行,但exec这个与source有很大的区别,source是执行shell脚本,而且执行后会返回以前的shell。
而exec的执行不会返回以前的shell了,而是直接把以前登陆shell作为一个程序看待,在其上经行复制。
举例说明:
root@localhost:~/test#exec ls
exp1 exp5 linux-2.6.27.54 ngis_post.sh test xen-3.0.1-install
<logout>
root@localhost:~/test#exec >text
root@localhost:~/test#ls
root@localhost:~/test#pwd
root@localhost:~/test#echo "hello"
root@localhost:~/test#exec>/dev/tty
root@localhost:~/test#cat text
exp1
exp5
linux-2.6.27.54
ngis_post.sh
test
text
xen-3.0.1-install
/root/test
hello
root@localhost:~/test#
Exec >text 是将当前shell的标准输出都打开到text文件中
root@localhost:~/test#cat test
ls
Pwd
root@localhost:~/test#bash
root@localhost:~/test#exec <test
root@localhost:~/test#ls
exp1 exp5 linux-2.6.27.54 ngis_post.sh test text xen-3.0.1-install
root@localhost:~/test#pwd
/root/test
root@localhost:~/test#
root@localhost:~/test#exit #自动执行
2. exec的重定向
先上我们进如/dev/fd/目录下看一下:
root@localhost:~/test#cd /dev/fd
root@localhost:/dev/fd#ls
0 1 2 255
默认会有这四个项:0是标准输入,默认是键盘。
1是标准输出,默认是屏幕/dev/tty
2是标准错误,默认也是屏幕
255
当我们执行exec 3>test时:
root@localhost:/dev/fd#exec 3>/root/test/test
root@localhost:/dev/fd#ls
0 1 2 255 3
root@localhost:/dev/fd#
看到了吧,多了个3,也就是又增加了一个设备,这里也可以体会下linux设备即文件的理念。
这时候fd3就相当于一个管道了,重定向到fd3中的文件会被写在test中。关闭这个重定向可以用exec 3>&-。
root@localhost:/dev/fd#who >&3
root@localhost:/dev/fd#ls >&3
root@localhost:/dev/fd#exec 3>&-
root@localhost:/dev/fd#cat /root/test/te
test text
root@localhost:/dev/fd#cat /root/test/test
root tty1 2010-11-16 01:13
root pts/0 2010-11-15 22:01 (192.168.0.1)
root pts/2 2010-11-16 01:02 (192.168.0.1)
0
1
2
255
3
3. 应用举例:
exec 3<test
while read -u 3 pkg
do
echo "$pkg"
done
. 系统调用exec是以新的进程去代替原来的进程,但进程的PID保持不变。因此,可以这样认为,exec系统调用并没有创建新的进程,只是替换了原来进程上下文的内容。
原进程的代码段,数据段,堆栈段被新的进程所代替。
一个进程主要包括以下几个方面的内容:
(1)一个可以执行的程序
(2) 与进程相关联的全部数据(包括变量,内存,缓冲区)
(3)程序上下文(程序计数器PC,保存程序执行的位置)
2. exec是一个函数簇,由6个函数组成,分别是以excl和execv打头的。
执行exec系统调用,一般都是这样,用fork()函数新建立一个进程,然后让进程去执行exec调用。
我们知道,在fork()建立新进程之后,父进各与子进程共享代码段,但数据空间是分开的,但父进程会把自己数据空间的内容copy到子进程中去,还有上下文也会copy到子进程中去。
而为了提高效率,采用一种写时copy的策略,即创建子进程的时候,并不copy父进程的地址空间,父子进程拥有共同的地址空间,
只有当子进程需要写入数据时(如向缓冲区写入数据),这时候会复制地址空间,复制缓冲区到子进程中去。从而父子进程拥有独立的地址空间。
而对于fork()之后执行exec后,这种策略能够很好的提高效率,如果一开始就copy,那么exec之后,子进程的数据会被放弃,被新的进程所代替。
3. exec与system的区别
(1) exec是直接用新的进程去代替原来的程序运行,运行完毕之后不回到原先的程序中去。
(2) system是调用shell执行你的命令,system=fork+exec+waitpid,执行完毕之后,回到原先的程序中去。继续执行下面的部分。
总之,如果你用exec调用,首先应该fork一个新的进程,然后exec. 而system不需要你fork新进程,已经封装好了。
exec I/O重定向详解及应用实例
1、 基本概念(这是理解后面的知识的前提,请务必理解)
a、 I/O重定向通常与 FD有关,shell的FD通常为10个,即 0~9;
b、 常用FD有3个,为0(stdin,标准输入)、1(stdout,标准输出)、2(stderr,标准错误输出),默认与keyboard、monitor、monitor有关;
c、 用 来改变送出的数据信道(stdout, stderr),使之输出到指定的档案;
e、 0 是 与 1> 是一样的;
f、 在IO重定向 中,stdout 与 stderr 的管道会先准备好,才会从 stdin 读进资料;
g、 管道“|”(pipe line):上一个命令的 stdout 接到下一个命令的 stdin;
h、 tee 命令是在不影响原本 I/O 的情况下,将 stdout 复制一份到档案去;
i、 bash(ksh)执行命令的过程:分析命令-变量求值-命令替代(``和$( ))-重定向-通配符展开-确定路径-执行命令;
j、 ( ) 将 command group 置于 sub-shell 去执行,也称 nested sub-shell,它有一点非常重要的特性是:
继承父shell的Standard input, output, and error plus any other open file descriptors。
k、 exec 命令:常用来替代当前 shell 并重新启动一个 shell,换句话说,并没有启动子 shell。
使用这一命令时任何现有环境都将会被清除。exec 在对文件描述符进行操作的时候,也只有在这时,exec 不会覆盖你当前的 shell 环境。
2、cmd &n 使用系统调用 dup (2) 复制文件描述符 n 并把结果用作标准输出
&- 关闭标准输出
n&- 表示将 n 号输出关闭
上述所有形式都可以前导一个数字,此时建立的文件描述符由这个数字指定而不是缺省的 0 或 1。如:
... 2>file 运行一个命令并把错误输出(文件描述符 2)定向到 file。
... 2>&1 运行一个命令并把它的标准输出和输出合并。(严格的说是通过复制文件描述符 1 来建立文件描述符 2 ,但效果通常是合并了两个流。)
我们对 2>&1详细说明一下 :2>&1 也就是 FD2=FD1 ,这里并不是说FD2 的值 等于FD1的值,因为 > 是改变送出的数据信道,
也就是说把 FD2 的 “数据输出通道” 改为 FD1 的 “数据输出通道”。如果仅仅这样,这个改变好像没有什么作用,因为 FD2 的默认输出和 FD1的默认输出本来都是 monitor,一样的!
但是,当 FD1 是其他文件,甚至是其他 FD 时,这个就具有特殊的用途了。请大家务必理解这一点。
3、 如果 stdin, stdout, stderr 进行了重定向或关闭, 但没有保存原来的 FD, 可以将其恢复到 default 状态吗?
*** 如果关闭了stdin,因为会导致退出,那肯定不能恢复。
*** 如果重定向或关闭 stdout和stderr其中之一,可以恢复,因为他们默认均是送往monitor(但不知会否有其他影响)。
如恢复重定向或关闭的 stdout: exec 1>&2 ,恢复重定向或关闭的stderr:exec 2>&1。
*** 如果stdout和stderr全部都关闭了,又没有保存原来的FD,可以用:exec 1>/dev/tty 恢复。
4、 cmd >a 2>a 和 cmd >a 2>&1 为什么不同?
cmd >a 2>a :stdout和stderr都直接送往文件 a ,a文件会被打开两遍,由此导致stdout和stderr互相覆盖。
cmd >a 2>&1 :stdout直接送往文件a ,stderr是继承了FD1的管道之后,再被送往文件a 。a文件只被打开一遍,就是FD1将其打开。
我想:他们的不同点在于:
cmd >a 2>a 相当于使用了两个互相竞争使用文件a的管道;
而cmd >a 2>&1 只使用了一个管道,但在其源头已经包括了stdout和stderr。
从IO效率上来讲,cmd >a 2>&1的效率应该更高!
exec 0exec 1>outfilename # 打开文件outfilename作为stdout
exec 2>errfilename # 打开文件 errfilename作为 stderr
exec 0&- # 关闭 FD1
exec 5>&- # 关闭 FD5
export
Linux export 命令用于设置或显示环境变量。
在 shell 中执行程序时,shell 会提供一组环境变量。export 可新增,修改或删除环境变量,供后续执行的程序使用。export 的效力仅限于该次登陆操作。
语法
export [-fnp][变量名称]=[变量设置值]
参数说明:
-f 代表[变量名称]中为函数名称。
-n 删除指定的变量。变量实际上并未删除,只是不会输出到后续指令的执行环境中。
-p 列出所有的shell赋予程序的环境变量。
# export MYENV=7 //定义环境变量并赋值
# export -p
declare -x HOME=“/root“
declare -x LANG=“zh_CN.UTF-8“
declare -x LANGUAGE=“zh_CN:zh“
fc
使用该指令显示历史命令,输入如下命令:
[root@localhost ~]# fc -l -10 #显示10条历史命令
1039 type -a grep
1040 export
1041 history 10
hash命令
显示、添加或清除哈希表>
linux系统下的hash指令:
说明:linux系统下会有一个hash表,当你刚开机时这个hash表为空,每当你执行过一条命令时,hash表会记录下这条命令的路径,就相当于缓存一样。
第一次执行命令shell解释器默认的会从PATH路径下寻找该命令的路径,当你第二次使用该命令时,shell解释器首先会查看hash表,没有该命令才会去PATH路径下寻找。
hash表的作用:大大提高命令的调用速率。
hash的参数:
[root@redhat ~]# hash //输入hash或hash -l 可以查看hash表的内容,我刚开机所以为空
hash: hash table empty
[root@redhat ~]# hash -l
hash: hash table empty
当我执行过2条命令后再看:
[root@redhat ~]# hash //hash表会记录下执行该命令的次数,以及命令的绝对路径
hits command
1 /bin/cat
1 /bin/ls
[root@redhat ~]# hash -l //加参数-l既可以看到hash表命令的路径,也可以看到它的名字,说不定会有别名哦
builtin hash -p /bin/cat cat
builtin hash -p /bin/ls ls
[root@redhat ~]# hash -p /bin/ls bb //添加hash表,可以看到我把ls命令重新写了一遍,改名为bb
[root@redhat ~]# bb //当我执行bb时就是执行ls命令
anaconda-ks.cfg icmp_echo_ignore_aly~ pub.key
dead.letter icmp_echo_ignore_alz~ rpmbuild
icmp_echo_ignore_all~ install.log RPM-GPG-KEY-useradd
icmp_echo_ignore_alw~ install.log.syslog RPM-GPG-KEY-westos
icmp_echo_ignore_alx~ passwd
[root@redhat ~]# hash -t ls //-t参数可以查看hash表中命令的路径,要是hash表中没有怎么办?
/bin/ls
[root@redhat ~]# hash -t df //我没使用过df,执行hash,就会提示找不到该命令
-bash: hash: df: not found
[root@redhat ~]# hash -r //清楚hash表,清楚的是全部的
[root@redhat ~]# hash -l
hash: hash table empty
[root@redhat ~]# hash -l
builtin hash -p /bin/cat cat
builtin hash -p /bin/ls ls
[root@redhat ~]# hash -d cat //清楚其中的某一条
[root@redhat ~]# hash -l
builtin hash -p /bin/ls ls
let
在shell中可以使用let来指示下面是算术表达式,let表达式内变量不用加$
var=1
let "var+=1" 或 let var+=1 这种写法运算符间不能有空格
echo $var
output:
2
这其中的let可以用(())代替,let ″j=i*6+2″等价于((j=i*6+2)), 就像很多的循环中用法一样
注意:let必须是完整的算术表达式,即有等号两边。(())、expr 可以只有等号右边的计算,由$((...))、$(expr ...)、`expr ...` 查看返回结果
var=1
((var++)) 查看结果: echo $(())
echo $var
output:
2
mapfile
bash提供了两个内置命令:readarray和mapfile,它们是同义词。它们的作用是从标准输入读取一行行的数据,然后每一行都赋值给一个数组的各元素。
显然,在shell编程中更常用的是从文件、从管道读取,不过也可以从文件描述符中读取数据。
需要先说明的是,shell并不像其它专门的编程语言对数组、列表提供了大量的操作工具,反而直接操作文本文件更为常见(sed、awk等),所以mapfile用的并不多。
1.语法
mapfile [OPTIONS] ARRAY
readarray [OPTIONS] ARRAY
其中options:
-O INDEX :指定从哪个索引号开始存储数据,默认存储数据的起始索引号为0
-n count :最多只拷贝多少行到数组中,如果count=0,则拷贝所有行
-s count :忽略前count行不读取
-c NUM :每读取NUM行就调用一次"-C callback"选项指定的callback程序
-C callback:每读取"-c NUM"选项指定的NUM行就执行一次callback回调程序
-d string :指定读取数据时的行分隔符,默认是换行符
-t :移除尾随行分隔符,默认是换行符
-u fd :指定从文件描述符fd而非标准输入中读取数据
•如果不指定ARRAY参数,则默认使用数组MAPFILE
•如果不指定"-O"选项,则在存储数据之前先清空数组(如果该数组已存在)
•给定了"-C callback"却没有给定"-c NUM"时,则默认为每5000行调用一次回调程序
•回调程序是在读取给定行数之后,赋值到数组元素之前执行的。所以流程为:"读NUM行-->callback-->赋值"
•每次调用回调函数时,都将调用callback之前的最后一行数据及其对应的索引号作为回调程序的参数。
例如-c 3 -C callback,则会将索引号2和第3行内容,索引号5和第6行内容作为callback程序的参数
•"-t"去除行尾分隔符,一般来说都是换行符。用其他语言编程过的人都知道行尾换行符有多烦心,但对于shell编程来说,倒是无所谓
2.几个示例和注意事项
先创建一个示例用的文件alpha.log,每行一个小写字母,共26行:
$ echo {a..z} | tr " " "
" >alpha.log
$ cat alpha.log
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
读取该文件并将每一行存储到数组myarr中(如果不指定,则存储到默认的MAPFILE数组中)。
$ mapfile myarr <alpha.log
$ echo ${myarr[@]}
a b c d e f g h i j k l m n o p q r s t u v w x y z
$ echo ${myarr[2]}
c
既然是读取标准输入,常见的就有以下几种读取形式:
$ mapfile myarr <alpha.log # 1.输入重定向
$ mapfile myarr < <(cat alpha.log) # 2.进程替换
$ cat alpha.log | mapfile myarr # 3.管道传递
第1、2种写法没什么问题,但第3种写法是有问题的。
$ cat alpha.log | mapfile myarr1
$ echo ${#myarr1[@]}
0
从结果中可以看到,myarr1根本就不存在。为什么?我在shell中while循环的陷阱中给出过解释。这里简单说明一下,对于管道组合的多个命令,
它们都会放进同一个进程组中,会进入子shell执行相关操作。当执行完毕后,进程组结束,子shell退出。
而子shell中设置的环境是不会粘滞到父shell中的(即不会影响父shell),所以myarr1数组是子shell中的数组,回到父shell就消失了。
解决方法是在子shell中操作数组:
$ cat alpha.log | { mapfile myarr1;echo ${myarr1[@]}; }
readarray命令
用于从标准输入或选项“-u”指定的文件描述符fd中读取文本行,然后赋值给索引(下标)数组array,如果不指定数组array,则使用默认的数组名MAPFILE。
下面解释readarray命令中各选项的作用。
“-n count”:复制最多count行,如果count为0,则复制所有的行。
“-O origin”:从下标位置origin开始对数组赋值,默认为0。
“-s count”:忽略开始读取的count行。
“-t”:删除文本行结尾的换行符。
“-u fd”:从文件描述符fd中读取文本行。
“-C callback”:每当读取选项“-c”指定的quantum行时(默认为5000行),就执行一次回调callback。
下面以简单的例子说明readarray命令的用法:
$ readarray foo
hello world
hello bash
^C
$ echo ${foo[@]}
hello world hello bash
$ echo ${#foo[@]}
2
hanjunjie@hanjunjie-HP:~$ echo ${foo[0]}
hello world
hanjunjie@hanjunjie-HP:~$ echo ${foo[1]}
hello bash
set 用来显示本地变量
env 用来显示环境变量
export 用来显示和设置环境变量
set 显示当前shell的变量,包括当前用户的变量
env 显示当前用户的变量
export 显示当前导出成用户变量的shell变量
每个shell有自己特有的变量(set)显示的变量,这个和用户变量是不同的,当前用户变量和你用什么shell无关,
不管你用什么shell都在,比如HOME,SHELL等这些变量,但shell自己的变量不同shell是不同的,比如BASH_ARGC, BASH等,
这些变量只有set才会显示,是bash特有的,export不加参数的时候,显示哪些变量被导出成了用户变量,因为一个shell自己的变量可以通过export “导出”变成一个用户变量
[root@linux ~]# aaa=bbb
[root@linux ~]# echo $aaa
bbb
[root@linux ~]# set|grep aaa
aaa=bbb
[root@linux ~]# env|grep aaa
[root@linux ~]# export aaa
[root@linux ~]# env|grep aaa
aaa=bbb
Bash 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 |
等待指定的进程完成,并返回退出状态码 |