Is there a simple alternative to "who am i" and "logname"? - linux

I noticed that RHEL 8 and Fedora 30 don't update the utmp file properly.
As a result, commands such as 'who am i', 'last', 'w' etc print incorrect results (who am i actually doesn't print anything)
After a bit of googling, I found 'logname' which worked in this case but I read that gnome is dropping support for utmp altogether so it's a matter of time until this stops working too.
I wrote the following script which finds the login name of the user (even if he is using sudo the moment he runs the command) but it's way too complicated so I'm looking for alternatives.
LOGIN_UID=$(cat /proc/self/loginuid)
LOGIN_NAME=$(awk -v val=LOGIN_UID -F ":" '$3==val{print $1}' /etc/passwd)
Is there a simple alternative which is not based on proper updating of /var/run/utmp ?
Edit 1: Solutions that don't work $HOME, $USER and id return incorrect values when used in a script that has been run with the sudo command. who am i and logname depend on utmp which isn't always updated by the terminal.
Working solution: After a bit of searching, a simpler way than the aforementioned was found in https://unix.stackexchange.com/users/5685/frederik-deweerdt 's comment to his own answer
Link to answer which contains the commment: https://unix.stackexchange.com/a/74312
Answer 1
stat -c "%U" $(tty)
Second answer found at https://stackoverflow.com/a/51765389/10630167
Answer 2
`pstree -lu -s $$ | grep --max-count=1 -o '([^)]*)' | head -n 1 | sed 's/[()]//g'`

Your question is not well-defined because if X and Y are not working, what are the chances that Z will work? This depends entirely on the precise failure mode you are attempting to handle, and there is nothing in your question to reveal the specific circumstances in which you need this.
With that out of the way, perhaps look at the POSIX id command, which has explicit options to print the real (login) or effective (after any setuid command) user id with -r or -u, respectively. Of course, the precise means by which it obtains this information are not specified, and will remain implementation-dependent, and thus might or might not work on your platform under your specific circumstances.
As an aside, here is a refactoring of your code to avoid polluting the variable name space with two separate variables.
LOGIN_NAME=$(awk 'NR==FNR { val=$0; next }
$3==val{print $1}' /proc/self/loginuid FS=":" /etc/passwd)

Related

Why ip_forward_use_pmtu added in the result of sysctl in linux server

So I did an OS version-up in a linux server, and was seeing if any setting has been changed.
And when I typed "sysctl -a | grep "net.ipv4.ip_forward"
The following line was added,
net.ipv4.ip_forward_use_pmtu = 0
I know that this is because this parameter is in /proc/sys.
But I think if the result of sysctl before upload did not show this line, it was not in /proc/sys before as well, right ?
I know that 0 means " this setting is not applied...So basically it does not do anything.
But why this line is added.
The question is
Is there any possible reason that can add this line?
Thank you, ahead.
Even the question itself "added in the result of sysctl in linux server" is wrong here.
sysctl in the way you invoked it, lists all the entries.
grep which you used to filter those entries "selects" matching texts, if you'd run grep foo against the list:
foo
foobar
both items would be matched. That's exactly what you see but the only difference is instead of "foo" you have "net.ipv4.ip_forward".
Using --color shows that clearly:
Pay attention to the use of fgrep instead of grep because people tend to forget that grep interprets some characters as regular expressions, and the dot . means any character, which might also lead to unexpected matches.

sed behavior: Pattern space and Address range [duplicate]

I noticed something a bit odd while fooling around with sed. If you try to remove multiple line intervals (by number) from a file, but any interval specified later in the list is fully contained within an interval earlier in the list, then an additional single line is removed after the specified (larger) interval.
seq 10 > foo.txt
sed '2,7d;3,6d' foo.txt
1
9
10
This behaviour was behind an annoying bug for me, since in my script I generated the interval endpoints on the fly, and in some cases the intervals produced were redundant. I can clean this up, but I can't think of a good reason why sed would behave this way on purpose.
Since this question was highlighted as needing an answer in the Stack Overflow Weekly Newsletter email for 2015-02-24, I'm converting the comments above (which provide the answer) into a formal answer. Unattributed comments here were made by me in essentially equivalent form.
Thank you for a concise, complete question. The result is interesting. I can reproduce it with your script. Intriguingly, sed '3,6d;2,7d' foo.txt (with the delete operations in the reverse order) produces the expected answer with 8 included in the output. That makes it look like it might be a reportable bug in (GNU) sed, especially as BSD sed (on Mac OS X 10.10.2 Yosemite) works correctly with the operations in either order. I tested using 'sed (GNU sed) 4.2.2' from an Ubuntu 14.04 derivative.
More data points for you/them. Both of these include 8 in the output:
sed -e '/2/,/7/d' -e '/3/,/6/d' foo.txt
sed -e '2,7d' -e '/3/,/6/d' foo.txt
By contrast, this does not:
sed -e '/2/,/7/d' -e '3,6d' foo.txt
The latter surprised me (even accepting the basic bug).
Beats me. I thought given some of sed's arcane constructs that you might be missing the batman symbol or something from the middle of your command but sed -e '2,7d' -e '3,6d' foo.txt behaves the same way and swapping the order produces the expected results (GNU sed 4.2.2 on Cygwin). /bin/sed on Solaris always produces the expected result and interestingly so does GNU sed 3.02. Ed Morton
More data: it only seems to happen with sed 4.2.2 if the 2nd range is a subset of the first: sed '2,5d;2,5d' shows the bug, sed '2,5d;1,5d' and sed '2,5d;2,6d' do not. glenn jackman
The GNU sed home page says "Please send bug reports to bug-sed at gnu.org" (except it has an # in place of ' at '). You've got a good reproduction; be explicit about the output you expect vs the output you get (they'll get the point, but it's best to make sure they can't misunderstand). Point out that the reverse ordering of the commands works as expected, and give the various other commands as examples of working or not working. (You could even give this Q&A URL as a cross-reference, but make sure that the bug report is self-contained so that it can be understood even if no-one follows the URL.)
You can also point to BSD sed (and the Solaris version, and the older GNU 3.02 sed) as behaving as expected. With the old version GNU sed working, it means this is arguably a regression. […After a little experimentation…] The breakage occurred in the 4.1 release; the 4.0.9 release is OK. (I also checked 4.1.5 and 4.2.1; both are broken.) That will help the maintainers if they want to find the trouble by looking at what changed.
The OP noted:
Thanks everyone for comments and additional tests. I'll submit a bug report to GNU sed and post their response. santayana

Bash run a function in background

Have a relatively simple question here. I need to run a function in the background in bash. Normally I would do it just like so:
FUNCTION &
but things are a bit more complicated than that. I have the following line that runs the main function for each record in a text database. I cant really edit this code all that much without vastly changing the rest of the entire project, but im still open to new ideas.
cat databases/$WAN | grep -v \# | while read LINE; do MAIN; done
I want to spawn a new terminal in background for each record to do a sort of parallel type processing, making things go much faster. Main takes a minute to process for each record. This however does not work.
cat databases/$WAN | grep -v \# | while read LINE; do MAIN &; done
Any suggestions?
* UPDATE *
Thanks for all the responses. Let me see if I can answer some of those questions.
gniourf_gniourf - Yes I know using cat like this is wrong. This was early on, and critical code, so I have not updated it yet. I now read into the while loop for most things I do. I will fix it eventually. You may be right about syntax. When I break it up like so, things seem to work now:
cat databases/$WAN | grep -v \# | while read LINE
do
MAIN & > /dev/null 2>&1
done
So that fixes the background problem. I wonder what was messed up in my single line syntax. Thanks
chepner - I don't believe LINE is a variable. I could be wrong though. Some things about Bash still confuse me. Maybe it is and is a variable that the entire record from the database gets stored to prior to processing.
Bruce K - Waiting is exactly what I was trying to avoid. If I let it run in the same terminal one at a time, it will slowly process each record in order. If I push each record to a seperate terminal for processing, all records will be processed simultaneously (at least in our eyes). The additional overhead is intentional in order to speed up how quickly the loop through the database occurs.
Radix - Yes you're right. I'll read up on that. Thanks for the link.
This worked for me:
$ function testt(){ echo "lineee is <$lineee>";}
$ grep 5432 /etc/services|while read lineee;do testt&done
lineee is <postgres 5432/udp # POSTGRES>
lineee is <postgres 5432/tcp # POSTGRES>
If, for some reason, your MAIN function is not seeing a LINE variable, you can try:
"export" the LINE variable beforehand:
$ export LINE
$ # do your thing
Or, pass the line read as an argument to the function:
$ function testt(){ LINE="$1"; echo "LINE is <$LINE>";}
$ grep 5432 /etc/services|while read LINE;do testt "$LINE"&done

Using -s command in bash script

I have a trivial error that I cant seem to get around. Im trying to return the various section numbers of lets say "man" since it resides in all the sections. I am using the -s command but am having problems. Every time I use it I keep getting "what manual page do you want". Any help?
In the case of getting the section number of a command, you want something like man -k "page_name" | awk -F'-' "/^page_name \(/ {print $1}", replacing any occurrence of page_name with whatever command you're needing.
This won't work for all systems necessarily as the format for the "man" output is "implementation-defined". In other words, the format on FreeBSD, OS X, various flavours of Linux, etc. may not be the same. For example, mine is:
page_name (1) - description
If you want the section number only, I'm sure there is something you can do such as saving the result of that line in a shell variable and use parameter expansion to remove the parentheses around the section number:
man -k "page_name" | awk -F'-' "/^page_name \(/ {print $1}" | while IFS= read sect ; do
sect="${sect##*[(]}"
sect="${sect%[)]*}"
printf '%s\n' "$sect"
done
To get the number of sections a command appears in, add | wc -l at the end on the same line as the done keyword. For the mount command, I have 3:
2
2freebsd
8
You've misinterpreted the nature of -s. From man man:
-S list, -s list, --sections=list
List is a colon- or comma-separated list of `order specific' manual sections to search. This option overrides the
$MANSECT environment variable. (The -s
spelling is for compatibility with System V.)
So when man sees man -s man it thinks you want to look for a page in section "man" (which most likely doesn't exist, since it is not a normal section), but you didn't say what page, so it asks:
What manual page do you want?
BTW, wrt "man is just the test case cuz i believe its in all the sections" -- nope, it is probably only in one, and AFAIK there isn't any word with a page in all sections. More than 2 or 3 would be very unusual.
The various standard sections are described in man man too.
The correct syntax requires an argument. Typically you're looking for either
man -s 1 man
to read the documentation for the man(1) command, or
man -s 7 man
to read about the man(7) macro package.
If you want a list of standard sections, the former contains that. You may have additional sections installed locally, though. A directory listing of /usr/local/share/man might reveal additional sections, for example.
(Incidentally, -s is not a "command" in this context, it's an option.)

What is a reliable way to determine which shared library will be loaded across linux platforms?

I need to find out which library will be loaded given in the information returned from /sbin/ldconfig. I came up with the following:
#!/bin/bash
echo $(dirname $(/sbin/ldconfig -p | awk "/$1/ {print \$4}" | head -n 1))
Running this results with:
$ whichlib libGL.so
/usr/X11R6/lib
This a two part question:
Will this produce a reliable result across platform?
Is there a slicker way to parse the output of ldconfig?
Thanks,
Paul
There're several ways the library is loaded by executeable:
1.
Using $LD_LIBRARY_PATH
Using ld cache
Libary with full path compiled into binary (-rpath gcc flag)
You're using option 2, while option 1 and 3 are not considered.
Depending on what exactly you're doing you may want to run ldd directly on the executable you're planning to run rather than the general case ldconfig.
Since you asked, you could write your script like this:
dirname "$(/sbin/ldconfig -p | awk "\$1 == "$1" {print \$4; exit}")"
It's a little more precise and has one less pipe. Also echo $(cmd) is redundant; you can just write cmd.

Resources