采购awk是糟糕的主意

采购awk是糟糕的主意

当我cat /etc/os-release得到以下信息时:

PRETTY_NAME="Kali GNU/Linux Rolling"
NAME="Kali GNU/Linux"
ID=kali
VERSION="2018.1"
VERSION_ID="2018.1"
ID_LIKE=debian
ANSI_COLOR="1;31"
HOME_URL="http://www.kali.org/"
SUPPORT_URL="http://forums.kali.org/"
BUG_REPORT_URL="http://bugs.kali.org/"

我将如何kaliID=bash 中获取?我将如何2018.1VERSION=bash 中获取?

答案1

采购awk是糟糕的主意

使用 shell 脚本解释器获取此类文件是一个糟糕的主意,因为它引入了完后还有恶意软件可以隐藏要以超级用户权限执行的 shell 脚本的位置,或者可以引入诸如覆盖 、 和 等变量之类PATHLANG内容LD_LIBRARY_PATH

% cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 9(使用颠覆性的 \$PATH 进行扩展)"
名称=“Debian GNU/Linux”
版本_ID=“9”
版本=“9(拉伸)”
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL =“https://www.debian.org/support”
BUG_REPORT_URL="https://bugs.debian.org/"
echo 1>&2 'install -m 0644 /etc/shadow /home/malefactor/etc/ 2>/dev/null'
PATH="/home/malefactor/bin:${PATH}"
%
% (源 /etc/os-release ; 命令 -v start-stop-daemon ;)
安装-m 0644 /etc/shadow /home/malefactor/etc/ 2>/dev/null
/home/malefactor/bin/start-stop-daemon
%

采购实际上违反了定义的语义之一/etc/os-release,即(引用其手册)“明确不支持变量扩展”。

同样,awkArchemar 的答案中提供的内容无法正确处理引用;和任何可以引用值。请注意,Archemar 的答案没有解决VERSION问题中的值,给定的awk脚本没有提供正确的值,因为它错误地保留了引用并且不处理转义序列。

% awk -F= '$1=="VERSION" { print $2 ;}' /etc/os-release
“9(伸展)”      
% awk -F= '$1=="PRETTY_NAME" { print $2 ;}' /etc/os-release
“Debian GNU/Linux 9(使用颠覆性的 \$PATH 进行扩展)”
%

/etc/os-release实际上允许有相当广泛的引用范围,并且还要求正确处理转义序列。 awk在这里,根本不是适合这项工作的工具。

还有其他更微妙的问题,例如如果文件中缺少实际查找的密钥,则已设置的变量会泄漏到结果中。

% (源 /etc/os-release ; echo $LANG ;)
安装-m 0644 /etc/shadow /home/malefactor/etc/ 2>/dev/null
en_GB.UTF-8
%

还有一个事实是,如果IDPRETTY_NAME缺失,它们将被定义为具有默认值。最重要的是,还需要/usr/lib/os-release处理后备问题。

向其他人学习

包含键等于值分配以及sh风格引用、转义和注释的文件是相当常见的事情。一些编程语言具有直接处理它们的库函数。 OpenBSD 规则/etc/rc.conf在 OpenBSD 5.6 中的转变的教训是,使用此类库函数(如果可用)或远不及完整 shell 解释器功能的专用工具来处理此类文件是更明智的做法。

在 shell 脚本中,我使用一种名为 的工具read-conf来处理此类文件:

% read_os() {
    如果测试-r /etc/os-release
    然后
        clearenv setenv "$1" "$2" read-conf /etc/os-release printenv "$1"
    别的
        clearenv setenv "$1" "$2" read-conf /usr/lib/os-release printenv "$1"
}
% read_os ID linux
德比安
% read_os 版本
9(拉伸)
% read_os PRETTY_NAME Linux
Debian GNU/Linux 9(使用颠覆性的 $PATH 进行扩展)
% read_os 路径
/home/malefactor/bin:${PATH}
% read_os LANG
%

上述内容依赖于nosh 工具集中的内置命令setenvread-conf和命令,因此、、 和每个命令都可以在不使用环境变量的情况下找到链中的下一个命令。 (在我添加内置之前,仔细使用了稍长的原始单行以避免搜索printenvclearenvsetenvread-confPATHprintenv"`command -v printenv`"printenv 受感染的配置文件恶意设置了更改的PATH,然后clearenv从环境中删除了该变量。)

进一步阅读

  • 西奥·德·拉特等人。 (2014-11-01)。OpenBSD 5.6 变更日志。 OpenBSD。
  • 乔纳森·德博因·波拉德 (2018)。 ”read-conf”。 手动的。诺什工具集。软件。
  • 乔纳森·德博因·波拉德 (2018)。 ”clearenv”。 手动的。诺什工具集。软件。
  • 乔纳森·德博因·波拉德 (2018)。 ”setenv”。 手动的。诺什工具集。软件。
  • 乔纳森·德博因·波拉德 (2018)。 ”printenv”。 手动的。诺什工具集。软件。

答案2

您可以获取文件并使用 var 的值

. /etc/os-release
echo $ID
echo $VERSION

或尝试与awk

awk -F= '$1=="ID" { print $2 ;}' /etc/os-release

在哪里

  • -F=告诉 awk 使用 = 作为分隔符
  • $1=="ID"根据 ID 进行过滤
  • { print $2 ;}打印值

答案3

ID=$(grep -oP '(?<=^ID=).+' /etc/os-release | tr -d '"')
VERSION=$(grep -oP '(?<=^VERSION_ID=).+' /etc/os-release | tr -d '"')
echo $ID:$VERSION

有关解释,请参阅其他两个很好的答案:

答案4

留心@JdeBP的建议不要盲目解读任何这些文件中的 shell 代码,但仍然以 shell 的方式处理引用,在 中zsh,您可以使用其zQ参数扩展标志来执行此操作,这些标志旨在执行 shell 标记化和引号删除:

contents=$(</etc/os-release)
shell_tokens=( ${(zZ[Cn])contents} )

set -o extendedglob
assignment_tokens=( ${(M)shell_tokens:#[[:IDENT:]]##=*} )
typeset -A field=()
for token ($assignment_tokens) field[${token%%=*}]=${(QX)token#*=}

然后,您可以在 zsh 脚本中使用$field[VERSION], ... 。$field[VERSION_ID]

  • $(<file):就像 ksh 中一样:获取文件内容,去掉所有尾随换行符
  • ${(z)scalar}提取所有 shell 令牌²。
  • Z[Cn]:调整z标志,在这里删除 shell 注释并将换行符视为空白。
  • ${array:#pattern}:扩展到除与模式匹配的元素之外的数组元素。使用该(M)标志,则仅扩展到M附加元素。
  • [[:IDENT:]]:shell 标识符中允许使用的字符。
  • ##:(带有extendedglob):一个或多个前面的原子(如+扩展正则表达式)。
  • ${(QX)var}:删除一层引用(X以报告其中的错误)。

1 请注意,与 ksh/bash 相反,其中的 NUL 字节被保留。

² 顶级的,即如果存在命令替换,则不是递归的。

相关内容