Linux Shell 脚本攻略
第1章 小试牛刀
-
$ 是普通用户,# 表示管理员用户 root。
-
shebang:
#!
。sharp / hash / mesh 称呼 #,bang 称呼 !。后接解释器命令路径。 -
两种运行脚本方式:作为命令行参数(无需 shebang),或授予脚本执行权限。
-
echo
后无引号或使用单引号,无须转义字符\
;双引号中使用转义字符须使用-e
;无引号时后面的文本中不能有;
;变量替换在单引号中无效。 -
printf
不像echo
会自动添加换行符。取消echo
自动添加换行符须使用-n
。 -
%[-][n]s
,-
为左对齐,n
为字符串内的字符数,如n
小于命令后对应的字符串长度,则n
被忽略。 -
打印红色文本,
echo -e "\e[1;31m This is red text. \e[0m"
。\e[1;31m
将颜色设为红色,\e[0m
将颜色重置回。常用背景颜色代码:重置=0,黑色=30,红色=31,绿色=32,黄色=33,蓝色=34,洋红=35,青色=36,白色=37。常用背景颜色代码:重置=0,黑色=40,红色=41,绿色=42,黄色=43,蓝色=44,洋红=45,青色=46,白色=47。 -
Bash 中每一个变量的值都是字符串。
-
进程的环境变量可用
cat /proc/$PID/environ
得到,PID/environ | tr '\0' '\n'```每行显示一个,更清楚。 -
var=value
是赋值操作,var = value
是相等操作。 -
获取字符串长度
${#var}
。 -
当前使用 shell:
echo $SHELL
或echo $0
。 -
root 用户的 $UID 是0。
-
PS1 是终端提示字符串。
-
自定义添加路径函数:
prepend() { [ -d "$2" ] && eval $1=\"$2\$\{$1:+':'\$$1\}\" && export $1 ; }
。
使用:prepend PATH /opt/myapp/bin
。 -
let result=no1+no2
,result=$[ no1 + no2 ]
,result=$(( no1 + no2 ))
,三者等价。等号后的变量名前加$
亦可。 -
高精度计算:
echo "scale=16; $no1 ^ $no2" | bc
。此处 no2 必须为整数。 -
重定向 stdin:
0>(>)
。重定向 stdout:1>(>)
。重定向 stderr:2>(>)
。 -
tee
屏显加保存,仅保存 stdout。有-a
为追加内容,没有为覆盖。 -
将 out 的内容写入 log.txt:
out 文件内容:
#!/bin/bash
cat << EOF > log.txt
hi
hello
EOF
运行 . out,log.txt 的内容为:
hi
hello
-
定义数组
array_var=(1 2 3 4 5)
。打印特定元素echo ${array_var[$index]}
。以清单形式打印所有元素echo ${array_var[*]}
,或echo ${array_var[@]}
。打印数组长度echo ${#array_var[*]}
。列出数组索引echo ${!array_var[*]}
。 -
定义关联数组(Bash 4.0 以上版本)
declare -A ass_array
。赋值ass_array=([index1]=val1 [index2=val2])
,或ass_array[index1]=val1; ass_array[index2]=val2
。 -
设置命令别名:
alias new_command='command sequence'
。 -
不使用别名:
\command
。尤其对于特权命令,可避免攻击者使用别名盗取信息。 -
获取终端行数和列数:
tput culs
,tput lines
。
打印出当前终端名:tput longname
。
将光标移到坐标(10,10)处:tput cup 10 10
。
设置终端背景色:tput setb n
。n 可在0至7之间取值。
设置终端前景色:tput setf n
。
设置粗体:tput bold
。
设置下划线:tput smul
。
删除从当前光标位置以后的所有内容:tput ed
。 -
禁止将输出发送到终端:
#!/bin/bash
echo -e "Enter password: "
stty -echo
read password
stty echo
echo
echo Password read.
-
纪元时或 Unix 时间:自世界标准时间(UTC)1970年1月1日0时0分0秒起所流逝的秒数。
-
当前纪元时:
date +%s
。
日期串转换成纪元时:date --date "Thu Nov 18 08:07:21 IST 2010" +%s
。--date
用于提供日期串。
转换为星期几:date --date "Jan 20 2001" +%A
。
打印日期:date "+%s/S/M/I/H/d/b/B/m/y/Y/a/A/D"
。
设置日期:date -s "格式化的日期字符串"
。 -
倒计时:
#!/bin/bash
echo -n count:
tput sc
count=11
while true
do
if [ $count -gt 0 ]
then
let count--
sleep 1
tput rc
tput ed
echo -n $count
else exit 0
fi
done
- 启用调试:
bash -x script.sh
。
部分调试:
#!/bin/bash
for i in {1..6}
do
set -x
echo $i
set +x
done
echo "Script executed."
执行时显示:-x
。读取时显示:-v
。
#!/bin/bash
改为#!/bin/bash -xv
,则直接运行即可。
自定义调试信息:
#!/bin/bash
function DEBUG()
{
[ "$_DEBUG" == "on" ] && $@ || :
}
for i in {1..10}
do
DEBUG echo $i
done
此处echo $i
即为调试信息。
- 递归函数:
Fork Bomb
:(){ :|:& }; :
即
:()
{
: | : &
}
:
: 为函数名。可通过ulimit -u 128
(暂时)或修改配置文件 /etc/security/limits.conf 来限制可生成的最大进程数来避开。
-
导出函数:
export -f fname
。 -
退出状态:
$?
。成功则为0,否则为非0。如果最后设置了exit n
,则总返回 n。 -
子 shell 法:
cmd_output=$(ls | cat -n)
。
反引用或反标记法:cmd_output=`ls | cat -n`
。
保留空格和换行符:echo "$cmd_output"
。 -
使用子 shell
()
:
pwd
(cd /bin; ls)
pwd
子 shell 中的命令对当前 shell 没有影响。
-
读取 n 个字符并存入变量:
read -n number_of_chars variable_name
。
用无回显的方式读取密码:read -s var
。
显示提示信息:read -p "xxx" var
。
在特定时限内读取输入:read -t timeout var
。
用特定的定界符作为输入行的结束:read -d ":" var
。 -
含延时重复函数:
repeat()
{
while true
do
$@ && return
sleep 30
done
}
更快的做法:repeat() { while :; do $@ && return; sleep 30; done}
。
:
是内建命令,总返回0
。
- 更改定界符:
data="name,sex,rollno,location"
oldIFS=$IFS
IFS=,
for item in $data
do
echo Item: $item
done
IFS=$oldIFS
#!/bin/bash
line="root:x:0:0:root:/root:/bin/bash"
oldIFS=$IFS
IFS=:
count=0
for item in $line
do
[ $count -eq 0 ] && user=$item
[ $count -eq 6 ] && shell=$item
let count++
done
IFS=$oldIFS
echo $user\'s shell is $shell
-
条件为真则执行 action:
[ condition ] && action
。
条件为假则执行 action:[ condition ] || action
。 -
使用字符串比较时,最好用双中括号,因为单中括号有时回产生错误。用
test
可以避免使用过多的括号。
第2章 命令之乐
-
压缩相邻空白行:
cat -s file
。
将制表符显示为^I
:cat -T file
,对排除缩进错误非常有用。
显示行号:cat -n file
,空白行不标号则用-b
。 -
录制终端会话:
script -t 2> timing.log -a output.session
,之后进行命令行操作,以exit
结束录制。
播放:scriptreplay timing.log output.session
。 -
find .
类似ls
。find . -print0
将以 NULL 取代空格作为分隔符,在文件名中含空格时有用。
忽略字母大小写:find path -iname
。
匹配多个条件中的一个:\( -name "*.txt" -o -name "*.pdf" \)
。
同时搜索文件和文件夹:-path
。
正则表达式搜索:-regex
或忽略大小写-iregex
。
否定参数:! -name
。
限制目录深度:最大深度-maxdepth
,开始的最小深度-mindepth
。这个选项应该放在最前面,以避免多余的搜索。
按类型搜索:-type f/l/d/c/b/s/p
。
按时间搜索:最近7天内访问过-atime -7
,恰好7天前修改过-mtime 7
,超过7天前变化过-ctime +7
。如计量时间为分钟,则用-amin/-mmin/-cmin
。比参考文件更新-newer file_to_be_compared
。
按大小搜索:-size nb/c/w/k/M/G
。
删除找到的文件:在最后加-delete
。
按特定权限搜索:-perm
。
按所有权搜索:-user
。
对搜索到的文件执行命令:-exec command/batch_file {} \;
。{}
将替换为搜索到的文件名。\;
表示命令结束。{} +
可减少命令运行次数。
跳过特定的目录:find . \( -name ".git" -prune \) -o \( -type f -print \)
。 -
多行输入转单行输出:
cat example.txt | xargs
。
单行输入转多行输出:cat example.txt | xargs -n 3
。此处每行3个参数。
指定定界符:xargs -d
。默认为空格。
固定格式选项:cat args.txt | xargs -I {} command/batch_file -p {} -l
。args.txt
中有几个参数,命令就会执行几次。
统计目录中所有 C 程序文件的行数:find . -type f -name "*.c" -print0 | xargs -0 wc -l
。必须使用-print0
和-0
,因文件名中可能包含空格。
对同一参数执行多条命令:cat files.txt | ( while read arg; do cat $arg; done )
。 -
ROT13 加密:
echo "xxxxxx" | tr 'a-zA-Z' 'n-za-mN-ZA-M'
。
删除字符:cat "Hello 123 World 456" | tr -d '0-9'
。
删除补集之外的所有字符:echo "a 1 b 2 c 3" | tr -d -c '0-9 \n'
。
压缩重复字符:tr -s char
。
列数字求和:cat sum.txt | echo $( tr '\n' '+' ) 0 | bc -l
。
字符类替换:tr '[:lower:]' '[:upper:]'
。其余字符类包括alnum/alpha/cntrl/digit/graph/print/punct/space/xdigit
。 -
输出校验和到文件:
md5sum file1 file2 ... > file_sum.md5
。
校验:md5sum -c *.md5
。须与被校验文件在同一目录下。
sha1sum
用法同md5sum
。二者均为单向散列算法,无法逆推出原始数据,是存储密码的理想方案。但是由于计算能力攀升使其容易破解,推荐使用bcrypt
或sha512sum
。
输出整个目录的校验和:md5deep -rl directory_path > directory.md5
,或find directory_path -type f -print0 | xargs -0 md5sum >> directory.md5
。 -
crypt
加密文件:crypt < input_file > encrypted_file
,或直接提供口令crypt PASSPHRASE < input_file > encrypted_file
。
解密:crypt PASSPHRASE -d < encrypted_file > output_file
。 -
gpg
加密文件:gpg -c filename
,输入口令后生成filename.gpg
。
解密:gpg filename.gpg
。 -
base64
加密:base64 file > output
。
解密:base64 -d output > file
。 -
生成 shadow 密码字符串:
openssl passwd -1 -salt random_string password
。-1
指使用 MD5 基于 BSD 的密钥算法。 -
检查是否已经排序:
sort -C
。
按第二列的第二个字符至第四个字符排序:sort -k 2.2,2.4
。
后接xargs
对行做操作时用:sort -z file | xargs -0 command
。
重复的行仅输出一次:sort file | uniq
。仅输出不重复的行:uniq -u
。统计出现次数uniq -c
。显示重复的行uniq -d
。 -
创建临时文件:
filename=`mktemp`
。
创建临时目录:dirname=`mktemp -d`
。
生成临时文件名:tmpfile=`mktemp -u`
。
按模板创建:mktemp tmp_model.xxx
。至少3个 xxx。 -
按大小或行数等分割文件:
split
。
按内容分割文件:csplit file /SpecificWord/ -n 2 -s {*} -f filename -b "%02d.suffix"
。-s
为静默模式。{*}
表示重复进行直到文件结束;若*
为数字,表示进行分割的次数。-n
指定分割后-f
指定的文件名filename
后的数字个数。-b
指定filename
后的数字格式及后缀。 -
提取文件名和扩展名:
file="sample.jpg"
name=${file%.*}
extension=${file#*.}
%
从右向左找最短匹配并删除,%%
找最长匹配并删除。#
和##
则从左向右找。
- 批量重命名:
#!/bin/bash
# 将当前目录下的png和jpg文件重命名
count=1
for img in `find . -iname '*.png' -o -iname '*.jpg' -type f -maxdepth 1`
do
new=image-$count.${img##*.}
echo "Renaming $img to $new"
mv "$img" "$new"
let count++
done
C 版本rename
:rename .JPG .jpg *.JPG
,即把所有.JPG
文件从.JPG
改为.jpg
。
Perl 版本rename
:rename *.JPG *.jpg
。所有文件的空格全部替换为下划线rename 's/ /_/g' *
。大写转小写rename 'y/A-Z/a-z/' *
。
- 精确检查单词是否在词典文件
words
中:
#!/bin/bash
word=$1
grep "^$1$" /usr/share/dict/words -q
if [ $? -eq 0 ]; then
echo $word is a dictionary word.
else
echo $word is not a dictionary word.
fi
其中,^
和$
标记单词的开始和结束。-q
禁止任何输出。或者
#!/bin/bash
word=$1
output=`echo \"$word\" | aspell list`
if [ -z $output ]; then
echo $word is a dictionary word.
else
echo $word is not a dictionary word.
fi
-
look
查询特定字符串开头的行:look string file
。file
必须是经过排序的,否则无效。 -
expect
可用于实现自动交互,不再手动输入。 -
利用多核并行:
#!/bin/bash
PIDARRAY=()
for file in File1.iso File2.iso
do
md5sum $file &
PIDARRAY+=("$!")
done
wait ${PIDARRAY[@]}
&
将命令置于后台运行。$!
获得最近一个后台进程 PID。wait
等待进程结束。
第3章 以文件之名
-
创建特定大小的文件:
dd if=/dev/zero of=junk.data bs=1c/w/b/k/M/G count=1
。
如果bs=2M count=2
,则文件大小为 4M。of=
一定要仔细检查。
dd
命令可用于测量内存的操作速度。 -
两个经过排序的文件的比较:
comm A.txt B.txt
。输出第一列为只在A.txt
中的行,第二列为只在B.txt
中的行,第三列为二者共有的行。只打印第三列在末尾加参数-1 -2
。 -
删除当前文件夹下的重复文件:
#!/bin/bash
# ls -lS --time-style=long-iso | awk 'BEGIN {
# getline; getline
ls -lS --time-style=long-iso | grep ^- | awk 'BEGIN {
getline
name1=$8; size=$5
}
{
name2=$8
if ( size==$5 )
{
"md5sum " name2 | getline; csum2=$1;
"md5sum " name1 | getline; csum1=$1;
if ( csum1==csum2 )
{
print name1; print name2
}
}
else
{
size=$5
}
name1=name2
}' | sort -u > duplicate_files
cat duplicate_files | xargs -I {} md5sum {} | sort | uniq -w 32 | awk '{print $2}' | sort -u > duplicate_sample
echo Removing...
comm duplicate_files duplicate_sample -2 -3 | tee /dev/stderr | xargs rm
rm duplicate_files duplicate_sample
echo Removed duplicates files successfully.
若含csum2=$1
和csum1=$1
的两行交换位置,则运行结果错误,原因不明。
-
setuid 只能用于 Linux ELF 格式二进制文件,不能用于脚本文件。
-
root 用户可设置文件不可修改:
chattr +i file
。任何用户均可查看lsattr
。 -
touch
更改文件时间:-a
只更改访问时间,-m
只更改修改时间,-d
同时更改访问和修改时间。 -
显示符号链接指向的路径:
readlink
。 -
统计文件信息:
!/bin/bash
if [ $# -ne 1 ]
then
echo "Usage is $0 basepath"
exit
fi
path=$1
declare -A statarray
while read line
do
ftype=`file -b "$line" | cut -d , -f 1`
let statarray["$ftype"]++
done < <(find $path -type f -print)
echo ======== File types and counts ========
for ftype in "${!statarray[@]}"
do
echo $ftype : ${statarray["$ftype"]}
done
其中,$#
是传递给脚本的参数个数,$0
是脚本自己的名字,$1
是传给脚本的第一个参数,file -b
只打印文件类型,cut -d , -f 1
选取以逗号分隔的第一列数据,<(find $path -type f -print)
获取子进程输出数据流。Bash 3.0 及更高版本中,亦可用done <<< "`find $path -type f -print`"
。
- 环回文件系统的创建和使用:
dd if=/dev/zero of=loopbackfile.img bs=1G count=1
mkfs.ext4 loopbackfile.img
mkdir /mnt/loopback
mount -o loop loopbackfile.img /mnt/loopback
umount /mnt/loopback
分区:
losetup /dev/loop1 loopbackfile.img
fdisk /dev/loop1
losetup -o 32256 /dev/loop2 loopbackfile.img
或使用:
kpartx -v -a loopbackfile.img
mount /dev/mapper/loop0p1 /mnt/loopback1
kpartx -d loopbackfile.img
将对挂载设备的更改即刻写入物理设备:sync
。
-
从光盘创建镜像:
cat /dev/cdrom > image.iso
,最好还是用dd if=/dev/cdrom of=image.iso
。
从文件创建镜像:mkisofs -V "Label" -o image.iso source_dir/
。
生成可引导的混合型 ISO:isohybrid image.iso
。
刻录 ISO:cdrecord -v dev=/dev/cdrom image.iso -speed 8
。多次刻录:-multi
。
开关光驱托盘:eject
和eject -t
。 -
生成修补文件:
diff -u version1.txt version2.txt > version.patch
。
修补:patch -p1 version1.txt < version.patch
。
撤销:再执行一次上一条命令。 -
生成目录的差异信息:
diff -Naur directory1 directory2
。 -
打印前 M 行:
head -n M file
。
打印除后 M 行之外的所有行:head -n -M file
。
打印后 M 行:tail -n M file
。
打印除前 M 行之外的所有行:tail -n +(M+1) file
。 -
关注文件的变化:
tail -f file
。
随进程结束而结束关注:tail -f file --pid $PID
。 -
列出当前目录下的目录:
ls -d */
,ls -F | grep /$
,ls -ls | grep ^d
,find . -type d -maxdepth 1 -print
。 -
命令行中的目录切换:
pushd
,在栈中压入目录并切换;dirs
显示栈中的目录;pushd +num
,切换到栈中的第num
个目录,从0开始;popd
删除当前目录并进入切换前的目录;popd +num
删除指定目录。 -
统计行数、单词数、字符数:
wc
,分别统计wc -l/w/c
。打印文件中最长一行的长度wc -L
。 -
以树状结构打印文件和目录:
tree PATH
。重点标记符合某样式的文件tree PATH -P PATTERN
。重点标记除某样式外的文件tree PATH -I PATTERN
。同时打印文件和目录的大小tree -h PATH
。以 HTML 形式输出目录树tree PATH -H http://localhost -o out.html
。
第4章 让文本飞
-
正则表达式的基本组成部分:
^
行首标记,$
行尾标记,.
匹配任意一个字符,[]
匹配包含在方括号内的任意一个字符,[^]
匹配除方括号内的任意一个字符,[-]
匹配指定范围内的任意一个字符,?
匹配之前的项1次或0次,+
匹配之前的项至少1次,*
匹配之前的项至少0次,()
创建用于匹配的子串,{n}
匹配之前的项 n 次,{n,}
匹配之前的项至少 n 次,{n,m}
匹配之前的项最少 n 次、最多 m 次,|
匹配两边的任意一项,\
将前述特殊字符转义。 -
着重标记匹配的字符串:
grep string filename --color=auto
。
使用扩展正则表达式:grep -E
或egrep
。
只输出匹配的文本:egrep -o
。
打印匹配行之外的所有行:grep -v
。
统计匹配行数:grep -c
。同一行内有多处匹配时,仅算一次。统计匹配的次数可结合egrep -o
和wc -l
。
打印匹配行的行号:grep -n
。
打印字符串的字符偏移:grep -bo
。从整个文本的第一字符算起,起始值为0。
打印包含匹配字符串的文件:grep -l
,不包含的文件grep -L
。
递归搜索:grep -R
。
忽略大小写:grep -i
。
多字符串匹配:grep -e string1 -e string2
。将每个字符串写入文件的每一行,根据文件搜索grep -f
。
指定文件搜索:grep "main()" . -r --include *.{c,cpp}
。
排除文件搜索:grep "main()" . -r --exclude "README"
。
排除目录:--exclude-dir
。
从文件中读取排除的文件列表:--exclude-from
。
输出以0值字节作为终结符的文件名:grep -lZ
。
静默输出:-q
。匹配成功返回0,失败返回非0。
打印匹配行及其后的 n 行:grep -A n
,之前-B n
,前后各 n 行-C n
。若有多行匹配,以--
分隔。 -
按列打印:
cut -f num file
,多列num1,num2,num3...
。
排除不含定界符的行:-s
。
打印除某列外的所有行:cut -f num --complement file
。
指定定界符:-d
。
按字符或字节选取:-c
或-b
,提取多字段时必须指定输出定界符cut -c 0-2,4-6 --output-delimiter " "
。 -
打印替换后的文本:
sed 's/text/replace/' file
。定界符/
可任意更换,如,
,|
,:
等等。
替换并保存:-i
,此时不打印;同时保留副本-i.bak
。
全行替换:sed 's/text/replace/g' file
。
全行第 n 处开始替换:sed 's/text/replace/ng' file
。
移除空白行:sed '/^$/d' file
。
替换所有的三位数:sed 's/\b[0-9]\{3\}\b/NUMBER/g' file
。\b
表示字符串边界。
所有单词放入中括号内:sed 's/\w\+/[&]/g
。\w\+
匹配每一个单词,&
对应匹配字符串。
大小写子串互换位置:sed 's/\([a-z]\+\) \([A-Z]\+\)/\2 \1/'
。
多个替换:sed 's/a/A/' | sed 's/c/C/'
或sed 's/a/A/; s/c/C/'
或sed -e 's/a/A/' -e 's/c/C/'
。
使用变量需要双引号:sed "s/$text/replace/"
。 -
awk
基本结构:awk 'BEGIN{ commands } pattern { commands } END{ commands }' file
。首先执行BEGIN
语句块;之后如果满足pattern
的条件则执行其后的语句块,如未提供pattern
则默认执行,循环执行直到输入流读取完毕;最后执行END
语句块。
NR
:当前行号。
NF
:当前行的字段数。
$0
:当前行的文本内容。
传递外部变量:awk -v VARIABLE=$VAR
,或awk '{ commands }' VARIABLE=$VAR
。
在BEGIN
语句块中读取输入流可以使用getline
。
pattern
过滤:NR < 5
行号小于5的行,NR==1, NR==5
行号在1到5之间的行,/string/
包含 string 的行,!/string/
不包含 string 的行,/string1/, /string2/
包含 string1 的行到包含 string2 的行。
指定定界符:awk -F
或BEGIN{ FS=" " }
,定界符|
替换为换行符BEGIN{ RS="|" }
,换行符替换为定界符BEGIN{ ORS="|" }
,指定输出分隔符BEGIN{ OFS="|" }
。
字符串控制函数:length(string)
,index(string, search_string)
,split(string, array, delimiter)
,substr(string, start-position, end-position)
,sub(regex, replacement_str, string)
,gsub(regex, replacement_str, string)
,match(regex, string)
。 -
统计词频:
#!/bin/bash
if [ $# -ne 1 ]
then
echo "Usage is $0 filename"
exit -1
fi
filename=$1
egrep -o "\b[[:alpha:]]+\b" $filename | \
awk '{ count[$0]++ }
END { printf("%-14s%s\n", "Word", "Count");
for( ind in count )
{ printf("%-14s%d\n", ind, count[ind]) }
}'
-
按列拼接文件:
paste
,-d
指定定界符。 -
按行逆序打印:
tac
,-s
替换换行符。
awk '{ lifo[NR]=$0 }
END { for ( lno=NR; lno > 0; lno-- ) { print lifo[lno] } }' file
-
提取电子邮件地址:
egrep -o '[A-Za-z0-9._]+@[A-Za-z0-9.]+\.[a-zA-Z]{2,4}'
。
提取 HTTP URL:egrep -o "http://[a-zA-Z0-9.]+\.[a-zA-Z]{2,3}"
。 -
替换变量中的文本:
${var/string/replace/}
。
生成变量子串:${var:start_position:length}
。
第5章 一团乱麻?没这回事
-
下载网页:
wget URL1 URL2 URL3 ...
。
下载文件:wget -t 0 ftp://example_domain.com/somefile -O downloaded_file -o log
。-O
重命名,-o
将输出重定向至 log 文件,-t 0
不断重试。
限速:--limit-rate 20k/m
。
限制最大磁盘配额:--quota
或-Q
。
断点续传:重新下载时使用wget -c
。
复制网站:wget --mirror --convert-links URL
,或wget -r -N -k -l depth URL
。
输入用户名和密码:wget --user username --password pass URL
,手动输入密码--ask-password
。 -
将网页内容以 ASCII 编码的形式存储到文件中:
lynx URL -dump > webpage_as_text.txt
。 -
下载数据写入文件:
curl URL -o filename --progress
。
断点续传:curl -C - URL
。
从特定的文件偏移处下载:curl URL/file -C offset
。
设置参照页:curl --referer referer_URL target_URL
。
第6章 B 计划
-
归档:
tar -cf output.tar file1 file2 file3 ...
。
列出归档的文件:tar -tf output.tar
,或冗长模式tar -tvf
。
追加文件:tar -rf output.tar new_file
。
提取:tar -xf output.tar
,指定目录tar -xf output.tar -C path
。
提取特定文件:tar -xf output.tar file1 file4
。
打包传输:tar -cvf - local_path | ssh user@example.com "tar -xv -C remote_path
。
拼接:tar -Af output1.tar output2.tar
。
追加更新的文件:tar -uf output.tar file1
,提取时会选最新的。
判断归档文件与本地同名文件是否相同:tar -df output.tar
。
删除:tar -f ouput.tar --delete file1 file2
。
压缩:-j
zip2 格式,-z
gzip 格式,--lzma
lzma 格式。
根据扩展名自动压缩:-a
。
归档时排除指定文件:tar -cf output.tar * --exclude "*.txt"
,或根据文件tar -cf output.tar * -X file
。
排除版本控制相关的文件和目录:--exclude-vcs
。
打印总归档字节数:--totals
。 -
保留所有文件属性的归档:
echo file1 file2 file3 | cpio -ov > archive.iso
。
列出归档的内容:cpio -it < archive.iso
。
提取:cpio -id < archive.cpio
。
cpio
提取至绝对路径,tar
提取至相对路径。 -
压缩:
gzip file
。
解压:gunzip file.gz
。
列出:gzip -l file.gz
。
指定输出文件:-c >
。
最低压缩比--fast
或-1
,最高压缩比--best
或-9
。
直接读取:zcat file.gz
。
另有bzip2
/bunzip2
、lzma
/unlzma
。 -
归档并压缩
zip
,提取unzip
不会删除源文件。
递归-r
,更新-u
,删除-d
,列出-l
。 -
利用多核归档并压缩:
tar -c directory_to_compress | pbzip2 -c > output.tar
。
解压并提取:pbzip2 -dc output.tar | tar -x
。
指定核数:-p
。
指定压缩比:-1
至-9
。 -
超高的压缩率,且无须解压即可读取少量文件,使用 squashfs 只读文件系统。
创建:mksquashfs SOURCES compressedfs.squashfs
。
利用环回方式挂载:mount -o loop compressedfs.squashfs /mnt/squash
。
创建时排除部分文件:-e
,或根据文件-ef
,使用通配符-wildcards
。 -
归档并压缩传输:
rsync -avz source destination
。路径最后有/
和没有是不同的。
排除部分文件:--exclude
,或根据文件--exclude-from
。
删除不存在的文件:--delete
。 -
版本控制:
在备份端的备份目录中执行git init --bare
;
在源端的源目录中添加编辑者信息git config --global user.name "FirstName FamilyName"
,git config --global user.email "username@somewhere.com"
;
在源目录中执行git init
,git commit --allow-empty -am "Init"
;
备份git remote add origin user@backuphost:backup_path
,git push origin master
;
添加到备份列表git add
,从备份列表中删除git rm
;
标注检查点git commit -m "Commit Message"
;
查看所有版本git log
;
恢复至某版本git checkout ID
;
修复git clone user@backuphost:backup_path
。 -
创建文件系统/分区备份:
fsarchiver savefs backup.fsa /dev/sda1 /dev/sda2
。
恢复分区:fsarchiver restfs backup.fsa id=0,dest=/dev/sda1 id=1,dest=/dev/sdb1
。id=0
表示提取第一个分区的内容。
第7章 无网不利
-
MAC 地址欺骗:
ifconfig eth0 hw ether new_MAC
。重启后失效。 -
列出域名的所有 IP 地址:
host google.com
。
同时列出 DNS 资源记录:nslookup google.com
。 -
设置默认网关:
route add default gw IP INTERFACE
。 -
查看途经的网关:
traceroute google.com
。 -
找出网络上所有的活动主机:
#!/bin/bash
for ip in 172.17.110.{1..255}
do
(
ping $ip -c 2 &> /dev/null
if [ $? -eq 0 ]
then
echo $ip is alive.
fi
)&
done
wait
或者使用fping -a 172.17.110.1/24 -g 2> /dev/null
或fping -a 172.17.110.0.1 172.17.110.255 -g
。
-
指定 SSH 端口:
ssh user@remotehost -p 422
。
远程执行,本地显示:ssh user@remotehost 'command1; command2; command3'
。
压缩传输:-C
。 -
FTP 连接:
sftp user@remotehost
。上传put
,下载get
。
指定端口:-oPort=PortNumber
。 -
scp -p
将保留文件的权限和模式。 -
SSH 自动化认证:
创建密钥,指定加密算法为 RSAssh-keygen -t rsa
;
上传至远程主机ssh user@remotehost "cat >> ~/.ssh/authorized_keys" < ~/.ssh/id_rsa.pub
或ssh-copy-id user@remotehost
。 -
将本地主机端口8000上的流量转发到 www.kernel.org 的端口80上:
ssh -L 8000:www.kernel.org:80 user@localhost
。
将远程主机端口8000上的流量转发到 www.kernel.org 的端口80上:
ssh -L 8000:www.kernel.org:80 user@REMOTE_MACHINE
。
非交互式:ssh -fL 8000:www.kernel.org:80 user@localhost -N
。-f
执行命令前转入后台,-N
说明无需执行命令。
反向端口转发:ssh -R 8000:localhost:80 user@REMOTE_MACHINE
。 -
在本地挂载点上挂载远程驱动器:
sshfs -o allow_other user@remotehost:remote_path /mnt/mountpoint
。 -
列出开放端口及运行的服务:
lsof -i
或netstat -tnp
。 -
在本地端口 1234 创建套接字
nc -l 1234
,连接到该套接字nc HOST 1234
,可相互发送信息。
文件传输:在接收端执行nc -l 1234 > destination_file
,在发送端执行nc HOST 1234 < source_file
。 -
创建无线热点:
#!/bin/bash
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -A FORWARD -i $1 -o $2 -s 10.99.0.0/16 -m conntrack --ctstate NEW -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A POSTROUTING -t nat -j MASQUERADE
运行./netsharing.sh eth0 wlan0
。
- 阻塞发送到特定 IP 的流量:
iptables -A OUTPUT -d 8.8.8.8 -j DROP
。
阻塞发送到特定端口的流量:iptables -A OUTPUT -p tcp -dport 21 -j DROP
。
-A
添加规则到OUTPUT
规则链中。
清除所有改动:iptables --flush
。
第8章 当个好管家
-
打印文件或目录占用的磁盘空间:
du
。
打印目录中每个文件占用的空间:-a
。
默认以字节为单位,标准容量单位-h
。
指定字节 / KB / MB / 块为单位:-b/k/m/B
。
打印总计:-c
,只打印总计-s
。
排除部分文件:--exclude
,或根据文件--exclude-from
。
指定深度:--max-depth
。
排除所有挂载点:-x
。
找出大文件:find . -type f -exec du -k {} \; | sort -nrk 1 | head
。 -
磁盘可用空间:
df -h
。 -
打印运行时间:
time COMMAND
。
统计信息写入文件:/usr/bin/time -o output COMMAND
,不覆盖而是追加至 output-a
。
格式化输出:-f "FORMAT STRING"
。real 时间%e
,user 时间%U
,sys 时间$S
。此外还有很多可用参数。 -
当前登录用户:
who
,w
,users
。 -
查看加电运行时间:
uptime
。 -
上一次启动及登录信息:
last
,指定记录日志-f
。
指定用户:last USER
。
指定会话:last reboot
。
失败的用户登录会话信息:lastb
。 -
统计一小时内占用 CPU 最多的十个进程:
#!/bin/bash
SECS=3600
UNIT_TIME=60
STEPS=$(( $SECS / $UNIT_TIME ))
echo Watching CPU usage...
for (( i=0; i<STEPS; i++ ))
do
ps -eo comm,pcpu | tail -n +2 >> /tmp/cpu_usage.$$
sleep $UNIT_TIME
done
echo
echo CPU eaters :
cat /tmp/cpu_usage.$$ | \
awk '{process[$1] += $2 }
END{
for ( i in process )
{
printf("%-20s %s\n", i, process[i])
}
}' | sort -nrk 2 | head
rm /tmp/cpu_usage.$$
comm
表示命令名,pcpu
表示 CPU 使用率,$$
是脚本的进程 ID。
-
以固定间隔监视命令输出:
watch
。
指定间隔秒数:-n
。默认为两秒。
突出差异:-d
。 -
监视目录访问:
#!/bin/bash
path=$1
inotifywait -m -r -e create,move,delete $path -q
持续监视变化-m
,递归-r
,指定需用监视的事件-e
。
-
logrotate
配置文件参数:
missingok
日志文件丢失则忽略,然后返回;
notifyempty
仅当源日志文件非空时才对其进行轮替;
size
限制日志文件的大小;
compress
压缩旧日志;
weekly
轮替时间间隔;
rotate
保留的旧日志文件的归档数量;
create 0600 root root
指定权限和属性。 -
重要的应用程序应将执行过程记录在日志文件中。Linux
/var/log
的重要日志文件包括:
boot.log
系统启动信息;
httpd
Apache Web服务器日志;
messages
发布内核启动信息;
auth.log
用户认证日志;
dmesg
系统启动信息;
mail.log
邮件服务器日志;
Xorg.0.log
X 服务器日志。 -
向
/var/log/messages
中写入日志信息:logger MESSAGE
。特定标记-t
。写入另一日志文件的最后一行-f
。 -
检测入侵:
#!/bin/bash
AUTHLOG=/var/log/secure
if [[ -n $1 ]];
then
AUTHLOG=$1
echo Using Log file : $AUTHLOG
fi
LOG=/tmp/valid.$$.log
grep -v "invalid" $AUTHLOG > $LOG
users=$(grep "Failed password" $LOG | awk '{ print $(NF-5) }' | sort | uniq)
printf "%-5s|%-10s|%-10s|%-13s|%-33s|%s\n" "Sr#" "User" "Attempts" "IP address" "Host_Mapping" "Time range"
ucount=0
ip_list="$(egrep -o "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" $LOG | sort | uniq)"
for ip in $ip_list
do
grep $ip $LOG > /tmp/temp.$$.log
for user in $users
do
grep $user /tmp/temp.$$.log > /tmp/$$.log
cut -c -16 /tmp/$$.log > $$.time
tstart=$(head -1 $$.time)
start=$(date -d "$tstart" "+%s")
tend=$(tail -1 $$.time)
end=$(date -d "$tend" "+%s")
limit=$(( $end - $start ))
if [ $limit -gt 120 ]
then
let ucount++
IP=$(egrep -o "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" /tmp/$$.log | head -1)
TIME_RANGE="$tstart-->$tend"
ATTEMPTS=$(cat /tmp/$$.log | wc -l)
HOST=$(host $IP | awk '{ print $NF }')
printf "%-5s|%-10s|%-10s|%-10s|%-33s|%-s\n" "$ucount" "$user" "$ATTEMPTS" "$IP" "$HOST" "$TIME_RANGE"
fi
done
done
rm /tmp/valid.$$.log /tmp/$$.log $$.time /tmp/temp.$$.log 2> /dev/null
排除不存在的用户名grep -v "invalid"
。
- 监视远程磁盘健康状况:
#!/bin/bash
logfile="diskusage.log"
if [[ -n $1 ]];
then
logfile=$1
fi
if [ ! -e $logfile ]
then
printf "%-8s %-14s %-9s %-8s %-6s %-6s %-6s %s\n" "Date" "IP address" "Device" "Capacity" "Used" "Free" "Percent" "Status" > $logfile
fi
ip_list="10.0.0.1 10.0.0.2"
(
for ip in $ip_list
do
ssh kangk@$ip 'df -H' | grep ^/dev/ > /tmp/$$.df
while read line
do
cur_date=$(date +%D)
printf "%-8s %-14s " $cur_date $ip
echo $line | awk '{ printf("%-9s %-8s %-6s %-6s %-8s", $1,$2,$3,$4,$5) }'
pusg=$(echo $line | egrep -o "[0-9]+%")
pusg=${pusg/\%/}
if [ $pusg -lt 80 ]
then
echo SAFE
else
echo ALERT
fi
done < /tmp/$$.df
done
echo
) >> $logfile
-
电源使用的测量与优化:
powertop
。生成 HTML 格式的报表--html
。 -
I/O 监视:
iotop
。只显示正在进行-o
,非交互式打印两次-b -n 2
,特定进程-p `pidof command`
。 -
检查磁盘:
fsck
。检查所有/etc/fstab
中的-A
。自动修复-a
。模拟操作-AN
。
第9章 管理重任
-
列出占用 CPU 最多的10个进程:
ps -eo comm,pcpu --sort -pcpu | head
。
指定有效用户:-u
。
指定真实用户:-U
。
指定 TTY:-t
。
输出线程相关信息:-L
。
列出依赖的环境变量:ps -eo cmd e
。 -
列出进程的所有 PID :
pgrep command
。
指定定界符:-d
。
指定用户:-u
。 -
列出所有可用的进程信号:
kill -l
。
发送指定信号:kill -s SIGNAL PID
。
常用信号:SIGHUP 1,SIGINT 2,SIGKILL 9,SIGTERM 15,SIGTSTP 20。
通过命令名:killall
。
捕捉并响应信号:trap 'signal_handler_function_name' SIGNAL_LIST
。