美文网首页
Linux Shell 脚本攻略

Linux Shell 脚本攻略

作者: 巴喬書摘 | 来源:发表于2017-07-04 11:50 被阅读0次

    第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 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/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 "username@somewhere.com"
      在源目录中执行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<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系统启动信息;
      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 脚本攻略

          本文链接:https://www.haomeiwen.com/subject/zxgthxtx.html