printf

格式化并输出结果

1
2
printf FORMAT [ARGUMENT]...
printf OPTION

选项

1
-v var:将结果输出到变量var中而不是输出到标准输出。

参数

format:输出格式。

arguments:一到多个参数。

1
2
3
4
5
转义序列:除了支持printf(1)和printf(3)的转义序列,内建printf还支持以下转义序列:

%b 展开参数中的反斜杠转义字符。
%q 将参数扩起以用作shell输入。
%(fmt)T 根据strftime(3)中的转义字符来输出日期时间字符串。

返回值

返回状态为成功除非给出了非法选项、写错误、赋值错误。

例子

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
# %-5s 格式为左对齐且宽度为5的字符串代替('-'表示左对齐),不使用则默认右对齐。
# %-4.2f 格式为左对齐宽度为4,保留两位小数。

printf "%-5s %-10s %-4s\n" NO Name Mark
printf "%-5s %-10s %-4.2f\n" 01 Tom 90.3456
printf "%-5s %-10s %-4.2f\n" 02 Jack 89.2345
printf "%-5s %-10s %-4.2f\n" 03 Jeff 98.4323

# 输出
NO Name Mark
01 Tom 90.35
02 Jack 89.23
03 Jeff 98.43


# %b %q %(fmt)T 的例子。
# see it again with a newline.
printf "%s\n" 'hello world'
# 展开换行符,和上面的结果一样。
printf "%b" 'hello world\n'

printf '%q\n' 'a b c'
# 输出
a\ b\ c

# %z为时区,%n为换行符。
printf "%(%F %T %z%n)T"
# 输出
2019-09-10 01:48:07 +0000

注意

  1. 该命令是 bash 内建命令,相关的帮助信息请查看help命令。

外部命令

概要

1
2
printf FORMAT [ARGUMENT]...
printf OPTION

主要用途

  • 格式化参数并输出。

选项

1
2
--help 显示帮助信息并退出。
--version 显示版本信息并退出。

参数

format:输出格式。

arguments:一到多个参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
在这里忽略了(%b %q),如果你安装的coreutils版本支持它们,那么请参考上面的例子。
支持的转义序列:

\" 双引号
\\ 反斜杠
\a 响铃
\b 退格
\c 截断输出
\e 退出
\f 翻页
\n 换行
\r 回车
\t 水平制表符
\v 竖直制表符
\NNN 八进制数 (1到3位数字)
\xHH 十六进制数 (1到2位数字)
\uHHHH Unicode字符附加4位十六进制数字
\UHHHHHHHH Unicode字符附加8位十六进制数字
%% 百分号

以及'diouxXfeEgGcs'中的一个结尾的C格式规范,将被转换为正确的类型并处理可变宽度。

例子

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
# 使用 /usr/bin/printf 确保调用的不是内建命令。
# 当然,在你关闭内建printf以及确认当前环境没有printf函数的情况下,可直接使用printf,详见末尾"注意"的链接。

# 按行打印数组和关联数组的下标及值。

# 声明数组可以不加'declare -a'或'local -a'(在函数内声明的局部变量)。
arr=('line1' 'line2')
/usr/bin/printf "%s\n" ${!arr[@]}
# 输出下标
0
1
/usr/bin/printf "%s\n" ${arr[@]}
# 输出值
line1
line2

#声明关联数组(也就是字典)必须加'declare -A'或'local -A'(在函数内声明的局部变量)。
declare -A assoc_arr=(['key1']='value1' ['key2']='value2')
/usr/bin/printf "%s\n" ${!assoc_arr[@]}
# 输出键。
key2
key1
/usr/bin/printf "%s\n" ${assoc_arr[@]}
# 输出值。
value2
value1

返回值

返回状态为成功除非给出了非法选项等。

注意

  1. 该命令是GNU coreutils包中的命令,相关的帮助信息请查看man -s 1 printfinfo coreutils 'pwd invocation'

  2. 启动或关闭内建命令请查看enable命令,关于同名优先级的问题请查看builtin命令的例子部分的相关讨论。

  3. 我通过和bug-bash@gnu.org的交流,得到了关于这几个格式说明符%b %q %(fmt)T的解释:

    printf(1)中的%b 格式说明符是 printf(3)支持的格式之外增加的一个 POSIX 特性。

    %q 和%T 说明符是非标准的,并且不受所有独立实现的 printf 的支持。

    更多细节请参考链接:

quota

显示磁盘已使用的空间与限制

quota命令 用于显示用户或者工作组的磁盘配额信息。输出信息包括磁盘使用和配额限制。

语法

1
quota(选项)(参数)

选项

1
2
3
4
5
-g:列出群组的磁盘空间限制;
-q:简明列表,只列出超过限制的部分;
-u:列出用户的磁盘空间限制;
-v:显示该用户或群组,在所有挂入系统的存储设备的空间限制;
-V:显示版本信息。

参数

用户或者工作组:指定要显示的用户或者工作组。

实例

我们可以限制某一群组所能使用的最大磁盘配额,而且可以再限制某一使用者的最大磁盘配额 ,好比做一个收费的应用,vip可以得到空间更大一些。另外,以 Link 的方式,来使邮件可以作为限制的配额(更改/var/spool/mail 这个路径),不2,需要重新再规划一个硬盘!直接使用 Link 的方式指向 /home (或者其它已经做好的 quota 磁盘)就可以!这通常是用在原本规划不好,但是却又不想要更动原有主机架构的情况中!

要求:Linux 主机里面主要针对 quser1 及 quser2 两个使用者来进行磁盘配额, 且这两个使用者都是挂在 qgroup 组里面的。每个使用者总共有 50MB 的磁盘空间 (不考虑 inode) 限制!并且 soft limit 为 45 MB;而宽限时间设定为 1 天, 但是在一天之内必须要将多余的文件删除掉,否则将无法使用剩下的空间 ;gquota 这个组考虑最大限额,所以设定为 90 MB!(注意,这样设置的好处是富有弹性,好比现在的邮件服务,那么多用户,承诺给用户每人最大空间为数GB,然而人们不可能每人都会使用那么大的空间,所以邮件服务的总空间,实际上肯定不是注册客户数乘以数GB,否则这样得多大啊。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@localhost ~]# groupadd qgroup
[root@localhost ~]# useradd -m -g qgroup quser1
[root@localhost ~]# useradd -m -g qgroup quser2
[root@localhost ~]# passwd quser1
[root@localhost ~]# passwd quser2
[root@localhost ~]# df ===> 自己找一个合适的分区来做实验,这里用/disk2
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/hda1 5952252 3193292 2451720 57% /
/dev/hdb1 28267608 77904 26730604 1% /disk2
/dev/hda5 9492644 227252 8775412 3% /disk1

[root@localhost ~]# vi /etc/fstab
LABEL=/ / ext3 defaults 1 1
LABEL=/disk1 /disk1 ext3 defaults 1 2
LABEL=/disk2 /disk2 ext3 defaults,usrquota,grpquota 1 2
/dev/hda3 swap swap defaults 0 0

注意多了usrquota,grpquota,在defaults,usrquota,grpquota之间都没有空格,务必正确书写。这样就算加入了 quota 的磁盘格式了!不过,由于真正的 quota 在读取的时候是读取/etc/mtab这个文件的,而该文件需要重新开机之后才能够以/etc/fstab 的新数据进行改写!所以这个时候可以选择:重新开机 (reboot)。

重新remount filesystem来驱动设定值。

1
2
3
4
[root@localhost ~]# umount /dev/hdb1
[root@localhost ~]# mount -a
[root@localhost ~]# grep '/disk2' /etc/mtab
/dev/hdb1 /disk2 ext3 rw,usrquota,grpquota 0 0

事实上,也可以利用 mount 的 remount 功能。

1
[root@localhost ~]# mount -o remount /disk2

这样就已经成功的将 filesystem 的 quota 功能加入。

扫瞄磁盘的使用者使用状况,并产生重要的 aquota.group 与 aquota.user:

1
2
3
4
5
6
7
[root@localhost ~]# quotacheck -avug
quotacheck: Scanning /dev/hdb1 [/disk2] done
quotacheck: Checked 3 directories and 4 files

[root@localhost ~]# ll /disk2
-rw------- 1 root root 6144 Sep 6 11:44 aquota.group
-rw------- 1 root root 6144 Sep 6 11:44 aquota.user

使用 quotacheck 就可以轻易的将所需要的数据给他输出了!但奇怪的是,在某些 Linux 版本中,不能够以 aquota.user(group) 来启动quota ,可能是因为旧版 quota 的关系, 所以就另外做了一个 link 文件按来欺骗 quota,这个动作非必要。(主要是学习这个思维很重要)

1
2
3
[root@localhost ~]# cd /disk2
[root@localhost ~]# ln -s aquota.user quota.user
[root@localhost ~]# ln -s aquota.group quota.group

启动 quota 的限额:

1
2
3
[root@localhost ~]# quotaon -avug
/dev/hdb1 [/disk2]: group quotas turned on
/dev/hdb1 [/disk2]: user quotas turned on ===> 看到turned on,才是真的成功!

编辑使用者的可使用空间:

1
2
3
4
5
[root@localhost ~]# edquota -u quser1
Disk quotas for user quser1 (uid 502):
Filesystem blocks soft hard inodes soft hard
/dev/hdb1 0 45000 50000 0 0 0
[root@localhost ~]# edquota -p quser1 quser2 ===> 直接复制给quser2

接下来要来设定宽限时间,还是使用 edquota

1
2
3
4
5
[root@localhost ~]# edquota -t
Grace period before enforcing soft limits for users:
time units may be: days, hours, minutes, or seconds
Filesystem Block grace period Inode grace period
/dev/hdb1 1days 7days

使用quota -v来查询:

1
2
3
4
5
6
7
[root@localhost ~]# quota -vu quser1 quser2
Disk quotas for user quser1 (uid 502):
Filesystem blocks quota limit grace files quota limit grace
/dev/hdb1 0 45000 50000 0 0 0
Disk quotas for user quser2 (uid 503):
Filesystem blocks quota limit grace files quota limit grace
/dev/hdb1 0 45000 50000 0 0 0

注意,由于使用者尚未超过45 MB,所以 grace ( 宽限时间 ) 就不会出现。

编辑群组可使用的空间:

1
2
3
4
5
6
7
8
9
[root@localhost ~]# edquota -g qgroup
Disk quotas for group qgroup (gid 502):
Filesystem blocks soft hard inodes soft hard
/dev/hdb1 0 80000 90000 0 0 0

[root@localhost ~]# quota -vg qgroup
Disk quotas for group qgroup (gid 502):
Filesystem blocks quota limit grace files quota limit grace
/dev/hdb1 0 80000 90000 0 0 0

readonly

标记shell变量或函数为只读

语法

1
2
readonly [-aAf] [name[=value] ...]
readonly -p

主要用途

  • 定义一到多个变量并设置只读属性。
  • 为已定义的一到多个变量设置只读属性。
  • 显示全部包含只读属性的变量。
  • 为已定义的一到多个函数设置只读属性。
  • 显示全部包含只读属性的函数。

选项

1
2
3
4
5
-a:指向数组。
-A:指向关联数组。
-f:指向函数。
-p:显示全部只读变量。
--:在它之后的选项无效。

参数

1
2
name(可选):变量名或函数名
value(可选):变量的值

返回值

readonly返回true除非你提供了非法选项或非法名称。

例子

1
2
3
4
5
# 定义变量并增加只读属性
readonly var1=13 var2
readonly -a arr1=(1 2 3 4 5) arr2=('z' 'x' 'c')
# 必须有 '-A' 选项
readonly -A dict1=(['key1']='value1')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 先定义变量、函数,然后再为它们添加只读属性
max=3
readonly max

# 数组定义时可以不加 `declare -a`
seasons=('spring' 'summer' 'autumn' 'winter')
# 为数组添加只读属性时可以不加 `-a` 选项
readonly seasons

declare -A man=(['age']=23 ['height']='190cm')
# 为关联数组添加只读属性时可以不加 `-A` 选项
readonly man

function foo(){ echo 'bar'; }
# 为函数添加只读属性时必须加 `-f` 选项
readonly -f foo
1
2
3
4
5
6
7
8
9
# 显示全部只读变量,以下两个命令的显示结果一样
readonly
readonly -p
# 显示全部拥有只读属性的数组
readonly -a
# 显示全部拥有只读属性的关联数组
readonly -A
# 显示全部拥有只读属性的函数
readonly -f

常见错误

对于只读变量而言,若用户对其值进行修改,则会立即报错。例如,使用该指令定义一个只读变量”test”,并且将其值初始化为”ok”,输入如下命令:

1
[root@localhost ~]# readonly test='ok'        #定义只读变量并初始化 

那么当用户直接修改该只读变量时就会报错,如下所示:

1
2
[root@localhost ~]# test='my'                 #试图修改只读变量的值
-bash: test: readonly variable

当用户试图修改只读变量的值时,会被提示该变量为只读变量。

注意

  1. 该命令是bash内建命令,相关的帮助信息请查看help命令。
  2. declare +r不能去除只读属性, unset不能删除只读变量。

rexec

运程执行Linux系统下命令

rexec命令 用于在指定的远程Linux系统主机上执行命令,向远程rexec服务器发出执行命令的请求。

rexec命令通过检查$HOME/.netrc文件(包含远程主机上使用的用户名和密码)来提供自动登录的功能。如果没有发现此类项或系统在安全方式下操作(参阅 securetcpip 命令),rexec命令提示输入一个远程主机的有效用户名和密码。这两种情况下,rexec均导致远程系统上的rexecd使用缺省的compat用户登录认证方法。rexecd不会为了备用的认证方法去查找/etc/security/user文件。也可以指定-n标志到rexec命令行上来重设自动登录功能。

语法

1
rexec(选项)(参数)

选项

1
2
3
4
-a:表示远程命令的标准错误与标准输出相同,不支持发送任意信号到远程进程;
-l<用户名>:指定连接远程rexec服务器的用户名;
-p<密码>:指定连接远程rexec服务器的密码;
-n:明确地提示输入用户名和密码。

参数

  • 远程主机:指定远程主机(ip地址或主机名);
  • 命令:指定需要在远程主机上执行的命令。

实例

要在一个远程主机上执行date命令,输入:

1
rexec host1 date

date命令的输出现在显示在本地系统上。本示例中,在本地主机上的$HOME/.netrc文件包含远程主机上有效的用户名和密码。如果没有远程主机的$HOME/.netrc文件中的有效项,将提示输入登录标识和密码。输入所要求的登录信息后,date命令的输出显示在本地系统上。

要重设自动登录功能并执行远程主机上的date命令,输入:

1
rexec -nhost1 date

出现提示时输入用户名和密码,date命令的输出现在显示在本地系统上。

列出远程主机上另一个用户的目录,输入:

1
rexec host1 ls -l /home/karen

在远程主机host1上的karen 用户的目录列表显示在本地系统上。

如果没有远程主机的$HOME/.netrc文件中的有效项,将提示您输入登录标识和密码。输入要求的登录信息后,在远程主机host1上的karen用户的目录列表显示在本地系统上。

screen

用于命令行终端切换

Screen 是一款由GNU计划开发的用于命令行终端切换的自由软件。用户可以通过该软件同时连接多个本地或远程的命令行会话,并在其间自由切换。GNU Screen可以看作是窗口管理器的命令行界面版本。它提供了统一的管理多个会话的界面和相应的功能。

会话恢复

只要Screen本身没有终止,在其内部运行的会话都可以恢复。这一点对于远程登录的用户特别有用——即使网络连接中断,用户也不会失去对已经打开的命令行会话的控制。只要再次登录到主机上执行screen -r就可以恢复会话的运行。同样在暂时离开的时候,也可以执行分离命令detach,在保证里面的程序正常运行的情况下让Screen挂起(切换到后台)。这一点和图形界面下的VNC很相似。

多窗口

在Screen环境下,所有的会话都独立的运行,并拥有各自的编号、输入、输出和窗口缓存。用户可以通过快捷键在不同的窗口下切换,并可以自由的重定向各个窗口的输入和输出。Screen实现了基本的文本操作,如复制粘贴等;还提供了类似滚动条的功能,可以查看窗口状况的历史记录。窗口还可以被分区和命名,还可以监视后台窗口的活动。 会话共享 Screen可以让一个或多个用户从不同终端多次登录一个会话,并共享会话的所有特性(比如可以看到完全相同的输出)。它同时提供了窗口访问权限的机制,可以对窗口进行密码保护。

GNU’s Screen 官方站点:http://www.gnu.org/software/screen/

语法

1
# screen -AmRvx -[ls -wipe][-d <作业名称>][-h <行数>][-r <作业名称>][-s ][-S <作业名称>]

选项

1
2
3
4
5
6
7
8
9
10
11
12
-A  将所有的视窗都调整为目前终端机的大小。
-d <作业名称>  将指定的screen作业离线。
-h <行数>  指定视窗的缓冲区行数。
-m  即使目前已在作业中的screen作业,仍强制建立新的screen作业。
-r <作业名称>  恢复离线的screen作业。
-R  先试图恢复离线的作业。若找不到离线的作业,即建立新的screen作业。
-s  指定建立新视窗时,所要执行的shell。
-S <作业名称>  指定screen作业的名称。
-v  显示版本信息。
-x  恢复之前离线的screen作业。
-ls或--list  显示目前所有的screen作业。
-wipe  检查目前所有的screen作业,并删除已经无法使用的screen作业。

常用screen参数

1
2
3
4
5
screen -S yourname -> 新建一个叫yourname的session
screen -ls -> 列出当前所有的session
screen -r yourname -> 回到yourname这个session
screen -d yourname -> 远程detach某个session
screen -d -r yourname -> 结束当前session并回到yourname这个session

在每个screen session 下,所有命令都以 ctrl+a(C-a) 开始。

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
C-a ? -> 显示所有键绑定信息
C-a c -> 创建一个新的运行shell的窗口并切换到该窗口
C-a n -> Next,切换到下一个 window
C-a p -> Previous,切换到前一个 window
C-a 0..9 -> 切换到第 0..9 个 window
Ctrl+a [Space] -> 由视窗0循序切换到视窗9
C-a C-a -> 在两个最近使用的 window 间切换
C-a x -> 锁住当前的 window,需用用户密码解锁
C-a d -> detach,暂时离开当前session,将目前的 screen session (可能含有多个 windows) 丢到后台执行,并会回到还没进 screen 时的状态,此时在 screen session 里,每个 window 内运行的 process (无论是前台/后台)都在继续执行,即使 logout 也不影响。
C-a z -> 把当前session放到后台执行,用 shell 的 fg 命令则可回去。
C-a w -> 显示所有窗口列表
C-a t -> time,显示当前时间,和系统的 load
C-a k -> kill window,强行关闭当前的 window
C-a -> 进入 copy mode,在 copy mode 下可以回滚、搜索、复制就像用使用 [vi 一样
C-b Backward,PageUp
C-f Forward,PageDown
H(大写) High,将光标移至左上角
L Low,将光标移至左下角
0 移到行首
$ 行末
w forward one word,以字为单位往前移
b backward one word,以字为单位往后移
Space 第一次按为标记区起点,第二次按为终点
Esc 结束 copy mode
C-a ] -> paste,把刚刚在 copy mode 选定的内容贴上

使用 screen

安装screen

流行的Linux发行版(例如Red Hat Enterprise Linux)通常会自带screen实用程序,如果没有的话,可以从GNU screen的官方网站下载。

1
2
3
4
[root@TS-DEV ~]# yum install screen
[root@TS-DEV ~]# rpm -qa|grep screen
screen-4.0.3-4.el5
[root@TS-DEV ~]#

创建一个新的窗口

安装完成后,直接敲命令screen就可以启动它。但是这样启动的screen会话没有名字,实践上推荐为每个screen会话取一个名字,方便分辨:

1
[root@TS-DEV ~]# screen -S david 

screen启动后,会创建第一个窗口,也就是窗口No. 0,并在其中打开一个系统默认的shell,一般都会是bash。所以你敲入命令screen之后,会立刻又返回到命令提示符,仿佛什么也没有发生似的,其实你已经进入Screen的世界了。当然,也可以在screen命令之后加入你喜欢的参数,使之直接打开你指定的程序,例如:

1
[root@TS-DEV ~]# screen vi david.txt

screen创建一个执行vi david.txt的单窗口会话,退出vi 将退出该窗口/会话。

查看窗口和窗口名称

打开多个窗口后,可以使用快捷键C-a w列出当前所有窗口。如果使用文本终端,这个列表会列在屏幕左下角,如果使用X环境下的终端模拟器,这个列表会列在标题栏里。窗口列表的样子一般是这样:

1
0$ bash  1-$ bash  2*$ bash  

这个例子中我开启了三个窗口,其中*号表示当前位于窗口2,-号表示上一次切换窗口时位于窗口1。

Screen默认会为窗口命名为编号和窗口中运行程序名的组合,上面的例子中窗口都是默认名字。练习了上面查看窗口的方法,你可能就希望各个窗口可以有不同的名字以方便区分了。可以使用快捷键C-a A来为当前窗口重命名,按下快捷键后,Screen会允许你为当前窗口输入新的名字,回车确认。

会话分离与恢复

你可以不中断screen窗口中程序的运行而暂时断开(detach)screen会话,并在随后时间重新连接(attach)该会话,重新控制各窗口中运行的程序。例如,我们打开一个screen窗口编辑/tmp/david.txt文件:

1
[root@TS-DEV ~]# screen vi /tmp/david.txt

之后我们想暂时退出做点别的事情,比如出去散散步,那么在screen窗口键入C-a d,Screen会给出detached提示:

暂时中断会话

半个小时之后回来了,找到该screen会话:

1
[root@TS-DEV ~]# screen -ls

重新连接会话:

1
[root@TS-DEV ~]# screen -r 12865

一切都在。

当然,如果你在另一台机器上没有分离一个Screen会话,就无从恢复会话了。这时可以使用下面命令强制将这个会话从它所在的终端分离,转移到新的终端上来:

清除dead 会话

如果由于某种原因其中一个会话死掉了(例如人为杀掉该会话),这时screen -list会显示该会话为dead状态。使用screen -wipe命令清除该会话:

关闭或杀死窗口

正常情况下,当你退出一个窗口中最后一个程序(通常是bash)后,这个窗口就关闭了。另一个关闭窗口的方法是使用C-a k,这个快捷键杀死当前的窗口,同时也将杀死这个窗口中正在运行的进程。

如果一个Screen会话中最后一个窗口被关闭了,那么整个Screen会话也就退出了,screen进程会被终止。

除了依次退出/杀死当前Screen会话中所有窗口这种方法之外,还可以使用快捷键C-a :,然后输入quit命令退出Screen会话。需要注意的是,这样退出会杀死所有窗口并退出其中运行的所有程序。其实C-a :这个快捷键允许用户直接输入的命令有很多,包括分屏可以输入split等,这也是实现Screen功能的一个途径,不过个人认为还是快捷键比较方便些。

screen 高级应用

会话共享

还有一种比较好玩的会话恢复,可以实现会话共享。假设你在和朋友在不同地点以相同用户登录一台机器,然后你创建一个screen会话,你朋友可以在他的终端上命令:

1
[root@TS-DEV ~]# screen -x

这个命令会将你朋友的终端Attach到你的Screen会话上,并且你的终端不会被Detach。这样你就可以和朋友共享同一个会话了,如果你们当前又处于同一个窗口,那就相当于坐在同一个显示器前面,你的操作会同步演示给你朋友,你朋友的操作也会同步演示给你。当然,如果你们切换到这个会话的不同窗口中去,那还是可以分别进行不同的操作的。

会话锁定与解锁

Screen允许使用快捷键C-a s锁定会话。锁定以后,再进行任何输入屏幕都不会再有反应了。但是要注意虽然屏幕上看不到反应,但你的输入都会被Screen中的进程接收到。快捷键C-a q可以解锁一个会话。

也可以使用C-a x锁定会话,不同的是这样锁定之后,会话会被Screen所属用户的密码保护,需要输入密码才能继续访问这个会话。

发送命令到screen会话

在Screen会话之外,可以通过screen命令操作一个Screen会话,这也为使用Screen作为脚本程序增加了便利。关于Screen在脚本中的应用超出了入门的范围,这里只看一个例子,体会一下在会话之外对Screen的操作:

1
[root@TS-DEV ~]# screen -S sandy -X screen ping www.baidu.com

这个命令在一个叫做sandy的screen会话中创建一个新窗口,并在其中运行ping命令。

屏幕分割

现在显示器那么大,将一个屏幕分割成不同区域显示不同的Screen窗口显然是个很酷的事情。可以使用快捷键C-a S将显示器水平分割,Screen 4.00.03版本以后,也支持垂直分屏,快捷键是C-a |。分屏以后,可以使用 C-a <tab> 在各个区块间切换,每一区块上都可以创建窗口并在其中运行进程。

可以用C-a X快捷键关闭当前焦点所在的屏幕区块,也可以用C-a Q关闭除当前区块之外其他的所有区块。关闭的区块中的窗口并不会关闭,还可以通过窗口切换找到它。

C/P模式和操作

screen的另一个很强大的功能就是可以在不同窗口之间进行复制粘贴了。使用快捷键C-a <Esc>或者C-a [可以进入copy/paste模式,这个模式下可以像在vi中一样移动光标,并可以使用空格键设置标记。其实在这个模式下有很多类似vi的操作,譬如使用/进行搜索,使用y快速标记一行,使用w快速标记一个单词等。关于C/P模式下的高级操作,其文档的这一部分有比较详细的说明。

一般情况下,可以移动光标到指定位置,按下空格设置一个开头标记,然后移动光标到结尾位置,按下空格设置第二个标记,同时会将两个标记之间的部分储存在copy/paste buffer中,并退出copy/paste模式。在正常模式下,可以使用快捷键C-a ]将储存在buffer中的内容粘贴到当前窗口。

更多screen功能

同大多数UNIX程序一样,GNU Screen提供了丰富强大的定制功能。你可以在Screen的默认两级配置文件/etc/screenrc和$HOME/.screenrc中指定更多,例如设定screen选项,定制绑定键,设定screen会话自启动窗口,启用多用户模式,定制用户访问权限控制等等。如果你愿意的话,也可以自己指定screen配置文件。

以多用户功能为例,screen默认是以单用户模式运行的,你需要在配置文件中指定multiuser on 来打开多用户模式,通过acl*(acladd,acldel,aclchg…)命令,你可以灵活配置其他用户访问你的screen会话。更多配置文件内容请参考screen的man页。

set

显示或设置shell特性及shell变量

set命令 作用主要是显示系统中已经存在的shell变量,以及设置shell变量的新变量值。使用set更改shell特性时,符号”+”和”-“的作用分别是打开和关闭指定的模式。set命令不能够定义新的shell变量。如果要定义新的变量,可以使用declare命令以变量名=值的格式进行定义即可。

1
set(选项)(参数)

选项

set 指令能设置所使用 shell 的执行方式,可依照不同的需求来做设置

-a  
标示已修改的变量,以供输出至环境变量。

-b  
使被中止的后台程序立刻回报执行状态。

-C  
转向所产生的文件无法覆盖已存在的文件。

-d  
Shell 预设会用杂凑表记忆使用过的指令,以加速指令的执行。使用-d参数可取消。

-e  
若指令传回值不等于 0,则立即退出 shell。

-f 
取消使用通配符。

-h  
自动记录函数的所在位置。

-H Shell
可利用”!”加<指令编号>的方式来执行 history 中记录的指令。

-k  
指令所给的参数都会被视为此指令的环境变量。

-l  
记录 for 循环的变量名称。

-m  
使用监视模式。

-n  
只读取指令,而不实际执行。

-p  
启动优先顺序模式。

-P  
启动-P参数后,执行指令时,会以实际的文件或目录来取代符号连接。

-t  
执行完随后的指令,即退出 shell。

-u  
当执行时使用到未定义过的变量,则显示错误信息。

-v  
显示 shell 所读取的输入值。

-x  
执行指令后,会先显示该指令及所下的参数。

+<参数> 
取消某个 set 曾启动的参数。

参数

取消某个set曾启动的参数。

实例

set -xset +x指令用于脚本调试。set 是把它下面的命令打印到屏幕
set -x 是开启 set +x是关闭 set -o是查看 (xtrace),set 去追中一段代码的显示情况。
执行set -x后,对整个脚本有效。

1
2
3
ls -l /bin/bash
+ ls --color=tty -l /bin/bash
-rwxr-xr-x 1 root root 722684 Jul 12 2006 /bin/bash

针对一部分 script,可以选择 set -x 和 set +x 配套使用。比如在一个脚本里:

1
2
3
set -x            # activate debugging from here
w
set +x # stop debugging from here

使用declare命令定义一个新的环境变量”mylove”,并且将其值设置为”Visual C++”,输入如下命令:

1
declare mylove='Visual C++'   #定义新环境变量

再使用set命令将新定义的变量输出为环境变量,输入如下命令:

1
set -a mylove                 #设置为环境变量

执行该命令后,将会新添加对应的环境变量。用户可以使用env命令和grep命令分别显示和搜索环境变量”mylove”,输入命令如下:

1
env | grep mylove             #显示环境变量值

此时,该命令执行后,将输出查询到的环境变量值。

sleep

将目前动作延迟一段时间

sleep命令 暂停指定的时间。

语法

1
sleep(参数)

参数

时间:指定要暂停时间的长度。

时间长度,后面可接 s、m、h 或 d,其中 s 为秒,m 为 分钟,h 为小时,d 为日数。

实例

有时在写一些以循环方式运行的监控脚本,设置时间间隔是必不可少的,下面是一个Shell进度条的脚本演示在脚本中生成延时。

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

b=''
for ((i=0;$i<=100;i++))
do
printf "Progress:[%-100s]%d%%\r" $b $i
sleep 0.1
b=#$b
done
echo

sort

对文本文件中所有行进行排序。

概要

1
2
sort [OPTION]... [FILE]...
sort [OPTION]... --files0-from=F

主要用途

  • 将所有输入文件的内容排序后并输出。
  • 当没有文件或文件为-时,读取标准输入。

选项

排序选项:

1
2
3
4
5
6
7
8
9
10
11
12
13
-b, --ignore-leading-blanks    忽略开头的空白。
-d, --dictionary-order 仅考虑空白、字母、数字。
-f, --ignore-case 将小写字母作为大写字母考虑。
-g, --general-numeric-sort 根据数字排序。
-i, --ignore-nonprinting 排除不可打印字符。
-M, --month-sort 按照非月份、一月、十二月的顺序排序。
-h, --human-numeric-sort 根据存储容量排序(注意使用大写字母,例如:2K 1G)。
-n, --numeric-sort 根据数字排序。
-R, --random-sort 随机排序,但分组相同的行。
--random-source=FILE 从FILE中获取随机长度的字节。
-r, --reverse 将结果倒序排列。
--sort=WORD 根据WORD排序,其中: general-numeric 等价于 -g,human-numeric 等价于 -h,month 等价于 -M,numeric 等价于 -n,random 等价于 -R,version 等价于 -V。
-V, --version-sort 文本中(版本)数字的自然排序。

其他选项:

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
--batch-size=NMERGE                    一次合并最多NMERGE个输入;超过部分使用临时文件。
-c, --check, --check=diagnose-first 检查输入是否已排序,该操作不会执行排序。
-C, --check=quiet, --check=silent 类似于 -c 选项,但不输出第一个未排序的行。
--compress-program=PROG 使用PROG压缩临时文件;使用PROG -d解压缩。
--debug 注释用于排序的行,发送可疑用法的警报到stderr。
--files0-from=F 从文件F中读取以NUL结尾的所有文件名称;如果F是 - ,那么从标准输入中读取名字。
-k, --key=KEYDEF 通过一个key排序;KEYDEF给出位置和类型。
-m, --merge 合并已排序文件,之后不再排序。
-o, --output=FILE 将结果写入FILE而不是标准输出。
-s, --stable 通过禁用最后的比较来稳定排序。
-S, --buffer-size=SIZE 使用SIZE作为内存缓存大小。
-t, --field-separator=SEP 使用SEP作为列的分隔符。
-T, --temporary-directory=DIR 使用DIR作为临时目录,而不是 $TMPDIR 或 /tmp;多次使用该选项指定多个临时目录。
--parallel=N 将并发运行的排序数更改为N。
-u, --unique 同时使用-c,严格检查排序;不同时使用-c,输出排序后去重的结果。
-z, --zero-terminated 设置行终止符为NUL(空),而不是换行符。
--help 显示帮助信息并退出。
--version 显示版本信息并退出。


KEYDEF的格式为:F[.C][OPTS][,F[.C][OPTS]] ,表示开始到结束的位置。
F表示列的编号
C表示
OPTS为[bdfgiMhnRrV]中的一到多个字符,用于覆盖当前排序选项。
使用--debug选项可诊断出错误的用法。


SIZE 可以有以下的乘法后缀:
% 内存的1%;
b 1;
K 1024(默认);
剩余的 M, G, T, P, E, Z, Y 可以类推出来。

参数

FILE(可选):要处理的文件,可以为任意数量。

返回值

返回0表示成功,返回非0值表示失败。

例子

sort将文件/文本的每一行作为一个单位相互比较,比较原则是从首字符向后依次按ASCII码值进行比较,最后将他们按升序输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@[mail text]# cat sort.txt
aaa:10:1.1
ccc:30:3.3
ddd:40:4.4
bbb:20:2.2
eee:50:5.5
eee:50:5.5

[root@mail text]# sort sort.txt
aaa:10:1.1
bbb:20:2.2
ccc:30:3.3
ddd:40:4.4
eee:50:5.5
eee:50:5.5

忽略相同行使用-u选项或者uniq

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@mail text]# cat sort.txt
aaa:10:1.1
ccc:30:3.3
ddd:40:4.4
bbb:20:2.2
eee:50:5.5
eee:50:5.5

[root@mail text]# sort -u sort.txt
aaa:10:1.1
bbb:20:2.2
ccc:30:3.3
ddd:40:4.4
eee:50:5.5

[root@mail text]# uniq sort.txt
aaa:10:1.1
ccc:30:3.3
ddd:40:4.4
bbb:20:2.2
eee:50:5.5

sort-n、-r、-k、-t选项的使用:

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
[root@mail text]# cat sort.txt
AAA:BB:CC
aaa:30:1.6
ccc:50:3.3
ddd:20:4.2
bbb:10:2.5
eee:40:5.4
eee:60:5.1

# 将BB列按照数字从小到大顺序排列:
[root@mail text]# sort -nk 2 -t: sort.txt
AAA:BB:CC
bbb:10:2.5
ddd:20:4.2
aaa:30:1.6
eee:40:5.4
ccc:50:3.3
eee:60:5.1

# 将CC列数字从大到小顺序排列:
# -n是按照数字大小排序,-r是以相反顺序,-k是指定需要排序的栏位,-t指定栏位分隔符为冒号
[root@mail text]# sort -nrk 3 -t: sort.txt
eee:40:5.4
eee:60:5.1
ddd:20:4.2
ccc:50:3.3
bbb:10:2.5
aaa:30:1.6
AAA:BB:CC

关于-k选项的解读和例子:

-k选项深度解读:

1
2
3
FStart.CStart Modifier,FEnd.CEnd Modifier
-------Start--------,-------End--------
FStart.CStart 选项 , FEnd.CEnd 选项

这个语法格式可以被其中的逗号,分为两大部分,Start 部分和 End 部分。
Start部分由三部分组成,其中的Modifier部分就是我们之前说过的选项部分;
我们重点说说Start部分的FStartC.StartC.Start是可以省略的,省略的话就表示从本域的开头部分开始。FStart.CStart,其中FStart就是表示使用的域,而CStart则表示在FStart域中从第几个字符开始算排序首字符。
同理,在End部分中,你可以设定FEnd.CEnd,如果你省略.CEnd或将它设定为0,则表示结尾到本域的最后一个字符。

例子:从公司英文名称的第二个字母开始排序:

1
2
3
4
5
$ sort -t ' ' -k 1.2 facebook.txt
baidu 100 5000
sohu 100 4500
google 110 5000
guge 50 3000

解读:使用了-k 1.2,表示对第一个域的第二个字符开始到本域的最后一个字符为止的字符串进行排序。你会发现baidu因为第二个字母是a而名列榜首。sohu和google第二个字符都是o,但sohu的h在google的o前面,所以两者分别排在第二和第三。guge只能屈居第四了。

例子:只针对公司英文名称的第二个字母进行排序,如果相同的按照员工工资进行降序排序:

1
2
3
4
5
$ sort -t ' ' -k 1.2,1.2 -nrk 3,3 facebook.txt
baidu 100 5000
google 110 5000
sohu 100 4500
guge 50 3000

解读:由于只对第二个字母进行排序,所以我们使用了-k 1.2,1.2的表示方式,表示我们只对第二个字母进行排序(如果你问我使用-k 1.2怎么不行?当然不行,因为你省略了End部分,这就意味着你将对从第二个字母起到本域最后一个字符为止的字符串进行排序)。
对员工工资进行排序,我们也使用了-k 3,3,这是最准确的表述,表示我们只对本域进行排序,因为如果你省略了后面的3,就变成了我们对第3个域开始到最后一个域位置的内容进行排序了。

注意

  1. 关于-g和-n选项的区别:stackoverflow

  2. 关于这个复杂命令的学习,建议您阅读info文档及参考博客、问答网站等。

  3. 该命令是GNU coreutils包中的命令,相关的帮助信息请查看man -s 1 shufinfo coreutils 'shuf invocation'

ssh

openssh套件中的客户端连接工具

ssh命令 是openssh套件中的客户端连接工具,可以给予ssh加密协议实现安全的远程登录服务器。

语法

1
ssh(选项)(参数)

选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-1:强制使用ssh协议版本1;
-2:强制使用ssh协议版本2;
-4:强制使用IPv4地址;
-6:强制使用IPv6地址;
-A:开启认证代理连接转发功能;
-a:关闭认证代理连接转发功能;
-b:使用本机指定地址作为对应连接的源ip地址;
-C:请求压缩所有数据;
-F:指定ssh指令的配置文件;
-f:后台执行ssh指令;
-g:允许远程主机连接主机的转发端口;
-i:指定身份文件;
-l:指定连接远程服务器登录用户名;
-N:不执行远程指令;
-o:指定配置选项;
-p:指定远程服务器上的端口;
-q:静默模式;
-X:开启X11转发功能;
-x:关闭X11转发功能;
-y:开启信任X11转发功能。

参数

  • 远程主机:指定要连接的远程ssh服务器;
  • 指令:要在远程ssh服务器上执行的指令。

实例

1
2
3
4
5
6
7
8
9
10
# ssh 用户名@远程服务器地址
ssh user1@172.24.210.101
# 指定端口
ssh -p 2211 root@140.206.185.170

# ssh 大家族
ssh user@ip -p22 # 默认用户名为当前用户名,默认端口为 22
ssh-keygen # 为当前用户生成 ssh 公钥 + 私钥
ssh-keygen -f keyfile -i -m key_format -e -m key_format # key_format: RFC4716/SSH2(default) PKCS8 PEM
ssh-copy-id user@ip:port # 将当前用户的公钥复制到需要 ssh 的服务器的 ~/.ssh/authorized_keys,之后可以免密登录

背后故事

英文:Tatu Ylonen
编译:Linux中国/kenxx
来源:https://linux.cn/article-8476-1.html

为什么 SSH(安全终端)的端口号是 22 呢,这不是一个巧合,这其中有个我(Tatu Ylonen,SSH 协议的设计者)未曾诉说的故事。

将 SSH 协议端口号设为 22 的故事

1995 年春我编写了 SSH 协议的最初版本,那时候 telnet 和 FTP 正被广泛使用。

当时我设计 SSH 协议想着是为了替代 telnet(端口 23)和 ftp(端口21)两个协议的,而端口 22 是空闲的。我想当然地就选择了夹在 telnet 和 ftp 的端口中间的数字。我觉得端口号虽然是个小事但似乎又存在着某种信念。但我到底要怎么拿到那个端口号呢?我未曾拥有过任何一个端口号,但我却认识几个拥有端口号的人!

在那时取得端口号的事情其实说来挺简单的。毕竟当时的因特网(Internet)并不是很大,是因特网爆炸的早期。端口号分配的活儿是 IANA(Internet Assigned Numbers Authority,互联网数字分配机构)干的。在那时这机构可相当于是因特网先驱 Jon PostelJoyce K. Reynolds 一般的存在。Jon 参与编写了多项主要的协议标准,例如 IP(RFC 791)、ICMP(RFC 792)和 TCP(RFC 793)等一些你应该早有耳闻的协议。

我可以说是敬畏 Jon 先生的,他参与编写了几乎所有主要的因特网标准文档(Internet RFC)!

1995 年 7 月,就在我发布 ssh-1.0 前,我发送了一封邮件给 IANA:

From ylo Mon Jul 10 11:45:48 +0300 1995
From: Tatu Ylonen
To: Internet Assigned Numbers Authority
Subject: 请求取得一个端口号
Organization: 芬兰赫尔辛基理工大学

亲爱的机构成员:

我写了个可以在不安全的网络环境中安全地从一台机器登录到另一台机器的程序。它主要是对现有的 telnet 协议以及 rlogin 协议的功能性提升和安全性改进。说的具体些,就是可以防御 IP、DNS > 或路由等欺骗行为。我打算将我的软件免费地发布在因特网上,以得到广泛地使用。

我希望为该软件注册一个特权端口号,要是这个端口号在 1 到 255 > 之间就更好了,这样它就可以用在名字服务器的 WKS 字段中了。

我在附件中附上了协议标准的草案。这个软件已经在本地运行了几个月了,我已准备在获得端口号后就发布。如果端口号分配一事安排的及时,我希望这周就将要发布的软件准备好。我目前在 beta 版测试时使用的端口号是 > 22,如果要是能够分配到这个端口,我就不用做什么更改了(目前这个端口在列表中还是空闲的)。

软件中服务的名称叫 ssh(系 Secure Shell 的缩写)。

您最真诚的,
Tatu Ylonen

(LCTT 译注:DNS 协议中的 WKS 记录类型意即“众所周知的业务描述”,是类似于 A、MX 这样的 DNS 记录类型,用于描述某个 IP 所提供的服务,目前鲜见使用。参见: https://docs.oracle.com/cd/E19683-01/806-4077/dnsintro-154/index.html 。)

第二天,我就收到了 Joyce 发来的邮件:

Date: Mon, 10 Jul 1995 15:35:33 -0700
From: jkrey@ISI.EDU
To: ylo@cs.hut.fi
Subject: 回复:请求取得一个端口号
Cc: iana@ISI.EDU
Tatu,
我们将端口号 22 分配给 ssh 服务了,你目前是该服务的主要联系人。
Joyce

这就搞定了!SSH 的端口正式使用 22!!!

1995 年 7 月 12 日上午 2 点 21 分,我给我在赫尔辛基理工大学的测试者们宣布了 SSH 的最后 beta 版本。当日下午 5 点 23 分,我给测试者们宣布了 ssh-1.0.0 版本。1995 年 7 月 12 日,下午 5 点 51 分,我将一份 SSH(安全终端)的宣告发给了 cypherpunks@toad.com 的邮件列表,此外我还将其发给了一些新闻组、邮件列表和一些在因特网上讨论相关话题的人们。

如何更改 SSH 服务的端口号

SSH 服务器是默认运行在 22 号端口上的。然而,由于某些原因需要,它也可以运行在别的端口上。比如为了方便测试使用,又比如在同一个宿主机上运行多个不同的配置。当然,极少情况下,不使用 root 权限运行它也可以,比如某些必须运行在非特权的端口的情况(端口号大于等于 1024)。

端口号可以在配置文件 /etc/ssh/sshd_config 中将 Port 22 更改。也可以使用 -p 选项运行 sshd。SSH 客户端和 sftp 程序也可以使用 -p 选项。

配置 SSH 协议穿越防火墙

SSH 是少数通常被许可穿越防火墙的协议之一。通常的做法是不限制出站的 SSH 连接,尤其常见于一些较小的或者比较技术型的组织中,而入站的 SSH 连接通常会限制到一台或者是少数几台服务器上。

出站的 SSH 连接

在防火墙中配置出站的 SSH 连接十分简单。如果完全限制了外发连接,那么只需要创建一个允许 TCP 端口 22 可以外发的规则即可。如果你想限制目标地址,你可以限制该规则仅允许访问你的组织放在云端的外部服务器或保护该云端的跳板服务器即可。

反向通道是有风险的

其实不限制出站的 SSH 连接虽然是可以的,但是是存在风险的,SSH 协议是支持 通道访问 的。最初的想法是在外部服务器搭建一个 SSH 服务监听来自各处的连接,将进入的连接转发到组织,并让这个连接可以访问某个内部服务器。

在某些场景下这当然非常的方便。开发者和系统管理员经常使用它打开一个通道以便于他们可以远程访问,比如在家里或者在旅行中使用笔记本电脑等场景。

然而通常来讲这些做法是违背安全策略的,跳过了防火墙管理员和安全团队保护的控制无疑是违背安全策略的,比如这些: PCI、HIPAA、NIST SP 800-53 等。它可以被黑客和外国情报机构用来在组织内留下后门。

CryptoAuditor 是一款可以控制通道穿过防火墙或者一组云端服务器入口的产品。该款产品可以配合 通用 SSH 密钥管理器(Universal SSH Key Manager) 来获得对 主机密钥(host keys)的访问,以在启用防火墙并阻挡未授权转发的场景中解密 SSH 会话。

入站的 SSH 访问

对于入站访问而言,这里有几点需要说一下:

配置防火墙,并转发所有去往 22 端口的连接只能流向到一个特定的内部网络 IP 地址或者一个 DMZ 主机。在该 IP 上运行 CryptoAuditor 或者跳板机来控制和审查所有访问该组织的连接。
在防火墙上使用不同的端口访问不同的服务器。
只允许使用 IPsec 协议这样的 VPN(虚拟专用网)登录后连接 SSH 服务。

通过 iptables 服务限制 SSH 访问

iptables 是一款内建在 Linux 内核的宿主防火墙。通常配置用于保护服务器以防止被访问那些未明确开启的端口。

如果服务器上启用了 iptables,使用下面的命令将可以允许进入的 SSH 访问,当然命令需要以 root 身份运行。

1
2
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p tcp --sport 22 -m conntrack --ctstate ESTABLISHED -j ACCEPT

如果你想将上述命令创建的规则持久地保存,在某些系统版本中,可使用如下命令:

1
service iptables save

test

shell环境中测试条件表达式工具

test命令 是shell环境中测试条件表达式的实用工具。

语法

1
test(选项)

选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-b<文件>:如果文件为一个块特殊文件,则为真;
-c<文件>:如果文件为一个字符特殊文件,则为真;
-d<文件>:如果文件为一个目录,则为真;
-e<文件>:如果文件存在,则为真;
-f<文件>:如果文件为一个普通文件,则为真;
-g<文件>:如果设置了文件的SGID位,则为真;
-G<文件>:如果文件存在且归该组所有,则为真;
-k<文件>:如果设置了文件的粘着位,则为真;
-O<文件>:如果文件存在并且归该用户所有,则为真;
-p<文件>:如果文件为一个命名管道,则为真;
-r<文件>:如果文件可读,则为真;
-s<文件>:如果文件的长度不为零,则为真;
-S<文件>:如果文件为一个套接字特殊文件,则为真;
-u<文件>:如果设置了文件的SUID位,则为真;
-w<文件>:如果文件可写,则为真;
-x<文件>:如果文件可执行,则为真。

实例

linux中shell编程中的test常见用法:

判断表达式

1
2
3
4
5
if test     #表达式为真
if test ! #表达式为假
test 表达式1 –a 表达式2 #两个表达式都为真
test 表达式1 –o 表达式2 #两个表达式有一个为真
test 表达式1 ! 表达式2 #条件求反

判断字符串

1
2
3
4
test –n 字符串    #字符串的长度非零
test –z 字符串 #字符串的长度是否为零
test 字符串1=字符串2 #字符串是否相等,若相等返回true
test 字符串1!=字符串2 #字符串是否不等,若不等反悔false

判断整数

1
2
3
4
5
6
test 整数1 -eq 整数2    #整数相等
test 整数1 -ge 整数2 #整数1大于等于整数2
test 整数1 -gt 整数2 #整数1大于整数2
test 整数1 -le 整数2 #整数1小于等于整数2
test 整数1 -lt 整数2 #整数1小于整数2
test 整数1 -ne 整数2 #整数1不等于整数2

判断文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
test File1 –ef File2    两个文件是否为同一个文件,可用于硬连接。主要判断两个文件是否指向同一个inode。
test File1 –nt File2 判断文件1是否比文件2新
test File1 –ot File2 判断文件1比是否文件2旧
test –b file #文件是否块设备文件
test –c File #文件并且是字符设备文件
test –d File #文件并且是目录
test –e File #文件是否存在 (常用)
test –f File #文件是否为正规文件 (常用)
test –g File #文件是否是设置了组id
test –G File #文件属于的有效组ID
test –h File #文件是否是一个符号链接(同-L)
test –k File #文件是否设置了Sticky bit位
test –b File #文件存在并且是块设备文件
test –L File #文件是否是一个符号链接(同-h)
test –o File #文件的属于有效用户ID
test –p File #文件是一个命名管道
test –r File #文件是否可读
test –s File #文件是否是非空白文件
test –t FD #文件描述符是在一个终端打开的
test –u File #文件存在并且设置了它的set-user-id位
test –w File #文件是否存在并可写
test –x File #文件属否存在并可执行

tftp

在本机和tftp服务器之间使用TFTP协议传输文件

tftp命令 用在本机和tftp服务器之间使用TFTP协议传输文件。

TFTP是用来下载远程文件的最简单网络协议,它其于UDP协议而实现。嵌入式linux的tftp开发环境包括两个方面:一是linux服务器端的tftp-server支持,二是嵌入式目标系统的tftp-client支持。因为u-boot本身内置支持tftp-client,所以嵌入式目标系统端就不用配置了。下面就详细介绍一下linux服务器端tftp-server的配置。

语法

1
tftp(选项)(参数)

选项

1
2
3
4
-c:指定与tftp服务器连接成功后,立即要执行的指令;
-m:指定文件传输模式。可以是ASCII或者Binary;
-v:显示指令详细执行过程;
-V:显示指令版本信息。

参数

主机:指定tftp要联机的tftp服务器的ip地址或主机名。

实例

1、安装tftp服务器

需要安装xinetd、tftp和tftp-server 3个软件

如果能上网,通过yum安装:

1
2
3
yum install xinetd
yum install tftp
yum install tftp-server

如果不能上网,可以直接安装提供的rpm包:

1
2
3
rpm -ivh xinetd-2.3.14-18.fc9.i386.rpm
rpm -ivh tftp-0.48-3.fc9.i386.rpm
rpm -ivh tftp-server-0.48-3.fc9.i386.rpm

2、配置tftp服务器

修改/etc/xinetd.d/tftp文件,将其中的disable=yes改为disable=no。主要是设置TFTP服务器的根目录,开启服务。修改后的文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
service tftp
{
socket_type =dgram
protocol =udp
wait =yes
user =root
server =/usr/sbin/in.tftpd
server_args =-s /home/mike/tftpboot -c
disable =no
per_source =11
cps =100 2
flags =IPv4
}

说明:修改项server_args= -s <path> -c,其中 <path> 处可以改为你的tftp-server的根目录,参数-s指定chroot,-c指定了可以创建文件。

3、启动tftp服务器并关闭防火墙

1
2
3
4
5
/etc/init.d/iptables stop        # 关闭防火墙
sudo /sbin/service xinetd start

service xinetd restart
/etc/init.d/xinetd start

看到启动[OK]就可以了

4、查看tftp服务是否开启

1
netstat -a | grep tftp

显示结果为udp 0 0 *:tftp *:*表明服务已经开启,就表明tftp配置成功了。

5、tftp使用

复制一个文件到tftp服务器目录,然后在主机启动tftp软件,进行简单测试。

1
2
3
4
5
tftp 192.168.1.2
tftp>get <download file>

tftp>put <upload file>
tftp>q

6、tftp命令用法如下

1
tftp your-ip-address

进入TFTP操作:

  • connect:连接到远程tftp服务器
  • mode:文件传输模式
  • put:上传文件
  • get:下载文件
  • quit:退出
  • verbose:显示详细的处理信息
  • tarce:显示包路径
  • status:显示当前状态信息
  • binary:二进制传输模式
  • ascii:ascii 传送模式
  • rexmt:设置包传输的超时时间
  • timeout:设置重传的超时时间
  • help:帮助信息
  • ? :帮助信息

7、如果老是出现“AVC Denial, click icon to view”的错误,并不能传输文件,需要作如下修改

修改/etc/sysconfig/selinux,将SELINUX设定为disable,使用命令setenforce 0让selinux配置文件生效。

8、Busybox中tftp命令的用法

命令格式为:

1
tftp [option] ... host [port]

如果要下载或上传文件的话是一定要用这些option的。

1
2
3
4
-g 表示下载文件 (get)
-p 表示上传文件 (put)
-l 表示本地文件名 (local file)
-r 表示远程主机的文件名 (remote file)

例如,要从远程主机192.168.1.2上下载 embedexpert,则应输入以下命令

1
tftp -g -r embedexpert 192.168.1.2

ulimit

控制shell程序的资源

ulimit命令 用来限制系统用户对shell资源的访问。如果不懂什么意思,下面一段内容可以帮助你理解:

假设有这样一种情况,当一台 Linux 主机上同时登陆了 10 个人,在系统资源无限制的情况下,这 10 个用户同时打开了 500 个文档,而假设每个文档的大小有 10M,这时系统的内存资源就会受到巨大的挑战。

而实际应用的环境要比这种假设复杂的多,例如在一个嵌入式开发环境中,各方面的资源都是非常紧缺的,对于开启文件描述符的数量,分配堆栈的大 小,CPU 时间,虚拟内存大小,等等,都有非常严格的要求。资源的合理限制和分配,不仅仅是保证系统可用性的必要条件,也与系统上软件运行的性能有着密不可分的联 系。这时,ulimit 可以起到很大的作用,它是一种简单并且有效的实现资源限制的方式。

ulimit 用于限制 shell 启动进程所占用的资源,支持以下各种类型的限制:所创建的内核文件的大小、进程数据块的大小、Shell 进程创建文件的大小、内存锁住的大小、常驻内存集的大小、打开文件描述符的数量、分配堆栈的最大大小、CPU 时间、单个用户的最大线程数、Shell 进程所能使用的最大虚拟内存。同时,它支持硬资源和软资源的限制。

作为临时限制,ulimit 可以作用于通过使用其命令登录的 shell 会话,在会话终止时便结束限制,并不影响于其他 shell 会话。而对于长期的固定限制,ulimit 命令语句又可以被添加到由登录 shell 读取的文件中,作用于特定的 shell 用户。

语法

1
ulimit(选项)

选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-a:显示目前资源限制的设定;
-c <core文件上限>:设定core文件的最大值,单位为区块;
-d <数据节区大小>:程序数据节区的最大值,单位为KB;
-e 默认进程优先级, 值越小优先级越高
-f <文件大小>:shell所能建立的最大文件,单位为区块;
-H:设定资源的硬性限制,也就是管理员所设下的限制;
-m <内存大小>:指定可使用内存的上限,单位为KB;
-n <文件数目>:指定同一时间最多可开启的文件数;
-p <缓冲区大小>:指定管道缓冲区的大小,单位512字节;
-s <堆叠大小>:指定堆叠的上限,单位为KB;
-S:设定资源的弹性限制;
-t <CPU时间>:指定CPU使用时间的上限,单位为秒;
-u <程序数目>:用户最多可开启的程序数目;
-v <虚拟内存大小>:指定可使用的虚拟内存上限,单位为KB。

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@localhost ~]# ulimit -a
core file size (blocks, -c) 0 #core文件的最大值为100 blocks。
data seg size (kbytes, -d) unlimited #进程的数据段可以任意大。
scheduling priority (-e) 0
file size (blocks, -f) unlimited #文件可以任意大。
pending signals (-i) 98304 #最多有98304个待处理的信号。
max locked memory (kbytes, -l) 32 #一个任务锁住的物理内存的最大值为32KB。
max memory size (kbytes, -m) unlimited #一个任务的常驻物理内存的最大值。
open files (-n) 1024 #一个任务最多可以同时打开1024的文件。
pipe size (512 bytes, -p) 8 #管道的最大空间为4096字节。
POSIX message queues (bytes, -q) 819200 #POSIX的消息队列的最大值为819200字节。
real-time priority (-r) 0
stack size (kbytes, -s) 10240 #进程的栈的最大值为10240字节。
cpu time (seconds, -t) unlimited #进程使用的CPU时间。
max user processes (-u) 98304 #当前用户同时打开的进程(包括线程)的最大个数为98304。
virtual memory (kbytes, -v) unlimited #没有限制进程的最大地址空间。
file locks (-x) unlimited #所能锁住的文件的最大个数没有限制。

uniq

显示或忽略重复的行。

概要

1
uniq [OPTION]... [INPUT [OUTPUT]]

主要用途

  • 将输入文件(或标准输入)中邻近的重复行写入到输出文件(或标准输出)中。
  • 当没有选项时,邻近的重复行将合并为一个。

选项

1
2
3
4
5
6
7
8
9
10
11
12
13
-c, --count                在每行开头增加重复次数。
-d, --repeated 所有邻近的重复行只被打印一次。
-D 所有邻近的重复行将全部打印。
--all-repeated[=METHOD] 类似于 -D,但允许每组之间以空行分割。METHOD取值范围{none(默认),prepend,separate}。
-f, --skip-fields=N 跳过对前N个列的比较。
--group[=METHOD] 显示所有行,允许每组之间以空行分割。METHOD取值范围:{separate(默认),prepend,append,both}。
-i, --ignore-case 忽略大小写的差异。
-s, --skip-chars=N 跳过对前N个字符的比较。
-u, --unique 只打印非邻近的重复行。
-z, --zero-terminated 设置行终止符为NUL(空),而不是换行符。
-w, --check-chars=N 只对每行前N个字符进行比较。
--help 显示帮助信息并退出。
--version 显示版本信息并退出。

参数

INPUT(可选):输入文件,不提供时为标准输入。

OUTPUT(可选):输出文件,不提供时为标准输出。

返回值

返回0表示成功,返回非0值表示失败。

例子

注意:命令2和命令3结果一样,命令1仅作了相邻行的去重。

1
2
3
uniq file.txt
sort file.txt | uniq
sort -u file.txt

只显示单一行,区别在于是否执行排序:

1
2
uniq -u file.txt
sort file.txt | uniq -u

统计各行在文件中出现的次数:

1
sort file.txt | uniq -c

在文件中找出重复的行:

1
sort file.txt | uniq -d

注意

  1. uniq只检测邻近的行是否重复,sort -u将输入文件先排序然后再处理重复行。

  2. 该命令是GNU coreutils包中的命令,相关的帮助信息请查看man -s 1 uniqinfo coreutils 'uniq invocation'

file

用来探测给定文件的类型

file命令 用来探测给定文件的类型。file命令对文件的检查分为文件系统、魔法幻数检查和语言检查3个过程。

语法

1
file(选项)(参数)

选项

1
2
3
4
5
6
7
-b:列出辨识结果时,不显示文件名称;
-c:详细显示指令执行过程,便于排错或分析程序执行的情形;
-f<名称文件>:指定名称文件,其内容有一个或多个文件名称时,让file依序辨识这些文件,格式为每列一个文件名称;
-L:直接显示符号连接所指向的文件类别;
-m<魔法数字文件>:指定魔法数字文件;
-v:显示版本信息;
-z:尝试去解读压缩文件的内容。

参数

文件:要确定类型的文件列表,多个文件之间使用空格分开,可以使用shell通配符匹配多个文件。

实例

显示文件类型

1
2
3
4
5
6
7
8
9
10
11
[root@localhost ~]# file install.log
install.log: UTF-8 Unicode text

[root@localhost ~]# file -b install.log <== 不显示文件名称
UTF-8 Unicode text

[root@localhost ~]# file -i install.log <== 显示MIME类别。
install.log: text/plain; charset=utf-8

[root@localhost ~]# file -b -i install.log
text/plain; charset=utf-8

显示符号链接的文件类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@localhost ~]# ls -l /var/mail
lrwxrwxrwx 1 root root 10 08-13 00:11 /var/mail -> spool/mail

[root@localhost ~]# file /var/mail
/var/mail: symbolic link to `spool/mail'

[root@localhost ~]# file -L /var/mail
/var/mail: directory

[root@localhost ~]# file /var/spool/mail
/var/spool/mail: directory

[root@localhost ~]# file -L /var/spool/mail
/var/spool/mail: directory

free

显示内存的使用情况

free命令 可以显示当前系统未使用的和已使用的内存数目,还可以显示被内核使用的内存缓冲区。

语法

1
free(选项)

选项

1
2
3
4
5
6
7
8
-b # 以Byte为单位显示内存使用情况;
-k # 以KB为单位显示内存使用情况;
-m # 以MB为单位显示内存使用情况;
-g # 以GB为单位显示内存使用情况。
-o # 不显示缓冲区调节列;
-s<间隔秒数> # 持续观察内存使用状况;
-t # 显示内存总和列;
-V # 显示版本信息。

实例

1
2
free -t    # 以总和的形式显示内存的使用信息
free -s 10 # 周期性的查询内存使用信息,每10s 执行一次命令

显示内存使用情况

1
2
3
4
5
free -m
total used free shared buffers cached
Mem: 2016 1973 42 0 163 1497
-/+ buffers/cache: 312 1703
Swap: 4094 0 4094

第一部分Mem行解释:

1
2
3
4
5
6
total:内存总数;
used:已经使用的内存数;
free:空闲的内存数;
shared:当前已经废弃不用;
buffers Buffer:缓存内存数;
cached Page:缓存内存数。

关系:total = used + free

第二部分(-/+ buffers/cache)解释:

1
2
(-buffers/cache) used内存数:第一部分Mem行中的 used – buffers – cached
(+buffers/cache) free内存数: 第一部分Mem行中的 free + buffers + cached

可见-buffers/cache反映的是被程序实实在在吃掉的内存,而+buffers/cache反映的是可以挪用的内存总数。

第三部分是指交换分区。

输出结果的第四行是交换分区SWAP的,也就是我们通常所说的虚拟内存。
区别:第二行(mem)的used/free与第三行(-/+ buffers/cache) used/free的区别。 这两个的区别在于使用的角度来看,第一行是从OS的角度来看,因为对于OS,buffers/cached 都是属于被使用,所以他的可用内存是2098428KB,已用内存是30841684KB,其中包括,内核(OS)使用+Application(X, oracle,etc)使用的+buffers+cached.

第三行所指的是从应用程序角度来看,对于应用程序来说,buffers/cached 是等于可用的,因为buffer/cached是为了提高文件读取的性能,当应用程序需在用到内存的时候,buffer/cached会很快地被回收。

所以从应用程序的角度来说,可用内存=系统free memory+buffers+cached。
如本机情况的可用内存为:

18007156=2098428KB+4545340KB+11363424KB

接下来解释什么时候内存会被交换,以及按什么方交换。

当可用内存少于额定值的时候,就会开会进行交换。如何看额定值:

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
cat /proc/meminfo

MemTotal: 16140816 kB
MemFree: 816004 kB
MemAvailable: 2913824 kB
Buffers: 17912 kB
Cached: 2239076 kB
SwapCached: 0 kB
Active: 12774804 kB
Inactive: 1594328 kB
Active(anon): 12085544 kB
Inactive(anon): 94572 kB
Active(file): 689260 kB
Inactive(file): 1499756 kB
Unevictable: 116888 kB
Mlocked: 116888 kB
SwapTotal: 8191996 kB
SwapFree: 8191996 kB
Dirty: 56 kB
Writeback: 0 kB
AnonPages: 12229228 kB
Mapped: 117136 kB
Shmem: 58736 kB
Slab: 395568 kB
SReclaimable: 246700 kB
SUnreclaim: 148868 kB
KernelStack: 30496 kB
PageTables: 165104 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 16262404 kB
Committed_AS: 27698600 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 311072 kB
VmallocChunk: 34350899200 kB
HardwareCorrupted: 0 kB
AnonHugePages: 3104768 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 225536 kB
DirectMap2M: 13279232 kB
DirectMap1G: 5242880 kB

交换将通过三个途径来减少系统中使用的物理页面的个数: 

  1. 减少缓冲与页面cache的大小,
  2. 将系统V类型的内存页面交换出去, 
  3. 换出或者丢弃页面。(Application 占用的内存页,也就是物理内存不足)。

事实上,少量地使用swap是不是影响到系统性能的。

那buffers和cached都是缓存,两者有什么区别呢?

为了提高磁盘存取效率, Linux做了一些精心的设计, 除了对dentry进行缓存(用于VFS,加速文件路径名到inode的转换), 还采取了两种主要Cache方式:

Buffer Cache和Page Cache。前者针对磁盘块的读写,后者针对文件inode的读写。这些Cache有效缩短了 I/O系统调用(比如read,write,getdents)的时间。
磁盘的操作有逻辑级(文件系统)和物理级(磁盘块),这两种Cache就是分别缓存逻辑和物理级数据的。

Page cache实际上是针对文件系统的,是文件的缓存,在文件层面上的数据会缓存到page cache。文件的逻辑层需要映射到实际的物理磁盘,这种映射关系由文件系统来完成。当page cache的数据需要刷新时,page cache中的数据交给buffer cache,因为Buffer Cache就是缓存磁盘块的。但是这种处理在2.6版本的内核之后就变的很简单了,没有真正意义上的cache操作。

Buffer cache是针对磁盘块的缓存,也就是在没有文件系统的情况下,直接对磁盘进行操作的数据会缓存到buffer cache中,例如,文件系统的元数据都会缓存到buffer cache中。

简单说来,page cache用来缓存文件数据,buffer cache用来缓存磁盘数据。在有文件系统的情况下,对文件操作,那么数据会缓存到page cache,如果直接采用dd等工具对磁盘进行读写,那么数据会缓存到buffer cache。

所以我们看linux,只要不用swap的交换空间,就不用担心自己的内存太少.如果常常swap用很多,可能你就要考虑加物理内存了.这也是linux看内存是否够用的标准.

如果是应用服务器的话,一般只看第二行,+buffers/cache,即对应用程序来说free的内存太少了,也是该考虑优化程序或加内存了。

ftp

用来设置文件系统相关功能

ftp命令 用来设置文件系统相关功能。ftp服务器在网上较为常见,Linux ftp命令的功能是用命令的方式来控制在本地机和远程机之间传送文件,这里详细介绍Linux ftp命令的一些经常使用的命令,相信掌握了这些使用Linux进行ftp操作将会非常容易。

语法

1
ftp(选项)(参数)

选项

1
2
3
4
5
-d:详细显示指令执行过程,便于排错或分析程序执行的情况;
-i:关闭互动模式,不询问任何问题;
-g:关闭本地主机文件名称支持特殊字符的扩充特性;
-n:不使用自动登录;
-v:显示指令执行过程。

参数

主机:指定要连接的FTP服务器的主机名或ip地址。

实例

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
ftp> ascii  # 设定以ASCII方式传送文件(缺省值) 
ftp> bell # 每完成一次文件传送,报警提示.
ftp> binary # 设定以二进制方式传送文件.
ftp> bye # 终止主机FTP进程,并退出FTP管理方式.
ftp> case # 当为ON时,用MGET命令拷贝的文件名到本地机器中,全部转换为小写字母.
ftp> cd # 同UNIX的CD命令.
ftp> cdup # 返回上一级目录.
ftp> chmod # 改变远端主机的文件权限.
ftp> close # 终止远端的FTP进程,返回到FTP命令状态, 所有的宏定义都被删除.
ftp> delete # 删除远端主机中的文件.
ftp> dir [remote-directory] [local-file] # 列出当前远端主机目录中的文件.如果有本地文件,就将结果写至本地文件.
ftp> get [remote-file] [local-file] # 从远端主机中传送至本地主机中.
ftp> help [command] # 输出命令的解释.
ftp> lcd # 改变当前本地主机的工作目录,如果缺省,就转到当前用户的HOME目录.
ftp> ls [remote-directory] [local-file] # 同DIR.
ftp> macdef # 定义宏命令.
ftp> mdelete [remote-files] # 删除一批文件.
ftp> mget [remote-files] # 从远端主机接收一批文件至本地主机.
ftp> mkdir directory-name # 在远端主机中建立目录.
ftp> mput local-files # 将本地主机中一批文件传送至远端主机.
ftp> open host [port] # 重新建立一个新的连接.
ftp> prompt # 交互提示模式.
ftp> put local-file [remote-file] # 将本地一个文件传送至远端主机中.
ftp> pwd # 列出当前远端主机目录.
ftp> quit # 同BYE.
ftp> recv remote-file [local-file] # 同GET.
ftp> rename [from] [to] # 改变远端主机中的文件名.
ftp> rmdir directory-name # 删除远端主机中的目录.
ftp> send local-file [remote-file] # 同PUT.
ftp> status # 显示当前FTP的状态.
ftp> system # 显示远端主机系统类型.
ftp> user user-name [password] [account] # 重新以别的用户名登录远端主机.
ftp> ? [command] # 同HELP. [command]指定需要帮助的命令名称。如果没有指定 command,ftp 将显示全部命令的列表。
ftp> ! # 从 ftp 子系统退出到外壳。

关闭FTP连接

1
2
3
bye
exit
quit

下载文件

1
2
ftp> get readme.txt # 下载 readme.txt 文件
ftp> mget *.txt # 下载

上传文件

1
2
ftp> put /path/readme.txt # 上传 readme.txt 文件
ftp> mput *.txt # 可以上传多个文件

git

是目前世界上最先进的分布式版本控制系统

git命令 很多人都知道,Linus在1991年创建了开源的Linux,从此,Linux系统不断发展,已经成为最大的服务器系统软件了。

Linus虽然创建了Linux,但Linux的壮大是靠全世界热心的志愿者参与的,这么多人在世界各地为Linux编写代码,那Linux的代码是如何管理的呢?

事实是,在2002年以前,世界各地的志愿者把源代码文件通过diff的方式发给Linus,然后由Linus本人通过手工方式合并代码!

你也许会想,为什么Linus不把Linux代码放到版本控制系统里呢?不是有CVS、SVN这些免费的版本控制系统吗?因为Linus坚定地反对CVS和SVN,这些集中式的版本控制系统不但速度慢,而且必须联网才能使用。有一些商用的版本控制系统,虽然比CVS、SVN好用,但那是付费的,和Linux的开源精神不符。

不过,到了2002年,Linux系统已经发展了十年了,代码库之大让Linus很难继续通过手工方式管理了,社区的弟兄们也对这种方式表达了强烈不满,于是Linus选择了一个商业的版本控制系统BitKeeper,BitKeeper的东家BitMover公司出于人道主义精神,授权Linux社区免费使用这个版本控制系统。

安定团结的大好局面在2005年就被打破了,原因是Linux社区牛人聚集,不免沾染了一些梁山好汉的江湖习气。开发Samba的Andrew试图破解BitKeeper的协议(这么干的其实也不只他一个),被BitMover公司发现了(监控工作做得不错!),于是BitMover公司怒了,要收回Linux社区的免费使用权。

Linus可以向BitMover公司道个歉,保证以后严格管教弟兄们,嗯,这是不可能的。实际情况是这样的:

Linus花了两周时间自己用C写了一个分布式版本控制系统,这就是Git!一个月之内,Linux系统的源码已经由Git管理了!牛是怎么定义的呢?大家可以体会一下。

Git迅速成为最流行的分布式版本控制系统,尤其是2008年,GitHub网站上线了,它为开源项目免费提供Git存储,无数开源项目开始迁移至GitHub,包括jQuery,PHP,Ruby等等。

历史就是这么偶然,如果不是当年BitMover公司威胁Linux社区,可能现在我们就没有免费而超级好用的Git了。

Git常用命令清单

语法

1
2
3
4
5
git [--version] [--help] [-C <path>] [-c name=value]
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p | --paginate | --no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
<command> [<args>]

选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
add              将文件内容添加到索引
bisect 通过二进制查找引入错误的更改
branch 列出,创建或删除分支
checkout 检查分支或路径到工作树
clone 将存储库克隆到新目录中
commit 将更改记录到存储库
diff 显示提交,提交和工作树等之间的更改
fetch 从另一个存储库下载对象和引用
grep 打印匹配图案的行
init 创建一个空的Git仓库或重新初始化一个现有的
log 显示提交日志
merge 加入两个或更多的开发历史
mv 移动或重命名文件,目录或符号链接
pull 从另一个存储库或本地分支获取并合并
push 更新远程引用以及相关对象
rebase 转发端口本地提交到更新的上游头
reset 将当前HEAD复位到指定状态
rm 从工作树和索引中删除文件
show 显示各种类型的对象
status 显示工作树状态
tag 创建,列出,删除或验证使用GPG签名的标签对象

例子

init

git init #初始化

status

git status #获取状态

add

git add file # .或*代表全部添加
git rm --cached <added_file_to_undo> # 在commit之前撤销git add操作
git reset head # 好像比上面git rm --cached更方便

commit

git commit -m "message" #此处注意乱码

remote

git remote add origin git@github.com:JSLite/test.git #添加源

push

1
2
3
git push -u origin master # push同事设置默认跟踪分支  
git push origin master
git push -f origin master # 强制推送文件,缩写 -f(全写--force)

clone

git clone git://github.com/JSLite/JSLite.js.git
git clone git://github.com/JSLite/JSLite.js.git mypro #克隆到自定义文件夹
git clone [user@]example.com:path/to/repo.git/ #SSH协议还有另一种写法。

git clone支持多种协议,除了HTTP(s)以外,还支持SSH、Git、本地文件协议等,下面是一些例子。git clone <版本库的网址> <本地目录名>

1
2
3
4
5
6
7
$ git clone http[s]://example.com/path/to/repo.git/
$ git clone ssh://example.com/path/to/repo.git/
$ git clone git://example.com/path/to/repo.git/
$ git clone /opt/git/project.git
$ git clone file:///opt/git/project.git
$ git clone ftp[s]://example.com/path/to/repo.git/
$ git clone rsync://example.com/path/to/repo.git/

配置

首先是配置帐号信息 ssh -T git@github.com 测试。

修改项目中的个人信息

1
2
3
4
git help config # 获取帮助信息,查看修改个人信息的参数  
git config --global user.name "小弟调调" # 修改全局名字
git config --global user.email "wowohoo@qq.com" # 修改全局邮箱
git config --list # 查看配置的信息

配置自动换行

自动转换坑太大,提交到git是自动将换行符转换为lf

1
git config --global core.autocrlf input

常见使用场景

创建SSH密钥

这个密钥用来跟 github 通信,在本地终端里生成然后上传到 github

1
2
3
ssh-keygen -t rsa -C 'wowohoo@qq.com' # 生成密钥  
ssh-keygen -t rsa -C "wowohoo@qq.com" -f ~/.ssh/ww_rsa # 指定生成目录文件名字
ssh -T git@github.com # 测试是否成功

多账号ssh配置

1.生成指定名字的密钥

ssh-keygen -t rsa -C "邮箱地址" -f ~/.ssh/jslite_rsa
会生成 jslite_rsajslite_rsa.pub 这两个文件

2.密钥复制到托管平台上

vim ~/.ssh/jslite_rsa.pub
打开公钥文件 jslite_rsa.pub ,并把内容复制至代码托管平台上

3.修改config文件

vim ~/.ssh/config #修改config文件,如果没有创建 config

1
2
3
4
5
6
7
8
9
10
11
12
Host jslite.github.com
HostName github.com
User git
IdentityFile ~/.ssh/jslite_rsa

Host work.github.com
HostName github.com
# Port 服务器open-ssh端口(默认:22,默认时一般不写此行)
# PreferredAuthentications 配置登录时用什么权限认证
# publickey|password publickey|keyboard-interactive等
User git
IdentityFile ~/.ssh/work_rsa
  • Host 这里是个别名可以随便命名
  • HostName 一般是网站如:git@ss.github.com:username/repo.git 填写 github.com
  • User 通常填写git
  • IdentityFile 使用的公钥文件地址

4.测试

1
2
3
ssh -T git@jslite.github.com  # `@`后面跟上定义的Host  
ssh -T work.github.com # 通过别名测试
ssh -i ~/公钥文件地址 Host别名 # 如 ssh -i ~/.ssh/work_rsa work.github.com

5.使用

1
2
3
4
5
# 原来的写法
git clone git@github.com:<jslite的用户名>/learngit.git
# 现在的写法
git clone git@jslite.github.com:<jslite的用户名>/learngit.git
git clone git@work.github.com:<work的用户名>/learngit.git

5.注意

如果你修改了id_rsa的名字,你需要将ssh key添加到SSH agent中,如:

1
2
3
4
ssh-add ~/.ssh/jslite_rsa
ssh-add -l # 查看所有的key
ssh-add -D # 删除所有的key
ssh-add -d ~/.ssh/jslite_rsa # 删除指定的key

免密码登录远程服务器

1
2
$ ssh-keygen -t rsa -P '' -f ~/.ssh/aliyunserver.key
$ ssh-copy-id -i ~/.ssh/aliyunserver.key.pub root@192.168.182.112 # 这里需要输入密码一次

编辑 ~/.ssh/config

1
2
3
4
5
Host aliyun1
HostName 192.168.182.112
User root
PreferredAuthentications publickey
IdentityFile ~/.ssh/aliyunserver.key

上面配置完了,可以通过命令登录,不需要输入IP地址和密码 ssh aliyun1

https协议下提交代码免密码

1
git clone https://github.com/username/rep.git

通过上面方式克隆可能需要密码,解决办法:进入当前克隆的项目 vi rep/.git/config 编辑 config, 按照下面方式修改,你就可以提交代码不用输入密码了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
- url = https://github.com/username/rep.git
+ url = https://用户名:密码@github.com/username/rep.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master

文件推向3个git库

1. 增加3个远程库地址

1
2
3
git remote add origin https://github.com/JSLite/JSLite.git  
git remote set-url --add origin https://gitlab.com/wang/JSLite.js.git
git remote set-url --add origin https://oschina.net/wang/JSLite.js.git

2. 删除其中一个 set-url 地址

1
2
3
usage: git remote set-url [--push] <name> <newurl> [<oldurl>]
or: git remote set-url --add <name> <newurl>
or: git remote set-url --delete <name> <url>

git remote set-url --delete origin https://oschina.net/wang/JSLite.js.git

3.推送代码

1
2
git push origin master
git push -f origin master # 强制推送

4.拉代码

只能拉取 origin 里的一个url地址,这个fetch-url
默认为你添加的到 origin的第一个地址

1
2
3
4
5
6
7
8
git pull origin master   
git pull --all # 获取远程所有内容包括tag
git pull origin next:master # 取回origin主机的next分支,与本地的master分支合并
git pull origin next # 远程分支是与当前分支合并

# 上面一条命令等同于下面两条命令
git fetch origin
git merge origin/next

如果远程主机删除了某个分支,默认情况下,git pull 不会在拉取远程分支的时候,删除对应的本地分支。这是为了防止,由于其他人操作了远程主机,导致git pull不知不觉删除了本地分支。
但是,你可以改变这个行为,加上参数 -p 就会在本地删除远程已经删除的分支。

1
2
3
4
$ git pull -p
# 等同于下面的命令
$ git fetch --prune origin
$ git fetch -p

5.更改pull

只需要更改config文件里,那三个url的顺序即可,fetch-url会直接对应排行第一的那个utl连接。

修改远程仓库地址

1
2
git remote remove origin  # 删除该远程路径  
git remote add origin git@jslite.github.com:JSLite/JSLite.git # 添加远程路径

撤销远程记录

1
2
git reset --hard HEAD~1 # 撤销一条记录   
git push -f origin HEAD:master # 同步到远程仓库

放弃本地的文件修改

1
git reset --hard FETCH_HEAD # FETCH_HEAD表示上一次成功git pull之后形成的commit点。然后git pull

git reset --hard FETCH_HEAD 出现错误

1
2
3
4
5
6
git pull
You are not currently on a branch, so I cannot use any
'branch.<branchname>.merge' in your configuration file.
Please specify which remote branch you want to use on the command
line and try again (e.g. 'git pull <repository> <refspec>').
See git-pull(1) FOR details.

解决方法:

1
2
git checkout -b temp # 新建+切换到temp分支 
git checkout master

最简单放弃本地修改内容

1
2
3
4
5
6
# 如果有的修改以及加入暂存区的话
git reset --hard
# 还原所有修改,不会删除新增的文件
git checkout .
# 下面命令会删除新增的文件
git clean -xdf

通过存储暂存区stash,在删除暂存区的方法放弃本地修改。

1
git stash && git stash drop 

回滚到某个commit提交

1
2
git revert HEAD~1 # 撤销一条记录 会弹出 commit 编辑
git push # 提交回滚

回退到某一个版本

1
2
3
4
git reset --hard <hash>
# 例如 git reset --hard a3hd73r
# --hard代表丢弃工作区的修改,让工作区与版本代码一模一样,与之对应,
# --soft参数代表保留工作区的修改。

去掉某个commit

1
2
# 实质是新建了一个与原来完全相反的commit,抵消了原来commit的效果
git revert <commit-hash>

新建一个空分支

1
2
3
4
5
6
# 这种方式新建的分支(gh-pages)是没有 commit 记录的
git checkout --orphan gh-pages
# 删除新建的gh-pages分支原本的内容,如果不删除,提交将作为当前分支的第一个commit
git rm -rf .
# 查看一下状态 有可能上面一条命令,没有删除还没有提交的的文件
git state

合并多个commit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 这个命令,将最近4个commit合并为1个,HEAD代表当前版本。
# 将进入VIM界面,你可以修改提交信息。
git rebase -i HEAD~4
# 可以看到其中分为两个部分,上方未注释的部分是填写要执行的指令,
# 而下方注释的部分则是指令的提示说明。指令部分中由前方的命令名称、commit hash 和 commit message 组成
# 当前我们只要知道 pick 和 squash 这两个命令即可。
# --> pick 的意思是要会执行这个 commit
# --> squash 的意思是这个 commit 会被合并到前一个commit

# 我们将 需要保留的 这个 commit 前方的命令改成 squash 或 s,然后输入:wq以保存并退出
# 这是我们会看到 commit message 的编辑界面

# 其中, 非注释部分就是两次的 commit message, 你要做的就是将这两个修改成新的 commit message。
#
# 输入wq保存并推出, 再次输入git log查看 commit 历史信息,你会发现这两个 commit 已经合并了。
# 将修改强制推送到前端
git push -f origin master

修改远程Commit记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
git commit --amend
# amend只能修改没有提交到线上的,最后一次commit记录
git rebase -i HEAD~3
# 表示要修改当前版本的倒数第三次状态
# 将要更改的记录行首单词 pick 改为 edit
pick 96dc3f9 doc: Update quick-start.md
pick f1cce8a test(Transition):Add transition test (#47)
pick 6293516 feat(Divider): Add Divider component.
# Rebase eeb03a4..6293516 onto eeb03a4 (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit

保存并退出,会弹出下面提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# You can amend the commit now, with
#
# git commit --amend
#
# Once you are satisfied with your changes, run
#
# git rebase --continue

# 通过这条命令进入编辑页面更改commit,保存退出
git commit --amend
# 保存退出确认修改,继续执行 rebase,
git rebase --continue
# 如果修改多条记录反复执行上面两条命令直到完成所有修改

# 最后,确保别人没有提交进行push,最好不要加 -f 强制推送
git push -f origin master

添加忽略文件

1
echo node_modules/ >> .gitignore

利用commit关闭一个issue

这个功能在Github上可以玩儿,Gitlab上特别老的版本不能玩儿哦,那么如何跟随着commit关闭一个issue呢? 在confirm merge的时候可以使用一下命令来关闭相关issue:

fixes #xxxfixed #xxxfix #xxxcloses #xxxclose #xxxclosed #xxx

同步fork的上游仓库

Github教程同步fork教程在Github上同步一个分支(fork)

设置添加多个远程仓库地址。

在同步之前,需要创建一个远程点指向上游仓库(repo).如果你已经派生了一个原始仓库,可以按照如下方法做。

1
2
3
4
5
6
7
8
9
10
11
12
$ git remote -v
# List the current remotes (列出当前远程仓库)
# origin https://github.com/user/repo.git (fetch)
# origin https://github.com/user/repo.git (push)
$ git remote add upstream https://github.com/otheruser/repo.git
# Set a new remote (设置一个新的远程仓库)
$ git remote -v
# Verify new remote (验证新的原唱仓库)
# origin https://github.com/user/repo.git (fetch)
# origin https://github.com/user/repo.git (push)
# upstream https://github.com/otheruser/repo.git (fetch)
# upstream https://github.com/otheruser/repo.git (push)

同步更新仓库内容

同步上游仓库到你的仓库需要执行两步:首先你需要从远程拉去,之后你需要合并你希望的分支到你的本地副本分支。从上游的存储库中提取分支以及各自的提交内容。 master 将被存储在本地分支机构 upstream/master

1
2
3
4
5
6
7
git fetch upstream
# remote: Counting objects: 75, done.
# remote: Compressing objects: 100% (53/53), done.
# remote: Total 62 (delta 27), reused 44 (delta 9)
# Unpacking objects: 100% (62/62), done.
# From https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY
# * [new branch] master -> upstream/master

检查你的 fork’s 本地 master 分支

1
2
git checkout master
# Switched to branch 'master'

合并来自 upstream/master 的更改到本地 master 分支上。 这使你的前 fork’s master 分支与上游资源库同步,而不会丢失你本地修改。

1
2
3
4
5
6
7
8
git merge upstream/master
# Updating a422352..5fdff0f
# Fast-forward
# README | 9 -------
# README.md | 7 ++++++
# 2 files changed, 7 insertions(+), 9 deletions(-)
# delete mode 100644 README
# create mode 100644 README.md

批量修改历史commit中的名字和邮箱

1.克隆仓库

注意参数,这个不是普通的clone,clone下来的仓库并不能参与开发

1
2
git clone --bare https://github.com/user/repo.git
cd repo.git

2.命令行中运行代码

OLD_EMAIL原来的邮箱
CORRECT_NAME更正的名字
CORRECT_EMAIL更正的邮箱

将下面代码复制放到命令行中执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
git filter-branch -f --env-filter '
OLD_EMAIL="wowohoo@qq.com"
CORRECT_NAME="小弟调调"
CORRECT_EMAIL="更正的邮箱@qq.com"
if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_COMMITTER_NAME="$CORRECT_NAME"
export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_AUTHOR_NAME="$CORRECT_NAME"
export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags

执行过程

1
2
3
Rewrite 160d4df2689ff6df3820563bfd13b5f1fb9ba832 (479/508) (16 seconds passed, remaining 0 predicted)
Ref 'refs/heads/dev' was rewritten
Ref 'refs/heads/master' was rewritten

3.同步到远程仓库

同步到push远程git仓库

1
git push --force --tags origin 'refs/heads/*'

我还遇到了如下面错误,lab默认给master分支加了保护,不允许强制覆盖。Project(项目)->Setting->Repository 菜单下面的Protected branches把master的保护去掉就可以了。修改完之后,建议把master的保护再加回来,毕竟强推不是件好事。

1
remote: GitLab: You are not allowed to force push code to a protected branch on this project.

当上面的push 不上去的时候,先 git pull 确保最新代码

1
2
3
git pull  --allow-unrelated-histories
# 或者指定分枝
git pull origin master --allow-unrelated-histories

查看某个文件历史

1
2
3
4
5
git log --pretty=oneline 文件名  # 列出文件的所有改动历史  
git show c178bf49 # 某次的改动的修改记录
git log -p c178bf49 # 某次的改动的修改记录
git blame 文件名 # 显示文件的每一行是在那个版本最后修改。
git whatchanged 文件名 # 显示某个文件的每个版本提交信息:提交日期,提交人员,版本号,提交备注(没有修改细节)

打造自己的git命令

1
2
3
4
git config --global alias.st status
git config --global alias.br branch
git config --global alias.co checkout
git config --global alias.ci commit

配置好后再输入git命令的时候就不用再输入一大段了,例如我们要查看状态,只需:

1
git st

中文乱码的解决方案

1
git config --global core.quotepath false

新建仓库

init

git init #初始化

status

git status #获取状态

add

git add file # .或*代表全部添加
git rm --cached <added_file_to_undo> # 在commit之前撤销git add操作
git reset head # 好像比上面git rm --cached更方便

commit

git commit -m "message" #此处注意乱码

remote

git remote add origin git@github.com:JSLite/test.git #添加源

push

1
2
3
git push -u origin master # push同事设置默认跟踪分支  
git push origin master
git push -f origin master # 强制推送文件,缩写 -f(全写--force)

clone

git clone git://github.com/JSLite/JSLite.js.git
git clone git://github.com/JSLite/JSLite.js.git mypro #克隆到自定义文件夹
git clone [user@]example.com:path/to/repo.git/ #SSH协议还有另一种写法。

git clone支持多种协议,除了HTTP(s)以外,还支持SSH、Git、本地文件协议等,下面是一些例子。git clone <版本库的网址> <本地目录名>

1
2
3
4
5
6
7
$ git clone http[s]://example.com/path/to/repo.git/
$ git clone ssh://example.com/path/to/repo.git/
$ git clone git://example.com/path/to/repo.git/
$ git clone /opt/git/project.git
$ git clone file:///opt/git/project.git
$ git clone ftp[s]://example.com/path/to/repo.git/
$ git clone rsync://example.com/path/to/repo.git/

本地

help

1
git help config # 获取帮助信息  

add

1
2
git add *   # 跟踪新文件   
git add -u [path] # 添加[指定路径下]已跟踪文件

rm

1
2
3
4
5
rm *&git rm *          # 移除文件  
git rm -f * # 移除文件
git rm --cached * # 取消跟踪
git mv file_from file_to # 重命名跟踪文件
git log # 查看提交记录

commit

1
2
3
4
5
6
7
8
git commit #提交更新   
git commit -m 'message' #提交说明
git commit -a #跳过使用暂存区域,把所有已经跟踪过的文件暂存起来一并提交
git commit --amend #修改最后一次提交
git commit log #查看所有提交,包括没有push的commit
git commit -m "#133" #关联issue 任意位置带上# 符号加上issue号码
git commit -m "fix #133" commit关闭issue
git commit -m '概要描述'$'\n\n''1.详细描述'$'\n''2.详细描述' #提交简要描述和详细描述

reset

1
2
3
4
5
git reset HEAD *  # 取消已经暂存的文件   
git reset --mixed HEAD * # 同上
git reset --soft HEAD * # 重置到指定状态,不会修改索引区和工作树
git reset --hard HEAD * # 重置到指定状态,会修改索引区和工作树
git reset -- files * # 重置index区文件

revert

1
2
3
git revert HEAD   # 撤销前一次操作   
git revert HEAD~ # 撤销前前一次操作
git revert commit # 撤销指定操作

checkout

1
2
3
4
5
git checkout -- file  # 取消对文件的修改(从暂存区——覆盖worktree file)  
git checkout branch|tag|commit -- file_name # 从仓库取出file覆盖当前分支
git checkout HEAD~1 [文件] # 将会更新 working directory 去匹配某次 commit
git checkout -- . # 从暂存区取出文件覆盖工作区
git checkout -b gh-pages 0c304c9 # 这个表示 从当前分支 commit 哈希值为 0c304c9 的节点,分一个新的分支gh-pages出来,并切换到 gh-pages

diff

1
2
3
4
5
6
7
8
9
10
git diff file     # 查看指定文件的差异   
git diff --stat # 查看简单的diff结果
git diff # 比较Worktree和Index之间的差异
git diff --cached # 比较Index和HEAD之间的差异
git diff HEAD # 比较Worktree和HEAD之间的差异
git diff branch # 比较Worktree和branch之间的差异
git diff branch1 branch2 # 比较两次分支之间的差异
git diff commit commit # 比较两次提交之间的差异
git diff master..test # 上面这条命令只显示两个分支间的差异
git diff master...test # 你想找出‘master’,‘test’的共有 父分支和'test'分支之间的差异,你用3个‘.'来取代前面的两个'.'

stash

1
2
3
4
5
6
git stash # 将工作区现场(已跟踪文件)储藏起来,等以后恢复后继续工作。   
git stash list # 查看保存的工作现场
git stash apply # 恢复工作现场
git stash drop # 删除stash内容
git stash pop # 恢复的同时直接删除stash内容
git stash apply stash@{0} # 恢复指定的工作现场,当你保存了不只一份工作现场时。

merge

1
git merge --squash test # 合并压缩,将test上的commit压缩为一条   

cherry-pick

1
2
git cherry-pick commit    # 拣选合并,将commit合并到当前分支   
git cherry-pick -n commit # 拣选多个提交,合并完后可以继续拣选下一个提交

rebase

1
2
3
4
5
6
git rebase master   # 将master分之上超前的提交,变基到当前分支  
git rebase --onto master 169a6 # 限制回滚范围,rebase当前分支从169a6以后的提交
git rebase --interactive # 交互模式,修改commit
git rebase --continue # 处理完冲突继续合并
git rebase --skip # 跳过
git rebase --abort # 取消合并

分支branch

删除

1
2
3
4
5
6
git push origin :branchName  # 删除远程分支  
git push origin --delete new # 删除远程分支new
git branch -d branchName # 删除本地分支,强制删除用-D
git branch -d test # 删除本地test分支
git branch -D test # 强制删除本地test分支
git remote prune origin # 远程删除了,本地还能看到远程存在,这条命令删除远程不存在的分支

提交

1
git push -u origin branchName # 提交分支到远程origin主机中  

拉取

git fetch -p #拉取远程分支时,自动清理 远程分支已删除,本地还存在的对应同名分支。

分支合并

1
2
3
4
git merge branchName      # 合并分支 - 将分支branchName和当前所在分支合并   
git merge origin/master # 在本地分支上合并远程分支。
git rebase origin/master # 在本地分支上合并远程分支。
git merge test # 将test分支合并到当前分支

重命名

git branch -m old new #重命名分支

查看

1
2
3
4
5
6
7
git branch      # 列出本地分支   
git branch -r # 列出远端分支
git branch -a # 列出所有分支
git branch -v # 查看各个分支最后一个提交对象的信息
git branch --merge # 查看已经合并到当前分支的分支
git branch --no-merge # 查看为合并到当前分支的分支
git remote show origin # 可以查看remote地址,远程分支

新建

1
2
3
4
git branch test # 新建test分支  
git branch newBrach 3defc69 # 指定哈希3defc69,新建分支名字为newBrach
git checkout -b newBrach origin/master # 取回远程主机的更新以后,在它的基础上创建一个新的分支
git checkout -b newBrach 3defc69 # 以哈希值3defc69,新建 newBrach 分支,并切换到该分支

连接

1
2
git branch --set-upstream dev origin/dev     # 将本地dev分支与远程dev分支之间建立链接  
git branch --set-upstream master origin/next # 手动建立追踪关系

分支切换

1
2
3
git checkout test     # 切换到test分支   
git checkout -b test # 新建+切换到test分支
git checkout -b test dev # 基于dev新建test分支,并切换

远端

1
2
3
4
5
6
7
8
git fetch <远程主机名> <分支名>   # fetch取回所有分支(branch)的更新  
git fetch origin remotebranch[:localbranch] # 从远端拉去分支[到本地指定分支]
git merge origin/branch # 合并远端上指定分支
git pull origin remotebranch:localbranch # 拉去远端分支到本地分支
git push origin branch # 将当前分支,推送到远端上指定分支
git push origin localbranch:remotebranch # 推送本地指定分支,到远端上指定分支
git push origin :remotebranch # 删除远端指定分支
git checkout -b [--track] test origin/dev # 基于远端dev分支,新建本地test分支[同时设置跟踪]

submodule

克隆项目同时克隆submodule

1
git clone https://github.com/jaywcjlove/handbook.git --depth=1 --recurse-submodules

克隆项目,之后再手动克隆 submodule 子项目

1
2
3
4
5
6
git submodule add --force '仓库地址' '路径'
# 其中,仓库地址是指子模块仓库地址,路径指将子模块放置在当前工程下的路径。
# 注意:路径不能以 / 结尾(会造成修改不生效)、不能是现有工程已有的目录(不能順利 Clone)
git submodule init # 初始化submodule
git submodule update # 更新submodule(必须在根目录执行命令)
git submodule update --init --recursive # 下载的工程带有submodule

当使用git clone下来的工程中带有submodule时,初始的时候,submodule的内容并不会自动下载下来的,此时,只需执行如下命令:

1
2
3
4
5
git submodule foreach git pull  # submodule 里有其他的 submodule 一次更新
git submodule foreach git pull origin master # submodule更新

git submodule foreach --recursive git submodule init
git submodule foreach --recursive git submodule update

删除文件

1
git rm -rf node_modules/

remote

git是一个分布式代码管理工具,所以可以支持多个仓库,在git里,服务器上的仓库在本地称之为remote。个人开发时,多源用的可能不多,但多源其实非常有用。

1
2
3
4
5
6
git remote add origin1 git@github.com:yanhaijing/data.js.git  
git remote # 显示全部源
git remote -v # 显示全部源+详细信息
git remote rename origin1 origin2 # 重命名
git remote rm origin # 删除
git remote show origin # 查看指定源的全部信息

标签tag

当开发到一定阶段时,给程序打标签是非常棒的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
git tag -a v0.1 -m 'my version 1.4' # 新建带注释标签   
git push origin --tags # 一次性推送所有分支
git push origin v1.5 # 推送单个tag到orgin源上
git tag -v v1.4.2.1 # 验证标签,验证已经签署的标签
git show v1.5 # 看到对应的 GPG 签

git tag # 列出现有标签
git tag v0gi.1 # 新建标签
git checkout tagname # 切换到标签
git tag -d v0.1 # 删除标签
git push origin :refs/tags/v0.1 # 删除远程标签
git pull --all # 获取远程所有内容包括tag
git --git-dir='<绝对地址>/.git' describe --tags HEAD # 查看本地版本信息

日志log

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
git config format.pretty oneline  #显示历史记录时,每个提交的信息只显示一行   
git config color.ui true #彩色的 git 输出
git log #查看最近的提交日志
git log --pretty=oneline #单行显示提交日志
git log --graph --pretty=oneline --abbrev-commit
git log -num #显示第几条log(倒数)
git reflog #查看所有分支的所有操作记录
git log --since=1.day #一天内的提交;你可以给出各种时间格式,比如说具体的某一天(“2008-01-15”),或者是多久以前(“2 years 1 day 3 minutes ago”)。
git log --pretty="%h - %s" --author=自己的名字 #查看自己的日志
git log -p -2 #展开两次更新显示每次提交的内容差异
git log --stat #要快速浏览其他协作者提交的更新都作了哪些改动
git log --pretty=format:"%h - %an, %ar : %s"#定制要显示的记录格式
git log --pretty=format:'%h : %s' --date-order --graph # 拓扑顺序展示
git log --pretty=format:'%h : %s - %ad' --date=short #日期YYYY-MM-DD显示
git log <last tag> HEAD --pretty=format:%s # 只显示commit
git config --global format.pretty '%h : %s - %ad' --date=short #日期YYYY-MM-DD显示 写入全局配置
选项 说明 选项 说明
%H 提交对象(commit)的完整哈希字串 %ad 作者修订日期(可以用 -date= 选项定制格式)
%h 提交对象的简短哈希字串 %ar 作者修订日期,按多久以前的方式显示
%T 树对象(tree)的完整哈希字串 %cn 提交者(committer)的名字
%t 树对象的简短哈希字串 %ce 提交者的电子邮件地址
%P 父对象(parent)的完整哈希字串 %cd 提交日期
%p 父对象的简短哈希字串 %cr 提交日期,按多久以前的方式显示
%an 作者(author)的名字 %s 提交说明
%ae 作者的电子邮件地址 - -

Pretty Formats

重写历史

1
2
3
4
git commit --amend    # 改变最近一次提交  
git rebase -i HEAD~3 # 修改最近三次的提交说明,或者其中任意一次
git commit --amend # 保存好了,这些指示很明确地告诉了你该干什么
git rebase --continue # 修改提交说明,退出编辑器。
1
2
3
pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file

改成

1
2
pick 310154e updated README formatting and added blame
pick f7f3f6d changed my name a bit

删除仓库

1
2
cd ..
rm -rf repo.git

Github官方教程

其它

1
2
git help *  # 获取命令的帮助信息  
git status # 获取当前的状态,非常有用,因为git会提示接下来的能做的操作

报错问题解决

1. git fatal: protocol error: bad line length character: No s

解决办法:更换remote地址为 http/https

2. The requested URL returned error: 403 Forbidden while accessing

解决github push错误的办法:

1
2
3
4
5
6
7
8
9
10
#vim 编辑器打开 当前项目中的config文件
vim .git/config

#修改
[remote "origin"]
url = https://github.com/jaywcjlove/example.git

#为下面代码
[remote "origin"]
url = https://jaywcjlove@github.com/jaywcjlove/example.git

3. git status 显示中文问题

在查看状态的时候 git status 如果是中文就显示下面的情况

1
\344\272\247\345\223\201\351\234\200\346\261\202

解决这个问题方法是:

1
git config --global core.quotepath false

参考资料

hexdump

显示文件十六进制格式

hexdump命令 一般用来查看“二进制”文件的十六进制编码,但实际上它能查看任何文件,而不只限于二进制文件。

语法

1
hexdump [选项] [文件]...

选项

1
2
3
4
5
6
7
8
9
-n length 只格式化输入文件的前length个字节。
-C 输出规范的十六进制和ASCII码。
-b 单字节八进制显示。
-c 单字节字符显示。
-d 双字节十进制显示。
-o 双字节八进制显示。
-x 双字节十六进制显示。
-s 从偏移量开始输出。
-e 指定格式字符串,格式字符串包含在一对单引号中,格式字符串形如:'a/b "format1" "format2"'。

每个格式字符串由三部分组成,每个由空格分隔,第一个形如a/b,b表示对每b个输入字节应用format1格式,a表示对每a个输入字节应用format2格式,一般a>b,且b只能为1,2,4,另外a可以省略,省略则a=1。format1和format2中可以使用类似printf的格式字符串,如:

1
2
3
4
%02d:两位十进制
%03x:三位十六进制
%02o:两位八进制
%c:单个字符等

还有一些特殊的用法:

1
2
3
4
%_ad:标记下一个输出字节的序号,用十进制表示。
%_ax:标记下一个输出字节的序号,用十六进制表示。
%_ao:标记下一个输出字节的序号,用八进制表示。
%_p:对不能以常规字符显示的用 . 代替。

同一行如果要显示多个格式字符串,则可以跟多个-e选项。

实例

1
2
3
4
hexdump -e '16/1 "%02X " "  |  "' -e '16/1 "%_p" "\n"' test
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F | ................
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F | ................
20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F | !"#$%&'()*+,-./

history

显示或操作历史列表。

概要

1
2
3
history [-c] [-d offset] [n]
history -anrw [filename]
history -ps arg [arg...]

主要用途

  • 显示历史列表。

  • 操作历史列表。

选项

1
2
3
4
5
6
7
8
-c           清空历史列表。
-d offset 根据offset删除记录。如果是正数则表示offset位置的记录,如果为负数则表示从结尾向前offset位置的记录。
-a 将当前终端的历史记录行添加到历史记录文件。
-n 将尚未从历史文件中读取的历史行追加到当前历史列表中。
-r 读取历史文件,并将其内容附加到历史列表中。
-w 将当前历史记录列表附加到历史记录文件中并且附加它们到历史列表中。
-p 在每个arg上执行历史记录扩展并在标准输出上显示结果,而不将结果存储在历史记录列表中。
-s 将每个arg作为单个条目附加到历史记录列表。

参数

n:可选,只列出最近的n条记录。

filename:可选,表示历史文件;默认调用顺序为filename、环境变量HISTFILE~/.bash_history

返回值

返回成功,除非提供了非法选项或出现了错误。

例子

使用history命令显示最近使用的10条历史命令

1
2
3
4
5
6
7
8
9
10
11
[root@localhost ~]# history 10
92 ls
93 cd ..
94 ls
95 exit
96 ls -a
97 cd .ssh/
98 ls
99 cat known_hosts
100 exit
101 history 10

清空历史记录

1
[root@localhost ~]# history -c

更多实例:

1
2
3
4
5
# 执行第 n 条历史命令
[root@localhost ~]# !n

# 执行最后一条 xxx 开头的命令
[root@localhost ~]# !xxx

注意

  1. 在命令行中,可以使用符号!执行指定序号的历史命令。例如,要执行第2个历史命令,则输入!2
  2. 关闭终端后,历史列表将被写入历史文件~/.bash_history
  3. 环境变量HISTSIZE决定了历史文件中命令的存储数量,默认存储1000条。
  4. 环境变量HISTTIMEFORMAT如果是非空值,则使用其值作为strftime(3)打印相关时间戳的格式字符串添加在每个显示的历史记录之前;否则不会打印时间戳。
  5. 该命令是bash内建命令,相关的帮助信息请查看help命令。

iconv

转换文件的编码方式

iconv命令 是用来转换文件的编码方式的,比如它可以将UTF8编码的转换成GB18030的编码,反过来也行。JDK中也提供了类似的工具native2ascii。Linux下的iconv开发库包括iconv_open,iconv_close,iconv等C函数,可以用来在C/C++程序中很方便的转换字符编码,这在抓取网页的程序中很有用处,而iconv命令在调试此类程序时用得着。

语法

1
iconv -f encoding [-t encoding] [inputfile]... 

选项

1
2
3
4
5
6
7
8
-f encoding :把字符从encoding编码开始转换。 
-t encoding :把字符转换到encoding编码。
-l :列出已知的编码字符集合
-o file :指定输出文件
-c :忽略输出的非法字符
-s :禁止警告信息,但不是错误信息
--verbose :显示进度信息
-f和-t所能指定的合法字符在-l选项的命令里面都列出来了。

实例

列出当前支持的字符编码: 

1
iconv -l 

将文件file1转码,转后文件输出到fil2中: 

1
iconv file1 -f EUC-JP-MS -t UTF-8 -o file2 

这里,没-o那么会输出到标准输出。

init

init进程是所有Linux进程的父进程

init命令 是Linux下的进程初始化工具,init进程是所有Linux进程的父进程,它的进程号为1。init命令是Linux操作系统中不可缺少的程序之一,init进程是Linux内核引导运行的,是系统中的第一个进程。

语法

1
init(选项)(参数)

选项

1
2
-b:不执行相关脚本而直接进入单用户模式;
-s:切换到单用户模式。

参数

运行等级:指定Linux系统要切换到的运行等级。

实例

几个常用的命令

查看系统进程命令:ps -ef | head
查看init的配置文件:more /etc/inittab
查看系统当前运行的级别:runlevel

运行级别

到底什么是运行级呢?简单的说,运行级就是操作系统当前正在运行的功能级别。这个级别从0到6 ,具有不同的功能。你也可以在/etc/inittab中查看它的英文介绍。

1
2
3
4
5
6
7
#0  停机(千万不能把initdefault 设置为0)
#1 单用户模式
#2 多用户,没有 NFS(和级别3相似,会停止部分服务)
#3 完全多用户模式
#4 没有用到
#5 x11(Xwindow)
#6 重新启动(千万不要把initdefault 设置为6)

less

分屏上下翻页浏览文件内容

less命令 的作用与more十分相似,都可以用来浏览文字档案的内容,不同的是less命令允许用户向前或向后浏览文件,而more命令只能向前浏览。用less命令显示文件时,用PageUp键向上翻页,用PageDown键向下翻页。要退出less程序,应按Q键。

语法

1
less(选项)(参数)

选项

1
2
3
4
5
6
7
8
-e:文件内容显示完毕后,自动退出;
-f:强制显示文件;
-g:不加亮显示搜索到的所有关键词,仅显示当前显示的关键字,以提高显示速度;
-l:搜索时忽略大小写的差异;
-N:每一行行首显示行号;
-s:将连续多个空行压缩成一行显示;
-S:在单行显示较长的内容,而不换行显示;
-x<数字>:将TAB字符显示为指定个数的空格字符。

参数

文件:指定要分屏显示内容的文件。

实例

1
sudo less /var/log/shadowsocks.log

login

登录系统或切换用户身份

login命令 用于给出登录界面,可用于重新登录或者切换用户身份,也可通过它的功能随时更换登入身份。在Slackware发行版中 ,您可在命令后面附加欲登入的用户名称,它会直接询问密码,等待用户输入。当/etc/nologin文件存在时,系统只root帐号登入系统,其他用户一律不准登入。

语法

1
login(选项)(参数)

选项

1
2
-p:告诉login指令不销毁环境变量;
-h:指定远程服务器的主机名。

参数

用户名:指定登录使用的用户名。

logout

退出当前登录的Shell

logout命令 用于退出当前登录的Shell,logout指令让用户退出系统,其功能和login指令相互对应。

语法

1
logout

make

GNU的工程化编译工具

make命令 是GNU的工程化编译工具,用于编译众多相互关联的源代码文件,以实现工程化的管理,提高开发效率。

语法

1
make(选项)(参数)

选项

1
2
3
4
5
6
7
8
9
-f:指定“makefile”文件;
-i:忽略命令执行返回的出错信息;
-s:沉默模式,在执行之前不输出相应的命令行信息;
-r:禁止使用build-in规则;
-n:非执行模式,输出所有执行命令,但并不执行;
-t:更新目标文件;
-q:make操作将根据目标文件是否已经更新返回"0"或非"0"的状态信息;
-p:输出所有宏定义和目标文件描述;
-d:Debug模式,输出有关文件和检测时间的详细信息。

Linux下常用选项与Unix系统中稍有不同,下面是不同的部分:

1
2
3
4
-c dir:在读取 makefile 之前改变到指定的目录dir;
-I dir:当包含其他 makefile文件时,利用该选项指定搜索目录;
-h:help文挡,显示所有的make选项;
-w:在处理 makefile 之前和之后,都显示工作目录。

参数

目标:指定编译目标。

知识扩展

无论是在linux 还是在Unix环境 中,make都是一个非常重要的编译命令。不管是自己进行项目开发还是安装应用软件,我们都经常要用到make或make install。利用make工具,我们可以将大型的开发项目分解成为多个更易于管理的模块,对于一个包括几百个源文件的应用程序,使用make和 makefile工具就可以简洁明快地理顺各个源文件之间纷繁复杂的相互关系。

而且如此多的源文件,如果每次都要键入gcc命令进行编译的话,那对程序员 来说简直就是一场灾难。而make工具则可自动完成编译工作,并且可以只对程序员在上次编译后修改过的部分进行编译。

因此,有效的利用make和 makefile工具可以大大提高项目开发的效率。同时掌握make和makefile之后,您也不会再面对着Linux下的应用软件手足无措了。

mktemp

创建临时文件供shell脚本使用

mktemp命令 被用来创建临时文件供shell脚本使用。

语法

1
mktemp(选项)(参数)

选项

1
2
3
-q:执行时若发生错误,不会显示任何信息;
-u:暂存文件会在mktemp结束前先行删除;
-d:创建一个目录而非文件。

参数

文件:指定创建的临时文件。

more

显示文件内容,每次显示一屏

more命令 是一个基于vi编辑器文本过滤器,它以全屏幕的方式按页显示文本文件的内容,支持vi中的关键字定位操作。more名单中内置了若干快捷键,常用的有H(获得帮助信息),Enter(向下翻滚一行),空格(向下滚动一屏),Q(退出命令)。

该命令一次显示一屏文本,满屏后停下来,并且在屏幕的底部出现一个提示信息,给出至今己显示的该文件的百分比:–More–(XX%)可以用下列不同的方法对提示做出回答:

  • Space 键:显示文本的下一屏内容。
  • Enter 键:只显示文本的下一行内容。
  • 按斜线符|:接着输入一个模式,可以在文本中寻找下一个相匹配的模式。
  • 按H键:显示帮助屏,该屏上有相关的帮助信息。
  • 按B键:显示上一屏内容。
  • 按Q键:退出more命令。

语法

1
more(语法)(参数)

选项

1
2
3
4
5
6
-<数字>:指定每屏显示的行数;
-d:显示“[press space to continue,'q' to quit.]”和“[Press 'h' for instructions]”;
-c:不进行滚屏操作。每次刷新这个屏幕;
-s:将多个空行压缩成一行显示;
-u:禁止下划线;
+<数字>:从指定数字的行开始显示。

参数

文件:指定分页显示内容的文件。

实例

显示文件file的内容,但在显示之前先清屏,并且在屏幕的最下方显示完成的百分比。

1
more -dc file

显示文件file的内容,每10行显示一次,而且在显示之前先清屏。

1
more -c -10 file

mysql

MySQL服务器客户端工具

mysql命令 是MySQL数据库服务器的客户端工具,它工作在命令行终端中,完成对远程MySQL数据库服务器的操作。

语法

1
mysql(选项)(参数)

选项

1
2
3
4
-h:MySQL服务器的ip地址或主机名;
-u:连接MySQL服务器的用户名;
-e:执行mysql内部命令;
-p:连接MySQL服务器的密码。

参数

数据库:指定连接服务器后自动打开的数据库。

nl

为每一个文件添加行号。

概要

1
nl [OPTION]... [FILE]...

主要用途

  • 将每一个输入的文件添加行号后发送到标准输出。
  • 当没有文件或文件为-时,读取标准输入
  • 处理逻辑页(logical page)。

选项

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
-b, --body-numbering=STYLE           使用STYLE 为body部分的行附加行号。
-d, --section-delimiter=CC 使用CC作为logical page的分隔符。
-f, --footer-numbering=STYLE 使用STYLE 为footer部分的行附加行号。
-h, --header-numbering=STYLE 使用STYLE 为header部分的行附加行号。
-i, --line-increment=NUMBER 行号递增间隔为NUMBER。
-l, --join-blank-lines=NUMBER 连续NUMBER行的空行作为一行处理。
-n, --number-format=FORMAT 根据FORMAT插入行号。
-p, --no-renumber 不要在每个部分重置行号。
-s, --number-separator=STRING 在行号后添加字符串STRING。
-v, --starting-line-number=NUMBER 每部分的起始行号。
-w, --number-width=NUMBER 行号宽度为NUMBER。
--help 显示帮助信息并退出。
--version 显示版本信息并退出。


默认选项为:-bt -d'\:' -fn -hn -i1 -l1 -nrn -sTAB -v1 -w6

CC是由两个字符组成的,默认为\: ,第二个字符如果缺失则默认为:

STYLE可以为下列可用值之一:

a 所有行标记行号。
t 仅为非空行标记行号。
n 不标记行号。
pBRE 符合基础正则表达式(BRE)的行会标记行号。

FORMAT可以为下列可用值之一:

ln 左对齐,不会在开始部分补充0以满足宽度。
rn 右对齐,不会在开始部分补充0以满足宽度。
rz 右对齐,会在开始部分补充0以满足宽度。

logical page
三部分组成(header, body, footer)
起始标记(header \:\:\:, body \:\:, footer \:)

参数

FILE(可选):要处理的文件,可以为一或多个。

返回值

返回0表示成功,返回非0值表示失败。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
nl_logicalpage.txt:该文件用于说明nl命令处理逻辑页,内容如下:
\:\:\:
header_1
\:\:
body_1
\:
footer_1
\:\:\:
header_2
\:\:
body_2
\:
footer_2
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
[user2@pc ~]$ nl nl_logicalpage.txt

header_1

1 body_1

footer_1

header_2

1 body_2

footer_2

[user2@pc ~]$ nl -v0 -fa -ha nl_logicalpage.txt

0 header_1

1 body_1

2 footer_1

0 header_2

1 body_2

2 footer_2

[user2@pc ~]$ nl -p -fa -ha nl_logicalpage.txt

1 header_1

2 body_1

3 footer_1

4 header_2

5 body_2

6 footer_2
1
2
3
4
nl_normal.txt:该文件用于说明nl命令处理普通文件,内容如下:
ZhuangZhu-74
2019-11-21
127.0.0.1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[user2@pc ~]$ nl nl_normal.txt
1 ZhuangZhu-74
2 2019-11-21
3 127.0.0.1

[user2@pc ~]$ nl -b p'1$' nl_normal.txt
ZhuangZhu-74
1 2019-11-21
2 127.0.0.1

[user2@pc ~]$ nl -b p'^[A-Z]' nl_normal.txt
1 ZhuangZhu-74
2019-11-21
127.0.0.1

注意

  1. 该命令是GNU coreutils包中的命令,相关的帮助信息请查看man -s 1 nlinfo coreutils 'nl invocation'

alias - 定义或显示别名

功能列表

  • 简化较长的命令
  • 定义一个或多个别名
  • 修改一个或多个已定义别名的值
  • 显示一个或多个已定义别名
  • 显示全部已定义的别名
1
alias [-p] [name[=value] ...]

alias 返回 true 除非您要显示的别名未定义

选项 & 参数

选项

-p

显示全部已定义的别名, 非必传参数, Mac 系统不支持

参数

name

指定要(定义、修改、显示)的别名

value

别名的值

实例 & 常用

1
2
3
4
5
6
7
8
9
10
11
# 显示全部已定义的别名
alias
alias -p

# 显示已定义的别名(假设当前环境存在以下别名)
alias ls
alias ls grep

# 定义或修改别名的值
alias ls='ls --color=auto'
alias ls='ls --color=never' grep='grep --color=never'

知识点

直接在 shell 里设定的命令别名,在终端关闭或者系统重新启动后都会失效,如何才能永久有效呢?

使用编辑器打开~/.bashrc,在文件中加入别名设置,如:alias rm=’rm -i’,保存后执行source ~/.bashrc,这样就可以永久保存命令的别名了。

因为修改的是当前用户目录下的~/.bashrc文件,所以这样的方式只对当前用户有用。如果要对所有用户都有效,修改/etc/bashrc文件就可以了。

请注意,以下内容可能与您实际使用的系统有出入:

在 CentOS7 下,这个文件是 /etc/bash.bashrc 。此外在 CentOS7 下,细看 ~/.bashrc 文件,会发现有这样一段代码:

1
2
3
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi

这个代码的意思就是如果存在那么就加载 .bash_aliases 文件,所以也可以在用户根目录下新建该文件用于单独存放命令别名设置。

错误用法

  • 要显示的别名未定义

  • 当您定义(修改)别名的值的时候,由于值的字符串有空格但您没有用 单引号扩起,那么会导致严重的问题:

1
2
3
4
5
6
# 为方便演示,删除全部别名
unalias -a
# 没有用单引号扩起
alias rm=rm -rf
# 执行命令后报错 bash: alias: -rf: not found
# 这时使用alias查看rm的别名时返回 alias rm='rm'
1
2
3
4
5
6
7
8
9
10
11
# 更具有迷惑性的例子
# 为方便演示,删除全部别名
unalias -a
# 仍然没有用单引号括起
alias ls=ls --color=never
# 执行命令后看起来没有报错

# 使用alias查看全部别名会发现运行结果如下:
# alias --color=never
# alias ls='ls'
# alias处理时将它们看成了两组

Q&A

Q:如果我要显示一到多个别名,但不知道其中是否有未定义的该怎么办?

A:正常执行就是了,alias 不会因为有一个未定义的别名就结束对剩余参数的执行。

Q:如果我这么定义alias cd='ls' ls='cd',会有什么后果?

A:运行 cd 依然会切换目录,运行 ls 依然会列出文件夹的内容;不要这样定义。

注意

  1. 执行脚本时请注意:

使用 source 命令执行的 bash 脚本如果执行了 aliasunalias 命令,那么有可能会对终端环境的别名设置产生影响;终端环境的别名设置也可能改变运行结果;

通过 sh 方式调用的 bash 脚本或直接运行当前用户有执行权限的脚本不受终端环境的别名影响。

  1. 删除别名,请查看 unalias 命令。

  2. 建议您不要对 mv cp rm 等命令的别名设置危险的 -f 选项,比如alias rm='rm -f'

  3. 需要注意别名是否和其他命令有冲突的情况。

  4. 该命令是 bash 内建命令,相关的帮助信息请查看 help 命令。

参考文章