Search code examples
phplaravelbashlaravel-scheduler

Execute Long Multi Bash Commands in Laravel Scheduler


I'm trying to wipe sensitive history commands daily off my server via Laravel scheduler

When I run this code:

$schedule->exec(`for h in $(history | grep mysql | awk '{print $1-N}' | tac) ; do history -d $h ; done; history -d $(history | tail -1 | awk '{print $1}') ; history`)->{$interval}()->emailOutputTo($email)->environments('prod');

I get this error:

⚡️  app2020  php artisan schedule:run                                                                                                             
                                                                                                                                                   
   ErrorException                                                                                                                                  
                                                                                                                                                   
  Undefined variable: h                                                                                                                            
                                                                                                                                                   
  at app/Console/Kernel.php:44

Noted that this is a perfect working command

for h in $(history | grep mysql | awk '{print $1-N}' | tac) ; do history -d $h ; done; history -d $(history | tail -1 | awk '{print $1}') ; history

Note: I know I can achieve it by adding the command above to the crontab, but the goal is to keep all cron activities organize in the Laravel project

Since bash and PHP use $ for their variable, how would one go above and make similar bash command works ?

Any hints, I will take.


Solution

  • You're wrapping your command in backticks, which will both interpolate variables and execute the value as a command. You want neither of these things.

    Use single quotes to build the command, and then pass it to exec().

    $cmd =  'for h in $(history | grep mysql | awk \'{print $1-N}\' | tac) ;';
    $cmd .= '    do history -d $h ;';
    $cmd .= 'done;';
    $cmd .= 'history -d $(history | tail -1 | awk \'{print $1}\') ;';
    $cmd .= 'history';
    
    $schedule
        ->exec($cmd)
        ->{$interval}()
        ->emailOutputTo($email)
        ->environments('prod');
    

    For a somewhat more efficient approach, you might try just editing the history file directly.

    $cmd = 'sed -i /mysql/d "$HOME/.bash_history"';
    

    There's no need to erase the last entry in the file, as it's a history of interactive shell commands.

    Obviously the best thing to do would be not putting "sensitive" things in the command line. For MySQL, this can be done by adding credentials to the .my.cnf file.