help-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Running commands as "$@"


From: Greg Wooledge
Subject: Re: Running commands as "$@"
Date: Thu, 2 Feb 2023 14:09:09 -0500

On Thu, Feb 02, 2023 at 05:35:37PM +0100, Cristian Zoicas wrote:
> Let's  assume that in a script I have a command:
> 
>     ls -l
> 
> Without making *any* modification to this line I want to put something in 
> front of
> it (a shell function for example) and have all the tokens that form that 
> command
> passed to the function and eventually executed. For example, for the command 
> above
> I would like to have something like this:
> 
> 
> check_execution_status()
> {
>     "$@"
>     echo "command $@: $?"
> }
> 
> check_execution_status ls -l

This looks like some weird variant of
<https://mywiki.wooledge.org/BashFAQ/050>.

> This case works. But if I have this line
> 
> A=B
> 
> then it will not work for
> 
> check_execution_status A=B

Then don't add it to those lines.  Only add it to the lines you actually
want to "check".  Your approach only works with simple commands with no
temporary environment variable assignments, so only put it on those.

But, really?  This is just command tracing.  Bash already has set -x for
this.  Why isn't set -x good enough?  Because it doesn't print the exit
status?

As far as I'm aware, there is *no* post-command hook in bash.  There are
plenty of before-command hooks (set -x and the DEBUG trap being the main
ones for scripts), but none that trigger *after* a command, so that you
can print the exit status.

The way this is usually done in interactive shells involves putting $?
in PS1 so that the exit status of your last command is shown as part
of your prompt.  Obviously, scripts can't make use of that, because they
don't print a prompt between commands, so they won't expand PS1.

You could set up a double shell thing to force your "script" to be read
by an interactive shell, so that you can set up PS1 to show the exit
status of every command, like:


#!/bin/sh
bash -is <<'EOF'
PS1='Exit status: $?\nCommand: '
echo hello world
(exit 14)
echo good-bye
EOF


But I don't think that's really what you want.  For starters, it redirects
stdin, which you might need.  It also doesn't trace the execution of
commands inside a loop or function, since those aren't being read over
and over again.  It only works with completely linear scripts.

The only other trick I can think of that *might* do what you seem to
want would involve setting up a magic alias.  This is in the "SHOULD NOT
be done" category that I referred to earlier.[1]  But, theoretically?
You could use a magic alias to capture a command exactly as it appears
(redirections, comments and all), eval it, and then print your message
that shows the exit status.  At least, that works in interactive shells.
I've never attempted to use a magic alias in a script before.

Now I'm just curious enough to try it.  Let's explore that.


#!/bin/bash
shopt -s expand_aliases
HISTFILE=/dev/null
set -o history

magic_helper() {
    local cmd rc
    read -r _ _ cmd < <(history 1)
    eval "$cmd"
    rc=$?
    printf 'Command %s: %s\n' "$cmd" "$rc"
    return "$rc"
}
alias check='magic_helper # '

check echo 'hello world'    # greet the user
check time sleep 1
check foo=bar
check (exit 14)


There.  That actually works, surprisingly enough.

Once again, I repeat that this SHOULD NOT be done.  It's horrible.  But it
seems to be what you wanted to do, even if I don't fully understand *why*
you wanted to do it.

[1] https://www.chiark.greenend.org.uk/~sgtatham/aliases.html
    The guy who *discovered* this thing calls it a "truly horrible hack".



reply via email to

[Prev in Thread] Current Thread [Next in Thread]