BNU-FZH

fengzhenhua@outlook.com

脚本命令

shell求交、并和差集
1
2
3
4
5
6
7
8
9
10
11
12
13
14
file_list_1=("test1" "test2" "test3" "test4" "test5" "test6")
file_list_2=("test5" "test6" "test7" "test8")

# 获取并集,A ∪ B
file_list_union=(`echo ${file_list_1[*]} ${file_list_2[*]}|sed 's/ /\n/g'|sort|uniq`)
echo ${file_list_union[*]}

# 获取交集,A n B
file_list_inter=(`echo ${file_list_1[*]} ${file_list_2[*]}|sed 's/ /\n/g'|sort|uniq -c|awk '$1!=1{print $2}'`)
echo ${file_list_inter[*]}

# 对称差集,不属于 A n B
file_list_4=(`echo ${file_list_1[*]} ${file_list_2[*]}|sed 's/ /\n/g'|sort|uniq -c|awk '$1==1{print $2}'`)
echo ${file_list_4[*]}

命令解释

在上述三条命令中,首先使用echo 输出由两个数组构成的集合,然后使用sed将空格替换成换行符\n, 再使用sort排序:

  • 使用uniq处理则获得并集.
  • 使用uniq -c处理则. 再使用awk分析第一个参数$1若不等于1就表示这是两个数组中,于是打印$2便得到交集.
  • 使用uniq -c处理则. 再使用awk分析第一个参数$1若等于1就表示这是两个数组中,于是打印$2便得到差集.

命令参考

执行结果

执行结果
1
2
3
test1 test2 test3 test4 test5 test6 test7 test8
test5 test6
test1 test2 test3 test4 test7 test8

添加前后缀

在Linux中使用bash正则表达式来为数组元添加前缀和后缀是一个标准的做法,这在脚本编写中是一个很方便的操作。例如:

bash正则表达式添加前后缀
1
2
3
ARRAY=( one two three )
echo ${ARRAY[@]/#/prefix_}
echo ${ARRAY[@]/%/_suffix}

除了正则表达式外,还有一种漂亮的解决方案:

数组加入前后缀
1
2
3
4
$ ARRAY=(A B C)
$ mapfile -t -d $'\0' EXPANDED < <(printf "prefix_%s_postfix\0" "${ARRAY[@]}")
$ echo "${EXPANDED[@]}"
prefix_A_postfix prefix_B_postfix prefix_C_postfix

mapfile将行读入数组的元素。使用-d $'\0',它将读取以null分隔的字符串,并且-t将从结果中省略分隔符。参见help mapfile

删除前后缀

删除后缀

bash正则表达式删除后缀
1
2
3
4
5
ARRAY=( one.git two.git three.git four.me )
echo ${ARRAY[@]%.git}
> one two three four.me
echo ${ARRAY[@]%.*}
> one two three four

删除前缀

bash正则表达式删除前缀
1
2
3
4
5
ARRAY=( pre.one pre.two pre.three me.four )
echo ${ARRAY[@]#pre.}
> one two three me.four
echo ${ARRAY[@]#*.}
> one two three four

注意:在删除前后缀中,使用的*是正则表达式,表示任意个字符,若要删除指定字符,应当输入具体的字符。

正则表达式可以极大的提高程序效率,本文提供Shell中的正则表达相关知识。

分类

正则表达式最早在 1950 年代由美国数学家 Stephen Cole Kleene 提出,后来被 Unix 操作系统的文本处理工具广泛使用。

经过多年的发展和实践,最终形成两大标准,一个是 POSIX 标准,另一个是 Perl 标准。后者本是为 Perl 语言实现的,由于其功能非常强大,被 JavaJavaScript等语言广泛借鉴,从而被广泛使用。

这里将正则表达式分为三类:

  • 基本正则表达式(Basic Regular Expression 简称 BRE),由 POSIX 标准定义。
  • 扩展正则表达式(Extended Regular Expression 简称 ERE),也由 POSIX 标准定义。
  • Perl 的正则表达式(Perl Regular Expression 简称 PRE),由 Perl 语言定义。

组成部分

基本组成部分

正则表达式 描述 示例 Basic RegEx Extended RegEx Perl regEx
\ 转义符,将特殊字符进行转义,忽略其特殊意义 a.b匹配a.b,但不能匹配ajb,.被转义为特殊意义 \ \ \
^ 匹配行首 ^tux匹配以tux开头的行 ^ ^ ^
$ 匹配行尾 tux$匹配以tux结尾的行 $ $ $
. 匹配除换行符\n之外的任意单个字符 ab.匹配abc或bad,不可匹配abcd或abde,只能匹配单字符 . . .
[] 匹配包含在[字符]之中的任意一个字符 coo[kl]可以匹配cook或cool [] [] []
[^] 匹配1之外的任意一个字符 1232不可以匹配1234或1235,1236、1237都可以 [^] [^] [^]
[-] 匹配[]中指定范围内的任意一个字符,要写成递增 [0-9]可以匹配1、2或3等其中任意一个数字 [-] [-] [-]
? 匹配之前的项1次或者0次 colou?r可以匹配color或者colour,不能匹配colouur 不支持 ? ?
+ 匹配之前的项1次或者多次 sa-6+匹配sa-6、sa-666,不能匹配sa- 不支持 + +
* 匹配之前的项0次或者多次 co*l匹配cl、col、cool、coool等 * * *
() 匹配表达式,创建一个用于匹配的子串 ma(tri)?匹配max或maxtrix 不支持 () ()
{n} 匹配之前的项n次,n是可以为0的正整数 [0-9]{3}匹配任意一个三位数,可以扩展为0-9[0-9] 不支持 {n} {n}
{n,} 之前的项至少需要匹配n次 [0-9]{2,}匹配任意一个两位数或更多位数 不支持 {n,} {n,}
{n,m} 指定之前的项至少匹配n次,最多匹配m次,n<=m [0-9]{2,5}匹配从两位数到五位数之间的任意一个数字 不支持 {n,m} {n,m}
交替匹配 两边的任意一项 ab(c d)匹配abc或abd

POSIX 字符类

POSIX字符类是一个形如[:...:]的特殊元序列(meta sequence),他可以用于匹配特定的字符范围。

正则表达式 描述 示例 Basic RegEx Extended RegEx Perl RegEx
[:alnum:] 匹配任意一个字母或数字字符 [[:alnum:]]+ [:alnum:] [:alnum:] [:alnum:]
[:alpha:] 匹配任意一个字母字符(包括大小写字母) [[:alpha:]]{4} [:alpha:] [:alpha:] [:alpha:]
[:blank:] 空格与制表符(横向和纵向) [[:blank:]]* [:blank:] [:blank:] [:blank:]
[:digit:] 匹配任意一个数字字符 [[:digit:]]? [:digit:] [:digit:] [:digit:]
[:lower:] 匹配小写字母 [[:lower:]]{5,} [:lower:] [:lower:] [:lower:]
[:upper:] 匹配大写字母 ([[:upper:]]+)? [:upper:] [:upper:] [:upper:]
[:punct:] 匹配标点符号 [[:punct:]] [:punct:] [:punct:] [:punct:]
[:space:] 匹配一个包括换行符、回车等在内的所有空白符 [[:space:]]+ [:space:] [:space:] [:space:]
[:graph:] 匹配任何一个可以看得见的且可以打印的字符 [[:graph:]] [:graph:] [:graph:] [:graph:]
[:xdigit:] 任何一个十六进制数(即:0-9,a-f,A-F) [[:xdigit:]]+ [:xdigit:] [:xdigit:] [:xdigit:]
[:cntrl:] 任何一个控制字符(ASCII字符集中的前32个字符) [[:cntrl:]] [:cntrl:] [:cntrl:] [:cntrl:]
[:print:] 任何一个可以打印的字符 [[:print:]] [:print:] [:print:] [:print:]

元字符

元字符(meta character)是一种 Perl 风格的正则表达式,只有一部分文本处理工具支持它,并不是所有的文本处理工具都支持。

正则表达式 描述 示例 Basic RegEx Extended RegEx Perl RegEx
\b 单词边界 \bcool\b 匹配cool,不匹配coolant \b \b \b
\B 非单词边界 cool\B 匹配coolant,不匹配cool \B \B \B
\d 单个数字字符 b\db 匹配b2b,不匹配bcb 不支持 不支持 \d
\D 单个非数字字符 b\Db 匹配bcb,不匹配b2b 不支持 不支持 \D
\w 单个单词字符(字母、数字与_) \w 匹配1或a,不匹配& \w \w \w
\W 单个非单词字符 \W 匹配&,不匹配1或a \W \W \W
\n 换行符 \n 匹配一个新行 不支持 不支持 \n
\s 单个空白字符 x\sx 匹配x x,不匹配xx 不支持 不支持 \s
\S 单个非空白字符 x\S\x 匹配xkx,不匹配xx 不支持 不支持 \S
\r 回车 \r 匹配回车 不支持 不支持 \r
\t 横向制表符 \t 匹配一个横向制表符 不支持 不支持 \t
\v 垂直制表符 \v 匹配一个垂直制表符 不支持 不支持 \v
\f 换页符 \f 匹配一个换页符 不支持 不支持 \f

常见命令中的使用

命令 Basic RegEx Extended RegEx Perl RegEx
grep 支持 需加 -E 参数 需加 -P 参数
egrep 支持 支持 需加 -P 参数
sed 支持 需加 -r 参数 不支持
awk 支持 支持 不支持

添加方式 语法 多元素 下标连续 下标改变 覆盖原元素
直接下标添加 array_name[index]=value
数组长度添加 array_name[${#array_name[@]}]=value
数组长度添加 array_name[${#array_name[*]}]=value
重新创建数组 array_name=("${array_name[@]}" value1 ... valueN)
赋值运算符+= array_name+=(value1 ... valueN)

综上可知,是最通用的方案。

当编写脚本时,可能会遇到需要的命令在系统中没有安装, 此时需要解决命令依赖的问题,解决方法为安装对应的软件。Linux下大量的发行版,且不同发行版一般使用不同的包管理器,这导致了安装软件需要执行不同的安装命令,于是需要解决第二个问题:使用通用Linux命令探测系统安装的包管理器,然后确定安装命令。但是有时候,软件包提供的命令与其包的名称并不一致,这里就需要建立一个命令与包的映射关系,以正确执行安装命令。按照这个思路,本文提供一个基础的脚本:

通用安装脚本
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
#! /bin/sh
#
# readycmd.sh
# Author : fengzhenhua
# Email : fengzhenhua@outlook.com
# Date : 2024-06-14 15:58
# CopyRight: Copyright (C) 2022-2030 FengZhenhua(冯振华)
# License : Distributed under terms of the MIT license.
# Objective: 在不同的发行版中安装命令与软件包名不同的命令
#
# 包管理器列表: 管理器=安装命令
declare -A RD_PKG=(\
["pacman"]="pacman -S --needed --confirm" #arch endeavour manjaro 等
["apt-get"]="apt-get -y install" #debian ubuntu 等
["yum"]="yum -y install" #redhat centos7 及以下
["dnf"]="dnf -y install" #fedora centos8
["zypper"]="zypper -y install" #open suse
)
# 待安装软件列表: 命令=软件名
declare -A RD_CMD=(\
["unar"]="unarchiver" ["ssh"]="openssh" ["nvim"]="neovim"
)
# 选择发行版中的包管理器
for sh_pkg in ${!RD_PKG[*]}; do
which $sh_pkg &> /dev/null
if [ $? = 0 ]; then
RD_PKG_INS=${RD_PKG[$sh_pkg]}
fi
done
# 使用发行版中的包管理器安装系统中缺失的命令
for sh_cmd in ${!RD_CMD[*]}; do
which ${sh_cmd} &> /dev/null
if [ ! $? = 0 ]; then
sudo ${RD_PKG_INS} ${RD_CMD[$sh_cmd]}
fi
done

find , locate, whereis 和which 的区别

  • which:常用于查找可直接执行的命令。只能查找可执行文件,该命令基本只在$PATH路径中搜索,查找范围最小,查找速度快。默认只返回第一个匹配的文件路径,通过选项 -a 可以返回所有匹配结果。

  • whereis:不只可以查找命令,其他文件类型都可以(man中说只能查命令、源文件和man文件,实际测试可以查大多数文件)。在$PATH路径基础上增加了一些系统目录的查找,查找范围比which稍大,查找速度快。可以通过 -b 选项,限定只搜索二进制文件。

  • locate:超快速查找任意文件。它会从linux内置的索引数据库查找文件的路径,索引速度超快。刚刚新建的文件可能需要一定时间才能加入该索引数据库,可以通过执行updatedb命令来强制更新一次索引,这样确保不会遗漏文件。该命令通常会返回大量匹配项,可以使用 -r 选项通过正则表达式来精确匹配。

  • find:直接搜索整个文件目录,默认直接从根目录开始搜索,建议在以上命令都无法解决问题时才用它,功能最强大但速度超慢。除非你指定一个很小的搜索范围。通过 -name 选项指定要查找的文件名,支持通配符。

find , locate, whereis 和which 的用法

1. find

find是最常见和最强大的查找命令,你可以用它找到任何你想找的文件。

find的使用格式如下:

  $ find <指定目录> <指定条件> <指定动作>

  - <指定目录>: 所要搜索的目录及其所有子目录。默认为当前目录。

  - <指定条件>: 所要搜索的文件的特征。

  - <指定动作>: 对搜索结果进行特定的处理。

如果什么参数也不加,find默认搜索当前目录及其子目录,并且不过滤任何结果(也就是返回所有文件),将它们全都显示在屏幕上。

find的使用实例:

  $ find . -name "my*"

搜索当前目录(含子目录,以下同)中,所有文件名以my开头的文件。

  $ find . -name "my*" -ls

搜索当前目录中,所有文件名以my开头的文件,并显示它们的详细信息。

  $ find . -type f -mmin -10

搜索当前目录中,所有过去10分钟中更新过的普通文件。如果不加-type f参数,则搜索普通文件+特殊文件+目录。

2. locate

locate命令其实是“find -name”的另一种写法,但是要比后者快得多,原因在于它不搜索具体目录,而是搜索一个数据库(/var/lib/locatedb),这个数据库中含有本地所有文件信息。Linux系统自动创建这个数据库,并且每天自动更新一次,所以使用locate命令查不到最新变动过的文件。为了避免这种情况,可以在使用locate之前,先使用updatedb命令,手动更新数据库。

locate命令的使用实例:

  $ locate /etc/sh

搜索etc目录下所有以sh开头的文件。

  $ locate ~/m

搜索用户主目录下,所有以m开头的文件。

  $ locate -i ~/m

搜索用户主目录下,所有以m开头的文件,并且忽略大小写。

3. whereis

whereis命令只能用于程序名的搜索,而且只搜索二进制文件(参数-b)、man说明文件(参数-m)和源代码文件(参数-s)。如果省略参数,则返回所有信息。

whereis 命令的使用实例
1
2
$ whereis grep 
grep: /usr/bin/grep /usr/share/man/man1/grep.1.gz /usr/share/man/man1p/grep.1p.gz /usr/share/info/grep.info.gz

4. which

which命令的作用是,在PATH变量指定的路径中,搜索某个系统命令的位置,并且返回第一个搜索结果。也就是说,使用which命令,就可以看到某个系统命令是否存在,以及执行的到底是哪一个位置的命令。

which 命令使用实例
1
2
$ which date
/usr/bin/date

python中存在一种叫做字典的数据类型,字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值 key:value 对用冒号 : 分割,每个键值对之间用逗号 , 分割,整个字典包括在花括号 {},格式如下所示:

1
d = {key1 : value1, key2 : value2 }

关联数组的定义

shell中称为关联数组,可以使用任意的字符串、或者整数作为下标来访问数组元素。关联数组使用 declare 命令来声明,语法格式如下:

1
declare -A array_name

-A选项就是用于声明一个关联数组。关联数组的键是唯一的。以下实例我们创建一个关联数组 site,并创建不同的键值:

实例
1
declare -A site=(["google"]="www.google.com" ["runoob"]="www.runoob.com" ["taobao"]="www.taobao.com")

我们也可以先声明一个关联数组,然后再设置键和值:

实例
1
2
3
4
declare -A site
site["google"]="www.google.com"
site["runoob"]="www.runoob.com"
site["taobao"]="www.taobao.com"

关联数组的访问

访问关联数组元素可以使用指定的键,格式如下:

实例
1
2
3
4
5
6
declare -A site
site["google"]="www.google.com"
site["runoob"]="www.runoob.com"
site["taobao"]="www.taobao.com"

echo ${site["runoob"]}

执行脚本,输出结果为:

result
1
www.runoob.com

对于一些不确定的大量数组,我们可以直接使用默认的编号方式赋值或取出结果,然后可以用循环来处理每一条记录。但是对于某些数量有限,且有一些特意义的情况使用关联数组可以大大提高程序的可读性。例如,我们需要在系统中检测一下是否存在命令unar, 如果不存在则安装程序,但是程序的名称却是unarchiver, 因此可以定义一个关联数组解决:

实例
1
2
3
declare -A cmd
cmd["unar"]="unarchiver"
sudo pacman -S ${cmd["unar"]}

在不同的系统中存在不同的软件包管理器,此时可以通过定义关系数组来实现统一的管理脚本程序。

关联数组的特殊值

关联数组的特殊值
1
2
3
4
5
6
7
8
declare -A site
site["google"]="www.google.com"
site["runoob"]="www.runoob.com"
site["taobao"]="www.taobao.com"

echo ${#site[*]} #输出数组的元的个数(大小)
echo ${!site[*]} #输出全部的键名
echo ${site[*]} #输出全部的值

注意:上述echo中的*可以替换成@,效果等价。

前言:

EOFEnd Of File的缩写,表示自定义终止符。既然自定义,那么EOF就不是固定的,可以随意设置别名,意思是把内容当作标准输入传给程序,Linux中按Ctrl-d就代表EOF

Shell中我们通常将EOF<< 结合使用,表示后续的输入作为子命令或子Shell的输入,直到遇到EOF为止,再返回到主调Shell。回顾一下<<的用法,当Shell看到<<的时候,它就会知道下一个词是一个分界符。在该分界符以后的内容都被当作输入,直到Shell又看到该分界符(位于单独的一行)。这个分界符可以是你所定义的任何字符串。

用法:

1
2
3
<<EOF        //开始
....
EOF //结束

也可以自定义,如:

1
2
3
<<FFF        //开始
....
FFF //结束

注意:

第一个EOF必须以重定向字符<<开始,第二个EOF必须顶格写,否则会报错。如果将开始处的<<切换为<<-, 则结束分界符EOF前有制表符或者空格,则EOF此时仍然被当做结束分界符,此时EOF不必顶格写。

EOF配合cat能够进行多行文本输出。

通过cat配合重定向能够生成文件并追加操作,在它之前先回顾几个特殊符号:

1
2
3
4
<   :输入重定向
> :输出重定向
>> :输出重定向,进行追加,不会覆盖之前内容
<< :标准输入来自命令行的一对分隔号的中间内容

例:

1
2
3
4
5
6
7
[root@localhost ~]# cat <<EOF   //运行后会出现输入提示符">"
> Hello
> wolrd
> EOF
输入结束后,在终端显示以下内容:
Hello
wolrd

1. 向文件file1.txt中输入内容

1
2
3
4
5
6
7
8
9
10
[root@localhost ~]# cat >file1.txt <<EOF
> aaa
> bbb
> ccc
> EOF

[root@localhost ~]# cat file1.txt
aaa
bbb
ccc

追加内容至file1.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@localhost ~]# cat >>file1.txt <<EOF
> 111
> 222
> 333
> EOF

[root@localhost ~]# cat file1.txt
aaa
bbb
ccc
111
222
333

覆盖file1.txt

1
2
3
4
5
6
[root@localhost ~]# cat >file1.txt <<EOF
> Hello wolrd
> EOF

[root@localhost ~]# cat file1.txt
Hello wolrd

2. 自定义EOF

1
2
3
4
5
6
7
8
[root@localhost ~]# cat >file1.txt <<FFF
> test
> hello
> FFF

[root@localhost ~]# cat file1.txt
test
hello

3. 编写一个脚本,生成mysql配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@localhost ~]# vim /root/test.sh
#!/bin/bash
cat >/root/EOF/my.cnf <<EOF
[client]
port=3306
socket=/usr/local/mysql/var/mysql.sock
basedir=/usr/local/msyql/
datadir=/data/mysql/data
pid-file=/data/mysql/data/mysql.pid
user=mysql
server-id=1
log_bin=mysql-bin
EOF

[root@localhost ~]# cat /root/EOF/my.cnf //查看生成的mysql配置文件
[client]
port=3306
socket=/usr/local/mysql/var/mysql.sock
basedir=/usr/local/msyql/
datadir=/data/mysql/data
pid-file=/data/mysql/data/mysql.pid
user=mysql
server-id=1
log_bin=mysql-bin

4. 编写脚本向mysql数据库建表、赋值并查询

1
2
3
4
5
6
7
8
9
10
[root@localhost ~]# vim eof.sh
#!/bin/bash
mysql -uroot -p123456 <<EOF
use test;
create table data(name varchar(15),age int,address varchar(25));
desc test.data;
insert into data values("tom",23,"china");
select * from data;
exit
EOF

4.1 运行脚本查看执行结果

1
2
3
4
5
6
7
8
9
[root@localhost ~]# bash eof.sh    //运行脚本,查看数据库中信息
mysql: [Warning] Using a password on the command line interface can be insecure.
Field Type Null Key Default Extra
name varchar(15) YES NULL
age int(11) YES NULL
address varchar(25) YES NULL

name age address
tom 23 china

4.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
[root@localhost ~]# mysql -uroot -p123456
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| test |
+--------------------+

mysql> use test;

mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| data |
+----------------+

mysql> desc test.data;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| name | varchar(15) | YES | | NULL | |
| age | int(11) | YES | | NULL | |
| address | varchar(25) | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+

mysql> select * from data;
+------+------+---------+
| name | age | address |
+------+------+---------+
| tom | 23 | china |
+------+------+---------+

5. EOF中的变量处理

在使用cat EOF中出现$变量通常会直接被执行,显示执行的结果。若想保持$变量不变需要使用 \ 符进行注释 . 当存在$变量过多,或存在赋值命令的时候可直接在EOF上加上双引号就行。

EOF处理变量
1
2
3
4
5
6
7
8
9
10
11
cat << "EOF" > ~/.gitconfig
# Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.zshrc.
# Initialization code that may require console input (password prompts, [y/n]
# confirmations, etc.) must go above this block; everything else may go below.
if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
fi
source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh
source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
source /usr/share/zsh-theme-powerlevel10k/powerlevel10k.zsh-theme
EOF

简介

ping (Packet Internet Groper)是一种因特网包探索器,用于测试网络连接量的程序。Ping是工作在 TCP/IP网络体系结构中应用层的一个服务命令, 但是缺点很明显,不够直观。可使用gping继续替代。gping是一个跨平台的命令行程序,当您尝试ping主机或网站时,该程序会在终端内部显示漂亮的图形。

github地址

https://github.com/orf/gping

安装方式

install gping
1
sudo pacman -S gping

使用方式

1
2
gping --help #查看帮助文档
gping -V #查看版本信息

实际运用

1
gping www.google.com
gping

取代ping

~/.zshrc
1
alias ping="gping"

这个简单的 ls 命令列出目录的内容十分方便,但是直到我发现 exa 之前从来没想过会有命令能替代它。

exa 命令简介

exa 是一个命令行工具,可以列出指定路径(如未指定则是当前目录)的目录和文件。这也许听起来很熟悉,因为这就是 ls 命令所做的事情。

exa 被视作从 UNIX 旧时代延续至今的古老的 ls 命令的一个现代替代品。如其所声称的那样,它有比 ls 命令更多的功能、更好的默认行为。

exa 功能

以下是一些你应该使用 exa 替代 ls 的原因:

  • exals 一样可移植(在所有主流 Linux 发行版、*BSD 和 macOS 上可用)
  • 默认彩色输出
  • exa 不同格式化的“详细”输出也许会吸引 Linux/BSD 新手
  • 文件查询是并行进行的,这使得 exals 的性能相当
  • 显示单个文件的 git 暂存或未暂存状态

exa 的另外一个不同的地方是它是用 Rust 编写的。顺便说一句,Rust 与 C 语言的执行速度相近,但在编译时减少了内存错误,使你的软件可以快速而安全地执行。

在 Linux 系统上安装 exa

exa 最近很流行,因为许多发行版开始将其包括在其官方软件库中。也就是说,你应该可以使用你的 [发行版的包管理器] 来安装它。

从 Ubuntu 20.10 开始,你可以使用 apt 命令来安装它:

1
sudo apt install exa

Arch Linux 已经有了它,你只需要 使用 pacman 命令即可:

1
sudo pacman -S exa

如果它无法通过你的包管理器安装,请不要担心。毕竟它是一个 Rust 包,你可以很容易地用 Cargo 安装它。请确保在你使用的任何发行版 或 Ubuntu 上安装了 Rust 和 Cargo

安装 Rust 和 Cargo 后,使用此命令安装 exa

1
cargo install exa

使用 exa

exa 有很多命令选项,主要是为了更好的格式化输出和一些提高舒适度的改进,比如文件的 git 暂存或未暂存状态等等。

下面是一些屏幕截图,展示了 exa 是如何在你的系统上工作的。

简单地使用 exa 命令将产生类似于 ls 但带有颜色的输出。这种彩色的东西可能没有那么吸引人,因为像 Ubuntu 这样的发行版至少在桌面版本中已经提供了彩色的 ls 输出。不过,ls 命令本身默认没有彩色输出。

1
exa
exa 命令的输出截图,没有任何额外的标志

请注意,exals 命令的选项不尽相同。例如,虽然 -l 选项在 exals 中都给出了长列表,但 -h 选项添加了一个列标题,而不是 ls 的人类可读选项。

1
exa -lh
正如我之前提到的,exa 有列标题以获得更好的“详细”输出

我前面说过,exa 已经内置了 Git 集成。下面的屏幕截图给出了 –git 标志的演示。请注意 test_filegittracked 列中显示 -N ,因为它尚未添加到存储库中。

1
exa --git -lh
演示 git 标志如何与 exa 一起工作

下面的例子不是我的猫键入的。它是各种选项的组合。exa 有可供你尝试和探索的很多选项。

1
exa -abghHliS

你可以通过在终端中运行以下命令来获取完整的选项列表:

1
exa --help

但是,如果你想了解 exa 所提供的功能,可以查看其 Git 存储库 上的 官方文档。值得从 ls 切换到 exa 吗?

对于类 UNIX 操作系统的新手来说,exa 可能是用户友好的,它牺牲了在脚本中容易使用的能力,以换取“易用性”和外观。其中,显示得更清楚并不是一件坏事。

无论如何,ls 就像通用命令。你可以将 exa 用于个人用途,但在编写脚本时,请坚持使用 ls。当预期输出与任一命令中的实际输出不匹配时,ls 和 exa 之间一个 [或多个] 标志的差异可能会让你发疯。

我想知道你对 exa 的看法。你已经尝试过了吗?你对它的体验如何?

但是,如果你想了解 exa 所提供的功能,可以查看其 Git 存储库 上的 官方文档

值得从 ls 切换到 exa 吗?

对于类 UNIX 操作系统的新手来说,exa 可能是用户友好的,它牺牲了在脚本中容易使用的能力,以换取“易用性”和外观。其中,显示得更清楚并不是一件坏事。

无论如何,ls 就像通用命令。你可以将 exa 用于个人用途,但在编写脚本时,请坚持使用 ls。当预期输出与任一命令中的实际输出不匹配时,lsexa 之间一个 [或多个] 标志的差异可能会让你发疯。

我想知道你对 exa 的看法。你已经尝试过了吗?你对它的体验如何?

本文转自 exa:一个 ls 命令的现代替代品,如有侵权,请联系删除。