4

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:

  1. 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.
  2. I have tried using $( exec ... ) where ... is the command being attempted here.
  3. 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! :)

  • What is the output of ls -l /usr/bin/print ? – xtrmz Nov 12 '16 at 21:10
  • Yes the output seems to be pointing to the incorrect command. Here is that output: lrwxrwxrwx 1 root root 11 Oct 30 2015 /usr/bin/print -> run-mailcap – neurojelly Nov 12 '16 at 21:26
  • Would /usr/bin/id -rnu solve your issues? – icarus Nov 12 '16 at 21:26
  • @ icarus. Thank you for the suggestion, but the issue is that the use of /usr/bin/id is not always system compatable. From another thread: On Solaris you have problems with methods, described above: id does not accept -u and -n parameters (so you will have to parse output). ...note that I'm not using Solaris, but I can't predict the future so I'm just trying to make my scripts as cross platform compatible as is possible while sticking with bash. – neurojelly Nov 12 '16 at 21:29
  • @neurojelly, Solaris id accepts -un provided you call the right id command (see my comment to that answer there). – Stéphane Chazelas Nov 12 '16 at 22:57
5

It's the third line (print $sessionuser) that causes that error, not the second.

print is a builtin command to output text in ksh and zsh, but not bash. In bash, you need to use printf or echo instead.

Also note that in bash (contrary to zsh, but like ksh), you need to quote your variables.

So zsh's:

print $sessionuser

(though I suspect you meant:

print -r -- $sessionuser

If the intent was to write to stdout the content of that variable followed by a newline) would be in bash:

printf '%s\n' "$sessionuser"

(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 after perl's upgrade to work around the fact that perl now warns you about those improper uses of { in regular expressions and nobody noticed.

{ is a regexp operator (for things like x{min,max}). Here in %{(.*?)}, that (.*?) is not a min,max, still perl 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 (here print'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:

sessionuser=$(logname)

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 queries utmp and generally only works for tty login sessions (as long as something like login or the terminal emulator registers the tty with utmp).

Or:

sessionuser=$(id -un)

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 execute id would be the same as the one that expands $$ and apart from zsh (via assignment to the EUID/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 and logname are standard (POSIX) commands (note that on Solaris, for id like for many other commands you'd need to make sure you place yourself in a POSIX environment to make sure you call the id command in /usr/xpg4/bin and not the ancient one in /bin. The only purpose of using ps 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 by sudo from the real user id of the process that executed sudo. 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:

sudo ps -fp "$$"

That $$ is expanded by the shell that invokes sudo to the pid of the process that executed that shell, not the pid of sudo or ps, so it will give not give you root here.

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

Would give you the process that executed that sh (running as root) which is now either still running sh or possibly ps for sh 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 as sudo that-script.

Note that in any case, neither bash nor sudo are POSIX commands. And there are many systems where neither are found.

  • Thank you for the informative response. That was indeed the issue. Thank you also for the reminder about quoting variables and the extra description of how other shells work with the syntax, and also for the much appreciated tips on having my script validate it's in a POSIX environment before it executes. You have given me so many idea's and options to play with in terms of shaking off my rust with bash and implementing some of the things I'm learning from other languages. Now its just a matter of remembering which syntax goes with which language. Thanks! :) – neurojelly Nov 12 '16 at 23:49
  • @neurojelly, see the edit that hopefully helps clearing up some more confusions. – Stéphane Chazelas Nov 13 '16 at 9:38
0

Where does the command /usr/bin/print come from ? Il could be a symlink run-mailcap on ubuntu.

Why not use echo or printf instead of print ?

  • Thanks for the reply. '/usr/bin/print' is a file that contains a lot of whatever for the print command. I am currently researching the use of 'printf' to see if it makes a difference. – neurojelly Nov 12 '16 at 21:18
  • P.S. the command [ps -o user= -p 1000 | awk '\{print $1}'] was obtained from the 3rd reply down in the following thread on how to get the current username into a variable in bash: stackoverflow.com/questions/19306771/… – neurojelly Nov 12 '16 at 21:24
  • @xtmz: RESULTS of using "printf" instead of "print" is almost the same: 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 "name_of_user_here". ...where "name_of_user_here" is a replacement of my actual username. – neurojelly Nov 12 '16 at 21:33
  • As for echo vs. printf, I'm not using echo basically for portability of code. See the following here: unix.stackexchange.com/questions/65803/… – neurojelly Nov 12 '16 at 21:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.