命令行

作者: bowen_wu | 来源:发表于2022-08-11 10:05 被阅读0次

    概述

    • kernel => 内核 => 负责和硬件交互
    • Shell => 壳 => 和 kernel 交互 => 广义的 Shell 包含一切通过字符终端控制计算机的方式
      • Windows => cmd/PowerShell/Git bash
      • UNIX/Linux => sh/zsh/Terminal

    本质

    • 命令行/终端的本质是一个进程
    • 在服务器上 cat /etc/passwd => root:x:0:0:root:/root:/bin/bash => 最后的 /bin/bash 就是登陆了之后运行哪个程序
    • 读取你的输入,并且做出反应
      • 内置命令,直接执行
      • 查找 $PATH 中对应可执行程序并将参数传递给它
      • 如果是 exit,终止自己
    • 大多数情况下,执行一个命令 == fork 一个新的进程
      1. source => 在当前 shell 中执行
      2. exec => 将 fork 的进程替换为参数的进程 => exec echo "123" => 暴力的将当前进程替换为 echo 进程

    命令的四要素

    • 如果四要素完全相同,那么命令一定可以重现
    • 如果命令出现诡异问题,那么一定是四要素之一发生了改变
    • 四要素
      1. Executable => 可执行程序
      2. Arguments => 参数
      3. Working Directory => 工作路径
      4. Environment Variable => 环境变量

    Environment Variable

    • 和进程相绑定的一组键值对
    • 所有的操作系统都支持 => Windows 上不区分大小写
    • 所有的编程语言都提供原生支持
    • 派生的子进程都会继承父进程所有的环境变量 => 不能逆向传递
    • Java 命令会读取 CLASSPATH 环境变量,作用相当于 -cp
    • 快速传递一个环境变量 => AAA=2 source 1.sh => 1.sh(echo $AAA)

    Executable

    • 可执行程序
      • Windows => exe/bat/com
      • UNIX => +x
    • Shell 如何找到可执行程序
      1. 内置命令
      2. alias
      3. $PATH
      4. Windows 会在当前目录里找

    分类

    1. 二进制可执行程序 => 符合对应操作系统的要求,能够直接执行,无需解释器 => file jdk-11.0.13.jdk/Contents/Home/bin/java => jdk-11.0.13.jdk/Contents/Home/bin/java: Mach-O 64-bit executable x86_64
    2. 脚本 => 在第一行指定解释器(shebang) => #!/usr/bin/env xxx(在当前环境变量中查找 xxx 程序) => 等同于 xxx <file>.sh

    Arguments

    • 参数完全由对应的可执行程序负责解析
    • Shell 操作参数
      1. 通配符展开(globbing)
      2. 变量展开 => Shell 中的变量($) & $() & ``
      3. 双引号会进行变量展开,单引号不会
    • Shell 获取参数 =>
      1. $0 => command
      2. $1 $2 $3 ...
      3. $@ 将所有参数装配成数组

    Working Directory

    • 启动命令的路径 => pwd => print working directory
    • 相对路径都是相对于这个路径
    • Java => new File(".") 代表相对当前目录的路径

    标准输入 & 标准输出 & 标准错误

    • 文件描述符 => file descriptor
    • 标准输入 stdin => fd=0 => 使用 < 或者 <<
      1. 使用 < 从文件读取数据输入给进程(不一定要是字符) => grep 4 < 1.txt(在 1.txt 中查找4)
      2. 使用 << 输入块数据
    • 标准输出 stdout => fd=1 => 使用 > 或者 >>
    • 标准错误 stderr => fd=2 => 使用 2> 或者 2>>
    • 标准输出和标准错误输出到同一文件 => <command> > output.txt 2>&1
    • UNIX 垃圾桶 => <command> > output.txt 2> /dev/null

    管道

    • 连接标准输出和标准输入,将上一个进程的标准输出作为下一个进程的标准输入
    • ls | grep class | wc -l => word count -line
    • cat 1.txt | grep 3 | xargs touch
    • echo ${PATH} | cut -d ':' -f 5
    • ps aux | grep java | awk '{print $2}' | xargs kill -9

    重定向

    • mvn -X compile | tee output.txt
    tee

    Shell 返回值

    • echo $? => 获取上一个进程的返回值
    • 每个进程都有返回值
    • 0 => success
    • 非0 => fail

    在 Java 程序中启动和执行命令

    • java 命令 => java <JVM 启动参数> <Main类> <传给 Main 类的参数>
    • Shell 传递的参数会进入 String[] args
    • 环境变量 => System.getenv("AAA"); => 只能读不能写
    • System Property(系统属性)只有 JVM 支持 => System.setProperty("BBB", "111"); System.getProperty("BBB"); => 可读可写
    • -Dfile.encoding=UTF-8 => 指定了系统中默认的编码
    JVM Default System Property

    Java 程序 fork 一个新进程

    import java.io.BufferedReader;
    import java.io.InputStream;
    
    public class ForkProcess {
        public static void main(String[] args) {
            // 获取当前项目的 git log 数据
            // executable git
            // arguments log --oneline
            // environment variable
            // working directory
            ProcessBuilder pb = new ProcessBuilder("git", "log", "--oneline");
            // pb.inheritIO().start();
            Process process = pb.start();
            InputStream inputStream = process.getInputStream();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, Charsets.UTF_8));
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                System.out.println(line);
            }
    
            pb.environment().put("AAA", "111");
            ProcessBuilder pb = new ProcessBuilder("ls", "*"); // Error。此时 * 不会进行通配符展开,通配符展开是 bash 的行为
            ProcessBuilder pb = new ProcessBuilder("bash", "-c", "ls *");
            ProcessBuilder pb = new ProcessBuilder("ls", "`pwd`/.."); // Error
            ProcessBuilder pb = new ProcessBuilder("ls", "|", "wc", "-l"); // Error
        }
    }
    

    输入输出重定向

    • 可能需要额外的线程
    • 如果 fork 的进程需要输入才能输出,那么 JVM 和 fork 的进程不能同时进行读写,需要创建一个 write 线程去负责输入,之后 JVM 才能正确读取 fork 进程的输出

    知识点

    1. CR/LF => CarriageReturn/LineFeed
    2. OS(操作系统) => 抹平了硬件的差异
    3. POSIX标准
    4. PS1 => terminal 的前缀提示符 Personal-Laptop-14 at ~ ❯ => echo $PS1
    5. bash doc
    6. bash -x <command> => 将命令内部的执行过程打印出来用于排查

    相关文章

      网友评论

          本文标题:命令行

          本文链接:https://www.haomeiwen.com/subject/hznywrtx.html