将命令输出保存到 bash 中的变量会导致“不推荐使用正则表达式中的未转义左大括号”

将命令输出保存到 bash 中的变量会导致“不推荐使用正则表达式中的未转义左大括号”

勘误表:

已经有人问过类似的问题,但经过几天的搜索后,似乎没有针对这个特定场景的答案。

问题描述:

以下 bash 脚本中的第二行触发错误:

#!/bin/bash
sessionuser=$( ps -o user= -p $$ | awk '{print $1}' )
print $sessionuser

这是错误消息:

Unescaped left brace in regex is deprecated, passed through in regex; marked by <-- HERE in m/%{ <-- HERE (.*?)}/ at /usr/bin/print line 528.

我尝试过的事情:

  1. 我已经尝试了单引号、后角单引号、双引号和间距的每种组合,我可以在 $() 命令输出捕获方法的内部和外部想到这些组合。
  2. 我尝试使用 $( exec ... ) 其中 ... 是此处尝试的命令。
  3. 我已经阅读了 bash,并搜索了这些论坛和许多其他论坛,但似乎没有任何内容可以说明为什么会出现此错误消息或如何解决它。

如果按照错误消息中给出的建议进行操作:

sessionuser=$( ps -o user= -p 1000 | awk '\{print $1}' )

它会导致以下错误消息与上一条错误消息相结合:

awk: cmd. line:1: \{print $1}
awk: cmd. line:1: ^ backslash not last character on line
Unescaped left brace in regex is deprecated, passed through in regex; marked by <-- HERE in m/%{ <-- HERE (.*?)}/ at /usr/bin/print line 528.

该消息引用 /usr/bin/print 中的第 528 行。这是该行:

$comm =~ s!%{(.*?)}!$_="'$ENV{$1}'";s/\`//g;s/\'\'//g;$_!ge;

我的 bash 脚本的合理性:

字符串 $USER 可以被重写,因此不一定可靠。命令“whoami”将返回不同的结果,具体取决于当前用户的权限是否已提升。

因此,需要可靠地获取当前会话用户名以实现脚本的可移植性,这是因为我可能不会永远保留相同的用户名,并且希望我的脚本能够继续工作,无论我登录的是谁作为。

所有这些都是因为正在备份的用户文件具有巨大的目录结构和许多文件。每隔一段时间,具有 root 所有权和权限的文件就会出现在该用户的备份堆栈中。

发生这种情况的原因有很多,有时只是因为该用户从系统目录结构中备份了他们喜欢的壁纸或主题,或者有时是因为该用户编译了一个项目及其需要的某些目录或文件。被设置为 root 所有权和权限,以便它以某种方式运行,而有时它可能是由于其他一些奇怪的不明原因造成的。

我知道 rsync 可能能够解决这个问题,但我想首先了解如何解决 Bash 脚本问题中的“未转义的左大括号”。

我可以自己研究 rsync,但是在尝试了几天之后,这个 bash 脚本似乎没有一个可以通过在线搜索或阅读手册轻松发现或阐明的解决方案。

[更新01]:

我的原始帖子中缺少一些信息,因此我将其添加到此处。以下是相关系统规格:

  • 操作系统:Xubuntu 16.04 x86_64
  • 重击:GNU bash,版本 4.3.46(1)-release (x86_64-pc-linux-gnu)

我正在使用的命令的来源和理由:

以下线程中的第三条回复:

https://stackoverflow.com/questions/19306771/get-current-users-username-in-bash

Print 与 Printf

我使用“print”而不是“printf”来发布此问题,因为我从中复制的源使用了“print”语法。使用“printf”后,我收到相同的错误消息,但输出中添加了一条错误消息:

Unescaped left brace in regex is deprecated, passed through in regex; marked by <-- HERE in m/%{ <-- HERE (.*?)}/ at /usr/bin/print line 528.
Error: no such file "sessions_username_here"

其中“sessions_username_here”是实际会话用户名的替换,目的是将讨论推广到可以或可能使用的任何用户名。

[最终更新]

Stéphane Chazelas 提供的所选解决方案在一篇文章中澄清了我的脚本遇到的所有问题。我错误地假设脚本的第二行因为输出抱怨括号。需要明确的是,是第三行触发了警告(请参阅 Chazelas 帖子了解原因和方式),这可能就是为什么每个人都建议 printf 而不是 print 的原因。我只需要指出脚本的第三行即可理解这些建议。

没有按照建议工作的事情:

 sessionuser=$(logname)

结果错误消息:

 logname: no login name

...所以也许这个建议并不像表面上看起来那么可靠。

如果用户权限被提升(运行脚本时有时会出现这种情况):

 id -un 

会输出

 root

而不是当前会话的用户名。这可能是一个简单的问题,确保脚本在执行之前退出 root 权限,这可以解决这个问题,但这超出了本线程的范围。

已按照建议执行或可以执行的操作:

在我弄清楚如何验证我的脚本是否在 POSIX 环境中运行并以某种方式取消 root 权限之后,我确实可以使用“id -un”来获取当前会话用户名,但这些验证和取消权限超出了该线程问题的范围。

目前,“没有”POSIX 验证、权限测试和降级,脚本会执行最初打算执行的操作,不会出现错误。该脚本现在如下所示:

 #!/bin/bash
 sessionuser=$( ps -o user= -p $$ | awk '{printf $1}' )
 printf '%s\n' "$sessionuser"

注意:如果使用提升的权限运行上述脚本,即使权限升级命令仍然输出“root”而不是当前会话用户名:

 sudo ps -o user= -p $$ | awk '{printf $1}'

将输出当前会话用户名而不是“root”,因此即使该线程的范围已得到解答,我又回到了该脚本的原点。

再次感谢 xtrmz、icarus,尤其是 Stéphane Chazelas,他以某种方式发现了我对这个问题的误解。这里的每一个人都给我留下了深刻的印象。谢谢您的帮助! :)

答案1

导致该错误的是第三行 ( print $sessionuser),而不是第二行。

print是一个内置命令,用于在ksh和中输出文本zsh,但不是bash.在 中bash,您需要使用printforecho来代替。

另请注意,在bash(与 相反zsh,但类似ksh)中,您需要引用变量。

所以zsh

print $sessionuser

(虽然我怀疑你的意思是:

print -r -- $sessionuser

如果目的是写入标准输出,则该变量的内容(后跟换行符)将位于bash

printf '%s\n' "$sessionuser"

(也适用于zsh/ ksh)。

有些系统在文件系统中还有一个print可执行命令,用于将某些内容发送到打印机,这就是您在此处实际调用的命令。它很少被使用的证据是,您的实现(与我的相同,作为 Debian 的 mime-support 包的一部分)在升级后尚未更新,perl以解决现在警告您有关正则表达式和中perl的不当使用的事实{没有人注意到。

{是一个正则表达式运算符(对于诸如 之类的东西x{min,max})。在这里%{(.*?)},这(.*?)不是 a min,max,仍然perl对此很宽容,并按字面意思对待它们,{而不是因正则表达式解析错误而失败。它曾经对此保持沉默,但现在它会报告一条警告,告诉您您的(此处print的)代码中可能存在问题:要么您打算使用{运算符,但其中出现了错误。或者你没有,然后你需要逃避那些{

顺便说一句,您可以简单地使用:

sessionuser=$(logname)

获取启动脚本所属登录会话的用户名。它使用getlogin()标准 POSIX 函数。在 GNU 系统上,该查询utmp通常仅适用于 tty 登录会话(只要类似login或 终端仿真器使用 注册 tty utmp)。

或者:

sessionuser=$(id -un)

获取与正在运行的进程的有效用户 ID 具有相同 uid 的一个用户的名称id(与运行该脚本的用户相同)。

它与您的方法等效,因为ps -p "$$"将执行的 shell 调用id与扩展的调用相同$$,并且除了zsh(通过分配给EUID//特殊变量)之外,shell 无法在不执行不同命令的情况下更改其 uid(并且当然,在所有命令中,UIDUSERNAMEid当然不是设置为 setuid)。

和都是标准 (POSIX) 命令(请注意,在 Solaris 上,与许多其他命令一样,您需要确保将自己置于 POSIX 环境中,以确保您在 中调用命令id,而不是在 中调用古老的命令。在您链接到的答案中使用的唯一目的是解决Solaris 上的限制)。lognameidid/usr/xpg4/bin/binps/bin/id

如果您想知道调用 的用户sudo,可以通过$SUDO_USER环境变量。这是一个来自于的用户sudo真实的执行的进程的用户 ID sudosudo稍后将该真实用户 ID 更改为目标用户的 ID(root默认情况下),以便该$SUDO_USER变量是知道它是什么的唯一方法。

请注意,当您这样做时:

sudo ps -fp "$$"

它由调用执行该 shell 的进程的 pid 的$$shell 扩展,而不是或 的pid ,因此它不会在此处给出。sudosudopsroot

sudo sh -c 'ps -fp "$$"'

sh将为您提供执行该命令(运行为)的进程,root该进程现在仍在运行sh,或者可能ps用于sh不为最后一个命令分叉额外进程的调用。

对于执行相同操作ps -p "$$"并且以sudo that-script.

请注意,无论如何,POSIX 命令都不是bashsudo而且有很多系统都找不到这两种方法。

答案2

命令从哪里来/usr/bin/print? Il 可能是 ubuntu 上的符号链接run-mailcap

为什么不使用echoorprintf代替print

相关内容