shebang

wiki

在计算机科学中,Shebang(也称为 Hashbang )是一个由井号和叹号构成的字符序列 #! ,其出现在文本文件的第一行的前两个字符

bash 的配置文件执行顺序

这里自己额外想到的, 所以整理了下网上的资料

Zsh/Bash startup files loading order (.bashrc, .zshrc etc.)

+----------------|-----------|-----------|------+
|                |Interactive|Interactive|Script|
|                |login      |non-login  |      |
+----------------|-----------|-----------|------+
|/etc/profile    |   A       |           |      |
+----------------|-----------|-----------|------+
|/etc/bash.bashrc|           |    A      |      |
+----------------|-----------|-----------|------+
|~/.bashrc       |           |    B      |      |
+----------------|-----------|-----------|------+
|~/.bash_profile |   B1      |           |      |
+----------------|-----------|-----------|------+
|~/.bash_login   |   B2      |           |      |
+----------------|-----------|-----------|------+
|~/.profile      |   B3      |           |      |
+----------------|-----------|-----------|------+
|BASH_ENV        |           |           |  A   |
+----------------|-----------|-----------|------+
|                |           |           |      |
+----------------|-----------|-----------|------+
|                |           |           |      |
+----------------|-----------|-----------|------+
|~/.bash_logout  |    C      |           |      |
+----------------|-----------|-----------|------+

对于 zsh

+----------------|-----------|-----------|------+
|                |Interactive|Interactive|Script|
|                |login      |non-login  |      |
+----------------|-----------|-----------|------+
|/etc/zshenv     |    A      |    A      |  A   |
+----------------|-----------|-----------|------+
|~/.zshenv       |    B      |    B      |  B   |
+----------------|-----------|-----------|------+
|/etc/zprofile   |    C      |           |      |
+----------------|-----------|-----------|------+
|~/.zprofile     |    D      |           |      |
+----------------|-----------|-----------|------+
|/etc/zshrc      |    E      |    C      |      |
+----------------|-----------|-----------|------+
|~/.zshrc        |    F      |    D      |      |
+----------------|-----------|-----------|------+
|/etc/zlogin     |    G      |           |      |
+----------------|-----------|-----------|------+
|~/.zlogin       |    H      |           |      |
+----------------|-----------|-----------|------+
|                |           |           |      |
+----------------|-----------|-----------|------+
|                |           |           |      |
+----------------|-----------|-----------|------+
|~/.zlogout      |    I      |           |      |
+----------------|-----------|-----------|------+
|/etc/zlogout    |    J      |           |      |
+----------------|-----------|-----------|------+

命令分隔

用分号或换行符. 即:

cmd1; cmd2

等同于

cmd1
cmd2

注释

#

# 符号及之后的行内容都是注释

终端打印

echo

  • 双引号的字符串则会进行变量替换
  • 单引号的字符串则不会进行变量替换
  • 默认情况下, 会自动添加换行

-n 参数: 忽略结尾的换行 -e 参数: 表示包含转义序列

彩色转出

文本

颜色码:

  • 0 重置
  • 30 黑色
  • 31 红色
  • 37 白色 等等

例如

echo "\e[1;31m Hello World\e[0m"

背景

颜色码:

  • 0 重置
  • 40 黑色
  • 41 红色
  • 47 白色 等等
echo "\e[1;42m Hello World\e[0m"

printf

与 C 语言中的 printf 函数参数一样.

  • 默认情况下, 不会自动添加换行

变量

在 Bash 中, 每一个变量的值都是字符串, 无论你给变量赋值时有没有使用引号, 值都会以字符串的形式存储.

环境变量

有一些特殊的变量会被Shell环境和操作系统用来存储一些特别的值, 这类变量就称为 环境变量

终端环境变量

使用 env 命令查看.

进行的环境变量

cat /proc/$PID/environ

上面返回的是变量的列表, 以 name=value 的形式来描述. 之间由 \0 分隔. 可将 \0 替换为 \n 就可使每一行显示了.

cat /proc/$PID/environ | tr '\0' '\n'

变量赋值

var=value

注意, 它不同于

var = value

前者才是赋值, 后者是相等操作.

引用变量的值

$var

${var}

注意, 如果包含在单引号里, 变量不会被扩展. 将依照原样显示.

获取变量的长度

length=${#var}

获取当前的SHELL

echo $0

或

echo $SHELL

检查是否 root 用户

if [ $UID -ne 0 ]; then
	echo "Non root user"
else
	echo "root user"
if

修改提示符字符串

在文件 ~/.bashrc 中的某一行的 PS1 变量中设置的.

Bash 中进行数学运算

let

使用let时, 变量名之前不需要添加 $ , 例如:

no1=4
no2=5

let result=no1+no2

echo $result

自加和自减,缩写

let no1++
let no1--

let no1+=6
let no1-=6

使用 []

result=$[ no1 + no2 ]

或

result=$[ $no1 + 5 ]

使用 (())

这种变量名之前要加上 $

result=$(( no1 + 50 ))

使用 expr

result=`expr 3 + 4`

result=$(expr $no1 + 5)

浮点运算 bc

echo "4 * 0.45" | bc

设置精度

echo "scale=2; 3/8" | bc

进制转换

echo "obase=2; 1000" | bc

obase : 输出进制 ibase : 输入进制

文件描述符

文件描述符是一种用于访问文件的抽象指示器. 存取文件离不开被称为 文件描述符 的特殊数字.

0 : 标准输入 stdin /dev/stdin
1 : 标准输出 stdout /dev/stdout
2 : 标准错误 stderr /dev/stderr

自定义文件描述符

文件打开模式通常有3种

  1. 只读
  2. 截断写入
  3. 追加写入
< : 从文件中读取内容到 stdin
> : 用于截断模式写入
>> : 用于追加模式写入

读取

创建一个文件描述符进行读取:

exec 3<input.txt

这表示使用文件描述符3打开并读取文件.

使用文件描述符

cat <&3

注意, 只能读取一次, 若要再次读取, 则要重新使用 exec 来分配文件描述符进行二次读取.

写入

# 创建
exec 4>output.txt

# 使用
echo "ehllo" >&4

追加

exec 5>>output.txt

echo "hello" >&5

注意, 是使用 >&5

重定向

> : 旧内容会被清空, 然后设置为新的内容. 这等同于 1>

>> : 追加到旧内容尾部. 这等同于 1>>


< : 用于从文件中读取内容到 stdin

stderrstdout 分别重定向到一个文件:

cmd 2>stderr.txt 1>stdout.txt

stderrstdout 一起重定向:

cmd 2>&1 output.txt

忽略错误输出:

cmd 2>/dev/null 1>output.txt

同时重定向到文件和 stdout

command | tee FILE2 FILE2

注意, tee 只能读取 stdout 而不包括 stderr

使用 stdin 作为命令行参数:

cmd 1 | cmd2 | cmd3 -

- 作为命令的文件名参数即可.

多行文件重定向

cat << EOF > log.txt
这里是内容
EOF

理解: stackoverflow.com

这表示, 指示 shell 直接从当前源中读取输入, 直到有一行的内容仅包含 EOF 这个单词.

命令的成功与否

echo $?

如果返回0, 表示上一个命令执行成功, 否则是失败.

数组

起始索引为0.

定义

array_var=(1 2 3 4 5 6)

使用

echo ${array_var[0]}

打印数组所有值

echo ${array_var[*]}

或

echo ${array_var[@]}

长度

echo ${#array_var[*]}

关联数组

定义

declare -A ass_array

ass_array=([key1]=value [key2]=value2)

或
ass_array[key1]=value1
ass_array[key2]=value2

使用

echo ${ass_array[key]}

列出数组索引

echo ${! ass_array[*]}

或

echo ${! ass_array[@]}

别名

alias new_command='command sequence'

永久保存: 将内容写到 ~/.bashrc

删除别名

alias new_command=
unalias new_command

日期和时间

纪元时或 Unix 时间: UTC 1970年1月1日 0时0分0秒起所流逝的秒数.

date

打印纪元时

date +%s

从指定时间转换为纪元

date --date "Thu Nov 18 08:07:21 IST 2010" +%s

--date 用于提供日期字符串作为输入.

获取星期几

date --date "Jan 20 2001" +%A

格式化日期

格式化占位符列表

date "+%d %B %Y"

设置日期和时间

date -s "格式化的日期字符串"

调试脚本

bash -x script.sh

在脚本内部, 进行部分调试

set -x
要调试的代码段
set +x

-x : 执行时显示命令和参数 +x : 禁止调试 -v : 当命令进行读取时显示输入 +v : 禁止打印输入

函数

定义

function fname() {
	statements;
}

或

fname() {
	statements;
}

执行

fname;

传递参数

fname arg1 arg2;

访问参数

function fname(){
	echo $1,  $2 # 访问参数1, 参数2
    echo "$@" # 以列表的方式一次性打印所有参数
    echo "$*"; # 类似 $@, 但是参数被作为单个实体
    return 0; # 返回值
}

  • $1 是第一个参数
  • $2 是第二个参数
  • $n 是第n个参数
  • $@ 被扩展为”$1” “$2” “$3” 等
  • $* 被扩展成 “$1c$2c$3”, 其中c是IFS的第一个字符
  • $@ 用的比 $* 多, 因为 $* 将所有参数当作单个字符串, 因此它很少被使用

递归

bash 支持递归调用

导出函数

函数也可以像环境变量一样用 export 导出, 如此一来, 函数的作用域就可以扩展到子进程中. 例如

export -f fname

读取命令返回值(状态)

cmd;
echo $?

$? 会给出命令 cmd 的返回值. 这个返回值被称为退出状态.

命令输出读入到变量

# 这种方法称为子shell
cmd_output=$(COMMANDS)

或
# 这种方法称为 反引用
cmd_output=`COMMANDS`

子shell

pwd;
(cd /bin ; ls);
pwd;

第二行中的语句即为子shell, 它不会对当前shell有任何影响.所有改变仅限于子shell内.

保留空格和换行

利用子shell或反引用的方法将命令的输出读入到一个变量中, 可以将它放在双引号中, 即可保留空格和换行符.

out=$(cat file.txt)

out1="$(cat file.txt)"

注, 像好这个没有效果. 我在 Ubuntu 16.04.2 LTS , GNU bash, version 4.3.46(1)-release (x86_64-pc-linux-gnu) 版本中测试没有效果. 有待处理.

read 命令

读取 N 个字符到变量

read -n N var_name

用无回显的方式读取密码

read -s var

显示提示信息

read -p "enter input:" var

在特定时限内读取

read -t timeout var

单位秒

用特定的定界符作为输入行的结束

read -d delim_char var

true 的命令

:

它是内建命令, 总是会返回为0的退出码.

字段分隔符 IFS

IFS 是存储定界符的环境变量, 它是当前 shell 环境使用的默认定界字符串.

它的值, 默认为 空白字符

#!/bin/bash

data="1,2,3,4,5"

oldIFS=$IFS

IFS=,

for item in $data;
do
  echo Item: $item
done

IFS=$oldIFS

输出

./hello.sh
Item: 1
Item: 2
Item: 3
Item: 4
Item: 5

for 循环

for var in list;
do

	commands;
done    

list 可以是字符串, 也可以是一个序列.

序列

{1..50}

表示从1到50的数字列表.

{a..z}

for 的 c 风格

for ((i=0;i<10;i+)) {
	commands;
}

while 循环

while condition
do

	commands;
done    

until 循环

until condition;
do
	commands;
done    

if


if condition;
then
	commands;
fi    

if .. else if .. else

if condition;
then
	commands;
else if condition; then
	commands;
else
	commands;
fi

算术比较

[ $var -eq 0 ]

注意, 中括号与操作数之间要有空白.

  • -gt : 大于
  • -lt : 小于
  • -ge : 大于等于
  • -le : 小于等于

组合

[ $var -ne 0 -a $var2 -gt 2 ] # 表示逻辑与, -a

[ $var -ne 0 -o $var2 -gt 2 ] # 表示逻辑或, -o

文件系统测试

[ -f $file_var ] # 如果是正常的文件路或文件名

[ -x $var ] # 如果给定的变量包含的文件可执行, 则返回真

[ -d $var ] # 如果是目录

[ -e $var ] # 如果文件存在

[ -c $var ] # 如果是字符设备

[ -b $var ] # 如果是块设备

[ -w $var ] # 如果文件可写

[ -r $var ] # 如果文件可读

[ -L $var ] # 如果是符号链接

字符串比较

最好使用双中括号

[[ $str1 = $str2 ]] # 如果相等

[[ $str1 == $str2 ]] # 如果相等

[[ $str1 != $str2 ]] # 如果不相等

[[ $str > $str2 ]] # 大于

[[ $str < $str2 ]] # 小于

[[ -z $str1 ]] # 如果是空字符串

[[ -n $str1 ]] # 如果是非空字符串

组合

[[ -n $str1 ]] && [[ -z $str2 ]]

test 命令

if [ $var -eq 0 ]; then
	echo "True";
fi    

与可以写为

if test $var -eq 0; then
	echo "True";
fi

cat

  • -s : 压缩多行空行为一行空行
  • -T : 显示制表符
  • -n : 显示行号
  • -b : 显示行号, 但不为空白行设置行号

find 命令

find base_path -print0
  • -print0 表示以 ‘\0’ 作为文件名的界定符. 默认为换行符, 但当文件名包含换行符时, 这就有用了. 注意, 是连起来的 -print0
  • -name “*.txt” 正则匹配.
  • -iname “*.txt” 忽略大小写

OR

find . \( -name "*.txt" -o -name "*.pdf" \) -print

否定

find . ! -name "*.txt" -print

表示匹配所有不以 *.txt 结尾的文件名.

基于目录深度

默认情况下, find 会遍历所有子目录.可以添加选项来限定深度.

  • -maxdepth
  • -mindepth

这两个参数应该是 find 的第三个参数, 否则可能会影响效率.

根据文件类型搜索

  • -type d 目录类型
  • -type f 文件类型
  • -type l 符号链接类型
  • -type c 字符设备
  • -type b 块设备
  • -type s 套接字

文件时间

  • -atime : 用户最近一次访问时间
  • -mtime : 文件内容最后一次修改时间
  • -ctime : 文件元数据最后一次改变的时间

后接 +N 或 -N 单位是天. 负号表示是小于, 正号表示大于

  • -amin : 访问时间
  • -mmin : 修改时间
  • -cmin : 变化时间

这些同上, 只是时间单位为分.

文件大小

  • -size +2K : 表示大于 2KB 的文件
  • -size -2K : 表示小于 2KB 的文件

单位

  • b : 512 字节
  • c : 字节
  • w : 2字
  • k : 1024 字节
  • M : 1024 K 字节
  • G : 1024 M 字节

删除匹配

  • -delete

基于权限

  • -perm 664

动作

  • -exec
find . -type f -exec cat {} \;> outout.txt

多命令

exec 不支持执行多命令, 但可以变换一个, 将命令写入脚本, 然后调用

-exec ./commands.sh {} \;

tr 命令

转换源.

映射

tr [options] set1 set2

删除字符

tr -d '[set1]'

压缩字符

tr -s ' '

将多个空白压缩为一个.

校验和

md5sum filename

sh1sum filename

加解密

crypt

它是一个简单的加密工具, 从 stdin 接受一个文件以及口令作为输入, 然后将加密数据输出到 stdout

crypt < input_file > output_file

解密

crypt -d < encrypted_file > output_file

gpg

加密

gpg -c filename

解密

gpg filename.gpg

base64

base64 filename > outputfile

解码

base64 -d file > outputfile

临时文件

filename=`mktemp`

dirname=`mktemp -d`

仅生成文件名, 而不是实际的文件

tempfile=`mktemp -u`

根据模板创建

mktemp test.XXX

它会生成 test.XXX 形式的临时文件.

提取文件名和扩展名

file_jpg="sample.jpg"

fileName=${file_jpg%.*}
suffixName=${file_jpg#*.}

注意, 单个 % 是非贪婪操作, 想要贪婪式的, 则使用 %% 类似的, 还有 # 是非贪婪的, 而 ## 是贪婪的.

并行操作

在循环里的命令, 使用 & 时, 它就会直接向下再执行了, 而不会等待命令执行完成.这样子就可以利用多核

#!/bin/bash

PIDARRAY=()

for file in File1.iso File2.iso
do
	md5sum $file &
    PIDARRAY+=("$!")
done

wait ${PIDARRAY[@]}

注意, 还要用 wait 来等待多进程的退出.

dd 命令

dd if=/dev/zero of=data.file bs=1M count1

不可删除的文件

chattr +i file

删除的时候, 它会提示

rm: remove write-protected regular empty file 'file'?

恢复为可删除

chattr -i file

diff 与文件补丁 patch

diff -u version1.txt version2.txt > version.patch

patch 命令可将修改应用于任意文件. 当应用于 version1.txt 时, 就可以得到 version2.txt , 当应用于 version2.txt 时, 就可以得到 version1.txt

patch -p1 version1.txt < version.patch

取消修改

patch -p1 version1.txt < version.patch

递归的形式来处理目录及文件

diff -Naur dir2 dir2

快速定位目录

pushd 压入

popd 弹出

dirs 查看栈的内容

cd - 返回上一次的目录

它是一个处理LIFO的数据结构. 目录被存储在栈中, 然后 push 和 pop 进行切换.

wc 命令

wc -l file # 统计文件 file 的行数
wc -w file # 单词数
wc -c file # 字符数
wc file # 行数, 单词数, 字符数
wc file -L # 最长一行的长度

正则

可视化正则

| 正则   | 描述                            |
|--------|---------------------------------|
| ^      | 行起始标记                      |
| $      | 行结尾标记                      |
| .      | 任意一个字符                    |
| []     | 在 [] 中的任意一个字符          |
| [^]    | 在 [^] 之外的任意一个字符       |
| [-]    | 在 [-] 指定范围内的任意一个字符 |
| ?      | 匹配之前的项1次或0次            |
| +      | 匹配之前的项1次或多次           |
| *      | 匹配之前的项0次或多次           |
| ()     | 创建一个用于匹配的子串          |
| {n}    | 匹配之前的项n次                 |
| {n, }  | 匹配之前项至少n次               |
| {n, m} | 匹配之前项n到m次                |
| \      | 转义                            |
| 一竖     | 匹配两边任意一项                        |

cut 命令

按列切分

提取指定列

cut -f 2,3 filename

这列表只显示第2,3列

排除指定列

cut -f3 --complement filename

这表示除第3列之外的所有列.

指定界定符

cut -d ";" filename

指定字段的字符或字节范围

cut -c1-5 filename

表示只打印前1~5个字符.

sed 命令

stream editor

替换字符串

sed 's/pattern/replace_string/' file

应用到文件

sed -i 's/pattern/replace_string/' file

全局应用

默认情况下, sed 将在每一行的第一处符合的内容替换. 要全部则:

sed 's/pattern/replace_string/g' file

只在第N处开始替换

sed 's/pattern/replace_string/2g' file

界定符

除了 / 还可以:

sed 's:text:replace:g'

sed 's|text|replace|g'

移除空白行

sed '/^$/d' file

awk

awk '{BEGIN {print "start"} pattern {commands} END {print "end"}}' file

处理流程:

  1. 执行 BEGIN {commands} 语句块中的语句
  2. 从文件或stdin 中读取一行, 然后执行 pattern {commands} , 重复这个过程, 直接文件全部被读取完.
  3. 当读至输入流末尾时, 执行 END {commands} 语句块.

特殊变量

  • NR : 当前行号
  • NF : 当前行的字段数
  • $0 : 当前行的内容
  • $1 : 第一个字段的内容
  • $2 : 第二个字段的内容

将外部变量传递给 awk

awk -v VAR=$OUT_VAR '{print VAR}'

设置字段界定符

awk -F: '{print $1}'

这样子, 就将字符界定符设置为 : 了.

内建函数

gnu awk

length(string) : 返回字符串的长度

paste 命令:列拼接

cat 是按行拼接的.

paste 则是按列来拼接的.

tac

逆序显示. cat 的逆单词.

wget

命令行下载工具

指定多个下载

wget URL1 URL2

指定输出文件

wget URL1 -O outputfile -o logfile

重试次数

wget -t 5 URL1

为0时, 表示无限重试.

限速

wget --limit-rate 20k URL

最大下载配额

wget -Q 100m URL

表示最大下载 100m 大小, 超过时就会停止.

断点续传

wget -c URL

复制整个网站

wget --mirror --covert-links URL

curl 命令

避免显示进度

curl --silent URL

下载

curl URL --silent -O

# 型进度条

curl URL --progress

断点续传

curl -C - URL

-C - 表示让 curl 自动计算.

设置 referer

curl --referer referer_url URL

doc

curl --cookie "k=v;k1=v1"
curl URL --cookie-jar cookie_file
或简写
curl URL -c cookie_file

请求时, 带上cookie文件中的cookie

curl URL -b cookie_file

限制带宽

curl URL --limit-rate 20k

只打印头部

curl -I URL

tar 命令

打包并压缩
tar -cvjf file.tar.bz2 file1 fiel2
tar -cvzf file.tar.gz file1 file2

解压
tar -xvjf file.tar.bz2
tar -xvzf file.tar.gz

rsync 命令

rsync -avzh source_path dest_path

rsync -avzh -e "ssh -p 22" remote_path dest_path

排除

rsync --exclude "*.txt"

同步时在dest_path中删除源端不存在的文件

rsync --delete

fsarchiver 命令

制作全盘镜像

创建

fsarchiver savefs backup.fsa /dev/sda1

多个分区同时备份
fsarchiver savefs backup.fsa /dev/sda1 /dev/sda2

恢复

fsarchiver restfs backup.fsa id=0,dest=/dev/sda1 id=1,dest=/dev/sdb2

脚本中使用 EOF 的 raw string

IFS='' read -r -d '' String <<"EOF"
<?xml version="1.0" encoding='UTF-8'?>
 <painting>
   <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
   <caption>This is Raphael's "Foligno" Madonna, painted in
   <date>1511</date>-<date>1512</date>.</caption>
 </painting>
EOF