Bash 日常应用总结

A Linux Bash Cookbook

Posted by pandaychen on June 10, 2022

0x00 前言

本文汇总下笔者常用的Bash指令备份

0x01 BASH

1、批量查找文件内容
主要使用grep -rl,如下面指令,查询当前目录下所有出现yyyy字符串的文件列表:

grep yyyy -rl --include="*.txt" ./

2、批量替换文件内容
常用sed搭配grep指令使用,如下面指令,将当前目录./下的所有.txt文件中的yyyy字符串替换为xxxx

sed -i s/yyyy/xxxx/g `grep yyyy -rl --include="*.txt" ./`

3、批量kill进程

ps aux|grep nginx|grep -v grep|awk '{print $2}'|xargs kill -9

4、仅替换当前目录下的文件内容

sed -i s/xxxx/yyyy/g ./*.txt

5、在匹配行的位置上面或者下面添加指定内容、删除指定行内容
注意下面sed指令替换那列的ai这两个参数:

sed -i /xxxx/d ./*.txt    #删除命中查询条件的行
sed -i /7777/a77777 ./*.txt #在命中查询条件的下一行添加77777
sed -i /7777/i77777 ./*.txt #在命中查询条件的上一行添加77777

6、解析pcap抓包文件
下面示例,通过tcpdump -r指令对抓包中的源IP进行TOP统计,并按照数量进行排序

tcpdump -r xxxx.pcap |awk '{print $3}'|awk -F '\.' '{print $1"."$2"."$3"."$4}' |sort |uniq -c |sort -nr -k 1

7、获取字符串最后一个字符

STR=123456abc
FINAL=`echo ${STR: -1}`
FINAL_O=${STR: -1}  #与上面等价
FINAL2=`echo ${STR: -2}` #获取最后两个字符

8、去掉字符串最后两个字符

var="12345467,"
echo ${var%??}

9、去掉字符串末尾一个字符

var="12345467,"
echo ${var%?}

10、sed如何处理ascii码为0的字符?

find . -print0 | sed -e 's/\x0/\n/g'

在上例中,find命令把当前目录下的文件名输出出来,以0字符分隔。输出被管道送给sed进行处理。在sed中,则把0字符替换成回车符

11、sed如何使用shell中的变量
如何在sed中使用shell定义的变量?要用双引号而不是单引号来引用sed命令,因为shell会处理双引号中的变量引用,而对单引号中的内容,shell完全不会处理

TERM1=term; TERM2=rxvt; echo "term" | sed -e "s/$TERM1/$TERM2/g"

0x02 字符串操作总结

12、字符串不对称分割
如何从指定字符(子字符串)开始截取?

1) 使用 # 号截取指定字符(或子字符串)右边字符

string="xx|xxxxx"
echo ${string#*|} #输出xxxxx

string="xx|123456"
echo ${string#*|123}  #输出456

echo ${string#xx|123} #输出456

语法为:

${string#*chars}

其中,string 表示要截取的字符,chars 是指定的字符(或者子字符串),*是通配符的一种,表示任意长度的字符串。*chars连起使用的意思是:忽略左边的所有字符,直到遇见 charschars 不会被截取)

2) 匹配到最后一个指定字符再结束,格式串为${string##*chars}

url="http://a.b.c/index.html"
echo ${url#*/}    #输出/a.b.c/index.html
echo ${url##*/}   #输出index.html

str="---aa+++aa@@@"
echo ${str#*aa}   #结果为 +++aa@@@
echo ${str##*aa}  #结果为 @@@

3) 使用 % 截取指定字符(或者子字符串)左边字符,格式为${string%chars*},注意*的位置

url="http://a.b.c/index.html"
echo ${url%/*}  #http://a.b.c
echo ${url%%/*}  #结果为 http:
str="---aa+++aa@@@"
echo ${str%aa*}  #结果为 ---aa+++
echo ${str%%aa*}  #结果为 ---

汇总下:

匹配格式 说明
${string: start :length} 从 string 字符串的左边第 start 个字符开始,向右截取 length 个字符
${string: start} 从 string 字符串的左边第 start 个字符开始截取,直到最后
${string: 0-start :length} 从 string 字符串的右边第 start 个字符开始,向右截取 length 个字符
${string: 0-start} 从 string 字符串的右边第 start 个字符开始截取,直到最后
${string#*chars} 从 string 字符串第一次出现 *chars 的位置开始,截取 *chars 右边的所有字符
${string##*chars} 从 string 字符串最后一次出现 *chars 的位置开始,截取 *chars 右边的所有字符
${string%*chars} 从 string 字符串第一次出现 *chars 的位置开始,截取 *chars 左边的所有字符
${string%%*chars} 从 string 字符串最后一次出现 *chars 的位置开始,截取 *chars 左边的所有字符

13、分割字符串

IN="bla@some.com;john@home.com"
arrIN=(${IN//;/ })
echo ${arrIN[1]}                  # Output: john@home.com
IFS=';' read -ra ADDR <<< "$IN"
for i in "${ADDR[@]}"; do
   echo "$i"
done
$ echo "bla@some.com;john@home.com" | cut -d ";" -f 1
bla@some.com
$ echo "bla@some.com;john@home.com" | cut -d ";" -f 2
john@home.com

14、Bash解析JSON

### 方法简要说明:
### 1. 是先查找一个字符串:带双引号的key。如果没找到,则直接返回defaultValue。
### 2. 查找最近的冒号,找到后认为值的部分开始了,直到在层数上等于0时找到这3个字符:,}]。
### 3. 如果有多个同名key,则依次全部打印(不论层级,只按出现顺序)
### @author lux feary
###
### 3 params: json, key, defaultValue
function getJsonValuesByAwk() {
    awk -v json="$1" -v key="$2" -v defaultValue="$3" 'BEGIN{
        foundKeyCount = 0
        while (length(json) > 0) {
            # pos = index(json, "\""key"\""); ## 这行更快一些,但是如果有value是字符串,且刚好与要查找的key相同,会被误认为是key而导致值获取错误
            pos = match(json, "\""key"\"[ \\t]*?:[ \\t]*");
            if (pos == 0) {if (foundKeyCount == 0) {print defaultValue;} exit 0;}

            ++foundKeyCount;
            start = 0; stop = 0; layer = 0;
            for (i = pos + length(key) + 1; i <= length(json); ++i) {
                lastChar = substr(json, i - 1, 1)
                currChar = substr(json, i, 1)

                if (start <= 0) {
                    if (lastChar == ":") {
                        start = currChar == " " ? i + 1: i;
                        if (currChar == "{" || currChar == "[") {
                            layer = 1;
                        }
                    }
                } else {
                    if (currChar == "{" || currChar == "[") {
                        ++layer;
                    }
                    if (currChar == "}" || currChar == "]") {
                        --layer;
                    }
                    if ((currChar == "," || currChar == "}" || currChar == "]") && layer <= 0) {
                        stop = currChar == "," ? i : i + 1 + layer;
                        break;
                    }
                }
            }

            if (start <= 0 || stop <= 0 || start > length(json) || stop > length(json) || start >= stop) {
                if (foundKeyCount == 0) {print defaultValue;} exit 0;
            } else {
                print substr(json, start, stop - start);
            }

            json = substr(json, stop + 1, length(json) - stop)
        }
    }'
}

json='{"code":200,"msg":"success","data":{"orderNo":"test_order_no"}}'
getJsonValuesByAwk "$json" "code" "defaultValue"
getJsonValuesByAwk "$json" "data" "defaultValue"
getJsonValuesByAwk "$json" "orderNo" "defaultValue"

15、bash解析JSON:使用工具

raw='{"name":"tenmao","age":10,"hobbies":["bar","foo"],"address":{"province":"gd","city":"sz"}}'
echo $raw | jq -r '.name'
echo $raw | jq -r '.hobbies'
echo $raw | jq -r '.hobbies[0]'
echo $raw | jq -r '.address.city'

16、清除字符串首尾的空格

STR="      A B  CCC D "
echo "$STR" | grep -o "[^ ]\+\( \+[^ ]\+\)*"

17、find

#列举出当前目录所有大于800M的文件
find . -type f -size +800M
du -h --max-depth=1
du -hm --max-depth=2 | sort -n
du -hm --max-depth=2 | sort -nr | head -12

#搜索文件并打印修改日期
find ./  -type f -name "*dns*" -printf "%TY-%Tm-%Td %TH:%TM %p\n"

# find搭配 xargs
find ./ -name "*.go" |xargs ls -l

# find 搭配tar打包
find . -type f -name "*.jpg" -print | xargs tar -czvf images.tar.gz

18、netstat or ss
在现网中建议使用ss命令更为高效且对系统影响较低

  • ss(socket statistics)命令是Linux系统中用于查看网络连接状态的工具,它可以显示更多详细的网络连接信息,比如连接的进程、内存使用情况等。ss命令的性能更好,因为它直接从内核中获取网络连接信息,而不需要像netstat那样扫描整个/proc文件系统。这使得ss命令在处理大量网络连接时更加高效
  • netstat(network statistics)命令也可以用于查看网络连接状态,但它已经被标记为过时,因为它在处理大量网络连接时性能较差。netstat命令通过扫描/proc文件系统来获取网络连接信息,这使得它在处理大量网络连接时速度较慢,对系统资源的消耗也较大

ss快的秘诀在于,它利用到了TCP协议栈中tcp_diagtcp_diag是一个用于分析统计的模块,可以获得Linux内核中第一手的信息,这就确保了ss的快捷高效。

常用命令1:查看当前服务器的网络连接统计

$ ss -s
Total: 295 (kernel 312)
TCP:   48 (estab 1, closed 31, orphaned 0, synrecv 0, timewait 0/0), ports 13

Transport Total     IP        IPv6
*         312       -         -
RAW       0         0         0
UDP       2         2         0
TCP       17        12        5
INET      19        14        5
FRAG      0         0         0
#在服务器产生大量sockets连接时,使用这个命令在做宏观统计

常用命令2:查看所有打开的网络端口

$ ss -l
Recv-Q Send-Q           Local Address:Port               Peer Address:Port
0      128                         :::webcache                      :::*
0      128                         :::http                         :::*
0      128                         :::snapenetio                      :::*
0      128                          *:snapenetio                       *:*
0      50                           *:8531                          *:*
0      9                           :::ftp                          :::*
0      9                            *:ftp                           *:*
0      128                          *:ddi-tcp-1                       *:*
0      100                        ::1:smtp                         :::*
0      100                  127.0.0.1:smtp                          *:*
0      128                          *:8541                          *:*
0      128                  127.0.0.1:entextxid                       *:*
0      50                           *:12421                         *:*
0      10                           *:amqp                          *:*
0      128                          *:12521                         *:*
0      50                           *:mysql                         *:*
#如果使用-pl参数的话,则会列出具体的程序名称。你会在输出中看到类似于这样的内容 ("nginx",15786,6)

常用命令3:查看这台服务器上所有的socket连接

ss -a
#如果只想查看TCP sockets,那么使用-ta选项
#如果只想查看UDP sockets,那么使用-ua选项
#如果只想查看RAW sockets,那么使用-wa选项
#如果只想查看UNIX sockets,那么使用-xa选项

19、xargs命令

常用命令1:xargs 结合 find 使用的场景,举个例子,用 rm 删除太多的文件时,可能会报错/bin/rm Argument list too long, 可使用 xargs 规避:

find . -type f -name "*.log" -print0 | xargs -0 rm -f       
#xargs -0 将 \0 作为定界符

常用命令2:

#统计一个源代码目录中所有 golang 文件的行数
find . -type f -name "*.go" -print0 | xargs -0 wc -l

#查找所有的 jpg 文件,并且压缩它们
find . -type f -name "*.jpg" -print | xargs tar -czvf images.tar.gz #这个有点意思

#使用 xargs下载所有链接
cat url-list.txt | xargs wget -c

# grep
find ./ -name "*.go" | xargs grep 'example'

0x03 参考