Bash

將命令輸出保存到 bash 中的變數會導致“不推薦使用正則表達式中未轉義的左大括號”

  • November 13, 2016

勘誤表:

有人問過類似的問題,但在搜尋了幾天后,似乎沒有對這個特定場景的答案。

問題描述:

以下 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 腳本似乎沒有一個可以通過線上搜尋或閱讀手冊輕鬆發現或闡明的解決方案。

$$ UPDATE 01 $$:

我的原始文章中缺少一些資訊,所以我在這裡添加它。以下是相關的系統規格:

  • 作業系統: Xubuntu 16.04 x86_64
  • Bash: GNU bash,版本 4.3.46(1)-release (x86_64-pc-linux-gnu)

我正在使用的命令的原始碼和 Rational:

3rd 在以下執行緒中回复:

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

列印與 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”是實際會話使用者名的替換,目的是為了將討論概括為可以或可能使用的任何使用者名。

$$ UPDATE FINAL $$

Stéphane Chazelas 提供的所選解決方案在一篇文章中闡明了我的腳本存在的所有問題。我錯誤地假設腳本的第二行,因為輸出抱怨括號。需要明確的是,觸發警告的是第 3 行(有關原因和方式,請參閱 Chazelas 文章),這可能就是為什麼每個人都建議使用 printf 而不是 print。為了理解這些建議,我只需要指向腳本的第 3 行。

沒有按建議工作的事情:

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,他們以某種方式發現了我對這個問題的誤解。我對這裡的每一個人都印象深刻。謝謝您的幫助!:)

導致該錯誤的是第三行 ( print $sessionuser),而不是第二行。

print``ksh是在and中輸出文本的內置命令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})。在%{(.*?)}, 那(.*?)不是min,max, 仍然perl對此很寬容,並按字面意思對待這些內容,{而不是因正則表達式解析錯誤而失敗。它曾經對此保持沉默,但現在它會報告一個警告,告訴您(此處print的)程式碼中可能存在問題:您打算使用{運算符,但是您內部有錯誤。或者你沒有,然後你需要逃避那些{

順便說一句,您可以簡單地使用:

sessionuser=$(logname)

獲取啟動該腳本所屬的登錄會話的使用者的名稱。它使用getlogin()標準的 POSIX 函式。在 GNU 系統上,該查詢utmp通常僅適用於 tty 登錄會話(只要類似login或終端仿真器使用 tty 註冊utmp)。

或者:

sessionuser=$(id -un)

獲取一個使用者的名稱,該使用者的 uid 與正在執行的程序的有效使用者 idid相同(與執行該腳本的使用者相同)。

它等效於您的ps -p "$$"方法,因為將執行的 shell 呼叫與id擴展的呼叫相同,$$並且除了zsh(通過分配給EUID//特殊變數),shell 不能在不執行不同命令的情況下更改其 uid(以及當然,在所有命令中,不會是setuid)。UID``USERNAME``id

id和都是logname標準(POSIX)命令(請注意,在 Solaris 上,id與許多其他命令一樣,您需要確保將自己置於 POSIX 環境中,以確保呼叫id命令 in/usr/xpg4/bin而不是古老的 in /binps在您連結到的答案中使用的唯一目的是解決/bin/idSolaris 上的限制)。

如果你想知道呼叫的使用者sudo,它是通過$SUDO_USER環境變數。sudo這是從執行的程序的真實使用者 ID派生的使用者名sudosudo稍後將該真實使用者 ID 更改為目標使用者的 ID(root預設情況下),以便該$SUDO_USER變數是知道它是哪個的唯一方法。

請注意,當您這樣做時:

sudo ps -fp "$$"

$$是由呼叫sudo執行該 shell 的程序的 pid 的 shell 擴展的,而不是sudoor的 pid ps,所以它不會在這裡給你root

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

會給你執行那個sh(執行為root)的程序,它現在要麼仍在執行sh,要麼可能ps用於sh不為最後一個命令派生額外程序的呼叫。

對於執行相同操作ps -p "$$"並且您以sudo that-script.

請注意,在任何情況下,POSIX 命令都不是bashsudo並且有許多系統都找不到。

引用自:https://unix.stackexchange.com/questions/322864