shell内建命令
<<<作用 * 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 " " "\n" >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
命令 | 说明 |
---|---|
: | 扩展参数列表,执行重定向操作 |
. | 读取并执行指定文件中的命令(在当前 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 | 等待指定的进程完成,并返回退出状态码 |