修改前缀
export PS1="bash> "
路径
dirname
strip last component from file name
dir=$(dirname "$0")
EXAMPLES
dirname /usr/bin/
-> "/usr"
dirname dir1/str dir2/str
-> "dir1" followed by "dir2"
dirname stdio.h
-> "."
set
运行 bash script.sh
后会创建一个子 shell,可通过 set
命令对子 shell 的运行环境参数做查看和写入。其中可定制的参数有很多,详见 gnu shell manual
- 直接执行 set 会显示已设置的环境变量 和 shell 函数
- set -e:只要出错(即返回非 0 值)就停止运行
- set -o pipefail:set -e 对于含有管道的命令无效,加上个参数后,管道中任意命令出错都停止运行。
- 若
$?
(表示上一次执行结果)非 0 则表示错误,如果希望忽略某些错误则可通过|| echo
强行输出返回值为 0
- 若
- set -u:遇到未定义变量或方法时,停止运行
- set -x:执行每条命令前,输出命令原文
常用函数
ts=$(date +%s) # 获取日期,如1682072409
echo 'a:b' | cut -d ':' -f 2 # 截取第二项(下标从0开始)
tag=$(cat a.txt) # 命令的结果需要用"${}"包围
uri=www."${tag}".com # 字符串拼接时,需要用$包裹变量
参数
$0
表示文件名
$1
表示第一个参数
if ["x$1" == "x"]
语句是一个等式检查,查看 “x$1”(即变量$1前面加上一个x)是否等于 “x”。如果等于,说明变量$1是空的。
使用 “x$1” 的原因是为了防止某些特殊情况造成的问题。例如,当 $1 的值为一个开头为 “-” 的字符串时(例如 “-n”),如果不加 “x”,表达式会变成 "-n" == ""
,这可能会被shell解析为一个命令选项,而不是字符串比较,从而导致错误。
所以,["x$1" == "x"]
语句的含义就是检查变量$1是否为空,而且这种方式可以避免某些特殊情况下的错误。
变量
local
在shell脚本中,local
是一种内建命令,用于在函数内部声明局部变量。这意味着这个变量的作用范围仅限于声明它的函数,一旦离开了该函数,这个变量就不再存在。
如果在函数内部没有使用local
声明变量,那么这个变量就是全局的,可以在整个脚本中访问。
例如,考虑以下脚本:
#!/bin/bash
function test_local() {
local local_var="I'm local"
global_var="I'm global"
echo $local_var
echo $global_var
}
test_local
echo $local_var
echo $global_var
运行这个脚本的输出将是:
I'm local
I'm global
<空行>
I'm global
可以看到,local_var
在函数外部是不可见的,而 global_var
在整个脚本中都是可见的
返回值
if [ -z $plugin ]; then
字符串是否为0’
正则
打印第 n 行
grep -n "" file.txt | grep -w '10' | cut -d: -f2
sed -n '10p' file.txt
awk '{if(NR==10){print $0}}' file.txt
tail -n +10 file.txt | head -1
获取行号
row_num=$(cat file.txt | wc -l)
echo $row_num
if [ $row_num -lt 10 ];then
echo "The number of row is less than 10"
else
awk '{if(NR==10){print $0}}' file.txt
fi
核对数据库各表数量
# input_table.list
table1
table2
table3
table4
table5
#!/bin/bash
start=$(date -j -f "%Y-%m-%dT%H:%M:%S" "2022-02-21T00:00:00" "+%s")
end=$(date -j -f "%Y-%m-%dT%H:%M:%S" "2022-02-22T00:00:00" "+%s")
start=$(bc -e "$start*1000")
end=$(bc -e "$end*1000")
for i in $(cat input_table.list); do
echo "$i"
psql -h192.168.2.163 -p5432 -Upostgres -ddeepface_data -c "select count(1) from $i where ts >= $start and ts < $end"
done
jq
查询
{
"FOO": {
"name": "Donald",
"location": "Stockholm"
},
"BAR": {
"name": "Walt",
"location": "Stockholm"
},
"BAZ": {
"name": "Jack",
"location": "Whereever"
}
}
# cat mytxt | jq .[] | jq ."name"
"Jack"
"Walt"
"Donald"
# jq '.[] | select(.location=="Stockholm")' mytxt
{
"name": "Donald",
"location": "Stockholm"
}
{
"name": "Walt",
"location": "Stockholm"
}
# jq -r .FOO.name mytxt # 查 json 某 key 对应的 value
Donald
检查日志
#!/bin/bash
# 过滤出.level = error, 但不含某些错误的日志
jq 'select(.level=="error")|select(.msg!="pq: no partition of relation found for row")|select(.msg!="json: cannot unmarshal object into Go struct field")' /logs/myprocess.err
sshpass
#!/bin/bash
set -x
app='a.txt b.json'
nodes=$(cat node.list)
password="qqq"
cd PKG
for i in $nodes; do
ip=$(echo $i| cut -d':' -f1)
port=$(echo $i| cut -d':' -f2)
sshpass -p "$password" scp -o StrictHostKeyChecking=no -P $port $app root@$ip:~/data
done
cd -
expect
expect 可以非交互式输入命令,设置如下 /Users/y/login.exp,然后 /Users/y/login.exp myname 192.168.2.99 mypass 即可ssh不需输入密-码,并且sudo -i不需输入密-码,简化操作。
➜ Desktop cat /Users/y/login.exp
#!/usr/bin/expect
set timeout 3
spawn ssh [lindex $argv 0]@[lindex $argv 1]
expect {
"(yes/no)?"
{send "yes\n";exp_continue}
"password:"
{send "[lindex $argv 2]\n";exp_continue}
"from"
{send "sudo -i\n";}
{send "sudo -i\n";exp_continue}
"password:"
{send "[lindex $argv 2]\n";}
}
interact
数组
xargs
在 xargs
命令中,{}
是一个占位符,它表示 xargs
应该在哪里插入输入的参数。在使用 xargs
时,你可以用 -I
选项指定一个替代字符串,然后在命令行中的任何位置使用这个字符串,xargs
就会把这个字符串替换为从输入中读取的每个参数。
例如,以下命令:
echo '1 2 3' | xargs -I {} echo "Number: {}"
会输出:
Number: 1
Number: 2
Number: 3
如果你在同一条命令中使用了两个 {}
,xargs
会把它们都替换为相同的参数。例如,以下命令:
echo '1 2 3' | xargs -I {} echo "Number: {} and again: {}"
会输出:
Number: 1 and again: 1
Number: 2 and again: 2
Number: 3 and again: 3
在这个例子中,每一行都包含两个 {}
,xargs
会把它们都替换为相同的参数。
示例:批量删除 docker container
docker ps | grep unload | awk '{print $11}' | xargs -I {} docker rm -f {}
1. `docker ps`:列出所有运行中的 Docker 容器。
2. `grep unload`:从上述列表中找出含有 "unload" 的行。
3. `awk '{print $1}'`:从找到的行中提取出第一个字段(默认情况下,这是容器的 ID)。
4. `xargs -I {} docker rm -f {}`:对每一个找到的容器 ID,执行 `docker rm -f` 命令来强制移除容器。
条件测试运算符
在Shell脚本中,有一些常用的条件测试运算符,用于执行条件判断和比较操作。以下是其中一些常见的条件测试运算符:
-eq
:等于,用于比较两个数值是否相等。
示例:if [ "$a" -eq "$b" ]; then ... fi
-ne
:不等于,用于比较两个数值是否不相等。
示例:if [ "$a" -ne "$b" ]; then ... fi
-lt
:小于,用于比较一个数值是否小于另一个数值。
示例:if [ "$a" -lt "$b" ]; then ... fi
-gt
:大于,用于比较一个数值是否大于另一个数值。
示例:if [ "$a" -gt "$b" ]; then ... fi
-le
:小于等于,用于比较一个数值是否小于等于另一个数值。
示例:if [ "$a" -le "$b" ]; then ... fi
-ge
:大于等于,用于比较一个数值是否大于等于另一个数值。
示例:if [ "$a" -ge "$b" ]; then ... fi
-z
:空字符串,用于检查一个字符串是否为空。
示例:if [ -z "$str" ]; then ... fi
-n
:非空字符串,用于检查一个字符串是否非空。
示例:if [ -n "$str" ]; then ... fi
!
:逻辑非,用于取反条件测试结果。
示例:if [ ! -z "$str" ]; then ... fi
-e
:用于检查文件或目录是否存在。
语法:-e 文件路径
如果指定的文件或目录存在,则条件成立,返回真(true);否则,条件不成立,返回假(false)。
这些条件测试运算符可以在if
语句中使用,用于执行条件判断和控制程序的执行流程。请注意,在Shell脚本中,条件测试运算符需要放在方括号 [ ]
内,并且方括号与测试表达式之间需要有空格。