命令行处理与版本控制全解析
命令行处理
在使用shell时,命令行处理是一个核心环节,它涉及多个步骤,理解这些步骤对于成为shell脚本编写专家或解决复杂问题至关重要。
命令行处理步骤
每一行从标准输入(STDIN)或脚本中读取的内容被称为管道(pipeline),因为它包含一个或多个由零个或多个管道字符(|)分隔的命令。shell处理命令行的步骤如下:
1.
分割为标记
:将命令按固定的元字符(空格、制表符、换行符、;、(、)、<、>、| 和 &)分割成标记,标记类型包括单词、关键字、I/O重定向符和分号。
2.
检查首个标记
:查看每个命令的第一个标记是否为无引号或反斜杠的关键字。如果是如
if
等控制结构开头的关键字,该命令为复合命令,shell会为其内部设置相关参数,然后读取下一个命令并重新开始处理;若不是复合命令开头的关键字,shell会提示语法错误。
3.
检查别名
:将每个命令的第一个单词与别名列表进行匹配。若找到匹配项,用别名的定义替换,然后回到步骤1;若未找到,则进入步骤4。此机制允许递归别名,还能为关键字定义别名。
4.
花括号扩展
:例如
a{b,c}
会扩展为
ab ac
。
5.
波浪号扩展
:若波浪号(~)在单词开头,将其替换为用户的主目录($HOME)。
6.
用户主目录替换
:将
~user
替换为用户的主目录。
7.
参数替换
:对以美元符号($)开头的表达式进行参数(变量)替换。
8.
命令替换
:对
$(string)
形式的表达式进行命令替换。
9.
算术替换
:计算
$((string))
形式的算术表达式。
10.
单词分割
:将参数、命令和算术替换后的部分再次分割成单词,这次使用
$IFS
中的字符作为分隔符。
11.
路径名扩展
:对出现的
*
、
?
和
[/]
对进行路径名扩展(通配符扩展)。
12.
命令查找
:按以下顺序查找第一个单词作为命令的来源:函数命令、内置命令、
$PATH
中任何目录下的可执行文件。
13.
执行命令
:设置好I/O重定向等操作后执行命令。
以下是一个命令行处理的示例:
假设已经执行了
alias ll="ls -l"
,用户
alice
的主目录为
/home/alice
,且有变量
$$
的值为
2537
。现在处理命令
ll $(type -path cc) ~alice/.*$(($$%1000))
,其处理过程如下:
1.
ll $(type -path cc) ~alice/.*$(($$%1000))
分割输入为单词。
2.
ll
不是关键字,步骤2无操作。
3.
ls -l $(type -path cc) ~alice/.*$(($$%1000))
用
ls -l
替换别名
ll
,然后重复步骤1 - 3,步骤2将
ls -l
分割为两个单词。
4.
ls -l $(type -path cc) ~alice/.*$(($$%1000))
无操作。
5.
ls -l $(type -path cc) /home/alice/.*$(($$%1000))
将
~alice
扩展为
/home/alice
。
6.
ls -l $(type -path cc) /home/alice/.*$((2537%1000))
用
2537
替换
$$
。
7.
ls -l /usr/bin/cc /home/alice/.*$((2537%1000))
对
type -path cc
进行命令替换。
8.
ls -l /usr/bin/cc /home/alice/.*537
计算算术表达式
2537%1000
。
9.
ls -l /usr/bin/cc /home/alice/.*537
无操作。
10.
ls -l /usr/bin/cc /home/alice/.hist537
用文件名替换通配符表达式
.*537
。
11. 找到命令
ls
在
/usr/bin
中。
12. 执行
/usr/bin/ls
并带上选项
-l
和两个参数。
命令行处理步骤的流程可以用以下mermaid流程图表示:
graph LR
A[分割为标记] --> B[检查首个标记]
B --> |非关键字| C[检查别名]
B --> |开头关键字| D[处理复合命令]
B --> |其他关键字| E[语法错误]
C --> |是别名| A
C --> |非别名| F[花括号扩展]
F --> G[波浪号扩展]
G --> H[参数替换]
H --> I[命令替换]
I --> J[算术替换]
J --> K[单词分割]
K --> L[路径名扩展]
L --> M[命令查找]
M --> N[执行命令]
引用
引用是让shell跳过部分命令行处理步骤的方法,主要有单引号和双引号两种:
-
单引号(’‘)
:绕过步骤1 - 10,包括别名替换。单引号内的所有字符保持不变,且单引号内不能再使用单引号,即使使用反斜杠转义也不行。
-
双引号(”“)
:绕过步骤1 - 4以及步骤9和10。双引号内忽略管道字符、别名、波浪号替换、通配符扩展和通过分隔符分割单词,但允许参数替换、命令替换和算术表达式计算。可以使用反斜杠转义双引号、
$
、反引号(`)和反斜杠本身。
以下是引用的示例表格:
| 表达式 | 值 |
| ---- | ---- |
|
$person
|
hatter
|
|
"$person"
|
hatter
|
|
\$person
|
$person
|
|
'$person'
|
$person
|
|
"'$person'"
|
’hatter’
|
|
~alice
|
/home/alice
|
|
"~alice"
|
~alice
|
|
'~alice'
|
~alice
|
在shell编程中,除非特别需要参数、命令或算术替换,否则使用单引号更安全。
eval命令
eval
命令可以让命令行处理过程再次执行,这看似奇怪,但实际上非常强大,它能让脚本动态创建命令字符串并传递给shell执行,使脚本在运行时能修改自身行为。
下面通过示例来理解
eval
的作用:
- 简单示例:
eval ls
将字符串 “ls” 传递给shell执行,shell会打印当前目录下的文件列表。
- 复杂示例:
listpage="ls | more"
$listpage
上述代码不会产生分页的文件列表,因为shell在计算变量时,管道字符在特定步骤之后才被识别,导致
|
和
more
被当作
ls
的参数,
ls
会报错找不到这些文件名。
而使用
eval $listpage
时,shell会将
ls
、
|
和
more
作为参数再次进行命令行处理,在步骤2中找到
|
并将命令分割为
ls
和
more
,最终实现分页显示文件列表。
eval
是一个高级特性,需要一定的编程技巧才能有效使用,它赋予了程序类似人工智能的能力,能让程序“编写”并执行其他程序,虽然在日常shell编程中可能不常用,但值得深入理解。
版本控制
版本控制系统(Revision Control Systems)不仅能让我们追溯代码的历史,还能查看不同时间点的变更情况,也被称为版本管理系统。它允许我们维护项目文件的中央存储库,跟踪文件的更改及原因,部分系统还支持多人同时协作处理同一项目甚至同一文件。
版本控制系统在现代软件开发中至关重要,同时在文档编写、系统配置跟踪(如
/etc
)和书籍创作等领域也有广泛应用。其主要优点包括:
1.
数据安全
:在存储库妥善备份的情况下,能有效防止代码丢失。
2.
变更管理
:便于实施变更控制,鼓励记录变更原因。
3.
多人协作
:支持多地人员共同参与项目,避免数据覆盖丢失。
4.
多地点工作
:允许个人在不同地点工作而不丢失进度。
5.
变更追溯
:可轻松撤销变更,查看不同版本间的具体差异(二进制文件除外),结合有效日志还能了解变更原因。
6.
关键字扩展
:通常支持在非二进制文件中嵌入版本元数据。
常见的免费和商业版本控制系统众多,下面介绍三种常见系统:CVS、Subversion和RCS,它们在主流现代操作系统中均可使用。
在使用版本控制系统前,需要确定以下几点:
1.
系统选择
:决定使用哪种版本控制系统。
2.
存储库位置
:确定中央存储库的位置(如适用)。
3.
项目结构
:规划存储库中项目或目录的结构。
4.
策略制定
:制定更新、提交、标记和分支策略。
CVS(Concurrent Versions System)
CVS是一个广泛使用且成熟的版本控制系统,提供了适用于主流现代操作系统(包括Windows)的命令行工具,部分系统还有GUI工具。
CVS的优缺点
| 优点 | 缺点 |
|---|---|
| 应用广泛且成熟 | 提交操作非原子性,可能导致存储库不一致 |
| 众多Unix管理员和开源开发者熟悉 | 按文件提交,如需引用一组文件需使用标签 |
| 简单项目易于使用 | 目录结构支持较差 |
| 便于访问远程存储库 | 难以在保留历史记录的同时重命名文件和目录 |
| 基于RCS,可对中央存储库进行一定修改 | 对二进制文件和符号链接等对象支持不佳 |
CVS按文件跟踪版本,每个文件有独立的内部版本号,因此单个项目不能用单一版本号跟踪,可使用标签实现此类跟踪。
CVS操作示例
以下是使用CVS的基本操作步骤:
1.
创建新存储库
:
/home/jp$ mkdir -m 0775 cvsroot
/home/jp$ chmod g+srwx cvsroot
/home/jp$ cvs -d /home/jp/cvsroot init
- 创建新项目并导入 :
/home/jp$ cd /tmp
/tmp$ mkdir 0700 scripts
/tmp$ cd scripts/
/tmp/scripts$ cat << EOF > hello
> #!/bin/sh
> echo 'Hello World!'
> EOF
/tmp/scripts$ cvs -d /home/jp/cvsroot import scripts shell_scripts NA
- 检出项目并更新 :
/tmp/scripts$ cd
/home/jp$ cvs -d /home/jp/cvsroot/ checkout scripts
/home/jp$ cd scripts
/home/jp/scripts$ ls -l
- 修改文件并检查状态 :
/home/jp/scripts$ echo "Hi Mom..." >> hello
/home/jp/scripts$ cvs status
/home/jp/scripts$ cvs -qn update
- 添加新脚本到版本控制 :
/home/jp/scripts$ cat << EOF > mcd
> #!/bin/sh
> mkdir -p "$1"
> cd "$1"
> EOF
/home/jp/scripts$ cvs add mcd
- 提交更改 :
/home/jp/scripts$ cvs commit
- 更新沙箱、再次修改并查看差异 :
/home/jp/scripts$ cvs update
/home/jp/scripts$ vi hello
/home/jp/scripts$ cvs diff hello
- 提交更改并避免使用编辑器 :
/home/jp/scripts$ cvs -m '* Fixed syntax error' commit
- 查看文件历史 :
/home/jp/scripts$ cvs log hello
- 添加版本元数据并提交 :
/home/jp/scripts$ vi hello
/home/jp/scripts$ cat hello
#!/bin/sh
$Id$
echo 'Hello World!'
echo 'Hi Mom...'
/home/jp/scripts$ cvs ci -m'Added ID keyword' hello
CVS的基本操作流程可以用以下mermaid流程图表示:
graph LR
A[创建存储库] --> B[创建项目并导入]
B --> C[检出项目]
C --> D[修改文件]
D --> E[检查状态]
E --> F[添加新文件]
F --> G[提交更改]
G --> H[更新沙箱]
H --> I[再次修改]
I --> J[查看差异]
J --> G
G --> K[查看文件历史]
K --> L[添加元数据]
L --> G
通过以上介绍,我们了解了命令行处理的详细步骤、引用和
eval
命令的使用,以及版本控制系统的重要性和CVS的基本操作,这些知识对于提高编程效率和项目管理能力具有重要意义。
超级会员免费看
3万+

被折叠的 条评论
为什么被折叠?



