我需要一个方法来跟踪bash脚本中最后成功执行的命令。例如,我有这个脚本:
#!/bin/bash
command1
command2
command1
和command2
都将返回一些退出代码。我需要的是保存上次成功执行的命令,当我重新运行这个脚本时,我希望它从那一点开始。因此,如果command1
正确执行且command2
失败,则下次运行脚本时,它将从command2
开始。
最简单的方法是将执行信息存储在附加文件中,该文件将在执行前由脚本读取。在执行命令之前,脚本将检查该命令是否已成功执行。
有更好的方法吗?这个实现有什么例子吗?我想我在配置脚本中看到了类似的东西。
答案 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
但无论如何,这已经太长了。)