是否有任何常用的(或不公正地使用的)实用程序"bash函数库"?像Apache commons-lang for Java这样的东西.Bash无处不在,在扩展库领域似乎奇怪地被忽略了.
jor*_*anm 38
bash的图书馆在那里,但不常见.bash库稀缺的原因之一是功能的限制.我相信这些限制最好在"Greg's Bash Wiki"中解释:
功能.Bash的"功能"有几个问题:
代码可重用性:Bash函数不返回任何内容; 他们只生产输出流.捕获该流并将其分配给变量或将其作为参数传递的每种合理方法都需要SubShell,这会破坏对外部作用域的所有赋值.(另请参阅BashFAQ/084以了解从函数中检索结果的技巧.)因此,可重用函数的库是不可行的,因为您不能要求函数将其结果存储在名称作为参数传递的变量中(除了通过执行eval后空翻).
范围:Bash有一个简单的局部范围系统,大致类似于"动态范围"(例如Javascript,elisp).函数查看其调用者的本地(如Python的"非本地"关键字),但无法访问调用者的位置参数(如果启用了extdebug,则通过BASH_ARGV除外).除非您诉诸奇怪的命名规则以使冲突不太可能,否则无法保证可重用函数不受命名空间冲突的影响.如果实现期望处理来自第n-3帧的变量名的函数,这可能已被n-2处的可重用函数覆盖,则这尤其成问题.Ksh93可以通过使用"function name {...}"语法声明函数来使用更常见的词法范围规则(Bash不能,但无论如何都支持这种语法).
闭包:在Bash中,函数本身总是全局的(具有"文件范围"),因此没有闭包.函数定义可以嵌套,但这些不是闭包,尽管它们看起来非常相似.函数不是"可通过的"(第一类),并且没有匿名函数(lambdas).事实上,没有什么是"可以通过的",特别是不是数组.Bash严格使用call-by-value语义(魔术别名hack除外).
还有许多并发症涉及:子壳; 出口功能; "功能崩溃"(定义或重新定义其他功能或自身的功能); 陷阱(及其继承); 以及函数与stdio交互的方式.不要因为不理解这一切而咬新手.壳牌功能完全成熟.
来源:http://mywiki.wooledge.org/BashWeaknesses
shell"库"的一个例子是基于Redhat的系统上的/etc/rc.d/functions.此文件包含sysV init脚本中常用的函数.
use*_*dly 17
我在这里看到一些好消息和不良信息.让我分享我所知道的,因为bash是我在工作中使用的主要语言(我们构建库......).谷歌在bash脚本上写得很不错,我认为这是一个很好的阅读:https://google.github.io/styleguide/shell.xml.
首先让我说你不应该想到一个bash库,因为你在其他语言中使用库.必须强制执行某些实践,以使bash库变得简单,有条理,最重要的是可重用.
除了打印的字符串和函数的退出状态(0-255)之外,没有从bash函数返回任何内容的概念.这里存在预期的局限性和学习曲线,特别是如果您习惯于高级语言的功能.一开始可能很奇怪,如果你发现自己处于字符串不切割它的情况下,你会想要利用外部工具,如jq.如果jq(或类似的东西)可用,您可以开始让您的函数打印格式化输出,以便像对象,数组等一样进行解析和利用.
有两种方法可以在bash中声明一个函数.一个在你当前的shell中运行,我们称之为Fx0.一个产生子壳进行操作,我们称之为Fx1.以下是如何声明它们的示例:
Fx0(){ echo "Hello from $FUNCNAME"; }
Fx1()( echo "Hello from $FUNCNAME" )
Run Code Online (Sandbox Code Playgroud)
这两个函数执行相同的操作 - 实际上.但是,这里有一个关键的区别.Fx1无法执行任何改变当前shell的操作.这意味着修改变量,更改shell选项和声明其他功能.后者可以被利用来防止名称间距问题,这些问题很容易在你身上蔓延.
# Fx1 cannot change the variable from a subshell
Fx0(){ Fx=0; }
Fx1()( Fx=1 )
Fx=foo; Fx0; echo $Fx
# 0
Fx=foo; Fx1; echo $Fx
# foo
Run Code Online (Sandbox Code Playgroud)
话虽这么说,唯一一次你应该使用"Fx0"类型的功能是当你想要重新声明当前shell中的东西时.始终使用"Fx1"函数,因为它们更安全,您不必担心在其中声明的任何函数的命名.正如您在下面所看到的,无辜函数被覆盖在Fx1内部,但是,在执行Fx1之后它仍然没有受到损坏.
innocent_function()(
echo ":)"
)
Fx1()(
innocent_function()( true )
innocent_function
)
Fx1 #prints nothing, just returns true
innocent_function
# :)
Run Code Online (Sandbox Code Playgroud)
如果您使用花括号,这可能会(可能)产生意想不到的后果.有用的"Fx0"类型函数的示例将专门用于更改当前shell,如下所示:
use_strict(){
set -eEu -o pipefail
}
enable_debug(){
set -Tx
}
disable_debug(){
set +Tx
}
Run Code Online (Sandbox Code Playgroud)
全局变量的使用,或者至少是那些预期有价值的变量,都是不好的做法.当您在bash中构建库时,您不希望函数依赖已经设置的外部变量.功能需要的任何东西都应该通过位置参数提供给它.这是我在其他人试图用bash构建的库中看到的主要问题.即使我发现了一些很酷的东西,我也无法使用它,因为我不知道我需要提前设置的变量的名称.它导致挖掘所有代码,最终只是为自己挑选有用的部分.到目前为止,为库创建的最佳函数非常小,甚至根本不使用命名变量.以下面的例子为例:
serviceClient()(
showUsage()(
echo "This should be a help page"
) >&2
isValidArg()(
test "$(type -t "$1")" = "function"
)
isRunning()(
nc -zw1 "$(getHostname)" "$(getPortNumber)"
) &>/dev/null
getHostname()(
echo localhost
)
getPortNumber()(
echo 80
)
getStatus()(
if isRunning
then echo OK
else echo DOWN
fi
)
getErrorCount()(
grep -c "ERROR" /var/log/apache2/error.log
)
printDetails()(
echo "Service status: $(getStatus)"
echo "Errors logged: $(getErrorCount)"
)
if isValidArg "$1"
then "$1"
else showUsage
fi
)
Run Code Online (Sandbox Code Playgroud)
通常情况下,你会看到顶部附近的东西local hostname=localhost,local port_number=80哪个很好,但没有必要.我认为,这些东西应该是功能化的,因为你正在构建以防止未来的痛苦,因为突然需要引入一些逻辑来获取值,例如:if isHttps; then echo 443; else echo 80; fi.你不希望在你的主要功能中放置这种逻辑,否则你很快就会让它变得丑陋和无法管理.现在,serviceClient具有在调用时声明的内部函数,这为每次运行增加了不明显的开销.现在您可以使用service2Client,其功能(或外部函数)与serviceClient的名称相同,绝对没有冲突.要记住的另一个重要事项是,重定向可以在声明它时应用于整个函数.看:isRunning或showUsage这就像面向对象一样接近我认为你应该使用bash.
. serviceClient.sh
serviceClient
# This should be a help page
if serviceClient isRunning
then serviceClient printDetails
fi
# Service status: OK
# Errors logged: 0
Run Code Online (Sandbox Code Playgroud)
我希望这可以帮助我的同伴抨击黑客.
在函数内声明但没有local关键字的变量是全局变量.
最好只在函数内声明变量,local以避免与其他函数和全局冲突(参见下面的foo()).
Bash函数库需要始终"来源".我更喜欢使用'source'同义词而不是更常见的点(.),所以我可以在调试期间更好地看到它.
以下技术至少适用于bash 3.00.16和4.1.5 ......
#!/bin/bash
#
# TECHNIQUES
#
source ./TECHNIQUES.source
echo
echo "Send user prompts inside a function to stderr..."
foo() {
echo " Function foo()..." >&2 # send user prompts to stderr
echo " Echoing 'this is my data'..." >&2 # send user prompts to stderr
echo "this is my data" # this will not be displayed yet
}
#
fnRESULT=$(foo) # prints: Function foo()...
echo " foo() returned '$fnRESULT'" # prints: foo() returned 'this is my data'
echo
echo "Passing global and local variables..."
#
GLOBALVAR="Reusing result of foo() which is '$fnRESULT'"
echo " Outside function: GLOBALVAR=$GLOBALVAR"
#
function fn()
{
local LOCALVAR="declared inside fn() with 'local' keyword is only visible in fn()"
GLOBALinFN="declared inside fn() without 'local' keyword is visible globally"
echo
echo " Inside function fn()..."
echo " GLOBALVAR=$GLOBALVAR"
echo " LOCALVAR=$LOCALVAR"
echo " GLOBALinFN=$GLOBALinFN"
}
# call fn()...
fn
# call fnX()...
fnX
echo
echo " Outside function..."
echo " GLOBALVAR=$GLOBALVAR"
echo
echo " LOCALVAR=$LOCALVAR"
echo " GLOBALinFN=$GLOBALinFN"
echo
echo " LOCALVARx=$LOCALVARx"
echo " GLOBALinFNx=$GLOBALinFNx"
echo
Run Code Online (Sandbox Code Playgroud)
源代码函数库由......表示
#!/bin/bash
#
# TECHNIQUES.source
#
function fnX()
{
local LOCALVARx="declared inside fnX() with 'local' keyword is only visible in fnX()"
GLOBALinFNx="declared inside fnX() without 'local' keyword is visible globally"
echo
echo " Inside function fnX()..."
echo " GLOBALVAR=$GLOBALVAR"
echo " LOCALVARx=$LOCALVARx"
echo " GLOBALinFNx=$GLOBALinFNx"
}
Run Code Online (Sandbox Code Playgroud)
运行TECHNIQUES产生以下输出......
Send user prompts inside a function to stderr...
Function foo()...
Echoing 'this is my data'...
foo() returned 'this is my data'
Passing global and local variables...
Outside function: GLOBALVAR=Reusing result of foo() which is 'this is my data'
Inside function fn()...
GLOBALVAR=Reusing result of foo() which is 'this is my data'
LOCALVAR=declared inside fn() with 'local' keyword is only visible in fn()
GLOBALinFN=declared inside fn() without 'local' keyword is visible globally
Inside function fnX()...
GLOBALVAR=Reusing result of foo() which is 'this is my data'
LOCALVARx=declared inside fnX() with 'local' keyword is only visible in fnX()
GLOBALinFNx=declared inside fnX() without 'local' keyword is visible globally
Outside function...
GLOBALVAR=Reusing result of foo() which is 'this is my data'
LOCALVAR=
GLOBALinFN=declared inside fn() without 'local' keyword is visible globally
LOCALVARx=
GLOBALinFNx=declared inside fnX() without 'local' keyword is visible globally
Run Code Online (Sandbox Code Playgroud)
这是我花了一个小时左右的谷歌搜索后发现的"值得你花时间" bash库的列表.
bashmenot是Halcyon和Haskell在Heroku上使用的一个库.以上链接指向可用功能的完整列表,其中包含示例 - 令人印象深刻的质量,数量和文档.
MBFL提供了一组实现常用操作和脚本模板的模块.相当成熟的项目,仍然活跃在github上
您需要查看代码以获得简要说明和示例.它背后有几年的发展.
这具有最少的基本功能.对于文档,您还必须查看代码.
小智 6
我在这里发现了一篇很好的旧文章,它提供了一个完整的实用程序库列表:
http://dberkholz.com/2011/04/07/bash-shell-scripting-libraries/