Notes of Basic Linux Part-3 bash, shell, and Network Security
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 ==