Notes of Basic Linux Part-3 bash, shell, and Network Security

Notes of Basic Linux Part-3 bash, shell, and Network Security

2012-03-03. Category & Tags: Vbird, Linux, Commands, 鸟哥

see also jlevy/the-art-of-command-line (multi-language)

Organised 2017 March.
Notes from 2012 April.
多年前(2012)写的“鸟哥的Linux 私房菜”学习笔记。 - chapters 10~13 -ing
基本的命令应该没什么变化吧。
原网站: 繁体 0160startlinux

Crtl + Alt + [F1]~[F6]
# 共有六个, tty1 ~ tty6 ,切换的方式为 Crtl + Alt + [F1]~[F6],其中, [F7] [F8] 为图形接口的使用。
man xxx => xxx(number) manual 中的命令级别 number 代表意义:

  • 1:一般用户可以使用的命令或可运行文件案;
  • 5:一些配置文件的文件内容格式;
  • 8:系统管理员能够使用的管理命令。

第十一章、认识与学习BASH #

认识 BASH 这个 Shell #

硬件、核心与 Shell #

// Hardware <=> Kernel  <=> Shell,KDE,App  

为何要学文字接口的 shell #

// 通用,效率高  

系统的合法 shell 与 /etc/shells 功能 #

/etc/shells #所有 shells  
/etc/passwd	// 每个用户的默认 shell  

Bash shell 的功能 #

// history, tab, alias, job control, scripts, wildcard  
// 当前登录的history在内存,注销时才会写入用户history文件。  

Bash shell 的内建命令: type #

type  [-tpa] commandName  
[ ]	:无参,说明式回显 commandName 类型,还和使用者语言有关  
-t  :单词式回显:  
      file    :外部命令  
      alias   :别名  
      builtin :bash内建  
-p  :如果后面接的 commandName 为外部命令,会显示完整文件名;  
-a  :会由 PATH 变量定义的路径中,将所有含 commandName 字符串的命令都列出来,包含 alias  

命令的下达 “" #

//  \ 仅跳脱『紧接着的下一个字符』  

Shell 的变量功能 #

什么是变量 #

变量的取用与配置 (echo, 变量配置守则, unset) #

// 变量的取用: echo  
    echo $varName  
    echo ${varName}  
    //  $varName 是 ${varName} 的简化。  
// 变量配置守则  
    // 赋值等号两边不能有空格  
    // 不能以数字开头  
    // 命令嵌套 eg : 
        ls -l `locate crontab` 
        // 反单引号 `命令` 或  $(命令) 等价 (推荐后者,因为容易识别,但是不要忘了那个$符号)。  
unset varName #注意:此处没有'$'  

环境变量的功能: env, export, declare #

// env 与 export 可观察环境变量,其中 export 可以将自定义变量转成环境变量;  
env #查看所有env变量  
    HOME #cd ~
    SHELL  
    HISTSIZE  
    MAIL  
    PATH  
    LANG #语系  
    RANDOM #/dev/random ; 0~32767 之间  
        declare -i number=$RANDOM*10/32768 ; echo $number #0~9 之间  
set #查看目前 bash 的所有变量 (环境变量+自定义变量);  
    //  set 还可以配置命令环境,见4.4  
    echo $$  
        // PID  
    echo $?  
        // 前一个命令回传值  
    cd - 或 cd $OLDPWD 
        // 前一个工作目录  
    PS1='[\u@\h#\#]\W\$'  
        // 字符界面行首提示符  
    \d :『星期 月 日』,如:"Mon Feb 2"  
    \H :完整的主机名『www.vbird.tsai』  
    \h :主机名在第一个小数点之前的,如『www』  
    \t :24 小时HH:MM:SS  
    \T :12 小时HH:MM:SS  
    \A :24 小时HH:MM  
    \@ :12 小时 am/pm  
    \u :当前账号,如『root』;  
    \v :BASH版本,如 3.2.25(1),仅取『3.2』显示  
    \w :完整pwd (除了~之外);  
    \W :当前所在文件夹 #利用 basename 函数取得工作目录名称  
    \# :命令的顺序编号  
    \$ :root 为 '#' ,否则 '$' 
    OSTYPE #linux-gnu  
    HOSTTYP #i686 #硬件 类型  
    MACHTYPE #i686-pc-linux-gnu #核心 类型  
export varName #<=> declare -x varName 
    // 将 varName 设置为 env变量 类型 (原来可能只是个 自定义变量 )。  
    // 对于子程序,  varName 在 本行之后才启动的子程序 中有效。(因为子程序会自动继承 自己被invoke时候 在父进程中有效的 env变量)。  

影响显示结果的语系变量 (locale) #

变量的有效范围 #

变量键盘读取、数组与宣告: read, array, declare / typeset #

read -p "promt string" -t timeOutInSecond varName  
    // 从键盘读取内容并存入varName; 
    -p  :prompt : 接提示字符  
    -t  :timeout: 接等待的『秒数』  
// array:  
    var[1]='this is the first'  
declare [-aixr] variable  
    -a  :将 variable 定义为 数组 (array) 类型  
    -i  :将 variable 定义为 整数 (int) 类型  
    -x  :#<=> export, 将 variable 变成环境变量;  
    -r  :配置成为 readonly 类型,且不能 unset  
    declare -x varName #export varName 
        // 将 varName 设置为 env变量 类型 (原来可能只是个 自定义变量 )。  
        // 如果export后没有varName,则 显示 所有 env变量。(-x 表示“转换为环境变量”参数,“-”不是chomd中的“去掉”)。  
    declare +x varName  
        // 将 varName 设置为 自定义变量 类型。(注意 +x 的含义,区别于 chmod)。  
    declare -p varName  
        // 显示 varName 变量类型(1.是 int(i) 还是 array(a) 还是 string( ); 2.是否 环境env变量(x); 3.是否只读(r))。  

与文件系统及程序的限制关系: ulimit #

ulimit  
    //  ??? 看不懂,学完操作系统再看。http://is.gd/I6ccfR『限制用户的某些系统资源』的,包括可以开启的文件数量, 可以使用的 CPU 时间,可以使用的内存总量等等。  
// hostname 更改主机名 how-to:三部曲  
    hostname newHostname #执行此命令  
    /etc/sysconfig/network #edit this file:  
        NETWORKING=yes  
        HOSTNAME=newHostname  
    /etc/hosts #edit this file.

For ubuntu 18+:

hostnamectl set-hostname newHostname
hostname newHostname
echo 'newHostname' > /etc/hostname

# !!! cloud-init:
sed 's/preserve_hostname: false/preserve_hostname: true/' /etc/cloud/cloud.cfg
# or:
touch /etc/cloud/cloud-init.disabled

变量内容的 匹配删除,匹配替换,状况测试 http://is.gd/ScOCjc #

**匹配删除**  
    newVarName=${varName#/*xxx}  
        'varName':源;'#':see below; 匹配并删除'/*xxx'(*:任意匹配)。  
    '#' :头部对齐(源varName必须以'/'开头才能匹配到),最短匹配。  
    '##':头部对齐,最长匹配。  
    '%' :尾部对齐,最短匹配。  
    '%%':尾部对齐,最长匹配。  
**匹配替换**  
    newVarName=${varName/oldString/newString}  
        // 匹配 首次出现  
    newVarName=${varName//oldString/newString}  
        // 匹配 所有  
**状况测试**  
    // 1)给 新变量 赋默认值  
        newVar=${oldVar<被匹配状况>defaultContent}  
        // 若匹配成功,以 defaultContent 赋值 newVar;          否则 以 oldVar 赋值 newVar。<被匹配状况>可能为:
     - oldVar 未配置  
    :- oldVar 未配置 或 为空字符串  
     + oldVar 已配置  
    :+ oldVar 已配置 且 非空字符串  
    // 2)给 双变量 赋默认值  
        newVar=${oldVar<被匹配状况>defaultContent}  
        // 若匹配成功,以 defaultContent 赋值 newVar 和 oldVar;否则 以 oldVar 赋值 newVar。<被匹配状况>可能为:  
     = oldVar 未配置  
    := oldVar 未配置 或 为空字符串  
        // 记忆:将'='理解为赋值号。  
    // 3)输出错误提示  
        newVar=${oldVar<被匹配状况>errInfo}  
        // 若匹配成功,输出 oldVar和errInfo 到 stderr;         否则 以 oldVar 赋值 newVar。<被匹配状况>可能为:  
     ? oldVar 未配置  
    :? oldVar 未配置 或 为空字符串  

别名 和 历史 #

命令别名配置: alias, unalias #

alias lm='ls -alF --color=always|more'  
unalias lm  

历史命令:history #

history [n]|[-c]|[-r|w|a [fileName]]  
[]:无参数, 显示所有在 当前shell的 hist内存 的hist。  
n:数字,最近n个(包括最后一个)。  
-c:clear all 当前shell的 hist内存。  
-r:从fileName覆盖读入到 当前shell的 hist内存。(默认文件为 ~/.bash_history)  
-w:当前shell的 hist内存 覆盖写入到 fileName。(默认文件为 ~/.bash_history)  
-a:当前shell的 hist内存  追加   到 fileName。(默认文件为 ~/.bash_history)  
echo $HISTSIZE  
    // hist size  
!number; !command; !!  
// 命令一般是先被记录才被执行,这三句话有额外的转换,然后再记录,再执行。所以 !num 中的num是正确的,不需要+1,是用 history 命令看到的那个编号。  
// logout时,可用 -c + -w 清除历史,防止cracker,尤其对root的crack。  

Bash Shell 的操作环境 #

命令搜索优先级: #

// 直接命令(相对或绝对路径);再 alias;再 shell-buildin;再由 $PATH 找到。  
alias echo='echo -n'; type -a echo  

Bash 的进站与欢迎信息: /etc/issue, /etc/issue.net, /etc/motd #

Bash 的环境配置文件 #http://minus.com/mbbIx0hdzh #

// 系统整体配置  
    /etc/profile : (for login shell) 系统整体配置,不要更改. 它会引入:  
        /etc/inputrc : 与bash输入相关的设置,例如热键,输入提示音。  
        /etc/profile.d/*.sh : bash颜色,命令别名等。  
        (/etc/profile.d/lang.sh :引入:  
    /etc/sysconfig/i18n : 语言等)  
// 个人配置 #~/.bash_login 或 ~/.bash_profile 或 ~/.profile #按此顺序优先级只读取一个  
    ~/.bash_profile :{{{ **引入 ~/.bashrc ** + $PATH }}}。它会引入:  
        ~/.bashrc (for non-login shell. **not-login只会读这一个文件。**): 引入:  
    /etc/bashrc (ubuntu为bash.bashrc) :{{{ umask + $PS1 }}} 引入:  
        /etc/profile.d/*.sh :(同 系统整体配置 )。  
        // umask:目前使用者在创建文件或目录时候 默认减掉的 权限值。  
            // 创建文件时:(-rw-rw-rw-) - (-----w--w-) ==> -rw-r--r-- :『文件』默认 没有『可运行( x )权限』。  
            // 创建目录时:(drwxrwxrwx) - (d----w--w-) ==> drwxr-xr-x :x 与是否可以进入此目录有关。  
// 其他配置:  
    /etc/man.config (centOs) 或 /etc/manpath.config (suse,ubuntu) ;{{{ $MANPATH }}} 即到哪个路径去找手册。  
    ~/.bash_history :每次登陆 bash 后,bash 会先读取这个文件,将所有的历史命令读入内存。  
    ~/.bash_logout : 注销 bash 前的命令,例如,清屏、清缓存、备份文件。  
source ~/.bashrc  
    // source 或小数点(.)命令 可以使某些配置不用重新登录就生效  

终端机的环境配置: stty, set #

stty [-a] #终端机的环境配置 (Setup TeleTYpe terminal unit).  
    eof		= ^D : !!! end of file ,『结束输入流(例如重定向时)』;没有输入流时结束当前shell-session。  
    stop	= ^S : !!! 停止 目前屏幕的输出;  
    start	= ^Q : !!! 重启 目前屏幕的输出;  
    erase	= ^? : 向后删除字符,  
    intr	= ^C : interrupt (中断) 的讯号给目前正在 run 的程序;  
    kill	= ^U : 删除在目前命令列上 光标左边 的所有文字;  
    werase	= ^W :删除在目前命令列上 光标左边 的所有文字;  
    quit	= ^\ : 送出一个 quit 的讯号给目前正在 run 的程序;?????????  
    susp	= ^Z : suspend 送出一个 terminal stop 的讯号给正在 run 的程序。  
    rprnt	= ^R :搜索以往命令,(非开头匹配,是任意位置匹配)。  
    // eg: 
        stty erase ^h #那么从此之后,你的删除字符就得要使用 [ctrl]+h 啰,按下 [backspace] 则会出现 ^? 字样  
set [-uvCHhmBx] #一般用不到???  

通配符与特殊符号 #

cp -a /etc/[^a-z]* /tmp  
    // 找出 /etc/ 底下,档名开头非为小写字母的文件名 复制到 /tmp 中。  
    -a: same as -dR --preserve=all.  
// 特殊符号:你的『档名』尽量不要使用到的这些字符啦!  
    // 	批注符号  
    ;	连续命令下达分隔符  
    !	逻辑『非』  
    ~	用户的家目录  
    &	工作控制 (job control):将命令变成背景下工作  
    !	逻辑运算意义上的『非』 not 的意思!  
    ` `	两个『 ` 』中间为可以先运行的命令,亦可使用 $( )。(!!!  $ 符号不要丢了 !!!)  
    ( )	在中间为子 shell 的起始与结束  

数据流 和 程序 的方向控制 #

数据流重定向 >, », <, «, tee, /dev/null #

perl phase1_server.pl >output.txt 2>&1 & #bg job  
    //  2是stderr, 所以这个 2> 是很容易理解的. 
    //  默认是1(stdout), 可以省略  
    // 注意:数字后没空格  
    // 不同流重定向到同一文件流:  
        find /home -name .bashrc > list.txt 2> list  <==错误(由于两股数据同时写入一个文件,又没有使用特殊的语法, 此时两股数据可能会交叉写入该文件内,造成次序的错乱。)  
        find /home -name .bashrc > list.txt 2>&1     <==正确//复合重定向 (推荐); 或者  
        find /home -name .bashrc &> list         <==正确  
cat > catfile < ~/.bashrc  
    // 简单输入重定向。(范例七:用 stdin 取代键盘的输入以创建新文件的简单流程。)  
cat > catfile << "xxx"  
    > This is a test.  
    > OK now stop  
    > xxx  <==输入这关键词,立刻就结束而不需要输入 [ctrl]+d 。
tee [-a] filename #复制重定向,不影响原有输出  
    -a :append:追加写入。默认是覆盖写入。  
/dev/null  
    // 黑洞dev 
    // eg: 
        find /home -name .bashrc 2> /dev/null  
重定向用于:  
    // 屏幕输出的信息很重要,而且我们需要将他存下来的时候;  
    // 背景bg运行中的程序,不希望他干扰屏幕正常的输出结果时;  
    // 一些系统的例行命令 (例如写在 /etc/crontab 中的文件) 的运行结果,希望他可以存下来时;  
    // 一些运行命令的可能已知错误信息时,想以『 2> /dev/null 』将他丢掉时;  
    // 错误信息与正确信息需要分别输出时。  

简单的流程控制, 命令运行的判断依据. #

cmd_1 && cmd_2; cmd_3 || cmd_4  
    // 判断原则: 0 ,非0 。
    // && : 如果 cmd_1 返回正确( 0),则 执行 cmd_2。  
    // && : 如果 cmd_3 返回错误(~0),则 执行 cmd_4。  
// eg: ls tmp 2> /dev/null || mkdir tmp -p && touch tmp/testFile  
    // Linux 中 || 与 && 相同优先级. pipe(|) 优先级低于 ||和&&.  
    // 但是要避免自己的逻辑错误 eg:  
    ls /tmp/vbirding && echo "  exist " || echo "no exist" <==正确  
    ls /tmp/vbirding || echo "no exist" && echo " exist  " <==错误(两个echo都会起作用)  
    // 结论:“command2 后”,先 “&& command2” 后 “|| command3” 。(原因是:其中的 command2 与 command3 一般放置着 肯定可以运行成功的命令。)  

管道 | (pipe) #

// 注意1:pipe 的第二个命令 可以直接处理 第一个命令的 std out,不能直接处理 std err。  
// 注意2:并不是所有命令都会接受std in,不接受std in的,放在第二个命令处也不会接受std in,例如 ls,cp,mv 等。  

撷取: cut, grep #

grep [-acinv] [--color=auto] 'key string' pendingFilename  
    -v :inVerse:反向,即:输出没有 'key string' 的那些行  
    -a :text Available:将 binary 文件以 text 文件 对待  
    -c :Count only:仅 输出数量  
    -i :case Insensitive:忽略大小写  
    -n :line Number:同时输出行号  
cut -[dfc] #eg: echo $PATH | cut -d ':' -f 3;  export | cut -c 12- ; export | cut -c 12-20 ; export | cut -c  -15;  
    // 常用格式:  
        cut -d'delimiterSign' -f fieldNumList	<==用于有特定分隔字符 否则:  
        cut -c 字符区间			<==用于排列整齐的信息  
    // 参数含义:  
        -d  :Delimiter :后面接分隔字符(只能是单个字符,且不支持转义字符),一般与 -f 一起使用。 -f  :Fiels:依据 -d 的结果,用 -f 取出第 fieldsNumber 段(从1开始数)。  
        -c  :Character:以Character(字符)计量 (从1开始数),而不是fields。  
    // 注:last | cut -d ' ' -f 1,3; 是错误的,因为 root 与  pts/1 之间空格有好几个。可以使用:  
        last | cut -c 1-4,22-38  

排序与统计: sort, uniq, wc #

// 注意:使用类似功能前,建议用 LANG=C 规范语境  
sort [-fbMnrtuk] [file or stdin];  #eg: 
    cat /etc/passwd | sort -nrt ':' -k 3|less  
        -f  :Fold lower to upper: 忽略大小写  
        -b  :ignore leading Blanks  
        -M  :Month: 以月份名(例如 JAN, DEC 等)排序  
        -n  :Numeric:以『纯数字』模式排序,而非默认的文本模式 #文本序(0,10,11,1,12)vs 数字序(1,1,10,11,12)  
        -h  :Human numeric :例如 1K,2G  
        -r  :Reverse:逆序  
        -u  :Unique,相同的数据,仅输出首次;(与-c (Check) 同用时,有其他意思)  
        -t  :Tab:分隔符(默认 [tab] )
        -k  :Key positions: (相当于 grep的 -f) :以给定 fieldNumberList 内容排序  
uniq #eg: 
    last | cut -d ' ' -f1 | sort | uniq -c  
        // uniq 只对连续重复起作用,所以 先 uniq 再 sort 会有另一种输出  
        -i  :case Insensitive:忽略大小写  
        -c  :Count:同时计数  
wc [-lwmc] [file or stdin];  #eg: 
    last | grep [a-zA-Z] | grep -v 'wtmp' | wc -l 
        // (last 会输出空白行与 wtmp 字样在最底下两行,利用grep去除).  
        // 默认会输出 行数 + 词数 + 字符数  
        -l  :Line  
        -w  :Word  
        -m  :character  
        -c  :byte  

双向重导向: tee (见 ch5) #

字符转换: tr, col, join, paste, expand #

tr [-ds] SET1 [SET2]; #translate; 常配合正则表达式使用  
    tr -d SET1     : 删除 符合 SET1 匹配的字符(串)eg: cat /etc/passwd | tr -d ':'  
        // eg:cat file.dos | tr -d '\r' > file.unix (\r 表示 ^M (tr不接受直接用'^M'),即dos比unix换行多出来的 http://ss64.com/bash/tr.html)  
    tr    SET1 SET2: 替换 符合 SET1 匹配的字符(串)  
col [-xb]  
    -x  :[tab] 转 对等 空格  
    -b  :在文字内有反斜杠 (/) 时,仅保留反斜杠最后接的那个字符 ??????????  
join -t ':' -1 4 /etc/passwd -2 3 /etc/group  
    // 比较 -1 4(第1个文件第4栏) 和 -2 3 (第2个文件第3栏),将 结果相同的 合并行,剪切第2个文件 第3栏和多余的分隔符 到 合并行 行首,(如果与 第一个文件 行首相同,即 -1 1,则只保留一个,不重复)。(可以与 head -n 3 /etc/passwd /etc/group 对比。)  
    // 注意:join前必须先sort。  
    -t  :separaTor:默认空格  
    -i  :忽略大小写  
    -1  :数字 1 ,代表 第1个文件  
    -2  :数字 2 ,代表 第2个文件  
    // eg:  
        join -t ':' /etc/passwd /etc/shadow ; (可以与 head -n 3 /etc/passwd /etc/shadow 对比。)  
paste /etc/passwd /etc/shadow  
    // 直接将行号相同的贴在一起,且中间以 [tab] 键隔开  
    -d  :Delimiter :默认  [tab]  
    -   :如果 file 部分写成 - ,表示 standard input  
expand [-t NUM] file  
    -t NUM:-t 后面一般接数字。tab 按键可以用 8 个空格键取代。  
    // eg:  
        grep '^MANPATH' /etc/man.config | head -n 3 | expand -t 6 - | cat -A 
        // 将 /etc/man.config 内行首为 MANPATH 的字样就取出, 三行; 6 个字符来代表一个 [tab] 的长度  

流分割: split #

split -b 300k originalFile splitedFilePrefix #文件分割  
    -b  : Bytes : 可加单位,例如 b, k, m 等;  
    -l  : Lines : 以行数分割。  
        // eg: ls -al / | split -l 10 - lsroot;  wc -l lsroot*; (参考下面对于 减号 的解释)  
    splitedFilePrefix : 新文件名为:"splitedFilePrefix" + "aa/ab/ac...."  
    // 合并:  
        cat splitedFilePrefix* >> originalFile  

参数代换 xargs #

xargs [-0epn] command #对于不支持 pipe 的 command ,可以透过 xargs 来提供 pipe 支持。  
    -0  :如果输入的 stdin 含有特殊字符,例如 `, \, 空格键等等字符时,这个 -0 参数 可以将他还原成一般字符。这个参数可以用于特殊状态喔!  
    -e  :End before key-word. 读取参数过程中,遇到某词就结束。  
        // 注意:  
        // 1. 必须是一个整词(以空格判断),不能只用开头或结尾,不能含空格。  
        // 2. 其后不能有空格,key-word 可以不用“”引起来。  
    -p  :promt : 执行 command 之前,输出命令行,并要求确认执行  
    -n NUM :max-args : 每次运行 command 最多给 NUM 个 args ,多次执行直到用完 args 。
    command: 默认 command 是 echo 
    // eg:  
        cut -d':' -f1 /etc/passwd  | head -n 15 | xargs -p -n 3 -e'lp' finger  
    // 取 /etc/passwd 第一列; 只取前三行; 每3个args(words)一组给 finger 执行,遇到‘lp’就停下,执行前确认。(finger: 显示某(几)个账户信息)  

- (减号) #

// 如果命令 某参数 应为 文件名,但却是 "-", “-” 就会被当成 stdin/stdout,in/out取决于原参数是输入还是输出。  
// 经典实例(没有实用价值,但是很经典):  
    tar -cvf - /home | tar -xvf - #将 /home 里面的文件给他打包,但打包的数据不是纪录到文件,而是传送到 stdout; 经过pipe将 tar -cvf - /home 传送给后面的 tar -xvf - 。后面的这个 ‘-’ 取用前一个命令的 stdout, 因此,我们就不需要使用 file 了!  

== 第十一章 end ==

第十二章、正规表示法与文件格式化处理 #

文件的格式化与相关处理 #

printf: 格式化列印 #

awk:好用的数据处理工具 #

文件比对工具:, diff, cmp, patch #

diff  
diff [-bBi] oldFile|oldDir newFile|newDir  
    -b: 忽略重复空格  
    -B:忽略重复空行  
    -i:case-Insensitive  
    eg1:比对 passwd.old 与 passwd.new 的差异:  
        [root@www test]# diff passwd.old passwd.new  
        4d3    <==左边第四行被删除 (d) 掉了,基准是右边的第三行  
        < adm:x:3:4:adm:/var/adm:/sbin/nologin  <==这边列出左边(<)文件被删除的那一行内容  
        6c5    <==左边文件的第六行被取代 (c) 成右边文件的第五行  
        < sync:x:5:0:sync:/sbin:/bin/sync  <==左边(<)文件第六行内容  
        ---  
        > no six line                    <==右边(>)文件第五行内容  
    eg2: diff /etc/rc3.d/ /etc/rc5.d/  
cmp 
patch 
patch [-R] [-pN] < fileName.patch  
    -p  :『取消几层目录』的意思。  
    -R  :Reverse, 还原  
    eg1: patch -p0 < passwd.patch #update  
        -rw-r--r-- 1 root root 1929 Feb 10 14:29 passwd.new  
        -rw-r--r-- 1 root root 1929 Feb 10 15:12 passwd.old  
    eg2: patch -R -p0 < passwd.patch #reverse  
        -rw-r--r-- 1 root root 1929 Feb 10 14:29 passwd.new  
        -rw-r--r-- 1 root root 1986 Feb 10 15:18 passwd.old  
    范例一:以 /tmp/test 内的 passwd.old 与 passwd.new  制作补丁文件  
        [root@www test]# diff -Naur passwd.old passwd.new > passwd.patch  
        [root@www test]# cat passwd.patch
--- passwd.old  2009-02-10 14:29:09.000000000 +0800 <==新旧文件的资讯
+++ passwd.new  2009-02-10 14:29:18.000000000 +0800
@@ -1,9 +1,8 @@   <==新旧文件要修改数据的界定范围,旧档在 1-9 行,新档在 1-8 行
 root:x:0:0:root:/root:/bin/bash
 bin:x:1:1:bin:/bin:/sbin/nologin
 daemon:x:2:2:daemon:/sbin:/sbin/nologin
-adm:x:3:4:adm:/var/adm:/sbin/nologin      <==左侧文件删除
 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
-sync:x:5:0:sync:/sbin:/bin/sync           <==左侧文件删除
+no six line                               <==右侧新档加入
 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
 halt:x:7:0:halt:/sbin:/sbin/halt
 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin

文件列印准备工具: pr #

== end ==

第十三章、学习 Shell Scripts #

什么是 Shell Script #

干嘛学习 shell scripts: #

// 自动化管理host状态; 管理系统服务; 简单入侵侦测; batch批处理; 配合awk和正则表达式简单处理数据/文本; 无需其他支持 跨unix-like平台.  

第一个 script 的撰写与运行 #

// 如果使用 sh scriptFilename.sh运行, script 不需要具备运行 x ,只要能读 r 就可以.  
// 首行 #!/bin/bash : 执行此脚本的 shell  
// 接下来是 : 功能 版本 作者 版权 历史 特殊命令的绝对路径 环境变量(PATH,LANG)  
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin; export PATH; LANG=C #如果有输出, 最好先 LANG=C 
exit 0
// 如果scrip 由 exit 退出, echo $? 会给出 script 的 exit 的数值.  

撰写 shell script 的良好习惯创建 #

// [tab], vim  

简单的 shell script 练习 #

简单范例: 对谈式脚本, 随日期变化, 数值运算 #

read -p "Please input your filename: " -t 4 fileuser; filename=${fileuser:-"filename"} #read 不支持 "\n".  
date1=$(date -d'2 days ago'  +%Y.%m.%d-%H:%M)  
    date [选项]... [+格式]  
    -d'字符串', --date='字符串'	如果不是现在,可用字符串指定具体日期/时间('20090101'),也可用描述性语句('2 days ago'). -d可跟空格, --date不可以.  
    %F    完整日期格式,等价于 %Y-%m-%d  
    %H    小时(00-23)  
    %l    时(1-12)  
    %m    月份(01-12)  
    %M    分(00-59)  
    %n    换行  
    %R    24 小时时间的时和分,等价于 %H:%M  
    %s    自UTC 时间 1970-01-01 00:00:00 以来所经过的秒数  
    %S    秒(00-60)  
    %t    输出制表符 Tab  
    %T    时间,等于%H:%M:%S  
    %y    年份最后两位数位 (00-99)  
    %Y    年份  
echo -e "\n now is : $date1. \n"  
    -e : Enable interpretation of backslash escapes #default is -E: disable  
    -n : 不输出新行  
total=$(($firstnu*$secnu)) #或者:  declare -i total=$firstnu*$secnu #推荐双括号式; 可用的计算有 +, -, *, /, % .

script 的运行方式差异 (source, sh script, ./script) #

sh filename / 直接命令(绝对路径/相对路径/$PATH内) 
    // 会启用一个新的 bash 子程序, 子程序内的 变量 或动作 在执行完后失效  
source filename  
    // 直接执行, 所以 被引用脚本执行完后 变量仍然持续有效  

善用判断式 #

test 命令 #

// 1.文档属性 (如 存在否)  
    -e 存在  (常用)  
    -f 存在、File(常用)  
    -d 存在、Directory(常用)  
    -b 存在、Block     device 装置  
    -c 存在、Character device 装置  
    -s 存在、Size > 0
    -S 存在、'S'ocket 
    -p 存在、Pipe (FIFO)  
    -L 存在、'L'ink # -l : Long int ??? 
// 2.文档权限 (如 可读否) (root 权限常有例外, 可能与ls显示不同)  
    -r 存在、可读  
    -w 存在、可写  
    -x 存在、可执行  
    -u 存在、setUid(4000): 仅文件.任何用户 执行该文件时, 权限 等同于 文件所有者.#eg: /usr/bin/passwd : -rwSr-xr-x. (实际全小写)  
        chmod u+s filename #加上setuid标志  
    -g 存在、setGid(2000): 仅目录.任何用户 创建的文件都 与该目录 属于相同的组.  
        chmod g+s dirname  
    -k 存在、sticKy(1000): 文件/目录. 仅所有者/root可删除. #eg: /tmp/ : drwxrwxrwT(实际全小写)  
        chmod o+t filename  
// 3.文档比较 eg: test file1 -nt file2  
    -nt	Newer Than	  
    -ot	Older Than  
    -ef	samE File #have the same device and inode numbers. 可用于判断 hard link  
// 4.整数比较 eg:  test n1 -eq n2  
    -eq	EQual  
    -ne	Not Equal  
    -gt	Greater Than  
    -lt	Less Than  
    -ge	Greater than or Equal  
    -le	Less than or Equal  
// 5.字符串 (必须使用引号) 
    [-n]	string		length Not zero. 非空为true. # -n 可省略  
    -z		string		length Zero  
    str1 == str2		相等为true # ==(推荐) 和 = 作用都是比较  
    str1 != str2		不等为true  
// 6. 多重条件判定,eg: test -r filename -a -x filename  
    -a	And  
        test -r file -a -x file #file 同时有 r 与 x 权限时 true  
    -o	Or  
    !	非  
        test ! -x file 

利用判断符号(中括号) [ ] #

[ "$yn" == "Y" -o "$yn" == "y" ] && echo "OK, continue" 
// 注意: 
    // a. ==(推荐) 和 = 作用都是比较; 
    // b. 中括号内各个组件之间 必须使用空格; 
    // c. 必须使用引号; 
    // d. 上式等同于:  
        [ "$yn" == "Y" ]||[ "$yn" == "y" ] && echo "OK, continue" #!!! 中括号里只能有一个判别式, [ "$yn" == "Y" || "$yn" == "y" ]是错误的  
        中括号之间的 &&, || 与命令下达的状态 (echo之前的) 的作用不同了.  

Shell script 的默认变量($0, $1…): shift #

$0 : 启动此脚本时的 无参命令行路径 (1.不一定是什么路径; 2.即使用 "bash xxx" 启动, $0也不包括shell名; 3.不含参数)  
$# : No. : 参数个数/最后一个参数脚标  
$@ : Array: 所有参数  
$* : 字符串, 所有参数. #一般用不着这个东西  
shift NUM #所有变量向左偏移, 溢出的丢掉 eg: 
    ./tmp 1 2 3 4 5 6
    echo "Total parameter number is ==> $#"  
    echo "parameters ==> '$@'"  
    shift 
    echo "Total parameter number is ==> $#"  
    echo "parameters ==> '$@'"  
    shift 3 
    echo "Total parameter number is ==> $#"  
    echo "parameters ==> '$@'"  
    // 会输出:  
    Total parameter number is ==> 6
    parameters ==> '1 2 3 4 5 6'  
    Total parameter number is ==> 5
    parameters ==> '2 3 4 5 6'  
    Total parameter number is ==> 2
    parameters ==> '5 6'  

条件判断式 #

利用 if …. then: 单层简单条件, 多重复杂条件, 检验$1内容, 网络状态, 退伍 #

	if#eg1:
		testing=$(netstat -tuln | grep ":80 ")   # 侦测看 port 80 在否?
		if [ "$testing" != "" ]; then
			echo "WWW is running in your system."
		fi
	if#eg2:
		read -p "Please input your demobilization date (YYYYMMDD ex>20090401): " date2
		date_d=$(echo $date2 |grep '[0-9]\{8\}')   # 看看是否有八个数字
		if [ "$date_d" == "" ]; then
			echo "You input the wrong date format...."
			exit 1
		fi
	if_else#eg:
		if [ ]; then
		else
		fi
	elif#eg:
		if [ ]; then
		elif [ ]; then
		else
fi

利用 case ….. esac 判断 #

case $var in	<==关键字为 case ,还有变量前有钱字号
  "第一匹配")		<==每个变量内容建议用双引号括起来,关键字则为小括号 )
    程序段
    ;;			<==每个类别结尾使用两个连续的分号来处理!
  "第二匹配")
    程序段
    ;;
  *)			<== 相当于 wildcard 
    其他程序段
    exit 1
    ;;
esac

function #

function printit(){
    echo "Your choice is $1"	#  这个 $1 是调用此函数的参数, 不是父程序的参数. 调用方法和执行普通程序一样.
}
printit 2		#  $1 为 2

loop (循环/回圈) #

不定循环 #

while...do...done #eg:
    while [ condition ]
    do
        xxxx
    done
until...do...done #eg:
    until [ condition ] 
    do
        xxxx
    done

固定循环 #

for_in...do...done #eg1:
    users=$(cut -d ':' -f1 /etc/passwd)  # 撷取帐号名称
    for username in $users               # 开始回圈进行!
    do
        id $username
        finger $username
    done
$(seq )#eg2:
    network="192.168.1"              # 先定义一个网域的前面部分!
    for sitenu in $(seq 1 100)       # seq 为 sequence(连续) 的缩写之意
    do
        ping -c 1 -w 1 ${network}.${sitenu} &> /dev/null && result=0 || result=1
        if [ "$result" == 0 ]; then
        echo "Server ${network}.${sitenu} is UP."
        else
        echo "Server ${network}.${sitenu} is DOWN."
        fi
    done

for…do…done #

for (( i=1; i<=$nu; i=i+1 ))
do
    xxxx
done

追踪与 debug #

sh [-nvx] scripts.sh  
    -n  :Non-run. 不运行,输出语法问题; 没问题 就 没输出.  
    -v  :Verbose. 输出所有行 再运行.  
    -x  :eXcute.  输出用到的行(行前增加'+'号), 新行输出对应结果.  

== 第十三章 end ==