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 World
,Hello
和World
直接存在5个空格,显示结果只包含一个空格:Hello World
。 若想输出多个空格可以只用”“把输出内容包裹起来echo "Hello World"
arg1 ... argN
一般以-
开头或者--
开头,以-
开头是方便输入的参数名简称,以--
开头则是参数名全称,例如ls -l
和ls --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中提供;
、&&
、||
进行命令组合:
;
方式:command1; command2
,无论command1
执行成功还是失败,command2
都会执行&&
方式:command1 && command2
,command1
执行成功command2
才会执行,command1
执行失败command2
则不会执行||
方式:command1 || command2
,command1
执行失败command2
才会执行,command1
执行成功command2
则不会执行
命令来源
which
和 type
ls
不是内建命令
/usr/bin/echo
和内建 echo
的主要区别在于它们是如何被shell调用和实现的。
- 来源:
/usr/bin/echo
是一个外部命令,也就是说,它是一个位于文件系统(通常是/usr/bin
目录)中的可执行文件。当你执行这个命令时,shell会启动一个新的进程来运行它。- 内建
echo
是shell(如bash、zsh等)的一部分,它直接由shell解释和执行,不需要启动新的进程。
- 性能:
- 由于内建
echo
不需要启动新的进程,所以它在性能上通常会比外部echo
快一些,因为它减少了上下文切换和其他与新进程创建和终止相关的开销。
- 由于内建
- 行为差异:
- 不同版本的
/usr/bin/echo
和不同的shell可能会有不同的行为,特别是在处理特殊字符和选项时。例如,某些版本的echo
可能会解释反斜杠(\
)后的某些字符(如\n
表示换行),而另一些版本则不会。为了增加兼容性,POSIX标准要求-E
选项用于禁用解释反斜杠,而-e
选项用于启用解释。但是,并非所有的echo
实现都遵循这些选项。 - 内建
echo
的行为通常更加一致,因为它是由特定的shell实现的。在bash中,内建echo
默认不会解释反斜杠,除非你使用了-e
选项。
- 不同版本的
- 可移植性:
- 由于外部
echo
的行为可能因系统和版本而异,因此使用外部echo
可能会降低脚本的可移植性。相比之下,内建echo
的行为更加一致,因此使用内建echo
可以提高脚本的可移植性。
- 由于外部
- 使用:
- 你可以使用
which
命令来查找外部echo
的位置,例如which echo
。但是,由于内建echo
是shell的一部分,所以which
命令不会显示它。相反,你可以使用type
命令或command -v
命令来区分内部和外部命令,例如type echo
或command -v echo
。
- 你可以使用
- 别名和函数:
- 如果你在shell中设置了别名或函数来覆盖
echo
,那么当你使用echo
时,实际上是在调用这个别名或函数,而不是外部或内建的echo
。但是,你可以使用command echo
或\echo
来绕过别名或函数,直接调用内建echo
。
- 如果你在shell中设置了别名或函数来覆盖
总之,虽然外部 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的查找和执行机制是不同的,这也涉及到它们的“优先级”。
-
内建命令(Builtin Commands):
- 内建命令是shell自身提供的功能,它们直接嵌入在shell中,不需要通过子进程来执行。
- 当你执行一个内建命令时,shell会直接在内部执行它,而不需要去搜索文件系统。
- 由于内建命令直接由shell处理,所以它们通常比外部命令更快。
- 你可以使用
type
命令或builtin
命令来查看一个命令是否是内建的。 - 例子:
cd
、echo
、alias
等都是常见的Bash内建命令。
-
外部命令(External Commands):
- 外部命令是存在于文件系统中的可执行文件,通常位于
PATH
环境变量指定的目录中。 - 当你执行一个外部命令时,shell会在
PATH
环境变量中指定的目录中搜索该命令的可执行文件。 - 如果找到了匹配的命令,shell会创建一个新的子进程来执行这个命令。
- 由于涉及到文件系统搜索和子进程创建,所以外部命令通常比内建命令慢一些。
- 你可以使用
which
命令来查找外部命令的完整路径。
- 外部命令是存在于文件系统中的可执行文件,通常位于
-
优先级:
- 当shell遇到一个命令时,它会首先检查这个命令是否是内建的。
- 如果是内建的,shell会直接执行它。
- 如果不是内建的,shell会在
PATH
环境变量中搜索该命令。 - 如果找到了匹配的外部命令,shell会创建子进程来执行它。
- 如果没有找到匹配的命令,shell会报错,告诉你该命令未找到。
因此,从“优先级”的角度来看,内建命令的优先级高于外部命令。但是要注意,这并不是说内建命令比外部命令更重要或更强大,而是说shell在处理命令时有一个特定的顺序和机制。