继上次的Spark-shell脚本源码分析,还剩下后面半段。由于上次涉及了不少shell的基本内容,因此就把trap和stty放在这篇来讲述。
function main() {
if $cygwin; then
# Workaround for issue involving JLine and Cygwin
# (see http://sourceforge.net/p/jline/bugs/40/).
# If you're using the Mintty terminal emulator in Cygwin, may need to set the
# "Backspace sends ^H" setting in "Keys" section of the Mintty options
# (see https://github.com/sbt/sbt/issues/562).
stty -icanon min 1 -echo > /dev/null 2>&1
export SPARK_SUBMIT_OPTS="$SPARK_SUBMIT_OPTS -Djline.terminal=unix"
"${SPARK_HOME}"/bin/spark-submit --class org.apache.spark.repl.Main --name "Spark shell" "$@"
stty icanon echo > /dev/null 2>&1
else
export SPARK_SUBMIT_OPTS
"${SPARK_HOME}"/bin/spark-submit --class org.apache.spark.repl.Main --name "Spark shell" "$@"
fi
}
# Copy restore-TTY-on-exit functions from Scala script so spark-shell exits properly even in
# binary distribution of Spark where Scala is not installed
exit_status=127
saved_stty=""
# restore stty settings (echo in particular)
function restoreSttySettings() {
stty $saved_stty
saved_stty=""
}
function onExit() {
if [[ "$saved_stty" != "" ]]; then
restoreSttySettings
fi
exit $exit_status
}
# to reenable echo if we are interrupted before completing.
trap onExit INT
# save terminal settings
saved_stty=$(stty -g 2>/dev/null)
# clear on error so we don't later try to restore them
if [[ ! $? ]]; then
saved_stty=""
fi
main "$@"
# record the exit status lest it be overwritten:
# then reenable echo and propagate the code.
exit_status=$?
onExit
总结一下,上面的代码大体上做了三件事:
- 1 捕获终端信号,执行退出方法,恢复一些操作
- 2 保存终端配置,当cygwin时关闭回显,之后再恢复
- 3 执行spark-submit,调用repl.Main
下面我们就循序渐进学习下这半段脚本涉及的内容:
什么是trap
trap
命令支持捕获特定的信号,然后执行某个命令。常用的用法有:
trap "commands" signal-list 捕获到特定的信号,执行commands命令
trap signal-list 捕获特定的信号,停止当前进程
trap " " signal-list 捕获特定的信号,什么也不做
支持的信号,可以利用kill- l
查询到:
[root@localnode3 test]# kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
SIGHUP 终止进程 终端线路挂断
SIGINT 终止进程 中断进程
SIGQUIT 建立CORE文件 终止进程,并且生成core文件
SIGILL 建立CORE文件 非法指令
SIGTRAP 建立CORE文件 跟踪自陷
SIGBUS 建立CORE文件 总线错误
SIGSEGV 建立CORE文件 段非法错误
SIGFPE 建立CORE文件 浮点异常
SIGIOT 建立CORE文件 执行I/O自陷
SIGKILL 终止进程 杀死进程
SIGPIPE 终止进程 向一个没有读进程的管道写数据
SIGALARM 终止进程 计时器到时
SIGTERM 终止进程 软件终止信号
SIGSTOP 停止进程 非终端来的停止信号
SIGTSTP 停止进程 终端来的停止信号
SIGCONT 忽略信号 继续执行一个停止的进程
SIGURG 忽略信号 I/O紧急信号
SIGIO 忽略信号 描述符上可以进行I/O
SIGCHLD 忽略信号 当子进程停止或退出时通知父进程
SIGTTOU 停止进程 后台进程写终端
SIGTTIN 停止进程 后台进程读终端
SIGXGPU 终止进程 CPU时限超时
SIGXFSZ 终止进程 文件长度过长
SIGWINCH 忽略信号 窗口大小发生变化
SIGPROF 终止进程 统计分布图用计时器到时
SIGUSR1 终止进程 用户定义信号1
SIGUSR2 终止进程 用户定义信号2
SIGVTALRM 终止进程 虚拟计时器到时
最常用的信号有四个,SIGHUP,SIGINT,SIGQUIT,SIGTSTP
,使用的时候可以简写:
trap "" 1 2 3 24
或
trap "" HUP INT QUIT TSTP
然后我们回头看看源码:
trap onExit INT
这句是说,捕获INT中断信号,然就执行onExit方法。onExit中判断是否恢复终端设置。
exit_status=127
saved_stty=""
# restore stty settings (echo in particular)
function restoreSttySettings() {
stty $saved_stty
saved_stty=""
}
function onExit() {
if [[ "$saved_stty" != "" ]]; then
restoreSttySettings
fi
exit $exit_status
}
什么是stty
stty命令可以用来改变终端的显示,比如说关闭一些按键,开启一些特殊字符的输入等等。
这个命令最常用的就是下面几个:
-a,--all 以人可读的方式打印所有当前设置;-a参数比单独的stty命令输出的终端信息更详细
-g,--save 以stty可读的方式打印当前所有设置
-F,--file=DEVICE 打开并使用特定的设备((DEVICE)以代替标准输入(stdin)
--help 显示帮助并退出
--version 显示版本并退出
stty size 打印终端行数和列数
我们先来试试stty size
这个命令
40 100
它就是打印出来了终端显示的行数和列数。
再看看stty -a
,看看都有什么内容:
speed 38400 baud; rows 40; columns 100; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>;
swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O;
min = 1; time = 0;
-parenb -parodd cs8 -hupcl -cstopb cread -clocal -crtscts -cdtrdsr
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel
-iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke
最开始speed是终端输入和输出的速度,单位是(位/秒),常用的有50、75、110、134、200、300、600、1200、1800、2400、4800、9600、19200、19.2、38400、38.4、exta 和 extb。具体还得看硬件是否支持。
后面显示了终端的基本信息,以及一些常用的按键。比如
intr 表示中断
quit 表示退出
erase 表示擦除最后一个字符
kill 表示擦除当前航
eof 表示文件结束
eol 表示当前行结束
eol2 可以设置两个字符
swtch 表示切换外壳
start 表示停止输出后重新开始
stop 表示停止输出
susp 表示终端停止
rprnt 表示刷新当前行
werase 表示擦除最后一个单词
lnext 表示输入下一个字符
flush ?????
再看后面:
min = 1; time = 0;
min = 1
通常与icaon搭配使用,表示一次读操作至少多少个字符time = 0
表示读超时的时间,N/10秒。
在后面一大堆的内容是stty支持的功能,详细的可以参考:
控制模式
clocal 假定一行没有调制解调器控制。
-clocal 假定一行带有调制解调器控制。
cread 启用接收器。
-cread 禁用接收器。
cstopb 每个字符选择两个停止位。
-cstopb 每个字符选择一个停止位。
cs5, cs6, cs7, cs8 选择字符大小。
hup,hupcl 最后关闭时挂起拨号连接。
-hup,-hupcl 最后关闭时不挂起拨号连接。
parenb 启用奇偶性校验的生成和检测。
-parenb 禁用奇偶性校验的生成和检测。
parodd 选择奇校验。
-parodd 选择偶校验。
0 立即挂起电话线路。
speed 将工作站输入和输出速度设置为指定的 speed 数(以位/秒为单位)。并不是所有的硬件接口都支持所有的速度。speed 的可能值有:50、75、110、134、200、300、600、1200、1800、2400、4800、9600、19200、19.2、38400、38.4、exta 和 extb。
注:
exta、19200 和19.2 是同义词;extb、38400 和38.4 是同义词。
ispeed speed 将工作站输入速度设置为指定的 speed 数(以位/秒为单位)。并不是所有的硬件接口都支持所有的速度,而且并不是所有的硬件接口都支持该选项。speed 的可能值与speed 选项相同。
ospeed speed 将工作站输出速度设置为指定的 speed 数(以位/秒为单位)。并不是所有的硬件接口都支持所有的速度,而且并不是所有的硬件接口都支持该选项。speed 的可能值与speed 选项相同。
输入模式
brkint 中断时发出 INTR 信号。
-brkint 中断时不发出 INTR 信号。
icrnl 输入时将 CR 映射为 NL。
-icrnl 输入时不将 CR 映射为 NL。
ignbrk 输入时忽略 BREAK。
-ignbrk 输入时不忽略 BREAK。
igncr 输入时忽略 CR。
-igncr 输入时不忽略 CR。
ignpar 忽略奇偶错误。
-ignpar 不忽略奇偶错误。
inlcr 输入时将 NL 映射为 CR。
-inlcr 输入时不将 NL 映射为 CR。
inpck 启用奇偶校验。
-inpck 禁用奇偶校验。
istrip 将输入字符剥离到 7 位。
-istrip 不将输入字符剥离到 7 位。
iuclc 将大写字母字符映射为小写。
-iuclc 不将大写字母字符映射为小写。
ixany 允许任何字符重新启动输出。
-ixany 只允许 START(Ctrl-Q 按键顺序)重新启动输出。
ixoff 当输入队列接近空或满时,发送 START/STOP 字符。
-ixoff 不发送 START/STOP 字符。
ixon 启用 START/STOP 输出控制。一旦启用 START/STOP 输出控制,您可以按下 Ctrl-S 按键顺序暂停向工作站的输出,也可按下 Ctrl-Q 按键顺序恢复输出。
-ixon 禁用 START/STOP 输出控制。
imaxbel 当输入溢出时,回送 BEL 字符并且废弃最后的输入字符。
-imaxbel 当输入溢出时,废弃所有输入。
parmrk 标记奇偶错误。
-parmrk 不标记奇偶错误。
输出方式
bs0, bs1 为退格符选择延迟样式(bs0 表示没有延迟)。
cr0, cr1, cr2, cr3 为 CR 字符选择延迟样式(cr0 表示没有延迟)。
ff0, ff1 为换页选择延迟样式(ff0 表示没有延迟)。
nl0, nl1 为 NL 字符选择延迟样式(nl0 表示没有延迟)。
ofill 使用延迟填充字符。
-ofill 使用延迟定时。
ocrnl 将 CR 字符映射为 NL 字符。
-ocrnl 不将 CR 字符映射为 NL 字符。
olcuc 输出时将小写字母字符映射为大写。
-olcuc 输出时不将小写字母字符映射为大写。
onlcr 将 NL 字符映射为 CR-NL 字符。
-onlcr 不将 NL 字符映射为 CR-NL 字符。
onlret 在终端 NL 执行 CR 功能。
-onlret 在终端 NL 不执行 CR 功能。
onocr 不在零列输出 CR 字符。
-onocr 在零列输出 CR 字符。
opost 处理输出。
-opost 不处理输出;即忽略所有其它输出选项。
ofdel 使用 DEL 字符作为填充字符。
-ofdel 使用 NUL 字符作为填充字符。
tab0, tab1, tab2 为水平制表符选择延迟样式(tab0 表示没有延迟)。
tab3 扩展制表符至多个空格。
vt0, vt1 为垂直制表符选择延迟样式(vt0 表示没有延迟)。
本地模式
echo 回送每个输入的字符。
-echo 不回送字符。
echoctl 以 ^X(Ctrl-X)回送控制字符,X 是将 100 八进制加到控制字符代码中给出的字符。
-echoctl 不以 ^X(Ctrl-X)回送控制字符。
echoe 以“backspace space backspace”字符串回送 ERASE 字符。
注:
该模式不保持对列位置的跟踪,因此您可能在擦除制表符和转义序列等符号时得到意外的结果。
-echoe 不回送 ERASE 字符,只回送退格符。
echok 在 KILL 字符后回送 NL 字符。
-echok 在 KILL 字符后不回送 NL 字符。
echoke 通过擦除输出行上的每个字符,回送 KILL 字符。
-echoke 只回送 KILL 字符。
echonl 回送 NL 字符。
-echonl 不回送 NL 字符。
echoprt 以 /(斜杠)和 \ (反斜杠) 向后回送擦除的字符。
-echoprt 不以 /(斜杠)和 \ (反斜杠) 向后回送擦除的字符。
icanon 启用规范输入(规范输入允许使用 ERASE 和 KILL 字符进行输入行的编辑)。请参阅 AIX 5L Version 5.2 Communications Programming Concepts 中的 Line Discipline Module (ldterm) 中关于canonical mode input 的讨论。
-icanon 禁用规范输入。
iexten 指定从输入数据中识别实现性定义的功能。要识别以下控制字符,需要设置 iexten:eol2、dsusp、reprint、discard、werase、lnext。与这些模式关联的功能也需要设置iexten:imaxbel、echoke、echoprt、echoctl。
-iexten 指定从输入数据中识别实现性定义的功能。
isig 启用对特殊控制字符(INTR、SUSP 和 QUIT)的字符检查。
-isig 禁用对特殊控制字符(INTR、SUSP 和 QUIT)的字符检查。
noflsh 不清除 INTR、SUSP 或 QUIT 控制字符之后的缓冲区。
-noflsh 清除 INTR、SUSP 或 QUIT 控制字符之后的缓冲区。
pending 下次读操作暂挂或输入到达时,要重新输入从原始模式转换为规范模式后被暂挂的输入。暂挂是一个内部状态位。
-pending 没有文本暂挂。
tostop 为背景输出发出 SIGTOU 信号。
-tostop 不为背景输出发出 SIGTOU 信号。
xcase 在输入中回送大写字符,并在输出显示的大写字符之前加上 \ (反斜杠)。
-xcase 不在输入时回送大写字符。
硬件流量控制模式
这些选项是对 《X/Open 可移植性指南,发行版 4》 标准的扩展。
cdxon 输出时启用 CD 硬件流量控制模式。
-cdxon 输出时禁用 CD 硬件流量控制模式。
ctsxon 输出时启用 CTS 硬件流量控制模式。
-ctsxon 输出时禁用 CTS 硬件流量控制模式。
dtrxoff 输入时启用 DTR 硬件流量控制模式。
-dtrxoff 输入时禁用 DTR 硬件流量控制模式。
rtsxoff 输入时启用 RTS 硬件流量控制模式。
-rtsxoff 输入时禁用 RTS 硬件流量控制模式。
组合模式
cooked 请参阅 -raw 选项。
ek 分别将 ERASE 和 KILL 字符设置为 Ctrl-H 和 Ctrl-U 按键顺序。
evenp 启用 parenb 和 cs7。
-evenp 禁用 parenb 并设置 cs8。
lcase,LCASE 设置 xcase,iuclc 和olcuc。在工作站只以大写字符使用。
-lcase,-LCASE 设置 -xcase、-iuclc 和-olcuc。
nl 设置 -icrnl 和-onlcr。
-nl 设置 icrnl、 onlcr、-inlcr、-igncr、-ocrnl和-onlret。
oddp 启用 parenb、 cs7 和 parodd。
-oddp 禁用 parenb 并设置 cs8。
parity 请参阅 evenp 选项。
-parity 请参阅 -evenp 选项。
sane 将参数重新设置为合理的值。
raw 允许原始模式输入(不包括输入处理,例如 erase、kill 或 interrupt);传回奇偶(校验)位。
-raw 允许规范输入方式。
tabs 保留制表符。
-tabs,tab3 打印时将制表符替换为空格。
窗口大小
cols n,columns n 将终端(窗口)大小记录为有 n 列。
rows n 将终端(窗口)大小记录为有 n 行。
size 将终端(窗口)大小打印到标准输出(先是行,再是列)中。
stty的小栗子
看完上面的东西,很多人都蒙B了,这么多东西咋用啊?咱们来个小栗子,体验一下stty的奇妙。
场景,当你远程ssh机器的时候是不是要输入密码?但是输入的密码是看不到的,这是怎么做到的?先来看看shell脚本吧!
#!/bin/bash
PASSWD="123"
USER=`whoami`
# save current stty setting
SAVEDSTTY=`stty -g`
# hide input characters
stty -echo
echo -n "Please input passwd:"
read passwd
echo ""
if [ "$PASSWD" = "$passwd" ];then
echo "Welcome $USER"
else
echo "Sorry"
fi
# echo input caharacters
stty echo
# restore stty
stty=$SAVEDSTTY
脚本的意思是:先关闭屏幕回显,即你输入啥屏幕也不显示了;然后提示输出密码;验证密码是否正确给予反馈;打开回显;恢复终端设置。
看看效果哈:
[root@localnode3 test]# sh stty.sh
Please input passwd:
Sorry
[root@localnode3 test]# sh stty.sh
Please input passwd:
Welcome root
[root@localnode3 test]#
挺有意思吧!
回头看源码
有了对stty的了解后,回头我们看看spark-shell脚本,就清晰明了了。
saved_stty=$(stty -g 2>/dev/null)
首先保存了当前的终端配置。
if [[ ! $? ]]; then
saved_stty=""
fi
如果收到退出命令,就恢复stty状态。
然后调用main方法,并传递所有的参数main "$@"
,最后根据返回状态,判断是直接终端退出还是恢复之前的终端界面。
回头再看看main方法就容易理解了:
function main() {
if $cygwin; then
stty -icanon min 1 -echo > /dev/null 2>&1
export SPARK_SUBMIT_OPTS="$SPARK_SUBMIT_OPTS -Djline.terminal=unix"
"${SPARK_HOME}"/bin/spark-submit --class org.apache.spark.repl.Main --name "Spark shell" "$@"
stty icanon echo > /dev/null 2>&1
else
export SPARK_SUBMIT_OPTS
"${SPARK_HOME}"/bin/spark-submit --class org.apache.spark.repl.Main --name "Spark shell" "$@"
fi
}
如果是cygwin,先关闭echo回显,设置读操作最少1个字符。然后启动spark-submit 执行org.apache.spark.repl.Main
类,并设置应用的名字,传递参数。执行完成后,再开启echo回显。
参考
本文转自博客园xingoo的博客,原文链接:Spark源码分析之Spark Shell(下),如需转载请自行联系原博主。