Linux Basics-Linux Bash历史和其概念名词解释part1

Linux Basics-Linux Bash历史和其概念名词解释part1

阅读本文你将知道:查看更多Linux Basics-Linux Bash历史和其概念名词解释part1BashFAQ.pdf

  Bash的历史及其特性

  Bash的如何工作

  Bash的概念解释

 


前提知识:

     对linux有一定基础而且了解Bash的简单命令

说明:

1.    资料参考将于文章后面列出,资料的参考源于wiki或者Bash作者写的Bash简介或者开源组织公开的手册,原文的阅读建议英文比较好的同学去参考

2.    对于Linux Bash的上的疑问和错误问题解答,可以参考我给的BashFAQ,亦可留言讨论

3.    资料为整合


 

Part 1  Bash历史及其简介

Ken Thompson 在创建了UNIX之后,于 1971 年开发了第一个用于 UNIX 的 shell(命令行解释器),名为 V6 shell。类似于它在 Multics 中的前身,这个 shell (/bin/sh) 是一个在内核外部执行的独立用户程序, Bourne shell 由 Stephen Bourne 在 AT&T Bell Labs 为 V7 UNIX 创建,作者在研究 ALGOL68 编译器之后开发了 Bourne shell,所以您会发现它的语法比其他 shell 更加类似于 Algorithmic Language (ALGOL)。尽管使用 C 开发,源代码本身甚至使用了宏赋予它一种 ALGOL68 特色。

Bourne shell 有两个主要目标:用作一个命令解释器来交互式执行操作系统命令,以及用于编写脚本(编写可通过 shell 调用的可重用脚本)。 除了取代 Thompson shell,Bourne shell 还提供了相对于其前身的多项优势。Bourne 向脚本中引入了控制流、循环和变量,提供了一种更加强大的语言来与操作系统交互(包括交互式和非交互式)。该 shell 还允许您使用 shell 脚本作为过滤器,提供对处理信号的集成支持,但缺乏定义函数的能力。 最后,它整合了我们如今使用的许多功能,包括命令替换(使用反引号)和用于将保留的字符串文字嵌入到脚本中的 HERE 文档。再后来就出现了Bash,Bash是出现在GUN操作系统上,常常用在Linux中,

Bash是 Bourne-Again Shell的简写 — 这是关于Bourne shell(sh)的一个双关语(Bourne again / born again), 诞生于 1987 年,是作为 GNU 项目开发的,主要由Stephen Bourne所写,它最最早期的作者是Brian FoxBrian FoxFree Software Foundation的雇员,之后Bash就由美国(Case Western Reserve University in Cleveland, Ohio) 凯斯西储大学的志愿者接管维护,最后的发展就是现在集成到linux系统中成为系统的标配. 现在是由Chet Ramey管理,Bash是Linux系统预安装的默认 shell 环境。

Bash 的优点之一是它具有内置的安全特性。Bash 会准确地记录用户输入的命令,并把记录写到用户主目录中的隐藏文件 .bash_history 中。bash 的语法具有其他 shell 所没有的许多扩展。与其他 shell 相比,bash 中的整数计算更高效,而且 bash 可以更方便地重定向标准输出(stdout)和标准错误(stderr)。

Bash 还非常适合于安全性要求高的环境,它具有受限制的启动模式,可以把 shell 中的用户限制为只能执行一组确定的命令。可以通过编辑自己的 bash shell 登录控制文件(即 .bashrc、.bash_profile、.bash_logout 和 .profile 等隐藏文件)定制登录 shell。

 


基本 shell 架构

一种假想的 shell 的基本架构很简单(Bourne 的 shell 就是一个证据)。在图 2中可以看到,基本架构看起来类似一个管道,其中会分析和解析输入,展开符号(使用各种方法,比如括号、波浪号、变量、参数扩展和替换,以及文件名生成),最终执行命令(使用 shell 内置的命令或外部命令)。

图 2. 假想 shell 的简单架构

blob.png

bash 登录过程

在登录时,用户通常执行一个全局概要文件和个人文件(.bash_profile 和 .bashrc)

图 3. bash shell 登录过程细节

blob.png

在此之后,用户就可以使用 bash shell 环境变量 $PATH 中指定的标准命令集,比如内置命令等

forking

命令或 bash shell 本身可能启动(或生成)新的 shell 子进程以执行某一任务,这称为 forking。当这个新进程(子进程)正在执行时,父进程仍然在运行。如果父进程先于子进程死亡,那么子进程就成了死进程(也称为僵尸进程 )


Bash如何运行

参考如下:

Bash Component Architecture

bash的组件架构:

 

blob.png

注释:

1 input

输入处理

1.1  taking characters from the terminal or a file, breaking them into lines, and passing the lines to the shell parser to transform into commands.

将用户输入的内容输送到命令流中,通过shell解释器翻译成命令

1.2 用户输入的内容Readline 库来记录用户输入的历史命令,每个字符作为一个键盘映射或者调度表(keymap, or dispatch table)也可以进行multiple-character(多字符)处理,别人判别键盘输入的快捷键,(ctrl+c就是终止程序的意思),每个字符八个字节

 


lexical analysis and Parsing

词法分析解析

先是把字符流组成一个单词(to separate the stream of characters into words),单词是解析器操作的基本单位,它是由元字符(metacharacter分隔的字符序列,元字符包括空格,制表符,&,分号等等

Shell解释器在任何情况下接受从readline输入的内容(readline就好比是read和line组成的一个单词,作者是Brian Fox,同时也是bashl最初的作者,readline提供命令行编辑功能和命令历史概念。read就是读的意思,从键盘中读取的意思,line的意思就是说,从键盘中read的字符character,把字符组装成为line,这个时候line就可以理解为一条字符流,之后把line送到shell解释器中,解释器翻译成为commend命令)

Shell使用转义符来消除字符的特色意义,转义符是用反斜杠表示,在解析之后,先进行字展开例如 $OSTYPE被替换为字符串 "linux-gnu",上述的例子是一个变量引用,变量的引用用$来引用,Bash的弱类型语言,除了几个少数例外,其他一般都视作字符串,在处理顺序上一般先是处理括号,比如pre{one,two,three}post会变成preonepost pretwopost prethreepost

Bash支持重定向,管道,内建命令,复合命令,作业控制等等

 


Part 2 附录:(概念解释)

 

1.Syntactic Units and Primitives

语法和原始数据类型

原始数据类型:

1对于Bash,有三种基本的标记(称为token),它们分别是: reserved words(保留字), words, and operators(运算符), reserved words一般是对于shell有特色意义的保留词,比如控制流程语句中的ifwhile, operators包括比如 | 或者 > 


 2.Bash中的元字符

Bash中的元字符是一些有着特殊含意的字符, 在Bash的解释中不是按照字面含意来解释的:

Charactors

Meaning

* ?   [ ] ~ + – @ !

Filename   metacharactors

Filename Metacharactors

In computer programming, the verb glob or globbing is used to refer to an instance of pattern matching behavior. The noun "glob" is sometimes used to refer to a particular pattern, e.g. "use the glob .log to match all those log files".

 

Bash用来匹配文件名的元字符:

               匹配零个或者多个字符

 ?              匹配单个字符

 [abc…]       匹配包含在集合中任意字符

 [!abc…]      匹配不包含在集合中的任意字符

 [a-zA-Z0-9]    匹配指定的字符范围中的字符

                (可移植性不强,根据区域设置,字符范围可能包含其他字符)

 ~              当前用户的Home目录

 ~name          name用户的Home目录

 ~+             当前的工作目录($PWD)

 ~-             上次的工作目录($OLDPWD)

 

下面即为一些特殊字符及其用途:

  • [空白]:空白字符(whitespace,包括空格、制表符及新行)。BASH 使用空白字符来判定词语的开头与结尾。每个命令的第一个词语为命令名称;任何其他词语则为该命令的参数。

  • $:展开字符。该字符用于大多数的替换,包括参数展开(变量代换)。以后会详述。

  • '文本':单引号可用于防止其中的文本被 shell 以任何形式展开,并阻止其分为多个词语或参数。他们也可防止任何在其内部的特殊字符的特殊意义。

  • "文本":双引号阻止其内部的文本分割为多个词语或参数,但它允许进行代换。它可阻止其内部的多数特殊字符——简单地说,所有除 $ 以外的字符。

  • #:注释字符。任何以 # 为开头的词语为注释的开始,并一直到行末。shell 不处理注释。

  • ;:命令分隔符。用户在把多个命令放在一行写时,分号用来把它们隔开。

  • \:转义字符:反斜杠用来阻止下一个字符的特殊用途。它在双引号内或引号外有效,但在单引号内无效。

  • ~:波浪线代表家目录。它单独使用或在后接一个      /时,与 $HOME 相同。在后接一个用户名时,则表示该用户的家目录。例:cd ~john/bin; cp coolscript ~/bin

  • > 或 <:重定向字符。这些字符用来更改(重定向)一命令的输入和/或输出。重定向会在以后谈到。

  • |:管道可以将一命令的输出传递给另一命令作为输入。

  • 表达式:测试表达式。测试表达式将条件表达式作为逻辑语句来求值,以判定其为“真”还是“假”。

  • { 命令; }:命令分组。括号中的命令被看作是一个命令。这在 BASH 的句法要求只可有一个命令,而你又找不到相应的可靠功能的时候很方便。

  • `命令`、$(命令):命令代换。(强烈推荐写作后一种形式。)命令代换先执行其内部的命令,然后将整个 `…` 或 $(…) 替换为该命令的输出。

  • (命令):子 shell 执行。这会在一个新的 bash shell 而非当前 shell 里面执行该命令。如果该命令有副作用(如更改变量),这些更改将不会影响当前 shell。

  • ((表达式)):算术命令。在括号内,算符 +、-、* 及 /等都被看作数学算符。这可以用来进行如 ((a=b+7)) 的赋值,及 if ((a < b)) 这样的测试。以后会详述。

  • $((表达式)):与上面的类似,只是该表达式会替换为算术运算的结果。例:echo "The average is $(( (a+b)/2 ))"。

 

 

   

Char.

Description

" "

Whitespace — this is a tab,   newline, vertical tab, form feed, carriage return, or space. Bash uses   whitespace to determine where words begin and end. The first word is the   command name and additional words become arguments to that command.

$

Expansion — introduces   various types of expansion: parameter expansion   (e.g. $var or ${var}), command substitution (e.g. $(command)),   or arithmetic expansion (e.g. $((expression))). More on expansions   later.

''

Single quotes — protect the text   inside them so that it has a literal meaning.   With them, generally any kind of interpretation by Bash is ignored: special   characters are passed over and multiple words are prevented from being split.

""

Double quotes — protect the text   inside them from being split into multiple words or arguments, yet allow   substitutions to occur; the meaning of most other special characters is   usually prevented.

\

Escape — (backslash) prevents the next character from   being interpreted as a special character. This works outside of quoting,   inside double quotes, and generally ignored in single quotes.

#

Comment — an introduction of a # character   begins a commentary that extends to the end of the line. Comments are notes   of explanation and are not processed by the shell.

[[]]

Test — an evaluation of a conditional expression to   determine whether it is "true" or "false". Tests are used   in Bash to evaluate a number of conditions. More of this will be covered   later.

!

Negate — used to negate or reverse a test or exit   status. For example: ! grep text file; exit $?.

>< 

Redirection — redirect a   command's output or input. Redirections will be covered   later.

|

Pipe — redirect output from a initial command to   the input of secondary command. This is a method of chaining commands   together.   Example:echo "Hello beautiful." | grep -o beautiful.

;

Command separator — a representation   of a newline. Used to separate multiple commands that are on the same line.

{}

Inline group — commands inside   the curly braces are treated as if they were one command. It is convenient to   use these when Bash syntax requires only one command and a function doesn't   feel warranted.

()

Subshell group — similar to the   above but where commands within are executed in subshell. Used much like a   sandbox, if a command causes side effects (like changing variables), it will   have no effect on the current shell.

(())

Arithmetic expression — with an arithmetic   expression, characters such as +, -, *, and / are   mathematical operators used for calculations. They can be used for variable   assignments like (( a = 1 + 4 )) as   well as tests like if (( a < b )). More on   this later.

$(())

Arithmetic expansion — Comparable to the   above, but the expression is replaced with the result of its arithmetic   evaluation. Example:echo "The average is $(( (a+b)/2 ))".

~

Home directory — the tilde is a   representation of the home directory. When followed by a /, it means the   current user's home directory; otherwise, a username will have to be   specified (e.g. ls ~/Documents; cp ~john/.bashrc .).

 

 


commend and arguments

命令与参数

BASH 从输入中读入命令(一般为终端或文件),它将自己读入的每一行输入都视为一条命令

BASH 在每一个空白字符(空格与 tab)处将每一个划分为不同的词语。BASH 第一个看到的命令为要执行的命令的名称

在本文中,$在一行的开头代表你的BASH提示。传统上,一个shell提示符要么用$,%或#来表示结束。如果结尾$,这表明一个shell,与Bourne shell的兼容(如POSIX外壳或Korn shell中,或Bash)。如果结尾%,这表明一个C shell中(csh或tcsh); 本文不包括C shell

 


命令的分类

BASH 理解多种不同种类的命令:函数、内置命令、关键词及可执行文件,别名

  • 函数:BASH 的函数与别名有些相似,但功能更为强大。与别名不同,它们可用于脚本。函数包含 shell 命令,很像一个小脚本;它甚至能够读入参数与创建本地变量。当函数被调用时,其中的命令就会执行。函数部分将会在本指南稍后的部分讲到。

  • 内置命令:BASH 内置了一些基本命令,如 cd(change directory,更换目录)、echo(写出输出)等等。你可以把它们看作是 BASH 已经提供的函数。

  • 关键词:关键词与内置命令很相似,但主要的不同是对于它们适用不同的语句分析规则。比如,[ 是 bash 内置命令,而      [[ 则是 bash 关键词。他们都是用来测试的命令,但由于 [[ 是一个关键词而非一个内置命令

  • 可执行命令:最后一种 BASH 可以执行的命令是可执行命令。也叫做外部命令或应用程序。可执行命令可以使用路径名来执行。如果该可执行命令在当前目录下,使用 ./myprogram。如果它在 /usr/local/bin 目录下,使用 /usr/local/bin/myprogram

  • 别名是一种缩短命令的方式。它们只能用于交互式 shell,不能用于脚本。用命令alias查看


脚本

脚本(script)可大致看作是文件里面的一系列命令。BASH 读取该文件并按顺序处理命令。它只有在当前命令运行结束时才会继续运行下一文件,除非当前文件是异步(asynchronously)运行的(在后台运行)。先不用担心后一种情况——你以后会学到它是怎么回事。

实际上我们这篇指南中的每一个命令都既可运行于命令行,又可用于文本中。

编写脚本很容易,只需创建一个新文件,并在顶部写入下面的语句:

 #!/usr/bin/env bash

该标头(header,也叫做 hashbang 或 shebang)用于确保脚本在执行时会使用 BASH 作为解释器。它工作的方式为,当内核执行非二进制文件时,它会查看该文件的第一行。如果这一行以 #! 开头,内核就会使用该行来判定将把代码交给哪一个解释器。(也可以用其他方法——见下文。)#!必须在文件的开头,前面不许有空格或空白行。你的脚本命令应该都放在下面不同的行中。

不要被网上使用 /bin/sh 的例子给欺骗了,sh 不是 bash。尽管 sh 的句法与 bash 的看起来很像,尽管大多数 bash 脚本可以在 sh 中运行,本指南中的许多例子只适用于 bash,如果使用 sh 的话会中断或异常行为。

而且,不要给脚本一个 .sh 扩展名,它是没用的,而且会误导(因为这是 bash 脚本而非 sh 脚本)。

顺便提一下,如果你使用 Windows 编写脚本完全没问题,但要尽可能地避免使用记事本(Notepad)来编写脚本。微软的记事本只能创建以 DOS 样式作行尾的文件。这意味着你在记事本里面写的每一行都会以两个字符作结尾:一个回车和一个换行符。BASH 只会以换行符来判断一行结束。结果,如果你不知道每一行还有一个回车符的话,它会给你带来很大的难题(非常奇怪的错误提示信息)。尽可能地使用一个合适的编辑器,如 Vim、Emacs、kate、GEdit、GVIM 或 xemacs。否则,你必须在运行脚本之前移除回车符。

当脚本编写完毕后,可以这样调用它:

$ bash myscript

在本例中,我们执行 BASH并告诉它读入我们的脚本。在我们这样做的时候,#!只是一个注释,BASH 会忽略它。

还有一种方式是,你可以给你的脚本一个可执行权限。这样,你就可以把它当作一个应用程序来执行,而非手动调用 BASH。

   

 $ chmod +x myscript
    $ ./myscript

以这种方式执行时,#! 一行就会告诉操作系统(OS)使用哪一个解释器。系统会运行 /usr/bin/env,然后运行 bash。bash 再读入我们的脚本。BASH 与上次一样忽略标头行。

一些人喜欢将一些脚本放在个人目录下,还有人喜欢把脚本放在 PATH 变量指定的某个目录下。大多数人都喜欢同时做这两项。

下面是我建议你做的:

   

 $ mkdir -p "$HOME/bin"
    $ echo 'PATH="$HOME/bin:$PATH"' >> "$HOME/.bashrc"
    $ exec bash

第一个命令会在你的家目录下创建一个叫做 bin 的目录。将含有命令的目录命名为 bin 是一种习惯,即使有的命令是脚本而非预编译(“二进制”)文件。第二个命令会为你的 .bashrc 文件添加一行语句,将我们刚创建的目录加入 PATH 变量的最前端。每一个新的 BASH 实例都会检查 bin 目录下的可执行脚本。最后,第三行将当前的 BASH 实例替换为新的实例,新的实例会读取 .bashrc 文件。

隐藏文件(DotFile)的更改不会立即生效。你必须采取一些步骤来重新读入这些文件。在本例中,我们使用 exec bash 来替换当前运行的 shell。如果你愿意的话,你可以关闭当前终端并打开一个新的。BASH 会通过读取 .bashrc 再次初始化。或者,你可以干脆在命令行上执行那一行(

PATH="$HOME/bin:$PATH"

)或者在正在运行的 shell 里面通过运行

source "$HOME/.bashrc"

手动处理 .bashrc 文件。

在任何情况下,我们都可以将脚本放到 bin 目录下将之作为一个标准命令来执行了(我们不再需要在脚本的前面加上路径,即在前几例中是 ./ 的部分):

   

 $ mv myscript "$HOME/bin"
    $ myscript

 


参数

参数是内存中的一种命名空间,可用来检索与存储信息。一般来说,它可以存储字符串数据,也可用来存储整数或数组。
参数分为两类:变量和特殊参数

名称规则:一个只包含字母、数字和下划线,并以字母或下划线开头的词语。也称为标识符

赋值语句

name=value

请注意,= 号两边不可以有空格

,如果你这样写:

   

 # This is wrong!
    $ varname = vardata

BASH 就不会知道你是要赋值。分析器将只能看到 varname 而看不到 =,然后又将 = 和 vardata 传递给 varname 作为参数。

为读取变量中存储的数据,我们使用参数展开(parameter expansion)。参数展开即把参数代换为它的值。意即该句式告诉 BASH 你想要使用变量的内容。此后,BASH 仍可以对结果进行额外操作。正确理解这个概念非常重要。因为这与在其他编程语言中处理变量的方式是很不一样的!

让我们使用这个例子来说明何为参数展开:

   

$ foo=bar
    $ echo "Foo is $foo"

当 BASH 要执行这段代码时,它首先通过读入要展开的变量($foo),并替换为 foo 的内容(bar),这段命令即变为:

   

$ echo "Foo is bar"
    Foo is bar

现在,BASH 就已准备好执行命令了。执行它时就会在屏幕上显示这段简单的句子。

需要理解的是参数扩展是用参数的内容代替 $参数。下面的例子会用到之前提到的参数划分:

 

   $ song="My song.mp3"
    $ rm $song
    rm: My: No such file or directory
    rm: song.mp3: No such file or directory

这里为什么不对?因为 BASH 将 $song 替换为其内容,即 My song.mp3;然后它又进行词语划分,直到这时才执行命令。相当于你键入了下面的语句:

$ rm My song.mp3

根据词语划分的规则,BASH 以为你用 My 和 song.mp3 来表示不同的文件。因为它们之间有空白字符且未引用。如何修改呢?我们要记住把每一个参数展开都置于引号内。

$ rm "$song"

参数:参数用于储存可以使用符号或名称来检索的数据。


特殊参数和变量

让我们在进入正式内容之前理顺一下词汇。我们知道有参数和变量。变量实际上只是一种参数——由名称来表示的参数。不属于变量的参数叫做特殊变量。相信你看了下面一些例子后就会再明白了:

   

$ # Some parameters that aren't variables:
    $ echo "My shell is $0, and has these options set: $-"
    My shell is -bash, and has these options set: himB
    $ # Some parameters that ARE variables:
    $ echo "I am $LOGNAME, and I live at $HOME."
    I am lhunath, and I live at /home/lhunath.

请注意:并不像 PHP/Perl/…参数不以 $- 开头。你看到的 $- 符号只是会使其后的参数被展开。展开大体意为 shell 将参数替换为其内容。在这里,LOGNAME 是含有你的用户名的参数(变量)。$LOGNAME 是以变量内容代替变量的表达式,对于我的情况是lhunath。
我觉得你应该明白了。下面对绝大多数特殊参数的概括:

  • 0:包含脚本的名称或路径。并不总是可靠。

  • 1 2 等:位置参数(Positional Parameters)包含传递到当前脚本或函数的参数(argument)。

  • *:

  • @:

  • #:展开为当前设置的位置参数的数量。

  • ?:展开为最近一次在前台完成的命令的退出值。

  • $:展开为当前 shell 的 PID 。

  • !:展开为最近一次在后台运行的命令的 PID。

  • _:展开为最近一次运行的命令的最后一个参数。

下面是一些由 shell 自带的变量:


常用 bash 环境变量

BASH_VERSION:包含描述 BASH 的版本的字符串。 

  • HOSTNAME:包含计算机的主机名。根据计算机的设置,可短可长。

  • PPID:包含当前 shell 的父 shell 的 PID。

  • PWD:包含当前工作目录。

  • RANDOM:每一次展开该变量时,都会产生一个 0 到 32767 之间的随机数字。

  • UID:当前用户的 ID 号码。对于安全与认证用途不可靠,呃。

  • COLUMNS:终端中每一行的最长字符数值。(终端的字符宽度。)

  • LINES:终端的最大行数。(终端的字符高度。)

  • HOME:当前用户的家目录。

  • PATH:

  • PS1:包含描述 shell 提示符的字符串。

  • TMDIR:包含 shell 用于存储临时文件的目录。

当然,你并不受限于这些变量,可以定义你自己的变量:

   

 $ country=Canada
    $ echo "I am $LOGNAME and I currently live in $country."
    I am lhunath and I currently live in Canada.

注意我们是怎么给 country 这个变量指定 Canada 这个值的。记住等号两边不许有空格!

  

  $ language = PHP
    -bash: language: command not found
    $ language=PHP
    $ echo "I'm far too used to $language."
    I'm far too used to PHP.

记住 BASH 不是 Perl 或 PHP。要记住展开的原理,以避免重大的麻烦。如果做不到的话,你写出的脚本就会很危险,尤其是在用 rm 犯下这样的错误:

 

   $ ls
    no secret  secret
    $ file='no secret'
    $ rm $file
    rm: cannot remove `no': No such file or directory

假如我们有两个文件,no secret 和 secret。第一个文件里面没什么有用的东西,但第二个则包含了拯救濒于末日的世界的机密。你如此粗心,以至忘记引用 file 这个要展开的参数。BASH 展开了这个参数,其结果为 rm no secret。BASH 像平常一样根据空白字符划分参数,然后给了 rm 两个参数:“no”和“secret”。结果,它没有删除文件 no,却将文件 secret 给删除了。该机密不见了!

变量类型

虽然 BASH 不是类型语言,它的确有不同类型的变量。这些类型描述了它们允许含有的内容。

类型信息存储在 Bash 内部。

数组:delare -a 变量:该变量为一列字符串。

结合数组:delare -a 变量:该变量为字符串的结合阵列(bash 4.0 或更高)。

整数:declare -i 变量:该变量含有一个整数。为此变量赋值会触发算术求值(Arithmetic Evaluation)。

声明:delcare -x 变量:该变量被标记或声明,会被其子进程继承。

简单说来,数组就是一列索引的(indexed)字符串。它可以把多个字符串储存在一起而不用分界符,非常方便。

将变量定义为整数的方便之处在于要将其赋值或对其进行修改时省去一些句法。

    $ a=5; a+=2; echo $a; unset a
    52
    $ a=5; let a+=2; echo $a; unset a
    7
    $ declare -i a=5; a+=2; echo $a; unset a
    7
    $ a=5+2; echo $a; unset a
    5+2
    $ declare -i a=5+2; echo $a; unset a
    7

然而,declare -i 在实际中却很少用。主要是因为它建立的行为让每个忽略了 declare 语句的人看上去都很惊讶。大多数有经验的 shell 脚本作者在进行算术运算时更愿意使用明确的算术命令(let or ((…)))。
使用 declare -a 来声明数组的情况也很少见。直接写 array=(…)就足够了,Bash 就会知道该变量是数组。但结合数组是特例,我们必须明确地声明:declare -A myarray。

 

参数展开(Parameter Expansion)

参数展开(Parameter Expansion)这一术语是指任何使一参数被展开(被其内容取代)的操作。最简单的辨识方法是,参数展开是在参数前加一 $ 符号而达到的。在某些情况下,需要用花括号将参数的名称括起来:

    $ echo "'$USER', '$USERs', '${USER}s'"
    'lhunath', '', 'lhunaths'

这个例子说明了简单的参数展开(PE)是什么样的。第二个 PE 的结果是个空字符串,因为参数 USERs 为空。我们没有想让 s 成为参数名称的一部分。因为 Bash 无从知道有一个字面的 s 跟在参数的值后面,你需要用花括号来标记参数的开头与结尾。这是我们在上面例子中的第三个 PE 中做的。
参数展开为我们提供了修改要展开的字符串的功能。这些操作非常方便:

 

   $ for file in *.JPG *.jpeg
    > do mv "$file" "${file%.*}.jpg"
    > done

上面的代码可以用来将所有以 .JPG 或 jpeg 的 JPEG 文件重命名为以 .jpg 为常见的扩展名。
表达式 ${file%.*} 从最末一个句点(.)开始截取掉所有字符。然后又在展开结果后附上了一个新的扩展名。
下面是对所有可用的 PE 技巧的归纳:
${参数:-词语}:使用默认值。若“参数”未设置或为空,则代换为“词语”。否则,则代换为“参数”。
${参数:=词语}:指定默认值。若“参数”未设置或为空,则代换为“词语“。
${参数:+词语}:使用可选值。若“参数”未设置或为空,则无代换。否则代换为“词语”。
${参数:偏距:长度}:子字符串展开。
${#参数}:代换为“参数”值的字符长度。
${参数#样式}:使用“样式”对“参数”从左至右进行匹配。结果为“参数”的值删除最短匹配的部分。
${参数##样式}:同上,但删除最长匹配的部分。
${参数%样式}:使用“样式”对“参数”从右至左进行匹配。结果为“参数”的值删除最短匹配的部分。
${参数%%样式}:同上,但删除最长匹配的部分。
${参数/样式/字符串}:将“参数”的值中第一个匹配“样式”的部分替换为“字符串”。
${参数//样式/字符串}:同上,但替换所有匹配“样式”的部分。
你会随着经验的增长掌握他们。他们比你想像得要方便得多。下面是一些简单的例子帮助你入门:

   

 $ file="$HOME/.secrets/007"; \
    > echo "File location: $file"; \
    > echo "Filename: ${file##*/}"; \
    > echo "Directory of file: ${file%/*}"; \
    > echo "Non-secret file: ${file/secrets/not_secret}"; \
    > echo; \
    > echo "Other file location: ${other:-There is no other file}"; \
    > echo "Using file if there is no other file: ${other:=$file}"; \
    > echo "Other filename: ${other##*/}"; \
    > echo "Other file location length: ${#other}"
    File location: /home/lhunath/.secrets/007
    Filename: 007
    Directory of file: /home/lhunath/.secrets
    Non-secret file: /home/lhunath/.not_secret/007
 
    Other file location: There is no other file
    Using file if there is no other file: /home/lhunath/.secrets/007
    Other filename: 007
    Other file location length: 26

记住 ${v##p} 和 ${v##p} 的不同。使用了两个 # 字符意为该样式变得很贪婪。 % 与此同理:

   

 $ version=1.5.9; echo "MAJOR: ${version%%.*}, MINOR: ${version#*.}."
    MAJOR: 1, MINOR: 5.9.
    $ echo "Dash: ${version/./-}, Dashes: ${version//./-}."
    Dash: 1-5.9, Dashes: 1-5-9.

注意:你不可以同时使用多个 PE。如果你需要对于同一参数执行多个 PE,就需要使用多个语句:

   

$ file=$HOME/image.jpg; file=${file##*/}; echo "${file%.*}"
Image

 

参考资料

BashGuide

The Bourne-Again Shell—–Chet Ramey

Ibm developerworks

Deepin wiki

原创文章,作者:M21-郝建勋,如若转载,请注明出处:http://www.178linux.com/55697

联系我们

400-080-6560

在线咨询

工作时间:周一至周五,9:30-18:30,节假日同时也值班

QR code