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 可用pgrep加进程名获得。每个环境变量由 null 字符(\0)分隔,可用cat /proc/$PID/environ | tr '\0' '\n'每行显示一个,更清楚。

  • var=value是赋值操作,var = value是相等操作。

  • 获取字符串长度${#var}

  • 当前使用 shell:echo $SHELLecho $0

  • root 用户的 $UID 是0。

  • PS1 是终端提示字符串。

  • 自定义添加路径函数:prepend() { [ -d "$2" ] && eval $1=\"$2\$\{$1:+':'\$$1\}\" && export $1 ; }
    使用:prepend PATH /opt/myapp/bin

  • let result=no1+no2result=$[ 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 culstput 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
    将制表符显示为^Icat -T file,对排除缩进错误非常有用。
    显示行号:cat -n file,空白行不标号则用-b

  • 录制终端会话:script -t 2> timing.log -a output.session,之后进行命令行操作,以exit结束录制。
    播放:scriptreplay timing.log output.session

  • find .类似lsfind . -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 {} -largs.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。二者均为单向散列算法,无法逆推出原始数据,是存储密码的理想方案。但是由于计算能力攀升使其容易破解,推荐使用bcryptsha512sum
    输出整个目录的校验和: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 版本renamerename .JPG .jpg *.JPG,即把所有.JPG文件从.JPG改为.jpg
Perl 版本renamerename *.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 filefile必须是经过排序的,否则无效。

  • 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=$1csum1=$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
    开关光驱托盘:ejecteject -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 ^dfind . -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 -Eegrep
    只输出匹配的文本:egrep -o
    打印匹配行之外的所有行:grep -v
    统计匹配行数:grep -c。同一行内有多处匹配时,仅算一次。统计匹配的次数可结合egrep -owc -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 -FBEGIN{ 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 [email protected] "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/bunzip2lzma/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 "[email protected]"
    在源目录中执行git initgit commit --allow-empty -am "Init"
    备份git remote add origin user@backuphost:backup_pathgit 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/sdb1id=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/nullfping -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 自动化认证:
    创建密钥,指定加密算法为 RSA ssh-keygen -t rsa
    上传至远程主机ssh user@remotehost "cat >> ~/.ssh/authorized_keys" < ~/.ssh/id_rsa.pubssh-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 -inetstat -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。此外还有很多可用参数。

  • 当前登录用户:whowusers

  • 查看加电运行时间: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> /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系统启动信息;
    httpdApache Web服务器日志;
    messages发布内核启动信息;
    auth.log用户认证日志;
    dmesg系统启动信息;
    mail.log邮件服务器日志;
    Xorg.0.logX 服务器日志。

  • /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

你可能感兴趣的:(Linux Shell 脚本攻略(书摘))