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 开展

  1. 1971 年, Ken Thompson 在贝尔实验室为 UNIX 开发了榜首个 shell, 称为 V6 shell。
  2. 1979 年, Stephen Bourne 在贝尔实验室在 V7 UNIX 发布了 Bourne shell , 即 sh。
  3. 1989 年, 开源安排 GNU 为了取代 Bourne shell 发布了榜首版 Bourne-Again shell,即 Bash

bash 是 sh 的超集, 可履行大部分 sh 脚本, 并集成了 Korn shell 和 C shell 的功用。

Shell 构成

shell 提供了与内核和设备交互的办法(接口), 还集成软件开发中通用的规划方式(如管道和过滤器), 具备操控流程、循环、变量和指令查找的机制。

shell 是一门指令解说器, 一起也是一门编程言语。

简单认识 Shell | 青训营笔记

Shell 基础语法

变量

类型 效果域 声明办法
自界说变量 当时 shell =
环境变量 当时 shell 以及子 shell exportdeclare -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

简单认识 Shell | 青训营笔记

自界说变量

#!/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 装备文件 指令

简单认识 Shell | 青训营笔记

bashrc 只效果在 bash 中。 ~/.bashrc 是用户等级的, /etc/bashrc 是体系等级的。默许状况下用户级的装备文件优先级是高于体系等级的。为什么叫做默许状况下呢? 因为你能够经过 source 装备文件 指令修正这个优先级。具体看示例:

简单认识 Shell | 青训营笔记

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, 则继续履行 cmd2
  • cmd1 && cmd2: 若 cmd1 履行完而且回来码 0, 则继续履行 cmd2
  • cmd1 ; 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

简单认识 Shell | 青训营笔记

重定向

简单认识 Shell | 青训营笔记

重定向输入符号:

  • >: 掩盖写入文件
  • >>: 追加写入文件
  • 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

履行过程

  1. 字符解析
  • 辨认换行符、 分号(;) 做行的切割
  • 辨认指令连接符( || && |) 做指令的切割
  • 辨认空格、tab符, 做指令和参数的切割
  1. shell 打开, 例如 {1..3} 解析为 1 2 3

  2. 重定向, 将 stdin, stdout, stderr 的文件描述符进行指向变更

  3. 履行指令

  • builtin 直接履行
  • builtin 运用 $PATH 查找, 然后发动子进程履行

builtin 指的是内置指令

  1. 收集状况并回来

shell 的架构图类似于一个流水线, 在里面进行输入分析和解析。bash 会以一特殊字符作为分隔符, 将文本进行分段解折。在 bash 脚本中是以回车或许分号作为一行命今完毕的标志, 这便是榜首层级的解折, 将大段的命今行进行分段符号拓宽(运用各种办法, 比方大括号 {}、波浪符 ~、变量和参数的打开/替换、文件名打开), 并终究履行命今(经过 shell 内置命今或外部命今)。

简单认识 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

调试和前端集成

这部分只做引子, 暂时不会给出具体事例

调试

  1. 打印输出。运用 echo, printf 指令
  2. 运用 set 指令。一般会在最初增加 set -uxe -o pipefail 装备。

set 指令用于设置和修正 shell 的选项和参数。-u 参数效果是 “遇到不存在的变量就会报错, 并中止履行”; -x 效果 “运转成果之前, 先输出履行的那一行指令”; -e 效果 “只要发生过错, 就中止履行”; -o pipefail 效果 “管道符连接的时, 只要一个子指令失利, 则整个管道指令就失利, 脚本会中止履行”

  1. 利用vscode debug 插件
  2. 安装插件(1必选3可选): Bash Debug 支撑单步调试; 还有三个可选的插件: shellman 用于代码提示和主动补全, shellcheck 代码语法校验, shell-format 代码格局化。
  3. 编写 launch.json 装备文件
  4. 确保 bash 在 4.x 以上版本。可经过 bash --version 检查版本

前端集成

在前端中也能够运用 shell 指令做相关操作。

  • node 中经过 execspawn 调用 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`
    }
    

课程总结

简单认识 Shell | 青训营笔记