继上篇 shell 入门文章, 该篇文章会列举出我在学习 shell 的过程中, 遇到的一些疑问, 然后自己查资料以后的解答.
如何运行 shell 脚本
回顾例子:
[root@host shell]# vim start
[root@host shell]# cat start
#!/bin/bash
echo 'hello'
[root@host shell]# chmod 755 start
[root@host shell]# ./start
hello
[root@host shell]#
执行时, 为什么要加 ./
?
这要从系统的 $PATH
变量说起, 当我们执行例如 ls
, pwd
等指令时, linux 并不会在整个系统翻箱倒柜搜索 ls
, pwd
程序, 而是会从 $PATH
变量定义的目录列表下查找, 如果找不到, 就会报 command not found
的错误.
因为我们的脚本不在 $PATH
变量目录列表下, 所以需要加上 ./
指示该应用程序处于当前目录下.
另外, 如果我们不想加 ./
, 可以将该目录作为系统搜寻指令的其中一个目录:
[root@host shell]# vim ~/.bash_profile
# 修改 .bash_profile, 添加你的脚本目录.
# $PATH 变量定义中, 多个目录用冒号 : 分隔
export PATH="$PATH":~/shell
# 让 shell 重新读取配置文件
[root@host shell]# source ~/.bash_profile
[root@host shell]# start
hello
第一行 #!/bin/bash
有什么作用 ?
告诉操作系统用什么解释器执行该脚本
为什么要给文件加权限 chmod 755
?
linux 系统默认不允许一个文本文件作为程序被执行, 所以要给文件添加可执行的权限
shell 和 bash 有什么关系 ?
shell 是一类应用程序, 它接收键盘输入的命令, 然后将命令传递给操作系统去执行, 是用户与操作系统的沟通桥梁.
bash 是使用广泛的一款 shell 程序, Linux 系统的标配, 也可以使用其他 shell 程序, 比如 sh.
~/.bashrc
, ~/.bash_profile
文件有什么区别 ?
这两个文件都是 shell 脚本文件, 会在不同的时机被执行.~/.bash_profile
会在 linux 用户登录的时候被执行.~/.bashrc
在非登录时就会被执行.
另外, 一般在 ~/.bash_profile
脚本内, 会将 ~/.bashrc
内容进行执行, 比如某些服务器的 ~/.bash_profile
文件内, 会有代码:
# Get the aliases and functions
if [-f ~/.bashrc]; then
. ~/.bashrc
fi
shell 命令内自带程序名字的来源有哪些 ?
- 在 shell 的配置文件中, 比如
~/.bashrc
, 内定义的$PATH
目录列表中可找到的程序 - 在 shell 配置文件内定义的 shell 函数
数据类型
回顾数组
# 初始化
a[2]=100
a=(2 4 'str' 4)
a=([0]=2 [2]='str' [4]=20)
# 单个赋值
a[2]='s'
# 打印整个数组
echo ${a[@]}
# 遍历数组
for i in ${a[@]}; do
echo $i
done
# 数组元素个数
echo ${#a[@]}
# 指定下标添加元素
a=([0]=2 [2]='str')
a[3]=1
a[6]=6
# 删除整个数组
unset a
# 删除数组内单个元素
unset a[1]
数组内可以放置字符串和整数的混搭吗 ?
可以, 比如
a=([1]=sun [6]=$((2+3)) )
对数组进行跨越 (不连续) 赋值, 遍历数组时, 会遍历未赋值元素吗 ?
不会
a=([1]=sun [6]=$((2+3)) )
for i in ${a[*]}; do
echo 0
echo $i
done
# 0
# sun
# 0
# 5
'
和 "
区别 ?
[root@host shell]# echo "$foo"
start
[root@host shell]# echo '$foo'
$foo
双引号内, 会进行参数展开.
单引号内, 不进行参数展开.
什么是参数展开 ?
即 将变量名替换为具体的值, 比如
[root@host shell]# a="-l"
[root@host shell]# ls $a
total 4
-rwxr-xr-x 1 root root 26 Oct 19 07:29 start
ls $a
参数展开以后会是 ls -l
变量, 表达式, 控制流
为什么要给变量加花括号 {}
?
a=1
echo ${a} # 1
echo $a # 1
以上, a
变量加不加花括号都没关系, 但是一些情况下, 需要给变量加上花括号, 为了更清晰的指出我们操作的是什么变量
例如我想修改文件名, 给文件名末尾加一个 “1”
[root@host shell]# foo=start
[root@host shell]# mv $foo $foo1
mv: missing destination file operand after‘start’Try 'mv --help' for more information.
[root@host shell]# mv $foo ${foo}1
[root@host shell]# ll
total 4
-rwxr-xr-x 1 root root 26 Oct 19 07:29 start1
有哪些 $
开头的 shell 自带变量 (待完善) ?
$? # 上一次的退出状态
$REPLY # 当使用 read 命令, 且 read 没带变量名, 则 $REPLY 包含所有输入
$(())
与 (())
区别 ?
复合命令 (())
, 用做算术运算, 用在条件判断中, 比如 if
的条件判断.
而 $(())
用作算术展开, 用作整数赋值, 比如 foo=$((1))
()
与 (())
区别 ?
()
用于创建 子 shell, 应该与 {}
一起讲(())
用于算术表达式求值, 可用在条件判断中
()
与 {}
区别 ?
二者语法为
(list) # 子 shell
# eg
(ls -l; echo 'hello') > output.txt
{list;} # 组合命令
# eg
{ls -l; echo 'hello';} > output2.txt
二者都可以内置一个命令列表, 运行以后会执行该命令列表.
- 语法上的不同:
在组合命令中, {
与 list 之间需要一个空格, 而 {list;}
的 list 最后需要带上分号 ;
, 而 (list)
没有这些限制
- 功能上的不同:
组合命令会在当前环境执行该命令列表, 产生的对环境变量的变动会保留在当前执行环境.
而子 shell 则会新建一个执行环境, 在新的执行环境内执行命令列表, 所以当子 shell 内命令列表执行完以后, 子 shell 会被销毁, 子 shell 内的环境变量也会相应的被销毁.
[]
与 [[]]
区别 ?
[]
用于控制流中的条件判断.[[]]
拥有 []
的能力, 且增加了正则表达式的能力.
$(())
与 (())
结合使用 ?
a=0
if (($(( $a + 1)) == 1 )); then
echo 'hello'
fi
解析:
- if 跟着的
(())
是复合命令, 用作条件判断 - 内部的
$(($a + 1))
是算术展开, 用作算术求值 -
$a + 1
是一个表达式
其他
如何在 vim 编辑器里运行 linux 命令 ?
Esc
:!linux_command
注意: 如果要运行正在编辑的 shell 脚本, 记得先进行保存: :w
参考
http://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Parameter-Expansion
http://billie66.github.io/TLCL/book/index.html
https://www.gnu.org/software/bash/manual/html_node/Command-Grouping.html#Command-Grouping
(如有错误或不同的见解, 望不吝指出, 愿共同进步!)