2

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?

  • 3
    Why don't you just write a manual page for yourself, containing just the stuff you've put into your text file? – Sami Laine Mar 11 '14 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. – Faheem Mitha Mar 11 '14 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 '14 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. – mkc Mar 11 '14 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 '14 at 19:30
3

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...

0

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/

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.