Shell 简介
Shell 基本概念
- 终端: 获取用户输入、展示运算结果的硬件设备。
- tty: teletypeWriter, 早期指的是电传打印机, 如今的 tty 变成一个虚拟的概念, 在 Linux 中是一个程序, 表示 输入/输出 环境。
- 终端模拟器: 每一个终端模拟器会关联一个虚拟 tty, 通过 tty 和系统内核交互。Mac Terminal、iTerm2(也是 mac 上的终端)、GNOME Terminal(Linux 上的) 和 Window Terminal (可能需要下载) 是常见的终端模拟器。
- Shell: command interpreter, 命令解释程序, 用来处理来自终端模拟器的输入、解释执行之后输出结果给终端。
- Bash: Shell 的一种具体实现。此外还有 Zsh、Fish 和 Powershell 等。
Shell 发展
- 1971 年, Ken Thompson 在贝尔实验室为 UNIX 开发了第一个 shell, 称为 V6 shell。
- 1979 年, Stephen Bourne 在贝尔实验室在 V7 UNIX 发布了 Bourne shell , 即 sh。
- 1989 年, 开源组织 GNU 为了取代 Bourne shell 发布了第一版 Bourne-Again shell,即 Bash
bash 是 sh 的超集, 可执行大部分 sh 脚本, 并集成了 Korn shell 和 C shell 的功能。
Shell 构成
shell 提供了与内核和设备交互的方法(接口), 还集成软件开发中通用的设计模式(如管道和过滤器), 具备控制流程、循环、变量和命令查找的机制。
shell 是一门命令解释器, 同时也是一门编程语言。
Shell 基础语法
变量
类型 | 作用域 | 声明方式 |
---|---|---|
自定义变量 | 当前 shell | = |
环境变量 | 当前 shell 以及子 shell | export 或 declare -x |
系统环境变量 | 所有 shell | 启动自动加载 |
declare
声明变量类型
格式: declare 选项 变量
选项 | 含义 |
---|---|
- | 给变量设定类型属性 |
+ | 取消变量的类型属性 |
-a | 将变量声明为数组类型 |
-i | 将变量声明为整数型 |
-x | 将变量声明为环境变量 |
-r | 将变量声明为只读变量 |
-p | 输出变量及其被声明的类型 |
示例代码
a.sh
内容:
#!/bin/bash
my_var=123
declare -p my_var
在 bash 终端下运行
$ bash a.sh
declare -x my_var="123
父子 shell
一个 shell 中可以调用另外一个 shell 脚本, 比如:
a.sh
文件内容:
#!/bin/bash
export my_var=123
bash b.sh
b.sh
文件内容:
echo ${my_var}
在 bash 终端下运行
$ bash a.sh
123
自定义变量
#!/bin/bash
# 变量名=变量值 等号左右不能有空格!
page_size=1
page_num=2
# 上面的变量值默认是字符串, 不能进行加减操作
# total=page_size*page_num ❌
# 声明变量为整型
let total=page_size*page_num # 方法1, 使用 let
declare -i total=page_size*page_num # 方法二, 使用 -i 参数
# 变量值是命令
_ls=ls
# 变量值是命令执行结果
file_list=${ls -a}
环境变量
page_size=1
page_num=2
let total=page_size*page_num
# 使用 export 将变量变成环境变量
export total
# 也使用 -x 参数声明为环境变量
declare -x total
系统环境变量
变量名 | 含义 | 常见操作 |
---|---|---|
$0 | 当前 shell 名称/脚本名声 | $1 , $2 等可以获取到传入参数 |
$# | 传入脚本的参数数量 | if[ $# -gt 1 ] |
$* | 传入脚本的所有参数 | |
$? | 上条命令执行的状态码 | if[ $? -eq 0 ] |
$PS1 | 命令提示符 | export PS1="\u@\h \w> " |
$USER | 用户名 | |
$HOME | 用户主文件夹 | cd ~ |
$PATH | 全局命令的搜索路径 | PATH=$PATH:[新增路径] |
PS1="\u@\h \w> "
含义: \u
是用户名; \h
是主机名; \w
是当前目录。
linux 中 $PATH
是使用 分号 :
作为分隔符。
配置文件加载顺序和优先级
系统环境变量是定义在配置文件中的, 配置文件有多个, 不同情况加载的配置文件是不一样的。
shell 可以分为 login shell 和 non-login shell。
login shell 是指用户登录系统时开启的 shell。在 login shell 中, 系统会执行 /etc/profile
文件来初始化环境变量和路径等系统范围的设置, 并且还会读取当前用户的 ~/.bash_profile
或 ~/.profile
文件来设置个人化的环境变量和命令别名等。
non-login shell 是指在登录后通过运行一个新的 shell 脚本而打开的 shell。这些 shell 不会执行系统范围的设置, 而是直接读取当前用户的 ~/.bashrc
文件来加载个人化的环境变量、命令别名和函数等。
如果修改了配置文件, 不会立即生效, 需要我们重启终端或者执行 source 配置文件
命令
bashrc 只作用在 bash 中。 ~/.bashrc
是用户级别的, /etc/bashrc
是系统级别的。默认情况下用户级的配置文件优先级是高于系统级别的。为什么叫做默认情况下呢? 因为你可以通过 source 配置文件
命令修改这个优先级。具体看示例:
linhieng@LINHIENG:~$ vim ~/.bashrc # 在内容末尾添加 export PS1="\u \w > " 环境变量
linhieng@LINHIENG:~$ source ~/.bashrc # 执行配置文件, 让其生效
# 此时可以看到效果:
linhieng ~ > sudo vim /etc/profile # 同样, 在内容末尾添加 export PS1="> " 系统环境变量
[sudo] password for linhieng:
linhieng ~ > source /etc/profile # 让其生效
# 此时可以看到效果, 这说明优先级最高的是最近执行的 source 配置文件, 其次才是默认情况下的优先级
> bash # 在当前 shell 下新建一个子 bash, 它是属于 non-login shell, 所以它会读取 ~/.bashrc, 可以理解为它执行了 source ~/.bashrc
# 故此时优先级又回到了 "默认情况", 即用户级别优先级高于系统级别优先级
linhieng ~ > exit
exit
# 退出时, 并不会再次执行用户级配置
> source ~/.bashrc
linhieng ~ > source /etc/profile # 可以通过 source 随意切换优先级
>
运算符和引用
类型 | 符号 | ||
---|---|---|---|
算术运算符 | + - * / % ` | & | ` |
逻辑运算符 | ` | && !` | |
比较运算符 | == != < > | ||
引号 | "" '' `` | ||
圆括号 | (()) () | ||
命令连接 | ` | && ;` | |
后台运行 | & |
引号和圆括号使用示例
#!/bin/bash
a=0
# 双引号, 部分引用
foo="${a}123"
echo $foo # 0123
# 单引号, 完全引用, 原样输出
foo='${a}123'
echo $foo # ${a}123
# 反引号, 内容会被认为是可执行的命令
foo=`echo 'hello, world!'`
echo $foo # hello, world!
# 双括号, 算术运算
foo=$((1+2))
echo $foo # 3
# 单括号, 效果同反引号
foo=$(echo 'hi~')
echo $foo # hi~
执行效果如下:
$ bash a.sh
0123
${a}123
hello, world!
3
hi~
对于命令连接, 特别注意一下, 返回码为 0 代表没有错误(和 C 语言一样)。
cmd1 || cmd2
: 若 cmd1 执行完并且返回码非 0, 则继续执行 cmd2cmd1 && cmd2
: 若 cmd1 执行完并且返回码 0, 则继续执行 cmd2cmd1 ; cmd2
: 先执行 cmd1 再执行 cmd2。 两者不会相互影响
后台运行的 &
一般搭配 nohup
命令一起使用。只使用 &
, 则当当前 shell 关闭后, 程序依旧会关闭。但若搭配 nohup
则关闭当前 shell 后, 程序也可继续执行。比如 nohup node index.js &
。
小 Tip: 所谓关闭 shell, 指的是正常关闭, 而不是异常关闭。比如通过 ssh 连接到云服务器后, 若直接关闭当前 shell 窗口, 这种情况属于异常关闭,
nohup
是会失效的。正确关闭的方法是执行exit
命令
管道
管道与管道符 |
, 作用是将前一个命令的结果传递给后面的命令。
语法: cmd1 | cmd2
要求: 管道右侧的命令必须能接受标准输入才行, 比如 grep
, wc
等命令可以直接使用管道符, 而 ls
等则不能直接使用, 需要借助 xargs
进行预处理
注意: 管道命令仅仅处理 stdout
, 对于 stderr
会予以忽略。不过, 可以使用 set -o pipefail
设置 shell
遇到管道错误退出
下面给出 xargs
的简单示例:
$ cd /usr
$ find . -maxdepth 2 -name "*.sh" # 文件数量太多, 使用 maxdepth 来限制搜索目录深度
./bin/gettext.sh
./bin/rescan-scsi-bus.sh
$ find . -maxdepth 2 -name "*.sh" | ls -l # ls 无法接受默认输入, 所以下面输出的结果和 find 的结果无关
total 0
drwxr-xr-x 1 root root 4096 Sep 12 2022 bin
drwxr-xr-x 1 root root 4096 Apr 18 2022 games
drwxr-xr-x 1 root root 4096 Aug 8 2022 include
drwxr-xr-x 1 root root 4096 Sep 12 2022 lib
drwxr-xr-x 1 root root 4096 Aug 8 2022 lib32
drwxr-xr-x 1 root root 4096 Aug 8 2022 lib64
drwxr-xr-x 1 root root 4096 Aug 8 2022 libexec
drwxr-xr-x 1 root root 4096 Aug 8 2022 libx32
drwxr-xr-x 1 root root 4096 Aug 8 2022 local
drwxr-xr-x 1 root root 4096 Sep 12 2022 sbin
drwxr-xr-x 1 root root 4096 Aug 8 2022 share
drwxr-xr-x 1 root root 4096 Aug 8 2022 src
$ find . -maxdepth 2 -name "*.sh" | xargs ls -l # 使用 xargs 对内容进行预处理, 此时 ls 的输入参数就是 find 的输出了
-rwxr-xr-x 1 root root 5188 Mar 25 2022 ./bin/gettext.sh
-rwxr-xr-x 1 root root 38762 Mar 25 2022 ./bin/rescan-scsi-bus.sh
重定向
重定向输入符号:
>
: 覆盖写入文件>>
: 追加写入文件2>
: 错误输出写入文件, 2 表示标准错误,>
相当于省略了前面的 1(标准输出)&>
: 正确和错误输出统一写入到文件中
示例:
# 测试 >
$ echo hello > a.txt
$ cat a.txt
hello
$ echo hi > a.txt
$ cat a.txt
hi
# 测试 >>
$ echo hello >> a.txt
$ cat a.txt
hi
hello
# 测试 2>>
linhieng@LINHIENG:~$ abcde >> a.txt
Command 'abcde' not found, but can be installed with:
sudo apt install abcde
linhieng@LINHIENG:~$ abcde 2>> a.txt
linhieng@LINHIENG:~$ cat a.txt
hi
hello
Command 'abcde' not found, but can be installed with:
sudo apt install abcde
# 测试 &>
$ abcde &> a.txt
$ cat a.txt
Command 'abcde' not found, but can be installed with:
sudo apt install abcde
输入重定向符号:
<
: 重定向输入, 后面可以接一个文件<<
: 用于在命令行界面中直接输入多行文本作为命令的输入,<<
后面需要接一个终止字符串
示例:
# 测试 <
# wc 输出结果是 " 行数 单词数 字节数 "
$ wc < a.txt
2 13 77
# 测试 <<
$ wc << end
> hello
> how are you
> end.
> end
3 5 23
判断命令
shell 中提供了 test
, [
和 [[
作为判断符号, 他们均可用于判断整数, 字符串和文件。test
和 [
功能单一, 只能使用自己支持的标志位, <
, >
和 =
只能用来比较字符串, 不能比较整数。而 [[
功能更丰富, 整型可以使用 <
, >
和 =
来比较, 并且字符串中支持 =~
正则比较。具体语法如下:
test condition
[ condition ]
注意中括号内的空格是有意义的, 不能省略[[ condition ]]
同上, 注意空格;
test
示例:
a.sh
文件内容:
#!/bin/bash
n1=1
n2=1
n3=3
# 整数测试
test $n1 -eq $n2 && echo '-eq 表示是否相等(equal)'
test $n1 -lt $n3 && echo '-lt 表示小于(less than)'
test $n3 -gt $n2 && echo '-gt 表示大于(greater than)'
# 字符串测试, 为避免字符串有空格其他分隔符, 应使用双引号包裹字符串
test -z "$str_a" && echo 'zero length. 若字符串为空则放回真(0), 否则返回假(非0)'
str_b=' '
test -n "$str_b" && echo 'non-zero length. 和 -z 相反'
str_a=' '
test "$str_a" = "$str_b" && echo '注意, 单独一个 = 是正确的'
# 文件测试
test -e a.sh && echo 'exits. 判断文件/目录是否存在'
test -f a.sh && echo 'file. 判断是否是普通文件'
在 bash 终端下运行
$ bash a.sh
-eq 表示是否相等(equal)
-lt 表示小于(less than)
-gt 表示大于(greater than)
zero length. 若字符串为空则放回真(0), 否则返回假(非0)
non-zero length. 和 -z 相反
注意, 单独一个 = 是正确的
exits. 判断文件/目录是否存在
file. 判断是否是普通文件
[
和 test
差不多, 就不给出示例了。
[[
示例:
#!/bin/bash
n1=1
n2=2
# 整数测试
[[ $n1 = $n2 ]] ; echo $? # = 或者 == 都是可以的
[[ $n1 < $n2 ]] ; echo $?
[[ $n1 > $n2 ]] ; echo $?
str1="hello world 123"
str2='hello world'
pattern="[0-9]+"
[[ "$str1" =~ $pattern ]] ; echo $? # 正则是字符串, 则需要使用变量形式
[[ "$str2" =~ [0-9]+ ]] ; echo $? # 不使用变量, 可直接写正则
分支语句
if else 语法1
if [ condition1 ] ; then
# execute commands if condition1 is true
elif [ condition2 ] ; then
# execute commands if condition2 is true
else
# execute commands if all above conditions are false
fi
案例:
#!/bin/bash
a=$1
b=$2
if [[ $a > $b ]]
then
echo "$a > $b"
elif [[ $a < $b ]]
then
echo "$a < $b"
else
echo "$a == $b"
fi
运行:
$ bash a.sh 1 2
1 < 2
$ bash a.sh 1 1
1 == 1
$ bash a.sh 2 1
2 > 1
case 语法:
case $variable in:
"pattern1")
# execute commands for pattern1
;;
"pattern2" | "pattern3")
# execute commands for pattern2 or pattern3
;;
*)
# execute commands if no pattern matches
;;
esac
案例:
#!/bin/bash
read -p "Input a number between 1 or 2: " num
case $num in
1)
echo "You entered one"
;;
2)
echo "You entered two"
;;
*)
echo "Invalid input"
;;
esac
运行:
$ bash a.sh
Input a number between 1 or 2: 1
You entered one
$ bash a.sh
Input a number between 1 or 2: 2
You entered two
$ bash a.sh
Input a number between 1 or 2: a
Invalid input
循环语句
while 循环
语法:
while [ condition ]
do
# command
done
示例:
#!/bin/bash
count=1
while [[ $count < 5 ]]
do
echo "Count is $count"
count=$((count + 1))
done
运行:
$ bash a.sh
Count is 1
Count is 2
Count is 3
Count is 4
until 循环
语法:
until [ condition ]
do
# command
done
示例:
#!/bin/bash
count=1
until [[ $count > 5 ]]
do
echo "Count is $count"
count=$((count + 1))
done
运行:
$ bash a.sh
Count is 1
Count is 2
Count is 3
Count is 4
Count is 5
for 循环
语法:
for variable in list
do
# command
done
示例:
#!/bin/bash
for i in 1 2 3 4 5
do
echo "Number: $i"
done
运行:
$ bash a.sh
Number: 1
Number: 2
Number: 3
Number: 4
Number: 5
函数
语法1:
function function_name() {
parameter1=$1
parameter2=$2
# ...
# commands
}
# 调用
function_name parameter1 parameter2 ...
语法2:
function_name() {
parameter1=$1
parameter2=$2
# ...
# commands
}
# 调用
function_name parameter1 parameter2 ...
示例1:
#!/bin/bash
printName() {
if [[ $# < 2 ]] ; then
echo 'illegal parameter.'
exit 1 # 这不是返回值, 而是直接结束脚本的运行
fi
echo "firstName: $1"
echo "lastName: $2"
}
printName jacky chen
printName $1 $2
运行:
$ bash a.sh JK lin
firstName: jacky
lastName: chen
firstName: JK
lastName: lin
$ bash a.sh JK
firstName: jacky
lastName: chen
illegal parameter.
示例2:
#!/bin/bash
f() {
echo 666
return $1
}
f 255
echo "执行状态: $?" # 255
f 256
echo "执行状态: $?" # 256 溢出, 结果为 0
r=$(f 256)
echo "执行结果: $r"
f
echo "执行状态: $?"
运行:
$ bash a.sh
666
执行状态: 255
666
执行状态: 0
执行结果: 666
示例3:
#!/bin/bash
f() {
test -n "$1"
}
f $1
echo "执行状态: $?"
运行:
$ bash a.sh
执行状态: 1
$ bash a.sh 1
执行状态: 0
示例4:
#!/bin/bash
f1() {
a=66
}
f1
echo "a: $a" # 66, 被函数内的变量污染了
f2() {
local b=77
}
f2
echo "b: $b" # 无输出, 没有被污染
f3() {
c=77
unset c
}
f3
echo "c: $c" # 无输出, 没有被污染
运行:
$ bash a.sh
a: 66
b:
c:
注意:
- shell 自上而下执行, 函数必须在使用前定义
- 函数获取形参是使用
$1
$2
... 这种方法, 并且不需要再括号内声明 - 函数内 return 仅仅表示函数执行状态, 不代表函数执行结果。返回值是一个 8 位的状态码, 它的最大值是 255。
- 想要获取函数返回的结果, 可通过
echo
,printf
, 不要通过 return 的方式 - 如果没有 return, 函数返回的状态函数内最后一条语句的执行状态
- 为了函数内定义的变量不污染全局, 最好在函数内使用 local 去定义, 或者在函数退出之前使用 unset 去处理一下
模块化
模块化的原理是在当前 shell 内执行函数文件, 具体是使用 source 函数库路径
来实现模块化。
案例:
math.sh
文件:
#!/bin/bash
# add function
# @return platForm
add() {
declare -i res=$1+$2
echo $res
}
a.sh
文件:
#!/bin/bash
source './math.sh'
total=`add $1 $2`
echo $total
运行:
$ bash a.sh 66 666
732
shell 常用命令
更详细的使用, 可执行 命令 --help
进行查看。
命令 | 说明 | |
---|---|---|
grep | global regular expression print, 文本搜索工具 | |
sort | 文本排序。 指定分隔符后以第三列进行排序: sort -t ' ' -k 3 | |
wc | 统计出现的行数、单词数、字符数 | |
head | 查看开头若干行, 默认 10 行 | |
tail | 查看末尾若干行。在实时跟踪日志时特别有用: tail -f -n 10 xx.log | |
cut | 对数据行的内容进行处理。以空格为分隔查看第三个 `cat a | cut -d ' ' -f 3` |
find | 文件和目录查找 | |
xargs | 参数处理 | |
which | 查找命令路径 |
Shell 执行过程和原理
执行
shell 脚本一般以 .sh 结尾, 也可以没有, 这是一个约定; 第一行需要指定用什么命令解释器来执行。
脚本以 #!
开头时, 操作系统(内核)会自动查找该行后面指定的解释器, 并使用它来运行该脚本。
#! /bin/bash
#! /usr/bin/env bash
脚本可以有三种启动方式:
# 文件名运行, 注意前缀 ./ 不能省略, 不然会识别为命令, 而不是文件
./xx.sh
# 解释器运行, 这里可以省略 ./
bash xx.sh
# source 运行
source xx.sh
执行过程
- 字符解析
- 识别换行符、 分号(
;
) 做行的分割 - 识别命令连接符(
||
&&
|
) 做命令的分割 - 识别空格、tab符, 做命令和参数的分割
-
shell 展开, 例如
{1..3}
解析为1 2 3
-
重定向, 将
stdin
,stdout
,stderr
的文件描述符进行指向变更 -
执行命令
builtin
直接执行- 非
builtin
使用$PATH
查找, 然后启动子进程执行
builtin
指的是内置命令
- 收集状态并返回
shell 的架构图类似于一个流水线, 在里面进行输入分析和解析。bash 会以一特殊字符作为分隔符, 将文本进行分段解折。在 bash 脚本中是以回车或者分号作为一行命今结束的标志, 这就是第一层级的解折, 将大段的命今行进行分段符号拓展(使用各种方法, 比如大括号 {}
、波浪符 ~
、变量和参数的展开/替换、文件名展开), 并最终执行命今(通过 shell 内置命今或外部命今)。
Shell 展开
大括号展开 Brace Expansion
一般由三部分构成: 前缀, 一对大括号和后缀。大括号内可以是逗号分割的字符串序列, 也可以是序列表达式 {x..y[..increase]}
# 字符串序列
a{b,c,d}e => abe ace ade
# 表达式序列, 数字可以使用 increase 调整增量, 但字母不行
{1..5} => 1 2 3 4 5
{1..5..2} => 1 3 5
{a..e} => a b c d e
波浪号展开 Tilde Expansion
# 当前用户主目录
~ => $HOME
# 指定用户主目录
~lin/foo => 用户 lin 的 $HOME/foo
# 当前工作目录
~+/foo => $PWD/foo
# 上一个工作目录
~-/foo => ${$OLDPWD-'~-'}/foo
参数展开 Shell Parameter Expansion
-
间接参数扩展
${!parameter}
, 类似于指针的指针#!/bin/bash var='hello' parameter='var' echo ${parameter} # var echo ${!parameter} # hello
-
参数长度
${#parameter}
#!/bin/bash var='hello' echo ${#var} # 5
-
空参数处理
${a:-word}
: 若 a 为空, 则结果为 word${b:=word}
: 若 b 为空, 则结果为 word, 并且 b='word'${c:?word}
: 若 c 为空, 则报错并退出${d:+word}
: 若 d 不为空, 则结果为 word
案例:
#!/bin/bash b=${a:-word} echo "a: $a b: $b" # a: b: word d=${c:=word} echo "c: $c d: $d" # c: word d: word g='haha' h=${g:+word} echo "g: $g h: $h" # g: haha h: word f=${e:?word} # a.sh: line 13: e: word echo "this isn't echo" # 不会输出, 因为已经报错退出了
运行:
$ bash a.sh a: b: word c: word d: word g: haha h: word a.sh: line 17: e: word
-
参数切片
${a:offset:length}
: 从第 offset 个字符(不包含offset)开始, 截取 length 个${a:offset}
: 没写 length, 默认截取到最后
#!/bin/bash a='abcdefg' echo ${a:2} # cdefg echo ${a:2:3} # cde
-
参数部分删除
${parameter%patter}
最小程度从后面截取 patter${parameter%%patter}
最大程度从后面截取 patter${parameter#patter}
最小程度从前面截取 patter${parameter##patter}
最大程度从前面截取 patter
所谓的最大程度和最小程度, 只有和正则相结合时才有效果, 如果 patter 只是一个字符串, 那么最大程度和最小程度都是一样的。
#!/bin/bash a='111' echo ${a%1} # 11 echo ${a%%1} # 11 echo ${a#1} # 11 echo ${a##1} # 11 b='hello-world-helloo' echo ${b%-*} # hello-world echo ${b%%-*} # hello echo ${b#*-} # world-helloo echo ${b##*-} # helloo
命令替换 Command Substitution
有两种形式, 也就是前面提到的 $(...)
和 ``
数学计算 Arithmetic Expansion
即前面提到的 $(())
文件名展开 Filename Expansion
() * ? []外壳文件名模式匹配
当有单词没有被引号包裹, 且其中出现了 *
, ?
, []
, {}
字符, 则 shell 会去按照正则匹配的方式查找文件名进行替换, 如果没找到则保持不变。
*
: 匹配任意数量的任意字符。?
: 匹配单个任意字符。[]
: 匹配一组指定范围内的任意字符。{}
: 扩展为逗号分隔的任意字符串列表。
#!/bin/bash
echo ?.*
echo *[.]*
echo {a,b}.txt
$ bash a.sh
a.js a.sh a.txt b.txt
a.js a.sh a.txt b.txt index.html math.sh package.json package-lock.json test.js
a.txt b.txt
调试和前端集成
这部分只做引子, 暂时不会给出具体案例
调试
- 打印输出。使用 echo, printf 命令
- 使用 set 命令。一般会在开头添加
set -uxe -o pipefail
配置。
set 命令用于设置和修改 shell 的选项和参数。
-u
参数作用是 "遇到不存在的变量就会报错, 并停止执行";-x
作用 "运行结果之前, 先输出执行的那一行命令";-e
作用 "只要发生错误, 就终止执行";-o pipefail
作用 "管道符连接的时, 只要一个子命令失败, 则整个管道命令就失败, 脚本会终止执行"
- 利用vscode debug 插件
- 安装插件(1必选3可选): Bash Debug 支持单步调试; 还有三个可选的插件: shellman 用于代码提示和自动补全, shellcheck 代码语法校验, shell-format 代码格式化。
- 编写 launch.json 配置文件
- 确保 bash 在 4.x 以上版本。可通过
bash --version
查看版本
前端集成
在前端中也可以使用 shell 命令做相关操作。
-
node 中通过
exec
或spawn
调用 shell 命令。这两个函数均在child_process
包中。exec
启动一个子 shell 进程执行传入的命令, 并且将执行结果保存在缓中区中, 并且缓中区是有大小限制(200KB)的, 执行完毕通过回调函数返回。spawn
默认不使用 shell, 而是直接启动子进程执行命令, 且会直接返回一个流对象, 支持写入或者读取流数据, 这个在大数据量交与的场景比较适合。
-
shell 脚本中调用 node 命令
#! /bin/bash node ./exec.js echo 'exec.js running'
-
借助 zx 等库进行 javascript, shell script 的融合
- 借助 shell 完成系统操作, 比如文件io, 内存, 盘系统状查询等
- 借助 nodejs 完成应用层能力, 比如网络io, 计算等
#!/usr/bin/env zx // 通过 $`command` 来调用 shell let files = await $`ls -a | grep node` console.log( chalk.blue(`files: ${files}.`)) await sleep(1000) await fetch('https://google.com') let answer = await question('do you want to create new dir ?') if (answer === 'y') { await $`mkdir temp_dir` }
课程总结
本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!