前言
大家好! 我叫Sasha,我已经在测试后端(Linux服务和API)超过六年了。 关于这篇文章的想法是在一个熟悉的测试人员的下一个要求下告诉我的,在面试前他告诉他可以在Linux命令上阅读什么。 通常,要求QA工程师职位的候选人知道基本的命令(当然,这意味着要使用Linux),但是如果您几乎没有Linux经验,或者在准备面试时如何理解应该准备阅读哪些命令?
因此,尽管已经写过很多篇文章,但我仍然决定写另一篇文章“ Linux初学者”,并在此处列出在使用Linux的部门(或公司)进行任何采访之前您需要了解的基本命令。 我考虑了我最常使用哪些命令和实用程序以及使用哪些参数,并收集了同事的反馈,并将其全部写在一篇文章中。 本文有条件地分为三个部分:首先,有关Linux终端中I / O基础的简要信息,然后是最基本的命令的概述,第三部分介绍了Linux中典型问题的解决方案。
每个团队都有很多选择,所有这些都不会在这里列出。 您总是可以输入` man <command> `或` <command> --help`来了解有关该命令的更多信息。
一个例子:
[user@testhost ~]$ mkdir --help Usage: mkdir [OPTION]... DIRECTORY... Create the DIRECTORY(ies), if they do not already exist. Mandatory arguments to long options are mandatory for short options too. -m, --mode=MODE set file mode (as in chmod), not a=rwx - umask -p, --parents no error if existing, make parent directories as needed -v, --verbose print a message for each created directory -Z set SELinux security context of each created directory to the default type --context[=CTX] like -Z, or if CTX is specified then set the SELinux or SMACK security context to CTX --help display this help and exit --version output version information and exit GNU coreutils online help: <http://www.gnu.org/software/coreutils/> For complete documentation, run: info coreutils 'mkdir invocation'
如果命令完成所需的时间太长,则可以通过在控制台中按Ctrl + C来完成( SIGINT信号发送到该进程)。
关于命令输出的一点
在Linux上启动进程时,将为此进程创建3个标准数据流:
stdin ,
stdout和
stderr 。 它们分别编号为0、1和2。 但是我们现在对
stdout感兴趣,在较小程度上对
stderr感兴趣。 从名称中可以很容易地猜测到
stdout用于输出数据,而
stderr用于显示错误消息。 默认情况下,在Linux上运行命令时,
stdout和
stderr会将所有信息打印到控制台,但是,如果命令输出很大,则可以方便地将其重定向到文件。 例如,可以这样做:
[user@testhost ~]$ man signal > man_signal
如果我们
打印出
man_signal文件的内容,我们将看到它与简单地运行`
man signal`命令时的内容相同。
`
> `重定向操作默认使用
stdout 。 您可以显式指定
stdout重定向:`
1> `。 同样,您可以指定
stderr重定向:`
2> `。 您可以组合这些操作,从而将正常输出与命令以及错误消息的输出分开:
[user@testhost ~]$ man signal 1> man_signal 2> man_signal_error_log
您可以将
stdout和
stderr都重定向到单个文件,如下所示:
[user@testhost ~]$ man signal > man_signal 2>&1
重定向操作“
2>&1”表示将
stderr重定向到
stdout定向到的地方。
另一个用于I / O的便捷工具(或更确切地说,它是进程间通信的便捷手段)是
管道 (或
管道 )。 管道通常用于链接多个命令:命令
stdout被重定向到以下命令
stdin ,依此类推:
[user@testhost ~]$ ps aux | grep docker | tail -n 2 root 1045894 0.0 0.0 7512 3704 ? Sl 16:04 0:00 docker-containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/2fbfddaf91c1bb7b9a0a6f788f3505dd7266f1139ad381d5b51ec1f47e1e7b28 -address /var/run/docker/containerd/docker-containerd.sock -containerd-binary /usr/bin/docker-containerd -runtime-root /var/run/docker/runtime-runc 531 1048313 0.0 0.0 110520 2084 pts/2 S+ 16:12 0:00 grep --color=auto docker
基本的Linux命令
密码
打印当前(工作)目录。
[user@testhost ~]$ pwd /home/user
日期
打印系统的当前日期和时间。
[user@testhost ~]$ date Mon Dec 16 13:37:07 UTC 2019 [user@testhost ~]$ date +%s 1576503430
w
此命令显示谁登录了系统。 此外,还会显示正常运行时间和LA(平均负载)。
[user@testhost ~]$ w 05:47:17 up 377 days, 17:57, 1 user, load average: 0,00, 0,01, 0,05 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT user pts/0 32.175.94.241 05:47 2.00s 0.01s 0.00sw
ls
打印目录的内容。 如果不传递路径,则显示当前目录的内容。
[user@testhost ~]$ pwd /home/user [user@testhost ~]$ ls qqq [user@testhost ~]$ ls /home/user qqq [user@testhost ~]$ ls / bin boot cgroup dev etc home lib lib64 local lost+found media mnt opt proc root run sbin selinux srv swap sys tmp usr var
就我个人而言,我经常使用选项
-l (长列表格式-输出到包含有关文件的其他信息的列),-
t (按文件/目录修改时间排序)和
-r (反向排序-与
-t结合使用)
“最”新鲜的文件将在下面):
[user@testhost ~]$ ls -ltr / total 4194416 drwxr-xr-x 2 root root 4096 Jan 6 2012 srv drwxr-xr-x 2 root root 4096 Jan 6 2012 selinux drwxr-xr-x 2 root root 4096 Jan 6 2012 mnt drwxr-xr-x 2 root root 4096 Jan 6 2012 media drwx------ 2 root root 16384 Oct 1 2017 lost+found drwxr-xr-x 2 root root 4096 Oct 1 2017 local drwxr-xr-x 13 root root 4096 Oct 1 2017 usr drwxr-xr-x 11 root root 4096 Apr 10 2018 cgroup drwxr-xr-x 4 root root 4096 Apr 10 2018 run -rw------- 1 root root 4294967296 Sep 10 2018 swap dr-xr-xr-x 10 root root 4096 Dec 13 2018 lib drwxr-xr-x 6 root root 4096 Mar 7 2019 opt drwxr-xr-x 20 root root 4096 Mar 19 2019 var dr-xr-xr-x 10 root root 12288 Apr 9 2019 lib64 dr-xr-xr-x 2 root root 4096 Apr 9 2019 bin dr-xr-xr-x 4 root root 4096 Apr 9 2019 boot dr-xr-xr-x 2 root root 12288 Apr 9 2019 sbin dr-xr-xr-x 3229 root root 0 Jul 2 10:19 proc drwxr-xr-x 34 root root 4096 Oct 28 13:27 home drwxr-xr-x 93 root root 4096 Oct 30 16:00 etc dr-xr-x--- 11 root root 4096 Nov 1 13:02 root dr-xr-xr-x 13 root root 0 Nov 13 20:28 sys drwxr-xr-x 16 root root 2740 Nov 26 08:55 dev drwxrwxrwt 3 root root 4096 Nov 26 08:57 tmp
有2个特殊的目录名称:“
。 ”和“
.. ”。 第一个是当前目录,第二个是父目录。 它们可以方便地用于各种命令中,尤其是
ls :
[user@testhost home]$ pwd /home [user@testhost home]$ ls .. bin boot cgroup dev etc home lib lib64 local lost+found media mnt opt proc root run sbin selinux srv swap sys tmp usr var [user@testhost home]$ ls ../home/user/ qqq
还有一个用于显示隐藏文件的有用选项(以“
。 ”开头)--
a :
[user@testhost ~]$ ls -a . .. 1 .bash_history .bash_logout .bash_profile .bashrc .lesshst man_signal man_signal_error_log .mongorc.js .ssh temp test .viminfo
您还可以使用
-h选项-以易于阅读的格式输出(注意文件大小):
[user@testhost ~]$ ls -ltrh total 16K -rwxrwx--x 1 user user 31 Nov 26 11:09 temp -rw-rw-r-- 1 user user 6.0K Dec 3 16:02 1 drwxrwxr-x 2 user user 4.0K Dec 4 10:39 test
光盘
更改当前目录。
[user@testhost ~]$ pwd /home/user [user@testhost ~]$ cd /home/ [user@testhost home]$ pwd /home
如果不将目录名作为参数传递,则将使用
$ HOME环境变量,即主目录。 使用`〜`也很方便,
〜是一个特殊字符,表示
$ HOME :
[user@testhost etc]$ pwd /etc [user@testhost etc]$ cd ~/test/ [user@testhost test]$ pwd /home/user/test
麦克迪尔
创建一个目录。
[user@testhost ~]$ mkdir test [user@testhost ~]$ ls -ltr total 38184 -rw-rw-r-- 1 user user 39091284 Nov 22 14:14 qqq drwxrwxr-x 2 user user 4096 Nov 26 10:29 test
有时您需要创建特定的目录结构:例如,目录中不存在的目录。 为了不连续多次输入
mkdir ,可以使用
-p选项-它允许您创建层次结构中所有缺少的目录。 同样,使用此选项,如果目录存在,
mkdir将不会返回错误。
[user@testhost ~]$ ls qqq test [user@testhost ~]$ mkdir test2/subtest mkdir: cannot create directory 'test2/subtest': No such file or directory [user@testhost ~]$ mkdir -p test2/subtest [user@testhost ~]$ ls qqq test test2 [user@testhost ~]$ ls test2/ subtest [user@testhost ~]$ mkdir test2/subtest mkdir: cannot create directory 'test2/subtest': File exists [user@testhost ~]$ mkdir -p test2/subtest [user@testhost ~]$ ls test2/ subtest
rm
删除文件。
[user@testhost ~]$ ls qqq test test2 [user@testhost ~]$ rm qqq [user@testhost ~]$ ls test test2
-r选项允许您递归删除目录及其所有内容,
-f选项允许您忽略删除时的错误(例如,不存在的文件)。 粗略地说,这些选项可以保证删除文件和目录的整个层次结构(如果用户拥有此权限),因此,应谨慎使用它们(经典的笑话示例为“
rm -rf / ”,在某些情况下,它将删除而不是整个系统,有很多文件对其性能很重要)。
[user@testhost ~]$ ls test test2 [user@testhost ~]$ ls -ltr test2/ total 4 -rw-rw-r-- 1 user user 0 Nov 26 10:40 temp drwxrwxr-x 2 user user 4096 Nov 26 10:40 temp_dir [user@testhost ~]$ rm -rf test2 [user@testhost ~]$ ls test
cp
复制文件或目录。
[user@testhost ~]$ ls temp test [user@testhost ~]$ cp temp temp_clone [user@testhost ~]$ ls temp temp_clone test
此命令还具有
-r和
-f选项;它们可用于确保目录和文件夹的层次结构将被复制到另一个位置。
MV
移动或重命名文件或目录。
[user@testhost ~]$ ls -ltr total 4 drwxrwxr-x 2 user user 4096 Nov 26 10:29 test -rw-rw-r-- 1 user user 0 Nov 26 10:45 temp -rw-rw-r-- 1 user user 0 Nov 26 10:46 temp_clone [user@testhost ~]$ ls test [user@testhost ~]$ mv test test_renamed [user@testhost ~]$ mv temp_clone test_renamed/ [user@testhost ~]$ ls temp test_renamed [user@testhost ~]$ ls test_renamed/ temp_clone
猫
打印文件的内容。
[user@testhost ~]$ cat temp Content of a file. Lalalala...
同样值得注意的是命令的
开头 (打印文件的前
n行或字节)和
结尾 (后面的内容)。
尾巴
打印文件的最后
n行或字节。
[user@testhost ~]$ tail -1 temp Lalalala...
-f选项非常有用-它允许您实时在文件中输出新数据。
少一点
有时,文本文件太大,使用
cat命令显示它很不方便。 然后,您可以使用
less命令打开该文件:文件将按部分显示,这些部分中将提供导航,搜索和其他简单功能。
[user@testhost ~]$ less temp
较少使用
管道也可能很方便:
[user@testhost ~]$ grep "ERROR" /tmp/some.log | less
ps
列出进程。
[user@testhost ~]$ ps PID TTY TIME CMD 761020 pts/2 00:00:00 bash 809720 pts/2 00:00:00 ps
我本人通常使用BSD的“
aux ”选项-列出系统中的所有进程(由于可以有很多进程,因此我使用管道和
head命令仅打印了其中的前5个进程):
[user@testhost ~]$ ps aux | head -5 USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.0 19692 2600 ? Ss Jul02 0:10 /sbin/init root 2 0.0 0.0 0 0 ? S Jul02 0:03 [kthreadd] root 4 0.0 0.0 0 0 ? I< Jul02 0:00 [kworker/0:0H] root 6 0.0 0.0 0 0 ? I< Jul02 0:00 [mm_percpu_wq]
许多人还使用BSD的“
axjf ”选项,该选项允许您显示进程树(这里我删除了部分输出以进行演示):
[user@testhost ~]$ ps axjf PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND 0 2 0 0 ? -1 S 0 0:03 [kthreadd] 2 4 0 0 ? -1 I< 0 0:00 \_ [kworker/0:0H] 2 6 0 0 ? -1 I< 0 0:00 \_ [mm_percpu_wq] 2 7 0 0 ? -1 S 0 4:08 \_ [ksoftirqd/0] ... ... ... 1 4293 4293 4293 tty6 4293 Ss+ 0 0:00 /sbin/mingetty /dev/tty6 1 532967 532964 532964 ? -1 Sl 495 0:00 /opt/td-agent/embedded/bin/ruby /usr/sbin/td-agent --log /var/log/td-agent/td-agent.log --use-v1-config --group td-agent --daemon /var/run/td-agent/td-agent.pid 532967 532970 532964 532964 ? -1 Sl 495 803:06 \_ /opt/td-agent/embedded/bin/ruby /usr/sbin/td-agent --log /var/log/td-agent/td-agent.log --use-v1-config --group td-agent --daemon /var/run/td-agent/td-agent.pid 1 537162 533357 532322 ? -1 Sl 0 5067:43 /usr/bin/dockerd --default-ulimit nofile=262144:262144 --dns=172.17.0.1 537162 537177 537177 537177 ? -1 Ssl 0 4649:28 \_ docker-containerd --config /var/run/docker/containerd/containerd.toml 537177 537579 537579 537177 ? -1 Sl 0 4:48 | \_ docker-containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/0ee89b20deb3cf08648cd92e1f3e3c661ccffef7a0971 537579 537642 537642 537642 ? -1 Ss 1000 32:11 | | \_ /usr/bin/python /usr/bin/supervisord -c /etc/supervisord/api.conf 537642 539764 539764 537642 ? -1 S 1000 0:00 | | \_ sh -c echo "READY"; while read -r line; do echo "$line"; supervisorctl shutdown; done 537642 539767 539767 537642 ? -1 S 1000 5:09 | | \_ php-fpm: master process (/etc/php73/php-fpm.conf) 539767 783097 539767 537642 ? -1 S 1000 0:00 | | | \_ php-fpm: pool test 539767 783131 539767 537642 ? -1 S 1000 0:00 | | | \_ php-fpm: pool test 539767 783185 539767 537642 ? -1 S 1000 0:00 | | | \_ php-fpm: pool test ... ... ...
该命令有许多不同的选项,因此,在积极使用时,建议您熟悉文档。 对于大多数情况,仅了解“
ps aux ”就足够了。
杀死
向过程发送信号。 默认情况下,将
发送SIGTERM信号,该信号将终止该过程。
[user@testhost ~]$ ps ux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND 531 1027147 0.0 0.0 119956 4260 ? S 14:51 0:00 sshd: user@pts/1 531 1027149 0.0 0.0 115408 3396 pts/1 Ss 14:51 0:00 -bash 531 1027170 0.0 0.0 119956 4136 ? R 14:51 0:00 sshd: user@pts/2 531 1027180 0.0 0.0 115408 3564 pts/2 Ss 14:51 0:00 -bash 531 1033727 0.0 0.0 107960 708 pts/1 S+ 15:17 0:00 sleep 300 531 1033752 0.0 0.0 117264 2604 pts/2 R+ 15:17 0:00 ps ux [user@testhost ~]$ kill 1033727 [user@testhost ~]$ ps ux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND 531 1027147 0.0 0.0 119956 4260 ? S 14:51 0:00 sshd: user@pts/1 531 1027149 0.0 0.0 115408 3396 pts/1 Ss+ 14:51 0:00 -bash 531 1027170 0.0 0.0 119956 4136 ? R 14:51 0:00 sshd: user@pts/2 531 1027180 0.0 0.0 115408 3564 pts/2 Ss 14:51 0:00 -bash 531 1033808 0.0 0.0 117268 2492 pts/2 R+ 15:17 0:00 ps ux
由于进程可能具有信号处理程序,因此
kill并不总是会导致预期的结果-进程立即终止。 要确定要“杀死”一个进程,您需要向该进程发送一个
SIGKILL信号。 但是,这可能会导致数据丢失(例如,如果进程必须在终止之前将某些信息保存到磁盘上),因此您需要仔细使用此命令。
SIGKILL的信号号为9,因此该命令的简短版本如下所示:
[user@testhost ~]$ ps ux | grep sleep 531 1034930 0.0 0.0 107960 636 pts/1 S+ 15:21 0:00 sleep 300 531 1034953 0.0 0.0 110516 2104 pts/2 S+ 15:21 0:00 grep --color=auto sleep [user@testhost ~]$ kill -9 1034930 [user@testhost ~]$ ps ux | grep sleep 531 1035004 0.0 0.0 110516 2092 pts/2 S+ 15:22 0:00 grep --color=auto sleep
除了提到的
SIGTERM和
SIGKILL,还有许多其他信号,它们的列表可以在Internet上轻松找到。 并且不要忘记
SIGKILL和
SIGSTOP信号不能被拦截或忽略。
ping
发送
ECHO_REQUEST数据包到ICMP主机。
[user@testhost ~]$ ping google.com PING google.com (172.217.15.78) 56(84) bytes of data. 64 bytes from iad23s63-in-f14.1e100.net (172.217.15.78): icmp_seq=1 ttl=47 time=1.85 ms 64 bytes from iad23s63-in-f14.1e100.net (172.217.15.78): icmp_seq=2 ttl=47 time=1.48 ms 64 bytes from iad23s63-in-f14.1e100.net (172.217.15.78): icmp_seq=3 ttl=47 time=1.45 ms 64 bytes from iad23s63-in-f14.1e100.net (172.217.15.78): icmp_seq=4 ttl=47 time=1.46 ms 64 bytes from iad23s63-in-f14.1e100.net (172.217.15.78): icmp_seq=5 ttl=47 time=1.45 ms ^C --- google.com ping statistics --- 5 packets transmitted, 5 received, 0% packet loss, time 4006ms rtt min/avg/max/mdev = 1.453/1.541/1.850/0.156 ms
默认情况下,
ping起作用,直到手动完成为止。 因此,
-c选项可能有用-发送后
ping会自行结束的数据包数量。 我有时使用的另一个选项是
-i ,即发送数据包之间的间隔。
[user@testhost ~]$ ping -c 3 -i 5 google.com PING google.com (172.217.5.238) 56(84) bytes of data. 64 bytes from iad30s07-in-f238.1e100.net (172.217.5.238): icmp_seq=1 ttl=47 time=1.55 ms 64 bytes from iad30s07-in-f14.1e100.net (172.217.5.238): icmp_seq=2 ttl=47 time=1.17 ms 64 bytes from iad30s07-in-f14.1e100.net (172.217.5.238): icmp_seq=3 ttl=47 time=1.16 ms --- google.com ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 10006ms rtt min/avg/max/mdev = 1.162/1.295/1.551/0.181 ms
ssh
OpenSSH SSH客户端,允许您连接到远程主机。
MacBook-Pro-User:~ user$ ssh user@11.11.22.22 Last login: Tue Nov 26 11:27:39 2019 from another_host [user@testhost ~]$ hostname testhost
使用SSH有很多细微差别,该客户端也有很多功能,因此,如果您需要(或需要),可以
更详细地了解它。
scp
在主机之间复制文件(为此使用
ssh )。
[user@testhost ~]$ pwd /home/user [user@testhost ~]$ ls temp test_renamed [user@testhost ~]$ exit logout Connection to 11.11.22.22 closed. MacBook-Pro-Aleksandr:~ user$ scp user@11.11.22.22:/home/user/temp Downloads/ temp 100% 31 0.2KB/s 00:00 MacBook-Pro-Aleksandr:~ user$ cat Downloads/temp Content of a file. Lalalala...
同步
另外,要在主机之间同步目录,可以使用
rsync (
-a-存档模式,允许您“按原样”复制目录的全部内容,
-v-在控制台上显示其他信息):
MacBook-Pro-User:~ user$ ls Downloads/user ls: Downloads/user: No such file or directory MacBook-Pro-User:~ user$ rsync -av user@testhost:/home/user Downloads receiving file list ... done user/ user/.bash_history user/.bash_logout user/.bash_profile user/.bashrc user/.lesshst user/.mongorc.js user/.viminfo user/1 user/man_signal user/man_signal_error_log user/temp user/.ssh/ user/.ssh/authorized_keys user/test/ user/test/created_today user/test/temp_clone sent 346 bytes received 29210 bytes 11822.40 bytes/sec total size is 28079 speedup is 0.95 MacBook-Pro-User:~ user$ ls -a Downloads/user . .bash_history .bash_profile .lesshst .ssh 1 man_signal_error_log test .. .bash_logout .bashrc .mongorc.js .viminfo man_signal temp
回声
显示一行文本。
[user@testhost ~]$ echo "Hello" Hello
这里值得注意的是
-n选项,该选项不以结尾的换行符作为补充,而
-e选项则以“ \”打开转义的解释。
[user@testhost ~]$ echo "\tHello\n" \tHello\n [user@testhost ~]$ echo -n "\tHello\n" \tHello\n[user@testhost ~]$ [user@testhost ~]$ echo -ne "\tHello\n" Hello
另外,使用此命令可以显示变量的值。 例如,在Linux上,最后完成的命令的退出代码存储在特殊变量
$?中。 ,因此您可以准确找出上一个正在运行的应用程序中发生了什么错误:
[user@testhost ~]$ ls # 1 man_signal man_signal_error_log temp test [user@testhost ~]$ echo $? # 0 — 0 [user@testhost ~]$ ls qwerty # ls: cannot access qwerty: No such file or directory [user@testhost ~]$ echo $? # 2 — Misuse of shell builtins (according to Bash documentation) 2 [user@testhost ~]$ echo $? # echo , 0 0
远程登录
TELNET协议的客户端。 用于与其他主机通信。
[user@testhost ~]$ telnet example.com 80 Trying 93.184.216.34... Connected to example.com. Escape character is '^]'. GET / HTTP/1.1 Host: example.com HTTP/1.1 200 OK Cache-Control: max-age=604800 Content-Type: text/html; charset=UTF-8 Date: Tue, 26 Nov 2019 11:59:18 GMT Etag: "3147526947+gzip+ident" Expires: Tue, 03 Dec 2019 11:59:18 GMT Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT Server: ECS (dcb/7F3B) Vary: Accept-Encoding X-Cache: HIT Content-Length: 1256 ... , ...
如果您需要使用TLS协议(让我提醒您,SSL很久以前已经过时),则
telnet将无法用于这些目的。 但是
openssl客户端合适:
在带有GET请求的响应中使用openssl的示例 [user@testhost ~]$ openssl s_client -connect example.com:443 CONNECTED(00000003) depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA verify return:1 depth=1 C = US, O = DigiCert Inc, CN = DigiCert SHA2 Secure Server CA verify return:1 depth=0 C = US, ST = California, L = Los Angeles, O = Internet Corporation for Assigned Names and Numbers, OU = Technology, CN = www.example.org verify return:1 --- Certificate chain 0 s:/C=US/ST=California/L=Los Angeles/O=Internet Corporation for Assigned Names and Numbers/OU=Technology/CN=www.example.org i:/C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA 1 s:/C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA 2 s:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA --- Server certificate -----BEGIN CERTIFICATE----- MIIHQDCCBiigAwIBAgIQD9B43Ujxor1NDyupa2A4/jANBgkqhkiG9w0BAQsFADBN MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMScwJQYDVQQDEx5E aWdpQ2VydCBTSEEyIFNlY3VyZSBTZXJ2ZXIgQ0EwHhcNMTgxMTI4MDAwMDAwWhcN MjAxMjAyMTIwMDAwWjCBpTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3Ju aWExFDASBgNVBAcTC0xvcyBBbmdlbGVzMTwwOgYDVQQKEzNJbnRlcm5ldCBDb3Jw b3JhdGlvbiBmb3IgQXNzaWduZWQgTmFtZXMgYW5kIE51bWJlcnMxEzARBgNVBAsT ClRlY2hub2xvZ3kxGDAWBgNVBAMTD3d3dy5leGFtcGxlLm9yZzCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBANDwEnSgliByCGUZElpdStA6jGaPoCkrp9vV rAzPpXGSFUIVsAeSdjF11yeOTVBqddF7U14nqu3rpGA68o5FGGtFM1yFEaogEv5g rJ1MRY/d0w4+dw8JwoVlNMci+3QTuUKf9yH28JxEdG3J37Mfj2C3cREGkGNBnY80 eyRJRqzy8I0LSPTTkhr3okXuzOXXg38ugr1x3SgZWDNuEaE6oGpyYJIBWZ9jF3pJ QnucP9vTBejMh374qvyd0QVQq3WxHrogy4nUbWw3gihMxT98wRD1oKVma1NTydvt hcNtBfhkp8kO64/hxLHrLWgOFT/l4tz8IWQt7mkrBHjbd2XLVPkCAwEAAaOCA8Ew ggO9MB8GA1UdIwQYMBaAFA+AYRyCMWHVLyjnjUY4tCzhxtniMB0GA1UdDgQWBBRm mGIC4AmRp9njNvt2xrC/oW2nvjCBgQYDVR0RBHoweIIPd3d3LmV4YW1wbGUub3Jn ggtleGFtcGxlLmNvbYILZXhhbXBsZS5lZHWCC2V4YW1wbGUubmV0ggtleGFtcGxl Lm9yZ4IPd3d3LmV4YW1wbGUuY29tgg93d3cuZXhhbXBsZS5lZHWCD3d3dy5leGFt cGxlLm5ldDAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG AQUFBwMCMGsGA1UdHwRkMGIwL6AtoCuGKWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNv bS9zc2NhLXNoYTItZzYuY3JsMC+gLaArhilodHRwOi8vY3JsNC5kaWdpY2VydC5j b20vc3NjYS1zaGEyLWc2LmNybDBMBgNVHSAERTBDMDcGCWCGSAGG/WwBATAqMCgG CCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMAgGBmeBDAEC AjB8BggrBgEFBQcBAQRwMG4wJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj ZXJ0LmNvbTBGBggrBgEFBQcwAoY6aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t L0RpZ2lDZXJ0U0hBMlNlY3VyZVNlcnZlckNBLmNydDAMBgNVHRMBAf8EAjAAMIIB fwYKKwYBBAHWeQIEAgSCAW8EggFrAWkAdwCkuQmQtBhYFIe7E6LMZ3AKPDWYBPkb 37jjd80OyA3cEAAAAWdcMZVGAAAEAwBIMEYCIQCEZIG3IR36Gkj1dq5L6EaGVycX sHvpO7dKV0JsooTEbAIhALuTtf4wxGTkFkx8blhTV+7sf6pFT78ORo7+cP39jkJC AHYAh3W/51l8+IxDmV+9827/Vo1HVjb/SrVgwbTq/16ggw8AAAFnXDGWFQAABAMA RzBFAiBvqnfSHKeUwGMtLrOG3UGLQIoaL3+uZsGTX3MfSJNQEQIhANL5nUiGBR6g l0QlCzzqzvorGXyB/yd7nttYttzo8EpOAHYAb1N2rDHwMRnYmQCkURX/dxUcEdkC wQApBo2yCJo32RMAAAFnXDGWnAAABAMARzBFAiEA5Hn7Q4SOyqHkT+kDsHq7ku7z RDuM7P4UDX2ft2Mpny0CIE13WtxJAUr0aASFYZ/XjSAMMfrB0/RxClvWVss9LHKM MA0GCSqGSIb3DQEBCwUAA4IBAQBzcIXvQEGnakPVeJx7VUjmvGuZhrr7DQOLeP4R 8CmgDM1pFAvGBHiyzvCH1QGdxFl6cf7wbp7BoLCRLR/qPVXFMwUMzcE1GLBqaGZM v1Yh2lvZSLmMNSGRXdx113pGLCInpm/TOhfrvr0TxRImc8BdozWJavsn1N2qdHQu N+UBO6bQMLCD0KHEdSGFsuX6ZwAworxTg02/1qiDu7zW7RyzHvFYA4IAjpzvkPIa X6KjBtpdvp/aXabmL95YgBjT8WJ7pqOfrqhpcmOBZa6Cg6O1l4qbIFH/Gj9hQB5I 0Gs4+eH6F9h3SojmPTYkT+8KuZ9w84Mn+M8qBXUQoYoKgIjN -----END CERTIFICATE----- subject=/C=US/ST=California/L=Los Angeles/O=Internet Corporation for Assigned Names and Numbers/OU=Technology/CN=www.example.org issuer=/C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA --- No client certificate CA names sent Peer signing digest: SHA256 Server Temp Key: ECDH, P-256, 256 bits --- SSL handshake has read 4643 bytes and written 415 bytes --- New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256 Server public key is 2048 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE No ALPN negotiated SSL-Session: Protocol : TLSv1.2 Cipher : ECDHE-RSA-AES128-GCM-SHA256 Session-ID: 91950DC50FADB57BF026D2661E6CFAA1F522E5CA60D2310E106EE0E0FD6E70BD Session-ID-ctx: Master-Key: 704E9145253EEB4E9DC47E3DC6725D296D4A470EA296D54F71D65E74EAC09EB096EA1305CBEDD9E7020B8F72FD2B68A5 Key-Arg : None Krb5 Principal: None PSK identity: None PSK identity hint: None TLS session ticket lifetime hint: 7200 (seconds) TLS session ticket: 0000 - 68 84 4e 77 be e3 f5 00-49 c5 44 40 53 4d b9 61 h.Nw....ID@SM.a 0010 - c9 fe df e4 05 51 d0 53-ae cf 89 4c b6 ef 6c 9e .....QS..L..l. 0020 - fe 12 9a f0 e8 e5 4e 87-42 89 ac af ca e5 4a 85 ......NB....J. 0030 - 38 08 26 e3 22 89 08 b5-62 c0 8b 7e b8 05 d3 54 8.&."...b..~...T 0040 - 8c 24 91 a7 b4 4f 79 ad-36 59 7c 69 2d e5 7f 62 .$...Oy.6Y|i-..b 0050 - f6 73 a3 8b 92 63 c1 e3-df 78 ba 8c 5a cc 82 50 .s...c...x..Z..P 0060 - 33 4e 13 4b 10 e4 97 31-cc b4 13 65 45 60 3e 13 3N.K...1...eE`>. 0070 - ac 9e b1 bb 4b 18 d9 16-ea ce f0 9b 5b 0c 8b bf ....K.......[... 0080 - fd 78 74 a0 1a ef c2 15-2a 0a 14 8d d1 3f 52 7a .xt.....*....?Rz 0090 - 12 6b c7 81 15 c4 c4 af-7e df c2 20 a8 dd 4b 93 .k......~.. ..K. Start Time: 1574769867 Timeout : 300 (sec) Verify return code: 0 (ok) --- GET / HTTP/1.1 Host: example.com HTTP/1.1 200 OK Cache-Control: max-age=604800 Content-Type: text/html; charset=UTF-8 Date: Tue, 26 Nov 2019 12:04:38 GMT Etag: "3147526947+ident" Expires: Tue, 03 Dec 2019 12:04:38 GMT Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT Server: ECS (dcb/7EC8) Vary: Accept-Encoding X-Cache: HIT Content-Length: 1256 <!doctype html> <html> <head> <title>Example Domain</title> <meta charset="utf-8" /> <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <style type="text/css"> body { background-color: #f0f0f2; margin: 0; padding: 0; font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; } div { width: 600px; margin: 5em auto; padding: 2em; background-color: #fdfdff; border-radius: 0.5em; box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02); } a:link, a:visited { color: #38488f; text-decoration: none; } @media (max-width: 700px) { div { margin: 0 auto; width: auto; } } </style> </head> <body> <div> <h1>Example Domain</h1> <p>This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.</p> <p><a href="https://www.iana.org/domains/example">More information...</a></p> </div> </body> </html>
解决典型的Linux问题
变更档案拥有者
您可以使用
chown命令更改文件或目录的所有权:
[user@testhost ~]$ chown user:user temp [user@testhost ~]$ ls -l temp -rw-rw-r-- 1 user user 31 Nov 26 11:09 temp
在参数中,必须为该命令指定新的所有者和组(可选),并用冒号分隔。 同样,当更改目录的所有者时,
-R选项可能会有用-然后目录的所有内容的所有者也会更改。
更改文件权限
使用
chmod命令可以解决此问题。 举例来说,我将给权限安装“允许所有者读取,写入和执行,允许组读取和写入,其余所有权限-不进行任何操作”的权限:
[user@testhost ~]$ ls -l temp -rw-rw-r-- 1 user user 31 Nov 26 11:09 temp [user@testhost ~]$ chmod 760 temp [user@testhost ~]$ ls -l temp -rwxrw---- 1 user user 31 Nov 26 11:09 temp
参数中的前7个(在位表示形式中为0b111)表示“所有者的所有权利”,后6个(在位表示形式中为0b110)-“读和写”,那么,0对其他人来说什么都不是。位掩码由三位组成:最低有效(“右”)位负责执行,下一个(“中间”)位用于写入,而最旧的(“左”)用于读取。您还可以使用特殊字符(助记符语法)设置权限。例如,在以下示例中,首先删除当前用户的执行权限,然后返回: [user@testhost ~]$ ls -l temp -rwxrw---- 1 user user 31 Nov 26 11:09 temp [user@testhost ~]$ chmod -x temp [user@testhost ~]$ ls -l temp -rw-rw---- 1 user user 31 Nov 26 11:09 temp [user@testhost ~]$ chmod +x temp [user@testhost ~]$ ls -l temp -rwxrwx--x 1 user user 31 Nov 26 11:09 temp
该命令有许多用途,因此我建议您阅读有关它的更多信息(尤其是有关助记符语法的信息,例如here)。打印二进制文件的内容
这可以使用hexdump实用程序来完成。以下是其用法示例。 [user@testhost ~]$ cat temp Content of a file. Lalalala... [user@testhost ~]$ hexdump -c temp 0000000 C on tent ofaf il 0000010 e . \n L ala lala . . . \n 000001f [user@testhost ~]$ hexdump -x temp 0000000 6f43 746e 6e65 2074 666f 6120 6620 6c69 0000010 2e65 4c0a 6c61 6c61 6c61 2e61 2e2e 000a 000001f [user@testhost ~]$ hexdump -C temp 00000000 43 6f 6e 74 65 6e 74 20 6f 66 20 61 20 66 69 6c |Content of a fil| 00000010 65 2e 0a 4c 61 6c 61 6c 61 6c 61 2e 2e 2e 0a |e..Lalalala....| 0000001f
使用此实用程序,可以以其他格式显示数据,但是,使用这些选项时最有用。搜索文件
您可以使用find命令在目录树中按名称查找文件: [user@testhost ~]$ find test_dir/ -name "*le*" test_dir/file_1 test_dir/file_2 test_dir/subdir/file_3
其他选项和搜索过滤器也可用。例如,您可以在测试文件夹中找到5天前创建的文件: [user@testhost ~]$ ls -ltr test total 0 -rw-rw-r-- 1 user user 0 Nov 26 10:46 temp_clone -rw-rw-r-- 1 user user 0 Dec 4 10:39 created_today [user@testhost ~]$ find test/ -type f -ctime +5 test/temp_clone
搜索文件中的文本
grep命令将有助于完成此任务。她有很多用途,这是最简单的例子。 [user@testhost ~]$ grep -nr "content" test_dir/ test_dir/file_1:1:test content for file_1 test_dir/file_2:1:test content for file_2 test_dir/subdir/file_3:1:test content for file_3
使用grep命令的一种流行方法是在管道中使用它: [user@testhost ~]$ sudo tail -f /var/log/test.log | grep "ERROR"
-v选项使您可以产生grep效果,并且将显示不包含传递给grep的模式的仅相反的行。查看已安装的软件包
没有通用命令,因为这全部取决于Linux发行版和所使用的软件包管理器。以下命令之一很可能会为您提供帮助: yum list installed apt list --installed zypper se —installed-only pacman -Qqe dpkg -l rpm -qa
查看目录树需要多少空间
du命令的一种用例: [user@testhost ~]$ du -h -d 1 test_dir/ 8,0K test_dir/subdir 20K test_dir/
您可以更改-d参数的值以获取有关目录树的更多详细信息。您还可以将命令与sort结合使用: [user@testhost ~]$ du -h -d 1 test_dir/ | sort -h 8,0K test_dir/subdir 16K test_dir/subdir_2 36K test_dir/ [user@testhost ~]$ du -h -d 1 test_dir/ | sort -h -r 36K test_dir/ 16K test_dir/subdir_2 8,0K test_dir/subdir
sort命令的-h选项允许您对以人类可读格式(例如1K,2G)写入的大小进行排序,-r选项允许您以相反的顺序对数据进行排序。目录中文件中的“查找和替换”
使用sed实用程序执行此操作(末尾没有g标志,仅替换字符串中第一个出现的“ old-text”): sed -i 's/old-text/new-text/g' input.txt
您可以一次将它用于多个文件: [user@testhost ~]$ cat test_dir/file_* test content for file_1 test content for file_2 [user@testhost ~]$ sed -i 's/test/edited/g' test_dir/file_* [user@testhost ~]$ cat test_dir/file_* edited content for file_1 edited content for file_2
从输出派生列
为了配合这项任务将有助于awk的。在此示例中,显示ps ps命令输出的第二列: [user@testhost ~]$ ps ux | awk '{print $2}' PID 11023 25870 25871 25908 25909
应该记住,awk具有更丰富的功能,因此,如果需要在命令行上使用文本,则应阅读有关此命令的更多信息。通过主机名找出IP地址
以下命令之一将对此有所帮助: [user@testhost ~]$ host ya.ru ya.ru has address 87.250.250.242 ya.ru has IPv6 address 2a02:6b8::2:242 ya.ru mail is handled by 10 mx.yandex.ru. [user@testhost ~]$ dig +short ya.ru 87.250.250.242 [user@testhost ~]$ nslookup ya.ru Server: 8.8.8.8 Address: 8.8.8.8#53 Non-authoritative answer: Name: ya.ru Address: 87.250.250.242
网络信息
您可以使用ifconfig: [user@testhost ~]$ ifconfig eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 47.89.93.67 netmask 255.255.224.0 broadcast 47.89.95.255 inet6 fd90::302:57ff:fe79:1 prefixlen 64 scopeid 0x20<link> ether 04:01:57:79:00:01 txqueuelen 1000 (Ethernet) RX packets 11912135 bytes 9307046034 (8.6 GiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 14696632 bytes 2809191835 (2.6 GiB) 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 0 (Local Loopback) RX packets 10 bytes 866 (866.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 10 bytes 866 (866.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
你可以ip: [user@testhost ~]$ ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 04:01:57:79:00:01 brd ff:ff:ff:ff:ff:ff inet 47.89.93.67/19 brd 47.89.95.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fd90::302:57ff:fe79:1/64 scope link valid_lft forever preferred_lft forever 3: ip_vti0: <NOARP> mtu 1500 qdisc noop state DOWN group default link/ipip 0.0.0.0 brd 0.0.0.0
同时,例如,如果您仅对IPv4感兴趣,则可以添加-4选项: [user@testhost ~]$ ip -4 a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 inet 47.89.93.67/19 brd 47.89.95.255 scope global eth0 valid_lft forever preferred_lft forever
查看打开的端口
为此,请使用netstat实用程序。例如,要查看所有使用侦听端口的进程的PID和端口的数字表示来侦听的TCP和UDP端口,则需要将其与以下选项一起使用: [user@testhost ~]$ netstat -lptnu
系统信息
您可以使用uname命令获取此信息。 [user@testhost ~]$ uname -a Linux alexander 3.10.0-123.8.1.el7.x86_64 #1 SMP Mon Sep 22 19:06:58 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
要了解以哪种格式进行输出,可以参考以下命令的“ 帮助 ”: [user@testhost ~]$ uname --help : uname []… . , -s. -a, --all , , -p -i, : -s, --kernel-name -n, --nodename -r, --release -v, --kernel-version -m, --machine -p, --processor «» -i, --hardware-platform «» -o, --operating-system --help --version
记忆体资讯
要了解已占用或释放了多少RAM,可以使用free命令。 [user@testhost ~]$ free -h total used free shared buff/cache available Mem: 3,9G 555M 143M 56M 3,2G 3,0G Swap: 0B 0B 0B
有关文件系统的信息(可用磁盘空间)
df命令使您可以查看已挂载的文件系统上有多少可用空间。 [user@testhost ~]$ df -hT % C /dev/vda1 ext4 79G 21G 55G 27% / devtmpfs devtmpfs 2,0G 0 2,0G 0% /dev tmpfs tmpfs 2,0G 0 2,0G 0% /dev/shm tmpfs tmpfs 2,0G 57M 1,9G 3% /run tmpfs tmpfs 2,0G 0 2,0G 0% /sys/fs/cgroup tmpfs tmpfs 396M 0 396M 0% /run/user/1001
-T选项指示应显示文件系统类型。有关任务的信息和系统上的各种统计信息
为此,请使用top命令。它能够显示各种信息:例如,使用RAM的顶级进程或使用处理器时间的顶级进程。它还显示有关内存,CPU,正常运行时间和LA(平均负载)的信息。 [user@testhost ~]$ top | head -10 top - 17:19:13 up 154 days, 6:59, 3 users, load average: 0.21, 0.21, 0.27 Tasks: 2169 total, 2 running, 2080 sleeping, 0 stopped, 0 zombie Cpu(s): 1.7%us, 0.7%sy, 0.0%ni, 97.5%id, 0.0%wa, 0.0%hi, 0.1%si, 0.0%st Mem: 125889960k total, 82423048k used, 43466912k free, 16026020k buffers Swap: 0k total, 0k used, 0k free, 31094516k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 25282 user 20 0 16988 3936 1964 R 7.3 0.0 0:00.04 top 4264 telegraf 20 0 2740m 240m 22m S 1.8 0.2 23409:39 telegraf 6718 root 20 0 35404 4768 3024 S 1.8 0.0 0:01.49 redis-server
该实用程序具有丰富的功能,因此,如果您需要经常使用它,最好使自己熟悉其文档。网络流量转储
要在Linux上拦截网络流量,请使用tcpdump实用程序。要在端口12345上转储流量,可以使用以下命令: [user@testhost ~]$ sudo tcpdump -i any -A port 12345
-A选项表示我们希望以ASCII格式查看输出(因此,这对于文本协议而言是很好的选择),- i any表示我们对网络接口port不感兴趣,即要转储的端口流量。代替端口可以使用的宿主,或其组合的主机和端口(主机A和端口X)。而且-n选项也可能有用-不要在输出中将地址转换为主机名。如果是二进制流量怎么办?然后,-X选项将帮助我们-以十六进制和ASCII输出数据: [user@testhost ~]$ sudo tcpdump -i any -X port 12345
应该牢记的是,在两种使用情况下,都会输出IP数据包,因此在每种情况的开头都将有二进制IP和TCP标头。这是发送到侦听端口12345的服务器的请求“ 123 ” 的输出示例: [user@testhost ~]$ sudo tcpdump -i any -X port 12345 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes 14:27:13.224762 IP localhost.49794 > localhost.italk: Flags [P.], seq 2262177478:2262177483, ack 3317210845, win 342, options [nop,nop,TS val 3196604972 ecr 3196590131], length 5 0x0000: 4510 0039 dfb6 4000 4006 5cf6 7f00 0001 E..9..@.@.\..... 0x0010: 7f00 0001 c282 3039 86d6 16c6 c5b8 9edd ......09........ 0x0020: 8018 0156 fe2d 0000 0101 080a be88 522c ...V.-........R, 0x0030: be88 1833 3132 330d 0a00 0000 0000 0000 ...3123......... 0x0040: 0000 0000 0000 0000 00 .........
代替输出
当然,在Linux中,还有更多有趣的东西可以在Habré,StackOverflow和其他站点上阅读(我将以《命令行的艺术》为例,该文件也进行了翻译))系统管理员和DevOps使用更多的命令和实用程序来配置服务器,但是即使测试人员也可能没有足够的这些命令。如果没有可用的磁盘空间,您可能需要检查客户端到服务器或服务器操作的一些棘手超时的正确性。例如,我不是在谈论Docker,现在许多公司都在积极使用它。作为本参考文章后续内容的一部分,分析一些在测试服务过程中使用各种控制台Linux实用程序的示例将很有趣。同时在评论中分享您的顶级团队:)