Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
619 views
in Technique[技术] by (71.8m points)

terminal - Using command sed but getting error every time

My task is:

Write a command line that displays the output of a cat /etc/passwd, removing comments, one line every two starting with the second, inverting each login and sorting in reverse alphabetical order, keeping only the logins between FT_LINE1 and FT_LINE2 included, separated by "," (without quotes), and ending with ".".

This is my attempt so far:

cat /etc/passwd | grep ':*:' | cut -d : -f 1 | sed '1!n;d' | rev |
sort -rdf | sed -n "$FT_LINE1,$FT_LINE2" | sed 's:,: :g' |
awk 1 ORS=', ' | sed 's/..$/./' | tr -d '
'

Using this code I get

sed: 1: ", p": invalid command code ,

What am I doing wrong?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

As noted in the comments, the primary problem is that the shell variables $FT_LINE1 and $FT_LINE2 are not set correctly. You must ensure they are set to suitable values.

The exercise is convoluted and nasty. It also requires UUoC (Useless Use of cat), but you can't help that.

There are several ways to do it. A first variant is:

# Set range of lines to print
FT_LINE1=3
FT_LINE2=9
cat /etc/passwd |                       # UUoC required by question
grep -v '^[[:space:]]*#' |              # Non-comment lines
sed -e 's/:.*//' -e '1!n;d' |           # Remove info after login name; print lines 2,4,6,8,...
rev |                                   # Reverse each name
sort -rdf |                             # Sort in reverse dictionary order, case-insensitively
sed -n "${FT_LINE1},${FT_LINE2}p" |     # Select lines in the range $FT_LINE1 to $FT_LINE2 inclusive
tr '
' ',' |                           # Convert newlines to commas
sed 's/,$/./'                           # Convert final comma to dot

This uses grep, sed, rev, sort and tr. The grep | sed part isn't entirely satisfactory, but line counting for lines 2, 4, 6, 8, … in sed gets messy if it also tries to eliminate comments.

A second variant uses awk twice. The first awk ignores comment lines and for every second non-comment line, reverses the data in field one before printing it (combining the grep | sed | rev sequence into one command). The second awk concatenates the lines from the sort into a comma-separated list and prints it with a dot at the end.

# Set range of lines to print
FT_LINE1=3
FT_LINE2=9
cat /etc/passwd |
awk -F: 
    '/^#/ { next }
     {  if (++numout % 2 == 0)
        {
            out = ""
            for (i = length($1); i > 0; i--)
                out = out substr($1, i, 1)
            print out
        }
     }
    ' |
sort -rdf |
awk -v line1="$FT_LINE1" -v line2="$FT_LINE2" 
    'NR >= line1 && NR <= line2 { out = out pad $1; pad = "," }
     END { print out "." }
    '

On my Mac, the output from the two scripts is the same — YMWV (Your Mileage Will Vary because your list of users will be different):

www_,vamalc_,toorsmvc_,toor,tocevod_,tnegaevitpac_,svc_.
www_,vamalc_,toorsmvc_,toor,tocevod_,tnegaevitpac_,svc_.

Given GNU Awk, it is probably possible to do the whole job in a single Awk script; it has built-in sorting functions. However, case-insensitive string comparison does not seem to be one of the built-in options, so it gets fiddly. Hence, I kept the external sort command. Where the first script has print out, a sorting script would use saved[i++] = out. The END block would then sort the saved array (probably using a custom function to sort case-insensitively), and then select elements from line1 through line2 of the sorted array, concatenating them and printing the result, rather like the second script does anyway.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...