Missing semeter - Shell工具和脚本

一、Shell 脚本

以bash脚本为例

1、变量赋值语法:foo=bar(shell脚本中使用空格会起到分割参数的作用,有时会造成混淆)

2、访问变量中储存的数值:$foo

3、以'定义的字符串为原义字符串,其中的变量不会被转义,而 "定义的字符串会将变量值进行替换。

4、bash也支持ifcasewhile 和 for 这些控制流关键字。同样地, bash 也支持函数,它可以接受参数并基于参数进行操作。

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

        这里 $1 是脚本的第一个参数。与其他脚本语言不同的是,bash使用了很多特殊的变量来表示参数、错误代码和相关变量。下面列举了其中一些变量 

  • $0 - 脚本名
  • $1 到 $9 - 脚本的参数。 $1 是第一个参数,依此类推。
  • $@ - 所有参数
  • $# - 参数个数
  • $? - 前一个命令的返回值
  • $$ - 当前脚本的进程识别码
  • !! - 完整的上一条命令,包括参数。常见应用:当你因为权限不足执行命令失败时,可以使用 sudo !!再尝试一次。
  • $_ - 上一条命令的最后一个参数。

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

        退出码可以搭配 &&(与操作符)和 ||(或操作符)使用,用来进行条件判断,决定是否执行其他程序。它们都属于短路运算符(short-circuiting) 同一行的多个命令可以用 ; 分隔。程序 true 的返回码永远是0false 的返回码永远是1

6、命令替换(command substitution) :以变量的形式获取一个命令的输出。$(cmd):例如,如果执行 for file in $(ls) ,shell首先将调用ls ,然后遍历得到的这些返回值。

7、进程替换(process substitution):<(cmd)会执行CMD并将结果输出到一个临时文件夹中,并将<(CMD)替换成临时文件名。例如,diff <(ls foo) <(ls bar)会显示文件夹foo和bar中文件的区别。

8、shell的通配(globbing):

        — 通配符:?和 * 可以匹配一个或任意个字符。例如,对于文件foofoo1foo2foo10 和 barrm foo?这条命令会删除foo1 和 foo2 ,而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

         注意,脚本并不一定只有用 bash 写才能在终端里调用。内核知道去用 python 解释器而不是 shell 命令来运行这段脚本,是因为脚本的开头第一行的 shebang。在Shebang之后,可以有一个或数个空白字符,后接解释器的绝对路径,用于指明执行这个脚本文件的解释器。

        使用 #!/usr/bin/env+脚本解释器名称 是一种常见的在不同平台上都能正确找到解释器的办法。因为 env 会利用我们第一节讲座中介绍过的PATH 环境变量来进行定位。

二、Shell 工具

1、查看命令如何使用:

man 命令是手册(manual)的缩写,它提供了命令的用户手册。

 TLDR pages 是一个很不错的替代品,它提供了一些案例,可以帮助您快速找到正确的选项。

2、查找文件:

 find是 shell 上用于查找文件的绝佳工具。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 \;

        fd,locate也是比较好的工具。

3、查找代码:

grep命令,它是用于对输入文本进行匹配的通用工具。

  grep 有很多选项,这也使它成为一个非常全能的工具。其中我经常使用的有 -C :获取查找结果的上下文(Context);-v 将对结果进行反选(Invert),也就是输出不匹配的结果。举例来说, grep -C 5 会输出匹配结果前后五行。当需要搜索大量文件的时候,使用 -R 会递归地进入子目录并搜索所有的文本文件。

        因此也出现了很多它的替代品,包括 ackag 和 rg。它们都特别好用,但是功能也都差不多,我比较常用的是 ripgrep (rg) ,因为它速度快,而且用法非常符合直觉。例子如下:

# 查找所有使用了 requests 库的文件
rg -t py 'import requests'
# 查找所有没有写 shebang 的文件(包含隐藏文件)
rg -u --files-without-match "^#!"
# 查找所有的foo字符串,并打印其之后的5行
rg foo -A 5
# 打印匹配的统计信息(匹配的行和文件的数量)
rg --stats PATTERN

三、课后练习

1、编写两个bash函数 marco 和 polo 执行下面的操作。 每当你执行 marco 时,当前的工作目录应当以某种形式保存,当执行 polo 时,无论现在处在什么目录下,都应当 cd 回到当时执行 marco 的目录。 为了方便debug,你可以把代码写在单独的文件 marco.sh 中,并通过 source marco.sh命令,(重新)加载函数。

#!/bin/bash
 marco(){
     # $(pwd)获取当前工作目录的绝对路径,并保存
     echo "$(pwd)" > $HOME/marco_history.log
     # 输出日志信息,显示保存路径
     echo "save pwd $(pwd)"
 }
 polo(){
     # 通过cat函数获取被保存的路径
     cd "$(cat "$HOME/marco_history.log")"
 }

2、 假设您有一个命令,它很少出错。因此为了在出错时能够对其进行调试,需要花费大量的时间重现错误并捕获输出。 编写一段bash脚本,运行如下的脚本直到它出错,将它的标准输出和标准错误流记录到文件,并在最后输出所有内容。 加分项:报告脚本在失败前共运行了多少次

#!/usr/bin/env bash

 n=$(( RANDOM % 100 ))

 if [[ n -eq 42 ]]; then
    echo "Something went wrong"
    >&2 echo "The error was using magic numbers"
    exit 1
 fi

 echo "Everything went according to plan"

解:用while来解答

 #!/usr/bin/env bash
 count=0
 # 创建一个空的out.log文件
 echo > out.log

 while true
 do    
     # 运行buggy.sh脚本将标准输出和错误追加到out.log文件中
     ./buggy.sh &>> out.log
     if [[ $? -ne 0 ]]; then
         cat out.log
         echo "failed after $count times"
         break
     fi
     ((count++))

 done

ps:&>> 是一种用来重定向输出的特殊符号,它将标准输出和标准错误都追加到指定的文件中。/

具体来说:

  • >:用于将标准输出重定向到文件,会覆盖文件中的内容。
  • >>:用于将标准输出追加到文件末尾,不会覆盖文件中的现有内容。
  • 2>:用于将标准错误重定向到文件,会覆盖文件中的内容。
  • 2>>:用于将标准错误追加到文件末尾,不会覆盖文件中的现有内容。

但是,&>&>> 的使用方式略有不同:

  • &>:将标准输出和标准错误都重定向到同一个文件,会覆盖文件中的内容。
  • &>>:将标准输出和标准错误都追加到同一个文件的末尾,不会覆盖文件中的现有内容。

3、编写一个命令,它可以递归地查找文件夹中所有的HTML文件,并将它们压缩成zip文件。注意,即使文件名中包含空格,您的命令也应该能够正确执行。

find . -type f -name "*.html" | xargs -d '\n'  tar -cvzf html.zip
  1. . 的含义. 表示当前目录。当你在命令行中运行 find . 时,find 命令会从当前目录开始查找。

  2. -type f 参数-type f 参数告诉 find 命令只找普通文件(regular files)。这样做的目的是排除目录、符号链接等其他文件类型。

  3. -name "*.html" 参数-name "*.html" 参数用于指定查找文件名以 .html 结尾的文件。这是一个模式匹配,会找到所有以 .html 结尾的文件,无论它们在哪个子目录里。

  4. 递归搜索find 命令默认情况下是递归搜索的。也就是说,它会从当前目录开始,深入进入每个子目录,直到找到所有符合条件的文件为止。

  5. 使用 -print0 参数来打印符合条件的文件名,每个文件名以 NULL 字符分隔。这确保了即使文件名中包含空格或特殊字符,也能正确地传递给下一个命令。使用 -0 参数来接收 find 命令输出的文件列表(每个文件名以 NULL 字符分隔),并将它们作为参数传递给 tar 命令。tar 命令创建一个名为 html.zip 的压缩文件,其中包含所有符合条件的 .html 文件。选项 -cvzf 分别表示创建压缩文件 (c),显示详细信息 (v),使用 gzip 压缩算法 (z),并指定压缩文件名为 html.zip (f html.zip)。

4、(进阶)编写一个命令或脚本递归的查找文件夹中最近使用的文件。更通用的做法,你可以按照最近的使用时间列出文件吗?

find . -type f -mmin -60 -print0 | xargs -0 ls -lt | head -10
  • 28
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱吃山竹的开心小迟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值