导语
Shell是UNIX操作系统的命令语言。
它既是用户与UNIX操作系统对话的语言,也可以作为程序设计的语言,所以掌握这项语言是十分有意义的。
本文是我学习shell时记录的笔记。
1
[Chen9@localhost ~]$ 

It tells you that you are on the machine Chen9 and your current working directory is ~ (short for “home”). The $ tells you that you are not the root user.

1
2
[Chen9@localhost ~]$ date
Tue Oct 24 21:54:48 CST 2023

The date prints the current date and time.

1
2
3
4
5
6
7
8
9
[Chen9@localhost ~]$ echo hello
hello

[Chen9@localhost ~]$ echo 'My Photos'
My Photos
[Chen9@localhost ~]$ echo "My Photos"
My Photos
[Chen9@localhost ~]$ echo My\ Photos
My Photos

The echo with the argument can print out its argument.
If you want to provide an argument that contains spaces of other special characters, you can quote the argument with ' or " , or use \ before the space.

1
2
3
4
5
6
[Chen9@localhost ~]$ echo $PATH
/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:/home/Chen9/.local/bin:/home/Chen9/bin
[Chen9@localhost ~]$ which echo
/user/bin/echo
[Chen9@localhost ~]$ /bin/echo $PATH
/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:/home/Chen9/.local/bin:/home/Chen9/bin

When we run a command, the shell will search through the :-separated list of directories in $PATH for a file by that name.
The which program can find out which file is executed for a given program name.
If you give the path to the file we want to execute, it can bypass $PATH.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[Chen9@localhost ~]$ pwd
/home/Chen9
[Chen9@localhost ~]$ cd /home
[Chen9@localhost home]$ pwd
/home
[Chen9@localhost home]$ cd ..
[Chen9@localhost /]$ pwd
/
[Chen9@localhost /]$ cd ./home
[Chen9@localhost home]$ pwd
/home
[Chen9@localhost home]$ cd Chen9
[Chen9@localhost ~]$ pwd
/home/Chen9
[Chen9@localhost ~]$ ../../bin/echo hello
hello

The pwd can show you the current working directory and change with the cd command.
The . refers to the current directory, and .. to its parent directory.

1
2
3
4
5
6
7
8
9
[Chen9@localhost ~]$ ls
Desktop Documents Downloads Music Pictures Public Templates Videos
[Chen9@localhost ~]$ cd ..
[Chen9@localhost home]$ ls
chen9 Chen9
[Chen9@localhost home]$ cd ..
[Chen9@localhost /]$ ls
bin dev home lib64 mnt proc run srv tmp var
boot etc lib media opt root sbin sys usr

The ls will print the contents of the current directory.

1
2
3
4
[Chen9@localhost ~]$ ls -l /home
total 8
drwx------. 15 chen9 chen9 4096 Oct 20 20:14 chen9
drwx------. 15 Chen9 Chen9 4096 Oct 25 10:36 Chen9

The -l will use a long listing format.
The d tells us that chen9(at the end of that line) is a directory.
Then follow three groups of three characters(rwx) indicate what permssion the owner of the file(chen9), the owning group(chen9), and everyone else have. - indicates that the given principal does not have the given permission.
The 4096 means the size of this file.

You can use mv to rename/move a file, cp to copy a file, mkdir to make a new directory.

1
[Chen9@localhost ~]$ man ls

The man program can show you a program’s arguments, inputs, outputs, or how it works in a manual page. Press q to exit.

1
2
3
4
5
6
7
8
9
10
11
12
[Chen9@localhost ~]$ echo hello > hello.txt
[Chen9@localhost ~]$ cat hello.txt
hello
[Chen9@localhost ~]$ cat < hello.txt
hello
[Chen9@localhost ~]$ cat < hello.txt > hello2.txt
[Chen9@localhost ~]$ cat hello2.txt
hello
[Chen9@localhost ~]$ cat < hello.txt >> hello2.txt
[Chen9@localhost ~]$ cat hello2.txt
hello
hello

You can use < file and > file to rewire the input and output streams of a program to a file respectively.
You can also use >> to append to a file.

1
2
[Chen9@localhost ~]$ ls -l / | tail -n1
drwxr-xr-x. 21 root root 4096 Oct 20 20:08 var

The | lets you chain programs such that the output of the former is the input of the latter.

1
2
3
[Chen9@localhost ~]$ find -L /sys/class/backlight -maxdepth 2 -name '*brightness*'
/sys/class/backlight/thinkpad_screen/brightness
[Chen9@localhost ~]$ cd /sys/class/backlight/thinkpad_screen

The sudo can let you do something as “su”(short for super user).
The /sys exposes a number of kernel parameters as files, you can reconfigure the kernel easily.

1
2
[Chen9@localhost ~]$ sudo echo 3 > brightness
An error occured while redirecting file 'brightness' open: Permission denied

Operations like |, >, and < are done by the shell, so the shell(which is authenticated just as your user) tries to open the brightness file for writing will be rejected.
You can work around this:

1
[Chen9@localhost ~]$ echo 3 | sudo tee brightness

The tee will print contents both to file and your terminal.

1
2
3
4
5
[Chen9@localhost ~]$ foo=bar
[Chen9@localhost ~]$ echo "$foo"
bar
[Chen9@localhost ~]$ echo '$foo'
$foo

The " delimiter will substitute variable values, but ' will not.

1
2
3
4
mcd () {
mkdir -p "$1"
cd "$1"
}

An example of a function that create a directory and cds into it.

  • $0 -Name of the script.
  • $1 to $9 -Arguments to the script. $1 is the first argument and so on.
  • $@ -All the arguments.
  • $# -Number of the arguments.
  • $? -[[#^d5548a|Return code]] of the previous command.
  • $$ -Process identification number(PID) for the current script.
  • !! -Entire last command, including arguments. You can re-execute the command with sudo by doing sudo !!.
  • $_ -Last argument from the last command. You can also quickly get this value by typing Esc + . or Alt + .

Return Code’s value of 0 usually means everything went OK; anything different from 0 means an error occurred.

1
2
3
4
5
6
7
8
9
10
[Chen9@localhost ~]$ false || echo "Oops, fail"
Oops, fail
[Chen9@localhost ~]$ true || echo "Will not be printed"
[Chen9@localhost ~]$ true && echo "Things went well"
Things went well
[Chen9@localhost ~]$ false && echo "Will not be printed"
[Chen9@localhost ~]$ true ; echo "This will always run"
This will always run
[Chen9@localhost ~]$ false ; echo "This will always run"
This will always run

The && and || are short-circuiting operators. Command can also be separated within the same line using a semicolon ;. true will have a 0 return code and the false command will have a 1 return code.

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash

echo "Starting program at $(date)"

echo "Running program $0 with $# arguments with pid $$"

for file in "$@"; do
grep foobar "$file" > /dev/null 2> /dev/null
if [[ $? -ne 0 ]]; then
echo "File $file does not have ant foobar, adding one"
echo "# foobar" >> "$file"
fi
done
1
2
3
[Chen9@localhost ~]$ ./scripttest.sh
Starting program at Mon Nov 6 10:45:09 CST 2023
Running program ./scripttest.sh with 0 arguments with pid 3485

command substitution: The $(CMD) command will execute CMD, get the output of the command and substitute it in place.
process substitution: The <(CMD) command will execute CMD and place the output in a temporary file and substitute the <() with that file’s name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
convert image.{png,jpg}
# Will expand to
convert image.png image.jpg

cp /path/to/project/{foo,bar,baz}.sh /newpath
# Will expand to
cp /path/to/project/foo.sh /path/to/project/bar.sh /path/to/project/baz.sh /newpath

# Globbing techniques can also be combined
mv *{.py,.sh} folder
# Will move all *.py and *.sh files


mkdir foo bar
# This creates files foo/a, foo/b, ... foo/h, bar/a, bar/b, ... bar/h
touch {foo,bar}/{a..h}
touch foo/x bar/y
# Show differences between files in foo and bar
diff <(ls foo) <(ls bar)
# Outputs
# < x
# ---
# > y
  • Wildcards - use ? to match one character and * to match any amount of characters.
  • Curly braces - use {} whenever you have a common substring in a series of commands, it can expand this automatically.
1
2
3
4
#!/user/local/bin/python
import sys
for arg in reversed(sys.argv[1:]):
print(arg)

Scripts need not nevessarily be written in bash to be called from the terminal.
A shebang line at the top of the script will let the kernel knows to execute this script with a python interpreter.
The env will make use of the PATH environment variable, so shebang line would look like #!/usr/bin/env python.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Find all directories named src
find . -name src -type d
# Find all python files that have a folder named test in their path
find . -path '*/test/*.py' -type f
# Find all files modified in the last day
find . -mtime -1
# Find all zip files with size in range 500k to 10M
find . -size +500k -size -10M -name '*.tar.gz'
# Find all directories named src
find . -name src -type d
# Find all python files that have a folder named test in their path
find . -path '*/test/*.py' -type f
# Find all files modified in the last day
find . -mtime -1
# Find all zip files with size in range 500k to 10M
find . -size +500k -size -10M -name '*.tar.gz'

The find will recursively search for files matching some criteria.