How do I subtract paths in elisp? - elisp

I've got two paths, one a subset of the other /foo/bar/ and /foo/bar/baz/gig. The path I'd like to extract is baz/gig, the relative path of the file from the dir.

Use file-relative-name:
(file-relative-name "/foo/bar/baz/gig" "/foo/bar/") ; => "baz/gig"
Documented in the Elisp manual under "Relative File Names".

You can also use f third-party file manipulation library. f-relative is what you need.
(f-relative "/a/b/c/d/e/file.txt" "/a/b/") ; "c/d/e/file.txt"
(f-relative "/a/b/c/d/e/file.txt" "/a/b") ; "c/d/e/file.txt"

Related

Bash/shell/OS interpretation of . and .. — can I define ...?

How do . and .., as paths (vs. ranges, e.g., {1..10}, which I'm not concerned with), really work? I know what they do, and use them all the time, but don't fully grasp how/where they're interpreted. Does the shell handle them? The interpreting process? The OS?
The reason why I'm asking is that I'd like to be able to use ... to refer to ../.., .... to refer to ../../.., etc. (up to some small finite number; I don't need bash to process an arbitrarily large number of dots). I.e., if my current directory is /tmp/let/me/out, and I call cd ..., my resulting current directory should be /tmp/let. I don't particularly care if ... etc. show up in ls -a output like . and .. do, but I would like to be able to call cat /tmp/let/me/out/..../phew.txt to print the contents of /tmp/phew.txt.
Pointers to relevant documentation appreciated as well as direct answers. This kind of syntax question is very hard to Google.
I'm using bash 4.3.42, by the way, with the autocd and globstar shell options.
. and .. are genuine directory names. They are not "sort-cuts", aliases, or anything fake.
They happen to point to the same inode as the other name you use. A file or directory can have several names pointing to the same inode, these are usually known as hard links, to distinguish them from symbolic (or soft) links.
If you are on Linux or OS X you can use stat to look at most of the inode metadata - it is what ls looks at. You will see there is an inode number. If you stat . and stat current-directory-name you will see that number is the same.
The one thing that is not held in the inode is the filename - that is held in the directory.
So . and .. reside in the directory on the file system, they are not a figment of the shell's imagination. So, for example, I can use . and .. quite happily from C.
I doubt you can change them - personally I have never tried and I never will. You would have to change what these filenames linked to by editing the directory. If you managed it you would probably do irreparable damage to your file system.
I write this to clarify what has already been written before.
In many file systems a DIRECTORY is a file; a special type of file that the file system identifies as being distinctly a directly.
A directory file contains a list of names that map to files on the disk
A file, including a directly does not have an intrinsic name associated with it (not true in all file systems). The name of a file exists only in a directory.
The same file can have an entry in multiple directories (hard link). The same file can then have multiple names and multiple paths.
The file system maintains in every directory entries for "." and ".."
In such file systems there are always directory ENTRIES for the NAMES "." and "..". These entries are maintained by the file system.
The name "." links to its own directory.
The name ".." links to the parent directory EXCEPT for the top level directory where it links to itself (. and .. thus link to the same directory file).
So when you use "." and ".." as in /dir1/dir2/../dir3/./dir4/whatever,
"." and ".." are processed in the exact same way as "dir1" and "dir2".
This translation is done by the file system; not the shell.
cd ...
Does not work because there is no entry for "..." (at least not normally).
You can create a directory called "..." if you want.
You can actually achieve something like this, though this is an ugly hack:
You can run a command before every command entered to bash, and after every command. For that you trap the DEBUG pseudo signal and set a command to PROMPT_COMMAND, respectively.
trap 'ln -s ../.. ... &>/dev/null | true' DEBUG
PROMPT_COMMAND='rm ...'
With this, it seems like there's an additional entry in the current directory:
pwd
# /tmp/crazy-stuff
ls -a
# . .. ... foo
ls -a .../tmp/crazy-stuff
# . .. ... foo
Though this only works in the current directory, because the symbolic links is deleted after each command invokation. Thus ls foo/bar/... won't work this way.
Another ugly hack would be to "override" mkdir such that it populates every new directory with these symbolic links.
See also the comments on the second answer here, particularly Eliah's: https://askubuntu.com/questions/327126/what-is-a-dot-only-named-folder
Much in the same way that when you cd into some directory subdir, you're actually following a pointer that points to that directory, .. is a pointer added by the OS that points to the parent directory, and I'd imagine . works the same way.

How to copy only new files using bash scripting

I have to use bash scripting to copy files from one folder to another. If the destination folder has a file with the same name but older timestamp, it should not copy. Only newer files should be copied. I could have used cp -u, but I was asked not to use it. Essentially I have to use the test command testing for "ot". Please let me know how could this be done. I believe two for loops one to read the files in the source and one for the destination directories can be used and the the time stamp compared. The problem is that both for loops produce the absolute path names along with the file name. So not sure how to compare them
Thanks
You can profit from the parameter substitution:
for file in "$folder1"/* ; do
filename=${file##*/} # Remove everything to the last slash.
Or, you can change the directory:
cd "$folder1"
for file in * ; do
## you have to use full or relative path to $folder2 here

Ruby - Search and collect files in all directorys

I'm trying to search for a certain file type within all directories on my unix system using a ruby script. I understand the following code will search all files ending with .pdf within the current directory:
my_pdfs = Dir['*pdf']
As well as:
my_pdfs = Dir.glob('*.pdf').each do |f|
puts f
end
But how about searching all directories and sub-directories for files with the .pdf extension?
Check out the Find module:
http://www.ruby-doc.org/stdlib-1.9.3/libdoc/find/rdoc/Find.html
Using Dir.glob is less than ideal since globbing doesn't handle recursion nearly as well as something like find.
Also if you're on a *nix box try using the find command. Its pretty amazingly useful for one liners.
Maybe something like:
pdfs=Dir['/**/*.pdf']
?
Not using Linux right now, so don't know if that will work. The ** syntax implies recursive listing.

How do I get the directory where the executable is located?

I got the filename like this:
_TCHAR filename[_MAX_PATH];
GetModuleFileName(NULL,filename,sizeof(filename));
How do I remove the filename from this full path? Should I use regex?
You can use the Windows shell API function PathRemoveFileSpec to do this. Example usage is listed on the linked page.
Since you use VS++, you can use:
_splitpath and _wsplitpath functions to break apart path

How to match string/dir in a path using bash scripting

I'm trying to create a Makefile that uses information from the path to create a relevant rpm name. Suppose I have two different possible paths:
PATH1 = /usr/local/home/jsmith/code/main
PATH2 = /usr/local/home/jsmith/code/dev/ver2
If "main" is detected in the path, I want to detect and append "main" to the rpm name. If "dev" is detected in the path, I want to detect and append "ver2" to the rpm name.
I'm new to shell scripting and really don't have a good idea on where to start. I could easily do this in something like python, but its for a Makefile so I need to do it in shell.
"main" in the path would be constant, but if "main" doesn't exist, the dev path name would need to be extracted. Here's a few mow examples:
/usr/local/home/jsmith/code/main
/usr/local/home/jsmith/code/dev/ver_usa
/usr/local/home/jsmith/code/dev/ver_mexico
/usr/local/home/jsmith/code/dev/ver3
If "dev" existed, it would be needed to extract "ver_usa", "ver_mexico", "ver3", etc. The dir name needing to be extracted would exactly follow "dev".
something like this, assuming "main" and "ver2" are not constant
some_rpm_name="some rpm"
PATH=/usr/local/home/jsmith/code/main
#PATH2=/usr/local/home/jsmith/code/dev/ver2
s=${PATH##*/}
case "$s" in
*main ) RPM_NAME="${some_rpm_name}_main";;
*ver2) RPM_NAME="${some_rpm_name}_ver2";;
esac
echo $RPM_NAME
Use grep and check for return value?
Also, you can run python in Makefile.
It looks like the last element of the path is always the one you want.
rpmname=$rpmname+${pathname##*/}
or
rpmname=$rpmname+$(basename pathname)

Resources