TGTGInsighttelegram intelligenceLIVE / telegram public index
← Welcome to the Black Parade
Welcome to the Black Parade avatar

TGINSIGHT POST

Post #43

@TheB1ackParade

Welcome to the Black Parade

Views162Post view count
PostedMar 803/08/2020, 09:02 AM
Post content

Post content

## Q1: 多次处理 stdout 比如我要先判断 stdout 里是否匹配某模式, 匹配则对 stdout 做进一步处理. 无脑一点, 我们可以把 stdout 写到临时文件里: tmpfile=$(mktemp) cmd | tee $tmpfile | inspect $pattern && process $tmpfile rm -f $tmpfile 本身也足够简单, 可读性高, 也好扩展, 也可以反复读, 适用于各种场景. 如果不满意有文件落地, 那可不太好办了, 非常不好办. 我们有三种可能的手段: 1. inspect 和 process 在同一进程, 形如 inspect $pat && process, 好处是我们可以通过变量来控制分支, 可以完成顺序式编程. 2. inspect 和 process 是父子进程关系, 形如 process > >(inspect $pat), process substitution 保证了父子进程关系, 此时我们可以通过 wait(1) 来完成跨进程同步. 3. inspect 和 process 是非父子关系的两个进程, 形如 inspect $pat | process, 我们只能让 inspect 过滤. 下面来演示一下, 例题简单一点, 检查 etcdctl 输出里是否有 ModRevision, 有的话把全体输出到终端. 思路一的做法: etcdctl get key -w fields | (while read line; do [[ "$line" =~ ModRevision ]] && found=1; content="$line"$'\n'"$content"; done && [ -n "$found" ] && echo "$content") 可以看到我们用 $found 来 toggle process. 思路二: etcdctl get /a -w fields | (content="$(cat -)"; echo "$content" > >(grep -q ModRevision); wait $!; [[ "$?" == 0 ]] && echo "$content" ) 里面使用了 $! 这一 bash4.4+ 才有的变量来获取 process substitution 的 pid, 然后 wait 直到 inspect 完成. 思路三和思路一的大部分代码相同, etcdctl get key -w fields | (while read line; do [[ "$line" =~ ModRevision ]] && found=1; content="$line"$'\n'"$content"; done && [ -n "$found" ] && echo "$content") | cat 要注意最后那个 cat 才是 process 进程, 中间一大串都是 inspect, 虽然代码和思路一相似, 但是这只是因为题目的 process 是打印到终端, 如果是写到文件那立刻就不同了. 如果是特殊一点的场景, 比如是匹配模式后抠字符, 那么多半会有合并的做法, 而实际上大部分场景我们都是面对格式很固定的数据. 比如如果模式和要抠的字符在同行, awk 的 filter + print columns 的这一套就非常合适. cmd | awk '/PAT/ {print $NF}' 如果是跨行, 但是模式我们很清楚, 比如要抠的字符在模式附近几行, gnu/grep + regex 就能很容易上手: cmd | grep $pat -A4 | grep -Po '\w{64}' 如果模式复杂, 因为 gnu/grep 的正则不能抠 capture group, 加上 pcre 的向前环视只能是固定字符, 在某些场景下可能 read 更简单: cmd | while read line; do read key _ value <<<$line; [[ "$key" == PAT ]] && echo $value; done ## Q2: stderr 打断进程 在一串 pipeline 中要对部分命令的 stderr 进行模式匹配, 匹配成功就退出进程. TTY=$(tty) cmd1 | (cmd2 2> >(grep $pat > $TTY) || kill -9 $$) | cmd3 这里的难点是, 1. 我不想把 cmd2 的 stderr dup 到 stdout 然后交给 cmd3 判断 2. 我依然想打印出 stderr 到 tty, 而在管道子进程中已经拿不到 tty 了, 只能通过环境变量 3. exit(1) 是没用的, 只有 kill(1) 能跨进程中断 不过上面的代码其实并不符合要求, 要求对 stderr 匹配模式才退出, 代码是 exit code 非零就退出. 如果要做到题目要求, 那么 $! 捕获 process subsititution 的 pid 又派上用场了: TTY=$(tty) cmd1 | (cmd2 2> >(grep $pat > $TTY); wait $!; [[ $? != 0 ]] && kill -9 $$) | cmd3 ## Q3: 怎么做好 cleanup 比如在 Q1 里的无脑解法创建了一个临时文件, 怎么尽可能保证临时文件能在进程退出后被清理掉. 我们可以用一个装饰器脚本, 名字叫 ONTEMP: #!/bin/bash echo "Creating tempfile" tempfile=$(mktemp) echo "Executing command: $@" eval $@ echo "Removing tempfile" rm -f $tempfile 不过这还不够, 我们还能处理好信号等情况: function cleanup() { echo "Removing tempfile" rm -f $tempfile } trap cleanup EXIT 利用好 trap(1) 可以很好地应对各种情况, 不过对于 kill -9 就不行了... 那也没办法了. 最终的代码: #!/bin/bash echo "Creating tempfile" tempfile=$(mktemp) function cleanup() { echo "Removing tempfile" rm -f $tempfile } trap cleanup EXIT echo "Executing command: $@" eval $@ 使用: ONTEMP sleep 20 Ctrl-C 后能检查临时文件被删除. from https://gist.github.com/jschwinger23/a2450771c1592aae035e6d912d1bf7f5#file-bash-practices-2020-03-md