命令行的艺术

BASH

  • ${#varname}
  • ${varname:offset:length}
  • ${variable#pattern}
  • ${variable##pattern}
  • ${path##*/}
  • ${variable/#pattern/string}
  • ${file%.png}.jpg
  • ${varname^^}
  • ${varname,,}

shell

  • $0 脚本文件名
  • $1 对应脚本的第一个参数
  • $# 参数的总数
  • $@ 全部的参数
  • read
1
2
3
4
5
6
filename='/etc/hosts'

while read myline
do
  echo "$myline"
done < $filename
  • IFS
1
2
3
4
5
input="/path/to/txt/file"
while IFS= read -r line
do
  echo "$line"
done < "$input"

上面的命令可以逐行读取文件,每一行存入变量line,打印出来以后再读取下一行。

  • test
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 写法一
if test -e /tmp/foo.txt ; then
  echo "Found foo.txt"
fi

# 写法二
if [ -e /tmp/foo.txt ] ; then
  echo "Found foo.txt"
fi

# 写法三
if [[ -e /tmp/foo.txt ]] ; then
  echo "Found foo.txt"
fi
  • 文件判断
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[ -a file ]:如果 file 存在,则为true。
[ -b file ]:如果 file 存在并且是一个块(设备)文件,则为true。
[ -c file ]:如果 file 存在并且是一个字符(设备)文件,则为true。
[ -d file ]:如果 file 存在并且是一个目录,则为true。
[ -e file ]:如果 file 存在,则为true。
[ -f file ]:如果 file 存在并且是一个普通文件,则为true。
[ -g file ]:如果 file 存在并且设置了组 ID,则为true。
[ -G file ]:如果 file 存在并且属于有效的组 ID,则为true。
[ -h file ]:如果 file 存在并且是符号链接,则为true。
[ -k file ]:如果 file 存在并且设置了它的“sticky bit”,则为true。
[ -L file ]:如果 file 存在并且是一个符号链接,则为true。
[ -N file ]:如果 file 存在并且自上次读取后已被修改,则为true。
[ -O file ]:如果 file 存在并且属于有效的用户 ID,则为true。
[ -p file ]:如果 file 存在并且是一个命名管道,则为true。
[ -r file ]:如果 file 存在并且可读(当前用户有可读权限),则为true。
[ -s file ]:如果 file 存在且其长度大于零,则为true。
[ -S file ]:如果 file 存在且是一个网络 socket,则为true。
[ -t fd ]:如果 fd 是一个文件描述符,并且重定向到终端,则为true。 这可以用来判断是否重定向了标准输入/输出错误。
[ -u file ]:如果 file 存在并且设置了 setuid 位,则为true。
[ -w file ]:如果 file 存在并且可写(当前用户拥有可写权限),则为true。
[ -x file ]:如果 file 存在并且可执行(有效用户有执行/搜索权限),则为true。
[ file1 -nt file2 ]:如果 FILE1 比 FILE2 的更新时间最近,或者 FILE1 存在而 FILE2 不存在,则为true。
[ file1 -ot file2 ]:如果 FILE1 比 FILE2 的更新时间更旧,或者 FILE2 存在而 FILE1 不存在,则为true。
[ FILE1 -ef FILE2 ]:如果 FILE1 和 FILE2 引用相同的设备和 inode 编号,则为true。
  • 字符串判断
1
2
3
4
5
6
7
8
[ string ]:如果string不为空(长度大于0),则判断为真。
[ -n string ]:如果字符串string的长度大于零,则判断为真。
[ -z string ]:如果字符串string的长度为零,则判断为真。
[ string1 = string2 ]:如果string1和string2相同,则判断为真。
[ string1 == string2 ] 等同于[ string1 = string2 ][ string1 != string2 ]:如果string1和string2不相同,则判断为真。
[ string1 '>' string2 ]:如果按照字典顺序string1排列在string2之后,则判断为真。
[ string1 '<' string2 ]:如果按照字典顺序string1排列在string2之前,则判断为真。
  • 整数判断
1
2
3
4
5
6
[ integer1 -eq integer2 ]:如果integer1等于integer2,则为true。
[ integer1 -ne integer2 ]:如果integer1不等于integer2,则为true。
[ integer1 -le integer2 ]:如果integer1小于或等于integer2,则为true。
[ integer1 -lt integer2 ]:如果integer1小于integer2,则为true。
[ integer1 -ge integer2 ]:如果integer1大于或等于integer2,则为true。
[ integer1 -gt integer2 ]:如果integer1大于integer2,则为true
  • case
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/bin/bash
# test.sh

read -n 1 -p "Type a character > "
echo
case $REPLY in
  [[:upper:]])    echo "'$REPLY' is upper case." ;;&
  [[:lower:]])    echo "'$REPLY' is lower case." ;;&
  [[:alpha:]])    echo "'$REPLY' is alphabetic." ;;&
  [[:digit:]])    echo "'$REPLY' is a digit." ;;&
  [[:graph:]])    echo "'$REPLY' is a visible character." ;;&
  [[:punct:]])    echo "'$REPLY' is a punctuation symbol." ;;&
  [[:space:]])    echo "'$REPLY' is a whitespace character." ;;&
  [[:xdigit:]])   echo "'$REPLY' is a hexadecimal digit." ;;&
esac
  • 循环
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/bin/bash

number=0
while [ "$number" -lt 10 ]; do
  echo "Number = $number"
  number=$((number + 1))
done

for i in word1 word2 word3; do
  echo $i
done


mp3s=( *.mp3 )

${array[i]}
"${activities[@]}"
${activities[*]}放在双引号之中,所有元素就会变成单个字符串返回。
hobbies=( "${activities[@]}" )

# 读取数组的长度
${#array[*]}
${#array[@]}

# 哪些位置是有值的
${!arr[@]}

# 取出成员
${array[@]:position:length}

foo=(a b c)
foo+=(d e f)

# 删除
unset foo[2]

# 清空整个数组
unset ArrayName

# 关联数组
declare -A colors
colors["red"]="#ff0000"
colors["green"]="#00ff00"
colors["blue"]="#0000ff"
  • set命令
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/usr/bin/env bash
set -u
set -x
set -e

# 放所有脚本的头部
set -eux

# 变量LINENO返回它在脚本里面的行号。
$LINENO
  • 临时文件
1
2
3
4
5
trap 'rm -f "$TMPFILE"' EXIT
TMPFILE=$(mktemp)

# 临时文件夹
mktemp -d