How to exclude multiple directories with Exuberant ctags? - vim

I have looked and tried to use exuberant ctags with no luck with what I want to do. I am on a Mac trying to work in a project where I want to exclude such directories as .git, node_modules, test, etc. When I try something like ctags -R --exclude=[.git, node_modules, test] I get nothing in return. I really only need to have it run in my core directory. Any ideas on how to accomplish this?

The --exclude option does not expect a list of files. According to ctags's man page, "This option may be specified as many times as desired." So, it's like this:
ctags -R --exclude=.git --exclude=node_modules --exclude=test

Read The Fantastic Manual should always be the first step of any attempt to solve a problem.
From $ man ctags:
--exclude=[pattern]
Add pattern to a list of excluded files and directories. This option may
be specified as many times as desired. For each file name considered by
both the complete path (e.g. some/path/base.ext) and the base name (e.g.
base.ext) of the file, thus allowing patterns which match a given file
name irrespective of its path, or match only a specific path. If appro-
priate support is available from the runtime library of your C compiler,
then pattern may contain the usual shell wildcards (not regular expres-
sions) common on Unix (be sure to quote the option parameter to protect
the wildcards from being expanded by the shell before being passed to
ctags; also be aware that wildcards can match the slash character, '/').
You can determine if shell wildcards are available on your platform by
examining the output of the --version option, which will include "+wild-
cards" in the compiled feature list; otherwise, pattern is matched
against file names using a simple textual comparison.
If pattern begins with the character '#', then the rest of the string is
interpreted as a file name from which to read exclusion patterns, one per
line. If pattern is empty, the list of excluded patterns is cleared.
Note that at program startup, the default exclude list contains "EIFGEN",
"SCCS", "RCS", and "CVS", which are names of directories for which it is
generally not desirable to descend while processing the --recurse option.
From the two first sentences you get:
$ ctags -R --exclude=dir1 --exclude=dir2 --exclude=dir3 .
which may be a bit verbose but that's what aliases and mappings and so on are for. As an alternative, you get this from the second paragraph:
$ ctags -R --exclude=#.ctagsignore .
with the following in .ctagsignore:
dir1
dir2
dir3
which works out to excluding those 3 directories without as much typing.

You can encapsulate a comma separated list with curly braces to handle multiples with one --exclude option:
ctags -R --exclude={folder1,folder2,folder3}
This appears to only work for folders in the root of where you're issuing the command. Excluding nested folders requires a separate --exclude option.

The other answers were straight to the point, and I thought a little example may help:
You should add an asterisk unix-like style to exclude the whole directory.
ctags -R --exclude={.git/*,.env/*,.idea/*} ./

A bit late but following on romainl response, you could use your .gitignore file as a basis, you only need to remove any leading slashes from the file, like so:
sed "s/\///" .gitignore > .ctagsignore
ctags -R --exclude=#.ctagsignore

I really only need to have it run in my core directory.
Simply remove the -R (recursion) flag!!!

Related

Should PATH contain directories or full paths to binaries?

I am trying to set up a correct PATH, but I'm wondering what it should contain. If I have
/usr/bin/ls
/usr/local/bin/ls
and I want to prefer the one in /usr/local/bin, which of the following should I use?
PATH=/usr/local/bin/ls:/usr/bin/ls
PATH=/usr/local/bin:/usr/bin
or something else entirely?
This is not per se a suitable question for Stack Overflow.
I expect this to be closed as General Computing or Too Broad;
but the answer is frequently needed by beginners, so I hope this won't be deleted.
PATH works only with directories, not with single files
From the POSIX standard (emphasis mine)
PATH
This variable shall represent the sequence of path prefixes that certain functions and utilities apply in searching for an executable file known only by a filename. The prefixes shall be separated by a colon ( ':' ). [...] The list shall be searched from beginning to end, applying the filename to each prefix, until an executable file with the specified name and appropriate execution permissions is found.
When you type in ls into your shell and your PATH is set to /usr/local/bin/ls:/usr/bin/ls then your shell will …
… look for an executable with the path /usr/local/bin/ls/ls (note the double ls at the end).
As that path does not exist on your system your shell will proceed to look for an executable with the path /usr/bin/ls/ls (double ls again). That path also doesn't exist.
The shell couldn't find an executable using all paths in PATH so your shell will print something like bash: ls: command not found.
So, what did we learn? Paths listed by PATH have to be directories. You cannot list single files. Therefore the correct answer in your case is PATH=/usr/local/bin:/usr/bin.
Where things get interesting
Imagine the following situation. You have two versions of program c1 and two versions of program c2. The different versions are stored in the directories /a/ and /b/.
/a/c1
/a/c2
/b/c1
/b/c2
How can we set PATH to prefer /a/c1 over /b/c1/ but at the same time /b/c2 over /a/c2?
Sadly, there is no way to achieve this directly as we can only specify directories in PATH. We have to move/rename some files or create symlinks and use the symlinks inside the paths. One possible solution:
mkdir /c
ln -s /a/c1 /c/c1
ln -s /b/c2 /c/c2
export PATH=/c:/a:/b
The trailing :/a:/b is not really necessary here. I included them under the assumption that /a and /b contain more executables than just c1 and c2.
Indeed, as you can easily find out through experimentation, the variable PATH should already contain a list of directories which are consulted in that order. In fact, you should already find that you have /usr/local/bin and /usr/bin in the default PATH, usually in this order (though perhaps with other directories between them, and certainly with more directories around them).
To inspect your current PATH value, try
echo "$PATH"
or for a slightly more human-readable rendering
echo "${PATH//:/$'\n'}" # bash only
or
echo "$PATH" | tr ':' '\012' # any POSIX system
If you managed to set your PATH to an invalid value (which would cause simple commands like ls and cat to no longer be found, and produce command not found errors) you can try
PATH=/usr/local/bin:/usr/bin:/usr
to hopefully at least restore the essential functionality so that you can use cp or a simple system editor to get back to the original, safe, system default PATH.

Copy a range of folders in command line

I found this link with an example of how I can actually copy range of files https://serverfault.com/questions/370403/copy-a-range-of-files-in-command-line-zsh-bash, using this
$cp P10802[75-83].JPG ~/Images/.
Is there any way I can also copy range of folders or directory?
Maybe something like this $cp -r folder[001-999] ~/images./
Use the -R flag to recursive copy the directories. According to Can I use shell wildcards to select filenames ranging across double-digit numbers, you can use the syntax {start..end} to match a number range. Putting that together would give you:
cp -R folder{001..999} ~/images./
Yes, using the same logic. Globbing and expansion (which is what bash uses to generate the individual names out of these patterns) work on files as well as on directory names.

ctags, generate tags using multiple paths?

When I build/update my tags file, ctags starts at the current directory and works its way down recursively.
I would like for it to also include a completely different search path, a mapped network drive, and add those results to my tags file as well.
Is there any way to do that?
When the files in the other directory are related and often change together with the current directory hierarchy, I'd write a custom :Ctags command that supplies the other path to the :!ctags call.
If the other files are unrelated, and rarely update (as based on your comments seems to be the case), I'd run ctags there separately and include them via
:set tags+=/path/to/other/dir/tags
NOTE: Add the tag filename at the end, else there will be "tag not found" error. By default the name is tags but it could be renamed with -f option as below.
ctags -f my_tags -R
:set tags+=/path/to/other/dir/my_tags

How to do partial search in Linux with locate?

I prefer to seach with locate command but I don't know how to perform a partial search with it.
Suppose I want to search file containing the word libevent. How can I do that?
Locate searches for file names. Not file contents.
The ugly way is to use grep It'll start searching from / directory.
grep -irn 'libevent' /
The better way is to narrow down the suspected directories where this files could exists. Suppose those directories' full paths are /path/to/dir1, /path/to/dir2 etc. Then invoke the following command.
for dir in /path/to/dir1 /path/to/dir2
do
grep -irn 'libevent' $dir
done
The locate command is not searching inside the content of files like grep (and other commands) do. It is simply searching inside file paths.
locate work by using a cache index of file paths, and this index is often updated by the updatedb utility.
addenda
A useful way to search some pattern inside (the content of) some files is to use the ability of zsh or some recent versions of bash to expand the ** file pattern, like e.g.
grep foo ~/gee/**/*.[ch]
with zsh this search inside all files named *.c or *.h under $HOME/gee/ containing foo. I find this feature tremendously useful, and justifying alone the adoption of zsh as my interactive shell. With other shells you might type the much longer
find $HOME/gee -name '*.ch' | xargs grep foo

How to directly overwrite with 'unexpand' (spaces-to-tabs conversion)?

I'm trying to use something along the lines of
unexpand -t 4 *.php
but am unsure how to write this command to do what I want.
Weirdly,
unexpand -t 4 file.php > file.php
gives me an empty file. (i.e. overwriting file.php with nothing)
I can specify multiple files okay, but don't know how to then overwrite each file.
I could use my IDE, but there are ~67000 instances of to be replaced over 200 files, and this will take a while.
I expect that the answers to my question(s) will be standard unix fare, but I'm still learning...
You can very seldom use output redirection to replace the input. Replacing works with commands that support it internally (since they then do the basic steps themselves). From the shell level, it's far better to work in two steps, like so:
Do the operation on foo, creating foo.tmp
Move (rename) foo.tmp to foo, overwriting the original
This will be fast. It will require a bit more disk space, but if you do both steps before continuing to the next file, you will only need as much extra space as the largest single file, this should not be a problem.
Sketch script:
for a in *.php
do
unexpand -t 4 $a >$a-notab
mv $a-notab $a
done
You could do better (error-checking, and so on), but that is the basic outline.
Here's the command I used:
for p in $(find . -iname "*.js")
do
unexpand -t 4 $(dirname $p)/"$(basename $p)" > $(dirname $p)/"$(basename $p)-tab"
mv $(dirname $p)/"$(basename $p)-tab" $(dirname $p)/"$(basename $p)"
done
This version changes all files within the directory hierarchy rooted at the current working directory.
In my case, I only wanted to make this change to .js files; you can omit the iname clause from find if you wish, or use different args to cast your net differently.
My version wraps filenames in quotes, but it doesn't use quotes around 'interesting' directory names that appear in the paths of matching files.
To get it all on one line, add a semi after lines 1, 3, & 4.
This is potentially dangerous, so make a backup or use git before running the command. If you're using git, you can verify that only whitespace was changed with git diff -w.

Resources