3

I often find myself running a series of commands on a single set of arguments.

For example:

$ nmap -Pn -sS 192.168.1.5
$ ssh 192.168.1.5
$ curl 192.168.1.5
$ wget -r 192.168.1.5

This can be sped up by putting the argument(s) in a variable,

$ a=192.168.1.5
$ nmap -Pn -sS $a
$ ssh $a
$ curl $a
$ wget -r $a

and sped up even more by using a couple of functions such as the following.

$ more .zshrc
...
paa() { PERSIST_ARGS+=("$@"); }
pa() { eval "$@" "${PERSIST_ARGS[@]}"; }
...
$ paa 192.168.1.5
$ pa nmap -Pn -sS
$ pa ssh
$ pa curl
$ pa wget -r

Is there a way to speed this up even more? For example,

$ <start special mode> 192.168.1.5
$ nmap -Pn -sS
$ ssh
$ curl
$ wget -r
$ <exit special mode>

I am looking for bash or zsh solutions. The solution has to work with an arbitrary set of commands.

  • For saving the command history inside shell scripts, you may want to look at the solution given here: http://unix.stackexchange.com/questions/5684/history-command-inside-bash-script – JustinC May 26 '13 at 19:27

3 Answers3

4

I feel bad answering my own question like this, but I have a solution:

pst() {
    [ $# = 0 ] && return 1

    if [ -n $ZSH_VERSION ]; then
        local read=vared
    else
        local read=read
    fi

    local cmd
    local -a args
    args=("$@")
    while $read -p '> ' cmd ; do
        test -z "$cmd" && continue
        $cmd "${args[@]}"
    done
}

This works as such:

$ pst 192.168.1.5
> nmap -Pn -sS
> ssh
> curl
> wget -r

I'm going to leave the question open for a little while to see if any superior solutions show up. One major downside of this solution is that the commands it runs are not logged.

4

Alt-_ already inserts the last word from the last command. For me, that's good enough, but you could add another key binding that inserts a space, that last word and accept the line (Enter).

With tcsh or zsh (for Alt-S, S for same):

bindkey -s '\es' ' \e_\r'

With bash:

bind '"\es": " \e_\r"'

Then type:

$ nmap -Pn -sS 192.168.1.5
$ ssh<Alt-S>
$ curl<Alt-S>
...

In zsh and tcsh at least, the numerical argument (that is when you prefix it with Alt-<number>) will be applied to the space character which is not very useful (Alt-3Alt-S would insert 3 spaces, the last word once and accept the line). It would be more useful to have it insert either the nth last word from the last command, or the last word from the nth last command, or the last n words from the last command.

In zsh at least, instead of using a macro, you could define a new widget. For instance with:

use-last-word() {
  LBUFFER+=" "
  zle insert-last-word
  zle accept-line
}

zle -N use-last-word
bindkey '\es' use-last-word

Alt-3Alt-S would insert the 3rd last word from the last command.

To insert the last word from the 3rd last command:

use-last-word() { 
  LBUFFER+=" "
  local n=$NUMERIC
  unset NUMERIC
  repeat ${n:-1} zle insert-last-word
  zle accept-line
}

To insert the last 3 words from the last command:

use-last-word() { 
  for ((NUMERIC=${NUMERIC:-1}; NUMERIC; NUMERIC--)) {
    LBUFFER+=" "; zle insert-last-word
  }
  zle accept-line
}
1

I've not implemented anything like this but I wonder if you could tap into the 2 facilities offered by the shell. The $PATH and the command complete.

When you're in your "special mode" you could provide something like the tab complete but instead of having to repeatedly hit it, you type a few letters of the actual command you want to run nm (for nmap) and hit a key combo similar to tab which would then interrogate the $PATH looking for executables that match what you've typed so far and then return a list to you like so:

I type:

$ pst 192.168.1.5
> nm <press SHIFT-TAB>

I'm offered the following list back:

1* - nmap 192.168.1.5    2 - nmap2 192.168.1.5    3 - nmap3 192.168.1.5

NOTE: The above list would be what matched what I typed from interrogating the $PATH.

From the list above I could either hit enter to accept the first response (1*), or type the number of the match that I want to use:

>> 2 <press ENTER>

You could also integrate the complete facility in bash and other shells to narrow down the matches so that only matches that can accept the types of arguments provided to pst <arg> would be considered in the match.

slm
  • 369,824