6
\$\begingroup\$

Below is a script to create and manage text snippets. It is also posted on GitHub. Most of the code is error checking and displaying help because I wanted to make a solid command line interface. The blanks function is used when copying a snippet with a blank (@) character in it. The user fills in the blanks.

I am looking for a review of the script: Is it easy to use? Is the code confusing? Are there any horrible bugs that I left unnoticed?

It is tested in OS X, so I am not sure if the syntax will work elsewhere. Specifically, the pbcopy and pbpaste aliases are untested.

#!/bin/sh
list() {
 echo "snp snippets:\n"
 echo "$(ls -R $snippets_dir)"
}
move() {
 if [[ "2ドル" == "" && 3ドル == "" ]]; then
 echo "snp usage:\n"
 echo "snp move <name> <group>"
 elif [[ -e $snippets_dir/2ドル ]]; then
 if -d $snippets_dir/3ドル ]]; then
 mv $snippets_dir/2ドル $snippets_dir/3ドル/2ドル
 else
 echo "ERROR: Group $snippets_dir/3ドル does not exist."; exit 1
 fi
 else
 echo "ERROR: Snippet 2ドル does not exist."; exit 1
 fi
}
new() {
 if [[ "2ドル" == "" && "3ドル" == "" ]]; then
 echo "snp usage:\n"
 echo "snp new <name> \"<text>\""
 echo "snp new group <name>"
 elif [[ "2ドル" == "group" || "2ドル" == "g" ]]; then # New group
 if [[ "3ドル" != "" ]]; then
 mkdir $snippets_dir/3ドル
 echo "Created new group \"3ドル\"."
 else
 echo "snp usage:\n"
 echo "snp new group <name>"
 fi
 else # New snippet
 if [[ "2ドル" == "" ]]; then
 echo "snp usage:\n"
 echo "snp new <name> \"<text>\""
 else
 if [[ -e $snippets_dir/2ドル ]]; then
 echo "Snippet $snippet_dir/2ドル already exists."
 echo "Overwrite? [y/n]"
 read yn
 if [[ $yn == "y" || $yn == "Y" ]]; then
 rm $snippets_dir/2ドル
 else
 return
 fi
 fi
 printf "3ドル" >> $snippets_dir/2ドル
 echo "Created new snippet \"2ドル\"."
 fi
 fi
}
remove() {
 if [[ "2ドル" == "" ]]; then
 echo "snp usage:\n"
 echo "snp remove <name>"
 echo "snp remove group <name>"
 elif [[ "2ドル" == "group" || "2ドル" == "g" ]]; then # Remove group
 if [[ "3ドル" != "" ]]; then
 if [[ -e "$snippets_dir/3ドル" ]]; then
 rm -rf $snippets_dir/3ドル
 echo "Removed group \"3ドル\"."
 else
 echo "ERROR: Group 3ドル does not exist."; exit 1
 fi
 else
 echo "snp usage:\n"
 echo "snp remove group <name>"
 fi
 elif [[ "2ドル" != "" ]]; then # Remove snippet
 if [[ -e $snippets_dir/2ドル ]]; then
 rm $snippets_dir/2ドル
 echo "Removed snippet \"2ドル\"."
 else
 echo "ERROR: Snippet 2ドル does not exist."; exit 1
 fi
 fi
}
blanks() {
 data=$(cat 1ドル)
 count=0
 OIFS=$IFS
 IFS="@"
 blanx=$(echo "$data" | tr -d -c '@' | wc -c | awk '{print 1ドル}')
 result=""
 for x in $data; do
 if [ $count -eq $blanx ]; then
 echo "$x"
 result="$result$x"
 break
 fi
 echo "$x"
 result="$result$x"
 read -p"snp> " v
 result="$result$v"
 count=`expr $count + 1`
 done
 IFS=$OIFS
 printf "$result" >> 1ドル-tmp
}
copy() {
 if [[ "1ドル" == "" ]]; then
 usage
 elif [[ -e $snippets_dir/1ドル ]]; then
 if [ ! $(uname -s) = "Darwin" ]; then
 alias pbcopy='xsel --clipboard --input'
 fi
 blanks $snippets_dir/1ドル
 cat $snippets_dir/1ドル-tmp | pbcopy
 echo "Snippet text copied to clipboard."
 rm $snippets_dir/1ドル-tmp
 else
 echo "ERROR: Snippet 1ドル does not exist."; exit 1
 fi
}
paste() {
 if [[ "2ドル" == "" ]]; then
 usage
 elif [[ -e $snippets_dir/2ドル ]]; then
 if [ ! $(uname -s) = "Darwin" ]; then
 alias pbcopy='xsel --clipboard --input'
 alias pbpaste='xsel --clipboard --output'
 fi
 blanks $snippets_dir/2ドル
 cat $snippets_dir/2ドル-tmp | pbcopy
 pbpaste
 rm $snippets_dir/2ドル-tmp
 else
 echo "ERROR: Snippet 2ドル does not exist."; exit 1
 fi
}
usage() {
 echo "snp usage:\n"
 echo "snp <name> # Copy snippet to clipboard"
 echo "snp paste <name> # Paste name (from clipboard)"
 echo "snp new <name> \"<text>\" # New snippet"
 echo "snp new group <name> # New group"
 echo "snp list # List snippets"
 echo "snp move <name> <group> # Move snippet to group"
 echo "snp remove <name> # Remove snippet"
 echo "snp remove group <name> # Remove group"
 echo "snp help # Display usage"
}
snippets_dir=~/".snp"
# Check for/create snippet dir
if [[ ! -d $snippets_dir ]]; then
 echo "ERROR: $snippets_dir doesn't exist."
 echo "Create it now? [y/n]"
 read yn
 if [[ $yn == "y" || $yn == "Y" ]]; then
 mkdir $snippets_dir
 echo "$snippets_dir created"
 else
 exit 0
 fi
fi
case 1ドル in
 p|paste)
 paste "$@" ;;
 n|new)
 new "$@" ;;
 l|list)
 list ;;
 m|move)
 move "$@" ;;
 r|remove)
 remove "$@" ;;
 h|help)
 usage ;;
 *)
 copy "$@" ;;
esac
exit 0
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Feb 19, 2014 at 15:47
\$\endgroup\$

2 Answers 2

7
\$\begingroup\$

Overall

  • You're using some bash-specific things ([[ ... && ... ]]), so your shebang line should be #!/bin/bash
  • very good indentation, readable code
  • more quotes: mv "$snippets_dir/2ドル" "$snippets_dir/3ドル/2ドル"

list function

  • ls knows how to print to the screen, don't need echo $(ls ...)

move function

  • you need echo -e if you want \n to be interpreted as a newline.
  • syntax error on 2nd if, missing [[

new function

blanks function

  • declare local variables with local
  • to read content from a file in bash: data=$(< "1ドル")
  • bash can do arithmetic: (( count++ )) -- see http://www.gnu.org/software/bash/manual/bashref.html#Conditional-Constructs
  • you only ever use this function to copy stuff to the clipboard, so you don't need a temp file:

    blanks "$snippets_dir/1ドル" | pbcopy
    
  • don't use printf "$astring" -- if $astring contains %-directives you'll get "not enough arguments" errors. Stick to echo, or if you're specifically avoiding a trailing newline, printf "%s" "$astring"

paste function

  • I would use case here, if you decide you need OS-specific aliases:

    case $(uname -a) in
     Darwin) : ;;
     *) alias pbcopy='...'
     alias pbpaste='...'
     ;;
    esac
    
answered Feb 19, 2014 at 16:40
\$\endgroup\$
1
  • 1
    \$\begingroup\$ One more thing I just noticed: the code where you set the pbcopy alias should not be in the copy function, it should be in the "main" scope, or in a setup or config function. \$\endgroup\$ Commented Feb 19, 2014 at 16:58
5
\$\begingroup\$

I am not a great fan of fall-through logic ... if there is an argument issue (missing argument, whatever), you are falling through the work functions, and letting the program end. I would make this more explicit with an exit 3. Programs should always set an exit code unless they successfully complete. I don't consider argument errors to be a successful completion.

aside - Bash has some reserved exit codes, which you should typically not use. 1 is one of the reserved exit codes. As a result, try to use values other than 1 or 2 for exit codes from shell scripts....

I would recommend creating an error-handling function... something along the lines of:

generalerror() {
 echo "snp usage:"
 echo 1ドル
 echo
 usage
 exit 3
}
checkargument() {
 if [[ "1ドル" == "" ]]; then
 generalerror 2ドル
 fi
}

This will allow you to centralize a lot of your error handling with some simpler messages/handling... the reason I am suggesting this is because of the following:

remove() {
 if [[ "2ドル" == "" ]]; then
 echo "snp usage:\n"
 echo "snp remove <name>"
 echo "snp remove group <name>"
 elif [[ "2ドル" == "group" || "2ドル" == "g" ]]; then # Remove group
 if [[ "3ドル" != "" ]]; then
 if [[ -e "$snippets_dir/3ドル" ]]; then
 rm -rf $snippets_dir/3ドル
 echo "Removed group \"3ドル\"."
 else
 echo "ERROR: Group 3ドル does not exist."; exit 1
 fi
 else
 echo "snp usage:\n"
 echo "snp remove group <name>"
 fi

That code does not exit 1.... probably because it was just an oversight... and this is why functions are good ;-)

The above code could be:

remove() {
 checkargument 2ドル "snp remove <name>\nsnp remove group <name>"
 if [[ "2ドル" == "group" || "2ドル" == "g" ]]; then # Remove group
 checkargument 3ドル "snp remove group <name>"
 ....

Right, then your error handling:

cat $snippets_dir/1ドル-tmp | pbcopy

The above line is critical to your program.... but, it does not check for errors.... Did the pbcopy succeed?

So:

  • functions to extract common logic are useful....
  • exit code set for bad values.
  • different exit code for functional problems ... (failed sub-commands)
  • error handling!
answered Feb 19, 2014 at 16:40
\$\endgroup\$
1
  • \$\begingroup\$ Thanks for your answer, this is a very interesting recommendation. I will definitely consider implementing it in the future. \$\endgroup\$ Commented Feb 19, 2014 at 16:56

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.