shell

本文大部分内容为MIT的《The Missing Semester of Your CS Education》课程的lecture 1和lecture 2的翻译整理

shell是什么?

计算机接收命令的文本接口,可以通过它运行程序,给程序输入,检查程序输出。

怎么用Shell?

下面主要介绍bash(Bourne Again SHell)

shell命令

Shell在环境变量$PATH指定的目录下搜索待运行的程序($PATH里的目录通过:号隔开,可以通过which查看程序的文件,如which echo得到/bin/echo),也可以直接指定待运行的程序的路径。

>输出重定向 <输入重定向 >> 追加到文件

sudo echo 3 > 系统文件

会报错

An error occurred while redirecting file ''
open: Permission denied

原因:><|操作是由shell完成的,而不是前面的echo程序,echo并不知道这些操作的存在。shell(用户权限)在将文件设置为sduo echo的输出前,会尝试打开该文件。因为shell不是作为root运行的,所以会报错。

正确的写法

echo 3 | sudo tee 系统文件

shell脚本

source、sh、bash、./都可以执行shell脚本,区别是?

source a.sh 或者. a.sh在当前shell内读取、执行脚本,而该脚本不需要有“执行权限”

sh a.sh或者bash a.sh 都是打开一个subshell去读取、执行脚本,同样该脚本不需要“执行权限”。通常在subshell运行的脚本里设置变量,不会影响到父shell。

./a.sh在当前shell执行脚本,即将a.sh当成一个文件执行,此时需要文件的运行权限。

语法

给变量赋值foo=bar,之后用$foo获取这个变量的值。而foo = bar会被解释为foo程序,参数为=和bar。一般,空格都会被解释为参数分隔符。

函数

mcd () {
    mkdir -p "$1"
    cd "$1"
}
  • $0 脚本名称
  • $1$9 第一到第九个参数
  • $@ 所有参数
  • $# 参数个数
  • $? 上一个命令的返回码
  • $$ 当前脚本的进程号
  • !! 整个最后一个命令,包括参数。一种常见的用法是仅执行命令以使其由于缺少权限而失败,然后通过sudo !! 快速执行它。也可以直接在交互式shell里这么用。
  • $_ 上一条命令的参数。在交互式shell里可以先按Esc再按 . 同样得到上一条命令的参数。

同一行两个命令用;隔开

命令替换:$( CMD ) 执行CMD,得到该命令的输出,并替换$()。例如for file in $(ls)

进程替换<( CMD )执行CMD,将输出保存在一个临时文件中,然后用该文件的名字替换<()。当命令期望通过文件而不是STDIN传递值时很有用。例如diff <(ls foo) <(ls bar)将比较foo和bar两个文件夹的里的文件的差异。

举个例子

#!/bin/bash #这行称作Shebang(或Hashbang)
echo "Starting program at $(date)"
echo "Running program $0 with $# arguments with pid $$"
for file in $@; do
    grep foobar $file > /dev/null 2> /dev/null
    # 当文件中不包含foobar,grep的退出状态为1
    # 将STDOUT和STDERR重定向到null,因为这里并不关心这两。其中2> 代表重定向STDERR,2和>中间不能有空格,2是文件描述符。
    if [[ $? -ne 0 ]]; then 
    # -ne 不等于,类似的还有 -eq 等于; -ge 大于等于; -gt 大于; -le 小于等于; -lt 小于
    # 直接用>或者<,则比较的是字符串
        echo "File $file does not have any foobar, adding one"
        echo "# foobar" >> "$file"
    fi
done

if条件,检查文件类型和比较值的语法可以见test

[ 和 [[ 的区别

​ [ 是命令,和test等价,即test exper[ expr ]等价

​ [[ 是关键字,功能更强大,定义了特殊的原语 [[ 有,但 [ 可能缺乏(取决于实现),例如-ef两个文件是否相等, 更多见http://mywiki.wooledge.org/BashFAQ/031。目前只有bash,Zsh和Korn支持[[]]

通配符

?匹配一个字符

*匹配任意个字符

{}只要在一系列命令中有一个公共子字符串,就可以用{}进行扩展。如:

convert image.{png, jpg}等价于convert image.png image.jpg

​ `cp /path/to/project/{foo, bar, baz}.sh /newpath``

``mv *{.py, .sh} folder`

touch {foo, bar}/{a..h}这个会创建 foo/a, foo/b, ... , foo/h等文件

常用命令

  • pwd 当前路径

  • man 显示一个程序的手册页,按q退出。如man ls

  • tee从输入中读取数据到输出中。如tee file1 file2将输入的数据同时保存在两个文件中。-a 追加。

  • cat用于连接文件并打印到标准输出设备上。

    例如cat -n file1 > file2把file1的内容加上行号后输入到file2中

    cat /dev/null > test.txt清空test.txt

  • touch用于修改文件或目录的时间属性,包括存取时间和更改时间。若文件不存在,系统会建立一个新的文件。

  • chmod[ugoa...][[+-=][rwxX]...][,....]

    u表示文件拥有者,g表示与该文件的拥有者同属一个group的用户,o表示其他,a表示这三者都是

    +表示增加权限,-表示取消权限,=表示唯一设置权限

    X表示只有当该文件是个子目录或者该文件已经被设定过可执行

  • stat用于显示inode内容

  • awk是一种处理文本文件的语言,是一个强大的文本分析工具。

    例如获取文件的最后修改时间 ll file | awk '{print $6 "-" $7 "-" $8}'

  • grep [-r][范本样式][文件或目录...] 用于查找文件里符合条件的字符串

    若不指定任何文件名称,或是文件名为- ,则从标准输入读取数据。

    -r 以递归的方式查找符合条件的文本

    例如grep test *file在当前目录中的后缀为file的文件中查找包含test字符串的文件,并打印出该字符串的行。

Tips

  • 多个单词作为一个参数,除了用单引号或者双引号,如”Hello World“,也可以用\, 如 Hello\ world
  • 单引号和双引号的区别 单引号将剥夺其中的所有字符的特殊含义,而双引号中的$(参数替换)和'(命令替换)是例外
  • 要进入一个目录,用户必须对该目录及其父目录具有搜索权限(execute,x)
  • 基本上/bin下的所有文件对于其他用户都是可执行的
  • sysfs 挂载在/sys 下,将许多内核参数以文件的形式暴露出来。例如:屏幕亮度参数在/sys/class/backlight/brightness,通过向该文件写入一个数字,可以改变屏幕亮度
  • 工具如https://github.com/koalaman/shellcheck可以帮助检测shell脚本的错误

你可能感兴趣的:(shell)