1

I am looking for a way to expand some predefined strings in the terminal when tab (or another key combination) is pressed. This is different from tab completion. My terminal already expands ~ to my home directory (I cannot remember how I figured it out, but I am pretty sure this is just an special case of what I am looking for).

Examples:

Assume you type cdd and press tab, it is expanded to cd /your/home/folder/Downloads/ and the cursor is placed at the end of the line. So, you can type the name of a folder inside the Downloads folder and run the command. A better example would be to expand sshu to ssh |@a-pre-defined-host.foo and place the cursor behind the @ sign to type in the username.

The is similar to code snippet expansion capability in many IDEs. Please don't say I can achieved the same thing through bash functions with parameters written in .bashrc file.

What is the point?

So what's the point of using such a thing compared to a function call? Well, unlike many of you command-liners, I don't have a good memory. Even for the commands that I use frequently, I cannot remember the details such as the parameters. I usually search the web for things like "how to POST a json file using curl?". Well, I can create a function that accepts a file name, a host name, a port name and probably a few other parameters and do the POST. But, remembering the parameters and the order of them is not simpler than remembering the whole command then. With the snippet expansion, I only need to remember the name of the snippet, something like snppost, or maybe I dont even need to remember the name of the snippet. I can type snp and press tab to get a list of snippets, find the one that I am looking for. Press tab again to expand the snippet. And I have the command that I was looking for without searching the web and copy/pasting. I can do whatever customization that I like to the command and run it.

6
  • What is |@ supposed to be? | is a control operator. Wouldn't it make more sense to type e.g. sshh, then type the username and then press tab for adding @a-pre-defined-host.foo? What is the advantage of first printing the host and moving the cursor backwards then? Furthermore the cursor would have to be in front of the @, not behind. Apr 6, 2014 at 13:19
  • 1
    If sshu is a function, you can just type sshu $user, without having to type Tab first, and you get the same result. The completion features in IDEs are designed for an environment where the completion itself needs to be stored in the source file, rather than a function that can be evaluated when the source file is read.
    – chepner
    Apr 6, 2014 at 14:40
  • While this is possible, it's going to clash a lot with the usual working of completion. What's the point anyway? Why would you want to insert this directory name after cdd: if you're using a custom command name, that custom command would presumably have the directory name built in. Same thing with the ssh example, best dealt with through aliases in .ssh/config. You're trying to fit a square peg in a round hole, or as we say here, it's an XY problem. Apr 6, 2014 at 22:49
  • @HaukeLaging: | means the cursor location. Sorry if it was confusing.
    – Helium
    Apr 6, 2014 at 23:27
  • @Gilles I guess the point is that the command does not have only one argument so that just the start directory is hard coded into the function / completion but that it must be possible to select paths below this point. Apr 6, 2014 at 23:37

2 Answers 2

1

This is not going to be as easy as you want. Programmable completion is not capable of replacing commands. And you have to finish (i.e. trailing whitespace) the command word before programmable completion kicks in.

This means you must create an alias or function for cdd which replaces it by cd. That shouldn't be a problem (except for directory names containing newline...). The rest can be done this way:

_cdd () { IFS=$'\n'; if [ 1 -eq "$COMP_CWORD" -a 0 -eq "${#COMP_WORDS[1]}" ]; 
then COMPREPLY=("/your/home/folder/Downloads/"); 
else COMPREPLY=($(compgen -A directory "${COMP_WORDS[1]}"));fi; }

complete -o filenames -F _cdd cdd

putting the cursor inside a word

I don't think this is a good idea. But with this in mind: It is possible to move the cursor backwards but I didn't get that working from within programmable completion:

back_seq=$'\e[D'
output=""; for((i=0;i<5;i++)); do output+="$back_seq"; done
echo -n 123456789"$output"; sleep 5
0

You may find something you can use in the READLINE functions.

See LESS='+/shell-expand-line' man bash for a start.

shell-expand-line may not be the best one for your purposes, but it is set by default (assuming you have the default shell options, i.e. echo $SHELLOPTS contains :emacs:).

Some other possibilities can be seen by running bind -p | grep expand. Any of these "expand" functions will do some form of what you're asking for.

Basically you will set an alias:

alias cdd='cd /your/home/folder/Downloads/'

...in your .bashrc, and when you want to expand it you type cdd<Esc><C-e> and it is expanded. That's using the already-set shell-expand-line as mentioned above—it expands aliases and history expansion, as well as variables. (Try typing l.; echo $SHELLOPTS; !! and pressing <Esc><C-e>.)

You can bind other functions such as simply alias-expand-line which may better serve your purposes.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .