├── BTBL ├── Firefox-updater ├── LICENSE ├── README.md ├── alert-bad-processes ├── alert-bad-processes-user ├── any2wav ├── audio-conf ├── avoid-vpn ├── avolume ├── boost-tuner ├── btc2usd ├── check_installed_rpms ├── check_installed_rpms_clean ├── clean_dnf ├── cpumode ├── cr ├── defrag ├── direct ├── dmesg-notify ├── dmesg-notify-user ├── findexe ├── firmware-updater.sh ├── gamma.sh ├── git-file-dates ├── git-pusher ├── ip-country ├── iperf3.service ├── macros ├── message_gui_user ├── metaclone ├── monitor_overclock ├── mouse-acceleration-toggle ├── nvidia-xfce-genmon ├── oneliners ├── par2create-batch ├── perfbias.sh ├── png2jxl ├── rescan_scsi ├── run_via_cgroup ├── ryzen-powersave.sh ├── tar_sorted ├── uniunpack ├── vbox_system_qt ├── watch_raw_io ├── watts_cpu ├── xsession-errors └── zip2date /BTBL: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # bluetooth battery level 4 | # 2022-06-28 01:27:46 - fixes 5 | # 2022-08-01 23:12:38 - show emoji 6 | 7 | dev=headset_dev_XX_XX_XX_XX_XX_XX # find it via upower -d 8 | test -n "$1" && dev="$1" 9 | getdata=`upower --show-info "/org/freedesktop/UPower/devices/$dev"` 10 | test -n "`echo "$getdata" | grep '(null)'`" && echo "⊝" && exit 11 | level=`echo "$getdata" | awk '/percentage/{print $2}' | sed 's/%//'` 12 | 13 | # check if it's a valid number 14 | case "$level" in 15 | ''|*[!0-9]*) exit 1;; 16 | esac 17 | 18 | # only show emoji for a graphical session 19 | if [ "$TERM" = "dumb" ]; then 20 | if [ "$level" -gt 39 ]; then # adjust to your situation: below 40% is already too low/little for me 21 | echo -n " 🔋" 22 | else 23 | echo -n " 🪫" 24 | fi 25 | fi 26 | 27 | echo "${level}%" 28 | -------------------------------------------------------------------------------- /Firefox-updater: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | #---------------------------------------------------------------------- 4 | # Description: the official Linux x86-64 Firefox distribution updater 5 | # Allows you to update Firefox from command line/CLI/terminal 6 | # 7 | # Author: Artem S. Tashkinov 8 | # Created at: Wed Dec 20 13:50 2017 9 | # 2019-05-29 15:08:05 Firefox 68 fix 10 | # 2022-12-17 01:47:32 Firefox 108 fix 11 | # 2025-04-09 14:34:47 Firefox 137 fix for the locale variable 12 | # 2025-05-01 21:45:14 Firefox 138 fix for the updater tool 13 | # Computer: localhost.localdomain 14 | # System: Linux 4.14.3-300.fc27.x86_64 on x86_64 15 | # 16 | # Copyright (c) 2017-2025 Artem S. Tashkinov All rights reserved. 17 | #---------------------------------------------------------------------- 18 | 19 | # Comment out to disable or set the number to add an iptables rule for the OUTPUT chain 20 | # firewall=6 21 | 22 | diewithmsg() 23 | { 24 | echo "ERROR: $1" 25 | exit 1 26 | } 27 | 28 | sanitychecker() 29 | { 30 | test -z "$1" && diewithmsg "Empty version string. Perhaps this script needs to be updated or wget failed." 31 | result=`echo "$1" | sed 's/\.//g;s/b//;s/[0-9]//g;s/esr//'` 32 | test -n "$result" && diewithmsg "The version string [$1] is wrong." 33 | } 34 | 35 | normalize() 36 | { 37 | # Add .0 for XX.0 releases, i.e. 60.0 becomes 60.0.0; remove esr; remove beta; awk: 60.1.2 -> 60 000 000 + 1000 + 2 38 | # will fail to upgrade beta to release 39 | echo "$1" | sed 's/^[0-9]*\.0$/\0.0/;s/esr//;s/b/\./' | awk -F "." '{print $1*1000000+$2*1000+$3}' 40 | } 41 | 42 | extractversion() 43 | { 44 | # Firefox 108+ fix 45 | datasrc=modules/AppConstants.jsm 46 | unzip -p "$1/omni.ja" "$datasrc" &> /dev/null || datasrc=modules/AppConstants.sys.mjs 47 | unzip -p "$1/omni.ja" "$datasrc" 2>/dev/null | egrep "MOZ_APP_VERSION_DISPLAY|MOZ_UPDATE_CHANNEL|INSTALL_LOCALE" | sed 's/: /=/;s/,//' 48 | # Firefox 68+ fix 49 | flocale=`unzip -p "$1/omni.ja" "$datasrc" 2>/dev/null | grep INSTALL_LOCALE` 50 | test -z "$flocale" && echo "INSTALL_LOCALE=`unzip -p "$1/omni.ja" update.locale 2>/dev/null`" 51 | # Firefox 137+ fix 52 | test -z "$flocale" && echo "INSTALL_LOCALE=`unzip -p "$1/omni.ja" default.locale 2>/dev/null`" 53 | } 54 | 55 | fetch() 56 | { 57 | wget --progress=dot:giga -O update.mar "$1" 58 | } 59 | 60 | unzip -v &> /dev/null || diewithmsg "Please install unzip first!" 61 | wget -V &> /dev/null || diewithmsg "Please install wget first!" 62 | bc -v &> /dev/null || diewithmsg "Please install bc first!" 63 | 64 | test -z "$2" && diewithmsg "Usage is: `basename "$0"` user_for_update firefox_installation_directory forced_new_version[optional]" 65 | grep -qw "$1" /etc/passwd || diewithmsg "Unable to find the user '$1'" 66 | test -f "$2/firefox" || diewithmsg "'$2' is not a valid Firefox installation directory" 67 | 68 | id=`id -u` || diewithmsg "id failed!" 69 | 70 | if [ "$id" == "0" ]; then 71 | if [ -n "$firewall" ]; then 72 | if [ -z "`iptables-save | grep uid-owner\ $1`" ]; then 73 | echo -n "Adding iptables rule number $firewall for the user $1 ... " 74 | iptables -I OUTPUT "$firewall" -p tcp -m owner --uid-owner "$1" -j ACCEPT && echo OK || diewithmsg "Failed" 75 | else 76 | echo "An iptables rule is already active" 77 | fi 78 | fi 79 | 80 | # Change files/directories ownership unconditionally 81 | echo -n "Applying permissions $1:$1 for '$2' ... " 82 | chown -Rc "$1:$1" "$2" && echo OK || diewithmsg "This mustn't happen." 83 | 84 | echo -n "Dropping root privileges ... " 85 | sudo -u "$1" "$0" "$1" "$2" "$3" 86 | exit $? 87 | fi 88 | 89 | echo "OK" 90 | echo "Running as user '`id -un`'." 91 | 92 | source <(extractversion "$2") 93 | versionhave="$MOZ_APP_VERSION_DISPLAY" 94 | 95 | echo "Locale: $INSTALL_LOCALE" 96 | 97 | if [ "$MOZ_UPDATE_CHANNEL" == "beta" ]; then 98 | echo "Selected the BETA update channel" 99 | channel="-beta" 100 | elif [ "$MOZ_UPDATE_CHANNEL" == "esr" ]; then 101 | echo "Selected the ESR update channel" 102 | channel="-esr" 103 | versionhave="${versionhave}esr" 104 | else 105 | echo "Selected the STABLE update channel" 106 | channel="" 107 | fi 108 | 109 | echo -n "Installed version: " 110 | sanitychecker "$versionhave" 111 | echo "$versionhave" 112 | 113 | echo -n "Available version: " 114 | versionnext="$3" 115 | if [ -z "$3" ]; then 116 | versionnext=`wget --spider --timeout=5 "https://download.mozilla.org/?product=firefox$channel-latest&os=linux64&lang=$INSTALL_LOCALE" 2>&1 | grep Location | sed 's/.*releases\/\(.*\)\/linux.*/\1/'` 117 | fi 118 | sanitychecker "$versionnext" 119 | echo "$versionnext" 120 | 121 | test "$versionhave" == "$versionnext" && echo "You're up to date. Exiting." && exit 0 122 | 123 | norm_vershave=`normalize "$versionhave"` 124 | norm_versnext=`normalize "$versionnext"` 125 | # bc: the result of all boolean operations are 0 and 1 (for false and true) as in relational expressions. 126 | test "`echo "$norm_vershave > $norm_versnext" | bc -l`" -eq 1 && diewithmsg "The installed version is newer than the available update! Perhaps you've updated manually." 127 | 128 | workdir=`mktemp -d` 129 | test -d "$workdir" || diewithmsg "Unable to create a working directory" 130 | cd "$workdir" || diewithmsg "Unable to enter a working directory" 131 | 132 | url_base="https://ftp.mozilla.org/pub/firefox/releases/$versionnext/update/linux-x86_64/$INSTALL_LOCALE" 133 | upd_incr="firefox-$versionhave-$versionnext.partial.mar" 134 | upd_full="firefox-$versionnext.complete.mar" 135 | url_incr="$url_base/$upd_incr" 136 | url_full="$url_base/$upd_full" 137 | echo -e "\nTrying to download delta from $versionhave to $versionnext ... " 138 | if ! fetch "$url_incr"; then 139 | echo "Trying to download full update to $versionnext ... " 140 | fetch "$url_full" || diewithmsg "Cannot download neither update" 141 | fi 142 | 143 | echo "Applying update ..." 144 | if [ $norm_vershave -lt 138000000 ]; then 145 | LD_LIBRARY_PATH="$2" "$2/updater" "$workdir" "$2" "$2" 146 | else 147 | LD_LIBRARY_PATH="$2" "$2/updater" 3 "$workdir" "$2" "$2" first 0 148 | fi 149 | 150 | echo -n "Cleaning up ... " 151 | rm -rf "$workdir" && echo OK 152 | 153 | echo "All done" 154 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Probably useful scripts 2 | 3 | ## [boost-tuner](https://raw.githubusercontent.com/birdie-github/useful-scripts/master/boost-tuner) 4 | I'm not content that my Ryzen 3700 CPU boosts so often and so much while I'm simply browsing the web (governor: ondemand) which leads to the CPU operating at relatively high temperatures (45-60C, with an average run-of-the-mill air cooler and around 19C temperature in the room where I am) and voltages (1.5V voltage doesn't sound safe to me despite AMD assurance this voltage is 100% safe). The whole situation made me write the script which disables boost unless certain predefined processes are found to be running. At the moment I've allowed only compilation but you can add anything you want. To run it you may add it to `/etc/rc.d/rc.local`, e.g. this way: 5 | `( sleep 60 && exec /root/bin/boost-tuner & ) &` # this is not to delay any further commands and probably to disable boost only after the user has logged on (60 seconds are more than enough to enter your password). It must be run under root. As a user you may force enable boost by creating /tmp/boost, e.g. `touch /tmp/boost` 6 | 7 | ## [btc2usd](https://raw.githubusercontent.com/birdie-github/useful-scripts/master/btc2usd) 8 | A shell script which shows the current bitcoin exchange rates for CoinDesk.com and CoinMarketCap.com in the United States dollars (USD). Requires the jq utility (can be installed in deb distros using `sudo apt-get install jq` or in Fedora using `sudo dnf install jq`). It could also be used under XFCE via the generic monitor applet. 9 | 10 | ## [clean_dnf](https://raw.githubusercontent.com/birdie-github/useful-scripts/master/clean_dnf) 11 | Ever wanted to get rid of useless/unnecessary/redundant RPMs lying on your disk? There's a script for that which uses DNF. Run it without arguments to walk through all the installed packaged, or give it a single argument (I may fix it to accept any number of packages) to check whether other packages depend on it. It will skip the RPMs which result in the removal of protected packages like DNF itself. 12 | 13 | ## [defrag](https://raw.githubusercontent.com/birdie-github/useful-scripts/master/defrag) 14 | fast recursive defrag for ext4 15 | * Fast recursive defragmentation for the specified directory residing on an ext4 filesystem; it doesn't support anything else 16 | * ext4 doesn't allow to defrag free space, so it's impossible to fully defragment a filesystem if free space is fragmented and there are files larger than continuous chunks of free space 17 | * If you want to defrag the root filesystem, start with `defrag /var`, then `defrag /` - files in var are usually heavily fragmented and need to be defragged first 18 | * It's theoretically possible to fully defrag the ext4 filesystem: you first need to shrink it, then expand it. I will never recommend this as it's quite a dangerous operation and mustn't be performed without backing up first; in addition you cannot obviously do that on a mounted filesystem 19 | 20 | ## [Firefox-updater](https://raw.githubusercontent.com/birdie-github/useful-scripts/master/Firefox-updater) 21 | If you're unwilling to let your user account update Firefox (it's a little bit unsafe) I've created a script for this situation which could be added in a crontab. This script automatically updates the official Linux x86-64 Firefox distribution installed on your PC. It currently supports the ESR, stable and beta channels. 22 | 23 | ## [monitor_overclock](https://raw.githubusercontent.com/birdie-github/useful-scripts/master/monitor_overclock) 24 | This script allows you to overclock your monitor (refresh rate/frequency) under Linux. It was created specifically for my sole monitor, so you'll have to adjust it if yours is different. Modifications to /etc/X11/xorg.conf.d/conf.conf are [required](https://devtalk.nvidia.com/default/topic/1054885/linux/monitor-refresh-frequency-overclocking-in-linux-is-not-available/). 25 | 26 | ## [watch_raw_io](https://raw.githubusercontent.com/birdie-github/useful-scripts/master/watch_raw_io) 27 | Monitor input/output read/write nicely formatted stats for all block devices/disks in the system in real time in Linux. Takes a single argument: a refresh interval which is 2 seconds by default. Works under a normal (non-root) user as well. 28 | 29 | ## Bottom line 30 | 31 | To be honest I have close to a hundred such scripts but many of them are either silly or useful only for me. I publish them elsewhere. 32 | 33 | ## SAST Tools 34 | 35 | [PVS-Studio](https://pvs-studio.com/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code. 36 | -------------------------------------------------------------------------------- /alert-bad-processes: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | #---------------------------------------------------------------------- 4 | # Description: alert audibly and visually if there are CPU heavy 5 | # processes running 6 | # if argument1 == 1 then alert audibly 7 | # if argument2 == 1 then alert visually 8 | # 9 | # Mon Jan 11 23:11:09 2016 v1.0.1 - made `top` version agnostic 10 | # Wed 16 Oct 2019 09:31:20 PM - fine tune for LXDM *only* 11 | # 12 | # Author: Artem S. Tashkinov 13 | # Created at: Mon Jan 11 18:54:14 2016 14 | # Computer: localhost.localdomain 15 | # System: Linux 4.4.0-ic64 on x86_64 16 | # 17 | # Copyright (c) 2016 Artem S. Tashkinov All rights reserved. 18 | #---------------------------------------------------------------------- 19 | 20 | faudio=/opt/kde3/share/sounds/KDE_Beep_ClockChime.wav 21 | timeout=3 # visual cue for this number of seconds 22 | threshold=75 # minimum CPU usage which is considered bad 23 | 24 | nl=' 25 | ' 26 | 27 | processes() 28 | { 29 | # doesn't work: shows the aggregated CPU usage which might be very low for long running processes 30 | # ps auxS | awk '{if ($3 > 80) for (i=11; i<=NF; i++) print $i" "}' 31 | 32 | # doesn't work: top version specific 33 | # HOME=/dev/null top -bn1 | tail -n +8 | awk 'BEGIN{ORS=""}{if ($9 > '$threshold') {print $9"%\t"; for (i=12; i<=NF; i++) print $i" "; print "\n"}}' 34 | 35 | # avoid using user's top settings 36 | HOME=/dev/null top -bn1 | \ 37 | awk ' 38 | BEGIN {ORS=""} 39 | { 40 | if ($0~/PID.*USER.*PR/) { 41 | for (i=1;i<=NF;i++) { 42 | if ($i~/CPU/) f_cpu=i; 43 | if ($i~/COMMAND/) f_cmd=i; 44 | found=1; 45 | } 46 | } 47 | if (found) { 48 | if ($f_cpu > '$threshold') { 49 | print $f_cpu"%\t"; 50 | for (i=f_cmd; i<=NF; i++) { 51 | if ($i != "`-") # top 3.3 likes a tree structure 52 | print $i" "; 53 | } 54 | print "\n"; 55 | } 56 | } 57 | }' 58 | } 59 | 60 | if [ "$1" = 1 ]; then 61 | if [ -n "`processes`" ]; then 62 | if [ -f "$faudio" ]; then 63 | aplay -q "$faudio" & 64 | elif eplay --version &>/dev/null; then 65 | play -q -n synth 0.5 sine & 66 | else 67 | echo -ne '\007' & 68 | fi 69 | fi 70 | fi 71 | 72 | pid_lxdm=`pidof lxdm-session` 73 | Xuser=0 # this might not work - needs to be tested 74 | test -z "$pid_lxdm" || Xuser=`ps -u --ppid "$pid_lxdm" | tail -1 | awk '{print $1}'` 75 | 76 | if [ "$2" = 1 -a -n "$Xuser" ]; then 77 | bad_processes=`processes` 78 | if [ -n "$bad_processes" ]; then 79 | su "$Xuser" -c "DISPLAY=:0 notify-send --expire-time="${timeout}000" --icon=/mnt/STORAGE/birdie/linux/new/Adwaita_32x32_dialog-warning.png 'Warning!' 'Detected > ${threshold}%:$nl$bad_processes' &> /dev/null" & 80 | fi 81 | fi 82 | -------------------------------------------------------------------------------- /alert-bad-processes-user: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | #---------------------------------------------------------------------- 4 | # Description: alert audibly and visually if there are CPU heavy 5 | # processes running 6 | # if argument1 is set then alert audibly 7 | # 8 | # 2026-01-11 23:11:09 v1.0.1 - made `top` version agnostic 9 | # 2019-10-16 21:31:20 fine tune for LXDM *only* 10 | # 2025-01-02 05:25:38 turned it into a user script to be run from ~/.config/autostart; fixed top's no config invocation (no option yet, filed a bug report) 11 | # 2025-01-03 05:25:38 now uses a standard audio file 12 | # 13 | # Author: Artem S. Tashkinov 14 | # Created at: Mon Jan 11 18:54:14 2016 15 | # Computer: localhost.localdomain 16 | # System: Linux 4.4.0-ic64 on x86_64 17 | # 18 | # Copyright (c) 2016-2025 Artem S. Tashkinov All rights reserved. 19 | #---------------------------------------------------------------------- 20 | 21 | faudio=/usr/share/sounds/freedesktop/stereo/complete.oga 22 | timeout=3 # visual cue for this number of seconds 23 | threshold=60 # minimum CPU usage which is considered bad 24 | volume=33 # audio notification volume 25 | interval=5 # check for bad processes every X seconds 26 | 27 | nl=' 28 | ' 29 | 30 | processes() 31 | { 32 | # doesn't work: shows the aggregated CPU usage which might be very low for long running processes 33 | # ps auxS | awk '{if ($3 > 80) for (i=11; i<=NF; i++) print $i" "}' 34 | 35 | # doesn't work: top version specific 36 | # HOME=/dev/null top -bn1 | tail -n +8 | awk 'BEGIN{ORS=""}{if ($9 > '$threshold') {print $9"%\t"; for (i=12; i<=NF; i++) print $i" "; print "\n"}}' 37 | 38 | # avoid using user's top settings 39 | env HOME=/dev/null XDG_CONFIG_HOME="" top -bn1 | \ 40 | awk ' 41 | BEGIN {ORS=""} 42 | { 43 | if ($0~/PID.*USER.*PR/) { 44 | for (i=1;i<=NF;i++) { 45 | if ($i~/CPU/) f_cpu=i; 46 | if ($i~/COMMAND/) f_cmd=i; 47 | found=1; 48 | } 49 | } 50 | if (found) { 51 | if ($f_cpu > '$threshold') { 52 | print $f_cpu"%\t"; 53 | for (i=f_cmd; i<=NF; i++) { 54 | if ($i != "`-") # top 3.3 likes a tree structure 55 | print $i" "; 56 | } 57 | print "\n"; 58 | } 59 | } 60 | }' 61 | } 62 | 63 | while :; do 64 | bad_processes=`processes` 65 | 66 | if [ -n "$bad_processes" ]; then 67 | 68 | notify-send --expire-time="${timeout}000" --icon=/mnt/STORAGE/birdie/linux/new/Adwaita_32x32_dialog-warning.png "Warning!" "Detected > ${threshold}%:$nl$bad_processes" &> /dev/null & 69 | 70 | if [ -f "$faudio" -a -n "$1" ]; then 71 | paplay --volume=$((volume*65535/100)) "$faudio" & 72 | fi 73 | 74 | fi 75 | sleep $interval 76 | done 77 | -------------------------------------------------------------------------------- /any2wav: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # Tue Jan 7 17:57:16 2014 4 | # Convert any media file with an audio stream to a wav file 5 | 6 | # Mon 04 Nov 2019 10:09:46 PM 7 | # mplayer -> ffmpeg; replace file extension instead of appending it - requires bash 8 | 9 | for i in "$@"; do 10 | echo -n "Processing '$i' ... " 11 | test -n "`echo "$i" | grep '\.wav$'`" && echo "$i is already a wav file. Skipping" && continue 12 | ext="${i##*.}" 13 | wav="${i%.$ext}.wav" 14 | test "$wav" == "$i" && wav="$i.wav" # In case I screwed up 15 | test -f "$wav" && echo "$wav already exists. Skipping." && continue 16 | ffmpeg -hide_banner -nostats -loglevel warning -i "$i" -map_metadata -1 -fflags +bitexact -vn "$wav" && echo "OK" 17 | done 18 | -------------------------------------------------------------------------------- /audio-conf: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # 2022-04-01 11:44:22 UTC 4 | # Load the TCP module to allow other local users to use audio devices 5 | # Change the default sampling rate to 48KHz 6 | # Enable more allowed sampling rates 7 | 8 | # https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Config-PulseAudio#module-native-protocol-tcp 9 | # "auth-ip-acl=127.0.0.1;192.168.1.0/24;::1" https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/847#note_828707 10 | # "listen=127.0.0.1;192.168.0.100;::1" https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/2259 11 | # 12 | # 2022-04-04 11:44:29 UTC 13 | # + module-x11-bell : /usr/share/sounds/freedesktop/stereo/bell.oga in sound-theme-freedesktop 14 | # 2022-12-22 10:27:26 UTC 15 | # + module-combine-sink : output to all enabled devices simultaneously (that's an output virtual "combined" device) 16 | # 2022-12-30 09:29:33 UTC 17 | # - module-combine-sink : wireplumber makes it default all the time and that fucks up volume management 18 | 19 | declare -A opts 20 | opts[module-native-protocol-tcp]="listen=127.0.0.1" 21 | opts[module-combine-sink]="sink_name=CombinedVirtual" 22 | 23 | for i in module-native-protocol-tcp module-x11-bell; do # module-combine-sink 24 | if [ -z "`pactl list modules | grep $i`" ]; then 25 | echo -n "Loading $i ... " 26 | pactl load-module $i ${opts[$i]} && echo OK 27 | else 28 | echo "$i is already loaded" 29 | fi 30 | done 31 | 32 | # https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Config-PipeWire 33 | # Get settings: `pw-metadata -n settings` 34 | 35 | if ! which pw-metadata &> /dev/null; then 36 | echo "Error: 'pw-metadata' is missing, please install pipewire-utils" 37 | exit 1 38 | fi 39 | 40 | echo "Setting extra options ... " 41 | pw-metadata -n settings 0 clock.rate 48000 42 | pw-metadata -n settings 0 clock.allowed-rates '[ 48000, 96000, 44100 ]' 43 | -------------------------------------------------------------------------------- /avoid-vpn: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | #---------------------------------------------------------------------- 4 | # Description: avoid VPN for certain either predefined or given hosts 5 | # Author: Artem S. Tashkinov 6 | # Created at: Mon Jan 1 22:09:05 2018 7 | # Thu Jan 31 15:40:07 2019 exclude tun when looking for a defgw 8 | # 2019-08-27 14:15:16 - no idea what it was 9 | # Wed 16 Oct 2019 08:58:46 PM - beautify IP using printf 10 | # 2022-07-18 16:37:59 - print debug only in the interactive console 11 | # 12 | # Computer: localhost.localdomain 13 | # System: Linux 4.14.10-ic64 on x86_64 14 | # 15 | # Copyright (c) 2018 Artem S. Tashkinov All rights reserved. 16 | #---------------------------------------------------------------------- 17 | 18 | eko() 19 | { 20 | test "$TERM" = "linux" -o "$TERM" = "xterm-256color" && echo "$@" 21 | } 22 | 23 | default=( "irc.freenode.net" "en.wikipedia.org" "distrowatch.com" "wiki.debian.org" "irc.gnome.org" ) 24 | (( $# == 0 )) && set -- "${default[@]}" # Set arguments to $default if no command line arguments are given 25 | defgw=`ip r | grep -v tun | awk '/default via/{print $3}'` 26 | test -z "$defgw" && eko "Default gateway is not defined" && exit 1 27 | eko "Default gateway is $defgw" 28 | 29 | for i in "$@"; do 30 | eko "Routing $i ... " 31 | for IP in `host "$i" | awk '/has address/{print $4}'`; do 32 | printf " %-14s : " "$IP" 33 | if [ -z "`ip r | grep -w "$IP"`" ]; then 34 | ip r a "$IP/32" via "$defgw" && eko "OK" || eko "failed" 35 | else 36 | eko "already routed" 37 | fi 38 | done 39 | done 40 | -------------------------------------------------------------------------------- /avolume: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # 2021-04-27 13:44:02 4 | amount=3 5 | test -n "$3" && amount="$3" 6 | 7 | volume=`audtool get-volume` || exit 1 8 | 9 | if [ "$1" = "+" ]; then 10 | audtool set-volume $((volume+amount)) 11 | else 12 | audtool set-volume $((volume-amount)) 13 | fi 14 | -------------------------------------------------------------------------------- /boost-tuner: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | #---------------------------------------------------------------------- 4 | # Description: enable CPU boost only when given applications are running 5 | # will be unconditionally enabled if /tmp/boost exists 6 | # 7 | # Author: Artem S. Tashkinov 8 | # Created at: Sun 05 Jan 2020 06:57:51 PM 9 | # Computer: localhost.localdomain 10 | # System: Linux 5.4.2-zen2 on x86_64 11 | # 12 | # Copyright (c) 2020 Artem S. Tashkinov All rights reserved. 13 | #---------------------------------------------------------------------- 14 | 15 | interval=10 16 | apps="^gcc|^cpp|^ld|^make|^configure|^cmake" 17 | fileon=/tmp/boost 18 | 19 | handler=/sys/devices/system/cpu/cpufreq/boost 20 | 21 | while :; do 22 | 23 | test ! -f "$handler" && echo "CPU frequency subsystem failure: boost not found" && exit 1 24 | result=`ps axco command | egrep "$apps"` 25 | 26 | if [ -n "$result" -o -f "$fileon" ]; then 27 | echo 1 > "$handler" 28 | else 29 | echo 0 > "$handler" 30 | fi 31 | 32 | sleep $interval 33 | done 34 | -------------------------------------------------------------------------------- /btc2usd: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # 2020-10-19 19:29:08 4 | # 2025-01-16 20:45:26 reordered, a hint for the XFCE command output applet 5 | # 2025-04-28 14:32:28 coindesk can go fuck themselves 6 | 7 | if jq --version &>/dev/null; then 8 | 9 | price1=`curl --silent --max-time 7 'https://data-api.cryptocompare.com/asset/v1/top/list?page=1&page_size=15&sort_by=CIRCULATING_MKT_CAP_USD&sort_direction=DESC&groups=ID%2CBASIC%2CSUPPLY%2CPRICE%2CMKT_CAP%2CVOLUME%2CCHANGE%2CTOPLIST_RANK&toplist_quote_asset=USD&response_format=JSON' | jq '.Data.LIST[] | select(.SYMBOL == "BTC") | .PRICE_USD'` 10 | price3=`curl --silent --max-time 7 'https://api.coingecko.com/api/v3/coins/bitcoin?developer_data=false&community_data=false&tickers=false' | jq '.market_data.current_price.usd'` 11 | 12 | printf " %'.f %'.f " "$price3" "$price1" 2>/dev/null 13 | echo "1st: coingecko.com; 2nd: coindesk.com via cryptocompare;" 14 | echo "firefox https://coingecko.com" 15 | 16 | else 17 | 18 | echo "Install jq" 19 | 20 | fi 21 | -------------------------------------------------------------------------------- /check_installed_rpms: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | #---------------------------------------------------------------------- 3 | # Description: a nifty script to verify all installed RPM 4 | # 5 | # Author: Artem S. Tashkinov <> 6 | # Created at: 2010-04-08 01:11:18 7 | # 8 | # Copyright (c) 2010 Artem S. Tashkinov. All rights reserved. 9 | #---------------------------------------------------------------------- 10 | 11 | echo "Running ... " 12 | 13 | logf=/tmp/RPM-V.$RANDOM$RANDOM 14 | cat > $logf << EndOfMessage 15 | S file Size differs 16 | M Mode differs (includes permissions and file type) 17 | 5 digest (formerly MD5 sum) differs 18 | D Device major/minor number mismatch 19 | L readLink(2) path mismatch 20 | U User ownership differs 21 | G Group ownership differs 22 | T mTime differs 23 | P caPabilities differ 24 | EndOfMessage 25 | 26 | r[0]='-' 27 | r[1]='\' 28 | r[2]='|' 29 | r[3]='/' 30 | r[4]='-' 31 | r[5]='\' 32 | r[6]='|' 33 | r[7]='/' 34 | 35 | j=0 36 | n=1 37 | 38 | total=`rpm -qa | wc -l` 39 | 40 | for i in `rpm -qa | sort`; do 41 | 42 | p=$((n*100/total)) 43 | echo -en "\r${r[$j]} $n [$p%]" 44 | RES=`nice -20 rpm -V "$i"` 45 | 46 | if [ -n "$RES" ]; then 47 | echo "******************** $i ********************" >> $logf 48 | echo "$RES" >> $logf 49 | fi 50 | 51 | n=$((n+1)) 52 | j=$((j+1)) 53 | test $j -gt 7 && j=0 54 | 55 | done 56 | 57 | echo 58 | echo "Done. $n packages verified. See log here: $logf" 59 | -------------------------------------------------------------------------------- /check_installed_rpms_clean: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # 2019-02-23 15:59:17 - clean version 4 | # 2023-05-03 12:48:05 - egrep -> grep -E 5 | 6 | echo "Running ... " 7 | 8 | [ -n "$1" ] && nodeps="|Unsatisfied dependencies|is needed by" 9 | 10 | logf=/tmp/RPM-V.$RANDOM$RANDOM 11 | > $logf 12 | 13 | r[0]='-' 14 | r[1]='\' 15 | r[2]='|' 16 | r[3]='/' 17 | r[4]='-' 18 | r[5]='\' 19 | r[6]='|' 20 | r[7]='/' 21 | 22 | j=0 23 | n=1 24 | 25 | total=`rpm -qa | wc -l` 26 | 27 | for i in `rpm -qa | sort`; do 28 | 29 | p=$((n*100/total)) 30 | echo -en "\r${r[$j]} $n [$p%]" 31 | RES=`nice -20 rpm -V "$i" | grep -E -v "ZZZXXXCCC$nodeps"` 32 | 33 | if [ -n "$RES" ]; then 34 | echo " ******************** $i ******************** " >> $logf 35 | echo "$RES" >> $logf 36 | fi 37 | 38 | n=$((n+1)) 39 | j=$((j+1)) 40 | test $j -gt 7 && j=0 41 | 42 | done 43 | 44 | echo 45 | echo "Done. $n packages verified. See log here: $logf" 46 | -------------------------------------------------------------------------------- /clean_dnf: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | #---------------------------------------------------------------------- 3 | # Description: script for rpm uninstallation within all installed rpms 4 | # 5 | # Author: Artem S. Tashkinov 6 | # Created at: 7 | # Mon Nov 21 20:12:04 2016 - dnf, show deps, no clear 8 | # Wed Dec 13 19:43:00 2017 - remove by mask, skip already deleted, skip protected 9 | # Fri May 11 18:52:51 2018 - skip packages with errors instead of waithing for the user input 10 | # stop background dependancy checking 11 | # set env var SKIP to skip up to a certain package 12 | # 13 | # Copyright (c) 2004 Artem S. Tashkinov. All rights reserved. 14 | #---------------------------------------------------------------------- 15 | 16 | #set -x 17 | dont_remove="|AdobeReader_enu|Thunar|icewm|hexchat|xterm|xfce4|qbittorrent|mkvtoolnix|rpm-build|wireshark|samba|libxml2-devel|libvorbis|libogg|alsa|lxdm|xfdesktop|openssh|vpx|google-chrome|mesa-lib|libxslt|openssl|pulseaudio-libs|electrum|firewall" 18 | 19 | clear 20 | 21 | resolve() 22 | { 23 | name=`rpm -q --queryformat "%{NAME}" "$1"` 24 | result=`dnf -C --setopt=clean_requirements_on_remove=False --assumeno remove "$1" 2>&1` # | grep -v "Operation aborted."` 25 | protect=`echo "$result" | egrep "protected packages$dont_remove"` 26 | if [ -z "$protect" ]; then 27 | myself=`echo "$result" | grep -vw "$name" | awk '/@/{print $1"-"$3"."$2}'` # awk: don't print the package itself as a dependancy: if (index("'"$1"'",$1)) next; 28 | if [ -z "$myself" ]; then 29 | echo "[OK]: no dependencies found." 30 | else 31 | echo -n "[WARN]: Dependent on $1: " 32 | echo "$result" | grep -vw "$name" | awk 'BEGIN{ORS=", "}/@/{print $1}' | sed 's/, $/\n/' 33 | fi 34 | echo -n "Remove (n)? " 35 | else 36 | echo -e "[ERROR]:\n$protect" 37 | return 1 38 | fi 39 | } 40 | 41 | some="." 42 | test -n "$1" && some="$1" 43 | 44 | for i in `rpm -qa | sort | grep "$some"`; do 45 | [[ "$i" < "$SKIP" ]] && continue 46 | # clear 47 | if rpm -qi "$i"; then 48 | if resolve "$i"; then # & 49 | read answer 50 | if [ "$answer" == "Y" -o "$answer" == "y" ]; then 51 | dnf -C remove "$i" 52 | fi 53 | else 54 | echo "[ERROR]: skipping a protected package" 55 | fi 56 | else 57 | echo "[WARNING]: Package "$i" has already been removed" 58 | fi 59 | echo "________________________________________________________________" 60 | done 61 | -------------------------------------------------------------------------------- /cpumode: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | #---------------------------------------------------------------------- 3 | # Description: Put CPU to and backto full powersave mode 4 | # Author: Artem S. Tashkinov 5 | # Created at: Sun Jun 19 00:14:40 2005 6 | # System: Linux 2.6.12-k8 on i686 7 | # 8 | # Thu Jul 1 19:18:10 2010 9 | # 1.0 Added SMP support 10 | # 11 | # Sun Dec 25 16:50:36 2011 12 | # 1.1 Completely reworked modules loading mechanism; userspace governor 13 | # 14 | # Sun Jul 22 05:30:16 2012 15 | # 1.2 Set/Reset; Improved output 16 | # 17 | # Mon Aug 6 00:46:25 2012 18 | # 1.2.1 unified userspace and maxfreq; unified everything; more error checkings 19 | # 20 | # Mon Feb 11 06:19:04 2013 21 | # 1.2.2 --now shows frequency in MHz 22 | # 23 | # Fri May 3 00:41:13 2013 24 | # 1.2.3 I've discovered that after suspend/resume max CPU cores 2, 3 and 4 25 | # frequences have been reset. This script "thought" those numbers are always 26 | # the same, now we tread carefully and check if they are indeed different 27 | # Also fix userspace set frequency - maxfreq also has to be adjusted 28 | # 29 | # Sun Aug 6 03:01:58 2017 30 | # 1.2.4 add an option to show available frequencies 31 | # 32 | # Mon 14 Oct 2019 04:21:23 PM 33 | # 1.2.5 add boost support 34 | # truncate the resulting frequencies 35 | # 36 | # Wed Mar 23 09:35:13 PM 2022 37 | # 1.2.6 add schedutil governor support 38 | # show the CPU frequency driver 39 | # 40 | # Tue Apr 5 06:18:12 AM 2022 41 | # 1.2.7 fix setting [max] CPU frequency with amd-pstate 42 | # 43 | # Fri Jun 17 01:04:33 PM 2022 44 | # 1.2.8 multiple fixes for intel-pstate 45 | # 46 | # Thu Jun 23 07:38:05 AM 2022 47 | # 1.2.9 multiple optimizations and fixes for intel-pstate boost 48 | # 49 | # Copyright (c) 2005-2022 Artem S. Tashkinov. All rights reserved. 50 | #---------------------------------------------------------------------- 51 | 52 | #set -x 53 | #PS4='+ $(date "+%s.%N ($LINENO) ")' 54 | 55 | version=1.2.9 56 | DRIVER=acpi_cpufreq 57 | lead=/sys/devices/system/cpu 58 | 59 | cpu0=$lead/cpu0/cpufreq 60 | min0=`cat $cpu0/cpuinfo_min_freq 2>/dev/null` 61 | max0=`cat $cpu0/cpuinfo_max_freq 2>/dev/null` 62 | lim0=`cat $cpu0/scaling_max_freq 2>/dev/null` 63 | favailfreq=$cpu0/scaling_available_frequencies 64 | SG=$lead/cpu*/cpufreq/scaling_governor 65 | SD=`cat $cpu0/scaling_driver 2>/dev/null` 66 | 67 | hz2mhz() 68 | { 69 | echo "$@" | awk 'END{ORS=" "; for (i=1;i<=NF;i++) print int($i/1000)"MHz"}' | sed 's/.$//' 70 | } 71 | 72 | setgov() 73 | { 74 | echo "$1" | tee $SG &> /dev/null 75 | test -n "`cat $SG | grep -v "$1"`" && echo "Failed swithing to the '$1' governor! Might not be supported or compiled in." && exit 100 76 | echo "$1 mode/governor has been activated" 77 | } 78 | 79 | getgov() 80 | { 81 | echo "Governor: `cat $SG | sort | uniq`" 82 | } 83 | 84 | boost() 85 | { 86 | on=1 87 | off=0 88 | fboost=/sys/devices/system/cpu/cpufreq/boost 89 | test "$SD" = "intel_pstate" && fboost=/sys/devices/system/cpu/intel_pstate/no_turbo && on=0 && off=1 90 | 91 | echo -n "Boost: " 92 | test ! -f "$fboost" && echo "is not supported" && return 93 | if [ -z "$1" ]; then 94 | test "`cat $fboost`" == "$on" && echo "enabled" || echo "disabled" 95 | else 96 | arg=`echo "$1" | tr A-Z a-z` 97 | if [ "$arg" == "1" -o "$arg" == "on" -o "$arg" == "yes" ]; then 98 | echo "$on" > $fboost 99 | test "`cat $fboost`" == "$on" && echo "enabled" || echo "FAILED to enable" 100 | else 101 | echo "$off" > $fboost 102 | test "`cat $fboost`" == "$off" && echo "disabled" || echo "FAILED to disable" 103 | fi 104 | fi 105 | } 106 | 107 | availfreq() 108 | { 109 | echo -n "The available frequencies are: " 110 | if [ -f "$favailfreq" ]; then 111 | cat "$favailfreq" 112 | else 113 | echo "anything from $(hz2mhz $min0) ($min0 KHz) to $(hz2mhz $max0)($max0 KHz)" 114 | fi 115 | } 116 | 117 | setspeed() 118 | { 119 | allowed=`cat $favailfreq 2>/dev/null` 120 | if [ -z "$2" ]; then 121 | echo -n "Need an argument to continue. " 122 | availfreq 123 | return 1 124 | fi 125 | 126 | if [ -z "`echo "$allowed" | grep -w "$2"`" -a -f $favailfreq ]; then 127 | echo "'$2' is not amongst the allowed frequencies: " 128 | echo $allowed 129 | return 2 130 | fi 131 | 132 | if [ "$1" == "userspace" ]; then 133 | setgov userspace 134 | echo -n "Setting user defined frequency of `hz2mhz $2` ... " 135 | echo "$2" | tee $lead/cpu*/cpufreq/scaling_max_freq > /dev/null 136 | echo "$2" | tee $lead/cpu*/cpufreq/scaling_setspeed > /dev/null 137 | [ "`cat $cpu0/scaling_setspeed 2>/dev/null`" == "$2" ] && echo OK || echo Failed 138 | elif [ "$1" == "maxfreq" ]; then 139 | getgov 140 | echo "Setting maximum frequency of `hz2mhz $2`" 141 | echo "$2" | tee $lead/cpu*/cpufreq/scaling_max_freq > /dev/null 142 | echo "Result: $(cat $lead/cpu*/cpufreq/scaling_max_freq | awk '{print $1/1000"MHz"}' | uniq)" 143 | else 144 | echo "Don't know what to do" 145 | fi 146 | } 147 | 148 | if [ ! -n "$SD" -a "$1" != "-l" -a "$1" != "--load" ]; then 149 | echo "Error: cpufrequency subsystem is not running!" 150 | exit 1 151 | fi 152 | 153 | governors=`cat $cpu0/scaling_available_governors 2>&1` 154 | 155 | for i in performance conservative userspace powersave ondemand; do 156 | cgovern=`echo $governors | grep $i` 157 | if [ -z "$cgovern" ]; then 158 | #echo "CPU governor '$i' is not present" # intel_pstate doesn't work with conservative userspace ondemand - no idea why - let's silence everything 159 | module="cpufreq_$i" 160 | #echo "Installing $module ... " # likewise 161 | modprobe $module >/dev/null 2>&1 162 | if [ "$?" != "0" ]; then 163 | echo "Error: can't load $module module. Do you have it installed?" 164 | exit 1 165 | else 166 | #echo "OK" 167 | : 168 | fi 169 | fi 170 | done 171 | 172 | test "$1" = "-l" -o "$1" = "--load" || current=`cat $SG` 173 | 174 | case $1 in 175 | --version|-v) #print the version number 176 | echo "`basename $0` version "$version 177 | exit 0;; 178 | 179 | --help|-h) #Display the help screen 180 | echo "Usage: `basename $0` [OPTION]" 181 | echo " -M --maximum - performance mode (minimum powersavings)" 182 | echo " -m --mimimum - powersave mode (minimum performance)" 183 | echo " -a --auto - ondemand mode (rapid auto performance)" 184 | echo " -b --boost - enable/disable boost" 185 | echo " -c --conservative - conservative mode (gradual auto performance)" 186 | echo " -s --schedutil - schedutil governor" 187 | echo " -u --userspace FREQ - user defined CPU frequency" 188 | echo " -l --load - load CPU powersave driver" 189 | echo " -n --now - show current mode" 190 | echo " -S --set FREQ - set maximum CPU frequency" 191 | echo " -r --reset - reset maximum CPU frequency" 192 | echo " -f --frequencies - show available frequencies" 193 | echo " -t --time - percentage of time spent in states" 194 | echo " -v --version - show application version and exit" 195 | echo " -h --help - show this help and exit" 196 | exit 0;; 197 | 198 | --load|-l) 199 | KDRIVER=`echo $DRIVER | sed 's/-/_/g'` 200 | if [ -z "`lsmod | grep $KDRIVER`" ]; then 201 | modprobe $DRIVER && echo "CPU powersave driver has been loaded successfully" 202 | else 203 | echo "CPU powersave driver is already loaded" 204 | fi 205 | exit;; 206 | 207 | --userspace|-u) 208 | setspeed userspace "$2" 209 | exit;; 210 | 211 | --conservative|-c) 212 | setgov conservative 213 | exit;; 214 | 215 | --schedutil|-s) 216 | setgov schedutil 217 | exit;; 218 | 219 | --minimum|-m) 220 | setgov powersave 221 | exit;; 222 | 223 | --maximum|-M) 224 | setgov performance 225 | exit;; 226 | 227 | --set|-S) 228 | setspeed maxfreq "$2" 229 | exit;; 230 | 231 | --reset|-r) 232 | getgov 233 | echo "Resetting maximum frequency ... " 234 | echo $max0 | tee $lead/cpu*/cpufreq/scaling_max_freq > /dev/null 235 | cat $lead/cpu*/cpufreq/scaling_max_freq 236 | exit;; 237 | 238 | --auto|-a) 239 | setgov ondemand 240 | exit;; 241 | 242 | --boost|-b) 243 | boost "$2" 244 | exit;; 245 | 246 | --frequencies|-f) 247 | availfreq 248 | exit;; 249 | 250 | --time|-t) 251 | if [ -f /sys/devices/system/cpu/cpufreq/policy0/stats/time_in_state ]; then 252 | awk '{time[$1]+=$2;sum+=$2}END{for (var in time) print var,":",time[var]/sum*100,"%"}' /sys/devices/system/cpu/cpufreq/policy*/stats/time_in_state 253 | else 254 | echo "time_in_state is not supported" 255 | fi 256 | exit;; 257 | 258 | --now|-n|*) 259 | echo "Driver: $SD" 260 | boost 261 | getgov 262 | 263 | cores=`cat $lead/cpu*/cpufreq/scaling_cur_freq 2>/dev/null` 264 | if [ -n "$cores" ]; then 265 | echo -n "Average and maximum frequencies now: " 266 | hz2mhz `echo $cores | awk 'BEGIN{max=0;avr=0} {for (i=1;i<=NF;i++) {if ($i>max) max=$i; avr+=$i}} END{print avr/NF, max}'` 267 | echo 268 | 269 | #echo -n "Cores are at: " # no one cares considering there are PCs with 32 virtual cores 270 | #hz2mhz $cores 271 | #echo 272 | else 273 | echo "This should have never happened!" 274 | fi 275 | 276 | # Check if maximum CPU frequences are mixed or maxed out 277 | for i in `cat $lead/cpu*/cpufreq/scaling_max_freq`; do 278 | this=$i 279 | test "$max0" != "$i" && notmaxed=1 280 | test "$next" != "$this" -a -n "$vin" && nonuni=1 # vin - do not run on a first loop invocation 281 | next=$this 282 | vin=1 283 | done 284 | 285 | if [ -z "$notmaxed" ]; then 286 | echo "No frequency limit is enforced." 287 | else 288 | if [ -n "$nonuni" ]; then 289 | echo "Maximum CPU frequency per CPU is :" 290 | hz2mhz `cat $lead/cpu*/cpufreq/scaling_max_freq` 291 | echo 292 | else 293 | echo "Maximum allowed frequency is `hz2mhz $lim0` (out of `hz2mhz $max0`)" 294 | fi 295 | fi 296 | 297 | exit;; 298 | esac 299 | -------------------------------------------------------------------------------- /cr: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # Too tired of remembering all the flags required to launch Google Chrome 4 | # 2018-03-06 22:47:33 5 | 6 | # 2021-04-17 17:32:06 a fix for 27" display 7 | 8 | RES=`xdpyinfo | awk '/dimensions/{print $2}'` 9 | extra= 10 | 11 | if [ "$RES" == "2560x1440" ]; then 12 | extra=--force-device-scale-factor=1 13 | echo "Setting $extra" 14 | fi 15 | 16 | env TZ=USA/Pacific FREETYPE_PROPERTIES=truetype:interpreter-version=35 chrome --disk-cache-dir=/tmp/.chrome-cache $extra "$@" 17 | -------------------------------------------------------------------------------- /defrag: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | #---------------------------------------------------------------------- 3 | # Description: fast defrag files in the specified directory for ext4 4 | # Author: Artem S. Tashkinov 5 | # Created at: Fri Jun 10 10:11:00 2022 6 | # 2023-04-22 13:45:01 egrep: warning: egrep is obsolescent; using grep -E 7 | # System: Linux 5.18.3-az3 on x86_64 8 | # 9 | # Copyright (c) 2022 Artem S. Tashkinov All rights reserved. 10 | # 11 | #---------------------------------------------------------------------- 12 | 13 | # Initially used this but it takes forever to complete: 14 | # 15 | # find . -xdev -type f | while read filename; do filefrag "$filename" | egrep -v ": 1 extent|: 0 extents"; done | while read line; do fname=`echo "$line" | awk -F ': ' '{print $1}'`; e4defrag -v "$fname" | grep -v "e4defrag 1"; done 16 | # 17 | # -P $(nproc) below could be too much or too little for your system - adjust accordingly 18 | 19 | isfragged() 20 | { 21 | for i in "$@"; do 22 | filefrag "$i" | grep -E -v ": 1 extent|: 0 extents" 23 | done 24 | } 25 | 26 | if [ "$1" = "isfragged" ]; then 27 | shift 28 | isfragged "$@" 29 | exit 0 30 | fi 31 | 32 | test "$UID" -ne "0" && echo "Must be run under root" && exit 0 33 | 34 | test ! -d "$1" && echo "Need a valid directory to continue" && exit 1 35 | cd "$1" || exit 2 36 | echo "Defragging [$1] ..." 37 | find . -xdev -type f -print0 | xargs -r -0 -P $(nproc) defrag isfragged | while read line; do fname=`echo "$line" | awk -F ': ' '{print $1}'`; e4defrag -v "$fname" | grep -v "e4defrag 1"; done 38 | -------------------------------------------------------------------------------- /direct: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # This is for VPN users: oftentimes you need to access certain websites directly while bypassing VPN and you don't want to disable it. 4 | # This script allows you to run any application, so that it talked to the Internet from your primary interface, not your VPN interface. 5 | # This requires https://github.com/Intika-Linux-Firewall/Bind-Interface-IP 6 | # Compile both for the i686 and x86-64 architectures and this script will work for an application regardless of its bitness 7 | # 8 | # 2022-08-23 15:20:09 bloody tired of Google who hates VPN 9 | # 2022-11-07 20:39:28 add `exec` to avoid an extra hanging bash process 10 | # 2022-12-22 10:47:51 UTC updated description 11 | 12 | ifdirect=`ip -br link show | awk '/^en/{print $1}'` 13 | ipdirect=`ip -4 -br addr show dev "$ifdirect" | awk '{print $3}' | awk -F '/' '{print $1}'` 14 | 15 | echo "Using IP adress $ipdirect ..." 16 | test -z "$1" && echo "Need a command to run :-)" && exit 1 17 | export BIND_ADDR="$ipdirect" 18 | export LD_PRELOAD="/usr/local/lib/bind.so /usr/local/lib64/bind.so" 19 | exec "$@" 20 | 21 | # This requires some preparation 22 | 23 | # 1. For rc.local 24 | ## v2 working 2020-11-24 15:34:04 25 | #direct2() 26 | #{ 27 | # grep -q "200 direct" /etc/iproute2/rt_tables || echo -e "\n# `date`\n200 direct" >> /etc/iproute2/rt_tables 28 | # ifdirect=`ip l | grep " en" | awk -F : '{print $2}' | sed 's/ //'` 29 | # if [ -n "$ifdirect" ]; then 30 | # ip rule add from 192.168.0.1/24 table direct prio 1 31 | # ip route add default via 192.168.0.1 dev "$ifdirect" table direct 32 | # fi 33 | #} 34 | 35 | # 2. For /etc/iproute2/rt_tables 36 | ## Tue Nov 24 03:24:42 PM 2020 37 | # 200 direct 38 | 39 | # 3. And lastly 40 | # https://github.com/Intika-Linux-Firewall/Bind-Interface-IP 41 | -------------------------------------------------------------------------------- /dmesg-notify: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | #---------------------------------------------------------------------- 4 | # Description: show dmesg messages on the screen for modern Linux'es 5 | # Fined tuned for lxdm-session 6 | # 7 | # Author: Artem S. Tashkinov 8 | # Created at: Tue 10 Dec 2019 03:29:57 AM 9 | # Computer: localhost.localdomain 10 | # System: Linux 5.4.2-zen2 on x86_64 11 | # 12 | # Copyright (c) 2019 Artem S. Tashkinov All rights reserved. 13 | #---------------------------------------------------------------------- 14 | 15 | icon=/path/to/icon_32x32.png 16 | timeout=3 # visual cue for this number of seconds 17 | 18 | nl=' 19 | ' 20 | 21 | journalctl -kf | while read -r line1; read -r -t 1 line2; read -r -t 1 line3; true; do 22 | 23 | test -n "$line2" && line1="$line1$nl" 24 | test -n "$line3" && line2="$line2$nl" 25 | 26 | pid_lxdm=`pidof lxdm-session` 27 | Xuser=0 # this might not work - needs to be tested 28 | test -z "$pid_lxdm" || Xuser=`ps -u --ppid "$pid_lxdm" | tail -1 | awk '{print $1}'` 29 | 30 | if [ -n "$Xuser" ]; then 31 | su "$Xuser" -c "DISPLAY=:0 notify-send --expire-time="${timeout}000" --icon="${icon}" 'Kernel message' '$line1$line2$line3' &> /dev/null" & 32 | fi 33 | 34 | done 35 | -------------------------------------------------------------------------------- /dmesg-notify-user: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | #---------------------------------------------------------------------- 4 | # Description: show dmesg messages on the screen for modern Linux'es 5 | # Fined tuned for lxdm-session 6 | # 7 | # Author: Artem S. Tashkinov 8 | # Created at: Tue 10 Dec 2019 03:29:57 AM 9 | # 2020-03-07 18:34:57 - skip FW messages 10 | # 2020-04-05 01:18:14 - fix grep taking forever to process input (add --line-buffered) 11 | # Computer: localhost.localdomain 12 | # System: Linux 5.4.2-zen2 on x86_64 13 | # 14 | # Copyright (c) 2019 Artem S. Tashkinov All rights reserved. 15 | #---------------------------------------------------------------------- 16 | 17 | icon=/usr/share/icons/gnome/48x48/status/dialog-warning.png 18 | #git 19 | #icon=/path/to/icon_32x32.png 20 | timeout=3 # visual cue for this number of seconds 21 | 22 | nl=' 23 | ' 24 | 25 | journalctl -kf | grep --line-buffered --invert-match "FW-INGRESS" | while read -r line1; read -r -t 1 line2; read -r -t 1 line3; true; do 26 | 27 | test -n "$line2" && line1="$line1$nl" 28 | test -n "$line3" && line2="$line2$nl" 29 | 30 | # pid_lxdm=`pidof lxdm-session` 31 | # Xuser=0 # this might not work - needs to be tested 32 | # test -z "$pid_lxdm" || Xuser=`ps -u --ppid "$pid_lxdm" | tail -1 | awk '{print $1}'` 33 | 34 | # if [ -n "$Xuser" ]; then 35 | # su "$Xuser" -c "DISPLAY=:0 notify-send --expire-time="${timeout}000" --icon="${icon}" 'Kernel message' '$line1$line2$line3' &> /dev/null" & 36 | # fi 37 | 38 | notify-send --expire-time="${timeout}000" --icon="${icon}" "Kernel message" "$line1$line2$line3" &> /dev/null & 39 | 40 | done 41 | -------------------------------------------------------------------------------- /findexe: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | #---------------------------------------------------------------------- 3 | # Description: find executables by name located in in the PATH 4 | # environment variable 5 | # 6 | # Author: Artem S. Tashkinov <> 7 | # Created at: Wed Jun 13 02:53:14 2001 8 | # 2001-11-03 22:22:37 Changes unknown 9 | # Sat Mar 3 18:09:56 2018 Fix grep 10 | # Tue Jan 28 18:34:45 2020 rewritten to be safe 11 | # 12 | # Copyright (c) 2004-2020 Artem S. Tashkinov. All rights reserved. 13 | #---------------------------------------------------------------------- 14 | 15 | test -z "$1" && echo "Usage is `basename "$0"` name" && exit 1 16 | 17 | echo "$PATH" | awk -F : '{ for (i=1;i<=NF;i++) print $i }' | while read -r cdir; do 18 | ls "$cdir" | grep -- "$1" 19 | done | while read -r name; do 20 | /usr/bin/which "$name" 21 | done | sort 22 | -------------------------------------------------------------------------------- /firmware-updater.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | #---------------------------------------------------------------------- 3 | # Description: updates installed firmware files 4 | # Author: Artem S. Tashkinov 5 | # Created at: Fri Jan 17 02:00:30 2025 6 | # Computer: elite 7 | # System: Linux 6.12.6-200.fc41.x86_64 on x86_64 8 | # 9 | # Copyright (c) 2025 Artem S. Tashkinov All rights reserved. 10 | # 11 | #---------------------------------------------------------------------- 12 | 13 | found=0 14 | items=0 15 | 16 | cd /lib/firmware || exit 1 17 | sdir=/tmp/linux-firmware 18 | test -n "$1" && sdir="$1" 19 | test ! -d "$sdir" && echo "$sdir doesn't exist" && exit 2 20 | test ! -f "$sdir/WHENCE" && echo "$sdir doesn't look like a directory with Linux firmware: WHENCE is missing" && exit 3 21 | 22 | while read fname; do 23 | cur=`sha256sum < $fname` 24 | new=`sha256sum 2>/dev/null < $sdir/$fname` || continue 25 | items=$((items+1)) 26 | if [ "$cur" != "$new" ]; then 27 | echo -n "Updating $fname ... " 28 | /bin/cp -a "$sdir/$fname" "$fname" && echo OK || exit 100 29 | chown 0:0 "$fname" 30 | chmod 755 "$fname" 31 | found=$((found+1)) 32 | fi 33 | done <<< "$(find . -type f)" 34 | 35 | echo "Scanned $items firmware files" 36 | test "$found" -eq 0 && echo "All the firmware is up to date" || echo "Updated $found firmware files" 37 | -------------------------------------------------------------------------------- /gamma.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | current=`xgamma 2>&1 | awk '{print $7}'` 4 | 5 | change() 6 | { 7 | new=1 8 | if [ "$1" = "+" -o "$1" = "-" ]; then 9 | new=`echo "${current}${1}0.05" | bc -l` 10 | fi 11 | xgamma -gamma "$new" 2>/dev/null 12 | xmessage -timeout 1 "New gamma value : $new" 2>/dev/null & 13 | } 14 | 15 | case $1 in 16 | "+" | "-" | "0" ) 17 | change $1 18 | ;; 19 | * ) 20 | echo "`basename $0` +/-/0" 21 | ;; 22 | esac 23 | -------------------------------------------------------------------------------- /git-file-dates: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | #---------------------------------------------------------------------- 3 | # Description: set git repository files modification dates to the date 4 | # they were pushed 5 | # Author: Artem S. Tashkinov 6 | # Created at: Tue Sep 26 14:28:52 2023 7 | # Computer: zen 8 | # System: Linux 6.5.5-zen3 on x86_64 9 | # 10 | # Copyright (c) 2023 Artem S. Tashkinov All rights reserved. 11 | # 12 | #---------------------------------------------------------------------- 13 | 14 | # otherwise bash under xargs won't find the function 15 | set -o allexport 16 | 17 | filesetdate() 18 | { 19 | unixtime=$(git log -1 --format="%at" -- "$1") 20 | touchtime=$(date -d @$unixtime +'%Y%m%d%H%M.%S') 21 | touch -t ${touchtime} "$1" 22 | } 23 | 24 | echo "Processing '$(pwd)' ..." 25 | # Let's hope there are no files/directories with newlines in their names 26 | git ls-tree -r --name-only HEAD | xargs -e -I {} -P $(nproc) bash -c 'filesetdate "{}"' 27 | echo "Done." 28 | 29 | # Or use parallel if it works (it may not if there are too many files) 30 | # parallel --keep-order --no-run-if-empty --null filesetdate "{}" 31 | -------------------------------------------------------------------------------- /git-pusher: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # A quickly hacked script to push files to GIT/github while preserving their original modification dates as git commits' dates 4 | # No licence. If you're gonna steal it, please retain this header, thanks! 5 | # Set debug to echo if you don't want to screw up everything 6 | # © 2024-05-21 12:58:00 UTC Artem S. Tashkinov 7 | # 8 | # Instructions 9 | # git init 10 | # git remote add origin git@github.com:user/repo.git 11 | # 12 | # Use it this way for all files at once: 13 | # find . -type f -not -path "*git*" -print0 | xargs -0 git-pusher 14 | # 15 | # or for individual files 16 | # git-pusher path/file 17 | 18 | set -x 19 | debug=echo 20 | 21 | for i in "$@"; do 22 | name=`echo "$i" | sed 's/\.\///'` # strip ./ from `find` output 23 | mtime=$(stat -c "%y" "$i") 24 | export GIT_AUTHOR_DATE="$mtime" 25 | export GIT_COMMITTER_DATE="$mtime" 26 | $debug git add "$name" 27 | $debug git commit -am "$name" # this sucks 28 | $debug git push --set-upstream origin master 29 | done 30 | -------------------------------------------------------------------------------- /ip-country: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # Show country code and IP address 4 | # 2018-04-25 12:37:31 - first release 5 | # 2024-08-31 20:17:00 - show real IP address as a tooltip (took 40 minutes to debug, getting old and stupid) 6 | # 2024-09-03 04:43:27 - i'm getting old and stupid 7 | # 2025-02-27 20:45:08 - ignore tun0's DOWN state 8 | 9 | fetcher() 10 | { 11 | # 🎭 👓 12 | test "$1" = "hasvpn" && note="🌍 " || note="" 13 | cleanup="cat" 14 | 15 | if [ "$1" = "direct" ]; then 16 | direct=direct 17 | cleanup="tail -n +2" 18 | fi 19 | data=`$direct curl --silent --max-time 10 https://ipapi.co/json/ 2>/dev/null | $cleanup` 20 | IP=`echo "$data" | jq .ip` 21 | country=`echo "$data" | jq .country` 22 | echo -n "$note$country:$IP" | sed 's/"//g' 23 | } 24 | 25 | tooltip() 26 | { 27 | echo "$*" 28 | } 29 | 30 | if jq --version &>/dev/null; then 31 | if [ -n "`ip l show tun0 2>/dev/null | grep DEFAULT | grep -v DOWN`" ]; then 32 | echo -n "" 33 | fetcher hasvpn 34 | echo "" 35 | tooltip `fetcher direct` 36 | else 37 | echo -n "" 38 | fetcher 39 | echo "" 40 | tooltip "Not using VPN" 41 | fi 42 | else 43 | echo "Install jq" 44 | fi 45 | 46 | echo "firefox https://myip.com" 47 | -------------------------------------------------------------------------------- /iperf3.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=iperf3 server for network throughput measurement that uses port 5201 by default 3 | 4 | After=basic.target network.target network-online.target 5 | Before=sshd.service 6 | Wants=basic.target network-online.target 7 | 8 | [Service] 9 | ExecStart=/usr/bin/iperf3 --server 10 | SyslogIdentifier=iperf3 11 | #CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SETPCAP CAP_SYS_CHROOT CAP_DAC_OVERRIDE CAP_AUDIT_WRITE 12 | #CapabilityBoundingSet=CAP_NET_ADMIN 13 | RestrictNamespaces=yes 14 | ProtectClock=true 15 | ProtectSystem=strict 16 | ProtectHostname=yes 17 | ProtectHome=yes 18 | ProtectKernelTunables=yes 19 | ProtectKernelModules=yes 20 | ProtectControlGroups=yes 21 | SystemCallFilter=@system-service 22 | SystemCallErrorNumber=EPERM 23 | NoNewPrivileges=yes 24 | PrivateTmp=yes 25 | UMask=0077 26 | RestrictAddressFamilies=AF_NETLINK AF_INET AF_INET6 27 | DeviceAllow=/dev/null rw 28 | PrivateTmp=true 29 | ProtectSystem=true 30 | ProtectHome=true 31 | DynamicUser=true 32 | Restart=always 33 | 34 | [Install] 35 | WantedBy=multi-user.target 36 | -------------------------------------------------------------------------------- /macros: -------------------------------------------------------------------------------- 1 | # 2021-05-17 17:07:50 2 | # this is for RPM/dnf updates - don't update installed files which match the appropriate files from new packages 3 | %_minimize_writes 1 4 | 5 | # 2021-12-14 15:47:01 https://bugzilla.redhat.com/show_bug.cgi?id=2032066 6 | # Locales to install 7 | %_install_langs C:en:en_US:en_US.UTF-8:ru_RU:ru_RU.UTF-8 8 | 9 | # 2022-05-08 14:28:22 10 | # Do not install /usr/share/doc documents 11 | # 2022-05-10 22:05:13 - this also prevents man pages from being installed :( 12 | # Instead use dnf install [packages] --setopt=install_weak_deps=False --setopt='tsflags=nodocs' 13 | # %_excludedocs 1 14 | -------------------------------------------------------------------------------- /message_gui_user: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | #---------------------------------------------------------------------- 4 | # Description: send a notification to a currently logged in user in X.org 5 | # Author: Artem S. Tashkinov 6 | # Created at: Thu Jan 16 22:57:14 2020 7 | # It took me three hours to write and debug it though it looks simple 8 | # Computer: zen 9 | # System: Linux 5.4.8-az2 on x86_64 10 | # 11 | # Copyright (c) 2020 Artem S. Tashkinov All rights reserved. 12 | # 13 | #---------------------------------------------------------------------- 14 | 15 | msg_icon=/path/to/icon_32x32.png 16 | 17 | test -z "$3" && echo "Usage is `basename "$0"` timeout(seconds) title message" && exit 1 18 | timeout=$1 19 | 20 | # message type modern/classic 21 | mstype=modern 22 | 23 | if ! [[ "$1" =~ ^[0-9]+$ ]]; then 24 | echo "$1 is not an integer number." 25 | exit 1 26 | fi 27 | 28 | 29 | whos() # print users whose sessions start with ":" i.e Xorg users 30 | { 31 | who | awk '{if ($2~/^:/) print}' 32 | } 33 | 34 | message() { 35 | echo "Detected user:$1 DISPLAY=$2. Attempting to show a $mstype message ..." 36 | if [ "$mstype" = "classic" ]; then 37 | su "$1" -c "DISPLAY=$2 xmessage -center -timeout $timeout -title '$3' '$4'" 38 | else 39 | su "$1" -c "DISPLAY=$2 notify-send --expire-time="${timeout}000" --icon="$msg_icon" '$3' '$4'" 40 | fi 41 | } 42 | 43 | pid_lxdm=`pidof lxdm-session` 44 | test -n "$pid_lxdm" && Xuser=`ps --no-headers -u --ppid "$pid_lxdm" | awk '{print $1}'` 45 | 46 | if [ -n "$Xuser" ]; then 47 | # LXDM version 48 | echo "LXDM session detected ..." 49 | Xdisp=`ps --no-headers -u --ppid "$pid_lxdm" | awk '{print $2}'` 50 | Xdisp=`tr '\0' '\n' < /proc/$Xdisp/environ | awk -F = '/^DISPLAY=/{print $2}'` 51 | message "$Xuser" "$Xdisp" "$2 for ${timeout}sec" "$3" 52 | elif [ -n "`whos`" ]; then 53 | # Universal version in case w does work (it doesn't for LXDM) 54 | echo "who is working :)" 55 | whos | while read Xuser Xdisp; do 56 | message "$Xuser" "$Xdisp" "$2 for ${timeout}sec" "$3" 57 | done 58 | else 59 | echo "Attemptin to show a display login manager message ..." 60 | message "root" ":0" "$2 for ${timeout}sec" "$3" 61 | fi 62 | -------------------------------------------------------------------------------- /metaclone: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | # Exit if any command fails 4 | set -e 5 | 6 | # 2012-10-03 05:51:23 7 | # a script to create a meta copy of the required directory 8 | 9 | src="$1" 10 | test -z "$src" -o "${src:0:1}" != "/" && echo "Usage is: `basename $0` directory_full_path" && exit 11 | test "$(ls "$src")" = "$(ls /)" && echo "I will not clone the root folder, sorry" && exit 12 | 13 | test ! -d "$src" && "'$src' is not a valid directory" 14 | cd "$src" 15 | 16 | tempdir=$(mktemp -d) 17 | basedir=$(basename "$src") 18 | dst=$tempdir/$basedir 19 | mkdir "$dst" 20 | 21 | mdsync() 22 | { 23 | touch --reference="$1" "$2" 24 | chown --reference="$1" "$2" 25 | chmod --reference="$1" "$2" 26 | } 27 | 28 | echo "Creating directories ..." 29 | find . -depth -type d -exec mkdir -p "$dst"/\{\} \; 30 | 31 | echo "Cloning files ..." 32 | find . -type f | while read filename; do 33 | test -f "$dst/$filename" && echo "$dst/$filename already exists. This should have never happened. Bailing out!" && exit 100 34 | fsize=`stat -c '%s' "$filename"` 35 | 36 | if [ $fsize -gt 0 ]; then 37 | fallocate -o $((fsize-1)) -l 1 "$dst/$filename" || exit 38 | fi 39 | 40 | mdsync "$filename" "$dst/$filename" 41 | done 42 | 43 | echo "Cloning directories dates/owners/accesses ..." 44 | find . -depth -type d | while read filename; do 45 | mdsync "$filename" "$dst/$filename" 46 | done 47 | 48 | echo "All done" 49 | 50 | cd "$tempdir" 51 | 52 | test -f "$basedir.tar" && echo "$basedir.tar already exists. This should have never happened. Bailing out!" && exit 100 53 | tar -Scf "$basedir.tar" "$basedir" 54 | echo 55 | echo "The resulting file can be located at $tempdir/$basedir.tar" 56 | echo "$tempdir/$basedir can be safely deleted" 57 | -------------------------------------------------------------------------------- /monitor_overclock: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # Jun 2 03:00 AM 2019 4 | 5 | cvt --help || exit 1 6 | 7 | if [ -z "$3" ]; then 8 | echo "No parameters were given (`basename "$0"` width height freq), using defaults" 9 | # Adjust according to your needs 10 | width=1920 11 | height=1080 12 | freq=74 13 | else 14 | width=$1 15 | height=$2 16 | freq=$3 17 | fi 18 | 19 | echo "Width: $width; height: $height; refresh rate: $freq" 20 | name=${width}x${height}_${freq}.00 21 | 22 | test -n "`xrandr | grep $name`" && echo "$name is already added" && exit 1 23 | 24 | # Specifically for the first monitor 25 | output=`xrandr | awk '/ connected/{print $1}' | head -1` 26 | echo "Output: $output" 27 | 28 | # Adjust according to your needs 29 | mode=`cvt $width $height $freq | grep Modeline | sed 's/Modeline //;s/"//g'` 30 | echo "Modeline: $mode" 31 | 32 | echo -n "Creating a new mode $mode ... " 33 | xrandr --newmode $mode && echo OK || exit 1 34 | 35 | echo -n "Adding mode $name to $output ... " 36 | xrandr --addmode $output $name && echo OK || exit 1 37 | 38 | echo -n "Activating mode $name for $output ... " 39 | xrandr --output $output --mode $name && echo OK 40 | 41 | echo 42 | echo "Use these two commands to undo the changes (*after* setting the normal refresh rate):" 43 | echo "xrandr --delmode $output $name" 44 | echo "xrandr --rmmode 1920x1080_74.00" 45 | -------------------------------------------------------------------------------- /mouse-acceleration-toggle: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | #---------------------------------------------------------------------- 4 | # Description: toggle or change mouse acceleration using libinput 5 | # Wed Jan 23 00:25:46 2019 : on/off argument 6 | # Author: Artem S. Tashkinov 7 | # Created at: Sun Jan 20 18:26:25 2019 8 | # 2019-01-23 00:31:42 changes unknown 9 | # 2022-10-21 01:54:22 fixes for new xinput output 10 | # Computer: localhost.localdomain 11 | # System: Linux 4.19.15-300.fc29.x86_64 on x86_64 12 | # 13 | # Copyright (c) 2019-2022 Artem S. Tashkinov All rights reserved. 14 | # 15 | #---------------------------------------------------------------------- 16 | 17 | test -z "$DISPLAY" && echo "DISPLAY is not set. Exiting" && exit -1 18 | 19 | # sed 's/.*id=\(.\).*/\1/'` - this should have never worked 20 | mouse=`xinput list | grep Mouse | grep -v Keyboard | sed 's/.*id=\([0-9]*\).*/\1/'` 21 | # was "Accel Profile Enabled (303)" - now 304, why? Let's remove the number 22 | current=`xinput --list-props "$mouse" | grep -i "Accel Profile Enabled (" | rev | cut -c -4 | rev` # alternatively tail -c 5 23 | 24 | w=`echo "$1" | tr '[:upper:]' '[:lower:]'` 25 | 26 | echo -n "[$current]" 27 | 28 | if [ "$current" = "1, 0" ]; then 29 | echo -n " Mouse acceleration is ENABLED. " 30 | test "$w" = "on" && echo "" && exit 0 31 | status="on" 32 | else 33 | echo -n " Mouse acceleration is disabled. " 34 | test "$w" = "off" && echo "" && exit 0 35 | status="off" 36 | fi 37 | 38 | if [ "$status" = "on" -o "$w" = "off" ]; then 39 | echo -n "Disabling ... " 40 | xinput --set-prop "$mouse" 'libinput Accel Profile Enabled' 0, 1 && echo OK 41 | elif [ "$status" = "off" -o "$w" = "on" ]; then 42 | echo -n "ENABLING ... " 43 | xinput --set-prop "$mouse" 'libinput Accel Profile Enabled' 1, 0 && echo OK 44 | fi 45 | -------------------------------------------------------------------------------- /nvidia-xfce-genmon: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # 2020-08-21 08:12:07 didn't sleep until 10am 4 | # 2020-08-22 03:34:07 replaced ρ with 🗘 5 | 6 | read temp draw fan driver pciev pciew loadgpu loadfb ramused pstate clocksgpu clocksmem plim < <(nvidia-smi \ 7 | --query-gpu=temperature.gpu,power.draw,fan.speed,driver_version,pcie.link.gen.current,pcie.link.width.current,utilization.gpu,utilization.memory,memory.used,pstate,clocks.gr,clocks.mem,power.limit \ 8 | --format=csv,noheader,nounits | sed 's/,//g') 9 | 10 | echo "${temp}° $(printf "%.1f" "$draw" 2>/dev/null)W 🗘 ${fan}" 11 | echo -e "GPU load: $loadgpu%\nGPU: $clocksgpu MHz\nFB load: $loadfb%\nGDDR: $clocksmem MHz\nVRAM: $ramused MiB\nPerf: $pstate\nPCI-E: v${pciev} x ${pciew}\nMax: $plim W\nDriver: $driver" 12 | echo "nvidia-settings" 13 | -------------------------------------------------------------------------------- /oneliners: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # Shows the maximum frequency of all CPU cores in the system 4 | cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq | awk '{if (max<$0) max=$0}END{printf "%.2fGHz",max/1000000}' 5 | 6 | # Maximum p7zip compression 7 | # mmt4 + 1.5GB dictionary requires 32GB of free(!) RAM 8 | 7za a -mx=9 -myx=9 -mfb=273 -bt -slp -mmt4 -md=1536m -mqs archive_name.7z [files or/and directories] 9 | 10 | # Maximum 7zz compression - requires a ton of RAM - 44GB maybe 11 | 7zz a -mx=9 -myx=9 -mfb=273 -bt -slp -mmt2 -md=3840m -mqs -stl -snl archive_name.7z [files or/and directories] 12 | 13 | # Compute md5sum for all the files and subdirectoties' files in the current directory 14 | find . -type f -print0 | xargs -0 [nocache] md5sum > output-file.md5 15 | 16 | # My favourite rsync invocation (mind the trailing slashes!) 17 | [ionice -c3] [nocache] rsync -avc --progress --preallocate --delete source/ /destination 18 | 19 | # My favourite mkfs.ext4/mke2fs invocation 20 | mkfs.ext4 -O ^huge_file,^has_journal /dev/device 21 | 22 | # My favourite pv invocation 23 | [nocache] pv -petrab /dev/source | tee /dev/destination | md5sum 24 | 25 | # Too tired of remembering all the flags required to launch Google Chrome, originally 2018-03-06 22:47:33 26 | env TZ=USA/Pacific FREETYPE_PROPERTIES=truetype:interpreter-version=35 chrome --disk-cache-dir=/tmp/.chrome-cache "$@" 27 | 28 | # Extract a cpio archive 29 | cpio -i -m -d -F "name.cpio" 30 | 31 | # Create a cpio archive 32 | find "directory" -depth | cpio -o -H crc -O "name.cpio" 33 | 34 | # Poll/watch NVIDIA GPU temperature and power consumption (with proprietary NVIDIA drivers) - e.g. for XFCE generic monitor 35 | nvidia-smi --query-gpu=temperature.gpu,power.draw --format=csv,noheader,nounits 36 | 37 | # Compile VMWare kernel modules, 2009-08-30 05:35:03 38 | vmware-modconfig --console --install-all 39 | 40 | # online ext4 defrag for the current directory and all subdirs - no idea when I wrote it, around 2017 maybe? 41 | # check the defrag script instead - works many times faster 42 | find . -xdev -type f | while read filename; do filefrag "$filename" | egrep -v ": 1 extent|: 0 extents"; done | while read line; do fname=`echo "$line" | awk -F ': ' '{print $1}'`; e4defrag -v "$fname" | grep -v "e4defrag 1"; done 43 | 44 | # dmesg show only serious messages without timestamps 45 | dmesg -t --level=alert,crit,err,warn 46 | 47 | # Build VirtualBox/NVIDIA modules with a custom kernel version 48 | KERN_VER=$VERSION make -j16 # VirtualBox 49 | SYSSRC=/lib/modules/$VERSION/source make -j16 # NVIDIA 50 | 51 | # Not one-liners 52 | # image2vvc - convert a bmp image to VVC somewhat losslessly (rgb to yuv420p10le is not lossless), 2021-04-27 19:39:38 53 | for i in "$@"; do 54 | xy=`identify "$i" | sed 's/.* \([0-9]*x[0-9]*\).*/\1/'` # ([[:digit:]]{1,5}x[[:digit:]]{1,5}) 55 | base="${i%.*}" 56 | test -f "$base.vvc" && echo "Found $base.vvc, skipping" && continue 57 | echo "File: $i Dimensions: $xy" 58 | ffmpeg -hide_banner -loglevel error -nostats -s "$xy" -i "$i" -vframes 1 -pix_fmt yuv420p10le "$base.yuv" 59 | # ffmpeg -s "$xy" -pix_fmt yuv420p10le -i "$base.yuv" -vframes 1 "$base.bmp" 60 | # --verbosity 5 61 | nice -20 ./vvencapp --input "$base.yuv" --size "$xy" --format yuv420_10 --frames 1 --preset slower --qp 0 --output "$base.vvc" 62 | rm "$base.yuv" 63 | touch -r "$i" "$base.vvc" 64 | done 65 | 66 | # vvc2bmp - decompress VVC to bmp, 2021-04-27 20:21:06 67 | for i in "$@"; do 68 | base="${i%.*}" 69 | test -f "$base.bmp" && echo "Found $base.bmp, skipping" && continue 70 | res=`./vvdecapp --bitstream "$i" --output "$base.yuv" | awk '/SizeInfo:/{print $4}'` 71 | echo "File: $i Dimensions: $res" 72 | ffmpeg -hide_banner -loglevel error -nostats -pix_fmt yuv420p10le -s "$res" -i "$base.yuv" "$base.bmp" 73 | rm "$base.yuv" 74 | done 75 | 76 | # get current date/time in a sane format, e.g. 77 | LANG=en_DK.UTF-8 date +"%x %X" 78 | 2022-08-09 02:54:42 79 | 80 | # Set git repo files modification dates/times to the time they were commited (stolen somewhere - don't remember where) 81 | git ls-tree -r --name-only HEAD | while read filename; do unixtime=$(git log -1 --format="%at" -- "${filename}"); touchtime=$(date -d @$unixtime +'%Y%m%d%H%M.%S'); touch -t ${touchtime} "${filename}"; done 82 | 83 | # What I run daily after the Sun sets: 84 | xcalib -v -blue 0.9 0 90 -alter 85 | 86 | # This is to cancel it 87 | xcalib -c 88 | 89 | # Main PC Fedora 38 nodocs packages (too fat) 90 | # 2024-01-01 16:17:36 UTC 91 | # alsa-lib annobin-docs annobin-plugin-gcc atk atk-devel bison cups-devel exo flac-devel fontconfig freeglut-devel freetype-devel gcc gdk-pixbuf2 gdk-pixbuf2-devel git git-core-doc glib2 glibc gnutls gnutls-devel goldendict lame-libs lcms2-devel libaom-devel libcurl libcurl-devel libgpg-error libicu-devel libidn2 libtiff libtiff-devel libtool libvpx-devel libxml2 libxml2-devel libxslt-devel libxslt-devel.i686 libxslt-devel.x86_64 mpfr mpg123-devel ncurses nettle-devel nspr nss openssl-devel opus-devel pango-devel pcre2-devel pcre-devel pipewire pulseaudio-libs-devel source-highlight Thunar valgrind xfce4-panel xfce4-settings xfce4-terminal xfce4-weather-plugin xfdesktop zlib-devel 92 | eatmydata dnf update --setopt='tsflags=nodocs' `cat ~/NODOCS` 93 | 94 | # Elite laptop 95 | # 2023-12-15 00:08:08 96 | # libaom-devel alsa-lib-devel annobin-docs bind-libs bison cups-devel curl expat fftw-libs-single flac-devel fontconfig freetype-devel gcc gdk-pixbuf2 gdk-pixbuf2-devel git git-core-doc glib2 glibc gnutls gnutls-devel gparted hwloc-libs krb5-devel lame-libs libcurl libgpg-error libtiff-devel libtool libtraceevent libxml2 libxslt libxslt-devel libXt libXt-devel luajit mpg123-devel ncurses nettle-devel nspr nss openssl opus-devel pcre2-devel pcre-devel pulseaudio-libs-devel ristretto SDL2 source-highlight Thunar xfce4-panel xfce4-settings xfce4-terminal xfdesktop zlib-devel 97 | 98 | # Common list 99 | # alsa-lib alsa-lib-devel annobin-docs annobin-plugin-gcc atk atk-devel bind-libs bison cups-devel curl exo expat fftw-libs-single flac-devel fontconfig freeglut-devel freetype-devel gcc gcc-c++ gdk-pixbuf2 gdk-pixbuf2-devel git git-core-doc glib2 glibc gnutls gnutls-devel goldendict gparted hwloc-libs json-glib-devel krb5-devel lame-libs lcms2-devel libaom-devel libcurl libcurl-devel libgpg-error libicu-devel libidn2 libstdc++-devel libtiff libtiff-devel libthai-devel libtool libtraceevent libvpx-devel libxml2 libxml2-devel libxslt libxslt-devel libxslt-devel.i686 libxslt-devel.x86_64 libXt libXt-devel luajit mpfr mpg123-devel ncurses nettle-devel nspr nss openssl openssl-devel opus-devel pango-devel pcre2-devel pcre-devel pipewire pulseaudio-libs-devel ristretto SDL2 source-highlight Thunar valgrind xfce4-panel xfce4-settings xfce4-terminal xfce4-weather-plugin xfdesktop zlib-devel libnotify-devel libgit2-devel libao-devel libdatrie-devel cryptsetup eb-devel gtest-devel libsndfile-devel nvme-cli openvpn python3-setuptools wireguard-tools xorg-x11-proto-devel 100 | -------------------------------------------------------------------------------- /par2create-batch: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | #---------------------------------------------------------------------- 4 | # Description: create single-file par2 recovery record for each specified file 5 | # Author: Artem S. Tashkinov 6 | # Created at: Sat Jun 30 02:02:38 2018 7 | # Computer: localhost.localdomain 8 | # System: Linux 4.17.2-ic64 on x86_64 9 | # 10 | # Copyright (c) 2018 Artem S. Tashkinov All rights reserved. 11 | # 12 | #---------------------------------------------------------------------- 13 | 14 | for i in "$@"; do 15 | echo "Processing $i ... " 16 | test -s "$i.rec.tar" && echo " PAR2 record already exists. Skipping" && continue 17 | echo -n " Creating PAR2 record ... " 18 | par2create -q -q -n1 -r1 "$i" && echo "OK" || exit 1 19 | echo -n " Verifying PAR2 record ... " 20 | par2verify -q -q "$i" && echo "OK" || exit 2 21 | md5sum "$i" "$i.par2" "$i.vol00+20.par2" > "$i.par2.md5" || exit 3 22 | touch -r "$i" "$i.par2" "$i.vol00+20.par2" "$i.par2.md5" || exit 4 23 | tar -cf "$i.rec.tar" "$i.par2" "$i.vol00+20.par2" "$i.par2.md5" || exit 5 24 | touch -r "$i" "$i.rec.tar" || exit 6 25 | /bin/rm "$i.par2" "$i.vol00+20.par2" "$i.par2.md5" || exit 7 26 | echo "Done" 27 | done 28 | -------------------------------------------------------------------------------- /perfbias.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # 2024-03-20 20:12:41 - first release 4 | # 2024-04-28 23:21:29 - simplify and improve, rewrite in pure bash 5 | 6 | # "default" is specified but doesn't work, wtf? 7 | modes='performance|balance_performance|balance_power|power' 8 | 9 | cpus=`ls /sys/devices/system/cpu/cpufreq/*/energy_performance_preference` 10 | test -z "$cpus" && echo "WTF?" && exit 1 11 | 12 | getter() 13 | { 14 | cat $cpus | sort -u 15 | } 16 | 17 | setter() 18 | { 19 | echo -n "Switching to: " 20 | echo "$1" | tee $cpus 21 | echo "Result: `getter`" 22 | } 23 | 24 | now=`getter` 25 | echo "Now: $now" 26 | 27 | if [[ "$1" =~ $modes && "$now" != "$1" ]]; then 28 | setter "$1" 29 | elif [ "$1" = "switch" ]; then 30 | if [[ "$now" =~ "power" ]]; then 31 | setter performance 32 | else 33 | setter power 34 | fi 35 | elif [ -z "$1" ]; then 36 | echo "Use is: `basename $0` [$modes|switch]" 37 | else 38 | echo "There's nothing to do" 39 | fi 40 | -------------------------------------------------------------------------------- /png2jxl: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | #---------------------------------------------------------------------- 3 | # Description: convert all PNG files in the current directory 4 | # to JXL/JPEG XL while preserving timestamps 5 | # 6 | # Author: Artem S. Tashkinov 7 | # Created at: Wed Apr 16 13:26:42 2025 8 | # Computer: zen 9 | # System: Linux 6.13.8-zen3 on x86_64 10 | # 11 | # Copyright (c) 2025 Artem S. Tashkinov All rights reserved. 12 | # 13 | #---------------------------------------------------------------------- 14 | 15 | #cjxl only supports PNG 16 | #for i in *.bmp *.png *.webp *.gif; do 17 | # output="${i%.*}.jxl" 18 | # operation "$i" "$output" 19 | #done 20 | 21 | # Let's speed it up and nice it to infinity 22 | encode() { 23 | input="$1" 24 | output="${input%.*}.jxl" 25 | nice -20 cjxl -q 100 --allow_expert_options -e 11 -v -v "$input" "$output" && touch -r "$input" "$output" 26 | } 27 | 28 | export -f encode 29 | parallel --keep-order --jobs 4 --halt now,fail=1 --color-failed --bar encode ::: *.png 30 | -------------------------------------------------------------------------------- /rescan_scsi: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | #---------------------------------------------------------------------- 3 | # Description: force rescan all SCSI/SATA endpoints 4 | # 5 | # Author: Artem S. Tashkinov <> 6 | # Created at: 2007-12-17 14:03:45 7 | # 8 | # Copyright (c) 2007 Artem S. Tashkinov. All rights reserved. 9 | #---------------------------------------------------------------------- 10 | 11 | SCSI=/sys/class/scsi_host 12 | test ! -d "$SCSI" && echo "Error: cannot find the $SCSI directory." && exit 1 13 | cd "$SCSI" || exit 1 14 | 15 | for i in *; do 16 | echo -n "Scanning $i ..." 17 | echo "- - -" > $i/scan && echo " done." 18 | done 19 | 20 | echo "Finished. Consult with 'dmesg' for details." 21 | -------------------------------------------------------------------------------- /run_via_cgroup: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | systemd-run --user --scope "$@" 3 | -------------------------------------------------------------------------------- /ryzen-powersave.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # 2023-08-07 00:46:52 - created 4 | # 2023-08-10 14:15:30 - lots more limits 5 | # 2023-08-12 20:11:58 - let's add a delay because --power-saving doesn't always work otherwise 6 | # 2023-08-21 20:16:20 - tune for console and rc.d 7 | # 2023-11-02 09:25:35 - setting for running on battery power 8 | # 2023-11-16 15:45:27 - rewrite 9 | # 2023-11-24 20:06:52 - dump the defaults to /dev/shm/AMD 10 | # 2023-11-29 09:53:29 - remove --power-saving, it breaks the CPU up on resume 11 | # 2023-12-29 22:25:19 - restore --power-saving, firmware is broken regardless 12 | # 2024-02-11 18:00:14 - since 6.7 /sys/class/graphics/fb0 is missing, adjusting accordingly 13 | # 2024-03-18 01:10:36 - drop wattage limits, temperature is enough 14 | delay=60 15 | 16 | binary=/usr/local/bin/ryzenadj 17 | 18 | gpupower() 19 | { 20 | gpl=device/power_dpm_force_performance_level 21 | gpudev=/sys/class/graphics/fb0/$gpl 22 | test -f $gpudev || gpudev=/sys/class/drm/card1/$gpl 23 | test -f $gpudev || gpudev=/sys/class/drm/card0/$gpl 24 | if [ -f $gpudev ]; then 25 | echo "$1" > $gpudev && ( echo -n "Setting GPU performance to: " && cat $gpudev ) || echo "Failed!" 26 | else 27 | echo "iGPU [ $gpudev ] is missing!" 28 | fi 29 | } 30 | 31 | conf() 32 | { 33 | if [ -z "$1" ]; then 34 | echo "Mains settings:" 35 | $binary --tctl-temp=80 --power-saving 2>&1 | grep -v SMU 36 | gpupower auto 37 | elif [ "$1" = "battery" ]; then 38 | echo "Battery settings:" 39 | $binary --tctl-temp=70 --power-saving --stapm-limit=15000 --fast-limit=15000 --slow-limit=10000 2>&1 | grep -v SMU 40 | echo "Enabling GPU low power, this limits RAM speed!" 41 | gpupower low 42 | elif [ "$1" = "fast" ]; then 43 | echo "Fast and hot settings:" 44 | $binary --tctl-temp=85 2>&1 | grep -v SMU 45 | gpupower auto 46 | fi 47 | 48 | true 49 | } 50 | 51 | test -f /dev/shm/AMD || $binary --info &> /dev/shm/AMD 52 | 53 | echo "Enabling power savings via ryzenadj and /sys ..." 54 | conf "$1" 55 | 56 | if [ "$TERM" = "linux" -o "$TERM" = "xterm-256color" ]; then 57 | : 58 | else 59 | echo "Second attempt in $delay seconds ..." 60 | ( sleep $delay; conf ) & 61 | fi 62 | -------------------------------------------------------------------------------- /tar_sorted: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | #---------------------------------------------------------------------- 4 | # Description: Create tar archive with files sorted by extension 5 | # Author: Artem S. Tashkinov 6 | # Created at: 2010-06-15 18:51:19 7 | # 2014-04-23 02:53:33 (--numeric-owner) 8 | # 2010-07-18 22:27:07 unknown fixes 9 | # 2019-09-10 16:21:14 fixed "if" - must have used "test" 10 | # 2023-02-23 14:05:52 a fix for filenames containing '\' - should this be applied to directories names as well? 11 | # 2023-07-02 14:21:04 simplify 12 | # Computer: localhost.localdomain. 13 | # System: Linux 2.6.33.4-ic on i686 14 | # 15 | # Copyright (c) 2010 Artem S. Tashkinov All rights reserved. 16 | #---------------------------------------------------------------------- 17 | 18 | # set -x 19 | 20 | [ ! -d "$1" ] && echo "Gimme an existing directory name" >&2 && exit 1 21 | [ -z "$2" ] && echo "Give me a destination filename" >&2 && exit 2 22 | [ -f "$2" ] && echo "[$2] already exists!" >&2 && exit 3 23 | 24 | [ -n "`find "$1" -iname '*|*'`" ] && echo "birdie is stupid :( - there are files with '|' in their names! This won't work." >&2 && exit 4 25 | 26 | find_files() 27 | { 28 | #find "$1"-type f | sed 's/^\.\///;s/\(..*\)\./\1\|/' | sort -t '|' -k2 | sed 's/|/\./' 29 | # Thanks to Neil Moore aka \ametyst at irc://irc.freenode.net/bash 30 | find "$1" ! -type d | sed -e 's,\(.*\)/,\1|,' -e 's,\(.*|..*\)\.,\1|,' | sort -i -t\| -k 3,3 -k 2,2 --buffer-size=1G | sed -e 's,|,/,' -e 's,|,.,' | sed 's/\\/\\\\/g' 31 | test "${PIPESTATUS[0]}" != "0" && return 1 32 | find "$1" -depth -type d || return 1 # Saving directories timestamps, you SHOULD run tar with --no-recursion option 33 | } 34 | 35 | filelist=`mktemp` || exit 5 36 | find_files "$1" > "$filelist" || exit 6 37 | 38 | tar --no-recursion --files-from="$filelist" -acf "$2" # --numeric-owner 39 | /bin/rm "$filelist" 40 | -------------------------------------------------------------------------------- /uniunpack: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | # Universal Unpacker by Artem S. Tashkinov 4 | # v1.4.0 Oct 18 15:35 2014: total rewrite; proper gzip support; no command output :( 5 | # v1.4.1 Oct 26 13:20 2014: file extension normalized 6 | # v1.4.2 Nov 4 04:29 2014: use RAR by default, use -r- for RAR, BASH->SH 7 | # v1.4.3 Mar 7 22:02 2015: use UNRAR by default 8 | # v1.4.4 Tue 04 Nov 2019 01:18:27: prefer p7zip for zip archives 9 | # v1.4.5 Fri Oct 22 2021 15:38:59: zst 10 | 11 | test -z "$1" && echo "Need an archive name to proceed" && exit 1 12 | test ! -e "$1" && echo "File '$1' doesn't exist" && exit 1 13 | 14 | lower=$(echo "$1" | tr A-Z a-z) 15 | 16 | case "$lower" in 17 | *.tar|*.tbz|*.txz|*.tgz|*.tar.bz2|*.tar.gz|*.tar.xz|*.tar.lz|*.tar.zst) 18 | tar xf "$1" 19 | ;; 20 | *.rar) 21 | unrar x -r- "$1" 22 | ;; 23 | *.7z) 24 | 7z x "$1" 25 | ;; 26 | *.bz2) 27 | bzip2 -kd "$1" 28 | ;; 29 | *.gz) 30 | gunzip -k "$1" 31 | ;; 32 | *.xz) 33 | xz -dk "$1" 34 | ;; 35 | *.zip) 36 | if 7z &> /dev/null; then 37 | 7z x "$1" 38 | else 39 | unzip "$1" 40 | fi 41 | ;; 42 | *.cpio) 43 | cpio -i -m -d -F "$1" 44 | ;; 45 | *.zst) 46 | zstd -d "$1" # --keep/-k is default 47 | ;; 48 | *) 49 | echo "The archive type for '$1' is unknown" 50 | echo -n "Would you like to try unpacking the archive using 7z (y/n)? " 51 | read answer 52 | test "$answer" = "Y" -o "$answer" = "y" && 7z x "$1" 53 | ;; 54 | esac 55 | 56 | res=$? 57 | test "$res" -ne 0 && echo "WARNING: Unpacking failed!" 58 | exit "$res" 59 | -------------------------------------------------------------------------------- /vbox_system_qt: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | #---------------------------------------------------------------------- 3 | # Description: replace VirtualBox own Qt libraries with system ones 4 | # Author: Artem S. Tashkinov 5 | # Created at: Thu Apr 16 03:44:38 2020 6 | # Computer: zen 7 | # System: Linux 5.6.3-az2 on x86_64 8 | # 9 | # Copyright (c) 2020 Artem S. Tashkinov All rights reserved. 10 | # 11 | #---------------------------------------------------------------------- 12 | 13 | test "$UID" -ne "0" && echo "Must be run under root" && exit 0 14 | 15 | cd /usr/lib/virtualbox || exit 1 16 | 17 | mkdir -p qt || exit 2 18 | for i in libQt*; do 19 | echo -n "Processing $i ... " 20 | test -L "$i" && echo "already processed" && continue 21 | mv -i "$i" qt || exit 3 22 | base=`echo "$i" | sed 's/VBox//'` 23 | ln -s "/usr/lib64/$base" "/usr/lib/virtualbox/$i" || exit 4 24 | echo " OK" 25 | done 26 | 27 | test -f qt.conf && mv -i qt.conf qt 28 | test -d plugins && mv -i plugins qt 29 | 30 | echo 31 | echo "Finished" 32 | echo "The original VirtualBox Qt libraries are at /usr/lib/virtualbox/qt" 33 | -------------------------------------------------------------------------------- /watch_raw_io: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | #---------------------------------------------------------------------- 4 | # Description: watch raw input output per block device stats in real time 5 | # Author: Artem S. Tashkinov 6 | # Created at: Sat Apr 6 18:34:33 2013 7 | # Computer: localhost.localdomain 8 | # System: Linux 3.8.7-ic on i686 9 | # 10 | # Copyright (c) 2013 Artem S. Tashkinov All rights reserved. 11 | # Sat Aug 24 19:59:32 2013 - implemented read/write interval counters 12 | # Sun Aug 25 23:51:14 2013 - made refreshing three times faster 13 | # Thu Feb 4 18:59:41 2016 - exit on pressing q/Q, read refresh rate from console 14 | # 2020-03-27 18:04:40 - fix finding the root in /proc/cmdline - remove an extra space, add -w instead 15 | # - sort devices numerically 16 | # 2021-10-23 04:51:04 - add nvme support 17 | # 18 | #---------------------------------------------------------------------- 19 | 20 | shopt -s nullglob 21 | export LANG=en_US.UTF-8 22 | rr=2 23 | 24 | trap "exit 0" TERM 25 | export TOP_PID=$$ 26 | 27 | qexit() 28 | { 29 | read -n 1 -t 0.5 -s char 30 | if [ "$char" = "q" -o "$char" = "Q" ]; then 31 | echo "Bye" 32 | kill -s TERM $TOP_PID 33 | fi 34 | } 35 | 36 | if [ -n "$1" ]; then 37 | if [ "$1" -eq "$1" ] 2>/dev/null; then 38 | rr="$1" 39 | else 40 | echo "[$1] is not a number, setting refresh rate to $rr" 41 | sleep 3 42 | fi 43 | fi 44 | 45 | dsrc=/sys/block 46 | cd $dsrc || die "$dsrc not found" 47 | 48 | declare -A intr intw blkr blkw 49 | 50 | while :; do 51 | clear 52 | date | tr -d '\n' 53 | echo " [ refresh rate: ${rr} seconds ]" 54 | echo "Device Read bytes Written bytes Interval read Interval written Mount point" 55 | for i in sd* md* nvme*; do # adjust for your environment 56 | # for j in $i/$i* $i; do 57 | for j in $i/$i[0-9] $i/$i[0-9][0-9] $i/$i[0-9][0-9][0-9] $i/nvme*n*p* $i; do # at most 999 partitions 58 | dev=${j##*/} 59 | test -z "$dev" && dev="$j" # when iterating $i itself 60 | 61 | read blkr blkw < <(awk '{print $3" "$7}' $j/stat) # let's speed up this a bit 62 | blkr["$dev"]=$blkr 63 | blkw["$dev"]=$blkw 64 | 65 | test -z "${intr[$dev]}" && intr[$dev]=0 66 | test -z "${intw[$dev]}" && intw[$dev]=0 67 | intr[$dev]=$((${blkr[$dev]}-${intr[$dev]})) 68 | intw[$dev]=$((${blkw[$dev]}-${intw[$dev]})) 69 | test "${blkr[$dev]}" == "${intr[$dev]}" && intr[$dev]=0 # Don't show when iterating for the first time 70 | test "${blkw[$dev]}" == "${intw[$dev]}" && intw[$dev]=0 71 | 72 | printf "%-10s %'17d %'17d %'14d %'14d " $dev $((blkr[$dev]*512)) $((blkw[$dev]*512)) $((intr[$dev]*512)) $((intw[$dev]*512)) 73 | 74 | mntp=`awk '/^\/dev\/'$dev' /{print $2}' /proc/mounts` 75 | if [ -z "$mntp" ]; then # FIXME: this won't work for partitons mounted via UUID/LABEL 76 | grep -qw "root=/dev/$dev" /proc/cmdline && mntp="/" 77 | fi 78 | echo "$mntp" 79 | 80 | intr["$dev"]=${blkr["$dev"]} 81 | intw["$dev"]=${blkw["$dev"]} 82 | done 83 | echo 84 | done 85 | qexit < /dev/stdin & 86 | sleep $rr 87 | done 88 | -------------------------------------------------------------------------------- /watts_cpu: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | #---------------------------------------------------------------------- 4 | # Description: show CPU package power consumption 5 | # Author: Artem S. Tashkinov 6 | # Created at: 2019-09-04 14:17:52 7 | # 2020-05-17 21:55:38 - output GPU wattage as well 8 | # 2022-04-14 09:24:31 - disable GPU wattage, don't show emoji for console 9 | # 2023-08-10 14:17:38 - adjust for updated AMD GPU sensors output 10 | # 11 | # Copyright (c) 2019-2023 Artem S. Tashkinov All rights reserved. 12 | # 13 | #---------------------------------------------------------------------- 14 | 15 | test ! -e /dev/cpu/0/msr && echo "MSR module not loaded. Exiting" && exit 1 16 | test ! -r /dev/cpu/0/msr && echo "No permissions to read MSR. Exiting" && exit 2 17 | 18 | interval=1 19 | test -n "$1" && interval=$1 20 | # only show emoji for a graphical session 21 | ECPU="CPU " 22 | EGPU="GPU " 23 | test "$TERM" = "dumb" && ECPU="⚙️" && EGPU="🕹️" 24 | echo -n " $ECPU" 25 | turbostat --quiet --num_iterations 1 --interval "$interval" --show PkgWatt --Summary | tail -1 | tr -d '\n' 26 | echo -n "W" 27 | 28 | echo -n " $EGPU" 29 | sensors 'amdgpu-pci-*' 2>/dev/null | awk '/power|PPT/{print $2 "W"}' 30 | -------------------------------------------------------------------------------- /xsession-errors: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # This works in modern Linux distros and under Wayland as well 4 | journalctl _UID=`id --user` 5 | -------------------------------------------------------------------------------- /zip2date: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # apply zip contents time to the zip files itself 4 | # Sat Dec 30 20:29:31 2017 5 | 6 | for i in "$@"; do 7 | if [ -n "`file "$i" 2>&1 | grep 'Zip archive data'`" ]; then 8 | nd=`unzip -l "$i" | awk '/[0-9][0-9]-..-..../{print $2" "$3}' | sort -n | tail -1 | sed 's/\(..\)-\(..\)-\(....\) \(..\):\(..\)/\3\1\2\4\5/'` 9 | echo "File '$i', date $nd" 10 | touch -t "$nd" "$i" 11 | else 12 | echo "Skipping file '$i': not a zip file" 13 | fi 14 | done 15 | --------------------------------------------------------------------------------