Friday, January 2, 2015

Recursive Bash script to clone any Sourceforge project

Source code
#!/usr/bin/env bash 



#############################################################################

###########################################################################

### Created by A. Danischewski (c) 20150102 v0.03

   ### 

    ## Usage: getsf.bsh <URL|"local.html"|"/full/path/to/local.html"> 

    ##   Files will be downloaded to the current directory or a newly created 

    ##   subdirectory if there are any folders

    ##

    ## Some Sourceforge projects don't provide convenient links to download 

    ## their software, no tar balls, no zip files, no svn or git links yet many 

    ## directories and files - downloading each is a lot of clicking and waiting -  

    ## that is time-consuming. That time that can be applied more productively. 

    ## 

    ## With this code you can recursively download entire Sourceforge projects. 

    ## It will dump all files to the directory from where it is run and 

    ## create subdirectories and load them for each folder link.  

    ## 

    ## Warning this program is simple and not very tested - likely to break at any 

    ## point in time since it relies on the current Sourceforge website layout

    ## but its simple and straightforward enough I thought I should share this

    ## for the concept of recursion. Your mileage will vary based on SF website 

    ## updates.  

    ##   

    ## If you fire this up one day and its broken, just take the string 

    ## $(lynx -dump -listonly -nonumbers "${1}"  | grep files | sed 's#/download##' \

    ## | grep http | grep -v timeline | grep -v \? | grep -v 'files/$' | uniq)

    ## and make that work to provide the URLS in the file list.

    ## 

    ## Make sure that the http://sourceforge.net/projects URL hasn't changed and 

    ## if need be adjust according to wherever the new pages live.  

    ##

    ## Then modify this: curl -b cookie_file -o "${DIRNM}.html" -L "$a"

    ## and make that work and you should have it basically functioning again. 

    ##

    ## Note: If you provide Sourceforge a useragent browser string (-A) 

    ##       this will break - the Sourceforge webserver has useragent logic

    ##       and it doesn't mind (at the time of this posting) providing files 

    ##       to the Curl agent. This is a case of - disguise yourself as 

    ##       a web browser and the website becomes ~less~ friendly. 

    ##    

    ## This program is free software: you can redistribute it and/or modify

    ## it under the terms of the GNU General Public License as published by

    ## the Free Software Foundation, either version 3 of the License, or

    ## (at your option) any later version.

    ## 

    ## This program is distributed in the hope that it will be useful,

    ## but WITHOUT ANY WARRANTY; without even the implied warranty of

    ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

    ## GNU General Public License for more details.

    ## You should have received a copy of the GNU General Public License

  #### along with this program.  If not, see <http://www.gnu.org/licenses/>.

###########################################################################

#############################################################################



### You can uncomment the next line if its broken to better see whats going on

#set -x 



declare -r STARTURL="${1}"

declare -r STARTCMD="${STARTCMD:-${0/^./${PWD}}}"

declare -r BASEREF='<base href="http://sourceforge.net/projects">'



  ### To keep the code simple and flexible by allowing Lynx to handle the initial 

  ### input we lose the ability to prefilter to eliminate parent directories - 

  ### curl -> lynx -stdin | sed could avoid this too but this is pretty easy.  

  ### We concatenate the directories we process to a variable and export it 

  ### to all the child processes to check against - that way we don't loop. 

TMPPARENT="$(echo "${STARTURL##*/projects/}" |sed 's#/#_#g;s/_$//')"   

if [[ ! -z "${PARENT}" ]] && (($(! grep -Fc $"${TMPPARENT}" <<< "${PARENT}"))); then 

 PARENT="${PARENT}${TMPPARENT}"

elif [[ -z "${PARENT}" ]]; then 

 PARENT="${TMPPARENT}" 

fi 

export PARENT STARTCMD PWD   ### Export PARENT, STARTCMD (in case run w/relative) and 

                             ### PWD to be safe or the shell may complain.



  ### As far as I know Lynx doesn't provide a base url option, so we need to add a base tag to 

  ### avoid relative path links expanding to file:///. 

  ### If we are starting with a file on the filesystem make sure that our base tag is present

  ### this is the case of when you download an html file then pull from it when on the fs.

if [[ -f "${TMPPARENT}.html" ]] && ((! grep -Fc $"$BASEREF" <<< "${TMPPARENT}.html")); then  

       sed -"7 i $BASEREF" "$STARTURL"

fi   



function usage() {

cat <<EOF



 Description: Clone a SourceForge project



   This software wants the html of a Sourceforge project *files* url. 

   Not the project homepage, although if you do by accident it will get that 

   and a bunch more stuff that you probably don't need or want.  

   

   Instead it is designed to start at the Files tab, it can be the first Files page, 

   then you will get the whole project, or you can choose a subfolder to start.  



   Files will be recursively downloaded to the current directory and 

   newly created subdirectories for any SF folders.



   This software requires Lynx and Curl (sudo apt-get install curl lynx). 

    

 Usage: ${0##*/} < "http://SF/Files/URL"| "local.html"| "/fullpath/to/local.html" > 



   E.g. ${0##*/} "http://sourceforge.net/projects/files/<..target project..>" 

   

EOF

exit 1





[[ ! -z "${1}" ]] && [[ "${1}" =~ ^- ]] || [[ -z "${1}" ]] && usage



  ### Arg 1 <string> URL|.html file to process.  

function process_dir() { 

 for in $(lynx -dump -listonly -nonumbers "${1}" | grep files | \

   sed 's#/download##' | grep http | grep -v timeline | grep -v \? | \

   grep -v 'files/$' | uniq)do 

   FILENM="${a##*/}"

   FILENM="${FILENM%.*}"

   if [[ -z "${FILENM}" ]]; then 

       ### Process as a directory 

       ### Remove "http[s]://sourceforge.net/projects/" and under_bar /'s 

       ### for dir name.  

     DIRNM="$(echo "${a##*/projects/}" |sed 's#/#_#g;s/_$//')"     



     if [[ -d "${DIRNM}" ]] || (($(! grep -Fc $"${DIRNM}" <<< "${PARENT}"))); then 

       ### If the directory exists presuming this entry processed, probably the 

       ### starting parent or a parent link in SF to a link traversed if you need  

       ### to restart it or refresh it you should rename the directory and download  

       ### it again or if its really big you can add options to curl to not clobber. 

       echo "Pid:$$ Skipping ${DIRNM} ..."

       continue

     else 

       mkdir "${DIRNM}" 

       cd "${DIRNM}"

       echo "Pid:$$ Saving ${a} to ${PWD}/${DIRNM}.html ..."

       curl -b cookie_file -o "${DIRNM}.html" -L "$a"

       sed -"7 i $BASEREF" "${DIRNM}.html" ## Inject base tag for Lynx

       PARENT="${PARENT}${DIRNM}" ### Add newly processed directory to PARENT string. 

       "${STARTCMD}" "${DIRNM}.html" ### Recursively process directories. 

       cd 1>/dev/null

    fi 

  else 

    echo "Pid:$$ Saving ${a} to ${PWD}/${a##*/} ..."

    curl  -b cookie_file -o "${a##*/}" -L "$a"

  fi 

 done





process_dir "${STARTURL}" 

echo "Pid:$$ Done!!" 

exit 0


Source Code: getsf.bsh    MD5 0743699a1350c1c3d86d665bb1d3c75a
(Google Drive incorrectly identifies this as a bin file - its not, its a bash text file)
Also if you copy and paste this text it may not match because of the html double spacing, you can pipe it through sed and the MD5 sum should match: sed "{:a;N;\$!b a};s/[^\n]\n[^\n]//g;s/\n\n/\n/g;s/\(.*\)\(\n.*\)$/\1\n\2/g"

You may want to try this alias out, seems to work on any SF project: E.g. $ gsfn nasm  
alias gsfn='_(){ getsf.bsh "http://sourceforge.net/projects/${1}/files/?source=navbar";}; _'

Thursday, January 1, 2015

Image Magick Bash Alias to Stipple Photos (like the WSJ)

alias stipple='_(){ if [[ ! -f "${1}" ]]; then echo "File does not exist: ${1}"; else TMPMAPF="/tmp/_stipple_map.png"; convert xc:White xc:Black -append "${TMPMAPF}"; FILEEXT="${1##*.}";FILENM="${1##*/}"; FILENM="${FILENM%.*}"; FILEPATH="$(dirname ${1})"; convert "${1}" -modulate 100,0,100 +level 30%,90% -remap "${TMPMAPF}" "${FILEPATH:-.}/${FILENM}_stipple.${FILEEXT}"; firefox "${FILEPATH:-.}/${FILENM}_stipple.${FILEEXT}"; fi }; _'
(To use this alias you need to have Image Magick installed: sudo apt-get install imagemagick)

Bash Script to Rename Files w/Special Characters

#!/usr/bin/env bash
#set -x
#############################################################################
###########################################################################
### Created by Adam Danischewski (c) 2014 v1.00
### Issues: If you find any issues emai1 me at my <first name> dot
###         <my last name> at gmail dot com. 
### This program handles the following known filename problems:
         ###  Spaces anywhere incl the begin/end of filenames ==> _'s
         ###  Newlines embedded filenames               ==> RANDOM hexchar a-f0-9
         ###  Multibyte Characters of any encoding type ==> RANDOM hexchar a-f0-9
         ###  Backslashes are replaced                  ==> RANDOM hexchar a-f0-9 
         ###  Other special characters in filenames     ==> RANDOM hexchar a-f0-9
              ### Although leading dashes are technically legal they
              ### can cause a lot of problems if you don't expect them
         ###  Leading dashes in filenames               ==> RANDOM hexchar a-f0-9               
    ##This program is free software: you can redistribute it and/or modify
    ##it under the terms of the GNU General Public License as published by
    ##the Free Software Foundation, either version 3 of the License, or
    ##(at your option) any later version.

    ##This program is distributed in the hope that it will be useful,
    ##but WITHOUT ANY WARRANTY; without even the implied warranty of
    ##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ##GNU General Public License for more details.

    ##You should have received a copy of the GNU General Public License
    ##along with this program.  If not, see <http://www.gnu.org/licenses/>.
###########################################################################
#############################################################################

declare -r  DATE_PREFIX=$(date +%Y%m%d)
declare -r  RENAME_BSH=".rename_${DATE_PREFIX}_$$.bsh"  
declare -r  UNRENAME_BSH=".unrename_${DATE_PREFIX}_$$.bsh"  
declare -i  MAXBADCHRS=150
declare -i  WROTE_ENTRIES=0
declare -r  VIEWLOGPROG="$(which geany || echo 'cat')"
declare -i  AUTOVIEWLOG=1
declare -i  AUTOEXECUTE=0
declare -r  DEFPERM=400
declare -i  QUIETMODE=0
declare -i  DEBUGMODE=0
declare     MVOPTIONS="-nv" 
declare     CPOPTIONS="-nv" 
declare     SENTINELS=""
#declare -r  ECHOCMD="stdbuf -o0 echo"
#declare -r  ECHOCMD="unbuffer echo"

function usage() {
cat <<EOF
Usage: ${0##*/} <-h> <-v=[on|off]> <-t|d|x|q|s> <-c=[numchars]>

OPTIONS:
      -h           Show this message
      -d           Turn on debug mode
      -v=<on|off>  Turn on/off auto review of rename file. <default is on>
      -t           Automatically tests the functionality on known troubly
                   named files and reports if this program is working properly. 
      -q|-s        Quiet/Silent mode - don't report much if anything
      -c=<num>     Sets the max number of bad characters to replace (default=150)          
      -x           "eXpert/eXpress/eXecute" mode -- it will chmod +x and
                   automatically execute the generated rename script.
                   This option is not recommended for sensitive data areas.
EOF
}

function help() {
cat <<EOF
Description: ${0##*/}

This program will create scripts to rename all files with special
characters in the directory where it is run. It replaces spaces with
_'s and really bad characters with random characters.

The scripts by default are named: .rename_<date>_<pid>.bsh and
.unrename_<date>_<pid>.bsh. It is recommended that you review them both
to make sure everything is okay and reversible before you run them. The
default permissions set are 400 so you usually need to chmod the files
before running them (e.g. chmod +x ./.rename_<date>_<pid>.bsh)
EOF

usage

cat <<EOF
  E.g.
      \$cd /directory/where/bad/files/live
      \$${0##*/}
      \$chmod + .rename_<date>_<pid>.bsh
           ... review files ... 
      \$bash ./.rename_<date>_<pid>.bsh
EOF
exit 0
}

function fail_test() {
 echo "$2"
 #echo -en "$2" | tee >(cat - >&2) 1>/dev/null
 echo "Unit Testing of ${0##*/} FAILED.. "
 echo "It is not recommended to use this program until you find out why.. "
 exit $1
}

  ## <arg 1> string - string to build random chr from
function get_random_chr() {
 local    BUILDSTR="$1"
 local    MD5STR="$(md5sum <<< "$BUILDSTR")"
 local -i INDEX=$(($RANDOM%9))
 echo "${MD5STR:$INDEX:1}"
} 

  ## <arg 1> string - string to build random chr from
function get_random_sentinel() {
 local    BUILDSTR="$1"
 local    MD5STR="$(md5sum <<< "$BUILDSTR")"
 local -i INDEX=$(($RANDOM%9))
 local    TMPSENTINEL="${MD5STR:$INDEX:3}"
   ### Loop until we know the string is a unique sentinel for our string
 while :; do
  CHCK="$(grep -o "$TMPSENTINEL" <<< "${BUILDSTR}${SENTINELS}")"
  if [[ -z "$CHCK" ]]; then
    break;
  else
    MD5STR="$(md5sum <<< "$(get_random_chr "${MD5STR}")${MD5STR}")"
    TMPSENTINEL="${MD5STR:$INDEX:3}"
  fi
 done
 echo "${TMPSENTINEL}"
} 

  #arg <1> string - a filename to test
function unit_test() {  
 #set -x
 local FILENAMEFROMHELL="$1"
 local OK=1
 TESTDIR="/tmp/_${0##*/}_test_$$_"
 mkdir -p "$TESTDIR"
 if [[ -d "$TESTDIR" ]]; then
    cd "$TESTDIR"
 else
    echo "Test directory does not exist.. "
    fail_test -3
 fi   
 touch "${FILENAMEFROMHELL}" 
 [[ ! -f "$FILENAMEFROMHELL" ]] && fail_test -4 "Cant find test file.. "
 ${0##./} -v=off -x
 ((! ! $?)) && fail_test -7 "Cant find this prog from test loc, put this prog in your path or use a FQN (/abs/toprog).. "
 chmod +x ./.*name*
 [[ -f "$FILENAMEFROMHELL" ]] && fail_test -5 "Test file not renamed.. "
 echo "Successfully renamed trouble file ... "
 ./.unrename*
 [[ ! -f "$FILENAMEFROMHELL" ]] && fail_test -6 "Test file not unrenamed.. "
 echo "Successfully unrenamed trouble file ... "
 cd -
 rm -f "${TESTDIR}/."* 2>/dev/null
 rm -f "${TESTDIR}/"* 2>/dev/null
 rmdir "${TESTDIR}"
 ((! ! $?)) && fail_test -8 "Could not delete test directory ${TESTDIR}" 
 echo "Testing of ${0##*/} Successful!! "
 return 0
}

function test_fire() {
  ## leading $'s, multiple backslashes, multiple newlines, multiple multibyte characters and trailing spaces
FNHORROR1=$(echo '$$th\\\\\\SSS\\#$(*&@isNNN # is !#\\\\#!$SSS#@*()&这NNN个程序\\\\\\是体SSS面^$%a NNNfile \\\\\  ' | sed "s/NNN/\xa/g" | sed "s/SSS/\'/")   
unit_test "$FNHORROR1"
  ## leading space, multiple backslashes, multibyte characters and newlines with a trailing backslash
FNHORROR2=$(echo ' \$$th\\\\\\SSS\\#$(*&@isNNN # is !#\\\\#!$\SSS#@*()&这NNN个程序\\\\\\是体SSS面^$%a NNNfile \\\\\ BACK' | sed "s/NNN/\xa/g" | sed "s/SSS/\'/" | sed "s/BACK/\x5c/")   
unit_test "$FNHORROR2"
echo "This program should work as expected (at least where it was run).. "
exit 0
}

function process_options() {
local OPTIND OPTARG OPTION h t v q s d c
#read -p "inside process options OPTARG is $OPTARG " -u 1 bbbb
while getopts “:htqsdc:xv:” OPTION "$@";
 do
     case "$OPTION" in
         h)
             help
             ;;
         c)
             OPTVAL="${OPTARG##*=}"
             if [[ ! -z "$(echo "$OPTVAL" | grep "^[0-9]*$")" ]]; then
               MAXBADCHRS=$OPTVAL
             else
               usage
               echo "${0##*/} -c=<num> option $OPTARG is invalid, must be an integer > 0."
               exit 0              
             fi 
             ;;
         t)
             test_fire
             ;;
         x)
             AUTOEXECUTE=1
             ;;
         d)
             DEBUGMODE=1
             ;;
         s)
             :
             ;&
         q)            
             MVOPTIONS="-n" 
             CPOPTIONS="-n" 
             AUTOVIEWLOG=0
             QUIETMODE=1
             ;;
         v)
             if [ "$OPTARG" == "=off" ]; then
               AUTOVIEWLOG=0
             elif [ "$OPTARG" == "=on" ]; then
               AUTOVIEWLOG=1
             else
               echo "${0##*/} -v option $OPTARG is invalid"; 
               usage
               exit 0
             fi 
             ;;
         ?)
             usage
             exit 0
             ;;
     esac
done
(($DEBUGMODE)) && set -xuo pipefail
}

process_options "$@"

if [[ ! -f "$RENAME_BSH" ]]; then
 echo "#!/usr/bin/env bash" > "$RENAME_BSH"
 echo "##### This script was automatically generated by ${0##*/}. #####" >> "$RENAME_BSH"
 echo "## If the data is sensitive you should review this script for " >> "$RENAME_BSH"
 echo "## accuracy prior to execution." >> "$RENAME_BSH"
 echo "## And it is recommended that you keep a copy of this script and the sister"  >> "$RENAME_BSH"
 echo "## unrename script for your records." >> "$RENAME_BSH"
 echo "## Commented mv commands are only for readability, they will break so don't use them.." >> "$RENAME_BSH"
else  
 echo "File already exists $RENAME_BSH not clobbering.. "
 exit -1
fi 

if [[ ! -f "$UNRENAME_BSH" ]]; then
 echo "#!/usr/bin/env bash" > "$UNRENAME_BSH"
 echo "##### This script was automatically generated by ${0##*/}. #####" >> "$UNRENAME_BSH"
 echo "## If the data is sensitive you should review this script for " >> "$UNRENAME_BSH"
 echo "## accuracy prior to execution." >> "$UNRENAME_BSH"
 echo "## And it is recommended that you keep a copy of this script and the sister"  >> "$UNRENAME_BSH"
 echo "## unrename script for your records." >> "$UNRENAME_BSH"
 echo "## Commented mv commands are only for readability, they will break so don't use them.." >> "$UNRENAME_BSH"
else
 echo "File already exists $UNRENAME_BSH not clobbering.. "
exit -1 
fi

while IFS= read -r -d '' a; do
 ((!$QUIETMODE)) && echo  "Processing $a ..."        
 TMP="$a"
 CHANGED=0
 for b in $(eval echo "{1..${MAXBADCHRS}}"); do
    TMP_MULTIBYTE=
    OLDHASH=$(echo "$TMP" | md5sum)
      ## probably should include way to include wider range of random replacements or a mapping.. switch
    TMP=$(echo "$TMP" | sed "s/ /_/g;s/[^0-9A-Za-z._-]/$(get_random_chr "${TMP}")/"|tr "\n" "$(get_random_chr "${TMP}")"|sed "s/^[^a-Z0-9._]/$(get_random_chr "${TMP}")/"|sed 's/.$/\n/' | LANG=C sed "s/[\x80-\xFF]/$(get_random_chr "${TMP}")/")          
    NEWHASH=$(echo "$TMP" | md5sum)
    [ "$OLDHASH" == "$NEWHASH" ] && break;
    CHANGED=1
 done
 if (($CHANGED)); then  
  WROTE_ENTRIES=1
  BACKSLASH="$(get_random_sentinel "$a")"
  SENTINELS="${SENTINELS}${BACKSLASH}"
  NEWLINE="$(get_random_sentinel "$a")"
  SENTINELS="${SENTINELS}${NEWLINE}"
  SINGLEQUOTE="$(get_random_sentinel "$a")"
  SENTINELS="${SENTINELS}${SINGLEQUOTE}"
  NEWFILE=$(sed "s/\x5c\x5c/${BACKSLASH}/g" <<< "$a")
  NEWFILE=$(echo -en "$NEWFILE" | sed ":a;N;\$!ba;s/\n/${NEWLINE}/g" | sed "s/\x27/${SINGLEQUOTE}/g")
  echo  "##mv ${MVOPTIONS} -- '${NEWFILE}' '${TMP}'"        >> "$RENAME_BSH"
  echo  "NEWFILE=\$(echo -n '${NEWFILE}'|sed \"s/${NEWLINE}/\xa/g\"|sed \"s/${BACKSLASH}/\x5c/g\"|sed \"s/${SINGLEQUOTE}/\x27/g\")"  >> "$RENAME_BSH"
  echo  "mv ${MVOPTIONS} -- \"\$NEWFILE\" '${TMP}'"         >> "$RENAME_BSH"
  echo  "##mv ${MVOPTIONS} -- '${TMP}' '${NEWFILE}'"        >> "$UNRENAME_BSH"
  echo  "NEWFILE=\$(echo -n '${NEWFILE}'|sed \"s/${NEWLINE}/\xa/g\"|sed \"s/${BACKSLASH}/\x5c/g\"|sed \"s/${SINGLEQUOTE}/\x27/g\")"   >> "$UNRENAME_BSH"
  echo  "mv ${MVOPTIONS} -- '${TMP}' \"\$NEWFILE\""         >> "$UNRENAME_BSH"
 fi
SENTINELS=
done < <(find . -maxdepth 1 ! -name ".rename*" -a ! -name ".unrename*" -a ! -path . -printf "%f\0")

if [[ -f "$UNRENAME_BSH" ]] && [[ -f "$RENAME_BSH" ]] && (($WROTE_ENTRIES)); then
 chmod ${DEFPERM} "$UNRENAME_BSH" "$RENAME_BSH"
 ((!$QUIETMODE)) && echo "Done building scripts.. "
 if ((! $AUTOEXECUTE)); then
  ((!$QUIETMODE)) && echo "Review $RENAME_BSH and if it looks sane enough, chmod +x and run it"
     ## Check to see if the filesystem where the scripts are generated is mounted noexec
  ((!$QUIETMODE)) && mount | awk "\$0 ~ \"$(df . | sed '1d' | awk '{print $1}')\"{if(\$0 ~ "noexec") print \"The filesystem where the rename scripts are located looks like it is\n mounted noexec.\nYou may have to run the scripts by prefixing bash prior to the script.\n  E.g. chmod +x ${RENAME_BSH}; bash ./${RENAME_BSH}\" }"
  (($AUTOVIEWLOG)) && $VIEWLOGPROG "$RENAME_BSH"
 else
  ((!$QUIETMODE)) && echo "Executing $RENAME_BSH .. "
  chmod -f +x "$RENAME_BSH"
  bash "$RENAME_BSH" ## call the interpreter outside the script to avoid noexec mounts causing failure to execute
  ((!$QUIETMODE)) && echo "Done!!"
 fi
else
 rm "$UNRENAME_BSH" "$RENAME_BSH"
 ((!$QUIETMODE)) && echo "Checked files and directories, looks like nothing needs renaming .. "
fi 

exit 0 

Get the latest version hosted at code.google.com here:  ren_file.bsh

Fixing Bash History - Impl. Zsh/preexec

Bash history is not shared automatically among different terminal sessions, so what you type in a respective terminal window is in the history for that respective terminal session. It is not normally viewable from other sessions.

Furthermore, Bash history does not provide an easy way to implement the functionality of preexec from ZSH that will execute a function hook *prior* to the command entered at the command prompt.

What Bash has is PROMPT_COMMAND, this will execute *after* the command at the prompt is executed.

So we can add a function to share history among sessions - w/entries in ~/.bashrc file like this:
function my_history() { 
 history -a; 
 history -c; 
 history -r; 
} 
export PROMPT_COMMAND="my_history; ${PROMPT_COMMAND}";
alias h="history -a; history -c; history -r; history" 
and type h from any session to see any commands that have been entered on any terminal after they have returned from executing.

But what if you type in a long running command and then something #(*$&amp; (bad) happens.

Upon rebooting wouldn't it be useful to be able to quickly look at the command history to see what commands were executing at the time of the problem?

If Bash had a preexec like Zsh then it would be easy to simply add our my_history function call to it and the history would be updated BEFORE the command is executed.

Bash does not have this but Bash can emulate the functionality by using trap your_cmd DEBUG, to hook the PROMPT_COMMAND call, the modified .bashrc:
function my_history() { 
 history -a; 
 history -c; 
 history -r; 
} 
  ### ZSH Pre-exec style functionality hack using DEBUG
set -o functrace > /dev/null 2>&1
shopt -s extdebug > /dev/null 2>&1
export PROMPT_COMMAND="my_history; ${PROMPT_COMMAND}";
trap 'my_history' DEBUG;
alias h="history -a; history -c; history -r; history" 

Now our history commands are updated PRIOR to executing and if anything goes wrong we will be able to simply check the history to see what commands were run even if they fail.

This idea is based on an more elaborate implementation that I found here: http://superuser.com/a/175802/365691 that has a link to the code: preexec.bash.txt, the original author is Glyph Lefkowitz according to the posting on superuser.com.

Potential #root Vulnerability on *'nix via man --html=browser

man --html=firefox

Man is owned and grouped root on my system, when you open a man page with the --html option you are root in the web browser.

You can then browse to any page and save a file it will be owned root and even create new directories with Firefox all owned as root. 

I hope this isn't as big of a security hole as it seems to be.

As a remedy on my system I have changed the ownership of /usr/bin/man to a less privileged user and the man command still functions as expected.

Colorized Show Swappers -- Scraping /proc/*/status

alias showswappers=$' CP="\\033[1;3" && Y="${CP}3m" && P="${CP}5m" && C="${CP}6m" && G="${CP}2m" && RST="${CP}0m" && for file in /proc/*/status; do SWPPID=$(cut -d \'/\' -f3 <<< "${file}"); echo -en "${Y}[[[[${RST} ${P}Pid:${RST} ${C}${SWPPID}${RST} "; awk \"/Name/{printf \\"${P}Cmd_Shortname:${RST} ${C}%s${RST} \\",\$2}/VmSwap/{printf \\"${P}Swap_Used:${RST}${C} %s %s ${RST}\\",\$2,\$3}END{printf \\"${RST}${Y}]]]] ==> ${RST}${G}\\"}\" "${file}"; cat /proc/${SWPPID}/cmdline; echo; done 2>/dev/null | sort -n -k 7'

[[[[ Pid: 25400 Cmd_Shortname: chrome Swap_Used: 9784 kB ]]]] ==> /opt/google/chrome/chrome --type=gpu-broker

Saturday, December 27, 2014

List file type counts in a directory based on file ext

   ### print counts of all file types in a directory based on file extension
alias lsx='ls -f1 | sed "s/\(.*\)\.\(.*\)/\2/" | sort | uniq -c | sort -n'

Wednesday, December 24, 2014

Figure out what command your running w/: command -V

$ command -V echo
echo is a shell builtin
$ command -V /bin/echo
/bin/echo is /bin/echo
$ command -V ls
ls is aliased to `ls --color=auto'

Fastest Way to Count Files in Dir: ls -f1 | wc -l

$ time set -- *; echo $#         $ time ls | wc -l
real    0m0.715s                    25878                  
user    0m0.678s                  real   0m0.198s 
sys    0m0.010s                   user   0m0.159s
25870                                  sys    0m0.013s

$ time ls -1f | wc -l 
25878                   
real    0m0.045s   
user    0m0.016s  
sys    0m0.007s    

This option is the clear winner, without sorting -f and the -1 for 1 entry per line is what most *nix's will have ls provide to a pipe yet it is better to be explicit.  

Tuesday, December 23, 2014

Bash Dynamic Brace Expansion w/Eval & w/o * files -- shopt -s nullglob

If you don't turn the Bash shell option nullglob on you will get *'s for the extensions you want to process.
$BRACES="{jp3g,jp4eg,g2if}"  
$ for a in $(eval "echo *${BRACES}"); do echo $a; done
*jp3g *jp4eg *g2if
$ shopt -s nullglob
$ for a in $(eval "echo *${BRACES}"); do echo $a; done
$
Voila, no more expansion to non-existent file names!

Monday, December 22, 2014

Simple Bash Function/Script to Convert Numbers to Strings e.g. 45 => Forty Five

Begin num2str.bsh :
(Thanks to logic I found somewhere on the internet in a file entitled NumberWordConverter.java - I can't recall exactly where I found it since I have since had problem with my browser and wiped the history out - I only converted it to Bash)   

#!/usr/bin/env bash

declare -a units=("" "one" "two" "three" "four" "five" "six" "seven" \
            "eight" "nine" "ten" "eleven" "twelve" "thirteen" "fourteen" \
            "fifteen" "sixteen" "seventeen" "eighteen" "nineteen")
declare -a tens=("" "" "twenty" "thirty" "forty" "fifty" "sixty" "seventy" "eighty" "ninety")

function convert() {
declare -i INTEGER=$1
 if (($INTEGER < 0)); then
   echo -en "minus "
   convert $(($INTEGER*-1))
elif (($INTEGER < 20)); then
   echo -en ${units[$INTEGER]}
elif (($INTEGER < 100)); then
   echo -en ${tens[$(($INTEGER/10))]} 
   (($INTEGER%10)) && echo -en " "
   echo -en ${units[$(($INTEGER%10))]}
elif (($INTEGER < 1000)); then
   echo -en "${units[$(($INTEGER/100))]} hundred"
   (($INTEGER%100)) && echo -en " "
   convert $(($INTEGER%100))
elif (($INTEGER < 1000000)); then
   convert $(($INTEGER/1000))
   echo -en " thousand"
   (($INTEGER%1000)) && echo -en " "
   convert $(($INTEGER%1000))
elif (($INTEGER < 1000000000)); then
   convert $(($INTEGER/1000000))
   echo -en " million"
   (($INTEGER%1000)) && echo -en " "
   convert $(($INTEGER%1000000))
else
   convert $(($INTEGER/1000000000))
   echo -en " billion"
   (($INTEGER%1000000000)) && echo -en " "
   convert $(($INTEGER%1000000000))
fi
}
convert $1
echo 


$ num2str.bsh 4541
four thousand five hundred forty one
$ num2str.bsh 45414
forty five thousand four hundred fourteen
$ num2str.bsh 4541443
four million five hundred forty one thousand four hundred forty three

Sunday, December 21, 2014

Quick Anagramatic Glob * Exception e.g. *[^host]*

/var$ ls
backups  cache  host  lib  local  lock  log  mail  opt  run  spool  tmp

/var$ ls -d *[^osht]*
backups  cache  lib  local  lock  log  mail  opt  run  spool  tmp

Of course this approach fails if you have an anagram so it should never be used for coding something serious. Yet its useful enough when you want to narrow something down when playing around or if you know for sure that you don't have anagrams to worry about.

e.g. ls -d /tmp/*[^.txt]  will list all the files without the .txt extension
e.g. ls -d /tmp/*[^.xtt]  does the same.. so be careful =)

Also if you don't mind playing with shell options you can more simply set the extglob option, yet I don't really recommend shoptions much since you can easily forget that your env is different and they tend to make things unpredictable and less portable (e.g. leaning tower of babel).

/var$ shopt -s extglob
/var$ ls -d !(host)
backups  cache  lib  local  lock  log  mail  opt  run  spool  tmp

Sunday, December 14, 2014

Bash Unset Readonly Variable - Hack

Found on the web at: 
(http://stackoverflow.com/questions/17397069/unset-readonly-variable-in-bash)  
E.g. Accidentally a READONLY variable is declared at Bash shell prompt
Here is the test script commands:   
declare -r RESTORE_BASHNOCASEMATCH="$(shopt -p nocasematch)"
echo "$RESTORE_BASHNOCASEMATCH"
unset RESTORE_BASHNOCASEMATCH
cat << EOF | sudo gdb -silent
attach $$
call unbind_variable("RESTORE_BASHNOCASEMATCH")
detach
EOF
echo "$RESTORE_BASHNOCASEMATCH"
unset RESTORE_BASHNOCASEMATCH 
Here is the result:   
$ declare -r RESTORE_BASHNOCASEMATCH="$(shopt -p nocasematch)"
$ echo "$RESTORE_BASHNOCASEMATCH"
shopt -u nocasematch
$ unset RESTORE_BASHNOCASEMATCH
bash: unset: RESTORE_BASHNOCASEMATCH: cannot unset: readonly variable
$ cat << EOF | sudo gdb -silent
> attach $$
> call unbind_variable("RESTORE_BASHNOCASEMATCH")
> detach
> EOF
(gdb) Attaching to process 5207
Reading symbols from /bin/bash...(no debugging symbols found)...done.
Reading symbols from /lib/x86_64-linux-gnu/libtinfo.so.5...(no debugging symbols found)...done.
 .... 
(gdb) Detaching from program: /bin/bash, process 5207
(gdb) quit 
$ echo "$RESTORE_BASHNOCASEMATCH"
$ unset RESTORE_BASHNOCASEMATCH
$  
No more errors, it was a success! 

Saturday, December 13, 2014

Emulate banner via figlet

alias mybanner='_(){ [[ $# -lt 3 ]] && echo "Usage: mybanner <chr> <string>"; TRCHR=$1; shift; figlet -f banner "$*" | tr "#" "$TRCHR"; }; _'

(figlet - Frank, Ian & Glenn's Letters - figlet can be installed via apt-get install figlet)

Friday, December 12, 2014

Ways to Replace Multiple Spaces in Text (Awk,Sed,tr)

The awk way:  
$ cat file.txt | awk '{gsub("[ ]+"," ");print}' 
The tr way:
$ cat file.txt | tr -s " " 
where -s stands for squeeze and can be used on any character not only spaces
The sed way: 
$ cat file | sed -r 's/[ ]+/ /g'

Thursday, December 11, 2014

Skip Variables in Awk (like shift in Bash except nonsequential)

All you need to do is set the variable to null and they disappear!

$ echo a b c d e f g | awk '{$1="";$3="";print}' 
b  d e f g

Wednesday, December 10, 2014

Using Kill To Signal a Process By Name / Taming CRAS

Sometimes CRAS (Chromium [OS] Audio Server) runs up the CPU to 100% sometimes even over 100% - if you send the hangup signal, that should save you trouble of needing to reboot:

$ kill -SIGHUP $(read a _< <(ps -eo pid,comm | grep -i cras); echo $a)

(similar to killall -r '.*[cC][Rr][aA][sS].*' -s SIGHUP )

or you can kill bash shell script sub processes like this: 

$ kill -SIGHUP $(ps -ejH | awk '!/bash/{ if ($2 ~ <process gid>) print $1}')

Tuesday, December 2, 2014

Rename directories that have special characters

while read a; do TMP="$a"; for b in {1..50}; do TMP=$(echo "$TMP" | sed "s/[^0-9A-Za-z]/$(($RANDOM%10))/"); done; mv "$a" "$TMP"; done < <(find . ! -path . -type d -printf "%f\n")

Wednesday, November 12, 2014

Simple Javascript

JavaScript - Example form
Selection:
Enter text:

Monday, November 10, 2014

Using Bash and Sed for Quick String Manipulations

To print the first string starting with an _  :

$ tmp="ffuts erom dna erom _this and _that _is here ___okay"

## Mark the first string for extraction, then use sed to grab it and remove the marker
$ echo "${tmp2/ _/ XXX_}" | sed 's/.*\(.*XXX[^ ]*\).*/\1/;s/XXX//'

Saturday, November 8, 2014

Dynamically building AWK statements w/eval

E.g. This will sum the column entries entered on the command line

#!/bin/bash
eval "awk '{print $(echo "${*:2}" | sed -r 's/\b[0-9]*\b/\$&+/g;s/\+$//')}' $1" 
E.g. cat num.txt
5 4 3 2 1
5 4 3 2 1
5 4 3 2 1
5 4 3 2 1
5 4 3 2 1
5 4 3 2 1
5 4 3 2 1
5 4 3 2 1
5 4 3 2 1
5 4 3 2 1

$>my.awk num.txt 2 3
7
7
7
7
7
7
7
7
7
7 

Thursday, November 6, 2014

Sed to modify every 1st and 3rd occurrence

sed 'H;$!d;x;:a;s/word/MATCH/1;s/word/IGNORE/1;s/word/MATCH/1;t a;s/MATCH/words/g;s/IGNORE/word/g' file

Okay, this may seem complicated at first but its simple - this copies all the text into the Hold space, after that it uses a 3 pattern first occurence match to modify the looked for text (word) with a substitute constant (to avoid reprocessing) then loops (t a) back to the label (:a) until the whole file is processed afterward it replaces the substitute constants.

You can use whatever substitute constants (MATCH,IGNORE) you want if those are problem for your data.

Markup tag stripping w/ SED

<?xml version="1.0" encoding="UTF-8" ?>
<Attributes>
   <Attribute>123</Attribute>
   <Attribute>959595</Attribute>
   <Attribute>1233</Attribute>
   <Attribute>jiji</Attribute>
</Attributes> 
To pull the second attribute:  
sed -n '/<Attributes>/,\#</Attributes>#{/<Attribute>/{
 n;s#.*<Attribute>\(.*\)</Attribute>.*#\1#;p;q};}'
 

Tuesday, November 4, 2014

Process Multivariables w/ read and a while Loop

To process data that requires you to pull multiple segments into variables, instead of a nested for loop or additional statements that would be clumsy and "awkward" (in a non-awk way) you can use a while loop and break the data into fields using awk or cut or your favorite method and read the data straight into variables:      

while read a b; do ((! $(grep -c "$a.*$b" File2.txt))) && (echo "$a $b" >> missing_pkgs.txt); done < <(awk '/>=/{ print $1" "$3 }' File1.txt)

Friday, October 31, 2014

Using Immediate documents (cat << EOF) to avoid shell quoting problems

If you want to add an entry to an alias file you normally have to jump through hoops to get the quoting properly, much easier with an immediate document since you can type the way would if editing the document itself:

cat <<EOF >> myaliases.bsh
alias go2usb='cd "/var/host/media/removable/USB Drive/"'
EOF

Thursday, October 30, 2014

Loop on tail -f for dynamic processing

Lets say you get some interesting files:

for a in {000001..249510}; do wget --random-wait -U "Mozilla/5.0 (Windows NT 5.1; rv:10.0.1)" "http://oeis.org/A$a"; done

then you suspect you missed a few... 

You can open a terminal window (or background) this: 

for a in {000001..249510}; do ((! $(grep -c A$a files.txt))) && (echo $a >> missing_files.txt); done

... and since it will take a while in a new terminal you can "Loop on tail -f for dynamic processing"

tail -f missing_files.txt | while read a; do wget --random-wait -U "Mozilla/5.1 (Windows NT 5.0; rv:10.0.2)" "http://oeis.org/A$a"; done