我有一个脚本,它的函数来源于其他脚本。我正在尝试逐行但 sed 正则表达式太复杂了。
#!/usr/bin/env bash
# This function will update the value associated with a key,
# remove a comment from the beginning of the line,
# or append the key value pair to the end of the file if the key is not found
# To use this function in a script
# source this script:
# . lineinfile
#
# To invoke the function:
# lineinfile "key=value" "filename"
# OR
# lineinfile "key value" "filename"
lineinfile() {
[ -s $2 ] || echo "${1}" >> ${2}
if [[ "$1" == *"="* ]]; then
sed -i -e "/^#\?.*\(${1%%=*}\).*/{s@@${1}@;:a;n;ba;q}" -e "\$a${1}" ${2}
elif [[ "$1" == *" "* ]]; then
sed -i -e "/^#\?.*\(${1%% *}\).*/{s@@${1}@;:a;n;ba;q}" -e "\$a${1}" ${2}
elif [[ "$1" == *$'\t\t'* ]]; then
sed -i -e "/^#\?.*\(${1%%$'\t\t'*}\).*/{s@@${1}@;:a;n;ba;q}" -e "\$a${1}" ${2}
fi
}
函数的第一行[ -s $2 ] || echo "${1}" >> ${2}
- 检查第二个位置参数是否是一个存在且大小非零的文件,然后将内容附加$1
到$2
文件末尾。为什么||
在这里使用?
我真的不确定 if-elif 块正在测试什么。在 if 条件中是什么*"="*
*" "*
并*$'\t\t'*
试图匹配?此外,我不知道 sed 命令在做什么。正则表达式很复杂。任何人都可以为我分解 sed 命令。
||
如果前一个命令的退出代码不为零(假/错误),则运行以下命令。相当于:
如果文件不存在或为空,则其中任何一个都会将第一个 arg (
$1
) 附加到文件 ( ) 中。$2
顺便说一句,请参阅下面关于引用的下一点 2。顺便说一句,该函数可以(并且应该)在这一点上返回。无需
sed
在文件上运行,因为它现在包含所需的值。例如(并使用printf
而不是echo
- 请参阅为什么 printf 比 echo 更好?):或更好:
对于第一种形式,
return
只有前一个命令 (printf
) 成功时才会执行。第二种形式总是返回,无论printf
成功与否。没有充分的理由使return
依赖于printf
成功(这只是在这种速记 if/then/fi 构造中“链接”命令的常见习语)。大多数时候,printf
会成功,但有时(例如权限或磁盘已满)会失败。如果失败,该函数无论如何都应该sed
返回——脚本也会失败,所以没有必要运行它们。顺便说一句,return
没有参数将返回要执行的最后一个命令的退出代码,因此调用者将能够检测成功或失败。作者似乎不明白在 shell 中引用是什么,或者它是如何工作的,或者花括号,例如,
${var}
不能替代引用,例如。请参阅$VAR 与 ${VAR} 并引用或不引用以及为什么我的 shell 脚本会因空格或其他特殊字符而窒息?."$var"
作者始终未能
$2
在应该引用时引用(文件名可以包含空格、制表符、换行符和 shell 元字符,如果不加引号则可能破坏 shell 脚本)。三个 if/elif 测试检查第一个参数 (
$1
) 是否包含=
符号、空格或两个制表符。它运行三个稍微不同版本的 sed 脚本之一,具体取决于它找到的版本。sed 脚本都检查其变体“key”后跟
=
、空格或两个制表符是否在文件中,可选用#
. 如果找到匹配项,它会将其替换为 的值$1
并运行一个循环以读取并输出文件的其余部分。我认为这里的目的是只替换第一次出现的key=value
.如果未找到匹配项(因此未执行循环和退出),它将附加
$1
到文件的末尾。作者似乎想多了(或者可能想得太少了)。
$1
如果首先将其拆分为键和值变量,则只需一个 sed 脚本即可完成此操作。即首先提取数据并将其“规范化”$1
为一致的形式,然后仅在一个sed
脚本中使用它。或者只是用 perl 重写整个东西,当你想做一些需要 shell 和 sed 的优势的事情时,这是一个不错的选择(并且比大多数版本的 sed 具有更好和更强大的正则表达式引擎)。例如
if/elif/elif/fi
,将函数的一部分替换为:这个 perl 版本适用于所有三个变体(=、空格、两个制表符 - 后两个需要被引用)。它一次吞下整个文件(
-0777
选项),并尝试进行多行搜索和替换操作(/m
正则表达式修饰符)。如果该操作失败,它会将第一个 arg(加上换行符)附加到文件的末尾 (\z
)。它还修复了原始版本中的一个错误,该错误无法区分例如foo=123
和foobar=123
.\b
单词边界标记用于执行此操作。在 sed 中,您将使用\<
and\>
包围键模式。顺便说一句,该
X unless Y
构造只是 perl 的语法糖if not Y, then do X
。它本来可以写成if (! s/^#?.*$key.*$/$r/m) {s/\z/$r\n/}
并且仍然可以完全一样地工作。函数名称
lineinfile
非常通用,但它的作用非常具体。更糟糕的是,名称不匹配,甚至不暗示函数的实际作用。这通常被认为是不好的做法。