摘要
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
ver 2
$ echo ${PATH:?not_exist_or_empty}
# ...
$ echo $?
0
$ echo ${PYTHONPATH:?not_exist_or_empty}
zsh: PYTHONPATH: not_exist_or_empty
$ echo $?
1
原理
参考 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 | "" |
+--------------------+----------------------+-----------------+-----------------+
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
怎样写成一行呢?
while true do read line $line done # 大错特错!
sh报错: 没有do
可是我们明明写了do?
while true; do read line; $line; done; # 😀👍
原来do也类似一条命令! 要么以;结尾要么以\n结尾!
sh中大致有三种命令
- 外存二进制镜像/解释器及脚本
which ls
→/usr/bin/ls
- 内置命令
which cd
→cd: shell built-in command
- 保留关键字
which esac
→esac: 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
为什么要手动构造管道呢? 因为默认构造的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
注意 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 ;这就是操作系统、分布式系统的内容了!