|

楼主 |
发表于 2005-3-17 08:44:47
|
显示全部楼层
四.Linux输入和输出命令详述
1.echo
首先,在LINUX中,要使转义符生效,需加参数-e
从echo的变量开始说起
如:e c h o命令输出转义符以及变量。
- # echo -e "\007your home is $HOME , you are connected on `tty`"
- your home is /root , you are connected on /dev/pts/1
- # echo -e "\ayour home is $HOME , you are connected on `tty`"
- your home is /root , you are connected on /dev/pts/1
- #
复制代码
本例中
\007或\a你可以让终端铃响一声
显示出$ H O M E目录,
并且可以让系统执行t t y命令(注意,该命令用键盘左上角的符号,法语中的抑音符引起来,不是单引号 )。
在e c h o命令输出之后附加换行,可以使用\ n选项:
- $ cat echod
- #!/bin/sh
- echo -e "this echo's 3 new lines\n\n\n"
- echo "OK"
复制代码
编辑一个新echod,如上内容,然后运行输出如下:
- $ ./echod
- this echo's 3 new lines
- OK
- $
复制代码
在e c h o语句中使用跳格符,记住别忘了加反斜杠\:
- $ echo -e "here is a tab\there are two tabs\t\tok"
- here is a tab here are two tabs ok
- $
复制代码
把一个字符串输出到文件中,使用重定向符号>。
在下面的例子中一个字符串被重定向到一个名为m y f i l e的文件中:
- $ echo "The log files have all been done"> myfile
复制代码
或者可以追加到一个文件的末尾,这意味着不覆盖原有的内容:
- $ echo "$LOGNAME carried them out at `date`">>myfile
复制代码
现在让我们看一下m y f i l e文件中的内容:
- The log files have all been done
- sam carried them out at 六 11月 13 12:54:32 CST 2004
复制代码
引号是一个特殊字符,所以必须要使用反斜杠\来使s h e l l忽略它的特殊含义。
假设你希望使用e c h o命令输出这样的字符串:“/ d e v / r m t 0”,那么我们只要在引号前面加上反斜杠\即可:
- $ echo ""/dev/rmt0""
- "/dev/rmt0"
- $
复制代码
上面说过,
那么echo可以用的转义字符有下面这些:
- \a 鸣叫beep
- \n 换行
- \t 水平制表
- \v 垂直制表
- \b 退格
- \r 回车
- \f 换页
- \\ \
- \' '
- " "
- \ddd 一到三位八进制数ddd所代表的字符
- \xhh 一到二位十六进制数hh所代表的字符
- (这两个都是ASCII码)
- \c 取消末行换行符,等价于echo -n ...
复制代码
ps:注意其中\n和\r的区别,可以自己实验......
补充:
echo的特殊用法
a.把字符串输出到标准显示的指定位置:
- r= #指定行
- c= #指定列
- echo -e "\033[${r};${c}H我在这里"
复制代码
b.隐藏光标:
其中25后面是字母l
c.ANSI控制码
例如:
- echo -ne "\033[32m" #可以将字符的显示颜色改为绿色
- echo -ne "\033[3;1H" #可以将光标移到第3行第1列处
复制代码
具体的摘抄一些如下:
- \033[0m #关闭所有属性
- \033[1m #设置高亮度
- \033[4m #下划线
- \033[5m #闪烁
- \033[7m #反显
- \033[8m #消隐
- \033[30m -- \33[37m #设置前景色
- \033[40m -- \33[47m #设置背景色
- \033[nA #光标上移n行
- \033[nB #光标下移n行
- \033[nC #光标右移n行
- \033[nD #光标左移n行
- \033[y;xH #设置光标位置
- \033[2J #清屏
- \033[K #清除从光标到行尾的内容
- \033[s #保存光标位置
- \033[u #恢复光标位置
- \033[?25l #隐藏光标
- \033[?25h #显示光标
复制代码
下面是摘抄的shell中俄罗斯方块程序,我也没有具体研究,先放到这里,有兴趣可以研究一下
运行于GNU bash, version 2.05a.0(1)-release (i686-pc-linux-gnu)
- #!/bin/bash
- # Tetris Game
- # 10.21.2003 xhchen<xhchen@winbond.com.tw>
- #颜色定义
- cRed=1
- cGreen=2
- cYellow=3
- cBlue=4
- cFuchsia=5
- cCyan=6
- cWhite=7
- colorTable=($cRed $cGreen $cYellow $cBlue $cFuchsia $cCyan $cWhite)
- #位置和大小
- iLeft=3
- iTop=2
- ((iTrayLeft = iLeft + 2))
- ((iTrayTop = iTop + 1))
- ((iTrayWidth = 10))
- ((iTrayHeight = 15))
- #颜色设置
- cBorder=$cGreen
- cScore=$cFuchsia
- cScoreValue=$cCyan
- #控制信号
- #改游戏使用两个进程,一个用于接收输入,一个用于游戏流程和显示界面;
- #当前者接收到上下左右等按键时,通过向后者发送signal的方式通知后者。
- sigRotate=25
- sigLeft=26
- sigRight=27
- sigDown=28
- sigAllDown=29
- sigExit=30
- #七中不同的方块的定义
- #通过旋转,每种方块的显示的样式可能有几种
- box0=(0 0 0 1 1 0 1 1)
- box1=(0 2 1 2 2 2 3 2 1 0 1 1 1 2 1 3)
- box2=(0 0 0 1 1 1 1 2 0 1 1 0 1 1 2 0)
- box3=(0 1 0 2 1 0 1 1 0 0 1 0 1 1 2 1)
- box4=(0 1 0 2 1 1 2 1 1 0 1 1 1 2 2 2 0 1 1 1 2 0 2 1 0 0 1 0 1 1 1 2)
- box5=(0 1 1 1 2 1 2 2 1 0 1 1 1 2 2 0 0 0 0 1 1 1 2 1 0 2 1 0 1 1 1 2)
- box6=(0 1 1 1 1 2 2 1 1 0 1 1 1 2 2 1 0 1 1 0 1 1 2 1 0 1 1 0 1 1 1 2)
- #所有其中方块的定义都放到box变量中
- box=(${box0[@]} ${box1[@]} ${box2[@]} ${box3[@]} ${box4[@]} ${box5[@]} ${box6[@]})
- #各种方块旋转后可能的样式数目
- countBox=(1 2 2 2 4 4 4)
- #各种方块再box数组中的偏移
- offsetBox=(0 1 3 5 7 11 15)
- #每提高一个速度级需要积累的分数
- iScoreEachLevel=50 #be greater than 7
- #运行时数据
- sig=0 #接收到的signal
- iScore=0 #总分
- iLevel=0 #速度级
- boxNew=() #新下落的方块的位置定义
- cBoxNew=0 #新下落的方块的颜色
- iBoxNewType=0 #新下落的方块的种类
- iBoxNewRotate=0 #新下落的方块的旋转角度
- boxCur=() #当前方块的位置定义
- cBoxCur=0 #当前方块的颜色
- iBoxCurType=0 #当前方块的种类
- iBoxCurRotate=0 #当前方块的旋转角度
- boxCurX=-1 #当前方块的x坐标位置
- boxCurY=-1 #当前方块的y坐标位置
- iMap=() #背景方块图表
- #初始化所有背景方块为-1, 表示没有方块
- for ((i = 0; i < iTrayHeight * iTrayWidth; i++)); do iMap[$i]=-1; done
- #接收输入的进程的主函数
- function RunAsKeyReceiver()
- {
- local pidDisplayer key aKey sig cESC sTTY
- pidDisplayer=$1
- aKey=(0 0 0)
- cESC=`echo -ne "\33"`
- cSpace=`echo -ne "\40"`
- #保存终端属性。在read -s读取终端键时,终端的属性会被暂时改变。
- #如果在read -s时程序被不幸杀掉,可能会导致终端混乱,
- #需要在程序退出时恢复终端属性。
- sTTY=`stty -g`
-
- #捕捉退出信号
- trap "MyExit;" INT TERM
- trap "MyExitNoSub;" $sigExit
-
- #隐藏光标
- echo -ne "\33[?25l"
-
- while (( 1 ))
- do
- #读取输入。注-s不回显,-n读到一个字符立即返回
- read -s -n 1 key
-
- aKey[0]=${aKey[1]}
- aKey[1]=${aKey[2]}
- aKey[2]=$key
- sig=0
- #判断输入了何种键
- if [[ $key == $cESC && ${aKey[1]} == $cESC ]]
- then
- #ESC键
- MyExit
- elif [[ ${aKey[0]} == $cESC && ${aKey[1]} == "[" ]]
- then
- if [[ $key == "A" ]]; then sig=$sigRotate #<向上键>
- elif [[ $key == "B" ]]; then sig=$sigDown #<向下键>
- elif [[ $key == "D" ]]; then sig=$sigLeft #<向左键>
- elif [[ $key == "C" ]]; then sig=$sigRight #<向右键>
- fi
- elif [[ $key == "W" || $key == "w" ]]; then sig=$sigRotate #W, w
- elif [[ $key == "S" || $key == "s" ]]; then sig=$sigDown #S, s
- elif [[ $key == "A" || $key == "a" ]]; then sig=$sigLeft #A, a
- elif [[ $key == "D" || $key == "d" ]]; then sig=$sigRight #D, d
- elif [[ "[$key]" == "[]" ]]; then sig=$sigAllDown #空格键
- elif [[ $key == "Q" || $key == "q" ]] #Q, q
- then
- MyExit
- fi
- if [[ $sig != 0 ]]
- then
- #向另一进程发送消息
- kill -$sig $pidDisplayer
- fi
- done
- }
- #退出前的恢复
- function MyExitNoSub()
- {
- local y
-
- #恢复终端属性
- stty $sTTY
- ((y = iTop + iTrayHeight + 4))
- #显示光标
- echo -e "\33[?25h\33[${y};0H"
- exit
- }
- function MyExit()
- {
- #通知显示进程需要退出
- kill -$sigExit $pidDisplayer
-
- MyExitNoSub
- }
- #处理显示和游戏流程的主函数
- function RunAsDisplayer()
- {
- local sigThis
- InitDraw
- #挂载各种信号的处理函数
- trap "sig=$sigRotate;" $sigRotate
- trap "sig=$sigLeft;" $sigLeft
- trap "sig=$sigRight;" $sigRight
- trap "sig=$sigDown;" $sigDown
- trap "sig=$sigAllDown;" $sigAllDown
- trap "ShowExit;" $sigExit
- while (( 1 ))
- do
- #根据当前的速度级iLevel不同,设定相应的循环的次数
- for ((i = 0; i < 21 - iLevel; i++))
- do
- sleep 0.02
- sigThis=$sig
- sig=0
- #根据sig变量判断是否接受到相应的信号
- if ((sigThis == sigRotate)); then BoxRotate; #旋转
- elif ((sigThis == sigLeft)); then BoxLeft; #左移一列
- elif ((sigThis == sigRight)); then BoxRight; #右移一列
- elif ((sigThis == sigDown)); then BoxDown; #下落一行
- elif ((sigThis == sigAllDown)); then BoxAllDown; #下落到底
- fi
- done
- #kill -$sigDown $$
- BoxDown #下落一行
- done
- }
- #BoxMove(y, x), 测试是否可以把移动中的方块移到(x, y)的位置, 返回0则可以, 1不可以
- function BoxMove()
- {
- local j i x y xTest yTest
- yTest=$1
- xTest=$2
- for ((j = 0; j < 8; j += 2))
- do
- ((i = j + 1))
- ((y = ${boxCur[$j]} + yTest))
- ((x = ${boxCur[$i]} + xTest))
- if (( y < 0 || y >= iTrayHeight || x < 0 || x >= iTrayWidth))
- then
- #撞到墙壁了
- return 1
- fi
- if ((${iMap[y * iTrayWidth + x]} != -1 ))
- then
- #撞到其他已经存在的方块了
- return 1
- fi
- done
- return 0;
- }
- #将当前移动中的方块放到背景方块中去,
- #并计算新的分数和速度级。(即一次方块落到底部)
- function Box2Map()
- {
- local j i x y xp yp line
- #将当前移动中的方块放到背景方块中去
- for ((j = 0; j < 8; j += 2))
- do
- ((i = j + 1))
- ((y = ${boxCur[$j]} + boxCurY))
- ((x = ${boxCur[$i]} + boxCurX))
- ((i = y * iTrayWidth + x))
- iMap[$i]=$cBoxCur
- done
-
- #消去可被消去的行
- line=0
- for ((j = 0; j < iTrayWidth * iTrayHeight; j += iTrayWidth))
- do
- for ((i = j + iTrayWidth - 1; i >= j; i--))
- do
- if ((${iMap[$i]} == -1)); then break; fi
- done
- if ((i >= j)); then continue; fi
-
- ((line++))
- for ((i = j - 1; i >= 0; i--))
- do
- ((x = i + iTrayWidth))
- iMap[$x]=${iMap[$i]}
- done
- for ((i = 0; i < iTrayWidth; i++))
- do
- iMap[$i]=-1
- done
- done
-
- if ((line == 0)); then return; fi
- #根据消去的行数line计算分数和速度级
- ((x = iLeft + iTrayWidth * 2 + 7))
- ((y = iTop + 11))
- ((iScore += line * 2 - 1))
- #显示新的分数
- echo -ne "\33[1m\33[3${cScoreValue}m\33[${y};${x}H${iScore} "
- if ((iScore % iScoreEachLevel < line * 2 - 1))
- then
- if ((iLevel < 20))
- then
- ((iLevel++))
- ((y = iTop + 14))
- #显示新的速度级
- echo -ne "\33[3${cScoreValue}m\33[${y};${x}H${iLevel} "
- fi
- fi
- echo -ne "\33[0m"
- #重新显示背景方块
- for ((y = 0; y < iTrayHeight; y++))
- do
- ((yp = y + iTrayTop + 1))
- ((xp = iTrayLeft + 1))
- ((i = y * iTrayWidth))
- echo -ne "\33[${yp};${xp}H"
- for ((x = 0; x < iTrayWidth; x++))
- do
- ((j = i + x))
- if ((${iMap[$j]} == -1))
- then
- echo -ne " "
- else
- echo -ne "\33[1m\33[7m\33[3${iMap[$j]}m\33[4${iMap[$j]}m[]\33[0m"
- fi
- done
- done
- }
- #下落一行
- function BoxDown()
- {
- local y s
- ((y = boxCurY + 1)) #新的y坐标
- if BoxMove $y $boxCurX #测试是否可以下落一行
- then
- s="`DrawCurBox 0`" #将旧的方块抹去
- ((boxCurY = y))
- s="$s`DrawCurBox 1`" #显示新的下落后方块
- echo -ne $s
- else
- #走到这儿, 如果不能下落了
- Box2Map #将当前移动中的方块贴到背景方块中
- RandomBox #产生新的方块
- fi
- }
- #左移一列
- function BoxLeft()
- {
- local x s
- ((x = boxCurX - 1))
- if BoxMove $boxCurY $x
- then
- s=`DrawCurBox 0`
- ((boxCurX = x))
- s=$s`DrawCurBox 1`
- echo -ne $s
- fi
- }
- #右移一列
- function BoxRight()
- {
- local x s
- ((x = boxCurX + 1))
- if BoxMove $boxCurY $x
- then
- s=`DrawCurBox 0`
- ((boxCurX = x))
- s=$s`DrawCurBox 1`
- echo -ne $s
- fi
- }
- #下落到底
- function BoxAllDown()
- {
- local k j i x y iDown s
- iDown=$iTrayHeight
- #计算一共需要下落多少行
- for ((j = 0; j < 8; j += 2))
- do
- ((i = j + 1))
- ((y = ${boxCur[$j]} + boxCurY))
- ((x = ${boxCur[$i]} + boxCurX))
- for ((k = y + 1; k < iTrayHeight; k++))
- do
- ((i = k * iTrayWidth + x))
- if (( ${iMap[$i]} != -1)); then break; fi
- done
- ((k -= y + 1))
- if (( $iDown > $k )); then iDown=$k; fi
- done
-
- s=`DrawCurBox 0` #将旧的方块抹去
- ((boxCurY += iDown))
- s=$s`DrawCurBox 1` #显示新的下落后的方块
- echo -ne $s
- Box2Map #将当前移动中的方块贴到背景方块中
- RandomBox #产生新的方块
- }
- #旋转方块
- function BoxRotate()
- {
- local iCount iTestRotate boxTest j i s
- iCount=${countBox[$iBoxCurType]} #当前的方块经旋转可以产生的样式的数目
- #计算旋转后的新的样式
- ((iTestRotate = iBoxCurRotate + 1))
- if ((iTestRotate >= iCount))
- then
- ((iTestRotate = 0))
- fi
- #更新到新的样式, 保存老的样式(但不显示)
- for ((j = 0, i = (${offsetBox[$iBoxCurType]} + $iTestRotate) * 8; j < 8; j++, i++))
- do
- boxTest[$j]=${boxCur[$j]}
- boxCur[$j]=${box[$i]}
- done
- if BoxMove $boxCurY $boxCurX #测试旋转后是否有空间放的下
- then
- #抹去旧的方块
- for ((j = 0; j < 8; j++))
- do
- boxCur[$j]=${boxTest[$j]}
- done
- s=`DrawCurBox 0`
- #画上新的方块
- for ((j = 0, i = (${offsetBox[$iBoxCurType]} + $iTestRotate) * 8; j < 8; j++, i++))
- do
- boxCur[$j]=${box[$i]}
- done
- s=$s`DrawCurBox 1`
- echo -ne $s
- iBoxCurRotate=$iTestRotate
- else
- #不能旋转,还是继续使用老的样式
- for ((j = 0; j < 8; j++))
- do
- boxCur[$j]=${boxTest[$j]}
- done
- fi
- }
- #DrawCurBox(bDraw), 绘制当前移动中的方块, bDraw为1, 画上, bDraw为0, 抹去方块。
- function DrawCurBox()
- {
- local i j t bDraw sBox s
- bDraw=$1
- s=""
- if (( bDraw == 0 ))
- then
- sBox="\40\40"
- else
- sBox="[]"
- s=$s"\33[1m\33[7m\33[3${cBoxCur}m\33[4${cBoxCur}m"
- fi
-
- for ((j = 0; j < 8; j += 2))
- do
- ((i = iTrayTop + 1 + ${boxCur[$j]} + boxCurY))
- ((t = iTrayLeft + 1 + 2 * (boxCurX + ${boxCur[$j + 1]})))
- #\33[y;xH, 光标到(x, y)处
- s=$s"\33[${i};${t}H${sBox}"
- done
- s=$s"\33[0m"
- echo -n $s
- }
- #更新新的方块
- function RandomBox()
- {
- local i j t
- #更新当前移动的方块
- iBoxCurType=${iBoxNewType}
- iBoxCurRotate=${iBoxNewRotate}
- cBoxCur=${cBoxNew}
- for ((j = 0; j < ${#boxNew[@]}; j++))
- do
- boxCur[$j]=${boxNew[$j]}
- done
-
- #显示当前移动的方块
- if (( ${#boxCur[@]} == 8 ))
- then
- #计算当前方块该从顶端哪一行"冒"出来
- for ((j = 0, t = 4; j < 8; j += 2))
- do
- if ((${boxCur[$j]} < t)); then t=${boxCur[$j]}; fi
- done
- ((boxCurY = -t))
- for ((j = 1, i = -4, t = 20; j < 8; j += 2))
- do
- if ((${boxCur[$j]} > i)); then i=${boxCur[$j]}; fi
- if ((${boxCur[$j]} < t)); then t=${boxCur[$j]}; fi
- done
- ((boxCurX = (iTrayWidth - 1 - i - t) / 2))
- #显示当前移动的方块
- echo -ne `DrawCurBox 1`
- #如果方块一出来就没处放,Game over!
- if ! BoxMove $boxCurY $boxCurX
- then
- kill -$sigExit ${PPID}
- ShowExit
- fi
- fi
-
-
- #清除右边预显示的方块
- for ((j = 0; j < 4; j++))
- do
- ((i = iTop + 1 + j))
- ((t = iLeft + 2 * iTrayWidth + 7))
- echo -ne "\33[${i};${t}H "
- done
- #随机产生新的方块
- ((iBoxNewType = RANDOM % ${#offsetBox[@]}))
- ((iBoxNewRotate = RANDOM % ${countBox[$iBoxNewType]}))
- for ((j = 0, i = (${offsetBox[$iBoxNewType]} + $iBoxNewRotate) * 8; j < 8; j++, i++))
- do
- boxNew[$j]=${box[$i]};
- done
- ((cBoxNew = ${colorTable[RANDOM % ${#colorTable[@]}]}))
-
- #显示右边预显示的方块
- echo -ne "\33[1m\33[7m\33[3${cBoxNew}m\33[4${cBoxNew}m"
- for ((j = 0; j < 8; j += 2))
- do
- ((i = iTop + 1 + ${boxNew[$j]}))
- ((t = iLeft + 2 * iTrayWidth + 7 + 2 * ${boxNew[$j + 1]}))
- echo -ne "\33[${i};${t}H[]"
- done
- echo -ne "\33[0m"
- }
- #初始绘制
- function InitDraw()
- {
- clear
- RandomBox #随机产生方块,这时右边预显示窗口中有方快了
- RandomBox #再随机产生方块,右边预显示窗口中的方块被更新,原先的方块将开始下落
- local i t1 t2 t3
- #显示边框
- echo -ne "\33[1m"
- echo -ne "\33[3${cBorder}m\33[4${cBorder}m"
-
- ((t2 = iLeft + 1))
- ((t3 = iLeft + iTrayWidth * 2 + 3))
- for ((i = 0; i < iTrayHeight; i++))
- do
- ((t1 = i + iTop + 2))
- echo -ne "\33[${t1};${t2}H||"
- echo -ne "\33[${t1};${t3}H||"
- done
-
- ((t2 = iTop + iTrayHeight + 2))
- for ((i = 0; i < iTrayWidth + 2; i++))
- do
- ((t1 = i * 2 + iLeft + 1))
- echo -ne "\33[${iTrayTop};${t1}H=="
- echo -ne "\33[${t2};${t1}H=="
- done
- echo -ne "\33[0m"
-
- #显示"Score"和"Level"字样
- echo -ne "\33[1m"
- ((t1 = iLeft + iTrayWidth * 2 + 7))
- ((t2 = iTop + 10))
- echo -ne "\33[3${cScore}m\33[${t2};${t1}HScore"
- ((t2 = iTop + 11))
- echo -ne "\33[3${cScoreValue}m\33[${t2};${t1}H${iScore}"
- ((t2 = iTop + 13))
- echo -ne "\33[3${cScore}m\33[${t2};${t1}HLevel"
- ((t2 = iTop + 14))
- echo -ne "\33[3${cScoreValue}m\33[${t2};${t1}H${iLevel}"
- echo -ne "\33[0m"
- }
- #退出时显示GameOVer!
- function ShowExit()
- {
- local y
- ((y = iTrayHeight + iTrayTop + 3))
- echo -e "\33[${y};0HGameOver!\33[0m"
- exit
- }
- #游戏主程序在这儿开始.
- if [[ $1 != "--show" ]]
- then
- bash $0 --show& #以参数--show将本程序再运行一遍
- RunAsKeyReceiver $! #以上一行产生的进程的进程号作为参数
- exit
- else
- #当发现具有参数--show时,运行显示函数
- RunAsDisplayer
- exit
- fi
复制代码
2.read
格式:
- read [-ers] [-u fd] [-t timeout] [-a aname] [-p prompt] [-n nchars] [-d delim] [name ...]
复制代码
当read命令没有参数[name ...]时,直接设在REPLY上。
可以用$REPLY引用.
其他命令参数看下面运行实例:
- zhyfly: ~/1$ read
- what
- zhyfly: ~/1$ echo $REPLY
- what
- zhyfly: ~/1$ read name
- zhyfly
- zhyfly: ~/1$ echo $name
- zhyfly
- zhyfly: ~/1$ read name veb name1
- i love you
- zhyfly: ~/1$ echo $name $veb $name1
- i love you
- zhyfly: ~/1$ read name veb name1
- i love
- zhyfly: ~/1$ echo "$name,$veb,$name1"
- i,love,
- zhyfly: ~/1$ read name veb
- i love you
- zhyfly: ~/1$ echo "$name,$veb"
- i,love you
- zhyfly: ~/1$ read -p "how old r u?" age
- how old r u?23
- zhyfly: ~/1$ echo $age
- 23
- zhyfly: ~/1$ read -p "some words?" -a words
- some words?i love u!
- zhyfly: ~/1$ echo ${words[*]}
- i love u!
- zhyfly: ~/1$ read -p "passwd:" -s passwd
- passwd:#输入密码zhyfly: ~/1$ echo $passwd
- zhyfly
- zhyfly: ~/1$ read -p "type the string end with n characters:" -n 5
- type the string end with n characters:34352zhyfly: ~/1$
- zhyfly: ~/1$ read -p "type the string end with the letter you set:" -dp
- type the string end with the letter you set:hahaw^?^?pzhyfly: ~/1$
- zhyfly: ~/1$ read -p "type the string in the seconds you set:" -t 5 test
- type the string in the seconds you set:zhyfly: ~/1$
- #5秒钟不反应自动退出
- zhyfly: ~/1$ read -p 'the \ will work as a metacharater!' a
- the \ will work as a metacharater!bc\$d
- zhyfly: ~/1$ echo $a
- bc$d
- zhyfly: ~/1$ read -r -p 'the \ will not work as a metacharater!' A
- the \ will not work as a metacharater!bc\$d
- zhyfly: ~/1$ echo $A
- bc\$d
复制代码
一个echo和read的例子:
- zhyfly: ~/1$ cat test
- #!/bin/bash
- echo -n "First name:"
- read firstname
- echo -n "Middle name:"
- read middlename
- echo -e "Last name:\c"
- read lastname
- echo "$firstname,$middlename,$lastname"
- zhyfly: ~/1$ sudo chmod +x test
- zhyfly: ~/1$ ./test
- First name:zhy
- Middle name:2
- Last name:fly
- zhy,2,fly
复制代码
3.cat
cat:显示文件内容,创建文件,还可以用它来显示控制字符。
注意:在文件分页符处不会停下来;会一下显示完整个文件。因此,可以使用m o r e命令或把c a t命令的输出通过管道传递到另外一个具有分页功能的命令中,使用命令less file可实现相同的功能。
如下形式
或
或
c a t命令的一般形式为:
- cat [options] filename1 ... filename2 ...
复制代码
a、显示名为m y f i l e的文件:
b、显示m y f i l e 1、m y f i l e 2、m y f i l e 3这三个文件,可以用:
- $ cat myfile1 myfile2 myfile3
复制代码
c、创建一个包含上述三个文件的内容,名为b i g f i l e的文件,可以用输出重定向到新文件中:
- $ cat myfile1 myfile2 myfile3 > bigfile
复制代码
d、如果cat的命令行中没有参数,输入的每一行都立刻被cat命令输出到屏幕上,输入完毕后按< C T R L - D >结束
- $ cat
- Hello world
- Hello world
- <ctrl+d>
- $
复制代码
e、新建文件
- $cat >myfile
- This is great
- <ctrl-d>
- $cat myfile
- This is great
复制代码
cat:参数选项
使用方式:
- cat [-AbeEnstTuv] [--help] [--version] fileName
复制代码
说明:把档案串连接后传到基本输出(萤幕或加 > fileName 到另一个档案)
参数:
-n 或 --number 由 1 开始对所有输出的行数编号
-b 或 --number-nonblank 和 -n 相似,只不过对于空白行不编号
-s 或 --squeeze-blank 当遇到有连续两行以上的空白行,就代换为一行的空白行
-v 或 --show-nonprinting 显示非打印字符
例:
显示时加上行号
- $cp /etc/httpd/conf/httpd /usr/sam
- $ cat -n httpd.conf
复制代码
把 httpd.conf 的内容加上行号后输入 httpd1.conf 这个文件里
- $cat -n httpd.conf > httpd1.conf
复制代码
对文件httpd.conf加上行号(空白不加)后显示
把 textfile1 和 textfile2 的档案内容加上行号(空白行不加)之后将内容附加到 textfile3 里。
- $ cat -b textfile1 textfile2 >> textfile3
复制代码
清空/etc/test.txt档案内容
- $cat /dev/null > /etc/test.txt
复制代码
使用 sed 与 cat 除去空白行
- $ cat -s /etc/X11/XF86Config | sed '/^[[:space:]]*$/d'
复制代码
cat 还可以在您查看包含如制表符这样的非打印字符的文件时起帮助作用。您可以用以下选项来显示制表符:
* -T 将制表符显示为 ^I
* -v 显示非打印字符,除了换行符和制表符,它们使用各自效果相当的“控制序列”。例如,当您处理一个在 Windows 系统中生成的文件时,这个文件将使用 Control-M(^M)来标记行的结束。对于代码大于 127 的字符,它们的前面将会被加上 M-(表示“meta”),这与其它系统中在字符前面加上 Alt- 相当。
* -E 在每一行的结束处添加美元符($)。
显示非打印字符
- $ cat -t /etc/X11/XF86Config
- ...
- # Multiple FontPath entries are allowed (they are concatenated together)
- # By default, Red Hat 6.0 and later now use a font server independent of
- # the X server to render fonts.
- ^IFontPath^I"/usr/X11R6/lib/X11/fonts/TrueType"
- ^IFontPath^I"unix/:7100"
- EndSection
- ...
复制代码
- $ cat -E /etc/X11/XF86Config
- ...
- # Multiple FontPath entries are allowed (they are concatenated together)$
- # By default, Red Hat 6.0 and later now use a font server independent of$
- # the X server to render fonts.$
- $
- FontPath "/usr/X11R6/lib/X11/fonts/TrueType"$
- FontPath "unix/:7100"$
- $
- EndSection$
- ...
复制代码
- $ cat -v /etc/X11/XF86Config
- ...
- ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@M-|M-8^X^@^@^@
- P^@^O"M-X^O M-@^M^@^@^@M-^@^O"M-@M-k^@M-8*^@
- @M-^H$M-@M-9|A(M-@)M-yM-|M-sM-*M-hW^A^@^@j^@
- M-|M-sM-%1M-@M-9^@^B^@^@M-sM-+fM-^A= ^@ ^@
- F^@^@ ^@M-9^@^H^@^@M-sM-$M-G^E(l!M-@M-^?
- ^IM-A5^@^@^D^@PM-^]M-^\X1M-H%^@^@^D^@tyM-G
- ...
复制代码
4.tee
tee:读取标准输入的数据,并将其内容输出成文件。
语 法:tee [-ai][--help][--version][文件…]
补充说明:tee指令会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件。我们可利用tee把管道导入的数据存成文件,甚至一次保存数份文件。
参 数:-a 附加到既有文件的面,而非覆盖它。如果给予tee指令的文件名称已经存在,预设会覆盖该文件的内容。加上此参数,数据会新增在该文件内容的最面,而不会删除原先之内容。
-i 忽略中断信号
--help 在线帮助
--version 显示版本信息
例一:
列出文本文件slayers.story的内容,同时复制3份副本,文件名称分别为ss-copy1、ss-copy2、ss-copy3:
- $ cat slayers.story |tee ss-copy1 ss-copy2 ss-copy3
复制代码
例二: 把列出当前目录,并把结果结到myfile里
管道:可以通过管道把一个命令的输出传递给另一个命令作为输入。管道用竖杠|表示。它的一般形式为:
命令1 |命令2
其中|是管道符号。
5.标准输入、输出和错误
当我们在s h e l l中执行命令的时候,每个进程都和三个打开的文件相联系,并使用文件描述符来引用这些文件。由于文件描述符不容易记忆, s h e l l同时也给出了相应的文件名。
下面就是这些文件描述符及它们通常所对应的文件名:
文件文件描述符
输入文件—标准输入stdin 0:它是命令的输入,缺省是键盘,也可以是文件或其他命令的输出。
输出文件—标准输出stdout 1:它是命令的输出,缺省是屏幕,也可以是文件。
错误输出文件—标准错误stderr 2:这是命令错误的输出,缺省是屏幕,同样也可以是文件。
如果没有特别指定文件说明符,命令将使用缺省的文件说明符(你的屏幕,更确切地说是你的终端)。
系统中实际上有1 2个文件描述符,但是正如我们在上表中所看到的, 0、1、2是标准输入、输出和错误。可以任意使用文件描述符3到9。
在执行命令时,可以指定命令的标准输入、输出和错误,要实现这一点就需要使用文件重定向。下面列出了最常用的重定向组合,并给出了相应的文件描述符。
在对标准错误进行重定向时,必须要使用文件描述符,但是对于标准输入和输出来说,这不是必需的。
常用文件重定向命令
command > filename 把标准输出重定向到一个新文件中
command >> filename 把标准输出重定向到一个文件中(追加)
command 1 > fielname 把标准输出重定向到一个文件中
command > filename 2>&1 把标准输出和标准错误一起重定向到一个文件中
command 2> filename 把标准错误重定向到一个文件中
command 2>> filename 把标准输出重定向到一个文件中(追加)
command >> filename 2>&1 把标准输出和标准错误一起重定向到一个文件中(追加)
command < filename >filename2 把c o m m a n d命令以f i l e n a m e文件作为标准输入,以f i l e n a m e 2文件作为标准输出
command < filename 把c o m m a n d命令以f i l e n a m e文件作为标准输入
command << delimiter 把从标准输入中读入,直至遇到d e l i m i t e r分界符
command <&m 把文件描述符m作为标准输入
command >&m 把标准输出重定向到文件描述符m中
command <&- 关闭标准输入
补充
File Descriptor (FD)
程式的運算,在大部份情況下都是進行數據(data)的處理,
這些數據?哪淖x進?又,送出到哪裡呢?
這就是 file descriptor (FD) 的功用了。
在 shell 程式中,最常使用的 FD 大概有三個,分別為:
0: Standard Input (STDIN)
1: Standard Output (STDOUT)
2: Standard Error Output (STDERR)
在標準情況下,這些 FD 分別跟如下設備(device)關聯:
stdin(0): keyboard
stdout(1): monitor
stderr(2): monitor
我們可以用如下下命令測試一下:
- $ mail -s test root
- this is a test mail.
- please skip.
- ^d (同時按 crtl 跟 d 鍵)
复制代码
很明顯,mail 程式所讀進的數據,就是? stdin 也就是 keyboard 讀進的。
不過,不見得每個程式的 stdin 都跟 mail 一樣? keyboard 讀進,
因為程式作者可以?臋n案參數讀進 stdin ,如:
但,要是 cat 之後沒有檔案參數則又如何呢?
哦,請您自己玩玩看囉.... ^_^
(請留意數據輸出到哪裡去了,最後別忘了按 ^d 離開...)
事?上,stderr 沒甚麼難理解的:說穿了就是"錯誤信息"要往哪邊送而已...
比方說,若讀進的檔案參數是不存在的,那我們在 monitor 上就看到了:
- $ ls no.such.file
- ls: no.such.file: No such file or directory
复制代码
若,一個命令同時產生 stdout 與 stderr 呢?
那還不簡單,都送到 monitor ?砭秃昧耍篬code]
$ touch my.file
$ ls my.file no.such.file
ls: no.such.file: No such file or directory
my.file
[/code]
okay,至此,關於 FD 及其名稱、還有相關聯的設備,相信你已經沒問題了吧?
那好,接下?碜屛覀兛纯慈绾胃淖冞@些 FD 的預設數據通道,
我們可用 < ?砀淖冏x進的數據通道(stdin),使之?闹付ǖ臋n案讀進。
我們可用 > ?砀淖兯统龅臄祿通道(stdout, stderr),使之輸出到指定的檔案。
比方說:
就是? my.file 讀進數據
- $ mail -s test root < /etc/passwd
复制代码
則是? /etc/passwd 讀進...
這樣一?恚瑂tdin 將不再是? keyboard 讀進,而是?臋n案讀進了...
嚴格?碚f,< 符號之前需要指定一個 FD 的(之間不能有空白),
但因為 0 是 < 的預設值,因此 < 與 0< 是一樣的﹗
那,要是用兩個 << 又是啥呢?
這是所謂的 HERE Document ,它可以讓我們輸入一段文本,直到讀到 << 後指定的字串。
比方說:
- $ cat <<FINISH
- first line here
- second line there
- third line nowhere
- FINISH
复制代码
這樣的話,cat 會讀進 3 行句子,而無需? keyboard 讀進數據且要等 ^d 結束輸入。
當你搞懂了 0< 原?砭褪歉淖 stdin 的數據輸入通道之後,相信要理解如下兩個 redirection 就不難了:
* 1>
* 2>
前者是改變 stdout 的數據輸出通道,後者是改變 stderr 的數據輸出通道。
兩者都是將原本要送出到 monitor 的數據轉向輸出到指定檔案去。
由於 1 是 > 的預設值,因此,1> 與 > 是相同的,都是改 stdout 。
用上面的 ls 例子?碚f明一下好了:
- $ ls my.file no.such.file 1>file.out
- ls: no.such.file: No such file or directory
复制代码
這樣 monitor 就只剩下 stderr 而已。因為 stdout 給寫進 file.out 去了。
- $ ls my.file no.such.file 2>file.err
- my.file
复制代码
這樣 monitor 就只剩下 stdout ,因為 stderr 寫進了 file.err 。
- $ ls my.file no.such.file 1>file.out 2>file.err
复制代码
這樣 monitor 就啥也沒有,因為 stdout 與 stderr 都給轉到檔案去了...
不過,有些地方還是要注意一下.
首先,是 file locking 的問題。比方如下這個例子:
- $ ls my.file no.such.file 1>file.both 2>file.both
复制代码
? file system 的角度?碚f,單一檔案在單一時間內,只能被單一的 FD 作寫入。
假如 stdout(1) 與 stderr(2) 都同時在寫入 file.both 的話,
則要看它們在寫入時否碰到同時競爭的情形了,基本上是"先搶先贏"的原則。
讓我們用"慢鏡頭"?砜匆幌 stdout 與 stderr 同時寫入 file.out 的情形好了:
* 第 1, 2, 3 秒為 stdout 寫入
* 第 3, 4, 5 秒為 stderr 寫入
那麼,這時候 stderr 的第 3 秒所寫的數據就?G失掉了﹗
要是我們能控制 stderr 必須等 stdout 寫完再寫,或倒過?恚瑂tdout 等 stderr 寫完再寫,那問題就能解決。
但?募夹g上,較難掌控的,尤其是 FD 在作"長期性"的寫入時...
那,如何解決呢?所謂山不轉路轉、路不轉人轉嘛,
我們可以換一個思維:將 stderr 導進 stdout 或將 stdout 導進 sterr ,而不是大家在搶同一份檔案,不就行了﹗
bingo﹗就是這樣啦:
* 2>&1 就是將 stderr ?氵M stdout 作輸出
* 1>&2 或 >&2 就是將 stdout ?氵M stderr 作輸出
於是,前面的錯誤操作可以改為:
- $ ls my.file no.such.file 1>file.both 2>&1
复制代码
或
- $ ls my.file no.such.file 2>file.both >&2
复制代码
在 Linux 檔案系統裡,有個設備檔位於 /dev/null 。
許多人都問過我那是甚麼玩意兒?那就是"空"啦﹗
沒錯﹗空空如也的空就是 null 了....
這個 null 在 I/O Redirection 中可有用得很呢:
* 若將 FD1 跟 FD2 轉到 /dev/null 去,就可將 stdout 與 stderr 弄不見掉。
* 若將 FD0 接到 /dev/null ?恚蔷褪亲x進 nothing 。
比方說,當我們在執行一個程式時,畫面會同時送出 stdout 跟 stderr ,
假如你不想看到 stderr (也不想存到檔案去),那可以:
- $ ls my.file no.such.file 2>/dev/null
- my.file
复制代码
若要相反:只想看到 stderr 呢?還不簡單﹗將 stdout 弄到 null 就行:
- $ ls my.file no.such.file >/dev/null
- ls: no.such.file: No such file or directory
复制代码
那接下?恚偃鐔渭冎慌艹淌剑幌肟吹饺魏屋敵鼋Y果呢?
除了用 >/dev/null 2>&1 之外,你還可以如此:
- $ ls my.file no.such.file &>/dev/null
复制代码
(提示:將 &> 換成 >& 也行啦~~! )
另外,看下面:
- $ echo "1" > file.out
- $ cat file.out
- 1
- $ echo "2" > file.out
- $ cat file.out
- 2
复制代码
看?恚覀冊谥貙 stdout 或 stderr 進一份檔案時,似乎永遠只獲得最後一次導入的結果。
那,之前的內容呢?
呵~~~ 要解決這個問提很簡單啦,將 > 換成 >> 就好:
$ echo "3" >> file.out
$ cat file.out
2
3
如此一?恚恢貙У哪繕藱n案之內容?K不會失去,而新的內容則一直增加在最後面去。
但,只要你再一次用回單一的 > ?碇貙У脑挘屈N,舊的內容還是會被"洗"掉的﹗
這時,你要如何避免呢?
----備份﹗ yes ,我聽到了﹗不過.... 還有更好的嗎?
- $ set -o noclobber
- $ echo "4" > file.out
- -bash: file: cannot overwrite existing file
复制代码
那,要如何取消這個"限制"呢?
哦,將 set -o 換成 set +o 就行:
- $ set +o noclobber
- $ echo "5" > file.out
- $ cat file.out
- 5
复制代码
再問:那... 有辦法不取消而又"臨時"蓋寫目標檔案嗎
- $ set -o noclobber
- $ echo "6" >| file.out
- $ cat file.out
- 6
复制代码
留意到沒有:在 > 後面再加個" | "就好(注意: > 與 | 之間不能有空白哦)....
再?磉有一個難題要你去參透的呢:
- $ echo "some text here" > file
- $ cat < file
- some text here
- $ cat < file > file.bak
- $ cat < file.bak
- some text here
- $ cat < file > file
- $ cat < file
复制代码
嗯?﹗注意到沒有?﹗﹗
---- 怎麼最後那個 cat 命令看到的 file 竟是空的?﹗
why? why? why?
要理解這一現像其?不難,這只是 priority 的問題而已:
* 在 IO Redirection 中,stdout 與 stderr 的管道會先準備好,才會? stdin 讀進資料。
也就是說,在上例中,> file 會先將 file 清空,然後才讀進 < file ,
但這時候檔案已經被清空了,因此就變成讀不進任何資料了...
哦~~~ 原?砣绱藒~~~ ^_^
那... 如下兩例又如何呢?
- $ cat <> file
- $ cat < file >> file
复制代码
pipe line
談到 pipe line ,我相信不少人都不會陌生:
我們在很多 command line 上常看到的" | "符號就是 pipe line 了。
不過,究竟 pipe line 是甚麼東東呢?
別急別急... 先查一下英?h字典,看看 pipe 是甚麼意思?
沒錯﹗它就是"水管"的意思...
那麼,你能想像一下水管是怎麼一根接著一根的嗎?
又,每根水管之間的 input 跟 output 又如何呢?
嗯??
靈光一閃:原? pipe line 的 I/O 跟水管的 I/O 是一模一樣的:
* 上一個命令的 stdout 接到下一個命令的 stdin 去了﹗
的確如此... 不管在 command line 上你使用了多少個 pipe line ,
前後兩個 command 的 I/O 都是彼此連接的﹗(恭喜:你終於開竅了﹗ ^_^ )
不過... 然而... 但是... ... stderr 呢?
好問題﹗不過也容易理解:
* 若水管漏水怎麼辦?
也就是說:在 pipe line 之間,前一個命令的 stderr 是不會接進下一命令的 stdin 的,
其輸出,若不用 2> 導到 file 去的話,它還是送到監視器上面?愆u
這點請你在 pipe line 運用上務必要注意的。
那,或許你又會問:
* 有辦法將 stderr 也餵進下一個命令的 stdin 去嗎?
方法當然是有,而且你早已學過了﹗ ^_^
我提示一下就好:
* 請問你如何將 stderr 合?氵M stdout 一同輸出呢?
或許,你仍意尤未盡﹗或許,你曾經碰到過下面的問題:
* 在 cm1 | cm2 | cm3 ... 這段 pipe line 中,若要將 cm2 的結果存到某一檔案呢?
若你寫成 cm1 | cm2 > file | cm3 的話,
那你肯定會發現 cm3 的 stdin 是空的﹗(當然啦,你都將水管接到別的水池了﹗)
聰明的你或許會如此解決:
cm1 | cm2 > file ; cm3 < file
是的,你的確可以這樣做,但最大的壞處是:這樣一?恚琭ile I/O 會變雙倍﹗
在 command 執行的整個過程中,file I/O 是最常見的最大效能殺手。
凡是有經驗的 shell 操作者,都會盡量避免或降低 file I/O 的頻率。
那,上面問題還有更好方法嗎?
有的,那就是 tee 命令了。
* 所謂 tee 命令是在不影響原本 I/O 的情況下,將 stdout 複製一份到檔案去。
因此,上面的命令行可以如此打:
cm1 | cm2 | tee file | cm3
在預設上,tee 會改寫目標檔案,若你要改為增加內容的話,那可用 -a 參數達成。
基本上,pipe line 的應用在 shell 操作上是非常?V泛的,尤其是在 text filtering 方面,
凡舉 cat, more, head, tail, wc, expand, tr, grep, sed, awk, ... 等等文字處理工具,
搭配起 pipe line ?硎褂茫銜驚覺 command line 原?硎腔畹萌绱司实末u
6.exec
e x e c命令可以用来替代当前s h e l l;换句话说,并没有启动子s h e l l。使用这一命令时任何现有环境都将会被清除,并重新启动一个s h e l l。它的一般形式为:
其中的c o m m a n d通常是一个s h e l l脚本。
我所能够想像得出的描述e x e c命令最贴切的说法就是:当这个脚本结束时,相应的会话可能就结束了。e x e c命令的一个常见用法就是在用户的. p r o f i l e最后执行时,用它来执行一些用于增强安全性的脚本。如果用户的输入无效,该s h e l l将被关闭,然后重新回到登录提示符。e x e c还常常被用来通过文件描述符打开文件。文件描述符:file descriptor(FD)
e x e c在对文件描述符进行操作的时候(也只有在这时),它不会覆盖你当前的s h e l l。
source和exec的区别
1,他们带的参数不一样
source通常是shell脚本,而exec不但可以把一个脚本当成参数,而且还可以把一个系统命令当参数,例如: exec ls
2,另外一个不同就是,exec任务执行完毕后,会执行类似logout的操作,而source执行完一个任务后返回当前的shell. |
|