Chinaunix首页 | 论坛 | 博客
  • 博客访问: 968983
  • 博文数量: 200
  • 博客积分: 5011
  • 博客等级: 大校
  • 技术积分: 2479
  • 用 户 组: 普通用户
  • 注册时间: 2008-06-27 15:07
文章分类

全部博文(200)

文章存档

2009年(12)

2008年(190)

我的朋友

分类:

2008-07-18 10:51:49


转载:

2. 从某个文件的某个起始位置开始,截取指定长度的数据,保存到某个输出文件中
默认的起始位置为0,
默认的长度是从起始位置到文件末尾之间的长度


bin-cut.sh

CODE:
#! /bin/bash
function check_pipe_status()
{
        local status
        for((i=1;i<=$#;i++)) {
                eval status=\$$i
                if [ $status -ne 0 ]; then
                        echo false;
                fi
        }
        echo true
}

    check_pipe_status中,eval status=\$$i语句经历了2次扩展,第一次扩展的结果是eval status=$n,其中n是该次循环的$i的值,第一个$被当作字符了,然后eval又使status=$n再次被当作shell指令执行一次,从而得到第n个寒暑参数的数值.
    对于函数来说,调用者得到的结果就是函数echo的结果,而不是return出来的结果,return出来的结果放到了$?里面。

function get_file_size()
{
        { ls -l "$1" | gawk '{print $5}' ; } 2>/dev/null
        if ! $(check_pipe_status "${PIPESTATUS[@]}"); then
                return 1
        fi
        return 0
}
    ${PIPESTATUS[@]}代表了前面刚执行的语句的pipe两头的语句的结果,如正确的话全是0。而文件的结果被输出了。print $5就是输出的文件大小.

function check_number()
{
        v=$(echo "$1" | grep '^\(+\?[0-9]\+\|0[xX][0-9a-fA-F]\+\)$' );
        if [ -z "$v" ]; then
                echo "Illeagal number $1" >&2;
                exit 1;
        fi
}
    这里使用grep的时候,要注意一点,grep默认使用的是basic regular expression的表达方式,即: ? + | ( ) { }这几个字符在basic里面就是字面字符,没有特殊含义,如果想要有特殊含义,就得加上backslash符号,即\?就代表了前面的内容可有可无。或者还可以使用grep -E 那么,此时就默认使用extended regular expression了,这些字符就有了特殊含义了。或者使用egrep。
    因此,'^\(+\?[0-9]\+\|0[xX][0-9a-fA-F]\+\)$'的含义就是:
    起头的,前面'+'可有可无的,至少有1个0-9之间的字符的,或者是以0x起头的,16进制格式的数字串。然后结尾。
    关于echo "Illeagal number $1" >&2 的作用,可以参看我的一个帖子,>&2的作用就是1>&2就是将fd2赋值给fd1,即echo所使用的fd1现在和fd2指向的目标一样。即单纯这条指令的数粗的fd1指向了和fd2一样的内容。我们自己做一个测试:

$cat test.sh
#!/bin/bash
echo "i am error output" >&2
echo "i am normal output"

$./test.sh
i am error outpur
i am normal output

$./test.sh >tmp
i am error output

$cat tmp
i am normal output

    解释:注意虽然最后我们将整个脚本的标准输出定向到tmp,但是,这只是将整个脚本的执行进程的fd1重定向向到了tmp文件,而并不改变内部的echo "i am error output" >&2这个命令的fd1的指向,即该命令的fd1继续指向stderr,即fd2的指向,那么造成的结果是i am error output依然会输出到stderr即显示终端,而i am normal output就被重定向到文件了。这样看来,整个脚本的命令中,有向fd2的输出,也有向fd1的输出,而整个脚本的fd1的输出被重定向了,而fd2的没有,因此向fd2的输出就被显示出来了。这个可以作为我们在校本中向stderr输出的方法。我们不能这样想:这个echo命令的fd1指向fd2的内容了,而后来对整个脚本的重定向有将fd1改为指向文件了。所以这个命令的输出也指向文件。这是错误的。
    实质是,在本脚本被执行前,其进程中的fd1就指向了文件tmp,fd2指向stderr,当执行到命令echo "i am error output" >&2时,其fd1开始也是指向tmp的,但是现在被更新为指向stderr,当此命令执行完后,别的命令的fd1还是指向文件。我们要说的是解释器先为真个进程设定好了fd的指向后采取执行各条命令,而不是执行完各条命令后再去看整个进程输出的指向。
    下面继续看代码:

SCRIPT_NAME=$(basename $0)
PAGESIZE=4096
inf="$1"
outf="$2"
start="$3"
length="$4"

if [ $# -lt 2 ]; then
        echo "Usage: $SCRIPT_NAME input-file output-file [start] [length]" >&2;
        exit 1;
fi

file_sz=$(get_file_size "$inf");
if [ $? -ne 0 ]; then
        echo "$inf is not present!" >&2;
        exit 2;
fi

    上面用$?来获取get_file_size函数return的数值,而该函数echo出来的内容就是filesize。看来shell中的函数结果和返回值是两码事。
if [ -z "$start" ]; then
        start=0;
        length=$file_sz;
elif [ -z "$length" ]; then
        length=$((file_sz-start));
        check_number "$start";
else
        check_number "$start";
        check_number "$length";
fi
    上面((...))是进行数学运算的意思。
if [[ $((start+length)) -gt $file_sz ]]; then
        printf "offset (%d,%d) beyond file length (%d)" $start $((start+length)) $file_sz >&2;
        exit 1;
fi

if true; then
        length1=$((length/PAGESIZE*PAGESIZE))
        length2=$((length%PAGESIZE))
        start1=$start
        start2=$((start1+length1))
        {
                if [ $length1 -gt 0 ]; then
                        dd if="$inf" skip=$(printf "%d" $start1) ibs=$PAGESIZE count=$(($length1/PAGESIZE)) 2>/dev/null
                fi
                if [ $length2 -gt 0 ]; then
                        dd if="$inf" skip=$(printf "%d" $start2) ibs=1 count=$length2 2>/dev/null
                fi
        } >"$outf"
else
        dd if="$inf" of="$outf" skip=$(printf "%d" $start) ibs=1 count=$(printf "%d" $length) 2>/dev/null
fi

   
   上面使用dd 命令,这就是为什么叫作二进制的输出。
阅读(11861) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~