if echo "$VAR" | grep -q txt # if [[ $VAR = *txt* ]]
then
echo "$VAR contains the substring sequence \"txt\""
fi
a=`echo "HELLO" | tr A-Z a-z`
需要注意的是 ``echo command``` 会删除 command` 中生成的所有换行符。
bash$ ls -l /usr/share/apps/kjezz/sounds
-rw-r--r-- 1 root root 1407 Nov 7 2000 reflect.au
-rw-r--r-- 1 root root 362 Nov 7 2000 seconds.au
bash$ echo `ls -l /usr/share/apps/kjezz/sounds`
total 40 -rw-r--r-- 1 root root 716 Nov 7 2000 reflect.au -rw-r--r-- 1 root root ...
# 嵌入一个换行符?
echo "Why doesn't this string \n split on two lines?"
# 不会分割。
# 让我们尝试一些其他的。
echo
echo $"A line of text containing
a linefeed."
# 打印不同的两行(嵌入换行符)。
# 但是,变量前缀的"$"符号真的必需吗?
echo
echo "This string splits
on two lines."
# 其实,并不需要"$"。
echo
echo "---------------"
echo
echo -n $"Another line of text containing
a linefeed."
# 打印不同的两行(嵌入换行符)。
# 即便是 -n 参数也无法取消这里的换行符。
echo
echo
echo "---------------"
echo
echo
# 然而,以下这行并没有实现预期的效果。
# 为什么?提示:赋值给一个变量。
string1=$"Yet another line of text containing
a linefeed (maybe)."
echo $string1
# 又一行包含换行的文本(也许)。
# ^^^
# 换行符变成了一个空格。
# 感谢Steve Parker指出。
bash$ type -a echo
echo is a shell builtin
echo is /bin/echo
#!/bin/bash
echo
echo "Enter a string terminated by a \\, then press <ENTER>."
echo "Then, enter a second string (no \\ this time), and again press <ENTER>."
read var1 # 当读取$var1时,"\"忽略换行符。
# first line \
# second line
echo "var1 = $var1"
# var1 = first line second line
# 对于以 “\” 结尾的每一行,
# 你都会在下一行出现提示,以继续将字符输入到var1中。
echo; echo
echo "Enter another string terminated by a \\ , then press <ENTER>."
read -r var2 # -r选项会使 "\" 以字面读取。
# first line \
echo "var2 = $var2"
# var2 = first line \
# 数据输入以第一个 <ENTER> 终止。
echo
exit 0
read命令有一些有趣的选项,允许在不敲击ENTER键的情况下输出提示甚至读取击键。
# 在不敲击ENTER的情况下读取击键。
read -s -n1 -p "Hit a key " keypress
echo; echo "Keypress was "\"$keypress\""."
# -s 选项表示不要输出输入。
# -n N 选项表示仅接受N个字符输入。
# -p 选项表示在读取输入之前输出以下提示。
# 使用这些选项比较棘手,因为它们需要按照正确的顺序。
read的-n选项同样支持检测箭头键以及其他某些不常用的键。
样例 15-6. 检测箭头键
#!/bin/bash
# arrow-detect.sh: 检测到箭头键,以及其他键。
# 感谢Sandro Magi告诉我怎么实现。
# --------------------------------------------
# 按键生成的字符代码。
arrowup='\[A'
arrowdown='\[B'
arrowrt='\[C'
arrowleft='\[D'
insert='\[2'
delete='\[3'
# --------------------------------------------
SUCCESS=0
OTHER=65
echo -n "Press a key... "
# 如果按下了上面未列出的键,可能还需要按ENTER键。
read -n3 key # 读取三个字符。
echo -n "$key" | grep "$arrowup" #检查是否检测到字符代码。
if [ "$?" -eq $SUCCESS ]
then
echo "Up-arrow key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$arrowdown"
if [ "$?" -eq $SUCCESS ]
then
echo "Down-arrow key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$arrowrt"
if [ "$?" -eq $SUCCESS ]
then
echo "Right-arrow key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$arrowleft"
if [ "$?" -eq $SUCCESS ]
then
echo "Left-arrow key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$insert"
if [ "$?" -eq $SUCCESS ]
then
echo "\"Insert\" key pressed."
exit $SUCCESS
fi
echo -n "$key" | grep "$delete"
if [ "$?" -eq $SUCCESS ]
then
echo "\"Delete\" key pressed."
exit $SUCCESS
fi
echo " Some other key pressed."
exit $OTHER
# ========================================= #
# Mark Alexander想出了上述脚本的简化版 (谢谢!)。
# 它消除了对grep的需要。
#!/bin/bash
uparrow=$'\x1b[A'
downarrow=$'\x1b[B'
leftarrow=$'\x1b[D'
rightarrow=$'\x1b[C'
read -s -n3 -p "Hit an arrow key: " x
case "$x" in
$uparrow)
echo "You pressed up-arrow"
;;
$downarrow)
echo "You pressed down-arrow"
;;
$leftarrow)
echo "You pressed left-arrow"
;;
$rightarrow)
echo "You pressed right-arrow"
;;
esac
exit $?
# ========================================= #
# Antonio Macchi有一个更简洁的替代方案。
#!/bin/bash
while true
do
read -sn1 a
test "$a" == `echo -en "\e"` || continue
read -sn1 a
test "$a" == "[" || continue
read -sn1 a
case "$a" in
A) echo "up";;
B) echo "down";;
C) echo "right";;
D) echo "left";;
esac
done
# ========================================= #
# 练习:
# --------
# 1) 添加对"Home"、"End"、"PgUp"和"PgDn"键的检测。
#!/bin/bash
read var1 <data-file
echo "var1 = $var1"
# var1设置为输入文件"data-file"的整个第一行
read var2 var3 <data-file
echo "var2 = $var2 var3 = $var3"
# 注意这里"read"的反直觉行为。
# 1) 倒回输入文件的开头。
# 2) 现在每个变量将设置为相应的字符串,
# 以空白符分割,而不是文本的一整行。
# 3) 最后一个变量获取该行的余数。
# 4) 如果要设置的变量比文件第一行的空格终止的字符串多,
# 则多余的变量保持为空。
echo "------------------------------------------------"
# 如何用循环解决上述问题:
while read line
do
echo "$line"
done <data-file
# 感谢Heiner Steven指出。
echo "------------------------------------------------"
# 如果您不希望默认值为空格,
# 请使用$IFS(内部字段分隔符变量)将输入行拆分后传递给read。
echo "List of all users:"
OIFS=$IFS; IFS=: # /etc/passwd使用":"作为字段分隔符。
while read name passwd uid gid fullname ignore
do
echo "$name ($fullname)"
done </etc/passwd # I/O重定向。
IFS=$OIFS # 恢复原先的$IFS。
# 这代码片段由Heiner Steven所写。
# 如果将$IFS变量设置在循环体中,
# 则无需将原始$IFS存储在临时变量中。
# 感谢Dim Segebart指出。
echo "------------------------------------------------"
echo "List of all users:"
while IFS=: read name passwd uid gid fullname ignore
do
echo "$name ($fullname)"
done </etc/passwd # I/O重定向。
echo
echo "\$IFS still $IFS"
exit 0
cat file1 file2 |
while read line
do
echo $line
done
#!/bin/bash
dir1=/usr/local
dir2=/var/spool
pushd $dir1
# 将自动执行"dirs"(将目录堆栈列表输出到标准输出)。
echo "Now in directory `pwd`." # 使用反引号括起来的'pwd'。
# 现在,在目录"dir1"中做一些事情。
pushd $dir2
echo "Now in directory `pwd`."
# 现在,在目录"dir2"中做一些事情。
echo "The top entry in the DIRSTACK array is $DIRSTACK."
popd
echo "Now back in directory `pwd`."
# 现在,在目录"dir1"中再做一些事情。
popd
echo "Now back in original working directory `pwd`."
exit 0
# 如果你不执行'popd' -- 然后退出该脚本会发生什么呢?
# 你会处在哪个目录下呢?为什么?
变量命令
let
样例 15-10. 让let来做算术运算
#!/bin/bash
echo
let a=11 # 等价于'a=11'
let a=a+5 # 等价于let "a = a + 5"
# (双引号和空格可以使其更易于阅读。)
echo "11 + 5 = $a" # 16
let "a <<= 3" # 等价于let "a = a << 3"
echo "\"\$a\" (=16) left-shifted 3 places = $a"
# 128
let "a /= 4" # 等价于let "a = a / 4"
echo "128 / 4 = $a" # 32
let "a -= 5" # 等价于let "a = a - 5"
echo "32 - 5 = $a" # 27
let "a *= 10" # 等价于let "a = a * 10"
echo "27 * 10 = $a" # 270
let "a %= 8" # 等价于let "a = a % 8"
echo "270 modulo 8 = $a (270 / 8 = 33, remainder $a)"
# 6
# "let"允许C风格的操作符吗?
# 是的,正如(( ... ))双括号结构可以。
let a++ # C风格(后置)增加。
echo "6++ = $a" # 6++ = 7
let a-- # C风格减少。
echo "7-- = $a" # 7-- = 6
# 当然,++a等也是允许的 . . .
echo
# 三元运算符。
# 参见上方代码,$a=6。
let "t = a<7?7:11" # True
echo $t # 7
let a++
let "t = a<7?7:11" # False
echo $t # 11
exit
#!/bin/bash
# revposparams.sh: 反转位置参数。
# 该脚本由Dan Jacobson所写,由本书作者对代码进行美化。
set a\ b c d\ e;
# ^ ^ 转义的空格
# ^ ^ 未转义的空格
OIFS=$IFS; IFS=:;
# ^ 保存旧IFS并且设置一个新的。
echo
until [ $# -eq 0 ]
do # 单步执行位置参数。
echo "### k0 = "$k"" # 执行前
k=$1:$k; # 将每个位置参数附加到循环变量。
# ^
echo "### k = "$k"" # 执行后
echo
shift;
done
set $k # 设置新的位置变量。
echo -
echo $# # 位置变量的数量。
echo -
echo
for i # 省略"in list"结构会将变量 -- i --
# 作为位置参数。
do
echo $i # 展示新的位置参数。
done
IFS=$OIFS # 恢复IFS。
# 问题:
# 是否有必要设置一个新的IFS,即内部字段分隔符,
# 来使此脚本正常工作?
# 如果不设置会怎样?请尝试一下。
# 并且,为什么要在第17行要使用 -- 冒号 -- 新的IFS
# 来附加到循环变量。
# 此目的究竟为何?
exit 0
$ ./revposparams.sh
### k0 =
### k = a b
### k0 = a b
### k = c a b
### k0 = c a b
### k = d e c a b
-
3
-
d e
c
a b
bash$ set
AUTHORCOPY=/home/bozo/posts
BASH=/bin/bash
BASH_VERSION=$'2.05.8(1)-release'
...
XAUTHORITY=/home/bozo/.Xauthority
_=/etc/bashrc
variable22=abc
variable23=xzy
#!/bin/bash
# self-exec.sh
# 注意:将该脚本的权限设为 555 或者 755,
# 然后执行 ./self-exec.sh 或者 sh ./self-exec.sh 进行调用。
echo
echo "This line appears ONCE in the script, yet it keeps echoing."
echo "The PID of this instance of the script is still $$."
# 演示子shell没有派生。
echo "==================== Hit Ctl-C to exit ===================="
sleep 1
exec $0 # 产生与该脚本完全相同的另一个实例来替换上一个。
echo "This line will never echo!" # 为什么不会?
exit 99 # 并不会在这里退出!
# 退出码不可能是99!