Splitting text files in folder - linux

I have been trying to make a shell script that will split text files one after the other through an entire folder and deposit every split chunk into another designated folder.
Here is what I have so far, I know its probably clunky(have never tried writing a .sh before):
#!/bin/bash
#File Split Automation
echo "Usage: split [Folder w/ Input] [Folder For Outputs] [Options] [PREFIX]
Options: -b [sizeMB]: Split by size
-l [No. of Lines]: Split by Lines
If No Output Folder is Defined Default is Set To: /Desktop/splitter-parts
If No Options Are Selected Default is Size=100MB"
inputdirc=$1
outputdirc=$2
spltion=$3
meastick=$4
prefixture=$5
if [ -d $1 ]
then
echo "You Picked The Folder $1 To Split Files From"
ls $1
else
exit
fi
if [ -d $2 ]
then
echo "Please Confirm Folder Path For Output $outputdirc"
else
cd /root/Desktop/
mkdir -p splitter-parts
fi
read -t 10 -p "Press Enter Or Wait 5 Sec. To Continue"
cd $2
for swordfile in $( ls $1);
do
command -p split $3 $4 -a 3 -d $swordfile $5
done
Anything you see going wrong? Because I am not getting the output I desired, though it functioned fine when I just had a file and a folder in the split-command string.
EDIT::::
Sorry, I apologize. Just getting a bit ahead of myself.
This is what I am seeing when I run it:
root#kali:~/Desktop/Wordlists# ./splitter.sh '/root/Desktop/Wordlists' ' /root/Desktop/Untitled Folder' s 100MB
Usage: split [Folder w/ Input] [Folder For Outputs] [Options] [PREFIX]
Options: -b [sizeMB]: Split by size
-l [No. of Lines]: Split by Lines
If No Output Folder is Defined Default is Set To: /Desktop/splitter-parts
If No Options Are Selected Default is Size=100MB
You Picked The Folder /root/Desktop/Wordlists To Split Files From
10dig10milup2.txt mixed.txt
10dig10miluplow2.txt movie-characters.txt
10dig10miluplow3.txt name1s.txt
((------------------CUT------------)
lower.lst xae2.txt
lower.txt xaf2.txt
mangled.lst xag2.txt
mangled.txt xah6.txt
misc-dictionary.txt
./splitter.sh: line 24: [: /root/Desktop/Untitled: binary operator expected
Press Enter Or Wait 5 Sec. To Continue
./splitter.sh: line 37: cd: /root/Desktop/Untitled: No such file or directory
split: extra operand `10dig10milup2.txt'
Try `split --help' for more information.
split: extra operand `10dig10miluplow2.txt'
Try `split --help' for more information.
split: extra operand `10dig10miluplow3.txt'
Try `split --help' for more information.
split: extra operand `10dig10miluplow4.txt'
Try `split --help' for more information.
...................MORE OF THE SAME.......
As far as what I am supposed to see, I haven't gotten that far yet, clearly I am missing some steps.

A quick rewrite with some notes to follow:
#!/bin/bash
#File Split Automation
usage="Usage: split [Options] [Folder w/ Input] [Folder For Outputs] [PREFIX]
Options: -b [sizeMB]: Split by size
-l [No. of Lines]: Split by Lines
If No Output Folder is Defined Default is Set To: /Desktop/splitter-parts
If No Options Are Selected Default is Size=100MB"
split_opt="-b 100MB"
while getopts hb:l: opt; do
case $opt in
h) echo "$usage"; exit ;;
b) split_opt="-b $OPTARG" ;;
l) split_opt="-l $OPTARG" ;;
esac
done
shift $((OPTIND - 1))
if [[ $# -eq 0 ]]; then
echo "$usage"
exit 1
fi
inputdirc=$1
if [[ -d $inputdirc ]]; then
ls $1
else
echo "no such directory: $inputdirc" >&2
exit 1
fi
if [[ -n $2 ]]; then
outputdirc=$2
else
outputdirc=/root/Desktop/splitter-parts
fi
prefixture=$3
mkdir -p "$outputdirc"
cd "$outputdirc"
for swordfile in "$inputdirc"/*; do
command -p split $split_opt -a 3 -d "$swordfile" $prefixture
done
Notes:
you generally want to quote all your variables. This is the cause of your error, because there was a file with whitespace and square brackets in the name.
I did not quote a couple in the split command because I specifically want the shell to perform word splitting on the values
since options are, well, optional, use getopts to collect them.
you store the positional parameters in variables, but you continue to use the positional parameters. Pick one or the other.

Related

find a file in a folder using a bash script (linux)

need to create a text file within the folder (done)
I use nano to create a file called spn.txt and input some data inside. (done)
4:3
5:5
1:5
3:1
4:3
4:1
create a script
2.1. ask user to input the name of the text file (txt) in the folder
2.2 find the txt file
if there no file found
ask user to input the name of the text file (txt) in the folder
2.3. if have the file read the value of the file and display the number of the left hand side
#!/bin/bash
echo "please input file name with extension (e.g spn.txt)"
read filename
how to create a while command that
look for the filename
if filename not found in the folder, ask user to input file name
if file name is found. display the number of the left hand side.
Disclaimer: this does not define a loop and hence maybe does not answer your question.
This said, if you need to quickly manage some user interactions you can try Zenity
#!/bin/bash
FILE=`zenity --file-selection --title="Select a File"`
case $? in
0)
echo "\"$FILE\" selected.";;
1)
echo "No file selected.";;
-1)
echo "An unexpected error has occurred.";;
esac
Obviously, you need Zenity to be installed, which can be a problem regarding the portability. The advantage is that you can create "complex" input forms. It all depends on your context.
You can easily achieve this with the following commands:
#!/bin/bash
filename=""
echo -e "\nStarting script ... You can press Ctrl+C anytime to stop program execution.\n"
##### Ensure the user will provide a valid file name #################
while [[ ! -r "${filename}" ]] ; do
echo -n "Please, enter the file name: (e.g spn.txt): "
read filename
done
#
# At this point we have a valid filename supplied by the user
# So, prints the left values of it, using the ':' as a field delimiter
cut -d':' -f 1 ${filename}
The cut command is used above with the following parameters:
-d':' -> Set the delimiter to ':'
-f 1 -> Display just the field number 1. If you would like to display just the right numbers, you could use -f 2 here, because assuming the field delimiter is the character ':', the right numbers are located on the second field.
#!/bin/bash
### Maintainer: Vrej Abramian, Git: https://github.com/vrej-ab, vrejab#gmail.com ###
### Configuration Parameters Start ###
request_filename_message="Please input file name with extension (e.g spn.txt): "
filename_variable='filename'
attempts_count='3'
_file_check='' # Leave this empty
### Configuration Parameters End ###
### Finctions Start ###
# Unified messages
echo_green(){ echo -en "\033[0;32m$*" ;echo -e "\033[0;0m"; }
echo_yellow(){ echo -en "\033[1;33m$*" ;echo -e "\033[0;0m"; }
echo_red(){ echo -en "\033[0;31m$*" ;echo -e "\033[0;0m"; }
# Promt and request to input a valid `filename`
request_filename_function(){
# Use `read` with `-p` option which will prompt your message and
# avoid using extra `echo` command.
read -p "${request_filename_message}" "${filename_variable}"
}
# Check if the inputed filename is accessible
file_check(){
if [ -r "${!filename_variable}" ] ;then
echo_green "\n[INFO]: ${!filename_variable} - file is available."
_file_check='pass'
else
echo_red "\n[ERROR]: ${!filename_variable} - file is either un-available or un-accessible."
_file_check='fail'
fi
}
# If provided `filename` wouldn't be available, this provides retrying chances
retry_function(){
local i=1
while [ "${_file_check}" != 'pass' ] && [ ${i} -lt ${attempts_count} ] ;do
echo_yellow "\n[WARN]: Please tray again - $(( attempts_count-i )) times left!"
local i=$(( ++i ))
request_filename_function
file_check
done
if [ ${i} -gt ${attempts_count} ] ;then
echo_red "[FATAL]: No more attempts, Bye-Bye...\n"
exit 1
fi
}
# Get the provided file's first column data - assuming that the delimiter is a ':' character in the file
left_handside_numbers_function(){
cat "${1}" | awk -F':' '{print $1}'
}
# Filter the user defined param,eters in this script
defined_parameters_function(){
defined_parameters=$(sed -n '/^### Configuration Parameters Start ###/,/^### Configuration Parameters End ###/p' "${0}" | \
grep -v '^\#' | awk -F'=' '{print $1}')
defined_parameters="${filename_variable} ${defined_parameters}"
}
# Run cleanup jobs
cleanup_function(){
unset ${defined_parameters}
}
### Functions End ###
### Script Body Start ###
request_filename_function
file_check
retry_function
left_handside_numbers_function "${!filename_variable}"
defined_parameters_function
cleanup_function
### Script Body End ###
exit 0

Linux Shell Script * or ???? is replace automatically

I have some files stored on a server. I have to get all those files using a pattern and exclude the file which contains the current date as the file name.
Files are given below
/var/tomcat/logs/catalina.2022-05-11.log
/var/tomcat/logs/catalina.2022-05-13.log
/var/tomcat/logs/catalina.2022-05-14.log
/var/tomcat/logs/catalina.2022-05-16.log
/var/tomcat/logs/error_1.log
/var/tomcat/logs/error_2.log
/var/tomcat/logs/error_3.log
/var/tomcat/logs/error_4.log
For this, I have stored patterns in a file and I want to read the pattern of that file and find all files with the help of those patterns.
Pattern Input File content is given below, in below I have used '%Y-%m-%d' to identify the date format so that I can exclude the current date file.
/var/tomcat/logs/catalina.*.log;%Y-%m-%d
/var/tomcat/logs/error_*.log
I have developed a shell script which is given below
#!/bin/sh
pattern_filepath=$1
while IFS= read -r line || [ -n "$line" ]; do
pattern_var="$line"
echo pattern: "$pattern_var"
filepath=""
date_format=""
if [[ $pattern_var == *";"* ]];
then
echo "Semicolons ; separator is there"
filepath=($(echo "$pattern_var" | cut -f1 -d ';'))
echo filepath: "$filepath"
date_format=($(echo "$pattern_var" | cut -f2 -d ';'))
else
echo "Semicolons ; separator is not there"
filepath=$pattern_var
fi
echo "date_format: "$date_format
done < "$pattern_filepath"
Command to run the script
sh /var/monitoring/test.sh "/var/monitoring/pattern" > /var/monitoring/test.log
Inside the log file, I can see, that in the file path variable I am getting the value as a date but that should be with an asterisk instead of a date.
Log file 'test.log'
pattern: /var/tomcat/logs/catalina.*.log;%Y-%m-%d
Semicolons ; separator is there
filepath: /var/tomcat/logs/catalina.2022-05-11.log
date_format: %Y-%m-%d
pattern: /var/tomcat/logs/error_*.log
Semicolons ; separator is not there
date_format:
Please help me with this, how can I achieve this?
Simple, straight to the point. Achieves the requirement, without the complexity of patterns, or lists, or ...
#!/bin/bash
sourcedir="/var/tomcat/logs"
if [[ ! -d "$sourcedir" ]]
then
echo "ERROR: source directory $sourcedir does not exist."
exit 1
fi
targetdir="/tmp/somedir"
if [[ ! -d "$targetdir" ]]
then
echo "ERROR: target directory $targetdir does not exist."
exit 1
fi
# Get the catalina logs
cp "$sourcedir"/catalina.*.log "$targetdir"
# Remove the catalina log for today
filetoremove="$targetdir"/catalina.$(date +%Y-%m-%d).log
if [[ -f "$filetoremove" ]]
then
rm -f "$filetoremove"
fi
# Get the error logs
cp "$sourcedir"/error_*.log "$targetdir"
You can add error checking and recovery for the cp and rm commands.

Logging inside a bash script file

I have a pretty large script ( functions contains around 4000 lines of code) . Here is a part of it :
#!/bin/bash
. ./functions > /dev/null 2>&1
some_function(){
while true
do
CHOICE=$(whiptail --menu "\n\n\n\n\n\n\n\n" --title "Tools" --nocancel $window 20 \
"1" "Option1" \
"2" "Option2" \
"3" "Option3" 3>&1 1>&2 2>&3)
case $CHOICE in
1)echo $1 $2 $3;;
2)echo $2 $1 $3;;
3)echo $3 $2 $1;;
esac
done
}
while true; do
arr=()
for ((n=1; n<=$node_num; n++))
do
node+=($n NODE$n)
done
OPTION=$(whiptail --menu "\n\n\n\nPlease choose:\n\n\n" --title "tools" $window 20 "${node[#]}" \
case $OPTION in
1) some_function 1 2 3 ;;
2) some_function 2 1 3 ;;
3) some_function 3 1 2 ;;
esac
done
I want to log the commands executed in the script.
What I have tried so far is :
#!/bin/bash -x --> this will log all the output , but will also "spam" the logs with unneeded information like variable values etc. However this seems to be the best way so far...
I have tried #!/bin/bash -i , enabling history with set -o history . The disadvantage of this is it will log everything . When I call the function file for example it will log every single line as if it was executed .
I have tried creating a log function :
logthis(){
## print the command to the logfile
echo "$(date) $#" >> $historyfile
## run the command and redirect it's error output
## to the logfile
eval "$#" 2>> $historyfile
}
This seems to work most of the time. But when I do, for example:
case $OPTION in
1) logthis "some_function 1 2 3" ;;
2) some_function 2 1 3 ;;
3) some_function 3 1 2 ;;
esac
it will not work as I will lose the arguments 1 2 3
Do you have any other ideas of doing an elegant logging system inside a bash script?
Get rid of the eval in your log function. Just write "$#" to execute the passed command.
logthis() {
echo "$(date): $#" >> "$historyfile"
"$#" 2>> "$historyfile"
}
Then you can log a command by simply prepending logthis. No need for extra quotes.
logthis some_function 1 2 3
This will very nicely preserve all the arguments--even if they have whitespace or other special characters.
I'd recommend a minor improvement to the echo command as well. If you use printf %q it'll log arguments with whitespace better.
echo "$(date):$(printf ' %q' "$#")" >> "$historyfile"
Try set -v
That doesn't parse the commands like set -x, just outputs what gets executed.
set -v
: all the things 'in' $0, 'yay!'
outputs exactly : all the things 'in' $0, 'yay!'
Doesn't even parse $0.
Arguments recorded, minimal spam. ;)
Consider wrapping curlies around the main block of code to manage output logging.
{ # after setup to parse args, set vars & traps, declare funcs, etc
your bulk code here
} 2>&1 | tee /some/file.log
You can save the set -x spam for --verbose mode. ;)

Reading variables from config file in Shell

I have question about Linux shell scripts. My question is realy abstract, so may not make sense. The idea is having 1 script and 2 config files.
Script can be like (drinkOutput.sh):
#!/bin/bash
echo -e " $1 \n"
echo -e " $2 \n"
First Config file contain (beer.conf):
drink1="heineken"
drink2="argus"
Second Config file contain (vine.conf):
drink1="chardonnay"
drink2="hibernal"
The key thing is calling the script. It has to be in next format (or with parameter)
./drinkOutput.sh beer.conf
In this case I need to have in $1 heineken and in $2 argus (inside of drinkOutput script). For
./drinkOutput.sh vine.conf
I need to get back into drinkOutput.sh chardonnay and hibernal.
Does anybody know? Thanks for any tips
You can source the config files if they are in the right format (and it seems it is in your example).
drinkOutput()
{
echo "$1"
echo "$2"
}
conf="$1"
source "$conf"
drinkOutput "$drink1" "$drink2"
If is possible if your script calls itself with the proper arguments after having parsed them from the conf file:
if [ $# == 2 ] ; then
# The arguments are correctly set in the sub-shell.
# 2 arguments: do something with them
echo magic happens: $1 $2
elif [ $# == 1 ] ; then
# 1 argument: conf file: parse conf file
arg1=`sed -n -e 's#drink1="\(.*\)"#\1#p' $1`
arg2=`sed -n -e 's#drink2="\(.*\)"#\1#p' $1`
$0 $arg1 $arg2
else
# error
echo "wrong args"
fi
test:
$ drinkOutput.sh beer.conf
magic happens: heineken argus

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