Get current directory (without full path) in Fish Shell - prompt

A buddy of mine finally got me to start using Fish Shell and I'm trying to set it up similar to how I had Bash. The PS1 in my .bash_profile listed the current directory I was in, followed by a >. It, however, wasn't the absolute path (e.g. /Users/me/Documents/... or ~/Documents/...). If I was in /Users/me/Documents/projects/Go/project1/, the prompt would simply say project1 >.
Is there a Fish Shell alternative to the \W substitution available for Bash? Again, I just want the folder I'm in, not the full path. I know you can use the echo (pwd) for all that.
I have looked into the basename program, and echo "${PWD##*/}", but these appear to only work in Bash.

Taken from #Jubobs' answer:
basename is just a Unix utility; it's not associated to a particular shell, and should work equally well in Bash and Fish.
It appeared I was using basename in the wrong context, and without a suffix.
This was solved by using the following:
function fish_prompt
echo (basename $PWD) "><> "
end

An alternative: fish ships with a function called prompt_pwd which displays /Users/me/Documents/projects/Go/project1/ as ~/D/p/G/project1
function fish_prompt
echo (prompt_pwd) "><> "
end

The complete code of prompt_pwd.fish is below. You must put it inside the directory ~/.config/fish/functions/
function prompt_pwd --description "Print the current working directory, shortened to fit the prompt"
set -q argv[1]
and switch $argv[1]
case -h --help
__fish_print_help prompt_pwd
return 0
end
# This allows overriding fish_prompt_pwd_dir_length from the outside (global or universal) without leaking it
set -q fish_prompt_pwd_dir_length
or set -l fish_prompt_pwd_dir_length 1
# Replace $HOME with "~"
set realhome ~
# #EDITED by Thiago Andrade
set tmpdir (basename $PWD)
set -l tmp (string replace -r '^'"$realhome"'($|/)' '~$1' $tmpdir)
# ORIGINAL VERSION
# set -l tmp (string replace -r '^'"$realhome"'($|/)' '~$1' $PWD)
if [ $fish_prompt_pwd_dir_length -eq 0 ]
echo $tmp
else
# Shorten to at most $fish_prompt_pwd_dir_length characters per directory
string replace -ar '(\.?[^/]{'"$fish_prompt_pwd_dir_length"'})[^/]*/' '$1/' $tmp
end
end
Then you'll see something like this

This is my function in
~/.config/fish/functions/prompt_pwd.fish
and this seems to work fine
function prompt_pwd
set -q argv[1]
and switch $argv[1]
case -h --help
__fish_print_help prompt_pwd
return 0
end
set -q fish_prompt_pwd_dir_length
or set -l fish_prompt_pwd_dir_length 1
set ttmp $PWD
set ttmp (string replace -r '^'$HOME'($|/)' '~$1' $PWD)
set -l tmp (basename $ttmp)
if [ $fish_prompt_pwd_dir_length -eq 0 ]
echo $tmp
else
string replace -ar '(\.?[^/]{'"$fish_prompt_pwd_dir_length"'})[^/]*/' '$1/' $tmp
end
end

Related

tcsh alias with if-then-else condition

I am trying to have an alias with if-then-else condition
on a command result.
the alias will get a file pattern
if there is just one file it will open the file
if there is more or less then 1 files - it will indicate with a message.
the alias I have tried is:
alias atest 'if \("ls \!:1" > 0 \) "nedit ls \!:1" else "echo cant open the file" '
There are a couple of ways, most of them are not very elegant, but the following is the best looking and easiest to create that I have discovered:
alias aliasname '`if (condition == check) echo "echo"` >&/dev/null && code_if_true || code_if_false'
You have the ability to nest if statements following this format, and can also use it as a checker for arguments if you change the interior of the if statement to be
if ("\!:1" =~ "check")
The >&/dev/null is to clean up the output of the function, but is not necessary. If you are interested, there is also a way to make for statements within aliases, but that method is a lot less elegant. I haven't used it but it is necessary if you wish to create an alias with a varying number of arguments. Again, that one is ugly, so at that point I'd just write a function.
You can't do this with an alias. An alias can only expand into a one-line command, but if requires multiple lines.
Instead, use a shell script. The script doesn't have to be in the same language you use interactively, so you can use POSIX shell, which is generally considered much better for programming than C shell (see Csh Programming Considered Harmful).
#!/bin/sh
if [ $# -eq 1 ]
then nedit "$1"
else
echo "Can't open the file"
exit 1
fi
Put this in a file named atest, give it execute permissions, and put it in a directory that's in your $PATH.
Building on Jonathan Roberts solution. This is part of a bash script on the local server. Checks for user XXXXX if true, sends through ssh the BASH command. Otherwise sends the TCSH command. The command checks to see if a directory exists returns true or false
if [[ ${LOCAL_USER} == "XXXXX" ]]; then
LOCAL_DIR_EXIST_CHECK=$(ssh -v -q -i ~/.ssh/"${LOCAL_SSH_KEY_FILE}" "${LOCAL_USER}#${LOCAL_SERVER}" "if [[ -d ${LOCAL_CLIENT_DIRECTORY} ]];then echo 'TRUE'; else echo 'FALSE'; fi")
else
LOCAL_DIR_EXIST_CHECK=$(ssh -v -q -i ~/.ssh/"${LOCAL_SSH_KEY_FILE}" "${LOCAL_USER}#${LOCAL_SERVER}" '`if ( -d ' ${LOCAL_CLIENT_DIRECTORY} ') echo "echo"` > & /dev/null && echo TRUE || echo FALSE')
fi
Actually, tcsh can have aliases with multiple lines in them. Just finish each line with backslash () and continue writing the alias on a next line.
~> alias atest 'set __atest_pattern="\!:1" \
? set __atest_files=( "`ls -d $__atest_pattern`" ) \
? if ( $#__atest_files == 1 ) then \
? nedit "$__atest_files[1]" \
? else if ( $#__atest_files > 1 ) then \
? echo "Too many files ($#__atest_files) are matched to '\''$__atest_pattern'\''" \
? endif'
~> alias atest
set __atest_pattern="!:1"
set __atest_files=( "`ls -d $__atest_pattern`" )
if ( $#__atest_files == 1 ) then
nedit "$__atest_files[1]"
else if ( $#__atest_files > 1 ) then
echo "Too many files ($#__atest_files) are matched to '$__atest_pattern'"
endif
~> atest *
Too many files (73) are matched to '*'
~> atest dummy
ls: cannot access dummy: No such file or directory
~> atest /dummy/*
ls: No match.
~> atest .cshrc
# Actually nedit was invoked here
~>
Using #JonathanRoberts answer I was able to finally improve an alias for exit that I use in shells inside screen so that I don't accidentally exit when I really want to detach. So, now I can override the faked exit with exit now if I want and really exit:
In ~/.tcshrc
if ($?TERM && $TERM =~ screen.*) then
#OLD ALIAS# alias exit "echo 'exit disabled (via alias)'; screen -d"
alias exit '`if ("\!:$" == "now") echo "echo"` >&/dev/null && exec false || echo "really? use: exit now" ; screen -d'
endif

How does this os x bash script that emulates linux's readlink work?

From superuser:
function abspath() {
pushd . > /dev/null;
if [ -d "$1" ]; then
cd "$1";
dirs -l +0;
else
cd "`dirname \"$1\"`";
cur_dir=`dirs -l +0`;
if [ "$cur_dir" == "/" ]; then
echo "$cur_dir`basename \"$1\"`";
else
echo "$cur_dir/`basename \"$1\"`";
fi;
fi;
popd > /dev/null;
}
I want to use this script, but I'm wary of using something I don't fully understand.
Besides the small detail that it does not really syntactically nor semantically emulate the readlink(1)/stat(1) command, you will get all the information you desire by invoking man bash and searching for the dirs, pushd, and popd builtin documentation therein. The basename and dirname commands have respective man pages as well, which explain the commands' functionalities much better than I would.
Having said this, let's describe your referred script somewhat:
1 function abspath() {
2 pushd . > /dev/null;
3 if [ -d "$1" ]; then
4 cd "$1";
5 dirs -l +0;
6 else
7 cd "`dirname \"$1\"`";
8 cur_dir=`dirs -l +0`;
9 if [ "$cur_dir" == "/" ]; then
10 echo "$cur_dir`basename \"$1\"`";
11 else
12 echo "$cur_dir/`basename \"$1\"`";
13 fi;
14 fi;
15 popd > /dev/null;
16 }
Some explanations:
1) Define a shell function
2) Add current working directory to the directory stack and change to it
3) If the submitted parameter corresponds to a directory
4) Change to it
5) Show full pathname of the current working directory
6) otherwise [if the submitted parameter is not a directory]
7) strip away the filename portion, beginning with the last slash `/' character to the end of string and change into the directory
8) Store the full pathname of the current working directory into the `cur_dir` variable
9) if the current working directory is '/' then
10) output the full qualified path using the cur_dir variable and the base name of the submitted parameter
11) otherwise
12) output the full qualified path using the cur_dir variable, a `/', and the base name of the submitted parameter
13) end of inner if block
14) end of outer if block
15) remove current working directory from directory stack and change back it, essentially moving back to the originating directory from where we called the function from.
16) close shell function definition
Together with the man pages and the respective bash builtin documentation, you should understand the inner workings of this shell function.

How to rename all files in a directory adding a prefix

I first create 10 digit random number
export mask=$RANDOM$RANDOM$RANDOM; let "mask %= 10000000000";
This command works well
for i in /home/testing/*; do mv "$i" "$mask$i"; done
The problem with the above command is that it only works when I am in /home/testing. As soon as I move the script, i get this error
mv: cannot move ‘/home/testing/rec001.mp4’ to ‘3960731225/home/testing/rec001.mp4’: No such file or directory
What am I doing wrong here?
You need to separate the path from the filename before you apply the mask. For example, to use in a script where the directory is passed as an argument to the script:
path="$1"
## Note: this assumes you are exporting mask earlier. If not, set mask here
for i in "${path}"/*; do
dir="${i%/*}" # path component
ffname="${i##*/}" # filename component
mv "$i" "${dir}/${mask}${ffname}"
done
This will apply mask to all files in the given directory, no matter where the directory is.
An example of a script that incorporates this is shown below. You can save this script wherever you like. You can either make it executable chmod 0755 scriptname or call it with bash scriptname. To use the script, add the path you want to prefix the files in as the first argument. E.g bash scriptname /path/to/files (or just scriptname /path/to/files if you made it executable):
#!/bin/bash
# validate input
[ -n "$1" ] || {
printf "error: insufficient input. Usage: %s /path/to/files\n" "${0//\//}"
exit 1
}
# validate directory
[ -d "$1" ] || {
printf "error: directory not found: '%s'\n" "$1"
exit 1
}
path="$1"
## Note: this assumes you are exporting mask earlier. If not, set mask here
## validate mask set and is 10 chars (added per comment)
[ -n "$mask" -a "${#mask}" -eq 10 ] || {
printf "error: mask '%s' either unset or not 10 characters\n" "$mask"
exit 1
}
# move files
for i in "${path}"/*; do
[ -f "$i" ] || continue # if not file, skip
dir="${i%/*}" # path component
ffname="${i##*/}" # full filename component (with .ext)
mv "$i" "${dir}/${mask}${ffname}"
done
Here is a sample of what moves would take place with the script named prefix.sh when called on the directory dat in the current working directory and when called on ~/tmp outside the current directory:
output (mask=3960731225):
$ ./prefix.sh dat
dat/f1f2.dat => dat/3960731225f1f2.dat
dat/field.txt => dat/3960731225field.txt
dat/flop.txt => dat/3960731225flop.txt
dat/hh.dat => dat/3960731225hh.dat
dat/hh1.dat => dat/3960731225hh1.dat
dat/hostnm => dat/3960731225hostnm
dat/hosts.dat => dat/3960731225hosts.dat
$ ./prefix.sh ~/tmp
/home/david/tmp/tcpd.tar.xz => /home/david/tmp/3960731225tcpd.tar.xz
/home/david/tmp/tcpdump-capt => /home/david/tmp/3960731225tcpdump-capt
/home/david/tmp/tcpdump.new.1000 => /home/david/tmp/3960731225tcpdump.new.1000
/home/david/tmp/test => /home/david/tmp/3960731225test
There is two commands that is quite helpful, basename and dirname.
They will give you the dir part and the filename, have a look at this test script.
#!/bin/bash
mask=$RANDOM$RANDOM$RANDOM; let "mask %= 10000000000";
echo $mask
mkdir -p testing
> testing/nisse.txt
> testing/guste.txt
> testing/berra.txt
ls testing/
for i in testing/*
do
file=$(basename $i)
dir=$(dirname $i)
newfile=$mask$file
echo $i $dir $file $newfile
mv "$dir/$file" "$dir/$newfile"
done
ls testing/
And it will output:
247639260
berra.txt gusten.txt nisse.txt
testing/berra.txt testing berra.txt 247639260berra.txt
testing/guste.txt testing guste.txt 247639260guste.txt
testing/nisse.txt testing nisse.txt 247639260nisse.txt
247639260berra.txt 247639260guste.txt 247639260nisse.txt
Please note that I wrote it very verbose to make it more clear and readable.
How about adding a cd command before your command now, wherever you want to move the script it works,
cd /home/testing
for i in /home/testing/*; do mv "$i" "$mask$i"; done

about the Linux Shell 'While' command

code:
path=$PATH:
while [ -n $path ]
do
ls -ld ${path%%:*}
path=${path#*:}
done
I want to get the each part of path .When run the script ,it can not get out of the while process 。Please tell me why . Is some problem in 'while [ -n $path ]' ?
The final cut never results in an empty string. If you have a:b:c, you'll strip off the a and then the b, but never the c. I.e., this:
${path#*:}
Will always result in a non-empty string for the last piece of the path. Since the -n check looks for an empty string, your loop runs forever.
If $path doesn't have a colon in it, ${path#*:} will return $path. So you have an infinite loop.
p="foo"
$ echo ${p#*:}
foo
$ p="foo:bar"
$ echo ${p#*:}
bar
You have some bugs in your code. This should do the trick:
path=$PATH
while [[ $path != '' ]]; do
# you can replace echo to whatever you need, like ls -ld
echo ${path%%:*}
if echo $path | grep ':' >/dev/null; then
path=${path#*:}
else path=''
fi
done
Your path, after is initialized, will always check True for [ -n path ] test. This is the main reason for which you never get out of the while loop.

Directory bookmarking for bash [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 15 days ago.
Improve this question
Is there any directory bookmarking utility for bash to allow move around faster on the command line?
Also, have a look at CDPATH
A colon-separated list of search paths available to the cd command, similar in function to the $PATH variable for binaries. The $CDPATH variable may be set in the local ~/.bashrc file.
ash$ cd bash-doc
bash: cd: bash-doc: No such file or directory
bash$ CDPATH=/usr/share/doc
bash$ cd bash-doc
/usr/share/doc/bash-doc
bash$ echo $PWD
/usr/share/doc/bash-doc
and
cd -
It's the command-line equivalent of the back button (takes you to the previous directory you were in).
Thanks for sharing your solution, and I'd like to share mine as well, which I find more useful than anything else I've came across before.
The engine is a great, universal tool: command-line fuzzy finder by Junegunn.
It primarily allows you to “fuzzy-find” files in a number of ways, but it also allows to feed arbitrary text data to it and filter this data. So, the shortcuts idea is simple: all we need is to maintain a file with paths (which are shortcuts), and fuzzy-filter this file. Here's how it looks: we type cdg command (from "cd global", if you like), get a list of our bookmarks, pick the needed one in just a few keystrokes, and press Enter. Working directory is changed to the picked item:
It is extremely fast and convenient: usually I just type 3-4 letters of the needed item, and all others are already filtered out. Additionally, of course we can move through list with arrow keys or with vim-like keybindings Ctrl+j/Ctrl+k.
Article with details: Fuzzy shortcuts for your shell.
It is possible to use it for GUI applications as well (via xterm): I use that for my GUI file manager Double Commander. I have plans to write an article about this use case, too.
Bashmarks is an amazingly simple and intuitive utility.
In short, after installation, the usage is:
s <bookmark_name> - Saves the current directory as "bookmark_name"
g <bookmark_name> - Goes (cd) to the directory associated with "bookmark_name"
p <bookmark_name> - Prints the directory associated with "bookmark_name"
d <bookmark_name> - Deletes the bookmark
l - Lists all available bookmarks
In bash script/command,
you can use pushd and popd
pushd
Save and then change the current directory. With no arguments, pushd exchanges the top two directories.
Usage
cd /abc
pushd /xxx <-- save /abc to environment variables and cd to /xxx
pushd /zzz
pushd +1 <-- cd /xxx
popd is to remove the variable (reverse manner)
bookmarks.sh provides a bookmark management system for the Bash version 4.0+. It can also use a Midnight Commander hotlist.
Yes there is DirB: Directory Bookmarks for Bash well explained in this Linux Journal article
An example from the article:
% cd ~/Desktop
% s d # save(bookmark) ~/Desktop as d
% cd /tmp # go somewhere
% pwd
/tmp
% g d # go to the desktop
% pwd
/home/Desktop
Inspired by the question and answers here, I added the lines below to my ~/.bashrc file.
With this you have a favdir command (function) to manage your favorites and a autocompletion function to select an item from these favorites.
# ---------
# Favorites
# ---------
__favdirs_storage=~/.favdirs
__favdirs=( "$HOME" )
containsElement () {
local e
for e in "${#:2}"; do [[ "$e" == "$1" ]] && return 0; done
return 1
}
function favdirs() {
local cur
local IFS
local GLOBIGNORE
case $1 in
list)
echo "favorite folders ..."
printf -- ' - %s\n' "${__favdirs[#]}"
;;
load)
if [[ ! -e $__favdirs_storage ]] ; then
favdirs save
fi
# mapfile requires bash 4 / my OS-X bash vers. is 3.2.53 (from 2007 !!?!).
# mapfile -t __favdirs < $__favdirs_storage
IFS=$'\r\n' GLOBIGNORE='*' __favdirs=($(< $__favdirs_storage))
;;
save)
printf -- '%s\n' "${__favdirs[#]}" > $__favdirs_storage
;;
add)
cur=${2-$(pwd)}
favdirs load
if containsElement "$cur" "${__favdirs[#]}" ; then
echo "'$cur' allready exists in favorites"
else
__favdirs+=( "$cur" )
favdirs save
echo "'$cur' added to favorites"
fi
;;
del)
cur=${2-$(pwd)}
favdirs load
local i=0
for fav in ${__favdirs[#]}; do
if [ "$fav" = "$cur" ]; then
echo "delete '$cur' from favorites"
unset __favdirs[$i]
favdirs save
break
fi
let i++
done
;;
*)
echo "Manage favorite folders."
echo ""
echo "usage: favdirs [ list | load | save | add | del ]"
echo ""
echo " list : list favorite folders"
echo " load : load favorite folders from $__favdirs_storage"
echo " save : save favorite directories to $__favdirs_storage"
echo " add : add directory to favorites [default pwd $(pwd)]."
echo " del : delete directory from favorites [default pwd $(pwd)]."
esac
} && favdirs load
function __favdirs_compl_command() {
COMPREPLY=( $( compgen -W "list load save add del" -- ${COMP_WORDS[COMP_CWORD]}))
} && complete -o default -F __favdirs_compl_command favdirs
function __favdirs_compl() {
local IFS=$'\n'
COMPREPLY=( $( compgen -W "${__favdirs[*]}" -- ${COMP_WORDS[COMP_CWORD]}))
}
alias _cd='cd'
complete -F __favdirs_compl _cd
Within the last two lines, an alias to change the current directory (with autocompletion) is created. With this alias (_cd) you are able to change to one of your favorite directories. May you wan't to change this alias to something which fits your needs.
With the function favdirs you can manage your favorites (see usage).
$ favdirs
Manage favorite folders.
usage: favdirs [ list | load | save | add | del ]
list : list favorite folders
load : load favorite folders from ~/.favdirs
save : save favorite directories to ~/.favdirs
add : add directory to favorites [default pwd /tmp ].
del : delete directory from favorites [default pwd /tmp ].
#getmizanur
I used your cdb script. I enhanced it slightly by adding bookmarks tab completion. Here's my version of your cdb script.
_cdb()
{
local _script_commands=$(ls -1 ~/.cd_bookmarks/)
local cur=${COMP_WORDS[COMP_CWORD]}
COMPREPLY=( $(compgen -W "${_script_commands}" -- $cur) )
}
complete -F _cdb cdb
function cdb() {
local USAGE="Usage: cdb [-h|-c|-d|-g|-l|-s] [bookmark]\n
\t[-h or no args] - prints usage help\n
\t[-c bookmark] - create bookmark\n
\t[-d bookmark] - delete bookmark\n
\t[-g bookmark] - goto bookmark\n
\t[-l] - list bookmarks\n
\t[-s bookmark] - show bookmark location\n
\t[bookmark] - same as [-g bookmark]\n
Press tab for bookmark completion.\n"
if [ ! -e ~/.cd_bookmarks ] ; then
mkdir ~/.cd_bookmarks
fi
case $1 in
# create bookmark
-c) shift
if [ ! -f ~/.cd_bookmarks/$1 ] ; then
echo "cd `pwd`" > ~/.cd_bookmarks/"$1"
complete -F _cdb cdb
else
echo "Try again! Looks like there is already a bookmark '$1'"
fi
;;
# goto bookmark
-g) shift
if [ -f ~/.cd_bookmarks/$1 ] ; then
source ~/.cd_bookmarks/"$1"
else
echo "Mmm...looks like your bookmark has spontaneously combusted. What I mean to say is that your bookmark does not exist." ;
fi
;;
# show bookmark
-s) shift
if [ -f ~/.cd_bookmarks/$1 ] ; then
cat ~/.cd_bookmarks/"$1"
else
echo "Mmm...looks like your bookmark has spontaneously combusted. What I mean to say is that your bookmark does not exist." ;
fi
;;
# delete bookmark
-d) shift
if [ -f ~/.cd_bookmarks/$1 ] ; then
rm ~/.cd_bookmarks/"$1" ;
else
echo "Oops, forgot to specify the bookmark" ;
fi
;;
# list bookmarks
-l) shift
ls -1 ~/.cd_bookmarks/ ;
;;
-h) echo -e $USAGE ;
;;
# goto bookmark by default
*)
if [ -z "$1" ] ; then
echo -e $USAGE
elif [ -f ~/.cd_bookmarks/$1 ] ; then
source ~/.cd_bookmarks/"$1"
else
echo "Mmm...looks like your bookmark has spontaneously combusted. What I mean to say is that your bookmark does not exist." ;
fi
;;
esac
}
Yes, one that I have written, that is called anc.
https://github.com/tobimensch/anc
Anc stands for anchor, but anc's anchors are really just bookmarks.
It's designed for ease of use and there're multiple ways of navigating, either by giving a text pattern, using numbers, interactively, by going back, or using [TAB] completion.
I'm actively working on it and open to input on how to make it better.
Allow me to paste the examples from anc's github page here:
# make the current directory the default anchor:
$ anc s
# go to /etc, then /, then /usr/local and then back to the default anchor:
$ cd /etc; cd ..; cd usr/local; anc
# go back to /usr/local :
$ anc b
# add another anchor:
$ anc a $HOME/test
# view the list of anchors (the default one has the asterisk):
$ anc l
(0) /path/to/first/anchor *
(1) /home/usr/test
# jump to the anchor we just added:
# by using its anchor number
$ anc 1
# or by jumping to the last anchor in the list
$ anc -1
# add multiple anchors:
$ anc a $HOME/projects/first $HOME/projects/second $HOME/documents/first
# use text matching to jump to $HOME/projects/first
$ anc pro fir
# use text matching to jump to $HOME/documents/first
$ anc doc fir
# add anchor and jump to it using an absolute path
$ anc /etc
# is the same as
$ anc a /etc; anc -1
# add anchor and jump to it using a relative path
$ anc ./X11 #note that "./" is required for relative paths
# is the same as
$ anc a X11; anc -1
# using wildcards you can add many anchors at once
$ anc a $HOME/projects/*
# use shell completion to see a list of matching anchors
# and select the one you want to jump to directly
$ anc pro[TAB]
In addition to #Dmitri Frank's answer - I have implemented the cdb command (aka cd bookmark) via simple alias (add this line to your ~/.bash_profile):
alias b='cat ~/.cd-bookmarks | grep -v "^\s*#" | grep -v "^\s*$" | fzf'
alias cdb='cd "$(b)"'
Create file ~/.cd-bookmarks and enter your paths, one per line. It supports empty lines and comments via #:
$ cat ~/.cd-bookmarks
### Devel stuff ###
/Users/pujezdsky/devel/projects/stackoverflow/
### Photo stuff ###
/Users/pujezdsky/Photos/
/Users/pujezdsky/Photos/last-import/
/Users/pujezdsky/Photos/dir with spaces/
Unfortunatelly it does not support ~ expansion so enter full paths.
Then you are able to do
$ cdb
And because of b alias even some more advanced stuff
$ mc `b`
$ cp my-file.txt `b`
$ touch `b`/test.sh
Unfortunately though, if you have spaces in your bookmark paths, you have to quote the `b` call. That makes it less handsome:
$mc "`b`"
Note 1:
Before you do this, check if you already have cdb, b commands / aliases to avoid their overwrite and malfunction. The easiest way is to use these commands that should return something like -bash: type: cdb: not found
$ type cdb
$ type b
Note 2:
The b alias can be simplified to
alias b='egrep -v "(^\s*#|^\s*$)" ~/.cd-bookmarks | fzf'
Note 3:
You can also make alias for adding current folder into your bookmarks. It is as simple as
alias ba='pwd >> ~/.cd-bookmarks'
For short term shortcuts, I have a the following in my respective init script (Sorry. I can't find the source right now and didn't bother then):
function b() {
alias $1="cd `pwd -P`"
}
Usage:
In any directory that you want to bookmark type
b THEDIR # <THEDIR> being the name of your 'bookmark'
It will create an alias to cd (back) to here.
To return to a 'bookmarked' dir type
THEDIR
It will run the stored alias and cd back there.
Caution: Use only if you understand that this might override existing shell aliases and what that means.
As practice shows, not so many bookmarks are needed every day.
So we can store them inside the script:
function go {
# go home dir if no params
if [ $# -eq 0 ]; then cd ~; return; fi;
# declare an assoc array with CAPITAL -A switch
declare -A o
# declare aliases and targets
o[apd]=$APPDATA
o[cli]='/mnt/c/CLI/'
o[closk]='/mnt/d/JOB/CLosk.Work/Dev._default/'
o[ds]='/mnt/d/JOB/DS/'
o[gh]='/mnt/d/Alpha/GitHub/'
o[pf]='/mnt/c/Program Files/'
o[wsa]='/mnt/d/JOB/WSA/Dev/'
# here we go
if [ ${o[$1]+_} ]; then cd "${o[$1]}"; fi
}
Using associative array let the list of links to be corrected with ease.
As you can see this script successfully used under Windows also.
I'm using this script under CentOS and Ubuntu too. With other links of course.
Also, I'm using this:
alias ~='go'
So:
~ # go to home dir
~ apd # go to system Application Data folder
And so on.
I wrote this function some time ago in ~/.bashrc:
cdfav() {
local favfile
local tmpfavfile
local match
favfile=~/.cdfav
if [ "$1" = "" ]; then
awk '{print i++ " " $0}' $favfile
return $?
fi
if [ "$1" = "-a" ]; then
readlink -f $2 >> $favfile
return $?
fi
if [ "$1" = "-d" ]; then
tmpfavfile=/tmp/.cdfav-tmp-${$}
awk '{print i++ " " $0}' $favfile | sed "/$2/d" | cut -f2- -d' ' > $tmpfavfile
mv $tmpfavfile $favfile
return 0
fi
if [ ! -e $favfile ]; then
return 1
fi
match=$(awk '{print i++ " " $0}' $favfile | grep "$1" | head -1 | cut -f2- -d' ')
if [ "$match" = "" ]; then
return 1
fi
cd $match
return $?
}
Usage examples:
~$ cdfav # lists favorite dirs
0 /home/user/dev/linux/
1 /home/user/dev/linux/references/v7unix
~$ cdfav ^1 # match 1 dir
~/dev/linux$ cdfav refe # no need for typing the whole dir name/path. The directory will be the first matched
~/dev/linux/references/v7unix$
I can add a (current) directory to my list with "-a ."
~/dev/linux/references/v7unix$ cd v7/
~/dev/linux/references/v7unix/v7$ cdfav -a .
~/dev/linux/references/v7unix/v7$ cdfav
0 /home/user/dev/linux/
1 /home/user/dev/linux/references/v7unix
2 /home/user/dev/linux/references/v7unix/v7
or remove a directories containing substring/matching regexp (with gnu grep on my machine) like this:
~/dev/linux/references/v7unix/v7$ cdfav -d v7
~/dev/linux/references/v7unix/v7$ cdfav
0 /home/user/dev/linux/
Have not tested with other shells like oksh yet, but I think it will work there also without much tweaking.

Resources