精通shell编程第二版Sriranga Veeraraghavan著 卢涛notes

来源:互联网 发布:江苏网络协会 编辑:程序博客网 时间:2024/05/21 17:58
精通shell编程 第二版 Sriranga Veeraraghavan著 卢涛译notes:

1. 工具如who和date等是以文件的形式存储在磁盘上的,只有在被请求时才加载到内存,加载后会在内存存在一段时间,以便下次执行更快。

2. shell和date一样,也是存储在磁盘上的程序,shell在你登陆系统时就加载了,然后一直驻留在内存里,直到你退出系统。

3. bash初始化过程:
Bourne Again shell (bash )的初始化过程是这样的:
1.bash 检查文件/etc/profile 是否存在
2. 如果存在,bash 就读取该文件,否则,跳过
3.bash 检查主目录下的文件.bash_profile 是否存在。
4. 如果存在,bash 就读取該文件,否则,跳过
5.bash 检查主目录下的.bash_login 是否存在。
6. 如果存在,bash 就读取该文件,否则,跳过
7.bash 检查主目录下的文件.profile 是否存在
8. 如果存在, bash 就读取该文件,否则,跳过。
这些文件主要是设置一个完整的工作环境
这些步骤都执行完后,就出现提示符了, bash 默认提示符是 $.
当登录bash退出时,它 将执行~/.bash_logout文件中的命令。

系统启动后运行的第一个进程是init,它的进程标识符PID是1。init派生出一个getty进程。该进程负责打开终端端口,提供stdin的来源,stdout与stderr的去向。接下来执行的是/bin/login程序。提示用户输入口令,加密并验证用户输入口令,设置初始化环境,启动用户的登陆shell,即bash。

bash有很多的启动文件,这些文件是可以执行source命令。登陆shell时,先对系统初始化文件/etc/profile执行source命令。接下来,查看用户主目录下有没有.bash_profile文件,如果有就对它执行source命令。.bash_profile先设置用户的别名和函数,再设置用户特定的环境变量和启动脚本。

如果没有.bash_profile文件,就查看有没有.bash_login文件,并对其执行source命令。如果也没有.bash_login文件,就查看.profile文件,并执行source命令。

bash处理初始化文件的顺序:
if /etc/profile exists,source it,
    if ~/.bash_profile exists,source it,
        if ~/.bashrc exists,source it,
    else if ~/.bash_login exists,source it,
    else if ~/.profile exists,source it.

ref: http://blog.chinaunix.net/uid-20507571-id-1660969.html

(1)/etc/profile

全局(公有)配置,不管是哪个用户,登录时都会读取该文件。
这个文件会调用/etc/profile.d目录下的脚本文件,所有这些文件都是开机自动启动,所以不要轻易修改,有一次,我将自己的一个脚本添加进入这个目录,由于脚本有问题,导致开机不断地进入帐号又退出,最后的最后,我只能使用livecd修复了.

(2)/ect/bashrc

Ubuntu没有此文件,与之对应的是/ect/bash.bashrc

它也是全局(公有)的

bash执行时,不管是何种方式,都会读取此文件。

(3)~/.profile

若bash是以login方式执行时,读取~/.bash_profile,若它不存在,则读取~/.bash_login,若前两者不存在,读取~/.profile。

另外,图形模式登录时,此文件将被读取,即使存在~/.bash_profile和~/.bash_login。

(4)~/.bash_login

若bash是以login方式执行时,读取~/.bash_profile,若它不存在,则读取~/.bash_login,若前两者不存在,读取~/.profile。

(5)~/.bash_profile

Unbutu默认没有此文件,可新建。

只有bash是以login形式执行时,才会读取此文件。通常该配置文件还会配置成去读取~/.bashrc。

(6)~/.bashrc

当bash是以non-login形式执行时,读取此文件。若是以login形式执行,则不会读取此文件。

(7)~/.bash_logout

注销时,且是longin形式,此文件才会读取。也就是说,在文本模式注销时,此文件会被读取,图形模式注销时,此文件不会被读取。

下面是在本机的几个例子:

1. 图形模式登录时,顺序读取:/etc/profile和~/.profile

2. 图形模式登录后,打开终端时,顺序读取:/etc/bash.bashrc和~/.bashrc

3. 文本模式登录时,顺序读取:/etc/bash.bashrc,/etc/profile和~/.bash_profile

4. 从其它用户su到该用户,则分两种情况:

(1)如果带-l参数(或-参数,--login参数),如:su -l username,则bash是lonin的,它将顺序读取以下配置文件:/etc/bash.bashrc,/etc/profile和~/.bash_profile。

(2)如果没有带-l参数,则bash是non-login的,它将顺序读取:/etc/bash.bashrc和~/.bashrc

5. 注销时,或退出su登录的用户,如果是longin方式,那么bash会读取:~/.bash_logout

6. 执行自定义的shell文件时,若使用“bash -l a.sh”的方式,则bash会读取行:/etc/profile和~/.bash_profile,若使用其它方式,如:bash a.sh, ./a.sh,sh a.sh(这个不属于bash shell),则不会读取上面的任何文件。

7. 上面的例子凡是读取到~/.bash_profile的,若该文件不存在,则读取~/.bash_login,若前两者不存在,读取~/.profile。

ref: http://www.linuxidc.com/Linux/2008-09/15347.htm

4. 非交互模式:当shell被用来执行一段shell脚本的时候,如/bin/bash 1.sh

5. 常见操作:
mkdir test/test/good/ 创建多层目录,即使当前目录下无test目录;
ll -d dir 只显示目录属性,而不显示它的内容;
cp -r dir1 dir2...  destdir 拷贝目录;
mv dir1 dir2 file destdir 移动目录和文件;
rmdir只能用来删除空的目录;
rm -r dir删除目录,为了更加安全添加-i选项;
printf也能用来输出,如printf "hello world\n",其可以用来格式化输出,printf命令在/usr/bin目录下;
多个命令重定向使用{},如: { date; uptime; who; } > mylog
file filename查看文件类型等信息
chown user:group file

6. 管道中的每个命令都是作为一个独立的进程运行的,管道的退出状态就是最后一个命令的退出状态。


7. /dev/null是unix系统中被用来丢弃输出信息的特殊文件,如果将一个命令的输出重定向到/dev/null,那么输出将被丢弃,不显示,如果用cat命令显示/dev/null文件的内容到一个文件,该文件的内容将被擦除,cat /dev/null > file,file文件大小会变成0.

8. 硬链接:如果原文件被删除,那么原文件的条目被删除了,但是其内容仍然保留在磁盘上,直到指向该文件的硬链接全被删除;
符号链接:是存储另一个文件的路径名的特殊文件,可以存储相对路径,它是相对于符号链接所在的目录而不是当前的工作目录。

9. 无论目录test是何种权限,root用户永远可以访问该目录,root用户任何操作都可以执行,目录文件的权限中的x位用来授权对目录的访问,如果该位没有设置,则读和写权限将不会产生效果,即若只拥有目录test的读权限,则无法用ls命令查看该目录下的文件及其属性,若只拥有目录的写权限,则也无法在该目录下增加和删除文件,只拥有对该目录的执行权限,不能查看或增加,删除该目录文件,但可以让用户运行该目录下的可执行文件,只有拥有x权限,才能cd到该目录下。

10. SUID: SUID位就在文件属主的执行权限位,文件的SUID被激活,且执行位也被设置,则文件属主执行权限位的位置上为小写s,若执行位没有被设置,则为大写S,当你执行一个设置了SUID位的程序时,当你执行该程序时,你就继承了该程序的属主的权限,如普通用户修改密码,密码保存在/etc/shadow文件中,对普通用户无修改权限。
SGID:如果一个目录设置了SGID位,任何加到该目录下的新文件的组为该目录的所属组。

11. 普通用户只能改变他们自己文件的属主;

12.
linux提供的fg和bg命令,可以让我们轻松调度正在运行的任务

  假如你发现前天运行的一个程序需要很长的时间,但是需要干前天的事情,你就可以用ctrl-z挂起这个程序,然后可以看到系统的提示:
[1]+ Stopped /root/bin/rsync.sh

然后我们可以吧程序调度到后台执行:(bg 作业号)
#bg 1
[1]+ /root/bin/rsync.sh &

用jobs命令查看任务
#jobs
[1]+ Running /root/bin/rsync.sh &

把它调回到控制台运行
#fg 1
/root/bin/rsync.sh
 这样,你这控制台上就只有等待这个任务完成了。

fg、bg、jobs、&、 ctrl+z都是跟系统任务有关的,学会了相当的实用

一、&最经常被用到
这个用在一个命令的最后,可以把这个命令放到后台执行

二、ctrl + z
可以将一个正在前台执行的命令放到后台,并且暂停

三、jobs
查看当前有多少在后台运行的命令

四、fg
将后台中的命令调至前台继续运行
如果后台有多个命令,可以用fg %jobnumber将选中的命令调出,%jobnumber是通过jobs命令查到的后台正在执行的命令的序号(不是pid)

五、bg
将一个在后台暂停的命令,变成继续执行
如果后台有多个命令,可以用bg %jobnumber将选中的命令调出,%jobnumber是通过jobs命令查到的后台正在执行的命令的序号(不是pid)


13. $0被执行命令的名字,对于shell脚本来说,这就是调用它的路径
#1.sh
#!/bin/bash

echo $0
如:bash 1.sh执行,输出1.sh,bash ./1.sh输出./1.sh
$!:最后一个后台命令的进程号

14. 变元和选项:
ls -aF good test 变元是-aF,good,test, 第一变元是-aF,第二变元是good,选项是-aF

处理变元例子:
#!/bin/bashUSAGE="Usage: $0 [-c|-t] [file|directory] list"output=""for (( i=2; ${i}<=${#}; i=$(( ${i}+1 )) ))do    eval j=\$$i    output="${output} ${j}"doneecho ${output}case ${1} in    -t) TARGS="-tvf ${output}" ;;    -c) TARGS="-cvf mytar.tar ${output}" ;;    *) echo "${USAGE}"; exit 0 ;;        esactar $TARGS执行:mytar -c 1.txt 2.txtmytar -t mytar.tar改进版:#!/bin/bashUSAGE="Usage: `basename $0` [-c|-t] [files|directories]"if [ $# -lt 2 ]; then    echo "${USAGE}";    exit 1;ficase "$1" in    -t) shift    TARGS="-tvf";    for i in "$@"    do        if [ -f "$i" ]; then            FILES=`tar ${TARGS} "$i" 2>/dev/null`            if [ $? -eq 0 ]; then                echo ; echo "$i"; echo "${FILES}"            else                echo "ERROR: $i not a tar file. "            fi        else            echo "ERROR: $i not a file. "        fi    done    ;;    -c) shift    TARGS="-cvf"    tar ${TARGS} archive.tar "$@"    ;;    *)    echo "${USAGE}"    exit 0    ;;esacexit $?---------------------------------------------basename:获得文件名或目录名#basename /usr/bin/sh 输出sh#basename /usr/bin/1.txt 输出1.txtshell中选项处理命令:getopts和getopt例子:http://www.cnblogs.com/FrankTan/archive/2010/03/01/1634516.html--------------------------------------------------------------------------------



15.函数:
函数定义:
name()
{
list;
}

 shell函数一旦定义了,函数将作为一个合法的命令,可以在所有后继shell中使用,如:
#lsl(){ ls -l; }
#( lsl ) 在子shell中也可以使用
在一个脚本中定义的函数可以在那个脚本和所有由这个脚本生成的子shell中使用。

函数链接:一个函数中调用另外一个函数的过程;

别名:
alias name="cmd",name为cmd的别名
unalias name 取消设置

取消函数:
unset name

stty -echo: 关闭输入回显
stty echo:开启回显

#!/bin/bash

perFunc()
{
        pear=2;
        echo "In perFunc(): pear is ${pear}" #输出2
}

perFunc

echo "outside of perFunc(): pear is ${pear}" #也输出2

默认的,除了与函数参数关联的特别变量,其他所有变量都有全局范围

局部变量:
local var1[-var11]...
如:
local fruit1 fruit2=banana 定义了两个局部变量,且fruit2为banana

#!/bin/bash

perFunc()
{
        local pear=2;
        echo "In perFunc(): pear is ${pear}" #输出2
}

perFunc

echo "outside of perFunc(): pear is ${pear}" #无输出

递归:#反序显示参数#12.sh:#!/bin/bashreverse(){    if [[ $# > 0 ]]; then        local arg="$1"        shift        reverse "$@"        echo "${arg}"    fi}reverse "$@"#bash 12.sh 1 2 3 输出:321



注:[[]]和(( ))中都可以使用普通的运算符

返回值:使用return或exit

shell中字段区分符IFS默认为空格和tab键,可以改变它,如下:
#dir.sh: 输出PATH中的内容 PATH中字段是以冒号分隔,所以为了提取每个字段,需要修改IFS

#!/bin/bash

dirs()
{
    OLDIFS="${IFS}"
    IFS=:

    for i in ${PATH}
    do
        echo "${i}"
    done

    echo
    IFS="${OLDIFS}"
}

dirs

---------------------------------------------------
文本过滤器:head,tail,grep,sort,uniq, tr
tr 'set1' 'set2' file, 将file中的所有set1字符替换为set2

例子:
1.txt:
this is sf sd, ; :
sfth, im s sf,
sf,good hello :world,
 s

执行:
tr '!?":;\[\]{}(),.\t\n' ' ' < 1.txt | tr 'A-Z' 'a-z' | tr -s ' ' | tr ' ' '\n' | sort
输出:
good
hello
im
is
s
s
sd
sf
sf
sf
sfth
this
world
tr '!?":;\[\]{}(),.\t\n' ' ' < 1.txt | tr 'A-Z' 'a-z' | tr -s ' ' | tr ' ' '\n' | sort | wc -l 输出13 单词数量

按单词出现次数高低排序:
tr '!?":;\[\]{}(),.\t\n' ' ' < 1.txt | tr 'A-Z' 'a-z' | tr -s ' ' | tr ' ' '\n' | sort | uniq -c | sort -rn


16. shell正则表达式过滤文本:
sed:
用法:sed '/pattern/p' file
sed删除行:sed '/pattern/d' file
sed执行替换:sed '/pattern1/s/pattern2/pattern3/' file 用匹配规则pattern1的每一行中的pattern2被替换为pattern3,pattern1若
被忽略,则对每一行处理
sed执行全局替换:s命令的默认属性:一行中只执行一次替换,为了执行多次,使用g操作
如:sed 's/eqal/equal/g' nash.txt 替换所有eqal为equal
s命令提供了&操作,使得我们在pattern3中可以重复使用匹配字符串pattern2
如:
fruit.txt:
fruit   price/lbs
banana 0.89
paech 0.79
目的:在价格前加上$
实现:sed 's/*[0-9][0-9]*\.[0-9][0-9]$/\$&/ ' fruit.txt


例子:
1.txt:
chapter 1
chapter 20
chapter 00 introducton
chapter 101

sed -n '/^chapter [1-9]*[0-9]$/p' 1.txt输出:
chapter 1
chapter 20
注:上面末尾的$表示要以0-9结尾即可

sed -n '/^chapter [0-9]*[0-9]$/p' 1.txt 输出:
chapter 1
chapter 20
chapter 101

2.txt:
ch01dadoc
ch02exdoc
chdoc

ch01.doc
ch02.doc
ch.doc

sed -n '/ch.*doc/p' 2.txt 输出:
2.txt的全部内容
注:.*表示匹配任意

3.txt:
abi1.sfsga
aa.21dfa
ab23.da
abcda

sed -n '/a*a/p' 3.txt 输出所有3.txt的内容

把$$的值附加到文件的末尾是一个常用的为临时文件唯一命名的方法

默认/the/会匹配the,there,为了单独匹配the,可使用空格/the /

可以使用多重sed命令
在管道中使用sed

fruit_prices.txt:
Fruit       price/lbs     Quantity
Banana        $0.89        100
Peach         $0.79        65
Kiwi          $1.5         22
Pineapple     $1.29        35
Apple         $0.99        78

awk:
用法:awk 'script' files
files为一个或多个列表,scirpt为如下形式:
/pattern/ {actions}, 若pattern被省略,awk将对每一行处理
例子:awk '{print; }' abc.txt 打印一个文件中的行


awk自动把输入分成字段,默认分隔符为tab和空格,一行被读入时,awk把它第一个字段放$1,$2=第二字段...
awk '{print $1 $3;}' fruit_prices.txt
awk '{print $1,$3;}' fruit_prices.txt在$1和$3之间打印一个空格,如下:
Fruit Quantity
Banana 100
Peach 65
Kiwi 22
Pineapple 35
Apple 78


awk '{printf "%=14s %s\n", $1, $2; }' abc.txt 也可以用printf

awk执行指定模式的操作,可以使用比较操作符,混合表达式,next命令,使用标准输入作为输入
awk变量,流程控制,循环,BEGIN,END


17. 内建命令:
eval,冒号:命令
type cmd1 cmd2..,告知命令的绝对路径
sleep n 暂停给定的秒数
find命令
xargs命令
expr命令 执行简单的整数运算
如:expr 8/3

18. shell解决具体问题:




1. 判断磁盘已用空间:
du命令:判断一个目录所占用的磁盘空间
-s: 代表总和
-k: 代表千字节
如:
du -sk /home/pub 输出/home/pub目录所占用的磁盘空间
8000   /home/pub  占用了8000KB

判断磁盘空闲空间:
df -k dir ,输出包含目录dir所在分区的信息


18. 用shell解决具体问题:
两个实例:启动脚本和维护地址簿
代码在书上


0 0