- 1.bash展开命令行执行顺序
- 2.防止符号扩展
- 3.bash环境相关
- 4.bash的配置文件
- 5.注意点:在一个文件夹(prodile.d)内的脚本(PATH变量等)
- XX–按功能划分,存在两类:
- 6.Bash 退出任务
- 7.set 命令
- 8.set 命令脚本常用
1.bash展开命令行执行顺序
- 把命令行分成单个命令词(token)和运算符 2. 与此同时展开别名
- 展开简单命令以及大括号的命令列表({ command of list; })
- 按顺序展开七大expansion
- 花括号扩展(brace expansion)
- 波浪符扩展(Tilde Expansion)
- 变量扩展(Shell Parameter Expansion)
- 算术扩展(arithmetic expansion)
- 命令替换$() (command substitution)
- 单词分割(word splitting)
- 文件名扩展(filename expansion)(通配符匹配)
- 引用符号去除(
\,'和"三个符号)(Quote Removal) - 准备I/0重导向(
< >) - 运行命令
- 等待命令完成和收集命令返回值
The following is a brief description of the shell’s operation when it reads and executes a command. Basically, the shell does the following:
1. Reads its input from a file (see Shell Scripts), from a string supplied as an argument to the -c invocation option (see Invoking Bash), or from the user’s terminal.
2. Breaks the input into words and operators, obeying the quoting rules described in Quoting. These tokens are separated by metacharacters. Alias expansion is performed by this step (see Aliases).
3. Parses the tokens into simple and compound commands (see Shell Commands).
4. Performs the various shell expansions (see Shell Expansions), breaking the expanded tokens into lists of filenames (see Filename Expansion) and commands and arguments.
5. Performs any necessary redirections (see Redirections) and removes the redirection operators and their operands from the argument list.
6. Executes the command (see Executing Commands).
7. Optionally waits for the command to complete and collects its exit status (see Exit Status).
2.防止符号扩展
- 反斜线(\)会使随后的字符按原意解释
echo Your cost: \$5.00
Your cost: $5.00
- 加引号来防止扩展
单引号('' )防止所有扩展
双引号("")也可防止扩展,但是以下情况例外
$(美元符号) 变量扩展
` ` (反引号) 命令替换
\(反斜线) 禁止单个字符扩展
!(叹号) 历史命令替换
- 注意:在shell脚本中的history命令不会显示任何历史,因此无法使用。
3.bash环境相关
- 注意三个的区分,具体详情查看man帮助
- bash内置变量 (Shell Variables)
- bash启动时文件(Bash Startup Files)
- 命令执行环境(Command Execution Environment)
- 简单解释一下就是:
- bash在启动的时候会设置许多bash的内置变量,这些内置变量的默认值设置是bash内部机制,我们无法修改;注意这和我们修改启动文件修改配置并不是一个意思;
- bash在调用(invoke)的时候会按照调用时的不同类型和一些参数配置等(比如交互式非登录shell),选择执行哪些启动时的文件(比如/etc/profile,从上到下执行里面的命令);同时我们修改或添加bash启动文件,进行各种变量设置和环境设置等等,其实是在bash调用之后,已经设置了一些初始默认值,然后再根据这些文件进行再次修改的;也就是说可以看做是bash调用之后,然后读取这些文件对这些文件中的命令进行了执行而已。
- 这里注意在小括号,命令替换,管道等中命令执行的时候,这里虽然也开启了子shell,但是仅仅是为了给这些被执行的命令一个执行环境,这里的的子shell的所有环境变量等等参数都是从父shell拷贝一份过去的。这个过程并不是上面所说的开启bash的过程,也就不存在执行启动时文件这种操作了;
- 但执行脚本时是的的确确开启了子shell(非交互式非登录方式),此时就需要执行bash启动时文件变量(要设置为全局的才可以)$BASH_ENV文件,具体详细看转载的博客文章;
- bash内的命令的执行环境有许多项(比如当前目录,各种变量,umask值等等),根据命令执行方式的不同,命令执行过程中是否开启了子shell等等,来判断命令的执行环境经过继承和重置之后的具体情况等等,详细看bash帮助;
- shell内部命令和函数用最普通方式执行的时候(不用命令列表,加小括号,管道等等方式),其执行环境就是bash帮助中的命令的执行环境的所有包含项目,可以查看帮助来了解这些项的具体值和内容的来源和规则。
- 然后需要注意的就是这些命令的执行的时候是否开启了子shell在,如果开启了子shell,则此时需要判断的环境就是子shell开启时的各种环境,这里就从上面的1和2启动文件中的设置来进行判断了
- 在各种独立的shell中和子shell中执行的命令,它的命令环境虽然大多继承于和父shell(除了trap和自己的专门设置的以外);但是在这些独立的shell和子shell中的命令不会对父shell的环境有任何的影响;在这些shell中进行的命令和环境设置等,也只能作用于它本身
- 其次需要注意的就是simple command这种命令执行的时候继承的环境,下面有详细介绍;
- 举例,比如常用的crontab命令,它既不是bash内部命令,也不是函数,所以它就符合简单命令执行的命令环境过程(具体查看帮助)1.这个命令过程会打开一个seperate environment,此时它继承的命令执行环境的内容就十分有限,并不包含帮助中全部的命令执行环境的内容,只有下面的几个项目: ------------------------------------------ When a simple command other than a builtin or shell function is to be executed, it is invoked in a separate execution environment that consists of the following. Unless otherwise noted, the values are inherited from the shell. - the shell’s open files, plus any modifications and additions specified by redirections to the command - the current working directory - the file creation mode mask - shell variables and functions marked for export, along with variables exported for the command, passed in the environment (see Environment) - traps caught by the shell are reset to the values inherited from the shell’s parent, and traps ignored by the shell are ignored A command invoked in this separate environment cannot affect the shell’s execution environment. --------------------------------------------- 2.注意crontab命令,包括其他的简单命令simple command,并没有开启非交互式的非登录的子shell(判断交互式非交互式可以用%-和PS1这两个变量来判断,这里只是补充说明一下怎么判断);
- 有一点一定要分清的就是,命令的执行环境和开启不开启子shell是两件事
- 先判断命令执行是否开启了子shell(开启bash方式,比如脚本),然后看命令是否是在小括号,管道,命令替换中执行的(看上面写的区别),继续再判断命令属于什么类型(内部命令还是简单命令),由此判断命令的执行环境;
- 比如一个内部命令,直接当前shell命令行命令行执行,那么这个命令的执行环境就是当前shell中的各种环境(看帮助中介绍的)
- 比如一个内部命令,在脚本中执行(开启了子shell),那么就先判断这个子shell继承了父shell的哪些环境,比如环境变量,当前目录等等;然后看脚本开启(子shell)的时候执行了哪些启动文件,在这里就是只有$BASH_ENV文会执行,然后这些所有的环境就是这个内部命令在子shell中的环境
- 比如一个内部命令,在小括号中执行,则虽然也是子shell,但是没有任何启动文件,只是父shell的所有环境被拷贝到了子shell中而已;这样做看起来像是在父shell中执行命令,但命令对于父shell的环境不会有任何影响
- 再比如一个简单命令,直接在当前环境命令行中执行,那么就是上面crontab命令相同,开启一个独立的环境(并没有开启shell),然后继承有限的命令执行环境项目。
- 剩下的两种和上面对应的情况,前面的过程是相同的,就是最后一步都是再开启一个独立的命令执行环境,同时只是继承了部分执行环境的项目,命令修改也不会对原环境造成影响
4.bash的配置文件
- 下面具体的可以参考转载的博客,这里写的可能有错误 http://feihu.me/blog/2014/env-problem-when-ssh-executing-command-on-remote/
- 从这里一直到set命令部分不用看,中间的注意点可以看看,然后
~/.bash_logout文件相关(注意它是bash退出不是系统关机)可以看看,其他的直接参考博客内容;
XX–按生效范围划分,存在两类:
- 全局配置
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
- 个人配置:
~/.bash_profile
~/.bashrc
XX–目前所写已知的变量及位置:
- /etc/profile
umask(原生自带)
HISTSIZE(原生自带)
- /etc/profile.d/env.sh(自己增加易删除):
PS1
HISTtimeFORMAT
HISTCONTROL
PATH
HISTFILESIZE
- /.bashrc :
alias
- 根目录下加了隐藏文件.vimrc
XX–交互式登录:
(1)直接通过终端输入账号密码登录 (2)使用“su – UserName” 切换的用户 执行顺序:/etc/profile –> /etc/profile.d/*.sh –> ~/.bash_profile ~/.bashrc –> /etc/bashrc
- 记忆:先profile后bashrc, 然后,Profile先全局后个人,bashrc先个人后全局
- 注意:~/.bash_profile 中调用了~/.bashrc,会显得.bashrc先执行,其实并非如此。
- 同样.bashrc也会调用 /etc/bashrc
XX–非交互式登录:
(1)su UserName (2)图形界面下打开的终端(这个错误,这个是交互式不登录) (3)执行脚本 (4)任何其它的bash实例 执行顺序: /etc/profile.d/*.sh –> /etc/bashrc –>~/.bashrc
XX–为了不混乱,所有变量最好只在一个文件里出现一次
5.注意点:在一个文件夹(prodile.d)内的脚本(PATH变量等)
- 如果在同一个文件夹内的配置文件,经过测试其执行顺序就是按照ls的默认排列顺序执行的。因此,在这一个文件夹中的*.sh文件中的变量,如果两个文件中有相同的变量,则后执行的会覆盖掉前执行的(比如PS1等)。
- 但是如果一个变量仍然保留它之前的旧的内容,只是在此基础上增加内容,比如PATH变量,则可以用 PATH=/data/app:$PATH 的写法写入各个不同的*.sh配置文件(比如把各个软件的路径分别用不同的.sh文件名字添加进去)中。
- 此时的逻辑是先执行了系统的/etc/profile文件,把里面定义的PATH变量读入内存并定义为环境变量(更详细看其他博客),然后再执行profile.d中的文件,将自己写的PATH变量的要添加内容添加PATH中。
- 特别注意点1:如果用这种写法,则所有的自己所写的*.sh文件中都要用这种写法,如果有一个用了PATH=直接赋值的方式来写,它会把前面执行的profile中的以及profile.d中已经执行过的.sh文件中的PATH变量都给覆盖掉。但是如果PATH=XXX:$PATH这种写法不管什么.sh的执行顺序都能添加进去,且不会覆盖掉之前的PATH变量
- 特别注意点2: 注意.sh文件中的格式必须是 PATH=/data/app:PATH’ > XXX.sh** 写法时,一定要在echo 后面要输入的 PATH内容上单引号括起来,不然会把变量解析后再写入文件中。这样的话就造成了特别注意点1中所写的直接赋值的情况发生了。
Last login: Mon Apr 29 09:30:21 2019 from 192.168.36.1
Why do you login me? I 'm sleeping, Get off!!!
Environment setting finised(/etc/profile.d/een.sh)~
Environment setting finised(/etc/profile.d/evn.sh)~
09:38[root@centos7 ~]# vim een.sh
09:38[root@centos7 ~]# cd /etc/profile.d
09:38[root@centos7 /etc/profile.d]# mv een.sh e
een.sh evn.sh
09:38[root@centos7 /etc/profile.d]# mv een.sh ewn.sh
09:39[root@centos7 /etc/profile.d]# exit
logout
修改een.sh为ewn.sh (ls顺序比evn.sh靠后)
退出ssh连接 再次打开shell:
Last login: Mon Apr 29 09:38:27 2019 from 192.168.36.1
Why do you login me? I 'm sleeping, Get off!!!
Environment setting finised(/etc/profile.d/evn.sh)~
Environment setting finised(/etc/profile.d/een.sh)~
09:39[root@centos7 ~]#
XX–按功能划分,存在两类:
profile类和bashrc类
XXProfile类
- profile类:为交互式登录的shell提供配置 全局:/etc/profile, /etc/profile.d/*.sh 个人:~/.bash_profile 功用: (1) 用于定义环境变量 (2) 运行命令或脚本
XX–Bashrc类
bashrc类:为非交互式和交互式登录的shell提供配置 全局:/etc/bashrc 个人:~/.bashrc 功用: (1) 定义命令别名和函数 (2) 定义本地变量
XX–修改profile和bashrc文件后需生效
两种方法: 1重新启动shell进程 2 . 或source 例: . ~/.bashrc
- 注意:这里不能用bash 加文件的方式来执行它,因为bash相当于开了一个子进程来解释它,这样的话变量都不会继承出来,所以得用source命令
6.Bash 退出任务
保存在~/.bash_logout文件中(用户)
在退出登录shell时运行
用于
- 创建自动备份
- 清除临时文件
7.set 命令
$- 变量
- 注意$_代表上一条命令最后一个参数,别混淆
- h:hashall,打开这个选项后,Shell 会将命令所在的路径hash下来,避免每都要查询。通过set +h将h选项关闭,set -h打开
- i:interactive-comments,包含这个选项说明当前的shell是一个交互式的shell。所谓的交互式shell,在脚本中,i选项是关闭的,因为脚本是批量执行的。
- m:monitor,打开监控模式,就可以通过Job control来控制进程的停止、继续,后台或者前台执行等。
- B:brace expand,大括号扩展,可将大括号内的序列展开
- H:history,H选项打开,可以展开历史列表中的命令,可以通过!感叹号来完成,例如“!!”返回上最近的一个历史命令,“!n” 返回第 n 个历史命令。如果关闭,虽然能看到历史,但是不能用!引用了
8.set 命令脚本常用
-u 在扩展一个没有设置的变量时,显示错误信息,等同set –o nounset
注意这个只是显示错误信息,并不会退出,结合后面的set -e才会退出
-e 如果一个命令返回一个非0退出状态值(失败)就退出,等同set –o errexit
因为脚本错误命令虽然错误,但仍然会执行后面的命令,用这个
用这个的时候注意要让整体命令输出正确,不然有些情况想要继续执行脚本但有错误结果的就无法继续执行脚本了,可以用||或命令等使整体正确。
- 更多命令查看帮助 help set
留言