如何每小时自动对 运行 制作一个脚本,使其完全按照命令行执行?

how can a script be made to run automatically every hour, such that it performs exactly as on the command line?

我的 perl 脚本,z.pl, 使用 Time::Local qw( timelocal_posix timegm_posix );File::Rsync。 (您会在 post 的底部找到 z.pl。)

目前我在 macOS Catalina 10.15.7 下的 macbook pro 上工作。 但我希望我的代码是可移植的, 以便将来, 如果我将我的文件移动到不同的机器或操作系统(或至少另一个 *nix), 代码不需要修复--- 除了源路径和目标路径的可能更改之外。 为此,我通常的 shebang 是 #!/usr/bin/env perl.

可以访问 Time::Local qw( timelocal_posix timegm_posix ); 我必须安装一个 up-to-date perl 版本。 (为此,我使用了自制软件。) 这是因为我的 macbook pro 上加载的 perl 已经过时了。 我们在这里看到了不同之处:

> /usr/bin/perl --version

This is perl 5, version 18, subversion 4 (v5.18.4) built for darwin-thread-multi-2level
(with 2 registered patches, see perl -V for more detail)

Copyright 1987-2013, Larry Wall

> /usr/bin/env perl --version

This is perl 5, version 34, subversion 0 (v5.34.0) built for darwin-thread-multi-2level

Copyright 1987-2021, Larry Wall

> /usr/local/bin/perl --version # same as previous

This is perl 5, version 34, subversion 0 (v5.34.0) built for darwin-thread-multi-2level

我们在这里看到旧版本没有我需要的东西:

> /usr/bin/env perl  -e 'use Time::Local qw( timelocal_posix timegm_posix );'
> /usr/bin/perl  -e 'use Time::Local qw( timelocal_posix timegm_posix );'
"timelocal_posix" is not exported by the Time::Local module
 "timegm_posix" is not exported by the Time::Local module
Can't continue after import errors at -e line 1.
BEGIN failed--compilation aborted at -e line 1.

我希望此代码每小时运行一次。 为此,可以打开命令行并输入

while [ : ]; do z.pl; sleep 3600; done

然后大约每小时 运行。 但这需要用户在每次重新启动计算机时记住 re-launch 命令。 它不是自动的。

有人会认为 cron 就是答案。 但是当我尝试用 cron 调用它时,第一个障碍是 cron 不知道我的路径。 因此,我的便携式 shebang 指向过时的 perl。 所以,为了让cron下的z.pl到运行,我对non-portability和hard-coded做了让步 shebang #!/usr/local/bin/perl.

但即使进行了此修改,rsync 部分仍会失败。 为了演示这一点,首先在我键入的命令行上

~/tmplocal/DUMS> z.pl >> out.txt 2>&1

随后我临时修改了我的 crontab 以包含

00  *  *  *  *  $HOME/u/kh/bin/z.pl >> $HOME/tmplocal/DUMS/z.pl/out.txt 2>&1

生成的 out.txt 文件如下。这两个不同的调用不仅可以通过 header 中的时间戳来区分,还可以通过 PATH 变量的差异来区分。在 cron 下,脚本不知道我的路径。

------------------------2021-12-27 Mon 12:52:22------------------------
HOME: /Users/kpr
USER: kpr
PATH: /Users/kpr/u/kh/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/TeX/texbin:/Library/Apple/usr/bin
$VAR1 = {
          'err' => '',
          'lastcmd' => 'rsync --itemize-changes --update --stats --compress --recursive --times --perms --links --verbose --exclude=.swp --exclude=.swo --exclude=.*.swp --exclude=.*.swo --exclude=JAW.gnucash.LCK --exclude=JAW.gnucash.LNK --exclude=JAW.gnucash*log /Users/kpr/TEMPBACKUP /Volumes/oom/quick',
          'out' => 'building file list ... done
.d..t.... TEMPBACKUP/2021/2021d/
>f+++++++ TEMPBACKUP/2021/2021d/2021-12-27-125000-z.pl
>f+++++++ TEMPBACKUP/2021/2021d/2021-12-27-125026-z.pl
>f+++++++ TEMPBACKUP/2021/2021d/2021-12-27-125055-z.pl
>f+++++++ TEMPBACKUP/2021/2021d/2021-12-27-125211-z.pl

Number of files: 184014
Number of files transferred: 4
Total file size: 60206266413 bytes
Total transferred file size: 7596 bytes
Literal data: 7596 bytes
Matched data: 0 bytes
File list size: 7048863
File list generation time: 16.863 seconds
File list transfer time: 0.000 seconds
Total bytes sent: 7052167
Total bytes received: 114

sent 7052167 bytes  received 114 bytes  328013.07 bytes/sec
total size is 60206266413  speedup is 8537.13
',
          'realstatus' => 0,
          'status' => 0
        };

------------------------2021-12-27 Mon 13:00:01------------------------
HOME: /Users/kpr
USER: kpr
PATH: /usr/bin:/bin
$VAR1 = {
          'err' => 'rsync: failed to set times on "/Volumes/oom/quick/TEMPBACKUP/2021/2021d": Operation not permitted (1)
rsync: mkstemp "/Volumes/oom/quick/TEMPBACKUP/2021/2021d/.2021-12-27-125615-month060.tex.qZ6XYt" failed: Operation not permitted (1)
rsync: mkstemp "/Volumes/oom/quick/TEMPBACKUP/2021/2021d/.2021-12-27-125720-junk.mvgt5F" failed: Operation not permitted (1)
rsync: failed to set times on "/Volumes/oom/quick/TEMPBACKUP/2021/2021d": Operation not permitted (1)
rsync error: some files could not be transferred (code 23) at /AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/rsync/rsync-54.120.1/rsync/main.c(996) [sender=2.6.9]
',
          'lastcmd' => 'rsync --itemize-changes --update --stats --compress --recursive --times --perms --links --verbose --exclude=.swp --exclude=.swo --exclude=.*.swp --exclude=.*.swo --exclude=JAW.gnucash.LCK --exclude=JAW.gnucash.LNK --exclude=JAW.gnucash*log /Users/kpr/TEMPBACKUP /Volumes/oom/quick',
          'out' => 'building file list ... done
.d..t.... TEMPBACKUP/2021/2021d/
>f+++++++ TEMPBACKUP/2021/2021d/2021-12-27-125615-month060.tex
>f+++++++ TEMPBACKUP/2021/2021d/2021-12-27-125720-junk

Number of files: 184016
Number of files transferred: 2
Total file size: 60206953341 bytes
Total transferred file size: 686928 bytes
Literal data: 686928 bytes
Matched data: 0 bytes
File list size: 7048935
File list generation time: 18.892 seconds
File list transfer time: 0.000 seconds
Total bytes sent: 7272636
Total bytes received: 70

sent 7272636 bytes  received 70 bytes  285204.16 bytes/sec
total size is 60206953341  speedup is 8278.48
',
          'realstatus' => 5888,
          'status' => 23
        };

这里是z.pl:

#!/usr/local/bin/perl # 2021v12v26vSunv18h08m02s for cron, which does not know my environment
##!/usr/bin/env perl  # does not have the required packages under cron
use strict; use warnings;
use Data::Dumper qw(Dumper); $Data::Dumper::Sortkeys = 1;
use Time::Local qw( timelocal_posix timegm_posix ); use POSIX qw(strftime);
select STDERR; # so that STDERR and STDOUT will appear in the proper order; without this, STDERR comes before STDOUT 

my $epoch_s=time;
my $dateJ  =(strftime '%Y-%m-%d %a %H:%M:%S', localtime($epoch_s));
print 
"\n",
'------------',
'------------',
$dateJ,
'------------',
'------------',
"\n",
;
print 'HOME: ',$ENV{HOME},"\n";
print 'USER: ',$ENV{USER},"\n";
print 'PATH: ',$ENV{PATH},"\n";

my%subvar;
$subvar{src}=join('/',$ENV{HOME},'TEMPBACKUP');
unless(-r$subvar{src} and -w$subvar{src} and -d$subvar{src})
{
    die $subvar{src},q#: unless(-r$subvar{src} and -w$subvar{src} and -d$subvar{src})#;
}
$subvar{dest}=q#/Volumes/oom/quick#;
unless(-r$subvar{dest} and -w$subvar{dest} and -d$subvar{dest})
{
    die $subvar{dest},q#: unless(-r$subvar{dest} and -w$subvar{dest} and -d$subvar{dest})#;
}

use File::Rsync;
my $FileRsync=File::Rsync->new(
     itemize_changes=>1,
     delete         =>0,
     update         =>1,
     stats          =>1,
    compress       =>1,
     recursive      =>1,
     times          =>1,
     perms          =>1,
     links          =>1,
    verbose        =>1,
     exclude        =>['.swp','.swo','.*.swp','.*.swo','JAW.gnucash.LCK','JAW.gnucash.LNK','JAW.gnucash*log',],
);
$FileRsync->exec(
          src=>$subvar{src},
          , 
          dest=>$subvar{dest},
);
$subvar{rsync}{out}='';$subvar{rsync}{out}=join('',@{$FileRsync->out})if(defined$FileRsync->out);
$subvar{rsync}{err}='';$subvar{rsync}{err}=join('',@{$FileRsync->err})if(defined$FileRsync->err);
$subvar{rsync}{status}=$FileRsync->status;
$subvar{rsync}{realstatus}=$FileRsync->realstatus;
$subvar{rsync}{lastcmd}=$FileRsync->lastcmd;
print Dumper$subvar{rsync};

有没有办法让 cron 与此一起工作?或者我应该使用 cron 以外的其他工具吗?

要启动 shell 并使其 运行 成为其登录脚本,您首先需要一个 shell,使用以下命令:

/bin/sh -l -c '...shell cmd...'

您也可以在 crontab 中设置环境变量,但这可能更容易。

这个对我自己问题的回答只是补充了@ikegami 解决方案的细节。我希望这是正确的协议,而不是编辑@ikegami 的作品。

首先,在 https://unix.stackexchange.com/questions/308907/how-to-set-shell-bin-bash-globally-for-cron 我们发现以下内容:

There is no way to set a global default shell for cron jobs.

这似乎解释了为什么 cron “不知道我的路径”。我习惯的路径是bashshell自动设置的交互路径。 作为 https://linuxhint.com/path_in_bash/ 说:

By default, the PATH variable contains the following locations.

/usr/bin /usr/sbin /usr/local/bin /usr/local/sbin /bin /sbin /snap/bin (if Snap is installed)

但是 cron shell 是 /bin/sh;这就是为什么 cron 没有我通常的路径。

现在,感谢@ikegami,以下两个解决方案奏效了:

> crontab -l
PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/TeX/texbin:/Library/Apple/usr/bin
00 16  *  *  *  $HOME/u/kh/bin/jaw20200410cronmakeccalfile.sh
45 14  *  *  *  $HOME/u/kh/bin/testcron.pl >> $HOME/tmplocal/DUMS/testcron.pl/out.txt 2>&1
> 

及以下,在crontab中少了一行代码,也避免了路径的硬编码,容易出现程序员(我自己)的错误:

> crontab -l
00 16  *  *  *  $HOME/u/kh/bin/jaw20200410cronmakeccalfile.sh
55 14  *  *  *  /bin/bash -l -c '$HOME/u/kh/bin/testcron.pl >> $HOME/tmplocal/DUMS/testcron.pl/out.txt 2>&1'
> 

请注意,我的问题的第二部分,关于 rsync 和权限,在这里没有得到回答。既然路径是固定的,我将不得不重新调查。