Linux 常用命令行

以下所介绍的部分命令,在设备上是由适合嵌入式 Linux 的工具集 BusyBox 提供,行为和 Linux 桌面发行版上不完全一致,这里均采用 BusyBox 所提供的指令用法。

ifconfig:查看接口状态

ifconfig 可设置网卡接口的状态,或是显示目前的设置。以下是一些主要的设置参数。

语法格式:ifconfig < -a > [网卡名称] <参数>

参数 作用
add <地址> 设置网卡的IPv6地址。
del <地址> 删除网卡的IPv6地址。
up 启动指定的网卡。
down 关闭指定的网卡。
metric <数目> 指定在计算数据包的转送次数时,所要加上的数目。
mtu <字节> 设置网卡的MTU(最大数据包大小)。
netmask <子网掩码> 设置网卡的子网掩码。
-broadcast <地址> 将要送往指定地址的数据包当成广播数据包来处理。
[IP地址] 指定网卡的IP地址。

直接输入 ifconfig 就会列出目前已激活的网卡,不论这个卡是否有给予 IP ,都会被显示出来。带接口的 ifconfig eth0 则仅会表示接口 eth0 的相关数据,无论其是否启动。使用 -a 选项,则会展示所有网卡的属性。

示例

1
2
3
4
5
6
7
8
$ ifconfig ra8
ra8 Link encap:Ethernet HWaddr 02:A1:BB:01:23:48
inet6 addr: fe80::a1:bbff:fe01:2348/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:130 errors:0 dropped:0 overruns:0 frame:0
TX packets:61 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:13971 (13.6 KiB) TX bytes:7767 (7.5 KiB)

展示的各项数据由上而下、由左而右分别代表:

  1. ra8:网卡的代号,也有 lo 这个 loopback 代表本地回环;
  2. HWaddr:网卡的硬件地址,即 MAC ;
  3. inet addr:IPv4 地址,后面的 Bcast,Mask 分别代表的是广播地址 broadcast 与子网掩码 netmask ,这里没有体现;
  4. inet6 addr:IPv6 地址,Scope 代表该地址的作用域;
  5. 网络状态:UP 代表网卡已开启;BROADCAST 代表支持广播;RUNNING 代表网卡正在运行中;MULTICAST代表支持组播;
  6. MTU:最大传输单元,单位是字节;
  7. Metric:路由算法的度量值;
  8. RX:代表网络由启动到目前为止的数据包接收情况,packets 代表数据包数、errors 代表数据包发生错误的数量、dropped 代表由于有问题而遭丢弃的数据包数量等等;
  9. TX:与 RX 相反,代表网络由启动到目前为止的传送情况;
  10. collisions:代表数据包碰撞的情况,如果发生太多次,表示网络状况不太好;
  11. txqueuelen:代表用来传输资料的缓冲区的储存长度;
  12. RX bytes/TX bytes:总接收、传送的字节总量。

通过观察这些数据,可以大致了解你的网络情况。尤其是 RX , TX 内的 error 数量,以及是否发生严重的 collision 情况,都是值得注意的。

brctl:查看网桥状态

brctl 命令用于设置、维护和检查 Linux 内核中的以太网网桥配置。

以太网网桥是一种设备,通常用于将以太网的不同网络连接在一起,以便这些以太网对参与者显示为一个以太网。所连接的每个以太网对应于网桥中的一个物理接口。这些单独的以太网被聚集成一个更大的“逻辑”以太网,这个更大的以太网对应于网桥网络接口。

语法格式:brctl [参数] <网桥名>

参数 作用
addbr 创建网桥
delbr 删除网桥
addif 将网卡接口接入网桥
delif 删除网桥接入的网卡接口
show 查询网桥信息
stp {on|off} 启用禁用 STP
showstp 查看网桥 STP 信息
setfd 设置网桥延迟
showmacs 查看 mac 信息

示例

1
2
3
4
5
6
7
8
9
10
# 使用 addbr 参数创建网桥:
$ brctl addbr br0
# 使用 addif 参数将接口加入网桥:
$ brctl addif br0 wl1
# 使用 show 参数查询网桥信息:
$ brctl show
# 启用网桥 STP :
$ brctl stp br0 on
# 关闭网桥 STP :
$ brctl stp br0 off

无线驱动较少关注网桥,但当出现 WiFi 无法连接的情况时,有可能是网桥出现了问题,比如对应的无线接口没有被加入到网桥中。此时可以通过 brctl show 指令查看网桥状态进行确认。

ps:查看进程状态

1
2
3
4
5
6
7
8
9
10
11
$ ps --help
BusyBox v1.30.1 (2021-06-11 10:17:09 CST) multi-call binary.

Usage: ps [-o COL1,COL2=HEADER] [-T]

Show list of processes

-o COL1,COL2=HEADER Select columns for display
-T Show threads
$ ps -o help
ps: bad -o argument 'help', supported arguments: user,group,comm,args,pid,ppid,pgid,etime,nice,rgroup,ruser,time,tty,vsz,sid,stat,rss

直接执行 ps,将打印正在执行的进程列表。

对于每个进程,默认显示这四列属性:

  1. PID:进程的PID
  2. USER:进程的所有者用户ID
  3. TIME:进程消耗的CPU时间总和
  4. COMMAND:进程由何种命令启动,包括所用的参数(args)

如果想自定义 ps 显示的列,可以使用 -o 参数。提示中所述的可选项目有很多,其中比较有用的除了默认的四项外,还有:

  1. stat:用不同的字符表示进程的状态。如 R 为可运行状态( Runnable ,正在运行或准备运行), S 为睡眠状态( Sleep ,等待某事件发生后继续运行), D 为不可中断状态( Disk Sleep ,等待 I/O 操作)等等
  2. vsz:进程分配的的虚拟内存,即进程可以访问的所有内存
  3. rss:进程的常驻内存集,即进程的物理内存占用,其中包括进程所使用的共享库占用的内存
  4. ppid:父进程的 PID
  5. etime: e 代表 Elapsed (经过), etime 是进程的挂钟时间 (wall-clock-time) ,即进程启动后经过的时间

示例

grep -v "\[.*\]" 是为了排除掉由内核创建的进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
$ ps -o pid,stat,time,rss,args | grep -v "\[.*\]"
PID STAT TIME RSS COMMAND
1 S 0:04 1392 init
302 S < 0:00 1180 /sbin/udevd -d
305 S 0:08 1256 tcwdog -t 1 /dev/watchdog
504 S 0:00 1092 /fhrom/bin/ubusd
509 S 0:04 2132 /usr/sbin/dbus-daemon --system
521 S 0:00 1780 -ash
528 S 0:00 1392 /fhrom/bin/sysproxy
529 S 3:59 3676 sysmgr
537 S 0:03 2164 thttpd -C /fhrom/fhconf/thttpd.conf -p 8080
546 S 0:00 1280 /userfs/bin/ponmgr_cfg
563 S 2:23 3964 cfgmgr

top:动态查看进程状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ top --help
BusyBox v1.30.1 (2021-06-11 10:17:09 CST) multi-call binary.

Usage: top [-b] [-nCOUNT] [-dSECONDS] [-m]

Provide a view of process activity in real time.
Read the status of all processes from /proc each SECONDS
and display a screenful of them.
Keys:
N/M/P/T: show CPU usage, sort by pid/mem/cpu/time
S: show memory
R: reverse sort
H: toggle threads, 1: toggle SMP
Q,^C: exit

Options:
-b Batch mode
-n N Exit after N iterations
-d N Delay between updates
-m Same as 's' key

top 的功能和 ps 差不多,但只要不手动退出,它就会自动刷新进程的占用情况(默认间隔5s)。

如 help 提示,在 top 界面中,可以用按键切换不同的排序和显示数据。需要注意 VSZ 和 RSS 的区别,进程的物理内存占用 RSS 只有按 S 后才会显示出来。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Mem: 255692K used, 181472K free, 180K shrd, 17092K buff, 65480K cached
CPU: 2.5% usr 4.7% sys 0.0% nic 91.6% idle 0.0% io 0.0% irq 1.1% sirq
Load average: 2.02 2.18 2.25 1/258 21037
PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND
249 2 root DW 0 0.0 0 1.3 [LDDLA_task_wait]
2986 2 root SW 0 0.0 0 0.5 [RtmpMlmeTask]
1475 1 root S 52008 11.8 0 0.4 wifimgr
627 1 root S 36936 8.4 1 0.4 lancc
1661 1 root S 52108 11.8 1 0.2 processcpumonitor
529 1 root S 35892 8.1 0 0.2 sysmgr
21001 521 root R 3236 0.7 1 0.2 top
1479 1 root S < 88236 20.1 1 0.1 /fhrom/bin/gdecms
1676 1 root S 67144 15.3 1 0.1 /fhrom/bin/dbuscheck
1477 1 root S 61804 14.1 1 0.1 easymeshagent
716 1 ctuser S 60344 13.7 1 0.1 /fhrom/bin/udhcpd /var/run/udhcpd.
596 1 ctuser S 58016 13.2 1 0.1 tr069 -F /fhconf/ -M 1 -L 5 -S 200
2847 2051 root S 51564 11.7 1 0.1 /sbin/easymesh
2830 2051 root S 41672 9.5 1 0.1 /usr/sbin/vpnclient
705 1 root S 37196 8.4 1 0.1 serviceMgr
2870 2051 root S 33504 7.6 0 0.1 /usr/sbin/dsmonitor
590 1 root S 26504 6.0 0 0.1 eventmgr 23
643 1 root S 26324 6.0 1 0.1 /fhrom/bin/fhomci -m -o /var/fhomc
602 1 root S 25496 5.8 0 0.1 fh_bsp_led_act
599 1 root S 25284 5.7 1 0.1 detectHwEvent

uptime:查看运行时间和平均负载

uptime 的功能非常简单,它打印出一行信息,包括:

  1. 当前时间
  2. 系统已运行时间
  3. 用户连接数,是总连接数而不是用户数
  4. 过去1/5/15分钟的平均负载

平均负载指的是处于可运行状态和不可中断状态,即 pstop 中状态为 R 或 D 的平均进程数。

当平均负载与机器的逻辑核心数基本相等时,说明每个核心上都在运行程序,性能被充分发挥。如果平均负载明显高于逻辑核心数,则说明机器的性能已经不够了。

示例

1
2
3
4
5
6
7
8
9
10
# 一台服务器的 uptime 和逻辑核心数
$ uptime
09:15:06 up 74 days, 12:55, 22 users, load average: 0.48, 1.27, 1.58
$ cat /proc/cpuinfo |grep "model name"|wc -l
56
# 一台路由器的 uptime 和逻辑核心数
$ uptime
08:51:02 up 51 min, 1 users, load average: 2.16, 2.24, 2.19
$ cat /proc/cpuinfo |grep "model name"|wc -l
2

tcpdump:抓包工具

tcpdump 是一个强大且复杂的本地抓包工具,限于篇幅,这里仅作简单介绍。

1
2
3
4
5
6
7
8
9
10
11
$ tcpdump -h
tcpdump version 4.9.2
libpcap version 1.8.1
Usage: tcpdump [-aAbdDefhHIJKlLnNOpqStuUvxX#] [ -B size ] [ -c count ]
[ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ]
[ -i interface ] [ -j tstamptype ] [ -M secret ] [ --number ]
[ -Q in|out|inout ]
[ -r file ] [ -s snaplen ] [ --time-stamp-precision precision ]
[ --immediate-mode ] [ -T type ] [ --version ] [ -V file ]
[ -w file ] [ -W filecount ] [ -y datalinktype ] [ -z postrotate-command ]
[ -Z user ] [ expression ]

抓包选项

-c:指定要抓取的包数量。注意,是最终要获取这么多个包。例如,指定 -c 10 将获取10个包,但可能已经处理了100个包,只不过只有10个包是满足条件的包。

-i interface:指定监听接口。若未指定该选项,将从系统接口列表中搜寻编号最小的已配置好的接口(不包括 loopback 接口,要抓取 loopback 接口使用 tcpdump -i lo),一旦找到第一个符合条件的接口,搜寻马上结束。可以使用 any 关键字表示所有网络接口。

-n:对地址以数字方式显式,否则显式为主机名,也就是说 -n 选项不做主机名解析。

-nn:除了 -n 的作用外,还把端口显示为数值,否则显示端口服务名。

-Q:指定要抓取的包是流入还是流出的包。可以给定的值为 inoutinout,默认为 inout

-s len:设置 tcpdump 的数据包抓取长度为len,如果不设置默认将会是65535字节。对于要抓取的数据包较大时长度设置不够可能会产生包截断,若出现包截断,输出行中会出现 [|proto] 的标志(proto 实际会显示为协议名)。但是抓取len越长,包的处理时间越长,并且会减少 tcpdump 可缓存的数据包的数量,从而会导致数据包的丢失,所以在能抓取我们想要的包的前提下,抓取长度越小越好。

输出选项

-e:输出的每行中都将包括数据链路层头部信息,例如源 MAC 和目标 MAC 。

-q:快速打印输出。即打印很少的协议相关信息,从而输出行都比较简短。

-X:输出包的头部数据,会以16进制和 ASCII 两种方式同时输出。

-XX:输出包的头部数据,会以16进制和 ASCII 两种方式同时输出,更详细。

-v, -vv, -vvv:当分析和打印的时候,产生不同详细程度的输出。

其他功能性选项

-D:列出可用于抓包的接口。将会列出接口的数值编号和接口名,它们都可以用于 -i 后。

-F:从文件中读取抓包的表达式。若使用该选项,则命令行中给定的其他表达式都将失效。

-w:将抓包数据输出到文件中而不是标准输出。可以同时配合 -G time 选项使得输出文件每time秒就自动切换到另一个文件。可通过 -r 选项载入这些文件以进行分析和打印。

-r:从给定的数据包文件中读取数据。使用 - 表示从标准输入中读取。

过滤器

在选项之后,可以使用多种过滤器组成的表达式来指定要抓取怎样的包,一些常用的过滤方式如下。

根据主机过滤:

1
2
3
4
5
6
# 抓取所有经过 eth1 ,目的或源地址是192.168.1.1的网络数据
$ tcpdump -i eth1 host 192.168.1.1
# 指定源地址
$ tcpdump -i eth1 src host 192.168.1.1
# 指定目的地址
$ tcpdump -i eth1 dst host 192.168.1.1

根据端口过滤:

1
2
3
4
5
6
# 抓取所有经过 eth1 ,目的或源端口是25的网络数据
$ tcpdump -i eth1 port 25
# 指定源端口
$ tcpdump -i eth1 src port 25
# 指定目的端口
$ tcpdump -i eth1 dst port 25

根据协议过滤:

1
2
3
4
5
# 比较严谨的方式是根据协议号来过滤。
# 根据IP协议号过滤出 ICMP 报文(在 /etc/protocols 文件中有注明 IP 协议号)
$ tcpdump -i ra0 ip proto 1 -w /var/icmp.pcap
# 根据以太类型 EtherType 过滤出 IEEE1905.1 报文(0x893a)
$ tcpdump -i ra0 ether proto 0x893a -w /var/1905.pcap

分析 tcpdump 的抓包结果

不建议使用 tcpdump 本身的打印来进行分析。

tcpdump 使用 -w 选项输出的 .pcap 类型文件可以用 Wireshark 或 OmniPeek 打开,进行详尽的分析。

分析 tcpdump 抓到的报文

tftp:简单文件传输协议

1
2
3
4
5
6
7
8
9
10
11
12
$ tftp --help
BusyBox v1.26.2 (2021-08-06 09:48:45 CST) multi-call binary.

Usage: tftp [OPTIONS] HOST [PORT]

Transfer a file from/to tftp server

-l FILE Local FILE
-r FILE Remote FILE
-g Get file
-p Put file
-b SIZE Transfer blocks of SIZE octets

TFTP (Trivial File Transferring Protocol) 用于进行简单的文件传输,由于占用很小,比较适合在电脑与嵌入式设备之间传输文件。

在 Windows 上,可以通过 tftpd64 软件使用 tftp 。

tftpd64

将 tftpd64 设置为 Tftp Server 模式,配置工作目录(需要配置一个具有读写权限的目录),即可在对端设备上进行文件传输操作。

1
2
3
4
# 将本地文件 lancc.log 上传到192.168.1.11的 tftp 工作目录处
$ tftp -pl lancc.log 192.168.1.11
# 从192.168.1.11的 tftp 工作目录处下载文件 gdb
$ tftp -gr gdb 192.168.1.11

chmod:更改文件权限

chmodCHange MODe 的缩写。使用 tftp 等方法将可执行文件、 Shell 脚本等传输到单板上时,这些文件可能不具备执行权限,此时就需要用 chmod 命令进行调整。

在Linux中使用 ls -l 以长格式列出文件时,能看到每行最前面有10个用于表示文件模式(权限)的字符:

1
2
3
4
5
lrwxrwxrwx    1 root     root             7 Jun 28  2021 cat -> busybox
drwxr-xr-x 2 root root 0 Jan 1 08:00 mnt
prwxr-xr-x 1 ctuser ctuser 0 Jan 1 08:00 notify_fifo
-rw-r--r-- 1 root root 7 Jan 2 08:29 ntp_failed
-rwxr-xr-x 1 root root 322 Jan 1 08:00 passwd

其中,第1个字符表示文件类型:

属性 文件类型
- 普通文件
d 目录文件
l 符号链接,其所指向的文件的属性才是真实的属性
c 字符设备文件
b 块设备文件
s 套接字文件
p 管道文件

后面的9个字符被称为文件属性,第2-4,5-7,8-10个字符分别代表文件的所有者、所属群组、其他所有用户对文件的操作权限。

属性 权限
r 读权限Read
w 写权限Write
x 执行权限eXecute(执行脚本,进入目录)

如果用4代表r,2代表w,1代表x,0代表无权限,则可以用一个八进制数来表示一种权限。3个这样的八进制数,就能代表9个字符的文件属性,然后用 chmod 进行修改。如:

1
2
3
4
5
6
# 修改文件权限为rwxr-xr--
$ chmod 754 <filename>
# 另一种用法是符号表示法,下面这条表示为所有用户添加执行权限
$ chmod +x <filename>
# 为文件所有者和所属群组之外的所有用户取消读写权限
$ chmod o-rw <filename>

xargs:批量执行命令

xargs 命令可以将标准输入转为命令行参数。真正要执行的命令紧跟在 xargs 后面,接受其传来的参数。

语法格式:xargs [-options] [command]

1
2
3
# 将管道左侧的标准输入,转为命令行参数,传给xargs后面的echo命令
$ echo "hello world" | xargs echo
hello world

使用 xargs 的必要性在于,许多命令(比如 rmmkdirls)要与管道一起使用时,都需要通过它将标准输入转为命令行参数。下面这个管道的使用例中,如果不加 xargs 就会报错,提示 mkdir 缺少操作参数:

1
2
3
$ echo "one two three" | xargs mkdir
# 与这条命令等效:
$ mkdir one two three

xargs 适合与 find 命令组合使用。先用 find 找出符合一定条件的条件,再用 xargs 对这些文件进行处理。

由于 xargs 默认将空格作为分隔符,这不适合直接处理文件名,因为文件名可能包含空格。但 find 命令有一个特别的参数 -print0 ,可以指定输出的文件列表以 null 分隔。同时,用 xargs 命令的 -0 参数可以表示将 null 当作分隔符,这样就能形成很好的配合。

1
2
# 删除 /tmp 路径下的所有通常文件
$ find /tmp -type f -print0 | xargs -0 rm

还有一个原因,使得 xargs 特别适合 find 命令。有些命令(比如 rm)一旦参数过多会报错“参数列表过长”,而无法执行,改用 xargs 就没有这个问题,因为它会对每个参数执行一次命令。

1
2
# 找出所有 txt 文件后,对每个文件搜索其中任意大小写形式的 wifi
$ find . -name "*.txt" | xargs grep -i "wifi"

-I 选项用于指定一个替换字符串(比如{}),这个字符串在 xargs 扩展时会被替换掉,当 -Ixargs 结合使用,每一个参数命令都会被执行一次:

1
2
# 将当前目录下所有 log 文件通过 tftp 传输到192.168.1.11
$ ls *.log | xargs -I {} tftp -pl {} 192.168.1.11

history:查看历史命令

history 用于查看当前用户的历史执行命令。

1
2
3
4
5
6
7
8
9
10
11
12
$ history
(…)
2004 cd DELAY/
2005 ls
2006 cd compile/
2007 ls
2008 cd en752x_ty4
2009 ls
2010 make clean_sdk
2011 nohup make build_sdk &
2012 tail -f nohup.out
2013 history

tail:输出文件结尾

tail 默认输出文件的最后10行。配合选项 -f,即可随文件增长即时输出新增数据。这使得它很适合用来监测日志。

1
2
# 在后台监测wifi_drv.log的新增内容
$ tail -f /var/log/wifi_drv.log &

nohup:退出终端后保持命令运行

nohupNO Hang UP,用于在系统后台不挂断地运行命令,退出终端不会影响程序的运行。适合在编译代码时使用,这样开始编译后即使本地断开连接甚至关机,服务器上也会继续进行编译任务。

nohup 命令在默认情况下(非重定向时),会输出一个名叫 nohup.out 的文件到当前目录下,如果当前目录的 nohup.out 文件不可写,则将输出重定向到 $HOME/nohup.out 文件中。

1
2
3
4
# 可以在编译大量代码时使用
$ nohup ./wrap_build.sh &
# 如果想实时看到编译的输出,可以用 tail
$ tail -f nohup.out

gdb:GNU 调试器

gdb的功能非常强大,堪称让开发者对运行中的程序拥有了上帝视角。它的用法很多,这里简单介绍如何用 GDB 排查程序异常退出的问题。

如果在编译代码时加入-g编译选项,且没有用 -s-S 等选项除去调试符号,则生成的程序可以使用 GDB 进行调试。若编译时设置优化等级为 -O0 不开启优化,还可以用 GDB 的 s (step) 指令进行单步调试。

1
2
3
4
5
$ gcc -g hello.c -o hello
$ gdb hello
(…)
Reading symbols from hello...done.
(gdb)

如果需要调试指定的进程下的子线程,可以使用 ps -T 查看子线程的信息,编码时可以给每个线程设置名称,这样在ps -T 时就可以对线程进行区分。

1
2
# 用 -p 选项指定 pid,调试 hello 进程
$ gdb -p `pidof hello`

GDB 接管成功后,执行 r (run) 命令,或者 c (continue) 命令,接着正常执行导致问题进程异常退出的用例即可,GDB 接管后不会影响进程的正常运行。

一旦接管的进程发生异常退出问题,GDB 就会收到到调试信号将进程停住。这时候只需要用 bt (backtrace) 命令查看调用栈、用 p (print) 命令将函数中的变量打印出来等方法即可对问题点进行分析。

接管进程成功后也可以通过 b (break) 命令设置断点进行调试,触发条件满足即可将进程暂停,查看、修改函数的入参和局部变量,进行动态调试。

另外,假如有程序异常终止,系统一般会报出 “core dumped”,并生成文件名以 core 开头的核心转储文件。这种文件保存了程序退出时进程地址空间的内容以及有关进程状态的其他信息,可以用 GDB 配合程序进行调试。

例如下图中对 MTK 方案 SDK 中主管 Band Steering 的程序 bs20 进行调试:

GDB查看退出原因

bt 指令查看函数调用栈,可以得知程序退出是在 mapfilter_init 函数中调用了 memcpy 导致的。虽然由于编译时没有加 -g 选项,这里不会显示出具体是代码中的哪一行,但基本上已经能定位到直接问题点了。

Shell 脚本调试

Shell 提供了一些用于调试脚本的选项,如下所示:

选项 作用
-n 读一遍脚本中的命令但不执行,用于检查脚本中的语法错误
-v 一边执行脚本,一边将执行过的脚本命令打印到标准错误输出
-x 提供跟踪执行信息,将执行的每一条命令和结果依次打印出来

使用这些选项有三种方法,一是执行脚本时在命令行提供参数:

1
$ sh -x ./script.sh

二是在脚本开头用 Shebang (#!) 提供参数:

1
#! /bin/sh -x

第三种方法是在脚本中用 set 命令启用或禁用参数:

1
2
3
4
5
6
7
8
9
#! /bin/sh
(…)
if [ -z "$1" ]; then
set -x
echo "ERROR: Insufficient Args."
exit 1
set +x
fi
(…)

这里 set -xset +x 分别表示启用和禁用 -x 参数,这样可以只对脚本中的某一段进行跟踪调试。

更多实用内容可参考《Linux命令行大全》(人民邮电出版社)。