shell启动另一个脚本进行统计,awk在每一行添加当前时间

Shell脚本实现服务控制与统计
本文介绍了一个使用Shell脚本来控制服务启动、停止和重启的方法,并通过另一个脚本实时统计并记录日志文件中的平均值。文章展示了如何利用Shell脚本结合awk等工具进行系统监控和服务管理。

控制脚本,

stat.sh

PID_NUM=$$

PID_FILE=stat.pid

#echo "pid : "$PID_NUM

PROG_NAME=$0
ACTION=$1

usage() {
    echo "Usage: ${PROG_NAME} {start|stop|restart}"
    exit 1;
}

start(){
    sh ./stat_run.sh &
    PID_NUM=$!
    echo "worker pid is " $PID_NUM
    echo ${PID_NUM} > ${PID_FILE}
}

stop(){
    PID_NUM=`cat ${PID_FILE}`
    if [ "" != "${PID_NUM}" ]; then

        echo kill pid is "${PID_NUM}"        
        kill ${PID_NUM}
    fi
}

case "${ACTION}" in
    start)
        start
    ;;
    stop)
        stop
    ;;
    restart)
        stop
        sleep 1
        start
    ;;
    *)
        usage
    ;;
esac

其中$$ 表示当前正在运行的进程的pid,$!表示最近的上一个进程pid

真正运行统计程序的脚本

cat stat_run.sh 
    STAT_FILE=stat_delay.log
    echo "" > $STAT_FILE
    while true; do 
        tail -n10000 perf.log | awk '{print $8}' | grep totalAll | awk -F, 'BEGIN {cnt=0;total=0} {cnt=cnt+1;total=total+$3} END{print '"\"$(date  +%Y-%m-%d-%H:%M:%S)\""', total/cnt}' >> $STAT_FILE;
        sleep 5;
    done;

上面脚本,每5秒统计最新生成的10000条记录的平均值

在awk中每行打印当前时间

[admin@v021130.sqa.cm4 logs]$ cat test.log 
2013-07-30-23:00:12 44.5791
2013-07-30-23:00:17 44.5791
2013-07-30-23:00:22 44.5791
2013-07-30-23:00:27 44.5791
2013-07-30-23:00:32 44.5791
2013-07-30-23:00:37 44.5791
2013-07-30-23:00:42 44.5791
2013-07-30-23:00:47 44.5791
2013-07-30-23:00:52 44.5791
2013-07-30-23:00:58 44.5791
[admin@v021130.sqa.cm4 logs]$ awk '{print '"\"$(date  +%Y-%m-%d-%H:%M:%S)\""', $1}' test.log           
2013-07-30-23:01:21 2013-07-30-23:00:12
2013-07-30-23:01:21 2013-07-30-23:00:17
2013-07-30-23:01:21 2013-07-30-23:00:22
2013-07-30-23:01:21 2013-07-30-23:00:27
2013-07-30-23:01:21 2013-07-30-23:00:32
2013-07-30-23:01:21 2013-07-30-23:00:37
2013-07-30-23:01:21 2013-07-30-23:00:42
2013-07-30-23:01:21 2013-07-30-23:00:47
2013-07-30-23:01:21 2013-07-30-23:00:52
2013-07-30-23:01:21 2013-07-30-23:00:58
[admin@v021130.sqa.cm4 logs]$ awk '{print '"\"$(date  +%Y-%m-%d-%H:%M:%S)\""', $2}' test.log  
2013-07-30-23:01:27 44.5791
2013-07-30-23:01:27 44.5791
2013-07-30-23:01:27 44.5791
2013-07-30-23:01:27 44.5791
2013-07-30-23:01:27 44.5791
2013-07-30-23:01:27 44.5791
2013-07-30-23:01:27 44.5791
2013-07-30-23:01:27 44.5791
2013-07-30-23:01:27 44.5791
2013-07-30-23:01:27 44.5791



<think>我们面对的问题:使用shell脚本命令修改一个shell脚本。 目标:编写一个shell脚本,该脚本能够修改一个shell脚本文件的内容。 思路: 1. 我们可以使用流编辑器(如sed)来对文件进行文本替换。 2. 也可以使用awk进行更复杂的文本处理。 3. 还可以使用一些文本处理工具(如grep)结合临时文件来实现。 常见场景: - 替换字符串:例如将脚本中的某个命令替换为一个命令。 - 插入或删除行:在特定位置插入或删除一行或多行。 - 修改特定行的内容:例如修改某个变量的赋值。 示例1:使用sed进行简单的字符串替换 假设我们有一个脚本文件original.sh,其中有一行: echo "Hello, World" 我们想将其修改为: echo "Hello, Universe" 我们可以使用以下命令: sed -i 's/World/Universe/' original.sh 示例2:在指定行后插入一行 假设我们想在文件original.sh的第5行后插入一行新内容: echo "This is a new line" 命令: sed -i '5a echo "This is a new line"' original.sh 示例3:删除包含特定字符串的行 删除所有包含"oldtext"的行: sed -i '/oldtext/d' original.sh 示例4:使用awk进行修改 假设我们想将文件中的某个变量的值进行修改,例如: var="old_value" 修改为: var="new_value" 我们可以使用: awk -v var="var" -v new_val="new_value" 'BEGIN{FS=OFS="="} $1 == var {$2="\""new_val"\""}1' original.sh > temp && mv temp original.sh 但是注意,上面的awk命令假设变量赋值语句是严格的`var="value"`格式,并且等号两边没有空格。如果有空格,则需要调整。 更复杂的修改可能需要组合使用这些工具,或者使用更高级的文本处理方式。 注意事项: - 修改文件时,尤其是脚本文件,要小心不要破坏原有结构。 - 使用-i选项可以直接修改文件,但建议先备份,或者在不使用-i的情况下测试输出到一个文件,确认无误后再替换。 下面我们通过一个具体的例子来演示: 需求:有一个脚本文件test.sh,内容如下: #!/bin/bash echo "Start" name="Alice" echo "Hello, $name" 我们想通过一个脚本modify.sh来修改test.sh: 1. 将name变量的值改为"Bob" 2. 在echo "Start"后面插入一行:echo "This is inserted line" 3. 将第一个echo命令的输出改为"Start of the program" 步骤: 1. 修改name变量:我们可以使用sed来替换这一行。 sed -i 's/name="Alice"/name="Bob"/' test.sh 2. 在echo "Start"后面插入一行:注意,我们需要在匹配到该行后进行插入。 sed -i '/echo "Start"/a echo "This is inserted line"' test.sh 3. 修改第一个echo命令:我们同样使用替换。 sed -i 's/echo "Start"/echo "Start of the program"/' test.sh 但是,上述步骤中,如果文件中有多个echo "Start",则可能会修改到其他行。因此,我们需要确保定位准确。 更精确的做法是结合行号或者上下文。例如,如果我们知道name变量在第三行,我们可以直接指定行号: sed -i '3s/Alice/Bob/' test.sh # 第三行,将Alice替换为Bob 但是,如果行号会变化,那么最好还是通过匹配来定位。例如,匹配以name=开头的行: sed -i '/^name=/s/Alice/Bob/' test.sh 然而,原文件中是`name="Alice"`,所以我们可以这样: sed -i '/^name=/s/"Alice"/"Bob"/' test.sh 这样更精确。 因此,我们的modify.sh脚本可以这样写: ```bash #!/bin/bash # 修改test.sh file="test.sh" # 1. 修改name变量的值 sed -i '/^name=/s/Alice/Bob/' "$file" # 2. 在echo "Start"后面插入一行 sed -i '/echo "Start"/a echo "This is inserted line"' "$file" # 3. 修改第一个echo命令,但这里我们同样使用匹配,注意可能不止一个echo "Start",所以我们只修改第一个? # 使用sed的地址范围:0表示第一次匹配 # 但是sed默认会替换所有匹配的行,所以如果只想替换第一个,我们可以这样: # 在第一次匹配到之后,执行替换,然后不再继续处理该模式(但这样并不容易,通常我们使用更精确的匹配) # 或者,我们可以通过匹配行内容来确保唯一性,或者使用行号(如果知道行号的话)。 # 由于我们在第二步已经插入了一行,所以原来的第二个echo命令(即修改前的第三行)现在变成了第四行?所以行号已经变化。 # 因此,我们最好在第一步修改之前先修改第一个echo命令,这样行号不会受后续插入的影响。 # 重新调整步骤: # 先做替换,再做插入(因为插入会影响行号) # 修改步骤顺序: # 3. 修改第一个echo命令(在插入之前,它在第二行) # 2. 然后在第二行后面插入 # 但在脚本中,我们可以先做第一个echo的替换,然后插入,最后修改变量(因为变量在第三行,插入后变成第三行?不对,插入在第二行后,所以原来的第三行变成了第四行?) # 实际上,我们修改的是同一个文件,所以每一步操作后文件的行号都会变化。 # 为了避免混淆,我们可以: # 1. 修改第一个echo命令(第二行) # 2. 在第二行后插入(此时第二行已经被修改,我们仍然匹配第二行原来的内容?不行,因为已经修改了) # 所以我们需要重新考虑顺序和匹配方式。 # 方案:先插入,再修改。但是插入后,原来的第二行变成了第三行?不对,插入是在第二行后面,所以第二行还是第二行,插入的内容成为第三行,原来的第三行变成第四行。 # 因此,我们可以: # 1. 在第二行(即echo "Start")后面插入一行(此时第二行还没有被修改,所以我们可以匹配到原来的内容) # 2. 然后修改第二行(将echo "Start"改为echo "Start of the program") # 3. 修改第四行(原来的第三行)的name变量。 # 但是这样依赖行号,如果脚本有变化就不稳定。 # 更好的做法是:不依赖行号,而是依赖内容。 # 步骤调整: # 1. 插入:在匹配到`echo "Start"`的行后面插入一行。注意:此时可能有多个匹配,但我们希望是第一个。 # sed默认会在每一处匹配都插入,但我们只希望插入一次?我们可以通过匹配上下文或者只匹配第一个出现的。 # sed没有内置的方法只处理第一次匹配,但我们可以通过退出命令来实现在第一次匹配后退出。例如: # sed -i '0,/echo "Start"/{//a echo "This is inserted line"}' "$file" # 但是,0,/pattern/ 表示从第一行到第一次匹配pattern的行,然后在匹配行后追加(a命令)。但是追加命令只会在匹配的范围内执行一次。 # 或者使用: # sed -i '/echo "Start"/{a echo "This is inserted line" # :a; n; ba}' "$file" # 这个命令会在匹配的第一行后插入,然后跳过剩余行?但这样很复杂。 # 简单做法:先插入,即使有多处匹配,但如果我们知道只有一处,或者我们就是要在每一处都插入,那也可以。但这里我们只希望在第一处插入。 # 一种方法:使用awk,可以控制只处理第一次匹配。 # 由于我们主要使用sed,这里介绍一个技巧:使用两次sed,第一次只处理第一处匹配。 # 使用:sed '0,/pattern/s//replacement/' # 在0到第一次匹配pattern的范围内替换,但这里我们是追加,不是替换。 # 实际上,对于追加,我们可以这样: # 在第一次匹配到pattern的行后追加一行,然后退出(不再处理后面的行): # sed -i '1,/echo "Start"/!b;//{a echo "This is inserted line" # q}' "$file" # 这个命令比较复杂,而且会提前退出,导致后续修改无法进行。 # 因此,我们可能需要分步骤进行,或者使用awk。 # 考虑到复杂性,如果我们的目标行是确定的(比如第二行),我们可以直接指定行号: # 插入:在第二行后面插入 # sed -i '2a echo "This is inserted line"' "$file" # 但是,如果文件的开头可能变化(比如可能增加注释行),那么第二行可能就不是我们想要的。 # 折中方案:我们通过匹配内容来找到行号,然后使用行号进行后续操作。 # 例如,获取echo "Start"第一次出现的行号: # line_num=$(grep -n 'echo "Start"' "$file" | head -1 | cut -d: -f1) # 然后使用这个行号进行插入和修改。但是,在sed中使用变量行号需要特别注意,因为插入后行号会变。 # 因此,我们分三步: # 第一步:修改第一个echo命令(注意:此时还没有插入,所以它在第二行?不一定,但我们可以通过匹配来修改,不依赖行号) # 第二步:在修改后的echo命令所在行后面插入(因为修改后,该行内容已经改变,所以我们无法再匹配原来的内容?) # 这就陷入了一个循环。 # 重新思考需求:我们实际上希望的是在第一个echo命令(即原来的echo "Start")后面插入一行,然后修改这个echo命令的内容,最后修改name变量。 # 我们可以这样: # 1. 先修改第一个echo命令:将"Start"改为"Start of the program"(通过匹配原内容) # 2. 然后,在修改后的行(内容为:echo "Start of the program")后面插入?不行,因为修改后我们无法再匹配到原来的内容,而且修改后的内容可能不是唯一的。 # 或者,我们可以在修改前插入: # 1. 在匹配到`echo "Start"`的行后面插入一行(只做第一次匹配) # 2. 然后,将匹配到的这一行(即原来的echo "Start")修改为"Start of the program" # 3. 修改name变量。 # 这样,插入后,原来的行还在,我们修改它。name变量行在插入后位置后移,但我们仍然可以通过匹配来修改。 # 具体步骤: # 1. 插入:在第一个匹配到`echo "Start"`的行后插入一行。 # 我们可以用:sed -i '0,/echo "Start"/{//a echo "This is inserted line"}' test.sh # 但是,0,/pattern/在有些sed版本中不支持,比如macOS的sed。所以为了兼容,我们使用一种方法:只处理第一次匹配。 # 使用:sed -i -e '/echo "Start"/{a echo "This is inserted line"' -e ':a' -e 'n;$!ba' -e '}' test.sh # 这个命令比较复杂,而且可能会在每一处匹配都插入。所以如果我们确保只有一个匹配,或者允许插入多处,可以简化。 # 假设我们只允许插入一次,并且知道文件结构,我们可以使用: # sed -i '/echo "Start"/a echo "This is inserted line"' test.sh # 在每一处匹配后都插入一行,但这里我们只希望一次。 # 如果文件中有多处,而我们只想在第一次匹配时插入,我们可以: # 先找到第一次出现的行号,然后在该行后插入: line_num=$(grep -n 'echo "Start"' "$file" | head -1 | cut -d: -f1) sed -i "${line_num}a echo \"This is inserted line\"" "$file" # 2. 然后,修改原来的那一行(即第$line_num行): sed -i "${line_num}s/.*/echo \"Start of the program\"/" "$file" # 3. 修改name变量(通过匹配): sed -i '/^name=/s/Alice/Bob/' "$file" # 注意:第一步插入后,原来的行号$line_num位置的行变成了我们修改后的内容,而原来的第$line_num行后面的行都后移了一行。 # 但是,第二步修改的是第$line_num行,而插入后,原来的第$line_num行内容并没有改变,只是我们在它后面插入了一行。所以我们需要修改的正是这一行。 # 因此,这个方法是可行的。 # 但是,使用行号需要确保grep找到的行号是准确的,并且文件中没有其他包含相同字符串的行。 # 所以,我们最好使用更精确的匹配。例如,匹配整行:grep -n '^ *echo "Start"',以避免匹配到其他地方的相同字符串。 # 综合以上,我们写一个修改脚本modify.sh: ```bash #!/bin/bash file="test.sh" # 1. 找到第一个echo "Start"的行号 line_num=$(grep -n 'echo "Start"' "$file" | head -1 | cut -d: -f1) # 如果找到了 if [ -n "$line_num" ]; then # 在该行后插入一行 sed -i "${line_num}a echo \"This is inserted line\"" "$file" # 修改该行内容 sed -i "${line_num}s/.*/echo \"Start of the program\"/" "$file" else echo "Target line not found." fi # 2. 修改name变量 sed -i '/^name=/s/Alice/Bob/' "$file" ``` 注意:这个脚本在第一次sed插入后,文件行数增加,但第二次sed修改行号$line_num时,该行号对应的行仍然是之前找到的那一行(因为插入发生在该行之后,所以该行本身没有后移,只是下面的行下移了)。所以修改该行是安全的。 但是,如果文件中没有匹配到`echo "Start"`,那么line_num为空,我们需要处理这种情况。 外,修改name变量时,如果匹配不到`^name=`,则不会做任何操作。 这个脚本只是一个示例,实际应用中需要根据具体需求调整匹配规则。 总结:使用shell脚本修改一个shell脚本,主要使用文本处理工具(sed、awk、grep等)。关键是要注意修改操作对文件行号的影响,以及匹配的精确性。 安全建议:在修改重要文件前,先备份。 §§相关问题§§ 1. 如何防止在修改脚本文件时误操作导致脚本损坏? 2. 除了sed和awk,还有哪些工具可以用于修改脚本文件? 3. 如何确保在修改脚本文件时,匹配的字符串是唯一的?</think>### 使用 Shell 脚本修改一个 Shell 脚本的方法 在 Shell 脚本中修改一个脚本文件的核心方法是使用**文本处理工具**(如 `sed`、`awk`)和**流重定向**。以下是具体方法和示例: --- #### 1. **基础方法:使用 `sed` 进行文本替换** `sed` 是流编辑器,适合简单的行内替换: ```bash # 将 old.sh 中的 "old_text" 替换为 "new_text" sed -i 's/old_text/new_text/g' target_script.sh ``` **示例**:修改配置值 假设 `config.sh` 中有 `PORT=8080`,需要改为 `PORT=9000`: ```bash sed -i 's/PORT=8080/PORT=9000/' config.sh ``` --- #### 2. **按行号修改内容** 通过行号精准定位修改位置: ```bash # 在第 5 行后插入新内容 sed -i '5a\echo "New Line Added"' target.sh # 替换第 3 行内容 sed -i '3c\new_command' target.sh ``` **示例**:在脚本开头插入注释 ```bash sed -i '1i\#!/bin/bash\n# Modified by automation script' deploy.sh ``` --- #### 3. **模式匹配修改** 结合正则表达式定位特定内容: ```bash # 在匹配到 "start_service" 的行后追加内容 sed -i '/start_service/a\logger "Service started"' service.sh ``` --- #### 4. **多步骤修改:使用临时文件** 复杂修改建议使用临时文件避免错误: ```bash # 生成临时修改文件 sed 's/old/new/g' original.sh > temp.sh awk '/pattern/{print $0 "\nnew_content"}1' temp.sh > modified.sh # 验证后替换原文件 mv modified.sh original.sh ``` --- #### 5. **变量化修改(动态内容)** 在脚本中定义变量实现动态修改: ```bash new_value="production" sed -i "s/ENV=.*/ENV=$new_value/" config.sh ``` > **注意**:双引号 `"` 才能解析变量,单引号 `'` 会视为普通文本。 --- #### 6. **安全操作建议** - **备份原文件**: ```bash cp target.sh target.sh.bak ``` - **测试修改效果**: 去掉 `-i` 选项先预览结果: ```bash sed 's/old/new/' target.sh ``` - **严格匹配**: 使用完整锚定(如 `^VAR=`)避免误替换: ```bash sed -i 's/^PATH=.*/PATH=\/new\/path/' env.sh ``` --- ### 完整示例 **任务**:修改 `deploy.sh` 中的配置并添加错误检查 ```bash #!/bin/bash # 修改配置 sed -i 's/DEBUG_MODE=true/DEBUG_MODE=false/' deploy.sh # 在 "main" 函数开头插入日志命令 sed -i '/main()/i\echo "[$(date)] Starting deployment" >> log.txt' deploy.sh # 替换过时的命令(整行替换) sed -i '/old_command/c\new_command --safe-mode' deploy.sh ``` --- ### 关键注意事项 1. **权限问题**:确保脚本有目标文件的写权限 2. **跨平台兼容**: macOS 需用 `sed -i ''`(无备份)或 `sed -i.bak`(保留备份) 3. **特殊字符转义**: 对 `/`、`&` 等字符需转义:`s/\/old\/path/\/new\/path/`
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值