#!/bin/bash # Begin profile # ------------------------------------------------------------------------------------------ # Version initiale écrite pour Beyond Linux From Scratch par # * James Robertson # * Dagmar d'Surreal # ------------------------------------------------------------------------------------------ # Version actuelle par Geoffray Levasseur # 16/02/2013 Version 1.0.0 : Initial version # 24/10/2015 Version 2.0.0 : Added some advanced functionnalities (clean, srr, etc.) # 04/02/2017 Version 2.0.1 : clean improvements (--shell) # 16/09/2018 Version 2.1.0 : Added rmhost, setc, setfr, review of locales management # 23/09/2019 Version 2.1.1 : [bugfix] dpkgs # 24/09/2019 Version 2.1.2 : [bugfix] bug in profile version display # 16/12/2019 Version 2.2.0 : Added showinfo, primary write of showdiskmap (to be finished) # 08/01/2020 Version 2.3.0 : Added use of figlet and neofetch as an equivalent to motd # 16/01/2020 Version 2.3.1 : [bugfix] non-interactive mode were blocked with some functions # 31/01/2020 Version 2.3.2 : Figlet : changed default font to ansi_shadow # 02/03/2020 Version 2.4.0 : Added command auzip # 03/03/2020 Version 2.5.0 : Added command taz and rmspc, auzip renamed utaz and improved # 05/03/2020 Version 2.5.1 : Language consistancy fix, added pigz support in taz # 06/03/2020 Version 2.5.2 : Few aliases sorted out # ------------------------------------------------------------------------------------------ export PROFVERSION="2.5.2" export DEFAULT_CITY="Toulouse" # ------------------------------------------------------------------------------------------ # path* : private functions for PATH variable management # ------------------------------------------------------------------------------------------ pathremove () { local IFS=':' local NEWPATH local DIR local PATHVARIABLE=${2:-PATH} for DIR in ${!PATHVARIABLE} ; do if [ "$DIR" != "$1" ] ; then NEWPATH=${NEWPATH:+$NEWPATH:}$DIR fi done export $PATHVARIABLE="$NEWPATH" } pathprepend () { pathremove $1 $2 local PATHVARIABLE=${2:-PATH} export $PATHVARIABLE="$1${!PATHVARIABLE:+:${!PATHVARIABLE}}" } pathappend () { pathremove $1 $2 local PATHVARIABLE=${2:-PATH} export $PATHVARIABLE="${!PATHVARIABLE:+${!PATHVARIABLE}:}$1" } # ------------------------------------------------------------------------------------------ # timer_* functions : internal timing function for prompt # ------------------------------------------------------------------------------------------ function timer_now { date +%s%N } function timer_start { timer_start=${timer_start:-$(timer_now)} } function timer_stop { local delta_us=$((($(timer_now) - $timer_start) / 1000)) local us=$((delta_us % 1000)) local ms=$(((delta_us / 1000) % 1000)) local s=$(((delta_us / 1000000) % 60)) local m=$(((delta_us / 60000000) % 60)) local h=$((delta_us / 3600000000)) # Goal: always show around 3 digits of accuracy if ((h > 0)); then timer_show=${h}h${m}m elif ((m > 0)); then timer_show=${m}m${s}s elif ((s >= 10)); then timer_show=${s}.$((ms / 100))s elif ((s > 0)); then timer_show=${s}.$(printf %03d $ms)s elif ((ms >= 100)); then timer_show=${ms}ms elif ((ms > 0)); then timer_show=${ms}.$((us / 100))ms else timer_show=${us}us fi unset timer_start } # ------------------------------------------------------------------------------------------ # Function triguered internaly by bash : defining prompt # ------------------------------------------------------------------------------------------ set_prompt () { Last_Command=$? # Must come first! Blue='\[\e[0;34m\]' White='\[\e[01;37m\]' Yellow='\[\e[01;93m\]' Red='\[\e[01;31m\]' Green='\[\e[01;32m\]' OnGrey='\[\e[47m\]' OnRed='\[\e[41m\]' OnBlue='\[\e[44m\]' ICyan='\[\e[0;96m\]' Default='\[\e[00m\]' FancyX='\342\234\227' Checkmark='\342\234\223' # Begin with time PS1="$Blue$OnGrey[ \t ]$Default " # Add a bright white exit status for the last command # If it was successful, print a green check mark. Otherwise, print # a red X. if [[ $Last_Command == 0 ]]; then PS1+="$White$OnBlue[ \$Last_Command " PS1+="$Green$Checkmark " else PS1+="$White$OnRed[ \$Last_Command " PS1+="$Yellow$FancyX " fi # Add the ellapsed time and current date timer_stop PS1+="($timer_show)$White ]$Default " # If root, just print the host in red. Otherwise, print the current user # and host in green. if [[ $EUID -eq 0 ]]; then PS1+="$Red\\u$Green@\\h " else PS1+="$Green\\u@\\h " fi # Print the working directory and prompt marker in blue, and reset # the text color to the default. PS1+="$ICyan\\w \\\$$Default " } # ------------------------------------------------------------------------------------------ # Change locale to French # ------------------------------------------------------------------------------------------ setfr () { # Set fr locale definitions export LANG=fr_FR.UTF-8 export LC_MESSAGES=fr_FR.UTF-8 export LC_ALL=fr_FR.UTF-8 } export setfr # ------------------------------------------------------------------------------------------ # Change locale to C standard # ------------------------------------------------------------------------------------------ setc () { # Locale definitions export LANG=C export LC_MESSAGES=C export LC_ALL=C } export setc # ------------------------------------------------------------------------------------------ # Display weather of the given city (or default one) # ------------------------------------------------------------------------------------------ meteo () { cities=$@ [[ $# -eq 0 ]] && local cities=$DEFAULT_CITY for city in $cities; do curl wttr.in/$city done } export meteo # ------------------------------------------------------------------------------------------ # Clean a directory or a tree from temporary or backup files # ------------------------------------------------------------------------------------------ clean () { for opt in $@ ; do case $opt in "-r"|"--recurs") local RECURSIVE=1 ;; "-h"|"--help") echo "clean : erase backup files in the given directories." echo echo "Usage : clean [option] [directory1] [...[directoryX]]" echo echo "Options :" echo " -h, --help Display that help screen" echo " -r, --recurs Do a recursive cleaning" echo " -f, --force Do not ask for confirmation (use with care)" echo " -s, --shell Do nothing and display what will be executed" echo return 0 ;; "-s"|"--shell") local OSHELL=1 ;; "-f"|"--force") local FORCE=1 ;; "-"*) echo "Invalid option, use \"clean --help\" to display usage." echo return 1 ;; *) local DIRLIST="$DIRLIST $opt" ;; esac done [[ ! $DIRLIST ]] && local DIRLIST=$(pwd) [[ ! $RECURSIVE ]] && local findopt="-maxdepth 1" [[ ! $FORCE ]] && local rmopt="-i" for dir in $DIRLIST; do local DELLIST=$(find $dir $findopt -type f -name "*~" -o -name "#*#" -o -name "*.bak" -o -name ".~*#") for f in $DELLIST; do if [[ ! $OSHELL ]]; then rm $rmopt $f else echo "rm $rmopt $f" fi done done unset FORCE OSHELL RECURSIVE DIRLIST findopt rmopt } export clean # ------------------------------------------------------------------------------------------ # Login root via SSH on the given machine # ------------------------------------------------------------------------------------------ ssr () { [[ $# -lt 1 ]] && echo "Please specify the server you want to log in." && return 1 local srv=$1 && shift ssh -x root@$srv $@ } export ssr # ------------------------------------------------------------------------------------------ # Look for a package within installed one # ------------------------------------------------------------------------------------------ dpkgs () { [[ $# -ne 1 ]] && echo "Please specify package name." && return 1 [[ -x /usr/sbin/dpkg ]] && "Error: dpkg seems unavialable." && return 2 local pkg=$1 && shift dpkg -l | grep $pkg } export dpkgs # ------------------------------------------------------------------------------------------ # Search processes matching the given string # ------------------------------------------------------------------------------------------ ppg () { ps -edf | grep $@ | grep -v "grep $@" } export ppg # ------------------------------------------------------------------------------------------ # Create a directory then goes inside # ------------------------------------------------------------------------------------------ mcd () { if [[ ! $# -eq 1 ]] ; then echo "Create a directory then goes inside." echo "Usage : mcd " return 1 fi mkdir -pv $1 && cd $1 } export mcd # ------------------------------------------------------------------------------------------ # Get PID list of the given process name # ------------------------------------------------------------------------------------------ gpid () { [[ $# -eq 1 ]] && local SINGLE=1 for pid in $@; do local RESULT=$(ps -A | grep $pid | awk '{print $1}') if [[ $SINGLE ]]; then [[ $RESULT ]] && echo "$RESULT" else [[ $RESULT ]] && echo "$pid: $RESULT" fi done [[ $RESULT ]] || return 1 } export gpid # ------------------------------------------------------------------------------------------ # Remove host from know_host (name and IP) for the active user # ------------------------------------------------------------------------------------------ rmhost () { if [[ "$#" -ne 1 ]]; then echo "*** Error: incorrect number of parameters." echo "Usage : rmhost " return 1 fi hst=$1 ip=$(host $hst | grep "has address" | awk '{print $NF}') echo "Removing host $hst from ssh known_host..." ssh-keygen -R $hst > /dev/null echo "Removing IP $ip from ssh known_host..." ssh-keygen -R $ip > /dev/null } export rmhost # ------------------------------------------------------------------------------------------ # Renome all files in current directory to replace spaces with _ # ------------------------------------------------------------------------------------------ rmspc () { for f in *\ *; do mv "$f" "${f// /_}"; done } export rmspc # ------------------------------------------------------------------------------------------ # Smartly uncompress archives (zip only) # ------------------------------------------------------------------------------------------ utaz() { for opt in $@ ; do case $opt in "-h"|"--help") echo "utaz : uncompress all the given files and/or the ones found in the given" echo " directories creating an host directory where needed." echo echo "Usage : utaz [option] [directorie(s)|file(s)]" echo echo "Options :" echo " -h, --help Display that help screen" echo " -d, --delete If decompression success, delete the source file" echo return 0 ;; "-d"|"--delete") local willrm=1 ;; "-"*) echo "Invalid option, use \"utaz --help\" to display options list" echo return 1 ;; *) local LIST="$LIST $opt" ;; esac done [[ ! $LIST ]] && local LIST="." for zitem in $LIST; do [[ $(ls $zitem/*.zip 2> /dev/null | wc -l) -eq 0 ]] && echo "$zitem contains no supported archive file, skipping." && continue for f in $zitem/*.zip; do echo -n "Processing archive $zitem/$f... " local dir=${f::-4} mkdir -p $dir [[ $? -gt 0 ]] && echo "[ filesystem can't create directories, exit ]" && return 1 unzip -o $f -d $dir > /dev/null 2>&1 case $? in 0) [[ $willrm ]] && rm -f $f && echo -n "Deleted ! " ;; 1) echo "No deletion on warnings " ;; *) echo "[ zip file corrupted, failed ]" rm -rf $dir > /dev/null 2>&1 continue ;; esac subdirs=$(find $dir -maxdepth 1 | wc -l) if [[ $subdirs -eq 2 ]]; then mv ./$dir/* ./ rmdir $dir echo -n "[ No subdir, " else echo -n "[ subdir created, " fi echo " OK ]" done done } export utaz # ------------------------------------------------------------------------------------------ # Compresse des répertoire en archives # ------------------------------------------------------------------------------------------ taz() { _doxz() { command -v xz >/dev/null 2>&1 || { echo -e >&2 "\t*** The program 'xz' is not installed, aborting." return 127 } [[ $4 ]] && local verb='-v' # Display a warning for this format echo -e "\t! Warning: xz format is not suited for long term archiving." echo -e "\t See https://www.nongnu.org/lzip/xz_inadequate.html for details." # Compresse to xz (lzma2) - Deprecated xz $verb --compress --keep -$3 -T $2 $1 return $? } _dolz() { local procopt="--threads $2" local command=plzip command -v plzip >/dev/null 2>&1 || { command -v lzip >/dev/null 2>&1 || { echo -e >&2 "\t*** Program 'plzip' or 'lzip' are not installed, aborting." return 127 } local command=lzip local procopt="" [[ $2 -gt 1 ]] && echo -e "\t! Warning: lzip doesn't support multithreading, falling back to 1 thread." && exho -e "\t* Consitder installing plzip to obtain multithreading abilities." } [[ $4 ]] && local verb="-vv" # Compresse au format lzip (lzma) $command $verb $procopt --keep -$3 $1 return $? } _dogz() { local procopt="--processes $2" local command=pigz command -v pigz >/dev/null 2>&1 || { command -v gzip >/dev/null 2>&1 || { echo -e >&2 "\t*** Program 'pigz' or 'gzip' are not installed, aborting." return 127 } local command="gzip --compress" local procopt="" [[ $2 -gt 1 ]] && echo -e "\t! Warning: gzip doesn't support multithreading, falling back to 1 thread." && exho -e "\t* Consitder installing pigz to obtain multithreading abilities." } [[ $4 ]] && local verb="--verbose" # Compresse au format bz2 $command $verb $procopt --keep -$3 $1 return $? } _dobz2() { local procopt="-p$2" local command=pbzip2 command -v pbzip2 >/dev/null 2>&1 || { command -v bzip2 >/dev/null 2>&1 || { echo -e >&2 "\t*** The program 'pbzip2' or 'bzip2' are not installed, aborting." return 127 } local command=bzip2 local procopt="" [[ $2 -gt 1 ]] && echo -e "\t! Warning: bzip2 doesn't support multithreading, falling back to 1 thread." && exho -e "\t* Consitder installing pbzip2 to obtain multithreading abilities." } [[ $4 ]] && local verb="-v" # Compresse au format bz2 $command $verb --compress $procopt --keep -$3 $1 return $? } _dolzo() { command -v lzop >/dev/null 2>&1 || { echo -e >&2 "\t*** The program 'lzop' is not installed, aborting." return 127 } [[ $4 ]] && local verb='-v' [[ $2 -gt 1 ]] && echo -e "\t! Warning: lzop doesn't support multithreading, falling back to 1 thread." # Compresse au format lzo lzop --keep -$3 $1 return $? } for opt in $@ ; do case $opt in "-h"|"--help") echo "taz : archive all files of a directory." echo echo "Usage : taz [option] [--format=] [répertoire(s)]" echo echo "Options :" echo " -h, --help Display that help screen" echo " -d, --delete Delete source file or directory after success" echo " -f, --format Chose archive format in the given list. If several format are" echo " given, the smalest is kept" echo " -p, --parallel Number of threads to use (if allowed by undelying utility)" echo " -v, --verbose Display progress where possible" echo " -1, .., -9 Compression level to use [1=fast/big, 9=slow/small]" echo echo "Supported archive format:" echo " Param.| programs | Algo. | Description" echo " ------+---------------+-------+----------------------------------------" echo " lz | plzip, lzip | lzma | Safe efficient default format" echo " xz | xz | lzma2 | Unsafe, not for long term" echo " bz2 | pbzip2, bzip2 | bzip2 | Historical but less efficient than lz" echo " gz | pigz, gzip | lz77 | Historical, safe, fast" echo " lzo | lzop | lzo | Very fast but no multithread" echo " tar | tar | tar | No compression" echo return 0 ;; "-d"|"--delete") local willrm=1 ;; "-f"?*|"--format"?*) local compform=$(echo "$opt" | cut -f 2- -d '=') ;; "-p"?*|"--parallel"?*) local nproc=$(echo "$opt" | cut -f 2- -d '=') ;; "-v"|"--verbose") local verbose=1 ;; "-"[1..9]) local complevel=${opt:1:1} ;; "-"*) echo "Option incorrecte, utilisez « taz --help » pour afficher la liste des options" echo return 1 ;; *) local LIST="$LIST $opt" ;; esac done [[ ! $compform ]] && compform=lz # safe and efficient (unless data are already compressed) [[ ! $nproc ]] && nproc=1 [[ ! $complevel ]] && complevel=6 for item in $LIST; do local donetar=0 echo "--- Processing $item..." if [[ -d $item ]]; then echo -ne "\t* Creating $item.tar... " tar -cf $item{.tar,} if [[ ! $? -eq 0 ]]; then echo "[ failed, skipping ]" continue fi local donetar=1 echo "[ OK ]" fi local fname=$item [[ $donetar -gt 0 ]] && fname=$item.tar # Skip compression part if tar is asked if [[ $compform != "tar" ]]; then echo -e "\t* Compressing archive..." _do$compform $fname $nproc $complevel $verbose [[ ! $? -eq 0 ]] && case $? in 127) echo -e "\t*** Compression program unavailable, aborting." return 127 ;; *) echo -e "\t*** Compression program returned an error, not deleting anything if asked, skipping to item." [[ $donetar -gt 0 ]] && rm $fname continue ;; esac [[ $donetar -gt 0 ]] && rm $fname fi if [[ $willrm ]]; then echo -en "\t* Deleting original source as asked... " rm -r $item && echo '[ OK ]' || echo '[ failed ]' fi echo "--- Done" done } export taz # ------------------------------------------------------------------------------------------ # Affiche des info sur les disques # ------------------------------------------------------------------------------------------ showdiskmap() { for opt in $@ ; do case $opt in "-h"|"--help") echo "showdiskmap : affiches des informations sur les disque (root permet d'en afficher d'avantage)." echo echo "Usage : showdiskmap [option]" echo echo "Options :" echo " -h, --help Affiche cette aide" echo " -w, --norootwarn N'affiche pas d'avertissement si non root" echo return 0 ;; "-w"|"--norootwarn") local norootwarn=1 ;; *) echo "Option incorrecte, utilisez « showdiskmap --help » pour afficher la liste des options" echo return 1 ;; esac done if [[ ! $EUID -eq 0 ]]; then [[ ! $norootwarn ]] && echo -e "Attention, pour afficher toutes les informations cette commande doit être exécuté en root.\n" export noroot=1 else export noroot=0 fi for disk in /dev/nvme?n? /dev/sd?; do local ata="ata inconnu" local model="modèle inaccessible" # Get ata name and partition table type if [[ $noroot -ne 0 ]]; then echo -e "Disque $disk :" else echo -e "Disque $disk ($ata) : $model" fi local partnum=0 for part in $(echo ${disk}?*); do (( partnum++ )) # Get size and type local ptype="inconnu" local psize="taille inconnue" local pmounted=$(mount | grep "$part") if [[ $noroot -ne 0 ]]; then echo -ne "\t$part, " else echo -ne "\t$part de type $ptype ($psize), " fi if [[ $pmounted ]]; then echo -e "monté sur $(mount | grep $part | awk '{print $3}')" else echo -e "probablement non monté" fi done [[ partnum -eq 0 ]] && echo -e "\tCe dique ne contient aucune partition" done } export showdiskmap # ------------------------------------------------------------------------------------------ # Affiche des info générales sur le système # ------------------------------------------------------------------------------------------ showinfo() { echo "" figlet -f ansi_shadow $(hostname) -t -k echo "" neofetch } export showinfo # ------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------ # ************************************ PROGRAMME PRINCIPAL ********************************* # ------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------ # Former PS1, stopped for much better function # Set the prompt look'n'feel # NORMAL="\[\e[0m\]" # RED="\[\e[1;31m\]" # GREEN="\[\e[1;32m\]" # PURPLE="\[\e[1;35m\]" # BLUEONGREY="\[\e[0;34m\e[47m\]" # if [[ $EUID == 0 ]] ; then # export PS1="$BLUEONGREY[\t]$NORMAL $PURPLE\$?$NORMAL|$RED\u@\H:$NORMAL\w$RED\$ $NORMAL" # else # export PS1="$BLUEONGREY[\t]$NORMAL $PURPLE\$?$NORMAL|$GREEN\u@\H:$NORMAL\w$GREEN\$ $NORMAL" # fi if [[ $EUID -eq 0 ]] ; then pathappend /sbin:/usr/sbin fi [[ -d /home/fatalerrors/.gem/ruby/2.3.0/bin ]] && pathappend /home/fatalerrors/.gem/ruby/2.3.0/bin # Set bash history export HISTSIZE=10000 export HISTIGNORE="&:[bf]g:exit" # Execute optionnal config script for script in ~/profile.d/*.sh ; do if [ -r $script ] ; then . $script fi done shopt -q login_shell && INTERACTIVE=1 #[[ $- == *i* ]] && INTERACTIVE=1 if [[ $INTERACTIVE ]]; then # For compiling (as we often compile with LFS/0linux...) export MAKEFLAGS='-j3' export PKGSOURCES='/share/src/archives' #Aliases alias ll='ls -laFh --color=auto' alias la='ls -Ah --color=auto' alias l='ls -CF --color=auto' alias ls='ls --color=auto' alias grep='grep --color=auto' alias egrep='egrep --color=auto' alias fgrep='fgrep --color=auto' alias mkck='make check' alias mkin='make install' alias mkdin='make DESTDIR=$PWD/dest-install install' alias ssh='ssh -x' alias wget='wget -c' # resume mode by default # Human readable by default alias df='df -H' alias du='du -ch' alias sdu='du -sk ./* | sort -n' # Define PS1 trap 'timer_start' DEBUG PROMPT_COMMAND='set_prompt' # Set default language setfr showinfo echo "Profile version $PROFVERSION chargé..." fi # Cleanup unset pathremove pathprepend pathappend return 0 # End /etc/profile