面试 linux常识

linux常识

每个脚本开始的 #!/bin/sh 或 #!/bin/bash 表示什么意思 ?

1
#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell

如何调试 bash 脚本

使用 -x 选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
通过在运行脚本时使用 -x 选项,Bash 会在执行每一行命令之前打印该命令。这有助于查看脚本的执行流程和变量的值变化。

sh -x myshell.sh

[root@bigdata02 myshells]# sh -x while.sh
+ s=0
+ i=1
+ '[' 1 -le 100 ']'
+ s=1
+ i=2
+ '[' 2 -le 100 ']'
+ s=3
+ i=3
+ '[' 3 -le 100 ']'
+ s=6
+ i=4
+ '[' 4 -le 100 ']'
+ s=10
+ i=5
+ '[' 5 -le 100 ']'
+ s=15
+ i=6
+ '[' 6 -le 100 ']'
+ s=21
+ i=7
+ '[' 7 -le 100 ']'
+ s=28
+ i=8
+ '[' 8 -le 100 ']'
+ s=36
+ i=9
+ '[' 9 -le 100 ']'
+ s=45
+ i=10
+ '[' 10 -le 100 ']'
+ s=55
+ i=11

设置 set 命令

1
2
3
4
5
6
7
8
9
在脚本内部,可以使用 set 命令来开启或关闭调试模式。set -x 会打开调试模式,set +x 会关闭调试模式。

例如:
# 开启调试模式
set -x
# 一些要被调试的命令
echo "Debugging"
# 关闭调试模式
set +x

逐步调试 -v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Bash 的 -v 选项可以打印出脚本中的每一行命令,就像它们从脚本中读取出来一样,而不是执行后的结果。这有助于了解脚本的流程。


[root@bigdata02 myshells]# sh -v while.sh
#!/bin/bash
s=0
i=1
while [ $i -le 100 ]
do
s=$[$s + $i]
i=$[$i + 1]
done
echo $s
5050

使用 trap 命令

变量

1
2
3
4
5
打印变量

A='A'
echo "$A"
echo "${A}"
1
2
3
4
5
6
只读变量 readonly

[root@bigdata01 ~]# readonly age=5
You have new mail in /var/spool/mail/root
[root@bigdata01 ~]# age=8
-bash: age: readonly variable
1
2
3
4
5
6
7
删除变量 unset

[root@bigdata01 ~]# unset A
You have new mail in /var/spool/mail/root
[root@bigdata01 ~]# echo "${A}"

[root@bigdata01 ~]#
1
2
3
4
5
环境变量

你可以将其想象成是一种全局变量,对于整个操作系统或进程而言都是可见的。环境变量存储了一些配置信息、路径和其他重要的数据,它们可以被不同的程序和脚本访问和使用。

export MY_VARIABLE="Hello"

如何向连接两个字符串 ?

1
2
3
4
5
6
V1="Hello"
V2="World"
V3=${V1}${V2} 不用花括号也行
echo $V3

用加号会把加号一起打出来
1
2
3
greeting="Hello"
name="Alice"
message=$greeting" "$name
1
2
3
4
[root@bigdata01 ~]# A='A'
[root@bigdata01 ~]# B='B'
[root@bigdata01 ~]# echo $A$B
AB
1
message="${greeting} ${name}"

如何进行两个整数相加 ? 有点特别

1
2
3
4
5
linux:~ # A=1
linux:~ # B=2
linux:~ # C=$(($A+$B))
linux:~ # echo $C
3
1
2
3
4
5
linux:~ # A=1
linux:~ # B=2
linux:~ # C=$[$A + $B]
linux:~ # echo $C
3
1
2
3
4
5
linux:~ # A=1
linux:~ # B=2
linux:~ # C=`expr $A + $B`
linux:~ # echo $C
3
1
2
3
4
5
linux:~ # A=1
linux:~ # B=2
linux:~ # let C=$A+$B 有无花括号都一样
linux:~ # echo $C
3

‘ 和 “ 引号有什么区别 ?

1
2
3
4
5
' - 当我们不希望把变量转换为值的时候使用它。
" - 会计算所有变量的值并用值代替。

echo $name
echo "My name is $name"

如何只用 echo 命令获取字符串变量的一部分 ?

1
2
3
4
5
6
7
8
echo ${variable:x:y}

x - 起始位置
y - 长度

例子:
variable="My name is Petras, and I am developer."
echo ${variable:11:6} # 会显示 Petras

如何打印变量的最后 5 个字符 ?

1
echo ${variable:-5} -前空格

如何获取变量长度 ?

1
2
3
4
5
6
7
${#variable}

[root@bigdata02 myshells]# echo ${#a}
7
You have new mail in /var/spool/mail/root
[root@bigdata02 myshells]# echo $a
kjdjlfd

如何仅用 echo 命令替换部分字符串?

1
echo ${变量//模式/替换}

数组

1
2
3
4
5
fruits=("apple" "banana" "cherry")

echo ${fruits[0]} # 输出:apple
echo ${fruits[1]} # 输出:banana
echo ${fruits[2]} # 输出:cherry

shell传递参数

1
2
3
4
5
获取位置参数

# 脚本名:myscript.sh
echo "第一个参数是: $1"
echo "第二个参数是: $2"
1
2
3
4
5
6
# 脚本名:special.sh
echo "脚本名:$0"
echo "参数个数:$#"
echo "参数列表:$@"
echo "参数列表(作为单个字符串):$*"
echo "上一个命令是否执行成功:$?"
1
2
3
4
5
6
7
./special.sh arg1 arg2 arg3

脚本名:./special.sh
参数个数:3
参数列表:arg1 arg2 arg3
参数列表(作为单个字符串):arg1 arg2 arg3
上一个命令的退出状态:0

如何在后台运行脚本 ?

1
nohup command&

重定向

> 和>> 输出重定向

1
2
3
4
重定向输出流到文件或另一个流。

>覆盖
>>追加

< 输入重定向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
输入重定向

[root@localhost ~]# wc</etc/inittab
53 229 1666

wc统计行数,单词数, 字符数

其中wc的输入来自 /etc/inittab


[root@localhost ~]# wc <<aa
> 1
> 11
> 111
> aa
3 3 9

wc统计分隔符aa之间的内容。

重定向标准输出和标准错误流到 log.txt 文件 ?

1
在脚本文件中添加 exec >log.txt 2>&1 命令的作用是将脚本的标准输出(stdout,文件描述符为1,这里省略了完整应该是 exec 1>log.txt 2>&1)和标准错误输出(stderr,文件描述符为2)都重定向到同一个文件(在这个例子中是log.txt $表示绑定 &1表示和1绑定)中。这意味着,无论脚本在运行时输出到控制台的信息还是错误信息,都会被写入到log.txt文件中,而不是显示在终端或控制台上。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Linux的IO输入输出有三类
默认的标准输入指的是键盘,默认的标准输出与标准错误输出是屏幕或者是终端。
系统为这三个文件分配了文件标识符fd(file descripter).分别为0,1,2

Standard Input 代码 0
Standard Output 代码 1
Standard Error 代码 2

举个例子:ls test.sh test1.sh >success.txt 2>&1

将前面执行结果的标准输出流写入success.txt文件,省略了1,全句为:ls test.sh test1.sh 1>success.txt 2>&1。

错误输出用2,如2>error.txt。用&1表示类似1,和1绑定到一起,输出到一个文件,用&表示绑定在一起。

但注意
command > file 2>file 与command > file 2>&1 是不同的:

command > file 2>file :是将命令所产生的标准输出信息stdout和错误的输出信息stderr送到file中,但这样会导致file会被打开两次,所以stdout和stderr会互相覆盖。
而command >file 2>&1:将stdout直接送向file,file只被打开一次。实际相当于stderr合并到stdout后一起输出到file中。

从IO效率上,前一条命令的效率要比后面一条的命令效率要低,所以在编写shell脚本的时候,较多的时候我们会用command > file 2>&1 这样的写法.
1
2
3
4
5
ls 1>/dev/null 2>/dev/null
ls >/dev/null 2>&1

/dev/null是一个垃圾箱,是一个无底洞,表示的含义为不显示。
以上两个命令表示的含义是相同的。

如何让 Shell 就脚本得到来自终端的输入?

read读取控制台输入

1
2
3
4
5
6
7
read(选项)(参数)
选项:
-p:指定读取值时的提示符;
-t:指定读取值时等待的时间(秒)
-s 使得输入对用户不可见,这在读取密码时特别有用。
参数:
变量:指定读取值的变量名
1
2
3
4
>>vi read.sh
#!/bin/bash
read -t 7 -p "input your name:" NAME
echo $NAME
1
2
3
4
5
[root@bigdata02 myshells]# sh read.sh 
input your name:
[root@bigdata02 myshells]# sh read.sh
input your name:tianyong
tianyong

使用管道或重定向

1
2
3
4
5
你还可以通过管道(|)或输入重定向(<)来传递数据给 Shell 脚本,这允许你将其他命令的输出作为输入传递给脚本。

示例:

echo "John" | ./welcome_script.sh

如果给定字符串 variable=”User:123:321:/home/dir”,如何只用 echo 命令获取 home_dir ?

1
2
3
4
variable="User:123:321:/home/dir"
echo ${variable#*:*:*:}

echo ${variable##*:}
1
2
3
其中 # 符号用于从变量值中删除最短匹配指定模式的部分。具体来说,${variable#pattern} 会从变量 variable 的值中删除最短匹配 pattern 的部分,并返回剩余的部分

## 表示从变量值的开头开始删除最长匹配的部分。
1
2
# 表示从变量值的开头开始删除。
*:*:*: 是要匹配的模式,这里 * 是一个通配符,匹配任意长度的任意字符(包括零个字符,但在这个上下文中,由于它是被冒号分隔的,所以实际上它至少会匹配到一个冒号之前的所有内容)。因此,*:*:*: 匹配从变量值开头到第三个冒号(包括这三个冒号及其之间的所有内容)为止的字符串。

如何从上面的字符串中获取 “User” ?

1
2
3
echo ${variable%:*:*:*}

echo ${variable%%:*}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在 Bash 脚本中,${variable%:*:*:*} 和 ${variable%%:*} 使用了参数扩展(Parameter Expansion)功能的不同模式,它们都是用于从变量值的末尾开始删除内容,但删除的方式略有不同。

${variable%:*:*:*}
这个表达式尝试从变量 variable 的值中删除最短匹配 :*:*:* 的部分(从末尾开始匹配)。然而,由于 * 是一个贪婪匹配符,它会尽可能多地匹配字符,但这里的模式 :*:*:* 意味着它期望至少有三个冒号分隔的字段,这在大多数情况下可能不是你想要的效果,特别是当字符串的末尾没有足够多的冒号来匹配这个模式时。

实际上,如果你的意图是从末尾开始删除直到倒数第四个冒号(包括冒号)之前的所有内容,这个表达式可能无法按预期工作,因为 Bash 的参数扩展并不直接支持这种反向的、具有固定数量分隔符的匹配。不过,如果字符串恰好符合这种模式,并且你只是想移除末尾的特定部分,它可能会起作用(尽管这通常不是它的用途)。

但在这个例子中,由于 variable="User:123:321:/home/dir" 并不以 :*:*:* 结尾,所以这个表达式实际上不会删除任何内容。

${variable%%:*}
这个表达式会从变量 variable 的值中删除最长匹配 :* 的部分(从末尾开始匹配)。%% 表示删除最长匹配,而 :* 匹配一个冒号后跟任意数量的字符(直到字符串的末尾)。因此,这个表达式会删除从最后一个冒号开始到字符串末尾的所有内容。

如果 variable="User:123:321:/home/dir",那么 ${variable%%:*} 的结果将是 User:123:321,因为它删除了最后一个冒号及其之后的所有内容。

总结:

${variable%:*:*:*} 通常不是你想要用于从末尾删除内容的表达式,除非你的用例非常特定且字符串格式严格匹配。
${variable%%:*} 用于从字符串末尾删除最后一个冒号及其之后的所有内容。

如何列出第二个字母为 a 或 b 的文件?

1
ls -d ?[ab]*

tr命令

1
2
3
4
以下是tr命令的一些常用选项:

-d:删除SET1中所有的字符。
-s:压缩SET1中所有重复的字符。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
将小写字母转换为大写字母:
[linux@bashcommandnotfound.cn ~]$ echo 'hello world' | tr 'a-z' 'A-Z'

删除字符串中的所有数字:
[linux@bashcommandnotfound.cn ~]$ echo 'hello world123' | tr -d '0-9'

将字符串中的所有空格替换为下划线:
[linux@bashcommandnotfound.cn ~]$ echo 'hello world' | tr ' ' '_'

将字符串中的所有数字替换为字母:
[linux@bashcommandnotfound.cn ~]$ echo 'hello world123' | tr '0-9' 'a-j'

压缩字符串中的连续空格:
[linux@bashcommandnotfound.cn ~]$ echo 'hello world' | tr -s ' '

tr 如何从字符串中删除所有空格?

1
2
3
4
5
6
7
[root@bigdata02 myshells]# c=`echo $a|tr -d "j"` 
[root@bigdata02 myshells]# echo $c
kdlfd
[root@bigdata02 myshells]# echo $a
kjdjlfd

echo $string|tr -d " "

[ $a == $b ] 和 [ $a -eq $b ] 有什么区别

1
2
[ $a == $b ] - 可以用于字符串比较
[ $a -eq $b ] - 应该用于数字测试

=和==

1
2
3
4
= 和 == 之间有什么区别

= - 我们用来给变量赋值
== - 我们用来比较字符串

[]和[[]]?

比较操作符

1
2
3
4
5
6
7
8
9
10
11
12
13
在 [[]] 中可以使用比较操作符,如 >、< 等,如下:
$ [[ 1 < 2 ]] && echo "1 is less than 2"
1 is less than 2

上述命令中,我们使用 < 符号来检查 1 是否小于 2,命令是可以运行。但如果使用 [] 时,命令会报错
在这种情况下,Bash 会认为 < 是一个重定向符。因此,我们需要使用转义符 \ 进行转义:

$ [ 1 \< 2 ] && echo "1 is less than 2"
1 is less than 2

现在,使用 [] 也可以执行成功。

类似的,对于 > 符号也需要使用转义符 \ 进行转义。

对整数比较符

1
2
3
4
5
6
对整数比较符 -eq、-ne、-gt、-lt、-ge 和 -le,[] 和 [[]] 都可以正常比较,如下:

$ [[ 1 -lt 2 ]] && echo "1 is less than 2"
1 is less than 2
$ [ 1 -lt 2 ] && echo "1 is less than 2"
1 is less than 2

布尔操作符

1
2
3
4
5
6
7
8
9
在 [[]] 中,我们可以使用逻辑运算 && 和 ||,如下:

$ [[ 3 -eq 3 && 4 -eq 4 ]] && echo "Numbers are equal"
Numbers are equal
bash
但是在 [] 中,我们必须使用 -a、-o 分别代替 && 和 ||,如下:

$ [ 3 -eq 3 -a 4 -eq 4 ] && echo "Numbers are equal"
Numbers are equal

聚合表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在 [[]] 中,我们可以使用括号 () 来聚合多个表达式,() 的使用可以让脚本更具可读性,如下:
$ [[ 3 -eq 3 && (2 -eq 2 && 1 -eq 1) ]] && echo "Parentheses can be used"
Parentheses can be used
bash
上述命令中,我们使用了 () 对表达式 2 -eq 2 && 1 -eq 1 做了聚合,然后将其作为第 2 个表达式参与到 && 运算中,最后结果为验证为真。

但如果在 [] 中使用 (),则会报语法错误,如下:
$ [ 3 -eq 3 -a (2 -eq 2 -a 1 -eq 1) ] && echo "Parentheses can be used"
bash: syntax error near unexpected token `('
bash
上述 [] 中,我们使用 -a 来代替 && 但还是得到了一个报错。

在这里,我们必须使用转义符对括号进行转义,并且前后要保留一个空格,如下:
$ [ 3 -eq 3 -a \( 2 -eq 2 -a 1 -eq 1 \) ] && echo "Parentheses can be used"
Parentheses can be used
bash

通过上面的改造,命令就可以成功执行。

模式匹配

1
2
3
4
5
6
7
在 [[]] 中,我们还可以使用通配符进行模式匹配,如下:

$ name=Alice
$ [[ $name = *c* ]] && echo "Name includes c"
Name includes c
$ echo $?
0

正则表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
正则表达式是另一种字符串模式匹配的方式,在 [[]] 中,我们可以使用正则表达式来做模式匹配的工作。

$ name=Alice
$ [[ $name =~ ^Ali ]] && echo "Regular expressions can be used"
Regular expressions can be used
bash

那如果在 [] 中使用正则表达式呢?
$ name=Alice
$ [ $name =~ ^Ali ] && echo "Regular expressions can be used"
bash: [: =~: binary operator expected
bash
我们得到了一个错误,所以得到结论:在 [] 中不能使用正则表达式。

单词分割

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
在 [[]] 中,Bash 不会对值中的单词进行分割,比如变量的值是一个包含空格的字符串,Bash 不会将其分割成多个单词。

$ filename="none existent file"
$ [[ ! -e $filename ]] && echo -n "File doesn't exist;" && echo $filename
File doesn't exist;none existent file
bash
上面命令中,我们用一个不存在的文件作为示例,检查文件是否存在,但是值包含字符串。在 [[]] 中,我们可以直接使用 filename 变量,那在 [] 又如何?

$ filename="none existent file"
$ [ ! -e $filename ] && echo -n "File doesn't exist;" && echo $filename
[: too many arguments
bash

上面命令中,我们得到了一个错误。如果希望在 [] 也能使用带空格的字符串,需要加上双引号,如下:
$ filename="none existent file"
$ [ ! -e "$filename" ] && echo -n "File doesn't exist;" && echo $filename
File doesn't exist;none existent file

单中括号 []

1
2
3
4
5
6
7
8
9
单中括号是Shell内置命令 test 的另一种形式。它主要用于基本的字符串和数值比较。以下是一些关键点:

数值比较:
支持 -lt(小于)、-gt(大于)等符号。例如: if [ $a -gt 5 ]; then echo "a 大于 5" fi

字符串比较:
仅支持 = 和 != 两种比较方式。如果需要使用 > 和 <,则需要进行转义。例如: if [ "a" \< "b" ]; then echo "a 小于 b" fi

文件测试:可以用于测试文件属性,如存在性、可读性

双中括号 [[]]

1
2
3
4
5
6
7
双中括号是Shell的关键字,提供了更强大的功能和更灵活的语法。以下是一些关键点:

逻辑运算:支持 && 和 || 等逻辑运算符。例如: if [[ $a -gt 5 && $b -lt 10 ]]; then echo "a 大于 5 且 b 小于 10" fi

模式匹配:支持字符串的模式匹配,可以使用 =~ 操作符进行正则表达式匹配。例如: if [[ $string =~ ^[a-z]+$ ]]; then echo "字符串是小写字母" fi

不进行单词拆分和文件名扩展:在 [[ ]] 中,变量不会进行单词拆分和文件名扩展,但会进行参数扩展和命令替换

使用建议

1
2
3
数值比较:使用单中括号 []。

字符串比较和模式匹配:使用双中括号 [[]],因为它提供了更强大的功能和更灵活的语法

[[ $string == abc* ]] 和 [[ $string == “abc*” ]] 有什么区别

1
2
[[ $string == abc* ]] - 将检查字符串是否以 abc 字母开头
[[ $string == "abc*" ]] - 将检查字符串是否完全等于 abc*

在 shell 脚本中,如何测试文件 ?

1
2
3
4
5
6
7
8
9
10
11
12
test 命令可以用来测试文件。

基础用法如下表格:

Test 用法
-d 文件名 如果文件存在并且是目录,返回true
-e 文件名 如果文件存在,返回true
-f 文件名 如果文件存在并且是普通文件,返回true
-r 文件名 如果文件存在并可读,返回true
-s 文件名 如果文件存在并且不为空,返回true
-w 文件名 如果文件存在并可写,返回true
-x 文件名 如果文件存在并可执行,返回true
1
2
3
4
5
6
[root@bigdata02 myshells]# echo test -f while.sh 
test -f while.sh
You have new mail in /var/spool/mail/root
[root@bigdata02 myshells]# test -f while.sh
[root@bigdata02 myshells]# echo $?
0

硬链接和软链接

1
2
3
4
5
6
7
软连接:
可以对文件或者目录创建软链接
标文件被移动、重命名或删除,软链接将会失效
软连接可以跨越不同的文件系统
具有不同的inode

ln -s [目标文件或目录] [链接名]
1
2
3
4
软连接为目录时:
cd 软连接
pwd 打印的是当前软链接的位置
pwd -P 打印的是软连接对应目录的位置
1
2
删除软链接 rm 名字
不要加-rf 因为如果软链接对应的是目录时,这种方法删除的不是软链接,而是目录下的所有
1
2
3
4
5
6
7
8
9
硬链接:
可以对文件创建硬链接
不能跨文件系统。
一个文件可以有多个硬链接
修改一个文件的内容,另外硬链接文件内容也会改变
删除其中一个,另外的文件还可以使用
具有相同的inode

ll 或者ls -l 如果是文件会显示该文件硬链接数
1
2
3
ln [目标文件] [链接名]

df -Th 可以看当前linux系统包含的文件系统类别和容量

image-20250701171127966

date

获取系统当前时间

1
2
3
1.date命令默认获取系统当前时间
>>date
Sat Oct 26 18:35:06 2024

格式化

1
2
3
4
5
2.date命令支持对时间进行格式化

echo `date "+%Y-%m-%d %H:%M:%S"` #双引号是因为第三个空格;date后面要有空格

注意:date后面的这些参数中间如果没有空格,可以省略双引号。

时间戳

1
2
3
4
5
6
7
8
3.时间戳

这里面的%s表示获取自1970-01-01 00:00:00以来的秒数
[root@localhost ~]# date +%s
1729939111

虽然date命令没有直接支持获取毫秒数,但是从秒换算为毫秒也很简单啊,最直接粗暴的方式就是在秒数后面加3个0
[root@localhost ~]# date +%s"000"

获取指定时间的时间戳

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
date命令 提供的有--date这个参数,可以指定时间
date --date="2026-01-01 00:00:00"

[root@localhost ~]# date --date="2026-01-01 00:00:00"
Wed Jan 1 00:00:00 CST 2026
[root@localhost ~]# date --date="2026-01-01 00:00:00" +%s
1577808000

获取昨天的日期,这个需求也需要使用--date参数实现
[root@localhost ~]# date --date="1 days ago"
Sat Mar 28 21:36:37 CST 2026

再对返回的结果进行格式化,只获取年月日
date --date="1 days ago" +%Y-%m-%d
[root@localhost ~]# date --date="1 days ago" +%Y-%m-%d
2026-03-28
1
2
3
4
5
6
$ date --date="2024-10-28" +%s
1730044800

ty@DESKTOP-V3JFR2H MINGW64 ~/Desktop
$ date --date="2024-10-28"
Mon Oct 28 00:00:00 2024

/etc/profile和/etc/profile.d

1
2
3
4
5
/etc/profile 文件 登录或者切换用户都会读这个文件 全局的

/etc/profile.d 目录 里面有一些脚本文件 /etc/profile会循环执行这些脚本

/etc/profile.d/my_env.sh可以创建这个文件,在这里面配置环境变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@bigdata02 profile.d]# ll
total 56
-rw-r--r--. 1 root root 771 Aug 9 2019 256term.csh
-rw-r--r--. 1 root root 841 Aug 9 2019 256term.sh
-rw-r--r--. 1 root root 196 Mar 25 2017 colorgrep.csh
-rw-r--r--. 1 root root 201 Mar 25 2017 colorgrep.sh
-rw-r--r--. 1 root root 1741 Aug 6 2019 colorls.csh
-rw-r--r--. 1 root root 1606 Aug 6 2019 colorls.sh
-rw-r--r--. 1 root root 80 Oct 31 2018 csh.local
-rw-r--r--. 1 root root 1706 Aug 9 2019 lang.csh
-rw-r--r--. 1 root root 2703 Aug 9 2019 lang.sh
-rw-r--r--. 1 root root 123 Jul 31 2015 less.csh
-rw-r--r--. 1 root root 121 Jul 31 2015 less.sh
-rw-r--r--. 1 root root 81 Oct 31 2018 sh.local
-rw-r--r--. 1 root root 164 Jan 28 2014 which2.csh
-rw-r--r--. 1 root root 169 Jan 28 2014 which2.sh

ll|grep hudi

1
目录下文件多的情况下,用这个快速过滤

netstat -anp|grep 9083

1
查看监听端口

ls -al

1
2
3
4
5
.开头的路径和文件 ls不会显示
这是隐藏文件

-a 显示隐藏文件
-l 显示的很详细 = ll

useradd

1
2
3
4
5
6
7
8
adduser ty
passwd ty

su ty

userdel -r ty

useradd -r -g mysql mysql 为用户添加用户组

用户组 groupadd

1
groupadd mysql 用户组添加

chown

1
2
3
4
5
6
7
8
9
chown 命令,可以认为是 "change owner" 的缩写,主要用于修改文件(或目录)的所有者,除此之外,这个命令也可以修改文件(或目录)的所属组。

当只需要修改所有者时,可使用如下chown命令的基本格式:
[root@localhost ~]# chown [-R] 所有者 文件或目录

-R(注意大写)选项表示连同子目录中的所有文件,都更改所有者。

如果需要同时更改所有者和所属组,chown 命令的基本格式为:
[root@localhost ~]# chown [-R] 所有者:所属组 文件或目录
1
2
3
4
5
6
7
修改文件的拥有者 
chown
功能:修改文件的拥有者
格式:chown [参数] 用户名 文件名

$ sudo chown root file
$ sudo chown [-R] root:root file 同时修改所有者和所属组

chgrp

1
2
3
4
5
6
7
8
9
10
修改文件的所属组
chgrp

功能:修改文件或目录的所属组
格式: chgrp [参数] 用户组名 文件名
常用选项: -R 递归修改文件或目录的所属组

实例:
# chgrp root file
$ sudo chgrp g1 file

权限 chmod

讲得好url

image-20240316183435935

1
超级用户的命令提示符是“#”,普通用户的命令提示符是“$”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
drwxr-xr-x.  4 root root     38 Feb 26 13:18 xdg

开头:
d:目录文件
-:普通文件(文本,源代码,可执行程序,第三方动静态库)
l:软链接(类似Windows的快捷方式)
b:块设备文件(例如硬盘、光驱等)
p:管道文件
c:字符设备文件(例如屏幕等串口设备)
s:套接口文件

后面的分三个三个来看 所有者权限 用户组权限 其他用户组权限
数字
第一个root位置 所有者
第二个root位置 用户组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
chmod
功能: 设置文件的访问权限
格式: chmod [参数] 权限 文件名

常用选项:
R -> 递归修改目录文件的权限
说明:只有文件的拥有者和root才可以改变文件的权限

法一:用户表示符+/-=权限字符
+:向权限范围增加权限代号所表示的权限
-:向权限范围取消权限代号所表示的权限
=:向权限范围赋予权限代号所表示的权限
用户符号:
u:拥有者
g:拥有者同组用
o:其它用户
a:所有用户

chmod u+x file
chmod g+x file
chmod u+x,g+x,o+rw file

法二:使用三位八进制数字
chmod 774 file

ps -ef|grep runJar

image-20240317161417201

1
runjar是hive的服务

jstat -gcutil PID 1000

1
通过jstat -gcutil PID 1000来看看这个进程GC的信息,每一秒钟刷新一次,如果GC次数增长过快,说明内存不够用

ip addr|ifconfig

touch cat more

1
2
3
touch 创建文件
cat -b 显示时输出行号
more 不一次显示全

tail head

1
2
3
4
tail -f 文件 动态显示
tail -F 文件 等同于–follow=name --retry,根据文件名进行追踪,并保持重试,即该文件被删除或改名后,如果再次创建相同的文件名,会继续追踪

tail -n 文件 文件后几行
1
2
如何获取文本文件的第 10 行 ?
head -10 file
1
2
3
获取文件的最后/第一行?
head -1 file
tail -1 file

echo

1
2
3
4
5
6
7
8
9
echo:将内容输出到设备,类似java里面的system.out.println()
常见用法:
echo "hello\t\t world!" 不解析转义字符
echo -e "hello\t\t world!" 解析转义字符

[root@localhost ~]# echo "hello\t\t world!"
hello\t\t world!
[root@localhost ~]# echo -e "hello\t\t world!"
hello world!

history

1
2
3
4
5
6
history保留了最近执行的命令记录,默认可以保留1000。
历史清单从0开始编号到最大值。
常见用法:
history N 显示最近N条命令
history -c 清除所有的历史记录
history -w xxx.txt 保存历史记录到文本xxx.txt

df

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
磁盘使用情况
df -h
df -h -T


[root@bigdata01 /]# df -h -T
Filesystem Type Size Used Avail Use% Mounted on
devtmpfs devtmpfs 898M 0 898M 0% /dev
tmpfs tmpfs 910M 0 910M 0% /dev/shm
tmpfs tmpfs 910M 9.5M 901M 2% /run
tmpfs tmpfs 910M 0 910M 0% /sys/fs/cgroup
/dev/mapper/centos-root xfs 47G 10G 38G 22% /
/dev/sda1 xfs 1014M 150M 865M 15% /boot
tmpfs tmpfs 182M 0 182M 0% /run/user/0
You have new mail in /var/spool/mail/root
[root@bigdata01 /]# ^C
You have new mail in /var/spool/mail/root
[root@bigdata01 /]# ^C
[root@bigdata01 /]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 898M 0 898M 0% /dev
tmpfs 910M 0 910M 0% /dev/shm
tmpfs 910M 18M 893M 2% /run
tmpfs 910M 0 910M 0% /sys/fs/cgroup
/dev/mapper/centos-root 47G 10G 38G 22% /
/dev/sda1 1014M 150M 865M 15% /boot
tmpfs 182M 0 182M 0% /run/user/0

vi

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1.查找字符串  对文件中字符串的快速查找
vi里的命令模式:/查找内容
有多个结果是n代表下一个查询结果

2.查找某一行内容 已经知道了需要修改的内容在文件的第几行
vi里的命令模式:shift+冒号+具体行号

3.使vi编辑里每行具有行号
shift+冒号+set nu

4.复制粘贴
使用vi命令打开hello.txt,把光标移动到希望复制的那一行内容上面,然后连按yy,这样就把这一行内容复制上了,然后按p就会把刚才复制的内容粘贴到下一行,按一次p粘贴一行,一直按到你喊停为止。
最后按shift和: 输入wq保存退出即可。

多行赋值:nyy
1
2
3
命令行模式下输入
6,9 co 12
复制第6行到第9行之间的内容到第12行后面。
1
2
3
4
5
6
7
有时候不想费劲看多少行或复制大量行时,可以使用标签来替代
光标移到起始行,输入ma
光标移到结束行,输入mb
光标移到粘贴行,输入mc
然后 :'a,'b co 'c 把 co 改成 m 就成剪切了

要删除多行的话,可以用 :5, 9 de
1
2
3
4
5.快速删除
到想要删除的那一行内容上面,连按dd,就可以删除当前行的内容。

还有一个大招,如果想要清空当前行下的所有内容,先连按999,然后再连按dd,这样就可以清空光标所在行下的所有内容了。

wc

1
2
wc -w  统计单词数 默认空格为分隔符
wc -l 行数

uniq

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
uniq:检查重复的行列
什么参数都不带的
[root@localhost ~]# cat hello.txt
hello world!
hello world!
hello world!
hello world!
hello world!
hello world!

[root@localhost ~]# uniq hello.txt 出现一次也是重复一次
hello world!

-c参数,这个参数表示在输出行的前面加上数据在文件中重复出现的次数
[root@localhost ~]# uniq -c hello.txt
6 hello world!

-u参数,表示返回文件中不重复的行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@bigdata02 myshells]# vi /data/uniqdata.txt
i love you
i love you
he love her
i love you
she love him

[root@bigdata02 myshells]# uniq data/uniqdata.txt
i love you
he love her
i love you
she love him

[root@bigdata02 myshells]# sort data/uniqdata.txt | uniq
he love her
i love you
she love him

ps

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
显示进程信息
一个典型应用就是在后面跟e和f参数,显示系统内的所有进程

grep命令以后,就可以把这两个命令组合到一块使用
ps -ef | grep java
这是一个比较常用的操作,过滤出系统内的java进程信息

[root@localhost ~]# ps -ef | grep java
root 2239 1973 0 22:19 pts/0 00:00:00 grep --color=auto java
注意:这里面返回的grep --color=auto这一行信息表示是grep这个命令本身

[root@localhost ~]# ps -ef | grep python
root 736 1 0 20:46 ? 00:00:00 /usr/bin/python2 -Es /usr/sbin/firewalld --nofork --nopid
root 1070 1 0 20:46 ? 00:00:00 /usr/bin/python2 -Es /usr/sbin/tuned -l -P
root 2241 1973 0 22:19 pts/0 00:00:00 grep --color=auto python
1
2
3
4
显示出这个信息其实说明没有找到java进程信息,下面返回的这一行表示是grep进程本身,这样容易给我们造成错觉,想把它去掉,怎么去掉呢?

很简单,使用grep加上-v参数再做一次过滤即可,表示忽略包含指定字符串的数据。
[root@localhost ~]# ps -ef | grep java | grep -v grep

netstat

1
2
3
4
5
6
7
netstat也是显示进程相关信息的,只不过可以比ps命令额外显示端口相关的信息

但是这个命令默认是没有安装的,最方便的方式就是使用yum来在线安装
yum install -y net-tools

netstat的常见用法是
netstat -anp
1
2
3
4
5
6
7
8
这里会显示很多的进程和端口信息,netstat也需要和grep命令结合使用
假设我们想看一下ssh服务的端口使用情况,ssh服务的端口是22,如果ssh服务开启了,那么22这个端口就会被监听。
netstat -anp | grep 22

[root@localhost ~]# netstat -anp | grep 22
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1071/sshd
tcp 0 52 192.168.182.132:22 192.168.182.1:51472 ESTABLISHED 1969/sshd: root@pts
tcp6 0 0 :::22 :::* LISTEN 1071/sshd

jps

1
2
3
4
5
6
7
jps:类似ps命令,不同的是ps是用来显示所有进程信息的,而jps只显示Java进程
准确的说jps是显示当前用户已启动的Java进程信息,信息包括进程号和简短的进程command

注意:jps是Java提供的命令,只有在安装配置好Java环境之后才能使用
jps -m 可以显示得更全面些

pe -ef | grep xxx

top

1
2
3
4
5
6
主要作用在于动态显示系统消耗资源最多的进程信息,包含进程ID、内存占用、CPU占用等
和ps命令作用基本相同,唯一的区别是top命令能够动态显示进程信息

注意:这里的CPU使用情况是总体CPU的使用情况,如果是多核CPU,想查看具体每个CPU的使用情况的话可以在监控模式中按键盘上的1,就可以查看每一个CPU的情况了

按q可以退出此监控模式

free

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
查看内存使用情况
free 查看内存和交换空间的使用情况
常见用法:
free -m:显示内存单位为MB
free -h:根据值的大小,显示易于识别的单位


[root@bigdata01 /]# free -h
total used free shared buff/cache available
Mem: 1.8G 185M 1.3G 17M 306M 1.4G
Swap: 2.0G 0B 2.0G
[root@bigdata01 /]# free -m
total used free shared buff/cache available
Mem: 1819 185 1326 17 306 1459
Swap: 2047 0 2047

kill

1
2
3
可以使用前面学习的ps命令,找到程序对应的PID,然后使用kill命令杀掉这个进程,进程被杀掉了,对应的程序也就停止了

如果遇到有一些进程使用kill命令杀不掉,那就可以使用kill -9 PID ,这样可以实现强制杀进程。

压缩 解压

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
常见用法:压缩和解压
参数:
-z 是否同时具有 gzip 的属性?亦即是否需要用 gzip 压缩?
-c 创建一个压缩文件的参数指令(create 的意思);
-x 解开一个压缩文件的参数指令!
-v 压缩的过程中显示文件!
-f 使用档案名字,这个参数是最后一个参数,后面只能接档案名!
注意:特别注意,在参数的下达中, c/x 仅能存在一个!不可同时存在!
~~~

**压缩**

tar -zcvf 打包及压缩(gzip方式)
将abc目录的内容打包压缩为abc.tar.gz
[root@localhost ~]# ll
drwxrwxrwx. 3 root root 17 Mar 29 14:24 abc
[root@localhost ~]# tar -zcvf abc.tar.gz abc
[root@localhost ~]# ll
drwxrwxrwx. 3 root root 17 Mar 29 14:24 abc
-rw-r--r--. 1 root root 130 Mar 29 15:24 abc.tar.gz



**解压**
tar -zxvf 解压(gzip包)
tar -zxvf abc.tar.gz

which | whereis | find | locate

which

1
2
3
4
5
6
7
which命令的作用是,在PATH变量指定的路径中,搜索某个系统命令的位置,并且返回第一个搜索结果。也就是说,使用which命令,就可以看到某个系统命令是否存在,以及执行的到底是哪一个位置的命令。

[root@bigdata01 /]# which hdfs
/data/soft/hadoop-3.2.0/bin/hdfs
You have new mail in /var/spool/mail/root
[root@bigdata01 /]# which hadoop
/data/soft/hadoop-3.2.0/bin/hadoop

whereis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
whereis命令用于搜索程序的二进制文件,源代码文件或帮助文档。例如:

[root@bigdata01 /]# whereis grep
grep: /usr/bin/grep /usr/share/man/man1/grep.1.gz


whereis ls #如果上述三者有,则三者都会显示。
ls: /bin/ls /usr/share/man/man1/ls.1.gz

whereis -m ls #只查看ls的帮助手册
ls: /usr/share/man/man1/ls.1.gz

whereis -b ls #只查找ls的二进制文件
ls: /bin/ls

whereis stdio.h #查找stdio.h头文件,和帮助手册
stdio: /usr/include/stdio.h /usr/share/man/man3/stdio.3.gz

find

以名称为条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 find / -name guava*

find是最常见和最强大的查找命令,你可以用它找到任何你想找的文件。
find的使用格式如下:
  $ find <指定目录> <指定条件> <指定动作>
  - <指定目录>: 所要搜索的目录及其所有子目录。默认为当前目录。
  - <指定条件>: 所要搜索的文件的特征。
  - <指定动作>: 对搜索结果进行特定的处理。
  

find的使用实例:
  $ find . -name "my*"
搜索当前目录(含子目录,以下同)中,所有文件名以my开头的文件。
  $ find . -name "my*" -ls
搜索当前目录中,所有文件名以my开头的文件,并显示它们的详细信息。
  $ find . -type f -mmin -10
搜索当前目录中,所有过去10分钟中更新过的普通文件。

以权限为条件

1
2
3
4
5
有时候需要查找特定权限的文件,可以使用-perm参数,例如查找当前目录下权限为777的文件:

find ./ -perm 777
./test
./sort.txt

以文件类型为条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
涉及参数-type,例如要查找当前目录下的符号链接文件:

find ./ -type l
./test
ls -al test
lrwxrwxrwx 1 hyb hyb 8 11月 24 10:10 test -> home.zip
主要类型有:

f 普通文件
d 目录
b 块设备文件
c 字符设备文件
l 符号链接
s 套接字
p 管道文件

以文件大小为条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
涉及参数-size,例如:

find ./ -size 1k #查找当前目录下小于1k的文件
./test
./sort4.txt
./sort2.txt
./sort3.txt
./test.sh
./sort.txt
find -size +1M #查找当前目录下大于1M的文件
./test.zip
常用单位有:

k 千字节
M 兆字节
G 吉字节
c 字节
b 块,一般为512字节
w 字大小,两个字节

以归属为条件

1
2
3
4
5
6
涉及参数-user,-nouser,-group,-nogroup等,例如:

find ./ -user root #查找当前目录下root用户的文件
find ./ -nouser #查找当前目录下root用户的被删除的文件

-group,-nogroup类似的用法,只不过条件是用户组。

以时间为条件

1
2
3
4
5
6
7
8
9
10
11
12
13
涉及参数-mtime,-atime,-ctime,-newer,-anewer,-cnewer,-amin,-cmin等,例如:

find ./ -mtime 3 #查找3天前更改过的文件
find ./ -mtime -3 #查找3天内更改过的文件
find ./ -mtime 0 #查找今天更改过的文件
find ./ -newer sort.txt #查找比sort.txt修改时间更新的文件
find ./ -anewer sort.txt #查找比sort.txt访问时间更新的文件
find ./ -amin 5 #查找5分钟之前访问过的文件
注:

atime 最后访问时间
mtime 最后修改时间
ctime 最后修改时间,这里包括属性和权限

Shell 文件包含

1
可以将一些通用的功能放在单独的脚本文件中,然后在需要的地方包含它们,以提高代码的模块化和可重用性。
1
2
3
4
#!/bin/bash

# 这是脚本1.sh的内容
echo "这是脚本1.sh"
1
2
3
4
#!/bin/bash

# 这是脚本2.sh的内容
echo "这是脚本2.sh"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
现在,你可以创建一个主脚本,将这两个文件包含进来。 主脚本.sh:

#!/bin/bash

# 包含脚本1.sh
source 脚本1.sh

# 或者使用 . 操作符
# . 脚本1.sh

# 包含脚本2.sh
source 脚本2.sh

# 主脚本的内容
echo "这是主脚本"

# 运行脚本1.sh和脚本2.sh中的命令

locate

1
locate命令其实是“find -name”的另一种写法,但是要比后者快得多,原因在于它不搜索具体目录,而是搜索一个数据库(/var/lib/locatedb),这个数据库中含有本地所有文件信息。Linux系统自动创建这个数据库,并且每天自动更新一次,所以使用locate命令查不到最新变动过的文件。为了避免这种情况,可以在使用locate之前,先使用updatedb命令,手动更新数据库。
1
2
3
4
5
6
7
locate命令的使用实例:
  $ locate /etc/sh
搜索etc目录下所有以sh开头的文件。
  $ locate ~/m
搜索用户主目录下,所有以m开头的文件。
  $ locate -i ~/m
搜索用户主目录下,所有以m开头的文件,并且忽略大小写。

文件系统

1
2
3
4
5
6
7
window NTFS
小容量u盘 FAT32
linux EXT2 EXT3 EXT4 XFS
EXT2 第二扩展文件系统
EXT3 第三扩展文件系统 引入日志
EXT4 第四扩展文件系统
XFS 新版linux默认文件系统类型

shell脚本系统学习

shell概述 了解

1
2
3
大数据程序员为什么要学习She11呢?。
1)需要看懂运维人员编写的 She11 程序。
2)偶尔会编写一些简单 she11程序来管理集群、提高开发效率。~

image-20241015100054448

1
2
3
4
5
Shell是一个命令行解释器,它接收应用程序/用户命令,然后调用操作系统内核。

Shel1还是一个功能相当强大的编程语言,易编写、易调试.炅活性强。

python是它的一个强劲对手,python比它可读性好,发布时间晚,结合优点去除缺点。

shell解释器 了解

image-20241016015635749

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
查看shell支持哪些解释器
>>cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
常用的是上面sh bash两种

查看默认使用的哪种解释器
>>echo $SHELL
/bin/bash

查看bash和shell解释器的关系
ll /bin | grep bash
sh->bash
sh解释器最终调用的还是bash

image-20241015101138211

image-20241015101246908

image-20241015101623428

shell脚本入门

hello world

1
2
3
4
5
6
7
8
9
10
11
touch ~/myshells/helloworld.sh
cd ~/myshells

vi helloworld.sh
#!/bin/bash
echo "hello world"

>>sh helloworld.sh
hello world
>>bash helloworld.sh
hello world

image-20241015103034054

1
2
3
4
5
-rw-r--r--. 1 root root 31 Oct 15 10:30 helloworld.sh

>>./helloworld.sh

-bash: ./helloworld.sh: Permission denied

image-20241015103147997

1
2
3
>>chmod 777 helloworld.sh
>>./helloworld.sh
hello world

image-20241015103412999

1
注意:第一种执行方法,本质是bash解析器帮你执行脚本,所以脚本本身不需要执行权限。第二种执行方法,本质是脚本需要自己执行,所以需要执行权限

多命令处理

1
在~/myshells/data日录下创建一个data.txt,在data.txt文件中增加“hello world”
1
2
3
#!/bin/bash
mkdir ~/myshells/data
echo "hello world" >> ~/myshells/data/data.txt

shell中的变量

系统变量

1
$PWD $SHELL $HOME $USER

自定义变量

定义变量
1
2
3
4
变量=值 等号两边不能有空格

>>A=2
>>echo $A
撤销变量

image-20241015110711835

1
2
>>unset A
>>echo $A
声明静态变量
1
2
3
readonly B=5 不能撤销
echo $B
unset B

image-20241015110947743

变量名定义规则
1
2
3
4
(1)变量名称可以由字母、数字和下划线组成,但是不能以数字开头,环境变量名建议大写。
(2)等号两侧不能有空格
(3)在bash中,变量默认类型都是字符串类型,无法直接进行数值运算。
(4)变量的值如果有空格,需要使用双引号或单引号括起来。

image-20241015111327284

1
2
3
4
5
>>echo 1+1
1+1
>>C=1+1
>>echo $C
1+1

image-20241015111457155

1
2
3
4
5
6
7
8
9
>>echo I love her
I love her
>>echo "I love her"
I love her
>>E=I love her
报错
>>E="I love her"
>>echo $E
I love her

image-20241015111836814

局部变量提升为全局变量
1
2
3
可把变量提升为全局环境变量,可供其他She11程序使用

如java_home hadoop_home
1
2
3
4
5
6
7
8
9
>>E="I love her"
>>vi globalvariabls.sh
#!/bin/bash
echo $E
>>sh globalvariabls.sh

>>export E
>>sh globalvariabls.sh
I love her

特殊变量

$n
1
2
1-9个参数用$n
>10个参数用${n}
1
2
3
4
5
6
vi parameters.sh
#!/bin/bash
echo "$0 $1 $2 $3"

>> sh parameters.sh i love her
parameters.sh i love her

image-20241015140218869

$#
1
S#	(功能描述:获取所有输入参数个数,常用于循环)
1
2
3
4
5
6
7
8
>>vi parameters.sh
#!/bin/bash
echo "$0 $1 $2 $3"
echo $#

>> sh parameters.sh i love her
parameters.sh i love her
3
$*和$@
1
2
$* (功能描述:这个变量代表命令行中所有的参数,$*把所有的参数看成一个整体)
$# (功能描述:这个变量也代表命令行中所有的参数,不过S@把每个参数区分对待)。
1
2
3
4
5
6
7
8
9
10
11
12
>>vi parameters.sh
#!/bin/bash
echo "$0 $1 $2 $3"
echo $#
echo $*
echo $@

>>sh parameters.sh
parameters.sh i love her
3
i love her
i love her
$?
1
$? (功能描述:最后一次执行的命令的返回状态。如果这个变量的值为0,证明上一个命令正确执行;如果这个变量的值为非0(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确了。)“

image-20241015141716716

$!
1
最近的后台命令PID
$??
1
最近的前台退出状态。
$$
1
2
如何打印当前shell的PID?
echo $$

运算符

1
2
3
4
(1)“$((运算式))”或“$[运算式]”
(2)expr +,-,\*,/,% 加,减,乘,除,取余。

注意:expr 运算符间要有空格

1
2
3
4
5
6
7
8
>>expr 2+3
2+3
>>expr 2 +3
expr: syntax error
>>expr 2+ 3
expr: syntax error
>>expr 2 + 3
5

1
2
3
>>expr `expr 2 + 3` \* 5

``表示执行里面的东西
1
2
>>echo $[(2+3)*5]
25

条件判断

基本语法

1
2
3
[ condition ](注意 condition 前后要有空格)

注意:条件非空即为true,[atguigu]返回 true,[]返回 false。

常用判断条件

两个整数比较
1
比较运算符左右也要有空格
1
2
3
4
5
6
= 字符串比较    
-lt 小于(less than)
-le 小于等于(less equal)
-gt 大于(greater than)
-ge 大于等于(greater equal)
-ne 不等于(not equal)
1
2
3
4
5
6
>>[ 23>22 ]  比较运算符左右也要有空格
>>echo $?
1
>>[ 23 > 22 ]
>>echo $?
0
1
2
>>[ 23 -lt 22 ]
1
1
2
3
4
5
6
7
8
9
10
#!/bin/bash
read -p "请输入一个数字: " num

if [ $num -eq 0 ]; then
echo "输入的数字是零"
elif [ $num -gt 0 ]; then
echo "输入的数字是正数"
else
echo "输入的数字是负数"
fi
按照文件权限进行判断
1
2
3
-r 有读的权限(read)
-w 有写的权限(write)
-x 有执行的权限(execute)
1
2
3
>>[ -w helloworld.sh ]
>>$?
0
按照文件类型进行判断
1
2
3
4
-f 文件存在并且是一个常规的文件(fle)
-e 文件存在(existence)
-d 文件存在并是一个目录(directory)。
-s 文件:检查文件是否不为空(即文件大小是否大于零)
1
2
3
4
5
6
如何检查文件系统中是否存在某个文件 ?

if [ -f /var/log/messages ]
then
b "File exists"
fi
1
2
3
>>[ -e ~/myshells/helloworld.sh ]
>>echo $?
0
判断字符串是否为空
1
-n 字符串:检查字符串是否非空。 -z 字符串:检查字符串是否为空。
1
2
3
if [ -n "Alice" ]; then
echo "姓名是Alice"
fi

多条件判断

1
2
&& 表示前一条命令执行成功时,才执行后一条命令
|| 表示上一条命令执行失败后,才执行下一条命令
1
2
>>[ xxxx ] && echo ok || echo "not ok"
>>ok
1
2
>>[ xxxx ] $$ [] || echo "not ok"
>>not ok

流程控制 重点

if判断

1
2
3
4
5
6
7
if [ 条件判断表达式 ];then
xxx
elif [ 其他条件 ];then
xxx
else
xxx
fi
1
2
3
注意事项:
(1)[ 条件判断式 ],括号和条件判断式之间必须有空格
(2)if后要有空格
1
2
3
4
5
6
7
8
9
10
11
12
13
>>touch if.sh
>>vim if.sh
#!/bin/bash
if [ $1 -eq 1 ]
then
echo "I love her"
elif [ $1 -eq 2 ]
then
echo "she love me"
else
then
xxx
fi

case语句

1
2
3
4
5
6
7
8
9
10
11
12
case $变量名 in
"值1")
xxxx
;; --相当于java中的break
"值2")
xxxx
;;
...
*) --相当于java中的default
xxxx
;;
esac
1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>touch case.sh
>>vim case.sh
#!/bin/bash
case $1 in
1)
echo "i love her"
;;
2)
echo "she love me"
;;
*)
echo "don't love"
;;
esac

for循环

for
1
2
3
4
for((初始值;循环控制条件;变量变化))
do
程序
done
1
2
3
4
for 变量 in 值1 值2 值3...
do
程序
done
1
2
3
4
#!/bin/bash
for fruit in apple banana cherry; do
echo "水果: $fruit"
done
1
2
3
4
5
6
7
8
9
>>touch for.sh
>>vim for.sh
#!/bin/bash
s=0
for((i=0;i<=100;i++))
do
s=$[$s+$i]
done
echo $s
1
2
3
4
for i in {1..254}
do

done
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for i in $(seq -w 1 30)
do

done

使用 seq -w 1 30 生成从 01 到 30 的数字序列。-w 选项确保序列中的数字被格式化为两位数,即 01, 02, …, 30。

01
02
03
04
05
06
07
08
...
1
2
3
for i in {0..100..3}; do echo $i; done
or
for (( i=0; i<=100; i=i+3 )); do echo "Welcome $i times"; done
1
2
for i in /etc/profile.d/*.sh /etc/profile.d/sh.local ; do
if [ -r "$i" ]; then
for in $* $@
1
for in和$*和$@一起用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>vim forin.sh
#!/bin/bash
for i in $*
do
echo "i love $i"
done

for i in $@
do
echo "i love $i"
done

echo "-------"

for i in "$*"
do
echo "i love $i"
done

for i in "$@"
do
echo "i love $i"
done
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@bigdata02 myshells]# sh forin.sh 1
i love 1
i love 1
-------
i love 1
i love 1
[root@bigdata02 myshells]# sh forin.sh 1 2 3
i love 1
i love 2
i love 3
i love 1
i love 2
i love 3
-------
i love 1 2 3
i love 1
i love 2
i love 3
1
2
3
4
5
for i in $(ls); do ... done 循环来遍历当前目录下的文件和目录是一个常见的做法

for i in $(ls);do
echo item:$i
done

while循环

1
2
3
4
while [ 条件表达式 ]
do
程序
done
1
2
3
4
5
6
7
8
9
10
>>vi while.sh
#!/bin/bash
s=0
i=1
while [ $i -le 100 ]
do
s=$[$s + $i]
i=$[$i + 1]
done
echo $s
1
2
3
4
5
6
7
8
在 Bash 中,当你使用 let 命令来执行算术运算时,你不需要在变量名前加 $ 符号来引用变量的值

#!/bin/bash
COUNTER=0
while [ $COUNTER -lt 10 ]; do
echo The counter is $COUNTER
let COUNTER=COUNTER+1
done
1
2
3
4
5
6
7
8
#!/bin/bash  

COUNTER=0

while [ $COUNTER -lt 10 ]; do
echo "The counter is $COUNTER"
COUNTER=$((COUNTER + 1))
done

until循环

1
2
3
4
5
6
#!/bin/bash
COUNTER=20
until [ $COUNTER -lt 10 ]; do
echo COUNTER $COUNTER
let COUNTER-=1
done

函数 了解

1
shell开发工程师需要重点掌握

系统函数

basename基本语法
1
2
3
4
basename [string/pathname][suffix]
(功能描述:basename命令会删掉所有的前缀包括最后一个('/')字符,然后将字符串显示出来。
选项:
suffix为后缀,如果suffix被指定了,basename会将pathname和suffix中的suffix去掉。
1
2
3
4
5
6
7
8
9
10
11
[root@bigdata02 myshells]# basename /root/myshells/read.sh
read.sh
[root@bigdata02 myshells]# basename /root/myshells/for.sh
for.sh
[root@bigdata02 myshells]# basename /root/myshells/for.sh .sh
for
[root@bigdata02 myshells]# basename /root/myshells/read.sh .sh
read

[root@bigdata02 myshells]# basename `pwd`/for.sh
for.sh
dirname基本语法
1
2
3
4
dirname 文件绝对路径

(功能描述:从给定的包含绝对路径的文件名中去除文件名
(非目录的部分),多然后返回剩下的路径(目录的部分))”
1
2
3
4
[root@bigdata02 myshells]# dirname /root/myshells/for.sh
/root/myshells
[root@bigdata02 myshells]# dirname `pwd`/while.sh
/root/myshells

自定义函数

1
2
3
4
5
6
7
[function] funname[()]
{
Action;
[return int;]

}
funname
1
2
3
4
5
6
7
8
9
#!/bin/bash

# 定义一个简单的函数
my_function() {
echo "这是一个自定义的Shell函数"
}

# 调用函数
my_function
1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

# 定义一个带参数的函数
greet() {
local name="$1"
echo "Hello, $name!"
}

# 调用函数,并传递参数
greet "Alice"
greet "Bob"
1
2
3
(1)必须在调用函数地方之前,
先声明函数,shell 脚本是逐行运行。不会像其它语言样先编译。
2)函数返回值,只能通过$?系统变量获得,可以显示加:retun返回,如果不加,将以最后一条命令运行结果,作为返回值。retuin后跟数值 n(0-255)。
1
2
3
4
5
6
7
8
9
10
11
12
>>vi function.sh
#!/bin/bash
function sum()
{
s=0
s=$[$1 + $2]
echo $s
}
read -p "输入参数1的值:" para1
read -p "输入参数2的值:" para2

sum para1 para2
1
2
3
4
[root@bigdata02 myshells]# sh function.sh 
输入参数1的值:8
输入参数2的值:10
18

shell工具 重点

cut

1
cut 的工作就是“剪”,具体的说就是在文件中负责剪切数据用的。cut 命令从文件的每行剪切字节、字符和字段并将这些字节、字符和字段输出。。
1
2
3
4
5
cut [选项参数] filename 
说明:默认分隔符是制表符

-f (分割后取第几列,或者几列)
-d 分隔符(只能是单个字符)
1
2
3
4
5
>>vi cutdata.txt
he love her
wangming love xiaohong
liming love xiaofang
i love you
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@bigdata02 myshells]# cut -d " " -f 1 ./data/cutdata.txt 
he
wangming
liming
i

[root@bigdata02 myshells]# cut -d " " -f 2 ./data/cutdata.txt
love


love

[root@bigdata02 myshells]# cut -d " " -f 3 ./data/cutdata.txt
her
love
love
you

[root@bigdata02 myshells]# cut -d " " -f 2,3 ./data/cutdata.txt
love her
love
love
love you
cut结合| grep
1
2
3
4
>>cat ./data/cutdata.txt | grep wangming
wangming love xiaohong
>>cat ./data/cutdata.txt | grep wangming | cut -d " " -f 1
wangming
1
2
3
4
5
6
7
8
切path

>>echo $PATH | cut -d : -f 3

[root@bigdata02 myshells]# echo $PATH
.:/data/soft/jdk1.8/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/data/soft/elasticsearch-8.11.4/bin:/data/soft/hadoop-3.2.0/bin:/root/bin
[root@bigdata02 myshells]# echo $PATH | cut -d : -f 3-
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/data/soft/elasticsearch-8.11.4/bin:/data/soft/hadoop-3.2.0/bin:/root/bin
1
2
3
切出ip  这个也是看的我有点懵逼 首先切割符必须为单个字符,后面空格不规范时我就有点蒙蔽了

还有ifconfig 返回的格式不一定一致
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
[root@bigdata01 sbin]# ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.40.128 netmask 255.255.255.0 broadcast 192.168.40.255
inet6 fe80::3e1c:34f6:4005:5c0a prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:fd:a8:cc txqueuelen 1000 (Ethernet)
RX packets 25096 bytes 30857038 (29.4 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 5698 bytes 472199 (461.1 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 256 bytes 14336 (14.0 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 256 bytes 14336 (14.0 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

[root@bigdata01 sbin]# ipconfig ens33
-bash: ipconfig: command not found
You have new mail in /var/spool/mail/root
[root@bigdata01 sbin]# ipconfig eth0
-bash: ipconfig: command not found
You have new mail in /var/spool/mail/root
[root@bigdata01 sbin]# ifconfig eth0
eth0: error fetching interface information: Device not found
[root@bigdata01 sbin]# ifconfig ens33
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.40.128 netmask 255.255.255.0 broadcast 192.168.40.255
inet6 fe80::3e1c:34f6:4005:5c0a prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:fd:a8:cc txqueuelen 1000 (Ethernet)
RX packets 25279 bytes 30872434 (29.4 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 5806 bytes 483871 (472.5 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

[root@bigdata01 sbin]# ifconfig ens33 | grep "inet "
inet 192.168.40.128 netmask 255.255.255.0 broadcast 192.168.40.255

[root@bigdata01 sbin]# ifconfig ens33 | grep "inet " | cut -d " " -f 10
192.168.40.128

image-20241016112236755

image-20241016112255988

image-20241016112324895

1
它这里使用的空格分割,避免了麻烦事
如何列出以 ab 或 xy 开头的用户名?
1
grep "^ab|^xy" /etc/passwd|cut -d: -f1

sed

1
sed 是一种流编辑器它一次处理一行内容处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间接着用 sed 命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。文件内容并没有改变,除接着处理下一行,这样不断重复,直到文件末尾。除非你使用重定向存储输出。”
1
sed [选项参数] 'command' filename
1
2
-e
直接在指令列模式上进行sed的动作编辑。
1
2
3
4
a 新增,a的后而可以接字串,在下一行出现($表示最后一行)
i 新增 增加的内容在行前($表示最后一行)
d 删除。
s 查找并替换
增加
1
原文件没改变
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[root@bigdata02 myshells]# vi /data/seddata.txt
dong shen
guan zhen
wo wo
lai lai

le le

>>vi sed.sh
#!/bin/bash
sed '2a mei nv' data/seddata.txt

[root@bigdata02 myshells]# sh sed.sh
dong shen
guan zhen
mei nv
wo wo
lai lai

le le

[root@bigdata02 myshells]# cat data/seddata.txt
dong shen
guan zhen
wo wo
lai lai

le le
1
2
3
4
[root@localhost ~]# sed '1a\haha' hello.txt 此操作会将数据添加到第一行下面(也就是第二行的位置)
[root@localhost ~]# sed '0a\haha' hello.txt 此操作会报错,行号是从1开始的

[root@localhost ~]# sed '$i\haha' hello.txt
删除
1
2
3
4
5
6
7
8
>>sed '/wo/d' data/seddata.txt

[root@bigdata02 myshells]# sed '/wo/d' ./data/seddata.txt
dong shen
guan zhen
lai lai

le le
1
2
[root@localhost ~]# sed '7d' hello.txt  
[root@localhost ~]# sed '$d' hello.txt $参数,删除最后一行,更加便捷
替换
1
2
3
sed [address]s/pattern/replacement/flags

g代表全局,不写只替换第一个
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>sed 's/wo/ni/g' data/seddata.txt

[root@bigdata02 myshells]# sed 's/wo/ni/g' data/seddata.txt
dong shen
guan zhen
ni ni
lai lai

le le
You have new mail in /var/spool/mail/root
[root@bigdata02 myshells]# cat data/seddata.txt
dong shen
guan zhen
wo wo
lai lai

le le
1
2
3
4
5
6
sed 's/l/a/1' hello.txt  一行中匹配的第一次替换
sed 's/l/a/2' hello.txt 一行中匹配的第2次替换
sed 's/l/a/3' hello.txt 一行中匹配的第3次替换
sed 's/l/a/g' hello.txt 全局替换
sed 's/l/a/' hello.txt 一行中匹配的第一次替换
上述是每一行只要满足要求,都会替换
1
2
我们现在的替换操作都是会匹配文件中的所有行,如果我们只想替换指定行中的内容怎么办呢?只需要增加address 参数即可。
sed '2s/l/a/g' hello.txt 对第二行进行这种操作
-i 修改源文件
1
2
注意了,咱们前面所讲的sed命令的所有操作,在执行之后都不会修改源文件中的内容,这样只能作为测试,如果需要修改源文件的话,其实也很简单,只需要增加一个 -i 参数即可
[root@localhost ~]# sed -i '2s/l/a/g' hello.txt
多语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>sed -e "2d" -e "s/wo/ni/g" data/seddata.txt

[root@bigdata02 myshells]# sed -e "2d" -e "s/wo/ni/g" data/seddata.txt
dong shen
ni ni
lai lai

le le
You have new mail in /var/spool/mail/root
[root@bigdata02 myshells]# cat data/seddata.txt
dong shen
guan zhen
wo wo
lai lai

le le

awk

1
一个强大的文本分析工具,把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行分析处理。
1
2
3
4
5
awk「选项参数] ‘patternl {actionl} pattern2 {action2}...’ filename.

pattern:表示AWK在数据中查找的内容,就是匹配模式
action:在找到匹配内容时所执行的一系列命令
注意:只有匹配了 pattemm 的行才会执行 actione。没有表达式时,表示都执行action
1
2
-F 指定输入文件折分隔符(分隔符与F有无空格都可以)
-v 赋值一个用户定义变量。
1
取以root开头那一行,用:分隔 取第七列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[root@bigdata02 myshells]# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
es:x:1000:1000::/home/es:/bin/bash

>>cp /etc/passwd ~/myshells/data
>>awk -F : '/^root/ {print $7}' ~/myshells/data/passwd

[root@bigdata02 myshells]# awk -F : '/^root/ {print $7}' ~/myshells/data/passwd
/bin/bash
1
2
3
4
5
6
2)搜索 passwd 文件以 root 关键字开头的所有行,并输出该行的第1列和第7列,中间以,号分割。

>>awk -F : '/^root/ {print $1","$7}' ~/myshells/data/passwd

[root@bigdata02 myshells]# awk -F : '/^root/ {print $1","$7}' ~/myshells/data/passwd
root,/bin/bash
1
2
3
4
5
6
7
8
9
10
还有一个特殊的 $0 它代表整个文本行的内容

[root@localhost ~]# awk '{print $0}' hello.txt
hello world!
heaao worad!
hello world!
hello world!
hello world!
hello world!
abc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
如果我们只想对某一列数据进行匹配呢?
awk '($1 ~ /world/) {print $1}' hello.txt 在这里面可以通过$来指定具体是哪一列,需要把具体的对比逻辑放到小括号里面

[root@localhost ~]# awk '($1 ~ /world/) {print $0}' hello.txt
[root@localhost ~]# awk '($2 ~ /world/) {print $0}' hello.txt
hello world!
hello world!
hello world!
hello world!
hello world!

[root@localhost ~]# awk '($2 ~ /wor[a-z]d/) {print $0}' hello.txt
hello world!
heaao worad!
hello world!
hello world!
hello world!
hello world!

注意:这里如果是不匹配的意思的话需要使用 !~
获取一个文件每一行的第三个元素 ?
1
2
3
4
5
6
7
awk的基本格式:awk [option] programe file

这里的option是一个可选项,一般在这里来指定文件中数据的字段分隔符
programe 是具体的处理逻辑
file表示我们要操作的文件

awk -F , '{PRINT $3}'
1
https://tianyong.fun/linux%E7%9B%B8%E5%85%B3%E5%91%BD%E4%BB%A4%E5%92%8C%E5%BC%82%E5%B8%B8.html
假如文件中某行第一个元素是 FIND,如何获取第二个元素
1
2
3
awk'{ if ($1 == "FIND") print $2}'

awk '^/FIND/ {PRINT $2}'
如何使用 awk 列出 UID 小于 100 的用户 ?
1
awk -F: '$3<100' < /etc/passwd
BEGIN END
1
注意:BEGIN 在所有数据读取行之前执行;END在所有数据执行之后执行,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
[root@bigdata02 myshells]# awk -F : '{print $1","$7}' data/passwd | sed -e '1i user,shell' -e '$a tianyong,/bin/zhenbang'
user,shell
root,/bin/bash
bin,/sbin/nologin
daemon,/sbin/nologin
adm,/sbin/nologin
lp,/sbin/nologin
sync,/bin/sync
shutdown,/sbin/shutdown
halt,/sbin/halt
mail,/sbin/nologin
operator,/sbin/nologin
games,/sbin/nologin
ftp,/sbin/nologin
nobody,/sbin/nologin
systemd-network,/sbin/nologin
dbus,/sbin/nologin
polkitd,/sbin/nologin
sshd,/sbin/nologin
postfix,/sbin/nologin
ntp,/sbin/nologin
es,/bin/bash
tianyong,/bin/zhenbang

[root@bigdata02 myshells]# awk -F : '{print $1","$7}' data/passwd | sed '1i user,shell' | sed '$a tianyong,/bin/zhenbang'
user,shell
root,/bin/bash
bin,/sbin/nologin
daemon,/sbin/nologin
adm,/sbin/nologin
lp,/sbin/nologin
sync,/bin/sync
shutdown,/sbin/shutdown
halt,/sbin/halt
mail,/sbin/nologin
operator,/sbin/nologin
games,/sbin/nologin
ftp,/sbin/nologin
nobody,/sbin/nologin
systemd-network,/sbin/nologin
dbus,/sbin/nologin
polkitd,/sbin/nologin
sshd,/sbin/nologin
postfix,/sbin/nologin
ntp,/sbin/nologin
es,/bin/bash
tianyong,/bin/zhenbang
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
3)只显示/etc/passwd 的第一列和第七列,以逗号分割,且在所有行前面添加列名user,
shell在最后一行添加"dahaige,/bin/zuishuai"。

[root@bigdata02 myshells]# awk -F: 'BEGIN{print "user,shell"} {print $1","$7} END{print "tianyong,/bin/zhenbang"}' data/passwd
user,shell
root,/bin/bash
bin,/sbin/nologin
daemon,/sbin/nologin
adm,/sbin/nologin
lp,/sbin/nologin
sync,/bin/sync
shutdown,/sbin/shutdown
halt,/sbin/halt
mail,/sbin/nologin
operator,/sbin/nologin
games,/sbin/nologin
ftp,/sbin/nologin
nobody,/sbin/nologin
systemd-network,/sbin/nologin
dbus,/sbin/nologin
polkitd,/sbin/nologin
sshd,/sbin/nologin
postfix,/sbin/nologin
ntp,/sbin/nologin
es,/bin/bash
tianyong,/bin/zhenbang
-v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
将passwd 文件中的用户id增加数值1并输出

>>awk -F: -v i=1 '{print $3+i}' data/passwd

[root@bigdata02 myshells]# awk -F: -v i=1 '{print $3+i}' data/passwd
1
2
3
4
5
6
7
8
9
12
13
15
100
193
82
1000
75
90
39
1001
1
这里换成{print $3+$i} {print $3+1} {print "$3+1"} {print $[$3+1]} {print "$[$3+1]"}都不行
awk的内置变量
1
2
3
FILENAME	文件名。
NR 已读的记录数。
NF 浏览记录的域的个数(切割后,列的个数)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
统计 passwd 文件名,每行的行号,每行的列数

>>awk -F: '{print FILENAME","NR","NF}' data/passwd

[root@bigdata02 myshells]# awk -F: '{print FILENAME","NR","NF}' data/passwd
data/passwd,1,7
data/passwd,2,7
data/passwd,3,7
data/passwd,4,7
data/passwd,5,7
data/passwd,6,7
data/passwd,7,7
data/passwd,8,7
data/passwd,9,7
data/passwd,10,7
data/passwd,11,7
data/passwd,12,7
data/passwd,13,7
data/passwd,14,7
data/passwd,15,7
data/passwd,16,7
data/passwd,17,7
data/passwd,18,7
data/passwd,19,7
data/passwd,20,7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
切割ip
[root@bigdata01 sbin]# ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.40.128 netmask 255.255.255.0 broadcast 192.168.40.255
inet6 fe80::8acc:63c2:430d:32a3 prefixlen 64 scopeid 0x20<link>
inet6 fe80::8c3f:dc1c:b22:6454 prefixlen 64 scopeid 0x20<link>
inet6 fe80::3e1c:34f6:4005:5c0a prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:fd:a8:cc txqueuelen 1000 (Ethernet)
RX packets 37569 bytes 31960869 (30.4 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 13889 bytes 1187881 (1.1 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 1278 bytes 71568 (69.8 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1278 bytes 71568 (69.8 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

[root@bigdata01 sbin]# ifconfig ens33 | awk -F " " '/^.+inet / {print $2}'
192.168.40.128
1
2
3
4
5
6
7
8
9
10
查询 sed.txt 中空行所在的行号,
[root@bigdata02 myshells]# cat data/seddata.txt
dong shen
guan zhen
wo wo
lai lai

le le
[root@bigdata02 myshells]# awk '/^$/ {print NR}' data/seddata.txt
5

sort

1
sort命令是在Limux里非常有用,它将文件进行排序,并将排序结果标准输出。
1
2
3
4
5
6
7
8
9
10
sort (选项) (参数)

-n 依照数值的大小排序。
-r 以相反的顺序来排序。
-t 设置排序时所用的分隔字符。
-k 指定需要排序的列。

参数:指定待排序的文件列表

默认根据第一个字符排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>vi sortdata.txt 
xz:50:2.3
bb:40:5.4
ss:30:1.6
bd:20:4.2
cls:10:3.5
>>sort -t: -n -r -k 2 data/sortdata.txt

[root@bigdata02 myshells]# sort -t: -n -r -k 2 data/sortdata.txt
xz:50:2.3
bb:40:5.4
ss:30:1.6
bd:20:4.2
cls:10:3.5

企业真实面试题

1
2
使用 Limux 命令查询 fle1 中空行所在的行号
>>awk '/^$/ {print NR}'
1
2
3
4
5
6
7
8
问题 2:有文件 chengji.txt 内容如下:
张三 40
李四 50
王五 60
使用 Linux 命令计算第二列的和并输出

>>[root@bigdata02 myshells]# cat data/chengji.txt | awk -F " " '{sum+=$2} END{print sum}'
150
1
2
awk 'temp=$8;getline;print temp","$8'
SUCCEEDED,SUCCEEDED
1
2
3
4
5
6
7
8
9
问题1:Shel 脚本里如何检查一个文件是否存在?如果不存在该如何处理?。

if [ -e file.txt ]
then
xx
else
then
xxx
fi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
问题:对文件内数据排序,最后输出总和
>>cat test.txt
2
5
9
1
3
5
4
8
9
7

[root@bigdata02 myshells]# sort -n data/test.txt | awk '{a+=$1;print $1}END{print a}'
1
2
3
4
5
5
7
8
9
9
53

grep的高级使用

1
2
3
4
5
6
7
要在某个路径下递归查找所有包含特定字符串的文件,可以使用 grep 命令结合一些选项进行操作。grep 是一种强大的文本搜索工具,可以在文件中搜索匹配指定模式的字符串。当与递归搜索选项结合使用时,grep 能够在指定目录及其所有子目录中搜索文件内容。

-r:或 --recursive 选项告诉 grep 递归地查找指定目录下的所有文件。
-i:忽略大小写。
-l:仅列出包含匹配项的文件名,而不显示匹配行的具体内容。
-n:显示匹配行及其行号。
-v:表示忽略包含指定字符串的数据
1
2
3
4
5
6
问题1:请用shell 脚本写出查找当前文件夹(ome)下所有的文本文件内容中包含有字符”shen”的文件名称

[root@bigdata02 myshells]# grep -r "shen"
data/seddata.txt:dong shen
[root@bigdata02 myshells]# grep -r "shen" | cut -d ":" -f 1
data/seddata.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
常用于查找文件里符合条件的字符串
[root@localhost ~]# cat hello.txt
hello world!
hello world!
hello world!
hello world!
hello world!
hello world!
abc

[root@localhost ~]# grep abc hello.txt
abc

grep后面跟的这个字符串是可以支持正则表达式的
查询hello.txt中以字母a开头的内容,这样写也是可以的
[root@localhost ~]# grep ^a hello.txt
abc

忽略大小写功能
[root@localhost ~]# grep ABC hello.txt
[root@localhost ~]# grep -i ABC hello.txt
abc

查询出来对应字符串带行号
[root@localhost ~]# grep -i ABC -n hello.txt
7:abc
1
2
3
4
5
6
7
[root@localhost ~]# ps -ef | grep java
root 2497 2247 0 22:34 pts/1 00:00:00 grep --color=auto java

显示出这个信息其实说明没有找到java进程信息,下面返回的这一行表示是grep进程本身,这样容易给我们造成错觉,想把它去掉,怎么去掉呢?

很简单,使用grep加上-v参数再做一次过滤即可,表示忽略包含指定字符串的数据。
[root@localhost ~]# ps -ef | grep java | grep -v grep

Shell 添加一个新组为class1,添加属于这个组的30个用户,用户名的形式为stdxx,其中xx从01 到30 ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
要实现这个需求,你可以编写一个 Shell 脚本来首先创建一个名为 class1 的新组,然后循环添加用户名为 std01 到 std30 的用户,并将这些用户分配到 class1 组。下面是一个简单的脚本示例,演示了如何执行这些操作:

#!/bin/bash

# 创建新组 class1
groupadd class1

# 循环添加用户 std01 到 std30
for i in $(seq -w 1 30); do
user="std$i"
# 添加用户,并将其加入到 class1 组
useradd -m -g class1 "$user"
echo "用户 $user 已创建并添加到组 class1。"
done

echo "所有用户已成功创建并添加到组 class1。"


这个脚本的关键点如下:
使用 groupadd class1 命令创建一个新组 class1。
使用 seq -w 1 30 生成从 01 到 30 的数字序列。-w 选项确保序列中的数字被格式化为两位数,即 01, 02, …, 30。
对于序列中的每个数字,构造用户名 stdXX,其中 XX 是当前的数字。
使用 useradd -m -g class1 "$user" 命令添加用户。-m选项表示创建用户的主目录,-g class1选项表示将用户添加到class1组。
打印一条消息确认用户已被创建并添加到组。

在运行这个脚本之前,请确保你有足够的权限来创建用户和组。在大多数系统上,这意味着你需要以root用户身份执行该脚本,或使用sudo 命令。

请注意,根据你的系统配置和已存在的用户/组设置,这个脚本可能需要进行适当的调整。例如,如果 class1 组已经存在,groupadd class1 命令将会失败,你可能需要检查组是否存在,然后决定是否需要执行添加组的命令。

获取行首行位几行

1
2
head -n
tail -n

‘ 和 “ 引号之间有什么区别?

1
2
3
4
5
6
7
8
9
Bash 使用过程中,经常会用双引号或单引号将字符串括起来,也可以不使用引号来定义字符串变量。

s=testString #无引号
s='testString' #单引号
s="testString" #双引号
1
2
3
因此字符串变量的定义有三种方式,分别是双引号、单引号和无引号。这三种方式有什么区别呢?单引号和双引号又有什么区别呢?
单引号
1
2
3
4
5
6
7
8
9
单引号是全引用,被单引号括起的内容不管是常量还是变量都不会发生替换。

也就是说单引号定义字符串所见即所得,将单引号内的内容输出,看到的是什么就会输出什么。

var=dablelv
echo '$var'
输出:

$var
双引号
1
2
3
4
5
6
7
双引号引用的内容,所见非所得。如果内容中有命令、变量等,会先把变量、命令解析出结果,然后在输出最终内容。双引号是部分引用,被双引号括起的内容常量还是常量,变量则会发生替换,替换成变量内容。

var=dablelv
echo "$var"
输出:

dablelv
无引号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
不使用引号定义字符串时,字符串不能包含空白字符(如 Space 或 Tab),需要加引号。一般连续的字符串、数字、路径等可以不加引号。如果内容有命令、变量等,会先把变量、命令解析出结果,然后再输出最终内容。

str1="test String"
str2='test String'
str3=test String

echo $str1
echo $str2
echo $str3
输出:

test String
test String

可见,字符串含有空格时不使用引号括起来,将无法正常输出。
建议
1
字符串常量使用单引号括起来,如果字符串含有变量、命令等使用双引号括起来,不建议不加引号。