shell clarify - msforest/notebook GitHub Wiki

shell 知识误区

单引号、双引号、反引号

  • 反引号=== $() 执行表达式,反引号是一个老的用法,$()是新的用法
  • 单引号:内容全被当做普通字符,忽略所有特殊字符转义含义
  • 双引号:忽略大多数特殊字符,不包括$ \ ``

单引号和双引号用于变量值出现空格时使用

小括号、大括号

支持多条命令写在一起,命令之间用";"隔开

  • () 执行一串命令时,需要重新开启一个子 Shell 来执行。
  • {} 执行一串命令时,在当前 Shell 中执行。
  • () 最后一条命令可以不用分号。
  • {} 最后一条命令要用分号。
  • () 里的各命令不必和括号有空格。
  • {} 的第一条命令和左括号之间必须有一个空格。
  • () 和 {} 中括号里面的某条命令的重定向只影响该命令,但括号外的重定向则会影响到括号里的所有命令。
    • { var1=test1;var2=test2;echo $var1>a;echo $var2;} # 输出 test1 被重定向到文件 a 中,而 test2 输出则仍输出到标准输出中。
    • { var1=test1;var2=test2;echo $var1;echo $var2;}>a # 括号内命令的标准输出全部被重定向到文件 a 中

``、$()、${}、$、$(())

  • 反引号=== $() 执行表达式,反引号是一个老的用法,$()是新的用法,新语法较直观
  • ${var} === $var 变量引用,两个用法的效果相同,${}更能区分变量边界,还能做一些字符串运算;如 slice/replace/length
  • $(()) 整数运算

()、(())、[]、、{}

更多区别

  • 双括号 —— 针对数字比较: 支持高级数学表达式;如幂运算(**)
  • 双方括号 —— 针对字符串比较: 支持字符串的高级比较表达式;如 [ a > b ] 和 a > b ,前者会被解析为重定向符号,若要解析正确,需加转移符'';后者可以正确解析大于号

let、expr、(())

TODO

.、source、./、bash、sh

. filename.sh
source filename.sh #等同于上面的点,运行在当前的shell进程中,不会衍生新的子进程,脚本运行结束后,变量依然可用

./filename.sh # 创建新的子进程,脚本里的变量在父进程中不可用;需要可执行权限
bash filename.sh # 创建新的子进程
sh filename.sh # 创建新的子进程

return、exit

  • return [n]:可以在函数体内的任何地方使用,表示退出整个函数;数值 n 表示函数的退出状态码
    • 如果returnfunction之外,使用[.|source] filename此命令执行,则直接停止该执行操作,并返回给定状态码 n(如果未给定,则为 0)
    • 如果returnfunction之外,使用./filename此命令执行,将是一个错误用法
  • exit [n]:可以在脚本的任何地方使用,表示退出整个脚本(即退出当前进程);数值 n 表示脚本的退出状态码

fork、exec、source(.)

脚本里执行另一个脚本,变量的使用

  • fork(./shell.sh): 直接运行一个 shell,衍生一个子进程,子进程可以使用父进程的变量
  • exec(exec shell.sh): 不会衍生子进程。使用 exec 调用一个新脚本以后, 父脚本中 exec 行之后的内容就不会再执行了。相当于父脚本被子脚本代替了,这是 exec 和 source 的区别。
  • source(. shell.sh): 不衍生子进程,父子脚本互相共享变量
## 1.sh
#!/bin/bash
A=B
echo "PID for 1.sh before exec/source/fork:$$"
export A
echo "1.sh: $A is $A"
case in
    exec)
        echo "using exec…"
        exec ./2.sh ;;
    source)
        echo "using source…"
        . ./2.sh ;;
    *)
        echo "using fork by default…"
        ./2.sh ;;
esac
echo "PID for 1.sh after exec/source/fork:$$"
echo "1.sh: $A is $A"


## 2.sh
#!/bin/bash
echo "PID for 2.sh: $$"
echo "2.sh get $A=$A from 1.sh"
A=C
export A
echo "2.sh: $A is $A"

## 执行结果
'
$ ./1.sh
PID for 1.sh before exec/source/fork:5845364
1.sh: $A is B
using fork by default…
PID for 2.sh: 5242940
2.sh get $A=B from 1.sh
2.sh: $A is C
PID for 1.sh after exec/source/fork:5845364
1.sh: $A is B

$ ./1.sh exec
PID for 1.sh before exec/source/fork:5562668
1.sh: $A is B
using exec…
PID for 2.sh: 5562668
2.sh get $A=B from 1.sh
2.sh: $A is C

$ ./1.sh source
PID for 1.sh before exec/source/fork:5156894
1.sh: $A is B
using source…
PID for 2.sh: 5156894
2.sh get $A=B from 1.sh
2.sh: $A is C
PID for 1.sh after exec/source/fork:5156894
1.sh: $A is C
'

halt、poweroff、reboot、shutdown

  • shutdown: 正常关机命令。
  • halt: 通知硬件来停止所有的 CPU 功能,但是仍然保持通电。你可以用它使系统处于低层维护状态。
  • poweroff: 发送一个 ACPI 信号来通知系统关机。
  • reboot: 通知系统重启。

mtime、ctime、atime

  • mtime(modification time): 当该文件的“内容数据”变更时,就会更新这个时间。内容数据指文件内容,而不是文件的属性和权限喔
  • ctime(status time): 当该文件的状态改变时,就会更新这个时间。比如属性和权限被更改了,都会更新这个时间喔
  • atime(access time): 当“该文件的内容被取用”时,就会更新这个时间

符号链接、硬链接

ls -li 查看文件更多信息

  • 硬链接可以随意删除,符号链接不能
  • 硬链接不支持目录,只支持文件;符号链接没有要求
  • 硬链接指向同一个 inode 数据块;符号链接内容保存的是目标的绝对路径

env、set、export

  • set: 设置当前进程的本地变量,只在当前shell进程有效,子进程无法使用
  • env: 仅为要执行的子进程设置环境变量
  • export: 将一个 shell 本地变量提升为当前 shell 进程的环境变量,从而被子进程自动继承,但是无法改变父进程的环境变量