存档在 ‘Shell学习笔记’ 分类

echo和backslash

2012年2月14日

echo命令是一个看似简单的命令,当它和转义,命令替换等相遇时,则会发生许多令人迷惑的现象。

1.echo \z和echo “\z”的不同

echo命令比较特殊,对于它本身而言,转义符加上某些字符会引发一些特殊含义,这与转义本身的含义又相反。比如\n在echo中表示回车换行,\t表示水平制表符等。不过,echo命令在bash中默认是不开启对这些特殊转义符进行解释的,如果要开启则必须加上-e选项。
对于下述的命令:

$ echo -e "\\thello"
	hello

它的执行过程是:

1).”\\thello”首先被bash解释为”\thello”。

2).”\thello”作为echo的参数,由于加入了-e,因此echo将\t解释为水平制表符。

3).显示结果。

上述命令发生了两次转义,第一即为执行过程1中bash处理命令行时,第二次即为2中echo处理自身参数时。

对于弱引用而言,双引号中的所有字符都被当做普通字符,除了\,$和反引号(本文所述的反引号就主键盘上数字1左边的符号)。

在对字符串不加引用的情况下,\会发挥其转义的特性,即显示字符字面含义,即便该字符是普通字符,因此echo \z的结果为z。而双引号中所有字符都被解释成shell解释成了普通字符,”\z”被解释的结果为\z,并且\z并不是echo的特定转义符号,因此echo “\z”的结果为\z。

edsionte@edsionte-laptop:~/mytest$ echo \z
+ echo z
z
edsionte@edsionte-laptop:~/mytest$ echo "\z"
+ echo '\z'
\z

这里你也许有疑问:弱引用是不能关闭$,\和反引号的特殊含义的,为何echo “\z”中的\没有起到转义的作用呢?

这里需要注意的是,弱引用中\符号仅对$,\,反引号和双引号这几个特殊字符起转义作用,而其他字符前的\均不起作用,也就是说shell会忽略它,将这个\作为普通字符。

edsionte@edsionte-laptop:~/mytest$ t=a
+ t=a
edsionte@edsionte-laptop:~/mytest$ echo "\$t"
+ echo '$t'
$t
edsionte@edsionte-laptop:~/mytest$ echo "\*t"
+ echo '\*t'
\*t

通过上述的分析,也就简单解释了为何echo \z和echo “\z”会有不同的结果。

2.echo `echo \z`的疑惑

接下来要说明的不是上述一条命令,而是关于它的“一系列”命令。

$ cat test2echo.sh
#!/bin/bash

echo `echo \z`
echo `echo \\z`
echo `echo \\\z`
echo `echo \\\\z`
echo `echo \\\\\\z`
echo `echo \\\\\\\z`
$ bash test2echo.sh
z
z
\z
\z
\z
\\z

这几条echo命令中均嵌套了另一个echo命令,命令替换使用的是反引号而不是$()。从上面的结果可以看出当echo遇到\时发生了一些奇怪的现象,比如为何第3,4,5条echo命令的结果是一致的。

在解释上述这几个echo命令之前,我们先看下面的脚本:

$ cat testecho.sh
#!/bin/bash

echo \z
echo \\z
echo \\\z
echo \\\\z
echo \\\\\\z
echo \\\\\\\z
$ bash testecho.sh
z
\z
\z
\\z
\\\z
\\\z

上面的脚本的结果是比较容易想到的。第一条echo中的\由于对z进行转义;第二条echo中的第一个\对第二个\进行转义;第三条echo中的第一个\对第二个\进行转义,第三个\对z进行转义;后续的echo命令的规则都是一致的。

如果将testecho.sh中的结果作为test2echo.sh中每个内层echo的输出,那么会不会得到test2echo.sh最终的结果呢?比如echo `echo \\\z`,内部echo的结果是\z,那么这个双层echo可以看作是echo \z。按照上述1中的讨论,结果应该是z。看来处理过程并不是如此。

那么echo `echo \\\z`是否可以看作是echo “\z”?根据上述1中的讨论结果,结果正确。不过对于echo `echo \\\\z`,按照这样的处理并不成立。

接下来我们使用set命令来追踪test2echo.sh的执行过程:

$ cat test2echo.sh
#!/bin/bash

set -x

echo `echo \z`
echo `echo \\z`
echo `echo \\\z`
echo `echo \\\\z`
echo `echo \\\\\\z`
echo `echo \\\\\\\z`
$ bash test2echo.sh
++ echo z
+ echo z
z
++ echo z
+ echo z
z
++ echo '\z'
+ echo '\z'
\z
++ echo '\z'
+ echo '\z'
\z
++ echo '\z'
+ echo '\z'
\z
++ echo '\\z'
+ echo '\\z'
\\z

通过加入-x选项我们可以发现内部echo的输出结果被单引号所引用,因此可以得知上述双层echo命令的结果都是内部echo的输出结果。因为每个内部echo的输出都通过单引号原封不动的作为外部echo的输入,并且单引号中的所有字符都是普通字符(除了单引号本身)。那么究竟是什么原因才导致这样令人迷惑的结果?

在上述问题1中我们已经说明过双引号中\对$,反引号,双引号和\起到转义作用,即显示这些字符的字面意思,那么这几个特殊字符前的\也将抽离所在字符串。同样,反引号中也存在这样的问题,如果反引号中有\,并且\后紧跟的字符是$,反引号,双引号和\这些字符时,那么这些特殊字符前的\将抽离。

按照上述的解释,test2echo.sh中各个echo的执行过程就明了了。以echo `echo \\\z`为例,它的执行过程如下:

1).\\\z由于反引号中的\前出现了\,因此第二个\被抽离,原始命令成为echo `echo \\z`。

2).执行反引号中的echo,原始命令成为echo ‘\z’

3).执行外部echo,结果为\z。

再举一例,比如上述的echo `echo \\\\\\\z`,它的执行过程如下:

1).内部\\\\\\\z由于反引号中的\前又出现了\,因此有三个\被抽离,原始命令成为echo `echo \\\\z`。

2).执行反引号中的echo,原始命令成为echo ‘\\z’

3).执行外部echo,结果为\\z。

其他命令的执行过程也是如此。每个命令中的反引号部分都先经过\的抽离,接着bash对字符串进行转移处理,再输入内部echo;内部echo处理完成后,传递给外部echo,最终输出结果。上述过程中的2)和3)均可以通过set -x来追踪。

 

从修改文件扩展名到子串删除

2012年1月30日

之前在这篇文章中已经列举了一种通用的批量修改文件后缀名的方法,今天看到变量替换${var%pattern},因此又有下面的一种方法:

#!/bin/bash

if [ $# -ne 3 ]
then
	echo "Usage:$(basename $0) old_name new_name dir"
	exit -1
fi

old_name="$1"
new_name="$2"
dir="$3"

for file in $(ls *.$old_name)
do
	newfile=${file%.$old_name}.$new_name
	mv $file $newfile
	echo "$file ===> $newfile"
done

exit 0

该脚本用到了变量替换中的子串删除方法:${var%pattern}。它的作用是从变量$var的末尾删除最小匹配$pattern字符串。与此对应的方法${var%%pattern}则是删除最大匹配$pattern字串。比如:

edsionte@edsionte-laptop:~/mytest$ var=abcd12345abc6789
edsionte@edsionte-laptop:~/mytest$ pattern=b*9
edsionte@edsionte-laptop:~/mytest$ echo "${var%$pattern}"
abcd12345a
edsionte@edsionte-laptop:~/mytest$ echo "${var%%$pattern}"
a

如果想从变量$var开头删除字符串$pattern,则可以使用${var#pattern}删除最小匹配的字符串,而${var##pattern}则可以删除最大匹配$pattern的字符串。比如:

edsionte@edsionte-laptop:~/mytest$ var=abcd12345abc6789
edsionte@edsionte-laptop:~/mytest$ pattern=a*c
edsionte@edsionte-laptop:~/mytest$ echo "${var#$pattern}"
d12345abc6789
edsionte@edsionte-laptop:~/mytest$ echo "${var##$pattern}"
6789

从变量中删除指定字符串是一种常见的操作。

删除日志文件的经典脚本

2012年1月29日

本文所示的删除/var/log下日志文件脚本源于abs这本书,虽然实际功能只是简单的清空该目录下的messages文件,但是这个脚本具有广义性,可以作为其他脚本的模板。

#!/bin/bash

LOG_DIR=/var/log
ROOT_UID=0
LINES=50
E_XCD=66
E_NOTROOT=67

为了更好的移植性,事先定义几个变量。比如用LOG_DIR指定日志所在目录,E_开头的变量为不同的错误码。

if [ "$UID" -ne "$ROOT_UID" ]
then 
	echo "Must be root to run this script."
	exit $E_NOTROOT
fi

if [ -n "$1" ]
then 
	lines=$1
else
	lines=$LINES
fi

由于删除/var/log下的文件需要root身份,因此一开始先判断运行该脚本的用户是否为root。接着判断参数1是否为空,lines指定默认日志保留的行数。

cd $LOG_DIR

if [ `pwd` != "$LOG_DIR" ]
then
	echo "Can't change to $LOG_DIR."
	exit $E_XCD
fi

判断是否成功进入了LOG_DIR所代表的目录。上述代码的更有效率的表示方法为:

# cd /var/log || {
#   echo "Cannot change to necessary directory." >&2
#   exit $E_XCD;
# }

最后才是实质性的日志删除功能:

tail -$lines messages > mesg.temp
mv mesg.temp messages

exit 0

最后只保留messages文件中lines行日志。

该脚本的功能虽然简单,但是却拥有用户身份检查、参数检查等功能,对其他脚本而言是个很好的模板。

shell批量替换文件扩展名

2011年10月8日

早上本想将一些照片上传到相册中,但是由于所有照片的扩展名都是JPG而不是小写的jpg,因此造成了“格式不正确”而不能上传照片。此刻就产生了这样一个问题:使用shell脚本如何批量将所有文件的扩展名JPG都改成小写的jpg?

既然要批量替换文件名,那么肯定得用一个for循环依次遍历指定目录下的每个文件。对于每个文件,假如该文件的名称为name.oldext,那么我们必须原始文件名中挖出name,再将它与新的文件扩展名newext拼接形成新的文件名name.newext。依照这样的思路,就诞生了下面的脚本:

#!/bin/bash
oldext="JPG"
newext="jpg"

dir="/home/edsionte/mypic"
cd $dir

for file in $(ls $dir | grep .$oldext)

	do
	name=$(ls $file | cut -d. -f1)
	mv $file ${name}.$newext
	done

下面对针对这个程序作简单说明:

1.变量oldext和newext分别指定旧的扩展名和新的扩展名。dir指定文件所在目录;

2.“ls $dir | grep .$oldext”用来在指定目录dir中获取扩展名为旧扩展名的所有文件;

3.在循环体内先利用cut命令将文件名中“.”之前的字符串剪切出来,并赋值给name变量;接着将当前的文件名重命名为新的文件名。

通过这个脚本,所有照片的扩展名都成功修改。为了使这个脚本更具有通用型,我们可以增加几条read命令实现脚本和用户之间的交互。改进版的脚本如下:

#!/bin/bash

read -p "old extension:" oldext
read -p "new extension:" newext
read -p "The directory:" dir
cd $dir

for file in $(ls $dir | grep .$oldext)

	do
	name=$(ls $file | cut -d. -f1)
	mv $file ${name}.$newext
	echo "$name.$oldext ====> $name.$newext"
	done
echo "all files has been modified."

修改后的脚本可以批量修改任意扩展名。done。

shell中的环境变量和自定义变量

2011年10月6日

Shell中的变量可以简单分为环境变量和自定义变量。环境变量有时也被称为全局变量,它是操作系统为Shell事先定义的一组变量,这些变量共同描述了当前Shell运行的系统环境;而自定义变量则是用户根据所需而定义的变量,它也被称为局部变量。为了区分两者的不同,环境变量通常用大写字母表示,而自定义变量通常使用小写子母表示。

1.环境变量

环境变量是一组变量的集合,它们描述了当前Shell运行的环境信息。最典型的环境变量即为PATH,它描述了可执行文件的路径信息。通过env命令可以查看当前Shell环境下所有环境变量及其内容。

edsionte@edsionte-desktop:~$ env
TERM=xterm
SHELL=/bin/bash
USER=edsionte
USERNAME=edsionte
PATH=/home/edsionte/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/edsionte/bin
PWD=/home/edsionte

…………

下面对部分环境变量做以解释:

HOME:表示当前用户的主目录。当输入cd或cd ~命令时,就会用到这个变量,如果更改了这个变量的值,那么使用上述命令的结果也会相应的改变。

SHELL:表示当前的shell环境所使用的shell程序。常见的shell程序有/bin/bash和/bin/sh,Linux中默认使用的是/bin/bash。

PWD:shell当前所在的工作目录,这个变量的值是通过pwd命令得到的。

USER:当前的用户名。

2.自定义变量

Shell除了环境变量,还包括用户自定义的变量。env命令查看的只是所有环境变量,而set命令既可以查看环境变量也可以查看自定义变量。比如:

edsionte@edsionte-desktop:~$ myvar=test
edsionte@edsionte-desktop:~$ env | grep myvar
edsionte@edsionte-desktop:~$ set | grep myvar
myvar=test

3.环境变量和自定义变量之间的转换

环境变量有在Linux下的shell程序默认为Bash,因此每一个Bash其实都是一个进程,在当前Bash下输入ps命令则可以查看该Bash的PID。对于环境变量而言,每个Bash进程都可以对其进行引用;而对于用户自定义的变量,只有对其定义的Bash进程可以引用它,其他的Bash进程甚至是其子进程均不可以引用。这也是环境变量和自定义变量的的主要区别之一。

从进程内存映像的结构分布可以很好的解释环境变量和自定义变量之间的差异。在Linux系统中,所有的Bash进程都是gnome-terminal进程的孩子或孙子,而gnome-terminal进程是由init进程创建的,由于环境变量保存在gnome-terminal进程的数据段,因此它的孩子或孙子(即Bash进程的孩子)均可以继承数据段的数据,即所有Bash进程都可以访问环境变量。对于自定义变量而言,它位于每个Bash进程的栈中,而每个进程它都有自己独立的段,因此自定义变量是不能被继承的,即自定义变量不能被其他bash进程引用。

那么,环境变量和自定义变量之间如何相互转换呢?通过export命令和declare命令即可完成。如果要将一个自定义变量转化为环境变量,则需要使用下述命令:

edsionte@edsionte-desktop:~$ myvar=test
edsionte@edsionte-desktop:~$ env | grep myvar
edsionte@edsionte-desktop:~$ export myvar
edsionte@edsionte-desktop:~$ env | grep myvar
myvar=test

如果要将环境变量转化为自定义变量,则需使用下述命令:

edsionte@edsionte-desktop:~$ declare +x myvar
edsionte@edsionte-desktop:~$ env | grep myvar
edsionte@edsionte-desktop:~$ echo $myvar
test

关于exprot和declare命令更多的用法,可以参考man手册。

windows 7 ultimate product key

windows 7 ultimate product key

winrar download free

winrar download free

winzip registration code

winzip registration code

winzip free download

winzip free download

winzip activation code

winzip activation code

windows 7 key generator

windows 7 key generator

winzip freeware

winzip freeware

winzip free download full version

winzip free download full version

free winrar download

free winrar download

free winrar

free winrar

windows 7 crack

windows 7 crack

windows xp product key

windows xp product key

windows 7 activation crack

windows7 activation crack

free winzip

free winzip

winrar free download

winrar free download

winrar free

winrar free

download winrar free

download winrar free

windows 7 product key

windows 7 product key