man command output full of ^H in vim - macos

this question seems hard to search answers by google.
I executed:
man cp > cp.txt
vim cp.txt
there are many "^H" in the file, it maybe backspace, how to get rid of them?

Just like our grand parents with their typewriters, man uses "backspace" (^H) to to go over the previous character and type it again to obtain "bold" letters.
While you can get rid of those ^H with a simple :%s/<C-v><C-h>//g you will get many doubled characters:
"original
N^HNA^HAM^HME^HE
"result
NNAAMMEE
So you would need to include the character just before or just after the ^H:
:%s/<C-v><C-h>.//g
But why go through all that trouble when you can obtain a clean text file directly with:
$ man cp | col -b > cp.txt

Those special control characters are probably ANSI escape sequences, used to add styling and color to the text. Vim doesn't parse those by default, but the AnsiEsc plugin uses Vim's conceal and syntax highlighting features to draw the text as intended.

Vim ships with a Man page reader. Add the following to your ~/.vimrc:
runtime ftplugin/man.vim
Now you can do :Man cp to open a man page on cp.
See :h :Man for more help.

Related

How do I edit current shell command without executing it?

There seems to be quite a lot of information on how to edit and execute a command using your editor using "edit-and-execute-command (C-x C-e)", but what I would like to achieve is take the current shell command, apply certain filtering (using a script) and then return it to prompt for further approval/manual changes before execution. Is this possible with bash?
Latest update based on my experience
The part 0"+y$dd in the following mapping is really something that you should carefully think about and tailor it to your taste/workflow/experience.
For instance, very frequently I've found myself ending up with multiple lines in the buffer, where I only want to execute the one the cursor is on; in this case I can use 0"+y$dd:%d<CR> instead of 0"+y$dd.
And this is just one of the possible scenarios.
Final answer for those who like vim
Set vim as your EDITOR/VISUAL, so that when editing a command line, you will use vim to edit it.
Put au BufEnter /tmp/bash-fc.* nn <Leader>d 0"+y$dd:wq<CR> in your ~/.vimrc file to map Leaderd (which you will rarely use when editing a command) to the action "delete the current line into the + register without the trailing EOL".
you can use either the + or the * register in the mapping above; the ways to paste into the terminal will likely differ; you need the +clipboard option for these registers to be available.
When finished editing a command in the vim editor, hit EscapeLeaderd.
Paste the clipboard into the terminal (this is terminal-dependent).
Original answer
I often need to do the same, and I do it as follows. (I normally use the set -o vi in bash, so points 1 and 2 in the following are different if you use set -o emacs, the default; based on your question it looks like points 1 and 2 are unified in Ctrl+x followed by Ctrl+e, which is harder to type, imho.)
hit Escape to be in normal mode,
hit v to enter the editor to edit the command,
edit the command as I like,
(This is where you ask the question.)
hit Escape0"+y$dd:wq,
Note: 0"+y$, not simply "+yy, as the latter would copy the newline too, and this would result in executing the command upon pasting it in the command line,
paste the clipboard on the command line
how to do this depends on the terminal you are using, I guess; I hit Ctrl+Alt+v in URxvt.
proceed to approval/manual edit.
Clearly this is just a workaround, consisting in copying the edited command into the clipboard before deleting the whole command, so that nothing gets executed upon exiting the editor; however it's the best I can get for myself.
Update
As my EDITOR (and VISUAL) is equal to vim, when I edit the command, I edit it in vim.
In this respect, I have noticed that the buffer is named /tmp/bash-fc.random, where random is a 6-characters alphanumeric random string.
This gives space to a lot of possiblities, if you use vim as your editor, as you can define some mapping in your .vimrc to execute the whole sequence Escape0"+y$dd:wq. For instance, one command that you'd rarely use when editing a command line is Leaderd; therefore you can put the following mapping in your .vimrc file
au BufEnter /tmp/bash-fc.* nn <Leader>d 0"+y$dd:wq<CR>
so that step 4 in the above recipe becomes
hit EscapeLeaderd
It's not possible to do that in Bash/readline but it's possible in zsh
using edit-command-line command:
darkstar% autoload edit-command-line; zle -N edit-command-line
darkstar% bindkey "^X^E" edit-command-line
Now press Control-x Control-e to open your editor, edit line, leave the editor - you will see the updated command line but it will not be executed automatically.
Now that I think about it, maybe a variation of what #kenorb suggested in a comment is the best workaround (as it seems no solution exists), if we want to stick to bash.
What you can do is prepend a # (the comment character in bash) to the command, rather than echo. Then when you exit the editor, the command will be ineffective, and you will only have to press arrow up (or k, if you use set -o vi), remove the # and confirming.
Note that this strategy adds just a few keystrokes, so it can be fairly efficient, depending on your typing level.
These pieces might get you closer:
a) replace the the normal binding for newline newline (ctrl-M)
bind -x '"\C-M":date"
b) grab the current line from the history using !#
replace date with whatever script you want.
c) edit manually
d) if necessary, somehow tie in !!:p which prints the new command to the command line but does not execute it, thus letting you manually edit it.
e) using ctrl-J submit edited command rather than a newline
or they might not ....
There is an option in bash to modify command from history without executing it. I'm not sure it it's possible to use script for this, doesn't seem to be likely. Although, you can make modifications using history modifiers.
Enable option histverify to prevent execution of modified command
Use chain of modifiers to change last command
Use "!!" to put your result to command line for final edit
Here is how it looks:
$ shopt -s histverify
$ ls *.sh
script1.sh script2.sh script3.sh script-return.sh
$ !!:s/*/script1/:p
ls script1.sh
$ !!:s/1/2/:p
ls script2.sh
$ !!
$ ls script2.sh
script2.sh
I'd like to point you to the Composure framework for Bash (I'm not affiliated with it): https://github.com/erichs/composure
It provides draft and revise functions that sound like they could help with what you're trying to do. Here's a (long) quote from the project's readme file:
Composure helps by letting you quickly draft simple shell functions,
breaking down your long pipe filters and complex commands into
readable and reusable chunks.
Draft first, ask questions later
Once you've crafted your gem of a command, don't throw it away! Use
draft () and give it a good name. This stores your last command as a
function you can reuse later. Think of it like a rough draft.
$ cat servers.txt
bashful: up
doc: down
up-arrow
$ cat servers.txt | grep down
doc: down
$ draft finddown
$ finddown | mail -s "down server(s)" admin#here.com
Revise, revise, revise!
Now that you've got a minimal shell function, you may want to make it
better through refactoring and revision. Use the revise () command
to revise your shell function in your favorite editor.
generalize functions with input parameters
add or remove functionality
add supporting metadata for documentation
$ revise finddown
finddown ()
{
about finds servers marked 'down' in text file
group admin
cat $1 | grep down
}
$ finddown servers.txt
doc: down
It does not seem possible with a keyboard shortcut, at least:
$ bind -P | grep -e command -e edit
complete-command can be found on "\e!".
edit-and-execute-command can be found on "\C-x\C-e".
emacs-editing-mode is not bound to any keys
possible-command-completions can be found on "\C-x!".
vi-editing-mode is not bound to any keys
This can be done in native bash using readline specifically READLINE_LINE and READLINE_POINT variables. I use this functionality all the time though not through vim, you would need to get the value of $selected from your vim command and if not empty it takes your original line + your input and replaces your original line with the combination without executing. output as a variable
_main() {
selected="$(__coms_select__ "$#")"
origonal_text=$READLINE_LINE READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}$selected${READLINE_LINE:$READLINE_POINT}"
READLINE_POINT=$(( READLINE_POINT + ${#selected} ))
}
bind -m emacs-standard -x '"\C-e": _main '
bind -m vi-command -x '"\C-e": _main '
bind -m vi-insert -x '"\C-e": _main '
Edit
Just remembered these two utilities that will let you do this as well.
Vipe allows you to run your editor in the middle of a unix pipeline and edit the data that is being piped between programs.
vp, up, vipe, Neomux (upgrade of nvim terminal) you can do some pretty neat throwing buffers between the terminal and split window.
and Athame (full vim on the command line)
https://github.com/ardagnir/athame
careful with that one though plugins work on the cli and it can get funky if you got tons of plugins

How to find out the default editor for man pages on osx?

I want to make the default editor for man pages to be vi. Right now it is something else.
I know it is something else, because when I try to do a case insensitive search, it doesn't work the way it does in vim:
/someWord\c
the above does not work in man pages, but it does work in vim.
How do I find out the editor for man pages? thank
The default pager for man is less. When -i is passed to less searches with no capital letters are case-insensitive.
The default pager for man is less. To use vim as the pager, just place the following code in your ~/.bash_profile or ~/.zshrc.
export MANPAGER="/bin/sh -c \"col -b | vim -c 'set ft=man ts=8 nomod nolist nonu noma' -\""
The above command uses the col utility to remove extra ^H (backspace) characters. Next it pipe's the output to vim with the options below.
ft=man enables the coloring of the man page.
ts=8 ensures the width of tab characters matches less.
nomod removes the modification warning when trying to quit.
nonu removes line numbers.
nolist disables listchars so trailing whitespace and extra tabs are
not highlighted.
noma prevents the buffer contents from being changed.

Bash auto-completion highlighting

I'd like to know if it is possible to make bash auto-completion highlight the matched part of the auto-complete suggestion.
For example,
I am have directories with files such as these:
LSFJOB_647169535/ LSFJOB_647158534/
In this case, if I type LSF and hit Tab, then I get:
LSFJOB_6471
But then I have to focus hard to get which character should I type next.
I'd like bash to suggest me something like:
LSFJOB_647169535/ LSFJOB_647158534/
or
LSFJOB_647169535/ LSFJOB_647158534/
Do you know a way of doing it?
That is a good question!
Dennis Williamson already answered it there (SuperUser).
So it turns out that there is a "ReadLine Variable" that does exactly that: colored-completion-prefix.
Sadly it's only available in Bash v4.4 :c Link to the diff
You can check its value with bind -v|grep color
I tried to play with compgen but it appears that it strips colors away /:
Instead of colored-completion-prefix, which requires Bash 4.4, you could add the older (Bash 4.0)
set completion-prefix-display-length 2
to your ~/.inputrc (see manual). This replaces any common prefix longer than 2 characters with an ellipsis when showing the completions:
$ ls
LSFJOB_647158534 LSFJOB_647169535
$ cd LSFJOB_6471<tab>
...58534/ ...69535/

Bold text and change color of text for MOTD in nano on OSX?

I'm modifying my MOTD in Terminal via "$ sudo nano /etc/motd", and I'm wondering if there's a way to bold text and/or change its colour. I've checked the help documentation within nano, which would usually lead me to believe such formatting to be impossible – but I've found evidence of Linux users being able to change colors in their MOTD's via nano.
Any help is appreciated.
Create a temporary shell script
touch /tmp/mtod.sh && chmod +x /tmp/mtod.sh
Edit the shell script with the message you want, for instance
echo "Welcome to \e[38;5;196mXXX\e[0m"
Output the message to the real motd file, then delete the temorary script
sudo sh -c '/tmp/mtod.sh > /etc/motd' && rm /tmp/mtod.sh
The /etc/motd file is simply cat'd to the terminal, and more than likely the terminal supports ANSI color escape sequences. You can embed that using (not quite any) many text editors. However the essential part is inserting (and keeping track) of the escape character (usually entered as a control[ on the keyboard). According to nano's documentation,
All keys, with the exception of Control and Meta key sequences, will enter text into the file being edited.
That exception seems to apply to the escape character. Reading further, it says that any character can be entered by typing escape twice, then entering its decimal value (27 for escape). But that does not work for the machine where I am typing. Perhaps it works for you, perhaps not.
You could work around that by reserving some relatively useless character (such as #) to act as the escape and then using tr to translate to/from, e.g.,
tr '\033' '#' /etc/motd >/tmp/motd
nano /tmp/motd
tr '#' '\033' /tmp/motd >/etc/motd
Doing that, bold text would be something like
#[1mBOLD#[0m
(\033 is the escape character, of course - some people prefer \x1b).
You can do this straight from the command line, by appending to the file.
First su to root...
Mac:~ user$ su root
Then use the following commands, substituting your ANSI color codes and text...
Mac:~ root# echo "\033[1;34m" >> /etc/motd
Mac:~ root# echo "Hello." >> /etc/motd
Mac:~ root# echo "\033[0m" >> /etc/motd
I realize this doesn't use nano as the question asks, but I thought some might find it helpful.

How to get rid of bash control characters by evaluating them?

I have an output file (namely a log from screen) containing several control characters. Inside the screen, I have programs running that use control characters to refresh certain lines (examples would be top or anything printing progress bars).
I would like to output a tail of this file using PHP. If I simply read in that file and echo its contents (either using PHP functions or through calling tail, the output is messy and much more than these last lines as it also includes things that have been overwritten. If I instead run tail in the command line, it returns just what I want because the terminal evaluates the control characters.
So my question is: Is there a way to evaluate the control characters, getting the output that a terminal would show me, in a way that I could then use elsewhere (e.g., write to a file)?
#5gon12eder's answer got rid of some control characters (thanks for that!) but it did not handle the carriage return part that was even more important to me.
I figured out that I could just delete anything from the beginning of a line to the last carriage return inside that line and simply keep everything after that, so here is my sed command accomplishing that:
sed 's/^.*\r\([^\r]\+\)\r\?$/\1\r/g'
The output can then be further cleaned using #5gon12eder's answer:
cat screenlog.0 | sed 's/^.*\r\([^\r]\+\)\r\?$/\1\r/g' | sed 's,\x1B\[[0-9?;]*[a-zA-Z],,g'
Combined, this looks exactly like I wanted.
I'm not sure what you mean by “evaluating” the control characters but you could remove them easily.
Here is an example using sed but if you are already using PHP, its internal regex processing functionality seems more appropriate. The command
$ sed 's,\x1B\[[0-9?;]*[a-zA-Z],,g' file.dat
will dump the contents of file.dat to standard output with all ANSI escape sequences removed. (And I'm pretty sure that nothing else is removed except if your file contains invalid escape sequences in which case the operation is ill-defined anyway.)
Here is a little demo:
$ echo -e "This is\033[31m a \033[umessy \033[46mstring.\033[0m" > file.dat
$ cat file.dat
# The output of the above command is not shown to protect small children
# that might be browsing this site.
$ reset # your terminal
$ sed 's,\x1B\[[0-9?;]*[a-zA-Z],,g' file.dat
This is a messy string.
The less program has some more advanced logic built in to selectively replace some escape sequences. Read the man page for the relevant options.

Resources