#!/usr/bin/env perl use v5.34; use warnings; use feature 'signatures'; no warnings ('experimental::signatures', 'experimental::smartmatch'); use Getopt::Std (); sub usage($fh) { print $fh <<~'EOF'; Usage: x COMMANDS... [ '&&' / '||' / '|' ] COMMANDS... x COMMANDS... [ 'AND' / 'OR' / 'PIPE' ] COMMANDS... x -h EOF } sub help($fh) { print $fh <<~'EOF'; Options: -h, --help show this message COMMAND the command to be executed '&&' / AND "AND" logical operator '||' / OR "OR" logical operator '|' / PIPE pipe operator Run command chained together with operators. NOTE: Remember to quote '&&', 'OR' and '|' operators, otherwise they'll get captured by the shell and not be passed to the 'x' program! Examples: Measure the time of two commands: $ time x sleep 1 '&&' sleep 2 # equivalent to: $ time sh -c 'sleep 1 && sleep 2' Notify when either of the commands finish: $ boop x cmd-1 '||' cmd-2 EOF } for (@ARGV) { last if $_ eq '--'; if ($_ eq '--help') { usage *STDOUT; help *STDOUT; exit; } } my %opts; if (!Getopt::Std::getopts('h', \%opts)) { usage *STDERR; exit 2; } if ($opts{h}) { usage *STDOUT; help *STDOUT; exit; } sub status_for($n) { if ($n == -1) { return 127; } elsif ($n & 127) { return $n & 127; } else { return $n >> 8; } } my @AND = ('&&', 'AND'); my @OR = ('||', 'OR'); my @PIPE = ('|', 'PIPE'); my @OPS = (@AND, @OR, @PIPE); my @CMD; for (@ARGV) { if ($_ ~~ @OPS) { system @CMD; @CMD = (); if ($_ ~~ @AND && $?) { exit status_for($?); } elsif ($_ ~~ @OR && !$?) { exit 0; } elsif ($_ ~~ @PIPE) { ... } } else { push @CMD, $_; } } exit status_for(system @CMD); __END__ =head1 NAME x - chain shell commands without creating a subshell =head1 SYNOPSYS x COMMANDS... [ '&&' / '||' / '|' ] COMMANDS... x COMMANDS... [ 'AND' / 'OR' / 'PIPE' ] COMMANDS... x -h =cut