3

I often end up in a situation, where I don't use a command for a while, but then suddenly need to use it; and I need to wade through several help pages until constructing the command line I want. Case in point: occasionally I need to use nmcli; and then I need to re-check nmcli nm help and nmcli con help several times, until I remember what sort of a command I need.

What I have done then, is basically document some of this in a text file; the problem is then I usually save it on Desktop; then every once in a while I clean the Desktop files, so I end up thinking "ah, I don't really use nmcli-examples.txt all that much, let me move this one elsewhere too"; then when I need it again, I have to issue a find for it first (provided I can remember the file name to look for).

So I was thinking - making a script that: would have a default location set (e.g. ~/customhelp), where one could keep specifically these kind of reminder text files; and would list the files in that directory upon bash smart completion for the first command argument; and would open such a file name supplied on the command line either in less (or in the default GUI text editor if a command line switch is supplied) shouldn't be too difficult.

But I'd rather not try reinventing the wheel, so I was wondering - if any sort of a command for a scheme of usage as described, exists already in a typical GNU/Linux system?

6
  • 3
    Why don't you just write a manual page for yourself, containing just the stuff you've put into your text file?
    – user48669
    Mar 11, 2014 at 15:46
  • 2
    Setting your bash history to be really, really big would be a start, I think. That's where I go to look for commands I've used before and forgotten about. Mar 11, 2014 at 15:50
  • Thanks @SamiLaine - was thinking about that; I am afraid of conflicting names of my text files vs. actual man pages; and then also, wanted to avoid thinking about which man section should these files belong to. But I guess this is as much of a built-in facility as it goes (also related: Where are man pages stored in Ubuntu? - Super User); cheers!
    – sdaau
    Mar 11, 2014 at 17:24
  • 1
    In addition to having a large history, you can 'tag' commands with key words using '#' at the end of the command. eg. myvery very long command #joeuser. This way, if you want to recall the command you just ctrl-r and look for joeuser and the command will come up.
    – Ketan
    Mar 11, 2014 at 19:15
  • 2
    Not a solution, just a thought. I keep a huge file in my home that I append interesting useful commands to. And I grep it when I need it. Very useful.
    – orion
    Mar 11, 2014 at 19:30

2 Answers 2

4

Expanding on the comment by @SamiLaine for using man - one reason I didn't like it, was because I expected it to be tedious to set up, and I think this post will show that; but, it seems to work. First, some introduction can be found here:

First let's create the directory and add it to MANPATH:

mkdir ~/myreminderhelp
echo 'MANPATH=$MANPATH:'$(echo ~/myreminderhelp) >> ~/.bashrc

Let's test if it is found: close the terminal, open a new terminal; and then:

manpath -d 2>&1 | grep myrem

Unfortunately, this reports nothing for me; even if echo $MANPATH says :~/myreminderhelp.

Trying with global /etc/bash.bashrc (after deleting the line in local ~/.bashrc, and restarting terminal again):

sudo bash -c "echo 'MANPATH=$MANPATH:'$(echo ~/myreminderhelp)" # check home dir printout
sudo bash -c "echo 'MANPATH=$MANPATH:'$(echo ~/myreminderhelp) >> /etc/bash.bashrc"

Restart terminal; still manpath does not report this directory. Let's now try this:

sudo bash -c "echo 'MANDATORY_MANPATH  '$(echo ~/myreminderhelp) >> /etc/manpath.config"

Close and reopen terminal again; finally, we get:

$ manpath 
/usr/local/man:/usr/local/share/man:/usr/share/man:/media/extern/texlive/2011/bin/i386-linux/man:~/myreminderhelp

In fact, after finding this, I erased the line from /etc/bash.bashrc, and manpath still reports the directory. So, I guess editing /etc/manpath.config is all that is needed.

Ok, so let's create an example custom reminder file here:

echo "Just a bit of a reminder...

Use nmcli con --help" >> ~/myreminderhelp/nmcli-reminder.txt

Then use txt2man to get a man formatted file, and gzip it:

cat ~/myreminderhelp/nmcli-reminder.txt | txt2man > ~/myreminderhelp/nmcli-reminder.1
gzip ~/myreminderhelp/nmcli-reminder.1

Restart terminal again, try typing man nm and press TAB - autocompletion shows nmcli-reminder is not found...

So let's try put our files in a man section subfolder; the links above indicate section 7 would be appropriate; so:

mkdir ~/myreminderhelp/man7
mv ~/myreminderhelp/nmcli-reminder.* ~/myreminderhelp/man7/
tree ~/myreminderhelp # to check - ok

Restart terminal again; try typing man nm and press TAB - autocompletion finally works:

$ man nmcli # and here press TAB:
nmcli           nmcli-reminder

... but now we have this problem:

$ man nmcli-reminder 
No manual entry for nmcli-reminder

Damn it. Could be that currently, our file is nmcli-reminder.1.gz indicating section 1 - let's rename it:

mv ~/myreminderhelp/man7/nmcli-reminder.1.gz ~/myreminderhelp/man7/nmcli-reminder.7.gz
man nmcli-reminder

... and finally the man command works!

So for this use case, probably it's best to keep the source .txt files directly in ~/myreminderhelp/, and then generate man pages in the appropriate subfolder - as in:

$ tree ~/myreminderhelp/
~/myreminderhelp/
├── man7
│   └── nmcli-reminder.7.gz
└── nmcli-reminder.txt

... the appropriate generation commands for this being:

cat ~/myreminderhelp/nmcli-reminder.txt | txt2man > ~/myreminderhelp/man7/nmcli-reminder.7
gzip ~/myreminderhelp/man7/nmcli-reminder.7

And here is a ~/myreminderhelp/buildreminders.sh script:

#!/usr/bin/env bash

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

for ix in $DIR/*.txt; do
  bx=$(basename $ix)
  isn=${bx%%.txt}
  echo Processing $isn;
  set -x
  # txt2man -t "$isn" -s 7 $ix > $DIR/man7/${isn}.7
  pandoc -f markdown -t man -s -o $DIR/man7/${isn}.7 $ix
  gzip --force $DIR/man7/${isn}.7
  { set +x; } 2>/dev/null
done

tree -a $DIR

EDIT: Turns out, it is very difficult to get txt2man to leave literal source code unformatted, as it tends to auto-extract information like man (sub)sections etc. I've modified the script above to use pandoc instead (via WritingManPages - HerzbubeWiki) -- at least with pandoc with a Markdown input, you have some control over what is literal preformatted text, and what isn't ...

However, both of these tools will indent contents, as is typical for a man page (since there, section headings are non-indented, rest of the text content as in paragraphs, is). And I'm not sure I end up liking that all too much...

1

Right, here is also a script with autocompletion as described in the OP; you could store it in ~/myreminderhelp/ directory as in the man answer, and then symlink it to /usr/bin. It should read all *.txt files in its directory, and make them available (without the extensions) through autocompletion; spaces in filenames should be handled. So you could call:

$ help-remind #[TAB] here:
nmcli-reminder  test reminder   
$ help-remind test\ reminder # `less` starts here

... or you could use something like this to open the files in a text editor:

scite "$(help-remind --getpath test\ reminder2)"

... which will also open non-existing files, so you can start editing them.

Here is the code for help-remind.sh:

#!/usr/bin/env bash

# sdaau, mar 2014

# symlink this script as:
# sudo ln -s $(readlink -f help-remind.sh) /usr/bin/help-remind

# http://stackoverflow.com/a/246128/277826
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do
  DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
done
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"

# declare here all options:
opts="--dumpopts --getdir --getpath"

function main() {
  [ ! -f /etc/bash_completion.d/help-remind ] &&
    generate_bash_completion ;
  # regex check of arguments - if contain "--getdir":
  [[ "$@" =~ --getdir ]]; REC=$?;
  if [[ $REC == 0 ]] ; then
    getdir
    exit
  fi
  [[ "$@" =~ --dumpopts ]]; REC=$?;
  if [[ $REC == 0 ]] ; then
    echo "$opts"
    exit
  fi
  [[ "$@" =~ --getpath ]]; REC=$?;
  if [[ $REC == 0 ]] ; then
    # expecting name without .txt here as second arg;
    # but handle also if it is given
    tfile="${2%%.txt}.txt"
    trlfile=$(readlink -f "$DIR"/"$tfile") ; #echo ${trlfile}
    # printf quoted too (so spaces are escaped):
    echo $(printf '%q' "$trlfile")
    exit
  fi
  if [[ "$@" == "" ]]; then
    echo "An argument is needed"
    exit
  fi
  # if we got here, we expect the text file name as the first argument
  tfile="${1%%.txt}.txt"
  trlfile=$(readlink -f "$DIR"/"$tfile") ; #echo ${trlfile}
  # open it in `less`
  less "$trlfile"
}

function getdir() {
  echo $DIR
}


function generate_bash_completion() {

# note, this requires terminal restart, each time the file is generated

sudo -s <<-'EOM'
cat > /etc/bash_completion.d/help-remind <<'EOF'

_help-remind()
{
    local cur prev opts
    local hrdir hrfiles hri thrf
    local IFS=$'\n'
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    opts="$(help-remind --dumpopts)"
    # to array:
    IFS=' ' read -a opts <<< "$opts"
    # back to LF separated string:
    opts="$(printf '%s\n' "${opts[@]}")"
    hrdir="$(help-remind --getdir)"
    hrfiles=()
    hri=0
    hrfilestr=""

    # create list of candidate text files; only basenames, no ext
    for file in "${hrdir}"/*.txt; do
      thrf=$(basename "$file")
      thrf="${thrf%%.txt}"
      hrfiles[hri++]="${thrf}"
      hrfilestr=$(printf "%s\n${hrfilestr}" "${thrf}")
    done
    #hrfilestr="$(printf '%s\n' "${hrfiles[@]}")"

    shopt -s extglob

    # just for --getpath, also match candidate text files:
    # compopt -o filenames to get spaces to be escaped once chosen via tab
    case $prev in
      --getpath)
        #_filedir
        compopt -o filenames 2>/dev/null
        COMPREPLY=( $(compgen -W "${hrfilestr}" -- ${cur}) )
        return 0
        ;;
    esac

    # show options, only if $cur starts with a -
    if [[ ${cur} =~ ^- ]] ; then
      COMPREPLY=( $(echo; compgen -W "${opts}" -- ${cur}) )
      return 0
    # else match candidate text files:
    # compopt -o filenames to get spaces to be escaped once chosen via tab
    elif [[ ${cur} == * ]] ; then
      compopt -o filenames 2>/dev/null
      COMPREPLY=( $(compgen -W "${hrfilestr}" -- ${cur}) )
      return 0
    fi
}
complete -F _help-remind help-remind

EOF
EOM

}

# $@ has to be quoted to handle spaces in filenames
main "$@"



# see also:
# http://unix.stackexchange.com/questions/77009/custom-autocomplete-deal-with-spaces-in-filenames
# http://stackoverflow.com/questions/10652492/bash-autocompletion-how-to-pass-this-array-to-compgen-without-significant-whit
# http://www.linuxquestions.org/questions/linux-software-2/escape-string-in-bash-script-so-it-can-be-used-in-command-line-360664/

You must log in to answer this question.

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