Shell编程实例——高级脚本编程(一)

高级脚本编程

1、以可移植的方式查找bash

你需要在多台机器上运行 bash 脚本,但 bash 未必总位于相同的位置​。

在 shebang 行中使用 /usr/bin/env 命令,也就是#!/usr/bin/env bash。如果所在系统的 /usr/ bin 中没有env,你可以请求系统管理员安装该命令并将其放到此位置,或者创建符号链接。

你也可以为 bash 自身创建符号链接,但使用 env 作为解决方案不仅规范,而且正确。

env 的作用是"在改动过的环境中运行程序"​,但因为它会搜索待运行的命令,所以非常适合在这里使用。

你也许想改用 !/bin/sh。别这么做。如果脚本中用到了bash 独有的特性,对于那些没有在 Bourne shell 模式下使用 bash 的机器(如 BSD、Solaris、Ubuntu 6.10+)​,这些特性将无法使用。即便现在没有涉及 bash 独有的特性,搞不好以后就把这件事给忘了。如果下定决心只使用POSIX 特性,一定要采用 !/bin/sh​,否则视具体情况而定。

有时你可能会发现 #! 和 /bin/whatever 之间有一个空格。过去有些系统要求有这个空格,不过我们很久没在实践中这么写了。运行 bash 的系统几乎都不会要求使用空格,不加空格是目前最常见的写法。但为了获得最大程度的向后兼容性,可以将空格加上。

我们选择在代码较多的脚本和函数中使用 #!/usr/bin/envbash,这样在大多数系统中就不用再修改了。但是,因为env 要借助 $PATH 来查找 bash,这可以说存在安全问题​,不过我们觉得也算不上什么大事。

我们尝试用 env 实现可移植性,而 shebang 行的处理在不同系统之间并不一致,这实在是讽刺。包括 Linux 在内的很多系统只允许解释器携带一个参数。因此,#!/usr/bin/env bash - 会产生错误。

bash 复制代码
/usr/bin/env: bash -: No such file or directory

原因在于解释器是 /usr/bin/env,允许出现在其后的单个参数是 bash -。其他系统(如 BSD 和Solaris)则没有这种限制。

尾部的 - 是一种常见的安全实践​,但并非所有系统都支持这种写法,因此存在安全性和可移植性问题。

要么以牺牲可移植性为代价,使用尾部的 - 来提升些许安全性;要么为了可移植性,直接将其忽略,代价是会带来一点潜在的安全风险。

因此,我们的建议是,如果考虑到可移植性而使用env,那么可以忽略 -;如果安全至关重要,则可以将解释器硬编码进 shebang 行并在末尾添加 -。

2、设置兼容POSIX工具的$PATH

你所在机器上的一些工具要么上了年代,要么属于专有工具(如 Solaris)​,你需要设置好路径,以便能够正常执行兼容 POSIX 的工具。

使用实用工具 getconf。

bash 复制代码
PATH=$(PATH=/bin:/usr/bin getconf PATH)

以下是几种系统上的一些默认路径和 POSIX 路径。

bash 复制代码
# Red Hat Enterprise Linux (RHEL) 4.3
$ echo $PATH
/usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/$USER/bin

$ getconf PATH
/bin:/usr/bin

# Debian Sarge
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games

$ getconf PATH
/bin:/usr/bin

# Solaris 10
$ echo $PATH
/usr/bin:

$ getconf PATH
/usr/xpg4/bin:/usr/ccs/bin:/usr/bin:/opt/SUNWspro/bin

# OpenBSD 3.7
$ echo $PATH
/home/$USER/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/X11R6/bin:/usr/local/bin:/usr/
local/sbin:/usr/games

$ getconf PATH
/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11R6/bin:/usr/local/bin

getconf 能够显示出各种系统配置变量,你可以用它来设置默认路径。但除非 getconf 本身就是内建的,否则还需要一个最小路径来找到它,因此解决方案还包括PATH=/bin:/usr/bin。

从理论上说,你应该使用变量 CS_PATH。但在实践中,PATH 在我们测试过的所有系统中都没有问题,而CS_PATH 在 BSD 中反倒不行。

3、开发可移植的shell脚本

你所编写的 shell 脚本需要在多个版本的 Unix 或 POSIX操作系统上运行。

首先,使用包含 -p 选项的 command 找出 program 的POSIX 版本。例如,在 Solaris 中的 /usr/xpg4 或/usr/xpg6:

bash 复制代码
command -p program args

然后,如果可能的话,找一台年代最久或者功能最少的Unix 机器,在该平台上开发脚本。如果不确定哪个平台的功能最少,可以使用 BSD 变体或 Solaris(版本越旧越好)​。

command -p 使用默认路径,以确保能够找到所有符合POSIX 标准的实用工具。如果你确定脚本只在 Linux 上运行,那就不用操心了;否则,避免在 Linux 或 Windows(例如,通过 Cygwin)上开发跨平台脚本。

在 Linux 上编写跨平台脚本存在两个问题。

  • /bin/sh 并非 Bourne shell,而是处于 POSIX 模式的/bin/bash,除非它指向的是 /bin/dash(如 Ubuntu6.10+)。两者都挺不错,但算不上完美,而且三者的工作方式也不完全一致,着实让人困惑不已。尤其是 echo 的行为不尽相同。
  • Linux 使用的是 GNU 工具,而不是原先的 Unix 工具。

别误会,我们热爱 Linux,天天都离不开它。但是,Linux真的和 Unix 不是一回事:它的某些行事方式不同,而且拥有 GNU 工具。GNU 工具挺不错,问题也就在于此。这些工具所包含的大量选项和特性在其他平台上根本找不到,不管多小心翼翼,你的脚本还是会莫名其妙地出错。相反,Linux 的兼容性非常好,为别的类 Unix 系统编写的脚本基本都能在 Linux 上运行。也许表现的不算完美(例如,echo 的默认行为是显示 \n,而不是输出换行符)​, 但通常已经够好了。

另一个为难的地方是,虽然 GNU 长选项在 shell 代码中可读性要好得多,但在其他系统中通常并不可用。因此,为了可移植性,还是老老实实用 sort -t,unsorted_file>sorted_ filesort 代替 sort --field-separator=, unsorted_file > sorted_file 吧。

别泄气:在非 Linux 系统上做开发要比以前更容易了。如果你已经拥有并正在使用此类系统,这显然不是什么问题。如果尚未配备,现在动手,很容易就可以弄到。Solaris 和 BSD 都能在虚拟化环境中运行​。

如果你使用的是运行 macOS(以前是 OS X)的 Mac,那么 bash 和 BSD 就是现成的了。

你还可以使用虚拟化环境轻松地测试脚本(参见 15.4节)​。这种解决方案的缺点是,AIX 和 HP-UX 等系统无法在 x86 架构之上运行,自然也就不能在 x86 虚拟化环境中运行。

Debian 和 Ubuntu 用户应该安装 devscripts软件包(aptitude install devscripts)​,它提供了checkbashisms 脚本,可以帮助查找那些不兼容POSIX、无法在 dash 中使用的 shell 脚本(bashisms)​。其他操作系统或 Linux 发行版的用户不妨看看该脚本是否可用于自己的系统。

4、用虚拟机测试脚本

你需要开发跨平台的脚本,但缺乏适合的系统或硬件。

如果目标平台运行在 x86 架构上,要么在众多的免费和商业虚拟化解决方案中选择一种,构建自己的测试虚拟机,要么在操作系统厂商的网站或者 Internet 上搜索预先构建好的虚拟机。也可以使用云供应商提供的免费(试用期间)或低成本的虚拟机。

这种解决方案的缺陷是,AIX 和 HP-UX 这种系统无法在x86 架构上运行,因此 x86 虚拟化也就无计可施了。还是那句话,如果你有这些系统,那就用;如果没有,参见1.18 节。

测试 shell 脚本通常不属于资源密集型操作,能运行VirtualBox 或者类似虚拟化软件的一般硬件就可以了。我们特别提及 VirtualBox 是因为它不用花钱,可以在Linux、macOS、Windows 平台上运行,网上不计其数的示例和工具(如 Vagrant)中都能看到其身影,而且灵活易用,不过肯定也有别的替代品。

配备 128 MB 内存(有时甚至更少)的最小虚拟机对于shell 环境测试已经绰绰有余了。可以设置 NFS 共享来存储测试脚本和数据,然后通过 SSH 连接到测试系统。如果想要自己构建虚拟机,Debian 是个不错的选择;记得在安装过程中取消能取消的一切选项。

Internet 上有不少预先构建好的虚拟机,但质量和安全性参差不齐。如果你是在工作中进行测试,请务必核对公司的政策;许多公司禁止将"从 Internet 随意下载的东西"带进公司网络。另外,你所在的公司也许会构建或提供自己的虚拟机映像以供内部使用。你可能只需要一个非常小的虚拟机来测试 shell 脚本,但"小"这个词的定义在不同的环境中差别颇大。估计你得花点心思才能找到符合需求的虚拟机。不妨先看看几个不错的地方。

5、使用可移植的循环

你要用到 for 循环,但同时希望它也能在较旧的 bash 版本中正常工作。

以下方法能够向后兼容到 bash 2.04+。

bash 复制代码
$ for ((i=0; i<10; i++)); do echo $i; done
0
1
2
3
4
5
6
7
8
9

较新的 bash 版本中还有一些更好的循环写法,但无法向后兼容。从 bash 3.0+ 开始,你可以使用语法 for {x...y}。

bash 复制代码
$ for i in {1..10}; do echo $i; done
1
2
3
4
5
6
7
8
9
10

如果系统有 seq 命令,也可以像下面这样做:

bash 复制代码
$ for i in $(seq 1 10); do echo $i; done
1
2
3
4
5
6
7
8
9
10

6、使用可移植的echo

编写的脚本将在多个版本的 Unix 和 Linux 上运行,你需要 echo 表现出一致的行为,即便没有使用 bash。

使用 printf "%b" whatever,或者对系统进行测试,根据需要使用 shopt -s xpg_echo 设置 xpg_echo。

如果忽略格式字符串 "%b"(如 printf whatever)​,那么printf 会尝试解释出现在 whatever 中的 % 字符,这可能不是你想要的结果。"%b" 格式是对标准 printf 格式的补充,能够避免这种误解并扩展 whatever 中出现的转义序列。

设置 xpg_echo 的一致性较差,因为它仅适用于 bash。如果你确定只在 bash 下运行,不会涉及 sh 或其他不使用xpg_echo 的 shell,这种方法还是行之有效的。

printf 的写法不同于 echo 语句,前者是由 POSIX 定义的,应该能在任何 POSIX shell 中保持一致性。具体来说,你得写成 printf "%b",而不仅仅是 echo。

如果顺手输入的是 b,而不是 %b,那可就要倒霉了,这时会出现一个空行,因为你指定的是空格式(null format)​,也就是说,除非已经定义过 b,在这种情况下,输出结果取决于 $b 的值。

不管怎样,这都是一个很难查找的 bug,毕竟 $b和 %b 看起来的确很像。

bash 复制代码
$ printf "%b" "Works"
Works
$ printf "$b" "Broken"
$

某些 shell 内建的 echo 的行为和外部程序的 echo 并不一样。这一点在 Linux 中未必总能被注意到,因为 /bin/sh指向的其实是 bash(通常是这样,在 Ubuntu 6.10+ 中则是 dash)​,有些 BSD 系统中也存在类似情况。两种 echo的区别在于是否扩展转义序列。shell 内建版本倾向于不扩展,而外部版本(如 /bin/echo 和 /usr/bin/echo)倾向 于扩展;但还是那句话,不同的系统之间会有变化。

典型的 Linux 系统(/bin/bash)如下所示。

bash 复制代码
$ type -a echo
echo is a shell builtin
echo is /bin/echo

$ builtin echo "one\ttwo\nthree"
one\ttwo\nthree\n

$ /bin/echo "one\ttwo\nthree"
one\ttwo\nthree\n

$ echo -e "one\ttwo\nthree"
one → two
three

$ /bin/echo -e "one\ttwo\nthree"
one → two
three

$ shopt -s xpg_echo

$ builtin echo "one\ttwo\nthree"
one → two
three

$ shopt -u xpg_echo

$ builtin echo "one\ttwo\nthree"
one\ttwo\nthree\n

典型的 BSD 系统(/bin/csh,然后是 /bin/sh)如下所示。

bash 复制代码
$ which echo
echo: shell builtin command.

$ echo "one\ttwo\nthree"
one\ttwo\nthree\n

$ /bin/echo "one\ttwo\nthree"
one\ttwo\nthree\n

$ echo -e "one\ttwo\nthree"
-e one\ttwo\nthree\n

$ /bin/echo -e "one\ttwo\nthree"
-e one\ttwo\nthree\n

$ printf "%b" "one\ttwo\nthree"
one → two

$ /bin/sh

$ echo "one\ttwo\nthree"
one\ttwo\nthree\n

$ echo -e "one\ttwo\nthree"
one → two
three

$ printf "%b" "one\ttwo\nthree"
one → two
three

Solaris 10(/bin/sh)系统如下所示。

bash 复制代码
$ which echo
/usr/bin/echo

$ type echo
echo is a shell builtin

$ echo "one\ttwo\nthree"
one → two
three

$ echo -e "one\ttwo\nthree"
-e one → two
three

$ printf "%b" "one\ttwo\nthree"
one → two
three

7、仅在必要时分割输出

你希望仅在输入超出限制时才分割输出,但 split 命令总是会创建至少一个新文件。

下例演示了一种方法,该方法可以仅在输入超出限制时才将其分割为固定大小的输出。

bash 复制代码
# 实例文件:func_split
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# 仅在超出限制时才将输入分割成固定大小的输出
# 调用方式:Split <file> <prefix> <limit option> <limit argument>
# 例如:Split $output ${output}_ --lines 100
# 选项细节参见split(1) 和wc(1)
function Split {
    local file=$1
    local prefix=$2
    local limit_type=$3
    local limit_size=$4
    local wc_option

    # 完备性检查
    if [ -z "$file" ]; then
        printf "%b" "Split: requires a file name!\n"
        return 1
    fi
    if [ -z "$prefix" ]; then
        printf "%b" "Split: requires an output file prefix!\n"
        return 1
    fi
    if [ -z "$limit_type" ]; then
        printf "%b" \
          "Split: requires a limit option (e.g. --lines), see 'man split'!\n"
        return 1
    fi
    if [ -z "$limit_size" ]; then
        printf "%b" "Split: requires a limit size (e.g. 100), see 'man split'!\n"
        return 1
    fi
    # 将split的选项转换成wc的选项
    # 并非所有系统上的wc/split都能够支持全部的选项
    case $limit_type in
        -b|--bytes)      wc_option='-c';;
        -C|--line-bytes) wc_option='-L';;
        -l|--lines)      wc_option='-l';;
    esac

    # 如果超出了限制
    if [ "$(wc $wc_option $file | awk '{print $1}')" -gt $limit_size ]; then
        # 进行分割
        split --verbose $limit_type $limit_size $file $prefix
    fi
} # Split函数定义完毕

根据使用的系统,某些选项(如 -C)可能不适用于 split或 wc。

8、以十六进制形式查看输出

你需要以十六进制形式查看输出,以核实某些空白字符或不可打印字符是否符合预期。

使用 hexdump 的 -C 选项获得规范的十六进制输出。

bash 复制代码
$ hexdump -C filename
00000000  4c 69 6e 65 20 31 0a 4c 69 6e 65 20 32 0a 0a 4c |Line 1.Line 2..L|
00000010  69 6e 65 20 34 0a 4c 69 6e 65 20 35 0a 0a       |ine 4.Line 5..|
0000001e
$

例如,nl 在其输出中使用了空格(ASCII 20)​,然后是行号,接着是制表符(ASCII 09)​。

bash 复制代码
$ nl -ba filename | hexdump -C
00000000  20 20 20 20 20 31 09 4c  69 6e 65 20 31 0a 20 20 |     1.Line 1.  |
00000010  20 20 20 32 09 4c 69 6e  65 20 32 0a 20 20 20 20 |   2.Line 2.    |
00000020  20 33 09 0a 20 20 20 20  20 34 09 4c 69 6e 65 20 | 3..     4.Line |
00000030  34 0a 20 20 20 20 20 35  09 4c 69 6e 65 20 35 0a |4.     5.Line 5.|
00000040  20 20 20 20 20 36 09 0a                          |     6..|
00000048
$

hexdump 是一款 BSD 实用工具,很多 Linux 发行版中也能看到它的身影。其他系统(尤其是 Solaris)默认没有安装该工具。你可以使用八进制转储命令 od,但它一次只能输出一种格式,而且地址(左列)为八进制,而非十六进制。

bash 复制代码
$ nl -ba filename | od -x
0000000 2020 2020 3120 4c09 6e69 2065 0a31 2020
0000020 2020 3220 4c09 6e69 2065 0a32 2020 2020
0000040 3320 0a09 2020 2020 3420 4c09 6e69 2065
0000060 0a34 2020 2020 3520 4c09 6e69 2065 0a35
0000100 2020 2020 3620 0a09
0000110

$ nl -ba filename | od -tx1
0000000 20 20 20 20 20 31 09 4c 69 6e 65 20 31 0a 20 20
0000020 20 20 20 32 09 4c 69 6e 65 20 32 0a 20 20 20 20
0000040 20 33 09 0a 20 20 20 20 20 34 09 4c 69 6e 65 20
0000060 34 0a 20 20 20 20 20 35 09 4c 69 6e 65 20 35 0a
0000100 20 20 20 20 20 36 09 0a
0000110

$ nl -ba filename | od -c
0000000                       1  \t   L   i   n   e       1  \n
0000020               2  \t   L   i   n   e       2  \n
0000040       3  \t  \n                       4  \t   L   i   n   e
0000060   4  \n                       5  \t   L   i   n   e       5  \n
0000100                       6  \t  \n
0000110

另外还有一个简单的 Perl 脚本,没准也管用。

bash 复制代码
$ ./hexdump.pl filename

       /0 /1 /2 /3 /4 /5 /6 /7 /8 /9/ A /B /C /D /E /F  0123456789ABCDEF
0000 : 4C 69 6E 65 20 31 0A 4C 69 6E 65 20 32 0A 0A 4C  Line 1.Line 2..L
0010 : 69 6E 65 20 34 0A 4C 69 6E 65 20 35 0A 0A        ine 4.Line 5..

9、使用bash的网络重定向

你需要发送或接受一些非常简单的网络流量,但又没有安装 netcat 这类工具。

如果你的 bash 版本是 2.04+,编译时使用了 --enable-net-redirections 选项(默认)​,那么只用 bash 就行了。以下示例也可用于 15.10 节。

bash 复制代码
$ exec 3<> /dev/tcp/checkip.dyndns.org/80
$ echo -e "GET / HTTP/1.0\n" >&3
$ cat <&3
HTTP/1.1 200 OK
Content-Type: text/html
Server: DynDNS-CheckIP/1.0
Connection: close
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 105

<html><head><title>Current IP Check</title></head>
<body>Current IP Address: 72.NN.NN.225</body></html>

$ exec 3<> /dev/tcp/checkip.dyndns.org/80
$ echo -e "GET / HTTP/1.0\n" >&3
$ egrep --only-matching 'Current IP Address: [0-9.]+' <&3
Current IP Address: 72.NN.NN.225
$

Debian 及其衍生发行版(如 Ubuntu)在bash 4.0 之前都是用 --disable-net-redirections编译的,因此本实例不适用于这些版本。

如 15.12 节所述,可以使用 exec 永久地重定向当前 shell会话中的文件句柄,因此第一个命令将输入和输出设置在文件句柄 3 上。第二行向先前命令中已经定义好的 Web服务器路径发送了一个普通的 HTTP GET 方法。注意,用户代理在 Web 服务器端会显示为"-"​,这会导致产生"flagged User Agent"警告。第三个命令只是显示结果。

TCP 和 UDP 都没有问题。下面给出了一种简单的方法, 可以向远程服务器发送 syslog 消息(但在生产环境中,我们还是推荐选择实用工具 logger,它对用户友好得多,也更稳健)​。

bash 复制代码
echo "<133>${0##*/}[$$]: Test syslog message from bash" \
  > /dev/udp/loghost.example.com/514

因为 UDP 是无连接的,所以这个其实要比之前的 TCP 示例更易用。<133> 是根据 RFC 3164 计算得出的local0.notice 的 syslog优先级。具体细节可参见该 RFC文档的 4.1.1 节和 logger 的手册页。 0 是程序名称, 0 是程序名称, 0是程序名称,{0##*/} 则是"基本名称"​,$$ 是当前程序的进程 ID。对于登录 shell,名称就是 -bash。

10、查找自己的IP地址

你需要知道自己所用机器的 IP 地址。

不存在什么一劳永逸的好方法,我们得分情况考虑。

首先,可以解析 ifconfig 的输出,从中找出 IP 地址。下例中的命令要么返回第一个非环回 IP 地址,要么什么都不返回(如果没有配置或激活接口的话)​。

bash 复制代码
# 实例文件:finding_ipas

# 使用awk、cut、head解析IPv4地址
$ /sbin/ifconfig -a | awk '/(cast)/ { print $2 }' | cut -d':' -f2 | head -1

# 用Perl解析IPv4,图个乐子而已
$ /sbin/ifconfig -a | perl -ne 'if ( m/^\s*inet (?:addr:)?([\d.]+).*?cast/ )
> { print qq($1\n); exit 0; }'

# 用awk、cut、head解析IPv6地址
$ /sbin/ifconfig -a | egrep 'inet6 addr: |address: ' | cut -d':' -f2- \
    | cut -d'/' -f1 | head -1 | tr -d ' '

# 用Perl解析IPv6,图个乐子而已
$ /sbin/ifconfig -a | perl -ne 'if
> ( m/^\s*(?:inet6)? \s*addr(?:ess)?: ([0-9A-Fa-f:]+)/ ) { print qq($1\n);
> exit 0; }'

其次,可以获得主机名,然后将其反向解析为 IP 地址。这种方法往往靠不住,因为现今系统(尤其是工作站)的主机名可能不完整或不正确,也可能位于动态网络,无法完成正确的反向解析。因此存在一定风险,需要经过充分测试。

bash 复制代码
host $(hostname)

如果前面提到的程序你一个都没有,但使用的是 2.04+ 版本的 bash,而且编译时使用了选项 --enable-net-redirections(在 Debian 及其衍生发行版中,bash 4.0 之前的版本并未使用该编译选项)​,你也可以依靠 bash。

bash 复制代码
$ exec 3<> /dev/tcp/checkip.dyndns.org/80
$ echo -e "GET / HTTP/1.0\n" >&3
$ cat <&3
HTTP/1.1 200 OK
Content-Type: text/html
Server: DynDNS-CheckIP/1.0
Connection: close
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 105

<html><head><title>Current IP Check</title></head>
<body>Current IP Address: 96.245.41.129</body></html>

$ exec 3<> /dev/tcp/checkip.dyndns.org/80
$ echo -e "GET / HTTP/1.0\n" >&3
$ egrep --only-matching 'Current IP Address: [0-9.]+' <&3
Current IP Address: 72.NN.NN.225
$

第一个解决方案中的 awk 和 Perl 代码很有意思,因为我们在这里会注意到操作系统的差异。但结果发现,我们感兴趣的那些行全都包含 Bcast 或 broadcast(要么是inet6addr:or address:)​,只要得到这些行,剩下的事就是从中解析出需要的字段了。当然,由于 Linux 采用了不同的格式,这增加了难度,但我们一样能搞定。

并非所有系统使用 ifconfig 时都要求指定路径(如果你不是 root)或 -a 选项,但指定绝对没错,因此最好还是使用 /sbin/ifconfig -a。

以下是一些出自不同机器的 ifconfig 的输出示例。

bash 复制代码
# Linux
$ /sbin/ifconfig
eth0      Link encap:Ethernet HWaddr 00:C0:9F:0B:8F:F6
          inet addr:192.168.99.11 Bcast:192.168.99.255 Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
          RX packets:33073511 errors:0 dropped:0 overruns:0 frame:827
          TX packets:52865023 errors:0 dropped:0 overruns:1 carrier:7
          collisions:12922745 txqueuelen:100
          RX bytes:2224430163 (2121.3 Mb) TX bytes:51266497 (48.8 Mb)
          Interrupt:11 Base address:0xd000

lo        Link encap:Local Loopback
          inet addr:127.0.0.1 Mask:255.0.0.0
          UP LOOPBACK RUNNING MTU:16436 Metric:1
          RX packets:659102 errors:0 dropped:0 overruns:0 frame:0
          TX packets:659102 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:89603190 (85.4 Mb) TX bytes:89603190 (85.4 Mb)

$ /sbin/ifconfig
eth0      Link encap:Ethernet HWaddr 00:06:29:33:4D:42
          inet addr:192.168.99.144 Bcast:192.168.99.255 Mask:255.255.255.0
          inet6 addr: fe80::206:29ff:fe33:4d42/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
          RX packets:1246774 errors:14 dropped:0 overruns:0 frame:14
          TX packets:1063160 errors:0 dropped:0 overruns:0 carrier:5
          collisions:65476 txqueuelen:1000
          RX bytes:731714472 (697.8 MiB) TX bytes:942695735 (899.0 MiB)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1 Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING MTU:16436 Metric:1
          RX packets:144664 errors:0 dropped:0 overruns:0 frame:0
          TX packets:144664 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:152181602 (145.1 MiB) TX bytes:152181602 (145.1 MiB)

sit0      Link encap:IPv6-in-IPv4
          inet6 addr: ::127.0.0.1/96 Scope:Unknown
          UP RUNNING NOARP MTU:1480 Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:101910 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)

# NetBSD
$ /sbin/ifconfig -a
pcn0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
        address: 00:0c:29:31:eb:19
        media: Ethernet autoselect (autoselect)
        inet 192.168.99.56 netmask 0xffffff00 broadcast 192.168.99.255
        inet6 fe80::20c:29ff:fe31:eb19%pcn0 prefixlen 64 scopeid 0x1
lo0: flags=8009<UP,LOOPBACK,MULTICAST> mtu 33196
        inet 127.0.0.1 netmask 0xff000000
        inet6 ::1 prefixlen 128
        inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
ppp0: flags=8010<POINTOPOINT,MULTICAST> mtu 1500
ppp1: flags=8010<POINTOPOINT,MULTICAST> mtu 1500
sl0: flags=c010<POINTOPOINT,LINK2,MULTICAST> mtu 296
sl1: flags=c010<POINTOPOINT,LINK2,MULTICAST> mtu 296
strip0: flags=0 mtu 1100
strip1: flags=0 mtu 1100

# OpenBSD, FreeBSD
$ /sbin/ifconfig
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 33224
        inet 127.0.0.1 netmask 0xff000000
        inet6 ::1 prefixlen 128
        inet6 fe80::1%lo0 prefixlen 64 scopeid 0x5
le1: flags=8863<UP,BROADCAST,NOTRAILERS,RUNNING,SIMPLEX,MULTICAST> mtu 1500
        address: 00:0c:29:25:df:00
        inet6 fe80::20c:29ff:fe25:df00%le1 prefixlen 64 scopeid 0x1
        inet 192.168.99.193 netmask 0xffffff00 broadcast 192.168.99.255
pflog0: flags=0<> mtu 33224
pfsync0: flags=0<> mtu 2020

# Solaris
$ /sbin/ifconfig -a
lo0: flags=1000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4> mtu 8232 index 1
        inet 127.0.0.1 netmask ff000000
pcn0: flags=1004843<UP,BROADCAST,RUNNING,MULTICAST,DHCP,IPv4> mtu 1500 index 2
        inet 192.168.99.159 netmask ffffff00 broadcast 192.168.99.255

# Mac
$ /sbin/ifconfig
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
        inet 127.0.0.1 netmask 0xff000000
        inet6 ::1 prefixlen 128
        inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280
stf0: flags=0<> mtu 1280

en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
        inet6 fe80::20d:93ff:fe65:f720%en0 prefixlen 64 scopeid 0x4
        inet 192.168.99.155 netmask 0xffffff00 broadcast 192.168.99.255
        ether 00:0d:93:65:f7:20
        media: autoselect (100baseTX <half-duplex>) status: active
        supported media: none autoselect 10baseT/UTP <half-duplex> 10baseT/UTP <fullduplex>
10baseT/UTP <full-duplex,hw-loopback> 100baseTX <half-duplex> 100baseTX
<full-duplex> 100baseTX <full-duplex,hw-loopback>
fw0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 2030
    lladdr 00:0d:93:ff:fe:65:f7:20
        media: autoselect <full-duplex> status: inactive
        supported media: autoselect <full-duplex>
相关推荐
深圳光特通信豆子2 小时前
1×9光模块DDM数字诊断监控技术手册
网络·物联网
Huangjin007_2 小时前
【Linux 系统篇(二十一)】进程(九):进程等待 wait/waitpid、status状态参数
linux·运维·服务器
云边云科技_云网融合2 小时前
从一盘散棋到一张蓝图:某连锁集团的云上重构
网络·科技·云计算
螺蛳粉 螺蛳粉2 小时前
mysql的备份与恢复
linux·运维·数据库·mysql
ao-weilai2 小时前
Linux网络编程:Socket UDP
linux·服务器·网络·c++
可乐鸡翅yeah_3 小时前
混合内容 Mixed‑Content 安全策略,HLS HTTPS 页面加载 HTTP 资源排错实战
运维·ffmpeg·音视频·媒体·m3u8
Quanqiucard3 小时前
监控用物联网卡如何优化管理,降低安防项目通信成本
网络·物联网
爱吃香菜的初学者3 小时前
九.Linux——文件操作
linux
园长的牧歌3 小时前
Ubuntu 打开应用在导航栏没有图标是个齿轮
linux·ubuntu