5333 private links
[[ is bash's improvement to the [ command. It has several enhancements that make it a better choice if you write scripts that target bash. My favorites are:
It is a syntactical feature of the shell, so it has some special behavior that [ doesn't have. You no longer have to quote variables like mad because [[ handles empty strings and strings with whitespace more intuitively. For example, with [ you have to write
if [ -f "$file" ]
to correctly handle empty strings or file names with spaces in them. With [[ the quotes are unnecessary:
if [[ -f $file ]]
Because it is a syntactical feature, it lets you use && and || operators for boolean tests and < and > for string comparisons.
(a) Braces ({}) are used to unambiguously identify variables. Example:
$ VARIABLE=abcdef
$ echo Variable: $VARIABLE
Variable: abcdef
$ echo Variable: $VARIABLE123456
Variable:
$ echo Variable: ${VARIABLE}123456
Variable: abcdef123456
To combine stderr
and stdout
into the stdout
stream, we append this to a command:
2>&1
e.g. to see the first few errors from compiling g++ main.cpp
:
g++ main.cpp 2>&1 | head
What does 2>&1
mean, in detail?
File descriptor 1 is the standard output (stdout
).
File descriptor 2 is the standard error (stderr
).
At first, 2>1
may look like a good way to redirect stderr
to stdout
. However, it will actually be interpreted as "redirect stderr
to a file named 1
".
&
indicates that what follows and precedes is a file descriptor, and not a filename. Thus, we use 2>&1
. Consider >&
to be a redirect merger operator.
find . -iname "foo*" | while read f
do
# ... loop body
done
Alternate:
$ for x in *; do echo "file: '${x}'"; done
or
for x in *
do
echo "file: '${x}'"
done
Many times when writing Shell scripts, you may find yourself in a situation where you need to perform an action based on whether a file exists or not.
In Bash, you can use the test command to check whether a file exists and determine the type of the file.
The test command takes one of the following syntax forms:
test EXPRESSION
[ EXPRESSION ]
[[ EXPRESSION ]]
If you want your script to be portable, you should prefer using the old test [ command, which is available on all POSIX shells. The new upgraded version of the test command [[ (double brackets) is supported on most modern systems using Bash, Zsh, and Ksh as a default shell.
Arguments passed to a script are processed in the same order in which they’re sent. The indexing of the arguments starts at one, and the first argument can be accessed inside the script using $1. Similarly, the second argument can be accessed using $2, and so on. The positional parameter refers to this representation of the arguments using their position. //
Using flags is a common way of passing input to a script. When passing input to the script, there’s a flag (usually a single letter) starting with a hyphen (-) before each argument.
Let’s take a look at the userReg-flags.sh script, which takes three arguments: username (-u), age (-a), and full name (-f).
We’ll modify the earlier script to use flags instead of relying on positional parameters. The getopts function reads the flags in the input, and OPTARG refers to the corresponding values:
To override a non-builtin with a function, use command
. For example:
ls() { command ls -l; }
which is the equivalent of alias ls='ls -l'
.
command works with builtins
as well. So, your cd
could also be written as:
cd() { echo before; command cd "$1"; echo after; }
To bypass a function or an alias and run the original command or builtin, you can put a \
at the beginning:
\ls # bypasses the function and executes /bin/ls directly
rm typically does not delete the targets of symlinks, but to say that it "does not follow symlinks" is not quite accurate, at least on my system (GNU coreutils 8.25). And deleting files is a place where accuracy is pretty important! Let's take a look at how it behaves in a few situations.
If your symlink is to a file, rather than to a directory, there is no plausible way to accidentally delete the target using rm. You would have to do something very explicit like rm "$(readlink file)".
Symlinks to directories, however, get a bit dicey, as you saw when you accidentally deleted one.
These are all safe:
rm test2
(deletes the symlink only)rm -r test2
(deletes the symlink only)rm -rf test2
(deletes the symlink only)rm test2/
(rm: cannot remove 'test2/'
: Is a directory -- no action taken)rm -rf *2
(or any other glob matching the symlink -- deletes the symlink only)
These are not safe:
rm -r test2/
(rm: cannot remove 'test2/'
: Not a directory -- but deletes the contents of thetest1
directory)rm -rf test2/
(deletes the contents of the directory, leaves the symlink, no error)rm -rf test2/*
(deletes the contents of the directory, leaves the symlink, no error)
The last unsafe case is probably obvious behavior, at least to someone well-acquainted with the shell, but the two before it are quite a bit more subtle and dangerous, especially since tab-completing the name of test2
will drop the trailing slash in for you!
It's interesting to note that test
has similar behavior, considering a symlink to a directory with a trailing slash to be not a symlink but a directory, while a symlink without a trailing slash is both:
$# Stores the number of command-line arguments that
were passed to the shell program.
$? Stores the exit value of the last command that was
executed.
$0 Stores the first word of the entered command (the
name of the shell program).
$ Stores all the arguments that were entered on the
command line ($1 $2 ...).
"$@" Stores all the arguments that were entered
on the command line, individually quoted ("$1" "$2" ...).
So basically, $# is a number of arguments given when your script was executed. $ is a string containing all arguments. For example, $1 is the first argument and so on. This is useful, if you want to access a specific argument in your script.
As Brian commented, here is a simple example. If you run following command:
./command -yes -no /home/username
$# = 3
$* = -yes -no /home/username
$@ = array: {"-yes", "-no", "/home/username"}
$0 = ./command, $1 = -yes etc.
The most effective debugging tool is still careful thought, coupled with judiciously placed print statements. – Brian Kernighan, “Unix for Beginners” (1979)
When writing shell scripts, the programming logic tends to be shorter and is often contained within a single file. So there are a few built-in debugging options we can use to see what is going wrong. The first option to mention is probably the most useful too – the xtrace option. This can be applied to a script by invoking Bash with the -x switch.
$ bash -x <scriptname>
first let’s contrast -x with its opposite -v, which shows each line before it is evaluated instead of after.
One of the most commonly used string operations is concatenation. String concatenation is just a fancy programming word for joining strings together by appending one string to the end of another string.
In this tutorial, we will explain how to concatenate strings in Bash.
Concatenating Strings
The simplest way to concatenate two or more string variables is to write them one after another:
VAR1="Hello,"
VAR2=" World"
VAR3="$VAR1$VAR2"
echo "$VAR3"