乐趣区

shell-脚本学习答疑

继上篇 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 命令内自带程序名字的来源有哪些 ?

  1. 在 shell 的配置文件中, 比如 ~/.bashrc, 内定义的 $PATH 目录列表中可找到的程序
  2. 在 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

(如有错误或不同的见解, 望不吝指出, 愿共同进步!)

退出移动版