Hello World

以下为Hello World示例代码,执行后输出Hello World

#!/bin/bash
echo Hello World

shebang行

Bash 脚本的第一行往往以 #! 开头,这一行称作 shebang 行。在 类 UNIX 系统中,shebang行用来指定脚本的解释器路径,通常出现在第一行,格式如下

#!interpreter_path [optional-arg]

shebang 行中开头 #! 字符的作用是告诉操作系统这不是一个普通二进制文件,而是需要通过解释器运行的东西而这个解释器则通过 #!字符后面来指定。例如 /bin/bash表示使用 bash 解释器来执行该脚本文件下面则是一些 Bash 脚本的 shebang 行:

#!/usr/bin/perl
#!/usr/bin/awk
#!/usr/bin/python

推荐写法 由于解释器可能不在/bin目录下,当使用#!/bin/bash会报错,更稳妥的写法是使用env在$PATH里面进行寻找响应的解释器:

#!/usr/bin/env bash
#!/usr/bin/env sh
#!/usr/bin/env python

# /usr/bin/env 二进制文件总是在目录 /usr/bin/

env还可以使用一些参数,可以通过env --help查看

执行

假设Hello World示例文件名为hello-word.sh(一般shell文件以.sh结尾),所在位置为/data目录下

# 执行方式一,输出Hello World
bash hello-world.sh

# 执行方式二,输出Hello World
sh hello-world.sh

# 执行方式三,输出Hello World;执行chmod +x hello-world.sh,确保有执行权限
/data/hello-world.sh

# 执行方式四,输出Hello World; 需要先cd /data目录下,执行chmod +x hello-world.sh,确保有执行权限
./hello-world
  • 方式一:是将hello-world.sh作为参数传给 bash 解释器(命令)来执行,不需要给脚本文件添加执行权限、会忽略shebang行指定的解释器路径
  • 方式二:同[方式一]原理相同,解释器由bash换成了sh
  • 方式三:通过绝对路径去执行脚本,需要脚本具有有执行权限,同时需要shebang行。操作系统在执行此命令时候会读取shebang行(假设为#!/bin/bash), 则实际执行为/bin/bash /data/hello-world.sh(可以通过把shebang行改为#!/usr/bin/ls -l确认此行为,其最终效果等同于执行/usr/bin/ls -l /data/hello-world.sh); 如果忘记写shebang行,操作系统会以默认的SHELL执行脚本,系统默认的shell可以通过echo $SHELL命令来查看(可以通过cat /etc/shells查看系统中所有的shell)
  • 方式四:原理同【方式三】

尝试把hello-world.sh的内容修改为:

#!/usr/bin/ls -l
echo Hello World

上述四种方式执行,查看一下效果

echo

echo 是 Bash 和其他 Unix shell 中的一个常用命令,用于在终端输出字符串或变量的值。它的名字来源于“echo”这个英文单词,意思是“回响”或“重复”,因为在早期系统中,echo 命令主要用于将输入(通常是命令行参数)简单地“回显”或显示在终端上。

基本用法

echo 命令的基本用法是将传递给它的参数显示在终端上。例如:

echo Hello, World!

这将在终端上输出 Hello, World!

变量输出

你也可以使用 echo 来输出变量的值。例如:

name="Alice"
echo Hello, $name!

这将在终端上输出 Hello, Alice!

转义字符

echo 支持转义字符,例如 \n(换行)、\t(制表符)等。但请注意,在某些系统中(如 macOS 的默认 bash shell),echo 命令可能不支持 -e 选项来解析转义字符。在这些情况下,你可能需要使用 printf 命令。

使用 -e 选项和转义字符的示例:

echo -e "Hello\nWorld"

这将在终端上输出两行,第一行是 Hello,第二行是 World

隐藏换行符

默认情况下,echo 命令会在其输出后添加一个换行符。如果你不希望有换行符,可以使用 -n 选项。

echo -n "No newline here"
echo " and here is a newline"

这将在终端上输出 No newline here and here is a newline,其中两个字符串之间没有额外的空行。

简单计算

虽然 echo 主要用于输出文本,但结合 Bash 的其他特性,你也可以使用它进行简单的数学计算。例如:

echo $((2 + 3))

这将在终端上输出 5

总结

echo 是一个简单但强大的命令,用于在终端输出文本或变量的值。它支持转义字符、变量插值和简单的数学计算,是 Bash 脚本编写中不可或缺的工具之一。echo还有一个-E选项意思是明确告诉echo不进行字符转义;可以使用help echo查看echo的详细使用方式

命令

Hello World示例中已经展示了echo命令的使用方式,通用的Shell命令使用基本方式:

command [ arg1 ... [ argN ]]
  • command是命令名称或者可执行文件名称,
  • arg1 ... argN是传递给命令的可选参数。 参数之间以空格分隔,若参数之间存在多个空格Bash只识别一个空格,忽略剩余的空格,如echo Hello WorldHelloWorld直接存在5个空格,显示结果只包含一个空格:Hello World。 若想输出多个空格可以只用”“把输出内容包裹起来echo "Hello World"
  • arg1 ... argN一般以-开头或者--开头,以-开头是方便输入的参数名简称,以--开头则是参数名全称,例如ls -lls --list是对应的
  • 单个命令一般都是一行,用户按下回车键,就开始执行。有些命令比较长,写成多行会有利于阅读和编辑,这时可以在每一行的结尾加上反斜杠,Bash 就会将下一行跟当前行放在一起解释
    echo Hello World
    #效果相同
    echo Hello \
    World
    

组合命令

有时候需要连续执行多个命令,比如切换到/data目录下查看hello-world.sh信息;可以使用cd /data;ls -l hello-world.sh(这个场景下可以使用绝对路径方式ls -l /data/hello-world.sh) ,Bash中提供;&&||进行命令组合:

  1. ;方式: command1; command2,无论command1执行成功还是失败,command2都会执行
  2. &&方式:command1 && command2command1执行成功command2才会执行,command1执行失败command2则不会执行
  3. ||方式:command1 || command2command1执行失败command2才会执行, command1执行成功command2则不会执行

命令来源

whichtype

ls 不是内建命令

/usr/bin/echo 和内建 echo 的主要区别在于它们是如何被shell调用和实现的。

  1. 来源
    • /usr/bin/echo 是一个外部命令,也就是说,它是一个位于文件系统(通常是 /usr/bin 目录)中的可执行文件。当你执行这个命令时,shell会启动一个新的进程来运行它。
    • 内建 echo 是shell(如bash、zsh等)的一部分,它直接由shell解释和执行,不需要启动新的进程。
  2. 性能
    • 由于内建 echo 不需要启动新的进程,所以它在性能上通常会比外部 echo 快一些,因为它减少了上下文切换和其他与新进程创建和终止相关的开销。
  3. 行为差异
    • 不同版本的 /usr/bin/echo 和不同的shell可能会有不同的行为,特别是在处理特殊字符和选项时。例如,某些版本的 echo 可能会解释反斜杠(\)后的某些字符(如 \n 表示换行),而另一些版本则不会。为了增加兼容性,POSIX标准要求 -E 选项用于禁用解释反斜杠,而 -e 选项用于启用解释。但是,并非所有的 echo 实现都遵循这些选项。
    • 内建 echo 的行为通常更加一致,因为它是由特定的shell实现的。在bash中,内建 echo 默认不会解释反斜杠,除非你使用了 -e 选项。
  4. 可移植性
    • 由于外部 echo 的行为可能因系统和版本而异,因此使用外部 echo 可能会降低脚本的可移植性。相比之下,内建 echo 的行为更加一致,因此使用内建 echo 可以提高脚本的可移植性。
  5. 使用
    • 你可以使用 which 命令来查找外部 echo 的位置,例如 which echo。但是,由于内建 echo 是shell的一部分,所以 which 命令不会显示它。相反,你可以使用 type 命令或 command -v 命令来区分内部和外部命令,例如 type echocommand -v echo
  6. 别名和函数
    • 如果你在shell中设置了别名或函数来覆盖 echo,那么当你使用 echo 时,实际上是在调用这个别名或函数,而不是外部或内建的 echo。但是,你可以使用 command echo\echo 来绕过别名或函数,直接调用内建 echo

总之,虽然外部 echo 和内建 echo 在功能上相似,但它们在性能、行为、可移植性和使用方式上有所不同。在编写shell脚本时,为了增加一致性和可移植性,通常建议优先使用内建命令(如果可用)。

在 Bash 和其他一些 shell 中,当你运行一个命令时,shell 会查找这个命令并执行它。为了加速这个过程,Bash 提供了一个特性叫做“哈希表”(hash table),用于存储最近执行过的命令的路径。这样,当你再次运行同一个命令时,Bash 可以直接从哈希表中获取命令的路径,而不需要每次都去 PATH 环境变量中搜索。

当你看到这样的消息:

ls is hashed (/usr/bin/ls)

这通常意味着 Bash 已经将 ls 命令的路径哈希到了 /usr/bin/ls。这通常是在你第一次运行 ls 命令后发生的,并且 Bash 记录了它的位置以便将来快速访问。

这个哈希表是 Bash 用来优化命令查找性能的一个内部机制。如果你移动了某个命令(例如,将 /usr/bin/ls 移动到了其他位置),但 Bash 的哈希表中仍然指向旧的位置,那么当你尝试运行这个命令时,Bash 可能会找不到它,因为哈希表中的路径已经过时了。

为了解决这个问题,你可以使用 hash 命令来管理 Bash 的哈希表。例如,要清除哈希表中的所有条目,你可以运行:

hash -r

之后,当你再次运行 ls 时,Bash 会重新搜索 PATH 环境变量来找到 ls 的位置,并更新哈希表。

通常,你不需要手动管理哈希表,除非你遇到了与命令位置更改相关的问题。在大多数情况下,Bash 会自动处理哈希表的更新。

在shell中,当您尝试执行一个命令时,shell会按照特定的顺序来查找并执行该命令。对于内建命令和外部命令(即位于PATH环境变量中指定的目录里的命令),shell的查找和执行机制是不同的,这也涉及到它们的“优先级”。

  1. 内建命令(Builtin Commands)

    • 内建命令是shell自身提供的功能,它们直接嵌入在shell中,不需要通过子进程来执行。
    • 当你执行一个内建命令时,shell会直接在内部执行它,而不需要去搜索文件系统。
    • 由于内建命令直接由shell处理,所以它们通常比外部命令更快。
    • 你可以使用type命令或builtin命令来查看一个命令是否是内建的。
    • 例子:cdechoalias等都是常见的Bash内建命令。
  2. 外部命令(External Commands)

    • 外部命令是存在于文件系统中的可执行文件,通常位于PATH环境变量指定的目录中。
    • 当你执行一个外部命令时,shell会在PATH环境变量中指定的目录中搜索该命令的可执行文件。
    • 如果找到了匹配的命令,shell会创建一个新的子进程来执行这个命令。
    • 由于涉及到文件系统搜索和子进程创建,所以外部命令通常比内建命令慢一些。
    • 你可以使用which命令来查找外部命令的完整路径。
  3. 优先级

    • 当shell遇到一个命令时,它会首先检查这个命令是否是内建的。
    • 如果是内建的,shell会直接执行它。
    • 如果不是内建的,shell会在PATH环境变量中搜索该命令。
    • 如果找到了匹配的外部命令,shell会创建子进程来执行它。
    • 如果没有找到匹配的命令,shell会报错,告诉你该命令未找到。

因此,从“优先级”的角度来看,内建命令的优先级高于外部命令。但是要注意,这并不是说内建命令比外部命令更重要或更强大,而是说shell在处理命令时有一个特定的顺序和机制。