• shell小技巧分享


    摘要

    posix shell 判断变量是否存在 使用所有shell sh bash zsh fish

    subprocess popen 交互式 shell interactive shell

    posix shell 判断变量是否存在 使用所有shell sh bash zsh fish

    代码

    例子中, PYTHONPATH不存在但是PATH存在.

    ver 1

    $ if [ -z ${PYTHONPATH:+exist_and_not_empty} ]; then return 1; else return 0; fi
    $ echo $?
    1
    $ if [ -z ${PATH:+exist_and_not_empty} ]; then return 1; else return 0; fi
    $ echo $?
    0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ver 2

    $ echo ${PATH:?not_exist_or_empty}
    # ...
    $ echo $?
    0
    $ echo ${PYTHONPATH:?not_exist_or_empty}
    zsh: PYTHONPATH: not_exist_or_empty
    $ echo $?
    1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    原理

    参考 stackoverflow.com/a/16753536

    +--------------------+----------------------+-----------------+-----------------+
    |   Expression       |       parameter      |     parameter   |    parameter    |
    |   in script:       |   Set and Not Null   |   Set But Null  |      Unset      |
    +--------------------+----------------------+-----------------+-----------------+
    | ${parameter:-word} | substitute parameter | substitute word | substitute word |
    | ${parameter-word}  | substitute parameter | substitute null | substitute word |
    | ${parameter:=word} | substitute parameter | assign word     | assign word     |
    | ${parameter=word}  | substitute parameter | substitute null | assign word     |
    | ${parameter:?word} | substitute parameter | error, exit     | error, exit     |
    | ${parameter?word}  | substitute parameter | substitute null | error, exit     |
    | ${parameter:+word} | substitute word      | substitute null | substitute null |
    | ${parameter+word}  | substitute word      | substitute word | substitute null |
    +--------------------+----------------------+-----------------+-----------------+
    
    
    +--------------------+----------------------+-----------------+-----------------+
    |   Expression       |  When FOO="world"    |  When FOO=""    |    unset FOO    |
    |   in script:       |  (Set and Not Null)  |  (Set But Null) |     (Unset)     |
    +--------------------+----------------------+-----------------+-----------------+
    | ${FOO:-hello}      | world                | hello           | hello           |
    | ${FOO-hello}       | world                | ""              | hello           |
    | ${FOO:=hello}      | world                | FOO=hello       | FOO=hello       |
    | ${FOO=hello}       | world                | ""              | FOO=hello       |
    | ${FOO:?hello}      | world                | error, exit     | error, exit     |
    | ${FOO?hello}       | world                | ""              | error, exit     |
    | ${FOO:+hello}      | hello                | ""              | ""              |
    | ${FOO+hello}       | hello                | hello           | ""              |
    +--------------------+----------------------+-----------------+-----------------+
    
    • 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
    • 26
    • 27
    • 28

    subprocess popen 交互式 shell interactive shell

    subprocess

    如果 p = Popen(…)

    p.communicate 无法交互式运行! 只要cmd执行完, 整个进程都结束了!

    p.stdin.write p.stdout.read p.wait 也无法交互式运行! wait导致整个进程都结束了!

    实际上 p.communicate = p.stdin.write p.stdout.read p.wait

    sh只能执行固定数量的shell命令. 但是, 终端的交互式shell是怎样实现的呢? 请看!

    $ while true
    > do read line
    > $line
    > done
    
    • 1
    • 2
    • 3
    • 4

    怎样写成一行呢?

    while true do read line $line done # 大错特错!
    
    • 1

    sh报错: 没有do

    可是我们明明写了do?

    while true; do read line; $line; done; # 😀👍
    
    • 1

    原来do也类似一条命令! 要么以;结尾要么以\n结尾!

    sh中大致有三种命令

    • 外存二进制镜像/解释器及脚本 which ls/usr/bin/ls
    • 内置命令 which cdcd: shell built-in command
    • 保留关键字 which esacesac: shell reserved word

    esac这种碳基生物整不出的活儿,竟然是为了防止保留字吃掉我们的变量名,他真的我哭死…😵

    do是一条命令 要么以;结尾要么以\n结尾 $line可以看作do的参数
    do是一条命令 要么以;结尾要么以\n结尾 $line可以看作do的参数
    do是一条命令 要么以;结尾要么以\n结尾 $line可以看作do的参数

    # leaf @ machine in ~ [16:33:52]
    $ mkfifo p q
    
    # leaf @ machine in ~ [16:34:02]
    $ sh -c 'while true; do read line && $line; done;' < p > q &
    [1] 299
    
    # leaf @ machine in ~ [16:34:09]
    $ cat q &
    [2] 308
    
    # leaf @ machine in ~ [16:34:17]
    $  echo 'pwd' > p
    /home/leaf
    
    # leaf @ machine in ~ [16:34:24]
    $ echo 'ls' > p
    bin
    mine
    p
    q
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    为什么要手动构造管道呢? 因为默认构造的subprocess.PIPE是unnamed pipe

    在这里插入图片描述

    # leaf @ machine in ~ [16:47:34]
    $ bash -c 'n=0; while (($n<=3)); do echo $n; n=$((n+1)); sleep 1; done;' | dd
    0
    1
    2
    3
    0+4 records in
    0+1 records out
    8 bytes copied, 4.00424 s, 0.0 kB/s
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    注意 4.00424 s 这说明匿名管道 写端完成 读端才可读 因此 subprocess.PIPE 无法实现交互式命令!

    ⚠ & 和 ; 都是shell的结束符, 二者至多出现一个.
    如果两个结束符之间为空, bash和zsh可能不报错, 但是/bin/sh会报错!

    sh -c 'while true; do read line && $line; done;' < p > q & ; ×

    sh -c 'while true; do read line && $line; done;' < p > q &

    怎样处理异步通信的问题呢?比方说 sleep 1 ;这就是操作系统、分布式系统的内容了!

  • 相关阅读:
    Java 调用 Cpp 代码简单示例
    SpringCloud Alibaba系列——17Seata AT模式源码分析
    用哈希简单封装unordered_map和unordered_set
    记录一次开机内存分析的全过程
    虚谷数据库-定时作业
    【Python Web】Flask框架(五)Bootstrap登录和后台管理案例
    【socket.js联合express】:搭建简约版聊天室
    HTML 简介
    一场技术破案的经过
    探花交友_第4章_圈子功能实现
  • 原文地址:https://blog.csdn.net/int_main_Roland/article/details/126300442