bash scripting - what does "ls .." (ls with double dot) do? - bash

My homework asked me to find what ls .. does.
I tried to search the internet but I couldn't find any answer to the question.
Does someone know what this command does?

.. is an entry in the current directory that refers to the parent of the current directory. It's not a special convention used just by ls. For example:
$ cd /usr/bin
$ pwd
/usr/bin
$ cd ..
$ pwd
/usr
$ [ -d .. ] && echo "It's a directory"
It's a directory
$ stat ..
<output specific to your installed version of stat>

Related

Cannot create directory with tilde

Given this script
# cat foo.sh
echo $HOME
set -x
mkdir ~
I am getting this result
# ./foo.sh
/home/Steven Penny
++ mkdir ''
mkdir: cannot create directory `': No such file or directory
If I run mkdir "$HOME" I have no problem. I think the issue is the space in the path, but can someone shed some light on this?
Looking at your debug output
++ mkdir ''
looks like "~" in mkdir ~ is expanding to nothing, thus causing mkdir to fail. As mkdir $HOME works fine it means you have enough space in the disk.
Can you check if echo ~ is displaying your home directory.
The problem was that the /etc/passwd file was missing.
To elaborate, even one this simple will work
Steven Penny::0:0::/home/Steven Penny

Bash find where the current execution is located

I have a shell program in a directory (ie dir1/dothis.sh) - works fine when I cd to that directory and ./dothis.sh
if I created a ln to that directoy with a new name - dir2 and do dir2/dothis.sh
it would execute but it thinks the current execution path is the new dir where dir2 is pointing to at
in dothis.sh - how do I find where dothis.sh actually located? The problem I have is that the dir1/dothis.sh can be relocated from system to system so there is no warranty where dir1/dothis.sh can be hard code
Use the bash built-in
#!/bin/bash
echo "Current path: $PWD"
try this:
#!/bin/bash
echo $0
a=`pwd`
echo $a
b=$a"/"$0
echo `dirname $b`
How about
dirname $(readlink -f $0)
It will also resolve symbolic link if any...

How can I return to the previous working directory quickly in Bash?

When I change into a directory with the cd command, I lose the previous working directory, unless I remember it in my memory. Is there some handy method to go back quickly?
Demo:
$ cd ~/some_path
$ cd /another_path
$ command_to_go_back_to_some_path
You can go back to the last dir with cd -
You can also do this
$ pushd ~/some_path
$ pushd /another_path
$ popd
$ popd
As mentioned you can use cd -. The shell internally does a cd $OLDPWD.
For usage in a script, you could use the OLDPWD shell variable: it contains the previous working directory.
$ pwd
/home/username
$ cd /usr/bin
$ pwd
/usr/bin
$ cd "$OLDPWD"
$ pwd
/home/username
I prefer this over cd - in scripts because I don't have to suppress any output.
If you want to use it in a script and suppress the output, do this:
cd - > /dev/null

bash - cd command not working?

I've somehow managed to screw up bash while fiddling with the $PATH variable in my bash_profile (I think...). All I did, as far as I can remember, was add a directory to the $PATH variable. Please HELP!
Here's what I get when I cd into various directories
my-MacBook-Pro:~ myuser$ cd .rvm
-bash: dirname: command not found
-bash: find: command not found
my-MacBook-Pro:.rvm myuser$ cd
-bash: find: command not found
And here's what happens when I try to get into my .bash_profile to undo whatever it is that I did...
my-MacBook-Pro:~ myuser$ emacs .bash_profile
-bash: emacs: command not found
my-MacBook-Pro:~ myuser$ sudo emacs .bash_profile
-bash: sudo: command not found
Any help would be massively appreciated. I'm completely screwed until I can get bash working normally again!
/usr/bin/emacs .bash_profile or similar should work when the PATH is broken.
The $PATH variable tells the shell where to look for commands. If you just bypass that by telling it the full path, it should work. Try /usr/bin/emacs .bash_profile.
When you do a cd, you're getting a bunch of other things. Since you're using BASH there are are two possible issues:
You have PROMPT_COMMAND defined. Try to undefining it:
$ unset PROMPT_COMMAND
There's an alias of the cd command: This was quite common in Kornshell where you don't have the nice backslashed characters you could put into your prompt string. If you wanted your prompt to have the name of your directory in it.
You had to do something like this:
function _cd
{
logname="$(logname)"
hostname="$(hostname)"
directory="$1"
pattern="$2"
if [ "$pattern" ] #This is a substitution!
then
\cd "$directory" "$pattern"
elif [ "$directory" ]
then
\cd "$directory"
else
\cd
fi
directory=$PWD
shortName=${directory#$HOME}
if [ "$shortName" = "" ]
then
prompt="~$logname"
elif [ "$shortName" = "$directory" ]
then
prompt="$directory"
else
prompt="~$shortName"
fi
title="$logname#$hostname:$prompt"
PS1="$title
$ "
}
alias cd="_cd"
Ugly isn't it? You don't have to go through all of that for BASH, but this does work in BASH too, and I've seen places where this was done either out of ignorance of inertia.
Try this:
$ type cd
You'll either get
$type cd
cd is a shell builtin
or you'll get
$ type cd
cd is an alias for ....
As for your updating of $PATH, you probably forgot to put $PATH back in the new definition, or quotation marks because someone has a directory name with a space in it. Your PATH setting should look like this:
PATH="/my/directory:$PATH"
Some people say it should be:
PATH="$PATH:/my/directory"
I guess, that you have defined $PROMPT_COMMAND (maybe in .bashrc) in a way that uses dirname and find.
That would explain the behavior of cd.
The find command is by default in /usr/bin/find. Thus, you can use it to find the locations of your imprtant commands and reconstruct you path information.

How can I set the current working directory to the directory of the script in Bash?

I'm writing a Bash script. I need the current working directory to always be the directory that the script is located in.
The default behavior is that the current working directory in the script is that of the shell from which I run it, but I do not want this behavior.
#!/bin/bash
cd "$(dirname "$0")"
The following also works:
cd "${0%/*}"
The syntax is thoroughly described in this StackOverflow answer.
Try the following simple one-liners:
For all UNIX/OSX/Linux
dir="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"
Bash
dir="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
Note: A double dash (--) is used in commands to signify the end of command options, so files containing dashes or other special characters won't break the command.
Note: In Bash, use ${BASH_SOURCE[0]} in favor of $0, otherwise the path can break when sourcing it (source/.).
*For Linux, Mac and other BSD:
cd "$(dirname "$(realpath -- "$0")")";
Note: realpath should be installed in the most popular Linux distribution by default (like Ubuntu), but in some it can be missing, so you have to install it.
Note: If you're using Bash, use ${BASH_SOURCE[0]} in favor of $0, otherwise the path can break when sourcing it (source/.).
Otherwise you could try something like that (it will use the first existing tool):
cd "$(dirname "$(readlink -f -- "$0" || realpath -- "$0")")"
For Linux specific:
cd "$(dirname "$(readlink -f -- "$0")")"
*Using GNU readlink on BSD/Mac:
cd "$(dirname "$(greadlink -f -- "$0")")"
Note: You need to have coreutils installed
(e.g. 1. Install Homebrew, 2. brew install coreutils).
In bash
In bash you can use Parameter Expansions to achieve that, like:
cd "${0%/*}"
but it doesn't work if the script is run from the same directory.
Alternatively you can define the following function in bash:
realpath () {
[[ "$1" = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}
This function takes 1 argument. If argument has already absolute path, print it as it is, otherwise print $PWD variable + filename argument (without ./ prefix).
or here is the version taken from Debian .bashrc file:
function realpath()
{
f=$#
if [ -d "$f" ]; then
base=""
dir="$f"
else
base="/$(basename -- "$f")"
dir="$(dirname -- "$f")"
fi
dir="$(cd -- "$dir" && /bin/pwd)"
echo "$dir$base"
}
Related:
How to detect the current directory in which I run my shell script?
How do I get the directory where a Bash script is located from within the script itself?
Bash script absolute path with OS X
Reliable way for a Bash script to get the full path to itself
See also:
How can I get the behavior of GNU's readlink -f on a Mac?
cd "$(dirname "${BASH_SOURCE[0]}")"
It's easy. It works.
The accepted answer works well for scripts that have not been symlinked elsewhere, such as into $PATH.
#!/bin/bash
cd "$(dirname "$0")"
However if the script is run via a symlink,
ln -sv ~/project/script.sh ~/bin/;
~/bin/script.sh
This will cd into the ~/bin/ directory and not the ~/project/ directory, which will probably break your script if the purpose of the cd is to include dependencies relative to ~/project/
The symlink safe answer is below:
#!/bin/bash
cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" # cd current directory
readlink -f is required to resolve the absolute path of the potentially symlinked file.
The quotes are required to support filepaths that could potentially contain whitespace (bad practice, but its not safe to assume this won't be the case)
This script seems to work for me:
#!/bin/bash
mypath=`realpath $0`
cd `dirname $mypath`
pwd
The pwd command line echoes the location of the script as the current working directory no matter where I run it from.
There are a lot of correct answers in here, but one that tends to be more useful for me (making sure a script's relative paths remain predictable/work) is to use pushd/popd:
pushd "$(dirname ${BASH_SOURCE:0})"
trap popd EXIT
# ./xyz, etc...
This will push the source file's directory on to a navigation stack, thereby changing the working directory, but then, when the script exits (for whatever reason, including failure), the trap will run popd, restoring the current working directory before it was executed. If the script were to cd and then fail, your terminal could be left in an unpredictable state after the execution ends - the trap prevents this.
I take this and it works.
#!/bin/bash
cd "$(dirname "$0")"
CUR_DIR=$(pwd)
Get the real path to your script
if [ -L $0 ] ; then
ME=$(readlink $0)
else
ME=$0
fi
DIR=$(dirname $ME)
(This is answer to the same my question here: Get the name of the directory where a script is executed)
cd "`dirname $(readlink -f ${0})`"
Most answers either don't handle files which are symlinked via a relative path, aren't one-liners or don't handle BSD (Mac). A solution which does all three is:
HERE=$(cd "$(dirname "$BASH_SOURCE")"; cd -P "$(dirname "$(readlink "$BASH_SOURCE" || echo .)")"; pwd)
First, cd to bash's conception of the script's directory. Then readlink the file to see if it is a symlink (relative or otherwise), and if so, cd to that directory. If not, cd to the current directory (necessary to keep things a one-liner). Then echo the current directory via pwd.
You could add -- to the arguments of cd and readlink to avoid issues of directories named like options, but I don't bother for most purposes.
You can see the full explanation with illustrations here:
https://www.binaryphile.com/bash/2020/01/12/determining-the-location-of-your-script-in-bash.html
echo $PWD
PWD is an environment variable.
If you just need to print present working directory then you can follow this.
$ vim test
#!/bin/bash
pwd
:wq to save the test file.
Give execute permission:
chmod u+x test
Then execute the script by ./test then you can see the present working directory.

Resources