knqyf263 / pet

Simple command-line snippet manager
MIT License
4.45k stars 224 forks source link

In bash, PREV=$(fc -lrn | head -n 1) doesn't retrieve the previous command #72

Open marcelpaulo opened 6 years ago

marcelpaulo commented 6 years ago

README.md suggests this function to add the last command to pet:

function prev() {
  PREV=$(fc -lrn | head -n 1)
  sh -c "pet new `printf %q "$PREV"`"
}

However, in bash PREV=$(fc -lrn | head -n 1) results in storing PREV=$(fc -lrn | head -n 1) in PREV:

paulo@monk:~$ ls
bash_completion.d  bin  bk  data  default  Desktop  doc  Downloads  foto  gdrive  go  log  Mail  man  PDF  playlist  RCS  src  stage  tmp  watch  xubuntu 17.10.txt
paulo@monk:~$ PREV=$(fc -lrn | head -n 1)
paulo@monk:~$ echo "$PREV"
     PREV=$(fc -lrn | head -n 1)
paulo@monk:~$ 

To work correctly in bash, that line should be:

PREV=$(fc -ln -1)

as you can see now:

paulo@monk:~$ ls
bash_completion.d  bin  bk  data  default  Desktop  doc  Downloads  foto  gdrive  go  log  Mail  man  PDF  playlist  RCS  src  stage  tmp  watch  xubuntu 17.10.txt
paulo@monk:~$ PREV=$(fc -ln -1)
paulo@monk:~$ echo "$PREV"
     ls
paulo@monk:~$ 

I'm struggling to get rid of the leading tab, piping the output to sed results in changing the history. If I find out a neat way of doing it, I will document it here.

marcelpaulo commented 6 years ago

Found out ! If there's a pipeline, bash seems to add the current command to the history BEFORE running fc, so, to retrieve the last command and get rid of the leading tab, this would do it:

PREV=$(fc -ln -2 -2 | sed 's/[ \t]*//')

To show it in action:

paulo@monk:~$ ls
bash_completion.d  bin  bk  data  default  Desktop  doc  Downloads  foto  gdrive  go  log  Mail  man  PDF  playlist  RCS  src  stage  tmp  watch  xubuntu 17.10.txt
paulo@monk:~$ PREV=$(fc -ln -2 -2 | sed 's/[ \t]*//')
paulo@monk:~$ echo $PREV
ls
paulo@monk:~$ 
marcelpaulo commented 6 years ago

As a suggestion, I'd replace function prev() with an alias:

alias prev='cmd=$(fc -ln -2 -2 | sed "s/^[ \\t]*//"); eval pet new $(printf %q "$cmd")'
marcelpaulo commented 6 years ago

This is still not good. When I use tab completion to complete a command, and the add it to pet with prev, with the alias that I suggested, the command comes with a trailing \:

paulo@monk:~$ ls --all 
.                  .bash_functions  .cddb      .face      go                  .inputrc       man              .pki             .sudo_as_admin_successful  watch
..                 .bash_history    .config    foto       .goobook_auth.json  .kde           .mozilla         playlist         .thumbnails                .Xauthority
.aptitude          .bash_logout     data       .gconf     .goobook_cache      .launchpadlib  .mplayer         .profile         tmp                        .XCompose
.audacity-data     .bashrc          default    gdrive     .goobookrc          .lesshst       .msmtprc         RCS              .vim                       .Xdefaults
.bash_aliases      bin              Desktop    .ghc       .gphoto             .local         .notmuch-config  .sane            .viminfo                   .xscreensaver
.bash_aliases.swp  bk               .dmrc      .gimp-2.8  .gtkrc-2.0          log            .pacplrc         src              .viminfo.tmp               .xsession-errors
.bash_completion   .cabal           doc        .gnome     .gvimrc             Mail           .parallel        stage            .vimrc                     .xsession-errors.old
bash_completion.d  .cache           Downloads  .gnupg     .ICEauthority       .mailcap       PDF              .stremio-server  .w3m                       xubuntu 17.10.txt
paulo@monk:~$ prev
Command>  ls --all\
Description>  

I type ls -al, then TAB to autocomplete, typed another l, TAB again to continue autocompleting.

There's still some tweaking to do with this alias ...

marcelpaulo commented 6 years ago

This will solve the problem:


alias prev='cmd=$(fc -ln -2 -2 | sed -E "s/^[ \\t]*//; s/[ ]*\$//"); eval pet new $(printf %q "$cmd")'
marcelpaulo commented 6 years ago

Just discovered now that the problem with the trailing \ has nothing to do with autocompletion: if there are trailing spaces, they'll with be escaped by printf %q, so they have to be removed before, as I did in the last version of the alias.