2

I want to manage my /etc/passwd and add names for each column..

I want to read from array...

What I tried is the following bash:

#!/bin/bash

FILE="/etc/passwd"
arr=( $(awk < $FILE -F: '{print $1 $5 $6 $7}') )

for Username Realname Homedir Loginshell in "${arr[@]}"; do
    result=$(printf 'Username: %s Realname: %s Homedir: %s Loginshell: %s' "$Username" "$Realname" "$Homedir" "Loginshell")
    echo "$result"
done

But there is the error:

./u.sh: line 6: syntax error near unexpected token `Realname'
./u.sh: line 6: `for Username Realname Homedir Loginshell in "${arr[@]}"; do'

The second question is:

How can I read $FILE as an argument in such scripts...because when change the script like:

#!/bin/bash

if [ "$#" -ne "1" ]; then
    echo "Usage: `basename $0` <input-file>"
    exit 1
fi

arr=( $(awk < $1 -F: '{print $1 $2 $6 $7}') )

for Username Realname Homedir Loginshell in "${arr[@]}"; do
    result=$(printf 'Username: %s Realname: %s Homedir: %s Loginshell: %s' "$Username" "$Realname" "$Homedir" "Loginshell")
    echo "$result"
done

It would crash...

Thanks

0

2 Answers 2

2

Use it like this:

while read Username Realname Homedir Loginshell; do
    result=$(printf "Username: %s Realname: %s Homedir: %s Loginshell: %s" "$Username" "$Realname" "$Homedir" "Loginshell")
    echo "$result"
done < <(awk -F: '{print $1 $2 $6 $7}' /etc/passwd)

EDIT: You can just use awk:

if (($# == 0)); then
   echo "Usage: `basename $0` <input-file>"
   exit 1
fi
FILENAME="$1"

awk -F: '{printf "Username: %s Realname: %s Homedir: %s Loginshell: %s\n",
       $1, $2, $6, $7}' "$FILENAME"
Sign up to request clarification or add additional context in comments.

8 Comments

now the result is not good: Username: rootx/root/bin/bash Realname: Homedir: Loginshell: Loginshell
You can just pass filename as argument and use $1 inside the script.
I know but when we use awk with $1 it confuses I think...and your answer doesn't work as well/// see the result please
and better to use arrays... Thank you....Your answer was now correct because the result is: Username: rootx/root/bin/bash Realname: Homedir: Loginshell: Loginshell
In general one should not attempt to use a particular method for solving a problem if it involves writing long complicated code which is usually solvable by an alternate simple command. Your problem is best solved by awk 1 liner as I showed above. Rest I can produce 5 alternatives in shell to solve it by making it complicated but real solution still remains same which is awk.
|
1

This approach avoids awk but using bash's IFS to separate the fields in FILE:

FILE="/etc/passwd"
while IFS=: read Username pass uid gid Realname Homedir Loginshell; do
    result=$(printf 'Username: %s Realname: %s Homedir: %s Loginshell: %s' "$Username" "$Realname" "$Homedir" "$Loginshell")
    echo "$result"
done <"$FILE"

To supply the file name as an argument (as per question 2):

if [ "$#" -ne "1" ]; then
    echo "Usage: `basename $0` <input-file>"
    exit 1
fi
FILE=$1

while IFS=: read Username pass uid gid Realname Homedir Loginshell; do
    result=$(printf 'Username: %s Realname: %s Homedir: %s Loginshell: %s' "$Username" "$Realname" "$Homedir" "$Loginshell")
    echo "$result"
done <"$FILE"

If you want to use arrays, then:

#!/bin/bash
if [ "$#" -ne "1" ]; then
    echo "Usage: `basename $0` <input-file>"
    exit 1
fi
FILE=$1

#declare -a fields
IFS=:
while read line; do
    fields=($line)
    result=$(printf 'Username: %s Realname: %s Homedir: %s Loginshell: %s' "${fields[0]}" "${fields[4]}" "${fields[5]}" "${fields[6]}")
    echo "$result"
done <"$FILE"

The above reads /etc/passwd one line at a time. With IFS set to a colon, each line is then converted to an array using fields=($line). To avoid surprises, you may want to reset IFS to its default value before executing any bash code that depends on bash's normal word separation rules.

Extra:

Here is an attempt at pretty-printing the output:

#!/bin/bash
if [ "$#" -ne "1" ]; then
    echo "Usage: `basename $0` <input-file>"
    exit 1
fi
FILE=$1

{
echo "========:========:=======:=========="
echo "Username:Realname:Homedir:Loginshell"
echo "========:========:=======:=========="

IFS=:
while read line; do
    fields=($line)
    echo "${fields[0]}:${fields[4]}:${fields[5]}:${fields[6]}"
done < "$FILE"
} | column -nts:

By default, column separates fields by whitespace. However, the realname output field can contain spaces. So, the -s: option is used so that column uses colon-separated fields on input. To assure that headings lined up with the data fields, both headings and data are submitted to the same instance of column.

7 Comments

could you possibly thell what does [ "${fields[0]}" ] || break and < <(sed 's/:/\n/g' "$FILE") do?
pardon, The second thing: It doesn't show me all lines in passwd... why?
@MortezaLSC Explanation added for those two items. Let me know if you need more detail. I just re-tested all three approaches on my passwd file and it read all the way to the end. I cannot reproduce the error that you are seeing.
the first two answers are correct..and very good..the answer used by arrays is my problem..when I run this script...wc -l /etc/passwd = 36 and ./scipt.sh /etc/passwd | wc -l=18 shouldn't we change readarray -tn7 ?
Thank you...now ./scipt.sh /etc/passwd | wc -l= 32 not 36 yet
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.