Errata:
Similar questions about this have been asked but after searching this for a few days there appears to be no answer to this specific scenario.
Description of the problem:
The second line in in the following bash script triggers the error:
#!/bin/bash
sessionuser=$( ps -o user= -p $$ | awk '{print $1}' )
print $sessionuser
Here is the error message:
Unescaped left brace in regex is deprecated, passed through in regex; marked by <-- HERE in m/%{ <-- HERE (.*?)}/ at /usr/bin/print line 528.
Things I have tried:
- I have tried every combination of single quotes, back angled single quotes, double quotes, and spacing I could think of both inside and outside the $() command output capture method.
- I have tried using $( exec … ) where … is the command being attempted here.
- I have read up on bash, and searched these forums and many others and nothing seems illuminate why this error message is happening or how to work around it.
If the suggestion given in the error message is followed like this:
sessionuser=$( ps -o user= -p 1000 | awk '\{print $1}' )
It results in the following error message combined with the previous one:
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.
The message refers to line 528 in /usr/bin/print. Here is that line:
$comm =~ s!%{(.*?)}!$_="'$ENV{$1}'";s/\`//g;s/\'\'//g;$_!ge;
Rational for my bash script:
The string $USER can be rewritten and is therefore not necessarily reliable. The command "whoami" will return different results depending on whether or not privileges have been elevated for the current user.
As such there is a need for reliably attaining the current session users name for portability of scripting, and that is because I am probably not going to keep the same user name forever and would like my scripts to continue working regardless of who I have logged in as.
All of that is because user files are being backed up that have huge directory structures and many files. Every once in a while a file with root ownership and permissions will end up in that backup stack for that user.
There are lots of reasons why this happens and sometimes its just because that user backed up a wallpaper or a theme they like from the system directory structure, or sometimes its because a project was compiled by that user and some of its directories or files needed to be set to root ownership and permissions for it to function in some way, and other times it may be due to some other strange unaccounted for thing.
I understand that rsync might be able to handle this problem, but I'd like to understand how to tackle the "Unescaped left brace" in a Bash script problem first.
I can study rsync on my own, but after trying for a few days this bash script doesn't appear to have a solution that is easy to discover or illuminate through either online searches or reading the manuals.
[UPDATE 01]:
Some information was missing from my original post so I'm adding it here.
Here are the relevant system specs:
- OS: Xubuntu 16.04 x86_64
- Bash: GNU bash, version 4.3.46(1)-release (x86_64-pc-linux-gnu)
Source and Rational for the commands I'm using:
3rd reply down in the following thread:
https://stackoverflow.com/questions/19306771/get-current-users-username-in-bash
Print vs. Printf
I posted this question using "print" instead of "printf" because the source I copied it from used the "print" syntax. After using "printf" I get the same error message with an added error message as output:
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"
Where "sessions_username_here" is a replacement of the actual sessions user name for the purpose of keeping the discussion generalized to whatever username could or might be used.
[UPDATE FINAL]
The chosen solution offered by Stéphane Chazelas clarified all the issues my script was having in a single post. I was mistakenly assuming that the 2nd line of the script since the output was complaining about brackets. To be clear it was the 3rd line that was triggering the warning (see Chazelas post for why and how) and that is probably why everyone was suggesting printf instead of print. I just needed to be pointed at the 3rd line of the script in order to make sense of those suggestions.
Things that didn't work as suggested:
sessionuser=$(logname)
Resulting error message:
logname: no login name
…so maybe that suggestion isn't quite as reliable as it might seem on the surface.
If user privileges are elevated which is sometimes the case when running scripts then:
id -un
would output
root
and not the current session's user name. This would probably be a simple matter of making sure the script drops out of root privileges before execution which could solve this issue but that is beyond the scope of this thread.
Things that did or could work as suggested:
After I figure out how to verify my script is running in a POSIX environment and somehow de-elevating root privileges, then I could indeed use "id -un" to acquire the current sessions username, but those verifications and de-escilations are beyond the scope of this threads question.
For now "without" POSIX verification, privilege testing, and de-escalation the script does what was originally intended to do without error. Here is what that script looks like now:
#!/bin/bash
sessionuser=$( ps -o user= -p $$ | awk '{printf $1}' )
printf '%s\n' "$sessionuser"
Note: The above script if run with elevated privileges still outputs "root" instead of the current sessions username even though the privilege escalated command:
sudo ps -o user= -p $$ | awk '{printf $1}'
will output the current sessions username and not "root" so even though the scope of this thread is answered I am back to square one with this script.
Thanks again to xtrmz, icarus, and especially Stéphane Chazelas who somehow was able catch my misunderstanding of the issue. I'm really impressed with every one here. Thanks for the help! 🙂
Best Answer
It's the third line (
print $sessionuser
) that causes that error, not the second.print
is a builtin command to output text inksh
andzsh
, but notbash
. Inbash
, you need to useprintf
orecho
instead.Also note that in
bash
(contrary tozsh
, but likeksh
), you need to quote your variables.So
zsh
's:(though I suspect you meant:
If the intent was to write to stdout the content of that variable followed by a newline) would be in
bash
:(also works in
zsh
/ksh
).Some systems also have a
print
executable command in the file system that is used to send something to a printer, and that's the one you're actually calling here. Proof that it is rarely used is that your implementation (same as mine, as part of Debian's mime-support package) has not been updated afterperl
's upgrade to work around the fact thatperl
now warns you about those improper uses of{
in regular expressions and nobody noticed.{
is a regexp operator (for things likex{min,max}
). Here in%{(.*?)}
, that(.*?)
is not amin,max
, stillperl
is lenient about that and treats those{
literally instead of failing with a regexp parsing error. It used to be silent about that, but it now reports a warning to tell you you probably have a problem in your (hereprint
's) code: either you intended to use the{
operator, but then you have a mistake within. Or you didn't and then you need to escape those{
.BTW, you can simply use:
to get the name of the user that started the login session that script is part of. That uses the
getlogin()
standard POSIX function. On GNU systems, that queriesutmp
and generally only works for tty login sessions (as long as something likelogin
or the terminal emulator registers the tty withutmp
).Or:
To get the name of one user that has the same uid as the effective user id of the process running
id
(same as the one running that script).It's equivalent to your
ps -p "$$"
approach because the shell invocation that would executeid
would be the same as the one that expands$$
and apart fromzsh
(via assignment to theEUID
/UID
/USERNAME
special variables), shells can't change their uids without executing a different command (and of course, of all commands,id
would not be setuid).Both
id
andlogname
are standard (POSIX) commands (note that on Solaris, forid
like for many other commands you'd need to make sure you place yourself in a POSIX environment to make sure you call theid
command in/usr/xpg4/bin
and not the ancient one in/bin
. The only purpose of usingps
in the answer you linked to is to work around that limitation of/bin/id
on Solaris).If you want to know the user that called
sudo
, it's via the$SUDO_USER
environment variable. That's a username derived bysudo
from the real user id of the process that executedsudo
.sudo
later changes that real user id to that of the target user (root
by default) so that$SUDO_USER
variable is the only way to know which it was.Note that when you do:
That
$$
is expanded by the shell that invokessudo
to the pid of the process that executed that shell, not the pid ofsudo
orps
, so it will give not give youroot
here.Would give you the process that executed that
sh
(running asroot
) which is now either still runningsh
or possiblyps
forsh
invocations that don't fork an extra process for the last command.That would be the same for a script that does that same
ps -p "$$"
and that you run assudo that-script
.Note that in any case, neither
bash
norsudo
are POSIX commands. And there are many systems where neither are found.