如何在bash脚本中返回特定命令?

时间:2015-01-01 13:07:00

标签: linux bash shell

我需要一个方法来跟踪bash脚本中最后成功执行的命令。例如,我有这个脚本:

#!/bin/bash
command1
command2

command1command2都将返回一些退出代码。我需要的是保存上次成功执行的命令,当我重新运行这个脚本时,我希望它从那一点开始。因此,如果command1正确执行且command2失败,则下次运行脚本时,它将从command2开始。

最简单的方法是将执行信息存储在附加文件中,该文件将在执行前由脚本读取。在执行命令之前,脚本将检查该命令是否已成功执行。

有更好的方法吗?这个实现有什么例子吗?我想我在配置脚本中看到了类似的东西。

4 个答案:

答案 0 :(得分:3)

#!/bin/bash -e
#
#  run those commands that did not yet run
#  breaks upon failed command due to -e
#
#  keeps the line number of the last successful command in lastline
#
#  first, some preparation:
#

lastfile=$(readlink -f lastline) # inspired by ton1c: Get absolute path
test -f $lastfile && lastline=`cat $lastfile` >/dev/null
test -z "$lastline" && lastline=0

e()
{
   thisline="$BASH_LINENO"
   test $lastline -lt $thisline || return 0
   "$@"
   echo $thisline > $lastfile
}

#
# and now prepend every command by e
# for example run this, interrupt the sleep 5 and run again
# to restart ALL commands, remove the file "lastline"
#

echo running sleep 3
e sleep 3
echo running sleep 5
e sleep 5
echo running sleep 7
e sleep 7

答案 1 :(得分:1)

#!/usr/bin/perl

use strict;

open(COMMANDS,"commands.txt")||die "Couldn't open command file\n";
my @cmd = <COMMANDS>;  #get all the file contents into array
close(COMMANDS);

#whacks file
open(COMMANDS,">commands.txt")||die "Couldn't open command file\n";




foreach my $cmd ( @cmd)
  { chomp($cmd);
    my($do,$status) = split(/:/,$cmd);

    if($status =~ /pending/i)
      {
    my $return = qx($do);

    if(!$return)
      {$status='failed';}
    else
      { print "$do returned $return\n";
        $status ='completed';
      }
      }
    else
      {$status ='pending';}
    print COMMANDS "$do:$status\n";

}
close(COMMANDS);

命令内容:

echo "fish":pending
date +%D:pending
whoamix:failed
whoami:pending

你必须弄清楚一些逻辑。如果命令失败,qx将返回undef(perl为false),否则返回输出。其次,你需要修复它,一旦列出的所有'完成'perl将它们再次更改为所有挂起。

答案 2 :(得分:0)

您是否希望在将来的任意时间重新启动脚本(这将很难),或者您是否只需要临时运行shell来执行某些命令然后返回脚本?如果是后者,你可以做类似的事情:

#!/bin/sh
cmd1 || ${SHELL-bash} || exit 1
cmd2 || ${SHELL-bash} || exit 1
cmd3 || ${SHELL-bash} || exit 1

如果cmd2失败,你应该得到一个shell提示符。做你想做的。完成后,如果希望脚本在cmd3处获取,请使用exit 0退出shell。如果要中止,请使用exit 1退出shell。

答案 3 :(得分:0)

这可能不是您正在寻找的答案,但简单的Makefile似乎满足您的要求。在开箱即用时终止执行错误,其目的是避免重复处理步骤。特别是如果(至少大部分)命令生成输出文件,那将是一个相当自然的选择。如果没有,保留一个简单的状态文件是一个常见的Make成语,用于将命令序列标记为成功完成。

(如果您是Make的新手,请注意目标中的每个命令都需要有缩进的文字选项卡。)

.PHONY: all
all: .command1-done .command2-done
.command1-done:
    command1
    touch $@
# Comment out the dependency to avoid forcing sequential execution
.command2-done: .command1-done
    command2
    touch $@

.PHONY: clean
clean:
    rm -f .*-done

因此,这会运行command1,如果成功,则继续执行配方中的下一个命令,该命令在信号量文件上运行touch(Make变量$@扩展为当前目标名称)。如果这也成功,它会运行command2,同样会创建它的&#34;完成&#34;如果成功则归档。再次运行make将告诉您不需要运行任何命令:

$ make
command1
touch .command1-done
command2
touch .command2-done

$ make
make: Nothing to be done for `all'.

如果您确实关心目标的执行顺序,那通常是因为存在依赖关系(command2失败,或者更糟糕的是,除非在command1之后运行,否则会产生错误的结果。需要声明这样的依赖关系 - 我举了一个如何声明这种关系的例子。如果没有依赖关系,你可能不应该声明一个;然后,Make会以任意顺序运行你的目标(虽然我使用的版本通常是可预测的);或者你可以让Make与make -j并行运行。

(状态文件通常以点开头,以使其隐藏在常规目录列表中。)

更现实地说,也许你的命令实际上会产生一些输出:

.PHONY: all
all: grep.out wc.out
grep.out:
    grep -Fw Debian /etc/motd >$@
wc.out: grep.out
    wc -l <$< >$@

(不幸的是,即使目标失败,shell重定向也会创建输出文件。解决方法是使用临时输出文件,然后仅在成功时将其移动到位:

.PHONY: all
all: grep.out wc.out
grep.out:
    grep -Fw Debian /etc/motd >.$@.tmp
    mv .$@.tmp $@
wc.out: grep.out
    wc -l <$< >.$@.tmp
    mv .$@.tmp $@

.PHONY: clean
clean:
    rm -f .*.tmp

但无论如何,这已经太长了。)