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
2 Answers 2
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 needecho $(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
- if you use outer single quotes, don't need to escape inner double quotes (unless you need variable interpolation, of course)
- I would test for "yes" like this:
if [[ ${yn,} == y* ]]
-- see http://www.gnu.org/software/bash/manual/bashref.html#Shell-Parameter-Expansion
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 toecho
, 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
-
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
orconfig
function. \$\endgroup\$glenn jackman– glenn jackman2014年02月19日 16:58:36 +00:00Commented Feb 19, 2014 at 16:58
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!
-
\$\begingroup\$ Thanks for your answer, this is a very interesting recommendation. I will definitely consider implementing it in the future. \$\endgroup\$adamlazz– adamlazz2014年02月19日 16:56:11 +00:00Commented Feb 19, 2014 at 16:56