shell 脚本定义函数
在“ shell脚本入门”中 ,我通过创建一个非常方便的“ despacer”脚本来删除文件名中令人讨厌的空格,从而介绍了Shell脚本的基础知识。 现在,我将把这个想法扩展到可以交换其他字符的应用程序。 这介绍了解析选项以及创建自定义函数的shift方法。
本文适用于bash , ksh和zsh shell。 它中的大多数原理都适用于tcsh ,但是tcsh语法和逻辑流程存在显着差异,因此值得自己撰写。 如果您正在使用tcsh并希望自己适应本课程,请研究函数hacks( tcsh hacks )和条件语句的语法,然后尝试一下。 奖励积分应予以奖励。
参数和选项解析
如果您一直在使用Shell脚本来自动化日常任务,那么您就会对可以从POSIX Shell执行的任何命令也都执行脚本这一想法很熟悉。 您以后可以随时运行脚本,以执行一系列本来必须手动执行的命令。 这种做法是开始编程的好方法,但是shell脚本不仅可以让您的计算机盲目读取和执行脚本,还可以做很多事情。 可以即时修改它们,以便它们在每次运行时都能适应您的需求。
为了说明不同之处,请采用上一篇文章中的简单脚本:
$ ~/bin/depspacer "foo bar.txt"
$ ls
foobar.txt
更高级的版本可能提供以下选项:
$ ~/bin/hello-2.0.sh --from ' ' --to '_' "foo bar.txt"
$ ls
foo_bar.txt
更好? 当然是啦!
要将即时用户输入传递到脚本中,您需要学习如何解析参数和选项。 解析输入的方法有很多,每种语言都有不同的方法,但是对于POSIX Shell,您可以使用一系列if / then语句来顺序检查每个参数。
回想一下,POSIX命令将自己视为$ 0 ,第一个参数为$ 1 ,然后为$ 2 ,依此类推。 如果在终端中输入despace --from' ,则despace为$ 0 , -- from为$ 1 , ''为$ 2 ,依此类推。
当前脚本如下所示:
#!/bin/sh
if [ -z "$1" ]; then
echo "Provide a \"file name\", using quotes to nullify the space."
exit 1
fi
mv -i "$1" `ls "$1" | tr -d ' '`
它通过检查是否有一个参数来精确地解析一个参数。 要扩展概念,请使用test编写if / then语句来分析每个参数。 这是不完整的,但这是一个示例,假设您希望能够发出该命令:
despacer --from ' ' --to '_' foo\ bar.txt
然后:
if [ "$1" = "--from" ]; then
FROM="$2"
elif [ "$3" = "--to" ]; then
TO="$4"
fi
您可能会看到潜在的问题。 它假定用户总是会首先提供--from参数,或者在所有和--to参数旁边。
解决方法是保持循环遍历参数,依次处理每个参数,直到不再有参数为止。 为了保持解析直到没有更多的参数要解析,您可以使用带有中断后备功能的while循环。 要强制脚本从一个参数继续到下一个参数,请使用shift 。
while [ True ]; do
if [ "$1" = "--from" ]; then
FROM="$2"
shift 2
elif [ "$1" = "--to" ]; then
TO="$2"
shift 2
elif [ "$1" = "--help" ]; then
echo "despacer [ options ] FILE"
echo "--from character to remove"
echo "--to character to insert"
echo "--help print this help message"
shift 1
else
break
fi
done
ARG="$1"
在此代码块中, $ 1和$ 2的值与匹配的内容有关。 当解析器找到--from时 ,则--from是$ 1 ,之后的任何东西都是$ 2 。 设置了一个变量( FROM = $ 2 ),并且那些参数被移开了( shift 2 )。
当解析器找到--to时 ,-- to是$ 2 ,其后下降的是$ 2 。 设置了一个变量( TO = $ 2 ),并且将这些参数移开了( 移位2 )。
只要--help找到,它就会变成$ 1 ,而之后不会有任何期望( 移位1 )。
当没有任何要解析的内容时,将触发else ,脚本将使用break退出循环。
您可以合理地假设现在剩下的就是您要删除的文件,因此将值分配给$ ARG 。
将其集成到despacer应用程序中:
#!/bin/sh
while [ True ]; do
if [ "$1" = "--from" ]; then
FROM="$2"
shift 2
elif [ "$1" = "--to" ]; then
TO="$2"
shift 2
elif [ "$1" = "--help" ]; then
echo "despacer [ options ] FILE"
echo "--from character to remove"
echo "--to character to insert"
echo "--help print this help message"
shift 1
else
break
fi
done
ARG="$1"
mv -i "$ARG" `ls "$ARG" | tr "$FROM" "$TO"`
试试看:
$ ./despacer --from ' ' --to '_' "foo bar.txt"
$ ls
foo_bar.txt
有用! 仅使用默认操作重试一次:
$ ./despacer.sh "foo bar.txt"
mv: target 'bar.txt' is not a directory
您已经引入了新功能,但是还引入了导致其失败的错误。 更糟糕的是,它破坏了应用程序过去的工作方式。
主要问题是,如果未设置$ FROM和$ TO的值,则tr命令将无法成功。 此外,如果未设置任何一个或未设置任何一个,则tr失败。
更棘手的是,如果设置了$ FROM但将$ TO保留为其默认值,则tr会失败,因为除非使用--delete (或简称-d )选项,否则tr需要两个参数。
但是,如果您用硬编码-d使tr起作用,则$ TO将永远无效。
这真是个难题。 你能猜出如何解决吗?
有许多可能的答案,但是我在这里解决的方法是通过操纵变量设置的顺序以及最终命令的启动方式。
但是,在继续之前,您需要了解自定义功能。
功能
大多数shell和编程语言都提供了一种方法,可以一次编写一系列语句,然后在代码中稍后重新使用它们。 这样,您只需要编写一次代码即可,这意味着,如果正在执行的操作中存在任何错误或逻辑错误,则只需在一个地方查找该错误。 另外,您通常最终只需要编写较少的代码行,这通常表明优化程度很高。
在这个小脚本中,您需要调整的代码已经简化为单一代码,因此使用函数不会给您带来太大的好处,但是它确实有助于保持代码的清洁度并使其易于遵循。
要在bash , ksh或zsh脚本中创建函数:
myfunction() {
echo "world"
}
稍后通过使用函数名称(就像是命令一样)在脚本中调用此函数:
printf "hello \n"
myfunction
您可以使用此技术为脚本提供不同的命令序列,具体取决于变量的设置方式。
功能解决方案
我先前提出的难题是通过功能和巧妙的变量设置的组合来解决的。
首先,提供一个默认的FROM值:一个空格( '' )。
另外,将默认的最终命令设置为包含tr -d“ $ FROM”短语的函数。
假设未提供任何选项,则这些默认设置为true。
如果提供新的FROM值,则默认命令保持为true,只有tr删除的字符不同。
但是,如果提供新的TO值,则tr不再处于删除模式,因此您将创建一个在其本机转换模式下使用tr的不同函数。
首先,设置初始值:
#!/bin/sh
FROM=' '
ACTION=trdelete
添加两个功能:
trtarget() {
mv -i "${ARG}" `ls "${ARG}" | tr "${FROM}" "${TO}"`
}
trdelete() {
mv -i "${ARG}" `ls "${ARG}" | tr -d "${FROM}"`
}
如果提供--to选项,请添加ACTION变量的替代 :
while [ True ]; do
if [ "${1}" = "--from" ]; then
FROM="${2}"
shift 2
elif [ "${1}" = "--to" ]; then
TO="${2}"
ACTION=trtarget
shift 2
elif [ "${1}" = "--help" ]; then
echo "despacer [ options ] FILE"
echo "--from character to remove"
echo "--to character to insert"
echo "--help print this help message"
shift 1
else
break
fi
done
ARG="${1}"
最后,执行最终命令,无论最终结果是什么:
$ACTION
这是一个功能齐全的bash , ksh或zsh脚本,具有变量覆盖,功能和选项解析。
如果这种骇客激怒了您,请不要就此止步! 在您的系统上查找经常执行的其他任务,并找到一种使它们更适合自己的方法。 并非您编写的每个脚本或应用程序都必须开创新。 有时,您编写代码只是为了避免出现愚蠢的错误,或者避免键入或单击太多内容。 换句话说,没有一项任务太小而无法改进,因此请进入并改善您的工作环境。 以后只会带来更大更好的事情。
翻译自: https://opensource.com/article/17/1/shell-scripting-shift-method-custom-functions
shell 脚本定义函数