1: Hello World
交互式 shell
Bash shell 常用于交互式使用: 它允许你输入和编辑命令,然后在按回车键时执行这些命令。许多基于 Unix 和类 Unix 的操作系统都将 Bash 作为默认 shell(尤其是 Linux 和 macOS)。终端启动时会自动进入交互式 Bash shell 进程。
输入以下内容,输出 Hello World:
echo "Hello World"
#> Hello World # 例子的输出结果
备注
只需在终端中输入 shell 的名称,即可更改 shell。例如:sh、bash 等。
echo 是 Bash 的内置命令,它会将收到的参数写入标准输出。默认情况下,它会在输出中加上换行符。
非交互式 shell
Bash shell 也可以通过脚本实现非交互式运行,这样就不需要人在终端前面交互从而实现一些定时任务等操作。交互行为和脚本行为应该是一致的,这是 Unix V7 Bourne shell 和 Bash 的重要设计考虑因素。因此,任何可以在命令行完成的操作都可以放在脚本文件中重复使用。
请按照以下步骤创建 Hello World 脚本:
1 创建一个名为 hello-world.sh 的新文件
touch hello-world.sh
2 运行以下命令使脚本可执行
chmod +x hello-world.sh
3 在文章中添加下面的代码:
#!/bin/bash
echo "Hello World"
第 1 行: 脚本的第一行必须以字符串 #! 开头,称为 shebang(中文Linux社区译名是“释伴”)。Shebang 命令操作系统运行 /bin/bash,即 Bash shell,并将脚本的路径作为参数传递给它。
例如: /bin/bash hello-world.sh
第 2 行: 使用 echo 命令将 Hello World 写入标准输出。
4 使用以下命令之一从命令行执行 hello-world.sh 脚本:
- ./hello-world.sh - 最常用,推荐使用
- /bin/bash hello-world.sh
- bash hello-world.sh – 假设 /bin 已经在你的 $PATH 中(默认 /bin 和 /usr/bin 有在 $PATH 中)
- sh hello-world.sh
在实际生产使用中,你可以省略 .sh 扩展名(无论如何这都是误导,因为这是一个 Bash 脚本,而不是 sh 脚本),或许可以将文件移动到你 $PATH 中的目录里面,这样你可以就像使用 cat 或 ls 等系统命令一样,无论你当前在什么工作目录都直接使用而不用加结对路径。
常见错误包括:
1 忘记对文件赋予执行权限,即 chmod +x hello-world.sh,导致执行命令时输出 ./hello-world.sh: Permission denied 权限被拒绝。
2 在 Windows 上编辑脚本,会产生 Bash 无法处理的错误行结束符。
常见的症状是:“command not found”(未找到命令),其中回车将光标强制移到行首,覆盖了错误信息中冒号前的文本。
可以使用dos2unix 程序修改脚本。如: dos2unix hello-world.sh 。dos2unix 会对文件进行内联编辑。
3 使用 sh ./hello-world.sh 执行脚本,没有意识到 bash 和 sh 是不同的 shell,具有不同的功能(不过由于 Bash 是向后兼容的,所以相反的错误是无害的)。
总之,在每个脚本的文件名前明确写上 bash 或 sh(或 python 或 perl 或 awk 或 ruby 或......)比直接依赖脚本的 shebang 行要好得多。
为了让你的脚本更易于移植,一个常用的 Shebang 行是使用 #!/usr/bin/env bash 而不是硬编码 Bash 的路径。这样,/usr/bin/env 必须存在,但除此之外,bash 只需要在你的 PATH 中即可。在许多系统中,/bin/bash并不存在,你应该使用/usr/local/bin/bash或其他绝对路径;这一改动避免了找出其中的细节。
2: 使用变量的 Hello World
新建一个名为 hello.sh 的文件,内容如下,并使用 chmod +x hello.sh 赋予其可执行权限。
通过 ./hello.sh World运行脚本。
#!/usr/bin/env bash
# Note that spaces cannot be used around the `=` assignment operator
whom_variable="World"
# Use printf to safely output the data
printf "Hello, %s\n" "$whom_variable"
#> Hello, World
执行后将在标准输出中打印 Hello, World。
要告诉 bash 解释器你的脚本在哪里,你需要非常明确地将它指向包含脚本的目录,如果它是你当前的工作目录,通常使用 ./,其中 . 是当前目录的别名。如果没有指定目录,bash 解释器会尝试在环境变量 $PATH 所包含的目录中找到脚本。
下面的代码接受参数 $1(即第一个命令行参数),并以格式化的字符串输出,如下所示:Hello,.
通过 ./hello.sh World运行脚本。
#!/usr/bin/env bash
printf "Hello, %s\n" "$1"
#> Hello, World
It is important to note that $1 has to be quoted in double quote, not single quote. "$1" expands to the first command line argument, as desired, while '$1' evaluates to literal string $1.
需要注意的是,$1 必须使用双引号,而不是单引号。"$1"会按要求展开为第一个命令行参数,而 '$1' 会求值为字面字符串 $1。
了解将变量文本置于双引号中的重要性。可以阅读这篇官方文档
https://unix.stackexchange.com/questions/171346/security-implications-of-forgetting-to-quote-a-variable-in-bash-posix-shells
3: 带用户输入的 Hello World
下面的程序将提示用户输入内容,然后将输入内容以字符串(文本)形式存储在一个变量中。然后使用该变量向用户发送一条信息。
#!/usr/bin/env bash
echo "Who are you?"
read name
echo "Hello, $name."
这里的读取命令是从标准输入中读取一行数据到变量名中。然后使用 $name 进行引用,并使用 echo 打印到标准输出。
输出示例
$ ./hello_world.sh
Who are you?
Matt
Hello, Matt.
在这里,用户输入了名字 "Matt",这段代码用来表示 “你好,Matt......”。
如果要在打印变量值时附加一些内容,请在变量名周围使用大括号,如下例所示:
#!/usr/bin/env bash
echo "What are you doing?"
read action
echo "You are ${action}ing."
示例输出:
$ ./hello_world.sh
What are you doing?
Sleep
You are Sleeping.
在这里,当用户输入一个操作时,“ing ”会在打印时附加到该操作上。
4: 引号在字符串中的重要性
引号对于 bash 中的字符串扩展非常重要。有了引号,你就可以控制 bash 如何解析和扩展字符串。
引号的使用有两种类型:
- 弱引用:使用双引号: ”
- 强引用:使用单引号: '
如果你想让 bash 引用你的参数变量,你可以使用弱引用:
#!/usr/bin/env bash
world="World"
echo "Hello $world"
#> Hello World
如果你不想 bash 引用的的参数变量,而只是使用字符串本身,你可以使用强引用:
#!/usr/bin/env bash
world="World"
echo 'Hello $world'
#> Hello $world
您也可以使用转义来防止扩展:
#!/usr/bin/env bash
world="World"
echo "Hello \$world"
#> Hello $world
5: 查看 Bash 内置项的信息
help
上面的这个命令这将显示指定内置程序的 Bash 帮助(手册)页面。
例如,help unset 将显示
unset: unset [-f] [-v] [-n] [名称 ...]
取消设定 shell 变量和函数的值和属性。
对每一个 NAME 名称,删除对应的变量或函数。
选项:
-f 将每个 NAME 视为函数
-v 将每个 NAME 视为变量
-n 将每个 NAME 视为名称引用,只取消其本身而非其指向的变量
不带选项时,unset 首先尝试取消设定一个变量,如果失败,再尝试取消设定一个函数。
某些变量不可以被取消设定;参见 `readonly'。
退出状态:
返回成功,除非使用了无效的选项或者 NAME 名称为只读。
要查看附有简短说明的所有内置程序列表,请使用 help -d。
[root@almalinux ~]# help -d
GNU bash,版本 5.1.8(1)-release (x86_64-redhat-linux-gnu)
这些 shell 命令是内部定义的。请输入 `help' 以获取一个列表。
输入 `help 名称' 以得到有关函数`名称'的更多信息。
使用 `info bash' 来获得关于 shell 的更多一般性信息。
使用 `man -k' 或 `info' 来获取不在列表中的命令的更多信息。
名称旁边的星号(*)表示该命令被禁用。
job_spec [&] history [-c] [-d 偏移量] [n] 或 history -anrw >
(( 表达式 )) if 命令; then 命令; [ elif 命令; then 命令; ]..>
. 文件名 [参数] jobs [-lnprs] [任务声明 ...] 或 jobs -x 命令 >
: kill [-s 信号声明 | -n 信号编号 | -信号声明] >
[ 参数... ] let 参数 [参数 ...]
[[ 表达式 ]] local [option] 名称[=值] ...
alias [-p] [名称[=值] ... ] logout [n]
bg [任务声明 ...] mapfile [-d 分隔符] [-n 计数] [-O 起始序号] [->
bind [-lpvsPSVX] [-m 键映射] [-f 文件名] [-q 名> popd [-n] [+N | -N]
break [n] printf [-v var] 格式 [参数]
builtin [shell 内建 [参数 ...]] pushd [-n] [+N | -N | 目录]
caller [表达式] pwd [-LP]
case 词 in [模式 [| 模式]...) 命令 ;;]... esac read [-ers] [-a 数组] [-d 分隔符] [-i 缓冲区文>
cd [-L|[-P [-e]] [-@]] [目录] readarray [-d 定界符] [-n 计数] [-O 起始序号] [>
command [-pVv] 命令 [参数 ...] readonly [-aAf] [名称[=值] ...] 或 readonly -p
compgen [-abcdefgjksuv] [-o option] [-A action] > return [n]
complete [-abcdefgjksuv] [-pr] [-DEI] [-o option> select NAME [in 词语 ... ;] do 命令; done
compopt [-o|+o 选项] [-DEI] [名称 ...] set [--abefhkmnptuvxBCHP] [-o 选项名] [--] [参>
continue [n] shift [n]
coproc [名称] 命令 [重定向] shopt [-pqsu] [-o] [选项名 ...]
declare [-aAfFgiIlnrtux] [-p] [name[=value] ...> source 文件名 [参数]
dirs [-clpv] [+N] [-N] suspend [-f]
disown [-h] [-ar] [任务声明 ... | pid ...] test [表达式]
echo [-neE] [参数 ...] time [-p] 管道
enable [-a] [-dnps] [-f 文件名] [名称 ...] times
eval [参数 ...] trap [-lp] [[参数] 信号声明 ...]
exec [-cl] [-a name] [command [argument ...]] [r> true
exit [n] type [-afptP] 名称 [名称 ...]
export [-fn] [名称[=值] ...] 或 export -p typeset [-aAfFgiIlnrtux] [-p] name[=value] ...
false ulimit [-SHabcdefiklmnpqrstuvxPT] [限制]
fc [-e 编辑器名] [-lnr] [起始] [终结] 或 fc -s > umask [-p] [-S] [模式]
fg [任务声明] unalias [-a] 名称 [名称 ...]
for 名称 [in 词语 ... ] ; do 命令; done unset [-f] [-v] [-n] [名称 ...]
for (( 表达式1; 表达式2; 表达式3 )); do 命令; do> until 命令; do 命令; done
function 名称 { 命令 ; } 或 name () { 命令 ; } variables - 一些 shell 变量的名称和含义
getopts optstring name [arg ...] wait [-fn] [-p var] [id ...]
hash [-lr] [-p 路径名] [-dt] [名称 ...] while 命令; do 命令; done
help [-dms] [模式 ...] { 命令 ; }
6: 调试模式中的 Hello World
$ cat hello.sh
#!/bin/bash
echo "Hello World"
$ bash -x hello.sh
+ echo Hello World
Hello World
使用 -x 参数可以查看脚本中的每一行。下面就是一个很好的例子:
$ cat hello.sh
#!/bin/bash
echo "Hello World\n"
adding_string_to_number="s"
v=$(expr 5 + $adding_string_to_number)
$ ./hello.sh
Hello World
expr: non-integer argument
以上提示的错误不足以追踪脚本;但使用以下方法可以更好地了解在脚本中查找错误的位置。
$ bash -x hello.sh
+ echo Hello World\n
Hello World
+ adding_string_to_number=s
+ expr 5 + s
expr: non-integer argument
+ v=
7: 处理参数命名
#!/bin/bash
deploy=false
uglify=false
while (( $# > 1 )); do case $1 in
--deploy) deploy="$2";;
--uglify) uglify="$2";;
*) break;
esac; shift 2
done
$deploy && echo "will deploy... deploy = $deploy"
$uglify && echo "will uglify... uglify = $uglify"
# how to run
# chmod +x script.sh
# ./script.sh --deploy true --uglify false
如果您对我的文章有兴趣,我把我发布的文章都归档到我私人网站中去,欢迎访问 Corner 三的小角落 -- 首页 查阅之前的文章。