我正在研究一个在Android上运行的脚本(因此奇怪的shebang路径).它涉及使用sed注释掉指定文件中的某些代码块.目前,我正在尝试将整个sed命令传递给函数,但我在这方面遇到了很多麻烦.
这是脚本:
#!/system/bin/sh
REM_RCTD=$1
REM_CCMD=$2
REM_TRITON=$3
DEVICE_CODE=$4
COLOR_GRN_PRE="<font color='#00ff00'>"
COLOR_YEL_PRE="<font color='#ffff00'>"
COLOR_POS="</font>"
YELLOW=0
GREEN=1
echoAndExec() {
CMD="$2"
if [ "$1" = ${YELLOW} ]; then
echo "$COLOR_YEL_PRE $CMD $COLOR_POS"
elif [ "$1" = ${GREEN} ]; then
echo "$COLOR_GRN_PRE $CMD $COLOR_POS"
fi
${CMD} || exit 1
}
if [ "$REM_RCTD" = "true" ]
then
CMD="sed -ir -e \_^# LG RCT(Rooting Check Tool)$_,/^$/{/^(#\|$)/!s/^/#/} init.lge.rc"
echoAndExec ${YELLOW} "${CMD}"
fi
if [ "$REM_CCMD" = "true" ]
then
CMD="sed -ir -e \_^service ccmd /system/bin/ccmd$_,/^$/{/^(#\|$)/!s/^/#/} init.lge.rc"
echoAndExec ${YELLOW} "${CMD}"
fi
if [ "$REM_TRITON" = "true" ]
then
CMD="sed -ir -e /# triton service/,\_chmod 644 /sys/devices/system/cpu/triton/enable_s/^/# / init.${DEVICE_CODE}.power.rc"
echoAndExec ${YELLOW} "${CMD}"
fi
Run Code Online (Sandbox Code Playgroud)
发送到echoAndExec()函数的每个命令都可以正常工作,但sed返回的除外
sed: bad pattern '\_^#@1 (\)
Run Code Online (Sandbox Code Playgroud)
对于第三个sed命令,它具有相同的问题/.
我尝试了一堆引号组合,有和没有使用变量,模式开头的语法不同(/#,_),但我真的迷路了.从我的脚本中可以明显看出,我几乎是shell脚本的初学者.
是否有可能使用的POSIX替代品sed可能更好用?或者是否有一些引号和转义的组合可以使这个工作?我只是想不出来.
如果有人需要更多细节,我很乐意提供; 我只是不知道什么是相关的.
TL; DR:您可以eval在函数中使用并在您传递的命令中包含正确的内部引号,这对于复杂命令来说很麻烦,并且可能使使用您的函数的代码很难读取,但这有一些优点.或者你也可以通过命令为多个参数到shell功能,初始颜色参数后,储存或以其它方式使用后的颜色参数推卸,并运行命令"$@".请参阅下面的完整详细信息 (这里也可能没有涉及的方法;或许其他人会写下关于它们的答案.)
将${CMD}在${CMD} || exit 1未引述,所以分词和通配进行.这只能在最简单的情况下实现你真正想要的.您的每个sed命令都包含您打算sed作为单个参数传递给的文本,但其中包含空格.这些空格使它分成多个单词,每个单词sed作为单独的命令行参数传递给它们.
考虑您要运行的命令的简化情况:
printf '%s\n' 'foo bar' 'baz quux'
Run Code Online (Sandbox Code Playgroud)
当您printf使用%s\n第一个参数运行时,它会在一行上自行打印其后续参数.这是检查单词拆分和通配的效果的便捷方法.该特定命令的输出是:
foo bar
baz quux
Run Code Online (Sandbox Code Playgroud)
如果将整个命令分配给CMD 没有内部引号,则此特定命令的简单性会使问题立即显而易见:shell无法知道您的位置,也不打算执行分词.
$ CMD="printf %s\n foo bar baz quux"
$ $CMD
foo
bar
baz
quux
Run Code Online (Sandbox Code Playgroud)
但是,这正是你与情况各您的sed命令.命令中的某些空格sed用于分隔参数,而其他空格则不是,并且shell无法知道您想要什么.
嵌入内部引号到的值CMD将不会解决问题.反正不是它本身.由于他们自己被引用,他们的特殊意义被压制.在这种情况下不会发生引用删除,因此它们只会保留在您放置它们的单词的边缘.此外,您打算引用它们的空格仍会导致分词:
$ CMD="printf %s\n 'foo bar' 'baz quux'"
$ $CMD
'foo
bar'
'baz
quux'
Run Code Online (Sandbox Code Playgroud)
当然,您可以$CMD 在双引号内扩展("$CMD").但这会阻止所有单词拆分,并尝试运行一个程序,其名称是您的整个命令,空格和所有.那不是你想要的:
$ "$CMD"
printf %s\n 'foo bar' 'baz quux': command not found
Run Code Online (Sandbox Code Playgroud)
有多种方法可以解决这个问题.我会展示两个.
eval一种解决方案是扩展$CMD双引号以防止分词,但不是运行它,而是将它作为参数传递给evalshell内置.这导致shell解析内容的CMD方式与它们在脚本中实际显示为一行时的内容相同.请注意,由于CMD未引用存储的实际文本,因此必须包含需要引用的所有内容的内部引号.在简单的情况下,您可以使用" "内部引号或外部引号:
$ CMD='printf "%s\n" "foo bar" "baz quux"'
$ eval "$CMD"
foo bar
baz quux
Run Code Online (Sandbox Code Playgroud)
$ CMD="printf '%s\n' 'foo bar' 'baz quux'"
$ eval "$CMD"
foo bar
baz quux
Run Code Online (Sandbox Code Playgroud)
但是,对于包含类似字符的命令,$如果在双引号内扩展可能会被特别处理 - 就像你正在使用的那样sed- 通常需要为两者使用单引号.为了达到这个目的,你可以结束引用足够长的时间来编写一个本身引用\的单引号,然后再次重新引用.也就是说,你可以在单引号内写一个单引号'\''.
$ CMD='printf '\''%s\n'\'' '\''foo bar'\'' '\''baz quux'\'''
$ eval "$CMD"
foo bar
baz quux
Run Code Online (Sandbox Code Playgroud)
这种方法的主要缺点是很难正确引用命令将它们传递给shell函数,而且你(或其他人)很难通过检查来验证它们是否正确.但是,它的优点是函数具有一个可以像命令一样运行的字符串.通常这不重要,但在您的情况下,它可能很重要,因为您向用户显示命令是什么.如果你希望用户看到,可以运行一个命令,逐字-也就是说,你要表现出适当的引用给用户,而不仅仅是正常运行命令-那么这个eval为基础的方法可能是实现最简单的方法那.
这是shell函数的修改版本.它将color开始标记中的属性值(而不是数字)作为第一个参数.我实际上建议你这样做,因为没有理由$YELLOW,$GREEN也不能只是#ffff00和#00ff00.但无论你选择写它,都应该展示你如何使用eval它.
echoAndExec() {
printf '<font color='\''%s'\''> %s </font>\n' "$1" "$2"
eval "$2" || exit 1
}
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,我也替换echo了printf,因为一些echo实现扩展了转义序列,你可能不想在这里.(有些人也可以对你传递的第一个参数进行处理,就好像它是一个选项,但这不是问题,因为你的第一个参数始于<,从不-.)最后,我将变量设置为小写.我建议为shell变量使用小写名称,除非您打算将它们导出为环境变量.对于环境变量和由shell专门处理的变量(例如,PS1)以大写字母命名是常见的,并且使用小写有助于避免冲突.但是,如果您愿意,可以使用大写字母.
以下是您可以调用该函数的方法:
$ green='#00ff00'
$ cmd='printf '\''%s\n'\'' '\''foo bar'\'' '\''baz quux'\'''
$ echoAndExec "$green" "$cmd"
<font color='#00ff00'> printf '%s\n' 'foo bar' 'baz quux' </font>
foo bar
baz quux
Run Code Online (Sandbox Code Playgroud)
要不就:
$ green='#00ff00'
$ echoAndExec "$green" 'printf '\''%s\n'\'' '\''foo bar'\'' '\''baz quux'\'''
<font color='#00ff00'> printf '%s\n' 'foo bar' 'baz quux' </font>
foo bar
baz quux
Run Code Online (Sandbox Code Playgroud)
当然,无论您是否先将命令分配给变量,都不必green每次都重新定义.
通常,当您使用双引号执行参数扩展时,将完全抑制分词.但是,参数是特殊的.并且都包含的文本所有的位置参数,一前一后,但他们的行为不同,当在双引号扩大.有了,你就得不到单词分裂 - 位置参数被连接起来,它们之间有单个空格(或者是第一个字符).@*@"$*"$IFS
相反,在位置参数之间"$@"执行字分割,但不在其中.这就是说每个位置参数都成为它自己的单词,但是包含空格的位置参数仍然不会被进一步分割(就像它在外面或外面一样).$*$@ " "
这提供了将命令传递给函数所需的功能,允许您以可读的方式编写命令,并使函数正确运行.如果您想采用这种方法,可以使用以下方法编写函数:
echoAndExec() {
printf '<font color='\''%s'\''> ' "$1"
shift
printf '%s </font>\n' "$*"
"$@" || exit 1
}
Run Code Online (Sandbox Code Playgroud)
"$@"最初运行不会做你想要的,因为它会在开头包含第一个位置参数.要移除第一个位置参数,同时将每个其他位置参数向下移动一个(或者您可能更喜欢将它们视为将它们左移一个),我使用了shift内置参数.
该代码的可读性稍差,因为我避免在shell函数中引入任何变量.我这样做的原因是POSIX实际上并不需要declare和localbuiltins ,我不确定你的shell - 以及你可能需要运行这个脚本的其他shell - 是否支持它们.没有它们,分配给shell函数中的变量也会导致它们被设置为调用者.对于您已经显示的特定脚本,这似乎不是一个问题,但我不知道您是否(或如何)最终扩展脚本或稍后可能在其中使用的变量名称.
与您的函数版本相比,上面显示的一些更改既不特定于扩展方法,"$@"也不特定于前面的使用方法eval.我在上一节中解释了这些变化.尽管你可以,但你不必用这种方式编写你的函数; 上述代码的目的是作为一个例子.
重要的是要记住,您必须以与调用您最初编写的函数不同的方式调用此函数(并且与eval上面显示的基于版本的版本不同).不要先将命令存储在变量中.只需将命令的每个单词传递给shell函数:
$ green='#00ff00'
$ echoAndExec "$green" printf '%s\n' 'foo bar' 'baz quux'
<font color='#00ff00'> printf %s\n foo bar baz quux </font>
foo bar
baz quux
Run Code Online (Sandbox Code Playgroud)
您会注意到,这更容易调用,因为除了直接运行命令之外,您不必使用任何引用.事实上,你必须不使用这些额外引用.使用此功能并了解您所编写的内容并验证其是否正确非常容易.
但是,这确实具有以下缺点:打印使用其原始引用传入的命令不再是微不足道的.shell调用函数时会删除这些引号.该功能无权访问它们.
您可能不关心这一点,但如果您这样做,那么您可以使shell函数在每个参数周围插入引号.它们不一定是最初使用的相同引号,如果参数本身包含单引号,它们甚至可能不正确.但是,他们通常应该以合理的方式明确指出通过的论点:
echoAndExec() {
printf '<font color='\''%s'\''> ' "$1"
shift
printf \''%s'\'' ' "$@"
printf '</font>\n'
"$@" || exit 1
}
Run Code Online (Sandbox Code Playgroud)
您将以相同的方式使用该功能.它看起来像这样:
$ green='#00ff00'
$ echoAndExec "$green" printf '%s\n' 'foo bar' 'baz quux'
<font color='#00ff00'> 'printf' '%s\n' 'foo bar' 'baz quux' </font>
foo bar
baz quux
Run Code Online (Sandbox Code Playgroud)
(正如上面提到的,你不必重新定义green各一次.我已经这样做了,所以它的清楚green的,我已经写了壳功能的版本手段,所以它是清楚如何测试这个容易.)
虽然你可以这样做,但我强调它显示的命令并不总是可以如图所示运行,因为它不能正确处理内部单引号.向用户显示的命令对人类来说非常好,但对于计算机来说并不是那么好.因此,尽管您可以使用此修改方法,但如果您需要向用户显示带有原始(或其他正确)引用的命令eval,则最好采用上面显示的基于方式的方法.
当然,也可以以更复杂的方式处理参数,例如,正确地将内部事件转换'为'\''.
| 归档时间: |
|
| 查看次数: |
286 次 |
| 最近记录: |