转载:
2. 从某个文件的某个起始位置开始,截取指定长度的数据,保存到某个输出文件中
默认的起始位置为0,
默认的长度是从起始位置到文件末尾之间的长度
bin-cut.sh
#! /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 命令,这就是为什么叫作二进制的输出。
阅读(11920) | 评论(0) | 转发(0) |