From e5abc1fe99340b979b18864d978179f71fe8f5c5 Mon Sep 17 00:00:00 2001 From: vlapa Date: Sat, 13 Jun 2026 20:33:26 +0300 Subject: First --- .obsidian/app.json | 14 + .obsidian/appearance.json | 4 + .obsidian/core-plugins-migration.json | 30 + .obsidian/core-plugins.json | 33 + .obsidian/graph.json | 22 + .obsidian/hotkeys.json | 1 + .obsidian/templates.json | 3 + .obsidian/workspace (10).json | 210 + .obsidian/workspace (11).json | 199 + .obsidian/workspace (12).json | 215 + .obsidian/workspace (13).json | 230 ++ .obsidian/workspace (2).json | 155 + .obsidian/workspace (3).json | 156 + .obsidian/workspace (4).json | 143 + .obsidian/workspace (5).json | 151 + .obsidian/workspace (6).json | 153 + .obsidian/workspace (7).json | 153 + .obsidian/workspace (8).json | 197 + .obsidian/workspace (9).json | 197 + .obsidian/workspace.json | 217 + AUTO/AUTO.md | 2 + AUTO/TOYOTA AYGO.md | 24 + DaVinciResolve/Davinci Resolve 19.md | 55 + DaVinciResolve/HEVC.md | 38 + ...273\320\260\320\262\320\270\321\210\320\270.md" | 55 + ...230\320\227\320\220\320\246\320\230\320\257.md" | 19 + ...272\320\260\320\261\320\265\320\273\321\214.md" | 2 + GIT.md | 252 ++ GIT_new-2.md | 218 ++ GIT_new.md | 165 + LINUX/AmneziaWG.md | 67 + "LINUX/BASH -\320\272\321\203\321\200\321\201.md" | 121 + LINUX/BASH.md | 109 + LINUX/BASH_github/bash-guide-1.md | 218 ++ LINUX/BASH_github/bash-guide-2.md | 225 ++ ...21\201\320\275\320\276\320\262\321\213 Bash.md" | 222 ++ ...200\320\270\320\277\321\202\320\276\320\262.md" | 211 + ...240\320\260\320\267\320\275\320\276\320\265.md" | 40 + ... \320\247\320\260\321\201\321\202\321\214 1.md" | 221 ++ ... \320\247\320\260\321\201\321\202\321\214 2.md" | 223 ++ ...262\320\273\320\265\320\275\320\270\320\271.md" | 24 + ...264\320\274\320\270\320\275\320\276\320\262.md" | 1 + LINUX/BAT.md | 21 + LINUX/BEGIN.md | 218 ++ LINUX/BorgBackup.md | 32 + LINUX/CAL.md | 9 + LINUX/CERTBOT.md | 123 + LINUX/CHMOD, CHOWN.md | 79 + LINUX/CIFS.md | 189 + LINUX/CRON.md | 93 + LINUX/DIFFERENT.md | 51 + LINUX/DISK/DF, DU.md | 101 + LINUX/DISK/FDISK.md | 217 + LINUX/DISK/FSTAB.md | 100 + ...\264\320\270\321\201\320\272 \320\262 Linux.md" | 68 + ...ex Cloud \320\264\320\270\321\201\320\272 ).md" | 73 + ...14 \320\264\320\270\321\201\320\272\320\260.md" | 133 + ...20\267\320\260\321\206\320\270\321\217 RAID.md" | 535 +++ LINUX/DISK/NTFS.md | 57 + LINUX/DISK/SMARTMONTOOLS.md | 85 + ...60 \320\264\320\270\321\201\320\272\320\260.md" | 26 + ...200\320\272\320\276\320\262\320\272\320\260.md" | 51 + ...16 \320\277\320\260\320\277\320\272\321\203.md" | 34 + ...320\275\321\213\320\271 raid \320\262 Linux.md" | 169 + LINUX/DOCKER.md | 87 + LINUX/ENVIRONMENT.md | 30 + LINUX/EXIFTOOL.md | 41 + LINUX/FIND.md | 156 + ...200\320\262\320\265\321\200\320\276\320\262.md" | 42 + LINUX/GPG, PASS.md | 276 ++ LINUX/GREP.md | 392 ++ ...\217\320\264\321\200\320\260 \320\262 Linux.md" | 147 + ...\275\320\264 \320\264\320\273\321\217 Linux.md" | 48 + ...0\260\320\274\320\265\320\275\320\260 Nginx.md" | 54 + LINUX/HOME_SERVER/watch.md | 48 + ...270\321\201\321\202\320\265\320\274\321\213.md" | 36 + ...65\320\275\320\270\320\265 \320\262 SYSTEMD.md" | 141 + ...0\275\320\276\321\201\321\202\320\270 Linux.md" | 178 + LINUX/HTOP.md | 13 + LINUX/IPTABLES, NFTABLES.md | 101 + LINUX/JOURNALCTL.md | 160 + LINUX/KILL, PKILL.md | 16 + LINUX/LEMP.md | 119 + "LINUX/LESS \320\270 SPLIT.md" | 39 + LINUX/LN.md | 15 + LINUX/LSOF.md | 87 + LINUX/Let's Encrypt.md | 33 + LINUX/MAILutils.md | 104 + LINUX/MC.md | 124 + LINUX/MOSQUITTO.md | 121 + LINUX/NC.md | 33 + LINUX/NCDU, BAT, FD, EXA, HTTPIE.md | 6 + LINUX/NCDU.md | 20 + LINUX/NETWORK/DNS.md | 36 + LINUX/NETWORK/IPERF3.md | 33 + LINUX/NETWORK/NETPLAN.md | 230 ++ LINUX/NETWORK/NETWORK.md | 162 + ...5 \320\262 \321\201\320\265\321\202\320\270.md" | 49 + ...276\320\275\321\201\320\276\320\273\320\270.md" | 17 + ...31 \320\232\320\220\320\240\320\242\320\253.md" | 10 + LINUX/NGINX.md | 538 +++ LINUX/PLEX.md | 33 + LINUX/PROGRAMS_bash.md | 311 ++ LINUX/PS.md | 34 + LINUX/PostgreSQL.md | 165 + LINUX/RANGER.md | 91 + ...272\320\265\321\202\320\260\320\274\320\270.md" | 33 + LINUX/RM.md | 10 + LINUX/ROOT.md | 1 + LINUX/RSYNC.md | 373 ++ LINUX/SAMBA.md | 262 ++ LINUX/SCP, SFTP.md | 70 + ...202\320\276\321\200\320\270\320\275\320\263.md" | 280 ++ ...276\320\274\320\260\320\275\320\264\321\213.md" | 63 + LINUX/SHUTDOWN, HALT, POWEROFF, REBOOT.md | 56 + LINUX/SSH.md | 302 ++ LINUX/SYSTEMCTL.md | 405 ++ LINUX/TAR.md | 314 ++ LINUX/TERMINAL.md | 207 + LINUX/TIMEDATE, CHRONY, NTP.md | 48 + LINUX/TLDR.md | 12 + LINUX/TMUX.md | 58 + LINUX/TRANSMISSION.md | 30 + LINUX/UFW.md | 165 + LINUX/USER.md | 137 + LINUX/VSFTPD.md | 367 ++ LINUX/WIREGUARD.md | 170 + LINUX/WORDPRESS.md | 471 +++ "LINUX/XARGS, TEE \320\270 WATCH.md" | 24 + LINUX/YAML.md | 84 + LINUX/__FLENOV.md | 1582 ++++++++ LINUX/iso fs _ ISO9660.md | 46 + LINUX/tr.md | 84 + ...\201\321\201\320\276\320\262 \320\262 Linux.md" | 27 + ...\201\321\201\320\276\320\262 \320\262 Linux.md" | 20 + ...13\320\265 \321\210\320\260\320\263\320\270.md" | 29 + ...1\203\321\202\320\270\320\262\320\260 linux.md" | 4 + ...72\320\260\320\272 read-only \320\262 Linux.md" | 39 + ...60 \321\207\320\260\321\201\321\202\320\270.md" | 71 + ...201\321\201\321\213\320\273\320\272\320\270.md" | 28 + ...\233\320\276\320\263\320\270 \320\262 Linux.md" | 226 ++ ...270\321\201\321\202\320\265\320\274\321\213.md" | 26 + ...13\321\205 \321\217\320\264\320\265\321\200.md" | 78 + ...\272\320\276\320\264\320\260 \320\262 Linux.md" | 42 + ...\270\320\274\320\272\321\203 \320\262 Linux.md" | 40 + ...276\320\263\321\200\320\260\320\274\320\274.md" | 26 + ...1\201\321\202\320\265\320\274\320\260 Linux.md" | 176 + ...2\320\270\320\274 \320\275\320\260 MacBook.md" | 98 + MacOS/Homebrew.md | 1 + MacOS/Postgresql.md | 113 + ...277\320\276\321\200\321\202\320\276\320\262.md" | 150 + NuPhy Air75.md | 122 + OBSIDIAN.md | 56 + PORT.md | 11 + PYTHON/-args, --kwargs.md | 43 + ...200\321\203\320\272\321\206\320\270\320\271.md" | 65 + ...20\275\320\260 \320\236\320\227\320\243 2Gb.md" | 9 + PYTHON/AsyncIO.md | 155 + PYTHON/BEGIN + PIP.md | 226 ++ "PYTHON/COM-\320\277\320\276\321\200\321\202.md" | 41 + "PYTHON/DAEMON \320\275\320\260 Python.md" | 126 + PYTHON/DEFAULTDICT.md | 33 + PYTHON/DJANGO.md | 36 + PYTHON/ENUMERATE, ENUM.md | 11 + PYTHON/ENUMERATE, ZIP.md | 2 + PYTHON/FOR, WHILE.md | 227 ++ PYTHON/HTTP.md | 18 + PYTHON/LIST COMPREHENSION.md | 6 + PYTHON/MAP.md | 15 + PYTHON/MATH.md | 1 + PYTHON/Matprotlib.md | 29 + PYTHON/NAMEDTUPLE.md | 18 + ...260\320\271\320\273\320\260\320\274\320\270.md" | 39 + PYTHON/OS.md | 18 + PYTHON/PATH.md | 111 + "PYTHON/PAUSE \320\262 PYTHON.md" | 13 + PYTHON/PEP8.md | 37 + PYTHON/PROGRAMS.md | 160 + PYTHON/PROGRAMS_py.md | 150 + PYTHON/PROGRESS BAR.md | 138 + PYTHON/PYINSTALLER.md | 164 + PYTHON/PYTHON_tuturial.md | 2910 ++++++++++++++ PYTHON/Password & key = PATH.md | 94 + PYTHON/PrettyTable.md | 15 + PYTHON/Python-cheatsheet.md | 3717 ++++++++++++++++++ PYTHON/ROUND, CEIL, FLOOR.md | 20 + PYTHON/SET, DICT, LIST, TUPLE, RANGE.md | 219 ++ PYTHON/SPEEDTEST python.md | 51 + PYTHON/SPLIT, JOIN.md | 2 + PYTHON/STRING.md | 121 + PYTHON/SnakeViz.md | 15 + PYTHON/TELEGRAMM BOT.md | 59 + PYTHON/THREADING.md | 128 + PYTHON/TKINTER.md | 52 + PYTHON/TRY ... EXCEPT.md | 269 ++ PYTHON/Translators.md | 20 + PYTHON/WITH.md | 28 + PYTHON/any, all.md | 22 + PYTHON/argparse.md | 5 + PYTHON/dir().md | 17 + PYTHON/exec.md | 6 + ...276\320\262\320\260\320\275\320\270\320\265.md" | 19 + PYTHON/itertools.md | 8 + PYTHON/keys() + &.md | 8 + ...265\320\271\321\201\320\260\320\274\320\270.md" | 56 + PYTHON/pandas.md | 2 + PYTHON/partition().md | 8 + PYTHON/poyo.md | 35 + PYTHON/pprint().md | 17 + PYTHON/sleep().md | 8 + PYTHON/tabulate.md | 12 + ...265\320\275\320\270\320\262\321\213\321\205.md" | 83 + ...260\321\207\320\265\320\275\320\270\320\271.md" | 5 + ...320\265\320\274 QR-\320\272\320\276\320\264.md" | 10 + ...265\320\264\320\266\320\265\321\200\321\213.md" | 8 + ...260\321\202\320\260\320\273\320\276\320\263.md" | 4 + ...13\320\271 \320\262\320\262\320\276\320\264.md" | 2 + ...265\321\200\320\260\321\202\320\276\321\200.md" | 17 + ...262\320\265\321\200\321\201\320\270\320\270.md" | 98 + ...32\320\220 \320\232\320\236\320\224\320\220.md" | 32 + ...273\320\272\320\260 \320\275\320\260 Python.md" | 156 + ...202\320\276\320\272\320\276\320\273\321\213.md" | 13 + ...272\320\260 \320\264\320\273\321\217 Python.md" | 161 + ...\274\320\276\321\211\321\214\321\216 Python.md" | 6 + ...274\320\270\320\275\320\260\320\273\320\265.md" | 27 + ...272\320\276\320\264 \320\275\320\260 Python.md" | 650 +++ ...243\320\235\320\232\320\246\320\230\320\230.md" | 192 + PyCharm.md | 26 + RADIO/ds18b20.md | 493 +++ ...261\320\273\320\265\320\275\320\270\321\217.md" | 16 + SQL/MySQL.md | 86 + SQL/SQL Cheatsheet.md | 313 ++ ...224\320\260\320\275\320\275\321\213\321\205.md" | 172 + SQL/SQLite Commands.md | 384 ++ SQL/SQLite.md | 556 +++ ...25 \320\244\320\236\320\240\320\234\320\253.md" | 41 + ...\223\320\220\320\233\320\232\320\220 SQLite.md" | 4139 ++++++++++++++++++++ VIM.md | 479 +++ VSCode.md | 151 + YANDEX_DISK.md | 65 + _PROGRAMs/C++/C UART.md | 165 + _PROGRAMs/C++/MQTT.md | 29 + _PROGRAMs/C++/TIME.md | 26 + _PROGRAMs/C++/WiFi.md | 40 + _PROGRAMs/C++/snprintf.md | 48 + ...224\320\235\320\225\320\235\320\230\320\225.md" | 67 + _PROGRAMs/ESP8266-ESP32/EEPROM.md | 33 + ...277\320\260\320\274\321\217\321\202\321\214.md" | 15 + ...207\320\265\320\261\320\275\320\270\320\272.md" | 1 + files/15d72c288938426db50c249effd75b07.png | Bin 0 -> 151303 bytes files/1Gb_cab.png | Bin 0 -> 1124787 bytes files/1fd1edd152259cb558abab7409faa120.png | Bin 0 -> 64308 bytes ...10\320\270\321\202\320\265\320\273\321\214.jpg" | Bin 0 -> 222740 bytes files/2025-02-20 04.18.59.jpg | Bin 0 -> 100545 bytes files/2025-06-20_22-34-47.png | Bin 0 -> 447089 bytes files/264fc3d5aab6c1a767f7659c207e1618.png | Bin 0 -> 4499 bytes files/2e66d41ed2450e40ffd3fa20ba1036d7.png | Bin 0 -> 7077 bytes files/32de918111f7fed3e8183b4b9148286b.png | Bin 0 -> 9340 bytes files/370b55d2c9ca15ef5153581f2af2b9b0.png | Bin 0 -> 10535 bytes files/4a2b1a7f1e1bee9a26d7885b1d5e0f8c.png | Bin 0 -> 17837 bytes files/6cda81091bcc9f99ad224bd5c5426f8e.png | Bin 0 -> 20207 bytes files/73ce6d03c95dd19ac4263224cc1acac4.png | Bin 0 -> 4520 bytes files/7c6829decd5925af5d5e0dcc27128438.png | Bin 0 -> 27481 bytes files/9132596e2802a7ecd3a47842f6475f60.png | Bin 0 -> 10234 bytes ...77\320\260\320\275\320\265\320\273\321\214.jpg" | Bin 0 -> 630407 bytes ...61\320\260\320\274\320\277\320\265\321\200.jpg" | Bin 0 -> 594843 bytes ...64\320\262\320\265\321\201\320\272\320\260.jpg" | Bin 0 -> 442923 bytes ...64\320\262\320\265\321\201\320\272\320\260.jpg" | Bin 0 -> 612071 bytes ...61\320\260\320\274\320\277\320\265\321\200.jpg" | Bin 0 -> 716685 bytes ...73\320\276\320\262\320\270\320\275\320\260.jpg" | Bin 0 -> 365074 bytes files/DB_metall.ods | Bin 0 -> 15786 bytes files/Pasted image 20240312221215.png | Bin 0 -> 283036 bytes files/Pasted image 20250214130256.png | Bin 0 -> 500088 bytes files/Pasted image 20250214130512.png | Bin 0 -> 476744 bytes files/Pasted image 20250217114433.png | Bin 0 -> 445372 bytes files/Pasted image 20250501030617.png | Bin 0 -> 915831 bytes files/Pasted image 20250501031129.png | Bin 0 -> 841084 bytes files/Pasted image 20250501031423.png | Bin 0 -> 362253 bytes files/Pasted image 20250501031545.png | Bin 0 -> 1203082 bytes files/Pasted image 20250501031558.png | Bin 0 -> 76175 bytes files/Pasted image 20250501031753.png | Bin 0 -> 1087886 bytes files/Pasted image 20250621003213.png | Bin 0 -> 936744 bytes files/Pasted image 20250621003225.png | Bin 0 -> 43966 bytes ...00\320\263\320\260\320\273\320\272\320\260.png" | Bin 0 -> 86954 bytes files/TG_bot_VPN-main.zip | Bin 0 -> 39225 bytes files/TG_bot_VPN-main/.env.template | 5 + files/TG_bot_VPN-main/.gitignore | 172 + files/TG_bot_VPN-main/LICENSE | 21 + files/TG_bot_VPN-main/README.md | 27 + files/TG_bot_VPN-main/config_data/__init__.py | 1 + files/TG_bot_VPN-main/config_data/config.py | 48 + files/TG_bot_VPN-main/database/__init__.py | 1 + files/TG_bot_VPN-main/database/models.py | 69 + files/TG_bot_VPN-main/handlers/__init__.py | 2 + .../handlers/custom_heandlers/__init__.py | 3 + .../handlers/custom_heandlers/admin_handlers.py | 405 ++ .../custom_heandlers/instruction_handlers.py | 26 + .../handlers/custom_heandlers/location_handlers.py | 135 + .../handlers/default_heandlers/__init__.py | 6 + .../handlers/default_heandlers/echo.py | 21 + .../handlers/default_heandlers/help.py | 13 + .../handlers/default_heandlers/start.py | 99 + files/TG_bot_VPN-main/keyboards/__init__.py | 2 + files/TG_bot_VPN-main/keyboards/inline/__init__.py | 3 + .../keyboards/inline/admin_buttons.py | 71 + .../keyboards/inline/app_buttons.py | 22 + files/TG_bot_VPN-main/keyboards/inline/servers.py | 19 + .../TG_bot_VPN-main/keyboards/inline/subscribed.py | 17 + files/TG_bot_VPN-main/keyboards/reply/__init__.py | 0 .../keyboards/reply/handlers_reply.py | 9 + files/TG_bot_VPN-main/loader.py | 30 + files/TG_bot_VPN-main/main.py | 31 + files/TG_bot_VPN-main/requirements.txt | 9 + files/TG_bot_VPN-main/states/__init__.py | 1 + files/TG_bot_VPN-main/states/states.py | 24 + files/TG_bot_VPN-main/utils/__init__.py | 1 + files/TG_bot_VPN-main/utils/functions.py | 65 + files/TG_bot_VPN-main/utils/generate_vpn_keys.py | 458 +++ files/TG_bot_VPN-main/utils/misc/__init__.py | 1 + files/TG_bot_VPN-main/utils/set_bot_commands.py | 8 + files/TG_bot_VPN-main/utils/tasks.py | 112 + files/TG_bot_VPN-main/utils/work_vpn_keys.py | 183 + files/ae81365154ba5166845a7a9171d2248b.png | Bin 0 -> 115660 bytes files/b74e1c10b7c49ab7cc235e746af07128.png | Bin 0 -> 26109 bytes files/dbb44ce3043db021cfbb195941e52a5a.png | Bin 0 -> 8202 bytes files/eafc9c46561ec70fb5744ea1d85eb1c0.png | Bin 0 -> 22267 bytes ...am-cloud-photo-size-2-5220111584251211570-y.jpg | Bin 0 -> 91418 bytes ...am-cloud-photo-size-2-5220111584251211572-y.jpg | Bin 0 -> 88722 bytes ...am-cloud-photo-size-2-5220111584251211573-y.jpg | Bin 0 -> 144624 bytes ...am-cloud-photo-size-2-5230966980947866123-y.jpg | Bin 0 -> 193733 bytes ...am-cloud-photo-size-2-5235693785730632253-y.jpg | Bin 0 -> 123664 bytes ...am-cloud-photo-size-2-5249336126576118394-y.jpg | Bin 0 -> 83438 bytes ...am-cloud-photo-size-2-5255812146984502723-y.jpg | Bin 0 -> 118528 bytes ...am-cloud-photo-size-2-5264765212471446942-y.jpg | Bin 0 -> 84481 bytes ...am-cloud-photo-size-2-5269638479933982570-y.jpg | Bin 0 -> 80715 bytes ...am-cloud-photo-size-2-5280728042542581828-y.jpg | Bin 0 -> 27819 bytes ...am-cloud-photo-size-2-5310086154055444585-y.jpg | Bin 0 -> 78368 bytes ...am-cloud-photo-size-2-5316919099131030050-y.jpg | Bin 0 -> 90364 bytes ...am-cloud-photo-size-2-5337133182321027005-y.jpg | Bin 0 -> 119325 bytes ...am-cloud-photo-size-2-5350524228924991162-y.jpg | Bin 0 -> 190704 bytes ...am-cloud-photo-size-2-5382031937209757544-y.jpg | Bin 0 -> 115408 bytes ...am-cloud-photo-size-2-5382031937209757546-y.jpg | Bin 0 -> 97084 bytes ...am-cloud-photo-size-2-5386535536837126461-y.jpg | Bin 0 -> 98080 bytes ...am-cloud-photo-size-2-5399847483028401010-y.jpg | Bin 0 -> 69574 bytes ...am-cloud-photo-size-2-5406976059292904279-x.jpg | Bin 0 -> 41636 bytes ...am-cloud-photo-size-2-5413616564063822987-y.jpg | Bin 0 -> 94165 bytes ...am-cloud-photo-size-4-5776315316576566340-y.jpg | Bin 0 -> 144882 bytes ...am-cloud-photo-size-4-5850234924966199387-w.jpg | Bin 0 -> 102190 bytes ...am-cloud-photo-size-4-5850234924966199390-w.jpg | Bin 0 -> 243882 bytes ...am-cloud-photo-size-4-5850234924966199391-w.jpg | Bin 0 -> 231183 bytes ...am-cloud-photo-size-4-5863854399405801203-w.jpg | Bin 0 -> 192010 bytes ...am-cloud-photo-size-4-6016931255788092228-y.jpg | Bin 0 -> 109413 bytes ...72\321\203\320\273\320\265\321\200\320\260.png" | Bin 0 -> 1793505 bytes ...20\320\264\320\274\320\270\320\275\320\260.jpg" | Bin 0 -> 141984 bytes ...0\275\320\260 2023-08-06 \320\262 22.19.19.png" | Bin 0 -> 2584244 bytes ...0\275\320\260 2023-10-06 \320\262 10.40.44.png" | Bin 0 -> 2155278 bytes ...0\275\320\260 2023-10-19 \320\262 09.53.24.png" | Bin 0 -> 4525482 bytes ...0\275\320\260 2023-10-19 \320\262 09.53.43.png" | Bin 0 -> 3336073 bytes ...0\275\320\260 2023-10-29 \320\262 14.23.34.png" | Bin 0 -> 83619 bytes ...0\275\320\260 2023-12-17 \320\262 20.47.18.png" | Bin 0 -> 2464847 bytes ...0\275\320\260 2023-12-17 \320\262 20.47.46.png" | Bin 0 -> 1526982 bytes ...0\275\320\260 2023-12-17 \320\262 20.53.26.png" | Bin 0 -> 1713217 bytes ...0\275\320\260 2023-12-17 \320\262 20.55.53.png" | Bin 0 -> 2448259 bytes ...0\275\320\260 2024-03-10 \320\262 20.55.05.png" | Bin 0 -> 2965519 bytes ...0\275\320\260 2024-03-14 \320\262 13.21.49.png" | Bin 0 -> 679205 bytes ...0\275\320\260 2024-03-14 \320\262 13.22.17.png" | Bin 0 -> 338154 bytes ...0\275\320\260 2024-03-14 \320\262 13.25.59.png" | Bin 0 -> 793645 bytes ...0\275\320\260 2024-03-14 \320\262 13.27.58.png" | Bin 0 -> 528937 bytes ...0\275\320\260 2024-03-14 \320\262 13.29.48.png" | Bin 0 -> 700405 bytes ...0\275\320\260 2024-03-14 \320\262 13.32.01.png" | Bin 0 -> 586014 bytes ...0\275\320\260 2024-03-25 \320\262 22.49.58.png" | Bin 0 -> 753319 bytes ...0\275\320\260 2024-09-15 \320\262 18.55.25.png" | Bin 0 -> 306474 bytes ...0\275\320\260 2024-09-15 \320\262 19.02.02.png" | Bin 0 -> 418613 bytes ...0\275\320\260 2024-09-15 \320\262 19.10.29.png" | Bin 0 -> 504493 bytes ...0\275\320\260 2024-09-16 \320\262 21.31.06.png" | Bin 0 -> 2324052 bytes ...0\275\320\260 2024-09-16 \320\262 21.36.18.png" | Bin 0 -> 2284228 bytes ...0\275\320\260 2024-09-16 \320\262 21.39.34.png" | Bin 0 -> 2478527 bytes ...0\275\320\260 2024-09-16 \320\262 21.45.31.png" | Bin 0 -> 1812563 bytes ...0\275\320\260 2024-09-16 \320\262 21.45.54.png" | Bin 0 -> 833864 bytes ...0\275\320\260 2024-11-01 \320\262 01.39.59.png" | Bin 0 -> 2312456 bytes ...0\275\320\260 2025-02-02 \320\262 18.05.19.png" | Bin 0 -> 454884 bytes ...0\275\320\260 2025-02-02 \320\262 18.06.09.png" | Bin 0 -> 442277 bytes ...0\275\320\260 2025-02-02 \320\262 18.13.01.png" | Bin 0 -> 518012 bytes ...0\275\320\260 2025-02-02 \320\262 18.29.43.png" | Bin 0 -> 371869 bytes ...0\275\320\260 2025-02-02 \320\262 18.30.02.png" | Bin 0 -> 466641 bytes ...0\275\320\260 2025-02-11 \320\262 01.15.10.png" | Bin 0 -> 1598241 bytes ...0\275\320\260 2025-02-11 \320\262 01.17.25.png" | Bin 0 -> 920489 bytes ...0\275\320\260 2025-05-14 \320\262 00.35.18.png" | Bin 0 -> 390001 bytes ...0\275\320\260 2025-05-14 \320\262 00.38.05.png" | Bin 0 -> 1575001 bytes sync | 24 + ...cloud-photo-size-2-5253592580900375225-x.jpg.md | 0 ...320\236\320\222\320\220\320\240\320\254 !!!.md" | 268 ++ 392 files changed, 39927 insertions(+) create mode 100644 .obsidian/app.json create mode 100644 .obsidian/appearance.json create mode 100644 .obsidian/core-plugins-migration.json create mode 100644 .obsidian/core-plugins.json create mode 100644 .obsidian/graph.json create mode 100644 .obsidian/hotkeys.json create mode 100644 .obsidian/templates.json create mode 100644 .obsidian/workspace (10).json create mode 100644 .obsidian/workspace (11).json create mode 100644 .obsidian/workspace (12).json create mode 100644 .obsidian/workspace (13).json create mode 100644 .obsidian/workspace (2).json create mode 100644 .obsidian/workspace (3).json create mode 100644 .obsidian/workspace (4).json create mode 100644 .obsidian/workspace (5).json create mode 100644 .obsidian/workspace (6).json create mode 100644 .obsidian/workspace (7).json create mode 100644 .obsidian/workspace (8).json create mode 100644 .obsidian/workspace (9).json create mode 100644 .obsidian/workspace.json create mode 100644 AUTO/AUTO.md create mode 100644 AUTO/TOYOTA AYGO.md create mode 100644 DaVinciResolve/Davinci Resolve 19.md create mode 100644 DaVinciResolve/HEVC.md create mode 100644 "DaVinciResolve/\320\223\320\276\321\200\321\217\321\207\320\270\320\265 \320\272\320\273\320\260\320\262\320\270\321\210\320\270.md" create mode 100644 "DaVinciResolve/\320\236\320\237\320\242\320\230\320\234\320\230\320\227\320\220\320\246\320\230\320\257.md" create mode 100644 "ETHERNET 1Gbit - \320\272\320\260\320\261\320\265\320\273\321\214.md" create mode 100644 GIT.md create mode 100644 GIT_new-2.md create mode 100644 GIT_new.md create mode 100644 LINUX/AmneziaWG.md create mode 100644 "LINUX/BASH -\320\272\321\203\321\200\321\201.md" create mode 100644 LINUX/BASH.md create mode 100644 LINUX/BASH_github/bash-guide-1.md create mode 100644 LINUX/BASH_github/bash-guide-2.md create mode 100644 "LINUX/BASH_github/\320\236\321\201\320\275\320\276\320\262\321\213 Bash.md" create mode 100644 "LINUX/BASH_script/15 \320\276\320\264\320\275\320\276\321\201\321\202\321\200\320\276\321\207\320\275\321\213\321\205 bash-\321\201\320\272\321\200\320\270\320\277\321\202\320\276\320\262.md" create mode 100644 "LINUX/BASH_script/BASH. \320\240\320\260\320\267\320\275\320\276\320\265.md" create mode 100644 "LINUX/BASH_script/BASH. \320\247\320\260\321\201\321\202\321\214 1.md" create mode 100644 "LINUX/BASH_script/BASH. \320\247\320\260\321\201\321\202\321\214 2.md" create mode 100644 "LINUX/BASH_script/\320\236\320\261\320\275\320\276\320\262\320\273\320\265\320\275\320\270\320\265 \321\201\320\277\320\270\321\201\320\272\320\260 \320\277\320\260\320\272\320\265\321\202\320\276\320\262 \320\270 \321\203\321\201\321\202\320\260\320\275\320\276\320\262\320\272\320\260 \320\276\320\261\320\275\320\276\320\262\320\273\320\265\320\275\320\270\320\271.md" create mode 100644 "LINUX/BASH_script/\320\241\320\272\321\200\320\270\320\277\321\202\321\213 \320\264\320\273\321\217 \320\260\320\264\320\274\320\270\320\275\320\276\320\262.md" create mode 100644 LINUX/BAT.md create mode 100644 LINUX/BEGIN.md create mode 100644 LINUX/BorgBackup.md create mode 100644 LINUX/CAL.md create mode 100644 LINUX/CERTBOT.md create mode 100644 LINUX/CHMOD, CHOWN.md create mode 100644 LINUX/CIFS.md create mode 100644 LINUX/CRON.md create mode 100644 LINUX/DIFFERENT.md create mode 100644 LINUX/DISK/DF, DU.md create mode 100644 LINUX/DISK/FDISK.md create mode 100644 LINUX/DISK/FSTAB.md create mode 100644 "LINUX/DISK/GPT-\320\264\320\270\321\201\320\272 \320\262 Linux.md" create mode 100644 "LINUX/DISK/GeeseFS ( Yandex Cloud \320\264\320\270\321\201\320\272 ).md" create mode 100644 "LINUX/DISK/HDPARM \321\201\320\272\320\276\321\200\320\276\321\201\321\202\321\214 \320\264\320\270\321\201\320\272\320\260.md" create mode 100644 "LINUX/DISK/MDADM \320\276\321\200\320\263\320\260\320\275\320\270\320\267\320\260\321\206\320\270\321\217 RAID.md" create mode 100644 LINUX/DISK/NTFS.md create mode 100644 LINUX/DISK/SMARTMONTOOLS.md create mode 100644 "LINUX/DISK/SOFT RAID1 (mdadm) \320\263\320\276\321\200\321\217\321\207\320\260\321\217 \320\267\320\260\320\274\320\265\320\275\320\260 \320\264\320\270\321\201\320\272\320\260.md" create mode 100644 "LINUX/DISK/WD - \320\260\320\262\321\202\320\276\320\277\320\260\321\200\320\272\320\276\320\262\320\272\320\260.md" create mode 100644 "LINUX/DISK/\320\220\320\262\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201\320\272\320\276\320\265 \320\274\320\276\320\275\321\202\320\270\321\200\320\276\320\262\320\260\320\275\320\270\320\265 USB \320\275\320\260\320\272\320\276\320\277\320\270\321\202\320\265\320\273\321\217 \320\262 \320\267\320\260\320\264\320\260\320\275\320\275\321\203\321\216 \320\277\320\260\320\277\320\272\321\203.md" create mode 100644 "LINUX/DISK/\320\237\321\200\320\276\320\263\321\200\320\260\320\274\320\274\320\275\321\213\320\271 raid \320\262 Linux.md" create mode 100644 LINUX/DOCKER.md create mode 100644 LINUX/ENVIRONMENT.md create mode 100644 LINUX/EXIFTOOL.md create mode 100644 LINUX/FIND.md create mode 100644 "LINUX/Fail2ban - \320\236\321\201\320\275\320\276\320\262\321\213 \320\267\320\260\321\211\320\270\321\202\321\213 \321\201\320\265\321\200\320\262\320\265\321\200\320\276\320\262.md" create mode 100644 LINUX/GPG, PASS.md create mode 100644 LINUX/GREP.md create mode 100644 "LINUX/GRUB \320\270 \320\277\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213 \321\217\320\264\321\200\320\260 \320\262 Linux.md" create mode 100644 "LINUX/HOME_SERVER/5 \320\274\320\260\320\273\320\276\320\270\320\267\320\262\320\265\321\201\321\202\320\275\321\213\321\205 \320\272\320\276\320\274\320\260\320\275\320\264 \320\264\320\273\321\217 Linux.md" create mode 100644 "LINUX/HOME_SERVER/Caddy - \320\267\320\260\320\274\320\265\320\275\320\260 Nginx.md" create mode 100644 LINUX/HOME_SERVER/watch.md create mode 100644 "LINUX/HOME_SERVER/\320\236\321\207\320\270\321\201\321\202\320\272\320\260 \321\201\320\270\321\201\321\202\320\265\320\274\321\213.md" create mode 100644 "LINUX/HOME_SERVER/\320\237\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270\320\265 \320\262 SYSTEMD.md" create mode 100644 "LINUX/HOME_SERVER/\320\243\321\201\320\270\320\273\320\265\320\275\320\270\320\265 \320\261\320\265\320\267\320\276\320\277\320\260\321\201\320\275\320\276\321\201\321\202\320\270 Linux.md" create mode 100644 LINUX/HTOP.md create mode 100644 LINUX/IPTABLES, NFTABLES.md create mode 100644 LINUX/JOURNALCTL.md create mode 100644 LINUX/KILL, PKILL.md create mode 100644 LINUX/LEMP.md create mode 100644 "LINUX/LESS \320\270 SPLIT.md" create mode 100644 LINUX/LN.md create mode 100644 LINUX/LSOF.md create mode 100644 LINUX/Let's Encrypt.md create mode 100644 LINUX/MAILutils.md create mode 100644 LINUX/MC.md create mode 100644 LINUX/MOSQUITTO.md create mode 100644 LINUX/NC.md create mode 100644 LINUX/NCDU, BAT, FD, EXA, HTTPIE.md create mode 100644 LINUX/NCDU.md create mode 100644 LINUX/NETWORK/DNS.md create mode 100644 LINUX/NETWORK/IPERF3.md create mode 100644 LINUX/NETWORK/NETPLAN.md create mode 100644 LINUX/NETWORK/NETWORK.md create mode 100644 "LINUX/NETWORK/\320\220\320\262\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201\320\272\320\276\320\265 \320\276\320\261\320\275\320\260\321\200\321\203\320\266\320\265\320\275\320\270\320\265 \320\270 \321\203\320\262\320\265\320\264\320\276\320\274\320\273\320\265\320\275\320\270\320\265 \320\276 \320\275\320\276\320\262\321\213\321\205 \321\203\321\201\321\202\321\200\320\276\320\271\321\201\321\202\320\262\320\260\321\205 \320\262 \321\201\320\265\321\202\320\270.md" create mode 100644 "LINUX/NETWORK/\320\222\320\275\320\265\321\210\320\275\320\270\320\271 IP \320\270\320\267 \320\272\320\276\320\275\321\201\320\276\320\273\320\270.md" create mode 100644 "LINUX/NETWORK/\320\241\320\232\320\236\320\240\320\236\320\241\320\242\320\254 \320\241\320\225\320\242\320\225\320\222\320\236\320\231 \320\232\320\220\320\240\320\242\320\253.md" create mode 100644 LINUX/NGINX.md create mode 100644 LINUX/PLEX.md create mode 100644 LINUX/PROGRAMS_bash.md create mode 100644 LINUX/PS.md create mode 100644 LINUX/PostgreSQL.md create mode 100644 LINUX/RANGER.md create mode 100644 "LINUX/REPOSITORIES \321\201\320\262\320\276\320\271 \321\200\320\265\320\277\320\276\320\267\320\270\321\202\320\276\321\200\320\270\320\271 \321\201 \320\277\320\260\320\272\320\265\321\202\320\260\320\274\320\270.md" create mode 100644 LINUX/RM.md create mode 100644 LINUX/ROOT.md create mode 100644 LINUX/RSYNC.md create mode 100644 LINUX/SAMBA.md create mode 100644 LINUX/SCP, SFTP.md create mode 100644 "LINUX/SERVER - \320\274\320\276\320\275\320\270\321\202\320\276\321\200\320\270\320\275\320\263.md" create mode 100644 "LINUX/SHELL \320\272\320\276\320\274\320\260\320\275\320\264\321\213.md" create mode 100644 LINUX/SHUTDOWN, HALT, POWEROFF, REBOOT.md create mode 100644 LINUX/SSH.md create mode 100644 LINUX/SYSTEMCTL.md create mode 100644 LINUX/TAR.md create mode 100644 LINUX/TERMINAL.md create mode 100644 LINUX/TIMEDATE, CHRONY, NTP.md create mode 100644 LINUX/TLDR.md create mode 100644 LINUX/TMUX.md create mode 100644 LINUX/TRANSMISSION.md create mode 100644 LINUX/UFW.md create mode 100644 LINUX/USER.md create mode 100644 LINUX/VSFTPD.md create mode 100644 LINUX/WIREGUARD.md create mode 100644 LINUX/WORDPRESS.md create mode 100644 "LINUX/XARGS, TEE \320\270 WATCH.md" create mode 100644 LINUX/YAML.md create mode 100644 LINUX/__FLENOV.md create mode 100644 LINUX/iso fs _ ISO9660.md create mode 100644 LINUX/tr.md create mode 100644 "LINUX/\320\220\320\262\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201\320\272\320\276\320\265 \320\276\320\261\320\275\320\260\321\200\321\203\320\266\320\265\320\275\320\270\320\265 \320\277\320\276\320\264\320\276\320\267\321\200\320\270\321\202\320\265\320\273\321\214\320\275\321\213\321\205 \320\277\321\200\320\276\321\206\320\265\321\201\321\201\320\276\320\262 \320\262 Linux.md" create mode 100644 "LINUX/\320\220\320\262\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201\320\272\320\276\320\265 \320\276\321\202\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265 \320\267\320\260\320\262\320\270\321\201\321\210\320\270\321\205 \320\277\321\200\320\276\321\206\320\265\321\201\321\201\320\276\320\262 \320\262 Linux.md" create mode 100644 "LINUX/\320\221\320\265\320\267\320\276\320\277\320\260\321\201\320\275\320\276\321\201\321\202\321\214 Linux - \320\277\321\200\320\276\321\201\321\202\321\213\320\265 \321\210\320\260\320\263\320\270.md" create mode 100644 "LINUX/\320\221\321\213\321\201\321\202\321\200\320\276 \320\277\320\276\321\201\320\274\320\276\321\202\321\200\320\265\321\202\321\214 \320\262\320\265\321\200\321\201\320\270\321\216 \320\270 \320\275\320\260\320\267\320\262\320\260\320\275\320\270\320\265 \320\264\320\270\321\201\321\202\321\200\320\270\320\261\321\203\321\202\320\270\320\262\320\260 linux.md" create mode 100644 "LINUX/\320\230\321\201\320\277\321\200\320\260\320\262\320\270\321\202\321\214 USB, \321\201\320\274\320\276\320\275\321\202\320\270\321\200\320\276\320\262\320\260\320\275\320\275\321\203\321\216 \320\272\320\260\320\272 read-only \320\262 Linux.md" create mode 100644 "LINUX/\320\232\320\260\320\272 \321\200\320\260\320\267\320\261\320\270\321\202\321\214 \321\204\320\260\320\271\320\273 \320\275\320\260 \321\207\320\260\321\201\321\202\320\270.md" create mode 100644 "LINUX/\320\232\320\260\320\272 \321\203\320\264\320\260\320\273\321\217\321\202\321\214 \321\201\321\201\321\213\320\273\320\272\320\270.md" create mode 100644 "LINUX/\320\233\320\276\320\263\320\270 \320\262 Linux.md" create mode 100644 "LINUX/\320\236\320\277\321\202\320\270\320\274\320\270\320\267\320\260\321\206\320\270\321\217 \320\267\320\260\320\263\321\200\321\203\320\267\320\272\320\270 Linux - \321\203\321\201\320\272\320\276\321\200\321\217\320\265\320\274 \321\201\321\202\320\260\321\200\321\202 \321\201\320\270\321\201\321\202\320\265\320\274\321\213.md" create mode 100644 "LINUX/\320\236\321\207\320\270\321\201\321\202\320\272\320\260 \320\270 \321\203\320\264\320\260\320\273\320\265\320\275\320\270\320\265 \321\201\321\202\320\260\321\200\321\213\321\205 \321\217\320\264\320\265\321\200.md" create mode 100644 "LINUX/\320\241\320\261\320\276\321\200\320\272\320\260 \320\277\321\200\320\276\320\263\321\200\320\260\320\274\320\274 \320\270\320\267 \320\270\321\201\321\205\320\276\320\264\320\275\320\276\320\263\320\276 \320\272\320\276\320\264\320\260 \320\262 Linux.md" create mode 100644 "LINUX/\320\241\320\272\321\200\321\213\321\202\321\213\320\265 \320\277\321\200\320\276\321\206\320\265\321\201\321\201\321\213 \320\270 rootkits, \320\272\320\260\320\272 \320\275\320\260\320\271\321\202\320\270 \320\275\320\265\320\262\320\270\320\264\320\270\320\274\320\272\321\203 \320\262 Linux.md" create mode 100644 "LINUX/\320\243\320\264\320\260\320\273\320\265\320\275\320\270\320\265 \320\277\321\200\320\276\320\263\321\200\320\260\320\274\320\274.md" create mode 100644 "LINUX/\320\244\320\260\320\271\320\273\320\276\320\262\320\260\321\217 \321\201\320\270\321\201\321\202\320\265\320\274\320\260 Linux.md" create mode 100644 "Linux \321\201\321\202\320\260\320\262\320\270\320\274 \320\275\320\260 MacBook.md" create mode 100644 MacOS/Homebrew.md create mode 100644 MacOS/Postgresql.md create mode 100644 "MikroTik \320\277\321\200\320\276\320\261\321\200\320\276\321\201 \320\277\320\276\321\200\321\202\320\276\320\262.md" create mode 100644 NuPhy Air75.md create mode 100644 OBSIDIAN.md create mode 100644 PORT.md create mode 100644 PYTHON/-args, --kwargs.md create mode 100644 "PYTHON/12 \320\276\320\264\320\275\320\276\321\201\321\202\321\200\320\276\321\207\320\275\321\213\321\205 \320\272\320\276\320\275\321\201\321\202\321\200\321\203\320\272\321\206\320\270\320\271.md" create mode 100644 "PYTHON/8Gb \321\204\320\260\320\271\320\273 \320\276\321\202\320\272\321\200\321\213\321\202\321\214 \320\275\320\260 \320\236\320\227\320\243 2Gb.md" create mode 100644 PYTHON/AsyncIO.md create mode 100644 PYTHON/BEGIN + PIP.md create mode 100644 "PYTHON/COM-\320\277\320\276\321\200\321\202.md" create mode 100644 "PYTHON/DAEMON \320\275\320\260 Python.md" create mode 100644 PYTHON/DEFAULTDICT.md create mode 100644 PYTHON/DJANGO.md create mode 100644 PYTHON/ENUMERATE, ENUM.md create mode 100644 PYTHON/ENUMERATE, ZIP.md create mode 100644 PYTHON/FOR, WHILE.md create mode 100644 PYTHON/HTTP.md create mode 100644 PYTHON/LIST COMPREHENSION.md create mode 100644 PYTHON/MAP.md create mode 100644 PYTHON/MATH.md create mode 100644 PYTHON/Matprotlib.md create mode 100644 PYTHON/NAMEDTUPLE.md create mode 100644 "PYTHON/OPEN - \321\200\320\260\320\261\320\276\321\202\320\260 \321\201 \321\204\320\260\320\271\320\273\320\260\320\274\320\270.md" create mode 100644 PYTHON/OS.md create mode 100644 PYTHON/PATH.md create mode 100644 "PYTHON/PAUSE \320\262 PYTHON.md" create mode 100644 PYTHON/PEP8.md create mode 100644 PYTHON/PROGRAMS.md create mode 100644 PYTHON/PROGRAMS_py.md create mode 100644 PYTHON/PROGRESS BAR.md create mode 100644 PYTHON/PYINSTALLER.md create mode 100644 PYTHON/PYTHON_tuturial.md create mode 100644 PYTHON/Password & key = PATH.md create mode 100644 PYTHON/PrettyTable.md create mode 100644 PYTHON/Python-cheatsheet.md create mode 100644 PYTHON/ROUND, CEIL, FLOOR.md create mode 100644 PYTHON/SET, DICT, LIST, TUPLE, RANGE.md create mode 100644 PYTHON/SPEEDTEST python.md create mode 100644 PYTHON/SPLIT, JOIN.md create mode 100644 PYTHON/STRING.md create mode 100644 PYTHON/SnakeViz.md create mode 100644 PYTHON/TELEGRAMM BOT.md create mode 100644 PYTHON/THREADING.md create mode 100644 PYTHON/TKINTER.md create mode 100644 PYTHON/TRY ... EXCEPT.md create mode 100644 PYTHON/Translators.md create mode 100644 PYTHON/WITH.md create mode 100644 PYTHON/any, all.md create mode 100644 PYTHON/argparse.md create mode 100644 PYTHON/dir().md create mode 100644 PYTHON/exec.md create mode 100644 "PYTHON/hash() - \321\205\321\215\321\210\320\270\321\200\320\276\320\262\320\260\320\275\320\270\320\265.md" create mode 100644 PYTHON/itertools.md create mode 100644 PYTHON/keys() + &.md create mode 100644 "PYTHON/nmcli - \320\240\320\260\320\261\320\276\321\202\320\260 \321\201 \321\201\320\265\321\202\320\265\320\262\321\213\320\274\320\270 \320\270\320\275\321\202\320\265\321\200\321\204\320\265\320\271\321\201\320\260\320\274\320\270.md" create mode 100644 PYTHON/pandas.md create mode 100644 PYTHON/partition().md create mode 100644 PYTHON/poyo.md create mode 100644 PYTHON/pprint().md create mode 100644 PYTHON/sleep().md create mode 100644 PYTHON/tabulate.md create mode 100644 "PYTHON/\320\221\320\270\320\261\320\273\320\270\320\276\321\202\320\265\320\272\320\270 \320\264\320\273\321\217 \320\273\320\265\320\275\320\270\320\262\321\213\321\205.md" create mode 100644 "PYTHON/\320\222\320\276\320\267\320\262\321\200\320\260\321\202 \320\275\320\265\321\201\320\272\320\276\320\273\321\214\320\272\320\270\321\205 \320\267\320\275\320\260\321\207\320\265\320\275\320\270\320\271.md" create mode 100644 "PYTHON/\320\223\320\265\320\275\320\265\321\200\320\270\321\200\321\203\320\265\320\274 QR-\320\272\320\276\320\264.md" create mode 100644 "PYTHON/\320\232\320\276\320\275\321\202\320\265\320\272\321\201\321\202\320\275\321\213\320\265 \320\274\320\265\320\275\320\265\320\264\320\266\320\265\321\200\321\213.md" create mode 100644 "PYTHON/\320\232\320\276\320\277\320\270\321\200\321\203\320\265\320\274 \321\204\320\260\320\271\320\273 \320\262 \320\264\321\200\321\203\320\263\320\276\320\271 \320\272\320\260\321\202\320\260\320\273\320\276\320\263.md" create mode 100644 "PYTHON/\320\234\320\275\320\276\320\266\320\265\321\201\321\202\320\262\320\265\320\275\320\275\321\213\320\271 \320\262\320\262\320\276\320\264.md" create mode 100644 "PYTHON/\320\234\320\276\321\200\320\266\320\276\320\262\321\213\320\271 \320\276\320\277\320\265\321\200\320\260\321\202\320\276\321\200.md" create mode 100644 "PYTHON/\320\236\320\221\320\235\320\236\320\222\320\233\320\225\320\235\320\230\320\225 \320\264\320\276 \320\277\320\276\321\201\320\273\320\265\320\264\320\275\320\265\320\271 \320\262\320\265\321\200\321\201\320\270\320\270.md" create mode 100644 "PYTHON/\320\236\320\242\320\233\320\220\320\224\320\232\320\220 \320\232\320\236\320\224\320\220.md" create mode 100644 "PYTHON/\320\237\321\200\320\276\320\263\321\200\320\260\320\274\320\274\320\260-\320\275\320\260\320\277\320\276\320\274\320\270\320\275\320\260\320\273\320\272\320\260 \320\275\320\260 Python.md" create mode 100644 "PYTHON/\320\237\321\200\320\276\321\202\320\276\320\272\320\276\320\273\321\213.md" create mode 100644 "PYTHON/\320\241\320\262\320\276\321\217 \320\261\320\270\320\261\320\273\320\270\320\276\321\202\320\265\320\272\320\260 \320\264\320\273\321\217 Python.md" create mode 100644 "PYTHON/\320\241\321\202\321\200\320\276\320\270\320\274 \320\273\320\270\320\275\320\265\320\271\320\275\321\213\320\265 \320\263\321\200\320\260\321\204\320\270\320\272\320\270 \321\201 \320\277\320\276\320\274\320\276\321\211\321\214\321\216 Python.md" create mode 100644 "PYTHON/\320\242\320\265\321\201\321\202 \320\272\320\276\320\264\320\260 \320\262 \321\202\320\265\321\200\320\274\320\270\320\275\320\260\320\273\320\265.md" create mode 100644 "PYTHON/\320\243\320\273\321\203\321\207\321\210\320\260\320\265\320\274 \320\272\320\276\320\264 \320\275\320\260 Python.md" create mode 100644 "PYTHON/\320\244\320\243\320\235\320\232\320\246\320\230\320\230.md" create mode 100644 PyCharm.md create mode 100644 RADIO/ds18b20.md create mode 100644 "RADIO/\320\240\320\225\320\233\320\225 \320\277\320\276\320\275\320\270\320\266\320\265\320\275\320\270\320\265 \320\277\320\276\321\202\321\200\320\265\320\261\320\273\320\265\320\275\320\270\321\217.md" create mode 100644 SQL/MySQL.md create mode 100644 SQL/SQL Cheatsheet.md create mode 100644 "SQL/SQL. \320\221\320\260\320\267\321\213 \320\224\320\260\320\275\320\275\321\213\321\205.md" create mode 100644 SQL/SQLite Commands.md create mode 100644 SQL/SQLite.md create mode 100644 "SQL/\320\235\320\236\320\240\320\234\320\220\320\233\320\254\320\235\320\253\320\225 \320\244\320\236\320\240\320\234\320\253.md" create mode 100644 "SQL/\320\250\320\237\320\220\320\240\320\223\320\220\320\233\320\232\320\220 SQLite.md" create mode 100644 VIM.md create mode 100644 VSCode.md create mode 100644 YANDEX_DISK.md create mode 100644 _PROGRAMs/C++/C UART.md create mode 100644 _PROGRAMs/C++/MQTT.md create mode 100644 _PROGRAMs/C++/TIME.md create mode 100644 _PROGRAMs/C++/WiFi.md create mode 100644 _PROGRAMs/C++/snprintf.md create mode 100644 "_PROGRAMs/C++/\320\243\320\241\320\240\320\225\320\224\320\235\320\225\320\235\320\230\320\225.md" create mode 100644 _PROGRAMs/ESP8266-ESP32/EEPROM.md create mode 100644 "_PROGRAMs/ESP8266-ESP32/RTC \320\277\320\260\320\274\321\217\321\202\321\214.md" create mode 100644 "__github \321\203\321\207\320\265\320\261\320\275\320\270\320\272.md" create mode 100644 files/15d72c288938426db50c249effd75b07.png create mode 100644 files/1Gb_cab.png create mode 100644 files/1fd1edd152259cb558abab7409faa120.png create mode 100644 "files/2016-07-21 21Aygo-\320\263\320\273\321\203\321\210\320\270\321\202\320\265\320\273\321\214.jpg" create mode 100644 files/2025-02-20 04.18.59.jpg create mode 100644 files/2025-06-20_22-34-47.png create mode 100644 files/264fc3d5aab6c1a767f7659c207e1618.png create mode 100644 files/2e66d41ed2450e40ffd3fa20ba1036d7.png create mode 100644 files/32de918111f7fed3e8183b4b9148286b.png create mode 100644 files/370b55d2c9ca15ef5153581f2af2b9b0.png create mode 100644 files/4a2b1a7f1e1bee9a26d7885b1d5e0f8c.png create mode 100644 files/6cda81091bcc9f99ad224bd5c5426f8e.png create mode 100644 files/73ce6d03c95dd19ac4263224cc1acac4.png create mode 100644 files/7c6829decd5925af5d5e0dcc27128438.png create mode 100644 files/9132596e2802a7ecd3a47842f6475f60.png create mode 100644 "files/Aygo-\320\267\320\260\320\264_\320\277\320\276\320\273\321\213_\320\270_\320\277\320\260\320\275\320\265\320\273\321\214.jpg" create mode 100644 "files/Aygo-\320\267\320\260\320\264\320\275\320\270\320\271_\320\261\320\260\320\274\320\277\320\265\321\200.jpg" create mode 100644 "files/Aygo-\320\267\320\260\320\264\320\275\321\217\321\217_\320\277\320\276\320\264\320\262\320\265\321\201\320\272\320\260.jpg" create mode 100644 "files/Aygo-\320\277\320\265\321\200\320\265\320\264\320\275_\320\277\320\276\320\264\320\262\320\265\321\201\320\272\320\260.jpg" create mode 100644 "files/Aygo-\320\277\320\265\321\200\320\265\320\264\320\275\320\270\320\271_\320\261\320\260\320\274\320\277\320\265\321\200.jpg" create mode 100644 "files/Aygo-\321\202\320\276\320\277\320\273_\320\261\320\260\320\272_\320\270_\320\263\320\276\321\200\320\273\320\276\320\262\320\270\320\275\320\260.jpg" create mode 100644 files/DB_metall.ods create mode 100644 files/Pasted image 20240312221215.png create mode 100644 files/Pasted image 20250214130256.png create mode 100644 files/Pasted image 20250214130512.png create mode 100644 files/Pasted image 20250217114433.png create mode 100644 files/Pasted image 20250501030617.png create mode 100644 files/Pasted image 20250501031129.png create mode 100644 files/Pasted image 20250501031423.png create mode 100644 files/Pasted image 20250501031545.png create mode 100644 files/Pasted image 20250501031558.png create mode 100644 files/Pasted image 20250501031753.png create mode 100644 files/Pasted image 20250621003213.png create mode 100644 files/Pasted image 20250621003225.png create mode 100644 "files/Python_\321\210\320\277\320\260\321\200\320\263\320\260\320\273\320\272\320\260.png" create mode 100644 files/TG_bot_VPN-main.zip create mode 100644 files/TG_bot_VPN-main/.env.template create mode 100644 files/TG_bot_VPN-main/.gitignore create mode 100644 files/TG_bot_VPN-main/LICENSE create mode 100644 files/TG_bot_VPN-main/README.md create mode 100644 files/TG_bot_VPN-main/config_data/__init__.py create mode 100644 files/TG_bot_VPN-main/config_data/config.py create mode 100644 files/TG_bot_VPN-main/database/__init__.py create mode 100644 files/TG_bot_VPN-main/database/models.py create mode 100644 files/TG_bot_VPN-main/handlers/__init__.py create mode 100644 files/TG_bot_VPN-main/handlers/custom_heandlers/__init__.py create mode 100644 files/TG_bot_VPN-main/handlers/custom_heandlers/admin_handlers.py create mode 100644 files/TG_bot_VPN-main/handlers/custom_heandlers/instruction_handlers.py create mode 100644 files/TG_bot_VPN-main/handlers/custom_heandlers/location_handlers.py create mode 100644 files/TG_bot_VPN-main/handlers/default_heandlers/__init__.py create mode 100644 files/TG_bot_VPN-main/handlers/default_heandlers/echo.py create mode 100644 files/TG_bot_VPN-main/handlers/default_heandlers/help.py create mode 100644 files/TG_bot_VPN-main/handlers/default_heandlers/start.py create mode 100644 files/TG_bot_VPN-main/keyboards/__init__.py create mode 100644 files/TG_bot_VPN-main/keyboards/inline/__init__.py create mode 100644 files/TG_bot_VPN-main/keyboards/inline/admin_buttons.py create mode 100644 files/TG_bot_VPN-main/keyboards/inline/app_buttons.py create mode 100644 files/TG_bot_VPN-main/keyboards/inline/servers.py create mode 100644 files/TG_bot_VPN-main/keyboards/inline/subscribed.py create mode 100644 files/TG_bot_VPN-main/keyboards/reply/__init__.py create mode 100644 files/TG_bot_VPN-main/keyboards/reply/handlers_reply.py create mode 100644 files/TG_bot_VPN-main/loader.py create mode 100644 files/TG_bot_VPN-main/main.py create mode 100644 files/TG_bot_VPN-main/requirements.txt create mode 100644 files/TG_bot_VPN-main/states/__init__.py create mode 100644 files/TG_bot_VPN-main/states/states.py create mode 100644 files/TG_bot_VPN-main/utils/__init__.py create mode 100644 files/TG_bot_VPN-main/utils/functions.py create mode 100644 files/TG_bot_VPN-main/utils/generate_vpn_keys.py create mode 100644 files/TG_bot_VPN-main/utils/misc/__init__.py create mode 100644 files/TG_bot_VPN-main/utils/set_bot_commands.py create mode 100644 files/TG_bot_VPN-main/utils/tasks.py create mode 100644 files/TG_bot_VPN-main/utils/work_vpn_keys.py create mode 100644 files/ae81365154ba5166845a7a9171d2248b.png create mode 100644 files/b74e1c10b7c49ab7cc235e746af07128.png create mode 100644 files/dbb44ce3043db021cfbb195941e52a5a.png create mode 100644 files/eafc9c46561ec70fb5744ea1d85eb1c0.png create mode 100644 files/telegram-cloud-photo-size-2-5220111584251211570-y.jpg create mode 100644 files/telegram-cloud-photo-size-2-5220111584251211572-y.jpg create mode 100644 files/telegram-cloud-photo-size-2-5220111584251211573-y.jpg create mode 100644 files/telegram-cloud-photo-size-2-5230966980947866123-y.jpg create mode 100644 files/telegram-cloud-photo-size-2-5235693785730632253-y.jpg create mode 100644 files/telegram-cloud-photo-size-2-5249336126576118394-y.jpg create mode 100644 files/telegram-cloud-photo-size-2-5255812146984502723-y.jpg create mode 100644 files/telegram-cloud-photo-size-2-5264765212471446942-y.jpg create mode 100644 files/telegram-cloud-photo-size-2-5269638479933982570-y.jpg create mode 100644 files/telegram-cloud-photo-size-2-5280728042542581828-y.jpg create mode 100644 files/telegram-cloud-photo-size-2-5310086154055444585-y.jpg create mode 100644 files/telegram-cloud-photo-size-2-5316919099131030050-y.jpg create mode 100644 files/telegram-cloud-photo-size-2-5337133182321027005-y.jpg create mode 100644 files/telegram-cloud-photo-size-2-5350524228924991162-y.jpg create mode 100644 files/telegram-cloud-photo-size-2-5382031937209757544-y.jpg create mode 100644 files/telegram-cloud-photo-size-2-5382031937209757546-y.jpg create mode 100644 files/telegram-cloud-photo-size-2-5386535536837126461-y.jpg create mode 100644 files/telegram-cloud-photo-size-2-5399847483028401010-y.jpg create mode 100644 files/telegram-cloud-photo-size-2-5406976059292904279-x.jpg create mode 100644 files/telegram-cloud-photo-size-2-5413616564063822987-y.jpg create mode 100644 files/telegram-cloud-photo-size-4-5776315316576566340-y.jpg create mode 100644 files/telegram-cloud-photo-size-4-5850234924966199387-w.jpg create mode 100644 files/telegram-cloud-photo-size-4-5850234924966199390-w.jpg create mode 100644 files/telegram-cloud-photo-size-4-5850234924966199391-w.jpg create mode 100644 files/telegram-cloud-photo-size-4-5863854399405801203-w.jpg create mode 100644 files/telegram-cloud-photo-size-4-6016931255788092228-y.jpg create mode 100644 "files/\320\240\320\265\320\263\321\203\320\273\321\217\321\202\320\276\321\200_\320\276\320\261\320\276\321\200\320\276\321\202\320\276\320\262_\320\272\321\203\320\273\320\265\321\200\320\260.png" create mode 100644 "files/\320\241\320\272\321\200\320\270\320\277\321\202\321\213\320\224\320\273\321\217\320\220\320\264\320\274\320\270\320\275\320\260.jpg" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2023-08-06 \320\262 22.19.19.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2023-10-06 \320\262 10.40.44.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2023-10-19 \320\262 09.53.24.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2023-10-19 \320\262 09.53.43.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2023-10-29 \320\262 14.23.34.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2023-12-17 \320\262 20.47.18.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2023-12-17 \320\262 20.47.46.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2023-12-17 \320\262 20.53.26.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2023-12-17 \320\262 20.55.53.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2024-03-10 \320\262 20.55.05.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2024-03-14 \320\262 13.21.49.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2024-03-14 \320\262 13.22.17.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2024-03-14 \320\262 13.25.59.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2024-03-14 \320\262 13.27.58.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2024-03-14 \320\262 13.29.48.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2024-03-14 \320\262 13.32.01.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2024-03-25 \320\262 22.49.58.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2024-09-15 \320\262 18.55.25.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2024-09-15 \320\262 19.02.02.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2024-09-15 \320\262 19.10.29.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2024-09-16 \320\262 21.31.06.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2024-09-16 \320\262 21.36.18.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2024-09-16 \320\262 21.39.34.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2024-09-16 \320\262 21.45.31.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2024-09-16 \320\262 21.45.54.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2024-11-01 \320\262 01.39.59.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2025-02-02 \320\262 18.05.19.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2025-02-02 \320\262 18.06.09.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2025-02-02 \320\262 18.13.01.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2025-02-02 \320\262 18.29.43.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2025-02-02 \320\262 18.30.02.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2025-02-11 \320\262 01.15.10.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2025-02-11 \320\262 01.17.25.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2025-05-14 \320\262 00.35.18.png" create mode 100644 "files/\320\241\320\275\320\270\320\274\320\276\320\272 \321\215\320\272\321\200\320\260\320\275\320\260 2025-05-14 \320\262 00.38.05.png" create mode 100644 sync create mode 100644 telegram-cloud-photo-size-2-5253592580900375225-x.jpg.md create mode 100644 "\320\220\320\275\320\263\320\273\320\276-\321\200\321\203\321\201\321\201\320\272\320\270\320\271 \320\241\320\233\320\236\320\222\320\220\320\240\320\254 !!!.md" diff --git a/.obsidian/app.json b/.obsidian/app.json new file mode 100644 index 0000000..453053f --- /dev/null +++ b/.obsidian/app.json @@ -0,0 +1,14 @@ +{ + "vimMode": true, + "attachmentFolderPath": "files", + "pdfExportSettings": { + "includeName": true, + "pageSize": "A4", + "landscape": false, + "margin": "0", + "downscalePercent": 100 + }, + "showLineNumber": false, + "autoPairMarkdown": true, + "spellcheck": false +} \ No newline at end of file diff --git a/.obsidian/appearance.json b/.obsidian/appearance.json new file mode 100644 index 0000000..21c46fc --- /dev/null +++ b/.obsidian/appearance.json @@ -0,0 +1,4 @@ +{ + "accentColor": "", + "theme": "moonstone" +} \ No newline at end of file diff --git a/.obsidian/core-plugins-migration.json b/.obsidian/core-plugins-migration.json new file mode 100644 index 0000000..436f43c --- /dev/null +++ b/.obsidian/core-plugins-migration.json @@ -0,0 +1,30 @@ +{ + "file-explorer": true, + "global-search": true, + "switcher": true, + "graph": true, + "backlink": true, + "canvas": true, + "outgoing-link": true, + "tag-pane": true, + "properties": false, + "page-preview": true, + "daily-notes": true, + "templates": true, + "note-composer": true, + "command-palette": true, + "slash-command": false, + "editor-status": true, + "bookmarks": true, + "markdown-importer": false, + "zk-prefixer": false, + "random-note": false, + "outline": true, + "word-count": true, + "slides": false, + "audio-recorder": false, + "workspaces": false, + "file-recovery": true, + "publish": false, + "sync": false +} \ No newline at end of file diff --git a/.obsidian/core-plugins.json b/.obsidian/core-plugins.json new file mode 100644 index 0000000..8e719d8 --- /dev/null +++ b/.obsidian/core-plugins.json @@ -0,0 +1,33 @@ +{ + "file-explorer": true, + "global-search": true, + "switcher": true, + "graph": true, + "backlink": true, + "canvas": true, + "outgoing-link": true, + "tag-pane": true, + "properties": false, + "page-preview": true, + "daily-notes": true, + "templates": true, + "note-composer": true, + "command-palette": true, + "slash-command": false, + "editor-status": true, + "bookmarks": true, + "markdown-importer": false, + "zk-prefixer": false, + "random-note": false, + "outline": true, + "word-count": true, + "slides": false, + "audio-recorder": false, + "workspaces": false, + "file-recovery": true, + "publish": false, + "sync": false, + "webviewer": false, + "footnotes": false, + "bases": true +} \ No newline at end of file diff --git a/.obsidian/graph.json b/.obsidian/graph.json new file mode 100644 index 0000000..42a46ec --- /dev/null +++ b/.obsidian/graph.json @@ -0,0 +1,22 @@ +{ + "collapse-filter": true, + "search": "", + "showTags": false, + "showAttachments": false, + "hideUnresolved": false, + "showOrphans": true, + "collapse-color-groups": true, + "colorGroups": [], + "collapse-display": true, + "showArrow": false, + "textFadeMultiplier": 0, + "nodeSizeMultiplier": 1, + "lineSizeMultiplier": 1, + "collapse-forces": true, + "centerStrength": 0.518713248970312, + "repelStrength": 10, + "linkStrength": 1, + "linkDistance": 250, + "scale": 1, + "close": true +} \ No newline at end of file diff --git a/.obsidian/hotkeys.json b/.obsidian/hotkeys.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/.obsidian/hotkeys.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/.obsidian/templates.json b/.obsidian/templates.json new file mode 100644 index 0000000..cda2aca --- /dev/null +++ b/.obsidian/templates.json @@ -0,0 +1,3 @@ +{ + "folder": "templates" +} \ No newline at end of file diff --git a/.obsidian/workspace (10).json b/.obsidian/workspace (10).json new file mode 100644 index 0000000..eecbabc --- /dev/null +++ b/.obsidian/workspace (10).json @@ -0,0 +1,210 @@ +{ + "main": { + "id": "22b54cdfe598d436", + "type": "split", + "children": [ + { + "id": "fd025cb4a7192636", + "type": "tabs", + "children": [ + { + "id": "7c0c92b3108c5e91", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "LINUX/WIREGUARD.md", + "mode": "source", + "source": false + } + } + }, + { + "id": "0326d9c93c8b7596", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "LINUX/GPG, PASS.md", + "mode": "source", + "source": false + } + } + } + ], + "currentTab": 1 + } + ], + "direction": "vertical" + }, + "left": { + "id": "ae9ce61e34e2a30b", + "type": "split", + "children": [ + { + "id": "272888dc6bd23a63", + "type": "tabs", + "children": [ + { + "id": "3d69f2baa3152c95", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "byModifiedTimeReverse" + } + } + }, + { + "id": "304816efb0ab556e", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + } + } + }, + { + "id": "e88a968f3bbf9578", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {} + } + } + ] + } + ], + "direction": "horizontal", + "width": 211.5 + }, + "right": { + "id": "b155a938b8a7e919", + "type": "split", + "children": [ + { + "id": "3f12dfc7ff91ce6e", + "type": "tabs", + "children": [ + { + "id": "a9fe2861a649f07f", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "LINUX/GPG, PASS.md", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "f5f51f424be955ad", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "LINUX/GPG, PASS.md", + "linksCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "daedec170123f202", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true + } + } + }, + { + "id": "ab86f8ad95ae5f4c", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "LINUX/GPG, PASS.md" + } + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Меню быстрого перехода": false, + "graph:Граф": false, + "canvas:Создать новый холст": false, + "daily-notes:Сегодняшняя заметка": false, + "templates:Вставить шаблон": false, + "command-palette:Открыть палитру команд": false + } + }, + "active": "0326d9c93c8b7596", + "lastOpenFiles": [ + "LINUX/Systemctl.md", + "LINUX/Автозапуск скрипта.md", + "LINUX/WIREGUARD.md", + "LINUX/SCP.md", + "LINUX/GPG, PASS.md", + "Login.md", + "GIT.md", + "PYTHON/PYINSTALLER.md", + "123.md", + "PYTHON/ФУНКЦИИ.md", + "PYTHON/SET, DICT.md", + "PYTHON/TUPLE.md", + "PYTHON/LIST.md", + "VIM.md", + "RANGER.md", + "PEP8.md", + "PEP8", + "LINUX/CHMOD.md", + "PYTHON", + "SHELL команды.md", + "LINUX/ENVIRONMENT.md", + "YANDEX_DISK.md", + "Образец.md", + "Installing Applications.md", + "LINUX/SSH.md", + "LINUX/HTOP.md", + "LINUX/DIFFERENT.md", + "LINUX/BEGIN.md", + "LINUX/NETWORK", + "files/Aygo-топл_бак_и_горловина.jpg", + "files/Aygo-передний_бампер.jpg", + "files/Aygo-передн_подвеска.jpg", + "files/Aygo-задняя_подвеска.jpg", + "files/Aygo-задний_бампер.jpg", + "files/Aygo-зад_полы_и_панель.jpg", + "files/2016-07-21 21Aygo-глушитель.jpg", + "files/ae81365154ba5166845a7a9171d2248b.png", + "files/6cda81091bcc9f99ad224bd5c5426f8e.png", + "files/4a2b1a7f1e1bee9a26d7885b1d5e0f8c.png", + "templates", + "REPORTS", + "Без названия 1", + "files", + "LINUX/DISK", + "LINUX" + ] +} \ No newline at end of file diff --git a/.obsidian/workspace (11).json b/.obsidian/workspace (11).json new file mode 100644 index 0000000..d775058 --- /dev/null +++ b/.obsidian/workspace (11).json @@ -0,0 +1,199 @@ +{ + "main": { + "id": "22b54cdfe598d436", + "type": "split", + "children": [ + { + "id": "cd9be558ae886730", + "type": "tabs", + "children": [ + { + "id": "f930a2b0468778c4", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "LINUX/SERVER - мониторинг.md", + "mode": "source", + "source": false + } + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "ae9ce61e34e2a30b", + "type": "split", + "children": [ + { + "id": "272888dc6bd23a63", + "type": "tabs", + "children": [ + { + "id": "3d69f2baa3152c95", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical" + } + } + }, + { + "id": "304816efb0ab556e", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + } + } + }, + { + "id": "e88a968f3bbf9578", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {} + } + } + ] + } + ], + "direction": "horizontal", + "width": 241.5 + }, + "right": { + "id": "b155a938b8a7e919", + "type": "split", + "children": [ + { + "id": "3f12dfc7ff91ce6e", + "type": "tabs", + "children": [ + { + "id": "a9fe2861a649f07f", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "LINUX/SERVER - мониторинг.md", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "f5f51f424be955ad", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "LINUX/SERVER - мониторинг.md", + "linksCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "daedec170123f202", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true + } + } + }, + { + "id": "ab86f8ad95ae5f4c", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "LINUX/SERVER - мониторинг.md" + } + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Меню быстрого перехода": false, + "graph:Граф": false, + "canvas:Создать новый холст": false, + "daily-notes:Сегодняшняя заметка": false, + "templates:Вставить шаблон": false, + "command-palette:Открыть палитру команд": false + } + }, + "active": "f930a2b0468778c4", + "lastOpenFiles": [ + "LINUX/BEGIN.md", + "LINUX/SCP.md", + "LINUX/TERMINAL.md", + "PYTHON/BEGIN + PIP.md", + "SPOOFDPI.md", + "SQL/MySQL.md", + "PYTHON/COM-порт.md", + "SQL/SQLite.md", + "LINUX/BASH_script/BASH. Разное.md", + "LINUX/BASH_github/Основы Bash.md", + "LINUX/BASH_github/bash-guide-1.md", + "SQL/SQLite Commands.md", + "SQL/ШПАРГАЛКА SQLite.md", + "SQL/SQL. Базы Данных.md", + "PYTHON/SET, DICT, LIST, TUPLE, RANGE.md", + "PYTHON/STRING.md", + "PYTHON/Password & key = PATH.md", + "SQL/SQL Cheatsheet.md", + "PYTHON/DJANGO.md", + "LINUX/RSYNC.md", + "PYTHON/PYINSTALLER.md", + "SQL/НОРМАЛЬНЫЕ ФОРМЫ.md", + "files/Снимок экрана 2024-09-16 в 21.45.54.png", + "files/Снимок экрана 2024-09-16 в 21.45.31.png", + "files/Снимок экрана 2024-09-16 в 21.39.34.png", + "files/Снимок экрана 2024-09-16 в 21.36.18.png", + "files/Снимок экрана 2024-09-16 в 21.31.06.png", + "files/Снимок экрана 2024-09-15 в 19.10.29.png", + "files/Снимок экрана 2024-09-15 в 19.02.02.png", + "files/Снимок экрана 2024-09-15 в 18.55.25.png", + "PYTHON/PrettyTable.md", + "files/telegram-cloud-photo-size-2-5413616564063822987-y.jpg", + "PYTHON/PROGRAMS_py.md", + "SQL", + "VSCode.md", + "LINUX/NETWORK/NETWORK.md", + "files/2024-09-05 01.32.11.jpg", + "LINUX/BASH_script", + "RADIO", + "LINUX/BASH_github", + "sync", + "gitSync.sh", + "AUTO", + "_PROGRAMs/C++", + "MacOS", + "_PROGRAMs/ESP8266-ESP32", + "Без названия.canvas" + ] +} \ No newline at end of file diff --git a/.obsidian/workspace (12).json b/.obsidian/workspace (12).json new file mode 100644 index 0000000..f32eadf --- /dev/null +++ b/.obsidian/workspace (12).json @@ -0,0 +1,215 @@ +{ + "main": { + "id": "22b54cdfe598d436", + "type": "split", + "children": [ + { + "id": "ebc417c74e936cbe", + "type": "tabs", + "children": [ + { + "id": "df49f7d5f8bd02a2", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "_PROGRAMs/C++/C UART.md", + "mode": "source", + "source": false + }, + "icon": "lucide-file", + "title": "C UART" + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "ae9ce61e34e2a30b", + "type": "split", + "children": [ + { + "id": "272888dc6bd23a63", + "type": "tabs", + "children": [ + { + "id": "3d69f2baa3152c95", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical" + }, + "icon": "lucide-folder-closed", + "title": "Файловый менеджер" + } + }, + { + "id": "304816efb0ab556e", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + }, + "icon": "lucide-search", + "title": "Поиск" + } + }, + { + "id": "e88a968f3bbf9578", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {}, + "icon": "lucide-bookmark", + "title": "Закладки" + } + } + ] + } + ], + "direction": "horizontal", + "width": 241.5 + }, + "right": { + "id": "b155a938b8a7e919", + "type": "split", + "children": [ + { + "id": "3f12dfc7ff91ce6e", + "type": "tabs", + "children": [ + { + "id": "a9fe2861a649f07f", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "GIT.md", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-coming-in", + "title": "Обратные ссылки для GIT" + } + }, + { + "id": "f5f51f424be955ad", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "GIT.md", + "linksCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-going-out", + "title": "Исходящие ссылки из GIT" + } + }, + { + "id": "daedec170123f202", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true + }, + "icon": "lucide-tags", + "title": "Теги" + } + }, + { + "id": "ab86f8ad95ae5f4c", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "GIT.md" + }, + "icon": "lucide-list", + "title": "Структура GIT" + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Меню быстрого перехода": false, + "graph:Граф": false, + "canvas:Создать новый холст": false, + "daily-notes:Сегодняшняя заметка": false, + "templates:Вставить шаблон": false, + "command-palette:Открыть палитру команд": false + } + }, + "active": "df49f7d5f8bd02a2", + "lastOpenFiles": [ + "GIT.md", + "_PROGRAMs/C++/C UART.md", + "_PROGRAMs/C++/WiFi.md", + "_PROGRAMs/C++/TIME.md", + "_PROGRAMs/C++/MQTT.md", + "_PROGRAMs/C++/УСРЕДНЕНИЕ.md", + "_PROGRAMs/ESP8266-ESP32/EEPROM.md", + "_PROGRAMs/ESP8266-ESP32/RTC память.md", + "LINUX/GPG, PASS.md", + "DaVinciResolve/HEVC.md", + "DaVinciResolve/ОПТИМИЗАЦИЯ.md", + "YANDEX_DISK.md", + "NuPhy Air75.md", + "LINUX/MOSQUITTO.md", + "DaVinciResolve", + "LINUX/UFW.md", + "LINUX/TMUX.md", + "LINUX/CHMOD, CHOWN.md", + "LINUX/USER.md", + "LINUX/BASH.md", + "LINUX/BEGIN.md", + "LINUX/SHELL команды.md", + "Login.md", + "PixelNAS.md", + "LINUX/DISK/SMART.md", + "LINUX/DISK/Working with DISK.md", + "LINUX/DISK/DF, DU.md", + "files/telegram-cloud-photo-size-2-5406976059292904279-x.jpg", + "files/DB_metall.ods", + "LINUX/TIME", + "files/Снимок экрана 2024-11-01 в 01.39.59.png", + "files/Снимок экрана 2024-09-16 в 21.45.54.png", + "files/Снимок экрана 2024-09-16 в 21.45.31.png", + "files/Снимок экрана 2024-09-16 в 21.39.34.png", + "files/Снимок экрана 2024-09-16 в 21.36.18.png", + "files/Снимок экрана 2024-09-16 в 21.31.06.png", + "files/Снимок экрана 2024-09-15 в 19.10.29.png", + "files/Снимок экрана 2024-09-15 в 19.02.02.png", + "files/Снимок экрана 2024-09-15 в 18.55.25.png", + "SQL", + "LINUX/BASH_script", + "RADIO", + "LINUX/BASH_github", + "sync", + "gitSync.sh", + "AUTO", + "Без названия.canvas" + ] +} \ No newline at end of file diff --git a/.obsidian/workspace (13).json b/.obsidian/workspace (13).json new file mode 100644 index 0000000..229abc5 --- /dev/null +++ b/.obsidian/workspace (13).json @@ -0,0 +1,230 @@ +{ + "main": { + "id": "22b54cdfe598d436", + "type": "split", + "children": [ + { + "id": "ebc417c74e936cbe", + "type": "tabs", + "children": [ + { + "id": "df49f7d5f8bd02a2", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "GIT.md", + "mode": "source", + "source": false + }, + "icon": "lucide-file", + "title": "GIT" + } + }, + { + "id": "edabed50a518b732", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "DaVinciResolve/Горячие клавиши.md", + "mode": "source", + "source": false + }, + "icon": "lucide-file", + "title": "Горячие клавиши" + } + } + ], + "currentTab": 1 + } + ], + "direction": "vertical" + }, + "left": { + "id": "ae9ce61e34e2a30b", + "type": "split", + "children": [ + { + "id": "272888dc6bd23a63", + "type": "tabs", + "children": [ + { + "id": "3d69f2baa3152c95", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical" + }, + "icon": "lucide-folder-closed", + "title": "Файловый менеджер" + } + }, + { + "id": "304816efb0ab556e", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + }, + "icon": "lucide-search", + "title": "Поиск" + } + }, + { + "id": "e88a968f3bbf9578", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {}, + "icon": "lucide-bookmark", + "title": "Закладки" + } + } + ] + } + ], + "direction": "horizontal", + "width": 241.5 + }, + "right": { + "id": "b155a938b8a7e919", + "type": "split", + "children": [ + { + "id": "3f12dfc7ff91ce6e", + "type": "tabs", + "children": [ + { + "id": "a9fe2861a649f07f", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "GIT.md", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-coming-in", + "title": "Обратные ссылки для GIT" + } + }, + { + "id": "f5f51f424be955ad", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "GIT.md", + "linksCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-going-out", + "title": "Исходящие ссылки из GIT" + } + }, + { + "id": "daedec170123f202", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true + }, + "icon": "lucide-tags", + "title": "Теги" + } + }, + { + "id": "ab86f8ad95ae5f4c", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "GIT.md" + }, + "icon": "lucide-list", + "title": "Структура GIT" + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Меню быстрого перехода": false, + "graph:Граф": false, + "canvas:Создать новый холст": false, + "daily-notes:Сегодняшняя заметка": false, + "templates:Вставить шаблон": false, + "command-palette:Открыть палитру команд": false + } + }, + "active": "edabed50a518b732", + "lastOpenFiles": [ + "DaVinciResolve/ОПТИМИЗАЦИЯ.md", + "DaVinciResolve/HEVC.md", + "DaVinciResolve/Горячие клавиши.md", + "GIT.md", + "LINUX/CHMOD, CHOWN.md", + "LINUX/RM.md", + "LINUX/BASH_github/bash-guide-1.md", + "LINUX/BASH_github/Основы Bash.md", + "LINUX/BASH.md", + "LINUX/GPG, PASS.md", + "LINUX/TAR.md", + "_PROGRAMs/C++/C UART.md", + "_PROGRAMs/C++/Без названия.md", + "_PROGRAMs/C++/WiFi.md", + "_PROGRAMs/C++/TIME.md", + "_PROGRAMs/C++/MQTT.md", + "_PROGRAMs/C++/УСРЕДНЕНИЕ.md", + "_PROGRAMs/ESP8266-ESP32/EEPROM.md", + "_PROGRAMs/ESP8266-ESP32/RTC память.md", + "YANDEX_DISK.md", + "NuPhy Air75.md", + "LINUX/MOSQUITTO.md", + "DaVinciResolve", + "LINUX/UFW.md", + "LINUX/TMUX.md", + "LINUX/USER.md", + "LINUX/BEGIN.md", + "files/telegram-cloud-photo-size-2-5406976059292904279-x.jpg", + "files/DB_metall.ods", + "LINUX/TIME", + "files/Снимок экрана 2024-11-01 в 01.39.59.png", + "files/Снимок экрана 2024-09-16 в 21.45.54.png", + "files/Снимок экрана 2024-09-16 в 21.45.31.png", + "files/Снимок экрана 2024-09-16 в 21.39.34.png", + "files/Снимок экрана 2024-09-16 в 21.36.18.png", + "files/Снимок экрана 2024-09-16 в 21.31.06.png", + "files/Снимок экрана 2024-09-15 в 19.10.29.png", + "files/Снимок экрана 2024-09-15 в 19.02.02.png", + "files/Снимок экрана 2024-09-15 в 18.55.25.png", + "SQL", + "LINUX/BASH_script", + "RADIO", + "LINUX/BASH_github", + "sync", + "gitSync.sh", + "AUTO", + "Без названия.canvas" + ] +} \ No newline at end of file diff --git a/.obsidian/workspace (2).json b/.obsidian/workspace (2).json new file mode 100644 index 0000000..66df600 --- /dev/null +++ b/.obsidian/workspace (2).json @@ -0,0 +1,155 @@ +{ + "main": { + "id": "22b54cdfe598d436", + "type": "split", + "children": [ + { + "id": "787893e48a6c8630", + "type": "tabs", + "children": [ + { + "id": "dcde0a554b665396", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "AUTO.md", + "mode": "source", + "source": false + } + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "ae9ce61e34e2a30b", + "type": "split", + "children": [ + { + "id": "272888dc6bd23a63", + "type": "tabs", + "children": [ + { + "id": "3d69f2baa3152c95", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical" + } + } + }, + { + "id": "304816efb0ab556e", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + } + } + }, + { + "id": "e88a968f3bbf9578", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {} + } + } + ] + } + ], + "direction": "horizontal", + "width": 300 + }, + "right": { + "id": "b155a938b8a7e919", + "type": "split", + "children": [ + { + "id": "3f12dfc7ff91ce6e", + "type": "tabs", + "children": [ + { + "id": "a9fe2861a649f07f", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "AUTO.md", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "f5f51f424be955ad", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "AUTO.md", + "linksCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "daedec170123f202", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true + } + } + }, + { + "id": "ab86f8ad95ae5f4c", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "AUTO.md" + } + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Меню быстрого перехода": false, + "graph:Граф": false, + "canvas:Создать новый холст": false, + "daily-notes:Сегодняшняя заметка": false, + "templates:Вставить шаблон": false, + "command-palette:Открыть палитру команд": false + } + }, + "active": "dcde0a554b665396", + "lastOpenFiles": [ + "HOME CURRENT.md", + "27-10-2023.md", + "Без названия 1.md" + ] +} \ No newline at end of file diff --git a/.obsidian/workspace (3).json b/.obsidian/workspace (3).json new file mode 100644 index 0000000..ed8fb4e --- /dev/null +++ b/.obsidian/workspace (3).json @@ -0,0 +1,156 @@ +{ + "main": { + "id": "22b54cdfe598d436", + "type": "split", + "children": [ + { + "id": "787893e48a6c8630", + "type": "tabs", + "children": [ + { + "id": "dcde0a554b665396", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "HOME CURRENT.md", + "mode": "source", + "source": false + } + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "ae9ce61e34e2a30b", + "type": "split", + "children": [ + { + "id": "272888dc6bd23a63", + "type": "tabs", + "children": [ + { + "id": "3d69f2baa3152c95", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical" + } + } + }, + { + "id": "304816efb0ab556e", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + } + } + }, + { + "id": "e88a968f3bbf9578", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {} + } + } + ] + } + ], + "direction": "horizontal", + "width": 300 + }, + "right": { + "id": "b155a938b8a7e919", + "type": "split", + "children": [ + { + "id": "3f12dfc7ff91ce6e", + "type": "tabs", + "children": [ + { + "id": "a9fe2861a649f07f", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "HOME CURRENT.md", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "f5f51f424be955ad", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "HOME CURRENT.md", + "linksCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "daedec170123f202", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true + } + } + }, + { + "id": "ab86f8ad95ae5f4c", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "HOME CURRENT.md" + } + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Меню быстрого перехода": false, + "graph:Граф": false, + "canvas:Создать новый холст": false, + "daily-notes:Сегодняшняя заметка": false, + "templates:Вставить шаблон": false, + "command-palette:Открыть палитру команд": false + } + }, + "active": "dcde0a554b665396", + "lastOpenFiles": [ + "AUTO.md", + "HOME CURRENT.md", + "27-10-2023.md", + "Без названия 1.md" + ] +} \ No newline at end of file diff --git a/.obsidian/workspace (4).json b/.obsidian/workspace (4).json new file mode 100644 index 0000000..c9b84b3 --- /dev/null +++ b/.obsidian/workspace (4).json @@ -0,0 +1,143 @@ +{ + "main": { + "id": "7120c54e418009c5", + "type": "split", + "children": [ + { + "id": "95827c3665516eb6", + "type": "tabs", + "children": [ + { + "id": "fef83e212ea030ea", + "type": "leaf", + "state": { + "type": "empty", + "state": {} + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "caef8b6d0a2e9854", + "type": "split", + "children": [ + { + "id": "897311b08e08ddd3", + "type": "tabs", + "children": [ + { + "id": "4e7ec7aef4d77191", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical" + } + } + }, + { + "id": "af93c627d0ddbf57", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + } + } + }, + { + "id": "701abe3445c1c9b2", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {} + } + } + ] + } + ], + "direction": "horizontal", + "width": 300 + }, + "right": { + "id": "49aef91a1183b1c4", + "type": "split", + "children": [ + { + "id": "6d19a527f796b391", + "type": "tabs", + "children": [ + { + "id": "6d74d404f2dc532e", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "234a0fb01aba93bd", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "linksCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "68c1a8a881843553", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true + } + } + }, + { + "id": "a045bb67d56f4645", + "type": "leaf", + "state": { + "type": "outline", + "state": {} + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Меню быстрого перехода": false, + "graph:Граф": false, + "canvas:Создать новый холст": false, + "daily-notes:Сегодняшняя заметка": false, + "templates:Вставить шаблон": false, + "command-palette:Открыть палитру команд": false + } + }, + "active": "fef83e212ea030ea", + "lastOpenFiles": [] +} \ No newline at end of file diff --git a/.obsidian/workspace (5).json b/.obsidian/workspace (5).json new file mode 100644 index 0000000..e7fd905 --- /dev/null +++ b/.obsidian/workspace (5).json @@ -0,0 +1,151 @@ +{ + "main": { + "id": "7120c54e418009c5", + "type": "split", + "children": [ + { + "id": "95827c3665516eb6", + "type": "tabs", + "children": [ + { + "id": "fef83e212ea030ea", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "AUTO.md", + "mode": "source", + "source": false + } + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "caef8b6d0a2e9854", + "type": "split", + "children": [ + { + "id": "897311b08e08ddd3", + "type": "tabs", + "children": [ + { + "id": "4e7ec7aef4d77191", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical" + } + } + }, + { + "id": "af93c627d0ddbf57", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + } + } + }, + { + "id": "701abe3445c1c9b2", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {} + } + } + ] + } + ], + "direction": "horizontal", + "width": 300 + }, + "right": { + "id": "49aef91a1183b1c4", + "type": "split", + "children": [ + { + "id": "6d19a527f796b391", + "type": "tabs", + "children": [ + { + "id": "6d74d404f2dc532e", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "AUTO.md", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "234a0fb01aba93bd", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "AUTO.md", + "linksCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "68c1a8a881843553", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true + } + } + }, + { + "id": "a045bb67d56f4645", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "AUTO.md" + } + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Меню быстрого перехода": false, + "graph:Граф": false, + "canvas:Создать новый холст": false, + "daily-notes:Сегодняшняя заметка": false, + "templates:Вставить шаблон": false, + "command-palette:Открыть палитру команд": false + } + }, + "active": "fef83e212ea030ea", + "lastOpenFiles": [] +} \ No newline at end of file diff --git a/.obsidian/workspace (6).json b/.obsidian/workspace (6).json new file mode 100644 index 0000000..c4cc2a8 --- /dev/null +++ b/.obsidian/workspace (6).json @@ -0,0 +1,153 @@ +{ + "main": { + "id": "7120c54e418009c5", + "type": "split", + "children": [ + { + "id": "95827c3665516eb6", + "type": "tabs", + "children": [ + { + "id": "fef83e212ea030ea", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "HOME CURRENT.md", + "mode": "source", + "source": false + } + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "caef8b6d0a2e9854", + "type": "split", + "children": [ + { + "id": "897311b08e08ddd3", + "type": "tabs", + "children": [ + { + "id": "4e7ec7aef4d77191", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical" + } + } + }, + { + "id": "af93c627d0ddbf57", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + } + } + }, + { + "id": "701abe3445c1c9b2", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {} + } + } + ] + } + ], + "direction": "horizontal", + "width": 300 + }, + "right": { + "id": "49aef91a1183b1c4", + "type": "split", + "children": [ + { + "id": "6d19a527f796b391", + "type": "tabs", + "children": [ + { + "id": "6d74d404f2dc532e", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "HOME CURRENT.md", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "234a0fb01aba93bd", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "HOME CURRENT.md", + "linksCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "68c1a8a881843553", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true + } + } + }, + { + "id": "a045bb67d56f4645", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "HOME CURRENT.md" + } + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Меню быстрого перехода": false, + "graph:Граф": false, + "canvas:Создать новый холст": false, + "daily-notes:Сегодняшняя заметка": false, + "templates:Вставить шаблон": false, + "command-palette:Открыть палитру команд": false + } + }, + "active": "fef83e212ea030ea", + "lastOpenFiles": [ + "AUTO.md" + ] +} \ No newline at end of file diff --git a/.obsidian/workspace (7).json b/.obsidian/workspace (7).json new file mode 100644 index 0000000..c5be379 --- /dev/null +++ b/.obsidian/workspace (7).json @@ -0,0 +1,153 @@ +{ + "main": { + "id": "7120c54e418009c5", + "type": "split", + "children": [ + { + "id": "95827c3665516eb6", + "type": "tabs", + "children": [ + { + "id": "fef83e212ea030ea", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "HOME CURRENT.md", + "mode": "source", + "source": false + } + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "caef8b6d0a2e9854", + "type": "split", + "children": [ + { + "id": "897311b08e08ddd3", + "type": "tabs", + "children": [ + { + "id": "4e7ec7aef4d77191", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical" + } + } + }, + { + "id": "af93c627d0ddbf57", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + } + } + }, + { + "id": "701abe3445c1c9b2", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {} + } + } + ] + } + ], + "direction": "horizontal", + "width": 300 + }, + "right": { + "id": "49aef91a1183b1c4", + "type": "split", + "children": [ + { + "id": "6d19a527f796b391", + "type": "tabs", + "children": [ + { + "id": "6d74d404f2dc532e", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "HOME CURRENT.md", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "234a0fb01aba93bd", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "HOME CURRENT.md", + "linksCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "68c1a8a881843553", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true + } + } + }, + { + "id": "a045bb67d56f4645", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "HOME CURRENT.md" + } + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Меню быстрого перехода": false, + "graph:Граф": false, + "canvas:Создать новый холст": false, + "daily-notes:Сегодняшняя заметка": false, + "templates:Вставить шаблон": false, + "command-palette:Открыть палитру команд": false + } + }, + "active": "4e7ec7aef4d77191", + "lastOpenFiles": [ + "AUTO.md" + ] +} \ No newline at end of file diff --git a/.obsidian/workspace (8).json b/.obsidian/workspace (8).json new file mode 100644 index 0000000..01817a9 --- /dev/null +++ b/.obsidian/workspace (8).json @@ -0,0 +1,197 @@ +{ + "main": { + "id": "22b54cdfe598d436", + "type": "split", + "children": [ + { + "id": "fd025cb4a7192636", + "type": "tabs", + "children": [ + { + "id": "7c0c92b3108c5e91", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "PEP8.md", + "mode": "source", + "source": false + } + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "ae9ce61e34e2a30b", + "type": "split", + "children": [ + { + "id": "272888dc6bd23a63", + "type": "tabs", + "children": [ + { + "id": "3d69f2baa3152c95", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "byModifiedTimeReverse" + } + } + }, + { + "id": "304816efb0ab556e", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + } + } + }, + { + "id": "e88a968f3bbf9578", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {} + } + } + ] + } + ], + "direction": "horizontal", + "width": 211.5 + }, + "right": { + "id": "b155a938b8a7e919", + "type": "split", + "children": [ + { + "id": "3f12dfc7ff91ce6e", + "type": "tabs", + "children": [ + { + "id": "a9fe2861a649f07f", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "PEP8.md", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "f5f51f424be955ad", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "PEP8.md", + "linksCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "daedec170123f202", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true + } + } + }, + { + "id": "ab86f8ad95ae5f4c", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "PEP8.md" + } + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Меню быстрого перехода": false, + "graph:Граф": false, + "canvas:Создать новый холст": false, + "daily-notes:Сегодняшняя заметка": false, + "templates:Вставить шаблон": false, + "command-palette:Открыть палитру команд": false + } + }, + "active": "7c0c92b3108c5e91", + "lastOpenFiles": [ + "VIM.md", + "RANGER.md", + "GIT.md", + "PEP8.md", + "PEP8", + "PYTHON/ФУНКЦИИ.md", + "LINUX/CHMOD.md", + "PYTHON/SET, DICT.md", + "PYTHON/TUPLE.md", + "PYTHON/LIST.md", + "Без названия.md", + "PYTHON", + "SHELL команды.md", + "LINUX/ENVIRONMENT.md", + "YANDEX_DISK.md", + "Образец.md", + "Installing Applications.md", + "LINUX/WIREGUARD.md", + "LINUX/SSH.md", + "LINUX/HTOP.md", + "LINUX/DIFFERENT.md", + "LINUX/BEGIN.md", + "LINUX/CRON.md", + "LINUX/RSYNC.md", + "LINUX/SAMBA.md", + "LINUX/NETWORK/IPERF3.md", + "LINUX/CHRONY.md", + "LINUX/NETWORK/NETPLAN.md", + "LINUX/NETWORK", + "files/Aygo-топл_бак_и_горловина.jpg", + "files/Aygo-передний_бампер.jpg", + "files/Aygo-передн_подвеска.jpg", + "files/Aygo-задняя_подвеска.jpg", + "files/Aygo-задний_бампер.jpg", + "files/Aygo-зад_полы_и_панель.jpg", + "files/2016-07-21 21Aygo-глушитель.jpg", + "files/ae81365154ba5166845a7a9171d2248b.png", + "files/6cda81091bcc9f99ad224bd5c5426f8e.png", + "files/4a2b1a7f1e1bee9a26d7885b1d5e0f8c.png", + "templates", + "REPORTS", + "Без названия 1", + "files", + "LINUX/DISK", + "LINUX" + ] +} \ No newline at end of file diff --git a/.obsidian/workspace (9).json b/.obsidian/workspace (9).json new file mode 100644 index 0000000..a1ec852 --- /dev/null +++ b/.obsidian/workspace (9).json @@ -0,0 +1,197 @@ +{ + "main": { + "id": "22b54cdfe598d436", + "type": "split", + "children": [ + { + "id": "fd025cb4a7192636", + "type": "tabs", + "children": [ + { + "id": "7c0c92b3108c5e91", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "PEP8.md", + "mode": "source", + "source": false + } + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "ae9ce61e34e2a30b", + "type": "split", + "children": [ + { + "id": "272888dc6bd23a63", + "type": "tabs", + "children": [ + { + "id": "3d69f2baa3152c95", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "byModifiedTimeReverse" + } + } + }, + { + "id": "304816efb0ab556e", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + } + } + }, + { + "id": "e88a968f3bbf9578", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {} + } + } + ] + } + ], + "direction": "horizontal", + "width": 211.5 + }, + "right": { + "id": "b155a938b8a7e919", + "type": "split", + "children": [ + { + "id": "3f12dfc7ff91ce6e", + "type": "tabs", + "children": [ + { + "id": "a9fe2861a649f07f", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "PEP8.md", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "f5f51f424be955ad", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "PEP8.md", + "linksCollapsed": false, + "unlinkedCollapsed": true + } + } + }, + { + "id": "daedec170123f202", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true + } + } + }, + { + "id": "ab86f8ad95ae5f4c", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "PEP8.md" + } + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Меню быстрого перехода": false, + "graph:Граф": false, + "canvas:Создать новый холст": false, + "daily-notes:Сегодняшняя заметка": false, + "templates:Вставить шаблон": false, + "command-palette:Открыть палитру команд": false + } + }, + "active": "7c0c92b3108c5e91", + "lastOpenFiles": [ + "PYTHON/PYINSTALLER.md", + "VIM.md", + "RANGER.md", + "GIT.md", + "PEP8.md", + "PEP8", + "PYTHON/ФУНКЦИИ.md", + "LINUX/CHMOD.md", + "PYTHON/SET, DICT.md", + "PYTHON/TUPLE.md", + "PYTHON/LIST.md", + "Без названия.md", + "PYTHON", + "SHELL команды.md", + "LINUX/ENVIRONMENT.md", + "YANDEX_DISK.md", + "Образец.md", + "Installing Applications.md", + "LINUX/WIREGUARD.md", + "LINUX/SSH.md", + "LINUX/HTOP.md", + "LINUX/DIFFERENT.md", + "LINUX/BEGIN.md", + "LINUX/CRON.md", + "LINUX/RSYNC.md", + "LINUX/SAMBA.md", + "LINUX/NETWORK/IPERF3.md", + "LINUX/CHRONY.md", + "LINUX/NETWORK", + "files/Aygo-топл_бак_и_горловина.jpg", + "files/Aygo-передний_бампер.jpg", + "files/Aygo-передн_подвеска.jpg", + "files/Aygo-задняя_подвеска.jpg", + "files/Aygo-задний_бампер.jpg", + "files/Aygo-зад_полы_и_панель.jpg", + "files/2016-07-21 21Aygo-глушитель.jpg", + "files/ae81365154ba5166845a7a9171d2248b.png", + "files/6cda81091bcc9f99ad224bd5c5426f8e.png", + "files/4a2b1a7f1e1bee9a26d7885b1d5e0f8c.png", + "templates", + "REPORTS", + "Без названия 1", + "files", + "LINUX/DISK", + "LINUX" + ] +} \ No newline at end of file diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json new file mode 100644 index 0000000..e2451f4 --- /dev/null +++ b/.obsidian/workspace.json @@ -0,0 +1,217 @@ +{ + "main": { + "id": "22b54cdfe598d436", + "type": "split", + "children": [ + { + "id": "1b52c0235b9fa69e", + "type": "tabs", + "children": [ + { + "id": "ee4472cdc91cb298", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "GIT.md", + "mode": "source", + "source": false + }, + "icon": "lucide-file", + "title": "GIT" + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "ae9ce61e34e2a30b", + "type": "split", + "children": [ + { + "id": "272888dc6bd23a63", + "type": "tabs", + "children": [ + { + "id": "3d69f2baa3152c95", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical", + "autoReveal": false + }, + "icon": "lucide-folder-closed", + "title": "Файловый менеджер" + } + }, + { + "id": "304816efb0ab556e", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + }, + "icon": "lucide-search", + "title": "Поиск" + } + }, + { + "id": "e88a968f3bbf9578", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {}, + "icon": "lucide-bookmark", + "title": "Закладки" + } + } + ] + } + ], + "direction": "horizontal", + "width": 263.5 + }, + "right": { + "id": "b155a938b8a7e919", + "type": "split", + "children": [ + { + "id": "3f12dfc7ff91ce6e", + "type": "tabs", + "children": [ + { + "id": "a9fe2861a649f07f", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "GIT.md", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-coming-in", + "title": "Обратные ссылки для GIT" + } + }, + { + "id": "f5f51f424be955ad", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "GIT.md", + "linksCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-going-out", + "title": "Исходящие ссылки из GIT" + } + }, + { + "id": "daedec170123f202", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true + }, + "icon": "lucide-tags", + "title": "Теги" + } + }, + { + "id": "ab86f8ad95ae5f4c", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "GIT.md" + }, + "icon": "lucide-list", + "title": "Структура GIT" + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "bases:Создать новую базу": false, + "switcher:Меню быстрого перехода": false, + "graph:Граф": false, + "canvas:Создать новый холст": false, + "daily-notes:Сегодняшняя заметка": false, + "templates:Вставить шаблон": false, + "command-palette:Открыть палитру команд": false + } + }, + "active": "ee4472cdc91cb298", + "lastOpenFiles": [ + "GIT_new.md", + "GIT_new-2.md", + "LINUX/TMUX.md", + "GIT.md", + "GIT_new-2", + "LINUX/MC.md", + "LINUX/WIREGUARD.md", + "LINUX/VSFTPD.md", + "LINUX/USER.md", + "LINUX/BASH_script/Обновление списка пакетов и установка обновлений.md", + "LINUX/BASH_script/15 однострочных bash-скриптов.md", + "LINUX/AmneziaWG.md", + "LINUX/BASH_script/BASH. Разное.md", + "LINUX/ROOT.md", + "LINUX/SSH.md", + "LINUX/UFW.md", + "LINUX/HOME_SERVER/Очистка системы.md", + "LINUX/HOME_SERVER/5 малоизвестных команд для Linux.md", + "LINUX/BASH_script/BASH. Часть 2.md", + "LINUX/BASH_script/BASH. Часть 1.md", + "LINUX/BASH_script/Скрипты для админов.md", + "LINUX/NETWORK/NETWORK.md", + "files/СкриптыДляАдмина.jpg", + "LINUX/DISK/DF, DU.md", + "PYTHON/BEGIN + PIP.md", + "PYTHON/HTTP.md", + "LINUX/MOSQUITTO.md", + "PYTHON/STRING.md", + "files/6cda81091bcc9f99ad224bd5c5426f8e.png", + "files/32de918111f7fed3e8183b4b9148286b.png", + "files/Pasted image 20250621003225.png", + "files/Pasted image 20250621003213.png", + "files/2025-06-20_22-34-47.png", + "files/telegram-cloud-photo-size-2-5425027665104058215-x.jpg", + "files/Снимок экрана 2025-05-14 в 00.38.05.png", + "files/Снимок экрана 2025-05-14 в 00.35.18.png", + "LINUX/HOME_SERVER", + "files/telegram-cloud-photo-size-2-5230966980947866123-y.jpg", + "files/TG_bot_VPN-main/utils/misc/__init__.py", + "files/TG_bot_VPN-main/keyboards/reply/handlers_reply.py", + "files/TG_bot_VPN-main/keyboards/reply/__init__.py", + "files/TG_bot_VPN-main/keyboards/inline/subscribed.py", + "files/TG_bot_VPN-main/keyboards/inline/servers.py", + "files/TG_bot_VPN-main/keyboards/inline/app_buttons.py", + "files/TG_bot_VPN-main/keyboards/inline/admin_buttons.py", + "files/TG_bot_VPN-main/keyboards/inline/__init__.py", + "Без названия.canvas" + ] +} \ No newline at end of file diff --git a/AUTO/AUTO.md b/AUTO/AUTO.md new file mode 100644 index 0000000..8c8ce8f --- /dev/null +++ b/AUTO/AUTO.md @@ -0,0 +1,2 @@ + +![[Снимок экрана 2023-10-19 в 09.53.43.png]]![[Снимок экрана 2023-10-19 в 09.53.24.png]] \ No newline at end of file diff --git a/AUTO/TOYOTA AYGO.md b/AUTO/TOYOTA AYGO.md new file mode 100644 index 0000000..a469309 --- /dev/null +++ b/AUTO/TOYOTA AYGO.md @@ -0,0 +1,24 @@ + + +![[2016-07-21 21Aygo-глушитель.jpg]] + + + + + +![[Aygo-задний_бампер.jpg]] + + + + + +![[Aygo-передн_подвеска.jpg]] + + + + + +![[Aygo-топл_бак_и_горловина.jpg]] + + + diff --git a/DaVinciResolve/Davinci Resolve 19.md b/DaVinciResolve/Davinci Resolve 19.md new file mode 100644 index 0000000..0ef5717 --- /dev/null +++ b/DaVinciResolve/Davinci Resolve 19.md @@ -0,0 +1,55 @@ +https://github.com/leinardi/JDInstaller/blob/master/roles/davinci_resolve/DaVinciResolve.md + +Я создал скрипт Ansible, который полностью автоматизирует процесс установки в Ubuntu 24.04. + +Вот что делает этот скрипт: + +- В отличие от других решений, **он не устанавливает старые версии системных зависимостей .** +- **Контейнеризация** (вроде `distrorun`) не требуется. +- Скрипт **извлекает файлы AppImage** из официального установщика и заменяет устаревшие библиотеки, создавая **символические ссылки на правильные версии** библиотек из вашей системы Ubuntu 24.04. +- Он загружает **официальный установщик** с сайта DaVinci Resolve. +- Устанавливает все в стандартный `/opt/`каталог. +- **Настраивает правила udev** для панели DaVinci и аналогичных периферийных устройств USB. +- Устанавливает необходимые **системные иконки** и **файлы MIME** . +- **Автоматически определяет ваш графический процессор** (AMD, Intel или Nvidia) и устанавливает соответствующие библиотеки OpenCL. + +Чтобы DaVinci Resolve 19 работал в Ubuntu 24.04, выполните следующие действия: + +1. Установите Git и Make, если они еще не установлены: + + ``` + sudo apt install git make + ``` + +2. Клонируйте репозиторий: + + ``` + git clone https://github.com/leinardi/JDInstaller.git + ``` + +3. Перейдите в каталог репозитория: + + ``` + cd JDInstaller + ``` + +4. Проверьте ветку DaVinci Resolve (она по умолчанию отключена в основном скрипте): + + ``` + git checkout davinci + ``` + +5. Запустите установку: + + ``` + make install TAGS=davinci_resolve + ``` + + +Это должно полностью автоматизировать процесс и заставить DaVinci Resolve работать на Ubuntu 24.04 без необходимости ручного понижения пакетов или контейнеров. Полный скрипт можно найти здесь: +[https://github.com/leinardi/JDInstaller](https://github.com/leinardi/JDInstaller) + +DaVinci Resolve, работающий в Ubuntu 24.04 на оборудовании Intel: [![введите описание изображения здесь](https://i.sstatic.net/3KT2Dunl.png)](https://i.sstatic.net/3KT2Dunl.png) + +DaVinci Resolve, работающий в Ubuntu 24.04 на оборудовании AMD: [![введите описание изображения здесь](https://i.sstatic.net/CbybXPPr.png)](https://i.sstatic.net/CbybXPPr.png) + diff --git a/DaVinciResolve/HEVC.md b/DaVinciResolve/HEVC.md new file mode 100644 index 0000000..509ce69 --- /dev/null +++ b/DaVinciResolve/HEVC.md @@ -0,0 +1,38 @@ +# Как установить кодек HEVC/H.265 на Windows 10 + +Простой + +[Настольные компьютеры](https://habr.com/ru/hubs/desktops/)[Работа с 3D-графикой*](https://habr.com/ru/hubs/3d_graphics/)[Работа с векторной графикой*](https://habr.com/ru/hubs/vector_graphics/)[Работа с видео*](https://habr.com/ru/hubs/video/)[Софт](https://habr.com/ru/hubs/soft/) + +Ожидает приглашения + +Доброго времени суток, товарищи, при монтировании видео в одной из популярных программ для обработки видео, я столкнулся с ошибкой "Media Offline". Проблема касалась только видеофайлов, которые были закодированы при помощи кодека H.265 или HEVC. Удалось выяснить, что данный формат сжатия видео не установлен у меня, также как и большинства пользователей ОС Windows 10/11. Процесс установки показался мне нетривиальным, поэтому инструкцию, **актуальную на 19.05.2024** я оставлю здесь. + +1. Переходим по ссылке [https://www.free-codecs.com/guides/how_to_download_hevc_video_extension_for_free.htm](https://www.free-codecs.com/guides/how_to_download_hevc_video_extension_for_free.htm) и бегло ознакамливаемся с содержимым. На данном ресурсе обновляются статьи по установке кодеков. + +2. Переходим на [Adguard](https://store.rg-adguard.net/) и в свободное поле вставляем ссылку на страницу HEVC кодека из магазина Microsoft. (Вот сама ссылка для копирования: https://www.microsoft.com/en-us/p/hevc-video-extensions-from-device-manufacturer/9n4wgh0z6vhq). Нажимаем на галочку и получаем 2 ссылки. + +![Окно на Adguard](https://habrastorage.org/r/w1560/getpro/habr/upload_files/e99/5d4/179/e995d4179cc14158e146a41bd0769a48.png "Окно на Adguard") + +Окно на Adguard + +3. Теперь необходимо скачать файл, который заканчивается на .appxbundle и весит 7MB. Просто так сделать это у Вас не получится, поэтому применим небольшую хитрость, которой воспользовался я. Наводим курсор на ссылку, нажимаем правую кнопку мыши и выбираем пункт "Открыть ссылку в новой вкладке". Далее, в открывшейся вкладке просто обновляем страницу нажав на F5 или на символ обновления страницы в верхнем левом углу. Файл начнет загружаться. Возможно необходимо будет подтвердить скачивание непроверенного файла. + +> Стоит отметить, что я использую браузер Google Chrome, в другом браузере данный финт может не сработать. + +4. Теперь переходим в проводник (Win + E) -> загрузки и видим наш установочный файл. (Советую его сразу переименовать на 123 или что-то подобное, это упростит установку). Установить его просто кликнув по нему не выйдет, поэтому немного поиграем с PowerShell. +Нажимаем Win + X и выбираем пункт Windows PowerShell (администратор). + +Убедитесь, что установка из непроверенных источников разрешена: + +- Выполните следующую команду для проверки состояния: + Get-ExecutionPolicy + +- Если возвращенный результат — «Restricted», измените его на менее ограничительный режим, выполнив: + Set-ExecutionPolicy Bypass -Scope Process + + +Теперь начнём установку, выполнив следующую команду: Add-AppxPackage .\YourAppName.appxbundle +Не забудьте изменить "YourAppName" на имя вашего файла, которое я советовал поменять. + +5. Поздравляю, нам осталось только перезагрузить компьютер и радоваться возможности работать с HEVC. \ No newline at end of file diff --git "a/DaVinciResolve/\320\223\320\276\321\200\321\217\321\207\320\270\320\265 \320\272\320\273\320\260\320\262\320\270\321\210\320\270.md" "b/DaVinciResolve/\320\223\320\276\321\200\321\217\321\207\320\270\320\265 \320\272\320\273\320\260\320\262\320\270\321\210\320\270.md" new file mode 100644 index 0000000..cad9db6 --- /dev/null +++ "b/DaVinciResolve/\320\223\320\276\321\200\321\217\321\207\320\270\320\265 \320\272\320\273\320\260\320\262\320\270\321\210\320\270.md" @@ -0,0 +1,55 @@ +============================= +### Настройки интерфейса Davinci для ускорения монтажа. +Активируем притягивание плейхэда +Устанавливаем подходящее разрешение воспроизведения тайм линии +A — selection mode, инструмент стрелочка +B — blade mode, инструмент лезвие +CMD/CTRL + B — разрезать по указателю воспроизведения +T — trim edit mode +↑ ↓ — переместить курсор к началу следующего или предыдущего клипа +← → — переместить курсор на один кадр +SHIFT + ← → переместить курсор на 1 секунду +SHIFT + [ ] обрезать по указателю воспроизведения слева или справа +CMD/CTRL + SHIFT + [ ] обрезать по указателю воспроизведения слева или справа со сдвигом +SHIFT + BACKSPACE — удалить клип со сдвигом +DEL — удалить клип со сдвигом +Y — выделить все клипы справа от курсора на основном треке +ALT/OPT + Y — выбрать все клипы справа от курсора на всех треках +CMD/CTRL + Y — выбрать все клипы слева от курсора на основном треке +ALT/OPT +CMD/CTRL + Y — выбрать все клипы слева от курсора на всех треках +CMD/CTRL + X — вырезать клип +CMD/CTRL + SHIFT + X — вырезать клип со сдвигом +CMD/CTRL + SHIFT + V — вставить клип со сдвигом +ALT/OPT + V — вставить атрибуты +ALT/OPT + 1/2/3... — смена камеры в мультикамерной секвенции + +============================= +### Моя раскладка по горячим клавишам:   + +I \ O — Входная и выходная точка на клипе в Media pool, на таймлайне и окне превью   + +Alt + X — отмена входной и выходной точки на клипе   + +Q \ W — подрезает начало и конец клипа на таймлайне   + +D — Razor tool разрезает клип на 2 части   + +2 \ 3 — просмотр вперед и назад со скоростью X2   + +P — полноэкранный режим   + +R — создание прокси   + +Shift + D — выделить все клипы на таймлайне справа   + +N — Вкл\выкл Snapping (магнит) для перемещения клипов на таймлайне с привязкой к другим клипам   + +B — Вкл\выкл один клип на таймлайне   + +1;2;3;4;5;6 num pad — смена цвета клипа   + +T — trim edit mode   + +M — маркер + +============================= \ No newline at end of file diff --git "a/DaVinciResolve/\320\236\320\237\320\242\320\230\320\234\320\230\320\227\320\220\320\246\320\230\320\257.md" "b/DaVinciResolve/\320\236\320\237\320\242\320\230\320\234\320\230\320\227\320\220\320\246\320\230\320\257.md" new file mode 100644 index 0000000..ac15eba --- /dev/null +++ "b/DaVinciResolve/\320\236\320\237\320\242\320\230\320\234\320\230\320\227\320\220\320\246\320\230\320\257.md" @@ -0,0 +1,19 @@ +============================= + +PROXY RESOLUTION -> PLAYBACK -> TIMELINE PROXY RESOLUTION = Half (Quarter) + +DAVINCI RESOLVE -> PREFERENCES -> USERS -> Playback Settings -> +Performance mode = Automatic +v = Hide UI overlays +v = Minimize interface updates during playback + +PROXY MEDIA -> выбираем медиа -> прав кн мыши = Generate Proxy Media +RENDER CACHE -> Playback -> Render Cache = Smart + +============================= + +Preferences -> Use vertical resolution (вертикальное видео) +Video -> Smart Reframe -> квадратик с точкой -> Reference Point (выбираем и наводим) - Re + + + diff --git "a/ETHERNET 1Gbit - \320\272\320\260\320\261\320\265\320\273\321\214.md" "b/ETHERNET 1Gbit - \320\272\320\260\320\261\320\265\320\273\321\214.md" new file mode 100644 index 0000000..0d48d69 --- /dev/null +++ "b/ETHERNET 1Gbit - \320\272\320\260\320\261\320\265\320\273\321\214.md" @@ -0,0 +1,2 @@ +![[Снимок экрана 2024-11-01 в 01.39.59.png]] + diff --git a/GIT.md b/GIT.md new file mode 100644 index 0000000..be92c2a --- /dev/null +++ b/GIT.md @@ -0,0 +1,252 @@ +![[GIT_шпаргалка.jpg]] + +```sh +sudo apt install git +git config --global --edit +git config --global user.name "Name_Name" +git config --global user.email email@example.com +git config --list + +# Создание "своего чистого" репозитория: +git init --bare ~/git/RadSens.git + +git clone vlapa@178.20.46.157:~/git/...... +# По умолчанию git clone загружает всю историю изменений, что может быть медленно и требовать много места. Если история не нужна, используйте: +git clone --depth=1 + +git commit -a -m + -m "text_commit" + -a (заменяет git add) + --amend - заменить/исправить последний commit + +git init +git add . # добавить все файлы +git commit -m "first commit" +git branch -M main +git remote add origin vlapa@178.20.46.157:~/git/...... +git push -u origin main + +git remote set-url origin new.git.url/here + +git@89.110.92.137:/home/git/my/esp8266/8266-clock_ws2812.git + +====================== +======= FLENOV ======= +====================== + +git checkout -b имяветки база +# git checkout говорит, что мы должны переключиться на репозиторий. Ключик -b говорит, что нужно не просто переключиться, а создать новую ветку с именем =имяветки=. +# После имени бренча через пробел идет база - по документации необязательный параметр - от какой ветки вы хотите создать свою. Я рекомендую всегда указывать этот параметр. Если вы не укажите базу, то в качестве нее будет выступать текущая ветка. Чаще всего ветки нужно создавать от основной master, а не от текущей. Если только вы явно не хотите создать свою ветку от какой-то другой, команда создания будет + +git checkout -b имяветки origin/master + +# Стоп, еще немного о именовании. В данном случае я указываю origin/master и это удаленный ствол. На это указывает слово origin. Если мы создаем новую ветку fixdone/345321, то это локальная копия на нашем диске. Удаленная копия на сервере git в репозитории будет иметь имя origin/fixdone/345321. Когда мы создаем новые ветки, то желательно создавать от удаленной версии master, но у нас ее пока нет, поэтому если попытаться выполнить такую команду, то она завершиться ошибкой. + +# Да, я рекомендую всегда использовать в качестве базы удаленную версию origin/master, которая указывает на версию на сервере, но если ее нет, то придется использовать локальную – просто master. Мы пока создали только локальный репозиторий. Пока нет удаленного, придется использовать локальный. Как только появиться удаленный, всегда используйте его. О удаленных ветках мы поговорим в следующий раз. + +# Итак, пока нет удаленного мастера, давайте создадим новую ветку от локального: +git checkout -b initialwork master + +# Я назвал ветку initialwork. В результате выполнения команды вы должны увидеть что-то типа: +Switched to a new branch 'initialwork' + +# Теперь мы находимся в ветке, где мы можем исправлять баг или работать над новым функционалом. Давайте создадим файл index.html. Вы можете воспользоваться для этого любым текстовым редактором. + +git add i* # все файлы начинающиеся с =i= +git add -u # все измененные файлы + +git checkout master +git merge initialwork + +git branch # отображает все ветки созданные локально + -M # переименовать ветку + -a # все ветки: локальные и удаленные + +echo "# demo" >> README.md +git init +git add README.md +git commit -m "first commit" +# git branch -M main +git remote add origin vlapa@178.20.46.157:~/git/name.git +git push -u origin master # main + +# Работа на другом компьютере: +git clone vlapa@178.20.46.157:~/git/name.git + +# или: +git init +git remote add origin vlapa@178.20.46.157:~/git/name.git +# получить последние изменения: +git fetch + +git checkout secondone +# изменеяем файл, далее: +git diff # file_name +git add -u +git commit -m "commit" +git push origin secondone +# теперь на сервере есть 2 ветки + +# на еще одном компьютере: +git checkout -b origin/secondone +git merge origin/secondone + + + + +============== +git show +git commit --amend # дополнение и изменение коммита +git commit --amend -m "новое название" # переименовать +git checkout file_name # откатить последние изменения +git reset --hard # сбросить все последние изменения +git status + # untracked - неотслеживаемый + # modified - в рабочей директории есть более новая версия по сравнению с хранящейся в HEAD или в области подготовленных файлов (изменения не находятся в следующем коммите). + # staget - в рабочей директории и области подготовленных файлов есть более новая версия по сравнению с хранящейся в HEAD (готов к коммиту). + # Без изменений — одна версия файла во всех разделах, т. е. в последнем коммите содержится актуальная версия. + +# Файл может быть одновременно в состоянии «изменён» и «подготовлен», если версия в рабочей директории новее, чем в области подготовленных файлов, которая в свою очередь новее версии в HEAD. + +# Мы можем использовать опцию -s для команды git status, чтобы получить более компактный вывод (по строке на файл). +# Если файл не отслеживается, то будет выведено ??; если он был изменён, то его имя будет красным, а если подготовлен — зелёным. + +# Чтобы посмотреть сами изменения, а не изменённые файлы, можно использовать следующие команды: + +git diff  +# — посмотреть что добавилось или сравнение рабочей директории с областью подготовленных файлов; + +git diff --staged +# — сравнение области подготовленных файлов с HEAD. + +# Если использовать аргумент <файл/папка>, то diff покажет изменения только для указанных файлов/папок, например git diff src/. + +.gitignore +# Чтобы отметить файлы, которые мы хотим игнорировать, можно использовать шаблоны поиска (считайте их упрощёнными регулярными выражениями): + +# /... — позволяет избежать рекурсивности — соответствует файлам только в текущей директории; +# .../ — соответствует всем файлам в указанной директории; +# *... — соответствует всем файлам с указанным окончанием; +# ! — игнорирование файлов, попадающих под указанный шаблон; +# [...] — соответствует любому символу из указанных в квадратных скобках; +# ? — соответствует любому символу; +# /**/ — соответствует вложенным директориям, например a/**/d соответствует a/d, a/b/d, a/b/c/d и т. д. + +# Мы даже можем использовать шаблоны поиска при указании файла/папки в других командах. Например:  +git add src/*.css +# добавит все файлы .css в папке src. + +git log # - можно передать разные опции: + --oneline # короткий лог + -p # показывает изменения в каждом коммите; + --stat # показывает сокращённую статистику для коммитов, например изменённые файлы и количество добавленных/удалённых строк в каждом их них; + -n # показывает n последних коммитов; + --since=___ и --until=___ # позволяет отфильтровать коммиты по промежутку времени, например --since="2019-01-01" покажет коммиты с 1 января 2019 года; + --pretty # позволяет указать формат логов (например, --pretty=oneline), также можно использовать --pretty=format для большей кастомизации, например --pretty=format:"%h %s"; + --grep и -S # фильтруют коммиты с сообщениями/изменениями кода, которые содержат указанную строку, например, git log -S имя_функции позволяет посмотреть добавление/удаление функции; + --no-merges # пропускает коммиты со слиянием веток; +ветка1..ветка2 # позволяет посмотреть, какие коммиты из ветки 2 не находятся в ветке 1 (полезно при слиянии веток). Например, `git log master..test` покажет, каких коммитов из ветки test нет в master (о ветках поговорим чуть позже). + --left-right # ветка1...ветка2 показывает коммиты, которые есть либо в ветке 1, либо в ветке 2, но не в обеих; знак < обозначает коммиты из ветка1, а > — из ветка2. Обратите внимание: используется три точки, а не две; + -L # принимает аргумент начало,конец:файл или :функция:файл и показывает историю изменений переданного набора строк или функции в файле. + +``` + +# Свой GIT +```sh +# Server: +sudo apt install git +sudo useradd git +sudo passwd git +ssh-keygen # - ??? +mkdir repo1.git +git init --bare +git init --bare ~/git/repo1.git +#------------------------ +# Client: +sudo apt install git +ssh-copy-id git@server_adresses + +git config --global --edit +git config --global user.name "Name_Name" +git config --global _user.email email@example.com +git config --global core.editor "vim" # устанавливаем редактор по умолчанию +git config --list + +mkdir /home/user/repo1 +cd /home/user/repo +git init +git add . +git commit -m "commit1" +git remote add origin git@addresses_server:/home/git/repo1.git +git push -u origin master + +#------------------------- +# Создаем свою ветку: +git branch # смотрим ветку +git branch -a # - смотрим все ветки +git branch new_branch # - создаем ветку new_branch +git checkout -b new_branch # - так тоже можно создать +git checkout new_branch # - переключаемся на нее +git diff # - посмотреть изменения +git status + +vim .gitignore # добавляем в игнор все лишнее +# .idea/ +# .vscode/ +# .pio +git add . +git commit -m "modified .gitignore" +git reset HEAD # - убрать файл из индекса +git push --set-upstream origin new_branch + # установить ветку new_branch как origin + # указывается только при первом PUSH + +git checkout # - переход на другой commit + +# Сливаемся: +git checkout master # переключаемся на ветку master +git checkout -b new-branch # создаём новую ветку +git merge new_branch # сливаемся: master/new_branch +git branch -d new_branch +git branch -r +git branch -d -r origin/new_branch +git push --set-upstream origin new-branch + +# Удалить файл удаленно и локально: +git branch -D new-branch # удаляем лишнюю ветку локально +git rm 'file name' +git rm -r 'folder name' +git commit -m 'message' +git push -u origin branch + +# Удалить файл только удаленно: +git push --delete origin new-branch # удаляем ветку в репозитории +git rm --cached 'file name' +git rm -r 'folder name' +git commit -m'message' +git push -u origin branch + +#===================================== + +git fetch # - загружает коммиты, файлы и ссылки из удаленного репозитория в ваш локальный репозиторий. + +# Извлеките данные с помощью команды fetch, если хотите увидеть, над чем работают остальные. Эта команда похожа на команду `svn update` и позволяет увидеть прогресс в центральном репозитории, но при этом не вынуждает вас выполнять слияние изменений с вашим репозиторием. + +git pull # - слить локальную и удаленную версию на локальной машине (на самом деле представляет собой комбинацию двух других команд: git fetch и git merge) + +git log --oneline # короткий LOG + +git reset --... +# -- soft HEAD~ - на 1 commit назад без удаления последних изменений +# -- mixed HEAD~ - на 1 commit назад без удаления последних изменений и не внесенными предпоследними изменениями +# -- hard HEAD~ - на 1 commit назад с удалением ВСЕХ последних изменений + +git rm file.name # - удаление и сразу git add . (если просто самому удалить файл из папки - то нужно самому git add .) + +git mv file.name new_file.name # - переименование и сразу git add . (если просто самому переименовать файл из папки - то нужно самому git add .) + +git restore file.name # - откат всех последних изменений до commit +--staged # убирает файл из зоны индекса + +``` + diff --git a/GIT_new-2.md b/GIT_new-2.md new file mode 100644 index 0000000..b7a126b --- /dev/null +++ b/GIT_new-2.md @@ -0,0 +1,218 @@ +**Шаг 1: Создайте пользователя для репозиториев (рекомендуется)** + +Для безопасности лучше не использовать вашего основного пользователя или `root`. + +bash + +# На сервере +sudo adduser git +sudo su - git +mkdir repos + +**Шаг 2: Создайте «голый» репозиторий для вашего проекта** + +bash + +#Находясь под пользователем git, в папке repos +mkdir my-project.git +cd my-project.git +git init --bare + +Поздравляю! Серверная часть готова. Путь к репозиторию: `/home/git/repos/my-project.git` + +**Шаг 3: Настройте доступ по SSH (самый простой способ)** + +Сгенерируйте SSH-ключ на вашем рабочем компьютере (если еще нет): + +bash + +#На вашем компьютере (Windows, Mac, Linux) +ssh-keygen -t ed25519 -C "your_email@example.com" + +Скопируйте публичный ключ на сервер: + +bash + +# На вашем компьютере +ssh-copy-id git@IP-вашего-сервера + +**Шаг 4: Используйте репозиторий** + +Теперь вы можете работать с ним как с удаленным репозиторием, например, GitHub. + +**Вариант А: Создать новый проект и запушить его** + +bash + +# На вашем компьютере +mkdir my-local-project +cd my-local-project +git init +echo "# Мой проект" > README.md +git add README.md +git commit -m "Первый коммит" +git remote add origin git@IP-вашего-сервера:/home/git/repos/my-project.git +git push -u origin master + +**Вариант Б: Склонировать существующий пустой репозиторий** + +bash + +git clone git@IP-вашего-сервера:/home/git/repos/my-project.git + +### 🚀 Полезные улучшения для «голого» Git + +Чтобы работа была комфортнее, добавьте несколько важных вещей. + +**1. Защита от перезаписи (очень важно для домашнего сервера!)** + +Добавьте эту настройку в «голый» репозиторий на сервере, чтобы случайно не затереть чужую работу (или свою же, но с другого компьютера): + +bash + +# На сервере, в папке /home/git/repos/my-project.git +git config receive.denyNonFastForwards true + +**2. Упростите путь к репозиториям с помощью `~/.ssh/config`** + +На вашем _локальном_ компьютере отредактируйте файл `~/.ssh/config` (создайте, если нет): + +text + +Host home-server + HostName IP-вашего-сервера + User git + Port 22 + +Теперь вместо `git@IP-вашего-сервера:/home/git/repos/my-project.git` вы можете писать: + +bash + +git remote add origin home-server:repos/my-project.git + +Намного короче и удобнее. + +**3. Быстрый просмотр содержимого (без клонирования)** + +Иногда нужно просто «посмотреть», какие коммиты и ветки есть на сервере, не клонируя весь репозиторий. Для этого есть команда: + +bash + +# На сервере, в папке с «голым» репозиторием +git log --oneline --all +git branch -a + +**4. Добавьте описание к репозиторию** + +bash + +# На сервере, в папке с «голым» репозиторием +echo "Мой супер-проект для расчетов траектории кота" > description + +Это описание будет видно, например, в GitWeb или при просмотре через `git instaweb`. + +### Как организовать несколько проектов? + +Просто создайте несколько папок в `/home/git/repos/`: + +text + +/home/git/repos/ +├── website.git +├── telegram-bot.git +├── home-automation.git +└── useful-scripts.git + +Каждый из них — это независимый «голый» репозиторий со своей историей. + +### 💾 Бэкапы для «голого» Git + +Здесь всё просто, потому что «голый» репозиторий — это просто обычная папка с файлами. Чтобы сделать бэкап всех ваших репозиториев: + +# На сервере или по SSH +tar -czf git-backup-$(date +%Y%m%d).tar.gz /home/git/repos/ + +А потом скопируйте этот архив на внешний диск или в облако (согласно правилу 3-2-1). +Для автоматизации добавьте в `crontab -e`: + +bash + +0 2 * * * tar -czf /home/git/backups/git-$(date +\%Y\%m\%d).tar.gz /home/git/repos/ + + +### Итог: когда выбирать «голый» Git? + +«Голый» Git идеален, если: +- Вы единственный разработчик (или команда из 2-3 доверенных человек) +- Вам нужен просто надежный «склад» для кода +- Сервер слабый (например, старенький Raspberry Pi или виртуалка с 256 MB RAM) +- Вы любите минимализм и контроль через командную строку + +Если позже захотите веб-интерфейс, вы всегда можете поверх этих же «голых» репозиториев поставить **cgit** (очень легкий) или просто открыть к ним доступ через **GitWeb**, не меняя саму структуру хранения. + + +### Важное замечание о бэкапах Git-репозиториев + +Для бэкапа "голых" Git-репозиториев есть даже более правильный способ, чем `tar`: + +bash + +# Клонируйте репозиторий с зеркалированием (делает полную копию) +git clone --mirror ~/repos/my.git /backups/my-backup.git +# Или для всех репозиториев +for repo in ~/repos/*.git; do + git clone --mirror "$repo" "/backups/$(basename $repo)" +done + +Этот метод лучше, потому что: +- Сохраняет все ссылки и конфигурации +- Позволяет проверить целостность +- Легче восстановить (`git clone --mirror /backups/my-backup.git`) + + +### Оптимальная структура для бэкапов + +Вот как лучше организовать бэкапы на вашем сервере: + +bash + +# На сервере (под пользователем git) +# Создайте структуру +mkdir -p ~/backups/{daily,weekly,monthly} +# Ежедневный бэкап +tar -czf ~/backups/daily/git-backup-$(date +%Y%m%d).tar.gz ~/repos/ +# Очистка старых бэкапов (например, удалять бэкапы старше 30 дней) +find ~/backups/daily -name "git-backup-*.tar.gz" -mtime +30 -delete + +### 🤖 Автоматизируйте бэкап с cron + +Добавьте в crontab, чтобы бэкап делался автоматически каждую ночь: + +bash + +# Редактируем crontab пользователя git +crontab -e +# Добавьте эту строку (делает бэкап каждый день в 2 часа ночи) +0 2 * * * tar -czf /home/git/backups/daily/git-backup-$(date +\%Y\%m\%d).tar.gz /home/git/repos/ && find /home/git/backups/daily -name "git-backup-*.tar.gz" -mtime +30 -delete + +### 📥 Как восстановиться из бэкапа? +Если что-то пойдет не так: + +# На сервере +cd ~/backups +# Распакуйте бэкап (замените дату на вашу) +tar -xzf git-backup-20260613.tar.gz -C / +# Или если хотите посмотреть содержимое бэкапа без распаковки +tar -tzf git-backup-20260613.tar.gz | head -20 + +### 💡 Важное дополнение + +Не забудьте про **правило 3-2-1**: +- У вас есть оригинал (на сервере) ✅ +- У вас есть бэкап (там же на сервере) ✅ +- **Скопируйте этот бэкап на внешний диск или в облако** (например, на ваш YandexDisk или Google Drive) ❌ пока не сделано + +# На вашем локальном компьютере (Windows) +scp git@v2670762:~/backups/git-backup-*.tar.gz "C:\Users\Villa-PC\YandexDisk\Backups\" + + diff --git a/GIT_new.md b/GIT_new.md new file mode 100644 index 0000000..b93ffa1 --- /dev/null +++ b/GIT_new.md @@ -0,0 +1,165 @@ +```sh +#========================================== +# Полный список подкоманд: +git help --all + +# Установка редактора по умолчанию: +export GIT_EDITOR=vim + +# Настройка автора фиксации: +git config --global user.name "my_name" +git config --global user.email "my_name@mail.com" + +-add # добавляет файлы в индекс +-bisect # выполняет бинарный поиск ошибки по истории фиксаций +-branch # выводит, создает, или удаляет ветки +-checkout # проверяет и переключает на ветку +-clone # клонирует репозиторий в новый каталог +-commit # записывает изменения в каталог (фиксация) +-diff # показывает изменения между фиксациями, рабочими деревьями... +-fetch # загружает объекты и ссылки из другого репозитория +-grep # выводит строки, соответствующие шаблону +-init # создает пустой репозиторий git, или переинициализирует существующий +-log # показывает журналы фиксации +-merge # объединяет две, или больше истории разработки +-mv # перемещает, или переименовывает файл, каталог, или символическую ссылку +-pull # получает изменения из удаленного репозитория и объединяет их с локальным, или локальной веткой +-push # загружет изменения из локального репозитория в удаленный +-rebase # строит ровную линию фиксаций +-reset # сбрасывает текущий HEAD в указанное состояние +-rm # удаляет файлы из рабочего дерева и из индекса +-show # показывает различные типы объектов +-status # показывает состояние рабочего дерева +-tag # создает, выводит, удаляет, или проверяет тег объекта, подписанного с GPG + + + +#========================================== +# Создание веток: +git branch name_branch [начальная фиксация] + +# Вывод имен веток в репозитории: +git branch + -r # удаленные ветки + -a # все ветки + +# Вывод детальной информации: +git show-branch + +# Переключение на другую ветку: +git checkout name_branch + +# Объединение изменений в другую ветку: +git checkout -m dev + +# Создание новой ветки и переключение на нее: +git checkout -b dev_new + +#========================================== +# Использование UNIX команды diff: +diff -u cat-1 cat-2 + +# Разница между рабочим каталогом и индексом: +git diff + +# Разница между рабочим каталогом и указанной фиксацией: +git diff фиксация + +# Разница между подготовленными в индексе изиенениями и заданной фиксацией: +git diff --cached фиксация + +#========================================== +# Исследование конфликтов слияния: +git merge alt + +# Когда есть кофликты, рабочая копия каждого проблемного файла добавляется маркерами слияния - маркеры показывают две возможные версии конфликтного участка файла: нужно просто выбрать одну из фраз, удалить маркеры конфликта и выполнить команды: +git add +git commit + +# Отмена, или перезапуск слияния: +# Перед последним git commit: +# команда восстановит рабочий каталог и индекс в состояние предшествующее git merge: +git reset --hard HEAD + +# После завершения слияния (уже есть новая фиксация): +git reset --hard ORIG_HEAD +# перед началом слияния GIT созраняет оригинальную ветку HEAD в ссылке ORIG_HEAD, чтобы можно было выполнить откат слияния + +# Вернуться к исходному состоянию конфликта, чтобы разрешить его снова: +git checkout -m + +#========================================== +# Применение изменений представленных =фиксацией= на текущей ветке: +# не изменяет существующую историю в репозитории, вместо этого она добавляет фиксацию в историю: +git cherry-pick фиксация +# обычно используется чтобы представить определенные фиксации из одной ветки в репозитории в другую ветку +``` +![[Снимок экрана 2025-02-02 в 18.06.09.png]] + +```sh +# Отменяет фиксацию использую хэш этой фиксации +# не изменяет существующую историю в репозитории +# добавляет в историю новую фиксацию: +git revert фиксация +``` + +![[Снимок экрана 2025-02-02 в 18.13.01.png]] +```sh +#========================================== +# Изменение последней фиксации: +git commit --amend + +# Изменение последовательности фиксаций: +git rebase +# требует, как минимум, имени другой ветки, в которую будут перемещены фиксации, по умолчанию перебазируются фиксации из текущей ветки, которых еще нет в целевой ветке +# типично: сохранить серию фиксаций из указанной ветки на последнюю фиксацию ветки =master= +``` +![[Снимок экрана 2025-02-02 в 18.29.43.png]] +![[Снимок экрана 2025-02-02 в 18.30.02.png]] +```sh +# использование git rebase - портирование, ветка topic была портирована в ветку master + +#========================================== +# Если нужно сохранить все и заняться чем-то другим: +git stash save "работаю над текущим проектом" +# занимаемся чем-то другим +..... +git commit -a -m "что-то другое" +git stash pop +# выполнить можно только в чистый рабочий каталог ! +#========================================== +# Запись изменений в головы веток +# показывает транзакции только для одной ссылки за один раз +git reflog show + +# журнал изменений ветки fred: +git reflog fred +# каждая строка-это отдельная транзакция из истории ссылки, нчиная с самого последнего изменения и вплоть до самого первого + +#========================================== +# Создание "чистого" (bare) репозитория: +git init --bare + +# Преобразовать репозиторий в другой чистый клон: +git clone --bare vlapa@178.20.46.157:~/git/name new_name.git + +# добавить удаленную конфигурацию +git remote add origin /путь/к/репозиторию +# команда git remote добавила новый раздел remote с названием origin + +git push origin master +# изменения ветки master отправлены удаленному репозиторию origin + +#========================================== +# Посмотреть список веток в своем репозитории: +git branch -a + +#========================================== +# Загрузка обновлений: +git pull # состоит из двух команд: + -git fetch + -git merge (rebase) - по умолчанию = merge + +#========================================== +``` + diff --git a/LINUX/AmneziaWG.md b/LINUX/AmneziaWG.md new file mode 100644 index 0000000..24e9ffa --- /dev/null +++ b/LINUX/AmneziaWG.md @@ -0,0 +1,67 @@ +AmneziaWG — это форк WireGuard с обфускацией трафика, который помогает обходить DPI-блокировки там, где обычный WireGuard режется [](https://github.com/bivlked/amneziawg-installer/blob/main/README.md)[](https://github.com/bivlked/amneziawg-installer/blob/main/README.en.md). + +Вот **три способа поднять сервер AmneziaWG** (от самого простого к более продвинутому): + +Этот скрипт установит AmneziaWG за пару минут. + +**1. Загрузите и запустите скрипт:** + +```sh +bash + +curl -O https://raw.githubusercontent.com/wiresock/amneziawg-install/main/amneziawg-install.sh +chmod +x amneziawg-install.sh +sudo ./amneziawg-install.sh +``` + +**2. Ответьте на вопросы скрипта:** + +- Укажите публичный IP вашего сервера +- Выберите порт (по умолчанию 51820, но лучше сменить) +- Скрипт сам сгенерирует ключи и параметры обфускации + +**3. Результат:** После установки готовый конфиг клиента появится в  +`~/awg0-client-<имя>.conf` [](https://github.com/wiresock/amneziawg-install/blob/main/README.md) + +### Обновление и управление: +```sh + +bash + +# Добавить или удалить клиентов +sudo ./amneziawg-install.sh + +# Если нужна веб-панель (опционально) +sudo ./amneziawg-web.sh install +``` +### Добавление нового клиента + +Если вам нужен конфиг для дополнительного клиента, выполните: + +bash + +sudo ./amneziawg-install.sh --add-client ИМЯ_КЛИЕНТА + + +### Параметры обфускации + +Если у вас возникли проблемы с подключением, попробуйте изменить параметры в конфиге сервера (`/etc/amnezia/amneziawg/awg0.conf`): + +|Параметр|Назначение|Рекомендуемое значение| +|---|---|---| +|`Jc`|Количество мусорных пакетов|4-10| +|`Jmin`|Мин. размер мусора (байт)|50| +|`Jmax`|Макс. размер мусора (байт)|1000| +|`S1`|Мусор в handshake init|0-150| +|`S2`|Мусор в handshake response|0-150| +|`H1-H4`|Магические заголовки|Уникальные числа [](https://github.com/telemt/telemt/blob/main/docs/Setup_examples/VPS_DOUBLE_HOP.ru.md)[](https://github.com/link0ln/amneziawg-docker-server)| + +### Важные замечания + +- **Все клиенты** должны использовать те же параметры `Jc, Jmin, Jmax`, что и сервер [](https://github.com/link0ln/amneziawg-docker-server) + +- Рекомендуется **сменить порт** с 51820 на что-то нестандартное (например, 8443 или 585) [](https://docs.amnezia.org/ru/documentation/instructions/install-vpn-on-server/#%d0%b4%d1%80%d1%83%d0%b3%d0%b8%d0%b5-%d0%b8%d0%bd%d1%81%d1%82%d1%80%d1%83%d0%ba%d1%86%d0%b8%d0%b8) + +- AmneziaWG совместим с обычным WireGuard, но для обфускации нужны специальные параметры + + diff --git "a/LINUX/BASH -\320\272\321\203\321\200\321\201.md" "b/LINUX/BASH -\320\272\321\203\321\200\321\201.md" new file mode 100644 index 0000000..79d5707 --- /dev/null +++ "b/LINUX/BASH -\320\272\321\203\321\200\321\201.md" @@ -0,0 +1,121 @@ +=========================== +```sh +# СОЗДАЕМ СКРИПТ ДЛЯ СОЗДАНИЯ ПОЛЬЗОВАТЕЛЯ И ГРУППЫ + +#!/bin/bash # шебанг + +set -e # при ошибке скрипт прервется! + +read -p "login: " login # ввод логина +read -p "Password: " -s password # секьюрный ввод пароля! +read -p "path of ssh public key: " -e ssh_pub_key # ввод ключа (выбор через TAB) + +homedir=/home/$login +ssh_dir=$homedir/.ssh + +sudo mkdir -p "$ssh_dir" + +sudo cat "$ssh_pub_path" >> "$ssh_dir/authorized_keys" # дописываем в конец файла +sudo cp -rT /etc/skel $homedir + +sudo useradd -d $homedir -s /bin/bash $login +sudo chown -R $login:$login $homedir + +echo "$login:$password" | sudo chpasswd + +cat /etc/passwd | grep $login + + +=========================== +# Выход из пользователя: +Ctrl+d +exit + +man usermod +# поиск: +/... +/enter # перемещаемся + +cat /var/TabTab +cat /var/log/TabTab +cat /var/log | tail -20 + +=========================== +# SUDO - находятся: +sudo vim /etc/sudoers # НЕ ИЗМЕНЯТЬ !!! + +# Добавлять с проверкой: +sudo vimsudo /etc/sudoers.d/user +# в файл добавляем: +user ALL=(ALL:ALL) ALL +user ALL=(ALL:ALL) NOPASSWD:ALL + +=========================== +# Скрипт +vim ~/script/... + +#!/bin/bash + +# генерируем 3 директории с правами для всех, для группы, для юзера. Сделаем проверку, что права выставлены верно: + +set -e + +login=$1 +homedir=/home/$login +list_name_dir=(secret_for_other secret_for_group secret_for_user) + +ready_dir_list=() + +for name in ${list_name_dir[@]}; # так для массива(списка) +do + mkdir -p $homedir/${login}_${name} + #chown -R $login:$login $homedir + ready_dir_list+=($homedir/${login}_${name}) + if [[ $(echo $name | grep other) ]]; + then + chmod -R 007 $homedir/${login}_${name} + elif [[ $(echo $name | grep group) ]]; + then + chmod -R 070 $homedir/${login}_${name} + elif [[ $(echo $name | grep user) ]]; + then + chmod -R 700 $homedir/${login}_${name} + fi +done + +echo ${list_name_dir[@]} +echo ${ready_dir_list[@]} + +counter=$(ls $homedir/ | wc -l) +while [ $counter -gt 0 ]; +do + counter=$(( $counter - 1 )) + access_rules=$(ls -ld ${ready_dir_list[$counter]} | cut -d' ' -f 1) + type_of_rules=$(echo ${ready_dir_list[$counter]} | cut -d'_' -f 4) + + if [[ ${access_rules:1} == "rwx------" ]] && [[ ${type_of_rules} == "user" ]]; + then + echo ${counter} " THIS IS RULES FOR" ${type_of_rules} + echo $access_rules + echo "GOOD JOB" + elif [[ ${access_rules:1} == "---rwx---" ]] && [[ ${type_of_rules} == "group" ]]; + then + echo "THIS IS RULES FOR" ${type_of_rules} + echo $access_rules + echo "GOOD JOB" + elif [[ ${access_rules:1} == "------rwx" ]] && [[ ${type_of_rules} == "other" ]]; + then + echo "THIS IS RULES FOR" ${type_of_rules} + echo $access_rules + echo "GOOD JOB" + else + echo ---------------- + echo "YOU MADE MISTAKE FOR" ${type_of_rules} + echo $access_rules + echo ---------------- + fi + +#done + + +``` diff --git a/LINUX/BASH.md b/LINUX/BASH.md new file mode 100644 index 0000000..c8812a0 --- /dev/null +++ b/LINUX/BASH.md @@ -0,0 +1,109 @@ +```bash +mydir=$(pwd) +mydir=`pwd` +echo $mydir + +=========================== + +=========================== +-z # строка пуста +-n # строка не пуста +=, (==) # строки равны +! = # строки не равны + +! # отрицание логическоно выражения +-a, (&&) # логическое И +-o, (||) # логическое ИЛИ + +var1=(( 5 + 5)) +-eq = +-ge >= +-gt > +-le <= +-lt < +-ne != + +if [ $val - gt 5] +then +elif +else +fi + +/dev/pts/$i + +IFS=$'\n' # переназначение переменной среды на только перевод строки +IFS=: +``` + +```sh +# Где я? +mydir=`pwd` +mydir=$(pwd) +echo $mydir + +# Математика: +var1=$(( 5 + 5 )) +echo $var1 + +# IF-THEN-ELSE-ELIF +user=likegeeks +if grep $user /etc/passwd +then +echo "The user $user Exists!" +else +echo "The user $user doesn't exist" +elif grep $user /usr/bin +then +echo "jhgvhjlgvljhgljhg" +fi +``` + +```sh +# Как в Linux, так и в OS X grep возвращает код выхода, поэтому легко проверить, был ли процесс найден или нет:** + +#!/bin/bash +ps cax | grep httpd > /dev/null +if [ $? -eq 0 ]; then + echo "Process is running." +else + echo "Process is not running." +fi + + +# Кроме того, если вам нужен список PID, вы также можете легко выполнить grep для них:** + +ps cax | grep httpd | grep -o '^[ ]*[0-9]*' + +# Чей вывод одинаков в Linux и OS X:** + +3519 3521 3523 3524 + +# Вывод следующего представляет собой пустую строку, что делает этот подход безопасным для процессов, которые не запущены:** + +echo `ps cax | grep aasdfasdf | grep -o '^[ ]*[0-9]*'` + +# Этот подход подходит для написания простого теста с пустой строкой, а затем даже для перебора обнаруженных PID. + +#!/bin/bash +PROCESS=$1 +PIDS=`ps cax | grep $PROCESS | grep -o '^[ ]*[0-9]*'` +if [ -z "$PIDS" ]; then + echo "Process not running." 1>&2 + exit 1 +else + for PID in $PIDS; do + echo $PID + done +fi + +# Вы можете протестировать ее, сохранив в файл (с именем "running") с разрешениями на выполнение (chmod + x running) и выполнив его с параметром: `./running "httpd"` + +#!/bin/bash +ps cax | grep httpd +if [ $? -eq 0 ]; then + echo "Process is running." +else + echo "Process is not running." +fi +``` + diff --git a/LINUX/BASH_github/bash-guide-1.md b/LINUX/BASH_github/bash-guide-1.md new file mode 100644 index 0000000..0128e08 --- /dev/null +++ b/LINUX/BASH_github/bash-guide-1.md @@ -0,0 +1,218 @@ +Бесплатная книга-сайт на русском, полный гайд +[Advanced Bash-Scripting Guide](http://rus-linux.net/MyLDP/BOOKS/abs-guide/flat/abs-book.html) + +# [Основы BASH. Часть 1](https://habr.com/post/47163/) +## Введение + +BASH — Bourne-Again SHell (что может переводится как «перерожденный шел», или «Снова шел Борна(создатель sh)»), самый популярный командный интерпретатор в юниксоподобных системах, в особенности в GNU/Linux. Ниже приведу ряд встроенных команд, которые мы будем использовать для создания своих скриптов. + + +>`break` выход из цикла for, while или until +> `continue` выполнение следующей итерации цикла for, while или until +> `echo` вывод аргументов, разделенных пробелами, на стандартное устройство вывода +> `exit` выход из оболочки +> `export` отмечает аргументы как переменные для передачи в дочерние процессы в среде +> `hash` запоминает полные имена путей команд, указанных в качестве аргументов, чтобы не искать их при следующем обращении +> `kill` посылает сигнал завершения процессу +> `pwd` выводит текущий рабочий каталог +> `read` читает строку из ввода оболочки и использует ее для присвоения значений указанным переменным.\ +> `return` заставляет функцию оболочки выйти с указанным значением +> `shift` перемещает позиционные параметры налево +> `test` вычисляет условное выражение +> `times` выводит имя пользователя и системное время, использованное оболочкой и ее потомками +> `trap` указывает команды, которые должны выполняться при получении оболочкой сигнала +> `unset` вызывает уничтожение переменных оболочки +> `wait` ждет выхода из дочернего процесса и сообщает выходное состояние. + + +И конечно же кроме встроенных команд мы будем использовать целую кучу внешних, отдельных команд-программ, с которыми мы познакомимся уже в процессе + +## Что необходимо знать с самого начала + +1. Любой bash-скрипт должен начинаться со строки: + +> `#!/bin/bash` + +*в этой строке после #! указывается путь к bash-интерпретатору, поэтому если он у вас установлен в другом месте(где, вы можете узнать набрав `whereis bash`) поменяйте её на ваш путь.* + +2. Коментарии начинаются с символа # (кроме первой строки). +3. В bash переменные не имеют типа(о них речь пойдет ниже) + +## Переменные и параметры скрипта + +Приведу как пример небольшой пример, который мы разберем: + +> `#!/bin/bash` #указываем где у нас хранится bash-интерпретатор +> \#присваиваем переменной parametr1 значение первого параметра скрипта +> `parametr1=$1` +> \#присваиваем переменной script_name значение имени скрипта +> `script_name=$0` +> \# команда echo выводит определенную строку, обращение к переменным осуществляется через $имя_переменной. +> `echo "Вы запустили скрипт с именем $script_name и параметром $parametr1"` +> \# здесь мы видим другие кавычки, разница в том, что в одинарных кавычках не происходит подстановки переменных. +> `echo 'Вы запустили скрипт с именем $script_name и параметром $parametr1'` +> \#Выход с кодом 0 (удачное завершение работы скрипта) +> `exit 0` + + +Результат выполнения скрипта: + +> `ite@ite-desktop:~$ ./test.sh qwerty` +> Вы запустили скрипт с именем ./test.sh и параметром qwerty +> Вы запустили скрипт с именем $script_name и параметром $parametr1 + + +После того как мы познакомились как использовать переменные и передавать скрипту параметры, время познакомиться с зарезервированными переменными: + +> `$DIRSTACK` - содержимое вершины стека каталогов +> `$EDITOR` - текстовый редактор по умолчанию +> `$EUID` - Эффективный UID. Если вы использовали программу su для выполнения команд от другого пользователя, то эта переменная содержит UID этого пользователя, в то время как... +> `$UID` - ...содержит реальный идентификатор, который устанавливается только при логине. +> `$FUNCNAME` - имя текущей функции в скрипте. +> `$GROUPS` - массив групп к которым принадлежит текущий пользователь +> `$HOME` - домашний каталог пользователя +> `$HOSTNAME` - ваш hostname +> `$HOSTTYPE` - архитектура машины. +> `$LC_CTYPE` - внутренняя переменная, котороя определяет кодировку символов +> `$OLDPWD` - прежний рабочий каталог +> `$OSTYPE` - тип ОС +> `$PATH` - путь поиска программ +> `$PPID` - идентификатор родительского процесса +> `$SECONDS` - время работы скрипта(в сек.) +> `$#` - общее количество параметров переданных скрипту +> `$*` - все аргументы переданыне скрипту(выводятся в строку) +> `$@` - тоже самое, что и предыдущий, но параметры выводятся в столбик +> `$!` - PID последнего запущенного в фоне процесса +> `$$` - PID самого скрипта + +## Условия + +Условные операторы, думаю, знакомы практически каждому, кто хоть раз пытался на чем-то писать программы. В bash условия пишутся след. образом (как обычно на примере): + + +> `#!/bin/bash` +> \#в переменную source засовываем первый параметр скрипта +> `source=$1` +> \#в переменную dest засовываем второй параметр скрипта +> `dest=$2` +> +> \# в ковычках указываем имена переменных для сравнения. -eq - логическое сравнение обозначающие "равны" +> `if [[ "$source" -eq "$dest" ]]` +> \# если они действительно равны, то +> `then` +> \#выводим сообщение об ошибке, т.к. $source и $dest у нас равны +> `echo "Применик $dest и источник $source один и тот же файл!"` +> \# выходим с ошибкой (1 - код ошибки) +> `exit 1` +> \# если же они не равны +> `else` +> \# то выполняем команду cp: копируем источник в приемник +> `cp $source $dest` +> `echo "Удачное копирование!"` +> `fi` #обозначаем окончание условия. + + +Результат выполнения скрипта: + + +> `ite@ite-desktop:~$ ./primer2.sh 1 1` +> Применик 1 и источник 1 один и тот же файл! + +> `ite@ite-desktop:~$ ./primer2.sh 1 2` +> Удачное копирование! + +Структура `if-then-else` используется следующим образом: + +> `if` <команда или набор команд возвращающих код возврата(0 или 1)> +> `then` +> <если выражение после if истино, то выполняется этот блок> +> `else` +> <если выражение после if ложно, тот этот> + + +В качестве команд возвращающих код возврата могут выступать структуры `[[ , [ , test, (( ))` или любая другая(или несколько) linux-команда. + + +> `test` - используется для логического сравнения. после выражения, неоьбходима закрывающая скобка "]" +>`[` - синоним команды test +>`[[` - расширенная версия "[" (начиная с версии 2.02)(как в примере), внутри которой могут быть использованы || (или), & (и). Долна иметь закрывающуб скобку "]]" +>`(( ))` - математическое сравнение. + + +для построения многоярусных условий вида: + +> `if ...` +>`then ....` +>`else` +>`if ....` +>`then....` +>`else ....` + +для краткости и читаемости кода, можно использовать структуру: + +> `if ..` +> `then ...` +> `elif ...` +> `then ...` +> `elif ...` + + +## Условия. Множественный выбор + +Если необходимо сравнивать какоую-то одну переменную с большим количеством параметров, то целесообразней использовать оператор case. + +> `#!/bin/bash` +> `echo "Выберите редатор для запуска:"` +> `echo "1 Запуск программы nano"` +> `echo "2 Запуск программы vi"` +> `echo "3 Запуск программы emacs"` +> `echo "4 Выход"` +\#здесь мы читаем в переменную $doing со стандартного ввода +> `read doing` +> +> `case $doing in` +> `1)` +> `/usr/bin/nano` # если $doing содержит 1, то запустить nano +> `;;` +> `2)` +> `/usr/bin/vi` # если $doing содержит 2, то запустить vi +> `;;` +> `3)` +> `/usr/bin/emacs` # если $doing содержит 3, то запустить emacs +> `;;` +> `4)` +> `exit 0` +> `;;` +> `*)` #если введено с клавиатуры то, что в case не описывается, выполнять следующее: +> `echo "Введено неправильное действие"` +> +> `esac` #окончание оператора case. + +Результат работы: + +> `ite@ite-desktop:~$ ./menu2.sh` +> Выберите редатор для запуска: +> 1 Запуск программы nano +> 2 Запуск программы vi +> 3 Запуск программы emacs +> 4 Выход + +После выбор цифры и нажатия Enter запуститься тот редактор, который вы выбрали(если конечно все пути указаны правильно, и у вас установлены эти редакторы :) ) + + +Прведу список логических операторв, которые используются для конструкции if-then-else-fi: + +> `-z` # строка пуста +> `-n` # строка не пуста +> `=, (==)` # строки равны +> `!=` # строки неравны +> `-eq` # равно +> `-ne` # неравно +> `-lt,(< )` # меньше +> `-le,(<=)` # меньше или равно +> `-gt,(>)` #больше +> `-ge,(>=)` #больше или равно +> `!` #отрицание логического выражения +> `-a,(&&)` #логическое «И» +> `-o,(||)` # логическое «ИЛИ» + diff --git a/LINUX/BASH_github/bash-guide-2.md b/LINUX/BASH_github/bash-guide-2.md new file mode 100644 index 0000000..92c42fa --- /dev/null +++ b/LINUX/BASH_github/bash-guide-2.md @@ -0,0 +1,225 @@ +# [Основы BASH. Часть 2](https://habr.com/post/52871/) + +## Циклы. Цикл for-in. + +Оператор `for-in` предназначен для поочередного обращения к значениям перечисленным в списке. Каждое значение поочередно в списке присваивается переменной. +Синтаксис следующий: + +``` +for переменная in список_значений +do +команды +done +``` + +Рассмотрим небольшой пример: + +``` +#!/bin/bash +for i in 0 1 2 3 4 #переменной $i будем поочередно присваивать значения от 0 до 4 включительно +do +echo "Console number is $i" >> /dev/pts/$i #Пишем в файл /dev/pts/$i(файл виртуального терминала) строку "Console number is $i" +done #цикл окончен +exit 0 +``` + +После выполнения примера в первых 5 виртуальных консолях(терминалах) появится строка с её номером. В переменную `$i` поочередно подставляются значения из списка и в цикле идет работа со значением этой переменной + +## Циклы. Цикл while. + +Цикл `while` сложнее цикла `for-in` и используется для повторения команд, пока какое-то выражение истинно( код возврата = 0). +Синтаксис оператора следующий: + +``` +while выражение или команда возвращающая код возврата +do +команды +done +``` + +Пример работы цикла рассмотрим на следующем примере: + +``` +#!/bin/bash +again=yes #присваиваем значение "yes" переменной again +while [ "$again" = "yes" ] #Будем выполнять цикл, пока $again будет равно "yes" +do +echo "Please enter a name:" +read name +echo "The name you entered is $name" + +echo "Do you wish to continue?" +read again +done +echo "Bye-Bye" +``` + + +А теперь результат работы скрипта: +``` +ite@ite-desktop:~$ ./bash2_primer1.sh +Please enter a name: +ite +The name you entered is ite +Do you wish to continue? +yes +Please enter a name: +mihail +The name you entered is mihail +Do you wish to continue? +no +Bye-Bye +``` + +Как видим цикл выполняется до тех пор, пока мы не введем что-то отличное от «yes». Между `do` и `done` можно описывать любые структуры, операторы и т.п., все они будут выполнятся в цикле.Но следует быть осторожным с этим циклом, если вы запустите на выполнение в нём какую-либо команду, без изменения переменной выражения, вы можете попасть в бесконечный цикл. + +Теперь об условии истинности. После `while`, как и в условном операторе `if-then-else` можно вставлять любое выражение или команду, которая возвращает код возврата, и цикл будет исполнятся до тех пор, пока код возврата = 0! Оператор `[` аналог команды `test`, которая проверяет истинность условия, которое ей передали. + +Рассмотрим еще один пример, я взял его из книги Advanced Bash Scripting. Уж очень он мне понравился :), но я его немного упростил. В этом примере мы познакомимся с еще одним типом циклов **UNTIL-DO**. Эта практически полный аналог цикла **WHILE-DO**, только выполняется пока какое-то выражение ложно. +Вот пример: +``` +#!/bin/bash +echo "Введите числитель: " +read dividend +echo "Введите знаменатель: " +read divisor + +dnd=$dividend #мы будем изменять переменные dividend и divisor, +#сохраним их знания в других переменных, т.к. они нам +#понадобятся +dvs=$divisor +remainder=1 + +until [ "$remainder" -eq 0 ] +do +let "remainder = dividend % divisor" +dividend=$divisor +divisor=$remainder +done + +echo "НОД чисел $dnd и $dvs = $dividend" +``` + +Результат выполнения скрипта: +``` +ite@ite-desktop:~$ ./bash2_primer3.sh +Введите числитель: +100 +Введите знаменатель: +90 +НОД чисел 100 и 90 = 10 +``` + +## Математические операции + +Команда `let`. +Команда `let` производит арифметические операции над числами и переменными. +Рассмотрим небольшой пример, в котором мы производим некоторые вычисления над введенными числами: + +``` +#!/bin/bash +echo "Введите a: " +read a +echo "Введите b: " +read b + +let "c = a + b" #сложение +echo "a+b= $c" +let "c = a / b" #деление +echo "a/b= $c" +let "c <<= 2" #сдвигает c на 2 разряда влево +echo "c после сдвига на 2 разряда: $c" +let "c = a % b" # находит остаток от деления a на b +echo "$a / $b. остаток: $c " +``` + +Результат выполнения: +``` +ite@ite-desktop:~$ ./bash2_primer2.sh +Введите a: +123 +Введите b: +12 +a+b= 135 +a/b= 10 +c после сдвига на 2 разряда: 40 +123 / 12. остаток: 3 +``` + + +Ну вот, как видите ничего сложного, список математических операций стандартный: + +> `+` — сложение +> `—` — вычитание +> `*` — умножение +> `/` — деление +> `**` — возведение в степень +> `%` — модуль(деление по модулю), остаток от деления +> `let` позволяет использовать сокращения арифметических команд, тем самым сокращая кол-во используемых переменных. Например: `a = a+b` эквивалентно `a +=b` и т.д + +## Работа с внешними программами при написании shell-скриптов + +Для начала немного полезной теории. +### Перенаправление потоков. + +В **bash** (как и многих других оболочках) есть встроенные файловые дескрипторы: `0 (stdin)`, `1 (stdout)`, `2 (stderr)`. +`stdout` — Стандартный вывод. Сюда попадает все что выводят программы +`stdin` — Стандартный ввод. Это все что набирает юзер в консоли +`stderr` — Стандартный вывод ошибок. +Для операций с этими дескрипторами, существуют специальные символы: `>` (перенаправление вывода), `<` (перенаправление ввода). Оперировать ими не сложно. Например: + +``` +cat /dev/random > /dev/null #перенаправить вывод команды cat /dev/random в /dev/null (абсолютно бесполезная операция :)) ) +``` +или + +``` +ls -la > listing #записать в файл listing содержание текущего каталога (уже полезней) +``` + +Если есть необходимость дописывать в файл(при использовании "`>`" он заменятеся), необходимо вместо "`>`" использовать "`>>`" +``` +sudo < my_password +``` +после просьбы sudo ввести пароль, он возьмется из файла my_password, как будто вы его ввели с клавиатуры. +Если необходимо записать в файл только ошибки, которые могли возникнуть при работе программы, то можно использовать: +``` +./program_with_error 2> error_file +``` +цифра 2 перед "`>`" означает что нужно перенаправлять все что попадет в дескриптор 2(stderr). +Если необходимо заставить `stderr` писать в `stdout`, то это можно след. образом: +``` +./program_with_error 2>&1 +``` +символ "`&`" означает указатель на дескриптор `1(stdout)` +(Поумолчанию `stderr` пишет на ту консоль, в котрой работает пользователь(вренее пишет на дисплей)). + +### 2. Конвееры. + +Конвеер — очень мощный инструмент для работы с консолью Bash. Синтаксис простой: +`команда1 | команда 2` — означает, что вывод команды 1 передастся на ввод команде 2 +Конвееры можно группировать в цепочки и выводить с помощью перенаправления в файл, например: +``` +ls -la | grep «hash» |sort > sortilg_list +``` +вывод команды `ls -la` передается команде `grep`, которая отбирает все строки, в которых встретится слово *hash*, и передает команде сортировке `sort`, которая пишет результат в файл *sorting_list*. Все довольно понятно и просто. + +Чаще всего скрипты на Bash используются в качестве автоматизации каких-то рутинных операций в консоли, отсюда иногда возникает необходимость в обработке `stdout` одной команды и передача на `stdin` другой команде, при этом результат выполнения одной команды должен быть неким образом обработан. В этом разделе я постораюсь объяснить основные принципы работы с внешними командами внутри скрипта. Думаю что примеров я привел достаточно и можно теперь писать только основные моменты. + +### 1. Передача вывода в переменную. + +Для того чтобы записать в переменную вывод какой-либо команды, достаточно заключить команду в ` `` ` ковычки, например +> a = \` echo "qwerty" \` +> echo $a + +Результат работы: qwerty + +Однако если вы захотите записать в переменную список директорий, то необходимо, должным образом обработать результат для помещения данных в переменную. Рассмотрим небольшой, пример: +``` +LIST=`find /svn/ -type d 2>/dev/null| awk '{FS="/"} {print $4}'| sort|uniq | tr '\n' ' '` +for ONE_OF_LIST in $LIST +do +svnadmin hotcopy /svn/$ONE_OF_LIST /svn/temp4backup/$ONE_OF_LIST +done +``` +Здесь мы используем цикл `for-do-done` для архивирование всех директорий в папке */svn/* с помощью команды `svnadmin hotcopy`(что в нашем случае не имеет никого значения, просто как пример). Наибольшй интерес вызывает строка: LIST=`find /svn/ -type d 2>/dev/null| awk '{FS="/"} {print $4}'| sort|uniq | tr '\n' ' '` В ней переменной LIST присваивается выполнение команды find, обработанной командами awk, sort, uniq,tr(все эти команды мы рассматривать не будем, ибо это отдельная статья). В переменной LIST будут имена всех каталогов в папке /svn/ пгомещенных в одну строку(для того чтобы её стравить циклу. \ No newline at end of file diff --git "a/LINUX/BASH_github/\320\236\321\201\320\275\320\276\320\262\321\213 Bash.md" "b/LINUX/BASH_github/\320\236\321\201\320\275\320\276\320\262\321\213 Bash.md" new file mode 100644 index 0000000..7100ab1 --- /dev/null +++ "b/LINUX/BASH_github/\320\236\321\201\320\275\320\276\320\262\321\213 Bash.md" @@ -0,0 +1,222 @@ +## синтаксис, переменные и команды ## + +Кодить будем в терминале Linux — он открывается сочетанием клавиш **Ctrl + Alt + T**. Если вы хотите научиться работать с Bash, но пока не пользуетесь Linux, то пишите код в одном из онлайн-терминалов — например, [Replit](https://repl.it/languages/bash). + +### **Переменные** + +Переменные в Bash объявляются просто: + +` message="Hello, World!" ` + +Теперь переменная message хранит значение — текстовое сообщение «Hello, World!». Давайте выведем его — сделать это можно с помощью команды echo: + +` echo $message # Будет выведено «Hello, World!» ` + +Обратите внимание на отсутствие пробелов. В Bash пробелы являются разделителями. Если пробелы есть, то Bash будет считать их отдельными токенами и выдавать ошибки. + +Следующий код приведёт к ошибке: + +` message = "Hello, World!" bash: message: command not found ` + +Также обратите внимание на символ $ **—** в Bash он называется символом расширения (expansion character). Поставленный перед именем переменной, он указывает на то, что нужно использовать именно значение переменной, а не одноимённую строку. + +Следующий код выведет сообщение «message»: + +` message="Hello, World!" echo message ` + +Чтобы сохранить в переменной введённое пользователем значение, используйте команду read: + +` read name # Пользователь вводит что-то — например, «Olga» echo $name # Будет выведено «Olga» ` + +Если вы хотите добавить сообщение перед пользовательским вводом, добавьте флаг -p и сообщение после команды read: + +` read -p "Enter your name: " name # Будет выведено «Enter your name: » # Пользователь вводит что-то — например, «Olga» echo $name # Будет выведено «Olga» ` + +В Bash есть несколько правил именования переменных: + +- Имя переменной должно начинаться с буквы латинского алфавита (нижнего или верхнего регистра) или символа подчёркивания _. +- Оно может включать буквы, цифры и символ подчёркивания, но не может содержать пробелы или специальные символы. +- Регистр букв в именах переменных имеет значение. Например, переменные var1 и Var1 будут рассматриваться как разные. +- Не рекомендуется использовать зарезервированные слова Bash в качестве имён переменных. + +### **Типы данных** + +Ну что же, вот мы и создали нашу первую переменную. Поздравляем! Пока эта переменная содержит всего один тип данных — строковый (string). Но в Bash он далеко не единственный — давайте разберём ещё несколько. + +**ВАЖНАЯ ОГОВОРКА** + +В Bash нет строгой системы типов, как в некоторых других языках. Тип данных зависит от содержимого переменной и контекста её использования. Поэтому термин «типы данных» в этой статье используется условно, для облегчения понимания. Простите нам это упрощение. + +**Строки (strings)** — это набор символов, заключённый в кавычки. В Bash можно писать строки в одинарных '…' или двойных "…" кавычках. Например, 'Привет' и "Мир" — это строки. + +**Числа (numbers)** — это целочисленные значения. Bash поддерживает арифметические операции с целыми числами, включая сложение, вычитание, умножение и деление. Арифметические выражения нужно предварять знаком $ и обрамлять двойными круглыми скобками: + +` a=2 b=3 echo $(($a + $b)) # Будет выведено 5 ` + +Также можно использовать команду expr: + +` echo $(expr $a + $b) # Будет выведено 5 ` + +Стандартные библиотеки Bash поддерживают следующие арифметические операции: + +- Сложение — +. +- Вычитание — -. +- Умножение — *. +- Целочисленное деление — /. +- Остаток от деления — %. +- Возведение в степень — **. + +**Примечание**: арифметические операторы в Bash не работают с числами с плавающей точкой. + +**Массивы (arrays)** — тип переменных, который может хранить несколько значений. Элементы массива перечисляются в круглых скобках через пробел: + +` my_array=(apple banana cherry) ` + +При использовании переменной массива в команде применяются фигурные скобки. Если мы хотим вывести его отдельные элементы, это можно сделать с помощью символа $ и номера индекса в квадратных скобках: + +` echo ${my_array[0]} # Выведет «apple» echo ${my_array[1]} # Выведет «banana» echo ${my_array[2]} # Выведет «cherry» ` + +А если нам нужно получить сразу все элементы массива, вместо индекса в квадратных скобках нужно поставить символ * или @: + +` echo ${my_array[*]} # Выведет «apple banana cherry» echo ${my_array[@]} # Выведет «apple banana cherry» ` + +**Логические значения (boolean)** — нужны для того, чтобы проверить, выполняется ли какое-то условие или нет. Они могут быть либо true (истина), либо false (ложь). Например, в операторах if и while мы пишем условие, и если оно true, то выполняем какое-то действие. + +В Bash логические значения представлены целыми числами. Число 0 означает true, а любое другое число означает false. Когда мы выводим логическое значение на экран, то вместо true мы видим 0. + +Также в Bash есть операторы сравнения. Например, эти нужны для строк: + +- = проверяет, одинаковы ли две строки. +- != проверяет, различаются ли две строки. + +А эти — для чисел: + +- -eq проверяет, равны ли два числа. +- -ne проверяет, различаются ли два числа. +- -gt проверяет, больше ли первое число, чем второе. +- -lt проверяет, меньше ли первое число, чем второе. +- -ge выводит true, если первое число больше или равно второму. +- -le выводит true, если первое число меньше или равно второму. + +Использовать операторы сравнения можно с помощью команды test. Команда test принимает два значения и оператор сравнения и возвращает 1 или 0 в зависимости от результата: + +` test "hello" = "hello" echo $? # Выведет 0 (true), так как строки одинаковые test "hello" != "world" echo $? # Выведет 0 (true), так как строки различаются ` + +Помимо команды test, операторы сравнения можно использовать квадратных скобках [ ]. Но для этого нам понадобится разобраться, как работать с ветвлениями. + +### **Ветвления** + +Ветвления if-then-else позволяют проверить условие и выполнить один набор команд, если условие истинно, и другой набор команд, если условие ложно. Это выглядит так: + +` if [ условие ]; then # Команды, которые нужно выполнить, если условие истинно else # Команды, которые нужно выполнить, если условие ложно fi ` + +**Обратите внимание**: открывающую и закрывающую квадратные скобки нужно отделять от операндов пробелами. + +А если в паре с основным условием нужно проверить дополнительное, используют конструкцию elif: + +` if [ условие_1 ]; then # Команды, которые нужно выполнить, если условие_1 истинно elif [ условие_2 ]; then # Команды, которые нужно выполнить, если условие_2 истинно else # Команды, которые нужно выполнить, если все условия ложны fi ` + +Существует и более короткая форма записи условной конструкции — if-then. Её используют, когда нужно проверить только одно условие: + +` if [ условие ]; then # Команды, которые нужно выполнить fi ` + +В этом случае команды будут выполнены, только если условие истинно. + +Для наглядности давайте напишем скрипт, который проверяет правильность введённого пароля: + +` #!/bin/bash # Задаём значение переменной password password="14pomTEr" # Запрашиваем ввод пароля у пользователя и сохраняем полученное значение в переменную user_input read -p "Enter the password: " user_input # Сравниваем значение переменной password с введённым пользователем значением user_input if [ "$password" = "$user_input" ]; then # Если пароли совпадают, выводим сообщение о том, что пароль введён правильно echo "Password accepted" else # Если пароли не совпадают, выводим сообщение о том, что пароль введён неправильно echo "Wrong password" fi ` + +Кроме того, в Bash есть логические операторы & & (логическое И) и **||** (логическое ИЛИ), которые позволяют комбинировать несколько условий и возвращать соответствующее булево значение. + +Например, вот как будет выглядеть наша программа, если мы добавим проверку логина: + +` #!/bin/bash username="OlgaBelova94" # Устанавливаем переменную с именем пользователя password="14pomTEr" read -p "Enter your username: " user_input_username # Запрашиваем имя пользователя read -p "Enter the password: " user_input_username # Запрашиваем пароль echo "Enter the password: " read user_input_password # Запрашиваем пароль if [ "$username" == "$user_input_username" ] && [ "$password" == "$user_input_password" ]; then echo "Password accepted" else echo "Wrong username or password" # Уточняем, что именно было неверно fi ` + +Здесь мы получим значение true и сообщение «Password accepted», только если будут выполнены оба условия. + +### **Циклы** + +Как и в большинстве языков программирования, в Bash есть циклы. Они бывают двух видов: + +- for — когда количество итераций заранее известно. +- while — когда количество итераций заранее не известно. + +Вот как выглядит сигнатура цикла for: + +` for переменная цикла in элемент1 элемент2 ... элементN do # Команды, которые будут выполняться в цикле done ` + +Количество итераций (часть между in и do) можно указать простым перечислением элементов: + +` for fruit in apple banana cherry do echo "I like $fruit" done # Будет выведено: # I like apple # I like banana # I like cherry ` + +Можно перебрать элементы массива: + +` my_array=("apple" "banana" "orange") for fruit in "${my_array[@]}" do echo "I like $fruit" done # Будет выведено: # I like apple # I like banana # I like cherry ` + +Для явного указания нужного количества итераций используют арифметические выражения: + +` for ((i=0; i<5; i++)) do echo "Iteration number $i" done # Будет выведено: # Iteration number 0 # Iteration number 1 # Iteration number 2 # Iteration number 3 # Iteration number 4 ` + +А вот так выглядит сигнатура цикла while: + +` while [ условие ] do # Команды, которые будут выполняться в цикле done ` + +**Условие** **(condition)** — это выражение, результат которого является логическим значением true или false. Команды внутри цикла будут выполняться до тех пор, пока condition возвращает true. + +Давайте усовершенствуем нашу программу проверки пароля так, чтобы она запрашивала его до тех пор, пока пользователь не введёт верный: + +` #!/bin/bash password="14pomTEr" user_input="" while [ "$user_input" != "$password" ] do read -p "Enter the password: " user_input echo "Wrong password, login denied" done echo "Password accepted, login allowed" ` + +**Что здесь происходит.** В этом примере цикл while запрашивает пароль у пользователя, а потом проверяет его на совпадение с правильным с помощью оператора ["$user_input" != "$password"]. А дальше происходит следующее: + +- Если пароль неверный, программа выводит соответствующее сообщение, а цикл продолжается. +- Если пароль верный, программа выводит сообщение о принятии пароля и завершает выполнение. + +### **Функции** + +Функции на языке Bash имеют такой вид: + +` имя_функции (аргументы) { # Тело функции } ` + +Например, вот как выглядит простая функция без аргументов: + +` say_hello () { # Создаём функцию echo hello } say_hello # Вызываем функцию, будет выведено «hello» ` + +А вот как выглядит функция, которая принимает аргументы: + +` repeat_text () { local text=$1 local reps=$2 for (( i=0; i<$reps; i++ )); do echo "$text" done } repeat_text "hello" 3 ` + +**Что здесь происходит:** + +- С помощью ключевого слова local мы создали две локальные переменные — то есть переменные, существующие только внутри функции. +- В одну из них поместили текст, а во вторую — количество повторов. +- Затем создали цикл, который берёт текст из первой переменной и повторяет его столько раз, сколько раз указано во второй. +- Вызвали функцию, передав ей значения hello и 3 — то есть приказали повторить слово «hello» три раза. + +## **Ну здравствуй, Bash! Пишем первый скрипт** + +Настало время применить знания на практике — напишем скрипт для автоматической очистки кэша браузера и DNS. + +Состоять наш скрипт будет из трёх функций: первая — для очистки кэша браузера, вторая — для очистки кэша DNS, а в третью завернём небольшое меню для выбора нужного действия. + +Начинаем, как водится, с шебанга — специальной строки, которая говорит компьютеру, с помощью какой программы нужно запускать скрипт. Так как мы хотим использовать bash, которая лежит в папке /bin, шебанг будет выглядеть так: + +` #!/bin/bash ` + +Теперь напишем функцию, которая будет очищать браузерный кэш. В комментариях — разбор синтаксиса. + +` clear_browser_cache() { # Проверяем, какой браузер установлен на компьютере if command -v google-chrome >/dev/null 2>&1; then # Если установлен Google Chrome, очищаем кэш браузера google-chrome --user-data-dir=$(mktemp -d) --disable-extensions --disable-plugins --disable-geolocation --no-default-browser-check --no-first-run --noerrdialogs --disable-session-crashed-bubble --disable-infobars --disk-cache-size=0 --media-cache-size=0 # Далее проверяем, установлен ли Firefox elif command -v firefox >/dev/null 2>&1; then # Если установлен Firefox, очищаем кэш браузера firefox --new-instance --safe-mode # Если браузер не установлен, выводим сообщение об ошибке else echo "Error: no supported browser found" fi } ` + +Далее напишем функцию для очистки кэша DNS. + +` clear_dns_cache() { # Проверяем, какая операционная система установлена на компьютере # Если установлена Linux, очищаем кэш DNS if [[ "$OSTYPE" == "linux-gnu"* ]]; then sudo systemd-resolve --flush-caches # Если установлена macOS, очищаем кэш DNS elif [[ "$OSTYPE" == "darwin"* ]]; then sudo dscacheutil -flushcache sudo killall -HUP mDNSResponder # Если операционная система не поддерживается, выводим сообщение об ошибке else echo "Error: unsupported operating system" fi } ` + +Ну а теперь дело за малым. Напишем небольшое меню, чтобы пользователь мог выбрать, кэш браузера или кэш DNS нужно очистить. + +` # Сначала выведем варианты clear_cache() { echo "Select cache to clear:" echo "1. Browser cache" echo "2. DNS cache" # Запросим у пользователя номер выбранного пункта меню read -p "Enter your choice: " choice # В зависимости от выбранного пункта меню вызываем соответствующую функцию для очистки кэша if [ "$choice" -eq 1 ]; then clear_browser_cache elif [ "$choice" -eq 2 ]; then clear_dns_cache else echo "Invalid choice" fi } ` + +Вуаля! Наш скрипт готов. Чтобы его запустить, нам нужно лишь вызвать функцию clear_cache: + +` clear_cache ` + +В теории можно сделать скрипт и ещё лучше — например, добавить больше браузеров, реализовать проверку прав администратора и другие фишки. Но на сегодня, пожалуй, достаточно. \ No newline at end of file diff --git "a/LINUX/BASH_script/15 \320\276\320\264\320\275\320\276\321\201\321\202\321\200\320\276\321\207\320\275\321\213\321\205 bash-\321\201\320\272\321\200\320\270\320\277\321\202\320\276\320\262.md" "b/LINUX/BASH_script/15 \320\276\320\264\320\275\320\276\321\201\321\202\321\200\320\276\321\207\320\275\321\213\321\205 bash-\321\201\320\272\321\200\320\270\320\277\321\202\320\276\320\262.md" new file mode 100644 index 0000000..555f4ff --- /dev/null +++ "b/LINUX/BASH_script/15 \320\276\320\264\320\275\320\276\321\201\321\202\321\200\320\276\321\207\320\275\321\213\321\205 bash-\321\201\320\272\321\200\320\270\320\277\321\202\320\276\320\262.md" @@ -0,0 +1,211 @@ +для упрощения рутинных задач + +1. Проверка использования +дискового пространства +Нехватка места на диске может привести к +сбою приложений. Эта команда выводит +список всех смонтированных разделов и +выделяет те, которые утилизированы на 80% +и более. +==`df -h | awk '$5+0 > 80 {print}' +- df -hотображает использование диска в +удобном для чтения формате. +- awk ‘$5+0 > 80 {print}’извлекает +строки, в которых утилизация превышает +80%. + +2. Поиск больших файлов, +которые занимают место +Определите 10 самых больших файлов в +системе. +==`find / -type f -exec du -h {} + | +sort -rh | head -10 +- find / -type fищет все файлы. +- du -hвычисляет размеры файлов в +удобном для чтения формате. +- sort -rhсортирует результаты по +убыванию. +- head -10ограничивает вывод 10 самыми +большими файлами. + +3. Отслеживание обновлений +лога в реальном времени +Просмотр файлов логов в режиме реального +времени, с фильтрацией сообщений об +ошибках. +==`tail -f /var/log/syslog | grep - +line-buffered "error" +- tail -fотслеживает файл журнала по +мере добавления новых записей. +- grep — line-buffered “error” +фильтрует строки, содержащие "error". + +4. Перезапуск службы в случае +сбоя +Проверка, что критически важная служба, +такая как Nginx, всегда запущена. +==`systemctl is-active - quiet nginx +|| systemctl restart nginx +- systemctl is-active — quiet +nginx проверяет состояние службы. +- Если она не запущена, systemctl +restart nginx перезапускает ее. + +5. Мониторинг использования +CPU в режиме реального +времени +Проверка загрузки CPU в реальном времени +для устранения неполадок с +производительностью. +==`top -b -n 1 | grep "Cpu(s)" +- top -b -n 1запускает «top» в пакетном +режиме для одной итерации. +- grep “Cpu(s)”извлекает сведения об +использовании CPU. + +6. Уничтожение самого +ресурсоемкого процесса +Поиск и завершение процесса, +потребляющего больше всего ресурсов CPU. +==`kill -9 $(ps -eo pid,%cpu - +sort=-%cpu | awk 'NR==2 {print +$1}') +- ps -eo pid,%cpu — sort=-%cpuвыводит +список процессов, отсортированных по +использованию CPU. +- awk ‘NR\==2 {print $1}’выбирает +процесс с наибольшим потреблением ЦП. +- kill -9принудительно завершает его. + +7. Получение публичного IP- +адреса +Быстрое получение публичного IP-адреса +сервера. +==`curl -s ifconfig.me +- Способ использует curl для запроса +текущего публичного IP-адреса из +«ifconfig.me». +- -s Подавляет ненужный вывод. + +8. Поиск всех открытых сетевых +портов +Перечисление всех активных +прослушиваемых портов на вашем +компьютере. +==`netstat -tulnp | grep LISTEN +- netstat -tulnpотображает открытые +порты и связанные с ними процессы. +- grep LISTENфильтрует результаты по +прослушиваемым портам. + +9. Копирование SSH-ключа на +удаленный сервер +Настройка беспарольной SSH- +аутентификации за секунды. +==`ssh-keygen -t rsa -b 2048 -q -N +"" && ssh-copy-id +user@remote_server +- ssh-keygen создает пару ключей RSA. +- ssh-copy-id передает открытый ключ на +удаленный сервер. + +10. Синхронизация файлов +между серверами +Использование rsync для зеркалирования +файлов с одного сервера на другой. +==`rsync -avz /local/path +user@remote:/remote/path +- -a сохраняет разрешения и временные +метки. +- -v включает подробный режим. +- -z сжимает данные для более быстрой +передачи. + +11. Планирование задач с +помощью Cron +Добавление задания cron для резервного +копирования файлов каждую ночь в 2 часа +ночи. +==`echo "0 2 * * * tar -czf / +backup.tar.gz /important/data" | +crontab - +- 0 2 * * *планирует задание на 2 часа +ночи ежедневно. +- tar -czfсжимает и создает резервную +копию "/important/data". + +12. Загрузка файла в фоновом +режиме +Использование «wget» для загрузки +больших файлов без блокировки терминала. +==`nohup wget -q https:// +example.com/bigfile.zip & +- nohup обеспечивает выполнение процесса +даже после выхода из системы. +- wget -q незаметно загружает файл. +- & запускает процесс в фоновом режиме. + +13. Поиск и удаление старых +файлов логов +Автоматическая очистка логов старше 7 +дней. +==`find /var/log -name "*.log" +-mtime +7 -exec rm -f {} \; +- -mtime +7выбирает файлы старше 7 дней. +- -exec rm -f {}удаляет их. + +14. Проверка открыт ли порт на +удаленном сервере +Проверка открыт ли определенный порт +(например, 443) на удаленном хосте. +==`nc -zv example.com 443 +- nc -zvпроверяет соединение без +отправки данных. +- Полезно для устранения неполадок +брандмауэра и подключения. + +15. Список всех запущенных +Docker-контейнеров +Удобный просмотр активных контейнеров +Docker. +==`docker ps - format "table {{.ID}} +\t {{.Image}} \t {{.Status}}" +- docker psвыводит список запущенных +контейнеров. +- — formatструктурирует вывод для лучшей +читабельности. + +========================= +Подборка полезных скриптов для опытных Linux-админов + +1) 🔍 Быстрый поиск крупных файлов (удобно перед очисткой диска) +du -ah / | sort -rh | head -n 30 + +2) 🛑 Массовое убийство зависших процессов по шаблону +pkill -f "pattern" + +3) 🧼 Очистка старых логов/кэша старше X дней +find /var/log -type f -mtime +7 -delete + +4) 🧬 Проверка файловой системы без остановки +fsck -n /dev/sdX + +5) 📦 Список пакетов, обновлённых за последние 5 дней +grep "upgrade" /var/log/dpkg.log | tail + +6) 🕵️ Слежение за открытыми портами в реальном времени +watch -n1 "ss -tulpn" + +7) 📊 Сбор top-нагружающих процессов по CPU за минуту +ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%cpu | head + +8) 🔁 Авто-рестарт службы при падении +while true; do systemctl restart myservice; sleep 5; done + +9) 🔗 Быстрый тест сетевой задержки и MTU +tracepath google.com + +10) 🔐 Сканирование слабых SSH-ключей +ssh-audit -p 22 localhost + +Такие короткие утилитарные команды экономят кучу времени в рутине админа. \ No newline at end of file diff --git "a/LINUX/BASH_script/BASH. \320\240\320\260\320\267\320\275\320\276\320\265.md" "b/LINUX/BASH_script/BASH. \320\240\320\260\320\267\320\275\320\276\320\265.md" new file mode 100644 index 0000000..f76b28f --- /dev/null +++ "b/LINUX/BASH_script/BASH. \320\240\320\260\320\267\320\275\320\276\320\265.md" @@ -0,0 +1,40 @@ +_**Использование стандартных ключей**_ + +При написании bash-скриптов вы можете выбирать любые буквы для ключей командной строки и произвольно задавать реакцию скрипта на эти ключи. Однако, в мире Linux значения некоторых ключей стали чем-то вроде стандарта, которого полезно придерживаться. Вот список этих ключей: + +``` +-a Вывести все объекты. +-c Произвести подсчёт. +-d Указать директорию. +-e Развернуть объект. + +-f Указать файл, из которого нужно прочитать данные. +-h Вывести справку по команде. +-i Игнорировать регистр символов. +-l Выполнить полноформатный вывод данных. + +-n Использовать неинтерактивный (пакетный) режим. (ввод на той же строке) +-o Позволяет указать файл, в который нужно перенаправить вывод. +-p предотвращения чтения оболочкой файлов, контролируемых пользователем +-t 5 параметр ключа задаёт время ожидания ввода в секундах +-q Выполнить скрипт в quiet-режиме. +-r Обрабатывать папки и файлы рекурсивно. +-s Выполнить скрипт в silent-режиме. команды read предотвращает отображение на экране данных, вводимых с клавиатуры +-v Выполнить многословный вывод. +-x Исключить объект. +-y Ответить «yes» на все вопросы. + +$REPLY - специальная переменная среды в которую запоминается ввод + +``` + +```sh +# простое чтение данных из файла: + +#!/bin/bash +count=1 +cat myfile | while read line do +echo "Line $count: $line" count=$(( $count + 1 )) +done +echo "Finished" +``` diff --git "a/LINUX/BASH_script/BASH. \320\247\320\260\321\201\321\202\321\214 1.md" "b/LINUX/BASH_script/BASH. \320\247\320\260\321\201\321\202\321\214 1.md" new file mode 100644 index 0000000..abc45f8 --- /dev/null +++ "b/LINUX/BASH_script/BASH. \320\247\320\260\321\201\321\202\321\214 1.md" @@ -0,0 +1,221 @@ +## Введение + +BASH — Bourne-Again SHell (что может переводится как «перерожденный шел», или «Снова шел Борна(создатель sh)»), самый популярный командный интерпретатор в юниксоподобных системах, в особенности в GNU/Linux. Ниже приведу ряд встроенных команд, которые мы будем использовать для создания своих скриптов. + + +>`break` выход из цикла for, while или until +> `continue` выполнение следующей итерации цикла for, while или until +> `echo` вывод аргументов, разделенных пробелами, на стандартное устройство вывода +> `exit` выход из оболочки +> `export` отмечает аргументы как переменные для передачи в дочерние процессы в среде +> `hash` запоминает полные имена путей команд, указанных в качестве аргументов, чтобы не искать их при следующем обращении +> `kill` посылает сигнал завершения процессу +> `pwd` выводит текущий рабочий каталог +> `read` читает строку из ввода оболочки и использует ее для присвоения значений указанным переменным.\ +> `return` заставляет функцию оболочки выйти с указанным значением +> `shift` перемещает позиционные параметры налево +> `test` вычисляет условное выражение +> `times` выводит имя пользователя и системное время, использованное оболочкой и ее потомками +> `trap` указывает команды, которые должны выполняться при получении оболочкой сигнала +> `unset` вызывает уничтожение переменных оболочки +> `wait` ждет выхода из дочернего процесса и сообщает выходное состояние. + + +И конечно же кроме встроенных команд мы будем использовать целую кучу внешних, отдельных команд-программ, с которыми мы познакомимся уже в процессе + +## Что необходимо знать с самого начала + +1. Любой bash-скрипт должен начинаться со строки: + +> `#!/bin/bash` + +*в этой строке после #! указывается путь к bash-интерпретатору, поэтому если он у вас установлен в другом месте(где, вы можете узнать набрав `whereis bash`) поменяйте её на ваш путь.* + +2. Коментарии начинаются с символа # (кроме первой строки). +3. В bash переменные не имеют типа(о них речь пойдет ниже) + +## Переменные и параметры скрипта + +Приведу как пример небольшой пример, который мы разберем: + +> `#!/bin/bash` #указываем где у нас хранится bash-интерпретатор +> \#присваиваем переменной parametr1 значение первого параметра скрипта +> `parametr1=$1` +> \#присваиваем переменной script_name значение имени скрипта +> `script_name=$0` +> \# команда echo выводит определенную строку, обращение к переменным осуществляется через $имя_переменной. +> `echo "Вы запустили скрипт с именем $script_name и параметром $parametr1"` +> \# здесь мы видим другие кавычки, разница в том, что в одинарных кавычках не происходит подстановки переменных. +> `echo 'Вы запустили скрипт с именем $script_name и параметром $parametr1'` +> \#Выход с кодом 0 (удачное завершение работы скрипта) +> `exit 0` + + +Результат выполнения скрипта: + +> `ite@ite-desktop:~$ ./test.sh qwerty` +> Вы запустили скрипт с именем ./test.sh и параметром qwerty +> Вы запустили скрипт с именем $script_name и параметром $parametr1 + + +После того как мы познакомились как использовать переменные и передавать скрипту параметры, время познакомиться с зарезервированными переменными: + +> `$DIRSTACK` - содержимое вершины стека каталогов +> `$EDITOR` - текстовый редактор по умолчанию +> `$EUID` - Эффективный UID. Если вы использовали программу su для выполнения команд от другого пользователя, то эта переменная содержит UID этого пользователя, в то время как... +> `$UID` - ...содержит реальный идентификатор, который устанавливается только при логине. +> `$FUNCNAME` - имя текущей функции в скрипте. +> `$GROUPS` - массив групп к которым принадлежит текущий пользователь +> `$HOME` - домашний каталог пользователя +> `$HOSTNAME` - ваш hostname +> `$HOSTTYPE` - архитектура машины. +> `$LC_CTYPE` - внутренняя переменная, котороя определяет кодировку символов +> `$OLDPWD` - прежний рабочий каталог +> `$OSTYPE` - тип ОС +> `$PATH` - путь поиска программ +> `$PPID` - идентификатор родительского процесса +> `$SECONDS` - время работы скрипта(в сек.) +> `$#` - общее количество параметров переданных скрипту +> `$*` - все аргументы переданыне скрипту(выводятся в строку) +> `$@` - тоже самое, что и предыдущий, но параметры выводятся в столбик +> `$!` - PID последнего запущенного в фоне процесса +> `$$` - PID самого скрипта + +> IFS=$'\n' - переназначение переменной среды на 'только перевод строки' +> IFS=: - переназначение переменной среды на ' : ' + +## Условия + +Условные операторы, думаю, знакомы практически каждому, кто хоть раз пытался на чем-то писать программы. В bash условия пишутся след. образом (как обычно на примере): + + +> `#!/bin/bash` +> \#в переменную source засовываем первый параметр скрипта +> `source=$1` +> \#в переменную dest засовываем второй параметр скрипта +> `dest=$2` +> +> \# в ковычках указываем имена переменных для сравнения. -eq - логическое сравнение обозначающие "равны" +> `if [[ "$source" -eq "$dest" ]]` +> \# если они действительно равны, то +> `then` +> \#выводим сообщение об ошибке, т.к. $source и $dest у нас равны +> `echo "Применик $dest и источник $source один и тот же файл!"` +> \# выходим с ошибкой (1 - код ошибки) +> `exit 1` +> \# если же они не равны +> `else` +> \# то выполняем команду cp: копируем источник в приемник +> `cp $source $dest` +> `echo "Удачное копирование!"` +> `fi` #обозначаем окончание условия. + + +Результат выполнения скрипта: + + +> `ite@ite-desktop:~$ ./primer2.sh 1 1` +> Применик 1 и источник 1 один и тот же файл! + +> `ite@ite-desktop:~$ ./primer2.sh 1 2` +> Удачное копирование! + +Структура `if-then-else` используется следующим образом: + +> `if` <команда или набор команд возвращающих код возврата(0 или 1)> +> `then` +> <если выражение после if истино, то выполняется этот блок> +> `else` +> <если выражение после if ложно, тот этот> + + +В качестве команд возвращающих код возврата могут выступать структуры `[[ , [ , test, (( ))` или любая другая(или несколько) linux-команда. + + +> `test` - используется для логического сравнения. после выражения, неоьбходима закрывающая скобка "]" +>`[` - синоним команды test +>`[[` - расширенная версия "[" (начиная с версии 2.02)(как в примере), внутри которой могут быть использованы || (или), & (и). Долна иметь закрывающуб скобку "]]" +>`(( ))` - математическое сравнение. + + +для построения многоярусных условий вида: + +> `if ...` +>`then ....` +>`else` +>`if ....` +>`then....` +>`else ....` + +для краткости и читаемости кода, можно использовать структуру: + +> `if ..` +> `then ...` +> `elif ...` +> `then ...` +> `elif ...` + + +## Условия. Множественный выбор + +Если необходимо сравнивать какоую-то одну переменную с большим количеством параметров, то целесообразней использовать оператор case. + +> `#!/bin/bash` +> `echo "Выберите редатор для запуска:"` +> `echo "1 Запуск программы nano"` +> `echo "2 Запуск программы vi"` +> `echo "3 Запуск программы emacs"` +> `echo "4 Выход"` +\#здесь мы читаем в переменную $doing со стандартного ввода +> `read doing` +> +> `case $doing in` +> `1)` +> `/usr/bin/nano` # если $doing содержит 1, то запустить nano +> `;;` +> `2)` +> `/usr/bin/vi` # если $doing содержит 2, то запустить vi +> `;;` +> `3)` +> `/usr/bin/emacs` # если $doing содержит 3, то запустить emacs +> `;;` +> `4)` +> `exit 0` +> `;;` +> `*)` #если введено с клавиатуры то, что в case не описывается, выполнять следующее: +> `echo "Введено неправильное действие"` +> +> `esac` #окончание оператора case. + +Результат работы: + +> `ite@ite-desktop:~$ ./menu2.sh` +> Выберите редатор для запуска: +> 1 Запуск программы nano +> 2 Запуск программы vi +> 3 Запуск программы emacs +> 4 Выход + +После выбор цифры и нажатия Enter запуститься тот редактор, который вы выбрали(если конечно все пути указаны правильно, и у вас установлены эти редакторы :) ) + + +Прведу список логических операторв, которые используются для конструкции if-then-else-fi: + +> `-z` # строка пуста +> `-n` # строка не пуста +> `=, (==)` # строки равны +> `!=` # строки неравны +> +> `-eq` # равно +> `-ne` # неравно +> `-lt,(< )` # меньше +> `-le,(<=)` # меньше или равно +> `-gt,(>)` # больше +> `-ge,(>=)` # больше или равно +> `!` # отрицание логического выражения +> +> `-a,(&&)` # логическое «И» +> `-o,(||)` # логическое «ИЛИ» + + +С основами языка и условиями мы разобрались, чтобы не перегружать статью, разобью её на несколько частей(допустим на 3). Во второй части разберем операторы цикла и выполнение математических операций. diff --git "a/LINUX/BASH_script/BASH. \320\247\320\260\321\201\321\202\321\214 2.md" "b/LINUX/BASH_script/BASH. \320\247\320\260\321\201\321\202\321\214 2.md" new file mode 100644 index 0000000..dea8c45 --- /dev/null +++ "b/LINUX/BASH_script/BASH. \320\247\320\260\321\201\321\202\321\214 2.md" @@ -0,0 +1,223 @@ +## Циклы. Цикл for-in. + +Оператор `for-in` предназначен для поочередного обращения к значениям перечисленным в списке. Каждое значение поочередно в списке присваивается переменной. +Синтаксис следующий: + +``` +for переменная in список_значений +do +команды +done +``` + +Рассмотрим небольшой пример: + +``` +#!/bin/bash +for i in 0 1 2 3 4 #переменной $i будем поочередно присваивать значения от 0 до 4 включительно +do +echo "Console number is $i" >> /dev/pts/$i #Пишем в файл /dev/pts/$i(файл виртуального терминала) строку "Console number is $i" +done #цикл окончен +exit 0 +``` + +После выполнения примера в первых 5 виртуальных консолях(терминалах) появится строка с её номером. В переменную `$i` поочередно подставляются значения из списка и в цикле идет работа со значением этой переменной + +## Циклы. Цикл while. + +Цикл `while` сложнее цикла `for-in` и используется для повторения команд, пока какое-то выражение истинно( код возврата = 0). +Синтаксис оператора следующий: + +``` +while выражение или команда возвращающая код возврата +do +команды +done +``` + +Пример работы цикла рассмотрим на следующем примере: + +``` +#!/bin/bash +again=yes #присваиваем значение "yes" переменной again +while [ "$again" = "yes" ] #Будем выполнять цикл, пока $again будет равно "yes" +do +echo "Please enter a name:" +read name +echo "The name you entered is $name" + +echo "Do you wish to continue?" +read again +done +echo "Bye-Bye" +``` + + +А теперь результат работы скрипта: +``` +ite@ite-desktop:~$ ./bash2_primer1.sh +Please enter a name: +ite +The name you entered is ite +Do you wish to continue? +yes +Please enter a name: +mihail +The name you entered is mihail +Do you wish to continue? +no +Bye-Bye +``` + +Как видим цикл выполняется до тех пор, пока мы не введем что-то отличное от «yes». Между `do` и `done` можно описывать любые структуры, операторы и т.п., все они будут выполнятся в цикле.Но следует быть осторожным с этим циклом, если вы запустите на выполнение в нём какую-либо команду, без изменения переменной выражения, вы можете попасть в бесконечный цикл. + +Теперь об условии истинности. После `while`, как и в условном операторе `if-then-else` можно вставлять любое выражение или команду, которая возвращает код возврата, и цикл будет исполнятся до тех пор, пока код возврата = 0! Оператор `[` аналог команды `test`, которая проверяет истинность условия, которое ей передали. + +Рассмотрим еще один пример, я взял его из книги Advanced Bash Scripting. Уж очень он мне понравился :), но я его немного упростил. В этом примере мы познакомимся с еще одним типом циклов **UNTIL-DO**. Эта практически полный аналог цикла **WHILE-DO**, только выполняется пока какое-то выражение ложно. +Вот пример: +``` +#!/bin/bash +echo "Введите числитель: " +read dividend +echo "Введите знаменатель: " +read divisor + +dnd=$dividend #мы будем изменять переменные dividend и divisor, +#сохраним их знания в других переменных, т.к. они нам +#понадобятся +dvs=$divisor +remainder=1 + +until [ "$remainder" -eq 0 ] +do +let "remainder = dividend % divisor" +dividend=$divisor +divisor=$remainder +done + +echo "НОД чисел $dnd и $dvs = $dividend" +``` + +Результат выполнения скрипта: +``` +ite@ite-desktop:~$ ./bash2_primer3.sh +Введите числитель: +100 +Введите знаменатель: +90 +НОД чисел 100 и 90 = 10 +``` + +## Математические операции + +Команда `let`. +Команда `let` производит арифметические операции над числами и переменными. +Рассмотрим небольшой пример, в котором мы производим некоторые вычисления над введенными числами: + +``` +#!/bin/bash +echo "Введите a: " +read a +echo "Введите b: " +read b + +let "c = a + b" #сложение +echo "a+b= $c" +let "c = a / b" #деление +echo "a/b= $c" +let "c <<= 2" #сдвигает c на 2 разряда влево +echo "c после сдвига на 2 разряда: $c" +let "c = a % b" # находит остаток от деления a на b +echo "$a / $b. остаток: $c " +``` + +Результат выполнения: +``` +ite@ite-desktop:~$ ./bash2_primer2.sh +Введите a: +123 +Введите b: +12 +a+b= 135 +a/b= 10 +c после сдвига на 2 разряда: 40 +123 / 12. остаток: 3 +``` + + +Ну вот, как видите ничего сложного, список математических операций стандартный: + +> `+` — сложение +> `—` — вычитание +> `*` — умножение +> `/` — деление +> `**` — возведение в степень +> `%` — модуль(деление по модулю), остаток от деления +> `let` позволяет использовать сокращения арифметических команд, тем самым сокращая кол-во используемых переменных. Например: `a = a+b` эквивалентно `a +=b` и т.д + +## Работа с внешними программами при написании shell-скриптов + +Для начала немного полезной теории. +### Перенаправление потоков. + +В **bash** (как и многих других оболочках) есть встроенные файловые дескрипторы: `0 (stdin)`, `1 (stdout)`, `2 (stderr)`. +`stdout` — Стандартный вывод. Сюда попадает все что выводят программы +`stdin` — Стандартный ввод. Это все что набирает юзер в консоли +`stderr` — Стандартный вывод ошибок. +Для операций с этими дескрипторами, существуют специальные символы: `>` (перенаправление вывода), `<` (перенаправление ввода). Оперировать ими не сложно. Например: + +``` +cat /dev/random > /dev/null #перенаправить вывод команды cat /dev/random в /dev/null (абсолютно бесполезная операция :)) ) +``` +или + +``` +ls -la > listing #записать в файл listing содержание текущего каталога (уже полезней) +``` + +Если есть необходимость дописывать в файл(при использовании "`>`" он заменятеся), необходимо вместо "`>`" использовать "`>>`" +``` +sudo < my_password +``` +после просьбы sudo ввести пароль, он возьмется из файла my_password, как будто вы его ввели с клавиатуры. +Если необходимо записать в файл только ошибки, которые могли возникнуть при работе программы, то можно использовать: +``` +./program_with_error 2> error_file +``` +цифра 2 перед "`>`" означает что нужно перенаправлять все что попадет в дескриптор 2(stderr). +Если необходимо заставить `stderr` писать в `stdout`, то это можно след. образом: +``` +./program_with_error 2>&1 +``` +символ "`&`" означает указатель на дескриптор `1(stdout)` +(Поумолчанию `stderr` пишет на ту консоль, в котрой работает пользователь(вренее пишет на дисплей)). + +### 2. Конвееры. + +Конвеер — очень мощный инструмент для работы с консолью Bash. Синтаксис простой: +`команда1 | команда 2` — означает, что вывод команды 1 передастся на ввод команде 2 +Конвееры можно группировать в цепочки и выводить с помощью перенаправления в файл, например: +``` +ls -la | grep «hash» |sort > sortilg_list +``` +вывод команды `ls -la` передается команде `grep`, которая отбирает все строки, в которых встретится слово *hash*, и передает команде сортировке `sort`, которая пишет результат в файл *sorting_list*. Все довольно понятно и просто. + +Чаще всего скрипты на Bash используются в качестве автоматизации каких-то рутинных операций в консоли, отсюда иногда возникает необходимость в обработке `stdout` одной команды и передача на `stdin` другой команде, при этом результат выполнения одной команды должен быть неким образом обработан. В этом разделе я постораюсь объяснить основные принципы работы с внешними командами внутри скрипта. Думаю что примеров я привел достаточно и можно теперь писать только основные моменты. + +### 1. Передача вывода в переменную. + +Для того чтобы записать в переменную вывод какой-либо команды, достаточно заключить команду в ` `` ` ковычки, например +> a = \` echo "qwerty" \` +> echo $a + +Результат работы: qwerty + +Однако если вы захотите записать в переменную список директорий, то необходимо, должным образом обработать результат для помещения данных в переменную. Рассмотрим небольшой, пример: +``` +LIST=`find /svn/ -type d 2>/dev/null| awk '{FS="/"} {print $4}'| sort|uniq | tr '\n' ' '` +for ONE_OF_LIST in $LIST +do +svnadmin hotcopy /svn/$ONE_OF_LIST /svn/temp4backup/$ONE_OF_LIST +done +``` +Здесь мы используем цикл `for-do-done` для архивирование всех директорий в папке */svn/* с помощью команды `svnadmin hotcopy`(что в нашем случае не имеет никого значения, просто как пример). Наибольшй интерес вызывает строка: LIST=`find /svn/ -type d 2>/dev/null| awk '{FS="/"} {print $4}'| sort|uniq | tr '\n' ' '` В ней переменной LIST присваивается выполнение команды find, обработанной командами awk, sort, uniq,tr(все эти команды мы рассматривать не будем, ибо это отдельная статья). В переменной LIST будут имена всех каталогов в папке /svn/ пгомещенных в одну строку(для того чтобы её стравить циклу. \ No newline at end of file diff --git "a/LINUX/BASH_script/\320\236\320\261\320\275\320\276\320\262\320\273\320\265\320\275\320\270\320\265 \321\201\320\277\320\270\321\201\320\272\320\260 \320\277\320\260\320\272\320\265\321\202\320\276\320\262 \320\270 \321\203\321\201\321\202\320\260\320\275\320\276\320\262\320\272\320\260 \320\276\320\261\320\275\320\276\320\262\320\273\320\265\320\275\320\270\320\271.md" "b/LINUX/BASH_script/\320\236\320\261\320\275\320\276\320\262\320\273\320\265\320\275\320\270\320\265 \321\201\320\277\320\270\321\201\320\272\320\260 \320\277\320\260\320\272\320\265\321\202\320\276\320\262 \320\270 \321\203\321\201\321\202\320\260\320\275\320\276\320\262\320\272\320\260 \320\276\320\261\320\275\320\276\320\262\320\273\320\265\320\275\320\270\320\271.md" new file mode 100644 index 0000000..bfa1302 --- /dev/null +++ "b/LINUX/BASH_script/\320\236\320\261\320\275\320\276\320\262\320\273\320\265\320\275\320\270\320\265 \321\201\320\277\320\270\321\201\320\272\320\260 \320\277\320\260\320\272\320\265\321\202\320\276\320\262 \320\270 \321\203\321\201\321\202\320\260\320\275\320\276\320\262\320\272\320\260 \320\276\320\261\320\275\320\276\320\262\320\273\320\265\320\275\320\270\320\271.md" @@ -0,0 +1,24 @@ +```sh +#!/bin/bash + +# Обновление списка пакетов и установка обновлений +echo "----------------------------" +echo "Обновление системы." +echo "----------------------------" +sudo apt-get update +sudo apt-get upgrade -y + +# Автоматическое удаление ненужных пакетов и очистка кэша +echo "----------------------------" +echo "Очистка системы." +echo "----------------------------" +sudo apt-get autoremove -y +sudo apt-get autoclean -y +sudo apt-get clean + +# Вывод сообщения о завершении +echo "----------------------------" +echo "Система обновлена и очищена." +echo "----------------------------" +``` + diff --git "a/LINUX/BASH_script/\320\241\320\272\321\200\320\270\320\277\321\202\321\213 \320\264\320\273\321\217 \320\260\320\264\320\274\320\270\320\275\320\276\320\262.md" "b/LINUX/BASH_script/\320\241\320\272\321\200\320\270\320\277\321\202\321\213 \320\264\320\273\321\217 \320\260\320\264\320\274\320\270\320\275\320\276\320\262.md" new file mode 100644 index 0000000..ec58a6a --- /dev/null +++ "b/LINUX/BASH_script/\320\241\320\272\321\200\320\270\320\277\321\202\321\213 \320\264\320\273\321\217 \320\260\320\264\320\274\320\270\320\275\320\276\320\262.md" @@ -0,0 +1 @@ +![[СкриптыДляАдмина.jpg]] \ No newline at end of file diff --git a/LINUX/BAT.md b/LINUX/BAT.md new file mode 100644 index 0000000..99adb16 --- /dev/null +++ b/LINUX/BAT.md @@ -0,0 +1,21 @@ +```sh +brew install bat + +sudo apt install bat + +#-------------------- + +bat +bat -p # открывает без строк нумерации +bat -H 14:17 # подсветка строк 14:17 + +# bat проинтегрирован с GIT: +bat -d # показать только изменения (diff) + +bat --list-themes # темы bat + +bat --themes dark # установить(открыть файл) с темой + + +``` + diff --git a/LINUX/BEGIN.md b/LINUX/BEGIN.md new file mode 100644 index 0000000..d5b7615 --- /dev/null +++ b/LINUX/BEGIN.md @@ -0,0 +1,218 @@ +/boot - 500Mb - ext2 +/ 20Gb - ext4 +swap = озу +/home - остальное + +```sh +# Изменение текстового редактора по умолчанию: +sudo update-alternatives --config editor +``` + +```bash +sudo apt update && sudo apt upgrade -y +sudo apt install mc ranger nload tmux wireguard samba gpg pass smartmontools lm-sensors + +# Диски: +mkdir 1Tb-a 1Tb-temp 2Tb-a 2Tb-b +sudo blkid +lsblk +# редактируем /etc/fstab, монтируем диски и RAID по UUID: +sudo mount -a +sudo systemctl daemon-reload + +# Samba: + + + + + + +sudo apt-get install -f +# Эта команда подтянет нужные пакеты и переустановит проблемный. + + +# Какая версия установлена: +lsb_release -a +``` + +## КОНСОЛЬ клавиши## +```sh +Ctrl-a - перейти в начало +Ctrl-e - перейти в конец + +Ctrl-w - вырезать слово перед курсором +Ctrl-k - вырезать от курсора и до конца строки +Ctrl-u - вырезать от курсора и назад до начала строки +Ctrl-y - вставить вырезанное +Ctrl- <- (Alt- ) +Ctrl- -> (Alt- ) + +Ctrl-u - очищает слева от курсора +Ctrl-k - очищает справа от курсора +Ctrl-r - поиск по истории команд + +history - список всех введенных команд + +reset - исправить вывод + +mkdir 123/name/345 -p - создать папку с полным путем + +cat /var/log/syslog | grep ssh +``` +## Файловая система SWAP ( файл подкачки ) ## +```sh +# Создание swap-пространство на разделе hda3 +mkswap /dev/hda3 + +# Включить swap-пространство, расположенное на разделе hda3 +swapon /dev/hda3 + +# Активировать swap-пространства, расположенные на разделах hda2 и hdb3 +swapon /dev/hda2 /dev/hdb3 +``` +## TERMINAL ## +```bash +# добавить сторонний репозиторий: +sudo add-apt-repository ppa:name_rep/ppa + +# Обновление информации о пакетах и обновление самих пакетов +sudo apt update && sudo apt upgrade -y + +# Установка пакетов +sudo apt install -y htop curl wget build-essential git openssh-server +sudo apt install nload git tmux vim mc wireguard gpg pass + +# Редактор по умолчанию: +update-alternatives --config editor + +# Печатаем сразу в файл +echo «Hello World !» > /tmp/file.txt + + +cat /etc/*-release +whoami +whatis +whereis + +# - выводит файл в консоль +cat (-n) + +# - выводит текст задом на перед +tac + +# - поиск в тексте +grep <какой> file.txt + +# найти и вывести все что начинается с '#': +sudo grep -v ^#/etc/ssh/sshd_config + +# + убрать все пустые строки в выводе: +sudo grep -v ^#/etc/ssh/sshd_config | grep -v ^$ + +# + посчитать кол-во строк в выводе: +sudo grep -v ^#/etc/ssh/sshd_config | grep -v ^$ | wc -l + +# посчитать общее кол-во строк в файле: +sudo wc -l /etc/ssh/sshd_config + +# - если текст длинный и перемещаемся кнопками вверх и вниз если нажать "/" то откроется строка для поиска в этом файле "n" - перемещаемся в поиске вперед и "ctrl+n" - назад "q" - выход +less + +# - последние или первые 10 строк файла `"-f"` онлайн просмотр +tail / head + +# - создание каталога с путем +mkdir -p + +# - переместить/переименовать +mv + +# - просмотр сразу 2 каталогов +ls dir1 dir2 + +# - копирование `-r` - копируем каталог +cp + +# - удаление каталога +rm -r +``` +## GROUP ## +```sh +groupadd # — не выводит ничего при создании группы; лучше при использовании в скрипте, так как нет лишнего вывода на терминал + +addgroup # — выводит создаваемый  при создании группы. +--gid 5555 # - можно задать при создании + +groupdel +delgroup + +# Чтобы пользователю добавить дополнительную группу можно воспользоваться командой  +usermod -aG +-a #— добавляет к текущим группам пользователя дополнительные. Без этой опции группы заменяются на новые, а с этой опцией к старым группам добавляются новые; +-G #— указывает, что работать будем с дополнительными группами а не с первичной. +#(`usermod -aG testgroup alex`) +# Утилитой  нельзя удалять дополнительные группы, можно только не используя опцию **-a**, написать список групп в которые должен будет входить данный пользователь, не указав ту группу, из которой вы хотите удалить пользователя. + +# Если мы хотим поменять пользователю первичную группу, то нужно использовать опцию -g: +usermod -g testgroup alex + +id alex +# uid=1000(alex) gid=1002(testgroup) группы=1002(testgroup),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),109(netdev)` + +# И вернем все как было: +usermod -g alex alex + +# Еще один способ добавить пользователя в дополнительную группу (и наверное более запоминающийся) это использовать утилиту adduser, при этом утилите вначале передается имя пользователя, затем группы: +adduser alex testgroup + +# Удалить пользователя из группы можно командой deluser: +deluser alex testgroup + +# И так, для добавления или удаления пользователя в дополнительную группу я бы рекомендовал использовать команды adduser и deluser. А для изменения первичной группы команду usermod -g. +``` +## PASSWD ## +```sh +passwd - изменить пароль текущему пользователю + +/etc/passwd +/etc/group +/etc/shadow + +# В passwd хранится информация о пользователях в системе, вот информация о пользователе alex: +grep alex /etc/passwd + +alex:x:1000:1000:Alexandr,,,:/home/alex:/bin/bash + +# То что после имени идет буква «x» означает что пароль зашифрован в файле shadow. + +# В /etc/group хранится информация о группах в системе, вот информация о группе alex и о дополнительных группах пользователя alex: +grep alex /etc/group + +cdrom:x:24:alex +floppy:x:25:alex +audio:x:29:alex +dip:x:30:alex +video:x:44:alex +plugdev:x:46:alex +netdev:x:109:alex +alex:x:1000: + +# Здесь вначале идет имя группы, потом символ «x» обозначает что пароль зашифрован, затем gid, затем пользователи Linux для которых эта группа является дополнительной. Таким образом группа alex имеет gid=1000, а пользователь alex входит в дополнительные группы cdrom, floppy и так далее. + +# В /etc/shadow хранятся соленые хэши паролей пользователей, вот например строка для alex: +grep alex /etc/shadow + +alex:$y$j9T$STsC82fbWIlhONPF0rXcm1$a.c./bIw7BUBiewrYoKnEBGBAH3Z5UGsWCFQsW3HOR8:19006:0:99999:7::: + +# Строка файла содержит поля разделённые двоеточиями, в полях находится следующая информация: + +- имя пользователя; +- хеш пароля; +- дата последней смены пароля в днях начиная с 1 января 1970 года, чем больше число, тем новее пароль; +- минимальный срок действия пароля, если 0 то неограничен; +- максимальный срок действия пароля; +- период предупреждения о необходимости сменить пароль; +- период неактивности пароля — у меня пустое поле, период неактивности отсутствует; +- дата истечения срока действия учётной записи — у меня пустое поле, учётная запись никогда не устаревает; +- зарезервированное поле для использования в будущем. +``` diff --git a/LINUX/BorgBackup.md b/LINUX/BorgBackup.md new file mode 100644 index 0000000..d737616 --- /dev/null +++ b/LINUX/BorgBackup.md @@ -0,0 +1,32 @@ +**BorgBackup: безопасные и эффективные бэкапы в Linux** + +**BorgBackup** — это инструмент для создания зашифрованных, сжатых и дедуплицированных резервных копий. Он идеально подходит как для серверов, так и для персональных машин. В этом гайде разберёмся, как его установить, настроить и использовать. + +1️⃣ **Установка BorgBackup** +В большинстве дистрибутивов Borg доступен в официальных репозиториях. +`sudo apt install borgbackup + +2️⃣ **Инициализация репозитория** +Перед созданием бэкапов необходимо инициализировать репозиторий. Это место, где будут храниться резервные копии. +`borg init --encryption=repokey /mnt/backup + +🟢 --encryption=repokey включает шифрование (ключ хранится в репозитории). +🟢 Можно использовать --encryption=none, если защита не нужна. + +3️⃣ **Создание бэкапа** +Допустим, нам нужно сделать резервную копию директорий Documents и Pictures: +`borg create --progress /mnt/backup::backup-$(date +%F) ~/Documents ~/Pictures + +🟢 /mnt/backup — путь к репозиторию. +🟢 backup-$(date +%F) — имя бэкапа с датой. +🟢 ~/Documents ~/Pictures — папки, которые сохраняем. +🟢 --progress — показывает прогресс выполнения. + +4️⃣ **Проверка и восстановление данных** +Посмотреть список всех бэкапов: +`borg list /mnt/backup + +Извлечь конкретный бэкап (например, backup-2025-01-28): +`borg extract /mnt/backup::backup-2025-01-28 --progress + +🔜 Если хотите автоматизировать создание бэкапов, можно добавить cron-задачу. \ No newline at end of file diff --git a/LINUX/CAL.md b/LINUX/CAL.md new file mode 100644 index 0000000..0d2995f --- /dev/null +++ b/LINUX/CAL.md @@ -0,0 +1,9 @@ +```sh +sudo apt install ncal +cal # отображает календарь в терминале.  + +cal # показывает текущий месяц, +cal 2025 # показывает календарь на 2025 год. + +# Идеально подходит для быстрого поиска дат! +``` diff --git a/LINUX/CERTBOT.md b/LINUX/CERTBOT.md new file mode 100644 index 0000000..c8b1b45 --- /dev/null +++ b/LINUX/CERTBOT.md @@ -0,0 +1,123 @@ +# [Создание сертификата Let’s Encrypt для Nginx в Ubuntu 20.04](https://www.8host.com/blog/sozdanie-sertifikata-lets-encrypt-dlya-nginx-v-ubuntu-20-04/ "Создание сертификата Let’s Encrypt для Nginx в Ubuntu 20.04") + +4 августа, 2020 12:20 + +Let’s Encrypt – это центр сертификации (ЦС, или Certificate Authority, CA), который предоставляет простой способ создания бесплатных сертификатов TLS/SSL, тем самым позволяя шифровать HTTPS на веб-серверах. Этот ЦС упрощает процесс создания сертификатов путём автоматизации большинства необходимых действий при помощи клиентского ПО. Процесс получения и установки сертификата полностью автоматизирован на веб-серверах Apache и Nginx. + +Данный мануал расскажет, как получить SSL-сертификат для Nginx в системе Ubuntu 20.04 с помощью Certbot и как обновлять этот сертификат. + +В мануале используется отдельный виртуальный хост Nginx, а не хост по умолчанию. Рекомендуем вам создать такой хост для каждого домена, чтобы избежать общих ошибок и иметь возможность использовать стандартные конфигурации в случае сбоя. + +### Требования + +- Сервер Ubuntu 20.04, настроенный по [этому мануалу](https://www.8host.com/blog/nachalnaya-nastrojka-servera-ubuntu-20-04/). +- Доменное имя, которое можно использовать в сертификате (в руководстве используется условный домен example.com). +- Записи А, связывающие домены example.com и www.example.com с внешним IP-адресом сервера. Они необходимы для того, чтобы Let’s Encrypt мог проверить доменное имя +- Установленный сервер Nginx и блок server для вашего домена (согласно [этому мануалу](https://www.8host.com/blog/ustanovka-nginx-v-ubuntu-20-04/)). Здесь в качестве примера используется хост /etc/nginx/sites-available/example.com. + +## 1: Установка Certbot + +Для начала нужно установить клиент Certbot на ваш сервер. + +Чтобы установить пакет certbot и плагин для Nginx с помощью стандартного пакетного менеджера, введите команду: + +`sudo apt install certbot python3-certbot-nginx` + +Теперь Certbot готов к работе, но для того, чтобы настроить SSL для Nginx, нужно проверить некоторые конфигурации. + +## 2: Проверка параметров Nginx + +Certbot должен иметь возможность найти целевой виртуальный хост (блок server) в конфигурации Nginx, чтобы автоматически настроить сертификат SSL. Для этого Certbot ищет директиву server_name – в ней указан домен, для которого предназначен запрашиваемый сертификат. + +Если вы следовали разделу по установке виртуального хоста в [мануале по установке Nginx](https://www.8host.com/blog/ustanovka-nginx-v-ubuntu-20-04/), у вас должен быть виртуальный хост для домена в ==`/etc/nginx/sites-available/example.com`==, а директива server_name в нем уже настроена соответствующим образом. + +Чтобы проверить это, откройте файл виртуального хоста, используя nano или другой текстовый редактор: + +`sudo nano /etc/nginx/sites-available/example.com` + +Найдите строку server_name: + +`... server_name example.com www.example.com; ...` + +Если она выглядит так, можете закрыть редактор. Если нет, вам нужно обновить ее. Укажите в ней свой домен, а затем сохраните и закройте файл. После этого нужно проверить синтаксис конфигурации. + +`sudo nginx -t` + +Если команда сообщила об ошибке, вам нужно снова открыть файл и исправить ее. После этого повторите проверку. + +Если ошибок в конфигурации нет, перезапустите веб-сервер. + +`sudo systemctl reload nginx` + +Теперь Certbot сможет найти правильный блок server и автоматически обновить его. + +## 3: Настройка брандмауэра + +Если во время настройки сервера вы включили брандмауэр ufw, вам нужно настроить его для поддержки соединений HTTPS. + +Во время установки Nginx регистрирует в ufw несколько профилей. Просмотреть текущие настройки можно с помощью команды: + +`sudo ufw status` + +Скорее всего, брандмауэр пока что поддерживает только трафик HTTP: + +`Status: active +To                         Action      From +--                         ------      ---- +OpenSSH                    ALLOW       Anywhere +Nginx HTTP                 ALLOW       Anywhere +OpenSSH (v6)               ALLOW       Anywhere (v6) +Nginx HTTP (v6)            ALLOW       Anywhere (v6)` + +Чтобы разблокировать трафик HTTPS, активируйте профиль Nginx Full и удалите профиль Nginx. + +`sudo ufw allow 'Nginx Full' sudo ufw delete allow 'Nginx HTTP'` + +Теперь параметры брандмауэра будут выглядеть так: + +`sudo ufw status +Status: active +To                         Action      From +--                         ------      ---- +OpenSSH                    ALLOW       Anywhere +Nginx Full                 ALLOW       Anywhere +OpenSSH (v6)               ALLOW       Anywhere (v6) +Nginx Full (v6)            ALLOW       Anywhere (v6)` + +Теперь пора запустить Certbot и получить сертификаты. + +## 4: Получение SSL-сертификата + +Certbot предлагает различные способы получения сертификатов SSL через плагины. Плагин Nginx позаботится о реконфигурации веб-сервера и при необходимости перезагрузит его. Чтобы использовать этот плагин, введите: + +`sudo certbot --nginx -d example.com -d www.example.com` + +Эта команда запустит certbot с плагином — nginx, а флаг –d определит имена, для которых предназначен сертификат. + +Если вы запускаете certbot впервые, вам будет предложено ввести адрес электронной почты и принять условия обслуживания. После этого certbot свяжется с сервером Let’s Encrypt, а затем отправит запрос, чтобы убедиться, что вы контролируете домен, для которого запрашиваете сертификат. + +Если проверка пройдет успешно, certbot спросит, как вы хотите настроить HTTPS: + +`Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1: No redirect - Make no further changes to the webserver configuration. 2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for new sites, or if you're confident your site works on HTTPS. You can undo this change by editing your web server's configuration. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Select the appropriate number [1-2] then [enter] (press 'c' to cancel):` + +Сделайте свой выбор, затем нажмите Enter. Конфигурация будет обновлена, и Nginx перезагрузится, чтобы использовать новые настройки. Клиент certbot выведет сообщение о том, что процесс был выполнен успешно и скажет, где хранятся ваши сертификаты: + +`IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/example.com/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/example.com/privkey.pem Your cert will expire on 2020-08-18. To obtain a new or tweaked version of this certificate in the future, simply run certbot again with the "certonly" option. To non-interactively renew *all* of your certificates, run "certbot renew" - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate Donating to EFF:                    https://eff.org/donate-le` + +Сертификаты будут загружены и установлены. Попробуйте перезагрузить свой сайт, указав https://, и обратите внимание на индикатор безопасности в браузере. Он должен указывать, что сайт надежно защищен (как правило, для этого используется зеленый замочек в адресной строке). Если вы протестируете свой сервер с помощью [SSL Labs Server Test](https://www.ssllabs.com/ssltest/), он получит оценку A. + +Теперь давайте протестируем процесс обновления сертификата. + +## 5: Проверка автоматического обновления сертификата + +Сертификаты Let’s Encrypt действительны только в течение девяноста дней. Потому пользователи должны автоматизировать процесс продления сертификата. Установленный вами пакет certbot позаботится об этом с помощью таймера systemd. Он запускается два раза в день и автоматически обновляет сертификат, срок действия которого истекает через тридцать дней. + +Чтобы проверить статус таймера, введите: + +`sudo systemctl status certbot.timer certbot.timer - Run certbot twice daily Loaded: loaded (/lib/systemd/system/certbot.timer; enabled; vendor preset: enabled) Active: active (waiting) since Mon 2020-05-04 20:04:36 UTC; 2 weeks 1 days ago Trigger: Thu 2020-05-21 05:22:32 UTC; 9h left Triggers: ● certbot.service` + +Чтобы протестировать процесс обновления сертификата, запустите сухой прогон: + +`sudo certbot renew --dry-run` + +Если вы не видите ошибок, все настроено правильно. При необходимости Certbot обновит ваши сертификаты и перезагрузит Nginx, чтобы активировать изменения. Если процесс автоматического обновления не срабатывает, Let’s Encrypt отправит сообщение на указанный вами адрес электронной почты, предупредив вас о завершении срока действия вашего сертификата. \ No newline at end of file diff --git a/LINUX/CHMOD, CHOWN.md b/LINUX/CHMOD, CHOWN.md new file mode 100644 index 0000000..97a50b8 --- /dev/null +++ b/LINUX/CHMOD, CHOWN.md @@ -0,0 +1,79 @@ +## CHMOD ## +```sh +chmod опции права /путь/к/файлу + +# Сначала рассмотрим какими бывают права доступа linux и как они устанавливаются. Пред этим рекомендую прочитать статью про права, ссылка на которую есть выше. Есть три основных вида прав: + +- r - чтение; +- w - запись; +- x - выполнение; +- s - выполнение  от имени суперпользователя (дополнительный); + +# Также есть три категории пользователей, для которых вы можете установить эти права на файл linux: + +- u - владелец файла; +- g - группа файла; +- o - все остальные пользователи; + +# Синтаксис настройки прав такой: +# Группа_пользователейДействиеВид_прав + +"+" - включить или  +"-" - отключить. + +- u+x - разрешить выполнение для владельца; +- ugo+x - разрешить выполнение для всех; +- ug+w - разрешить запись для владельца и группы; +- o-x - запретить выполнение для остальных пользователей; +- ugo+rwx - разрешить все для всех; + +# Но права можно записывать не только таким способом. Есть еще восьмеричный формат записи, он более сложен для понимания, но пишется короче и проще. Я не буду рассказывать как считать эти цифры, просто запомните какая цифра за что отвечает, так проще: + +# rwx = 7 | x=1 w=2 r=4 + +- 0 - никаких прав; +- 1 - только выполнение; +- 2 - только запись; +- 3 - выполнение и запись; +- 4 -  только чтение; +- 5 - чтение и выполнение; +- 6 - чтение и запись; +- 7 - чтение запись и выполнение. + +# Права на папку linux такие же, как и для файла. Во время установки прав сначала укажите цифру прав для владельца, затем для группы, а потом для остальных. Например, : + +- 744 - разрешить все для владельца, а остальным только чтение; +- 755 - все для владельца, остальным только чтение и выполнение; +- 764 - все для владельца, чтение и запись для группы, и только чтение для остальных; +- 777 - всем разрешено все. + +Каждая из цифр не зависит от предыдущих, вы вбираете именно то, что вам нужно. Теперь давайте рассмотрим несколько опций команды, которые нам понадобятся во время работы: + +-c - выводить информацию обо всех изменениях; +-f - не выводить сообщения об ошибках; +-v - выводить максимум информации; +--preserve-root - не выполнять рекурсивные операции для корня "/"; +--reference - взять маску прав из указанного файла; +-R - включить поддержку рекурсии; +--version - вывести версию утилиты; +``` + +## CHOWN ## +```sh +chown пользователь:группа опции /путь/к/файлу + +# В поле пользователь надо указать пользователя, которому мы хотим передать файл. Также можно указать через двоеточие группу, например, пользователь:группа. Тогда изменится не только пользователь, но и группа. Вот основные опции, которые могут вам понадобиться: + +-c, --changes - подробный вывод всех выполняемых изменений; +-f, --silent, --quiet - минимум информации, скрыть сообщения об ошибках; +--dereference - изменять права для файла к которому ведет символическая ссылка вместо самой ссылки (поведение по умолчанию); +-h, --no-dereference - изменять права символических ссылок и не трогать файлы, к которым они ведут; +--from - изменять пользователя только для тех файлов, владельцем которых является указанный пользователь и группа; +-R, --recursive - рекурсивная обработка всех подкаталогов; +-H - если передана символическая ссылка на директорию - перейти по ней; +-L - переходить по всем символическим ссылкам на директории; +-P - не переходить по символическим ссылкам на директории (по умолчанию). +``` + + +![[2024-09-05 01.32.11.jpg]] \ No newline at end of file diff --git a/LINUX/CIFS.md b/LINUX/CIFS.md new file mode 100644 index 0000000..163bff7 --- /dev/null +++ b/LINUX/CIFS.md @@ -0,0 +1,189 @@ +```sh +sudo apt install cifs-utils -y + +# Монтирование ресурса выглядит так: +mount.cifs {ресурс} {путь к директории в которую монтируем } + +# Автоматическое монтирование директории происходит с использованием файла fstab. Для начала создадим дополнительный файл с учетными данными пользователя для авторизации. Оправданно будет создать этот файл в директории root. +sudo vim /root/.smbselus + +# вносим в файл: +username = vlapa +password = uspass + +sudo vim /etc/fstab + +# добавляем: +//10.10.10.2/что_монтируем /куда_монтирем cifs user,rw,crdentials=/root/.smbselus 0 0 + +# ======= +cifs user 一 разрешает монтировать любому пользователю; +rw 一 предоставляет права на чтение и запись; +credentials=/root/.smbselus 一 файл с учетными данными пользователя; +_0_ используется утилитой dump 0 一 выключен; +_0_ используется утилитой fsck 0 一 выключен. + +В некоторых случаях требуется добавить директиву domain в файл .smbselus. + +#================ +# Настройка сетевой корзны: +sudo mkdir /var/recycle +sudo chmod 777 /var/recycle + +sudo vim /etc/samba/smb.conf + +[Recycle] + comment = rec_dir + path = /var/recycle + public = yes + browseable = yes + writable = yes + vfs objects = recycle + recycle:repository = .recycle/%U + recycle:keeptree = Yes + recycle:touch = Yes + recycle:version = Yes + recycle:maxsize = *.tmp, ~$* + recycle:exclude_dir = /tmp + +#============== +[Recycle] 一 имя ресурса, отображаемое клиенту при подключении                                                                       + +comment = rec_dir 一 комментарий + +path = /var/recycle 一 директория, в которой размещена сетевая корзина + +public = yes 一 доступ на чтение всем авторизованным пользователям, также гостевым  + +browseable = yes 一 параметр устанавливает, будет ли общий ресурс отображаться в списке доступных общих ресурсов в сетевом окружении и в списке просмотра + +writable = yes 一 право записи в сетевой ресурс + +vfs objects = recycle 一 использование подсистемы recycle + +recycle:repository = .recycle/%U 一 место хранения удаленных объектов. В этой директиве настроено попадание удаленных файлов в каталог с именем пользователя, который создается автоматически в директории .recycle + +recycle:keeptree = Yes 一 удаление с сохранением дерева каталогов + +recycle:touch = Yes 一 изменение даты при перемещении файла в сетевую корзину + +recycle:versions = Yes 一 добавление номера версии при удалении файлов с одинаковыми именами + +recycle:maxsize = 0 一 параметр отвечает за максимальный размер файла, который может быть перемещен в корзину. При текущем значении возможно переместить файл любого размера + +recycle:exclude = *.tmp, ~$*  一 исключение файлов заданного типа + +recycle:exclude_dir = /tmp 一 исключение директории заданного типа + +``` +# Как в Linux монтировать шару CIFS + + ![Обновлено](https://www.dmosk.ru/img/public/time.jpg "Дата обновления") Обновлено: 19.06.2024  ![Опубликовано](https://www.dmosk.ru/img/public/time.jpg "Дата публикации") Опубликовано: 18.07.2016 + +Работа с общими папками Windows происходит с использованием протокола CIFS (SMB). Все примеры в данном руководстве выполняются на Linux Ubuntu и CentOS. + +### Установка пакетов + +`apt install cifs-utils + +### Сетевые порты + +Если мы будем монтировать сетевую папку, сервер которой находится за брандмауэром, необходимо открыть следующие порты: + +- 137/UDP +- 138/UDP +- 139/TCP +- 445/TCP + +## Синтаксис + +`mount.cifs <папка на сервере> <во что монтируем> <-o опции> + +_* вместо **mount.cifs** можно написать **mount -t cifs**._ + +Пример: + +`mount.cifs //192.168.1.1/public /mnt + +_* простой пример монтирования папки **public** на сервере **192.168.1.1** в локальный каталог **/mnt**._ + +`apt install samba-client +`smbclient -L 192.168.1.1 + + или, при необходимости авторизоваться на файловом сервере:> +`smbclient -L 192.168.1.1 -U username + +## Ручное монтирование + +Теперь монтирование можно выполнить следующей командой: + +`mount.cifs //192.168.1.10/share /mnt -o user=dmosk + +_* в данном примере будет примонтирован каталог **share** на сервере **192.168.1.10** в локальную папку **/mnt** под учетной записью **dmosk**._ + +То же самое, с использованием домена: + +`mount.cifs //192.168.1.10/share /mnt -o user=dmosk,domain=dmosk.local + +## Автоматическое монтирование CIFS через fstab + +Для начала создаем файл, в котором будем хранить данные авторизации при подключении к общей папке: + +vim /root/.smbclient + +И добавляем в него данные следующего вида: + +```sh +username=dmosk +password=dPassw0rd +domain=dmosk.local +``` +_* в этом примере создана пара логин/пароль — **dmosk**/**dPassw0rd**;_ **domain** указывать не обязательно, если аутентификация выполняется без него. + +Задаем права на созданный файл, чтобы доступ был только у пользователя, скажем, root: +`chmod 600 /root/.smbclient +`chown root:root /root/.smbclient + +Теперь открываем конфигурационный файл fstab: +`vim /etc/fstab + +и добавляем в него следующее: +`//192.168.1.10/share /mnt cifs user,rw,credentials=/root/.smbclient 0 0 + +_* в данном примере выполняется монтирование общей папки **share** на сервере с IP-адресом **192.168.1.10** в каталог **/mnt**. Параметры для подключения — **user**: позволяет выполнить монтирование любому пользователю, **rw**: с правом на чтение и запись, **credentials**: файл, который мы создали на предыдущем шаге._ + +Чтобы проверить правильность настроек, вводим следующую команду: +`mount -a + +## Примеры использования опций + +### Версии SMB + +Если на стороне Windows используется старая или слишком новая версия протокола SMB, при попытке монтирования мы можем получить ошибку **mount error(112): Host is down**. Чтобы это исправить, указываем версию: +`mount.cifs //192.168.1.10/share /mnt/ -o vers=1.0 + +_* монтирование по протоколу SMB1.0_ + +### Монтирование от гостевой учетной записи + +Если сервер принимает запросы без логина и пароля, то клиент подключается, как гость: +`mount.cifs //192.168.1.10/share /mnt -o guest + +или в fstab: +`//192.168.1.10/share    /mnt    cifs    guest    0 0 + +Для монтирования шары Windows, иногда, недостаточно указать опцию **guest**. Нужно делать так: +`mount.cifs //192.168.1.10/share /mnt -o user=guest,pass='' + +Или в fstab: +`//192.168.1.10/share    /mnt    cifs    guest,pass    0 0 + +### Права на примонтированные каталоги + +При монтировании папки мы можем указать определенные права: +`mount.cifs //192.168.1.10/share /mnt -o file_mode=0777,dir_mode=0777 + +Для указания владельца, который будет назначен для примонтированного каталога, используем: + `mount.cifs //192.168.1.10/share /mnt -o uid=33,gid=33 + +_* чтобы посмотреть идентификаторы пользователя, вводим **id -u <имя пользователя>** и **id -g <имя группы>**._ \ No newline at end of file diff --git a/LINUX/CRON.md b/LINUX/CRON.md new file mode 100644 index 0000000..ca554dc --- /dev/null +++ b/LINUX/CRON.md @@ -0,0 +1,93 @@ +_www.crontab.guru_ + + + +```sh +systemctl status cron +crontab --help + +crontab -e # настройка для текущего пользователя +crontab -l # настройка для супер пользователя +crontab -r # удалить все задачи +``` + +**_минута_** **_час_** **_день_** **_месяц_** **_день_недели_** **_/путь/к/исполняемому/файлу_** +**обязательно нужно писать полный путь к команде** **_!_** +**Дата и время указываются с помощью цифр или символа ' * '.** +**интервал указывается «** **7-9** **»** +**несколько раз - «** **5,7,12 »** +**раз в 10 мин - « /10 »** + +****@reboot**** **- при загрузке, только один раз;** +****@yearly, @annually**** - раз год; +****@monthly**** - раз в месяц; +****@weekly**** - раз в неделю; +****@daily, @midnight**** - каждый день; +****@hourly**** - каждый час. + +команда запуска скрипта раз в час: + +`@hourly /usr/local/bin/serve` + +Если же вы собрались добавить скрипт в одну из папок, то, как я уже говорил, нужно чтобы его имя было без точек и у него были права на выполнение: + +`sudo vi /etc/corn.daily/basckup` + +`#!/bin/bash ......` + +смотрим лог cron. Иногда он находится в `/var/log/cron`, +а иногда пишется в syslog. + +`grep CRON /var/log/syslog` + +`sudo run-parts /etc/cron.daily/` + +Дальше вы увидите весь вывод, включая вывод скрипта и сможете быстро понять в чем проблема. + + +Можно еще отправлять результат работы скрипта на почту указав команду MAILTO=user@email.ru + +`tail /var/log/syslog` +-получаем "хвост", последние 10 строк лог-файла + +``` +@hourly rsync -avzhe ssh vlapa@178.20.46.157:/home/vlapa/git/ /home/vlapa/1Tb_r1-a/Backup/_git/ + +@hourly rsync -avzh /home/vlapa/300Gb/yaDisk/All-reports /home/vlapa/1Tb_r1-a/Backup/ + +@hourly rsync -avzh /home/vlapa/300Gb/yaDisk/Family /home/vlapa/1Tb_r1-a/Backup/ + +#@hourly rsync -avzh /home/vlapa/300Gb/yaDisk/_MY /home/vlapa/1Tb_r1-a/Backup/ +``` +⚙️ **Синтаксис** + +*`* * * * * команда + +Каждая звезда отвечает за время: + +•  минуты +•  часы +•  дата +•  месяц +•  день недели + +`30 2 * * * /home/user/backup.sh + +Эта задача будет выполняться каждый день в 2:30. + +👀 **Просмотр и редактирование** + +• Чтобы редактировать cron-задачи, используйте: +`crontab -e + +• Чтобы увидеть текущие: +`crontab -l + +✍️ **Логирование cron-задач** +Важно отслеживать выполнение задач. Для этого можно записывать вывод задач в файл: + +`* * * * * /path/to/script.sh >> /var/log/cron.log 2>&1 + +2>&1 — это перенаправление стандартного потока ошибок (stderr) в тот же файл, что и стандартный вывод. То есть любые ошибки скрипта будут также записываться в файл cron.log + + diff --git a/LINUX/DIFFERENT.md b/LINUX/DIFFERENT.md new file mode 100644 index 0000000..f19a55f --- /dev/null +++ b/LINUX/DIFFERENT.md @@ -0,0 +1,51 @@ +============================ +`dpkg --list` +`sudo apt remove package-name` +`sudo apt purge package-name` +`sudo apt autoremove` + +`sudo apt autoclean` + +============================ +Сделать пользователя СуперПользователем: +`sudo usermod -aG sudo sampleuser` +`sudo gpasswd -a sampleuser sudo` + +проверить: +`groups sampleuser` + +удалить из группы sudo: +`sudo gpasswd -d sampleuser sudo` + +============================ +Сразу выкл: +`sudo power off` ??? +`shutdown -h now` + + +пароль, потом выкл: +`sudo poweroff` + +Запланировать выключение через: +`shutdown -h` (time in minutes) +`Shutdown +30` +`shutdown +30 "you have a meeting right now"`` + +Отменить запланированное выкл: +`shutdown -c` + +Выключение в: +`shutdown –h 20:3` + +Справка: +`poweroff --help` + +============================= +`sudo reboot` +`sudo shutdown -r` +`sudo shutdown -r +mins` +`sudo shutdown -r hh:mm` + +`init 6` + +============================= \ No newline at end of file diff --git a/LINUX/DISK/DF, DU.md b/LINUX/DISK/DF, DU.md new file mode 100644 index 0000000..a06b473 --- /dev/null +++ b/LINUX/DISK/DF, DU.md @@ -0,0 +1,101 @@ +мгновенно создать пустой файл, не используя touch: +`>text.txt + +================================ + +команда `du` дает вам возможность исключить файлы определенного типа при расчете размера каталога + +`du -h --exclude="*.txt" tutorials + +================================ + +`df -help` +`df -h` - размер и информация о дисках +`df -i` - inode + +. du -help` +`du -h` - размер директории +`du -d1 / | sort -nr - с сортировкой по весу папки + +$ **du** **опции** **/путь/к/папке** + +А вот опции утилиты: + +_**-a, --all**_ - выводить размер для всех файлов, а не только для директорий, по умолчанию размер выводится только для папок; +_**-B, --block-size**_ - указать единицы вывода размера, доступно: K,M,G,T,P,E,Z,Y для 1024 и KB, MB и так далее для 1000; +_**-c, --total**_ - выводить в конце общий размер всех папок; +_**-d, --max-depth**_ - максимальная глубина вложенности директорий; +_**-h, --human-readable**_ - выводить размер в единицах измерения удобных для человека; +_**--inodes**_ - выводить информацию об использованию inode; +_**-L, --dereference**_ - следовать по всем символическим ссылкам; +_**-l, --count-links**_ - учитывать размер файла несколько раз для жестких ссылок; +_**-P, --no-dereference**_ - не следовать по символическим ссылкам, это поведение используется по умолчанию; +_**-S, --separate-dirs**_ - не включать размер подпапок в размер папки; +_**--si**_ - выводить размер файлов и папок в системе си, используется 1000 вместо 1024; +_**-s, --summarize**_ - выводить только общий размер; +_**-t, --threshold**_ - не учитывать файлы и папки с размером меньше указанного; +_**--time**_ - отображать время последней модификации для файла или папки, вместо времени модификации можно выводить такие метки: atime, access, use, ctime; +_**-X, --exclude**_ - исключить файлы из подсчёта; +_**-x, --one-file-system**_ - пропускать примонтированные файловые системы; +_**--version**_ - вывести версию утилиты. + +Здесь перечислены не все опции. Если вам надо больше, смотрите в: + +_**man du**_ + +## Bash скрипт мониторинга свободного места в Linux + +Рассмотрим простой bash скрипт, для мониторинга дискового пространства на сервере и отправке email уведомления администратору, если места недостаточно. + +```sh +#!/bin/bash +CURRENT=$(df /dev/sdd5 | grep / | awk '{ print $5}' | sed 's/%//g) +THRESHOLD=90 +if [ "$CURRENT" -gt "$THRESHOLD" ] ; then +echo "Осталось слишком мало дискового пространства. Используется: $CURRENT%" | mail -s 'Заканчивается дисковое пространство !' -r home@server.ru vlapa@yandex.ru +fi +``` + +При достижении заполняемости директории / до 90% к вам на почту будет отправляться уведомление. Добавьте данный скрипт в [cron](https://winitpro.ru/index.php/2020/04/21/planirovshhik-zadach-cron-v-linux/) и мониторьте вашу систему. Можно изменить скрипт, чтобы он смотрел за размером определенного раздела или каталога. + +=========================== +**du** — позволяет быстро и эффективно проанализировать использование дискового пространства, найти самые «тяжёлые» файлы и папки, чтобы освободить место. + +🟠 **Показать размер папки:** +`du -sh /path/to/folder +➡️ Выводит общий размер папки в удобном формате (МБ/ГБ). + +🟠 **Посмотреть размеры всех вложенных папок:** +`du -h /path/to/folder +➡️ Отлично подходит для поиска «тяжёлых» файлов. + +🟠 **Сортировка по размеру:** +`du -h /path/to/folder | sort -h +➡️ Самые большие файлы окажутся внизу списка. + +=========================== +**Быстрое удаление больших файлов в Linux** + +Когда на диске заканчивается место, полезно быстро находить и удалять самые "тяжелые" файлы. В Linux это можно сделать несколькими способами. + +**🟢 Найти файлы больше 1ГБ:** +find / -type f -size +1G 2>/dev/null +Отобразит файлы больше 1ГБ во всей системе. + +**🟢 Найти и удалить файлы больше 500МБ в указанной папке:** +find /var/log -type f -size +500M -delete +Удаляет файлы в /var/log, превышающие 500МБ. + +**🟢 Найти самые "тяжелые" каталоги:** +du -ah / | sort -rh | head -10 +Выведет 10 самых больших файлов и папок. + +🗣 **Полезно для очистки места на сервере, анализа логов и удаления ненужных файлов.** + +=========================== + +=========================== + +=========================== + +=========================== diff --git a/LINUX/DISK/FDISK.md b/LINUX/DISK/FDISK.md new file mode 100644 index 0000000..f034e53 --- /dev/null +++ b/LINUX/DISK/FDISK.md @@ -0,0 +1,217 @@ +## Разбить диск на разделы с помощью команды fdisk + +Выполните следующие действия, чтобы разбить диск в Linux с помощью команды `fdisk`. +#### Шаг 1. Список существующих разделов + +Выполните следующую команду, чтобы вывести список всех существующих разделов: +`sudo fdisk -l +Вывод содержит информацию о дисках и разделах хранилища: + +[![sudo fdisk -l](https://wiki.merionet.ru/images/kak-sozdavat-razdely-diska-v-linux/9.png "sudo fdisk -l")](https://wiki.merionet.ru/images/kak-sozdavat-razdely-diska-v-linux/9.png) +#### Шаг 2: Выберите диск для хранения + +Выберите диск для хранения, на котором вы хотите создать разделы, выполнив следующую команду: +`sudo fdisk /dev/sdb + +Диск `/dev/sdbstorage` открыт: + +[![/dev/sdbstorage](https://wiki.merionet.ru/images/kak-sozdavat-razdely-diska-v-linux/10.png "/dev/sdbstorage")](https://wiki.merionet.ru/images/kak-sozdavat-razdely-diska-v-linux/10.png) +Будет предложено ввести `m` для справки, вводим и смотрим какие есть возможности по работе с диском +Help: + +  **GPT** +   M   enter protective/hybrid MBR + +  **Generic** +   d   delete a partition +   F   list free unpartitioned space +   l   list known partition types +   n   add a new partition +   p   print the partition table +   t   change a partition type +   v   verify the partition table +   i   print information about a partition + +  **Misc** +   m   print this menu +   x   extra functionality (experts only) + +  **Script** +   I   load disk layout from sfdisk script file +   O   dump disk layout to sfdisk script file + +  **Save & Exit** +   w   write table to disk and exit +   q   quit without saving changes + +  **Create a new label** +   g   create a new empty GPT partition table +   G   create a new empty SGI (IRIX) partition table +   o   create a new empty MBR (DOS) partition table +   s   create a new empty Sun partition table +   +#### Шаг 3: Создайте новый раздел + +1. Запустите команду `n`, чтобы создать новый раздел. +2. Выберите номер раздела, набрав номер по умолчанию (2). +3. После этого вас попросят указать начальный и конечный сектор вашего жесткого диска. Лучше всего ввести в этом разделе номер по умолчанию (3622912). +4. Последний запрос связан с размером раздела. Вы можете выбрать несколько секторов или установить размер в мегабайтах или гигабайтах. Введите `+ 2 GB`, чтобы установить размер раздела 2 ГБ. + +Появится сообщение, подтверждающее создание раздела. + +[![n](https://wiki.merionet.ru/images/kak-sozdavat-razdely-diska-v-linux/11.png "n")](https://wiki.merionet.ru/images/kak-sozdavat-razdely-diska-v-linux/11.png) +#### Шаг 4: запись на диск + +Система создала раздел, но изменения не записываются на диск. +1. Чтобы записать изменения на диск, выполните команду `w`: + +[![w](https://wiki.merionet.ru/images/kak-sozdavat-razdely-diska-v-linux/12.png "w")](https://wiki.merionet.ru/images/kak-sozdavat-razdely-diska-v-linux/12.png) +2. Убедитесь, что раздел создан, выполнив следующую команду: +`sudo fdisk -l +Как видите, раздел `/dev/sdb2` создан. + +### Отформатируйте раздел + +После создания раздела с помощью команды `parted` или `fdisk` отформатируйте его перед использованием. +Отформатируйте раздел, выполнив следующую команду: +`sudo mkfs -t ext4 /dev/sdb1 + +[![sudo mkfs -t ext4 /dev/sdb1](https://wiki.merionet.ru/images/kak-sozdavat-razdely-diska-v-linux/14.png "sudo mkfs -t ext4 /dev/sdb1")](https://wiki.merionet.ru/images/kak-sozdavat-razdely-diska-v-linux/14.png) +#### Смонтировать раздел + +Чтобы начать взаимодействие с диском, создайте точку монтирования (mount point) и смонтируйте к ней раздел. + +1. Создайте точку монтирования, выполнив следующую команду: +`sudo mkdir -p /mt/sdb1 +2. После этого смонтируйте раздел, введя: +`sudo mount -t auto /dev/sbd1 /mt/sdb1 + +Терминал не распечатывает вывод, если команды выполнены успешно. +3. Убедитесь, что раздел смонтирован, с помощью команды `df hT`: +[![df hT](https://wiki.merionet.ru/images/kak-sozdavat-razdely-diska-v-linux/15.png "df hT")](https://wiki.merionet.ru/images/kak-sozdavat-razdely-diska-v-linux/15.png) +========================================== +`lsblk` - диски со структурой + +`cat /proc/mdstat` - информация о RAID массивах в системе + +`sudo blkid` +Примерный вывод: + +`/dev/sda1: UUID="00a4ad8e-dbcc-4c32-a0b4-074b5ee2bfb4" TYPE="ext4"` +`/dev/sda2: LABEL="Win7" UUID="0E42D6D242D6BE21" TYPE="ntfs"` +`/dev/sda3: LABEL="Shared" UUID="7ECCC7F5CCC7A62D" TYPE="ntfs"` +`/dev/sda4: UUID="1050e484-896f-42a9-bdb3-49eb1255cc77" TYPE="swap"` +`/dev/sr0: LABEL="DrWebLiveCD" TYPE="iso9660"` +`/dev/sdb1: LABEL="TRENDNET8GB" UUID="355B-FE37" TYPE="vfat"` + +Предположим, мы хотим подключить раздел «Shared» (NTFS-раздел, находящийся на основном диске компьютера) и «TRENDNET8GB» (флэшка, отформатированная в FAT32). + +Бонус использования команды blkid по сравнению с инспекцией каталога `/dev/disk/by-uuid/` в том,что вы сразу видите тип файловой системы на блочном устройстве, если ядру удалось его определить. + +- Создаём каталоги для монтирования: + + `sudo mkdir /mnt/Shared /mnt/TrendNet8Gb` + +- Открываем **от имени администратора** файл _/etc/fstab_, содержащий информацию о всех монтируемых при загрузке разделах: + +`sudo nano /etc/fstab + +Добавляем в конец файла, +для NTFS: + +`UUID="7ECCC7F5CCC7A62D" /mnt/Shared ntfs rw,nls=utf8,gid=plugdev,umask=0002 0 0` + +для FAT и FAT32: + +`UUID="355B-FE37" /mnt/TrendNet8Gb vfat rw,exec,codepage=866,nls=utf8,gid=plugdev,umask=0002,nofail,users 0 0` + +Монтируем только что вписанные разделы диска: + +`sudo mount -a` + +`fdisk -u -l /dev/sd?` +`sudo mkdir /mnt/raid0` +`sudo mount /dev/md0 /mnt/raid0 -t auto -o rw,nls=utf8,noatime` + +`$ mount` + +_**....**_ + +`/dev/md0 on /mnt/raid0 type ext3 (rw,noatime,nls=utf8)` + + +`$ mount опции -t файловая_система -o опции_монтирования файл_устройства папка_назначения` + +Опции задают различные дополнительные особенности работы утилиты. Опция -t необязательна, но она позволяет задать файловую систему, которая будет использована и иногда это очень полезно. С помощью опции -o вы можете задать различные параметры монтирования, например, монтировать только для чтения и т д. Последних два параметра - это файл устройства, например, /dev/sda1 и папка назначения, например, /mnt. + +Перед тем как перейти к рассмотрению примеров работы утилитой, давайте рассмотрим ее основные опции: + +- **-V** - вывести версию утилиты; +- **-h** - вывести справку; +- **-v** - подробный режим; +- **-a, --all** - примонтировать все устройства, описанные в fstab; +- **-F, --fork** - создавать отдельный экземпляр mount для каждого отдельного раздела; +- **-f, --fake** - не выполнять никаких действий, а только посмотреть что собирается делать утилита; +- **-n, --no-mtab** - не записывать данные о монтировании в /etc/mtab; +- **-l, --show-labels** - добавить метку диска к точке монтирования; +- **-c** - использовать только абсолютные пути; +- **-r, --read-only** - монтировать раздел только для чтения; +- **-w, --rw** - монтировать для чтения и записи; +- **-L, --label** - монтировать раздел по метке; +- **-U, --uuid** - монтировать раздел по UUID; +- **-T, --fstab** - использовать альтернативный fstab; +- **-B, --bind** - монтировать локальную папку; +- **-R, --rbind** - перемонтировать локальную папку. + +`sudo umount /home/vlapa/2Tb_ext` + +`lsof -w /mnt` + +`sudo umount -l /mnt` +-жесткое размонтирование + + +`lsblk ( -f )` + + +****N********AME**** ****-**** имя устройства +****M********AJ********:M********IN**** ****-**** Гланый и Минимальные Номера устройств +****RM**** - Является ли устройство съемным (1) или не (0) +****S********IZE**** - Объем устройства +****RO -**** устройство, только для чтения (1) или не (0) +****TYPE**** - типа устройства, т.е., если это - диск или разделы и т.д. +****M********OUNT POINT**** - точка монтирования устройства (если применимо). + +`sudo fdisk -l` +`sudo parted -l` + + +`sudo mkdir /mnt/name_folder` +`sudo mount /dev/имя_раздела /точка/монтирования` +`sudo chmod 0777 /mnt/name_folder` +`sudo umount /dev/имя_раздела` +`sudo umount /точка_монтирования` + +`sudo nano /etc/fstab` + +`/etc/fstab` +: static file system information. + + +Use =='blkid'== to print the universally unique identifier for a +device; this may be used with UUID= as a more robust way to name devices that works even if disks are added and removed. See fstab(5). + + + + +/dev/disk/by-uuid/c1ca47b1-6f02-4cbb-a917-0bf76c7beae6 none swap sw 0 0 +# / was on /dev/sda3 during curtin installation +/dev/disk/by-uuid/2b69c1d7-9e7a-48bb-91f5-28cce8b4a0e2 / ext4 defaults 0 1 +# /home was on /dev/sda4 during curtin installation +/dev/disk/by-uuid/0c68c1b7-3621-454b-8a4a-02a5c3ce5e92 /home ext4 defaults 0 1 +# /home/1Tb +/dev/disk/by-uuid/1c513d3e-3aa1-4f67-b815-e62369251830 /home/vlapa/1Tb ext4 defaults 0 1 +# /home/2Tb-a +/dev/disk/by-uuid/caa5ad3b-4f1c-45ae-9cd9-d8011ad024e5 /home/vlapa/2Tb-a ext4 defaults 0 1 +# /home/2Tb-b +/dev/disk/by-uuid/e800aa0b-5217-42ad-bac0-5a3f07a748bf /home/vlapa/2Tb-b ext4 defaults 0 1 diff --git a/LINUX/DISK/FSTAB.md b/LINUX/DISK/FSTAB.md new file mode 100644 index 0000000..3d4d30f --- /dev/null +++ b/LINUX/DISK/FSTAB.md @@ -0,0 +1,100 @@ +```sh +# При загрузке Linux на определенном этапе выполняется чтение файла /etc/fstab, в котором описаны правила монтирования разделов. Рассмотрим синтаксис его заполнения. + +# Каждый раздел, который должен быть смонтирован записывается с новой строки: + +#                 + +# device — накопитель, который мы монтируем. Это может быть путь до устройства или его идентификатор. +# dir — каталог, в который выполняем монтирование **device**. +# type — файловая система. +# options — опции монтирования, например, владелец каталога или параметры аутентификации. +# dump — указываем, нужно ли создать резервную копию с помощью утилиты dump. +# fsck — нужно ли проверять файловую систему при помощи fsck. Стоит очень аккуратно относится к данному параметру, так как не со всеми файловыми системами утилита fsck корректно работает. Если нет уверенности, ставим 0. Доступны значения: + # 0 — не выполнять проверку. + # 1 — проверять с высоким приоритетом. Рекомендовано только для корневого раздела. + # 2 — проверять с низким приоритетом. + +# Пример файла fstab: + +#                       +/dev/pve/root     /               ext4     errors=remount-ro  0       1 +/dev/pve/swap     none            swap     sw                 0       0 + +# в данном примере будут смонтированы 2 раздела: + +# /dev/pve/root — в корневой раздел с файловой системой ext4. +# /dev/pve/swap — файл подкачки. + +``` +## Монтирование по имени диска +```sh +vi /etc/fstab + +# добавляем в него следующую строчку: + +/dev/sda1     /db     xfs     defaults     0 0 + +# Где: +# /dev/sda1 — диск, который мы монтируем; +# /db — каталог, в который монтируем диск; +# xfs — файловая система; defaults — стандартные опции; +# 0 0 — первый отключает создание резервных копий при помощи утилиты dump, второй отключает проверку диска. + +# создаем каталог, в который монтируем диск (в данном примере, /db): +mkdir /db + +# И монтируем вручную диск, чтобы проверить правильность настроек fstab: +mount /db + +# Должен появиться новый диск. Это можно проверить следующей командой +mount +``` + +## Монтирование по идентификатору +```sh +# Данный способ не сильно отличается от монтирования по имени диска — вместо имени мы указываем идентификатор. Такой подход считается более надежным, так как при переподключении дисков к другим портам материнской платы может поменяться приоритет и, как следствие, имя устройства — это может привести к проблемам монтирования. UUID диска не меняется и поэтому, данная проблема при таком монтировании не страшна. +blkid + +# Мы получим ответ на подобие: +/dev/sdb: UUID="3e53fc7e-a417-4fa5-b034-b227bea2474c" BLOCK_SIZE="4096" TYPE="ext4" +/dev/sda1: UUID="358f032e-3efb-42ab-b3ba-05ddc82fedfd" BLOCK_SIZE="512" TYPE="xfs" PARTUUID="0beca126-01" + +# допустим, диск который мы хотим примонтировать — sda1. Его идентификатор: 358f032e-3efb-42ab-b3ba-05ddc82fedfd + +vim /etc/fstab +# добавляем в него следующую строчку: +UUID=358f032e-3efb-42ab-b3ba-05ddc82fedfd     /db     xfs     defaults     0 0 + +# Монтирование можно проверить командой: +mount -a + +# Пример монтирования сетевого диска + +192.168.0.1/network /mnt cifs user,rw,noauto,credentials=/root/.smbclient 0 0 + +# выполняется монтировние общей папки *network* на сервере с IP-адресом 192.168.0.1 в каталог /mnt. В качестве сетевой файловой системы используется *cifs (протокол SMB: сервер samba или общая папка Windows). Параметры для подключения: +# user - позволяет выполнить монтирование любому пользователю +# rw - с правом на чтение и запись +# noauto - не монтировать автоматически при старте системы +# credentials - файл, в котором написаны логин и пароль для подключения к общей папке + +# Теперь создадим файл аутентификации (credentials): +vim /root/.smbclient + +# и приведем его к следующему виду: +username=backup +password=backup + +# username - имя пользоватля +# password - пароль (Само собой, в вашем случае )указываются свои данные) + +# Теперь вводим следующую команду: +mount /mnt + +# Должна примонтироваться сетевая папка //192.168.0.1/network +# Это можно проверить следующей командой: + +mount +``` + diff --git "a/LINUX/DISK/GPT-\320\264\320\270\321\201\320\272 \320\262 Linux.md" "b/LINUX/DISK/GPT-\320\264\320\270\321\201\320\272 \320\262 Linux.md" new file mode 100644 index 0000000..8c313f4 --- /dev/null +++ "b/LINUX/DISK/GPT-\320\264\320\270\321\201\320\272 \320\262 Linux.md" @@ -0,0 +1,68 @@ + (носитель более 2 ТБ) + + ![Обновлено](https://www.dmosk.ru/img/public/time.jpg "Дата обновления") Обновлено: 24.05.2024  ![Опубликовано](https://www.dmosk.ru/img/public/time.jpg "Дата публикации") Опубликовано: 17.01.2017 + +C [GPT](https://www.dmosk.ru/terminus.php?object=gpt) в системах [Linux](https://www.dmosk.ru/terminus.php?object=linux) работает [утилита](https://www.dmosk.ru/terminus.php?object=utility) **parted**. + +Чтобы узнать, какой именно тип диска (GPT или [MBR](https://www.dmosk.ru/terminus.php?object=mbr)) настроен вводим команду: + +fdisk /dev/sdb -l + +_* где sdb — наш [жесткий диск](https://www.dmosk.ru/terminus.php?object=hdd)._ + +Либо будет выведено предупреждение **WARNING: GPT (GUID Partition Table) detected**, значит используется GPT, либо мы увидим: + +Тип метки диска: gpt + +Также, для определения диска можно использовать **gdisk**: + +gdisk /dev/sdb + +Мы должны увидеть строку на подобие: + +Found valid GPT with protective MBR; using GPT. + +## Создаем GPT + +Открываем наш диск утилитой parted: + +parted /dev/sdb + +Откроется интерактивное меню утилиты — нажимаем **p:** + +(parted) p + +На экран будет выведена информация о разделах. Если они есть, удаляем командой **rm:** + +(parted) rm 1 + +_* где **1** — номер раздела. Команду нужно повторить для всех разделов. Их список мы смотрели на предыдущем шаге командой **p**. +* если на разделе есть важная информация, ее нужно скопировать на другой носитель._ + +Создаем разметку GPT: + +(parted) mklabel gpt + +И создаем новый раздел: + +(parted) mkpart primary 0 0 + +_* где **primary** указывает, что раздел является основным; **0** и **0** — использовать весь диск для создания раздела, но можно использовать часть диска, например, 0 2048GB._ + +Выходим из parted: + +(parted) q + +И форматируем диск: + +mkfs.ext4 /dev/sdb + +_* где в качестве файловой системы выбрана **ext4**._ + +Для проверки, монтируем отформатированный диск в директорию **/mnt**: + +mount /dev/sdb /mnt + +И выводим список дисков: + +df -h \ No newline at end of file diff --git "a/LINUX/DISK/GeeseFS ( Yandex Cloud \320\264\320\270\321\201\320\272 ).md" "b/LINUX/DISK/GeeseFS ( Yandex Cloud \320\264\320\270\321\201\320\272 ).md" new file mode 100644 index 0000000..1539cc5 --- /dev/null +++ "b/LINUX/DISK/GeeseFS ( Yandex Cloud \320\264\320\270\321\201\320\272 ).md" @@ -0,0 +1,73 @@ +### Установка компонентов + +Сначала устанавливаем утилиты fuse и wget. В зависимости  + +а) Для Deb (Debian / Ubuntu): + +apt update + +apt install fuse wget + +б) Для RPM (Rocky / CentOS): + +yum install fuse fuse3 wget + +Скачиваем geesefs и сохраняем ее в /usr/bin/geesefs: + +wget https://github.com/yandex-cloud/geesefs/releases/latest/download/geesefs-linux-amd64 -O /usr/bin/geesefs + +Выставляем разрешение утилиты geesefs на запуск: + +chmod a+x /usr/bin/geesefs + +Проверяем работу утилиты, посмотрев ее версию: + +geesefs -v + +Мы должны увидеть что-то на подобие: + +geesefs version 0.36.2 + +### Авторизация + +Yandex Cloud для монтирования диска требует прохождение аутентификации с помощью токена. Его мы получаем в личном кабинете хостера. + +Для настройки доступа создаем каталог **.aws** в домашней директории пользователя, под которым будем монтировать диск. В моем случае, пользователь root: + +mkdir /root/.aws + +После создаем файл с токеном: + +vi /root/.aws/credentials + +[default] +aws_access_key_id = H.....D +aws_secret_access_key = H......7 + +### Ручное монтирование + +Для монтирования используем команду geesefs: + +geesefs <имя бакета> <точка монтирования> + +Например: + +geesefs testbacket /mnt + +### Автоматическое монтирование + +Чтобы раздел автоматически монтировался при перезапуске компьютера, открываем на редактирование файл fstab: + +vi /etc/fstab + +Добавляем строку: + +<имя бакета>    <точка монтирования>    fuse.geesefs    _netdev,allow_other,--file-mode=0666,--dir-mode=0777    0   0 + +> Если мы хотим, чтобы точке монтирования назвачался определенный владелец и группы, указываем опции **uid** и **gid**: +> +> <имя бакета>    <точка монтирования>    fuse.geesefs    _netdev,allow_other,--file-mode=0666,--dir-mode=0777,--uid=1024,--gid=1024    0   0 + +Чтобы применить настройку и проверить ее, вводим: + +mount -a \ No newline at end of file diff --git "a/LINUX/DISK/HDPARM \321\201\320\272\320\276\321\200\320\276\321\201\321\202\321\214 \320\264\320\270\321\201\320\272\320\260.md" "b/LINUX/DISK/HDPARM \321\201\320\272\320\276\321\200\320\276\321\201\321\202\321\214 \320\264\320\270\321\201\320\272\320\260.md" new file mode 100644 index 0000000..866f5be --- /dev/null +++ "b/LINUX/DISK/HDPARM \321\201\320\272\320\276\321\200\320\276\321\201\321\202\321\214 \320\264\320\270\321\201\320\272\320\260.md" @@ -0,0 +1,133 @@ +```sh +hdparm -tT /dev/sda + +``` + +/dev/sda: + Timing cached reads:   24138 MB in  1.98 seconds = 12174.75 MB/sec + + Timing buffered disk reads: 1006 MB in  3.00 seconds = 335.09 MB/sec +  + +`$ hdparm опция значение /dev/имя_файла_диска` + +Если опцию передать без значения, то как правило, она просто выводит текущее значения параметра, если передать значение, то оно устанавливается вместо старого. Давайте сначала рассмотрим основные опции утилиты, которые мы будем сегодня использовать: + +- ****-a**** - позволяет включить режим read-ahead, когда чтение выполняется в первую очередь. Этот режим позволяет улучшить производительность чтения больших объемов данных; + +- ****-A**** - позволяет включить режим read-look-ahead, когда диск просматривается перед чтением. По умолчанию включена; +- ****-b**** - остановить жесткий диск до следующего к нему обращения; +- ****-B**** - настройка управления питанием Advanced Power Management (APM), чем ниже значение, тем лучше энергосбережение. Чтобы отключить режим полностью установите значение в 255. +- ****-c**** - включает 32-битный режим передачи для IDE; +- ****-C**** - позволяет посмотреть текущий режим питания IDE; +- ****-d**** - включает режим DMA; +- ****-D**** - включает обнаружение дефектных областей на диске; +- ****-E**** - позволяет установить скорость для CD/DVD привода; +- ****-f**** - позволяет записать кэш диска на носитель; +- ****-i**** - выводит информацию о диске; +- ****-I**** - тоже выводит информацию о диске, но в более подробной форме; +- ****-m**** - позволяет указать количество секторов для многосекторного ввода/вывода. Чтобы отключить эту опцию установите её в 0; +- -M - позволяет управлять уровнем шума диска с помощью Automatic Acoustic Management (AAM); +- ****-n**** - позволяет включить флаг ****ignore write errors**** для игнорирования ошибок записи; +- ****-r**** - включает флаг read only. После этого с устройства можно только читать данные, но не записывать; +- ****-S**** - устанавливает время неактивности диска для перехода его в режим сна. Если установлено в 0, то диск сразу же отключается; +- ****-T**** - выполняет тест скорости чтения из кэша; +- ****-t**** - выполняет тест скорости чтения с диска без кэширования; +- ****-v**** - выводит все настройки; +- ****-w**** - выполняет сброс диска; +- ****-W**** - включает или отключает кэш для записи данных на IDE; +- ****-y**** - переводит IDE диск в режим низкого энергопотребления, замедляя скорость вращения диска; +- ****-Y**** - переводит IDE диск в режим низкого энергопотребления останавливая его; +- ****-z**** - просит ядро перечитать таблицу разделов для этого диска; +- ****-Z**** - отключает режим энергосбережения диска. + +### Hdparm + +`hdparm` - программа для получения/установки параметров устройств SATA/IDE - из одноимённого пакета, которая поддерживает управление временем автопарковки: + +Проверить, что установлено (`hdparm` работает от имени **root**): + +$ su - +`hdparm -J /dev/sdX + +Выключить: + +`hdparm -J0 --please-destroy-my-drive /dev/sdX + +Настройка диска (команда idle3ctl из пакета idle3-tools) + +1. idle3ctl -s 900 /dev/sda +2. idle3ctl -d /dev/sda + +- [https://forum.altlinux.org/index.php?topic=29692.105](https://forum.altlinux.org/index.php?topic=29692.105) +- [https://bugs.etersoft.ru/show_bug.cgi?id=9438](https://bugs.etersoft.ru/show_bug.cgi?id=9438) + +Если указанный выше способ не сработал, можно отключить парковку головок жесткого диска через функцию **APM (расширенного управления питанием)** жесткого диска. Сначала нужно узнать, какой уровень управления энергопитанием установлен: + +$ su - +`hdparm -B /dev/sdX +где sdX - имя нужного жесткого диска в системе. + +Во время питания ноутбука от сети значение APM равно 254. При работе от батареи можно установить его на уровне 128, а при одном-двух запущенных приложениях — 96. Запуска веб-браузера в этом случае стоит избегать. + +$ sudo hdparm -B 96 -S 0 /dev/sda + +При частой работе за ноутбуком от аккумулятора, вероятно, лучшим решением будет использование твердотельного накопителя — SSD: он очевидно превосходит HDD в экономии энергии. Возможно также комбинировать два варианта, когда на SDD установлена сама операционная система, а HDD используется для хранения данных. + +Возможные значения варьируются от 1 до 254. Наивысшая степень управления питанием достигается при значении 1, а наивысшая производительность ввода-вывода - при значении 254. Значение 255 указывает `hdparm` на полное отключение расширенного управления питанием на диске (не все диски поддерживают его отключение, но большинство поддерживают). + + +Для настройки APM при загрузке — создаем исполняемый файл `[rc.local](https://www.altlinux.org/Rc.local "Rc.local")`: + +$ su - +`echo '#!/bin/sh' > /etc/rc.d/rc.local +`chmod +x /etc/rc.d/rc.local + +В этот файл добавляем строчку: + +hdparm -B 254 /dev/sda + +Для настройки APM при выходе из ждущего режима — создаем файл `/etc/systemd/system/root-resume.service`: + +`touch /etc/systemd/system/root-resume.service + +В этот файл добавляем строчки: + +[Unit] +Description=Local system resume actions +After=suspend.target + +[Service] +Type=simple +ExecStart=/sbin/hdparm -B 254 /dev/sda + +[Install] +WantedBy=suspend.target + +И выполняем команду: + +`systemctl enable root-resume + +Для настройки APM при выходе из спящего режима — создаем файл `/etc/systemd/system/root-resume-sleep.service`: + +`touch /etc/systemd/system/root-resume-sleep.service + +В этот файл добавляем строчки: + +[Unit] +Description=Local system resume actions +After=hibernate.target + +[Service] +Type=simple +ExecStart=/sbin/hdparm -B 254 /dev/sda + +[Install] +WantedBy=hibernate.target + +И выполняем команду: + +`systemctl enable root-resume-sleep + + + diff --git "a/LINUX/DISK/MDADM \320\276\321\200\320\263\320\260\320\275\320\270\320\267\320\260\321\206\320\270\321\217 RAID.md" "b/LINUX/DISK/MDADM \320\276\321\200\320\263\320\260\320\275\320\270\320\267\320\260\321\206\320\270\321\217 RAID.md" new file mode 100644 index 0000000..7a50f40 --- /dev/null +++ "b/LINUX/DISK/MDADM \320\276\321\200\320\263\320\260\320\275\320\270\320\267\320\260\321\206\320\270\321\217 RAID.md" @@ -0,0 +1,535 @@ +![Обновлено](https://www.dmosk.ru/img/public/time.jpg "Дата обновления") Обновлено: 28.09.2023  ![Опубликовано](https://www.dmosk.ru/img/public/time.jpg "Дата публикации") Опубликовано: 08.03.2019 + +mdadm — утилита для работы с программными RAID-массивами различных уровней. В данной инструкции рассмотрим примеры ее использования. + +[Установка утилиты mdadm](https://www.dmosk.ru/miniinstruktions.php?mini=mdadm#install) +[Создание RAID](https://www.dmosk.ru/miniinstruktions.php?mini=mdadm#create) +    [Подготовка носителей](https://www.dmosk.ru/miniinstruktions.php?mini=mdadm#create-prepare) +    [Сборка отказоустойчивого массива](https://www.dmosk.ru/miniinstruktions.php?mini=mdadm#create-raid) +[Файл mdadm.conf](https://www.dmosk.ru/miniinstruktions.php?mini=mdadm#conf) +[Создание файловой системы и монтирование](https://www.dmosk.ru/miniinstruktions.php?mini=mdadm#fs-mount) +[Получение информации о RAID](https://www.dmosk.ru/miniinstruktions.php?mini=mdadm#info) +[Проверка целостности массива](https://www.dmosk.ru/miniinstruktions.php?mini=mdadm#check) +[Восстановление RAID](https://www.dmosk.ru/miniinstruktions.php?mini=mdadm#restore) +    [Заменой диска](https://www.dmosk.ru/miniinstruktions.php?mini=mdadm#restore-disk) +    [Пересборка массива](https://www.dmosk.ru/miniinstruktions.php?mini=mdadm#restore-assemble) +[Использование Hot Spare](https://www.dmosk.ru/miniinstruktions.php?mini=mdadm#spare) +[Добавление диска к массиву](https://www.dmosk.ru/miniinstruktions.php?mini=mdadm#add-to-raid) +[Удаление массива](https://www.dmosk.ru/miniinstruktions.php?mini=mdadm#remove) + +## Установка mdadm + +`apt-get install mdadm + +## Сборка RAID + +Перед сборкой, стоит подготовить наши носители. Затем можно приступать к созданию рейд-массива. + +### Подготовка носителей + +Сначала необходимо занулить суперблоки на дисках, которые мы будем использовать для построения RAID (если диски ранее использовались, их суперблоки могут содержать служебную информацию о других RAID): + +`mdadm --zero-superblock --force /dev/sd{b,c} + +_* в данном примере мы зануляем суперблоки для дисков **sdb** и **sdc**._ + +> Посмотреть, какие в вашей системе есть устройства можно командой: +> +> `lsblk + +Если мы получили ответ: + +`mdadm: Unrecognised md component device - /dev/sdb +`mdadm: Unrecognised md component device - /dev/sdc + +... то значит, что диски не использовались ранее для RAID. Просто продолжаем настройку. + +Далее нужно удалить старые метаданные и подпись на дисках: + +`wipefs --all --force /dev/sd{b,c} + +### Создание рейда + +Для сборки избыточного массива применяем следующую команду: + +`mdadm --create --verbose /dev/md0 -l 1 -n 2 /dev/sd{b,c} + +_* где:_ +- _**/dev/md0** — устройство RAID, которое появится после сборки;_  +- _**-l 1** — уровень RAID;_  +- _**-n 2** — количество дисков, из которых собирается массив;_  +- _**/dev/sd{b,c}** — сборка выполняется из дисков sdb и sdc._ + +Мы должны увидеть что-то на подобие: + +`mdadm: Note: this array has metadata at the start and +    `may not be suitable as a boot device.  If you plan to +    `store '/boot' on this device please ensure that +    `your boot-loader understands md/v1.x metadata, or use +    `--metadata=0.90 +`mdadm: size set to 1046528K + +Также система задаст контрольный вопрос, хотим ли мы продолжить и создать RAID — нужно ответить **y**: + +`Continue creating array? y + +Мы увидим что-то на подобие: + +`mdadm: Defaulting to version 1.2 metadata +`mdadm: array /dev/md0 started. + +Вводим команду: + +`lsblk + +... и находим информацию о том, что у наших дисков sdb и sdc появился раздел md0, например: +... +sdb                     8:16   0    2G  0 disk   +??md0                   9:0    0    2G  0 raid1  +sdc                     8:32   0    2G  0 disk   +??md0                   9:0    0    2G  0 raid1  +... + +_* в примере мы видим собранный raid1 из дисков sdb и sdc._ + +## Создание файла mdadm.conf + +В файле mdadm.conf находится информация о RAID-массивах и компонентах, которые в них входят. Для его создания выполняем следующие команды: + +`mkdir /etc/mdadm + +`echo "DEVICE partitions" > /etc/mdadm/mdadm.conf + +`mdadm --detail --scan --verbose | awk '/ARRAY/ {print}' >> /etc/mdadm/mdadm.conf + +Пример содержимого: + +`DEVICE partitions +`ARRAY /dev/md0 level=raid1 num-devices=2 metadata=1.2 name=proxy.dmosk.local:0 UUID=411f9848:0fae25f9:85736344:ff18e41d + +_* в данном примере хранится информация о массиве **/dev/md0** — его уровень 1, он собирается из 2-х дисков._ + +## Создание файловой системы и монтирование массива + +Создание файловой системы для массива выполняется также, как для раздела, например: + +`mkfs.ext4 /dev/md0 + +_* данной командой мы создаем на **md0** файловую систему **ext4**._ +или: + +`mkfs.xfs /dev/md0 + +_* для файловой системы **xfs**._ + +Примонтировать раздел можно командой: + +`mount /dev/md0 /mnt + +_* в данном случае мы примонтировали наш массив в каталог **/mnt**._ + +Чтобы данный раздел также монтировался при загрузке системы, добавляем в fstab. + +Сначала смотрим идентификатор раздела: + +`blkid + +Мы увидим что-то на подобие: + +`/dev/md0: UUID="ffa7c082-7613-4dbc-ae62-3f5508652cf6" TYPE="ext4" + +_* в моем примере мы будем использовать идентификатор **ffa7c082-7613-4dbc-ae62-3f5508652cf6**._ + +Открываем теперь fstab и добавляем строку: + +`vim /etc/fstab + +`UUID="ffa7c082-7613-4dbc-ae62-3f5508652cf6"    /mnt    ext4    defaults    0 0 + +Если ранее мы монтировали раздел в каталог /mnt, вводим: + +`umount /mnt + +И после:  + +`mount -a + +Мы должны увидеть примонтированный раздел md, например: + +`df -h + +`/dev/md0               990M         2,6M  921M            1% /mnt + +## Информация о RAID + +Посмотреть состояние всех RAID можно командой: + +`cat /proc/mdstat + +В ответ мы получим что-то на подобие: + +`md0 : active raid1 sdc[1] sdb[0] +      `1046528 blocks super 1.2 [2/2] [UU] + +_* где **md0** — имя RAID устройства; **raid1 sdc[1] sdb[0]** — уровень избыточности и из каких дисков собран; **1046528 blocks** — размер массива; **[2/2] [UU]** — количество юнитов, которые на данный момент используются. +** мы можем увидеть строку **md0 : active(auto-read-only)** — это означает, что после монтирования массива, он не использовался для записи._ + +Подробную информацию о конкретном массиве можно посмотреть командой: + +`mdadm -D /dev/md0 + +_* где **/dev/md0** — имя RAID устройства._ + +Пример ответа: + +           Version : 1.2 +     Creation Time : Wed Mar  6 09:41:06 2019 +        Raid Level : raid1 +        Array Size : 1046528 (1022.00 MiB 1071.64 MB) +     Used Dev Size : 1046528 (1022.00 MiB 1071.64 MB) +      Raid Devices : 2 +     Total Devices : 2 +       Persistence : Superblock is persistent + +       Update Time : Wed Mar  6 09:41:26 2019 +             State : clean  +    Active Devices : 2 +   Working Devices : 2 +    Failed Devices : 0 +     Spare Devices : 0 + +Consistency Policy : resync + +              Name : proxy.dmosk.local:0  (local to host proxy.dmosk.local) +              UUID : 304ad447:a04cda4a:90457d04:d9a4e884 +            Events : 17 + +    Number   Major   Minor   RaidDevice State +       0       8       16        0      active sync   /dev/sdb +       1       8       32        1      active sync   /dev/sdc + +_* где:_ + +- _**Version —** версия метаданных._ +- _**Creation Time —** дата в время создания массива._ +- _**Raid Level —** уровень RAID._ +- _**Array Size —** объем дискового пространства для RAID._ +- _**Used Dev Size —** используемый объем для устройств. Для каждого уровня будет индивидуальный расчет: RAID1 — равен половине общего размера дисков, RAID5 — равен размеру, используемому для контроля четности._ +- _**Raid Devices —** количество используемых устройств для RAID._ +- _**Total Devices —** количество добавленных в RAID устройств._ +- _**Update Time —** дата и время последнего изменения массива._ +- _**State —** текущее состояние. clean — все в порядке._ +- _**Active Devices —** количество работающих в массиве устройств._ +- _**Working Devices —** количество добавленных в массив устройств в рабочем состоянии._ +- _**Failed Devices —** количество сбойных устройств._ +- _**Spare Devices —** количество запасных устройств._ +- _**Consistency Policy —** политика согласованности активного массива (при неожиданном сбое). По умолчанию используется **resync** — полная ресинхронизация после восстановления. Также могут быть bitmap, journal, ppl._ +- _**Name —** имя компьютера._ +- _**UUID —** идентификатор для массива._ +- _**Events —** количество событий обновления._ +- **Chunk Size** (для RAID5) — размер блока в килобайтах, который пишется на разные диски. + +Подробнее про каждый параметр можно прочитать в мануале для mdadm: + +`man mdadm + +Также, информацию о разделах и дисковом пространстве массива можно посмотреть командой fdisk: + +`fdisk -l /dev/md0 + +## Проверка целостности + +Для проверки целостности вводим: + +`echo 'check' > /sys/block/md0/md/sync_action + +Результат проверки смотрим командой: + +`cat /sys/block/md0/md/mismatch_cnt + +_* если команда возвращает **0**, то с массивом все в порядке._ + +Остановка проверки: + +`echo 'idle' > /sys/block/md0/md/sync_action + +## Восстановление RAID + +Рассмотрим два варианта восстановлении массива. + +### Замена диска + +В случае выхода из строя одного из дисков массива, команда cat /proc/mdstat покажет следующее: + +`cat /proc/mdstat + +Personalities : [raid1]  +md0 : active raid1 sdb[0] +      1046528 blocks super 1.2 [2/1] [U_] + +_* о наличии проблемы нам говорит нижнее подчеркивание вместо U — **[U_]** вместо **[UU]**._ + +Или: + +`mdadm -D /dev/md0 + +... +       Update Time : Thu Mar  7 20:20:40 2019 +             State : clean, degraded +... + +_* статус **degraded** говорит о проблемах с RAID._ + +Для восстановления, сначала удалим сбойный диск, например: + +`mdadm /dev/md0 --remove /dev/sdc + +> Если нам нужно заменить диск, но при этом он в mdadm имеет рабочее состояние, мы получим ошибку: +> +> ... device or resource busy +> +> Сначала, нам нужно пометить диск как сбойный: +> +> `mdadm /dev/md0 --manage --fail /dev/sdc +> +> _* в данном примере диск **sdb** массива **md0** будет помечен сбойным._ +> +> Теперь удаляем: +> +> `mdadm /dev/md0 --remove /dev/sdc + +Теперь добавим новый: + +`mdadm /dev/md0 --add /dev/sde + +Смотрим состояние массива: + +`mdadm -D /dev/md0 + +... +       Update Time : Thu Mar  7 20:57:13 2019 +             State : clean, degraded, recovering +... +    Rebuild Status : 40% complete +... + +_* **recovering** говорит, что RAID восстанавливается; **Rebuild Status** — текущее состояние восстановления массива (в данном примере он восстановлен на **40%**)._ + +Если синхронизация выполняется слишком медленно, можно увеличить ее скорость. Для изменения скорости синхронизации вводим: + +`echo '10000' > /proc/sys/dev/raid/speed_limit_min + +_* по умолчанию скорость **speed_limit_min** = 1000 Кб, **speed_limit_max** — 200000 Кб. Для изменения скорости, можно поменять только минимальную._ + +### Пересборка массива + +Если нам нужно вернуть ранее разобранный или развалившийся массив из дисков, которые уже входили в состав RAID, вводим: + +`mdadm --assemble --scan + +_* данная команда сама найдет необходимую конфигурацию и восстановит RAID._ + +Также, мы можем указать, из каких дисков пересобрать массив: + +`mdadm --assemble /dev/md0 /dev/sdb /dev/sdc + +=============================================== + +**Чтобы вытащить данные из одного диска RAID-1 в Ubuntu, можно попробовать следующее:** + +При подключении одного жесткого диска к моей машине Ubuntu `fdisk -l`отчеты: + +``` +Disk /dev/sdb: 1.8 TiB, 2000398934016 bytes, 3907029168 sectors + Units: sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 4096 bytes +I/O size (minimum/optimal): 4096 bytes / 4096 bytes +Disklabel type: gpt +Disk identifier: D1C4E003-11FD-4223-AA98-61CB9C6322BD + +Device Start End Sectors Size Type +/dev/sdb1 2048 1050623 1048576 512M Linux swap +/dev/sdb2 3147776 3904929899 3901782124 1.8T Microsoft basic data +/dev/sdb3 3904931840 3907029134 2097295 1G Microsoft basic data +/dev/sdb4 1050624 3147775 2097152 1G Microsoft basic data +``` + +Я попробовал шаги из похожего вопроса на этом сайте: + +``` +root@host:/mnt# mount /dev/sdb2 /mnt/old_nas/ +mount: /mnt/old_nas: unknown filesystem type 'linux_raid_member'. + +root@host:/mnt# mdadm --assemble --run /dev/md0 /dev/sdb2 +mdadm: failed to RUN_ARRAY /dev/md0: Invalid argument +mdadm: Not enough devices to start the array. +``` + +Предположим, /deb/sdb2 — это старый участник raid 1, который вы хотите восстановить. + +**Первый вариант** : попробуйте заново собрать RAID 1 со старым диском: + +Вам необходимо собрать деградировавший RAID-массив на новой машине, используя что-то вроде этого: + +``` +mdadm --force --assemble --run /dev/md0 /dev/sdb2 +``` + +Конечно, выберите номер помимо md0, если он уже используется. Затем вы можете смонтировать /dev/md0, где захотите. + +Другой вариант: + +``` +mdadm --force --run --assemble --scan +``` + +Вы получите что-то вроде этого: + +``` +mdadm: /dev/md/0 has been started with 1 drive (out of 2) +``` + +Затем смонтируйте и просмотрите свои файлы: + +``` +cd /mnt && mkdir md0 && mount /dev/md0 md0 +ls -la md0 +``` + +Наконец, вы можете добавить второй диск в массив: + +``` +mdadm /dev/md0 --manage --add /dev/sdc1 +``` + +**Второй вариант** : использование циклического устройства только для чтения + +Получите смещение данных для старого диска: + +``` +mdadm --examine /dev/sdb2 | grep -i offset +``` + +Создайте петлевое устройство (представим, что смещение данных равно 2048): + +``` +losetup --find --show --read-only --offset $((2048*512)) /dev/sdb2 +``` + +Смонтируйте печатное устройство петли, полученное в результате предыдущей команды. + +``` +mount -o ro /dev/loo1 /mnt/ +``` + +Mount может сделать это напрямую с помощью опций -o ro,loop,offset=: + +``` +mount -o ro,loop,offset=$((2048*512)) /dev/sdb2 /mnt +``` + +Насколько я понимаю, проблема, с которой вы столкнулись, может быть связана с тем, что mdadm считает ваш массив RAID 1 "чистым", что не так. Перед тем, как извлечь диск из массива, вы должны явно сообщить mdadm, что ваш второй диск (sdb2) вышел из строя: + +``` +mdadm --manage /dev/md0 --fail /dev/sdb2 +mdadm --manage /dev/md0 --remove /dev/sdb2 +``` + +=============================================== +## Запасной диск (Hot Spare) + +Если в массиве будет запасной диск для горячей замены, при выходе из строя одного из основных дисков, его место займет запасной. + +Диском Hot Spare станет тот, который просто будет добавлен к массиву: + +`mdadm /dev/md0 --add /dev/sdd + +Информация о массиве изменится, например: + +`mdadm -D /dev/md0 + +... +    Number   Major   Minor   RaidDevice State +       0       8       16        0      active sync   /dev/sdb +       2       8       48        1      active sync   /dev/sdc + +       3       8       32        -      spare   /dev/sdd + +Проверить работоспособность резерва можно вручную, симулировав выход из строя одного из дисков: + +`mdadm /dev/md0 --fail /dev/sdb + +И смотрим состояние: + +`mdadm -D /dev/md0 + +... +    Rebuild Status : 37% complete +... +    Number   Major   Minor   RaidDevice State +       3       8       32        0      spare rebuilding   /dev/sdd +       2       8       48        1      active sync   /dev/sdc + +       0       8       16        -      faulty   /dev/sdb +... + +_* как видим, начинается ребилд. На замену вышедшему из строя **sdb** встал hot-spare **sdd**._ + +## Добавить диск к массиву + +В данном примере рассмотрим вариант добавления активного диска к RAID, который будет использоваться для работы, а не в качестве запасного. + +Добавляем диск к массиву: + +`mdadm /dev/md0 --add /dev/sde + +Новый диск мы увидим в качестве spare: + +4       8       16        -      spare   /dev/sde + +Теперь расширяем RAID: + +`mdadm -G /dev/md0 --raid-devices=3 + +_* в данном примере подразумевается, что у нас RAID 1 и мы добавили к нему 3-й диск._ + +## Удаление массива + +При удалении массива внимательнее смотрите на имена массива и дисков и подставляйте свои значения. + +Если нам нужно полностью разобрать RAID, сначала размонтируем и остановим его: + +`umount /mnt + +_* где **/mnt** — каталог монтирования нашего RAID._ + +> Если мы получим ошибку **umount: /mnt: target is busy**, то смотрим пользовательские процессы, которые мешают отмонтировать раздел: +> +> `fuser -vm /mnt +> +> И останавливаем их. + +`mdadm -S /dev/md0 + +_* где **/dev/md0** — массив, который мы хотим разобрать._ + +> Если мы получим ошибку **mdadm: fail to stop array /dev/md0: Device or resource busy**, с помощью команды +> +> `lsof -f -- /dev/md0 +> +> ... смотрим процессы, которые используют раздел и останавливаем их. + +Затем очищаем суперблоки на всех дисках, из которых он был собран: + +`mdadm --zero-superblock /dev/sdb + +`mdadm --zero-superblock /dev/sdc + +`mdadm --zero-superblock /dev/sdd + +_* где диски **/dev/sdb**, **/dev/sdc**, **/dev/sdd** были частью массива md0._ + +А также удаляем метаданные и подпись: + +`wipefs --all --force /dev/sd{b,c,d} + diff --git a/LINUX/DISK/NTFS.md b/LINUX/DISK/NTFS.md new file mode 100644 index 0000000..c813187 --- /dev/null +++ b/LINUX/DISK/NTFS.md @@ -0,0 +1,57 @@ +### Установка компонентов + +Для начала необходимо установить пакет ntfs-3g. В противном случае, при попытке запустить команду на монтирование раздела NTFS мы получим ошибку **mount: unknown filesystem type 'ntfs'.** + +В зависимости от дистрибутива Linux, наши действия будут отличаться. + +**а) Linux Deb (Debian / Ubuntu / Astra):** + +apt update + +apt install ntfs-3g + +**б) Linux RPM (Rocky / РЕД ОС / CentOS):** + +yum install epel-release + +yum install ntfs-3g + +После установки данного пакета в системе появятся дополнительные команды **mount.ntfs-fuse** и **mount.ntfs-3g**., а также команда mount распознает опцию -t ntfs. + +### Ручное монтирование + +Монтирование NTFS на чтение и запись выполняется любой из трех команд: + +mount -t ntfs /dev/sdb1 /mnt + +или:  + +mount -t ntfs-3g /dev/sdb1 /mnt + +или + +mount.ntfs-3g /dev/sdb1 /mnt + +_* в данном примере, примотировано устройство **/dev/sdb1** в раздел **/mnt**. Понять, какой устройство нужно монтировать можно при помощи команды **fdisk -l**_ + +### Автоматическое монтирование + +Чтобы раздел автоматически монтировался при перезапуске компьютера, открываем на редактирование файл: + +vi /etc/fstab + +Добавляем строку: + +/dev/sdb1     /mnt      ntfs    defaults    0 0 + +_* в данном примере мы раздел /dev/sdb1 будем монтировать в каталог /mnt._ + +Чтобы применить настройку и проверить ее, вводим: + +mount -a + +Посмотреть примонтированные разделы и файловые системы можно командой: + +df -hT + + diff --git a/LINUX/DISK/SMARTMONTOOLS.md b/LINUX/DISK/SMARTMONTOOLS.md new file mode 100644 index 0000000..4101b02 --- /dev/null +++ b/LINUX/DISK/SMARTMONTOOLS.md @@ -0,0 +1,85 @@ +```sh +sudo apt install smartmontools + +# узнаём какие диски подключены: +ls -l /dev | grep -E 'sd|hd' +#  или: +smartctl --scan +lsblk + +# отображение информации о диске: +smartctl --info /dev/sda + +# запустить проверку: +smartctl -s on -a /dev/sda + +# Здесь опция -s включает флаг SMART на указном устройстве. Вы можете его убрать если поддержка SMART уже включена. Информация о диске разделена на несколько разделов, В разделе **READ SMART DATA** находится общая информация о здоровье жесткого диска. + +`=== START OF READ SMART DATA SECTION === SMART overall-health self-assessment rest result: PASSED` + +# Этот тест может быть пройден (PASSED) или нет (FAILED). В последнем случае сбой неизбежен, начинайте резервное копирование данных с этого диска. +``` +Следующая вещь которую можно посмотреть, когда выполняется диагностика HDD в linux, это таблица SMART атрибутов. + +[![](https://losst.pro/wp-content/uploads/2015/08/Snimok-ekrana-ot-2020-05-14-14-20-12-1024x576.png)](https://losst.pro/wp-content/uploads/2015/08/Snimok-ekrana-ot-2020-05-14-14-20-12.png) + В SMART таблице записаны параметры, определенные для конкретного диска разработчиком, а также порог отказа для этих параметров. Таблица заполняется автоматически и обновляется на основе прошивки диска. + +- **ID #** - идентификатор атрибута, как правило, десятичное число между 1 и 255; +- **ATTRIBUTE_NAME** - название атрибута; +- **FLAG** - флаг обработки атрибута; +- **VALUE** - это поле представляет нормальное значение для состояния данного атрибута в диапазоне от 1 до 253, 253 - лучшее состояние, 1 - худшее. В зависимости от свойств, начальное значение может быть от 100 до 200; +- **WORST** - худшее значение value за все время; +- **THRESH** - самое низкое значение value, после перехода за которое нужно сообщить что диск непригоден для эксплуатации; +- **TYPE** - тип атрибута, может быть Pre-fail или Old_age. Все атрибуты по умолчанию считаются критическими, то-есть если диск не прошел проверку по одному из атрибутов, то он уже считается не пригодным (FAILED) но атрибуты old_age не критичны; +- **UPDATED** - показывает частоту обновления атрибута; +- **WHEN_FAILED** - будет установлено в **FAILING_NOW** если значение атрибута меньше или равно THRESH, или в "**-**" если выше. В случае **FAILING_NOW**, лучше как можно скорее выполнить резервное копирование, особенно если тип атрибута **pre-fail**. +- **RAW_VALUE** - значение, определенное производителем. + + +## Автоматическая диагностика в smartd + +Автоматическая диагностика HDD в Linux настраивается очень просто. Сначала отредактируйте файл конфигурации smartd - /etc/smartd.conf. Добавьте следующую строку: + + `nano /etc/smartd.conf` + +`/dev/sda -m myemail@mydomain.com -M test` + +Здесь: + +- **-m ** - адрес электронной почты для отправки результатов проверки. Это может быть адрес локального пользователя, суперпользователя или внешний адрес, если настроен сервер для отправки электронной почты; +- **-M** - частота отправки писем. once - отправлять только одно сообщение о проблемах с диском. **daily** - отправлять сообщения каждый день если была обнаружена проблема. **diminishing** - отправлять сообщения через день если была обнаружена проблема. **test** - отправлять тестовое сообщение при запуске smartd. **exec** - выполняет указанную программу в место отправки почты. + +Сохраните изменения и перезапустите **smartd**: + +`sudo systemctl restart smartd` + +Вы должны получить на электронную почту письмо о том, что программа была запущена успешно. Это будет работать только если на компьютере настроен почтовый сервер. + +[![](https://losst.pro/wp-content/uploads/2015/08/Snimok-ekrana-ot-2020-05-14-15-27-39-1024x576.png)](https://losst.pro/wp-content/uploads/2015/08/Snimok-ekrana-ot-2020-05-14-15-27-39.png) + +Также можно запланировать тесты по своему графику, для этого используйте опцию **-s** и регулярное выражение типа **T/MM/ДД/ДН/ЧЧ**, где: + +Здесь **T** - тип теста: + +- **L** - длинный тест; +- **S** - короткий тест; +- **C** - тест перемещения (ATA); +- **O** - оффлайн тест. + +Остальные символы определяют дату и время теста: + +- **ММ** - месяц в году; +- **ДД** - день месяца; +- **ЧЧ** - час дня; +- **ДН** -  день недели (от 1 - понедельник 7 - воскресенье; +- **MM, ДД и ЧЧ** - указываются с двух десятичных цифр. + +Точка означает все возможные значения, выражение в скобках (A|B|C) - означает один из трех вариантов, выражение в квадратных скобках [1-5] означает диапазон (от 1 до 5). + +Например, чтобы выполнять полную проверку жесткого диска linux каждый рабочий день в час дня добавьте опцию **-s** в строчку конфигурации вашего устройства: + +`/dev/sda -m myemail@mydomain.com -M once -s (L /../../[1-5]/13)` + +Если вы хотите чтобы утилита сканировала и проверяла все устройства, которые есть в системе используйте вместо имени устройства директиву DEVICESCAN: + +`DEVICESCAN -m myemail@mydomain.com -M once -s (L /../../[1-5]/13)` \ No newline at end of file diff --git "a/LINUX/DISK/SOFT RAID1 (mdadm) \320\263\320\276\321\200\321\217\321\207\320\260\321\217 \320\267\320\260\320\274\320\265\320\275\320\260 \320\264\320\270\321\201\320\272\320\260.md" "b/LINUX/DISK/SOFT RAID1 (mdadm) \320\263\320\276\321\200\321\217\321\207\320\260\321\217 \320\267\320\260\320\274\320\265\320\275\320\260 \320\264\320\270\321\201\320\272\320\260.md" new file mode 100644 index 0000000..b8935d7 --- /dev/null +++ "b/LINUX/DISK/SOFT RAID1 (mdadm) \320\263\320\276\321\200\321\217\321\207\320\260\321\217 \320\267\320\260\320\274\320\265\320\275\320\260 \320\264\320\270\321\201\320\272\320\260.md" @@ -0,0 +1,26 @@ +```sh +# Смотрим диски: +cat /proc/mdstat +lsblk + +# Смотрим SMART: +sudo smartctl -a /dev/sda +# смотрим: -Rellocated Sector_ct +# -Current_Pending_Sector + +# Копируем и сохраняем Serial Number плохого диска + +# Открываем в разных окнах: +watch n1 cat /proc/mdstat + +# Говорим что sdc1 в md0 это fail: +sudo mdadm /dev/md0 --fail /dev/sdc1 + +# Удаляем sdc1 из md0: +sudo mdadm /dev/md0 --remove /dev/sdc1 + +sudo vim /etc/mdadm.conf +# В файле нужно указать опцию: +MAILADDR vlapa@ya.ru +``` + diff --git "a/LINUX/DISK/WD - \320\260\320\262\321\202\320\276\320\277\320\260\321\200\320\272\320\276\320\262\320\272\320\260.md" "b/LINUX/DISK/WD - \320\260\320\262\321\202\320\276\320\277\320\260\321\200\320\272\320\276\320\262\320\272\320\260.md" new file mode 100644 index 0000000..43da641 --- /dev/null +++ "b/LINUX/DISK/WD - \320\260\320\262\321\202\320\276\320\277\320\260\321\200\320\272\320\276\320\262\320\272\320\260.md" @@ -0,0 +1,51 @@ +```sh +sudo apt install idle3-tools + +# Чтобы получить текущее значение времени парковки нужно выполнить полученный в процессе сборки файл idle3ctl со следующим параметром: +idle3ctl -g /dev/sda + +# Отключить idle3 таймер: +idle3ctl -d /dev/sda + +# Выставить таймер на 10 секунд: +idle3ctl -s 100 /dev/sda +``` +Дополнительные опции указаны на сайте программы и доступны по ключу -h. + +Чтобы новые значения вступили в действие нужно выключить и включить диск, по-этому после установки нового значения таймера программа попросит вас перезагрузиться. + +Проверено на двух 2TB WD20EARX. Счетчик количества парковок перестал так быстро расти, интенсивность уменьшилась до нескольких парковок в час. + +========================================== +#### Western Digital - отключение автопарковки в дисках серии EARS + +Заглянув в очередной раз в параметры SMART диска Western Digital(WD) серии Green (WD15EARS) увидел аномально высокое значение Load_Cycle_Count: + +manumba ~ # smartctl -A /dev/sdb +... +193 Load_Cycle_Count 0x0032 194 194 000 Old_age Always - 18713 +... + +Для сравнения параметры SMART диска WD серии BLACK: + +manumba ~ # smartctl -A /dev/sdc +... +193 Load_Cycle_Count 0x0032 200 200 000 Old_age Always - 18 +... + +Load_Cycle_Count - показывает количество парковок диска. Почитав информацию в интернете узнал что диски WD серии Green паркуются с целью энергосбережения. +Понаблюдав за параметром Load_Cycle_Count диска WD15EARS обнаружил что он увеличивается на 200 каждые сутки. Это примерно 72000 парковок в год. Согласно данным производителя, предел количества парковок для дисков WD Green находится на отметке около 600 000 парковок, т.е. при данном темпе парковок диску отмерено около 8-9 лет. С одной стороны за это время диск морально устареет, или (все может быть) выйдет из строя по другим причинам. С другой стороны взбредет что то прошивке диска, изменится характер нагрузки на диск, и вдруг диск начнет парковаться с скоростью 1000 парковок в день? Поскольку энергосбережение 10-15 ватт для домашнего компьютера мало значит, думаю выгодней отключить эту странную функцию. + +### Отключение автопарковки + +Для отключения автопарковки нужно использовать утилиту wdidle3 от WD. Утилита для DOS, поэтому лучше скачать какой нибудь LiveCD содержащий эту утилиту, для того что бы удобно запустить оную. Например [HirenBootCD](http://rutracker.org/forum/viewtopic.php?t=3419055 "http://rutracker.org/forum/viewtopic.php?t=3419055"). Итак: + +- Скачиваем [HirenBootCD](http://rutracker.org/forum/viewtopic.php?t=3419055 "http://rutracker.org/forum/viewtopic.php?t=3419055"). +- Выключаем компьютер. +- Отключаем все диски кроме WD15EARS, на котором мы будем отключать автопарковку (это рекомендовано WD и сторонними источниками в интернете). +- Загружаемся в HirenBootCD и выбираем из Hard Disk tools: wdidle3. +- В командной строке набираем wdidle3.exe /D (Эта команда отключит функцию автопарковки) +- Выключаем компьютер. +- Подключаем все остальные диски обратно. +- Включаем BIOS, восстанавливаем порядок дисков в BIOS (иначе первым диском по версии BIOS может оказаться диск на котором отключалась автопарковка, что в случае если порядок дисков был раньше другой, может стать удивительным открытием для GRUB) +- Загружаемся и радуемся отключенной автопарковке. \ No newline at end of file diff --git "a/LINUX/DISK/\320\220\320\262\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201\320\272\320\276\320\265 \320\274\320\276\320\275\321\202\320\270\321\200\320\276\320\262\320\260\320\275\320\270\320\265 USB \320\275\320\260\320\272\320\276\320\277\320\270\321\202\320\265\320\273\321\217 \320\262 \320\267\320\260\320\264\320\260\320\275\320\275\321\203\321\216 \320\277\320\260\320\277\320\272\321\203.md" "b/LINUX/DISK/\320\220\320\262\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201\320\272\320\276\320\265 \320\274\320\276\320\275\321\202\320\270\321\200\320\276\320\262\320\260\320\275\320\270\320\265 USB \320\275\320\260\320\272\320\276\320\277\320\270\321\202\320\265\320\273\321\217 \320\262 \320\267\320\260\320\264\320\260\320\275\320\275\321\203\321\216 \320\277\320\260\320\277\320\272\321\203.md" new file mode 100644 index 0000000..0f2b882 --- /dev/null +++ "b/LINUX/DISK/\320\220\320\262\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201\320\272\320\276\320\265 \320\274\320\276\320\275\321\202\320\270\321\200\320\276\320\262\320\260\320\275\320\270\320\265 USB \320\275\320\260\320\272\320\276\320\277\320\270\321\202\320\265\320\273\321\217 \320\262 \320\267\320\260\320\264\320\260\320\275\320\275\321\203\321\216 \320\277\320\260\320\277\320\272\321\203.md" @@ -0,0 +1,34 @@ +Этот скрипт автоматически монтирует USB-накопитель в указанную директорию при подключении. Полезен для серверов, киосков и систем с минимальным GUI. + +```sh +#!/bin/bash + +# Точка монтирования +MOUNT_DIR="$HOME/usb_mount" + +# Создаём папку, если её нет +mkdir -p "$MOUNT_DIR" + +# Ищем новое USB-устройство +DEVICE=$(lsblk -o NAME,TYPE,HOTPLUG | awk '$2 == "disk" && $3 == 1 {print $1}' | tail -n1) + +# Если устройство найдено +if [ -n "$DEVICE" ]; then +    # Получаем раздел (например, sdb1) +    PARTITION="/dev/${DEVICE}1" + +    # Монтируем раздел +    sudo mount "$PARTITION" "$MOUNT_DIR" + +    echo "✅ Устройство $PARTITION смонтировано в $MOUNT_DIR" +else +    echo "❌ USB-устройство не найдено" +fi +``` + +🟢 Использует lsblk для поиска подключённых устройств +🟢 Автоматически монтирует первый раздел USB-устройства +🟢 Создаёт директорию монтирования, если она отсутствует. + +➡️ **Скрипт можно запускать вручную или автоматически через** udev**-триггеры.** + diff --git "a/LINUX/DISK/\320\237\321\200\320\276\320\263\321\200\320\260\320\274\320\274\320\275\321\213\320\271 raid \320\262 Linux.md" "b/LINUX/DISK/\320\237\321\200\320\276\320\263\321\200\320\260\320\274\320\274\320\275\321\213\320\271 raid \320\262 Linux.md" new file mode 100644 index 0000000..af8e621 --- /dev/null +++ "b/LINUX/DISK/\320\237\321\200\320\276\320\263\321\200\320\260\320\274\320\274\320\275\321\213\320\271 raid \320\262 Linux.md" @@ -0,0 +1,169 @@ +### Доступны такие режимы: + +- **RAID 0** - позволяет увеличить скорость записи. Все диски массива будут использоваться для записи данных, поэтому их скорости будут складываться. Например, если у вас есть три диска объемом 512 Гб и скоростью записи 200 Мб в секунду, то объединив их в RAID 0 вы получите виртуальный диск объемом 1,5 Тб и максимальной скоростью записи 600 Мб в секунду. +- **RAID 1** - этот режим увеличивает сохранность данных. Во время записи одни и те же данные пишутся параллельно на подключённые диски. В результате у вас будет несколько копий одних и тех же данных. Если один из дисков массива выйдет из строя, система продолжит работать, так как данные есть ещё и на другом диске. Например, если вы объедините два диска по 1 Тб в RAID 1, то получите один виртуальный диск с объемом 1 Тб. +- **RAID 10** - объединяет в себе два предыдущих варианта. Он может состоять как минимум из четырех дисков. В таком случае сначала создаются два массива RAID 1, а поверх них создается массив RAID 0 для увеличения производительности. + +Конечно, существуют и другие режимы работы, но эти самые популярные. В сегодняшней статье мы рассмотрим как создать программный RAID уровней 0 и 1. + +## Создание программного RAID в Linux + +### Шаг 1. Установка mdadm + +Для управления программными RAID массивами в Linux используется утилита mdadm. Для того чтобы установить её в Ubuntu или Debian выполните такую команду: + +`sudo apt install mdadm` + +[![](https://losst.pro/wp-content/uploads/2021/06/raid4-830x576.png)](https://losst.pro/wp-content/uploads/2021/06/raid4.png) + +Для установки утилиты в CentOS/Fedora/RedHat необходимо выполнить: + +`sudo yum install mdadm` + +### Шаг 2. Подготовка дисков + +Посмотреть список дисков, подключённых к системе можно с помощью команды lsblk: + +`lsblk` + +[![](https://losst.pro/wp-content/uploads/2021/06/raid-830x576.png)](https://losst.pro/wp-content/uploads/2021/06/raid.png) + +В этой статье я покажу как объединить три диска в RAID на примере дисков **/dev/sda**, **/dev/sdb** и **/dev/sdc**. Сначала необходимо определиться стоит ли размещать RAID непосредственно на диски или на разделы. Лучше выбрать разделы, так как это дает больше гибкости и безопасности. Во первых, операционная система может перезаписать суперблок RAID если он размещён прямо на диске. Во вторых, если вы выделяете весь диск под RAID, то у вас могут возникнуть проблемы при замене диска. Диски одинакового объема, обычно, немного отличаются у разных производителей. Поэтому для замены вам придется искать точно такой же диск с точно таким же реальным объемом. Если же у вас будет раздел, вы просто сможете создать раздел нужного объема. + +Сначала нужно создать таблицу разделов на всех выбранных дисках: + +`sudo parted /dev/sda mklabel msdos` +`sudo parted /dev/sdb mklabel msdos` +`sudo parted /dev/sdc mklabel msdos` + +[![](https://losst.pro/wp-content/uploads/2021/06/raid1-830x576.png)](https://losst.pro/wp-content/uploads/2021/06/raid1.png) + +Если на диске уже существует таблица разделов программа предупредит о том, что создание новой сотрёт все данные с диска. После создания таблицы разделов следует создать по разделу на каждом диске. Например, создадим разделы размером 460 гигабайт. Для этого можно воспользоваться той же командой parted: + +`sudo parted /dev/sda mkpart primary ext4 2048 460Gb` +`sudo parted /dev/sdb mkpart primary ext4 2048 460Gb` +`sudo parted /dev/sdc mkpart primary ext4 2048 460Gb` + +[![](https://losst.pro/wp-content/uploads/2021/06/raid2-830x576.png)](https://losst.pro/wp-content/uploads/2021/06/raid2.png) + +Теперь диски готовы к размещению на них RAID: + +[![](https://losst.pro/wp-content/uploads/2021/06/raid3-830x576.png)](https://losst.pro/wp-content/uploads/2021/06/raid3.png) + +### Шаг 3. Создание RAID 0 + +Для создания RAID массива надо выполнить команду mdadm с опцией --create, указать режим работы массива, количество дисков и сами диски. Синтаксис команды такой: + +**$ sudo mdadm --create /dev/имя_массива --level=режим_работы --raid-devices=количество_устройств список устройств** + +Например: + +`sudo mdadm --create /dev/md0 --level=0 --raid-devices=3 /dev/sda1 /dev/sdb1 /dev/sdc1` + +[![](https://losst.pro/wp-content/uploads/2021/06/raid5-830x576.png)](https://losst.pro/wp-content/uploads/2021/06/raid5.png) + +После выполнения этой команды вы увидите раздел raid в lsblk. С этим разделом можно работать как с любым обычным разделом в вашей системе. + +### Шаг 4. Тестирование RAID 0 + +Давайте для примера отформатируем полученный раздел в файловую систему Ext4, смонтируем и попробуем записывать туда файлы: + +`sudo mkfs -t ext4 /dev/md0` +`sudo mount /dev/md0 /mnt` + +Затем можно тестировать скорость с помощью dd: + +`sudo dd if=/dev/zero of=/mnt/file bs=1G count=5` + +[![](https://losst.pro/wp-content/uploads/2021/06/raid8-830x576.png)](https://losst.pro/wp-content/uploads/2021/06/raid8.png) + +Как видите, при записи 5 Гб данных мы получаем скорость 400 Мб/сек, это уже на уровне обычного SSD. + +### Шаг 5. Информация о RAID + +Найти информацию обо всех созданных в системе RAID массивах вы можете в файле **/proc/mdstat:** + +`cat /proc/mdstat` + +[![](https://losst.pro/wp-content/uploads/2021/06/raid9-830x576.png)](https://losst.pro/wp-content/uploads/2021/06/raid9.png) + +Именно так можно посмотреть RAID Linux. Посмотреть более детальную информацию о массиве /dev/md0 можно с помощью самой утилиты mdadm: + +`sudo mdadm --detail /dev/md0` + +[![](https://losst.pro/wp-content/uploads/2021/06/raid10-830x576.png)](https://losst.pro/wp-content/uploads/2021/06/raid10.png) + +Здесь в том числе отображается состояние RAID Linux. Посмотреть детальную информацию о каждом устройстве, которое входит в RAID можно с помощью опции **--examine**: + +`sudo mdadm --examine /dev/sda1 /dev/sdb1 /dev/sdc1` + +[![](https://losst.pro/wp-content/uploads/2021/06/raid11-830x576.png)](https://losst.pro/wp-content/uploads/2021/06/raid11.png) + +### Шаг 6. Сохранение RAID массива + +В принципе, уже сейчас RAID массив работает и продолжит работать после перезагрузки, потому что mdadm просканирует все диски, найдёт метаданные массива и построит его. Но неизвестно какое имя программа присвоит полученному массиву и неизвестно все ли параметры будут восстановлены верно. Поэтому конфигурацию массива лучше сохранить. Для этого используйте такую команду: + +`sudo mdadm --detail --scan --verbose | sudo tee -a /etc/mdadm/mdadm.conf` + +[![](https://losst.pro/wp-content/uploads/2021/06/raid12-830x576.png)](https://losst.pro/wp-content/uploads/2021/06/raid12.png) + +Затем нужно пересоздать initramfs с поддержкой этого массива: + +`sudo update-initramfs -u` + +С полученным массивом можно обращаться как с обычным разделом диска. Например, для того чтобы автоматически монтировать его в систему добавьте такую строчку в /etc/fstab: + +`sudo vi /etc/fstab` + +`/dev/md0 /mnt/ ext4 defaults 0 0` + +На этом создание raid массива linux завершено. + +### Шаг 7. Переименование RAID массива + +Если вы не выполните предыдущий пункт и перезагрузите компьютер, то можете получить RAID массив с именем md127 вместо md0, такое имя также может быть присвоено второму RAID массиву. Для того чтобы переименовать массив, его придется пересобрать. Для этого сначала остановите существующий массив: + +`sudo mdadm --stop /dev/md127` + +Затем выполните команду переименования. Синтаксис у неё такой: + +**$ sudo mdadm --assemble --update=name --name=номер /dev/md_номер список устройств** + +Например: + +`sudo mdadm --assemble --update=name --name=0 /dev/md0 /dev/sda1 /dev/sdb1 /dev/sdc1` + +После этого следует повторить предыдущий шаг для уже правильного сохранения RAID устройства. + +### Шаг 8. Удаление RAID массива + +Если вы не хотите чтобы ваши диски и дальше были объединены в RAID, его можно удалить. Для этого выполните такую команду: + +`sudo mdadm --remove /dev/md0` + +Она удалит все метаданные с дисков /dev/md0. Дальше останется только удалить или закомментировать секцию данного RAID массива в /etc/mdadm/mdadm.conf + +`sudo vi /etc/mdadm/mdadm.conf` + +[![](https://losst.pro/wp-content/uploads/2021/06/raid13-830x576.png)](https://losst.pro/wp-content/uploads/2021/06/raid13.png) +### Шаг 10. Создание массива RAID 1 + +Теперь вы знаете как создать raid linux на примере RAID 0. Давайте ещё разберемся с RAID 1. Для создания RAID 1 используется такая же команда как и для RAID 0, но указывается другой уровень работы массива: + +`sudo mdadm --create /dev/md0 --level=1 --raid-devices=3 /dev/sda1 /dev/sdb1 /dev/sdc1` + +Затем вы можете убедится что RAID создан посмотрев информацию о нём: + +`sudo mdadm --detail /dev/md0` + +Ну и с помощью lsblk можно оценить размер устройства: + +`lsblk` + +[![](https://losst.pro/wp-content/uploads/2021/06/raid14-830x576.png)](https://losst.pro/wp-content/uploads/2021/06/raid14.png) + +Как и ожидалось, размер не увеличился, поскольку копии данных будут записываться на все три диска. Теперь давайте посмотрим на скорость: + +[![](https://losst.pro/wp-content/uploads/2021/06/raid15-830x576.png)](https://losst.pro/wp-content/uploads/2021/06/raid15.png) + +Скорость записи данных такая же как и у одного диска. Это цена сохранности данных. Если вы отключите один из дисков, то все данные всё равно будут вам доступны. \ No newline at end of file diff --git a/LINUX/DOCKER.md b/LINUX/DOCKER.md new file mode 100644 index 0000000..0f6c942 --- /dev/null +++ b/LINUX/DOCKER.md @@ -0,0 +1,87 @@ +**Собираем минимальный Docker образ** + +**Docker** — это инструмент для запуска приложений в изолированных контейнерах. Он делает разработку и развертывание проще, быстрее и безопаснее. + +**Нужен он для:** изоляции приложений и зависимостей, переносимости (софтина работает одинаково  на любой системе) и DevOps-процессов. + +**Плюсы докера:** простота развёртывания, экономия ресурсов по сравнению с виртуальными машинами, стандартизация окружения. + +**Минусы докера:** не подходит для задач, требующих полной виртуализации, не оптимален для GUI-приложений, требует грамотной настройки безопасности. + +Минимальный пример запуска первого **Docker-контейнера** с **Python** (**FastAPI**): + +1. Для развертывания для начала создадим новый проект и установим зависимости. Если не имели раньше дела с питухоном питоном и докером, также скачиваем все необходимое: +```sh +sudo apt install python3 python3-pip -y +sudo apt install docker.io -y +``` +После установки создаем рабочую папку: + +```sh +mkdir fastapi_backend && cd fastapi_backend +``` + +Активируем виртуальное окружение, чтобы изолировать зависимости проекта и с помощью команды **pip freeze** автоматически сформировать **requirements.txt**, не заполняя его вручную: + +```sh +python3 -m venv venv +source venv/bin/activate +``` +Cкачиваем необходимые для создания API зависимости с помощью пакетного менеджера pip и сохраняем их в **requirements.txt**: + +```sh +pip install fastapi uvicorn +pip freeze > requirements.txt +``` +2. Создадим в каталоге проекта файл "**main.py**" с простым **API**: + +```sh +touch main.py +``` + +```python +from fastapi import FastAPI + +app = FastAPI() + +@app.get("/") +def read_root(): +    return {"Hello": "LinuxCamp"} +``` +3. Ну и наконец добавим в локальный каталог сам **Dockerfile** - это инструкция по созданию **docker** контейнера. Cейчас приведу общий пример, в следующих постах подробнее разберем его структуру: +```docker +FROM python:3.11-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install -r requirements.txt + +COPY . . + +CMD ["uvicorn", "main:app", "--host", "127.0.0.1", "--port", "8000"] +``` + +4. На этом все! Мы уже можем собрать свой **Docker-образ** и запустить его, после чего приложение будет доступно по адресу ниже: + +```sh +sudo docker build -t myapp . +sudo docker run -d -p 8000:8000 myapp +``` + +```sh +curl -L http://127.0.0.1:8000  +{"Hello":"LinuxCamp"} +``` +http://127.0.0.1:8000 + +Также можно посмотреть наличие нашего docker контейнера в списке запущенных контейнеров с помощью команды **docker ps**: + +```sh +docker ps + +CONTAINER ID   IMAGE                     +COMMAND              +640d6f4d1acb   fastapi_backend   +"uvicorn main:app --…" +``` diff --git a/LINUX/ENVIRONMENT.md b/LINUX/ENVIRONMENT.md new file mode 100644 index 0000000..85cfb8d --- /dev/null +++ b/LINUX/ENVIRONMENT.md @@ -0,0 +1,30 @@ +Системные переменные окружения: +`/etc/enviroments` +==`echo $PATH`== + +Сессионные переменные пользователя: +`.bashrc` +`export WORKON_HOME = ~/.envs` + +Системный bashrc (только локально): +`/etc/bash.bashrc` + +Системный profile (только для ВСЕХ удаленных): +`/etc/profile` + +Переменные SSH сессии: +`.bash_profile` + +После каждого изменения: +`source file_name` +или выход/вход в сессию + + +Создание с помощью файла: +`vim setenv.sh` +`export VAR1=234` +`export VAR2=hdhdy` +`export VAR3=7564888` + +запускаем файл: +==`./setenv.sh`== diff --git a/LINUX/EXIFTOOL.md b/LINUX/EXIFTOOL.md new file mode 100644 index 0000000..e68a0f3 --- /dev/null +++ b/LINUX/EXIFTOOL.md @@ -0,0 +1,41 @@ +🔍  **ExifTool — универсальный инструмент для анализа и правки метаданных** + +Если ты работаешь с фото, видео, документами или архивами — exiftool поможет тебе **увидеть, изменить или удалить метаданные** в любых файлах. + +💡  **Поддерживает более 130 форматов, включая:** +✔️ JPG, PNG, TIFF   +✔️ PDF, DOCX, XLSX   +✔️ MP4, MOV, AVI   +✔️ ZIP, RAR, ISO   +✔️ даже .exe, .torrent и .psd + +📌 **Установка (Debian/Ubuntu):** +`sudo apt install libimage-exiftool-perl + +🔧 **Основные команды:** + +📖 Посмотреть все метаданные файла: +`exiftool файл.jpg + +✏️ Удалить все метаданные: +`exiftool -all= файл.jpg + +🔁  Изменить дату создания: +`exiftool -CreateDate="2024:01:01 12:00:00" файл.jpg + +📝  Переименовать фото по дате съёмки: +`exiftool '-FileName -prune -o -name "*.json" - print + +find . -type d -name "* -prune -o -name "*.json" - print + +# поиск без учета регистра: +find . -iname "*.JSoN" + +# поиск файлов которые были изменены ДО: +find . -type f -atime 0 # =0= суток + +# или ПОСЛЕ: +find . -type f -mtime 0 # =0= суток + +# найти файлы доступ к которому был позднее чем: +find . -type f -anewer + +# найти файлы измененные позднее чем: +find . -type f -cnewer + +# ищем файлы размером: +find . -type f -size 140M + +# ищем файлы размером больше чем: +find . -type f -size +140M + +# ищем файлы размером меньше чем: +find . -type f -size -140M + +# ищем файлы размером между: +find . -type f -size -140M -a -type f -size +120M + +# отображаем размер файлов с сортировкой по размеру: +find . -type f -exec du -sh {} \; | sort -h | tail -3 # или | head -3 + +# вывести содержимое всех файлов: +find . -type f -exec {} \; + +# вывести содержимое всех файлов с разделением: +find . -type f -exec ls -la {} \; -exec cat {} \; -exec echo "" \; +``` +======================================= +💡 Архивируйте и удаляйте логи старше 7 дней с помощью этой команды: + +`find /var/log -type f -mtime +7 -exec tar -rvf old_logs.tar {} \; -exec rm {} \; + +🌟 Вы также можете запланировать это в cron, чтобы автоматизировать очистку! + +======================================= +⚙️ **Поиск последних измененных файлов в Linux** + +Когда нужно найти недавно измененные файлы, например, для отладки или мониторинга активности, можно использовать команду find. + +**📌 Поиск файлов по времени изменения** + +**🟢 Найти файлы, измененные за последние 24 часа:** + +find /path/to/dir -type f -mtime -1 + +Выведет файлы, измененные за последние сутки. + +**🟢 Найти файлы, измененные за последние 10 минут:** + +find /var/log -type f -mmin -10 + +Полезно для отслеживания активных логов. + +**🟢 Вывести 10 последних измененных файлов:** + +ls -lt | head -10 + +Сортирует файлы по времени изменения и показывает 10 самых свежих. + +🗣 **Эти команды помогают быстро находить файлы, с которыми недавно работали, а также анализировать активность в системе.** + +======================================= +⚙️ **Поиск и удаление пустых файлов и папок в Linux** + +Со временем в системе накапливаются пустые файлы и каталоги, занимая место и создавая беспорядок. Эти команды помогут быстро их найти и удалить. + +**📌 Поиск и очистка пустых файлов и папок** + +**🟢 Найти все пустые файлы в каталоге:** + +find /path/to/dir -type f -empty + +Выведет список всех пустых файлов в указанной директории. + + +**🟢 Удалить все пустые файлы:** + +find /path/to/dir -type f -empty -delete + +Автоматически удалит все найденные пустые файлы. + + +**🟢 Найти все пустые папки:** + +find /path/to/dir -type d -empty + +Выведет список пустых каталогов. + + +**🟢 Удалить пустые папки:** + +find /path/to/dir -type d -empty -delete + +Очищает систему от ненужных пустых директорий. + +➡️ **Автоматическая проверка битых символических ссылок в Linux** +```sh +#!/bin/bash + +# Целевая директория +TARGET_DIR="$HOME" + +# Поиск и удаление битых симлинков +find "$TARGET_DIR" -xtype l -print -delete + +echo "✅ Все битые символические ссылки удалены из $TARGET_DIR" +``` +🟢 Использует find с флагом -xtype l для поиска битых ссылок +🟢 Удаляет только символические ссылки, указывающие на несуществующие файлы. + +➡️ **Можно добавить в crontab для автоматической очистки раз в месяц:** +`0 5 1 * * /path/to/script.sh + +🗣 **Эти команды помогают поддерживать порядок в системе и освобождать место на диске.** + +#========================= +🖥 Команда **find** очень мощная, но многие пользователи не осознают, насколько гибкой может быть опция -exec! + +🌟 Так можно найти и удалить все файлы .log, которым больше 7 дней: +`find /path/to/logs -type f -name "*.log" -mtime +7 -exec rm {} \; + +🌟 А вот так найти и сжать все файлы .txt: +`find /path/to/files -type f -name "*.txt" -exec gzip {} \; + diff --git "a/LINUX/Fail2ban - \320\236\321\201\320\275\320\276\320\262\321\213 \320\267\320\260\321\211\320\270\321\202\321\213 \321\201\320\265\321\200\320\262\320\265\321\200\320\276\320\262.md" "b/LINUX/Fail2ban - \320\236\321\201\320\275\320\276\320\262\321\213 \320\267\320\260\321\211\320\270\321\202\321\213 \321\201\320\265\321\200\320\262\320\265\321\200\320\276\320\262.md" new file mode 100644 index 0000000..6f7e476 --- /dev/null +++ "b/LINUX/Fail2ban - \320\236\321\201\320\275\320\276\320\262\321\213 \320\267\320\260\321\211\320\270\321\202\321\213 \321\201\320\265\321\200\320\262\320\265\321\200\320\276\320\262.md" @@ -0,0 +1,42 @@ + +**Fail2ban** — это инструмент, который автоматически блокирует IP-адреса, совершающие подозрительную активность (например, попытки подбора паролей). Он анализирует логи и применяет временные баны через брандмауэр. + +`sudo apt install fail2ban + +⚙️ **Базовая настройка** + +1. Создадим копию конфига: +`sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local + +2. Отредактируем `jail.local`. Он поделён на секции, так называемые «изоляторы» (jails), каждая секция отвечает за определённый сервис и тип атаки. Добавим сервис, который нужно защищать, например SSH: +```sh +[DEFAULT] +ignoreip = 127.0.0.1/8 +bantime  = 600 +maxretry = 3 +banaction = iptables-multiport + +[ssh] +enabled  = true +port     = ssh +filter   = sshd +logpath  = /var/log/auth.log +maxretry = 6 +``` +• maxretry: количество неудачных попыток входа. +• bantime: время блокировки IP. + +Секция [ssh] отвечает за защиту SSH от повторяющихся неудачных попыток авторизации на SSH–сервере, проще говоря, «brute–force». + +Параметры из секции [DEFAULT] применяются ко всем остальным секциям, если не будут переопределены. + +3. После настройки, запускаем службу: +`sudo systemctl start fail2ban +`sudo systemctl enable fail2ban + +🔐 **Проверка работы** +• Посмотреть заблокированные IP: +`fail2ban-client status + +• Увидеть детали конкретного сервиса: +`fail2ban-client status ssh diff --git a/LINUX/GPG, PASS.md b/LINUX/GPG, PASS.md new file mode 100644 index 0000000..3720f0f --- /dev/null +++ b/LINUX/GPG, PASS.md @@ -0,0 +1,276 @@ +```sh +sudo su +apt install gpg + +gpg —full-gen-key + +# ставим 4096 бит +# 0 - ключ бессрочный +# y +# Real name: vlapa +# Email address: vlapa@ya.ru +# Comment: My Keys ! +# o +# Passphrase: `xxxxxxxxxx` +# мышь, клавиатура +``` + +```sh +echo "keyid-format 0xlong +> throw-keyids +> no-emit-version +> no-comments" > ~/.gnupg/gpg.conf + +gpg -k - публичный ключ +gpg -K - приватный ключ + +cd /tmp +echo "Hello World !" > /tmp/file.txt +ls +file.txt +cat file.txt +Hello World ! + + +# шифруем: +gpg -e -a -r vlapa@ya.ru file.txt + +# расшифровываем: +gpg -d -o file.txt file.txt.asc + +======================================== +# экспортируем: +gpg —export -a vlapa@ya.ru > pubkey.gpg +gpg —export-secret-key -a vlapa@ya.ru > seckey.gpg + +# Удаляем: +gpg —delete-secret-keys vlapa@ya.ru +gpg —delete-keys vlapa@ya.ru + +# импортируем: +gpg —import pubkey.gpg +gpg —import seckey.gpg + +# делаем доверенным: +gpg --edit-key +# - trust +# - 5 +# - Y +# - quit + +# Если нужно изменить пароль GPG-ключа (не содержимое записи) + +# Если вы сменили пароль самого GPG-ключа, но `pass` по-прежнему запрашивает старый, выполните: + +gpg --edit-key "Ваше_имя_или_email" + +# Затем в интерактивном меню введите: +passwd +save + +# Где найти имя ключа? +# Посмотрите в файле: +~/.password-store/.gpg-id +# там указано имя или ID ключа, используемого pass. + +======================================== +``` + +```sh +sudo apt install pass +which pass +pass init vlapa@ya.ru + +cd /root/.password-store/ +ls -la ~/.password-store/ + +# Добавить запись: +pass insert -m Email/mail.ru +конец - < ctrl+d > + +# Удалить запись: +pass rm Email/mail.ru + +# Редактировать запись: +pass edit Email/mail.ru + +# Система откроет файл в текстовом редакторе (по умолчанию заданный в переменной окружения $EDITOR или $VISUAL). + + +# Посмотреть запись: +pass Email/ya.ru + +# Помощь: +pass —-help | less + +# Сгенерировать: +pass generate email/vlapa.ru 25 + +# Получить пароль: +pass -c Email/ya.ru +# Будет предложено ввести мастер пароль Pass. Пароль будет находиться в буфере в течении 45 секунд. После чего буфер обмена будет очищен. + +# Если просто необходимо отобразить сохраненный пароль, просто удалите `-c` + +# Изменить данные пароля: +pass edit Email/ya.ru + +# =============================== + +pass git init +pass git remote add origin git@10.10.10.2:~/git/pass +pass git push +# commit делать не надо! +``` + +```sh +# Заменить первую строку в записи pass новым сгенерированным паролем, оказывается, можно так: + +pass generate -i Email/my@email.com 20 +``` + +============================================ +## Шифрование файлов при помощи GPG + +GNU Privacy Guard – это и есть GPG – свободная реализация шифрования данных для их защиты. Это очень актуально, ведь информацию, если она персональная, конфиденциальная или просто секретная необходимо защищать. Технология GPG основана на защите данных при помощи ключей шифрования, которые изначально должны быть созданы, а также соответствующим образом распространены между участниками обмена защищаемыми данными. + +Как работает защита данных с помощью ключей GPG + +Для того, чтобы стороны могли передавать друг другу зашифрованные данные, должен быть выполнен ряд условий: + +- наличие ключей шифрования GPG, которые представляют собой пару ключей — открытый и закрытый; + +- обе стороны должны обменяться открытыми ключами; + +- передающая сторона должна зашифровать данные с помощью + + открытого ключа, полученного от принимающей стороны при обмене + + этими ключами; + +- принимающая сторона при получении шифрованных данный должна + + расшифровать их своим закрытым ключом, открытой частью которого (т. е. открытым ключом) были зашифрованы данные передающей стороной. + + Отсюда вытекают два важных момента: + + +- расшифровать данные можно только закрытым ключом, который должен храниться у его владельца в надёжном и недоступном для посторонних месте; + +- данные всегда шифруются именно ключом адресата, т. е. того, кому нужно передать зашифрованные данные. + + Открытые ключи распространяются свободно. Ими можно обмениваться непосредственно, либо загружать на специальные сервера для хранения и обслуживания ключей GPG. + + Также необходимо иметь специальный сертификат для отзыва открытых ключей, чтобы сделать их недействительными. Кроме того механизм защиты данных по ключам позволяет подписывать отправляемые данные закрытым ключом, т. е. гарантировать, что эти данные отправлены именно тем, кто указан как отправитель. А соответствующий этому закрытому ключу открытый ключ, имеющийся у принимающей стороны может это подтвердить. Таким образом и достигается достоверная идентификация пользователей и надёжная защита передаваемых данных. + + Установка системы GPG в Linux + + Практически во всех популярных дистрибутивах Linux установить систему GPG позволяет система управления пакетами (СУП) этого дистрибутива. Например, для Ubuntu: + + `$ sudo apt install gnupg + + Наименования пакетов также могут различаться в зависимости от используемого дистрибутива, так для CentOS: + + `$ sudo yum install gnupg2 + + Теперь можно приступить к созданию самих ключей. + + Генерация и подпись ключей + + Для генерации и управления ключами системы GPG предназначена команда gpg. Более подробно с ней можно ознакомиться на страницах интерактивного руководства, используя команду man gpg. Для создания же, собственно, пары (открытый и закрытый) ключей нужно выполнить команду gpg с опцией —gen-key: + + +`$ gpg --gen-key + +В ходе выполнения команды у пользователя будут запрошены некоторые персональные данные, такие как полное имя и e-mail, необходимо их ввести, после чего начнётся генерация ключей, которая может занять некоторое время. После чего будет выведена информация о созданных ключах: + +`gpg (GnuPG) 2.2.4; Copyright (C) 2017 Free Software Foundation, Inc. +`This is free software: you are free to change and redistribute it. +`There is NO WARRANTY, to the extent permitted by law. +Замечание: "gpg --full-generate-key" вызывает полнофункциональный диалог создания ключа. + +GnuPG должен составить идентификатор пользователя для идентификации ключа. +Ваше полное имя: John Silver +Адрес электронной почты: john@silver.ru + +Вы выбрали следующий идентификатор пользователя: +"John Silver " +Сменить (N)Имя, (E)Адрес; (O)Принять/(Q)Выход? O +Необходимо получить много случайных чисел. Желательно, чтобы Вы +в процессе генерации выполняли какие-то другие действия (печать +на клавиатуре, движения мыши, обращения к дискам); это даст генератору случайных чисел больше возможностей получить достаточное количество энтропии. + +Необходимо получить много случайных чисел. Желательно, чтобы Вы +в процессе генерации выполняли какие-то другие действия (печать +на клавиатуре, движения мыши, обращения к дискам); это даст генератору случайных чисел больше возможностей получить достаточное количество энтропии. +gpg: ключ 49192168EGELF50S помечен как абсолютно доверенный +gpg: создан каталог '/home/john/.gnupg/openpgp-revocs.d' +gpg: сертификат отзыва записан в '/home/john/.gnupg/openpgp- revocs.d/FFBD0C66CBA22A8739ACB07949192168EGELF50S.rev'. +открытый и секретный ключи созданы и подписаны. + +`pub rsa3072 2019-03-27 [SC] [годен до: 2021-03-26] FFBD0C66CBA22A8739ACB07949192168EGELF50S uid John Silver +`sub rsa3072 2019-03-27 [E] [годен до: 2021-03-26] + +Здесь pub – это открытый ключ, значение «FFBD0C66CBA22A8739ACB07949192168EGELF50S» — его отпечаток, uid – идентификатор, состоящий из полей имени и e-mail, пользователя-владельца ключа. Этот идентификатор нужно использовать при указании ключа, а также + +для обозначения адресата, которому предназначается сообщение (данные), зашифрованное этим ключом. Можно указывать любое из полей. В нижеследующих примерах будет указываться email. + +Для созданных ключей также автоматически будет сгенерирован и сертификат отзыва. Либо это можно сделать вручную: + +`$ gpg --output ~/revocation.crt --gen-revoke your_email@address.ru + +Созданный сертификат revocation.crt нужно хранить также надёжно, как и закрытые ключи. При создании сертификата отзыва нужно указать возможную причину отзыва ключа, а также комментарий к отзыву. Эту информацию будут видеть другие пользователи систем управления ключами GPG. Опция —output служит для вывода сертификата отзыва в файл, в данном случае revocation.crt. + +Очень важно также подписывать ключи, которые были получены от других пользователей, если достоверно известно, что это именно те пользователи, за которых они себя выдают и ключи принадлежат действительно им. Это часто происходит при импорте открытых ключей других пользователей. После проверки их (и ключей и их пользователей-владельцев) очень желательно эти ключи подписать: + +`$ gpg --sign-key other_user@address.ru + +После подписи импортированного ключа нужно дать знать об этом его владельцу, чтобы он мог использовать этот ключ (ведь он подписанный) как аргумент для других пользователей, которые буду видеть, что его уже кто-то «одобрил» и доверяет ему. Для этого подписанный ключ экспортируется в файл и отправляется обратно его владельцу. + +Импорт и экспорт ключей + +Для того, чтобы экспортировать, например подписанный (в предыдущей главе) ключ нужно выполнить команду: + +`$ gpg –-output ~/signed.key –-export –-armor other_user@address.ru + +Опция —armor создаёт вывод в виде символов ASCII, т. е. в виде текста. По- умолчанию (если эту опцию не указывать) gpg создаст двоичный вывод. Теперь полученный файл с подписанным ключом можно отправлять его владельцу. Он в свою очередь может его импортировать себе в GPG, а также внести некоторую информацию о подписи: + +`$ gpg --import ~/signed.key + +Таким образом происходит, собственно обмен открытыми и подписанными ключами между пользователями. + +Если нужно перенести свой секретный ключ куда-либо (для использования на других компьютерах), то нужно использовать опцию —export-secrert-key: + +`$ gpg --output mygpgkey_private.txt --armor --export-secret-key my_email.address.ru + +Чтобы теперь импортировать этот секретный ключ на другом компьютере, нужно воспользоваться опцией —allow-secret-key-import: + +`$ gpg --allow-secret-key-import --import mygpgkey_private.txt + +Шифрование и дешифровка данных + +Чтобы зашифровать данные в GPG используется опция —encrypt: + +`$ gpg --recipient other_user@address.ru --encrypt somefile + +Здесь какой-либо файл somefile зашифруется открытым ключом пользователя, идентифицируемого с помощью его e-mail (other_user@address.ru) и только он сможет расшифровать этот файл своим закрытым ключом: + +`$ gpg --decrypt-files somefile.gpg + +Как можно видеть, при шифровке на выходе получаются файлы с расширением *.gpg. + +Проверка подлинности ключа пользователя + +Каким образом можно узнать, что пользователь, предоставивший свой открытый ключ действительно тот, за кого себя выдаёт, т. е. подлинный владелец соответствующего закрытого ключа? + +Для этого существует отпечаток ключа, который можно получить с помощью опции —fingerprint: + +`$ gpg --fingerprint john@silver.ru +`pub rsa3072 2019-03-27 [SC] [годен до: 2021-03-26] FFBD 0C66 CBA2 2A87 39AC B079 4919 2168 EGEL F50S uid [ абсолютно ] John Silver +`sub rsa3072 2019-03-27 [E] [годен до: 2021-03-26] + +Значение «FFBD 0C66 CBA2 2A87 39AC B079 4919 2168 EGEL F50S» нужно сверять с соответствующим значением, полученным таким же образом, но на стороне пользователя, приславшего этот ключ. Значения должны быть идентичны. + +Заключение + +В заключение необходимо отметить некоторую сложность для быстрого понимания самого механизма защиты GPG. Которая главным образом заключается в предварительном обмене ключами между взаимодействующими сторонами. Механизм GPG становится довольно просто использовать на практике. Если хотя бы пару раз выполнить обмен шифрованными данными, тем более, что это очень надёжно. \ No newline at end of file diff --git a/LINUX/GREP.md b/LINUX/GREP.md new file mode 100644 index 0000000..47619ef --- /dev/null +++ b/LINUX/GREP.md @@ -0,0 +1,392 @@ +```sh +# Найти точное соответствие +Для того чтобы искать соответствия только целым словам, используйте флаг "-w": +ls +'test script.sh' test test_1 test_2 +ls | grep -w test +test script.sh +test + + +# Аналогичного результата можно добиться через регулярное выражение: +ls | grep "\" +test script.sh +test + + +# "\<" — начало слова. +# "test" — искомый текст. +# "\>" — конец слова. + +# Угловые скобки должны экранироваться, иначе они будут интерпретироваться как простые символы. + +# Игнорировать регистр + +# По умолчанию, grep чувствительна к регистру, что немного мешает поиску. Проблема решается путем добавления ключа "-i" к команде: +ls | grep -i new +newFile +NewFile + + +#Вывод файлов с совпадениями. Используйте параметр "-l", чтобы вывести не сами строки, а только пути к файлам, в которых они содержатся: +grep -r -l "libnma" /usr/share +/usr/share/doc/libnma-gtk4-0/copyright + + +# Параметр "-r" отвечает за рекурсивный поиск - grep прошерстит не только 1 уровень каталога, а пойдет вглубь. + +# Поиск по нескольким параметрам. Для того, чтобы передать команде несколько вводных данных для определения совпадений, используется флаг "-e" перед каждым объектом поиска: +ls | grep -i -e new -e old +NewFile +newFile +oldFile +# Эту команду система понимает, как "или-или" и выводит все вхождения указанных слов. + +# Использование регулярных выражений. +# Настоящая сила grep проявляется, когда вы переходите от сопоставления простых строк к сопоставлению шаблонов - регулярных выражений. + +# Выражения бывают 2 типов: basic и extended. Одна из особенностей второго типа заключается в том, что некоторые символы требуют экранирования для сохранности фич: ?, +, {, |, (, ), <, >. + +# Но можно обойтись просто добавлением флага "-E": +ls | grep -E "script|test" + + +# Квадратные скобки "[]" используются, чтобы провести проверку на соответствие одному из указанных символов: +ls | grep "test[23]" +test2 +test3 + + +# Можем указать "-" для того, чтобы использовать диапазон значений: +ls | grep "test[1-3]" +test2 +test3 + + +# Каретка "^" используется для поиска строк, которые начинаются с указанного шаблона. Давайте найдем совпадение всех строк, начинающихся с заглавной буквы: +grep "^[A-Z]" myfile +A lot of text +Hello world + + +# Знак доллара "$" означает конец строки. В результат входят только те элементы, конец которых подходит под шаблон: +grep "world$" myfile +Hello world +hello world + + +# Точка "." обозначает любой одиночный символ, кроме новой строки. Регулярное выражение "....." обозначает любую строку, содержащую минимум 5 символов: +grep '.....' myfile + + +# Вертикальная черта "|" выполняет роль логического оператора "ИЛИ": +ls | grep "script\|test" +script +test + + +# В общем, регулярные выражения прекрасны и многообразны. Детальнее ознакомиться с ними можете по ссылке. + +# Экранирование +# Иногда выражения приводят к неожиданным результатам. +# Предположим, вы хотите найти в файле "myfile" только те строки, которые заканчиваются на ".sh". + +# Следующая команда выдаст неверный результат, поскольку точка — это регулярное выражение, означающее «любой символ»: +grep ".sh" myfile +script_name.sh +script_namesh + + +# Чтобы обойти эту проблему, требуется экранировать спецсимвол: +grep "\.sh" myfile +script_name.sh + + +# Такое решение иногда становится громоздким. К счастью, можно заставить grep забыть о регулярных выражениях и искать буквально то, что мы указываем. + +# Для этого используется параметр (fixed) "-F": +grep -F ".sh" myfile +script_name.sh + +``` + +**Полезные опции для поиска файлов через grep** + +Иногда нужно найти один/несколько файлов, содержащих определённую строку в: логах, конфигах или исходном коде. Для этого очень часто под рукой оказывается утилита **grep**. Для сводки, интересные флаги и регулярные выражения для grep мы рассматривали тута. + +По умолчанию команда фильтрует один файл. Для того чтобы выполнять поиск по каталогу, нужно добавить флаг "**-r**": + +```sh +grep -r "root" /etc/ +/etc/mdadm/mdadm.conf:MAILADDR root +``` + +Если хотим подсветить вхождения цветом, используем опцию "**--color=always**": +```sh +grep --color=always -r "root" /etc/ +``` + +Опции "-**C, -B, -A**" вообще имбовые, использую их целую неделю) Вы можете включить отображение не только текущей строки, в которой было найдено вхождение, но и **n** строк **до/после**: + +1) **-C**: выводит n строк до и после вхождения; + +```sh +grep -C2 -r --color=always "Hello3" ./ +./hello-Hello1 +./hello-Hello2 +./hello:Hello3 +./hello-Hello4 +./hello-Hello5 +``` + +1) **-B**: выводит n строк перед вхождением; +```sh +grep -B2 -r --color=always "Hello3" ./ +./hello-Hello1 +./hello-Hello2 +./hello:Hello3 +``` + +1) **-A**: выводит n строк после вхождения; + +```sh +grep -A2 -r --color=always "Hello3" ./ +./hello:Hello3 +./hello-Hello4 +./hello-Hello5 +``` + +Если наш шаблон поиска походит на регулярное выражения, нужно использовать флаг "**-F**". Таким образом мы говорим команде искать ровно то, что мы запросили - без скрытых подтекстов. + +Без флага мы получим строки, в которых присутствует хотя бы 1 символ из набора (**H, e, l, o**). С флагом получим строки с буквальным совпадением: + +```sh +grep "[Hello]" hello +Help me please +Helloup +[Hello] +``` + +```sh +grep -F "[Hello]" hello +[Hello] +``` +==================================================== +Команды `grep`, `egrep`и `fgrep`являются мощными утилитами для текстового поиска в Linux. Они позволяют пользователям выполнять поиск в файлах или выводе по шаблонам текста с использованием регулярных выражений (regex). В статье мы углубимся в эти команды, их расширенное использование и то, чем они отличаются. + +**grep** расшифровывается как **Global Regular Expression Print** . Утилита ищет шаблоны в файлах или входных данных и печатает строки, соответствующие шаблону. + +### Начнем с простого: базовый синтаксис и флаги grep: + +`grep [OPTIONS] PATTERN [FILE...] + +### Популярные опции: + +- `-i`: Поиск без учета регистра. +- `-v`: Обратное соответствие (показать строки, не соответствующие шаблону). +- `-r`: Рекурсивный поиск файлов в каталогах. +- `-l`: Показывать только те имена файлов, которые содержат совпадения. +- `-c`: Показать количество совпадающих строк. +- `-n`: Показывать номера строк с соответствующими строками. +- `--color`: Выделение соответствующих шаблонов в выходных данных. +- `-f`опция считывает шаблоны из указанного файла +- `-F`пропускает механизм регулярных выражений, это быстрее, чем использование обычного`grep` + +**Примеры:** + +Поиск без учета регистра: поиск "error" в файле, игнорируя регистр +`grep -i "error" /var/log/syslog + +Рекурсивный поиск в каталоге: поиск шаблона "disk" во всех файлах в /etc/, включая подкаталоги: +`grep -r "disk" /etc/ + +Отображение номеров строк: отображение строк, содержащих "bash" в ~/.bashrc, включая номера строк +`grep -n "bash" ~/.bashrc + +Обратное совпадение: отображение всех строк, не содержащих "root", в /etc/passwd +`grep -v "root" /etc/passwd + +Поиск по нескольким шаблонам с -E: поиск строк, содержащих либо "warn", либо "error" +`grep -E "warn|error" /var/log/syslog + +`egrep`: расширенный `grep`с увеличенной мощностью + +**egrep** является сокращением от **Extended GREP** . По сути, это то же самое, что и `grep -E`, то есть утилита использует расширенные регулярные выражения, которые поддерживают более продвинутые функции регулярных выражений, такие как `+`, `?`, `|`, и скобки без необходимости их экранирования. + +Найти строки, содержащие "error" или "fail" без экранирования | +`egrep "error|fail" /var/log/syslog + +Эквивалентно: +`grep -E "error|fail" /var/ log /syslog + +Найти строки с "color" или "colour" +`egrep "colou?r" file.txt + +Расширенные регулярные выражения — шаблоны соответствия: найти строки, где появляется "log", за которым следует 1 или более цифр +`egrep "log[0-9]+" logfiles.txt + +Расширенные шаблоны grep: находим то, что другие не могут найти: + +1. Мониторинг системных журналов в режиме реального времени + +Вы можете использовать `grep` совместно с`tail`для динамического мониторинга журналов и фильтрации определенных шаблонов: + +`tail -f /var/log/syslog | grep --color=auto -E "error|warn|critical" + +- **Пояснение:** в примере выше выполняется отслеживание `/var/log/syslog`в реальном времени и фильтрует строки, содержащие «error», «warn» или «critical». Опция `--color`выделяет совпадения. + +2. Поиск и замена шаблонов в нескольких файлах + +Используйте `grep`совместно с `sed`для замены шаблонов в файлах. Это полезно для массовых обновлений в нескольких файлах конфигурации. + +`grep -rl "old-string" /path/to/directory | xargs sed -i 's/old-string/new-string/g' + +- **Пояснение**: в примере выше выполняется рекурсивный поиск ( `-r`) всех файлов, содержащих «old-string», и передача их для `sed`замены на «new-string» ( `-i`для редактирования файлов на месте). + +3. Извлечение определенных данных из больших логов + +Для извлечения определенных типов информации, например IP-адресов или временных меток из файла лога, можно использовать расширенные регулярные выражения. + +**Пример:** извлечение всех IP-адресов из файла лога: + +`grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" /var/log/apache2/access.log + +- **Пояснение**: в примере выше используется `-oE`для сопоставления IP-адресов (формат адреса IPv4, соответствующий регулярному выражению) и их вывода. + +**Пример:** извлечение всех временных меток из файлов журнала: + +`grep -oE "\[([0-9]{2}/[A-Za-z]+/[0-9]{4}):([0-9]{2}:[0-9]{2}:[0-9]{2})" /var/log/apache2/access.log + +- **Пояснение** : в примере выше извлекаются временные метки лога Apache в формате `[dd/Mon/yyyy:hh:mm:ss]`. + +4. Аудит прав доступа к файлам + +Если вам нужно выполнить аудит безопасности, вы можете использовать `grep`для поиска файлов с определенными разрешениями. + +**Пример:** найти все файлы с разрешением `777` в каталоге и его подкаталогах: + +`find / -type f -perm 777 2>/dev/null | grep --color=auto "^" + +- **Пояснение** в примере выше утилита`find`ищет файлы с разрешениями `777`и направляет вывод в `grep`для их дополнительного выделения цветом. + +5. Поиск файлов на основе метаданных (размер, время, владелец) + +`grep`может использоваться в сочетании с `find`для поиска файлов на основе более конкретных метаданных, таких как время изменения файла, размер и владельцы. + +**Пример:** найти и отобразить файлы размером более 100 МБ со словом «ERROR»: + +`find /var/log/ - type f -size +100M - exec grep -H "ERROR" {} \; + +- **Пояснение**: этот метод использует `find`для поиска файлов размером более 100 МБ в `/var/log/`,а `-exec`запускает `grep`для поиска строк, содержащих «ERROR». + +**Пример:** найти файлы, измененные за последние 7 дней, и выполнить поиск по слову «failure»: + +`find /etc/ -type f -mtime -7 -exec grep -H "failure" {} \; + +- **Пояснение**: `find`ищет файлы, измененные за последние 7 дней, и передает их `grep`для поиска «failure». + +6. Параллельный grep для повышения производительности + +При поиске в больших каталогах с большим количеством файлов параллельный запуск `grep` может значительно повысить производительность. + +**Пример:** параллельный поиск строки «timeout» в большом каталоге: + +`find /path/to/dir -type f | xargs -P 4 grep "timeout" + +- **Пояснение**: `xargs -P 4`запускает параллельно 4 процесса `grep`, ускоряя поиск. + +7. Поиск множественных сложных шаблонов + +Вы можете использовать `grep`для поиска несколько сложных шаблонов, используя расширенные регулярные выражения. + +**Пример:** поиск строк, содержащих «error» с последующим числом или любую строку с «timeout»: + +`grep -E "error[[:digit:]]+|timeout" /var/log/syslog + +- **Пояснение**: этот подход позволяет найти строки, в которых после слова «error» следует одна или несколько цифр, или строки, содержащие слово «timeout». + +**Пример:** найти в логах строки, содержащие либо «failed login», либо «successful login»: + +`grep -E "(failed|successful) login" /var/log/auth.log + +- **Пояснение**: здесь используется расширенное регулярное выражение для сопоставления «failed login» или «successful login». + +8. Поиск в двоичных файлах и игнорирование непечатаемых символов + +`grep`может использоваться для поиска строк в двоичных файлах, игнорируя непечатаемые символы. + +**Пример:** поиск строки «password» во всех двоичных файлах: + +`grep -a "password" /usr/bin/* + +- **Пояснение**: опция `-a` обрабатывает двоичные файлы как текстовые файлы, позволяя искать понятные человеку строки внутри двоичных файлов. + +9. Фильтрация и подсчет уникальных вхождений + +Вы можете использовать `grep` совместно с утилитами`sort`и `uniq`для фильтрации и подсчета уникальных шаблонов, таких как IP-адреса, имена пользователей или ошибки. + +**Пример:** подсчет уникальных IP-адресов, обращающихся к веб-серверу: + +`grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" /var/log/apache2/access.log | sort | uniq -c | sort -nr + +- **Пояснение**: эта команда извлекает IP-адреса из access-лога, сортирует их, подсчитывает уникальные вхождения, а затем сортирует их по частоте. + +**Пример:** найти наиболее частые типы ошибок в файле лога: + +`grep "ERROR" /var/log/syslog | awk '{print $5}' | sort | uniq -c | sort -nr + +- **Пояснение**: команда выше извлекает типы ошибок (предполагая, что 5-е поле — это тип ошибки), подсчитывает уникальные ошибки и сортирует их по частоте. + +### Повышение производительности: ускорьте поиск с помощью оптимизации grep + +10. Объедините grep с другими инструментами автоматизации + +**Пример:** отправка оповещения, если в журналах появляется определенная ошибка + +Вы можете создать автоматизированную систему оповещений на основе определенных шаблонов в логах, используя `grep`. + +`grep -i "critical error" /var/log/syslog | mail -s "Critical Error Found" admin@example.com + +- **Пояснение**: здесь выполняется поиск «critical error» в системном логе и, если она найдена, администратору отправляется электронное письмо с соответствующими строками. + +11. Извлечение определенных данных из диапазона строк + +Используя `grep`в сочетании с `sed`или `awk`, вы можете извлечь определенные данные из диапазона строк. + +**Пример:** извлечение линий между двумя шаблонами: + +`sed -n '/START_PATTERN/,/END_PATTERN/p' file.txt | grep "important_string" + +- **Пояснение** : в примере выше извлекаются строки между `START_PATTERN`и `END_PATTERN`и передаются в`grep`для поиска `important_string`. + +12. grep с удаленных серверов с использованием SSH + +Вы можете удаленно выполнять поиск в журналах или файлах на другом сервере, используя SSH и `grep`. + +**Пример:** Поиск «error» в удаленных журналах: + +`ssh user@remote_server 'grep "error" /var/log/syslog' + +- **Пояснение**: в примере выше выполняется команда `grep` на удаленном сервере через SSH. + +13. Найти выполненные команды в истории Bash + +Чтобы отследить конкретную команду, использовавшуюся пользователями в прошлом, вы можете выполнить поиск в файлах истории bash. + +**Пример:** поиск всех выполненных команд `mount` в истории всех пользователей: + +`grep "mount" /home/*/.bash_history + +- **Пояснение**: команда выше проверяет файлы истории bash всех пользователей на предмет выполнения ими команды `mount`. + +14. Расширенное сопоставление с образцом с отрицательным просмотром + +`grep`не поддерживает просмотр вперед напрямую, но вы можете добиться похожего поведения с помощью креативного регулярного выражения. + +**Пример:** сопоставьте строки, содержащие «error», но не содержащие «network error»: + +`grep -E "error" file.txt | grep -v "network error" + +- **Пояснение** : сначала команда находит все строки, содержащие «error», а затем исключают строки, содержащие «network error». + +Эти расширенные варианты использования демонстрируют универсальность `grep`, `egrep`, и `fgrep`при применении в сложных случаях. Независимо от того, анализируете ли вы логи, ищете в больших наборах данных или автоматизируете административные задачи, эти мощные инструменты помогут вам эффективно найти именно то, что вам нужно. \ No newline at end of file diff --git "a/LINUX/GRUB \320\270 \320\277\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213 \321\217\320\264\321\200\320\260 \320\262 Linux.md" "b/LINUX/GRUB \320\270 \320\277\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213 \321\217\320\264\321\200\320\260 \320\262 Linux.md" new file mode 100644 index 0000000..1b8ace7 --- /dev/null +++ "b/LINUX/GRUB \320\270 \320\277\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213 \321\217\320\264\321\200\320\260 \320\262 Linux.md" @@ -0,0 +1,147 @@ +GRUB нужен, если: +- Нужно загрузить систему с особыми параметрами. +- Нужно добавить поддержку нескольких ОС в меню загрузки. +- Надо включить/выключить режим отладки ядра. +- Любите жить опасно и копаетесь в экспериментальных функциях Linux. +### Структура конфигурации GRUB: + +**Основной конфиг:** `/etc/default/grub` +Этот файл — первая остановка при настройке GRUB. Открываем: +```sh +sudo nano /etc/default/grub +``` +Пример стандартного конфига: +``` +GRUB_DEFAULT=0GRUB_TIMEOUT=5GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"GRUB_CMDLINE_LINUX="" +``` + +Обозначение параметров: +- `GRUB_DEFAULT=0` — загружается первая запись в меню GRUB. +- `GRUB_TIMEOUT=5` — пауза в секундах перед автозагрузкой. +- `GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"` — параметры, которые передаются ядру (по умолчанию он загружается без лишнего вывода). +- `GRUB_CMDLINE_LINUX` — дополнительные параметры, которые работают во всех режимах. + +Это далеко не все параметры. Ниже будет спойлер со всеми параметрами. +Изменения в файле **не вступят в силу сами по себе**. После редактирования надо обновить конфиг: +```sh +sudo update-grub +``` +Бывает, что система не загружается, но есть время на спасение. В такие моменты жмём `e` в меню GRUB, находим строку вида: +``` +linux /boot/vmlinuz-5.15.0-52-generic root=UUID=xxxx-xxxx-xxxx-xxxx ro quiet splash +``` +И меняем параметры ядра прямо в загрузчике. Например, если система зависает из‑за ACPI, можно добавить: +``` +acpi=off +``` +После чего грузимся с помощью `Ctrl + X` или `F10`. Эти изменения действуют **только для текущей загрузки**. + +Все параметры GRUB: +Если уже загрузились и хотите узнать, какие параметры применились: +``` +cat /proc/cmdline +``` +### Применение + +Несколько вариантов применения: +Если провайдер не поддерживает IPv6, а система продолжает упрямо пытаться его использовать: +``` +GRUB_CMDLINE_LINUX_DEFAULT="quiet splash ipv6.disable=1" +``` +Обновляем GRUB, перезагружаем систему. + +Если вам нужно больше информации при загрузке системы: +``` +GRUB_CMDLINE_LINUX_DEFAULT="debug ignore_loglevel" +``` + +После этого получите максимально подробный лог загрузки. +Если у вас простая система, можно попробовать грузиться без `initrd`: +1. В `/etc/default/grub` добавляем: + ``` + GRUB_CMDLINE_LINUX_DEFAULT="noinitrd" + ``` +2. Пересобираем загрузочный образ: + ``` + sudo update-initramfs -u + ``` +3. Обновляем GRUB и перезагружаемся. +### Безопасность GRUB + +Если не хотите, чтобы кто‑то случайно (или специально) изменил параметры ядра: +1. Создаём хеш пароля: + ``` + sudo grub-mkpasswd-pbkdf2 + ``` +2. Открываем файл `/etc/grub.d/40_custom`: + ``` + sudo nano /etc/grub.d/40_custom + ``` +3. Добавляем: + ``` + set superusers="admin"password_pbkdf2 admin grub.pbkdf2.sha512.10000.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + ``` +4. Обновляем GRUB: + ``` + sudo update-grub + ``` +Теперь при попытке редактирования параметров загрузки GRUB попросит пароль. +### GRUB Modules + +GRUB умеет очень многое, но многие даже не подозревают, что можно подгружать **модули** прямо в загрузчике. + +Загрузка по сети через PXE: +``` +insmod pxeinsmod netinsmod tftpset net_pxe_ifname=eth0set net_pxe_ip=192.168.1.100set net_pxe_server=192.168.1.1set net_pxe_gateway=192.168.1.1set root=(tftp,192.168.1.1)/ +``` +Можно добавить другие полезные модули: +- `lvm` — для работы с логическими томами. +- `cryptodisk` — для расшифровки зашифрованных дисков перед загрузкой. +- `usb` — если грузимся с USB‑диска. +### Кастомизация экрана загрузки GRUB + +Стандартное меню GRUB скучное? +1. Кладём красивую картинку в `/boot/grub/` (лучше PNG 1024×768). +2. Открываем `/etc/default/grub` и добавляем: + ``` + GRUB_BACKGROUND="/boot/grub/background.png" + ``` +3. Обновляем конфиг: + ``` + sudo update-grub + ``` + +Теперь при загрузке будет фон вместо унылого чёрного экрана. +Можно поменять цвета текста в файле `/etc/grub.d/05_debian_theme`: +``` +set menu_color_normal=white/blackset menu_color_highlight=yellow/blue +``` +(меняем белый текст на чёрном фоне на жёлтый текст на синем фоне) +### Если что-то пошло не так + +Сломали GRUB? +1. Загружаемся с LiveCD или флешки с Linux. +2. Открываем терминал и монтируем системный раздел: + ``` + sudo mount /dev/sdXn /mntsudo mount --bind /dev /mnt/devsudo mount --bind /proc /mnt/procsudo mount --bind /sys /mnt/sys + ``` +3. Заходим в chroot‑окружение: + ``` + sudo chroot /mnt + ``` +4. Переустанавливаем GRUB: + ``` + grub-install /dev/sdXupdate-grub + ``` +5. Перезагружаемся. Система снова жива. + +Если GRUB сломался окончательно, можно загрузить ядро вручную из UEFI: +1. Загружаемся в UEFI Shell или LiveCD. +2. Выполняем: + ``` + linux /boot/vmlinuz-5.15.0-52-generic root=/dev/sda2 roinitrd /boot/initrd.img-5.15.0-52-genericboot + ``` +3. Если сработало — переустанавливаем GRUB: + ``` + sudo grub-install /dev/sdasudo update-grub + ``` diff --git "a/LINUX/HOME_SERVER/5 \320\274\320\260\320\273\320\276\320\270\320\267\320\262\320\265\321\201\321\202\320\275\321\213\321\205 \320\272\320\276\320\274\320\260\320\275\320\264 \320\264\320\273\321\217 Linux.md" "b/LINUX/HOME_SERVER/5 \320\274\320\260\320\273\320\276\320\270\320\267\320\262\320\265\321\201\321\202\320\275\321\213\321\205 \320\272\320\276\320\274\320\260\320\275\320\264 \320\264\320\273\321\217 Linux.md" new file mode 100644 index 0000000..36ec72f --- /dev/null +++ "b/LINUX/HOME_SERVER/5 \320\274\320\260\320\273\320\276\320\270\320\267\320\262\320\265\321\201\321\202\320\275\321\213\321\205 \320\272\320\276\320\274\320\260\320\275\320\264 \320\264\320\273\321\217 Linux.md" @@ -0,0 +1,48 @@ +### tldr + +Если набрать, например, man tar, вы получите труднопонятную простыню текста. В отличие, от man, результат вывода команды ниже более понятен. + +tldr [command] + +Например: +tldr tar +Create a .tar archive +tar cf archive.tar file1 file2 + +Extract a .tar.gz archive +tar xzf archive.tar.gz + +List contents of a tar file +tar tf archive.tar + +### timeout + +Команда timeout позволяет выполнять команду в течение определенного времени. Если время истечет, тайм-аут убьет процесс, так что скрипт не будет выполняться вечно. Пара примеров: +timeout 5s ping google.com +timeout 30s ./myscript.sh + +### ncdu + +Инструмент ncdu похож на du, но с текстовым интерактивным интерфейсом, который позволяет проверять использование диска и удалять мусор на месте. +ncdu +--- /home/user ------------------------ +. 3.1 GiB [##########] Downloads + 1.2 GiB [###.......] Videos + 800.0 MiB [##........] Projects + 450.0 MiB [#.........] Documents + +### fd + +Это удобная альтернатива find с простым синтаксисом (больше никаких длинных флагов или странных шаблонов). Несколько примеров: +fd notes.txt +fd -e py +fd config /etc + +### trash + +Если вы быстро набирали rm и удаляли не тот файл (или, что еще хуже, целую папку), вы знаете, как жесток Linux в отношении удаления файлов. Кнопки «отменить» нет. Если что-то исчезло, значит, оно действительно исчезло. + +Вот тут-то и пригодится команда trash. Она работает так же, как rm, но вместо того, чтобы навсегда удалять файлы, она отправляет их в корзину, откуда их можно легко восстановить позже. Несколько примеров: +trash filename.txt +trash file1.txt file2.txt folder/ +trash-restore \ No newline at end of file diff --git "a/LINUX/HOME_SERVER/Caddy - \320\267\320\260\320\274\320\265\320\275\320\260 Nginx.md" "b/LINUX/HOME_SERVER/Caddy - \320\267\320\260\320\274\320\265\320\275\320\260 Nginx.md" new file mode 100644 index 0000000..3b4cfd6 --- /dev/null +++ "b/LINUX/HOME_SERVER/Caddy - \320\267\320\260\320\274\320\265\320\275\320\260 Nginx.md" @@ -0,0 +1,54 @@ +Caddy - замена Nginx с авто-TLS и минимальной настройкой + +Ты наверняка используешь Nginx как прокси-сервер или для выдачи статики. Но есть более свежий и удивительно простой веб-сервер - Caddy. Он сам получит HTTPS, сам перезапустится при изменении конфига и вообще просит к себе минимум внимания. Разбираемся, чем он крут. + +Caddy — это современный веб-сервер, написанный на Go. Его главная фишка — всё работает из коробки: + +1) Автоматический HTTPS от Let's Encrypt +2) Перезапуск при изменении конфига +3) Встроенный reverse proxy +4) Простая конфигурация (Caddyfile) + +Установка: + + +sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https + +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy.gpg + +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list + +sudo apt update +sudo apt install caddy + + +Минимальный пример Caddyfile: + + +example.com { + reverse_proxy localhost:3000 +} + +- Заменяешь "example.com" на свой домен +- localhost:3000 (твой сервис); + +Запуск сервера + +Caddy работает как systemd-сервис. Чтобы запустить или перезапустить: + + +sudo systemctl restart caddy + +После этого caddy сам проверит домен, получит HTTPS-сертификат от Let's Encrypt, настроит прокси и всё заработает) + +Почему это удобно + +- Не нужно возиться с SSL — HTTPS работает из коробки +- Конфигурация проще, чем у Nginx +- Отлично подходит для pet-проектов, демо, VPS-серверов + +Когда лучше остаться с Nginx + +1) Если у тебя уже сложная конфигурация с множеством rewrite, map, ssl_params +2) Если нужна кастомная сборка модулей +3) Если используешь stream (TCP/UDP proxy) \ No newline at end of file diff --git a/LINUX/HOME_SERVER/watch.md b/LINUX/HOME_SERVER/watch.md new file mode 100644 index 0000000..8c9409a --- /dev/null +++ b/LINUX/HOME_SERVER/watch.md @@ -0,0 +1,48 @@ +Команда watch: живой взгляд на процессы и метрики + +watch - утилита, которая повторно запускает любую команду через заданный интервал (по‑умолчанию 2 сек) и отображает её вывод полноэкранно. Подходит, когда: + +1) нужно увидеть изменение метрики прямо сейчас; +2) нет времени поднимать полноценный мониторинг; +3) требуется «подкараулить» событие и выйти, как только оно случится; +4) работает везде: на Debian, BusyBox в Alpine, внутри Docker‑контейнера; + +Базовый синтаксис + + +watch [-n <сек>] [-d] [-g] [-t] [-p] [-e] [-x] <команда> + +Ключевые опции + +"-n 5” — устанавливает интервал в 5 с. Хорош для медленных команд и экономии CPU; +-d — подсвечивает изменения относительно предыдущего вывода. Удобно отслеживать «скачки»; +-g — автоматически выходит, как только вывод изменится; полезно, когда ждёте окончания бэкапа; +-e — завершает работу при ненулевом exit‑коде команды. Превращает watch в health‑check; +-t — убирает заголовок watch. Идеален для чистых скриншотов; +-p — синхронизирует запуск с системными секундами. Это важно при очень частых опросах, например "-n 0.2"; +-x — запускает команду без обёртки в shell. Это помогает, если команда содержит символы вроде $ или *, которые интерпретируются bash'ем; + +Практические примеры + +Топ прожорливых процессов + + +watch -n 1 -d 'ps -eo pid,cmd,%cpu --sort=-%cpu | head' + +Ждём появления строки ERROR в логе и выходим + + +watch -g "grep -q ERROR /var/log/app.log" +echo 'Нашли ERROR!' + +Мини‑график latency + + +watch -n 0.2 -p 'ping -c1 1.1.1.1 | awk -F"=" "/time=/{print $4}"' + +Лайфхаки + +"-d" подсвечивает изменения — удобно, когда хочешь быстро понять, что именно изменилось. Добавь "--differences=permanent", чтобы подсветка не исчезала между обновлениями. +"-t" убирает заголовок watch — идеально для чистых логов или скриншотов. +"-p" даёт ровный ритм (1 Hz и выше), полезно для прецизионного мониторинга. +"-g" или "-e" + немного shell'а — и у тебя мини-скрипт, который реагирует на событие. \ No newline at end of file diff --git "a/LINUX/HOME_SERVER/\320\236\321\207\320\270\321\201\321\202\320\272\320\260 \321\201\320\270\321\201\321\202\320\265\320\274\321\213.md" "b/LINUX/HOME_SERVER/\320\236\321\207\320\270\321\201\321\202\320\272\320\260 \321\201\320\270\321\201\321\202\320\265\320\274\321\213.md" new file mode 100644 index 0000000..82c3ed6 --- /dev/null +++ "b/LINUX/HOME_SERVER/\320\236\321\207\320\270\321\201\321\202\320\272\320\260 \321\201\320\270\321\201\321\202\320\265\320\274\321\213.md" @@ -0,0 +1,36 @@ +```sh +# удалить старые версии установленных пакетов и очистить кэш пакетов от ненужных файлов: +sudo apt autoclean +# очистить кэш пакетов от ненужных файлов: +sudo apt clean + +sudo dpkg --remove +sudo dpkg --purge + +# удалить старое и неиспользуемое ядро: +sudo apt --purge remove linux-image- +# текущее работающее ядро: +uname -r + +snap list +sudo snap remove + +# удалить файлы журнала сталше 7 дней: +sudo journalctl --vacuum-time=7d +# удалить файлы пока общий размер журналов не станет меньше 100Мб: +sudo journalctl --vacuum-size=100M + +# увидеть использование диска домашним каталогом (для дальнейшей очистки): +du -h --max-depth=1 ~ + +# очистка кэшированных миниатюр в системе (графической): +rm -rf ~/.cache/thumbnails/* +``` +## Очистка кэша миниатюр + +Ubuntu создает миниатюры для предварительного просмотра изображений, видео и документов для быстрого доступа. Со временем эти миниатюры могут накапливаться и занимать место. +Чтобы удалить их, используйте следующую команду: + +==`rm -rf ~/.cache/thumbnails/* + +Это удалит все кэшированные миниатюры в вашей системе. \ No newline at end of file diff --git "a/LINUX/HOME_SERVER/\320\237\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270\320\265 \320\262 SYSTEMD.md" "b/LINUX/HOME_SERVER/\320\237\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270\320\265 \320\262 SYSTEMD.md" new file mode 100644 index 0000000..d0c989c --- /dev/null +++ "b/LINUX/HOME_SERVER/\320\237\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270\320\265 \320\262 SYSTEMD.md" @@ -0,0 +1,141 @@ +```sh +sudo systemctl edit --force --full mqtt_log.service +# vim /etc/systemd/system/mqtt_log.service + +#================================== +[Unit] +Description=mqtt_log +After=network.target + +[Service] +WorkingDirectory=/home/vlapa/mqtt_log +StandardError=append:/var/log/myapp/mqtt_log_error.log +User=vlapa +Group=vlapa +Type=simple +Restart=always +RestartSec=5 +ExecStart=/home/vlapa/mqtt/.venv/bin/python3 mqtt_log.py +Environment="PATH=/home/vlapa/mqtt/.venv/bin:/usr/bin:/bin" + + +[Install] +WantedBy=multi-user.target + +#================================== +# Создаем папку и файл +mkdir /var/log/myapp +touch /var/log/myapp/mqtt_log_error.log + +#================================== +sudo systemctl daemon-reload +sudo systemctl enable myapp +sudo systemctl start myapp +sudo systemctl status myapp + +# Логи сервиса с последнего запуска +journalctl -u mqtt_log.service -b + +``` +============ +sudo nano /etc/systemd/system/myapp.service +``` +# Пример конфигурации unit-файла + +[Unit] +Description=My Python Application +After=network.target + +[Service] +Type=simple +# Если используете виртуальное окружение: +ExecStart=/path/to/venv/bin/python /path/to/your/app.py +# Или без виртуального окружения: +# ExecStart=/usr/bin/python3 /path/to/your/app.py + +# Рабочая директория +WorkingDirectory=/path/to/your/app/directory + +# Пользователь для запуска (опционально) +User=username +Group=groupname + +# Перезапуск при ошибке +Restart=always +RestartSec=5 + +# Переменные окружения +Environment="PYTHONPATH=/path/to/your/app" +Environment="DATABASE_URL=mysql://user:pass@localhost/db" + +[Install] +WantedBy=multi-user.target + + +# Для приложений с зависимостями (виртуальное окружение) + +[Service] +Type=simple +ExecStart=/home/user/myapp/venv/bin/python /home/user/myapp/main.py +WorkingDirectory=/home/user/myapp +Environment="PATH=/home/user/myapp/venv/bin" + + +# Основные команды для управления + + +# Загрузить изменения конфигурации +sudo systemctl daemon-reload + +# Запустить сервис +sudo systemctl start myapp + +# Остановить сервис +sudo systemctl stop myapp + +# Включить автозапуск при загрузке +sudo systemctl enable myapp + +# Проверить статус +sudo systemctl status myapp + +# Просмотреть логи +sudo journalctl -u myapp -f + + +# Полезные опции для Python-приложений + +- **`Restart`** - политика перезапуска: + + - `no` - не перезапускать + + - `on-success` - только при успешном завершении + + - `on-failure` - при ошибке (рекомендуется) + + - `always` - всегда перезапускать + +- **`StandardOutput`/`StandardError`** - куда перенаправлять вывод: + + ini + + StandardOutput=journal + StandardError=journal + # Или в файл: + StandardOutput=file:/var/log/myapp.log + + +# Проверка и отладка + +bash + +# Проверить синтаксис конфигурации +sudo systemd-analyze verify /etc/systemd/system/myapp.service + +# Тестовый запуск без systemd +sudo -u username /path/to/python /path/to/app.py + +# Подробные логи +sudo journalctl -u myapp -n 100 -f + +``` \ No newline at end of file diff --git "a/LINUX/HOME_SERVER/\320\243\321\201\320\270\320\273\320\265\320\275\320\270\320\265 \320\261\320\265\320\267\320\276\320\277\320\260\321\201\320\275\320\276\321\201\321\202\320\270 Linux.md" "b/LINUX/HOME_SERVER/\320\243\321\201\320\270\320\273\320\265\320\275\320\270\320\265 \320\261\320\265\320\267\320\276\320\277\320\260\321\201\320\275\320\276\321\201\321\202\320\270 Linux.md" new file mode 100644 index 0000000..5d4ada7 --- /dev/null +++ "b/LINUX/HOME_SERVER/\320\243\321\201\320\270\320\273\320\265\320\275\320\270\320\265 \320\261\320\265\320\267\320\276\320\277\320\260\321\201\320\275\320\276\321\201\321\202\320\270 Linux.md" @@ -0,0 +1,178 @@ +### 10 bash-скриптов + +## 1. Включение автоматических обновлений безопасности + +Одной из важнейших практик поддержания безопасности систем является поддержание программного обеспечения в актуальном состоянии. Этот скрипт включит автоматические обновления безопасности в системе Linux, гарантируя, что система всегда будет защищена от уязвимостей. +```sh +#!/bin/bash +# Автоматическое обновление и установка исправлений безопасности +sudo apt-get update -y +sudo apt-get upgrade -y +sudo apt-get dist-upgrade -y +# Установка unattended-upgrades, если они не установлены +sudo apt-get install unattended-upgrades -y +# Включение автоматических обновлений +sudo dpkg-reconfigure --priority=low unattended-upgrades +``` +- `apt-get update -y` обновляет, а `apt-get upgrade -y`модернизирует все системные пакеты. +- `unattended-upgrades`обеспечивает применение автоматических обновлений безопасности. +- `dpkg-reconfigure`используется для настройки автоматических обновлений в системе. + +## 2. Отключение ненужных служб + +Во многих системах ненужные службы запущены по умолчанию. Отключение этих служб уменьшает количество целей для атаки сервера. Этот скрипт выведет список и отключит ненужные службы. +```sh +#!/bin/bash +# Список всех активных служб +echo "Список активных служб:" +sudo systemctl list-units --type=service --state=active +# Отключение ненужных служб +echo "Отключение ненужных служб..." +sudo systemctl stop apache2 +sudo systemctl disable apache2 +sudo systemctl stop mysql +sudo systemctl disable mysql +``` +- `systemctl list-units`показывает все запущенные службы. +- `systemctl stop`останавливает службу и `systemctl disable`отключает ее запуск при загрузке. +- Этот скрипт отключает Apache2 и MySQL в качестве примера. + +## 3. Внедрение политики надежных паролей + +Обеспечение соблюдения пользователями политики надежных паролей необходимо для безопасности системы. Этот скрипт настроит политику паролей системы для обеспечения надежных паролей. +```sh +#!/bin/bash +# Установите пакет libpam-pwquality для настройки политик паролей +sudo apt-get install libpam-pwquality -y +# Установите требования к сложности пароля +sudo sed -i 's/# minlen = 8/minlen = 12/' /etc/security/pwquality.conf +sudo sed -i 's/# minclass = 1/minclass = 4/' /etc/security/pwquality.conf +# Включите политику паролей в конфигурации PAM +sudo echo "password tribute pam_pwquality.so retry=3" >> /etc/pam.d/common-password +``` +- пакет `libpam-pwqualityп` предоставляет инструменты для соблюдения политики надежных паролей. +- параметр`minlen` устанавливает минимальную длину пароля, `minclass`задает количество требуемых классов символов (заглавные, строчные, цифры, специальные символы). + +## 4. Отключение входа root по SSH + +Разрешение входа root через SSH представляет собой существенный риск безопасности. Этот скрипт отключает вход root SSH путем редактирования конфигурации SSH. +```sh +#!/bin/bash +# Отключение вход root через SSH +sudo sed -i 's/^PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config +# Перезапуск службу SSH, чтобы применить изменения +sudo systemctl restart sshd +``` +- директива `PermitRootLogin` в конфигурации SSH контролирует, разрешен ли вход root. Установка значения «no» отключает его. +- `systemctl restart sshd`перезапускает службу SSH для применения изменений. + +## 5. Обеспечение прав доступа к конфиденциальным файлам + +Обеспечение правильных разрешений для конфиденциальных файлов важно для безопасности системы. Этот скрипт проверяет и корректирует разрешения для важных файлов, таких как `/etc/passwd`и `/etc/shadow`. +```sh +#!/bin/bash +# Убедитесь, что у /etc/passwd и /etc/shadow правильные права доступа +sudo chmod 644 /etc/passwd +sudo chmod 000 /etc/shadow +# Проверьте права доступа +echo "Права доступа для /etc/passwd:" +ls -l /etc/passwd +echo "Права доступа для /etc/shadow:" +ls -l /etc/shadow +``` +- `chmod 644`устанавливает разрешения на чтение и запись для владельца файла и только на чтение для остальных. +- `chmod 000`гарантирует, что никто не сможет читать или записывать в файл`/etc/shadow`, содержащий информацию о пароле пользователя. + +## 6. Настройка брандмауэра (UFW) + +Использование брандмауэра — важный шаг в защите сервера. Этот скрипт настраивает простой брандмауэр (UFW) для блокировки ненужных портов и разрешения только необходимых служб. +```sh +##!/bin/bash +# Включить UFW +sudo ufw enable +# Разрешить SSH (порт 22) и HTTP (порт 80) +sudo ufw allow 22 +sudo ufw allow 80 +# Запретить все остальные входящие соединения +sudo ufw default deny incoming +sudo ufw default allow outgoing +# Показать статус UFW +sudo ufw status +``` +- `ufw enable`включает несложный брандмауэр (UFW). +- команды «allow» разрешают соединения SSH и HTTP. +- «default deny incoming» и «default allow outgoing» устанавливают правила по умолчанию для блокировки входящего трафика и разрешения исходящего трафика. + +## 7. Регулярная ротация логов + +Чтобы избежать заполнения диска логами, следует настроить регулярную ротацию логов. Этот скрипт настраивает ротацию логов на ежедневной основе. +```sh +#!/bin/bash +# Установить logrotate, если он не установлен +sudo apt-get install logrotate -y +# Создать пользовательский файл конфигурации logrotate +echo "/var/log/*.log { + daily + rotate 7 + compress + delaycompress + missingok + notifempty + create 0640 root root +}" | sudo tee /etc/logrotate.d/custom_logs +# Проверить конфигурацию logrotate +sudo logrotate --debug /etc/logrotate.conf +``` +- `logrotate`управляет ротацией файлов журналов. +- файл конфигурации указывает, что журналы должны ротироваться ежедневно, сохранять 7 старых журналов и сжимать старые журналы для экономии места. +- флаг `--debug` проверяет правильность конфигурации logrotate. + +## 8. Блокировка учетных записей пользователей после неудачных попыток входа в систему + +Блокировка учетных записей пользователей после определенного количества неудачных попыток входа в систему является распространенной и важной функцией безопасности. Этот скрипт настраивает блокировку учетных записей пользователей после 5 неудачных попыток входа в систему. +```sh +#!/bin/bash +# Установить pam_tally2, если он не установлен +sudo apt-get install libpam-modules -y +# Установить политику блокировки для блокировки учетных записей пользователей после 5 неудачных попыток +echo "auth required pam_tally2.so onerr=fail deny=5" | sudo tee -a /etc/pam.d/common-auth +# Сбросить счетчик неудачных попыток входа для всех пользователей +sudo pam_tally2 --user * --reset +``` +- модуль`pam_tally2` отслеживает попытки входа в систему и блокирует учетную запись после указанного количества неудачных попыток ( `deny=5`). +- флаг `--reset` сбрасывает счетчик неудачных попыток входа для всех пользователей. + +## 9. Обнаружение и удаление файлов SUID/SGID + +Файлы SUID и SGID предоставляют пользователям повышенные привилегии и могут представлять угрозу безопасности. Этот скрипт проверяет и удаляет файлы SUID/SGID из системы. +```sh +#!/bin/bash +# Поиск файлов SUID и SGID +echo "Поиск файлов SUID и SGID..." +find / - type f -executable -perm /4000 -o -perm /2000 +# При желании удалите их (закомментируйте эту строку, если не хотите удалять файлы) +# find / -type f -executable -perm /4000 -o -perm /2000 -exec rm -f {} \; +``` +- `find / -type f -executable -perm /4000`ищет файлы с установленным битом SUID (Set User ID). +- `find / -type f -executable -perm /2000`ищет файлы с установленным битом SGID (Set Group ID). +- Скрипт выводит список файлов, вы можете удалить их, раскомментировав последнюю строку. + +## 10. Обеспечение надлежащих прав доступа пользователей к критически важным файлам + +Этот скрипт проверяет и гарантирует, что конфиденциальные файлы принадлежат пользователю root и доступны только ему. +```sh +#!/bin/bash +# Установка владельца и разрешений для критических системных файлов +sudo chown root:root /etc/passwd +sudo chmod 644 /etc/passwd +sudo chown root:root /etc/shadow +sudo chmod 600 /etc/shadow +# Проверка изменений +ls -l /etc/passwd /etc/shadow +``` +- `chown root:root`гарантирует, что файлами владеет пользователь root. +- «chmod 644» гарантирует, что файл `/etc/passwd`доступен для чтения другим пользователям, но доступен для записи только пользователю root. +- «chmod 600» ограничивает доступ `/etc/shadow`только пользователю root. + + +На этом все! Спасибо за внимание! Если статья была интересна, подпишитесь на телеграм-канал [usr_bin](https://t.me/usr_bin_linux), где будет еще больше полезной информации. \ No newline at end of file diff --git a/LINUX/HTOP.md b/LINUX/HTOP.md new file mode 100644 index 0000000..b16c7fe --- /dev/null +++ b/LINUX/HTOP.md @@ -0,0 +1,13 @@ +`F5` `t` - дерево каталогов +`N P M T` - сортировка по: PID, CPU%, MEM%, TIME +`F3 / +name` - поиск процесса +`F4 \ +name` - фильтр по имени +`\ ESC` - сброс фильтра +`u + выбор пользователя` - процессы 1 пользователя +`F9 k + выбор` - убить процесс +`l` - какие файлы открыты процессом +отображение всех переменных процесса: +выбираем процесс + е +`пробел` - выделение +`U` - отмена +`F2` - настройки diff --git a/LINUX/IPTABLES, NFTABLES.md b/LINUX/IPTABLES, NFTABLES.md new file mode 100644 index 0000000..2ab407c --- /dev/null +++ b/LINUX/IPTABLES, NFTABLES.md @@ -0,0 +1,101 @@ +**iptables** — это утилита командной строки для межсетевого экрана, которая позволяет настраивать правила управления трафиком в Linux. + +## Установка iptables + +Большинство дистрибутивов Linux включают iptables по умолчанию. Чтобы установить или обновить его: + +- **Debian/Ubuntu**: + +`sudo apt update sudo apt install iptables + +## Основные команды iptables + +- **Просмотреть текущие правила**: +`sudo iptables -L -v -n + +- **Разрешить входящий трафик SSH**: +`sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT + +- **Заблокировать IP-адрес**: +`sudo iptables -A INPUT -s [IP-адрес] -j DROP + +## **Сохранить правила**: + +- **Debian/Ubuntu:** +`sudo iptables-save > /etc/iptables/rules.v4 + +## Пример конфигурации: основные правила брандмауэра + +Чтобы настроить простой брандмауэр с помощью iptables, вы можете определить правила, разрешающие трафик SSH и HTTP, блокируя весь остальной входящий трафик: + +```sh +sudo iptables -P INPUT DROP +sudo iptables -P FORWARD DROP +sudo iptables -P OUTPUT ACCEPT +sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT +sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT +sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT +sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT +``` + +### Практические советы по настройке брандмауэра Linux + +1. **Всегда используйте политику сброса по умолчанию**: установите политику по умолчанию для сброса всего входящего трафика, разрешая только доверенные соединения. +2. **Разрешайте только определенные службы**: избегайте широких разрешений и указывайте только необходимые службы (например, SSH, HTTP). +3. **Ограничьте доступ по SSH**: ограничьте доступ по SSH, разрешив только доверенные IP-адреса и отключив вход с правами root. +4. **Используйте ведение журнала с осторожностью**: включите ведение журнала для отклоненных подключений, но ограничьте частоту, чтобы избежать переполнения журналов. + + +# nftables: будущее межсетевых экранов Linux + +**nftables** — это современная замена **iptables** , которая обеспечивает повышение производительности, более простой синтаксис и большую гибкость. Она работает с **netfilter** и предназначена для объединения функций брандмауэра, NAT и искажения пакетов. + +## Установка nftables + +В большинстве последних дистрибутивов Linux nftables предустановлен. Вы можете установить или обновить его с помощью: + +- **Debian/Ubuntu:** +`sudo apt install nftables + +## Базовые команды nftables + +- **Запуск и включение nftables**: +`sudo systemctl start nftables +`sudo systemctl enable nftables + +- **Просмотр активных правил:** +`sudo nft list ruleset + +- **Сброс всех правил**: +`sudo nft flush ruleset + +## Пример конфигурации nftables + +Вот как можно настроить nftables для разрешения трафика SSH, HTTP и HTTPS: + +1. **Откройте файл конфигурации nftables**: +`sudo nano /etc/nftables.conf + +**2. Добавьте набор правил**: + +```sh +table inet filter { + chain input { + type filter hook input priority 0; policy drop; + ct state established,related accept + tcp dport { 22, 80, 443 } accept + } + chain forward { + type filter hook forward priority 0; policy drop; + } + chain output { + type filter hook output priority 0; policy accept; + } +} +``` + +**3. Загрузите наборы правил**: + +`sudo nft -f /etc/nftables.conf + +Эта конфигурация по умолчанию сбрасывает все входящие соединения, но разрешает установленные соединения и новые соединения через порты SSH, HTTP и HTTPS. \ No newline at end of file diff --git a/LINUX/JOURNALCTL.md b/LINUX/JOURNALCTL.md new file mode 100644 index 0000000..f3ba698 --- /dev/null +++ b/LINUX/JOURNALCTL.md @@ -0,0 +1,160 @@ +## Синтаксис и опции journalctl + +Если утилита не выводит ничего, выполните её от имени суперпользователя: + +**journalctl опции** + +- **--full, -l** - отображать все доступные поля; +- **--all, -a** - отображать все поля в выводе full, даже если они содержат непечатаемые символы или слишком длинные; +- **--pager-end, -e** - отобразить только последние сообщения из журнала; +- **--lines, -n** - количество строк, которые нужно отображать на одном экране, по умолчанию 10; +- **--no-tail** - отображать все строки доступные строки; +- **--reverse, -r** - отображать новые события в начале списка; +- **--output, -o** - настраивает формат вывода лога; +- **--output-fields** - поля, которые нужно выводить; +- **--catalog, -x** - добавить к информации об ошибках пояснения, ссылки на документацию или форумы там, где это возможно; +- **--quiet, -q** - не показывать все информационные сообщения; +- **--merge, -m** - показывать сообщения из всех доступных журналов; +- **--boot, -b** - показать сообщения с момента определенной загрузки системы. По умолчанию используется последняя загрузка; +- **--list-boots** - показать список сохраненных загрузок системы; +- **--dmesg, -k** - показывает сообщения только от ядра. Аналог вызова команды dmesg; +- **--identifier, -t** - показать сообщения с выбранным идентификатором; +- **--unit, -u** - показать сообщения от выбранного сервиса; +- **--user-unit** - фильтровать сообщения от выбранной сессии; +- **--priority, -p** - фильтровать сообщения по их приоритету. Есть восемь уровней приоритета, от 0 до 7; +- **--grep, -g** - фильтрация по тексту сообщения; +- **--cursor, -c** - начать просмотр сообщений с указанного места; +- **--since, -S, --until, -U** - фильтрация по дате и времени; +- **--field, -F** - вывести все данные из выбранного поля; +- **--fields, -N** - вывести все доступные поля; +- **--system** - выводить только системные сообщения; +- **--user** - выводить только сообщения пользователя; +- **--machine, -M** - выводить сообщения от определенного контейнера; +- **--header** - выводить заголовки полей при выводе журнала; +- **--disk-usage** - вывести общий размер лог файлов на диске; +- **--list-catalog** - вывести все доступные подсказки для ошибок; +- **--sync** - синхронизировать все не сохраненные журналы с файловой системой; +- **--flush** - перенести все данные из каталога /run/log/journal в /var/log/journal; +- **--rotate** - запустить ротацию логов; +- **--no-pager** - выводить информацию из журнала без возможности листать страницы; +- **-f** - выводить новые сообщения в реальном времени, как в команде tail; +- **--vacuum-time** - очистить логи, давностью больше указанного периода; +- **--vacuum-size** - очистить логи, чтобы размер хранилища соответствовал указанному. + +## Горячие клавиши journalctl + +По умолчанию информация лога выводится в формате, в котором её можно листать. Давайте разберем горячие клавиши, которые вы можете для этого использовать: + +- **Стрелка вниз, Enter, e** или **j** - переместиться вниз на одну строку; +- **Стрелка вверх, y** или **k** - переместиться на одну строку вверх; +- **Пробел** - переместиться на одну страницу вниз; +- **b** - переместиться на одну страницу вверх; +- **Стрелка вправо, стрелка влево** - горизонтальна прокрутка; +- **g** - перейти на первую строку; +- **G** - перейти на последнюю строку; +- **p** - перейти на позицию нужного процента сообщений. Например, 50p перенесет курсор на середину вывода; +- **/** - поиск по журналу; +- **n** - найти следующее вхождение; +- **N** - предыдущее вхождение; +- **q** - выйти. + +## Шпаргалка по journalctl + +Чтобы найти именно то, что вам нужно, необходимо научится перемещаться по этому списку. Формат вывода лога довольно простой: + +**янв 13 20:55:55 sergiy-pc kernel: Linux version 4.15.0-43-generic** + +- **янв 13 20:55:55** - дата и время события; +- **sergiy-pc** - хост, на котором произошло событие; +- **kernel** - источник события, обычно это программа или сервис. В данном случае ядро; +- **Linux version 4.15.0-43-generic** - само сообщение. + +### 1. Просмотр логов сервисов + +Самый частый случай использования journalctl - это когда вы пытаетесь запустить какой-либо сервис с помощью systemd, он не запускается и systemd выдает вам такое сообщение подобного содержания: Failed to start service use journalctl -xe for details. Система сама предлагает вам какую команду надо выполнить: + +`sudo journalctl -xe` + +Как вы помните из опций, эта команда отображает последние сообщения в журнале и добавляет к ним дополнительную информацию, если она есть. Учитывая, что последнее, что мы делали - был наш сервис, то здесь будут сообщения от него и вы быстро сможете понять почему он не запускается. + +Чтобы отфильтровать сообщения только от определенного сервиса можно использовать опцию -u. Например: + +`sudo journalctl -eu apache2.service` + +### 2. Просмотр логов в режиме tail + +С помощью опции -f можно указать утилите, что необходимо выводить новые сообщения в реальном времени: + +`sudo journalctl -f` + +В этом режиме less не поддерживается, поэтому для выхода нажмите сочетание клавиш **Ctrl+C**. + +### 3. Просмотр логов загрузки + +В логе journalctl содержатся все логи, в том числе и логи загрузки. Для того чтобы открыть лог последней загрузки используйте опцию -b: + +`sudo journalctl -b` + +Посмотреть список всех сохраненных загрузок можно командой: + +`sudo journalctl -list-boots` + +Теперь, чтобы посмотреть сообщения для нужной загрузки используйте её идентификатор: + +`sudo journalctl -b 37d5c906c9c6404682f029b2c34ec9dc` + +### 4. Фильтрация по дате + +С помощью опции **--since** вы можете указать дату и время, начиная с которой нужно отображать логи: + +`sudo journalctl --since "2019-01-20 15:10:10"` + +Опция **--until** помогает указать по какую дату вы хотите получить информацию: + +`sudo journalctl -e --until "2019-01-20 15:05:50"` + +Или сразу скомбинируем две эти опции чтобы получить логи за нужный период: + +`sudo journalctl --since "2019-01-20 15:10:10" --until "2019-01-20 15:05:50"` + +Кроме даты в формате YYYY-MM-DD в этих опциях можно использовать такие слова, как yesterday, today, и tomorrow. Также допустимы конструкции **1 day ago** (один день назад) или **3 hours ago** (три часа назад). Ещё можно использовать знаки + и -. Например **-1h30min** будет означать полтора часа назад. + +### 5. Журнал ядра + +Если вы хотите посмотреть только сообщения ядра используйте опцию -k: + +`sudo journalctl -ek` + +### 6. Настройка формата вывода + +По умолчанию journalctl выводит информацию с помощью утилиты less, в которой вы можете её удобно листать и просматривать. Но формат вывода можно изменить: + +- **short** - используется по умолчанию; +- **verbose** - также, как и short, только выводится намного больше информации; +- **json** - вывод в формате json, одна строка лога в одной строке вывода; +- **json-pretty** - форматированный вывод json для более удобного восприятия; +- **cat** - отображать только сообщения, без метаданных. + +Чтобы указать нужный формат используйте опцию -o. Например: + +`sudo journalctl -o json-pretty` + +Или: + +`sudo journalctl -eo json-pretty` + +### 7. Очистка логов + +Сначала нужно посмотреть сколько ваши логи занимают на диске. Для этого используйте такую команду: + +`sudo journalctl --disk-usage` + +Чтобы уменьшить размер лога можно использовать опцию --vacuum-size. Например, если вы хотите, чтобы ваши файлы журналов занимали на диске не более 2 Гб, выполните команду: + +`sudo journalctl --vacuum-size=2G` + +Теперь старые логи будут удалены, пока общий объем хранилища не будет составлять 2 гигабайта. + +Также можно удалять логи по времени. Для этого используется опция --vacuum-time. Например, оставим только логи за последний год: + +`journalctl --vacuum-time=1years` \ No newline at end of file diff --git a/LINUX/KILL, PKILL.md b/LINUX/KILL, PKILL.md new file mode 100644 index 0000000..41443b2 --- /dev/null +++ b/LINUX/KILL, PKILL.md @@ -0,0 +1,16 @@ +Иногда процессы зависают или потребляют слишком много ресурсов. В Linux есть несколько удобных инструментов для их управления. + +📌 Основные команды: + +🟢 kill – завершение процесса по PID: + +ps aux | grep firefox # Найти PID процесса +kill 12345 # Завершить процесс + +🟢 pkill – завершение по имени процесса: + +pkill -9 firefox # Принудительно завершить Firefox + +🟢 htop – интерактивный мониторинг: + +htop # Открыть интерфейс для управления процессами \ No newline at end of file diff --git a/LINUX/LEMP.md b/LINUX/LEMP.md new file mode 100644 index 0000000..ed55edc --- /dev/null +++ b/LINUX/LEMP.md @@ -0,0 +1,119 @@ +`Sudo apt update && sudo` +`apt install nginx` +`sudo ufw app list` +`sudo ufw allow 'Nginx HTTP'` +`sudo ufw status` +`ip addr show` +`hostname -I` +`curl -4 icanhazip.com` + +## MySQL : + +`sudo apt install mysql-server` +`sudo mysql_secure_installation` + +Вам будет задан вопрос, хотите ли вы настроить `**VALIDATE PASSWORD PLUGIN**`**.** +Ответьте `Y`«да» или что-нибудь еще, чтобы продолжить без включения, + +Если вы ответите «да», вам будет предложено выбрать уровень проверки пароля. Имейте в виду, что если вы войдете `2`на самый сильный уровень, вы получите ошибки при попытке установить любой пароль, не содержащий цифр, прописных и строчных букв и специальных символов: + +Независимо от того, решили ли вы настроить `**VALIDATE PASSWORD PLUGIN**`, ваш сервер попросит вас выбрать и подтвердить пароль для пользователя **root** MySQL . Его не следует путать с **корнем системы**. Пользователь **root базы данных** — это пользователь-администратор с полными привилегиями в системе базы данных. Несмотря на то, что метод аутентификации по умолчанию для пользователя root MySQL не требует использования пароля, **даже если он установлен**, вам следует определить здесь надежный пароль в качестве дополнительной меры безопасности. + +Чтобы ответить на остальные вопросы, нажмите `Y`и ударьте `ENTER`клавишу при каждом подсказке. Это приведет к удалению некоторых анонимных пользователей и тестовой базы данных, отключению удаленного входа в систему с правами root и загрузке этих новых правил, чтобы MySQL немедленно принял внесенные вами изменения. + +Когда вы закончите, проверьте, можете ли вы войти в консоль MySQL: + +`sudo mysql` +`sudo mysql -**_``_**u root -p**_` + + +## PHP : + + +'sudo apt install php8.1-fpm php-mysql' +'sudo mkdir /var/www/your_domain' +`sudo chown -R $USER:$USER /var/www/` +`sudo vim /etc/nginx/sites-available/` + +`_server {_` + `_listen 80;_` + `_server_name your_domain www.your_domain;_` + `_root /var/www/your_domain;_` + + `_index index.html index.htm index.php;_` + + + +`_location / {_` +`_try_files $uri $uri/ =404;_` +`_}_` + +`_location ~ \.php$ {_` +`_include snippets/fastcgi-php.conf;_` +`_fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;_` +`_}_` + +`_location ~ /\.ht {_` +`_deny all;_` +`_}_` + +`_}_` + + +`listen` — Определяет, какой порт будет прослушивать Nginx. + +`root` — Корень документа, в котором хранятся файлы, обслуживаемые этим веб-сайтом. + +`Index` — Определяет, в каком порядке Nginx будет отдавать приоритет индексным файлам для этого веб-сайта. Обычной практикой является перечисление `index.html` файлов с более высоким приоритетом, чем `index.php` у файлов, чтобы можно было быстро настроить целевую страницу обслуживания в приложениях PHP. Вы можете настроить эти параметры, чтобы они лучше соответствовали потребностям вашего приложения. + +`server_name` — Определяет, за какие доменные имена и/или IP-адреса должен отвечать этот блок серверов. +**Укажите в этой директиве доменное имя или общедоступный IP-адрес вашего сервера.** + +`Location /` — Первый блок местоположения включает `try_files` директиву, которая проверяет наличие файлов или каталогов, соответствующих запросу URL. Если Nginx не сможет найти подходящий ресурс, он вернет ошибку 404. + +`location ~ \.php$` — Этот блок местоположения управляет фактической обработкой PHP, указывая Nginx на `fastcgi-php.conf` файл конфигурации и `php8.1-fpm.sock` файл, который объявляет, какой сокет связан с `php8.1-fpm`. + +`location ~ /\.ht` — Последний блок _**location**_ касается `.htaccess` файлов, которые Nginx не обрабатывает. Благодаря добавлению `deny all` директивы, если какие-либо `.htaccess` файлы попадут в корень документа, они не будут доставлены посетителям. + + +Активируйте свою конфигурацию, связавшись с файлом конфигурации из `sites-enabled` каталога Nginx: + +`sudo ln -s /etc/nginx/sites-available/your_domain /etc/nginx/sites-enabled/` + +Затем отсоедините файл конфигурации по умолчанию от `/sites-enabled/` каталога: + +`sudo unlink /etc/nginx/sites-enabled/default` + +`sudo nginx -t` +- проверить конфигурацию` + +`sudo systemctl reload nginx` + +`nano /var/www//index.html` + +`` +`` +`your_domain website` +`` +`` +`

Hello World!

` + +`

This is the landing page of your_domain.

` +`` +`` + +***/etc/_nginx/nginx.conf*** +***nginx -s*** - сигнал + +_Где сигнал может быть одним из нижеследующих:_ + +_**stop** — быстрое завершение_ +_**quit** — плавное завершение_ +_**reload** — перезагрузка конфигурационного файла_ +_**reopen** — переоткрытие лог-файлов_ + + +_Директивы, помещённые в конфигурационном файле вне любого контекста, считаются находящимися в контексте main. Директивы events и http располагаются в контексте main, server — в http, а location — в server._ + + +_В случае если что-то работает не как ожидалось, можно попытаться выяснить причину с помощью файлов access.log и error.log из каталога ==/var/log/nginx ( /usr/local/nginx/logs )== \ No newline at end of file diff --git "a/LINUX/LESS \320\270 SPLIT.md" "b/LINUX/LESS \320\270 SPLIT.md" new file mode 100644 index 0000000..0b0704f --- /dev/null +++ "b/LINUX/LESS \320\270 SPLIT.md" @@ -0,0 +1,39 @@ +**Работа с большими файлами в терминале: + +Работа с объемными файлами может быть затруднительна, однако есть два инструмента, которые несколько облегчают процесс. В этом посте кратко рассмотрим их. + +**less: Просмотр больших файлов** + +less — это инструмент для постраничного просмотра больших файлов без их полной загрузки в память. + +💬 **Открытие файла:** +`less bigfile.log + +Используйте клавиши Space для перехода вниз и b для возврата вверх. + +💬  **Поиск текста:** +Нажмите / и введите текст для поиска: +`/ошибка + +Используйте n для перехода к следующему совпадению. + +💬 **Вывод с подсветкой:** +Для удобства можно использовать less с подсветкой: + +`grep "ошибка" bigfile.log | less -R + +2️⃣ **split: Разделение больших файлов** + +Когда файл слишком большой для обработки, split поможет разбить его на части. + +💬 **Разделение файла по строкам:** +`split -l 1000 bigfile.log part_ + +**Результат:** файлы part_aa, part_ab, part_ac и т. д. + +💬 **Разделение по размеру:** +Например, каждая часть по 50 МБ: +`split -b 50M bigfile.log part_ + +💬 **Собрать файл обратно:** +`cat part_* > mergedfile.log \ No newline at end of file diff --git a/LINUX/LN.md b/LINUX/LN.md new file mode 100644 index 0000000..6273b79 --- /dev/null +++ b/LINUX/LN.md @@ -0,0 +1,15 @@ +ln -s <на какой существующий объект будет вести> <создаваемый симлинк> + +В системах на базе Linux (например, Ubuntu или [CentOS](https://www.dmosk.ru/terminus.php?object=centos)) и [FreeBSD](https://www.dmosk.ru/terminus.php?object=freebsd) симлинк для каталога и файла создаются одинаково: + +ln -s /usr/share/nginx/html/index.php /home/dmosk/ + +ln -s /usr/share/nginx/html /home/dmosk/ + +* в первом примере создана символьная ссылка в домашней директории пользователя **dmosk** на файл **index.php**; во втором — на каталог **/usr/share/nginx/html**. + +### Удаление + +Также используется одна команда: + +rm /home/dmosk/index.php \ No newline at end of file diff --git a/LINUX/LSOF.md b/LINUX/LSOF.md new file mode 100644 index 0000000..34ca676 --- /dev/null +++ b/LINUX/LSOF.md @@ -0,0 +1,87 @@ +👩‍💻 **Как использовать lsof для поиска процессов, блокирующих файлы** + +`lsof` — позволяет просматривать информацию об открытых файлах и связанных с ними процессах. Особенно полезно, если вы сталкиваетесь с ошибками доступа к файлам, например, при монтировании, удалении или использовании разделов. + +⚙️ **Примеры использования** +🟢 К примеру, если мы не можем удалить файл или получить к нему доступ, выполним: + +`lsof /путь/к/файлу + +В результате получим список процессов, которые используют файл, с подробной информацией: PID, пользователь, команда и доступ. + +🟢 Чтобы просмотреть все файлы, которые были открыты в определенном каталоге, используйте следующую команду: + +`lsof +D /директория + +➕ **Дополнительно:** + +➡️ `lsof -u username` — покажет файлы, открытые процессами указанного пользователя. + +➡️ `lsof -p PID` — отображает все файлы, которые использует процесс с заданным PID. + +➡️ `lsof -i :PORT` — покажет процессы, использующие определённый сетевой порт. + +=================================== +⚙️ **Проверка использования порта в Linux** + +Иногда нужно узнать, какой процесс использует определённый порт. В Linux это можно сделать несколькими способами. + +**📌 Просмотр процессов, занявших порт** + +**🟢 Определение процесса по порту:** +`sudo lsof -i :80` +Покажет процесс, использующий порт 80. + +**🟢 Вывести PID процесса:** +`sudo netstat -tulnp | grep :80` + +Альтернативный способ проверки. + +**🟢 Завершение процесса по PID:** +`sudo kill -9 ` +Останавливает процесс, если он мешает запуску сервиса. + +**Полезно при отладке серверов, проверки конфликтов между сервисами и устранения проблем с сетью.** + + +======================= + +**Как исправить "rm: cannot remove ‘file’: Device or Resource Busy"? + +Бывало же у вас такое - заканчиваешь работать с каким-то ресурсом (файлом/каталогом/устройством), хочешь его удалить и видишь ошибку о том, что он чем-то занят (используется системой либо каким-то процессом). + +`rm: cannot remove 'file-or-directory': Device or resource busy + +Тут нужно понять, в чем конкретная причина и чем он задействован: то ли процессом, то ли это, быть может, точка монтирования для какой-то FS…  + +Распространенных решения вижу 2:  +1. Использование команд "**lsof + fuser**" для определения занятых ресурсов и завершения процессов. Если вы не знаете, какая программа мешает выполнить операцию, используйте **lsof**: + +`$ lsof +D Sources/App/ + +COMMAND   PID    USER   FD   NAME +server  22331 xoadmin  txt    Sources/App/server +client  22332 xoadmin  txt    Sources/App/client + +Если у нас ресурс занимают 2 процесса, можно и через **kill** все грохнуть. Если их много, можно более автоматизированно - через **fuser**. Эта команда завершит все процессы, которые что-то используют в целевом каталоге: + +`$ fuser -k Sources/App/ + +После выполнение мы вырубим сразу оба процесса **22331** и **22332**. +2. Определение точки монтирования. Это полезно, когда какая-нибудь флешка или внешний диск монтирован в целевой каталог. Узнать, так ли это, можно через команду **mount**. Допустим, у меня не получается удалить **test**: + +`$ rm -rf test + +`rm: cannot remove 'test/tmpfsdir': Device or resource busy + +Проверяем, является ли **tmpfsdir** точкой монтирования: + +`$ mount | grep test/tmpfsdir + +`none on /home/u/test/tmpfsdir type tmpfs (rw,relatime,inode64) + +Вывод говорит о том, что в каталог **tmpfsdir** примонтирована FS **tmpfs**. Для успешного выполнения операции нужно размонтировать каталог: + +`$ sudo umount test/tmpfsdir + +`$ rm -rf test \ No newline at end of file diff --git a/LINUX/Let's Encrypt.md b/LINUX/Let's Encrypt.md new file mode 100644 index 0000000..43e6f4f --- /dev/null +++ b/LINUX/Let's Encrypt.md @@ -0,0 +1,33 @@ +**Установка и автоматизация SSL-сертификатов с Let's Encrypt** + +**Let's Encrypt** — это бесплатный сервис для генерации SSL/TLS-сертификатов. С его помощью можно легко защитить свои сайты HTTPS-соединением. Для автоматизации работы используется инструмент Certbot. + +1️⃣ **Установка Let's Encrypt (Certbot)** +Пример для дистрибутивов на базе Ubuntu/Debian: +`sudo apt update   +`sudo apt install certbot python3-certbot-nginx + +Если используется другой веб-сервер (например, Apache), замените nginx на apache2. + +2️⃣ **Получение сертификата** +Выполните команду для автоматического выпуска и настройки сертификата для Nginx: +`sudo certbot --nginx  + +Certbot автоматически обнаружит конфигурацию вашего веб-сервера и предложит выбрать домен. Далее сертификат будет установлен и активирован. + +3️⃣ **Автоматическое продление сертификатов** +Let's Encrypt выдает сертификаты сроком на 90 дней. Для их автоматического обновления настройте cron-задание: + +— Откройте редактор Cron: +`crontab -e   + +— Добавьте следующую строку для проверки и продления сертификатов каждые 12 часов: +`0 */12 * * * certbot renew --quiet   + +Certbot автоматически перезапустит веб-сервер, если сертификат обновится. +4️⃣ **Проверка работы** + +Убедитесь, что сертификат активен, открыв сайт через HTTPS. Также проверьте успешность автоматизации: +`sudo certbot renew --dry-run   + +Теперь ваш сайт защищен, а обслуживание сертификатов полностью автоматизировано 👍 \ No newline at end of file diff --git a/LINUX/MAILutils.md b/LINUX/MAILutils.md new file mode 100644 index 0000000..029fd6c --- /dev/null +++ b/LINUX/MAILutils.md @@ -0,0 +1,104 @@ +```sh +sudo apt update && sudo apt upgrade -y +sudo apt install postfix mailutils +``` +### Настройка Postfix + +Данный раздел покажет, как настроить Postfix для отправки сообщений с сервера, на котором он установлен (то есть, с локального хоста). + +Для этого Postfix должен быть настроен на прослушивание только интерфейса внутренней петли (loopback interface) – это виртуальный сетевой интерфейс, который используется сервером для внутреннего взаимодействия. Откройте конфигурационный файл Postfix с помощью редактора nano: + +`sudo nano /etc/postfix/main.cf` + +Найдите в нем следующий блок кода: + +`mailbox_size_limit = 0 +`recipient_delimiter = + +`inet_interfaces = all` + +Замените строку +`inet_interfaces = all +строкой +`inet_interfaces = loopback-only +Теперь этот блок выглядит так: + +`mailbox_size_limit = 0 +`recipient_delimiter = + +`inet_interfaces = loopback-only` + +Вместо loopback-only можно также использовать localhost: + +`mailbox_size_limit = 0 +`recipient_delimiter = + +`inet_interfaces = localhost` + +Завершив редактирование файла, сохраните изменения и закройте его (CTRL+X, затем Y и ENTER). После этого перезапустите Postfix: + +`sudo service postfix restart` +`sudo systemctl restart postfix` + +### 1. Синтаксис + +(В ТЕМЕ И ТЕКСТЕ СООБЩЕНИЯ ИСПОЛЬЗОВАТЬ ТОЛЬКО ENG !!!) +Можно отправить сообщение следующей командой: + +`echo "Test text" | mail -s "Test title" master@dmosk.ru + +_* в данном примере будет отправлено письмо на электронный адрес **master@dmosk.ru** с темой **Test title** и телом письма — **Test text**._ + +Если при отправке возникли проблемы, можно проверить логи следующей командой: + +`tail /var/log/mail.log + +### 2. Отправка письма с вложением +`echo 'Attachment' | mail -s 'Subject attachment message' -a /var/log/maillog -a /var/log/maillog2 master@dmosk.ru + +... или: + +`echo 'Attachment' | mail -s 'Subject attachment message' -A /var/log/maillog -A /var/log/maillog2 master@dmosk.ru + +_* где **/var/log/maillog** и **/var/log/maillog2** — файлы, которые будут прикреплены к письму. Обратите внимание, что обе команды отличаются по регистру опции **a** и **A** — все зависит от версии и сборки Linux (в каких-то нужно использовать маленькую, в каких-то — большую). Также обратите внимание, что в примере для Ubuntu мы отправим 2 файла — для этого просто добавляем к команде еще одну опцию прикрепления файла._ + +### 3. Несколько получателей + +Для отправки письма нескольким получателям, просто перечисляем их через запятую: + +`echo "Test text" | mail -s "Test title" master@dmosk.ru,shmaster@dmosk.ru + +_* в данном примере мы отправил письмо на ящики **master@dmosk.ru** и **shmaster@dmosk.ru**._ + +### 4. Отправка с копией + +Отправить копию на адрес master2@dmosk.ru: + +`echo "Test copy" | mail -s "Test copy title" master@dmosk.ru -c master2@dmosk.ru + +Отправить скрытую копию на адрес master3@dmosk.ru: + +`echo "Test hidden copy" | mail -s "Test hidden copy title" master@dmosk.ru -b master3@dmosk.ru + +### 5. Указать отправителя + +`echo "Test text" | mail -s "Test title" master@dmosk.ru -aFrom:postmaster@dmosk.ru + +### 6. Отправка через внешний SMTP сервер + +**а) Для Ubuntu / Debian:** + +`echo "Test text" | mail -s "Test title" -a "Smtp: smtp.mail.ru:25" -a "From: postmaster@dmosk.ru" -a "Return-path: postmaster@dmosk.ru" master@dmosk.ru + +Однако, если сторонний почтовый сервер работает по шифрованному каналу и требует аутентификацию, необходимо ввести следующее: + +`echo "Test text" | mail -v -s "Test title" -S smtp="smtp.dmosk.ru:587" -S smtp-use-starttls -S smtp-auth=login -S smtp-auth-user="master@dmosk.ru" -S smtp-auth-password="password" -S ssl-verify=ignore -S nss-config-dir=/etc/pki/nssdb -S from=postmaster@dmosk.ru master@dmosk.ru + +* где: + +- **smtp.dmosk.ru** — сервер smtp;  +- **587** — порт для подключения к серверу отправки;  +- **smtp-use-starttls** указывает на использование шифрования через TLS;  +- **smtp-auth=login** задает аутентификацию с использованием логина и пароля;  +- **smtp-auth-user** и **smtp-auth-password** — соответственно логин и пароль;  +- **ssl-verify=ignore** отключает проверку подлинности сертификата безопасности;  +- **nss-config-dir** указывает на каталог с базами nss;  +- **from** задает поле FROM + diff --git a/LINUX/MC.md b/LINUX/MC.md new file mode 100644 index 0000000..a1b6351 --- /dev/null +++ b/LINUX/MC.md @@ -0,0 +1,124 @@ +Чтобы открывать по f4 свой редактор, снимаем в настройках (в верхнем меню Настройки-Конфигурация) крестик с пункта Встроенный редактор. +Далее в .bashrc добавляем: + +`alias ss='subl'` +`alias mc='EDITOR=subl mc'` + +**Общее** +Ctrl+x,j — Показ фоновых задач +Сtrl-X+A — текущие VFS (ftp и пр.) +ESC+цифра — тоже что и F{цифра} (ESC+5 = F5) + +**F1-F12** +F1 — Помощь +F2 — Сохранить изменения в файл при редактировании +F3 — Просмотр файла +F3 — (Во время редактирования) Начать выделение текста. Повторное нажатие F3 закончит выделение +F4 — Редактирование файла +F5 — Скопировать выделенное +F6 — Переместить выделенное +F8 — Удалить выделенное   +Shift+F1 — вызывается меню быстрого перехода между точками монтирования на левой панели +Shift+F2 — вызывается меню быстрого перехода между точками монтирования на правой панели +Shift+F5 — скопировать файл в этот же каталог +Shift+F6 — переместить/переименовать файл в этот же каталог + +**Поиск** +ctrl-s — Быстрый поиск файла или каталога +alt-shift-? — Вызывается меню расширенного поиска файлов + +**Командная строка** +ctrl-a — курсор в начало +ctrl-e — курсор в конец +esc-! — выполнить команду с параметрами +ctrl-x, p или alt+a — вставить имя каталога +esc, enter; ctrl-enter — послать текущий объект в командную строку +ctrl-x,t — Вставка всех выделенных объектов в командную строку +esc, tab — дополнение команды +alt-a — вставить текущий каталог в командную строку +Alt-TAB (ESC+TAB) — Крутейшая комбинация для поиска загружаемых файлов (например, вы забыли как правильно пишется: netcfg или netconfig. Достаточно набрать в командной строке net и нажать Alt-TAB — вам сразу покажут что можно запустить, которое начинается со слова net) нажимать два раза! +alt-shift-! — вывод результата команды +ctrl-shift-v — вставка из буфера обмена + +**Операции над файлами** +Shift+F4 — Создает новый файл +shift-f6 — переименовать файл +Ctrl+x,i — Быстрый просмотр информации о текущем объекте на второй панели +Ctrl+x,q — Быстрый просмотр содержимого файла на второй панели +alt-. — показать скрытые файлы +ctrl-x, c — права на файл +ctrl-x, o — владелец файла +Shift-F3 — просмотр файла (raw/ без учета расширения) +Ctrl-X+L — создать ссылку на файл +Ctrl-X+S — создать символическую ссылку на файл +Ctrl + x v — создать относительную символическую ссылку   +trl+x,Ctrl+s — Редактировать символическую ссылку +F12 — Save as +ctrl-t — отметить файл либо снять отметку ++ — выбрать (отметить) группу файлов по регулярному выражению +\ — снять отметку с группы файлов +ctrl-x, ctrl-d — сравнить файлы +ctrl-t или Insert — Выделение текущего объекта + +**Работа с каталогами** +alt-o — открыть каталог под курсором в соседней панели +alt-i — открыть текущий каталог в соседней панели +сtrl-\ — показать «Справочник каталогов» +ctrl-x, h — добавить ссылку на текущий каталог в меню быстрого доступа +alt-shift-h — Отобразить историю каталогов +Ctrl+l — Перерисовать экран +Ctrl+r — перечитать содержимое каталога +Ctrl+Space — Показать размер текущего каталога +Ctrl-X+D — сравнить каталоги +ESC+C — быстро сменить каталог +alt-c -окно «Быстрая смена каталога» + +**Работа с панелями** +Ctrl-O — погасить панели и посмотреть что под ними (работает ТОЛЬКО в linux!) здесь же можно поработать в shell’e. +alt-, — Переключить режим отображения панелей (вертикально/горизонтально) +alt-t переключение подробного режима отображения панели +Meta+c — Вызывается меню быстрой смены текущего каталога на активной панели +ctrl-u — поменять панели местами +alt-g — отмечает первый файл или каталог в отображаемом списке на панели (по сути как клавиша Home, поднимает курсор вверх) +alt-e — поменять кодировку панели +Ctrl + x i — переводит пассивную панель в режим «Информация» +Ctrl + x q — переводит пассивную панель в режим быстрого просмотра +ctrl-x ! — меню → «Критерий панелизации» (git) + +**История** +alt+h; esc, h — история командной строки +alt-p, alt-n — вернуть предыдущую (следущую) командную строку (строки) +alt-y. Перемещение к предыдущему каталогу из истории перемещения по каталогам; эквивалентно нажатию мышкой на символ ‘<‘ в верхнем углу панели. +alt-u. Перемещение к следующему каталогу из истории перемещения по каталогам; эквивалентно нажатию мышкой на символ ‘>’. + +**Редактор** +F4 (в редакторе) — поиск с заменой +F6 (в редакторе) — поиск с помощью регулярного выражения +Shift-Del (в редакторе) — удалить блок (и положить его в clipboard) +Shift-Ins (в редакторе) — вставить блок из буфера (работает даже в другой консоли!!!) Можно редактировать два и более файлов на разных консолях и гонять блоки между ними. +Shift+F3 — Начать выделение блока текста. Повторное нажатие F3 закончит выделение +Shift+F5 — Вставка текста из внутреннего буфера обмена mc (прочитать внешний файл) +Meta+i — Переключение режима «Автовыравнивание возвратом каретки», удобно при вставке отформатированного текста из буфера обмена +Meta+l — Переход к строке по её номеру +Meta+q — Вставка литерала (непечатного символа).См. таблицу ниже +Meta+t — Сортировка строк выделенного текста +Meta+u — Выполнить внешнюю команду и вставить в позицию под курсором её вывод +Ctrl+f — Занести выделенный фрагмент во внутренний буфер обмена mc (записать во внешний файл) +Ctrl+k — Удалить часть строки до конца строки +Ctrl+n — Создать новый файл +Ctrl+s — Включить или выключить подсветку синтаксиса +Ctrl+t — Выбрать кодировку текста +Ctrl+u — Отменить действия +Ctrl+x — Перейти в конец следующего +Ctrl+y — Удалить строку +Ctrl+z — Перейти на начало предыдущего слова +Meta+Enter — Диалог перехода к определению функции +Meta+- — Возврат после перехода к определению функции +Meta++ — Переход вперед к определению функции +Meta+n — Включение/отключение отображения номеров строк +tab Отодвигает вправо выделенный текст, если выключена опция «Постоянные блоки» +Meta-tab Отодвигает влево выделенный текст, если выключена опция «Постоянные блоки» +Shift+Стрелки Выделение текста +Meta+Стрелки Выделение вертикального блока +Meta+Shift+- Переключение режима отображения табуляций и пробелов +Meta+Shift++ Переключение режима «Автовыравнивание возвратом каретки» \ No newline at end of file diff --git a/LINUX/MOSQUITTO.md b/LINUX/MOSQUITTO.md new file mode 100644 index 0000000..94efb8f --- /dev/null +++ b/LINUX/MOSQUITTO.md @@ -0,0 +1,121 @@ +```bash +sudo apt-add-repository ppa:mosquitto-dev/mosquitto-ppa +sudo apt-get update +sudo apt install mosquitto mosquitto-clients + +sudo service mosquitto status +# mosquitto start/running, process 16614 + +sudo mosquitto_passwd -c /etc/mosquitto/mosquitto.pwd user_name +sudo mosquitto_passwd /etc/mosquitto/mosquitto.pwd user_name + +sudo vim /etc/mosquitto/mosquitto.conf + +# Добавляем в конец файла, чем говорим MQTT где искать наш файл с паролями: +allow_anonymous false +password_file /etc/mosquitto/mosquitto.pwd + +vim /etc/mosquitto/mosquitto.pwd +# добавляем клиента: +user_name-1:EXAMPLE_PASSWORD +user_name-2:STRONG_PASSWORD + +# Зашифруйте открытые пароли пользователей с помощью `mosquitto_passwd`утилиты: +sudo mosquitto_passwd -U /etc/mosquitto/mosquitto.pwd +# посмотрим: +sudo cat /etc/mosquitto/mosquitto.pwd + +sudo chmod 0644 /etc/mosquitto/mosquitto.pwd + +sudo service mosquitto restart + +``` + +Подпишитесь на `/home/sensor/temperature`тему, используя действительный логин и пароль. Например, `john_doe`и `EXAMPLE_PASSWORD`: +```sh +mosquitto_sub -u home -P 1234 -t test + +mosquitto_sub -u john_doe -P EXAMPLE_PASSWORD -t "/home/sensor/temperature" +``` + +Опубликуйте новое сообщение в `/home/sensor/temperature`теме: +```sh +mosquitto_pub -t "test" -m "hello world" -u home -P 1234 + +mosquitto_pub -u john_doe -P EXAMPLE_PASSWORD -t "home/sensor/temperature" -m "22.5" -q 1 -r +``` + +Вывод: +```sh +22.5 +``` + +Просмотрите все активные фоновые задания и запишите `mosquitto_sub`идентификатор задания: +```sh +jobs +``` + +Вывод: +```sh +[1] Running mosquitto_sub -u john_doe -P EXAMPLE_PASSWORD -t "home/lights/sitting_room" & +``` + +Остановите тему, указав идентификатор задания: +```sh +kill %1 +``` + +Вывод: +```sh +[1] Done mosquitto_sub -u john_doe -P EXAMPLE_PASSWORD -t "home/lights/sitting_room" & +``` + +Попробуйте подписаться на `/home/sensor/temperature`тему, используя неверные учетные данные +```sh +mosquitto_pub -u john_doe -P VERY_WRONG_PASSWORD -t "home/sensor/temperature" -m "22.5" -q 1 -r +``` + +Вывод: +```sh +Connection error: Connection Refused: not authorised. +Error: The connection was refused. +``` + +Вышеприведенный вывод показывает, что соединение не устанавливается, поскольку Mosquitto не может авторизовать пользователя. + + +**Настройка** + +После установки вы получите mqtt-брокера, слушающего только локальные интерфейсы. Причем, он об этом вам не скажет. У меня ушло довольно много времени, чтобы понять что не так, вы же получите эти знания сразу ) +Поправим конфигурационный файл: + +**/etc/mosquitto/mosquitto.conf** +```sh + +# пишем в логи всё (если не нужно, то потом можно отключить) +log_type error +log_type warning +log_type notice +log_type information + +# разрешаем подключаться без пароля (мне это было надо) +allow_anonymous true + +# те самые "слушатели", без них москит будет слушать только локальные интерфейсы +listener 1883 192.168.1.2 +listener 1883 192.168.3.2 + ``` + + В принципе, это все, по крайней мере, что было нужно мне. + Обратите внимание, что у **listener** вначале идет порт и только потом ip. Если ip не указывать, записать listener 1883, то будут доступны все интерфейсы, как внутренние, так и внешние. + + **Эксплуатация** + +Новые настройки сами себя не применят, поэтому сервис нужно перезагрузить: +`systemctl restart mosquitto + +Логи москит пишет в **/var/log/mosquitto/mosquitto.log** + +Помните, мы вместе с mosquitto поставили mosquitto-clients? При помощи клиента, например, можно смотреть сообщения в брокере: + + `mosquitto_sub -d -t '#' diff --git a/LINUX/NC.md b/LINUX/NC.md new file mode 100644 index 0000000..b51c5ae --- /dev/null +++ b/LINUX/NC.md @@ -0,0 +1,33 @@ +Эффективно копируем гигабайтные файлы по сети в Linux / Unix / Mac OS + +```sh +# Передатчик: +nc -lp 8888 -q 1 < file_name.ext + +# приемник: +nc vlapa@178.20.46.157 > file_name.ext + +watch "du -h file_name.ext" + +#=============================== +# Проверка <адрес порт> : +nc localhost 22 + +# Проверка портов: +nc -z -v localhost 1-30 + + +#=============================== +# Передача файлов через сеть (с ожиданием и не защищенно): + +# запускаем сервер: +nc -lp 8888 -q 1 > file.name + +# скачиваем удаленно: +nc 192.168.88.61 8888 < file.name + + +du -h file.name + +watch «du -h file.name» +``` \ No newline at end of file diff --git a/LINUX/NCDU, BAT, FD, EXA, HTTPIE.md b/LINUX/NCDU, BAT, FD, EXA, HTTPIE.md new file mode 100644 index 0000000..3d581b1 --- /dev/null +++ b/LINUX/NCDU, BAT, FD, EXA, HTTPIE.md @@ -0,0 +1,6 @@ +NCDU - замена размер в интерактивном режиме +BAT - замена с подсветкой синтаксиса +FD - замена поиск +EXA - замена с подсветкой +`exa --tree --level=2 --long` + diff --git a/LINUX/NCDU.md b/LINUX/NCDU.md new file mode 100644 index 0000000..eb81446 --- /dev/null +++ b/LINUX/NCDU.md @@ -0,0 +1,20 @@ +— лучший способ быстро очистить место на сервере + +Когда на сервере внезапно заканчивается место, первое, что хочется — понять, что его съело. Вместо того, чтобы долго ковыряться в du и find, просто поставь ncdu. + +Это интерактивная утилита для анализа диска в терминале. Работает быстро, показывает размеры папок/файлов и позволяет удалять ненужное прямо из интерфейса: + +`sudo apt install ncdu + +Для сканирования нужно просто ввести команду и передать целевой каталог. Чтобы промониторить корень: + +`sudo ncdu / + +Нужные кнопочки: + +Навигация - стрелками вверх/вниз. +Удаление - клавиша d. +Обновить - g. +Выйти - q. + +В отличие от "du -sh *", ncdu не только показывает вес директорий, но и позволяет разобраться в них быстрее. Особенно полезно, если забит логами /var/log, кто-то накатил 100500 .tar.gz или забыли почистить кэш в home. Утилита очень выручает, когда каждая секунда важна. Установил - посмотрел - удалил - освободил. \ No newline at end of file diff --git a/LINUX/NETWORK/DNS.md b/LINUX/NETWORK/DNS.md new file mode 100644 index 0000000..4931e61 --- /dev/null +++ b/LINUX/NETWORK/DNS.md @@ -0,0 +1,36 @@ +```sh +sudo vim /etc/resolv.conf +#======== +nameserver 8.8.8.8 +nameserver 8.8.4.4 + + +sudo resolvectl status + +# Через NETPLAN + +network: + version: 2 + renderer: networkd + ethernets: + enp0s3: + dhcp4: no + addresses: [192.168.157.140/24] + gateway4: 192.168.157.140 + nameservers: + addresses: [8.8.8.8,8.8.4.4] +``` + +В части сервера имен этого примера указано, что система должна разрешать имена хостов с помощью публичных DNS-серверов Google ( `8.8.8.8`и `8.8.4.4`). Кроме того, у машины есть IP-адрес `192.168.157.140`и маска подсети `24`. `192.168.157.140`— это шлюз, через который проходит трафик. + +После внесения изменений в файл конфигурации выполните следующую команду, чтобы изменения вступили в силу: + +```sh +sudo netplan --debug generate +sudo netplan try +sudo netplan apply +``` + +Сделав это, вы сможете использовать серверы имен, перечисленные в файле конфигурации, и обновлять настройки DNS системы. + +``` \ No newline at end of file diff --git a/LINUX/NETWORK/IPERF3.md b/LINUX/NETWORK/IPERF3.md new file mode 100644 index 0000000..869d8f6 --- /dev/null +++ b/LINUX/NETWORK/IPERF3.md @@ -0,0 +1,33 @@ +```bash +# Проверка скорости подключения + +sudo apt install iperf3 + +# Порт по умолчанию = 5201, открываем его: +sudo ufw allow 5201 + +# Запуск клиента: +iperf3 -c адрес_сервера -f m + +# Запуск сервера: +iperf3 -s -f m + +iperf3 -h # help +-p, --port - указать порт для сервера или к какому порту надо подключаться; +-f, --format - формат выводимых данных, доступны значения: k,m,g,t,K,M,G,T; +-i, --interval - интервал времени между выводами результата тестирования; +-F, --file - использовать данные из файла вместо генерации случайных данных; +-V, --verbose - вывод максимально подробной информации; +-J, --json - вывод в формате JSON; +--logfile - записывать информацию в лог файл; +--forceflush - очищать историю вывода перед выводом следующего результата тестирования; +-v, --version - версия программы. + +k - килобиты; +m - мегабиты; +g - гигабиты; +K - килобайты; +M - мегабайты; +G - гигабайты; +T - терабайты. +``` diff --git a/LINUX/NETWORK/NETPLAN.md b/LINUX/NETWORK/NETPLAN.md new file mode 100644 index 0000000..e556960 --- /dev/null +++ b/LINUX/NETWORK/NETPLAN.md @@ -0,0 +1,230 @@ +```bash +sudo systemctl --type=service | grep net +sudo netplan apply +networkctl status +journalctl -f -u systemd-networkd.service + +#=============================== +ip a +sudo vim /etc/netplan/file_name.yaml + +#=============================== + +network: + renderer: networkd + ethernets: + enp2s0: + dhcp4: no # true + addresses: + - 192.168.88.2/24 + routes: + - to: default + via: 192.168.88.1 + nameservers: + addresses: + - 192.168.88.1 + - 8.8.8.8 + - 8.8.4.4 + version: 2 + +#=============================== + +network: +  version: 2 +  renderer: networkd +  ethernets: +    enp2s0: +      dhcp4: no +      addresses: +        - 192.168.88.40/24 +      routes: +        - to: default +          via: 192.168.88.1 +      nameservers: +        addresses: +          - 192.168.88.1 +          - 77.88.8.8 +          - 77.88.8.1 +          - 8.8.8.8 +          - 8.8.4.4 +          +#=============================== +network: +  version: 2 +  renderer: networkd +  ethernets: +    enp6s0: +      dhcp4: no +      addresses: [192.168.88.2/24] +      nameservers: +        addresses: [192.168.88.1,77.88.8.8,77.88.8.1] +      routes: +       - to: default +         via: 192.168.88.1 +  wifis: +    wlp4s0: +      dhcp4: yes +    # addresses: [192.168.88.3/24] +      nameservers: +        addresses: [192.168.88.1,77.88.8.8,77.88.8.1] +      access-points: +        "link": +           password: "dkfgf#*12091997" +             #      routes: +             # - to: default  +             # via: 192.168.88.1 +#=============================== + +chmod 600 /etc/netplan/your_config_file.yaml + +sudo netplan --debug generate +sudo netplan try +sudo netplan -d apply +sudo netplan apply + +sudo systemctl restart network-manager +sudo systemctl restart system-networkd +``` + +Для каждого сетевого интерфейса добавляем блоки **routes** и **routing-policy**. Итоговая запись для 1 интерфейса будет выглядеть так: + +```sh +network: + version: 2 + ethernets: + enp0s5: + addresses: + - 78.89.90.12/24 + gateway4: 78.89.90.1 + match: + macaddress: 54:43:32:21:10:09 + mtu: 1500 + nameservers: + addresses: + - 8.8.8.8 + - 1.1.1.1 + - 8.8.4.4 + search: [] + set-name: enp0s5 + routes: + - to: 0.0.0.0/0 + via: 78.89.90.1 +          table: 700 +          routing-policy: +          - from: 78.89.90.12 +          table: 700 +          priority: 300 +``` + +Для других интерфейсов запись будет аналогична, начиная с имени интерфейса (**enp0s5**), но у других должен отсутствовать параметр **gateway4**, т.к. шлюз по умолчанию должен быть один. Настройки **routes** и **routing-policy**: + +- to – адрес назначения пакета. Для наших целей – любой (0.0.0.0/0) +- via – адрес шлюза для данного интерфейса +- table – имя таблицы маршрутизации. Должны быть указаны одинаковые значения в **routes** и **routing-policy** для одного интерфейса, но быть различными от интерфейса к интерфейсу. Данные таблицы мы создали в файле **rt_tables** +- from – адрес источника пакета. Для целей данного руководства – это IP адрес интерфейса +- priority – обязательное числовое значение + +После внесения настроек и сохранения файла проверим конфигурацию на ошибки: + +`netplan generate` + +============================================== +Пожалуйста, добавьте эту конфигурацию: + +``` +network: + renderer: networkd + ethernets: + ens18: + addresses: [1.2.3.4/24] + gateway4: 1.2.3.254 + dhcp4: no + dhcp6: no + nameservers: + search: [domain.tld] + addresses: [123.456.789.101] + routes: + - to: 1.2.3.254/32 + via: 0.0.0.0 + scope: link + ens19: + dhcp4: true + dhcp6: no + version: 2 +``` + +и нет необходимости перезагружать просто + +``` +sudo netplan --debug apply +``` + +```sh +Computer 1 +network: + version: 2 + renderer: networkd + ethernets: + enp1s0: + dhcp4: true + enp4s0: + addresses: + - xxx.xxx.xxx.xxx/24 + routes: + - to: default + via: 10.0.0.1 + tunnels: + wg0: + mode: wireguard + port: 51820 + key: XXX + addresses: + - 10.0.0.1/24 + peers: + - allowed-ips: [10.0.0.0/24] + endpoint: xxx.xxx.xxx.xxx:51820 + keys: + public: XXX + vxlan0: + mode: vxlan + id: 100 + local: 10.8.0.1 + remote: 10.8.0.2 + port: 4789 + link: enp4s0 + +Computer 2 + +network: + version: 2 + renderer: networkd + ethernets: + enp1s0: + dhcp4: true + enp4s0: + addresses: + - xxx.xxx.xxx.xxx/24 + routes: + - to: default + via: 10.0.0.2 + tunnels: + wg0: + mode: wireguard + port: 51820 + key: XXX + addresses: + - 10.0.0.1/24 + peers: + - allowed-ips: [10.0.0.0/24] + endpoint: xxx.xxx.xxx.xxx:51820 + keys: + public: XXXX + vxlan0: + mode: vxlan + id: 100 + local: 10.8.0.2 + remote: 10.8.0.1 + port: 4789 + link: enp4s0 + ``` + diff --git a/LINUX/NETWORK/NETWORK.md b/LINUX/NETWORK/NETWORK.md new file mode 100644 index 0000000..38dd456 --- /dev/null +++ b/LINUX/NETWORK/NETWORK.md @@ -0,0 +1,162 @@ +```sh +# Проверка нагрузки на сеть в Linux +# Иногда необходимо узнать, какие процессы активно используют сеть и сколько трафика они потребляют. + +# Просмотр сетевой активности. Вывести активные соединения:** +netstat -tunapl +# Отобразит все активные соединения и их состояние. + +# Проверить процессы, использующие сеть:** +sudo lsof -i -P -n +# Покажет, какие программы используют сетевые порты. + +# Мониторинг трафика в реальном времени:** +iftop +# Позволяет в реальном времени отслеживать трафик по интерфейсам. + +# Полезно для диагностики сетевых проблем, выявления утечек трафика и мониторинга серверов. + +#============================= +# +netstat -tlpn + +# кто слушает TCP порт? +sudo ss -tlpn + +# кто слушает UDP порт? +sudo ss -ulpn + +# Просмотр всех узлов в сети: +arp -n + +#============================= +# Сетевое соединение: +hostnamectl + +# отображение всех socket: +sudo ss -tlpn + +sudo apt-get install nload bmon nethogs + +ifconfig +ip a +ls /sys/class/net + +nc localhost 22 + +sudo lshw -C network + +sudo lspci | grep Network +lspci -knn | grep Net -A2 + +sudo ethtool enp2s0 + +ip link show +ip -br link show + +wg-quick up wg0 +wg-quick down wg0 + + +# получить IP: +curl ifconfig.me + + +sudo apt install networking-tool +sudo iwconfig +sudo iwlist wlan0 scan + + +# - поиск беспроводных pci-карт +lspci | grep -i wireless + +# - вывод этой команды будет содержать все сетевые интерфейсы +ip link list + +# раскрасить и отобразить все IP-адреса и интерфейсы в Linux в табличном формате для лучшей читаемости: +ip -br -c addr show +ip -br -c link show + +# - показать устройства wi-fi +iw dev + +# - включить устройство wlp2s0 +ip link set wlp2s0 up + +# !!! Если у вас возникнет ошибка: +# !!! RTNETLINK answers: Operation not possible due to RF-kill +# !!! Можно установить утилиту rfkill и с ее помощью решить эту проблему + +# - присвоить SSID "MyNet" +iwconfig wlan0 essid "MyNet" + +# - установит канал 3 +iwconfig wlan0 channel 3 + +# - сменить режим Managed на Ad-Hoc +iwconfig wlan0 mode Ad-Hoc + +# - использовать любую точку доступа +iwconfig wlan0 ap any + +# - установить чувствительность -50 +iwconfig wlan0 sens -50 + +# - установить значение 20 для повторной передачи MAC +iwconfig wlan0 retry 20 + +# - установить пароль 1234-pass-123 +ifconfig wlan0 key 1234-pass-123 + +# - подключение к незащищенной сети +iwconfig wlp4s0 essid ваш_essid + +# - подключение с задающим каналом сети +iwconfig wlp4s0 essid ваш_essid freq 2432M + +# -подключение к WEP зашифрованному каналу сети, ключа ASCII +iwconfig wlp4s0 essid ваш_essid key s:ваш_ключ + +# - включение режима экономии питания +iwconfig wlp4s0 power on + +# - показать статус соединения +iw dev wlp4s0 link iwconfig wlp4s0 + +# -сканирование сети на точки доступа +iw dev wlp4s0 scan iwlist wlp4s0 scan + +#  - настройка режима работы ad-hoc +iw dev wlp4s0 set type ibss + +# - подключение к незащищенной сети +iw dev wlp4s0 connect ваш_essid + +# - подключение с задающим каналом сети +iw dev wlp4s0 connect ваш_essid 2432 + +# -  -подключение к WEP зашифрованному каналу сети, ключа ASCII +iw dev wlp4s0 connect ваш_essid key 0:ваш_ключ + +# - включение режима экономии питания +iw dev wlp4s0 set power_save on + +# - сканируем сеть +iw dev wlp4s0 scan | less + +#================================== +# Изменить скорость сетевой карты + +sudo ethtool enp2s0 + +sudo ethtool -s eth0 duplex full speed 100 autoneg off + +sudo ethtool -s eth0 duplex full speed 1000 autoneg off + +# как то же самое сделать не имея ethtool? +mii-tool -F 100baseTx-FD eth0 + +# вариант совсем для гиков, на «чистом» линуксе :): +ifconfig eth0 media 100baseTX mediaopt full-duplex + +#================================== diff --git "a/LINUX/NETWORK/\320\220\320\262\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201\320\272\320\276\320\265 \320\276\320\261\320\275\320\260\321\200\321\203\320\266\320\265\320\275\320\270\320\265 \320\270 \321\203\320\262\320\265\320\264\320\276\320\274\320\273\320\265\320\275\320\270\320\265 \320\276 \320\275\320\276\320\262\321\213\321\205 \321\203\321\201\321\202\321\200\320\276\320\271\321\201\321\202\320\262\320\260\321\205 \320\262 \321\201\320\265\321\202\320\270.md" "b/LINUX/NETWORK/\320\220\320\262\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201\320\272\320\276\320\265 \320\276\320\261\320\275\320\260\321\200\321\203\320\266\320\265\320\275\320\270\320\265 \320\270 \321\203\320\262\320\265\320\264\320\276\320\274\320\273\320\265\320\275\320\270\320\265 \320\276 \320\275\320\276\320\262\321\213\321\205 \321\203\321\201\321\202\321\200\320\276\320\271\321\201\321\202\320\262\320\260\321\205 \320\262 \321\201\320\265\321\202\320\270.md" new file mode 100644 index 0000000..677a0b3 --- /dev/null +++ "b/LINUX/NETWORK/\320\220\320\262\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201\320\272\320\276\320\265 \320\276\320\261\320\275\320\260\321\200\321\203\320\266\320\265\320\275\320\270\320\265 \320\270 \321\203\320\262\320\265\320\264\320\276\320\274\320\273\320\265\320\275\320\270\320\265 \320\276 \320\275\320\276\320\262\321\213\321\205 \321\203\321\201\321\202\321\200\320\276\320\271\321\201\321\202\320\262\320\260\321\205 \320\262 \321\201\320\265\321\202\320\270.md" @@ -0,0 +1,49 @@ +```sh +#!/bin/bash + +# Укажите диапазон сети (пример: 192.168.1.0/24) +NETWORK="192.168.1.0/24" + +# Файл для хранения предыдущего списка устройств +KNOWN_DEVICES="$HOME/known_devices.txt" + +# Файл для временного списка +CURRENT_DEVICES="$HOME/current_devices.txt" + +# Сканируем сеть и сохраняем MAC-адреса активных устройств +arp-scan --localnet | awk '/:/{print $2}' | sort > "$CURRENT_DEVICES" + +# Если это первый запуск, просто сохраняем список +if [ ! -f "$KNOWN_DEVICES" ]; then + cp "$CURRENT_DEVICES" "$KNOWN_DEVICES" + echo "🔍 Список известных устройств сохранён." + exit 0 +fi + +# Поиск новых устройств +NEW_DEVICES=$(comm -13 "$KNOWN_DEVICES" "$CURRENT_DEVICES") + +if [ -n "$NEW_DEVICES" ]; then + echo "🚨 Обнаружены новые устройства в сети:" + echo "$NEW_DEVICES" + notify-send "Новые устройства в сети" "$NEW_DEVICES" + echo "$NEW_DEVICES" >> "$KNOWN_DEVICES" +else + echo "✅ Новых устройств не обнаружено." +fi + +# Обновляем список известных устройств +mv "$CURRENT_DEVICES" "$KNOWN_DEVICES" +``` + + Использует arp-scan для поиска активных устройств. + +🟢 Сохраняет список известных устройств и сравнивает его с новыми данными. + +🟢 Отправляет уведомление, если в сети появилось новое устройство. + +🟢 Можно добавить в crontab для автоматического мониторинга: + +`*/5 * * * * /path/to/script.sh` + +Этот скрипт поможет отслеживать неизвестные подключения и контролировать безопасность сети. \ No newline at end of file diff --git "a/LINUX/NETWORK/\320\222\320\275\320\265\321\210\320\275\320\270\320\271 IP \320\270\320\267 \320\272\320\276\320\275\321\201\320\276\320\273\320\270.md" "b/LINUX/NETWORK/\320\222\320\275\320\265\321\210\320\275\320\270\320\271 IP \320\270\320\267 \320\272\320\276\320\275\321\201\320\276\320\273\320\270.md" new file mode 100644 index 0000000..d1a5d4f --- /dev/null +++ "b/LINUX/NETWORK/\320\222\320\275\320\265\321\210\320\275\320\270\320\271 IP \320\270\320\267 \320\272\320\276\320\275\321\201\320\276\320\273\320\270.md" @@ -0,0 +1,17 @@ +```sh +# Сетевое соединение: +hostnamectl + +wget -qO- eth0.me + +wget -qO- ipecho.net/plain +curl ifconfig.me +wget -O - -q ifconfig.me/ip + + +curl https://icanhazip.com +curl https://ipinfo.io/ip +curl https://ifconfig.me + + +``` diff --git "a/LINUX/NETWORK/\320\241\320\232\320\236\320\240\320\236\320\241\320\242\320\254 \320\241\320\225\320\242\320\225\320\222\320\236\320\231 \320\232\320\220\320\240\320\242\320\253.md" "b/LINUX/NETWORK/\320\241\320\232\320\236\320\240\320\236\320\241\320\242\320\254 \320\241\320\225\320\242\320\225\320\222\320\236\320\231 \320\232\320\220\320\240\320\242\320\253.md" new file mode 100644 index 0000000..66ad165 --- /dev/null +++ "b/LINUX/NETWORK/\320\241\320\232\320\236\320\240\320\236\320\241\320\242\320\254 \320\241\320\225\320\242\320\225\320\222\320\236\320\231 \320\232\320\220\320\240\320\242\320\253.md" @@ -0,0 +1,10 @@ +```sh +# Сетевое соединение: +hostnamectl + +ip a + +cat /sys/class/net//speed + +sudo ethtool | grep Speed +``` diff --git a/LINUX/NGINX.md b/LINUX/NGINX.md new file mode 100644 index 0000000..21e9380 --- /dev/null +++ b/LINUX/NGINX.md @@ -0,0 +1,538 @@ +## Настройка перенаправлений + +Настройки необходимо вносить в файлах конфигураций виртуальных доменов. В [Linux](https://www.dmosk.ru/terminus.php?object=linux) на основе RPM (CentOS, Red Hat), как правило, они расположены в директории /etc/nginx/conf.d/. В Linux на основе Deb (Ubuntu, Debian) — в директории /etc/nginx/sites-enabled/. Во [FreeBSD](https://www.dmosk.ru/terminus.php?object=freebsd) все в одном файле — /usr/local/etc/nginx/nginx.conf. + +Саму настройку на перенаправление в NGINX можно прописать несколькими способами. + +1. Первый: + +rewrite ^ https://$host$request_uri? <флаг>; + +_* **$host** — имя хоста из запроса, если отсутствует — имя в поле «Host» заголовка, если тоже отсутствует — имя сервера; **$request_uri** — первоначальный запрос с аргументами (все, что идет после доменного имени). +** где флаги могут быть следующие:_ + +- _permanent — перенаправление с кодом 301._ +- _redirect — перенаправить с кодом 302._ +- _last — закончить обработку с переходом в новый location._ +- _break — закончить обработку и остаться в текущем location._ + +2. Второй:  + +return <код> https://$host$request_uri; + +_* где коды могут использоваться любые, но чаще всего — 301, 302, 404._ + +Есть различные мнения, какой из методов лучше и безопаснее, поэтому каким воспользоваться — решать по ситуации. В данных примерах используются оба варианта. + +После внесения изменений, необходимо проверить их корректность: + +nginx -t + +И для их применения перезапустить веб-сервер: + +systemctl restart nginx + +service nginx restart + +_* в первом примере перезапуск выполняется на новых системах Linux. Второй пример — на устаревших или FreeBSD._ + +Проверяя редиректы в [браузере](https://www.dmosk.ru/terminus.php?object=brouser), следует учесть, что настройки могут [кэшироваться](https://www.dmosk.ru/terminus.php?object=cache). Для обновления кэша используйте комбинацию Ctrl + F5. Если и это не помогает, закрывайте вкладку и открывайте новую. + +## С HTTP на HTTPS (другой порт) + +Пример конфигурации для перенаправления запросов на другой порт — с 80 (http) на 443 (https): + +server { +    listen 80; +    server_name domain.ru www.domain.ru; +    return 301 https://$host$request_uri; +} + +_* в данном примере для всех обращений к сайту domain.ru по 80 порту ([http](https://www.dmosk.ru/terminus.php?object=http)) будет работать редирект на 443 порт ([https](https://www.dmosk.ru/terminus.php?object=https)) с кодом 301 (для склеивания [доменов](https://www.dmosk.ru/terminus.php?object=domain))._ + +Также мы можем добавить условие, чтобы не перенаправлять на https для определенных ссылок, например: + +server { +    listen 80; +    server_name domain.ru www.domain.ru; +    if ($uri !~ /page.html){ +        return 301 https://$host$request_uri; +    } +} + +_* в данном примере запрос на страницу **/page.html** будет открыт по http._ + +Есть еще способ настройки директивы **server** сразу для http и https. При этом, если зайти по 80 порты, нас перекинет на 443: + +server { +    listen       80; +    listen       443 ssl; +    server_name  domain.ru www.domain.ru; + +    if ($scheme = 'http') { +        return 301 https://$host$request_uri; +    } + +    ssl_certificate     /etc/nginx/ssl/cert.pem; +    ssl_certificate_key /etc/nginx/ssl/cert.key; + +    ... +} + +_* данный способ удобен, чтобы не создавать несколько секций server со своими настройками._ + +## С одного домена на другой + +server { +        ... +        server_name domain1.ru; +        return 302 http://domain2.ru$request_uri; +} + +## C домена без www на домен с www + +server { +        ... +        server_name domain.ru; +        return 301 http://www.$host$request_uri; +} + +## С www на без www + +Конкретный домен: + +server { +        ... +        server_name www.domain.ru; +        return 301 $scheme://domain.ru$request_uri; +} + +Все домены, которые обслуживает nginx: + +server { +        ... +        server_name "~^www\.(.*)$"; +        return 301 $scheme://$1$request_uri; +} + +Или с использованием if: + +server { +        ... +        if ($host ~ "^www\.(.*)$") { +                return 301 $scheme://$1$request_uri; +        } +        ... +} + +## C index.php на / (корень) + +Данная настройка позволит перевести все запросы с /index.php на корневой адрес /: + +server { +        ... +        if ($request_uri ~ "^(.*)index\.(?:php|html)") { +                return 301 $1; +        } +} + +## Перенаправление запросов для отсутствующих доменов (перенаправление по умолчанию) + +Если обращение к веб-серверу идет по [IP-адресу](https://www.dmosk.ru/terminus.php?object=ipaddr) или домену, который не прописан в конфигурационном файле, можно перенаправить весь трафик на домен по умолчанию: + +server { +        listen 80 default_server; +        return 302 https://welcome.domain.ru$request_uri; +} + +или независимо от протокола: + +server { +        listen 80 default_server; +        return 302 $scheme://welcome.domain.ru$request_uri; +} + +server { +        listen 443 ssl default_server; +        return 302 $scheme://welcome.domain.ru$request_uri; + +        ssl_certificate /etc/nginx/ssl/cert.pem; +        ssl_certificate_key /etc/nginx/ssl/cert.key; +} + +_* **$scheme** позволяет перевести запрос на тот же протокол (http или https), по которому он был инициирован. +* если nginx должен слушать и обрабатывать запросы по https, необходимо указывать в настройках пути к сертификатам._ + +## С IP-адреса на домен + +В данном случае мы переводим все запросы по IP-адресу на конкретный домен: + +server { +        listen 80; +        server_name 192.168.1.15; +        return 301 http://site.ru$request_uri; +} + +_* при отправке http-запроса на сервер **192.168.1.15** по IP-адресу, он будет переведен на домен **site.ru**._ + +## Редирект домена и всех его поддоменов + +server { +        ... +        server_name  domain domain.*; +        return 301 https://$host$request_uri; +} + +## На другой файл + +Это скорее не перенаправление, а алиас или rewrite. Позволяет по запросу одного из файлов, отдать другой: + +server { +        ... +        location = /robots.txt { +            rewrite ^/robots.txt$ /robots2.txt; +        } +} + +_* в данном примере по запросу **robots.txt**, сервер отдаст содержимое **robots2.txt**._ + +## Часть url на другой сервер + +Перенаправить запрос на другой сервер при обращении по url page1: + +server { +        ... +        server_name  domain1.ru; +        location  ~ ^/page1/(.*)$ { +            return 301 $scheme://domain2.ru/$1; +        } +} + +_* в данном примере для всех запросов, начинающихся на **/page1/...** будет работать перенаправление на другой домен **domain2.ru**._ + +## Редирект со слешем + +**1. Убрать слеш в конце url** + +server { +    ... +    if (!-f $request_filename ) { +        rewrite ^/(.*)/$ /$1 permanent; +    } +} + +**2. Добавить слеш в конце url** + +server { +    ... +    if (!-f $request_filename ) { +        rewrite ^(.*[^/])$ $1/ permanent; +    } +} + +## Удаляем расширение + +Для перенаправления запроса, где в URL есть полное название файла скрипта (с расширением) используем конфигурацию: + +server { +    ... +    if ($request_uri ~ "^(.*)\.(?:php|html)") { +        return 301 $1; +    } +} + +_* в данном случае все запросы, которые заканчиваются на **.php** или **.html**, будут перенаправляться на страницы без данных расширений._ + +Для примера, запрос http://site-example.ru/page.php будет переведен на http://site-example.ru/page. + +## На другую страницу + +Нам может понадобиться перенаправлять запросы с одной страницы сайта на другую. Приведем примеры, как это сделать с помощью return и rewrite. + +а) с помощью rewrite: + +server { +        ... +        rewrite ^/page1$ /page2 permanent; +} + +б) с помощью return: + +server { +        ... +        location = /page1 { +            return 301 /page2; +        } +} + +## Удалить часть URL + +Иногда нужно удалять часть url. Это можно сделать следующими способами: + +server { +        ... +        rewrite /deleted-url/(.*) /$1 permanent; +} + +или: + +server { +        ... +        if ($request_uri ~ "/deleted-url/(.*)") { +                return 301 $1; +        } +} + +_* в данном примере из url мы удалим **deleted-url/**._ + +## Перенаправить запрос в случае обращения к несуществующим файлам + +Предположим, что нам нужно обращаться к скриптам на сервере, но без прописывания в URL .php на конце. Запрос будет выглядеть, примерно, http://url/. Чтобы nginx перекинул запрос на url.php вставляем следующее: + +server { +    ... +    location / { +       root /var/www; +       try_files $uri $uri/ $uri.php $uri.html /404.php =404; +    } +} + +_* в данном примере мы проверим наличие скрипта по пути **$uri**, затем **$uri/**. И уже после мы добавим к запросу **.php** и **.html**. Если ни одного скрипта не будет найдено, запрос будет перенаправлен на страницу **404.php**, которая вернет код 404._ + +Также можно решить задачу с помощью **if** и **rewrite**: + +server { +        ... +        if (!-e $request_filename){ +                rewrite ^(.*)$ /$1.php; +        } +} + +_* в данном примере мы проверяем наличие файла, к которому идет обращение. И если его нет, то происходит замена адреса на такое же имя файла с .php на конце._ + +## Перевод запросов, если файла не существует + +Данное действие не является редиректом, но близко по смыслу — NGINX проверяет наличие файла скрипта, к которому идет обращение, и если его нет, переводит запрос на другой файл. Как правило, это используется для того, чтобы перевести все обращения на файл index.php. + +server { +    ... +    location / { +        try_files $uri $uri/ /index.php?$query_string; +    } +    ... +} + +_* в данном примере мы скажем веб-серверу сначала проверить наличие скрипта по пути **$uri**, затем **$uri/** и если ответ будет 404, запросы будут обрабатываться с помощью файла **index.php** в корневой директории._ + +## Проксирование + +Проксирование, в отличие от редиректа, не передает инструкции браузеру перейти на другой url — NGINX сам выполняет http-запрос по другому адресу и возвращает готовый ответ. Эта возможность может применяться для внутреннего распределения серверных ресурсов. + +Хоть это и не совсем редирект, рассмотрим примеры его настройки, так как очень часто нужно не перенаправление, а, как раз, обратное проксирование. + +### 1. На другой сервер + +Пример внутреннего перенаправления http-запроса на другой веб-сервер: + +location / { +        proxy_pass          http://192.168.0.15:8080/; +        proxy_redirect      off; +        proxy_http_version  1.1; + +        proxy_set_header  Host               $host; +        proxy_set_header  Upgrade            $http_upgrade; +        proxy_set_header  Connection         "upgrade"; +        proxy_set_header  X-Real-IP          $remote_addr; +        proxy_set_header  X-Forwarded-For    $proxy_add_x_forwarded_for; +        proxy_set_header  X-Forwarded-Proto  http; +} + +* в данном случае, принимать запросы от браузера и отвечать на них будет NGINX, а сама обработка будет выполняться на сервере с IP-адресом 192.168.0.15 на порту 8080. Рассмотрим все директивы: + +- **proxy_pass —** схема, узел и порт. По сути, это как и куда будет перенаправлен запрос. +- **proxy_redirect —** позволяет отловить редиректы или определенные адреса и заменить их на другие. Пример работы с данной директивой [описан ниже](https://www.dmosk.ru/miniinstruktions.php?mini=nginx-redirects#proxypass-redirects). +- **proxy_http_version —** задает версию протокола http. По умолчанию имеет значение 1.0. +- **proxy_set_header —** добавляет http-заголовки: + - **Host —** сообщяет принимающей стороне, какой виртуальный хост использовать. Важный заголовок, если данная принимающая сторона обслуживает разные домены и требуется точная передача имени. + - **Upgrade —** действует только для http версии 1.1. Позволяет обновить протокол или его версию для уже созданного соединения. Например, с HTTP 1.1 до HTTP 2.0 или в WebSocket. + - **Connection —** определяет, останется ли сетевое соединение открытым после завершения текущей транзакции. + - **X-Real-IP —** позволяет передать принимающей стороне реальный IP-адрес посетителя. В противном случае, принимающая сторона получить адрес самого nginx. + - **X-Forwarded-For —** второй заголовок, с помощью которого передается реальный IP-адрес посетителя. + - **X-Forwarded-Proto —** определяет протокол, по которому клиент подключился к nginx. По возможности, стоит использовать https, но все зависит от принимающей стороны — она может отказаться принимать https или наоборот, требовать только его. + +Использование NGINX в качестве http-прокси: + +server { +        ... +        server_name site1.ru www.site1.ru; +        location / { +            proxy_pass http://192.168.1.21/; +            proxy_redirect     off; +            proxy_set_header   Host             $host; +            proxy_set_header   X-Real-IP        $remote_addr; +            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for; +        } +} + +server { +        ... +        server_name site2.ru www.site2.ru; +        location / { +            proxy_pass http://192.168.1.22/; +            proxy_redirect     off; +            proxy_set_header   Host             $host; +            proxy_set_header   X-Real-IP        $remote_addr; +            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for; +        } +} + +_* в данном примере запросы на **site1.ru** будут перекинуты на сервер **192.168.1.21**, а запросы на **site2.ru** — **192.168.1.22**._ + +HTTP proxy с авторизацией (если удаленный веб-сервер требует аутентификации): + +server { +    ... +    location / { +        proxy_pass http://10.10.10.10/page/; +        proxy_set_header Authorization "Basic dGVzdDp0ZXN0"; +        ... +    } +} + +_* где **10.10.10.10/page** — страница, на которую будут перекинуты запросы; **dGVzdDp0ZXN0** — логин:пароль test:test, закодированные в формате base64._ + +### 2. Часть url на другой сервер + +Выше мы рассмотрели пример перенаправления запроса по части веб-адреса. По схожему сценарию мы можем делать проксирование: + +server { +    ... +    location  ~ ^/page1/(.*)$ { +        proxy_pass   $scheme://10.10.10.10/$1$is_args$args; +    } +} + +_* и так, в данном примере при обращении по адресу **site.ru/page1/<что-то еще>**, nginx сделает внутренний запрос на сервер **10.10.10.10** по адресу **10.10.10.10/<что-то еще>** и вернет готовый ответ. +** обратите внимание, что мы добавили **$is_args$args —** если строка запроса имеет аргументы, то их также нужно передать._ + +### 3. На другой сайт + +Мы можем сделать так, что при переходе по одному адресу у нас будет открываться совершенно другой сайт: + +server { +    ... +    server_name dmosk.local; +    location / { +        proxy_pass https://www.dmosk.ru; +        proxy_set_header   Host             www.dmosk.ru; +        proxy_set_header   X-Real-IP        $remote_addr; +        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for; +    } +} + +_* в данном случае мы при обращении к нашему серверу (до доменному имени **dmosk.local**) будем попадать на сайт **https://www.dmosk.ru**. Обратите внимание, что в **proxy_set_header** мы передаем хосту его имя — в противном случае, как правило, другой сервер вернет ошибку. Также мы не указываем **proxy_redirect**, иначе, nginx будет переводить запросы на реальный сайт (отправлять инструкции браузеру перейти на него), а не тот, что мы используем за http-прокси._ + +### 4. На другой сайт по части URL + +Если нам нужно настроить проксирование на другой сайт при обращении к определенной странице сайта, настраиваем NGINX так: + +server { +    ... +    server_name www.dmosk.ru; +    location /page { +        rewrite /page/(.*) /$1  break; +        proxy_pass https://www.site.ru; +        proxy_set_header   Host             www.site.ru; +        proxy_set_header   X-Real-IP        $remote_addr; +        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for; +    } +} + +_* в данном примере, если мы перейдем на страницу **www.dmosk.ru/page**, то мы попадем на сайт **www.site.ru**._ + +### 5. Редиректы при проксировании + +Если при проксировании хост возвращает инструкцию браузеру для выполнения редиректа, обозреватель может сменить адрес сайта. Это особенно не удобно, когда проксирование мы выполняем на другой сайт. Чтобы отловить редиректы и заменить их своими значениями, мы должны воспользоваться опцией **proxy_redirect**. Рассмотрим ее применение для предыдущего примера, когда мы проксировали запрос на сайт www.dmosk.ru: + +server { +    listen 80; +    server_name dmosk.local www.dmosk.local; +    location / { +        proxy_pass https://www.dmosk.ru; +        proxy_set_header   Host             www.dmosk.ru; +        proxy_set_header   X-Real-IP        $remote_addr; +        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for; + +        proxy_redirect https://www.dmosk.ru/url1 http://dmosk.local/url2; +        proxy_redirect https://www.dmosk.ru/ http://dmosk.local/; +    } +} + +_* в конкретном случае мы проксируем запросы **http://dmosk.local** на сайт **www.dmosk.ru**, но если он вернет инструкцию для редиректа **https://www.dmosk.ru/url1**, в браузере он должен быть заменен на **http://dmosk.local/url2**. А также любое перенаправление для **https://www.dmosk.ru/** будет заменено на **http://dmosk.local/**._ + +### 6. Таймауты + +Сервер может генерировать, довольно, долго ответ. Если за определенное время nginx не получит ответ, он вернет ошибку с кодом 504. Мы можем задать свои значения таймаутов, если приложение должно отвечать долго, например: + +    location / { +        ... +        proxy_connect_timeout  30; +        proxy_send_timeout     30; +        proxy_read_timeout     300; +        ... +    } + +* где: + +- **proxy_connect_timeout —** время соединения с удаленным сервером. +- **proxy_send_timeout —** время ожидания для отправки запроса на сервер. +- **proxy_read_timeout —** время ожидания ответа от сервера. + +** время указывается в секундах. + +## Работа с location + +Обратите внимание, что мы часто использовали в качестве условия запроса его URN (Unifrorm Resource Name или унифицированное имя ресурса). Поэтому важно понимать, как мы можем работать с ними при помощи nginx, когда будем обрабатывать запросы для редиректов или проксирований. + +### Типы location + +Выше мы применяли различные ваианты написания location. Рассмотрим их чуть подробнее: + +- **= —** явное указание на точное название адреса запроса. +- **^~ —** регулярное выражение. Требует совпадения в начале строки. +- **/ или /path —** строковое выражение. Требует совпадения в начале строки. Сильно напоминает **^~**, но не позволяет применять регулярные выражения. +- **~ —** регулярное выражение. С учётом регистра. +- **~* —** регулярное выражение. Без учёта регистра. + +### Приоритеты + +В конфигурации nginx мы можем создать много конструкций на подобие: + +location /api { +} + +location /news { +} + +location /newstest { +} + +Они начнут обрабатываться сверху вниз, пока не будет найден первый подходящий вариант. Таким образом, в нашем примере, содержимое страницы **newstest** не сможет загрузиться, так как будет загружено содержимое страницы **news** (ее location выше в конфигурации). + +Приоритеты при выборе, какой location будет грузиться раньше, работают не только по принципу "сверху вниз". Также учитывается тип обработки (выше мы уже их рассмотрели): + +**1. location = /path.** Точное совпадение. Самый высокий приоритет. Сначала будут обрабатываться все локейшены данного типа. + +**2. location ^~ /path.** Регулярное выражение с совпадением в начале строки. + +**3. location ~ /path/ , location ~* .(api|test).** Регулярные выражения. + +**4. location /path.** Строковое выражение. Самый низкий приоритет. + +## Немного о 301 и 302 + +В чем принципиальная разница между ответом с кодом 301 и 302? Для обычного посетителя сайта разницы нет. А вот для поискового робота разница огромная. + +301-й редирект говорит о склеивании страниц. Это означает для поисковика то, что старая и новая страницы — это одно и то же. Таким образом, результаты ранжирования необходимо сохранить для новой страницы. + +302-о перенаправление просто говорит о том, что нужно перейти по другому адресу. Поисковый робот не сохраняет результат выдачи для новой страницы, индексируя его с нуля. \ No newline at end of file diff --git a/LINUX/PLEX.md b/LINUX/PLEX.md new file mode 100644 index 0000000..5e178d8 --- /dev/null +++ b/LINUX/PLEX.md @@ -0,0 +1,33 @@ +Plex Media Server — это ключевое решение для энтузиастов потоковой передачи, которые стремятся организовать и получить доступ к своим медиабиблиотекам без проблем с различных устройств. Его способность перекодировать медиафайлы на лету обеспечивает совместимость на разных платформах, что делает его незаменимым для унифицированного потокового опыта. + +Ключевые особенности Plex Media Server включают в себя: + +- **Централизованная медиабиблиотека:** объединяет ваши видео, музыку и фотографии на одной простой в навигации платформе. +- **Транскодирование «на лету»:** преобразует медиаданные в оптимальный формат для каждого устройства, улучшая качество воспроизведения. +- **Удаленный доступ:** транслируйте свой контент из любой точки мира, на любое устройство с доступом в Интернет. +- **Удобный интерфейс:** обеспечивает визуально привлекательную и интуитивно понятную навигацию. +- **Настраиваемые плейлисты и библиотеки:** адаптируйте свои впечатления от просмотра и прослушивания, создавая персонализированные коллекции. +- **Расширенная поддержка устройств:** совместимость с различными устройствами, от смартфонов до смарт-телевизоров. +- **Безопасный обмен:** безопасно делитесь выбранным контентом с друзьями и семьей. +- **Регулярные обновления:** постоянно совершенствуется за счет новых функций и улучшений. + +```sh +sudo apt update && sudo apt upgrade -y +sudo apt install gpg ca-certificates software-properties-common apt-transport-https curl -y +curl -fsSL https://downloads.plex.tv/plex-keys/PlexSign.key | sudo gpg --dearmor -o /usr/share/keyrings/plex.gpg +echo "deb [signed-by=/usr/share/keyrings/plex.gpg] https://downloads.plex.tv/repo/deb public main" | sudo tee /etc/apt/sources.list.d/plexmediaserver.list + +sudo apt update +sudo apt install plexmediaserver + +systemctl status plexmediaserver + +sudo systemctl start plexmediaserver +sudo systemctl enable plexmediaserver +sudo systemctl restart plexmediaserver + + +sudo ufw enable +sudo ufw allow 32400 + +``` \ No newline at end of file diff --git a/LINUX/PROGRAMS_bash.md b/LINUX/PROGRAMS_bash.md new file mode 100644 index 0000000..e406fbe --- /dev/null +++ b/LINUX/PROGRAMS_bash.md @@ -0,0 +1,311 @@ +Волшебная строчка, которую нужно добавить после `#!/bin/bash`, чтоб скрипт можно было отлаживать по шагам +```bash +#!/bin/bash +trap 'echo "# $BASH_COMMAND";read' DEBUG + +echo line1 +echo line2 +echo line3 +``` + +**Процесс отладки** + +Запускаем скрипт, перед выполнением каждой команды выводится то, что будет исполняться, затем интерпретатор начинает ожидать нажатия клавиши ``. + +Если понимаем, что что-то пошло не так, нажимаем Ctrl+C и выходим из отладки. + +**Ингредиенты** + +команда trap, которая умеет перехватывать разные сигналы и в нашем случае она перехватывает сигнал DEBUG, посылаемый перед выполнением команды + +команда read, которая умеет ожидать ввода чего-нибудь с клавиатуры (в данном случае нам нужно только либо ENTER либо Ctrl+C + +переменная окружения ==`$BASH_COMMAND`==, валидная внутри обработчика команды trap. + +=============================================== +**Автоматическое выключение компьютера при низком заряде батареи** + +```bash +#!/bin/bash + +# Порог заряда батареи в процентах +THRESHOLD=15 + +# Получение текущего заряда батареи +BATTERY_LEVEL=$(acpi -b | grep -P -o '[0-9]+(?=%)') + +# Проверка уровня заряда и выключение, если ниже порога +if [ "$BATTERY_LEVEL" -lt "$THRESHOLD" ]; then +  echo "Низкий уровень заряда батареи: $BATTERY_LEVEL%. Выключение..." +  shutdown -h now +fi +``` + **Скрипт проверяет уровень заряда батареи и автоматически выключает компьютер, если заряд ниже указанного порога.** + +Особого внимания заслуживает использование команды acpi для получения уровня заряда батареи. Скрипт полезен для защиты ноутбуков от аварийного выключения при низком заряде. + +=============================================== +### Автоматическое обновление IP-адреса в DNS-записи ### +```bash +#!/bin/bash + +# Получение текущего внешнего IP-адреса +CURRENT_IP=$(curl -s http://ifconfig.me) + +# Путь к файлу с последним IP-адресом +IP_FILE="$HOME/current_ip.txt" + +# Чтение последнего сохраненного IP-адреса +if [ -f "$IP_FILE" ]; then + LAST_IP=$(cat "$IP_FILE") +else + LAST_IP="" +fi + +# Если IP-адрес изменился, обновить DNS-запись +if [ "$CURRENT_IP" != "$LAST_IP" ]; then + echo "IP изменился на $CURRENT_IP, обновление DNS..." + + # Обновление DNS-записи (пример с использованием Cloudflare API) + curl -X PUT "https://api.cloudflare.com/client/v4/zones/your_zone_id/dns_records/your_record_id" \ + -H "Authorization: Bearer your_api_token" \ + -H "Content-Type: application/json" \ + --data "{\"type\":\"A\",\"name\":\"yourdomain.com\",\"content\":\"$CURRENT_IP\",\"ttl\":120,\"proxied\":false}" + + # Сохранение нового IP-адреса + echo "$CURRENT_IP" > "$IP_FILE" +else + echo "IP не изменился, обновление не требуется." +fi +``` +**Скрипт автоматически проверяет, изменился ли внешний IP-адрес, и при изменении обновляет DNS-запись с использованием Cloudflare API.** + +Особого внимания заслуживает логика проверки текущего и предыдущего IP-адреса, что позволяет избежать ненужных запросов к DNS-серверу. В случае изменения IP-адреса, скрипт использует API Cloudflare для обновления DNS-записи. Такой скрипт полезен для пользователей с динамическим IP-адресом, которые хотят поддерживать актуальную DNS-запись для своего домена. + +=============================================== +### Автоматическое резервное копирование файлов с указанием даты ### +```bash +#!/bin/sh +cp -r /path/to/source /path/to/backup/backup_$(date +%Y%m%d) +``` +Скрипт создает резервную копию указанной директории, добавляя текущую дату в имя папки для каждой копии. + +=============================================== +### Работа с логами в Linux с помощью journalctl ### +```bash +# Вывод всех системных логов +journalctl +``` +выводит полный журнал событий системы. Вывод может содержать сообщения ядра, системные службы и другие системные события. + +```bash +# Фильтрация логов по дате: +# Чтобы отфильтровать логи по дате и времени, можно использовать параметры +journalctl --since +journalctl --until + +# Логи за определённый промежуток времени +journalctl --since "2023-08-01 10:00:00" --until "2023-08-01 12:00:00" +``` +С помощью этих параметров можно просматривать логи за конкретный период, что упрощает поиск информации о системных событиях, произошедших в определённое время. +```bash +# Логи службы nginx +journalctl -u nginx +``` +С помощью флага -u можно выводить логи, связанные с конкретной системой или службой, например, nginx. Это полезно для диагностики и устранения проблем, связанных с отдельными компонентами системы. +```bash +# Отслеживание логов в реальном времени: +# Функция реального времени позволяет наблюдать за логами по мере их поступления в систему. + +journalctl -f +``` +Команда ==`journalctl -f`== выводит логи в реальном времени, аналогично команде ==`tail -f`== для текстовых файлов. Это особенно полезно при мониторинге работы системы или приложений в режиме реального времени. + +Использование `journalctl` позволяет легко анализировать системные логи, отфильтровывать нужную информацию и отслеживать проблемы в работе системы или сервисов. + +================================================ +## Отчет о системной информации и отправка его по электронной почте ## +```bash +#!/bin/bash + +# Создание файла отчета +report_file="$HOME/system_report.txt" + +# Запись заголовка в файл отчета +echo "Отчет о системной информации - $(date)" > "$report_file" +echo "========================================" >> "$report_file" + +# Системная информация +echo -e "\nСистемная информация:" >> "$report_file" +uname -a >> "$report_file" + +# Информация о загрузке системы +echo -e "\nИнформация о загрузке системы:" >> "$report_file" +uptime >> "$report_file" + +# Использование дискового пространства +echo -e "\nИспользование дискового пространства:" >> "$report_file" +df -h >> "$report_file" + +# Использование памяти +echo -e "\nИспользование памяти:" >> "$report_file" +free -h >> "$report_file" + +# Список работающих процессов +echo -e "\nСписок работающих процессов:" >> "$report_file" +ps aux --sort=-%mem | head -n 10 >> "$report_file" + +# Сетевые подключения +echo -e "\nСетевые подключения:" >> "$report_file" +ss -tuln >> "$report_file" + +# Отправка отчета по электронной почте +recipient="user@example.com" +mail -s "Отчет о системной информации" "$recipient" < "$report_file" + +echo "Отчет о системной информации создан и отправлен на $recipient" +``` +Этот скрипт полезен для системных администраторов и может быть настроен для автоматического выполнения с помощью cron, чтобы регулярно получать отчеты о состоянии системы. + +================================= +### Найдите все неработающие симлинки (символьные ссылки) в вашей системе с помощью команды: ### + +```bash +find . -xtype l +``` +================================= +### Обновление и очистка системы ### +```bash +#!/bin/bash + +# Обновление списка пакетов и установка обновлений +sudo apt-get update +sudo apt-get upgrade -y + +# Автоматическое удаление ненужных пакетов и очистка кэша +sudo apt-get autoremove -y +sudo apt-get autoclean -y +sudo apt-get clean + +# Вывод сообщения о завершении +echo "Система обновлена и очищена." +``` +================================= +### Синхронизация директорий с помощью rsync ### +```bash +#!/bin/bash + +# Исходная и целевая директории +source_directory="$HOME/source_directory" +target_directory="$HOME/target_directory" + +# Лог-файл +log_file="$HOME/rsync_log.txt" + +# Запуск синхронизации +rsync -av --delete "$source_directory/" "$target_directory/" >> "$log_file" 2>&1 + +# Проверка результата синхронизации +if [ $? -eq 0 ]; then + echo "Синхронизация завершена успешно: $(date)" >> "$log_file" +else + echo "Ошибка синхронизации: $(date)" >> "$log_file" +fi + +echo "Синхронизация завершена. Подробности в лог-файле $log_file" +``` +Скрипт синхронизирует содержимое исходной директории с целевой директорией, создавая резервную копию и записывая результаты в лог-файл. + +================================= +### Функция для создания резервной копии ### +```bash +#!/bin/bash + +# Путь к отслеживаемому каталогу +WATCHED_DIR="/path/to/watched/directory" + +# Путь для сохранения резервных копий +BACKUP_DIR="/path/to/backup/directory" + +# Функция для создания резервной копии +backup() { + TIMESTAMP=$(date +%Y%m%d_%H%M%S) + tar -czf "$BACKUP_DIR/backup_$TIMESTAMP.tar.gz" -C "$WATCHED_DIR" . + echo "Backup created at $BACKUP_DIR/backup_$TIMESTAMP.tar.gz" +} + +# Использование inotifywait для мониторинга изменений в каталоге +inotifywait -m -r -e modify -e move -e create -e delete "$WATCHED_DIR" --format '%T %w %f %e' --timefmt '%Y-%m-%d %H:%M:%S' | +while read date time dir file event +do + echo "Detected $event on $file at $date $time" + backup +done +``` +================================= +### Создание резервной копии базы данных ### +```bash +#!/bin/bash + +# Параметры базы данных +db_name="mydatabase" +db_user="myuser" +db_password="mypassword" +backup_dir="$HOME/db_backups" +backup_file="$backup_dir/${db_name}_$(date +%Y%m%d%H%M%S).sql" + +# Создание каталога для резервных копий, если он не существует +mkdir -p "$backup_dir" + +# Создание резервной копии базы данных +mysqldump -u "$db_user" -p"$db_password" "$db_name" > "$backup_file" + +# Проверка результата резервного копирования +if [ $? -eq 0 ]; then + echo "Резервное копирование базы данных завершено успешно: $(date)" >> "$backup_dir/backup_log.txt" +else + echo "Ошибка резервного копирования базы данных: $(date)" >> "$backup_dir/backup_log.txt" +fi + +echo "Резервная копия базы данных создана: $backup_file" +``` +================================= +### Создание резервной копии базы данных 2 ### +```bash +#!/bin/bash +# Установите желаемую длину пароля +password_length=12 +# Генерация пароля +password=$(tr -dc 'A-Za-z0-9_!@#$%^&*' < /dev/urandom | head -c $password_length) +------ +echo "Сгенерированный пароль: $password +------ +#!/bin/bash +# Настройки для подключения к базе данных +DB_USER="yourusername" +DB_PASSWORD="yourpassword" +DB_NAME="yourdbname" +BACKUP_PATH="/path/to/your/backup/directory" +DATE=$(date +%Y%m%d%H%M) + +# Создание дампа базы данных +mysqldump -u $DB_USER -p$DB_PASSWORD $DB_NAME > $BACKUP_PATH/${DB_NAME}_$DATE.sql + +# Опционально: удаление старых резервных копий старше 30 дней +find $BACKUP_PATH -name "*.sql" -type f -mtime +30 -exec rm {} \; + +echo "✔️ Резервная копия базы данных $DB_NAME создана успешно." +``` +Скрипт создает резервную копию указанной MySQL базы данных и сохраняет её в заданный каталог с меткой текущей даты и времени в имени файла. + +================================= +### Архив tar.gz текущего каталога ### +```bash +#!/bin/sh +tar -czf "../${PWD##*/}.tar.gz" . +``` +Скрипт создает архив tar.gz текущего каталога. + +Особого внимания здесь заслуживает конструкция ==`${PWD##*/}`==, которая берет полный путь до текущего каталога (переменная $PWD) и удаляет из него первую часть вплоть до последнего слеша, оставляя, таким образом, только имя самого каталога. Далее к нему добавляется расширение tar.gz. + +================================= diff --git a/LINUX/PS.md b/LINUX/PS.md new file mode 100644 index 0000000..c333381 --- /dev/null +++ b/LINUX/PS.md @@ -0,0 +1,34 @@ +```sh +# отправить программу в фон и поставить на паузу: +ctrl + z (cmd + z) + +[1] + 68920 suspended vim . + +ps | grep vim +... +... + +# Посмотреть свернутые процессы: +jobs + +[1] + 68920 suspended vim . + +# Восстановить: +fg %2 (fg # если одна) + +# Запустить исполнение в фоне: +bg :2 (bg # если один) + +# Можно запускать программы уже в бекграунде используя амперсанд: +python app.py & + +kill -9 68920 + +# Запущенная в консоли программа: +ctrl + z (cmd + z) + +tail -f /tmp/log + + +``` + diff --git a/LINUX/PostgreSQL.md b/LINUX/PostgreSQL.md new file mode 100644 index 0000000..ce3ebf8 --- /dev/null +++ b/LINUX/PostgreSQL.md @@ -0,0 +1,165 @@ +# Как настроить сетевое подключение + + ![Обновлено](https://www.dmosk.ru/img/public/time.jpg "Дата обновления") Обновлено: 11.12.2024  ![Опубликовано](https://www.dmosk.ru/img/public/time.jpg "Дата публикации") Опубликовано: 11.10.2016 + +По умолчанию, сервер баз данных pgsql разрешает подключение только с локального компьютера. Данная инструкция показывает, как разрешить подключения с удаленных систем. + +Для настройки нам нужно будет внести изменения в два файла: +## Основной конфигурационный файл postgresql.conf + +Для начала посмотрим путь расположения конфигурационного файла **postgresql.conf**: + +su - postgres -c "psql -c 'SHOW config_file;'" + +Команда выдаст, примерно, следующее: + +----------------------------------------- + /var/lib/pgsql/data/postgresql.conf +(1 row) + +_* нас интересует строчка **/var/lib/pgsql/data/postgresql.conf** — это путь расположения нужного нам конфигурационного файла._ + +Теперь открываем на редактирование основной файл конфигурации PostgreSQL по найденному пути: + +vi /var/lib/pgsql/data/postgresql.conf + +Находим и редактируем следующее: + +listen_addresses = '*' + +_* по умолчанию, параметр закомментирован и настроен на прослушивание запросов только с локального сетевого интерфейса. В данном примере мы разрешили прослушивание запросов на всех IP-адресах **(*)**, но, если требуется более безопасная настройка, можно просто перечислить последние через пробел._ + +Чтобы изменения вступили в силу, перезапускаем службу postgresql: + +systemctl restart postgresql + +_* данное имя службы (**postgresql**) может сильно отличаться, в зависимости от системы. Иногда, необходимо добавить версию postgresql или использовать другое имя. Стоит обратиться к документации, чтобы выяснить точное название сервиса._ + +Проверим, что настройка применилась: + +ss -tunlp | grep 5432 + +_* где **5432 —** порт, на котором работает postgresql. Данный номер используется по умолчанию, но в вашей системе он может быть другим._ + +В ответ мы должны получить строку, содержащую что-то на подобие: + +... 0.0.0.0:5432 ... + +Первые **0.0.0.0** говорят о том, что PostgreSQL слушает сетевые запросы на всех сетевых интерфейсах. Если мы ограничили прослушивание, настраивая опцию **listen_addresses**, то вместо нулей мы увидим конкретный IP, на котором разрешили работать службе. + +## Файл host-based authentication (pg_hba.conf) + +Смотрим путь расположения конфигурационного файла **pg_hba.conf**: + +su - postgres -c "psql -c 'SHOW hba_file;'" + +Команда выдаст, примерно, следующее: + +--------------------------------- + /var/lib/pgsql/data/pg_hba.conf +(1 row) + +Открываем на редактирование файл, путь до которого вернула команда выше: + +vi /var/lib/pgsql/data/pg_hba.conf + +Находим: + +# IPv4 local connections: + +и сразу под ним добавляем строку на подобие: + +host     all     all     192.168.0.10/32     md5 + +* в данном примере мы: + +- разрешаем удаленные подключения к серверу с компьютера **192.168.0.10**. +- доступ предоставляется всем базам (первое **all**). +- доступ предоставляется всем учетным записям (второе **all**). +- обратите внимание на **md5** — способ хэширования пароля. В зависимости от версии postgresql может потребоваться использовать другой вариант. + +** При желании, вместо all можно указать конкретные данные для повышения безопасности. + +Чтобы изменения вступили в силу, перезапускаем службу postgresql: + +systemctl restart postgresql + +## Проверка подключения + +Проверить подключение можно с удаленного компьютера следующей командой: + +psql -h 192.168.0.15 -U usersql + +_* где  **192.168.0.15** — IP-адрес сервера баз данных; **usersql** — имя учетной записи, от которой идет подключение._ + +================================================== +```sh + +# Установка POSTGRES из исходников: +curl ............ + +sudo tar xzvf postgresql.tar.gz +cd postgresql + +./configure --prefix=/var/lib/postgresql +make world +sudo make install world +sudo chown -R vlapa:vlapa /var/lib/postgresql + +export PGDATA=/var/lib/postgresql + +./bin/pg_ctl initdb +./bin/pg_ctl -D /var/lib/postgresql/main/ start +./bin/psql -h localhost -U vlapa postgres + \d + create database some_temp_db; + \c some_temp_db + + +#============================= +sudo tar xzvf file_name*.tgz -C /opt/ +cd /opt/file_name + +./install + +tar -xvzf имя_файла + +# Для архивов сжатых с помощью BZIP (bz, bz2 и т.д.) выполняем: +tar -xvjf имя_файла + +#Команды Tar: + +-x — извлечь файлы из архива; +-v — подробный вывод информации на экран; +-f — Обязательная опция. Если не указать, Tar будет пытаться использовать магнитную ленту; +-z — обработать архив сжатый gzip’ом; +-j — обработать архив сжатый bzip’ом. + +vim `~/.local/share/applications/PyCharm.desktop +``` + +```bash +[Desktop Entry] +Version=1.1 +Type=Application +Name=nsnake +GenericName=Classic snake game on the terminal +NoDisplay=false//отображать в меню +Icon=nsnake +Exec=nsnake +Terminal=true//запускать в терминале +Actions= +Categories=ActionGame;Game; +Keywords=snake;ncurses;textual;terminal; +``` + +```bash +[Desktop Entry] //desktop-файл всегда начинается с этого +Encoding=UTF-8 //кодировка +Type=Application //тип +Name=Eclipse C/C++IDE //имя +Comment=Eclipse C/C++IDE //комментарий в лаунчере +Terminal=false +Exec=/home/kofed/eclipse/eclipse //путь к исполняемому файлу +Icon=/home/kofed/eclipse/icon.xpm //путь к иконке +``` diff --git a/LINUX/RANGER.md b/LINUX/RANGER.md new file mode 100644 index 0000000..4886c13 --- /dev/null +++ b/LINUX/RANGER.md @@ -0,0 +1,91 @@ +Откройте `ranger`, нажмите S, чтобы открыть `ranger`оболочку. +Затем введите `select-editor`, появится пронумерованный список доступных опций, и вам нужно будет ввести номер нужной опции. + +***Навигация:*** +``` +j/Down # - переместиться вниз +k/Up # - переместиться вверх +h # - перейти в родительский каталог +gg # - перейти в начало списка +G # - перейти в конец списка +-f # - листать вниз +-b # - листать вверх +J # - листать вниз на 1/2 страницы +K # - листать вверх на 1/2 страницы +H # - вернуться назад через историю навигации +L # - переместиться вперед по истории навигации +``` + +***Переключение директории:*** +``` +gh = cd ~ +ge = cd / etc +gu = cd / usr +gd = cd / dev +go = cd / opt +gv = cd / var +gm = cd / media +gM = cd / mnt +gs = cd / srv +gr = cd / +gR = cd для глобальной конфигурации +``` + +***Работа с файлами:*** +``` +i: показать файл (позволяет просто просмотреть текстовый файл в пейджере вместо редактирования) +l или E: открыть файл (открывает файл в файловом обработчике по умолчанию) +r: открыть файл с помощью… (позволяет выбрать программу, с помощью которой будет открыт файл) +z: изменить настройки (часто используемые тумблеры) +zh: просмотреть скрытые файлы +: выбрать текущий файл +t: отметить файл +cw: переименовать текущий файл +/: поиск файлов +n: перейти к следующему совпадению +N: вернуться к предыдущему совпадению +yy: скопировать файл +dd: отметить файл, чтобы вырезать +pp: вставить файл +: удалить выбранный файл. +``` + +***Вкладки:*** +``` +-n = Создать новую вкладку +-w = Закрыть текущую вкладку + = Следующая вкладка +  = Предыдущая вкладка +``` + +***Общие команды:*** +``` +?: просмотр мануала Ranger +1?: просмотреть комбинации клавиш +2?: справка по командам +3?: справка по настройкам +R: обновить текущий каталог +Q: выход. + +! – выполнить команду из оболочки (не-ranger команду) +: — выполнить команду ranger +du – измерить использование диска текущего каталога +chmod – изменить права на текущий файл. +``` + +Также есть множество флагов, которые можно использовать для изменения поведения команды. Их нужно использовать с тире после символа восклицательного знака (для команд оболочки) или двоеточия (для команд ranger). +``` +-s: # игнорировать вывод текущей команды +-d: # отключить процесс и работать в фоновом режиме +-p: # вернуть вывод в пейджер Ranger +-w: # Не возвращать сразу после выполнения команды. +-c: # выполнить команду на текущий файл (не на выбранные файлы). +``` + +Также Ranger позволяет объединять файлы в группы для выполнения замены. Это делается при помощи заполнителей: +``` +%f: # заменить выделенный файл +%d: # заменить текущий каталог +%s: # заменить выбранные файлы +%t: # заменить текущие отмеченные файлы. +``` diff --git "a/LINUX/REPOSITORIES \321\201\320\262\320\276\320\271 \321\200\320\265\320\277\320\276\320\267\320\270\321\202\320\276\321\200\320\270\320\271 \321\201 \320\277\320\260\320\272\320\265\321\202\320\260\320\274\320\270.md" "b/LINUX/REPOSITORIES \321\201\320\262\320\276\320\271 \321\200\320\265\320\277\320\276\320\267\320\270\321\202\320\276\321\200\320\270\320\271 \321\201 \320\277\320\260\320\272\320\265\321\202\320\260\320\274\320\270.md" new file mode 100644 index 0000000..172b126 --- /dev/null +++ "b/LINUX/REPOSITORIES \321\201\320\262\320\276\320\271 \321\200\320\265\320\277\320\276\320\267\320\270\321\202\320\276\321\200\320\270\320\271 \321\201 \320\277\320\260\320\272\320\265\321\202\320\260\320\274\320\270.md" @@ -0,0 +1,33 @@ +Если вам нужно централизованно управлять программным обеспечением в сети или хранить свои пакеты, можно создать локальный репозиторий. Это полезно для DevOps, админов и разработчиков. + +⚙️ **Настройка локального репозитория (Debian/Ubuntu)** +```sh +# Создаем каталог для пакетов:** + +mkdir -p /var/repo/{conf,incoming} + +# Генерируем конфигурацию репозитория** + +# В /var/repo/conf/distributions добавляем: +Origin: MyRepo   +Label: My Custom Repository   +Suite: stable   +Codename: custom   +Components: main   +Architectures: amd64   +Description: My private Debian repository + +# Добавляем пакеты в репозиторий:** +cp mypackage.deb /var/repo/incoming/ + +# Индексируем репозиторий: +cd /var/repo && dpkg-scanpackages . /dev/null | gzip -9c > Packages.gz + +# Добавляем в систему (клиент) + +# Редактируем /etc/apt/sources.list: +deb [trusted=yes] file:///var/repo custom main + +sudo apt update + +``` diff --git a/LINUX/RM.md b/LINUX/RM.md new file mode 100644 index 0000000..78e1612 --- /dev/null +++ b/LINUX/RM.md @@ -0,0 +1,10 @@ +Как **удалить** непустую папку **Linux**? + +Если хотите **удалить** пустой каталог, задействуйте опцию - d +`rm -d dirname + +Если хотим **удалить** **непустой** каталог и все файлы, которые в нём находятся, поступаем следующим образом: +`rm -r dirname + +Опять же, в случае наличия защиты от записи, система **Linux** спросит пользователя, стоит ли выполнять **удаление**. Если мы хотим **удалить** файлы и **непустые** каталоги без лишних вопросов, делаем так: +`rm -rf dirname \ No newline at end of file diff --git a/LINUX/ROOT.md b/LINUX/ROOT.md new file mode 100644 index 0000000..47a70dd --- /dev/null +++ b/LINUX/ROOT.md @@ -0,0 +1 @@ +sudo passwd root diff --git a/LINUX/RSYNC.md b/LINUX/RSYNC.md new file mode 100644 index 0000000..8ced185 --- /dev/null +++ b/LINUX/RSYNC.md @@ -0,0 +1,373 @@ +```sh +sudo apt-get install rsync + +rsync_ _опции_ _источник_ _приемник_ + +rsync -avzhe ssh vlapa@178.20.46.157:~/git/ ~/1Tb_r1-a/Backup/_git/ +``` + +****-v**** - Выводить подробную информацию о процессе копирования;** +-****-q**** - Минимум информации; +****-c**** - Проверка контрольных сумм для файлов; +****-a**** - Режим архивирования, когда сохраняются все атрибуты оригинальных файлов; +****-R**** - Относительные пути; +****-b**** - Создание резервной копии; +****-u**** - Не перезаписывать более новые файлы; +****-l**** - Копировать символьные ссылки; +****-L**** - Копировать содержимое ссылок; +****-H**** - Копировать жесткие ссылки; +****-p**** - Сохранять права для файлов; +****-g**** - Сохранять группу; +****-t**** - Сохранять время модификации; +****-x**** - Работать только в этой файловой системе; +****-e**** - Использовать другой транспорт, например, ssh; +****-z**** - Сжимать файлы перед передачей; +****--delete**** - Удалять файлы которых нет в источнике; +****--exclude**** - Исключить файлы по шаблону; +****--recursive**** - Перебирать директории рекурсивно; +****--no-recursive**** - Отключить рекурсию; +****--progress**** - Выводить прогресс передачи файла; +****--stat**** - Показать статистику передачи; +****--********max-size**** ****—**** максимальный размер файла для передачи; +****--********bwlimit**** ****—**** ограничение скорости для передачи файлов; +****--version**** - Версия утилиты. + + +```sh +sudo vim /etc/rsyncd.conf` + +pid file = /var/run/rsyncd.pid +lock file = /var/run/rsync.lock` +log file = /var/log/rsync.log` +[share] path = /tmp/share/` +hosts allow = 192.168.56.1` +hosts deny = * list = true` +uid = root gid = root` +read only = false` +``` + +**Здесь мы задаем путь к нашей папке для синхронизации, разрешаем доступ к серверу только с ip адреса (192.168.56.1) и запрещаем все остальные подключения. Параметры uid и gid указывают пользователя и группу, от которых будет запущен демон. Лучше не использовать root, а указать пользователя nobody и выдать ему права на ту папку, в которую будет выполняться синхронизация каталогов rsync.** + +Настройка rsync завершена, остается сохранить файл, запустить сервер rsync и добавить его в автозагрузку: + +```sh +sudo systemctl start rsync +sudo systemctl enable rsync + +sudo service cron reload +``` + +**Копирование и синхронизация файлов на локальном компьютере + +`rsync -zvh file /tmp/backups/` + +**Указав опцию** `--progress` **вы можете видеть сколько процентов уже скопировано, а сколько еще осталось:** + +`rsync -zvh --progress file /tmp/backups/` + +**Синхронизация папок на локальной машине** + +`rsync -azvh /home/user/documents /tmp/backups/` + + +**Синхронизация с удаленным сервером** + +`rsync -avz /home/sergiy/tmp/ root@192.168.56.102:/home/` + + +**По умолчанию rsync попытается использовать транспорт ssh. Если вы хотите использовать ранее созданный сервер rsync, нужно указать это явно:** + +`rsync -avz /home/sergiy/tmp/ rsync://192.168.56.102:/share` + +Точно также можно синхронизировать файлы с rsync из удаленного сервера: + +`rsync -avz root@192.168.56.102:/home/ /home/sergiy/tmp/` + +Адрес удаленного сервера записывается в таком формате: + +`имя_пользователя@адрес_машины/папка/на/удаленной_машине` + +Синхронизация папок rsync будет выполняться на стандартном порту. + + +### _**Синхронизация файлов по SSH**_ + +Чтобы задать протокол подключения используется опция -e. При использовании SSH все передаваемые данные шифруются и передаются по защищенному каналу, таким образом, чтобы никто не мог их перехватить. Для использования SSH вам нужно знать пароль пользователя в системе. + +Синхронизация файлов rsync с удаленного сервера по ssh будет выглядеть вот так: + +`rsync -avzhe ssh root@192.168.56.102:/root/install.log /tmp/` + +Если вы используете другой порт для ssh, то здесь его можно указать: + +`rsync -avzhe "ssh -p 22" root@192.168.56.102:/root/install.log /tmp/` + +А теперь передадим данные на тот же сервер: + +`rsync -avzhe ssh backup.tar root@192.168.0.101:/backups/` + +**Просмотр прогресса при синхронизации** + +Для просмотра прогресса копирования файла с одной машины на другую используется опция progress: + +`rsync -avzhe ssh --progress /home/user/documents root@192.168.56.102:/root/documents` + +**Синхронизация не всех файлов в rsync** + +Опции include и exclude позволяют указать какие файлы нужно синхронизировать, а какие исключить. Опции работают не только с файлами но и с директориями. + +Например, скопируем все файлы, начинающиеся на букву R: + +`rsync -avze ssh --include 'R*' --exclude '*'` +`root@192.168.56.102:/root/documents/ /root/documents` + +**Удаление при синхронизации** + +Во время синхронизации можно удалять файлы, которых нет на машине откуда идет rsync синхронизация, для этого используется опция --delete. + +Например: + +`rsync -avz --delete root@192.168.56.102:/documents/ /tmp/documents/` + +Если перед выполнением этой команды создать в папке файл которого нет на удаленном сервере, то он будет удален. + +**Максимальный размер файлов** + +Вы можете указать максимальный размер файлов, которые нужно синхронизировать. Для этого используется опция --max-size. Например, будем синхронизировать только файлы меньше 200 килобайт: + +`rsync -avzhe ssh --max-size='200k' /user/documents/` +`root@192.168.56.102:/root/documents` + +**Удаление исходных файлов** + +Есть возможность удалять исходные файлы после завершения синхронизации с удаленным сервером: + +`rsync --remove-source-files -zvh backup.tar /tmp/backups/` + +Таким образом, файл backup.tar будет удален после завершения копирования в папку /tmp/backups. + +**Режим симуляции rsync** + +Если вы новичок, и еще не использовали rsync, то возможно захотите посмотреть как отработает команда без применения реальных действий в файловой системе. Для этого есть опция dry-run. Команда только выведет все выполняемые действия в терминал, без выполнения реальных изменений: + +`rsync --dry-run --remove-source-files -zvh backup.tar /tmp/backups/` + + +**Ограничить скорость передачи** + +Вы можете ограничить использование пропускной способности сети с помощью опции ==`--bwlimit`==: + +`rsync --bwlimit=100 -avzhe ssh /user/home/documents/ root@192.168.56.102:/root/documents/` + +Как я уже писал выше, rsync синхронизирует только части файла, если вы хотите синхронизировать файл целиком используйте опцию ==-W==: + +`rsync -zvhW backup.tar /tmp/backups/backup.tar backup.tar` + +**Автоматическая синхронизация папок rsync** + +Можно расписать автоматическую синхронизацию с помощью cron. Но в случае доступа к серверу по SSH необходимо будет создать ключ и загрузить его на сервер, чтобы аутентификация проходила без запроса пароля. + +Создаем ключ: + +`ssh-keygen -t rsa` + +Загружаем ключ на сервер к с которым собираемся синхронизироваться: + +`ssh-copy-id -i /home/sk/.ssh/id_rsa.pub sk@192.168.1.250` + +Теперь можем переходить к настройке расписания cron. Будем запускать синхронизацию каждый день: + +`crontab -e` + +`00 05 * * * rsync -azvre ssh /home/user/Downloads/ 192.168.56.102:/share` + +rsync синхронизация каталогов будет выполняться каждый день в пять утра. + +`@hourly rsync -avzhe ssh vlapa@178.20.46.157:/home/vlapa/git/ /home/vlapa/1Tb_r1-a/Backup/_git/` + +rsync синхронизация каталогов будет выполняться каждый час. + +============================== + +**Автоматическая синхронизация файлов с удалённым сервером** +```bash +#!/bin/bash + +# Локальная директория для синхронизации +LOCAL_DIR="/path/to/local/dir" + +# Удалённый сервер и директория +REMOTE_USER="user" +REMOTE_SERVER="server.com" +REMOTE_DIR="/path/to/remote/dir" + +# Синхронизация с удалённым сервером +rsync -avz $LOCAL_DIR $REMOTE_USER@$REMOTE_SERVER:$REMOTE_DIR + +# Вывод сообщения о завершении +echo "Синхронизация завершена!" +``` +✔️ **Скрипт синхронизирует файлы из локальной директории на удалённый сервер с помощью** rsync**.** + +======================================= +# Утилита rcync: синхронизация директорий и backup-ы + +15.12.2019 + +Теги: [CLI](https://tokmakov.msk.ru/blog/tags/15) • [Linux](https://tokmakov.msk.ru/blog/tags/1) • [Директория](https://tokmakov.msk.ru/blog/tags/37) • [Команда](https://tokmakov.msk.ru/blog/tags/2) • [Конфигурация](https://tokmakov.msk.ru/blog/tags/11) • [Настройка](https://tokmakov.msk.ru/blog/tags/266) • [Сервер](https://tokmakov.msk.ru/blog/tags/301) • [Файл](https://tokmakov.msk.ru/blog/tags/35) + +Утилита rsync — это программное обеспечение с открытым исходным кодом, которое можно использовать для синхронизации файлов и директорий с локального компьютера на удаленный и наоборот. Примечательная особенность rsync — возможность передавать файлы с шифрованием, используя ssh в качестве транспорта. + +Синтаксис команды + +$ rsync опции источник приемникКопировать + +## Примеры использования + +Перед тем, как копировать файлы, всегда стоит сделать пробный прогон (ключ `-n`) в режиме с показом подробностей (`-v`): + +`rsync -avn /path/to/src/ user@example.com:/path/to/dstКопировать + +В этом режиме `rsync` покажет список файлов, которые он скопирует. После этого можно запускать настоящее копирование: + +`rsync -av /path/to/src/ user@example.com:/path/to/dstКопировать + +В этой команде ключ `-a` подразумевает рекурсивное копирование всех файлов и каталогов включая их атрибуты: владелец, группа, дата создания и изменения. Ключ `-v` даст подробный отчет о работе по мере выполнения и по окончании. + +Обратите внимание на слэш в конце первого аргумента — без него файлы источника `/path/to/src/файлы` будут размещены в `/path/to/dst/src/файлы`. А при наличии слэша в конце первого аргумента — файлы будут в `/path/to/dst/файлы`, что скорее всего и нужно. + +По умолчанию `rsync` попытается использовать транспорт ssh. При использовании другого порта для ssh, его нужно указать: + +`rsync -av -e "ssh -p 2222" /path/to/src/ user@example.com:/path/to/dstКопировать + +Точно также можно скопировать файлы из удаленного сервера на локальную машину: + +`rsync -av user@example.com:/remote/machine/path/ /local/machine/pathКопировать + +Обратите внимание, что источник всегда является первым аргументом, а приемник — вторым. + +Для автоматического копирования, например по `cron`, необходимо настроить авторизацию с использованием ключей. + +## Основные опции + +**Опция** `-a` или `--archive` — рекурсивно копирует файлы, включая разыменованные символьные ссылки, с сохранением связанных с файлами сведений об их владельцах, групповых и индивидуальных правах и времени последнего изменения. Использование этого ключа равносильно указанию ключей `--group`, `--links`, `--owner`, `--perms`, `--recursive` и `--times`. + +**Опция** `-b` или `--backup` — переименовывает файлы, которые в противном случае должны быть удалены или перезаписаны, добавляя к их существующим именам знак тильды (~). Эта опция обычно используется вместе с `--backup-dir` — вместо переименования `rsync` перемещает эти файлы в указанный каталог. + +**Опция** `--backup-dir=dir` — используется вместе с `--backup` и перемещает в директорию `dir` те файлы, которые в противном случае должны быть удалены или перезаписаны. После перемещения старой версии файла в каталог `dir` новая версия из источника копируется в приемник. + +Если `dir` указан с использованием относительного пути, то это путь относительно приемника. + +**Опция** `--copy-unsafe-links` — для каждого файла, являющегося символьной ссылкой на файл за пределами той иерархии, которой принадлежит источник, производится копирование файла, на который указывает ссылка, а не символьной ссылки на него. + +**Опция** `--delete` заставляет `rsync` удалять файлы в приемнике, которые отсутствуют в источнике. + +**Опция** `-n` или `--dry-run` — запуск утилиты без записи на диск. С опцией `--verbose` позволяет посмотреть, что будет сделано, если запустить rsync без `--dry-run`. Особенно полезно запускать перед использованием `--delete`. + +**Опция** `-g` или `--group` — сохранение групповых связей копируемых файлов. + +**Опция** `-l` или `--links` — для каждого файла, являющегося символьной ссылкой, копирование символьной ссылки, а не файла, на который указывает ссылка, даже если файл, на который указывает ссылка, не присутствует в источнике. + +**Опция** `--link-dest=dir` — если файл существует в источнике, но его нет в приемнике, rsync ищет в каталоге `dir` файл с точно таким же именем. Если точная копия файла будет найдена, rsync создает из файла в `dir` жесткую ссылку на него в приемнике. Если точная копия не будет найдена, rsync копирует файл в приемник. + +**Опция** `-o` или `--owner` — сохранение данных о владельце копируемых файлов (только для пользователей с root-правами). + +**Опция** `-p` или `--perms` — сохранение данных о правах доступа копируемых файлов. + +**Опция** `-r` или `--recursive` — рекурсивный спуск по каталогу-источнику и копирование всех файлов в иерархии. + +**Опция** `-t` или `--times` — сохранение данных о времени последнего изменения копируемых файлов. + +**Опция** `-u` или `--update` — пропускать файл, если в приемнике оказался файл новее, чем в источнике. + +**Опция** `-v` или `--verbose` — вывод информации о том, что в данный момент делает rsync. + +**Опция** `-z` или `--compress` — сжатие файлов в процессе копирования. + +## Зеркальное копирование + +Утилиту rsync можно использовать для создания зеркальной копии каталога. На удаленной системе должен быть установлен ssh-сервер (к нему можно подключиться с использованием утилиты ssh). Если нужно запускать копирование по расписанию, следует настроить авторизацию с использованием ключей. + +`rsync --archive --update --delete /path/to/mirror/ user@example.com:/path/to/mirrorКопировать + +Опция `--update` заставляет rsync не перезаписывать самые последние версии файлов на удаленной системе более старыми версиями таких же файлов, взятых из локальной системы. Опция `--delete` заставляет rsync удалять на удаленном сервере файлы, отсутствующие на локальной системе. + +## Резервное копирование + +После проведения исходного резервного копирования rsync может эффективно осуществлять дополнительное резервное копирование, экономя место на диске и время копирования. При дополнительном (инкрементном) копировании сохраняются только те файлы, которые претерпели изменения со времени последнего резервного копирования. + +С помощью опции `--link-dest=dir` можно превращать каждое дополняющее резервное копирование в полноценное, путем создания жестких ссылок между дополняющими резервными копиями и неизменившимися файлами, имеющимися в исходной полноценной резервной копии. + +Если `dir` задается как отосительный путь, то он задается относительно файла-приемника. + +`rsync --archive --link-dest=../backup /path/to/src/ user@example.com:/path/to/dstКопировать + +При запуске этой команды rsync проходит сверху вниз по содержимому каталога `/path/to/src`, проверяя каждый найденный файл. Для каждого файла в каталоге `/path/to/src` утилита ищет в каталоге `/path/to/dst` точную копию этого файла. + +- Если она находит точную копию в каталоге `/path/to/dst` — переходит к следующему файлу +- Если она не находит точную копию в каталоге `/path/to/dst` — ищет точную копию в каталоге `backup` + - Если она находит точную копию файла в каталоге `backup`, утилита создает жесткую ссылку из файла в каталоге `backup` на файл в каталоге `/path/to/dst` + - Если она не находит точную копию файла в каталоге `backup`, утилита копирует файл из каталога `/path/to/src` в каталог `/path/to/dst` + +Небольшой bash-скрипт, демонстрирующий создание полноценной и дополняющей резервной копии: + +```bash +#!/bin/bash +rm -rf backup-two-days-ago +mv backup-one-day-ago backup-two-days-ago +mv backup-latest-state backup-one-day-ago +rsync --archive --link-dest=../backup-one-day-ago /some/path/data/ backup-latest-state +``` + +Можно немного доработать этот скрипт, чтобы хранить не три, а тридцать резервных копий: + +```bash +#!/bin/bash + +SOURCE='/home/evgeniy/data' +BACKUP='/home/evgeniy/backup' +COUNT=30 # кол-во бэкапов + +rm -rf ${BACKUP}/backup-${COUNT} +for (( i = COUNT; i > 1; i-- )); do + mv ${BACKUP}/backup-$((i - 1)) ${BACKUP}/backup-${i} +done + +rsync --archive --link-dest=../backup-2 ${SOURCE}/ ${BACKUP}/backup-1 +``` + +========================================== +Шпора по флагам rsync + +![[2025-06-20_22-34-47.png]] + +Rsync - программка, которую можно использовать для синхронизации файлов и каталогов с локального компьютера на удаленный и наоборот. Чем-то напоминает scp. + +`rsync file1 file2 ... user@host:dest + +`rsync -a dir host:dest_dir + +Взаимодействием с сервером возможности утилиты не ограничиваются. Все операции можно выполнять и локально. По умолчанию rsync копирует ресурсы без учета предыдущего содержимого целевого каталога. + +Например, если вы перенесли каталог d, содержащий файлы a и b, на машину, на которой был файл d/c, то после копирования сервер будет содержать d/a, d/b и d/c. + +Чтобы создать точную копию исходного каталога, необходимо из целевого каталога удалить не существующие в исходном каталоге файлы, например d/c в примере: + + +`rsync -a --delete dir host:dest_dir + +Посмотрим на еще 1 пример: + +`rsync -avh --del --progress dir1 dir2 dir3 backup + +В результате выполнения, каталоги "dir1, dir2 и dir3", вместе со всем их содержимым, будут рекурсивно скопированы в каталог backup. + +Если какие-то файлы уже есть в каталоге backup, и не были изменены, они будут пропущены. + +Исключение файлов и каталогов + +Еще одной значительной фичей rsync является способность исключать файлы и каталоги из процесса передачи. + +Например, ты хочешь перенести локальный каталог src на хост и при этом исключить все данные с именем .git. Можно сделать следующим образом: + +`rsync -a --exclude=.git src host: diff --git a/LINUX/SAMBA.md b/LINUX/SAMBA.md new file mode 100644 index 0000000..357b5e4 --- /dev/null +++ b/LINUX/SAMBA.md @@ -0,0 +1,262 @@ +```shell +sudo su +apt install -y samba samba-client +systemctl enable smbd +systemctl start smbd +systemctl status smbd +systemctl stop smbd +systemctl restart smbd + +systemctl disable smbd +systemctl reload smbd + +ufw allow Samba +ufw allow 445 +ufw allow 137 +ufw allow 138 +ufw allow 139 + +#=================================== +smbclient -L 192.168.1.1 +smbclient -L 192.168.1.1 -U username +#=================================== + +### Сброс кэша сетевых подключений Windows: +# (если не дает подключиться) +# cmd + +net use * /delete +ipconfig /flushdns +nbtstat -R + +#=================================== + +sudo cp /etc/samba/smb.conf /etc/samba/res_smb.conf + +# оставим в файле конфигурации только те строки, которые используются в работе и не являются комментарием: +grep -v '^ *#\|^ *$' /etc/samba/smb.conf | sudo tee /etc/samba/smb.conf + +sudo vim /etc/samba/smb.conf + +#============================ +[global] +workgroup = WORKGROUP +security = user +map to guest = bad user +wins support = no +dns proxy = no + +[150Gb] +path = /home/media/150Gb +public = yes +writable = yes +read only = no +guest ok = yes +create mask = 0775 +directory mask = 0775 +force create mode = 0775 +force directory mode = 0775 + +[private] +path = /media/samba/private +public = no +writable = yes +read only = no +guest ok = no +create mask = 0775 +directory mask = 0775 +force create mode = 0775 +force directory mode = 0775 +#============================ + +testparm -s +systemctl restart smbd nmbd + +# Доступ по учетным данным: +# Создаем каталог для новой папки: +mkdir /data/staff + +# Задаем права на созданный каталог: +chmod 775 /data/staff + +sudo adduser samba # add a regular Linux user +sudo smbpasswd -a samba # add Samba user called `samba` +sudo smbpasswd -e samba # enable - may not be needed + +# Создаем пользователя в системе Linux: +useradd staff1 + +# где_ staff1 _— имя пользователя +# Задаем пароль для пользователя: +passwd staff1 + +# Теперь создадим пользователя в samba: +smbpasswd -a staff1 + +# Перезапускаем samba: +systemctl restart smbd nmbd + +# Посмотреть пользователей samba: +sudo pdbedit -L -v +-L - to list users.  +-v - to be verbose. + +# Доступ для пользователей и групп:*** +sudo mkdir /var/acc_gr_and_user +sudo chmod 777 /var/acc_gr_and_user +``` + +```bash +[acc_gr_and_user] +    comment = Private Folder +    path = /var/acc_gr_and_user +    public = no +    writable = no +    read only = yes +    guest ok = no +    valid users = admin, staff2, staff3, @privateusers +    write list = admin, staff2 +    create mask = 0775 +    directory mask = 0775 +    force create mode = 0775 +    force directory mode = 0775 +    inherit owner = yes + + +systemctl restart smbd nmbd +``` + +*стоит обратить внимание на следующие настройки: + +- ***path = /data/private*** — используем новый путь до папки +- ***writable = no*** +- ***read only = yes*** +- в данном примере мы разрешим запись в каталог только некоторым пользователям. Поэтому общие настройки, разрешающие запись в папку, должны быть запрещены +- ***valid users*** - список пользователей, которым разрешено подключаться к каталогу. В данном примере разрешения работают для пользователей +- **_admin_**__,__ **_staff2_** __и__ **_staff3_**, а также для всех, кто входим в группу ***@privateusers*** +- ***write list*** - список пользователей, которые имеют доступ к папке на чтение и запись. В данном примере мы разрешаем это только для пользователей. ***admin*** и ***staff2*** +- ***inherit owner*** - опция позволяем включить наследование владельца при создании папок и файлов + +*если мы хотим, чтобы доступ к каталогу был полный у определенных пользователей (без разделения на тех, кто может только читать и тех, кто может также писать в папку), то опцию +***write list*** можно не указывать, а опции ***writable*** и ***read only*** оставить как в примерах выше + +***Вывести список сетевых ресурсов samba:*** +`smbclient -L localhost -U%` + +***Подключение к ресурсу из Linux***: +```sh +sudo apt install cifs-utils -y +mount.cifs {ресурс} {путь к директории в которую монтируем } {-o опции} +mount.cifs //10.8.0.1/anonimus_share /mnt +``` + +Автоматическое монтирование директории происходит с использованием файла **fstab**. Для начала создадим дополнительный файл с учетными данными пользователя для авторизации. Оправданно будет создать этот файл в директории **root**. + +`sudo nano /root/.smbselus` + +Внесем в файл следующие директивы: + +`username=selectel password=uspass` + +Сохраняем и переходим к конфигурационному файлу **fstab**. + +`sudo nano /etc/fstab` + +Добавляем в него следующий блок инструкций: + +`//10.8.0.1/acc_gr_and_user /mnt cifs user,rw,credentials=/root/.smbselus 0 0` + +Конкретизируем каждый пункт: + +**_//10.8.0.1/acc_gr_and_user_** 一 директория, которую необходимо смонтировать; +**_/mnt_** 一 директория, в которую необходимо смонтировать; +**_cifs user_** 一 разрешает монтировать любому пользователю; +**_rw_** 一 предоставляет права на чтение и запись; +**_credentials=/root/.smbselus_** 一 файл с учетными данными пользователя; +_0_ используется утилитой dump 0 一 выключен; +_0_ используется утилитой fsck 0 一 выключен. + +В некоторых случаях требуется добавить директиву **domain** в файл **.smbselus.** +## Настройка сетевой корзины + +По умолчанию файлы из директории удаляются совсем. При определенных условиях необходимо иметь возможность восстановить эти файлы. В Samba данная возможность реализована с использованием сетевой корзины. Алгоритм работы следующий: настраивается скрытый каталог, в который при удалении перемещаются файлы. + +Первым этапом создадим директорию для корзины и назначим ей права: +```sh +sudo mkdir /var/recycle | sudo chmod 777 /var/recycle + +# Внесем блок директив в конфигурационный файл:  + +sudo vim /etc/samba/smb.conf + +[Recycle] +comment = rec_dir +path = /var/recycle +public = yes +browseable = yes +writable = yes +vfs objects = recycle +recycle:repository = .recycle/%U +recycle:keeptree = Yes +recycle:touch = Yes +recycle:versions = Yes +recycle:maxsize = 0 +recycle:exclude = *.tmp, ~$* +recycle:exclude_dir = /tmp +``` + +Значения директив: + +**_[Recycle]_** 一 имя ресурса, отображаемое клиенту при подключении;                                                                            +**_comment = rec_dir_** 一 комментарий; +**_path = /var/recycle_** 一 директория, в которой размещена сетевая корзина. +**_public = yes_** 一 доступ на чтение всем авторизованным пользователям, также гостевым;  +**_browseable = yes_** 一 параметр устанавливает, будет ли общий ресурс отображаться в списке доступных общих ресурсов в сетевом окружении и в списке просмотра; +**_writable = yes_** 一 право записи в сетевой ресурс; +**_vfs objects = recycle_** 一 использование подсистемы recycle; +**_recycle:repository = .recycle/%U_** 一 место хранения удаленных объектов. В этой директиве настроено попадание удаленных файлов в каталог с именем пользователя, который создается автоматически в директории .**recycle;** +**_recycle:keeptree = Yes_** 一 удаление с сохранением дерева каталогов; +**_recycle:touch = Yes_** 一 изменение даты при перемещении файла в сетевую корзину; +**_recycle:versions = Yes_** 一 добавление номера версии при удалении файлов с одинаковыми именами; +**_recycle:maxsize = 0_** 一 параметр отвечает за максимальный размер файла, который может быть перемещен в корзину. При текущем значении возможно переместить файл любого размера; +**_recycle:exclude = *.tmp, ~$*_**  一 исключение файлов заданного типа; +**_recycle:exclude_dir = /tmp_** 一 исключение директории заданного типа. + +```sh +mkdir /data/recycle +chmod 777 /data/recycle + +# Перезапускаем самбу: + +systemctl restart smbd + +# Пробуем зайти в сетевой каталог Recycle и создать, а после удалить файл. Он должен оказаться к скрытой папке .recycle. + +# Для автоматической чистки сетевой корзины можно создать скрипт: + +mkdir /scripts +vim /scripts/cleanrecycle.sh + +#!/bin/bash + +recyclePath="/data/recycle/.recycle" +maxStoreDays="30" + +/usr/bin/find $recyclePath -name "*" -ctime +$maxStoreDays -exec rm {} \; + +# в данном скрипте мы ищем все файлы в каталоге /data/recycle/.recycle, которые старше 30 дней и удаляем их. + +# Разрешаем запуск скрипта: + +chmod +x /scripts/cleanrecycle.sh + +# Создаем задание в планировщике: + +crontab -e + +0 5 * * * /scripts/cleanrecycle.sh + +# в данном примере мы будем запускать скрипт по очистке сетевой корзины каждый день в 05:00. +``` + + diff --git a/LINUX/SCP, SFTP.md b/LINUX/SCP, SFTP.md new file mode 100644 index 0000000..e133e5f --- /dev/null +++ b/LINUX/SCP, SFTP.md @@ -0,0 +1,70 @@ +Скопируем файл с удаленного хоста в локальный каталог: +$ scp user@host:/path/to/file /local/dir + +Скопируем файлы с локального компьютера на удаленный хост: +$ scp /local/dir/file user@host:/remote/dir + +Скопируем файл с одного удаленного хоста на другой: +$ scp user1@host1:/remote/file user2@host2:/remote/dir + +======================================== +Прога **sftp** не требует постоянно указывать адрес **ssh** сервера. Вместо этого мы подключаемся к нему один раз и используем интерфейс **get** и **put**: + +$ sftp user@host +sftp> get /remote/dir/file /local/dir +sftp> put /local/dir/file /remote/dir + +Для передачи каталогов используем флаг "**-r**":  +$ scp -r /local/dir user@host:/remote/dir +$ sftp> put -r /local/dir /remote/dir + +***Также хочется отметить, что **sftp** имеет важное преимущество перед обычным **ftp** — она не требует, чтобы на удаленном узле работал сервер **FTP**. Ей необходим только **SSH**. + +======================================== +```sh +mkdir bot +scp /полный/путь/к файлу/скрипта/bot.py vlapa@10.10.10.2:~/work_prog + +# Копируем файл на сервер (копирум = что -> куда): + +scp -r source_file_name username@destination_host:destination_folder +# параметр: -r – рекурсивное копирование папок (включая подкаталоги) + +scp /etc/wireguard/wg0.conf vlapa@10.0.0.1:/home/vlapa/wg000.conf +scp vlapa@10.0.0.1:/home/vlapa/wg0.conf log.txt +scp path/myfile user@8.8.8.8:/full/path/to/new/location/ + +# Копируем файл с сервера: + +scp user@8.8.8.8:/full/path/to/file /path/to/put/here +``` + +``` +-r - рекурсивное копирование +-p - сохраняет временные метки и права доступа на файлы +-q - тихий режим. Подавляет все сообщения, кроме ошибок +-C - включает сжатие данных +-P 1345 - указывает порт подключения к удаленному хосту +``` +💻 **Передача файлов с помощью scp** +Когда нам необходимо быстро передать файл на сервер или наоборот, скопировать файл с сервера на локалку, мы можем воспользоваться утилитой — **scp (Secure Copy Protocol)**.  + +SCP — это утилита командной строки в Linux, используемая для безопасной передачи файлов между локальным и удаленным узлами или между двумя удаленными узлами. Она использует протокол SSH (Secure Shell) для передачи данных, обеспечивая безопасное копирование файлов с использованием тех же мер аутентификации и безопасности, что и SSH. + +⚙️ **Примеры использования** +• Копирование файла на удалённый сервер: + +`scp file.txt user@remote_host:/path/to/destination   + +• Скачивание файла с сервера: +`scp user@remote_host:/path/to/file.txt ./   + +• Передача директории: +`scp -r папка user@remote_host:/path/to/destination   +`-r — ключ для копирования директории + + • Указание порта SSH: +Если вдруг используется нестандартный порт, мы можем его передать установив флаг -P: +`scp -P 2222 file.txt user@remote_host:/path/to/destination  + +➡️ Для оптимизации передачи больших файлов, можем добавить флаг -C для сжатия файлов. А также, для проверки удачной передачи, включаем подробный вывод -v. \ No newline at end of file diff --git "a/LINUX/SERVER - \320\274\320\276\320\275\320\270\321\202\320\276\321\200\320\270\320\275\320\263.md" "b/LINUX/SERVER - \320\274\320\276\320\275\320\270\321\202\320\276\321\200\320\270\320\275\320\263.md" new file mode 100644 index 0000000..6875dd6 --- /dev/null +++ "b/LINUX/SERVER - \320\274\320\276\320\275\320\270\321\202\320\276\321\200\320\270\320\275\320\263.md" @@ -0,0 +1,280 @@ +```sh +# Топ процессов по загрузке CPU +ps -eo pid,comm,%cpu --sort=-%cpu | head + +# Запуск интерактивного мониторинга +top +htop  # Более удобный интерфейс (если установлен) + +# Убить процесс с высокой нагрузкой по PID +kill -9 + +# Ограничить нагрузку от процесса (через nice) +nice -n 10 командa + +🟢 ps + sort быстро выявляют топовые процессы +🟢 top и htop позволяют следить в реальном времени +🟢 nice — задаёт приоритет и снижает нагрузку + +#================================================= +# Просмотр истории команд пользователя   +history + +# Узнаем кто зарегистрирован в системе и сколько времени в ней находится: +who + +# список всех пользователей, которые сейчас зарегистрированы в системе: +users + +# Подробная информация о сессиях пользователей   +w + +# когда и сколько времени определенный пользователь находился в системе: +last user_name + +# выдаст информацию о последних пяти входах: +last -n 5 user_name + +# вывод на экран перечня всех пользователей с датами их последнего подключения к системе: +lastlog +# удобно контролировать системные записи. У них дата последнего входа должна быть = Never logged in =, потому что под ними нельзя войти в систему (в качестве командной оболочки установлены /bin/false, /dev/null, /sbin/nologin и др.). Если вы заметили, что кто-либо проник в систему через одну из этих учетных записей, то это значит, что хакер использует ее, изменив настройки. Простая замена командной оболочки в файле /etc/passwd может открыть хакеру потайную дверь, и администратор не заметит этой трансформации. Но после выполнения команды lastlog все неявное становится явным.  + +# Обращайте внимание на тип подключения и адрес. Если что-то вызывает подозрение, то можно выявить атаку на этапе ее созревания. + +# Просмотр логов SSH-подключений   +sudo cat /var/log/auth.log | grep "ssh" + +# определить, какие файлы и какими пользователями открыты в данный момент: +lsof + +# Файлы, открытые определённым пользователем +lsof -u username + +# Определить, кто использует файл +lsof /path/to/file + +# Какие файлы открыты на определённом порту +sudo lsof -i :80 + +# Удалённые, но занятые файлы +lsof | grep deleted + +# узнать, какие процессы используют интернет прямо сейчас? + +lsof -i -P -n + +# Расшифровка: +# - lsof — список открытых файлов (в Linux всё — файл) +# -i — показывает сетевые соединения +# -P — отображает порты в числовом виде (без разрешения имён) +# -n — не резолвит имена хостов (быстрее) + +# Это отличный способ отследить: какие приложения слушают порты, кто подключен к твоему серверу, утечки сетевой активности. Полезно при аудитах, отладке и настройке брандмауэров. + + +# запустить программу на выполнение в фоновом режиме,программа запускается на выполнение, но не блокирует консоль. Да, вы видите вывод на экране, но в любой момент может забрать консоль себе, нажав Enter. Вы можете закрывать консоль, и стартующая программа не прервет свою работу. +start-olap-server.sh & + +# список программ работающих в фоновом режиме +jobs +# Число, которое вы видите в квадратных скобках – это номер выполняемой задачи. В фон можно отправить команду нажатием Ctrl+Z. В этом случае команда уходит в фон, но перестает выполняться. + +# Псевдонимы +alias ll="ls -l" + +# Перенаправление ввода/вывода +ls > outfile.txt +# В результате выполнения этой команды, результат (т.е. содержимое каталога) не будет выведено на экран, а будет сохранено в файл.  + +# Если отобразить содержимое файла:  +cat outfile.txt  +# То вы увидите то, что должно было быть на экране еще после выполнения той команды.  + +# получать параметры из файла. Например, можно выполнить команду: +cat < infile.txt  +# Команде cat в качестве параметров будут передано содержимое infile.txt. + +# Pipe +lastlog | sort + +# Посмотреть общее время загрузки системы +systemd-analyze + +# Показать список сервисов и их время загрузки +systemd-analyze blame + +# Проверить, какие службы запускаются при старте +systemctl list-unit-files --state=enabled + +# Отключить ненужный сервис +sudo systemctl disable имя_сервиса + +# Установить задержку старта для тяжёлого сервиса +sudo systemctl edit имя_сервиса +# В открывшемся файле добавить: +# [Service] +# ExecStartPre=/bin/sleep 30 + +🟢 systemd-analyze показывает время загрузки системы +🟢 systemctl позволяет отключать или откладывать запуск лишних служб +🟢 Оптимизация автозагрузки ускоряет старт системы + +# Регулярная проверка и настройка автозагрузки особенно важна для серверов и рабочих станций с ограниченными ресурсами. + +#================================================== +# информация о процессоре: +less /proc/cpuinfo +lscpu +lshw +nproc +sudo dmidecode -t 4 + + +I #================================================== +# === Изменение имени сервера ===: +hostnamectl set-hostname hostname +# перезагрузка не потребуется — новое имя начнет использоваться сразу ??? +# Если, по каким-либо причинам, новое имя не начнет использоваться, вводим: +systemctl restart systemd-hostnamed +# Подробнее, статус можно посмотреть командой: +hostnamectl status +# Также, можно задать дополнительное для описания имя (прелестное): +hostnamectl set-hostname "Description Name" --pretty + +#================================================== +# === Диски ===: +df -h +du -h +df -i + +lsblk +smartctl -a /dev/sda +smartctl -a /dev/nvme1n1 +cat /proc/mdstat + +iostat +iotop + +#================================================== +# === Процессы / память ===: +top +uptime +htop + +vmstat +free -m +cat /proc/meminfo + +ps aux +kill 123456 +systemctl status nginx +strace -p 1234567 + +#================================================== +# === Сеть ===: +# Быстрое отображение всех прослушиваемых портов +ss -tuln +# Показать процессы, связанные с портами +ss -tulnp +# Сканировать локальный хост с помощью nmap +nmap -sT -p- 127.0.0.1 +# Сканировать удалённый сервер на стандартные порты +nmap example.com +🟢 ss — встроенный инструмент, заменяющий netstat +🟢 nmap — мощный сканер безопасности и сетевых портов +🟢 Позволяет быстро увидеть, какие порты открыты и кем используются. + +ip a +ifconfig +netstat -rn +ip r +ping 8.8.8.8 +traceroute 8.8.8.8 +mtr 8.8.8.8 +dig vlapa.ru +dig @8.8.8.8 vlapa.ru +dig -t mx vlapa.ru +curl -Lv vlapa.ru +curl -v telnet://127.0.0.1:22 +curl --unix-socket /var/run/docker.sock http:/images/json +tcpdump -i any port 9100 -nn + +curl ifconfig.me + +#================================================== +# === Логи ===: + +cd /var/log +ls -lah +tail -f -n50 /var/log/syslog +ls -lah /var/log/auth.log +ls -lah /var/log/kern.log +dmesq -T +journalctl -xeu nginx + +#================================================== +# === Температура ===: +sudo apt install lm-sensors +sudo sensors-detect + +watch -n 1 sensors +watch -d -n 1 sensors + +#================================================== +# === Стресс тест ===: +sudo apt-get install stress-ng +# CPU: +stress-ng --cpu 32 --sequential 8 --timeout 60s --metrics-brief +#  Memory: +stress-ng --class memory --sequential 8 --timeout 60s --metrics-brief +# Disk IO: +stress-ng --class io --sequential 8 --timeout 60s --metrics-brief +# File System: +stress-ng --class filesystem --sequential 8 --timeout 60s --metrics-brief +# All: +# Если необходимо провести комплексное стресс-тестирование, можно задействовать работу нескольких основных подсистем вместе одной командой: + +stress-ng --cpu 4 --io 4 --vm 1 --vm-bytes 1G --timeout 60s --metrics-brief + +# Эта команда запустит тест для CPU в 4 потока, тест виртуальной памяти с размещением в ней одного гигабайта данных, а также 4 потока для тестирования операций ввода/вывода. + +# Что бы запустить тестирование всего «железа», используется команда: +stress-ng --sequential 0 --timeout 60s --metrics-brief + +# Эта команда запустит все тесты. После выполнения результат будет выведен в консоль. Во время выполнения команды сервер не нагружать другой работой. + +#================================================== +# Автоматическое уведомление при высокой загрузке CPU + +# Этот скрипт следит за загрузкой процессора и отправляет уведомление, если нагрузка превышает критическое значение. Полезен для серверов и рабочих станций, чтобы избежать перегрева и зависаний. + +#!/bin/bash +# Пороговое значение загрузки CPU в процентах +THRESHOLD=80 +# Лог-файл +LOG_FILE="$HOME/cpu_usage.log" +# Получаем среднюю загрузку CPU +CPU_LOAD=$(awk '{print $1}' < /proc/loadavg | awk '{print $1*100}') +if (( ${CPU_LOAD%.*} >= THRESHOLD )); then +    MESSAGE="⚠️ Высокая загрузка CPU: $CPU_LOAD%" +    echo "$MESSAGE" | tee -a "$LOG_FILE" +    notify-send "CPU Alert" "$MESSAGE" +fi + +# Проверяет загрузку процессора по /proc/loadavg. +# Если нагрузка превышает установленный порог, отправляет уведомление через notify-send. +# Записывает события в лог-файл для анализа. + +# Автоматический мониторинг: +# Добавьте скрипт в crontab для проверки каждые 5 минут + +*/5 * * * * /path/to/script.sh + +# Этот скрипт поможет отслеживать пиковые нагрузки и предотвратить зависания системы. + +#================================================== + + + +``` + diff --git "a/LINUX/SHELL \320\272\320\276\320\274\320\260\320\275\320\264\321\213.md" "b/LINUX/SHELL \320\272\320\276\320\274\320\260\320\275\320\264\321\213.md" new file mode 100644 index 0000000..b348b41 --- /dev/null +++ "b/LINUX/SHELL \320\272\320\276\320\274\320\260\320\275\320\264\321\213.md" @@ -0,0 +1,63 @@ +```sh +pwd +ls ; ls . ; ls -1 ; ls -la +cd ; cd .. ; cd ~ ; cd - + +mkdir name_dir ; mkdir -p /name1/name2 +touch test.txt ; > test.txt +echo "test" > test.txt + +cp test.txt test1.txt +cp -r dir1 dir2 +mv test.txt test2.txt +mv test2.txt dir + +rm test.txt +rm /путь/к/файлам/*.txt +rm -r dir_name +rm -i /путь/к/файлам/*.txt +rm -f /путь/к/файлам/*.txt + +ln file1 file2 + +chmod xxxx dir +chmod -R xxxx dir + +cat test.txt +cat test.txt | head -2 +cat test.txt | tail -1 + +grep 'string' test.txt + +find ./ -name 'test' +find ~/ -name 'test*' +find ~/ -name '*.php*' + +tar -cf arhive_name.tar dir_name +tar -xvf arhive.tar + +history +clear +exit + +Ctrl-C +Ctrl-D +Ctrl-U +Ctrl-Z +Ctrl-W +!! +Ctrl-Shift_c +Ctrl-Shift-V + +fg ; bg + +==================================== +sodu mkdir demo +^sodu^sudo + +# Это заменит «sodu» на «sudo» и повторно выполнит команду. + +# Это особенно полезно, если вы допустили ошибку в длинной команде, так как избавляет вас от необходимости повторно вводить команду. + +``` + diff --git a/LINUX/SHUTDOWN, HALT, POWEROFF, REBOOT.md b/LINUX/SHUTDOWN, HALT, POWEROFF, REBOOT.md new file mode 100644 index 0000000..57192ec --- /dev/null +++ b/LINUX/SHUTDOWN, HALT, POWEROFF, REBOOT.md @@ -0,0 +1,56 @@ +========= +**shutdown +```sh +shutdown # Schedule a shutdown +shutdown now # Shutdown immediately +shutdown 13:20 # Shutdown at 1:20 PM +shutdown -p now # Power off the machine +shutdown -H now # Halt the machine +shutdown -r 09:35 # Reboot at 9:35 AM + +shutdown -c # отменить +``` +- `-r`: Перезагрузка после выключения. +- `-h`: Остановка или выключение питания (решает система). +- `-H`: Остановите систему. +- `-P`: Выключите систему. +- `-c`: Отменить запущенное завершение работы. +- `-k`: Только отправлять предупреждающие сообщения (без выключения). + +**Примечание по исправлению** : распространенной ошибкой является использование `shutdown -p`, что недопустимо. Правильный вариант выключения — `shutdown -P` + +========= +**halt +```sh +halt # Остановить машину +halt -p # Выключить машину +halt --reboot # Перезагрузить машину +``` + +========= +**poweroff +```sh +poweroff # Выключить машину +poweroff --halt # Остановить машину +poweroff --reboot # Перезагрузить машину +``` + +========= +**reboot +```sh +reboot # Перезагрузить машину +reboot --halt # Остановить машину +reboot -p # Выключить машину +``` + +========= +**systemctl +```sh +sudo systemctl poweroff # Выключить систему +sudo systemctl reboot # Перезагрузить систему +sudo systemctl halt # Остановить систему +``` + +Эти альтернативы работают надежно и считаются лучшими в системах на базе systemd. + + diff --git a/LINUX/SSH.md b/LINUX/SSH.md new file mode 100644 index 0000000..a169457 --- /dev/null +++ b/LINUX/SSH.md @@ -0,0 +1,302 @@ +#### SSH alias !!! #### +```sh +ssh-keygen --help # -h + +vim ~/.ssh/config + +UseRoaming no + +Host +Hostname 178.20.46.157 # или vlapa.ru +User vlapa +Port 22 + +##########################: +# заходим на сервер +ssh newHostName +``` +#### Проброс портов SSH: #### +```sh +ssh -L 9000:localhost:8000 server2 + +ssh -L [локальный_адрес:]порт:удалённый_адрес:порт [user@]hostname + +# Если не указывать [локальный_адрес:], соединение по умолчанию будет привязано к интерфейсу localhost. +``` + +#### SSH #### +```sh +ssh-keygen -t rsa +cat ~/.ssh/id_rsa.pub + +ssh-keygen -t ed25519 +ssh-keygen -t ed25519 -f ~/.ssh/key2 -C "new key" + +# -сменить пароль на ключ +ssh-keygen -p + +# -открытый ключ. копируют на сервера +~/.ssh/id_rsa.pub + +# -закрытый ключ (у нас) +~/.ssh/id_rsa + + +~/.ssh/authorized_keys +chown -R vlapa:vlapa /home/vlapa/.ssh +# Права обязательно !!! +chmod 700 ~/.ssh +chmod 644 ~/.ssh/* + +chmod 600 authorized_keys + +ssh -i ~/.ssh/key2 vlapa@192.168.88.2 + +cd /etc/ssh +ll + +vim /etc/sshd_config + + Port 22 + PermitRootLogin no + PubkeyAuthentication yes + PasswordAuthentication no + +systemctl restart sshd +systemctl status sshd + +ssh -i ~/.ssh/key2 vlapa@192.168.88.2 + +vim .ssh/config + +Host \*.com + StrictHostKeyChecking no + User etoosamoe + ForwardAgent yes + IdentityFile /Users/username/.ssh/id_rsa + IdentitiesOnly yes + UserKnownHostsFile=/dev/null + UseKeychain yes + AddKeysToAgent yes + ServerAliveInterval 60 + ServerAliveCountMax 1200 +Host std-\* + StrictHostKeyChecking no + User ci-user + ForwardAgent yes + IdentityFile /Users/username/.ssh/id_rsa_second + IdentitiesOnly yes + UserKnownHostsFile=/dev/null + UseKeychain yes + AddKeysToAgent yes + ServerAliveInterval 60 + ServerAliveCountMax 1200 +``` + +```sh +# В каталоге пользователя, под которым вы хотите зайти, если создать файл и положить туда открытый ключ, то можно будет заходить без пароля. Обратите внимание, права на файл не должны давать возможность писать в этот файл посторонним пользователям (и группам !!!), иначе ssh его не примет. В ключе последнее поле — user@machine Оно не имеет никакого отношения к авторизации и служит только для удобства определения где чей ключ. Заметим, это поле может быть поменяно (или даже удалено) без нарушения структуры ключа. + +ssh-copy-id user@server +# позволяет скопировать ключ не редактируя файлы вручную. + +# Если у вас ssh на нестандартном порту, то ssh-copy-id требует особого ухищрения при работе: + +ssh-copy-id '-p 443 user@server' # внимание на кавычки ! + +# Первый раз, когда вы заходите на сервер, ssh вас спрашивает, доверяете ли вы ключу. Если отвечаете нет, соединение закрывается. Если да — ключ сохраняется в файл ~/.ssh/known_hosts Узнать, где какой ключ нельзя (ибо несекьюрно). + +# Удалить известный ключ сервера можно командой: +ssh-keygen -R server + +# При этом нужно удалить ещё и ключ IP (они хранятся раздельно): +ssh-keygen -R 127.0.0.1 + +# Ключ сервера хранится в +/etc/ssh/ssh_host_rsa_key + +/etc/ssh/ssh_host_rsa_key.pub + +# Их можно: +# а) скопировать со старого сервера на новый. +# б) сгенерировать с помощью ssh-keygen Пароля при этом задавать не надо (т.е. пустой). Ключ с паролем ssh-сервер использовать не сможет. + +# Заметим, если вы сервера клонируете (например, в виртуалках), то ssh-ключи сервера нужно обязательно перегенерировать. + +# Старые ключи из know_hosts при этом лучше убрать, иначе ssh будет ругаться на duplicate key + +sudo ssh service restart +sudo sshd service restart + +# Чтобы удаленно выполнить команду с локального компьютера, добавьте инструкцию к команде SSH. Например, чтобы удалить файл, введите: + +ssh test.server.com rm ~/Desktop/Dir1/sample4 + +# Введите пароль, и файл на удаленном сервере будет удален без создания новой оболочки. + +sudo systemctl restart ssh + +vim /etc/ssh/sshd_config + +- PasswordAuthentication no +- PermitRootLogin no +- AllowUsers www root + + +sudo service ssh restart + + +# Ключ игнорируется сервером + +# В редких случаях можно столкнуться с проблемой, что вы настроили все корректно, но не можете подключиться к серверу с использованием SSH-ключей. Видя следующий вывод, можно подумать, что сервер не видит ключ: + +Permission denied (publickey). + +# Вероятнее всего, сервер SSH считает, что установлены неподходящие разрешения к некоторым каталогам. Для решения этой проблемы следует изменить права доступа.  + +# Настройка удаленного узла + +# Полный доступ к папке .ssh только владельцу, остальным — полный запрет: + +chmod rwx------ ~/.ssh + +# Права на запись и чтение для файла authorized_keys только владельцу, остальным — полный запрет:  + +chmod rw------- ~/.ssh/authorized_keys + +# Отмена записи группой и остальными пользователями: + +chmod go-w ~/ + +# Настройка локальной машины + +# Полный доступ к папке .ssh только владельцу, остальным — полный запрет: + +chmod rwx------ ~/.ssh + +# Права на запись и чтение для файла ключа только владельцу, остальным — полный запрет: + +chmod rw------- ~/.ssh/*key* ( 600) + +# После применения этих политик ошибка должна исчезнуть. +``` + +=========================================== +### Как общаться с удаленным сервером через SSH + +`ssh-keygen -t rsa -b 4096 -f host_ca -C host_ca + +Файл `host_ca` является закрытым ключом центра сертификации хоста и должен храниться в защищенном месте. Не выдавайте его никому, не копируйте куда-либо и убедитесь, что как можно меньше людей имеют к нему доступ. В идеале он «живет» на машине, которая не допускает прямого доступа, и все сертификаты выдаются автоматически. + +Рекомендуется использовать два отдельных CA – один для подписания сертификатов хостов, другой для пользователей. Одни и те же процессы не будут добавлять и хосты, и юзеров. А если закрытый ключ скомпрометируют, нужно будет лишь пересоздать сертификаты для хостов или пользователей, а не оба сразу. + +Вот так создается `user_ca`: + +`$ ssh-keygen -t rsa -b 4096 -f user_ca -C user_ca` + +Файл `user_ca` – закрытый ключ пользователя, который должен быть защищен, как и закрытый ключ хоста. + +### Выдача сертификатов хостам + +Создадим новый ключ и подпишем с помощью центра сертификации. + +ssh_host_rsa_key-cert.pub содержит подписанный сертификат хоста. + +```sh +$ ssh-keygen -f ssh_host_rsa_key -N '' -b 4096 -t rsa + +$ ls -l +-rw------- 1 palpatine palpatine 3247 Mar 17 14:49 ssh_host_rsa_key +-rw-r--r-- 1 palpatine palpatine 764 Mar 17 14:49 ssh_host_rsa_key.pub + +$ ssh-keygen -s host_ca -I host.example.com -h -n host.example.com -V +52w ssh_host_rsa_key.pub +Enter passphrase: # the passphrase used for the host CA +Signed host key ssh_host_rsa_key-cert.pub: id "host.example.com" serial 0 for host.example.com valid from 2020-03-16T15:00:00 to 2021-03-15T15:01:37 + +$ ls -l +-rw------- 1 palpatine palpatine 3247 Mar 17 14:49 ssh_host_rsa_key +-rw-r--r-- 1 palpatine palpatine 2369 Mar 17 14:50 ssh_host_rsa_key-cert.pub +-rw-r--r-- 1 palpatine palpatine 764 Mar 17 14:49 ssh_host_rsa_key.pub +``` + +Вот описание используемых флагов: + +- **-s host_ca**: указывает имя файла закрытого CA-ключа, используемого для подписи. +- **-I host.example.com**: буквенно-цифровая строка, идентифицирующая сервер. При необходимости значение можно использовать для отзыва сертификата. + +- **-h**: указывает, что сертификат выдается хосту, а не юзеру. + +- **-n host.example.com**: указывает разделенный запятыми список участников, для проверки подлинности которых будет действителен сертификат. + +- **-V +52w**: срок действия сертификата. В данном случае 52 недели (один год). По умолчанию сертификаты действительны вечно. Указание периода истечения срока действия сертификатов хоста настоятельно рекомендуется для ротации и замены сертификатов. + +## Настройка SSH для использования сертификатов + +Нужно указать серверу, чтобы он использовал новый сертификат. Скопируйте три сгенерированных файла на сервер в каталог `/etc/ssh`, установите разрешения и добавьте в файл `/etc/ssh/sshd_config` следующую строку: + + `HostCertificate /etc/ssh/ssh_host_rsa_key-cert.pub` + + +Перезагрузите **sshd** с помощью `systemctl restart sshd`. Теперь сервер настроен на выдачу сертификата. Чтобы локальный ssh-клиент мог автоматически доверять хосту на основе удостоверения сертификата, нужно добавить открытый ключ CA в `known_hosts`. Вы можете сделать это, добавив в начало файла `host_ca.pub` следующее: `@cert-authority *.example.com`, а затем и в `~/.ssh/known_hosts`: + + `@cert-authority *.example.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDwiOso0Q4W+KKQ4OrZZ1o1X7g3yWcmAJtySILZSwo1GXBKgurV4jmmBN5RsHetl98QiJq64e8oKX1vGR251afalWu0w/iW9jL0isZrPrmDg/p6Cb6yKnreFEaDFocDhoiIcbUiImIWcp9PJXFOK1Lu8afdeKWJA2f6cC4lnAEq4sA/Phg4xfKMQZUFG5sQ/Gj1StjIXi2RYCQBHFDzzNm0Q5uB4hUsAYNqbnaiTI/pRtuknsgl97xK9P+rQiNfBfPQhsGeyJzT6Tup/KKlxarjkMOlFX2MUMaAj/cDrBSzvSrfOwzkqyzYGHzQhST/lWQZr4OddRszGPO4W5bRQzddUG8iC7M6U4llUxrb/H5QOkVyvnx4Dw76MA97tiZItSGzRPblU4S6HMmCVpZTwva4LLmMEEIk1lW5HcbB6AWAc0dFE0KBuusgJp9MlFkt7mZkSqnim8wdQApal+E3p13d0QZSH3b6eB3cbBcbpNmYqnmBFrNSKkEpQ8OwBnFvjjdYB7AXqQqrcqHUqfwkX8B27chDn2dwyWb3AdPMg1+j3wtVrwVqO9caeeQ1310CNHIFhIRTqnp2ECFGCCy+EDSFNZM+JStQoNO5rMOvZmecbp35XH/UJ5IHOkh9wE5TBYIeFRUYoc2jHNAuP2FM4LbEagGtP8L5gSCTXNRM1EX2gQ== host_ca` + + +Значение `*.example.com` указывает на то, что этому сертификату следует доверять для идентификации любого хоста, к которому вы подключаетесь и который имеет домен `*.example.com`. Это разделенный запятыми список имен хостов для сертификата: **host1**, **host2**, **host3** или **1.2.3.4**, **1.2.3.5** в зависимости от обстоятельств. + +Как только закончите настройку, удалите все старые записи для `host.example.com` в `~/.ssh/known_hosts` и запустите подключение. Правильность создания сертификата проверяется так: + + `$ ssh -vv host.example.com 2>&1 | grep "Server host certificate" debug1: Server host certificate: ssh-rsa-cert-v01@openssh.com SHA256:dWi6L8k3Jvf7NAtyzd9LmFuEkygWR69tZC1NaZJ3iF4, serial 0 ID "host.example.com" CA ssh-rsa SHA256:8gVhYAAW9r2BWBwh7uXsx2yHSCjY5OPo/X3erqQi6jg valid from 2020-03-17T11:49:00 to 2021-03-16T11:50:21 debug2: Server host certificate hostname: host.example.com` + + +На этом этапе вы можете продолжить выдачу сертификатов всех хостов, используя центр сертификации. Польза от этого следующая: вам больше не нужно полагаться на небезопасную модель **Trust On First Use (TOFU)** для новых хостов, и если вы когда-нибудь повторно развернете сервер и, следовательно, измените ключ хоста, новый хост автоматически представит подписанный сертификат без ужасного предупреждения: **WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!** + +## Выдача сертификатов пользователям (для аутентификации пользователей на хостах) + +В этом примере мы создадим новый ключ пользователя и подпишем с помощью центра сертификации. + + `$ ssh-keygen -f user-key -b 4096 -t rsa $ ls -l -rw-r--r--. 1 tatooine tatooine 737 Mar 19 16:33 user-key.pub -rw-------. 1 tatooine tatooine 3369 Mar 19 16:33 user-key $ ssh-keygen -s user_ca -I gus@gravitational.com -n ec2-user,gus -V +1d user-key.pub Enter passphrase: # the passphrase used for the user CA Signed user key user-key-cert.pub: id "gus@gravitational.com" serial 0 for ec2-user,gus valid from 2020-03-19T16:33:00 to 2020-03-20T16:34:54 $ ls -l -rw-------. 1 tatooine tatooine 3369 Mar 19 16:33 user-key -rw-r--r--. 1 tatooine tatooine 2534 Mar 19 16:34 user-key-cert.pub -rw-r--r--. 1 tatooine tatooine 737 Mar 19 16:33 user-key.pub` + + +`user-key-cert.pub` содержит подписанный сертификат пользователя. Для входа в систему понадобится как этот, так и приватный ключ. Вот описание используемых флагов: + +- **-s user_ca**: указывает приватный ключ CA, используемый для подписи. +- **-I tatooine@proglib.io**: буквенно-цифровая строка, которая будет видна в логе SSH. Для точной идентификации пользователя рекомендуем использовать адрес электронной почты или логин. + +- **-n palpatine, tatooine**: указывает разделенный запятыми список участников, для проверки подлинности которых будет действителен сертификат. В примере мы предоставляем сертификат доступа как для `palpatine`, так и для `tatooine`. + +- **-V +1d**: указывает срок действия сертификата, в данном случае +1d означает 1 день. + +Если нужно увидеть параметры подписи сертификата, используйте`ssh-keygen -L`: + + `$ ssh-keygen -L -f user-key-cert.pub user-key-cert.pub: Type: ssh-rsa-cert-v01@openssh.com user certificate Public key: RSA-CERT SHA256:egWNu5cUZaqwm76zoyTtktac2jxKktj30Oi/ydrOqZ8 Signing CA: RSA SHA256:tltbnMalWg+skhm+VlGLd2xHiVPozyuOPl34WypdEO0 (using ssh-rsa) Key ID: "palpatine@proglib.io" Serial: 0 Valid: from 2020-03-19T16:33:00 to 2020-03-20T16:34:54 Principals: tatooine palpatine Critical Options: (none) Extensions: permit-X11-forwarding permit-agent-forwarding permit-port-forwarding permit-pty permit-user-rc` + + +## Настройка SSH для проверки подлинности + +После того как сертификат подписан, нужно сообщить серверу, что можно доверять пользователским сертификатам. Копируем `user_ca.pub` в директорию `/etc/ssh`, поправляем права и добавляем в конфиг `/etc/ssh/sshd_config` строку: + + `TrustedUserCAKeys /etc/ssh/user_ca.pub` + + +Как раньше, перезагружаем **sshd** с помощью `systemctl restart sshd`. Теперь сервер настроен на пропуск всех, кто представляет сертификат, выданный центром сертификации при подключении. Если сертификат хранится в том же каталоге, что и ваш приватый ключ (указанный с флагом `-i`, например `ssh -i /home/tatooine/user-key` `palpatine @proglib.io`), он будет автоматически использоваться при подключении к серверам. + +## Проcмотр логов + +Если вы посмотрите в журнал sshd сервера, запустив `journalctl -u sshd`, то увидите имя используемого для аутентификации сертификата, а также данные центра сертификации: + + `sshd[14543]: Accepted publickey for tatooine from 1.2.3.4 port 53734 ssh2: RSA-CERT ID palpatine@proglib.io (serial 0) CA RSA SHA256:tltbnMalWg+skhm+VlGLd2xHiVPozyuOPl34WypdEO0` + + +Если пользователь попытается войти в систему под логином, у которого нет доступа, в логе вы увидите такую ошибку: + + `sshd[14612]: error: key_cert_check_authority: invalid certificate sshd[14612]: error: Certificate invalid: name is not a listed principal` + + +Если срок действия сертификата истек, вы увидите следующую ошибку: + + `sshd[14240]: error: key_cert_check_authority: invalid certificate sshd[14240]: error: Certificate invalid: expired` + + +В качестве домашнего задания напишите скрипт, который будет использовать центр сертификации для выдачи сертификатов, действительных в течение следующих двух часов. Каждое использование этого скрипта должно журналировать того, кто запросил сертификат и его почту, а потом встраивать этот email в сертификат. Это поможет в мониторинге. + +Взгляните на `man ssh-keygen` для новых идей. \ No newline at end of file diff --git a/LINUX/SYSTEMCTL.md b/LINUX/SYSTEMCTL.md new file mode 100644 index 0000000..e550eed --- /dev/null +++ b/LINUX/SYSTEMCTL.md @@ -0,0 +1,405 @@ +Почему стоит перейти на ==`systemd`== вместо скриптов: +- Более надёжная демонизация. +- Автоматический перезапуск при падении. +- Логи сохраняются через journalctl. +- Возможность ограничения ресурсов (CPU, память). +- Красивое управление (`start`, stop, restart, `status`). + +#### ▪️ Правильный systemd-юнит для сервиса: +vim /etc/systemd/system/myapp.service: +```sh +[Unit] +Description=MyApp Server +After=network.target + +[Service] +Type=simple +ExecStart=/opt/myapp/server +Restart=always +RestartSec=5 +WorkingDirectory=/opt/myapp/ +StandardOutput=append:/var/log/myapp/server.log # Каталог и файл должны существовать!!! +StandardError=append:/var/log/myapp/server_error.log # Каталог и файл должны существовать!!! +User=appuser +Group=appuser +Environment=ENV=production + +[Install] +WantedBy=multi-user.target +``` +▪️ Что важно здесь: +- Restart=always — перезапуск при сбоях. +- StandardOutput и StandardError перенаправляют логи прямо в файлы. +- User и Group указывают, под кем должен работать процесс. +- WorkingDirectory задаёт рабочую папку для приложения. + +▪️ Как активировать сервис: +```sh +sudo systemctl daemon-reload +sudo systemctl enable myapp +sudo systemctl start myapp +sudo systemctl status myapp +``` +▪️ Теперь управление сервисом становится надёжным и простым. +▪️ Дополнительные вопросы на собеседовании: +▪️ Вывод: +Переход от скриптов к systemd — это шаг к профессиональной эксплуатации Linux-приложений.   +Меньше ручных ошибок, лучшее управление процессами, больше надёжности. +```sh +# Проверить, включен ли автозапуск сервиса +systemctl is-enabled your_service.service + +# Посмотреть статус сервиса (включая последние ошибки) +systemctl status your_service.service + +# Включить автозапуск +sudo systemctl enable your_service.service + +# Логи сервиса с момента последнего запуска +journalctl -u your_service.service -b + +# Показать ошибки systemd на загрузке +systemd-analyze blame +``` +🟢 systemctl — управление сервисами и проверка их состояния +🟢 journalctl — просмотр логов systemd +🟢 systemd-analyze помогает понять, что тормозит загрузку системы. + +#======================== +_bot.service_ +```sh +# Создаем НОВУЮ службу или редактируем первоисточник: +sudo systemctl edit --force --full bot + +# или редактируем уже имеющуюся (открывается пустой файл +# для добавления или исправления): +sudo systemctl edit bot + +# Все сервисы находятся в: +cd /etc/systemd/system/ +#============================ +``` + +```shell +[Unit] +Description=bot +After=network.target # только после запуска сети +# After=multi-user.target + +[Service] +Type=simple +ExecStart=python3 /home/vlapa/folder/bot.py +WorkingDirectory=/home/vlapa +Restart=always # on-failure +#---------------------------- +User=root +Group=root +#---------------------------- + +[Install] +WantedBy=multi-user.target # default.target +``` + +```sh +# перезагрузка всех служб: +sudo systemctl daemon-reload + +# включить в автозагрузку: +sudo systemctl enable bot.service + +# перезапуск с перечитыванием конфигов +systemctl restart bot.service + +# перезапуск без перечитывания конфигов +systemctl reload bot.service + +# включить в автозагрузку +systemctl enable bot.service + +# выключить из автозагрузки +systemctl disable bot.service + +# проверка включена или нет в автозагрузку +systemctl is-enabled ssh + +# Все модули: +systemctl list-units --all +systemctl list-unit-files --type=service "ssh*" + +# Оптимизация загрузки: +systemd-analyze blame + +``` + +```sh +# Создание исполняемого скрипта: +#!/usr/bin/env python3 +chmod +x my_script.py + +# Запуск скрипта: +./my_script.py +``` + +=============================== +Все модули можно найти в трёх каталогах: + +`/usr/lib/systemd/system` +— юниты сервисов, установленных с помощью менеджера пакетов. Самый простой пример — веб-серверы: Apache или Nginx. +`/run/systemd/system` +— юниты, которые создаются в процессе работы системы. +`/etc/systemd/system` +— юниты, которые создаёт администратор системы. + +Список всех запущенных модулей можно посмотреть, использовуя команду `systemctl`. В терминале вы увидите таблицу со статусом каждой службы. Разберём, что означает каждый из столбцов: + +1. **UNIT**. Название модуля +2. **LOAD**. Статус загрузки конфигурации, если успешно — loaded. +3. **ACTIVE**. Статус службы. Один из следующих: _active, inactive, running, exited, dead, loaded, not-found, plugged, mounted, waiting, listening._ +4. **SUB**. Детальная информация о юните. +5. **DESCRIPTION**. Краткое описание модуля — за что он отвечает. + +Чтобы увидеть все модули, независимо от того, запущены они или нет, воспользуйтесь командой `systemctl list-units --all`. Вы увидите все модули, которые когда-либо загружал или пытался загрузить диспетчер systemd. Для отображения конкретных записей используйте флаг `--state`. Так, например, для того, чтобы увидеть все неактивные юниты, нужно использовать: + +`sudo systemctl list-units --all --state=inactive` `` + +### Типы юнитов + +Модули делятся на категории по типу. Тип каждого модуля можно узнать из названия файла — он указан через точку. Все типы модулей: + +1. .service — описывает, как управлять службой и приложением +2. .socket — описывает сетевой буфер, который используется для активации сокета +3. .device — описывает устройство как необходимое для управления systemd +4. .mount — определяет точку монтирования в системе +5. .automount — настраивает автоматическую установку точки монтирования +6. .swap — описывает пространство подкачки в системе +7. .target — обеспечивает синхронизацию устройств при загрузке системы +8. .path — определяет путь, который используется для активации +9. .timer — определяет задержку или активацию по плану +10. .snapshot — делает снимок системы для последующего восстановления +11. .slice — ограничивает узлы группы управления +12. .scope — systemd создаёт эти модули автоматически, пользуясь информацией из интерфейса шины + +```sh +[Unit] +Description=OpenSSH server daemon +Documentation=man:sshd(8) man:sshd_config(5) +After=network.target sshd-keygen.target +Wants=sshd-keygen.target + +[Service] +Type=notify Environment +File=-/etc/crypto-policies/back-ends/opensshserver.config EnvironmentFile=-/etc/sysconfig/sshd +ExecStart=/usr/sbin/sshd -D $OPTIONS $CRYPTO_POLICY +ExecReload=/bin/kill -HUP $MAINPID +KillMode=process +Restart=on-failure +RestartSec=42s + +[Install] +WantedBy=multi-user.target +``` + +**Unit** + +Это первая обязательная секция. В ней описаны правила взаимодействия с другими службами и указаны метаданные — описание и документация. Подробнее: + +- Description — описание модуля. +- Documentation — страница руководства man. +- After — после каких демонов и событий нужно запускать модуль. Например, модули веб-серверов запускаются только после сетевых интерфейсов. +- Requires — сервисы, необходимые для работы юнита +- Wants — сервисы, которые желательно запустить перед стартом модуля + +**Service** + +В этой секции необходимо указать, какими командами запускать службу: + +- Type — как запускается демон? По умолчанию — simple, то есть служба просто запускается. Notify это примерно то же самое, но в таком случае процесс сообщит диспетчеру, что он готов к работе. Есть также типы forking (после запуска демона родительский процесс завершается) и one-shot (выполняется один раз). +- PIDFile — ссылка на основной процесс +- WorkingDirectory — текущая рабочая директория для запуска команд +- User — пользователь для запуска сервиса +- Group — группа для запуска сервиса +- ExecStart — команда для запуска сервиса +- ExecStop — команда для остановки сервиса +- TimeoutSec — время, которое служба ожидает остановки или старта +- Restart — настройки перезапуска + +**Install** + +В этой секции нужно указать, на каком уровне загрузки системы нужно запускать сервис. Переменная WantedBy сообщает, как включится устройство, в качестве параметра указываются специальные файлы целей. Они служат для того, чтобы привести систему в разные состояния.  + +Список доступных целей вы можете получить, выполнив команду  + +`sudo systemctl list-unit-files --type=target` + + +### Редактирование модулей + +Модули нельзя править напрямую. Для этого нужно использовать команду  + +`systemctl edit` + +Откроется файл, который можно использовать для переопределения директив службы или добавления новых. При этом настройки из файла переопределения приоритетнее, чем настройки по умолчанию.  + +Если нужно отредактировать файл модуля целиком, а не добавлять новые директивы, нужно использовать флаг `--full`: + +`sudo systemctl edit --full apache2.service` `` + +Перед вами откроется редактор, в котором вы можете править настройки юнита.  + +По умолчанию файлы переопределения хранятся в  + +`/etc/systemd/system/` + +в каталоге который называется так же, как модуль, но с суффиксом `.d`. Например, в случае apache2 это будет директория  + +`/etc/systemd/system/apache2.service.d/`.  + +Если вы редактируете конфигурацию напрямую с использованием флага `--full`, нужный файл находится в каталоге  + +`/etc/systemd/system/` + +и называется так же, как модуль. + +Для того, чтобы удалить переопределения, которые вы добавили, просто удалите каталог с суффиксом `.d`. Если нужно восстановить настройки по умолчанию, удалите весь файл модуля. В случае с apache2 это: + +`sudo rm -r /etc/systemd/system/apache2.service.d` `` +или  +`sudo rm /etc/systemd/system/apache2.service` `` + +После этого нужно перезагрузить процесс systemd: + +`sudo systemctl daemon-reload` ` + +=============================== + +## Создание сервиса + +В подсистеме systemd также можно легко создать собственную службу и использовать ее для автозапуска приложений или собственных скриптов. Для этого в каталоге **/usr/lib/systemd/system** создаем юнит (файл) с расширением **.service**. + +Пример простой и частоиспользуемой конфигурации: + +[Unit] +Description=Service Name +After=network.target + +[Service] +User=root +Group=root +Type=simple + +ExecStart=/usr/bin/myapp +ExecReload=/bin/kill -HUP $MAINPID +Restart=on-failure + +[Install] +WantedBy=multi-user.target + +* как правило, файл разделен на 3 части: + +- **Unit** — позволяет определить метаданные для юнита. +- **Service** — раздел для основной конфигурации юнита. +- **Install** — определение поведения для юнита при его включении или отключении. + +Подробнее можно почитать о структуре и возможных опциях на странице [https://linux-notes.org/pishem-systemd-unit-fajl/](https://linux-notes.org/pishem-systemd-unit-fajl/) + +Более сложный вариант разберем на примере сервиса bind: + +vi /usr/lib/systemd/system/named.service + +Содержимое может быть следующего содержания: + +[Unit] +Description=Berkeley Internet Name Domain (DNS) +Wants=nss-lookup.target +Wants=named-setup-rndc.service +Before=nss-lookup.target +After=network.target +After=named-setup-rndc.service + +[Service] +Type=forking +Environment=NAMEDCONF=/etc/named.conf +EnvironmentFile=-/etc/sysconfig/named +Environment=KRB5_KTNAME=/etc/named.keytab +PIDFile=/run/named/named.pid +ExecStartPre=/bin/bash -c 'if [ ! "$DISABLE_ZONE_CHECKING" == "yes" ]; then /usr/sbin/named-checkconf -z "$NAMEDCONF"; else echo "Checking of zone files is disabled"; fi' +ExecStart=/usr/sbin/named -u named -c ${NAMEDCONF} $OPTIONS +ExecReload=/bin/sh -c '/usr/sbin/rndc reload > /dev/null 2>&1 || /bin/kill -HUP $MAINPID' +ExecStop=/bin/sh -c '/usr/sbin/rndc stop > /dev/null 2>&1 || /bin/kill -TERM $MAINPID' +PrivateTmp=true + +[Install] +WantedBy=multi-user.target + +После внесения изменений и сохранения файла, необходимо перечитать изменения командой: + +systemctl daemon-reload + +Теперь можно разрешить автозапуск: + +systemctl enable named + +### Редактирование сервисов + +Если мы хотим внести изменения в юнит-файл сервиса, который был установлен с последним, необходимо использовать drop-in файл или файл переопределения настроек. В противном случае, после обновления программы наши изменения могут быть удалены. + +И так, мы для примера взяли юнит для bind. Чтобы создать для него drop-in файл, вводим: + +systemctl edit named + +И вносим, например, такие изменения: + +[Service] +Restart=on-failure + +_* будет создан файл **/etc/systemd/system/named.service.d/override.conf**, который будет переопределять настройки основного юнит-файла. В данном примере, мы указываем на необходимость перезапуска сервиса при сбое._ + +Чтобы убедиться в использовании Drop-In файла смотрим статус сервиса: + +systemctl status named + +Мы должны увидеть что-то на подобие: + +Drop-In: /etc/systemd/system/named.service.d +           — override.conf + +Также мы можем редаетировать файл юните без drop-in. Для этого добавляем опцию full: + +systemctl edit --full named + +Однако, как было сказано выше, при очередном обновлении приложения, если разработчик пакета предоставляет файл systemd наши изменения могут быть перетерты. + +## Таймеры + +Выше мы рассмотрели возможность автоматического запуска сервисов с помощью systemd. Мы также можем настроить периодический запуск данных юнитов по таймеру. Для этого нам нужно создать юнит с таким же названием, но на конце должен быть суффикс ==.timer==. + +Предположим, мы хотим создать запуск по таймеру named, для которого выше в примере создали юнит сервиса. Тогда создаем файл: + +```sh +vi /usr/lib/systemd/system/named.timer + +[Unit] +Description=Run named every 10 min + +[Timer] +OnBootSec=5min +OnUnitActiveSec=10min + +[Install] +WantedBy=timers.target + +# в данном примере наш таймер будет создан для юнита named; он запустится через 5 минут после старта службы и будет запускать ее каждые 10 минут + +# Перечитаем изменения в systemd: +systemctl daemon-reload + +# Разрешаем наш таймер: +systemctl enable named.timer + +# Запустим его: +systemctl start named.timer + +# Вывести список таймеров можно командой: +systemctl list-timers +``` + diff --git a/LINUX/TAR.md b/LINUX/TAR.md new file mode 100644 index 0000000..7bb98c5 --- /dev/null +++ b/LINUX/TAR.md @@ -0,0 +1,314 @@ +1️⃣ **Распаковка tar** +Для распаковки архива в формате **.tar** в текущую директорию выполните команду: + +`tar xvf archive.tar + +2️⃣ **Распаковка tar.bz2, tar.bzip2, tbz2, tb2, tbz** + +`tar xvjf archive.tar.bz2 + +3️⃣ **Распаковка tar.xz, txz** + +`tar xvJf archive.tar.xz + +4️⃣ **Распаковка tar.gz, tgz** + +`tar xvzf archive.tar.gz + +5️⃣ **Распаковка tar.lzma** + +`tar --lzma -xvf archive.tar.lzma + +🤔 **Пояснение опций** + +x — распаковать архив. +v — Verbose-режим (вывод на экран дополнительной информации во время распаковки). +f — выполнить распаковку архива из файла. +j — вызвать bzip2 для распаковки архива. +z — вызвать gzip +J — вызвать xz + +Чтобы распаковать архив в определенную директорию используется опция -C или --directory, например: + +`tar xvf archive.tar -C /to/directory + +===================================== + ![Обновлено](https://www.dmosk.ru/img/public/time.jpg "Дата обновления") Обновлено: 24.07.2024  ![Опубликовано](https://www.dmosk.ru/img/public/time.jpg "Дата публикации") Опубликовано: 18.11.2016 +### Создать + +Создание выполняется с ключом **c**. Синтаксис следующий: + +tar -c<опции> <новый архивный файл> <что сжимаем> + +Пример создания: + +tar -czvf archive.tar.gz /home/dmosk + +_* в данном примере будет создан архив **archive.tar.gz** домашней директории пользователя (**/home/dmosk**) +** где **z** — сжать архив в gzip (без этого параметра, tar не сжимает, а создает так называемый тарбол); **c** — ключ на создание архива; **v** — verbose режим, то есть с выводом на экран процесса (очень удобно для наблюдением за ходом работы, но в скриптах можно упустить); **f** — использовать файл (обязательно указываем, так как в большей степени работаем именно с файлами)._ + +### Распаковать + +Распаковка выполняется с ключом **x** с синтаксисом: + +tar -x<опции> <архивный файл>  + +Пример распаковки: + +tar -xvf archive.tar.gz  + +## Форматы + +Утилита tar позволяет работать с архивами различных форматов. Рассмотрим их кратко. + +### gz + +Сжатие: + +tar -cf archive.tar.gz /files + +Распаковка .gz файла: + +tar -xf archive.tar.gz  + +_* при возникновении ошибки **This does not look like a tar archive**, можно воспользоваться командой **gzip -d archive.tar.gz**._ + +### bz2 + +tar -xvjf archive.tar.bz2 + +_* ключ j отвечает за работу с bz2._ + +Если система ругается на bzip2, значит нужно его установить: + +apt-get install bzip2 + +Если видим ошибку **tar: Unrecognized archive format**, воспользуемся следующей командой: + +bzip2 -d archive.tar.bz2 + +### gzip + +tar -xvzf archive.tar.gzip + +_* ключ z отвечает за работу с gzip._ + +### tgz + +TGZ — tar-архив, сжатый с помощью утилиты gzip. По сути, это тот же tar.gz, и работа с ним ведется такими же командами. Рассмотрим примеры работы с данным форматом. + +Для создания архива tgz выполняем: + +tar -czf archive.tgz /files + +Распаковывается, как gzip: + +tar -xzf archive.tgz + +### xz + +Это формат сжатия данных с помощью алгоритма LZMA. Для работы с ним устанавливаем: + +apt install xz-utils + +_* на Debian / Ubuntu._  + +yum install xz + +_* на Rocky Linux / CentOS._ + +Теперь можно пользоваться. + +Создать архив: + +tar -cpJf archive.tar.xz /home/user + +Распаковка: + +tar -xpJf archive.tar.xz + +## Описание ключей tar + +### Команды для действия + +|Ключ|Описание| +|---|---| +|-A|Добавление файлов в архив.| +|-c|Создание нового архивного файла.| +|-d|Показать отличающиеся данные между каталогом-исходником и содержимым архива.| +|--delete|Удалить файлы внутри архива.| +|-r|Добавить файлы в конец архива.| +|-t|Показать содержимое архива.| +|-u|Добавить файлы, которых нет в архиве.| +|-x|Извлечь файлы из архива.| + +_* нельзя использовать несколько вышеперечисленных ключей в одной команде._ + +### Дополнительные опции + +|Ключ|Описание| +|---|---| +|--atime-preserve|Оставить прежнюю метку времени доступа для файла.| +|-b N|Задать размер блока N x 512.| +|-C|Смена каталога. По умолчанию, используется тот, в котором мы находимся.| +|--checkpoint|Показать имена папок при чтении архивного файла.| +|-G|Использование старого формата инкрементального резервирования при отображении или извлечения.| +|-g|Использование нового формата инкрементального резервирования при отображении или извлечения.| +|-h|Не дублировать символьные ссылки. Только файлы, на которые указывают эти симлинки.| +|-i|Игнорировать блоки нулей.| +|-j|Использование bzip2.| +|--ignore-failed-read|Игнорировать не читаемые файлы.| +|-k|При распаковке, существующие файлы не заменяются соответствующими файлами из архива.| +|-L N|Смена магнитной ленты после N*1024 байт.| +|-m|При извлечении игнорировать время модификации объекта.| +|-M|Многотомные архивы.| +|-N DATE|Сохранять только более новые файлы относительно DATE| +|-O|Направление извлекаемого на стандартный вывод.| +|-p|Извлечение защищенной информации.| +|-P|Не отбрасывает начальный слэш (/) из имен.| +|-s|Сортировка файлов при извлечении.| +|--preserve|Аналогично -ps| +|--remove-files|Удалить исходные файлы после добавления в архив.| +|--same-owner|Сохранить владельца при извлечении.| +|--totals|Вывод байт при создании архива.| +|-v|Протоколирование действий — отображение списка объектов, над которыми происходит действие.| +|-V NAME|Создание архива на томе с меткой NAME.| +|--version|Показать версию tar.| +|-w|Требовать подтверждения для каждого действия.| +|-W|Проверка архива после записи.| +|--exclude FILE|Исключить файл FILE.| +|-X FILE|Исключить файлы FILE.| +|-Z|Фильтрует архив с помощью compress.| +|-z|Использование gzip.| + +* актуальный список опций можно получить командой **man tar**. + +## Примеры + +Рассмотрим некоторые сценарии использования tar. + +### Распаковать в определенную папку + +tar -C /home/user -xvf archive.tar.gz + +_* ключ **-C** используется для указания папки, куда необходимо распаковать файлы из архива._ + +### Распаковка без вложенной папки + +Такой способ можно использовать для распаковки в заранее подготовленный каталог. Будет некий эффект переименовывания каталога или аналог «Распаковать здесь»: + +tar -C /home/admin/mytar -xvf admin.tar.gz --strip-components 1 + +_* каталог **/home/admin/mytar** заранее должен быть создан; **--strip-components 1** пропустит одну вложенную папку внутри архива._ + +### Указать конкретный файл или каталог для распаковки + +Если у нас большой архив, но извлечь нужно конкретный файл или каталог, то можно указать его через пробел после указания файла с архивом. Синтаксис: + +tar zxf <путь до архива> <что извлечь> + +Рассмотрим пример. Напомним, что можно посмотреть содержимое архива с помощью опции t: + +tar tf /backup/samba.tar.gz + +Теперь можно распаковать архив: + +tar -zxf /backup/samba.tar.gz mail + +_* предположим, что внутри архива **samba.tar.gz** есть что-то и каталог **mail**. Извлечем мы только последний._ + +### Исключение файлов по маске + +Если необходимо пропустить некоторые файлы, вводим команду с ключом --exclude: + +tar --exclude='sess_*' -czvf archive.tar.gz /wwwsite + +_* в данном примере мы создадим архив **archive.tar.gz**, в котором не будет файлов, начинающихся на **sess_**._ + +Также можно исключить несколько файлов или папок, добавляя несколько опций exclude: + +tar --exclude='/data/recycle' --exclude='*.tmp' -zcf /backup/samba/2021-08-29.tar.gz /data/ + +_* в данном примере мы исключим папку **recycle** и файлы, которые заканчиваются на **.tmp**_ + +### Работа с архивами, разбитыми на части + +Разбить архив на части может понадобиться по разным причинам — нехватка места на носителе, необходимость отправки файлов по почте и так далее. + +Чтобы создать архив, разбитый на части, вводим команду: + +tar -zcvf - /root | split -b 100M - root_home.tar.gz + +_* данная команда создаст архив каталога **/root** и разобьет его на части по **100 Мб**._ + +> Если у нас уже есть архив, который нужно разбить, команда будет такой: +> +> split -b 100M root_home.tar.gz root_home.tar.gz +> +> _* в данном примере архив **root_home.tar.gz** будет разбит на части, состояние из файлов по 100 Мб._ + +В итоге мы получим, примерно, такую картину: + +root_home.tar.gzaa  root_home.tar.gzac  root_home.tar.gzae +root_home.tar.gzag  root_home.tar.gzai  root_home.tar.gzab +root_home.tar.gzad  root_home.tar.gzaf  root_home.tar.gzah + +Чтобы собрать архив и разархивировать его, вводим команду: + +cat root_home.tar.gz* | tar -zxv + +> Если мы хотим только собрать разбитый архив, вводим: +> +> cat root_home.tar.gz* > root_home.tar.gz + +### Создание tar из отдельных файлов с последующим его архивированием + +Мы можем собрать файл tar, добавляя по очереди в него файлы, после чего создать сжатый архив. Предположим, у нас 3 файла: file1, file2, file3. + +Сначала создадим tar-файл с первым файлом внутри: + +tar -cf ./my_archive.tar ./file1 + +Следующими двумя командами мы добавим в архив файлы file2 и file3: + +tar -rf ./my_archive.tar ./file2 + +tar -rf ./my_archive.tar ./file3 + +Сожмем содержимое tar-файла: + +gzip < ./my_archive.tar > ./my_archive.tar.gz + +Файл tar можно удалить: + +rm -f my_archive.tar + +### Рекурсивная распаковка + +Предположим, что у нас есть архив, в котором есть еще архив. И еще и еще много раз. Чтобы не заниматься ручной распаковкой, можно использовать команду с while: + +while [ -f *.tar.gz ]; do filename="$(ls -1 *.tar.gz | head -n 1)"; tar -xvf $filename && rm $filename; done; + +Обратите внимание, что данная команда удалит исходный файл архива, а также будет искать архив по маске, поэтому в каталог, где выполняем команду не стоит копировать другие архивы. + +## ZIP + +Tar не работает с zip-архивами. В системах UNIX для этого используем утилиты zip и unzip. Для начала, ставим нужные пакеты: + +yum install zip unzip + +apt-get install zip unzip + +pkg install zip unzip + +_* соответственно, для RPM based, deb based, BSD based._ + +Создание ZIP: + +zip -r archive.zip /home/dmosk + +_* создает архив каталога **/home/dmosk** в файл **archive.zip**._ + +Распаковка ZIP: + +unzip archive.zip \ No newline at end of file diff --git a/LINUX/TERMINAL.md b/LINUX/TERMINAL.md new file mode 100644 index 0000000..d71aaa4 --- /dev/null +++ b/LINUX/TERMINAL.md @@ -0,0 +1,207 @@ + +### Смена редактора по умолчанию: +```sh +which vim +export EDITOR=/usr/bin/vim +``` +### Ускорить работу в терминале + +Используй !$ — это магия последнего аргумента предыдущей команды. + +`mkdir new_project +`cd !$ + +📌 Ещё пример: + +`curl https://example.com/file.zip +`wget !$ + +🔁 Работает с vim, cp, mv, tar и др. +💡 Экономит много времени! + + +### Горячие клавиши в терминале + +🌟 Ctrl + A — переход в начало строки +🌟Ctrl + E — переход в конец строки +🌟Ctrl + U — удаление от курсора до начала строки +🌟 Ctrl + K — удаление от курсора до конца строки +🌟Ctrl + W — удаление слова перед курсором +🌟Ctrl + L — очистить экран терминала +🌟Ctrl + C — остановить текущий процесс/команду +🌟 Ctrl + D — выход из системы +🌟 Ctrl + Z — поставить текущий процесс на паузу (в отличии от Ctrl + C, данный процесс можно будет продолжить)  +🌟 Ctrl + R — поиск по истории команд +🌟 Стрелка вверх — показать предыдущую команду +🌟 Стрелка вниз — показать следующую команду +🌟 !! — повторить последнюю команду +🌟 !n — повторить n команду +🌟 Tab — автозаполнение команд, файлов и каталогов +🌟 Ctrl + Shift + C — копировать +🌟 Ctrl + Shift + V — вставить +🌟 Ctrl + Shift + N — открыть новое окно терминала +🌟 Ctrl + Shift + T — открыть новую вкладку в терминале +🌟 Ctrl + Tab — переключение между вкладками терминала + +## Управление окном эмулятора терминала + +Указанные ниже сочетания клавиш упростят вашу работу с терминалом GNOME и большинством других приложений эмуляторов терминала. + +- **Ctrl + Shift + Q** — закроет текущее окно терминала. +- **Ctrl + Shift + T** — откроет дополнительную вкладку терминала. +- **Ctrl + Shift + W** — закроет текущую вкладку. +- **Ctrl + Shift + D** — превратит вкладку в отдельное окно. +- **Ctrl + PgUp** — переместит на предыдущую вкладку. +- **Ctrl + PgDown** — переместит на следующую вкладку. + +## Переключение между tty-сессиями в терминале. + +В терминале Linux есть возможность переключаться между разными сессиями терминала, своеобразными «рабочими столами» в контексте командной строки. Для переключения между разными сессиями, например, когда кажется, что терминал завис, нажмите комбинацию: **Ctrl + Alt + f2**. Вы можете переключаться между шестью разными сессиями. Для переключения назад на первую tty-сессию терминала нажмите **Ctrl + Alt + f1**. + +Нажатие комбинации **Ctrl + Alt + f7**, в свою очередь, управляет отображением графической оболочки ОС. + +## Управление вводом: автодополнение команд и исправление опечаток + +### TAB + +Клавиша TAB — самая частая и основная в использовании среди всех горячих клавиш Linux. Данная клавиша поможет вам дополнить команду или путь к файлу или предложит варианты, если их несколько. +Для использования данного функционала начните набирать команду и нажмите TAB после введения нескольких символов. + +- Одно нажатие — дополняет команду. +- Два нажатия — предлагает несколько вариантов, если введенный текст имеет несколько вариантов продолжения. Например, двойное нажатие **Tab** поможет написать длинный путь к нужному файлу или директории. + +### Ctrl + Shift + «-» + +Сочетание клавиш **Ctrl Shift** и знака «минус» поможет вам отменить последний ввод в терминал. Команда очень полезна, если вы выбрали не тот вариант из меню предложенных в автодополнении или сделали опечатку. + +### Ctrl + T + +Это сочетание клавиш меняет местами два соседних символа и может быть очень полезна, если вы опечатались и написали слово неправильно. Например, используя данное сочетание клавиш, можно с легкостью исправить **selectle** на правильный вариант — **selectel**. + +### Alt + T + +Это сочетание клавиш уже посерьезнее, чем предыдущее и меняет местами целые слова. Например, с помощью этого сочетания, можно парой нажатий исправить **I Selectel Love** на правильный вариант — **I Love Selectel**. + +### Alt + Backspace и Alt + D + +Эти два сочетания помогут быстро удалить ненужные части введенных ранее команд. + +- **Alt + Backspace** поможет удалить слово, стоящее перед курсором (слева). +- **Alt + D удаляет** все символы, которые находится справа от курсора в текущей строке. + +## Управление курсором + +В какой-то момент стрелочек на клавиатуре будет недостаточно для быстрого перемещения по тексту, и вам понадобятся новые комбинации. Они позволят быстрее ориентироваться в тексте и набирать команды. + +- **Ctrl+A (Home)** совершит быстрый переход в начало строки. +- **Ctrl+E (End)** переместит курсор в конец строки текста. +- **Alt+B** переместит курсор назад (влево) на одно слово. +- **Ctrl+B** переместит курсор назад (влево) на один символ. +- **Alt+F** сдвинет курсор на слово вправо. +- **Ctrl+F** сдвинет курсор на слово вправо. +- **Ctrl+XX** мгновенно переместит курсор в начало текущей строки, выделив весь текст на своем пути. С помощью этого сочетания клавиш можно выделить строку или исправить что-то в ее начале. Второе нажатие комбинации переместит позицию курсора назад. + +## Работа с буфером обмена + +В терминале также есть возможность пользоваться буфером обмена: копировать в него текст, вырезать и вставлять в строку или в файл, открытый в терминале. + +### Ctrl + W + +Это сочетание клавиш поможет вырезать стоящее перед курсором слово и скопировать его в буфер обмена. + +### Ctrl + U + +Это сочетание вырежет и скопирует в буфер обмена текущую строку. + +### Ctrl + K + +Сочетание клавиш **Ctrl и K** вырежет строку, находящуюся справа от курсора. + +### Shift + Insert + +Данное сочетание клавиш нужно для того, чтобы вставить в терминал текст, находящийся в буфере обмена. + +### Ctrl + Y + +Вставит в поле ввода последний скопированный или вырезанный в буфер обмена текст. + +## Форматирование текста в терминале + +Следующий набор горячих клавиш сильно упростит работу с регистром символов, введенных в терминал. В Linux и UNIX-системах регистр играет важную роль, поэтому данные команды также будут очень полезны. + +### Alt + U + +После того, как вы нажмете это сочетание клавиш, все символы справа от курсора и до конца текущего слова сменят регистр на верхний. + +### Alt + L + +Сочетание **Alt + L** работает наоборот: все символы справа от курсора и до конца текущего слова сменят регистр на нижний. + +### Alt + C + +При активации этого сочетания клавиш символ, находящийся под курсором, изменит регистр с нижнего на верхний, а курсор переместится в конец слова. + +## Работа с историей bash + +Терминал хранит историю выполненных пользователем команд. Если вы хотите ее посмотреть, выполните следующую команду: + + `history` + +Эта команда не очень удобна, когда нужно быстро вспомнить ранее введенную команду и выполнить ее в командной оболочке. В этом нам помогут сочетания клавиш. + +### Ctrl + P + +Данное сочетание клавиш заменяет стрелку «вверх». Используйте его, чтобы переместиться к команде, выполненной ранее. Вы можете использовать данное сочетание неограниченное количество раз, каждый раз перемещаясь к более ранней команде. + +### **Ctrl + N** + +Это сочетание клавиш, в свою очередь, является альтернативой стрелке «вниз» и нужно, чтобы переместиться по истории в обратную сторону. + +### Ctrl + R + +После нажатия этого сочетания клавиш в терминале появится строка поиска, которая поможет найти и выполнить команду из истории команд, ранее запущенных через терминал. Для выполнения предложенной команды, просто нажмите **Enter,** а для редактирования предложенной команды или выхода из поиска — **Esc**. + +- **Ctrl + 0** сразу выполнит найденную команду без изменений. +- **Ctrl + G** завершит работу с инструментом поиска по истории без выполнения найденной команды. + +### Alt + R + +Если вы нашли в истории команду, уже отредактировали и хотите отменить изменения, нажмите это сочетание клавиш. Команда вернется к первоначальному виду. + +## Управление процессами + +В терминале Linux также есть горячие клавиши для легкого управления запущенными в терминале процессами. + +### Ctrl + C + +Данное сочетание клавиш необходимо знать любому пользователю терминала. С его помощью можно завершить процесс, выполняющийся на данный момент в терминале. Это сочетание клавиш мгновенно отправит программе сигнал **SIGINT**, и если программа не завершит работу должным образом, он будет принудительно остановлен. Иными словами, эта комбинация убивает текущий процесс. + +### Ctrl + D + +Сочетание **Ctrl + D** завершает работу терминала, посылая в терминал запрос EOF или «конец файла». После получения данного запроса, работа терминала завершается. Если у вас в терминале открыто активное ssh подключение, то комбинация **Ctrl + D** разъединит вас с удаленным хостом. Это сочетание клавиш является альтернативой команды **exit**. + +### Ctrl + Z + +Сочетание **Ctrl + Z** пошлет текущему процессу запрос **SIGSTP** и приостановит его выполнение до востребования. Запустить приостановленный процесс можно с помощью следующей команды: + +`fg` + +Посмотреть список приостановленных процессов вы можете, выполнив команду: + +`jobs` + +## Сочетания клавиш для управления отображением + +При помощи сочетаний клавиш, приведенных ниже, вы сможете легко управлять количеством информации, находящейся в окне терминала. + +### Ctrl + L + +Это сочетание — альтернатива команде clear. После нажатия Ctrl + L терминал очистится от результатов выполнения предыдущих команд. + +### Ctrl + S + +Сочетание клавиш **Ctrl + S** остановит вывод в терминал. Команда не прекратит выполняться, но вывод данных непосредственно в терминал будет приостановлен. + +### Ctrl + Q + +Нажатие этой горячей клавиши снимет вывод в терминал с паузы, на которую вы его поставили нажатием клавиши **Ctrl + S**. \ No newline at end of file diff --git a/LINUX/TIMEDATE, CHRONY, NTP.md b/LINUX/TIMEDATE, CHRONY, NTP.md new file mode 100644 index 0000000..606238b --- /dev/null +++ b/LINUX/TIMEDATE, CHRONY, NTP.md @@ -0,0 +1,48 @@ +```sh +# TIMEDATE + +timedatectl status +timedatectl list-timezones +sudo set timedatectl set-timezone Europe/Moscow + +========================================== +# CHRONY + +sudo apt install chrony +sudo systemctl enable chrony +sudo systemctl start chrony + +sudo timedatectl set-timezone Europe-Moscow + +date +Mon Dec 6 07:13:49 UTC 2021 + +========================================== +# NTP + +sudo apt install ntp +sntp --version +sudo vim /etc/ntp.conf +``` + +![[Снимок экрана 2023-10-29 в 14.23.34.png]] +Замените на свои. + +```sh +sudo service ntp restart +sudo service ntp status +sudo ufw allow from any port 123 proto udp + +sudo apt install ntpdate +sudo vim /etc/hosts +sudo ntpdate NTP-server-host +sudo timedatectl set-ntp off +sudo apt install ntp +sudo vim /etc/ntp.conf + +# добавьте: +server NTP-server=host prefer iburst` +sudo service ntp restart` +ntpq -ps` +``` + diff --git a/LINUX/TLDR.md b/LINUX/TLDR.md new file mode 100644 index 0000000..5c05592 --- /dev/null +++ b/LINUX/TLDR.md @@ -0,0 +1,12 @@ +**tldr вместо man: как быстро найти примеры использования команды** + +Вместо того, чтобы блуждать по длинным описаниям команд в man, можем использовать tldr. Эта утилита предоставляет краткие и понятные примеры команд. + +✔️ **Установка** +`sudo apt install tldr  # Для Ubuntu/Debian +`sudo pacman -S tldr    # Для Arch + +⚙️ **Пример использования** +`tldr tar + +В результате получите наглядные примеры: как распаковать архив, добавить файлы и так далее... \ No newline at end of file diff --git a/LINUX/TMUX.md b/LINUX/TMUX.md new file mode 100644 index 0000000..6a640e1 --- /dev/null +++ b/LINUX/TMUX.md @@ -0,0 +1,58 @@ +```sh +cd +git clone https://github.com/gpakosz/.tmux.git +ln -s -f .tmux/.tmux.conf +cp .tmux/.tmux.conf.local . +tmux new-session -s name +``` + +**tmux** **команда** **опции** +_**attach-session**_ (attach) - подключиться к уже существующей сессии. В параметре необходимо передать опцию -t и идентификатор сессии; +tmux attach -t 0 +_**detach-session**_ (detach) - отключить всех клиентов (или переданного с помощью опции -t) от сессии, переданной в опции -s; +_**has-session**_ - проверить существует ли сессия, аналогично, надо передать идентификатор сессии; +_**kill-server**_ - остановить все запущенные сессии; +_**kill-session**_ - завершить сессию переданную в параметре -t; +_**list-clients**_ - посмотреть клиентов, подключенных к сессии -t; +_**list-sessions**_ (ls) - вывести список всех запущенных сессий; +_**new-session**_ - создать новую сессию, можно передать имя сессии в опции -s и стартовую директорию в опции -c; +_**rename-session**_ - переименовать сессию, нужно передать идентификатор сессии и новое имя; + +**Ctrl+b c** - создать новое окно; +**Ctrl+b w** - выбрать окно из списка; +**Ctrl+b 0-9** - открыть окно по его номеру; +**Ctrl+b ,** - переименовать текущее окно; +**Ctrl+b %** - разделить текущую панель по горизонтали; +**Ctrl+b "** - разделить текущую панель по вертикали; +**Ctrl+b стрелка** - перейти на панель, находящуюся в стороне, куда указывает стрелка; +**Ctrl+b Ctrl+стрелка** - изменить размер текущей панели; +**Ctrl+b o** - перейти на следующую панель; +**Ctrl+b ;** - переключаться между текущей и предыдущей панелью; +**Ctrl+b x** - закрыть текущую панель; +**Ctrl+b \[** - войти в режим копирования (подробнее ниже); +**Ctrl+b ]** - вставить из внутреннего буфера обмена tmux; +**Ctrl+b d** - отключится от текущей сессии; +**Ctrl+b :** - открыть командную строку. +**Ctrl+b "** - деление окна горизонтально +**Ctrl+b %** - деление окна + + + ```sh +sudo nvim ~/.tmux.conf + +# Установка двух префиксов: от 'Ctrl+b' до 'Alt+b'**_ +set-option -g prefix M-b +set-option -g prefix2 C-b + +# для включения режима мыши (скролл, изменение размера панели, выбор панели и др.) +set -g mouse on + +tmux_conf_copy_to_os_clipboard=true + +tmux source ~/.tmux.conf # -перезапуск + + +tmux new -s name -d # запускаем и отцепляемся от сессии +tmux split-window -v -t name:0.0 # разбиваем вертикально на 2 окна +tmux split-window -h -t name:0.1 # разбиваем горизонтально на 2 окна +``` diff --git a/LINUX/TRANSMISSION.md b/LINUX/TRANSMISSION.md new file mode 100644 index 0000000..9867465 --- /dev/null +++ b/LINUX/TRANSMISSION.md @@ -0,0 +1,30 @@ +`sudo apt install transmission-daemon` + + +**# остановить сервис** + +`sudo systemctl stop transmission-daemon.service` + +**# проверить состояние** + +`sudo systemctl status transmission-daemon.service` + + +_файл инициализирующий старт демона:_ + +`/etc/init.d/transmission-daemon` + + +_Файл конфигурации:_ + +`/etc/default/transmission-daemon` + + +_Файл global settings:_ + +`/etc/transmission-daemon/settings.json` + + +_Файл local settings:_ + +`~/.config/transmission-daemon/settings.json` diff --git a/LINUX/UFW.md b/LINUX/UFW.md new file mode 100644 index 0000000..af92d4e --- /dev/null +++ b/LINUX/UFW.md @@ -0,0 +1,165 @@ +```sh +sudo su + +ufw status + +ufw enable + +ufw app list +ufw allow OpenSSH + +# Чтобы увидеть, что в настоящее время заблокировано или разрешено, вы можете использовать параметр **verbose** при запуске **ufw status**, как показано ниже: + +sudo ufw status verbose + +sudo ufw disable + +# Блокировка обращения IP-адреса +# Чтобы заблокировать все сетевые подключения, исходящие с определенного IP-адреса, выполните следующую команду, заменив выделенный IP-адрес на IP-адрес, который вы хотите заблокировать: + +sudo ufw deny from 91.198.174.190 + +# Заблокировать подсеть +# Если вам нужно заблокировать всю подсеть, вы можете использовать адрес подсети в качестве параметра «from» в команде ufw deny. Это заблокирует все IP-адреса в приведенной в примере подсети 91.198.174.0/24: + +sudo ufw deny from 91.198.174.0/24 + +# Блокирование входящих подключений к сетевому интерфейсу +# Чтобы заблокировать входящие подключения с определенного IP-адреса к определенному сетевому интерфейсу, выполните следующую команду, заменив IP-адрес на IP-адрес, который вы хотите заблокировать: + +sudo ufw deny in on eth0 from 91.198.174.192 + +# Параметр «in» указывает брандмауэру применять правило только для входящих соединений, а параметр «on eth0» указывает, что правило применяется только для интерфейса eth0. +# Это может быть полезно, если у вас есть система с несколькими сетевыми интерфейсами (включая виртуальные), и вам нужно заблокировать внешний доступ к некоторым из этих интерфейсов, но не ко всем. + +# Разрешить обращения IP-адреса +# Чтобы разрешить все сетевые подключения, исходящие с определенного IP-адреса, выполните следующую команду, заменив выделенный IP-адрес на IP-адрес, к которому вы хотите разрешить доступ: + +sudo ufw allow from 91.198.174.192 + +# Если теперь вы запустите «sudo ufw status», вы увидите вывод, похожий на этот, с надписью ALLOW рядом с IP-адресом, который вы только что добавили: + +# Разрешение входящих подключений к сетевому интерфейсу +# Чтобы разрешить входящие подключения с определенного IP-адреса к определенному сетевому интерфейсу, выполните следующую команду, заменив IP-адрес на тот, который вы хотите разрешить: + +sudo ufw allow in on eth0 from 91.198.174.22 + +# Параметр «in» указывает брандмауэру применять правило только для входящих соединений, а параметр «on eth0» указывает, что правило применяется только для интерфейса «eth0». + +# Удалить правило брандмауэра +# Чтобы удалить правило, ранее установленное в UFW, используйте **ufw delete**, затем введите правило (allow / deny) и целевую спецификацию. В следующем примере удаляется правило, ранее установленное для разрешения всех подключений с IP-адреса 91.198.174.192: + +sudo ufw delete allow from 91.198.174.192 + +# Другой способ указать, какое правило вы хотите удалить, – это указать ID правила. Эту информацию можно получить с помощью следующей команды: + +sudo ufw status numbered + +sudo ufw delete 1 + +# Список доступных профилей приложений +# При установке приложения, которые полагаются на сетевые коммуникации, обычно устанавливают профиль брандмауэра, который можно использовать для разрешения подключения с внешних адресов. Это часто то же самое, что запустить «ufw allow from», с тем преимуществом, что это ярлык, который абстрагирует конкретные номера портов, используемые службой, и обеспечивает удобную номенклатуру для ссылающихся служб. + +sudo ufw app list + +# Если вы установили службу, например, веб-сервер или другое сетезависимое программное обеспечение, и профиль не был доступен в брандмауэре, сначала убедитесь, что служба включена. + +# В следующем примере мы включаем профиль OpenSSH, который разрешит все входящие SSH-соединения на стандартном порту SSH. + +sudo ufw allow OpenSSH + +# Отключить профиль для определенного приложения +# Чтобы отключить профиль приложения, который вы ранее настроили в брандмауэре, вам нужно удалить соответствующее правило. + +sudo ufw status + +# Этот вывод указывает на то, что профиль приложения «Nginx Full» в настоящее время включен, разрешая любые соединения с веб-сервером как через HTTP[](https://serverspace.ru/support/glossary/http/), так и через HTTPS. + +# Если вы хотите разрешить только HTTPS-запросы к вашему веб-серверу, вам нужно сначала включить наиболее ограничивающее правило, которым в данном случае будет «Nginx HTTPS», а затем отключить активное правило «Nginx Full»: + +sudo ufw allow "Nginx HTTPS" sudo ufw delete allow "Nginx Full" + +# Помните, что вы можете перечислить все доступные профили приложений с помощью: + +sudo ufw app list + + + +ip addr show +hostname -I + +curl -4 icanhazip.com +``` +===================================================== +### Шпаргалка по ufw для базовой настройки фаервола + +Сетевой безопасностью часто пренебрегают при создании своих проектов на своем сервере. Но даже базовая настройка фаервола уже существенно снижает риски. + +`ufw (Uncomplicated Firewall)` - простой способ настроить фаервол на Linux. Работает как обёртка над iptables, но не требует глубоких знаний сетевого стека. + +Зачем вообще нужен фаервол? + +Когда ты поднимаешь сервер (особенно публичный), он сразу становится видимым всему интернету. И даже если ты не настраиваешь ничего «опасного», какие-то сервисы по умолчанию могут слушать порты. + +ufw помогает ограничить доступ: только те порты, которые ты явно разрешил - будут открыты. Если ты только поднял VPS и хочешь защитить сервер - это именно то, что нужно. Если по какой-то причине у тебя ufw не установлен: + +`sudo apt install ufw + +Перед включением стоит задать основные правила, иначе можно случайно отрезать себя от SSH-доступа (особенно на VPS). Начнем с самого важного: + +`sudo ufw allow ssh + +Теперь можно активировать фаервол: + +`sudo ufw enable + +Просмотр статуса и активных правил: + +`sudo ufw status verbose + +Отключение при необходимости: + +`sudo ufw disable + +Базовые правила, которые стоить задать + +Одно правило по разрешению ssh-доступа мы уже добавили, также будет правильно разрешить веб-траффик (http/https): + + +$ sudo ufw allow 80,443/tcp + + +Полезно запретить весь входящий трафик по умолчанию и разрешить только нужное. Также оставить открытым все на выход: + +`sudo ufw default deny incoming + +`sudo ufw default allow outgoing + +#### Полезные команды: + +**Удалить правило: + +`sudo ufw delete allow 80/tcp + +**Разрешить доступ по конкретному IP: + +sudo ufw allow from 192.168.1.10 + +Это может понадобиться, например, чтобы открыть порты для веб-траффика только Cloudflare или ServicePipe и закрыть для всего остального: + +`ufw default deny incoming +`ufw default allow outgoing + +предположим что 192.168.1.10 - IP нашего ServicePipe + +`sudo ufw allow from 192.168.1.10 to any port 22 + +Сбросить все правила и начать с нуля: + +`sudo ufw reset + +После настройки рекомендую протестировать сервер из другого устройства или использовать онлайн-сервисы вроде portquiz или nmap для сканирования портов. Также полезно мониторить логи: + +`sudo less /var/log/ufw.log + +Если `ufw` работает - ты увидишь записи о заблокированных соединениях, особенно на нестандартные порты. Даже минимальные настройки ufw — это уже огромный шаг вперёд в безопасности. Он не требует глубокого понимания iptables и легко вписывается в ежедневный рабочий процесс. Один раз настроил — и забыл. \ No newline at end of file diff --git a/LINUX/USER.md b/LINUX/USER.md new file mode 100644 index 0000000..62597a2 --- /dev/null +++ b/LINUX/USER.md @@ -0,0 +1,137 @@ +```sh +# Создаем пользователя и добавляем в SUDO: +sudo adduser vlapa +sudo usermod -aG sudo vlapa + +# Блокируем ROOT: +sudo passwd -l root + +# Переключаемся на VLAPA и проверяем: +su - vlapa +sudo whoami # должно вывести "root" (работает через sudo) +sudo su - # должно запросить ваш пароль, а не root + +# Запрещаем вход по SSH: +sudo vim /etc/ssh/sshd_config + +# Найдите строку PermitRootLogin и установите: + +text + +PermitRootLogin no + +# Затем перезапустите SSH: +sudo systemctl restart sshd + +# ## Что делать, если root уже активирован? +sudo passwd -l root # блокируем пароль +sudo usermod -s /bin/bash root # возвращаем shell (на всякий случай) + +# После этого войти как root будет невозможно. Все административные задачи будут выполняться через `sudo` от вашего пользователя. + +#========================================= +# Заблокировать аккаунт пользователя: +passwd -l имя_пользователя + +# Проверить статус аккаунта: +passwd -S имя_пользователя + +# Разблокировать пользователя: +passwd -u имя_пользователя + +#========================================= +# Создание пользователя с привилегиями Sudo +sudo adduser *имя_пользователя* +sudo adduser *имя_пользователя* sudo +su - *имя_пользователя* + +# ======================================== +# - root - правильное переключение sudo su - +sudo su + +# - НЕ интерактивная (лучше создании пользователя из скрипта), НЕ создаст пользователю домашний каталог и НЕ придумает пароль если не использовать дополнительные опции команды. Зато создаст одноименную группу пользователя и включит этого пользователя в неё. + +useradd # -не задает вопросы, а создает пользователя используя опции +-m # — создать домашний каталог пользователю; +-p <пароль> # — здесь нужно ввести хешированный пароль, что не всегда удобно, поэтому не стоит использовать эту опцию; +-s <путь_к_оболочке> # — указать командную оболочку используемую по умолчанию для пользователя (сейчас мы работает в оболочке bash, путь к ней — /bin/bash); + +# - интерактивная (лучше при ручном создании пользователя) утилита у нас запросит пароль для нового пользователя, создаст одноименную группу для пользователя, также мы можем ввести дополнительную информацию о пользователе: полное имя, номер комнаты, рабочий телефон, домашний телефон. +adduser +—no-create-home # — не создавать домашний каталог пользователю; +—shell <Оболочка> # — задать оболочку для пользователя, по умолчанию задается оболочка bash; + +userdel +-f # — завершить все процессы пользователя и удалить насильно, даже если пользователь сейчас работает в системе; +-r # — удалить домашний каталог пользователя; + +deluser +—remove-home # — удалить домашний каталог пользователя; +—force # — завершить все процессы пользователя и удалить насильно, даже если пользователь сейчас работает в системе; +``` + +#### Как создать Sudo пользователя в Ubuntu 22.04 LTS + +[Подписывайтесь на наш канал в Telegram](https://tx.me/amur_pro) + +В Ubuntu-системе, да и в принципе, в любой другой Linux-системе, обязательно есть один привилегированный пользователь — **root**.  + +**sudo** — утилита, которая дает привилегии root для выполнения административных операций. + +Простым языком команда sudo позволяет пользователям Ubuntu выполнять команды от имени суперпользователя root, либо других пользователей. + +## Создание пользователя с привилегиями Sudo + +Сперва, при помощи команды «**adduser**» создаем нового пользователя.  + +sudo adduser *имя_пользователя* + +При создании пользователя будет предложено ввести пароль и другие необязательные данные (Имя, Номер комнаты, Телефон и тд), указываем их по своему желанию. + +![Как создать Sudo пользователя в Ubuntu 22.04 LTS](https://img.amur.pro/blog/2022/07/465/f7f11c9f78533ca611bcb35fa5ef99b6.jpg) + +Обращаем внимание, что только суперпользователь с правами root имеет право добавлять новых пользователей в Ubuntu. Следовательно, запускаем команду adduser с sudo. + +## Добавляем пользователя в группу Sudo + +Следующим шагом является **добавление созданного пользователя в группу sudo**, которая выдаст пользователю все привилегии sudo: + +sudo adduser *имя_пользователя* sudo + +При успешном добавлении в группу Sudo получим уведомление в терминале Ubuntu: «**Добавление пользователя *имя* в группу sudo. Готово.**». + +![Как создать Sudo пользователя в Ubuntu 22.04 LTS](https://img.amur.pro/blog/2022/07/465/dd5b8b4e960d8b99976ca866840c4bfd.jpg) + +Отлично! Пользователь добавлен в группу sudo, теперь проверим, есть ли у него привилегии sudo. + +## Тестирование пользователя Sudo + +Переключаемся на вновь созданного пользователя (пользователь с именем admin в нашем случае) при помощи команды «**su**»: + +su - *имя_пользователя* + +После того, как мы войдем в систему под новым пользователем, мы получим сообщение, как правильно использовать команду «**sudo**» — «**To run a command as administrator (user *root*), use *sudo command***». + +![Как создать Sudo пользователя в Ubuntu 22.04 LTS](https://img.amur.pro/blog/2022/07/465/59cf2584eb8be26da0695ac9aeea46eb.jpg) + +Данное уведомление в терминале будет означает, что пользователь находится в группе sudo. + +================================== +```sh +groups +groups plex + +``` + +================================== +▪**Блокировка учетной записи пользователя:** + +`passwd -l user_name + +▪**Проверьте статус акаунта:** + +`passwd -S user_name + +▪**Разблокировать пользователя:** + +`passwd -u user_name \ No newline at end of file diff --git a/LINUX/VSFTPD.md b/LINUX/VSFTPD.md new file mode 100644 index 0000000..d2e80ed --- /dev/null +++ b/LINUX/VSFTPD.md @@ -0,0 +1,367 @@ +## Обновление системных пакетов + +Перед установкой любого нового ПО рекомендуем обновлять системные пакеты до актуальных версий. Рассмотрим, какие команды для этого следует выполнить. + +1. Обновление списка доступных пакетов и их версий: + + `sudo apt update` + +2. Обновление установленных пакетов до последних версий: + + `sudo apt upgrade -y` + +Теперь система готова к установке FTP-сервера. + +## Выбор FTP-сервера на Ubuntu + +FTP-серверы широко используют для обмена файлами, так как FTP-протокол позволяет одновременно передавать большие объемы данных, например резервные копии. Быстрое копирование файлов на FTP-сервер достигается благодаря передаче данных потоком.  + +Существует несколько популярных FTP-серверов для Linux. Рассмотрим некоторые из них. + +**VSFTPD (Very Secure FTP Daemon)** + +Преимущества: высокий уровень безопасности, отличная производительность, поддержка виртуальных пользователей, возможность ограничения доступа к домашним директориям. + +Недостатки: для обеспечения максимальной безопасности нужна детальная настройка. + +**ProFTPD (Professional FTP Daemon)** + +Преимущества: гибкие настройки, поддержка модулей, возможность использования конфигурации, похожей на Apache. + +Недостатки: более длительная настройка по сравнению с VSFTPD, меньшая производительность при высокой нагрузке. + +**Pure-FTPd** + +Преимущества: легкость установки и использования, высокая производительность, поддержка виртуальных пользователей. + +Недостатки: меньшее количество настроек безопасности по сравнению с VSFTPD и ProFTPD. + +### Что выбрали + +Для демонстрации установки и настройки будем использовать VSFTPD по нескольким причинам. + +- Низкий ресурсный overhead. VSFTPD потребляет минимум ресурсов, что делает его идеальным для систем с ограниченными возможностями. +- Высокая скорость передачи данных. Обеспечивает отличную производительность при передаче файлов, что важно для высоконагруженных серверов. +- Встроенная поддержка шифрования для безопасной передачи данных — SSL/TLS. +- Многоплатформенность. Работает на большинстве UNIX-подобных систем. +- Простота интеграции с системами безопасности. Легко интегрируется с PAM и другими системами аутентификации для гибкого управления доступом. + +[ + +## Создайте FTP-сервер на мощностях Selectel + +Более 100 фиксированных конфигураций с готовностью — 2 минуты. + +](https://selectel.ru/services/cloud/servers/ftp/) + +## Установка FTP-сервера на Ubuntu + +### Установка VSFTPD + +VSFTPD прост в установке — он доступен в стандартных репозиториях Ubuntu. Откроем терминал и выполним следующую команду: + + `sudo apt install vsftpd -y` + +### Запуск и настройка автозагрузки VSFTPD + +После установки запустим VSFTPD: + + `sudo systemctl start vsftpd` + +Совет: для настройки автозагрузки VSFTPD при запуске системы выполните команду sudo systemctl enable vsftpd. + +### Проверка состояния VSFTPD + +После запуска службы проверим ее состояние: + + `sudo systemctl status vsftpd` + +В выводе отобразится текущий статус службы VSFTPD. Если она запущена корректно, мы увидим сообщение, что VSFTPD активен и работает (Active: active (running)). + +Пример вывода sudo systemctl status vsftpd: + + `● vsftpd.service - vsftpd FTP server Loaded: loaded (/lib/systemd/system/vsftpd.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2024-06-23 14:35:26 UTC; 2h 15min ago Main PID: 12345 (vsftpd) Tasks: 1 (limit: 4915) Memory: 1.3M CGroup: /system.slice/vsftpd.service └─12345 /usr/sbin/vsftpd /etc/vsftpd.conf` + +### Настройка файрвола для FTP + +Если на сервере включен файрвол, следует открыть порты для FTP, чтобы обеспечить доступ к серверу. + +В примере используем UFW (Uncomplicated Firewall). Команды далее могут отличаться в зависимости от вашего брандмауэра. + + +1. По умолчанию FTP использует порты 20 и 21 (TCP-трафик), а также диапазон 30000-31000 (пассивный режим FTP). Чтобы открыть их, выполним команды: + + `sudo ufw allow 20/tcp sudo ufw allow 21/tcp sudo ufw allow 30000:31000/tcp` + +2. Перезапустим файрвол для применения изменений: + + `sudo ufw reload` + +3. Убедимся, что правила файрвола применены корректно: + + `sudo ufw status` + +Пример вывода sudo ufw status: + + `Status: active To Action From -- ------ ---- 20/tcp ALLOW Anywhere 21/tcp ALLOW Anywhere 30000:31000/tcp ALLOW Anywhere 22/tcp ALLOW Anywhere` + +## Настройка FTP-сервера в Ubuntu + +### Основная настройка + +Конфигурационный файл VSFTPD находится в директории /etc/vsftpd.conf. Чтобы его отредактировать, можно воспользоваться любым текстовым редактором, например nano: + + `sudo nano /etc/vsftpd.conf` + +Для обеспечения безопасности и эффективности работы сервера изменим конфигурационный файл VSFTPD в соответствии с настройками. + +1. Отключим анонимный доступ, заменив значение параметра anonymous_enable на NO: + + `anonymous_enable=NO` + +2. Для разрешения входа на сервер локальным пользователям установим значение параметра local_enable на YES: + + `local_enable=YES` + +3. Разрешим команды записи. Чтобы пользователи могли загружать файлы на сервер, установим значение параметра write_enable на YES: + + `write_enable=YES` + +4. Настроим права доступа для новых файлов, создаваемых пользователями. Их автоматическое назначение осуществляется с помощью параметра local_umask. Значение 022 позволяет владельцу файла записывать, читать и выполнять, а всем остальным — читать и выполнять: + + `local_umask=022` + +Набор прав состоит из трех цифр: + +- первая — права владельца, +- вторая — права группы владельца, +- третья — права остальных пользователей. + +Каждая из них может принимать значения: + +- 0 — нет прав; +- 1 — выполнение; +- 2 — запись; +- 3 — запись и выполнение; +- 4 — чтение; +- 5 — чтение и выполнение; +- 6 — чтение и запись; +- 7 — чтение, запись и выполнение. + +Новые файлы и директории будут создаваться с правами, которые исходят из полного набора (777) за вычетом значений local_umask. Например, маска 022 означает набор прав 755. + +5. Включим отображение приветственных сообщений при входе в директории: + + `dirmessage_enable=YES` + +Для настройки текста сообщений следует перейти в интересующую директорию и создать в ней файл .message. Когда пользователь будет переходить в данную директорию, он будет получать сообщение, текст которого указан в файле. + +6. Для корректной работы файрвола установим использование порта 20 для передачи данных: + + `connect_from_port_20=YES` + +7. Чтобы ограничить пользователей их домашними директориями, установим значение параметра chroot_local_user на YES: + + `chroot_local_user=YES allow_writeable_chroot=YES` + +Параметр allow_writeable_chroot используется, чтобы разрешить запись в домашнюю директорию пользователя при использовании chroot_local_user=YES. + +8. Зададим корневую директорию для каждого пользователя: + + `user_sub_token=$USER local_root=/home/$USER/ftp` + +9. После внесения всех изменений сохраним файл (**Ctrl+O** в nano), закроем редактор (**Ctrl+X** в nano) и перезапустим службу VSFTPD для применения изменений: + + `sudo systemctl restart vsftpd` + +### Тестирование VSFTPD + +1. Проверим корректность применения настроек. Для начала создадим тестового пользователя: + + `sudo adduser testuser sudo passwd testuser` + +2. Создадим необходимые директории и настроим на них права: + + `sudo mkdir -p /home/testuser/ftp/upload sudo chown nobody:nogroup /home/testuser/ftp sudo chmod a-w /home/testuser/ftp sudo chown -R testuser:testuser /home/testuser/ftp/upload` + +3. Добавим пользователя в список пользователей VSFTPD: + + `echo "testuser" | sudo tee -a /etc/vsftpd.userlist` + +4. Подключимся к серверу с учетными данными тестового пользователя и убедимся в возможности загружать файлы в директорию /upload. Можно использовать любой FTP-клиент, в рамках инструкции будем пользоваться FileZilla. + +### Настройка часового пояса + +Чтобы сервер использовал локальное время, откроем конфигурационный файл VSFTPD и установим параметр use_localtime на YES: + + `use_localtime=YES` + +### Настройка логов + +Для настройки логирования перейдем в конфигурационный файл VSFTPD и включим логирование всех FTP-транзакций: + + `xferlog_enable=YES xferlog_std_format=YES` + +Параметр xferlog_std_format позволяет использовать стандартный формат логов. + +Также следует указать корректный путь к файлу логов (обычно /var/log/vsftpd.log): + + `xferlog_file=/var/log/vsftpd.log` + +### Настройка PAM-сервиса + +PAM (Pluggable Authentication Modules) — это система аутентификации, используемая в UNIX-подобных ОС, включая Linux. PAM позволяет администраторам настраивать политику аутентификации для различных приложений и служб. Для этого используется набор модулей, которые могут быть включены и настроены по мере необходимости.  + +Для использования PAM необходимо предварительно включить его в файле /etc/vsftpd.conf. + +1. Откроем файл /etc/vsftpd.conf: + + `sudo nano /etc/vsftpd.conf` + +2. Отредактируем строку pam_service_name: + + `pam_service_name=vsftpd` + +Для дальнейшей настройки сервиса PAM в VSFTPD следует внести изменения в файл /etc/pam.d/vsftpd. Внутри него — инструкции, которые PAM использует для управления аутентификацией пользователей при подключении к FTP-серверу.  + +3. Откроем файл конфигурации PAM для VSFTPD: + + `sudo nano /etc/pam.d/vsftpd` + +4. Настроим параметры аутентификации в соответствии со списком: + + `auth required pam_listfile.so item=user sense=deny file=/etc/vsftpd.ftpusers onerr=succeed auth required pam_shells.so auth include common-auth account include common-account session include common-session` + +- auth required pam_listfile.so item=user sense=deny file=/etc/vsftpd.ftpusers onerr=succeed — отказывает в доступе пользователям из файла /etc/vsftpd.ftpusers. +- auth required pam_shells.so — проверяет, что оболочка пользователя разрешена. +- auth include common-auth — включает общие правила аутентификации. +- account include common-account — включает общие правила управления учетными записями. +- session include common-session — включает общие правила управления сеансами. + +5. Создадим файл списка пользователей: + + `sudo nano /etc/vsftpd.ftpusers` + +Добавим имена пользователей, которым следует запретить доступ (по одному на строку). + +6. Перезапустим службу VSFTPD: + + `sudo systemctl restart vsftpd` + +## Создание пользователей + +### Создание + +Для создания нового пользователя на сервере выполним команду: + + `sudo adduser ftpuser` + +Далее следует ввести пароль для нового пользователя и несколько дополнительных данных (имя, номер телефона и прочее). Можно оставить эти поля пустыми, нажав **Enter**. + +Для назначения или изменения пароля пользователя нужно выполнить команду sudo passwd ftpuser, ввести новый пароль и подтвердить его. + +### Настройка домашних папок + +1. Создадим домашнюю папку пользователя: + + `sudo mkdir -p /home/ftpuser/ftp` + +2. Домашний каталог пользователя должен быть защищен от записи, чтобы соблюсти требования безопасности VSFTPD: + + `sudo chown nobody:nogroup /home/ftpuser/ftp sudo chmod a-w /home/ftpuser/ftp` + +3. Далее создадим папку, в которую пользователь сможет загружать файлы, и настроим права доступа: + + `sudo mkdir -p /home/ftpuser/ftp/upload sudo chown -R ftpuser:ftpuser /home/ftpuser/ftp/upload sudo chmod -R 0755 /home/ftpuser/ftp/upload` + +### Настройка анонимного или только авторизованного доступа + +VSFTPD поддерживает доступ как анонимных, так и авторизованных пользователей. Для настройки политики следует внести изменения в конфигурационный файл. + +#### Анонимный доступ + +Если нам нужно разрешить анонимный доступ к FTP-серверу, следует указать для параметра anonymous_enable значение YES в конфигурационном файле /etc/vsftpd.conf: + + `anonymous_enable=YES` + +#### Авторизованный доступ + +Чтобы разрешить доступ к FTP-серверу авторизованным пользователям, укажем для параметра local_enable значение YES: + + `local_enable=YES` + +### Разрешение / запрет доступа по SSH + +Все настройки доступа к серверу по SSH находятся в конфигурационном файле /etc/ssh/sshd_config. В нем описываются разрешающие и запрещающие правила. + +Для разрешения или запрета доступа к серверу по SSH группе пользователей следует указать в конфигурационном файле одну из следующих строк соответственно: + + `AllowGroups ftpusers` + + `DenyGroups ftpusers` + +Чтобы разрешить или запретить доступ для отдельных пользователей, следует использовать следующие параметры: + + `AllowUsers ftpuser` + + `DenyUsers ftpuser` + +Группа ftpusers и пользователь ftpuser указаны в качестве примера. + +Для применения новых правил перезапустим службу SSH: + + `sudo systemctl restart ssh` + +Порядок правил, указанных в конфигурационном файле SSH, имеет важное значение в определении политики доступа пользователя. Например, первым указано правило, запрещающее пользователям группы ftpusers доступ по SSH, а затем указано правило, разрешающее доступ пользователю ftpuser1 (входящему в группу ftpusers).  + +В таком случае у пользователя ftpuser1 будет доступ к серверу по SSH. При установке этих правил в обратном порядке, пользователь ftpuser1 не получит доступ по SSH. + +## Настройка SSL/TLS + +Обеспечение безопасности FTP-соединений может обеспечить [SSL/TLS-протокол](https://selectel.ru/blog/what-is-ssl/).   + +1. Создадим директорию,  в которой будут храниться SSL-сертификаты и ключи: + + `sudo mkdir /etc/ssl/private` + +2. Создадим самоподписанный сертификат: + + `sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout /etc/ssl/private/vsftpd.key -out /etc/ssl/private/vsftpd.pem` + +Ключ будет действителен в течение 10 лет. + +В процессе генерации будет предложено ввести информацию о сертификате: страну, город, организацию и т. д. Можно оставить некоторые поля пустыми, нажав **Enter**. + +3. Для использования созданного сертификата откроем конфигурационный файл VSFTPD: + + `sudo nano /etc/vsftpd.conf` + +4. Внесем в него изменения в соответствии со следующим списком: + + `rsa_cert_file=/etc/ssl/private/vsftpd.pem rsa_private_key_file=/etc/ssl/private/vsftpd.key ssl_enable=YES allow_anon_ssl=NO force_local_data_ssl=YES force_local_logins_ssl=YES ssl_tlsv1=YES ssl_sslv2=NO ssl_sslv3=NO require_ssl_reuse=NO ssl_ciphers=HIGH` + +- rsa_cert_file и rsa_private_key_file — указывают путь к сертификату и приватному ключу. +- ssl_enable=YES — включает SSL. +- allow_anon_ssl=NO — запрещает анонимный доступ через SSL. +- force_local_data_ssl=YES и force_local_logins_ssl=YES — требуют использование SSL для всех данных и логинов локальных пользователей. +- ssl_tlsv1=YES, ssl_sslv2=NO, ssl_sslv3=NO — включают TLSv1 и отключают устаревшие SSLv2 и SSLv3. +- require_ssl_reuse=NO — отключает требование повторного использования SSL-сессий. +- ssl_ciphers=HIGH — устанавливает использование только сильных шифров. + +5. После внесения изменений сохраним файл и перезапустим службу VSFTPD: + + `sudo systemctl restart vsftpd` + +При установке SSL/TLS-сертификатов подключение через FTP-клиенты осуществляется стандартным путем. + +## Проверка подключения к серверу через FTP-клиент + +Теперь FTP-сервер настроен и готов принимать безопасные соединения от клиентов. + +Популярные программы, позволяющие подключаться к FTP-серверам, — FileZilla, WinSCP, FAR Manager, Total Commander, Midnight Commander и Cyberduck. Далее будем использовать наиболее известный кроссплатформенный FTP-клиент — FileZilla. + +Для его установки в Ubuntu выполним команду: + + `sudo apt-get install filezilla` + +Для подключения к FTP-серверу следует заполнить поля **Хост** (IP-адрес сервера), **Имя пользователя**, **Пароль** и нажать на кнопку **Быстрое соединение**. После подключения к FTP-серверу в правой части окна появится список директорий, доступных для данного пользователя. \ No newline at end of file diff --git a/LINUX/WIREGUARD.md b/LINUX/WIREGUARD.md new file mode 100644 index 0000000..14a8597 --- /dev/null +++ b/LINUX/WIREGUARD.md @@ -0,0 +1,170 @@ +***На сервере: + +```bash +sudo su +apt update && apt upgrade -y +apt install -y wireguard + +cd /etc/wireguard + +wg genkey | tee privatekey | wg pubkey | tee publickey + +chmod 600 privatekey +cat privatekey +``` + +**Редактируем/создаем: vim wg0.conf** +```sh +# - заменить на свой !!! +[Interface] +PrivateKey = +Address = 10.0.0.1/24 +ListenPort = 51830 +PostUp = iptables -A FORWARD -i %i(wg0) -j ACCEPT; iptables -t nat -A POSTROUTING -o -j MASQUERADE +PostDown = iptables -D FORWARD -i %i(wg0) -j ACCEPT; iptables -t nat -D POSTROUTING -o -j MASQUERADE + +[Peer] +# user_1 Publickey +Publickey = +AllowedIPs = 10.0.0.2/32 + +[Peer] +# user_2 Publickey** +Publickey = +AllowedIPs = 10.0.0.3/32 + +[Peer] +# user_3 Publickey +Publickey = +AllowedIPs = 10.0.0.4/32 +``` + +```sh +echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf +sysctl -p + +net.ipv4.ip_forward=1 + +systemctl enable wg-quick@wg0.service + +>Created symlink /etc/systemd/system/multi-user.target.wants/wg-quick@wg0.service → /lib/systemd/system/wg-quick.service + +systemctl start wg-quick@wg0.service +systemctl status wg-quick@wg0.service + +systemctl restart wg-quick@wg0.service +systemctl status wg-quick@wg0.service + +systemctl daemon-reload + +sudo wg show + +#################################### +# ОБЯЗАТЕЛЬНО ПРОВЕРЯЙТЕ ФАЕРВОЛ !!! +sudo ufw status +sudo ufw allowed http +sudo ufw allowed https +#################################### +``` +===================================== +***Генерация ключей клиента: + +`wg genkey | tee /etc/wireguard/_key/win_home_priv | wg pubkey | tee /etc/wireguard/win_home_pub` + + +***На компьютере-клиенте: +```sh +sudo apt install wireguard resolvconf -y +vim Documents/new_wg.conf +``` + +***Конфигурация клиента: + vim wg0.conf +```bash +[Interface] +PrivateKey = +Address = 10.10.10.2/32 +DNS = 8.8.8.8,77.88.8.8 + +[Peer] +PublicKey = +Endpoint = :51830 +AllowedIPs = 0.0.0.0/0 # (весь трафик через WireGuard) >>> 10.10.10.0/24 +PersistentKeepalive = 20 +``` + +```sh +systemctl enable wg-quick@wg0 +systemctl start wg-quick@wg0.service +systemctl restart wg-quick@wg0.service +systemctl status wg-quick@wg0.service + +systemctl daemon-reload + +curl ifconfig.me + +sudo wg show + +``` +===================================== +***GUI для Wireguard: +``` +wget https://github.com/UnnoTed/wireguird/releases/download/v1.0.0/wireguird_amd64.deb +sudo dpkg -i ./wireguird_amd64.deb + + +nm-connection-editor +nmcli + +wg-quick up wg0-client +wg-quick down wg0-client +``` +===================================== +### Настройка VPN с WireGuard и LDAP за пару шагов  + +Представь, как удобно: ты настраиваешь VPN, а доступ автоматически контролируется через LDAP. Никакого хаоса с ключами, всё централизовано. Вот как это сделать, чтобы всё работало чётко. + +### **1. Настраиваем WireGuard**   + +Сначала давай поднимем сам VPN:   +1. Устанавливаешь WireGuard:   +   `sudo apt install wireguard + +2. Генерируешь ключи: +   `wg genkey | tee privatekey | wg pubkey > publickey + +3. Делаешь конфиг /etc/wireguard/wg0.conf: +   ```sh +[Interface] +   PrivateKey = <твой_приватный_ключ> +   Address = 10.0.0.1/24 +   ListenPort = 51820 + +   [Peer] +   PublicKey = <публичный_ключ_клиента> +   AllowedIPs = 10.0.0.2/32 +   ``` + +4. Включаешь VPN: +   `sudo wg-quick up wg0 + +Теперь у тебя рабочий VPN, но это только начало. + +--- +### **2. Подключаем LDAP**   + +Теперь сделаем так, чтобы только проверенные пользователи могли подключаться:   + +1. Установи LDAP-клиент:   +   `sudo apt install ldap-utils + +2. Проверяешь связь с сервером: +   `ldapsearch -x -H ldap:// -D "cn=admin,dc=example,dc=com" -W + +3. Подключаешь проверку пользователей через LDAP. Например, перед выдачей ключей скрипт проверяет, есть ли юзер в базе:   +   `ldapsearch -x -b "dc=example,dc=com" "uid=<имя_пользователя>" + +4. Для автоматизации можно настроить генерацию конфигов и ключей через API или свои скрипты.   +### **Почему это круто?**   + +Тебе больше не нужно вручную раздавать доступы или следить за кучей ключей. Всё централизовано: добавил юзера в LDAP — он тут же получил доступ к VPN. Удобно, чисто, профессионально. \ No newline at end of file diff --git a/LINUX/WORDPRESS.md b/LINUX/WORDPRESS.md new file mode 100644 index 0000000..68cfc81 --- /dev/null +++ b/LINUX/WORDPRESS.md @@ -0,0 +1,471 @@ +## Установка WordPress: техническая часть + +### Проверим пользователя + +Убедимся, что мы находимся в нужной папке (директории) и не под пользователем root: + +``` +serveruser@ИМЯСЕРВЕРА:/root$ +``` + +Если у Вас как на примере выше (вместо serveruser может быть другой пользователь, главное, чтобы не root), то все хорошо. А если у Вас так: + +``` +root@ИМЯСЕРВЕРА:~# +``` + +То выполите инструкции по созданию нового пользователя из этой статьи: [Установка Nginx, MySQL 8, PHP8.1-FPM на Ubuntu 22.04 LTS с нуля](https://dnevnik.dev/2022/07/11/ustanovka-nginx-mysql-8-php8-1-fpm-na-ubuntu-22-04-lts-s-nulya/). + +### Скачаем WordPress + +Перейдем в нужный каталог, введем: + +``` +cd /var/www/ +``` + +Затем, нужно создать каталог для нашего будущего сайта (у меня НАЗВАНИЕ_САЙТА это dnevnik): + +``` +sudo mkdir НАЗВАНИЕ_САЙТА +sudo chown -R www-data:www-data НАЗВАНИЕ_САЙТА +sudo find /var/www/НАЗВАНИЕ_САЙТА/ -type d -exec chmod 750 {} \; +sudo find /var/www/НАЗВАНИЕ_САЙТА/ -type f -exec chmod 640 {} \; +``` + +Если нужно проверить, свободно ли выбранное Вами название сайта, то сделать это можно здесь: [https://reg.ru](https://www.reg.ru/domain/new/?rlink=reflink-10793423). + +Переместимся в созданный каталог, и скачаем WordPress: + +``` +cd НАЗВАНИЕ_САЙТА +sudo -u www-data wget https://wordpress.org/latest.tar.gz +``` + +Распакуем архив: + +``` +sudo -u www-data tar -xzvf latest.tar.gz +``` + +И удалим его: + +``` +sudo -u www-data rm latest.tar.gz +``` + +Затем переместим содержимое папки wordpress в родительскую для ее директорию: + +``` +sudo -u www-data mv wordpress/* . +``` + +И удалим папку wordpress: + +``` +sudo -u www-data rm -R wordpress +``` + +### Создадим базу данных в MySQL + +Войдем в командную строку MySQL: + +``` +sudo mysql +``` + +Создадим новую базу данных: + +``` +CREATE DATABASE НАЗВАНИЕ_САЙТА; +``` + +Создадим нового пользователя: + +``` +CREATE USER 'НАЗВАНИЕ_САЙТА'@'localhost' IDENTIFIED BY 'aef@234R345t2iJhfvar'; +``` + +«aef@234R345t2iJhfvar» — это пароль пользователя НАЗВАНИЕ_САЙТА. + +Дадим пользователю НАЗВАНИЕ_САЙТА права на базу данных НАЗВАНИЕ_САЙТА: + +``` +GRANT ALL ON НАЗВАНИЕ_САЙТА.* TO 'НАЗВАНИЕ_САЙТА'@'localhost'; +``` + +Выйдем из командной строки MySQL: + +``` +exit +``` + +Теперь давайте войдем в командную строку MySQL под новым MySQL-пользователем: + +``` +mysql -u НАЗВАНИЕ_САЙТА -p +``` + +Вас попросят ввести пароль, введите его. Если Вы не меняли его в примере выше, то он будет такой: + +``` +aef@234R345t2iJhfvar +``` + +Вот мы и вошли под новым пользователем. Посмотрим, какие базы данных ему видны, введем: + +``` +SHOW DATABASES; +``` + +Результат будет такой: + +``` ++---------------------------+ +| Database | ++---------------------------+ +| information_schema | +| НАЗВАНИЕ_САЙТА | ++---------------------------+ +2 rows in set (0.06 sec) +``` + +Видим нашу базу НАЗВАНИЕ_САЙТА, отлично, выйдем из командной строки MySQL: + +``` +exit +``` + +Давайте двигаться дальше, настроим Nginx. + +### Настроим Nginx + +Тут нужно сказать, что я буду иметь в виду под «НАЗВАНИЕ_САЙТА», «НАЗВАНИЕ_САЙТА.ДОМЕН» и «www.НАЗВАНИЕ_САЙТА.ДОМЕН». + +На примере моего сайта это будет так: + +НАЗВАНИЕ_САЙТА = dnevnik +НАЗВАНИЕ_САЙТА.ДОМЕН = dnevnik.dev +www.НАЗВАНИЕ_САЙТА.ДОМЕН = www.dnevnik.dev + +Если Вам нужен домен, советую приобретать его здесь: [https://reg.ru](https://www.reg.ru/domain/new/?rlink=reflink-10793423). К примеру, домен этого сайта (dnevnik.dev) зарегистрирован именно там. + +Создадим новый конфигурационный файл для Nginx, для этого введем: + +``` +sudo vim /etc/nginx/sites-available/НАЗВАНИЕ_САЙТА +``` + +Команда выше создаст новый пустой файл. Вставим в него содержимое: + +``` +server { + listen 80; + server_name НАЗВАНИЕ_САЙТА.ДОМЕН www.НАЗВАНИЕ_САЙТА.ДОМЕН; + root /var/www/НАЗВАНИЕ_САЙТА; + + index index.html index.htm index.php; + + location / { + try_files $uri $uri/ /index.php$is_args$args; + } + + location ~ \.php$ { + include snippets/fastcgi-php.conf; + fastcgi_pass unix:/var/run/php/php8.1-fpm.sock; + } + + location ~ /\.ht { + deny all; + } +} +``` + +И для сохранения файла, нажмите «:wq», а затем Enter. Файл сохранен. + +Активируем конфигурацию этой командой: + +``` +sudo ln -s /etc/nginx/sites-available/НАЗВАНИЕ_САЙТА /etc/nginx/sites-enabled/ +``` + +Перезапустим Nginx, чтобы изменения вступили в силу: + +``` +sudo systemctl reload nginx +``` +======================================== +Сначала вы проверяете, что порт 80 уже работает, + +``` +sudo netstat -plunt | grep 80 + +tcp6 0 0 :::80 :::* LISTEN 1573456/apache2 +``` + +Если он запущен, то отключите apache: + +``` +sudo service apache2 stop +``` + +Теперь запустите Nginx: + +``` +sudo service nginx start +``` +======================================== + +Теперь нужно настроить WordPress. + +### Настроим WordPress + +Переместимся в директорию сайта: + +``` +cd /var/www/НАЗВАНИЕ_САЙТА +``` + +Переименуем файл wp-config-sample.php: + +``` +sudo -u www-data mv wp-config-sample.php wp-config.php +``` + +И откроем его для редактирования: + +``` +sudo -u www-data vim wp-config.php +``` + +Увидим что-то вроде этого: + +``` + +Теперь можно выполнять различные операции над файлами. Чтобы узнать, какие +команды доступны, введите директиву help или знак вопроса (?). Команды, кото­ +рые вы увидите, очень похожи на команды FTP (более подробно с FTP мы позна­ +комимся в главе 10, см. также приложение /). Для отключения от ресурса необхо­ +димо выполнить команду exit. + +#========================================== +### Настройка sendmail + +Основной конфигурационный файл, который вам понадобится, — `/etc/sendmail.cf` или +`/etc/mail/sendmail cf`. Если файла не существует, то, скорей всего, sendmail не установ­ +лен, что случается в установке дистрибутива по умолчанию. Устанавливаем: +`sudo apt-get install sendmail +После установки сразу же должна начаться автоматическая настройка. Если и по­ +сле этого файла конфигурации не оказалось в системе, то выполните эту команду: +sudo sendmaiIconfig +Программа начальной настройки запросит пути к конфигурационным файлам* Со­ +гласитесь со значениями по умолчанию, но обратите внимание, какие пути у вас +спрашивают. В последнем релизе дистрибутива Ubuntu версия sendmail предлагает +использовать путь /etc/mail/ для всех файлов. +Сервер sendmail имеет плохую репутацию в связи со сложностью его настройки. +Действительно, стоит лишь взглянуть на размер файла sendmail.cf, и становится жут­ +ко — это 32 килобайта (более 1000 строк). А если заглянуть внутрь файла, то станет +еще страшнее от непонятных ключей и директив. +В файле конфигурации sendmail все параметры сгруппированы по разделам. Раз­ +биение оформляется в виде следующих строк: +```sh +################## +# Local info # +################## +``` +Столь «красивое» оформление означает, что далее следует раздел Local info. По­ +добных секций несколько — в частности, обычно присутствуют такие: +□ Local info — локальная информация, основные сведения о сервере и домене; +□ options — настройки работы программы sendmail; +□ Message precedences — приоритеты сообщений; +□ Trusted users — доверенные пользователи; +□ Format of headers — форматы Заголовков. +Рассмотреть все настройки в этой книге невозможно — в моей системе этот конфи­ +гурационный файл занимает 50 Кбайт. Если расписывать каждый параметр, понадо­ +бится отдельное издание. Наша цель— эффективность и безопасность, поэтому +рассмотрим только те настройки, которые касаются этих вопросов, и испытаем +sendmail в действии. +Для упрощения конфигурирования sendmail в последних версиях этого почтового +сервиса используется новый файл — sendmail.mс (МС означает Minimal Configuration или +минимальная конфигурация), который можно найти в каталоге /etc/mail. +```sh +divert(-1) +dnl This is the sendmail macro config file. If you make changes to this +dnl file, you need the sendmail-cf rpm installed and then have to +dnl generate a new /etc/sendmail.cf by running the following command: +dnl Это файл макроконфигурации sendmail. Если вы делаете изменения в этом +dnl файле, вам нужна инсталляция rpm sendmail-cf, с помощью которой можно +dnl сгенерировать новый /etc/sendmail.cf, запустив команду: +dnl +dnl +m4 /etc/mail/sendmail.me > /etc/sendmail.cf +dnl +include('/usr/share/sendmail-cf/m4/cf .m4’) +VERSIONID('linux setup for ASPLinux’)dnl +OSTYPE('linux') +dnl Uncomment and edit the following line if your mail needs to be +dnl sent out through an external mail server: +dnl Раскомментируйте следующую строку, если вам нужно посылать почту +dnl через внешний почтовый сервер: +dnl define('SMART_HOST','smtp.your.provider') +define('confDEF_USER_ID',''8:12'')dnl +undefine('UUCP_RELAY')dnl +undefine('BITNET_RELAY')dnl +``` +Файл sendmail.me имеет более простой формат по сравнению со старым sendmail.cf, +благодаря чему уменьшается вероятность допустить ошибку при его конфигуриро­ +вании. После внесения изменений его необходимо скомпилировать (перевести файл +sendmail.mс в формат sendmail.cf) специальной командой, в результате чего получает­ +ся файл /etc/sendmail.cf. +Мы в основном будем рассматривать параметры, которые нужно устанавливать +в файле sendmail.cf, а если описывается параметр файла sendmail.mс, я буду явно ука­зывать на это. +Иногда при неправильных настройках загрузка Linux может зависать при старте +сервиса sendmail. Это происходит из-за того, что ваш почтовый сервер не может +определить имя компьютера. Откройте файл /etc/hosts. В нем чаще всего будет в на­личии только одна строка: +127.0.0.1 localhost.localdomain localhost + +Подробно с этим файлом мы познакомимся в главе 77, когда станем рассматривать +DNS. Сейчас нам надо знать, что эта строка описывает IP-адрес 127.0.0.1, который +соответствует имени localhost. В любой системе такие адрес и имя указывают на +вашу локальную машину. Когда в сетевых программах вы указываете localhost, то +это имя преобразуется в IP-адрес 127.0.0.1. +Настройка DNS может быть очень важна для правильной отправки почты. Допус­тим, что у меня есть домен www.flenov.ru. Этот домен может загружать веб-сайт, а +может отправлять почту, если послать что-либо на почтовый ящик info@flenov.ru +(в реальности такой ящик не существует). В обоих 'случаях домен один и тот же, но +веб-сервер и почтовый сервер могут находиться на разных серверах с разными IP. +Настройка DNS позволяет указать, какой IP-адрес у веб-сервера и какой адрес +у почтового. +Для почтового сервера нужно будет в настройки DNS добавить следующие строки: +IN MX 10 mx.flenov.ru. +mx.flenov.ru. IN АААА 1р_адрес_в_формате^6 +mx.flenov.ru. IN А 1р_адрес_в_формате^4 +Домен Fienov.ru поменяйте здесь на свой, и точка после него не случайна. +Ну и домен этот должен быть прописан в host-файле: +127.0.0.1 flenov.ru +Пока мы с DNS еще не знакомы, поэтому в sendmail можно для тестов использовать +имя компьютера, которое вы указали во время установки ОС Linux. Выполните +команду hostname, чтобы это имя узнать. В моем случае это FienovM. Но на деле все +сетевые обращения происходят не по имени, а по IP-адресу, и если sendmail не мо­жет определить адрес по имени FienovM, происходит зависание. Чтобы исправить +эту ситуацию, добавьте в файл /etc/hosts строку вида: +192.168.77.1 FlenovM +FlenovM +Не забудьте только заменить приведенный здесь параметр FienovM на имя вашего +компьютера и, разумеется, указать свой IP-адрес. После этого программу sendmail +можно помещать в автозапуск, и она будет работать великолепно даже с настрой­ +ками по умолчанию. +Для каждого пользователя системы автоматически создается почтовый ящик, и ес­ +ли сервис sendmail запущен, то им уже можно пользоваться. Все почтовые ящики +хранятся в каталоге /var/spool/mail/. Их имена соответствуют именам пользователей. +Так, для учетной записи root ящик будет расположен в файле /var/spool/mail/root. +Для работы с почтой нам нужен почтовый клиент, который будет отправлять пись­ +ма серверу и принимать от него новые сообщения. Таких программ существует +множество, и в некоторых дистрибутивах Linux предлагается аж семь различных +клиентов. Какой выберете вы, зависит только от личных предпочтений. +В общем-то можно обойтись и без клиента, а соединение с сервером осуществлять +напрямую через сервис Telnet, благо команды SMTP достаточно просты и ими легко пользоваться. В этом случае для отправки почты нужно подключиться к пор­ту 25 (порт SMTP), а для получения — к 110 (порт POP3). +#### Полезные команды +Давайте рассмотрим некоторые команды, которые помогут вам в администрирова­ +нии sendmail-сервера: +□ hoststat— показать состояние хостов, которые недавно работали с локальным +почтовым сервером. Команда является эквивалентом sendmail -bh, которая по +умолчанию неактивна; +□ mailq — отобразить краткую информацию о сообщениях в очереди, ожидающих +обработки. Пример результата выполнения команды: + +/var/spool/mqueue (1 request) +--- Q-ID----—Size—------Q-Time----- ---- Sender/Recipient----- +jOIAnST11838 6 Tue Jan 18 13:49 +(host map: lookup (flenovm.ru): deferred) + + +Из первой строки видно, что в очереди находится одно сообщение. Вторая стро­ка включает дату посылки и адрес отправителя: fienov@fienovm.ru. В последней +строке отображается получатель сообщения: root@f lenovm. ru; +□ mailstats — отобразить статистику сообщений и количества байтов; +□ sendmail — запустить сервер sendmail. Используя эту команду с различными +ключами, можно увидеть достаточно много полезной информации. За более +подробной справкой следует обратиться к документации: man sendmail. +#### Только отправка почты +Очень часто почтовые сервисы используют только для отправки почты. Например, +на веб-серверах sendmail может стоять лишь для того, чтобы можно было отослать +письмо прямо из сценариев на Perl или РНР. Если ваш сервер не должен принимать +писем, то необходимо запретить режим приема. Для этого откройте файл /etc/ +sysconfig/sendmail и измените его содержимое: +DEAMON=yes +QUEUE=”qlh” +Вторая строка задает параметры, которые будут передаваться программе sendmail +при запуске. Чтобы возобновить прием почты, измените значение qih на ьа. +Если у вас нет файла /etc/sysconfig/sendmail (он используется не во всех дистрибути­ +вах), то придется редактировать сценарий /etc/rc.d/init.d/sendmail. Найдите в этом фай­ +ле параметры, которые передаются программе, и измените их на qih прямо в тексте +сценария. + +Для защиты сервера от DoS-атак со стороны хакера нам помогут следующие пара­ +метры сервиса sendmail: +□ MaxDeamonChiidren— ограничение количества одновременно запущенных про­ +цессов. С помощью этого параметра мы можем защитить ресурсы сервера (про­ +цессор) от излишней перегрузки. По умолчанию установлено значение 12, но +для мощного компьютера его можно повысить, чтобы эффективнее использо­ +вать процессор, а для слабого — уменьшить; +□ connectionRateThrottie— максимальное количество открываемых соединений +в секунду. По умолчанию этот параметр равен 3, и повышать его без особой на­ +добности не стоит, разве что вы уверены в производительности сервера. + +#========================================== +### Локальный файл hosts + +Мы уже знаем, что изначально для сопоставления имен и адресов использовался +файл /etc/hosts. Это текстовый файл с записями типа: +127.0.0.1 localhost.localdomain localhost +192.168.77.1 FlenovM +Каждая строка — это соответствие IP-адреса его имени. По умолчанию в файле +будет всего две строки, как в приведенном примере. Первая строка — это «пет­ +ля». Напоминаю, что во всех компьютерах имя localhost и IP-адрес 127.0.0.1 ука­ +зывают на текущую машину. Это значит, что, если нужно выполнить ping, адре­ +совав запросы на локальный компьютер, можно написать: +ping 127.0.0.1 +Во второй записи устанавливается соответствие между IP-адресом, заданным для +вашего сетевого интерфейса, и символьным именем. Здесь моей сетевой карте при­ +своен адрес 192.168.77.1, И ему соответствует ИМЯ FlenovM. Это значит, что при вы­ +полнении команды ping можно указывать или IP-адрес, или имя компьютера. +Следующие две команды идентичны: +ping 192.168.77.1 +ping FlenovM +При выполнении второй команды сначала происходит обращение к файлу /etc/hosts, +который вернёт программе адрес 192.168.77.1 и уже на него направится эхо-запрос. +А что будет использоваться для поиска адреса первым: файл /etc/hosts или DNS-база +данных? Это зависит от настроек ОС. Посмотрим на файл /etc/host.conf. В нем нахо­ +дится строка: +order hosts,bind +Директива order как раз и задает порядок просмотра. В нашем случае на первом +месте находится файл /etc/hosts, и только после этого будет запущена команда bind +для выполнения запроса к DNS-серверу. Что это нам дает? А то, что можно увели­ +чить скорость доступа к основным серверам. Допустим, что вы каждый день посе­ +щаете сайт www.redhat.com. При этом каждый раз происходит запрос к DNS- +серверу, что может приводить к задержке в пару секунд перед началом загрузки +страницы. Чтобы ускорить этот процесс, можно вручную прописать в файле +/etc/hosts следующую запись: +209.132.183.105 +www.redhat.com +Вн и м ан и е! +Адрес 209.132.183.105 действительно соответствует сайту www.redhat.com на мо­ +мент подготовки этого издания книги, но может измениться. +Еслй сайт по каким-либо причинам перестал загружаться, то необходимо удалить +Соответствующую ЗаПИСЬ ИЗ файла hosts И С ПОМОЩЬЮ КОМаНДЫ ping redhat.com +проверить связь с сервером, а заодно и уточнить его адрес. В ответ на эту директи­ +ву на экране обязательно отображается реальный IP-адрес, с которым происходит +обмен эхо-сообщениями. Благо IP-адреса у большинства сайтов изменяются редко, +так что, один раз добавив такую запись в локальный файл /etc/hosts, можно сэконо­ +мить достаточно много времени и нервов в случае проблем с DNS-сервером, пото­ +му что запросов к нему отправляться не будет. +#### Внешние DNS-серверы +Если в локальном файле /etc/hosts не найдено записи о нужном имени, то компьютер +должен запросить эту информацию у DNS-сервера. Для этого нужно знать IP-адрес +этого самого сервера. Как система его узнает? Из файла /etc/resolv.conf, который +должен выглядеть примерно следующим образом: +search' FlenovM +domain domain.name +nameserver 10.1.1.1 +nameserver 10.1.1.2 + +В первой строке находится команда search с параметром (сервер поиска имени хос­ +та). В вашем файле, скорее всего, есть эта запись, и в качестве сервера вписано имя +вашего компьютера. Здесь может быть перечислено несколько серверов, разделен­ +ных пробелами или символами табуляции. Например: +search FienovM MyServer +Поиск на локальном сервере происходит довольно быстро, а вот на удаленных — +может отнять достаточно много времени. +Вторая строка содержит команду domain с параметром. Пользователи иногда любят +задавать имя компьютера без указания домена — например, redhat вместо +redhat. com. Вы должны задать полное имя узла. Чаще всего этот параметр настраи­ +вается в локальных сетях со специфичным именем домена. +Оставшиеся две строки содержат команду nameserv с параметром. Это внешний +DNS-сервер, которому будут направляться запросы. В системе их может быть не­ +сколько (в настоящее время для большинства дистрибутивов не более трех). Они +будут опрашиваться в порядке указания в файле, пока искомый адрес не будет +найден. +В большинстве случаев достаточно и одного сервера, потому что все они работают +рекурсивно. Но я рекомендую указывать два. Бывают случаи, когда один DNS- +сервер выходит из строя, тогда второй спасает положение и вступает в работу. +#### Настройка DNS-сервиса +В настоящее время наиболее распространенным сервисом DNS для Linux является +bind. Для этого сервиса существует программа bindconf, которая имеет графический +интерфейс и проста в использовании. Зайдите в графическую оболочку и в консоли +выполните команду: +bindconf & +Знак & говорит о том, что, запустив программу, консоль не должна дожидаться ее +завершения. Это очень удобно при запуске графических утилит, чтобы они не оста­ +навливали работу консоли. Учтите, что если закрыть окно консоли, то все про­ +граммы, запущенные с ключом &, тоже закроются. +На рис. 11.2 изображено запущенное приложение для настройки DNS-сервиса. +В центре показано окно, которое открывается по нажатию кнопки Добавить. Как +видите, достаточно только выбрать тип зоны и ввести имя домена — и все готово. +Несмотря на наличие простой графической программы, мы рассмотрим конфигура­ +ционные файлы, которые могут использоваться сервисом DNS. Их прямое редакти­ +рование позволит сделать более тонкую настройку, и вы лучше будете понимать +процесс работы DNS. +Настройка DNS-сервера начинается с файла /etc/named.conf. Пример его содержимо­ +го приведен в листинге 11.1. +```sh +options { +directory "/var/named/"; +}; +zone ”." { +type hint; +file "named.ca" ; +}; +zone "sitename.com" { +type mastery­ +file "sitename.zone"; +}; +zone "10.12.190.in-addr.arpa" { +type mastery­ +file "10.12.190.in-addr.arpa.zone"; +}; +``` +В этом примере файл разбит на четыре раздела, каждый из которых имеет следую­ +щий формат: +тип имя { +Параметр1; +Параметр2; +}; +Давайте рассмотрим назначение разделов. Первым идет options: +options { +directory "/vaг/named/"; +}; +В фигурных скобках только один параметр— directory, который указывает на +домашний каталог DNS-сервера. Все его файлы будут располагаться там. +Остальные разделы имеют тип zone и через пробел в кавычках содержат имя зоны. +В каждом из них по два параметра: type (определяет тип зоны) и file (файл, в ко­ +тором содержится описание). +Самая первая зона в нашем примере— zone ".". Что это за зона в виде точки? +Вспомните структуру DNS, которую мы рассматривали в начале главы. В базе дан­ +ных DNS так обозначается корень иерархии. Получается, что раздел описывает +корневую зону. Тип зоны hint, т. е. здесь хранятся лишь ссылки на DNS-cepfeepw. +Поскольку это корневая зона, то и ссылки будут на корневые серверы. +В параметре file указывается имя файла, содержащего все ссылки на корневые +серверы. В системе этого файла может и не быть, потому что информация в нем +подвержена изменениям, и лучше всего получить его последнюю версию с сервера +internic.net. Для этого выполните команду: +dig 0rs.internic.net . ns > named.са +А можно скачать этот файл непосредственно с FTP-сервера internic.net по адресу +ftp://ftp.rs.internic.net/domain/named.root и поместить в каталог /var/named. +Перейдем к следующему разделу zone "sitename.com". Здесь описывается зона +sitename.com. Тип записи master, значит, ваш DNS-сервер будет главным, а все +остальные станут только сверяться с ним и кэшировать информацию. Сведения об +этой зоне будут храниться в файле sitename.zone рабочего каталога. В нашем случае +это: /var/named. +Следующая зона описывает обратное преобразование IP-адресов 190.12.10.* в име­ +на. Тип записи — снова master, и в последней строке указан файл, где будет нахо­диться описание зоны. + +#========================================== +### Журналирование + +В Linux работают одновременно сразу несколько журналов, и по содержащейся +в них информации вы сможете вычислить хакера и увидеть, когда он проник в сис­ +тему, а также узнать еще много интересного. Так как журналирование — один из +инструментов обеспечения безопасности, мы рассмотрим журналы достаточно +подробно. Это позволит вам лучше контролировать свои владения. +#### Основные команды +Информация о текущих пользователях системы сохраняется в файле /var/run/utmp. +Если попытаться посмотреть его содержимое, например, командой cat, то ничего +хорошего нашему взору не откроется. Этот журнал хранит данные не в текстовом, +а бинарном виде, и получение информации из него возможно только с помощью +специализированных программ (команд). +who +Команда who позволяет узнать, кто сейчас зарегистрирован в системе и сколько вре­ +мени он в ней находится. Информация, как уже было отмечено, извлекается из +файла /var/run/utmp. Введите эту команду, и на экране появится список примерно сле­ +дующего вида: +robert , ttyl +Dec 8 10:15 +root +tty2 +Dec 8 11:07 +Из этого списка становится ясно, что пользователь robert работает за первым тер­ +миналом (ttyl) и вошел в систему 8 декабря в 10 часов 15 минут, а через 52 минуты +со второго терминала вошел пользователь root. +Большинство хакеров при входе в систему выполняют эту команду, чтобы выяс­ +нить, есть ли сейчас в системе администратор. Если пользователь root присутству­ +ет, то начинающие хакеры часто уходят, т. к. опасаются, что их знаний не хватит, +чтобы остаться незамеченными. При этом, вероятно, потом они вернутся. +А это еще одна причина, по которой администратор не должен входить в систему +под учетной записью root. Лучше всего работать как простой пользователь, а когда +не хватает прав, то переключаться на привилегированного. На такой случай я создал +учетную запись, для которой установил UID равным нулю. Она позволяет получить +доступ ко всей системе, и при этом имеет имя, отличное от root, и не вызывает по­ +дозрений, когда я под ним работаю. Так что в моей системе никогда нельзя увидеть +пользователя root. +users +Команда users также позволяет вытащить из журнала /var/run/utmp список всех поль­ +зователей, которые сейчас зарегистрированы в системе. +В этом журнале информация хранится временно — только в период присутствия +пользователя. Когда он выходит из системы, соответствующая запись удаляется. +После этого выяснить, кто и когда работал, удастся только из журнала /var/log/wtmp. +Это также бинарный файл, и его содержимое можно увидеть с помощью специали­ +зированных программ. +last +Команда last позволяет выяснить, когда и сколько времени определенный пользо­ +ватель находился в системе. В качестве параметра ей передается интересующее +имя. Например, следующая директива отображает время входа и продолжитель­ +ность нахождения в системе пользователя robert: +last Robert +Выполнив команду, вы увидите на экране примерно следующий текст: +robert +ttyl +Thu Dec 2 12:17 - 12:50 (00:33) +По этой записи можно понять, что robert находился за терминалом (ttyl), зашел +в систему 2 декабря на 33 минуты (с 12:17 до 12:50). Если пользователь работал не +локально, а через сеть, то будет отображена информация о хосте, с которого он +входил в систему. +Если выполнить эту команду для себя, то может вывалиться такой список, что чи­ +тать его будет невозможно, потому что вы довольно часто работаете в системе. +Чтобы ограничить выводимые данные, можно указать ключ -п и количество ото­ +бражаемых строк. Например, следующая команда выдаст информацию о последних +пяти входах: +last -п 5 robert +history +Команда history выводит историю выполненных в консоли команд. Список может +быть достаточно большим. Вы можете ограничить список, указав после имени +команды число. Следующий пример покажет только последние 10 выполненных +в консоли команд: +history 10 +lastlog +Если выполнить команду lastlog, то она выведет на экран перечень всех пользова­ +телей с датами их последнего подключения к системе. Пример результата ее вы­ +полнения можно увидеть в листинге 12.1. +Список состоит из четырех колонок: +“1 имя пользователя из файла/etc/passwd; +□ порт или терминал, на который происходило подключение; +□ адрес компьютера, если вход был по сети; +□ время входа. +С помощью lastlog удобно контролировать системные записи. У них дата послед­ +него входа должна быть **Never logged in**, потому что под ними нельзя войти +в систему (в качестве командных оболочек установлены /bin/false, /dev/null, /sbin/nologin +и др.). Если вы заметили, что кто-либо вошел в систему через одну из этих учетных +записей, то это значит, что хакер использует ее, изменив настройки. +Простая замена командной оболочки в файле /etc/passwd может открыть хакеру +«потайную дверь», и администратор не заметит этой трансформации. Но после +выполнения команды lastlog все неявное становится явным. +Обращайте внимание на тип подключения и адрес. Если что-то вызывает подозре­ +ние, то можно выявить атаку на этапе ее созревания. +Isof +С помощью команды lsof можно определить, какие файлы и какими пользователя­ +ми открыты в текущий момент. Результат ее выполнения приведен в листинге 12.2. +Это далеко не полный результат. Даже если вы сейчас один работаете с системой, +количество открытых файлов может исчисляться парой десятков, и число их замет­ +но растет, если в системе несколько пользователей, — ведь один файл может от­ +крываться несколько раз каждым из них. Это касается в основном системных кон­ +фигурационных файлов. +#### Пользовательские журналы +Все команды, выполненные пользователем, сохраняются в файле .bash_history (если +используется интерпретатор команд /bin/bash), который находится в пользователь­ +ском домашнем каталоге. Когда вы определили, под какой учетной записью в сис­ +теме находился взломщик, его действия можно проследить по этому журналу. +Вы сможете узнать, какие команды или программы выполнялись, и эта информация +подскажет, как злоумышленник проник в систему и что изменил. Если хакер добавил +пользователя или модифицировал какой-либо важный системный файл, то вы это +увидите, сможете вернуть все в исходное состояние и тем самым быстро закрыть +«двери» в системе, которые открыл хакер. + +#========================================== +### Резервирование в Linux + +Самый простой вариант создания резервной копии — использование команды ср +(копирование файлов). Только при копировании необходимо обязательно сохранять +права доступа к файлу. Вот как может выглядеть команда, сохраняющая каталог +/home на примонтированный к системе диск /mnt/resdisk: +ср -a /home /mnt/resdisk +Здесь я использовал ключ -а, который равносилен указанию сразу трех ключей +(-dpR): +□ -d — не следовать по символьным ссылкам. Мы копируем каталог в таком виде, +как он есть; +□ -р — сохранять права доступа к файлу; +□ -R — копировать каталоги рекурсивно, чтобы на архивный диск попали все фай­ +лы и подкаталоги. +Получается, что команда, показанная ранее, идентична следующей: +ср -dpR /home /mnt/resdisk +С помощью этой директивы мы создаем полную копию каталога. А как можно со­ +хранить изменения? Для этого необходимо произвести копирование на тот же диск, +только с указанием ключа -и: +ср -аи /home /mnt/resdisk +При этом будут скопированы все файлы из каталога /home, дата изменения которых +позже, чем у документов из каталога /mnt/resdisk. +#### Утилита tar +Копировать по одному файлу не очень удобно. Лучше, когда все находится под од­ +ной крышей. В Linux есть утилита tar, которая собирает все файлы в один. Этот +процесс называют архивированием, но вы должны учитывать, что tar не сжимает +файлы. Если вы объединяете файлы общим размером в 2 Мбайт, то архив будет +иметь размер чуть больше (размер всех файлов плюс заголовок tar). +Преимущество сборки целого каталога в один файл состоит в том, что им проще +потом управлять и сжимать специализированными программами. +Для архивирования как минимум нужно выполнить команду: +tar cf архив.tar каталог +Здесь у нас два параметра: +П с — указывает на необходимость создания архива; +□ f — назначает архивный файл или устройство. По умолчанию используется уст­ +ройство /dev/rmtO. +Итак, для архивирования каталога /home выполняем следующую команду: +tar cf backup.tar /home +При использовании параметров cf в архиве сохраняется и путь к файлам. Если рас­ +паковать созданный нами архив, то в результате в текущем каталоге будет создан +каталог home, а в нем уже будут располагаться все пользовательские каталоги. На­ +пример, если запустить команду разархивирования в каталоге /home, то каталоги +пользователей окажутся в /home/home, а если находиться в /etc, то пользователи уви­ +дят свои каталоги в /etc/home. +Чтобы добиться правильного результата, нужно перейти в корень диска и выпол­ +нять команду там: +cd / +tar xf /home/backup.tar +Здесь мы из корневого каталога разархивируем резервную копию, которая находит­ +ся в файле /home/backup.tar. +Помимо этого, для архивных операций могут пригодиться следующие ключи ути­ +литы tar: +□ v— вывести на экран информацию об архивируемом (или распаковываемом) +в данный момент файле; +□ z — найти и обработать при распаковке GZIP-архивы; +□ р — разархивировать всю информацию о безопасности; +□ d — найти различия между архивом и файлами системы; +О t — просмотреть содержимое архива; +□ и — обновить файлы в архиве, которые были изменены; +□ n date — добавить в архив только те файлы, которые изменены позже указанной +даты. Параметр date должен быть заменен необходимой датой; +□ р — не удалять первый символ /. В этом случае, в каком каталоге вы ни запусти­ +те разархивирование, файлы попадут на свое родное место. +С помощью утилиты tar можно архивировать сразу несколько каталогов. Следую­ +щая команда помещает в архив каталоги /home и /etc: +tar cf backup.tar /home /etc +Чтобы просмотреть содержимое архива, можно выполнить команду: +tar tvf backup.tar +В ответ на это на экране будут показаны все файлы и каталоги архива, их права +доступа и владельцы. +Посмотрите на последнюю колонку, где показано расположение файла. Обратите +внимание, что путь не начинается с символа /, указывающего на корень диска. По­ +этому такой архив нужно распаковывать в корне, иначе он будет создаваться +в текущем каталоге. +С помощью tar очень удобно и легко создавать резервные копии, которые содержат +только изменения. Для этого мы просто задаем ключ -д и указываем какой-то файл, +который будет использоваться для отслеживания того, какие файлы уже резервиро­ +вались и какая версия уже находится в архиве. +Давайте рассмотрим все это на примере: +tar -cf backupO.tar -g home-flenov.snar /home/flenov +Здесь: +□ c — указывает на необходимость создания архива; +□ f — указывает на то, что мы хотим задать определенное имя файла и это имя +идет сразу после ключа: backupO.tar; +□ g — указывает на то, что нужно копировать только изменения. После этого клю­ +ча надо указать файл, где будут храниться изменения. В качестве его имени я +использую полный путь к нему, просто вместо слэшей указываю символ тире. +Я заметил, что очень часто таким файлам дают расширение snar. Я точно не +знаю, но мне кажется, что это объединение двух слов: sn, которое указывает +на слово snapshot (снимок), и аг, которое указывает на архив tar. Возможно, я тут +не прав, но в любом случае расширение может быть и другим; +□ ну и в конце идет каталог, резервную копию которого вы хотите создать. +Запустите команду: tar создаст полную резервную копию, если файл flenov.snar не +существует. +Теперь попробуйте изменить пару файлов или создать новый и снова выполнить +команду: +tar -cf backupl.tar -g home-flenov.snar /home/flenov +Я увеличил число в имени файла архива с 0 до 1. В этот файл уже попадут только +изменения с момента последней резервной копии. +#### Утилита gzip +В ОС Linux есть достаточно много различных утилит для упаковки данных. Наибо­ +лее популярной из них является gzip. Преимущество архивирования перед простой +упаковкой данных заключается в том, что результирующая копия занимает меньше +места, а значит, носитель для резервирования понадобится меньшего объема. +Чаще всего копируются документы, размер которых в заархивированном виде мо­ +жет уменьшаться на 90%. Текстовые данные сжимаются намного лучше, чем про­ +граммы. +Недостаток архивирования — возрастает нагрузка на процессор, и может потребо­ +ваться больше времени на создание полной копии. Однако за счет того, что архив +занимает намного меньше места, его копирование на сетевые ресурсы или запись +на съемные носители (ZIP, JAZ, CD-R/RW, DVD-R/RW и др.) производится быст­ +рее. В итоге может выйти, что затраты времени на копирование и процессорного +времени на архивирование окажутся идентичны затратам на копирование без архи­ +вирования или даже меньше. +Прежде чем сжимать какой-либо файл, рекомендуется подготовить TAR-архив. По­ +том достаточно выполнить команду упаковки: +gzip -уровень файл.tar +В качестве ключа -уровень нужно указать степень компрессии. Максимальный уро­ +вень равен 9. После этого указывается имя TAR-архива. Давайте сожмем архивный +файл, который мы создали из каталога /home, применяя наибольшую компрессию. +Выполните следующую команду: +gzip -9 backup.tar +Теперь просмотрите содержимое каталога (команда is). Обратите внимание, что +файла backup.tar больше нет. Вместо него появился файл backup.tar.gz, размер кото­ +рого значительно уменьшился. +Чтобы разархивировать такой файл, можно пользоваться все той же командой tar, +только необходимо указать ключи xf z: +cd / +tar xfz /home/backup.tar. gz +Эта команда сначала разархивирует GZ-файл и тут же распакует TAR-архив. +Если необходимо из GZ-файла снова получить TAR-архив (без его распаковки), то +можно выполнить команду: +gzip -d /home/backup.tar.gz +После этого вы снова увидите файл backup.tar, а файл backup.tar.gz исчезнет. +Теперь вы готовы написать свой сценарий, который будет собирать каталоги для +архивирования в TAR-файл, а затем сжимать его, чтобы уменьшить размер. Но +можно не использовать две команды, а обойтись одной. Вот как это делается: +tar cvf - /home | gzip -9с > backup.tar.gz +Здесь мы собираем в TAR-архив каталог /home и тут же сжимаем его утилитой gzip. +Помимо gzip для архивирования иногда используется утилита compress, но ее воз­ +можности по сжатию ниже, и к тому же вокруг нее возникали скандалы и разбира­ +тельства по поводу лицензии. Большинство администраторов уже перешли на ис­ +пользование gzip, и я вам рекомендую с самого начала привыкать к этой программе. +#### Утилита dump +Все предыдущие команды, которые мы рассматривали в этой главе, не являются +специализированными командами резервирования. Это просто команды копирова­ +ния и архивирования файлов. А вот утилита dump предназначена именно для соз­ +дания резервной копии файловой системы Ext (поддерживаются версии, начиная со +второй). +Для выполнения резервной копии нужно как минимум указать: +□ -п — уровень резервной копии, где п может изменяться от о до 9. При значе­ +нии о создается полная резервная копия. Уровни выше о означают формирова­ +ние резервной копии изменений, произошедших с момента последней полной +резервной копии или создания копии с меньшим уровнем; +□ -и— требование при удачном завершении резервирования обновить файл +/etc/dumpdates, в котором хранятся даты создания резервных копий; +□ -f файл— имя файла или устройство, на которое нужно производить резервное +копирование. +Итак, простейшая команда создания полной резервной копии выглядит следующим +образом: +dump -Ou -f /home/backup.bak +Для сохранения изменений нужно изменить уровень, указав значение больше нуле­ +вого, например: +dump — lu -f /home/backup.bak +Для восстановления файлов из архива служит команда restore. Но прежде чем ее +запускать, вы должны убедиться, что находитесь в каталоге, который принадлежит +восстанавливаемой файловой системе. +В команде restore достаточно только указать ключ -f и файл, который нужно вос­ +становить. Если применить ключ -i, то вы попадаете в интерактивный режим, +в котором можно задать файлы для восстановления. Интерактивный режим похож +на режим командной строки, только вместо реальной файловой системы в нем вы +путешествуете по архиву. При этом можно выполнять следующие директивы: +□ help — вывести краткую помощь по доступным командам; +□ is — отобразить содержимое текущего каталога; +□ pwd — показать текущий каталог; +□ add каталог — добавить в список для восстановления указанный в качестве ар­ +гумента каталог; +□ cd — сменить текущий каталог; +□ delete каталог — удалить из списка восстановления каталог, указанный в каче­ +стве параметра; +□ extract — восстановить все файлы из созданного списка; +□ quit — ВЫХОД. + +#========================================== +### CHAGE + +Как заставить пользователя менять пароли через определенные промежутки време­ +ни? Нет, не нужно ходить и нудить ему над ухом — есть способ проще и намного +эффективнее. В Linux имеется утилита chage, которая запускается следующим об­ +разом: +chage параметры пользователь +В качестве параметра можно указывать следующие ключи: +□ -mN— минимальное число дней (n ) д о смены пароля. Указав это значение чуть +меньше, чем максимальный период (см. следующий параметр), вы защитите +систему от нежелательной смены паролей. Это значит, что если хакер захватит +учетную запись, он не сможет изменить пароль. Конечно же, злоумышленник +тоже может выполнить команду chage, но только если у него есть права админи­ +стратора. Три-четыре дня разницы между минимальным и максимальным значе­ +нием необходимы для того, чтобы пользователь смог сменить пароль, пока он не +устарел. Устанавливать разницу меньше трех дней нежелательно, потому что +существуют выходные, и если срок действия попадет на воскресенье, пользо­ +ватель не успеет сменить пароль. По умолчанию используется значение -1, что +соответствует отсутствию проверки; +□ -м N — максимальный диапазон в днях (n ), в течение которого действует пароль. +После этого пароль считается недействительным, и пользователь не сможет вой­ +ти в систему. По умолчанию установлено 99999, что соответствует бесконечно­ +сти, а значит, пароль никогда не устареет; +□ -d N— дата последнего изменения пароля. Параметр N указывает количество +дней начиная с 1 января 1970 года. Если установить юоо, то получится 27 сен­ +тября 1972 года. Чтобы не высчитывать дату в днях, можно указать ее явным +образом в формате ГГГГ-ММ-ДЦ; +□ -Е дата — дата окончания действия пароля; +□ -I N— период в днях, после которого неиспользуемая учетная запись блокиру­ +ется. Рекомендую указать не менее 3 и не более 4 дней, чтобы приостановить +действие записи на время отпуска или болезни работника; +□ -w n — количество дней до окончания срока действия пароля, когда пользовате­ +лю будет выводиться предупредительное сообщение. Нежелательно указывать +менее 3 дней, чтобы не попасть на выходные; +□ -1 пользователь — с этим параметром команда может вызываться любыми поль­ +зователями и позволяет им узнать информацию о времени жизни их пароля. +Чтобы получить сведения о пароле root, выполните директиву: +chage -1 root. +Результат выполнения команды имеет следующий вид: +Password Expires: Never +Password Inactive:Never +Minimum: +-1 +Maximum: +99999 +Warning: +Inactive: +-1 +-1 +Last Change: +Feb 04, 2004 +Account Expires: +Never +Здесь отображаются следующие значения: +• Minimum — минимальный срок действия пароля; +• Maximum— максимальный период для пользования паролем; +• warning— количество дней, за которые будет выдаваться предупреждение +о завершении срока действия пароля; +• inactive— максимальное количество дней, в течение которых учетная за­ +пись может не активироваться; +• Last change — последняя дата изменения; +• Password Expires — дата окончания действия пароля; +• Password inactive — дата, когда пароль стал неактивным; +• Account Expires — дата окончания действия учетной записи. +Чтобы задать максимальное количество дней жизни пароля в 60 дней, выполните +команду: +chage -М 60 robert +Слишком частая смена паролей приводит к тому, что пользователи просто не успе­ +вают запомнить их. Из-за этого сложные комбинации начинают писать на бумаге, +чтобы случайно не забыть, или просто меняют пароль на старый. Ваша задача — +контролировать замену, и в то же время не стоит заставлять пользователя делать +это слишком часто. Период в 2-3 месяца (или 60-90 дней) считается вполне прием­ +лемым. +А как проверить, что выбран достаточно сложный пароль и при этом не указан снова +старый? В этом нам поможет РАМ-модуль pam_cracklib.so, который выполняет +основные проверки и позволяет сделать их сложнее. Например, нельзя будет устано­ +вить старый пароль или воспользоваться большей его частью. + +#========================================== diff --git a/LINUX/iso fs _ ISO9660.md b/LINUX/iso fs _ ISO9660.md new file mode 100644 index 0000000..0ca6fa5 --- /dev/null +++ b/LINUX/iso fs _ ISO9660.md @@ -0,0 +1,46 @@ +Так, а чем по своей сути вообще является iso файл? Грубо говоря, это несжатый архив с точной копией данных блочного устройства **(hdd, ssd, cd)** в формате файловой системы **ISO9660**: + +`file linuxmint-22.1-xfce-64bit.iso + +linuxmint-22.1-xfce-64bit.iso: ISO 9660 CD-ROM filesystem data (DOS/MBR boot sector) ... (bootable) + +В зависимости от содержимого образ может быть загрузочным и не загрузочным. Давайте из приведенного выше описания iso-шника выведем возможные способы работы с ним: +1) он представляет собой архив - его можно **распаковать**; +2) он содержит файловую систему ISO9660 - его можно **монтировать**; + +Начнем с "**распакоуки**". Для этого можем воспользоваться утилитой **7z**: + +`sudo apt install p7zip-full +$ 7z x linux*.iso -o./extract + +После завершения процесса мы вольны вносить в файловую структуру свои коррективы. После правок нужно будет заново собрать установочный iso образ. Для этого есть утилитка **mkisofs**, которая корректно собирает **ISO9660** архивы из файлов и каталогов. + +**Второй способ** - монтировать образ и скопировать его содержимое.  + +В привычном для нас понимании, монтирование относится к блочным устройствам **(usb, ssd, hdd)**, которые содержат определенную файловую систему **(exFAT, ext, zfs)**. Такой механизм позволяет нам получить доступ к содержимому носителей. + +Но вот в чем дело, **iso** файл не является **блочным устройством** и его нельзя просто так примонтировать... Ничего, есть решение - механизм **loop device**. + +**Loop device** — это виртуальное блочное устройство, которое позволяет использовать обычный ресурс с файловой системой, как физический девайс. Через него можно смонтировать образ и получить доступ к его файловой системе.  + +Посмотреть то, какое устройство с каким ресурсом соотносится, можно через команду **losetup**: + +`sudo losetup -a + +/dev/loop13: [64512]:2672215 (/path/to/output.iso) + +Для того, чтобы создать и монтировать "**loop device**", используем **mount**: + +$ sudo mount -o loop test.iso ./mnt_point + +Теперь, т.к. файловая система **ISO9660** является "**read-only**", нам нужно скопировать все содержимое образа в отдельный каталог: + +`mkdir new_mnt_point + +`cp -rT mnt_point new_mnt_point + +Далее "по уму" редактируем образ и пересобираем его с помощью **mkisofs**: + +`sudo mkisofs -o ./new.iso -quiet -R -b boot/grub/bios.img -no-emul-boot -boot-load-size 4 -boot-info-table ./new_mnt_point + +Если все сделано правильно, на выходе мы получим "**bootable**" образ. \ No newline at end of file diff --git a/LINUX/tr.md b/LINUX/tr.md new file mode 100644 index 0000000..34a584a --- /dev/null +++ b/LINUX/tr.md @@ -0,0 +1,84 @@ +**Прокачиваемся в tr** + +Более глубокий взгляд на tr +`cat file | tr [:lower:] [:upper: ] + +Сегодня у нас на разборе одна из культовых утилит "**tr**". Используется она для форматирования текста: удаления и замены символов. + +Чаще всего можно встретить в паре со сторонними командами, которые выводят в **stdout** текстовый поток: **cat, head, echo** и т.д. + +Команда имеет следующий вид: + +$ tr флаг SET1 [SET2] + +Сетами представлены наборы символов, с которыми **tr** будет работать. Второй набор не всегда используется.  + +Команда разбирает текст посимвольно и выполняет операцию для каждого элемента отдельно. + +Флаги команды tr: +1) **-d (--delete)**: удаляет элементы из первого набора: +$ echo "Hello 123 world" | tr -d ' a-zA-Z' +123 +1) **-c (--complement)**: обработать каждый символ, который не входит в набор "**SET1**": +$ echo "Hello 123 world" | tr -cd 'a-zA-Z\n' +Helloworld + +В примере мы удалили все символы, которые не входят в перечисление '**a-zA-Z\n**'; + +Таким же макаром можно все буквы удалить и оставить только цифры: + +$ cat customer.csv + +USA,1234567890 + +Canada,0987654321 + +$ cat customer.csv | tr -cd '0-9\n' + +1234567890 + +0987654321 + +1) **-s (--squeeze-repeats)**: cжимает повторяющиеся символы из первого набора до одного: + +$ echo "Hello    World" | tr -s ' ' + +Hello World + +1) **-t (--truncate-set1)**: обрезает первый набор до длины второго: + +$ echo "abcdef" | tr -t 'abcdef' '123' + +123def + +Если длина первого набора больше и мы не указываем флаг, каждый последующий его символ, который (**по номеру > конец "SET2"**), будет заменен: + +$ echo "abcdef" | tr 'abcdef' '123' + +123333 + +При своем дефолтном поведении, программа заменяет символы из "**SET1**" на элементы "**SET2**": + +$ echo "Hello World" | tr 'a-z' 'A-Z' + +HELLO WORLD + +Команда умеет парсить шаблоны: **[:upper:], [:lower:]** и т.д. Детальнее можно ознакомиться в мануале: + +$ echo "hello world" | tr [:lower:] [:upper:] + +HELLO WORLD + +Данные на вход можно получить и через файлик. Для этого мы используем перенаправление: + +$ cat infile + +www.baeldung.com + +$ tr 'a-z' 'A-Z' < infile + +WWW.BAELDUNG.COM + +Измененные данные нужно куда-то записать? Правильно, перенаправляем вывод: + +$ tr 'a-z' 'A-Z' < infile > outfile \ No newline at end of file diff --git "a/LINUX/\320\220\320\262\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201\320\272\320\276\320\265 \320\276\320\261\320\275\320\260\321\200\321\203\320\266\320\265\320\275\320\270\320\265 \320\277\320\276\320\264\320\276\320\267\321\200\320\270\321\202\320\265\320\273\321\214\320\275\321\213\321\205 \320\277\321\200\320\276\321\206\320\265\321\201\321\201\320\276\320\262 \320\262 Linux.md" "b/LINUX/\320\220\320\262\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201\320\272\320\276\320\265 \320\276\320\261\320\275\320\260\321\200\321\203\320\266\320\265\320\275\320\270\320\265 \320\277\320\276\320\264\320\276\320\267\321\200\320\270\321\202\320\265\320\273\321\214\320\275\321\213\321\205 \320\277\321\200\320\276\321\206\320\265\321\201\321\201\320\276\320\262 \320\262 Linux.md" new file mode 100644 index 0000000..4e359d0 --- /dev/null +++ "b/LINUX/\320\220\320\262\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201\320\272\320\276\320\265 \320\276\320\261\320\275\320\260\321\200\321\203\320\266\320\265\320\275\320\270\320\265 \320\277\320\276\320\264\320\276\320\267\321\200\320\270\321\202\320\265\320\273\321\214\320\275\321\213\321\205 \320\277\321\200\320\276\321\206\320\265\321\201\321\201\320\276\320\262 \320\262 Linux.md" @@ -0,0 +1,27 @@ +Этот скрипт помогает находить потенциально вредоносные или несанкционированные процессы, которые маскируются под системные, используют необычные порты или расположены в нетипичных путях. +```sh +#!/bin/bash + +echo "Поиск подозрительных процессов..." + +# Процессы, запущенные из /tmp, /dev, /run — подозрительно +echo "Процессы из временных директорий:" +ps axeo pid,comm,args | grep -E '/(tmp|dev|run)/' | grep -v grep + +# Процессы без имени (заглушки или rootkit-обфускация) +echo -e "\n❓ Процессы без имени:" +ps -eo pid,comm | awk '$2 == "[]" || $2 == ""' + +# Подозрительные net-соединения (в обход ssh, http, https) +echo -e "\n🌐 Нетипичные входящие соединения:" +sudo lsof -i -nP | grep LISTEN | grep -Ev ':(22|80|443)' | grep -v "COMMAND" +``` + +🟢 Ищет процессы в нестандартных директориях (/tmp, /dev) +🟢 Показывает безымянные процессы — возможные признаки rootkit +🟢 Выводит порты, на которых слушают нетипичные демоны + +➡️ **Можно добавить в crontab для ежедневного обновления**: + +`0 * * * * /path/to/proc_check.sh >> /var/log/proc_scan.log + diff --git "a/LINUX/\320\220\320\262\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201\320\272\320\276\320\265 \320\276\321\202\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265 \320\267\320\260\320\262\320\270\321\201\321\210\320\270\321\205 \320\277\321\200\320\276\321\206\320\265\321\201\321\201\320\276\320\262 \320\262 Linux.md" "b/LINUX/\320\220\320\262\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201\320\272\320\276\320\265 \320\276\321\202\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265 \320\267\320\260\320\262\320\270\321\201\321\210\320\270\321\205 \320\277\321\200\320\276\321\206\320\265\321\201\321\201\320\276\320\262 \320\262 Linux.md" new file mode 100644 index 0000000..bb7ee74 --- /dev/null +++ "b/LINUX/\320\220\320\262\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201\320\272\320\276\320\265 \320\276\321\202\320\272\320\273\321\216\321\207\320\265\320\275\320\270\320\265 \320\267\320\260\320\262\320\270\321\201\321\210\320\270\321\205 \320\277\321\200\320\276\321\206\320\265\321\201\321\201\320\276\320\262 \320\262 Linux.md" @@ -0,0 +1,20 @@ +Этот скрипт проверяет системную нагрузку, выявляет зависшие процессы (с высоким временем работы) и автоматически завершает их, если они мешают работе системы. +```sh +#!/bin/bash + +# Пороговое время работы процесса (в секундах) +THRESHOLD=600 + +# Поиск и завершение зависших процессов +ps -eo pid,etimes,comm --no-headers | while read pid etime command; do + if [ "$etime" -gt "$THRESHOLD" ]; then + echo "Процесс $command (PID $pid) завис! Завершаем..." + kill -9 "$pid" + fi +done +``` + +🟢 Скрипт автоматически ищет процессы, которые работают дольше указанного времени, и принудительно завершает их. +🟢 Полезен для предотвращения зависания системы из-за долгих или неответственных процессов. + +Можно расширить функционал и добавить отправку уведомлений в Telegram, чтобы контролировать, какие процессы были завершены. \ No newline at end of file diff --git "a/LINUX/\320\221\320\265\320\267\320\276\320\277\320\260\321\201\320\275\320\276\321\201\321\202\321\214 Linux - \320\277\321\200\320\276\321\201\321\202\321\213\320\265 \321\210\320\260\320\263\320\270.md" "b/LINUX/\320\221\320\265\320\267\320\276\320\277\320\260\321\201\320\275\320\276\321\201\321\202\321\214 Linux - \320\277\321\200\320\276\321\201\321\202\321\213\320\265 \321\210\320\260\320\263\320\270.md" new file mode 100644 index 0000000..d6da61c --- /dev/null +++ "b/LINUX/\320\221\320\265\320\267\320\276\320\277\320\260\321\201\320\275\320\276\321\201\321\202\321\214 Linux - \320\277\321\200\320\276\321\201\321\202\321\213\320\265 \321\210\320\260\320\263\320\270.md" @@ -0,0 +1,29 @@ +Linux сам по себе безопасная система, но небольшие улучшения могут значительно повысить её защиту. Эти команды и практики помогут вам укрепить систему. + +**Обновить пакеты и установить критические исправления** +`sudo apt update && sudo apt upgrade + +**Включить автоматическую установку обновлений безопасности** +`sudo apt install unattended-upgrades +`sudo dpkg-reconfigure unattended-upgrades + +**Установить и настроить брандмауэр UFW** +`sudo apt install ufw +`sudo ufw enable +`sudo ufw default deny incoming +`sudo ufw default allow outgoing +`sudo ufw allow ssh + +**Проверить активные сетевые соединения** +`sudo lsof -i -P -n | grep LISTEN + +**Проверить наличие rootkit'ов** +`sudo apt install rkhunter +`sudo rkhunter --check + +🟢 unattended-upgrades автоматически ставит обновления +🟢 ufw защищает от несанкционированного доступа +🟢 rkhunter помогает находить потенциальные угрозы + +🗣 Регулярная проверка и настройка безопасности особенно важна для серверов и публичных систем. + diff --git "a/LINUX/\320\221\321\213\321\201\321\202\321\200\320\276 \320\277\320\276\321\201\320\274\320\276\321\202\321\200\320\265\321\202\321\214 \320\262\320\265\321\200\321\201\320\270\321\216 \320\270 \320\275\320\260\320\267\320\262\320\260\320\275\320\270\320\265 \320\264\320\270\321\201\321\202\321\200\320\270\320\261\321\203\321\202\320\270\320\262\320\260 linux.md" "b/LINUX/\320\221\321\213\321\201\321\202\321\200\320\276 \320\277\320\276\321\201\320\274\320\276\321\202\321\200\320\265\321\202\321\214 \320\262\320\265\321\200\321\201\320\270\321\216 \320\270 \320\275\320\260\320\267\320\262\320\260\320\275\320\270\320\265 \320\264\320\270\321\201\321\202\321\200\320\270\320\261\321\203\321\202\320\270\320\262\320\260 linux.md" new file mode 100644 index 0000000..7a44420 --- /dev/null +++ "b/LINUX/\320\221\321\213\321\201\321\202\321\200\320\276 \320\277\320\276\321\201\320\274\320\276\321\202\321\200\320\265\321\202\321\214 \320\262\320\265\321\200\321\201\320\270\321\216 \320\270 \320\275\320\260\320\267\320\262\320\260\320\275\320\270\320\265 \320\264\320\270\321\201\321\202\321\200\320\270\320\261\321\203\321\202\320\270\320\262\320\260 linux.md" @@ -0,0 +1,4 @@ +а так же на базе чего он построен. + +==`cat /etc/*release* + diff --git "a/LINUX/\320\230\321\201\320\277\321\200\320\260\320\262\320\270\321\202\321\214 USB, \321\201\320\274\320\276\320\275\321\202\320\270\321\200\320\276\320\262\320\260\320\275\320\275\321\203\321\216 \320\272\320\260\320\272 read-only \320\262 Linux.md" "b/LINUX/\320\230\321\201\320\277\321\200\320\260\320\262\320\270\321\202\321\214 USB, \321\201\320\274\320\276\320\275\321\202\320\270\321\200\320\276\320\262\320\260\320\275\320\275\321\203\321\216 \320\272\320\260\320\272 read-only \320\262 Linux.md" new file mode 100644 index 0000000..b74f25d --- /dev/null +++ "b/LINUX/\320\230\321\201\320\277\321\200\320\260\320\262\320\270\321\202\321\214 USB, \321\201\320\274\320\276\320\275\321\202\320\270\321\200\320\276\320\262\320\260\320\275\320\275\321\203\321\216 \320\272\320\260\320\272 read-only \320\262 Linux.md" @@ -0,0 +1,39 @@ +Иногда Linux монтирует флешку или диск в режиме только для чтения (`read-only`), что блокирует любые операции записи. Вот пошаговое решение: + +🔹 Шаг 1: Проверка и исправление файловой системы + +`sudo umount /dev/sdX1 +`sudo fsck -f /dev/sdX1 + +Замените sdX1 на имя вашего устройства (узнать можно через `lsblk`). + +🔹 Шаг 2: Примонтировать с правами на запись + +`sudo mount -o remount,rw /dev/sdX1 + +🔹 Шаг 3: Физическая защита от записи + +Некоторые USB-накопители имеют переключатель блокировки записи. Убедитесь, что он отключён. + +🔹 Шаг 4: Аппаратная блокировка из-за износа + +Если dmesg показывает: + +Write-protect is on + +— это может означать, что флешка изношена и контроллер принудительно заблокировал запись. Здесь помочь может только замена. + +🔹 Шаг 5: Очистка и форматирование (если данные не важны) + +`sudo wipefs -a /dev/sdX +`sudo mkfs.vfat /dev/sdX1 + +📌 Быстрая таблица + +| 🚩 Проблема | 💡 Решение | +|-------------------------------|----------------------------------------| +| Ошибки в файловой системе | `fsck` и перемонтирование | +| Случайный `read-only` | `mount -o remount,rw` | +| Защита от записи физическая | Проверить переключатель | +| Аппаратная блокировка | Устройство под замену | +| Данные не важны | `wipefs` + `mkfs` | diff --git "a/LINUX/\320\232\320\260\320\272 \321\200\320\260\320\267\320\261\320\270\321\202\321\214 \321\204\320\260\320\271\320\273 \320\275\320\260 \321\207\320\260\321\201\321\202\320\270.md" "b/LINUX/\320\232\320\260\320\272 \321\200\320\260\320\267\320\261\320\270\321\202\321\214 \321\204\320\260\320\271\320\273 \320\275\320\260 \321\207\320\260\321\201\321\202\320\270.md" new file mode 100644 index 0000000..88b812e --- /dev/null +++ "b/LINUX/\320\232\320\260\320\272 \321\200\320\260\320\267\320\261\320\270\321\202\321\214 \321\204\320\260\320\271\320\273 \320\275\320\260 \321\207\320\260\321\201\321\202\320\270.md" @@ -0,0 +1,71 @@ +В некоторых случаях один файл нужно разбить на несколько частей, например, для загрузки либо копирования в случае слишком большого размера. Проще всего это сделать с помощью утилиты ==split. + +Команда используется для разбивки файлов по размеру, количеству строк и на определенное количество составных частей. +### Разбить по размеру + +Для этого используем опцию "-b", определяющую максимальный размер элемента. За основу возьмем файлик в 721 байт. Его нужно разбить на части по 100 байт. Для удобства зададим префикс имени "file-part_": + + +`$ ls -l +`721 Apr 20 13:11 file_orig + + +`$ split -b 100 ./file_orig ./file-part_ + + +Помимо оригинала появятся еще 8 файлов: + + +`$ ls -l +`721 Apr 20 13:11 file_orig +`100 Apr 20 13:12 file-part_aa +`... +`21 Apr 20 13:12 file-part_ah + + +Разбить по количеству строк + +Может быть нужно разбить один текстовый документ на несколько, с количеством строк "<= n". В этом вопросе полезной окажется опция "-l". Команда поделит большой log-файл на части по 10 тысяч строк в каждом: + + +`$ split -l 1000 ~/Logs/log ~/Logs/log-part_ + +### Разбить на определённое количество файлов + +Еще одна интересная задача, в решении которой поможет опция "-n". Размер будет, по возможности, поделен поровну. Достаточно прописать итоговое количество файлов и запустить команду: + + +`$ split -n 3 ./file_orig ./file-part_ + + +`$ ls -l +`240 Apr 20 13:31 file-part_aa +`240 Apr 20 13:31 file-part_ab +`241 Apr 20 13:31 file-part_ac +`721 Apr 20 13:11 file_orig + + +Настройка имени частей файла + +Как мы уже поняли, для split префикс определяет название части файла, после чего идет дефолтный суффикс из двух латинских букв. + +С помощью опций можно изменить его длину "-a", переключиться на числа "-d" или hex-символы "-x". В последних двух сценариях получится выбрать начальную точку отчета "--numeric-suffixes" для чисел и "--hex-suffixes" для hex-символов. + +Возьмем задачу – разделить текстовый файл на 3 части равного размера, чтобы каждый из них имел префикс "file-part_" и числовой суффикс из одного символа, начиная с 1: + + +$ split -a 1 --numeric-suffixes=1 -n 3 ./file_orig ./file-part_ + + +$ ls -l +240 Apr 20 13:36 file-part_1 +240 Apr 20 13:36 file-part_2 +241 Apr 20 13:36 file-part_3 + + +Объединение частей файла + +Следующий шаг – объединение нескольких частей в единый файл. Для этих целей отлично подойдет утилита cat. Сначала нужно задать имена частей, потом итоговый файл: + + +`cat ~/Archive/split-archive.part_* > ~/Archives/cat-archive.tar.gz diff --git "a/LINUX/\320\232\320\260\320\272 \321\203\320\264\320\260\320\273\321\217\321\202\321\214 \321\201\321\201\321\213\320\273\320\272\320\270.md" "b/LINUX/\320\232\320\260\320\272 \321\203\320\264\320\260\320\273\321\217\321\202\321\214 \321\201\321\201\321\213\320\273\320\272\320\270.md" new file mode 100644 index 0000000..404e9a9 --- /dev/null +++ "b/LINUX/\320\232\320\260\320\272 \321\203\320\264\320\260\320\273\321\217\321\202\321\214 \321\201\321\201\321\213\320\273\320\272\320\270.md" @@ -0,0 +1,28 @@ +В общем-то, задача довольно примитивная, но и пост получится не гигантский. Посмотрим на несколько команд, которые помогут более эффективно расправляться с ссылками. + +Обычно мы как проверяем ссылку - выполняем "ls -l" и смотрим на тип: + +`$ ls -l +`lrwxrwxrwx ... sftp-server -> openssh/sftp-server + + +Если будем работать через find, руками тип смотреть не придется. Для фильтрации по символьным ссылкам у нас есть опция "-type l": + + +`$ find /usr/lib -type l +`/usr/lib/gvfs/gvfsd-afp +`/usr/lib/libmultipath.so + + +Если мы переживаем, что удалим что-то лишнее, можем проверить путь к файлу, на который указывает ссылка. Для этого нужно для вывода команды выполнить "-exec ls -l {} \": + + +`$ find /usr/lib -type l -exec ls -l {} \; +`/usr/lib/gvfs/gvfsd-afp -> ../../libexec/gvfsd-afp +`/usr/lib/libmultipath.so -> libmultipath.so.0 + + +Напоследок разберемся с поиском и удалением битых ссылок, которые никуда не ведут. Для такой цели подойдет параметр "-xtype l": + + +`$ find ~/broken_links/ -xtype l \ No newline at end of file diff --git "a/LINUX/\320\233\320\276\320\263\320\270 \320\262 Linux.md" "b/LINUX/\320\233\320\276\320\263\320\270 \320\262 Linux.md" new file mode 100644 index 0000000..6357dc5 --- /dev/null +++ "b/LINUX/\320\233\320\276\320\263\320\270 \320\262 Linux.md" @@ -0,0 +1,226 @@ +# Как разбирать: journalctl, grep, awk и sed + +Каждый админ хотя бы раз сталкивался с ситуацией, когда сервер внезапно начинает тупить: подвисают процессы, появляются странные задержки, что‑то перестаёт работать. И первая остановка в таком случае — это журналы событий. В Linux системные логи хранятся в **systemd‑journald**, а его главный инструмент для работы — `journalctl`. Но просто читать логи — это скучно. Разберем, **как фильтровать, искать ошибки, анализировать данные и автоматизировать разбор логов** с помощью `grep`, `awk`, `sed` и других утилит. + +#### Работа с journalctl + +Самый простой способ заглянуть в системные журналы — просто вызвать `journalctl`: + +``` +journalctl +``` + +Но если сервер работает долго, логов там будет много. Выведем их без постраничного режима: + +``` +journalctl --no-pager +``` + +Если команда зависает, скорее всего, журнал слишком большой. Ограничим его по времени, например, **выведем логи за последний час**: + +``` +journalctl --since "1 hour ago" +``` + +Теперь можно быстро отследить недавние события. + +#### Как понять, что произошло после перезагрузки? + +Если сервер неожиданно перезагрузился, полезно посмотреть, что записалось в логи **после старта**: + +``` +journalctl -b +``` + +Если система несколько раз перезагружалась, можно посмотреть события после конкретного запуска, например, **минус первый перезапуск**: + +``` +journalctl -b -1 +``` + +Это поможет понять, что произошло перед падением системы. + +#### Фильтрация по сервису + +Чтобы разобраться в проблемах конкретного сервиса, например, `nginx`, используем флаг `-u`: + +``` +journalctl -u nginx.service --no-pager +``` + +Пример вывода: + +``` +Feb 02 10:15:00 myserver nginx[1234]: Starting nginx: [OK] +Feb 02 10:16:00 myserver nginx[1234]: 502 Bad Gateway +``` + +Можно дополнительно **сортировать логи от новых к старым**: + +``` +journalctl -u nginx.service --reverse +``` + +#### grep для поиска ошибок + +Журналы бывают огромными, но нам нужны только ошибки. Отфильтруем их с помощью `grep`: + +``` +journalctl -u nginx.service | grep "error" +``` + +Пример вывода: + +``` +Feb 02 11:20:00 myserver nginx[5678]: error: failed to bind socket +Feb 02 11:22:00 myserver nginx[5678]: error: no permission to access /var/www/html +``` + +Чтобы **не зависеть от регистра**, используем `-i`: + +``` +journalctl -u nginx.service | grep -i "error" +``` + +Теперь `ERROR`, `Error` и `error` будут найдены. + +Иногда важно видеть не только саму ошибку, но и контекст вокруг. Например, **две строки до и после найденного слова**: + +``` +journalctl -u nginx.service | grep -A 2 -B 2 "error" +``` + +Так можно увидеть, что происходило **до и после ошибки**. + +#### Выбираем нужные данные с awk + +Журналы содержат много лишней информации. Например, оставим **только дату, время и сообщение**: + +``` +journalctl -u nginx.service | awk '{print $1, $2, $3, $6}' +``` + +Вывод: + +``` +Feb 02 12:00:00 Starting +Feb 02 12:05:00 error: +Feb 02 12:10:00 Listening +``` + +Если сообщения длинные, можно **обрезать текст**: + +``` +journalctl -u nginx.service | awk '{print substr($0, index($0,$6))}' +``` + +Это оставит только само сообщение без метаданных. + +#### Очистка логов с sed + +Некоторые логи содержат ненужные теги или мусорные символы. Например, уберём всё, что находится в квадратных скобках `[INFO]`: + +``` +journalctl -u nginx.service | sed 's/\[.*\]//g' +``` + +Исходный лог: + +``` +Feb 02 13:30:00 myserver nginx[1234]: [INFO] Server started +``` + +После обработки: + +``` +Feb 02 13:30:00 myserver nginx[1234]: Server started +``` + +Также можно **заменить текст**, например, заменить `error` на `!!!ERROR!!!` для выделения: + +``` +journalctl -u nginx.service | sed 's/error/!!!ERROR!!!/g' +``` + +#### Автоматический сбор ошибок + +Допустим, мы нужно **каждый день автоматически собирать ошибки Nginx** и записывать их в файл. Используем **crontab** и скрипт: + +``` +journalctl -u nginx.service --since "1 day ago" | grep -i "error" | awk '{print $1, $2, $3, $6, $7, $8}' > /var/log/nginx_errors.log +``` + +Этот код: + +1. Берёт логи Nginx за последние 24 часа. +2. Ищет строки с `"error"`. +3. Оставляет только дату, время и сообщение. +4. Записывает результат в `/var/log/nginx_errors.log`. +#### Вывод логов в реальном времени + +А если хочется **смотреть логи в реальном времени**, то спользуйте флаг `-f`: + +``` +journalctl -u nginx.service -f= +``` + +Это как `tail -f`, но для journalctl — вы сразу увидите, если сервис упадёт. + +===================================== +## Расположение логов по умолчанию + +Большинство файлов логов Linux находятся в папке **/var/log/** вы можете список файлов логов для вашей системы с помощью команды ls: + + `ls -l /var/log/` + +Ниже мы рассмотрим 20 различных файлов логов Linux, размещенных в каталоге **/var/log/**. Некоторые из этих логов встречаются только в определенных дистрибутивах. + +- **/var/log/messages** - содержит глобальные системные логи Linux, в том числе те, которые регистрируются при запуске системы. В этот лог записываются несколько типов сообщений: это почта, cron, различные сервисы, ядро, аутентификация и другие. +- **/var/log/dmesg** - содержит сообщения, полученные от ядра. Регистрирует много сообщений еще на этапе загрузки, в них отображается информация об аппаратных устройствах, которые инициализируются в процессе загрузки. Можно сказать это еще один лог системы Linux. Количество сообщений в логе ограничено, и когда файл будет переполнен, с каждым новым сообщением старые будут перезаписаны. Вы также можете посмотреть сообщения из этого лога с помощью команды dmseg. +- **/var/log/auth.log** - содержит информацию об авторизации пользователей в системе, включая пользовательские логины и механизмы аутентификации, которые были использованы. +- **/var/log/boot.log** - Содержит информацию, которая регистрируется при загрузке системы. +- **/var/log/daemon.log** - Включает сообщения от различных фоновых демонов +- **/var/log/kern.log** - Тоже содержит сообщения от ядра, полезны при устранении ошибок пользовательских модулей, встроенных в ядро. +- **/var/log/lastlog** - Отображает информацию о последней сессии всех пользователей. Это нетекстовый файл, для его просмотра необходимо использовать команду lastlog. +- **/var/log/maillog /var/log/mail.log** - журналы сервера электронной почты, запущенного в системе. +- **/var/log/user.log** - Информация из всех журналов на уровне пользователей. +- **/var/log/Xorg.x.log** - Лог сообщений Х сервера. +- **/var/log/alternatives.log** - Информация о работе программы update-alternatives. Это символические ссылки на команды или библиотеки по умолчанию. +- **/var/log/btmp** - лог файл Linux содержит информацию о неудачных попытках входа. Для просмотра файла удобно использовать команду last -f /var/log/btmp +- **/var/log/cups** - Все сообщения, связанные с печатью и принтерами. +- **/var/log/anaconda.log** - все сообщения, зарегистрированные при установке сохраняются в этом файле +- **/var/log/yum.log** - регистрирует всю информацию об установке пакетов с помощью Yum. +- **/var/log/cron** - Всякий раз когда демон Cron запускает выполнения программы, он записывает отчет и сообщения самой программы в этом файле. +- **/var/log/secure** - содержит информацию, относящуюся к аутентификации и авторизации. Например, SSHd регистрирует здесь все, в том числе неудачные попытки входа в систему. +- **/var/log/wtmp или /var/log/utmp -** системные логи Linux**,** содержат журнал входов пользователей в систему. С помощью команды wtmp вы можете узнать кто и когда вошел в систему. +- **/var/log/faillog** - лог системы linux, содержит неудачные попытки входа в систему. Используйте команду faillog, чтобы отобразить содержимое этого файла. +- **/var/log/mysqld.log** - файлы логов Linux от сервера баз данных MySQL. +- **/var/log/httpd/ или /var/log/apache2** - лог файлы linux11 веб-сервера Apache. Логи доступа находятся в файле access_log, а ошибок в error_log +- **/var/log/lighttpd/** - логи linux веб-сервера lighttpd +- **/var/log/conman/** - файлы логов клиента ConMan, +- **/var/log/mail/** - в этом каталоге содержатся дополнительные логи почтового сервера +- **/var/log/prelink/** - Программа Prelink связывает библиотеки и исполняемые файлы, чтобы ускорить процесс их загрузки. /var/log/prelink/prelink.log содержит информацию о .so файлах, которые были изменены программой. +- **/var/log/audit/**- Содержит информацию, созданную демоном аудита auditd. +- **/var/log/setroubleshoot/ -** SE Linux использует демон setroubleshootd (SE Trouble Shoot Daemon) для уведомления о проблемах с безопасностью. В этом журнале находятся сообщения этой программы. +- **/var/log/samba/** - содержит информацию и журналы файлового сервера Samba, который используется для подключения к общим папкам Windows. +- **/var/log/sa/** - Содержит .cap файлы, собранные пакетом Sysstat. +- **/var/log/sssd/** - Используется системным демоном безопасности, который управляет удаленным доступом к каталогам и механизмами аутентификации. + +## Просмотр логов в Linux + +Смотрим лог **/var/log/dmesg**, с возможностью прокрутки: + `less /var/log/dmesg` + +Просмотр логов Linux, в реальном времени: + `tail -f /var/log/dmesg` + +Открываем лог файл dmesg: + `cat /var/log/dmesg` + +Первые строки dmesg: + `head /var/log/dmesg` + +Выводим только ошибки из /var/log/messages: + `grep -i error /var/log/dmesg` +  +===================================== diff --git "a/LINUX/\320\236\320\277\321\202\320\270\320\274\320\270\320\267\320\260\321\206\320\270\321\217 \320\267\320\260\320\263\321\200\321\203\320\267\320\272\320\270 Linux - \321\203\321\201\320\272\320\276\321\200\321\217\320\265\320\274 \321\201\321\202\320\260\321\200\321\202 \321\201\320\270\321\201\321\202\320\265\320\274\321\213.md" "b/LINUX/\320\236\320\277\321\202\320\270\320\274\320\270\320\267\320\260\321\206\320\270\321\217 \320\267\320\260\320\263\321\200\321\203\320\267\320\272\320\270 Linux - \321\203\321\201\320\272\320\276\321\200\321\217\320\265\320\274 \321\201\321\202\320\260\321\200\321\202 \321\201\320\270\321\201\321\202\320\265\320\274\321\213.md" new file mode 100644 index 0000000..65ead09 --- /dev/null +++ "b/LINUX/\320\236\320\277\321\202\320\270\320\274\320\270\320\267\320\260\321\206\320\270\321\217 \320\267\320\260\320\263\321\200\321\203\320\267\320\272\320\270 Linux - \321\203\321\201\320\272\320\276\321\200\321\217\320\265\320\274 \321\201\321\202\320\260\321\200\321\202 \321\201\320\270\321\201\321\202\320\265\320\274\321\213.md" @@ -0,0 +1,26 @@ +Со временем Linux-система может загружаться медленнее из-за лишних сервисов. Эти команды помогут проанализировать и ускорить процесс загрузки. + +Посмотреть общее время загрузки системы +`systemd-analyze + +Показать список сервисов и их время загрузки +`systemd-analyze blame + +Проверить, какие службы запускаются при старте +`systemctl list-unit-files --state=enabled + +Отключить ненужный сервис +`sudo systemctl disable имя_сервиса + +Установить задержку старта для тяжёлого сервиса +`sudo systemctl edit имя_сервиса + +В открывшемся файле добавить: +`[Service] +`ExecStartPre=/bin/sleep 30 + +🟢 systemd-analyze показывает время загрузки системы +🟢 systemctl позволяет отключать или откладывать запуск лишних служб +🟢 Оптимизация автозагрузки ускоряет старт системы + +🗣 Регулярная проверка и настройка автозагрузки особенно важна для серверов и рабочих станций с ограниченными ресурсами. diff --git "a/LINUX/\320\236\321\207\320\270\321\201\321\202\320\272\320\260 \320\270 \321\203\320\264\320\260\320\273\320\265\320\275\320\270\320\265 \321\201\321\202\320\260\321\200\321\213\321\205 \321\217\320\264\320\265\321\200.md" "b/LINUX/\320\236\321\207\320\270\321\201\321\202\320\272\320\260 \320\270 \321\203\320\264\320\260\320\273\320\265\320\275\320\270\320\265 \321\201\321\202\320\260\321\200\321\213\321\205 \321\217\320\264\320\265\321\200.md" new file mode 100644 index 0000000..a9745c8 --- /dev/null +++ "b/LINUX/\320\236\321\207\320\270\321\201\321\202\320\272\320\260 \320\270 \321\203\320\264\320\260\320\273\320\265\320\275\320\270\320\265 \321\201\321\202\320\260\321\200\321\213\321\205 \321\217\320\264\320\265\321\200.md" @@ -0,0 +1,78 @@ +```sh +ls /boot + +sudo uname -a + +sudo apt list --installed | egrep "linux-image | linux-headers" + +dpkg --list | egrep "linux-image | linux-headers" + +# Здесь **rc** - означает, что пакет был установлен, но уже удален, а **ii** - что пакет сейчас установлен. На основе полученного списка вы можете удалить любую версию, например, 4.15.0-91: + +sudo apt purge linux-image-4.15.0-66-generic linux-headers-4.15.0-91-generic + +# Удалить все старые ядра: +sudo apt --purge autoremove +sudo apt autoremove +sudo update-grub + + +``` + +========================================== +#### Как очистить сервер от мусора + +Со временем сервер обрастает мусором: логи, временные файлы, старые кэши. Разберем команды по удалению всего этого добра. Бонусом в конце будет готовый файл для запуска скрипта с логированием, который будет каждый день удалять все ненужное 🙂 + +Команда для удаления .log-файлов старше 7 дней: + +`find /var/log -type f -name "*.log" -mtime +7 -delete + +find - команда поиска файлов +/var/log - папка, где хранятся логи +"-type f" - ищем только файлы +-name "*.log" - по шаблону *.log +"-mtime +7" - старше 7 дней +-delete - сразу удалять + +Очистить systemd-журналы: + +`journalctl --vacuum-time=7d + +Эта команда удаляет внутренние журналы системы, которым больше 7 дней. Такие журналы хранят события: старты, ошибки, перезагрузки, службы — всё, что происходило в системе. Обычно занимают много места, особенно если сервер работает давно. + +Очистить временные файлы: + +`rm -rf /tmp/* /var/tmp/* + +Удаляет всё из временных директорий. Будь осторожен, если на сервере кто-то работает прямо сейчас. + +Очистить кэш и мусор после установки пакетов: + +`apt clean && apt autoremove -y + +"apt clean" - очищает кэш установленных .deb файлов +"apt autoremove" - удаляет больше не нужные зависимости +"-y" - выполнять без лишних вопросов + +Ниже прикладываю готовый файл. Его нужно будет сделать исполняемым и можно добавить в crontab с запуском в каждое воскресенье в 0:00. Все результаты будут сохраняться в файл "/var/log/server-cleanup.log": + +```sh +#!/bin/bash + +LOG_FILE="/var/log/server-cleanup.log" +echo "[$(date)] Очистка начата" >> "$LOG_FILE" + +find /var/log -type f -name "*.log" -mtime +7 -delete >> "$LOG_FILE" 2>&1 +journalctl --vacuum-time=7d >> "$LOG_FILE" 2>&1 +rm -rf /tmp/* /var/tmp/* >> "$LOG_FILE" 2>&1 +apt clean && apt autoremove -y >> "$LOG_FILE" 2>&1 + +echo "[$(date)] Очистка завершена" >> "$LOG_FILE" + +sudo chmod +x /usr/local/bin/clean-server.sh + +sudo crontab -e + +0 0 * * 0 /usr/local/bin/clean-server.sh +``` diff --git "a/LINUX/\320\241\320\261\320\276\321\200\320\272\320\260 \320\277\321\200\320\276\320\263\321\200\320\260\320\274\320\274 \320\270\320\267 \320\270\321\201\321\205\320\276\320\264\320\275\320\276\320\263\320\276 \320\272\320\276\320\264\320\260 \320\262 Linux.md" "b/LINUX/\320\241\320\261\320\276\321\200\320\272\320\260 \320\277\321\200\320\276\320\263\321\200\320\260\320\274\320\274 \320\270\320\267 \320\270\321\201\321\205\320\276\320\264\320\275\320\276\320\263\320\276 \320\272\320\276\320\264\320\260 \320\262 Linux.md" new file mode 100644 index 0000000..9b11cd1 --- /dev/null +++ "b/LINUX/\320\241\320\261\320\276\321\200\320\272\320\260 \320\277\321\200\320\276\320\263\321\200\320\260\320\274\320\274 \320\270\320\267 \320\270\321\201\321\205\320\276\320\264\320\275\320\276\320\263\320\276 \320\272\320\276\320\264\320\260 \320\262 Linux.md" @@ -0,0 +1,42 @@ + +Иногда, случаются ситуации когда нам нужен какой-то софт, но для него нет пакета, или же версия пакета сильно отстает. Тут может пригодиться умение установки из исходников. + +Для большинства приложений процесс установки из исходного кода состоит из шагов, которые мы разберём на примере популярного инструмента для мониторинга процессов — **htop**.   + + 1️⃣ **Скачиваем исходный код** +Для этого найдите архив с исходниками на офциальном сайте или репозитории GitHub. Для нашего примера, скачаем архив исходников htop: + +`wget https://github.com/htop-dev/htop/archive/refs/tags/3.2.2.tar.gz + +2️⃣ **Распаковываем архив и переходим в каталог** +`tar -xvzf 3.2.2.tar.gz +`cd htop-3.2.2 + +3️⃣ **Устанавливаем зависимости** +Большинство программ требуют компилятора и библиотек. Убедитесь, что у вас установлен набор инструментов для сборки:   +`sudo apt update +`sudo apt install build-essential libncursesw5-dev + +**4️⃣ Конфигурируем сборку** +Подготовьте сборку для вашей системы. Это этап проверки зависимостей и настройки параметров:   +`./autogen.sh +`./configure + +Если программа не поддерживает autogen.sh, обычно достаточно только ./configure.  Используется для проверки окружения и создания Makefile, который нужен для компиляции. + +5️⃣ **Компилируем программу** +`make + +Процесс может занять некоторое время в зависимости от мощности системы. + +**6️⃣ Устанавливаем программу** +Завершаем установку в системный каталог: +`sudo make install + +7️⃣ **Проверяем установку** +Для проверки, что мы установили все корректно, проверим версию: +`htop --version + +➡️ Если вам нужно выполнить установку в пользовательский каталог, то можем указать префикс конфигурации: +`./configure --prefix=$HOME/local + diff --git "a/LINUX/\320\241\320\272\321\200\321\213\321\202\321\213\320\265 \320\277\321\200\320\276\321\206\320\265\321\201\321\201\321\213 \320\270 rootkits, \320\272\320\260\320\272 \320\275\320\260\320\271\321\202\320\270 \320\275\320\265\320\262\320\270\320\264\320\270\320\274\320\272\321\203 \320\262 Linux.md" "b/LINUX/\320\241\320\272\321\200\321\213\321\202\321\213\320\265 \320\277\321\200\320\276\321\206\320\265\321\201\321\201\321\213 \320\270 rootkits, \320\272\320\260\320\272 \320\275\320\260\320\271\321\202\320\270 \320\275\320\265\320\262\320\270\320\264\320\270\320\274\320\272\321\203 \320\262 Linux.md" new file mode 100644 index 0000000..7202ac0 --- /dev/null +++ "b/LINUX/\320\241\320\272\321\200\321\213\321\202\321\213\320\265 \320\277\321\200\320\276\321\206\320\265\321\201\321\201\321\213 \320\270 rootkits, \320\272\320\260\320\272 \320\275\320\260\320\271\321\202\320\270 \320\275\320\265\320\262\320\270\320\264\320\270\320\274\320\272\321\203 \320\262 Linux.md" @@ -0,0 +1,40 @@ +Иногда ps, top и даже htop не показывают зловредный процесс, хотя ты точно знаешь — он где-то сидит.  + +Вот как найти спрятанное 👇 + +🔍 **1. chkrootkit — проверка на известные rootkit'ы** +```sh +sudo apt install chkrootkit +sudo chkrootkit +``` +📌 Ищет признаки заражения, подмену системных утилит, скрытые процессы, ловушки ядра. + +🛡 **2. rkhunter — глубокий аудит системы** +```sh +sudo apt install rkhunter +sudo rkhunter --check +``` +📌 Проверяет бинарники, конфиги, права, подозрительные изменения. + +🫥 **3. unhide — находим скрытые процессы** +```sh +sudo apt install unhide +sudo unhide proc +sudo unhide sys +``` +📌 Сравнивает процессы в /proc, syscalls и ps. Помогает, если ps не показывает, но процесс явно есть. + + 🧩**4. auditctl — аудит системных вызовов** + ```sh + sudo apt install auditd +sudo auditctl -a exit,always -F arch=b64 -S execve +``` +📌 Ловит каждый execve, даже если процесс мгновенно скрывается. Идеально для отлова аномалий. + + +🧠 **Что делать, если ps и top молчат, а диск, сеть и CPU загружены?** + +✅ Смотри /proc, ищи "дырки" в PID   +✅ Используй lsof, netstat, ss — они могут показать процессы, которых нет в ps   +✅ Сравни /bin/ps с версией из пакета (debsums, rpm -V)   +✅ Запусти live-дистрибутив и просканируй систему снаружи \ No newline at end of file diff --git "a/LINUX/\320\243\320\264\320\260\320\273\320\265\320\275\320\270\320\265 \320\277\321\200\320\276\320\263\321\200\320\260\320\274\320\274.md" "b/LINUX/\320\243\320\264\320\260\320\273\320\265\320\275\320\270\320\265 \320\277\321\200\320\276\320\263\321\200\320\260\320\274\320\274.md" new file mode 100644 index 0000000..9977ef4 --- /dev/null +++ "b/LINUX/\320\243\320\264\320\260\320\273\320\265\320\275\320\270\320\265 \320\277\321\200\320\276\320\263\321\200\320\260\320\274\320\274.md" @@ -0,0 +1,26 @@ +```sh +sudo apt remove name_progr +sudo apt purge name_progr +apt list --installed + + +snap list +sudo snap remove name_progr + + +sudo apt autoremove +sudo apt autoclean + + +# Удаление программ без пакетного менеджера + +cd /папка/с/исходниками +sudo make uninstall + +# Первая команда переходит в папку с исходниками, вторая - удаляет программу. Если скрипта не сохранилось, то вы можете скачать его из интернета. Иначе программу не удалить. Еще один вариант установки программ - с помощью скрипта из официального сайта. Такие программы удалить намного проще. Они устанавливаются, как правило, в одну папку, для них существует специальный каталог в файловой системе Linux. Здесь они создают новую папку, где расположены все их файлы, включая исполняемые и файлы настроек. Например, чтобы удалить браузер Google Chrome, установленный, таким образом, нужно набрать: + + sudo rm -Rf /opt/google-chrome/ + +# Возможно, после удаления программы, вам стоит проверить папку /etc, так как там обязательно должны были остаться файлы настроек пути к библиотекам этой программы, исполняемым файлам и файлы сервисов. + +``` \ No newline at end of file diff --git "a/LINUX/\320\244\320\260\320\271\320\273\320\276\320\262\320\260\321\217 \321\201\320\270\321\201\321\202\320\265\320\274\320\260 Linux.md" "b/LINUX/\320\244\320\260\320\271\320\273\320\276\320\262\320\260\321\217 \321\201\320\270\321\201\321\202\320\265\320\274\320\260 Linux.md" new file mode 100644 index 0000000..177e091 --- /dev/null +++ "b/LINUX/\320\244\320\260\320\271\320\273\320\276\320\262\320\260\321\217 \321\201\320\270\321\201\321\202\320\265\320\274\320\260 Linux.md" @@ -0,0 +1,176 @@ +#========================================== + +Помимо единственного корневого раздела root, присутствуют подкаталоги, к которым привязаны соответствующие каталоги. Типовая структура первых двух уровней выглядит так: + +├── bin -> usr/bin +├── boot +│ ├── grub +│ └── lost+found +├── dev +│ ├── block +│ └── … +├── etc +│ ├── … +│ ├── update-manager +│ ├── update-motd.d +│ └── xdg +├── home +├── lib -> usr/lib +├── lib32 -> usr/lib32 +├── lib64 -> usr/lib64 +├── libx32 -> usr/libx32 +├── lost+found +├── media +├── mnt +├── opt +├── proc +│ ├── … +│ └── tty +├── root +├── run +│ └── … +├── sbin -> usr/sbin +├── srv +├── sys +├── tmp +│ └── … +├── usr +└── var + +Каждый файл в ФС Линукса имеет конкретный индекс Inode. У одного файла в такой структуре может быть несколько имен, т.е. путей. В структуре системы файлы могут различаться, но на жестком диске им соответствует один файл. Такое свойство называют перекрестной иерархичностью — ветви дерева пересекаются. + +Inode — индексный дескриптор, который используется в UNIX-системах. В нем хранятся все мета-данные о файле: владелец, время последнего обращения, размер, другая информация. + +Каталог можно переместить в другую ФС, после этого его дескриптор будет создан заново. Только после этого исходник будет удален. В пределах одной системы изменится только путь файла. Каталоги и файлы существуют до момента, пока хранятся данные об их имени или пути к ним. При удалении этой информации блоки освобождаются под другие файлы. + +В числе других особенностей Linux — ссылки двух типов: + +- жесткая ссылка (то есть hard link) — путь файла, заданный командой ls -li; +- символьная ссылка (symbolic link) — файл стандарта UNIX с текстовой строкой, задающей путь к оригинальному файлу. + +Существует также суперблок, где хранятся все параметры ФС — общее количество блоков и Inode, наличие свободных блоков и данные о них. Суперблок должен сохранять целостность — это напрямую влияет на стабильность и работу всей системы. Для безопасности в Линукс создается несколько копий для восстановления информации в случае ее потери. + +## Ключевые директории и их назначение + +В Linux директория — фундаментальный элемент системы. Это каталог (папка), где содержатся другие файлы, расположенные согласно иерархии. Отправной точкой, а точнее, базой выступает корневой каталог «/», на котором выстраивается вся система. + +В некоторых источниках этот каталог сравнивают с диском С на винде, но это не совсем корректное сравнение — в Линуксе нет буквенных дисков. В Windows дополнительный раздел расположен на диске D, в Линукс это будет отдельная папка с тем же корнем. Иными словами, путь к любому файлу проходит через корень. + +Пример. Файл /home/user/alien имеет структуру root -> home -> user -> documents, и никакую другую. + +Рассмотрим наиболее востребованные директории и их функции. + +### Пользовательские файлы /bin и /sbin + +В этой директории расположены бинарные исполняемые файлы, которые требуются для загрузки системы и ее работы. Именно здесь вы найдете команды ls, cp, grep, mv, ping и прочие утилиты, доступные каждому юзеру. В /usr/bin хранятся и такие приложения, как браузер Chrome, Firefox и другие. + +В каталоге /sbin, который имеет схожую конфигурацию, содержатся важные исполняемые файлы для системного администрирования. Это iptables, reboot, fdisk и т.д.. + +### Пользовательские данные /home (домашняя папка) + +В этом каталоге хранится домашняя папка с личными файлами и настройками. Например, если ваше имя юзера — denis, у вас будет домашняя папка /home/denis. Сюда записываются, например, скачанные файлы. Если пользователей несколько, они будут записывать файлы в свои папки. Для изменения других системных файлов потребуются права root. + +### Конфигурационные файлы /etc + +Это место хранения файлов конфигурации — сетевых настроек, приложений, файлов инициализации различных служб (серверов печати, хостов и т.д). Папка крайне важна для работы всей системы. При необходимости общие файлы можно редактировать вручную в текстовом редакторе. + +### Файлы устройств /dev и системные файлы /proc + +В директории /dev хранятся интерфейсы системных и периферийных устройств — дисков, принтеров, динамиков и прочих. В Линуксе устройства представлены в виде файлов. Стоит понимать, что это не совсем файлы в том виде, к которому мы привыкли. Псевдоустройства из этого каталога — это, например, генераторы случайных чисел /dev/random. + +Файлы ядра и процессов содержатся в каталоге /proc. В нем, как и в /dev, нет стандартных файлов, а есть специфические области данных о системах и процессах. Здесь есть информация о процессоре /proc/cpuinfo; сведения о памяти, используемой системой — /proc/meminfo; время — /proc/uptime. Тут же содержится информация о системных ресурсах. + +### Переменные (изменяемые) данные /var + +В этой директории лежат данные, которые в процессе работы системы постоянно меняются. Сюда относятся логи, кэши, системные журналы, почтовые очереди и прочие файлы, создаваемые программами. + +Это записываемая директория, которая в стандартном режиме доступна только для чтения. Логи содержатся в /var/log, базы данных в /var/lib, файлы для перезагрузки в /var/tmp. + +### Статические файлы для загрузки /boot + +Это важные файлы для загрузки ОС, в том числе ядра Линукс. Запуск системы без этой папки проблематичен. При этом файлы конфигурации загрузчика находятся в другой папке — /etc, о которой мы писали выше. + +### Общие файлы библиотеки /lib + +В директории /lib находятся файлы, которые обеспечивают работу системных программ и ядра. Эти данные приложения и бинарные файлы /bin и /sbin используют для выполнения заданных функций. + +### Съемный носитель /media + +В этом директории монтируются съемные носители. К примеру, когда вы используете компакт-диск или флешку, автоматически создается каталог. Юзеры получают полный доступ к содержимому носителей. + +### Временные файлы /tmp + +В директории хранятся временные файлы, которые создаются приложениями в процессе работы. При перезагрузке содержимое этого каталога, как правило, очищается. Можно удалить файлы без перезагрузки с помощью соответствующих утилит. + +### Восстановленные данные /lost+found + +В любой ФС Linux есть такая директория. Если в системе случится сбой, при следующей загрузке произойдет автоматическая проверка, и любые обнаруженные поврежденные файлы отправятся в каталог /lost+found, чтобы пользователь при необходимости смог восстановить данные. + +### Файлы состояния приложений /run + +Относительно новая директория в Линукс. Здесь хранятся необходимые приложениям временные файлы — сокеты, идентификаторы процессов и другие. В отличие от каталога /tmp данные в /run нельзя удалить. + +## Типы файлов в Linux + +Линукс подчиняется концепции «Всё есть файл», то есть работа с системой — это по сути взаимодействие с файлами. Применяются разные файлы, используемые как для стандартных данных, так и для устройств. + +Плюс этой концепции в том, что система не тратит ресурсы на реализацию отдельных API для каждого приложения, так как все стандартные программы и процессы реализуются через работу с файлами. + +Рассмотрим типы файлов, которые используются в Linux: + +- Обычные (regular). Это стандартные файлы, используемые чаще других — данные, текст, исходный код, медиа и все остальное; +- Файлы устройств. Сюда входят символьные и блочные файлы (char и block devices), представляющие внешние устройства — принтеры, жесткий диск и другие; +- Именованные каналы. Файлы named pipes требуются для взаимодействия между процессами и передачи данных; +- Ссылки. Используется два типа ссылок. Символические (так называемые симлинки) — работают как ярлыки для указания других каталогов и файлов; жесткие — отвечают за альтернативные способы доступа к физическим данным на диске и выступают дубликатами файлов, но без их фактического дублирования; +- Каталоги. Папки со ссылками на файлы и другие папки. Отвечают за организацию данных и их распределение по категориям, упрощают доступ к различным разделам; +- Сокеты. Файлы, предназначенные для обмена данными при выполнении рабочих задач. Используются как внутри системы, так и при взаимодействии устройств друг с другом. По сути, это своего рода почтовые службы, с помощью которых программы пересылают информацию друг другу; +- Двери. Еще один механизм, ответственный за взаимодействие между процессами.  + +В Линукс у каждого типа файла своя задача в системе управления. Такая схема работы обеспечивает мощность, придает ей гибкость и устойчивость. Поскольку внешние устройства представлены блочными файлами, с ними можно работать с помощью стандартных методов. + +## Управление файловой системой + +Через консоль управления (терминал) в Linux доступен целый арсенал для работы с директориями и файлами. + +Основные команды управления: + +- `Is`. Просмотр содержимого папки и навигация. При использовании с различными опциями возможности расширяются, например, команда ls -l создает  подробный список; +- `cd`. Путь к папке — меняет текущую директорию на указанную пользователем; +- `pwd`. Обозначает текущие местоположение юзера в структуре каталогов; +- `mkdir`. Используется для создания новой папки с присвоением ей имени; +- `cp`. Копирует файлы из исходной папки в целевую; +- `mv`. Перемещает или переименовывает каталоги и файлы; +- `rm`. Удаляет указанные файлы; +- `cat`. Выводит содержимое указанного файла в окно панели управления; +- `less`. Используется для постраничного пролистывания файла; +- `touch`. Создает новый пустой файл (с новым именем); +- `ln -s`. Генерирует символьную ссылку, указывающую на выбранный каталог или файл; +- `which`. Демонстрирует пошаговый путь к исполняемым файлам; +- `nano`. Используется для текстовой редактуры файла; +- `locate`. Применяется для поиска файлов по заданным шаблонам.  + +Указанные выше команды формируют основу для взаимодействия с ФС. При необходимости используется более расширенный список. + +## Монтирование и файловые системы Linux + +Монтирование — подключение файловых систем, жестких дисков и внешних носителей, получение доступа к ним, ручное и автоматическое отключение. При загрузке ядро устройства на Linux автоматически монтирует разделы, считывая данные из конфигурации /etc/fstab. + +При подключении устройств каталог ассоциируется с ними с помощью драйвера, которому передается сгенерированная ссылка. В случае успешного взаимодействия ядро вносит данные в таблицу монтирования. + +Монтирование системы реализуется с помощью команд mount и umount, подготавливающих устройства и точки монтирования. Они реализуют автоматическое подключение ФС. К виртуальной машине подключается диск /dev/sdc, на котором создаются разделы для файловой системы. После создания точки монтирования процесс загрузки ФС запускается. + +Какие файловые системы на Linux наиболее востребованные: + +- Ext. Первая ФС, разработанная специально для Линукс. Система значительно превзошла предыдущие решения по производительности и функционалу; +- Ext2. Улучшенная версия первой ФС. Повышает надежность, улучшает управление и позволяет работать с большим количеством данных и файлов; +- Ext3. Третье поколение ФС для Линукс. Поддерживает журналирование, что делает работу более стабильной и минимизирует риски потери данных после сбоев системы; +- Ext4. Новейшая версия с повышенной эффективностью и масштабируемостью. Технически более совершенный вариант, упрощающий работу с большим объемом данных;  +- JFS. Разработка компании IBM. Эффективно использует ресурсы, отличается высокой производительностью, рассчитана на сильные нагрузки; +- XFS. Высокопроизводительная ФС, разработанная компанией SGI. Подходит для работы с серверами и системами хранения данных;  +- ReiserFS. Разработка программиста Ганса Райзера. Главная особенность — возможность при необходимости увеличивать размер блока, тем самым избегая фрагментации; +- Btrfs. Продукт компании Oracle. Обеспечивает гибкость при управлении данными, предоставляет опции оперативной проверки и восстановления файлов, интегрирует разные устройства в единую ФС;  +- Swap. Позволяет использовать дисковое пространство как виртуальную память, если физические возможности RAM исчерпаны. Технически этот инструмент нельзя назвать файловой системой, но он обеспечивает широкие возможности для управления данными в Линукс.  + +ОС Linux уникальна в плане адаптации к разным сценариям использования, поскольку поддерживает работу многих файловых систем. Функциональное разнообразие обогащает платформу, позволяя тестировать и внедрять в практику множество полезных инноваций. + +> При этом у пользователя всегда есть свобода выбора — например, между стабильностью линейки Ext и гибкими возможностями файловых систем XFS, JFS. Безграничные возможности выбора отражают дух сообщества Linux, где независимые разработчики постоянно предлагают новые решения, обеспечивая постоянное развитие экосистемы. \ No newline at end of file diff --git "a/Linux \321\201\321\202\320\260\320\262\320\270\320\274 \320\275\320\260 MacBook.md" "b/Linux \321\201\321\202\320\260\320\262\320\270\320\274 \320\275\320\260 MacBook.md" new file mode 100644 index 0000000..e1684ca --- /dev/null +++ "b/Linux \321\201\321\202\320\260\320\262\320\270\320\274 \320\275\320\260 MacBook.md" @@ -0,0 +1,98 @@ +## Шаг 1. Подготовка установочной USB-флешки + +Одним из самых важных этапов при установке Linux является подготовка установочной флешки. Несмотря на наличие специализированных утилит, позволяющих создать загрузочный USB-накопитель из графической оболочки, наиболее верным является монтирование образа посредством командной строки и приложения Терминал в OS X. + +Первое, что необходимо сделать – загрузить установочный образ Linux с официального сайта. В качестве примера рассмотрим установку дружественной и популярной версии **Linux Ubuntu**. + +1. Откройте [страницу загрузки Ubuntu](http://www.ubuntu.com/download/desktop) и выберите **64-битную версию** ОС (32-битная для Mac попросту не подойдет). Нажмите _Download_ и на следующей страницу выберите пункт _Not now, take me to the download_. + +![LinuxInstSt1](https://www.iphones.ru/wp-content/uploads/2015/07/LinuxInstSt1.jpg) + +Дистрибутив Ubuntu распространяется абсолютно бесплатно и, таким образом, вы отказываетесь от финансовой поддержки разработчиков, но если вы все же хотите выразить слова благодарности – перетяните соответствующие «долларовые» ползунки на требуемое значение и оплатите результирующую сумму с помощью платежной системы _PayPal_. + +Дождитесь окончания загрузки дистрибутива. + +2. Подключите USB-флешку. Для монтирования загрузочного образа подойдет накопитель, размер которого будет **не менее 2 ГБ**. Все данные, которые в настоящий момент находятся на флешке, будут **безвозвратно стерты**. + +3. Откройте утилиту **Терминал**. Наша задача – конвертировать скачанный образ ISO в общепринятый в среде Mac IMG/DMG. Для этого нам потребуется запустить выполнение несложной команды. + +**`hdiutil convert -format UDRW -o ~/path/to/target.img ~/path/to/ubuntu.iso`** + +По умолчанию все загрузки в среде Mac попадают в папку Загрузки. Для того, чтобы указать путь к скачанному образу наберите в Терминале первую часть команды: **hdutil convert -format UDRW -o**. Затем перетяните скачанный файл с образом (он имеет расширение ISO) на окно терминала. Автоматически вставится правильный адрес к исходному файлу. Имя файла **ubuntu-14.04.2-desktop-amd64.iso** можно изменить на любое, скажем, _LinuxInstall_, но с **ОБЯЗАТЕЛЬНЫМ** изменением расширения _ISO_ на **IMG**. Поставьте пробел и вновь перетяните скачанный образ в окно терминала. На этот раз никаких изменений вносить не нужно. Так выглядит введенная команда: + +![LinuxInstSt2](https://www.iphones.ru/wp-content/uploads/2015/07/LinuxInstSt2.jpg) + +**Будьте внимательны**: сначала следует указать имя и место размещения директории с получаемым в процессе конвертации образом, а после – с файлом исходником (скачанным дистрибутивом). После ввода команды нажимаем ENTER и ждем пока окончится процесс конвертации. + +![LinuxInstSt3](https://www.iphones.ru/wp-content/uploads/2015/07/LinuxInstSt3_2.jpg) + +Если после выполнения команды вы увидели строку **created** с адресом файла в формате **DMG** – все прошло успешно и можно переходить к следующему этапу. + +4. Разворачиваем установочный образ на флешку. После выполнения вышеуказанной команды в папке _Загрузки_ появился новый файл **LinuxInstall.img.dmg**. Наша задача – развернуть его на флешку. Убедитесь, что USB-накопитель подключен к Mac и в Терминале введите следующую команду: + +**`diskutil list`** + +Результат выполнения команды: + +![LinuxInstSt3_3](https://www.iphones.ru/wp-content/uploads/2015/07/LinuxInstSt3_3.jpg) + +Перед нами открылась картина всех подключенных логических дисков системы. Наша USB-флешка числится под идентификатором **disk1** (размер 2 ГБ). Отключим ее с помощью следующей команды: + +**`diskutil unmountDisk /dev/disk1`** + +**Disk1** – частный случай и в команде прописывайте имя, под которым флешка значится в вашей системе. После извлечение накопителя приступим к записи конвертированного образа: + +**`sudo dd if=/path/to/downloaded.img of=/dev/rdisk1 bs=1m`** + +Как и в пункте 3 для более быстрого указания директории введите фрагмент команды **sudo dd if=** после чего перетяните новый образ (с расширением DMG) на окно терминала. Закончите ввод команды. Помните, что **rdisk1** может иметь другой порядковый номер и имя, и зависит от конкретно вашей системы. Нажмите ENTER и введите пароль администратора. + +В течение последующих нескольких минут в окне терминала не будет происходить никаких процессов. **Не спешите закрывать утилиту** и беспокоиться о подвисании процесса. Разворачивание образа происходит «_в тихом режиме_». Дождитесь окончания процесса. Сигналом об окончании разворачивания образа станет сообщение: + +![LinuxInstSt4](https://www.iphones.ru/wp-content/uploads/2015/07/LinuxInstSt4.jpg) + +Нажмите **Извелчь**, но саму **флешку из порта не доставайте**. Загрузочный накопитель готов. + +## Шаг 2. Раздел диска. + +Для установки операционной системы Linux по-соседству с OS X потребуется создать дополнительный логический раздел диска. Сделать это можно с помощью **Дисковой утилиты**, являющейся штатным приложением OS X. + +Запустите Дисковую утилиту. Подсветите ваш основной винчестер (именно винчестер, не раздел) и откройте вкладку **Раздел диска**. + +![LinuxInstSt3_3](https://www.iphones.ru/wp-content/uploads/2015/06/ElCaptain2dOS1.jpg) + +Нажите на знак **«+»** и установите желаемый размер раздела (для нормальной работы Linux достаточно 10 ГБ; рекомендуем объем – 20 ГБ). Нажмите **Применить** и дождитесь окончания процесса. + +![LinuxInstSt5](https://www.iphones.ru/wp-content/uploads/2015/07/LinuxInstSt5.jpg) + +**Примечание:** Если по какой-либо причине схема разделения недоступна (знак «+/-» серым цветом и не реагирует на нажатия), добавить новый раздел можно из **загрузочного меню Mac**. Перезагрузите компьютер и сразу после того, как экран загорится белым зажмите клавиши **Command + R**. Удерживайте до появления системного меню Mac, а затем откройте Дисковую утилиту. Процесс разделения полностью аналогичен с описанным выше. + +После разделения диска **ОБЯЗАТЕЛЬНО** запомните точный размер отделенной логической области в гигабайтах. Перезагружаем компьютер. + +## Шаг 3. Установка Linux Ubuntu + +Сразу после того, как началась перезагрузка, зажмите **клавишу ALT** и дождитесь появления меню с выбором логических дисков, с которых следует произвести установку. Выберите **съемный накопитель EFI** (первый) и нажмите Enter. + +![LinuxInstSt6](https://www.iphones.ru/wp-content/uploads/2015/07/LinuxInstSt6.jpg) + +В появившемся установочном меню операционной системы Ubuntu выберите: Установить Ububntu. Начнется подготовительный процесс установки. После выбора требуемого языка и подключения к сети Wi-Fi установщик спросит как именно мы планируем устанавливать систему. + +**Внимание! Будьте осторожны!**Если вы хотите сохранить работоспособность OS X и Linux выбирайте пункт **Другой Вариант** (выбор пункта _Стереть диск и установить Ubuntu приведет_ к **полному форматированию всей структуры диска и удалению ранее созданных разделов**, включая раздел с системой OS X). + +В открывшемся меню найдите созданный под Linux раздел диска (поможет знание его размера). + +![LinuxInstSt7](https://www.iphones.ru/wp-content/uploads/2015/07/LinuxInstSt7.jpg) + +Дважды щелкните по требуемому разделу и установите следующие параметры: + +– файловая система Ext2; +– точка монтирования: /; + +![LinuxInstSt8](https://www.iphones.ru/wp-content/uploads/2015/07/LinuxInstSt8.jpg) + +Нажмите **ОК** и **Установить сейчас**. Сообщение об отсутствии файла-подкачки проигнорируйте, нажав Продолжить. Начнется установка операционной системы Linux. Возможно однократная перезагрузка Mac. После завершения установки пользователю будет предложено указать имя учетной записи и придумать пароль. Сам процесс инсталяции занимает от 5 до 15 минут. + +![LinuxFinal](https://www.iphones.ru/wp-content/uploads/2015/07/LinuxFinal.jpg) + +Установка завершена. При включении Mac по умолчанию теперь будет загружаться Linux Ubuntu. Изменить приоритетную систему можно в OS X, перейдя в **Настройки – Загрузочный том** и _подсветив раздел_, загрузка которого должна осуществляться в первую очередь. + +![1 Звезд](https://www.iphones.ru/wp-content/plugins/wp-postratings/images/squares/rating_on.gif "1 Звезд")![2 Звезды](https://www.iphones.ru/wp-content/plugins/wp-postratings/images/squares/rating_on.gif "2 Звезды")![3 Звезды](https://www.iphones.ru/wp-content/plugins/wp-postratings/images/squares/rating_on.gif "3 Звезды")![4 Звезды](https://www.iphones.ru/wp-content/plugins/wp-postratings/images/squares/rating_on.gif "4 Звезды")![5 Звезд](https://www.iphones.ru/wp-content/plugins/wp-postratings/images/squares/rating_half.gif "5 Звезд") (**29** голосов, общий рейтинг: **4.62** из 5) \ No newline at end of file diff --git a/MacOS/Homebrew.md b/MacOS/Homebrew.md new file mode 100644 index 0000000..17d98e2 --- /dev/null +++ b/MacOS/Homebrew.md @@ -0,0 +1 @@ +`/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"` diff --git a/MacOS/Postgresql.md b/MacOS/Postgresql.md new file mode 100644 index 0000000..57b6bda --- /dev/null +++ b/MacOS/Postgresql.md @@ -0,0 +1,113 @@ +`brew install postgresql` + +_В автозагрузку:_ +`brew services start postgresql` + +_Запуск вручную:_ +`pg_ctl -D /usr/local/var/postgresql start` + +_Перезагрузка:_ +`brew services restart postgresql` + +_Проверка версии:_ +`brew info postgresql` + +_Инициализация по умолчанию:_ +`initdb --locale=C -E UTF-8 /opt/homebrew/var/postgresql@14` + +_Экспортировать переменную:_ +`export PATH=/opt/homebrew/bin:$PATH` + +_Если автозагрузка не сработает, директория здесь:_ +`~/Library/LaunchAgents` + +_Создаем симлинк:_ +`ln -sfv /usr/local/opt/postgresql/*.plist ~/Library/LaunchAgents` + +_Добавляем в автозагрузку:_ +`launchctl load ~/Library?LaunchAgents/homebrew.mxcl.postgresql.plist` + +_Подключение к БД на MacOS (отличается от Linux). Проверяем пользователей:_ +`psql -l` + +![[Pasted image 20240312221215.png]] + +_Для подключения указываем Owner из таблицы выше:_ +`sudo psql -U vlapa -d postgres` + +_Создаем пользователя с паролем (сначала подключаемся как описано выше):_ +`CREATE USER ploshadka WITH PASSWORD '123456';` + +_Назначаем ему кодировку:_ +`ALTER ROLE ploshadka SET client_encoding TO 'utf8';` + +_Создаем БД для русской локали, чтобы была возможность поиска:_ +`CREATE DATABASE dbname TEMPLATE=template0 ENCODING 'UTF-8' LC_COLLATE 'ru_RU.UTF-8' LC_CTYPE 'ru_RU.UTF-8';` + +_Назначаем этому пользователю новую БД:_ +`GRANT ALL PRIVILEGES ON DATABASE dbname TO ploshadka;` + +_проверим по таблице все-ли верно:_ +`\list` + +_Как подключиться (на localhost достаточно создать БД и подключиться к ней указав только имя):_ +`dbname='stocks'` +_Остальное подхватится по умолчанию._ + +### Узнаем размер БД: + +_Заходим на сервер в оболочку Postgres:_ +`sudo psql -U vlapa -d postgres` + +_Проверяем размер:_ +`SELECT pg_size_pretty(pg_database_size('vlapa_db'));` + +### Узнаем размеры таблиц: + +_Находясь в оболочке Postgres входим в нужную БД:_ +`\c vlapa_db` + +_Вводим команду отсортировать таблицы по большему размеру:_ +```bash +SELECT nspname || '.' || relname AS "relation", +    pg_size_pretty(pg_total_relation_size(C.oid)) AS "total_size" +  FROM pg_class C +  LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) +  WHERE nspname NOT IN ('pg_catalog', 'information_schema') +    AND C.relkind <> 'i' +    AND nspname !~ '^pg_toast' +  ORDER BY pg_total_relation_size(C.oid) DESC +  LIMIT 20; +``` + +################################################## + +_PostgreSQL - SQL команды:_ + +Как добавить колонку в правильное место: +```bash +alter table tablename rename to oldtable; +create table tablename (column defs go here); +insert into tablename (col1, col2, col3) select col1, col2, col3 from oldtable; +``` + +_Как добавить колонку в конец:_ +```sh +ALTER TABLE portfolio ADD COLUMN price_diff_open_current FLOAT NULL; +``` + +_Переименовать таблицу:_ +```sh +alter table portfolio rename column lots to quantity; +``` + +_Обновить всю колонку одним значением:_ +```sh +UPDATE operations SET user_id='1' +``` + +_Переименовать базу данных:_ +```sh +alter database "stocks" rename to "captures"; +``` + diff --git "a/MikroTik \320\277\321\200\320\276\320\261\321\200\320\276\321\201 \320\277\320\276\321\200\321\202\320\276\320\262.md" "b/MikroTik \320\277\321\200\320\276\320\261\321\200\320\276\321\201 \320\277\320\276\321\200\321\202\320\276\320\262.md" new file mode 100644 index 0000000..571a917 --- /dev/null +++ "b/MikroTik \320\277\321\200\320\276\320\261\321\200\320\276\321\201 \320\277\320\276\321\200\321\202\320\276\320\262.md" @@ -0,0 +1,150 @@ +Находя в сети различные инструкции по работе с Mikrotik RouterOS, я обратил внимание, что большинство из них мало информативны — в лучшем случае они представлены в виде скриншотов Winbox со скудными комментариями, в худшем — просто груда строк для командной строки, из которых что-либо понять вообще нет никакой возможности.  + +Поэтому я решил написать небольшой набор инструкций, в которых буду объяснять, не только ЧТО нужно выполнить, но и ЗАЧЕМ и ПОЧЕМУ. + +Итак, начнём с самого простого и, в тоже время, нужного — проброс порта из внутренней сети «наружу». + +Любую сеть можно схематично представить вот в таком виде: + +![[7c6829decd5925af5d5e0dcc27128438.png]] + +Посмотрим немного подробнее, на примере роутера Mikrotik RB951-2n. У него 5 портов, плюс модуль WiFi. + +Подключаемся через web-интерфейс или Winbox'ом к роутеру(по умолчанию логин admin, без пароля), заходим в список интерфейсов, это второй пункт в меню, **Interfaces**. + +![[1fd1edd152259cb558abab7409faa120.png]] + +Тут мы видим все интерфейсы, доступные нашему роутеру(интерфейс — это, грубо говоря, канал связи роутера с другим компьютером/компьютерами) + +![[15d72c288938426db50c249effd75b07.png]] + +Первым идёт **bridge-local** — «локальный мост». В данном случае он служит для установки моста между локальной сетью и беспроводными подключениями по WiFi (В Микротике для соединения вместе нескольких интерфейсов можно или объединить их в свитч, или соединить мостом(бриджем). У каждого из этих двух вариантов есть свои сильные и слабые стороны. Но это тема для отдельной статьи). + +Далее, **ether1-gateway**. Ether1 — это первый RJ45 разъём на лицевой панели роутера. По умолчанию считается, что в него воткнут кабель с интернетом, приходящий от провайдера. Поэтому он называется Gateway — «Ворота» (В принципе, это не обязательно должен быть первый порт, при желании можно назначить gateway'ем любой другой порт, и даже несколько портов одновременно, при наличии подключений от нескольких провайдеров). + +Затем идут 4 порта локальной сети. Они объединены в свитч, начиная со второго RJ45 разъёма, он называется **ether2-master-local**, остальные 3 — неполиткорректно называются **slave-local**. Тут логика простая: в параметрах портов 3, 4 и 5 прописано, что главным у них является порт №2 — соответственно, все четыре порта автоматически объединяются в виртуальный свитч. А этот виртуальный свитч внутренней сети объединен с беспроводной сетью посредством бриджа из первой строки. + +Между внешним интернетом и внутренней сетью находится NAT — Network Address Translation, дословно «Трансляция сетевых адресов». Механизм, позволяющий компьютерам, находящимся в локальной сети, иметь внутренние адреса своей сети, и при этом нормально общаться со всеми другими компьютерами и серверами в интернете (при этом для внешнего сервера все компьютеры внутри сети представляются одним адресом — внешним адресом роутера). Вкратце, это работает таким образом: при обращении из внутренней сети куда-нибудь в интернет, роутер подменяет внутренний адрес компьютера своим адресом, принимает ответ от сервера в интернете, и переадресовывает на локальный адрес компьютера, сделавшего исходный запрос. Это называется Masquerade — «Маскарад»: данные от машины в локальной сети как-бы «надевают маску» и для внешнего сервера это выглядит так, будто они исходят от самого роутера. Точно так же, при подключении из внешней сети, нам нужно где-то задать правило, какой именно компьютер в локальной сети должен отвечать на попытки подключиться к какому-то порту на роутере. Поясню на примере: + +![[eafc9c46561ec70fb5744ea1d85eb1c0.png]] + +Мы хотим, чтобы при подключении к нашей сети в лице роутера на порты 110 и 51413 — подключение переадресовывалось на компьютер PC1, а при подключении к порту 3189 — на машину PC2. + +Для этого и нужен NAT. + +В Микротике управление NAT'ом находится в разделе **IP**->**Firewall**->**NAT**: + +![[b74e1c10b7c49ab7cc235e746af07128.png]] +![[ae81365154ba5166845a7a9171d2248b.png]] + +Тут мы видим, что одно правило уже есть. Оно автоматически создаётся конфигурацией по-умолчанию — это тот самый «Маскарад», он необходим для того, чтобы компьютеры во внутренней сети могли нормально обращаться к серверам в интернете. + +Нам же нужно ровно наоборот — добавить правило для подключения извне к одной из машин в локальной сети. Например, для эффективной работы торрент-клиента мы хотим сделать доступным порт 51413 на машине с именем nas. + +Нажимаем синий плюсик  + +![[370b55d2c9ca15ef5153581f2af2b9b0.png]] + +В появившемся окне нового правила нам нужно всего несколько опций + +![[32de918111f7fed3e8183b4b9148286b.png]] + +Первое — это  + +**Chain** (Цепочка). Тут может быть всего два варианта — **srcnat** и **dstnat**. Цепочка — это, грубо говоря, направление потока данных.  + +**Srcnat** — из внутренней сети во внешнюю,  + +**dstnat** — соответственно, из внешней во внутреннюю.  + +Как легко догадаться, в нашем случае требуется выбрать **dstnat**. + +Далее идут  + +**Src. Address** (исходный адрес) и  + +**Dst. Address** (адрес назначения). Для подключения извне исходный адрес будет адресом одного из миллиардов компьютеров в интернете, а адрес назначения — всегда внешний адрес роутера. Эти пункты ставить смысла нет. + +Потом пункт  + +**Protocol** (протокол). Здесь значение надо выбрать обязательно, иначе мы не сможем указать номер порта. Для торрентов выбираем тут **tcp**.  + +**Src. Port** (исходящий порт) — это тот порт, из которого удалённая машина инициирует соединение с нашим роутером. Нам это абсолютно без разницы, тут ничего не ставим. + +**Dst. Port** (порт назначения) — а это как раз тот порт, на который мы хотим принимать соединение. В моём торрент-клиенте это 51413. + +Затем идёт забавный пункт  + +**Any. Port** (любой порт) — так и есть, это объединение двух предыдущих пунктов, тут можно указать значение, которое будет работать и как исходный порт, так и как порт назначения. В целом, нам это не требуется. + +Теперь очень важный момент: + +**In. interface** (входящий интерфейс) — это, грубо говоря, тот интерфейс, на котором «слушается» указанный порт. Если не указан это параметр, то этот порт перестанет так же быть доступен из внутренней сети, даже несмотря на то, что цепочка у нас **dstnat**. Поэтому, выбираем тут интерфейс, через который мы подключены к интернету, в нашем случае — **ether1-gateway**. + +**Out. interface** (исходящий интерфейс) — интерфейс, к которому подключена та машина, на которую мы делаем переадресацию. Тут что-либо ставить не имеет смысла. + +Далее идут узкоспециализированные параметры, я сейчас не буду на них останавливаться. + +В итоге, получается вот такая картина: + +![[9132596e2802a7ecd3a47842f6475f60.png]] + +Вкладки **Advanced** (Продвинутый) и **Extra** (Дополнительный) содержат различные параметры тонкой настройки, нам они без надобности, идём сразу в **Action** (Действие) + +![[dbb44ce3043db021cfbb195941e52a5a.png]] + +Тут из списка **Action** нужно выбрать конкретное действие, которое следует выполнить с подключением на указанный ранее порт. Выбрать есть из чего: + +**accept** — Просто принимает пакет; + +**add-dst-to-address-list** — Добавляет адрес назначения в указанный список адресов; + +**add-src-to-address-list** — Аналогично предыдущему, но для исходного адреса; + +**dst-nat** — Переадресовывает данные, пришедшие из внешней сети, во внутреннюю; + +**jump** — Позволяет применить для данных правила из другой цепочки. Например, для цепочки **srcnat** — применить правила цепочки **dstnat**; + +**log** — Просто добавляет информацию о пакете в лог роутера; + +**masquerade** — Тот самый «Маскарад»: подмена внутреннего адреса машины из локальной сети на адрес роутера; + +**netmap** — Отображение одного адреса на другой. Фактически, развитие dst-nat; + +**passthrough** — Пропуск текущего пункта правил и переход к следующему. Используется в основном для статистики; + +**redirect** — Перенаправляет данные на другой порт в пределах роутера; + +**return** — Возвращает управление обратно, если в эту цепочку выполнялся прыжок правилом jump; + +**same** — применяется в очень редких случаях, когда нужно применять одни и те-же правила для группы адресов; + +**src-nat** — Обратная dst-nat операция: перенаправление данных из внутренней сети во внешнюю. + +Для наших целей подходит **dst-nat** и **netmap**. Последнее является более новым и улучшенным вариантом первого, логично использовать его: + +![[2e66d41ed2450e40ffd3fa20ba1036d7.png]] + +В поле **To Addresses** нужно указать адрес машины, на которую мы хотим переадресовать порт, в поле To Ports — соответственно, сам порт. + +Чтобы не заморачиваться, вместо адреса пишу имя комьютера, и указываю порт: + +![[264fc3d5aab6c1a767f7659c207e1618.png]] + +И нажимаю кнопку **Apply**, роутер сам находит адрес машины: + +![[73ce6d03c95dd19ac4263224cc1acac4.png]] + +Напоследок имеет смысл нажать **Comment** и указать комментарий для правила, чтобы в дальнейшем не приходилось вспоминать, что это за правило и зачем + +![[4a2b1a7f1e1bee9a26d7885b1d5e0f8c.png]] + +Всё, нажимаем **OК** в окошке ввода комментария и **ОК** в окне ввода правила: + +![[6cda81091bcc9f99ad224bd5c5426f8e.png]] + +Правило создано, всё работает. + +Обратите внимание — дополнительно «открывать» порт не требуется! + +Инструкция актуальна для версий прошивки 5 и 6. До версии 7 вряд-ли что-либо изменится. \ No newline at end of file diff --git a/NuPhy Air75.md b/NuPhy Air75.md new file mode 100644 index 0000000..e03f9ec --- /dev/null +++ b/NuPhy Air75.md @@ -0,0 +1,122 @@ + Ресурсы из видео: + +1. [https://typerun.top/#rus_basic](https://www.youtube.com/redirect?event=video_description&redir_token=QUFFLUhqbU5WZVdmMnVnVnh4bXp0Vm9DTGpNRF9tdjI5d3xBQ3Jtc0tsY2NWM3RtTXE1dnlKR0J0b0lzd2swTFV0S3NyTWlCWkt2eUZ5S1BWTjcya0UyMXpUZjAwZzViNjgxM2FJdXFlZ05PLWV0QVFMdjZvXzZIZEdVNmhsYl9iaHZVUzJrejNjeTFoTUdLNmdTN2wzekpMTQ&q=https%3A%2F%2Ftyperun.top%2F%23rus_basic&v=TGVoOBmvTJs) - тренировка зон пальцев +2. [https://www.keybr.com/](https://www.youtube.com/redirect?event=video_description&redir_token=QUFFLUhqbXFxYVd0ZlVDb3M4YWFuSkxBaWtLaUhJN2k3UXxBQ3Jtc0ttUlVFdVY3ekl5Zk5iWEpoN1lKTGhKMVZJTXlBR2NnanA4alg4cHNiNUQxU29abnZsSXlYaVdsQXYzMDVRQW9rU2ZONDZ6SEtBRERTRnBIeXloQm5PaHFhVkR0SGxYak5wRzh5YTBueUY2VFZFa2VhOA&q=https%3A%2F%2Fwww.keybr.com%2F&v=TGVoOBmvTJs) - отработка букв и сочетаний +3. [https://monkeytype.com/](https://www.youtube.com/redirect?event=video_description&redir_token=QUFFLUhqbW9QVGg5TGtucXhBVURUX3NkN28wQjNfZExGZ3xBQ3Jtc0ttUzU3blVFNUFHaWVDUnV3WW9yaHJTbDBtenJpVmlkVFNKQUdjOWRQQ1g0cE5xWkpqTndhTlpOYXlyOFZNd3JqRENaQmlfLThkeDFvTzQzazBNVnNBM2pvUU96YkZXeTJNc3dhZmJvWWJTbEtYYkw0RQ&q=https%3A%2F%2Fmonkeytype.com%2F&v=TGVoOBmvTJs) - соревнования и практика + +![[Снимок экрана 2024-03-25 в 22.49.58.png]] + + +# Инструкция NuPhy Air75 + +[Главная](https://wiki.geekboards.ru/) / [Инструкции](https://wiki.geekboards.ru/category/instructions/) / Инструкция NuPhy Air75 + +![](https://wiki.geekboards.ru/wp-content/uploads/2022/08/NuPhy-Air75.jpg.pagespeed.ce.jMxJOInlVT.jpg) + +## Боковые LED индикаторы + +![](https://wiki.geekboards.ru/wp-content/uploads/2022/08/NuPhy-Air75-LED.jpg.pagespeed.ce.reafztdgVc.jpg) + +## Переключатели на задней стороне + +![](https://wiki.geekboards.ru/wp-content/uploads/2022/09/01.jpg.pagespeed.ce.QZOhB43Cpo.jpg) + +![](https://wiki.geekboards.ru/wp-content/uploads/2022/09/00.jpg.pagespeed.ce.BXLA6ysend.jpg) + +Поменять режим ОС. + +![](https://wiki.geekboards.ru/wp-content/uploads/2022/09/00_1.jpg.pagespeed.ce.9JtNyUIoKV.jpg) + +**Выбрать режим подключения.** + +_В проводном режиме важно использовать data-кабель для подключения клавиатуры._ + +--- + +## Беспроводное подключение + +![](https://wiki.geekboards.ru/wp-content/uploads/2022/09/02.jpg.pagespeed.ce.2JmjVEJoxm.jpg) + +Используйте короткое нажатие для переключения между устройствами; +Нажмите **FN + 1…3** и удерживайте **3 секунды**, чтобы войти в режим сопряжения. +_*Подключите приемник 2,4 ГГц к компьютеру, прежде чем нажать **FN + 4**._ + +--- + +## Настройки подсветки + +![](https://wiki.geekboards.ru/wp-content/uploads/2022/09/04.jpg.pagespeed.ce.5qPdmpp5hH.jpg) + +**FN + <** = Переключение эффекта подсветки. + +**FN + >** = Переключение цвета подсветки. + +**FN + ˄ ˅** = Яркость +/-. + +--- + +## Настройка подсветки боковых индикаторов + +![](https://wiki.geekboards.ru/wp-content/uploads/2022/09/05.jpg.pagespeed.ce.Syr533y3vN.jpg) + +**FN + ? + <** = Переключение эффекта подсветки боковых индикаторов. + +**FN + ? + >** = Переключение цвета подсветки боковых индикаторов. + +**FN + ? + ˄ ˅** = Яркость боковых индикаторов +/-. + +--- + +## Индикатор заряда батареи + +![](https://wiki.geekboards.ru/wp-content/uploads/2022/09/06.jpg.pagespeed.ce.l1atKjBoJQ.jpg) + +Зажмите **FN + |**, чтобы показать цвет текущего уровня заряда батареи. _см. «[Боковые LED индикаторы](https://wiki.geekboards.ru/instructions/instruction-nuphy-air75/#1)» в начале инструкции._ + +**FN + ]** = Всегда показывать цвет текущего уровня заряда батареи. + +**FN + |** = Одиночное нажатие выключает постоянную индикацию заряда. + +--- + +## Другие комбинация клавиш + +![](https://wiki.geekboards.ru/wp-content/uploads/2022/09/07.jpg.pagespeed.ce.HsLV159eL0.jpg) + +**✀** **(Mac)** = Скриншот области. + +**FN + ✀ (Mac)** = Полноэкранный скриншот. + +**✀ (Win)** = Полноэкранный скриншот. + +**ᗢ** = Активация виртуального помощника (Siri / Cortana)*. + +_*MacOS (Siri): Системные настройки → Siri → Выберете опцию «Press Fn(Function) Space»._ + +_*Windows (Cortana): Пуск → Параметры → Cortana и установите горячие клавиши для вызова на Win+C._ + +--- + +## Настройка спящего режима + +![](https://wiki.geekboards.ru/wp-content/uploads/2022/09/08.jpg.pagespeed.ce.AKjptBfKzT.jpg) + +**FN + TAB + S** = Выключить спящий режим. + +**FN + TAB + W** = Включить спящий режим. + +--- + +## Сброс к заводским настройкам + +![](https://wiki.geekboards.ru/wp-content/uploads/2022/09/09.jpg.pagespeed.ce.h2apPSoU9v.jpg) + +**FN + TAB + R** = Сбросить клавиатуру до заводских настроек. + +--- + +## Переназначить F-ряд на мультимедиа + +![](https://wiki.geekboards.ru/wp-content/uploads/2022/12/%D1%84%D0%B8%D0%B3%D0%BD%D1%8F-5.png) + +**FN + TAB + F**. После этого F-ряд будет выполнять функции мультимедиа клавиш. Повторное нажатие комбинации вернет ряду исходное значение. \ No newline at end of file diff --git a/OBSIDIAN.md b/OBSIDIAN.md new file mode 100644 index 0000000..03a1fc6 --- /dev/null +++ b/OBSIDIAN.md @@ -0,0 +1,56 @@ + +# New +## New +### New +**Text** +*Text* +_Text_ +~~Text~~ +==Text== +Суперпользователь: `sudo su` +%% Text %% + +$Text$ + +![[Снимок экрана 2023-10-06 в 10.40.44.png | 400]] + +- первый +- второй +- третий + +[Ссылка на Яндекс](www://ya.ru) + +>"ну шо там" +>задница? + +> пипец! +> kkkkkk + +>lkjhgfd + +>[!info]- # Заголовок: +>Здесь какой-то текст +>и тут тоже - текст +>супер! + +>[!faq]- # Заголовок: +>Здесь какой-то текст + +>[!note] # Заголовок: +>Здесь какой-то текст + +>[!example]- Заголовочек +># Заголовок: +>Здесь какой-то текст + +>[!note]- Заметка о птичках +> # Заголовок: +>Здесь какой-то текст + +```rust +fn main() { + println("Hello World !") +} +``` + +![[Снимок экрана 2023-08-06 в 22.19.19.png | 600]] diff --git a/PORT.md b/PORT.md new file mode 100644 index 0000000..ce73667 --- /dev/null +++ b/PORT.md @@ -0,0 +1,11 @@ +###  Порты от 0 до 1023 + +По сути, это самые узнаваемые порты, потому что **зарезервированы для операционной системы рассматриваемого компьютера** и, кроме того, они являются наиболее важными протоколами, гарантирующими оптимальную работу. Таким образом, яркий пример этого можно найти в **порт 80 для HTTP**. + +###  Порты с 1024 по 49151 + +Во второй обозначенной категории мы находим зарегистрированные порты, состоящие из тех, которые **используются исключительно приложениями и играми, установленными на машине**. Таким образом, они обычно автоматически настраиваются в процессе установки. + +###  Порты с 49152 по 65535 + +Эти интерфейсы называются **«Динамические или частные порты»** и они относятся к тем, которые соответствуют всем тем приложениям, которые должны подключаться к серверу для правильной работы. Среди наиболее ярких примеров в этом отношении мы находим **клиенты скачивания P2P**. \ No newline at end of file diff --git a/PYTHON/-args, --kwargs.md b/PYTHON/-args, --kwargs.md new file mode 100644 index 0000000..22029c2 --- /dev/null +++ b/PYTHON/-args, --kwargs.md @@ -0,0 +1,43 @@ +**args**, когда неуверены, сколько аргументов будет передано функции, или если хотим передать сохраненный список или кортеж аргументов функции.  + +**kwargs** используется, когда мы не знаем, сколько аргументов ключевых слов будет передано, а также он может быть использован для передачи значений словаря в качестве аргументов ключевых слов. Идентификаторы args и kwargs необязательны, вы можете изменить их на другие, типа *другой **пример, но лучше использовать имя по умолчанию. + +```python +# Пример *args +def sample( *args) : +print(args) +sample( 'time', 1, True) + +# Пример **kwargs +def sample(**kwargs) : +print(kwargs) +sample(a = 'time', b = 1) +``` + +==================================== +В Python можно распаковывать коллекции в аргументы или структуры — это мощно. + +```python +#_***args и kwargs — собирают аргументы:_ + +def f(*args, **kwargs): +    print(args, kwargs) + +f(1, 2, x=10)  # (1, 2), {'x': 10} + +# 👍 _* и ** — распаковывают:_ + +nums = [1, 2, 3] +print(*nums)  # 1 2 3 + + +params = {"sep": " | "} +print(1, 2, 3, **params)  # 1 | 2 | 3 + +#_Распаковка в переменные:_ + +a, *middle, b = [1, 2, 3, 4, 5] +print(middle)  # [2, 3, 4] +``` + +**Где применять: передача параметров, адаптация API, читаемый код** diff --git "a/PYTHON/12 \320\276\320\264\320\275\320\276\321\201\321\202\321\200\320\276\321\207\320\275\321\213\321\205 \320\272\320\276\320\275\321\201\321\202\321\200\321\203\320\272\321\206\320\270\320\271.md" "b/PYTHON/12 \320\276\320\264\320\275\320\276\321\201\321\202\321\200\320\276\321\207\320\275\321\213\321\205 \320\272\320\276\320\275\321\201\321\202\321\200\321\203\320\272\321\206\320\270\320\271.md" new file mode 100644 index 0000000..1e5d8be --- /dev/null +++ "b/PYTHON/12 \320\276\320\264\320\275\320\276\321\201\321\202\321\200\320\276\321\207\320\275\321\213\321\205 \320\272\320\276\320\275\321\201\321\202\321\200\321\203\320\272\321\206\320\270\320\271.md" @@ -0,0 +1,65 @@ +`pip instal bpython` + +```python +x, y, z = map(int, input().strip().split()) +print(f"{x=}, {y=}, {z=}") + +x, y, z = map(int, (x, y, z)) + +############### +from functools import reduce + +volume = reduce(lambda x, y: x * y, map(int, input().strip().split())) +print(f"(volume= {volume})) + +############### +names = ['Христофор', 'Адемар', 'Тэя', 'Стефания', 'Архип'] +names_starts_a = [name for name in names if name.startswith('A')] +print(names_starts_a) + +############### +names = ['Христофор', 'Адемар', 'Тэя', 'Стефания', 'Архип'] +names_starts_a = filter(lambda name: name.startswith('A'), names) +print(list(names_starts_a)) + +############### +numbers = [1, 2, 3] + +# Копирование массива: +another_numbers = numbers[:] + +# Перевернуть массив: +numbers[::-1] + +# Проверка на вхождение: +name = "Василий" +if name in ('Алексей', 'Пётр', 'Христофор'): + print(name) + +# Проверка все-ли True: +if all((a, b, c, d, e)): print("All True") + +# Проверка если хотя бы один True: +if any((a, b, c, d, e)): print("Any True") +############### +# Тернарный оператор: +admin_email = "admin@site.ru" if config.IS_PRODACTION else "mymail@mail.com" + +# Выбор метода в зависимости от : +class User: + def __init__(self, group: str): + self.group = group + +user = User(group='admin') + +group_to_process_method = { + 'admin': process_admin_request, + 'manager': process_manager_request, + 'client': process_client_request, + 'anon': process_anon_request, + +} + + +``` + diff --git "a/PYTHON/8Gb \321\204\320\260\320\271\320\273 \320\276\321\202\320\272\321\200\321\213\321\202\321\214 \320\275\320\260 \320\236\320\227\320\243 2Gb.md" "b/PYTHON/8Gb \321\204\320\260\320\271\320\273 \320\276\321\202\320\272\321\200\321\213\321\202\321\214 \320\275\320\260 \320\236\320\227\320\243 2Gb.md" new file mode 100644 index 0000000..6ee4267 --- /dev/null +++ "b/PYTHON/8Gb \321\204\320\260\320\271\320\273 \320\276\321\202\320\272\321\200\321\213\321\202\321\214 \320\275\320\260 \320\236\320\227\320\243 2Gb.md" @@ -0,0 +1,9 @@ +#### Как прочитать файл объемом 8 ГБ на Python с помощью компьютера с 2 ГБ ОЗУ? + +Это решение работает для любых больших файлов. + +Когда вы открываете файл, всё, что вам нужно сделать, это использовать объект файла в качестве итератора: при циклическом просмотре этого объекта, вы будете извлекать по одной строке за раз, а предыдущие строки будут удалены из памяти. + +Таким образом, файл никогда не будет полностью загружен в память, и ваша обработка будет выполняться на ходу. + +![[Pasted image 20250214130256.png]] \ No newline at end of file diff --git a/PYTHON/AsyncIO.md b/PYTHON/AsyncIO.md new file mode 100644 index 0000000..2c8e365 --- /dev/null +++ b/PYTHON/AsyncIO.md @@ -0,0 +1,155 @@ +### Под капотом asyncio: принципы работы и ключевые концепции ### + +**Библиотека asyncio предоставляет полный набор инструментов для организации параллельного выполнения кода в Python с использованием концепции асинхронности. Но как на самом деле работает asyncio? Давайте разберемся в ключевых принципах и понятиях. + +Библиотека asyncio предоставляет полный набор инструментов для организации параллельного выполнения кода в Python с использованием концепции асинхронности. Но как на самом деле работает asyncio? Давайте разберемся в ключевых принципах и понятиях. + +Этот материал взят из нашей субботней [email-рассылки](https://proglib.io/w/edaee37b), посвященной Python. Подпишитесь, чтобы быть в числе первых, кто получит дайджест. + +**Asyncio** – библиотека, которая предоставляет инфраструктуру для написания параллельного кода с использованием концепции асинхронного программирования. Она позволяет эффективно обрабатывать многочисленные задачи ввода-вывода (например, сетевые операции или чтение/запись из файлов) без необходимости создавать множество потоков или процессов. Ключевые концепции asyncio: + +**Событийный цикл (Event Loop)** + +- Это ядро **asyncio**, которое отвечает за планирование и выполнение задач (корутин). +- Реализован на языке **C** для максимальной эффективности. +- Работает по принципу **однопоточной многозадачности** (single-threaded concurrency). +- Постоянно опрашивает очередь событий и выполняет соответствующие задачи. + +**Корутины (Coroutines)** + +- Это специальные функции, выполнение которых можно приостанавливать на определенных точках с помощью ключевого слова `await`. +- Используются для написания асинхронного, событийно-ориентированного кода. +- Могут ждать асинхронные события (ввод-вывод, сигналы или другие корутины). +- Определяются с помощью `async def`. + +**Задачи (Tasks)** + +- Представляют собой обертки над корутинами для их планирования и выполнения. +- Создаются с помощью `asyncio.create_task(coroutine)` или `asyncio.ensure_future(coroutine)`. +- Задачи можно отменять, объединять с другими задачами и отслеживать на предмет исключений. +- Задачи добавляются в событийный цикл и выполняются в порядке их готовности. + +**Будущие объекты (Futures)** + +- Представляют результат асинхронной операции, который может быть доступен позже. +- Задачи наследуются от класса `Future` и добавляют собственные методы управления. +- Могут быть использованы для координации между разными частями программы. + +**Транспорты и протоколы** + +- **Транспорты** (sockets) отвечают за **передачу** данных по сети. +- **Протоколы** определяют, как **интерпретировать** эти данные (HTTP, WebSocket и др.). +- asyncio предоставляет высокоуровневые абстракции для работы с транспортами и протоколами. + +**Другие инструменты** + +- Пулы для управления ограниченными ресурсами (потоки, подпроцессы). +- Синхронизаторы для координации между корутинами (**Lock****,** **Event**, **Condition)**. +- Очереди для безопасной передачи данных между корутинами. +- Сигналы для обработки внешних событий (UNIX-сигналы). + +**Вот как работает asyncio:** + +- Создается **событийный цикл**, который будет управлять выполнением задач. +- Определяются корутины, которые выполняют некоторую работу и могут приостанавливаться на операциях ввода-вывода с помощью ключевого слова `await`. +- Корутины оборачиваются в задачи с помощью функции `asyncio.create_task()`. +- Задачи планируются для выполнения в событийном цикле с помощью функции `asyncio.run()` или добавляются в цикл вручную. +- Событийный цикл начинает выполнять задачи по очереди. Когда задача достигает операции ввода-вывода (например, `await asyncio.sleep()`), она приостанавливается, и событийный цикл переключается на другую задачу. +- Когда операция ввода-вывода завершается, задача возобновляется и продолжает выполнение до следующей операции ввода-вывода или завершения.  + +Статья по теме + +[Упрощаем разработку: асинхронные функции Python](https://proglib.io/p/uproshchaem-razrabotku-asinhronnye-funkcii-python-2019-10-06) + +Так asyncio позволяет эффективно использовать **один** поток для выполнения **множества** задач с операциями ввода-вывода, избегая блокировки и простоев из-за ожидания завершения этих операций. + +Важно отметить, что asyncio оптимален **именно для I/O-bound задач** (сеть, файловые операции и т. д.), и **не подходит для CPU-bound операций** (вычислительно-интенсивных задач), поскольку в этом случае он не сможет переключаться на другие задачи во время блокировки. Для такого рода задач лучше использовать многопоточность или многопроцессорность. Подробнее о принципах работы asyncio – в [этой статье](https://jacobpadilla.com/articles/recreating-asyncio) и [в этом видео](https://www.youtube.com/watch?v=RIVcqT2OGPA)[](http://savefrom.net/?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DRIVcqT2OGPA&utm_source=userjs-safari&utm_medium=extensions&utm_campaign=link_modifier "Получи прямую ссылку"). + +======================================= +🚀 **asyncio** – это библиотека для асинхронного программирования, позволяющая писать код, выполняющий несколько задач параллельно (без потоков и процессов). + +✅ async def – объявление асинхронной функции (корутина). +✅ await – приостанавливает выполнение до завершения асинхронной операции. +✅ asyncio.run() – запускает главную асинхронную функцию. +✅ asyncio.create_task() – запускает корутину в фоновом режиме. +✅ asyncio.sleep() – имитирует задержку без блокировки других задач. + +```python +import asyncio + +async def say_hello(): +    await asyncio.sleep(1)  # Имитация задержки +    print("Привет, мир!") + +async def main(): +    task1 = asyncio.create_task(say_hello()) +    task2 = asyncio.create_task(say_hello()) +    await task1 +    await task2 + +asyncio.run(main())  # Запуск асинхронной программы +``` +**Выведет с задержкой 1 секунду:** + +Привет, мир! +Привет, мир! + +Обе задачи выполняются **параллельно**, а не последовательно! + +📌 Когда использовать asyncio? + +✅ Запросы в интернет (aiohttp). +✅ Работа с базами данных (aiomysql, asyncpg). +✅ Асинхронное чтение файлов/сокетов. +✅ Многозадачность без потоков и процессов. + +#====================================== +**Вот как работает asyncio — без магии:** + +📌 1. **Event Loop — это «планировщик», который гоняет корутины. Он сам не создаёт потоки. Он ждёт, когда одна задача «освободит» CPU, и сразу берётся за другую.** +```python +import asyncio + +async def main(): +    print("Start") +    await asyncio.sleep(1) +    print("End") + +asyncio.run(main()) +``` +Здесь await asyncio.sleep(1) — не блокирует поток. Это ***yield*,** который говорит: "я пока жду, можешь заняться чем-то ещё". + +📌 2. **Ты не должен использовать time.sleep() в async-коде. Это блокирует весь event loop. Заменяй на await asyncio.sleep().** + +📌 3. **Любая синхронная тяжёлая операция — угроза. Например, чтение файла или SQL-запрос через обычный драйвер.** + +✔️ Как решить: + +👍 Используй aiofiles для файлов. +👍 Используй async-драйверы (asyncpg, aiomysql, httpx и т.д.) +👍 Если нет async-аналога — выноси в ThreadPoolExecutor. +```python +from concurrent.futures import ThreadPoolExecutor +import asyncio + +def blocking(): +    ... + +async def main(): +    loop = asyncio.get_running_loop() +    await loop.run_in_executor(None, blocking) +``` + +📌 4.**Не забывай про семафоры, если запускаешь 1000 задач одновременно. async не значит всё сразу.** +```python +sem = asyncio.Semaphore(100) + +async def safe_task(): +    async with sem: +        await do_something() +``` + +🧠 asyncio — это не про параллельность. Это про эффективное переключение задач, которые  ждут  чего-то: ответа сервера, данных, паузы. + +🗣 **Применяется в: Web-серверах (FastAPI, aiohttp), парсинге, бэкенде с I/O, высоконагруженных API.** + diff --git a/PYTHON/BEGIN + PIP.md b/PYTHON/BEGIN + PIP.md new file mode 100644 index 0000000..d67c8c0 --- /dev/null +++ b/PYTHON/BEGIN + PIP.md @@ -0,0 +1,226 @@ + +![[Python_шпаргалка.png]] + +```bash +which python3 +which pip + +sudo apt install -y python3-pip +pip install numpy + +sudo apt install -y python3-virtualenv +which virtualenv + +# в папке проекта для ДЕактивации среды: +deactivate + +#========================================= +cd code +mkdir pythontests +cd pythontests + +python3 -m venv .venv-name +. ./.venv-name/bin/activate + +nvim pyrightconfig.json + +{ + "venv": "env", + "venvPath": "." +} + +# в папке проекта для ДЕактивации среды: +deactivate + +#========================================== +# Список пакетов с зависимостями: +pip freeze + +# Создаем файл с зависимостями: +pip freeze > requirements.txt + +# Устанавливаем все зависимости в другом месте: +pip install -r requirements.txt + +# список установленных пакетов: +pip list + +# удалить пакет: +pip uninstall -y + +``` + +```sh +# Активация в Windows: +python -m venv venv +.\venv\Scripts\activate + +# Создаем файл с зависимостями: +pip freeze > requirements.txt + +# создаем файл requirements.txt (со всеми необходимыми зависимостями) и запускаем команду: +pip install -r .\requirements.txt + +``` +```sh +git clone user@address:~/packet.git +cd packet + +# Создаем сам скрипт: +vim install.sh + +# Вносим в него: +apt update +apt install -y gcc python3-dev python3-venv +python3 -m venv env +source env-allocation/bin/activate +pip install wheel +pip install -r requirements.txt +export KEY_SECRET='' + +# возможно нужно дать разрешение на запуск! +sudo sh ./install.sh + +sudo python3 ./runner.py + +``` +========================= +Вот как поместить Python-программу в systemd для автозапуска: + +## 1. Создайте unit-файл для systemd + +Создайте файл в `/etc/systemd/system/` (для системных сервисов) или `~/.config/systemd/user/` (для пользовательских сервисов): + +bash + +sudo nano /etc/systemd/system/myapp.service + +## 2. Пример конфигурации unit-файла + +ini + +[Unit] +Description=My Python Application +After=network.target + +[Service] +Type=simple +# Если используете виртуальное окружение: +ExecStart=/path/to/venv/bin/python /path/to/your/app.py +# Или без виртуального окружения: +# ExecStart=/usr/bin/python3 /path/to/your/app.py + +# Рабочая директория +WorkingDirectory=/path/to/your/app/directory + +# Пользователь для запуска (опционально) +User=username +Group=groupname + +# Перезапуск при ошибке +Restart=always +RestartSec=5 + +# Переменные окружения +Environment="PYTHONPATH=/path/to/your/app" +Environment="DATABASE_URL=mysql://user:pass@localhost/db" + +[Install] +WantedBy=multi-user.target + +## 3. Для приложений с зависимостями (виртуальное окружение) + +ini + +[Service] +Type=simple +ExecStart=/home/user/myapp/venv/bin/python /home/user/myapp/main.py +WorkingDirectory=/home/user/myapp +Environment="PATH=/home/user/myapp/venv/bin" + +## 4. Основные команды для управления + +bash + +# Загрузить изменения конфигурации +sudo systemctl daemon-reload + +# Запустить сервис +sudo systemctl start myapp + +# Остановить сервис +sudo systemctl stop myapp + +# Включить автозапуск при загрузке +sudo systemctl enable myapp + +# Проверить статус +sudo systemctl status myapp + +# Просмотреть логи +sudo journalctl -u myapp -f + +## 5. Полезные опции для Python-приложений + +- **`Restart`** - политика перезапуска: + + - `no` - не перезапускать + + - `on-success` - только при успешном завершении + + - `on-failure` - при ошибке (рекомендуется) + + - `always` - всегда перезапускать + +- **`StandardOutput`/`StandardError`** - куда перенаправлять вывод: + + ini + + StandardOutput=journal + StandardError=journal + # Или в файл: + StandardOutput=file:/var/log/myapp.log + ## 6. Пример для Flask/Django приложения + +ini + +[Unit] +Description=My Flask Web Application +After=network.target mysql.service + +[Service] +Type=simple +User=www-data +Group=www-data +WorkingDirectory=/var/www/myapp +ExecStart=/var/www/myapp/venv/bin/gunicorn --workers 3 --bind unix:/tmp/myapp.sock wsgi:app +Environment="FLASK_ENV=production" +Restart=always + +[Install] +WantedBy=multi-user.target + +## 7. Проверка и отладка + +bash + +# Проверить синтаксис конфигурации +sudo systemd-analyze verify /etc/systemd/system/myapp.service + +# Тестовый запуск без systemd +sudo -u username /path/to/python /path/to/app.py + +# Подробные логи +sudo journalctl -u myapp -n 100 -f + +## Советы: + +1. Убедитесь, что Python-скрипт имеет права на выполнение + +2. Используйте абсолютные пути во всех настройках + +3. Для разработки можно запускать как user-сервис без sudo + +4. Для приложений с GUI может понадобиться `Environment="DISPLAY=:0"` + +===================== diff --git "a/PYTHON/COM-\320\277\320\276\321\200\321\202.md" "b/PYTHON/COM-\320\277\320\276\321\200\321\202.md" new file mode 100644 index 0000000..b1706c3 --- /dev/null +++ "b/PYTHON/COM-\320\277\320\276\321\200\321\202.md" @@ -0,0 +1,41 @@ +```python +import serial + +from serial.tools import list_ports +ports = list(list_ports.comports()) +i = 1 +for p in ports: + print(i, ' - ', p.device) + i += 1 + +port = 8 # int(input('Введите номер COM-порта: ')) +baudrate = 115200 # int(input('Введите скорость COM-порта: ')) + +while port > len(ports): + print("Такого порта - НЕТ !") + port = int(input('Введите номер COM-порта: ')) + +print(ports[port - 1].device) + +try: + ser = serial.Serial(ports[port - 1].device, baudrate=baudrate) + print("Serial connection established.") + + while True: + line = ser.readline().decode().strip() + + if line: + print(line) + +except serial.SerialException as se: + print("Serial port error:", str(se)) + +except KeyboardInterrupt: + pass + +finally: + if ser.is_open: + ser.close() + print("Serial connection closed.") +``` + diff --git "a/PYTHON/DAEMON \320\275\320\260 Python.md" "b/PYTHON/DAEMON \320\275\320\260 Python.md" new file mode 100644 index 0000000..94416cc --- /dev/null +++ "b/PYTHON/DAEMON \320\275\320\260 Python.md" @@ -0,0 +1,126 @@ + +Демон — это процесс, который работает в фоновом режиме, обычно без прямого взаимодействия с пользователем. Демоны часто запускаются системой при старте и продолжают работать независимо от пользовательских сеансов. + +### Разница между демоном и обычным процессом + +Отличие демона от обычного процесса состоит в том, что демон отделён от терминала, не имеет своего интерфейса ввода-вывода и продолжает работать в фоновом режиме, даже когда пользователь завершает свою сессию. + +### Области применения демонов + +- Проверка состояния серверов и служб. +- Автоматическая фоновая обработка данных. +- Выполнение периодических задач, таких как обновление баз данных или отправка отчетов. + +## Основные библиотеки для создания демонов + +Для создания демонов на языке Python существует несколько популярных библиотек: + +- `daemon`: Простая и удобная библиотека для создания демонов. +- `systemd`: Более продвинутый инструмент для управления демонами, интегрированный с системой и использующий все её возможности. +- `multiprocessing`: Позволяет создавать многопроцессорные приложения с функциональностью демонов. + +### Преимущества и недостатки каждой библиотеки + +- **daemon**: +- Преимущества: Простота использования. +- Недостатки: Ограниченные возможности конфигурации и интеграции с системой. + +- **systemd**: +- Преимущества: Глубокая интеграция с системой, мощные возможности конфигурации. +- Недостатки: Требует знания специфики конфигурации systemd. + +- **multiprocessing**: +- Преимущества: Поддержка многопроцессорных задач. +- Недостатки: Сложность в настройке и управлении демонами. + +## Подготовка окружения Python + +Перед тем как начать писать код демона, необходимо установить необходимые библиотеки и настроить окружение Python. + +### Установка необходимых библиотек + +```plain +pip install daemon +``` + +## Написание базового демона + +Создадим базовую структуру демона, уделяя внимание функции `run_as_daemon()`. + +### Создание базовой структуры демон-программы + +```python +import daemon +import time + +def run() -> None: + """Основная функция работы демона.""" + while True: + print('Демон работает...') + time.sleep(5) + +if __name__ == '__main__': + with daemon.DaemonContext(): + run() +``` + +## Логирование и какие библиотеки использовать + +Логирование позволяет отслеживать события и ошибки, что облегчает диагностику и поддержку демона. + +### Примеры настройки логирования + +```python +import logging + +logging.basicConfig(level=logging.INFO, filename='daemon.log') + +def run() -> None: + """Основная функция работы демона с логированием.""" + while True: + logging.info('Демон работает...') + time.sleep(5) +``` + +## Обработка сигналов + +Сигналы позволяют управлять работой демона, например, корректно завершить его работу. + +### Объяснение, что такое сигналы и как их обработка помогает управлять демоном + +Сигналы — это механизмы межпроцессного взаимодействия, позволяющие одному процессу посылать уведомления другому. + +### Примеры обработки сигналов, таких как SIGTERM и SIGINT + +```python +import signal + +def handle_signal(signum: int, frame) -> None: + """Обработка сигнала завершения.""" + logging.info('Демон завершает работу...') + exit(0) + +signal.signal(signal.SIGTERM, handle_signal) +signal.signal(signal.SIGINT, handle_signal) +``` + +### Создание systemd юнита для автоматического запуска демона +```sh +[Unit] +Description=Мой Демон + +[Service] +ExecStart=/path/to/your/daemon + +[Install] +WantedBy=multi-user.target +``` + +### Как управлять демоном с помощью системных команд +```sh +sudo systemctl start my-daemon.service +sudo systemctl stop my-daemon.service +sudo systemctl enable my-daemon.service +sudo systemctl disable my-daemon.service +``` + diff --git a/PYTHON/DEFAULTDICT.md b/PYTHON/DEFAULTDICT.md new file mode 100644 index 0000000..4efc9f9 --- /dev/null +++ b/PYTHON/DEFAULTDICT.md @@ -0,0 +1,33 @@ +**Использование коллекций** defaultdict + +Часто при работе со словарями в Python приходится проверять наличие ключа перед его использованием. + +Вместо этого можно использовать defaultdict из модуля collections. + +`from collections import defaultdict   + +# Создаем словарь с дефолтным значением 0   + +`word_count = defaultdict(int)   + +`words = ["python", "code", "python", "script", "code", "python"]   + +`for word in words:   + +    word_count[word] += 1  # Автоматически создаются новые ключи   + +`print(word_count)   + +# Вывод: defaultdict(, {'python': 3, 'code': 2, 'script': 1}) + +**📌 Почему это удобно?** + +🟢 Не нужно вручную проверять наличие ключа + +🟢 Автоматически создаются значения по умолчанию + +🟢 Полезно для счетчиков, группировки и работы со структурами данных + + + +➡️ Также можно использовать `defaultdict(list)` или `defaultdict(set)` для удобного хранения данных. \ No newline at end of file diff --git a/PYTHON/DJANGO.md b/PYTHON/DJANGO.md new file mode 100644 index 0000000..07d5ac8 --- /dev/null +++ b/PYTHON/DJANGO.md @@ -0,0 +1,36 @@ +```bash +python3 -V + +python3 -m venv venv + +. venv/bin/activate +# deactivate - выключить вирт окружение + +pip install --upgrade pip +pip install django + +django-admin startproject coolsite +cd coolsite +python3 manage.py runserver +# CTRL_C + +python3 manage.py startapp women +``` + +```python +# coolsite/settings.py +'women.apps.WomenConfig' + +# women/views.py +from django.http import HttpResponse + +def index(Request): #HttpRequest + return HttpResponse("Страница приложения women.") + +# coolsite/urls.py +from coolsite.women.views import index + +path('women/', index), + + +``` \ No newline at end of file diff --git a/PYTHON/ENUMERATE, ENUM.md b/PYTHON/ENUMERATE, ENUM.md new file mode 100644 index 0000000..ee2ed64 --- /dev/null +++ b/PYTHON/ENUMERATE, ENUM.md @@ -0,0 +1,11 @@ +Итерация по любому содержимому в Python проста – обычный цикл for. Однако очень часто требуются одновременно и индекс элемента, и сам элемент. Программисты используют len() и range() для перебора списка по индексу, но есть более простой способ. + + +![[Pasted image 20250501031753.png]] + +![[Pasted image 20250217114433.png]] + +## **ENUM + +![[telegram-cloud-photo-size-2-5221956955604644168-y.jpg]] + diff --git a/PYTHON/ENUMERATE, ZIP.md b/PYTHON/ENUMERATE, ZIP.md new file mode 100644 index 0000000..139597f --- /dev/null +++ b/PYTHON/ENUMERATE, ZIP.md @@ -0,0 +1,2 @@ + + diff --git a/PYTHON/FOR, WHILE.md b/PYTHON/FOR, WHILE.md new file mode 100644 index 0000000..5bf423d --- /dev/null +++ b/PYTHON/FOR, WHILE.md @@ -0,0 +1,227 @@ +```python +# Фильтрация элементов по типу: +def filter_list(list_to_filter, value_type): + filtered_list = [] + for element in list_to_filter: + if type(element) == value_type: + filtered_list.append(element) + + # Not recomended! Because bool is subclass of int ! + # if isinstance(element, value_type): + # filtered_list.append(element) + + return filtered_list + + +print(filter_list([35, True, 'abc', 10], int)) +print(filter_list([35, True, 'abc', 10], str)) +print(filter_list([35, True, 'abc', 10], bool)) + +########################################################### +# Сокращенный FOR: +all_nums = [-3, 1, 0, 10, -20, 5] + +absolute_nums = [abs(num) for num in all_nums] + +print(absolute_nums) +print(all_nums) + +#----------------- +all_nums = [-3, 1, 0, 10, -20, 5] + +positive_nums = [num for num in all_nums if num > 0] + +# positive_nums = [] + +# for num in all_nums: +# if num > 0: +# positive_nums.append(num) + +print(positive_nums) +print(all_nums) + +#----------------- +my_set = {1, 10, 15} + +new_set = {val * val for val in my_set} + +print(new_set) +print(my_set) + +#----------------- +my_scores = { + 'a': 10, + 'b': 7, + 'm': 14 +} + +scores = {k: v * 10 for k, v in my_scores.items()} + +# scores = {} + +# for key, value in my_scores.items(): +# scores[key] = value * 10 + +print(scores) +print(my_scores) + +#----------------- +my_scores = [10, 7, 14] + +scores = {index: elem * 2 for index, elem in enumerate(my_scores)} + +print(scores) + +#----------------- +# my_motorbike = { + # 'brand': 'bmw', + # 'country': 'germany', + # 'owner': 'vlapa' +# } + +# bike = {k: v.upper() for k, v in my_motorbike.items()} + + +my_motorbike = ['BMW', 'Germany', 'Vlapa'] + +bike = [el for el in my_motorbike if len(el) > 3] + + +print(bike) + +print(my_motorbike) + +########################################################### + +while True: + try: + num_one = float(input("Please enter number one: ")) + num_two = float(input("Please enter number two: ")) + except ValueError as e: + print(e) + print("You must enter numbers!") + continue + + print(num_one / num_two) + + answer = input("Do you want to continue? (yes/no): ") + if answer == 'no': + break +``` + +1️⃣ **Цикл** for + +Используется для перебора элементов в итерируемых объектах (списки, строки, множества, кортежи, словари, генераторы и т. д.). + +**Пример 1: Перебор списка** + +fruits = ["яблоко", "банан", "вишня"] +for fruit in fruits: +    print(fruit) + +**Пример 2:** for **с** range() + +for i in range(5):  # От 0 до 4 +    print(i) + +**Пример 3: Перебор словаря** + +person = {"имя": "Алиса", "возраст": 25} +for key, value in person.items(): +    print(f"{key}: {value}") + +2️⃣ **Цикл** while + +Выполняется **до тех пор, пока условие истинно.** + +**Пример:** + +x = 5 +while x > 0: +    print(f"x = {x}") +    x -= 1  # Уменьшаем x + +🗣️ Используйте for когда заранее известно количество итераций, и while когда не известно. + +🖥 **Циклы. Управление потоком** + +1️⃣ break – прерывает выполнение цикла. +2️⃣ continue – пропускает текущую итерацию и переходит к следующей. +3️⃣ pass – заглушка, ничего не делает (используется, когда код еще не написан). + +**Примеры:** +# break – выход из цикла при достижении 5 + +for i in range(10): +    if i == 5: +        break +    print(i)  # Выведет 0,1,2,3,4 + +# continue – пропускает 5 + +for i in range(7): +    if i == 5: +        continue +    print(i)  # Выведет 0,1,2,3,4,6 + + +# pass – заглушка + +for i in range(3): +    pass  # Код пока не написан + + +🖥 **Использование** else **в циклах** + +else в цикле выполняется, если цикл завершился естественным образом (без break и return). + +**Пример 1:** for **...** else + +for i in range(3): +    print(i) +else: +    print("Цикл завершился без break") +0 +1 +2 +# Цикл завершился без break + +**Пример 2:** while **...** else + +x = 3 +while x > 0: +    print(x) +    x -= 1 +else: +    print("Цикл while завершен") +3 +2 +1 + +# Цикл while завершен + +**Пример 3: проверка вхождения в список** + +def contains(num, numbers): +    for n in numbers: +        if n == num: +            return True +    else: +        return False +print(contains(3, [1,2,3,4,5])) # True +print(contains(3, [1,2,4,5])) # False + +**Пример 4: определение простого числа** + +def is_prime_number(num): +    for i in range(2, int(num ** 0.5) + 1): +        if num % i == 0: +            print(f"{num} не простое число") +            break +    else: +        print(f"{num} — простое число") + +is_prime_number(17) # 17 — простое число +is_prime_number(16) # 16 не простое число + +🗣️ Если нет break и return, else всегда сработает, и его можно просто вынести за пределы цикла. \ No newline at end of file diff --git a/PYTHON/HTTP.md b/PYTHON/HTTP.md new file mode 100644 index 0000000..9e83b93 --- /dev/null +++ b/PYTHON/HTTP.md @@ -0,0 +1,18 @@ +```shell +# Быстрый запуск простого HTTP сервера для передачи ФАЙЛОВ ! +# по умолчанию порт = 8000 + +# его нужно открыть в firewall: +sudo ufw allow 8000 +# sudo firewall-cmd --add-port=8000/tcp + +# переходим в нужный каталог: +cd + +# запускаем: +python3 -m http.server 8000 +# python3 -m http.server --bind 127.0.0.1 8000 + +# открываем в браузере http://89.110.92.137:8000 +# и качаем нужные ФАЙЛЫ ! +``` diff --git a/PYTHON/LIST COMPREHENSION.md b/PYTHON/LIST COMPREHENSION.md new file mode 100644 index 0000000..0f9f0bd --- /dev/null +++ b/PYTHON/LIST COMPREHENSION.md @@ -0,0 +1,6 @@ +![[telegram-cloud-photo-size-2-5220111584251211568-y.jpg]] +![[telegram-cloud-photo-size-2-5220111584251211577-y.jpg]] +![[telegram-cloud-photo-size-2-5220111584251211571-y.jpg]] +![[telegram-cloud-photo-size-2-5220111584251211570-y.jpg]] +![[telegram-cloud-photo-size-2-5220111584251211574-y.jpg]] +![[telegram-cloud-photo-size-2-5220111584251211572-y.jpg]]![[telegram-cloud-photo-size-2-5220111584251211573-y.jpg]] diff --git a/PYTHON/MAP.md b/PYTHON/MAP.md new file mode 100644 index 0000000..f1b79ff --- /dev/null +++ b/PYTHON/MAP.md @@ -0,0 +1,15 @@ +```python +# Функция map() применяется к каждому элементу последовательности и возвращает итератор. + +numbers = [1, 2, 3, 4] +squared = map(lambda x: x**2, numbers) +print(list(squared))  # [1, 4, 9, 16] + +# Можно передавать несколько списков: + +a = [1, 2, 3] +b = [4, 5, 6] +print(list(map(lambda x, y: x + y, a, b)))  # [5, 7, 9] + +# Полезно для применения функций к списку без for +``` diff --git a/PYTHON/MATH.md b/PYTHON/MATH.md new file mode 100644 index 0000000..6912e2c --- /dev/null +++ b/PYTHON/MATH.md @@ -0,0 +1 @@ +![[telegram-cloud-photo-size-2-5230966980947866123-y.jpg]] diff --git a/PYTHON/Matprotlib.md b/PYTHON/Matprotlib.md new file mode 100644 index 0000000..ed42f2d --- /dev/null +++ b/PYTHON/Matprotlib.md @@ -0,0 +1,29 @@ +📊 **Как нарисовать график в 3 строки кода** + +С matplotlib можно строить графики всего за пару секунд! + +**✔️ Установка:** + +```sh +pip install matplotlib +``` + +**➡️ Простой график:** +```python +import matplotlib.pyplot as plt + +plt.plot([1, 2, 3, 4], [10, 20, 25, 30]) +plt.show() +``` + +⚙️ matplotlib поддерживает линии, гистограммы, диаграммы и многое другое. +🗣 **Если хочешь визуализировать данные —** matplotlib **твой инструмент**! + + +![[Pasted image 20250621003213.png]] +![[Pasted image 20250621003225.png]] +Базовый пример построения графика с помощью Python + +Для начала требуется установить библиотеку matplotlib — pip install matplotlib. Сама программа для создания примера простого графика представлена на картинке. + +В функцию plot передаются два массива — координаты точек кривой. Первый массив — координаты по x, второй массив — y. Функция show выдает картинку с графиком. \ No newline at end of file diff --git a/PYTHON/NAMEDTUPLE.md b/PYTHON/NAMEDTUPLE.md new file mode 100644 index 0000000..0c5614f --- /dev/null +++ b/PYTHON/NAMEDTUPLE.md @@ -0,0 +1,18 @@ +```python +# Обычно для хранения данных используют словари или классы. namedtuple — это легковесная альтернатива, сочетающая удобство атрибутов класса с простотой кортежей. + +# Создание namedtuple: + +from collections import namedtuple + +Point = namedtuple("Point", ["x", "y"]) +p = Point(3, 4) + +print(p.x, p.y)  # 3 4 + +# Работает как обычный кортеж, но с доступом по именам: + +print(p[0], p[1])  # 3 4 + +# Полезно, когда нужен неизменяемый контейнер с понятными атрибутами! +``` diff --git "a/PYTHON/OPEN - \321\200\320\260\320\261\320\276\321\202\320\260 \321\201 \321\204\320\260\320\271\320\273\320\260\320\274\320\270.md" "b/PYTHON/OPEN - \321\200\320\260\320\261\320\276\321\202\320\260 \321\201 \321\204\320\260\320\271\320\273\320\260\320\274\320\270.md" new file mode 100644 index 0000000..e8d3d51 --- /dev/null +++ "b/PYTHON/OPEN - \321\200\320\260\320\261\320\276\321\202\320\260 \321\201 \321\204\320\260\320\271\320\273\320\260\320\274\320\270.md" @@ -0,0 +1,39 @@ +_Ключи команды:_ +r - открывает файл на чтение (по умолчанию) +w - запись, если нет - будет создан, если есть - будет перезаписан. +x - запись, если есть - ошибка +a - добавляет данные, если файла нет - создает +t - открывает файл как текстовый (по умолчанию) +\+ - и чтение и запись +```python +file = open(file='hello.txt', mode='r') +print(file.name) # имя файла с которым сейчас работаем +print(file.mode) # режим в котором работаем с файлом +file.close() # устаревшее + +with open(file='hello.txt) as file: + print(file.read(8)) + print(file.readline()) + print(file.readlines()) + + for line in file.readlines(): + print(linestrip()) + + for line in file: + print(line.strip()) + +password_list = ['qwerty, '12345', 'xxxxx'] + +with open(file='secret_file.txt', mode='w) as file: + for passwd in password_list: + file.write(f'{passwd}\n) + + file.writelines([line+'\n' for line in password_list]) + +# Сохранение изображений и др.: +import requessts + +response = requests.get('https://<ссылка>') + file.write(response.content) +``` + diff --git a/PYTHON/OS.md b/PYTHON/OS.md new file mode 100644 index 0000000..1a95ef9 --- /dev/null +++ b/PYTHON/OS.md @@ -0,0 +1,18 @@ +**Узнать текущую директорию + +`os.getcwd() +`C:/Users/...` + + +**Удаляем файл из папки на компьютере** + +Здесь сначала мы импортируем модуль `os`. Затем указываем полный путь к удаляемому файлу в переменной `file_path`. С помощью функции `os.path.exists()` проверяем, существует ли файл по указанному пути.  + +Если файл существует, мы удаляем его с помощью функции `os.remove()`. В противном случае выводим информацию, что такой файл не найден. + +![[telegram-cloud-photo-size-2-5235693785730632253-y.jpg]] + + +**Выключение компьютера** +![[telegram-cloud-photo-size-2-5425027665104058215-x.jpg]] + diff --git a/PYTHON/PATH.md b/PYTHON/PATH.md new file mode 100644 index 0000000..6eaa7c7 --- /dev/null +++ b/PYTHON/PATH.md @@ -0,0 +1,111 @@ +```python +import pathlib + +p = pathlib.Path(__file__) +print(p) +# получим путь к файлу, в котором сейчас находимся: + +/home/name/package/example.py + +``` + +``` +from pathlib as Path +``` +- `Path.cwd()`: Возвращает путь объекта текущей рабочей директории; +- `Path.home()`: Возвращает путь объекта домашней директории; +- `Path.stat()`: Возвращает информацию о пути; +- `Path.chmod()`: Меняет режим и уровень доступа файла; +- `Path.glob(pattern)`: Получение всех файлов которые соответствую паттерну, например `*.jpg` (все картинки) или `*.mp3` (все песни); +- `Path.mkdir()`: создает новую папку по данному пути; +- `Path.open()`: Открывает файл, созданный в пути; +- `Path.rename()`: Переименовывает файл или директорию указанной цели; +- `Path.rmdir()`: Удаляет пустую директорию; +- `Path.unlink()`: Удаляет файл или символическую ссылку. + + +## Генерация кроссплатформенных путей в Pathlib + +Пути используют разные соглашения в разных операционных системах. Windows использует обратный слеш `\` между названиями папок, тогда как все другие популярные операционные системы используют прямой слеш `/`. + +Если вы хотите, чтобы ваш код работал, независимо от базовой ОС, вам нужно будет обрабатывать различные соглашения, характерные для базовой платформы. Модуль Pathlib упрощает работу с путями к файлам. В Pathlib можно просто передать путь или название файла объекту `Path()`, используя слеш, независимо от ОС. Pathlib занимается всем остальным. + +Python + +| | | +|---|---| +|1|pathlib.Path.home() / 'python' / 'samples' / 'test_me.py'| + +Объект `Path()` конвертирует `/` в слеш соответствующий операционной системе. `pathlib.Path` может представлять путь Windows или Posix. Кроме того, Pathlib решает многие кросс-функциональные баги, легко обрабатывая пути. + +## Получение информации о пути в Pathlib + +Во время работы с путями зачастую требуется найти родительскую директорию файла/папки или получить символические ссылки. У класса Path есть несколько удобных для этого методов, различные части пути доступны как свойства, что включают следующее: + +- `drive`: [строка](https://python-scripts.com/string "Создание строки в питоне"), что представляет название жесткого диска. К примеру, `PureWindowsPath('c:/Program Files/CSV').drive` вернет `"C:"`; +- `parts`: возвращает [кортеж](https://python-scripts.com/lists-tuples-dictionaries#tuple "Создать кортеж в питоне"), что дает доступ к компонентам пути; +- `name`: компонент пути без директории; +- `parent`: последовательность обеспечивает доступ к логическим предкам пути; +- `stem`: финальный компонент пути без суффикса; +- `suffix`: разрешение файла финального компонента; +- `anchor`: часть пути перед директорией. `/` используется для создания дочерних путей и имитации поведения `os.path.join`; +- `joinpath`: совмещает путь с предоставленными аргументами; +- `match(pattern)`: возвращает `True/False`, основываясь на совпадении пути с предоставленным шаблоном поиска. + +Например, у нас есть данный путь `"/home/projects/pyscripts/python/sample.md"`: + +- `path`: — возвращает _PosixPath(‘/home/projects/pyscripts/python/sample.md’);_ +- `path.parts`: — возвращает _(‘/’, ‘home’, ‘projects’, ‘pyscripts’, ‘python’);_ +- `path.name`: — возвращает _‘sample.md’;_ +- `path.stem`: — возвращает _‘sample’;_ +- `path.suffix`: — возвращает _‘.md’;_ +- `path.parent`: — возвращает _PosixPath(‘/home/projects/pyscripts/python’);_ +- `path.parent.parent`: — возвращает _PosixPath(‘/home/projects/pyscripts’);_ +- `path.match('*.md')`: возвращает _True;_ +- `PurePosixPath('/python').joinpath('edited_version')`: возвращает _(‘home/projects/pyscripts/python/edited_version._ + + +## Альтернатива для модуля glob + +Помимо модулей `os`, `os.path` в Python также доступен модуль `glob`, что предоставляет путь связанных утилит. Функция `glob.glob` модуля `glob` используется для **нахождения файлов**, соответствующих шаблону. + +Python + +| | | +|---|---| +|1

2

3

4|from glob import glob

top_xlsx_files = glob('*.xlsx') # Все файлы с расширением .xlsx

all_xlsx_files = glob('**/*.xlsx', recursive=True)| + +Pathlib предоставляет свою реализацию `glob`: + +Python + +| | | +| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| 1

2

3

4 | from pathlib import Path

top_xlsx_files = Path.cwd().glob('*.xlsx')

all_xlsx_files = Path.cwd().rglob('*.xlsx') | + +Функциональность **glob** доступна с объектами `Path`. Следовательно, модуль Pathlib упрощают сложные задачи. + +## Чтение и запись файлов с использованием Pathlib + +Следующие методы используются для выполнения основных операций, таких как **чтение и запись файлов**: + +- `read_text`: Файл открывается в **текстовом режиме** для чтения содержимого файла и его закрытия после чтения; +- `read_bytes`: Используется для открытия файла в **бинарном режиме**, возвращения содержимого в бинарном форме и последующего закрытия файла; +- `write_text`: Используется для открытия файла, записи туда текста и последующего закрытия; +- `write_bytes`: Используется для записи бинарных данных в файл и закрытия файла по завершении процесса. + +Давайте испытаем модуль Pathlib используя популярные файловые операции. Следующий пример используется для **чтения содержимого файла**: + +Python + +| | | +|---|---| +|1

2|path = pathlib.Path.cwd() / 'Pathlib.md'

path.read_text()| + +Метод `read_text()` для объекта `Path` используется для чтения содержимого файла. В примере ниже данные записываются в файл, в текстовом режиме: + +Python + +| | | +| ---------------------------- | -------------------------------------------------------------------------------------------------------------------- | +| 1

2

3

4 | from pathlib import Path

p = Path('sample_text_file')

p.write_text('Образец данных для записи в файл') | \ No newline at end of file diff --git "a/PYTHON/PAUSE \320\262 PYTHON.md" "b/PYTHON/PAUSE \320\262 PYTHON.md" new file mode 100644 index 0000000..9a611f0 --- /dev/null +++ "b/PYTHON/PAUSE \320\262 PYTHON.md" @@ -0,0 +1,13 @@ +Иногда нужно приостановить выполнение программы: + +➡️ Использование time.sleep(): + +```python +import time + +print("Начало") +time.sleep(2) # Пауза в 2 секунды +print("Конец") +``` + + diff --git a/PYTHON/PEP8.md b/PYTHON/PEP8.md new file mode 100644 index 0000000..7ce650b --- /dev/null +++ b/PYTHON/PEP8.md @@ -0,0 +1,37 @@ +***Отступы*** - 4 пробела +Макс длина строки = 79 символов (можно разбить " \\ " или операторы конкатенации) + +***Пробелы*** - операторы и операнды, скобки НЕ отделять! + +***Именование*** - верблюд и змея + +***Комментарии*** - # и \`\`\` ... \`\`\` + +***Импорты*** - в алфавитном порядке, лучше без " * " +группы разделяйте пустой строкой + +***Пробелы*** вокруг операторов + +***Название функций и методов*** - глаголы с резделением " _ " + +***Название переменных*** - подробно ! + +***Расположение функций и классов*** - разделяйте пустыми строками, функции должны быть определены перед их использованием. + +***Тройные кавычки*** - документация кода + +--------------------------- +_Полезные инструменты:_ + +***Линтеры***: + +- *pylint +- *flake8 +- *PyCharm + + +***Автоматические форматировщики:*** + +- *Black +- *autopep8 +- *YAPF diff --git a/PYTHON/PROGRAMS.md b/PYTHON/PROGRAMS.md new file mode 100644 index 0000000..b4d3d53 --- /dev/null +++ b/PYTHON/PROGRAMS.md @@ -0,0 +1,160 @@ +```sh +mkdir weather +cd weather + +true > weather +chmod +x weather +ls + +nvim weather +#--------------- +#!/usr/bin/env python3.11 +print("Hello World !") +#--------------- +./weather +-> Hello World ! + +# создать ссылку чтобы запускалось везде: +sudo ln -s $(pwd)/weather /usr/local/bin/ +# удалить: +sudo rm /usr/local/bin/weather + +################# +true > gps_coordinates.py +true > weather_api_service.py +true > printer.py + +ls -l + +gps_coordinates.py +printer.py +weather +weather_api_service.py + +mv printer.py weather_formatter.py + +nvim weather +``` + +```python +#!/usr/bin/env python3.11 + +from gps_coordinates import get_gps_coordinates +from weather_api_service import get_weather +from weather_formatter import format_weather + +def main(): + coordinates = get_gps_coordinates() + weather = get_weather(coordinates) + print(format_weather(weather)) + +if __name__ == "__main__": + main() +``` +`nvim gps_coordinates.py` +```python +def get_gps_coordinates(): + """ Returns current coordinates using MackBook GPS """ + pass + +########################## +# Структура программы со слоями: + +from abc import get_abc + +def main(): + abc = get_abc() + print("Что то там") + +if __name__ == "__main__": + main() + +########################## +# Просто кортеж: + +def abc() -> tuple[float, int]: + """Return""" + return (0.0, 890) + +# Просто список: + +def abc() -> dict[str, int]: + """Return""" + return {'latitude': 10.0, 'longitide': 890} + +#################################################### +# Именованный кортеж: + +from typing import NamedTuple + +class Coordinates(NamedTuple): + latitude: float + longitude: float + +def get_coordinates() -> tuple[float, float]: + """Return""" + return Coordinates(latitude=10, longitude=20) + +coordinates = get_coordinates() + print(coordinates.latitude) + print(coordinates.longitude) + +lat, long = get_coordinates() +print(lat, long) + +########################## +# Именованный словарь: + +from typing import TupedDict + +class Coordinates(TupedDict): + latitude: float + longitude: float + +def get_coordinates() -> Coordinates: + """Return""" + return Coordinates(**{latitude=10, longitude=20}) + +########################## +# Список: + +from typing import Literal + +def get_coordinates() \ + -> dict[Literal['latitude'] | Literal['longitude], float]: + """Return""" + return {'latitude': 10.0, 'longitude': 20.0} + + +########################## +# DataClass лучше именованного кортежа и можно менять в программе: +from dataclasses import dataclass + +@dataclass +class Coordinates: + latitude: float + longitude: float + +def get_coordinates() -> Coordinates: + """Return""" + return Coordinates(**{latitude=10, longitude=20}) + +########################## +``` + +```python +# Выход/завершение программы: +import sys + +sys.exit([status]) +# Необязательный аргумент `status` представляет собой статус выхода. Это целочисленное значение, которое указывает на причину завершения программы. Принято считать, что статус 0 означает успешное выполнение, а любой ненулевой статус указывает на ошибку или ненормальное завершение. + +# Если аргумент `status` не указан, используется значение по умолчанию 0. + +################# +quit() + +################# +exit() + +``` \ No newline at end of file diff --git a/PYTHON/PROGRAMS_py.md b/PYTHON/PROGRAMS_py.md new file mode 100644 index 0000000..01de73d --- /dev/null +++ b/PYTHON/PROGRAMS_py.md @@ -0,0 +1,150 @@ +=============================================== +### TinyDB ### +это легковесная, чисто Python база данных без сервера, которая сохраняет данные в виде JSON-файлов. +Подходит для небольших проектов, скриптов и приложений, где не требуется полноценная SQL-база данных. +Подходит для разработчиков, которым нужно быстро внедрить базу данных без сложной настройки и установки. +```python +from tinydb import TinyDB, Query + +# Создание базы данных в файле db.json +db = TinyDB('db.json') + +# Добавление данных +db.insert({'name': 'Alice', 'age': 24}) +db.insert({'name': 'Bob', 'age': 30}) + +# Поиск данных +User = Query() +results = db.search(User.age > 25) +print(results) + +# Обновление данных +db.update({'age': 26}, User.name == 'Alice') + +# Удаление данных +db.remove(User.name == 'Bob') + +# Установка +pip install tinydb +``` +=============================================== +### Библиотека Python: Arrow ### + +**Arrow** — библиотека для работы с датами и временем, которая упрощает создание, обработку и форматирование временных меток. + +Подходит для разработчиков, которые хотят избежать сложности работы с модулями `datetime` и `timezones`, предоставляя более интуитивные и мощные инструменты для управления временем. +```python +import arrow + +# Текущее время +now = arrow.now() +print(now) + +# Создание объекта даты и времени +date = arrow.get('2023-08-13T12:30:00') +print(date) + +# Форматирование даты и времени +formatted_date = date.format('DD MMMM YYYY HH:mm') +print(formatted_date) + +# Работа с часовыми поясами +utc_time = arrow.utcnow() +local_time = utc_time.to('US/Pacific') +print(local_time) + +# Установка +pip install arrow +``` +=============================================== +### REMBG ### +```python +# удаление фона изображения: +pip install rembg + +from rembg inport remove +from PIL import Image + +input_path = 'name.jpg' +output_path = 'out.png' + +input = Image.open(input_path) +output = remove(input) +output,save(output_path) + +# фото в ч/б: +from PIL import Image + +img = Image.open('girl.png') +baw = img.convert('L') # L - оттенки серого +baw.save('baw_girl.png') +``` +=============================================== +### PANDAS ### +Обработка CSV-файлов с помощью pandas позволяет эффективно управлять, анализировать и преобразовывать данные из таблиц, что полезно для работы с большими объемами данных. +```python +pip install pandas + +import pandas as pd + +# Чтение CSV-файла +df = pd.read_csv('data.csv') + +# Вывод первых 5 строк таблицы +print(df.head()) + +# Фильтрация данных: выбор строк, где значение в столбце 'age' больше 30 +filtered_df = df[df['age'] > 30] + +# Запись отфильтрованных данных в новый CSV-файл +filtered_df.to_csv('filtered_data.csv', index=False) +``` +Этот скрипт читает CSV-файл, отображает первые 5 строк таблицы, фильтрует строки по значению столбца и сохраняет отфильтрованные данные в новый файл. pandas упрощает обработку табличных данных и позволяет работать с большими массивами данных в удобной форме. + +=============================================== +```python +# Объединение нескольких CSV-файлов: +import pandas as pd + +# Чтение нескольких CSV-файлов и их объединение в один DataFrame +files = ['data1.csv', 'data2.csv', 'data3.csv'] +df_list = [pd.read_csv(file) for file in files] +combined_df = pd.concat(df_list) + +# Сохранение объединенных данных в новый CSV-файл +combined_df.to_csv('combined_data.csv', index=False) +``` +В этом примере несколько CSV-файлов объединяются в одну таблицу, что упрощает работу с данными из разных источников. pandas позволяет легко манипулировать такими данными и проводить различные операции, такие как фильтрация, группировка и сортировка. + +=============================================== +### % заряда батареи ### + +```python +# заряд батареи: +import psutil + +battery psutil.sensors_battery() +persent = int(battery.persent) +print(f'Заряд батареи: {persent}%) +``` +=============================================== +### Скриншот ### +```python +import pyautogui + +pyautogui.screenshot( + 'path_to_file.jpg', + region=(0, 0, 300, 400) +) +# функционал - большой ! +``` +=============================================== +### MAX элемент СПИСКА +который входит самое большое кол-во раз: +```python +my_list = [5, 9, 0, 5, 3, 6, 3, 5, 0, 4, 3] + +mpv = max(set(my_list), key=my_list.count) +print(mpv) +``` +=============================================== \ No newline at end of file diff --git a/PYTHON/PROGRESS BAR.md b/PYTHON/PROGRESS BAR.md new file mode 100644 index 0000000..d9d054d --- /dev/null +++ b/PYTHON/PROGRESS BAR.md @@ -0,0 +1,138 @@ +# + +![](https://habrastorage.org/getpro/habr/post_images/466/0af/83f/4660af83f239a7d8e1f14a02fb8ed13d.gif) +Зачем нужны индикаторы прогресса? + +Индикаторы прогресса (progress bar) — визуальное отображение процесса работы. Они избавляют нас от необходимости беспокоиться о том, не завис ли скрипт, дают интуитивное представление о скорости его выполнения и подсказывают, сколько времени осталось до завершения. + +Человек ранее не использовавший индикаторы прогресса может предположить, что их внедрение может сильно усложнить код. К счастью, это не так. Небольшие примеры ниже покажут, как быстро и просто начать отслеживать прогресс в консоли или в интерфейсе быстро набирающей популярность графической библиотеки [PySimpleGUI](https://github.com/PySimpleGUI/PySimpleGUI). + +# Используем Progress + +Первым у нас идёт модуль [Progress](https://github.com/verigak/progress/)_._ + +Всё, что от вас потребуется, это указать количество ожидаемых итераций, тип индикатора и вызывать функцию при каждой итерации: + +```python +import time +from progress.bar import IncrementalBar + +mylist = [1,2,3,4,5,6,7,8] + +bar = IncrementalBar('Countdown', max = len(mylist)) + +for item in mylist: + bar.next() + time.sleep(1) + +bar.finish() +``` +Результат работы: +![](https://habrastorage.org/getpro/habr/post_images/f89/560/66b/f8956066bc8771df24f3b195b73e9fb1.gif) + +Есть индикаторы на любой вкус: + +![](https://habrastorage.org/getpro/habr/post_images/466/0af/83f/4660af83f239a7d8e1f14a02fb8ed13d.gif) +# Используем tqdm + +> Быстрый и расширяемый индикатор прогресса для Python и CLI + +Всего один вызов функции понадобится для получения результата аналогичного предыдущему: + +```python +import time +from tqdm import tqdm + +mylist = [1,2,3,4,5,6,7,8] + +for i in tqdm(mylist): + time.sleep(1) +``` +Получаем: +![](https://habrastorage.org/getpro/habr/post_images/77a/31b/de3/77a31bde39a1237549def40677d7d653.gif) + +Само собой, в комплекте идёт куча настроек и опций. + +# Используем alive-progress + +Ещё один [вариант](https://github.com/rsalmei/alive-progress) синтаксиса, побольше дефолтных анимаций, чем в предыдущих примерах: + +```python +from alive_progress import alive_bar +import time + +mylist = [1,2,3,4,5,6,7,8] + +with alive_bar(len(mylist)) as bar: + for i in mylist: + bar() + time.sleep(1) +``` +Результат: + +![](https://habrastorage.org/getpro/habr/post_images/c60/529/2a0/c605292a0763b239ae950fc4fff0bfec.gif) + +# GUI индикатор прогресса для скрипта + +Иногда возникает необходимость предоставить конечному пользователю графический индикатор. + +![](https://habrastorage.org/getpro/habr/post_images/f8a/fd7/70b/f8afd770b2c1c74dfac23dd492c686f5.gif) + +Сколько кода нужно, чтобы достигнуть такого результата? Немного: +```python +import PySimpleGUI as sg +import time + +mylist = [1,2,3,4,5,6,7,8] + +for i, item in enumerate(mylist): + sg.one_line_progress_meter('This is my progress meter!', i+1, len(mylist), '-key-') + time.sleep(1) +``` + +# Индикатор в приложении PySimpleGUI + +Рассмотрим реализацию индикатора в [PySimpleGUI](https://github.com/PySimpleGUI/PySimpleGUI). + +![](https://habrastorage.org/getpro/habr/post_images/580/d8d/0a3/580d8d0a3e852ec2ef74db199755f2f6.gif) + +Вот как это сделать: + +```python +import PySimpleGUI as sg +import timemylist = [1,2,3,4,5,6,7,8] + +progressbar = [ + [sg.ProgressBar(len(mylist), orientation='h', size=(51, 10), key='progressbar')]] +outputwin = [ + [sg.Output(size=(78,20))] +] + +layout = [ + [sg.Frame('Progress',layout= progressbar)], + [sg.Frame('Output', layout = outputwin)], + [sg.Submit('Start'),sg.Cancel()] +] + +window = sg.Window('Custom Progress Meter', layout) +progress_bar = window['progressbar'] + +while True: + event, values = window.read(timeout=10) + if event == 'Cancel' or event is None: + break + elif event == 'Start': + for i,item in enumerate(mylist): + print(item) + time.sleep(1) + progress_bar.UpdateBar(i + 1) + +window.close() +``` +# Заключение + +Как видите, нет ничего сложного в добавлении информации о прогрессе выполнения: кода немного, а отзывчивость повышается очень сильно. Используйте индикаторы, чтобы больше никогда не гадать, завис ли процесс или нет! + +========================================== + +![[telegram-cloud-photo-size-2-5337133182321027005-y.jpg]] diff --git a/PYTHON/PYINSTALLER.md b/PYTHON/PYINSTALLER.md new file mode 100644 index 0000000..8ba9c87 --- /dev/null +++ b/PYTHON/PYINSTALLER.md @@ -0,0 +1,164 @@ +`pip install pyinstaller` + +`pyinstaller main.py` +- cоздастся папка `dist` с исполняемым файлом + +`pyinstaller --onefile main.py` +- одним файлом + +`pyinstaller --noconsole --onefile main.py` +- будет создан исполняемый файл, работающий в фоновом режиме + +================================ + +# Превращаем код на Python в исполняемый EXE-файл + +Отредактировано 20 дек 2023 + +Как скомпилировать Python-код в файл EXE, чтобы защитить свой код или наглядно показать работу вашей программы. + +Обычно код на Питон запускают двумя способами: через терминал или через текстовый редактор / IDE. + +При этом стоит отметить, что файлик Python — это всего лишь скрипт, который сохранён в расширении .py. Да, это очевидно, но в этом и кроется проблема: иногда нужно скрыть исходный код, а поделиться программкой (в виде альфа-версии, например) очень хочется. + +Одно из решений, которое закрывают боль с исходным кодом — это конвертация файла Python в исполняемый exe-файлик. + +В этой статье мы научимся создавать «экзешники» при помощи двух библиотек: Pyinstaller and auto-py-to-exe. + +Перед тем, как продолжить чтение статьи, скачайте этот репозиторий. Он здорово поможет вам понять процесс, если вы будете повторять все шаги вместе с нами. + +## Почему возникает потребность конвертировать Python-код в EXE + +1. Код могут украсть. Об этом мы уже говорили выше. Куда проще достать исходный код непосредственно из py-файла, чем из скомпилированного приложение. Py-файл достаточно просто открыть! +2. Хочется поделиться кодом с другими юзерами. Дело не только в том, что ваш код могут украсть. Зачастую код содержит зависимости, которыми должен владеть другой юзер перед запуском вашего Python-кода. Но что, если он вообще ничего не смыслит в программировании? Не заставишь ведь его сперва выучить программирование, чтобы оценить ваше новое суперприложение. Проще создать исполняемый файл. +3. Исполняемый файл можно запускать автоматически, по расписанию. Этот пункт актуален, если вы решили написать утилиту для вашего ПК, и эта утилита должна запускаться либо в определенное время, либо вместе с запуском ОС. + +## Конвертация кода Python в файл .exe + +Для начала создадим скрипт на Python. В качестве примера мы используем открытый код бронирования жилья AitBnb в Нью-Йорке. Если у вас уже есть готовый код, можете сразу использовать его. + +```python +import pandas as pd +from pandas.api.types import CategoricalDtype + +def perform_validation(filename:str): + """ + A function to validate inputs for NYC Airbnb Open data. + """ + path_to_data = "../.." + data = pd.read_csv(f"{path_to_data}{filename}") + + # Convert neighbourhood_group to type category + neighbourhood_group_to_cat = CategoricalDtype( + categories=["Manhattan", "Brooklyn", "Queens", "Bronx", "Staten Island"], + ordered=False + ) + + data["neighbourhood_group"] = data["neighbourhood_group"].astype(neighbourhood_group_to_cat) + + # Convert room_type to type category + room_type_to_cat = CategoricalDtype( + categories=["Entire home/apt", "Private room", "Shared room"], + ordered=False + ) + + data["room_type"] = data["room_type"].astype(room_type_to_cat) + + # Convert last_review to datetime + data["last_review"] = pd.to_datetime(data["last_review"]) + + # Minimum nights a person can stay is one night + assert data["minimum_nights"].min() >= 1 + + # Minimum number of reviews is 0 + assert data["number_of_reviews"].min() >= 0 + + # Minimum number of reviews per month + assert data["reviews_per_month"].min() >= 0.00 + + # Minimum amount of listings per host + assert data["calculated_host_listings_count"].min() >= 1 + + # Number of days when listing is available for books + # Could be 0 if tennant has long term booking + assert data["availability_365"].min() >= 0 + + # Save validated data + data.to_csv("validated_ab_nyc_2019.csv", index=False) + +if __name__ == "__main__": + # User inputs filename + filename = input("Enter filename: ") + + # Ensure it's a string + if not filename.isalpha(): + filename = str(filename) + + # Automated validation + perform_validation(filename) +``` + +Вы не забыли скачать библиотеки по ссылке по введении? Сделайте это сейчас. + +](https://tproger.ru/articles/secure-app-development) + +Вот структура файлов на GitHub: + +``` +# Directory structure +| AB_NYC_2019.csv +| ++---auto_py_to_exe +| | requirements.txt +| | validation.py ++---pyinstaller + | requirements.txt + | validation.py +``` + +Каждая из директорий должна обладать своей виртуальной средой. Убедитесь, что среды развёрнуты и в директории pyinstaller, и в auto-py-to-exe. + +Сохраните ваш код и скопируйте его в обе директории. + +## Что делать в Pyinstaller + +Ещё раз убедитесь, что виртуальная среда развёрнута. Да, мы настаиваем. Проверьте, установлены ли все зависимости между библиотеками и кодом. + +Всё в порядке? Запускайте команду: + +``` +pyinstaller --onefile validation.py +``` + +Процесс должен запуститься, и в конце консоль сообщит: «Completed successfully». Значит, всё прошло успешно. ? + +![Превращаем код на Python в исполняемый EXE-файл 1](https://res.cloudinary.com/dyd911kmh/image/upload/v1661780977/executable_file_being_created_980fc79c16.png) + +В папке с библиотекой и кодом должны появиться две новые директории под названиями build и dist. В папке dist вы найдёте заветный exe-файл. Запустите его и проверьте работу приложения. + +## Что делать в Auto-py-to-exe + +Также скиньте файлик с кодом в папку с библиотекой и проверьте зависимости между кодом и библиотекой. + +Запустите команду: + +``` +auto-py-to-exe +``` + +Команда запустит графический интерфейс. + +![Превращаем код на Python в исполняемый EXE-файл 2](https://res.cloudinary.com/dyd911kmh/image/upload/v1661780977/graphical_user_interface_75a54c6f4b.png) + +Запускаем компилятор Auto-py-to-exe // DataCamp + +1. В строчке «Script Location» нажмите «Browse» и выберите py-скрипт. +2. В строчке «Onefile» выберите, скомпилируется код в единый файл или в папку. В папке будет содержаться exe-файл и все зависимости кода. +3. В строчке «Console Window» выберите настройки отображения exe-приложения, когда оно будет запущено. Если выбрать «Console Based», запустится и программа, и консоль, в которой будут показаны процессы. Опция «Window Based» скрывает консоль. +4. Нажмите кнопку «Convert .py to .exe». Готово! + +## Запускаем exe-файл + +В обоих случаях, после компиляции и запуска exe-файла, тот запустит консоль и попросит вас ввести имя файла. Придумайте его и укажите расширение, в котором всё должно работать. К примеру, `Tproger-is-the-best-it-media-in-the-world.csv`. Тогда консоль закроется, а в папке появится заветный файлик с расширением .csv. + +И всё — вы великолепны! \ No newline at end of file diff --git a/PYTHON/PYTHON_tuturial.md b/PYTHON/PYTHON_tuturial.md new file mode 100644 index 0000000..f223a47 --- /dev/null +++ b/PYTHON/PYTHON_tuturial.md @@ -0,0 +1,2910 @@ + +https://habr.com/ru/articles/728568/ +# Ядро планеты Python. Интерактивный учебник + +Туториал + +Добрый день! Меня зовут Михаил Емельянов, недавно я опубликовал на «Хабре» небольшую [статью](https://habr.com/ru/articles/697760/) с примерным путеводителем начинающего Python-разработчика. Пользуясь этим материалом как своего рода оглавлением книги, я написал первые четыре главы мини-учебника «Ядро планеты Python», где постарался коротко, но достаточно ёмко раскрыть специфику, удобство, красоту и силу этого прекрасного языка. + + + +Оригинал учебника лежит на [GitHub](https://github.com/amaargiru/pycore), вы вольны сколько угодно дополнять и переделывать его. Самое главное — учебник написан на Jupiter Notebook, а это значит, что вы можете интерактивно редактировать код, мгновенно добавляя новые сущности или проясняя непонятные моменты. + + + +![Core of the planet Python](https://habrastorage.org/r/w1560/getpro/habr/post_images/58f/2c5/a2c/58f2c5a2c6142c8bee312e39ec2f4f00.jpg) + + + +Погружаясь в Python, не забывайте про прекрасную официальную документацию [docs.python.org](https://docs.python.org/). Изучив её, хотя бы по диагонали, и постепенно углубляясь в нужные разделы, вы сможете убедиться, что многие «хаки», «открытия» и прочие неочевидные вещи уже давно разжеваны, описаны и имеют подробные примеры применения. + + + +Также я бы рекомендовал для изучения базового синтаксиса Python на полную катушку использовать [leetcode.com](https://leetcode.com/problemset/all/?difficulty=EASY&page=1&status=NOT_STARTED). Если отфильтровать задачи по уровню «Easy», а потом добавить дополнительную сортировку по столбцу «Acceptance», то перед вами предстанет не волчий оскал соревновательной платформы, а ванильный букварь с плавно нарастающим уровнем задачек. + + + +## 1. Структуры данных + + + +![Data structures](https://habrastorage.org/r/w1560/getpro/habr/post_images/190/f06/61d/190f0661deb5799fa48e2b28385d98a4.png) + + + +Как известно, программирование = структуры данных + алгоритмы (у Никлауса Вирта даже книжка такая есть). Начнем с данных, а потом плавненько перейдем к методам их обработки. + + + +### Список (list) + + + +Список — самая универсальная и популярная структура данных в Python. Если вы пока точно не определились, какая структура понадобится в вашем проекте, просто возьмите список, с него достаточно просто мигрировать на что-нибудь более специализированное. +Список представляет собой упорядоченную изменяемую коллекцию объектов произвольных типов. Внутреннее строение списка — массив (точнее, vector) указателей, т. е. список является динамическим массивом. + + + +``` +a = [] # Создаем пустой список +a: list[int] = [10, 20] +b: list[int] = [30, 40] +a.append(50) # Добавляем значение в конец спискаb.insert(2, 60) # Вставляем значение по определенному индексу +print(a, b) + +a += bprint(f"Add: {a}") +a.reverse()b = list(reversed(a)) # reversed() возвращает итератор, а не списокprint(f"Reverse: {a}, {b}") + +b = sorted(a) # Возвращает новый отсортированный список +a.sort() # Модифицирует исходный список и не возвращает ничего +print(f"Sort: {a}, {b}") + + +s: str = "A whole string"list_of_chars: list = list(s)print(list_of_chars)list_of_words: list = s.split()print(list_of_words)i: int = list_of_chars.index("w") # Возвращает индекс первого вхождения искомого элемента или вызывает исключение ValueErrorprint(i)list_of_chars.remove("w") # Удаляет первое вхождение искомого элемента или вызывает исключение ValueErrore = list_of_chars.pop(9) # Удаляет и возвращает значение, расположенное по индексу. pop() (без аргумента) удалит и вернет последний элемент спискаprint(list_of_chars, e)a.clear() # Очистка списка +``` + +``` +[10, 20, 50] [30, 40, 60] +Add: [10, 20, 50, 30, 40, 60] +Reverse: [60, 40, 30, 50, 20, 10], [10, 20, 50, 30, 40, 60] +Sort: [10, 20, 30, 40, 50, 60], [10, 20, 30, 40, 50, 60] +['A', ' ', 'w', 'h', 'o', 'l', 'e', ' ', 's', 't', 'r', 'i', 'n', 'g'] +['A', 'whole', 'string'] +2 +['A', ' ', 'h', 'o', 'l', 'e', ' ', 's', 't', 'i', 'n', 'g'] r +``` + +### Кортеж (tuple) + +Кортеж — тоже список, только неизменяемый (immutable) и хэшируемый (hashable). Кортеж, содержащий те же данные, что и список, занимает меньше места: + +``` +a = [2, 3, "Boson", "Higgs", 1.56e-22]b = (2, 3, "Boson", "Higgs", 1.56e-22)print(f"List: {a.__sizeof__()} bytes")print(f"Tuple: {b.__sizeof__()} bytes") +``` + +``` +List: 104 bytes +Tuple: 64 bytes +``` + +### Именованный кортеж (named tuple) + +В соответствии с названием, имеет именованные поля. Удобно! + +``` +from collections import namedtuplerectangle = namedtuple('rectangle', 'length width')r = rectangle(length = 1, width = 2)print(r)print(r.length)print(r.width)print(r._fields) +``` + +``` +rectangle(length=1, width=2) +1 +2 +('length', 'width') +``` + +### Словарь (dict) + +Словарь — вторая по частоте использования структура данных в Python. dict — реализация хеш-таблицы, поэтому в качестве ключа нельзя брать нехешируемый объект, например, список (тут-то нам и может пригодиться кортеж). Ключом словаря может быть любой неизменяемый объект: число, строка, datetime и даже функция. Такие объекты имеют метод **__hash__()**, который однозначно сопоставляет объект с некоторым числом. По этому числу словарь ищет значение для ключа. + +Списки, словари и множества (которые мы рассмотрим чуть ниже) изменяемы и не имеют метода хеширования, при попытке подставить их в словарь возникнет ошибка. + +``` +d = {} # Создаем пустой словарьd: dict[str, str] = {"Italy": "Pizza", "US": "Hot-Dog", "China": "Dim Sum"} # Непосредственное создание словаряk = ["Italy", "US", "China"]v = ["Pizza", "Hot-Dog", "Dim Sum"]d = dict(zip(k, v)) # Создание словаря из двух коллекций при помощи zipk = d.keys() # Коллекция ключей. Отражает изменения в основном словареv = d.values() # Коллекция значений. Тоже отражает изменения в основном словареk_v = d.items() # Кортежи ключ-значение, которые тоже отражают изменения в основном словареprint(d)print(k)print(v)print(k_v)print(f"Mapping: {k.mapping['Italy']}")d.update({"China": "Dumplings"}) # Добавление значение. При совпадении ключа старое значение будет перезаписаноprint(f"Replace item: {d}")c = d["China"] # Читаем значениеprint(f"Read item: {c}")try: v = d.pop("Spain") # Удаляет значение или вызывает исключение KeyErrorexcept KeyError: print("Dictionary key doesn't exist")# Примеры dict comprehension (более подробно comprehension будет рассмотрено ниже)b = {k: v for k, v in d.items() if "a" in k} # Вернет новый словарь, отфильтрованный по значению ключаprint(b)c = {k: v for k, v in d.items() if len(v) >= 7} # Вернет новый словарь, отфильтрованный по длине значенийprint(c)d.clear() # Очистка словаря +``` + + + +``` +{'Italy': 'Pizza', 'US': 'Hot-Dog', 'China': 'Dim Sum'} +dict_keys(['Italy', 'US', 'China']) +dict_values(['Pizza', 'Hot-Dog', 'Dim Sum']) +dict_items([('Italy', 'Pizza'), ('US', 'Hot-Dog'), ('China', 'Dim Sum')]) +Mapping: Pizza +Replace item: {'Italy': 'Pizza', 'US': 'Hot-Dog', 'China': 'Dumplings'} +Read item: Dumplings +Dictionary key doesn't exist +{'Italy': 'Pizza', 'China': 'Dumplings'} +{'US': 'Hot-Dog', 'China': 'Dumplings'} +``` + +### Решение проблемы вычисления хеша при работе со словарем + +Любая хеш-таблица, в том числе и питоновский словарь, должна уметь решать проблему вычисления хеша. Для этого используются техники **open addressing** или **chaining**. Python [использует](https://stackoverflow.com/questions/9010222/why-can-a-python-dict-have-multiple-keys-with-the-same-hash) open addressing. + +Новый словарь инициализируется с 8 пустыми слотами. + +Интерпретатор сначала пытается добавить новую запись по адресу, зависящему от хеша ключа. + +``` +addr = hash(key) & mask, +``` + +где + +``` +mask = PyDictMINSIZE - 1 +``` + +Если этот адрес занят, то интерпретатор проверяет (при помощи ==) хеш и ключ. Если оба совпадают, то, значит, запись уже существует. Тогда начинается зондирование свободных слотов, которое идет в псевдослучайном порядке (порядок зависит от значения ключа). Новая запись будет добавлена по первому свободному адресу. + +Чтение из словаря происходит аналогично, интерпретатор начинает поиск с позиции addr и идет по тому же псевдослучайному пути, пока не прочитает нужную запись. + +### Defaultdict + +Если попытаться прочитать из обычного словаря значение ключа, которого там нет, то будет выброшено исключение KeyError (исключения будут рассмотрены ниже). Defaultdict позволяет не писать обработчик исключений, а просто воспринимает чтение несуществующего ключа как команду записать в этот ключ и вернуть значение по умолчанию; например, defaultdict(int) вернет 0. + +``` +from collections import defaultdictdd = defaultdict(int)print(dd[10]) # Печать int, будет выведен ноль, значение по умолчаниюdd = {} # "Обычный" пустой словарь# print(dd[10]) # вызовет исключение KeyError +``` + +``` +0 +``` + + +### Счетчик (counter) + +Счетчик подсчитывает передаваемые ему объекты. Иногда очень удобно просто бухнуть в счетчик какой-нибудь список и сразу получить структуру данных с подсчитанными элементами. + +``` +from collections import Countershirts_colors = ["red", "white", "blue", "white", "white", "black", "black"]c = Counter(shirts_colors)print(c)c["blue"] += 1print(f"After shopping: {c}") +``` + +``` +Counter({'white': 3, 'black': 2, 'red': 1, 'blue': 1}) +After shopping: Counter({'white': 3, 'blue': 2, 'black': 2, 'red': 1}) +``` + +Объяснение работы Counter() при помощи defaultdict(): + +``` +from collections import defaultdictshirts_colors = ["red", "white", "blue", "white", "white", "black", "black"]d = defaultdict(int)for shirt in shirts_colors: d[shirt] += 1print(d) +``` + + + +``` +defaultdict(, {'red': 1, 'white': 3, 'blue': 1, 'black': 2}) +``` + + + +### Множество (set) + + + +Третья по распространенности питоновская структура данных. Когда-то, когда Python был молод, множества представляли собой несколько редуцированные словари, но со временем их судьбы (и реализации) стали расходиться. Однако, множество всё-таки является хеш-таблицей с соответствующим быстродействием на разных типах операций. + + + +``` +big_cities: set["str"] = {"New-York", "Los Angeles", "Ottawa"}american_cities: set["str"] = {"Chicago", "New-York", "Los Angeles"}big_cities |= {"Sydney"} # Добавить значение (или add())american_cities |= {"Salt Lake City", "Seattle"} # Сложить множества (или update())print(big_cities, american_cities)union_cities: set["str"] = big_cities | american_cities # Или union()intersected_cities: set["str"] = big_cities & american_cities # Или intersection()dif_cities: set["str"] = big_cities - american_cities # Или difference()symdif_cities: set["str"] = big_cities ^ american_cities # Или symmetric_difference()issub: bool = big_cities <= union_cities # Или issubset()issuper: bool = american_cities >= dif_cities # Или issuperset()print(union_cities)print(intersected_cities)print(dif_cities)print(symdif_cities)print(issub, issuper)big_cities.add("London")big_cities.remove("Ottawa") # Удаляет значение, если оно имеется или выбрасывает KeyErrorbig_cities.discard("Los Angeles") # Удаляет значение без выбрасывания KeyErrorbig_cities.pop() # Возвращает и удаляет случайное значение (порядок в set неопределен) или выбрасывает KeyErrorbig_cities.clear() # Очищает множество +``` + + + +``` +{'New-York', 'Los Angeles', 'Sydney', 'Ottawa'} {'New-York', 'Seattle', 'Chicago', 'Los Angeles', 'Salt Lake City'} +{'Ottawa', 'Salt Lake City', 'Chicago', 'New-York', 'Seattle', 'Sydney', 'Los Angeles'} +{'New-York', 'Los Angeles'} +{'Ottawa', 'Sydney'} +{'Seattle', 'Ottawa', 'Chicago', 'Salt Lake City', 'Sydney'} +True False +``` + + + +### Иммутабельное множество (frozen set) + + + +Frozen set — то же множество, только иммутабельное и хешируемое. Напоминает разницу между списком и кортежем, не правда ли? + + + +``` +a = frozenset({"New-York", "Los Angeles", "Ottawa"}) +``` + + + +### Массив (array, bytes, bytearray) + + + +Я перешел на Python с языков, более приближенных к «железу» (C, C#, даже на ассемблере когда-то писал за деньги :) и сначала немного удивлялся, что обычный массив, в котором всё так удобно лежит на своих местах, используется относительно редко. Массив в Python не является структурой данных, выбираемой по умолчанию и используется только в случаях, когда решающую роль начинают играть размер структуры и скорость её обработки. Но, с другой стороны, если вы смотрите в сторону NumPy и Pandas (немного затронуты ниже), то массивы — ваше всё. + + + +Массив хранит переменные определенного типа, поэтому, в отличие от списка, не требует создания нового объекта для каждой новой переменной и выигрывает у списка в размерах и скорости доступа. Можно сказать, что это тонкая обёртка над Си-массивами. + + + +Следует различать array («просто» массив), bytes (иммутабельный массив, содержащий только байты, наследие str из Python 2) и bytearray (мутабельный байтовый массив). + + + +``` +from array import arraya1 = array("l", [1, 2, 3, -4])a2 = array("b", b"1234567890")b = bytes(a2)print(a1)print(a2[0])print(b)print(a1.index(-4)) # Возвращает индекс элементы или выбрасывает ValueError +``` + + + +``` +array('l', [1, 2, 3, -4]) +49 +b'1234567890' +3 +``` + + + +``` +# Созданиеb1 = bytes([1, 2, 3, 4]) # Целые числа должны быть в диапазоне от 0 to 255b2 = "The String".encode('utf-8')b3 = (-1024).to_bytes(4, byteorder='big', signed=True) # byteorder = "big"/"little"/"sys.byteorder", signed = False/Trueb4 = bytes.fromhex('FEADCA') # Для большей читаемости hex-значения могут быть разделены пробеламиb5 = bytes(range(10,30,2))print(b1, b2, b3, b4, b5)# Преобразованиеc: list = list(b"\xfc\x00\x00\x00\x00\x01")s: str = b'The String'.decode("utf-8")b: int = int.from_bytes(b"\xfc\x00", byteorder='big', signed=False) # byteorder = "big"/"little"/"sys.byteorder", signed = False/Trues2: str = b"\xfc\x00\x00\x00\x00\x01".hex(" ")print(c, s, b, s2)with open("1.bin", "wb") as file: # Байтовая запись в файл file.write(b1)with open("1.bin", "rb") as file: # Чтение из файла b6 = file.read()print(b6) +``` + + + +``` +b'\x01\x02\x03\x04' b'The String' b'\xff\xff\xfc\x00' b'\xfe\xad\xca' b'\n\x0c\x0e\x10\x12\x14\x16\x18\x1a\x1c' +[252, 0, 0, 0, 0, 1] The String 64512 fc 00 00 00 00 01 +b'\x01\x02\x03\x04' +``` + + + +### Односвязный список + + + +[Односвязный список](https://en.wikipedia.org/wiki/Linked_list#Singly_linked_list) представляет набор связанных узлов, каждый из которых хранит собственные данные и ссылку на следующий узел. В практике применим редко, но его любят использовать интервьюеры на собеседованиях, чтобы кандидат мог блеснуть своими алгоритмическими знаниями. В Python встроенной реализации не имеет, можно или использовать deque (в основе которого лежит двусвязный список), или написать свою реализацию. + + + +### Двусвязный список (Deque) + + + +Ссылки в каждом узле [двусвязного списка](https://en.wikipedia.org/wiki/Linked_list#Doubly_linked_list) указывают на предыдущий и на последующий узел в списке. Можно или использовать deque, или написать свою реализацию. + + + +``` +from collections import dequed = deque([1, 2, 3, 4], maxlen=1000)d.append(5) # Add element to the right side of the dequed.appendleft(0) # Add element to the left side of the deque by appending elements from iterabled.extend([6, 7]) # Extend the right side of the dequed.extendleft([-1, -2]) # Extend the left side of the dequeprint(d)a = d.pop() # Remove and return an element from the right side of the deque. Can raise an IndexErrorb = d.popleft() # Remove and return an element from the left side of the deque. Can raise an IndexErrorprint(a, b)print(d) +``` + + + +``` +deque([-2, -1, 0, 1, 2, 3, 4, 5, 6, 7], maxlen=1000) +7 -2 +deque([-1, 0, 1, 2, 3, 4, 5, 6], maxlen=1000) +``` + + + +### Queue + + + +Queue реализует FIFO со множественными поставщиками данных и множественными потребителями. Может быть особенно полезен при многопоточности, позволяя корректно обмениваться информацией между потоками. Также существуют LifoQueue для реализации LIFO и PriorityQueue для реализации очереди с приоритетом. + + + +``` +from queue import Queueq = Queue(maxsize=1000)q.put("eat", block=True, timeout=10)q.put("sleep") # По умолчанию block=True, timeout=Noneq.put("code")q.put_nowait("repeat") # Эквивалент put("repeat", block=False). Если свободный слот не будет предоставлен немедленно, будет вызвано исключение queue.Fullprint(q.queue)a = q.get(block=True, timeout=10) # Удалить и возвратить элемент из FIFOb = q.get() # По умолчанию block=True, timeout=Nonec = q.get_nowait() # Эквивалент get(False)print(a, b, c, q.queue) +``` + + + +``` +deque(['eat', 'sleep', 'code', 'repeat']) +eat sleep code deque(['repeat']) +``` + + + +### Бинарное дерево (binary tree) + + + +Иерархическая структура данных, в которой каждый узел имеет не более двух потомков. Встроенной реализации не имеет, нужно писать свою. Как правило, используются деревья с дополнительными свойствами, рассмотренные ниже. + + + +### Куча (heap) + + + +Бинарное дерево, удовлетворяющее свойство кучи: если B является узлом-потомком узла A, то ключ(A) ≥ ключ(B). Куча является максимально эффективной реализацией абстрактного типа данных, который называется очередью с приоритетом и поддерживающего две обязательные операции — добавить элемент и извлечь минимум (или максимум, в зависимости от реализации). + + + +В Python min-куча (наименьшее значение всегда лежит в корне) реализована на базе списка при помощи встроенного модуля heapq. Если вам нужна max-куча, с максимальным значением в корне, можете воспользоваться советами со [Stackoverflow](https://stackoverflow.com/questions/2501457/what-do-i-use-for-a-max-heap-implementation-in-python). + + + +``` +import heapqh = [211, 1, 43, 79, 12, 5, -10, 0]heapq.heapify(h) # Превращаем список в кучуprint(h)heapq.heappush(h, 2) # Добавляем элементprint(h)m = heapq.heappop(h) # Извлекаем минимальный элементprint(h, m) +``` + + + +``` +[-10, 0, 5, 1, 12, 211, 43, 79] +[-10, 0, 5, 1, 12, 211, 43, 79, 2] +[0, 1, 5, 2, 12, 211, 43, 79] -10 +``` + + + +Пробежимся коротенько по остальным структурам данных, которые в Python не имеют встроенной реализации, но, тем не менее, могут весьма пригодиться в реальном проекте. + + + +### Би-дерево (B-tree) + + + +Сбалансированное дерево, оптимизированное для доступа к относительно медленным элементам памяти (например, дисковым структурам или индексам баз данных); как ветви, так и листья представляют собой списки (для того, чтобы можно было считать такой список в один проход для дальнейшего быстрого разбора в ОЗУ). Нужно писать свою реализацию. Либо — воспользоваться встроенной в Python поддержкой базы данных sqlite3, эта БД как раз реализована на би-дереве. + + + +### Красно-черное дерево + + + +Самобалансирующееся двоичное дерево поиска, позволяющее быстро выполнять основные операции: добавление, удаление и поиск узла. Сбалансированность достигается за счёт введения дополнительного признака узла дерева — «цвета». Этот атрибут может принимать одно из двух возможных значений — «чёрный» или «красный». Листовые узлы КЧ деревьев не содержат данных, поэтому не требуют выделения памяти — достаточно просто записать в узле-предке нулевой указатель на потомка. Нужно писать свою реализацию. +Возможно, вы читали о том, что при собеседовании в FAANG претендентов «заставляют крутить красно-черное дерево на доске». Это «кружение» и есть балансировка, после операции вставки или удаления элемента дерево нужно отбалансировать, с примерным объемом кода вы можете ознакомиться [здесь](https://blog.boot.dev/python/red-black-tree-python/) или [здесь](https://codereview.stackexchange.com/questions/244971/red-black-tree-implementation-in-python). + + + +### АВЛ-дерево + + + +В АВЛ-деревьях операции вставки и удаления работают медленнее, чем в красно-черных деревьях (при том же количестве листьев красно-чёрное дерево может быть выше АВЛ-дерева, но не более чем в 1,388 раза). Поиск же в АВЛ-дереве выполняется быстрее (максимальная разница в скорости поиска составляет 39 %). Нужно писать свою реализацию. + + + +### Префиксное дерево + + + +[Префиксное дерево](https://en.wikipedia.org/wiki/Trie) (или trie) — структура данных, позволяющая хранить ассоциативный массив, ключами которого являются строки. Нужно писать свою реализацию. + + + +### Таблица выбора структуры данных + + + +В квадратных скобках показан худший случай. + + + +|Структура|Реализация|Применение|Индексация|Поиск|Вставка|Удаление|Память| +|---|---|---|---|---|---|---|---| +|Динамический массив|list||1|n|n|n|n| +|Хэш таблица|dict, set|||1
[n]|1
[n]|1
[n]|n| +|Массив|array, bytes, bytearray|Для хранения однотипных данных|1|n|n|n|n| +|Односвязный список|— (~deque)||n|n|1|1|n| +|Двусвязный список|deque|FIFO, LIFO|n|n|1|1|n| +|Бинарное дерево|-||logn
[n]|logn
[n]|logn
[n]|logn
[n]|n| +|Куча|heapq|Очередь с приоритетом||1
(find min)|logn|logn
(del min)|n| +|B-tree (Би-дерево)|~sqlite|Для памяти с медленным доступом|logn|logn|logn|logn|n| +|КЧ дерево|-||logn|logn|logn|logn|n| +|АВЛ дерево|-||logn|logn|logn|logn|n| +|Префиксное дерево|-|T9,
алгоритм [Ахо–Корасик](https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_algorithm),
алгоритм [LZW](https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch)||key|key|key|| + + + +### Перечисление (Enum, IntEnum) + + + +Удобные конструкции для определения заранее известных перечислений. + + + +``` +from enum import Enum, autoimport randomclass Currency(Enum): euro = 1 us_dollar = 2 yuan = auto()local_currency = Currency.us_dollarprint(local_currency)local_currency = Currency["us_dollar"] # Может вызвать исключение KeyErrorprint(local_currency)local_currency = Currency(2) # Может вызвать исключение ValueErrorprint(local_currency)print(local_currency.name)print(local_currency.value)list_of_members = list(Currency)member_names = [e.name for e in Currency]member_values = [e.value for e in Currency]random_member = random.choice(list(Currency))print(list_of_members, "\n", member_names, "\n", member_values, "\n", random_member) +``` + + + +``` +Currency.us_dollar +Currency.us_dollar +Currency.us_dollar +us_dollar +2 +[, , ] + ['euro', 'us_dollar', 'yuan'] + [1, 2, 3] + Currency.euro +``` + + + +### Целочисленный диапазон (range) + + + +range() возвращает иммутабельную последовательность чисел, которая часто используется как задатчик диапазона для цикла for. + + + +``` +r1: range = range(11) # Возвращает последовательность чисел от 0 до 10r2: range = range(5, 21) # Возвращает последовательность чисел от 5 до 20r3: range = range(20, 9, -2) # Возвращает последовательность чисел от 20 до 10 с шагом 2print("To exclusive: ", end="")for i in r1: print(f"{i} ", end="")print("\nFrom inclusive to exclusive: ", end="")for i in r2: print(f"{i} ", end="")print("\nFrom inclusive to exclusive with step: ", end="")for i in r3: print(f"{i} ", end="")print(f"\nFrom = {r3.start}")print(f"To = {r3.stop}") +``` + + + +``` +To exclusive: 0 1 2 3 4 5 6 7 8 9 10 +From inclusive to exclusive: 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 +From inclusive to exclusive with step: 20 18 16 14 12 10 +From = 20 +To = 9 +``` + + + +### Классы данных (dataclass) + + + +Декоратор, автоматически создающий методы init(), repr() и eq(). Нужен для создания классов, главной задачей которых является хранение данных. Аннотации типов обязательны. Существует более продвинутая альтернатива под названием [attrs](https://pypi.org/project/attrs/). + + + +``` +from dataclasses import dataclassfrom decimal import *from datetime import datetime@dataclassclass Transaction: value: Decimal issuer: str = "Default Bank" dt: datetime = datetime.now()t1 = Transaction(value=1000_000, issuer="Deutsche Bank", dt = datetime(2022, 1, 1, 12))t2 = Transaction(1000)print(t1)print(t2) +``` + + + +``` +Transaction(value=1000000, issuer='Deutsche Bank', dt=datetime.datetime(2022, 1, 1, 12, 0)) +Transaction(value=1000, issuer='Default Bank', dt=datetime.datetime(2022, 9, 6, 17, 50, 36, 162897)) +``` + + + +Dataclass может быть сделан иммутабельным с директивой _frozen=True_. + + + +``` +from dataclasses import dataclass@dataclass(frozen=True)class User: name: str account: int +``` + + + +### Бинарная запаковка (struct) + + + +Запаковка (и распаковка, разумеется) данных в байтовые последовательности с предопределенными размерами каждого элемента данных, их порядка в структуре, а также порядка байт для многобайтовых типов данных. Позволяет превращать Python-овский int в, например, short int или long int ([подробности про систему типов языка Си](https://ru.wikipedia.org/wiki/%D0%A1%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0_%D1%82%D0%B8%D0%BF%D0%BE%D0%B2_%D0%A1%D0%B8)). +При работе со структурами вам нужно будет знать, что такое little-endian и big-endian, а также не забывать, что размер типа данных в Си бывает разным. + + + +``` +from struct import pack, unpack, iter_unpackb = pack(">hhll", 1, 2, 3, 4)print(b)t = unpack(">hhll", b)print(t)i = pack("ii", 1, 2) * 5print(i)print(list(iter_unpack('ii', i))) +``` + + + +``` +b'\x00\x01\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04' +(1, 2, 3, 4) +b'\x01\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00' +[(1, 2), (1, 2), (1, 2), (1, 2), (1, 2)] +``` + + + +## Строка (string) + + + +Строки в Python 3 — иммутабельные последовательности, использующие кодировку Unicode. + + + +``` +se: str = "" # Пустая строкаsi: str = str(12345) # Создает строку из числаsj: str = " ".join(["Follow", "the", "white", "rabbit"]) # Собирает строку из кусочков, используя указанный сепараторprint(f"Joined string: {sj}")is_contains: bool = "rabbit" in sj # Проверка наличия подстрокиis_startswith = sj.startswith("Foll")is_endswith = sj.endswith("bbit")print(f"is_contains = {is_contains}, is_startswith = {is_startswith}, is_endswith = {is_endswith}")sr: str = sj.replace("rabbit", "sheep") # Замена подстроки. Можно указать количество замен: sr: str = sj.replace("rabbit", "sheep", times)print(f"After replace: {sr}")i1 = sr.find("rabbit") # Возвращает стартовый индекс первого вхождения или -1. Есть еще rfind(), начинающий искать с конца строкиi2 = sr.index("sheep") # Возвращает стартовый индекс первого вхождения или выкидывает ValueError. Есть еще rindex(), начинающий искать с конца строкиprint(f"Start index of 'rabbit' is {i1}, start index of 'sheep' is {i2}")d = str.maketrans({"a" : "x", "b" : "y", "c" : "z"})st = "abc".translate(d)print(f"Translate string: {st}")sr = sj[::-1] # Реверс через slice с отрицательным шагомprint(f"Reverse string: {sr}") +``` + + + +``` +Joined string: Follow the white rabbit +is_contains = True, is_startswith = True, is_endswith = True +After replace: Follow the white sheep +Start index of 'rabbit' is -1, start index of 'sheep' is 17 +Translate string: xyz +Reverse string: tibbar etihw eht wolloF +``` + + + +### Datetime + + + +Для работы с датами и временем в _datetime_ есть типы _date_, _time_, _datetime_ и _timedelta_. Все они хешируемы и иммутабельны. + + + +### Конструкторы + + + +``` +from datetime import date, time, datetime, timedeltad: date = date(year=1964, month=9, day=2)t: time = time(hour=12, minute=30, second=0, microsecond=0, tzinfo=None, fold=0)dt: datetime = datetime(year=1964, month=9, day=2, hour=10, minute=30, second=0)td: timedelta = timedelta(weeks=1, days=1, hours=12, minutes=13, seconds=14)print (f"{d}\n {t}\n {dt}\n {td}") +``` + + + +``` +1964-09-02 + 12:30:00 + 1964-09-02 10:30:00 + 8 days, 12:13:14 +``` + + + +### Now + + + +Получение текущей даты или даты/времени. + + + +``` +from datetime import date, datetimeimport pytzimport timed: date = date.today()dt1: datetime = datetime.today()dt2: datetime = datetime.utcnow()dt3: datetime = datetime.now(pytz.timezone('US/Pacific'))t1 = time.time() # Эпоха Unixt2 = time.ctime()print (f"{d}\n {dt1}\n {dt2}\n {dt3}\n {t1}\n {t2}") +``` + + + +``` +2022-09-27 + 2022-09-27 09:47:02.430474 + 2022-09-27 04:47:02.430474 + 2022-09-26 21:47:02.430474-07:00 + 1664254022.4304743 + Tue Sep 27 09:47:02 2022 +``` + + + +### Timezone + + + +Часовые пояса. + + + +``` +from datetime import date, time, datetime, timedelta, tzinfofrom dateutil.tz import UTC, tzlocal, gettz, datetime_exists, resolve_imaginarytz1: tzinfo = UTC # Часовой пояс UTCtz2: tzinfo = tzlocal() # Местный часовой поясtz3: tzinfo = gettz() # Местный часовой поясtz4: tzinfo = gettz("America/Chicago") # Или, например, "Asia/Kolkata". Полный список: en.wikipedia.org/wiki/List_of_tz_database_time_zoneslocal_dt = datetime.today()utc_dt = local_dt.astimezone(UTC) # Конвертация местного часового пояса в часовой пояс UTCprint (f"{tz1}\n {tz2}\n {tz3}\n {tz4}\n {local_dt}\n {utc_dt}") +``` + + + +``` +tzutc() + tzlocal() + tzlocal() + tzfile('US/Central') + 2022-09-27 09:19:35.399362 + 2022-09-27 04:19:35.399362+00:00 +``` + + + +## 2. Обработка данных + + + +![Data management](https://habrastorage.org/r/w1560/getpro/habr/post_images/26e/edf/fd8/26eedffd8bf1081be95015e2dccecfc7.png) + + + +### Срез (slice) + + + +Самый простой метод обработки данных, просто возвращает ту часть данных, местоположение которой (индексы) удовлетворяет определенным условиям. + + + +``` +a:str = "Pack my box with five dozen liquor jugs"start, stop = 8, 21b:str = a[start:stop] # Значения от start до stop-1c:str = a[start:] # Значения от start до конца структурыd:str = a[:stop] # Значения от начала до stop-1e:str = a[:] # Полная копия структурыprint(b, "\n", c, "\n", d, "\n", e, "\n") +``` + + + +``` +box with five + box with five dozen liquor jugs + Pack my box with five + Pack my box with five dozen liquor jugs +``` + + + +Значения start и stop могут быть отрицательными, это будет означать, что отсчет ведется от конца структуры. Можно также использовать значение step, чтобы на выход среза попали не все подряд данные из входной структуры. + + + +``` +a:str = "Step on no pets"b:str = a[-4:] # «Хвостик»c:str = a[::-1] # Реверс входной строкиd:str = a[4::-1] # Первые четыре значения, реверсированыe:str = a[::2] # Каждый второй символprint(b, "\n", c, "\n", d, "\n", e, "\n") +``` + + + +``` +pets + step on no petS + petS + Se nn es +``` + + + +### Сортировка (sort, sorted) + + + +В сортировке всё самое интересное спрятано под капотом (мы ненадолго вернемся к этой теме чуть ниже, в разделе «Алгоритмы»), пока рассмотрим только Python-специфичный синтаксис. +Надо различать методы sort() и sorted(), первый сортирует данные [in-place](https://en.wikipedia.org/wiki/In-place_algorithm), второй порождает новую структуру. + + + +``` +a: list = [5, 2, 3, 1, 4]b: list = sorted(a)print(a, b)a.sort()print(a) +``` + + + +``` +[5, 2, 3, 1, 4] [1, 2, 3, 4, 5] +[1, 2, 3, 4, 5] +``` + + + +И sort(), и sorted() имеют параметр key для указания функции, которая будет вызываться на каждом элементе. Если вам больше по нраву сортировка при помощи функции, принимающей два аргумента (или вы привыкли к cmp в Python 2), присмотритесь к functools.cmp_to_key(). + + + +``` +# Регистрозависимое сравнение строкdinos: str = "Dinosaurs were Big and small"a = sorted(dinos.split())print(a)# Регистронезависимое сравнение строкdinos: str = "Dinosaurs were Big and small"b = sorted(dinos.split(), key=str.lower)print(b) +``` + + + +``` +['Big', 'Dinosaurs', 'and', 'small', 'were'] +['and', 'Big', 'Dinosaurs', 'small', 'were'] +``` + + + +Сложносочиненные структуры данных можно сортировать по key=lambda el: el[1] или даже, например по key=lambda el: (el[1], el[0]). + + + +### Comprehension + + + +Comprehension, которое переводится то как включение в список, то как абстракция списков ([Википедия](https://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D0%B8%D1%81%D0%BA%D0%BE%D0%B2%D0%BE%D0%B5_%D0%B2%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5)), то вообще никак не переводится — способ компактного описания операций обработки списков (а применительно к Python — еще и словарей, и множеств). + + + +Проще говоря, если вам нужно получить из списка другой список, включающий только те значения, которые удовлетворяют какому-то определенному условию, или вычисляемые из первого списка по каким-то определенным правилам, то comprehension — претендент на решение этой задачи № 1. + + + +``` +# Примеры Comprehensiona = [i+1 for i in range(10)] # listb = {i for i in range(10) if i > 5} # setc = (2*i+5 for i in range(10)) # iterd = {i: i**2 for i in range(10)} # dictprint(a,"\n", b, "\n", list(c), "\n", d) +``` + + + +``` +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + {8, 9, 6, 7} + [5, 7, 9, 11, 13, 15, 17, 19, 21, 23] + {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81} +``` + + + +Тут главное не перегнуть палку. Если запись comprehension становится слишком сложной и нечитаемой, возможно, стоит развернуть логику в «нормальный» цикл или в другой более удобочитаемый алгоритм. Comprehension соблазняет записывать «однострочниками» достаточно сложные выражения, но не забывайте, что программист примерно 90 % времени читает код, и только 10 % пишет, так что если выражение будет плохочитаемым, вы усложните жизнь и себе, и свои коллегам. + + + +Есть более-менее [удачные](https://leetcode.com/problems/flipping-an-image/discuss/2378360/python-1-liner-988-speed-97-mem) «однострочники», есть быстрые, но [плохочитаемые](https://leetcode.com/problems/reverse-string-ii/discuss/2281269/python-fast-beats-984-and-short-almost-1-line-solution-with-python-38-features-pep572), написанные из спортивного интереса (это ссылки на решенные мной задачки на leetcode), желательно использовать comprehension в меру; лучше написать понятный развернутый алгоритм, чем непонятный, но обложенный пояснениями (если нет особых требований к производительности, само собой). + + + +Еще немного про list comprehension: + + + +``` +# new_list = [expression for member in iterable (if conditional)]fruits: list = ["Lemon", "Apple", "Banana", "Kiwi", "Watermelon", "Pear"]e_fruits = [fruit for fruit in fruits if "e" in fruit]# ☝ условиеprint(e_fruits)upper_fruits = [fruit.upper() for fruit in fruits]# ☝ выражениеprint(upper_fruits)# Пример разбиения списка на фрагменты одинаковой длиныchunk_len = 2chunk_fruits = [fruits[i:i + chunk_len] for i in range(0, len(fruits), chunk_len)]print(chunk_fruits) +``` + + + +``` +['Lemon', 'Apple', 'Watermelon', 'Pear'] +['LEMON', 'APPLE', 'BANANA', 'KIWI', 'WATERMELON', 'PEAR'] +[['Lemon', 'Apple'], ['Banana', 'Kiwi'], ['Watermelon', 'Pear']] +``` + + + +Dict comprehension, включение в словарь: + + + +``` +# new_dict = {expression for member in iterable (if conditional)}d: dict = {"Italy": "Pizza", "US": "Hot-Dog", "China": "Dim Sum", "South Korea": "Kimchi"}print(d)a: dict = {k: v for k, v in d.items() if "i" in v} # Вернет новый словарь, отфильтрованный по значениюprint(a)b: dict = {k: v for k, v in d.items() if "i" in k} # Вернет новый словарь, отфильтрованный по ключуprint(b)c: dict = {k: v for k, v in d.items() if len(v) >= 7} # Вернет новый словарь, отфильтрованный по длине значенийprint(c) +``` + + + +``` +{'Italy': 'Pizza', 'US': 'Hot-Dog', 'China': 'Dim Sum', 'South Korea': 'Kimchi'} +{'Italy': 'Pizza', 'China': 'Dim Sum', 'South Korea': 'Kimchi'} +{'China': 'Dim Sum'} +{'US': 'Hot-Dog', 'China': 'Dim Sum'} +``` + + + +Попробуйте самостоятельно поиграться с set comprehension. Не забывайте, что set «переваривает» только уникальные значения, поэтому в результате вы можете получить не совсем то, на что рассчитывали. + + + +Попробуйте также освоить nested (вложенный) comprehension, используя конструкции вида [[func(y) for y in x] for x in n]. Для примера создайте двумерный массив, содержащий случайные значения, среднее значение которых плавно нарастает ближе к правому нижнему углу (если не получится, готовый пример есть чуть ниже, в коде, иллюстрирующем применение matplotlib). + + + +### Операции над строками. lower(), upper(), capitalize() и title() + + + +``` +s: str = "camelCase string"print(s.lower())print(s.upper())print(s.capitalize())print(s.title()) +``` + + + +``` +camelcase string +CAMELCASE STRING +Camelcase string +Camelcase String +``` + + + +### strip() + + + +``` +s: str = " ~~##A big blahblahblah##~~ "s = s.strip() # Strips all whitespace characters from both endsprint(s)s = s.strip("~#") # Strips all passed characters from both endsprint(s)s = s.lstrip(" A") # Strips all passed characters from left endprint(s)s = s.rstrip("habl") # Strips all passed characters from right endprint(s) +``` + + + +``` +~~##A big blahblahblah##~~ +A big blahblahblah +big blahblahblah +big +``` + + + +### split() + + + +``` +s1: str = "Follow the white rabbit, Neo"c1 = s1.split() # Splits on one or more whitespace charactersprint(c1)c2 = s1.split(sep=", ", maxsplit=1) # Splits on "sep" str at most "maxsplit" timesprint(c2)s2: str = "Beware the Jabberwock, my son!\n The jaws that bite, the claws that catch!"c3 = s2.splitlines(keepends=False) # On [\n\r\f\v\x1c-\x1e\x85\u2028\u2029] and \r\n.print(c3)# split() vs rsplit()c4 = s2.split(maxsplit=2)c5 = s2.rsplit(maxsplit=2)print(c4, c5) +``` + + + +``` +['Follow', 'the', 'white', 'rabbit,', 'Neo'] +['Follow the white rabbit', 'Neo'] +['Beware the Jabberwock, my son!', ' The jaws that bite, the claws that catch!'] +['Beware', 'the', 'Jabberwock, my son!\n The jaws that bite, the claws that catch!'] ['Beware the Jabberwock, my son!\n The jaws that bite, the claws', 'that', 'catch!'] +``` + + + +### ord(), chr() + + + +``` +s1: str = "abcABC!"for ch in s1: print(f"{ch} -> {ord(ch)}") # Returns an integer representing the Unicode characternums = [72, 101, 108, 108, 111, 33]for num in nums: print(f"{num} -> {chr(num)}") +``` + + + +``` +a -> 97 +b -> 98 +c -> 99 +A -> 65 +B -> 66 +C -> 67 +! -> 33 +72 -> H +101 -> e +108 -> l +108 -> l +111 -> o +33 -> ! +``` + + + +### Regex + + + +Регулярные выражения — отдельная область знаний, и весьма-весьма непростая область. Тут, пожалуй, самое время для бородатой шутки про то, что если вы решили свою проблему при помощи регулярных выражений — теперь у вас две проблемы. + + + +Регулярки похожи на вхождение в воду на пляже острова Гуам в сторону Марианской впадины — даже когда вы думаете, что погрузились _реально_ глубоко, то, скорее всего, вы просто не видите впередилежащей бездны. Но — знать регулярные выражения, хотя бы на начальном уровне, необходимо для решения целого класса задач, а то, что вёрткие регулярки периодически поворачиваются к вам своими, кхм… новыми гранями, придется простить, переварить и принять. + + + +Вот [здесь](https://habr.com/ru/post/349860/) есть грамотное и методически выдержанное введение в тему, пока же окинем взглядом основные возможности регулярных выражений: + + + +``` +import res1: str = "123 abc ABC 456"m1 = re.search("[aA]", s1) # Ищет первое вхождение паттерна, при неудаче возвращает Noneprint(m1, m1.group(0))m2 = re.fullmatch("[aA]", s1) # Проверка, подходит ли строка под шаблонprint(m2)c1: list = re.findall("[aA]", s1) # Найти в строке все непересекающиеся шаблоныprint(c1)def replacer(s): return chr(ord(s[0]) + 1) # Следующий символ из алфавитаs2 = re.sub("\w", replacer, s1) # Вы можете использовать функцию вместо шаблонаprint(s2)c2 = re.split("\d", s1)print(c2)iter = re.finditer("\D", s1) # Итератор по непересекающимся шаблонамfor ch in iter: print(ch.group(0), end= "") +``` + + + +``` + a +None +['a', 'A'] +234 bcd BCD 567 +['', '', '', ' abc ABC ', '', '', ''] + abc ABC +``` + + + +### Match Object + + + +``` +import rem3 = re.match(r"(\w+) (\w+)", "John Connor, leader of the Resistance")s3: str = m3.group(0) # Возвращает полное совпадениеs4: str = m3.group(1) # Возвращает часть в первых скобкахt1: tuple = m3.groups()start: int = m3.start() # Возвращает начальный индекс совпаденияend: int = m3.end() # Возвращает конечный индекс совпаденияt2: tuple[int, int] = m3.span() # Кортеж (start, end)print (f"{s3}\n {s4}\n {t1}\n {start}\n {end}\n {t2}\n") +``` + + + +``` +John Connor + John + ('John', 'Connor') + 0 + 11 + (0, 11) +``` + + + +### Создание переменных datetime + + + +Python использует Unix Epoch: "1970-01-01 00:00 UTC" + + + +``` +from datetime import datetimefrom dateutil.tz import tzlocaldt1: datetime = datetime.fromisoformat("2021-10-04 00:05:23.555+00:00") # Может вызвать ValueErrordt2: datetime = datetime.strptime("21/10/04 17:30", "%d/%m/%y %H:%M") # Подробнее про форматы - https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codesdt3: datetime = datetime.fromordinal(100_000) # 100000-й день от 1.1.0001dt4: datetime = datetime.fromtimestamp(20_000_000.01) # Время в секундах с начала Unix Epochtz = tzlocal()dt5: datetime = datetime.fromtimestamp(20_000_000.01, tz) # С учетом часового поясаprint (f"{dt1}\n {dt2}\n {dt3}\n {dt4}\n {dt5}") +``` + + + +``` +2021-10-04 00:05:23.555000+00:00 + 2004-10-21 17:30:00 + 0274-10-16 00:00:00 + 1970-08-20 16:33:20.010000 + 1970-08-20 16:33:20.010000+05:00 +``` + + + +### Преобразование переменных datetime + + + +``` +from datetime import datetimedt1: datetime = datetime.today()s1: str = dt1.isoformat()s2: str = dt1.strftime("%d/%m/%y %H:%M") # https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codesi: int = dt1.toordinal()a: float = dt1.timestamp() # Секунды с начала Unix Epochprint (f"{dt1}\n {s1}\n {s2}\n {i}\n {a}") +``` + + + +``` +2022-09-06 17:50:38.041159 + 2022-09-06T17:50:38.041159 + 06/09/22 17:50 + 738404 + 1662468638.041159 +``` + + + +### Арифметика datetime + + + +``` +from datetime import date, time, datetime, timedeltafrom dateutil.tz import UTC, tzlocal, gettz, datetime_exists, resolve_imaginaryd: date = date.today()dt1: datetime = datetime.today()dt2: datetime = datetime(year=1981, month=12, day=2)td1: timedelta = timedelta(days=5)td2: timedelta = timedelta(days=1)d = d + td1 # date = date ± timedeltadt3 = dt1 - td1 # datetime = datetime ± timedeltatd3 = dt1 - dt2 # timedelta = datetime - datetimetd4 = 10 * td1 # timedelta = const * timedeltac: float = td1/td2 # timedelta/timedeltaprint (f"{d}\n {dt3}\n {td3}\n {td4}\n {c}") +``` + + + +``` +2022-09-11 + 2022-09-01 17:50:38.132916 + 14888 days, 17:50:38.132916 + 50 days, 0:00:00 + 5.0 +``` + + + +### bisect и бинарный поиск + + + +Бинарный поиск существенно быстрее, чем обычный (см. раздел «Алгоритмы»), но требует предварительной сортировки коллекции, по которой осуществляется поиск. + + + +``` +import bisecta: list[int] = [12, 6, 8, 19, 1, 33]a.sort()print(f"Sorted: {a}")print(bisect.bisect(a, 20)) # Найти индекс для потенциальной вставкиbisect.insort(a, 15) # Вставка значения в отсортированную последовательностьprint(a)# Бинарный поискdef binary_search(a, x, lo=0, hi=None): if hi is None: hi = len(a) pos = bisect.bisect_left(a, x, lo, hi) return pos if pos != hi and a[pos] == x else -1print(binary_search(a, 15)) +``` + + + +``` +Sorted: [1, 6, 8, 12, 19, 33] +5 +[1, 6, 8, 12, 15, 19, 33] +4 +``` + + + +### Функциональное программирование (Map, Filter, Reduce, Partial) + + + +На случай, если начиная с этого момента и до конца текущего жизненного цикла вы собираетесь к месту и не месту использовать приёмы функционального программирования, чтобы сделать свой код «воистину крутым», просто процитирую вам Джоэля Граса, автора книги «Data Science: Наука о данных с нуля»: «В первом издании этой книги были представлены функции partial, map, reduce и filter языка Python. На своем пути к просветлению я понял, что этих функций лучше избегать, и их использование в книге было заменено включениями в список, циклами и другими, более Python'овскими конструкциями». Такие дела… + + + +``` +import functools# Преобразует все входящие значения при помощи указанной функцииiter1 = map(lambda x: x + 1, range(10))print(list(iter1))# Передает в выходной итератор только значения, удовлетворяющие условиюiter2 = filter(lambda x: x > 5, range(10))print(list(iter2))# Применяет указанную функцию ко всей последовательности входных данных, сводя их к единственному значениюa = functools.reduce(lambda out, x: out + x, range(10))print(a) +``` + + + +``` +[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +[6, 7, 8, 9] +45 +``` + + + +``` +import functoolsdef sum(a,b): return a + badd_const = functools.partial(sum, 10)print(add_const(5)) +``` + + + +``` +15 +``` + + + +Если вам не сразу станет понятно, как работает функция partial (и зачем она нужна), не расстраивайтесь, вы не одиноки :). Вот, пожалуйста, тема на Stackoverflow: «[I am not able to get my head on how the partial works](https://stackoverflow.com/questions/15331726/how-does-functools-partial-do-what-it-does)». Там, кстати, есть совет, как partial могут быть полезны при организации pipe с включением функций, имеющих разное количество аргументов. + + + +### Any, All + + + +any() вернет True, если хотя бы один элемент итерируемой коллекции истинен, all() вернет True только в случае истинности всех элементов коллекции. + + + +``` +animals = ["Squirrel", "Beaver", "Fox"]sentence = "Bison likes squirrels and beavers"any_animal: bool = any(animal.lower() in sentence.lower() for animal in animals)print(any_animal)all_animal: bool = all(animal.lower() in sentence.lower() for animal in animals)print(all_animal) +``` + + + +``` +True +False +``` + + + +### Файлы + + + +Файловые операции стоят немного особняком от остальных методов обработки данных, как подразумевающие не сиюминутную торопливую обработку информации, а взаимодействие с неким постоянным энергонезависимым хранилищем данных. Так что если вам нужно сохранить данные _на завтра_, или, наоборот, нужно прочитать данные, которые вам предоставили _неделю назад_, то вам, очевидно, нужно будет работать с файлами. В файлах же осядет информация, которую мы передаем базам данных, но эту тему мы рассмотри ниже. + + + +``` +f = open("f.txt", mode='r', encoding="utf-8", newline=None)print(f.read()) +``` + + + +``` +Hello from file! +``` + + + +На всякий случай, если вы испытываете программистский зуд даже небольшой степени выраженности, напоминаю — обязательно прогоняйте в IDE все непонятные куски кода, не надо на них _смотреть_, их надо видоизменять, корректировать, дорабатывать; только когда концы свяжутся, только когда вы поймете, как функционирует этот кусочек кода, только тогда промелькнёт маленькая искорка и ваша квалификация как программиста немного подрастёт. + + + +Режимы (mode): +"r" — чтение (поведение по умолчанию) +"w" — запись (информация, ранее присутствующая в файле, будет стёрта) +"x" — эксклюзивное создание и запись; если файл уже существует, будет выброшено исключение FileExistsError +"a" — открытие с последующим добавлением в конец файла +"w+" — чтение и запись +"r+" — чтение и запись с начала файла +"a+" — чтение и запись с конца файла +"t" — текстовый режим ("rt", "wt" и т. д.; поведение по умолчанию) +"b" — двоичный режим ("rb", "wb", "xb" и т. д.) + + + +encoding=None — будет использована кодировка по умолчанию (зависит от системы, см. getpreferredencoding()). Если нет специальных требований, просто используйте везде encoding="utf-8"; без этого, например, русский текст запишется в текстовый файл в виде человеконечитаемой последовательности. + + + +newline=None — при чтении системные символы конца строки будут конвертированы в "\n"; при записи, наоборот, "\n" будут конвертированы в системные символы конца строки. + + + +Возможные исключения при работе с файлами: +_FileNotFoundError_ при чтении в режиме "r" или "r+". +_FileExistsError_ при записи в режиме "x". +_IsADirectoryError_, _PermissionError_ — в любом режиме. + + + +### Чтение из файла + + + +Открывает файл и возвращает файловый объект. +Для работы с файлами лучше использовать менеджеры контекста (рассмотрены ниже), т. е. конструкции вида "with open...". Даже если что-то пойдет не так, как задумано (например, вы не обработаете исключение во время работы с файлом), менеджер контекста «зачистит хвосты», и ваша оплошность не отразится, например, на файловой системе. + + + +``` +with open("f.txt", encoding="utf-8") as f: chars = f.read(5) # Reads chars/bytes or until EOF print(chars) f.seek(0) # Moves to the start of the file. Also seek(offset) and seek(±offset, anchor), where anchor is 0 for start, 1 for current position and 2 for end lines: list[str] = f.readlines() # Also readline() print(lines) +``` + + + +``` +Hello +['Hello from file!'] +``` + + + +### Запись в файл + + + +``` +with open("f.txt", "w", encoding="utf-8") as f: f.write("Hello from file!") # Или f.writelines() +``` + + + +### JSON + + + +Человекочитаемый формат для хранения и передачи данных. + + + +``` +import jsond: dict = {1: "Lemon", 2: "Apple", 3: "Banana!"}object_as_string: str = json.dumps(d, indent=2)print(object_as_string)restored_object = json.loads(object_as_string)# Write object to JSON filewith open("1.json", 'w', encoding='utf-8') as file: json.dump(d, file, indent=2)# Read object from JSON filewith open("1.json", encoding='utf-8') as file: restored_from_file = json.load(file)print(restored_from_file) +``` + + + +``` +{ + "1": "Lemon", + "2": "Apple", + "3": "Banana!" +} +{'1': 'Lemon', '2': 'Apple', '3': 'Banana!'} +``` + + + +### Pickle + + + +Бинарный формат для хранения и передачи данных. + + + +``` +import pickled: dict = {1: "Lemon", 2: "Apple", 3: "Banana!"}# Запись объекта в бинарный файлwith open("1.bin", "wb") as file: pickle.dump(d, file)# Чтение объекта из файлаwith open("1.bin", "rb") as file: restored_from_file = pickle.load(file)print(restored_from_file) +``` + + + +``` +{1: 'Lemon', 2: 'Apple', 3: 'Banana!'} +``` + + + +### Protocol Buffers + + + +Если вы хотите передавать и хранить данные, используя универсальную структуру, одинаково хорошо понимаемую всеми языками программирования (как JSON) и занимающую мало места (как Pickle), то можно посмотреть в сторону Protocol Buffers ([Wikipedia](https://en.wikipedia.org/wiki/Protocol_Buffers), [примеры для Python](https://developers.google.com/protocol-buffers/docs/pythontutorial)). Есть еще альтернативы, например, [FlatBuffers](https://google.github.io/flatbuffers/), [Apache Avro](https://avro.apache.org/) или [Thrift](https://thrift.apache.org/). + + + +### Пути (Paths) + + + +При работе с файлами не обойтись без манипулирования файловыми путями. + + + +``` +from os import getcwd, path, listdirfrom pathlib import Paths1: str = getcwd() # Возвращает текущую рабочую директориюprint(s1)s2: str = path.abspath("f.txt") # Возвращает полный путьprint(s2)s3: str = path.basename(s2) # Возвращает имя файлаs4: str = path.dirname(s2) # Возвращает путь без файлаt1: tuple = path.splitext(s2) # Возвращает кортеж из пути и имени файлаprint(s3, s4, t1)p = Path(s2)st = p.stat()print(st)b1: bool = p.exists()b2: bool = p.is_file()b3: bool = p.is_dir()print(b1, b2, b3)c: list = listdir(path=s1) # Возвращает список имен файлов, находящихся по указанному путиprint(c)s5: str = p.stem # Возвращает имя файла без расширенияs6: str = p.suffix # Возвращает расширение файлаt2: tuple = p.parts # Возвращает все элементы пути как отдельные строкиprint(s5, s6, t2) +``` + + + +``` +c:\Works\amaargiru\pycore +c:\Works\amaargiru\pycore\f.txt +f.txt c:\Works\amaargiru\pycore ('c:\\Works\\amaargiru\\pycore\\f', '.txt') +os.stat_result(st_mode=33206, st_ino=2251799814917120, st_dev=3628794147, st_nlink=1, st_uid=0, st_gid=0, st_size=16, st_atime=1662468638, st_mtime=1662468638, st_ctime=1661089564) +True True False +['.git', '.gitignore', '.pytest_cache', '01_python.ipynb', '01_python.md', '02_postgre.md', '03_architecture.md', '04_algorithms.ipynb', '04_algorithms.md', '05_admin_devops.md', '06_pytest_mock.ipynb', '06_pytest_mock.md', '07_fastapi.md', '08_flask.md', '1.bin', '1.json', 'compose_readme.bat', 'coupling_vs_cohesion.svg', 'f.txt', 'gitflow.svg', 'graph_for_dfs.jpg', 'pycallgraph3.png', 'readme.md'] +f .txt ('c:\\', 'Works', 'amaargiru', 'pycore', 'f.txt') +``` + + + +### Простейшие вычисления — Sum, Count, Min, Max + + + +``` +a: list[int] = [1, 2, 3, 4, 5, 2, 2]s = sum(a)print(s)c = a.count(2) # Вернет количество вхожденийprint(c)mn = min(a)print(mn)mx = max(a)print(mx) +``` + + + +``` +19 +3 +1 +5 +``` + + + +Присмотритесь к [встроенным функциям](https://docs.python.org/3/library/functions.html), там есть еще кое-что, касающееся элементарной математики. + + + +### Базовая математика + + + +``` +from math import pia: float = pi ** 2 # Or pow(pi, 2)print(f"Power: {a}")b: float = round(pi, 2)print(f"Round: {b}")c: int = round(256, -2)print(f"Int round: {c}")d: float = abs(-pi)print(f"Abs: {d}")e: float = abs(10+10j) # Or e: float = abs(complex(real=10, imag=10))print(f"Complex abs: {e}") +``` + + + +``` +Power: 9.869604401089358 +Round: 3.14 +Int round: 300 +Abs: 3.141592653589793 +Complex abs: 14.142135623730951 +``` + + + +### Побитовые операции + + + +``` +a: int = 0b01010101b: int = 0b10101010print(f"And: 0b{a&b:08b}")print(f"Or: 0b{a|b:08b}")print(f"Xor: 0b{a^b:08b}")print(f"Left shift: 0b{a << 4:08b}")print(f"Right shift: 0b{b >> 4:08b}")print(f"Not: 0b{~a:08b}") +``` + + + +``` +And: 0b00000000 +Or: 0b11111111 +Xor: 0b11111111 +Left shift: 0b10101010000 +Right shift: 0b00001010 +Not: 0b-1010110 +``` + + + +### Подсчет битов + + + +``` +a: int = 4242print(f"{a} in binary format: 0b{a:b}")c = a.bit_count() # Returns the number of ones in the binary representation of the absolute value of the integerprint(f"Bit count: {c}") +``` + + + +``` +4242 in binary format: 0b1000010010010 +Bit count: 4 +``` + + + +### Fractions + + + +``` +from fractions import Fractionf = Fraction("0.2").as_integer_ratio()print(f) +``` + + + +``` +(1, 5) +``` + + + +### Евклидово расстояние между двумя точками + + + +``` +import mathp1 = (0.22, 1, 12)p2 = (-0.12, 3, 7)print(math.dist(p1, p2)) +``` + + + +``` +5.39588732276722 +``` + + + +### NumPy + + + +Мини-язык для манипулирования массивами. На удачных сценариях работает в сотни раз быстрее встроенных функций. Еще более быстрая альтернатива работает на GPU, называется [CuPy](https://github.com/cupy/cupy) и опять-таки [обещает](https://medium.com/rapids-ai/single-gpu-cupy-speedups-ea99cbbb0cbb) стократный прирост производительности, только уже по сравнению с NumPy. Так что если вам нужен какой-нибудь быстрый [FFT](https://en.wikipedia.org/wiki/Fast_Fourier_transform) или еще какой числогрыз, то вы знаете, что делать. Если вы дружите с английским, то изучайте [официальный мануал](https://numpy.org/doc/stable/), если нет — на «Хабре» есть [перевод](https://habr.com/ru/post/352678/) (как всегда, читайте комментарии, там немало полезного). + + + +Небольшое отступление. + + + +Во-первых, тут мы переходим границу между встроенной функциональность языка и внешними библиотеками. Надо понимать, что успех Python во многом основан именно на богатстве его экосистемы (хотя, впрочем, то же самое можно сказать и про JavaScript, и про C#); сам язык предоставляет богатую, но всё же ограниченную функциональность, в то время как функционал внешних библиотек практически безграничен; это как бесконечно разнообразные кубики Лего. Соответственно, очень часто для решения задачи не нужно реализовывать алгоритм с нуля на чистом Python'е, достаточно подобрать нужную библиотеку. + + + +Во-вторых, популярность разных библиотек Python (в том числе и конкурирующих) сильно разнится. Например, NumPy — очень популярная библиотека, но в мире существуют буквально миллионы Python-разработчиков, которые _никогда_ не работали с NumPy, просто в силу своего круга функциональных обязанностей. + + + +Для начинающего разработчика это представляет собой довольно нешуточную проблему — как конкретно двигаться вперед, какие библиотеки изучать, ведь знания чистого Python, как правило, недостаточно для формирования актуального резюме. + + + +Дам вам небольшой совет. Ежегодно компания JetBrains (делающая среди прочего очень классную IDE PyCharm) проводит всемирный опрос Python-разработчиков, а потом выкладывает полученные результаты в виде так называемого [Python Developers Survey Results](https://lp.jetbrains.com/python-developers-survey-2021/). Например, если вы почитаете результаты последнего исследования, то найдете там довольно чёткие ориентиры: скажем, в разделе «Data science frameworks and libraries» в топе находятся NumPy, Pandas (рассмотрен ниже) и Matplotlib; в тестировании с большим отрывом лидирует pytest (смотри ниже), в других областях вперед вырываются Flask (Django на втором месте с крошечным отрывом), SQLAlchemy (vs Django ORM) и PostgreSQL (vs SQLite), про них мы тоже еще поговорим. Так что в целом, общее направление развития определить можно. + + + +Однако, вернемся к NumPy. Не забывайте, что в основе NumPy лежат массивы, а все данные в массиве должны быть одинакового типа (просто на случай, если вы уже познали пр-р-рел-л-лесть списков Python). Создание массивов: + + + +``` +import numpy as npa1 = np.array([1, 2, 3, 4, 5], float) # Получение массива из спискаprint(a1[0:2])a2 = np.zeros(5) # Массив, заполненный нулямиprint(a2)a3 = np.arange(0, 6, 1) # Использование диапазона, np.arange(from_inclusive, to_exclusive, step_size)print(a3)a4 = np.random.randint(6, size=10) # Создание массива, содержащего случайные значения, np.random.randint(low_inclusive, high_exclusive=None, size=None, dtype=int)print(a4)a5 = np.random.randint(6, size=(2, 5)) # Создание многомерного массива, содержащего случайные значенияprint(a5)print(a5.shape) # Число строк и столбцов в массивеprint(a5.dtype) # Тип переменныхprint(1 in a5) # Проверка наличия элемента +``` + + + +``` +[1. 2.] +[0. 0. 0. 0. 0.] +[0 1 2 3 4 5] +[2 2 4 0 0 0 0 4 0 5] +[[1 0 3 5 0] + [3 1 4 2 2]] +(2, 5) +int32 +True +``` + + + +Базовые математические операции ([полный список](https://numpy.org/doc/stable/reference/routines.math.html)): + + + +``` +import numpy as npa1 = np.array([1, 2, 3, 4, 5])a2 = np.array([6, 7, 8, 9, 10])a3 = a1 + 1print(a3)a4 = a1 + a2print(a4)a5 = a1 ** 3print(a5)a6 = a1 ** a2print(a6) +``` + + + +``` +[2 3 4 5 6] +[ 7 9 11 13 15] +[ 1 8 27 64 125] +[ 1 128 6561 262144 9765625] +``` + + + +Вообще, можно сказать, что быстрые математические операции над многомерными массивами — это главная «фишка» NumPy. Вы просто говорите: возьми такие-то массивы и проделай над ними такую-то операцию. Далее все эти данные «проваливаются» в высокоскоростное ядро NumPy, где к ним уже можно применить всю мощь вашего процессора, которая раньше была вам недоступна (ну, или доступна не полностью) из-за ограничений Python-интерпретатора. Так что, если вы пытаетесь в цикле итерировать массив NumPy, по факту передавая данные на нижний уровень небольшими порциями (например, объектами row), то имейте в виду, что тем самым используете возможности NumPy недостаточно эффективно; попробуйте решить задачу без итерирования. + + + +### Sum, Min, Max + + + +``` +import numpy as npa1 = np.random.randint(6, size=(2, 10)) # NumPy поддерживает несколько десятков видов распределений, например, Пуассона и Стьюдентаprint(a1)s = np.sum(a1) # Сумма всех элементовprint(s)mn = a1.min(axis=0) # Наименьшие числа в каждом столбцеprint(mn)mx = a1.max(axis=1) # Наибольшие числа в каждой строкеprint(mx)amin = a1.argmin(axis=0) # Индексы минимальных элементов в каждом столбцеprint(amin)amax = a1.argmax(axis=1) # Индексы максимальных элементов в каждой строкеprint(amax)uniq = np.unique(a1) # Извлечение уникальных элементовprint(uniq) +``` + + + +``` +[[3 0 4 1 0 5 4 0 1 3] + [0 3 4 0 0 1 4 0 5 4]] +42 +[0 0 4 0 0 1 4 0 1 3] +[5 5] +[1 0 0 1 0 1 0 0 0 0] +[5 8] +[0 1 3 4 5] +``` + + + +В качестве домашнего задания попробуйте самостоятельно применить prod(), mean(), var(), std(), median(), cov() и corrcoef(). + + + +Форматирование массивов: + + + +``` +import numpy as npa = np.random.randint(6, size=(3, 5))print(a)a1 = a.reshape((5, 3)) # Форматирование. Если есть возможность, создается новый view на те же самые данныеprint(a1)a.shape = (5, 3) # Форматирование in-placeprint(a)print(a.shape)a = a[:, :, np.newaxis] # Увеличение размерности массива с 2 до 3print(a)print(a.shape)a = a.flatten() # Конвертация в одномерный массивprint(a)print(a.shape) +``` + + + +``` +[[5 5 5 1 1] + [0 2 0 5 5] + [0 2 5 4 5]] +[[5 5 5] + [1 1 0] + [2 0 5] + [5 0 2] + [5 4 5]] +[[5 5 5] + [1 1 0] + [2 0 5] + [5 0 2] + [5 4 5]] +(5, 3) +[[[5] + [5] + [5]] + + [[1] + [1] + [0]] + + [[2] + [0] + [5]] + + [[5] + [0] + [2]] + + [[5] + [4] + [5]]] +(5, 3, 1) +[5 5 5 1 1 0 2 0 5 5 0 2 5 4 5] +(15,) +``` + + + +Копирование массивов: + + + +``` +import numpy as npimport copya = np.random.randint(10, size=(4, 4))print(a)# Неглубокая (shallow) копияa1 = np.copy(a)# Глубокая (deep) копияa2 = copy.deepcopy(a)# Копирование ссылкиa3 = aa[0, 0] = 10print(a[0, 0] == a1[0, 0])print(a[0, 0] == a2[0, 0])print(a[0, 0] == a3[0, 0]) +``` + + + +``` +[[5 3 1 4] + [0 8 7 0] + [1 7 4 7] + [5 3 5 2]] +False +False +True +``` + + + +NumPy очень мощный инструмент, не зря же он стоит на первом месте в списке «Data science frameworks and libraries» обзора, который мы упоминали чуть выше. Но углубляться в эту тему очень уж глубоко в рамках нашего достаточно поверхностного очерка, пожалуй, не стоит; вряд ли прямо сейчас вам кровь из носу нужно освоить скалярное, тензорное и внешнее произведение матриц или познать (вспомнить?) специфику линейной алгебры. Думаю, даже если мы сейчас начнем описывать транспонирование или выбор оси, по которой будет произведена конкатенация массивов, то это уже будет, что называется, «не в коня корм». +К тому же, изучая тонкости употребления NumPy, начинает появляться соблазн упоминания SciPy, предоставляющего еще более широкий функционал, а после первого "import scipy" у нас начнётся уже полное непотребство. Давайте пока пройдем мимо этой кроличьей норы, для первого знакомства она слишком глубока. + + + +Единственное, что еще можно освоить в конце ознакомительного курса NumPy — взаимодействие с внешним миром. Изучите для начала load/save/savez (бинарники) и loadtxt/savetxt (человекочитаемый формат). + + + +### Pandas + + + +Библиотека обработки и анализа данных. Работа с данными строится поверх библиотеки NumPy. +В первом, грубом приближении pandas можно воспринимать как связку «Excel + VisualBasic-скрипты», только более гибкую и удобную. Библиотека создает своеобразный мостик между профессиями Python-программиста, дата-сайентиста и аналитика, позволяя сосредоточиться в большей степени именно на очистке и анализе данных, на читабельности отчетов, а не на программировании. Pandas также поддерживает широкий спектр «красивостей» при выводе информации, позволяя, например, добавлять в выводимые данные градиентную подсветку (heatmap) или визуализировать отклонение от среднего (bar chart). + + + +Для того чтобы как следует «распробовать» pandas, по-хорошему надо загрузить какой-нибудь развесистый набор данных, но мы, пожалуй, не будем погружаться в глубины глубин, просто поиграем небольшим самодельным датасетом. + + + +``` +import pandas as pds = pd.Series([0, 1, 4, 7, 8, 10, 12])print(s)print(s[2]) +``` + + + +``` +0 0 +1 1 +2 4 +3 7 +4 8 +5 10 +6 12 +dtype: int64 +4 +``` + + + +Series — базовая структура данных pandas. Вы можете воспринимать её как упорядоченный словарь или как строку Excel, смотря по тому, какая аналогия вам ближе. + + + +``` +import pandas as pds = pd.Series([0, 1, 4, 7, 8, 10, 12], index=["a", "b", "c", "d", "x", "y", "z"]) # Индексы Series можно задавать вручнуюprint(s)print(s["x"])print(s[["x", "y", "z"]]) # Выборкаprint(s[s > 5]) # Фильтрацияprint(s.max()) # Математика, примерно как в NumPyprint(s.sum()) +``` + + + +``` +a 0 +b 1 +c 4 +d 7 +x 8 +y 10 +z 12 +dtype: int64 +8 +x 8 +y 10 +z 12 +dtype: int64 +d 7 +x 8 +y 10 +z 12 +dtype: int64 +12 +42 +``` + + + +При объединении нескольких Series получается DataFrame, вторая базовая структура данных pandas, которую в первом приближении можно рассматривать как лист Excel. + + + +``` +import pandas as pdfrom pandas import DataFrames1 = pd.Series([0, 1, 4, 7, 8, 10, 12])s2 = pd.Series([0, 100, 200, 300, 600, 900, 1200])df = pd.DataFrame([s1, s2])print(df)print(df[1])print(df[2][0])print(df.iloc[0][2:4]) +``` + + + +``` + 0 1 2 3 4 5 6 +0 0 1 4 7 8 10 12 +1 0 100 200 300 600 900 1200 +0 1 +1 100 +Name: 1, dtype: int64 +4 +2 4 +3 7 +Name: 0, dtype: int64 +``` + + + +Давайте сделаем что-то более похожее на реальный анализ данных. При помощи формулы [ИМТ](https://ru.wikipedia.org/wiki/%D0%98%D0%BD%D0%B4%D0%B5%D0%BA%D1%81_%D0%BC%D0%B0%D1%81%D1%81%D1%8B_%D1%82%D0%B5%D0%BB%D0%B0) выясним, кто из знаменитостей не следит за собой и обзавелся лишним весом: + + + +``` +from pandas import DataFrameimport matplotlib.pyplot as pltdef bmi(row): return row["weight"] / row["height"] ** 2if __name__ == '__main__': celebs: dict = {"Britney Spears": {"height": 1.63, "weight": 57}, "Melanie Griffith": {"height": 1.73, "weight": 63}, "Kylie Minogue": {"height": 1.52, "weight": 46}, "Hulk Hogan": {"height": 1.98, "weight": 137}} df = DataFrame(celebs) # Создаем DataFrame df.loc["bmi"] = df.apply(lambda row: bmi(row), axis=0) # Добавлем новую строку с ИМТ df = df.sort_values(by="bmi", ascending=True, axis=1) # Сортируем print(df) df.loc["bmi"].plot.bar() # Визуализация plt.show() +``` + + + +``` + Kylie Minogue Melanie Griffith Britney Spears Hulk Hogan +height 1.520000 1.730000 1.630000 1.980000 +weight 46.000000 63.000000 57.000000 137.000000 +bmi 19.909972 21.049818 21.453574 34.945414 +``` + + + +![png](https://habrastorage.org/r/w1560/getpro/habr/post_images/501/fe2/953/501fe29532c00ff9b3e59fd7754455e7.png) + + + +На самом деле Халк, конечно, не толстяк, а профессиональный спортсмен, к которым формула ИМТ малоприменима, но крошка Кайли действительно вырывается вперед, даже с учетом своего небольшого роста. + + + +### Matplotlib/Seaborn + + + +Библиотеку визуализации matplotlib мы уже слегка задействовали в примере выше. Прямо здесь и прямо сейчас глубоко погружаться в разбор возможностей matplotlib/seaborn, наверное, особого смысла не имеет; все вы видели примеры иллюстраций в научной и бизнес-литературе и, разумеется, все эти графики и иллюстрации можно повторить при помощи рассматриваемых библиотек. + + + +Давайте просто для затравки нарисуем пару симпатичных визуализаций, чтобы наглядно показать полезность качественного оформления результатов проделанной работы. + + + +Тепловая карта (heatmap), наглядно показывающая достижения отдельных членов команды: + + + +``` +from random import randrangeimport numpy as npimport matplotlib.pyplot as pltimport uuidtargets = ["authorities", "humans", "parrots", "cars", "motorcycles", "buildings", "warehouses"]robots = ["Terminator #" + str(uuid.uuid4())[:5] for _ in range(7)]harvest = np.array([[randrange(i * j) for i in range(10, 80, 10)] for j in range(1, 8)])fig, ax = plt.subplots()im = ax.imshow(harvest)ax.set_xticks(np.arange(len(robots)), labels=robots)ax.set_yticks(np.arange(len(targets)), labels=targets)plt.setp(ax.get_xticklabels(), rotation=60, ha="right", rotation_mode="anchor")for i in range(len(targets)): for j in range(len(robots)): text = ax.text(j, i, harvest[i, j], ha="center", va="center", color="w")ax.set_title("Targets destroyed")fig.tight_layout()plt.rcParams['figure.figsize'] = [4, 4]plt.rcParams['figure.dpi'] = 200plt.show() +``` + + + +![png](https://habrastorage.org/r/w1560/getpro/habr/post_images/eba/76c/412/eba76c412c65b8139db64f7a7aebfc19.png) + + + +Аналогичная тепловая карта, визуализированная при помощи seaborn: + + + +``` +from random import randrangeimport numpy as npimport matplotlib.pyplot as pltimport uuidimport seaborn as snssns.set_theme()targets = ["authorities", "humans", "parrots", "cars", "motorcycles", "buildings", "warehouses"]robots = ["Terminator #" + str(uuid.uuid4())[:5] for _ in range(7)]harvest = np.array([[randrange(i * j) for i in range(10, 80, 10)] for j in range(1, 8)])fig, ax = plt.subplots()im = ax.imshow(harvest)ax.set_title("Targets destroyed")plt.rcParams['figure.figsize'] = [4, 4]sns.heatmap(harvest, annot=True, fmt="d", linewidths=.5, ax=ax, xticklabels=robots, yticklabels=targets)plt.setp(ax.get_xticklabels(), rotation=60, ha="right", rotation_mode="anchor")plt.xticks(rotation=60)plt.show() +``` + + + +![png](https://habrastorage.org/r/w1560/getpro/habr/post_images/fce/04a/0d5/fce04a0d5530e395ba123a3d045d9f2a.png) + + + +А вот так будет выглядеть «Доска почёта» при отрисовке в 3D: + + + +``` +import uuidfrom random import randrangeimport matplotlib.cm as cmimport matplotlib.colors as colorsimport matplotlib.pyplot as pltimport numpy as nptargets = ["authorities", "humans", "parrots", "cars", "motorcycles", "buildings", "warehouses"]robots = ["Terminator #" + str(uuid.uuid4())[:5] for _ in range(7)]harvest = np.array([[randrange(i * j) for i in range(10, 80, 10)] for j in range(1, 8)])fig = plt.figure(figsize=(5, 5))ax = fig.add_subplot(projection='3d')ax.set_xticks(np.arange(len(robots)), labels=robots)ax.set_yticks(np.arange(len(targets)), labels=targets)plt.setp(ax.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor")plt.setp(ax.get_yticklabels(), ha="left", rotation_mode="anchor")ax.set_title("Our team")xx, yy = np.meshgrid(range(len(targets)), range(len(robots)))x1d, y1d = xx.ravel(), yy.ravel()harvest1d = harvest.ravel()# Setup color schemeoffset = harvest1d + np.abs(harvest1d.min())fracs = offset.astype(float) / offset.max()norm = colors.Normalize(fracs.min(), fracs.max())colors = cm.jet(norm(fracs))ax.bar3d(x1d, y1d, np.zeros_like(x1d + y1d), 0.7, 0.7, harvest1d, color=colors)plt.show() +``` + + + +![png](https://habrastorage.org/r/w1560/getpro/habr/post_images/a70/52e/4eb/a7052e4eb1eba178f4ef91a78351ff7e.png) + + + +### Нейронные сети + + + +Углубиться достаточно глубоко в тематику глубокого обучения в рамках нашего несколько неглубокого формата, конечно, не получится (каким-нибудь томом «Введение в Deep Learning» вполне можно нанести себе существенные травмы, если неудачно уронить со стола), так что скользнём буквально по верхушкам, рассмотрев основные понятия. + + + +Глубинная нейронная сеть (deep neural network) — это, формально выражаясь, _многослойная_ искусственная нейронная сеть, использующая алгоритмы машинного обучения для моделирования высокоуровневых абстракций с применением нелинейных преобразований. В ходу есть еще такие термины, как «поверхностное машинное обучение», «слабый ИИ», «сильный ИИ»; существует классификация понятий «глубокое обучение», «машинное обучение» и «искусственный интеллект» (вот, например, [вариант](https://learn.microsoft.com/en-us/azure/machine-learning/concept-deep-learning-vs-machine-learning) от Microsoft, такую же структуру-матрёшку демонстрирует Франсуа Шолле в своей книге «Глубокое обучение на Python»), но сейчас, по крайней мере при неформальном общении, всё чаще ставится знак равенства между «машинным обучением» и «искусственным интеллектом»; причём в работе, как правило, используется «машинное обучение», а при подготовке презентаций — «искусственный интеллект» :) Дело, по всей видимости, в том, что глубокое обучение стало самой многообещающей и динамично развивающейся областью машинного обучения, а искусственным интеллектом уже давно именовали всю эту область знаний как популяризаторы науки, так и журналисты. + + + +В общем, при первоначальном знакомстве с TensorFlow или PyTorch можете смело всем говорить, что занимаетесь AI, а если захотите углубиться — уж терминологией-то овладеете. +Что касается сути работы нейронных сетей, то здесь можно выделить следующие основные понятия: + + + +**Датасет** — маркированные данные, используемые для обучения сети и для последующей проверки качества этого обучения. Вариант простого датасета — коллекция изображений одинакового размера с рукописными цифрами и буквами, где про каждое изображение точно известно, какай именно символ в нём содержится; такой датасет нужен для разработки систем распознавания рукописного текста (handwritten text recognition, HTR). Создание качественных датасетов — большая, тяжелая работа, поэтому сейчас идет активная работа по созданию моделей, способных работать с большими объёмами _немаркированных_ данных. Как правило, датасет разбивается на две части — для обучения сети и для проверки качества проведенного обучения. + + + +**Искусственная нейронная сеть** — программный или аппаратный аналог биологической нейронной сети; в самом простом варианте, в сети прямого распространения, это последовательно соединенные слои нейронов. Первые реализации нейронных сетей получили практическое воплощение еще в 60-х годах XX века, хотя существенный прогресс и практическое внедрение приходятся примерно на последние лет пятнадцать. + + + +**Архитектура** искусственной нейронной сети — определяет общие принципы её построения, вот [здесь](https://www.asimovinstitute.org/neural-network-zoo/) есть хорошее введение в тему, наглядно показано, чем, например, GAN отличается от LSTM. + + + +**Обучение** нейронной сети — если в двух словах, то это нахождение коэффициентов связи между нейронами. Применительно к машинному обучению заменяет процесс собственно программирования. Когда мы говорим о DALL-E 2, способном создавать изображения по текстовым описаниям или об AlphaGo, обыгрывающем профессиональных игроков в го — мы говорим в первую очередь об обученных нейронных сетях, создатели которых проявили бездну изобретательности, чтобы все коэффициенты связи были на своём месте. Дообучение — частичная корректировка коэффициентов связи между нейронами при модификации старого или добавлении нового функционала. + + + +Зачем нужно машинное обучение? Сильный ИИ, который будет (скрестим пальцы) решать все наши проблемы в режиме реального времени, еще за горизонтом, а прямо сейчас нейронные сети решают задачи, которые не по зубам классическим алгоритмам. Можно ли при помощи «обычного» программирования решить, например, задачу распознавания отсканированного текста или перевода с одного языка на другой? Да, можно, и такие небезуспешные попытки неоднократно предпринимались. Но, учитывая прорыв в развитии математической базы машинного обучения, наметившийся в последние 10-15 лет, помноженный на гигантский прирост производительности даже обычных повседневных вычислительных устройств, вроде смартфонов, фактически и распознавание, и перевод сейчас реализуют только при помощи машинного обучения. Для некоторых же классов задач, таких как распознавание изображений и видео с последующей классификацией объектов или беспилотная транспортировка, решения на базе классических алгоритмов никогда не заходили дальше вялотекущих концептов. + + + +Мало-помалу машинное обучение делает нашу жизнь лучше. Со временем, надеюсь, каждый из нас сможет воспользоваться плодами работы искусственного интеллекта, хоть мы и величаем его «слабым». И речь идет не только о громких проектах, вроде автопилота «Теслы» (хотя и это крайне немаловажно), но в первую очередь о постоянном сканировании эксабайтных потоков информации, порождаемых современной цифровой цивилизацией — видео с уличных камер, сканов МРТ и КТ, телеметрии с фитнес-браслетов, отчетов об исследовании лекарственных средств и пищевых добавок. И если всё это пройдет перед, может быть, пока не очень умными, но зато неустанными глазами предварительно должным образом обученной нейронной сети, то кратно уменьшится неверных диагнозов, людей, умерших от инсультов или замерзших на улицах, а также лекарств с тяжелыми побочными эффектами. + + + +Но помните, однако — даже если вы полны энтузиазма и готовы хоть прямо сейчас нырнуть во все эти модные, интересные и актуальные сущности вроде бигдаты или ИИ — чем дальше вы будете углубляться в эту тематику, тем меньше в вас будет программиста, тем больше исследователя, учёного, математика. Где-то в самом конце этого длинного-предлинного коридора сидит вовсе не Нео из «Матрицы», а эдакий себе [Григорий Перельман](https://en.wikipedia.org/wiki/Grigori_Perelman), уставший человек с ручкой и листом бумаги, иногда этой ручкой на этой бумаге пишущий вещи, которые потом приходится два года разжевывать лучшим математикам мира. Крепко подумайте, как глубоко вы хотите и можете зайти, взвесьте свои силы. + + + +## 3. Потоки данных + + + +![Data Flows](https://habrastorage.org/r/w1560/getpro/habr/post_images/4b1/afd/231/4b1afd231fcd5e11f79e1347cff43e7e.png) + + + +### Itertools + + + +Методы модуля itertools возвращают _итераторы_. + + + +Итератор — механизм поэлементного обхода данных, который использует метод next() для получения следующего значения последовательности. Подробнее создание итераторов будет рассмотрено ниже, в разделе «ООП / Утиная типизация». В «нормальные» данные итераторы перегоняются при помощи for, next или list(). + + + +Itertools содержит множество готовых итераторов, которые могут быть бесконечными (порождаются при помощи count, cycle или repeat), конечными (accumulate, chain, takewhile и другие) и комбинаторными (product, combinations, combinations_with_replacement, permutations). Лучше изучить их все, хотя бы поверхностно, потому что даже относительно редко употребляемый метод, например, какой-нибудь zip_longest(), иногда весьма и весьма пригождается, идеально ложась на поставленную задачу. + + + +Пример работы с бесконечными итераторами: + + + +``` +from itertools import count, repeat, cycle# Итератор, возвращающий равномерно распределенные значенияi1 = count(start=0, step=.1)print(next(i1))print(next(i1))print(next(i1))# Итератор, циклично и бесконечно возвращающий элементы итерируемого объектаi2 = cycle([1, 2])print(next(i2))print(next(i2))print(next(i2))# Итератор, возвращающий один и тот же объект бесконечно, если не указано значение аргумента timesi3 = repeat("Wow!", times=3)print(list(i3)) +``` + + + +``` +0 +0.1 +0.2 +1 +2 +1 +['Wow!', 'Wow!', 'Wow!'] +``` + + + +Применение некоторых конечных итераторов: + + + +``` +from itertools import accumulate, chain, compress, dropwhile, takewhile, pairwiseimport operator# Итератор, возвращающий накопленный результат выполнения указанной функции (по умолчанию — сложение)i1 = accumulate([1, 2, 3, 4])i2 = accumulate([1, 2, 3, 4], initial=10)print(list(i1), list(i2))i3 = accumulate([ -3, -2, -1, 1, 2, 3, 4], operator.mul)print(list(i3))# Можно использовать свою функциюdef myfunc(accumulated, current): return accumulated + 2 * currenti4 = accumulate([1, 2, 3, 4], func=myfunc)print(list(i4))# Можно использовать лямбду (подробнее рассмотрены ниже)i5 = accumulate([1, 2, 3, 4], lambda accumulated, current: accumulated + 2 * current)print(list(i5))# Итератор, возвращающий только те элементы входной последовательности,# которые имеют соответствующий элемент, равный True или 1 в последовательности selectorsi6 = compress("ABCDEF", [1, 1, 1, 0, 0, 1])print(list(i6))# Итератор, отбрасывающий элементы входной последовательности, если результат выполнения функции равен True.# Как только предикат становится False, то отбрасывание прекращается (предикат больше не применяется)i7 = dropwhile(lambda x: x<5, [1, 4, 6, 4, 1, 1, 1, 0])print(list(i7))# takewhile, в отличие от dropwhile, наоборот, возвращает элементы входной последовательности,# если результат выполнения функции равен Truei8 = takewhile(lambda x: x<5, [1, 4, 6, 0, 4, 1, 2, 1])print(list(i8))# Итератор, формирующий из нескольких входных последовательностей одну общуюi2 = chain(["A", "B", "C"],["D", "E", "F"],["G", "H", "I"])print(list(i2))# Кстати, такой же трюк можно провернуть при помощи обычной sum(), задав ей начальный параметр [] (т. е. пустой список)a = sum([["A", "B", "C"],["D", "E", "F"],["G", "H", "I"]], [])print(a)# Возвращает элементы входной коллекции попарноi6 = pairwise([1, 2, 3, 4, 5])print(list(i6)) +``` + + + +``` +[1, 3, 6, 10] [10, 11, 13, 16, 20] +[-3, 6, -6, -6, -12, -36, -144] +[1, 5, 11, 19] +[1, 5, 11, 19] +['A', 'B', 'C', 'F'] +[6, 4, 1, 1, 1, 0] +[1, 4] +['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'] +['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'] +[(1, 2), (2, 3), (3, 4), (4, 5)] +``` + + + +Комбинаторика + + + +``` +from itertools import product, combinations, combinations_with_replacement, permutations# Создает множество, содержащее все упорядоченные пары элементов из входных множествa = product("abc", "xyz")print(list(a))b = product([0, 1], repeat=3)print(list(b))# Возвращает подпоследовательности длины r из элементов входного итерируемого объекта, повторяющиеся элементы не допускаютсяc = combinations("abc", r=2)print(list(c))# Выдает перестановки элементов итерируемого объектаd = permutations("abc", r=2)print(list(d))# Возвращает подпоследовательности длины r из элементов входного итерируемого объекта, повторяющиеся элементы допустимыe = combinations_with_replacement("abc", r=2)print(list(e)) +``` + + + +``` +[('a', 'x'), ('a', 'y'), ('a', 'z'), ('b', 'x'), ('b', 'y'), ('b', 'z'), ('c', 'x'), ('c', 'y'), ('c', 'z')] +[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)] +[('a', 'b'), ('a', 'c'), ('b', 'c')] +[('a', 'b'), ('a', 'c'), ('b', 'a'), ('b', 'c'), ('c', 'a'), ('c', 'b')] +[('a', 'a'), ('a', 'b'), ('a', 'c'), ('b', 'b'), ('b', 'c'), ('c', 'c')] +``` + + + +### Enumerate + + + +Иногда, при переборе объектов в цикле for, нужно получить не только сам объект, но и его порядковый номер. Разумеется, это можно сделать, создав дополнительную переменную, которая будет инкрементироваться на каждом шаге цикла. Однако, гораздо удобнее это делать с помощью итератора enumerate, введенным в [PEP-279](https://peps.python.org/pep-0279/). Enumerate — синтаксический сахар («introduces… to simplify a commonly used looping idiom»), позволяющий проще и нагляднее работать с объектами, поддерживающими итерацию. Метод __next__() enumerate возвращает кортеж, содержащий значение индекса и соответствующее этому индексу значение. + + + +В документации работа enumerate упрощенно объясняется через генератор: + + + +``` +def enumerate(sequence, start=0): n = start for elem in sequence: yield n, elem n += 1 +``` + + + +На самом деле enumerate — не генератор, а итератор: + + + +``` +import collectionsimport typese = enumerate("abcdef")print(isinstance(e, enumerate))print(isinstance(e, collections.Iterable))print(isinstance(e, collections.Iterator))print(isinstance(e, types.GeneratorType)) +``` + + + +``` +True +True +True +False +``` + + + +Enumerate реализован не на Python, а на C, и в его [исходном коде](https://github.com/python/cpython/blob/master/Objects/enumobject.c#L289), разумеется, нет ключевого слова yield. + + + +Примеры использования enumerate: + + + +``` +values = ["a", "b", "c", "d"]for count, value in enumerate(values): print(count, value)print("\n")for count, value in enumerate(values, start=10 ): print(count, value) +``` + + + +``` +0 a +1 b +2 c +3 d + +10 a +11 b +12 c +13 d +``` + + + +### Генератор (generator) + + + +Любая функция, содержащая ключевое слово _yield_, вернет генератор. Генератор не хранит в памяти все необходимые элементы, а просто содержит метод для вычисления очередного элемента; результат может создаваться на основе математического алгоритма или брать элементы из другого источника данных (коллекция, файл, сетевое подключение и т. д.), при необходимости модифицируя их. + + + +Пройти генератор в цикле можно только один раз, на каждом шаге возможно вычислить только следующий элемент, но не предыдущий. Элемент генератора нельзя извлечь по индексу, будет выброшена ошибка, т. к. генератор не поддерживает метод __getitem__(). + + + +Бесконечный генератор: + + + +``` +def count(start, step): current = start while True: yield current current += stepc = count(100, 10)print(next(c))print(next(c))print(next(c)) +``` + + + +``` +100 +110 +120 +``` + + + +Конечный генератор. +Также, как и конечный итератор, конечный генератор можно превратить в список при помощи list() (вы можете попробовать превратить в list и бесконечный генератор, но процесс рискует несколько затянуться :): + + + +``` +def count(start, stop, step): current = start while current <= stop: yield current current += stepc = count(100, 200, 10)print(next(c))print(next(c))print(next(c))print(list(c)) +``` + + + +``` +100 +110 +120 +[130, 140, 150, 160, 170, 180, 190, 200] +``` + + + +Следует разделять итераторы и генераторы. Итератор — объект, который использует метод __next__() для получения следующего значения последовательности. Генератор — функция, которая позволяет отложено создавать результат при итерации. + + + +#### Объявление генератора + + + +Объявить генератор можно несколькими методами. Первый метод — объявить функцию с yield, как было показано выше. +Второй метод — использовать _генераторное выражение_ (generator expression): + + + +``` +r = range(1, 11)squares = (n**2 for n in r)print(list(squares)) +``` + + + +``` +[1, 4, 9, 16, 25, 36, 49, 64, 81, 100] +``` + + + +Можно объединить генераторы или делегировать часть функционала генератора другому генератору при помощи конструкции _yield from_: + + + +``` +def subg(): yield 'World'def generator(): yield 'Hello' yield from subg() yield '!'for i in generator(): print(i, end = ' ') +``` + + + +``` +Hello World ! +``` + + + +До широкого распространения asyncio конструкция yield from использовалась для создания [корутин на базе генераторов](https://docs.python.org/3.7/library/asyncio-task.html#asyncio.coroutine). + + + +### Декораторы + + + +Что такое декораторы? + + + +Декоратор в широком смысле – паттерн проектирования, когда один объект изменяет поведение другого. Декораторы — это, по сути, своеобразные «обёртки», которые дают нам возможность делать что-либо до или после того, что сделает декорируемая функция, не изменяя её. Можно сказать, что декоратор является просто синтаксическим сахаром для конструкции вида: + + + +``` +my_function = my_decorator(my_function) +``` + + + +``` +def makebold(fn): def wrapped(): return "" + fn() + "" return wrappeddef makeitalic(fn): def wrapped(): return "" + fn() + "" return wrapped# Разумеется, при последовательном применении нескольких декораторов играет роль порядок декорирования.@makebold@makeitalicdef hello(): return "Hello, world!"print(hello()) +``` + + + +``` +Hello, world! +``` + + + +Декоратор, подсчитывающий время работы оборачиваемой функции: + + + +``` +import timedef perf_counter(function): def counted(*args): start_time = time.perf_counter_ns() res = function(*args) print(f"{time.perf_counter_ns() - start_time} ns") return res return counted@perf_counterdef slow_sum(x, y): time.sleep(1) return x + yprint(slow_sum(1, 2)) +``` + + + +``` +1002478400 ns +3 +``` + + + +### LRU Cache + + + +Декоратор, кеширующий значения, возвращаемые функцией. Все аргументы функции должны быть хэшируемы. + + + +``` +import functoolsdef recursion_sum(n): if n == 1: return n print(n, end=" ") return n + recursion_sum(n - 1)recursion_sum(5)print("\n")recursion_sum(9)print("\n")@functools.lru_cachedef recursion_sum2(n): if n == 1: return n print(n, end=" ") return n + recursion_sum2(n - 1)recursion_sum2(5)print("\n")recursion_sum2(9) +``` + + + +``` +5 4 3 2 + +9 8 7 6 5 4 3 2 + +5 4 3 2 + +9 8 7 6 + +45 +``` + + + +Размер кеша по умолчанию 128 значений. Ограничение можно отменить при помощи 'maxsize=None'. + + + +Пока мы не ушли далеко от тема кеша, погуглите заодно модуль weakref и WeakValueDictionary, позволяющие организовать более гибкую работу с кешем. + + + +### Параметризованный декоратор + + + +В декоратор можно передать и позиционные, и именованные аргументы — args и kwargs соответственно. Синтаксис декораторов с аргументами немного отличается — декоратор с аргументами должен возвращать функцию, которая принимает функцию и возвращает другую функцию: + + + +``` +def text_wrapper(wrap_text): def wrapped(function): def wrapper(*args, **kwargs): result = function(*args, **kwargs) return f"{wrap_text}\n{result}\n{wrap_text}" return wrapper return wrapped@text_wrapper('============')def my_decorated_function(text): return textprint(my_decorated_function('Hello, world!')) +``` + + + +``` +============ +Hello, world! +============ +``` + + + +### Контекстный менеджер + + + +Код, размещенный внутри оператора with выполняется с особенностью: как до, так и после срабатывают события входа в блок with и выхода из него. Объект, который определяет логику событий, называется контекстным менеджером. + + + +На уровне класса события определены методами __enter__ и __exit__. +__enter__ срабатывает в тот момент, когда ход исполнения программы переходит внутрь with. Метод может вернуть значение. Оно будет доступно расположенному внутри блока with коду. +__exit__ срабатывает в момент выхода блока, в т.ч. и в случае исключения. В этом случае в метод будет передана тройка значений (exc_class, exc_instance, traceback). + + + +Самый распространённый контекстный менеджер – класс, порожденный функцией open. Он гарантирует, что файл будет закрыт даже в том случае, если внутри блока возникнет ошибка. + + + +Желательно побыстрее выходить из контекстного менеджера, освобождая контекст и ресурсы. + + + +``` +with open('file.txt') as f: data = f.read()process_data(data) +``` + + + +В примере выше мы вышли из блока with сразу же после прочтения файла. Обработка данных происходит в основном блоке программы. + + + +Контекстные менеджеры можно использовать для временной замены параметров, переменных окружения, транзакций БД. + + + +Напишем свой контекстный менеджер для подключения к БД SQLite: + + + +``` +import sqlite3class db_conn: def __init__(self, db_name): self.db_name = db_name # Открываем подключение к БД def __enter__(self): self.conn = sqlite3.connect(self.db_name) return self.conn # Закрываем подключение к БД def __exit__(self, exc_type, exc_value, exc_traceback): self.conn.close() if exc_value: raiseif __name__ == "__main__": db = "test_context_connect.db" with db_conn(db) as conn: cursor = conn.cursor() +``` + + + +### Контекстный менеджер на базе contextlib + + + +Перепишем наш контекстный менеджер для подключения к БД SQLite при помощи contextlib: + + + +``` +import sqlite3from contextlib import contextmanager# Схема конструирования следующая: всё, что написано до оператора yield - вызывается в рамках функции __enter__,# всё что после – в рамках __exit__.@contextmanagerdef db_conn(db_name): # Открываем подключение к БД conn = sqlite3.connect(db_name) yield conn # Закрываем подключение к БД conn.close()if __name__ == "__main__": db = "test_contextlib_connect.db" with db_conn(db) as conn: cursor = conn.cursor() +``` + + + +## 4. ООП + + + +![OOP](https://habrastorage.org/r/w1560/getpro/habr/post_images/7ec/81b/9a6/7ec81b9a69c879e5ff97b2f1a10ad093.png) + + + +### Классы и объекты + + + +Тут, конечно, было бы к месту кратенькое, минут на сорок, введеньице в тему классов и объектов, но в наш текущий формат такая мощная врезка не совсем укладывается. Объясню максимально просто, на доступных примерах из киновселенной «Чужих»: +объект — это один конкретный ксеноморф; +класс — это Королева ксеноморфов. Класс либо рожает ксеноморфа, либо может вступить в бой сам (@staticmethod); +метапрограммирование — это такая Супер-Королева, размером с «Сулако», которая рожает других Королев; +наследование — это ксеноморф из «Воскрешения», помните, миленький такой, взявший лучшее и от собственной генетической программы и от генов Рипли. + + + +Мы попробуем вернуться к теме объектов с чуть более серьезным настроением позже, в главе «Архитектура», но, вообще, в объектно-ориентированном программировании нет ничего особо сложного; просто до него лучше дойти, предварительно немного погрязнув в поддержке обычного процедурного подхода, когда зачастую стоит выбор — попробовать подлечить этот кусок кода или уже усыпить и переписать всё по новой? Когда-то давно, когда я писал относительно несложные программы на ассемблере для микроконтроллеров, то читая Страуструпа, слегка недоумевал — зачем всё это? Чтобы осознать потребность в обуви, надо походить босиком. + + + +### Магические методы + + + +Специальные (называемые также magic или dunder) методы класса — перегрузка, позволяющая классам определять собственное поведение по отношению к операторам языка. +Магические они потому, что почти никогда не вызываются явно. Их вызывают встроенные функции или синтаксические конструкции. Например, функция len() вызывает метод __len__() переданного объекта. Метод __add__(self, other) вызывается автоматически при сложении оператором +. + + + +Примеры магических методы: + + + +__init__: конструктор класса +__add__: сложение с другим объектом +__eq__: проверка на равенство с другим объектом +__cmp__: сравнение (больше, меньше, равно) +__iter__: при подстановке объекта в цикл + + + +``` +print(dir(int), "\n")class A: # An empty class ...a = A()print(dir(a), "\n")print(repr(a), "\n")print(str(a)) +``` + + + +``` +['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_count', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes'] + +['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__'] + +<__main__.A object at 0x000001A1FFF3AF20> + +<__main__.A object at 0x000001A1FFF3AF20> +``` + + + +Особенностью метода __init__ является то, что он не должен ничего возвращать. При попытке возврата данных будет сгенерировано исключение. +__repr__ (representation) возвращает более-менее машино-читаемое представление объекта, полезное для отладки. +_Иногда_ repr может содержать достаточно информации для восстановления объекта. +__str__ возвращает человеко-читаемое сообщение. Если __str__ не определён, то str использует repr. + + + +``` +class Person: # A simple class with init, repr and str methods def __init__(self, name: str): self.name: str = name def __repr__(self): return f"Person '{self.name}'" def __str__(self): return f"{self.name}" def say_hi(self): print("Hi, my name is", self.name)p = Person("Charlie")p.say_hi()print(repr(p))print(str(p)) +``` + + + +``` +Hi, my name is Charlie +Person 'Charlie' +Charlie +``` + + + +### @property + + + +Декоратор [@property](https://docs.python.org/3/library/functions.html?highlight=property#property) используется для определения методов, доступных как поля. Таким образом операции чтения/записи поля можно обрамить дополнительной логикой, например, проверкой допустимых значений входного аргумента. + + + +``` +import mathclass Circle: def __init__(self, radius, max_radius): self._radius = radius self.max_radius = max_radius @property def radius(self): return self._radius @radius.setter def radius(self, value): if value <= self.max_radius: self._radius = value else: raise ValueError @property def area(self): return 2 * self.radius * math.picircle = Circle(10, 100)circle.radius = 20 # OK# circle.radius = 101 # Raises ValueErrorprint(circle.area) +``` + + + +``` +125.66370614359172 +``` + + + +### @staticmethod + + + +Обычный метод (т. е. не помеченный декораторами @staticmethod или @classmethod) имеет доступ к свойствам конкретного экземпляра класса. + + + +@staticmethod — метод, принадлежащий классу, а не экземпляру класса. Можно вызывать без создания экземпляра, т. к. метод не имеет доступа к свойствам экземпляра. При помощи @staticmethod помечают функционал, логически связанный с классом, но не требующий доступа к свойствам экземпляра. + + + +### @classmethod, cls, self + + + +Если метод не должен иметь доступа к свойствам конкретного экземпляра класса (как @staticmethod), но должен иметь доступ к другим методам и переменным класса, то следует использовать @classmethod. + + + +``` +class B(object): def foo(self, x): print(f"Run foo({self}, {x})") @classmethod def class_foo(cls, x): print(f"Run class_foo({cls}, {x})") @staticmethod def static_foo(x): print(f"Run static_foo({x})")b = B()b.foo(1)b.class_foo(1)b.static_foo(1) +``` + + + +``` +Run foo(<__main__.B object at 0x000001A1FFF3A980>, 1) +Run class_foo(, 1) +Run static_foo(1) +``` + + + +У @classmethod первым параметром должен быть cls (класс), а у обычного метода — self (экземпляр класса). +Для @staticmethod не требуется ни cls, ни self. + + + +### __dict__ + + + +Каждый класс и каждый объект имеет атрибут __dict__. Это «системный», определённый интерпретатором атрибут, его не нужно создавать вручную. __dict__ — словарь, который хранит пользовательские атрибуты, и в котором ключом является _имя атрибута_, значением, соответственно, _значение атрибута_. + + + +``` +class Supercriminal: publisher = 'DC Comics'Riddler = Supercriminal()print(Supercriminal.__dict__)print(Riddler.__dict__)Riddler.name = 'Edward Nygma'print(Riddler.__dict__) # Values from object __dict__print(Riddler.publisher) # Value from class __dict__ +``` + + + +``` +{'__module__': '__main__', 'publisher': 'DC Comics', '__dict__': , '__weakref__': , '__doc__': None} +{} +{'name': 'Edward Nygma'} +DC Comics +``` + + + +Каждый раз при запросе пользовательского атрибута Python последовательно обыскивает сам объект, класс объекта и классы, от которых унасаледован класс объекта. + + + +### __slots__ + + + +Если вы припомните разницу между списком и кортежем, а также между множеством и иимутабельным множеством, то заметите, что создатели Python пытаются предоставлять разработчикам выбор между удобством и скоростью (тут Си с ассемблерными вставками слегка напрягается, но потом опадает, как будто хотел что-то сказать, но благоразумно передумал). К списку таких же особенностей языка, заточенных на увеличение производительности и уменьшение занимаемой памяти, относится и __slots__. + + + +Вот [официальная документация](https://docs.python.org/3/reference/datamodel.html?highlight=slots#object.__slots__) по __slots__, а вот [дополнительные разъяснения](https://stackoverflow.com/questions/472000/usage-of-slots/28059785#28059785) от одного из разработчиков официальной документации. При выборе «slots или не slots» не забывайте также про существование [PEP 412 – Key-Sharing Dictionary](https://peps.python.org/pep-0412/), который внёс некоторый раздрай в некогда однозначное отношение к __slots__. + + + +__dict__, рассмотренный чуть выше – изменяемая структура, и вы можете на лету добавлять и удалять поля из класса, что удобно, но порой медленно. Вы можете разменять удобство на скорость и размер занимаемой памяти, создав __slots__ — жестко заданный список предопределенных атрибутов, резервирующий память, создание которого _запрещает дальнейшее создание __dict__ и __weakref___. Слоты можно использовать, когда у класса может быть очень много полей, например, в ORM, либо когда критична производительность, потому что доступ к списку работает быстрее, чем поиск в словаре. + + + +``` +class Clan: __slots__ = ["first", "second"]clan = Clan()clan.first = "Joker"clan.second = "Lex Luthor"# clan.third = "Green Goblin" # Raises AttributeError# print(clan.__dict__) # Raises AttributeError +``` + + + +Слоты используются, скажем, в библиотеках requests (например, __slots__ = ["url", "netloc", "simple_url", "pypi_url", "file_storage_domain"]) или ORM peewee (__slots_\_ = ('stack', '_sql', '_values', 'alias_manager', 'state')). + + + +Наследование __slots__ имеет определенную специфику и будет рассмотрено ниже. + + + +Чтобы было понятно, о каком приросте производительности и снижении потребления памяти идёт речь, сделаем простое сравнение: + + + +``` +import timeitimport pympler.asizeof # В нашем случае sys.getsizeof - не лучший вариант, берем стороннее решениеclass NotSlotted(): passclass Slotted(): __slots__ = 'foo'not_slotted = NotSlotted()slotted = Slotted()def get_set_delete_fn(obj): def get_set_delete(): obj.foo = "Never Ending Song of Love" del obj.foo return get_set_deletens = min(timeit.repeat(get_set_delete_fn(not_slotted)))s = min((timeit.repeat(get_set_delete_fn(slotted))))print(ns, s, f'{(ns - s) / s * 100} %')print(pympler.asizeof.asizeof(not_slotted), 'bytes')print(pympler.asizeof.asizeof(slotted), 'bytes') +``` + + + +``` +0.10838449979200959 0.08712740009650588 24.39772066187959 % +280 bytes +40 bytes +``` + + + +Я в Python 3.10 в Windows вижу 24 % разницы. + + + +На всякий случай напоминаю еще раз — прогняйте все непонятные примеры кода в IDE, их можно и нужно анализировать, корректировать и видоизменять. Попробуйте, например, самостоятельно посмотреть потребление памяти объектов с __dict__ и __slots__. А заодно на практике испытайте давно напрашивающийся, и наконец появившийся в Python 3.10 [симбиоз](https://docs.python.org/3/library/dataclasses.html#module-contents) между __slots__ и dataclass. + + + +### Утиная типизация + + + +[Утиная типизация](https://en.wikipedia.org/wiki/Duck_typing) (duck types) — постулирование реализации интерфейса классом не через явное объявление, а через реализацию методов интерфейса. Так, каждый класс, реализующий методы __next__() и __iter__(), автоматически становится итератором, несмотря на отсутствие явного объявления (что-нибудь вроде [iterator](https://habr.com/ru/users/iterator/)) или, скажем, наследования от класса Iterator. + + + +### Iterator + + + +Итератор — класс, реализующий методы __next__() и __iter__(). +Метод __next__() должен возвращать следующее значение итератора или выкидывать исключение StopIteration, чтобы сигнализировать о том, что итератор исчерпал доступные значения. +Метод __iter__() должен возвращать "self". + + + +``` +class LimitCounter: def __init__(self, max_value: int): self.count = 0 self.max_value = max_value def __next__(self): self.count += 1 if self.count <= self.max_value: return self.count else: raise StopIteration def __iter__(self): return selflimit_counter = LimitCounter(2)print(next(limit_counter))print(next(limit_counter))# print(next(limit_counter)) # Raises StopIteration +``` + + + +``` +1 +2 +``` + + + +### Comparable + + + +Начиная с Python 3.4, для того, чтобы экземпляры метода можно было сравнивать между собой, достаточно определить методы __lt__ (меньше) и __eq__ (равно), а также задействовать декоратор @functools.total_ordering. + + + +``` +from functools import total_ordering@total_orderingclass Person: def __init__(self, firstname: str, lastname: str): self.firstname: str = firstname self.lastname: str = lastname def _is_valid_operand(self, other): return hasattr(other, "lastname") and hasattr(other, "firstname") def __eq__(self, other): if not self._is_valid_operand(other): return NotImplemented return (self.lastname, self.firstname) == (other.lastname, other.firstname) def __lt__(self, other): if not self._is_valid_operand(other): return NotImplemented return (self.lastname, self.firstname) < (other.lastname, other.firstname)Finn = Person("Finn", "the Human")Jake = Person("Jake", "the Dog")print(Finn != Jake) +``` + + + +``` +True +``` + + + +### Hashable + + + +Хешируемые объекты должны реализовывать методы __hash__() и __eq__(). Хеш объекта должен быть неизменен в течении всего жизненного цикла. Хешируемые объекты можно использовать как ключи в словарях и как элементы множеств, так как эти структуры используют хеш-таблицу для внутреннего представления данных. + + + +Hashable objects that compare equal must have the same hash value, meaning default hash() that returns `'id(self)'` will not do. That is why Python automatically makes classes unhashable if you only implement eq(). + + + +``` +class Hero: def __init__(self, name: str, level: int): self.name: str = name self.level: int = level def _is_valid_operand(self, other): return hasattr(other, "name") and hasattr(other, "level") def __eq__(self, other): if not self._is_valid_operand(other): return NotImplemented return (self.name, self.level) == (other.name, other.level) def __hash__(self): return hash((self.name, self.level))Finn = Hero("Finn the Human", 10_000)Jake = Hero("Jake the Dog", 10_000)print(hash(Finn))print(hash(Jake)) +``` + + + +``` +-8707075988359731747 +-2276052447712954388 +``` + + + +### Sortable + + + +Для возможности применения к последовательностям объектов таких методов как sort() или max() необходимо, как и в случае Comparable, определить методы __lt__ (меньше) и __eq__ (равно), а также задействовать декоратор @functools.total_ordering. + + + +Для более предсказумого поведения объекта в условиях различного контекста вы можете определить полное множество функций сравнения (__lt()__, __gt()__, __le__() и __ge__()). + + + +Для примера создадим класс студентов, которых можно будет сортировать не по имени, а по среднему баллу. + + + +``` +from functools import total_orderingfrom statistics import mean@total_orderingclass Student: def __init__(self, name: str, grades: list[int]): self.name: str = name self.grades: list[int] = grades def _is_valid_operand(self, other): return hasattr(other, "name") and hasattr(other, "grades") def __eq__(self, other): if not self._is_valid_operand(other): return NotImplemented return mean(self.grades) == mean(other.grades) def __lt__(self, other): if not self._is_valid_operand(other): return NotImplemented return mean(self.grades) < mean(other.grades) # определим str для человеко-читаемой репрезентации объекта def __str__(self): return self.name + " " + str(mean(self.grades))Melissa = Student("Melissa Andrew", [4, 3, 4, 5, 4])Peter = Student("Peter Shining Jr.", [3, 3, 4, 5, 3])Joe = Student("Just Joe", [5, 5, 4, 5, 5])print([str(stud) for stud in sorted([Peter, Melissa, Joe], reverse=True)]) +``` + + + +``` +['Just Joe 4.8', 'Melissa Andrew 4', 'Peter Shining Jr. 3.6'] +``` + + + +### Callable + + + +Для возможности вызова объекта в качестве функции необходимо реализовать метод __call__. Типы, поддерживающие возможность их вызова в качестве функции, могут принимать набор аргументов. + + + +``` +class Counter: def __init__(self): self.i = 0 def __call__(self): self.i += 1 return self.icounter = Counter()print(counter())print(counter())print(counter()) +``` + + + +``` +1 +2 +3 +``` + + + +@classmethod нельзя вызывать в качестве функции: + + + +``` +class Check(): @classmethod def class_method(cls): pass @staticmethod def static_method(): pass def instance_method(self): pass for attr, val in vars(Check).items(): if not attr.startswith("__"): print (attr, f"{'is' if callable(val) else 'is NOT'} callable") +``` + + + +``` +class_method is NOT callable +static_method is callable +instance_method is callable +``` + + + +Контекстные менеджеры, описанные в предыдущей главе, тоже, как мы теперь видим, определяются через утиную типизацию при помощи методов __enter__ и __exit__. + + + +### Утиная типизация итерируемых объектов + + + +### Iterable + + + +[Iterable](https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable) — объект, который для предоставления возможности поочерёдного прохода по всем своим элементам должен реализовывать метод __iter__(), возвращающий итератор. У каждого объекта с методом __iter__() автоматически начинает работать метод __contains__(). + + + +``` +class MyIterable: def __init__(self, *args): self.a = list(args) def __iter__(self): return iter(self.a)mi = MyIterable(1, 2, 3, 4)print([el for el in mi])print(1 in mi) # __contains__() +``` + + + +``` +[1, 2, 3, 4] +True +``` + + + +### Collection + + + +[Collection](https://docs.python.org/3/library/collections.abc.html#collections.abc.Collection) — объект, предоставляющий возможность поочерёдного прохода по всем своим элементам и обладающий конечным размером. +В дополнение к iter() должен быть реализован метод len(), возвращающий размер коллекции. + + + +``` +class MyCollection: def __init__(self, *args): self.a = list(args) def __iter__(self): return iter(self.a) def __len__(self): return len(self.a)mc = MyCollection(1, 2, 3, 4)print([el for el in mc])print(1 in mc)print(len(mc)) +``` + + + +``` +[1, 2, 3, 4] +True +4 +``` + + + +### Sequence + + + +Требует методы len() and getitem(). getitem() должен отдавать элемент с требуемым индексом или вызывать исключение IndexError. +Автоматически будут порождены методы iter(), reversed() и contains(). + + + +``` +class MySequence: def __init__(self, a): self.a = a def __len__(self): return len(self.a) def __getitem__(self, i): return self.a[i] +``` + + + +### ABC Sequence + + + +Коллекция Sequence из [Abstract Base Classes for Containers](https://docs.python.org/3/library/collections.abc.html) предоставляет расширенный интерфейс по сравнению с обычной Sequence. +Всё так же требуя __getitem__ и __len__, предоставляет __contains__, __iter__, __reversed_\_, index и count. + + + +``` +from collections import abcclass MyAbcSequence(abc.Sequence): def __init__(self, a): self.a = a def __len__(self): return len(self.a) def __getitem__(self, i): return self.a[i] +``` + + + +### Таблица требуемых и доступных методов: + + + +``` ++------------+------------+------------+------------+--------------+ +| | Iterable | Collection | Sequence | ABC Sequence | ++------------+------------+------------+------------+--------------+ +| iter() | нужен | нужен | + | + | +| contains() | + | + | + | + | +| len() | | нужен | нужен | нужен | +| getitem() | | | нужен | нужен | +| reversed() | | | + | + | +| index() | | | | + | +| count() | | | | + | ++------------+------------+------------+------------+--------------+ +``` + + + +И вообще, потщательнее присмотритесь с collections.abc, там есть множество заготовок, которые помогут вам сэкономить немало времени. Например, если к упомянутым относительно ABC Sequence __getitem__ и __len__ добавить __setitem__, __delitem__ и insert, то в ответ вы получите коллекцию MutableSequence, которая, кроме возможностей Sequence, имеет еще методы append, reverse, extend, pop, remove и __iadd__. + + + +### Копирование объектов + + + +В Python оператор присваивания (=) не копирует объекты. Вместо этого он создает связь между существующим объектом и именем целевой переменной. Чтобы создать копии объекта в Python, необходимо использовать модуль copy. Более того, существует два способа создания копий для данного объекта с помощью модуля copy. + + + +Shallow Copy – это побитовая копия объекта. Созданный скопированный объект имеет точную копию значений в исходном объекте. Если одно из значений является ссылкой на другие объекты, копируются только адреса ссылок на них. + + + +Deep Copy – рекурсивно копирует все значения от исходного объекта к целевому, т. е. дублирует даже объекты, на которые ссылается исходный объект. + + + +``` +from copy import copy, deepcopyclass A: def __init__(self, val: list): self.val = val def change_val(self, val: list): self.val = vala = A(list("one"))# Просто копирование ссылки на объектb = a # Assignment# Создание нового объекта и копирование ссылок на объекты, найденные в изначальном объектеc = copy(a) # Shallow copy# Создание нового объекта с последующим рекурсивным копированием содержащихся внутри объектовd = deepcopy(a) # Deep Copyb.change_val(list("two"))c.change_val(list("three"))d.change_val(list("four"))print(a.val, b.val, c.val, d.val)print(id(a), id(b), id(c), id(d))print(id(a.val[1]), id(c.val[1])) +``` + + + +``` +['t', 'w', 'o'] ['t', 'w', 'o'] ['t', 'h', 'r', 'e', 'e'] ['f', 'o', 'u', 'r'] +1795295519472 1795295519472 1793149281968 1793149273808 +1795217224688 1795217321264 +``` + + + +### Наследование + + + +``` +class Person: def __init__(self, name, age): self.name = name self.age = ageclass Employee(Person): def __init__(self, name, age, staff_num, email): super().__init__(name, age) self.staff_num = staff_num self.email = email +``` + + + +### Множественное наследование + + + +При множественном наследовании порядок разрешения методов (method resolution order, MRO) позволяет Питону выяснить, из какого класса-предка нужно вызывать метод, если он не обнаружен непосредственно в классе-потомке. + + + +``` +class PrivateStaffData: def __init__(self, private_email): self.private_email = private_emailclass PublicStaffData: def __init__(self, work_email): self.work_email = work_emailclass StaffData(PrivateStaffData, PublicStaffData): def __init__(self, private_email, work_email): super().__init__()print(StaffData.mro()) +``` + + + +``` +[, , , ] +``` + + + +MRO строит иерархию наследования таким образом, чтобы более специфичные методы класса-потомка перекрывали менее специфичные методы класса-предка. MRO строит упорядоченный список классов, в которых будет производиться поиск метода слева направо (линеаризация класса). + + + +Для решения проблемы ромбовидной структуры (которая неявно присутствует даже в простейшем случае, так как все классы наследуются от object) линеаризация должна быть монотонной. Монотонность — свойство, которое требует соблюдения в линеаризации класса-потомка того же порядка следования классов-прародителей, что и в линеаризации класса-родителя. Линеаризация по сути является [топологической сортировкой](https://en.wikipedia.org/wiki/Topological_sorting). В ранних версиях Python использовался алгоритм DLR, сейчас в ходу [C3-линеаризация](https://en.wikipedia.org/wiki/C3_linearization). + + + +Если после удовлетворения свойства монотонности остаётся больше одного варианта линеаризации, то применяется порядок локального старшинства (local precedence ordering), т. е. порядок соблюдения для классов-родителей в линеаризации класса-потомка того же порядка, что и при его объявлении. Например, если класс объявлен как D(A, B, C), то в линеаризации D класс A должен стоять раньше B, а класс B — раньше C. + + + +Если разрешение всех конфликтов при линеаризации невозможно, то остается три пути: +1 — переменой мест классов-предков в объявлении класса-потомка (но это помогает далеко не всегда); +2 — пересмотр иерархии наследования; +3 — определение своей собственной линеаризации через метаклассы при помощи метода mro(cls). Но при данном подходе надо быть готовым к тому, что будет использован менее специфичный метод класса-родителя вместо более специфичного метода класса-потомка. + + + +При задании своей собственной линеаризации Python отключает встроенные проверки. + + + +### Наследование классов со __slots__ + + + +При одиночном наследовании __slots__ нормально наследуется, но это не предотвращает создание __dict__: + + + +``` +class SlotsClass: __slots__ = 'foo', 'bar'class ChildSlotsClass(SlotsClass): ...obj = ChildSlotsClass()print(obj.__slots__)obj.something_new = "underwater stones"print(obj.__dict__) +``` + + + +``` +('foo', 'bar') +{'something_new': 'underwater stones'} +``` + + + +Для ограничения дочернего класса слотами нужно в нём снова присвоить значение атрибуту __slots__, родительские поля дублировать не нужно. + + + +``` +class SlotsClass: __slots__ = 'foo', 'bar'class ChildSlotsClass(SlotsClass): __slots__ = 'baz'obj = ChildSlotsClass()# obj.something_new = "underwater stones" # Raises AttributeError: 'ChildSlotsClass' object has no attribute 'something_new' +``` + + + +Множественное же наследование классов с _непустыми_ __slots__ невозможно. + + + +### Метапрограммирование + + + +Что такое класс? Это, в принципе, просто кусок кода, описывающий, как создать объект. Но в Python класс — это нечто большее, классы также являются объектами; как только используется ключевое слово class, Python исполняет команду и создаёт объект: + + + +``` +class A: ... +``` + + + +В памяти будет создан объект с именем A. + + + +Классы, как и другие объекты, можно создавать на ходу: + + + +``` +def custom_class(name): if name == "foo": class Foo: ... return Foo # Возвращает именно класс, а не экземпляр else: class Bar: ... return BarMyClass = custom_class("foo")print(MyClass) # Функция возвращает класс, а не экземплярprint(my_class := MyClass()) # Можно создать экземпляр класса +``` + + + +``` +.Foo'> +<__main__.custom_class..Foo object at 0x000001F0ECF97610> +``` + + + +Но это не очень удобно, так как нам до сих пор приходится писать весь код класса. + + + +Основная цель метаклассов — автоматически изменять класс в момент создания, генерируя классы в соответствии с текущим контекстом. +Сами по себе метаклассы достаточно просты и работают примерно следующим образом: +перехватывают создание класса, +изменяют класс, +возвращают модифицированный класс. + + + +Но обычно логику работы метаклассов насыщают вещами вроде [интроспекции](https://en.wikipedia.org/wiki/Type_introspection#Python) или манипуляцией наследованием, поэтому конечный код выглядит достаточно громоздко. + + + +Здесь неплохо было бы добавить еще пару страниц про ньюансы создания и работы метаклассов, но позвольте переадресовать вас на вот эту [прекрасную статью](https://habr.com/ru/post/145835/). + + + +При помощи метаклассов хорошо решаются задачи, например, генерации классов для ORM. Скажем, для + + + +``` +class Person(models.Model): name = models.CharField(max_length=30) age = models.IntegerField() +``` + + + +код + + + +``` +keanu = Person(name="Keanu Reeves", age=58) print(keanu.age) +``` + + + +распечатает число, взятое из БД, потому что models.Model определяет __metaclass__, который сотворит некоторую магию и превратит класс Person, который мы только что определили достаточно простым выражением, в сложную привязку к базе данных. + + + +Если вы всё еще ломаете голову, где бы вам пришить метапрограммирование в своём текущем проекте, чтобы потом упомянуть об этом в резюме, то вот вам на 147 % уместная цитата из [Тима Питерса](https://en.wikipedia.org/wiki/Tim_Peters_(software_engineer)): «[Metaclasses] are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don’t (the people who actually need them know with certainty that they need them, and don’t need an explanation about why)», что в вольном переводе означает «Метаклассы нужны только уверенным в себе людям, которые точно знают, чего хотят от жизни, а вовсе не тебе». + + + +### @abstractmethod + + + +Абстрактный класс в Python — аналог интерфейса в других языках (например, в C#) — класс, содержащий только сигнатуры методов, без реализации. Реализация методов переложена на классы-потомки. Задача абстрактного класса соответствует задаче интерфейса — _обязать_ классы-потомки реализовывать _все_ методы, заложенные в классе-родителе. + + + +``` +import abcclass AbstractClass(metaclass=abc.ABCMeta): @abc.abstractmethod def return_anything(self): returnclass ConcreteClass(AbstractClass): def return_anything(self): return 42c = ConcreteClass()print(c.return_anything()) +``` + + + +``` +42 +``` + + + +Если не специфицировать return_anything() в ConcreteClass, при попытке вызвать c.return_anything() будет выброшено исключение TypeError: Can't instantiate abstract class ConcreteClass with abstract method return_anything. + + + +## Источники + + + +Официальная документация Python [docs.python.org](https://docs.python.org/), включающая [The Python Standard Library](https://docs.python.org/3/library/index.html). +Весьма подробное руководство (совсем уж базовый синтаксис не включен): [Comprehensive Python Cheatsheet](https://github.com/gto76/python-cheatsheet). +Руководство с включением базового синтаксиса: [Python Cheatsheet](https://github.com/wilfredinni/python-cheatsheet). Включает практические Jupiter [Notebooks](https://github.com/wilfredinni/python-cheatsheet/tree/master/jupyter_notebooks). +Сипсок библиотек и фреймворков: [Awesome Python](https://github.com/vinta/awesome-python). +Около-питоновские практические советы (pip, virtualenv, pyInstaller и т. д.): ["The Hitchhiker’s Guide to Python"](https://github.com/realpython/python-guide). +Мануал для начинающих дата-сайентистов: [Joel Grus, "Data Science from Scratch"](https://github.com/joelgrus/data-science-from-scratch). +Руководство для начинающих: ["Python Notes for Professionals"](https://goalkicker.com/PythonBook/). +Руководство для опытных программистов: ["Python 3 Patterns, Recipes and Idioms"](https://python-3-patterns-idioms-test.readthedocs.io/en/latest/index.html). +Архитектурные паттерны: [Harry Percival & Bob Gregory, "Architecture Patterns with Python"](https://www.cosmicpython.com/book/preface.html). + +Теги: + +- [учебник](https://habr.com/ru/search/?target_type=posts&order=relevance&q=[%D1%83%D1%87%D0%B5%D0%B1%D0%BD%D0%B8%D0%BA]) +- [python](https://habr.com/ru/search/?target_type=posts&order=relevance&q=[python]) +- [roadmap](https://habr.com/ru/search/?target_type=posts&order=relevance&q=[roadmap]) +- [career](https://habr.com/ru/search/?target_type=posts&order=relevance&q=[career]) + +Хабы: + +- [Python](https://habr.com/ru/hubs/python/) +- [Программирование](https://habr.com/ru/hubs/programming/) +- [Карьера в IT-индустрии](https://habr.com/ru/hubs/career/) \ No newline at end of file diff --git a/PYTHON/Password & key = PATH.md b/PYTHON/Password & key = PATH.md new file mode 100644 index 0000000..a5dc455 --- /dev/null +++ b/PYTHON/Password & key = PATH.md @@ -0,0 +1,94 @@ +```python +# Пример куска программы: +from django.conf import settings + +def webhook(request): + ''' Обработчик вебхука от платежной системы ''' + api_token = settigs.MY_API_KEY # ключ берем из файла settings.py + add_mailchimp_email_with_tag(email=request.POST.get('email', + audience_name='DONATES', + tag='DONATE') +``` +==`vim config/settings.py`== +```sh +#.......... + +MY_API_KEY = os.getenv('MY_API_KEY') + +#.......... +``` + +### Уровни переменных окружения: + +##### - OS (операционная система) +```sh +sudo vim /etc/environment +#-------------- +THIRD_VAR="123" +# будет видна после перезагрузки +``` +##### - USER (пользователь) +```sh +vim ~/.bashrc +#-------------- +export USER_VAR=123 +# exit +echo $USER_VAR +``` +##### - SESSION (сессия) +```sh +export SESSION_VAR=777 +echo $SESSION_VAR +# после выхода из сессии переменная пропадает +``` +##### - APPLICATION (приложение) +```sh +vim setenv.sh +#-------------- +#!bash/bin/env python3 + +export VAR1=123 +export VAR1=12345 +export VAR1=123567hbkjbjk +#-------------- + +# запустим скрипт: +. ./setenv.sh + +# или +source ./setenv.sh + +# Также можно сразу запускать приложение с установкой переменных, +> PYTHON_ENV=123 VAR=435 VAR2=jfhfg3 python testenv.py + +``` + +```python +# Использование модуля < python-dotenv > +pip install python-dotenv + +nvim .env +API_KEY='my_secret_key' +# Сразу добавляем этот файл в .gitignore !!! + +# Программа: +from dotenv import load_dotenv + +load_dotenv() + +key = os.getenv('API_KEY') +print(key) + +#################### +# Скрытие пароля при вводе: +from getpass import getpass +import pyAesCrypt + +password = getpass(promt='Пароль для расшифровки') +pyAesCrypt.decryptFile( + "secret_data.aes", + "secret_data.txt", + password +) +``` + diff --git a/PYTHON/PrettyTable.md b/PYTHON/PrettyTable.md new file mode 100644 index 0000000..f344197 --- /dev/null +++ b/PYTHON/PrettyTable.md @@ -0,0 +1,15 @@ +```python +from prettytable import PrettyTable + +table = PrettyTable() +table.field_names = ['Name', 'Password', 'Sex', 'Age'] +table.add_row(['Anna', 'qwerty', 'Female', 24]) +table.add_row(['Victor', '12345', 'Male', 31]) +table.add_row(['Denis', 'mcxc', 'Male', 19]) +table.add_row(['Mia', 'be6djjx', 'Female', 42]) +table.add_row(['Oleg', 'mdso98676', 'Male', 29]) + +print(table) +``` + +![[telegram-cloud-photo-size-2-5413616564063822987-y.jpg]] \ No newline at end of file diff --git a/PYTHON/Python-cheatsheet.md b/PYTHON/Python-cheatsheet.md new file mode 100644 index 0000000..227e7cb --- /dev/null +++ b/PYTHON/Python-cheatsheet.md @@ -0,0 +1,3717 @@ +--- +title: Python CheatSheet +description: The most commonly used Python concepts are given here. +created: 2022-10-20 +--- + +# Python CheatSheet for Developers + +## Content Outlines + +**1. Collections:** **[`List`](#list)**__,__ **[`Dictionary`](#dictionary)**__,__ **[`Set`](#set)**__,__ **[`Tuple`](#tuple)**__,__ **[`Range`](#range)**__,__ **[`Enumerate`](#enumerate)**__,__ **[`Iterator`](#iterator)**__,__ **[`Generator`](#generator)**__.__ +**2. Types:** **[`Type`](#type)**__,__ **[`String`](#string)**__,__ **[`Regular_Exp`](#regex)**__,__ **[`Format`](#format)**__,__ **[`Numbers`](#numbers-1)**__,__ **[`Combinatorics`](#combinatorics)**__,__ **[`Datetime`](#datetime)**__.__ +**3. Syntax:** **[`Args`](#arguments)**__,__ **[`Inline`](#inline)**__,__ **[`Import`](#imports)**__,__ **[`Decorator`](#decorator)**__,__ **[`Class`](#class)**__,__ **[`Duck_Types`](#duck-types)**__,__ **[`Enum`](#enum)**__,__ **[`Exception`](#exceptions)**__.__ +**4. System:** **[`Exit`](#exit)**__,__ **[`Print`](#print)**__,__ **[`Input`](#input)**__,__ **[`Command_Line_Arguments`](#command-line-arguments)**__,__ **[`Open`](#open)**__,__ **[`Path`](#paths)**__,__ **[`OS_Commands`](#os-commands)**__.__ +**5. Data:** **[`JSON`](#json)**__,__ **[`Pickle`](#pickle)**__,__ **[`CSV`](#csv)**__,__ **[`SQLite`](#sqlite)**__,__ **[`Bytes`](#bytes)**__,__ **[`Struct`](#struct)**__,__ **[`Array`](#array)**__,__ **[`Memory_View`](#memory-view)**__,__ **[`Deque`](#deque)**__.__ +**6. Advanced:** **[`Threading`](#threading)**__,__ **[`Operator`](#operator)**__,__ **[`Introspection`](#introspection)**__,__ **[`Metaprograming`](#metaprogramming)**__,__ **[`Eval`](#eval)**__,__ **[`Coroutines`](#coroutines)**__.__ +**7. Libraries:** **[`Progress_Bar`](#progress-bar)**__,__ **[`Plot`](#plot)**__,__ **[`Table`](#table)**__,__ **[`Curses`](#curses)**__,__ **[`Logging`](#logging)**__,__ **[`Scraping`](#scraping)**__,__ **[`Web`](#web)**__,__ **[`Profile`](#profiling)**__,__ +**[`NumPy`](#numpy)**__,__ **[`Image`](#image)**__,__ **[`Audio`](#audio)**__,__ **[`PyGames`](#pygame)**__,__ **[`Pandas`](#pandas)**__,__ **[`OpenCV`](#opencv)**__,__ **[`DataFrame`](#dataframe)**__.__ + + +Main +---- +```python +if __name__ == '__main__': # Runs main() if file wasn't imported. + main() +``` + +**[🔼Back to Top](#content-outlines)** + +Shortcut to comment/uncomment the selected code +---- + +| Editor | Shortcut Block Comment | Shortcut Block Uncomment | +| :----------: | :----------------------: | :----------------------: | +| Eclipse | `CTRL + /` | `CTRL + /` | +| PyDev | `CTRL + /` | `CTRL + /` | +| PyCharm | `CTRL + /` | `CTRL + /` | +| Notepad++ | `CTRL + K` | `CTRL + SHIFT + K` | +| IDLE | `ALT + 3` | `ALT + 4` | + +**[🔼Back to Top](#content-outlines)** + +List +---- +```python + = [] # Or: [from_inclusive : to_exclusive : ±step] +``` + +```python +.append() # Or: += [] | adds a new element to the end of the list +.extend() # Or: += | adds all elements of a collection(list,tuples,etc) to the end of the list +``` + +```python +.sort() # Sorts in ascending order. +.reverse() # Reverses the list in-place. + = sorted() # Returns a new sorted list. + = reversed() # Returns reversed iterator. +``` + +```python +sum_of_elements = sum() +elementwise_sum = [sum(pair) for pair in zip(list_a, list_b)] +sorted_by_second = sorted(, key=lambda el: el[1]) +sorted_by_both = sorted(, key=lambda el: (el[1], el[0])) +flatter_list = list(itertools.chain.from_iterable()) +product_of_elems = functools.reduce(lambda out, el: out * el, ) +list_of_chars = list() +``` +* **For details about sorted(), min() and max() see [sortable](#sortable).** +* **Module [operator](#operator) provides functions itemgetter() and mul() that offer the same functionality as [lambda](#lambda) expressions above.** + +```python +.insert(, ) # Inserts item at index and moves the rest to the right. + = .pop([]) # Removes and returns item at index or from the end. + = .count() # Returns number of occurrences. Also works on strings. + = .index() # Returns index of the first occurrence or raises ValueError. +.remove() # Removes first occurrence of the item or raises ValueError. +.clear() # Removes all items. Also works on dictionary and set. +``` + +**[🔼Back to Top](#content-outlines)** + +Dictionary +---------- +```python + = .keys() # Coll. of keys that reflects changes. + = .values() # Coll. of values that reflects changes. + = .items() # Coll. of key-value tuples that reflects chgs. +``` +```python +value = .get(key, default=None) # Returns default if key is missing. +value = .setdefault(key, default=None) # Returns and writes default if key is missing. + = collections.defaultdict() # Returns a dict with default value of type. + = collections.defaultdict(lambda: 1) # Returns a dict with default value 1. +``` + +```python + = dict() # Creates a dict from coll. of key-value pairs. + = dict(zip(keys, values)) # Creates a dict from two collections. + = dict.fromkeys(keys [, value]) # Creates a dict from collection of keys. +``` + +```python +.update() # Adds items. Replaces ones with matching keys. +value = .pop(key) # Removes item or raises KeyError. +{k for k, v in .items() if v == value} # Returns set of keys that point to the value. +{k: v for k, v in .items() if k in keys} # Returns a dictionary, filtered by keys. +``` + +### Counter +```python +>>> from collections import Counter +>>> colors = ['blue', 'blue', 'blue', 'red', 'red'] +>>> counter = Counter(colors) +>>> counter['yellow'] += 1 +Counter({'blue': 3, 'red': 2, 'yellow': 1}) +>>> counter.most_common()[0] +('blue', 3) +``` + +**[🔼Back to Top](#content-outlines)** + +Set +--- +```python + = set() # `{}` returns a dictionary. +``` + +```python +.add() # Or: |= {} +.update( [, ...]) # Or: |= +``` + +```python + = .union() # Or: | + = .intersection() # Or: & + = .difference() # Or: - + = .symmetric_difference() # Or: ^ + = .issubset() # Or: <= + = .issuperset() # Or: >= +``` + +```python + = .pop() # Raises KeyError if empty. +.remove() # Raises KeyError if missing. +.discard() # Doesn't raise an error. +``` + +### Frozen Set +* **Is immutable and hashable.** +* **That means it can be used as a key in a dictionary or as an element in a set.** +```python + = frozenset() +``` + +**[🔼Back to Top](#content-outlines)** + +Tuple +----- +**Tuple is an immutable and hashable list.** +```python + = () # Empty tuple. + = (,) # Or: , + = (, [, ...]) # Or: , [, ...] +``` + +### Named Tuple +**Tuple's subclass with named elements.** + +```python +>>> from collections import namedtuple +>>> Point = namedtuple('Point', 'x y') +>>> p = Point(1, y=2) +Point(x=1, y=2) +>>> p[0] +1 +>>> p.x +1 +>>> getattr(p, 'y') +2 +``` + +**[🔼Back to Top](#content-outlines)** + +Range +----- +**Immutable and hashable sequence of integers.** +```python + = range(stop) # range(to_exclusive) + = range(start, stop) # range(from_inclusive, to_exclusive) + = range(start, stop, ±step) # range(from_inclusive, to_exclusive, ±step_size) +``` + +```python +>>> [i for i in range(3)] +[0, 1, 2] +``` + +**[🔼Back to Top](#content-outlines)** + +Enumerate +--------- +```python +for i, el in enumerate( [, i_start]): + ... +``` + +**[🔼Back to Top](#content-outlines)** + +Iterator +-------- +```python + = iter() # `iter()` returns unmodified iterator. + = iter(, to_exclusive) # A sequence of return values until 'to_exclusive'. + = next( [, default]) # Raises StopIteration or returns 'default' on end. + = list() # Returns a list of iterator's remaining elements. +``` + +### Itertools +```python +import itertools as it +``` + +```python + = it.count(start=0, step=1) # Returns updated value endlessly. Accepts floats. + = it.repeat( [, times]) # Returns element endlessly or 'times' times. + = it.cycle() # Repeats the sequence endlessly. +``` + +```python + = it.chain(, [, ...]) # Empties collections in order (figuratively). + = it.chain.from_iterable() # Empties collections inside a collection in order. +``` + +```python + = it.islice(, to_exclusive) # Only returns first 'to_exclusive' elements. + = it.islice(, from_inc, …) # `to_exclusive, +step_size`. Indices can be None. +``` + +**[🔼Back to Top](#content-outlines)** + +Generator +--------- +* **Any function that contains a yield statement returns a generator.** +* **Generators and iterators are interchangeable.** + +```python +def count(start, step): + while True: + yield start + start += step +``` + +```python +>>> counter = count(10, 2) +>>> next(counter), next(counter), next(counter) +(10, 12, 14) +``` + +**[🔼Back to Top](#content-outlines)** + +Type +---- +* **Everything is an object.** +* **Every object has a type.** +* **Type and class are synonymous.** + +```python + = type() # Or: .__class__ + = isinstance(, ) # Or: issubclass(type(), ) +``` + +```python +>>> type('a'), 'a'.__class__, str +(, , ) +``` + +#### Some types do not have built-in names, so they must be imported: +```python +from types import FunctionType, MethodType, LambdaType, GeneratorType, ModuleType +``` + +### Abstract Base Classes +**Each abstract base class specifies a set of virtual subclasses. These classes are then recognized by isinstance() and issubclass() as subclasses of the ABC, although they are really not. ABC can also manually decide whether or not a specific class is its virtual subclass, usually based on which methods the class has implemented. For instance, Iterable ABC looks for method iter(), while Collection ABC looks for iter(), contains() and len().** + +```python +>>> from collections.abc import Iterable, Collection, Sequence +>>> isinstance([1, 2, 3], Iterable) +True +``` + +```text ++------------------+------------+------------+------------+ +| | Iterable | Collection | Sequence | ++------------------+------------+------------+------------+ +| list, range, str | yes | yes | yes | +| dict, set | yes | yes | | +| iter | yes | | | ++------------------+------------+------------+------------+ +``` + +```python +>>> from numbers import Number, Complex, Real, Rational, Integral +>>> isinstance(123, Number) +True +``` + +```text ++--------------------+----------+----------+----------+----------+----------+ +| | Number | Complex | Real | Rational | Integral | ++--------------------+----------+----------+----------+----------+----------+ +| int | yes | yes | yes | yes | yes | +| fractions.Fraction | yes | yes | yes | yes | | +| float | yes | yes | yes | | | +| complex | yes | yes | | | | +| decimal.Decimal | yes | | | | | ++--------------------+----------+----------+----------+----------+----------+ +``` + +**[🔼Back to Top](#content-outlines)** + +String +------ +```python + = .strip() # Strips all whitespace characters from both ends. + = .strip('') # Strips all passed characters from both ends. +``` + +```python + = .split() # Splits on one or more whitespace characters. + = .split(sep=None, maxsplit=-1) # Splits on 'sep' str at most 'maxsplit' times. + = .splitlines(keepends=False) # On [\n\r\f\v\x1c-\x1e\x85\u2028\u2029] and \r\n. + = .join() # Joins elements using string as a separator. +``` + +```python + = in # Checks if string contains a substring. + = .startswith() # Pass tuple of strings for multiple options. + = .endswith() # Pass tuple of strings for multiple options. + = .find() # Returns start index of the first match or -1. + = .index() # Same, but raises ValueError if missing. +``` + +```python + = .replace(old, new [, count]) # Replaces 'old' with 'new' at most 'count' times. + = .translate() # Use `str.maketrans()` to generate table. +``` + +```python + = chr() # Converts int to Unicode character. + = ord() # Converts Unicode character to int. +``` +* **Also: `'lstrip()'`, `'rstrip()'` and `'rsplit()'`.** +* **Also: `'lower()'`, `'upper()'`, `'capitalize()'` and `'title()'`.** + +### Property Methods +```text ++---------------+----------+----------+----------+----------+----------+ +| | [ !#$%…] | [a-zA-Z] | [¼½¾] | [²³¹] | [0-9] | ++---------------+----------+----------+----------+----------+----------+ +| isprintable() | yes | yes | yes | yes | yes | +| isalnum() | | yes | yes | yes | yes | +| isnumeric() | | | yes | yes | yes | +| isdigit() | | | | yes | yes | +| isdecimal() | | | | | yes | ++---------------+----------+----------+----------+----------+----------+ +``` +* **Also: `'isspace()'` checks for `'[ \t\n\r\f\v\x1c-\x1f\x85\u2000…]'`.** + +**[🔼Back to Top](#content-outlines)** + +Regex +----- +```python +import re + = re.sub(, new, text, count=0) # Substitutes all occurrences with 'new'. + = re.findall(, text) # Returns all occurrences as strings. + = re.split(, text, maxsplit=0) # Use brackets in regex to include the matches. + = re.search(, text) # Searches for first occurrence of the pattern. + = re.match(, text) # Searches only at the beginning of the text. + = re.finditer(, text) # Returns all occurrences as Match objects. +``` + +* **Argument 'new' can be a function that accepts a Match object and returns a string.** +* **Search() and match() return None if they can't find a match.** +* **Argument `'flags=re.IGNORECASE'` can be used with all functions.** +* **Argument `'flags=re.MULTILINE'` makes `'^'` and `'$'` match the start/end of each line.** +* **Argument `'flags=re.DOTALL'` makes dot also accept the `'\n'`.** +* **Use `r'\1'` or `'\\1'` for backreference (`'\1'` returns a character with octal code 1).** +* **Add `'?'` after `'*'` and `'+'` to make them non-greedy.** + +### Match Object +```python + = .group() # Returns the whole match. Also group(0). + = .group(1) # Returns part in the first bracket. + = .groups() # Returns all bracketed parts. + = .start() # Returns start index of the match. + = .end() # Returns exclusive end index of the match. +``` + +### Special Sequences +```python +'\d' == '[0-9]' # Matches decimal characters. +'\w' == '[a-zA-Z0-9_]' # Matches alphanumerics and underscore. +'\s' == '[ \t\n\r\f\v]' # Matches whitespaces. +``` + +* **By default, decimal characters, alphanumerics and whitespaces from all alphabets are matched unless `'flags=re.ASCII'` argument is used.** +* **As shown above, it restricts all special sequence matches to the first 128 characters and prevents `'\s'` from accepting `'[\x1c-\x1f]'` (the so-called separator characters).** +* **Use a capital letter for negation (all non-ASCII characters will be matched when used in combination with ASCII flag).** + +**[🔼Back to Top](#content-outlines)** + +Format +------ +```python + = f'{}, {}' # Curly brackets can also contain expressions. This is named "Fstring" + = '{}, {}'.format(, ) # Or: '{0}, {a}'.format(, a=) + = '%s, %s' % (, ) # Redundant and inferior C style formatting. +``` + +### Attributes +```python +>>> Person = collections.namedtuple('Person', 'name height') +>>> person = Person('Jean-Luc', 187) +>>> f'{person.height}' +'187' +>>> '{p.height}'.format(p=person) +'187' +``` + +### General Options +```python +{:<10} # ' ' +{:^10} # ' ' +{:>10} # ' ' +{:.<10} # '......' +{:0} # '' +``` +* **Options can be generated dynamically: `f'{:{}[…]}'`.** +* **Adding `'!r'` before the colon converts object to string by calling its [repr()](#class) method.** + +### Strings +```python +{'abcde':10} # 'abcde ' +{'abcde':10.3} # 'abc ' +{'abcde':.3} # 'abc' +{'abcde'!r:10} # "'abcde' " +``` + +### Numbers +```python +{123456:10} # ' 123456' +{123456:10,} # ' 123,456' +{123456:10_} # ' 123_456' +{123456:+10} # ' +123456' +{123456:=+10} # '+ 123456' +{123456: } # ' 123456' +{-123456: } # '-123456' +``` + +### Floats +```python +{1.23456:10.3} # ' 1.23' +{1.23456:10.3f} # ' 1.235' +{1.23456:10.3e} # ' 1.235e+00' +{1.23456:10.3%} # ' 123.456%' +``` + +#### Comparison of presentation types: +```text ++--------------+----------------+----------------+----------------+----------------+ +| | {} | {:f} | {:e} | {:%} | ++--------------+----------------+----------------+----------------+----------------+ +| 0.000056789 | '5.6789e-05' | '0.000057' | '5.678900e-05' | '0.005679%' | +| 0.00056789 | '0.00056789' | '0.000568' | '5.678900e-04' | '0.056789%' | +| 0.0056789 | '0.0056789' | '0.005679' | '5.678900e-03' | '0.567890%' | +| 0.056789 | '0.056789' | '0.056789' | '5.678900e-02' | '5.678900%' | +| 0.56789 | '0.56789' | '0.567890' | '5.678900e-01' | '56.789000%' | +| 5.6789 | '5.6789' | '5.678900' | '5.678900e+00' | '567.890000%' | +| 56.789 | '56.789' | '56.789000' | '5.678900e+01' | '5678.900000%' | ++--------------+----------------+----------------+----------------+----------------+ +``` + +```text ++--------------+----------------+----------------+----------------+----------------+ +| | {:.2} | {:.2f} | {:.2e} | {:.2%} | ++--------------+----------------+----------------+----------------+----------------+ +| 0.000056789 | '5.7e-05' | '0.00' | '5.68e-05' | '0.01%' | +| 0.00056789 | '0.00057' | '0.00' | '5.68e-04' | '0.06%' | +| 0.0056789 | '0.0057' | '0.01' | '5.68e-03' | '0.57%' | +| 0.056789 | '0.057' | '0.06' | '5.68e-02' | '5.68%' | +| 0.56789 | '0.57' | '0.57' | '5.68e-01' | '56.79%' | +| 5.6789 | '5.7' | '5.68' | '5.68e+00' | '567.89%' | +| 56.789 | '5.7e+01' | '56.79' | '5.68e+01' | '5678.90%' | ++--------------+----------------+----------------+----------------+----------------+ +``` +* **When both rounding up and rounding down are possible, the one that returns result with even last digit is chosen. That makes `'{6.5:.0f}'` a `'6'` and `'{7.5:.0f}'` an `'8'`.** +* **This rule only effects numbers that can be represented exactly by a float (`.5`, `.25`, …).** + +### Ints +```python +{90:c} # 'Z' +{90:b} # '1011010' +{90:X} # '5A' +``` + +**[🔼Back to Top](#content-outlines)** + +Numbers +------- +```python + = int() # Or: math.floor() + = float() # Or: + = complex(real=0, imag=0) # Or: ± j + = fractions.Fraction(0, 1) # Or: Fraction(numerator=0, denominator=1) + = decimal.Decimal() # Or: Decimal((sign, digits, exponent)) +``` +* **`'int()'` and `'float()'` raise ValueError on malformed strings.** +* **Decimal numbers are stored exactly, unlike most floats where `'1.1 + 2.2 != 3.3'`.** +* **Floats can be compared with: `'math.isclose(, )'`.** +* **Precision of decimal operations is set with: `'decimal.getcontext().prec = '`.** + +### Basic Functions +```python + = pow(, ) # Or: ** + = abs() # = abs() + = round( [, ±ndigits]) # `round(126, -1) == 130` +``` + +### Math +```python +from math import e, pi, inf, nan, isinf, isnan # ` == nan` is always False. +from math import sin, cos, tan, asin, acos, atan # Also: degrees, radians. +from math import log, log10, log2 # Log can accept base as second arg. +``` + +### Statistics +```python +from statistics import mean, median, variance # Also: stdev, quantiles, groupby. +``` + +### Random +```python +from random import random, randint, choice # Also shuffle, gauss, triangular, seed. + = random() # A float inside [0, 1). + = randint(from_inc, to_inc) # An int inside [from_inc, to_inc]. + = choice() # Keeps the sequence intact. +``` + +### Bin, Hex +```python + = ±0b # Or: ±0x + = int('±', 2) # Or: int('±', 16) + = int('±0b', 0) # Or: int('±0x', 0) + = bin() # Returns '[-]0b'. +``` + +### Bitwise Operators +```python + = & # And (0b1100 & 0b1010 == 0b1000). + = | # Or (0b1100 | 0b1010 == 0b1110). + = ^ # Xor (0b1100 ^ 0b1010 == 0b0110). + = << n_bits # Left shift. Use >> for right. + = ~ # Not. Also - - 1. +``` + +**[🔼Back to Top](#content-outlines)** + +Combinatorics +------------- +* **Every function returns an iterator.** +* **If you want to print the iterator, you need to pass it to the list() function first!** + +```python +import itertools as it +``` + +```python +>>> it.product([0, 1], repeat=3) +[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), + (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)] +``` + +```python +>>> it.product('abc', 'abc') # a b c +[('a', 'a'), ('a', 'b'), ('a', 'c'), # a x x x + ('b', 'a'), ('b', 'b'), ('b', 'c'), # b x x x + ('c', 'a'), ('c', 'b'), ('c', 'c')] # c x x x +``` + +```python +>>> it.combinations('abc', 2) # a b c +[('a', 'b'), ('a', 'c'), # a . x x + ('b', 'c')] # b . . x +``` + +```python +>>> it.combinations_with_replacement('abc', 2) # a b c +[('a', 'a'), ('a', 'b'), ('a', 'c'), # a x x x + ('b', 'b'), ('b', 'c'), # b . x x + ('c', 'c')] # c . . x +``` + +```python +>>> it.permutations('abc', 2) # a b c +[('a', 'b'), ('a', 'c'), # a . x x + ('b', 'a'), ('b', 'c'), # b x . x + ('c', 'a'), ('c', 'b')] # c x x . +``` + +**[🔼Back to Top](#content-outlines)** + +Datetime +-------- +* **Module 'datetime' provides 'date' ``, 'time' ``, 'datetime' `
` and 'timedelta' `
` classes. All are immutable and hashable.** +* **Time and datetime objects can be 'aware' ``, meaning they have defined timezone, or 'naive' ``, meaning they don't.** +* **If object is naive, it is presumed to be in the system's timezone.** + +```python +from datetime import date, time, datetime, timedelta +from dateutil.tz import UTC, tzlocal, gettz, datetime_exists, resolve_imaginary +``` + +### Constructors +```python + = date(year, month, day) # Only accepts valid dates from 1 to 9999 AD. + = time(hour=0, minute=0, second=0) # Also: `microsecond=0, tzinfo=None, fold=0`. +
= datetime(year, month, day, hour=0) # Also: `minute=0, second=0, microsecond=0, …`. +
= timedelta(weeks=0, days=0, hours=0) # Also: `minutes=0, seconds=0, microsecond=0`. +``` +* **Use `'.weekday()'` to get the day of the week as an int, with Monday being 0.** +* **`'fold=1'` means the second pass in case of time jumping back for one hour.** +* **Timedelta normalizes arguments to ±days, seconds (< 86 400) and microseconds (< 1M).** + +### Now +```python + = D/DT.today() # Current local date or naive datetime. + = DT.utcnow() # Naive datetime from current UTC time. + = DT.now() # Aware datetime from current tz time. +``` +* **To extract time use `'.time()'`, `'.time()'` or `'.timetz()'`.** + +### Timezone +```python + = UTC # UTC timezone. London without DST. + = tzlocal() # Local timezone. Also gettz(). + = gettz('/') # 'Continent/City_Name' timezone or None. + =
.astimezone() # Datetime, converted to the passed timezone. + = .replace(tzinfo=) # Unconverted object with a new timezone. +``` + +### Encode +```python + = D/T/DT.fromisoformat('') # Object from ISO string. Raises ValueError. +
= DT.strptime(, '') # Datetime from str, according to format. + = D/DT.fromordinal() # D/DTn from days since the Gregorian NYE 1. + = DT.fromtimestamp() # Local time DTn from seconds since the Epoch. + = DT.fromtimestamp(, ) # Aware datetime from seconds since the Epoch. +``` +* **ISO strings come in following forms: `'YYYY-MM-DD'`, `'HH:MM:SS.mmmuuu[±HH:MM]'`, or both separated by an arbitrary character. All parts following hours are optional.** +* **Python uses the Unix Epoch: `'1970-01-01 00:00 UTC'`, `'1970-01-01 01:00 CET'`, ...** + +### Decode +```python + = .isoformat(sep='T') # Also: `timespec='auto/hours/minutes/seconds/…'`. + = .strftime('') # Custom string representation. + = .toordinal() # Days since Gregorian NYE 1, ignoring time and tz. + = .timestamp() # Seconds since the Epoch, from DTn in local tz. + = .timestamp() # Seconds since the Epoch, from aware datetime. +``` + +### Format +```python +>>> dt = datetime.strptime('2015-05-14 23:39:00.00 +2000', '%Y-%m-%d %H:%M:%S.%f %z') +>>> dt.strftime("%A, %dth of %B '%y, %I:%M%p %Z") +"Thursday, 14th of May '15, 11:39PM UTC+02:00" +``` +* **`'%Z'` only accepts `'UTC/GMT'` and local timezone's code. `'%z'` also accepts `'±HH:MM'`.** +* **For abbreviated weekday and month use `'%a'` and `'%b'`.** + +### Arithmetics +```python + = ±
# Returned datetime can fall into missing hour. + = - # Returns the difference, ignoring time jumps. + = - # Ignores time jumps if they share tzinfo object. + = * # Also: = abs() and = ±% . + = / # How many weeks/years there are in TD. Also //. +``` + +**[🔼Back to Top](#content-outlines)** + +Arguments +--------- +### Inside Function Call +```python +func() # func(0, 0) +func() # func(x=0, y=0) +func(, ) # func(0, y=0) +``` + +### Inside Function Definition +```python +def func(): ... # def func(x, y): ... +def func(): ... # def func(x=0, y=0): ... +def func(, ): ... # def func(x, y=0): ... +``` +* **Default values are evaluated when function is first encountered in the scope.** +* **Any mutation of a mutable default value will persist between invocations.** + + +Splat Operator +-------------- +### Inside Function Call +**Splat expands a collection into positional arguments, while splatty-splat expands a dictionary into keyword arguments.** +```python +args = (1, 2) +kwargs = {'x': 3, 'y': 4, 'z': 5} +func(*args, **kwargs) +``` + +#### Is the same as: +```python +func(1, 2, x=3, y=4, z=5) +``` + +### Inside Function Definition +**Splat combines zero or more positional arguments into a tuple, while splatty-splat combines zero or more keyword arguments into a dictionary.** +```python +def add(*a): + return sum(a) +``` + +```python +>>> add(1, 2, 3) +6 +``` + +#### Legal argument combinations: +```python +def f(*args): ... # f(1, 2, 3) +def f(x, *args): ... # f(1, 2, 3) +def f(*args, z): ... # f(1, 2, z=3) +``` + +```python +def f(**kwargs): ... # f(x=1, y=2, z=3) +def f(x, **kwargs): ... # f(x=1, y=2, z=3) | f(1, y=2, z=3) +``` + +```python +def f(*args, **kwargs): ... # f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3) | f(1, 2, 3) +def f(x, *args, **kwargs): ... # f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3) | f(1, 2, 3) +def f(*args, y, **kwargs): ... # f(x=1, y=2, z=3) | f(1, y=2, z=3) +``` + +```python +def f(*, x, y, z): ... # f(x=1, y=2, z=3) +def f(x, *, y, z): ... # f(x=1, y=2, z=3) | f(1, y=2, z=3) +def f(x, y, *, z): ... # f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3) +``` + +### Other Uses +```python + = [* [, ...]] # Or: list() [+ ...] + = (*, [...]) # Or: tuple() [+ ...] + = {* [, ...]} # Or: set() [| ...] + = {** [, ...]} # Or: dict(** [, ...]) +``` + +```python +head, *body, tail = # Head or tail can be omitted. +``` + +**[🔼Back to Top](#content-outlines)** + +Inline +------ +### Lambda +```python + = lambda: # A single statement function. + = lambda , : # Also accepts default arguments. +``` + +### Comprehensions +```python + = [i+1 for i in range(10)] # Or: [1, 2, ..., 10] + = (i for i in range(10) if i > 5) # Or: iter([6, 7, 8, 9]) + = {i+5 for i in range(10)} # Or: {5, 6, ..., 14} + = {i: i*2 for i in range(10)} # Or: {0: 0, 1: 2, ..., 9: 18} +``` + +```python +>>> [l+r for l in 'abc' for r in 'abc'] +['aa', 'ab', 'ac', ..., 'cc'] +``` + +### Map, Filter, Reduce +```python + = map(lambda x: x + 1, range(10)) # Or: iter([1, 2, ..., 10]) + = filter(lambda x: x > 5, range(10)) # Or: iter([6, 7, 8, 9]) + = reduce(lambda out, x: out + x, range(10)) # Or: 45 +``` +* **Reduce must be imported from the functools module.** + +### Any, All +```python + = any() # Is `bool(el)` True for any element. + = all() # Is True for all elements or empty. +``` + +### Conditional Expression +```python + = if else # Only one expression gets evaluated. +``` + +```python +>>> [a if a else 'zero' for a in (0, 1, 2, 3)] +['zero', 1, 2, 3] +``` + +### Named Tuple, Enum, Dataclass +```python +from collections import namedtuple +Point = namedtuple('Point', 'x y') # Creates a tuple's subclass. +point = Point(0, 0) # Returns its instance. +``` + +```python +from enum import Enum +Direction = Enum('Direction', 'n e s w') # Creates an enum. +direction = Direction.n # Returns its member. +``` + +```python +from dataclasses import make_dataclass +Player = make_dataclass('Player', ['loc', 'dir']) # Creates a class. +player = Player(point, direction) # Returns its instance. +``` + +**[🔼Back to Top](#content-outlines)** + +Imports +------- +```python +import # Imports a built-in or '.py'. +import # Imports a built-in or '/__init__.py'. +import . # Imports a built-in or '/.py'. +``` +* **Package is a collection of modules, but it can also define its own objects.** +* **On a filesystem this corresponds to a directory of Python files with an optional init script.** +* **Running `'import '` does not automatically provide access to the package's modules unless they are explicitly imported in its init script.** + + +Closure +------- +**We have/get a closure in Python when:** +* **A nested function references a value of its enclosing function and then** +* **the enclosing function returns the nested function.** + +```python +def get_multiplier(a): + def out(b): + return a * b + return out +``` + +```python +>>> multiply_by_3 = get_multiplier(3) +>>> multiply_by_3(10) +30 +``` + +* **If multiple nested functions within enclosing function reference the same value, that value gets shared.** +* **To dynamically access function's first free variable use `'.__closure__[0].cell_contents'`.** + +### Partial +```python +from functools import partial + = partial( [, , , ...]) +``` + +```python +>>> import operator as op +>>> multiply_by_3 = partial(op.mul, 3) +>>> multiply_by_3(10) +30 +``` +* **Partial is also useful in cases when function needs to be passed as an argument because it enables us to set its arguments beforehand.** +* **A few examples being: `'defaultdict()'`, `'iter(, to_exclusive)'` and dataclass's `'field(default_factory=)'`.** + +### Non-Local +**If variable is being assigned to anywhere in the scope, it is regarded as a local variable, unless it is declared as a 'global' or a 'nonlocal'.** + +```python +def get_counter(): + i = 0 + def out(): + nonlocal i + i += 1 + return i + return out +``` + +```python +>>> counter = get_counter() +>>> counter(), counter(), counter() +(1, 2, 3) +``` + +**[🔼Back to Top](#content-outlines)** + +Decorator +--------- +* **A decorator takes a function, adds some functionality and returns it.** +* **It can be any [callable](#callable), but is usually implemented as a function that returns a [closure](#closure).** + +```python +@decorator_name +def function_that_gets_passed_to_decorator(): + ... +``` + +### Debugger Example +**Decorator that prints function's name every time the function is called.** + +```python +from functools import wraps + +def debug(func): + @wraps(func) + def out(*args, **kwargs): + print(func.__name__) + return func(*args, **kwargs) + return out + +@debug +def add(x, y): + return x + y +``` +* **Wraps is a helper decorator that copies the metadata of the passed function (func) to the function it is wrapping (out).** +* **Without it `'add.__name__'` would return `'out'`.** + +### LRU Cache +**Decorator that caches function's return values. All function's arguments must be hashable.** + +```python +from functools import lru_cache + +@lru_cache(maxsize=None) +def fib(n): + return n if n < 2 else fib(n-2) + fib(n-1) +``` +* **Default size of the cache is 128 values. Passing `'maxsize=None'` makes it unbounded.** +* **CPython interpreter limits recursion depth to 1000 by default. To increase it use `'sys.setrecursionlimit()'`.** + +### Parametrized Decorator +**A decorator that accepts arguments and returns a normal decorator that accepts a function.** +```python +from functools import wraps + +def debug(print_result=False): + def decorator(func): + @wraps(func) + def out(*args, **kwargs): + result = func(*args, **kwargs) + print(func.__name__, result if print_result else '') + return result + return out + return decorator + +@debug(print_result=True) +def add(x, y): + return x + y +``` +* **Using only `'@debug'` to decorate the add() function would not work here, because debug would then receive the add() function as a 'print_result' argument. Decorators can however manually check if the argument they received is a function and act accordingly.** + +**[🔼Back to Top](#content-outlines)** + +Class +----- +```python +class : + def __init__(self, a): + self.a = a + def __repr__(self): + class_name = self.__class__.__name__ + return f'{class_name}({self.a!r})' + def __str__(self): + return str(self.a) + + @classmethod + def get_class_name(cls): + return cls.__name__ +``` +* **Return value of repr() should be unambiguous and of str() readable.** +* **If only repr() is defined, it will also be used for str().** +* **Methods decorated with `'@staticmethod'` do not receive 'self' nor 'cls' as their first arg.** + +#### Str() use cases: +```python +print() +f'{}' +logging.warning() +csv.writer().writerow([]) +raise Exception() +``` + +#### Repr() use cases: +```python +print/str/repr([]) +f'{!r}' +Z = dataclasses.make_dataclass('Z', ['a']); print/str/repr(Z()) +>>> +``` + +### Constructor Overloading +```python +class : + def __init__(self, a=None): + self.a = a +``` + +### Inheritance +```python +class Person: + def __init__(self, name, age): + self.name = name + self.age = age + +class Employee(Person): + def __init__(self, name, age, staff_num): + super().__init__(name, age) + self.staff_num = staff_num +``` + +### Multiple Inheritance +```python +class A: pass +class B: pass +class C(A, B): pass +``` + +**MRO determines the order in which parent classes are traversed when searching for a method or an attribute:** +```python +>>> C.mro() +[, , , ] +``` + +### Property +**Pythonic way of implementing getters and setters.** +```python +class Person: + @property + def name(self): + return ' '.join(self._name) + + @name.setter + def name(self, value): + self._name = value.split() +``` + +```python +>>> person = Person() +>>> person.name = '\t Guido van Rossum \n' +>>> person.name +'Guido van Rossum' +``` + +### Dataclass +**Decorator that automatically generates init(), repr() and eq() special methods.** +```python +from dataclasses import dataclass, field + +@dataclass(order=False, frozen=False) +class : + : + : = + : list/dict/set = field(default_factory=list/dict/set) +``` +* **Objects can be made sortable with `'order=True'` and immutable with `'frozen=True'`.** +* **For object to be hashable, all attributes must be hashable and 'frozen' must be True.** +* **Function field() is needed because `': list = []'` would make a list that is shared among all instances. Its 'default_factory' argument can be any [callable](#callable).** +* **For attributes of arbitrary type use `'typing.Any'`.** + +#### Inline: +```python +from dataclasses import make_dataclass + = make_dataclass('', ) + = make_dataclass('', ) + = ('', [, ]) +``` + +#### Rest of type annotations (CPython interpreter ignores them all): +```python +def func(: [= ]) -> : ... +: typing.List/Set/Iterable/Sequence/Optional[] +: typing.Dict/Tuple/Union[, ...] +``` + +### Slots +**Mechanism that restricts objects to attributes listed in 'slots' and significantly reduces their memory footprint.** + +```python +class MyClassWithSlots: + __slots__ = ['a'] + def __init__(self): + self.a = 1 +``` + +### Copy +```python +from copy import copy, deepcopy + = copy() + = deepcopy() +``` + +**[🔼Back to Top](#content-outlines)** + +Duck Types +---------- +**A duck type is an implicit type that prescribes a set of special methods. Any object that has those methods defined is considered a member of that duck type.** + +### Comparable +* **If eq() method is not overridden, it returns `'id(self) == id(other)'`, which is the same as `'self is other'`.** +* **That means all objects compare not equal by default.** +* **Only the left side object has eq() method called, unless it returns NotImplemented, in which case the right object is consulted. False is returned if both return NotImplemented.** +* **Ne() automatically works on any object that has eq() defined.** + +```python +class MyComparable: + def __init__(self, a): + self.a = a + def __eq__(self, other): + if isinstance(other, type(self)): + return self.a == other.a + return NotImplemented +``` + +### Hashable +* **Hashable object needs both hash() and eq() methods and its hash value should never change.** +* **Hashable objects that compare equal must have the same hash value, meaning default hash() that returns `'id(self)'` will not do.** +* **That is why Python automatically makes classes unhashable if you only implement eq().** + +```python +class MyHashable: + def __init__(self, a): + self._a = a + @property + def a(self): + return self._a + def __eq__(self, other): + if isinstance(other, type(self)): + return self.a == other.a + return NotImplemented + def __hash__(self): + return hash(self.a) +``` + +### Sortable +* **With 'total_ordering' decorator, you only need to provide eq() and one of lt(), gt(), le() or ge() special methods and the rest will be automatically generated.** +* **Functions sorted() and min() only require lt() method, while max() only requires gt(). However, it is best to define them all so that confusion doesn't arise in other contexts.** +* **When two lists, strings or dataclasses are compared, their values get compared in order until a pair of unequal values is found. The comparison of this two values is then returned. The shorter sequence is considered smaller in case of all values being equal.** + +```python +from functools import total_ordering + +@total_ordering +class MySortable: + def __init__(self, a): + self.a = a + def __eq__(self, other): + if isinstance(other, type(self)): + return self.a == other.a + return NotImplemented + def __lt__(self, other): + if isinstance(other, type(self)): + return self.a < other.a + return NotImplemented +``` + +### Iterator +* **Any object that has methods next() and iter() is an iterator.** +* **Next() should return next item or raise StopIteration.** +* **Iter() should return 'self'.** +```python +class Counter: + def __init__(self): + self.i = 0 + def __next__(self): + self.i += 1 + return self.i + def __iter__(self): + return self +``` + +```python +>>> counter = Counter() +>>> next(counter), next(counter), next(counter) +(1, 2, 3) +``` + +#### Python has many different iterator objects: +* **Sequence iterators returned by the [iter()](#iterator) function, such as list\_iterator and set\_iterator.** +* **Objects returned by the [itertools](#itertools) module, such as count, repeat and cycle.** +* **Generators returned by the [generator functions](#generator) and [generator expressions](#comprehensions).** +* **File objects returned by the [open()](#open) function, etc.** + +### Callable +* **All functions and classes have a call() method, hence are callable.** +* **When this cheatsheet uses `''` as an argument, it actually means `''`.** +```python +class Counter: + def __init__(self): + self.i = 0 + def __call__(self): + self.i += 1 + return self.i +``` + +```python +>>> counter = Counter() +>>> counter(), counter(), counter() +(1, 2, 3) +``` + +### Context Manager +* **Enter() should lock the resources and optionally return an object.** +* **Exit() should release the resources.** +* **Any exception that happens inside the with block is passed to the exit() method.** +* **If it wishes to suppress the exception it must return a true value.** +```python +class MyOpen: + def __init__(self, filename): + self.filename = filename + def __enter__(self): + self.file = open(self.filename) + return self.file + def __exit__(self, exc_type, exception, traceback): + self.file.close() +``` + +```python +>>> with open('test.txt', 'w') as file: +... file.write('Hello World!') +>>> with MyOpen('test.txt') as file: +... print(file.read()) +Hello World! +``` + + +Iterable Duck Types +------------------- +### Iterable +* **Only required method is iter(). It should return an iterator of object's items.** +* **Contains() automatically works on any object that has iter() defined.** +```python +class MyIterable: + def __init__(self, a): + self.a = a + def __iter__(self): + return iter(self.a) + def __contains__(self, el): + return el in self.a +``` + +```python +>>> obj = MyIterable([1, 2, 3]) +>>> [el for el in obj] +[1, 2, 3] +>>> 1 in obj +True +``` + +### Collection +* **Only required methods are iter() and len(). Len() should return the number of items.** +* **This cheatsheet actually means `''` when it uses `''`.** +* **I chose not to use the name 'iterable' because it sounds scarier and more vague than 'collection'. The only drawback of this decision is that a reader could think a certain function doesn't accept iterators when it does, since iterators are the only built-in objects that are iterable but are not collections.** +```python +class MyCollection: + def __init__(self, a): + self.a = a + def __iter__(self): + return iter(self.a) + def __contains__(self, el): + return el in self.a + def __len__(self): + return len(self.a) +``` + +### Sequence +* **Only required methods are len() and getitem().** +* **Getitem() should return an item at the passed index or raise IndexError.** +* **Iter() and contains() automatically work on any object that has getitem() defined.** +* **Reversed() automatically works on any object that has len() and getitem() defined.** +```python +class MySequence: + def __init__(self, a): + self.a = a + def __iter__(self): + return iter(self.a) + def __contains__(self, el): + return el in self.a + def __len__(self): + return len(self.a) + def __getitem__(self, i): + return self.a[i] + def __reversed__(self): + return reversed(self.a) +``` + +#### Discrepancies between glossary definitions and abstract base classes: +* **Glossary defines iterable as any object with iter() or getitem() and sequence as any object with getitem() and len(). It does not define collection.** +* **Passing ABC Iterable to isinstance() or issubclass() checks whether object/class has method iter(), while ABC Collection checks for iter(), contains() and len().** + +### ABC Sequence +* **It's a richer interface than the basic sequence.** +* **Extending it generates iter(), contains(), reversed(), index() and count().** +* **Unlike `'abc.Iterable'` and `'abc.Collection'`, it is not a duck type. That is why `'issubclass(MySequence, abc.Sequence)'` would return False even if MySequence had all the methods defined. It however recognizes list, tuple, range, str, bytes, bytearray, memoryview and deque, because they are registered as Sequence's virtual subclasses.** +```python +from collections import abc + +class MyAbcSequence(abc.Sequence): + def __init__(self, a): + self.a = a + def __len__(self): + return len(self.a) + def __getitem__(self, i): + return self.a[i] +``` + +#### Table of required and automatically available special methods: +```text ++------------+------------+------------+------------+--------------+ +| | Iterable | Collection | Sequence | abc.Sequence | ++------------+------------+------------+------------+--------------+ +| iter() | REQ | REQ | Yes | Yes | +| contains() | Yes | Yes | Yes | Yes | +| len() | | REQ | REQ | REQ | +| getitem() | | | REQ | REQ | +| reversed() | | | Yes | Yes | +| index() | | | | Yes | +| count() | | | | Yes | ++------------+------------+------------+------------+--------------+ +``` +* **Other ABCs that generate missing methods are: MutableSequence, Set, MutableSet, Mapping and MutableMapping.** +* **Names of their required methods are stored in `'.__abstractmethods__'`.** + +**[🔼Back to Top](#content-outlines)** + +Enum +---- +```python +from enum import Enum, auto +``` + +```python +class (Enum): + = + = , + = auto() +``` +* **If there are no numeric values before auto(), it returns 1.** +* **Otherwise it returns an increment of the last numeric value.** + +```python + = . # Returns a member. + = [''] # Returns a member or raises KeyError. + = () # Returns a member or raises ValueError. + = .name # Returns member's name. + = .value # Returns member's value. +``` + +```python +list_of_members = list() +member_names = [a.name for a in ] +member_values = [a.value for a in ] +random_member = random.choice(list()) +``` + +```python +def get_next_member(member): + members = list(member.__class__) + index = (members.index(member) + 1) % len(members) + return members[index] +``` + +### Inline +```python +Cutlery = Enum('Cutlery', 'fork knife spoon') +Cutlery = Enum('Cutlery', ['fork', 'knife', 'spoon']) +Cutlery = Enum('Cutlery', {'fork': 1, 'knife': 2, 'spoon': 3}) +``` + +#### User-defined functions cannot be values, so they must be wrapped: +```python +from functools import partial +LogicOp = Enum('LogicOp', {'AND': partial(lambda l, r: l and r), + 'OR': partial(lambda l, r: l or r)}) +``` +* **Member names are in all caps because trying to access a member that is named after a reserved keyword raises SyntaxError.** + +**[🔼Back to Top](#content-outlines)** + +Exceptions +---------- +```python +try: + +except : + +``` + +### Complex Example +```python +try: + +except : + +except : + +else: + +finally: + +``` +* **Code inside the `'else'` block will only be executed if `'try'` block had no exceptions.** +* **Code inside the `'finally'` block will always be executed (unless a signal is received).** + +### Catching Exceptions +```python +except : ... +except as : ... +except (, [...]): ... +except (, [...]) as : ... +``` +* **Also catches subclasses of the exception.** +* **Use `'traceback.print_exc()'` to print the error message to stderr.** +* **Use `'print()'` to print just the cause of the exception (its arguments).** +* **Use `'logging.exception()'` to log the exception.** + +### Raising Exceptions +```python +raise +raise () +raise ( [, ...]) +``` + +#### Re-raising caught exception: +```python +except as : + ... + raise +``` + +### Exception Object +```python +arguments = .args +exc_type = .__class__ +filename = .__traceback__.tb_frame.f_code.co_filename +func_name = .__traceback__.tb_frame.f_code.co_name +line = linecache.getline(filename, .__traceback__.tb_lineno) +traceback = ''.join(traceback.format_tb(.__traceback__)) +error_msg = ''.join(traceback.format_exception(exc_type, , .__traceback__)) +``` + +### Built-in Exceptions +```text +BaseException + +-- SystemExit # Raised by the sys.exit() function. + +-- KeyboardInterrupt # Raised when the user hits the interrupt key (ctrl-c). + +-- Exception # User-defined exceptions should be derived from this class. + +-- ArithmeticError # Base class for arithmetic errors. + | +-- ZeroDivisionError # Raised when dividing by zero. + +-- AssertionError # Raised by `assert ` if expression returns false value. + +-- AttributeError # Raised when an attribute is missing. + +-- EOFError # Raised by input() when it hits end-of-file condition. + +-- LookupError # Raised when a look-up on a collection fails. + | +-- IndexError # Raised when a sequence index is out of range. + | +-- KeyError # Raised when a dictionary key or set element is missing. + +-- MemoryError # Out of memory. Could be too late to start deleting vars. + +-- NameError # Raised when an object is missing. + +-- OSError # Errors such as “file not found” or “disk full” (see Open). + | +-- FileNotFoundError # When a file or directory is requested but doesn't exist. + +-- RuntimeError # Raised by errors that don't fall into other categories. + | +-- RecursionError # Raised when the maximum recursion depth is exceeded. + +-- StopIteration # Raised by next() when run on an empty iterator. + +-- TypeError # Raised when an argument is of wrong type. + +-- ValueError # When an argument is of right type but inappropriate value. + +-- UnicodeError # Raised when encoding/decoding strings to/from bytes fails. +``` + +#### Collections and their exceptions: +```text ++-----------+------------+------------+------------+ +| | List | Set | Dict | ++-----------+------------+------------+------------+ +| getitem() | IndexError | | KeyError | +| pop() | IndexError | KeyError | KeyError | +| remove() | ValueError | KeyError | | +| index() | ValueError | | | ++-----------+------------+------------+------------+ +``` + +#### Useful built-in exceptions: +```python +raise TypeError('Argument is of wrong type!') +raise ValueError('Argument is of right type but inappropriate value!') +raise RuntimeError('None of above!') +``` + +### User-defined Exceptions +```python +class MyError(Exception): pass +class MyInputError(MyError): pass +``` + +**[🔼Back to Top](#content-outlines)** + +Exit +---- +**Exits the interpreter by raising SystemExit exception.** +```python +import sys +sys.exit() # Exits with exit code 0 (success). +sys.exit() # Prints to stderr and exits with 1. +sys.exit() # Exits with passed exit code. +``` + +**[🔼Back to Top](#content-outlines)** + +Print +----- +```python +print(, ..., sep=' ', end='\n', file=sys.stdout, flush=False) +``` +* **Use `'file=sys.stderr'` for messages about errors.** +* **Use `'flush=True'` to forcibly flush the stream.** + +### Pretty Print +```python +from pprint import pprint +pprint(, width=80, depth=None, compact=False, sort_dicts=True) +``` +* **Levels deeper than 'depth' get replaced by '...'.** + +**[🔼Back to Top](#content-outlines)** + +Input +----- +**Reads a line from user input or pipe if present.** + +```python + = input(prompt=None) +``` +* **Trailing newline gets stripped.** +* **Prompt string is printed to the standard output before reading input.** +* **Raises EOFError when user hits EOF (ctrl-d/ctrl-z⏎) or input stream gets exhausted.** + +**[🔼Back to Top](#content-outlines)** + +Command Line Arguments +---------------------- +```python +import sys +scripts_path = sys.argv[0] +arguments = sys.argv[1:] +``` + +### Argument Parser +```python +from argparse import ArgumentParser, FileType +p = ArgumentParser(description=) +p.add_argument('-', '--', action='store_true') # Flag. +p.add_argument('-', '--', type=) # Option. +p.add_argument('', type=, nargs=1) # First argument. +p.add_argument('', type=, nargs='+') # Remaining arguments. +p.add_argument('', type=, nargs='*') # Optional arguments. +args = p.parse_args() # Exits on error. +value = args. +``` + +* **Use `'help='` to set argument description that will be displayed in help message.** +* **Use `'default='` to set the default value.** +* **Use `'type=FileType()'` for files. Accepts 'encoding', but 'newline' is None.** + +**[🔼Back to Top](#content-outlines)** + +Open +---- +**Opens the file and returns a corresponding file object.** + +```python + = open(, mode='r', encoding=None, newline=None) +``` +* **`'encoding=None'` means that the default encoding is used, which is platform dependent. Best practice is to use `'encoding="utf-8"'` whenever possible.** +* **`'newline=None'` means all different end of line combinations are converted to '\n' on read, while on write all '\n' characters are converted to system's default line separator.** +* **`'newline=""'` means no conversions take place, but input is still broken into chunks by readline() and readlines() on every '\n', '\r' and '\r\n'.** + +### Modes +* **`'r'` - Read (default).** +* **`'w'` - Write (truncate).** +* **`'x'` - Write or fail if the file already exists.** +* **`'a'` - Append.** +* **`'w+'` - Read and write (truncate).** +* **`'r+'` - Read and write from the start.** +* **`'a+'` - Read and write from the end.** +* **`'t'` - Text mode (default).** +* **`'b'` - Binary mode (`'br'`, `'bw'`, `'bx'`, …).** + +### Exceptions +* **`'FileNotFoundError'` can be raised when reading with `'r'` or `'r+'`.** +* **`'FileExistsError'` can be raised when writing with `'x'`.** +* **`'IsADirectoryError'` and `'PermissionError'` can be raised by any.** +* **`'OSError'` is the parent class of all listed exceptions.** + +### File Object +```python +.seek(0) # Moves to the start of the file. +.seek(offset) # Moves 'offset' chars/bytes from the start. +.seek(0, 2) # Moves to the end of the file. +.seek(±offset, ) # Anchor: 0 start, 1 current position, 2 end. +``` + +```python + = .read(size=-1) # Reads 'size' chars/bytes or until EOF. + = .readline() # Returns a line or empty string/bytes on EOF. + = .readlines() # Returns a list of remaining lines. + = next() # Returns a line using buffer. Do not mix. +``` + +```python +.write() # Writes a string or bytes object. +.writelines() # Writes a coll. of strings or bytes objects. +.flush() # Flushes write buffer. Runs every 4096/8192 B. +``` +* **Methods do not add or strip trailing newlines, even writelines().** + +### Read Text from File +```python +def read_file(filename): + with open(filename, encoding='utf-8') as file: + return file.readlines() +``` + +### Write Text to File +```python +def write_to_file(filename, text): + with open(filename, 'w', encoding='utf-8') as file: + file.write(text) +``` + +**[🔼Back to Top](#content-outlines)** + +Paths +----- +```python +from os import getcwd, path, listdir, scandir +from glob import glob +``` + +```python + = getcwd() # Returns the current working directory. + = path.join(, ...) # Joins two or more pathname components. + = path.abspath() # Returns absolute path. +``` + +```python + = path.basename() # Returns final component of the path. + = path.dirname() # Returns path without the final component. + = path.splitext() # Splits on last period of the final component. +``` + +```python + = listdir(path='.') # Returns filenames located at path. + = glob('') # Returns paths matching the wildcard pattern. +``` + +```python + = path.exists() # Or: .exists() + = path.isfile() # Or: .is_file() + = path.isdir() # Or: .is_dir() +``` + +```python + = os.stat() # Or: .stat() + = .st_mtime/st_size/… # Modification time, size in bytes, … +``` + +### DirEntry +**Unlike listdir(), scandir() returns DirEntry objects that cache isfile, isdir and on Windows also stat information, thus significantly increasing the performance of code that requires it.** + +```python + = scandir(path='.') # Returns DirEntry objects located at path. + = .path # Returns whole path as a string. + = .name # Returns final component as a string. + = open() # Opens the file and returns a file object. +``` + +### Path Object +```python +from pathlib import Path +``` + +```python + = Path( [, ...]) # Accepts strings, Paths and DirEntry objects. + = / [/ ...] # First or second path must be a Path object. +``` + +```python + = Path() # Returns relative cwd. Also Path('.'). + = Path.cwd() # Returns absolute cwd. Also Path().resolve(). + = Path.home() # Returns user's home directory (absolute). + = Path(__file__).resolve() # Returns script's path if cwd wasn't changed. +``` + +```python + = .parent # Returns Path without the final component. + = .name # Returns final component as a string. + = .stem # Returns final component without extension. + = .suffix # Returns final component's extension. + = .parts # Returns all components as strings. +``` + +```python + = .iterdir() # Returns directory contents as Path objects. + = .glob('') # Returns Paths matching the wildcard pattern. +``` + +```python + = str() # Returns path as a string. + = open() # Also .read/write_text/bytes(). +``` + +**[🔼Back to Top](#content-outlines)** + +OS Commands +----------- +```python +import os, shutil, subprocess +``` + +```python +os.chdir() # Changes the current working directory. +os.mkdir(, mode=0o777) # Creates a directory. Permissions are in octal. +os.makedirs(, mode=0o777) # Creates all path's dirs. Also: `exist_ok=False`. +``` + +```python +shutil.copy(from, to) # Copies the file. 'to' can exist or be a dir. +shutil.copytree(from, to) # Copies the directory. 'to' must not exist. +``` + + +```python +os.rename(from, to) # Renames/moves the file or directory. +os.replace(from, to) # Same, but overwrites 'to' if it exists. +``` + +```python +os.remove() # Deletes the file. +os.rmdir() # Deletes the empty directory. +shutil.rmtree() # Deletes the directory. +``` +* **Paths can be either strings, Paths or DirEntry objects.** +* **Functions report OS related errors by raising either OSError or one of its [subclasses](#exceptions-1).** + +### Shell Commands +```python + = os.popen('') # Executes command in sh/cmd. Returns its stdout pipe. + = .read(size=-1) # Reads 'size' chars or until EOF. Also readline/s(). + = .close() # Closes the pipe. Returns None on success. +``` + +#### Sends '1 + 1' to the basic calculator and captures its output: +```python +>>> subprocess.run('bc', input='1 + 1\n', capture_output=True, text=True) +CompletedProcess(args='bc', returncode=0, stdout='2\n', stderr='') +``` + +#### Sends test.in to the basic calculator running in standard mode and saves its output to test.out: +```python +>>> from shlex import split +>>> os.popen('echo 1 + 1 > test.in') +>>> subprocess.run(split('bc -s'), stdin=open('test.in'), stdout=open('test.out', 'w')) +CompletedProcess(args=['bc', '-s'], returncode=0) +>>> open('test.out').read() +'2\n' +``` + +**[🔼Back to Top](#content-outlines)** + +JSON +---- +**Text file format for storing collections of strings and numbers.** + +```python +import json + = json.dumps() # Converts object to JSON string. + = json.loads() # Converts JSON string to object. +``` + +### Read Object from JSON File +```python +def read_json_file(filename): + with open(filename, encoding='utf-8') as file: + return json.load(file) +``` + +### Write Object to JSON File +```python +def write_to_json_file(filename, an_object): + with open(filename, 'w', encoding='utf-8') as file: + json.dump(an_object, file, ensure_ascii=False, indent=2) +``` + +**[🔼Back to Top](#content-outlines)** + +Pickle +------ +**Binary file format for storing Python objects.** + +```python +import pickle + = pickle.dumps() # Converts object to bytes object. + = pickle.loads() # Converts bytes object to object. +``` + +### Read Object from File +```python +def read_pickle_file(filename): + with open(filename, 'rb') as file: + return pickle.load(file) +``` + +### Write Object to File +```python +def write_to_pickle_file(filename, an_object): + with open(filename, 'wb') as file: + pickle.dump(an_object, file) +``` + +**[🔼Back to Top](#content-outlines)** + +CSV +--- +**Text file format for storing spreadsheets.** + +```python +import csv +``` + +### Read +```python + = csv.reader() # Also: `dialect='excel', delimiter=','`. + = next() # Returns next row as a list of strings. + = list() # Returns a list of remaining rows. +``` +* **File must be opened with a `'newline=""'` argument, or newlines embedded inside quoted fields will not be interpreted correctly!** +* **To print the spreadsheet to the console use [Tabulate](#table) library.** +* **For XML and binary Excel files (xlsx, xlsm and xlsb) use [Pandas](#dataframe-plot-encode-decode) library.** + +### Write +```python + = csv.writer() # Also: `dialect='excel', delimiter=','`. +.writerow() # Encodes objects using `str()`. +.writerows() # Appends multiple rows. +``` +* **File must be opened with a `'newline=""'` argument, or '\r' will be added in front of every '\n' on platforms that use '\r\n' line endings!** + +### Parameters +* **`'dialect'` - Master parameter that sets the default values. String or a Dialect object.** +* **`'delimiter'` - A one-character string used to separate fields.** +* **`'quotechar'` - Character for quoting fields that contain special characters.** +* **`'doublequote'` - Whether quotechars inside fields are/get doubled or escaped.** +* **`'skipinitialspace'` - Is space character at the start of the field stripped by the reader.** +* **`'lineterminator'` - How writer terminates rows. Reader is hardcoded to '\n', '\r', '\r\n'.** +* **`'quoting'` - 0: As necessary, 1: All, 2: All but numbers which are read as floats, 3: None.** +* **`'escapechar'` - Character for escaping quotechars if doublequote is False.** + +### Dialects +```text ++------------------+--------------+--------------+--------------+ +| | excel | excel-tab | unix | ++------------------+--------------+--------------+--------------+ +| delimiter | ',' | '\t' | ',' | +| quotechar | '"' | '"' | '"' | +| doublequote | True | True | True | +| skipinitialspace | False | False | False | +| lineterminator | '\r\n' | '\r\n' | '\n' | +| quoting | 0 | 0 | 1 | +| escapechar | None | None | None | ++------------------+--------------+--------------+--------------+ +``` + +### Read Rows from CSV File +```python +def read_csv_file(filename, dialect='excel'): + with open(filename, encoding='utf-8', newline='') as file: + return list(csv.reader(file, dialect)) +``` + +### Write Rows to CSV File +```python +def write_to_csv_file(filename, rows, dialect='excel'): + with open(filename, 'w', encoding='utf-8', newline='') as file: + writer = csv.writer(file, dialect) + writer.writerows(rows) +``` + +**[🔼Back to Top](#content-outlines)** + +SQLite +------ +**A server-less database engine that stores each database into a separate file.** + +```python +import sqlite3 + = sqlite3.connect() # Opens existing or new file. Also ':memory:'. +.close() # Closes the connection. +``` + +### Read +```python + = .execute('') # Can raise a subclass of sqlite3.Error. + = .fetchone() # Returns next row. Also next(). + = .fetchall() # Returns remaining rows. Also list(). +``` + +### Write +```python +.execute('') # Can raise a subclass of sqlite3.Error. +.commit() # Saves all changes since the last commit. +.rollback() # Discards all changes since the last commit. +``` + +#### Or: +```python +with : # Exits the block with commit() or rollback(), + .execute('') # depending on whether any exception occurred. +``` + +### Placeholders +```python +.execute('', ) # Replaces '?'s in query with values. +.execute('', ) # Replaces ':'s with values. +.executemany('', ) # Runs execute() multiple times. +``` +* **Passed values can be of type str, int, float, bytes, None, bool, datetime.date or datetime.datetime.** +* **Bools will be stored and returned as ints and dates as [ISO formatted strings](#encode).** + +### Example +**Values are not actually saved in this example because `'conn.commit()'` is omitted!** +```python +>>> conn = sqlite3.connect('test.db') +>>> conn.execute('CREATE TABLE person (person_id INTEGER PRIMARY KEY, name, height)') +>>> conn.execute('INSERT INTO person VALUES (NULL, ?, ?)', ('Jean-Luc', 187)).lastrowid +1 +>>> conn.execute('SELECT * FROM person').fetchall() +[(1, 'Jean-Luc', 187)] +``` + +### SqlAlchemy +```python +# $ pip3 install sqlalchemy +from sqlalchemy import create_engine, text + = create_engine('').connect() # Url: 'dialect://user:password@host/dbname'. + = .connect() # Creates a connection. Also .close(). + = .execute(text(''), …) # Replaces ':'s with keyword arguments. +with .begin(): ... # Exits the block with commit or rollback. +``` + +```text ++------------+--------------+-----------+-----------------------------------+ +| Dialects | pip3 install | import | Dependencies | ++------------+--------------+-----------+-----------------------------------+ +| mysql | mysqlclient | MySQLdb | www.pypi.org/project/mysqlclient | +| postgresql | psycopg2 | psycopg2 | www.psycopg.org/docs/install.html | +| mssql | pyodbc | pyodbc | apt install g++ unixodbc-dev | +| oracle | cx_oracle | cx_Oracle | Oracle Instant Client | ++------------+--------------+-----------+-----------------------------------+ +``` + +**[🔼Back to Top](#content-outlines)** + +Bytes +----- +**Bytes object is an immutable sequence of single bytes. Mutable version is called bytearray.** + +```python + = b'' # Only accepts ASCII characters and \x00-\xff. + = [] # Returns an int in range from 0 to 255. + = [] # Returns bytes even if it has only one element. + = .join() # Joins elements using bytes as a separator. +``` + +### Encode +```python + = bytes() # Ints must be in range from 0 to 255. + = bytes(, 'utf-8') # Or: .encode('utf-8') + = .to_bytes(n_bytes, …) # `byteorder='little/big', signed=False`. + = bytes.fromhex('') # Hex pairs can be separated by whitespaces. +``` + +### Decode +```python + = list() # Returns ints in range from 0 to 255. + = str(, 'utf-8') # Or: .decode('utf-8') + = int.from_bytes(, …) # `byteorder='little/big', signed=False`. +'' = .hex() # Returns hex pairs. Accepts `sep=`. +``` + +### Read Bytes from File +```python +def read_bytes(filename): + with open(filename, 'rb') as file: + return file.read() +``` + +### Write Bytes to File +```python +def write_bytes(filename, bytes_obj): + with open(filename, 'wb') as file: + file.write(bytes_obj) +``` + +**[🔼Back to Top](#content-outlines)** + +Struct +------ +* **Module that performs conversions between a sequence of numbers and a bytes object.** +* **System’s type sizes, byte order, and alignment rules are used by default.** + +```python +from struct import pack, unpack + = pack('', [, ...]) # Packages arguments into bytes object. + = unpack('', ) # Use iter_unpack() for iterator of tuples. +``` + +```python +>>> pack('>hhl', 1, 2, 3) +b'\x00\x01\x00\x02\x00\x00\x00\x03' +>>> unpack('>hhl', b'\x00\x01\x00\x02\x00\x00\x00\x03') +(1, 2, 3) +``` + +### Format +#### For standard type sizes and manual alignment (padding) start format string with: +* **`'='` - System's byte order (usually little-endian).** +* **`'<'` - Little-endian.** +* **`'>'` - Big-endian (also `'!'`).** + +#### Besides numbers, pack() and unpack() also support bytes objects as part of the sequence: +* **`'c'` - A bytes object with a single element. For pad byte use `'x'`.** +* **`'s'` - A bytes object with n elements.** + +#### Integer types. Use a capital letter for unsigned type. Minimum and standard sizes are in brackets: +* **`'b'` - char (1/1)** +* **`'h'` - short (2/2)** +* **`'i'` - int (2/4)** +* **`'l'` - long (4/4)** +* **`'q'` - long long (8/8)** + +#### Floating point types: +* **`'f'` - float (4/4)** +* **`'d'` - double (8/8)** + +**[🔼Back to Top](#content-outlines)** + +Array +----- +**List that can only hold numbers of a predefined type. Available types and their minimum sizes in bytes are listed above. Sizes and byte order are always determined by the system.** + +```python +from array import array + = array('', ) # Array from collection of numbers. + = array('', ) # Array from bytes object. + = array('', ) # Treats array as a sequence of numbers. + = bytes() # Or: .tobytes() +.write() # Writes array to the binary file. +``` + +**[🔼Back to Top](#content-outlines)** + +Memory View +----------- +* **A sequence object that points to the memory of another object.** +* **Each element can reference a single or multiple consecutive bytes, depending on format.** +* **Order and number of elements can be changed with slicing.** +* **Casting only works between char and other types and uses system's sizes.** +* **Byte order is always determined by the system.** + +```python + = memoryview() # Immutable if bytes, else mutable. + = [] # Returns an int or a float. + = [] # Mview with rearranged elements. + = .cast('') # Casts memoryview to the new format. +.release() # Releases the object's memory buffer. +``` + +### Decode +```python + = bytes() # Returns a new bytes object. + = .join() # Joins mviews using bytes object as sep. + = array('', ) # Treats mview as a sequence of numbers. +.write() # Writes mview to the binary file. +``` + +```python + = list() # Returns a list of ints or floats. + = str(, 'utf-8') # Treats mview as a bytes object. + = int.from_bytes(, …) # `byteorder='little/big', signed=False`. +'' = .hex() # Treats mview as a bytes object. +``` + +**[🔼Back to Top](#content-outlines)** + +Deque +----- +**A thread-safe list with efficient appends and pops from either side. Pronounced "deck".** + +```python +from collections import deque + = deque(, maxlen=None) +``` + +```python +.appendleft() # Opposite element is dropped if full. +.extendleft() # Collection gets reversed. + = .popleft() # Raises IndexError if empty. +.rotate(n=1) # Rotates elements to the right. +``` + +**[🔼Back to Top](#content-outlines)** + +Threading +--------- +* **CPython interpreter can only run a single thread at a time.** +* **That is why using multiple threads won't result in a faster execution, unless at least one of the threads contains an I/O operation.** +```python +from threading import Thread, RLock, Semaphore, Event, Barrier +from concurrent.futures import ThreadPoolExecutor +``` + +### Thread +```python + = Thread(target=) # Use `args=` to set the arguments. +.start() # Starts the thread. + = .is_alive() # Checks if the thread has finished executing. +.join() # Waits for the thread to finish. +``` +* **Use `'kwargs='` to pass keyword arguments to the function.** +* **Use `'daemon=True'`, or the program will not be able to exit while the thread is alive.** + +### Lock +```python + = RLock() # Lock that can only be released by acquirer. +.acquire() # Waits for the lock to be available. +.release() # Makes the lock available again. +``` + +#### Or: +```python +with : # Enters the block by calling acquire(), + ... # and exits it with release(). +``` + +### Semaphore, Event, Barrier +```python + = Semaphore(value=1) # Lock that can be acquired by 'value' threads. + = Event() # Method wait() blocks until set() is called. + = Barrier(n_times) # Wait() blocks until it's called n_times. +``` + +### Thread Pool Executor +* **Object that manages thread execution.** +* **An object with the same interface called ProcessPoolExecutor provides true parallelism by running a separate interpreter in each process. All arguments must be [pickable](#pickle).** + +```python + = ThreadPoolExecutor(max_workers=None) # Or: `with ThreadPoolExecutor() as : …` +.shutdown(wait=True) # Blocks until all threads finish executing. +``` + +```python + = .map(, , ...) # A multithreaded and non-lazy map(). + = .submit(, , ...) # Starts a thread and returns its Future object. + = .done() # Checks if the thread has finished executing. + = .result() # Waits for thread to finish and returns result. +``` + +### Queue +**A thread-safe FIFO queue. For LIFO queue use LifoQueue.** +```python +from queue import Queue + = Queue(maxsize=0) +``` + +```python +.put() # Blocks until queue stops being full. +.put_nowait() # Raises queue.Full exception if full. + = .get() # Blocks until queue stops being empty. + = .get_nowait() # Raises queue.Empty exception if empty. +``` + +**[🔼Back to Top](#content-outlines)** + +Operator +-------- +**Module of functions that provide the functionality of operators.** +```python +import operator as op + = op.add/sub/mul/truediv/floordiv/mod(, ) # +, -, *, /, //, % + = op.and_/or_/xor(, ) # &, |, ^ + = op.eq/ne/lt/le/gt/ge(, ) # ==, !=, <, <=, >, >= + = op.itemgetter/attrgetter/methodcaller() # [index/key], .name, .name() +``` + +```python +elementwise_sum = map(op.add, list_a, list_b) +sorted_by_second = sorted(, key=op.itemgetter(1)) +sorted_by_both = sorted(, key=op.itemgetter(1, 0)) +product_of_elems = functools.reduce(op.mul, ) +union_of_sets = functools.reduce(op.or_, ) +first_element = op.methodcaller('pop', 0)() +``` +* **Binary operators require objects to have and(), or(), xor() and invert() special methods, unlike logical operators that work on all types of objects.** +* **Also: `' = &|^ '` and `' = &|^ '`.** + +**[🔼Back to Top](#content-outlines)** + +Introspection +------------- +**Inspecting code at runtime.** + +### Variables +```python + = dir() # Names of local variables (incl. functions). + = vars() # Dict of local variables. Also locals(). + = globals() # Dict of global variables. +``` + +### Attributes +```python + = dir() # Names of object's attributes (incl. methods). + = vars() # Dict of writable attributes. Also .__dict__. + = hasattr(, '') # Checks if getattr() raises an AttributeError. +value = getattr(, '') # Raises AttributeError if attribute is missing. +setattr(, '', value) # Only works on objects with '__dict__' attribute. +delattr(, '') # Same. Also `del .`. +``` + +### Parameters +```python + = inspect.signature() # Function's Signature object. + = .parameters # Dict of Parameter objects. + = .kind # Member of ParameterKind enum. + = .default # Default value or .empty. + = .annotation # Type or .empty. +``` + +**[🔼Back to Top](#content-outlines)** + +Metaprogramming +--------------- +**Code that generates code.** + +### Type +**Type is the root class. If only passed an object it returns its type (class). Otherwise it creates a new class.** + +```python + = type('', , ) +``` + +```python +>>> Z = type('Z', (), {'a': 'abcde', 'b': 12345}) +>>> z = Z() +``` + +### Meta Class +**A class that creates classes.** + +```python +def my_meta_class(name, parents, attrs): + attrs['a'] = 'abcde' + return type(name, parents, attrs) +``` + +#### Or: +```python +class MyMetaClass(type): + def __new__(cls, name, parents, attrs): + attrs['a'] = 'abcde' + return type.__new__(cls, name, parents, attrs) +``` +* **New() is a class method that gets called before init(). If it returns an instance of its class, then that instance gets passed to init() as a 'self' argument.** +* **It receives the same arguments as init(), except for the first one that specifies the desired type of the returned instance (MyMetaClass in our case).** +* **Like in our case, new() can also be called directly, usually from a new() method of a child class (**`def __new__(cls): return super().__new__(cls)`**).** +* **The only difference between the examples above is that my\_meta\_class() returns a class of type type, while MyMetaClass() returns a class of type MyMetaClass.** + +### Metaclass Attribute +**Right before a class is created it checks if it has the 'metaclass' attribute defined. If not, it recursively checks if any of his parents has it defined and eventually comes to type().** + +```python +class MyClass(metaclass=MyMetaClass): + b = 12345 +``` + +```python +>>> MyClass.a, MyClass.b +('abcde', 12345) +``` + +### Type Diagram +```python +type(MyClass) == MyMetaClass # MyClass is an instance of MyMetaClass. +type(MyMetaClass) == type # MyMetaClass is an instance of type. +``` + +```text ++-------------+-------------+ +| Classes | Metaclasses | ++-------------+-------------| +| MyClass --> MyMetaClass | +| | v | +| object -----> type <+ | +| | ^ +--+ | +| str ----------+ | ++-------------+-------------+ +``` + +### Inheritance Diagram +```python +MyClass.__base__ == object # MyClass is a subclass of object. +MyMetaClass.__base__ == type # MyMetaClass is a subclass of type. +``` + +```text ++-------------+-------------+ +| Classes | Metaclasses | ++-------------+-------------| +| MyClass | MyMetaClass | +| v | v | +| object <----- type | +| ^ | | +| str | | ++-------------+-------------+ +``` + +**[🔼Back to Top](#content-outlines)** + +Eval +---- +```python +>>> from ast import literal_eval +>>> literal_eval('[1, 2, 3]') +[1, 2, 3] +>>> literal_eval('1 + 2') +ValueError: malformed node or string +``` + +**[🔼Back to Top](#content-outlines)** + +Coroutines +---------- +* **Coroutines have a lot in common with threads, but unlike threads, they only give up control when they call another coroutine and they don’t use as much memory.** +* **Coroutine definition starts with `'async'` and its call with `'await'`.** +* **`'asyncio.run()'` is the main entry point for asynchronous programs.** +* **Functions wait(), gather() and as_completed() start multiple coroutines at the same time.** +* **Asyncio module also provides its own [Queue](#queue), [Event](#semaphore-event-barrier), [Lock](#lock) and [Semaphore](#semaphore-event-barrier) classes.** + +#### Runs a terminal game where you control an asterisk that must avoid numbers: + +```python +import asyncio, collections, curses, curses.textpad, enum, random + +P = collections.namedtuple('P', 'x y') # Position +D = enum.Enum('D', 'n e s w') # Direction +W, H = 15, 7 # Width, Height + +def main(screen): + curses.curs_set(0) # Makes cursor invisible. + screen.nodelay(True) # Makes getch() non-blocking. + asyncio.run(main_coroutine(screen)) # Starts running asyncio code. + +async def main_coroutine(screen): + moves = asyncio.Queue() + state = {'*': P(0, 0), **{id_: P(W//2, H//2) for id_ in range(10)}} + ai = [random_controller(id_, moves) for id_ in range(10)] + mvc = [human_controller(screen, moves), model(moves, state), view(state, screen)] + tasks = [asyncio.create_task(cor) for cor in ai + mvc] + await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED) + +async def random_controller(id_, moves): + while True: + d = random.choice(list(D)) + moves.put_nowait((id_, d)) + await asyncio.sleep(random.triangular(0.01, 0.65)) + +async def human_controller(screen, moves): + while True: + ch = screen.getch() + key_mappings = {258: D.s, 259: D.n, 260: D.w, 261: D.e} + if ch in key_mappings: + moves.put_nowait(('*', key_mappings[ch])) + await asyncio.sleep(0.005) + +async def model(moves, state): + while state['*'] not in (state[id_] for id_ in range(10)): + id_, d = await moves.get() + x, y = state[id_] + deltas = {D.n: P(0, -1), D.e: P(1, 0), D.s: P(0, 1), D.w: P(-1, 0)} + state[id_] = P((x + deltas[d].x) % W, (y + deltas[d].y) % H) + +async def view(state, screen): + offset = P(curses.COLS//2 - W//2, curses.LINES//2 - H//2) + while True: + screen.erase() + curses.textpad.rectangle(screen, offset.y-1, offset.x-1, offset.y+H, offset.x+W) + for id_, p in state.items(): + screen.addstr(offset.y + (p.y - state['*'].y + H//2) % H, + offset.x + (p.x - state['*'].x + W//2) % W, str(id_)) + await asyncio.sleep(0.005) + +if __name__ == '__main__': + curses.wrapper(main) +``` +
+ +**[🔼Back to Top](#content-outlines)** + +Libraries +========= + +Progress Bar +------------ +```python +# $ pip3 install tqdm +>>> from tqdm import tqdm +>>> from time import sleep +>>> for el in tqdm([1, 2, 3], desc='Processing'): +... sleep(1) +Processing: 100%|████████████████████| 3/3 [00:03<00:00, 1.00s/it] +``` + +**[🔼Back to Top](#content-outlines)** + +Plot +---- +```python +# $ pip3 install matplotlib +import matplotlib.pyplot as plt +plt.plot(, [, label=]) # Or: plt.plot() +plt.legend() # Adds a legend. +plt.savefig() # Saves the figure. +plt.show() # Displays the figure. +plt.clf() # Clears the figure. +``` + +**[🔼Back to Top](#content-outlines)** + +Table +----- +#### Prints a CSV file as an ASCII table: +```python +# $ pip3 install tabulate +import csv, tabulate +with open('test.csv', encoding='utf-8', newline='') as file: + rows = csv.reader(file) + header = next(rows) + table = tabulate.tabulate(rows, header) +print(table) +``` + +**[🔼Back to Top](#content-outlines)** + +Curses +------ +#### Runs a basic file explorer in the terminal: +```python +import curses, curses.ascii, os +from curses import A_REVERSE, KEY_DOWN, KEY_UP, KEY_LEFT, KEY_RIGHT, KEY_ENTER + +def main(screen): + ch, first, selected, paths = 0, 0, 0, os.listdir() + while ch != curses.ascii.ESC: + height, _ = screen.getmaxyx() + screen.erase() + for y, filename in enumerate(paths[first : first+height]): + screen.addstr(y, 0, filename, A_REVERSE * (selected == first + y)) + ch = screen.getch() + selected += (ch == KEY_DOWN) - (ch == KEY_UP) + selected = max(0, min(len(paths)-1, selected)) + first += (first <= selected - height) - (first > selected) + if ch in [KEY_LEFT, KEY_RIGHT, KEY_ENTER, 10, 13]: + new_dir = '..' if ch == KEY_LEFT else paths[selected] + if os.path.isdir(new_dir): + os.chdir(new_dir) + first, selected, paths = 0, 0, os.listdir() + +if __name__ == '__main__': + curses.wrapper(main) +``` + +**[🔼Back to Top](#content-outlines)** + +Logging +------- +```python +# $ pip3 install loguru +from loguru import logger +``` + +```python +logger.add('debug_{time}.log', colorize=True) # Connects a log file. +logger.add('error_{time}.log', level='ERROR') # Another file for errors or higher. +logger.('A logging message.') # Logs to file/s and prints to stderr. +``` +* **Levels: `'debug'`, `'info'`, `'success'`, `'warning'`, `'error'`, `'critical'`.** + +### Exceptions +**Exception description, stack trace and values of variables are appended automatically.** + +```python +try: + ... +except : + logger.exception('An error happened.') +``` + +### Rotation +**Argument that sets a condition when a new log file is created.** +```python +rotation=||| +``` +* **`''` - Max file size in bytes.** +* **`''` - Max age of a file.** +* **`'