首页 > 解决方案 > How do you load bash_profile for bash commands run from perl script?

问题描述

I wrote simple command that lets me run the last N commands from terminal history. It looks like this: $> r 3 which will replay the last 3 commands.

I have the following alias in my bash profile: alias r="history -w; runlast $1"

And then the following simple perl script for the runlast command:

#!/usr/bin/env perl
use strict;
use warnings;

my $lines = $ARGV[0] || exit;

my @last_commands = split /\n/, 
    `bash -ic 'set -o history; history' | tail -$lines`;

@last_commands = 
    grep { $_ !~ /(^r |^history |^rm )/ } 
    map { local $_ = $_; s/^\s+\d+\s+//; $_ } 
    @last_commands;

foreach my $cmd (@last_commands) {
  system("$cmd");
}

This works but my bash profile has aliases and other features (e.g. color output) I want the perl script to have access to. How do I load the bash profile for perl so it runs the bash commands with my bash profile? I read somewhere that if you "source the bash profile" for perl you can get it to work. So I tried adding source ~/.bash_profile; to my r command alias but that didn't have an effect. I'm not sure if I was doing that correctly, though.

标签: bashperl

解决方案


系统派生出一个运行 shell 的进程,该进程是非登录和非交互的;所以没有初始化完成,你没有别名。另请注意,使用的 shell 是/bin/sh,它通常是指向另一个 shell 的链接。这通常bash但并非总是如此,因此请bash明确运行。

为了避免这种情况,您需要使用别名来获取文件,但正如bash手册页所说

当 shell 不是交互式时,别名不会展开,除非 expand_aliases shell 选项使用 shopt 设置(参见下面 SHELL BUILTIN COMMANDS 下的 shopt 描述)。

因此,如上所述,您需要shopt -s expand_aliases。但是还有另一个问题:在同一条物理线路上,别名尚不可用;所以它不会像这样在单行中工作。

我还建议将别名放在 中.bashrc,或者放在一个单独的文件中。

解决方案

  • 添加shopt -s expand_aliases到您的~/.bashrc, 并在定义别名之前(或获取它们的文件),并bash作为登录 shell运行

    system('/bin/bash', '-cl', 'source ~/.bashrc; command');
    

    哪里-l--login.

    在我的测试中source ~/.bashrc,不需要;但是,手册页说

    当 bash 作为交互式登录 shell 或作为带有 --login 选项的非交互式 shell 调用时,它首先从文件/etc/profile中读取并执行命令(如果该文件存在)。读取该文件后,它会按顺序查找~/.bash_profile~/.bash_login~/.profile,并从第一个存在且可读的文件中读取并执行命令。

    并继续指定~/.bashrc在非登录的交互式 shell 运行时读取的内容。所以我添加了明确的采购。

    在我的测试中,在不作为登录 shell 运行时采购.bashrcshopt添加)不起作用,我不知道为什么。

    这有点重手。此外,从脚本运行初始化可能是不可取的。

  • ~/.bashrc并发出shopt命令,然后在命令前换行

    system('/bin/bash', '-c', 
        'source ~/.bashrc; shopt -s expand_aliases\ncommand');
    

    真的。有用。

最后,这有必要吗?它自找麻烦,而且可能有更好的设计。

其他的建议

  • 反引号(qx)是上下文感知的。如果在列表上下文中使用它——例如,它的返回分配给一个数组——那么命令的输出将作为行列表返回。当您将它用作参数时,split它在标量上下文中,当所有输出都以一个字符串返回时。就放下split

    my @last_commands = `bash -ic 'set -o history; history $lines`;
    

    我也history N用来获取最后N一行的地方。在这种情况下,换行符会保留。

  • history N返回N历史的最后几行,因此无需管道last

  • map可以在不更改原始内容的情况下完成a 中的正则表达式替换

    map { s/^\s+\d+\s+//r } @last_commands;
    

    使用/r修饰符,s///运算符返回新字符串,而不是更改原始字符串。从 v5.14开始提供这种“非破坏性替换”

  • 不需要$_在最后明确使用grep,也不需要在正则表达式中使用括号

    grep { not /^r |^history |^rm ?/ } ...
    

    或者

    grep { not /^(?:r|history|rm)[ ]?/ } ...
    

    现在需要括号的地方,但因为它仅用于分组,?:所以它不会捕获匹配项。我曾经[ ]强调该空间是有意的;这是没有必要的。

    我还添加?了使空间可选,因为history(和r?)可能没有空间。


推荐阅读