用户是通过终 端会话同 shell 环境打交道的。如果你使用的是基于图形用户界面的系统,这指的就是终端窗口。如果没有图形用户界面(生产服务器或SSH会话),那么登录后你看到的就是 shell 提示符。

在终端中显示文本是大多数脚本和实用工具经常需要执行的任务。shell 可以使用多种方法和格式显示文本。

1.2.1 预备知识

命令都是在终端会话中输入并执行的。打开终端时会出现一个提示符。有很多方法可以配置提示符,不过其形式通常如下:

  1. username@hostname$

或者也可以配置成root@hostname #,或者简单地显示为$#
image.png
$表示普通用户,#表示管理员用户rootrootLinux系统中权限最高的用户。

root 用户(管理员)的身份直接使用 shell 来执行任务可不是个好主意。因为如果 shell 具备较高的权限,命令中出现的输入错误有可能造成更严重的破坏,所以推荐使用普通用户(shell会在提示符中以$来表明这种身份)登录系统,然后借助sudo这类工具来运行特权命令。使用sudo <command> <arguments>执行命令的效果和 root 一样。

shell 脚本通常以 shebang 开始的

shebang这个词其实是两个字符名称(sharp-bang)的简写。在Unix的行话里,用sharphash(有时候是mesh)来称呼字符“#”,用bang来称呼惊叹号“!”,因而shebang 合起来就代表了这两个字符。详情请参考:https://en.wikipedia.org/wiki/Shebang_(Unix))

  1. #!/bin/bash

D:\Projects\Github\marmotedu\iam\scripts\install\environment.sh

  1. #!/usr/bin/env bash

shebang 是一个文本行,其中#!位于解释器路径之前。/bin/bashbash 的解释器命令路径。bash 将以 **#** 符号开头的行视为注释。脚本中只有第一行可以使用 shebang 来定义解释该脚本所使用的解释器。

脚本的执行方式有两种。

  1. 将脚本名作为命令行参数:

    1. bash myScript.sh
  2. 授予脚本执行权限,将其变为可执行文件:

    1. chmod 755 myScript.sh
    2. ./myScript.sh.

    如果将脚本作为 bash 的命令行参数来运行,那么就用不着使用shebang了。可以利用shebang来实现脚本的独立运行。可执行脚本使用shebang之后的解释器路径来解释脚本。

使用chmod命令赋予脚本可执行权限:

  1. $ chmod a+x sample.sh

该命令使得所有用户可以按照下列方式执行该脚本:

  1. $ ./sample.sh #./表示当前目录

或者

  1. $ /home/path/sample.sh #使用脚本的完整路径

内核会读取脚本的首行并注意到shebang#!/bin/bash。它会识别出/bin/bash并执行该脚本:

  1. $ /bin/bash sample.sh

当启动一个交互式shell时,它会执行一组命令来初始化提示文本、颜色等设置。这组命令来自用户主目录中的脚本文件~/.bashrc(对于登录shell则是~/.bash_profile)。Bash shell还维护了一个历史记录文件~/.bash_history,用于保存用户运行过的命令。

~表示主目录,它通常是/home/user,其中user是用户名,如果是root用户,则为/root。登录shell是登录主机后创建的那个shell。但登录图形化环境(比如GNOMEKDE等)后所创建的终端会话并不是登录shell。使用GNOMEKDE这类显示管理器登录后并不会读取.profile.bash_profile(绝大部分情况下不会),而使用ssh登录远程系统时则会读取.profileshell使用分号或换行符来分隔单个命令或命令序列。比如:

  1. $ cmd1;cmd2

这等同于:

  1. $ cmd1
  2. $ cmd2

注释部分以#为起始,一直延续到行尾。注释行通常用于描述代码或是在调试期间禁止执行某行代码

  1. # sample.sh - echoes "hello world"
  2. echo "hello world"

现在让我们继续讨论基本特性。

1.2.2 实战演练

echo是用于终端打印的最基本命令。

默认情况下,echo在每次调用后会添加一个换行符:

  1. [root@dev iam]# echo "Welcome to Bash"
  2. Welcome to Bash
  3. [root@dev iam]#

只需要将文本放入双引号中,echo命令就可以将其中的文本在终端中打印出来。类似地,不使用双引号也可以得到同样的输出结果:

  1. [root@dev iam]# echo Welcome to Bash
  2. Welcome to Bash
  3. [root@dev iam]#

实现相同效果的另一种方式是使用单引号:

  1. [root@dev iam]# echo 'text in quotes'
  2. text in quotes
  3. [root@dev iam]#

这些方法看起来相似,但各有特定的用途及副作用。双引号允许shell解释字符串中出现的特殊字符。单引号不会对其做任何解释。

思考下面这行命令:

  1. [root@dev iam]# echo "cannot include exclamation - ! within double quotes"
  2. cannot include exclamation - ! within double quotes
  3. [root@dev iam]#

命令输出如下:

  1. cannot include exclamation - ! within double quotes

如果需要打印像!这样的特殊字符,那就不要将其放入双引号中,而是使用单引号,或是在特殊字符之前加上一个反斜线(\):

  1. [root@dev iam]# echo Hello world !
  2. Hello world !
  3. [root@dev iam]#

或者

  1. [root@dev iam]# echo 'Hello world !'
  2. Hello world !

或者

  1. $ echo "Hello world \!" #将转义字符放在前面

如果不使用引号,我们无法在echo中使用分号,因为分号在Bash shell中用作命令间的分隔符:

  1. echo hello; hello

image.png
对于上面的命令,Bash将echo hello作为一个命令,将hello作为另外一个命令。

在下一条攻略中将讨论到的变量替换不会在单引号中执行。

另一个可用于终端打印的命令是printf。该命令使用的参数和C语言中的printf函数一样。

例如:

  1. [root@dev iam]# printf "Hello world"
  2. Hello world[root@dev iam]#

printf 命令接受引用文本或由空格分隔的参数。我们可以在printf中使用格式化字符串来指定字符串的宽度、左右对齐方式等。默认情况下,printf并不会自动添加换行符,我们必须在需要的时候手动指定,比如在下面的脚本中:

  1. #!/bin/bash
  2. #文件名: printf.sh
  3. printf "%-5s %-10s %-4s\n" No Name Mark
  4. printf "%-5s %-10s %-4.2f\n" 1 Sarath 80.3456
  5. printf "%-5s %-10s %-4.2f\n" 2 James 90.9989
  6. printf "%-5s %-10s %-4.2f\n" 3 Jeff 77.564
  1. [root@dev workspace]# vi printf.sh
  2. [root@dev workspace]# chmod +x ./printf.sh
  3. [root@dev workspace]# ./printf.sh

可以得到如下格式化的输出:

  1. [root@dev workspace]# ./printf.sh
  2. No Name Mark
  3. 1 Sarath 80.35
  4. 2 James 91.00
  5. 3 Jeff 77.56
  6. [root@dev workspace]#

1.2.3 工作原理

%s%c%d%f都是格式替换符(format substitution character),它们定义了该如何打印后
续参数。%-5s指明了一个格式为左对齐且宽度为 5 的字符串替换(-表示左对齐)。如果不指明-
字符串就采用右对齐形式。宽度指定了保留给某个字符串的字符数量。对Name而言,其保留宽度是 10。因此,任何Name字段的内容都会被显示在 10 字符宽的保留区域内,如果内容不足 10 个字符,余下的则以空格填充。

对于浮点数,可以使用其他参数对小数部分进行舍入(round off)。

对于Mark字段,我们将其格式化为%-4.2f,其中.2指定保留两位小数。注意,在每行的格
式字符串后都有一个换行符(\n)。

1.2.4 补充内容

使用echoprintf的命令选项时,要确保选项出现在命令中的所有字符串之前,否则Bash会将其视为另外一个字符串。

在echo中转义换行符

默认情况下,echo会在输出文本的尾部追加一个换行符。可以使用选项-n来禁止这种行为。
echo同样接受双包含转义序列的双引号字符串作为参数。在使用转义序列时,需要使用echo-e
“包含转义序列的字符串”这种形式。例如:

  1. [root@dev workspace]# echo "1\t2\t3"
  2. 1\t2\t3
  3. [root@dev workspace]# echo -e "1\t2\t3"
  4. 1 2 3
  5. [root@dev workspace]#

打印彩色输出

脚本可以使用转义序列在终端中生成彩色文本。
文本颜色是由对应的色彩码来描述的。其中包括:重置=0黑色=30红色=31绿色=32黄色=33蓝色=34洋红=35青色=36白色=37
要打印彩色文本,可输入如下命令:

  1. [root@dev workspace]# echo -e "\e[1;31m This is red text \e[0m"

image.png
其中\e[1;31m是一个转义字符串,可以将颜色设为红色,\e[0m将颜色重新置回。只需要将31
换成想要的色彩码就可以了。

对于彩色背景,经常使用的颜色码是:重置=0黑色=40红色=41绿色=42黄色=43蓝色=44洋红=45青色=46白色=47

要设置彩色背景的话,可输入如下命令:

  1. echo -e "\e[1;42m Green Background \e[0m"

image.png
这些例子中包含了一些转义序列。可以使用man console_codes来查看相关文档。

shell 参数换行

Linux 参数太长,一般可取续行符 “**\**“进行参数换行/续行。反斜杠\后面紧跟回车,表示下一行是当前行的续行。

如下的代码所示

  1. python target_attack.py \
  2. --input_dir="${INPUT_DIR}" \
  3. --output_dir="${OUTPUT_DIR}" \
  4. --max_epsilon="${MAX_EPSILON}" \
  5. --checkpoint_path_adv_inception_v3=adv_inception_v3.ckpt \
  6. --checkpoint_path_ens3_adv_inception_v3=ens3_adv_inception_v3.ckpt \
  7. --checkpoint_path_ens4_adv_inception_v3=ens4_adv_inception_v3.ckpt \
  8. --checkpoint_path_ens_adv_inception_resnet_v2=ens_adv_inception_resnet_v2.ckpt \
  9. --checkpoint_path_adv_inception_resnet_v2=adv_inception_resnet_v2.ckpt \
  10. --iterations=11 \
  11. --prob=0.5

最近遇到一个坑,在\后面加上了空格,导致后面的参数不能进行结息。但是代码又比较考算力,跑了一个晚上的代码,后来发现参数没有赋值成功,发现赋值失败,有--prob=0.5: command not found 的提示字段。

后来一点点排查,才知道是\后面多了一个空格,但是在编辑器中看不出来任何差别。。。

【注意】
\后面紧接着是enter换行符,即使用\回车的方式,不能有空格之类的任何符号,否则会造成解析错误,导致后面的参数赋值不成功。