Vim shell commands (c flag) behaving differently in bash - bash

If I run this in the terminal (Mac OSX):
vim dump.md -c 'vsplit poa.md' -c 'split one.md' -c ':wincmd h'
It works great (creates three windows with those files).
If I have it as part of a bash script, there's a problem. After doing what it's supposed to do, it leaves me with this in the vim status line:
1:dump.md 2:poa.md 3:one.md
1:dump.md 2:poa.md 3:one.md
Press ENTER or type command to continue
I have to press ENTER 6 times to clear the status line and return to editing (each ENTER outputs the above again).
Anyway around this?
Also, not sure if this is significant, but vim is aliased to mvim (MacVim).
EDIT
Here is the bash script, although I don't think it matters I've run the whole script separately before manually putting the vim dump.md... command it, and it works fine.
13
12 #!/bin/bash
11
10 cd ~/Dropbox/Journal/dumps/
9
8 LAST=`ls -tAF | grep '/$' | head -1`
7 NEW=dump-$(date +"%Y-%m-%d_%T")
6
5 mkdir $NEW
4 cd $NEW
3
2 cp ../$LAST/poa.md ./poa.md
1
14 vim dump.md -c 'vsplit poa.md' -c 'split one.md' -c ':wincmd h'
1

At first:
ls -al
And see just your file is existed...If your file is test make sure vim doesn't cread such files:
.test OR test~ OR something like these
Then :
vim file
Do your changes...
After that close it by saying:
:wq!
Then try to open it again...The problem should be solved
UPDATE
try this one:
ls -a | grep swp
if there was a file like : .script.swp or something like these you should remove them...
Your problem would be solved...You should see if your script it existed with .script.... and you should remove it...
If you can't find files start with . try to move all files(scripts) to another directory then work with them..and use :q! to quite and use :wq! to save and quite...

Related

Unable to cat a data file into a script file (bash) on terminal command line [duplicate]

This question already has answers here:
Terminal - command not found
(2 answers)
Closed 3 years ago.
Im new to bash, and I am unable to cat a file and use the pipe command on the terminal using bash.
This is what ive tried on the terminal command line
$ cat data | readlooptest
however i always get this message when i use the pipe |
-bash: readlooptest: command not found
I have a Script named readlooptest, and a data file
script contents of readlooptest
#!/bin/bash
read myLine
sum=0
for i in $myLine
do
sum=`expr $sum + $i`
done
echo "sum is: $sum"
data file contents are
6 4 4 7 7
So once the commands are entered in terminal, the output should be
$ chmod +x readlooptest
$ cat data | readlooptest
sum is : 28
However I get
-bash: readlooptest: command not found
If readlooptest is not installed in one of your $PATH directories, you have to give the path to it to run it.
So for your piped cat, if you are cd in the same directory as readlooptest:
cat data | ./readlooptest
This has nothing to do with piping or cat. The actual problem is that you need to specify where readlooptest is, since it's not in your PATH. If it's in the working directory, simply add ./ to the start:
$ cat data | ./readlooptest

How do I make a command in cygwin to run Sublime Text?

I'm trying to mimic the subl command in iterm for mac computers in cygwin.
Basically, I want to be able to open a current folder from cygwin by typing subl .
I haven't found any good instructions. I know where my .bashrc file is located. I just dont know what to do to create the command subl and make it so that the path following subl opens with Sublime.
Can anyone help?
You'd want to make an alias and source it from bashrc.
Example
Create a file ~/.bash_aliases with:
alias subl='/cygdrive/c/sublime.exe' #make sure this command is correct for you
Now in ~/.bashrc do:
source ~/.bash_aliases
Log out and log back in, and subl . should work
Assuming you want correct behaviour when doing subl ~, a simple alias or adding Sublime Text to your PATH will not work.
You can use the following bash function instead (put it in your .bashrc):
function subl {
cygpath --windows $# | sed 's/\\/\\\\/g' | tr '\n' '\0' | xargs -0 -n1 /cygdrive/c/Program\ Files/Sublime\ Text\ 3/subl.exe
}
Note that when passing paths to xargs you need to 1) escape the backslashes, 2) use the NUL character as argument delimiter for it to work with paths with spaces.
Edit: Use subl.exe rather than sublime_text3.exe so that it would detach itself from the terminal.
To open files and use Git tools (commit, rebase, tag, ... messages):
#!/bin/bash
# To create in [.babun/]cygwin/usr/local/bin/subl with chmod +x
ARGS=""
while test $# -gt 0
do
ARGS="$ARGS ${1#/cygdrive/[a-zA-Z]}"; # Remove /cygdrive and disk letter from the path
shift
done
/cygdrive/c/Program\ Files/Sublime\ Text\ 3/subl.exe $ARGS
https://gist.github.com/cmalard/16c58869319c9a88473ec08cc7989c6b
You can also simply add Sublime to your PATH variable. For cygwin, you can:
Go to your home directory and verify .bash_profile exists with ls -a
Open .bash_profile with something like vim .bash_profile
add the line export PATH=$PATH:"/cygdrive/c/Program Files/Sublime Text 3" (or whatever top level installation folder Sublime is in)
Reopen your terminal and use subl to verify it works

How to run a series of vim commands from command prompt

I have four text files A.txt, B.txt, C.txt and D.txt
I have to perform a series of vim editing in all these files.
Currently how I am doing is open each files and do the same vim commands one by one.
Is it possible to make a script file which I can run from the command prompt, means without open the actual file for vim editing.
for example, if I have to perform the below vim commands after opening the A.txt file in vim editor:
:g/^\s*$/d
:%s/^/[/
:%s/\(.*\)\(\s\+\)\(.*\)/\3\2\1
:%s/$/]/
:%s/design/test/
Is it possible to make a script file and put all these commands including gvim A.txt (first command in the file).
and edit run the script file from command prompt.
If it is possible, please let me know how to do it and how it can be done with single or multiple files at a time?
vim -c <command> Execute <command> after loading the first file
Does what you describe, but you'll have to do it one file at a time.
So, in a windows shell...
for %a in (A,B,C,D) do vim -c ":g/^\s*$/d" -c "<another command>" %a.txt
POSIX shells are similar, but I don't have a machine in front of me at the moment.
I imagine you could load all the files at once and do it, but it would require repeating the commands on the vim command line for each file, similar to
vim -c "<command>" -c "<command>" -c ":n" (repeat the previous -c commands for each file.) <filenames go here>
EDIT: June 08 2014:
Just an FYI, I discovered this a few minutes ago.
vim has the command bufdo to do things to each buffer (file) loaded in the editor. Look at the docs for the bufdo command. In vim, :help bufdo
The amount of -c commands directly passed to Vim on the command-line is limited to 10, and this is not very readable. Alternatively, you can put the commands into a separate script and pass that to Vim. Here's how:
Silent Batch Mode
For very simple text processing (i.e. using Vim like an enhanced 'sed' or 'awk', maybe just benefitting from the enhanced regular expressions in a :substitute command), use Ex-mode.
REM Windows
call vim -N -u NONE -n -es -S "commands.ex" "filespec"
Note: silent batch mode (:help -s-ex) messes up the Windows console, so you may have to do a cls to clean up after the Vim run.
# Unix
vim -T dumb --noplugin -n -es -S "commands.ex" "filespec"
Attention: Vim will hang waiting for input if the "commands.ex" file doesn't exist; better check beforehand for its existence! Alternatively, Vim can read the commands from stdin. You can also fill a new buffer with text read from stdin, and read commands from stderr if you use the - argument.
Full Automation
For more advanced processing involving multiple windows, and real automation of Vim (where you might interact with the user or leave Vim running to let the user take over), use:
vim -N -u NONE -n -c "set nomore" -S "commands.vim" "filespec"
Here's a summary of the used arguments:
-T dumb Avoids errors in case the terminal detection goes wrong.
-N -u NONE Do not load vimrc and plugins, alternatively:
--noplugin Do not load plugins.
-n No swapfile.
-es Ex mode + silent batch mode -s-ex
Attention: Must be given in that order!
-S ... Source script.
-c 'set nomore' Suppress the more-prompt when the screen is filled
with messages or output to avoid blocking.
With all the commands you want to run on each file saved in a script, say "script.vim", you can execute that script on one file like this (as others have mentioned):
vim -c "source script.vim" A.txt
Taking this one step further, you can save your file at the end of the script, either by putting a :w command inside the script itself, or passing it from the command-line:
vim -c "source script.vim | w" A.txt
Now, you can run any command in Vim on multiple files, by using the argdo command. So your command turns into:
vim -c "argdo source script.vim | w" A.txt B.txt C.txt D.txt
Finally, if you want to quit Vim after running your script on every file, just add another command to quit:
vim -c "argdo source script.vim | w" -c "qa" A.txt B.txt C.txt D.txt
Try the following syntax:
ex foo.txt <<-EOF
g/^\s*$/d
%s/^/[/
%s/\(.*\)\(\s\+\)\(.*\)/\3\2\1
%s/$/]/
%s/design/test/
wq " Update changes and quit.
EOF
The ex command is equivalent to vim -E. Add -V1 for verbose output.
Alternative one-liner syntax is for example:
ex +"g/^\s*$/d" +"%s/^/[/" +"%s/design/test/" -cwq foo.txt
To load commands from the file, use -s cmds.vim.
You can also use shebang for Vim to parse the file from the argument.
For more examples, see:
How to edit files non-interactively (e.g. in pipeline)?
BashFAQ/021
JimR and Ingo have provided excellent answers for your use case.
Just to add one more way to do it, however, you could use my vimrunner plugin to script the interaction in ruby: https://github.com/AndrewRadev/vimrunner.
Example:
vim = Vimrunner.start
vim.edit "file.txt"
vim.insert "Hello"
vim.write
vim.kill
This can be useful for more complicated interactions, since you get the full power of a programming language.
Use vim -s ... to script not only colon commands, but also normal-mode commands such as = for formatting:
Create a file with all keystrokes that you want vim to execute.
Run vim -s SCRIPT-FILE FILE-TO-EDIT
For example: Let's say you want to use vim's = command to re-indent all the lines of myfile.html. First, using vim itself, make a file named myscript that has this:
gg=G:wq
(gg moves to the top of the file; =G re-indents from the current location to the end of the file; :wq<Enter> saves the file and exits.)
Then, run this to have vim launch, edit myfile.html, and save it:
vim -s myscript myfile.html

xargs with command that open editor leaves shell in weird state

I tried to make an alias for committing several different git projects. I tried something like
cat projectPaths | \
xargs -I project git --git-dir=project/.git --work-tree=project commit -a
where projectPaths is a file containing the paths to all the projects I want to commit. This seems to work for the most part, firing up vi in sequence for each project so that I can write a commit msg for it. I do, however, get a msg:
"Vim: Warning: Input is not from a terminal"
and afterward my terminal is weird: it doesn't show the text I type and doesn't seem to output any newlines. When I enter "reset" things pretty much back to normal, but clearly I'm doing something wrong.
Is there some way to get the same behavior without messing up my shell?
Thanks!
Using the simpler example of
ls *.h | xargs vim
here are a few ways to fix the problem:
xargs -a <( ls *.h ) vim
or
vim $( ls *.h | xargs )
or
ls *.h | xargs -o vim
The first example uses the xargs -a (--arg-file) flag which tells xargs to take its input from a file rather than standard input. The file we give it in this case is a bash process substitution rather than a regular file.
Process substitution takes the output of the command contained in <( ) places it in a filedescriptor and then substitutes the filedescriptor, in this case the substituted command would be something like xargs -a /dev/fd/63 vim.
The second command uses command substitution, the commands are executed in a subshell, and their stdout data is substituted.
The third command uses the xargs --open-tty (-o) flag, which the man page describes thusly:
Reopen stdin as /dev/tty in the child process before executing the
command. This is useful if you want xargs to run an interactive
application.
If you do use it the old way and want to get your terminal to behave again you can use the reset command.
The problem is that since you're running xargs (and hence git and hence vim) in a pipeline, its stdin is taken from the output of cat projectPaths rather than the terminal; this is confusing vim. Fortunately, the solution is simple: add the -o flag to xargs, and it'll start git (and hence vim) with input from /dev/tty, instead of its own stdin.
The man page for GNU xargs shows a similar command for emacs:
xargs sh -c 'emacs "$#" < /dev/tty' emacs
(in this command, the second "emacs" is the "dummy string" that wisbucky refers to in a comment to this answer)
and says this:
Launches the minimum number of copies of Emacs needed, one after the
other, to edit the files listed on xargs' standard input. This example
achieves the same effect as BSD's -o option, but in a more flexible and
portable way.
Another thing to try is using -a instead of cat:
xargs -a projectPaths -I project git --git-dir=project/.git --work-tree=project commit -a
or some combination of the two.
If you have GNU Parallel http://www.gnu.org/software/parallel/ installed you should be able to do this:
cat projectPaths |
parallel -uj1 git --git-dir={}/.git --work-tree={} commit -a
In general this works too:
cat filelist | parallel -Xuj1 $EDITOR
in case you want to edit more than one file at a time (and you have set $EDITOR to your favorite editor).
-o for xargs (as mentioned elsewhere) only works for some versions of xargs (notably it does not work for GNU xargs).
Watch the intro video to learn more about GNU Parallel http://www.youtube.com/watch?v=OpaiGYxkSuQ
Interesting! I see the exact same behaviour on Mac as well, doing something as simple as:
ls *.h | xargs vim
Apparently, it is a problem with vim:
http://talideon.com/weblog/2007/03/xargs-vim.cfm

Prevent duplicates from being saved in bash history [closed]

Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 10 years ago.
Improve this question
I'm trying to prevent bash from saving duplicate commands to my history. Here's what I've got:
shopt -s histappend
export HISTIGNORE='&:ls:cd ~:cd ..:[bf]g:exit:h:history'
export HISTCONTROL=erasedups
export PROMPT_COMMAND='history -a'
This works fine while I'm logged in and .bash_history is in memory. For example:
$ history
1 vi .bashrc
2 vi .alias
3 cd /cygdrive
4 cd ~jplemme
5 vi .bashrc
6 vi .alias
$ vi .bashrc
$ history
1 vi .alias
2 cd /cygdrive
3 cd ~jplemme
4 vi .alias
5 vi .bashrc
$ vi .alias
$ history
1 cd /cygdrive
2 cd ~jplemme
3 vi .bashrc
4 vi .alias
$ exit
But when I log back in, my history file looks like this:
$ history
1 vi .bashrc
2 vi .alias
3 cd /cygdrive
4 cd ~jplemme
5 vi .bashrc
6 vi .alias
7 vi .bashrc
8 vi .alias
What am I doing wrong?
EDIT: Removing the shopt and PROMPT_COMMAND lines from .bashrc does not fix the problem.
As far as I know, it is not possible to do what you want. I see this as a bug in bash's history processing that could be improved.
export HISTCONTROL=ignoreboth:erasedups # no duplicate entries
shopt -s histappend # append history file
export PROMPT_COMMAND="history -a" # update histfile after every command
This will keep the in memory history unique, but while it does saves history from multiple sessions into the same file, it doesn't keep the history in the file itself unique. history -a will write the new command to the file unless it's the same as the one immediately before it. It will not do a full de-duplication like the erasedups setting does in memory.
To see this silliness in action, start a new terminal session, examine the history, and you'll see repeated entries, say ls. Now run the ls command, and all the duplicated ls will be removed from the history in memory, leaving only the last one. The in memory history becomes shorter as you run commands that are duplicated in the history file, yet the history file itself continues to grow.
I use my own script to clean up the history file on demand.
# remove duplicates while preserving input order
function dedup {
awk '! x[$0]++' $#
}
# removes $HISTIGNORE commands from input
function remove_histignore {
if [ -n "$HISTIGNORE" ]; then
# replace : with |, then * with .*
local IGNORE_PAT=`echo "$HISTIGNORE" | sed s/\:/\|/g | sed s/\*/\.\*/g`
# negated grep removes matches
grep -vx "$IGNORE_PAT" $#
else
cat $#
fi
}
# clean up the history file by remove duplicates and commands matching
# $HISTIGNORE entries
function history_cleanup {
local HISTFILE_SRC=~/.bash_history
local HISTFILE_DST=/tmp/.$USER.bash_history.clean
if [ -f $HISTFILE_SRC ]; then
\cp $HISTFILE_SRC $HISTFILE_SRC.backup
dedup $HISTFILE_SRC | remove_histignore >| $HISTFILE_DST
\mv $HISTFILE_DST $HISTFILE_SRC
chmod go-r $HISTFILE_SRC
history -c
history -r
fi
}
I'd love to hear more elegant ways to do this.
Note: the script won't work if you enable timestamp in history via HISTTIMEFORMAT.
Bash can improve the situation by
fix history -a to only write new data if it does not match any history in memory, not just the last one.
de-deduplicate history when files are read if erasedups setting is set . A simple history -w in a new terminal would then clean up the history file instead of the silly script above.
The problem is definitely the histappend. Tested and confirmed on my system.
My relevant environment is:
$ set | grep HIST
HISTFILE=/Users/hop/.bash_history
HISTFILESIZE=500
HISTIGNORE=' *:&:?:??'
HISTSIZE=500
$ export HISTCONTROL=erasedups
$ shopt | grep hist
cmdhist on
histappend off
histreedit off
histverify off
lithist off
Now that I think about it, the problem is probably with the history -a. history -w should write the current history without any duplicates, so use that if you don't mind the concurrency issues.
export HISTCONTROL=ignoreboth
Here is what I use..
[vanuganti# ~]$ grep HIST .alias*
.alias:HISTCONTROL="erasedups"
.alias:HISTSIZE=20000
.alias:HISTIGNORE=ls:ll:"ls -altr":"ls -alt":la:l:pwd:exit:mc:su:df:clear:ps:h:history:"ls -al"
.alias:export HISTCONTROL HISTSIZE HISTIGNORE
[vanuganti# ~]$
and working
[vanuganti# ~]$ pwd
/Users/XXX
[vanuganti# ~]$ pwd
/Users/XXX
[vanuganti# ~]$ history | grep pwd | wc -l
1
inside your .bash_profile add
alias hist="history -a && hist.py"
then put this on your path as hist.py and make it executable
#!/usr/bin/env python
from __future__ import print_function
import os, sys
home = os.getenv("HOME")
if not home :
sys.exit(1)
lines = open(os.path.join(home, ".bash_history")).readlines()
history = []
for s in lines[:: -1] :
s = s.rstrip()
if s not in history :
history.append(s)
print('\n'.join(history[:: -1]))
now when you want the short list just type hist

Resources