删除重复的数组元素
创建临时关联数组。设置关联数组
值并发生重复赋值时,bash会覆盖该键。这
允许我们有效地删除数组重复。
CAVEAT:需要bash4+
示例功能:
remove_array_dups() {
# Usage: remove_array_dups array
declare -A tmp_array
for i in $@; do
[[ $i ]] tmp_array[${i:- }]=1
done
printf %s\n ${!tmp_array[@]}
}
用法示例:
$ remove_array_dups 1 1 2 2 3 3 3 3 3 4 4 4 4 4 5 5 5 5 5 5
1
2
3
4
5
$ arr=(red red green blue blue)
$ remove_array_dups ${arr[@]}
red
green
blue
随机数组元素
示例功能:
random_array_element() {
# Usage: random_array_element array
local arr=($@)
printf %s\n ${arr[RANDOM % $#]}
}
用法示例:
$ array=(red green blue yellow brown)
$ random_array_element ${array[@]}
yellow
# Multiple arguments can also be passed.
$ random_array_element 1 2 3 4 5 6 7
3
循环一个数组
每次printf调用时,都会打印下一个数组元素。当
打印到达最后一个数组元素时,它
再次从第一个元素开始。
arr=(a b c d)
cycle() {
printf %s ${arr[${i:=0}]}
((i=i=${#arr[@]}-1?0:++i))
}
在两个值之间切换
这与上面的工作方式相同,这只是一个不同的用例。
arr=(true false)
cycle() {
printf %s ${arr[${i:=0}]}
((i=i=${#arr[@]}-1?0:++i))
}
LOOPS
循环一系列数字
替代seq。
# Loop from 0-100 (no variable support).
for i in {0..100}; do
printf %s\n $i
done
循环遍历可变数字范围
替代seq。
# Loop from 0-VAR.
VAR=50
for ((i=0;i=VAR;i++)); do
printf %s\n $i
done
循环数组
arr=(apples oranges tomatoes)
# Just elements.
for element in ${arr[@]}; do
printf %s\n $element
done
循环遍历带索引的数组
arr=(apples oranges tomatoes)
# Elements and index.
for i in ${!arr[@]}; do
printf %s\n ${arr[i]}
done
# Alternative method.
for ((i=0;i${#arr[@]};i++)); do
printf %s\n ${arr[i]}
done
循环遍历文件的内容
while read -r line; do
printf %s\n $line
done file
循环遍历文件和目录
不要用ls。
# Greedy example.
for file in *; do
printf %s\n $file
done
# PNG files in dir.
for file in ~/Pictures/*.png; do
printf %s\n $file
done
# Iterate over directories.
for dir in ~/Downloads/*/; do
printf %s\n $dir
done
# Brace Expansion.
for file in /path/to/parentdir/{file1,file2,subdir/file3}; do
printf %s\n $file
done
# Iterate recursively.
shopt -s globstar
for file in ~/Pictures/**/*; do
printf %s\n $file
done
shopt -u globstar
文件处理
CAVEAT: bash在版本中不能正确处理二进制数据 4.4。
将文件读取为字符串
替代cat命令。
file_data=$(file)
将文件读取到数组(按行)
替代cat命令。
# Bash 4
IFS=#39;\n read -d -ra file_data file
# Bash 4+
mapfile -t file_data file
获取文件的前N行
替代head命令。
CAVEAT:需要bash4+
示例功能:
head() {
# Usage: head n file
mapfile -tn $1 line $2
printf %s\n ${line[@]}
}
用法示例:
$ head 2 ~/.bashrc
# Prompt
PS1=➜
$ head 1 ~/.bashrc
# Prompt
获取文件的最后N行
替代tail命令。
CAVEAT:需要bash4+
示例功能:
tail() {
# Usage: tail n file
mapfile -tn 0 line $2
printf %s\n ${line[@]: -$1}
}
用法示例:
$ tail 2 ~/.bashrc
# Enable tmux.
# [[ -z $TMUX ]] && exec tmux
$ tail 1 ~/.bashrc
# [[ -z $TMUX ]] && exec tmux
获取文件中的行数
替代wc -l。
示例函数(bash 4):
lines() {
# Usage: lines file
mapfile -tn 0 lines $1
printf %s\n ${#lines[@]}
}
示例函数(bash 3):
此方法使用的内存少于mapfile方法,并在bash3中工作,但对于较大的文件,它的速度较慢。
lines_loop() {
# Usage: lines_loop file
count=0
while IFS= read -r _; do
((count++))
done $1
printf %s\n $count
}
用法示例:
$ lines ~/.bashrc
48
$ lines_loop ~/.bashrc
48
计算目录中的文件或目录
这是通过将glob的输出传递给函数然后计算参数的数量来实现的。
示例功能:
count() {
# Usage: count /path/to/dir/*
# count /path/to/dir/*/
printf %s\n $#
}
用法示例:
# Count all files in dir.
$ count ~/Downloads/*
232
# Count all dirs in dir.
$ count ~/Downloads/*/
45
# Count all jpg files in dir.
$ count ~/Pictures/*.jpg
64
创建一个空文件
替代touch。
# Shortest.
file
# Longer alternatives:
:file
echo -n file
printf file
提取两个标记之间的线条
示例功能:
extract() {
# Usage: extract file opening marker closing marker
while IFS=#39;\n read -r line; do
[[ $extract $3 ]] &&
printf %s\n $line
[[ $line == $2 ]] && extract=1
[[ $line == $3 ]] && extract=
done $1
}
用法示例:
# Extract code blocks from MarkDown file.
$ extract ~/projects/pure-bash/README.md ```sh ```
# Output here...
文件路径
获取文件路径的目录名称
替代dirname命令。
示例功能:
dirname() {
# Usage: dirname path
printf %s\n ${1%/*}/
}
用法示例:
$ dirname ~/Pictures/Wallpapers/1.jpg
/home/black/Pictures/Wallpapers/
$ dirname ~/Pictures/Downloads/
/home/black/Pictures/
获取文件路径的基本名称
替代basename命令。
示例功能:
basename() {
# Usage: basename path
: ${1%/}
printf %s\n ${_##*/}
}
用法示例:
$ basename ~/Pictures/Wallpapers/1.jpg
1.jpg
$ basename ~/Pictures/Downloads/
Downloads
变量
使用变量分配和访问变量
$ hello_world=value
# Create the variable name.
$ var=world
$ ref=hello_$var
# Print the value of the variable name stored in hello_$var.
$ printf %s\n ${!ref}
value
或者,在bash4.3+上:
$ hello_world=value
$ var=world
# Declare a nameref.
$ declare -n ref=hello_$var
$ printf %s\n $ref
value
根据另一个变量命名变量
$ var=world
$ declare hello_$var=value
$ printf %s\n $hello_world
value
ESCAPE序列
与流行的看法相反,使用原始逃逸序列没有问题。使用tput与手动打印相同的ANSI序列的摘要。更糟糕的是,tput实际上并不便携。有许多tput变体,每个变体都有不同的命令和语法(尝试tput setaf 3使用FreeBSD系统)。原始序列很好。
文字颜色
注意:需要RGB值的序列仅适用于真彩色终端仿真器。
序列
它有什么作用?
值
\e[38;5;NUMm
设置文本前景色。
0-255
\e[48;5;NUMm
设置文本背景颜色。
0-255
\e[38;2;R;G;Bm
将文本前景色设置为RGB颜色。
R,G,B
\e[48;2;R;G;Bm
将文本背景颜色设置为RGB颜色。
R,G,B
文字属性
序列
它有什么作用?
\e[m
重置文本格式和颜色。
\e[1m
粗体。
\e[2m
微弱的文字。
\e[3m
斜体文字。
\e[4m
下划线文字。
\e[5m
慢慢眨眼。
\e[7m
交换前景色和背景色。
光标移动
序列
它有什么作用?
值
\e[LINE;COLUMNH
将光标移动到绝对位置。
line, column
\e[H
将光标移动到原位(0,0)。
\e[NUMA
将光标向上移动N行。
num
\e[NUMB
将光标向下移动N行。
num
\e[NUMC
将光标向右移动N列。
num
\e[NUMD
将光标向左移动N列。
num
\e[s
保存光标位置。
\e[u
恢复光标位置。
删除文本
序列
它有什么作用?
\e[K
从光标位置删除到行尾。
\e[1K
从光标位置删除到行首。
\e[2K
擦除整个当前行。
\e[J
从当前行删除到屏幕底部。
\e[1J
从当前行删除到屏幕顶部。
\e[2J
清除屏幕。
\e[2J\e[H
清除屏幕并将光标移动到0,0。
参数扩展
间接
参数
它有什么作用?
${!VAR}
根据值访问变量VAR。
${!VAR*}
展开为IFS以VAR。开头的变量名称的分隔列表。
${!VAR@}
展开为IFS以VAR。开头的变量名称的分隔列表。如果是双引号,则每个变量名称都会扩展为单独的单词。
替换
参数
它有什么作用?
${VAR#PATTERN}
从字符串的开头删除模式的最短匹配。
${VAR##PATTERN}
从字符串的开头删除模式的最长匹配。
${VAR%PATTERN}
从字符串末尾删除模式的最短匹配。
${VAR%%PATTERN}
从字符串末尾删除模式的最长匹配。
${VAR/PATTERN/REPLACE}
用字符串替换第一个匹配。
${VAR//PATTERN/REPLACE}
用字符串替换所有匹配项。
${VAR/PATTERN}
删除第一场比赛。
${VAR//PATTERN}
删除所有比赛。
长度
参数
它有什么作用?
${#VAR}
字符变量的长度。
${#ARR[@]}
元素中的数组长度。
扩张
参数
它有什么作用?
${VAR:OFFSET}
N从变量中删除第一个字符。
${VAR:OFFSET:LENGTH}
从N字符到N字符获取子字符串。
(${VAR:10:10}:从char 10到char 获取子字符串20)
${VAR:: OFFSET}
N从变量中获取第一个字符。
${VAR:: -OFFSET}
N从变量中删除最后一个字符。
${VAR: -OFFSET}
N从变量中获取最后一个字符。
${VAR:OFFSET:-OFFSET}
削减第一个N字符和最后一个N字符。
bash 4.2+
案例修改
参数
它有什么作用?
警告
${VAR^}
大写第一个字符。
bash 4+
${VAR^^}
大写所有字符。
bash 4+
${VAR,}
小写第一个字符。
bash 4+
${VAR,,}
小写所有字符。
bash 4+
默认值
参数
它有什么作用?
${VAR:-STRING}
如果VAR为空或未设置,请使用STRING其值。
${VAR-STRING}
如果VAR未设置,请使用STRING其值。
${VAR:=STRING}
如果VAR为空或未设置,请将值设置VAR为STRING。
${VAR=STRING}
如果VAR未设置,请将值设置VAR为STRING。
${VAR:+STRING}
如果VAR不为空,则使用STRING其值。
${VAR+STRING}
如果VAR已设置,则使用STRING其值。
${VAR:?STRING}
如果为空或未设置,则显示错误。
${VAR?STRING}
如果未设置则显示错误。
BRACE EXPANSION
范围
# Syntax: {START..END}
# Print numbers 1-100.
echo {1..100}
# Print range of floats.
echo 1.{1..9}
# Print chars a-z.
echo {a..z}
echo {A..Z}
# Nesting.
echo {A..Z}{0..9}
# Print zero-padded numbers.
# CAVEAT: bash 4+
echo {01..100}
# Change increment amount.
# Syntax: {START..END..INCREMENT}
# CAVEAT: bash 4+
echo {1..10..2} # Increment by 2.
字符串列表
echo {apples,oranges,pears,grapes}
# Example Usage:
# Remove dirs Movies, Music and ISOS from ~/Downloads/.
rm -rf ~/Downloads/{Movies,Music,ISOS}
有条件的表达
文件条件
表达
值
它有什么作用?
-a
file
如果文件存在。
-b
file
如果文件存在并且是块特殊文件。
-c
file
如果文件存在并且是字符特殊文件。
-d
file
如果文件存在且是目录。
-e
file
如果文件存在。
-f
file
如果文件存在且是常规文件。
-g
file
如果文件存在且其set-group-id位已设置。
-h
file
如果文件存在并且是符号链接。
-k
file
如果文件存在且其粘滞位已设置
-p
file
如果文件存在并且是命名管道(FIFO)。
-r
file
如果文件存在且可读。
-s
file
如果文件存在且其大小大于零。
-t
fd
如果文件描述符是打开的并且引用了终端。
-u
file
如果文件存在且其set-user-id位已设置。
-w
file
如果文件存在且可写。
-x
file
如果文件存在且可执行。
-G
file
如果文件存在且由有效组ID拥有。
-L
file
如果文件存在并且是符号链接。
-N
file
如果文件存在且自上次读取后已被修改。
-O
file
如果文件存在并且由有效用户ID拥有。
-S
file
如果文件存在且是套接字。
文件比较
表达
它有什么作用?
file -ef file2
如果两个文件都引用相同的inode和设备编号。
file -nt file2
如果file比file2(使用修改时间)更新或file存在file2而不存在。
file -ot file2
如果file早于file2(使用修改时间)或file2存在file而不存在。
可变条件
表达
值
它有什么作用?
-o
opt
如果启用了shell选项。
-v
var
如果变量具有指定的值。
-R
var
如果variable是名称引用。
-z
var
如果字符串的长度为零。
-n
var
如果字符串的长度不为零。
变量比较
表达
它有什么作用?
var = var2
等于。
var == var2
等于(同义词=)。
var != var2
不等于。
var var2
小于(以ASCII字母顺序排列。)
var var2
大于(以ASCII字母顺序排列。)
算术运算符
分配
运营商
它有什么作用?
=
初始化或更改变量的值。
算术
运营商
它有什么作用?
+
加成
-
减法
*
乘法
/
师
**
幂
%
模
+=
加 - 等于(增加变量。)
-=
减去等于(减少变量。)
*=
时间相等(乘以变量。)
/=
Slash-Equal(除以变量。)
%=
Mod-Equal(除去变量的剩余部分。)
按位
运营商
它有什么作用?
按位左移
=
左移平等
按位右移
=
右移平等
" data-track="439">按位AND
" data-track="442">按位AND-Equal
`
`
按位OR
`
=`
按位OR-等于
~
按位NOT
^
按位异或
^=
按位XOR-Equal
合乎逻辑
运营商
它有什么作用?
!
不
" data-track="469">和
`
`
要么
杂
运营商
它有什么作用?
例
,
逗号分隔符
((a=1,b=2,c=3))
算术
设置变量的语法更简单
# Simple math
((var=1+2))
# Decrement/Increment variable
((var++))
((var--))
((var+=1))
((var-=1))
# Using variables
((var=var2*arr[2]))
三元测试
# Set the value of var to var2 if var2 is greater than var.
# var: variable to set.
# var2var: Condition to test.
# ?var2: If the test succeeds.
# :var: If the test fails.
((var=var2var?var2:var))
TRAPS
陷阱允许脚本在各种信号上执行代码。在pxltrm(用bash编写的像素艺术编辑器)中,陷阱用于在窗口大小调整时重绘用户界面。另一个用例是在脚本退出时清理临时文件。
应该在脚本开头附近添加陷阱,以便捕获任何早期错误。
注意:有关信号的完整列表,请参阅trap -l。
在脚本退出时做一些事情
# Clear screen on script exit.
trap printf \\e[2J\\e[H\\e[m EXIT
忽略终端中断(CTRL + C,SIGINT)
trap INT
对窗口调整大小做出反应
# Call a function on window resize.
trap code_here SIGWINCH
在每个命令之前做点什么
trap code_here DEBUG
当shell函数或源文件完成执行时执行某些操作
trap code_here RETURN
性能
禁用Unicode
如果不需要unicode,则可以禁用它以提高性能。结果可能会有所不同,但是neofetch和其他程序有明显改善。
# Disable unicode.
LC_ALL=C
LANG=C
已过时的语法
家当
用#!/usr/bin/env bash而不是#!/bin/bash。
前者搜索用户PATH以查找bash二进制文件。后者假设它始终安装/bin/可能导致问题。
# Right:
#!/usr/bin/env bash
# Wrong:
#!/bin/bash
命令替换
用$()而不是。
# Right.
var=$(command)
# Wrong.
var=`command`
# $() can easily be nested whereas `` cannot.
var=$(command $(command))
功能声明
不要使用function关键字,它会降低与旧版本的兼容性bash。
# Right.
do_something() {
# ...
}
# Wrong.
function do_something() {
# ...
}
内部变量
获取bash二进制文件的位置
$BASH
获取当前正在运行的bash进程的版本
# As a string.
$BASH_VERSION
# As an array.
${BASH_VERSINFO[@]}
打开用户首选的文本编辑器
$EDITOR $file
# NOTE: This variable may be empty, set a fallback value.
${EDITOR:-vi} $file
获取当前函数的名称
# Current function.
${FUNCNAME[0]}
# Parent function.
${FUNCNAME[1]}
# So on and so forth.
${FUNCNAME[2]}
${FUNCNAME[3]}
# All functions including parents.
${FUNCNAME[@]}
获取系统的主机名
$HOSTNAME
# NOTE: This variable may be empty.
# Optionally set a fallback to the hostname command.
${HOSTNAME:-$(hostname)}
获取操作系统的体系结构
$HOSTTYPE
获取操作系统/内核的名称
这可用于为不同的操作系统添加条件支持,
而无需调用uname。
$OSTYPE
获取当前的工作目录
这是pwd内置的替代品。
$PWD
获取脚本运行的秒数
$SECONDS
获取伪随机整数
每次$RANDOM使用时,之间的不同整数0及32767被返回。此变量不应用于与安全性相关的任何内容(包括加密密钥等)。
$RANDOM
有关终端的信息
获取行和列中的终端大小(来自脚本)
在纯bash中编写脚本和stty/ tput无法
调用时,这很方便。
示例功能:
get_term_size() {
# Usage: get_term_size
# (:;:) is a micro sleep to ensure the variables are
# exported immediately.
shopt -s checkwinsize; (:;:)
printf %s\n $LINES $COLUMNS
}
用法示例:
# Output: LINES COLUMNS
$ get_term_size
15 55
以像素为单位获取终端大小
CAVEAT:这在某些终端仿真器中不起作用。
示例功能:
get_window_size() {
# Usage: get_window_size
printf %b ${TMUX:+\\ePtmux;\\e}\\e[14t${TMUX:+\\e\\\\}
IFS=;t read -d t -t 0.05 -sra term_size
printf %s\n ${term_size[1]}x${term_size[2]}
}
用法示例:
# Output: WIDTHxHEIGHT
$ get_window_size
1200x800
# Output (fail):
$ get_window_size
x
获取当前光标位置
在纯bash中创建TUI时,这很有用。
示例功能:
get_cursor_pos() {
# Usage: get_cursor_pos
IFS=[; read -p #39;\e[6n -d R -rs _ y x _
printf %s\n $x $y
}
用法示例:
# Output: X Y
$ get_cursor_pos
1 8
转换
将十六进制颜色转换为RGB
示例功能:
hex_to_rgb() {
# Usage: hex_to_rgb #FFFFFF
# hex_to_rgb 000000
: ${1/\#}
((r=16#${_:0:2},g=16#${_:2:2},b=16#${_:4:2}))
printf %s\n $r $g $b
}
用法示例:
$ hex_to_rgb #FFFFFF
255 255 255
将RGB颜色转换为十六进制
示例功能:
rgb_to_hex() {
# Usage: rgb_to_hex r g b
printf #%02x%02x%02x\n $1 $2 $3
}
用法示例:
$ rgb_to_hex 255 255 255
#FFFFFF
CODE GOLF
更短的for循环语法
# Tiny C Style.
for((;i++10;)){ echo $i;}
# Undocumented method.
for i in {1..10};{ echo $i;}
# Expansion.
for i in {1..10}; do echo $i; done
# C Style.
for((i=0;i=10;i++)); do echo $i; done
更短的无限循环
# Normal method
while :; do echo hi; done
# Shorter
for((;;)){ echo hi;}
更短的功能声明
# Normal method
f(){ echo hi;}
# Using a subshell
f()(echo hi)
# Using arithmetic
# This can be used to assign integer values.
# Example: f a=1
# f a++
f()(($1))
# Using tests, loops etc.
# NOTE: ‘while’, ‘until’, ‘case’, ‘(())’, ‘[[]]’ can also be used.
f()if true; then echo $1; fi
f()for i in $@; do echo $i; done
if语法更短
# One line
# Note: The 3rd statement may run when the 1st is true
[[ $var == hello ]] && echo hi || echo bye
[[ $var == hello ]] echo there; } || echo bye
# Multi line (no else, single statement)
# Note: The exit status may not be the same as with an if statement
[[ $var == hello ]] &&
echo hi
# Multi line (no else)
[[ $var == hello ]] && {
echo hi
# ...
}
case设置变量的简单语句
在:内置的可以用来避免重复variable=在一个case语句。该$_变量存储的最后一个命令的最后一个参数。:总是成功,所以它可以用来存储变量值。
# Modified snippet from Neofetch.
case $OSTYPE in
darwin*)
: MacOS
;;
linux*)
: Linux
;;
*bsd* | dragonfly | bitrig)
: BSD
;;
cygwin | msys | win32)
: Windows
;;
*)
printf %s\n Unknown OS detected, aborting... &2
exit 1
;;
esac
# Finally, set the variable.
os=$_
其他
使用read作为替代的sleep命令
令人惊讶的是,sleep是一个外部命令而不是bash内置命令。
CAVEAT:需要bash4+
示例功能:
read_sleep() {
# Usage: sleep 1
# sleep 0.2
read -rst ${1:-1} -N 999
}
用法示例:
read_sleep 1
read_sleep 0.1
read_sleep 30
检查程序是否在用户的PATH中
# There are 3 ways to do this and either one can be used.
type -p executable_name /dev/null
hash executable_name /dev/null
command -v executable_name /dev/null
# As a test.
if type -p executable_name /dev/null; then
# Program is in PATH.
fi
# Inverse.
if ! type -p executable_name /dev/null; then
# Program is not in PATH.
fi
# Example (Exit early if program is not installed).
if ! type -p convert /dev/null; then
printf %s\n error: convert is not installed, exiting...
exit 1
fi
使用获取当前日期 strftime
Bash printf有一个内置的获取日期的方法,可用于代替date命令。
CAVEAT:需要bash4+
示例功能:
date() {
# Usage: date format
# See: man strftime for format.
printf %($1)T\\n -1
}
用法示例:
# Using above function.
$ date %a %d %b - %l:%M %p
Fri 15 Jun - 10:00 AM
# Using printf directly.
$ printf %(%a %d %b - %l:%M %p)T\n -1
Fri 15 Jun - 10:00 AM
# Assigning a variable using printf.
$ printf -v date %(%a %d %b - %l:%M %p)T\n -1
$ printf %s\n $date
Fri 15 Jun - 10:00 AM
获取当前用户的用户名
CAVEAT:需要bash4.4+
$ : \\u
# Expand the parameter as if it were a prompt string.
$ printf %s\n ${_@P}
black
生成UUID V4
CAVEAT:生成的值不具有加密安全性。
示例功能:
uuid() {
# Usage: uuid
C=89ab
for ((N=0;N16;++N)); do
B=$((RANDOM%256))
case $N in
6) printf 4%x $((B%16)) ;;
8) printf %c%x ${C:$RANDOM%${#C}:1} $((B%16)) ;;
3|5|7|9)
printf %02x- $B
;;
*)
printf %02x $B
;;
esac
done
printf \n
}
用法示例:
$ uuid
d5b6c731-1310-4c24-9fe3-55d556d44374
进度条
这是一种绘制进度条的简单方法,无需
在函数本身中使用for循环。
示例功能:
bar() {
# Usage: bar 1 10
# ^----- Elapsed Percentage (0-100).
# ^-- Total length in chars.
((elapsed=$1*$2/100))
# Create the bar with spaces.
printf -v prog %${elapsed}s
printf -v total %$(($2-elapsed))s
printf %s\r [${prog// /-}${total}]
}
用法示例:
for ((i=0;i=100;i++)); do
# Pure bash micro sleeps (for the example).
(:;:) :) :) :) :)
# Print the bar.
bar $i 10
done
printf \n
获取脚本中的函数列表
get_functions() {
# Usage: get_functions
IFS=#39;\n read -d -ra functions (declare -F)
printf %s\n ${functions[@]//declare -f }
}
绕过shell别名
# alias
ls
# command
# shellcheck disable=SC1001
\ls
绕过shell函数
# function
ls
# command
command ls
在后台运行命令
这将运行给定命令并使其保持运行,即使在终端或SSH连接终止后也是如此。忽略所有输出。
bkr() {
(nohup $@ /dev/null &)
}
bkr ./some_script.sh # some_script.sh is now running in the background
原文地址:https://www.cnblogs.com/lucius/p/10965638.html