get back to the previous location after 'cd' command? - unix

I'm writing a shell script that needs to cd to some location. Is there any way to get back to the previous location, that is, the location before cd was executed?

You can simply do
cd -
that will take you back to your previous location.
Some shells let you use pushdir/popdir commands, check out this site. You may also find this SO question useful.

If you're running inside a script, you can also run part of the script inside a sub-process, which will have a private value of $PWD.
# do some work in the base directory, eg. echoing $PWD
echo $PWD
# Run some other command inside subshell
( cd ./new_directory; echo $PWD )
# You are back in the original directory here:
echo $PWD
This has its advantages and disadvantages... it does isolate the directory nicely, but spawning sub-processes may be expensive if you're doing it a lot. ( EDIT: as #Norman Gray points out below, the performance penalty of spawning the sub-process probably isn't very expensive relative to whatever else is happening in the rest of the script )
For the sake of maintainability, I typically use this approach unless I can prove that the script is running slowly because of it.

You could echo PWD into a variable and the cd back to that variable. It may be quieter.

Another alternative is a small set of functions that you can add to your .bashrc that allow you to go to named directories:
# cd /some/horribly/long/path
# save spud
# cd /some/other/horrible/path
# save green
...
# go spud
/some/horribly/long/path
This is documented at A directory navigation productivity tool, but basically involves saving the output of "pwd" into the named mnemonics ("spud" and "green") in the above case, and then cd'ing to the contents of the files.

Related

Sourcing Alias Prevents Less Buffer From Displaying

I'm writing a bash script that needs to both be able to cd in the current shell and use less to display longform text. To be able to cd, I understand that I need to source the script when I call it, which I've done via an alias in my ZSH config. However, when I do this, less breaks: instead of echo -e "$result" | less displaying its usual scrolling buffer, the long text gets dumped into the shell.
For context, this is a bash script acting as a wrapper for a Node.js script so as to be able to have native access to bash commands (like cd, open, etc.). The alias in my zshrc is as follows (with the path truncated): alias bk='source ~/.../bookmark/bookmark.sh'.
Is there any way to satisfy both the need to cd and the need to use less?
Fixed! This turned out to be an issue in my script's logic. I was using condition=$(echo $result | cut -c 1-3), but in reality need the first three characters (not columns) of $result, which I then did by using $result | head -c 3. What's interesting about this is that fetching the first three columns from $result when determined by running ./bookmark.sh works as an equivalent to fetching the first three characters, but running the alias yields the issue here.

Simple command completion using _alternative in zsh

Finally getting around to switching to zsh (from bash)... I'm trying to understand a bit more about the completion system and could use a quick pointer. I have been able to get other completions to work for command arguments, but I'm struggling with path completions.
I use a simple function (cdp) to jump to project directories. I've set up a very basic completion script, which almost works. I just can't seem to get the behavior that I'm hoping for.
Ideally, typing cdp in{tab} would expand to all projects starting with in, such as:
~/Projects/indigo ~/Projects/instant
Instead, I can only get cdp {tab} to get the ~/Projects path. From there, it will expand the first-level directory. I'd like to be able to just run standard completion for cd once the project directory is expanded.
Here is the completion script, save in _cdp and added to fpath:
#compdef cdp
basedir="$HOME/Projects"
# the function for jumping to directories...
cdp() {
if [ -z "$1" ] ; then
cd $basedir
else
cd "$1"
fi
}
# completion helper...
_alternative "directories:user directory:($basedir/*)"
It's pretty basic, I'm just stuck trying to sort out where to go next. Any thoughts or pointers would be great. Thanks!
UPDATE
I'm finding that cdpath works fine for most of what I need... It would still be interesting to know how to complete this simple function, but for now at least I have a working solution using cdpath and auto_cd.

Combine multiple scripts in an "index.html" like fashion?

Is there a standard way in a unixesque (sh/bash/zsh) system to execute a group of scripts as if the group of scripts was one script? (Think index.html). The point is to avoid additional helper scripts like you usually find and keep small programs self sufficient and easier to maintain.
Say I have two (in bold) ruby scripts.
/bin /bin/foo_master /bin/foo_master/main
/bin/foo_master/helper.rb
So now when I execute foo_master
seo#macbook ~ $foo_master [/bin/foo_master/main]: Make
new friends, but keep the old. [/bin/foo_master/helper.rb]: One
is silver and the other gold.
If you're trying to do this without creating a helper script, the typical way to do this would just be to execute both (note: I'll use : $; to represent the shell prompt):
: $; ./main; ./helper.rb
Now, if you're trying to capture the output of both into a file, say, then you can group these into a subshell, with parenthesis, and capture the output of the subshell as if it was a single command, like so:
: $; (./main; ./helper.rb) > index.html
Is this what you're after? I'm a little unclear on what your final goal is. If you want to make this a heavily repeatable thing, then one probably would want to create a wrapper command... but if you just want to run two commands as one, you can do one of the above two options, and it should work for most cases. (Feel free to expand the question, though, if I'm missing what you're after.)
I figured out how to do this in a semi-standard complaint fashion.
I used the eval syntax in shell scripting to lambda evaluate the $PATH at runtime. So in my /etc/.zshrc
$REALPATH = $PATH
$PATH = $REALPATH:`find_paths`
where find_paths is a function that recursively searches the $PATH directories for folders (pseudocode below)
(foreach path in $PATH => ls -d -- */)
So we go from this:
seo#macbook $ echo $PATH
/bin/:/usr/bin/
To this, automagically:
seo#macbook $ echo $PATH
/bin/:/usr/bin/:/bin/foo_master/
Now I just rename main to "foo_master" and voilĂ ! Self contained executable, dare I say "app".
Yep that's an easy one!
#!/bin/bash
/bin/foo_master/main
/bin/foo_master/helper.rb
Save the file as foo_master.sh and type this in the shell:
seo#macbook ~ $sudo chmod +x foo_master.sh
Then to run type:
seo#macbook ~ $./foo_master.sh
EDIT:
The reason that an index.html file is served at any given directory is because the HTTP Server explicitly looks for one. (In server config files you can specify names of files to look for to server like index.html i.e. index.php index.htm foo.html etc). Thus it is not magical. At some point, a "helper script" is explicitly looking for files. I don't think writing a script like above is a step you can skip.

Complex command execution in Makefile

I have a query regarding the execution of a complex command in the makefile of the current system.
I am currently using shell command in the makefile to execute the command. However my command fails as it is a combination of a many commands and execution collects a huge amount of data. The makefile content is something like this:
variable=$(shell ls -lart | grep name | cut -d/ -f2- )
However the make execution fails with execvp failure, since the file listing is huge and I need to parse all of them.
Please suggest me any ways to overcome this issue. Basically I would like to execute a complex command and assign that output to a makefile variable which I want to use later in the program.
(This may take a few iterations.)
This looks like a limitation of the architecture, not a Make limitation. There are several ways to address it, but you must show us how you use variable, otherwise even if you succeed in constructing it, you might not be able to use it as you intend. Please show us the exact operations you intend to perform on variable.
For now I suggest you do a couple of experiments and tell us the results. First, try the assignment with a short list of files (e.g. three) to verify that the assignment does what you intend. Second, in the directory with many files, try:
variable=$(shell ls -lart | grep name)
to see whether the problem is in grep or cut.
Rather than store the list of files in a variable you can easily use shell functionality to get the same result. It's a bit odd that you're flattening a recursive ls to only get the leaves, and then running mkdir -p which is really only useful if the parent directory doesn't exist, but if you know which depths you want to (for example the current directory and all subdirectories one level down) you can do something like this:
directories:
for path in ./*name* ./*/*name*; do \
mkdir "/some/path/$(basename "$path")" || exit 1; \
done
or even
find . -name '*name*' -exec mkdir "/some/path/$(basename {})" \;

Change to xth directory terminal

Is there a way in a unix shell (specifically Ubuntu) to change directory into the xth directory that was printed from the ls command?
I know you can sort a directory in multiple ways, but using the output from ls to get the xth directory?
An example shell:
$ ls
$ first_dir second_dir third_really_long_and_complex_dir
where I want to move into the third_really_long_and_complex_dir by passing 3 (or 2 in proper array format).
I know I could simply copy and paste, but if I'm already using the keyboard, it would be easier to type something like "cdls 2" or something like that if I knew the index.
The main problem with cd in an interactive session is that you generally want to change the current directory of the shell that is processing the command prompt. That means that launching a sub-shell (e.g. a script) would not help, since any cd calls would not affect the parent shell.
Depending on which shell you are using, however, you might be able to define a function to do this. For example in bash:
function cdls() {
# Save the current state of the nullglob option
SHOPT=`shopt -p nullglob`
# Make sure that */ expands to nothing when no directories are present
shopt -s nullglob
# Get a list of directories
DIRS=(*/)
# Restore the nullblob option state
$SHOPT
# cd using a zero-based index
cd "${DIRS[$1]}"
}
Note that in this example I absolutely refuse to parse the output of ls, for a number of reasons. Instead I let the shell itself retrieve a list of directories (or links to directories)...
That said, I suspect that using this function (or anything to this effect) is a very good way to set yourself up for an enormous mess - like using rm after changing to the wrong directory. File-name auto-completion is dangerous enough already, without forcing yourself to count...

Resources