missing semester - Shell Tools and Scripting

Shell Scripting

变量、赋值、字符串

在Bash中为变量赋值的语法是foo=bar,访问变量中存储的数值,其语法为 $foo

需要注意的是,foo = bar (使用空格隔开)是不能正确工作的,因为解释器会调用程序foo 并将 =bar作为参数。(在shell脚本中使用空格会起到分割参数的作用)

Bash中的字符串通过'"分隔符来定义,但是它们的含义并不相同。

  • '定义的字符串为原义字符串,其中的变量不会被转义。

  • "定义的字符串会将变量值进行替换。

foo=bar
echo "$foo"
# 打印 bar
echo '$foo'
# 打印 $foo

函数

bash 支持函数,它可以接受参数并基于参数进行操作。

下面这个函数是一个例子,它会创建一个文件夹并使用cd进入该文件夹。

mcd () {
    mkdir -p "$1"
    cd "$1"
}

Bash中使用特殊变量来表示参数、错误代码和相关变量:

  • $0 - 脚本名
  • $1$9 - 脚本的参数。 $1 是第一个参数,依此类推。
  • $@ - 所有参数
  • $# - 参数个数
  • $? - 前一个命令的返回值
  • $$ - 当前脚本的进程识别码
  • !! - 完整的上一条命令,包括参数。常见应用:当你因为权限不足执行命令失败时,可以使用 sudo !!再尝试一次。
  • $_ - 上一条命令的最后一个参数。如果你正在使用的是交互式shell,你可以通过按下 Esc 之后键入 . 来获取这个值。

命令通常使用 STDOUT来返回输出值,使用STDERR 来返回错误及错误码,便于脚本以更加友好的方式报告错误。

返回码或退出状态是脚本/命令之间交流执行状态的方式。返回值0表示正常执行,其他所有非0的返回值都表示有错误发生。

布尔运算

退出码可以搭配&& (与操作符) 和 || (或操作符)使用,用来进行条件判断,决定是否执行其他程序。它们都属于短路运算符(short-circuiting) 。

同一行的多个命令可以用;分隔。(命令之间没有关系)

程序 true 的返回码永远是0false 的返回码永远是1。(true肯定成功执行、false肯定执行失败)

false || echo "Oops, fail"
# Oops, fail

true || echo "Will not be printed"
#

true && echo "Things went well"
# Things went well

false && echo "Will not be printed"
#

false ; echo "This will always run"
# This will always run

命令替换 与 进程替换(process substitution

  • $( CMD ) 命令替换 (command substitution):通过 $( CMD ) 这样的方式来执行CMD 这个命令时,它的输出结果会替换掉 $( CMD ) 。例如,如果执行 for file in $(ls) ,shell首先将调用ls ,然后遍历得到的这些返回值。

  • <( CMD ) 进程替换(process substitution) :<( CMD ) 会执行 CMD 并将结果输出到一个临时文件中,并将 <( CMD ) 替换成临时文件名。这在我们希望返回值通过文件而不是STDIN传递时很有用。例如: diff <(ls foo) <(ls bar) 会显示文件夹 foobar 中文件的区别。

$( CMD )替换为输出结果;而<( CMD ) 是替换以一个临时文件名。区别的例子:

# $( CMD )
stat $(which vim)
# 16777232 1152921500312785999 -rwxr-xr-x 1 root wheel 0 4548272 "Jan  1 16:00:00 2020" "Jan  1 16:00:00 2020" "Jan  1 16:00:00 2020" "Jan  1 16:00:00 2020" 4096 5360 0x80020 /usr/bin/vim
# <( CMD )
cat <(echo hi)
# hi
echo <(echo hi)
# /dev/fd/13

例子

#!/bin/bash

echo "Starting program at $(date)" # date会被替换成日期和时间

echo "Running program $0 with $# arguments with pid $$"

for file in "$@"; do
    grep foobar "$file" > /dev/null 2> /dev/null
    # 如果模式没有找到,则grep退出状态为 1
    # 我们将标准输出流和标准错误流重定向到Null,因为我们并不关心这些信息
    if [[ $? -ne 0 ]]; then
        echo "File $file does not have any foobar, adding one"
        echo "# foobar" >> "$file"
    fi
done

Bash的比较

在条件语句中,我们比较 $? 是否等于0。 Bash实现了许多类似的比较操作,您可以查看 test 手册。 在bash中进行比较时,尽量使用双方括号 [[ ]] 而不是单方括号 [ ],这样会降低犯错的几率,尽管这样并不能兼容 sh

通配符

当执行脚本时,经常需要提供形式类似的参数。bash使我们可以轻松的实现这一操作,它可以基于文件扩展名展开表达式。这一技术被称为shell的 通配globbing

  • 通配符 - 当你想要利用通配符进行匹配时,你可以分别使用 ?* 来匹配一个或任意个字符。例如,对于文件foo, foo1, foo2, foo10bar, rm foo?这条命令会删除foo1foo2 ,而rm foo* 则会删除除了bar之外的所有文件。
  • 花括号{} - 当你有一系列的指令,其中包含一段公共子串时,可以用花括号来自动展开这些命令。这在批量移动或转换文件时非常方便。
convert image.{png,jpg}
# 会展开为
convert image.png image.jpg

cp /path/to/project/{foo,bar,baz}.sh /newpath
# 会展开为
cp /path/to/project/foo.sh /path/to/project/bar.sh /path/to/project/baz.sh /newpath

# 也可以结合通配使用
mv *{.py,.sh} folder
# 会移动所有 *.py 和 *.sh 文件

mkdir foo bar

# 下面命令会创建foo/a, foo/b, ... foo/h, bar/a, bar/b, ... bar/h这些文件
touch {foo,bar}/{a..h}
touch foo/x bar/y
# 比较文件夹 foo 和 bar 中包含文件的不同
diff <(ls foo) <(ls bar)
# 输出
# < x
# ---
# > y

Shell Tools

查看命令如何使用

  1. 为对应的命令行添加-h--help 标记。
  2. 使用man 命令。man 命令是手册(manual)的缩写,它提供了命令的用户手册。
  3. tldr。

查找文件

所有的类UNIX系统都包含一个名为find工具。find命令会递归地搜索符合条件的文件,例如:

# 查找所有名称为src的文件夹
find . -name src -type d
# 查找所有文件夹路径中包含test的python文件
find . -path '*/test/*.py' -type f
# 查找前一天修改的所有文件
find . -mtime -1
# 查找所有大小在500k至10M的tar.gz文件
find . -size +500k -size -10M -name '*.tar.gz'

除了列出所寻找的文件之外,find还能对所有查找到的文件进行操作。

# 删除全部扩展名为.tmp 的文件
find . -name '*.tmp' -exec rm {} \;
# 查找全部的 PNG 文件并将其转换为 JPG
find . -name '*.png' -exec convert {} {}.jpg \;

更高效的工具:

  1. fd 就是一个更简单、更快速、更友好的程序,它可以用来作为find的替代品。它有很多不错的默认设置,例如输出着色、默认支持正则匹配、支持unicode并且它的语法更符合直觉。以模式PATTERN 搜索的语法是 fd PATTERN

  2. locate : 通过编译索引或建立数据库的方式来实现更加快速地搜索。locate 使用一个由 updatedb负责更新的数据库,在大多数系统中 updatedb 都会通过 cron每日更新。这便需要我们在速度和时效性之间作出权衡。locate只能通过文件名检索。

查找代码

查看文件的内容使用grep命令,它是用于对输入文本进行匹配的通用工具。

经常使用的选项有 :

-C :获取查找结果的上下文(Context);

-v: 将对结果进行反选(Invert),也就是输出不匹配的结果。

-R: 递归搜索子目录

更高效的工具:ripgrep

查找 shell 命令

  1. 按向上的方向键会显示你使用过的上一条命令,继续按上键则会遍历整个历史记录。
  2. history 命令允许您以程序员的方式来访问shell中输入的历史命令。
  3. 使用 Ctrl+R 对命令历史记录进行回溯搜索。反复按下就会在所有搜索结果中循环。

Directory Navigation

Fasd 基于 frecency对文件和文件排序,也就是说它会同时针对频率(frequency )和时效( recency)进行排序。默认情况下,fasd使用命令 z 帮助我们快速切换到最常访问的目录。例如, 如果经常访问/home/user/files/cool_project 目录,那么可以直接使用 z cool 跳转到该目录。

对于 autojump,则使用j cool代替即可。

tree可以用来以树的形式表示目录结构。

posted @ 2021-09-20 15:04  zju_cxl  阅读(422)  评论(0编辑  收藏  举报