查看内核路由表(包含网关等信息)
netstat -rn or route -en
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
0.0.0.0 9.134.xxx.1 0.0.0.0 UG 0 0 0 eth1
9.134.xxx.0 0.0.0.0 255.255.252.0 U 0 0 0 eth1
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
- Destination. 目标网络或目标主机。Destination 为 default(0.0.0.0)时,Gateway就表示默认网关,所有数据都发到这个网关(这里是 9.134.xxx.1)
- Gateway. 网关地址,0.0.0.0 表示当前记录对应的 Destination 跟本机在同一个网段,通信时不需要经过网关
- Genmask. Destination 字段的网络掩码,Destination 是主机时需要设置为 255.255.255.255,是默认路由时会设置为 0.0.0.0
- Iface. 网卡名字,例如 eth0
Flags 含义:
U 路由是活动的
H 目标是个主机
G 需要经过网关
R 恢复动态路由产生的表项
D 由路由的后台程序动态地安装
M 由路由的后台程序修改
! 拒绝路由
以上结果等价于命令ip route show table main
配置路由规则和路由表
linux的路由配置由两部分组成:路由规则routing rule和路由表routing table
-
table. 一个table记录了若干route(路线). the command
ip route可以指定某个table写入route -
rule. rule选择table中的route构成routing policy. 也就是说,table中的route必须要被rule选中之后才会成为路由策略的一部分。the command
ip rulemanipulates rules in the routing policy database, and control the route selection algorithm.
ip rule & ip route 命令可以分别配置路由规则和路由表,而 route命令也可以配置路由表
linux路由表
linux可以自定义从1-252个路由表,另外linux自身维护了4个路由表:
0#表 系统保留表
253#表 default table 默认路由记录都放在该表
254#表 main table 主路由表/传统路由表,ip route若没显式指定表,则使用main table,而route -en也是查询该表
255#表 local table 保存本地接口地址,广播地址、NAT地址,例如在本机上执行ssh 127.0.0.1时,就会参考这份路由表的内容。该表由系统维护,用户无法更改
(文件/etc/iproute2/rt_tables记录了路由表名和表号的对应关系)
在查看路由表之前,首先使用ip rule show命令来查看目前的路由规则使用了哪些路由表
ip rule refers rules in routing policy database
接着,再使用ip route show [table id | name]命令来查看相关路由表的内容
[root@VM-165-116-centos ~]# ip rule show
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
[root@VM-165-116-centos ~]#
[root@VM-165-116-centos ~]# ip route list table main
default via 9.aaa.164.1 dev eth1 proto dhcp metric 100
9.aaa.164.0/22 dev eth1 proto kernel scope link src 9.aaa.165.116 metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
[root@VM-165-116-centos ~]#
[root@VM-165-116-centos ~]# route -en
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
0.0.0.0 9.134.164.1 0.0.0.0 UG 0 0 0 eth1
...
[root@VM-165-116-centos ~]
解读:
当前的路由规则将使用local/main/default这三个路由表
如果有数据包要送到9.aaa.164.0/22这个网段,就直接将数据包由eth1接口送出即可,而本机临近这个网段的IP是9.aaa.165.116(这个就是本机eth1的IP)
如果有数据包要送到172.17.0.0/16这个网段,就直接将数据包由docker0接口送出即可,而本机临近这个网段的IP是172.17.0.1(这个就是本机docker0的IP)
其他目的地的数据包,经过eth1送往gateway 9.aaa.164.1,9.aaa.164.1就是我们在网络配置中所设置的“默认网关”
下面实践【配置路由表table 10】并【让路由规则使用table 10】
[root@VM-165-116-centos ~]# ip route add default via 192.168.1.254 table 10
Error: Nexthop has invalid gateway.
[root@VM-165-116-centos ~]# ip route add default via 192.168.1.254 dev eth1 table 10
Error: Nexthop has invalid gateway.
[root@VM-165-116-centos ~]# ip route add 192.168.1.0/24 dev eth1 table 10
[root@VM-165-116-centos ~]# ip route add default via 192.168.1.254 table 10
[root@VM-165-116-centos ~]# ip route show table 10
default via 192.168.1.254 dev eth1
192.168.1.0/24 dev eth1 scope link
# 最初add default via 192.168.1.254 table 10失败,是因为**内核发现eth1和机器192.168.1.254并不联通**(192.168.1.254 是随便乱写的IP,当然不联通)
# 而我们先ip route add 192.168.1.0/24 dev eth1 table 10,**内核不会检查eth1与192.168.1.0/24网段是否互通**,而是内核直接认为该记录合法
# 在这个基础上,内核就认为192.168.1.254可由达,于是add default via 192.168.1.254也顺利加入table10
[root@VM-165-116-centos ~]#
[root@VM-165-116-centos ~]# ip rule show
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
# 此时只是配置好了路由表,而没有被rule应用
[root@VM-165-116-centos ~]#
[root@VM-165-116-centos ~]# ip rule add from 192.168.1.10 table 10
[root@VM-165-116-centos ~]# ip rule add to 168.95.1.1 table 10
[root@VM-165-116-centos ~]# ip rule show
0: from all lookup local
32764: from all to 168.95.1.1 lookup 10
32765: from 192.168.1.10 lookup 10
32766: from all lookup main
32767: from all lookup default
# 来自机器192.168.1.10和去往168.95.1.1的数据包,使用table 10的配置
[root@VM-165-116-centos ~]#
[root@VM-165-116-centos ~]# ip rule del from all to 168.95.1.1 # 删除`from all to 168.95.1.1 lookup 10`
[root@VM-165-116-centos ~]# ip rule del table 10 # 删除所有使用table10的规则
[root@VM-165-116-centos ~]# ip route flush table 10 # 清空table10的路由记录
in addition:
从机器网卡发出的任何数据包,都需要一个路径(route)作为依据
机器向网关发送数据包,【发往网关】这个路径本身也是由routing rule / routing table决定的
假设机器eth1的IP是192.168.1.1,所连接的局域网网关为192.16那么8.1.254,给routing table配置这样的route是非法的:
ip route add 192.168.1.0/24 via 192.168.1.254 dev eth1
这个命令表示,目的网络是192.168.1.0/24则需要通过eth1先发往网关192.168.1.254
但是网关192.168.1.254本身也处在目标网络192.168.1.0/24内,因此发往【192.168.1.254】需要通过eth1先发往【192.168.1.254】
so the kernel has no idea how to get to gateway 192.168.1.254
事实上,发往局域网内的数据包不经过网关
网络抓包
tcpdump
[-c count(抓几个包)]
[-s snaplen-bytes 每个包抓多长字节的数据 -s64每包抓64字节 -s0还原到默认]
[-i network-interface(指定网卡)]
[-nn Don't convert protocol and port numbers etc. to names either.]
[-v 让抓包过程打印更多详情]
[-w file(保存到文件)]
[ expression (see pcap-filter)]
only packets that match expression will be processed by tcpdump
eg. tcpdump -i eth1 -s64 -c10 -w /filter.pcap tcp port 6666 and host 9.9.9.9
查看指定网卡IP
ifconfig eth1 |grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr -d "addr:"
查看dns配置
cat /etc/resolv.conf
netstat查看网络连接/端口监听
netstat -tunlp是一个Linux系统管理员经常使用的命令,可以展示当前正在监听或者连接的网络连接和它们使用的进程。
具体含义如下:
"-t"参数显示TCP连接。
"-u"参数显示UDP连接。
"-n"参数禁用名称解析,直接显示IP地址和端口号。
"-l"参数显示当前正在监听的所有连接。
"-p"参数显示进程PID和名字。
因此,netstat -tunlp命令用于显示所有正在监听或者连接的TCP和UDP端口,包括它们的IP地址和端口号,并且显示处理网络连接的进程的PID和名称。
netstat类似于TCPView或者活动监视器(Windows系统)的功能。而lsof是用于查看系统中打开的文件("list open files")和相关进程的信息,其中也包括网络连接。它可以列出当前系统中被打开的所有文件和网络连接,不仅显示网络连接的信息,还可以查看文件相关的信息,比如文件名、文件描述符等
lsof看的是文件,netstat看的是网络连接。由于lsof能够查看打开文件描述符和端口号的详细信息,因此在调试和排查网络连接问题时,更经常使用lsof命令。而netstat则更常用于监控当前网络连接的状态
为什么lsof的结果显示,FD相同,但是PID却不同
eg.
root@centos:~# lsof -i:7500
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
gunicorn 38756 root 7u IPv4 894555171 0t0 TCP *:silhouette (LISTEN)
gunicorn 38757 root 7u IPv4 894555171 0t0 TCP *:silhouette (LISTEN)
gunicorn 38758 root 7u IPv4 894555171 0t0 TCP *:silhouette (LISTEN)
gunicorn 38759 root 7u IPv4 894555171 0t0 TCP *:silhouette (LISTEN)
gunicorn 38760 root 7u IPv4 894555171 0t0 TCP *:silhouette (LISTEN)
gunicorn 38761 root 7u IPv4 894555171 0t0 TCP *:silhouette (LISTEN)
gunicorn 38762 root 7u IPv4 894555171 0t0 TCP *:silhouette (LISTEN)
gunicorn 38764 root 7u IPv4 894555171 0t0 TCP *:silhouette (LISTEN)
gunicorn 38765 root 7u IPv4 894555171 0t0 TCP *:silhouette (LISTEN)
gunicorn 38766 root 7u IPv4 894555171 0t0 TCP *:silhouette (LISTEN)
gunicorn 38767 root 7u IPv4 894555171 0t0 TCP *:silhouette (LISTEN)
gunicorn 38768 root 7u IPv4 894555171 0t0 TCP *:silhouette (LISTEN)
gunicorn 38770 root 7u IPv4 894555171 0t0 TCP *:silhouette (LISTEN)
在lsof命令的输出中,同一文件描述符(FD)对应的进程ID通常是唯一的。但在某些情况下,可能会出现相同的文件描述符对应不同的进程ID的情况,这通常发生在:
程序使用了“fork()”系统调用创建了子进程,而子进程没有关闭对应的文件句柄,在父进程和子进程都能够访问同一个文件描述符的情况下,会存在同一个文件描述符对应多个进程ID的情况。
程序使用了共享内存或者共享套接字,这也会导致同一文件描述符对应不同的进程PID。
在这些情况下,可以通过查看"TYPE"字段,来判断同一文件描述符对应的是同一种文件类型,同一种类型的文件描述符才可能被多个不同的进程使用。
配置系统http/https代理
部分linux服务器处于内网或者受限制的网络环境下,想与外网进行http/https通信只能通过nat或者加proxy的方式。nat服务器有网段的限制,而http/https proxy代理则没有
linux设置http/https proxy的方法,在当前shell、或者/etc/bashrc、或者/etc/profile中添加如下环境变量
export http_proxy=http://1.1.1.1:8082
export https_proxy=http://1.1.1.1:8082
配置某些域名禁用代理
在当前shell、或者/etc/bashrc、或者/etc/profile中添加如下环境变量
export no_proxy='a.test.com,127.0.0.1,2.2.2.2'
vim内查找目标
vim file后,键入/进入查找模式
/后接需要查找的目标
- 支持大小写敏感(默认就是大小写敏感)
在查找模式中,加入\c表示大小写不敏感查找,\C表示大小写敏感查找
例如:
/foo\c 大小写不敏感
- 支持正则表达式
例如:
/^foo$\c
- 对于多个结果,键入
n跳转到下一个,键入N跳转到下一个
根据文件名查找当前文件夹及其子文件夹的所有文件
find . -type f -name "regular_expression"
从文件中查找匹配pattern的行
grep -e “regular_expression" target_file
Eg.
grep -e "message.*已经分配给ID为" ./do_reservation.log
"message" : "IP[10.0.128.4]已经分配给ID为[574917731]的服务器",
"message" : "IP[10.0.130.20]已经分配给ID为[574918748]的服务器",
# 按:分割并且输出第2部分
grep -e "message.*已经分配给ID为" ./do_reservation.log | cut -d ":" -f 2
"IP[10.0.128.4]已经分配给ID为[574917731]的服务器",
"IP[10.0.130.20]已经分配给ID为[574918748]的服务器",
# 使用 -o --only-matching Prints only the matching part of the lines. 仅输出IP地址
grep -e "message.*已经分配给ID为" ./do_reservation.log | cut -d ":" -f 2 | grep -E -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
or
grep -e "message.*已经分配给ID为" ./do_reservation.log | cut -d ":" -f 2 | grep -E -o '([0-9]{1,3}[\.]){3}[0-9]{1,3}'
10.0.128.4
10.0.130.20
目录跳转 pushd popd
命令行的当前目录等于目录栈栈顶目录
[user@server /usr/ports] $ pushd /etc
/etc /usr/ports ->pushd后当前目录栈的情况
[user@server /etc] $ popd
/usr/ports ->popd后当前目录栈的情况
[user@server /usr/ports] $
追踪dns查询过程 dig(domain information groper)
dns的查询过程:DNS客户端和本地名称服务器是递归查询,而本地名称服务器和其他名称服务器之间是迭代查询。Most DNS administrators use dig command to troubleshoot DNS problems
common command: dig [Options] [TYPE] [Domain_Name]
dig targetdomain
[root@VM-165-116-centos workspace]# dig stackoverflow.com
; <<>> DiG 9.10.6 <<>> stackoverflow.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 17243
;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4000
;; QUESTION SECTION: # -> 显示查询内容,查询stackoverflow.com的A记录
;stackoverflow.com. IN A
;; ANSWER SECTION: # -> 显示dns的应答结果。stackoverflow.com有4个A记录,且time-to-live都是299秒(这个时间内无需重新查询)
stackoverflow.com. 299 IN A 151.101.65.69
stackoverflow.com. 299 IN A 151.101.193.69
stackoverflow.com. 299 IN A 151.101.129.69
stackoverflow.com. 299 IN A 151.101.1.69
;; Query time: 655 msec
;; SERVER: 10.123.119.98#53(10.123.119.98) # -> 本机的dns server是10.123.119.98,dns服务端口为53
;; WHEN: Thu Jan 27 17:36:48 CST 2022
;; MSG SIZE rcvd: 115 # -> 响应字节数115
dig +trace targetdomain
参考https://www.2daygeek.com/dig-command-check-find-dns-records-lookup-linux/
清除dns缓存
What is DNS cache and what it Does?
The DNS cache is a temporary database maintained by the computer’s operating system.
It stores information about previous DNS lookups (like information on recently visited websites and other web domains).
This will quickly resolve DNS queries when you visit the cached website by getting details from the local DNS database instead of the actual DNS server.
How to Clear/Flush the DNS Cache on Linux
By default, DNS caching is not installed or enabled at the OS level, but if you have installed any of the caching services listed below, use the appropriate commands to flush them.
Below is a list of the major DNS cache services used in the Linux operating system.
systemd Resolved Service
nscd DNS Cache
dnsmasq DNS Cache
BIND server DNS Cache
systemd Resolved Service
Most modern Linux operating systems use systemd as the default init proccess(当前大部分linux os 使用systemd作为默认的init程序), so use the command below to flush the DNS cache.
$ sudo systemctl is-active systemd-resolve.service # check if the DNS cache service is active on your system.
$ sudo systemd-resolve --flush-caches
nscd DNS Cache
nscd stands for name service cache daemon, nscd is a daemon that provides a cache for the most common name service requests. The default configuration file located at /etc/nscd.conf. 里面配置了enable-cache的类型、time-to-live、max-db-size等参数,决定了哪些信息需要被cached、保持时长、cached数据库大小等
$ sudo systemctl restart nscd # 重启以清理缓存
dnsmasq DNS Cache
Dnsmasq is a lightweight, small footprint, easy to configure, DNS forwarder and DHCP server. It is designed to provide DNS and optionally, DHCP, to a small network & suitable for resource constrained routers and firewalls. It can serve the names of local machines which are not in the global DNS. It is designed for personal computer use and small size networks, not for big networks. dnsmasq为私有dns服务
$ sudo systemctl restart dnsmasq # 重启以清理缓存
BIND Server DNS Cache
BIND stands for “Berkeley Internet Name Domain”. The most widely used Name Server Software, BIND is open source software that implements the Domain Name System (DNS) protocols for the Internet. BIND is by far the most widely used DNS software on the Internet, providing a robust and stable platform. 通过起named service来提供dns服务
$ sudo systemctl restart named # 使用重启服务的方式,flush the BIND server DNS cache on Systemd-based Linux systems.
or
$ sudo rndc flushname 2daygeek.com # rndc, named的服务管理工具,指定删除某个域名的dns缓存
很多dns服务都通过重启来实现清理缓存的目的
关于/etc/hosts和nslookup
nslookup only does proper DNS resolution, which is significantly different from the Name Service Switch subsystem that your other applications use; that is to say nslookup ignores /etc/hosts and mDNS.
To test local dns resolutions after modifying /etc/hosts, use something that uses NSS. ping <hostname> for example
修改环境变量
- 永久生效:
系统环境变量
vim /etc/profile
source /etc/profile用户shell环境变量
Linux系统中有很多种类的shell,不管是bash,sh,dash,都会有一个~/.bashrc的隐藏文件,该文件存在于用户的家目录,是每一个用户运行shell时执行的文件,当shell被打开时,该文件被读取。这个文件主要保存个人用户的一些个性化的设置
vim ~/.bashrc
source ~/.bashrc
cat ~/.bashrc
# .bashrc
# User specific aliases and functions
# for TencentOS, remove these aliases
#alias rm='rm -i'
#alias cp='cp -i'
#alias mv='mv -i'
# Source global definitions ->source用户的~/.bashrc前,会先source一下全局bashrc
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
export PATH="$PATH:$HOME/.ft:$GOPATH/bin"
- 当前shell临时生效的变量
eg.export PATH=$PATH:/opt/au1200_rm/build_tools/bin. 当shell退出的时候,这些变量就会失效
注意:-
do not use
sudobeforeexport. Because "export" is a "shell built-in", not a "command"(a command is an executable binary on the host). "sudo" runs commands. That's why it can't find a command when you try to "sudo export". -
take care of the variable scope in shell. shell是一个进程,拥有独立的内存空间,进程间数据不可见
一个shell中用export定义的环境变量只会对当前shell及其子shell有效,而无法作用于父shell。shell结束之后,变量生命周期结束
-
do not use
内核
一个os发行版可以支持多个kennel
安装内核 yum install 内核,在grab.conf中配置使用的内核
非交互式修改用户密码
chpasswd命令
[root@VM-165-116-centos sbin]# chpasswd -h
Usage: chpasswd [options]
Options:
-c, --crypt-method METHOD the crypt method (one of NONE DES MD5 SHA256 SHA512)
-e, --encrypted supplied passwords are encrypted
-h, --help display this help message and exit
-m, --md5 encrypt the clear text password using
the MD5 algorithm
-R, --root CHROOT_DIR directory to chroot into
-s, --sha-rounds number of SHA rounds for the SHA*
crypt algorithms
[root@VM-165-116-centos sbin]# echo "username":"newpwd" | /usr/sbin/chpasswd -c MD5
curl 获取时延详情
-w, --write-out <format>
Make curl display information on stdout after a completed transfer
-s, --silent
Silent or quiet mode. Don't show progress meter or error messages. Makes Curl mute. It will still
output the data you ask for, potentially even to the terminal/stdout unless you redirect it.
curl https://www.baidu.com --connect-timeout 5 -m 10 -o /dev/null -s -w time_namelookup:%{time_namelookup},time_connect:%{time_connect},time_appconnect:%{time_appconnect},time_starttransfer:%{time_starttransfer},time_total:%{time_total}"\n"
All variables are specified as %{variable_name} and to output a normal % you just write them as %%. You can output a newline by using \n, a carriage return with \r and a tab space with \t.
login shell and non-login shell
A login shell is the first process that executes under your user ID when you log in for an interactive session. The login process tells the shell to behave as a login shell with a convention: passing argument 0, which is normally the name of the shell executable, with a - character prepended (e.g. -bash whereas it would normally be bash.
Login shells typically read a file that does things like setting environment variables:
/etc/profile and
~/.profile for the traditional Bourne shell,
~/.bash_profile additionally for bash,
/etc/zprofile and ~/.zprofile for zsh,
/etc/csh.login and ~/.login for csh,
etc.
to tell if you are in a login shell:
prompt> echo $0
-bash # "-" is the first character. Therefore, this is a login shell.
prompt> echo $0
bash # "-" is NOT the first character. This is NOT a login shell.
When you log in on a text console, or through SSH, or with su -, you get an interactive login shell. When you log in in graphical mode (on an X display manager), you don't get a login shell, instead you get a session manager or a window manager.
When you start a shell in a terminal in an existing session (screen, X terminal, Emacs terminal buffer, a shell inside another, etc.), you get an interactive, non-login shell.
That shell might read a shell configuration file:
~/.bashrc for bash,
/etc/zshrc and ~/.zshrc for zsh,
/etc/csh.cshrc and ~/.cshrc for csh,
etc【各种rc】
rc refers to 'run commands'
su (switch user with login shell or non-login shell)
The su (short for substitute or switch user) utility allows you to run commands with another user’s privileges(the fact is to run commands with substitute user and group ID)
For backward compatibility su defaults to not change the current directory and to only set the environment variables HOME and SHELL (plus USER and LOGNAME if the target user is not root).
It is recommended to always use the --login option (instead it's shortcut -) to avoid side effects caused by mixing environments. This makes the shell a login shell with an environment very similar to a real login and changes the current directory
-, -l, --login
Starts the shell as login shell with an environment similar to a real login:
o clears all environment variables except for TERM
o initializes the environment variables HOME, SHELL, USER, LOGNAME, PATH
o changes to the target user's home directory
o sets argv[0] of the shell to '-' in order to make the shell a login shell
redirecton
What does " 2>&1 " mean?
File descriptor 1 is the standard output (stdout).
File descriptor 2 is the standard error (stderr).
- At first, 2>1 may look like a good way to redirect stderr to stdout. However, it will actually be interpreted as "redirect stderr to a file named 1".
- & indicates that what follows and precedes is a file descriptor(like a pointer in C), and not a filename. Thus, we use 2>&1. Consider >& to be a redirect merger operator.
至于为什么不是&2>&1 2> &1,我也不知道
bash set
set 顾名思义,可以理解为对shell解释器本身的行为进行控制,如访问不存在的变量是否报错,运行错误是否自动exit等。有点类似于sysctl去控制内核的行为
eg.
set -e e stands for errexit
set -e 根据命令返回值来判断一个命令是否运行失败;只要运行失败发生错误,且不在shell的正常handle范围,就终止执行并且退出当前函数或者当前shell
Exit immediately if a pipeline (which may consist of a single simple command), a list, or a compound command, exits with a non-zero status
注意,以下几种情况不会exit,它们属于shell正常handle范围
The shell does not exit if the command that fails is
- part of the command list immediately following a while or until keyword, (控制逻辑的一部分)
- part of the test following the if or elif reserved words, (判断逻辑的一部分)
- part of any command executed in a && or || list except the command following the final && or || (非
&&||的最后一个命令,这种情况属于shell的正常handle范围:例如 A && B,A fails后,shell自动停止向后执行B;A || B,A fails后,shell自动向后执行B,保证||语义正确) - any command in a pipeline but the last(非管道的最后一个命令)
- or if the command's return value is being inverted with
!
[root@VM-165-116-centos ~]# set -e; foo | echo hhh
hhh
bash: foo: command not found...
[root@VM-165-116-centos ~]#
[root@VM-165-116-centos ~]# set -e; foo && echo hhh
bash: foo: command not found...
[root@VM-165-116-centos ~]# echo $?
127
[root@VM-165-116-centos ~]#
[root@VM-165-116-centos ~]# set -e; echo hhh && foo
hhh
bash: foo: command not found...
Terminal Disconnect.
exitCode: 127
reason: no reason
Press <Enter> to reconnect...
为什么会有set -e —— 关键是遇错自动Abort
当一个shell脚本在运行中出现错误,我们不期望脚本继续运行下去而是直接退出,我们可以通过在每条命令或者关键命令的最后加上|| exit 1来实现。但是这样一来,shell脚本会变得冗余并且丑陋。于是,shell的开发者专门针对“错误捕捉”开发了set -e
Its goal was to cause the shell to abort any time an error occurred, so you don't have to put || exit 1 after each important command
The developers of the original Bourne shell decided that they would create a feature that would allow the shell to check the exit status of every command that it runs, and abort if one of them returns non-zero. Thus, set -e was born
为什么shell需要这个略微奇葩的错误处理机制
归根到底,shell是低可控性的脚本语言,发生什么都是有可能的,所以干脆搞一个报错退出
低可控性体现在两个方面:
- 最主要的原因就是偏底层,其错误信息基本上只依靠return code,无法提供更丰富的错误信息,导致无法较方便地实现更上层的错误处理。除非每行命令后都进行
if [ $? == 0 ]判断 - shell脚本调用了大量的external programs,你无法知道这些external programs会如何返回它的return code. 例如,shell 的builtIn
let arg [arg ...]If the last arg evaluates to 0, let returns 1; 0 is returned otherwise. 所以i=0; let i++;会将i自增1,但是i++本身evaluates to 0,因此let i++returncode是 1,等于是逻辑全部正常执行,但是返回了“错误”
番外:
In modern high-level languages, most tasks are performed by using the language's builtin commands or features. The language knows whether (for example) you tried to divide by zero, or open a file that you can't open, and so on. It can take action based on this knowledge.
But in the shell, most of the tasks you actually care about are done by external programs. The shell can't tell whether an external program encountered something that it considers an error -- and even if it could, it wouldn't know whether the error is an important one, worthy of aborting the entire program, or whether it should carry on.
Exercise 1: why doesn't this example print anything?
#!/usr/bin/env bash
set -e
i=0
let i++ # returncode is 1 and exit immediately
echo "i is $i"
Exercise 2: why does this one sometimes appear to work? In which versions of bash does it work, and in which versions does it fail?
#!/usr/bin/env bash
set -e
i=0
((i++))
echo "i is $i"
Exercise 3: why aren't these two scripts identical?
#!/usr/bin/env bash
set -e
test -d nosuchdir && echo no dir # err in test is ignored by set -e
echo survived # echo successfully
#!/usr/bin/env bash
set -e
f() { test -d nosuchdir && echo no dir; }
f # exit immediately
echo survived
Exercise 4: why aren't these two scripts identical?
set -e
f() { test -d nosuchdir && echo no dir; }
f # exit immediately because if there is no return in a func, the func use the last command's return code as its return code
echo survived
set -e
f() { if test -d nosuchdir; then echo no dir; fi; }
f
echo survived # echo successfully
more for https://wangdoc.com/bash/set, http://mywiki.wooledge.org/BashFAQ/105
subnet mask 和 umask
subnet mask中1表示位有效,0表示位无效,就像遮蔽一样
ip 与 subnet mask 按位与,得到子网地址
而umask和subnet mask表示遮蔽的方式是相反的,1表示遮蔽(这真是迷,subnet mask更容易接受)
因此,要用【按位与】去计算最终文件权限,必须先对umask取反
如,文件的原始权限是666,root的默认umask=0022(高位0忽略,有用的就是022)
则最终文件权限计算如下:
110 110 110 (6 6 6)
& 111 101 101 (~0 ~2 ~2) # ~0 = 111,表示无遮蔽;~2 = 101,表示遮蔽中间位
---------------------------
110 100 100 (6 4 4)
download using wget or curl
wget --connect-timeout 10 -t2 https://mirrors.tencent.com/repository/generic/xxx/xxx.tgz [-O xxx.tgz]
or
curl -s --resolve "mirrors.tencent.com:80:100.115.83.221" --resolve "mirrors.tencent.com:80:9.2.87.192" http://mirrors.tencent.com/repository/generic/xxx/xxx.tgz --connect-timeout 5 [-o xxx.tgz]