shell编程 (3) —— 调试

shell调试技巧

shell脚本虽然不像高级语言那样有专门的调试工具和调试机制,但是前辈们仍然想出了一些办法来进行shell脚本的错误检测。

trap

shell脚本在执行的时候会产生三伪信号(不是操作系统发出的信号)。我们可以使用trap捕获信号然后进行shell的调试。
shell伪信号的产生:

信号产生条件
EXIT正常退出
ERR返回非零状态码
DEBUG命令执行之前

trap的使用: trap commaond sig1,sig2,…
注:command使用单引号”’”包围的。
例子:
code:

#!/bin/bash
trap 'echo "the $LINENO line before executing, a is $a"' DEBUG
time=0
a=1
while (( $time<6 ))
do
   let "a=a*2"
   let "time++"
done

output:

$ ./two.sh
the 3 line before executing, a is
the 4 line before executing, a is
the 5 line before executing, a is 1
the 7 line before executing, a is 1
the 8 line before executing, a is 2
the 5 line before executing, a is 2
the 7 line before executing, a is 2
the 8 line before executing, a is 4
the 5 line before executing, a is 4
the 7 line before executing, a is 4
the 8 line before executing, a is 8
the 5 line before executing, a is 8
the 7 line before executing, a is 8
the 8 line before executing, a is 16
the 5 line before executing, a is 16
the 7 line before executing, a is 16
the 8 line before executing, a is 32
the 5 line before executing, a is 32
the 7 line before executing, a is 32
the 8 line before executing, a is 64
the 5 line before executing, a is 64

这种调适技巧针对递归的作用是有限的:
code

trap 'echo "the $LINENO line before executing, a is $a, b is $b"' DEBUG
a=120
b=250
gcd(){
    if [ $b -eq 0 ]; then
        return $a
    else
        t=$a
        a=$b
        b=`expr $t % $b`
    gcd $a $b
    fi
}
gcd $a $b
echo "gcd is "$

执行代码

$ ./trap.sh
the 3 line before executing, a is , b is
the 4 line before executing, a is 120, b is
the 15 line before executing, a is 120, b is 250
the 16 line before executing, a is 10, b is 0
gcd  is 10

试试ERR:
code:

#!/bin/bash
trap 'echo "focus on $LINENO,"$string'  ERR
string="here is a error"
ipconfig

执行脚本:
(我们将标准错误输出重定向到文件file中)

$ ./trap.sh >&2 2>file
focus on 4,here is a error
$ cat file
./trap.sh: line 4: ipconfig: command not found

tee

有关tee的解释是这样的: tee - read from standard input and write to standard output and files
tee读入数据,然后产生两支分流,一支是标准输出,另一支输出数据到文件。
一个例子:
查阅本机eth0的mac地址。
它可以用这条语句实现:
cat /etc/sysconfig/network-scripts/ifcfg-eth0 |grep HWADDR |cut -d= -f2
但是,这看起来有些复杂。。。
我们可以使用tee来跟踪流。
cat /etc/sysconfig/network-scripts/ifcfg-eth0 | tee -a txt| grep HWADDR |tee -a txt | cut -d= -f2 | tee -a txt
运行结果:

$ ./tee.sh
00:0c:29:e8:97:1b

$ cat txt
DEVICE=eth0
HWADDR=00:0c:29:e8:97:1b
TYPE=Ethernet
UUID=c5857400-a96d-451d-8983-10a52193427c
ONBOOT=no
NM_CONTROLLED=yes
BOOTPROTO=dhcp
IPADDR=192.168.137.99
NETMASK=255.255.0.0
IPV6INIT=no
USERCTL=no
HWADDR=00:0c:29:e8:97:1b
00:0c:29:e8:97:1b

调试钩子

调试钩子的思想有些像C中的条件编译。当我们需要进行中间变量的输出时打开它,否则关闭它。
shell中的调试钩子的格式一般是这样的:

DEBUG(){
   if [ "$DEBUG" = "true" ]; then
   ...
   fi
}

还是那个2^6的例子:

DEBUG(){
    if [ "$DEBUG" = "true" ]; then
        echo "time is $time, a is $a"
    fi
}
time=0
a=1
while (( $time<6 ))
do
    let "a=a*2"
    let "time++"
    DEBUG   
done

执行脚本:

$ export DEBUG="true"; ./debug.sh 
time is 1, a is 2
time is 2, a is 4
time is 3, a is 8
time is 4, a is 16
time is 5, a is 32
time is 6, a is 64

shell选项

选项名称简写意义
noexecn语法检验,不执行命令
xtracex执行每一条命令之前将命令打印出来
没有c…从…读取命令

直接使用指令sh也就是使用bash - GNU Bourne-Again SHell
他们的手册查询结果是一样的。

[edemon@CentOS ~]$ man sh > sh.txt
[edemon@CentOS ~]$ man bash > bash.txt
[edemon@CentOS ~]$ diff bash.txt sh.txt
[edemon@CentOS ~]$ 

例子:

echo I start to run....
if [ 1 -lt 2 ] then
    echo yes
fi
[edemon@CentOS ~]$ ./err.sh 
I start to run....
./err.sh: line 4: syntax error near unexpected token `fi'
./err.sh: line 4: `fi'
[edemon@CentOS ~]$ bash -n err.sh 
err.sh: line 4: syntax error near unexpected token `fi'
err.sh: line 4: `fi'
[edemon@CentOS ~]$ sh -n err.sh 
err.sh: line 4: syntax error near unexpected token `fi'
err.sh: line 4: `fi'

仍然以2^6的我例子来做说明:

#!/bin/bash
trap 'echo "the $LINENO line before executing, a is $a"' DEBUG
time=0
a=1
while (( $time<6 ))
do
   let "a=a*2"
   let "time++"
done

add -x to run our program:
在普通语句前面加上’+’,变量已经更新。
在trap debug前面加上“++”

$ bash -x trap_debug.sh 
+ trap 'echo "the $LINENO line before executing, a is $a"' DEBUG
++ echo 'the 3 line before executing, a is '
the 3 line before executing, a is 
+ time=0
++ echo 'the 4 line before executing, a is '
the 4 line before executing, a is 
+ a=1
++ echo 'the 5 line before executing, a is 1'
the 5 line before executing, a is 1
+ ((  0<6  ))
++ echo 'the 7 line before executing, a is 1'
the 7 line before executing, a is 1
+ let 'a=a*2'
++ echo 'the 8 line before executing, a is 2'
the 8 line before executing, a is 2
+ let time++
++ echo 'the 5 line before executing, a is 2'
the 5 line before executing, a is 2
+ ((  1<6  ))
++ echo 'the 7 line before executing, a is 2'
the 7 line before executing, a is 2
+ let 'a=a*2'
++ echo 'the 8 line before executing, a is 4'
the 8 line before executing, a is 4
+ let time++
++ echo 'the 5 line before executing, a is 4'
the 5 line before executing, a is 4
+ ((  2<6  ))
++ echo 'the 7 line before executing, a is 4'
the 7 line before executing, a is 4
+ let 'a=a*2'
++ echo 'the 8 line before executing, a is 8'
the 8 line before executing, a is 8
+ let time++
++ echo 'the 5 line before executing, a is 8'
the 5 line before executing, a is 8
+ ((  3<6  ))
++ echo 'the 7 line before executing, a is 8'
the 7 line before executing, a is 8
+ let 'a=a*2'
++ echo 'the 8 line before executing, a is 16'
the 8 line before executing, a is 16
+ let time++
++ echo 'the 5 line before executing, a is 16'
the 5 line before executing, a is 16
+ ((  4<6  ))
++ echo 'the 7 line before executing, a is 16'
the 7 line before executing, a is 16
+ let 'a=a*2'
++ echo 'the 8 line before executing, a is 32'
the 8 line before executing, a is 32
+ let time++
++ echo 'the 5 line before executing, a is 32'
the 5 line before executing, a is 32
+ ((  5<6  ))
++ echo 'the 7 line before executing, a is 32'
the 7 line before executing, a is 32
+ let 'a=a*2'
++ echo 'the 8 line before executing, a is 64'
the 8 line before executing, a is 64
+ let time++
++ echo 'the 5 line before executing, a is 64'
the 5 line before executing, a is 64
+ ((  6<6  ))

bash -c 让我们从字符串中读取命令。

$ bash -c "echo \"hello world\""
hello world

shell的内部变量:
|变量名|意义|
|LINENO|行号|
|FUNCNAME|函数数组|
|ps4|设置提示格式|

练习

codingame —— Onboarding

https://www.codingame.com/ide/6041381b0ab6222a931a6ef5a54be06f24fba59
大意:外星飞船来了,每一轮都有两只飞船向我们袭来,我们需要击毁最近的那个。

# CodinGame planet is being attacked by slimy insectoid aliens.
# <---
# Hint:To protect the planet, you can implement the pseudo-code provided in the statement, below the player.


# game loop
while true; do
    # enemy1: name of enemy 1
    read enemy1
    # dist1: distance to enemy 1
    read dist1
    # enemy2: name of enemy 2
    read enemy2
    # dist2: distance to enemy 2
    read dist2

    # Write an action using echo
    # To debug: echo "Debug messages..." >&2
    if [ $dist1 -lt $dist2 ]; then
    result=${enemy1}
    else result=${enemy2}
    fi
    # You have to output a correct ship name to shoot ("Buzz", enemy1, enemy2, ...)
    echo $result
done

codingame —— Temperatures

https://www.codingame.com/ide/6270485820e1a14078b9a510c3d65717f5ee20f
大意:给出n个气温,求出最接近0的温度。如果有一正一负都最接近0摄氏度,那么输出正整数。

注意下面字符串拆分成数组,求解整数绝对值的方法。

# Auto-generated code below aims at helping you parse
# the standard input according to the problem statement.

# n: the number of temperatures to analyse
read n
# temps: the n temperatures expressed as integers ranging from -273 to 5526
read temps
array=(${temps// / })    #(${temps//,/ })表示以逗号拆分
dis=6000
ans=0
for val in ${array[@]}; do
if [ ${val#-} -lt $dis ]; then
ans=$val
dis=${val#-}
elif [[ ${val#-} -eq $dis && $val -gt 0 ]]; then
ans=$val
fi
done
# Write an action using echo
# To debug: echo "Debug messages..." >&2

echo $ans

codingame —— ASCII Art

https://www.codingame.com/ide/627078537de52bce6cbc8fda39cbd62341ae42a
大意:
输入L,H,old_string, 英文字符表示符。
输出old_string对应的新表示符。
比如:

input
4
5 
E
 #  ##   ## ##  ### ###  ## # # ###  ## # # #   # # ###  #  ##   #  ##   ## ### # # # # # # # # # # ### ### 
# # # # #   # # #   #   #   # #  #    # # # #   ### # # # # # # # # # # #    #  # # # # # # # # # #   #   # 
### ##  #   # # ##  ##  # # ###  #    # ##  #   ### # # # # ##  # # ##   #   #  # # # # ###  #   #   #   ## 
# # # # #   # # #   #   # # # #  #  # # # # #   # # # # # # #    ## # #   #  #  # # # # ### # #  #  #       
# # ##   ## ##  ### #    ## # # ###  #  # # ### # # # #  #  #     # # # ##   #  ###  #  # # # #  #  ###  #  
Output
### 
#   
##  
#   
### 

访问字符串中的单个字符,自己暂时仅想到这样的办法:

str="ABCDEF"
echo ${#str}
for ((i=0;i<${#str};i++)); do
echo -e ${str:$i:1}'\c'
done
echo ""
[edemon@CentOS workspace]$ ./test.sh
6
ABCDEF

自己一开始使用C写了一个:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/**
 * Auto-generated code below aims at helping you parse
 * the standard input according to the problem statement.
 **/
int main()
{
    //freopen("read","r",stdin);
    int L;
    scanf("%d", &L);
    int H;
    scanf("%d", &H); 
    fgetc(stdin);
    char T[257] = {0};
    gets(T);
    char str[200*1025] = {0};
    int i,j,k;
    for (i = 0; i < H; i++) {
        char ROW[1025] = {0};
        gets(ROW);
        strcat(str,ROW);
    }
    short dex_arr[200] = {0}, len = 0, dex = 0;
    for(i=0;T[i];i++){
        if((T[i]>=65 && T[i]<=90) || (T[i]>=97 && T[i]<=122)){
            dex = T[i]-97;
            if(dex < 0) dex += 32;
        }
        else dex = 26;
        dex_arr[len++] = dex;
    }
    //printf("dex len is %d, dex_arr[0] is %d\n",len,dex_arr[0]);
    for(i=0;i<H;i++){
        for(j=0;j<len;j++){
            int s=dex_arr[j]*L;
            for(k=s;k<s+L;k++){
                printf("%c",str[i*27*L+k]);
            }
        }
        puts("");
    }
    return 0;
}

然后再尝试用bash来写,但是有一个问题,怎么进行两个英文字符的相减操作?字母转成整数?可是怎么转?

# Auto-generated code below aims at helping you parse
# the standard input according to the problem statement.

read L
read H
read T
string=""
for (( i=0; i<H; i++ )); do
    read ROW
    string=$string$ROW
done
dex=()
dex_len=0
for (( i=0;${#T[@]};i++ )) do
    char=${string:i:1}
    #temp=`expr $char - 'A' ` #---> how can we sub two letters? 
    if [ $temp -lt 0 ]; then
        temp=`expr $temp + 32`
    fi
    dex[${dex_len}]=$temp
    dex_len=`expr $dex_len + 1`    
done
#echo ${dex[@]}
for (( i=0;i<H;i++ )); do
    for (( j=0;j<dex_len;j++ )); do
        s=`expr $j * L`;
        for (( k=s;k<s+L;k++ )); do
            echo -e ${string[`expr $i * 27 * $L + $k`]}'\c'
        done
    done    
    echo ""
done

打印九九乘法表

这里写图片描述

for (( i=1;i<10;i++ )); do
    for (( j=1;j<=i;j++ )); do
        echo -e $j" * "$i" = "`expr $j \* $i`"\t\c"
    done
    echo ""
done

在test下创建30个目录,并赋予所有者读、写、可执行执行的权限,同组其他人员读、可执行权限,其他人员读的权限。

for (( i=1;i<=30;i++ ));do
mkdir -p ./test/dir$i
chmod -R 754 ./test/dir$i
done
ls -l ./test
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值