• Shell(Bash)学习笔记(一)

    以前只是简单看过一些文章,了解一些。平时也就是写个简单的命令组合啥的,没写过复杂的脚本。最近一次为了恢复数据,写了一下脚本,发现这东西光学不练确实不行。顺便记一下学习笔记备用。

    约定:本文中的shell特指bash.

    由于javaeye博客编辑器没有提供shell代码格式,就只好勉强用javascript脚本格式插入代码。

    一.变量

    变量直接赋值,不用提前声明。

     

    var="Hello"
    var=Hello
    var='Hello'
    

     

     

    注意几点:

    1.变量和值之间不能有空格,否则解释器会认为是几个命令。很多程序员的习惯是在=号两边留空格为了好看,但这点在shell中行不通。

    2.字符串不必用”号或者’,上面的几种赋值方式是等价的。除非字符串之间有空格的时候。

    如:

     

    var="Hello World"
    

     这时候就需要用引号。

     

    3.

    var=
    

     这样的语句也是合法的,表示var的值为空。

     

     

    4.使用变量的时候需要在变量前面加上$符号。这一点php程序员比较熟悉。

    如:

    echo $var
    

     

     这也就是为什么shell中的字符串不必用引号的原因。你如果直接运行

     

    echo var
    

     

     

    系统会将var当作字符串,而不是变量处理。在变量前加上$号还有个好处就是在字符串中输出变量的时候,直接在字符串里面用变量就行。

    如:

    var=World
    var2="$var World"
    echo $var2
    

     

     

    但与php不同的是变量赋值的时候不用加$符号。因为shell中没有==这个符号,判断相等也是用=号。如果赋值的时候加上$号,会产生混淆。

    还有一点要注意的就是单引号(’)字符串 中的变量不会被替换。

     

     

    var=World
    var2='$var World'
    echo $var2
    

     

     

    上面的语句输出结果:$var World

    这个可以用来输出$等特殊符号,而不用担心字符被当作变量替换。

    二.语句

    1.shell的语句不必用;号结束,除非是同一行写几个语句的时候。

    2.shell中的语句块不用{}号扩起来。if语句一般就用fi表示结束。当然这个也有特例,后面会提到。

    3.if语句

    if [ condition ]
    then
        action
    elif [ condition2 ]
    then
        action2
    .
    .
    .
    elif [ condition3 ]
    then
    else
        actionx
    fi
    

     

     

    需要注意的是shell中没有elseif,而是elif(这个缩写也太变态了吧,有必要么?).

    每个或者elif后跟着then,并且要换行或者用;分开。then可以和后面的action在一行。

     

    3.for循环

    var="one two three four"
    for x in $var
    do
         echo $x
    done
    

     

     for循环用do  和 done表示开始结束,不要举一反三,认为是用 rof结束。

    4.while和util循环

     

    while [ condition ]
     do
         statements
     done
    

     

    until [ condition ]
     do
         statements
     done
    

     

     

    5.条件语句

    shell中的条件语句用[]号括起来,用于if,while,until等结构。

     条件判断用=号,而不是==号。条件语句与[] 号之间要有空格分开。

    如:

    gender="boy"
    if [ "$gender" = "girl" ]
    then
          echo 'Welcome!'
    else
          echo 'We only welcome girl.'
    fi
    

     

     

    还有要注意的是条件语句中=号两旁要有空格分开,否则shell会将条件语句整个作为一个字符串处理,条件永远为真。条件语句中的变量最好用”号引
    起来,否则如果该变量中有空格,shell就会报too many arguments错误。如果变量正好为空,则会报 =: unary
    operator expected.错误。因为变量为空的话,条件语句少了一边,当然会出错。所以,将字符串变量用双引号括起来是shell编程的好习惯,尤其在条件语句中。

     

    其他的比较符号,如 >,号在shell中有特殊含义。下面是shell的比较运算符号表示方法:

     

     

    算术比较运算符
    
    num1-eq num2 	等于	[ 3 -eq $mynum ]
    num1-ne num2 	不等于	[ 3 -ne $mynum ]
    num1-lt num2 	小于	[ 3 -lt $mynum ]
    num1-le num2 	小于或等于	[ 3 -le $mynum ]
    num1-gt num2 	大于	[ 3 -gt $mynum ]
    num1-ge num2 	大于或等于	[ 3 -ge $mynum ]
    

     

    字符串比较运算符
    
    -z string 	如果 string长度为零,则为真 	[ -z "$myvar" ]
    -n string 	如果 string长度非零,则为真 	[ -n "$myvar" ]
    string1= string2 	如果 string1与 string2相同,则为真 	[ "$myvar" = "one two three" ]
    string1!= string2 	如果 string1与 string2不同,则为真 	[ "$myvar" != "one two three" ]
    

    6.case 语句

    gender="boy"
    case "$gender" in
          boy)
                echo "We only welcome girl."
                ;;
          girl)
                echo 'Welcome !'
                ;;
          *)
                echo "unknow."
                ;;
     esac  
    

     

     

    case语句的语法比较怪,乍一看比较别扭。每个pattern用 半括号括起来,用;;结束。

     

    三.算术

    shell 默认是用来处理字符串的,所以如果你直接运行:

     

    echo 1 1
    

     

     它会直接输出1 1,而不会输出2。 如果需要计算表达式的值,则只需用”$((” 和 “))”将表达式
    括起。

     

    echo $((1 1))
    

     

     

    四.函数

     

    add(){
        result=0
        for n in $*
        do
            result=$(($result $n))
        done
        return $result
    }
    

     执行:

    add 1 2 3
    echo $?
    echo $result
    

     

     

    两个输出结果都是:6.

    这里需要几点说明.shell中是不能直接获得函数的返回值的,如果你要用函数返回值,只能用全局变量传输。
    shell中的变量默认都是全局的,除非你在前面加了local修饰符。如上面的例子,在函数外面,result变量也是可见的。如果你在result前
    加local修饰,result变量在函数外就不可见了。但shell会把函数返回值放在$?全局变量中,你可以用$?
    来取得前个函数调用的返回值。$*可以获得函数的所有输入参数,$1表示第一个参数,以此类推。

     

    五.其他

    shell内置的一些特征,可以很容易的处理文件,以及和其他程序交互。

     

    文件比较运算符
    
    -e filename 	如果 filename存在,则为真 	[ -e /var/log/syslog ]
    -d filename 	如果 filename为目录,则为真 	[ -d /tmp/mydir ]
    -f filename 	如果 filename为常规文件,则为真 	[ -f /usr/bin/grep ]
    -L filename 	如果 filename为符号链接,则为真 	[ -L /usr/bin/grep ]
    -r filename 	如果 filename可读,则为真 	[ -r /var/log/syslog ]
    -w filename 	如果 filename可写,则为真 	[ -w /var/mytmp.txt ]
    -x filename 	如果 filename可执行,则为真 	[ -L /usr/bin/grep ]
    filename1-nt filename2 	如果 filename1比 filename2新,则为真 	[ /tmp/install/etc/services -nt /etc/services ]
    filename1-ot filename2 	如果 filename1比 filename2旧,则为真 	[ /boot/bzImage -ot arch/i386/boot/bzImage ]
    

     

     for 循环中很容易遍历文件

     

    for file in /home/*
    do
       if [ -d "$file" ]
       then
          echo $file is a directory
        fi
    done
    

     

     很容易调用其他程序的输出结果:

     

    for user in `awk -F":" '{ print $1 }' /etc/passwd`
    do
        echo find user $user
    done
    

     shell脚本中要使用其他命令的输出结果,只需用`符号把命令包含起来。注意:这个符号不是单引号,在键盘左上角那个位置。

     

     

    六. 后记

    基本的shell语法就学了这些,高级的还没弄通。这篇笔记也差不多长了,别的再边学边写。shell是基础,要配合awk,grep,sed这些工具才能发挥出效果。最近在看《unix编程艺术》,上面谈到了*nix系统的几个哲学基础原则,其中有个原则就是:

    组合原则:设计时考虑拼接组合

    *nix系统程序的的输入和输出一般都是简单,文本化,面向流的格式。这样便于程序和程序之间的交互和拼接。*nix系统下的程序一般都只完成单一功能,如果你需要要一个复杂的功能,那就需要把小程序拼接在一起。这一特征也决定了shell在*uix系统中的重要性。

     

    七.shell学习资料

    1.IBM Shell编程系列 

    本文中的许多例子以及资料就是来自该系列教程

    2.Bash 参考手册

    Gnu的bash官方参考手册

  • 一次linux服务器管理的惨痛教训

    系统用的是fedroa8,机房给装系统的时候,分区按默认方式,用lvm管理。

    后来一次机房给拔了一下电,估计文件系统哪儿出问题
    了,磁盘全部变成只读。然后我想检查一下磁盘,运行了一下fsck,结果检查失败,而文件系统又被卸载掉了,所有命令都用不了。只好让机房给重启一下,然
    后系统就起不来了。怀疑机房重启系统的时候发现系统没反应,多起了几下,导致系统检查硬盘的过程被中断。

    为了保存数据,我只好重新加了块硬盘,把系统装在新硬盘上,挂上旧硬盘,我慢慢恢复数据。

    先是遇到lvm的分区挂载问题,这东西不能像别的分区一样直接挂在。网上查了一下:

    先用vgscan,找到虚拟卷,然后用 vgchange -ay 激活虚拟卷,/dev下就有了虚拟卷设备。  默认是/dev/VolGroup00/LogVol00 /dev/VolGroup00/LogVol01这样的。


    mount  /dev/VolGroup00/LogVol00 /root/vg0 挂载,结果报错:mount: you must specify the filesystem type


    fdisk查看
    了一下,发现虚拟卷识别出来了,但就是分区表被破坏了,系统识别不出分区格式。只好再用fsck修复。不停提示,开始还看,后来烦了,加了个   
    fsck
    -y。好,一路顺利,检查完了。mount上虚拟卷,进入目录,傻眼了,分区下只剩下一个lost found文件夹,别的啥都没了。进到
    lost found下面,ls了一下,等了半天才显示出来结果。原来所有文件都被移到这个文件下了,并且都重命名为以#开头,一串数字结尾的文件,好几
    万文件。欲哭无泪啊。

    无奈间,试着grep了一下,发现还好,有的文件夹名字变了但结构还在。赶忙写了个shell脚本恢复数据。幸运的是,主要数据基本上都恢复了。

    总结了一下教训:

    1。fsck不能乱用。先要把文件系统umount掉,然后检查。最好启动到单用户模式下fsck。

    2。
    不必要的时候不要用lvm。真不知道fedora为什么默认用lvm,为了实验技术?这个鬼东西把硬盘弄成一块,要坏全坏,还影响磁盘io性能。网上看到
    还有把boot文件夹也放到lvm下的,这样系统起不来的时候,你想手动加载一下内核地址,你都不知道内核地址应该怎么表示。

    3。分区和备份太重要了。数据库的datadir要手动指定到放数据的分区下,这样重装系统的时候,数据不会丢失。etc目录下的配置文件要备份,不然系统重装后,重新配置一边也够受的。

     

    平时自己本地用linux,服务器管理经验不多,让老鸟看了笑话。吃一堑长一智,我这也算长了一智。

  • 一个纯java的H.264播放器–Onavia Video Player

    在网上闲逛,遇到一个纯java的H.264播放器,看了它的官方网站上的applet,效果确实不错。

     

    支持以下几种格式:

    • .mp4 standard H.264 file format
    • .mpeg standard H.264 file format
    • .h264 standard H.264 file format
    • .avi generic video file extension

    有以下特征:

     

     

    • High definition video. The Onavia H.264 Java Video
      Player plays the standard resolution and high definition video files
      that conform to the latest H.264 video compression standard.
    • End-user controls. The player has all end-user
      functions including: play, pause, rewind, fast forward, video duration,
      elapsed time, clickable progress bar.
    • Open APIs. Video publishers and web developers can
      customize the entire player (e.g. look & feel, colors, borders,
      window size, video resolution, streaming bandwidth, end-user controls).
      The player is fully customizable, and allows a publisher not only to
      control the end user experience, but also gives flexibility to rapidly
      change the end user experience to keep their website and video viewing
      experience fresh. It also allows a publisher to create different end
      user viewing experiences for different websites or within different
      sections of the same website.
    • Cost-effective delivery of high quality video.
      Enables publishers and web developers to implement H.264 quality video
      into a website without the need to implement costly Flash servers.
    • Can pull a video file from multiple domains/servers
    • Platform independence. It works on PCs, Macs, and all popular browser (Internet Explorer, Mozzilla, Firefox)
    • Device independence. Onavia H.264 Java Video
      Player can be easily ported to a variety of mobile devices and
      environments. The only requirement is that a device supports Java.
    • Automatic Bandwidth detection. The player
      automatically senses the available bandwidth at the end user device and
      adjusts the buffering and streaming to deliver the best possible
      viewing experience.

     

    官方网站:

     

    java在多媒体方面很让人失望,那个JMF框架架构不错,但就是功能太弱。这个播放器的出现,至少说明了java在这方面还是有能力的,只是没发挥出来。

  • struts2的时间格式转换问题

    struts2提供了一个时间标签:

     但这个标签很只能在显示的时候用,但如果我想在输入框里显示时间,让用户直接修改时间,怎么弄?datepicker?选择太麻烦,我想让用户输入,并且
    兼容多种日期格式。还有,如果用时间标签的话,每个地方都需要指定format,如果我想修改一下格式,所有的时间显示都变,怎么弄?

    *  In some situations you may wish to apply a type converter globally.
     *  This can be done by editing the file
     * xwork-conversion.properties in the root of your class path
     * (typically WEB-INF/classes) and providing a
     * property in the form of the class name of the object you wish to convert
     * on the left hand side and the class name of
     * the type converter on the right hand side. For example, providing
     * a type converter for all Point objects would mean
     * adding the following entry:
     *
     * com.acme.Point = com.acme.PointConverter
    

     XWorkConverter,先在classpath root下找xwork-conversion.properties文件,这个文件定义了全局转换。然后每遇到新的类需要转换,便查找是否有特殊的自定义转换配置。特殊自定义转换配置文件的路径是:

    className.replace('.', '/')   "-conversion.properties";
    

     比方com.acme.Point的转换配置就是classpath 下的/com/acme/Point-coversion.properties文件。

    ok,这个问题好解决了。

    我的xwork-coversion.properties文件:

    java.util.Date=cn.jolstar.struts.type.DateTypeConverter
    

      我的DateTypeConverter代码:

    /**
     *
     */
    package cn.jolestar.struts.type;
    
    import java.text.DateFormat;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Map;
    
    import org.apache.log4j.Logger;
    import org.apache.struts2.util.StrutsTypeConverter;
    
    /**
     * @author jolestar
     *
     */
    public class DateTypeConverter extends StrutsTypeConverter {
    
    	private static final Logger log = Logger.getLogger(DateTypeConverter.class);
    	public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    
    	//暂时只考虑这几种日期格式
    	public static final DateFormat[] ACCEPT_DATE_FORMATS = {
    			new SimpleDateFormat(DEFAULT_DATE_FROMAT),
    			new SimpleDateFormat("yyyy年MM月dd日"),
    			new SimpleDateFormat("yyyy/MM/dd") };
    
    	/**
    	 *
    	 */
    	public DateTypeConverter() {
    	}
    
    	/*
    	 * (non-Javadoc)
    	 *
    	 * @see org.apache.struts2.util.StrutsTypeConverter#convertFromString(java.util.Map,
    	 *      java.lang.String[], java.lang.Class)
    	 */
    	@Override
    	public Object convertFromString(Map context, String[] values, Class toClass) {
    		if (values[0] == null || values[0].trim().equals(""))
    			return null;
    		for (DateFormat format : ACCEPT_DATE_FORMATS) {
    			try {
    				return format.parse(values[0]);
    			} catch (ParseException e) {
    				continue;
    			} catch (RuntimeException e) {
    				continue;
    			}
    		}
    		log.debug("can not format date string:"   values[0]);
    		return null;
    	}
    
    	/*
    	 * (non-Javadoc)
    	 *
    	 * @see org.apache.struts2.util.StrutsTypeConverter#convertToString(java.util.Map,
    	 *      java.lang.Object)
    	 */
    	@Override
    	public String convertToString(Map context, Object o) {
    		if (o instanceof Date) {
    			SimpleDateFormat format = new SimpleDateFormat(
    					DEFAULT_DATE_FORMAT);
    			try {
    				return format.format((Date) o);
    			} catch (RuntimeException e) {
    				return "";
    			}
    		}
    		return "";
    	}
    
    }
    

     

    这样,从字符串转换为日期对象的时候,会尝试上面列出的多种日期格式,但输出的时候,则会统一按照DEFAULT—DATE—FORMAT转换。 需要修改格式,只需要修改DEFAULT—DATE—FORMAT。当然,你也可以把它方在配置文件里,便于修改。

     

    了解了这一点,其实也就 明白了struts的类型转换模式。然后,无论是字符串id到持久化对象的转换,还是自定义的字符串到对象之间的转换,都容易了。

  • Dspace架设经验

    1.Dspace是什么?
          dspace.org
          The DSpace digital repository system captures, stores, indexes, preserves, and distributes digital research material.
          简单一点,Dspace是一个数字图书馆系统,一般用于研究机构储存和发布研究资源.

    2.Dspace的安装:
       windows
        wiki.dspace.org/index.php/DSpaceOnWindows
        linux:
        wiki.dspace.org/index.php/Installing_DSpace_on_Ubuntu_6.06_(LTS)

    3.Dspace架设的一些经验
         我当时使用的版本是dspace3.2
        中文问题
         Dspace会把每个item的标题和作者的首字母截取下来,做为按字母索引.但这对中文就不起作用了.所以我用pinyin4j这个开源项目,把标题和作者转换为拼音,然后截取首字母.
         邮件验证问题
         Dspace中只让用户设置邮件服务器,但并没让设置用户和密码,所以你必须用自己的邮件服务器.这个可以修改一下org.dspace.core.Email类的源码,加入验证.在代码Transport.send(message)处,加入以下代码:

    java 代码

    //jolestar add for mail auth
             String user = ConfigurationManager.getProperty("mail.user");
             String password = ConfigurationManager.getProperty("mail.password");
             Transport transport=session.getTransport("smtp");
             if(user==null||password==null)
             {
                 user = "";
                 password="";
             }
             transport.connect(server,user,password);
             transport.sendMessage(message,message.getAllRecipients());
             transport.close();
    
             //Transport.send(message); 
    

     并将上面那行代码注释了.在配置文件中就可以用mail.user和mail.password配置邮件用户了.不过这个问题在最新版的dspace中已经不存在了.

  • struts2 checkboxlist 的一个问题

    下面是java对象。

    package cn.jolestar.struts;
    
    /**
     * @author jolestar
     *
     */
    public class Language {
    
    	private Long id;
    	private String name;
    
    	/**
    	 *
    	 */
    	public Language() {
    	}
    
    	public Long getId() {
    		return id;
    	}
    
    	public void setId(Long id) {
    		this.id = id;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	@Override
    	public int hashCode() {
    		final int prime = 31;
    		int result = 1;
    		result = prime * result   ((id == null) ? 0 : id.hashCode());
    		return result;
    	}
    
    	@Override
    	public boolean equals(Object obj) {
    		if (this == obj)
    			return true;
    		if (obj == null)
    			return false;
    		if (getClass() != obj.getClass())
    			return false;
    		final Language other = (Language) obj;
    		if (id == null) {
    			if (other.id != null)
    				return false;
    		} else if (!id.equals(other.id))
    			return false;
    		return true;
    	}
    
    }
    

     

     

     

    package cn.jolestar.struts;
    
    import java.util.List;
    
    /**
     * @author jolestar
     *
     */
    public class Album {
    
    	private Long id;
    	private String name;
    	private List languages;
    	/**
    	 *
    	 */
    	public Album() {
    	}
    	public List getLanguages() {
    		return languages;
    	}
    	public void setLanguages(List languages) {
    		this.languages = languages;
    	}
    
    }
    

     

     下面是jsp标签:

     

     

     

     

    action中有个getLanguages()方法,返回数据库中的所有语言。按道理我编辑Album的时候,checkboxlist中Album的已经拥有的语言会自动选中。但怎么弄,它就是不选中。没办法,看了一下struts2的相关源码,才明白了。

     

    struts2 checkboxlist 的freemarker模板:

     

    ${itemValue?html}
        
    
       
    

     

     if tag.contains(parameters.nameValue,
    itemKey)这个if语句决定该checkbox是否选中。tag.contains()最终调用的是
    org.apache.struts2.util.ContainUtil中的这个方法:

     

    public static boolean contains(Object obj1, Object obj2) {
            if ((obj1 == null) || (obj2 == null)) {
                //log.debug("obj1 or obj2 are null.");
                return false;
            }
    
            if (obj1 instanceof Map) {
                if (((Map) obj1).containsKey(obj2)) {
                    //log.debug("obj1 is a map and contains obj2");
                    return true;
                }
            } else if (obj1 instanceof Collection) {
                if (((Collection) obj1).contains(obj2) || ((Collection) obj1).contains(obj2.toString())) {
                    //log.debug("obj1 is a collection and contains obj2");
                    return true;
                }
            } else if (obj1.getClass().isArray()) {
                for (int i = 0; i < Array.getLength(obj1); i  ) {
                    Object value = null;
                    value = Array.get(obj1, i);
    
                    if (value.equals(obj2)) {
                        //log.debug("obj1 is an array and contains obj2");
                        return true;
                    }
                }
            } else if (obj1.toString().equals(obj2.toString())) {
                //log.debug("obj1 is an object and it's String representation equals obj2's String representation.");
                return true;
            } else if (obj1.equals(obj2)) {
                //log.debug("obj1 is an object and equals obj2");
                return true;
            }
    
            //log.debug("obj1 does not contain obj2: "   obj1   ", "   obj2);
            return false;
        }
    

     

     

    tag.contains(parameters.nameValue,
    itemKey)的参数parameters.nameValue是該标签的值,在这里就是album.languages,
    parameters.list
    是languages,itemKey是循环中的当前language的id。album.languages包含的是Language对象,而
    struts2却在其中查找itemKey–就是Language的id,当然就无法查找到,所以不能自动选中。

     

    我把checkboxlist的模板改了一下。

     

    ${itemValue?html}
    	
    
      &nbsp;
    

     就是将tag.contains(parameters.nameValue,
    itemKey)改成了tag.contains(parameters.nameValue,it),it就是当前循环的Language对象,在
    struts2的iterator 标签里设置

     

    .(注意:如果是最新版的struts2,应该用var 而不是id).

     

    这样就好了。需要注意的是language必须正确overwrite  equals和hashCode方法。

     

subscribe via RSS