missing-semester-2

敖炜 Lv5

Shell工具和脚本

  • 今天住进了峨眉山山里,网速感人,环境感人,所以这几天的更新就随缘咯

一点小知识

  • bash是Mac或Linux系统中默认的Shell,与其他Shell兼容

Shell脚本

  1. 在bash中为变量赋值的语法是foo=bar,访问变量中存储的数值,语法为$foo
  2. 定义变量时,注意不能使用空格
  3. 定义字符串时,可用双引号/单引号定义字符串
    • 对于纯文本来说,两种定义等价
    • 对于变量的引用,双引号内会将变量值进行替换;单引号内不会替换变量值
  4. 下面会让shell记住sh文件中的函数,之后你就可以调用了
    1
    source xxx.sh
  5. 和大多数的编程语言一样,bash也支持if,case,while和for这些控制流关键字。bash也支持函数,可接受参数并基于参数进行操作。
  6. bash使用很多特殊的变量来表示参数、错误代码和相关变量。完整参考链接:这里
    • $0: 脚本名
    • $1到$9: 脚本参数
    • $@: 所有参数
    • $#: 参数个数
    • $?: 前一个命令的返回值
    • $$: 当前脚本的进程识别码
    • !!: 完整的上一条命令,包括参数。sudo !!很有用
    • $_: 上一条命令的最后一个参数。交互式shell可通过按下Esc之后按.来获取这个值
  7. 同一行的多个命令可以用;分隔
  8. 以变量的形式获取一个命令的输出,可通过以下方法
    • 命令替换
      • 通过$(CMD)执行CMD这个命令时,输出结果会替换掉$(CMD)
      • 执行for file in $(ls), shell首先调用ls,然后遍历得到的这些返回值
      • foo=$(pwd)将pwd的执行结果存到foo中
    • 进程替换
      • <(CMD)会执行CMD并将结果输出到一个临时文件中,并将<(CMD)替换成临时文件名
      • cat <(ls) <(ls ..)会在内部执行,然后将输出放到一个类似临时文件的东西中,并将文件标识符提供给最左边的命令
  9. example1
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #!/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,因为我们并不关心这些信息,/dev/null被称为黑洞文件,2>用于重定向标准错误流
    if [[ $? -ne 0 ]]; then
    # -ne表示“not equal”
    echo "File $file does not have any foobar, adding one"
    echo "# foobar" >> "$file"
    fi
    done
  • 使用source x.sh来调用shell脚本
  1. 在bash中进行比较时,尽量使用双方括号[[]]而不是单方括号[],这样会降低犯错的几率,尽管这样并不能兼容sh。
  2. 当执行脚本时,我们经常需要提供形式类似的参数。bash使我们可以轻松的实现这一操作,它可以基于文件拓展名展开表达式。这一技术被称为shell的通配
    • 通配符:?*可匹配一个或任意个字符
    • 花括号{}:一系列指令中包含一段公共字串时,可用花括号来自动展开这些命令。在批量移动转换文件时非常方便。还可以多层操作,做笛卡尔积
  3. 检查sh/bash脚本中的错误的工具:shellcheck 。bash并不现代化,有时候调试起来很麻烦
  4. 脚本并不一定只有用bash写才能在终端里调用。如下的Python脚本(将输入的参数倒序输出),内核知道用python解释器而不是shell命令来运行这段脚本(因为脚本的开头第一行的shebang。实际上可以用许多不同语言来实现与shell交互的脚本。python默认情况下不会与shell交互
    1
    2
    3
    4
    #!/usr/local/bin/python
    import sys
    for arg in reversed(sys.argv[1:]):
    print(arg)
    • 开头的魔法行叫做shebang,是让shell知道如何运行这个脚本,shebang展示的就是运行这个脚本的程序所在路径
    • 你可能无法提前知道python或者其他解释器安装在哪里,所以你可以使用“env”,#!/usr/bin/env python,env在/usr/bin
    • 使用python x.py来调用py脚本
  5. 在shebang行中使用env 命令会利用环境变量中的程序来解析该脚本,提高了可移植性
  6. shell函数和脚本的不同点
    • shell函数只能与shell使用相同的语言,脚本可以使用任何语言。脚本中包含shebang是很重要的
    • 函数仅在定义时被加载,脚本会在每次被执行时加载。使得函数的加载比脚本略快一些,但每次修改函数定义,都要重新加载一次
    • 函数会在当前的shell环境中执行,脚本会在单独的进程中执行。因此函数可以对环境变量进行更改,如改变当前工作目录,脚本则不行。脚本需要使用export 将环境变量导出,并将值传递给环境变量
    • 与其他程序语言一样,函数可以提高代码模块性、代码复用性并创建清晰性的结构。shell脚本中往往也会包含那它们自己的函数定义
  7. 在bash中有一系列的比较运算符,可通过man test查看

Shell工具

查看命令如何使用

  1. man有时候没有实例,而且太过详细,所以不太方便
  2. 可以安装一个很好的,叫做tldr的工具,可以得到一些工具很好的例子

查找文件

  1. find用于查找文件很好用
    1. -name指定搜索名称
    2. -type指定搜索类型
    3. -path指定搜索路径
    4. -mtime指定修改时间,-x表示x天之内
    5. 还可指定大小、所有者、权限等
  2. find不仅可以查找东西,也可以在找到这些文件时执行一些操作-exec 操作
  3. fd可以快捷查找文件名包含某些字符的文件,默认使用正则表达式,甚至还能忽略搜索你的git文件
  4. 如果一天中要进行多次find,采用数据库方式并首先构建一个索引,然后让该索引以某种方式进行更新。大多数Unix系统已经通过“locate”命令完成了这一点。locate只会查找你的文件系统中包含你想要的子字符串的路径。因为为文件系统建立了索引,所以检索速度比较快。为了时期保持更新,可以使用updatedb命令来更新此数据库

查找代码

  1. 有时你实际上关心的不是文件本身,而是文件的内容,因此,可使用我们之前看到的“grep”命令。
    • 递归地搜索当前结构并查找更多文件,使用grep的-R标志,它将遍历整个目录
      1
      grep -R 查找内容 查找基目录
    • -C:获取查找结果的上下文(Context)
    • -v对结果进行反选(Invert),输出不匹配的结果
  2. ripgrep,它与grep的想法有点相同,但是ripgrep更好看,如彩色编码文件处理等,也支持unicode,有许多有用的标志

查找shell命令

  1. 如何查找你已经使用过的命令,不是找文件或代码
    1. 显然的方法就是使用上箭头,浏览后寻找
    2. 更轻松的方法,history会打印出所有命令的历史记录,结合管道和grep可筛选特定命令
  2. 几乎所有的shell默认都会将“Ctrl+R”这个键绑定为反向搜索
    • 这里我们有一个反向(回溯)搜索,我们可以输入“convert”,一直按“Ctrl+R”会浏览这些匹配项,让我们在原地重新执行
  3. fzf是一个模糊查找器,就像它可以让你进行交互式的“grep”,如果启用了默认绑定,它会绑定到你的“Ctrl+R”
  4. 用来避免重新输入非常长的命令的工具,叫做基于历史的自动补全,动态地在历史记录中搜索具有相同前缀的相同命令,它会随着匹配列表停止工作而改变,然后当你按右箭头时,你可以选择那个命令,然后重新执行它

文件夹导航

  1. 快速目录列表和目录导航:总是可以使用“ls -R”来递归地列出某个目录结构。但这可能不是最优的,这不是很好理解

    • 有一个叫做“tree”的工具,它更友好地打印目录结构
    • 有一个“broot”的工具,它可以做同样的事情
  2. 其他好用的工具

    • nnn
  3. cd更好用、更方便的快速到达一些地方的方法

    • 设置alias,使用ln -s创建符号连接等
    • 使用fasdautojump两个工具来查找最常用或最近使用的文件和目录
      • fasd基于frecency对文件和文件排序,会同时针对频率和时效进行排序

习题

  1. 阅读man ls,然后使用ls命令进行如下操作
    • 所有文件(包括隐藏文件):ls -a或者ls --all
    • 文件打印以人类可以理解的格式输出:ls -l -h或者ls -l -human-readable
    • 文件以最近访问顺序排序:ls -t -u
    • 以彩色文本显示输出结果:ls --color=auto或者ls --color=always
  2. 编写两个bash函数marcopolo执行下面的操作。每当你执行marco时,当前的工作目录应当以某种形式保存,当执行polo时,无论现在处在什么目录下,都应当cd回到当时执行marco的目录。为了方便debug,你可以把代码写在单独的文件marco.sh中,并通过source marco.sh命令,(重新)加载函数。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #!/usr/bin/env bash
    marco(){
    echo "$(pwd)" > $HOME/marco_history.log
    echo "save pwd $(pwd)"
    }

    polo(){
    cd "$(cat "$HOME/marco_history.log")"
    }
    或者
    1
    2
    3
    4
    5
    6
    7
    #!/usr/bin/env bash
    marco(){
    export MARCO=$(pwd)
    }
    polo(){
    cd "$MARCO"
    }
    之后执行source marco.sh,就可直接执行这两个函数
  3. 假设您有一个命令,它很少出错。因此为了在出错时能够对其进行调试,需要花费大量的时间重现错误并捕获输出。 编写一段bash脚本,运行如下的脚本直到它出错,将它的标准输出和标准错误流记录到文件,并在最后输出所有内容。 加分项:报告脚本在失败前共运行了多少次。
    待测试脚本
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #!/usr/bin/env bash

    n=$(( RANDOM % 100 ))

    if [[ n -eq 42 ]]; then
    echo "Something went wrong"
    >&2 echo "The error was using magic numbers"
    #>&2可以定义“错误”输出到STDERR指定的文件
    exit 1
    fi

    echo "Everything went arrording to plan"
    使用while循环实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #!/usr/bin/env bash
    count=0
    echo > out.log

    while true
    do
    ./buggy.sh &>> out.log
    #$>>将输出或错误信息都追加到out.log
    if [[ $? -ne 0 ]]; then
    cat out.log
    echo "failed after $count times"
    break
    fi
    ((count++))

    done
  4. 解答
    • 关键点:xargs命令可以将输入中的内容作为参数,可使用-d 值检查xargs中的值
  5. 编写一个命令或脚本递归的查找文件夹中最近使用的文件。更通用的做法,你可以按照最近的使用时间列出文件吗?解答

最后

因为在山里,网络不太好,再加上最近在读另一本有趣的书《学会呼吸:重新掌握天生本能》 ,所以更新的比较慢,其实主要是拖延

  • 标题: missing-semester-2
  • 作者: 敖炜
  • 创建于 : 2023-07-01 09:29:07
  • 更新于 : 2024-04-19 09:32:13
  • 链接: https://ao-wei.github.io/2023/07/01/missing-semester-2/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论