Can't run a linkage script through VIM - c++

Hello, I wrote a bash script that compile several cpp's and object files
in g++. My goal is to run the script in vim by :!, but
it doesn't works within vim, only when I'm outside.
In addition I wanted to why using % in a script
doesn't give me the current file, but gives an error
instead.
the script:
#!/bin/bash
# Search for the main module and remove the ext.
delimain=`grep main *.cpp | cut -d. -f1`
# Checks if there are also object files
if [ -f ./*.o ]; then
g++ -g *.cpp *.o -o $delimain.exe
# If There are only cpp file
else
g++ -g *.cpp -o $delimain.exe
fi
Thanks!

RE your comment
I'm using ubuntu 13.10 alias: alias link 'sh ~/bin/lin_script %'
You should not invoke a shell script with an explicit interpreter; the #!/bin/bash first line tells the shell already which interpreter to use. You're obviously a beginner in Bash; try to read some introductions to gain a better understanding.
Aliases won't work in Vim because they are only defined in an interactive shell, but the commands launched from Vim usually are launched in a non-interactive shell (because this is faster and comes with less unnecessary stuff).
The alias is interpreted by the shell, but the % symbol is special to Vim. The two are not the same. See my other answer how to pass a filename to the script.

You're right that % is automatically expanded when supplied to an Ex command inside Vim, but this does not apply to external scripts. What you have to do is pass the current file when invoking the external script, and in there reference the command-line argument:
Inside Vim:
:!linkage.sh %
In your script:
if [ $# -gt 0 ]; then
delimain=$1
else
delimain=`grep ...

Related

Run ninja without rules

I have a build where it would be easiest for now to just have unique build rules for each build target (eg object file and library). Is it possible to specify in the build.ninja-file what to do without first specifying a rule for it.
For example (toy syntax)
build this_file: depends_on_this.o depends_on_that.o - gcc argumentss_to_use files.o etc
As usual I found out a way to do this a minute after asking.
We could just forward the whole command as a variable
rule run
command = $cmd
build log.txt: run
cmd = echo $hello >> log.txt

Interactive find-and-replace in all files including those in sub-directories using Vim

I would like to use Vim to find certain string and replace it with another. For every replacements, it should ask for confirmation similar to what %s/foo/replace/gc does for a single file in Vim.
What have I tried?
sed: It doesn't do interactive replacements.
One of the comments in the following this link suggests vim -esnc '%s/foo/bar/g|:wq' file.txt. I tried vim -esnc '%s/foo/bar/gc|:wq' file.txt (used gc instead of g). Now the terminal gets stuck.
Emacs xah-find-replace package. Unfortunately it didn't do interactive replacements as promised in the link.
Combining :argdo with the substitute command would be the recommended way to do this.
You can populate the args by either opening all the files vim *.txt or manually populate this after opening vim using the command:
:args `find . -type f -name '*.txt'`
Now set hidden using the command:
:set hidden
this is required so that you're not prompted to save the file when switching from one buffer to the other. Refer, :h hidden for more information.
Now use the substitute command like you're used to, prefixing the argdo to perform this for every file in the argslist
:silent argdo %s/pattern/replace/gec
The silent is optional and just mutes the reporting. The e flag is to stop reporting the error no matches found message in some of the buffers
Now after replace, you can write the changes using the following command
:argdo update
This will write buffers that were modified only.
If you are looking for an interactive mode of replacement, it is easier to do it with vim.
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' FILENAME
The stuck terminal in your case is due to piping the save command to the replacement string, as it does not allow the interactive mode to come in to action. And it is not a stuck terminal, if you type "yes" and press enter it should still show you the expected result.
In case multiple files are involved which is spread across multiple subdirectories, using find command with for loop will help as mentioned below:
for FILENAME in `find DIRECTORYPATH -type f -name *.txt`
do
vim -c '%s/PATTERN/REPLACEMENT/gc' -c 'wq' $FILENAME
done
In bash turn on double star to list all files in all subdirectories:
shopt -s globstar
Now start vim once with all files and run the substitute command for all files, then save and exit:
vim -c 'set nomore' -c 'argdo %s/foo/bar/gc' -c xa **/*.txt

Vim [compile and] run shortcut

Basically what I want is a keyboard shortcut in vim that lets me [compile and] run the currently being edited C, C++ or Python program. In psuedocode:
when a shortcut key is pressed:
if current_extension == 'c' then
shell: gcc this_filename.c -o this_filename_without_extension
if retcode == 0 then shell: ./this_filename_without_extension
else if current_extension == 'cpp' then
shell: g++ this_filename.cpp -o this_filename_without_extension
if retcode == 0 then shell: ./this_filename_without_extension
else if current_extension == 'py' then
shell: python this_filename.py
end if
end key
I realise I might be asking a bit much but would love it if this was possible!
Something like this would work. Just create filetype autocmd that map <F4> or whatever you want to save and compile and run the program. It uses exec to build the string and uses shellescape to escape the file name.
autocmd filetype python nnoremap <F4> :w <bar> exec '!python '.shellescape('%')<CR>
autocmd filetype c nnoremap <F4> :w <bar> exec '!gcc '.shellescape('%').' -o '.shellescape('%:r').' && ./'.shellescape('%:r')<CR>
autocmd filetype cpp nnoremap <F4> :w <bar> exec '!g++ '.shellescape('%').' -o '.shellescape('%:r').' && ./'.shellescape('%:r')<CR>
% is the current buffer filename. %:r is the buffer filename without extension
http://singlecompile.topbug.net seems to do more than what you want. For a simpler solution you could also just add the following to your vimrc
au BufEnter *.cpp set makeprg=g++\ -g\ %\ -o\ %<
au BufEnter *.c set makeprg=gcc\ -g\ %\ -o\ %<
au BufEnter *.py set makeprg=python\ %
au BufEnter *.[rR] set makeprg=Rscript\ %
map <F5> :call CompileGcc()<CR>
func! CompileGcc()
exec "w"
silent make
endfunc
HTH
It's 2018 now, vim 8 has released for 2 years and shipped with all the mean stream Linux distributions and Mac OS X. But a lot of vim tutorials are still teaching people something ten years ago.
You can compile your C++/Java programs in vim as convenience as Sublime Text or NotePad++ with some dedicated plugins for Vim 8 or NeoVim.
For example, the AsyncRun plugin will allow you run shell commands in background and read output from quickfix window in realtime.
See the screen capture.
Just like compiling programs in IDEs, the compilation errors will be matched by errorformat and be highlighted and become selectable. You can navigate errors in the quickfix window or continue editing while compiling.
Quick setup
Copy & paste the lines below to your vimrc:
Plug 'skywind3000/asyncrun.vim'
" open quickfix window automatically when AsyncRun is executed
" set the quickfix window 6 lines height.
let g:asyncrun_open = 6
" ring the bell to notify you job finished
let g:asyncrun_bell = 1
" F10 to toggle quickfix window
nnoremap <F10> :call asyncrun#quickfix_toggle(6)<cr>
When you input “:AsyncRun echo hello ” in the command line:
see the capture here
You will see the realtime command output in the open quickfix window.
Compile and run a single file
Compiling a single file with AsyncRun is much simpler than Sublime Text’s build system. We can setup F9 for this:
noremap <silent> <F9> :AsyncRun gcc -Wall -O2 "$(VIM_FILEPATH)" -o "$(VIM_FILEDIR)/$(VIM_FILENOEXT)" <cr>
The macros in $(..) form will be expanded as the real file name or directory, and then we will have F5 to run the executable:
noremap <silent> <F5> :AsyncRun -raw -cwd=$(VIM_FILEDIR) "$(VIM_FILEDIR)/$(VIM_FILENOEXT)" <cr>
The double quotation mark is used to handle path names containing spaces. The option -cwd=$(VIM_FILEDIR) means running the file in the file's directory. The absolute path name $(VIM_FILEDIR)/$(VIM_FILENOEXT) is used because linux needs a ./ prefix to running executables in current directory, but windows doesn't . Using the absolute path name of the binary file can handle this crossing platform issue.
Another option -raw means the output will not be matched by vim's errorformat, and will be displayed in quickfix as what it is. Now you can compile your file with F9, check the compilation errors in quickfix window and press F5 to run the binary.
Build C/C++ Projects
No matter what build tool you are using, make or cmake, project building means acting to a group of files. It requires locating the project root directory. AsyncRun uses a simple method called root markers to identify the project root. The Project Root is identified as the nearest ancestor directory of the current file which contains one of these directories or files:
let g:asyncrun_rootmarks = ['.svn', '.git', '.root', '_darcs']
If none of the parent directories contains these root markers, the directory of the current file is used as the project root. This enables us to use either <root> or $(VIM_ROOT) to represent the project root. and F7 can be setup to build the current project:
noremap <silent> <F7> :AsyncRun -cwd=<root> make <cr>
What if your current project is not in any git or subversion repository ? How to find out where is my project root ? The solution is very simple, just put an empty .root file in your project root, it will be located easily.
Let’s move on, setup F8 to run the current project:
noremap <silent> <F8> :AsyncRun -cwd=<root> -raw make run <cr>
The project will run in its root directory. Of course, you need define the run rule in your own makefile. then remap F6 to test:
noremap <silent> <F6> :AsyncRun -cwd=<root> -raw make test <cr>
If you are using cmake, F4 can be map to update your Makefile:
nnoremap <silent> <F4> :AsyncRun -cwd=<root> cmake . <cr>
Due to the implementation of c runtime, if the process is running is a non-tty environment, all the data in stdout will be buffered until process exits. So, there must be a fflush(stdout) after your printf statement if you want to see the real-time output. or you can close the stdout buffer at the beginning by
setbuf(stdout, NULL);
At the mean time, if you are writing C++ code, a std::endl can be appended to the end of std::cout. It can force flush the stdout buffer. If you are developing on windows, AsyncRun can open a new cmd window for the child process:
nnoremap <silent> <F5> :AsyncRun -cwd=$(VIM_FILEDIR) -mode=4 "$(VIM_FILEDIR)/$(VIM_FILENOEXT)" <cr>
nnoremap <silent> <F8> :AsyncRun -cwd=<root> -mode=4 make run <cr>
Using the option -mode=4 on windows will open a new prompt window to run the command, just like running command line programs in Visual Studio. Finally, we have these key mappings below:
F4: update Makefile with cmake.
F5: run the single file
F6: run project test
F7: build project
F8: run project
F9: compile the single file
F10: toggle quickfix window
It is more like build system in NotePad++ and GEdit. If you are using cmake heavily, you can write a simple shell script located in ~/.vim/script/build.sh to combine F4 and F7 together: it will update Makefile if CMakeList.txt has been changed, then exectute make.
Advanced usage
You can also define shell scripts in your dotfiles repository and execute the script with F3:
nnoremap <F3> :AsyncRun -cwd=<root> sh /path/to/your/dotfiles/script/build_advanced.sh <cr>
The following shell environment variables are defined by AsyncRun:
$VIM_FILEPATH - File name of current buffer with full path
$VIM_FILENAME - File name of current buffer without path
$VIM_FILEDIR - Full path of current buffer without the file name
$VIM_FILEEXT - File extension of current buffer
$VIM_FILENOEXT - File name of current buffer without path and extension
$VIM_CWD - Current directory
$VIM_RELDIR - File path relativize to current directory
$VIM_RELNAME - File name relativize to current directory
$VIM_ROOT - Project root directory
$VIM_CWORD - Current word under cursor
$VIM_CFILE - Current filename under cursor
$VIM_GUI - Is running under gui ?
$VIM_VERSION - Value of v:version
$VIM_COLUMNS - How many columns in vim's screen
$VIM_LINES - How many lines in vim's screen
$VIM_SVRNAME - Value of v:servername for +clientserver usage
All the above environment variables can be used in your build_advanced.sh. Using the external shell script file can do more complex work then a single command.
Grep symbols
Sometimes, If you don't have a well setup environment in you remote linux box, grep is the most cheap way to search symbol definition and references among sources. Now we will have F2 to search keyword under cursor:
if has('win32') || has('win64')
noremap <F2> :AsyncRun! -cwd=<root> grep -n -s -R <C-R><C-W> --include='*.h' --include='*.c*' '<root>' <cr>
else
noremap <F2> :AsyncRun! -cwd=<root> findstr /n /s /C:"<C-R><C-W>" "\%CD\%\*.h" "\%CD\%\*.c*" <cr>
endif
The above script will run grep or findstr in your project root directory, and find symbols in only .c, .cpp and .h files. Now we move around the cursor and press F2, the symbol references in current project will be displayed in the quickfix window immediately.
This simple keymap is enough for most time. And you can improve this script to support more file types or other grep tools in your vimrc .
That’s the practical way to build/run C/C++ projects in Vim 8 or NeoVim. Just like Sublime Text’s build system and NotePad++’s NppExec.
No more outdated vim tutorials again, try something new.
this is my map it really work you can update it to any language:
" <!----------------------------" gcc compile C files----------------------------------------!>
autocmd filetype c nnoremap <Leader>c :w <CR>:!gcc % -o %:r && ./%:r<CR>
" <!----------------------------" java compile files----------------------------------------!>
autocmd filetype java nnoremap <Leader>c :w <CR>:!javac % :r&& java %:r<CR>
" <!----------------------------" php compile files----------------------------------------!>
autocmd filetype php nnoremap <Leader>c :w <CR>:!clear && php %<CR>
now if you are ine one of those file in vim just do in normal mode:
<leader>c
I know I came here 7 years later. I tried the code of other answers and the result didn't satisfied me, so I tried this:
autocmd filetype cpp nnoremap <F9> :w<bar>term ++shell g++ %:p -o %:p:r && %:p:r<CR>
When F9 is pressed, it (like all the answers above) compiles and executes the current file. The output is displayed in a :terminal section splitted screen.
You can change the g++ command to run another language code.
It is saved in a gist, with another mapping to display the output, also in a splitted section, but in a new file, so you can save the output.
Hopefully it could help someone.
I wanted to find a shortcut too. But I didn't want to use autocmd for some reason. I used bash script. I was already using a bash script to compile and run my C/C++ codes. So, I thought, why don't I use a bash script and use it in fltplugin file for C and C++. I made two separate bash scripts. One for C and one for C++. Here is the script for C (For C++, it is also similar just change the compiler to clang++/g++,
std=c2x to std=c++20 and $filename.c to $filename.cpp),
filename=$1
compiler=clang-10
if [ $2 ]; then
if [ $2 == "-v" ]; then
FLAGS="-g -Wall -Wextra -pedantic -std=c2x"
fi
else
FLAGS="-Wall -Wextra -pedantic -std=c2x"
fi
$compiler $FLAGS $filename.c -o $filename -lm
return_value=$?
if [ $return_value -eq 0]; then
if [ $2 ]; then
if [ $2 == "-v" ]; then
valgrind ./$filename
rm $filename
fi
else
./$filename
echo
echo "[process exited $return_value]"
rm $filename
fi
fi
Saved it as run. I made it executable for all users,
$ chmod 777 run
I moved it to my user bin directory.
$ mv run ~/bin
If you dont have a user bin directory, make on. Go to your home directory, and make a directory named bin.
$ cd ~
$ mkdir bin
Then move the run (or whatever name you gave to your script) file to the bin directory.
$ mv run ~/bin
Let's move on.
Then I made a c.vim file in ~/.vim/after/ftplugin directory. And add two new key remaps to the c.vim file.
nnoremap <F9> :vertical bo term run %:r<CR>
# This will run my code with valgrind
nnoremap <F2> :vertical bo term run %:r -v<CR>
Here are some screen shots,
Clicking F9 shows me the output screen in a vim buffer,
Clicking F2 shows me the output using valgrind in a vim buffer
You can do same for C++, just make a cpp.vim file in ~/.vim/after/ftplugin directory. And make another run file (like run_cpp), save it in your user bin(~/bin) file. Make some custom key bindings, and you are good to go.

Copy and Rename Multiple Files with Regular Expressions in bash

I've got a file structure that looks like:
A/
2098765.1ext
2098765.2ext
2098765.3ext
2098765.4ext
12345.1ext
12345.2ext
12345.3ext
12345.4ext
B/
2056789.1ext
2056789.2ext
2056789.3ext
2056789.4ext
54321.1ext
54321.2ext
54321.3ext
54321.4ext
I need to rename all the files that begin with 20 to start with 10; i.e., I need to rename B/2022222.1ext to B/1022222.1ext
I've seen many of the other questions regarding renaming multiple files, but couldn't seem to make it work for my case. Just to see if I can figure out what I'm doing before I actually try to do the copy/renaming I've done:
for file in "*/20?????.*"; do
echo "{$file/20/10}";
done
but all I get is
{*/20?????.*/20/10}
Can someone show me how to do this?
You just have a little bit of incorrect syntax is all:
for file in */20?????.*; do mv $file ${file/20/10}; done
Remove quotes from the argument to in. Otherwise, the filename expansion does not occur.
The $ in the substitution should go before the bracket
Here is a solution which use the find command:
find . -name '20*' | while read oldname; do echo mv "$oldname" "${oldname/20/10}"; done
This command does not actually do your bidding, it only prints out what should be done. Review the output and if you are happy, remove the echo command and run it for real.
Just wanna add to Explosion Pill's answer.
On OS X though, you must say
mv "${file}" "${file_expression}"
Or the mv command does not recognize it.
Brace expansions like :
{*/20?????.*/20/10}
can't be surrounded by quotes.
Instead, try doing (with Perl rename) :
rename 's/^10/^20/' */*.ext
You can do this using the Perl tool rename from the shell prompt. (There are other tools with the same name which may or may not be able to do this, so be careful.)
If you want to do a dry run to make sure you don't clobber any files, add the -n switch to the command.
note
If you run the following command (linux)
$ file $(readlink -f $(type -p rename))
and you have a result like
.../rename: Perl script, ASCII text executable
then this seems to be the right tool =)
This seems to be the default rename command on Ubuntu.
To make it the default on Debian and derivative like Ubuntu :
sudo update-alternatives --set rename /path/to/rename
The glob behavior of * is suppressed in double quotes. Try:
for file in */20?????.*; do
echo "${file/20/10}";
done

Is there a "watch" / "monitor" / "guard" program for Makefile dependencies?

I've recently been spoiled by using nodemon in a terminal window, to run my Node.js program whenever I save a change.
I would like to do something similar with some C++ code I have. My actual project has lots of source files, but if we assume the following example, I would like to run make automatically whenever I save a change to sample.dat, program.c or header.h.
test: program sample.dat
./program < sample.dat
program: program.c header.h
gcc program.c -o program
Is there an existing solution which does this?
(Without firing up an IDE. I know lots of IDEs can do a project rebuild when you change files.)
If you are on a platform that supports inotifywait (to my knowledge, only Linux; but since you asked about Make, it seems there's a good chance you're on Linux; for OS X, see this question), you can do something like this:
inotifywait --exclude '.*\.swp|.*\.o|.*~' --event MODIFY -q -m -r . |
while read
do make
done
Breaking that down:
inotifywait
Listen for file system events.
--exclude '.*\.swp|.*\.o|.*~'
Exclude files that end in .swp, .o or ~ (you'll probably want to add to this list).
--event MODIFY
When you find one print out the filepath of the file for which the event occurred.
-q
Do not print startup messages (so make is not prematurely invoked).
-m
Listen continuously.
-r .
Listen recursively on the current directory. Then it is piped into a simple loop which invokes make for every line read.
Tailor it to your needs. You may find inotifywait --help and the manpage helpful.
Here is a more detailed script. I haven't tested it much, so use with discernment. It is meant to keep the build from happening again and again needlessly, such as when switching branches in Git.
#!/bin/sh
datestampFormat="%Y%m%d%H%M%S"
lastrun=$(date +$datestampFormat)
inotifywait --exclude '.*\.swp|.*\.o|.*~' \
--event MODIFY \
--timefmt $datestampFormat \
--format %T \
-q -m -r . |
while read modified; do
if [ $modified -gt $lastrun ]; then
make
lastrun=$(date +$datestampFormat)
fi
done