[{"content":"在Docker中测试MySQL 8.x 主从复制 最近在学习MySQL的主从复制,就闲来无事在docker里面练练手,记录一下学习过程.\nSource Replication配置 拉镜像 拉默认最新版本的MySQL即可\n1 docker pull mysql 创建docker network 保证Source/Replica可以成功同步的前提是网络得通,那么第一步就是重新创建一个单独的docker network,当然默认的也可以,但是我不太喜欢大锅饭.\n1 docker network create mysql_bridge #创建MySQL专用的docker网络 创建容器 我文件夹下面的mysql_01_conf和mysql_02_conf的MySQL配置文件,照抄即可,能自己写更好.\n1 2 3 4 5 6 7 # mysql_01_conf/source.cnf [mysqld] server-id=1 #唯一ID log_bin=mysql-bin#开启binlog binlog_format=ROW#行级复制 gtid_mode=ON#开启GTID enforce_gtid_consistency=ON #强制GTID一致性 注意哈!文件必须是.cnf结尾的.\n1 2 3 4 5 6 7 # mysql_02_conf/replica.cnf [mysqld] server-id=2 #唯一ID 必须和其他的server-id不一样 relay-log=relay-bin #中继日志 gtid_mode=ON #启用gtid模式,不用老的binlog+pos enforce_gtid_consistency=ON #强制GTID一致性 read_only=ON #防止误写 我默认是只持久化配置文件,那些/var/lib/mysql我就没有同步了,毕竟是测试数据都不重要,但是生产环境还是要做好数据持久化的,bind mount或者 volume mount都可以.\n1 2 docker run -d --rm -v $PWD/mysql_01_conf:/etc/mysql/conf.d --name mysql_01 --network mysql_bridge -e MYSQL_ROOT_PASSWORD=123456 mysql #这个用来做主数据库 docker run -d --rm -v $PWD/mysql_02_conf:/etc/mysql/conf.d --name mysql_02 --network mysql_bridge -e MYSQL_ROOT_PASSWORD=123456 mysql #这个用来做从数据库 主库(Source) 先进容器\n1 docker exec -it mysql_01 mysql -uroot -p 1 2 3 4 5 6 7 8 9 10 create user \u0026#39;repl\u0026#39;@\u0026#39;%\u0026#39; identified by \u0026#39;123456\u0026#39;;#创建一个用于同步的用户 grant replication slave on *.* to \u0026#39;repl\u0026#39;@\u0026#39;%\u0026#39;;#给予所有数据库权限 flush privileges; show binary log status;#查看一下 +------------------+----------+--------------+------------------+------------------------------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+------------------------------------------+ | mysql-bin.000003 | 198 | | | 39e8fe3d-5913-11f1-a31e-3af1c8c7e730:1-5 | +------------------+----------+--------------+------------------+------------------------------------------+ 从库(Replication) 先进容器\n1 docker exec -it mysql_02 mysql -uroot -p 1 2 3 4 5 change replication source to source_host=\u0026#39;mysql_01\u0026#39;,#docker创建的第一个容器名称即可 source_user=\u0026#39;repl\u0026#39;, source_password=\u0026#39;123456\u0026#39;, source_auto_position=1;#开启GTID自动同步,必须要开的 1 2 start replica;#开始主从备份 show replica status\\G #查看一下,看到 Replica_IO_Running: Yes,Replica_SQL_Running: Yes 就是正常成功同步啦 完整步骤 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 45 46 47 48 49 50 51 52 # mysql_01 Source docker exec -it mysql_01 mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 10 Server version: 9.7.0 MySQL Community Server - GPL Copyright (c) 2000, 2026, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; mysql\u0026gt; mysql\u0026gt; mysql\u0026gt; show binary logs; +------------------+-----------+-----------+ | Log_name | File_size | Encrypted | +------------------+-----------+-----------+ | mysql-bin.000001 | 181 | No | | mysql-bin.000002 | 2997943 | No | | mysql-bin.000003 | 198 | No | +------------------+-----------+-----------+ 3 rows in set (0.002 sec) mysql\u0026gt; show binary log status; +------------------+----------+--------------+------------------+------------------------------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+------------------------------------------+ | mysql-bin.000003 | 198 | | | 39e8fe3d-5913-11f1-a31e-3af1c8c7e730:1-5 | +------------------+----------+--------------+------------------+------------------------------------------+ 1 row in set (0.001 sec) mysql\u0026gt; create user \u0026#39;repl\u0026#39;@\u0026#39;%\u0026#39; identified by \u0026#39;123456\u0026#39;; Query OK, 0 rows affected (0.076 sec) mysql\u0026gt; grant replication slave on *.* to \u0026#39;repl\u0026#39;@\u0026#39;%\u0026#39;; Query OK, 0 rows affected (0.017 sec) mysql\u0026gt; flush privileges; Query OK, 0 rows affected, 1 warning (0.018 sec) mysql\u0026gt; show binary log status; +------------------+----------+--------------+------------------+------------------------------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+------------------------------------------+ | mysql-bin.000003 | 899 | | | 39e8fe3d-5913-11f1-a31e-3af1c8c7e730:1-8 | +------------------+----------+--------------+------------------+------------------------------------------+ 1 row in set (0.001 sec) 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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 # mysql_02 Replication docker exec -it mysql_02 mysql -uroot -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \\g. Your MySQL connection id is 10 Server version: 9.7.0 MySQL Community Server - GPL Copyright (c) 2000, 2026, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. mysql\u0026gt; change replication source to -\u0026gt; source_host=\u0026#39;mysql_01\u0026#39;, -\u0026gt; source_user=\u0026#39;repl\u0026#39;, -\u0026gt; source_password=\u0026#39;123456\u0026#39;, -\u0026gt; source_auto_position=1; Query OK, 0 rows affected, 2 warnings (0.090 sec) mysql\u0026gt; start replica; Query OK, 0 rows affected (0.068 sec) mysql\u0026gt; show replica status\\G *************************** 1. row *************************** Replica_IO_State: Waiting for source to send event Source_Host: mysql_01 Source_User: repl Source_Port: 3306 Connect_Retry: 60 Source_Log_File: mysql-bin.000003 Read_Source_Log_Pos: 899 Relay_Log_File: relay-bin.000003 Relay_Log_Pos: 1116 Relay_Source_Log_File: mysql-bin.000003 Replica_IO_Running: Yes # Replica_SQL_Running: Yes # Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 0 Last_Error: Skip_Counter: 0 Exec_Source_Log_Pos: 899 Relay_Log_Space: 2999300 Until_Condition: None Until_Log_File: Until_Log_Pos: 0 Source_SSL_Allowed: Yes Source_SSL_CA_File: Source_SSL_CA_Path: Source_SSL_Cert: Source_SSL_Cipher: Source_SSL_Key: Seconds_Behind_Source: 0 Source_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 0 Last_SQL_Error: Replicate_Ignore_Server_Ids: Source_Server_Id: 1 Source_UUID: 39e8fe3d-5913-11f1-a31e-3af1c8c7e730 Source_Info_File: mysql.slave_master_info SQL_Delay: 0 SQL_Remaining_Delay: NULL Replica_SQL_Running_State: Replica has read all relay log; waiting for more updates Source_Retry_Count: 10 Source_Bind: Last_IO_Error_Timestamp: Last_SQL_Error_Timestamp: Source_SSL_Crl: Source_SSL_Crlpath: Retrieved_Gtid_Set: 39e8fe3d-5913-11f1-a31e-3af1c8c7e730:1-8 Executed_Gtid_Set: 39e8fe3d-5913-11f1-a31e-3af1c8c7e730:1-8, 48d2ce30-5913-11f1-aad7-7ae654cbeca4:1-5 Auto_Position: 1 Replicate_Rewrite_DB: Channel_Name: Source_TLS_Version: Source_public_key_path: Get_Source_public_key: 0 Network_Namespace: 1 row in set (0.002 sec) 至此结束了,操作还是很简单的,主要还是要理解Mysql主从同步的原理.\n验证 其他知识 主从复制核心日志 Source Replication Binlog relay log 从库同步数据原理: 从库有两个重要的线程:IO和SQL,\nIO线程负责拉取主库binlog,然后写入本地relay log SQL线程执行relay log才是真正的数据同步 主从延迟: show replica status\\G查看,有时候发现Seconds_Behind_Source:60,落后主库60s,这个是正常的,有可能是\n大事务,一次几百万行数据 SQL慢,从库执行不过来 锁等待,卡住 单线程复制,老版本问题 磁盘差,IO跟不上 也可能是刻意配置的 binlog三种格式 STATEMENT 记录sql,执行SQL,哪都好,问题是:uuid(),now(),rand()大概率(99.99\u0026hellip; \u0026hellip;%)不一样\nROW 记录数据变化,最安全,生产环境主流。\nMIXED 我不知道谁在用\n生产环境重要配置 半同步复制\n异步(默认):主库写完直接返回,风险点是:主库突然挂了,从库可能没同步到.\n建议至少一个从库收到才返回,更安全.\n常见命令: 1 2 3 4 5 6 stop replica; #停止复制 start replica; #启动复制 reset replica all;#重置复制,生产环境勿用,会导致事务报错,清空复制信息。 show variables like \u0026#39;%gtid%\u0026#39;;#查看GTID show binary logs;#查看binlog show relaylog events;#查看relaylog 主从复制的模式 基于位置Position 是传统模式,例如：mysql-bin.000001 + pos=157,\nbinlog切换容易错 恢复麻烦 很多老系统还在用。\n基于GTID MySQL8 主流方案,\n自动定位 自动续传 故障恢复方便 主从切换简单 生产环境都GTID。\n","date":"2026-05-27T01:12:21+08:00","image":"https://www.bing.com/th?id=OHR.WartburgCastle_EN-CN1100541820_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/docker_mysql_source_replication/","title":"在Docker中测试MySQL 8.x 主从复制"},{"content":"Armbian 手动配置AP Device：Phicomm N1\nOS: Armbian jammy\n前提条件：iw list | grep -A5 \u0026quot;Supported interface modes\u0026quot; 必须同时出现 managed和AP\n1 2 3 4 5 6 Supported interface modes: * IBSS * managed * AP * P2P-client * P2P-GO 我个人是2.4G连接无线网，然后创建5G的AP（我的随身WiFi只支持2.4G）。~\n实践出真知，我连接的2.4G的WiFi，AP也会被降级到2.4Ghz。\n安装 hostapd、dnsmasq 1 sudo apt install hostapd dnsmasq 配置虚拟网卡 1 2 sudo iw dev wlan0 interface add ap0 type __ap #wlan0 是根据实际物理网卡选择的，先看一下能不能创建名为：ap0的虚拟网卡。 1 2 3 4 5 6 7 8 9 10 vi /etc/systemd/network/20-ap0.network ------ [Match] Name=ap0 [Network] Address=192.168.88.1/24 DHCPServer=no #专业的事情交给dnsmasq去做 #我需要独立网段，这个ap0就不会被NAT和转发 配置hostapd 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 vi /etc/hostapd/hostapd.conf ------ interface=ap0 driver=nl80211 ssid=Armbian_5G ignore_broadcast_ssid=0 # 1=隐藏，0=可见 # 5GHz 关键 hw_mode=a # \u0026#39;a\u0026#39; = 5GHz, \u0026#39;g\u0026#39; = 2.4GHz channel=36 # 可选 36,40,44,48 等 ieee80211n=1 ieee80211ac=1 # 5G 必须打开 ac wpa=2 wpa_passphrase=StrongPass123 wpa_key_mgmt=WPA-PSK rsn_pairwise=CCMP 只要你连接2.4Ghz的WiFi，那你AP也的是同频率和频段的，上不了5Ghz。\n让dnsmasq只对ap0提供DHCP 1 2 3 4 5 vi /etc/dnsmasq.d/ap0.conf ------ interface=ap0 bind-interfaces dhcp-range=192.168.88.100,192.168.88.150,12h 开机自启动AP 开机自动创建 ap0\n1 2 3 4 5 6 7 8 9 10 11 12 13 [Unit] Description=Create AP After=network-pre.target Before=hostapd.service [Service] Type=oneshot ExecStart=/sbin/iw dev wlan0 interface add ap0 type __ap ExecStartPost=/sbin/ip link set ap0 up RemainAfterExit=yes [Install] WantedBy=multi-user.target 除此之外，还需要修改一下/lib/systemd/system/hostapd.service 和 /lib/systemd/system/dnsmasq.service,让hostapd和dnsmasq等ap0启动成功之后在创建AP，开始DHCP，要不然开机后，AP必然创建失败，你还要手动重启一遍hostapd和dnsmasq。\n1 2 3 4 5 6 7 [Unit] Description=... After=network.target After=ap0.service Requires=ap0.service ... ... 1 sudo systemctl enable systemd-networkd hostapd dnsmasq ap0.service 注意！ 如果你一直在用netplan管理网络的话，那你需要注意一个地方！你需要把netplan的底层从NetworkManager换成networkd\n1 2 3 network: version: 2 renderer: networkd 大部分不能创建AP的问题就是没有用networkd，可以用networkctl status ap0查看ap状态，如果看到State: routable (configured)那就基本上没有问题了，\n然后sudo reboot，或者sudo systemctl restart systemd-networkd hostapd dnsmasq ap0.service就可以了\n","date":"2026-03-22T00:25:21+08:00","image":"https://www.bing.com/th?id=OHR.GB25Anni_EN-CN0990242803_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/armbian_use_netplan_setup_ap/","title":"Armbian使用netplan配置AP"},{"content":"使用Qemu跑Openwrt当旁路由并扩容 Openwrt先跑起来 本人选择的Openwrt是官方原版的ext4带EFI版本的 generic-ext4-combined-efi.img.gz，虽然带了EFI，但是我好像还是没有直接启动成功，加了一个bios引导才成功，不知道为什么。反正先不求甚解，能跑起来再说。\n我是准备拿openwrt跑个旁路由的，\n1 2 3 4 5 6 7 8 9 10 qemu-system-aarch64 \\ -M virt \\ -accel hvf \\ -cpu host \\ -smp 1 \\ -m 512 \\ -drive if=virtio,file=./openwrt.img,format=raw,if=virtio \\ -bios ./QEMU_EFI.fd \\ -nographic \\ -nic vmnet-bridged,ifname=en0,model=virtio-net-pci#搞了一个桥接，默认接到openwrt里面的eth0，然后到br-lan网桥上，也就是lan侧 QEMU_EFI.fd下载地址，前面那个qemu跑Ubuntu的post里面有。\n网络配置再修改一下 然后就配置一下系统，因为默认的网络配置是192.168.1.1和我本地的网关冲突了，需要修改一下\n1 vi /etc/config/network 1 2 3 4 5 6 7 8 9 10 11 12 13 14 config device option name \u0026#39;br-lan\u0026#39; option type \u0026#39;bridge\u0026#39; list ports \u0026#39;eth0\u0026#39; option macaddr \u0026#39;02:11:22:33:44:56\u0026#39; #这里必须要修改Mac地址，因为默认是读取你设备的真实网卡地址，而且又是和宿主机一个网口出去，会导致网络丢包，不但影响虚拟机的网络，也会导致宿主机网络质量直线下降，丢包，延迟甚至直接断网也是有可能的。 config interface \u0026#39;lan\u0026#39; option device \u0026#39;br-lan\u0026#39; option proto \u0026#39;static\u0026#39; option ipaddr \u0026#39;192.168.1.50\u0026#39;#改一下地址 option netmask \u0026#39;255.255.255.0\u0026#39; option gateway \u0026#39;192.168.1.1\u0026#39;#这里需要加个网关，因为是旁路由，直通局域网，Openwrt没有wan出口，只能直接配置网关 option dns \u0026#39;114.114.114.114\u0026#39;#没有网关了，dns肯定也是没有的，需要手动指定一个 option ip6assign \u0026#39;60\u0026#39; 配置好了之后就可以service network restart重启网络了\n基本上openwrt上需要做的也就这么多，其他设备上网关指向192.168.1.50就可以了。\nOpenwrt扩容 默认的img镜像跑起来后，可使用的空间已经所剩无几，大概只有20m左右，安装一些软件什么的都不够塞牙缝的。\n先关机！ 首先需要先停止运行openwrt，先poweroff关机，最好不要qemu直接kill掉进程，我就遇到了img文件损坏的问题了\n数据第一！先备份！ 1 cp openwrt.img openwrt.img.bk img扩容 1 qemu-img resize openwrt.img 512M #你想要多大就改多大 扩容完成就可以直接拷贝镜像至其他Linux系统，我就直接用scp拷贝到qemu里面跑的Ubuntu了\n1 scp openwrt.img user@192.168.x.x:~/ 映射img至本地设备 登录其他Linux系统，先映射img到本地设备\n1 sudo losetup -P -f ./openwrt.img 这个时候sudo fdisk -l或者lsblk应该就可以看到/dev/loop的设备了\n1 2 3 4 5 6 7 8 9 lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS loop0 7:0 0 512M 0 loop ├─loop0p1 259:3 0 128M 0 part ├─loop0p2 259:4 0 383.7M 0 part └─loop0p128 259:5 0 239K 0 part vda 253:0 0 30G 0 disk ├─vda1 253:1 0 1G 0 part /boot/efi └─vda2 253:2 0 28.9G 0 part / 然后就是重写分区表了\n1 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 sudo parted /dev/loop0 #一般么有挂其他设备就是loop0 ，根据lsblk看到的来调整 GNU Parted 3.6 Using /dev/loop0 Welcome to GNU Parted! Type \u0026#39;help\u0026#39; to view a list of commands. (parted) print #查看一下信息 Warning: Not all of the space available to /dev/loop0 appears to be used, you can fix the GPT to use all of the space (an extra 15532032 blocks) or continue with the current setting? Fix/Ignore? fix #这里需要修复一下 Model: Loopback device (loopback) Disk /dev/loop0: 8489MB Sector size (logical/physical): 512B/512B Partition Table: gpt Disk Flags: Number Start End Size File system Name Flags 128 17.4kB 262kB 245kB bios_grub 1 262kB 134MB 134MB fat16 EFI System Partition boot, legacy_boot, esp 2 134MB 537MB 402MB ext4 (parted) resizepart 2 100% #调整 2 占用100%的空间 (parted) print #查看是否修改成功 Model: Loopback device (loopback) Disk /dev/loop0: 8489MB Sector size (logical/physical): 512B/512B Partition Table: gpt Disk Flags: Number Start End Size File system Name Flags 128 17.4kB 262kB 245kB bios_grub 1 262kB 134MB 134MB fat16 EFI System Partition boot, legacy_boot, esp 2 134MB 8489MB 8355MB ext4 (parted) quit Information: You may need to update /etc/fstab. 然后再检查一下是否有问题，当然刚调整完100%有问题，fix一下就好了\n1 2 3 4 5 6 7 8 9 10 11 sudo e2fsck -f /dev/loop0p2 e2fsck 1.47.0 (5-Feb-2023) Pass 1: Checking inodes, blocks, and sizes Inode 7, i_size is 29405184, should be 33607680. Fix\u0026lt;y\u0026gt;? y #修复下 Pass 2: Checking directory structure Pass 3: Checking directory connectivity Pass 4: Checking reference counts Pass 5: Checking group summary information rootfs: ***** FILE SYSTEM WAS MODIFIED ***** rootfs: 1718/19968 files (0.0% non-contiguous), 8603/98235 blocks 扩展文件系统 1 2 sudo resize2fs /dev/loop0p2 sudo losetup -d /dev/loop0 扩展完成之后就可以把img文件拷贝回原处启动一下openwrt看看你不能起得来。一般情况下是可以的，二般情况下我就不知道了，你去问GPT Claude吧。\n","date":"2025-08-20T19:17:32+08:00","image":"https://www.bing.com/th?id=OHR.SwedenOwl_EN-CN0544153650_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/running_openwrt_as_a_secondary_router_with_qemu_and_expanding_disk/","title":"使用Qemu跑Openwrt当旁路由并扩容"},{"content":"最近把我的Hugo博客从原来的x86_64服务器的Docker上迁移到macOS的qemu虚拟机里面的Docker了，之前的服务器因为没有什么重要数据，一直没开防火墙，裸奔中，但是 现在我准备All-In-One了，把所有的服务都整合在qemu里面的虚拟化，现在要注意安全🔐了，但是我又不想使用iptables了，所以就改用更好用的nftables。\n但是我启动docker容器的时候，oh吼～\n1 2 ~$ docker start hugo Error response from daemon: driver failed programming external connectivity on endpoint hugo (5cf48cfcd4d592fc2fc599781dbfb98eb176f3841949c6a75ad97c56dd36be17): Unable to enable DNAT rule: (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 1313 -j DNAT --to-destination 172.17.0.2:1313 ! -i docker0: iptables: No chain/target/match by that name. 报错了。。。\niptables的规则不能生效，Nat\u0026amp;Forward 规则不生效。那怎么可能生效啊，iptables我已经不用了，改用nftables了。\n然后问了一下哎GPT，它就说。哎～，Docker和iptables这两个锁死的，你要是想用Docker，那你要不然就放弃nftables，改回iptables，要不然，你就别用了，别的方法都很折腾，至少我觉得很麻烦。\n然后还有一种方法就是转换一下，把iptables的规则转成nftables的后端实现，命令很简单，如下：\n1 2 sudo update-alternatives --set iptables /usr/sbin/iptables-nft sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-nft 这样转换一下实现就可以正常启动docker容器了。\n还有就是后续docker在开机启动时可能还会启动失败，这个没有关系的，重启一下docker，让他重新写一下iptables的规则就行了\n1 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 sudo nft list ruleset#验证一下，看看是不是有自动生成的规则 ... ... # Warning: table ip filter is managed by iptables-nft, do not touch! table ip filter { chain DOCKER { ip daddr 172.17.0.2 iifname != \u0026#34;docker0\u0026#34; oifname \u0026#34;docker0\u0026#34; tcp dport 1313 counter packets 0 bytes 0 accept } chain DOCKER-ISOLATION-STAGE-1 { iifname \u0026#34;docker0\u0026#34; oifname != \u0026#34;docker0\u0026#34; counter packets 0 bytes 0 jump DOCKER-ISOLATION-STAGE-2 counter packets 0 bytes 0 return } chain DOCKER-ISOLATION-STAGE-2 { oifname \u0026#34;docker0\u0026#34; counter packets 0 bytes 0 drop counter packets 0 bytes 0 return } chain FORWARD { type filter hook forward priority filter; policy accept; counter packets 0 bytes 0 jump DOCKER-USER counter packets 0 bytes 0 jump DOCKER-ISOLATION-STAGE-1 oifname \u0026#34;docker0\u0026#34; ct state related,established counter packets 0 bytes 0 accept oifname \u0026#34;docker0\u0026#34; counter packets 0 bytes 0 jump DOCKER iifname \u0026#34;docker0\u0026#34; oifname != \u0026#34;docker0\u0026#34; counter packets 0 bytes 0 accept iifname \u0026#34;docker0\u0026#34; oifname \u0026#34;docker0\u0026#34; counter packets 0 bytes 0 accept } chain DOCKER-USER { counter packets 0 bytes 0 return } } ","date":"2025-08-14T08:53:43+08:00","image":"https://www.bing.com/th?id=OHR.PrinceChristianSound_EN-CN2366123963_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/docker_change_iptables_backend/","title":"Linux上nftables和Docker默认iptables规则冲突问题记录📝"},{"content":"服务迁移至QEMU记录 之前国补在狗东入手了Mac mini 万兆丐版，想着我那n2920的小主机也是时候该退休了。小主机是2019年买的，中途ssd坏过一次，万幸数据救回来了。虽然换了ssd，但是也不是新的ssd是从另一个设备上拆下来的。就怕二次出现故障，加上服役6年多了，配置也很低。所以索性就把服务全部都迁移到mac上。\nCPU Intel N2920 Memry 32G Disk 西数1T（PMR的蓝盘，还算抗造） OS Ubuntu 24.04.2 LTS 现在最大的问题莫过于架构问题了，n2920虽然性能拉跨，但是胜在x86_64的指令集，很多软件跑起来都没有问题。但是如果要转qemu虚拟化来跑的话，就要从x86_64过渡到arm64也就是aarch64了。我表示很担心兼容性问题。但是问了一下GPT，他表示问题不大，完全可以迁移，大部分软件都有aarch64版本，要是担心兼容性问题，可以用Rosetta 2转译运行，但是会有部分性能损失。我表示 不怕兼容性问题，就怕不能榨干M4的所有性能，那就直接装aarch64版本的系统吧！\n安装qemu 这个很简单！直接用brew安装就完事了，并且qemu官网也是推荐的brew安装的。\n1 brew install qemu Qemu创建虚拟化主机 创建磁盘 在创建虚拟主机之前，需要创建一块虚拟磁盘给虚拟机用，大小就30G吧，毕竟是丐版，磁盘给太大，对我这256G的磁盘来说就不礼貌了。\n1 qemu-img create -f qcow2 ubuntu-arm64.qcow2 30G 下载Ubuntu iso镜像 可以去 Ubuntu for Arm 官网下载镜像。我一般是不用GUI的，就直接下载server版本了。如果要下载desktop版本的，还需要配置VNC，麻烦不说，页面可能会卡顿。还不如我ssh直接敲命令方便。\n1 wget https://cdimage.ubuntu.com/releases/24.04/release/ubuntu-24.04.2-live-server-arm64.iso 下载Bootloader 虽然不知道为什么要这个Bootloader，但是不用就无法启动。按道理说qemu应该是自带引导的，不管了，不求甚解，能用就行。\n1 wget https://releases.linaro.org/components/kernel/uefi-linaro/16.02/release/qemu64/QEMU_EFI.fd 启动虚拟机安装系统 1 2 3 4 5 6 7 8 9 10 11 12 13 sudo qemu-system-aarch64 \\ -M virt \\ #通用虚拟化平台，支持加速 -accel hvf \\ #虚拟化加速 -cpu host \\ #直接使用本机CPU -smp 2 \\ #分配2核心 -m 2048 \\ #2G内存足以 -drive if=virtio,file=./ubuntu-arm64.qcow2,format=qcow2 \\ #使用刚刚创建的虚拟磁盘用作虚拟机的磁盘 -cdrom ./ubuntu-24.04.2-live-server-arm64.iso \\ #下载的arm64的Ubuntu镜像 -boot d \\ #从cdrom启动 -bios ./QEMU_EFI.fd \\ #引导文件 -device virtio-gpu-pci \\ #虽然不需要GUI，但是保不齐后续我想玩一下VNC呢 -nographic \\ #无GUI模式，所有操作在terminal中完成 -nic vmnet-bridged,ifname=en0,model=virtio-net-pci #macos特有的桥接方式。ifname 为需要桥接的目的网络接入接口 安装系统 这个就没有什么好说的了，下一步，下一步，设置密码，装内核，重启就可以了。\n配合 tmux 一键自动运行 其实也可以设置开机自动运行的，但是我没有设置，因为偶尔还会用mac和老婆玩玩星露谷和我的世界，那就需要运行的时候在手动启动一下吧。\n1 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 #!/bin/bash QEMU_CMD=\u0026#34;sudo /opt/homebrew/bin/qemu-system-aarch64 \\ -M virt \\ -accel hvf \\ -cpu host \\ -smp 2 \\ -m 2048 \\ -drive if=virtio,file=/xxx/ubuntu-arm64.qcow2,format=qcow2 \\ -boot d \\ -bios /xxx/QEMU_EFI.fd \\ -nographic \\ -nic vmnet-bridged,ifname=en0,model=virtio-net-pci \u0026#34; TMUX_SESSION_NAME=\u0026#34;qemu\u0026#34; # 检查 tmux 会话是否已经在运行 if ! /opt/homebrew/bin/tmux has-session -t \u0026#34;$TMUX_SESSION_NAME\u0026#34; 2\u0026gt;/dev/null; then echo \u0026#34;Starting new tmux session \u0026#39;$TMUX_SESSION_NAME\u0026#39; and running QEMU...\u0026#34; # 启动 tmux 会话并在其中执行 QEMU 命令 # -d 确保 tmux 会话在创建后立即分离，不会阻塞启动进程 /opt/homebrew/bin/tmux new-session -s \u0026#34;$TMUX_SESSION_NAME\u0026#34; -d \u0026#34;$QEMU_CMD\u0026#34; echo \u0026#34;QEMU started in tmux session \u0026#39;$TMUX_SESSION_NAME\u0026#39;.\u0026#34; else echo \u0026#34;Tmux session \u0026#39;$TMUX_SESSION_NAME\u0026#39; already exists. QEMU might be running.\u0026#34; fi 服务迁移 大部分服务都可以正常迁移到arm64环境下，go，java，nodejs，python这些都么有问题，我需要的nginx，tailscale，docker还有aria2c等都正常！\n总结 目前看来arm64用作生产力也不是不行。qemu虚拟化出来的性能也还可以。留待以后慢慢使用感受吧！\n","date":"2025-07-14T16:07:12+08:00","image":"https://www.bing.com/th?id=OHR.NaqsheRustam_EN-CN2220219039_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/migration_to_qemu_virtual_machine_record/","title":"x86_64 Linux 服务器 手动迁移部分服务至qemu arm64 虚拟机中"},{"content":"首先Openssl一般都是用来生成密钥的，它支持\n对称密钥算法：AES、DES、3DES、RC4、ChaCha20 等； 非对称密钥算法：RSA、DSA、ECDSA、EdDSA 等； 密码协议：TLS/SSL、DTLS、S/MIME、PKCS 等。 一个最简单的例子：\n1 2 3 openssl genpkey -algorithm RSA -out key.pem #生成私钥 openssl req -new -key key.pem -out cert.csr #生成签名请求文件 openssl x509 -req -days 365 -in cert.csr -signkey key.pem -out cert.pem #响应请求签署证书 不知道你注意没有！我使用的 algorithm 而不是 cipher ，加密方法作为 OpenSSL 的核心，我们需要区分algorithm和cipher的区别，\nalgorithm和cipher的区别 算法（algorithm）和密码（cipher）是两个相关但不同的概念\nalgorithm通常指的是一种数学上的方法，用于加密和解密数据。例如，RSA、DSA、ECDSA、ECDH 等是一些常见的加密算法。\n而cipher则是指的是具体的实现方式，包括加密算法、填充方式、运作模式等，比如 AES-256-GCM、AES-128-CBC、ChaCha20-Poly1305 等。\n所以我们使用cipher指定具体的实现方式要更常见一点。\n加密算法 只列举常见（或者略微 有名的）\n对称加密算法 目前推荐AES-256，别的都不安全。\nAES Blowfish Camellia CAST5 IDEA SEED DES Triple DES（3des） RC4 Blowfish 和 Camellia 都是比较常见的对称加密算法（但是我们一般不用），而 CAST5 和 IDEA 相对来说已经被一些新的加密算法所取代，不再是主流的加密算法；SEED 是韩国政府推广使用的加密算法，但在国际上的应用并不广泛。后面几个则是安全有问题而不在使用了。\n非对称加密算法 RSA家族： RSA：基本的RSA加密算法。 RSA-PSS：RSA Probabilistic Signature Scheme，一种基于RSA的数字签名算法。 RSA-OAEP：RSA Optimal Asymmetric Encryption Padding，是基于RSA加密算法的一种填充方案数字签名算法。 RSA-KEM：RSA Key Encapsulation Mechanism，一种基于RSA的密钥交换算法。 RSA-CRT：RSA Chinese Remainder Theorem，一种优化RSA的计算方法（不属于签名算法或密钥交换算法）。 虽然家族成员很多，但是我们一般还是只使用rsa，因为它兼容性最好😂。\nECC家族： 支持的曲线类型包括 prime256v1、secp384r1 和 secp521r1 等。\nECDSA：(Elliptic Curve Digital Signature Algorithm) 基于椭圆曲线密码学的数字签名算法，（主要用于签名和验证，比如签发证书）。\nEdDSA：(Edwards-curve Digital Signature Algorithm) 基于蒙哥马利曲线的数字签名算法，比ECDSA更高效，更安全（更多用于消息认证、数字签名）。\nEd25519：基于Curve25519的数字签名算法，比ECDSA更高效，更安全。（用于签名和验证，比如ssh）\nECDH：(Elliptic Curve Diffie-Hellman) 基于椭圆曲线密码学的密钥协商算法，用于生成共享密钥。\nX25519：基于Curve25519的密钥交换算法，用于生成共享密钥。\nX448：基于Curve448的密钥交换算法，用于生成共享密钥。\necc家族里一般用的也挺花，ECDSA，EDDSA，Ed25519\nDSA家族： DSA家族只包含一种算法，即DSA本身。他的变种在ecc家族的ExDSA。\n∵ 它的加解密速度比较慢，但对于数字签名等场景，其安全性较高。\n∴ DSA更常用于数字签名，而RSA更常用于加密\n哈希算法 MD5 SHA-1 SHA-2系列，如SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256 SHA-3系列，如SHA3-224、SHA3-256、SHA3-384、SHA3-512 BLAKE2系列，如BLAKE2s-128、BLAKE2s-160、BLAKE2s-224、BLAKE2s-256、BLAKE2b-160、BLAKE2b-256、BLAKE2b-384、BLAKE2b-512 md5和sha1已被证实不安全，一般sha3-256或者sha-256\n提问：如果用ecc签名！为什么用ECDSA而不用Ed25519？\n实际上，使用Ed25519算法作为数字证书的签名算法是可行的，因为Ed25519签名算法的安全性已经得到广泛认可。但是，Ed25519算法并不是所有软件和硬件都支持的，因此在实际应用中可能存在兼容性问题。而且在数字证书领域，目前使用最广泛的签名算法是RSA和ECDSA，而不是Ed25519。\n创建理想的CA 首先你需要知道CA是什么！说白了！就是一个密钥对，首先就需要创建CA的私钥！这个至关重要！以后的在签发都是靠这个，所以必须要保存好。\n1 openssl ecparam -out ca.key -name prime256v1 -genkey 这一步是创建私钥，默认就是ECDSA算法，-name 可以选择 prime256v1、secp384r1 和 secp521r1\n除此之外，你要是不喜欢默认的ECDSA还可以选择EDDSA里面的ed25519，但是好像目前还没有多少使用ed25519来生成CA证书的。但是你要是喜欢尝鲜也是可以使用下面的命令来生成一个自己玩玩的。\n1 openssl genpkey -algorithm ed25519 -out ca.key 为什么这次使用 genpkey 了呢，因为ed25519不是 椭圆曲线(Elliptic Curve) 而是 Edwards 曲线(Edwards Curve) 虽然师出同门ECC，但是不是一种曲线，只能算殊途同归吧。\ngenpkey也可以生成EC但是要指定curve类型（特指ECDSA）是prime256v1、secp384r1 还是 secp521r1。比较麻烦，还是推荐使用ecparam生成ECDSA。\n1 openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:prime256v1 -out ca.key 创建证书申请csr 自签这一步可以省略，但是如果需要签发SSL证书，这一步又绕不开，所以还是循规蹈矩，也创建csr申请，而且csr证书申请中又可以填写与私钥相对应的公钥和一些其他信息，如组织名、域名等。这样，证书机构就可以通过验证这些信息来保证证书的可信度。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [ca] preserve = no x509_extensions = CA_DN copy_extensions = copy [req] prompt = no distinguished_name = CA_DN utf8 = yes req_extensions = v3_ca [CA_DN] countryName = US stateOrProvinceName = us localityName = us organizationName = us organizationalUnitName = us commonName = us emailAddress = us@us.com [v3_ca] subjectKeyIdentifier = hash basicConstraints = critical,CA:true,pathlen:2 keyUsage = keyCertSign,cRLSign nsComment = \u0026#34;US Signed ™\u0026#34; 把上面的基础配置复制到ca.cnf里面，接着再去发起CSR证书请求，命令如下：\n1 openssl req -new -key ca.key -out ca.csr -config ca.cnf req就是专门用来发起证书请求的，可以请求csr，也可以请求crt公钥哈。-new是根据输入的私钥新申请一个CSR证书请求。-config 就是使用我们上面写好的配置文件里面的信息。\n根据CSR生成CRT(生成公钥) X.509证书包含了公钥以及相关的证书信息，所以在这里就表示生成公钥。\n1 openssl req -new -x509 -key ca.key -out ca.crt -days 3650 -extensions v3_ca -config ca.cnf 这样一个简单的CA证书就签名好了。看看信息：\n1 openssl x509 -in ca.crt -text -noout 签发SSL证书 创建私钥 这一步和上面没有啥区别，看个人心情来创建\n1 2 mkdir ssl openssl ecparam -out ssl/ssl.key -name prime256v1 -genkey 然后保存下面的内容到ssl.cnf里面\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [req] prompt = no distinguished_name = ssl_DN utf8 = yes req_extensions = ssl_ext [ssl_DN] countryName = CN stateOrProvinceName = AnHui localityName = NanJing organizationName = TLS Inc organizationalUnitName = ssl commonName = *.jokeme.top #这里换成你需要保护的域名 emailAddress = xyz@jokeme.top [ssl_ext] subjectKeyIdentifier = hash basicConstraints = CA:FALSE subjectAltName = @alt_names [alt_names] DNS.1 = *.jokeme.top #这里换成你需要保护的域名 发起CSR证书申请 1 openssl req -new -key ssl/ssl.key -out ssl/ssl.csr -config ssl.conf 签署证书(生成公钥) 1 openssl x509 -req -in ssl/ssl.csr -out ssl/ssl.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 365 -extfile ssl.conf -extensions ssl_ext 无了！\nshell脚本 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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 #!/bin/bash # author : Frelon Lee # Date : 2023/04/12 12:54:27 writeCACnf() { cat \u0026lt;\u0026lt; EOF \u0026gt; conf/ca.cnf [ca] preserve = no x509_extensions = CA_DN copy_extensions = copy [req] prompt = no distinguished_name = CA_DN utf8 = yes req_extensions = v3_ca [CA_DN] countryName = CN stateOrProvinceName = AnHui localityName = HeFei organizationName = CA organizationalUnitName = CA commonName = CA emailAddress = ca@ca.com [v3_ca] subjectKeyIdentifier = hash basicConstraints = critical,CA:true,pathlen:2 keyUsage = keyCertSign,cRLSign nsComment = \u0026#34;This is CA\u0026#34; EOF } writeSSLCnf(){ cat \u0026lt;\u0026lt; EOF \u0026gt; conf/ssl.cnf [req] prompt = no distinguished_name = ssl_DN utf8 = yes req_extensions = ssl_ext [ssl_DN] countryName = CN stateOrProvinceName = AnHui localityName = HeFei organizationName = SSL organizationalUnitName = SSL commonName = YYYYYYYY emailAddress = c@c.co [ssl_ext] subjectKeyIdentifier = hash basicConstraints = CA:FALSE subjectAltName = @alt_names [alt_names] DNS.1 = YYYYYYYY DNS.2 = *.YYYYYYYY EOF } geneCA(){ openssl ecparam -out ca/ca.key -name prime256v1 -genkey openssl req -new -key ca/ca.key -out ca/ca.csr -config conf/ca.cnf openssl req -new -x509 -key ca/ca.key -out ca/ca.crt -days 3650 -extensions v3_ca -config conf/ca.cnf } geneSSL(){ openssl ecparam -out ssl.key -name prime256v1 -genkey openssl req -new -key ssl.key -out ssl.csr -config $1/conf/ssl.cnf openssl x509 -req -in ssl.csr -out ssl.crt -CA $1/ca/ca.crt -CAkey $1/ca/ca.key -CAcreateserial -days 365 -extfile $1/conf/ssl.cnf -extensions ssl_ext } initENV() { echo \u0026#34;检查openssl环境...\u0026#34; x=$(openssl version | awk \u0026#39;{print $2}\u0026#39; | awk -F. \u0026#39;{print $1}\u0026#39;) if [ $x -ge 1 ]; then echo -e \u0026#34;OpenSSL环境：\\033[;32m通过\\033[0m\u0026#34; else echo -e \u0026#34;OpenSSL环境：\\033[;31m低于1.x的版本\\033[0m\u0026#34; return 0 fi echo \u0026#34;\u0026#34; echo \u0026#34;检测文件夹是否存在...\u0026#34; if [ -d \u0026#34;ssl/\u0026#34; -a -d \u0026#34;ca/\u0026#34; -a -d \u0026#34;conf/\u0026#34; ]; then echo -e \u0026#34;检测结果：\\033[;33m已存在\\033[0m\u0026#34; else if [ ! -d \u0026#34;ssl/\u0026#34; ];then mkdir ssl fi if [ ! -d \u0026#34;ca/\u0026#34; ];then mkdir ca fi if [ ! -d \u0026#34;conf/\u0026#34; ];then mkdir conf fi echo -e \u0026#34;检测结果：\\033[;32m不存在，已创建\\033[0m\u0026#34; fi echo \u0026#34;\u0026#34; echo -e \u0026#34;检测配置文件...\u0026#34; if [ -f \u0026#34;conf/ca.cnf\u0026#34; -o -f \u0026#34;conf/ssl.cnf\u0026#34; ]; then echo -e \u0026#34;检测结果：\\033[;33m存在配置文件\\033[0m 是否保留？[y/n]:\\c\u0026#34; read ipt if [ $ipt == \u0026#34;y\u0026#34; ]; then echo -e \u0026#34;执行结果：\\033[;33m已忽略\\033[0m\u0026#34; else rm -rfv conf/ca.cnf conf/ssl.cnf writeCACnf writeSSLCnf echo -e \u0026#34;执行结果：\\033[;32m文件已重新创建\\033[0m\u0026#34; fi else writeCACnf writeSSLCnf echo -e \u0026#34;检测结果：\\033[;32m文件已创建\\033[0m\u0026#34; fi echo \u0026#34;\u0026#34; echo \u0026#34;检测是否有其他CA证书...\u0026#34; if [ -f \u0026#34;ca/ca.key\u0026#34; ]; then echo -e \u0026#34;检测结果：\\033[;31m存在其他CA密钥\\033[0m 是否保留？[y/n]:\\c\u0026#34; read selectNum if [ $selectNum == \u0026#34;n\u0026#34; ]; then rm -rfv ca/ca.key ca/ca.crt ca/ca.csr geneCA echo -e \u0026#34;执行结果：\\033[;32m已生成新的CA密钥对\\033[0m\u0026#34; openssl x509 -in ca/ca.crt -text -noout else echo -e \u0026#34;执行结果：\\033[;33m已忽略\\033[0m\u0026#34; return 0 fi else geneCA echo -e \u0026#34;检测结果：\\033[;32m已生成CA密钥对\\033[0m\u0026#34; openssl x509 -in ca/ca.crt -text -noout fi } location=`pwd` if [ $# == \u0026#34;1\u0026#34; ]; then if [ $1 == \u0026#34;init\u0026#34; ]; then initENV elif [ $1 == \u0026#34;help\u0026#34; -o $1 = \u0026#34;-h\u0026#34; -o $1 = \u0026#34;--help\u0026#34; ]; then echo \u0026#34;Script Verion: 0.0.1a\u0026#34; echo \u0026#34;首次执行：./cert.sh init \u0026#34; echo -e \u0026#34;申请证书：./cert.sh \u0026#39;example.com\u0026#39; \\033[;35meg: ./cert.sh s.com\\033[0m\u0026#34; else if [ -d \u0026#34;ssl/$1\u0026#34; ];then cd \u0026#34;ssl/$1\u0026#34; else mkdir \u0026#34;ssl/$1\u0026#34; cd \u0026#34;ssl/$1\u0026#34; fi sed -i \u0026#34;s/YYYYYYYY/$1/g\u0026#34; $location/conf/ssl.cnf geneSSL $location sed -i \u0026#34;s/$1/YYYYYYYY/g\u0026#34; $location/conf/ssl.cnf echo -e \u0026#34;执行结果：\\033[;32m成功创建SSL证书\\033[0m\u0026#34; openssl x509 -in ssl.crt -text -noout fi else echo \u0026#34;Script Verion: 0.0.1a\u0026#34; fi 这一次，又学了一遍openssl，我又写了个小脚本，方便生成和管理自签名的CA和SSL证书。\n具体用法就是 ./cert.sh init 创建初始化环境，./cert.sh xx.com 创建ssl证书。默认的算法都是ECDSA，觉得不妥可以在脚本里面自己修改。\n","date":"2023-04-12T19:32:27+08:00","image":"https://www.bing.com/th?id=OHR.RioArazas_EN-CN2093024655_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/learn_openssl_again/","title":"Openssl再次学习"},{"content":"MySql学习笔记 基础知识： 冷知识：schema = database。show databases; = show schemas;\nMySQL数据类型 数值类型 类型 大小 范围（有符号） 范围（无符号） TINYINT 1 Bytes -128 到 127 0 到 255 SMALLINT 2 Bytes -32768 到 32767 0 到 65535 MEDIUMINT 3 Bytes -8388608 到 8388607 0 到 16777215 INT或INTEGER 4 Bytes -2147483648 到 2147483647 0 到 4294967295 BIGINT 8 Bytes -9223372036854775808 到 9223372036854775807 0 到 18446744073709551615 FLOAT 4 Bytes -3.402823466E+38 到 3.402823466E+38 反正很大，略 DOUBLE 8 Bytes -1.7976931348623157E+308 到 1.7976931348623157E+308 反正很大，略 DECIMAL 对DECIMAL(M,D) ，如果M\u0026gt;D，为M+2否则为D+2 依赖于M和D的值 依赖于M和D的值 日期和时间类型 类型 大小 ( bytes) 范围 格式 DATE 3 1000-01-01/9999-12-31 YYYY-MM-DD TIME 3 \u0026lsquo;-838:59:59\u0026rsquo;/\u0026lsquo;838:59:59\u0026rsquo; HH:MM:SS YEAR 1 1901/2155 YYYY DATETIME 8 \u0026lsquo;1000-01-01 00:00:00\u0026rsquo; 到 \u0026lsquo;9999-12-31 23:59:59\u0026rsquo; YYYY-MM-DD hh:mm:ss TIMESTAMP 4 \u0026lsquo;1970-01-01 00:00:01\u0026rsquo; UTC 到 \u0026lsquo;2038-01-19 03:14:07\u0026rsquo; UTC结束时间是第 2147483647 秒，北京时间 2038-1-19 11:14:07，格林尼治时间 2038年1月19日 凌晨 03:14:07 YYYY-MM-DD hh:mm:ss 字符串类型 类型 大小 CHAR 0-255 bytes VARCHAR 0-65535 bytes TINYBLOB 0-255 bytes TINYTEXT 0-255 bytes BLOB 0-65 535 bytes TEXT 0-65 535 bytes MEDIUMBLOB 0-16 777 215 bytes MEDIUMTEXT 0-16 777 215 bytes LONGBLOB 0-4 294 967 295 bytes LONGTEXT 0-4 294 967 295 bytes root改密码登录 Mysql安装完成以后，默认的root用户使用 auth_socket 验证方式，而不是caching_sha2_password密码方式，可以使用这个命令来验证一下：\n1 select authentication_string ,User,Host,plugin from user; 而auth_socket是根据当前登录用户来验证身份的，所以无法使用密码方式登录Mysql。所以要想Mysql可以通过密码登录那就需要修改其plugin为 caching_sha2_password 。可以使用下面的语句来修改：\n1 2 alter user \u0026#39;root\u0026#39;@\u0026#39;localhost\u0026#39; identified with caching_sha2_password by \u0026#39;12345\u0026#39;; flush privileges; 有一些网站上的教程说使用 mysql_native_password 作为验证插件，这也可以，但是不推荐，不到万不得已还是用新的 caching_sha2_password 好一点。\n字符集 在使用Mysql的时候经常会遇到各种中文字符乱码的问题，那就可能是charset设置有错误，所以在创建数据库的时候就推荐：\n1 create database xxx default charset utf8mb4; 密码策略 当修改密码但是不符合Mysql的策略时，可能会遇到下面的错误： ERROR 1819 (HY000): Your password does not satisfy the current policy requirements 但是你依然想要修改密码，好的！下面的sql语句可以帮到你：\n1 2 3 4 SHOW VARIABLES LIKE \u0026#39;validate_password%\u0026#39;; # 可以修改以下参数，来设置弱密码，虽然不建议，但是用起来真的很爽！😍 SET GLOBAL validate_password.policy=LOW; SET GLOBAL validate_password.length=4; 查询数据库版本 1 select version() 数据库操作 1 2 3 4 5 create database (if not exists) xxx (default charset utf8mb4); drop database (if exists) xxx; show databases; select database(); use xxx; 数据表结构操作 查询相关操作 1 2 3 show tables; desc xxx; show create table xxx;# 查询创建表的语句 创建相关操作 1 2 3 4 5 6 create table xxx( field type (comment \u0026#39;注释\u0026#39;), field type (comment \u0026#39;注释\u0026#39;), ... ... field type (comment \u0026#39;注释\u0026#39;) )[comment \u0026#39;注释\u0026#39;] 修改相关操作 添加字段 1 alter table xxx add field type (comment \u0026#39;xxx\u0026#39;); 修改字段 1 2 alter table xxx modify field new_type;# 只修改type alter table xxx change field new_field type (comment \u0026#39;xxx\u0026#39;);# 修改field和type 删除字段 1 alter table drop field; 修改数据表的名称 1 alter table xxx rename to xxx; 删除相关操作 1 2 drop table (if exists) xxx; truncate table xxx;# 清空所有数据 数据表数据操作 插入相关操作 给指定字段插入数据 1 insert into xxx (field1 field2...)values(value1,value2...); 给全部字段插入数据 1 insert into xxx values(value1,value2...); 批量插入数据 1 2 insert into xxx (field1 field2...)values(value1,value2...),(value1,value2...),(value1,value2...);# 批量给指定字段插入数据 insert into xxx values(value1,value2...),(value1,value2...),(value1,value2...);# 批量给所有字段插入数据 更新相关操作 1 update xxx set field1=value1, field2=value2, ... (where 条件)# 没有where表示修改整个表 删除相关操作 1 delete from xxx (where 条件)# 不加条件就是删除所有的数据 查询相关操作 下面是一个包含了各种情况的数据查询语句，sql里面查询是使用次数最多的，当时为了应对各种各样的需求，就有了各种各样的查询语句。\n1 select field1,field2 from xxx where 条件 group by 条件 having 条件 order by 条件 limit 条件 基本查询 1 2 3 4 select field1, field2, ... from xxx select field1 as a, field2 as b, ... from xxx# 设置别名 select * from xxx; select distinct (* | field) from xxx;# 去重查询 条件查询(where) 1 select field1, field2, ... from xxx where 条件 具体有哪些条件可以看下面的：\n\u0026gt;、 \u0026lt;、 =、!=、between x and x、in(x)、like、is null、and \u0026amp;\u0026amp; 、or || 、**not !**等\n1 2 3 select * from xxx where age in (10,20,30); select * from xxx where age = 10 or age = 20 or age = 30 # 上面两种写法一个意思，in就是来简化这种多or情况的 1 2 3 select * from xxx where age between 20 and 40; select * from xxx where age \u0026gt;= 20 and age \u0026lt;=40; # 这两条语句也是一样的between...and...是包括查询的数据本身的 1 2 select * from xxx where name like \u0026#39;__\u0026#39;;# 查询任意满足两个字符的的数据； select * from xxx where name like \u0026#39;张%\u0026#39;# 查询张开头的； 聚合函数 min、max、sum、count、avg\n作用于数据表里所有的字段，很简单就不多介绍了，一个例子就够了\n1 select sum(age) from xxx; 分组查询(group by) 1 select * from xxx (where 条件) group by field (having 条件) 其中 where 里面不可以使用聚合函数，但是 having 里面可以\n1 2 select city,count(city) from xxx where age \u0026gt; 20 group by city having count(city) \u0026gt; 10; # 查询年龄大于20岁的，并且分组后city的数量大于10的记录 排序查询(order by) 也很简单就 order by + 条件\n1 2 select * from xxx order by age,name desc;# 不写默认是asc升序 # 先按field1排序，再按field2排序 分页查询(limit) limit 的用法很简单，limit 起始索引，页面展示数\n1 2 select * from xxx limit 10;# 从0开始查询10条数据 select * from xxx limit 10,20;# 从第十条数据开始，第二页数据展示20条数据； SQL冷知识：虽然我们的sql编写顺序为 select -\u0026gt; from -\u0026gt; where -\u0026gt; group by -\u0026gt; having -\u0026gt; order by -\u0026gt; limit\n但是真正的执行顺序为 from -\u0026gt; where -\u0026gt; group by -\u0026gt; having -\u0026gt; select -\u0026gt; order by -\u0026gt; limit 也就是说在前面执行的别名可以在后面执行过程中使用，而没有执行的别名无法在前面使用\n用户管理 创建用户 1 2 use mysql; create user \u0026#39;f1\u0026#39;@\u0026#39;localhost\u0026#39; identified by \u0026#39;123456\u0026#39;;# 如果需要任意主机登录用%来替换localhost； 修改用户 1 2 3 use mysql; alter user \u0026#39;f1\u0026#39;@\u0026#39;%\u0026#39; identified with mysql_native_password by \u0026#39;xxx\u0026#39;;# 这个是MySQL之前的plugin存储密码的方式 alter user \u0026#39;f1\u0026#39;@\u0026#39;%\u0026#39; identified with caching_sha2_password by \u0026#39;xxx\u0026#39;;# 这个是最新的Mysql存储密码的方式 删除用户 1 2 use mysql; drop user \u0026#39;f1\u0026#39;@\u0026#39;localhost\u0026#39;; 权限管理 首先来介绍一下Mysql里面常见的那些权限：\nall/all privileges 所有权限 select 查询记录 insert 插入记录 update 修改记录 delete 删除记录 alter 修改表 drop 删除库/表/视图 create 创建库/表 查询用户权限 1 show grants for \u0026#39;f1\u0026#39;@\u0026#39;%\u0026#39;; 授权用户 1 grant select on scheme.table to \u0026#39;f1\u0026#39;@\u0026#39;%\u0026#39;;# 如果需要给所有数据库都授权，那就用 *.* 来替代 schema.table 取消权限 1 revoke update on schema.table from \u0026#39;f1\u0026#39;@\u0026#39;%\u0026#39;; SQL函数 字符串函数 函数名称 功能 concat(x) 拼接字符串 lower(x) 转小写 upper(x) 转大写 lpad(x,y,z) 👈左填充 rpad(x,y,z) 👉右填充 trim(x) 去除头尾空格 substring(x,y,z) 切分字符串 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 select concat(\u0026#34;你好\u0026#34;,\u0026#34; halo\u0026#34;); #+-------------------------+ #| concat(\u0026#34;你好\u0026#34;,\u0026#34;halo\u0026#34;) | #+-------------------------+ #| 你好halo | #+-------------------------+ select lower(\u0026#34;QWER\u0026#34;); #+---------------+ #| lower(\u0026#34;QWER\u0026#34;) | #+---------------+ #| qwer | #+---------------+ # upper()略 select lpad(\u0026#34;12\u0026#34;,5,\u0026#34;#\u0026#34;); #+------------------+ #| lpad(\u0026#34;12\u0026#34;,5,\u0026#34;#\u0026#34;) | #+------------------+ #| ###12 | #+------------------+ # 一个参数为需要填充的字符串，第二个参数是填充以后的总字符，第三个参数是以什么字符串填充 # rpad()略 select trim(\u0026#34; 拿 你号 \u0026#34;); #+---------------------------------+ #| trim(\u0026#34; 拿 你号 \u0026#34;) | #+---------------------------------+ #| 拿 你号 | #+---------------------------------+ select substring(\u0026#34;ABC的风格\u0026#34;,2,3); #+-------------------------------+ #| substring(\u0026#34;ABC的风格\u0026#34;,2,3) | #+-------------------------------+ #| BC的 | #+-------------------------------+ 注意substring和别的语言不一样，第二个参数表示从哪里开始，sql里面是从索引是从1开始，也就是说，第一个就是1，第二个就是2。第三个表示要截取几个字符这个和别的语言一样。\n那么这些函数怎么使用呢？\n1 2 update xxx set id = lpad(id,6,0); # 该操作是把原来的id补0；还有一个前提，修改的field是字符串哈！ 数值函数 函数名称 功能 ceil(x) 向上取整 floor(x) 向下取整 mod(x,y) 返回x/y的余数 rand() 随机数0-1 round(x,y) x四舍五入保留y位小数 下面这个小例子生产随机6位数就用到了上面的函数。\n1 select lpad(round(rand()*1000000,0),6,\u0026#34;0\u0026#34;); 日期函数 curdate() 获取当前日期 curtime() 获取当前时间 now() 获取当前日期时间 year(date) 获取date的年 month(date) 获取date的月 day(date) 获取date的日 date_add(date,interval x type) 获取date x 个type后的日期 datediff(date,date) 获取两个date间隔的时间 一个小例子就可以了\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 select concat(curdate(),\u0026#34; | \u0026#34;,curtime(),\u0026#34; | \u0026#34;,year(now()),\u0026#34; | \u0026#34;,date_add(now(),interval 30 day)); #+-------------------------------------------------------------------------------------------+ #| concat(curdate(),\u0026#34; | \u0026#34;,curtime(),\u0026#34; | \u0026#34;,year(now()),\u0026#34; | \u0026#34;,date_add(now(),interval 30 day)) | #+-------------------------------------------------------------------------------------------+ #| 2023-03-10 | 16:09:24 | 2023 | 2023-04-09 16:09:24 | #+-------------------------------------------------------------------------------------------+ select datediff(curdate(),\u0026#34;2021-11-11\u0026#34;); #+----------------------------------+ #| datediff(curdate(),\u0026#34;2021-11-11\u0026#34;) | #+----------------------------------+ #| 484 | #+----------------------------------+ select name,datediff(curdate(),birthday) as bd from user order by bd desc; 流程控制 if(value,t,f) value为true返回t，否则返回f ifnull(value1,value2) value1不为空返回value1，否则返回value2 case when value1 then res1\u0026hellip; else x end value1为true，返回res1\u0026hellip; 否则返回x case when expr then res1\u0026hellip; else x end expr = true，返回res1\u0026hellip; 否则返回x 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 select name,case address when \u0026#34;上海\u0026#34; then \u0026#34;大城市\u0026#34; when \u0026#34;安徽\u0026#34; then \u0026#34;小城市\u0026#34; end as \u0026#34;所住城市规模\u0026#34; from users; #+--------+--------------+ #| name | 所住城市规模 | #+--------+--------------+ #| TianLu | 小城市 | #| HeTian | 大城市 | #+--------+--------------+ # 把上面的写法换成下面的也可以 select name,case when address = \u0026#34;上海\u0026#34; then \u0026#34;大城市\u0026#34; when address = \u0026#34;安徽\u0026#34; then \u0026#34;小城市\u0026#34; end as \u0026#34;所住城市规模\u0026#34; from users; #+--------+--------------+ #| name | 所住城市规模 | #+--------+--------------+ #| TianLu | 小城市 | #| HeTian | 大城市 | #+--------+--------------+ 约束 非空约束 not null 唯一约束 unique 主键约束 primary key 默认约束 default 检查约束（8.0.16） check 外键约束 foreign key 自增 auto_increment\n外键约束 字段可以同时有多个约束，别的约束都简单，那就都杂糅在外键约束里面记录了。\n先说一下外键约束的作用：可以保证数据的一致性和完整性\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 create table school_t1( name varchar(20) unique not null, sid varchar(6) primary key ); insert into school_t1 values (\u0026#34;安徽大学\u0026#34;,\u0026#34;ah003\u0026#34;),(\u0026#34;江西理工大学\u0026#34;,\u0026#34;jx005\u0026#34;); create table user_t1( name varchar(10) not null, age int not null default 18 check(age \u0026gt; 18 and age \u0026lt; 80), uuid varchar(20) primary key, city varchar(20) not null, phone char(11) not null unique, user_name varchar(30) unique, school varchar(20), constraint fk_school foreign key (school) references school_t1(sid)#这是在创建表的时候就添加约束 ); insert into user_t1 values(\u0026#34;frelon\u0026#34;,23,\u0026#34;w2f5-gsa-f3f\u0026#34;,\u0026#34;安徽\u0026#34;,\u0026#39;12345678912\u0026#39;,null,\u0026#34;ah003\u0026#34;),(\u0026#34;Jing\u0026#34;,22,\u0026#34;adwb-ser2-saw\u0026#34;,\u0026#34;江西\u0026#34;,\u0026#39;12345678911\u0026#39;,null,\u0026#34;jx005\u0026#34;); # 而当试图插入外键约束不允许的值就会报错 insert into user_t1 values(\u0026#34;jsts\u0026#34;,26,\u0026#34;wrde-pna-bny\u0026#34;,\u0026#34;安徽\u0026#34;,\u0026#39;12345678916\u0026#39;,null,\u0026#34;ah006\u0026#34;); # ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`ut`.`user_t1`, CONSTRAINT `fk_school` FOREIGN KEY (`school`) REFERENCES `school_t1` (`sid`)) 创建外键约束 还可以在表创建以后再添加约束\n1 2 3 alter table user_t1 add constraint fk_school foreign key(school) references school_t1(sid); # 还可以在创建表的时候写像下面这样写 constraint fk_school foreign key (school) references school_t1(sid) 删除外键约束 1 alter table user_t1 drop foreign key fk_school; 删除/更新行为 一般这种 连级操作 都是并不允许使用的。\n默认有以下几种行为RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT\nrestrict 和 no action 一样！更新记录时，如果没有对应的外键记录，则不允许更新。删除时，还存在着外键记录，则也一样不允许删除。\ncascade 更新/删除时会检查外键，如果有那也一起更新/删除。\nset null 更新/删除时，把外键相应记录设置为null。\nset default 更新/删除时，把外键设置为default。\n设置也很简单，只需要在设置外键的后面加上 on update on delete 就可以了。\n1 alter table user_t1 add constraint fk_school foreign key(school) references school_t1(sid) on update no action on delete no action; 多表查询 最简单的多表查询\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 create table vip_users( name varchar(10) not null, age tinyint, sign_way char(1), vip_level char(1) default 1, cellphone char(11) unique, sign_date datetime ); insert into vip_users (name,age,sign_way,cellphone,sign_date) values (\u0026#34;frelon\u0026#34;,23,\u0026#39;a\u0026#39;,12345678901,now()),(\u0026#34;fbi\u0026#34;,44,\u0026#39;b\u0026#39;,12345678902,now()),(\u0026#34;Tom\u0026#34;,56,\u0026#39;d\u0026#39;,12345678903,now()); create table sign_way( sign_way_id char(1) primary key, sign_way varchar(20) ): insert into sign_way values (\u0026#39;a\u0026#39;,\u0026#34;ads\u0026#34;),(\u0026#39;b\u0026#39;,\u0026#34;朋友推荐\u0026#34;),(\u0026#39;c\u0026#39;,\u0026#34;自主下载\u0026#34;),(\u0026#39;d\u0026#39;,\u0026#34;强制安装\u0026#34;); alter table vip_users add constraint fk_sign_way foreign key (sign_way) references sign_way(sign_way_id); # 懂得都懂 1 select * from vip_users,sign_way where vip_users.sign_way = sign_way.sign_way_id; 这就是最简单的多表查询也就是隐式内连接。\n内连接 用于查询两表交集的部分的数据\n1 2 select * from table1,table2 where 条件; # 隐式内连接 select * from table1 (inner) join table2 on table1.x = table2.y; # 显式内连接 以上面创建的表为例，使用内连接查询：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 select * from vip_users,sign_way where vip_users.sign_way = sign_way.sign_way_id; #+--------+------+----------+-----------+-------------+---------------------+-------------+--------------+ #| name | age | sign_way | vip_level | cellphone | sign_date | sign_way_id | sign_way | #+--------+------+----------+-----------+-------------+---------------------+-------------+--------------+ #| frelon | 23 | a | 1 | 12345678901 | 2023-03-10 22:05:23 | a | ads | #| fbi | 44 | b | 1 | 12345678902 | 2023-03-10 22:05:23 | b | 朋友推荐 | #| Tom | 56 | d | 1 | 12345678903 | 2023-03-10 22:05:23 | d | 强制安装 | #+--------+------+----------+-----------+-------------+---------------------+-------------+--------------+ select * from vip_users as vip join sign_way as way on vip.sign_way = way.sign_way_id; #+--------+------+----------+-----------+-------------+---------------------+-------------+--------------+ #| name | age | sign_way | vip_level | cellphone | sign_date | sign_way_id | sign_way | #+--------+------+----------+-----------+-------------+---------------------+-------------+--------------+ #| frelon | 23 | a | 1 | 12345678901 | 2023-03-10 22:05:23 | a | ads | #| fbi | 44 | b | 1 | 12345678902 | 2023-03-10 22:05:23 | b | 朋友推荐 | #| Tom | 56 | d | 1 | 12345678903 | 2023-03-10 22:05:23 | d | 强制安装 | #+--------+------+----------+-----------+-------------+---------------------+-------------+--------------+ 外连接 left join left join 就是完全包含 left 部分的表，还包含两表重合部分的表。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 insert into vip_users (name,age,cellphone,sign_date) values (\u0026#34;Joker\u0026#34;,11,12345678904,now()); select * from vip_users v left join sign_way s on v.sign_way = s.sign_way_id; #+--------+------+----------+-----------+-------------+---------------------+-------------+--------------+ #| name | age | sign_way | vip_level | cellphone | sign_date | sign_way_id | sign_way | #+--------+------+----------+-----------+-------------+---------------------+-------------+--------------+ #| frelon | 23 | a | 1 | 12345678901 | 2023-03-10 22:05:23 | a | ads | #| fbi | 44 | b | 1 | 12345678902 | 2023-03-10 22:05:23 | b | 朋友推荐 | #| Tom | 56 | d | 1 | 12345678903 | 2023-03-10 22:05:23 | d | 强制安装 | #| Joker | 11 | NULL | 1 | 12345678904 | 2023-03-10 22:28:14 | NULL | NULL | #+--------+------+----------+-----------+-------------+---------------------+-------------+--------------+ select * from vip_users v join sign_way s on v.sign_way = s.sign_way_id; #+--------+------+----------+-----------+-------------+---------------------+-------------+--------------+ #| name | age | sign_way | vip_level | cellphone | sign_date | sign_way_id | sign_way | #+--------+------+----------+-----------+-------------+---------------------+-------------+--------------+ #| frelon | 23 | a | 1 | 12345678901 | 2023-03-10 22:05:23 | a | ads | #| fbi | 44 | b | 1 | 12345678902 | 2023-03-10 22:05:23 | b | 朋友推荐 | #| Tom | 56 | d | 1 | 12345678903 | 2023-03-10 22:05:23 | d | 强制安装 | #+--------+------+----------+-----------+-------------+---------------------+-------------+--------------+ 可以看到虽然新添加的Joker用户没有sign_way，但是 left join 也查询出来了，但是如果我们使用 join 就查不到该用户的数据了。\nright join right join 就是完全包含 right 部分的表，还包含两表重合部分的表。\n1 2 3 4 5 6 7 8 9 10 11 12 insert into vip_users (name,age,sign_way,cellphone,sign_date) values (\u0026#34;Jing\u0026#34;,11,\u0026#39;a\u0026#39;,12345678905,now()); select * from vip_users v right join sign_way s on v.sign_way = s.sign_way_id; #+--------+------+----------+-----------+-------------+---------------------+-------------+--------------+ #| name | age | sign_way | vip_level | cellphone | sign_date | sign_way_id | sign_way | #+--------+------+----------+-----------+-------------+---------------------+-------------+--------------+ #| frelon | 23 | a | 1 | 12345678901 | 2023-03-10 22:05:23 | a | ads | #| Jing | 11 | a | 1 | 12345678905 | 2023-03-10 22:33:18 | a | ads | #| fbi | 44 | b | 1 | 12345678902 | 2023-03-10 22:05:23 | b | 朋友推荐 | #| NULL | NULL | NULL | NULL | NULL | NULL | c | 自主下载 | #| Tom | 56 | d | 1 | 12345678903 | 2023-03-10 22:05:23 | d | 强制安装 | #+--------+------+----------+-----------+-------------+---------------------+-------------+--------------+ 可以看到 right join 就会把右边的表的数据都查不来，null也给你展示，而左表之前新建的Joker因为sign_way为null，就没有查询出来。\n","date":"2023-03-10T23:16:33+08:00","image":"https://www.bing.com/th?id=OHR.EdaleValley_EN-CN2024294986_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/mysql_learn_note/","title":"Mysql学习笔记"},{"content":"Wsl2 挂载 webdav 首先需要安装 davfs2 1 sudo apt install -y davfs2 首次安装时问你要不要允许非root用户挂载，安全起见选 \u0026lt;yes\u0026gt;\n配置账号密码密码 1 2 3 4 sudo nano /etc/davfs2/secrets #\t输入以下内容 http://192.168.1.169:8080 username password #\t格式： 挂载地址\t用户名\t密码 也可以不配置，但是在下面挂载的时候每次都需要输入账号密码\n挂载webdav 1 2 mkdir webdav sudo mount.davfs http://192.168.1.169:8080 webdav Windows挂载webdav 另外踢一下垃圾Windows，尼玛的，Windows默认只允许https的webdav，这就导致http方式的webdav挂载会报错，只能通过修改注册表来允许http连接。修改 HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\WebClient\\Parameters 把BasicAuthLevel 值改成2，然后重启webclient服务\n1 2 net stop webclient net start webclient 或者 服务 \u0026gt; webclient 重启就可以了。# Wsl2 挂载 webdav\n","date":"2022-10-29T18:45:59+08:00","image":"https://www.bing.com/th?id=OHR.AlaskaMoose_EN-CN1990708733_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/wsl2_mount_webdav/","title":"Wsl2 挂载 webdav"},{"content":"使用自签名Ca证书签名ssl 最近在家里的服务器搭建了一个Emby服务器，（主要是因为Jellyfin太拉跨了，体验还是拉Emby一大截）并且通过自建的frp给了个公网端口。 然后想想看，Emby被代理出来的端口还是http协议的，在这个全明ssl加密的时代，http显得格格不入啊。本来想用 Let’s Encrypt 的免费ssl证书的，但是就三个月，虽然有那种自动续签的脚本。但是我觉得自己用没必要这么奢侈。自己签一个10年的管饱的不就好了吗！\n通用证书签名 生成CA私钥 1 2 openssl genrsa -des3 -out ca.key 4096 #需要密码，后面签名的时候会用到 openssl genrsa -out ca.key 4096 #不想需要密码 过程：\n1 2 3 4 Generating RSA private key, 4096 bit long modulus (2 primes) ......++++ ..............................................................++++ e is 65537 (0x010001) 我是以不要密码的 做个示范，后面过程都是不要密码的。\n签发证书（公钥） 1 openssl req -new -x509 -days 3651 -key ca.key -out ca.crt 过程：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter \u0026#39;.\u0026#39;, the field will be left blank. ----- Country Name (2 letter code) [AU]: State or Province Name (full name) [Some-State]: Locality Name (eg, city) []: Organization Name (eg, company) [Internet Widgits Pty Ltd]: Organizational Unit Name (eg, section) []: Common Name (e.g. server FQDN or YOUR name) []:JokemeBlog Email Address []: 这里的信息自己随便填！注意 Organization Name 下面ssl私钥要填和这里一样的就可以了。\n生成ssl私钥 1 openssl genrsa -out ssl.key 2048 过程：\n1 2 3 4 Generating RSA private key, 2048 bit long modulus (2 primes) .......................................+++++ ........+++++ e is 65537 (0x010001) 生成签名请求 1 openssl req -new -key ssl.key -out ssl.csr 过程：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter \u0026#39;.\u0026#39;, the field will be left blank. ----- Country Name (2 letter code) [AU]: State or Province Name (full name) [Some-State]: Locality Name (eg, city) []: Organization Name (eg, company) [Internet Widgits Pty Ltd]: Organizational Unit Name (eg, section) []: Common Name (e.g. server FQDN or YOUR name) []:*.jokeme.top Email Address []: Please enter the following \u0026#39;extra\u0026#39; attributes to be sent with your certificate request A challenge password []: An optional company name []: 签发证书 1 2 echo \u0026#34;1234\u0026#34; \u0026gt; ca.srl openssl x509 -req -in ssl.csr -CA ca.crt -CAkey ca.key -out ssl.crt -days 3650 -sha256 过程：\n1 2 3 Signature ok subject=C = AU, ST = Some-State, O = Internet Widgits Pty Ltd, CN = *.jokeme.top Getting CA Private Key 查看证书信息 1 openssl x509 -in ssl.crt -text 过程：\n1 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 Certificate: Data: Version: 1 (0x0) Serial Number: 1122869 (0x112235) Signature Algorithm: sha256WithRSAEncryption Issuer: C = AU, ST = Some-State, O = Internet Widgits Pty Ltd, CN = JokemeBlog Validity Not Before: Oct 12 18:29:41 2022 GMT Not After : Oct 9 18:29:41 2032 GMT Subject: C = AU, ST = Some-State, O = Internet Widgits Pty Ltd, CN = *.jokeme.top Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:b8:b4:8a:ba:16:da:eb:ac:f7:64:f5:13:77:ed: xxx xxx 6d:97:3e:cf:59:58:3d:19:0e:9a:d0:bd:91:eb:1a: 85:65 Exponent: 65537 (0x10001) Signature Algorithm: sha256WithRSAEncryption 7d:9c:07:4e:d2:46:1f:ad:e2:70:f6:63:72:89:9b:83:57:af: xxx xxx 66:23:09:a5:cc:67:4f:b9 -----BEGIN CERTIFICATE----- MIIELDCCAhQCAxEiNTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJBVTETMBEG xxx xxx 880sCRdh9cZfwv0F0/zSnZfgk3IWHjVVcCFArvAbrf3V9SBItlK+LQy/YOWWRgpI fx/Y9eDvFcNmIwmlzGdPuQ== -----END CERTIFICATE----- 这样通用证书就签发好了，用的时候只需要把 ca.crt 导出，安装在系统的 ⌈信任根证书签发机构⌋ 里面就可以正常使用了。\n还有一点需要说明！就是这个证书是通用的安全加密证书，但是它不是被浏览器所信任的 SAN证书 ，那什么是SAN证书呢？\nSAN(Subject Alternate Names)，证书备用名称。其实就是给浏览器加上下面的信息\n1 2 3 DNS.1 = baidu.com DNS.2 = baifubao.com DNS.3 = www.baidu.cn 来达到佐证签名本身应该工作在什么网站上的目的。\n如果直接使用自签的证书，现代浏览器还是不会信任这种缺失SAN的证书滴。因为一旦没有通过所谓的SAN检查，来证明证书本身应该工作在当前网站，那该网站就有可能是被入侵的，任何人访问都有可能造成风险。\n那么如何才能让浏览器信任我们的自签证书呢？很明显，只需要自签的证书里包含 SAN 信息就可以了。\nSAN证书签发 这里我参考照抄了 stackoverflow 上的答案，自己稍微的整理了一下，写了个一键生成的脚本。StackOverflow上那个老哥建议加密，那我也就把密码给加上了。\n话不多说，直接上脚本：\n1 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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 cat \u0026lt;\u0026lt; EOF \u0026gt; ca.cnf HOME = . RANDFILE = \\$ENV::HOME/.rnd #################################################################### [ ca ] default_ca = CA_default # The default ca section [ CA_default ] default_days = 3651 # How long to certify for default_crl_days = 3650 # How long before next CRL default_md = sha256 # Use public key default MD preserve = no # Keep passed DN ordering x509_extensions = ca_extensions # The extensions to add to the cert email_in_dn = no # Don\u0026#39;t concat the email in the DN copy_extensions = copy # Required to copy SANs from CSR to cert #################################################################### [ req ] default_bits = 4096 default_keyfile = ca.key distinguished_name = ca_distinguished_name x509_extensions = ca_extensions string_mask = utf8only #################################################################### [ ca_distinguished_name ] countryName = Country Name (2 letter code) countryName_default = US stateOrProvinceName = State or Province Name (full name) stateOrProvinceName_default = OGS localityName = Locality Name (eg, city) localityName_default = OGS organizationName = Organization Name (eg, company) organizationName_default = JokemeBlog Team organizationalUnitName = Organizational Unit (eg, division) organizationalUnitName_default = dev commonName = Common Name (e.g. server FQDN or YOUR name) commonName_default = JokemeBlog emailAddress = Email Address emailAddress_default = lmlj@jokeme.top #################################################################### [ ca_extensions ] subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always, issuer basicConstraints = critical, CA:true keyUsage = keyCertSign, cRLSign EOF openssl req -x509 -config ca.cnf -newkey rsa:4096 -days 3651 -sha256 -out ca.pem -outform PEM cat \u0026lt;\u0026lt; EOF \u0026gt; ssl.cnf HOME = . RANDFILE = \\$ENV::HOME/.rnd #################################################################### [ req ] default_bits = 2048 default_keyfile = ssl.key distinguished_name = server_distinguished_name req_extensions = server_req_extensions string_mask = utf8only #################################################################### [ server_distinguished_name ] countryName = Country Name (2 letter code) countryName_default = US stateOrProvinceName = State or Province Name (full name) stateOrProvinceName_default = OGS localityName = Locality Name (eg, city) localityName_default = OGS organizationName = Organization Name (eg, company) organizationName_default = JokemeBlog Team commonName = Common Name (e.g. server FQDN or YOUR name) commonName_default = *.jokeme.top emailAddress = Email Address emailAddress_default = lmlj@jokeme.top #################################################################### [ server_req_extensions ] subjectKeyIdentifier = hash basicConstraints = CA:FALSE keyUsage = digitalSignature, keyEncipherment subjectAltName = @alternate_names nsComment = \u0026#34;OpenSSL Generated Certificate\u0026#34; #################################################################### [ alternate_names ] DNS.1 = jokeme.top DNS.2 = *.jokeme.top DNS.3 = *.*.jokeme.top EOF openssl req -config ssl.cnf -newkey rsa:2048 -sha256 -days 3650 -out ssl.csr cat \u0026lt;\u0026lt; EOF \u0026gt; ca.cnf HOME = . RANDFILE = \\$ENV::HOME/.rnd #################################################################### [ ca ] default_ca = CA_default # The default ca section [ CA_default ] base_dir = . certificate = \\$base_dir/ca.pem # The CA certifcate private_key = \\$base_dir/ca.key # The CA private key new_certs_dir = \\$base_dir # Location for new certs after signing database = \\$base_dir/index.txt # Database index file serial = \\$base_dir/serial.txt # The current serial number unique_subject = no # Set to \u0026#39;no\u0026#39; to allow creation of # several certificates with same subject. default_days = 3651 # How long to certify for default_crl_days = 3650 # How long before next CRL default_md = sha256 # Use public key default MD preserve = no # Keep passed DN ordering x509_extensions = ca_extensions # The extensions to add to the cert email_in_dn = no # Don\u0026#39;t concat the email in the DN copy_extensions = copy # Required to copy SANs from CSR to cert #################################################################### [ req ] default_bits = 4096 default_keyfile = ca.key distinguished_name = ca_distinguished_name x509_extensions = ca_extensions string_mask = utf8only #################################################################### [ ca_distinguished_name ] countryName = Country Name (2 letter code) countryName_default = US stateOrProvinceName = State or Province Name (full name) stateOrProvinceName_default = OGS localityName = Locality Name (eg, city) localityName_default = OGS organizationName = Organization Name (eg, company) organizationName_default = JokemeBlog Team organizationalUnitName = Organizational Unit (eg, division) organizationalUnitName_default = dev commonName = Common Name (e.g. server FQDN or YOUR name) commonName_default = JokemeBlog emailAddress = Email Address emailAddress_default = lmlj@jokeme.top #################################################################### [ ca_extensions ] subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always, issuer basicConstraints = critical, CA:true keyUsage = keyCertSign, cRLSign #################################################################### [ signing_policy ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional #################################################################### [ signing_req ] subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer basicConstraints = CA:FALSE keyUsage = digitalSignature, keyEncipherment EOF touch index.txt \u0026amp;\u0026amp; echo \u0026#39;01\u0026#39; \u0026gt; serial.txt openssl ca -config ca.cnf -policy signing_policy -extensions signing_req -out ssl.pem -infiles ssl.csr 过程还是那个过程，只不过中间加SAN的时候就不能用系统默认的配置了，会抹掉SAN信息，所以就使用自己的配置文件，让签名的时候继承这些SAN扩展信息。\n脚本执行完了，大概会有以下信息，其中 ss.sh 是上面脚本的内容，ssl.passwd是给nginx用的，因为ssl加密的时候输入了密码，nginx如果不知道密码也就不能处理https的证书了。\n1 2 01.pem ca.key index.txt index.txt.old serial.txt.old ssl.cnf ssl.key ssl.pem ca.cnf ca.pem index.txt.attr serial.txt ss.sh ssl.csr ssl.passwd oh！nginx添加有密码的ssl证书需要添加上下面这句配置：\n1 ssl_password_file /home/xx/cert/ssl.passwd; 这样可以被浏览器承认的ssl证书就签发好了，把ca.pem导出安装到你的系统中就可以了。以后所有的用该ca签名的证书都会被信任。并且有效期十年。根本不折腾，我就不相信十年以后你还在用现在的电脑💻/手机📱\n什么？你说你不会在Windows上安装pem格式的CA证书？\n直接改后缀名为 .crt 就可以了。因为Linux上默认使用PEM编码的，这个pem和crt都是PEM编码的，内容没有区别。\n","date":"2022-10-12T03:12:09Z","image":"https://www.bing.com/th?id=OHR.AmmoniteGraveyard_EN-CN1897278161_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/signing_ssl_certificate_by_yourself/","title":"使用自签名Ca证书签名ssl"},{"content":"给Git的commit添加GPG验证 使用Git和GitHub的都知道，只要别人知道了你的邮件就可以以你的名义推送代码，所以在GitHub上，只要你没有给代码添加GPG验证，就会显示此次commit为黄色的：Unverified 如下。\n而commit有了GPG验证以后就有了绿色的：Verified 如下。\nGithub文档 关于提交签名验证\n主要分为以下步骤：\n检查现有的 GPG 密钥 生成新的 GPG 密钥 将 GPG 密钥添加到您的 GitHub 帐户 告诉 Git 你的签名密钥 签署提交 标志标签 但是我建议只是看看就可以了，还是直接按我下面的来的更快。\n生成GPG 下面两个命令都可以\n1 2 gpg --full-generate-key #这个需要完成下面两步 gpg --default-new-key-algo rsa4096 --gen-key #可以跳过下面两步 这一步加密方法，默认回车就可以。\n1 2 3 4 5 6 7 Please select what kind of key you want: (1) RSA and RSA (default) (2) DSA and Elgamal (3) DSA (sign only) (4) RSA (sign only) (14) Existing key from card Your selection? 长度务必写4096，要不然GitHub不支持。\n1 2 3 RSA keys may be between 1024 and 4096 bits long. What keysize do you want? (3072) 4096 Requested keysize is 4096 bits 如果你选择下面这种方式，现在可以开始了，下面的步骤就开始是两种创建方式都需要的了\n1 gpg --default-new-key-algo rsa4096 --gen-key 直接两个回车，GPG永不过期。\n1 2 3 4 5 6 7 8 9 Please specify how long the key should be valid. 0 = key does not expire \u0026lt;n\u0026gt; = key expires in n days \u0026lt;n\u0026gt;w = key expires in n weeks \u0026lt;n\u0026gt;m = key expires in n months \u0026lt;n\u0026gt;y = key expires in n years Key is valid for? (0) Key does not expire at all Is this correct? (y/N)y 然后信息按照你想填的来，注意！Email必须是你GitHub验证过的邮箱，或者是你的登陆邮箱。\n1 2 3 4 5 6 7 8 9 GnuPG needs to construct a user ID to identify your key. Real name: frelon Email address: xxx@xxx.com Comment: test You selected this USER-ID: \u0026#34;frelon (test) \u0026lt;xxx@xxx.com\u0026gt;\u0026#34; Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o 这一步你就需要填写密码了，要记住哦！以后每一次commit都需要 输入密码。\n1 2 3 4 5 6 7 8 ┌──────────────────────────────────────────────────────┐ │ Please enter the passphrase to │ │ protect your new key │ │ │ │ Passphrase: ________________________________________ │ │ │ │ \u0026lt;OK\u0026gt; \u0026lt;Cancel\u0026gt; │ └──────────────────────────────────────────────────────┘ 看到这一步那GPG就是创建完成了。\n1 2 3 4 5 6 7 8 9 We need to generate a lot of random bytes. It is a good idea to perform ... ... public and secret key created and signed. Note that this key cannot be used for encryption. You may want to use the command \u0026#34;--edit-key\u0026#34; to generate a subkey for this purpose. pub ... ... CECxxxxxx62B uid frelon \u0026lt;xxx@xxx.com\u0026gt; 可以用下面的命令查看创建的GPG的长格式。\n1 gpg --list-secret-keys --keyid-format=long 大概内容如下：\n1 2 3 sec rsa4096/22B4B8515915762B 2022-09-24 [SC] [expires: 2024-09-23] CEC47723D9BF723D6709CE0722B4B8515915762B uid [ultimate] frelon \u0026lt;xxx@xxx.com\u0026gt; 在GitHub添加GPG密钥 GitHub文档在此：Add a GPG key\n首先导出 Public Key\n1 gpg --armor --export 22B4B8515915762B 大概内容如下：\n1 2 3 4 5 6 7 -----BEGIN PGP PUBLIC KEY BLOCK----- mQINBGMvXnEBEAC30FGoWxFPnPBcWTI28w4ZRFrNs126RsvImapeXLQR+Y7fEYLN ... ... t66fFPRKLh3VtYVqIZNhTun98fL786U+UYURZlNzJvj2toYjoxntg3nXgxem =uc2y -----END PGP PUBLIC KEY BLOCK----- 然后复制！去GitHub操作。\n在任何页面的右上角，单击个人资料照片，然后单击“设置”。 In the \u0026ldquo;Access\u0026rdquo; section of the sidebar, click SSH and GPG keys. 单击“新建 GPG 密钥”。 在“密钥”字段中，粘贴生成 GPG 密钥时复制的 GPG 密钥。 单击“添加 GPG 密钥” 要确认操作，请输入您的 GitHub 密码。 配置Git使用GPG验证commit 让Git使用GPG\n1 git config --global user.signingkey 22B4B8515915762B 然后将 GPG 密钥添加到 .zshrc 启动文件\n1 2 [ -f ~/.zshrc ] \u0026amp;\u0026amp; echo \u0026#39;export GPG_TTY=$(tty)\u0026#39; \u0026gt;\u0026gt; ~/.zshrc source ~/.zshrc Git commit 测试 在本地分支中提交更改时，将 -S 标志添加到 git commit 命令：\n1 git commit -S -m \u0026#34;your commit message\u0026#34; 在创建提交后输入生成 GPG 密钥时设置的密码。\n在本地完成创建提交后，将它们推送到 GitHub 上的远程存储库：\n然后就可以发现我们push的commit就变成了 verified 。\n","date":"2022-09-25T04:22:16+08:00","image":"https://cn.bing.com/th?id=OHR.RajaAmpat_EN-CN5052014368_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/git_add_gpg/","title":"给Git的commit添加GPG验证"},{"content":"wsl添加systemd 最近突然看到wsl可以支持 systemd 了！嚯！总算是等到它了。为什么这么期待它呢？因为wsl有了systemd就会变得更完整了。主要的影响就是可以用 systemctl 管理软件服务了。话不多说，开整。\n原文链接 Systemd support is now available in WSL!\nCraig Loewen September 21 2022\nSystemd support lands in WSL – unleash the full power of Ubuntu today\nOliver Smith 21 September 2022\n放弃wsl安装wsl2 详细步骤 不过很遗憾的是wsl好像是不支持的，只支持wsl2，所以只能从MS Store里面下载新版本的wsl2，如果之前设置默认版本是wsl1的话，可以用下面的命令改回去。\n1 wsl --set-default-version 2 因为 Windows11 预览版 自带Windows Subsystem for Linux Preview，所以 wsl --version 可以正常显示：\n1 2 3 4 5 6 7 8 PS C:\\Users\\frelon\u0026gt; wsl -v WSL 版本： 0.67.6.0 内核版本： 5.15.62.1 WSLg 版本： 1.0.44 MSRDC 版本： 1.2.3401 Direct3D 版本： 1.606.4 DXCore 版本： 10.0.25131.1002-220531-1700.rs-onecore-base2-hyp Windows版本： 10.0.22622.601 如果你 wsl --version 报错，不要怕，在 MS Store下载 Windows Subsystem for Linux Preview 就可以解决这个问题。\n不过还有一个问题，那就是截止到目前 2022年9月24日10点58分 MS Store的版本还是0.66.2 如下：\n1 2 3 4 5 6 7 8 PS C:\\Users\\frelon\u0026gt; wsl -v WSL 版本： 0.66.2.0 内核版本： 5.15.57.1 WSLg 版本： 1.0.42 MSRDC 版本： 1.2.3401 Direct3D 版本： 1.606.4 DXCore 版本： 10.0.25131.1002-220531-1700.rs-onecore-base2-hyp Windows版本： 10.0.22000.1042 并不能达到 0.67.6 及更高版本 的要求。所以建议想尝鲜的可以直接去GitHub下载\nWSL 发布页面\n极速版 安装完那个msixbundle以后就可以开始安装wsl2了。当然我这是在有wsl1的基础上的步骤。你要是Windows11 预览版那就可以直接安装Ubuntu-22。因为它默认就是wsl2 并且 WSL 版本为 0.66.2。不是Windows11预览版就按上面的来，装Windows Subsystem for Linux Preview就可以了。\n安装wsl2 直接在MS Store搜索Ubuntu就可以了，来个最新版本的哈。然后下载完成后有一个wslg的界面，大概就是配置界面，设置一下语言\u0026amp;密码就可以了。wslg安装界面示例如下：\n安装完成后和wsl1是差不多的。这个时候可以执行一下 pstree 看看\n1 2 3 4 frelon@amdpc:~$ pstree init─┬─init───zsh ├─init───zsh───pstree └─3*[{init} 嗯！这个是wsl1的，wsl2的我忘了整了，不过大概也是这样的，基于init启动的系统，并不是systemd。\n开始设置systemd 然后来换个国内的源：\n1 sudo sed -i \u0026#39;s/archive.ubuntu.com/mirrors.ustc.edu.cn/g\u0026#39; /etc/apt/sources.list 升级systemd\n1 2 sudo apt update sudo apt upgrade systemd 编辑 /etc/wsl.conf\n1 sudo nano /etc/wsl.conf 添加下面的配置到 /etc/wsl.conf 里面。\n1 2 [boot] systemd=true 然后在Windows的powershell/cmd环境里面执行：\n1 wsl --shutdown 然后重新开启wsl2，可能会有一点慢。要稍等一下。然后执行： pstree 惊喜来咯！系统基于systemd启动了，而不是init。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 frelon@amdpc:~$ pstree systemd─┬─ModemManager───2*[{ModemManager}] ├─2*[agetty] ├─cron ├─dbus-daemon ├─init─┬─init───init───bash───pstree │ ├─login───bash │ └─2*[{init}] ├─networkd-dispat ├─polkitd───2*[{polkitd}] ├─rsyslogd───3*[{rsyslogd}] ├─snapd───12*[{snapd}] ├─3*[snapfuse] ├─systemd───(sd-pam) ├─systemd-journal ├─systemd-logind ├─systemd-network ├─systemd-resolve ├─systemd-timedat ├─systemd-udevd ├─udisksd───4*[{udisksd}] └─unattended-upgr───{unattended-upgr} ","date":"2022-09-24T11:34:17+08:00","image":"https://cn.bing.com/th?id=OHR.SwordFern_EN-CN0942795276_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/wsl_add_systemd/","title":"wsl添加systemd"},{"content":"Frp+Nginx反代RDP 先说一下背景，因为现在住的地方的电信宽带有动态的公网IP，老家有一台常年不关机的Windows主机和一台Linux服务器，并且都开启了通电自启。但是连回去是一个问题，之前一直是用的tailscale来做异地组网来解决这个问题。但是现在用久了发现延时越来越大，可能是服务器不在国内的，导致连接质量极其的差。有时候甚至会连接不上。所以有了此文。\nfrps服务器的准备 frps的搭建教程在这里 Frp 内网穿透 或者这个回首frp搭建\n不过这一次我是直接在路由器上自带的frps里面搭建的\n配置就不用多介绍了，懂得都懂主要就是下面的这几个需要配置一下\n1 2 3 4 5 6 7 8 9 10 绑定端口 UDP 绑定端口 KCP 绑定端口 Vhost HTTP端口 Vhost HTTPS 端口 仪表板端口 仪表板用户 仪表板密码 令牌 子域主机 然后启动frps，用访问ip/domain:仪表板端口用仪表板用户+仪表板密码登录就可以看到，frps的运行状态了。\nnginx准备及设置 我这里使用的系统是Ubuntu20.04所以可以直接安装nginx即可。还有哦！这个服务器是和我Windows主机一个局域网下面的。后面的nginx和frpc操作都是在这个Ubuntu主机上完成的。\n1 sudo apt install nginx 这个版本的nginx在安装的时候会把stream模块也安装上，所以不需要再手动安装了，如果你是其他系统，请自行nginx -V查看有没有包括关键词：stream，或者下面这两个。\n1 2 --with-stream --with-stream_ssl_module 然后编辑 /etc/nginx/nginx.conf添加下面的一行配置。当然你也可以直接在stream里面写配置，但是我不喜欢修改主配置文件的这种方式。\n1 2 3 stream { include /etc/nginx/tcp.d/*.lee; } 然后就是在**/etc/nginx/新建tcp.d**目录，并在该目录下面新建配置文件：tcp.lee。\n1 2 3 4 5 6 7 upstream rdp{ server 192.168.1.3:3389; } server{ listen 3389; proxy_pass rdp; } frpc设置 1 2 3 4 5 6 7 8 9 10 [common] server_addr = xxx.com server_port = 绑定端口 token = 令牌 [rdp] type = tcp local_ip = 127.0.0.1 local_port = 3389 remote_port = 对外暴露的公网端口 然后用Windows自带的远程桌面访问：ip/domain:对外暴露的公网端口就可以连上家里面没有公网的Windows主机了。\n适用情况 那你可能又要说了，为什么不直接在Windows上用frpc呢！其实我情况特殊，主要原因有二\n第一个原因 我当时使用tailscale虽然设置了开机自启，但是那个自启需要用户登录才会启动。这就导致我重启电脑以后，tailscale直接没有启动，启动我就需要先登陆，但是我登陆就需要tailscale先启动。无解！虽然后来设置了run unattended来解决这个问题。但是因为延时问题。我还是放弃了tailscale。\n第二个原因 我对Windows不熟悉，也不愿意学习Windows的那些dos或者powershell操作，更喜欢使用Linux完成这些功能。\n","date":"2022-09-17T00:58:07+08:00","image":"https://cn.bing.com/th?id=OHR.Reynisfjara_EN-CN0317269513_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/frp_and_nginx_reverse_proxy_rdp/","title":"Frp+Nginx反代RDP"},{"content":"自己动手写一个cf-ddns脚本 首先我建议你去看一下官网的api介绍,因为你需要了解的知识这里都写的清清楚楚.\nhttps://api.cloudflare.com/#dns-records-for-a-zone-update-dns-record\n了解如何修改dns记录: 从官网文档里可以看到以下信息:\n1 PUT zones/:zone_identifier/dns_records/:identifier 首先我们需要了解的是:zones后面的:zone_identifier参数是个啥? 在哪里搞到的.然后是dns_records后面的:identifier参数又是个啥.\n我也不拐弯抹角了,直接说了哈!\n:zone_identifier查询 :zone_identifier有好几种方法得到! 最简单的就是:\n查看登录页的 [API-区域ID] 就是了\n利用命令查询\n1 2 3 4 curl -X GET \u0026#34;https://api.cloudflare.com/client/v4/zones\u0026#34; \\ -H \u0026#34;Content-Type:application/json\u0026#34; \\ -H \u0026#34;X-Auth-Key:b3373w343489u23n34340923b7we454873565\u0026#34; \\ -H \u0026#34;X-Auth-Email:jxxx@outlook.com\u0026#34; 当然这个时候你看到X-Auth-Key肯定是懵逼的,那个email大概还可以猜得出来是登陆的邮箱,那这玩意又是个啥呢?\n其实啊! X-Auth-Key == Global API Key,\n就是点击刚刚区域ID下面的获取您的 API 令牌来拿到的\n一般来说,点进去都会有一个 Global API Key,查看你的 Global API Key就可以了\n然后你就可以顺利的拿到类似的数据:\n1 2 3 4 5 6 7 8 { \u0026#34;result\u0026#34;: { \u0026#34;id\u0026#34;: \u0026#34;63346566745de46456fe345u3473oe56\u0026#34;, ... ... \u0026#34;success\u0026#34;: true, \u0026#34;errors\u0026#34;: [], \u0026#34;messages\u0026#34;: [] } 第一个id就是我们需要的!\n:identifier查询 这一步是没办法了, 必须要整命令来查询了\n1 2 3 4 curl -X GET \u0026#34;https://api.cloudflare.com/client/v4/zones/63346566745de46456fe345u3473oe56/dns_records?type=AAAA\u0026amp;name=example.top\u0026amp;match=all\u0026#34; \\ -H \u0026#34;X-Auth-Email: jokemetoday@outlook.com\u0026#34; \\ -H \u0026#34;X-Auth-Key: e65080e7e929d926d600888a4fe3326ab5d73\u0026#34; \\ -H \u0026#34;Content-Type: application/json\u0026#34; 注意: 这里的.../v4/zones/后面的参数,就是刚刚前面查到的参数,然后不出意外,你又可以查到以下参数\n1 2 3 4 5 6 { \u0026#34;result\u0026#34;: [{ \u0026#34;id\u0026#34;: \u0026#34;xxx777xxx777xxx\u0026#34;, \u0026#34;zone_id\u0026#34;: \u0026#34;63346566745de46456fe345u3473oe56\u0026#34;, ... ... } 这个id就是我们需要的 :identifier了,然后,用上面所查到的所有参数,来构建我们最终需要修改DNS记录的一条命令\n更新DNS记录 使用下面的命令进行更新IPv6的DNS记录\n1 2 3 4 5 curl -X PUT \u0026#34;https://api.cloudflare.com/client/v4/zones/63346566745de46456fe345u3473oe56/dns_records/xxx777xxx777xxx\u0026#34; \\ -H \u0026#34;X-Auth-Email: jxxx@outlook.com\u0026#34; \\ -H \u0026#34;X-Auth-Key: b3373w343489u23n34340923b7we454873565\u0026#34; \\ -H \u0026#34;Content-Type: application/json\u0026#34; \\ --data \u0026#39;{\u0026#34;type\u0026#34;:\u0026#34;AAAA\u0026#34;,\u0026#34;name\u0026#34;:\u0026#34;example.top\u0026#34;,\u0026#34;content\u0026#34;:\u0026#34;2409:aaaa:aaaa:aaaa::1\u0026#34;,\u0026#34;ttl\u0026#34;:1}\u0026#39; 上面所有的操作都是为这一步的,而且上面的数据是固定的,或者是你不主动修改不会变的,那么咱们就可以一劳永逸了,都干到这里了,随便写个脚本让cron5min执行一下\n编写脚本 1 2 3 4 5 6 7 8 #!/bin/sh ipv6=`ifconfig enp2s0 | grep 2409 | awk \u0026#39;{print $2}\u0026#39;` curl -X PUT \u0026#34;https://api.cloudflare.com/client/v4/zones/63346566745de46456fe345u3473oe56/dns_records/xxx777xxx777xxx\u0026#34; \\ -H \u0026#34;X-Auth-Email: jxxx@outlook.com\u0026#34; \\ -H \u0026#34;X-Auth-Key: b3373w343489u23n34340923b7we454873565\u0026#34; \\ -H \u0026#34;Content-Type: application/json\u0026#34; \\ --data `echo \u0026#39;{\u0026#34;type\u0026#34;:\u0026#34;AAAA\u0026#34;,\u0026#34;name\u0026#34;:\u0026#34;xxx.example.com\u0026#34;,\u0026#34;content\u0026#34;:\u0026#34;\u0026#39;$ipv6\u0026#39;\u0026#34;,\u0026#34;ttl\u0026#34;:1}\u0026#39;` 1 */5 * * * * sh /home/li/.bin/cf-ddns 好的,已经一步到位了,暂时不愁了\n","date":"2022-08-16T20:08:28+08:00","image":"https://cn.bing.com/th?id=OHR.LlanberisSlate_EN-CN7840797742_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/cf-ddns/","title":"自己动手写一个cf-ddns脚本"},{"content":"OpenWrt单线多拨以及负载均衡 最近因为叼毛在搞多播，还鼓动我也试试看，那没办法了，我只能在晚上 抽时间来搞一搞多播了。\n在文章开始前先简单介绍一下openwrt的网卡吧，要不然会云里雾里的\neth0 是一块物理网卡。eth0.1 eth0.2都是从此设备上虚拟出来的。 eth0.1 是vlan 1分出的lan口。 eth0.2 是vlan 2分出的wan口。 br-lan 是虚拟设备，用于LAN口设备桥接 = eth0.1 + wlan0 + wlan1\n我用的Openwrt是21.02如果你使用的是L大的固件，可能略微有点不同。\n准备工作 多播第一步，先换软件源\n1 2 3 sed -i \u0026#39;s/downloads.openwrt.org/mirrors.ustc.edu.cn\\/openwrt/g\u0026#39; /etc/opkg/distfeeds.conf opkg update opkg install kmod-macvlan 因为需要安装的东西还是挺多的，用默认的软件源很慢，浪费时间，而且以后opkg update也会变快一点的。\n首先我们先看一下自己的[接口-设备]，实在虚拟机里面演示的，因为现在是白天还在上网课，我要是动路由器，室友会把我祭天的。所以咱们就虚拟机和路由器对比起来分析。\n我这里的虚拟机的eth0 eth1对应路由器上的eth0.1 eth0.2，因为虚拟机要多少网口就有多少网口，所以不需要划分vlan，而路由器只有单wan口，所以要想多播只能靠划分vlan。\n可以看到路由器全部是依靠一个eth0网卡来干所有的事情，一个网卡处理这么多接口的数据可想而知，那应该是挺吃力的。\n接口设置 到了这一步，咱路由器上需要删除已存在的wan wan6接口，具体是什么原因呢，咱也不知道，反正就是一顿操作，先把eth0.2整没了。在一顿操作创建一个接口把eth0.2整回来。【常规设置】协议随便选，设备如果有eth0.2就选，没有就手写一个，不过前提是没有动过vlan，也就是路由器上的【switch】。还有就是记得把开机自动运行勾上。【防火墙设置】选择wan区域就可以了。\n至于虚拟机里面，咱不用这大怨种操作，因为咱有单独的eth1接口用于拨号。\n多播设置 路由器设备 咱路由器就是爽，直接新建pppoe拨号接口，设置按照下图来，你要多播几次就新建几个接口\n还有就是在创建的时候【高级设置】-【使用网关跃点】，如果我没有记错范围是（1-255）0是数据包随机选择你创建的接口线路走。一些教程说一定要配置，其实也不一定，咱路由器要是性能可以，大马拉小车，千兆路由器，百兆宽带，完全没必要配置。反正我是这么感觉的，如果配置了反而会优先使用跃点小的，让一个累死，另一个清闲。\n最后看一下多播成功的效果。真让人羡慕，又多了一个公网IP，嘿嘿嘿，凡尔赛一下:）\nx86设备 那咱就有点冤大头了，咱需要先在【接口】【设备】里面添加macvlan设备，按照下面来。\n解释一下哈：重要的参数有下面几个\n1 2 3 4 5 设备类型:macvlan 基设备:eth1 #也就是wan接口所在的网卡 模式:桥接、私有都可以，直通只能虚拟出来一个，如果你创建第二个直通会报错 设备名:随便写 MAC 地址:如果你有需求就写，不写也有 你要多播几次，就创建几个macvlan\n然后再基于这些macvlan创建接口\n据说啊，也可以用命令创建，但是我试了，x86设备好像不太行，大家可以也试试看，我他喵的只能在21.02的路由器上用这些命令。\n1 2 3 4 ip link add link eth0.2 name veth1 type macvlan ifconfig veth1 up ip link add link eth0.2 name veth2 type macvlan ifconfig veth2 up 这个是效果图，虽然我是用的dhcp，但是换成pppoe一样可以用哈，我在路由器上已经播过号了，所以只能用dhcp替代一下。\n","date":"2022-04-26T20:52:57+08:00","image":"https://cn.bing.com/th?id=OHR.MadHares_EN-CN9556585767_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/openwrt_multicast/","title":"openwrt多播"},{"content":"IOS安装老版本app方法 众所周知，从AppStore里面只能升级app，但是并不能降级app，这就导致有时候升级了app但是却带来了很多我们不喜欢的功能甚至广告，我们只能被迫接受。以前我甚至为了降级app特地越狱我的设备，但是这种方法的副作用也很大。\n好了废话不多说，先下载好我分享的两个文件：fiddler iTunes 其中fiddler是绿色免安装版，中文汉化过了。\n不用我的这个iTunes也可以，只要保证版本低于12.7即可，苹果在12.7版本之后的iTunes软件中删除了app store功能。就导致我们不能下载ipa了\n分享链接在这里：oldipa\t提取码: wk60\n下载完了安装iTunes即可，然后登陆上你的apple id\n然后打开fiddler文件夹里面的fiddler.exe\n【工具】 \u0026gt; 【选项】 \u0026gt; 【https】 把那几个勾都打上\n如果到时候显示证书到期的话，你就可以【重置所有证书】\n然后一路Yes同意就可以了\n然后按F12，左下角会显示捕获中，这个时候，按下ALT + Q 输入：bpu MZBuy.woa/wa/buyProduct然后回车即可\n然后回到iTunes搜索你想要的app，下载，这个时候fiddler就会拦截到一个 pxx-buy.itunes.apple.com的请求，就比如：\nhttps://p30-buy.itunes.apple.com/\n这个时候咱们就需要知道对应app的老版本的版本号啦\n推荐这个网站：sunny\n然后搜索app，找到对应的版本号，就比如我想要下载6版本，那我就会选择6.6.4后面的版本号。\n然后复制版本号，回到fiddler，点击那个pxx-buy，右边就会出现下面的界面，然后咱们选择 【检查器】\u0026gt; 【textview】\n然后不出意外的话，你的iTunes就开始下载app了，这个时候你就可以回到fiddler按F12关闭捕获，让它下载的更快。\n下载完成以后你就可以在：%homepath%\\Music\\iTunes\\iTunes Media\\Mobile Applications\\ 不过有一点需要注意的是！你手机的AppStore登陆的账号要和刚刚下载ipa用的账号一样！\n然后你就可以用爱思来安装这个ipa到手机里了\n","date":"2022-02-26T16:14:43+08:00","image":"https://cn.bing.com/th?id=OHR.MTCradle_EN-CN9383528705_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/ios_install_old_app/","title":"IOS安装老版本app的方法"},{"content":"Vue简单常识: v-bind:(单项数据绑定) 用来绑定数据的,区别于{{}},v-bind多用于标签元素的attribution,而{{}}多用于标签中间的数字/数值/字符串等\nv-bind可以简写为:,而且他是单项数据绑定,数据只能由data里面的变量决定,即使在表单元素里面修改,v-bind绑定的数据也不会有反应\nv-model:(双向数据绑定) 虽然都是数据绑定,但是v-model只能用于表单元素,而且表单内数据的更新会带着data内的数据一起更新.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 \u0026lt;div id=\u0026#34;doubt\u0026#34;\u0026gt; \u0026lt;h3\u0026gt;{{eerr}} {{Date.now()}} \u0026lt;/h3\u0026gt; \u0026lt;a v-bind:href=\u0026#34;urls\u0026#34; v-bind:title=\u0026#34;span_title\u0026#34; \u0026gt;{{intos}}\u0026lt;/a\u0026gt; \u0026lt;a :href=\u0026#34;urls\u0026#34; :title=\u0026#34;span_title\u0026#34; \u0026gt;{{intos}}\u0026lt;/a\u0026gt;\u0026lt;br\u0026gt; \u0026lt;input type=\u0026#34;text\u0026#34; v-model:value=\u0026#34;eerr\u0026#34; style=\u0026#34;width: 100%;margin-top: 30px\u0026#34;\u0026gt; \u0026lt;input type=\u0026#34;text\u0026#34; v-model=\u0026#34;intos\u0026#34; style=\u0026#34;width: 100%;margin-top: 30px\u0026#34;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;script type=\u0026#34;text/javascript\u0026#34; src=\u0026#34;vue.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; Vue.config.productionTip=false Vue.config.devtools=false const vapp = new Vue({ el: \u0026#34;#doubt\u0026#34;, data:{ intos:\u0026#34;去JokemeBlog\u0026#34;, eerr: \u0026#34;一刀999,是兄弟就来砍我\u0026#34;, urls:\u0026#34;https://jokeme.top\u0026#34;, span_title:\u0026#34;JOkemeBlog官网\u0026#34; } }) \u0026lt;/script\u0026gt; 使用mount挂载 其实咱可以不用上来就指定好el的值,咱可以已mount的形式来挂在上.\n1 2 3 \u0026lt;div id=\u0026#34;niubi\u0026#34;\u0026gt; {{welcome}} \u0026lt;/div\u0026gt; 1 2 3 4 5 6 const vapp = new Vue({ data:{ welcome:\u0026#34;欢迎学习Vue 2.0!\u0026#34; } }) vapp.$mount(\u0026#34;#niubi\u0026#34;) 说完了el咱再来说 data\ndata函数式 其实这里的data还可以写为函数式的. json/对象式 \u0026amp; 函数式都可以正常使用,以后使用组件的时候就必须要用函数式\n1 2 3 4 5 6 7 const vapp = new Vue({ data(){ return{ welcome:\u0026#34;欢迎学习Vue 2.0!\u0026#34; } } }) defineProperty Vue的代理模式,其中先大概了解一下defineProperty的用法:defineProperty(obj,properties,options/functions)\n代码我也懒得整,就截了两张图片,学过高级语言的 应该一眼就看明白了.代理也就是通过set()/get()操作的\nv-click 就是监听点击事件的,然后执行一个函数,可以加括号,不加括号就不传参\n1 2 3 4 \u0026lt;div id=\u0026#34;niubi\u0026#34;\u0026gt; \u0026lt;button @click=\u0026#34;callbaby(\u0026#39;ss\u0026#39;,$event)\u0026#34;\u0026gt;点我!!!!\u0026lt;/button\u0026gt; \u0026lt;!-- 传参的时候,这个$event就相当于关键字了,加上$才有事件,不加就是变量 --\u0026gt; \u0026lt;/div\u0026gt;\u0026gt; 1 2 3 4 5 6 7 8 9 const vapp = new Vue({ methods:{ callbaby(a,b){ console.log(a,b) alert(\u0026#34;Hello啊!同学!\u0026#34;) } } }) vapp.$mount(\u0026#34;#niubi\u0026#34;) 但是! 但是! 现在Vue应该是改了,不传$event 也可以直接输出event了,如下\n1 2 \u0026#34;callbaby(\u0026#39;ss\u0026#39;)\u0026#34; console.log(a,event) **Tips: **methods里面的方法也可以丢data里面哦,但是data会多一个代理,浪费电脑性能!又学到了一个离职埋坑小技巧哦!\nclick的事件修饰符\n反正就这几个常用的,而且还是前三个常用.\neg:\n1 \u0026lt;button class=\u0026#34;qq\u0026#34; @click.once=\u0026#34;callbaby(12)\u0026#34;\u0026gt;点我!!!!\u0026lt;/button\u0026gt;\u0026lt;br\u0026gt; 下面的这个就是这个click只在第一次操作触发,第二次及以后都不在响应事件了\n详细解说见 哔哩哔哩-尚硅谷-事件修饰符\n键盘监听 @keyup\n键盘回弹触发事件\n@keydown\n键盘按下触发事件\n下面的小代码就可以让你理解了,其中.enter是遇到回车键才执行\n为了方便起见,只贴出关键代码,其余部分都没有怎么变得就省略了,上面也有.\n1 \u0026lt;input type=\u0026#34;text\u0026#34; placeholder=\u0026#34;随便写点啥吧!\u0026#34; @keydown.enter=whichOneUInput\u0026gt; 1 2 3 whichOneUInput(e){ console.log(e.target.value) } 像这样的操作,Vue为我们准备了一下几个\n按键 别名 回车 enter 删除(捕获『删除』\u0026amp;『退回』) delete 退出 esc 空格 space 换行 tab 上下 up/down 左右 left/right @keydown.ctrl.y就可以让ctrl + y触发\n更多按键见:哔哩哔哩-尚硅谷-键盘事件\n计算属性 computed类似data,el,methods它也是Vue带来的全新属性,可以直接从data里面获取数据进行操作/计算\n1 2 3 4 5 6 7 8 9 10 11 12 13 data(){ return{ welcome:\u0026#34;欢迎学习Vue 2.0!\u0026#34;, dabo:\u0026#34;Jack\u0026#34; } }, computed:{ fullname:{ get(){ return this.welcome + \u0026#39;-\u0026#39; + this.dabo } } } 1 \u0026lt;p\u0026gt;{{fullname}}\u0026lt;/p\u0026gt; 计算完成以后就可以直接调用了,有点像Linux里的 alias,给一些列操作定义一个名字,我调用这个名字就执行这个操作\n监视属性 watch是一个用于监听数据变化的属性,他有一个方法handler(new,old)可以在监视的数据发生变化时做出相应的处理.其中new是改变后的数据,old是改变前的数据.\nimmediate:true可以让数据在初始化的时候就开始监听,就是从无到有的过程也执行一次handler()\n还有一点需要注意的是,被监视的数据只能是 data和computed里面的.\ndeep监视不能监视多层数据里面改变前的数据,只能拿到改变后的数据.\n1 \u0026lt;button @click=\u0026#34;dbqs.tom++\u0026#34;\u0026gt;加一\u0026lt;/button\u0026gt; 1 2 3 4 5 6 7 8 watch:{ temperature:{ immediate:true, handler(a,b){ console.log(a+\u0026#34;``\u0026#34;+b) } } }, 除此之外watch还有一种api调用方法\n1 2 3 4 5 6 shit.$watch(\u0026#34;temperature\u0026#34;,{ immediate:true, handler(a,b){ console.log(a+\u0026#34;``\u0026#34;+b) } }) deep的用法\n1 2 3 4 5 6 dbqs:{ deep:true, handler(a,b,c){ console.log(a.tom+\u0026#34;----\u0026#34;+b.tom) } } 此外当你在监视的时候,用不到set,你可以用简写\n1 2 3 temperature(a,b){ console.log(a+\u0026#34;----\u0026#34;+b) }, 直接就是你需要监视的数据变成函数名就可以了,handler有啥参数,这个简写就有啥.\n绑定Class样式 多说无益,先上代码\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 .basic{ width: 400px; height: 200px; border: 2px tomato solid; font-family: \u0026#39;Trebuchet MS\u0026#39;, \u0026#39;Lucida Sans Unicode\u0026#39;, \u0026#39;Lucida Grande\u0026#39;, \u0026#39;Lucida Sans\u0026#39;, Arial, sans-serif; margin-top: 30px; text-align: center; } .day { background-color: rgb(255, 255, 255); color: rgb(26, 26, 26); } .night{ background-color: rgb(49, 49, 49); color: rgb(226, 226, 226); } 1 2 3 4 5 6 \u0026lt;div id=\u0026#34;bobp\u0026#34; class=\u0026#34;basic\u0026#34; :class=\u0026#34;color\u0026#34; \u0026gt; \u0026lt;P\u0026gt;今天是个好日子啊!\u0026lt;/P\u0026gt; \u0026lt;p\u0026gt;老Tom今天在干嘛啊\u0026lt;/p\u0026gt; \u0026lt;hr style=\u0026#34;width: 80%;margin-left: auto;margin-right: auto;margin-top: 3px;margin-bottom: 3px;\u0026#34;\u0026gt; \u0026lt;button @click=\u0026#34;change\u0026#34;\u0026gt;改变\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; 1 2 3 4 5 6 7 8 9 data() { return { color:\u0026#34;day\u0026#34;, }, methods: { change(){ this.color == \u0026#34;day\u0026#34; ? this.color = \u0026#34;night\u0026#34; : this.color = \u0026#34;day\u0026#34; } }, 原理::class绑定一个data里面的变量color,当你点击按钮的时候,就会做出判断,给原来的样式basic 后面再加上day/night样式\n下面的两种会用就可以,根据实际情况来,数组和对象一般用的不是很多\nv-if 1 2 3 4 \u0026lt;p v-if=\u0026#34;codelang[0] == \u0026#39;Java\u0026#39;\u0026#34;\u0026gt;{{codelang[0]}}\u0026lt;/p\u0026gt; \u0026lt;p v-else-if=\u0026#34;codelang[1] == \u0026#39;Python\u0026#39;\u0026#34;\u0026gt;{{codelang[1]}}\u0026lt;/p\u0026gt; \u0026lt;p v-if=\u0026#34;codelang[2] == Cpp\u0026#34;\u0026gt;{{codelang[2]}}\u0026lt;/p\u0026gt; \u0026lt;p v-else\u0026gt;{{codelang[3]}}\u0026lt;/p\u0026gt; 1 codelang:[\u0026#34;Java\u0026#34;,\u0026#34;Python\u0026#34;,\u0026#34;Cpp\u0026#34;,\u0026#34;Golang\u0026#34;] 可以看的出来哈，v-if只会只要命中条件以后就会显示后面的内容而没有命中的条件都不不会出现。这里也没有啥难点，和别的语言的if else没有啥差别\n\u0026lt;template\u0026gt;标签只能与v-if一块用。其作用是不破坏dom结构。\nv-for 1 2 3 \u0026lt;ul style=\u0026#34;list-style-type: none;margin: auto;padding: 0;border: orange 1px solid;width: 80%;\u0026#34;\u0026gt; \u0026lt;li v-for=\u0026#34;item in codelang\u0026#34; :key=\u0026#34;index\u0026#34; style=\u0026#34;margin-top: 5px;margin-bottom: 5px;\u0026#34;\u0026gt;{{item}}\u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; 下面就来一段具体操作\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const shit = new Vue({ data() { return { user:[ {uuid:123,name:\u0026#34;lx\u0026#34;,sex:0}, {uuid:124,name:\u0026#34;by\u0026#34;,sex:1}, {uuid:163,name:\u0026#34;lj\u0026#34;,sex:1}, ], } }, methods: { adduser(){ // 这个函数在调用的 时候需要加上.once，否则会一直加人 this.user.unshift({uuid:156,name:\u0026#34;lx\u0026#34;,sex:1}) // unshift() 直接把数据干到最上面 // push() 数据加在最后面 } }, }) 1 2 3 4 5 \u0026lt;button @click.once=\u0026#34;adduser\u0026#34;\u0026gt;click me!\u0026lt;/button\u0026gt; \u0026lt;ul\u0026gt; \u0026lt;li v-for=\u0026#34;item in user\u0026#34; :key=\u0026#34;item.uuid\u0026#34; \u0026gt;{{item.name}} --- {{item.uuid}} \u0026lt;input type=\u0026#34;text\u0026#34; :placeholder=\u0026#34;item.name\u0026#34;\u0026gt; \u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; 箭头函数 复习一下！看这里 廖雪峰的官方网站\nfilter 同上复习：w3school\n表单收集 哔哩哔哩-尚硅谷-表单收集\n自定义Vue指令 哔哩哔哩-尚硅谷-自定义指令\n生命周期 1 2 3 4 5 6 7 8 9 10 mounted() { setInterval(() =\u0026gt; { this.opacity -= 0.01 if (this.opacity \u0026lt;= 0) { a = Math.random(0, 1) this.opacity = a console.log(a) } }, 15) }, 主要的函数都在这里\n哔哩哔哩-尚硅谷-生命周期\ncomponents(组件) Q1：什么是components(组件)？\nVue里面的组件其实就是相当于一个小型的Vue实例，Vue可以干的，它也可以，但是它就是不可以直接被$mount,换句话说也就是不能有自己的el:元素\nQ2：如何定义一个组件？\n问得好呀！\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 //这种是完整写法 const jokeme = Vue.extend({ template:`` ... ... }) // 简写形式,只需要为对象即可,以后基本上都是用这种 const goofiest = { template:` \u0026lt;div\u0026gt; \u0026lt;p\u0026gt;网站名称: {{webname}} \u0026lt;/p\u0026gt; \u0026lt;p\u0026gt;网站地址: {{site}} \u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; `, data(){ return{ webname: \u0026#34;goofiest\u0026#34;, site:\u0026#34;goofiest.top\u0026#34; } } } Q3：定义完了以后咋使用呀？\n1 2 3 4 5 6 7 8 9 10 11 12 13 const vm = new Vue({ components:{ // 局部注册,Σ(っ °Д °;)っ,只有当前的Vue实例可以使用 laozhang:goofiest,// goofiest, 如果前后名字一样,直接写一个也是可以的 // laoli:jokeme }, methods:{ show(){ console.log(\u0026#34;靓仔你谁呀?\u0026#34;) } } }) Vue.component(\u0026#34;ll\u0026#34;,jokeme)//全局注册所有的Vue实例都可以使用 render\u0026amp;\u0026amp;ref 中间几节课简单就没有写代码了，链接在下面\n哔哩哔哩-尚硅谷-render函数\n哔哩哔哩-尚硅谷-修改默认配置\n哔哩哔哩-尚硅谷-ref属性\nprops传参 咱们先在Windows.vue里面定义好下面的props\n1 2 3 4 5 6 7 8 9 10 11 12 13 props:{ giveup:String, version:Number, }, //带有数据校验的接收 // props:{ // giveup:{ // type:String, // required:true, // default:\u0026#39;111\u0026#39; // } // } // 这种方式就可以更详细的约定那些是需要的,那些不需要,默认值等等... 然后在App.vue里面的Windows标签传递参数，\n1 \u0026lt;Windows ref=\u0026#34;xx\u0026#34; giveup=\u0026#34;No\u0026#34; :version=\u0026#34;19043.1466\u0026#34; \u0026gt;\u0026lt;/Windows\u0026gt; 学习课程见：哔哩哔哩-尚硅谷-props配置\nmixin 咱首先需要在main.js同级目录下建一个 **mixin.js**的文件\n然后文件内容就是：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const wheseisvc = { data(){ return { hell:\u0026#34;dashaz\u0026#34; } }, methods:{ sch(){ console.log(\u0026#34;i am here\u0026#34;,this.hell); // alert(\u0026#34;I am here\u0026#34;) } } } // 在这个文件里可以定义data,methods,watch... vue允许的都可以 // 可以全局引入mixin方法就是在main.js 里面定义 // Vue.mixin(whereisvc) // 单组件定义就在该组件里面import使用即可 export default wheseisvc; 写完了以后一定要export出去，要不然导入的时候会报错的\n1 2 3 4 5 6 7 \u0026lt;template\u0026gt; \u0026lt;h2 @click.once=\u0026#34;sch\u0026#34;\u0026gt;OS_Creator: {{creator}} \u0026lt;/h2\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;script\u0026gt; import whereisvc from \u0026#34;../mixin\u0026#34; \u0026lt;script\u0026gt; 就在标签里面可以直接调用mixin里面定义好的方法了\n学习课程见：哔哩哔哩-尚硅谷-mixin\nplugins\u0026amp;\u0026amp;scoped 我也没有写代码，学习课程在下面\n哔哩哔哩-尚硅谷-插件\n哔哩哔哩-尚硅谷-scoped\n简单案例 姓名案例 1 2 3 4 5 6 \u0026lt;div id=\u0026#34;niubi\u0026#34;\u0026gt; {{welcome}}\u0026lt;br\u0026gt; \u0026lt;input type=\u0026#34;text\u0026#34; placeholder=\u0026#34;随便写点啥吧!\u0026#34; v-model=welcome\u0026gt; \u0026lt;input type=\u0026#34;text\u0026#34; placeholder=\u0026#34;随便写点啥吧!\u0026#34; v-model=dabo\u0026gt; \u0026lt;button @click=fullname\u0026gt;dianwo\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 \u0026lt;html\u0026gt; ... \u0026lt;laozhang\u0026gt;\u0026lt;/laozhang\u0026gt; \u0026lt;ll\u0026gt;\u0026lt;/ll\u0026gt; \u0026lt;/html\u0026gt; \u0026lt;script\u0026gt; const vapp = new Vue({ data(){ return{ welcome:\u0026#34;欢迎学习Vue 2.0!\u0026#34;, dabo:\u0026#34;Jack\u0026#34; } }, methods:{ fullname(){ console.log(this.welcome + \u0026#34;-\u0026#34; + this.dabo) } } }) vapp.$mount(\u0026#34;#niubi\u0026#34;) \u0026lt;/script\u0026gt; 天气案例 点击按钮切换今天的天气\n1 2 3 4 \u0026lt;div id=\u0026#34;bobp\u0026#34;\u0026gt; \u0026lt;P\u0026gt;今天是个好日子啊!天气{{weatherQ}}\u0026lt;/P\u0026gt; \u0026lt;button @click=\u0026#34;turnWeather()\u0026#34;\u0026gt;点我\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const shit = new Vue({ data() { return { temperature:true } }, methods: { turnWeather(){ this.temperature == true ? this.temperature = false :this.temperature = true } }, computed: { weatherQ:{ get(){ return this.temperature ? \u0026#34;很热\u0026#34; : \u0026#34;凉爽\u0026#34; } } } }) shit.$mount(\u0026#34;#bobp\u0026#34;) 通过计算属性拿到temperature以后在计算返回文字weatherQ,这更加人性化\n名称查找 在input框内输入，下面的ul显示查询结果\nVue watch实现 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 45 46 47 48 49 50 51 52 \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;UTF-8\u0026#34;\u0026gt; \u0026lt;meta name=\u0026#34;viewport\u0026#34; content=\u0026#34;width=device-width, initial-scale=1.0\u0026#34;\u0026gt; \u0026lt;title\u0026gt;Document\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div id=\u0026#34;kendaman\u0026#34;\u0026gt; \u0026lt;input type=\u0026#34;text\u0026#34; placeholder=\u0026#34;输入以搜索🔍\u0026#34; v-model=\u0026#34;kw\u0026#34;\u0026gt; \u0026lt;ul\u0026gt; \u0026lt;li v-for=\u0026#34;it in userT\u0026#34; :key=\u0026#34;it.uuid\u0026#34;\u0026gt; {{it.name}} \u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;script src=\u0026#34;vue.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; Vue.config.productionTip = false Vue.config.devtools = false const vapp = new Vue({ data() { return { kw: \u0026#39;\u0026#39;, user: [ { uuid: 123, name: \u0026#34;托塔天王\u0026#34;, sex: 0 }, { uuid: 124, name: \u0026#34;斗战胜佛\u0026#34;, sex: 1 }, { uuid: 163, name: \u0026#34;净坛使者\u0026#34;, sex: 1 }, { uuid: 165, name: \u0026#34;祥龙罗汉\u0026#34;, sex: 1 } ], userT: [], } }, methods: { }, watch: { kw: { immediate: true, handler(newkw) { this.userT = this.user.filter((u) =\u0026gt; { return u.name.indexOf(newkw) !== -1 }) } } }, }) vapp.$mount(\u0026#34;#kendaman\u0026#34;) \u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 这里的一个小知识点是：immediate在初始化的时候就会执行一次，而此时，kw即我们的输入为空，newkw监视变化前的值也为空，filter在过滤的时候，匹配到空，是会返回0的而不是-1，匹配成功，因而最终返回user数组里面所有的数据给userT。众所周知js里面，任何字符串里都包含空字符串的。狗头保命！\nVue 计算属性实现 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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;style\u0026gt; .bk{ background-color: #2b2b2b; color: #dbdbdb; } .er { width: 300px; height: 200px; margin: 100px auto; border: 1px rgb(104, 104, 104) solid; border-radius: 10px; box-shadow: 1px 1px 4px 1px cornflowerblue ; } .ipt{ background-color: #3f3f3f; } .ert{ width: 80%; line-height: 30px; margin:0 auto; margin-top: 10px; list-style: none; border-radius:5px; text-align: center; padding: 0; } li{ width: 80%; border-radius: 3px; margin:4px auto; background-color: #3f3f3f; font-size: 14px; } li:hover{ border: 1px #caa155 solid; margin: -1px auto; font-size: 15px; } \u0026lt;/style\u0026gt; \u0026lt;meta charset=\u0026#34;UTF-8\u0026#34;\u0026gt; \u0026lt;meta name=\u0026#34;viewport\u0026#34; content=\u0026#34;width=device-width, initial-scale=1.0\u0026#34;\u0026gt; \u0026lt;title\u0026gt;Document\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body class=\u0026#34;bk\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;er\u0026#34; id=\u0026#34;kendaman\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;ert\u0026#34;\u0026gt; \u0026lt;input class=\u0026#34;ert ipt\u0026#34; type=\u0026#34;text\u0026#34; placeholder=\u0026#34;输入以搜索🔍\u0026#34; v-model=\u0026#34;kw\u0026#34;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;ul class=\u0026#34;ert\u0026#34;\u0026gt; \u0026lt;li v-for=\u0026#34;it in userT\u0026#34; :key=\u0026#34;it.uuid\u0026#34;\u0026gt; {{it.name}} \u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;script src=\u0026#34;vue.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; Vue.config.productionTip = false Vue.config.devtools = false const vapp = new Vue({ data() { return { kw: \u0026#39;\u0026#39;, user: [ { uuid: 123, name: \u0026#34;托塔天王\u0026#34;, sex: 0 }, { uuid: 124, name: \u0026#34;斗战胜佛\u0026#34;, sex: 1 }, { uuid: 163, name: \u0026#34;净坛使者\u0026#34;, sex: 1 }, { uuid: 165, name: \u0026#34;祥龙罗汉\u0026#34;, sex: 1 } ], } }, methods: { }, computed: { userT() { return this.user.filter((u) =\u0026gt; { return u.name.indexOf(this.kw) !== -1 }) } } }) vapp.$mount(\u0026#34;#kendaman\u0026#34;) \u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; computed里面有两个返回值可能比较搞心态，但是你只需要明白，第一个返回值是给filter做判断的，第二个返回值是给userT的就可以了，主要的原理和上面的一样，也是filter和箭头函数\n咱们来欣赏一下成果吧！暗黑模式真好看，我真厉害😜!\n排序案例 接上面的案例继续,如果咱们有一个排序的要求并且是按照uuid或者是姓名来排序的呢?这活应该怎么整?\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 computed: { userT() { let hugo = this.user.filter((u) =\u0026gt; { return u.name.indexOf(this.kw) !== -1 }) if(this.sortby){ hugo.sort((a,b)=\u0026gt;{ if(this.sortby == 1){ return a.uuid - b.uuid//升序前减后 } if(this.sortby == 2){ return b.uuid - a.uuid//降序后减前 } }) } return hugo }, } 废话也不多说,就这样写,杠杠的! 对搜索后的数据进行排序那是相当的easy了,只需要在查找到的符合条件的数组后面接着写一个排序即可,写完了把排序后的数组再返回出去就可以了.\nnpm基础 npm是node.js的一个包管理器,有点像pip,咱们在学Vue以后是需要vue-cli来操作的create,build\u0026hellip;\n首先安装node！不管是用apt、yum。还是手动下载安装包配置环境变量都可以，装上能有node环境就可以了\n安装完node以后呢，由于默认的服务器在国外，咱们下载包会非常的慢，所以！换源！\n1 npm config set registry https://registry.npm.taobao.org 或者直接使用**cnpm**替代npm\n接着就可以安装Vue了，咱们这里全局安装：\n1 npm install -g @vue/cli 安装完Vue就可以创建咱们的第一个项目了\n1 vue create xxx 创建完Vue项目以后，最后一行会提示你cd xxx ；npm run serve\n按照命令来就可以了\n就可以在浏览器看到项目了\ntodolist(一代目) 首先是项目拆分,看看todolist可以怎么样拆,\n我是把项目拆分为三个模块的:input,itenlist.status\nApp.vue 这个组件也就是用来调度所有的VC，所以在\u0026lt;script\u0026gt;里面先导入三个子组件，并且在components里面注册三个组件\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 \u0026lt;script\u0026gt; import ipt from \u0026#34;./components/input\u0026#34;; import todoitem from \u0026#34;./components/todoitem\u0026#34;; import status from \u0026#34;./components/status\u0026#34;; export default { name: \u0026#34;App\u0026#34;, components: { ipt, todoitem, status }, } \u0026lt;/script\u0026gt; 其次就是methods这里面的方法大都是配合与子组件通信用的。做法都是把函数定义在父组件里面，通过子组件的调用来达到传参的目的，这种做法算是最最最基础原始的做法。但是缺点也很明显如果 父 -\u0026gt; 孙就太麻烦了，需要父 -\u0026gt; 子；子 -\u0026gt; 孙。耦合性太高了，如果需要改动代码，那就即将是一件非常痛苦的事情。但是以我目前的水平也就只能写出这样的扣脚代码！但是咱不急！等着后面二代目，三代目版本的慢慢优化吼！\n1 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 \u0026lt;script\u0026gt; methods: { //添加待办事项的 // 配合input.vue组件的使用,在这里定义好函数,然后再在input里面调用达到input-\u0026gt;App传递数据的目的 adddolist(e) { this.dolist.unshift(e); }, // 删除项目 原理同上 delt(uid){ for(var er = 0;er\u0026lt;this.dolist.length;er++){ if (this.dolist[er].uuid == uid) { this.dolist.splice(er, 1); break; } } }, // 标记项目已完成 原理同上 setstatus(uid){ for (let er = 0; er \u0026lt; this.dolist.length; er++){ if(this.dolist[er].uuid == uid){ this.dolist[er].done = !(this.dolist[er].done) } } } }, \u0026lt;/script\u0026gt; 再来看\u0026lt;template\u0026gt;标签，这里面都是App向其子组件传递的数据、函数\n1 2 3 4 5 6 7 \u0026lt;template\u0026gt; \u0026lt;div class=\u0026#34;edge\u0026#34;\u0026gt; \u0026lt;ipt :adddolist=\u0026#34;adddolist\u0026#34;\u0026gt;\u0026lt;/ipt\u0026gt; \u0026lt;todoitem :itemlist=\u0026#34;dolist\u0026#34; :delt=\u0026#34;delt\u0026#34; :setstatus=\u0026#34;setstatus\u0026#34;\u0026gt;\u0026lt;/todoitem\u0026gt; \u0026lt;status :itemlistSta=\u0026#34;dolist\u0026#34;\u0026gt;\u0026lt;/status\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; input.vue 这个组件就是来完成输入的整体功能是相当的简单🍳，咱们先来看看\u0026lt;template\u0026gt;标签，就一个\u0026lt;div\u0026gt;包裹着\u0026lt;input\u0026gt;但是这个input有一个键盘⌨监听事件“addItem”，可在用户按下enter时执行函数。\n1 2 3 4 5 \u0026lt;template\u0026gt; \u0026lt;div :class=\u0026#34;ipt\u0026#34; id=\u0026#34;commom\u0026#34;\u0026gt; \u0026lt;input type=\u0026#34;text\u0026#34; :placeholder=\u0026#34;pleaseholder\u0026#34; @keydown.enter=\u0026#34;addItem\u0026#34;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; 接下来咱再来看看\u0026lt;script\u0026gt;里面写了啥\ndata:定义了placeholder，可以让我们自己去动态的更新！很人性！\nprops:接收adddolist函数。\nmethods:就是添加数据的时候不允许空值，然后把数据打包成数据发给父组件，最后清空一下value就大功告成了\nwatch:自动根据时间切换黑暗模式\n1 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 \u0026lt;script\u0026gt; export default { data() { return { ipt:\u0026#34;ipt-day\u0026#34;, pleaseholder:\u0026#34;你想干啥?\u0026#34; } }, props:[\u0026#34;adddolist\u0026#34;], methods: { addItem(e){ if (e.target.value == \u0026#34;\u0026#34;) { alert(\u0026#34;不允许输入空值哦!\u0026#34;)\t} else { const abcd = Math.floor(Math.random()*1000+1) const fna ={\u0026#34;uuid\u0026#34;:abcd,\u0026#34;thing\u0026#34;:e.target.value,\u0026#34;done\u0026#34;:false} this.adddolist(fna) e.target.value = \u0026#34;\u0026#34; } }, }, watch:{ ipt:{ immediate:true, handler(){ let ho = new Date().getHours() if (ho \u0026gt;= 7 \u0026amp;\u0026amp; ho \u0026lt; 18) { this.ipt=\u0026#34;ipt-day\u0026#34; } else { this.ipt=\u0026#34;ipt-night\u0026#34; } } } } } \u0026lt;/script\u0026gt; todoitem.vue 1 2 3 4 5 6 7 8 9 10 11 12 13 \u0026lt;template\u0026gt; \u0026lt;div :class=\u0026#34;sus\u0026#34; id=\u0026#34;todoit\u0026#34; v-show=\u0026#34;itemlist.length != 0\u0026#34; \u0026gt; \u0026lt;div class=\u0026#34;fir\u0026#34; v-for=\u0026#34;item in itemlist\u0026#34; :key=\u0026#34;item.uuid\u0026#34;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;label @click=\u0026#34;checkstat($event,item.uuid)\u0026#34;\u0026gt; \u0026lt;input type=\u0026#34;checkbox\u0026#34; :checked=\u0026#34;item.done\u0026#34;\u0026gt; \u0026lt;span\u0026gt;{{item.thing}} \u0026lt;/span\u0026gt; \u0026lt;/label\u0026gt; \u0026lt;button @click=\u0026#34;deletet(item.uuid)\u0026#34;\u0026gt;删除\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; 首先就是v-show=\u0026quot;itemlist.length != 0\u0026quot;在没有数据的情况下不会显示该项。其次v-for=\u0026quot;item in itemlist\u0026quot;利用v-for渲染待办事项。然后就是利用\u0026lt;label\u0026gt;标签来提升一下用户体验，一个checkbox类型的input来标识是否完成\n1 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 \u0026lt;script\u0026gt; export default { data(){ return { sus:\u0026#34;item-sur-day\u0026#34; } }, props:[\u0026#39;itemlist\u0026#39;,\u0026#34;delt\u0026#34;,\u0026#34;setstatus\u0026#34;], methods:{ //删除待办 deletet(uuid){ this.delt(uuid) }, //完成待办， checkstat(ele,uid){ if(ele.target.tagName == \u0026#34;SPAN\u0026#34;){ return } this.setstatus(uid) } }, watch:{ sus:{ immediate:true, handler(){ let ho = new Date().getHours() if (ho \u0026gt;= 7 \u0026amp;\u0026amp; ho \u0026lt; 18) { this.sus=\u0026#34;item-sur-day\u0026#34; } else { this.sus=\u0026#34;item-sur-night\u0026#34; } } }, } } \u0026lt;/script\u0026gt; status.vue 其实这个没有啥好说的，代码里面都是浅显易懂的东西！主要就是统计一共有几个待办事项，已完成几个。还有和父组件的参数传递\n最后来看看一代目的成品吧\n","date":"2022-01-19T15:58:02+08:00","image":"https://cn.bing.com/th?id=OHR.LakeWinnipeg_EN-CN1852349163_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/learn_vuejs/","title":"学习Vue.js笔记"},{"content":"webdav搭建 最近闲来无事,想利用一下自己的公网IP做点别的有意思的事,因为之前搭建的网盘都统统没有用了,可能是用不来吧,还是在用的samba.\n就突然想到了为什么不搭建一个webdav呢！\n因为不打算用apache或者nginx来搭建，就去GitHub找了一下，还真的有一个不错的项目，使用Golang。那就开干吧。\n准备工作 首先需要下载这个webdav，我只贴了项目地址，因为可能到时候这个项目又更新了。\n1 2 3 4 5 wget https://github.com/hacdias/webdav/releases/download/v4.1.1/linux-amd64-webdav.tar.gz mkdir ~/.ENV/webdav\t# ~/.ENV 是我个人习惯放的地方 mv linux-amd64-webdav.tar.gz ~/.ENV/webdav tar -zxvf linux-amd64-webdav.tar.gz \u0026amp;\u0026amp; rm linux-amd64-webdav.tar.gz touch config.yml #config.{json, toml, yaml, yml} 都可以，而且它会在 ./ \u0026amp; /etc/webdav/下查找文件 配置文件 然后就是配置文件的内容了，我的是yaml格式的，大家可以参考一下。\n1 2 3 4 5 6 7 8 9 address: 0.0.0.0 port: 8090 auth: true tls: false prefix: / users: - username: username password: password scope: /home/username/xxx 下面的这个是官网给的详细介绍，比较全但是也有很多我用不到的配置。\n1 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 scope: . modify: true rules: [] # CORS configuration cors: enabled: true credentials: true allowed_headers: - Depth allowed_hosts: - http://localhost:8080 allowed_methods: - GET exposed_headers: - Content-Length - Content-Range users: - username: admin password: admin scope: /a/different/path - username: encrypted password: \u0026#34;{bcrypt}$2y$10$zEP6oofmXFeHaeMfBNLnP.DO8m.H.Mwhd24/TOX2MWLxAExXi4qgi\u0026#34; - username: \u0026#34;{env}ENV_USERNAME\u0026#34; password: \u0026#34;{env}ENV_PASSWORD\u0026#34; - username: basic password: basic modify: false rules: - regex: false allow: false path: /some/file - path: /public/access/ modify: true 开机自启 这一步需要编写service文件 webdav.service.example，官网给了栗子了，我还是贴一下自己的吧。\n1 2 3 4 5 6 7 8 9 10 11 12 [Unit] Description=WebDAV server After=network.target [Service] Type=simple User=root ExecStart=/home/frelon/.ENV/webdav/webdav --config /home/frelon/.ENV/webdav/config.yml Restart=on-failure [Install] WantedBy=multi-user.target 1 2 3 sudo systemctl daemon-reload sudo systemctl start webdav sudo systemctl enable webdav 然后就可以正常使用了，但是如果你觉得端口不如意，咱可以用nginx代理到80。\nnginx代理 1 2 3 4 5 6 7 8 9 10 11 12 13 server { #webdav listen 80; server_name 192.168.31.100 jokeme.top; location / { proxy_pass http://127.0.0.1:8090; proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; } } proxy_pass下面的五个是必须的，要不然容易出事。\n然后重启nginx就大功告成了。\n","date":"2021-12-16T17:28:59+08:00","image":"https://cn.bing.com/th?id=OHR.HumpbackMom_EN-CN4611779179_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/configure_webdav/","title":"webdav搭建"},{"content":"KMP查找算法 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 import java.util.Arrays; public class getstr { public static void main(String[] args) { String txt = \u0026#34;qwertyqwertyuisdgfsdnhjt\u0026#34;; String gol = \u0026#34;sdgfsdn\u0026#34;; int i = kmp(txt,gol); System.out.println(\u0026#34;matchPoint: \u0026#34; + i); } static int kmp(String str1, String str2){ int [] ary =getnext(str2); System.out.println(Arrays.toString(ary)); for(int i =0,j = 0; i \u0026lt; str1.length();i++){ while(j \u0026gt; 0 \u0026amp;\u0026amp; str1.charAt(i) != str2.charAt(j) ){ j = ary[j-1]; } if(str1.charAt(i) == str2.charAt(j)){ j++; } if(j == str2.length()){ // System.out.println(\u0026#34;i = \u0026#34;+i+\u0026#34;\\nj = \u0026#34;+j); return i-j+1; } } return -1; } static int [] getnext(String str2){ int [] ary = new int[str2.length()]; ary[0] = 0; for(int i = 1, y = 0; i \u0026lt; str2.length(); i++ ){ while(y \u0026gt; 0 \u0026amp;\u0026amp; str2.charAt(i) != str2.charAt(y)){ y-=1; } if(str2.charAt(i) == str2.charAt(y)){ y++; } ary[i] = y; } return ary; } } 其实一开始我也没有咋明白,但是后来我找到了下面这个文章 smoke_zl_图解kmp算法-通俗易懂kmp算法 我就一下捂了,这个查找算法太妙了,就靠这一个数组,就可以实现快速查找不回溯\n","date":"2021-11-05T23:31:27+08:00","image":"https://cn.bing.com/th?id=OHR.LoftedMadagascar_EN-CN4501988190_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/kmp/","title":"kmp算法Java版"},{"content":"今天晚上就突然想搭建一个Hadoop的伪分布式玩一玩,说干就干\n准备:CentOS 7 系统 Hadoop 2.7.7 安装包 Java SE 1.8 安装包\nHadoop 2.7.7 下载\nJava SE 1.8 下载\nCentOS 7下载\nVMware 安装系统就不用说了,先安装一个系统就可以,另一个可以直接clone\n我因为已经安装完成了,就在WSL-Ubuntu里面大概敲一下代码\n解压Java SE 和 Hadoop的安装包 1 2 tar -zxvf hadoop-2.8.5.tar.gz tar -zxvf jdk-8u231-linux-x64.tar.gz 配置环境变量 我是编辑 .bash_profile 让我一个用户可以使用这个环境变量,大家也可以配置 /etc/profile文件,全局使用此配置,作用一样\n1 2 3 4 5 6 export JAVA_HOME=/home/jokeme/jdk1.8.0_231 export HADOOP_HOME=hadoop-2.8.5 export JRE_HOME=/home/jokeme/jdk1.8.0_231/jre export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib export PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin 然后:\n1 source .bash_profile 试一试\n1 2 3 java -version javac -version hadoop -version 如果配置正确就可以弹出正确的版本号\n更改Hostname和Hosts文件 1 sudo vim /etc/hostname 1 HadoopMaster 保存退出\n1 sudo vim /etc/hosts 1 2 192.168.1.199 HadoopClient 192.168.1.215 HadoopMaster 根据具体的 IP 地址和 Hostname 更改 . 注意 : Hostname 不区分大小写\n保存退出\n关闭防火墙和SELINUX 1 2 systemctl disable firewalld vim /etc/selinux/config 修改为以下内容\n1 SELINUX=disabled 重启系统,使配置生效\n1 reboot 开始配置Hadoop的配置文件 1 2 cd hadoop-2.8.5/etc/hadoop vim hadoop-env.sh 编辑一下具体的 JAVA_HOME\n1 export JAVA_HOME=/home/hadoop/jdk1.8.0_231 保存退出\n1 vim core-site.xml 1 2 3 4 5 6 7 8 9 10 11 \u0026lt;configuration\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;fs.defaultFS\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;hdfs://HadoopMaster:9000\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;hadoop.tmp.dir\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;/home/hadoop/hadoop-2.7.7/tmp\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;/configuration\u0026gt; 保存退出\n1 vim hdfs-site.xml 1 2 3 4 5 6 \u0026lt;configuration\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;dfs.replication\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;1\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;/configuration\u0026gt; 保存退出\n1 2 cp mapred-site.xml.template mapred-site.xml vim mapred-site.xml 1 2 3 4 5 6 \u0026lt;configuration\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;mapreduce.framework.name\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;yarn\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;/configuration\u0026gt; 保存退出\n1 vim yarn-site.xml 1 2 3 4 5 6 7 8 9 10 11 \u0026lt;configuration\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.resourcemanager.hostname\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;HadoopMaster\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.nodemanager.aux-services\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;mapreduce_shuffle\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;/configuration\u0026gt; 保存退出\n1 vim slaves 1 2 HadoopMaster HadoopClient 这里需要把 DataNode Hostname都添加进来,根据具体情况修改\n关闭虚拟机Clone一个新的主机出来 Clone完成后还需要更改 Hostname 和 IP 地址以及 MAC 地址\n1 vim /etc/sysconfig/network-scripts/ifcfg-ens33 修改以下代码\n1 IPADDR=xxx.xxx.xxx.xxx 添加以下代码\n1 MACADDR=xx.xx.xx.xx.xx.xx 都是根据具体情况修改\n配置免密登陆 1 ssh-keygen 一直回车就可以了\n然后再\n1 2 ssh-copy-id hadoop@HadoopMaster ssh-copy-id hadoop@HadoopClient 再修改以下配置文件,允许免密登陆\n1 sudo vim /etc/ssh/sshd_config 1 PubkeyAuthentication yes HadoopMaster 上也是同样操作,不在赘述\n启动Hadoop 回到 HadoopMaster\n1 2 cd ~/hadoop2.7.7/sbin ./start-all.sh 安装过程大概就这些,如果启动成功就可以用 jps 命令查看到以下进程\n1 6899 DataNode 7092 SecondaryNameNode 7253 ResourceManager 7366 NodeManager 8330 Jps 6796 NameNode 但是我 jps 时少了一个 NameNode 节点 导致我一直进不去 HDFS 的 web 界面,这让我非常头疼,百度了半天也没有结果, 于是用 Google 搜了一下,说可能是我的 NameNode 没有格式化,于是我敲了以下命令,重启了一下Hadoop,就好了\n1 hadoop namenode -format 再次进入 web 界面也正常了 , 8088 端口也可以访问了\nApache 官方文档\nApache 的 fs shell 操作大全 cat 使用方法：hadoop fs -cat URI [URI …]\n将路径指定文件的内容输出到stdout。\n示例：\nhadoop fs -cat hdfs://host1:port1/file1 hdfs://host2:port2/file2 hadoop fs -cat file:///file3 /user/hadoop/file4 返回值： 成功返回0，失败返回-1。\nchgrp 使用方法：hadoop fs -chgrp [-R] GROUP URI [URI …] Change group association of files. With -R, make the change recursively through the directory structure. The user must be the owner of files, or else a super-user. Additional information is in the Permissions User Guide.\n改变文件所属的组。使用-R将使改变在目录结构下递归进行。命令的使用者必须是文件的所有者或者超级用户。更多的信息请参见HDFS权限用户指南。\nchmod 使用方法：hadoop fs -chmod [-R] \u0026lt;MODE[,MODE]... | OCTALMODE\u0026gt; URI [URI …]\n改变文件的权限。使用-R将使改变在目录结构下递归进行。命令的使用者必须是文件的所有者或者超级用户。更多的信息请参见HDFS权限用户指南。\nchown 使用方法：hadoop fs -chown [-R] [OWNER][:[GROUP]] URI [URI ]\n改变文件的拥有者。使用-R将使改变在目录结构下递归进行。命令的使用者必须是超级用户。更多的信息请参见HDFS权限用户指南。\ncopyFromLocal 使用方法：hadoop fs -copyFromLocal \u0026lt;localsrc\u0026gt; URI\n除了限定源路径是一个本地文件外，和put命令相似。\ncopyToLocal 使用方法：hadoop fs -copyToLocal [-ignorecrc] [-crc] URI \u0026lt;localdst\u0026gt;\n除了限定目标路径是一个本地文件外，和get命令类似。\ncp 使用方法：hadoop fs -cp URI [URI …] \u0026lt;dest\u0026gt;\n将文件从源路径复制到目标路径。这个命令允许有多个源路径，此时目标路径必须是一个目录。 示例：\nhadoop fs -cp /user/hadoop/file1 /user/hadoop/file2 hadoop fs -cp /user/hadoop/file1 /user/hadoop/file2 /user/hadoop/dir 返回值：\n成功返回0，失败返回-1。\ndu 使用方法：hadoop fs -du URI [URI …]\n显示目录中所有文件的大小，或者当只指定一个文件时，显示此文件的大小。 示例： hadoop fs -du /user/hadoop/dir1 /user/hadoop/file1 hdfs://host:port/user/hadoop/dir1 返回值： 成功返回0，失败返回-1。\ndus 使用方法：hadoop fs -dus \u0026lt;args\u0026gt;\n显示文件的大小。\nexpunge 使用方法：hadoop fs -expunge\n清空回收站。请参考HDFS设计文档以获取更多关于回收站特性的信息。\nget 使用方法：hadoop fs -get [-ignorecrc] [-crc] \u0026lt;src\u0026gt; \u0026lt;localdst\u0026gt; 复制文件到本地文件系统。可用-ignorecrc选项复制CRC校验失败的文件。使用-crc选项复制文件以及CRC信息。\n示例：\nhadoop fs -get /user/hadoop/file localfile hadoop fs -get hdfs://host:port/user/hadoop/file localfile 返回值：\n成功返回0，失败返回-1。\ngetmerge 使用方法：hadoop fs -getmerge \u0026lt;src\u0026gt; \u0026lt;localdst\u0026gt; [addnl]\n接受一个源目录和一个目标文件作为输入，并且将源目录中所有的文件连接成本地目标文件。addnl是可选的，用于指定在每个文件结尾添加一个换行符。\nls 使用方法：hadoop fs -ls \u0026lt;args\u0026gt;\n如果是文件，则按照如下格式返回文件信息： 文件名 \u0026lt;副本数\u0026gt; 文件大小 修改日期 修改时间 权限 用户ID 组ID 如果是目录，则返回它直接子文件的一个列表，就像在Unix中一样。目录返回列表的信息如下： 目录名 修改日期 修改时间 权限 用户ID 组ID 示例： hadoop fs -ls /user/hadoop/file1 /user/hadoop/file2 hdfs://host:port/user/hadoop/dir1 /nonexistentfile 返回值： 成功返回0，失败返回-1。\nlsr 使用方法：hadoop fs -lsr \u0026lt;args\u0026gt; ls命令的递归版本。类似于Unix中的ls -R。\nmkdir 使用方法：hadoop fs -mkdir \u0026lt;paths\u0026gt; 接受路径指定的uri作为参数，创建这些目录。其行为类似于Unix的mkdir -p，它会创建路径中的各级父目录。\n示例：\nhadoop fs -mkdir /user/hadoop/dir1 /user/hadoop/dir2 hadoop fs -mkdir hdfs://host1:port1/user/hadoop/dir hdfs://host2:port2/user/hadoop/dir 返回值：\n成功返回0，失败返回-1。\nmovefromLocal 使用方法：dfs -moveFromLocal \u0026lt;src\u0026gt; \u0026lt;dst\u0026gt;\n输出一个”not implemented“信息。\nmv 使用方法：hadoop fs -mv URI [URI …] \u0026lt;dest\u0026gt;\n将文件从源路径移动到目标路径。这个命令允许有多个源路径，此时目标路径必须是一个目录。不允许在不同的文件系统间移动文件。 示例：\nhadoop fs -mv /user/hadoop/file1 /user/hadoop/file2 hadoop fs -mv hdfs://host:port/file1 hdfs://host:port/file2 hdfs://host:port/file3 hdfs://host:port/dir1 返回值：\n成功返回0，失败返回-1。\nput 使用方法：hadoop fs -put \u0026lt;localsrc\u0026gt; ... \u0026lt;dst\u0026gt;\n从本地文件系统中复制单个或多个源路径到目标文件系统。也支持从标准输入中读取输入写入目标文件系统。 hadoop fs -put localfile /user/hadoop/hadoopfile hadoop fs -put localfile1 localfile2 /user/hadoop/hadoopdir hadoop fs -put localfile hdfs://host:port/hadoop/hadoopfile hadoop fs -put - hdfs://host:port/hadoop/hadoopfile 从标准输入中读取输入。 返回值：\n成功返回0，失败返回-1。\nrm 使用方法：hadoop fs -rm URI [URI …]\n删除指定的文件。只删除非空目录和文件。请参考rmr命令了解递归删除。 示例：\nhadoop fs -rm hdfs://host:port/file /user/hadoop/emptydir 返回值：\n成功返回0，失败返回-1。\nrmr 使用方法：hadoop fs -rmr URI [URI …]\ndelete的递归版本。 示例：\nhadoop fs -rmr /user/hadoop/dir hadoop fs -rmr hdfs://host:port/user/hadoop/dir 返回值：\n成功返回0，失败返回-1。\nsetrep 使用方法：hadoop fs -setrep [-R] \u0026lt;path\u0026gt;\n改变一个文件的副本系数。-R选项用于递归改变目录下所有文件的副本系数。\n示例：\nhadoop fs -setrep -w 3 -R /user/hadoop/dir1 返回值：\n成功返回0，失败返回-1。\nstat 使用方法：hadoop fs -stat URI [URI …]\n返回指定路径的统计信息。\n示例：\nhadoop fs -stat path 返回值： 成功返回0，失败返回-1。\ntail 使用方法：hadoop fs -tail [-f] URI\n将文件尾部1K字节的内容输出到stdout。支持-f选项，行为和Unix中一致。\n示例：\nhadoop fs -tail pathname 返回值： 成功返回0，失败返回-1。\ntest 使用方法：hadoop fs -test -[ezd] URI\n选项： -e 检查文件是否存在。如果存在则返回0。 -z 检查文件是否是0字节。如果是则返回0。 -d 如果路径是个目录，则返回1，否则返回0。 示例：\nhadoop fs -test -e filename\ntext 使用方法：hadoop fs -text \u0026lt;src\u0026gt; 将源文件输出为文本格式。允许的格式是zip和TextRecordInputStream。\ntouchz 使用方法：hadoop fs -touchz URI [URI …] 创建一个0字节的空文件。\n示例：\nhadoop -touchz pathname 返回值： 成功返回0，失败返回-1。\n","date":"2021-10-19T23:43:58+08:00","image":"https://cn.bing.com/th?id=OHR.JouxFog_EN-CN2462005370_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/hadoop_install/","title":"基于CentOS 7安装 Hadoop"},{"content":"B/S分层 分层介绍 dto层 大概就是 MVC 中的 model 层,主要是用来写与 数据库相对应的实体类的,并且需要有 get/set 方法\ndao层 dao 层的文件习惯用 Mapper 来表示 dao 层会调用 dto层 ,并且在这一层主要就是 crud 操作\nservice层 service层会调用dao层和dto层,service层也会对数据进行一定的处理,比如条件判断和数据筛选等等；\ncontroller层 最后，是controller层，controller层会调用前面三层，controller层一般会和前台的js文件进行数据的交互， controller层是前台数据的接收器，后台处理好的数据也是通过controller层传递到前台显示的。\nSpringBoot中的分层 SpringBoot 分为四层：controller层、service层、dao层、entity层。\nentity层： 和 model 层一样，存放的是实体类，属性值与数据库值保持一致，实现 setter 和 getter 方法。\ndao层： 即 mapper层，对数据库进行持久化操作，他的方法使针对数据库操作的，基本上用的就是增删改查，他就是个接口，只有方法名，具体实现在mapper.xml中实现。\nservice层： 业务层，存放业务逻辑处理，不直接对数据库进行操作，有接口和接口实现类，提供 controller 层调用方法。\ncontroller层： 控制层，导入 service层，调用你service方法，controller通过接受前端传来的参数进行业务操作，在返回一个制定的路径或数据表。\n各层介绍 model层 model层即数据库实体层，也被称为entity层，pojo层。 一般数据库一张表对应一个实体类，类属性同表字段一一对应。 dao层 dao层即数据持久层，也被称为mapper层。 dao层的作用为访问数据库，向数据库发送sql语句，完成数据的增删改查任务。 service层 service层即业务逻辑层。 service层的作用为完成功能设计。 service层调用dao层接口，接收dao层返回的数据，完成项目的基本功能设计。 controller层 controller层即控制层。 controller层的功能为请求和响应控制。 controller层负责前后端交互，接受前端请求，调用service层，接收service层返回的数据，最后返回具体的页面和数据到客户端。 各模块简介 Java\nbean 实体类，对应数据库中的表(model)\n构造方法\n属性\ngetter setter\ntoString()\ndao 持久层 数据库增删改查\nservice 业务层\ncontroller 控制层 跳转哪里\nresources\nmapper dao实现类 applicationContext.xml Spring的配置文 db.properties 数据库连接信息 log4j.properties 日志配置文件 springmvc.xml 配置springmvc webapp\njsp等页面 web-inf web.xml 加载过滤器 拦截 各种命名介绍 PO (persistant object) 持久对象\n在 o/r 映射的时候出现的概念，如果没有 o/r 映射，没有这个概念存在了。通常对应数据模型 ( 数据库 ), 本身还有部分业务逻辑的处理。可以看成是与数据库中的表相映射的 java 对象。最简单的 PO 就是对应数据库中某个表中的一条记录，多个记录可以用 PO 的集合。 PO 中应该不包含任何对数据库的操作。\nDO （Domain Object）领域对象\n就是从现实世界中抽象出来的有形或无形的业务实体。\nTO (Transfer Object) ，数据传输对象\n在应用程序不同 tie( 关系 ) 之间传输的对象\nDTO （Data Transfer Object）数据传输对象\n这个概念来源于J2EE的设计模式，原来的目的是为了EJB的分布式应用提供粗粒度的数据实体，以减少分布式调用的次数，从而提高分布式调用的性能和降低网络负载，但在这里，我泛指用于展示层与服务层之间的数据传输对象。\nVO (value object) 值对象\n通常用于业务层之间的数据传递，和 PO 一样也是仅仅包含数据而已。但应是抽象出的业务对象 , 可以和表对应 , 也可以不 , 这根据业务的需要 。用 new 关键字创建，由 GC 回收的。\nBO (business object) 业务对象\n从业务模型的角度看 , 见 UML 元件领域模型中的领域对象。封装业务逻辑的 java 对象 , 通过调用 DAO 方法 , 结合 PO,VO 进行业务操作。 business object: 业务对象 主要作用是把业务逻辑封装为一个对象。这个对象可以包括一个或多个其它的对象。 比如一个简历，有教育经历、工作经历、社会关系等等。 我们可以把教育经历对应一个 PO ，工作经历对应一个 PO ，社会关系对应一个 PO 。 建立一个对应简历的 BO 对象处理简历，每个 BO 包含这些 PO 。 这样处理业务逻辑时，我们就可以针对 BO 去处理。\nPOJO (plain ordinary java object) 简单无规则 java 对象\n纯的传统意义的 java 对象。就是说在一些 Object/Relation Mapping 工具中，能够做到维护数据库表记录的 persisent object 完全是一个符合 Java Bean 规范的纯 Java 对象，没有增加别的属性和方法。我的理解就是最基本的 Java Bean ，只有属性字段及 setter 和 getter 方法！。\nDAO (data access object) 数据访问对象\n是一个 sun 的一个标准 j2ee 设计模式， 这个模式中有个接口就是 DAO ，它负持久层的操作。为业务层提供接口。此对象用于访问数据库。通常和 PO 结合使用， DAO 中包含了各种数据库的操作方法。通过它的方法 , 结合 PO 对数据库进行相关的操作。夹在业务逻辑与数据库资源中间。配合 VO, 提供数据库的 CRUD 操作.\nVO vo就是我们在web的controller层返回的Object， 在接口中这个VO都会被转成Json对象输出，view object。\nDO DO就是一个业务实体对象\nDTO DTO就是一个复合的DO对象，由于业务需要我们需要调用业务A查询数据得到业务对象A，再调用业务B查询数据得到业务对象B然后一系列封装转化得到复合的对象C此时他就是一个DTO，data transfer object 它是一个服务层和服务层以上之间转换的对象。\nPO po持久化对象 一般放在domain 或者 Entry中是一个与数据库表关联的对象，每一个属性都是表中的一个字段。 当业务过于简单时，po ，do，dto，vo并没有什么区别的时候我们也可以直接复用PO\nmodel = dto\ndao = mapper\ncontroller = 除了上面两个所有的\nModel是模型一般而言，会有人分的更细，VO，DTO 等等。我并不推荐分的更细，这个Model常常和持久化的数据一一对应，如Mysql和MongoDB。\nService的概念很大，它的重点是在于提供一个服务。这个服务可能包括一系列的数据处理，也有可能会调用多个Util，或者是调用别的服务。总归一句话，就是，有什么事情，你来找我。\nDao一般而言，都是用来和底层数据库通信，负责对数据库的增删改查。Dao最好都是要独立出来。到现在为止，最佳实践就是一个Service只对应一个Dao。Service会做一些额外的检查，如货物是否损坏，入库单是否完整，等等等等。\nSpringBoot 集成MyBatis 中的@MapperScan注解 在SpringBoot中集成MyBatis，可以在mapper接口上添加@Mapper注解，将mapper注入到Spring,但是如果每一给mapper都添加@mapper注解会很麻烦，这时可以使用@MapperScan注解来扫描包。\n经测试发现，@MapperScan注解只会扫描包中的接口，不会扫描类，所以可以在包中写Provider类。\n@MapperScan(\u0026ldquo;com.demo.mapper\u0026rdquo;)：扫描指定包中的接口\n@MapperScan(\u0026ldquo;com.demo..mapper\u0026rdquo;)：一个代表任意字符串，但只代表一级包,比如可以扫到com.demo.aaa.mapper,不能扫到com.demo.aaa.bbb.mapper\n@MapperScan(\u0026ldquo;com.demo.**.mapper\u0026rdquo;)：两个*代表任意个包,比如可以扫到com.demo.aaa.mapper,也可以扫到com.demo.aaa.bbb.mapper\n可以扫描到跨模块的包\n","date":"2021-09-10T20:35:28+08:00","image":"https://cn.bing.com/th?id=OHR.HallesWood_EN-CN6355536404_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/bs/","title":"B/S分层简介"},{"content":"最近办电信宽带，无意中发现电信给的是公网IP地址，这家伙可给我高兴坏了。在路由器上DDNS解析了一下IP地址到我的域名上，然后就开始捯饬了。\n搭建服务端（frps） 先说一下我的实验环境哈\nfrp v0.37.1 Ubuntu 20.04 这个frp是根据CPU选择版本的，鄙人就选择了amd64版本的压缩包。\n下载完了以后，选一个合适的地方解压文件。\n1 tar -zxvf frp_0.37.1_linux_amd64.tar.gz 然后直接编辑frp_0.37.1_linux_amd64/frps.ini\n1 2 3 4 5 6 7 8 9 [common] bind_port = 7000 bind_udp_port = 7001 vhost_http_port = 8089\t//电信封80 居然丧心病狂的连8080也一起封了。 vhost_https_port = 444\t//电信封443端口，https只能改端口了。 dashboard_port = 7500\tdashboard_user = xxx dashboard_pwd = ********** token = ****** 你也看到了配置很简单的，frps端没有啥需要操作的，然后就是 systemd 文件夹里面的 frps.service 文件，需要修改一下\n1 ExecStart=/usr/bin/frps -c /etc/frp/frps.ini 为你自己的实际文件地址，别的就没有啥问题了，如果你这样还是不行可以把配置文件 [Service] 里面的 User=nobody 改成 User=root\n1 2 3 4 cp systemd/frps.service /etc/systemd/system/ #复制service文件到systemd里面，这样我们可以直接systemctl调用frps sudo systemctl daemon-reload #重新加载service配置文件 sudo systemctl start frps #开启frps sudo systemctl enable frps\t#开机自启动frps 配置客户端（frpc） 这里也需要下载上面的那个压缩包，因为frp的作者是吧frps和frpc放一起打包的。\n下载解压缩的过程咱们就略过去啦。\n主要是这个： frpc.ini\n1 nano frpc.ini 1 2 3 4 5 6 7 8 9 10 11 12 13 [common] server_addr = your.ip.xxx.jokeme.top server_port = 7000 user = xxx token = ****** [web] type = https local_ip = 10.0.0.1 local_port = 443 custom_domains = xxx.jokeme.top use_encryption = true use_compression = true 这样就可以搭建一个公网可以访问的http服务了，当然必须要通过：IP:Port 的形式来访问了，毕竟80、8080、443都不能用得情况下，保证可以用就不错了，这用的舒服就算了吧。\n同样得，你需要修改 systemd 文件夹里面的 frpc.service 文件，然后把文件复制到systemd里面去 。。。\n然后你还可以利用frp搭建别的服务，RDP、SSH、Mysql等等，官网上有很多示例：frp_Readme.md\n作者还有一个中文文档，但是网站经常抽风打不开： https://gofrp.org\n声明： 本文没有参考任何互联网文章，文中所提到的软件：frp来自Github用户：fatedier\n请勿滥用此软件搭建非法服务，本人只对本人所写文章负责，任何由个人造成的问题本人概不负责，请悉知。\n","date":"2021-09-09T01:58:49+08:00","image":"https://cn.bing.com/th?id=OHR.ParnidisSundial_EN-CN6070614674_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/review_frp_set_up/","title":"回首frp搭建"},{"content":"最近在用 Infuse 重温仙剑三的的时候发现它的新增文件来源里面有一个 **『添加媒体服务器』**的选项,这是什么鬼？卧槽，世界上还有除了samba、ftp、nfs以外的文件共享服务吗？可能实在是我太孤陋寡闻了吧！一直只会用samba。\n这个Plex Emby Jellyfin又是个啥？然后百度一番。哦哦原来是本地看视频的！等等！啥软件不能看视频呀！还要专门搭建一个服务来看视频？？？这不是脱裤子放屁吗！\n好了回归正题！咱们想要了解一番，只能自己动手搭建一个试试看了，看了一下Plex和Emby都是收费的，或者免费版本有功能限制，我就动手搭建了Jellyfin，这个开源软件，功能多，不要钱😋！\n准备工作 准备工作当然是准备安装环境啦！Ubuntu 20.04 、Jellyfin deb安装包\n当然你也可以参考官网推荐的另一种方式 添加软件源，或者是直接用docker都是可以的。我个人是选择了下载安装包的，因为那个鬼软件源下载速度太慢了，简直慢的离谱。估计服务器在火星上面吧。\n对了，先放一下官网的下载页面链接，这里也有安装教程：https://jellyfin.org/downloads/\n好了不多说，咱们下载以下几个安装包\njellyfin-ffmpeg_4.3.2-1-focal_amd64 jellyfin 10.7.6-1_all jellyfin-server_10.7.6-1_amd64 jellyfin-web_10.7.6-1_all 下载时注意后三个版本号是否一样，第一个就选最新版准没错！买新不买旧嘛！然后把他们上传到Ubuntu服务器上面安装一下就可以了。\n1 dpkg -i jellyfin* jellyfin-* web页面配置 安装完成以后jellyfin就会自动监听8096端口，毕竟是Emby的开源版本，这点没有变的。首次登陆会让你设置一堆的东西，这些无关紧要，随便设置一下就可以了，然后咱们就可以来欣赏一下登录页面了，非常的好看！还是暗黑主题的。\n登录以后，如果你添加了媒体源，它会在后台刮削的，然后慢慢的给你的视频补齐海报、介绍、字幕啥的，当然国内的电视不一定有。\n下面就一起来看看最终的成果吧\n看上去还挺有内味的哈，效果是相当的不错哦！还可以选择集数啥啥啥的。\n然后功能丝毫不逊色于那些视频网站的播放器，可以选择播放质量，不过最高的质量取决于本地视频的最高质量，网络不好，播放卡顿可以向下降低视频质量来保证流畅的播放。\n总结 其实搭建这个jellyfin服务也挺简单的，就安装几个deb格式的安装包，打开网页配置一下就可以，不得不感叹这个软件是真的强大，视频封面啥的根本不用愁。以前下载好了视频用samba看的时候，每一集的封面都一样，因为是抽取指定时间的画面，刚巧，这些画面就在片头里面抽的。还有 Infuse 也老牛逼了，看视频贼流畅，加载视频很快！表扬一下它们两。\n","date":"2021-08-27T00:37:42+08:00","image":"https://cn.bing.com/th?id=OHR.MagneticIsland_EN-CN4779142621_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/install_jellyfin/","title":"Jellyfin影音平台搭建"},{"content":"以前写博客呢基本上都没有配过图片，因为没有找到适合的图床和顺手的工具，免费的图床要不然就是广告，要不然就是不能使用。但是现在不一样了，我不傻了，我变机灵了！我学会白嫖七牛云的Kodo了。\n好的首先进入正题，咱们今天的主角是 PicGo-Core 你也可以用app，但是我不太喜欢。首先选择 PicGo-Core 下载二进制包，如果你之前用过的话，直接点击下面的 打开配置文件\n这个时候你的配置文件里面应该啥都没有，就需要咱们用命令来自动生成一下配置文件了\n1 picgo use uploader 输入完命令以后因该是下面的样子，然后选择对应的选项，填入正确的数据即可。\n然后如果你不懂的话看一看大佬的文档就可以明白了 PicGo-Core\n然后AK和SK就是七牛云那边的密钥，bucket就是你的Kode名字，area就选择对应的地方即可，完整配置如下。整个过程下来没有啥难得地方，就是激活七牛云的账号比较麻烦。\n然后咱就可以试试看Typora在复制图片的时候会不会自动上传图片并把图片的本地链接替换成Kode外链。不出意外的话咱们就可以直接起飞了。祝好运吧。\n","date":"2021-08-19T00:08:17Z","image":"https://cn.bing.com/th?id=OHR.MtEtna_EN-CN8869032750_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/picgo_with_typora/","title":"在Typora里使用PicGo-Core自动上传图片"},{"content":"Aria2下载器配置WebUI界面 aria2是一款非常非常Nice的下载器，之前我也介绍过 记录最近捯饬Linux的收获，上一次大概介绍了一下命令行里面的操作，这一次咱们来说说WebUI上的操作。\n准备环境 首先去Github下载一下AriaNG 当然你可以选择别的WebUI其实大概步骤都差不多。\n1 wget https://github.com/mayswind/AriaNg/releases/download/1.2.2/AriaNg-1.2.2.zip 下载完成以后咱们去、/var/www/html里面创建一个文件夹\n1 sudo mkdir /var/www/html/ariaweb 接着咱们把下载下来的文件移动到创建的文件夹里面顺便解压一下\n1 2 3 mv AriaNg-1.2.2.zip /var/www/html/ariaweb cd /var/www/html/ariaweb unzip AriaNg-1.2.2.zip 不知道你们哪里有没有安装 Nginx，如果没有安装的话，安装一下，会用到nginx，如果你愿意也可以用apache2，tomcat等，这里我就以nginx演示了。\n1 sudo apt-get install nginx 接着是需要安装一下我们今天的主角：aria2\n1 2 sudo apt-get install aria2 #aria2 is already the newest version (1.35.0-1build1). 目前 Ubuntu20.04最新的是1.35.0，GitHub最新版本也是1.35.0\n修改配置文件 aria2.conf 首先就是aria2的配置文件，默认是读取：~/.aria2/aria2.conf，当然我们需要手动创建一下文件的\n1 2 3 mkdir ~/.aria2 touch ~/.aria2/aria2.conf touch ~/.aria2/aria2.session 然后我们就需要把自己的配置文件加上去了\n1 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 #http download continue=true max-connection-per-server=5 max-tries=10 retry-wait=600 #bt download bt-max-open-files=1000 bt-request-peer-speed-limit=40K bt-tracker=[udp://93.158.213.92:1337/announce,http://93.158.213.92:1337/announce,udp://207.241.226.111:6969/announce] enable-dht=true enable-peer-exchange=true dht-file-path=/home/user/.cache/aria2/dht.dat max-upload-limit=300K #RPC setting enable-rpc=true rpc-allow-origin-all=true rpc-listen-all=true rpc-secret=llll #Other setting disable-ipv6=true file-allocation=falloc save-session=/home/user/.aria2/aria2.session save-session-interval=90 dir=udisk/tor input-file=/home/user/.aria2/aria2.session 这些都是简单的配置，从字面意思就可以看出设置了啥，根据自己的具体情况修改即可，就像我这里，IPv6异常的烂，我直接禁用了v6。\nnginx.conf nginx咱们只需要添加一个站点就可以了\n1 sudo nano /etc/nginx/sites-available/xxx 内容如下:\n1 2 3 4 5 6 7 8 server { # AriaNG listen 80; charset utf-8; root /var/www/html/ariaweb;#填上面创建的文件夹地址 index index.html index.htm index.nginx-debian.html; server_name 10.0.0.2;#填你的IP地址 } 还有一点,你需要确保你的sites-available/xxx是启用的，检查一下include /etc/nginx/sites-enabled/*;有没有启用还有就是这个sites-available/xxx有没有被软连接到sites-enabled/下面，注意这些细节不要出现问题。\n运行AriaNG 其实也非常简单，只需要打开aria2和nginx就可以了\n1 2 sudo systemctl start nginx aria2 接着上面，我们去浏览器输入10.0.0.2进一步配置AriaNG\nAriaNg 设置 -\u0026gt; RPC (10.0.0.2:6800) -\u0026gt; Aria2 RPC 密钥\n在这里输入aria2.conf里面配置的密码即可，AriaNG会提示你重新加载。此时只要你Terminal里面的aria2没有停止运行，你就可以在WebUI里面下载了，而不是晦涩难懂的命令界面。\n","date":"2021-08-09T18:33:21Z","image":"https://cn.bing.com/th?id=OHR.Inisheer_EN-CN8412531756_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/ariang_webui/","title":"Aria2下载器配置WebUI界面"},{"content":"aria2 配置 众所周知哈 aria2 是一款非常强大的下载工具呐，但是我之前一直都只会他的最最最简单的用法一直不会那种进阶玩法，所以我就去官网找了找 aria2 的参数具体怎么用，还有 RPC 用法\n基本参数 1 -d,--dir = 存储下载文件的目录。 1 -i,--input-file = 下载中列出的 URI FILE。您可以通过将多个 URI 放在由TAB字符分隔的一行中来为单个实体指定多个源 。 1 -l,--log = 日志文件的文件名。如果`-`指定，则将日志写入 `stdout`. 如果指定了空字符串(\u0026#34;\u0026#34;)，或者省略了此选项，则根本不会将日志写入磁盘。 1 -j,--max-concurrent-downloads = 为每个队列项设置最大并行下载数。默认：`5` 1 -V,--check-integrity [true|false] 通过验证片段散列或整个文件的散列来检查文件完整性。此选项仅在 BitTorrent、带校验和的 Metalink 下载或带`--checksum`选项的HTTP(S)/FTP 下载中有效 。默认： `false` 1 -c,--continue [true|false] 继续下载部分下载的文件。使用此选项可恢复由 Web 浏览器或其他从头开始按顺序下载文件的程序启动的下载。目前此选项仅适用于 HTTP(S)/FTP 下载。 1 -h,--help[=|] 帮助信息按标签分类。标签以 `#`. 例如，键入`--help=#http`以获取标记为 的选项的用法`#http`。如果给出了非标记词，则打印名称包含该词的选项的用法。可用值：`#basic`, `#advanced`, `#http`, `#https`, `#ftp`, `#metalink`, `#bittorrent`, `#cookie`, `#hook`, `#file`, `#rpc`, `#checksum`, `#experimental`, `#deprecated`, `#help`,`#all` 默认值：`#basic` HTTP/FTP/SFTP 选项 1 --all-proxy = 为所有协议使用代理服务器。要覆盖先前定义的代理，请使用“”。您还可以覆盖此设置并使用`--http-proxy`、`--https-proxy`和`--ftp-proxy`选项为特定协议指定代理服务器。 1 --all-proxy-passwd = 为`--all-proxy`选项设置密码。 1 --all-proxy-user = 为`--all-proxy`选项设置用户。 1 --checksum == 设置校验和。TYPE 是哈希类型。 1 --connect-timeout = 以秒为单位设置连接超时以建立到 HTTP/FTP/代理服务器的连接。建立连接后，此选项无效，而是使用选项`--timeout`。默认：`60` 1 2 --dry-run [true|false] 如果`true`给出，aria2 只检查远程文件是否可用，不下载数据。此选项对 HTTP/FTP 下载有影响。如果`true`指定，则取消 BitTorrent 下载。默认：`false` 1 2 --lowest-speed-limit``=` 如果下载速度低于或等于此值（每秒字节数），则关闭连接。 `0`意味着 aria2 没有最低速度限制。您可以附加`K`或`M`(1K = 1024, 1M = 1024K)。此选项不影响 BitTorrent 下载。默认：`0` 1 -x,--max-connection-per-server = 每次下载到一台服务器的最大连接数。默认：`1` 1 --max-file-not-found = 如果 aria2 NUM 次从远程 HTTP/FTP 服务器收到“文件未找到”状态而没有获得一个字节，则强制下载失败。指定`0`禁用此选项。此选项仅在使用 HTTP/FTP 服务器时有效。重试次数计入`--max-tries`，因此也应进行配置。默认： `0` 1 -m,--max-tries = 设置尝试次数。`0`意味着无限。可以设置重试等待时间`--retry-wait`，默认：`5` 1 -k,--min-split-size = aria2 不会拆分小于 2*SIZE 的字节范围。例如，让我们考虑下载 20MiB 文件。如果 SIZE 为 10M，aria2 可以将文件分成 2 个范围 [0-10MiB) 和 [10MiB-20MiB) 并使用 2 个源下载它`--split`当然，如果\u0026gt;= 2）。如果 SIZE 为 15M，由于 2*15M \u0026gt; 20MiB，aria2 不会拆分文件并使用 1 个源下载。您可以附加`K`或`M`,可能的值：`1M`-`1024M`默认值：`20M` 1 2 --netrc-path = 指定 netrc 文件的路径。默认：`$(HOME)/.netrc` note : .netrc 文件的权限必须为 600。否则，该文件将被忽略。 1 -n,--no-netrc [true|false] 禁用 netrc 支持。默认情况下启用 netrc 支持。 1 --no-proxy = 指定一个逗号分隔的主机名、域和网络地址列表，其中应使用或不使用代理的子网掩码。 1 2 -o,--out = 下载文件的文件名。它总是相对于`--dir`选项中给出的目录。 note:您不能为 Metalink 或 BitTorrent 下载指定文件名。此处指定的文件名仅在直接在命令行上给出提供给 aria2 的 URI 时使用`--input-file`有效。 1 --proxy-method = 设置在代理请求中使用的方法。方法是`get`或 `tunnel`。`tunnel`无论此选项如何，始终使用 HTTPS 下载。默认：`get` 1 -R,--remote-time [true|false] 从远程 HTTP/FTP 服务器检索远程文件的时间戳，如果可用，将其应用于本地文件。默认：`false` 1 --reuse-uri [true|false] 如果没有未使用的 URI，则重用已使用的 URI。默认：`true` 1 --retry-wait = 设置重试之间等待的秒数。 1 --server-stat-of = 指定保存服务器性能配置文件的文件名。您可以使用`--server-stat-if`选项加载保存的数据 1 --server-stat-if = 指定文件名以加载服务器的性能配置文件。 1 --server-stat-timeout = 以秒为单位指定超时，以使服务器自上次联系以来的性能配置文件无效。默认值：`86400`（24 小时） 1 2 -s,--split = 使用 N 个连接下载文件。如果给出的 URI 多于 N 个，则使用前 N 个 URI，其余 URI 用于备份。如果给定的 URI 少于 N 个，则这些 URI 将被多次使用，以便同时建立总共 N 个连接。到同一主机的连接数受`--max-connection-per-server`选项限制 。另可以设置最小的分片数`--min-split-size`。默认：`5`。 note:一些 Metalinks 规定要连接的服务器数量。aria2 严格尊重他们。这意味着如果 Metalink 定义的 `maxconnections`属性低于 N，则 aria2 使用这个较低值的值而不是 N。 1 2 3 4 5 6 7 8 9 --stream-piece-selector = 指定用于 HTTP/FTP 下载的片段选择算法。Piece 是指在分段下载中并行下载的固定长度段。 如果`default`给定，则 aria2 选择块以减少建立连接的次数。这是合理的默认行为，因为建立连接是一项昂贵的操作。 如果`inorder`给出，aria2 选择具有最小索引的片段。Index=0 表示文件的第一个。这对于在下载电影时查看电影很有用。`--enable-http-pipelining`选项可能有助于减少重新连接开销。请注意 aria2 尊重 `--min-split-size`选项，因此有必要为`--min-split-size`选项指定一个合理的值 。 如果`random`给定，aria2 会随机选择一块。 如果`geom`给出，在开始 aria2 选择具有最小索引的片段`inorder`，但它以指数方式增加与先前选择的片段的空间。这将减少建立连接的次数，同时它会先下载文件的开头部分。这对于在下载电影时查看电影很有用。默认：`default` 1 -t,--timeout = 以秒为单位设置超时。默认：`60` 1 2 3 4 5 6 7 8 9 --uri-selector = 指定 URI 选择算法。可能的值为`inorder`、 `feedback`和`adaptive`。 如果`inorder`给出，则按照 URI 列表中出现的顺序尝试 URI。 如果`feedback`给出，aria2 使用在之前下载中观察到的下载速度并在 URI 列表中选择最快的服务器。这也有效地跳过了死镜。观察到的下载速度是`--server-stat-of`和 `--server-stat-if`选项中提到的服务器性能配置文件的一部分。 如果`adaptive`给定，则为第一个和保留的连接选择一个最佳镜像。对于补充的，它返回尚未测试的镜像，如果每个镜像都已经测试过，则返回必须再次测试的镜像。否则，它不再选择镜像。像`feedback`，它使用服务器的性能配置文件。 默认：`feedback` HTTP 特定选项 1 2 3 --ca-certificate = 使用 FILE 中的证书颁发机构来验证对等方。证书文件必须是 PEM 格式，并且可以包含多个 CA 证书。使用`--check-certificate`选项启用验证。 note:如果您使用 OpenSSL 或具有`gnutls_certificate_set_x509_system_trust()`功能的最新版本的 GnuTLS 构建，并且正确配置了库以定位系统范围的 CA 证书存储，aria2 将在启动时自动加载这些证书。 note:*WinTLS*和*AppleTLS*不支持此选项。相反，您必须将证书导入操作系统信任库。 1 2 3 --certificate = 使用 FILE 中的客户端证书。证书必须是 PKCS12（.p12、.pfx）或 PEM 格式。PKCS12 文件必须包含证书、密钥和可选的附加证书链。只能打开导入密码为空的 PKCS12 文件！使用 PEM 时，您还必须指定私钥 via `--private-key`。 note:*WinTLS*目前不支持 PEM 文件。用户必须使用 PKCS12 文件。 noye:*AppleTLS*用户应使用 KeyChain Access 实用程序导入客户端证书并从与该证书对应的信息对话框中获取 SHA-1 指纹。要启动 aria2c，请使用--certificate=\u0026lt;SHA-1\u0026gt;。或者，也支持 PKCS12 文件。但是，不支持 PEM 文件。 1 --check-certificate [true|false] 使用`--ca-certificate`选项中指定的证书验证对等方。默认：`true` 1 2 --http-accept-gzip [true|false] 发送请求标头`Accept: deflate, gzip`,如果远程服务器以`Content-Encoding: gzip`，`Content-Encoding: defaultlate`响应，则会升级响应。默认:false` note:某些服务器本身是以 gzipped 文件的文件进行响应。由于响应标头，aria2 无论如何都会把它们升级到。`Content-Encoding: gzip` 1 --http-auth-challenge [true|false] 仅在服务器请求时发送 HTTP 授权标头。如果`false`设置，则授权标头总是发送到服务器。有一个例外：如果用户名和密码嵌入在 URI 中，则无论此选项如何，授权标头始终发送到服务器。默认：`false` 1 --http-no-cache`` [true|false]` 发送和标头以避免缓存内容。 1 --http-user = 设置 HTTP 用户。这会影响所有 URI。 1 --http-passwd = 设置 HTTP 密码。这会影响所有 URI。 1 --http-proxy=为 HTTP 使用代理服务器。这会影响所有 http 下载。PROXY 的格式是`[http://][USER:PASSWORD@]HOST[:PORT]` 1 --http-proxy-passwd = 为代理设置密码 1 --http-proxy-user = 为代理设置用户 1 --https-proxy = 为 HTTPS 使用代理服务器。这会影响所有 https 下载。PROXY 的格式是`[http://][USER:PASSWORD@]HOST[:PORT]` 1 --https-proxy-passwd = 同上http用法 1 --https-proxy-user = 同上http用法 1 --private-key = 使用 FILE 中的私钥。私钥必须解密并采用 PEM 格式。给出加密时的行为是未定义的。 1 --referer = 设置一个 http 引荐来源网址（Referer）。这会影响所有 http/https 下载。如果`*`给出，下载 URI 也用作引用。 1 --enable-http-keep-alive [true|false] 启用 HTTP/1.1 持久连接。默认：`true` 1 2 --enable-http-pipelining [true|false] 启用 HTTP/1.1 流水线。默认：`false` note:从性能角度来看，启用此选项通常没有优势。 1 2 --header = 将 HEADER 附加到 HTTP 请求标头。您可以重复使用此选项来指定多个标题： `aria2c --header=\u0026#34;X-A: b78\u0026#34; --header=\u0026#34;X-B: 9J1\u0026#34; \u0026#34;http://host/file\u0026#34; ` 1 2 --load-cookies = 使用 Firefox3 格式 (SQLite3)、Chromium/Google Chrome (SQLite3) 和 Mozilla/Firefox(1.x/2.x)/Netscape 格式从 FILE 加载 Cookie。 note:如果 aria2 是在没有 libsqlite3 的情况下构建的，那么它不支持 Firefox3 和 Chromium/Google Chrome cookie 格式。 1 --save-cookies = 以 Mozilla/Firefox(1.x/2.x)/Netscape 格式将 Cookie 保存到 FILE。如果 FILE 已存在，则将其覆盖。会话 Cookie 也会被保存，它们的到期值被视为 0。可能的值：`/path/to/file` 1 --use-head [true|false] 对 HTTP 服务器的第一个请求使用 HEAD 方法。默认：`false` 1 -U,--user-agent = 为 HTTP(S) 下载设置用户代理。默认值：`aria2/$VERSION`, $VERSION 由包版本替换。 FTP/SFTP 特定选项 1 --ftp-user = 设置FTP用户。这会影响所有 URI。默认：`anonymous` 1 --ftp-passwd = 设置FTP密码。这会影响所有 URI。如果输入了用户名但 URI 中缺少密码，aria2 会尝试使用 .netrc 解析密码。如果在 .netrc 中找到密码，则将其用作密码。如果没有，请使用此选项中指定的密码。默认：`ARIA2USER@` 1 2 -p,--ftp-pasv [true|false]`在 FTP 中使用被动模式。如果`false`给出，将使用活动模式。默认：`true` note:SFTP 传输忽略此选项。 1 --ftp-proxy = 使用代理服务器进行 FTP。这会影响所有 ftp 下载。PROXY 的格式是`[http://][USER:PASSWORD@]HOST[:PORT]` 1 --ftp-proxy-passwd = 为`--ftp-proxy`选项设置密码。 1 --ftp-proxy-user = 为`--ftp-proxy`选项设置用户。 1 --ftp-type = 设置 FTP 传输类型。TYPE 是`binary`或`ascii`。默认：`binary`笔记SFTP 传输忽略此选项。 1 --ftp-reuse-connection [true|false] 在 FTP 中重用连接。默认：`true` 1 --ssh-host-key-md == 设置 SSH 主机公钥的校验和。TYPE 是哈希类型。支持的哈希类型是`sha-1`or `md5`。 BitTorrent/Metalink 选项 1 2 --select-file = 通过指定其索引来设置要下载的文件。您可以使用该`--show-files`选项找到文件索引。可以使用 指定多个索引`,`，例如：`3,6`。您还可以使用`-`来指定一个范围：`1-5`。 `,`并且`-`可以一起使用：`1-5,8,9`. note:在多文件 torrent 中，也可以下载此选项指定的相邻文件。这是设计使然，而不是错误。单个片段可能包含多个文件或部分文件，aria2 将片段写入相应的文件。 1 -S,--show-files [true|false] 打印“.torrent”、“.meta4”和“.metalink”文件的文件列表并退出。如果是“.torrent”文件，还会打印附加信息（信息哈希、片段长度等）。 BitTorrent 特定选项 1 --bt-detach-seed-only [true|false] 在计算并发活动下载时排除仅种子下载（请参阅`-j`选项）。这意味着如果`-j3`给出并打开此选项并且 3 个下载处于活动状态，其中一个进入种子模式，则它被排除在活动下载计数之外（因此变为 2），并且开始等待队列中的下一个下载。但请注意，在 RPC 方法中，播种项目仍被识别为活动下载。默认：`false` 1 --bt-enable-hook-after-hash-check [true|false] 允许`-V`在 BitTorrent 下载中的哈希检查（见选项）后调用钩子命令。默认情况下，当哈希检查成功时，`--on-bt-download-complete`执行由给出的命令。要禁用此操作，请`false`选择此选项。默认：`true` 1 --bt-enable-lpd [true|false] 启用本地对等发现。如果在 torrent 中设置了私有标志，则 aria2 不会将该功能用于该下载，即使`true`已提供。默认：`false` 1 --bt-exclude-tracker = [,...]` 排除这些逗号分隔的 BitTorrent tracker服务器 URI 列表。 1 --bt-external-ip = 指定要在 BitTorrent 下载和 DHT 中使用的外部 IP 地址。它可能会被发送到 BitTorrent 跟踪器。对于 DHT，此选项应设置为报告本地节点正在下载特定的 torrent。这对于在专用网络中使用 DHT 至关重要。尽管此函数名为`external`，但它可以接受任何类型的 IP 地址。 1 --bt-force-encryption [true|false] 需要使用 arc4 对 BitTorrent 消息负载加密。这是=arc4的简写。此选项不会更改这些选项的选项值。如果给出，则拒绝旧版 BitTorrent 握手并仅使用混淆握手并始终加密消息有效负载。默认：`false` 1 --bt-hash-check-seed [true|false] 如果`true`给出，在使用`--check-integrity`选项和文件完成哈希检查后，继续种子文件。如果您只想在文件损坏或不完整时检查并下载它，请将此选项设置为`false`。此选项仅对 BitTorrent 下载有效。默认：`true` 1 2 --bt-load-saved-metadata [true|false] 在使用磁力链接下载时从 DHT 获取 torrent 元数据之前，首先尝试读取`--bt-save-metadata`选项保存的文件 。如果成功，则跳过从 DHT 下载元数据。默认：`false` note:如果你经常下载相同的BT文件，你可以启用 1 --bt-lpd-interface = 使用给定的接口进行本地对等发现。如果未指定此选项，则选择默认接口。您可以指定接口名称和 IP 地址。可能的值：接口、IP 地址 1 --bt-max-open-files = 指定在多文件 BitTorrent/Metalink 下载中全局打开的最大文件数。默认：`100` 1 --bt-max-peers = 指定每个 torrent 的最大对等点数。 `0`意味着无限。默认：`55` 1 --bt-metadata-only [true|false] 仅下载元数据。元数据中描述的文件将不会被下载。此选项仅在使用 BitTorrent Magnet URI 时有效。默认：`false` 1 --bt-min-crypto-level = plain|arc4 设置加密方法的最低级别。如果 peer 提供了多种加密方法，aria2 会选择满足给定级别的最低加密方法。默认：`plain` 1 --bt-prioritize-piece =head[=],tail[=]尝试首先下载每个文件的第一个和最后一个部分。这对于预览文件很有用。参数可以包含 2 个关键字： `head`和`tail`。要包括这两个关键字，它们必须用逗号分隔。这些关键字可以采用一个参数 SIZE。例如，如果`head=`指定，则每个文件的第一个 SIZE 字节范围内的片段获得更高的优先级。 `tail=`表示每个文件的最后 SIZE 字节的范围。SIZE 可以包括`K`或`M`(1K = 1024, 1M = 1024K)。如果省略 SIZE，则使用 SIZE=1M。 1 2 --bt-remove-unselected-file [true|false] 在 BitTorrent 中完成下载后删除未选择的文件。要选择文件，请使用 `--select-file`选项。如果不使用，则所有文件都被选中。请小心使用此选项，因为它实际上会从您的磁盘中删除文件。默认：`false` 1 --bt-require-crypto [true|false] 如果`true`给出，aria2 不接受并与旧版 BitTorrent 握手（\\19BitTorrent 协议）建立连接。因此 aria2 总是使用混淆握手。默认：`false` 1 --bt-request-peer-speed-limit = 如果每个 torrent 的整体下载速度低于 SPEED，aria2 会临时增加 peer 的数量以尝试更快的下载速度。在某些情况下，使用您首选的下载速度配置此选项可以提高您的下载速度。您可以附加`K`或`M`(1K = 1024, 1M = 1024K)。默认：`50K` 1 --bt-save-metadata [true|false] 如果true给出，将元数据另存为“xxx.torrent”文件。此选项仅在使用 BitTorrent Magnet URI 时有效。文件名是带有后缀“.torrent”的十六进制编码信息哈希。保存目录与保存下载文件的目录相同。如果相同的文件已经存在，则不保存元数据。默认：`false` 1 --bt-seed-unverified`` [true|false]`在不验证片段哈希的情况下为先前下载的文件播种。默认：`false` 1 --bt-stop-timeout = 如果下载速度在连续 SEC 秒内为 0，则停止 BitTorrent 下载。如果`0`给出，则禁用此功能。默认：`0` 1 --bt-tracker =[,...]`附加 BitTorrent 跟踪器的公告 URI 的逗号分隔列表。这些 URI 不受`--bt-exclude-tracker`选项的影响。 1 --bt-tracker-connect-timeout = 以秒为单位设置连接超时以建立与跟踪器的连接。建立连接后，此选项无效，而是使用选项`--bt-tracker-timeout`。默认： `60` 1 --bt-tracker-interval = 设置跟踪器请求之间的间隔（以秒为单位）。这完全覆盖了间隔值，而 aria2 仅使用此值并忽略跟踪器响应中的最小间隔和间隔值。如果`0`设置，aria2 将根据 tracker 的响应和下载进度确定间隔。默认：`0` 1 --bt-tracker-timeout = 以秒为单位设置超时。默认：`60` 1 --dht-entry-point =:将主机和端口设置为 IPv4 DHT 网络的入口点。 1 --dht-entry-point6 =:将主机和端口设置为 IPv6 DHT 网络的入口点。 1 --dht-file-path = 将 IPv4 DHT 路由表文件更改为 PATH。默认值：`$HOME/.aria2/dht.dat`如果存在，否则为 `$XDG_CACHE_HOME/aria2/dht.dat`。 1 --dht-file-path6 = 同上。默认值：`$HOME/.aria2/dht6.dat` 1 --dht-listen-addr6 = 指定地址以绑定 IPv6 DHT 的套接字。它应该是主机的全球单播 IPv6 地址。 1 2 --dht-listen-port = ...设置 DHT(IPv4, IPv6) 和 UDP 跟踪器使用的 UDP 侦听端口。可以使用 指定多个端口`,`，例如： `6881,6885`。您还可以使用`-`来指定一个范围： `6881-6999`。`,`并且`-`可以一起使用。默认：`6881-6999` note:确保为传入的 UDP 流量打开指定的端口。 1 --dht-message-timeout = 以秒为单位设置超时。默认：`10` 1 --enable-dht [true|false]启用 IPv4 DHT 功能。它还启用 UDP 跟踪器支持。如果在 torrent 中设置了私有标志，则 aria2 不会为该下载使用 DHT，即使`true`已给出。默认：`true` 1 --enable-dht6 [true|false] 启用 IPv6 DHT 功能。如果在 torrent 中设置了私有标志，则 aria2 不会为该下载使用 DHT，即使`true`已给出。使用`--dht-listen-port`选项指定要侦听的端口号。 1 --enable-peer-exchange [true|false] 启用对等交换扩展。如果在 torrent 中设置了私有标志，即使提供了该功能，该功能也会被禁用`true`。默认：`true` 1 --follow-torrent =true|false|mem 如果指定`true`或`mem`，当下载后缀为`.torrent`或内容类型`application/x-bittorrent`的文件时，aria2 将其解析为torrent 文件并下载其中提到的文件。如果`mem`指定，torrent 文件不会写入磁盘，而只会保存在内存中。如果`false`指定，`.torrent`文件将下载到磁盘，但不会被解析为 torrent 并且不会下载其内容。默认：`true` 1 -O,--index-out == 为 index=INDEX 的文件设置文件路径。您可以使用该`--show-files`选项找到文件索引。PATH 是`--dir`选项中指定路径的相对路径。您可以多次使用此选项。使用此选项，您可以指定 BitTorrent 下载的输出文件名。 1 --listen-port = ... 为 BitTorrent 下载设置 TCP 端口号。可以使用 指定多个端口`,`，例如：`6881,6885`。您还可以使用`-`来指定一个范围：`6881-6999`。 `,`并且`-`可以一起使用：`6881-6889,6999`. 默认：`6881-6999`笔记确保为传入 TCP 流量打开指定的端口。 1 --max-overall-upload-limit = 以字节/秒为单位设置最大整体上传速度。 `0`意味着不受限制。您可以附加`K`或`M`(1K = 1024, 1M = 1024K)。要限制每个 torrent 的上传速度，请使用`--max-upload-limit`选项。默认：`0` 1 -u, --max-upload-limit= 以字节/秒为单位设置每个种子的最大上传速度。 `0`意味着不受限制。您可以附加`K`或`M`(1K = 1024, 1M = 1024K)。要限制整体上传速度，请使用`--max-overall-upload-limit`选项。默认：`0` 1 --peer-id-prefix = 指定peer ID的前缀。BitTorrent 中的对等 ID 为 20 字节长度。如果指定了 20 个以上的字节，则仅使用前 20 个字节。如果指定少于 20 个字节，则添加随机字节数据使其长度为 20 个字节。默认：`A2-$MAJOR-$MINOR-$PATCH-`, $MAJOR, $MINOR 和 $PATCH 分别替换为主要、次要和补丁版本号。例如，aria2 版本 1.18.8 具有前缀 ID `A2-1-18-8-`。 1 --peer-agent = 指定在对等客户端版本的 bitorrent 扩展握手期间使用的字符串。默认：`aria2/$MAJOR.$MINOR.$PATCH`, $MAJOR, $MINOR 和 $PATCH 分别替换为主要、次要和补丁版本号。例如，aria2 版本 1.18.8 有 peer agent `aria2/1.18.8`。 1 --seed-ratio = 指定份额比率。种子完成种子直到共享比率达到 RATIO。强烈建议您在`1.0`此处指定等于或大于。指定`0.0`您是否打算进行播种而不管份额比率如何。如果`--seed-time`选项与此选项一起指定，则在满足至少一个条件时结束播种。默认：`1.0` 1 2 --seed-time = 以（小数）分钟为单位指定播种时间。另请参阅 `--seed-ratio`选项。 note:指定`--seed-time=0`在下载完成后禁用种子。 1 -T,--torrent-file = “.torrent”文件的路径。您不需要使用此选项，因为您可以指定不带 .torrent 的“.torrent”文件`--torrent-file`。 Metalink 特定选项 1 --follow-metalink``=true|false|mem` 如果指定了`true`或`mem`，当下载后缀为`.meta4`或者`.metalink`或内容类型为`application/metalink4+xml`或者`application/metalink+xml`的文件时， aria2 将其解析为 metalink 文件并下载其中提到的文件。如果`mem`指定，Metalink 文件不会写入磁盘，而只是保存在内存中。如果`false`指定，则`.metalink`文件会下载到磁盘，但不会被解析为 metalink 文件，也不会下载其内容。默认：`true` 1 --metalink-base-uri = 指定基本 URI 以解析存储在本地磁盘中的 metalink 文件中的 metalink:url 和 metalink:metaurl 元素中的相对 URI。如果 URI 指向目录，则 URI 必须以`/`. 1 -M,--metalink-file= “.meta4”和“.metalink”文件的文件路径。从读取输入`stdin`时`-`指定的。您不需要使用此选项，因为您可以指定不带 .metalink 的“.metalink”文件[`--metalink-file`](http://aria2.github.io/manual/en/html/aria2c.html#cmdoption-metalink-file)。 1 --metalink-language = 要下载的文件的语言。 1 --metalink-location = [,...]首选服务器的位置。逗号分隔的位置列表是可以接受的，例如，`jp,us`。 1 --metalink-os = 要下载的文件的操作系统。 1 --metalink-version = 要下载的文件的版本。 1 --metalink-preferred-protocol = 指定首选协议。可能的值是`http`，`https`，`ftp`和`none`。指定`none`禁用此功能。默认：`none` 1 --metalink-enable-unique-protocol [true|false] 如果`true`给出并且有几个协议可用于 metalink 文件中的镜像，则 aria2 使用其中之一。默认：`true` RPC 选项 1 --enable-rpc [true|false] 启用 JSON-RPC/XML-RPC 服务器。强烈建议使用`--rpc-secret`选项设置秘密授权令牌。默认：`false` 1 --pause [true|false] 添加后暂停下载。此选项仅在`--enable-rpc=true`给出时有效 。默认：`false` 1 --pause-metadata [true|false] 暂停因元数据下载而创建的下载。aria2 中有 3 种类型的元数据下载： (1) 下载 .torrent 文件。(2) 使用磁力链接下载 torrent 元数据。(3)下载metalink文件。这些元数据下载将使用其元数据生成下载。此选项会暂停这些后续下载。此选项仅在`--enable-rpc=true`给出时有效 。默认：`false` 1 --rpc-allow-origin-all [true|false] 将带有值的 Access-Control-Allow-Origin 标头字段添加`*`到 RPC 响应。默认：`false` 1 2 --rpc-certificate = 将 FILE 中的证书用于 RPC 服务器。证书必须是 PKCS12（.p12、.pfx）或 PEM 格式。PKCS12 文件必须包含证书、密钥和可选的附加证书链。只能打开导入密码为空的 PKCS12 文件！使用 PEM 时，您还必须指定私钥 via `--rpc-private-key`。使用`--rpc-secure`选项启用加密。 note:*WinTLS*目前不支持 PEM 文件。用户必须使用 PKCS12 文件。笔记*AppleTLS*用户应使用 KeyChain Access 实用程序首先生成自签名 SSL-Server 证书，例如使用向导，然后从与该新证书对应的信息对话框中获取 SHA-1 指纹。`--rpc-secure`使用 --rpc-certificate=\u0026lt;SHA-1\u0026gt;启动 aria2c 。或者，也支持 PKCS12 文件。但是，不支持 PEM 文件。 1 --rpc-listen-all [true|false] 在所有网络接口上侦听传入的 JSON-RPC/XML-RPC 请求。如果给出 false，则仅在本地环回接口上侦听。默认：`false` 1 --rpc-listen-port = 定要侦听的 JSON-RPC/XML-RPC 服务器的端口号。可能的值：`1024`-`65535`默认值：`6800` 1 --rpc-max-request-size = 设置 JSON-RPC/XML-RPC 请求的最大大小。如果 aria2 检测到请求超过 SIZE 字节，则会断开连接。默认：`2M` 1 --rpc-passwd = 设置 JSON-RPC/XML-RPC 密码。警告`--rpc-passwd`选项将在未来版本中弃用。`--rpc-secret`尽快迁移到选项。 1 --rpc-private-key = 将 FILE 中的私钥用于 RPC 服务器。私钥必须解密并采用 PEM 格式。使用`--rpc-secure`选项启用加密。另见`--rpc-certificate`选项。 1 --rpc-save-upload-metadata [true|false] 将上传的 torrent 或 metalink 元数据保存在`--dir`option指定的目录中。文件名由元数据的 SHA-1 哈希十六进制字符串和扩展名组成。对于 torrent，扩展名为“.torrent”。对于 metalink，它是“.meta4”。如果此选项设置为 false，则选项添加 `aria2.addTorrent()`或`aria2.addMetalink()`不保存下载`--save-session`。默认：`true` 1 --rpc-secret = 设置 RPC 秘密授权令牌。 1 --rpc-secure [true|false] RPC 传输将由 SSL/TLS 加密。RPC 客户端必须使用 https 方案来访问服务器。对于 WebSocket 客户端，使用 wss 方案。使用`--rpc-certificate`和 `--rpc-private-key`选项指定服务器证书和私钥。 1 --rpc-user = 设置 JSON-RPC/XML-RPC 用户。警告--rpc-user选项将在未来版本中弃用。--rpc-secret`尽快迁移到选项。 高级选项 1 --allow-overwrite [true|false] 如果相应的控制文件不存在，则从头开始重新下载。另见`--auto-file-renaming`选项。默认： `false` 1 --allow-piece-length-change [true|false] 如果给出 false，则当片段长度与控制文件中的片段长度不同时，aria2 将中止下载。如果给出 true，您可以继续，但会丢失一些下载进度。默认：`false` 1 --always-resume [true|false]始终恢复下载。如果`true`给出，aria2 总是尝试恢复下载，如果恢复是不可能的，中止下载。如果 `false`给定，当所有给定的 URI 不支持恢复或 aria2 遇到不支持恢复的`N`URI（`N`是使用`--max-resume-failure-tries`选项指定的值）时，aria2 会从头开始下载文件。默认：`true` 1 --async-dns [true|false] 启用异步 DNS。默认：`true` 1 --async-dns-server = [,...] 异步 DNS 解析器中使用的 DNS 服务器地址的逗号分隔列表。通常异步 DNS 解析器从`/etc/resolv.conf`. 使用此选项时，它将使用此选项中指定的 DNS 服务器而不是 `/etc/resolv.conf`. 您可以同时指定 IPv4 和 IPv6 地址。当系统没有`/etc/resolv.conf`并且用户没有创建它的权限时，此选项很有用。 1 --auto-file-renaming [true|false] 如果相同的文件已存在，则重命名文件名。此选项仅适用于 HTTP(S)/FTP 下载。新文件名有一个点和一个数字 (1..9999) 附加在名称之后，但在文件扩展名之前（如果有）。默认：`true` 1 --auto-save-interval = 每 SEC 秒保存一个控制文件 (*.aria2)。如果`0`给出，则在下载期间不保存控制文件。aria2 在停止时保存控制文件，而不管值如何。可能的值介于`0`到之间`600`。默认：`60` 1 --conditional-get [true|false] 仅当本地文件比远程文件旧时才下载文件。此功能仅适用于 HTTP(S) 下载。如果在 Metalink 中指定了文件大小，则它不起作用。它还忽略 Content-Disposition 标头。如果存在控制文件，则此选项将被忽略。此函数使用 If-Modified-Since 标头有条件地仅获取较新的文件。获取本地文件的修改时间时，`--out`如果`--out`未指定，则使用用户提供的文件名（请参阅选项）或 URI 中的文件名部分。要覆盖现有文件，`--allow-overwrite`是必需的。默认：`false` 1 --conf-path = 将配置文件路径更改为PATH。默认值：`$HOME/.aria2/aria2.conf`如果存在，否则为 `$XDG_CONFIG_HOME/aria2/aria2.conf`。 1 --console-log-level = 将日志级别设置为输出到控制台。LEVEL或者是`debug`， `info`，`notice`，`warn`或`error`。默认：`notice` 1 --content-disposition-default-utf8 [true|false] 将 Content-Disposition 标头中的带引号的字符串处理为 UTF-8 而不是 ISO-8859-1，例如，文件名参数，而不是扩展版本的文件名*。默认：`false` 1 -D,--daemon [true|false] 作为守护进程运行。当前工作目录将被更改为`/` 并且标准输入、标准输出和标准错误将被重定向到`/dev/null`. 默认：`false` 1 --deferred-input [true|false] 如果`true`给定，aria2 不会`--input-file`在启动时从option指定的文件中读取所有 URI 和选项，而是在以后需要时一一读取。如果输入文件包含大量要下载的 URI，这可能会减少内存使用。如果`false`给出，aria2 会在启动时读取所有 URI 和选项。默认：`false`警告`--deferred-input`选项`--save-session`一起使用时将被禁用 。 1 --disable-ipv6 [true|false]禁用 IPv6。如果您必须使用损坏的 DNS 并希望避免非常缓慢的 AAAA 记录查找，这将非常有用。默认：`false` 1 --disk-cache = 启用磁盘缓存。如果 SIZE 为`0`，则禁用磁盘缓存。此功能将下载的数据缓存在内存中，最多可增长到 SIZE 字节。缓存存储是为 aria2 实例创建的，并由所有下载共享。磁盘缓存的优点之一是减少了磁盘 I/O，因为数据是以更大的单位写入的，并按文件的偏移量重新排序。如果涉及散列检查并且数据缓存在内存中，我们不需要从磁盘读取它们。SIZE 可以包括`K`或`M` (1K = 1024, 1M = 1024K)。默认：`16M` 1 --download-result = 此选项更改格式化方式。如果 OPT 是，则打印 GID、状态、平均下载速度和路径/URI。如果涉及多个文件，则打印第一个请求文件的路径/URI，并省略其余文件。如果 OPT 是 ，则打印 GID、状态、平均下载速度、进度百分比和路径/URI。为每一行中的每个请求文件打印进度百分比和路径/URI。如果 OPT 是， 则隐藏。默认：`Download Results``default``full``hide``Download Results``default` 1 --dscp = 为 QoS 设置 BitTorrent 流量的传出 IP 数据包中的 DSCP 值。该参数仅设置 IP 数据包 TOS 字段中的 DSCP 位，而不是整个字段。如果您从*/usr/include/netinet/ip.h*取值， 将它们除以 4（否则值将不正确，例如您的`CS1`类会变成`CS4`）。如果您从 RFC、网络供应商的文档、维基百科或任何其他来源获取常用值，请按原样使用它们。 1 --rlimit-nofile = 设置打开文件描述符的软限制。此打开仅在以下情况下有效：系统支持它（posix）限制不超过硬限制。指定的限制大于当前的软限制。这相当于通过 ulimit 设置 nofile，只是它永远不会减少限制。此选项仅在支持 rlimit API 的系统上可用。 1 --enable-color [true|false] 为终端启用颜色输出。默认：`true` 1 --enable-mmap [true|false] 将文件映射到内存中。如果未预先分配文件空间，则此选项可能不起作用。见`--file-allocation`。默认： `false` 1 --event-poll = 指定轮询事件的方法。可能的值是 `epoll`，`kqueue`，`port`，`poll`和`select`。对于每个`epoll`、 `kqueue`、`port`和`poll`，如果系统支持它是可用的。 `epoll`在最近的 Linux 上可用。`kqueue`可用于各种 *BSD 系统，包括 Mac OS X。`port`可用于 Open Solaris。默认值可能因您使用的系统而异。 1 --file-allocation = 指定文件分配方法。 `none`不预先分配文件空间。`prealloc`在下载开始前预先分配文件空间。这可能需要一些时间，具体取决于文件的大小。如果您使用较新的文件系统，例如 ext4（支持扩展区）、btrfs、xfs 或 NTFS（仅限 MinGW 构建），`falloc`则是您的最佳选择。它几乎立即分配大（很少 GiB）文件。不要使用`falloc`遗留文件系统，如 ext3 和 FAT32，因为它几乎与`prealloc`aria2花费相同的时间，并且它会完全阻塞 aria2 直到分配完成。`falloc`如果您的系统没有*posix_fallocate(3)*函数，则可能不可用 。 `trunc`使用*ftruncate(2)*系统调用或特定于平台的对应项将文件截断为指定长度。可能的值：`none`、`prealloc`、`trunc`、`falloc` 默认值：`prealloc`警告使用`trunc`看似分配磁盘空间很快，但实际上它在文件系统中设置文件长度元数据，并且根本不分配磁盘空间。这意味着它无助于避免碎片化。笔记在多文件 torrent 下载中，与指定文件相邻的文件如果共享同一块，也会被分配。 1 --force-save [true|false] `--save-session`即使下载完成或删除，也可以选择保存下载。在这种情况下，此选项还会保存控制文件。这对于保存被识别为完成状态的 BitTorrent 种子可能很有用。默认：`false` 1 --save-not-found [true|false] `--save-session`即使在服务器上找不到文件，也可以使用选项保存下载。在这种情况下，此选项还会保存控制文件。默认：`true` 1 --gid = 手动设置 GID。aria2 通过称为 GID 的 ID 标识每个下载。GID 必须是 16 个字符的十六进制字符串，因此允许 [0-9a-fA-F] 并且不得去除前导零。GID 全 0 是保留的，不得使用。GID必须唯一，否则报错，不添加下载。此选项在恢复使用`--save-session`选项保存的会话时很有用 。如果不使用此选项，则由 aria2 生成新的 GID。 1 --hash-check-only [true|false]如果`true`给定，则在使用`--check-integrity`选项进行哈希检查后 ，无论下载是否完成都中止下载。默认：`false` 1 --human-readable [true|false]在控制台读数中以人类可读格式（例如，1.2Ki、3.4Mi）打印尺寸和速度。默认：`true` 1 --interface = 将套接字绑定到给定的接口。您可以指定接口名称、IP 地址和主机名。可能的值：接口、IP 地址、主机名笔记如果一个接口有多个地址，强烈建议明确指定 IP 地址。另见`--disable-ipv6`。如果您的系统没有*getifaddrs(3)*，则此选项不接受接口名称。 1 --keep-unfinished-download-result [true|false] 保留未完成的下载结果，即使这样做超过 `--max-download-result`. 如果所有未完成的下载必须保存在会话文件中（请参阅`--save-session`，这将很有用 。请记住，要保留的未完成下载结果的数量没有上限。如果这是不受欢迎的，请关闭此选项。默认：`true` 1 --max-download-result = 设置保存在内存中的最大下载结果数。下载结果已完成/错误/已删除下载。下载结果存储在FIFO队列中，最多可以存储NUM个下载结果。当队列已满并创建新的下载结果时，将最旧的下载结果从队列的前面删除，并将新的下载结果推到后面。在此选项中设置大数字可能会导致数千次下载后内存消耗过高。指定 0 表示不保留下载结果。请注意，无论此选项值如何，未完成的下载都会保留在内存中。见`--keep-unfinished-download-result`选项。默认：`1000` 1 --max-mmap-limit = 设置最大文件大小以启用 mmap（请参阅 `--enable-mmap`选项）。文件大小由一次下载中包含的所有文件的总和决定。例如，如果下载包含 5 个文件，则文件大小是这些文件的总大小。如果文件大小严格大于此选项中指定的大小，将禁用 mmap。默认：`9223372036854775807` 1 --max-resume-failure-tries = 与`--always-resume=false`一起使用时，当 aria2 检测到 N 个不支持恢复的 URI 时，会从头开始下载文件。如果 N 是`0`，当所有给定的 URI 不支持恢复时，aria2 会从头开始下载文件。见`--always-resume`选项。默认：`0` 1 --min-tls-version = 指定要启用的最低 SSL/TLS 版本。可能的值：`TLSv1.1`, `TLSv1.2`,`TLSv1.3` 默认值：`TLSv1.2` 1 --multiple-interface = 将套接字绑定到的以逗号分隔的接口列表。请求将在接口之间拆分以实现链路聚合。您可以指定接口名称、IP 地址和主机名。如果 `--interface`使用，此选项将被忽略。可能的值：接口、IP 地址、主机名 1 --log-level = 将日志级别设置为输出。LEVEL或者是`debug`，`info`，`notice`，`warn`或`error`。默认：`debug` 1 --on-bt-download-complete =对于 BitTorrent，`--on-download-complete`在下载完成和播种结束后调用 中指定的命令。另一方面，此选项设置在下载完成后但在播种之前执行的命令。有关 COMMAND 的更多详细信息，请参阅[事件挂钩]。可能的值：`/path/to/command` 1 --on-download-complete = 设置下载完成后执行的命令。有关 COMMAND 的更多详细信息，请参阅[事件挂钩]。另见`--on-download-stop`选项。可能的值：`/path/to/command` 1 --on-download-error = 设置下载因错误中止后执行的命令。有关 COMMAND 的更多详细信息，请参阅[事件挂钩]。另见`--on-download-stop`选项。可能的值： `/path/to/command` 1 --on-download-pause = 设置下载暂停后执行的命令。有关 COMMAND 的更多详细信息，请参阅[事件挂钩]。可能的值：`/path/to/command` 1 --on-download-start = 设置下载开始后要执行的命令。有关 COMMAND 的更多详细信息，请参阅[事件挂钩]。可能的值：`/path/to/command` 1 --on-download-stop = 设置下载停止后执行的命令。您可以使用`--on-download-complete`和覆盖要为特定下载结果执行的命令 `--on-download-error`。如果指定了它们，则不执行此选项中指定的命令。有关 COMMAND 的更多详细信息，请参阅[事件挂钩]。可能的值：`/path/to/command` 1 --optimize-concurrent-downloads [true|false|:] 根据可用带宽优化并发下载数。aria2 使用在先前下载中观察到的下载速度，根据规则 N = A + B Log10（速度以 Mbps 为单位）调整并行启动的下载数量。系数 A 和 B 可以在选项参数中自定义，A 和 B 用冒号分隔。默认值 (A=5, B=25) 导致通常在 1Mbps 网络上使用 5 个并行下载，在 100Mbps 网络上使用 50 个以上。并行下载的数量仍然限制在`--max-concurrent-downloads`参数定义的最大值之下 。默认：`false` 1 --piece-length = 设置 HTTP/FTP 下载的片段长度。这是 aria2 拆分文件时的边界。所有分裂都发生在这个长度的倍数处。此选项在 BitTorrent 下载中将被忽略。如果 Metalink 文件包含片段哈希，它也会被忽略。默认：`1M`笔记`--piece-length` option的可能用例是更改一个 HTTP 管道请求中的请求范围。要启用 HTTP 流水线，请使用 `--enable-http-pipelining` 1 --show-console-readout [true|false] 显示控制台读数。默认：`true` 1 --stderr`` [true|false] 将所有将在 stdout 中打印的控制台输出重定向到 stderr。默认：`false` 1 --summary-interval = 以秒为单位设置间隔以输出下载进度摘要。设置会`0`抑制输出。默认：`60` 1 -Z,--force-sequential [true|false] 在命令行中按顺序获取 URI 并在单独的会话中下载每个 URI，就像通常的命令行下载实用程序一样。默认：`false` 1 --max-overall-download-limit = 以字节/秒为单位设置最大整体下载速度。 `0`意味着不受限制。您可以附加`K`或`M`(1K = 1024, 1M = 1024K)。要限制每次下载的下载速度，请使用`--max-download-limit`选项。默认：`0` 1 --max-download-limit = 以字节/秒为单位设置每次下载的最大下载速度。`0`意味着不受限制。您可以附加`K`或`M`(1K = 1024, 1M = 1024K)。要限制整体下载速度，请使用`--max-overall-download-limit`选项。默认：`0` 1 --no-conf [true|false]禁止加载 aria2.conf 文件。 1 --no-file-allocation-limit = 不会为大小小于 SIZE 的文件分配文件。您可以附加`K`或`M`(1K = 1024, 1M = 1024K)。默认：`5M` 1 -P,--parameterized-uri [true|false] 启用参数化 URI 支持。您可以指定一组零件：`http://{sv1,sv2,sv3}/foo.iso`. 您也可以使用计步器指定数字序列： `http://host/image[000-100:2].img`。可以省略计步器。如果所有 URI 不指向同一个文件，例如上面的第二个示例，则需要 -Z 选项。默认：`false` 1 -q,--quiet [true|false] 使 aria2 安静（无控制台输出）。默认：`false` 1 --realtime-chunk-checksum [true|false] 如果提供了块校验和，则在下载文件时通过计算校验和来验证数据块。默认：`true` 1 --remove-control-file [true|false] 下载前删除控制文件。与`--allow-overwrite=true`下载一起使用 总是从头开始。这对于禁用恢复的代理服务器后面的用户很有用。 1 --save-session = 退出时将错误/未完成的下载保存到 FILE。您可以使用`--input-file`重启选项将此输出文件传递给 aria2c 。如果您希望输出被 gzip 压缩，请在文件名后附加一个 .gz 扩展名。请注意，下载添加 `aria2.addTorrent()`和`aria2.addMetalink()`RPC方法，其元数据无法保存为文件不会被保存。使用`aria2.remove()`和 删除的下载`aria2.forceRemove()`不会被保存。GID 也用 保存 `gid`，但有一些限制，见下文。笔记通常，会保存下载本身的 GID。但有些下载使用元数据（例如，BitTorrent 和 Metalink）。在这种情况下，有一些限制。磁铁 URI，然后是 torrent 下载BitTorrent 元数据下载的 GID 已保存。torrent 文件的 URI，然后是 torrent 下载Torrent 文件下载的 GID 已保存。metalink 文件的 URI，然后是 metalink 文件中描述的文件下载保存metalink文件下载的GID。本地种子文件Torrent 下载的 GID 已保存。本地金属链接文件不会保存任何有意义的 GID。 1 --save-session-interval = `--save-session`每隔 SEC 秒将错误/未完成的下载保存到选项指定的文件中 。如果`0`给定，文件将仅在 aria2 退出时保存。默认：`0` 1 --socket-recv-buffer-size = 以字节为单位设置最大套接字接收缓冲区。指定`0` 将禁用此选项。此值将使用`SO_RCVBUF`带`setsockopt()` 调用的套接字选项设置为套接字文件描述符。默认：`0` 1 --stop = 在经过 SEC 秒后停止应用程序。如果`0`给出，则禁用此功能。默认：`0` 1 --stop-with-process = 当进程 PID 未运行时停止应用程序。如果 aria2 进程是从父进程派生的，这很有用。父进程可以使用自己的 pid fork aria2，当父进程由于某种原因退出时，aria2 可以检测到它并自行关闭。 1 --truncate-console-readout [true|false]截断控制台读数以适合单行。默认：`true` 1 -v,--version 打印版本号、版权和配置信息并退出。 选项说明 可选参数 参数被方括号 ([]) 包围的选项采用可选参数。通常省略参数被评估为true。如果您使用这些选项的缩写形式（例如-V）并给出一个参数，则选项名称及其参数应该连接起来（例如 -Vfalse）。如果在选项名称和参数之间插入任何空格，则参数将被视为 URI，通常这不是您所期望的。\n单位（K 和 M） 一些选项分别采用K和M来方便地表示 1024 和 1048576。aria2 以不区分大小写的方式检测这些字符。换言之，k并且m可以使用，以及K和M分别。\nURI、磁体、TORRENT_FILE、METALINK_FILE 您可以在命令行中指定多个 URI。除非您指定--force-sequential选项，否则 所有 URI 必须指向同一个文件，否则下载将失败。\n您可以指定任意数量的 BitTorrent Magnet URI。请注意，它们始终被视为单独的下载。支持十六进制编码的 40 个字符的 Info Hash 和 Base32 编码的 32 个字符的 Info Hash。tr支持多个参数。由于 BitTorrent Magnet URI 可能包含\u0026amp;字符，因此强烈建议始终使用单 ( ') 或双 ( \u0026quot;) 引号引用 URI 。强烈建议启用 DHT，尤其是在tr 缺少参数时。 有关 BitTorrent Magnet URI 的更多详细信息，请参阅http://www.bittorrent.org/beps/bep_0009.html。\n您还可以指定存储在本地驱动器上的任意数量的 Torrent 文件和 Metalink 文档。请注意，它们始终被视为单独的下载。支持 Metalink4 和 Metalink 3.0 版。\n您可以使用 -T 选项和 URI 指定 Torrent 文件。通过这样做，您可以同时从 torrent swarm 和 HTTP(S)/FTP/SFTP 服务器下载文件，同时将来自 HTTP(S)/FTP/SFTP 的数据上传到 torrent swarm。对于单个文件 torrent，URI 可以是指向资源的完整 URI，或者如果 URI 以 / 结尾，则添加 torrent 中 torrent 文件中的名称。对于多文件种子文件，添加名称和路径以形成每个文件的 URI。\nnote:如果 URI包含或任何在 shell 中具有特殊含义的字符，请确保使用单 ( ') 或双 ( \u0026quot;) 引号引用 URI \u0026amp;。\n继续下载 通常，如果之前的传输是由 aria2 完成的，您只需发出相同的命令（aria2c URI）即可恢复传输。\n如果之前的传输是由浏览器或 wget 之类的顺序下载管理器进行的，则使用--continue选项继续传输。\n事件挂钩 aria2 提供了在特定事件发生后指定任意命令的选项。目前有以下选项可用： --on-bt-download-complete、 --on-download-pause、 --on-download-complete。 --on-download-start, --on-download-error, --on-download-stop.\naria2 在执行时将 3 个参数传递给指定的命令。这些参数是：GID、文件数和文件路径。对于 HTTP、FTP 和 SFTP 下载，通常文件数为 1。BitTorrent 下载可以包含多个文件。如果文件数大于一个，则文件路径为第一个。换句话说，这是在aria2.getFiles()RPC 方法的响应中选择的键为真的第一个结构的路径键的值。如果要获取所有文件路径，请考虑使用 JSON-RPC/XML-RPC。请注意，由于重定向或 Content-Disposition 标头，在 HTTP 下载过程中文件路径可能会发生变化。\n让我们看一个如何将参数传递给命令的示例：\n1 2 3 4 5 $ cat hook.sh #!/bin/sh echo \u0026#34;Called with [$1] [$2] [$3]\u0026#34; $ aria2c --on-download-complete hook.sh http://example.org/file.iso Called with [1] [1] [/path/to/file.iso] 退出状态 由于 aria2 可以同时处理多个下载，因此在会话中会遇到很多错误。aria2 根据上次遇到的错误返回以下退出状态。\n0\n如果所有下载都成功。\n1\n如果发生未知错误。\n2\n如果发生超时。\n3\n如果没有找到资源。\n4\n如果 aria2 看到指定数量的“资源未找到”错误。见--max-file-not-found选项。\n5\n如果下载因下载速度太慢而中止。见--lowest-speed-limit选项。\n6\n如果出现网络问题。\n7\n如果有未完成的下载。仅当所有完成的下载都成功并且当 aria2Ctrl-C通过用户按下或发送 TERM 或 INT 信号退出时，队列中还有未完成的下载时，才会报告此错误。\n8\n如果远程服务器不支持恢复，则需要恢复才能完成下载。\n9\n如果没有足够的可用磁盘空间。\n10\n如果片段长度与 .aria2 控制文件中的长度不同。见 --allow-piece-length-change选项。\n11\n如果 aria2 在那一刻正在下载相同的文件。\n12\n如果 aria2 在那一刻正在下载相同的信息散列种子。\n13\n如果文件已经存在。见--allow-overwrite选项。\n14\n如果重命名文件失败。见--auto-file-renaming选项。\n15\n如果 aria2 无法打开现有文件。\n16\n如果 aria2 无法创建新文件或截断现有文件。\n17\n如果发生文件 I/O 错误。\n18\n如果 aria2 无法创建目录。\n19\n如果名称解析失败。\n20\n如果 aria2 无法解析 Metalink 文档。\n21\n如果 FTP 命令失败。\n22\n如果 HTTP 响应标头错误或意外。\n23\n如果发生太多重定向。\n24\n如果 HTTP 授权失败。\n25\n如果 aria2 无法解析编码文件（通常是“.torrent”文件）。\n26\n如果“.torrent”文件已损坏或缺少 aria2 所需的信息。\n27\n如果 Magnet URI 不好。\n28\n如果给出了错误/无法识别的选项或给出了意外的选项参数。\n29\n如果远程服务器由于临时过载或维护而无法处理请求。\n30\n如果 aria2 无法解析 JSON-RPC 请求。\n31\n预订的。不曾用过。\n32\n如果校验和验证失败。\n笔记\n完成下载时发生的错误不会报告为退出状态。\n环境 aria2 识别以下环境变量。\nhttp_proxy [http://][USER:PASSWORD@]HOST[:PORT]\n指定用于 HTTP 的代理服务器。覆盖配置文件中的 http-proxy 值。命令行选项--http-proxy会覆盖此值。\nhttps_proxy [http://][USER:PASSWORD@]HOST[:PORT]\n指定用于 HTTPS 的代理服务器。覆盖配置文件中的 https-proxy 值。命令行选项--https-proxy会覆盖此值。\nftp_proxy [http://][USER:PASSWORD@]HOST[:PORT]\n指定用于 FTP 的代理服务器。覆盖配置文件中的 ftp-proxy 值。命令行选项--ftp-proxy会覆盖此值。\nall_proxy [http://][USER:PASSWORD@]HOST[:PORT]\n如果未指定特定于协议的代理，请指定要使用的代理服务器。覆盖配置文件中的所有代理值。命令行选项--all-proxy会覆盖此值。\n笔记\n尽管 aria2在代理 URI 中接受ftp://和https://方案，但它只是假定http://已指定并且不会根据指定的方案更改其行为。\nno_proxy [DOMAIN,...]\n指定一个以逗号分隔的主机名、域和网络地址列表，带有或不带有子网掩码，其中不应使用代理。覆盖配置文件中的 no-proxy值。命令行选项 --no-proxy会覆盖此值。\n档案 aria2.conf 默认情况下，aria2 检查遗留路径 $HOME/.aria2/aria2.conf是否存在，否则它会解析 $XDG_CONFIG_HOME/aria2/aria2.conf为它的配置文件。您可以使用--conf-path 选项指定配置文件的路径。如果您不想使用配置文件，请使用 --no-conf选项。\n配置文件是一个文本文件，每行有 1 个选项。在每一行中，您可以按以下格式指定名称-值对： NAME=VALUE，其中 name 是不带--前缀的长命令行选项名称 。您可以对命令行选项使用相同的语法。开头的行#被视为注释：\n1 2 3 4 5 6 # sample configuration file for aria2c listen-port=60000 dht-listen-port=60000 seed-ratio=1.0 max-upload-limit=50K ftp-pasv=true 笔记\n诸如用户/密码之类的机密信息可能包含在配置文件中。建议更改配置文件的文件模式位（例如），以免其他用户看到文件内容。chmod 600 aria2.conf\n环境变量，例如${HOME}，由 shell 扩展。这意味着配置文件中使用的那些变量不会被扩展。但是，${HOME}在配置文件中引用用户的主目录以指定文件路径很有用。因此，aria2 将${HOME}在以下选项值中找到的扩展到用户的主目录：\nca-certificate certificate dht-file-path dht-file-path6 dir input-file load-cookies log metalink-file netrc-path on-bt-download-complete on-download-complete on-download-error on-download-start on-download-stop on-download-pause out private-key rpc-certificate rpc-private-key save-cookies save-session server-stat-if server-stat-of torrent-file 请注意，即使在命令行中使用了上述选项，也会发生这种扩展。这意味着扩展可能会发生 2 次：首先是 shell，然后是 aria2c。\n数据文件 除传统的文件路径$HOME/.aria2/dht.dat，并 $HOME/.aria2/dht6.dat都指向现有文件，IPv4的DHT的路由表中保存的路径 $XDG_CACHE_HOME/aria2/dht.dat和IPv6 DHT的路由表中保存的路径$XDG_CACHE_HOME/aria2/dht6.dat。\n网路 默认情况下为 HTTP(S)/FTP/SFTP 启用 Netrc 支持。要禁用 netrc 支持，请指定--no-netrc选项。您的 .netrc 文件应该具有正确的权限 (600)。\n如果机器名称以 开头.，aria2 将执行域匹配而不是精确匹配。这是 aria2 的扩展。例如域匹配，想象以下 .netrc 条目：\n1 machine .example.org login myid password mypasswd aria2.example.org域匹配.example.org并使用myid和 mypasswd.\n一些域匹配示例如下：example.net不域匹配 .example.org。example.org不域匹配.example.org ，因为前面的.。如果要匹配example.org，请指定 example.org。\n控制文件 aria2 使用控制文件来跟踪下载进度。控制文件放置在与下载文件相同的目录中，其文件名是下载文件的文件名后.aria2 附加。例如，如果您正在下载 file.zip，那么控制文件应该是 file.zip.aria2。（此命名约定有一个例外。如果您正在下载多 torrent，其控制文件是 torrent 的“顶级目录”名称并.aria2 附加。“顶级目录”名称是“名称”中的值info\u0026quot; 目录中的 torrent 文件。）\n通常，一旦下载完成，控制文件就会被删除。如果 aria2 决定无法恢复下载（例如，从不支持恢复的 HTTP 服务器下载文件时），则不会创建控制文件。\n通常，如果您丢失了控制文件，则无法继续下载。但是，如果您有一个带有文件块校验和的 torrent 或 metalink，您可以通过在命令行中为 aria2c 提供 -V 选项来恢复下载而无需控制文件。\n输入文件 输入文件可以包含要下载的 aria2 URI 列表。您可以为单个实体指定多个 URI：使用 TAB 字符在一行中单独指定 URI。\n每行都被视为在命令行参数中提供。因此它们受--force-sequential和--parameterized-uri选项的影响。\n由于输入文件中的 URI 是由 aria2 直接读取的，因此不能用单（'）或双（\u0026quot;）引用来引用它们。\n以 开头的行#被视为注释并被跳过。\n此外，可以在每行 URI 之后指定以下选项。这些可选行必须以空格开头。\nall-proxyall-proxy-passwdall-proxy-userallow-overwriteallow-piece-length-changealways-resumeasync-dnsauto-file-renamingbt-enable-hook-after-hash-checkbt-enable-lpdbt-exclude-trackerbt-external-ipbt-force-encryptionbt-hash-check-seedbt-load-saved-metadatabt-max-peersbt-metadata-onlybt-min-crypto-levelbt-prioritize-piecebt-remove-unselected-filebt-request-peer-speed-limitbt-require-cryptobt-save-metadatabt-seed-unverifiedbt-stop-timeoutbt-trackerbt-tracker-connect-timeoutbt-tracker-intervalbt-tracker-timeoutcheck-integritychecksumconditional-getconnect-timeoutcontent-disposition-default-utf8continuedirdry-run enable-http-keep-aliveenable-http-pipeliningenable-mmapenable-peer-exchangefile-allocationfollow-metalinkfollow-torrentforce-saveftp-passwdftp-pasvftp-proxyftp-proxy-passwdftp-proxy-userftp-reuse-connectionftp-typeftp-usergidhash-check-onlyheaderhttp-accept-gziphttp-auth-challengehttp-no-cachehttp-passwdhttp-proxyhttp-proxy-passwdhttp-proxy-userhttp-userhttps-proxyhttps-proxy-passwdhttps-proxy-userindex-outlowest-speed-limitmax-connection-per-servermax-download-limitmax-file-not-foundmax-mmap-limitmax-resume-failure-tries max-triesmax-upload-limitmetalink-base-urimetalink-enable-unique-protocolmetalink-languagemetalink-locationmetalink-osmetalink-preferred-protocolmetalink-versionmin-split-sizeno-file-allocation-limitno-netrcno-proxyoutparameterized-uripausepause-metadatapiece-lengthproxy-methodrealtime-chunk-checksumrefererremote-timeremove-control-fileretry-waitreuse-urirpc-save-upload-metadataseed-ratioseed-timeselect-filesplitssh-host-key-mdstream-piece-selectortimeouturi-selectoruse-headuser-agent 这些选项与命令行选项中的选项具有完全相同的含义，但它仅适用于它所属的 URI。请注意，--必须去除输入文件前缀中的选项。\n比如uri.txt的内容是：\n1 2 3 4 http://server/file.iso http://mirror/file.iso dir=/iso_images out=file.img http://foo/bar 如果使用选项执行 aria2 ，则 另存为并从和下载。该文件 是从下载并保存为.-i uri.txt -d /tmp``file.iso``/iso_images/file.img``http://server/file.iso``http://mirror/file.iso``bar``http://foo/bar``/tmp/bar\n在某些情况下，out参数不起作用。有关--out 限制，请参阅选项说明。\n服务器性能配置文件 本节介绍服务器性能配置文件的格式。该文件是纯文本文件，每行有几NAME=VALUE对，以逗号分隔。目前可以识别以下名称：\nhost\n服务器的主机名。必需的。\nprotocol\n此配置文件的协议，例如 ftp、http。必需的。\ndl_speed\n在上一次下载中观察到的平均下载速度（以每秒字节数为单位）。必需的。\nsc_avg_speed\n在上一次下载中观察到的平均下载速度（以每秒字节数为单位）。此值仅在下载是在单连接环境中完成且仅由 AdaptiveURISelector 使用时才会更新。可选的。\nmc_avg_speed\n在上一次下载中观察到的平均下载速度（以每秒字节数为单位）。只有在多连接环境中完成下载并且仅由 AdaptiveURISelector 使用时，才会更新此值。可选的。\ncounter\n服务器使用了多少次。目前该值仅由 AdaptiveURISelector 使用。可选的。\nlast_updated\n与此服务器的 GMT 上次联系时间，以自纪元以来的秒数指定（1970 年 1 月 1 日的 00:00:00，UTC）。必需的。\nstatus\n当无法访问服务器或出现服务中断或超时时设置 ERROR。否则，设置为 OK。\n这些字段必须存在于一行中。字段的顺序并不重要。您可以放置上述以外的对；他们只是被忽略了。\n一个例子如下：\n1 2 host=localhost, protocol=http, dl_speed=32000, last_updated=1222491640, status=OK host=localhost, protocol=ftp, dl_speed=0, last_updated=1222491632, status=ERROR 杂项 控制台读数 下载文件时，aria2 会向控制台打印读数以显示下载进度。控制台读数如下所示：\n1 [#2089b0 400.0KiB/33.2MiB(1%) CN:1 DL:115.7KiB ETA:4m51s] 本节介绍这些数字和字符串的含义。\n#NNNNNN\nGID 的前 6 个字符作为十六进制字符串。GID 是每个下载的唯一 ID，在 aria2 内部。GID 在使用 RPC 接口与 aria2 交互时特别有用。\nX/Y(Z%)\n完成长度、总文件长度及其进度。如果 --select-file使用，这是所选文件的总和。\nSEED\naria2 播种完成的种子时的共享比率。\nCN\naria2 已建立的连接数。\nSD\naria2 所连接的播种机数量。\nDL\n下载速度（每秒字节数）。\nUL\n上传速度（每秒字节数）和上传的字节数。\nETA\n完成下载的预期时间。\n当正在进行不止一次下载时，为了显示多次下载的信息，将省略上述某些信息。并且整体下载和上传速度显示在行首。\n当 aria2 正在分配文件空间或验证校验和时，它还会打印这些操作的进度：\n文件分配\nGID，已分配的长度和总长度（以字节为单位）。\n校验和\nGID，已验证的长度和总长度（以字节为单位）。\n例子 HTTP/FTP 分段下载 下载文件 1 $ aria2c \u0026#34;http://host/file.zip\u0026#34; 笔记\n要停止下载，请按Ctrl-C。您可以通过在同一目录中使用相同参数运行 aria2c 来恢复传输。只要 URI 指向同一个文件，您就可以更改它们。\n从两个不同的 HTTP 服务器下载文件 1 $ aria2c \u0026#34;http://host/file.zip\u0026#34; \u0026#34;http://mirror/file.zip\u0026#34; 使用多个连接从一台主机下载文件 1 $ aria2c -x2 -k1M \u0026#34;http://host/file.zip\u0026#34; 笔记\n-x 选项指定允许的连接数，而 -k 选项指定块的大小。\n同时从 HTTP 和 FTP 服务器下载文件 1 $ aria2c \u0026#34;http://host1/file.zip\u0026#34; \u0026#34;ftp://host2/file.zip\u0026#34; 同时下载文本文件中列出的文件 1 $ aria2c -ifiles.txt -j2 笔记\n-j 选项指定并行下载的数量。\n使用代理 对于 HTTP：\n1 2 $ aria2c --http-proxy=\u0026#34;http://proxy:8080\u0026#34; \u0026#34;http://host/file\u0026#34; $ aria2c --http-proxy=\u0026#34;http://proxy:8080\u0026#34; --no-proxy=\u0026#34;localhost,127.0.0.1,192.168.0.0/16\u0026#34; \u0026#34;http://host/file\u0026#34; 对于 FTP：\n1 $ aria2c --ftp-proxy=\u0026#34;http://proxy:8080\u0026#34; \u0026#34;ftp://host/file\u0026#34; 笔记\n有关详细信息， 请参阅--http-proxy、--https-proxy、和。您可以在环境变量中指定代理。参见环境部分。--ftp-proxy--all-proxy--no-proxy\n使用授权的代理 1 2 $ aria2c --http-proxy=\u0026#34;http://username:password@proxy:8080\u0026#34; \u0026#34;http://host/file\u0026#34; $ aria2c --http-proxy=\u0026#34;http://proxy:8080\u0026#34; --http-proxy-user=\u0026#34;username\u0026#34; --http-proxy-passwd=\u0026#34;password\u0026#34; \u0026#34;http://host/file\u0026#34; Metalink 下载 使用远程 Metalink 下载文件 1 $ aria2c --follow-metalink=mem \u0026#34;http://host/file.metalink\u0026#34; 使用本地 metalink 文件下载 1 $ aria2c -p --lowest-speed-limit=4000 file.metalink 笔记\n要停止下载，请按Ctrl-C。您可以通过在同一目录中使用相同参数运行 aria2c 来恢复传输。\n下载几个本地的metalink文件 1 $ aria2c -j2 file1.metalink file2.metalink 仅下载选定的文件 1 $ aria2c --select-file=1-4,8 file.metalink 笔记\n使用 -S 选项将索引打印到控制台。\n使用具有用户首选项的本地 metalink 文件下载文件 1 $ aria2c --metalink-location=jp,us --metalink-version=1.1 --metalink-language=en-US file.metalink BitTorrent 下载 使用远程 BitTorrent 文件下载文件 1 $ aria2c --follow-torrent=mem \u0026#34;http://host/file.torrent\u0026#34; 使用本地 torrent 文件下载 1 $ aria2c --max-upload-limit=40K file.torrent 笔记\n\u0026ndash;max-upload-limit 指定最大上传速率。\n笔记\n要停止下载，请按Ctrl-C。您可以稍后通过在同一目录中使用相同参数运行 aria2c 来恢复传输。\n使用 BitTorrent Magnet URI 下载 1 $ aria2c \u0026#34;magnet:?xt=urn:btih:248D0A1CD08284299DE78D5C1ED359BB46717D8C\u0026amp;dn=aria2\u0026#34; 笔记\n在命令行上指定 URI 时，不要忘记引用包含\u0026amp; 单 ( ') 或双 ( \u0026quot;) 引号字符的BitTorrent Magnet URI 。\n下载 2 个种子 1 $ aria2c -j2 file1.torrent file2.torrent 通过 torrent 和 HTTP/FTP 服务器并行下载文件 1 $ aria2c -Ttest.torrent \u0026#34;http://host1/file\u0026#34; \u0026#34;ftp://host2/file\u0026#34; 仅下载特定文件（通常称为“选定下载”） 1 $ aria2c --select-file=1-4,8 file.torrent 笔记\n使用 -S 选项将索引打印到控制台。\n下载 .torrent 文件，但不要下载 torrent 1 $ aria2c --follow-torrent=false \u0026#34;http://host/file.torrent\u0026#34; 指定输出文件名 要为 BitTorrent 下载指定输出文件名，您需要知道 torrent 中文件的索引（请参阅 参考资料--show-files）。例如，输出如下所示：\n1 2 3 4 5 6 7 8 idx|path/length ===+====================== 1|dist/base-2.6.18.iso |99.9MiB ---+---------------------- 2|dist/driver-2.6.18.iso |169.0MiB ---+---------------------- 要将“dist/base-2.6.18.iso”保存在“/tmp/mydir/base.iso”中并将“dist/driver-2.6.18.iso”保存在“/tmp/dir/driver.iso”中，请使用以下命令：\n1 $ aria2c --dir=/tmp --index-out=1=mydir/base.iso --index-out=2=dir/driver.iso file.torrent 更改传入对等连接的侦听端口 1 $ aria2c --listen-port=7000-7001,8000 file.torrent 笔记\n由于 aria2 没有为端口转发配置防火墙或路由器，因此由您手动完成。\n指定种子下载完成后停止播种的条件 1 $ aria2c --seed-time=120 --seed-ratio=1.0 file.torrent 笔记\n在上例中，程序在下载完成或种子比率达到 1.0 后 120 分钟后停止播种。\n限制上传速度 1 $ aria2c --max-upload-limit=100K file.torrent 启用 IPv4 DHT 1 $ aria2c --enable-dht --dht-listen-port=6881 file.torrent 笔记\nDHT 使用 UDP。由于 aria2 没有为端口转发配置防火墙或路由器，因此由您手动完成。\n启用 IPv6 DHT 1 $ aria2c --enable-dht6 --dht-listen-port=6881 --dht-listen-addr6=YOUR_GLOBAL_UNICAST_IPV6_ADDR 笔记\n对于 IPv6，aria2 使用与 IPv4 相同的端口。\n添加和删除跟踪器 URI 忽略在 file.torrent 中定义的所有跟踪器公告 URI 并使用 http://tracker1/announce，http://tracker2/announce而是：\n1 $ aria2c --bt-exclude-tracker=\u0026#34;*\u0026#34; --bt-tracker=\u0026#34;http://tracker1/announce,http://tracker2/announce\u0026#34; file.torrent 更高级的 HTTP 功能 加载 cookie 1 $ aria2c --load-cookies=cookies.txt \u0026#34;http://host/file.zip\u0026#34; 笔记\n您可以不加修改地使用 Firefox/Mozilla/Chromium 的 cookie 文件。\n恢复由网络浏览器或其他程序启动的下载 1 $ aria2c -c -s2 \u0026#34;http://host/partiallydownloadedfile.zip\u0026#34; 笔记\n这仅在初始下载不是多分段时才有效。\nSSL/TLS 的客户端证书授权 指定一个 PKCS12 文件如下：\n1 $ aria2c --certificate=/path/to/mycert.p12 笔记\n中指定的文件--certificate必须包含一个 PKCS12 编码的证书和密钥。密码必须为空。\n或者，如果支持 PEM 文件，请使用如下命令：\n1 $ aria2c --certificate=/path/to/mycert.pem --private-key=/path/to/mykey.pem https://host/file 笔记\n中指定的文件--private-key必须解密。给出加密时的行为是未定义的。\n使用给定的 CA 证书验证 SSL/TLS 服务器 1 $ aria2c --ca-certificate=/path/to/ca-certificates.crt --check-certificate https://host/file 笔记\n此选项仅在针对 GnuTLS 或 OpenSSL 编译 aria2 时可用。WinTLS 和 AppleTLS 将始终使用系统证书存储。而不是``\u0026ndash;ca-certificate`在该商店中 安装证书。\nRPC 使用 SSL/TLS 加密 RPC 流量 指定服务器 PKC12 文件：\n1 $ aria2c --enable-rpc --rpc-certificate=/path/to/server.p12 --rpc-secure 笔记\n中指定的文件--rpc-certificate必须包含一个 PKCS12 编码的证书和密钥。密码必须为空。\n或者，当支持 PEM 文件（GnuTLS 和 OpenSSL）时，指定服务器证书文件和私钥文件如下：\n1 $ aria2c --enable-rpc --rpc-certificate=/path/to/server.crt --rpc-private-key=/path/to/server.key --rpc-secure 以及更多高级功能 限制下载速度 每次下载：\n1 $ aria2c --max-download-limit=100K file.metalink 全面的：\n1 $ aria2c --max-overall-download-limit=100K file.metalink 修复损坏的下载 1 $ aria2c -V file.metalink 笔记\n与带有块校验和的 BitTorrent 或 Metalink 一起使用时，可以有效地修复损坏的下载。\n如果下载速度低于指定限制，则断开连接 1 $ aria2c --lowest-speed-limit=10K file.metalink 参数化 URI 支持 您可以指定一组零件：\n1 $ aria2c -P \u0026#34;http://{host1,host2,host3}/file.iso\u0026#34; 您可以指定数字序列：\n1 $ aria2c -Z -P \u0026#34;http://host/image[000-100].png\u0026#34; 笔记\n如果 URI 不指向同一个文件，则需要 -Z 选项，例如在上面的示例中。\n您可以指定计步器：\n1 $ aria2c -Z -P \u0026#34;http://host/image[A-Z:2].png\u0026#34; 验证校验和 1 $ aria2c --checksum=sha-1=0192ba11326fe2298c8cb4de616f4d4140213837 http://example.org/file 并行下载任意数量的 URI、metalink、torrent 1 $ aria2c -j3 -Z \u0026#34;http://host/file1\u0026#34; file2.torrent file3.metalink BitTorrent 加密 使用 ARC4（混淆）加密整个有效负载：\n1 $ aria2c --bt-min-crypto-level=arc4 --bt-require-crypto=true file.torrent 也可以看看 项目网址：https : //aria2.github.io/\nMetalink 主页：http : //www.metalinker.org/\nMetalink 下载描述格式： RFC 5854\n","date":"2021-08-09T13:12:19+08:00","image":"https://cn.bing.com/th?id=OHR.BifengxiaPanda_EN-CN7747319780_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/aria2_conf/","title":"Aria2 神器的进阶用法"},{"content":"Windows 10 输入法多了ENG解决方案 ​\t最近突然发现了一个算是 Windows10 的Bug，就是在远程桌面的时候会莫名其妙的多了一个ENG输入法，让原本用着 巨硬输入法 的我们不太适应，而且在断开远程连接后，这个ENG会一直留在电脑上，就非常蛋疼。下面就跟着我来操作操作吧！\n​\t首先出了问题，你肯定会在设置里面找输入法啥啥啥设置，但是呢，你很快就会发现，啥也找不到，原来的微软输入法也没有问题，并且也没有多出来的输入法，这就奇了他妈的怪了。我明明啥都是没有问题的，这个多出来的ENG输入法哪里来的呢？\n​\t一开始我也很疑惑，然后百度了大半天，都是广告，不忍直视。这里再来骂一遍百度垃圾。然后只能自己摸索呀！我看了这个ENG输入法属于 Englisg(美国) ，我直接好家伙，我输入法明明是 中文(简体，中国) ，这个多出来的ENG输入法居然是一个我没有安装过的语言带来的，这就离谱呀。\n​\t找到了问题在这个多出来的输入法以后，我就想尽办法去把这个ENG输入法删掉，可是怎么都删不掉它。而且设置里面也没有英文输入法。我一气之下就下载了这个 Englisg(美国) 语言，安装完了后会自动开启ENG输入法，这次我直接删 Englisg(美国)语言，好家伙，烦了我好长时间的ENG输入法终于是滚出我的电脑了。\n","date":"2021-07-19T22:07:17Z","image":"https://cn.bing.com/th?id=OHR.MassapequaOwl_EN-CN6825529741_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/kill_windows_eng/","title":"Windows 10 输入法多了ENG解决方案"},{"content":"监控狗东商品价格并利用bark推送价格到手机上 众所周知啊，狗东上面商品的价格经常在变动，有时候你在最贵的时候买下来了，那你就是冤大头了。除非你急着用。\n先来说一下我的思路吧！\n我一开始是想着在我的服务器上监控商品价格，然后每十分钟对比一下价格，再利用邮件来推送给我自己。这个想法是挺好的，奈何我利用postfix来relay邮件时我的outlook账号死活登陆不了，换qq邮箱，发邮件吧，妈的直接吞邮件。\n既然邮件都不可以，我就想着用公众号吧，直接推送到我的微信，然后登陆我尘封好几年的公众号，嗯！用不了。\n然后我就直接放弃了！好了文章结束！\n哈哈哈哈，开玩笑的，然后我又想到了 bark ，开源免费的推送，还可以自己搭建自己的推送服务端！兄弟们！把安全好用打在公屏上！！！\n好了！不废话了！\n监控狗东商品价格 其实这一部分很简单，调用API就可以了，别问我什么API，自己找吧！下面就是我找到的获取价格的API\n1 https://pe.3.cn/prices/mgets?skuids=商品ID 编写shell脚本查询价格 写shell脚本那不就是右手就行了嘛！都用不到左手！😄\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #!/bin/bash price=`curl https://pe.3.cn/prices/mgets\\?skuids\\=2066790 2\u0026gt;/dev/null | awk -F\\\u0026#34; \u0026#39;{print $4}\u0026#39; | awk -F\\. \u0026#39;{print $1}\u0026#39;` dates=`date +\u0026#34;%Y-%m-%d_%H:%M:%S\u0026#34;` nslo=`tail -n 1 ~/price.log | awk \u0026#39;{print $2}\u0026#39;` printf \u0026#34;%s %s\\n\u0026#34; $dates $price \u0026gt;\u0026gt; ~/price.log if [[ $price -eq $nslo ]] then echo \u0026#34;$dates 狗东居然还不降价！\u0026#34; curl -k \u0026#34;https://api.jokeme.top/xyzxyzxyzxyzxyzxyz/当前价格$price/狗东居然还不降价?group=bark\u0026#34; elif [[ $price -gt $nslo ]] then echo \u0026#34;狗东居然涨价？\u0026#34; curl -k \u0026#34;https://api.jokeme.top/xyzxyzxyzxyzxyzxyz/当前价格$price/狗东居然涨价/?group=bark\u0026#34; else curl -k \u0026#34;https://api.jokeme.top/xyzxyzxyzxyzxyzxyz/当前价格$price/狗东降价啦?group=bark\u0026#34; fi 接着为了定时执，只需要把这个脚步加入crontab就可以了\n","date":"2021-06-17T22:49:29Z","image":"https://cn.bing.com/th?id=OHR.LyonAstronomical_EN-CN6225991486_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/bark_jd_price/","title":"监控狗东商品价格并利用bark推送价格到手机上"},{"content":"定时备份hugo帖子 今天出差,在高铁上实在是太无聊,信号也不怎么好,我就写了个每天晚上备份Hugo文章的脚本\n1 2 3 4 5 6 7 8 9 10 11 12 #!/bin/bash datee=`date +%Y-%m-%d` bn=`docker ps | egrep hugo` if [[ -n $bn ]] then mkdir ~/udisk/bkhugo/$datee docker cp hugo:/root/pblog/content ~/udisk/bkhugo/$datee/ #记得换成你自己的路径 echo -e \u0026#34;\\033[49;32mBackup posts successfully\\033[0m\u0026#34; else echo -e \u0026#34;\\033[49;31mService hugo is not running\\033[0m\u0026#34; fi 也没有啥好说的,都是一些简单的命令组合而成,然后再利用crontab每天00:00执行\n","date":"2021-06-08T23:12:18Z","image":"https://cn.bing.com/th?id=OHR.AztecNewYear_EN-CN5467528764_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/backup_hugo_posts/","title":"定时备份hugo帖子"},{"content":"Spring IOC DI 学习 最近在海底捞上班搞运维，但是我也没有放下我的Java开发学习，今天下班早点，我就又开始学习Springboot啦\n因为去年写的wh项目太拉胯啦，有好多东西都是一知半解，只是知道某一个方法应该怎么用，但是为什么要这么使用，其中的道理我还是不太明白，虽然之前我也是写过两篇Springboot开发中的知识，但是现在显而易见，我已经把他们给忘记啦。所以现在再拉出来鞭尸一遍巩固学习的知识。\nIOC简介 ioc [Inversion of Control] 控制反转，具体的官话我也说不出来，反正我个人的理解就是：\n不需要我这个小垃圾来管理Java bean啦，我不需要亲自的new对象了，这些事情都交给Springboot来做。总的来说就是我解放了。\nIOC是什么用？ 众所周知，Springboot是注解式开发，那么IOC肯定是配合着注解来使用的。\n假如，现在我有一个项目，里面有一个pojo package，里面有一个unix类如下：\n1 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 package top.jokeme.funny.pojo; import org.springframework.stereotype.Component; @Component public class unix { private Integer year; private String name; private Boolean unix; @Override public String toString() {return \u0026#34;unix{\u0026#34; +\u0026#34;year=\u0026#34; + year +\u0026#34;, name=\u0026#39;\u0026#34; + name + \u0026#39;\\\u0026#39;\u0026#39; +\u0026#34;, unix=\u0026#34; + unix +\u0026#39;}\u0026#39;; } public Integer getYear() {return year;} public void setYear(Integer year) {this.year = year;} public String getName() {return name;} public void setName(String name) {this.name = name;} public Boolean getUnix() {return unix;} public void setUnix(Boolean unix) {this.unix = unix;} } 可以看到我加了一个注解：@Component\n然后我的server package下面有：interface ：halo 、 package：impl 、impl下面有一个halo的实现类：haloimpl\nhalo内容如下：\n1 2 3 4 5 package top.jokeme.funny.service; public interface halo { public String sayhalo (); } haloimpl内容如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package top.jokeme.funny.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import top.jokeme.funny.pojo.unix; import top.jokeme.funny.service.halo; @Service public class haloimpl implements halo { @Autowired private unix unix; @Override public String sayhalo() { unix.setUnix(true); unix.setYear(1973); unix.setName(\u0026#34;Unix_like Linux_Ubuntu_20.04 Build 2021 03 06 13:09:35\u0026#34;); return unix.toString(); } } 可以看的出来，这里最重要的注解就是@Autowired ，我们没有new对象，但是在加了注解以后就可以直接调用方法了。\n如果你愿意加上一个controller，那你就可以在网页上查看你写的数据！\ncontroller如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package top.jokeme.funny.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import top.jokeme.funny.service.halo; @Controller public class retUnix { @Autowired private halo haloimpl; @ResponseBody @RequestMapping(\u0026#34;getunix\u0026#34;) public String getunix (){ return haloimpl.sayhalo(); } } 可以看到，万众瞩目的注解 @Autowired 依然在这里发光发热，我们只需要调用接口halo，就有一个实现类被实例化了。\n如果此时，你再一不小心运行啦该代码，并且又一不小心的访问web页面，你就会发现，你写的东西已经被成功的返回啦！\n我的疑惑 问题一 为什么同样在/下面，unix类需要加上注解 @Component 才能被Springboot接管，而haloimpl没有加 @Component 就可以被Springboot接管呢？或者通俗的说就是haloimpl可以通过 @Autowired获取unix类的原因我知道，是因为unix加了 @Component 注解，但是 haliimpl 没有加注解为什么没有加 @Component 也可以被retUnix 获取到呢？\n我个人现在也觉得这个问题很无聊，因为haloimpl 有一个 @Service 注解。\n再来给自己解释一下注解：\n@Component：加上该注解就表示当前类需要被Spring的容器管理； @Service：加上该注解就表示当前类需要在业务逻辑类当中使用。 @Repositorty： 加上该注解就表示当前类在数据访问层使用 @Controller：加上该注解就表示当前类在展现层（MVC）使用\n问题二 为什么加了 @Autowired 参数以后就可以直接调用方法了？\n其实吧加注解这一步就是叫 DI 也就是依赖注入，@Component 和 @Autowired 是成对出现的，除非你让Spring管理你的类，你自己再手动new。我想正常开发没个十年脑血栓不会这么干吧！\n文章部分内容参考：所念皆山海\n","date":"2021-06-03T17:27:33Z","image":"https://cn.bing.com/th?id=OHR.CapePerpetua_EN-CN1657098545_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/spring_ioc_di/","title":"Spring IOC DI 学习"},{"content":"Hackintosh Laptop 常见快捷键 一般来说可以macOS和Windows对应键位参考下面的对照表即可\nmacOS Windows Command / ⌘ win Option / ⌥ alt Control / ⌃ ctrl Shift / ⇧ shift 所有的通用快捷键加粗标记\n系统快捷键: 强制退出：win + alt + esc\n对标Windows的任务管理器，但是简化到了只有强制退出功能\n切换桌面：ctrl + ◀︎ / ▶︎\n切换任务：win + tab\n打开finder：win + t\n连接samba：win + k\n常见通用快捷键： 全选：win + a\n复制字符：win + c\n保存：win + s\n粘贴字符：win + v\n退出登陆：win + alt + shift + q\n剪切字符：win + x\n撤销操作：win + z\n中断执行：ctrl + c\n行首：win + ◀︎ / ctrl + a\n行尾：win + ▶︎/ ctrl + e\n页首：win + ▲\n页尾：win + ▼\n输入emoji：ctrl + win + space\n删除选中：win + back\n常见软件快捷键 Typora 一级标题：win + 1\n二级标题：win + 2\n加粗：win + b\n表格：win + t\n斜体：win + i\n代码：ctrl + `\n下划线：win + u\n超链接：win + k\n无需列表：win + alt + u\n有序列表：win + alt + o\n显示大纲：win + shift + l / ctrl + win + 1\n代码块：win + alt + c / 建议还是直接打 ``` 或者 ~~~\n分割线：win + alt + -\n目录生成：[toc]\nSafari / Chrome 开发者模式：win + alt + i\n刷新网页：win + r\n切换标签页：ctrl + tab\n进入/退出 全屏模式：win + ctrl + f\n显示/隐藏 状态栏：win + shift + b\n显示/隐藏 所有历史记录：win + y\n打开收藏栏第一个：win + alt + 1\n打开收藏栏第二个：win + alt + 2\n在侧边栏打开收藏栏：win + ctrl + 1\n在侧边栏打开稍后阅读：win + ctrl + 2\n新建窗口：win + alt + n\n新建无痕窗口：win + shift + n\n新建标签页：win + t / win + n\n关闭标签页：win + w\n退出当前软件：win + q\n打开软件偏好设置：win + ,\n放大视图：win + +\n缩小视图：win + -\n","date":"2021-06-01T20:32:07Z","image":"https://cn.bing.com/th?id=OHR.HinterseeRamsau_EN-CN5297881270_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/hackintosh_laptop_hotkey/","title":"Hackintosh Laptop 常见快捷键"},{"content":"一个有意思的shell变量问题 今天在上班时遇到了一个批量解压zip的需求，由于几十个压缩包的名称一样，chrome下载下来以后就自动给加了编号。并且解压后的文件名也是一样的，只不过里面的文件不一样。\n虽然在Windows下有批量解压缩工具，但是我没有安装。我电脑上只有7zip。既然是批量处理文件，那我就想到了用shell来做这件事。\n首先！我遇到的问题就是！文件名里面有空格，类似**\u0026ldquo;abc xyz.zip\u0026rdquo;**一个文件名被shell捕获成变量以后，在for循环里面会被错误的解析成两个不同的变量，这就导致了代码逻辑上没有问题。你不亲自调试一下，就不知道为什么会报错。\n为此我百度了良久，找到了以下两种解决方案\n解决方案一：修改IFS 我百思不得其解，为什么这么多一毛一样的文章都推荐使用IFS，而且只修改了IFS，而且用完还没有给修改回来。这不是给自己挖坑吗\nIFS的默认值为空白字符（换行符、制表符或者空格）。如果你手动的修改它为别的字符，那后续的代码也就会以该字符为定界符。只能说是简单粗暴，贼鸡儿好用！\n解决方案二：修改文件名 一开始我看到这种方案还很疑惑！我尼玛要是有那修改文件名的功夫我还写代码干啥，哈哈哈哈。然后我看了一下思路，嗯！还是有点道理的。原文见脚本之家。\n大概思路我就放在下面的代码里面讲解吧！\n1 2 3 4 5 6 7 8 allzip=`ls | grep zip | sed \u0026#39;s/ (/(/g\u0026#39;`\t#先用sed把\u0026#39; (\u0026#39;替换成\u0026#39;(\u0026#39;保存到list里面 echo $allzip\t#echo一下,让自己放心一点 for zip in $allzip do filen=`echo $zip | sed \u0026#39;s/(/ (/g\u0026#39;`\t#把存起来的没有空格的变量还原成真正的有空格的文件名 mv \u0026#34;$filen\u0026#34; $zip\t#拿到了真正的文件名,为了后续方便,直接把有空格的文件修改为没有空格的 unzip $zip\t#顺带给它解压一下 done 好了就是这么的简单，也不用修改什么IFS。虽然有一丢丢的绕，但是也是非常容易理解的。你们也看出来了我个人是偏向于第二种方案的，因为修改IFS万一你忘记给改回去，那你下面还有字符串类分割的操作就会度过一段非常蛋疼的调试时光。\n","date":"2021-05-23T02:33:06Z","image":"https://cn.bing.com/th?id=OHR.RollingHills_EN-CN1321221405_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/funny_space_in_shell_script/","title":"一个有意思的shell变量问题"},{"content":"ClangLearn Clang 定义符号常量 在宏中定义符号常量非常简单,格式如下\n1 #define KEYWORD \u0026#34;String\u0026#34; eg:white_check_mark:下面这句废话!啥也没有干就是替换了一句话.\n1 2 3 4 5 6 7 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int main() { printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } Thinking:电脑💻为什么知道字符串常量在哪里该结束呢?万一把后面的代码也 读取为字符串常量了呢?\n其实==Clang==会自动在字符串常量结尾加上一个转义字符\\0来表示读取结束.\nClang 数据类型 graph LR a[数据类型]--\u003eb[基本类型] a[数据类型]--\u003ec[指针类型] a[数据类型]--\u003ee[空白类型] a[数据类型]--\u003ed[构造类型] b--\u003ef(整数类型:int) b--\u003eg(浮点类型:float,double) b--\u003eh(字符类型:char) b--\u003ei(布尔类型:_Bool) d--\u003ej(枚举类型) d--\u003ek(数组类型) d--\u003el(结构类型) d--\u003em(联合类型)signed和unsigned的区别\n一般情况下,signed都表示有正负号的区别。而unsigned表示没有负数,只有正数。\n==1Byte=8bit==\nByte==比特\nClang字符串 声明字符串:char name[number];\n字符串赋值:name[0]=\u0026lsquo;F\u0026rsquo;;\n1 2 3 4 5 6 7 8 9 10 11 12 13 char Gage[10]; Gage[0] = \u0026#39;J\u0026#39;; Gage[1] = \u0026#39;a\u0026#39;; Gage[2] = \u0026#39;v\u0026#39;; Gage[3] = \u0026#39;a\u0026#39;; Gage[4] = \u0026#39;N\u0026#39;; Gage[5] = \u0026#39;o\u0026#39;; Gage[6] = \u0026#39;.\u0026#39;; Gage[7] = \u0026#39;1\u0026#39;; printf(\u0026#34;我想说:%s\\n\u0026#34;, Gage); /**-------------------------**/ char newBee[]={\u0026#39;J\u0026#39;,\u0026#39;a\u0026#39;,\u0026#39;v\u0026#39;,\u0026#39;a\u0026#39;,\u0026#39; \u0026#39;,\u0026#39;N\u0026#39;,\u0026#39;o\u0026#39;,\u0026#39;.\u0026#39;,\u0026#39;1\u0026#39;,\u0026#39;\\0\u0026#39;}; printf(\u0026#34;I think :%s\\n\u0026#34;,newBee ); 或者使用第二种\n1 char battle[]= \u0026#34;我认为Java天下第一\u0026#34;; 需要注意的是:我们需要手动在数组末尾加上\\0来表示读取结束,否则会出现下面的情况\n1 2 3 4 5 6 7 8 9 10 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int main() { printf(\u0026#34;Java天下第一\\n\u0026#34;); char newBee[]={\u0026#39;J\u0026#39;,\u0026#39;a\u0026#39;,\u0026#39;v\u0026#39;,\u0026#39;a\u0026#39;,\u0026#39; \u0026#39;,\u0026#39;N\u0026#39;,\u0026#39;o\u0026#39;,\u0026#39;.\u0026#39;,\u0026#39;1\u0026#39;}; printf(\u0026#34;I think :%s\\n\u0026#34;,newBee ); printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } 上面是源码,下面的是运行结果.\n1 2 3 4 /mnt/d/Clang_test » gcc hello_world.c -o hell \u0026amp;\u0026amp; ./hell frelon@Joker-Lee I think :Java No.1` �5 Written By Frelon O(∩_∩)O 1 char newBee[]={\u0026#39;J\u0026#39;,\u0026#39;a\u0026#39;,\u0026#39;v\u0026#39;,\u0026#39;a\u0026#39;,\u0026#39; \u0026#39;,\u0026#39;N\u0026#39;,\u0026#39;o\u0026#39;,\u0026#39;.\u0026#39;,\u0026#39;1\u0026#39;,\u0026#39;\\0\u0026#39;}; 改成下面这样的就不会报错了\nClang if判断 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 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int main() { int a; printf(\u0026#34;How old are u :\u0026#34; ); scanf(\u0026#34;%d\u0026#34;,\u0026amp;a); if(a\u0026lt;12) printf(\u0026#34;Out of here\\n\u0026#34;); else printf(\u0026#34;Please come in\\n\u0026#34;); printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } /** ---------------------------- **/ #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int main() { int a; printf(\u0026#34;How old are u :\u0026#34; ); scanf(\u0026#34;%d\u0026#34;,\u0026amp;a); if(a\u0026lt;12) { printf(\u0026#34;Out of here\\n\u0026#34;); } else { printf(\u0026#34;Please come in\\n\u0026#34;); } printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } 可以看到我们可以用这种最简便的方式来写if else,当然下面的那种才是我们提倡的方式,因为方便改Bug🤣。\nClang switch判断 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int main() { int a = 40; switch(a){ case 10 : printf(\u0026#34;10\\n\u0026#34;);break; case 20 : printf(\u0026#34;20\\n\u0026#34;);break; default : printf(\u0026#34;Error!\\n\u0026#34;); } printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } 可以看出来switch很简单,但是实际编程中我们用for更多一点。\nClang 找到素数 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 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; void main() { int gol [100]; for (int se = 0;se\u0026lt;=99;se++){ gol[se] = (se+1); } for (int sup = 0;gol[sup]\u0026lt;=100;sup++){ if (gol[sup]==1||gol[sup]==2) { printf(\u0026#34;%d是素数\\n\u0026#34;,gol[sup]); }else{ int yupe; for (int i = 2; i \u0026lt; gol[sup]; i++) { yupe = gol[sup] % i; if (yupe == 0){ printf(\u0026#34;%d不是素数\\n\u0026#34;,gol[sup] ); break; } if(yupe != 0 \u0026amp;\u0026amp; (gol[sup]-1) == i){ printf(\u0026#34;%d是素数\\n\u0026#34;,gol[sup] ); } } } } printf(\u0026#34;%s\\n\u0026#34;, FRELON); } 这个是上面知识的综合运用\nClang指针 指针：一般是一个抽象概念,用于指向内存中的地址\n指针变量：一般是☞指向内存中的某个地址值\n定义指针变量：类型名 *指针变量名;\neg：int *num;\nclang指针取地址运算符:\u0026amp;\nclang指针取指针变量数据值:*\neg:\n1 2 char *str = \u0026amp;a;//取内存中的地址的值 printf(\u0026#34;%c \\n\u0026#34;,*str);//取指针变量指向的数据的值 Clang🈲给未初始化的指针变量赋值! Clang给未初始化的指针变量赋值的做法是非常危险的 ! 并且也是不能这么写的,因为该指针没有初始化,系统就不知道这个指针应该指向内存中的具体哪一个值。如果这个时候给它赋值,就相当于在内存中随便找一个地址给其赋值,如果我们修改了内存中系统关键的数据,那就有可能导致系统崩溃,又或者我们随机赋值给了别的程序,也会导致别的程序出现问题,同时程序本身也不能给正确的数据赋值,也可能会出现错误\n1 2 3 4 5 6 7 #include \u0026lt;stdio.h\u0026gt; int main { int *ios;//这种不初始化的行为非常危险 int *android = null;//建议所有的不知道目的的指针都这样，指向null。 *ios = 123; return 0; } Clang指针与数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; void main() { char ui[] = \u0026#34;Hello_world\u0026#34;; char *jk = ui; printf(\u0026#34;ui[0] = %p\\n\u0026#34;, \u0026amp;ui[0]); printf(\u0026#34;ui = %p\\n\u0026#34;, \u0026amp;ui);//整个字符串数组的地址就是数组第一个元素的地址 printf(\u0026#34;ui[1] = %p\\n\u0026#34;, \u0026amp;ui[1]); printf(\u0026#34;p(ui+1) = %p\\n\u0026#34;, (jk+1));//按照上面的思路来,那么数组第二个元素应该是数组的指针变量+1 printf(\u0026#34;%s\\n\u0026#34;, FRELON); } 1 2 3 4 5 6 /mnt/d/Clang_test » gcc hello_world.c -o hell \u0026amp;\u0026amp; ./hell ui[0] = 0x7fffcd7b163c ui = 0x7fffcd7b163c ui[1] = 0x7fffcd7b163d p(ui+1) = 0x7fffcd7b163d Written By Frelon O(∩_∩)O 上面就是clang指针与字符串数组,下面我们再看看clang指针与数字数组\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; void main() { int osi [] = {1,2,3,4,5,6,7,8,9}; int *sdCard = osi; printf(\u0026#34;osi[0] 的指针: %p\\n\u0026#34; , \u0026amp;osi[0]); printf(\u0026#34;osi 的指针: %p\\n\u0026#34; , \u0026amp;osi); printf(\u0026#34;sdCard 的指针: %p\\n\\n\u0026#34;, sdCard); printf(\u0026#34;osi[1] 的指针: %p\\n\u0026#34; , \u0026amp;osi[1]); printf(\u0026#34;sdCard+1 的指针: %p\\n\u0026#34; , sdCard+1); printf(\u0026#34;%s\\n\u0026#34;, FRELON); } 1 2 3 4 5 6 7 8 /mnt/d/Clang_test » gcc hello_world.c -o hell \u0026amp;\u0026amp; ./hell osi[0] 的指针: 0x7ffff6824620 osi 的指针: 0x7ffff6824620 sdCard 的指针: 0x7ffff6824620 osi[1] 的指针: 0x7ffff6824624 sdCard+1 的指针: 0x7ffff6824624 Written By Frelon O(∩_∩)O Tips：使用scanf的时候，大家有没有发现，当我们需要输入单个int，char，scanf(\u0026quot;%d\u0026quot;,\u0026amp;sis)。我们需要用到指针来指向该变量内存中的地址，但是当我们要获取字符串数组的时候，我们可以scanf(\u0026quot;%s\u0026quot;,name) 我们就不需要给数组也来个**\u0026amp;**取它的内存中的地址，想想看这个是为什么？\n在这里我们就可以推测出，数组名可能就是一个☝️内存中的地址信息，而且还是第一个元素的地址\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int main() { int lolita [] = {1,2,3,4,5,6,7,8,9}; int *po = lolita; printf(\u0026#34;lolita = %p\\n\u0026#34;, lolita); printf(\u0026#34;lolita[0] = %p\\n\u0026#34;,\u0026amp;lolita[0] ); printf(\u0026#34;po = %p\\n\u0026#34;, po); printf(\u0026#34;*po = %d\\n\\n\u0026#34;, *po); printf(\u0026#34;lolita[1] = %d\\n\u0026#34;,lolita[1]); printf(\u0026#34;*(po+1) = %d\\n\u0026#34;, *(po+1)); printf(\u0026#34;lolita[1] = %p \\n\u0026#34;,\u0026amp;lolita[1]); printf(\u0026#34;po+1 = %p\\n\u0026#34;, (po+1)); printf(\u0026#34;\\nlolita[0] = %p\\nlolita[1] = %p\\nlolita[2] = %p\\nlolita[3] = %p\\nlolita[4] = %p\\nlolita[5] = %p\\n\u0026#34;,\u0026amp;lolita[0],\u0026amp;lolita[1],\u0026amp;lolita[2],\u0026amp;lolita[3],\u0026amp;lolita[4],\u0026amp;lolita[5]); printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 lolita = 0x7ffeed7d89b0 lolita[0] = 0x7ffeed7d89b0 po = 0x7ffeed7d89b0 *po = 1 lolita[1] = 2 *(po+1) = 2 lolita[1] = 0x7ffeed7d89b4 po+1 = 0x7ffeed7d89b4 lolita[0] = 0x7ffeed7d89b0 lolita[1] = 0x7ffeed7d89b4 lolita[2] = 0x7ffeed7d89b8 lolita[3] = 0x7ffeed7d89bc lolita[4] = 0x7ffeed7d89c0 lolita[5] = 0x7ffeed7d89c4 Written By Frelon O(∩_∩)O 上面的数据可以看出，同一个数组内的元素在内存中确实是连续排列的，并且一个int类型的元素占用了4B（字节）内存空间，合情合理！\n再思考一个问题吼！为什么我们指针加一，却可以指向下一个数组的元素呢？不应该是0x7ffeed7d89c0 + 1 = 0x7ffeed7d89c1这样吗？为什么0x7ffeed7d89c0 + 1 = 0x7ffeed7d89c4？\n因为我们之前定义过指针的类型为int，所以指针的加一是根据我们定义的数据类型的长度来加的，而不是直接的数学意义上的在原数基础上相加1，这就是指针的迷人之处，它可以完美的和数组结合起来，\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int main() { char *log = \u0026#34;jokemetopsdfsdf\u0026#34;; int ok = strlen(log); for(int er = 0;er \u0026lt; ok ; er++){ printf(\u0026#34;%c\u0026#34;, *(log+er)); } printf(\u0026#34;\\n\u0026#34;); printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } 可以看到可以用指针来直接定义一个数组，然后用指针加一的方式来访问各个元素。\nTips：数组名是一个地址，而指针是一个左值（不可以被修改）\n指针数组和数组指针 int *p[5] = 指针数组\nint(*p)[5] = 数组指针\n1 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 45 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int main() { int qwe [3][3] = {0}; // int *rt = \u0026amp;qwe int k = 0; for(int er = 0;er \u0026lt; 3; er++ ){ for (int i = 0; i \u0026lt; 3; i++) { qwe[er][i] = ++k; } } printf(\u0026#34;qwe = %p\\n\u0026#34;,qwe); printf(\u0026#34;*qwe = %p\\n\u0026#34;,*qwe ); printf(\u0026#34;**qwe = %d\\n\\n\u0026#34;,**qwe ); printf(\u0026#34;qwe + 1 = %p\\n\u0026#34;,(qwe+1) ); printf(\u0026#34;*(qwe + 1) = %p\\n\u0026#34;,*(qwe+1) ); printf(\u0026#34;**(qwe + 1) = %d\\n\\n\u0026#34;,**(qwe+1) ); printf(\u0026#34;*(qwe + 1)+1 = %p\\n\u0026#34;,*(qwe+1)+1 ); printf(\u0026#34;*(*(qwe+1)+1) = %d\\n\u0026#34;,*(*(qwe+1)+1) ); printf(\u0026#34;*((qwe + 1)+1) = %p\\n\u0026#34;,*((qwe+1)+1) ); printf(\u0026#34;**((qwe + 1)+1) = %d\\n\\n\u0026#34;,**((qwe+1)+1) ); printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } /** ------------------------------------------------- **/ qwe = 0x7ffee1b399c0 *qwe = 0x7ffee1b399c0 **qwe = 1 qwe + 1 = 0x7ffee1b399cc *(qwe + 1) = 0x7ffee1b399cc **(qwe + 1) = 4 *(qwe + 1)+1 = 0x7ffee1b399d0 *(*(qwe+1)+1) = 5 *((qwe + 1)+1) = 0x7ffee1b399d8 **((qwe + 1)+1) = 7 Written By Frelon O(∩_∩)O 注意⚠️区分 *(point+1)+1和*((point+1)+1) = ((point+1)+1) 的区别\nVoid Point void point 并不是空指针，它表示还没有确定类型的指针，你可以将任何类型的指针指向void point，看操作\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int main() { int yyu = 4; char ui[] = \u0026#34;Winner\u0026#34;; void *qw; qw = \u0026amp;yyu; printf(\u0026#34;%p \\n%p\\n\u0026#34;,qw,\u0026amp;yyu ); printf(\u0026#34;*wq = %d\\n\u0026#34;,*(int *)qw );//(int *)表示的是强制类型转换，给void转换成 int qw = \u0026amp;ui; printf(\u0026#34;%p \\n%p\\n\u0026#34;,qw,\u0026amp;ui ); printf(\u0026#34;*qw = %s\\n\u0026#34;,(char *)qw );//因为字符串比较特殊，所以不用解引用，也可以直接通过指针直接读取，直到读取到‘\\0’ printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } point to point P2P:指向指针的指针，其实也非常简单，就是多套了一层娃，要想使用就需要两次解引用，即两个**看操作\n1 2 3 4 5 6 7 8 9 10 11 12 13 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int main() { int ios = 4; int *rt = \u0026amp;ios; int **goo = \u0026amp;rt; printf(\u0026#34;rt = %p\\n\u0026#34;, rt); printf(\u0026#34;goo = %p\\n*goo = %p\\n**goo = %d \\n\u0026#34;, goo,*goo,**goo); printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } 指向常量的指针 指向常量的指针：可以修改为指向不同的变量（常量），可以通过解引用来读取指针指向的数据 ，但是不能修改\n解读：指向 常量（被const修饰过的基本数据类型）的 指针（指针没有被const修饰，可以修改）\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int main() { const int pr = 3; int ps = 1024; const int *ae = \u0026amp;pr; printf(\u0026#34;Now ae = %d\\n\u0026#34;,*ae ); ae = \u0026amp;ps; printf(\u0026#34;Now ae = %d\\n\u0026#34;,*ae ); printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } 指向非常量的常量指针：上面的指针可以被修改指向，而常量指针就是利用cons使指针不可以被修改指向\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int main() { const int vmware = 999; int exsi = 1999; int * const HyperV = \u0026amp;exsi; printf(\u0026#34;HyperV = %d\\n\u0026#34;, *HyperV); *HyperV = vmware; printf(\u0026#34;HyperV = %d\\n\u0026#34;, *HyperV); printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } 性质： 虽然不可以修改const修饰过的指针指向，但是可以通过解引用来修改指针指向的值\n指向常量的常量指针\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int main() { const int vmware = 999; int exsi = 1999; const int * const HyperV = \u0026amp;exsi; printf(\u0026#34;HyperV = %d\\n\u0026#34;, *HyperV); *HyperV = vmware; printf(\u0026#34;HyperV = %d\\n\u0026#34;, *HyperV); printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } 指向常量的常量指针就是在指针的基本数据类型前面加上const，让指针解引用的方式修改不了数据。\nClang函数 函数的出现其目的就是为了提高代码的复用性，让我们可以用更少的 代码量完成更多的任务。\n1 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 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int main() { int a=12,b=13,c=14,d=13,e=15,f=15,g=17,h=13,i=12,j=10; if(a\u0026gt;b){ printf(\u0026#34;%d\\n\u0026#34;,a); }else{ printf(\u0026#34;%d\\n\u0026#34;,b); } if(c\u0026gt;d){ printf(\u0026#34;%d\\n\u0026#34;,c); }else{ printf(\u0026#34;%d\\n\u0026#34;,d); } if(e\u0026gt;f){ printf(\u0026#34;%d\\n\u0026#34;,e); }else{ printf(\u0026#34;%d\\n\u0026#34;,f); } if(g\u0026gt;h){ printf(\u0026#34;%d\\n\u0026#34;,g); }else{ printf(\u0026#34;%d\\n\u0026#34;,h); } if(i\u0026gt;j){ printf(\u0026#34;%d\\n\u0026#34;,i); }else{ printf(\u0026#34;%d\\n\u0026#34;,j); } printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } 可以看到，为了一件简单的小事，我们如果不用函数需要多次写重复的代码，降低了代码的复用性，也给我们的后期维护带来了很大的难题，当我们用上函数以后，如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int whoBigger(int a,int b){ if (a\u0026gt;b) { printf(\u0026#34;%d\\n\u0026#34;,a); }else{ printf(\u0026#34;%d\\n\u0026#34;,b); } return 0; } int main() { int a=12,b=13,c=14,d=13,e=15,f=15,g=17,h=13,i=12,j=10; whoBigger(a,b); whoBigger(c,d); whoBigger(e,f); whoBigger(g,h); whoBigger(i,j); printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } 可以看得出来，有了函数以后代码量明显的减少了很多\n函数传递指针参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; void vbs(int *bbc){ printf(\u0026#34;size of bbc = %lu\\n\u0026#34;,sizeof(bbc) ); printf(\u0026#34;bbc+2 =%d \\n\u0026#34;,*(bbc+2) ); *(bbc+2) = 7; printf(\u0026#34;bbc+2 =%d \\n\u0026#34;,*(bbc+2) ); } int main() { int we[] = {1,2,3,4,5,6,7,8,9,0,1,2,3,4}; printf(\u0026#34;sizeof we = %lu\\n\u0026#34;,sizeof(we) ); vbs(we); printf(\u0026#34;we[2] = %d\\n\u0026#34;,we[2]); printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } 可以看到与我们别的语言的函数不同，如果函数的参数是指针的话，那么无论在main函数还是自定义函数，修改指针就会直接修改内存中的数据。别的语言例Java可能需要先定义一个全局变量，然后在自定义函数里操作这个全局变量，在我们Clang里，就可以直接利用指针参数来达到自定义函数内部对全局参数的修改。\n可变参函数 其实可变参数函数就是利用Clang的一个头文件 #include \u0026lt;stdarg.h\u0026gt; 实现的,其参数包括：va_list,va_start,va_arg,va_end\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdarg.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int sum(int n,...){//...就表示有未知个参数 int all = 0; va_list vl;//定义参数列表 va_start(vl , n);//将上面的列表传入va_start，再加上一个参数n for (int i = 0; i \u0026lt; n; i++) { all+=va_arg(vl,int);//va_arg用于获取参数列表里面的各个值（就是我们传入的未知个参数） } va_end(vl);//然后还要关闭参数列表 return all; } int main() { int bolose = sum(6,12,34,14,456,46,897); printf(\u0026#34;%d \\n\u0026#34;,bolose ); printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } 指针函数 返回指针的函数，本质是函数\n返回数据类型 + * + 函数名 + (变量类型1,…);\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; char * cre(char op){ switch(op){ case \u0026#39;a\u0026#39;:return \u0026#34;Apple\u0026#34;; case \u0026#39;b\u0026#39;:return \u0026#34;baidu.com\u0026#34;; case \u0026#39;c\u0026#39;:return \u0026#34;cdn\u0026#34;; default:return \u0026#34;google.com\u0026#34;; } } int main() { char io_i; printf(\u0026#34;Please input word:\u0026#34;); scanf(\u0026#34;%c\u0026#34;,\u0026amp;io_i); char * poj = cre(io_i); printf(\u0026#34;%s\\n\u0026#34;,poj ); printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } Tips：不要在函数里面返回局部变量的指针，因为局部函数的数据只在局部函数里面可以使用，不可以在主函数里面使用。\n函数指针 顾名思义函数指针就是指向函数的指针，其本质还是一个指针\n指针函数：int *p ( );\n函数指针：int (*p) ( );\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int squ(int num){ return num*num; } int main() { int num; int (*piute)(int); printf(\u0026#34;Please input a number:\u0026#34;); scanf(\u0026#34;%d\u0026#34;,\u0026amp;num); piute = squ;//piute = \u0026amp;squ;也是没有问题的 printf(\u0026#34;The result is %d\\n\u0026#34;,(*piute)(num) );//piute(num)也是OK的 printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } 函数指针作为参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int add (int numb1, int numb2){ return numb1+numb2; } int sub (int numb1 , int numb2){ return numb1-numb2; } int comp(int (*weic)(int,int),int numb1,int numb2){//该函数共有三个参数，一个是函数指针（该指针要求需要两个int类型的参数，还需要一个int类型的返回值），其余两个参数是int类型即可。 return (*weic)(numb1,numb2); } int main() { printf(\u0026#34;%d\\n\u0026#34;, comp(add,3,5)); printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } 其实关键点就是comp，它有一个函数指针作为参数，该函数指针指向的函数需要两个参数，这两个参数传给comp，comp在计算时再调用指针函数，将comp的两个int类型参数传给指针函数，指针函数刚刚好接收这两个参数进行运算。其实过程非常简单，只不过是套了两层娃。\n下面是一个更加简单的实例，你要是还看不懂，我也没办法🌝。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; char whobig(int abc,int def){ if (abc\u0026gt;=def) { return \u0026#39;Y\u0026#39;; }else{ return \u0026#39;N\u0026#39;; } } char javafirst(char (*yep)(int,int),int aa,int bb){ return (*yep)(aa,bb); } int main() { printf(\u0026#34;%c\\n\u0026#34;,javafirst(whobig,1,13)); printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } 函数指针作为返回值 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 45 46 47 48 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; char whobig(int abc,int def){ if (abc\u0026gt;=def) { printf(\u0026#34;I\u0026#39;m whobig\\n\u0026#34;); return \u0026#39;Y\u0026#39;; }else{ printf(\u0026#34;I\u0026#39;m whobig\\n\u0026#34;); return \u0026#39;N\u0026#39;; } } char whosmall(int abc,int def){ if (abc\u0026lt;def) { printf(\u0026#34;I\u0026#39;m whosmall\\n\u0026#34;); return \u0026#39;N\u0026#39;; }else{ printf(\u0026#34;I\u0026#39;m whosmall\\n\u0026#34;); return \u0026#39;Y\u0026#39;; } } char (* noclang(int ios))(){//总体来看，这是一个指针函数，其返回值为指针类型。其结构为：char * noclang (){} if (ios == 0) { return whobig;\t}else{ return whosmall; } } char javafirst(char (*yep)(int,int),int aa,int bb){ return (*yep)(aa,bb); } int main() { int a = 1; int b = 10; char (*ok) ();//声明一个函数指针ok。 ok = noclang(0);//将函数noclang()的地址值赋给ok。 printf(\u0026#34;%d 大于 %d : %c\\n\u0026#34;,a,b,javafirst(ok,a,b)); printf(\u0026#34;%s\\n\u0026#34;, FRELON); return 0; } 一句话区分指针函数和函数指针：谁*前面有括号谁就是函数指针。\nTips⚠️：\n函数指针：重点在指针，表示它是一个指针，它指向的是一个函数。eg: int (*fun)();\n指针函数：重点在函数，表示它是一个函数，它的返回值是指针。　eg: int* fun();\n数组指针：重点在指针，表示它是一个指针，它指向的是一个数组。int (*fun)[8];\n指针数组：重点在数组，表示它是一个数组，它包含的元素是指针　itn* fun[8];\nClang函数作用域\u0026amp;\u0026amp;生存期 Clang存储类型 clang的存储类型有：auto 、register、static、extern\n一般情况下我们所有的变量存储类型都为auto，\n寄存器变量：register，并不是说定义为寄存器变量就一定会被CPU放入寄存器，而是有可能会被放入寄存器，如果没有被放入寄存器那么这些变量就会自动变为auto变量，如果一个变量被定义为register变量以后，就无法通过取址操作（\u0026amp;）来获取该变量的地址\nstatic：静态变量，其中静态全局变量和静态局部变量的生存期相同，他们都是随着函数执行结束才会被销毁。\nextern：extern 存储类用于提供一个全局变量的引用，全局变量对所有的程序文件都是可见的。当您使用 extern 时，对于无法初始化的变量，会把变量名指向一个之前定义过的存储位置。当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时，可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。可以这么理解，extern 是用来在另一个文件中声明一个全局变量或函数。extern 修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候。\n[关于Clang extern的一个困惑](关于Clang extern的一个困惑.md)\nClang递归 话不多说直接上代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int reuse(int iso){ printf(\u0026#34;%d\\n\u0026#34;,iso); iso+=1; if(iso == 17){ printf(\u0026#34;GameOver\\n\u0026#34;); return 0; }else{ reuse(iso); } return iso; } int main(){ int oo = reuse(1); printf(\u0026#34;%d\\n\u0026#34;,oo ); printf(\u0026#34;%s\\n\u0026#34;,FRELON); return 0; } 可以看到这个递归就是在函数的内部不停的继续调用函数本身，这不就是套娃🪆嘛！\n但是套娃也需要注意⚠️：因为一定要有停止条件，否则一直在执行就会无休止的浪费内存（ps：并不会无休止的浪费，因为到达一定次数以后就会造成栈内存溢出，代码就会被终止）\nClang hanoi总结 在学习递归的时候接触到了汉诺塔这个小游戏。发现真的是烧脑子，被这个小游戏虐的体无完肤，就像是一年级小朋友做高三题目，根本无处下牙。虽然知道用递归的方法来解决问题，但是就不知道代码该怎么写。看了网上的教程，知道了代码如何写的了，但是还是不明白为什么要这样写，大部分都是只说出了原理，像我这种比较笨的人，听懂了原理依然很蒙。没办法就只能用最原始的办法，用最小的数字慢慢的往上套，来一步一步的理解。\n先上代码:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; void hano(int n,char x,char y,char z){ if(n == 1){ printf(\u0026#34;%c --\u0026gt; %c\\n\u0026#34;,x,z); }else{ hano(n-1,x,z,y); printf(\u0026#34;%c --\u0026gt; %c\\n\u0026#34;,n,x,z); hano(n-1,y,x,z); } } int main (){ hano(3,\u0026#39;A\u0026#39;,\u0026#39;B\u0026#39;,\u0026#39;C\u0026#39;); return 0; } 经测试代码没有问题，但是我有问题啊！为什么要\n​\thano(n-1,x,z,y); ​\tprintf(\u0026quot;%c --\u0026gt; %c\\n\u0026quot;,n,x,z); ​\thano(n-1,y,x,z);\n而且为什么是hanoi（n-1，x，z，y）而不是xyz。真的很蒙，然后用笔纸慢慢的计算，发现这样写才是正确🙆‍♂️的做法，因为我也不知道怎么解释，但是就这样是正确的，然后我又发现，hanoi这个函数，在n为奇数或者是偶数的时候选择的路径并不一样，以 n = 3 \u0026amp;\u0026amp; n = 4为例\n1 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 JokerMBP L-🌵-轩-🍂-X ~/Clang_test 580 ◯ : gcc hello_world.c -o hell \u0026amp;\u0026amp; ./hell [~] 第一步: A --\u0026gt; C 第2 步: A --\u0026gt; B 第一步: C --\u0026gt; B ​`````````````````` 第3 步: A --\u0026gt; C 第一步: B --\u0026gt; A 第2 步: B --\u0026gt; C 第一步: A --\u0026gt; C ​`````````````````` ​`````````````````` #上面👆是n=3 JokerMBP L-🌵-轩-🍂-X ~/Clang_test 580 ◯ : gcc hello_world.c -o hell \u0026amp;\u0026amp; ./hell [~] 第一步: A --\u0026gt; B 第2 步: A --\u0026gt; C 第一步: B --\u0026gt; C ​`````````````````` 第3 步: A --\u0026gt; B 第一步: C --\u0026gt; A 第2 步: C --\u0026gt; B 第一步: A --\u0026gt; B ​`````````````````` ​`````````````````` 第4 步: A --\u0026gt; C 第一步: B --\u0026gt; C 第2 步: B --\u0026gt; A 第一步: C --\u0026gt; A ​`````````````````` 第3 步: B --\u0026gt; C 第一步: A --\u0026gt; B 第2 步: A --\u0026gt; C 第一步: B --\u0026gt; C ​`````````````````` ​`````````````````` ​`````````````````` #上面👆是n=4 JokerMBP L-🌵-轩-🍂-X ~/Clang_test 580 ◯ : 当n为奇数时，A \u0026ndash;\u0026gt; C\n当n为偶数时，A \u0026ndash;\u0026gt; B\n虽然这是一个小发现，但是对我理解这个函数有很大的帮助，因为我之前一直搞不懂 printf(\u0026quot;%c --\u0026gt; %c\\n\u0026quot;,n,x,z);实质上它的输出是上一步传过来的x，y，z。而并非单纯的为x，y，z本身。可以利用下面的图片理解！\nClang内存管理（stdlib.h） void * malloc(size_t size);申请内存 malloc函数向系统申请一个size大小的内存空间，并返回该块空间的指针(void类型的指针，方便我们转化)。注意，这里申请的内存位于内存的堆里面，如果不手动释放该内存，那么他就会一直被占用，直到程序被关闭。栈内存一般都是存放局部变量的，某个方法（函数）被调用才会进栈空间，调用结束就会自动被清理掉。\n1 2 3 4 5 6 7 8 9 10 11 12 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int main (){ int * p = malloc(sizeof(int)); *p = 99; printf(\u0026#34;%d\\n\u0026#34;,*p); free(p); printf(\u0026#34;%s\\n\u0026#34;,FRELON); return 0; } void free (void * p); 释放内存 接收一个指针，指向动态内存的区域。然后释放该内存，必须是由：malloc，calloc，realloc申请的内存，否则无效，还有一点需要注意，就是不能将这些动态申请到的指针重新指向一个内存地址，这样会导致内存地址的指针丢失，也就无法再释放该内存地址。\n还要注意内存泄漏，如果没有即时释放掉内存，就有可能导致程序在运行时内存占用越来越多。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int main (){ int * p = malloc(8 * sizeof(int)); for(int er = 1; er \u0026lt;= 8 ;er++ ){ *p = er; printf(\u0026#34;%d\\n\u0026#34;,*p); p +=1; } free(p); printf(\u0026#34;%s\\n\u0026#34;,FRELON); return 0; } malloc申请定长内存，可以用于数组。\ncalloc malloc申请的内存并没有初始化，所以申请到的内存都是一些混乱的数据\n我们可以使用以下函数来快速初始化：（这些方法依赖，string.h）\nmemset 使用常量字节填充内存空间 mencpy 拷贝内存空间的数据 memmove 移动内存空间到另一个内存空间 memcmp 比较内存空间 memchr 在内存空间搜索字符 所以为了让在申请内存后自动被初始化，就有了**calloc( )**它和malloc是一样的用法，只是会自动初始化而已。\nvoid * realloc(* point , size ); 用于修改malloc｜calloc申请内存空间的大小，如果修改后的空间大于原空间，那么一切没问题，但是如果小于原空间，会造成数据丢失。该函数会返回一个新的指针，并不会在原来的基础上进行修改。但是原空间也会被自动释放。还有一点需要注意，注意内存溢出。\nClang宏定义 不带参数的宏定义，一般都是只做替换操作的。并且在定义时也会全部大写。末尾不需要分号；\n宏定义的作用域是从定义的那一步到整个程序结束，但是可以随时用 #undef来终止宏定义，宏定义可以套娃。\n1 2 3 4 5 6 7 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; int main (){ printf(\u0026#34;%s\\n\u0026#34;,FRELON); return 0; } 这就是一个最简单的宏定义程序；\nClang结构体声明 struct name{\n结构体1；\n结构体2；\n。。。\n}；\n1 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 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; struct Web{ char siteUrl[128]; char siteName[20]; int isPub; int isFree;\t} web; void movlop(char * a,char b [],int c){ for(int i = 0; i\u0026lt;= c;i++){ *(a+i) = b[i]; } } int main (){ char url [] = \u0026#34;https://jokeme.top\u0026#34;; char name [] = \u0026#34;Jokeme Blog\u0026#34;; movlop(web.siteUrl,url,sizeof(url)); movlop(web.siteName,name,sizeof(name)); web.isPub = 1; web.isFree = 1; printf(\u0026#34;网站url:%s\\n网站名称:%s\\n可否访问:%d\\n是否免费:%d\\n\u0026#34;,web.siteUrl,web.siteName,web.isPub,web.isFree ); printf(\u0026#34;%s\\n\u0026#34;,FRELON); return 0; } 结构体的初始化非常的简单，可以直接在结构体后面写\n还有就是结构体可以嵌套🪆，嵌套后的初始化也简单，看操作：\n1 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 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; struct Date { int year; int month; int day; } date; struct Web { char siteUrl[128]; char siteName[20]; struct Date date; int isPub; int isFree;\t} web = { \u0026#34;https://jokeme.top\u0026#34;, \u0026#34;Jokeme Blog\u0026#34;, {2019,11,23}, 1, 1 }; int main (){ printf(\u0026#34;网站地址:%s\\n网站名称:%s\\n成立日期:%d-%d-%d\\n可否访问:%d\\n是否免费:%d\\n\u0026#34;,web.siteUrl,web.siteName,web.date.year,web.date.month,web.date.day,web.isPub,web.isFree ); printf(\u0026#34;%s\\n\u0026#34;,FRELON); return 0; } 结构体数组 声明方式一：在定义期间声明\n1 2 3 struct 结构体名 { ... ...; } 数组名[长度]; 声明方式二：先声明一个结构体，在以此为基础定义一个结构体数组\n1 2 3 4 struct 结构体名 { ... ...; }; struct 结构体名 数组名[长度]; 结构体指针 众所周知，有数组的地方就有指针的存在，结构体指针的定义\n1 struct Web * pin; 注意！结构体的变量名并不是指针，所以在对结构体指针操作时需要自己取址。\n通过指针来访问结构体成员有两种方法：\n1: (*结构体指针).成员名\n2: 结构体指针 -\u0026gt; 成员名\n可以看得出来，第二种更加简便\n1 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 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; struct Date { int year; int month; int day; } date; struct Web { char siteUrl[128]; char siteName[20]; struct Date date; int isPub; int isFree;\t} web = { \u0026#34;https://jokeme.top\u0026#34;, \u0026#34;Jokeme Blog\u0026#34;, {2019,11,23}, 1, 1 }; int main (){ struct Web * pin = \u0026amp;web; printf(\u0026#34;成立年份：%d\\n\u0026#34;,pin-\u0026gt;date.year); printf(\u0026#34;%s\\n\u0026#34;,FRELON); return 0; } 说明两个相同类型的结构体是可以直接进行赋值的\n1 2 3 4 5 6 7 8 9 struct Darwin{ int a; int b; } x , y; x.a = 1; x.b = 2; y = x; //这种操作是可以的。 把结构体作为参数传递 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 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; struct Date { int year; int month; int day; } date; struct Web { char siteUrl[128]; char siteName[20]; struct Date date; int isPub; int isFree;\t} web = { \u0026#34;https://jokeme.top\u0026#34;, \u0026#34;Jokeme Blog\u0026#34;, {2019,11,23}, 1, 1 },wb2; struct Web ptWeb(struct Web web){ printf(\u0026#34;网站地址:%s\\n网站名称:%s\\n成立日期:%d-%d-%d\\n可否访问:%d\\n是否免费:%d\\n\u0026#34;,web.siteUrl,web.siteName,web.date.year,web.date.month,web.date.day,web.isPub,web.isFree ); return web; } int main (){ wb2 = ptWeb(web); putchar(\u0026#39;\\n\u0026#39;); ptWeb(wb2); printf(\u0026#34;%s\\n\u0026#34;,FRELON); return 0; } 把结构体指针作为参数传递 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 #include \u0026lt;stdio.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; struct Date { int year; int month; int day; } date; struct Web { char siteUrl[128]; char siteName[20]; struct Date date; int isPub; int isFree;\t} web = { \u0026#34;https://jokeme.top\u0026#34;, \u0026#34;Jokeme Blog\u0026#34;, {2019,11,23}, 1, 1 },wb2; struct Web ptWeb(struct Web * web){ printf(\u0026#34;网站地址:%s\\n网站名称:%s\\n成立日期:%d-%d-%d\\n可否访问:%d\\n是否免费:%d\\n\u0026#34;,web-\u0026gt;siteUrl,web-\u0026gt;siteName,web-\u0026gt;date.year,web-\u0026gt;date.month,web-\u0026gt;date.day,web-\u0026gt;isPub,web-\u0026gt;isFree ); return *web; } int main (){ wb2 = ptWeb(\u0026amp;web); putchar(\u0026#39;\\n\u0026#39;); ptWeb(\u0026amp;wb2); printf(\u0026#34;%s\\n\u0026#34;,FRELON); return 0; } 可以看到其实把结构体指针作为参数传递其实更能体现clang语言的特性「运行效率高」，因为传递一个结构体占用的内存远远比一个几字节的指针占用的内存多，所以作为程序员我们尽量选择运行效率高的那种方式来实现功能。\n动态申请结构体 可以使用malloc为结构体动态申请内存空间，此时结构体就在堆内存中\nTen = malloc(sizeof(struct Web));\nClang链表 链表有很多种，单链表，多链表，循环链表，块状链表\n链表结构为:\ngraph LR A[结构体\\\\指针部分]--\u003eB[结构体\\\\指针部分]--\u003eC[...]--\u003eD[结构体部分\\\\指针部分]--\u003eE[null]可以看出链表在内存中并不一定是连续的，他们是通过指针来寻找到下一部分链表的。可以看的出来链表就是由多个有指针的结构体构成的。\n下面就是链表的简单操作，对链表的搜索。\n1 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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #define FRELON \u0026#34;Written By Frelon O(∩_∩)O\u0026#34; struct Date { int year; int month; int day; struct Date * ndate; }; void printDate(struct Date * nw){ printf(\u0026#34; %p\\n\u0026#34;,nw); printf(\u0026#34;year:%d\\nmonth:%d\\nday:%d\\nndate:%p\\n\\n\u0026#34;,nw-\u0026gt;year,nw-\u0026gt;month,nw-\u0026gt;day,nw-\u0026gt;ndate); } void searchDay(struct Date * nd,int target){ while(nd!=NULL){ if (nd-\u0026gt;month==target) {\tprintf(\u0026#34;year:%d\\nmonth:%d\\nday:%d\\nndate:%p\\n\\n\u0026#34;,nd-\u0026gt;year,nd-\u0026gt;month,nd-\u0026gt;day,nd-\u0026gt;ndate); nd=nd-\u0026gt;ndate; }else{ nd=nd-\u0026gt;ndate; printf(\u0026#34;Soryy ! Not found\\n\\n\u0026#34;); } } } void setdate(struct Date * dt,struct Date * nt){ if( (dt-\u0026gt;year) == 0){ dt-\u0026gt;year = 2021; dt-\u0026gt;month = 1; dt-\u0026gt;day = 4; nt-\u0026gt;year = 2021; nt-\u0026gt;month = 2; nt-\u0026gt;day = 8; } if (dt-\u0026gt;ndate == NULL) { dt-\u0026gt;ndate = nt; } } int main (){ struct Date * nowDate = malloc(sizeof(struct Date)); struct Date * nowdat1 = malloc(sizeof(struct Date)); struct Date * nowdat2 = malloc(sizeof(struct Date)); struct Date * nowdat3 = malloc(sizeof(struct Date)); setdate(nowDate,nowdat1); setdate(nowdat1,nowdat2); setdate(nowdat2,nowdat3); // printDate(nowDate); // printDate(nowdat1); searchDay(nowDate,2); printf(\u0026#34;%s\\n\u0026#34;,FRELON); return 0; } Clang typedef typedef的作用是给数据类型定义一个别名\n","date":"2021-05-18T11:26:19Z","image":"https://cn.bing.com/th?id=OHR.LoganClouds_EN-CN1283366423_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/clang_note2/","title":"Clang学习笔记"},{"content":"博客迁移 最近我把我的个人博客从solo迁移到了hugo项目上了。\n不是solo不够优秀而是solo不符合我的需求了，我总结了以下几点\n可定制化程度太低，千篇一律，单调枯燥，乏味。 登录需要用社区账号，不确定因素太大。 经常更新导致的版本不兼容。 我也想过给自己偷个懒使用Bolo作为solo的替代品，但是还是不符合我的预期。虽然不需要社区账号登陆，但是他和solo太相似了，以至于我感觉我换了个寂寞。\n初识Hugo 在我有了迁移博客到其他平台的念想时我就一直在物色新的平台，然后我突然间就在GitHub上看到了hugo。\n一款golang写的软件，可以直接用markdown来更新文章，而且主题贼多，数据啥的也都是放自己手里。卧槽，我不就是我想找的吗！\nHugo部署 其实上面扯了些没用的，到这里才是我的技术博客开始记录的地方。\n下载hugo压缩包 1 wget https://github.com/gohugoio/hugo/releases/download/v0.83.1/hugo_extended_0.83.1_Linux-64bit.tar.gz -O hugo.tar.gz 首先，Github找到hugo的最新版，并且是extended版本的，否者我们后续是无法自行修改scss的，我一开始就因为这点，整整折腾了一个晚上都无法修改主题为我喜欢的样式。Google了良久才发现这个问题。\n解压hugo 这一步是很容易的\n1 tar -zxvf hugo.tar.gz 然后你可以把没有用的LIENCE和README.md删除了。只留一个hugo的二进制可执行文件。\n并且要把这个二进制文件放在环境变量里面。\n1 2 mv hugo /usr/local/bin #我个人是推荐放这里的，你要是喜欢放别的地方也是可以的 配置hugo 这里我建议你先看看hugo的quick_start，虽然看了和没有看一样，但是这也是一种学习的过程吧。\n1 hugo new site site_name 到这里你的网站就建立完成了。是不是很惊愕！你甚至连如何运行网站都不了解。\n没事，咱们慢慢来！\n先下载一个好看的Theme，当然不下载也可以，但是你没有主题的网站是真的丑呀。\n1 2 3 4 5 6 cd site_name wget https://github.com/CaiJimmy/hugo-theme-stack/archive/refs/tags/v2.3.0.zip -O themes/v2.3.0.zip cd themes unzip v2.3.0.zip mv hugo-theme-stack-2.3.0 hugo-theme-stack rm v2.3.0.zip 接下来就是配置文件了\n1 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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 baseurl: http://example.com #根据你自己改 languageCode: en-us theme: hugo-theme-stack paginate: 20 title: Blog #根据你自己改 # Change it to your Disqus shortname before using #disqusShortname: hugo-theme-stack # GA Tracking ID #googleAnalytics: # Theme i18n support # Available values: en, fr, id, ja, ko, pt-br, zh-cn, es, de DefaultContentLanguage: zh-cn permalinks: post: /p/:slug/ page: /:slug/ params: mainSections: - post featuredImageField: image rssFullContent: true favicon: /bk3.png #根据你自己改 footer: since: 2019 customText: dateFormat: published: Jan 02, 2006 lastUpdated: Jan 02, 2006 15:04 MST sidebar: emoji: 🥰 subtitle: 人生没有白走的路,每一步都算数。 avatar: local: true src: author.jpg #根据你自己改 article: math: true license: enabled: true default: Licensed under CC BY-NC-SA 4.0 comments: enabled: false provider: disqus #根据你自己改 utterances: repo: issueTerm: pathname label: remark42: host: site: locale: widgets: enabled: - search - archives - tag-cloud archives: limit: 20 tagCloud: limit: 30 opengraph: twitter: # Your Twitter username site: # Available values: summary, summary_large_image card: summary_large_image defaultImage: opengraph: enabled: false local: false src: colorScheme: # Display toggle toggle: true # Available values: auto, light, dark default: auto imageProcessing: cover: enabled: true content: enabled: true ### Custom menu ### See https://docs.stack.jimmycai.com/configuration/custom-menu ### To remove about, archive and search page menu item, remove `menu` field from their FrontMatter menu: main: - identifier: home name: Home url: / weight: -100 pre: home related: includeNewer: true threshold: 60 toLower: false indices: - name: tags weight: 100 - name: categories weight: 200 markup: highlight: noClasses: false ignoreErrors: \u0026#34;error-remote-getjson\u0026#34; 然后这样还是不能运行滴！\n调整主题 我们可以先看看这个主题的作者给的文档\n基本上就是照着来一遍\n步骤很多，还很麻烦，这里我就直接写了个Dockerfile，按照README的教程来，你一定可以用docker快速搭建起一个hugo环境。\n虽然主题不一定是你想要的类型，但是可以简化你用hugo搭建网站的步骤还能节省时间。如果喜欢的话，希望star、fork、watch。\n运行hugo 如果你是使用Dockerfile来构建镜像运行的话，你就不用看下面的教程了\n如果你还是想自己动手丰衣足食的话，那你就按照我Dockerfile里面的shell一步一步来操作也是可以在你的服务器上成功运行起来基于hugo的网站的，你可以使用以下命令来运行网站，因为hugo默认只监听127.0.0.1，所以需要单独设置以下hugo监听任何网卡。\n1 hugo server -p 1313 --bind \u0026#34;0.0.0.0\u0026#34; ","date":"2021-05-16T23:47:03Z","image":"https://cn.bing.com/th?id=OHR.Wakodahatchee_EN-CN9780170147_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/solo_2_hugo/","title":"从solo迁移到hugo"},{"content":"今天也是摸鱼的一天没怎么看书,一直在玩Linux。并且在无意间看到了我之前下载的 DirectoryLister，就想着在Docker里面部署玩一下。\n一开始我是没有想写Dockerfile的，直接在开了一个Ubuntu容器整的，但是想了想，以后想用肯定不方便，这个容器我随时随地都有可能废了它。所以就从网上看了看Dockerfile怎么写，开整！\n首先我并不想介绍Dockerfile怎么样去写，网上的教程一大把，自己看就可以了 我是参考菜鸟教程的教程。\nDockerfile无非就是一个自动化的过程，至少在我看来就是这样的，可能我刚接触了解不深吧。但是按照这个思路是绝对可以写出来Dockerfile的，因为我自己就是例子🌰。\n来看看我的build\n1 2 3 4 5 6 7 8 9 10 7168 ◯ : docker build -t flt:v1.0 . ... ... Removing intermediate container 8fe78ec98a65 ---\u0026gt; da56585b17c1 Step 4/4 : CMD [\u0026#34;/root/np.sh\u0026#34;] ---\u0026gt; Running in 23bd36103ddd Removing intermediate container 23bd36103ddd ---\u0026gt; 114c7dec7e2f Successfully built 114c7dec7e2f Successfully tagged flt:v1.0 选择基础镜像 因为Dockerfile是基于一些底层的镜像制作的，所以在这里我选择的是 ubuntu:20.04\n因为我平时就是在Ubuntu环境使用的比较多,你要是使用CentOS更顺手你也可以选择使用Redhat系的基础镜像。\n这里无可争议，过。\n1 FROM ubuntu:20.04 配置环境变量 其实我这里也没有啥环境变量，我也不知道啥东西要设置为环境变量，就把DirectoryLister的版本号设置为环境变量了，方便以后升级。哈哈哈哈，比较菜只能想到这东西了。\n1 ENV DL_VERSION 3.7.7 自动化运行命令 这里也没有啥好说的，有点Linux基础就可以，就是这个写法很操蛋！要不停的 \u0026amp;\u0026amp; \u0026hellip; \u0026hellip; \\，至于为什么要这样呢，我查了一下，说是一个RUN就是一层，你要是写太多RUN就会套太多层，非常的臃肿。这些暂时也不是我需要考虑的，别人怎么写，我就怎么写。\n这里我遇到了一个自动化安装软件时，需要用户手动输入参数的问题，平时咱手动输入参数可以，但是这自动化的时候可不能这样呀，废了老大劲网上找到了解决办法，就是利用 debconf 提前配置好参数，详细解决办法见Ubuntu问答 。\n这里我特喵的还是要喷一喷CSDN，妈的一篇帖子万人抄，也不管对不对，往自己的的账号里面薅就对了。妈的好几页都是垃圾文章，浪费我的时间。\n自动化里面基本上就是安装软件修改配置等等，强化了一下我的awk和sed。这俩命令太重要了！一定要学好！\nCMD命令 没错我直接跳到了CMD命令，啥COPY、ADD、ENTRYPOINT、ARG、VOLUME、EXPOSE、WORKDIR、USER、HEALTHCHECK、ONBUILD 我都没有用上，真的是不知道在哪里用，也用不好，索性就不用了，直接一个CMD就打完收工。\nCMD里面的脚本也是我倒腾半天网上学来的，这样写虽然不太优雅，但是总比一开始的时候，一运行就结束了好。说到这我就觉得这里还是有点坑的，因为Docker容器里面必须有一个前台的进程，要是没有进程就会整个容器停止运行，我一开始还傻傻的看网上的教程写nohup，写了半天也不行。然鹅nohup并不是真正的前台进程，因为你再按一次回车就直接退出来了，nohup并没有在前台继续运行，只是nohup的那个命令或者脚本没死在后台活着，但是前台没有进程就会直接退出。正是这种命令或者脚本还活着的幻觉让我们觉得这他妈的Docker是不是有病呀？我这居然还不行！\n所以有些东西还是要靠自己慢慢的积累，网上很多东西也都是错误的，不知道我上面的观点对不对，欢迎指正。\n","date":"2021-04-13T04:59:44Z","image":"https://cn.bing.com/th?id=OHR.PadarIsland_EN-CN9304851314_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/dockerfile/","title":"Dockerfile编写"},{"content":"Nginx目录美化 我们大家都知道啊! nginx是一个高性能的Web服务器,用于分享文件也是一个非常不错的选择,但是由于Nginx默认的目录太丑了, 都不好意思拿出手。这就有了本篇文章。\n先来说一下我的环境: Ubuntu 20.04.1 ，Nginx 1.18\n添加Index.html至主机根目录 1 nano index.html 内容如下\n1 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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 \u0026lt;!-- autoindex.html 21.02, see https://phus.lu --\u0026gt; \u0026lt;script\u0026gt; !function(){ var website_title = \u0026#39;\u0026#39; var datetime_format = \u0026#39;%Y-%b-%d %H:%M\u0026#39; var show_readme_md = true var enable_footer_js = true var max_name_length = 20 var dom = { element: null, get: function (o) { var obj = Object.create(this) obj.element = (typeof o == \u0026#34;object\u0026#34;) ? o : document.createElement(o) return obj }, add: function (o) { var obj = dom.get(o) this.element.appendChild(obj.element) return obj }, text: function (t) { this.element.appendChild(document.createTextNode(t)) return this }, html: function (s) { this.element.innerHTML = s return this }, attr: function (k, v) { this.element.setAttribute(k, v) return this } } head = dom.get(document.head) head.add(\u0026#39;meta\u0026#39;).attr(\u0026#39;charset\u0026#39;, \u0026#39;utf-8\u0026#39;) head.add(\u0026#39;meta\u0026#39;).attr(\u0026#39;name\u0026#39;, \u0026#39;viewport\u0026#39;).attr(\u0026#39;content\u0026#39;, \u0026#39;width=device-width,initial-scale=1\u0026#39;) if (!document.title) { document.write([\u0026#34;\u0026lt;div class=\\\u0026#34;container\\\u0026#34;\u0026gt;\u0026#34;, \u0026#34;\u0026lt;h3\u0026gt;nginx.conf\u0026lt;/h3\u0026gt;\u0026#34;, \u0026#34;\u0026lt;textarea rows=8 cols=50\u0026gt;\u0026#34;, \u0026#34;# download autoindex.html to /wwwroot/\u0026#34;, \u0026#34;location ~ ^(.*)/$ {\u0026#34;, \u0026#34; charset utf-8;\u0026#34;, \u0026#34; autoindex on;\u0026#34;, \u0026#34; autoindex_localtime on;\u0026#34;, \u0026#34; autoindex_exact_size off;\u0026#34;, \u0026#34; add_after_body /autoindex.html;\u0026#34;, \u0026#34;}\u0026#34;, \u0026#34;\u0026lt;/textarea\u0026gt;\u0026#34;, \u0026#34;\u0026lt;/div\u0026gt;\u0026#34;].join(\u0026#34;\\n\u0026#34;)) return } var bodylines = document.body.innerHTML.split(\u0026#39;\\n\u0026#39;) document.body.innerHTML = \u0026#39;\u0026#39; var titlehtml = document.title.replace(/\\/$/, \u0026#39;\u0026#39;).split(\u0026#39;/\u0026#39;).slice(1).reduce(function(acc, v, i, a) { return acc + \u0026#39;\u0026lt;a href=\u0026#34;/\u0026#39; + a.slice(0, i+1).join(\u0026#39;/\u0026#39;) + \u0026#39;/\u0026#34;\u0026gt;\u0026#39; + v + \u0026#39;\u0026lt;/a\u0026gt;/\u0026#39; }, \u0026#39;\u0026lt;a href=\u0026#34;/\u0026#34;\u0026gt;Index\u0026lt;/a\u0026gt; of /\u0026#39;) if (website_title) { document.title = website_title + \u0026#39; - \u0026#39; + document.title } head.add(\u0026#39;meta\u0026#39;).attr(\u0026#39;name\u0026#39;, \u0026#39;description\u0026#39;).attr(\u0026#39;content\u0026#39;, document.title) div = dom.get(\u0026#39;div\u0026#39;).attr(\u0026#39;class\u0026#39;, \u0026#39;container\u0026#39;) div.add(\u0026#39;table\u0026#39;).add(\u0026#39;tbody\u0026#39;).add(\u0026#39;tr\u0026#39;).add(\u0026#39;th\u0026#39;).html(titlehtml) tbody = div.add(\u0026#39;table\u0026#39;).attr(\u0026#39;class\u0026#39;, \u0026#39;table-hover\u0026#39;).add(\u0026#39;tbody\u0026#39;) names = [\u0026#39;Name\u0026#39;, \u0026#39;Date\u0026#39;, \u0026#39;Size\u0026#39;] thead = tbody.add(\u0026#39;tr\u0026#39;) for (i = 0; i \u0026lt; names.length; i++) thead.add(\u0026#39;td\u0026#39;).add(\u0026#39;a\u0026#39;).attr(\u0026#39;href\u0026#39;, \u0026#39;javascript:sortby(\u0026#39;+i+\u0026#39;)\u0026#39;).attr(\u0026#39;class\u0026#39;, \u0026#39;octicon arrow-up\u0026#39;).text(names[i]); var insert = function(filename, datetime, size) { if (/\\/$/.test(filename)) { css = \u0026#39;file-directory\u0026#39; size = \u0026#39;\u0026#39; } else if (/\\.(zip|7z|bz2|gz|tar|tgz|tbz2|xz|cab)$/.test(filename)) { css = \u0026#39;file-zip\u0026#39; } else if (/\\.(py|js|php|pl|rb|sh|bash|lua|sql|go|rs|java|c|h|cpp|cxx|hpp|css|html|)$/.test(filename)) { css = \u0026#39;file-code\u0026#39; } else if (/\\.(jpg|png|bmp|gif|ico|webp)$/.test(filename)) { css = \u0026#39;file-media\u0026#39; } else if (/\\.(flv|mp4|mkv|avi|mkv|vp9|m3u8)$/.test(filename)) { css = \u0026#39;device-camera-video\u0026#39; } else { css = \u0026#39;file\u0026#39; } displayname = decodeURIComponent(filename.replace(/\\/$/, \u0026#39;\u0026#39;)) if (displayname.length \u0026gt; max_name_length) displayname = displayname.substring(0, max_name_length-3) + \u0026#39;..\u0026gt;\u0026#39;; if (!isNaN(Date.parse(datetime))) { d = new Date(datetime) pad = function (s) {return s \u0026lt; 10 ? \u0026#39;0\u0026#39; + s : s} mon = function (m) {return [\u0026#39;01\u0026#39;,\u0026#39;02\u0026#39;,\u0026#39;03\u0026#39;,\u0026#39;04\u0026#39;,\u0026#39;05\u0026#39;,\u0026#39;06\u0026#39;,\u0026#39;07\u0026#39;,\u0026#39;08\u0026#39;,\u0026#39;09\u0026#39;,\u0026#39;10\u0026#39;,\u0026#39;11\u0026#39;,\u0026#39;12\u0026#39;][m]} datetime = datetime_format .replace(\u0026#39;%Y\u0026#39;, d.getFullYear()) .replace(\u0026#39;%m\u0026#39;, pad(d.getMonth()+1)) .replace(\u0026#39;%d\u0026#39;, pad(d.getDate())) .replace(\u0026#39;%H\u0026#39;, pad(d.getHours())) .replace(\u0026#39;%M\u0026#39;, pad(d.getMinutes())) .replace(\u0026#39;%S\u0026#39;, pad(d.getSeconds())) .replace(\u0026#39;%b\u0026#39;, mon(d.getMonth())) } tr = tbody.add(\u0026#39;tr\u0026#39;) tr.add(\u0026#39;td\u0026#39;).add(\u0026#39;a\u0026#39;).attr(\u0026#39;class\u0026#39;, \u0026#39;octicon \u0026#39; + css).attr(\u0026#39;href\u0026#39;, filename).text(displayname) tr.add(\u0026#39;td\u0026#39;).text(datetime) tr.add(\u0026#39;td\u0026#39;).text(size) } var readme = \u0026#39;\u0026#39;, footer = \u0026#39;\u0026#39; insert(\u0026#39;../\u0026#39;, \u0026#39;\u0026#39;, \u0026#39;-\u0026#39;) for (var i in bodylines) { if (m = /\\s*\u0026lt;a href=\u0026#34;(.+?)\u0026#34;\u0026gt;(.+?)\u0026lt;\\/a\u0026gt;\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s*/.exec(bodylines[i])) { filename = m[1] datetime = m[3] + \u0026#39; \u0026#39; + m[4] size = m[5] insert(filename, datetime, size) switch (filename.toLowerCase()) { case \u0026#39;readme.md\u0026#39;: readme = filename break case \u0026#39;footer.js\u0026#39;: footer = filename break } } } document.body.appendChild(div.element) if (show_readme_md \u0026amp;\u0026amp; readme !== \u0026#39;\u0026#39;) { tbody = div.add(\u0026#39;table\u0026#39;).add(\u0026#39;tbody\u0026#39;); tbody.add(\u0026#39;tr\u0026#39;).add(\u0026#39;th\u0026#39;).attr(\u0026#39;class\u0026#39;, \u0026#39;octicon octicon-book\u0026#39;).text(readme) tbody.add(\u0026#39;tr\u0026#39;).add(\u0026#39;td\u0026#39;).add(\u0026#39;div\u0026#39;).attr(\u0026#39;id\u0026#39;, \u0026#39;readme\u0026#39;).attr(\u0026#39;class\u0026#39;, \u0026#39;markdown-body\u0026#39;) xhr = new XMLHttpRequest() xhr.open(\u0026#39;GET\u0026#39;, location.pathname.replace(/[^/]+$/, \u0026#39;\u0026#39;)+readme, true) xhr.onload = function() { if (xhr.status \u0026lt; 200 \u0026amp;\u0026amp; xhr.status \u0026gt;= 400) return wait = function (name, callback) { var interval = 10; // ms window.setTimeout(function() { if (window[name]) { callback(window[name]) } else { window.setTimeout(arguments.callee, interval) } }, interval) } wait(\u0026#39;marked\u0026#39;, function() { document.getElementById(\u0026#34;readme\u0026#34;).innerHTML = marked(xhr.responseText) }) } xhr.send() div.add(\u0026#39;script\u0026#39;).attr(\u0026#39;src\u0026#39;, \u0026#39;https://cdn.staticfile.org/marked/0.7.0/marked.min.js\u0026#39;) div.add(\u0026#39;link\u0026#39;).attr(\u0026#39;rel\u0026#39;, \u0026#39;stylesheet\u0026#39;).attr(\u0026#39;href\u0026#39;, \u0026#39;https://cdn.staticfile.org/github-markdown-css/3.0.1/github-markdown.min.css\u0026#39;) } if (enable_footer_js \u0026amp;\u0026amp; footer !== \u0026#39;\u0026#39;) { div.add(\u0026#39;script\u0026#39;).attr(\u0026#39;src\u0026#39;, footer) } }() function sortby(index) { rows = document.getElementsByClassName(\u0026#39;table-hover\u0026#39;)[0].rows link = rows[0].getElementsByTagName(\u0026#39;a\u0026#39;)[index] arrow = link.className == \u0026#39;octicon arrow-down\u0026#39; ? 1 : -1 link.className = \u0026#39;octicon arrow-\u0026#39; + (arrow == 1 ? \u0026#39;up\u0026#39; : \u0026#39;down\u0026#39;); [].slice.call(rows).slice(2).map(function (e, i) { type = e.getElementsByTagName(\u0026#39;a\u0026#39;)[0].className == \u0026#39;octicon file-directory\u0026#39; ? 0 : 1 text = e.getElementsByTagName(\u0026#39;td\u0026#39;)[index].innerText if (index === 0) { value = text } else if (index === 1) { value = new Date(text).getTime() } else if (index === 2) { m = {\u0026#39;G\u0026#39;:1024*1024*1024, \u0026#39;M\u0026#39;:1024*1024, \u0026#39;K\u0026#39;:1024} value = parseInt(text || 0) * (m[text[text.search(/[KMG]B?$/)]] || 1) } return {type: type, value: value, index: i, html: e.innerHTML} }).sort(function (a, b) { if (a.type != b.type) return a.type - b.type if (a.value != b.value) return a.value \u0026lt; b.value ? -arrow : arrow return a.index \u0026lt; b.index ? -arrow : arrow }).forEach(function (e, i) { rows[2+i].innerHTML = e.html }) } \u0026lt;/script\u0026gt; \u0026lt;style\u0026gt; body { margin: 0; font-family: \u0026#34;ubuntu\u0026#34;, \u0026#34;Tahoma\u0026#34;, \u0026#34;Microsoft YaHei\u0026#34;, Arial, Serif; } .container { padding-right: 15px; padding-left: 15px; margin-right: auto; margin-left: auto; } @media (min-width: 768px) { .container { max-width: 750px; } } @media (min-width: 992px) { .container { max-width: 970px; } } @media (min-width: 1200px) { .container { max-width: 1170px; } } table { width: 100%; margin-bottom: 20px; border: 1px solid #ddd; border-collapse: collapse; } table th { font-size: 14px; } table tr { border: 1px solid #ddd; } table tr:nth-child(odd) { background: #dfdfdf } table th, table td { border: 1px solid #ddd; line-height: 25px; width:auto; text-align: left; } a { color: #369; text-decoration: none; } a:hover, a:focus { color: #2a6496; text-decoration: underline; } table.table-hover \u0026gt; tbody \u0026gt; tr:hover \u0026gt; td, table.table-hover \u0026gt; tbody \u0026gt; tr:hover \u0026gt; th { background-color: #f5f5f5; } .markdown-body { float: left; font-family: \u0026#34;ubuntu\u0026#34;, \u0026#34;Tahoma\u0026#34;, \u0026#34;Microsoft YaHei\u0026#34;, Arial, Serif; } /* octicons */ .octicon { background-position: center left; background-repeat: no-repeat; padding-left: 25px; } .file { background-image: url(\u0026#34;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAGB0lEQVR4Xu2cv4tcVRTHz5khtVEIaeNfIKSIKQ2kyrJpFbVIZyxcQ+Td6/gDVzDOvvuMIbFQO0EUbTfERiGWi4X/QmwloOll5sjArI26zD3vnbf3nv0OTHfPr+/3M/e+2Tf7mIxee3t7z00mky0R2WLmM0S0ep82KldS2o9CCO+X1NBRvfDQjaaUrhHRdSJ6fujcFeWrBoLBAOi6bltEdojockVGmbUqIrdijO+ZFRgo8SAAdF13R0RuDNSTmzQ1QNAbgJTSj/jU/z+zzHyraZpid4JeAHRdd0NE7rj5yBoNUjIEagC6rntVRL420sxj2o9DCO+WNpgKgPl8fm46nR4Q0dnSBiq8n+IgUAHQtu1tZr5ZuNiltlcUBNkApJTOM/OBiJwqVeEK+ioGgmwA2rZtmDlVIHLRLYrIPMb4znE3mQ1A13X7IrJ93I17qF8CBNkApJSeENFTHgwoYQZmnjdNc2w7QRYA66v/RyUI56mH44QgC4CU0hUieuBJ/IJm2QshzMbuJwsAXACa2zM6BLkA7DLzB+YynOwCo0IAAMqEbTQIAECZAJCItDHGt63bAwDWCvfIPwYEAKCHQWOEMnPbNI3ZTgAAxnCxZw1LCABAT3NGDE8hhDh0PQAwtKK2+QaHAADYGmaRfVAIAICFRfY5B4MAANibZVVhEAgAgJU9I+Rl5q5pmtCnFADoo14BsX0hAAAFmNi3hT4QAIC+6hcSr4UAABRi4EBtfBJCaHJyAYActepYmwUBAKjD1I27FJEPY4y7mwYAgE2VqmQdAKjEKKs2AYCVspXkBQCVGGXVJgCwUraSvACgEqOs2gQAVspWkhcAVGKUVZsAwErZSvICgEqMsmoTAFgpW0leAFCJUVZtAgArZSvJCwAqMcqqTQBgpWwleQFAJUZZtQkArJStJC8AqMQoqzYBgJWyleQFAJUYZdUmALBStpK8bgBYDVKJ5hu1Odbj9dwAsFgsLs1ms583UrfwRfP5/IXpdPpwjDYBwBgqZ9YAAJmCrZZjB1CIRrR6vqCPfwwBAAAA1wAKBrADKESzDsE1gEJhHAEK0TxdA+jGR5SbIwBW6hQAADrd3EQBADdW6gYBADrd3EQBADdW6gYBADrd3EQBADdW6gYBADrd3EQBADdW6gZxAwB+EXTCAcC9AACA28EKBtwcAdgBFO57uhsIAAAAjgAFAzgCFKJZh+AXQQqFcQQoRPN0DaAbH1FujgBYqVMAAOh0cxMFANxYqRsEAOh0cxMFANxYqRsEAOh0cxMFANxYqRsEAOh0cxMFANxYqRvEDQCrPwXrJLCJGusRL327dwVAKc8IGvNmDgBYK1DSzSAAsDalbdvdsR53BgB0ewGOAJ1uR0ZhB8AOMNpz/vryix2gr4L/EY8d4Jh2AAMv1SnxNZCIxrwIVDt1wgPdHAEn3Ef1+ABALZ2PQADgw0f1FABALZ2PQADgw0f1FABALZ2PQADgw0f1FABALZ2PQADgw0f1FABALZ2PQADgw0f1FABALZ2PQGsAGmZOPqTyOYWIhBhjt+l0vOnC1bqU0hUiepATg7WjK7AVQvhh06pZAMzn83PT6fTRpsmxbnwFFovFs7PZ7LdNK2cBsN4F/iCipzctgHWjKvBnCOGZnIoaAL4johdzimDtaAp8H0J4KadaNgBt2+4w892cIlg7jgIi8maM8V5OtWwAUkrnmflARE7lFMJaWwWY+S8RuRhC+DWnUjYAq+Rt295m5ps5hbDWVgER+TTG+FZuFRUA628DB0R0Nrcg1pso8PtisbiYc/V/2IUKgPW3gTeIKOu8MRkdSVcK7IQQPtNIoQZgDcE3RPSypjBiBlPg2xDCK9psvQBYFe26bl9EtrUNIE6vADPfb5rmqj4DUW8A1hC8JiJf9GkEsXkKMPP1pmm+zIv69+pBAFhDsC0iO0R0uW9TiD9SgZ+Y+V7TNPeH0GkwAA6bSSldI6LXiejCEA0ixz8K/EJEn4cQvhpSk8EBOGyubdsLk8nk6nK5vMTMZ4ho9T49ZPOOcz0hosci8ngymTxcLpf7McYVAIO//gayknO9Cn9ztwAAAABJRU5ErkJggg==\u0026#34;); background-repeat: no-repeat; background-size: 20px 20px; } .file-directory { background-image: url(\u0026#34;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAFWklEQVR4Xu2ZS2icZRiFzztJ01yMpFYTTU0Fo9GQaVIxShW84i1Jc1Ek4Mp0VxDdKbpSwUVFBLt0VQpu6qK2XqDaagVF0aJIOlWqCKVFoa02wbTNkHHmF4Wu1PzTzvtl5sx3up3O+c/7nIc/pTHoT9QELOrrdTwkQOQSSAAJEDmByM/XG0ACRE4g8vP1BpAAkROI/Hy9ASRA5AQiP19vAAkQOYHIz9cbQAJETiDy8/UGkACRE4j8fL0BJEDkBCI/X28ACRA5gcjPv6g3QLJ3cAKl4vWAddQktwzOIbEjmFv4xLYcy9dkxxorVZYAye6hPmSKbwK4t8b6/1+dBSR42R7NvU7St2o1yxNgb3YRCZqr1vJSH5zBuE3k3r/Ur8fwvVQBkneyL8HwIimM92wqN0HafUVqpwuwZ2AHYDMr0ibEQxoyt9r47LchoushswwBsgeJfvb/exPDczaZe60exgpxQwQC2D6bPDwSAl49ZNa/AEABQK9N5U7Uw2DeN8QgAGDJjE0e2ekNrx7y4hBgTf9J9D/5Qz0MtuwNCU4jwT50nd9pNl0s5944BGi6HLjlWSCzqhwm/H/HMI9SstWuHt+Vdky6AO8OH0IpP5wWVPOf928BOm6q+ZqOBRMsNV5pPY+cWS4zXYAP7/8Fi6e6HYtVJ6r7LuC6seo8u1pPNXvBOse2VSbAgdHzOHu8pVo3uD23rRsYfMYtjiRov3VtfqgyAT4eXcTCcb7fA/zX1RueAi7rIdnOoabhU+vcfJ8EuEBg/Qiw7h4HsiQRrgI0JUBrAjSXgAwJgNhqNrQDrUNA+53Aqk7ATQAca0Z7KTac3PeunQbab3f4EXDwwTzaTqzmphFp+55XPrNrHr+7sn8DfL6pgKb5xkgRcp/d8fBP1re9rzIBvhwsoWEp9f8LuEnVafuW3t9swwdXVSbAV9kS7E8JwOhI07XztvHAGgnAOJ5HZwngQZE4QwIQj+dRXQJ4UCTOkADE43lUlwAeFIkzJADxeB7VJYAHReIMFwG+3lgE8vr9H6MHLgJ8s6mAon4XwLg/JADlbH6lJYAfS8okCUA5m19pCeDHkjJJAlDO5ldaAvixpEySAJSz+ZWWAH4sKZMkAOVsfqUlgB9LyiQJQDmbX2kJ4MeSMkkCUM7mV1oC+LGkTJIAlLP5lZYAfiwpkyQA5Wx+pSWAH0vKJAlAOZtfaQngx5IySQJQzuZXWgL4saRMkgCUs/mVlgB+LCmTJADlbH6lJYAfS8okCUA5m19pCeDHkjJJAlDO5ldaAvixpEySAJSz+ZWWAH4sKZMkAOVsfqUlgB9LyiQJQDmbX2kJ4MeSMkkCUM7mV1oC+LGkTJIAlLP5lZYAfiwpkyQA5Wx+pSWAH0vKJAlAOZtfaQngx5IySQJQzuZXWgL4saRMkgCUs/mVlgB+LCmTJADlbH6lJYAfS8okCUA5m19pCeDHkjLJR4A7CijONVICiL306vVzNvTRFcthsDRGyeHHfsfi98uGpGXo8yoRaBs+agNv3VyZAD8+vQPz+2eqdIIeWwmBtVPPW++2VysS4O8vJ7NjZ5H/ua2SLvruChNoHfrVsrvWpT019UfAPwKcevsGnN69B+e+G0gL1Oc1QKD9ti/Q+MSI3Tj6R1qbsgS4EJKcfKMLZ2a3olToSQvW51Ug0NByFMUHttvA9FK5T78oAcoN1d/jISABeLYK0lQCBMHKEyoBeLYK0lQCBMHKEyoBeLYK0lQCBMHKEyoBeLYK0lQCBMHKEyoBeLYK0lQCBMHKEyoBeLYK0lQCBMHKEyoBeLYK0lQCBMHKEyoBeLYK0lQCBMHKE/oXjq52n+wTU1kAAAAASUVORK5CYII=\u0026#34;); background-repeat: no-repeat; background-size: 20px 20px; } .file-zip { background-image: url(\u0026#34;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAI/0lEQVR4Xu2dZ8gdRRSGnyj2jlhi16ioGMXesZfYuyg2bChiL6gg6B8VK3aNIqgoWFEUKwqKBdFgV1TEihoxdrH+kJfsJTdfdnZ2987u2e/eMxDy45uZc+Y9z53dnZ2dM4HqZU3grOrNxn2LT4FHgI8jI5E++wKTgFeB14APuzr6CTUcG1UAelJdBHwb0G0ioL+PLc8AD9XQuvEmDkB1iV8C7g40OwLYOvC3d4EHgOnVTTbXwgGoru1bwM2BZro0aoYMlRkZBG9WN9tMCweguq6PAY/XBKDX7B7gxeqm07dwAKppOg2YWtAkNgP0N30UeKKa+fS1UwOgX8ewFt34CYCiUgUA9fMccL+lYKkBuKrEY5LleJu2XRUA+aPHRN1U/tu0c3n9OwBpVa8DgDz4ALgL+CmtO/HeHIC4RlVqhAB4Adg20tGXGQRfVTE4aF0HYFAFZ28fAkD3RtJ6z4i5HzMIWls5dADaA0CPjpoFDouY/Ae4N1tGTutdTm8OQFqJi2aA3trBhsAxwDwR008DD6d1b87eHIC0CpcBQBa1Wng0sGTE/MvZJSGtl329OQBppS0LgKwuBxwFrBJx4W3gprRuzurNAUirbBUAZHkx4Ehg3YgbehV9TRNrBQ6ALQCyPjdwALBjxJXvMgh+TumyA5BSzZkbZfLeBha9QOp5MCXbSFLk0e+AVlu/SeW2A5BKyZn9DAKA2u8EHFTCpSuBT0rUi1ZxAKISVaowKAAyVmatQPV0Y6gbxIGKAzCQfHM0TgGAOt0ye0KIeXcn8EqsUtHfHYBB1JuzbSoA1PPGwHHZEnKRlw8Cz9YdhgNQV7n8dikBkIXJwAnAvBE37wOerzMUB6COauE2qQGQJT1VnAgsFHFVu461+7hScQAqyRWt3AQAMqrVwpOAxSMenA38FvWyr4IDUEWteN0iAGIflMR7B207X7qgYuUdWQ5AGdnL16m7I6i8heKaDkAqJWv24wD4ptDCD0NqclW6mc8ApaVqpqLPAD4D+AyQ4m63md9n8736DDDiM0DziM1cGAqdz+D3AG1EwNiGA2AcAGvzDoB1BIztOwDGAbA27wBYR8DYvgNgHABr8w6AdQSM7TsAxgGwNu8AWEfA2L4DYBwAa/MOgHUEjO07AMYBsDbvAFhHwNi+A2AcAGvzDoB1BIztOwDGAbA27wBYR8DYvgNgHABr8w6AdQSM7TsAxgGwNu8AWEfA2L4DYBwAa/MOgHUEjO07AMYBsDafHICiLFehwRZ9mGAt0CjYT6a/zge4dRQU8zHmK+AAjDgZDoAD4JeAUWbAZ4BRjn52CqXfBI4wBD4DjHDwNXQHwAFo9SZQx8foFIs2SujSFkreULTCJn/LJH0IjWsRYGFA/68ArASsBSzRhhBFNtqeAUYVgLwYLAhsDmwNLG8FggMwS/kmZ4Ci+GpmOD6bEVrnwAGwB6DngdLK7tU2AcMKwFZZOrY8PS3uAcrG9VBgu7KVU9RrG4AUPvf6uBxQPr2xZRngzIKj1ZsAILaWMh1QdnClfuulkM3TYn7gjBLJJJPpOF4BUE5d5dbNK8qwsVGBQhYA9LvzGXBZgX96OhAErZTxCEBRKtUdgEMiylkDIPeUJv78Aj8vzB4XG4dgvAHwB6CpX1Pp2LJyNvVrGi0qXQBA/im9i9K85BUllt6i8eiPw5XAu4GXAsKcUiIHr5p2BQD5cgEwI2c8OwMHOgCzK6D8eMqTl1d2B/YpKViXALgBeDfH702BY0uOZ6Bq4+US8D1wBfBrzmi1gKO7/rLpb7oEQCjTV2xRaqCg9zceLwDcAryZM+q5spOzV6+gSJcAmApM8xmgOHrKiqnsmHllP2C3CsHv2j3ARcC3fg8QjqCemZUp+7+cKusBJ1cMfpcA+AK4JOD/4cA2NcZWuUnXLwFXAx/ljGqBbOpfsfKIu/MUcD3wXsB/PR3osbbx0mUAHgGeDCgwyJq59T3A78BdBanfdT9zTuORzwx0FQD9MvQLySubZFm162rUBAChL3V6PiroSumqp5iidwGqX3Y9o+74Z2vXRQD+zK77X+eMULlzlR93qQFG3wQAA7gzW9MpwL6pOivTTxcBuBd4IeB8iiXSrgKgpV+Nr9XSNQBeA+4IKKCtU0qePGjpGgDzAfrl61/rpUsA/JBtGNWbsrFl2eyuf9EECnUFgN6ewC2BOk8zCaTo1rbw24HXA6M6EdggyYibeQyMuda/K3i1bBPoGqO4Kzgk1PPAfYE/7ggcHFO4wt+bmAFiO4IquNdu1S5cArQipgWfv3KGrsUQ3fXPm1AWB6BPzC4AcB3wfiDApwHrJAy+unIAOgSAFkUUkLyyB7B34uA7AGMEtZwBPgCuDQS4yffhPgN0YAb4O7vuf54DgN7x67o/qYFfv88AHZkB7geeCwR4f2DXhoLvAHQAgDeA2wIBrvuOvwovfgkwvAT8lE392uM3tugdf+oVsby3dA6AIQCDfGNf5VeuuqEbSQfAAch9Jx978iiC11cCS/48fQYoKVRb1dpeB3AA2opsSTsWAJR0LUm1vAMXQiuPMlh0QEPddkkG0lQnbQPQ1Di835oKOAA1hRuWZg7AsESy5jgcgJrCDUszB2BYIllzHA5ATeGGpZkDMCyRrDkOB6CmcMPSzAEYlkjWHEfZY1X6uy/6evVG4J2avnizcgokzxtYzuysWvpK5+JAozbX+qv6PSz1zQHQJ03XBNTUr1+zgJfmFDAHQEPTiV153+n9Apzb3Ni954KNLhJHyTiUk6F0qXMPoM4PAHYJWCk6AbO0Y14xqEAnZoC1gdMLgnQpkLfl2+M6uAKdAEDDOA9YNTAeHYXS2jk3g2s6rnroDACbAccUSKeDnfXdn88EafnqDAAaVpkDjZ7KvvvPO/MnrTSj0VunANBhB6cC2tMfK5oRvgH0ObgOgvJSX4HQ1rXWngL6XY9tp64/TG9ZVQETAOSkDnLQ6ZZebBUwA0DD1jk4RwGTbTUYaeumAEj5uQHlv1PunljqlpGOVEODNwegN66JwPaAMl+UuUFsSI+R67YzAPSUV/DXz/7peFddJvRPM4WX9ApUBuB/zONuC70V+ZUAAAAASUVORK5CYII=\u0026#34;); background-repeat: no-repeat; background-size: 20px 20px; } .file-code { background-image: url(\u0026#34;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAJ6UlEQVR4Xu2dd+wtRRXHPwh/aAQ1NsACChhiwRLsBUSaEkU0ImgUUUARNb4XfU+KqNgoohAFFGKNlZJQNEqxgIoCAQNEiUZBgwUBY1QgQgJCvmR+yfXm7cyZnX33zuyek+xf9+yZM9/zvbOzM7PnbIDLpBHYYNK9987jBJg4CZwAToCJIzDx7vsI4ASYOAIT776PAE6AYgQeArwA2DFcmwK6Ni627AZmEbgduDlclwC6fgn8pwSm0hHgEOADwBYlTvi9vRG4ETgWOKWvhb4E2Ag4D3hF34b9vkER+AGwJ3B3rtU+BHg9cHpuQ66/EAT2Ac7IaSmXAC8DfpTTgOsuHIGdgR9bW80hwIZ9hhirI643KAJ6RN9jsZhDgOOANRajrrN0BD4FrLV4YSXAmJ77/wgz52uBawJIzwCeHt5oHmkBbk6nRpum+YCFAA8DrgK26gFMbbf8DNgL+GeHYw8HzgFekuF4rTZvALYH/hXri4UAAuzsDEBqVf0q8Fajc18B9jfo1m7zNYHQnV2xEOAEYJUBjJpV/gQ8DbjD6OSDgV8DT4jot2DzRGB16QhwKfBCI3C1qmm1UpPYHNEkSqtsXdKCzV8ALyolwB+ArXOQq1D3VcD3Mv16JfDdyD0t2Lwe2KaUALeNYGPnMcBNmQTYHPhb5J4WbGoDaZNSAtybCVyN6puFXbQc37Sj+ffIDa3YjM7zLJPAMRBgd+DCnOgDuwEXRO5pxaYTIKxgHp9JgPcDWlHrEq2KtmDTCQBopW7byALQfJC1IPQ7ILYq2IpNJ0CIrlbsdjCOAj81rga2YNMJMBN0rdy9O7IgpAWgk4yrgCtma7fpBJj712sF7/PAdWGPQz9rzfwpwDsTq39dA0jNNp0AxmF/rGpOgLFG1tgvJ4ARqLGqOQHGGlljv5wARqDGquYEGGtkjf1yAhiBGqtaMQHGCoz3CzxFzNRZYNkOnjpGo+6/E2DU4U13zgmQxmjUGk6AUYc33TknQBqjUWtYCDCGM4GjDmKic8XrAE6AtunjBGg7fsXeOwGKIWzbgBOg7fgVe+8EKIawbQNOgLbjV+y9E6AYwrYNOAHajl+x906AYgjbNuAEaDt+xd47AYohbNuAE6Dt+BV77wQohrBtA06AjPjdBRwOnBtyCimnwGHA3hk2alN1AmRE5NkzXwzP3qZMIcoY0qI4AYxROw14R0T3yvAZudFcNWpOAGMoXhrq8HSpfw3Yz2irJjUngCEaPwFUDCMmRwOHGmzVpuIEMEREmUG+kND7NrCvwVZtKqMggFKeKw3LA4AnAQ8aEGUlg3xqIoOYJXm0XPov8HvgfyHVjFLtL1uaJ8CZgApWrIgIoDJpuwyE7GeB9yZsKc38lxM6PwRURk8EWBEVcFr2K2TTBDgK+EgH8F8EDhiABC8GlBE9JirL9vKIwpeAAzt+l/8fHsDPviaaJUAs+AJD1UqVDr1ELgopYWM2nh8qdMZ0lE5fVTy7ZJkkaJIAqeCvAP1n4HEFDDgI0EgSk88kii78BXi8wYdlkaA5AliDL8wF/mMN4K9LRfdq8hervfvQUDkkRrK/ZpBwGSRoigA5wU8VdEjxQv/s9yWUNEJohTAlOcUjFk2CZgjwCeCDKaRnflftYgHfV/Rsvzxxs1LM72poQJVFVLvXKh8HjrAqF+o1QYBPZ262lP6LNKvfIwGs3g6UDNoqOaOXbCrVfGoEsrYd06ueAHqnf1dGT0uDr6b0Xq8kzzHR+sB7MvySai4JTg5rB5nNZKlXTQDV53tbRneGCL6l3NsjwuRPZWFyJZcEWmCy1jPM9UX61RJAa+tvzOjREMFXcyofp5JvMdHegEamvpJLgm8Bb+jbWOK+KglwcXgGa+3cIkMFX211HfqY9UPl13eyOBbRySGB9ja+D2hLemipkgCqzftzY0+HDL5ltq4gaHt4CMkhQe6k0+pflQR4NHCroQdDBl/NvRn4RqJdDf16BAwl6oOIkJJHAbeklHr8XiUBrDX3hiSAKqCqfrAOfnaJagWqZnCfEvJdNq2jgNq0/ClyOVAlAZJVrWd6ORQJPmlYfNFrn17/hhJr8NWeavxaH4s5/lVJgKvDJCta235gEjwTuCaBnLVamCUAOcGf3CRQAM4f9EiBWjISnA28NtGADphoe3gIyQm+2lufB0eqHAFWQF4UCfSO/Z1EZLXpo82fUqkp+OpL1QRYxEjw2zD5uycSWW0pa/JXeoavtuA3QYD1TYKPGo5krQa0PVwiNQa/GQL0IYF1O1ivfr9JRFZHy3TErK9YFphmba/PZ/58H6p/BMw6nDMnsBwIsdjTYU9tD5dIzoGQRQa/qRGgz8TwJiC2Y6cj2WclIqsTvTk7kvPm9F3B5kb2LDr4TRJATus7vP0NoP4xUutXk7rtEja2DJO/jQ1tdaloe/mJhvt1/uAtBr2hVZp6BMx2XlW8YwcyngdcFkHrQ8DHEmiuCdvDpaCnjpd9LlQtL22nz/3NEkCd1QeZStiwLtGOXdf2qV75NPnTK2BMrgCe0wfVuXu0vd21fawlaCWZWJY0TQCBNv96peet/t0HRxC1HDaxTCJzgqaPS/XKqXnJipSsXua0HdNtngDq3L+BG8PHoVsZPg61bDatj+/9dcDlhvBx6BaAvitYtoyCADkgaqPpWYkbtg6TvwfmGG5Ud3IE0JxBc4eY6JmsZ/MUZFIE0GEPTf50+CMmvzKMEmMhx6QI8HVDHp+9AG0PT0UmRQDLkuw3M4+jt06UyRBAadxS7/RK/KgVwo1aj2qG/5MhwFpACR1jcmR4V8/Ar3nVyRBA/36NAjG51rA/0HzE5zowGQJoQ+eOSPReF84hji3Aqf5MhgDbANdH0Dh9LttYCrix/D4ZAmhx55iOqO0OnD+WiGb2YzIEEC7r6qzW45XB68mZwI1FfVIEUNBeDWiydyfwJmDnRI6/sQS6qx/FBLgNKDkxM3aAa+7f7cAmMQej7Ag3al1du2cu7SGgSbEmx51iIYDSqCoTpkt7COi4uz46LSJADada2oO+Do+TH6tYRgBl89BXsy7tIbBDKtWdhQDqdqv1ctoL2XAeXxXyIUUtWgnwduDU4XxzSwtAQAWwkmlurQTwUWABERuwCdO/X+3lEGBqJ2kGjMfCTVlORd/vVA4BpL8KOGHh3fEGcxDQp+4nWm/IJYDsDv1BhdVX10sjYDkS939W+hBABp4bUqlun/bJNRaAgJ75KlilT92ypC8BVhrR24EuJ0IW7IMpK/Ca6Sdn+10tlhJgxe5uobCCloyVbFGXbyANFuf7DWlj5+ZwaYlXGc1U0KJIhiJAkRN+8/IQcAIsD/sqWnYCVBGG5TnhBFge9lW07ASoIgzLc8IJsDzsq2jZCVBFGJbnhBNgedhX0fJ9t7LokKEG8cMAAAAASUVORK5CYII=\u0026#34;); background-repeat: no-repeat; background-size: 20px 20px; } .file-media { background-image: url(\u0026#34;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAKpElEQVR4Xu2deZBUxR3Hv72LwHLfy7knILogKgwmJUZNCZZUIFjgBlFQUlYOWagKZaVyGktTOf4gqZKlSBmNMVYSo6nSHIVlojkqSiGzMVFiRFhgkWuXXWDdY2YPdjr122FTsM7M6/de90z3+Ot/9o/p/vW3v7/P9pt5r1+3gIaytU7eKiVuh8ASCcxFAlMhUKghtLMhrh2H5kJg1aY5Yq/NgxBhxD0YlV8skNgKgavDxMnHtvPHASUjEGvqwaJN5eKArWMMBEBNnVyABGoh8ClbB5ZrXQTA/LFAay/a2/sw7+4ScSrXmlL17xuAmqhcBeA5AEU2DsgWTQMAkJ5z3Tjf1YaS6irRYYu+AR2+ANiyT66UAr+3bRA26rkUANJ3tgdnuvdgenW16LNJrzIAW6NyfkJgLyRG2jQAW7UMBoB0tnTj5OpZYqZNmpUBqInKvwG42SbxNmtJBQDpbYqjYU2pKLdFuxIANW/KL6EAu2wR7YKOdACQ9sYYDq4tE1faMA41AKLyPQDzbBDsioZMANAYTsWxv7pUXJPr8XgCsGWvXC4L8UquhbrWvxcA/RDEUFddJiK5HJs3AHVyu5TYlkuRLvatAgCN63gMr99dJm7K1Rg9AaiJytcB3Jgrga72qwoAje+DGF5dXyaW5WKsKgA0AijOhTiX+/QDAI3zWCf+cE+5oJtsWS0qAMisKsqTzvwCcBGC39xTLtZl0wIGwJDbQQAgKQ2d+Pm95WKTIVkfCcsAGHI6KAAk52gndm0oFw8aknZZWAbAkMthACBJRzqwfWOFeMiQvP+HZQAMORwWgP6ZoAOPbagQDxuS2B+WATDkrg4AErIfgq/fVyl+YEgmA2DKWB0AkLYLEvJ4J7ZuqBC1JrTyDGDCVQC6ACB53QkkjnfigfsrxdO65TIAuh29GE8nABQynsCF0zGs31ghXtApmQHQ6eYlsXQDQKE7LqCnJYbV984WL+uSzQDocnJQHBMAUBdtFxBvjWP5+kpBz2hCFwYgtIWpA5gCgHpr7UVHRzeWrqsQb4eVzwCEdTBNe5MAUJfnetB6RSGuu2OaaAgzBAYgjHsZ2poG4CIELcVjMO+GMeJs0GEwAEGd82iXDQBIQksXGicVYe7SyaI9yFAYgCCuKbSpHAVEJipU1FCluRvH57ZhdlWV6PEbjgHw65hi/UnDgNumKlbWUK2pC0fWlIhKv6EYAL+OKdYfVgDcOUuxsqZqTd04sGaWuMpPOAbAj1s+614/AZg72mejkNUbu/DO2hKxUDUMA6DqVIB6I4cAy6YCw7O8U8LpOOruKlVbbs4ABEisnyYThgLXjgemDPfTKnzdkzG88bkysdQrEgPg5ZCmz+lnIcEwfihQlKUZ4VQcr1WXitsyDYEB0JRgW8PURkTGHDMAtmZOky4GQJORroZhAFzNnCbdDIAmI10NwwC4mjlNuhkATUa6GoYBcDVzmnQzAJqMdDUMA+Bq5jTpZgA0GelqGAbA1cxp0s0AaDLS1TBOATBnNHDdeGBqETCtCBg1xFXbL9fd0g2cjAHN3cBb5/p3BstacQaA9WXAJydlzZecdvRGM/DcsexIcAKAbfOA8lHZMcSWXn5aD7zTal6N9QAsHA884Hstq3njTPdwvgf4/rtA3PDm8dYDsLYEuHmKabvtjP/MEaDunFlt1gOweS4wb4xZE2yN/spp4I8nzaqzHoDvLQRGX2HWBFuj//s88NRhs+qsB2DHYrMG2Bz9UDvw+PtmFeY1AO+39R/Dgt4EMHEYsGCcWTN1R2cAAASdAZ48DLx9/vKU3FIMrMny61hhoGAAAgLw8ilgd5pT+BZNAO6vCJOW7LVlAAIA0NYLfNNjYxRXvlgyAAEAaOgEttMJRhnKQ1cBpQ4cbscABACAHqg8uj8zAN9ZAND7+bYXBiAAAJTU2oMA/QJIVWaNAL7qyFHWDEBAAI51AvQw5cPejyLwtSpghiOnGjMAAQGgtFPy9zQD+1uBrj7g6rHAiunACIfWEDAAIQCw/fquoo8BYAD4VnDQO4Eq/2G21+EZwOEZ4JmjAJ3osSnEXUcGwFEAaD0freujsngCcF9ACBgABwF48Tjwl6bLLy5LJgIbyv1fcBgAxwCgB1D0ICpVoRXNtLLZT2EAHALgtUbgpROZ03vjZGBdqToCDIAjAPyjGXhecR3/TVOA6hI1CBgABwDYdxZ49qhaQgdq3TIFWKMAAQNgOQC04ohWHgUpny723iyaAbAYgANtwK5Dyd/6QQttF//ZmelbMwCWAnC0I5l8HW/t3D4N+MyM1BAwABoB+Nf55JvFYcvJOPCTQ0Cr77M30vd8x/Tkk8rBhQHQBMDAtXrlDGD5tOAI0BJz+s8/0xU8RqqW9Nr71isZgJSuhn0YROsB6IvawLX6E5MAAmGMz7eN2nuTyTfx7j4DkOEfKgwA734IPFnff8L2ZYUObFo5E6C/KqUnkUx+faBzt7x7YAAMAPBeWzL5lLxUhWYAmgloRvAqdM0nmEwVBkAzALQglKZ9WgrmVeg7AYGQrjx9JLlti8nCAGgEgL45U/JjF9RTRr8O6JIwedBS8V82AHtb1OMErckAaALgcAfwVD3Q7iP5A13PHJGcCWjxKJXffgD8/UzQlPprxwBoAIBuztC79KmWgqumg87yo5mAXi/702nVVuHrMQAhAaD3ACj5tK+Oi4UBCAEA/S6n5J/tdjH1Sc0MQEAATsSAnx1ObrDocmEAAgBwKp5MfpPm27K5AIkBCAAA7Z1DP/nyoTAADAA/DErHQLpnATwD6Jn7nN0ljAFgAPg7gAYGrJ8BvlGVPBtgcMm0E5gGX7IaglYD0aqgwWVPC/DrBrNSrAfg85Xpl3Jd+g6eWZvMRc/0ssjvTgCvNprrmyJbD8CqmcCyqelNoJU6jY7eC5gyHBibYWXSE/XJHU5MFusBcGlTJ52JomXnOw/qjJg6lvUAkGx6n46myo9T+eF/AbrVbbo4AQCZcM04gA6PGD/UtCW5jU+3tukl0/8YnvoHRukMACS4qBCoGps8Nax4ODDSoR2/MmFFy80b48mHWqav+YN1OAVAbv8387N3BiA/86o8KgZA2ar8rMgA5GdelUfFAChblZ8VGYD8zKvyqBgAZavysyIDkJ95VR4VA6BsVX5WZADyM6/Ko2IAlK3Kz4oMQH7mVXlUOgA4BGC2co9c0SYH6msjYk4mQcJLbU1UvghgtVc9/txKB16qjYg7wwLwFQA/snJ4LMrLgW21EfHjUAB8OSorCoGAG6Z66ePPTTrQB1TuiogjoQCgxjVR+TyAu0yK5djaHXihNiKqvaJ6fgfoB+BNuRgFiHoF488tciCBSO0Nos5LkRIAFGTzPvmYEPiWV0D+PPcOSInv7lwivq2iRBmAi5cC/kWg4mpu63h+879Uni8AHvmrHNIyCrsBLMvtGLn3NA78eVIHVjxyq1DeR80XAAOdbonKX0hgA6fBHgcE8OyOiNjoV1EgAC5eDr4A4FEAxX475fpaHaBD7B6ujYgngkQNDAB1tmW3HJaYiM0FhVgvJRYFEcBtgjkgBP6Z6MOvCs5i544VIvA2WqEAuFT65rdkaUEfIlJgtgAmS8Dnhu3BjPi4tBJArwSaExL1ohDRndcLxXPMMjv0PyM5lL1eCWsKAAAAAElFTkSuQmCC\u0026#34;); background-repeat: no-repeat; background-size: 20px 20px; } .device-camera-video { background: url(\u0026#34;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAALYUlEQVR4Xu2de1BU1x3Hf2dBVFCQDmlofCDsro2ROlqjrRmnpL6CM01SUKadJlbbvDpqoyg6xtpMUquJHYFMI7Vo06SxiQ6yGh9t1fioVSuhJlYRq+6yiPgCREBgWWB3T+deQ8YH7D33nsvhzPK7Mw5/7O/8ft/z/X68e+/uZSCAR692gPTq3ePmAQHo5RAgAAhAL3egl28fzwAIQC93oJdvH88ACIA4B3bW1aXHhFumRJGwx2pdLuu+goK4y+Xl4bwK5q9Y0TTcbvfz9rnkdIblrVkzgLdPTGxsYGVOTmOwPn6g/oYAbWj0+2o8gUBFK4WSGq/3b6/Ex5fwztezvlvPAPNcrqEpcV/71dDwPjPsffsNiQsPt9wtrry8HBYtWgQXL17Uo/mBWpvNBrm5uaD85D1cLhdkZmaC8pPnSE1NVTXpPap9vvZan+8KpfQ/j/Xvv4gQcl1vDz313QLA7lu3XhvSr98vEiMihsWEhQXVU1FRoUJw/vx5PbpDGoKOzdX7/YEzLZ7PyjwtWT9/+OF/cxnUxWJTAcitrp71eGT/nElRA4bqEVtZWalCcO7cOT3LegUEyibr/D74wuM5UdTWnrEyLu4ql0n3LTYFgIyCgrCfTp9+8LtRUSlx4cbe0q9du6ZCUFLC9xYYSm8H9wdd0dbqP9/i/UvqoEEvmAUBNwDLP/54ScqoUb9JHT06klfUjRs3VAhOnz7N1SqUIVCMudDqrfh1337WbYRwX/hyAbBkx461pzdsWBYRHg7r1q2DkSNHcgWnLK6urlYhOHXqFFevUIfgant784KrV5M/SUy8xGOUYQCWvv9+we61azM6hickJKgQJCcn8+hR1968eVO9Ej958iRXr1CHwBMIBOa63d/aZrcbvngyBEBWbs7hPfkbn7w/ncGDB6sQjBkzhis49cKnrk49ExQXF3P1CnUIGvx+/wtX3SMcCSPcRozSDcDS7OyduzdteqarYfHx8SoE48aNM6LnnjUNDQ0qBEVFRVy9Qh2Cs60t9dnXq77xQWKiV69RugBYsXXrjKL33vv7tcrKoHPi4uIgOzsbJkyYoFfPA/WNjY3q28Hx48e5eoU6BKXelsrk/pHD9JqkC4AXly27fWzXroEsQ2JjY1UIJk6cyFIetKa5uVmF4OjRo1y9Qh2CQ02NhVMGRn91XcZiFjMAWfn5/9iTm5vK0rSjZuDAgZCTkwOTJk3Ss6zTWq/Xq74dHDlyhKtXKENwy+eDfzU3T00bNOggq0lMADzndEa3rVpVX1JczFR/9/DIyEgVgpSUFFZNXda1tbWpZ4JDhw5x9QplCIo9Htd3oqLsrAYxBbro3XdP7c3LM3xpHxERoX4xMnnyZFZdXdb5fD4VggMHDnD1ClUI2iiFD2urf/LSQ/FbWAzSBODJ8vJ+0VlZnoulpZq1wQaGhYWpEEybNo1FV9CaQCCgQrB//36uXqEKweGmxnOTB0aPYjFHM9Q3/rp539bfrp7O0kyrRrkwTExMBEqp+k85gv0M9trt27dB+f6A94iJiYHo6GjDOjr2oGj1eDzQ0tLCKwmUM6bFcueb8848YPHum3v2DNtpswW/XQPQfiz8lWXLPEd27erPuyuj3493Nnfv3r3qGYD3mD9/PixYsIC3jbp+/fr1kJeXx91LOUsqXvEef6q9ueGluIfmafUJegbI3Ltr1Ik33zpbX1en1Sfo6xg+m31mhe/3+2He6683bVyzRvOWPSgASz/avGn3qtUvssnvvArDZ3PPrPDb29shKysLzjudELNxY+S2oUODvicFBWDhOzn/2/fHjY+ybeHBKgyfzTmzwm9tbYUlS5bAwYN3Pgb44ZpVy99Oz1gbTEVQAOa++qqnaP9+Q+//GL7Y8JULUOV//uHDh78anDp37n/fWb58rGEAns3I8F8oKbnnQU6WbWH4LC6BeltsxgVfU1OTGv79n5KmpKVV5b/1VrxhAKY+9RS9UlHBtpsvqzB8NrvMCl+5HVZO+8eOHXtg8BMzZjT+OTc32hAAM5zOvvVz5njrbt1i2xGASrORR6E7G4C3etq219fXw+LFi+HEiROdFo9PSfFuzs8P+hbe5TXADKczujItrUH56JXlwPBZXDLvtF9bW6uGH+yBmdHjx7cXbN4cYegMoHwB9PnTTzewbAvDZ3HJvPBramrU8LUemRs1dqzPsWVLn24FAMMXG35VVZX6KSjLQ7PdDgCGLzb869evq89EnDlzhmlwtwKA4TNlYNqt3pUrV9TwS0tL2QYDQLcBgOGzZWDWrd7ly5dh4cKFun9/slsAwPDFhq/8BrUSvtPpZBt8VxU3AGWZmQ/cBUQMiNItpLMFbU3NpvRRmoSqJp+3FQKMt+GdmUnbWls+2/9p0F/Z6/JzAOU20BtGmG4DTUsSG5nqACFwqDDRNsXwbSACYGoewpshAMItl2sgAiBXHsLVIADCLZdrIAIgVx7C1SAAwi2XayACIFcewtUgAMItl2sgAiBXHsLVIADCLZdrIAIgVx7C1SAAwi2XayACIFcewtUgAMItl2sgAiBXHsLVIADCLZdrIAIgVx7C1SAAwi2XayACIFcewtUgAMItl2sgAiBXHsLVIADCLZdrIAIgVx7C1SAAwi2XayACIFcewtUgAMItl2sgAiBXHsLVIADCLZdrIAIgVx7C1SAAwi2XayACIFcewtUgAMItl2sgAiBXHsLVIADCLZdrIAIgVx7C1SAAwi2XayACIFcewtUgAMItl2sgAiBXHsLVIADCLZdrIAIgVx7C1SAAwi2XayACIFcewtUgAMItl2sgAiBXHsLVIADCLZdrIAIgVx7C1SAAwi2XayACIFcewtUgAMItl2sgAiBXHsLVIADCLZdrIAIgVx7C1SAAwi2XayAXALNv3IjyeJqa5NoSqtHjAAHyaWGSdXqwNV3+3UBl0Uy3KwAAQWv0CMJawQ5Q+MRhtaXxAFAFAF8XLBvHmefAJkeS7WUOAMqKAeh48/RgJ8EOrHQk2VZzAOD6EABmCxaN48xywEJmOYZbHYYBSC9zLiKE5JqlB/sIdsAXsDpGjHAbBiCjwjUu4IeTgmXjOFMcoG5Hkt2q1UrzCn+m21UBAMO0GuHrcjlAgf5he5J9vpYqbQDKy3KA0kytRvi6XA4QSqYWWq0HtVRpApDudH6bhJHPtRrh6xI5QKHUYbUlsyjSBEBpMtNdthuA/oClIdZI4IDFssAxPCmPRQkTAOmXXN8nATjE0hBretgBCmUOq83GqoIJgDtnAedmAPI8a2Os6xkHCMCcwiSb8vkN08EMQLrTOYSEkbMAEMPUGYuEO0AI7ChMtKXrGcwMgNI03e2eTSDATJceIVjL7UCNLwDjdtpslXo66QJAaTzL7VpHAZboGYK13e8ABfrs9iT7Lr2TdAOgngnKXVsIhR/rHYb13ebALx1JtvVGuhsC4MszwU4K8IyRobjGRAcIec2RaH3baEfDANy5M3BtBYAfGR2O6/gcoABZ25Ns2TxduADAawIe67nW+omFzCkcbv2Iq4tZj3vNdDufByC/B4BYXkG4XtOBIuqn87fb7V9oVjIUcJ8BOmZklJfH+wO+NYSQnzHMxRIDDlBK39hutb9pYGmXS0wDoGPCrEuuJyBAMinQWWYK7c29CIV8Gt7nd46EhKAPdxjxyHQAOkTMLL/wKKFhz1GAmQAw0oi43r2GlFCgBeCnH2y32690lxfdBsDdgtMuXRpJfL7vWSzwOAWaDECS8Gnjux0i9UCpGyxQQgKkmIQH/rktwX6uu0K/Z7KIIZ3NeJnSPjVVZbHEY+nfUxp6fK7F29YeMahh9yOPeHpKi5AzQE9tDudqO4AAaHsU0hUIQEjHq705BEDbo5CuQABCOl7tzSEA2h6FdMX/AXa7VdvnsmGyAAAAAElFTkSuQmCC\u0026#34;); background-repeat: no-repeat; background-size: 20px 20px; } .octicon-book { padding-left: 20px; background-image: url(\u0026#34;data:image/svg+xml;charset=utf8,%3Csvg xmlns=\u0026#39;http://www.w3.org/2000/svg\u0026#39; width=\u0026#39;16\u0026#39; height=\u0026#39;16\u0026#39; viewBox=\u0026#39;0 0 16 16\u0026#39;%3E%3Cpath d=\u0026#39;M3,5 L7,5 L7,6 L3,6 L3,5 L3,5 Z M3,8 L7,8 L7,7 L3,7 L3,8 L3,8 Z M3,10 L7,10 L7,9 L3,9 L3,10 L3,10 Z M14,5 L10,5 L10,6 L14,6 L14,5 L14,5 Z M14,7 L10,7 L10,8 L14,8 L14,7 L14,7 Z M14,9 L10,9 L10,10 L14,10 L14,9 L14,9 Z M16,3 L16,12 C16,12.55 15.55,13 15,13 L9.5,13 L8.5,14 L7.5,13 L2,13 C1.45,13 1,12.55 1,12 L1,3 C1,2.45 1.45,2 2,2 L7.5,2 L8.5,3 L9.5,2 L15,2 C15.55,2 16,2.45 16,3 L16,3 Z M8,3.5 L7.5,3 L2,3 L2,12 L8,12 L8,3.5 L8,3.5 Z M15,3 L9.5,3 L9,3.5 L9,12 L15,12 L15,3 L15,3 Z\u0026#39; /%3E%3C/svg%3E\u0026#34;); } .arrow-down { font-weight: bold; text-decoration: none !important; background-image: url(\u0026#34;data:image/svg+xml;charset=utf8,%3Csvg xmlns=\u0026#39;http://www.w3.org/2000/svg\u0026#39; width=\u0026#39;10\u0026#39; height=\u0026#39;16\u0026#39; viewBox=\u0026#39;0 0 10 16\u0026#39;%3E%3Cpolygon id=\u0026#39;Shape\u0026#39; points=\u0026#39;7 7 7 3 3 3 3 7 0 7 5 13 10 7\u0026#39;%3E%3C/polygon%3E%3C/svg%3E\u0026#34;); } .arrow-up { font-weight: bold; text-decoration: none !important; background-image: url(\u0026#34;data:image/svg+xml;charset=utf8,%3Csvg xmlns=\u0026#39;http://www.w3.org/2000/svg\u0026#39; width=\u0026#39;10\u0026#39; height=\u0026#39;16\u0026#39; viewBox=\u0026#39;0 0 10 16\u0026#39;%3E%3Cpolygon id=\u0026#39;Shape\u0026#39; points=\u0026#39;5 3 0 9 3 9 3 13 7 13 7 9 10 9\u0026#39;%3E%3C/polygon%3E%3C/svg%3E\u0026#34;); } \u0026lt;/style\u0026gt; 编辑Nginx里的主机配置 nginx 配置如下\n1 2 3 4 5 6 7 location ~ ^(.*)/$ { charset utf-8; autoindex on; autoindex_localtime on; autoindex_exact_size off; add_after_body /autoindex.html; } 重启Nginx 这个时候你就会发现默认的目录已经变好看了一丢丢，当然你可以基于这些代码继续更改。直到你满意为止。\n本文参考 ： 91yun论坛 侵联删\n","date":"2021-03-23T21:33:56Z","image":"https://cn.bing.com/th?id=OHR.WWDLions_EN-CN5288663061_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/nginx_beautify/","title":"Nginx目录美化"},{"content":"ffmpeg是一款非常强大的工具,它可以在命令行下对视频类文件进行多种操作,之前我也介绍过ffmpeg,戳这里\n在使用过程中我们经常可以遇到使用ffmpeg无法下载文件的情况,那这是为什么呢？\n其实有时候,服务端会对我们客户端的请求进行检验,其中最简单的就是检查请求头：User-Agent ，如果你连请求头都没,那你肯定就暴露了你不是正常的用户，你既然不是正常的用户，那那服务器那边就会拒绝你的访问请求，所以就导致了你被403。\n既然这样那咱们简单设置一下请求头就可以了：\n1 ffmpeg -user_agent \u0026#34;User-Agent: AppleCoreMedia/1.0.0.18C66 (iPhone; U; CPU OS 14_3 like Mac OS X; zh_cn)\u0026#34; -i https://www.baidu.com/xxx.mp4\\?id\\=2399977 -c copy str4.mp4 当你设置了请求头以后你还别高兴太早了！因为服务器那边既然开启了检测，就不会通过这麽简单的方法来拦截非正常用户的访问。一般来说还有各种headers\n这时我们就需要通过抓包来获得具体的请求头里的参数了，下面我就简单的介绍一下具体的参数该如何写。注意 $'\\r\\n'这是一种换行的写法。\n1 ffmpeg -user_agent \u0026#34;User-Agent: AppleCoreMedia/1.0.0.18C66 (iPhone; U; CPU OS 14_3 like Mac OS X; zh_cn)\u0026#34; -headers \u0026#34;Authorization: fudkmnso-... ...9-0sdf-0ea\u0026#34;$\u0026#39;\\r\\n\u0026#39;\u0026#34;Xplay-session-ID:9885998\u0026#34; -i https://www.baidu.com/xxx.mp4\\?id\\=2399977 -c copy str4.mp4 ","date":"2021-03-23T14:21:09Z","image":"https://cn.bing.com/th?id=OHR.Comma_EN-CN6081365526_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/ffmpeg_user_agent/","title":"ffmpeg设置请求头"},{"content":"记录最近捯饬Linux的收获 虽然最近在准备专升本考试吧，但是也没有认真的复习，最近还是天天玩Linux，在这里记录一下。\nWindows Terminal设置 如果你关注IT圈的话，你一定知道微软在Github开源了新的Terminal，并且这是一个颜值高,用着爽的全新版本,支持Unicode。这些都是后话，我要说的是WSL和Terminal相关的设置。因为Terminal打开WSL默认的路径是该用户的在Windows下的家目录，而此时你就需要修改setting.json对应位置添加以下配置：\u0026quot;startingDirectory\u0026quot;: \u0026quot;//wsl$/Ubuntu-18.04/home/frelon\u0026quot;\n如果你还没有用过Windows Terminal那你可以先看看这个Windows Terminal的简介\naria2下载 众所周知，aria2 是一款非常好用的一款下载工具，并且它开源，体积小，运行效率高等。什么？你没有听说过？那你总听说过Motrix吧！什么你还没有听说过？那你一定知道PanDown吧？没错这款伟大的软件就是基于aria2。\naria2本身只是命令行工具，所以我们可以在命令行里安装它。\n1 sudo apt install aira2 如果你只是简简单单的下载文件，那curl 和 wget 、aira2是没有啥大的区别的。额说到这里哈，就不得不说今天是curl的23岁生日，curl是 March 20. 1998 年发布的。没错！确实是23年前的今天！因为时差的原因，那边还是20号，咱们这边已经21号了，不是我记错了。\n然后我就根据我个人的使用习惯给alias一下了，因为我不喜欢这种字母和数组夹杂的命令，遇到了都会给重命名一下方便以后自己敲命令。\n1 alias ac=\u0026#34;aria2c\u0026#34; 首先既然是下载工具，那我为什么要用aria2？\n我们先来看看aria2支持啥协议\nURI, MAGNET, TORRENT_FILE, METALINK_FILE\n传统的http，https，ftp，磁力。bt种子，metalink。你就说它强大不强大吧，可以在这么小体量的情况下，做这么多事情。现在你知道为什么这么多人夸赞它了吧。\n现在我们来说说看Windows这边的下载巨头：迅雷。\n为什么我们都瞧不起迅雷？\n因为迅雷是白眼狼，喝BT的血起家，把自己养肥了。到最后迅雷却不反哺BT下载的生态圈。就这一点就足够可耻的了。\n获取CPU温度 1 echo `echo \u0026#34;scale=2; $(cat /sys/class/thermal/thermal_zone2/temp)/1000\u0026#34; | bc` °C 用Ubuntu 20的小伙伴可以直接用这条命令查看cpu温度，Ubuntu18我还没有试过，别的系统我就不得而知了。\nawk 、sed awk 是一个文本处理命令，常用的就是查找文本内部的字符。\n默认情况下awk是用空格来分割，\n1 awk \u0026#39;{print $1}\u0026#39; 如果你想用特定的字符串分割,可以用 -F来指定。\n1 awk -F/ \u0026#39;{print $1}\u0026#39; sed它同样也是文本处理，用来替换文本的，最常用到的可能就是在刚安装完系统后换软件源的时候。用国内的url来替换国外的url\n1 sed -i \u0026#39;s/xxx/yyy/g\u0026#39; filename 这个就是把文件内部的xxx替换为yyy。如果需要在文件行首添加字符，就可以sed -i 's/^/yyy/g' filename，有行首肯定是有行尾，那行尾把 ^换成$就可以了。\n","date":"2021-03-21T01:25:43Z","image":"https://cn.bing.com/th?id=OHR.BlueTitDaffs_EN-CN2615365443_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/linux_reward/","title":"记录最近捯饬Linux的收获"},{"content":"Ubuntu 20.04.2 配置 vsftpd 的脑坑 今天是 2020-03-01 02:13:13，刚结束了我在我哥饭店帮忙的欢乐时光，刚下班回来的我就准备掏出我的大宝贝好好的爽一爽，毕竟大半个月都没有咋用我的电脑了。因为之前刚换的Ubuntu20还有好多东西没有配置好，我就想着今天晚上把 ftp 整一下，然后就遇到了我特别尴尬的一幕，记录一下。\n安装 VsFtpd 1 sudo apt install vsftpd 这一步毫无争议，没啥可说。略\n配置文件 1 2 sudo cp /etc/vsftpd.conf /etc/vsftpd.conf.bk sudo nano /etc/vsftpd.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 listen=NO listen_ipv6=YES anonymous_enable=NO local_enable=YES write_enable=YES allow_writeable_chroot=YES local_root=/home/frelon dirmessage_enable=YES use_localtime=YES xferlog_enable=YES connect_from_port_20=YES ascii_upload_enable=YES ascii_download_enable=YES ls_recurse_enable=YES secure_chroot_dir=/var/run/vsftpd/empty pam_service_name=vsftpd rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key ssl_enable=NO utf8_filesystem=YES 一样是毫无争议，按照自己喜欢的配置来。\n重启 vsftpd 1 sudo systemctl restart vsftpd 我就特喵的在这一步出现了问题了。因为我平时使用samba比较多，在 Windows 下面映射一下就可以把局域网的 samba 当本地硬盘使，所以我就莫名其妙的输入了 sudo systemctl restart smbd ,我就本能的，条件反射的输入了 smbd，然后我就用我的用户名登陆 ftp，一下子给我整的不自信了，咋回事？这 ftp 咋还不能用呢，卧槽？ftp 出问题啦？我就改了半天的配置文件。甚至允许匿名登陆都特喵的不能用，当时就心态炸了。然后又检查了好几遍配置文件，还把防火墙给关了也于事无用\n我当时甚至怀疑系统出问题啦，还把服务器给重启了一遍（重启的时候我偏偏手贱，把配置文件还原了）,还不能登陆，我就接着改配置文件 ，接着重启 smbd，然后我打算放弃了，觉得这 ftp 是不是出现了 bug。我本来打算去洗澡的，但是心里面又有一点不甘，用手机接着来，当我又一次打算重启 smbd 的时候我突然发现了问题，我特喵的用的是 ftp，我老在这重启 samba 干啥？？？这就像你抓鲁迅关我周树人什么事一样的感觉。我就突然觉得挺对不起我的 samba 的，逮着它就是一顿重启，阿哈哈哈。\n","date":"2021-03-01T22:37:16Z","image":"https://cn.bing.com/th?id=OHR.VolcanoLlaima_EN-CN5183277848_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/linux_vsftpd/","title":"Ubuntu 20.04.2 配置 vsftpd 的脑坑"},{"content":"Home-brew 简介 先介绍一下本篇文章的主角：Home-brew，它是一款macOS的软件包管理器，当然也同样适用于Linux（不会真的有人 Linux 服务器用 brew 吧？）。\n它的官网上有关于 brew 非常详细的文档，大家可以自己去看看。介绍了以后就到了我们的安装环节了！\n安装 Brew 安装 brew 非常的简单，就需要一条命令就可以了，当然大家 99% 会遇到网络问题，安装过程中建议最好用 v 要不然缓慢的到超时的网络会让你年轻漂亮有活力的 ?！\n1 /bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; brew 换源 平时我们执行 brew 命令安装软件的时候，跟以下 3 个仓库地址有关：\nbrew.git homebrew-core.git homebrew-bottles 通过以下操作将这 3 个仓库地址全部替换为 Alibaba 提供的地址\n替换 / 还原 brew.git 仓库地址 1 2 3 4 5 6 7 # 替换成阿里巴巴的 brew.git 仓库地址: cd \u0026#34;$(brew --repo)\u0026#34; git remote set-url origin https://mirrors.aliyun.com/homebrew/brew.git #======================================================= # 还原为官方提供的 brew.git 仓库地址 cd \u0026#34;$(brew --repo)\u0026#34; git remote set-url origin https://github.com/Homebrew/brew.git 替换 / 还原 homebrew-core.git 仓库地址 1 2 3 4 5 6 7 # 替换成阿里巴巴的 homebrew-core.git 仓库地址: cd \u0026#34;$(brew --repo)/Library/Taps/homebrew/homebrew-core\u0026#34; git remote set-url origin https://mirrors.aliyun.com/homebrew/homebrew-core.git #======================================================= # 还原为官方提供的 homebrew-core.git 仓库地址 cd \u0026#34;$(brew --repo)/Library/Taps/homebrew/homebrew-core\u0026#34; git remote set-url origin https://github.com/Homebrew/homebrew-core.git 替换 / 还原 homebrew-bottles 访问地址 这个步骤跟你的 macOS 系统使用的 shell 版本有关系\n所以，先来查看当前使用的 shell 版本\n1 2 3 echo $SHELL # 如果你的输出结果是 /bin/zsh，参考?的 zsh 终端操作方式 # 如果你的输出结果是 /bin/bash，参考?的 bash 终端操作方式 zsh 终端操作方式 1 2 3 4 5 6 7 8 # 替换成阿里巴巴的 homebrew-bottles 访问地址: echo \u0026#39;export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles\u0026#39; \u0026gt;\u0026gt; ~/.zshrc source ~/.zshrc #======================================================= # 还原为官方提供的 homebrew-bottles 访问地址 vi ~/.zshrc # 然后，删除 HOMEBREW_BOTTLE_DOMAIN 这一行配置 source ~/.zshrc bash 终端操作方式 1 2 3 4 5 6 7 8 # 替换 homebrew-bottles 访问 URL: echo \u0026#39;export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles\u0026#39; \u0026gt;\u0026gt; ~/.bash_profile source ~/.bash_profile #======================================================= # 还原为官方提供的 homebrew-bottles 访问地址 vi ~/.bash_profile # 然后，删除 HOMEBREW_BOTTLE_DOMAIN 这一行配置 source ~/.bash_profile 安装软件 当我们已经配置好国内源时，我们就可以正式使用 brew 来安装一些软件了\neg：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 JokerMBP L-🌵-轩-🍂-X ~ 812 ◯ : brew install nginx ⏎ [~] ==\u0026gt; Downloading https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles/bottles/ng ==\u0026gt; Downloading from https://d29vzk4ow07wi7.cloudfront.net/9deda8734db308f21e3c8 ######################################################################## 100.0% ==\u0026gt; Pouring nginx-1.19.5.big_sur.bottle.tar.gz ==\u0026gt; Caveats Docroot is: /usr/local/var/www The default port has been set in /usr/local/etc/nginx/nginx.conf to 8080 so that nginx can run without sudo. nginx will load all files in /usr/local/etc/nginx/servers/. To have launchd start nginx now and restart at login: brew services start nginx Or, if you don\u0026#39;t want/need a background service you can just run: nginx ==\u0026gt; Summary 🍺 /usr/local/Cellar/nginx/1.19.5: 25 files, 2.2MB ==\u0026gt; `brew cleanup` has not been run in 30 days, running now... Pruned 0 symbolic links and 1 directories from /usr/local 可以看到在安装完成后 brew 给了我们很多有用的信息\n我先来总结一下：\n1：Docroot is: /usr/local/var/www\n默认文档位置在 /usr/local/var/www\n2：The default port has been set in /usr/local/etc/nginx/nginx.conf to 8080\n你丫的没有用sudo安装，我只能把默认端口给设置为 8080，你丫要是想改默认端口就要修改 /usr/local/etc/nginx/nginx.conf里的配置\n3：To have launchd start nginx now and restart at login: brew services start nginx\n你小子要想让 nginx 开机自启动就要运行： brew services start nginx\n4：if you don\u0026rsquo;t want/need a background service you can just run: nginx\n你小子 要死 要是想让 nginx 不开机自启动，直接运行命令： nginx 就可以了。\n就这一小段话，如果你没有注意，或者直接忽略了，那你可能在以后的使用过程中就会遇到很多的麻烦事！而且百度还可能找不到问题的答案！所以我就建议大家在安装完软件以后留意一下这个Tips。\n设置软件开机自启 既然 brew 可以设置开机自启动那这篇文章还有啥意义？\n其实只是想让大家了解 macOS 开机自启的原理而已\n现在大家应该都知道了 brew 安装软件的具体位置了，没错就是在 /usr/local/Cellar 文件夹下面，比如说我安装的 nginx 就在 /usr/local/Cellar/nginx/ 下面。我们不妨看看文件夹里面有啥：\n1 2 3 4 5 6 7 8 9 10 JokerMBP L-?-轩-?-X /usr/local/Cellar/nginx 978 ◯ : ls 1.19.5 total 640 -rw-r--r-- 1 frelon staff 300K 11 24 23:06 CHANGES -rw-r--r-- 1 frelon staff 791B 2 22 20:09 INSTALL_RECEIPT.json -rw-r--r-- 1 frelon staff 1.4K 11 24 23:06 LICENSE -rw-r--r-- 1 frelon staff 49B 11 24 23:06 README drwxr-xr-x 3 frelon staff 96B 11 24 23:06 bin -rw-r--r-- 1 frelon staff 571B 2 22 20:09 homebrew.mxcl.nginx.plist lrwxr-xr-x 1 frelon staff 16B 2 22 20:09 html -\u0026gt; ../../../var/www drwxr-xr-x 4 frelon staff 128B 11 24 23:06 share 可以看到啊，哪里有一个显眼的 .plist 结尾的文件，此时此刻我相信大家心里面肯定有答案了，开机自启动肯定和这个文件有着千丝万缕的关系。大家不要急哈，一步一步来！先把这个文件软连接到 /Library/LaunchAgents/ 目录下，然后就可以执行\n1 launchctl load /Library/LaunchAgents/homebrew.mxcl.xxx.plist 此时此刻这个软件就可以开机自启了！要知道有一些小众软件并不能通过 brew 安装，我们只能手动编译安装，这个时候如果我们想要使它开机自启动怎么办？没有了 brew 是不是突然就不知所措了！其实这个 .plist 文件我们自己也是可以手动编写的，我们再通过上面的步骤是不是就可以很容易的就让一个自己编译的软件开机自启动了。\n本文参考博客园用户：wygbbb \u0026amp; tulintao 的文章而写，侵联删。\n","date":"2021-02-22T19:34:20Z","image":"https://cn.bing.com/th?id=OHR.TurfHouse_EN-CN2756398853_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/macos_brew_installed_app_auto_start_at_boot/","title":"macOS设置brew安装的软件自启动"},{"content":"Phicomm N1 刷 OpenWrt 做旁路由 众所周知啊！N1 这个产品呢比较的全能，可以刷armbian、Openwrt、Android 电视盒子甚至windows也都不在话下。\n在去年也是以低价入手了一个盒子，但是一直就没有机会捯饬，因为我个人还有一个 x86 架构的低功耗小主机用于个人服务器使用，并且也是在 Docker 里面部署了一个 Openwrt 做旁路由来方便自己上墙，其实我个人是比较喜欢这种旁路由的方式的。因为连上局域网以后必须要手动指定网关才可以上墙，直接 DHCP 分配的用户没有任何影响，这样就不会对没有需求的用户产生影响。\n这里说一下我的设备，我的 N1 买回来就是刷好的电视盒子系统，所以就没有降级的步骤了，但是降级工具包都包含在了 N1 工具包里面了可以自行百度如何降级，其实都非常简单，输入一个 IP 地址回车就可以了。\n我个人是准备把 Openwrt 刷入自带的 emmc 里面的，这样可以省很多事，而且运行速度比在 U 盘里面有天然优势。\n环境准备 斐讯 N1\n适配 N1 的 OpenWrt 镜像\nN1工具包（非必须）\nU 盘一个（选好一点的 U 盘，可以上 USB3.0）\nEtcher / rufus （写盘工具）\n在这里给大家推荐就几个 N1 的 Openwrt 镜像\nGitHub：tuanqing的固件\nGitHub：IvanSolis1989的固件\n恩山大佬：flippy的固件\n有一点需要注意，上面两个 GitHub 的固件都是没有无线网的，只有 flippy 的固件才有 WI-FI，自己看着选吧！\n刻录镜像 从上面任意的一个镜像里选一个下载然后使用刻录工具来把镜像刻录进 U 盘里，这一步基本上不会出现什么问题。注意 U 盘里面的数据记得备份，写盘会把 U 盘里面的数据全部抹除。\n引导 OpenWrt 启动 这一步需要先把 N1 启动连接好网线，插上刚刚刻录好的 U 盘，打开 N1 的 adb，然后可以去路由器管理界面查看 N1 盒子的 IP 地址，然后利用工具包里面的批处理脚本（N1 盒子进入刷机模式。bat）来引导 N1 从 Android 电视盒子重启到 Openwrt 中。基本上都是一键操作非常简单。\n把 OpenWrt 写入 emmc 进过上面的步骤后，不意外的话你就已经在 OpenWrt 的命令行里面了，然后运行下面的命令\n1 2 cd ~ ./install #然后按下Tab，具体脚本名我已经忘记了，但是运行这个准没错 然后就是脚本自动运行了，然后需要重启一下就可以了，这样 OpenWrt 就写入了我们的 emmc 了\n1 iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE 据说要在防火墙规则里面加上上面这句，但是我没有加使用起来也没有啥感觉，可能是我家 100m 的宽带限制了吧\n顺带说一下 OpenWrt 换源\n1 sed -i \u0026#39;s/downloads.openwrt.org/mirrors.ustc.edu.cn\\/openwrt/g\u0026#39; /etc/opkg/distfeeds.conf ","date":"2021-01-28T21:33:56Z","image":"https://cn.bing.com/th?id=OHR.TwinsDenning_EN-CN2137499137_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/phicomm_n1_openwrt/","title":"Phicomm N1 刷 OpenWrt 做旁路由"},{"content":"数学思想 先声明一下！我是数学菜鸡！以下内容是我个人总结在学习数学中的一些重要的数学思想，写的不好！望海涵！!!\n无中生一 这个是我最先总结出来的了， 所谓无中生一就是我们在做题目时常常会遇到一些写着写着就没办法写下去的题目，也不是写错了，但就是卡在了这里想了半天也没有想出来可以用什么公式来解决问题！ 这个时候我们就需要重新审视一下题目了， 我们真的应该这样写吗？看例题\n$$ 求 f(x)=ln( \\sqrt{1+x^2} +x) 的奇偶性 $$$$ f(-x) = ln(\\sqrt{1+(-x)^2}-x) $$$$ f(-x) = ln(\\sqrt{1+x^2}-x) $$$$ f(-x)=ln[(\\sqrt{1+x^2}-x)*\\frac{\\sqrt{x^2+1}+x}{\\sqrt{x^2+1}+x}] $$$$ 因为后面的\\frac{\\sqrt{x^2+1}+x}{\\sqrt{x^2+1}+x}为一,对整个结果没有影响可得 $$$$ f(-x)=ln\\frac {(\\sqrt{x^2+1}+x)*(\\sqrt{1+x^2}-x)} {\\sqrt{x^2+1}+x} $$$$ f(-x)=ln(\\frac{1}{\\sqrt{x^2+1}+x})=ln(\\sqrt{x^2+1}+x)^{-1}=-ln(\\sqrt{x^2+1}+x) $$文章持续更新 ing❤️\n不更新了，数学太难了，放弃了！！！💔 我是傻X！我不配学数学\n","date":"2020-12-06T17:18:43Z","image":"https://cn.bing.com/th?id=OHR.JinliStreet_EN-CN2006370773_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/math_thought/","title":"数学思想"},{"content":"Ubuntu一直是我喜欢用的Linux发行版本,界面简洁,上手简单,用户广泛\n在Ubuntu中使用docker时,一直就有两个问题困扰着我\nUbuntu pull 镜像速度太缓慢 这个问题百度一搜一大把,但是作为个人学习 类型的博客,还是记录一下的好?\nAliyun 镜像加速大法 打开aliyun的docker加速登陆页面,虽然操作略微繁琐,但是我个人觉得这是速度最快的也是最稳定的\n然后找到 容器镜像服务 \u0026gt; 镜像加速器 根据提升来操作就可以啦\n其中Docker 1.10.0版本以上的直接就可以在 /etc/docker/daemon.json文件里加上你的专属加速链接\n1 2 sudo systemctl daemon-reload sudo systemctl restart docker 少侠记得重启加载一下配置文件\nDocker 网桥IP和实际物理IP冲突 虽然这个问题大家不一定会遇到,因为我们使用的大部分的路由器都是192开头的,docker的网桥IP则是选择172开头的,但是在学校,公司等场所就很容易出现冲突,而且一旦出现冲突你还不一定会想到问题出现在Docker网桥IP的问题上,所以当我遇到了这个问题我也是半天才反应过来要修改docker网桥IP,下面看操作 !\n一样 ! 编辑上面的/etc/docker/daemon.json文件,\n1 2 3 4 { \u0026#34;registry-mirrors\u0026#34;: [\u0026#34;https://xxxxxxxxxxxx.mirror.aliyuncs.com\u0026#34;], \u0026#34;bip\u0026#34;:\u0026#34;192.168.66.1/24\u0026#34; #换成你想要修改的ip网段 } 需要注意的是这个文件是json,保存的时候检查一下是否是json格式的\n1 2 sudo systemctl daemon-reload sudo systemctl restart docker 少侠记得重启加载一下配置文件\n","date":"2020-11-15T07:02:33Z","image":"https://cn.bing.com/th?id=OHR.Trevi_EN-CN2352584327_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/ubuntu_docker_tips/","title":"Ubuntu \u0026\u0026 Docker 使用建议"},{"content":"众所周知啊，OpenWrt 是一个非常优秀的路由器 OS,但是因为是为路由器优化的，所以我们在使用的时候就会遇到各种各样的问题，就比如安装 ohmyzsh\n因为网络问题，咱可以直接去GitHub看源码，顺便 Ctrl C / V\n安装依赖 1 2 3 4 5 opkg update opkg install zsh git git-http ca-certificates curl sh -c \u0026#34;$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)\u0026#34; #如果你Timeout了,不要慌!去Github下载源码,手动安装吧 ./install.sh 没了 开始造作啦！?\n如果你内存空间不够安装 zsh,那你可以看看飞尘@IT草的文章,最小化安装zsh后,仅占用不到的1m空间\n","date":"2020-10-07T21:33:56Z","image":"https://cn.bing.com/th?id=OHR.DalmatianPelicans_EN-CN7342550146_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/openwrt_install_zsh/","title":"记录一次OpenWrt安装zsh"},{"content":"OpenStack 常用命令 MySQL 1 2 show variables like \u0026#34;storage_engine\u0026#34;;\t//查询默认存储引擎InnoDB show variables like \u0026#34;have%\u0026#34;;\t//查询支持的存储引擎 Keyston 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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 source /etc/keystone/admin-openrc.sh openstack user create --domain demo --password password testuser //--domain为所属域 --password为密码 testuser是用户名 [root@controller ~]# openstack user list //查看用户列表 +----------------------------------+----------+ | ID | Name | +----------------------------------+----------+ | 1877ca2b73b04380af77a5cd3d057734 | demo | | 21a0b28505a140b9b54ca5a20542746c | glance | | 65e4fa15c643474d99f0b7950b5328bb | neutron | | 710f538564124ecd9fdcfaef40792e84 | admin | | 7caa95a15cb54d789d69e511c8214cb8 | nova | | cce5f695864940a0a32eab0d2015528c | testuser | +----------------------------------+----------+ [root@controller ~]# openstack endpoint list //endpoint列表 +---------------+-----------+--------------+--------------+---------+-----------+------------------+ | ID | Region | Service Name | Service Type | Enabled | Interface | URL | +---------------+-----------+--------------+--------------+---------+-----------+------------------+ | 1f7824e81d9f4 | RegionOne | neutron | network | True | internal | http://controlle | | f04b595b97a0a | | | | | | r:9696 | | e78f46 | | | | | | | | 399280c404a54 | RegionOne | glance | image | True | public | http://controlle | | a1bb314531c2b | | | | | | r:9292 | | 880ca1 | | | | | | | | . ... ... ... ... ... ... ... ... ... ... ... 略 ... ... ... ... ... ... ... ... ... ... ... . | | fcb27923a6674 | RegionOne | neutron | network | True | public | http://controlle | | 064a5f6d6ddd6 | | | | | | r:9696 | | 90fa54 | | | | | | | +---------------+-----------+--------------+--------------+---------+-----------+------------------+ [root@controller ~]# openstack role list //查询OpenStack的角色列表 +----------------------------------+-------+ | ID | Name | +----------------------------------+-------+ | 2a818016714e4f828d96e05475a67208 | user | | a38871259df34ea39884b9019cea926b | admin | +----------------------------------+-------+ [root@controller ~]# openstack service list //查看keystone 服务 +----------------------------------+----------+----------+ | ID | Name | Type | +----------------------------------+----------+----------+ | 40d9e5252f5f4967bc336542e6faab23 | glance | image | | 442e7454d7244037b7fdd05a96440dec | keystone | identity | | 6a95c79cc01147029bf79b5dfa3304f5 | neutron | network | | 8a78c7d6a9af432f89ae9a4d185b29d5 | nova | compute | | d821457f69fa4838ba42074652649548 | neutron | network | +----------------------------------+----------+----------+ [root@controller ~]# openstack project list //查看keystone租户 +----------------------------------+---------+ | ID | Name | +----------------------------------+---------+ | 00fa84b7a654447886421978253a2829 | demo | | 869f732ec387457ba9cfdcb424cc8ea2 | admin | | b421cbca4234472a94de962c634b5bb9 | service | +----------------------------------+---------+ [root@controller ~]# openstack project show demo //查看租户情况,show 后面加上上面查询到的用户 name +-------------+----------------------------------+ | Field | Value | +-------------+----------------------------------+ | description | Demo Project | | domain_id | ad5088bf450145819f9338635ebfb7a6 | | enabled | True | | id | 00fa84b7a654447886421978253a2829 | | is_domain | False | | name | demo | | parent_id | ad5088bf450145819f9338635ebfb7a6 | +-------------+----------------------------------+ [root@controller ~]# openstack domain create --description \u0026#34;PPX_PiXiaoMei_Only\u0026#34; NeiHanDuanZi_TV //创建域 //--description \u0026#34;PPX_PiXiaoMei_Only\u0026#34; 不是必须的, 下同 +-------------+----------------------------------+ | Field | Value | +-------------+----------------------------------+ | description | PPX_PiXiaoMei_Only | | enabled | True | | id | 57581f0aa311416c88c9fdb36688a46e | | name | NeiHanDuanZi_TV | +-------------+----------------------------------+ [root@controller ~]# openstack project create --domain NeiHanDuanZi_TV --description \u0026#34;PDL\u0026#34; PXM //创建项目（租户） +-------------+----------------------------------+ | Field | Value | +-------------+----------------------------------+ | description | PDL | | domain_id | 57581f0aa311416c88c9fdb36688a46e | | enabled | True | | id | 4b6c52f02496465ebc70447b740a043c | | is_domain | False | | name | PXM | | parent_id | 57581f0aa311416c88c9fdb36688a46e | +-------------+----------------------------------+ [root@controller ~]# openstack user create --domain NeiHanDuanZi_TV --password-prompt frelon //创建用户 User Password: Repeat User Password: +-----------+----------------------------------+ | Field | Value | +-----------+----------------------------------+ | domain_id | 57581f0aa311416c88c9fdb36688a46e | | enabled | True | | id | 5ac456db906a43e8a8427ae7f3a5334a | | name | frelon | +-----------+----------------------------------+ [root@controller ~]# openstack role create oiL //创建角色 +-----------+----------------------------------+ | Field | Value | +-----------+----------------------------------+ | domain_id | None | | id | 893a47b1b1ce4f558858418da04c86e4 | | name | oiL | +-----------+----------------------------------+ openstack role add --project PXM --user frelon oiL //为某项目中的某用户指定角色 Tenant也叫project,译为租户 , 所有的资源(包括但不限于CPU,Memory,Disk等)都属于Tenant , User 本生并没有任何调度资源的权力,只有把 User加入Tenant后,用户才有资源的调度权限,Tenant就相当于一个 用户组,只不过在OpenStack这里被强化了,用户必须加入一个或多个用户组才可以调度资源\nRole主要是解决用户登陆后的权限问题,即用户登录以后可以干什么,有没有权利操作资源等等,简而言之就是鉴权,一个用户必须有一个或多个Role才有权力执行相应操作. Role是全局的,所有Domain里的Group,User\nGroup是用来配合Role来使用的,给Group加上相应的权限,再把用户加入Group该用户就可以获得Group所有的权限了,这样就方便了Role的分配\nDomain 常常表示一个客户/合作伙伴,并且 project,Group,User在同一个Domain里面不可以重复\nglance 1 2 3 4 5 6 7 8 glance image-create --name \u0026#34;testone\u0026#34; --disk-format \u0026#34;qcow2\u0026#34; --container-format bare --progress \u0026lt; /root/CentOS_6.5_x86_64_XD.qcow2 //上传镜像 并 用--name 命名为 \u0026#34;testone\u0026#34; openstack image set testone --name examimage //修改已上传的镜像名称 openstack image list //列出已上传的镜像列表 nova 1 2 nova usage-list //查询资源使用情况 neutron 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 neutron router-list //列出当前存在的网络/路由器 neutron router-show + id/name [root@controller ~]# neutron router-list +--------------------------------------+------+-----------------------+-------------+-------+ | id | name | external_gateway_info | distributed | ha | +--------------------------------------+------+-----------------------+-------------+-------+ | bb46e65a-ad51-4c13-b1c6-51c845dcfb52 | cvn | null | False | False | +--------------------------------------+------+-----------------------+-------------+-------+ [root@controller ~]# neutron router-show bb46e65a-ad51-4c13-b1c6-51c845dcfb52 +-------------------------+--------------------------------------+ | Field | Value | +-------------------------+--------------------------------------+ | admin_state_up | True | | availability_zone_hints | | | availability_zones | nova | | description | | | distributed | False | | external_gateway_info | | | ha | False | | id | bb46e65a-ad51-4c13-b1c6-51c845dcfb52 | | name | cvn | | routes | | | status | ACTIVE | | tenant_id | 869f732ec387457ba9cfdcb424cc8ea2 | +-------------------------+--------------------------------------+ trove OpenStack命令大全 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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 access token create project create acl delete project delete acl get project list acl submit project set acl user add project show acl user remove ptr record list aggregate add host ptr record set aggregate create ptr record show aggregate delete ptr record unset aggregate list quota set aggregate remove host quota show aggregate set recordset create aggregate show recordset delete availability zone list recordset list backup create recordset set backup delete recordset show backup list region create backup restore region delete backup show region list ca get region set ca list region show catalog list request token authorize catalog show request token create command list role add complete role assignment list compute agent create role create compute agent delete role delete compute agent list role list compute agent set role remove compute service delete role set compute service list role show compute service set router create configuration show router delete console log show router list console url show router set consumer create router show consumer delete secret container create consumer list secret container delete consumer set secret container get consumer show secret container list container create secret delete container delete secret get container list secret list container save secret order create container set secret order delete container show secret order get container unset secret order list credential create secret store credential delete secret update credential list security group create credential set security group delete credential show security group list dataprocessing cluster create security group rule create dataprocessing cluster delete security group rule delete dataprocessing cluster list security group rule list dataprocessing cluster scale security group rule show dataprocessing cluster show security group set dataprocessing cluster template create security group show dataprocessing cluster template delete server add security group dataprocessing cluster template list server add volume dataprocessing cluster template show server create dataprocessing cluster template update server delete dataprocessing cluster update server dump create dataprocessing cluster verification server image create dataprocessing data source create server list dataprocessing data source delete server lock dataprocessing data source list server migrate dataprocessing data source show server pause dataprocessing data source update server reboot dataprocessing image list server rebuild dataprocessing image register server remove security group dataprocessing image show server remove volume dataprocessing image tags add server rescue dataprocessing image tags remove server resize dataprocessing image tags set server resume dataprocessing image unregister server set dataprocessing job binary create server shelve dataprocessing job binary delete server show dataprocessing job binary download server ssh dataprocessing job binary list server start dataprocessing job binary show server stop dataprocessing job binary update server suspend dataprocessing job delete server unlock dataprocessing job execute server unpause dataprocessing job list server unrescue dataprocessing job show server unset dataprocessing job template create server unshelve dataprocessing job template delete service create dataprocessing job template list service delete dataprocessing job template show service list dataprocessing job template update service provider create dataprocessing job type configs get service provider delete dataprocessing job type list service provider list dataprocessing job update service provider set dataprocessing node group template create service provider show dataprocessing node group template delete service set dataprocessing node group template list service show dataprocessing node group template show snapshot create dataprocessing node group template update snapshot delete dataprocessing plugin configs get snapshot list dataprocessing plugin list snapshot set dataprocessing plugin show snapshot show domain create snapshot unset domain delete software config create domain list software config delete domain set software config list domain show software config show ec2 credentials create software deployment create ec2 credentials delete software deployment delete ec2 credentials list software deployment list ec2 credentials show software deployment metadata show endpoint create software deployment output show endpoint delete software deployment show endpoint list stack abandon endpoint set stack adopt endpoint show stack cancel extension list stack check federation domain list stack create federation project list stack delete federation protocol create stack event list federation protocol delete stack event show federation protocol list stack hook clear federation protocol set stack hook poll federation protocol show stack list flavor create stack output list flavor delete stack output show flavor list stack resource list flavor set stack resource mark unhealthy flavor show stack resource metadata flavor unset stack resource show group add user stack resource signal group contains user stack resume group create stack show group delete stack snapshot create group list stack snapshot delete group remove user stack snapshot list group set stack snapshot restore group show stack snapshot show help stack suspend host list stack template show host show stack update hypervisor list subnet delete hypervisor show subnet list hypervisor stats show subnet pool delete identity provider create subnet pool list identity provider delete subnet pool show identity provider list subnet show identity provider set tld create identity provider show tld delete image add project tld list image create tld set image delete tld show image list token issue image remove project token revoke image save trust create image set trust delete image show trust list ip fixed add trust show ip fixed remove usage list ip floating add usage show ip floating create user create ip floating delete user delete ip floating list user list ip floating pool list user password set ip floating remove user set ip floating show user show keypair create volume create keypair delete volume delete keypair list volume list keypair show volume qos associate limits show volume qos create mapping create volume qos delete mapping delete volume qos disassociate mapping list volume qos list mapping set volume qos set mapping show volume qos show module list volume qos unset network create volume set network delete volume show network list volume type create network set volume type delete network show volume type list object create volume type set object delete volume type show object list volume type unset object save volume unset object set zone abandon object show zone axfr object store account set zone blacklist create object store account show zone blacklist delete object store account unset zone blacklist list object unset zone blacklist set orchestration build info zone blacklist show orchestration resource type list zone create orchestration resource type show zone delete orchestration service list zone list orchestration template function list zone set orchestration template version list zone show policy create zone transfer accept request policy delete zone transfer accept show policy list zone transfer request create policy set zone transfer request delete policy show zone transfer request list port delete zone transfer request set port show zone transfer request show ","date":"2020-09-25T21:33:56Z","image":"https://cn.bing.com/th?id=OHR.Porto_EN-CN2330328060_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/openstack_command/","title":"OpenStack 常用命令"},{"content":"CentOS 7 磁盘扩容 假设已经添加了磁盘,到了需要在系统里操作的步骤\n对新增加的硬盘进行分区 1 2 3 4 5 6 7 8 9 10 11 12 fidsk -l fdisk /dev/sda (根据实际情况选择) n\t(新增加一个分区) p\t(区类型选择为主分区) 回车\t(分区号) 回车　（起始扇区） 回车　（结束扇区） t 上面的分区号 8e 将分区“Linux”的类型更改为“Linux LVM” w 保存 reboot 对新增加的硬盘格式化 1 mkfs.ext4 /dev/sda3 添加新LVM到已有的LVM组，实现扩容 1 2 3 4 5 6 lvm　进入lvm管理 lvm\u0026gt; pvcreate /dev/sda3 这是初始化刚才的分区，必须的 lvm\u0026gt;vgextend centos /dev/sda3 将初始化过的分区加入到虚拟卷组vg_dc01 lvm\u0026gt;lvextend -L +100G /dev/mapper/centos-root　扩展已有卷的容量（注意容量大小） lvm\u0026gt;pvdisplay　查看卷容量，这时你会看到一个很大的卷了 lvm\u0026gt;quit　以上只是卷扩容了，下面是文件系统的真正扩容，输入以下命令：\n1 resize2fs /dev/mapper/centos-root resize2fs: Bad magic number in super-block 当尝试打开 /dev/mapper/centos-root 时\n报错：当尝试打开 /dev/mapper/centos-root 时 找不到有效的文件系统超级块\n因为我的centos7的某些分区用的是xfs的文件系统（使用df -T查看即可知道）\n将resize2fs替换为xfs_growfs，重新执行一遍即可，如下：\n1 xfs_growfs /dev/mapper/centos-root 步骤记录 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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 [root@last ~]# fdisk /dev/sda Welcome to fdisk (util-linux 2.23.2). Changes will remain in memory only, until you decide to write them. Be careful before using the write command. Command (m for help): p Disk /dev/sda: 107.4 GB, 107374182400 bytes, 209715200 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk label type: dos Disk identifier: 0x000ae200 Device Boot Start End Blocks Id System /dev/sda1 * 2048 2099199 1048576 83 Linux /dev/sda2 2099200 41943039 19921920 8e Linux LVM Command (m for help): n Partition type: p primary (2 primary, 0 extended, 2 free) e extended Select (default p): Using default response p Partition number (3,4, default 3): First sector (41943040-209715199, default 41943040): Using default value 41943040 Last sector, +sectors or +size{K,M,G} (41943040-209715199, default 209715199): Using default value 209715199 Partition 3 of type Linux and of size 80 GiB is set Command (m for help): p Disk /dev/sda: 107.4 GB, 107374182400 bytes, 209715200 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk label type: dos Disk identifier: 0x000ae200 Device Boot Start End Blocks Id System /dev/sda1 * 2048 2099199 1048576 83 Linux /dev/sda2 2099200 41943039 19921920 8e Linux LVM /dev/sda3 41943040 209715199 83886080 83 Linux Command (m for help): t Partition number (1-3, default 3): 8e Partition number (1-3, default 3): Hex code (type L to list all codes): 8e Changed type of partition \u0026#39;Linux\u0026#39; to \u0026#39;Linux LVM\u0026#39; Command (m for help): p Disk /dev/sda: 107.4 GB, 107374182400 bytes, 209715200 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk label type: dos Disk identifier: 0x000ae200 Device Boot Start End Blocks Id System /dev/sda1 * 2048 2099199 1048576 83 Linux /dev/sda2 2099200 41943039 19921920 8e Linux LVM /dev/sda3 41943040 209715199 83886080 8e Linux LVM Command (m for help): w The partition table has been altered! Calling ioctl() to re-read partition table. WARNING: Re-reading the partition table failed with error 16: Device or resource busy. The kernel still uses the old table. The new table will be used at the next reboot or after you run partprobe(8) or kpartx(8) Syncing disks. [root@last ~]# [root@last ~]# reboot Connection to 192.168.50.130 closed by remote host. Connection to 192.168.50.130 closed. ➜ frelon ➜ frelon ➜ frelon ssh root@192.168.50.130 Last login: Sat Sep 19 04:08:59 2020 from 192.168.50.1 [root@last ~]# mkfs.ext4 /dev/sda3 mke2fs 1.42.9 (28-Dec-2013) Filesystem label= OS type: Linux Block size=4096 (log=2) Fragment size=4096 (log=2) Stride=0 blocks, Stripe width=0 blocks 5242880 inodes, 20971520 blocks 1048576 blocks (5.00%) reserved for the super user First data block=0 Maximum filesystem blocks=2168455168 640 block groups 32768 blocks per group, 32768 fragments per group 8192 inodes per group Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 4096000, 7962624, 11239424, 20480000 Allocating group tables: done Writing inode tables: done Creating journal (32768 blocks): done Writing superblocks and filesystem accounting information: done [root@last ~]# [root@last ~]# [root@last ~]# lvm lvm\u0026gt; pvcreate /dev/sda3 WARNING: ext4 signature detected on /dev/sda3 at offset 1080. Wipe it? [y/n]: y Wiping ext4 signature on /dev/sda3. Physical volume \u0026#34;/dev/sda3\u0026#34; successfully created. lvm\u0026gt; lvm\u0026gt; vgextend centos /dev/sda3 Volume group \u0026#34;centos\u0026#34; successfully extended lvm\u0026gt; lvextend -L +79G /dev/mapper/centos-root Size of logical volume centos/root changed from \u0026lt;17.00 GiB (4351 extents) to \u0026lt;96.00 GiB (24575 extents). Logical volume centos/root successfully resized. lvm\u0026gt; quit Exiting. [root@last ~]# xfs_ xfs_admin xfs_db xfs_fsr xfs_io xfs_metadump xfs_quota xfs_bmap xfs_estimate xfs_growfs xfs_logprint xfs_mkfile xfs_repair xfs_copy xfs_freeze xfs_info xfs_mdrestore xfs_ncheck xfs_rtcp [root@last ~]# xfs_ xfs_admin xfs_db xfs_fsr xfs_io xfs_metadump xfs_quota xfs_bmap xfs_estimate xfs_growfs xfs_logprint xfs_mkfile xfs_repair xfs_copy xfs_freeze xfs_info xfs_mdrestore xfs_ncheck xfs_rtcp [root@last ~]# xfs_growfs /dev/mapper/c centos-root centos-swap control [root@last ~]# xfs_growfs /dev/mapper/centos-root meta-data=/dev/mapper/centos-root isize=512 agcount=4, agsize=1113856 blks = sectsz=512 attr=2, projid32bit=1 = crc=1 finobt=0 spinodes=0 data = bsize=4096 blocks=4455424, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0 ftype=1 log =internal bsize=4096 blocks=2560, version=2 = sectsz=512 sunit=0 blks, lazy-count=1 realtime =none extsz=4096 blocks=0, rtextents=0 data blocks changed from 4455424 to 25164800 [root@last ~]# [root@last ~]# [root@last ~]# df -h Filesystem Size Used Avail Use% Mounted on devtmpfs 898M 0 898M 0% /dev tmpfs 910M 0 910M 0% /dev/shm tmpfs 910M 9.5M 901M 2% /run tmpfs 910M 0 910M 0% /sys/fs/cgroup /dev/mapper/centos-root 96G 1.4G 95G 2% / /dev/sda1 1014M 150M 865M 15% /boot tmpfs 182M 0 182M 0% /run/user/0 由于在安装centos系统的时候，如果在安装时没有分配磁盘空间，选择的是默认分配的，在安装完成后，可以发现大容量磁盘往往分配在了home下面。\n如果要把home下面的磁盘空间分配到root磁盘下面。可以进行如下操作。\n查看CentOS的系统版本 1 2 [root@controller ~]# cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) 查看分区 df -h (centos-home和centos-root每人的名字可能不一样)\n1 2 3 4 5 6 7 8 9 10 11 12 13 [root@controller ~]# df -h Filesystem Size Used Avail Use% Mounted on /dev/sda3 50G 8.5G 42G 17% / devtmpfs 1.9G 0 1.9G 0% /dev tmpfs 1.9G 0 1.9G 0% /dev/shm tmpfs 1.9G 8.7M 1.9G 1% /run tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup /dev/sda6 20G 33M 20G 1% /home /dev/sda5 26G 804M 26G 4% /var /dev/sda2 2.0G 125M 1.9G 7% /boot /dev/loop0 4.1G 4.1G 0 100% /mnt/centos /dev/loop1 2.7G 2.7G 0 100% /mnt/xiandi tmpfs 378M 0 378M 0% /run/user/0 备份home分区文件 1 2 3 4 [root@controller ~]# tar cvzf /mnt/home.tar /home/ tar: Removing leading `/\u0026#39; from member names /home/ /home/frelon/ 卸载/home，如果无法卸载，先终止使用/home文件系统的进程 1 [root@controller ~]# umount /home （卸载） 如果卸载时，发现/home在使用中，所以先终止。\n1 [root@controller ~]# fuser -km /home/（终止） 再次卸载，没有报错，表示成功。\n1 [root@controller ~]# umount /home （卸载） 删除/home所在的lv 1 lvremove /dev/mapper/centos-home 扩展/root所在的lv 1 lvextend -L +100G /dev/mapper/centos-root 扩展/root文件系统 1 xfs_growfs /dev/mapper/centos-root 重新创建home lv （创建时计算好剩余的磁盘容量，建议比剩余小1G左右） 1 lvcreate -L 41G -n /dev/mapper/centos-home 创建文件系统 1 mkfs.xfs /dev/mapper/centos-home 挂载home 1 mount /dev/mapper/centos-home home文件恢复 1 tar xvf /tmp/home.tar -C /home/ 再次使用df -h查看系统磁盘大小 可以看到home下面100G的磁盘容量已经转移到root下面了，至此，转移任务结束。此为在CentOS7.2系统下测试使用的，在CentOS6版本下还没测试过。\n声明 : 文章来自互联网(实在是找不到原作者链接出处了),侵联删\n","date":"2020-09-25T16:51:16Z","image":"https://cn.bing.com/th?id=OHR.LeMorneBrabant_EN-CN8130983751_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/disk_expansion/","title":"CentOS 7 磁盘扩容"},{"content":"clang 学习笔记 什么是 c C 语言是一种通用的高级语言，最初是由丹尼斯·里奇在贝尔实验室为开发 UNIX 操作系统而设计的。C 语言最开始是于 1972 年在 DEC PDP-11 计算机上被首次实现。\n在 1978 年，布莱恩·柯林汉（Brian Kernighan）和丹尼斯·里奇（Dennis Ritchie）制作了 C 的第一个公开可用的描述，现在被称为 K\u0026amp;R 标准。\nUNIX 操作系统，C 编译器，和几乎所有的 UNIX 应用程序都是用 C 语言编写的。由于各种原因，C 语言现在已经成为一种广泛使用的专业语言。\n易于学习。 结构化语言。 它产生高效率的程序。 它可以处理底层的活动。 它可以在多种计算机平台上编译。 关于 C C 语言是为了编写 UNIX 操作系统而被发明的。 C 语言是以 B 语言为基础的，B 语言大概是在 1970 年被引进的。 C 语言标准是于 1988 年由美国国家标准协会（ANSI，全称 American National Standard Institute）制定的。 截至 1973 年，UNIX 操作系统完全使用 C 语言编写。 目前，C 语言是最广泛使用的系统程序设计语言。 大多数先进的软件都是使用 C 语言实现的。 当今最流行的 Linux 操作系统和 RDBMS（Relational Database Management System：关系数据库管理系统） MySQL 都是使用 C 语言编写的。 为什么要使用 C？ C 语言最初是用于系统开发工作，特别是组成操作系统的程序。由于 C 语言所产生的代码运行速度与汇编语言编写的代码运行速度几乎一样，所以采用 C 语言作为系统开发语言。下面列举几个使用 C 的实例：\n操作系统 语言编译器 汇编器 文本编辑器 打印机 网络驱动器 现代程序 数据库 语言解释器 实体工具 C 程序 一个 C 语言程序，可以是 3 行，也可以是数百万行，它可以写在一个或多个扩展名为 \u0026quot;.c\u0026quot; 的文本文件中，例如，hello.c。您可以使用 \u0026ldquo;sublime text3\u0026rdquo;、\u0026ldquo;vim\u0026rdquo;、\u0026ldquo;vscode\u0026rdquo; 或任何其他文本编辑器来编写您的 C 语言程序。\n下面看一个c语言入门实例\n1 2 3 4 5 6 7 8 9 #include \u0026lt;stdio.h\u0026gt; int main() { /* 我的第一个 C 程序 */ printf(\u0026#34;Hello, World! \\n\u0026#34;); return 0; } 所有的 C 语言程序都需要包含 main() 函数。 代码从 main() 函数开始执行。 /* \u0026hellip; */ 用于注释说明。 printf() 用于格式化输出到屏幕。printf() 函数在 \u0026ldquo;stdio.h\u0026rdquo; 头文件中声明。 stdio.h 是一个头文件 (标准输入输出头文件) , #include 是一个预处理命令，用来引入头文件。 当编译器遇到 printf() 函数时，如果没有找到 stdio.h 头文件，会发生编译错误。 return 0; 语句用于表示退出程序。 配置 c 环境 Linux 一般 Linux 自带 gcc , 终端输入 gcc -v 即可查看当前版本\n(本教程以Linux环境为基础)\nmacOS 安装xcode , 一劳永逸，并且可以减少你不少的麻烦，也可以自己安装，推荐使用brew包管理器来安装\nWindows 为了在 Windows 上安装 GCC，您需要安装 MinGW。为了安装 MinGW，请访问 MinGW 的主页 www.mingw.org，进入 MinGW 下载页面，下载最新版本的 MinGW 安装程序，命名格式为 MinGW- .exe。\n当安装 MinGW 时，您至少要安装 gcc-core、gcc-g++、binutils 和 MinGW runtime，但是一般情况下都会安装更多其他的项。\n添加您安装的 MinGW 的 bin 子目录到您的 PATH 环境变量中，这样您就可以在命令行中通过简单的名称来指定这些工具。\n当完成安装时，您可以从 Windows 命令行上运行 gcc、g++、ar、ranlib、dlltool 和其他一些 GNU 工具\nclang 程序结构 1 2 3 4 5 6 7 8 9 #include \u0026lt;stdio.h\u0026gt; int main() { /* 我的第一个 C 程序 */ printf(\u0026#34;Hello, World! \\n\u0026#34;); return 0; } 接下来我们讲解一下上面这段程序：\n程序的第一行 *#include * 是预处理器指令，告诉 C 编译器在实际编译之前要包含 stdio.h 文件。 下一行 int main() 是主函数，程序从这里开始执行。 下一行 /\u0026hellip;/ 将会被编译器忽略，这里放置程序的注释内容。它们被称为程序的注释。 下一行 printf(\u0026hellip;) 是 C 中另一个可用的函数，会在屏幕上显示消息 \u0026ldquo;Hello, World!\u0026quot;。 下一行 return 0; 终止 main() 函数，并返回值 0。 想要执行c程序，只需要简单的编译即可运行\n1 2 3 $ gcc hello.c -c hello $ ./hello Hello, World! clang 关键字 下表列出了 C 中的保留字。这些保留字不能作为常量名、变量名或其他标识符名称。\n关键字 说明 auto 声明自动变量 break 跳出当前循环 case 开关语句分支 char 声明字符型变量或函数返回值类型 const 定义常量，如果一个变量被 const 修饰，那么它的值就不能再被改变 continue 结束当前循环，开始下一轮循环 default 开关语句中的\u0026quot;其它\u0026quot;分支 do 循环语句的循环体 double 声明双精度浮点型变量或函数返回值类型 else 条件语句否定分支（与 if 连用） enum 声明枚举类型 extern 声明变量或函数是在其它文件或本文件的其他位置定义 float 声明浮点型变量或函数返回值类型 for 一种循环语句 goto 无条件跳转语句 if 条件语句 int 声明整型变量或函数 long 声明长整型变量或函数返回值类型 register 声明寄存器变量 return 子程序返回语句（可以带参数，也可不带参数） short 声明短整型变量或函数 signed 声明有符号类型变量或函数 sizeof 计算数据类型或变量长度（即所占字节数） static 声明静态变量 struct 声明结构体类型 switch 用于开关语句 typedef 用以给数据类型取别名 unsigned 声明无符号类型变量或函数 union 声明共用体类型 void 声明函数无返回值或无参数，声明无类型指针 volatile 说明变量在程序执行中可被隐含地改变 while 循环语句的循环条件 C99 新增关键字 _Bool _Complex _Imaginary inline restrict C11 新增关键字 _Alignas _Alignof _Atomic _Generic _Noreturn _Static_assert _Thread_local 看看就可以了，一般我们也不会傻 x 到用这些来命名变量\nC 数据类型 C 中的类型可分为以下几种：\n序号 类型与描述 1 基本类型： 它们是算术类型，包括两种类型：整数类型和浮点类型。 2 枚举类型： 它们也是算术类型，被用来定义在程序中只能赋予其一定的离散整数值的变量。 3 void 类型： 类型说明符 void 表明没有可用的值。 4 派生类型： 它们包括：指针类型、数组类型、结构类型、共用体类型和函数类型。 整数类型 下表列出了关于标准整数类型的存储大小和值范围的细节：\n类型 存储大小 值范围 char 1 字节 -128 到 127 或 0 到 255 unsigned char 1 字节 0 到 255 signed char 1 字节 -128 到 127 int 2 或 4 字节 -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647 unsigned int 2 或 4 字节 0 到 65,535 或 0 到 4,294,967,295 short 2 字节 -32,768 到 32,767 unsigned short 2 字节 0 到 65,535 long 4 字节 -2,147,483,648 到 2,147,483,647 unsigned long 4 字节 0 到 4,294,967,295 注意，各种类型的存储大小与系统位数有关，但目前通用的以 64 位系统为主。\n以下列出了 32 位系统与 64 位系统的存储大小的差别（windows 相同）：\n为了得到某个类型或某个变量在特定平台上的准确大小，您可以使用 sizeof 运算符。表达式 sizeof(type) 得到对象或类型的存储字节大小。下面的实例演示了获取 int 类型的大小：\n1 2 3 4 5 6 7 8 9 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;limits.h\u0026gt; int main() { printf(\u0026#34;int 存储大小 : %lu \\n\u0026#34;, sizeof(int)); return 0; } 1 int 存储大小 : 4 浮点类型 下表列出了关于标准浮点类型的存储大小、值范围和精度的细节：\n类型 存储大小 值范围 精度 float 4 字节 1.2E-38 到 3.4E+38 6 位小数 double 8 字节 2.3E-308 到 1.7E+308 15 位小数 long double 16 字节 3.4E-4932 到 1.1E+4932 19 位小数 void 类型 void 类型指定没有可用的值。它通常用于以下三种情况下：\n序号 类型与描述 1 函数返回为空 C 中有各种函数都不返回值，或者您可以说它们返回空。不返回值的函数的返回类型为空。例如 void exit (int status); 2 函数参数为空 C 中有各种函数不接受任何参数。不带参数的函数可以接受一个 void。例如 int rand(void); 3 指针指向 void 类型为 void * 的指针代表对象的地址，而不是类型。例如，内存分配函数 void*malloc( size_t size ); 返回指向 void 的指针，可以转换为任何数据类型。 如果现在您还是无法完全理解 void 类型，不用太担心，在后续的章节中我们将会详细讲解这些概念。\nNote 常用基本数据类型占用空间（64 位机器为例）\nchar ： 1 个字节 int ：4 个字节 float：4 个字节 double：8 个字节 基本类型书写\n整数\na，默认为 10 进制 ，10 ，20。 b，以 0 开头为 8 进制，045，021。 c.，以 0b 开头为 2 进制，0b11101101。 d，以 0x 开头为 16 进制，0x21458adf。 小数\n单精度常量：2.3f 。\n双精度常量：2.3，默认为双精度。\n字符型常量\n用英文单引号括起来，只保存一个字符\u0026rsquo;a\u0026rsquo;、\u0026lsquo;b\u0026rsquo; 、\u0026rsquo;*\u0026rsquo; ，还有转义字符 \u0026lsquo;\\n\u0026rsquo; 、\u0026rsquo;\\t\u0026rsquo;。\n字符串常量\n用英文的双引号引起来 可以保存多个字符：\u0026ldquo;abc\u0026rdquo;。\n1、数据类型转换：C 语言中如果一个表达式中含有不同类型的常量和变量，在计算时，会将它们自动转换为同一种类型；在 C 语言中也可以对数据类型进行强制转换；\n2、自动转换规则：\na）浮点数赋给整型，该浮点数小数被舍去； b）整数赋给浮点型，数值不变，但是被存储到相应的浮点型变量中； 3、强制类型转换形式： (类型说明符)(表达式)\n实例程序：\n1 2 3 4 5 6 7 8 9 10 11 #include\u0026lt;stdio.h\u0026gt; int main() { float f,x=3.6,y=5.2; int i=4,a,b; a=x+y; b=(int)(x+y); f=10/i; printf(\u0026#34;a=%d,b=%d,f=%f,x=%f\\n\u0026#34;,a,b,f,x); } 例中先计算 x+y 值为 8.8，然后赋值给 a，因为 a 为整型，所以自取整数部分 8，a=8;\n接下来 b 把 x+y 强制转换为整型;\n最后 10/i 是两个整数相除，结果仍为整数 2，把 2 赋给浮点数 f;\nx 为浮点型直接输出。\nclang 变量 类型 描述 char 通常是一个字节（八位）。这是一个整数类型。 int 对机器而言，整数的最自然的大小。 float 单精度浮点值。单精度是这样的格式，1 位符号，8 位指数，23 位小数。 double 双精度浮点值。双精度是 1 位符号，11 位指数，52 位小数。 void 表示类型的缺失。 C 中的变量声明 变量的声明有两种情况：\n1、一种是需要建立存储空间的。例如：int a 在声明的时候就已经建立了存储空间。 2、另一种是不需要建立存储空间的，通过使用 extern 关键字声明变量名而不定义它。 例如：extern int a 其中变量 a 可以在别的文件中定义的。 除非有extern关键字，否则都是变量的定义。 1 2 extern int i; //声明，不是定义 int i; //声明，也是定义 代码实例： 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 #include \u0026lt;stdio.h\u0026gt; // 函数外定义变量 x 和 y int x; int y; int addtwonum() { // 函数内声明变量 x 和 y 为外部变量 extern int x; extern int y; // 给外部变量（全局变量）x 和 y 赋值 x = 1; y = 2; return x+y; } int main() { int result; // 调用函数 addtwonum result = addtwonum(); printf(\u0026#34;result 为: %d\u0026#34;,result); return 0; } 如果我们将**addtwonum()**独立为一个文件呢？试试看！\n1 2 3 4 5 6 7 8 #include \u0026lt;stdio.h\u0026gt; /*外部变量声明*/ extern int x ; extern int y ; int addtwonum() { return x+y; } 1 2 3 4 5 6 7 8 9 10 11 12 13 #include \u0026lt;stdio.h\u0026gt; /*定义两个全局变量*/ int x=1; int y=2; int addtwonum(); int main(void) { int result; result = addtwonum(); printf(\u0026#34;result 为: %d\\n\u0026#34;,result); return 0; } 如果需要在一个源文件中引用另外一个源文件中定义的变量，我们只需在引用的文件中将变量加上 extern 关键字的声明即可。\n常量 整数常量 整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数：0x 或 0X 表示十六进制，0 表示八进制，不带前缀则默认表示十进制。\n整数常量也可以带一个后缀，后缀是 U 和 L 的组合，U 表示无符号整数（unsigned），L 表示长整数（long）。后缀可以是大写，也可以是小写，U 和 L 的顺序任意。\n以下是各种类型的整数常量的实例：\n1 2 3 4 5 6 7 85 /* 十进制 */ 0213 /* 八进制 */ 0x4b /* 十六进制 */ 30 /* 整数 */ 30u /* 无符号整数 */ 30l /* 长整数 */ 30ul /* 无符号长整数 */ 浮点常量 浮点常量由整数部分、小数点、小数部分和指数部分组成。您可以使用小数形式或者指数形式来表示浮点常量。\n当使用小数形式表示时，必须包含整数部分、小数部分，或同时包含两者。当使用指数形式表示时， 必须包含小数点、指数，或同时包含两者。带符号的指数是用 e 或 E 引入的。\n下面列举几个浮点常量的实例：\n1 2 3 4 5 3.14159 /* 合法的 */ 314159E-5L /* 合法的 */ 510E /* 非法的：不完整的指数 */ 210f /* 非法的：没有小数或指数 */ .e55 /* 非法的：缺少整数或分数 */ 字符常量 字符常量是括在单引号中，例如，\u0026lsquo;x\u0026rsquo; 可以存储在 char 类型的简单变量中。\n字符常量可以是一个普通的字符（例如 \u0026lsquo;x\u0026rsquo;）、一个转义序列（例如 \u0026lsquo;\\t\u0026rsquo;），或一个通用的字符（例如 \u0026lsquo;\\u02C0\u0026rsquo;）。\n在 C 中，有一些特定的字符，当它们前面有反斜杠时，它们就具有特殊的含义，被用来表示如换行符（\\n）或制表符（\\t）等。下表列出了一些这样的转义序列码：\n转义序列 含义 \\|\\ 字符 ' \u0026rsquo; 字符 \u0026quot; \u0026quot; 字符 ? ? 字符 \\a 警报铃声 \\b 退格键 \\f 换页符 \\n 换行符 \\r 回车 \\t 水平制表符 \\v 垂直制表符 \\ooo 一到三位的八进制数 \\xhh . . . 一个或多个数字的十六进制数 下面的实例显示了一些转义序列字符：\n字符串常量 字符串字面值或常量是括在双引号 \u0026quot;\u0026rdquo; 中的。一个字符串包含类似于字符常量的字符：普通的字符、转义序列和通用的字符。\n您可以使用空格做分隔符，把一个很长的字符串常量进行分行。\n下面的实例显示了一些字符串常量。下面这三种形式所显示的字符串是相同的。\n1 2 3 4 5 6 7 \u0026#34;hello, dear\u0026#34; \u0026#34;hello, \\ dear\u0026#34; \u0026#34;hello, \u0026#34; \u0026#34;d\u0026#34; \u0026#34;ear\u0026#34; 定义常量 在 C 中，有两种简单的定义常量的方式：\n使用 #define 预处理器。 使用 const 关键字。 define 预处理器 下面是使用 #define 预处理器定义常量的形式：\n1 #define identifier value 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include \u0026lt;stdio.h\u0026gt; #define LENGTH 10 #define WIDTH 5 #define NEWLINE \u0026#39;\\n\u0026#39; int main() { int area= LENGTH * WIDTH; printf(\u0026#34;value of area : %d\u0026#34;, area); printf(\u0026#34;%c\u0026#34;, NEWLINE); return 0; } 1 value of area : 50 const 关键字 您可以使用 const 前缀声明指定类型的常量，如下所示：\n1 const type variable = value; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include \u0026lt;stdio.h\u0026gt; int main() { const int LENGTH = 10; const int WIDTH = 5; const char NEWLINE = \u0026#39;\\n\u0026#39;; int area = LENGTH * WIDTH; printf(\u0026#34;value of area : %d\u0026#34;, area); printf(\u0026#34;%c\u0026#34;, NEWLINE); return 0; } 1 value of area : 50 C 存储类 存储类定义 C 程序中变量/函数的范围（可见性）和生命周期。这些说明符放置在它们所修饰的类型之前。下面列出 C 程序中可用的存储类：\nauto register static extern auto 存储类 auto 存储类是所有局部变量默认的存储类。\n1 2 3 4 { int mount; auto int month; } 上面的实例定义了两个带有相同存储类的变量，auto 只能用在函数内，即 auto 只能修饰局部变量。\nregister 存储类 register 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。这意味着变量的最大尺寸等于寄存器的大小（通常是一个词），且不能对它应用一元的 \u0026lsquo;\u0026amp;\u0026rsquo; 运算符（因为它没有内存位置）。\n1 2 3 { register int miles; } 寄存器只用于需要快速访问的变量，比如计数器。还应注意的是，定义 \u0026lsquo;register\u0026rsquo; 并不意味着变量将被存储在寄存器中，它意味着变量可能存储在寄存器中，这取决于硬件和实现的限制。\nstatic 存储类 static 存储类指示编译器在程序的生命周期内保持局部变量的存在，而不需要在每次它进入和离开作用域时进行创建和销毁。因此，使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。\nstatic 修饰符也可以应用于全局变量。当 static 修饰全局变量时，会使变量的作用域限制在声明它的文件内。\n全局声明的一个 static 变量或方法可以被任何函数或方法调用，只要这些方法出现在跟 static 变量或方法同一个文件中。\n以下实例演示了 static 修饰全局变量和局部变量的应用：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include \u0026lt;stdio.h\u0026gt; static int count=10; /* 全局变量 - static 是默认的 */ void func1(void) { /* \u0026#39;thingy\u0026#39; 是 \u0026#39;func1\u0026#39; 的局部变量 - 只初始化一次 * 每次调用函数 \u0026#39;func1\u0026#39; \u0026#39;thingy\u0026#39; 值不会被重置。 */ static int thingy=5; thingy++; printf(\u0026#34; thingy 为 %d ， count 为 %d\\n\u0026#34;, thingy, count); } int main() { while (count--) { func1(); } return 0; } 1 2 3 4 5 6 7 8 9 10 thingy 为 6 ， count 为 9 thingy 为 7 ， count 为 8 thingy 为 8 ， count 为 7 thingy 为 9 ， count 为 6 thingy 为 10 ， count 为 5 thingy 为 11 ， count 为 4 thingy 为 12 ， count 为 3 thingy 为 13 ， count 为 2 thingy 为 14 ， count 为 1 thingy 为 15 ， count 为 0 可以看出加了 static 后，局部变量就变成了类似全局变量了，但是仅仅是定义了这个存储类的对象可以访问\nextern 存储类 extern 存储类用于提供一个全局变量的引用，全局变量对所有的程序文件都是可见的。当您使用 extern 时，对于无法初始化的变量，会把变量名指向一个之前定义过的存储位置。\n当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时，可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。可以这么理解，extern 是用来在另一个文件中声明一个全局变量或函数。\nextern 修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候，如下所示：\n第一个文件：main.c\n1 2 3 4 5 6 7 8 9 10 #include \u0026lt;stdio.h\u0026gt; int count ; extern void write_extern(); int main() { count = 5; write_extern(); } 第二个文件：support.c\n1 2 3 4 5 6 7 8 #include \u0026lt;stdio.h\u0026gt; extern int count; void write_extern(void) { printf(\u0026#34;count is %d\\n\u0026#34;, count); } 在这里，第二个文件中的 extern 关键字用于声明已经在第一个文件 main.c 中定义的 count。现在 ，编译这两个文件，如下所示：\n1 $ gcc main.c support.c 这会产生 a.out 可执行程序，当程序被执行时，它会产生下列结果：\n1 count is 5 ","date":"2020-08-31T04:21:02Z","image":"https://cn.bing.com/th?id=OHR.AABday_EN-CN1797262692_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/clang_note/","title":"clang 学习笔记"},{"content":"解决一个Ubuntu 18 安装MySQL 5.7不能远程登录的问题 首先编辑 /etc/mysql/mysql.conf.d/mysqld.cnf 配置文件：\n1 nano /etc/mysql/mysql.conf.d/mysqld.cnf 注释掉\n1 2 bind-address = 127.0.0.1 #如果没有就跳过 还有就是要给root 设置一个密码，允许他远程登录\n1 2 3 4 5 6 7 8 use mysql; select user,host from user; update user set host=\u0026#34;%\u0026#34; where user=\u0026#34;root\u0026#34;; update user set authentication_string=PASSWORD(\u0026#34;yourPassword\u0026#34;) where user=\u0026#34;root\u0026#34;; /* authentication_string在mysql 5.7.9以后废弃了password字段和password()函数 5.7.9及后面的版本要使用下面的方法 */ /* alter user \u0026#39;root\u0026#39;@\u0026#39;localhost\u0026#39; identified with mysql_native_password by \u0026#39;xcsd1234\u0026#39;; */ /* alter user \u0026#39;root\u0026#39;@\u0026#39;localhost\u0026#39; identified by \u0026#39;yourPassword\u0026#39;; #通用修改密码 */ flush privileges; 如果MySQL5.7 这样还不能远程登录上，咱见面持刀互a，如果你比我猛，当我没有说！\n你以为这就完了吗？？哪有这么简单，你还需要下面这条命令\n1 2 update user set plugin=\u0026#39;mysql_native_password\u0026#39;; /* 放在上面 use mysql； 后面执行 仅对5.7.9以下有效 */ 很多人都是忽略了这条命令，导致一直无法远程登录上MySQL* 必要的话可以重启一下MySQL哦！\n","date":"2020-07-28T02:19:28Z","image":"https://cn.bing.com/th?id=OHR.BluebirdsEastern_EN-CN2662548051_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/mysql_login_problem/","title":"解决一个Ubuntu 18 安装MySQL 5.7不能远程登录的问题"},{"content":"笔记本坏了，这几天不能写代码了 \u0026lt;2020-07-27 14:25\u0026gt; 今天吃完饭以后，发现笔记本无法唤醒了。我还觉得是电池没有电了，可是插上电源依然不能开机，我慌了，八成是笔记本坏了。\n笔记本坏了那也不能影响我写bug呀，我就找了个老的台式机装上了Ubuntu 18。\n太难受了，换了一个 Ubuntu 系统的老电脑，卡的怀疑人生，还没有好的中文输入法，明天就去修电脑去，希望小米的授权维修店不会坑我吧\n······························································\n\u0026lt;2020-07-28 01:21\u0026gt;\n好了，?早点洗洗睡觉了\n······························································\n\u0026lt;2020-07-28 17:56\u0026gt;\n现在刚刚修完电脑回来，话说小米的售后还是可以的，只是客服有点坑，给我说要发票，要三包卡\n结果我去了小米授权维修点，检查了一下电脑状况，主板出问题了，查了一下保修日期还有一天，也没有给我拖时间，当场发顺丰了，还说大概三天就可以维修好，我不是吹捧小米哈，我是 果粉 就觉得小米这个牌子也确实是还可以的。\n等过几天看看返厂修的怎么样，不行我再喷，哈哈哈\n\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\n\u0026lt;2020-09-25 20:52\u0026gt;\n忘记了写后续了，今天来补一下后续 ?\n大概是 5 天后，我的笔记本被修好寄回，快递员大早上的给我打电话，那个时候我还在睡着觉，然后快递员打不通我的电话，就打我室友的。我室友叫我下去拿快递。妈的，我刚到楼下就看到这个狗屁快递员，直接把我的快递 ?从 2 米多高的铁门外丢进来了，然后当场潇洒离去，卧槽，我都看呆了，那个是我的电脑！没错吧？快递员敢这么随便的扔进来？?\n然后我就拿快递回去拆开，检查了一下电脑 ?,好在小米那边泡沫纸裹的多，电脑没有啥事！那我就开个机吧！\n然后就挺惨不忍睹的，换了主板以后，各种卡，开机以后至少还要卡个 30s 这样才能反应过来，一开始我就想凑合着用吧，反正不要钱的，还要啥自行车呢 ?‍♂️ . 但是我还是找了随电脑寄过来的专属售后吐槽，加了微信以后，我就说着各种我遇到的问题，一开始他还理我，后来直接鸟都不鸟我，心里一万个 mmp\n大概是五天后吧，我他们电脑又坏了，莫名其妙的自己坏的，我发誓真的就是一顿饭的功夫\u0026hellip; \u0026hellip;就是这么突然！我又找我的专属客服哔哔！依旧是不理我，我也就自认倒霉了，打算发工资了重新换一下笔记本\n然后又是两天后！我闲着无聊搁哪看我收到的垃圾短信，啊哈哈哈，无意间看到了小米给我发的售后服务评价，我就点进去链接，一顿妈卖批的骂，什么垃圾小米，什么垃圾售后，才几天又坏了，反正就是各种怼。\n大概是一天以后，小米客服打电话给我了，和我大概了解了一下情况后，让我再次返厂，再给我维修一下(实际上是给我换了一块主板)\n然后又是几天的折腾，电脑又给我修好啦，这次终于靠谱了，虽然也有一点卡顿，但是打完驱动，满血复活！New Bee ! 还给我报销了邮费\n好了，今天算是完结了！\n\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\n","date":"2020-07-27T02:18:50Z","image":"https://cn.bing.com/th?id=OHR.PeritoMorenoArgentina_EN-CN8965492888_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/my_laptop_do_not_work/","title":"笔记本坏了，这几天不能敲代码了"},{"content":"关于 Maven 下载插件缓慢的问题 众所周知,咱们大天朝由于各种原因,只能活在局域网里面.这可能对那些不深度使用网络的人来说不痛不痒,但是你要是搞开发,搞运维,那才是各种烦呢\n就拿 maven来说,我™下载一个几kb得依赖都要好几个小时,甚至还要看运气\n网上也给了各种各样得解决方案特指 CSDN,但是我去尝试后,他妈的怎么弄都不行!!!我就搞不明白了,有些作者特指那种只会复制粘贴别人文章的,连别人文章的有效性都不去验证一下,就直接拿过来当作自己的了,太恶心了??\n好嘞废话不多说了,还是说 maven 吧 以前阿里源还是 http ,所以用网上的方法还是有效的,但是当阿里把 http 升级为https 后,原来的方法就失效了\nidea编辑器:忽略ssl的配置,Maven -\u0026gt; Runner -\u0026gt; VM Options添加 1 -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -Dmaven.wagon.http.ssl.ignore.validity.dates=true Eclipse操作大致同上,忽略 ssl即可\n另外想屏蔽CSDN得话可以用 Tampermonkey 脚本来屏蔽一下 p.s. 需要先安装 Tampermonkey 再安装 百度搜索结果屏蔽csdn博客\n文章转自博客园作者:lisongyu 原文链接:关于maven使用阿里源无法生效问题\n","date":"2020-07-23T03:48:26Z","image":"https://cn.bing.com/th?id=OHR.PurpleFlowers_EN-CN8622188560_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/download_maven_plugin_faster/","title":"关于 Maven 下载插件缓慢的问题"},{"content":"VS code 快捷键大全 General Ctrl+Shift+P, F1 Show Command Palette\nCtrl+P Quick Open, Go to File…\nCtrl+Shift+N New window/instance\nCtrl+Shift+W Close window/instance\nCtrl+, User Settings\nCtrl+K Ctrl+S Keyboard Shortcuts\nBasic editing Ctrl+X Cut line (empty selection)\nCtrl+C Copy line (empty selection)\nAlt+ ↑ / ↓ Move line up/down\nShift+Alt + ↓ / ↑ Copy line up/down\nCtrl+Shift+K Delete line\nCtrl+Enter Insert line below\nCtrl+Shift+Enter Insert line above\nCtrl+Shift+\\ Jump to matching bracket\nCtrl+] / [ Indent/outdent line\nHome / End Go to beginning/end of line\nCtrl+Home Go to beginning of file\nCtrl+End Go to end of file\nCtrl+↑ / ↓ Scroll line up/down\nAlt+PgUp / PgDn Scroll page up/down\nCtrl+Shift+[ Fold (collapse) region\nCtrl+Shift+] Unfold (uncollapse) region\nCtrl+K Ctrl+[ Fold (collapse) all subregions\nCtrl+K Ctrl+] Unfold (uncollapse) all subregions\nCtrl+K Ctrl+0 Fold (collapse) all regions\nCtrl+K Ctrl+J Unfold (uncollapse) all regions\nCtrl+K Ctrl+C Add line comment\nCtrl+K Ctrl+U Remove line comment\nCtrl+/ Toggle line comment\nShift+Alt+A Toggle block comment\nAlt+Z Toggle word wrap\nNavigation Ctrl+T Show all Symbols\nCtrl+G Go to Line\u0026hellip;\nCtrl+P Go to File\u0026hellip;\nCtrl+Shift+O Go to Symbol\u0026hellip;\nCtrl+Shift+M Show Problems panel\nF8 Go to next error or warning\nShift+F8 Go to previous error or warning\nCtrl+Shift+Tab Navigate editor group history\nAlt+ ← / → Go back / forward\nCtrl+M Toggle Tab moves focus\nSearch and replace Ctrl+F Find Ctrl+H Replace\nF3 / Shift+F3 Find next/previous\nAlt+Enter Select all occurences of Find match\nCtrl+D Add selection to next Find match\nCtrl+K Ctrl+D Move last selection to next Find match\nAlt+C / R / W Toggle case-sensitive / regex / whole word\nMulti-cursor and selection Alt+Click Insert cursor\nCtrl+Alt+ ↑ / ↓ Insert cursor above / below\nCtrl+U Undo last cursor operation\nShift+Alt+I Insert cursor at end of each line selected\nCtrl+L Select current line\nCtrl+Shift+L Select all occurrences of current selection\nCtrl+F2 Select all occurrences of current word\nShift+Alt+→ Expand selection\nShift+Alt+← Shrink selection\nShift+Alt + (drag mouse) Column (box) selection\nCtrl+Shift+Alt + (arrow key) Column (box) selection\nCtrl+Shift+Alt +PgUp/PgDn\nRich languages editing Ctrl+Space Trigger suggestion\nCtrl+Shift+Space Trigger parameter hints\nShift+Alt+F Format document\nCtrl+K Ctrl+F Format selection\nF12 Go to Definition\nAlt+F12 Peek Definition\nCtrl+K F12 Open Definition to the side\nCtrl+. Quick Fix\nShift+F12 Show References\nF2 Rename Symbol\nCtrl+K Ctrl+X Trim trailing whitespace\nCtrl+K M Change file language\nEditor management Ctrl+F4, Ctrl+W Close editor\nCtrl+K F Close folder\nCtrl+\\ Split editor\nCtrl+ 1 / 2 / 3 Focus into 1 st, 2nd or 3rd editor group\nCtrl+K Ctrl+ ←/→ Focus into previous/next editor group\nCtrl+Shift+PgUp / PgDn Move editor left/right\nCtrl+K ← / → Move active editor group\nFile management Ctrl+N New File\nCtrl+O Open File\u0026hellip;\nCtrl+S Save\nCtrl+Shift+S Save As\u0026hellip;\nCtrl+K S Save All\nCtrl+F4 Close\nCtrl+K Ctrl+W Close All\nCtrl+Shift+T Reopen closed editor\nCtrl+K Enter Keep preview mode editor open\nCtrl+Tab Open next\nCtrl+Shift+Tab Open previous\nCtrl+K P Copy path of active file\nCtrl+K R Reveal active file in Explorer\nCtrl+K O Show active file in new window/instance\nDisplay F11 Toggle full screen\nShift+Alt+0 Toggle editor layout (horizontal/vertical)\nCtrl+ = / - Zoom in/out Ctrl+B Toggle Sidebar visibility\nCtrl+Shift+E Show Explorer / Toggle focus\nCtrl+Shift+F Show Search\nCtrl+Shift+G Show Source Control\nCtrl+Shift+D Show Debug\nCtrl+Shift+X Show Extensions\nCtrl+Shift+H Replace in files\nCtrl+Shift+J Toggle Search details\nCtrl+Shift+U Show Output panel\nCtrl+Shift+V Open Markdown preview\nCtrl+K V Open Markdown preview to the side\nCtrl+K Z Zen Mode (Esc Esc to exit)\nDebug F9 Toggle breakpoint\nF5 Start/Continue\nShift+F5 Stop\nF11 / Shift+F11 Step into/out\nF10 Step over\nCtrl+K Ctrl+I Show hover\nIntegrated terminal Ctrl+` Show integrated terminal\nCtrl+Shift+` Create new terminal\nCtrl+C Copy selection\nCtrl+V Paste into active terminal\nCtrl+↑ / ↓ Scroll up/down\nShift+PgUp / PgDn Scroll page up/down\nCtrl+Home / End Scroll to top/bottom\n译文:\n一般 Ctrl + Shift + P，F1显示命令面板\nCtrl + P快速打开，转到文件…\nCtrl + Shift + N新窗口/实例\nCtrl + Shift + W关闭窗口/实例\nCtrl +，用户设置\nCtrl + K Ctrl + S键盘快捷键\n基本编辑 Ctrl + X剪切线（空选择）\nCtrl + C复制行（空选择）\nAlt +↑/↓上下移动行\nShift + Alt +↓/↑复制上/下一行\nCtrl + Shift + K删除行\nCtrl + Enter在下面插入行\nCtrl + Shift + Enter在上方插入行\nCtrl + Shift + \\跳转到匹配的括号\nCtrl +] / [缩进/缩进行\nHome / End转到行首/行尾\nCtrl + Home转到文件开头\nCtrl + End转到文件末尾\nCtrl +↑/↓上下滚动\nAlt + PgUp / PgDn向上/​​向下滚动页面\nCtrl + Shift + [折叠（折叠）区域\nCtrl + Shift +]展开（展开）区域\nCtrl + K Ctrl + [折叠（折叠）所有子区域\nCtrl + K Ctrl +]展开（展开）所有子区域\nCtrl + K Ctrl + 0折叠（折叠）所有区域\nCtrl + K Ctrl + J展开（展开）所有区域\nCtrl + K Ctrl + C添加行注释\nCtrl + K Ctrl + U删除行注释\nCtrl + /切换行注释\nShift + Alt + A切换块注释\nAlt + Z切换自动换行\n导航 Ctrl + T显示所有符号\nCtrl + G转到行\u0026hellip;\nCtrl + P转到文件\u0026hellip;\nCtrl + Shift + O转到符号\u0026hellip;\nCtrl + Shift + M显示问题面板\nF8转到下一个错误或警告\nShift + F8转到上一个错误或警告\nCtrl + Shift + Tab导航编辑器组的历史记录\nAlt +←/→返回/前进\nCtrl + M切换选项卡可移动焦点\n搜索并替换 Ctrl + F查找Ctrl + H替换\nF3 / Shift + F3查找下一个/上一个\nAlt + Enter选择所有出现的“查找匹配项”\nCtrl + D将选择添加到下一个查找匹配项\nCtrl + K Ctrl + D将最后一个选择移至下一个查找匹配项\nAlt + C / R / W切换区分大小写/正则表达式/整个单词\n多光标和选择 Alt +单击插入光标\nCtrl + Alt +↑/↓在上方/下方插入光标\nCtrl + U撤消上一个光标操作\nShift + Alt + I在选定的每行末尾插入光标\nCtrl + L选择当前行\nCtrl + Shift + L选择所有当前选中项\nCtrl + F2选择所有出现的当前单词\nShift + Alt +→扩展选择\nShift + Alt +←缩小选择\nShift + Alt +（拖动鼠标）列（框）选择\nCtrl + Shift + Alt +（箭头键）列（框）选择\nCtrl + Shift + Alt + PgUp / PgDn\n富语言编辑 Ctrl +空格触发建议\nCtrl + Shift +空格键触发参数提示\nShift + Alt + F格式文件\nCtrl + K Ctrl + F格式选择\nF12转到定义\nAlt + F12偷看定义\nCtrl + K F12在侧面打开定义\nCtrl +。快速解决\nShift + F12显示参考\nF2重命名符号\nCtrl + K Ctrl + X修剪尾随空格\nCtrl + K M更改文件语言\n编辑器管理 Ctrl + F4，Ctrl + W关闭编辑器\nCtrl + K F关闭文件夹\nCtrl + \\分割编辑器\nCtrl + 1/2/3聚焦到第1，第2或第3编辑器组\nCtrl + K Ctrl +←/→聚焦到上一个/下一个编辑器组\nCtrl + Shift + PgUp / PgDn左右移动编辑器\nCtrl + K←/→移动活动的编辑器组\n文件管理 Ctrl + N新建文件\nCtrl + O打开文件\u0026hellip;\nCtrl + S保存\nCtrl + Shift + S另存为\u0026hellip;\nCtrl + K S全部保存\nCtrl + F4关闭\nCtrl + K Ctrl + W关闭所有\nCtrl + Shift + T重新打开关闭的编辑器\nCtrl + K输入保持打开预览模式编辑器\nCtrl + Tab打开下一步\nCtrl + Shift + Tab打开上一个\nCtrl + K P复制活动文件的路径\nCtrl + K R在资源管理器中显示活动文件\nCtrl + K O在新窗口/实例中显示活动文件\n显示 F11切换全屏\nShift + Alt + 0切换编辑器布局（水平/垂直）\nCtrl + = /-放大/缩小Ctrl + B切换边栏可见性\nCtrl + Shift + E显示资源管理器/切换焦点\nCtrl + Shift + F显示搜索\nCtrl + Shift + G显示源代码控制\nCtrl + Shift + D显示调试\nCtrl + Shift + X显示扩展\nCtrl + Shift + H替换为文件\nCtrl + Shift + J切换搜索详细信息\nCtrl + Shift + U显示输出面板\nCtrl + Shift + V打开Markdown预览\nCtrl + K V在侧面打开Markdown预览\nCtrl + K Z禅模式（Esc Esc退出）\n调试 F9切换断点\nF5开始/继续\nShift + F5停止\nF11 / Shift + F11跳入/跳出\nF10越过\nCtrl + K Ctrl + I显示悬停\n集成终端 Ctrl +`显示集成终端\nCtrl + Shift +`创建新终端\nCtrl + C复制选择\nCtrl + V粘贴到活动终端\nCtrl +↑/↓向上/向下滚动\nShift + PgUp / PgDn向上/​​向下滚动页面\nCtrl + Home / End滚动到顶部/底部\n本文在 visualstudiocode 快捷键的原文基础上进行整理修改\n","date":"2020-07-17T18:18:21Z","image":"https://cn.bing.com/th?id=OHR.VerzascaValley_EN-CN0835346354_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/code_hot_key/","title":"VS code 快捷键大全"},{"content":"简单解释 NAT[转自知乎] NAT 大家应该十分熟悉了，它分为几种。一种就叫做 NAT，它只对 IP 地址进行转换；另一种叫做 NAPT（Network Address/Port Translation），它可以对整个会话的端点（由 IP 地址和端口号组成）做转换，这是一种更加常见的 NAT 变种。\n当然了，NAPT 也分为许多种，我们这里就不深入探讨了，大家如果有兴趣可以查阅相关的文献。\n下面就简单介绍一下 NAT 的工作原理：\n首先，NAT A 网下的设备 1（192.168.1.101）想与某公网 IP 通讯，设备 1 将包发给 NAT A，然后 NAT A 对源 IP 进行转换发给 NAT B（中间可能还会经过多重 NAT）。\n这样做的目的是，NAT B 并不知晓 NAT A 下的各个设备，他只能与 NAT A 本身通讯，因此发送给 NAT B 的包源 IP 必须是 NAT A 的公网 IP，不然 NAT B 没有办法进行回复。\n接下来 NAT B 将回复包再发回 NAT A，此时就是 NAT 发挥作用的时候了，NAT A 现在要做的就是将包再分发回之前的设备，如何确定要发给谁呢？NAT 中记录了一张表，之前 192.168.1.101 通过 2333 端口与 42.120.241.46 端口 443 通讯了，并且 NAT A 是用 60001 的端口转发出去的，那么这次接受到发往该 NAT 60001 端口的包时就应该再通过 2333 端口转发给 192.168.1.101。经过这样的过程，NAT A 下的设备都可以连接到互联网了！\nUDP 打洞原理及过程 如上图所示，由于 NAT 的存在，当 NAT A 的设备 1 想与 NAT B 下的设备通讯时，必然要将目标 IP 设置为 NAT B 的公网地址，而 NAT B 转发表中并没有记录过 NAT A 与自身网络下设备的通讯记录，因此 NAT B 会将包丢掉。\n下面我们来看看 UDP 打洞是怎么解决这个问题的。\n为了能够进行 UDP 打洞，我们需要一台公网的服务器作为中转站，它是 NAT A 与 NAT B 之间的信使。\n（为了方便起见，我们把地址为 192.168.1.101 的设备称为设备 1，把地址为 192.168.1.2 的设备称为设备 2，信使服务器称为 S）\n首先，设备 1 和设备 2 都向 S 注册自己，S 中能记录各个设备此时使用的公网 IP 地址和端口号，例如设备 1 是 123.122.53.20:31000，设备 2 是 42.120.241.46:41000。\n然后设备 1 与设备 2 都向 S 获取对方的公网 IP 与之前预留的端口号，就像这样：\n然后就是最关键的一步，打洞。\n设备 1 向 42.120.241.46:41000 发一个包，NAT B 自然能接收到这个包，然而它不知道来自 NAT A 的包应该发给谁，因此 NAT B 将这个包舍弃。但是由于设备 1 向 42.120.241.46:41000 发过包，NAT A 会记录：以后来自 42.120.241.46:41000 的包都发给设备 1。\n设备 2 也做相同的操作，让 NAT B 也知道：以后来自 123.122.53.20:31000 的包都发给设备 2。\n至此，NAT A 与 NAT B 都互相为对方保留了端口，就可以愉快地通讯了。\n本文 参考 抄袭自知乎文章 简单解释 NAT\n","date":"2020-07-15T22:53:18Z","image":"https://cn.bing.com/th?id=OHR.OceanHeart_EN-CN3106304306_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/learn_nat/","title":"简单解释 NAT"},{"content":"两小时 Git 入门 建立 Git 仓库 1 $ git init 把文件添加到仓库 1 $ git add + filename 把文件提交到仓库 1 $ git commit -m \u0026#34;wrote a readme file\u0026#34; 查看提交结果/对比工作区和文件和仓库文件的不同之处 1 $ git status 比对并列出文件和仓库文件的不同之处 1 2 3 $ git diff #如果git status告诉你有文件被修改过，用git diff可以查看修改内容。 Git 查看历史记录 1 $ git log/git log --pretty=oneline(查看精简版) 退回曾经的某个版本 退回最新版 1 2 3 $ git reset --hard + (对应的append GPL的commit id) $ git reset --hard HEAD^ 用 HEAD 表示当前版本，上一个版本就是 HEAD^，上上一个版本就是 HEAD^^，当然往上 100 个版本写 100 个 ^ 比较容易数不过来，所以写成 HEAD~100\n查找所有版本 append GPL 的 commit id 1 $ git reflog Git 原理： 1.工作区（Working Directory）就是 Git 创建仓库所在的目录\n2 版本库（Repository）工作区有一个隐藏目录。Git，这个不是工作区，而是 Git 的版本库 Git 的版本库里存了很多东西，其中最重要的就是称为 stage（或者叫 index）的暂存区，还有 Git 为我们自动创建的第一个分支 master，以及指向 master 的一个指针叫 HEADgit add 实际上就是把文件修改添加到暂存区 Git commit 实际上就是把暂存区的所有内容提交到当前分支我们创建 Git 版本库时，Git 自动为我们创建了唯一一个 master 分支，所以，现在，Git commit 就是往 master 分支上提交更改\n查看工作区和版本库里面最新版本的区别 1 $ git diff HEAD -- +文件名 放弃对文件的更改 放弃对\u0026quot;工作区\u0026quot;文件的更改 1 2 3 $ git checkout -- 文件名 $ git checkout -- file 命令中的\u0026ndash;很重要，没有\u0026ndash;，就变成了“切换到另一个分支”的命令\n放弃对\u0026quot;暂存区\u0026quot;文件的更改 1 $ git reset HEAD + 文件名 场景 1：当你改乱了工作区某个文件的内容，想直接丢弃工作区的修改时，用命令 Git checkout \u0026ndash; file。\n场景 2：当你不但改乱了工作区某个文件的内容，还添加到了暂存区时，想丢弃修改，分两步，第一步用命令 Git reset HEAD ，就回到了场景 1，第二步按场景 1 操作。\n场景 3：已经提交了不合适的修改到版本库时，想要撤销本次提交，参考版本回退 \u0026lt;number 7\u0026gt;，不过前提是没有推送到远程库。\n命令 Git rm 用于删除一个文件。如果一个文件已经被提交到版本库，那么你永远不用担心误删，但是要小心，你只能恢复文件到最新版本，你会丢失最近一次提交后你修改的内容\n创建 GitHub 远程仓库 第 1 步：创建 SSH Key。在用户主目录下，看看有没有。SSH 目录，如果有，再看看这个目录下有没有 id_rsa,id_rsa.pub 这两个文件，如果已经有了，可直接跳到下一步。如果没有，打开 Shell（Windows 下打开 Git Bash），创建 SSH Key\n1 $ ssh-keygen -t rsa -C\u0026#34;youremail@example.com\u0026#34; 第 2 步：登陆 GitHub，打开“Account settings”，“SSH Keys”页面，然后，点“Add SSH Key”，填上任意 Title，在 Key 文本框里粘贴 id_rsa.pub 文件的内容\n第 3 步： 登陆 GitHub，在右上角找到“Create a new repo”按钮，创建一个新的仓库，在 Repository name 填入仓库名，其他保持默认设置，点击“Create repository”按钮，就成功地创建了一个新的 Git 仓库\n第 4 步： 在本地 shell 上 对 GitHub 上的仓库绑定： Git remote add frelon git@github.com:lixuanliming/frelon.git 这里的 frelon 是远程仓库的名字，后面是 GitHub 给的 URL11.推送更新到 GitHub 仓库上\n1 $ git push -u 仓库名 master #由于远程库是空的，我们第一次推送 master 分支时，加上了-u 参数，Git 不但会把本地的 master 分支内容推送的远程新的 master 分支，还会把本地的 master 分支和远程的 master 分支关联起来，在以后推送或者拉取时就不用加 -u 参数\n1 $ git push 仓库名 master 关于远程仓库 从远程仓库克隆 如果自己需要新建一个项目，且从零开发，那么最好的方式是先创建远程库，然后，在 shell 里从远程库克隆\n建仓库过程省略，在建立仓库的时候我们可以勾选 Initialize this repository with a README，这样 GitHub 会自动为我们创建一个 README.md 文件。创建完毕后，可以看到 README.md 文件\nClone 远程仓库 1 git clone git@github.com:lixuanliming/frelon.git Git 分支 创建 Git 分支 1 $ git checkout -b bayu git checkout -b bayu -b 表示 branch 相当于下面两条命令 Git branch bayu 建立\u0026rsquo;bayu\u0026rsquo;分支 Git check bayu 切换到\u0026rsquo;bayu\u0026rsquo;分支\n查看分支 1 $ git branch 当前分支前面会标一个*号\n合并分支到当前分支 Fast 合并分支 1 $ git merge frelon 注意提示的 Fast-forward 信息，Git 告诉我们，这次合并是“快进模式”，也就是直接把 master 指向 dev 的当前提交，所以合并速度非常快。当然，也不是每次合并都能 Fast-forward\n常规合并分支 1 $ git merge --no-ff -m \u0026#34;merge with no-ff\u0026#34; frelon 合并分支时，加上\u0026ndash;no-ff 参数就可以用普通模式合并，合并后的历史有分支，能看出来曾经做过合并，而 fast forward 合并就看不出来曾经做过合并。\n删除多余分支 1 $ git branch -d bayu 丢弃未合并的分支 1 $ git branch -D fork1 假设 fork1 是未合并的分支，那么用 13.4 中的删除方法并不可行，只能用 -D 强制删除查看分支： git branch 创建分支： git branch 切换分支： git checkout 创建 + 切换分支： git checkout -b 合并某分支到当前分支： git merge 删除分支： git branch -d Git 冲突解决 Git 合并时遇到的冲突问题 当 Git 无法自动合并分支时，就必须首先解决冲突。解决冲突后，再提交，合并完成。解决冲突就是把 Git 合并失败的文件手动编辑为我们希望的内容，再提交。用 git log --graph 命令可以看到分支合并图。\n查看分支合并图 1 $ git log --graph 15.git 解决 bug 分支的问题 修复 bug 时，我们会通过创建新的 bug 分支进行修复，然后合并，最后删除。当手头工作没有完成时，先把工作现场 git stash 一下，然后去修复 bug，修复后，再 git stash pop ，回到工作现场。\n把工作区的文件暂时隐藏起来 1 $ git stash 切换到 master 分支下面 1 $ git checkout master 建立 issue-101 的修复 bug 的分支 \u0026ldquo;并进入该分支修复 bug\u0026rdquo; 1 $ git checkout -b issue-101 把文件提交到暂存区 1 2 3 $ git add xxx.py $ git commit -m \u0026#34;fix bug 101\u0026#34; 提交文件到仓库 回到原分支 1 $ git checkout master 删除 issue-101 分支 1 $ git merge --no-ff -m \u0026#34;merged bug fix 101\u0026#34; issue-101 返回原分区 1 $ git checkout dev 还原工作区 1 $ git status 继续工作 1 $ git stash pop Git 的多人协作 查看远程仓库的信息 1 $ git remote -v(显示详细信息) 推送分支到远程仓库 1 2 3 $ git push origin master 这是推送主分支(origin是远程仓库的名字) $ git push origin dev 还可以推送别的副分支(origin是远程仓库的名字) 抓取分支 从本地推送分支 使用 git push origin branch-name ，如果推送失败，先用 git pull 抓取远程的新提交； 在本地创建和远程分支对应的分支，使用 git checkout -b branch-name origin/branch-name ，本地和远程分支的名称最好一致； 建立本地分支和远程分支的关联，使用 git branch --set-upstream branch-name origin/branch-name ； 从远程抓取分支，使用 Git pull，如果有冲突，要先处理冲突。\nGit 标签管理 Git 创建标签 1 $ git tag tag_Name 对 Git 已经 commit 过的历史文件打标签 1 $ git log --pretty=oneline --abbrev-commit 查看log中所记录的commit id 1 2 3 4 5 6 7 $ git tag v0.9 f52c633 #对commit id为 f52c633 的文件打上标签为 v0.9 #还可以创建带有说明的标签， #用-a指定标签名，-m指定说明文字 #如下实例: $ git tag -a v0.1 -m \u0026#34;blabla\u0026#34; 1094ad 查看标签信息 1 2 $ git tag 查看所有 tag信息 $ git show \u0026lt;tagname\u0026gt; 查看指定tag信息 推送标签到远程 1 2 $ git push frelon v1.0 给某个仓库打标签 $ git push frelon --tags 一次性推送全部尚未推送到远程的本地标签,前提是本地都打了标签 删除标签 1 $ git tag -d v0.1 这适用于尚为推送到远程仓库使用如果标签已经推送到远程，要删除远程标签就麻烦一点，先从本地删除：\n1 $ git tag -d v0.9 $ git push frelon :refs/tags/v0.9 命令 git push origin 可以推送一个本地标签；\n命令 git push origin --tags 可以推送全部未推送过的本地标签；\n命令 git tag -d 可以删除一个本地标签；\n命令 git push origin :refs/tags/ 可以删除一个远程标签。\n本文参考 廖雪峰的官方网站/Git教程 曾经在微信公众上也发过，但是因为公众号维护起来比较麻烦就拖到这里来了，文章写的匆忙，如有错误欢迎评论区指导/纠正\n","date":"2020-07-14T21:04:27Z","image":"https://cn.bing.com/th?id=OHR.YearoftheOx_EN-CN2506406791_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/learn_git/","title":"两小时 Git 入门"},{"content":"教大家如何在微软官网下载 Windows10 ISO 安装包 进入 Windows10 下载页面 CTRL+SHIFT+I 后再 CTRL+SHIFT+M再接一个 CTRL+R 你就可以看到让你选择 Windows 版本,个人建议不要选家庭中文版 然后一路下来也没有啥难点了,他就会为你创建一个24H内有效的链接,记得尽快下载系统哦\n就这了\n","date":"2020-07-14T15:42:51Z","image":"https://cn.bing.com/th?id=OHR.PenitentSnow_EN-CN4445586340_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/download_windows10_iso/","title":"在微软官网下载 Windows10 ISO 安装包"},{"content":"Python 爬虫抓取播放源(m3u8) 今天没有事的时候在捯饬 dotPlayer [IOS 平台 APP/￥ 收费] 时发现这个东西是个宝贝呀！可以看 m3u8 格式的视频，这都不是重点，重点是使用起来非常舒服，虽然直接复制 m3u8 链接到 Safari 也可以播放，但是 dotPlayer 还可以有封面和标题！这两者体验可谓是天差地别(我没有收钱哈！我是真的觉得好用，希望 dotplayer 的作者看到了打一下广告费！)\n具体可以看\n简书作者： NickXXXXXXXX 给出的图片简介\nGitHub 上的 help_zh.md 给出的使用简介\n这都不是我们需要关注の重点，重点是这款 app 是用来播放 m3u8 流の\n播放视频很简单，可是这 m3u8 文件从哪里来呢？??\n去 Telegram 加入组群 dotPlayer ,群里经常会分享相关的订阅 自己动手丰衣足食 这次用的爬虫和上一次一模一样，只是我优化了一下过程，就随便讲讲吧\n1 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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #coding=utf-8 import requests from lxml import etree import time import datetime import re ​ def makeUrl(Num): # Num为html的页数 allinks=[] for link in range(1,Num+1): link=\u0026#34;https://www.xxxx.com/xzy{}\u0026#34;.format(link) allinks.append(link) return allinks ​ def getRealUrl(fakeUrl): webPage = requests.get( url=fakeUrl, headers={\u0026#34;User-Agent\u0026#34;: \u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36\u0026#34;} ) indexPage=webPage.text indexHtml=etree.HTML(indexPage) nextPageUrl=indexHtml.xpath(\u0026#34;//div[@id=\u0026#39;posts\u0026#39;]/div[@class=\u0026#39;post grid\u0026#39;]/h3/a/@href\u0026#34;) picurls=indexHtml.xpath(\u0026#34;//div[@class=\u0026#39;img\u0026#39;]/a/img/@data-src\u0026#34;) Set = [] Set.append(nextPageUrl) Set.append(picurls) return Set ​ def ownSource(realUrl): webPagess = requests.get( url=realUrl, headers={\u0026#34;User-Agent\u0026#34;: \u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36\u0026#34;} ) indexPagess=webPagess.text indexHtmlss=etree.HTML(indexPagess) title=indexHtmlss.xpath(\u0026#34;//header/h1/text()\u0026#34;) source=indexHtmlss.xpath(\u0026#34;//div[@class=\u0026#39;article-content\u0026#39;]/div/@data-item\u0026#34;) finalUrl = [] finalUrl.append(title) finalUrl.append(source) return finalUrl ​ Derailed = makeUrl(110) for iPhoneSE in Derailed: ceo = getRealUrl(iPhoneSE) qq = ceo[0] a=0 for SourceUrl in qq: helloWorld = ownSource(SourceUrl) MUlink=helloWorld[1][0] realMULinks=MUlink.split(\u0026#39;\u0026#34;\u0026#39;) realMULink=realMULinks[5].replace(\u0026#34;\\\\\u0026#34;,\u0026#34;\u0026#34;) title=helloWorld[0][0].replace(\u0026#34; \u0026#34;,\u0026#34;\u0026#34;) thePicUrl = ceo[1][a] ​ p=re.compile(r\u0026#34;[-,$()#+\u0026amp;*\u0026#39; ]\u0026#34;) lastTitle=re.sub(p,\u0026#34;\u0026#34;,title) str =\u0026#39;{\\n\u0026#39;+\u0026#39;\u0026#34;name\u0026#34;:\u0026#34;\u0026#39;+lastTitle+\u0026#39;\u0026#34;,\\n\u0026#39;+\u0026#39;\u0026#34;logo\u0026#34;:\u0026#34;\u0026#39;+thePicUrl+\u0026#39;\u0026#34;,\\n\u0026#39;+\u0026#39;\u0026#34;url\u0026#34;:\u0026#34;\u0026#39;+realMULink+\u0026#39;\u0026#34;\\n\u0026#39;+\u0026#39;},\\n\u0026#39; with open (\u0026#34;weicha8.json\u0026#34;,\u0026#34;a+\u0026#34;,encoding=\u0026#39;utf-8\u0026#39;) as f: f.write(str) a+=1 print(str) print(a) print(datetime.datetime.now()) time.sleep(1) 整体分为三个部分：\n1 2 3 def makeUrl(Num): def getRealUrl(fakeUrl): def ownSource(realUrl): def makeUrl(Num): 该函数，接收一个 int 类型的参数， return 一个 字符串列表\n这部分就是分析页面链接？的规则，在本地生成 URL ,并返回给下一个函数处理， 爬虫能在本地干的就经量在本地完成，不过度占用服务器的资源，不给站长添堵！ 这也是为了自己好？?? 俗话说的好：爬虫学的好！ \u0026hellip; \u0026hellip;\ndef getRealUrl(fakeUrl): 该函数，接收一个 字符串列表 return 一个 嵌套列表\n这部分就是接收到上面生成の URL ,访问并分析网页结构。拿到视频的 封面 真实播放页 の URL,并且返回给下一个函数来执行\ndef ownSource(realUrl): 该函数，接收一个 嵌套列表 return 一个 嵌套列表\n这部分也很简单，接收上面来的链接，一个一个的访问，拿到 标题 播放 URL ,然后返回一个 嵌套列表\n编写代码 函数已经写好了，我们自己调用即可，需要注意循环嵌套的时候不能犯迷糊就可以，最后处理一下所得到の数据，写入本地 JSON 文件就可以了\n虽然我爬虫很菜，但是我在一点点的学习！一起加油吧！!!\n最新代码在 Github 上，这篇文章，以后就不更新了\n","date":"2020-07-01T21:33:56Z","image":"https://cn.bing.com/th?id=OHR.Parrotfish_EN-CN1212515803_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/python_spider_download_m3u8/","title":"Python 爬虫抓取m3u8播放源"},{"content":"利用 Python 爬虫获取 bing.com 每天的高清壁纸 众所周知，必应是主力做壁纸の搜索引擎！\n每天都更新搜索页面的背景图片，这些图片也的的确确很好看，那我们要是 ♥心动了，想保存欣赏欣赏怎么办呐？\n方法一：直接开发者模式拿图片 这个方法应该是最简单的了， ctrl+ shift+i\n1 2 3 4 5 6 7 8 9 ... ... \u0026lt;tbody\u0026gt; \u0026lt;tr\u0026gt; \u0026lt;td id=\u0026#34;hp_cellCenter\u0026#34; ... ...\u0026gt; \u0026lt;div id=\u0026#34;hp_container\u0026#34; ... ...\u0026gt; \u0026lt;div id=\u0026#34;bgDiv\u0026#34; ... ...\u0026gt; \u0026lt;div id=\u0026#34;bgImgProgLoad\u0026#34; data-ultra-definition-src=\u0026#34;/th?id=OHR.WildflowersBC_EN-CN3359054435_UHD.jpg\u0026amp;rf=LaDigue_UHD.jpg\u0026amp;pid=hp\u0026amp;w=1920\u0026amp;h=1080\u0026amp;rs=1\u0026amp;c=4\u0026#34; data-explicit-bing-load=\u0026#34;false\u0026#34; data-dynamic-size=\u0026#34;true\u0026#34;\u0026gt; \u0026lt;/div\u0026gt; ... ...\t这个链接 ?就在 \u0026lt;div id=\u0026quot;bgImgProgLoad\u0026quot; の标签里面，复制 data-ultra-definition-src 所对应的值，再在前面补上 cn.bing.com 就可以啦\n不过这种方法下载的图片是被压缩以后的图片，大小也就几百 kb,而如果我们想要下载原图的话，就需要把链接 ?里面第一个 .jpg 后面的字符全给干掉，最终的的 URL 应该是： https://cn.bing.com/th?id=OHR.WildflowersBC_EN-CN3359054435_UHD.jpg\n方法二：python 爬虫 没有什么好说の,都是最简单的 python 命令，\n运行环境： Linux\nPython 版本： Python3\npip 依赖库： requests,lxml\n基本上大部分的 Linux 都会预装 Python3 ,所以就从安装 pip 开始了\n1 2 3 4 5 6 7 8 sudo apt install python3-pip pip3 install lxml pip3 install requests mkdir -p ~/Bing_pic/pic/ cd Bing_pic nano xxx.py\t#名字就根据你自己的喜好来吧,把下面的代码粘进去就可以啦 python3 xxx.py\t#如果没有报错,大功告成啦 ls pic/\t#看一下下载的壁纸 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 #coding=utf-8 import requests from lxml import etree import os webPage = requests.get(url = \u0026#34;https://cn.bing.com/?FORM=BEHPTB\u0026amp;ensearch=1\u0026#34;, headers={\u0026#34;User-Agent\u0026#34;:\u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36\u0026#34;} ) we = webPage.text ssr = etree.HTML(we) Links = ssr.xpath(\u0026#34;//div/div[@id=\u0026#39;bgDiv\u0026#39;]/div/@data-ultra-definition-src\u0026#34;) Desc=ssr.xpath(\u0026#34;//a/div/h2/text()\u0026#34;) Str=\u0026#34;https://cn.bing.com\u0026#34; desc=\u0026#34;\u0026#34; re = [] for sein in Desc: desc=sein print(desc) for se in Links: Str = Str+se re = Str.split(\u0026#34;\u0026amp;\u0026#34;,1) Str=re[0] print(Str) picfile=\u0026#34;/home/jokeme/pic/\u0026#34;+desc+\u0026#34;.jpg\u0026#34; #这里需要根据实际情况修改 picture=requests.get(Str) with open (picfile,\u0026#34;wb+\u0026#34;) as f: f.write(picture.content) print(\u0026#34;Get The Picture of Bing Successfully! \u0026#34;+\u0026#34;The Picture in \\\u0026#34;\u0026#34;+picfile+\u0026#34;\\\u0026#34;\u0026#34;) 如果你想每天都自动执行这段代码的话，就可以用 crontab 来定时执行\n1 2 crontab -e 30 0 * * * python3 ~/Bing_pic/Bing_spider.py \u0026gt;\u0026gt; ~/Bing_pic/Bing_spider_log.txt 这样的话，每天凌晨 12:30 就会自动执行该命令啦\n使用须知：这个脚本爬取的是 Bing 国际版的图片，所以当天中午 15:00 以后爬取的图片和第二天中午 15:00 之前爬取的图片一致，也就是第一天会出现这种情况，以后都没有问题的\n","date":"2020-06-14T21:33:56Z","image":"https://cn.bing.com/th?id=OHR.TheWave_EN-CN9165010424_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/python_spider_download_bing_wallpaper/","title":"利用 Python 爬虫获取 bing.com 每天的高清壁纸"},{"content":"Linux 常用的小技巧 apt-get remove vsftpd \u0026ndash;purge 删除所有配置，下次安装的时候会重新生成配置文件\nnano快捷键 ALT+Shift+3\t显示行号 CTRL+SHIFT+_\t快速跳到某行\ninit 3 如果不经常使用 Linux の桌面，但是又不希望桌面吃内存，可以试试看\n1 sudo init 3 echo 颜色输出(python通用) echo -e \u0026quot;\\033[背景颜色；文字颜色 m 字符串\\033[0m\u0026quot;\n可以选择的编码如下所示(这些颜色是 ANSI 标准颜色)：\n1 2 3 4 5 6 7 8 9 10 40 黑色背景 41 红色背景 42 绿色背景 43 黄色背景 44 蓝色背景 45 紫色背景 46 青色背景 47 白色(灰色)背景 49 缺省黑色背景 文字颜色是背景颜色减10 zsh美化 在 GitHub 上有一个 oh-my-zsh の项目，相信我，用上这个美化后的 zsh 以后，你会离不开它的，安装脚本附上\n1 2 sh -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)\u0026#34; sh -c \u0026#34;$(wget -O- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)\u0026#34; ","date":"2020-06-10T20:48:55Z","image":"https://cn.bing.com/th?id=OHR.MountSefton_EN-CN4284831269_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/linux_skill/","title":"Linux 常用的小技巧"},{"content":"CentOS编译安装Nano 众所周知,因为 CentOS 非常的稳定,所以导致 CentOS の YUM 源里面的软件非常的老旧 就拿 Nano 来说, 官方已经到了 4.0版本, 而 CentOS 这边还在2.0徘徊,这就非常的难受了.所以咱就手动编译安装!\n安装准备 1 2 3 4 5 6 sudo su cd yum -y install gcc ncurses-devel wget wget https://www.nano-editor.org/dist/v4/nano-4.6.tar.gz tar zxvf nano-4.6.tar.gz cd nano-4.6 准备编译 1 2 ./configure make \u0026amp;\u0026amp; make install 复制文件 1 2 cp src/nano /usr/bin cp doc/sample.nanorc /root/.nanorc 常用插件也给安排上\n1 2 3 4 5 6 7 8 echo \u0026#39;include \u0026#34;/usr/local/share/nano/*.nanorc\u0026#34;\u0026#39; \u0026gt;\u0026gt; /root/.nanorc #用于高亮显示 echo \u0026#39;set nowrap\u0026#39; \u0026gt;\u0026gt; /root/.nanorc #关闭自动换行 echo \u0026#39;set linenumbers\u0026#39; \u0026gt;\u0026gt; /root/.nanorc #打开行号显示 本文参考: Hiwbb.com\n","date":"2020-06-10T13:58:18Z","image":"https://cn.bing.com/th?id=OHR.IceWalking_EN-CN6785374690_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/compile_nano/","title":"CentOS编译安装Nano"},{"content":"Frp 内网穿透 自打搞一个软路由，我就玩的可带劲了，什么 Linux , Docker , Java 各种技术都被逼提升了很多。 闲话不多说，直接说重点了\n准备工作： frp-server \u0026amp; frp-client 可以在 GitHub 里下载 域名(需要备案) 有公网 IP の服务器一台 配置 server 端 我是用的是 amd64 架构的，如果和我一样的话可以按我的命令来\n1 2 3 4 5 6 wget https://github.com/fatedier/frp/releases/download/v0.33.0/frp_0.33.0_linux_amd64.tar.gz tar -zxvf frp_0.33.0_linux_amd64.tar.gz mv frp_0.33.0_linux_amd64.tar.gz frp #为了接下来方便一点 cd frp rm frpc* #删除不必要的配置,也可以不删 nano frps.ini 1 2 3 4 5 6 7 8 9 10 11 12 [common] bind_port = 7000 #对外提供服务的端口 dashboard_port = 7500 #访问控制板的端口 token = 123456 #token,相对于一个简单的验证 vhost_http_port = 80 #http服务在服务器的代理端口 可以根据自己的喜好设定这些配置\n配置 client 端 1 2 3 4 5 6 wget https://github.com/fatedier/frp/releases/download/v0.33.0/frp_0.33.0_linux_amd64.tar.gz tar -zxvf frp_0.33.0_linux_amd64.tar.gz mv frp_0.33.0_linux_amd64.tar.gz frp #为了接下来方便一点 cd frp rm frps* #删除不必要的配置,也可以不删 nano frpc.ini 单网站配置(非必选) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [common] server_addr = yourdomain.com #你的域名/填写IP地址也可以 \u0026lt;记得把你的域名解析到服务器的IP地址上\u0026gt; server_port = 7000 #需要和上面的server配置相同 token = 123456 #需要和上面的server配置相同 [http] type = http local_port = 80 custom_domains = yourdomain.com 多网站配置(非必选) 如果你有多个 Web 项目的话，哪就建议你用二级域名\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [common] server_addr = yourdomain.com server_port = 7000 token = 123456 [web1] type = http local_port = 80 custom_domains = a.yourdomain.com [web2] type = http local_port = 8080 custom_domains = b.yourdomain.com [web3] type = http local_port = 8088 custom_domains = c.yourdomain.com SSH 配置(非必选) 1 2 3 4 5 6 7 8 [ssh] type = tcp #ssh服务请使用tcp连接 local_ip = 127.0.0.1 local_port = 22 remote_port = 11484 #需要打开服务器端的该端口の防火墙 浏览器验证 浏览器输入 http://yourdomain.com 理论上就可以打开你在 client 上的 Web 项目了\n如果你遇到了\nChrome 浏览器打不开网页！\nIE 浏览器也打不开网页\n别急！ ! 换Firefox浏览器试一下 / |\u0026amp;\u0026amp;| \\ 使用Chrome的无痕模式打开试一下\n","date":"2020-06-08T19:01:05Z","image":"https://cn.bing.com/th?id=OHR.SuperbOwl_EN-CN4550239476_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/frp_penetrate/","title":" Frp 内网穿透"},{"content":"Spring学习 提醒⏰:在开始之前,我们需要配置以下基本环境(还有文件的编码方式也要注意)\nFile \u0026raquo;\u0026gt; Project Structcture(CTRL+ALT+Shift+S) \u0026raquo;\u0026gt; Project \u0026raquo;\u0026gt; Project SDK:\nFile \u0026raquo;\u0026gt; Project Structcture(CTRL+ALT+Shift+S) \u0026raquo;\u0026gt; Project \u0026raquo;\u0026gt; Project language level\nFile \u0026raquo;\u0026gt; Project Structcture(CTRL+ALT+Shift+S) \u0026raquo;\u0026gt; Modules \u0026raquo;\u0026gt; Sources \u0026raquo;\u0026gt; Languages level\nFile \u0026raquo;\u0026gt; Settings(CTRL+ALT+S) \u0026raquo;\u0026gt; Build,Execution,Deployment \u0026raquo;\u0026gt; Compiler \u0026raquo;\u0026gt; Java Compiler \u0026raquo;\u0026gt; Project bytecode version\nFile \u0026raquo;\u0026gt; Settings(CTRL+ALT+S) \u0026raquo;\u0026gt; Build,Execution,Deployment \u0026raquo;\u0026gt; Compiler \u0026raquo;\u0026gt; Java Compiler \u0026raquo;\u0026gt;Per-module bytecode version\n配置文件 \u0026lt;pom.xml\u0026gt; 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 \u0026lt;properties\u0026gt; \u0026lt;project.build.sourceEncoding\u0026gt;UTF-8\u0026lt;/project.build.sourceEncoding\u0026gt; \u0026lt;java.version\u0026gt;1.8\u0026lt;/java.version\u0026gt; \u0026lt;/properties\u0026gt; \u0026lt;!-- 首先配置一下基本的文件编码和Java版本 --\u0026gt; \u0026lt;dependencies\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-web\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;2.2.2.RELEASE\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;!-- 提供一些 mvc，aop 的依赖包，这是玩Spring-boot必备的依赖包 --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-parent\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;2.2.2.RELEASE\u0026lt;/version\u0026gt; \u0026lt;type\u0026gt;pom\u0026lt;/type\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;!-- 这个依赖是帮助我们自动选择一些依赖最合适的版本，这个是玩Spring-Boot必备的 --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-maven-plugin\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;2.1.7.RELEASE\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;!--该插件是将Springboot项目打包为可执行的jar包 --\u0026gt; \u0026lt;/dependencies\u0026gt; spring-boot-starter-parent 是版本仲裁者,所以我们在导入依赖时,可以省略一些依赖的版本号\nspring-boot-starter-web web 模块的场景启动器组件,还有别的一些 starter 都是被 Sprint boot 抽取出来的,以后在项目里只需导入这些启动器就可以开箱即用\n建立Controller \u0026amp; Starter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package top.jokeme; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Starter { public static void main(String[] args) { SpringApplication.run(Starter.class,args); } } #~~~~~~~~~~~~~~~~ package top.jokeme; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class Controller { @RequestMapping(\u0026#34;/word\u0026#34;) public String restr(){ return \u0026#34;\u0026lt;p style=\u0026#39;color:red;text-align:center\u0026#39;\u0026gt;Awesome Java\u0026lt;/p\u0026gt;\u0026#34;; } } 这就是一个最简单的 SpringBoot 应用程序\n1 2 3 4 5 6 7 8 9 10 11 12 package top.jokeme; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @ComponentScan(\u0026#34;top.jokeme.ayibe\u0026#34;) @SpringBootApplication public class Starter { public static void main(String[] args) { SpringApplication.run(Starter.class,args); } } @SpringBootApplication 该注解就标识着 Springboot 的主配置类,启动类,从这里的 main 方法开始执行\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )} ) public @interface SpringBootApplication { ...... } 注: @import 是 SpringBoot の底层注解.其作用就是给容器导入组件\n需要注意两个比较重要的注解 Registrar.class 1 2 3 4 @SpringBootApplication 中的 @EnableAutoConfiguration 中的 @AutoConfigurationPackage 中的 @Import({Registrar.class}) 其中 Registrar.clss就是\n1 2 3 4 5 6 7 8 9 10 11 12 static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { Registrar() { } public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()); } //这个方法就是将标记了@SpringBootApplication的类,及其子包的所有组件扫描到Spring容器里面去 public Set\u0026lt;Object\u0026gt; determineImports(AnnotationMetadata metadata) { return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata)); } AutoConfigurationImportSelector 1 2 3 @SpringBootApplication 中的 @EnableAutoConfiguration 中的 @Import({AutoConfigurationImportSelector.class}) AutoConfigurationImportSelector 就是导入哪些组件的选择器,并且会将需要导入的组件以全类名の方式返回,然后这些组件就会被添加到容器之中;\n在这个过程中,会导入非常多的自动配置类诸如 [ xxxAutoConfiguration ] 等,目的就是给容器导入这些场景所需的所有组件,并自动配置好\n上 AutoConfigurationImportSelector 代码片段:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return NO_IMPORTS; } else { AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } } protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } else { AnnotationAttributes attributes = this.getAttributes(annotationMetadata); List\u0026lt;String\u0026gt; configurations = this.getCandidateConfigurations(annotationMetadata, attributes); configurations = this.removeDuplicates(configurations); Set\u0026lt;String\u0026gt; exclusions = this.getExclusions(annotationMetadata, attributes); this.checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = this.filter(configurations, autoConfigurationMetadata); this.fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions); } } 1 2 3 4 5 protected List\u0026lt;String\u0026gt; getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List\u0026lt;String\u0026gt; configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()); Assert.notEmpty(configurations, \u0026#34;No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.\u0026#34;); return configurations; } 然后就用到了 SpringFactoriesLoader.loadFactoryNames\n1 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 public static List\u0026lt;String\u0026gt; loadFactoryNames(Class\u0026lt;?\u0026gt; factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); } private static Map\u0026lt;String, List\u0026lt;String\u0026gt;\u0026gt; loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap\u0026lt;String, String\u0026gt; result = (MultiValueMap)cache.get(classLoader); if (result != null) { return result; } else { try { Enumeration\u0026lt;URL\u0026gt; urls = classLoader != null ? classLoader.getResources(\u0026#34;META-INF/spring.factories\u0026#34;) : ClassLoader.getSystemResources(\u0026#34;META-INF/spring.factories\u0026#34;); LinkedMultiValueMap result = new LinkedMultiValueMap(); while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); Iterator var6 = properties.entrySet().iterator(); while(var6.hasNext()) { Entry\u0026lt;?, ?\u0026gt; entry = (Entry)var6.next(); String factoryTypeName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 = var9.length; for(int var11 = 0; var11 \u0026lt; var10; ++var11) { String factoryImplementationName = var9[var11]; result.add(factoryTypeName, factoryImplementationName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException var13) { throw new IllegalArgumentException(\u0026#34;Unable to load factories from location [META-INF/spring.factories]\u0026#34;, var13); } } } SpringBoot 在启动的时候,主要就是从类路径下的 META-INF/factories 里获取 EnableAutoConfiguration 指定的值,并将这些值作为自动配置类导入容器中,然后自动配置类生效,帮我们实现自动配置的工作\n这些都归功于:\nspring-boot-autoconfigure-2.2.2.RELEASE.jar\n注: Part 2.1的一些内容可能与 Part 3 的内容有冲突,以 Part 3 为准,Part 2.1有点老\n自动配置原理 application.properties 配置文件里面都可以配置什么内容?\nSpringBoot在启动的时候会加载主配置类,这个主配置类就是我们添加了注解 [ @SpringBootApplication ]的类\n并且为这个类开启了自动配置功能 (继承至注解:@SpringBootApplication の注解 @EnableAutoConfiguration)\n小技巧 :在使用Idea的时候,我们想要看导入的一些第三方代码的源码可以按 ALT+CTRL+鼠标点击你要看源码的 类/接口/注解\u0026hellip;\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package org.springframework.boot.autoconfigure; import ......; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )} ) public @interface SpringBootApplication { ...... } EnableAutoConfigurationの作用: 使用 AutoConfigurationImportSelector 给容器导入相关组件\n虽然没太看懂 selectImports 代码,但是我猜大概的意思是:\n依赖 AutoConfigurationMetadataLoader 来导入 META-INF 下面的 properties 文件\n详细解释可以看这个 Other+ 的文章\n1 2 3 4 5 6 7 8 9 public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return NO_IMPORTS; } else { AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } 所有可以在配置文件中配置的属性,都在xxxProperties类中 由于**@SpringBootApplication** 有一个默认的ComponentScan注解指定了扫描包の方式,所以在我们需要DIY扫包方式的时候就需要用 @ComponentScan 来指定我们需要扫描的包\neg: @ComponentScan(\u0026ldquo;top.jokeme.ayibe\u0026rdquo;) 指定扫描 ayibe 这个包\u0026hellip;.如果指定以后就不会 使用默认的扫描方式,只会扫描指定の包\nYAML \u0026amp; Properties yaml基本语法: 1 2 3 4 5 K: V ############## K: V1: 443 V2: sew yaml是大小写敏感的,并且需要注意缩进和空格\n注意:yaml里面\n1 \u0026#34; \u0026#34; \u0026amp; \u0026#39; \u0026#39; 作用不一样 \u0026quot; \u0026ldquo;会转义字符串,比如 \\n 等\n\u0026rsquo; \u0026rsquo; 不会转义字符串,输入啥,输出啥\nyamlの 对象 1 2 3 4 5 food: name: apple tasty: true #################### food: {name: apple,tasty: true} yamlの数组 1 2 3 4 5 6 interesting: - 13 - 14 - 15 ##################### interesting: [13,14,15] yaml文件 \u0026amp; 数据绑定 1 2 3 4 5 person: sname: zhangsan sage: 18 Sod: \u0026#39;AnHui Provience\u0026#39; Lovelydog: {age: 13,name: lisi} 1 2 3 4 5 6 7 8 9 10 11 12 package top.day2_yaml; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @Component @ConfigurationProperties(prefix = \u0026#34;person\u0026#34;) public class Person { } 如果不出意外:\n1 2 3 #localhost:8080/se #会出现以下效果 Person{sname=\u0026#39;zhangsan\u0026#39;, sage=18, Sod=\u0026#39;AnHui Provience\u0026#39;, Lovelydog=dog{age=13, name=\u0026#39;lisi\u0026#39;}} 注意1:@Component 注解是帮助绑定值的一个注解,所以不能忘记添加该注解\n注意2:这里可能需要导入以下依赖\n1 2 3 4 5 6 \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-configuration-processor\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;2.3.0.RELEASE\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;!-- 配置文件处理模块,配置文件的数据绑定就依赖该模块 --\u0026gt; Properties properties の用法大致与 yaml 差不多,但是比 yaml 费事一点点\n1 2 3 4 5 6 person.sname=zhangsan person.sage=18 person.Sod=\u0026#39;AnHui Provience\u0026#39; person.lovelydog.age=13 person.lovelydog.name=lisi server.port=8088 ∵ Properties 是大小写不敏感的\n∴ person.age = person.Age\n@Value @Value 使用方法 1 2 3 4 5 6 7 8 package top.day3_atValue; import org.springframework.beans.factory.annotation.Value; public class human { @Value(\u0026#34;Person\u0026#34;) String Typeof; } @ConfigurationProperties \u0026amp; @Value の区别 @ConfigurationProperties @Value 特征 批量注入配置文件里的属性 一个个指定 松散语法 ?YES ?NO SpEL ?NO ?YES JSR303 ?YES ?NO 复杂类型封装 ?YES ?NO eg: 复杂类型封装就是指只, map/list/\u0026hellip;\n我们在何时使用他们 如果我们只是某项业务逻辑中需要获取某个属性,推荐使用\n如果我们是需要进行大规模配置文件,就要使用 @ConfigurationProperties\n@PropertySource \u0026amp; @ImportResource @PropertySource使用方法 1 2 3 4 5 6 7 8 package top.day3_atValue; import org.springframework.context.annotation.PropertySource; @PropertySource(value={\u0026#34;classpath:per.properties\u0026#34;}) public class human { } @PropertySource 加载指定☞的配置文件\n@ConfigurationProperties 默认获取全局の配置文件里的属性\n⚠注意: @PropertySource 必须和 @ConfigurationProperties 一起使用才有效,单独使用没有效果\n@ImportResource使用方法 其作用是导入 Spring 里面的配置文件并让其生效\n1 @ImportResource(locations = {classpath:\u0026#34;abc.xml\u0026#34;}) 配置文件占位符 先来一个实例:\n1 2 3 4 5 6 person.sname=${random.uuid} person.sage=${random.int} person.Sod=\u0026#39;AnHui Provience\u0026#39; person.lovelydog.age=${person.lol:233} person.lovelydog.name=lisi server.port=8080 这就是用随机数 $random 来生成的,还有用占位符生成の\n${person.lol:233} の意思就是 有person.lol就用它,没有就用233\n1 Person{sname=\u0026#39;d573e71a-f8b2-4a48-a688-10449b4b55fc\u0026#39;, sage=-1817647899, Sod=\u0026#39;\u0026#39;AnHui Provience\u0026#39;\u0026#39;, Lovelydog=dog{age=233, name=\u0026#39;lisi\u0026#39;}} Profile文件支持 多Profile文件 properties 可以用多个带有 application-{env}.properties 来切换环境\n注: 默认的profile是 application.properties\n如果需要切换生产环境,可以在 application.properties 里面配置\n1 spring.profiles.active=env 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 dog2.SunDay=24 dog2.Example.k1=sim dog2.Example.k2=sib dog2.Friends={lisi,zhangsan} dog2.soul.Weight=30 dog2.soul.Data=35 dog2.soul.Power=80 dog2.soul.Lovely=true spring.profiles.active=ssr person.sname=${random.uuid} person.sage=${random.int} person.Sod=\u0026#39;AnHui Provience\u0026#39; person.lovelydog.age=${person.lol:233} person.lovelydog.name=lisi server.port=8080 spring.profiles=ssr yaml 如果配置文件是 yaml 我们还可以用更为简便の多文档块の方式\n这里就涉及到了基本の yaml 语法 - - - 这种连续三个 - 就表示文本块,看操作:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 person: sname: zhangsan sage: 18 Sod: \u0026#39;AnHui Provience\u0026#39; Lovelydog: {age: 13,name: lisi} spring: profiles: active: ssr --- person: sname: ${random.uuid} sage: ${random.int} Sod: \u0026#39;AnHui Provience\u0026#39; Lovelydog: {age: 13,name: lisi} server.port: 8088 spring: profiles: ssr SpringBoot配置文件の加载顺序 首先关心一下,配置文件放哪里才会被加载\n1.file:./config 2.file:/ 3.classpath:/config/ 4.classpath:/ 按照顺序,1-4按顺序扫描,然后1-4の优先级依次降低,当配置文件里的属性冲突时,以优先级高的配置文件为准\n还有一点需要注意的就是,这些文件都会被加载,形成互补配置\n外部配置文件加载顺序 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 1、命令行参数：所有的配置都可以在命令行参数中指定，每个配置项前使用--，多个配置间使用空格隔开，例如： java -jar spring-boot-02-0.0.1-SNAPSHOT.jar --server.port=8088 --server.context-path=boot 2、来自java:comp/env的JNDI属性 3、java的系统属性(System.getProperties(\u0026#34;\u0026#34;)) 4、操作系统环境变量 5、RandomValuePropertySource配置的random.*属性值 6、jar包外部的application-{profile}.properties或application-{profile}.yml(带spring.profile配置) 7、jar包内部的application-{profile}.properties或application-{profile}.yml(带spring.profile配置) 8、jar包外部的application.properties或application.yml(不带spring.profile配置) 9、jar包内部的application.properties或application.yml(不带spring.profile配置) 10、@Configuration注解类上的@PropertySource 11、通过SpringApplication.setDefaultProperties()指定的默认属性 注： ①以上配置文件的优先级顺序由高到低，高优先级的覆盖低优先级的并形成互补 ②6、8所指的jar包外指的是和jar包同一个文件夹下 ","date":"2020-05-29T17:27:33Z","image":"https://cn.bing.com/th?id=OHR.VosgesBioReserve_EN-CN8863541867_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/springboot_note_1/","title":"SpringBoot学习笔记(1)"},{"content":"Ubuntu 使用 netplan 配置 WIFI 在折腾 Ubuntu 的时候，我突然发现 Ubuntu 有好几套网络管理方案，但是在安装了图形化界面以后，只有Network Manager \u0026amp; Systemd-networkd 之一生效，\n那像我这种爱折腾的人，肯定是不满足被束缚啊，为什么不能用命令行来连接 ? WiFi 呢？\n查看网卡名字 1 2 3 4 5 6 7 8 9 $ ~ iw dev phy#0 Interface wlx200db035fad3 ifindex 3 wdev 0x1 addr 20:0d:b0:35:fa:d3 ssid Tenda_277D40 type managed txpower 12.00 dBm 打开无线网卡 1 $ ~ sudo ip lin set wlx200db035fad3 up 搜索 WiFi 信号 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 45 46 47 48 49 50 51 52 53 $ ~ sudo iw dev wlx200db035fad3 scan BSS **:**:**:**:**:**(on wlx200db035fad3) -- associated TSF: 1588670071330336 usec (18387d, 09:14:31) freq: 2437 beacon interval: 100 TUs capability: ESS Privacy ShortSlotTime (0x0411) signal: -80.00 dBm last seen: 0 ms ago SSID: Tenda_277D40 Supported rates: 1.0* 2.0* 5.5 11.0 18.0 24.0 36.0 54.0 DS Parameter set: channel 6 ERP: \u0026lt;no flags\u0026gt; ERP D4.0: \u0026lt;no flags\u0026gt; Extended supported rates: 6.0 9.0 12.0 48.0 HT capabilities: Capabilities: 0x18fc HT20 SM Power Save disabled RX Greenfield RX HT20 SGI RX HT40 SGI TX STBC No RX STBC Max AMSDU length: 7935 bytes DSSS/CCK HT40 Maximum RX AMPDU length 65535 bytes (exponent: 0x003) Minimum RX AMPDU time spacing: 8 usec (0x06) HT RX MCS rate indexes supported: 0-15, 32 HT TX MCS rate indexes are undefined HT operation: * primary channel: 6 * secondary channel offset: no secondary * STA channel width: 20 MHz * RIFS: 1 * HT protection: no * non-GF present: 0 * OBSS non-GF present: 0 * dual beacon: 0 * dual CTS protection: 0 * STBC beacon: 0 * L-SIG TXOP Prot: 0 * PCO active: 0 * PCO phase: 0 WPA: * Version: 1 * Group cipher: CCMP * Pairwise ciphers: CCMP * Authentication suites: PSK * Capabilities: 16-PTKSA-RC 1-GTKSA-RC (0x000c) WMM: * Parameter version 1 * BE: CW 15-1023, AIFSN 3 * BK: CW 15-1023, AIFSN 7 * VI: CW 7-15, AIFSN 2, TXOP 3008 usec * VO: CW 3-7, AIFSN 2, TXOP 1504 usec 我个人认为这一步纯属装 13! 我连接 ? WiFi,我肯定知道这个 WiFi の名字啊，那密码我肯定也知道啊，那还扫描个锤锤 ?啊？\n配置 WiFi 1 $ ~ sudo nano /etc/netplan/01-network-manager-all.yaml 这个文件名大家理论上都是不一样的，但是配置文件都在这个 /etc/netplan/ 里面\n1 2 3 4 5 6 7 8 9 10 network: version: 2 renderer: networkd wifis: wlx200db035fad3: dhcp4: yes dhcp6: no access-points: Tenda_277D40: password: \u0026#34;password\u0026#34; 这个配置文件是 YAML 使用的时候需要注意缩进，这样一番操作以后就可以\n1 2 $ ~ sudo netplan generate //检查语法错误的 $ ~ sudo netplan apply 可以参考下面的文章,写的很规范 : connect-wifi-terminal-ubuntu\n","date":"2020-05-06T21:33:56Z","image":"https://cn.bing.com/th?id=OHR.MountNemrut_EN-CN8469644685_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/netplan_wifi/","title":" Ubuntu 使用 netplan 配置 WIFI"},{"content":"Ubuntu安装ffmpeg下载m3u8格式的视频 最近想下载一个某音视频，但是我能看不能下载，这不是扯吗？于是不能忍的我就打算盘盘这个视频\n准备环境：Ubuntu 18.04 , ffmpeg 本着万物皆可 Linux の初心，我就不想用 Windows 了。\n首先安装 ffmpeg\n1 sudo apt -y install ffmpeg 抓包短视频的 m3u8 地址 iOS 端，我是使用的 Thor 这个 app,比较好用，应用商店搜索就有\n然后得到真实的地址：\n1 http://XXX.com/xxx/xxx.m3u8 使用ffmpeg进行下载 1 ffmpeg -i http://XXX.com/xxx/xxx.m3u8 -c copy xxx.mp4 然后就需要慢慢等待下载完成就可以了，真的是特别特别的简单\n","date":"2020-04-15T23:39:23Z","image":"https://cn.bing.com/th?id=OHR.RainbowMarmot_EN-CN8037758700_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/ffmpeg_download_m3u8/","title":"Ubuntu安装ffmpeg下载m3u8 格式的视频"},{"content":"安装 Linux 系统(Ubuntu)经验总结 进来将近两三个星期的装系统经历，让我异常痛苦，先是 Bay Trail 主板的一些坑，然后是显卡驱动，然后又是一堆莫名其妙的问题\u0026hellip;\u0026hellip;\nBay Trail 主板的先天问题 在 这系列主板上安装 Windows 可能没有什么感觉，但是安装 Linux 异常的难，会造成啥啥啥内核错误，导致卡慢\n需要在引导的后面 加上一句\n1 intel_idle.max_cstate=1 显卡驱动问题 在安装 Linux 时，常常会遇到没有对应显卡驱动的问题，这时候就要在引导的时候加上\n1 nomodeset 开机卡紫屏问题 这个问题原因可能各不一样，网上一大推，不赘述\n开机卡只有鼠标和壁纸 说实话这个问题最好解决了，鼠标键盘都可以正常使用，ctrl + alt + t 打开 terminal,把 gnome 删掉即可，如果喜欢用图形界面还可以在重启以后在安装回来\n","date":"2020-03-31T17:27:33Z","image":"https://cn.bing.com/th?id=OHR.ToledoIldefonso_EN-CN9964509063_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/ubuntu_install_faq/","title":"安装 Linux 系统(Ubuntu)经验总结"},{"content":"Docker 中运行 OpenWrt 文章有误，不建议参考了！但是我当时确确实实运行起来了，可能是我写的有问题吧！我也不想重新搞了，就这样吧\n前几天解决了小主机 Ubuntu 系统的引导问题以后。便开始折腾 OpenWrt 了，在 GitHub 上找到了 L 大 的 Lede 源码，需要自己编译。\n关于如何编译我也不说了，L 大 在 Readme 里面说的清清楚楚的，没有难度，就是需要有耐心和良好的网络环境，编译过程会很慢(建议扶梯子，我没有用梯子，整整编译了两天才完成！? ? ?) 也可以用我编译好的 x86_64 的固件(集成有ssr,v2ray,adblock plus,samba,vsftpd等常用插件)\n对于编译出来的文件我来介紹一下：\nopenwrt-x86-64-rootfs-squashfs.img OpenWrt for Docker の img openwrt-x86-64-combined-squashfs.vmdk 虚拟机文件，丢进 VMware 里面使用 openwrt-x86-64-combined-squashfs.img 我们编译的固件，刻录到 u 盘上来安装 我一开始也不知道这些镜像应该选哪一个刻录，然后爬各种论坛终于被我发现了这几个文件的用法了\n好！环境已经没有问题，安装包也有了，那就先在 Docker 里面试试看这个 OpenWrt 怎么样吧\n导入 OpenWrt 的镜像 1 2 3 4 5 cd ~/lede/bin/targets/x86/64 mkdir -p ~/opt mount -o loop openwrt-x86-64-rootfs-squashfs.img ~/opt tar -cvzf ~/Openwrt.tar.gz ~/opt/* docker import ~/OpenWrt.tar.gz OpenWrt Docker 创建网络 1 docker network create -d macvlan --subnet=192.168.100.0/24 --gateway=192.168.100.1 -o parent=enp2s0 fet 这个 IP 地址需要根据你的实际 IP 更改，网卡也一样，不能照抄\nDocker 运行容器 1 docker run -d --name lede --network fet --privileged OpeenWrt /sbin/init 在这里我一开始参考 OpenWrt 官方文档,结果一直报错，我一开始以为是他给的例子 ?里面系统太老，我就换成 19.10,依旧报错，然后在网上瞎转悠的时候看到了一篇来自 美丽应用 的文章，虽然，他是在 树莓派 当中安装 OpenWrt 但是过程大多一样 (感谢 美丽应用 ?)\n到这时，我们可以\n1 2 3 4 docker ps [sudo] frelon 的密码： CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES 4fe146d86277\tOpenWrt\t\u0026#34;/sbin/init\u0026#34; 2 hours ago Up 2 hours\tlede 看一下我们的容器是否在正常运行，如果还在运行，那就说明容器没有问题可以进行下一步了\n进入 OpenWrt 容器内，进一步配置 1 docker exec -it lede /bin/ash 这里要注意的是 /bin/ash 而不是 /bin/bash\n配置网络 查看一下IP 1 2 3 4 5 6 7 / # ip a 1: lo: \u0026lt;LOOPBACK,UP,LOWER_UP\u0026gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 25: eth0@if2: \u0026lt;BROADCAST,MULTICAST\u0026gt; mtu 1500 qdisc noqueue state DOWN group default link/ether 02:42:c0:a8:64:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 稍等片刻\n1 2 3 4 5 6 7 8 9 10 11 12 / # ip a 1: lo: \u0026lt;LOOPBACK,UP,LOWER_UP\u0026gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 4: br-lan: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether 02:42:c0:a8:64:02 brd ff:ff:ff:ff:ff:ff inet 192.168.1.1/24 brd 192.168.1.255 scope global br-lan valid_lft forever preferred_lft forever 25: eth0@if2: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc noqueue master br-lan state UP group default link/ether 02:42:c0:a8:64:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 4.1.2 简易修改防火墙规则 1 2 3 opkg update opkg install nano nano /etc/config/firewall 找到 config zone 重点是把 option input/output/forward の REJRECT 改为 ACCEPT\n1 2 3 4 5 6 7 8 9 config zone opion name \u0026#39;wan\u0026#39; list network \u0026#39;wan\u0026#39; list network \u0026#39;wan6\u0026#39; option input \u0026#39;ACCEPT\u0026#39; option output \u0026#39;ACCEPT\u0026#39; option forward \u0026#39;ACCEPT\u0026#39; option masq \u0026#39;1\u0026#39; option mtu_fix \u0026#39;1\u0026#39; 然后重启一下\n1 /etc/init.d/firewall restart 正常情况会报一堆错，无视即可\n4.1.3 修改网口 1 nano /etc/config/network 可以看到 interface lan 使用的是 eth0\n然后又添加了一个网络进该容器，并分配给 interface wan 充当 eth1\n1 2 3 4 5 6 7 8 9 10 11 12 config interface \u0026#39;lan\u0026#39; option type \u0026#39;bridge\u0026#39; option ifname \u0026#39;eth0\u0026#39; option proto \u0026#39;static\u0026#39; option ipaddr \u0026#39;192.168.1.1\u0026#39; option netmask \u0026#39;255.255.255.0\u0026#39; option ip6assign \u0026#39;60\u0026#39; option gateway \u0026#39;192.168.100.105\u0026#39; config interface \u0026#39;wan\u0026#39; option ifname \u0026#39;eth1\u0026#39; option proto \u0026#39;dhcp\u0026#39; 看着修改就可以了\n1 /etc/init.d/network restart 网页登录 查看容器分配的 ip,在网页中打开，正常情况下是可以打开的，\n输入默认密码 ?password 就可以看到 OpenWrt の 状态信息\n再添加一个网口到 OpenWrt 容器 1 2 docker network connect bridge OpenWrt docker exec -it OpenWrt /etc/init.d/network restart 由于前面已经把 eth1 写入到配置信息里面，我们现在只需要重启一下网络即可，至此 OpenWrt の基本配置已经完成\n如果需要配置 OpenWrt 旁路网关，建议参考上文提到的 美丽应用\nps:补充一下 在实际使用中我发现，经常 opkg update 失败 但是网络却是没有任何问题，网关也可以 ping 通，然后就发现了 OpenWrt for Docker 的一个小 Bug\n每一次重启(有时候退出容器)就会重置 /etc/resolv.conf ,而这个文件就是我们的 DNS 文件！所以我们在使用域名 の时候就会出现失败\n我也试过在 /etc/config/network 里面设置 DNS,也同样会被重置，我不知道这是为什么，只能在每次重启以后写个脚本，设置一下DNS\n文章有误，我也不想更新了，但是当时确实是正常运行了，不建议参考了。\n本文参考资料来源：\nLean · coolsnowwolf/lede - GitHub : https://github.com/coolsnowwolf/lede OpenWrt 官方帮助文档 : https://openwrt.org/zh/docs/guide-user/virtualization/docker_openwrt_image 美丽应用 : https://mlapp.cn/376.html ","date":"2020-03-20T21:33:56Z","image":"https://cn.bing.com/th?id=OHR.RedRobin_EN-CN8071349218_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/run_openwrt_in_docker/","title":"Docker 中运行 OpenWrt"},{"content":"Bay Trail 主板安装 Linux 的坑 最近搞了一个 N2920 的低功耗小主机，本来打算搞软路由，但是家里网速不行，就打算先刷个 Linux 玩玩，于是我就被这个问题烦了将近半个月！\n一开始我打算安装一个 CentOS 7 玩玩，当我下载完系统刻录完开始安装的时候发现 CentOS 7 的 installer 异常的卡顿，而且耗时也非常的多，差不多需要将近半个小时才可以到图形界面的安装(我这个小主机是有固态的，这个速度肯定有很大的问题),而且好不容易到了图形界面的安装环节，还经常卡死！刚开始 我还以为可能是这个 ISO 镜像有问题，又换了一个 CentOS 8 的镜像。\n刻录\u0026ndash;安装\u0026ndash;等待\u0026ndash;卡死\nAgain\n开机\u0026ndash;安装\u0026ndash;等待\u0026ndash;卡死\n? ? ?我真的是\u0026hellip;\u0026hellip;一点办法都没有\n这结果属实有点意外呀，怎么 CentOS 8 也是这个鸟样？\n我不甘，又换了一个 Ubuntu 18\n刻录\u0026ndash;安装\u0026ndash;等待\u0026ndash;卡死\n卧槽，这是什么问题换系统 都不行，应该就不是软件的问题了吧，我就从主板开始动刀，会不会是因为主板的 BIOS 太老了，本来打算去更新一下 BIOS 结果，这是一个不知名的小板，不知道从哪里搞到 BIOS 文件，就放弃了！\n然后去百度 为什么电脑安装 Linux 卡顿\n众所周知，百度相当垃圾，大部分都是答非所问，要不然就是 CSDN,简书 的水贴，都没有用 :(\n然后我又找隔壁 二狗子 意念扶梯子，用 Google 搜一下\n然后我就在国外某个被大陆 404 的的技术论坛上找到了一点线索 ? ? ?\n贴子网址： askubuntu\n反正他们大概说的是 Bay Trail 主板上安装 Linux 出现的各种异常，刚刚好我的主板也是，出现的问题也和他们的很相似\n在这里截取一些解决方法：\n1 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 45 46 47 48 49 50 51 52 53 #Your processor is affected by the c-state bug #This causes total freezes when the CPU tries to enter an unsupported sleep state. It\u0026#39;s a problem for many Bay Trail devices especially with newer (4.*) kernels. #Affected processors AFAIK: #Atom Z3735F (Asus X205TA, Acer Aspire Switch 10, Lenovo MIIX 3 1030) #Atom Z3735G #Celeron J1900 (Asus ET2325IUK, shuttle XS35V4) #Celeron N2940 (Acer Aspire ES1-711, Chromebook) #Celeron N2840 (Acer Aspire ES1-311) #Celeron N2930 (Jetway JBC311U93, Zotac Nano CI320) #Pentium N3520 #Pentium N3530 (Acer V3-111P) #Pentium N3540 (Dell Inspiron 15 3000, Lenovo G50, ASUS X550MJ) #(please (suggest an) edit to add your own device if affected) #Complete list of Bay Trail processors may be found here #There is a simple workaround for this until it gets properly fixed upstream. #You just need to pass a kernel boot parameter and the random freezing stops completely. The parameter may increase battery consumption slightly, but it will give you a usable system. #You do this by editing the configuration file for GRUB: #Boot Ubuntu and open a terminal by pressing Ctrl+Alt+T then type sudo nano /etc/default/grub #Find the line that starts GRUB_CMDLINE_LINUX_DEFAULT= #This needs to be changed to include intel_idle.max_cstate=1 #So after your edit it reads something like GRUB_CMDLINE_LINUX_DEFAULT=\u0026#34;quiet splash intel_idle.max_cstate=1\u0026#34; #quiet and splash are default parameters for Ubuntu Desktop - no need to change them, or any other pre-existing parameters #Now save the file by pressing ctrl+o then enter and exit by pressing ctrl+x #Now run sudo update-grub #Then reboot. #What to do if you don\u0026#39;t have enough time to do this before the system hangs #No problem. As explained on the help page I linked to earlier, you can add the parameter to GRUB before booting. Note that this only passes the parameter for the current boot, so you still have to edit /etc/default/grub once you have booted to make the change permanent. #You need to get to the GRUB menu. If you are dual booting this will appear anyway, if not you have to press and hold (or tap) shift after pressing the power button to turn on. #When you get to this screen select Advanced Options for Ubuntu. You can move the cursor to a different kernel, or leave it in place to edit options for the default. Instead of pressing enter, press e and you will go into edit mode, looking vaguely like this. #Move the cursor down to where it says quiet splash, put a space after splash and carefully type intel_idle.max_cstate=1 making sure there is a space after it as well. 反正大概意思就是这种主板有残疾，在引导的时候会导致一些问题，需要加上这些参数就可以了\n至此，这个烦了我将近半个月的问题没有了，就在引导的时候加了几个参数，鼠标正常了，键盘也可以用了，也不会卡顿了\n","date":"2020-03-16T05:01:31Z","image":"https://cn.bing.com/th?id=OHR.ArcticWolf_EN-CN8268127090_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/install_linux_on_bay_trail/","title":"Bay Trail主板安装Linux的坑"},{"content":"Java 注解（Annotation） Java 注解其实并不难，从名字上你都知道注解是帮助 Java 代码执行的，它的形式跟接口很类似，不过前面多了一个 @ 符号\nJava 元注解 @Retention\nRetention 保留期，决定注解的生命时常，取值如下\nSOURCE 只在源码阶段保留 CLASS 只被保留到编译进行的时候 RUNTIME 注解可以保留到程序运行的时候 eg：该注解被保留到运行时\n1 2 3 @Retention(RetentionPolicy.RUNTIME) public @interface Annotation { } - @Documented 它的作用是能够将注解中的元素包含到 Javadoc 中去\n- @Target Target 指定了注解运用的地方\nANNOTATION_TYPE 给一个注解进行注解 CONSTRUCTOR 给构造方法进行注解 FIELD 给属性进行注解 LOCAL_VARIABLE 给局部变量进行注解 METHOD 给方法进行注解 PACKAGE 给包进行注解 PARAMETER 给方法内的参数进行注解 TYPE 给一个类型进行注解，比如类、接口 - @Inherited 一个类被 @Inherited 注解过的注解进行注解的话，如果它的子类没有被任何注解应用的话，那么这个子类就继承了该类的注解。\n- @Repeatable 是可重复的意思\nJava 内置注解 @Deprecated 这个元素是用来标记过时的元素 @Override 子类要复写父类中的方法 @SuppressWarnings 忽略编辑器的警告 ⚠ @SafeVarargs 参数安全类型注解。它的目的是提醒开发者不要用参数做一些不安全的操作， 它的存在会阻止编译器产生 unchecked 这样的警告。 @FunctionalInterface 函数式接口注解，函数式接口可以很容易转换为 Lambda 表达式 注解与反射。 注解通过反射获取。首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解\n1 public boolean isAnnotationPresent(Class\u0026lt;? extends Annotation\u0026gt; annotationClass) {} 然后通过 getAnnotation() 方法来获取 Annotation 对象。\n1 public \u0026lt;A extends Annotation\u0026gt; A getAnnotation(Class\u0026lt;A\u0026gt; annotationClass) {} 或者是 getAnnotations() 方法。\n1 public Annotation[] getAnnotations() {} 前一种方法返回指定类型的注解，后一种方法返回注解到这个元素上的所有注解。\n如果获取到的 Annotation 如果不为 null，则就可以调用它们的属性方法了\n注解应用实例 JUnit\n1 2 3 4 5 6 public class JUnitTest { @Test public void solo(){ System.out.println(\u0026#34;Awesome Junit\u0026#34;); } } ","date":"2020-02-15T21:33:56Z","image":"https://cn.bing.com/th?id=OHR.HeartAustralia_EN-CN7705551345_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/java_annotation/","title":"Java 注解的简单学习"},{"content":"Java JDBC 操作 MySQL Maven 导包 1 2 3 4 5 6 7 8 9 10 \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;mysql\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;mysql-connector-java\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;5.1.48\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;groupId\u0026gt;org.junit.jupiter\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;junit-jupiter\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;RELEASE\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;compile\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt; 填在 pom 文件里面即可，无难度\n用代码连接数据库 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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 import java.io.*; import java.sql.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class Fre{ public static final String driver = \u0026#34;com.mysql.jdbc.Driver\u0026#34;; public static final String uri = \u0026#34;jdbc:mysql://127.0.0.1:3306/solo\u0026#34;; public static final String user = \u0026#34;root\u0026#34;; public static final String passcode = \u0026#34;123456\u0026#34;; final String sqlScript = \u0026#34;select * from b3_solo_article\u0026#34;; Connection connection= null; public ResultSet StartCollection() throws SQLException { try{ Class.forName(driver); connection = DriverManager.getConnection(uri,user,passcode); if(!connection.isClosed()) System.out.println(\u0026#34;connection successfully\u0026#34;); Statement statement = connection.createStatement(); ResultSet replyString = statement.executeQuery(sqlScript); return replyString; } catch (ClassNotFoundException e) { e.printStackTrace(); return null; } } public static String toTheJson(List\u0026lt;Map\u0026gt; list){ return list.toString().replace(\u0026#34;{\u0026#34;, \u0026#34;{\\\u0026#34;\u0026#34;).replace(\u0026#34;}\u0026#34;, \u0026#34;\\\u0026#34;}\u0026#34;).replace(\u0026#34;=\u0026#34;,\u0026#34;\\\u0026#34;:\\\u0026#34;\u0026#34;).replace(\u0026#34;, \u0026#34;, \u0026#34;\\\u0026#34;,\\\u0026#34;\u0026#34;).replace(\u0026#34;}\\\u0026#34;,\\\u0026#34;{\u0026#34;, \u0026#34;},{\u0026#34;).replace(\u0026#34;{\u0026#34;, \u0026#34;{\\\u0026#34;\u0026#34;).replace(\u0026#34;}\u0026#34;, \u0026#34;\\\u0026#34;}\u0026#34;).replace(\u0026#34;=\u0026#34;,\u0026#34;\\\u0026#34;:\\\u0026#34;\u0026#34;).replace(\u0026#34;, \u0026#34;, \u0026#34;\\\u0026#34;,\\\u0026#34;\u0026#34;).replace(\u0026#34;}\\\u0026#34;,\\\u0026#34;{\u0026#34;, \u0026#34;},{\u0026#34;).replace(\u0026#34;\\\u0026#34;\\\u0026#34;\u0026#34;,\u0026#34;\\\u0026#34;\u0026#34;); } public void close(){ try{ this.connection.close(); } catch (SQLException e) { e.printStackTrace(); } } @Test public void Solomon() throws SQLException, IOException { Map\u0026lt;String,Object\u0026gt; outer = new HashMap\u0026lt;String, Object\u0026gt;(); List\u0026lt;Map\u0026gt; list = new ArrayList\u0026lt;Map\u0026gt;(); Fre solo = new Fre(); ResultSet so = solo.StartCollection(); while(so.next()){ Map\u0026lt;String,Object\u0026gt; map = new HashMap\u0026lt;String, Object\u0026gt;(); map.put(\u0026#34;User_ID\u0026#34;,so.getString(1)); map.put(\u0026#34;User_Name\u0026#34;,so.getString(2)); map.put(\u0026#34;User_Mod\u0026#34;,so.getString(3)); map.put(\u0026#34;User_IDE\u0026#34;,so.getString(4)); list.add(map); } String Solo = toJson(list); System.out.println(Solo); FileOutputStream fos = new FileOutputStream(\u0026#34;solo.json\u0026#34;,true); OutputStreamWriter osw = new OutputStreamWriter(fos,\u0026#34;utf-8\u0026#34;); osw.write(Solo); System.out.println(\u0026#34;Write Over\u0026#34;); osw.close(); } } } 代码详解 1 2 3 4 5 6 public static final String driver = \u0026#34;com.mysql.jdbc.Driver\u0026#34;; public static final String uri = \u0026#34;jdbc:mysql://127.0.0.1:3306/solo\u0026#34;; public static final String user = \u0026#34;root\u0026#34;; public static final String passcode = \u0026#34;123456\u0026#34;; final String sqlScript = \u0026#34;select * from b3_solo_article\u0026#34;; Connection connection= null; 在定义这些常量的时候，最好不要直接定义 String ,而是 public static final String ,怎么安全，怎么来\n而且对象尽量不要在方法里面 new ,如果发生异常就不能释放系统资源了\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 public ResultSet StartCollection() throws SQLException { try{ Class.forName(driver); connection = DriverManager.getConnection(uri,user,passcode); if(!connection.isClosed()) System.out.println(\u0026#34;connection successfully\u0026#34;); Statement statement = connection.createStatement(); ResultSet replyString = statement.executeQuery(sqlScript); return replyString; } catch (ClassNotFoundException e) { e.printStackTrace(); return null; } } 如果从安全角度来考虑的话，那么我们不应该用 Statement 而是用 PreparedStatement ,我在自己电脑上也不怕什么 SQL注入，所以没有用\n1 2 3 public static String toTheJson(List\u0026lt;Map\u0026gt; list){ return list.toString().replace(\u0026#34;{\u0026#34;, \u0026#34;{\\\u0026#34;\u0026#34;).replace(\u0026#34;}\u0026#34;, \u0026#34;\\\u0026#34;}\u0026#34;).replace(\u0026#34;=\u0026#34;,\u0026#34;\\\u0026#34;:\\\u0026#34;\u0026#34;).replace(\u0026#34;, \u0026#34;, \u0026#34;\\\u0026#34;,\\\u0026#34;\u0026#34;).replace(\u0026#34;}\\\u0026#34;,\\\u0026#34;{\u0026#34;, \u0026#34;},{\u0026#34;).replace(\u0026#34;{\u0026#34;, \u0026#34;{\\\u0026#34;\u0026#34;).replace(\u0026#34;}\u0026#34;, \u0026#34;\\\u0026#34;}\u0026#34;).replace(\u0026#34;=\u0026#34;,\u0026#34;\\\u0026#34;:\\\u0026#34;\u0026#34;).replace(\u0026#34;, \u0026#34;, \u0026#34;\\\u0026#34;,\\\u0026#34;\u0026#34;).replace(\u0026#34;}\\\u0026#34;,\\\u0026#34;{\u0026#34;, \u0026#34;},{\u0026#34;).replace(\u0026#34;\\\u0026#34;\\\u0026#34;\u0026#34;,\u0026#34;\\\u0026#34;\u0026#34;); } 主要是我没有导 JSON 相关的库，就这样简化 List\u0026lt;Map\u0026lt;String,String\u0026raquo; \u0026raquo;\u0026gt; JSON 主要就是字符串的替换，还有转义可能有点懵 ?,小心即可\n1 2 3 4 5 6 7 8 public void close(){ try{ this.connection.close(); } catch (SQLException e) { e.printStackTrace(); } } 使用完资源以后肯定也要释放的，没有什么好说的\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Test public void Solomon() throws SQLException, IOException { Map\u0026lt;String,Object\u0026gt; outer = new HashMap\u0026lt;String, Object\u0026gt;(); List\u0026lt;Map\u0026gt; list = new ArrayList\u0026lt;Map\u0026gt;(); Fre solo = new Fre(); ResultSet so = solo.StartCollection(); while(so.next()){ Map\u0026lt;String,Object\u0026gt; map = new HashMap\u0026lt;String, Object\u0026gt;(); map.put(\u0026#34;User_ID\u0026#34;,so.getString(1)); map.put(\u0026#34;User_Name\u0026#34;,so.getString(2)); map.put(\u0026#34;User_Mod\u0026#34;,so.getString(3)); map.put(\u0026#34;User_IDE\u0026#34;,so.getString(4)); list.add(map); } String Solo = toTheJson(list); System.out.println(Solo); FileOutputStream fos = new FileOutputStream(\u0026#34;solo.json\u0026#34;,true); OutputStreamWriter osw = new OutputStreamWriter(fos,\u0026#34;utf-8\u0026#34;); osw.write(Solo); System.out.println(\u0026#34;Write Over\u0026#34;); osw.close(); } } 写完先测试一下\n","date":"2020-02-01T21:33:56Z","image":"https://cn.bing.com/th?id=OHR.Taormina_EN-CN8042812380_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/java_jdbc/","title":"Java通过jdbc操作MySQL数据库"},{"content":"(1) 按两下 gg，定位光标到第一行。\n(2) 按住 Shift+v，进入可视化编辑的列编辑模式。\n(3) Shift+g，选中整个代码。\n(4) 按下等号 =，格式化所有代码。\n(5) 新年快乐🎉\n","date":"2020-02-01T17:27:33Z","image":"https://cn.bing.com/th?id=OHR.ChurchRock_EN-CN7171111056_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/vim_hot_key/","title":"vim的常用操作快捷键"},{"content":"nginx 反向代理 nginx 一直都是我们比较常用的工具，它不仅功能强悍，而且性能也非常好 ?,一直深受开发者的喜爱\n并且我们经常用 nginx 反代来做负载均衡，那么 nginx の反代原理我也就不说了，咱就说咋操作吧\n我把我的网站的 nginx 配置给拷贝了下来\n1 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 server { listen 443 ssl; server_name www.jokeme.top; add_header Strict-Transport-Security \u0026#34;max-age=31536000; includeSubDomains;preload\u0026#34; always; ssl_certificate /ssl/3355633_www.jokeme.top.pem; ssl_certificate_key /ssl/3355633_www.jokeme.top.key; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; ssl_prefer_server_ciphers on; location / { root /usr/share/nginx/html; index index.html index.htm; proxy_pass http://www.jokeme.top:8080; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } server{ listen 80; server_name localhost; rewrite ^(.*) https://$host$1 permanent; } 我的网站呢，是通过 docker 跑在 8080 端口的 https ,如果日常需要访问的话是不太方便の,还需要敲端口号\nnginx 通过把 8080 端口的数据代理到 443 端口，来实现我们直接通过域名来访问，也就是 location 里面の proxy_pass实现了反向代理的功能\n而且当用户通过 80 端口访问 http 的时候还会自动转到 443 端口的 https 这个就是下面的那个 server rewrite实现的\n还有一个就是大家反代的时候一定要确认你那个端口是允许访问的，要不然你的配置没有问题，但就是拒接连接，或者连接超时，昨天晚上我就出现了这种情况，配置没有问题，但是网站就是不能访问，然后倒腾了一圈，才想起来是我防火墙没有开放端口 ?\n","date":"2020-01-11T21:33:56Z","image":"https://cn.bing.com/th?id=OHR.Molas_EN-CN3740095364_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/nginx_proxy/","title":"学习nginx代理相关的笔记"},{"content":"微信小程序遇到的坑 微信小程序其实很简单，和 HTML 差不多，但是又加入了微信的很多特色，我也是本着写着玩的心态写了几天的小程序，发现有很多坑哦，和 HTML还是有点差距的\n文本超出隐藏并加… 其实在 HTML 里面这段代码很简单，只要在父元素里面定义\n1 2 3 overflow:hidden; text-overflow: ellipsis; white-space: nowrap; 但是微信小程序就邪了门了，在电脑上看的好好的，可到了真机预览就失效了，\n而且还只是 iOS 平台出现问题，Android 并没有问题，这就见鬼了吗！!同样的代码，还能有不同疗效的？??\nAnswer : 然后百度，得答案：子元素还必须是 text 元素才会生效，这是因为 iOS 的 Safari 浏览器的特立独行吧，\n还有的说需要设置一下宽度就可以解决，反正这个方法我没有试，懒得试\nbackground-attachment:fixed 这个属性是用来设置背景图的，但是还是在 iOS 这里出现了问题，反观 Android 那边一切都很正常 ?\n本来固定背景图片就是一个常用的功能，但是 iOS 这边就是死活不兼容这个属性，一开始我这样写，出错了我还以为是我自己の问题，在哪里倒腾了大半天，无果，百度发现这是一个很普遍的现象，那些大神解决问题的方式倒也很直接，不用这个属性，自己在写一个 view 放在最下面一层\nAnswer : 1 2 3 4 5 6 7 8 position:fixed; top:0; left:0; width:100%; height:100%; background:url(\u0026#39;http://172.17.150.251:8080/pic/bk7.jpg\u0026#39;) no-repeat #000; background-size:cover; z-index:-1; wx:for 1 2 3 4 5 6 7 8 \u0026lt;view class=\u0026#34;acf\u0026#34; wx:for=\u0026#34;{{heloi}}\u0026#34; wx:key=\u0026#34;index\u0026#34;\u0026gt; \u0026lt;view class=\u0026#34;bhk\u0026#34; data-arturl=\u0026#34;{{item.article}}\u0026#34; bindtap=\u0026#34;showNextPage\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;guli\u0026#34;\u0026gt;\u0026lt;text class=\u0026#34;intro\u0026#34;\u0026gt;{{item.introduce}}\u0026lt;/text\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;ddas\u0026#34;\u0026gt; \u0026lt;image class=\u0026#34;pic\u0026#34; src=\u0026#39;{{item.picurl}}\u0026#39;\u0026gt;\u0026lt;/image\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/view\u0026gt; \u0026lt;/view\u0026gt; 1 2 3 4 5 6 7 8 9 10 11 onLoad: function () { var _self = this wx.request({ url: \u0026#34;http://172.17.150.251:8080/as.json\u0026#34;, success: function (qum) { _self.setData({ heloi: qum.data.page, }) } }) }, wx:for 用来解析服务器返回的 JSON 是真的很棒的，可以一键迭代生成很多的内容\n不同页面间传参 这是困扰我最长时间的一个问题了，因为不太熟悉小程序，只能看别人怎么写，我就跟着这样写，毫无灵魂，也上网百度了，都是看的一知半解，就目前而言，我还不是特别会页面间的传参，但是，还是可以写出来点东西的\n上代码：\n1 \u0026lt;view class=\u0026#34;bhk\u0026#34; data-arturl=\u0026#34;{{item.article}}\u0026#34; bindtap=\u0026#34;showNextPage\u0026#34;\u0026gt;LOL?\u0026lt;/view\u0026gt; 不知道为什么这个 data-arturl 必须要和 bindtap 一起才可以读取到数据，如果把 bingtap 定义在 data-arturl 上面，我怎么样都拿不到数据，实在是搞不懂啥原因 ?\n1 2 3 4 5 6 7 8 9 10 11 showNextPage: function (event) { var url = event.currentTarget.dataset.arturl console.log(url) wx.navigateTo({ url: \u0026#34;/pages/aitrcle/aitrcle?url=\u0026#34;+url, success: function () { console.log(\u0026#34;Success! Congratulate!\u0026#34;) }, fail: function () { console.log(\u0026#34;faild!!!\u0026#34;) }, }) } 下面是 article 的 js\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 onLoad: function (options) { var that = this that.data.paurl =options.url console.log(that.data.paurl) wx.request({ url:that.data.paurl, success:function(){ console.log(\u0026#34;Yes! U did\u0026#34;) }, false:function(){ console.log(\u0026#34;false\u0026#34;) } }) } 我只会这种简单的页面传参，还有别的传参方法，比如：\n1 2 3 4 5 使用数据库传递数据, 全局变量使用方法 使用缓存传递参数,使用组件模板 template传递参数 小程序页面跳转 常见的有四种方法\nwx.navigateTo({}) 保留当前页面，跳转到应用内的某个页面，使用 wx.navigateBack 可以返回; wx.redirectTo() 关闭当前页面，跳转到非 tabBar 的某个页面 ** ** ** 点击跳转 ** wx.switchTab 跳转到 tabBar 的某个页面 举个例子 ?\n1 2 3 4 5 6 7 8 9 10 11 12 13 wx.navigateTo({ url:\u0026#39;../test/test?id=1\u0026amp;page=4\u0026#39;, //跳转页面的路径，可带参数 ？隔开，不同参数用 \u0026amp; 分隔；相对路径，不需要.wxml后缀 success:function(){} //成功后的回调； fail：function(){} //失败后的回调； complete：function(){} //结束后的回调(成功，失败都会执行) }) //传递的参数在接收页面onLoad()函数中得到值：option.id就可以得到了 onLoad: function (option) { console.log(option)//可以打印一下option看查看参数 this.setData({ id:option.id, }); 这大概就是这几天踩得坑，虽然都不难，但是对于我们这种初学者还是有一点难度的 ?,暂时就更新到这里，过几天遇到了坑，再继续更新\n","date":"2020-01-10T17:27:33Z","image":"https://cn.bing.com/th?id=OHR.ChuDangYa_EN-CN3555284407_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/wechat_little_program/","title":"微信小程序遇见的坑"},{"content":"Python 爬虫 用python写爬虫其实是比较简单的，主要还是靠第三方的库，常用的有 requests \u0026amp; urllib 至于解析 HTML,我目前使用的是 xpath ,了解了基本操作，咱就试试看吧\n代码附上\n1 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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 # coding:utf-8 import json import requests from lxml import etree def getWebUrl(uri): webPage = requests.get(url = uri, headers={\u0026#34;User-Agent\u0026#34;:\u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36\u0026#34;} ) we = webPage.text ssr = etree.HTML(we) Links = ssr.xpath(\u0026#34;//div/table/tbody/tr/td/p/a/@href\u0026#34;) Str = \u0026#34;http://172.17.150.251\u0026#34; Allinks = [] for se in Links: link = Str+se Allinks.append(link) dicName = {} dicName = {uri:Allinks} return dicName def getInfo(uri): webPage = requests.get(url = uri, headers={\u0026#34;User-Agent\u0026#34;:\u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36\u0026#34;} ) page = webPage.text pageinfo = etree.HTML(page) info = pageinfo.xpath(\u0026#34;//div/span[@class=\u0026#39;sp4\u0026#39;]/text()\u0026#34;) notNesInfo = pageinfo.xpath(\u0026#34;//div/span[@class=\u0026#39;sp2\u0026#39;]/text()\u0026#34;) salaryInfo = pageinfo.xpath(\u0026#34;//div[@class=\u0026#39;cn\u0026#39;]/strong/text()\u0026#34;) job = pageinfo.xpath(\u0026#34;//p[position()=1]/span[@class=\u0026#39;el\u0026#39;]/text()\u0026#34;) city = pageinfo.xpath(\u0026#34;//span[@class=\u0026#39;lname\u0026#39;]/text()\u0026#34;) if (len(notNesInfo)!=0): for er in notNesInfo: info.append(er) information = {\u0026#34;info\u0026#34;:info,\u0026#39;salaryInfo\u0026#39;:salaryInfo,\u0026#39;job\u0026#39;:job,\u0026#39;city\u0026#39;:city} return information # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ we = {} fun = {} tot = [] wilu = [] for a in range(1,41): strin = \u0026#34;http://172.17.150.251/xueqing-web/course/index/{}\u0026#34;.format(a) tot.append(strin) we = getWebUrl(strin) fun.update(we) we.clear() for osx in tot: wilu = fun.get(osx) for xs in wilu: ufo = {} ufo = getInfo(xs) fuc ={xs:ufo} we.update(fuc) wei = json.dumps(we,indent=1,ensure_ascii=False) with open(\u0026#34;wuli.json\u0026#34;,\u0026#39;w\u0026#39;,encoding=\u0026#39;utf-8\u0026#39;) as f: f.write(wei) print(\u0026#34;?\u0026#34;) 因为我是真的又菜又爱玩，所以才写的这么烂的代码 ?,以后还是要慢慢提升啊\n我爬取的是 51job 网站，可是如果爬虫大规模爬取，网站就会封 IP,所以我就把页面抓下来，放到本地，开一个 Web 服务器，慢慢调试代码代码 ?‍♂️\n最基本的两个库，requests \u0026amp; lxml simplejson 不是必要的，我只是为了把爬取到的数据保存到本地查看才出此下策，实在是菜的无奈 ?才这样干了，其实我也是懒得配置数据库了\n代码解释 getWebUrl() 是拿取网站一共有多少页并且获取该页面下的求职信息详情页面链接，再传递给下一个函数处理我给写死了\ngetInfo() 处理geWebUrl() 传递过来的详情页面链接，并爬取数据返回我需要的求职信息\n还有需要注意的是 JSON 格式化dictChinese 时，非常容易乱码，所以在 dumps()方法里，我没有用 ascii,这也算是一个小坑吧，一开始的时候 我保存数据，不管用啥方法都乱码，心态都炸了，# coding:utf-8也用了，就是不奏效\n爬取到的信息节选 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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 { \u0026#34;http://172.17.150.251/xueqing-web/51job/72396849.html\u0026#34;: { \u0026#34;info\u0026#34;: [ \u0026#34;3-4年经验\u0026#34;, \u0026#34;本科\u0026#34;, \u0026#34;招1人\u0026#34;, \u0026#34;02-08发布\u0026#34;, \u0026#34;英语良好\u0026#34;, \u0026#34;计算机科学与技术 自动化\u0026#34; ], \u0026#34;salaryInfo\u0026#34;: [ \u0026#34;15-28万/年\u0026#34; ], \u0026#34;job\u0026#34;: [ \u0026#34;软件工程师\u0026#34;, \u0026#34;算法工程师\u0026#34; ], \u0026#34;city\u0026#34;: [ \u0026#34;广州\u0026#34; ] }, \u0026#34;http://172.17.150.251/xueqing-web/51job/98287424.html\u0026#34;: { \u0026#34;info\u0026#34;: [ \u0026#34;5-7年经验\u0026#34;, \u0026#34;大专\u0026#34;, \u0026#34;招若干人\u0026#34;, \u0026#34;02-08发布\u0026#34; ], \u0026#34;salaryInfo\u0026#34;: [ \u0026#34;1-2万/月\u0026#34; ], \u0026#34;job\u0026#34;: [ \u0026#34;销售经理\u0026#34; ], \u0026#34;city\u0026#34;: [ \u0026#34;北京\u0026#34; ] }, \u0026#34;http://172.17.150.251/xueqing-web/51job/98370374.html\u0026#34;: { \u0026#34;info\u0026#34;: [ \u0026#34;5-7年经验\u0026#34;, \u0026#34;本科\u0026#34;, \u0026#34;招2人\u0026#34;, \u0026#34;02-08发布\u0026#34; ], \u0026#34;salaryInfo\u0026#34;: [], \u0026#34;job\u0026#34;: [ \u0026#34;高级软件工程师\u0026#34;, \u0026#34;网站运营经理/主管\u0026#34; ], \u0026#34;city\u0026#34;: [ \u0026#34;深圳\u0026#34; ] }, \u0026#34;http://172.17.150.251/xueqing-web/51job/97846012.html\u0026#34;: { \u0026#34;info\u0026#34;: [ \u0026#34;无工作经验\u0026#34;, \u0026#34;本科\u0026#34;, \u0026#34;招若干人\u0026#34;, \u0026#34;02-08发布\u0026#34;, \u0026#34;英语熟练\u0026#34;, \u0026#34;计算机科学与技术 计算机网络\u0026#34; ], \u0026#34;salaryInfo\u0026#34;: [ \u0026#34;10-15万/年\u0026#34; ], \u0026#34;job\u0026#34;: [ \u0026#34;系统工程师\u0026#34;, \u0026#34;系统架构设计师\u0026#34; ], \u0026#34;city\u0026#34;: [ \u0026#34;北京-昌平区\u0026#34; ] }, \u0026#34;http://172.17.150.251/xueqing-web/51job/96612644.html\u0026#34;: { \u0026#34;info\u0026#34;: [ \u0026#34;无工作经验\u0026#34;, \u0026#34;招若干人\u0026#34;, \u0026#34;02-08发布\u0026#34; ], \u0026#34;salaryInfo\u0026#34;: [ \u0026#34;1-2万/月\u0026#34; ], \u0026#34;job\u0026#34;: [ \u0026#34;信息技术经理/主管\u0026#34; ], \u0026#34;city\u0026#34;: [ \u0026#34;北京-西城区\u0026#34; ] } } ","date":"2019-12-31T21:33:56Z","image":"https://cn.bing.com/th?id=OHR.BolivianSummer_EN-CN6022233005_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/python_spider/","title":"Python_spider"},{"content":"Collection 框架的工具类 Collections sort 按照自然顺序排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package sourceCode; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class Collections_utils { public static void main(String[] args) { List\u0026lt;String\u0026gt; se = new ArrayList\u0026lt;String\u0026gt;(); se.add(\u0026#34;fdw\u0026#34;); se.add(\u0026#34;jokeme\u0026#34;); se.add(\u0026#34;dfipo\u0026#34;); se.add(\u0026#34;daase\u0026#34;); Collections.sort(se); System.out.println(se); } } ~~~~~~~~~~~~~~~~~~~~ [daase, dfipo, fdw, jokeme] 用法很简单： Collections.sort(List)\nreverse 倒序排列自然排序的 List 1 2 3 4 Collections.reverse(se); System.out.println(se); ~~~~~~~~~~~~~~~ [jokeme, fdw, dfipo, daase] 用法也很简单： Collections.reverse(List)\n如果单独使用时，其效果和 reverseOrder() 作用一样\n自定义排序方法 1 Collections.reverseOrder(Comparator\u0026lt;T\u0026gt; cmp()) 自定义排序方法时就需要使用 reverseOrder() 并且需要自定义一个比较方法 ? 传进去\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Collections.sort(se, Collections.reverseOrder(new compareByLength())); System.out.println(se); ​~~~~~~~~~~~~~~~ [fdw, daase, dfipo, jokeme] ​~~~~~~~~~~~~~~~ package sourceCode; import java.util.Comparator; public class compareByLength implements Comparator\u0026lt;String\u0026gt; { @Override public int compare(String str1,String str2) { int temp = str2.length() - str1.length(); return temp==0?str2.compareTo(str1):temp; } } **上面我们可以看出 reverseOrder() 就是万金油，不传参就是倒序排列自然排序的 List , 而传参就可以自定义我们需要的比较方法 \u0026lt; 注意 reverseOrder 要联合 sort 使用，只有 reverse 可以单独使用 \u0026gt; **\n将非同步集合转为同步集合 ","date":"2019-12-28T21:33:56Z","image":"https://cn.bing.com/th?id=OHR.RSOakTree_EN-CN8050195880_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/java_collections_utils/","title":"Java collections框架的工具类"},{"content":"Java-IO Java Input-Output 主要是用于文件的存储和读取，相关操作一般都会放在 Java.io 包中\nFile 类 file 类的方法是我们操作文件必不可少的，是必须要学习的类\n使用示例： 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package Java_IO; import java.io.File; import java.io.IOException; public class fileClass { public static void main(String[] args) throws IOException { File se = new File(\u0026#34;D:\\\\Frelon.txt\u0026#34;); File we = new File(\u0026#34;D:\\\\\u0026#34;,\u0026#34;sda.py\u0026#34;); System.out.println(se+\u0026#34;~~~~\u0026#34;+we); File dir = new File(\u0026#34;D:\\\\KW\u0026#34;+File.separator); File sdCard = new File (dir,\u0026#34;a.java\u0026#34;); if(sdCard.createNewFile()) { System.out.println(\u0026#34;Create File Successfully ! \u0026#34;+sdCard); }else { System.out.println(\u0026#34;Filed!\u0026#34;); } } } File类常见方法 获取文件路径 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package Java_IO; import java.io.File; public class filePath { public static void main(String[] args) { File file = new File(\u0026#34;a.sh\u0026#34;); String str = file.getAbsolutePath(); String str1 = file.getPath(); System.out.println(str+\u0026#34;~~~~~\u0026#34;+str1); } } ​~~~~~~~~~~~~~~~~~~ D:\\github\\Hadoop_build\\JavaLearn\\a.sh~~~~~a.sh getPath()只是获取相对路径 \u0026lt; 程序在哪运行，路径就是那里 \u0026gt;\ngetAbsolutePath()表示获取绝对路径\n获取文件修改时间 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package Java_IO; import java.io.File; import java.text.DateFormat; import java.util.Date; public class filePath { public static void main(String[] args) { File file = new File(\u0026#34;.classpath\u0026#34;); Long lon = file.lastModified(); Date date = new Date(lon); String mod_time = DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.LONG).format(date); System.out.println(lon+\u0026#34; \u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt; \u0026#34;+mod_time); } } 1 1575876750643 \u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt; 2019年12月9日 下午03时32分30秒 该方法并不困难，稍稍有难度的是如何把获取到的 Long 类型的数字转化为具体的时间\n1 2 3 4 Date date = new Date(1575876750643l); DateFormat time = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG); String str = time.format(date); System.out.println(str); 常见操作 1 2 3 4 5 6 7 8 9 boolean boof = file.createNewFile(); boolean boo = file.canExecute(); boolean booa = file.canRead(); boolean bood = file.canWrite(); boolean booh = file.exists(); boolean booj = file.isAbsolute(); boolean book = file.isDirectory(); boolean bool = file.isFile(); boolean booz = file.isHidden(); 认识即可，没有难度\n获取文件夹内容 当操作文件夹时，肯定要知道这个文件夹里面有什么东西，我们才好操作，常用的方法有两种 list() , listFile() 但是建议用后者，因为list() 有的它也有，他有的方法**list()**没有\n1 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 package Java_IO; import java.io.File; public class visitFile { public static void main(String[] args) { File alean = new File(\u0026#34;c:\\\\\u0026#34;); if (alean.exists()) { String[] dd = alean.list(); for (String ss : dd) { System.out.println(ss); } System.out.println(\u0026#34;~~~~~~~~~~~~~~~~~~~~~~\u0026#34;); File[] fo = alean.listFiles(); for (File fs : fo) { if(fs.getName().contains(\u0026#34;Logs\u0026#34;)) { System.out.println(fs+\u0026#34; ~~~ \u0026#34;+fs.getName()); } } System.out.println(\u0026#34;~~~~~~~~~~~~~~~~~~~~~~\u0026#34;); Long sis = alean.getFreeSpace(); System.out.println(alean + \u0026#34; \u0026#34; + sis / Math.pow(1024, 3) + \u0026#34;Gb\u0026#34; + \u0026#34; free!\u0026#34;); } else { System.out.println(\u0026#34;No this file\u0026#34;); } } } 文件过滤器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 File[] str1 = dir.listFiles(new fileFilterByName()); for (File strr : str1) { System.out.println(strr); } ​~~~~ package Java_IO; import java.io.File; import java.io.FileFilter; public class fileFilterByName implements FileFilter { @Override public boolean accept(File arg0) { return arg0.isDirectory(); } } 它可以过滤出该 ?内的 ?,还可以过滤出 ?内的 ?,只需要把 fileFilterByName の accept() 方法改一下即可\n1 2 3 4 5 6 public boolean accept(File arg0) { return arg0.isDirectory(); //\treturn arg0.isFile(); // return arg0.isFile(); // return arg0.getName().endSWith(\u0026#34;.java\u0026#34;) } 文件名过滤器 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 package Java_IO; import java.io.File; import java.io.FilenameFilter; public class filterByName implements FilenameFilter { private String se=\u0026#34;\u0026#34;; public filterByName(String se) { super(); this.se = se; } public filterByName() { super(); } @Override public boolean accept(File arg0, String arg1) { return arg1.endsWith(se); } } ​~~~~~~~~~~~~~~~~~~~~~~ package Java_IO; import java.io.File; public class FileFilter { public static void main(String[] args) { File file = new File(\u0026#34;D:\\\\hadoop-2.8.5\u0026#34;); String[] str = file.list(new filterByName(\u0026#34;.txt\u0026#34;)); for (String name : str) { System.out.println(name); } } } 这种过滤器不常用，因为需要一个过滤器实现FileNameFilter, 实际 coding 我们可以使用FileFilter 替代，它不仅仅可以过滤文件夹 ?还可以过滤文件 ?\n递归 获取 ?内的内容 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 package Java_IO; import java.io.File; import java.io.IOException; import java.nio.file.NoSuchFileException; public class getAllFiles { public static void main(String[] args) throws IOException { File dir = new File(\u0026#34;D:\\\\github\u0026#34;); getAllFile(dir); } public static void getAllFile(File se) throws IOException { File[] dir1 = se.listFiles(); if(!se.exists()) { throw new NoSuchFileException(\u0026#34;No such Path\u0026#34;); } for (File der : dir1) { if (der.isDirectory()) { getAllFile(der); } else { System.out.println(\u0026#34;Visited \u0026#34; + der); } } } } 什么是递归？\n其实 递归就是在方法内部调用该方法，该方法不断地进栈，在使用递归时，一定要有判断条件，否则一直递归下去会导致内存溢出\n1 2 3 4 5 6 7 public void met(){ void show(); } public void show(){ void met(); } 这就是一种递归，但是它是错误 ❌的递归，两个方法相互调用会导致栈内存溢出，\n1 2 3 public void show(){ void show(); } 这也是一种错误 ❌的递归，方法内部无线调用自己 ✌会导致栈内存溢出\n1.3.1 使用递归删除 ? 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 package Java_IO; import java.io.File; import java.nio.file.NoSuchFileException; public class deleteFiles { public static void main(String[] args) throws NoSuchFileException { File dir = new File(\u0026#34;D:\\\\KWS\u0026#34;); removeFiles(dir); } public static void removeFiles(File se) throws NoSuchFileException { File [] wow = se.listFiles(); for(File wo : wow) { if(!wo.exists()) { throw new NoSuchFileException(null); } if(wo.isDirectory()) { removeFiles(wo); }else { Boolean esc = wo.delete(); System.out.println(\u0026#34;delete the file \u0026#34;+wo+\u0026#34; \u0026#34;+esc); } } Boolean esc = se.delete(); System.out.println(\u0026#34;delete the file \u0026#34;+se+\u0026#34; \u0026#34;+esc); } } 递归删除很简单，只是需要注意，这个删除是不经过回收站的，会被直接删除，不可找回\n还有就是需要判断该 ?有没有权限访问，要不然会报 java.lang.NullPointerException 异常，甚至还需要我们自己来抛一些 IO 异常\n前三 part 测试 需求：\n1 2 获取某个?内所有文件的集合 获取该?内某个?内的.txt文件,并存储到集合中打印?出来 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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package Java_IO; import java.io.File; import java.io.FilenameFilter; import java.util.ArrayList; import java.util.List; public class IO_Test1 { public static void main(String[] args) { File dir = new File(\u0026#34;c:\\\\tempfile\u0026#34;); List\u0026lt;String\u0026gt; list = new ArrayList\u0026lt;String\u0026gt;(); fileFilterByName filter = new fileFilterByName(\u0026#34;.txt\u0026#34;); getFile(dir, filter, list); for (String ser : list) { System.out.println(ser); } } private static void getFile(File file, fileFilterByName filter, List\u0026lt;String\u0026gt; list) { File[] fileName = file.listFiles(); for (File se : fileName) { if (se.isDirectory()) { getFile(se, filter, list); } else { if (filter.accept(se)) { list.add(se.getPath()); } } } } } ​~~~~~~~~~~~~~~~~~~~~ package Java_IO; import java.io.File; import java.io.FileFilter; public class fileFilterByName implements FileFilter { private String str; public fileFilterByName(String str) { super(); this.str = str; } public fileFilterByName() { super(); } @Override public boolean accept(File arg0) { return arg0.getName().endsWith(this.str); } } 给自己点个赞 ?,我真厉害?\nFileOutputStream 向 ?内写入内容 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 package Java_IO; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; public class writeIntoFile { private static final String Line_separator = System.getProperty(\u0026#34;line.separator\u0026#34;); public static void main(String[] args) throws IOException { File dir = new File(\u0026#34;c:\\\\tempfile\u0026#34;); if(!dir.exists()) { dir.mkdir(); } String str =\u0026#34;i Love Java\u0026#34;; FileOutputStream fops = null; try { fops = new FileOutputStream(\u0026#34;c:\\\\tempfile\\\\fops.java\u0026#34;,true); String ssr = Line_separator+\u0026#34;Hello World\u0026#34;+Line_separator; fops.write(ssr.getBytes()); fops.write(str.getBytes()); fops.write(ssr.getBytes()); System.out.println(\u0026#34;写入成功\u0026#34;); } catch (IOException e) { e.printStackTrace(); } finally { if(fops!=null) {} fops.close(); } } } 写入内容很容易，只需要调用一下write()方法即可，但是难点就是处理各种Exception ,我们需要考虑到各种各样的异常\n比如 FileNotFoundException,,,NullPointerException 等等等， 因为一旦出现了这些问题我们的代码可能就占用了相关的系统资源，但是程序执行失败后，我们并没有释放该资源，如此循环往复就会导致系统的卡慢，甚至死机重启，这里我们经常使用try catch finally 方法来解决，需要执行的代码放在try 里面，在finally 里面放 close()方法，保证我们的代码在申请完资源以后，无论是否报错，都可以被释放掉，还有一点就是如果我们使用try catch finally 那我们一定不能在try里面 new 对象，因为到时候finally 关闭资源时会找不到对象 ?的\n还有write() 方法是会覆盖掉文件内部的内容的，如果我们需要在当前内容上继续添加内容，那就需要在创建对象时加一个true 表示续写该文件，代码如下\n1 FileOutputStream fops = new FileOutputStream(\u0026#34;c:\\\\tempfile\\\\fops.java\u0026#34;,true); FileInputStream 读取 ?内容 FileInputStream 类和 FileOutputStream 类有许多的共同点，都需要传一个 File 类的对象(new 对象时要保证 Path✔无误，要不然很容易造成空指针异常) 还有就是在使用完资源以后要及时释放掉资源，还有要注意的是异常の处理，如果没有处理好异常也会导致我们无法释放掉资源，导致程序占用过多的系统资源，使系统卡慢\n❌ read() 入门级读取 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package Java_IO; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class readFile { private static final int LIMSize = 8192; public static void main(String[] args) throws IOException { File dir = new File(\u0026#34;c:\\\\tempfile\\\\fops.java\u0026#34;); FileInputStream fipts = new FileInputStream(dir); int byt; while ((byt = fipts.read()) != -1) { System.out.println(byt); } fipts.close(); } } 日常开发中，基本上用不上 PASS ❌\n❌ read() 初级读取 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package Java_IO; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class readFile { private static final int LIMSize = 8192; public static void main(String[] args) throws IOException { File dir = new File(\u0026#34;c:\\\\tempfile\\\\fops.java\u0026#34;); byte[] buf = new byte[2]; FileInputStream fip = new FileInputStream(dir); int len = fip.read(buf); System.out.println(len + \u0026#34;~~~\u0026#34; + new String(buf)); fip.close(); } } 日常开发中，基本上用不上 PASS ❌\n✔ read() 进阶级读取 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package Java_IO; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class readFile { private static final int LIMSize = 8192; public static void main(String[] args) throws IOException { File dir = new File(\u0026#34;c:\\\\tempfile\\\\fops.java\u0026#34;); FileInputStream fips = new FileInputStream(dir); byte[] by = new byte[LIMSize]; int length = 0; while ((length = fips.read(by)) != -1) { System.out.println(new String(by, 0, length)); } fips.close(); } } 日常开发中可以使用，✔建议根据需要改变缓冲区的大小，一般建议设置成 :8192\n✔ read()高级读取 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package Java_IO; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class readFileAdvantage { public static void main(String[] args) throws IOException { File dir = new File(\u0026#34;c:\\\\tempfile\\\\fops.java\u0026#34;); FileInputStream fips = new FileInputStream(dir); byte [] byt = new byte [fips.available()]; fips.read(byt); System.out.println(new String(byt)); fips.close(); } } ✔这种是我比较喜欢的一种方法，简洁好用，也不用考虑缓冲区大小 ?,而且一般 File 对象没有问题也就不会出错啊(相对于前面几个),这是重点 ✔,圈起来！下次会考！!!\n但是！!但是！!但是！ 这种方法只是比较适合处理比较小的文件，如果处理好几个 G 的文件，当场挂..所以呢，还是 进阶 和 高级 一起用\nFileOutputStream 和 FileInputStream 一起读写文件 低级写法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package Java_IO; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class IO_CopyFile { public static void main(String[] args) throws IOException { File dir = new File(\u0026#34;C:\\\\tempfile\\\\fops.java\u0026#34;); FileInputStream fis = new FileInputStream(dir); File dir1 = new File(\u0026#34;C:\\\\tempfile\\\\fops.txt\u0026#34;); FileOutputStream fos = new FileOutputStream(dir1); int len = 0; while ((len = fis.read()) != -1) { fos.write(len); } fis.close(); fos.close(); } } 不推荐这种写法，因为效率比较低，读取一个写入一个，太浪费时间\n高级写法 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 package Java_IO; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import APICourceCode.dateAPI; public class copyFileByBuf { public static void main(String[] args) throws IOException { dateAPI date = new dateAPI(); date.getDate(); File dir = new File(\u0026#34;d:\\\\tempfile\\\\idea.exe\u0026#34;); FileOutputStream fos = null; FileInputStream fis = null; try { fis = new FileInputStream(dir); fos = new FileOutputStream(\u0026#34;d:\\\\tempfile\\\\idea.wilu\u0026#34;); byte[] buf = new byte[10240]; int len = 0; while ((len = fis.read(buf)) != -1) { fos.write(buf, 0, len); } } catch (IOException e) { throw new IOException(); } finally { fis.close(); fos.close(); date.getDate(); System.out.println(\u0026#34;Copy Over\u0026#34;); } } } 我无聊就试了试，这个byte 数组の长度对IO の性能影响有多大，就把长度从 10,102,1024,2048,4096,8192,10240 都试了个遍，得出以下答案：\n时间\\byte 长度 10 102 1024 2048 4096 8192 10240 20480 Start 19:38:27 19:53:42 19:54:52 19:55:50 19:56:25 19:57:05 19:57:40 19:58:16 Stop 19:44:33 19:54:19 19:54:57 19:55:54 19:56:28 19:57:08 19:57:42 19:58:18 time 366s 37s 5s 4s 3s 3s 2s 2s 测试所使用的文件大小为： 584935KB 大约是 571MB\n可以看出 byte 数组的长度也是越大越好的，只要你内存足够大，磁盘性能足够强，理论上是可以做到一秒内复制任何文件的，可是！! 科技日益发达的今天电脑内存和磁盘还依然是一个瓶颈，有待我们突破啊，So 为了适应各种大小的文件，还是选择大小为8192 或者10240 也是可以的\nBuffer 缓冲区 buffer 原理就是调用一片内存，作为缓冲区，然后再为 FileInput/OutputStream 使用，原理和上面的相似\n使用示例 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 package Java_IO; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class buffer { public static void main(String[] args) throws IOException { copyfile(); } public static void copyfile() throws IOException { File dir = new File(\u0026#34;d:\\\\LinuxSoftware_VariedOS\\\\Python-3.8.0.tgz\u0026#34;); FileInputStream pis = new FileInputStream(dir); FileOutputStream pos = new FileOutputStream(\u0026#34;d:\\\\tempfile\\\\Python.wilu\u0026#34;); BufferedInputStream bufpis = new BufferedInputStream(pis); BufferedOutputStream bufpos = new BufferedOutputStream(pos); byte [] buf = new byte[8192]; int len = 0; while((len=bufpis.read(buf))!=-1) { bufpos.write(buf,0,len); } bufpis.close(); bufpos.close(); System.out.println(\u0026#34;Copy over\u0026#34;); } } 字符读写 使用 I/O-Stream-Reader/Writer 由于中文一个字符占两字节，所以我们不能像读写英文那样，直接操作数据了，\n1 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 package Java_IO; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; public class StreamReader { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream(\u0026#34;D:\\\\tempfile\\\\fpos.java\u0026#34;); FileOutputStream fos = new FileOutputStream(\u0026#34;D:\\\\tempfile\\\\pofs.c\u0026#34;,true); InputStreamReader isr = new InputStreamReader(fis); OutputStreamWriter osw = new OutputStreamWriter(fos,\u0026#34;utf-8\u0026#34;); int in ; while((in = isr.read()) != -1) { osw.write(in); } isr.close(); osw.close(); System.out.println(\u0026#34;Copy over\u0026#34;); } } 中文处理时比较的麻烦，因为各种电脑支持的编码可能不一样，就导致乱码问题，这时候我们在new 对象的时候就需要指定我们所需要的编码方式，今天就因为编码就倒腾了半天的时间 ⏰\n所以为了保险起见，在创建 FileOutputStream 的时候还是加上 UTF-8 好一点\nFileReader/FileWriter 类 FileReader/FileWriter 其实就是简化了FileOutputStream ＆ FileOutputStream の 使用过程，但是缺点( •̀ ω •́ )y 非常明显，就是不支持设置编码方式，默认的是 ISO-8859-1 or US-ASCII ,非常容易导致乱码 ~%?…,# *\u0026rsquo;☆\u0026amp;℃ $︿★?\n处理乱码也不难，就是不用这个类 ?\nway 1\n1 BufferedWriter writer = new BufferedWriter (new OutputStreamWriter (new FileOutputStream (filePath,true),\u0026#34;UTF-8\u0026#34;)); 虽然这也是一种解决办法吧，但是如果一点要用的话那就只能用下面这种方法了\nway 2\n1 Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream (\u0026#34;String\u0026#34;), \u0026#34;UTF-8\u0026#34;)); 让FileWriter 继承 Write の 编码方式，不过也不好用\nFileReader / FileWrite 初级写法 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 package Java_IO; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.OutputStreamWriter; public class fileWriter { public static void main(String[] args) throws IOException { FileOutputStream fos = new FileOutputStream(\u0026#34;D:\\\\tempfile\\\\pofs.c\u0026#34;); OutputStreamWriter osw = new OutputStreamWriter(fos,\u0026#34;UTF-8\u0026#34;); FileReader fr = new FileReader(\u0026#34;D:\\\\tempfile\\\\fpos.java\u0026#34;); FileWriter fw = new FileWriter(\u0026#34;D:\\\\tempfile\\\\pofs.c\u0026#34;); int in; while((in=fr.read())!=-1) { fw.write((char)in); } fr.close(); System.out.println(fw.getEncoding()); fw.close(); osw.close(); System.out.println(\u0026#34;Copy over\u0026#34;); } } FileReader / FileWrite 高级写法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package Java_IO; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class IO_SimplifyChinese { public static void main(String[] args) throws IOException { copyChineseChar(); } public static void copyChineseChar() throws IOException { FileReader fr = new FileReader(\u0026#34;D:\\\\LinuxSoftware_VariedOS\\\\jdk-8u231-linux-x64.tar.gz\u0026#34;); FileWriter fw = new FileWriter(\u0026#34;D:\\\\tempfile\\\\jdk8.wuli\u0026#34;); char [] chara = new char[2048]; int len ; while ((len=fr.read(chara))!= -1) { fw.write(chara,0,len); } fr.close(); fw.close(); System.out.println(\u0026#34;copy over\u0026#34;); } } BufferedReader / BufferedWriter 主要是利用缓冲区来提高读写效率，也没有啥难处，会用就可以了\n1 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 package Java_IO; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class Buffered_IO { public static void main(String[] args) throws IOException { buff(); } public static void buff() throws IOException { FileReader fr = new FileReader(\u0026#34;D:\\\\tempfile\\\\fos.java\u0026#34;); BufferedReader br = new BufferedReader(fr); FileWriter fw = new FileWriter(\u0026#34;D:\\\\tempfile\\\\copyed_fos.wuli\u0026#34;); BufferedWriter bw = new BufferedWriter(fw); char [] chara = new char[2048]; int in ; while((in=fr.read(chara))!= -1) { bw.write(chara,0,in); } br.close(); bw.close(); System.out.println(\u0026#34;Copy over\u0026#34;); } } Final\t写在最后： 在经过学习后，我发现字节流和字符流都用于复制文件，但字符流的出现主要是为了解决与字符相关的问题，所以不适合复制文件(编码原因),字符流复制文件时可能会出现莫名其妙的问题导致复制后的文件破损无法使用，但字节流就不存在这种问题所以得出\n1 复制文件时使用字节流:复制文本文件时使用字符流 ","date":"2019-12-28T21:33:56Z","image":"https://cn.bing.com/th?id=OHR.FichtelbergWinter_EN-CN7709514197_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/java_io/","title":"Java IO简单操作文件"},{"content":"泛型 泛型特性 百度得如下解释：\n1 泛型是程序设计语言的一种特性,允许程序员在强类型程序设计语言中编写代码时定义一些可变部分，那些部分在使用前必须作出指明。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。将类型参数化以达到代码复用提高软件开发工作效率的一种数据类型。泛型类是引用类型，是堆对象，主要是引入了类型参数这个概念。 举个栗子 1 2 3 4 5 6 7 List Li = new ArrayList(); Li.add(\u0026#34;sdn\u0026#34;); Li.add(90); for (Object se :Li){ System.out.println(se); } 1 java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String 典型的类型转换异常，Integer不能被转换成String\n正确的写法如下：\n1 List\u0026lt;String\u0026gt; Li = new ArrayList\u0026lt;String\u0026gt;(); 泛型的使用 泛型有三种使用方式，分别为：泛型类、泛型接口、泛型方法\n泛型类 泛型类型用于类的定义中，被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类，如：List、Set、Map。\n一个最普通的泛型类：\n1 2 3 4 5 6 7 8 9 public class examples\u0026lt;Human\u0026gt;{ private Human key; public examples(Human key) { this.key = key; } public Human getKey(){ return key; } } 注意事项\n泛型的类型参数只能是类类型，不能是简单类型。\n不能对确切的泛型类型使用instanceOf操作，编译时会出错。\n泛型接口 泛型接口与泛型类的定义及使用基本相同\n1 2 3 public interface killSky\u0026lt;Gun\u0026gt; { public Gun next(); } 实现泛型接口的类也必须要保证该类的泛型和接口保持一致\n1 2 3 4 5 6 class Fun\u0026lt;Gun\u0026gt; implements killSky\u0026lt;Gun\u0026gt;{ @Override public Gun next() { return null ; } } 泛型通配符 在写代码时可能会遇到下面的情况，通配符就会统统匹配\n1 2 3 4 public static void printCollection(Collection\u0026lt;?\u0026gt; coll) { for (Object obj:coll){ System.out.println(obj); } 泛型方法 泛型类，是在实例化类的时候指明泛型的具体类型，那么泛型方法，就是在调用方法的时候指明泛型的具体类型 。\n静态方法与泛型 静态方法有一种情况需要注意一下，那就是在类中的静态方法使用泛型：静态方法无法访问类上定义的泛型；如果静态方法操作的引用数据类型不确定的时候，必须要将泛型定义在方法上。\n即：如果静态方法要使用泛型的话，必须将静态方法也定义成泛型方法 。\n1 2 3 4 5 6 7 public class StaticGenerator\u0026lt;T\u0026gt; { .... .... public static \u0026lt;T\u0026gt; void show(T t){ } } 泛型上下边界 在使用泛型的时候，我们还可以为传入的泛型类型实参进行上下边界的限制，如：类型实参只准传入某种类型的父类或某种类型的子类。\n为泛型添加上边界，即传入的类型实参必须是指定类型的子类型\n1 2 3 4 5 6 public void showKeyValue1(Generic\u0026lt;? extends Number\u0026gt; obj){ System.out.println(obj) } public void showKeyValue1(Generic\u0026lt;? super Number\u0026gt; obj) { System.out.println(obj) } 1 2 3 4 5 6 7 //在泛型方法中添加上下边界限制的时候，必须在权限声明与返回值之间的\u0026lt;T\u0026gt;上添加上下边界，即在泛型声明的时候添加 //public \u0026lt;T\u0026gt; T showKeyName(Generic\u0026lt;T extends Number\u0026gt; container)，编译器会报错：\u0026#34;Unexpected bound\u0026#34; public \u0026lt;T extends Number\u0026gt; T showKeyName(Generic\u0026lt;T\u0026gt; container){ System.out.println(\u0026#34;container key :\u0026#34; + container.getKey()); T test = container.getKey(); return test; } 看到了很多文章中都会提起泛型数组，经过查看 sun 的说明文档，在 Java 中是”不能创建一个确切的泛型类型的数组”的。\n也就是说下面的这个例子是不可以的：\n1 List\u0026lt;String\u0026gt;[] ls = new ArrayList\u0026lt;String\u0026gt;[10]; 而使用通配符创建泛型数组是可以的，如下面这个例子：\n1 List\u0026lt;?\u0026gt;[] ls = new ArrayList\u0026lt;?\u0026gt;[10]; 这样也是可以的：\n1 List\u0026lt;String\u0026gt;[] ls = new ArrayList[10]; 下面使用Sun的一篇文档的一个例子来说明这个问题：\n","date":"2019-12-28T21:33:56Z","image":"https://cn.bing.com/th?id=OHR.ChateauBeynac_EN-CN7541724153_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/java_generics/","title":"简单的Java泛型学习"},{"content":"Collection-Map 1 2 3 4 Map--|双列集合,一次存一对键值对,必须保证唯一性 --|HashMap:哈希表,不同步,允许空的Key,Value --|TreeMap:二叉树,线程不同步,可以对Map集合的键?排序 --|Hashtable:哈希表,线程同步效率低下,不允许空的Key,Value Map 集合共性功能 添加 V put(K,V) / putAll(Map\u0026lt;K,V\u0026gt; map) 删除 V remove(K) / void clear() 判断 Boolean containsKey / Value() 获取 V get(K) / int size() 注意：如果对同一个键多次存储会出现值被覆盖的现象\nMap 集合取出键值对方法 迭代器取出法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package sourceCode; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class collMap { public static void main(String[] args) { Map\u0026lt;Integer,String\u0026gt; se = new HashMap\u0026lt;Integer,String\u0026gt;(); se.put(1, \u0026#34;Now\u0026#34;); se.put(5, \u0026#34;aset\u0026#34;); se.put(6, \u0026#34;kEREd\u0026#34;); //\tSystem.out.println(se); Set\u0026lt;Integer\u0026gt; keySet = se.keySet(); for (Iterator\u0026lt;Integer\u0026gt; iter = keySet.iterator(); iter.hasNext();) { Integer ssr = (Integer) iter.next(); System.out.println(se.get(ssr)); } } } Map.Entry 1 2 3 4 5 6 7 Set\u0026lt;Map.Entry\u0026lt;Integer, String\u0026gt;\u0026gt; Solo = se.entrySet(); for (Iterator\u0026lt;Map.Entry\u0026lt;Integer, String\u0026gt;\u0026gt; itera = Solo.iterator(); itera.hasNext();) { Map.Entry\u0026lt;Integer, String\u0026gt; me = itera.next(); Integer er =me.getKey(); String sun =me.getValue(); System.out.println(er+\u0026#34;::::\u0026#34;+sun);\t} 这里解释以下Map.Entry 其实，Entry就是Map接口内的一个接口\n它随着Map的加载而加载\n1 2 3 4 5 6 7 8 interface Map{ public static interface Entry { } } class xxx implements Map.Entry{ } Values 方法取出键值对 1 2 3 4 5 Collection\u0026lt;String\u0026gt; value = se.values(); for (Iterator\u0026lt;String\u0026gt; uuid = value.iterator(); uuid.hasNext();) { String dock =uuid.next(); System.out.println(dock); } 三种方法结果如下： 1 2 3 4 5 6 7 8 9 10 11 Now aset kEREd ​~~~~~~~~~~~ 1::::Now 5::::aset 6::::kEREd ​~~~~~~~~~~~ Now aset kEREd 关于 Map.Entry 方法 其实Map.Entry可以直接写为Entry 方法，因为我在敲代码的时候偶然发现，不敲 Map. 程序依然可以正常运行\n百度得：\n1 2 3 Entry是Map中的一个静态内部类，用来表示Map中的每个键值对。除非使用了静态导入:import static java.util.Map.* 除了使用静态导入外，还可以直接导入这个类，因为它是public的。即:import java.util.Map.Entry。 导入后也可以在当前空间直接使用Entry。 ","date":"2019-12-28T21:17:12Z","image":"https://cn.bing.com/th?id=OHR.BloodyBrook_EN-CN9582612899_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/collection_map/","title":"Java中的collections的map"},{"content":"时间类 API 在日常学习生活中，我们经常遇到时间相关的问题，现在虽然 Date 类已经不是主流了，但任然有许多方法要学习\nDateFormat 使用示例 1 2 3 4 5 6 7 8 9 10 11 12 13 package APICourceCode; import java.text.DateFormat; import java.util.Date; public class dateAPI { public static void main(String[] args) { Date date = new Date(); DateFormat ses = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL); String sesa = ses.format(date); System.out.println(sesa); } } 其常见的Style如下\n1 2 3 4 FULL:2019年12月16日 星期一 下午06时38分17秒 CST LONG:2019年12月16日 下午06时39分20秒 MEDIUM:2019-12-16 18:39:41 (默认方式) SHORT:19-12-16 下午6:40 如果需要自定义的格式，就需要按照下面的表格自定义，自定义以后，系统自带的Style将不生效\n字母 意义 y 年 M 月份 w 年份中的周数 W 月份中的周数 d 月份中的天数 D 年份中的天数 F 月份中的星期 E 星期中的天数 a am/pm 表示上下午 H 一天中的小时数(0-23) h am/pm 中的一天小时数(1-12) m 小时中的分钟数 s 分钟中的秒数 S 毫秒数 z PST;GMT 时区 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package APICourceCode; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; public class dateAPI { public static void main(String[] args) { Date date = new Date(); DateFormat ses = DateFormat.getDateTimeInstance(); ses = new SimpleDateFormat(\u0026#34;yyyy/MM/dd HH-mm-ss-SS\u0026#34;); String sesa = ses.format(date); System.out.println(sesa); } } 总结： DateFormat方法其实很简单，\n① 首先new一个Date类对象，就不用管了\n② 调用DateFormat方法对象(不需要 new),生成自定义的Style\n③ 使用字符串接收DateFormat对象的Format方法，需要传Date类的对象\nDateFormat 反解析 使用示例： 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package APICourceCode; import java.text.DateFormat; import java.text.ParseException; import java.util.Date; public class ReverseDateFormat { public static void main(String[] args) throws ParseException { String str = \u0026#34;2019-07-02\u0026#34;; DateFormat se = DateFormat.getDateInstance(); Date date =se.parse(str); System.out.println(date); } } Calendar 使用示例： 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package APICourceCode; import java.util.Calendar; public class CalenderL { public static void main(String[] args) { Calendar se = Calendar.getInstance(); se.add(Calendar.YEAR, 1); int year = se.get(Calendar.YEAR); int month = se.get(Calendar.MONTH)+1; int day = se.get(Calendar.DAY_OF_MONTH); int hours = se.get(Calendar.HOUR_OF_DAY); int minute = se.get(Calendar.MINUTE); int sec = se.get(Calendar.SECOND); System.out.println(year + \u0026#34;-\u0026#34; + month + \u0026#34;-\u0026#34; + day + \u0026#34;-\u0026#34; + hours + \u0026#34;-\u0026#34; + minute + \u0026#34;-\u0026#34; + sec); } } ---------- 2020-12-16-20-16-53 常见的Calendar参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 java.util.GregorianCalendar[ time=1576496255445, firstDayOfWeek=1, YEAR=2019, MONTH=11, WEEK_OF_YEAR=51, WEEK_OF_MONTH=3, DAY_OF_MONTH=16, DAY_OF_YEAR=350, DAY_OF_WEEK=2, DAY_OF_WEEK_IN_MONTH=3, HOUR=7, HOUR_OF_DAY=19, MINUTE=37, SECOND=35, MILLISECOND=445] 日期的偏移 1 se.add(Calendar.MONTH, 1); 个人认为作用不是很大\n","date":"2019-12-22T21:33:56Z","image":"https://cn.bing.com/th?id=OHR.BrightonSnow_EN-CN7201070198_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/java_api_of_time/","title":"Java时间类api"},{"content":"collection集合 1 2 3 collection--| |--list:有序,有索引,允许重复元素 |--set:不允许重复元素,元素存取无序 共性功能 a.添加\n1 2 Boolean add (Obj obj);//添加单个对象 Boolean addAll (Collection c);//添加一组的collection的对象 b.删除\n1 2 3 void clear();//清空collection内所有对象/元素 Boolean remove (Object o);//删除collection里面某个对象 Boolean removeAll (Collection c);//删除一组的collection c.获取长度\n1 int size(); d.判断\n1 2 3 Boolean isEmpty(); Boolean contains(Object o); Boolean containsAll(Collection c) e.集合转数组\n1 2 toArray(); toArray([]); f.迭代器\n1 Iterator iterator(); List集合 list集合的具体子类 1 2 3 4 List---- |-- Vector:数组数据结构,长度可变,线程同步\u0026lt;多线程安全\u0026gt;,但是速度比较慢(现在已经不常用-被ArrayList替代),长度按数组长度的100%延长 |-- ArrayList:数组结构,长度可变,线程不同步\u0026lt;多线程不安全\u0026gt;.查,速度快,增 删 改速度慢,长度按数组长度的50%延长 |-- LinkedList:链表结构,线程不同步\u0026lt;多线程不安全\u0026gt;,增,删,改,速度快,查 速度慢 迭代器使用注意 1 2 3 Exception in thread \u0026#34;main\u0026#34; java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(Unknown Source) at java.util.ArrayList$Itr.next(Unknown Source) 遇到这种在使用迭代器时还修改迭代器内元素的时候，JVM会报错，这时候就需要用ListIterator\n犯错实记: 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 package sourceCode; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; public class listTest { public static void clean(ArrayList\u0026lt;person\u0026gt; jsr) { List\u0026lt;person\u0026gt; dot = new ArrayList\u0026lt;person\u0026gt;(); for (ListIterator\u0026lt;person\u0026gt; lis = jsr.listIterator(); lis.hasNext();) { person pop = lis.next(); if (!dot.contains(pop)) { dot.add(pop); } } jsr.clear(); jsr.addAll(dot); } public static void main(String[] args) { ArrayList\u0026lt;person\u0026gt; jsr = new ArrayList\u0026lt;person\u0026gt;(); jsr.add(new person(20, \u0026#34;frelon\u0026#34;)); jsr.add(new person(21, \u0026#34;frelon\u0026#34;)); jsr.add(new person(23, \u0026#34;frelon\u0026#34;)); jsr.add(new person(21, \u0026#34;frelon\u0026#34;)); clean(jsr); System.out.println(jsr); } } 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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package sourceCode; public class person { private int age; private String name; public person(int age, String name) { super(); this.age = age; this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return \u0026#34;person [age=\u0026#34; + age + \u0026#34;, name=\u0026#34; + name + \u0026#34;]\u0026#34;; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; person other = (person) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } public boolean equals(person obj) { if (this==obj) { return true; } return this.name.equals(obj.name)\u0026amp;\u0026amp;this.age==obj.age; } } 上面这个例子，是我用了两天时间得出Java面向接口编程的一个实例\n为什么说用了两天时间呢？\n因为我一直都不太明白 Java 面向对象编程的具体表现，所以在写代码的时候也比较呆板，再者就是我还没有搞明白接口里面的类有没有被子类所实现\n1 ArrayList\u0026lt;person\u0026gt; dot = new ArrayList\u0026lt;person\u0026gt;(); 这就是我犯错的一段代码，我总是用List的子类 ArrayList来new新对象，而我Overwrite的方法已经被ArrayList实现，使用无论我怎么调用都还是无法调用到我自己 Overwrite后的代码\n1 List\u0026lt;person\u0026gt; dot = new ArrayList\u0026lt;person\u0026gt;(); 这就是导致我卡了两天的细节，我用List new对象以后代码完美执行了\n这就是因为没有掌握面向对象编程的思想，犯下的一个低级错误\n重写 equals和hashcode LinkedList a.特有方法 addFirst() removeFirst() getFirst() #添加到第一个 #删除第一个 #获取第一个 addlast() removeLast() getLast() #添加到最后一个 #删除最后一个 #获取最后一个 JDK1.6以后推荐使用下列方法替代，这样在遇到空值时就不会报错而是返回null\nofferFirst() pollFirst() peekFirst() #添加到第一个 #删除第一个 #获取第一个 offerLast() pollLast() peekLast() #添加到最后一个 #删除最后一个 #获取最后一个 b.实战演练 实现 先进后出\n1 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 45 package sourceCode; import java.util.LinkedList; public class linkedlist_test { public static void main(String[] args) { test loser = new test(); loser.setKail(\u0026#34;hello\u0026#34;); loser.setKail(\u0026#34;CNN\u0026#34;); loser.setKail(\u0026#34;Love China\u0026#34;); while (loser.Kailisempty()) { System.out.println(loser.getKail()); } } } class test { private LinkedList\u0026lt;Object\u0026gt; kail; public Object getKail() { return kail.pollLast(); } public Boolean Kailisempty() { if (kail.isEmpty()) { return false; } else if (!kail.isEmpty()) { return true; } return false; } @Override public String toString() { return \u0026#34;test [kail=\u0026#34; + kail + \u0026#34;]\u0026#34;; } public void setKail(Object obj) { this.kail.addLast(obj); } public test() { kail = new LinkedList\u0026lt;Object\u0026gt;(); } } Set 集合 1 2 3 4 Set--:不允许重复元素,方法和collection相同.set集合只能用Iterator取出元素 |--Hashset:不保证存入和取出的顺序一致,不允许重复,效率高 |--LinkedHashSet:有序的HashSet表,不允许重复\t|--Treeset:可以对元素进行排序,其排序方式需要元素具备比较功能,且实现CompareTo()方法,且不允许存储相同元素的方法是依据compareto()返回值是否为0,其数据结构为二叉树结构, HashSet HashSet主要就是靠hashcode()表来确定元素排列的，在使用时必须要重写hashCode()方法，而hashCode()方法内部又调用到了equals()方法来解决Hash 冲突,所以在使用时必须要Overwrite这两个方法\nHashSet 使用示例 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 package sourceCode; import java.util.HashSet; import java.util.Set; public class hashset { public static void main(String[] args) { Set doThat = new HashSet(); doThat.add(new person(13,\u0026#34;coco\u0026#34;)); doThat.add(new person(22,\u0026#34;peon\u0026#34;)); doThat.add(new person(23,\u0026#34;frelon\u0026#34;)); doThat.add(new person(23,\u0026#34;frelon\u0026#34;)); System.out.println(doThat); } } person类同上,这里仅写出 Overwrite 部分 @Override public boolean equals(Object obj) { if (this==obj) { return true; }\tperson se = (person) obj; return this.name.equals(se.name)\u0026amp;\u0026amp;this.age==se.age; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } TreeSet Comparable 接口的学习 1 2 3 4 Exception in thread \u0026#34;main\u0026#34; java.lang.ClassCastException: sourceCode.person cannot be cast to java.lang.Comparable at java.util.TreeMap.compare(Unknown Source) at java.util.TreeMap.put(Unknown Source) at java.util.TreeSet.add(Unknown Source) 这个异常对于学习 TreeSet 的我是一脸懵逼啊 ?,啥啥啥啊，就报了个异常，再看看我这代码没有错啊，编辑器也没有报错啊！\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 package sourceCode; import java.util.Set; import java.util.TreeSet; public class treeSetLearn { public static void main(String[] args) { Set ssf = new TreeSet(); ssf.add(new person(12, \u0026#34;hfh\u0026#34;)); ssf.add(new person(19, \u0026#34;frelon\u0026#34;)); ssf.add(new person(13, \u0026#34;jokoe\u0026#34;)); } } 遂百度，知，需要person 类 implements comparable 接口并 Overwrite 该接口的 CompareTo() 方法\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package sourceCode; public class person implements Comparable { ​~~~ 略 ​~~~ 方法一:只能按年龄排序,忽略了姓名 public int compareTo(Object osi) { person seao = (person) osi; if(this.age\u0026gt;seao.age) { return 1; } if(this.age\u0026lt;seao.age) return -1; return 0; } ​~~~ 方法二:完美方案? public int compareTo(Object osi){ int temp = this.age-seao.age; return temp==0?this.name.compareTo(osi.name):temp; } 按照方法二改完以后，成功按照年龄(从小到大)排序，并且同姓名，同年龄算一个人，不存进去\n实现Comparator接口示例 当我们需要需要 对没有比较功能的元素进行排序时or 自定义元素排序方式时，我们就不能再依赖通过实现Comparable接口的**CompareTo()**方法，而是要自定义一个新的比较器\n使用自定义的比较器对TreeSet排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package sourceCode; import java.util.Set; import java.util.TreeSet; public class diySavePerson { public static void main(String[] args) { Set t1 = new TreeSet(new diyCompare()); t1.add(new person(12,\u0026#34;frelon\u0026#34;)); t1.add(new person(29,\u0026#34;lion\u0026#34;)); t1.add(new person(29,\u0026#34;aion\u0026#34;)); t1.add(new person(19,\u0026#34;zoco\u0026#34;)); for(Object se : t1) { System.out.println(se); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package sourceCode; import java.util.Comparator; public class diyCompare implements Comparator\u0026lt;Object\u0026gt; { @Override public int compare(Object arg0, Object arg1) { person p1 = (person) arg0; person p2 = (person) arg1; int temp = p1.getName().compareTo(p2.getName()); int temp2 = p1.getAge() - p2.getAge(); return temp == 0 ? temp2 : temp; } } 对根据字符长短排序 下面的方案主要是根据名字的长短来排序的，如长短相同则根据名字的hashCode决定谁在前面\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package sourceCode; import java.util.Set; import java.util.TreeSet; public class diySavePerson { public static void main(String[] args) { Set\u0026lt;String\u0026gt; set1 = new TreeSet\u0026lt;String\u0026gt;( new diyCompare()); set1.add(\u0026#34;jeAreF\u0026#34;); set1.add(\u0026#34;jpwdth\u0026#34;); set1.add(\u0026#34;jogeth\u0026#34;); set1.add(\u0026#34;jokeme\u0026#34;); set1.add(\u0026#34;jsrdds\u0026#34;); for (Object se: set1) { System.out.println(se); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package sourceCode; import java.util.Comparator; public class diyCompare implements Comparator\u0026lt;Object\u0026gt; { @Override public int compare(Object arg0, Object arg1) { String str1 = (String)arg0; String str2 = (String)arg1; int temp1=str1.length()-str2.length(); int sss = str1.hashCode(); int ssd = str2.hashCode(); return temp1==0?sss-ssd:temp1; } } 三元运算符 语法为：条件表达式？表达式 1：表达式 2。\n说明：问号前面的位置是判断的条件，判断结果为 boolean 型，为 true 时调用表达式 1，为 false 时调用表达式 2。\n","date":"2019-12-20T21:33:56Z","image":"https://cn.bing.com/th?id=OHR.GentooLeap_EN-CN7598340092_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/java_collection/","title":"Java collection "},{"content":"equals() \u0026amp;\u0026amp; contains() Java 中常见的比较有equals和hashcode\n今天在学习的时候就遇到了一个头疼的问题(菜鸟级的我有大大疑问)\n在比较两个对象内的元素时，无论是使用equals还是hashcode都始终得不到我想要的结果。遂百度，记此文\n那么这两种方法到底有什么区别呢？\nequals()方法是根类 Object 的默认方法，查看 Object 中 equals()的默认实现：\n1 2 3 4 public boolean equals(Object obj) { return (this == obj); } ✅ 可以看出没有重写过的 equals()方法和 == 是一样的，都是比较两个对象引用指向的内存地址是否一样判断两个对象是否相等。\n也就是说，基本上每次都要Overwrite这个方法\nhashCode是JDK根据对象的地址或者字符串或者数字计算该对象的哈希码值的方法。\nhashcode 和 equals 两者必须同时重写。\n注意在类中，有一些 equals 方法 OverWrite 时必须要传入 Object 类型\n1 2 3 4 5 public boolean equals (person obj){ return (this == obj); }\t❌ //这样写就是错误的,equals()方法不允许传入非 Object 类 ","date":"2019-12-20T21:33:56Z","image":"https://cn.bing.com/th?id=OHR.Calakmul_EN-CN9963471796_UHD.jpg\u0026pid=hp\u0026w=1080\u0026h=720\u0026rs=1\u0026c=4","permalink":"/p/java_equals_and_contains/","title":"Java的equals和contains的一些区别"}]