ZB-016-命令行详解01

什么是命令行

什么是 Kernel

负责和计算机硬件交互的,负责CPU调度和进程相关的调度/IO之类的操作。操作系统最底层

我们如何操作内核呢?

Linux 通过命令行里(终端)来与内核进行交互

通过命令行操作内核的过程 叫做 Shell

  • Kernel(内核) 与 Shell(壳)
  • 广义的命令行一切通过字符终端控制计算机的方式
    • Windows cmd/PowerShell/Git bash
    • UNIX/Linux系列 sh/zsh/Terminal etc

为什么需要 Shell

  • 因为你不能直接和操作系统内核进行交互

为什么需要命令行

  • 因为你不得不用
    • 几乎所有的服务器都运行在 linux 上
  • 将工作自动化
    • 自动化是一切生产力的来源(提高生产力)
    • 你一台机子需要 点点点操作,10000台呢?
  • 相比GUI,命令行更容易开发和维护
    • bug少
  • 远程连接命令行占用资源远远低于GUI
  • 命令行上的开发者工具更丰富

命令行的历史和流派

  • UNIX家族
    • POSIX标准
    • macOS
    • Linux
    • Windows Subsystem for Linux
  • Windows
    • 奇葩

Windows 和 UNIX 系统的区别

  • UNIX遵循POSIX标准,只要遵循POSIX标准就可以以同样的方式工作
    • 默认命令行交互
    • GUI只是个扩展
    • 只有一个盘符
  • Windows
    • GUI主要
    • 命令行是扩展的(导致命令行在windows上是个残废)
    • 多个盘符 cdef

如何安装 windows 的 linux 子系统

  • 搜索 Windows Subsystem for Linux

命令的全部要素

  • 可执⾏程序(Executable)
  • 参数
  • 环境变量(Environment variable)
  • ⼯作⽬录(Working directory)
    • 启动命令的路径
    • 相对路径都是相对于这个路径
    • 在Java中的应⽤

以上四个要素相同,就可以完全地“重现”⼀个命令

你碰到的各种各样古怪的问题,原因⼀定是上述四个要素之⼀

可执行程序和参数

1
2
3
ls -al 
# ls就是可执行程序
# -al就是参数

工作目录

1
pwd 就是当前的目录

环境变量

  • 进程(Process)
    • 进程是计算机程序运⾏的最⼩单位
    • 独占⾃⼰的内存空间和⽂件资源
  • 每个进程都和⼀组变量相绑定

    • 传递不同的环境变量可以让程序表现出不同的⾏为

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      # 当前终端里
      export aa=1

      # 当前终端进入node环境
      node
      # 获取环境变量
      > process.env.aa
      # 输出 '1'
      '1'

      # 此时新开一个终端
      node
      > process.env.aa
      undefined
    • CLASSPATH/GOPATH

  • 在进程的fork过程中,环境变量可以被完全继承(fork 就是生进程的过程)
  • 所有的操作系统/编程语⾔都⽀持环境变量
  • 局部和全局的环境变量

进程是那里来的

  • 进程是它爹生的,最开始系统启动的时候只有一个进程,在 Linux 中叫做 init进程,它生了一堆孩子 桌面/终端
  • 在终端里输入 node 就导致 终端产生一个孩子 node进程
  • 这就形成了一棵树——进程树
  • 环境变量会一级一级的向下继承
  • 意思是 父进程里有一个环境变量 a=1 那么它的子进程也有这个 a=1

环境变量实战

  • 通过export/set设置环境变量,通过echo读取环境变量
  • 从Java/Go/Python/Node.js中读取环境变量
  • 向Docker容器传递环境变量

    1
    2
    # -e 代表环境变量
    docker run -it -e AAA=1 ubuntu
  • 快速传递⼀个环境变量

  • 使⽤环境变量控制程序的⾏为

java中读取环境变量

1
2
3
4
5
public class Main{
public static void main(String[] args){
System.out.println(System.getenv("aa"));
}
}

局部和全局的环境变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 只在当前终端里有效,新开 终端无效
export a = 1

# 只对当前的命令有效
a=1 node aa.js

# 全局的环境变量
# 编辑你的 ~/.bash_profile
vi ~/.bash_profile
# 输入如下内容
export aaa = 123456

# 然后新开终端输入
echo $aaa

# 你可能遇到,为什么刚改了现在没生效
# 你要 source 一下
source ~/.bash_profile


# 注意!!! bash_profile只对 bash有效
# 注意!!! bash_profile只对 bash有效
# 注意!!! bash_profile只对 bash有效

取决于你用的终端是 bash 还是 zsh

可执⾏程序

  • Windows:exe/bat/com
  • UNIX/Linux:x权限 - 可执⾏权限
  • 去哪⾥找程序?

    • Windows:Path环境变量 + 当前⽬录
    • UNIX/Linux:PATH环境变量
      1
      2
      3
      echo $PATH

      /Users/huangjiaxi/flutter/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/huangjiaxi/Desktop/soft/apache-maven-3.6.1/bin
  • 在脚本的第⼀⾏指定解释器(shebang)

  • 别名(alias)

设置环境变量

1
2
3
4
5
6
7
# windows
set ABC=123
echo %ABC%

# linux
export ABC=123
echo $ABC

初学命令行的困扰

1
2
3
4
5
6
7
# 执行命令时,windows会在 PATH 和 当前目录里找,


# 而 linux 只会在 PATH 里找
# 假设当前目录有 main 脚本
# 直接 main 不会执行 因为 linux 只会在 PATH里找
./main 才执行

如何执行一个文本文件

echo.sh 内容如下

1
2
echo 123
echo 456

分配执行权限

1
2
3
4
chmod 777 echo.sh

# 执行你的脚本
./echo.sh

写一个 node脚本

echo.sh

1
2
console.log(123);
console.log(456);

但是操作系统不认识 node!!!

教操作系统认识node

echo.sh

1
2
3
#!/usr/bin/env node
console.log(123);
console.log(456);

shell的约定

1
2
3
4
5
6
7
8
shell中 “#” 代表注释

首行 代表指定当前的文件由谁来解释和执行
# 从当前的上下文环境中查找 node 命令
#!/usr/bin/env node

为什么不推荐写绝对路径,因为你不知道别人的node是不是安装在这个路径
#!/usr/local/bin/node

参数

  • UNIX参数的约定——然鹅Java并不⻦这个约定

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ‘-’ 约定
    ls -alth
    # 约定,以一个 ‘-’ 开头的可以 -a -l -t -h 也可以合并为 -alth
    # 意思就是 '-' 后面跟一个字符

    '--' 约定 后面跟单词
    ls -a <==> ls --all
    git push -f <==> git push --force


    但是 java 不遵守这个
    java -version
  • 参数中包含空格或者字符串

    • 单引号
    • 双引号
  • 参数的展开

坑人的 单双引号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 当前环境里没有 A 变量,运行正常
javap Main$A.class

# 此时
export A=123
javap Main$A.class 报错了
实际执行的是 javap Main123.class

# 默认情况下 “$” 是把命令展开的符号

# 通过单引号来包裹它,运行正常,除了可以让多个空格分开的字符串连接在一起,还可以告诉命令行 里面的内容全不要替换
javap 'Main$A.class'

# 换个 "双引号" 试试
javap "Main$A.class"

双引号会把 里面的变量展开 ==> javap Main$123.class

# 想给程序传递 单引号怎么办?
# 包双引号
./main "'I am a boy'"
# 转义
./main \'I am a boy\'

扩展的参数

1
2
3
4
5
6
7
8
ls *.java
此时会把所有 .java扩展名的文件罗列出来

# 如何想不被扩展原样执行呢?

ls '*.java'

# * 代表通配符 默认会展开,如果你不想它自动扩展 就包含 单引号