linux基础命令学习

面试自闭了, 回顾一下基础

准备

靶场:

https://www.hackerrank.com/

不足的地方再补充

手册:

shell基础

shebang

shell脚本的语法还是要了解的, 这有利于我们写脚本

一般我们会在第一行写一个Shebang, 也就是声明用的环境

#!/bin/bash

这样的话在运行的时候可以不加前面的解释器, 直接运行

./shell.sh

变量

一般来说空格不应该影响语言的实现, 但是在shell脚本中, 空格有时候会有些特殊的含义, 所以能不加还是不加吧

变量声明如下

i=0
# error example
i = 0

声明后通过加$来引用

echo $i

获取输入的话用的是read

read num

循环

可以参考 https://www.cnblogs.com/EasonJim/p/8315939.html

一般来说我们循环都是通过

while/for 循环语句
do
# do something
done

这样的方式来编写, 例如for语句

for((i=1;i<=10;i++));  
do
echo $i;
done

在shell脚本中, ;可以写也可以不写, 如果一行一个语句的话是没有区别的

for语句的地方需要用(())来包裹, 不然会报错

正确执行如下

另一种写法如下

for i in {1..10}
do
echo $i
done

道理相同, while的话如下

while ((i<10))
do
echo $i
((i++))
done

可以看到这里也是用了(())进行包裹, 在shell中, (())一般用来表示数学运算, 如果不加的话直接会报错

其中((i++))等价于i=$((i+1))

if-else

大概的语法是

if [ express ]
then
# do something
elif [ express ]
then
# do something
else
# do something
fi

注意[ express ]的空格必须保留

关于[][[]]的区别可以阅读 https://stackoverflow.com/questions/669452/is-double-square-brackets-preferable-over-single-square-brackets-in-ba

从简单的使用来说, [[]]会方便一些

数组

数组的声明方式如下

array=(A B "C" D)

或者直接用

array[0]="A"

来声明, 这个用法差不多, 比较有趣的特性是

获取数组所有元素

${array[@]}
# or
${array[*]}

获取数组长度

${#array[@]}

对数组元素进行处理

${array[@]/[a-z]/*

题目

  1. 输出1到50

    #!/bin/bash
    for i in {1..50}
    do
    echo $i
    done

  2. 计算两个数的加减乘除

    #!/bin/bash
    read x
    read y
    echo `expr $x + $y`
    echo `expr $x - $y`
    echo `expr $x \* $y`
    echo `expr $x / $y`

    这里使用了read读取输入, 然后通过expr计算表达式并输出

  3. 比较两个数的大小并输出相应的值

    #!/bin/bash
    read x;
    read y;
    [[ $x -gt $y ]] && echo "X is greater than Y";
    [[ $x -eq $y ]] && echo "X is equal to Y";
    [[ $x -lt $y ]] && echo "X is less than Y";

    这里用了&&来进行输出, 很好懂

  4. 输入y|Y则输出YES, 反之输出NO

    #!/bin/bash
    read choice
    if [[ $choice == "Y" || $choice == "y" ]]
    then
    echo "YES"
    else
    echo "NO"
    fi

  5. 计算表达式并输出3位小数

    #!/bin/bash
    read exp
    printf "%.3f" $(echo $exp|bc -l)

    这里使用了printf进行格式化输出, 和c是一样的, 另外用bc进行运算, -l表示设置小数位数为20, 也可以设置其他的值

  6. 计算平均数

    #!/bin/bash
    read n
    exp=0
    for(( i=0;i<$n;i++ ))
    do
    read x
    exp=$((exp + x))
    done
    printf "%.3f" $(echo "$exp/$n"|bc -l)

  7. 读入数组并输出3:5的部分

    #!/bin/bash
    i=0
    while read line
    do
    words[$i]=$line
    i=$((i+1))
    done
    echo ${words[@]:3:5}

    更简洁的做法是

    #!/bin/bash
    words=$(cat)
    echo ${words[@]:3:5}

    通过cat获取输入, 然后输出切片即可

  8. 过滤数组中含有a|A的元素

    #!/bin/bash
    words=$(cat)
    echo ${words[@]/*[aA]*/}

    他的语法是

    • 使用b替换a, 只替换第一个

      /a/b

    • 全部替换

      //a/b

      有点像正则, 但是又有些不太一样

  9. 取出下标为3的元素

    #!/bin/bash
    words=$(cat)
    echo ${words[3]}

  10. 输出元素个数

    #!/bin/bash
    words=$(cat)
    echo ${#words[@]}

  1. 替换首字母为*

    #!/bin/bash
    words=$(cat)
    echo ${words[@]/[A-Z]/.}

  2. 输出没有重复的值

    read
    words=$(cat)
    words2=${words[@]// /^}
    echo $((words2))

    做法很巧妙, 由于输入的值只有一个是不重复的, 我们可以使用异或来求值

    首先读取输入为数组

    words=$(cat)

    然后展开为字符串, 并替换空格为^

    words2=${words[@]// /^}

    然后计算表达式, 输出

    echo $((words2))

简单的脚本编写就到这里了, linux的灵魂还是在于命令的组合

文本处理

知道就行, 这一块其实用的不多, 比较重要的是后面的sed, awk, grep

cut

主要参数如下

  • -b: 按bytes选取
  • -c: 按char选取
  • -f: 按列选取
  • -d: 设置分隔符, 如-d';'

选取的范围语法

从题目来进行了解

题目

假定题目内容使用管道传入, 其他命令相同, 相关的输入内容可以到靶场查看,

  1. 选取第3个字符

    cut -c3

  2. 选取第2和第7个字符(超出则不显示, 不报错)

    cut -c2,7

  3. 选取第2到第7个字符

    cut -c2-7

  4. 选取到第4个字符

    cut -c-4

  5. 选取前3列, 按\t分隔

    cut -f-3

  6. 选取第4列, 按分隔

    cut -d' ' -f4

head & tail

这两个命令相近, 放在一起

head语法如下

tail语法如下

主要参数如下

  • -c: 按bytes选取
  • -n: 按行选取
  • -f: 监视文件更新(tail)

题目

  1. 选取前20行

    head -n 20

  2. 选取前20个字节

    head -c 20

  3. 选取后20行

    tail -n 20

  4. 选取第12行到22行

    head -n 22|tail -n +12

    这里的+12(tail)的意思是, 打印从12行开始的所有行, 同样我们可以使用-12(head)表示打印从最后的12行往前的所有行

tr

用于替换

其中特殊的pattern如下

题目

  1. 替换()[]

    tr "()" "[]"

  2. 删除所有小写字母

    tr -d "[:lower:]"

  3. 替换多个空格为单个空格

    tr -s "[:space:]"

sort

主要的参数如下

  • -d: 字典序
  • -n: 数字排序
  • -r: 逆序(默认为升序)
  • -f: 大小写忽略(视为大写)
  • -t: 列的分隔符

题目

  1. 按字典序升序

    sort

  2. 按数字降序

    sort -rn

  3. 按第二列升序排序, 分隔符为

    sort -k2,2 -t' '

    这里需要注意的是, -k2,2表示按第二列排序(如果不加,2在相同的时候会按照后面的列排序), -t''则是表示按空格分隔

  4. 按第二列升序排序, 分隔符为\t

    sort -k2,2 -t$'\t'

    这里不同的地方在于, 分隔符为特殊符号, 我们需要让他解释为TAB, 而不是\+t, 因此这里需要加一个$, 并且不能使用双引号, 具体的原因可能是底层实现的解释有关吧, 一般来说我们使用"包裹都应该可以转义的

uniq

直接看题目吧

题目

  1. 输出重复的值

    uniq

  2. 输出每个值及重复次数(包括1次)

    uniq -c|xargs -L1

    我们看一下xargs和不加的区别

    其实就是消除前面的空格而已

  3. 忽略大小写输出重复的值和重复次数

    uniq -c -i|xargs -L1

  4. 输出不重复的值

    uniq -u

paste

我觉得也是替换

题目

  1. 将多行内容合并为一行, 用分号分隔

    paste -sd';'

    这样的看的话应该比较好理解

    -s将多行合并为一行, 然后用-d标志分隔符

  2. 将多行内容每三行合并为一行, 用分号分隔

    paste -sd';;\n'

    这个命令简直神奇, 他的分隔符是可以迭代的, 这里传入了三个, 那么依次为;, ;, \n作为分隔符, 十分神奇

sed, grep, awk

这三个号称三剑客, 在linux的命令中占有相当大的比重, 需要重点关注

grep

这个应该都用过了, 先拿来讲讲

部分参数如下

比如我们要搜索一个字符串, 最简单的就是

grep cat

如果是带有符号的, 需要加上"

如果想要输出行号, 加上-n

要输出前后的行呢

想要找到没有匹配的, 加上-v

想要忽略大小写, 就加上-i, 想用正则, 就加上-e

….

这个命令的使用场景很多, 可以参考 https://linux.cn/article-5453-1.html

我们先看一下题目吧

题目

  1. 找出所有含有the的行

    grep -w "the"

    按词匹配

  2. 找出所有含有the|those|that的行

    grep -Ew "th(e|at|en|ose)"

    使用E表示拓展的正则表达式, 用起来比较方便, 如果是e的话, 一些特殊符号需要加上\转义

    具体的区别可以看 https://stackoverflow.com/questions/17130299/whats-the-difference-between-grep-e-and-grep-e

  3. 忽略大小写找出含有the的行

    grep -wi "the"

  4. 找出没有the的行

    grep -vw "the"

  5. 找出信用卡号码存在相邻位相同的卡号, 例如1234 4567中的4 4

    grep -e "\([0-9]\)\s*\1"

    这里用的是-e来进行正则匹配, 其中\1表示第一个块(括号)匹配到的项, 在这里是([0-9])这个块, \s表示空格或\t\n, *表示0或多次, 假设括号匹配到1, 那么\1就是4

当然我们常用的还有-r递归查找的, 例如

grep -rin flag

则表示在当前目录递归查找flag字符串

另一个比赛常用的是

find / -iname "*flag*"

这个查找的是文件名, 另外还有一个更快的查找文件内容的是ag, 不过不在本文范围内, 感兴趣的可以了解一下, 真的快很多, awd可能有奇效

sed

sed命令主要是用来进行替换字符串, 当然功能比前面提到的要强大很多

更详细的介绍可以参考 https://coolshell.cn/articles/9104.html, 下面会引用部分内容

看一下相关的使用场景吧

s命令

假设我们现在存在一个文件input.txt

From fairest creatures we desire increase,
That thereby beauty's rose might never die,
But as this riper should by time decease,
His tender heir might bear his memory:
But thou contracted to thine own bright eyes,
Feed'st thy light's flame with self-substantial fuel,
Making a famine where abundance lies,
Thy self thy foe, to thy sweet self too cruel:
Thou that art now this world's fresh ornament,
And only herald to this gaudy spring,
Within thine own bud buriest thy content,
And tender churl mak'st waste in niggarding:
Pity this world, or else this glutton be,
To eat this world's due, by the grave and thee.
When forty winters shall besiege thy brow,
And dig deep trenches in thy beauty's field,
Thy youth's proud livery so gazed on now,
Will be a tattered weed of small worth held:
Then being asked, where all thy beauty lies,
Where all this treasure of thy lusty days;
To say within thine own deep sunken eyes,
Were an all-eating shame, and thriftless praise.
How much more praise deserved thy beauty's use,
If thou couldst answer 'This fair child of mine
Shall sum my count, and make my old excuse'

将其中的the全部替换为this

sed "s/\bthe\b/this/"

根据手册, 如果没有提供-e, -f等参数, 则会默认使用第一个不是-开头的参数作为script(也就是相当于前面加上了-e)

首先是替换的格式, 将每行的首个a替换成b,

sed "s/a/b/"

全部替换的是

sed "s/a/b/g"

\b表示的是单词边界, 他是零宽的, 也就是不会实际匹配任何字符, 但是会识别任意的单词分隔, 在一些边界更准确, 如果只是

sed "s/the/this/"

会使得如then也被替换

如果是

sed "s/\sthe\s/this/"

由于\s不是零宽的, 也会匹配上, 在替换的时候就会有些问题

和vim类似, 我们需要匹配某几行的时候, 可以用

3,5s/a/b/3

这样的方法来匹配第3行到第5行中的每一行的第3个a

假设我们要去掉一段html文本中的内容

<b>This</b> is what <span style="text-decoration: underline;">I</span> meant. Understand?

我们如果

sed "s/<.*>//g"

是因为正则是默认贪婪匹配的, 那么我们可以这样

sed "s/<[^>]*>//g"

试了一下加上?不能实现非贪婪匹配, 查了资料需要用其他的方法, 有兴趣的可以去看看

而这里的正则和其他的正则一样, 都可以用\num来获取前面括号的匹配内容, 比如下面的例子

sed 's/This is my \([^,&]*\),.*is \(.*\)/\1:\2/g'

我们拆分一下, 首先第一个括号是

([^,&]*)

表示匹配除了,&之外的任意多次的内容

第二个是

(.*)

匹配任意内容

假设文本为

This is my cat, my cat is betty

那么匹配为

This is my (cat), my cat is (betty)

N命令

将下一行的内容纳入缓冲区一起匹配

也就是原本匹配一行变成了两行一起匹配

看下面的例子, 原本

sed "s/my/your/"

会替换每一行的第一个my, 而加上`N”后

偶数行的部分没有被替换, 那如何每两行连接在一起呢

sed "N;s/\n//"

a命令和i命令

和vim很像, a表示追加(append), i表示插入(insert)

sed "1 i line1"

表示在第一行前面插入

sed "$ a line last"

表示在最后一行后面追加

c命令

替换匹配行

可以用

sed "1 c line1"

或者

sed "/fish/c line1"

d命令

删除匹配行

使用方式类似

sed "1 d"

sed "/fish/d"

p命令

这是用来打印的命令, 如果匹配到就会打印多一次

加上-n则只会输出匹配到的行 (grep)

如果要全文本打印的话(cat)

sed -n "p"

写入文件

上面的操作都是在STDOUT中操作的, 对文件内容没有修改, 如果需要修改, 可以加上-i参数

others

对于地址的匹配模式为

[start[,end]][!]{cmd}

其中startend都可以使用模式匹配或直接声明数字, 比如

1,3
1,+3
/dog/, /fish/

!表示匹配到是否执行后面的cmd, 如果加上!则不执行

cmd可以嵌套

# 单条命令
{/fish/d}
# 命令与
{/fish/{/name/d}}
# 多条命令
{/fish/d;s/goat/target/}

还有一个是关于hold spacespattern space的介绍, 图很形象, 这里就直接搬运了

题目

  1. 将每行第一个the换成this

    sed "s/\bthe\b/this/"

  2. 将所有的thy换成they

    sed "s/\bthe\b/this/g"

  3. 将所有的thy{}包裹起来, 忽略大小写

    sed "s/thy/{&}/Ig"

    其中&表示前面匹配到的内容

  4. 将每行的前三组数字替换成****

    sed -E "s/[0-9]{4} /**** /g"

  5. 将每行的四组数字逆序输出

    sed -E "s/(.+) (.+) (.+) (....)/\4 \3 \2 \1/"

    其中\num表示前面第几组括号的匹配内容

    下面开始是网上收集的一些题目

  6. 打印文件的所有行

    sed -n "p"

  7. 将每行第一个数字移动到行末尾

    sed -r "s/([0-9])(.+)$/\2\1"

  8. 逆序输出

    sed "1!G;h;$!d"

  9. 输出偶数行

    sed -n "n;p"

更多高级用法可以参考 http://www.178linux.com/96587

再深入就不好记了, 不如到生活中去学习吧

awk

不知道是不是我装的版本问题…他竟然没有--help, 直接敲awk即可看到帮助

这个看起来似乎少了很多选项, 但是它的功能也十分强大, 毕竟是能用来提权的命令

主要用于提取文本和格式化输出

同样是参考自 https://coolshell.cn/articles/9070.html, 实验文本最后两行做了一点小修改

Proto Recv-Q Send-Q Local-Address          Foreign-Address             State
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN
tcp 0 0 coolshell.cn:80 124.205.5.146:18245 TIME_WAIT
tcp 0 0 coolshell.cn:80 61.140.101.185:37538 FIN_WAIT2
tcp 0 0 coolshell.cn:80 110.194.134.189:1032 ESTABLISHED
tcp 0 0 coolshell.cn:80 123.169.124.111:49809 ESTABLISHED
tcp 0 0 coolshell.cn:80 116.234.127.77:11502 FIN_WAIT2
tcp 0 0 coolshell.cn:80 123.169.124.111:49829 ESTABLISHED
tcp 0 0 coolshell.cn:80 183.60.215.36:36970 TIME_WAIT
tcp 0 4166 coolshell.cn:80 61.148.242.38:30901 ESTABLISHED
tcp 0 1 coolshell.cn:80 124.152.181.209:26825 FIN_WAIT1
tcp 0 0 coolshell.cn:80 110.194.134.189:4796 ESTABLISHED
tcp 0 0 coolshell.cn:80 183.60.212.163:51082 TIME_WAIT
tcp 0 1 coolshell.cn:80 208.115.113.92:50601 LAST_ACK
tcp 0 0 coolshell.cn:80 123.169.124.111:49840 ESTABLISHED
tcp 0 0 coolshell.cn:80 117.136.20.85:50025
tcp 0 0 :::22

首先看一下基本的格式

awk '{print $1,$5}'

注意列数的计数从1开始, 默认按空格或\t分隔, 使用{}包裹命令, 然后$num代表列数, $0则是整行

缺少的列数则为空, 还有就是一般用'单引号包裹, 避免$被shell解析

假设我们要筛选只有4列的行

awk '!$5 {print $0}'

我们可以用-F来指定分隔符, 加上[]可以指定多个

awk -F'[:]' '{print $1}'

输出的分隔符可以使用OFS指定

awk -F'[:]' '{print $1,$2}' OFS=":"

内部的预定义变量为

$0 当前记录(这个变量中存放着整个行的内容)
$1~$n 当前记录的第n个字段,字段间由FS分隔
FS 输入字段分隔符 默认是空格或Tab
NF 当前记录中的字段个数,就是有多少列
NR 已经读出的记录数,就是行号,从1开始,如果有多个文件话,这个值也是不断累加中。
FNR 当前记录数,与NR不同的是,这个值会是各个文件自己的行号
RS 输入的记录分隔符, 默认为换行符
OFS 输出字段分隔符, 默认也是空格
ORS 输出的记录分隔符,默认为换行符
FILENAME 当前输入文件的名字

如果想当成grep用的话, 可以这样

awk '/LISTEN/'

对某一列进行选取

awk '$6 ~ /LISTEN/'

~表示模式的开始, 前面是匹配的项, 也就是第六列, 取反的话加上一个!

awk '$6 !~ /LISTEN/'

如果选择多个的话我们可以使用|等逻辑运算符

如果想用awk来进行一些运算, 例如计算总行数

awk '{sum+=1} END{print sum}'

这里利用的是END语句来进行最后的输出, 而不是在每行处理的时候进行输出

或者利用内建变量

awk 'END{print NR}'

当然最简洁的是

wc -l

而且awk还能使用if-else这类的语句

awk '{if($6=="FIN_WAIT2")print "FIN";else print "OTHERS"}'

我们可以当成一个脚本解释器来运用他, 因为他甚至还有循环和数组语句, 十分可怕

体会一下逆序输出

awk '{line[NR]=$0} END{for(i=NR;i>0;i--) print line[i]}'

在脚本比较复杂的时候, 我们可以使用文件加载脚本(前面的sed也有这样的功能)

{ 
line[NR] = $0
}

END {
for (i = NR; i > 0; i =--)
print line[i]
}

题目

  1. 打印只有两项成绩的学生名

    awk '!$4 {print $1}'

  2. 如果有一项成绩低于50, 则标记为FAIL, 否则标记为PASS

    awk '{print $1,":", ($2<50||$3<50||$4<50) ? "FAIL" : "PASS"}'

    这是是用了三目表达式, 和c是一样的, 另外用,分隔的项, 输出会自动加上空格分隔

  3. 根据平均分标记不同的等级

    awk '{avg=($2+$3+$4)/3;print $1,":", (avg>=80)?"A":(avg>=60)?"B":(avg>=50)?"C":"FAIL"}'

  4. 每两行合并输出

    awk '{if(NR%2) printf "%s;" $0;else print $0}'

  5. 打印第一列和最后一列, 使用@作为分隔符

    awk OFS="@" '{print $1,$NF}'

  6. 将第二列相加求和

    awk '{sum+=$2} END{print "sum", sum}'

  7. 打印20行以后的行

    awk 'NR>20{print $0}'

后记

文章就先到这里了, 其实这些命令还有很多东西, 想了解的可以多看看教程和man手册

可能有朋友会问, 为什么要用这些很旧的工具呢? 明明有那么多图形界面的工具, 我用vscode不香吗?

香是肯定香的, 但是一般我们在服务器场景中, 没有图形界面, 只能用这些命令, 而且在大数据的处理中(海量日志), 这些命令的速度要比你下载下来, 然后编辑器打开, ctrl+f/h快很多的, 还能节省很多网络传输的时间

wireshark做的这么好, 不还是有tshark吗XD

参考链接

  1. https://www.hackerrank.com/
  2. https://coolshell.cn/articles/9104.html
  3. https://coolshell.cn/articles/9070.html
  4. https://my.oschina.net/u/3771583/blog/1632823


作者: cjm00n
地址: https://cjm00n.top/Linux/linux-command-learning.html
版权声明: 除特别说明外,所有文章均采用 CC BY 4.0 许可协议,转载请先取得同意。

系统命令学习 学习CVE-2020-1938

评论