进程是一条命令在linux 上的执行。用户登录时启动的shell ,也是一条命令,或者进程。但用户在命令行中输入一个linux 工具名时,就启动了一个进程。当用户运行一个shell 脚本的时候,系统创建另一个shell 进程,并为脚本中每行命令创建另外的进程。根据用户调用脚本方式的不同,脚本既可以由当前shell 运行(利用'.' 命令),也可以由当前shell 的一个子shell 运行。后一种方式更加普通。如果用户输入一条内置命令如cd ,此时系统并不会创建进程。
>sleep 10 &
>ps -f
>sleep 10 &
>ps -f
选项'-f' 调用ps 时,ps 显示每个进程信息的完整列表。
>pstree
>pstree -p
执行命令
fork 和 sleep 当用户向shell 中输入一行命令时,shell 创建一个子进程来执行这条命令。当子进程执行该条命令期间,父进程转入睡眠状态,等待被唤醒。当子进程的命令执行完毕之后,它将通过其退出状态通知其父进程自己执行成功或失败,然后消逝。父进程被唤醒,然后提示用户输入另一个命令。
后台进程 当通过在命令结尾处加上 & 符号将进程置于后台运行时, shell 创建一个子进程,但是 shell 并不睡眠,也不等待子进程运行完毕。子进程在后台运行独立于他的父进程。
Shell 运行内置命令的时候, shell 并不创建新进程。
默认情况下,变量只适用于进程本地范围。当进程创建一个子进程时,并不将变量传递给它的子进程。使用 export 内置命令可以将变量设为全局而允许子进程的访问。
命令历史机制:它维护了用户最近发出的命令行的列表,这为重新执行列表中任何事件提供了一个便捷的方式。用户也可通过查看历史记录修正错误。
>history | less
>history | tail
>history 10
HISTSIZE 在一次会话期间保存的最大事件数目
HISTFILE 历史文件的位置
HISTFILESIZE 会话之间保存的时间最大数目
>echo $HISTSIZE $HISTFILE $HISTFILESIZE
重新执行和编辑命令 可以重新执行历史列表中的任何事件。内置命令 fc 使用感叹号 使用 Readline 库
fc 显示编辑和重新执行命令
fix command 修改命令,可用来显示历史列表内容,并可以编辑和重新执行前面的命令。
显示历史列表中的命令,不带参数将显示最近16 个命令。
>fc -l
>fc -l [first [last]]
>fc -l 100 200 | less
>fc -l vim apropos | less
>fc -l 1027 1027
编辑和重新执行前面的命令:
> fc [-e editor] [first [last]]
'-e' 调用fc 时,如果后面还带某个编辑器的名字,那么fc 将调用该编辑器,并将事件放到工作缓冲区中。如果不带first 和last 参数,fc 默认最近命令。如果设置了FCEDIT ,那么没有必要在命令行指定使用的编辑器。
使用vim 编辑最近使用的命令
>fc -e vi
设置FCEDIT 并利用fc 编辑最近使用的命令,使用的编辑器将依照FCEDIT 的设置。
>export FCEDIT=/usr/bin/emacs
>fc
fc 调用编辑器编辑指定命令
>fc 21
调用编辑器编辑以字母vim 开头的最近事件到206 之间的事件出
>fc vim 206
fc 利用编辑器编辑相关命令,退出编辑器的时候, shell 将执行存放在编辑器缓冲区内的任何内容,如果需要不执行命令,那么退出之前需要删除缓冲区中的所有内容。
>echo $PS1
>PS1=”/u@/h:/w/!$”
>ls -li
>fc -e vi
>whereis vim
>export FCEDIT=”/usr/bin/vim”
>fc 509
>fc 500 509
不调用编辑器而重新执行命令
调用 fc 时带上 '-s' 选项那么将跳过编辑阶段而重新执行该命令
>fc-s 509
>fc -s
使用感叹号引用事件
! 除非后面紧接着空格符 换行符 =或者( ,否则立即开始某个历史事件。
!! 前一个命令
!n 历史列表中编号为 n 的命令
!-n 往前第n 条命令
!string 最近以 string 开始的命令行
!?string[?] 包含sting 的最近命令行
!# 当前命令 目前正在输入的部分
!{event} !{-3}3 后跟着3 的第3 个最近执行的命令
>echo example*
>!!
>readelf example
>!! | less
>!515
>history 10
>!whereis
>!?example
字标志符
字标志符指定了事件中的某个字或者一组字。这些字的编号从 0 开始 ( 命令行的第一个字通常是命令名 ) 。为了指定前一个事件中的某个字,在事件标识符 (!107) ,后面跟一个冒号和一个表示该字在这个命令中的标号。
n 第n 个字;字0 为命令名
^ 第一个字,紧跟命令之后
$ 最后那个字
m-n 从编号m 到n 的所有字
n* n 到最后
* 除命令名以外所有字
>echo $PS1
>PS1='/u@/h:/w-/!/$'
>echo apple grape orange pear
>echo !109:*
>echo !109:2-4
>echo !109:$
>!109
>!109:0-$
>cd example
>cat example.sh
>vim !$
!$ 表示前一个命令的最后一个字。
替换修饰符 语法如下:
[g]s/old/new
>cat exampleji.sh
>!!:s/exampleji/example
快速替换符是^ 。可以用它来执行最近事件同时改变该事件的内容。
>cat exampleji.sh
>^exampleji^example
别名:别名是一种名称, shell 将其翻译成另一个名字或者命令。 定义别名语法如下:
>alias [name[=value]]
如果value 中含有空格或者制表符,需要利用引号将其括起来。别名并不替换自己,这避免了在处理如下别名时可能存在的递归。
>alias ls='ls -F'
用 unalias 删除别名。 不带参数的alias 将列出当前所有别名的列表。
>alias
单双引号的区别:value 用双引号引起来,那么当创建别名时,value 中任何变量都将扩展。如果将value 用单引号括起来,那么在使用该别名之前变量都不会被扩展。
>alias dirA=”echo working directory is $PWD”
>alias dirB='echo working directory is $PWD'
>alias dirA dirB
>cd ..
>dirA
>dirB
shell 只检查那些简单的,未经引用的命令,看其是否为别名。那些相对或者绝对路径名的命令和引用命令都不被检查。在命令前加反斜杠或者执行命令时使用绝对路径,将不进行别名替换。
>alias
>ls
>/ls
>whereis ls
>/bin/ls
别名示例:
>alias r='fc -s'
>alias l='ls -ltr'
>alias zap='rm -i'
>ls
>r
>l
>zap example*
>alias ls='ls -F'
>alias ll='ls -l'
>ll ( 结果同ls -F -l)
>unalias r l zap ll
>alias
>unalias dirA dirB
函数 shell 函数类似于 shell 脚本,里面存放了一系列可在后期执行的命令。 然而因为shell 函数存放在RAM 而不是磁盘文件中,因此shell 访问函数的速度要比访问脚本速度快得多。shell 函数的执行和调用在同一个shell 中进行的。可以使用 unset 删除 shell 函数。如果 shell 变量和函数同名则 unset 删除 shell 变量,如果再次用相同的名字调用 unset ,就会删除这个函数。
声明函数语法如下:
>[function] function-name() RETURN
{ RETURN
command RETURN
} RETURN
>function whoson() RETURN
{ RETURN
date RETURN
echo “users currently logged on” RETURN
who RETURN
} RETURN
>whoson
>function whoson2() { who; date; } RETURN
>cat >whoson.sh RETURN
date RETURN
echo “users currently logged on” RETURN
who RETURN
COUNTROL+D
>chmod u+x whoson.sh
>./whoson.sh
>set | grep whoson
>function echo_arg() RETURN
{ RETURN
echo “The arg is $1 && $2” RETURN----”” 不可省略
echo The arg is $1 $2 $3
} RETURN
>echo_arg arg1 arg2 arg3
>unset echo_arg whoson
控制shell 的特性:利用 set 和 shopt 控制 shell 特性
有两种类型的命令行选项,短选项和长选项,短选项由一个连字符后面跟一个字母构成,而长选项由两个连字符后面跟多个字母构成。同一命令行上的长选项必须放在短选项之前。
--: 在命令行中表示选项的结束,后面的字将作为参数,即使他们以连字符开头。
shell 特性,可以通过打开或者关闭bash 的功能的方式来控制他的行为。内置命令set 控制一部分功能,shopt 控制另一部分功能。
set +-o 开启和关闭 shell 特性,‘ -o’ 开启特性, '+o' 关闭特性。
>set -o noclobber
>set -o vi
>set +o vi
>set -o
>set +o
>shopt -s
>shopt -u
>shopt -s dotglob
>shopt -u dotglob
'-s' 开启特性, '-u' 关闭特性。
处理命令行:
无论是采取交互式还是运行shell 脚本,bash 在处理命令行之前都需要读取该命令行,即bash 在处理一条命令之前总是读取至少一行命令。如果bash 知道某条命令跨越多行,则在处理该命令之前将读取整条命令。
>echo 'this is RETURN
echo .e.g' RETURN
>function hello() RETURN
{ RETURN
echo hello RETURN
} RETURN
在读取一条命令之后,bash 对该命令应用历史扩展和别名扩展。
历史扩展:bash 将历史命令转换到可执行命令行的过程。对于交互式 shell 历史扩展默认是开启的,使用 'set +o histexpand' 可将其关闭;而非交互式 shell(shell 脚本 ) 不能使用历史扩展。
>!!
>!108
别名替换:别名将一个简单命令的第一个字替换为一个字符串。默认情况下shell 的别名特性是开启的,在非交互式shell 中则是关闭的。使用'shopt -u expand_alias' 可将其关闭。
>alias ls='ls -F'
>ls
>alias
>unalias ls
解析和扫描命令行:在处理完历史命令和别名之后,bash 并不立即执行命令。首先是将命令行解析成标记或者字,然后shell 扫描每个标记,查找特殊字符和模式,shell 需要对他们采取某些动作。进行命令行扩展。
命令行扩展:
在交互式用法和非交互式用法中,shell 在将命令行传递给被调用程序之前均使用命令行扩展。按照下列顺序进行命令行扩展:
花括号扩展
代字符扩展
参数扩展和变量扩展
算数扩展
命令替换
分词
路径名扩展
处理替换
引用删除:在处理完命令行扩展后,bash 将不是扩展结果的单引号 双引号和反斜杠从命令行中删除。称为引用删除。
扩展顺序
bash 执行命令行扩展的步骤的顺序会影响到命令的解释。
>SENDIT=”>/tmp/saveit”
>echo xxx $SENDIT
命令的运行结果是:shell 并不进行输出的重定向,他在计算变量的值之前识别输入输出的重定向。其结果是将'>/tmp/saveit' 最为参数传递给echo 。
切记双引号和单引号将使 shell 在进行扩展时表现出不同的行为。双引号允许参数和变量的扩展,但是抑制其他类型的扩展,而单引号则抑制所有的扩展。
花括号扩展:当不能利用路径名扩展时,它为指定文件名提供了一个便利方式。尽管花括号扩展主要用于指定文件名,该机制还可以用来产生任意字符串。
花括号扩展在交互式和非交互式 shell 中都是默认开启的。 可使用'set +o braceexpand' 关闭。
>ls
>echo example{1,2,3}.sh
当有较长的前缀或者后缀时,花括号扩展很有用,避免多次输入长的路径名
>cp /usr/local/src/c/{main,f1,f2,tmp}.c .
>mkdir example{A,B,C}
>ls -F
>rmdir example{A,B,C}
>mkdir example[A-C]
>ls
>rmdir example[A-C]
带字符扩展:
当代字符出现在命令行某字的起始处时,它就属于一个特殊的字符。bash 将后面的字符串( 在第一个斜杠之前或者如果没有斜杠,到达字的末尾) 作为一个可能的登录名。如果可能的登录名是空的,那么shell 将用HOME 变量的值取代这个代字符。
>echo $HOME
>echo ~
>echo ~/example.sh
>cp ~/example/example.sh .
如果后面的字符串构成一个合法的登录名,那么shell 将用与该登录名相对应的主目录路径名来替换这个代字符和名字,如果他不为空且不是一个合法的登录名,shell 将不进行任何替换:
>echo ~root
>echo ~terry
>echo ~nouser
参数扩展和变量扩展:
后面没有开放的圆括号的 $ 将引入参数或者变量扩展。参数包括命令行,或者位置参数和特殊参数。变量包括用户创建的变量和关键字变量。
如果参数和变量用单引号引起来或者开头的$ 符号被转义,那么这些参数和变量将不会扩展。
算术扩展:
shell 计算算术表达式的值并用该值替换表达式,这就是算数扩展。
$((expression))
不需要在expression 中的变量名前加上$ 符号。
>cat >age_check.sh RETURN
#!/bin/bash RETURN
echo -n “How old are you?” RETURN
read age RETURN
echo “Wow, in $((60-age)) years, you'll be 60!” RETURN
CONTROL+D
>chmod u+x age_check.sh
>./age_check.sh
>x=2 y=4
>echo $((2*$x+5*$y))
>echo $((2*x+5*y))
>unset x y
>x=3+6 y=4+7
>echo $((x+y))
>echo $x
>x=$((3+6))
>echo $x
>unset x y
命令替换:
利用命令的输出来替换该命令。
$(command)
shell 将在子shell 中执行command 并用command 的标准输出取代command ,连同两边的标点。
>echo $(pwd)
>echo “You are using the $(pwd) directory.”
>echo $(ls -l)
分词:
参数和变量扩展,命令替换和算术扩展的结果都可以作为分词的候选者,bash 利用IFS 定义的每个字符作为可能的分割符,将这些候选着划分成字或者标记。
路径名扩展:
路径名扩展又称为文件名生成或者文件名补全,它表示解释模糊文件引用以及替换合适文件名的列表的过程。如果 bash 在处理模糊文件引用时,找不到任何匹配该模式的文件,那么带有模糊文件引用的标记将被保留。
>echo example*
>echo *
>echo example?
>echo .*
>ls .*
>ls *
>echo nofile*
引用:
用双引号把参数括起来将使 shell 抑制路径名扩展和所有除参数扩展和变量扩展之外的其他扩展。而用单引号把参数括起来会抑制所有类型的扩展。
>echo “example* $HOME”
>echo 'example* $HOME'
>echo example* $HOME
>a=example*
>echo $a
>a='example*'
>echo $a
>echo “$a”
>echo '$a'
>unset a