1.linux命令

nohup java -jar a.jar产生nohup.out(不是脚本输出打印而是异常打印)
netstat -ntlp查看运行程序的端口。
kill -9 %1(这个1就是jobs命令的1编号)。
top -d/n 1
内存条:dmidecode |grep -P -A5 "Memory\s+Device"|grep Size|grep -v Range
ps -ef | grep memtester | grep -v grep | cut -c 9-16 | xargs kill -9

echo b > /proc/sysrq-trigger # 强制重启(echo 1 > /proc/sys/kernel/sysrq激活,不保存数据,直接终止所有进程)
reboot -d 60 # 表示60秒后重启,发送SIGTERM信号通知进程保存数据
shutdown -r +5  # 系统将在5分钟后重启
shutdown -r now  # 立即重启

rm -rf !(bak)
du -sh openbmc
scp -r -P
chown -R a:a ... # change owner(更改所有者), chmod: change mode(更改权限)
sudo rsync -av --exclude='build' openbmc/ /home/a/openbmc # 拷贝
grep -ne webui-vue -r --exclude-dir=build
find . -ls | grep 'usr/bin/'
find . -iname a -not -path "./build/*"

1.1 用户管理及权限

useradd -m -s /bin/bash john (/bin/sh: chsh -s /bin/bash john),passwd john
vi /etc/sudoers, a ALL=(ALL:ALL) ALL , wq!保存, a用户能切到root用户。p是指管道文件。

在这里插入图片描述
在这里插入图片描述

1.2 find

在这里插入图片描述
日志文件没清空非常大,要找到删除,如下找系统中大文件,超过10M。
在这里插入图片描述
如下查找文件夹,文件夹有相应名字或大小属性。
在这里插入图片描述
如下基于修改时间,time是天。-1:今天一天之内。1:1天前这一天。+1:1天前。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如下指定最大文件深度。
在这里插入图片描述
如下是find指令总结。
在这里插入图片描述

1.3 grep

在这里插入图片描述
如下* 可换成 * .txt
在这里插入图片描述
-r:递归子路径,-n:显示行号。-i:忽略大小写。
在这里插入图片描述
如下用于java日志文件非常大,要grep出某个异常如ioexception,且需要打印出exception下面几行看什么出了错。
在这里插入图片描述
在这里插入图片描述
如下.中的\是转义符,.168这样重复3次(注意168前面有.,不是168,而是数字)。
在这里插入图片描述
在这里插入图片描述

1.4 cat/more

cat的文件非常大,非常占用cpu和内存,这时候可以每次读取一小部分。
在这里插入图片描述
如下通过空格往后翻页。
在这里插入图片描述
如下指定从第四行开始读。
在这里插入图片描述
如下查看前后10行。
在这里插入图片描述
在这里插入图片描述

1.5 head/tail

在这里插入图片描述
如下打印文件最后两行,tail -f 阻塞监控。
在这里插入图片描述
df -h查看磁盘使用,占用率太高就需要使用前面find,grep指令并进行删除。
在这里插入图片描述
题目:输出当前路径及当前路径子路径下所有.txt文件,要求大小超过1M,并且按照从大到小顺序进行排序输出前10个?
先通过find . -name '*.txt' -size +1M -type f 查看是否有大于1M的txt文件,没有的话就不用继续了。
再通过find . -name '*.txt' -size +1M -type f -print0|xargs -0 du -m|sort -nr|head -10

1.6 打包和解压

# tar只是打包,并不压缩或解压
-f:--file
-c: 建立,create
-x:解压,eXtract
-t:查看内容
-j:--bzip2
-z:--gzip
-v:--verbose,详细列出处理过的文件

在这里插入图片描述

1.7 正则

^[]$:中括号内部只匹配一个字符。\d,?* +这些是[a-z]{m,n}这些的简写。
在这里插入图片描述
\d:相等于[0- 9],中括号里是什么或什么。\D:相等于[^0- 9],除了0到9外的任意字符。如下匹配数字\d 数字外任意字符\D(也就是匹配任意字符)。
在这里插入图片描述
\w:字母,数字或下划线,常用于用户名的命名上。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
组group:如上只想获取@前面的用户名,上面中括号,大括号都出现了,就差如下小括号。first就是组名,右边是js语法,underfined因为没命名。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.8 sed

如下将逗号替换为空格。s表示替换,g表示全局,即行中所有匹配项都被替换。sed -i ‘s/sys_led/sys_url/g’ /home/sysfs1/s3ip_sysfs_frame/sysurl_sysfs.c。
在这里插入图片描述
^匹配行首,$匹配行尾,如下d删除空行或只包含空格的行(因为行首行尾中间为空)。
在这里插入图片描述
sed常用于管道过滤,如下把x替换成y。
在这里插入图片描述
如下-r打开扩展正则,将逗号换成TAB。sed -n ‘60,70p’ a.txt :查看文件第60-70行。
在这里插入图片描述

1.9 awk

如下按逗号分隔并打印分割后的第三列和第四列,默认用空格/制表符/换行符作为字段分隔符。-F指定分隔符,-V设置变量,NF列数,$NF是一行数据最后一列的值,多用于对字段(像数据库中的字段)。
在这里插入图片描述
如下BEGIN指定了处理文本之前需要执行的操作。END指定了处理完所有行之后所需要执行的操作。
在这里插入图片描述
如下按逗号分隔并打印最后一列内容。
在这里插入图片描述
在这里插入图片描述
对比上面,如下一行一行读取并打印,列数>0打印。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如下-c统计个数。
在这里插入图片描述
如下2是个数,不是序号。
在这里插入图片描述
在这里插入图片描述

1.10 shell语法

unset删除变量。
在这里插入图片描述
在这里插入图片描述
获取字符串长度:echo ${#str}expr length “${str}”
截取字符串:echo ${str:1:4}:显示字符串第1到第4个字符。
echo ${str:4}:从左边第4个字符开始,一直到结束。
echo ${str:0-6:3}:从倒数第6个字符开始的3个字符。
echo ${str:0-6}:从倒数第6个字符开始,一直到结束。

file=/dir1/dir2/dir3/my.file.txt
${file#*/}:删掉第一个 / 及其左边的字符串:dir1/dir2/dir3/my.file.txt     / 可换成 .#*.
${file##*/}:删掉最后一个 / 及其左边的字符串:my.file.txt
${file%/*}:删掉最后一个 / 及其右边的字符串:/dir1/dir2/dir3
${file%%/*}:删掉第一个 / 及其右边的字符串:(空值)
# "/sys/bus/i2c/drivers/xdpe12284/29-0040/hwmon/hwmon*/in2_input" , *当成字符串, 不匹配
DRIVER_PATH="/sys/bus/i2c/drivers/xdpe12284/29-0040/"  # 最后加上 /
VOUT1_NODE="in2_input"
vout1_node=$(find "$DRIVER_PATH" -name "$VOUT1_NODE")

在这里插入图片描述
在这里插入图片描述

#!/bin/bash
for i in `ifconfig | grep -o ^[a-z0-9.]*`
do
	name=$i 
	echo $name
	ipaddr=$(ifconfig $i|sed -n 2p|awk '{ print $2 }'|tr -d 'addr:')  # -d删除
	echo http://$1:8080/api/slave -d '{"slave":"'$2${name}'","ip":"'${ipaddr}'"}'
done

在这里插入图片描述
输入字符大小写转换:
在这里插入图片描述
在这里插入图片描述
删除字符:
在这里插入图片描述
压缩字符:
在这里插入图片描述
如下c取反,删除非:
在这里插入图片描述

#!/bin/bash
while true
do
    memtester 1G 5 &   # ssd用fio
    stress -c 10 -t 600 &     
    sleep 600
    data=$(ps aux | grep stress | awk -F ' ' '{print $2}')
    echo "$data" | while IFS= read -r line; do
        kill -9 "$line"
    done
    # pkill -9 -f "stress|memtester"    # pkill -9 -f "microcom|mTerm_client
    sleep 600
done

cat /var/log/c | while IFS= read -r line; do
    if [ "$line" != "gpiochip0 193" ] && [ "$line" != "gpiochip0 93" ] ; then
        echo "$line"    
    fi
done

cat /var/log/d | grep -vE "^3[0-9]{3}$"   # 排除文件中3111这类数字

if [[ $a =~ all ]]; then  # 同 if [[ $a == *all* ]]; then
  echo "a 包含 'all'"
fi

如下截取 = 号左边即第一个。shell中与&&,或||。
在这里插入图片描述

"-d")
    shift  # 默认左移一位
    case ${1} in
        "lc1" | "lc2" | "cmm" | "fb1" | "fb2")
            ;;
        *)
            usage
            ;;
    esac
    dev=${1}
    led_devie=${dev%[^a-zA-Z]}    # lc
    if [ "$1" != "cmm" ];then
        index=${dev/*[a-zA-Z]/}   # 1
    fi
    ;;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

# shift.sh , $# 是可执行程序后面参数个数,不包含可执行程序
until [ $# -eq 0 ]
do
	echo "第一个参数为: $1 参数个数为: $#"
	shift
done

$./shift.sh 1 2 3 4    
第一个参数为: 1 参数个数为: 4
第一个参数为: 2 参数个数为: 3
第一个参数为: 3 参数个数为: 2
第一个参数为: 4 参数个数为: 1
basename /usr/local/nginx/conf/nginx.conf
nginx.conf
basename -s .conf /usr/local/nginx/conf/nginx.conf
nginx
dirname //
/
dirname /a/b/
/a 
dirname a
.
dirname a/b
a
realpath $path : 返回$path的绝对路径,路径不存在会报错,文件不存在不会报错
root@bmc:~# parameter_str=$(fruid-util | head -n 1 | awk -F "[" '{print $2}')
root@bmc:~# echo $parameter_str
all, ocm, bmc, base, mac, fcb, fan1, fan2, fan3, fan4, fan5, fan6 ]

root@bmc:~# parameter_r_arr=${parameter_str//,/ }   #逗号替换为空格
root@bmc:~# echo $parameter_r_arr                  
all ocm bmc base mac fcb fan1 fan2 fan3 fan4 fan5 fan6 ]

root@bmc:~# parameter_r_arr=(${parameter_str//,/ })
root@bmc:~# echo $parameter_r_arr
all

root@bmc:~# unset "parameter_r_arr[-1]"    #删除最后 ]
root@bmc:~# echo $parameter_r_arr
all
root@bmc:~# echo ${parameter_r_arr[@]}
all ocm bmc base mac fcb fan1 fan2 fan3 fan4 fan5 fan6

let和$(( ))和expr用法类似,都是用来对整数进行运算,不能对小数(浮点数)或字符串运算。
在这里插入图片描述
在这里插入图片描述

a=2
a+=1   # 拼接
echo $a # 21

在这里插入图片描述
在这里插入图片描述

declare -i index=${1: -1}
echo $index
# ./a.sh 
0
# ./a.sh ls1  #取最后一个int类型数字
1

/dev/null是一个特殊文件,写入到它的内容都会被丢弃,从该文件读取内容也什么也读不到。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

a(){
    return 0   # return其他值,下面用if ! a
}
if a ; then   # if后面必须是表达式,0是真,没有if 0
   echo "111" # 打印
fi
// 如下是C语言,python一样。 if [ -n str1 ] 当字符串的长度大于0时为真(字符串非空) 。
if (0) { // 0不打印,其他值都打印,同编译#if 0
    printf("111");
}

unsigned char a = 0x01;
if ( a ) {
	// a = 0x01 , "0x00" , "0x01"
}
else {
	// 0x00
}

while(1!=2); //真,运行后一直阻塞

command > /dev/null 2>&1不在屏幕上显示输出结果错误
在这里插入图片描述

# 检测shell 语法1:shellcheck  ./
# 检测Python语法1:python3 -m flake8 ./
# 检测Python语法2:python3 -m black ./
# 检测C语法 :cppcheck 4 -q --enable=all ./
# 服务器做CppCheck时,需要过滤一些Check的文件。比如MakeFile、证书文件等
filter_make_file="Makefile"
filter_pem_file=".pem"
count=-1
for value in ${files_array[@]}  # files_array变量包含了所有要检测的文件(过滤前)
do
	if [[ $value =~ $filter_make_file || $value =~ $filter_crt_file ]]
	then
		continue
	fi
	count=$[$count+1]
	new_files_array[$count]=$value  # 将检测文件(过滤后)加入新的数组中
done
psu_model_check()
{
    for((i=0;i<"${PSUNUM}";i++))
    {
        val=$(get_psu_present $((i + 1)))
        if [ "${val}" -eq 0 ];then  #present
            PSU_SYSFS_DIR=$(i2c_device_sysfs_abspath "${PSU_PMBUS_SLAVE_ADDR_ARRAY[$i]}")
            psu_model_value[$i]=$(head -n 1 "${PSU_SYSFS_DIR}/mfr_model")
        else
            psu_model_value[$i]="NA"
        fi
    }

    for((i=0;i<"(${PSUNUM}-1)";i++))
    {
        if [ "${psu_model_value[$i]}" == "NA" ];then
            continue
        fi

        if [ "${psu_model_value[$i]}" != "${psu_model_value[$((i+1))]}" ];then
            psu_model_log_flag[$i]=true
        else
            psu_model_log_flag[$i]=false
        fi

        if [ "${psu_model_log_flag[$i]}" != "${psu_model_last_log_flag[$i]}" ]; then  # 这行last表示上一次,作用是状态不变就不记录
            psu_model_last_log_flag[$i]="${psu_model_log_flag[$i]}" # 这行last表示最新
            if ${psu_model_log_flag[$i]} ; then
                logger -p user.warning -t "$PROGRAM" "Abnormal: PSU Model Different: $i:${psu_model_value[$i]},$((i+1)):${psu_model_value[$((i+1))]}"
            else
                logger -p user.info -t "$PROGRAM" "Normal: All PSU Model Same: $i:${psu_model_value[$i]},$((i+1)):${psu_model_value[$((i+1))]}"
            fi
        fi
    }
}
#!/bin/bash
IP=$1
FILE1_PATH=$2
FILE2_PATH=$3
MAX_ATTEMPTS=$4
prog=$(basename "$0")

if [ "$#" -ne 4 ]; then
	echo "Usage: $prog IP FILE1 FILE2 MAX_ATTEMPTS"
	echo "Example: $prog  10.130.6.1  xg2_nvme_bp_cpld_v1.04_forobmc.tar.gz  xg2_6nvme_cpld_v0_00_02_forobmc.tar.gz 10"
	exit 1
fi

token=`curl -k -H "Content-Type: application/json" -X POST https://$IP/login -d '{"username" :  "root", "password" :  "root"}' 2>&1 | grep token | awk '{print $2;}' | tr -d '"'`
if [ -z "$token" ]; then
    echo "[ERROR] Authentication failed"
    exit 1
fi

for ((i=1; i<=$MAX_ATTEMPTS; i++))
do
	echo -e "\n========= 第 ${i} 次升级尝试 ========="
	result1=$(curl -sik \
	-H "Expect:" \
	-H "Content-Type: multipart/form-data" \
	-H "X-Auth-Token: $token" \
	-F "UpdateFile=@\"$FILE1_PATH\";type=application/octet-stream" \
	"https://$IP/redfish/v1/UpdateService/update-multipart" 2>&1
	)
	result1_error=`echo $result1 | grep error`

	if [ -z "$result1_error" ]; then
		sleep 30
		result2=$(curl -sik \
		-H "Expect:" \
		-H "Content-Type: multipart/form-data" \
		-H "X-Auth-Token: $token" \
		-F "UpdateFile=@\"$FILE2_PATH\";type=application/octet-stream" \
		"https://$IP/redfish/v1/UpdateService/update-multipart" 2>&1
		)
		result2_error=`echo $result2 | grep error`
		if [ -z "$result2_error" ]; then
			sleep 30
			continue
		else
			echo $result2
			break
		fi
	else
		echo $result1
		break
	fi
done
# 案例:清空文件夹但保留特定内容
#!/bin/bash
# 要清理的目标目录
target_dir="./A"
# 要保留的目录/文件白名单
keep=(
    "./A/B/C"
    "./A/E.txt"
)

# 根据 keep ,生成要保护的父路径列表,装在 extra_keep 中
# 比如 keep 中写明了 ./A/B/C ,那就得额外确保 ./A/B 和 ./A 不会被删除,因为它们也是会出现在 find 的结果里的
# 不知怎么的,GPT 就是考虑不到这点
add_parent_path() {
    local path="$1"
    # target_dir 不在这里保护,其由最下面的 -mindepth 1 保护
    while [ "$path" != "$target_dir" ]; do
        extra_keep+=("$path")
        # 去掉路径的最后一段,得到父路径
        path=$(dirname "$path")
    done
}
for item in "${keep[@]}"; do
    add_parent_path "$item"
done

# 构建 find 命令的排除参数
# 对于 keep ,既要保护文件/目录本身,又要保护其子目录(如果有的话)
# 比如 keep 中写明了 ./A/B ,那 ./A/B/C 就不能被删除掉
exclude_params=()
for item in "${keep[@]}"; do
    # 这里的 -prune 起到了排除子目录的效果
    exclude_params+=(-path "$item" -prune -o)
done
# 对于 extra_keep ,只需要保护目录本身不被删除即可,无需保护子目录
for item in "${extra_keep[@]}"; do
    exclude_params+=(-path "$item" -o)
done

# 打印被删除的文件/目录列表
find "$target_dir" -mindepth 1 "${exclude_params[@]}" -exec echo '{}' +

# 利用 find 命令执行删除
# -mindepth 1 是必要的,因为 extra_keep 并不会保护到 target_dir ,它被删了就前功尽弃了
find "$target_dir" -mindepth 1 "${exclude_params[@]}" -exec rm -rf '{}' +

在这里插入图片描述

1.11 i2ctransfer

dd if=/dev/zero of=a bs=10M count=1 2> b (创建10M的a文件,控制台打印的log到b文件,b内容是“1+0 records in …”)。
在这里插入图片描述

# openbmc_write_rtk_with_bin.sh  [file] <busid>   # rtl芯片和switch芯片相关
# [file]  : binary file"  要写入的二进制文件: RTL8364NB-VB_EE_PATCH_V1.4_ID1_hua_20210810.bin
# <busid> : busid used for i2ctransfer"     # i2c设备的总线号:3

#!/bin/sh
# 如下将二进制bin文件转为十六进制(0xff)文本文件再写入/tmp/excute_rtk.bin或excute_rtk.txt
# $1:RTL8364NB-VB_EE_PATCH_V1.4_ID1_hua_20210810.bin   $2:/tmp/excute_rtk.bin
pad_hexdata(){  
    binsize=$(stat -c "%s" $1)  # %s 文件大小(单位是字节byte) 
    remainder=$[$binsize%16]    # % :除以16字节取余 , itransfer一行写16字节
    if [ $remainder -ne 0 ];then  # 比如33字节,第一行16,第二行16,第三行1
        declare -i pad_num=16-$remainder  
        dd if=/dev/zero bs=$pad_num count=1 | tr "\000" "\377" >> $1  # 二进制文件不足的 我要这一行补0xff ,tr命令把00转换成ff
    fi
    touch /tmp/t.txt /tmp/p.txt
    cat $1 | od -x -v | awk '{$1=null;print $0}'  |sed '/^$/d' |sed s/[[:space:]]//g |sed 's/.\{2\}/& /g' > /tmp/t.txt   # od -x -v和hexdump一样用16进制查看bin文件
    cat /tmp/t.txt | awk '{print " " $2 " " $1 " " $4 " " $3 " " $6 " " $5 " " $8 " " $7 " " $10 " " $9 " " $12 " " $11 " " $14 " " $13 " " $16 " " $15}' > /tmp/p.txt
    sed s/[[:space:]]//g /tmp/p.txt |sed 's/../0x&/g'|sed 's/.\{4\}/& /g' > $2
    rm /tmp/t.txt /tmp/p.txt
}

# 如下生成/tmp/excute_rtk.sh ,每一行前面拼接i2ctransfer,i2ctransfer将上面/tmp/excute_rtk.bin内容写入设备
pad_shell(){  # $1:/tmp/excute_rtk.sh    $2:3
    offset=0
    while read line
    do 
        hex_offset=$(printf "%04x" $offset)
        hex_offset_h=${hex_offset: 0: 2}
        hex_offset_l=${hex_offset: 2: 2}
        echo "i2ctransfer -f -y  ${2}  w18@0x50 0x$hex_offset_h 0x$hex_offset_l $line" >>$1
        offset=$((${offset}+16))
    done < $d_path_hex
    sed -i '1i #!/bin/sh' $1
}

do_action(){ #  $1 : RTL8364NB-VB_EE_PATCH_V1.4_ID1_hu_20210810.bin     $2 : 3
    busid=$2
    s_path=$1
    d_path_hex="/tmp/excute_rtk_bin"
    d_path_sh="/tmp/excute_rtk.sh"
    if [ -f ${d_path_hex} ];then
        rm ${d_path_hex}
    fi
    if [ -f ${d_path_sh} ];then
        rm ${d_path_sh}
    fi
    touch ${d_path_sh}
    touch ${d_path_hex}
    
    pad_hexdata ${s_path} ${d_path_hex}
    echo "create $d_path_hex success!!"
    
    pad_shell ${d_path_sh} ${busid}
    echo "create $d_path_sh success!!"
    
    sh ${d_path_sh}  # /tmp/excute_rtk.sh将/tmp/excute_rtk_bin内容写入设备
}

usage(){
    program=$(basename "$0")
    echo "Usage:"
    echo "$program [file] <busid>"
    echo "  [file]          : binary file"
    echo "  <busid>         : busid used for i2ctransfer"
    echo "Note:"
    echo "  generate /tmp/excute_rtk_bin.sh based on binary file then execute shell"
    echo "Examples:"
    echo "  $program RTL8364NB-VB_EE_PATCH_V1.4_ID1_huaqin_20210810.bin 3 "
    echo ""
}

check_parameter(){
    if [ $# != 2 ];then
        usage
        exit 0
    fi
    if [ ! -f $1 ];then
        echo "$1 doesnot exist"
        exit 0
    fi
}

check_parameter "$@"
do_action "$@"

在这里插入图片描述

# hexdump -e  '8/1 "0x%02x, " "\n"' test.bin > xxd.hex  # 格式化输出binary二进制文件
# cat xxd.hex
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xfe,
0x01, 0x06, 0x19, 0xc9, 0x42, 0x69, 0x67, 0x56,
0x65, 0x6e, 0x64, 0x6f, 0x72, 0xc5, 0x4d, 0x79,
0x42, 0x4d, 0x43, 0xc4, 0x41, 0x41, 0x42, 0x42,
0xc4, 0x43, 0x43, 0x44, 0x44, 0xc4, 0x45, 0x45,
0x46, 0x46, 0xc4, 0x48, 0x48, 0x47, 0x47, 0xc0,
0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61,
# xxd -r -p xxd.hex xxd.bin  # 使用xxd转成binary 文件, 检查md5一致

1.12 expect

# remote_pdu_cycle.sh
#!/bin/sh
cmd_link="curl -X get http://240.1.1.1:8080/api/bmc/info | grep refused"
cmd_version="curl -X get http://240.1.1.1:8080/api/bmc/info | grep A-CM-v1.03.00"
max_count_test=5000
j=0

do_action() {
    test_count=6
    i=0
    while [ $i -lt $test_count ]; do
        result=$(sshpass -p 0penBmc /usr/bin/ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null  root@10.55.205.180  $cmd_link)
        if [ -z "$bmc_ver" ]; then
            time_value=$(date)
            echo "$time_value: restful ok"  >> ./pdu.log
            break
        fi
        let i+=1
        sleep 30
    done
    if [ $i -ge $test_count ]; then
        time_value=$(date)
        echo "$time_value: restful fail"  >> ./pdu.log
        exit 1
    fi

    result=$(sshpass -p 0penBmc /usr/bin/ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null  root@10.55.205.180  $cmd_version)
    if [ -z "$result" ]; then
        echo "$time_value: $result version fail"  >> ./pdu.log
        exit 1
    else
        echo "$time_value: $result version ok"  >> ./pdu.log
        return 0
    fi
}

while [ $j -lt $max_count_test ];
do
    echo "==============LOOP $j==============" >> ./pdu.log
    date  >> ./pdu.log
    echo "power off 22,23" >> ./pdu.log
    ./remote_pdu_cycle_off.sh
    sleep 10
    echo "power on 22,23" >> ./pdu.log
    ./remote_pdu_cycle_on.sh
    sleep 300
    do_action
    date  >> ./pdu.log
    let j+=1
done
# remote_pdu_cycle_off.sh
#!/usr/bin/expect 
set timeout 200000
set passwd 123456
set name apc
set passwderror 0

# spawn ssh-keygen -f "/home/morton/.ssh/known_hosts" -R $IP
# spawn scp $local_file /usr/local/morton/bmc_upgrade_for_ALI.sh root@$IP:~
spawn telnet 10.75.135.100 
expect {
    "*User Name*" { 
        send "$name\r" 
        exp_continue 
    }  
    "*Password*" { 
        send "$passwd\r" 
        exp_continue 
    } 
    "*apc>*" {
        sleep 1
        send "oloff 22,23\r"
        sleep 1
        exit 1
    }
    timeout { 
        puts "connect is timeout" 
        exit 3 
    }
}
# b文件:llength等是expect中专属的,和ftp那些命令一样。
#!/usr/bin/expect
puts "$argv0"

set arg_leng [llength $argv]
puts "$arg_leng" 

set argv_0   [lindex  $argv 0]
puts "$argv_0"

# @obmc-server:~/test$ ./b w
# ./b
# 1
# w

2.工具

# samba:/etc/samba/smb.conf ,systemctl restart smb ,smbpasswd -a a(将本地用户转变为samba用户即共享用户)
[a-home]
    comment = private share folder
    path = /home/a
    writable = yes
    browsable = yes
    valid users = a

# writeable=no指除了下行外都不能写。[a-home]是共享名即对方能看到的网络文件夹名称,comment是可共享目录的描述信息(可加可不加),valid_users指谁可以访问。

# windows -> 此电脑 -> ... -> 映射网络驱动器 -> \\ip\a-home

su root(需输入root密码)。sudo su/-i(当前用户暂时申请root权限,需输入当前用户密码)。https://smallpdf.com/pdf-to-jpg。选择文件夹右击"授权访问权限",选择"特定用户",添加"所有人",权限改为"读取与写入",以共享方式访问,就可删除文件夹了,还有删除里面文件就可删除文件夹。
在这里插入图片描述
curl ipinfo.io 查公网出口ip。
在这里插入图片描述
https://www.usb-drivers.org/ft232r-usb-uart-driver.html。
在这里插入图片描述
1.内存卡插入读卡器再插入电脑的usb接口,将u盘格式化为FAT32格式。如果右击格式化没有FAT32则用格式化fat32工具:http://www.ridgecrop.demon.co.uk/guiformat.exe。树莓派系统官网:https://www.raspberrypi.org/downloads/raspbian/。下载系统(1.9G)用写盘工具(https://www.balena.io/etcher/或win32diskimager)将系统写入u盘,写完后不要格式化其他盘。

2.树莓派插上内存卡,接上网线(网络认证)或连上wifi(连wifi,eth0信息为空,因为没有连接网线,wlan0显示内网ip)用ifconfig查看网络。service ssh start开启ssh服务,树莓派默认用户名pi,密码raspberry,sudo netstat -antp可查看到当前有22端口即ssh端口被监听,这时可关闭显示器。如果忘了树莓派内网ip,可用win下cmd中输入arp -a查看局域网下所有ip和mac对应。本电脑就可以ssh连接树莓派再通过picocom -b 115200 /dev/ttyUSB0(有实际的线连)连接到机器终端,minicom -D /dev/ttyUSB0 -b登不上时进入/var/lock,删除lockfile,minicom又可正常启动。

Ubuntu镜像网站http://mirrors.ustc.edu.cn/ubuntu-releases/16.04/。软碟通Ultraiso将从itellyou下载的win10或ubuntu镜像写入U盘。
在这里插入图片描述
bios启动过程按F7进入如下设置,UEFI启动U盘如下(legacy为老版本不用)。进入u盘引导选择install ubutu:安装过程如果出现/dev/sda一般是U盘,SSD一般为/dev/nvmeon1。设置时间,联网,apt-get install vim。bios启动依赖cpu和dimm。
在这里插入图片描述
vscode连接远程服务器报XHR failed:https://update.code.visualstudio.com/commit:d037ac076cee195194f93ce6fe2bdfe2969cc82d/server-linux-x64/stable (切换浏览器下载),拷贝进远程服务器解压如下:
在这里插入图片描述

2.1 pip换源

pip install速度慢,用pip换源:进入python,看os在哪个py脚本里即python工作路径在1下,所以在1下建pip文件夹可以执行。
在这里插入图片描述
在桌面新建pip文件夹再拖进1,在pip里新建.txt,再改为.ini文件,点击打开输入下面代码保存。
在这里插入图片描述
apt install python3-pip --fix-missing(断开vpn,cp /etc/apt/sources.list /etc/apt/sources.list.bak,python3 -m pip install --upgrade pip,apt-get update,pip install 包名 -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com)
在这里插入图片描述
三个源:https://pypi.tuna.tsinghua.edu.cn/simple,https://pypi.douban.com/simple,https://mirrors.aliyun.com/pypi/simple,upgrade升级下numpy,看下载速度:pip install numpy -U

以上为windows换源,下面为ubuntu换源同理:cd ~mkdir .pipcd .pipgedit pip.conf,将以下内容复制到pip配置文件pip.conf中,保存退出即可。

[global]
index-url = http://mirrors.aliyun.com/pypi/simple/
[install]
trusted-host = mirrors.aliyun.com (上面http地址中间一部分,这行为了获取ssl证书,不然会报错)

2.2 环境变量

/etc/profile(所有用户永久生效)。用户目录下.bash_profile(单一用户永久生效,.bash_profile中都含有export,用户一登录就执行.bash_profile)。export命令(只对当前shell临时生效,用户登出再登录,env查看没有刚export的)。

# a.sh:
BASH_SOURCE="$_"   # If using $_, it needs to be placed at the beginning of the script
echo "$0"
echo "$_"
echo "$BASH_SOURCE"
[ "$BASH_SOURCE" != "$0" ] && echo "不等于bash, 就是source"

$ source a.sh 
/bin/bash   # source命令本身是由/bin/bash执行的
/bin/bash
a.sh
不等于bash, 就是source

$ ./a.sh  # 等于bash
./a.sh
./a.sh
./a.sh

# a.sh: echo "$0"
$ source ./././a.sh = . a.sh  # 当前shell进程
/bin/bash  

$ /bin/bash a.sh  = bash a.sh  = ./a.sh  # 创建子shell进程
a.sh

在这里插入图片描述

chmod 777 /home/y/app/bin/build_bmc
~$ vi .bash_profile   # 和windows里设置path环境变量一个道理
export PATH=/home/y/app/bin:$PATH
~$ vi .bashrc
alias gr='grep -nr'

!/bin/bash  直接指定应该去哪找bash
!/usr/bin/env bash  告诉系统去$PATH包含的目录中挨个去找,先找到哪个就用哪个

可执行程序都要PATH指定如ls,pwd(也是可执行程序)不加./执行,因为在冒号分隔(冒号不是连接)的几个目录下找。
在这里插入图片描述
当两个目录下都有同名可执行文件,执行哪一个取决于下面PATH先后顺序,env PATH=/usr/bin perl命令指定哪个执行。
在这里插入图片描述
在这里插入图片描述
如下/etc/profile.d/ 是目录,在sss.sh中写入export XXXX=‘cccc’。/etc/profile这个文件中有这么一段shell:for i in /etc/profile.d/*.sh;,会在每次启动时自动加载profile.d目录中每个配置即当用户重新登录shell如下或source /etc/profile 时会触发。
在这里插入图片描述

crontab

在这里插入图片描述
在这里插入图片描述
如下每星期每月每日每小时的第20到第40分钟每分钟都会执行一次(1小时执行21次)。
在这里插入图片描述
在这里插入图片描述
crontab -e进入如下,dow表示星期。
在这里插入图片描述

3.systemd

journalctl等带ctl都属于systemd,systemctl查看所有service状态,Restart=on failure是失败会继续执行,默认执行5次(同rc.d中sv restart sensor-mon)。
在这里插入图片描述
systemd 可以管理所有系统资源,不同的资源统称为Unit,一共分如下12种:systemctl --state failed | cat
在这里插入图片描述
在这里插入图片描述
target是特殊的unit,它不执行任何实际的操作,作用把一组unit利用各种依赖和包含关系组织起来共同完成某件事情。systemd用目标(target类似状态标记) 替代了initd中运行级别的概念:
在这里插入图片描述

# systemctl get-default   # 查询当前默认开机运行的target
multi-user.target
# systemctl cat multi-user.target
[Unit]
Description=Multi-User System
Documentation=man:systemd.special(7)
Requires=basic.target   # 要求basic.target一起运行
Conflicts=rescue.service rescue.target  # 如果rescue.service或rescue.target正在运行,multi-user.target就不能运行,反之亦然
After=basic.target rescue.service rescue.target
AllowIsolate=yes  # 允许使用systemctl isolate命令切换到multi-user.target

# service-a.service
Requires=service-b.service # 强依赖:b失败,a也失败
After=service-b.service # 强制a在b之后启动
# systemctl daemon-reload
# systemctl list-dependencies --type=service --no-pager # 查看依赖树

4.文件操作

4.1 文本文件

返回FILE结构体:fgets和fprintf以行方式读写文本文件,但不能读写二进制文件。用fread和fwrite可以读写文本文件和二进制文件。
在这里插入图片描述
在这里插入图片描述
vi /tmp/test1.txt,可见有5行记录,不管执行多少次都是5行记录,因为文件打开方式是w,每次打开文件时都会清空原文件中的记录。
在这里插入图片描述
如下301可换成sizeof(strbuf),300字符(字符串结束符\0)+1个换行符。
在这里插入图片描述

int main() {
    int fd;
    char buffer[5] = {0};
    ssize_t bytesRead;

    fd = open("/home_a/abcd", O_RDONLY, 0444);
    bytesRead = read(fd, buffer, 4);
    if (bytesRead == -1) {
        printf("read error");
        return 1;
    }
    printf("Read %zd bytes: %s\n", bytesRead, buffer);  //Read 4 bytes: 0xff
    return 0;
}

#include <stdio.h>
int main() { // a.c
    char str[100];
    printf("请输入一行字符串: ");
    if (fgets(str, sizeof(str), stdin) != NULL) {
        char *newline = strchr(str, '\n'); // strchr: 查找\n以及后面字符
        printf("换行符:%s\n", newline);
        if (newline != NULL) {
            *newline = '\0'; //替换为结束符
        }
        printf("你输入的字符串是: %s\n", str);
        return 0;
    }
}

在这里插入图片描述

4.2 二进制文件

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.3 文件定位

文件内部有一个位置指针,用来指向当前读写的位置,也就是读到第几个字节。在文件打开时,如果打开模式是r和w,位置指针指向文件的第一个字节。如果打开模式是a,位置指针指向文件的尾部,每当从文件里读n个字节或文件里写入n个字节后,位置指针会后移n个字节。
在这里插入图片描述

FILE *fptime;
fptime=fopen("/tmp/time","w");
time_t time_log = time(NULL);
struct tm* tm_log = localtime(&time_log);
fprintf(fptime, "flag[%d]  LINE[%d]    %04d-%02d-%02d %02d:%02d:%02d\r\n",sensor_flag, __LINE__, tm_log->tm_year + 1900, tm_log->tm_mon + 1, tm_log->tm_mday, tm_log->tm_hour, tm_log->tm_min, tm_log->tm_sec);
fflush(fptime);
fclose(fptime);

FILE *fpLedColor=fopen(led_color,"w");
fseek(fpLedColor,0,SEEK_SET);  // SEEK_SET:从文件开头开始计算偏移量
fprintf(fpLedColor,"%s",sensor_flag?LED_GREEN_CODE:LED_YELLOW_CODE);
fprintf(stderr,"11\n"); // 打印在控制台,cpp也可用
fflush(fpLedColor);
fclose(fpLedColor);

5.动静态库

编译【预处理(语法检查),编译(.c->.s汇编文件),汇编(.s->.o二进制文件),链接(多个.o合并成1个执行文件)】的最后阶段即将依赖引入过程叫链接,静态链接同【notes7】2.2章节,so打到二进制文件里,跨机器不用机器上有so文件。

动态链接的so文件通过mmap加载进共享内存,动态链接a.out文件小且内存占用小,此外动态链接在so库更新后不需重新编译,一般首选。很多进程用到C语言libc.so里stdio.h里打印函数,如果通过静态链接,每个二进制进程占一个libc.so,占用的内存多。
在这里插入图片描述
和内核交互除了syscall,大部分用库函数如libc,libc是一个标准有很多实现:g(gnu)libc【ubuntu】,musllibc【alpine】
在这里插入图片描述
如上默认编译链接是动态,如下static指定静态链接。gcc是gnu的编译工具集合,gcc不光编译c语言且支持很多平台
在这里插入图片描述
如下alpine系统没有glibc库。
在这里插入图片描述

5.1 静态库

公用函数库的public.cpp是源代码,对任何人可见,实际开发出于保密并不希望提供公用函数库源代码。C/C++提供了一个保证代码安全性方法,public.cpp编译成库。

// public.h
#ifndef PUBLIC_H
#define PUBLIC_H 1
#include <stdio.h>
void func();   // 自定义函数的声明
#endif
// public.cpp
#include "public.h"
void func()   // 自定义函数的实现
{
  printf("我心匪石,不可转也。我心匪席,不可卷也。威仪棣棣,不可选也。\n");
}
// book265.cpp
#include "public.h"  // 把public.h头文件包含进来
int main()
{
  func();
}
g++ -o book265 book265.cpp public.cpp
./book265
我心匪石,不可转也。我心匪席,不可卷也。威仪棣棣,不可选也。

链接库的文件名是libpublic.a,链接库名是public,缺点使用的静态库发生更新改变,程序必须重新编译

gcc -c -o libpublic.a public.cpp

使用方法一:直接把调用者源代码和静态库文件名一起编译:

g++ -o book265 book265.cpp libpublic.a

使用方法二:用L参数指定静态库文件的目录,-l参数指定静态库名:如果要指定多个静态库文件的目录,用法是“-L/目录1 -L目录2 -L目录3”;如果要指定多个静态库,用法是“-l库名1 -l库名2 -l库名3”

g++ -o book265 book265.cpp -L/home/w/demo -lpublic
./book265
我心匪石,不可转也。我心匪席,不可卷也。威仪棣棣,不可选也。

5.2 动态库

动态库发生改变,程序不需要重新编译,动态库升级方便。

g++ -fPIC -shared -o libpublic.so public.cpp

如果在动态库文件和静态库文件同时存在,优先使用动态库编译:

g++ -o book265 book265.cpp -L/home/w/demo -lpublic

执行程序./book265时,出现以下提示:/book265: error while loading shared libraries: libpublic.so: cannot open shared object file: No such file or directory,因为采用了动态链接库的可执行程序在运行时需要指定动态库文件的目录,Linux系统中采用LD_LIBRARY_PATH环境变量指定动态库文件的目录。采用以下命令设置LD_LIBRARY_PATH环境变量。

export LD_LIBRARY_PATH=/home/w/demo:.

如果要指定多个动态库文件的目录,用法是“export LD_LIBRARY_PATH=目录1:目录2:目录3:.”,目录之间用半角的冒号分隔,最后的圆点指当前目录。接下来修改动态库中func函数的代码:

// printf("我心匪石,不可转也。我心匪席,不可卷也。威仪棣棣,不可选也。\n");
printf("生活美好如鲜花,不懂享受是傻瓜;\n");

如下重新编译动态库,无需重新编译book265,直接执行程序。

g++ -fPIC -shared -o libpublic.so public.cpp
./book265
生活美好如鲜花,不懂享受是傻瓜;

5.3 编译时为什么要加上 –lm

// 代码一
#include <stdio.h>
#include <math.h> //exp, man exp:Link with -lm
int main(int argc, char const *argv[]){
   printf("The exponential value of %lf is %lf\n", 0, exp(0));
   printf("The exponential value of %lf is %lf\n", 0+1, exp(0+1)); //e的1次幂
   printf("The exponential value of %lf is %lf\n", 0+2, exp(0+2));
   return(0);
}

在这里插入图片描述

// 代码二
#include <stdio.h>
#include <math.h>
int main(int argc, char const *argv[]){
   double x = 0;
   printf("The exponential value of %lf is %lf\n", x, exp(x));
   printf("The exponential value of %lf is %lf\n", x+1, exp(x+1));
   printf("The exponential value of %lf is %lf\n", x+2, exp(x+2));
   return(0);
}

在这里插入图片描述
在这里插入图片描述
代码一调用exp传入的参数是常量为0 。代码二调用exp传入的参数是变量 x,代码一会不会在运行之前就计算好了呢?如下代码一没有看到调用exp的身影,当传入参数为常量时就已计算好了值,最后不需调用exp函数。代码二通过如下main.s汇编代码可见多次调用call函数。
在这里插入图片描述
math.h中声明的库函数还有一点特殊之处,gcc命令行必须加-lm选项,因为数学函数位于libm.so库文件中(这些库文件通常位于/lib目录下),-lm选项告诉编译器,程序中用到的数学函数要到这个库文件里找。

gcc a.c -o a.outarm-linux-gcc a.c -o b.out,如果执行out文件出现No such file or directory,则将如下两个so文件互相ln -s建软链接。
在这里插入图片描述

6.信号

getpid库函数功能是获取进程编号,该函数没有参数,返回值是进程的编号(相同程序在不同时间执行,进程编号不同)。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
僵尸进程:一个进程执行了exit系统调用退出时会向父进程发送SIGCHLD信号,而其父进程并没有为它收尸(调用wait或waitpid来获得它的结束状态,如进程ID、终止状态等等)的进程,ps显示有< default >。父处理子进程死后的信息,如果不想处理就需要交给系统处理。
在这里插入图片描述
进程间通信IPC方式:1.管道:ls | grep。FIFO(first in first out)是管道一种。
2.消息队列:内核创建的一个消息队列,os中多个进程都能操作这个消息队列,可以往里面发送消息,可以接收消息,类似socket。
3.共享内存:每个进程访问内存时,有一个虚拟内存地址和物理内存地址的一个映射:一般两个进程的虚拟内存地址可以一样,但映射的物理内存地址一般不一样。共享内存就是将它们映射的物理内存地址也变一样,这时两个进程能同时访问一块相同的物理内存,借助这块物理内存实现通信。
4.套接字socket:访问数据库进程和数据库进程本身,这两个进程间通信就是通过3306号端口建立起的tcp套接字。本机访问mysql不走tcp的套接字,而是走unix域套接字(UDS,不用ip)。
5.信号量/灯:类似一个计数器,控制多个进程对一个共享资源的访问,起到控制数量的锁机制。
6.信号:一个进程可向另一个进程发送一个信号,进程可处理这个信号。如下列出所有信号,linux中信号大多数是把另一个进程杀死,干脆把这个指令叫kill了,如下64种死法。 如键盘中断ctrl+c是当前shell向tail -f这个进程发送一个信号值为2的SIGINT的信号,如用kill+进程id命令是15,kill -9是9。
在这里插入图片描述
捕捉信号2:在死循环之前注册下信号的处理,如下让ctrl+c无效。
在这里插入图片描述
捕捉了ctrl+c,无法停止,只能用kill。
在这里插入图片描述
在这里插入图片描述
程序后台运行两种方法:&:ctrl+c无法中止,用killall book1或kill 进程号。if (fork()>0)return 0 父进程退出
在这里插入图片描述
信号作用:服务程序在后台运行,如果想终止它,杀了它不是个好办法,因为没有释放资源。如果能向程序发一个信号,程序收到这个信号后调用一个函数,在函数中编写释放资源代码。
在这里插入图片描述
下面 EXIT函数就是自定义函数,TcpServer设为全局变量因为EXIT函数要访问它并关闭socket。
在这里插入图片描述

7.socket

http(client和server间req和res),socket(client和server间长连接)。
在这里插入图片描述
accept第一个参数是listen fd,第二个参数是addr(如果不关心对端地址,直接填 NULL。非空时把对端 IP/端口填进去,格式跟bind一致),第三个参数是addrlen。listen fd是门卫,accept 把排到队的客人拉进来,发给你一张新门票(clientfd),以后只跟这张门票打交道。
在这里插入图片描述
在这里插入图片描述
一个字节是2的8次方即256,所以0-255。ipv4组合方式共2的32(4*8=32)次方即43亿个。B/S(浏览器对服务器)和C/S(客户端对服务器)。
在这里插入图片描述
下面将三要素融合,https中s就是secure。
在这里插入图片描述
如下文件上传:上面是服务端,socket封装了io流,所以要关。高层socket关了,低层也会关。
在这里插入图片描述

import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

public class UploadClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 10086);

//11111111111111111111111111111111111111111111111111111111111111111111111111111
        FileInputStream fis = new FileInputStream("柳岩.jpg"); //读本地文件        
        OutputStream os = socket.getOutputStream();    

        int length = -1;
        byte[] buffer = new byte[1024];
        while((length = fis.read(buffer)) != -1){ //读
            os.write(buffer,0,length); //写到服务器socket中
        }
        fis.close();
        socket.close();
    }
}

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
public class UploadServer {
    public static void main(String[] args) throws IOException {        
        ServerSocket serverSocket = new ServerSocket(10086); //只创建一个服务端,一个端口不能被两个程序占用,不能在这行上面加while(true)
        while(true){  //死循环接收客户端请求            
            Socket socket = serverSocket.accept(); //虽然while(true),但这行会阻塞,单线程
            new Thread(new Runnable() { //不能放accept()前面,不能无限开线程
                @Override
                public void run() {
                    try {
                        InputStream is = socket.getInputStream();                        
                        // long time = System.currentTimeMillis(); // 以系统当前时间命名,同一毫秒,有两个用户同时上传文件, 就会有用户文件覆盖
                        String name = UUID.randomUUID().toString().replace("-", ""); //下行time改为name
                        FileOutputStream fos = new FileOutputStream("c:/test/server/" + name + ".jpg");
                        
                        int length = -1;
                        byte[] buffer = new byte[1024];
                        while((length = is.read(buffer)) != -1){
                            fos.write(buffer,0,length);
                        }                        
                        fos.close(); //应该放在finally中
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
//         serverSocket.close(); // 服务器不能关闭,关了就不回到while(true)了
        }
    }
}

如下BS案例:socket被写,页面被读。写一个浏览器程序难度和操作系统一样,鸿蒙os就是浏览器。
在这里插入图片描述

import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class BsServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(10090);
        Socket socket = serverSocket.accept(); //接收浏览器请求
        
//1111111111111111111111111111111111111111111111111111111111111111111111111111
        OutputStream os = socket.getOutputStream(); //写出页面
        os.write("HTTP/1.1 200 OK\r\n".getBytes()); // http协议的响应行  第一行
        os.write("Content-Type:text/html\r\n\r\n".getBytes()); //http协议的响应头 第二行,第三行为空白
        FileInputStream fis = new FileInputStream("index.html"); //已有,用来被读

        int length = -1;
        byte[] buffer = new byte[1024];
        while((length = fis.read(buffer)) != -1){
            os.write(buffer,0,length);  //第四行,响应体
        }
        fis.close();
        socket.close();
        serverSocket.close();
    }
}

在这里插入图片描述
在这里插入图片描述

8.std&boost库

下载boost库解压后执行编译和install脚本,一般.so或.a自动装在/usr/local/lib下。
在这里插入图片描述

int main(){        
	const size_t n = 100;       
	int v[n];
	// 标准C: 引入一个变量 i, 辅助完成循环访问 v 中每个元素的任务,循环条件上也必须写明循环终止条件,否则容易发生栈溢出错误
	size_t i;        
	for (i = 0; i < n; i++) { v[i] = 0;}           
	// c++17: 隐藏了迭代的细节(编译器保证正确)
	for (auto& x : v) {x = 0;}
}
std::pair<int, int> divide(int dividend, int divisor) {
    int quotient = dividend / divisor; // 除数
    int remainder = dividend % divisor;  // 余数
    return std::make_pair(quotient, remainder);
}
int main() {
    std::pair<int, int> result = divide(10, 3);
    std::cout << "Quotient: " << result.first << ", Remainder: " << result.second << std::endl;  // Quotient: 3, Remainder: 1
    return 0;
}
int main() {
    std::string str1 = "Hello";
    std::string str2 = std::move(str1);
    std::cout << "str1: " << str1 << std::endl;  // 此时 str1 的内容已经被移动,为空
    std::cout << "str2: " << str2 << std::endl;  // 输出 "Hello"
    return 0;
}

#define INFO_LOG Logger<true>(std::cout)  // true为false时,日志代码在编译期被完全移除
// Logger 类的构造函数需要一个 std::ostream&(输出流引用)参数,std::cout是标准输出流的全局实例,代表控制台输出
template <bool active>
struct Logger
{
    std::ostream& stream;
    Logger(std::ostream& _stream) : stream(_stream)
    {
        if constexpr (active)  // true
        {
            stream << std::dec; // 强制设为十进制格式,C++流对象(如 std::cout)保留格式状态(如十六进制、八进制等)
        }
    }
    template <typename T>   // 模板参数支持任意数据类型
    Logger& operator<<(const T& value)
    {
        if constexpr (active)
        {
            stream << value;   // 实际输出
        }
        return *this;
    }
    ~Logger()
    {
        if constexpr (active)
        {
            stream << std::endl; // 输出换行并刷新缓冲区
        }
    }
};
int main() {
    INFO_LOG << "Starting SPD ownership";  // 串口打印
    INFO_LOG << std::hex << 255;  // 输出 "255" 而不是 "ff"(构造函数中的std::dec会重置格式)
}

int main() {
    std::cout << "222" << std::endl;
}
void a()
{
    std::cout << "111" << std::endl;
}
void a() __attribute__((constructor));  // main函数之前执行

$ g++ a.cpp -o a
$ ./a
111
222
int main(int argc, char **argv) {
    static std::deque<bool>liquidStatus = {true, true, true};
    int status = 0; // 如下0 false,1 true
    bool nowLiquidStatusFlag = status ? true : false;
    std::cout << "nowLiquidStatusFlag is : " << std::boolalpha << nowLiquidStatusFlag << std::endl;
    liquidStatus.push_back(nowLiquidStatusFlag);
    liquidStatus.pop_front();  // 移除队列最前面的元素

    std::cout << "Current liquidStatus: ";
    for (bool val : liquidStatus) {
        std::cout << val << " "; // 最后空格
    }
    std::cout << std::endl; // 换行

    uint8_t data = 0x1c;
    if (data & 0x10) // bit4=1,不用>>4,非0进入
    {
        std::cout << "val非0" << std::endl;
    }
    return 0;
}

在这里插入图片描述

std::string text = "ID: 123";               // 待匹配的字符串
std::regex pattern(R"(ID:\s(\d+))");        // 定义正则表达式
// \s 匹配 任意空白字符(如空格 、制表符 \t、换行符 \n 等)
// () 表示 捕获组,匹配的内容会被单独存储,可以通过 match[1] 访问
std::smatch match;                          // 存储匹配结果
if (std::regex_search(text, match, pattern)) {  // 执行匹配
    std::cout << "整个匹配: " << match[0] << std::endl;  // "ID: 123"
    std::cout << "捕获组 1: " << match[1] << std::endl;  // "123"  // 等于std::string indexStr = *(match.begin() + 1);
} else {
    std::cout << "未匹配" << std::endl;
}

// std::string_view对字符串的处理,不用把字符串拷贝,直接赋值就行
// 调用dbus获取property时是字符串类型还是uint8/16等都可以用std::any或std::variant,不会崩溃

8.1 boost.asio实现异步inotify事件监听

// a.cpp
#include <fcntl.h>
#include <sys/inotify.h>
#include <boost/asio.hpp>
#include <functional>
#include <iostream>
#include <memory>

static constexpr const size_t eventBufSize = 1 * (sizeof(struct inotify_event) + NAME_MAX + 1);  // 选择一个最小的缓冲区大小,恰好装下一个满事件。

static void onEventReceived(struct inotify_event* event)  // 处理单个事件
{
    std::string_view fileName(event->name);
    std::cout << fileName << std::endl;
}

static void rawEventHandler(  // async_read_some() 的原始 handler ,需要处理一次读到多个事件的情况,并安排“异步递归”
    std::shared_ptr<boost::asio::posix::stream_descriptor> stream, char* buf,
    const boost::system::error_code& ec, size_t sizeRead)
{
    if (ec)
    {
        std::cerr << "Failed when reading inotify event " << ec.message()
                  << std::endl;
        return;
    }
    char* pos = buf; // 缓冲区中可能存在多个事件,逐一对其进行处理
    while (pos - buf < sizeRead)
    {
        struct inotify_event* event = reinterpret_cast<struct inotify_event*>(pos);  // 取出单个事件event
        onEventReceived(event);
        pos += sizeof(struct inotify_event) + event->len; // inotify_event 是个可变长度结构体,必须加上 len 才能得到其实际长度
    }

    // 本次读取完成,准备下次读取
    stream->async_read_some(boost::asio::buffer(buf, eventBufSize), 
                            std::bind_front(rawEventHandler, stream, buf));
}


static void setupInotify(boost::asio::io_context& io)
{
    static char buf[eventBufSize];  // inotify 事件是变长结构体 inotify_event,需要一块连续且长期有效的缓冲区。写成 static → 放在静态存储区,直到进程退出才释放;
    int fd = inotify_init1(IN_NONBLOCK); // 创建一个inotify fd , IN_NONBLOCK保证后续read()不会阻塞线程
    if (fd < 0)
    {
        std::cerr << "Unable to set up inotify" << std::endl;
        return;
    }

    inotify_add_watch(fd, "/home/bak/test", IN_CREATE); // 把目录 /home/bak/test 挂到 fd 上,只监听 新建文件 事件

    // 如下把裸 fd 包装成 asio 的 AsyncReadStream 对象。
    // 必须用 shared_ptr:
    // async_read_some 是异步操作,回调可能在 setupInotify 返回很久之后才执行;
    // 若 stream 是局部对象,离开作用域就会被析构,fd 被 close(),后续回调里访问已关闭的fd。
    // shared_ptr 把生存期塞进回调闭包里,保证“只要回调活着,stream 就活着”。
    auto stream = std::make_shared<boost::asio::posix::stream_descriptor>(io, fd); 
 
    stream->async_read_some(boost::asio::buffer(buf, eventBufSize),  // async_read_some 需要固定大小的缓冲区,因此选择拿 buffer() 把上面的 buf 包一下
                            std::bind_front(rawEventHandler, stream, buf));  // stream和buf作为rawEventHandler参数,读取 事件即struct inotify_event,将事件存入 buf 中,调用rawEventHandler依次处理buf里的事件
}

int main()
{
// boost.asio异步单线程:替换轮询while(1) 和for循环
// io_context 是一个epoll包装,参考【notes8】,fd有数据调回调
    boost::asio::io_context io;
    setupInotify(io);
    io.run(); // 理论上永不退出
}

// g++ -std=c++20 a.cpp
// ./a.out 一直卡在这里不退出
// /home/bak/test里新增文件,上一行就会打印出文件名

9.raii

作用域(scope):{} 围起来的那一块代码区域。生命周期(lifetime):对象在内存里真正存活的时间段。RAII全称 “Resource Acquisition is Initialization”即资源获取即初始化:将资源的生命周期与对象的生命周期绑定,当对象创建时获取资源,对象销毁时自动释放资源。

class MyClass {
public:
    MyClass() { std::cout << "constructor" << std::endl; }
    ~MyClass() { std::cout << "destructor" << std::endl; }
};
void func()
{
    MyClass c;
    std::cout << "111" << std::endl;
} // 调用析构
int main()
{
    std::cout << "before func()" << std::endl;
    func();
    std::cout << "after func()" << std::endl;
}
// 运行结果: 
before func()                                                                                                              
constructor // 调用func() 构造MyClass
111
destructor //  MyClass的作用域结束,析构
after func()


struct CustomFd
{
  public:
    CustomFd() = delete; // 把“默认无参构造函数”删了
	  ...
    explicit CustomFd(int fd) : fd(fd) {}  // 构造函数
    ~CustomFd()
    {
        if (fd >= 0)
        {
            close(fd);
        }
    }
};
CustomMap Signature::mapFile(const fs::path& path, size_t size)
{
    CustomFd fd(open(path.c_str(), O_RDONLY));
    return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0), size);
}  // fd作为CustomFd实例,在此处超出作用域后,会自动调用析构函数,关闭文件描述符,不会导致文件描述符泄漏。struct RemovablePath析构函数中直接删除tar包,避免空间浪费。

void context::spawn_complete()  {
{
			std::lock_guard l{lock};          
       ...    
} // lock_guard和需要保护的变量放置到同一个作用域(用花括号括起来),lock_guard构造时上锁; 超出作用域后释放锁
}

10.智能指针

在这里插入图片描述

void foo(char *ptr){  // 我们往往会陷入此场景下需不需要调用 free 释放内存,若释放了会不会dobule free的焦虑。克服这种焦虑的方法只能是:API文档说明或者代码中添加注释
	do(ptr);  // (1)  
	free(ptr) // (2)
}
void bar(){        
	char *p = malloc(100);        
	timer(5, foo, p); // 立即返回,5s后执行foo(p)
	free(p); // (3) // (1)可能遇到悬空指针,正确的做法应从(2) 中执行free,资源所有权的转移,使其看上去违背了“谁申请,谁释放”的原则
} 

void foo(int *v){    
	if( v ! = NULL)   // 对指针进行了判空处理     
        *v = 10;   // 存在 use after free 的可能性,不为空不代表没被释放
	free(v)    // 释放指针指向的内存:存在double free的可能性。若不调用free,存在内存泄漏的可能性
}

// 如下cpp智能指针解决下面问题
void foo(std::unique_ptr<int> v)  // 函数参数是一个独占式智能指针,foo管理其生命周期
{      
		if(v) { // 指针判空                    
			*v = 10; // 赋值,因是独占所有权,指针一定有效                        
		} 
} // 指针变量的生命周期结束,自动调用析构函数释放该指针。自动释放,不存在内存泄漏问题。指针别处不可见,不会use after free

如下是怎么正确使用 unique_ptr:

// 值传递 = 所有权交割,必须 std::move :
void foo(std::unique_ptr<MyClass> p){(void)p;}
int main(){
	auto p = std::make_unique<MyClass>(); // 同std::uniqpue_ptr<MyClass> p{new MyClass()}
	foo(p); // 编译报错,没有显式的资源转移表达
	foo(std::move(p));  // 显式表达资源所有权转移,资源转移后p失效,编译通过
}

// 引用传递 = 只给钥匙不改户主,reset 可以偷偷把房子拆了再盖新的,但房产证上还是原来那个人:
void foo(std::unique_ptr<MyClass>& p){             
	p.reset(new MyClass); // 重装指针,使unique_ptr释放原来管理的指针,进而管理一个新指针
}
int main(){        
	MyClass* ptr = new MyClass;          
	std::cout << ptr << std::endl; // 0x5561a66b62b0        
	std::unique_ptr<MyClass> p { ptr };   // 让unique_ptr管理原始指针     
	std::cout << p.get() << std::endl; // 0x5561a66b62b0  打印出 unique_ptr 管理的原始指针,就是ptr
	foo(p);  
	std::cout << p.get() << std::endl; // 0x5561a66b66e0   打印出 unique_ptr 管理的原始指针,因为发生了重装则不再是ptr      
}
std::unique_ptr<int> pr1(new int(10));
std::unique_ptr<double> pr2 = std::make_unique<double>(2.0);

struct MyClass {   
	 MyClass(int val) : value(val){}
	 int value;
};
std::unique_ptr<MyClass> ptr3 = std::make_unique<MyClass>(30);

std::cout << *ptr1 << std::endl; // 打印 10
std::cout << ptr3->value << std::endl; // 打印 30
(*ptr3).value = 50;
std::cout << ptr3->value << std::endl; // 打印 50

10.1 从0实现unique_ptr

struct Student{int age; std::string name;};
void foo(){   
	Student *s = new Student(18, "Jim");
	doSomeThings(s);    
	delete s;
}

class StudentWrapper{
	public:        
		explicit StudentWrapper(Student *ptr = nullptr): ptr_(ptr){}        
		~StudentWrapper(){delete ptr_;} 
		Student* get() const {return ptr_;}    
	private:        
		Student *ptr_;
};
void foo(){    
	StudentWrapper s(new Student(18, "Jim"));
	doSomeThing(s.get());
} // RAII
template <typename T>
class UniquePtr {
public:
    explicit UniquePtr(T* ptr = nullptr) : ptr_(ptr) {}  // : ptr_(ptr) 即 把形参ptr值直接赋给数据成员ptr_
    ~UniquePtr(){delete ptr_;}
    T* get() const {return ptr_;}
    T& operator*() const {return *ptr_;}
    T* operator->() const {return ptr_;}
private:    
		T* ptr_;
};
UniquePtr<Student> ptr1 { new Student(18, "Jim") };
UniquePtr<int> ptr2 { new int(10) };
std::cout << ptr1->name << std::endl;
std::cout << *ptr2 << std::endl;

// 再添加 禁掉拷贝构造和赋值行为,表明独占所有权
UniquePtr(const UniquePtr&) = delete;
UniquePtr& operator=(const UniquePtr&) = delete;

// 实现一个make_unique函数
template <typename T, typename... Args>
UniquePtr<T> make_unique(Args&&... args){return UniquePtr<T>(new T(std::forward<Args>(args)...));}

11.引用

int i ; // 值类型,有地址,可以&i取地址
int* o; // 指针类型
int& ii=i; // 引用类型,必须赋一个值,指向引用的谁

11.1 左右值

cpp将变量分为左值(有地址)和右值(无地址)。
在这里插入图片描述

11.2 为什么要右值引用

在这里插入图片描述
如下接着上面,隐含buffer b=buf,调用打印“拷贝构造”函数即值传递,拷贝构造函数里最后2行开和拷贝内存里数值到当前buf,有开销。为避免开销,只改void dothing(buffer& b),变成引用/左值传递传地址,运行没有任何打印。
在这里插入图片描述
如下传入是临时构造的即右值,void dothing(const buffer& b)也可以(表示这个引用不会被修改即const ,因为右值不能被修改)。
在这里插入图片描述
在这里插入图片描述
当传入右值是:dothing(拷贝构造),右值是将亡值(经历析构),不需要构造里开和拷贝内存,所以添加右值引用构造器即move拷贝构造器。

11.3 copy & swap

如下移动构造里进行swap相当于拷贝将亡值到this。
在这里插入图片描述

11.4 std::move

在这里插入图片描述
remove_reference字面意思去引用,简化如下:
在这里插入图片描述

12.完美转发

传入左值或右值在函数体里也是左值或右值,不变。
在这里插入图片描述
如下在main中,1是右值,却调用了左值构造器。
在这里插入图片描述
在这里插入图片描述

12.1 引用折叠

如下只有最后一行转换后是右值。
在这里插入图片描述

12.2 万能引用

如下不管传入右/左值,都打印万能引用。
在这里插入图片描述

12.3 std::forward

如下满足了完美转发:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如下是std::forward源码:
在这里插入图片描述

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐