vadimbog
vadimbog

Reputation: 396

getting last executed command from script

I'm trying to get last executed command from command line from a script to be saved for a later reference:

Example:

# echo "Hello World!!!"
> Hello World!!!
# my_script.sh
> echo "Hello World!!!"

and the content of the script would be :

#!/usr/bin/ksh

fc -nl -1 | sed -n 1p

Now as you notices using here ksh and fc is a built in command which if understood correctly should be implemented by any POSIX compatible shells. [I understand that this feature is interactive and that calling same fc again will give different result but this is not the concern do discuss about]

Above works so far so good only if my_script.sh is being called from the shell which is as well ksh, or if calling from bash and changing 1st line of script as #!/bin/bash then it works too and it doesn't if shells are different.

I would like to know if there is any good way to achieve above without being constrained by the shell your script is called from. I understand that fc is a built in command and it works per shell thus most probably my approach is not good at all from what I want to achieve. Any better suggestions?

Upvotes: 2

Views: 3390

Answers (3)

mcoolive
mcoolive

Reputation: 4205

fc is designed to be used interactively. I tested your example on cygwin/bash and the result was different. Even with bash everywhere the fc command didn't work in my case.

I think fc displays the last command of the current shell (here I don't speak about the shell interpretor, but shell as the "process box". So the question is more why it works for you.

I don't think there is a clean way to achieve what you want because (maybe I miss something) you want two different process (bash and your magic command [my_script.sh]) and by default OS ensure isolation between them.

You can rely on what you observe (not portable, depends on the shell interpretor etc.)

You cannot rely on BASH historic because it's in-memory (the file is updated only on exit).

You can use an alias or a function (edited: @Charles Duffy is right). In this case you won't be able to use your "magic command" from another terminal, but for an interactive use it does the job.

Edited: Or you can provide two commands: one to save and another to look for. In this case you manage your own historic but you have to save explicitly each command that is painful...

So I look for a hook. And I found this other thread : https://superuser.com/questions/175799/does-bash-have-a-hook-that-is-run-before-executing-a-command

# At the beginning of the Shell (.bashrc for example) 
save(){ history 1 >>"$HOME"/myHistory ; }
trap 'save' DEBUG
# An example of use
rm -f "$HOME"/myHistory
echo "1 2  3"
cat "$HOME"/myHistory
    14  echo "1 2  3"
    15  cat "$HOME"/myHistory

But I observe it slows down the interpretor...

Upvotes: 1

Henk Langeveld
Henk Langeveld

Reputation: 8456

I actually attempted this, but it cannot be done between different shells consistently.

While fc -l`, is the POSIX standard command for showing $SHELL history, implementation details may be different.

At least bash and ksh93 both will report the last command with

fc -n -l -1 -1

However, POSIX does not guarantee that shell history will be carried over to a new instance of the shell, as this requires the presence of a $HISTFILE. If none is present, the shell may default to $HOME/.sh_history.

However, this history file or Command History List is not portable between different shells.

The POSIX Shell description of the Command History List says:

When the sh utility is being used interactively, it shall maintain a list of commands previously entered from the terminal in the file named by the HISTFILE environment variable. The type, size, and internal format of this file are unspecified.

Emphasis mine

What this means is that for your script needs to know which shell wrote that history.

I tried to use $SHELL -c 'fc -nl -1 -1', but this did not appear to work when $SHELL refers to bash. Calling ksh -c ... from bash actually worked.

The only way I could get this to work is by creating a function.

last_command() { (fc -n -l -1 -1); }

In both ksh and bash, this will give the expected result. Variations of this function can be used to write the result elsewhere. However, it will break whenever it's called from a different process than the current.

The best you can do is to create these functions and source them into your interactive shell.

Upvotes: 4

Ivan X
Ivan X

Reputation: 2195

Little convoluted, but I was able to use this command to get the most recent command in zsh, bash, ksh, and tcsh on Linux:

history | tail -2 | head -1 | sed -r 's/^[ \t]*[0-9]*[ \t]+([^ \t].*$)/\1/'

Caveats: this uses GNU sed, so you'll need to install that if you're using BSD, OS X, etc; and also, tcsh will display the time of the command before the command itself. Regular csh doesn't seem to having a functioning history command when I tried it.

Upvotes: 0

Related Questions