├── .flake8 ├── .gitignore ├── .lgtm.yml ├── .mypy.ini ├── 7zcp.sh ├── LICENSE.txt ├── OSdetect.py ├── README.md ├── absolute_path_of_bash_script.sh ├── archive_bash ├── findtext.sh ├── findtext_printmatch.sh ├── findvid.sh ├── latexdiff_recursive.sh ├── tarcp.sh └── xzcp.sh ├── bash_time_per_line.sh ├── calc-utils ├── 10log10 ├── log10 └── sin ├── checkIP.sh ├── cmake_setup.sh ├── compare_binary.py ├── count_bash_forks.sh ├── cvpn ├── diffFN.sh ├── diffdir.py ├── diffdir.sh ├── diskfree_sigterm.py ├── dvpn ├── eps2pdf.sh ├── findbig ├── getIP.py ├── getIP.sh ├── getIP_curl.py ├── id2email.py ├── jpg2pdf.sh ├── mU ├── mX ├── memfree.py ├── monDMC ├── pdf2png.sh ├── pdfocr.sh ├── png2pdf.sh ├── pybashutils ├── ChDir.py ├── __init__.py ├── getfreeport.py ├── logdemo.py ├── os_detect.py ├── tests │ ├── test_mod.py │ └── test_unix.py └── ulimit.py ├── pydeptree.sh ├── pyproject.toml ├── recursive_sed.sh ├── ren.sh ├── setup.cfg ├── setup.py ├── shredFind.sh ├── symlink_permissions.py ├── tarcp.py ├── uU ├── uX ├── ulimit_nofile.py ├── update-compilers.sh └── xrandr_force.sh /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 132 3 | exclude = .git,__pycache__,.eggs/,doc/,docs/,build/,dist/,archive/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .eggs/ 2 | .coverage 3 | htmlcov/ 4 | .mypy_cache/ 5 | .pytest_cache/ 6 | .cache/ 7 | *.svg 8 | testdep/ 9 | 10 | build/ 11 | dist/ 12 | *.egg-info/ 13 | __pycache__/ 14 | *.pyc 15 | __pycache__/* 16 | *.pyd 17 | *.egg-info 18 | build/ 19 | 20 | # Compiled Object files 21 | *.slo 22 | *.lo 23 | *.o 24 | *.obj 25 | 26 | # Precompiled Headers 27 | *.gch 28 | *.pch 29 | 30 | # Compiled Dynamic libraries 31 | *.so 32 | *.dylib 33 | *.dll 34 | 35 | # Fortran module files 36 | *.mod 37 | 38 | # Compiled Static libraries 39 | *.lai 40 | *.la 41 | *.a 42 | *.lib 43 | 44 | # Executables 45 | *.exe 46 | *.out 47 | *.app 48 | -------------------------------------------------------------------------------- /.lgtm.yml: -------------------------------------------------------------------------------- 1 | extraction: 2 | python: 3 | python_setup: 4 | version: 3 5 | -------------------------------------------------------------------------------- /.mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | files = . 3 | 4 | ignore_missing_imports = True 5 | strict_optional = False 6 | allow_redefinition = True 7 | -------------------------------------------------------------------------------- /7zcp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Michael Hirsch 2013 3 | # 7zip's files to a directory 4 | 5 | : ${2?example: 7zcp ~/inDir /media/outDir} 6 | 7 | MainDir=$1 8 | OutDir=$2 9 | 10 | mkdir -p $OutDir 11 | 12 | #list ONLY 1st level subdirectories 13 | dlist=($(find "$MainDir" -maxdepth 1 -mindepth 1 -type d | sort)) 14 | echo "Found directories: ${dlist[*]}" 15 | 16 | #main loop 17 | for din in ${dlist[*]}; do 18 | cout="$OutDir/$(basename $din).7z" 19 | 20 | # no clobber 21 | [[ -a $cout ]] && { echo "Skipping $din"; continue; } 22 | 23 | echo "7zipping and moving $din to $cout" 24 | 25 | # don't use the -o option, it doesn't seem to work right 26 | 7z a -t7z -mx=3 -mmt=on -m0=lzma2 "$cout" "$din" 27 | 28 | done 29 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Michael Hirsch 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /OSdetect.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Demo for Os class, allowing fine-grained Windows Cygwin/WSL OS detection not available in Python standard library 4 | """ 5 | 6 | from pybashutils.os_detect import Os 7 | 8 | print(Os()) 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python and Shell utility scripts 2 | 3 | [![Zenodo](https://zenodo.org/badge/DOI/10.5281/zenodo.1252220.svg)](https://zenodo.org/record/1252220) 4 | [![PyPi Download stats](http://pepy.tech/badge/pybashutils)](http://pepy.tech/project/pybashutils) 5 | 6 | Collection of Bash and Python scripts I've made that may be generally 7 | useful 8 | 9 | function | description 10 | -----------------|------------------------------------------------------------- 11 | checkIP | Sends you an email automatically if your IP address changes 12 | getIP | gets your public IP address (not the internal NAT address) 13 | mx | mount network share example using SSHFS 14 | memfree | Estimates available RAM 15 | doc2pdf | convert .doc, .docx, .rtf to PDF using LibreOffice 16 | 17 | ## Usage 18 | 19 | ### SSHFS mount/unmount 20 | 21 | 1. Mounting the "U" network drive at Boston University over SSHFS 22 | (slight modifications to the script allow using this anywhere) 23 | 24 | one time setup: 25 | 26 | mkdir ~/U 27 | 28 | 2. mount U drive to your PC, like "mounting a network drive" in 29 | Windows, here we assume the BU username is `jdoe`: 30 | 31 | mU jdoe 32 | 33 | and your network drive is available as ~/U 34 | 35 | 3. Unmounting the "U" drive. When done for the day, suggest unmounting 36 | in case to help mitigate security risks: 37 | 38 | uU 39 | 40 | Note: if you have any files open (like say a spreadsheet on the `~/U` 41 | drive), `~/U` will stay connected until you close that file(s). 42 | 43 | --- 44 | 45 | Get Public IP address 46 | 47 | ```sh 48 | python getIP.py 49 | ``` 50 | -------------------------------------------------------------------------------- /absolute_path_of_bash_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo $(realpath $(dirname ${BASH_SOURCE[0]})) 4 | -------------------------------------------------------------------------------- /archive_bash/findtext.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | maxFileSize=10M #don't search inside huge binary files 4 | 5 | case $# in 6 | 1) 7 | srchText=$1 8 | fDir="." 9 | fntmp="*" ;; 10 | 2) 11 | fDir="." 12 | srchText=$1 13 | fntmp=$2 ;; 14 | 3) 15 | fDir=$1 16 | srchText=$2 17 | fntmp=$3 ;; 18 | *) 19 | echo usage: "findtext [DIR] TEXT [FILENAME]" 20 | echo searches for TEXT under DIR and echos back filenames 21 | exit ;; 22 | esac 23 | 24 | # dont use -execdir, or you wont see which directory the found files are in! 25 | find $fDir -type f -name "$fntmp" -size -$maxFileSize \ 26 | -exec grep --ignore-case --files-with-matches "$srchText" {} + 27 | -------------------------------------------------------------------------------- /archive_bash/findtext_printmatch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | maxFileSize=10M #don't search inside huge binary files 4 | 5 | case $# in 6 | 1) 7 | srchText=$1 8 | fDir="." 9 | fntmp="*" ;; 10 | 2) 11 | fDir="." 12 | srchText=$1 13 | fntmp=$2 ;; 14 | 3) 15 | fDir=$1 16 | srchText=$2 17 | fntmp=$3 ;; 18 | *) 19 | echo usage: "findtext [DIR] TEXT [FILENAME]" 20 | echo searches for TEXT under DIR and echos back filenames 21 | exit ;; 22 | esac 23 | 24 | # dont use -execdir, or you wont see which directory the found files are in! 25 | find $fDir -type f -name "$fntmp" -size -$maxFileSize \ 26 | -exec grep -A1 --color -n --ignore-case "$srchText" {} + 27 | -------------------------------------------------------------------------------- /archive_bash/findvid.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ $# -eq 0 ]]; then 4 | fDir="." 5 | elif [[ $# -eq 1 ]]; then 6 | fDir=$1 7 | else 8 | echo usage: "findvid [DIR]" 9 | exit 0 10 | fi 11 | 12 | find $fDir -regextype posix-egrep \ 13 | -type f \ 14 | -iregex '.*\.(avi|mov|mp4|mpg|mpeg|webm|ogv|mkv|wmv)$' 2>/dev/null 15 | -------------------------------------------------------------------------------- /archive_bash/latexdiff_recursive.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Runs latexdiff on every file in a directory 3 | # 4 | # Cygwin prereqs: 5 | # texlive-collection-bibtexextra texlive-collection-binextra perl 6 | # 7 | # Michael Hirsch 8 | 9 | old=$1 10 | new=$2 11 | main=$3 #optional for compilation 12 | 13 | flist=$(find -H $old -maxdepth 1 -type f -name "*.tex") 14 | 15 | 16 | for f in ${flist[*]}; do 17 | b=${f##*/} #$(basename $f) 18 | latexdiff $old/$b $new/$b > /tmp/$b 19 | done 20 | 21 | echo "output to /tmp" 22 | 23 | if [[ -n $main ]]; then 24 | ( 25 | cd /tmp 26 | pdflatex $main 27 | bibtex $main 28 | pdflatex $main 29 | pdflatex $main 30 | ) 31 | fi 32 | -------------------------------------------------------------------------------- /archive_bash/tarcp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Michael Hirsch 3 | # tars (w/o compressing) and moves files per directory 4 | : ${2?example: tarcp ~/inDir /media/outDir} 5 | 6 | MainDir=$1 7 | OutDir=$2 8 | 9 | mkdir -p $OutDir 10 | 11 | #list ONLY 1st level subdirectories 12 | dlist=($(find "$MainDir" -maxdepth 1 -mindepth 1 -type d | sort)) 13 | echo "Found directories: ${dlist[*]}" 14 | 15 | #main loop 16 | for din in "${dlist[@]}"; do 17 | cout="$OutDir/$din.tar" 18 | echo "tarring $din to $cout" 19 | tar cf "$cout" "$din" 20 | done 21 | 22 | echo "extract with tar xvf --strip-components=1 *.tar to get rid of . dot directories" 23 | 24 | # below (actually wasn't) better for 100,000 files per directory since {} + acts like xargs 25 | # UPDATE: I have tried code below and it is no faster than tar itself 26 | #find "$currDir" -type f -name "*" \ 27 | # -execdir tar --append --file="$currTarOut" --directory="$currDir" {} + 28 | -------------------------------------------------------------------------------- /archive_bash/xzcp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Michael Hirsch 3 | # tars, XZ, and moves files per directory 4 | : ${2?example: tarcp ~/inDir /media/outDir} 5 | 6 | MainDir=$1 7 | OutDir=$2 8 | 9 | mkdir -p $OutDir 10 | 11 | #list ONLY 1st level subdirectories 12 | dlist=($(find "$MainDir" -maxdepth 1 -mindepth 1 -type d | sort)) 13 | echo "Found directories: ${dlist[*]}" 14 | 15 | #main loop 16 | for din in "${dlist[@]}"; do 17 | cout="$OutDir/$din.tar.xz" 18 | echo "tarring $din to $cout" 19 | XZ_OPT=-0 tar cJf "$cout" "$din" 20 | done 21 | 22 | echo "extract with tar xvf --strip-components=1 *.tar to get rid of . dot directories" 23 | 24 | # below (actually wasn't) better for 100,000 files per directory since {} + acts like xargs 25 | # UPDATE: I have tried code below and it is no faster than tar itself 26 | #find "$currDir" -type f -name "*" \ 27 | # -execdir tar --append --file="$currTarOut" --directory="$currDir" {} + 28 | -------------------------------------------------------------------------------- /bash_time_per_line.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | # monitor time for each line of script (needs #!/bin/bash -x) 3 | # http://stackoverflow.com/questions/4336035/performance-profiling-tools-for-shell-scripts 4 | 5 | PS4='$(date "+%s.%N ($LINENO) + ")' 6 | 7 | X=10 8 | for i in $(seq 1 $X); do #fastest way, forks onces 9 | #for i in $(eval echo {1..$X}); do #medium speed, forks once 10 | #for ((i=1;i<=$X;i++));do #slowest way, does not fork 11 | : #no-op 12 | done 13 | 14 | -------------------------------------------------------------------------------- /calc-utils/10log10: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import math 4 | 5 | val = float(sys.argv[1]) 6 | print(10*math.log10(val)) 7 | -------------------------------------------------------------------------------- /calc-utils/log10: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import math 4 | 5 | val = float(sys.argv[1]) 6 | print(math.log10(val)) 7 | -------------------------------------------------------------------------------- /calc-utils/sin: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import math 4 | 5 | val = float(sys.argv[1]) 6 | print(math.sin(val)) 7 | -------------------------------------------------------------------------------- /checkIP.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Hirsch, Ph.D. 4 | # sends an email when an IP address change is detected (use with cron) 5 | # https://scivision.dev/get-email-upon-change-of-ip-address/ 6 | # 7 | # Note: Most home ISPs block port 25 STMP, you would need to use port 587. 8 | # Check /var/log/mail.log to see if you're getting blocked from a simple 9 | # echo "testing" | mail -s test my@email.address 10 | # 11 | # Setup: 12 | # ------- 13 | # 1. install a simple mail server: 14 | # apt install mailutils 15 | # 16 | # 2. add to top of your crontab by "crontab -e": 17 | # SHELL=/bin/bash 18 | # PATH=/sbin:/bin:/usr/sbin:/usr/bin 19 | # 20 | # 3. add via "crontab -e" this script. I choose to run at reboot and hourly by: 21 | # @reboot ~/code/pybashutils/checkIP.sh my@email.address 22 | # @hourly ~/code/pybashutils/checkIP.sh my@email.address 23 | 24 | set -u 25 | emailaddr=$1 26 | 27 | CurIP=$(hostname -I | tr -d [:space:]) 28 | 29 | OldIP=$(tr ' ' '\n' < ~/.current_ip) #space to \n for consistency 30 | 31 | if [[ ${CurIP} != ${OldIP} ]]; then 32 | echo -e "IP change detected\n $CurIP \n $OldIP" 33 | echo "New IP address: ${CurIP} (old address: ${OldIP} )" | mail -s "IP address change: $(hostname)" "$emailaddr" 34 | echo -e "${CurIP}" > ~/.current_ip 35 | fi 36 | -------------------------------------------------------------------------------- /cmake_setup.sh: -------------------------------------------------------------------------------- 1 | # This file moved to https://github.com/scivision/cmake-utils 2 | -------------------------------------------------------------------------------- /compare_binary.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | compare binary output files 4 | """ 5 | import filecmp 6 | from pathlib import Path 7 | from argparse import ArgumentParser 8 | 9 | 10 | def compbin(dir1: Path, dir2: Path, pat: str): 11 | dir1 = Path(dir1).expanduser() 12 | dir2 = Path(dir2).expanduser() 13 | 14 | fl1 = dir1.glob(pat) 15 | fl2 = dir2.glob(pat) 16 | 17 | for f, g in zip(fl1, fl2): 18 | if not filecmp.cmp(f, g, False): # type: ignore 19 | print("difference:", f.name) 20 | 21 | 22 | def main(): 23 | p = ArgumentParser() 24 | p.add_argument("dirs", help="dir1 dir2 to compare files matching pat", nargs=2) 25 | p.add_argument("pat", help="filename pattern") 26 | P = p.parse_args() 27 | 28 | compbin(P.dirs[0], P.dirs[1], P.pat) 29 | 30 | 31 | if __name__ == "__main__": 32 | main() 33 | -------------------------------------------------------------------------------- /count_bash_forks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # count number of times script forked 3 | # http://stackoverflow.com/questions/17718508/how-to-count-number-of-forked-sub-processes 4 | 5 | 6 | fork=0 7 | set -o monitor 8 | trap "((++fork))" CHLD 9 | 10 | 11 | X=10 12 | for i in $(seq 1 $X); do #fastest way, forks onces 13 | #for i in $(eval echo {1..$X}); do #medium speed, forks once 14 | #for ((i=1;i<=$X;i++));do #slowest way, does not fork 15 | : #no-op 16 | done 17 | 18 | 19 | echo "this script forked $fork times" 20 | 21 | -------------------------------------------------------------------------------- /cvpn: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | dvpn 4 | sudo vpnc-connect $1 5 | getIP 6 | -------------------------------------------------------------------------------- /diffFN.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Michael Hirsch 3 | # 4 | # compares directory filenames, based on filename only--ignores file contents 5 | # since some systems don't have "tree" 6 | # handles spaces in filenames 7 | # a lot faster than checking file contents, as a preliminary check of 8 | # massive file systems 9 | # 10 | # must specify full path: NOT ~ or $HOME 11 | # EXAMPLE 12 | # diffFN /home/me/dir1 /home/me/dir2 13 | 14 | [[ $# -ne 2 ]] && { echo "diffFN DIR1 DIR2"; exit 1; } 15 | 16 | #remove single trailing slash, if present 17 | d1=${1%/} 18 | d2=${2%/} 19 | 20 | #count files, removing root directory name, sorting to keep out false differences 21 | dir1list=($(find $d1 -type f 2>/dev/null | sed "s,^${d1}/,," | sort)) 22 | dir2list=($(find $d2 -type f 2>/dev/null | sed "s,^${d2}/,," | sort)) 23 | 24 | echo "${#dir1list[@]} files in $d1" 25 | echo "${#dir2list[@]} files in $d2" 26 | 27 | #make each element of sorted array appear on its own line 28 | diff <(printf "%s\n" "${dir1list[@]}") <(printf "%s\n" "${dir2list[@]}") 29 | -------------------------------------------------------------------------------- /diffdir.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Pure Python stdlib directory comparison by filename 4 | """ 5 | from pathlib import Path 6 | import filecmp 7 | from argparse import ArgumentParser 8 | 9 | 10 | def diff_dir(path1: Path, path2: Path): 11 | path1 = Path(path1).expanduser() 12 | path2 = Path(path2).expanduser() 13 | 14 | if path1.samefile(path2): 15 | raise OSError(f"you are comparing {path1} with itself!") 16 | 17 | diff = filecmp.dircmp(path1, path2) # type: ignore 18 | print("\n".join(diff.diff_files), end="") # type: ignore 19 | print("\n".join(diff.left_only), end="") # type: ignore 20 | print("\n".join(diff.right_only), end="") # type: ignore 21 | # %% fix exit prompt position 22 | if any([diff.diff_files, diff.left_only, diff.right_only]): 23 | print() 24 | 25 | 26 | def main(): 27 | p = ArgumentParser() 28 | p.add_argument("path1", help="first path to compare") 29 | p.add_argument("path2", help="second path to compare") 30 | P = p.parse_args() 31 | 32 | diff_dir(P.path1, P.path2) 33 | 34 | 35 | if __name__ == "__main__": 36 | main() 37 | -------------------------------------------------------------------------------- /diffdir.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # consider Python dircmp, filecmp 4 | 5 | [[ $# -ne 2 ]] && { echo "diffdir DIR1 DIR2"; exit 1; } 6 | 7 | #compares directory trees by filename only -- only differences are reported 8 | 9 | diff <(cd $1 && find 2>/dev/null | sort) <(cd $2 && find 2>/dev/null | sort) 10 | -------------------------------------------------------------------------------- /diskfree_sigterm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Terminates particular process/PID if disk free space too low. 4 | 5 | E.g. use with crontab to check every 10 minutes or hour, stopping a disk-writing process. 6 | 7 | python diskfree_sigterm.py ~ mylogger 8 | """ 9 | 10 | from __future__ import annotations 11 | from pathlib import Path 12 | import subprocess 13 | import shutil 14 | import os 15 | import signal 16 | import logging 17 | from argparse import ArgumentParser 18 | 19 | SIG = signal.SIGTERM 20 | 21 | 22 | def diskfree_sigterm( 23 | disk: str | Path, pid: list, freethres: int, verbose: bool = False 24 | ): 25 | def _stop(pid: int): 26 | if verbose: 27 | print("sending", SIG, "to", pid) 28 | os.kill(pid, SIG) 29 | 30 | disk = Path(disk).expanduser().resolve().anchor 31 | 32 | du = shutil.disk_usage(disk) 33 | 34 | freerat = du.free / du.total 35 | if freerat < freethres: 36 | for p in pid: 37 | if isinstance(p, str): 38 | try: 39 | pstr = subprocess.check_output( 40 | ["pgrep", "-f", p], timeout=10, universal_newlines=True 41 | ) 42 | except Exception: 43 | logging.error(f"did not find PID for {p}") 44 | for s in pstr.split(): 45 | _stop(int(s)) 46 | 47 | _stop(p) 48 | 49 | if verbose: 50 | print(f"{disk} free percentage {freerat*100:.1f}") 51 | 52 | 53 | def main(): 54 | p = ArgumentParser() 55 | p.add_argument("disk", help="disk path to check") 56 | p.add_argument( 57 | "pid", help="process name or PID to terminate if disk space low", nargs="+" 58 | ) 59 | p.add_argument( 60 | "-freethres", help="minimum frace free before sigterm", default=0.1, type=float 61 | ) 62 | p.add_argument("-v", "--verbose", action="store_true") 63 | P = p.parse_args() 64 | 65 | try: 66 | pid = list(map(int, P.pid)) 67 | except ValueError: # name 68 | pid = P.pid 69 | 70 | diskfree_sigterm(P.disk, pid, P.freethres, P.verbose) 71 | 72 | 73 | if __name__ == "__main__": 74 | main() 75 | -------------------------------------------------------------------------------- /dvpn: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | sudo vpnc-disconnect 3 | getIP 4 | -------------------------------------------------------------------------------- /eps2pdf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | [[ $# == 0 ]] && { echo "Usage: eps2pdf input.eps"; exit 1; } 3 | [[ $# > 2 ]] && { echo "Usage: eps2pdf input.eps"; exit 1; } 4 | [[ $# == 1 ]] && OutFN=joined.pdf 5 | [[ $# == 2 ]] && OutFN=$2 6 | #[[ $# -eq 3 ]] && Orient=$3 7 | 8 | 9 | gs -sDEVICE=pdfwrite \ 10 | -dRotatePages=true \ 11 | -dEPSFitPage \ 12 | -dNOPAUSE -dBATCH -dSAFER \ 13 | -sOutputFile=$OutFN \ 14 | $1 15 | #-dEPSFitPage 16 | #-dAutoRotatePages=/PageByPage \ #didn't work for matlab eps 17 | # -c "<> setpagedevice" \ # didn't work 18 | # -c quit 19 | 20 | 21 | echo "wrote $1 to $OutFN" 22 | -------------------------------------------------------------------------------- /findbig: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | \du -hd1 $1 | sort -hr | head -n 8 4 | -------------------------------------------------------------------------------- /getIP.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from urllib.request import urlopen 3 | 4 | print(urlopen("https://ident.me").read().decode("ascii")) 5 | -------------------------------------------------------------------------------- /getIP.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # gets public IP address and displays in Terminal 4 | 5 | url='https://ident.me' 6 | 7 | curl -6 -s -m 2 $url 8 | echo 9 | curl -4 -s -m 2 $url 10 | echo 11 | 12 | # Note: if you get error message curl: (77) error setting certificate verify locations, it's because curl can't find your certificates. Fix this by: 13 | # 14 | # mkdir -p /etc/pki/tls/certs 15 | # ln -s /etc/ssl/certs/ca-certificates.crt /etc/pki/tls/certs/ca-bundle.crt 16 | # 17 | # Ref: http://stackoverflow.com/questions/3160909/how-do-i-deal-with-certificates-using-curl-while-trying-to-access-an-https-url 18 | -------------------------------------------------------------------------------- /getIP_curl.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | gets interface IPv4 and IPv6 public addresses using libCURL 5 | This uses the "reflector" method, which I feel is more reliable for finding public-facing IP addresses, 6 | WITH THE CAVEAT that man-in-the-middle, etc. attacks can defeat the reflector method. 7 | 8 | PyCurl does not have a context manager. 9 | 10 | https://ident.me ipv6 and ipv4 11 | https://api.ipify.org # ipv4 only 12 | """ 13 | 14 | from __future__ import annotations 15 | from argparse import ArgumentParser 16 | import ipaddress 17 | import pycurl 18 | from io import BytesIO 19 | 20 | 21 | length = 45 # http://stackoverflow.com/questions/166132/maximum-length-of-the-textual-representation-of-an-ipv6-address 22 | 23 | URL = "https://ident.me" 24 | 25 | 26 | def main(): 27 | p = ArgumentParser() 28 | p.add_argument("iface", help="network interface to use", nargs="?") 29 | p.add_argument("--url", help="plain text server", default="https://ident.me") 30 | P = p.parse_args() 31 | 32 | addr = getip(P.url, P.iface) 33 | for a in addr: 34 | print(a) 35 | 36 | 37 | def getip( 38 | url: str = None, iface: str = None 39 | ) -> list[ipaddress.IPv4Address | ipaddress.IPv6Address]: 40 | if url is None: 41 | url = URL 42 | 43 | addrs = [] 44 | for v in (pycurl.IPRESOLVE_V4, pycurl.IPRESOLVE_V6): 45 | addr = _public_addr(v, url, iface) 46 | if addr is not None: 47 | addrs.append(addr) 48 | 49 | return addrs 50 | 51 | 52 | def _public_addr( 53 | v, url: str, iface: str = None 54 | ) -> ipaddress.IPv4Address | ipaddress.IPv6Address: 55 | B = BytesIO() 56 | C = pycurl.Curl() 57 | addr = None 58 | # %% set options 59 | C.setopt(pycurl.TIMEOUT, 3) # 1 second is too short for slow connections 60 | if iface: 61 | C.setopt(pycurl.INTERFACE, iface) 62 | C.setopt(C.URL, url) # type: ignore 63 | C.setopt(pycurl.IPRESOLVE, v) 64 | C.setopt(C.WRITEDATA, B) # type: ignore 65 | # %% get public IP address 66 | ret = None 67 | try: 68 | C.perform() 69 | ret = B.getvalue() 70 | C.close() 71 | except pycurl.error: 72 | pass 73 | # %% validate response 74 | if ret: 75 | addr = ipaddress.ip_address(ret.decode("utf8")) 76 | 77 | return addr 78 | 79 | 80 | if __name__ == "__main__": 81 | main() 82 | -------------------------------------------------------------------------------- /id2email.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | convert spreadsheet email ID list to email addresses 4 | """ 5 | import pandas 6 | from argparse import ArgumentParser 7 | from pathlib import Path 8 | import numpy as np 9 | 10 | 11 | p = ArgumentParser() 12 | p.add_argument("fn", help=".xlsx filename") 13 | p.add_argument("domain", help="@domain.com") 14 | p = p.parse_args() 15 | 16 | ids = pandas.read_excel(Path(p.fn).expanduser(), usecols="A,B").dropna() 17 | ids = np.append(ids.iloc[:, 0].values, ids.iloc[:, 1].values) 18 | 19 | emails = (ids + f"@{p.domain}").tolist() 20 | 21 | print("; ".join(emails)) 22 | -------------------------------------------------------------------------------- /jpg2pdf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # converts directory of PNGs to PDF via globbing 3 | convert -page letter -adjoin "$1*.jp*g" "$2joined.pdf" 4 | 5 | -------------------------------------------------------------------------------- /mU: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Michael Hirsch 3 | # for connecting to BU research grid shares 4 | # requires being on BU network or via VPN 5 | 6 | #be sure username was specified 7 | [[ $# -eq 1 ]] || { echo "you must specify your BU username e.g. mU jdoe"; exit 1; } 8 | 9 | 10 | #take care of hung up connections 11 | uU 12 | user=$1 #your kerberos username 13 | # user parameters 14 | sshfsHost=engineering-grid.bu.edu 15 | sshfsDir=/ad/eng/research/eng_research_irs 16 | 17 | #now connect 18 | echo mounting AD U 19 | sshfs \ 20 | -o sshfs_debug \ 21 | -o follow_symlinks \ 22 | -o PubKeyAuthentication=no \ 23 | $user@"$sshfsHost":"$sshfsDir" \ 24 | ~/U 25 | #-d 26 | # dont use -C from office, it's half the speed due to CPU load of server! 27 | -------------------------------------------------------------------------------- /mX: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Michael Hirsch 3 | # for connecting to your BU personal network drive 4 | # requires being on BU network or via VPN 5 | 6 | #be sure username was specified 7 | [[ $# -eq 1 ]] || { echo "you must specify your BU username e.g. mU jdoe"; exit; } 8 | 9 | user=$1 #your BU kerberos login 10 | #take care of hung up connections 11 | uX 12 | 13 | sshfsHost=engineering-grid.bu.edu 14 | sshfsDir=/ad/eng/users/${user:0:1}/${user:1:1}/$user/ 15 | 16 | #now connect 17 | echo Mounting AD X 18 | sshfs -o sshfs_debug \ 19 | -o follow_symlinks \ 20 | -o PubKeyAuthentication=no \ 21 | $user@"$sshfsHost":"$sshfsDir" \ 22 | ~/X 23 | 24 | # dont use -C from office, it's half the speed due to CPU load of server! 25 | -------------------------------------------------------------------------------- /memfree.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | platform-independent free memory to stdout in bytes 4 | """ 5 | 6 | import psutil 7 | 8 | print(psutil.virtual_memory().available) 9 | -------------------------------------------------------------------------------- /monDMC: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # by Michael Hirsch Jan 2013 3 | set -e #quit on error 4 | 5 | i=0 6 | PreviousNumFiles=0 7 | green="\e[1;32m" 8 | normal="\e[00m" 9 | red="\e[1;31m" 10 | waitTime=60 #seconds 11 | maxIter=500 12 | minutesMonitored=$(($waitTime*$maxIter/60)) #integer math 13 | 14 | echo "Press -c to abort monitoring..." 15 | echo "Monitoring directory:" $1 every $waitTime "seconds for" $minutesMonitored "minutes." 16 | 17 | while [ $i -lt $maxIter ]; do 18 | i=$(($i+1)) 19 | 20 | #explanation of command: 21 | # ls -1 (numeral one): list file names only, ONE FILENAME PER LINE 22 | # wc -l (ell): count lines in console output 23 | CurrentNumFiles=$( ls -1 $1 | wc -l ) #compute number of files in user-specified directory 24 | 25 | #test to see that number of files is increasing! 26 | if [[ $CurrentNumFiles -gt $PreviousNumFiles ]]; then #good 27 | echo -ne ${green} 28 | else #bad, no new files being written 29 | echo -ne ${red} 30 | fi 31 | PreviousNumFiles=$CurrentNumFiles 32 | 33 | echo -e "$CurrentNumFiles ${normal}files in directory" $1 $(date -u) 34 | sleep $waitTime 35 | done 36 | 37 | -------------------------------------------------------------------------------- /pdf2png.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # uses Ghostscript to convert PDF to PNG at good quality 3 | # Michael Hirsch 4 | 5 | echo "if you're trying to extract images, use pdfimages program instead" 6 | inpdf=$1 7 | firstpg=$2 8 | lastpg=$3 9 | dpi=600 #user specified 10 | 11 | #basestem=$(basename $inpdf .pdf) #no directory 12 | instem=${inpdf%.*} #no $ on inpdf 13 | 14 | gs -r$dpi -dSAFER -dBATCH -dNOPAUSE -sDEVICE=png16m \ 15 | -dFirstPage=$firstpg -dLastPage=$lastpg \ 16 | -sOutputFile="$instem.png" "$inpdf" 17 | -------------------------------------------------------------------------------- /pdfocr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # converts non-text PDF with OCR 3 | # Michael Hirsch 2014 4 | 5 | # BUGS: won't work right if operating in parallel with two exactly the same filename stems 6 | 7 | InputPDF="$1" 8 | 9 | #get number of pages 10 | nPages=$(pdftk $InputPDF dump_data | grep NumberOfPages | cut -d" " -f2) 11 | echo "$nPages pages found in $InputPDF" 12 | 13 | baseStem=$(basename $InputPDF .pdf) #no directory 14 | tf=/dev/shm/tess_$baseStem # making it ready for GNU parallel 15 | 16 | inStem=${InputPDF%.*} #no $ before InputPDF 17 | outFN="$inStem-OCR.txt" 18 | 19 | # if existing OCR file, move it aside 20 | [[ -f $outFN ]] && mv -v $outFN $outFN-$(date -r $outFN '+%FT%T').txt 21 | 22 | for (( i=1; i<=$nPages; i++ )); do 23 | echo "OCR page $i / $nPages into $outFN" 24 | # convert $InputPDF[$i] -resize 1000% -monochrome -density 2400 $tf #terrible tif 25 | # convert $InputPDF[$i] -geometry 8000x $tf #png #better but not great 26 | pdftoppm -f $i -l $i -r 600 -gray -singlefile $InputPDF $tf #wonderful 27 | tesseract $tf.pgm "$tf" |& grep -v "Tesseract Open Source OCR Engine v" #suppress welcome msg 28 | #now append to output file 29 | echo "---- Page $i / $nPages (OCR) ----" >> "$outFN" 30 | cat "$tf.txt" >> "$outFN" 31 | done 32 | 33 | rm -v $tf.* 34 | -------------------------------------------------------------------------------- /png2pdf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # converts directory of PNGs to PDF via globbing 3 | convert -page letter -adjoin "$1*.png" "$2joined.pdf" 4 | 5 | -------------------------------------------------------------------------------- /pybashutils/ChDir.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | class ChDir(object): 5 | """ 6 | context manager for changing directories -- compatible with pathlib 7 | from https://pythonadventures.wordpress.com/2013/12/15/chdir-a-context-manager-for-switching-working-directories/ 8 | Step into a directory temporarily. 9 | """ 10 | 11 | def __init__(self, path): 12 | self.old_dir = os.getcwd() 13 | self.new_dir = path 14 | 15 | def __enter__(self): 16 | os.chdir(self.new_dir) 17 | 18 | def __exit__(self, *args): 19 | os.chdir(self.old_dir) 20 | -------------------------------------------------------------------------------- /pybashutils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scivision/pybashutils/5b262112315d3c8185e98b5b613f74cb365d06eb/pybashutils/__init__.py -------------------------------------------------------------------------------- /pybashutils/getfreeport.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | This Python script returns a free network port. 4 | You can optionally, on Linux only and requiring sudo specify the network interface. 5 | """ 6 | import socket 7 | 8 | 9 | # http://stackoverflow.com/questions/8437726/can-python-select-what-network-adapter-when-opening-a-socket 10 | SO_BINDTODEVICE = 25 11 | 12 | 13 | def freeport(iface=None): 14 | s = socket.socket() 15 | 16 | if iface: # Linux only, requires sudo 17 | s.setsockopt(socket.SOL_SOCKET, SO_BINDTODEVICE, bytes(iface, "utf8")) 18 | 19 | s.bind(("", 0)) 20 | port = s.getsockname()[1] 21 | s.close() # slight race condition 22 | 23 | return port 24 | 25 | 26 | if __name__ == "__main__": 27 | from argparse import ArgumentParser 28 | 29 | p = ArgumentParser() 30 | p.add_argument( 31 | "-i", "--iface", help="network interface to use (requires Linux and sudo)" 32 | ) 33 | P = p.parse_args() 34 | 35 | port = freeport(P.iface) 36 | 37 | print(port) 38 | -------------------------------------------------------------------------------- /pybashutils/logdemo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | If you have a project with multiple .py files, you can log them all to this logger here by in each file doing 4 | import logging 5 | 6 | that's it! 7 | 8 | By default, logging will log to stderr (on your console) 9 | 10 | 11 | Ref: http://stackoverflow.com/questions/6290739/python-logging-use-milliseconds-in-time-format 12 | """ 13 | 14 | import logging 15 | 16 | logging.basicConfig( 17 | format="%(asctime)s.%(msecs)03d %(filename)s/%(funcName)s:%(lineno)d %(message)s", 18 | datefmt="%Y-%m-%d %H:%M:%S", 19 | ) 20 | 21 | 22 | def alwaysfail(): 23 | logging.warning("is when this event was logged.") 24 | 25 | 26 | if __name__ == "__main__": 27 | alwaysfail() 28 | -------------------------------------------------------------------------------- /pybashutils/os_detect.py: -------------------------------------------------------------------------------- 1 | from platform import system, uname 2 | 3 | 4 | class Os: 5 | """ 6 | returns class with properties: 7 | .cygwin Cygwin detected 8 | .wsl Windows Subsystem for Linux (WSL) detected 9 | .mac Mac OS detected 10 | .linux Linux detected 11 | .bsd BSD detected 12 | """ 13 | 14 | def __init__(self): 15 | syst = system().lower() 16 | 17 | # initialize 18 | self.cygwin = False 19 | self.wsl = False 20 | self.mac = False 21 | self.linux = False 22 | self.windows = False 23 | self.bsd = False 24 | 25 | if "cygwin" in syst: 26 | self.cygwin = True 27 | self.os = "cygwin" 28 | elif "darwin" in syst: 29 | self.mac = True 30 | self.os = "mac" 31 | elif "linux" in syst: 32 | self.linux = True 33 | self.os = "linux" 34 | if "Microsoft" in uname().release: 35 | self.wsl = True 36 | self.linux = False 37 | self.os = "wsl" 38 | elif "windows" in syst: 39 | self.windows = True 40 | self.os = "windows" 41 | elif "bsd" in syst: 42 | self.bsd = True 43 | self.os = "bsd" 44 | 45 | def __str__(self): 46 | return self.os 47 | -------------------------------------------------------------------------------- /pybashutils/tests/test_mod.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import pytest 3 | from pybashutils.getfreeport import freeport 4 | from pybashutils.os_detect import Os 5 | 6 | 7 | def test_getfreeport(): 8 | 9 | port = freeport() 10 | assert isinstance(port, int) 11 | 12 | 13 | def test_os(): 14 | os = str(Os()) 15 | 16 | assert isinstance(os, str) and len(os) >= 3 17 | 18 | 19 | if __name__ == "__main__": 20 | pytest.main(["-x", __file__]) 21 | -------------------------------------------------------------------------------- /pybashutils/tests/test_unix.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import pytest 3 | 4 | 5 | def test_ulimit(): 6 | 7 | ulimit = pytest.importorskip("pybashutils.ulimit") 8 | 9 | soft, hard = ulimit.raise_nofile(4096) 10 | 11 | assert soft >= 4096 12 | assert hard >= 4096 13 | 14 | 15 | if __name__ == "__main__": 16 | pytest.main(["-x", __file__]) 17 | -------------------------------------------------------------------------------- /pybashutils/ulimit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from __future__ import annotations 4 | import resource as res 5 | 6 | 7 | def raise_nofile(nofile_atleast: int = 4096) -> tuple[int, int]: 8 | """ 9 | sets nofile soft limit to at least 4096, useful for running matlplotlib/seaborn on 10 | parallel executing plot generators vs. Ubuntu 16.04 default ulimit -n 1024 or OS X El Captian 256 11 | temporary setting extinguishing with Python session. 12 | """ 13 | # %% (0) what is current ulimit -n setting? 14 | soft, ohard = res.getrlimit(res.RLIMIT_NOFILE) 15 | hard = ohard 16 | # %% (1) increase limit (soft and even hard) if needed 17 | if soft < nofile_atleast: 18 | soft = nofile_atleast 19 | 20 | if hard < soft: 21 | hard = soft 22 | 23 | print("setting soft & hard ulimit -n {} {}".format(soft, hard)) 24 | try: 25 | res.setrlimit(res.RLIMIT_NOFILE, (soft, hard)) 26 | except (ValueError, res.error): 27 | try: 28 | hard = soft 29 | print( 30 | "trouble with max limit, retrying with soft,hard {},{}".format( 31 | soft, hard 32 | ) 33 | ) 34 | res.setrlimit(res.RLIMIT_NOFILE, (soft, hard)) 35 | except Exception: 36 | print("failed to set ulimit, giving up") 37 | soft, hard = res.getrlimit(res.RLIMIT_NOFILE) 38 | 39 | return soft, hard 40 | -------------------------------------------------------------------------------- /pydeptree.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | [[ ! -z $1 ]] && cd $1 6 | 7 | virtualenv testdep # it's OK if it already exists 8 | 9 | . testdep/bin/activate 10 | 11 | pip install pipdeptree[graphviz] 12 | 13 | pip install -e .[tests] # extras_require={'tests':['nose','coveralls']} 14 | 15 | python ~/code/pybashutils/pydeptree.py --graph-output svg > dep.svg 16 | 17 | . deactivate 18 | 19 | eog dep.svg & # whatever your favorite image viewing program is 20 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel"] -------------------------------------------------------------------------------- /recursive_sed.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # The quoted variables allow using backticks and other "nasty" characters in sed. 4 | # 5 | # usage: 6 | # ./recursive_sed.sh pathtofiles yuck yay 7 | # 8 | # References: 9 | # https://stackoverflow.com/a/1585810 10 | # https://unix.stackexchange.com/a/128758 11 | 12 | set -e 13 | set -u 14 | 15 | path=$1 16 | old=$2 17 | new=$3 18 | 19 | echo "${old} => ${new}" 20 | 21 | find $path -not -path '*/\.git*' -type f -exec sed -i 's/'"${old}"'/'"${new}"'/g' {} + -------------------------------------------------------------------------------- /ren.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #don't mess with the quotes on $pat. You will need to "" quote your input pattern 4 | 5 | pat=$1 6 | [[ -z $pat ]] && { echo "must specify a file pattern to process. Glob inside double quotes *.png "; exit 1; } 7 | 8 | for f in $(ls -A $pat); do 9 | g=$(echo $f | sed -e 's/\.//') #removes first (leftmost) dot in filename. FFMPEG can handle only one dot in filename. 10 | g=$(echo $g | sed -e 's/\:/-/g') #removes all colons in filename, which FFMPEG can't handle 11 | #echo $g 12 | mv -iv $f $g 13 | done 14 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = pybashutils 3 | version = 0.6.4 4 | author = Michael Hirsch, Ph.D. 5 | author_email = scivision@users.noreply.github.com 6 | description = Cross-platform utilities for computer maintenance 7 | url = https://github.com/scivision/pybashutils 8 | keywords = 9 | bash 10 | networking 11 | cmake 12 | classifiers = 13 | Development Status :: 5 - Production/Stable 14 | Environment :: Console 15 | Intended Audience :: Developers 16 | Intended Audience :: Information Technology 17 | Intended Audience :: System Administrators 18 | Operating System :: OS Independent 19 | Programming Language :: Python :: 3 20 | Topic :: System :: Networking 21 | Topic :: Utilities 22 | license_files = 23 | LICENSE.txt 24 | long_description = file: README.md 25 | long_description_content_type = text/markdown 26 | 27 | [options] 28 | python_requires = >= 3.7 29 | packages = find: 30 | install_requires = 31 | colorama 32 | binaryornot 33 | 34 | [options.extras_require] 35 | tests = 36 | pytest 37 | lint = 38 | flake8 39 | mypy 40 | types-pycurl 41 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from setuptools import setup 3 | 4 | setup() 5 | -------------------------------------------------------------------------------- /shredFind.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # shreds files and subdirectories 3 | 4 | : ${1?example: shredFind ~/shredDirAndSubDir ntimes} 5 | 6 | MainDir=$1 7 | if [ $# -gt 1 ]; then nTimes=$2; else nTimes=3; fi 8 | 9 | #list ONLY subdirectories (not invocation directory itself) 10 | DirList=($(find "$MainDir" -mindepth 1 -type d | sort)) 11 | nDir=${#DirList[@]}; 12 | nDir1=$(($nDir - 1)) 13 | 14 | #main loop 15 | for i in $(seq 0 1 $nDir1); do 16 | #currDirIn="$(readlink -f ${DirList[$i]})/" 17 | currDirIn="${DirList[$i]}" 18 | echo "shredding $currDirIn" 19 | find "$currDirIn" -type f -name "*" \ 20 | -execdir shred --verbose --iterations=$nTimes {} + 21 | # -execdir echo {} + #for testing 22 | done 23 | 24 | -------------------------------------------------------------------------------- /symlink_permissions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | checks if you can create a file and symlink to the file 4 | 5 | if you get error on Windows: 6 | 7 | OSError: symbolic link privilege not held 8 | 9 | see: 10 | 11 | https://www.scivision.dev/windows-symbolic-link-permission-enable/ 12 | """ 13 | 14 | from pathlib import Path 15 | import argparse 16 | 17 | p = argparse.ArgumentParser() 18 | p.add_argument("source", help="file to create and link to") 19 | p.add_argument("target", help="where to create the soft / symbolic link") 20 | P = p.parse_args() 21 | 22 | source = Path(P.source).expanduser() 23 | target = Path(P.target).expanduser() 24 | 25 | if not source.exists(): 26 | source.touch() 27 | 28 | print("attempting to symlink", source, target) 29 | 30 | target.symlink_to(source, source.is_dir()) 31 | 32 | assert target.is_symlink(), f"{target} was not linked to {source}" 33 | -------------------------------------------------------------------------------- /tarcp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | tars (optional compression) and moves files per directory 4 | """ 5 | import logging 6 | from pathlib import Path 7 | import tarfile 8 | from argparse import ArgumentParser 9 | 10 | 11 | def tarcp(din, fout, method="xz", compresslevel=1): 12 | # %% io setup 13 | assert isinstance(method, str), 'xz bz2 gz or ""' 14 | din = Path(din).expanduser() 15 | assert din.is_dir(), "must specify input directory" 16 | # %% must have nested .tar suffix or some archivers don't understand file 17 | fout = Path(fout).expanduser().with_suffix(".tar." + method) 18 | fout.parent.mkdir(parents=True, exist_ok=True) 19 | # %% list ONLY 1st level subdirectories 20 | dlist = [d for d in din.iterdir() if d.is_dir()] 21 | print(f"Found {len(dlist)} directories in {din} tarring to {fout}") 22 | # %% main loop 23 | with tarfile.open(fout, mode="w:" + method, compresslevel=compresslevel) as tar: 24 | for d in dlist: 25 | try: 26 | tar.add(d, recursive=True) 27 | except PermissionError: 28 | logging.error(f"E: permission: {d}") 29 | 30 | assert fout.is_file(), f"something prevented {fout} from being created" 31 | 32 | 33 | def main(): 34 | p = ArgumentParser( 35 | description="tars (optional compression) and moves files per directory" 36 | ) 37 | p.add_argument("din", help="input directory to recursively tar") 38 | p.add_argument("fout", help="tar file to write including path") 39 | P = p.parse_args() 40 | 41 | tarcp(P.din, P.fout) 42 | 43 | 44 | if __name__ == "__main__": 45 | main() 46 | -------------------------------------------------------------------------------- /uU: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | fusermount -quz ~/U 3 | 4 | 5 | -------------------------------------------------------------------------------- /uX: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | fusermount -quz ~/X 3 | -------------------------------------------------------------------------------- /ulimit_nofile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from argparse import ArgumentParser 3 | import pybashutils.ulimit as ulimit 4 | 5 | 6 | def main(): 7 | p = ArgumentParser() 8 | p.add_argument( 9 | "-n", "--nofile", help="max number of open files", type=int, default=4096 10 | ) 11 | P = p.parse_args() 12 | 13 | soft, hard = ulimit.raise_nofile(P.nofile) 14 | print(f"ulimit -n soft, hard: {soft}, {hard}") 15 | 16 | 17 | if __name__ == "__main__": 18 | main() 19 | -------------------------------------------------------------------------------- /update-compilers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | declare -a compilers=(gcc g++ gfortran) 4 | declare -a vers=(4.8 4.9 5 6) 5 | 6 | for g in "${compilers[@]}"; do 7 | i=1 #lower number, lower priority 8 | for v in "${vers[@]}"; do 9 | if [[ $($g-$v -dumpversion) ]]; then #is compiler installed 10 | update-alternatives --install /usr/bin/$g $g /usr/bin/$g-$v $i 11 | ((i++)) 12 | fi 13 | done 14 | done 15 | 16 | -------------------------------------------------------------------------------- /xrandr_force.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | # RESOLUTION SETTINGS 5 | # This sets your VGA1 monitor to its best resolution. 6 | xrandr --output DVI-I-1 --mode 1440x900 --rate 60 7 | # This sets your laptop monitor to its best resolution. 8 | xrandr --output LVDS-0 --mode 1920x1028 --rate 60 9 | 10 | # MONITOR ORDER 11 | # Put the Laptop right, VGA1 monitor left 12 | xrandr --output DVI-I-1 --left-of LVDS-0 13 | # Put the Laptop left, VGA1 monitor right 14 | #xrandr --output LVDS --left-of VGA-0 15 | 16 | # PRIMARY MONITOR 17 | # This sets your laptop monitor as your primary monitor. 18 | xrandr --output LVDS-0 --primary 19 | # This sets your VGA monitor as your primary monitor. 20 | # xrandr --output VGA1 --primary 21 | --------------------------------------------------------------------------------