change terminal title according to the last 2 directories in the path - bash

I want to change the title everytime I enter a new directory ( when using cd ), but show only the last 2 directories. I'm using tcsh at work and bash at home.
For example: if I'm at folder ~/work/stuff and I write: cd 1.1, I want my new title to be stuff/1.1.
I already know how to change the title everytime I change the folder:
alias cd 'cd \!*; echo "\033]0;`pwd`\a"'
And I know how to take only the 2 last directories:
pwd | awk -F / -v q="/" '{print $(NF-1)q$NF}'
The question is how to combine these two, or how to do it in a different way?
It doesn't have to be through alias to cd.

What I did was creating a script file named titleRename.tcsh with the following code:
#!/bin/tcsh -f
set fullpath = "`pwd`\a"
set newTitle = `echo $fullpath | awk -F / '{OFS="/"; if(NF > 2){printf $(NF-2);printf "/"; printf $(NF-1); printf "/"; print $NF;}else print $0}'`
echo "\033]0;$newTitle"
It splits the pwd with awk, getting only the last 3 directories, and then it prints to the tab name.
Then I added in the .alias file the following:
alias cd 'cd \!*; <dir of script file>/titleRename.tcsh'
Now the title name changes automatically whenever I cd to a different directory :)

I originally thought you should be able to use the full command where you have pwd in backticks in the alias ie:
alias cd 'cd \!*; echo "\033]0;`pwd | awk -F / -v q="/" '{print $(NF-1)q$NF}'`\a"'
but now I realise there may be problems with the nested quoting. And that wouldn't work in bash anyway; I don't think there's a way to access command parameters in an alias.
Instead of aliasing cd you should update the title with the prompt. I don't know tcsh, but in bash the normal way to do this sort of thing is with the special pseudo-variable PS1:
# Minimalist prompt
PS1="\$ "
# Additionally set terminal title to cwd
case "$TERM" in
xterm*|rxvt*)
PROMPT_DIRTRIM=2
PS1="\033]0;\w\a$PS1"
;;
*)
;;
esac
That won't trim the directory name quite the way you were doing it, but unfortunately I can't get the quoting right to be able to use the escape sequence in PROMPT_COMMAND.

Related

ksh - why presence of the "\" file in current working directory changes behavior of variable processing

I had an issue with third party ksh script.
Found out, that it was failing because of file named "\" in user home directory.
Here is a simple testcase:
$ mkdir -p ~/dir1 && cd ~/dir1 && touch '\' && x="\* a" && echo $x
\ a
$ mkdir -p ~/dir2 && cd ~/dir2 && x="\* a" && echo $x
\* a
The question is, why the presence of "\" file in a working directory changes the result.
Is this expected?
Thanks.
T.
Looks like the expected behaviour.
If you want the same behaviour in both cases, either use set -o noglob inside your script, or run the script with the -f option to disable file name substitution.
The default is that the * is a special character when interpolating so will match whatever file exists (in your case dir1 will contain only one real file with the name of the backslash character.)
The second directory dir2 has no real files so ksh just shows the pattern exactly as you typed it.

Why is this bash script not changing path?

I wrote a basic script which changes the directory to a specific path and shows the list of folders, but my script shows the list of files of the current folder where my script lies instead of which I specify in script.
Here is my script:
#!/bin/bash
v1="$(ls -l | awk '/^-/{ print $NF }' | rev | cut -d "_" -f2 | rev)"
v2=/home/PS212-28695/logs/
cd $v2 && echo $v1
Does any one knows what I am doing wrong?
Your current script makes no sense really. v1 variable is NOT a command to execute as you expect, but due to $() syntax it is in fact output of ls -t at the moment of assignment and that's why you have files from current directory there as this is your working directory at that particular moment. So you should rather be doing ordinary
ls -t /home/PS212-28695/logs/
EDIT
it runs but what if i need to store the ls -t output to variable
Then this is same syntax you already had, but with proper arguments:
v1=$(ls -t /home/PS212-28695/logs/)
echo ${v1}
If for any reason you want to cd then you have to do that prior setting v1 for the same reason I explained above.

Bash: passing a variable to mv command option

--Bash 4.1.17 (running with Cygwin)
Hello, I am trying to pass the date into the --suffix option on the move (mv) command. I am able to pass in a simple string (like my name) but unable to pass in the date. If you run the script below you will see that the mv command with the suffix="$var" works but suffix="$now" does not.
#!/bin/bash
dir="your directory goes here"
now="$(date "+%m/%d/%y")"
var="_CARL!!!"
echo "$now"
echo "$var"
cd "$dir"
touch test.txt
# error if already exists
mkdir ./stack_question
touch ./stack_question/test.txt
mv -b --suffix="$var" test.txt ./stack_question/
The idea is that if test.txt already exists when trying to move the file, the file will have a suffix appended to it. So if you run this script with:
--suffix="$var"
you will see that the stack_question directory contains two files:
test.txt & test.txt_CARL!!!
But, if you run this script with:
--suffix="$now"
you will see that in the stack_question directory only contains:
test.txt
Any help on this would be greatly appreciated!
It is because you have embedded / in your date format try
now="$(date +%m_%d_%y)"

Get current directory or folder name (without the full path)

How could I retrieve the current working directory/folder name in a bash script, or even better, just a terminal command.
pwd gives the full path of the current working directory, e.g. /opt/local/bin but I only want bin.
No need for basename, and especially no need for a subshell running pwd (which adds an extra, and expensive, fork operation); the shell can do this internally using parameter expansion:
result=${PWD##*/} # to assign to a variable
result=${result:-/} # to correct for the case where PWD=/
printf '%s\n' "${PWD##*/}" # to print to stdout
# ...more robust than echo for unusual names
# (consider a directory named -e or -n)
printf '%q\n' "${PWD##*/}" # to print to stdout, quoted for use as shell input
# ...useful to make hidden characters readable.
Note that if you're applying this technique in other circumstances (not PWD, but some other variable holding a directory name), you might need to trim any trailing slashes. The below uses bash's extglob support to work even with multiple trailing slashes:
dirname=/path/to/somewhere//
shopt -s extglob # enable +(...) glob syntax
result=${dirname%%+(/)} # trim however many trailing slashes exist
result=${result##*/} # remove everything before the last / that still remains
result=${result:-/} # correct for dirname=/ case
printf '%s\n' "$result"
Alternatively, without extglob:
dirname="/path/to/somewhere//"
result="${dirname%"${dirname##*[!/]}"}" # extglob-free multi-trailing-/ trim
result="${result##*/}" # remove everything before the last /
result=${result:-/} # correct for dirname=/ case
Use the basename program. For your case:
% basename "$PWD"
bin
$ echo "${PWD##*/}"
​​​​​
Use:
basename "$PWD"
OR
IFS=/
var=($PWD)
echo ${var[-1]}
Turn the Internal Filename Separator (IFS) back to space.
IFS=
There is one space after the IFS.
You can use a combination of pwd and basename. E.g.
#!/bin/bash
CURRENT=`pwd`
BASENAME=`basename "$CURRENT"`
echo "$BASENAME"
exit;
How about grep:
pwd | grep -o '[^/]*$'
This thread is great! Here is one more flavor:
pwd | awk -F / '{print $NF}'
basename $(pwd)
or
echo "$(basename $(pwd))"
I like the selected answer (Charles Duffy), but be careful if you are in a symlinked dir and you want the name of the target dir. Unfortunately I don't think it can be done in a single parameter expansion expression, perhaps I'm mistaken. This should work:
target_PWD=$(readlink -f .)
echo ${target_PWD##*/}
To see this, an experiment:
cd foo
ln -s . bar
echo ${PWD##*/}
reports "bar"
DIRNAME
To show the leading directories of a path (without incurring a fork-exec of /usr/bin/dirname):
echo ${target_PWD%/*}
This will e.g. transform foo/bar/baz -> foo/bar
echo "$PWD" | sed 's!.*/!!'
If you are using Bourne shell or ${PWD##*/} is not available.
Surprisingly, no one mentioned this alternative that uses only built-in bash commands:
i="$IFS";IFS='/';set -f;p=($PWD);set +f;IFS="$i";echo "${p[-1]}"
As an added bonus you can easily obtain the name of the parent directory with:
[ "${#p[#]}" -gt 1 ] && echo "${p[-2]}"
These will work on Bash 4.3-alpha or newer.
There are a lots way of doing that I particularly liked Charles way because it avoid a new process, but before know this I solved it with awk
pwd | awk -F/ '{print $NF}'
For the find jockeys out there like me:
find $PWD -maxdepth 0 -printf "%f\n"
i usually use this in sh scripts
SCRIPTSRC=`readlink -f "$0" || echo "$0"`
RUN_PATH=`dirname "${SCRIPTSRC}" || echo .`
echo "Running from ${RUN_PATH}"
...
cd ${RUN_PATH}/subfolder
you can use this to automate things ...
Just use:
pwd | xargs basename
or
basename "`pwd`"
Below grep with regex is also working,
>pwd | grep -o "\w*-*$"
If you want to see only the current directory in the bash prompt region, you can edit .bashrc file in ~. Change \w to \W in the line:
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u#\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
Run source ~/.bashrc and it will only display the directory name in the prompt region.
Ref: https://superuser.com/questions/60555/show-only-current-directory-name-not-full-path-on-bash-prompt
I strongly prefer using gbasename, which is part of GNU coreutils.
Just run the following command line:
basename $(pwd)
If you want to copy that name:
basename $(pwd) | xclip -selection clipboard
An alternative to basname examples
pwd | grep -o "[^/]*$"
OR
pwd | ack -o "[^/]+$"
My shell did not come with the basename package and I tend to avoid downloading packages if there are ways around it.
You can use the basename utility which deletes any prefix ending in / and the suffix (if present in string) from string, and prints the
result on the standard output.
$basename <path-of-directory>
Just remove any character until a / (or \, if you're on Windows). As the match is gonna be made greedy it will remove everything until the last /:
pwd | sed 's/.*\///g'
In your case the result is as expected:
λ a='/opt/local/bin'
λ echo $a | sed 's/.*\///g'
bin
Here's a simple alias for it:
alias name='basename $( pwd )'
After putting that in your ~/.zshrc or ~/.bashrc file and sourcing it (ex: source ~/.zshrc), then you can simply run name to print out the current directories name.
The following commands will result in printing your current working directory in a bash script.
pushd .
CURRENT_DIR="`cd $1; pwd`"
popd
echo $CURRENT_DIR

Can I get the absolute path to the current script in KornShell?

Is it possible to find out the full path to the script that is currently executing in KornShell (ksh)?
i.e. if my script is in /opt/scripts/myscript.ksh, can I programmatically inside that script discover /opt/scripts/myscript.ksh ?
Thanks,
You could use:
## __SCRIPTNAME - name of the script without the path
##
typeset -r __SCRIPTNAME="${0##*/}"
## __SCRIPTDIR - path of the script (as entered by the user!)
##
__SCRIPTDIR="${0%/*}"
## __REAL_SCRIPTDIR - path of the script (real path, maybe a link)
##
__REAL_SCRIPTDIR=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )
In korn shell, all of these $0 solutions fail if you are sourcing in the script in question. The correct way to get what you want is to use $_
$ cat bar
echo dollar under is $_
echo dollar zero is $0
$ ./bar
dollar under is ./bar
dollar zero is ./bar
$ . ./bar
dollar under is bar
dollar zero is -ksh
Notice the last line there? Use $_. At least in Korn. YMMV in bash, csh, et al..
Well it took me a while but this one is so simple it screams.
_SCRIPTDIR=$(cd $(dirname $0);echo $PWD)
since the CD operates in the spawned shell with $() it doesn't affect the current script.
How the script was called is stored in the variable $0. You can use readlink to get the absolute file name:
readlink -f "$0"
The variable $RPATH contains the relative path to the real file or the real path for a real file.
CURPATH=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )
CURLOC=$CURPATH/`basename $0`
if [ `ls -dl $CURLOC |grep -c "^l" 2>/dev/null` -ne 0 ];then
ROFFSET=`ls -ld $CURLOC|cut -d ">" -f2 2>/dev/null`
RPATH=`ls -ld $CURLOC/$ROFFSET 2>/dev/null`
else
RPATH=$CURLOC
fi
echo $RPATH
This is what I did:
if [[ $0 != "/"* ]]; then
DIR=`pwd`/`dirname $0`
else
DIR=`dirname $0`
fi
readlink -f would be the best if it was portable, because it resolves every links found for both directories and files.
On mac os x there is no readlink -f (except maybe via macports), so you can only use readlink to get the destination of a specific symbolic link file.
The $(cd -P ... pwd -P) technique is nice but only works to resolve links for directories leading to the script, it doesn't work if the script itself is a symlink
Also, one case that wasn't mentioned : when you launch a script by passing it as an argument to a shell (/bin/sh /path/to/myscript.sh), $0 is not usable in this case
I took a look to mysql "binaries", many of them are actually shell scripts ; and now i understand why they ask for a --basedir option or need to be launched from a specific working directory ; this is because there is no good solution to locate the targeted script
This works also, although it won't give the "true" path if it's a link. It's simpler, but less exact.
SCRIPT_PATH="$(whence ${0})"
Try which command.
which scriptname
will give you the full qualified name of the script along with its absolute path
I upgraded the Edward Staudt's answer, to be able to deal with absolute-path symbolic links, and with chains of links too.
DZERO=$0
while true; do
echo "Trying to find real dir for script $DZERO"
CPATH=$( cd -P -- "$(dirname -- "$(command -v -- "$DZERO")")" && pwd -P )
CFILE=$CPATH/`basename $DZERO`
if [ `ls -dl $CFILE | grep -c "^l" 2>/dev/null` -eq 0 ];then
break
fi
LNKTO=`ls -ld $CFILE | cut -d ">" -f2 | tr -d " " 2>/dev/null`
DZERO=`cd $CPATH ; command -v $LNKTO`
done
Ugly, but works...
After run this, the path is $CPATH and the file is $CFILE
Try using this:
dir = $(dirname $0)
Using $_ provides the last command.
>source my_script
Works if I issue the command twice:
>source my_script
>source my_script
If I use a different sequence of commands:
>who
>source my_script
The $_ variable returns "who"

Resources