相关文章推荐
潇洒的电梯  ·  java获取季度日期_java ...·  1 周前    · 
笑点低的消防车  ·  Getting Spring ...·  1 年前    · 
仗义的沙发  ·  excel vba error 429 ...·  1 年前    · 
卖萌的马铃薯  ·  create-react-app ...·  1 年前    · 
  • 如何把命令的输出放到文件里?如何把命令的输出作为其他命令的输入?
  • 在命令行黑乎乎的窗口,我要怎么下载文件?
  • 在命令行下,下载任务可以放在后台吗?如果我在命令行下结束下载任务,会不会使已下载部分全部白费?
  • 在命令行下怎么查找与替换文件中的字符串?
  • Bash 语法和 C 语言类似吗?和 Powershell 类似吗?
  • Bash script 出 bug 的时候我该如何调试呢?
  • 下面内容可以解答你的疑问。

    I/O 重定向与管道

    重定向

    一般情况下命令从 标准输入(stdin) 读取输入,并输出到 标准输出(stdout) ,默认情况下两者都是你的终端。使用重定向可以让命令从文件读取输入/输出到文件。下面是以 echo 为例的重定向输出:

    $ echo "Hello Linux!" > output_file # 将输出写入到文件(覆盖原有内容)
    $ cat output_file
    Hello Linux!
    $ echo "rewrite it" > output_file
    $ cat output_file # 可以看到原来的 Hello Linux! 被覆盖了。
    rewrite it
    $ echo "append it" >> output_file # 将输出追加到文件(不会覆盖原有内容)
    $ cat output_file
    rewrite it
    append it
    

    无论是 > 还是 >>,当输出文件不存在时都会创建该文件。

    重定向输入使用符号 <

    command < inputfile
    command < inputfile > outputfile
    

    除了 stdin 和 stdout 还有标准错误(stderr),他们的编号分别是 0、1、2。stderr 可以用 2> 重定向(注意数字 2 和 > 之间没有空格)。

    使用 2>&1 可以将 stderr 合并到 stdout。

    管道(pipe),操作符 |,作用为将符号左边的命令的 stdout 接到之后的命令的 stdin。管道不会处理 stderr。

    管道是类 UNIX 操作系统中非常强大的工具。通过管道,我们可以将实现各类小功能的程序拼接起来干大事。

    示例如下:

    $ ls / | grep bin  # 筛选 ls / 输出中所有包含 bin 字符串的行
    

    网络下载

    为何使用 wget 和 cURL

    在 Windows 下,很多人下载文件时会使用「迅雷」、「QQ 旋风」(停止运营)、「IDM」之类的软件来实现下载。那么在 Linux 环境下呢?在终端下,没有可视化软件提供点击下载。即使有桌面环境,有 Firefox 可以很方便地下载文件,硬件资源也会被很多不必要的服务浪费。通过以下内容讲述的 wget (wget) 和 cURL (curl) 工具,我们可以 Linux 上进行轻量的下载活动。

    Wget

    wget 是强力方便的下载工具,可以通过 HTTP 和 FTP 协议从因特网中检索并获取文件。

    Wget 的特点

    支持以非交互方式工作,能够在用户注销后在后台进行工作。

    在不稳定的连接中依旧可以正常工作,支持断点续传功能。

    支持 HTML 页面以及 FTP 站点的递归检索,您可以使用它来获取网站的镜像,或者像爬虫一样遍历网络。

    在文件获取时可以增加时间标记,因此可以自动识别远程文件自上次检索后是否发生更改,并自动检索新版本。

    支持代理服务器,以减轻网络负载,加快检索速度。

    使用 Wget

    使用 man wget 得到的结果为 wget [option]... [URL]...,其中的更多参数可以通过查看帮助 wget -h 来获取。

    常用的选项

    安装 oh-my-zsh:

    $ sh -c "$(wget https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh -O -)"
    

    cURL

    cURL (curl) 是一个利用 URL 语法在命令行下工作的文件传输工具,其中 c 意为 client。虽然 cURL 和 Wget 基础功能有诸多重叠,如下载。但 cURL 由于可自定义各种请求参数,所以在模拟 web 请求方面更擅长;wget 由于支持 FTP 协议和递归遍历,所以在下载文件方面更擅长。

    使用 cURL

    wget 部分,我们可以查看帮助 curl -h 了解其用法。

    常用的选项

    下载 USTCLUG 的 logo:

    $ curl -O "https://ftp.lug.ustc.edu.cn/misc/logo-whiteback-circle.png"
    

    只展示 HTTP 响应头内容:

    $ curl -I "http://cn.bing.com"
    

    除了 Wget、cURL,还有 mwget(多线程版本 wget)、Axel、Aria2(支持 BT 协议、支持 JSON-RPC 和 XML-RPC 接口远程调用)之类下载工具,其中 Aria2 在 Windows 下使用也很广泛。

    文本处理

    在进行文本处理时,我们有一些常见的需求:

  • 获取文本的行数、字数
  • 比较两段文本的不同之处
  • 查看文本的开头几行和最后几行
  • 在文本中查找字符串
  • 在文本中替换字符串
  • 下面介绍如何在 shell 中做到这些事情。

    文本统计:wc

    wc 是文本统计的常用工具,它可以输出文本的行数、单词数与字符(字节)数。

    $ wc file
         427    2768   20131 file
    

    统计中文文本时的问题

    wc 在统计中文文本时,会出现一些问题,比如:

    $ echo '中文测试' | wc
    1       1      13
    

    这里显示文本只有 1 个单词,但是有 13 个字符,这显然是不对的。

    对于字符数统计结果,可以使用 wc -m 命令要求 wc 考虑宽字符:

    $ echo '中文测试' | wc -m
    

    换行符也是一个符号,所以结果为 5(而非 4)。

    由于中文文本的单词统计涉及分词算法问题,wc 无法准确统计。

    文本比较:diff

    diff 工具用于比较两个文件的不同,并列出差异。

    $ echo hello > file1
    $ echo hallo > file2
    $ diff file1 file1
    $ diff file1 file2
    < hello
    > hallo
    

    加参数 -w 可忽略所有空白字符, -b 可忽略空白字符的数量变化。

    假如比较的是两个文本文件,差异之处会被列出;假如比较的是二进制文件,只会指出是否有差异。

    文本开头与结尾:head & tail

    顾名思义,head 和 tail 分别用来显示开头和结尾指定数量的文字。

    以 head 为例,这里给出共同的用法:

  • 不加参数的时候默认显示前 10 行
  • -n <NUM> 指定行数,可简化为 -<NUM>
  • -c <NUM> 指定字节数
  • $ head file  # 显示 file 前 10 行
    $ head -n 25 file  # 显示 file 前 25 行
    $ head -25 file  # 显示 file 前 25 行
    $ head -c 20 file  # 显示 file 前 20 个字符
    $ tail -10 file  # 显示 file 最后 10 行
    

    除此以外,tail 还有一个非常实用的参数 -f:当文件末尾内容增长时,持续输出末尾增加的内容。这个参数常用于动态显示 log 文件的更新(试一试 tail -f /var/log/syslog)。

    文本查找:grep

    grep 命令可以查找文本中的字符串:

    $ grep 'hello' file  # 查找文件 file 中包含 hello 的行
    $ ls | grep 'file'  # 查找当前目录下文件名包含 file 的文件
    $ grep -i 'Systemd' file  # 查找文件 file 中包含 Systemd 的行(忽略大小写)
    $ grep -R 'hello' .  # 递归查找当前目录下内容包含 hello 的文件
    

    不止如此!

    grep 事实上是非常强大的查找工具,第九章将在介绍正则表达式语法之后进一步介绍 grep。

    文本替换:sed

    sed 命令可以替换文本中的字符串:

    $ sed 's/hello/world/g' file  # 将文件 file 中的 hello 全局(global)替换为 world 后输出
    $ sed 's/hello/world/' file  # 将文件 file 的每一行第一个出现的 hello 替换为 world 后输出
    $ echo 'helloworld' | sed 's/hello/world/g'  # 管道也是可以的
    $ sed -i 's/hello/world/g' file  # -i 参数会直接写入文件,操作前记得备份哦!
    $ sed -i.bak 's/hello/world/g' file  # 当然,也可以让 sed 帮你备份到 file.bak
    

    对于大多数用户来说,最常用 sed 的场合是替换软件源的时候。在阅读了上面的示例之后,以下例子就很简单了:

    $ sudo sed -i 's/cn.archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
    $ sudo sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
    $ sudo sed -i 's/security.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
    

    同样不止如此!

    sed 事实上是非常强大的文本操作工具,不仅支持正则表达式,而且能够做的操作也不止是替换。第九章将进一步介绍 sed。

    Shell 脚本

    什么是 Shell

    Shell 是 Linux 的命令解释程序,是用户和内核之间的接口。除了作为命令解释程序外,Shell 同时还提供了一个可支持强大脚本语言的程序环境。

    Bash

    Bourne Shell (sh),是 Unix 系统的默认 Shell,简单轻便,脚本编程功能强,但交互性差。

    Bourne Again Shell,即 Bash,是 GNU 开发的一个 Shell,也是大部分 Linux 系统的默认 Shell,是 Bourne Shell 的扩展。

    Bash 允许用户定制环境以满足自己需要。通过修改环境文件 .bash_profile.bashrc.bash_logout,配置合适的环境变量,可以改变主目录、命令提示符、命令搜索路径等用户工作环境。

    此外,bash 也支持使用 alias 别名代替命令关键字(alias name='命令')。输入 alias,可以查看目前存在的别名:

    $ alias
    alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
    alias egrep='egrep --color=auto'
    alias fgrep='fgrep --color=auto'
    alias grep='grep --color=auto'
    alias l='ls -CF'
    alias la='ls -A'
    alias ll='ls -alF'
    alias ls='ls --color=auto'
    $ ll  # 执行 ll 相当于执行 ls -alF
    总用量 128
    drwxr-xr-x 18 ustc ustc 4096 2月  28 00:51 ./
    drwxr-xr-x  3 root root 4096 11月 17 20:26 ../
    drwxr-xr-x  2 ustc ustc 4096 11月 17 20:45 公共的/
    drwxr-xr-x  2 ustc ustc 4096 11月 17 20:45 模板/
    (以下省略)
    

    Bash 脚本的运行

    可以使用几种方法运行 Bash 脚本:

    在指定的 Shell 下执行,将脚本程序名作为 Shell 的第一个参数:

    $ bash show.sh [option]
    

    使用 . 命令执行脚本。与其他命令一样,. 也是一条命令,其后要有空格:

    $ . ./show.sh [option]
    

    将脚本设置为可执行,然后像外部命令一样执行:

    $ chmod a+x show.sh
    $ ./show.sh [option]
    

    许多 Bash 脚本会在文件首行加上 #!/bin/bash 。这里 #! 符号的名称是 shebang(也叫 sha-bang,即 sharp # 与 bang !)。当一个文本文件首行有 shebang,且以可执行模式执行时,shebang 后的内容会看作这个脚本的解释器和相关参数,系统会执行解释器命令,并将脚本文件的路径作为参数传递给该命令。

    例如,某个 foo.sh 首行为 #!/bin/bash,则执行 ./foo.sh 就等于执行 /bin/bash ./foo.sh

    Bash 也支持在同一个行中安排多个命令:

    shell 变量

    像大多数程序设计语言一样,shell 也允许用户在程序中使用变量。但 shell 不支持数据类型,它将任何变量值都当作字符串。但从赋值形式上看,可将 shell 变量分成四种形式:用户自定义、环境变量、位置变量和预定义特殊变量。

    用户自定义变量

    变量定义:name=串,其中 = 两边不允许有空格。如果字串中含空格,就要用双引号括起。在引用时,使用 $name${name},后者花括号是为了帮助解释器识别变量边界。

    已定义的变量可以通过 unset name 来删除。

    变量使用示例

    变量定义:

    for skill in Ada Coffee Action Java; do
        echo "I am good at ${skill}Script"
    
    I am good at AdaScript
    I am good at CoffeeScript
    I am good at ActionScript
    I am good at JavaScript
    

    如果不给 skill 加花括号标明变量名的边界,写成 echo "I am good at $skillScript" ,解释器就会把 $skillScript 当成一个变量(其值为空)。

    删除变量:

    Today=1024
    unset Today
    echo $Today
    

    输出为空。

    处理未定义的变量

    在以上的例子中,我们可以注意到 bash 中未定义的变量默认值为空值。在编写 shell 脚本时,我们可能会希望能够严格一些:如果变量未定义,就直接报错退出。这样的话,如果变量名出现了拼写错误,我们就能第一时间发现。

    可以在脚本开头加上 set -u 来实现这一点:

    #!/bin/bash
    set -u
    echo $nonexist
    echo "This will never be printed."
    

    执行可以发现输出类似如下的错误:

    example.sh: line 4: nonexist: unbound variable
    

    环境变量

    每个用户登录系统后,Linux 都会为其建立一个默认的工作环境,由一组环境变量定义,用户可以通过修改这些环境变量,来定制自己工作环境。在 Bash 中,可用 env 命令列出所有已定义的环境变量。通常,用户最关注的几个变量是:

    HOME:用户主目录,一般情况下为 /home/用户名

    LOGNAME:登录用户名。

    PATH:命令搜索路径,路径以冒号分割。当我们输入命令名时,系统会在 PATH 变量中从前往后逐个搜索对应的程序是否在目录中。

    PWD:用户当前工作目录路径。

    SHELL:默认 shell 的路径名。

    TERM:使用的终端名。

    可以使用 export 命令来定义环境变量。在同一个 shell 中使用 export 定义之后,这个环境变量会一直保留,直到这个 shell 退出。

    $ export A=1
    $ env | grep A=
    A=1
    

    此外,也可以在命令前加上环境变量的定义。此时只有这一条命令的环境变量出现变化。

    $ B=1 env | grep B=
    B=1
    $ env | grep B=
    $ # B=1 的环境变量定义仅对该命令有效
    

    位置变量

    Shell 解释用户的命令时,把命令程序名后面的所有字串作为程序的参数。分别对应 $1$2$3、……、$9,程序名本身对应 $0

    可用 shift <n> 命令,丢弃开头的 n 个位置变量,改变 $1$2$3 等的对应关系。

    可用 set 命令,重置整个位置变量列表,从而给 $1$2$3 等赋值。

    $ set one two three
    $ echo $1 $2 $3
    one two three
    $ shift 2
    $ echo $1 $2 $3
    three
    $ # 此时 $2 和 $3 已不存在
    

    特殊变量

    Shell 中还有一组有 shell 定义和设置的特殊变量,用户只能引用,而不能直接改变或重置这些变量。

    特殊字符示例

    $ ls /mnt/c/Program Files/
    ls: cannot access /mnt/c/Program: No such file or directory
    ls: cannot access Files/: No such file or directory
    $ # 对于 ls 来说,它接收到了两个参数:/mnt/c/Program 和 Files/,因此会报错。
    $ # 可以使用反斜杠来转义空格
    $ ls /mnt/c/Program\ Files/  # 输出省略
    $ # 或者使用双引号或单引号包裹
    $ ls "/mnt/c/Program Files/"
    $ ls '/mnt/c/Program Files/'
    $ echo "$PWD"  # 双引号中仍然可以使用各种 shell 特殊符号
    /home/ustc
    $ echo '$PWD'  # 但是单引号则不行
    $ ls -lh `which ls`  # 查看 ls 命令对应的程序信息,使用反引号语法
    -rwxr-xr-x 1 root root 139K Sep  5  2019 /usr/bin/ls
    $ ls -lh $(which ls) # 使用 $(command) 语法也是一样的
    -rwxr-xr-x 1 root root 139K Sep  5  2019 /usr/bin/ls
    

    变量输入与输出

    可以使用 read 命令读取用户输入,并将输入的内容赋值给变量。其中 -p 参数后可以设置输出的提示信息。

    $ name=""
    $ read -p "Enter your name: " name  # 输出提示,然后从输入读取一个值,存储到 $name 中
    Enter your name: linux
    $ echo $name
    linux
    

    在使用 read 时,建议加上 -r 参数,否则 \ 会被视为转义符号。

    $ message=""
    $ read -p "Enter your message: " message
    Enter your message: \(^o^)/~
    $ echo $message  # 可以看到,反斜杠被认为是转义符号,结果被丢弃
    (^o^)/~
    $ read -r -p "Enter your message: " message
    Enter your message: \(^o^)/~
    $ echo $message  # 加上 -r 参数后,反斜杠完好无损
    \(^o^)/~
    

    可以使用 echo 命令输出变量信息。其中 -n 参数不会在结尾输出换行符,而 -e 参数会解析文本中的转义字符(例如 \n)。

    $ echo -n "hello"
    hello$ # 由于这里 echo 结尾不输出换行,shell 就会在这里继续接受用户输入
    $ # 这也是为什么在 C 语言中最后的 printf 输出需要加上 \n 的原因
    $ name="linux"
    $ echo "Hello $name.\nWelcome to bash!"  # 可以看到 \n 没有被转义成换行
    Hello linux.\nWelcome to bash!
    $ echo -e "Hello $name.\nWelcome to bash!"  # 加上 -e 之后就被转义了
    Hello linux.
    Welcome to bash!
    

    此外,printf 命令也可以用来输出,它的使用方法类似于 C 中的 printf() 函数。

    $ name="linux"
    $ printf "Hello %s" "$name"
    Hello linux$ # 和 echo 不同,printf 结尾默认不输出换行符
    $ printf "Hello %s\n" "$name"
    Hello linux
    $ # 所以为了正常显示,需要在结尾补上 \n
    

    算术运算

    在 Bash 中进行算术运算,需要使用 expr 计算算术表达式值或 let 命令赋值表达式值到变量。基本运算符是 +-\* (转义)、/%。在 expr 中,运算符两边与操作数之间必须有空格,小括号要转义;但 let 则没有这个要求,运算符前后有无空格均可,小括号不需转义,但 = 前后不能有空格。

    另外,所有标准的 shell 都支持另一种语法 (( 表达式 )),其中 表达式 是一个 C 风格的数学表达式,可以计算,也可以赋值。(( 表达式 )) 是一条完整的命令,命令的返回值为 0 或 1。若表达式的结果非零,那么 (( 表达式 )) 命令返回零,而当表达式结果为零时命令返回 1。

    (( 表达式 )) 的返回值
    $ (( 1 + 1 ))
    $ echo $?  # 结果为 2,所以返回值为 0
    $ (( 1 - 1 ))
    $ echo $?  # 返回值为 1,因为结果是 0
    

    使用 $(( 表达式 )) 可以将计算结果用作为命令行的一部分,就像使用变量一样。

    exprlet 使用示例
    $ expr length "ustclug"
    $ let a=0
    $ echo $a
    $ let a++
    $ echo $a
    $ ((a+=1))
    $ echo $a
    $ echo $((a+=a/a))
    

    条件表达式

    条件表达式写成 test 条件表达式,或 [ 条件表达式 ],注意表达式与方括号之间有空格。

    字符串比较
    条件分支:if

    序列中可嵌套 if 语句,在 shell 中也允许有多个 elif ,但 shell 的流程控制不可为空。末尾的 fi 就是 if 倒过来写,后面还会遇到类似的结束符。

    if condition1
      commands1
    elif condition2
      commands2
      commands3
    
    按值选择:case

    选项值必须以右括号 ) 结尾,若匹配多个离散值,用 | 分隔。这里的 esac 也是 case 倒着写。

    case <variable> in
    value1|value2)
      command1
      command2
    value3)
      command3
      command4
    
    枚举循环:for

    for 循环可以对一个列表中的每个值都执行一系列命令。

    for var in list
      commands $var
    
    条件循环:while 和 until

    while 循环用于不断执行一系列命令,命令通常为测试条件。until 与 while 相反,仅在测试条件失败时循环。

    while condition
      commands
    until condition
      commands
    MAX_NO=0
    read -r -p "Enter Number between (5 to 9) : " MAX_NO
    if ! [ "$MAX_NO" -ge 5 -a "$MAX_NO" -le 9 ] ; then
      echo "I ask to enter number between 5 and 9, Okay"
      exit 1
    clear
    for (( i=1; i<=MAX_NO; i++ ))
      for (( s=MAX_NO; s>=i; s-- ))
        echo -n " "
      for (( j=1; j<=i;  j++ ))
        echo -n " ."
      echo ""
    for (( i=MAX_NO; i>=1; i-- ))
      for (( s=i; s<=MAX_NO; s++ ))
        echo -n " "
      for (( j=1; j<=i;  j++ ))
        echo -n " ."
      echo ""
    

    输出结果:

    Enter Number between (5 to 9) : 9
           . . .
          . . . .
         . . . . .
        . . . . . .
       . . . . . . .
      . . . . . . . .
     . . . . . . . . .
     . . . . . . . . .
      . . . . . . . .
       . . . . . . .
        . . . . . .
         . . . . .
          . . . .
           . . .
    

    除此之外,用于流程控制的还有在 C 语言中同样常见的 breakcontinue。与 C 语言不同的是,它们还接受一个数字作为参数,即 break ncontinue n,其中参数 n 均表示跳出或跳过 n 层循环。

    与其他编程语言类似,shell 也可以定义函数。其定义格式为:

    # POSIX syntax
    name() {
        commands
        [return <int>]
    # Bash syntax
    function name {
        commands
        [return <int>]
    

    其中函数的参数可以像命令一样通过 $1$2$3 等获取,返回值可以显式用 return 返回,或以最后一条命令运行结果作为返回值。在函数中使用 return 会结束本次函数执行,而使用 exit 会直接结束退出包含函数的当前脚本程序。

    函数在使用前必须定义,因此应将函数定义放在脚本开始的部分。在调用函数时仅使用其函数名即可。

    某带函数的某脚本程序内容如下:

    #!/bin/bash
    hello() {
      echo "hello $1, today's date is `date`"
    echo "going to call test function:"
    hello ustc
    

    运行脚本,输出结果:

    going to call test function:
    hello ustc, today's date is Tue Feb 22 22:22:22 CST 2022
    

    Bash 脚本调试

    Bash shell 本身提供了调试方法:

    命令行中:$ bash -x script.sh

    脚本开头:#!/bin/bash -x

    在脚本中用 set 命令调整(set -x 启用,set +x 禁用)。

    其中参数选项可以更改,-n:读一遍脚本中的命令但不执行,用于检查语法错误;-v:一边执行脚本、一边将执行过的脚本命令打印到标准输出;-x:提供跟踪执行信息,将执行的每一条命令和结果依次打印出来。注意避免几种调试选项混用。

    除了 Bash shell 内置的选项,还有 BASH Debuggershellcheck 等第三方脚本分析工具。

    思考题

    I/O 重定向的小细节

    wc -l filewc -l < file 输出有什么区别?为什么?

    echo < file 会输出什么?

    设定 HTTP 请求头

    尝试查询 curlwget 的文档,给出设定 HTTP 请求头的方法。

    附:HTTP 在请求时,会发送诸如以下的信息:

    GET / HTTP/1.1
    Host: cn.bing.com
    User-Agent: curl/7.54.0
    Accept: */*
    

    GET / HTTP/1.1 下面的几行就是 HTTP 请求头。

    HTTP 请求头在向服务器发送 HTTP 请求时发出,包含了诸如访问服务的域名(Host)、用户程序标识(User-Agent)、希望接收到的语言(Accept-Language)等各种信息。

    关于断点续传

    查找资料,了解下载器的「断点续传」功能依赖于什么 HTTP 特性?

    /usr/bin/env

    你可能会发现,某些脚本(不仅仅是 shell 脚本)的第一行开头为 #!/usr/bin/env。尝试解释原因。

    Shell 脚本编写练习 #1

    ffmpeg 程序可以提取视频中的音频流,并输出到音频格式文件(如 MP3)。

    现在,你下载了很多视频在目录下(可以假设后缀相同,例如都是 flv)。你需要提取这些视频的音频轨道到 MP3 文件中。尝试搜索 ffmpeg 的使用资料,编写一个 shell 脚本实现。

    Shell 脚本编写练习 #2

    现有两个程序 ./a./b,它们分别接受一个数字作为参数。现在需要编写一个脚本,要求:

  • 检查输入的第一个参数 (设为 x) 是否存在。如果不是,输出提示,结束。
  • 先执行 ./a,以 x 作为第一个参数。如果 ./a 执行成功,就执行 ./b,以 x 的平方作为第二个参数。
  • 假设输入 x 是数字且不大。

    Shell 脚本编写练习 #3 (难)

    建议阅读第九章后再尝试完成本题。

    尝试编写一个 shell 脚本,下载某个网页上所有的 PDF 文件(例如 2019年春季全校《电磁学》小论文竞赛获奖名单 这个网页)。已知所有的文件都以小写的 .pdf 结尾,并且都在 a 标签的 href 属性中。

    引用来源

  • catonmat
  • vbird
  • runoob
  • linuxde
  • Bash Quoting
  •