How to use select with awk in bash script? - linux

I have to write a bash script for university, the text says:
Write a bash script that allows root user, to get a list of all users
of the machine. Selecting a user, using select, will be required to
indicate a directory (indicate the absolute path). At this point in
the output will have be shown a list of all files folder owned by the
user, ranked in ascending order according to the size of file.
To check if the user is root i used:
if[ "$(id -u)" = 0 ]; then
To get the list of users of the machine I was thinking of using awk:
awk -F':' '{ print$1}' /etc/passwd
How can I use select with awk?
Is there another way without using awk?
Thank you so much in advance

Here is the way to use awk in select statement, you need finish the rest for your homework (for example, sort the result)
#!/usr/bin/env bash
select user in $(awk -F ":" '{print $1}' /etc/passwd )
do
read -p "input the absolute directory: " path
find $path -type f -user "$user" -ls
done

Another way to test the UID by arithmetic (smarter!?) is :
if((UID==0)); then
...
else
...
fi
Check http://wiki.bash-hackers.org/syntax/arith_expr

Related

Awk script which takes as input the path to a directory and displays all the files whose size are more than limit?

I'm fairly new to linux and awk. I want to display all files whose size are more than (eg 3Kb) and where those files are found within a directory whose path is specified by the user.
I managed to do it by "hard-coding" the path in the terminal like this :
ls -l /home/user/Documents | ./testScript
testScript contains:
#!/bin/bash -f
awk '
BEGIN{
if($5>3000){
print $9
}
}
'
How do I do this with the user specifying a directory path?
It would be easier to use find, than a combination of ls and a script
find PATH_TO_DIRECTORY -size +10k
you can make it a bash function taking a parameter
Posting an answer as I can't comment for lack of reputation points:
Not sure what you mean by "path specified by user" but assuming you can read the path in some variable then just do this in your code:
ls -l $mypath | ./testScript
altagir's find is a better solution, but in the cases where someone wants to use this general structure but doesn't know a way other than ls to do it:
stat -c "%s %n" "$someDir"/* | awk -v max=$maxval '$1 > max { print $2 }'

Creating Users from a text file using Bash

I've been looking at some of the other answers for this question but it hasn't solved my issue.
Basically, I need to create a script that reads from a text file, that is given by a user input, and creates users from the contents of the file. I've managed to get it to create the first user but it doesn't seem to be creating the other users in the file.
My text document is literally:
user1
user2
user3
And heres the code I have:
echo -n "Enter name of text file "; read text
while read USER;
do
USERNAME=$(cut -d$'\n' -f $text)
echo $USERNAME
useradd -m "${USERNAME}"
done < $text
It seems to only be reading the very first entry in the text file but I thought using the \n would mean it cut the other lines and use them next? I tried using the 'cat' command instead but wasn't having much luck with it and this is the furthest I've managed to get but I was hoping someone would help me find where I've gone wrong.
Thanks :)
First, you should start your script with
#!/usr/bin/env bash
to ensure that it is interpreted as bash.
Second, the value you need is already available in the USER variable. There is no need to use cut.
#!/usr/bin/env bash
echo -n "Enter name of text file: "; read FILENAME
while read USER; do
echo "$USER"
useradd -m "${USER}"
done < "${FILENAME}"
You can use awk to print and pipe to shell
$ awk '{print "useradd -m "$1}' <file> | sh

How to avoid magic-numbers in shell?

I always write some magic numbers in my interactive shells and shell scripts.
For instance, If I want to list my users's names and shells, I'll write
cut --delimiter=: --fields=1,7 /etc/passwd
There exist two magic-numbers 1,7. And there are more and more magic-numbers in other circumstances.
Question
How to avoid magic-numbers in interactive shells and shell scripts?
Supplementary background
Our teacher told us using cut -d: -f1,7 /etc/passwd. But for new linux-users, they don't konw what's meaning of d,f,1,7.(not just for new linux-users,the whole system has so many configuration files that it is not easy for a person to remember every magic-numbers)
So, in interactive shells, we can use --delimiter, --fields,and the bash repl(or zsh,fish) has good tab completion to it.
How about the 1 and 7? In shell scripts, It's a good method to declare some const variables like LoginField=1 and ShellField=7 after reading the man 5 passwd. But when some one is writing in the interactive shells, it's not a good idea to open a new window and search the constants of LoginField=1,ShellField=7 and define it. how to using some thing like tab completion to simplify operations?
Use variables:
LoginField=1 ShellField=7
cut --delimiter=: --fields="$LoginField,$ShellField" /etc/passwd
Just like in other languages - by using variables. Example:
$ username_column=1
$ shell_column=7
$ cut --delimiter=: --fields="$username_column","$shell_column" /etc/passwd
The variables may be defined at the top of the script so that can be
easily modified or they can be set in an external config-like file
shared by multiple scripts.
The classic way to parse /etc/passwd is to read each column into an appropriately named variable:
while IFS=: read name passwd uid gid gecos home shell _; do
...
done < /etc/passwd
Use export:
export field_param="1,7"
(you can put it .bashrc file to have configured each time shell session is started). This export can be part of .sh script. It's a good practice to put them in the head/top of the file.
Then:
cut --delimiter=: --fields=$field_param /etc/passwd
This way you will need to edit the magic number in the only location.
Continuing from my comment, it's hard to tell exactly what you are asking. If you just want to give meaningful variable names, then do as shown in the other answers.
If however you want to be able to specify which fields are passed to cut from the command line, then you can use the positional parameters $1 and $2 to pass those values into your script.
You need to validate that two inputs are given and that both are integers. You can do that with a few simple tests, e.g.
#!/bin/bash
[ -n "$1" ] && [ -n "$2" ] || { ## validate 2 parameters given
printf "error: insufficient input\nusage: %s field1 field2\n" "${0##*/}"
exit 1
}
## validate both inputs are integer values
[ "$1" -eq "$1" >/dev/null 2>&1 ] || {
printf "error: field1 not integer value '%s'.\n" "$1"
exit 1
}
[ "$2" -eq "$2" >/dev/null 2>&1 ] || {
printf "error: field2 not integer value '%s'.\n" "$2"
exit 1
}
cut --delimiter=: --fields=$1,$2 /etc/passwd
Example Use/Output
$ bash fields.sh
error: insufficient input
usage: fields.sh field1 field2
$ bash fields.sh 1 d
error: field2 not integer value 'd'.
$ bash fields.sh 1 7
root:/bin/bash
bin:/usr/bin/nologin
daemon:/usr/bin/nologin
mail:/usr/bin/nologin
ftp:/usr/bin/nologin
http:/usr/bin/nologin
uuidd:/usr/bin/nologin
dbus:/usr/bin/nologin
nobody:/usr/bin/nologin
systemd-journal-gateway:/usr/bin/nologin
systemd-timesync:/usr/bin/nologin
systemd-network:/usr/bin/nologin
systemd-bus-proxy:/usr/bin/nologin
<snip>
Or if you choose to look at fields 1 and 3, then all you need do is pass those as the parameters, e.g.
$ bash fields.sh 1 3
root:0
bin:1
daemon:2
mail:8
ftp:14
http:33
uuidd:68
dbus:81
nobody:99
systemd-journal-gateway:191
systemd-timesync:192
systemd-network:193
systemd-bus-proxy:194
<snip>
Look things over and let me know if you have further questions.
Scraping the output of man 5 passwd for human-readable header names:
declare $(man 5 passwd |
sed -n '/^\s*·\s*/{s/^\s*·\s*//;y/ /_/;p}' |
sed -n 'p;=' | paste -d= - - )
See "how it works" below for what that does, then run:
cut --delimiter=: \
--fields=${login_name},${optional_user_command_interpreter} /etc/passwd
Which outputs the specified /etc/passwd fields.
How it works.
The man page describing /etc/passwd contains a bullet list of header names. Use GNU sed to find the bullets (·) and leading whitespace, then remove the bullets and whitespace, replace the remaining spaces with underlines; a 2nd instance of sed provides fresh line numbers, then paste the header names to the line numbers, with a = between:
man 5 passwd |
sed -n '/^\s*·\s*/{s/^\s*·\s*//;y/ /_/;p}' |
sed -n 'p;=' | paste -d= - -
Outputs:
login_name=1
optional_encrypted_password=2
numerical_user_ID=3
numerical_group_ID=4
user_name_or_comment_field=5
user_home_directory=6
optional_user_command_interpreter=7
And declare makes those active in the current shell.

How can i search for a specific string after an ls command

I am trying to make a shell script that searches the /proc directory and finds the user of the process. how am i supposed to do that? i am trying different ways with grep like
NUM=`egrep -ri "$a1" /proc/ 2> error.txt `
echo "$NUM"
but nothing happen.Can someone help?
grep searches the contents of files. To search files owned by someone, try something like
find /proc -user someone -ls
Trivially, you could pipe ls through a filter to obtain only those lines where the third field is someone, as hinted at in comments;
ls -l | awk '$3=="someone"'
... but for many various reasons you should avoid parsing ls output in general.
If you know the numeric user ID, you could do something like
awk '$1=="Uid" && $2==1234 { print FILENAME }' /proc[1-9]*/status
to obtain the file names of the processes owned by UID number 1234. Something very similar could be obtained by find, as in the first example. (Maybe restrict search to only numeric directory names?)
To find the userid of a process via the /proc pseudo-filesystem, grab the "Uid" line from the "status" file:
uid_line=`grep -e ^Uid /proc/1/status`
You can parse that apart into real, effective, saved set, and filesystem uids. The second field is the real uid:
real_uid=`echo "$uid_line"|cut -f 2`
You can also convert uid into username using getent:
username=`getent passwd $real_uid|cut -d: -f 1`
I am pretty sure userids for processes are in /proc/PID/loginid.

Why can't I run my shell script to list users?

users='awk '{print $1}' /etc/passwd | sort -u'
for user in $users
do
echo " - $user"
done
this is my shell script . Problem is that show's an error.
the error is ---> users: command not found
please give me the solution frinds
With the code the way it is now I see that you're not assigning the output of the awk|sort command to the variable (maybe you wanted to use ` instead of ' ?)
This works:
#!/bin/bash
users=$(awk '{print $1}' /etc/passwd | sort -u)
for user in $users
do
echo " - $user"
done
Although you should be aware that /etc/passwd is not separated by spaces, so awk '{print $1}' won't give you the user's name (which maybe is what you wanted)
Edit:
As per #Andy Lester's comment to your question: If you save this code in a file (let's say /tmp/myscript.bash) to run it you have to type in a terminal:
/bin/bash /tmp/myscript.bash
or, since it starts with #!/bin/bash (read here) you could make it executable (using chmod u+x /tmp/myscript.bash) and then call it, just typing /tmp/myscript.bash. You can also save it in one of the PATH directories (type echo $PATH to see which are they), make it executable and then you'll be able to call it from anywhere, but I don't really recommend doing that because you may end up overwriting juicy system's commands if you're not careful. For instance, let's say you call your script with the unfortunate name of ls, save it in the first directory of the $PATH (in my case, /usr/local/sbin) Every time you type ls, you won't be listing directories, but calling your script... Which is bad.

Resources