Conditionally source a script in zsh - zsh

I currently source an initializations script in my .zshrc. Unfortunately this makes some modifications to my environment and $PATH I don't want, unless I'm in some specific directories.
Sure, I could compare the output of pwd with the current dir, but I would like to source if I'm in a specific directory or one of its subdirectories at any level.
And is there probably an entirely different solution to my problem?

Using a child process just to run pwd is overkill. You find your current directory always stored in the variable PWD. This variable is maintained automatically by zsh.
You can find whether your working directory is in or below a certain directory dir by
if [[ $PWD:A == $dir:A || $PWD:A == $dir:A/** ]]
then
# I am in $dir or below it
...
fi
The :A takes care of the cases where one of the path components involved in the comparision happens to be a symlink. I'm not sure whether you will need this for PWD, but it does not harm using it.

Related

Folderstructure with rsync in bash

I looked up the forum but didn't find an article which matches my problem. Maybe there is some, and you can help me out with it.
My problem is I want to sync an folder with the command rsync -a -v. The point is I got 5 different Maschinen. On every maschine is a scratch folder I want to sync into the folder: ~/work_dir/scratch_maschines and inside the /scratch_maschines folder should be a folder for maschine_a, maschine_b and so on.
On the maschines it is always the same path: /scratch/my_name. So when I use now this command for the first two maschines:
rsync -a -v --exclude='*.chk' --exclude='*.rwf' --exclude='*.fchk' --delete sp02:/scratch/my_name ~/work_dir/scratch_maschine01; rsync -a -v --exclude='*.chk' --exclude='*.rwf' --exclude='*.fchk' --delete maschine02:/scratch/my_name ~/work_dir/scratch_maschine02
I got a folders for scratch_maschine01 and scratch_maschine02 in my working directory but inside these folders are not direct my data there is first a folder inside with my_name and this folder contains the data. So my question is how can I use the rsync command and get the files from the scratch directorys straight to the folders for each machine?
You might want to consider reformulating your commands similar to the following:
START=`pwd`
EXCLUDES="--exclude='*.chk' --exclude='*.rwf' --exclude='*.fchk'"
{ SOURCE="sp02:/scratch/my_name"
REMOTE="${HOME}/work_dir/scratch_maschine01"
cd "${SOURCE}"
rsync --recursive -v --delete ${EXCLUDES} "./" "${REMOTE}/"
}>${START}/job.log 2>${START}/job.err
The key elements there are
the --recursive which will rsync will expand to include all content and subdirs of the SOURCE directory.
the / behind the ${SOURCE} notifies rsync to limit itself to content of the SOURCE directory, but not the directory itself.
the / behind the ${REMOTE} notifies rsync to limit itself to depositing content into that directory and expect it to already exist, to specifically fail if that does not already exist at REMOTE; this ensures that the remote site doesn't attempt a failsafe PWD and deposit files elsewhere than expected.
The above approach lends itself to a function form that could be placed into a loop with pre-attempt condition checks, along with having a complementary case for all variable assignments grouped under a destination heading (i.e. case statements).
Using such an approach with meaningful labels for variables lends itself to a type of implicit documentation, making the code more meaningful to someone not familiar with the code, as well as a refresher for yourself after a long period of not working or using the code.
I try to avoid the "~" because I prefer to always enclose definitions for variables in double quotes, to avoid issues that might arise from paths that may include unexpected characters or spaces. That way, you are sure to have your defined paths correctly interpreted by commands in scripts.
Lastly, I prefer to use the long form for the rsync options (and almost every other command) so that I don't have to refer to the manual every time to translate the single-character options when trying to understand what is coded, if the need arises for troubleshooting unexpected errors (I have always had poor memory).
My own backup command is as follows. The only reason why the
${PathMirror}${dirC}/
is not encapsulated in single quotes within the double quotes for COM is because I know those variables all evaluate to non-complex strings which cannot be misinterpreted.

GNU-Make: how to copy files from source to destination

I know this is a basic question but I'm missing something fundamental about makefiles.
Take this simple rule/action:
doc: ${SRC_DIR}/doc/dir1/file1.pdf ${SRC_DIR}/doc/dir1/file2.pdf
cp $? ${DEST_DIR}/doc/
the first time I run it, it copies file1.pdf and file2.pdf to the destination/doc directory. Perfect. I'm expecting the next time I run it, for it to do nothing. The source files haven't changed, aren't they a dependency? But when I run I get :
cp : cannot create regular file ..... :Permission denied.
so, 2 questions:
1) Why is it trying to do it again? When I run make -d I see it eventually says: No need to remake target .../file1.pdf and .../file2.pdf but then
it says : must remake target 'doc'
If it doesn't need to make either pdf file, why does it need to make doc?
2) say the pdf files had changed in the source, they are read only though, so it gets the permission denied error. How do you get around this?
A make rule:
target: preqreq0 prereq1...
command
...
says that target needs to be (re)made if it does not exist or is older than
any of the prerequisites preqreq0 prereq1..., and that target shall be
(re)made by running the recipe command ....
Your rule:
doc: ${SRC_DIR}/doc/dir1/file1.pdf ${SRC_DIR}/doc/dir1/file2.pdf
cp $? ${DEST_DIR}/doc/
never creates a file or directory doc, so doc will never exist when
the rule is evaluated (unless you create doc by other means), so the recipe
will always be run.
The kind of target that I believe you want doc to be is a phony target,
but you are going about it wrongly. A reasonable makefile for the purpose would
be:
SRC_DIR := .
DEST_DIR := .
PDFS := file1.pdf file2.pdf
PDF_TARGS := $(patsubst %,$(DEST_DIR)/doc/%,$(PDFS))
.PHONY: doc clean
doc: $(PDF_TARGS)
$(DEST_DIR)/doc/%.pdf: $(SRC_DIR)/doc/dir1/%.pdf
cp $< $#
clean:
rm -f $(PDF_TARGS)
I recommend The GNU Make documentation
As for your second problem, how to overwrite "readonly" files, it is unrelated to make.
You cannot overwrite files to which you do not have write permission, regardless
of the means by which you try to do it. You must get write permission to any files
that you need to write to. It is a system administration matter. If you do not
understand file permissions you may find help at sister-site Unix & Linux
or serverfault

mkdir's "-p" option

So this doesn't seem like a terribly complicated question I have, but it's one I can't find the answer to. I'm confused about what the -p option does in Unix. I used it for a lab assignment while creating a subdirectory and then another subdirectory within that one. It looked like this:
mkdir -p cmps012m/lab1
This is in a private directory with normal rights (rlidwka). Oh, and would someone mind giving a little explanation of what rlidwka means? I'm not a total noob to Unix, but I'm not really familiar with what this means. Hopefully that's not too vague of a question.
The man pages is the best source of information you can find... and is at your fingertips: man mkdir yields this about -p switch:
-p, --parents
no error if existing, make parent directories as needed
Use case example: Assume I want to create directories hello/goodbye but none exist:
$mkdir hello/goodbye
mkdir:cannot create directory 'hello/goodbye': No such file or directory
$mkdir -p hello/goodbye
$
-p created both, hello and goodbye
This means that the command will create all the directories necessaries to fulfill your request, not returning any error in case that directory exists.
About rlidwka, Google has a very good memory for acronyms :). My search returned this for example: http://www.cs.cmu.edu/~help/afs/afs_acls.html
Directory permissions
l (lookup)
Allows one to list the contents of a directory. It does not allow the reading of files.
i (insert)
Allows one to create new files in a directory or copy new files to a directory.
d (delete)
Allows one to remove files and sub-directories from a directory.
a (administer)
Allows one to change a directory's ACL. The owner of a directory can always change the ACL of a directory that s/he owns, along with the ACLs of any subdirectories in that directory.
File permissions
r (read)
Allows one to read the contents of file in the directory.
w (write)
Allows one to modify the contents of files in a directory and use chmod on them.
k (lock)
Allows programs to lock files in a directory.
Hence rlidwka means: All permissions on.
It's worth mentioning, as #KeithThompson pointed out in the comments, that not all Unix systems support ACL. So probably the rlidwka concept doesn't apply here.
-p|--parent will be used if you are trying to create a directory with top-down approach. That will create the parent directory then child and so on iff none exists.
-p, --parents
no error if existing, make parent directories as needed
About rlidwka it means giving full or administrative access. Found it here https://itservices.stanford.edu/service/afs/intro/permissions/unix.
mkdir [-switch] foldername
-p is a switch, which is optional. It will create a subfolder and a parent folder as well, even if parent folder doesn't exist.
From the man page:
-p, --parents no error if existing, make parent directories as needed
Example:
mkdir -p storage/framework/{sessions,views,cache}
This will create subfolder sessions,views,cache inside framework folder irrespective of whether 'framework' was available earlier or not.
PATH: Answered long ago, however, it maybe more helpful to think of -p as "Path" (easier to remember), as in this causes mkdir to create every part of the path that isn't already there.
mkdir -p /usr/bin/comm/diff/er/fence
if /usr/bin/comm already exists, it acts like:
mkdir /usr/bin/comm/diff
mkdir /usr/bin/comm/diff/er
mkdir /usr/bin/comm/diff/er/fence
As you can see, it saves you a bit of typing, and thinking, since you don't have to figure out what's already there and what isn't.
Note that -p is an argument to the mkdir command specifically, not the whole of Unix. Every command can have whatever arguments it needs.
In this case it means "parents", meaning mkdir will create a directory and any parents that don't already exist.

Making multiple files from multiple files with one command in gnu make

Assume 1000 files with extension .xhtml are in directory input, and that a certain subset of those files (with output paths in $(FILES), say) need to be transformed via xslt to files with the same name in directory output. A simple make rule would be:
$(FILES): output/%.xhtml : input/%.xhtml
saxon s:$< o:$# foo.xslt
This works, of course, doing the transform one file at a time. The problem is that I want to use saxon's batch processing to do all the files at one time, since, given the number of files, that would be much faster, considering the overhead of loading java and saxon for each file. Saxon allows the -s (source) option to be a directory and processes all files in that directory, placing the results with the same name in the directory specified in the -o: option.
I'm aware of the well-known technique to get GNU make to do a single command to update multiple files by using pattern rules:
output/%.xhtml: input/%.xhtml
saxon s:input -o:output foo.xslt
But in my case this suffers from two problems. First, it will run the transform on all files in the input directory, not just the ones that have changed; and second, it will not limit the transform to the subset of files specified in $(FILES). The GNU make feature of running a recipe given in a pattern rule only once for all matched targets does not work in the case of so-called "static pattern rules" (see [here]), as the rule given at the top of the post is known.
In order to use the saxon batching feature, I need to create a temporary directory, copy to it only those files to be processed, then run the transform with that temporary directory as the input directory. I tried creating a temporary directory, and remember its name using a target-specific variable for future use, using
$(FILES): TMPDIR:=$(shell mktemp -d)
but this creates a new temporary directory for every single target that is out-of-date. In any case, I'm not sure how to structure the rule that would then copy the necessary files into that directory. I don't want to create the temporary directory at the time the makefile is parsed, since I have a non-recursive make system that will parse all make files, even those not related to the current top-level target, and don't want to create the temporary directory for situations in which it is not necessary/will not be used.
I'm well aware that many questions have been asked on SO in the past about creating multiple files from a single input; one solution is (non-static) pattern rules; other solutions involve phony targets. However, in this case I'm stuck as to how to put all this together.
I can identify the files that changed and copy them using the static pattern rule
$(FILES): output/%.xhtml : input/%.xhtml
TMPDIR=`mktemp -d`
cp $< $(TMPDIR)
but actually I would prefer to copy the files with a single cp command, whereas this copies them one by one. Perhaps there is some application here of cp -u?
I also considered using an ad-hoc extension for those files needing updating but could not see how to get this to work either. I'm about to give up and just run the saxon transform on all files when any of them have changed, but is there any better way?
Personally, I wouldn't try to do this from the command line. That's partly because I'm not a shell scripting wizard. I'm not an Ant wizard either, but because the requirement is to process files that haven't changed, this seems to fall very much into Ant territory. On the other hand, Ant will recompile the stylesheet for each transformation, which is an overhead you might want to avoid; if that's the case then your best bet is probably to write a little Java application. It's probably only 100 lines or less.
Final possibility is to do the processing within Saxon: that is, a single transformation that reads multiple input files using the collection() function and generates multiple result files using xsl:result-document. Saxon (commercial editions) offers an extension function last-modified that allows you to filter the files to be processed. With 1000 files you might also want the extension function saxon:discard-document() to prevent the heap filling.
Personally, I like your original one-compiler-per-file formulation. Does not this work well with make's -j n flag?
You can of course batch up files by copying, and then running saxon at the end. Recursive make (ugh!) can sort out the ordering. Something like:
.PHONY: all
all:
rm -rf tmpdir
${MAKE} tmpdir/sentinel
saxon -s:tmpdir -o:output foo.xslt
tmpdir/sentinel: $(FILES) ; touch $#
$(FILES): output/%.xhtml: input/%.xhtml
ln $< $(patsubst input/%,tmpdir/%,$<)
This does work, though I am very queasy about lying to make (the static pattern rule purports to create the target in output/, but in fact does its dirty deed in tmpdir/).
Note in the recipe for tmpdir/sentinel, that $? is correctly set to the list of output files that are out of date. This might be useful if you can pass a bunch of files to saxon rather than a folder.
I think one issue here is that 'saxon' supports either one file or all files in a directory, so isn't suitable for batch processing without copying to temporary directories.
Otherwise, this is quite simple to do by using a timestamp marker file as a proxy target. For example:
output/.timestamp : $(FILES)
mkdir -p $(#D)
$(COMMAND) -outputdir=output $?
touch $#
The three commands are:
Ensure that the output directory exists.
Run the batch command on files newer than the timestamp file.
Update the timestamp file (creating it if necessary).
Remembering that each line of a command is executed in its own subshell, and that if any command line fails, then subsequent lines are not invoked.
This approach is useful with Java builds.

Can zsh chdir search and match history?

I'm a newbie of zsh.
Could I type something like cd %wiki to jump to ~/prj/golang/gowiki if it's unique.
But if there are more than two directories posible for cd %unix, just show the matching directories.
Here is my sample dirs history.
$ dirs -v
0 ~/prj/golang
1 ~
2 ~/prj/unixconf
3 ~/prj/unixconf/srv
4 ~/memo
5 ~/prj/golang/gowiki
I do not think you can get that without writing a custom version of cd (i.e. creating a function called cd that would take over from the builtin cd.
You could do something like:
DIRSTACKSIZE=20
setopt auto_pushd # Make cd push the old directory onto the directory stack.
setopt pushd_ignore_dups # Ignore duplicates at the directory stack.
setopt pushd_minus # makes the whole pushd list easier to use from 'cd'
Then if you did
% cd -[TAB]
1 -- /tmp
2 -- /etc
You could just use the number:
cd -2 # jumps to /etc
Also notice that you can use the directory stack from other commands (mv, cp etc) through ~-NUMBER
mv notes.txt ~-[TAB]
1 -- /tmp
2 -- /etc
3 -- /my/very/complicated/dir/path
Refer to the zshall meta-manpage (man zshall) for easiest access to learn about many wonderful zsh tricks. Also notable are the zshcontrib and zshmisc manpages.
Here is an excerpt to help make remembering directories in the dirstack easier.
REMEMBERING RECENT DIRECTORIES
The function cdr allows you to change the working directory to a previous
working directory from a list maintained automatically. It is similar in
concept to the directory stack controlled by the pushd, popd and dirs
builtins, but is more configā€ urable, and as it stores all entries
in files it is maintained across sessions and (by default) between
terminal emulators in the current session. (The pushd directory stack is
not actually modified or used by cdr unless you configure it to do so as
described in the configuration section below.)
Installation
The system works by means of a hook function that is called every time
the directory changes. To install the system, autoload the required
functions and use the add-zsh-hook function described above:
autoload -Uz chpwd_recent_dirs cdr add-zsh-hook
add-zsh-hook chpwd chpwd_recent_dirs
Now every time you change directly interactively, no matter which command
you use, the directory to which you change will be remembered in
most-recent-first order.
Use
All direct user interaction is via the cdr function.
The argument to cdr is a number N corresponding to the Nth most recently
changed-to directory. 1 is the immediately preceding directory; the
current directory is remembered but is not offered as a destination.
Note that if you have multiple windows open 1 may refer to a directory
changed to in another window; you can avoid this by having per-terminal
files for storing directory as described for the recent-dirs-file style
below.
If you set the recent-dirs-default style described below cdr will behave
the same as cd if given a non-numeric argument, or more than one
argument. The recent directory list is updated just the same however you
change directory.
If the argument is omitted, 1 is assumed. This is similar to pushd's
behaviour of swapping the two most recent directories on the stack.
Completion for the argument to cdr is available if compinit has been run;
menu selection is recommended, using:
zstyle ':completion:*:*:cdr:*:*' menu selection
to allow you to cycle through recent directories; the order is
preserved, so the first choice is the most recent directory before the
current one. The verbose style is also recommended to ensure the
directory is shown; this style is on by default so no action is required
unless you have changed it.
For named directories, you will want to use the hash -d name=/path and throw it in your zshrc. You can then cd to those directories with cd ~name

Resources