0

I am really new in UNIX (bash) and been trying to play around with the language. I am currently trying to create a table with a fixed header from a .txt file.

So I came across a code which is like this :

column -t -s : <(echo Name:Age:Gender:Height:Weight) test.txt | sed "1{p;s/./*/g}"

I am totally stumped regarding the parts of the sed command at the end. I know that the ./*/g means that the sed is replacing all of . with * and /g means global occurrence.

However, what does the 1 and the p;s actually mean?

  • Note that syntax is not portable/standard as there's a missing semicolon between the g and the }. – Stéphane Chazelas Jan 12 '18 at 12:02
2

That's a reasonably clever bit of code.

Sed, as you can learn in a good tutorial, works on addresses and commands. If the address is omitted the command runs on every line, such as the commonly seen s/old/new/.

An address can be a line number, or a regex. Or it can be a comma-separated address range, which I won't describe further. (See tutorial linked above for details.)

A command can be a single command, or multiple commands contained in curly braces.

This sed command runs the print command and then the substitute command on line 1 of the input. The result is that after the first line of input will come a string of ***'s the same length.

You may notice that the s command doesn't have any print command after it, but at the end of each line whatever is held in the "pattern space" is implicitly printed, unless the -n option was passed to sed in the first place. (Again, see the above-linked tutorial for more.)


Essentially, it's a quick way to make a pretty header for the table.


Edit to answer comment:

So the p command can be placed anywhere either at the start of the 1sedor at the end? Also, the.` (period) means that the address is omitted?

No, not quite.

The 1 means, apply the following list of commands (enclosed within curly braces) only on the first line of input.

The p means, print the current contents of the pattern space, followed by a newline. (The pattern space will at that time contain the first line of input, since that's how Sed works.)

The s/./*/g means, for EVERY match (globally) of the regex . (which matches any character), substitute the character *. This operates on the pattern space.

Then at the end of the commands (the close curly brace), since there are no more commands, the contents of the pattern space is implicitly printed. At this point it will just contain a bunch of asterisks (***).

Then the next line of input is read into the pattern space. Since the address 1 doesn't match, the curly brace block of commands is skipped. At the end of the curly brace block, since there are no more commands, the contents of the pattern space is implicitly printed. Repeat this paragraph as long as there is input. So the result is that the rest of the input after the first line is printed without change.

So the output will contain all the lines of the input, PLUS one line after the first line which will consist only of * characters and which will be the same length as the first line.

  • Hi, Thanks for replying to my queries. Oh.. So the p command can be placed anywhere either at the start of the sed or at the end? Meaning at the position of x. sed "x/.../.../g" or sed ".../.../.../g;x" Also, the .(period) means that the address is ommitted ? I initially thought that the sed will find for . and change it to the '*' pattern. – pandora Jan 12 '18 at 6:24
  • @pandora, see my edit; see if that more fully clarifies your questions. – Wildcard Jan 12 '18 at 6:31
  • 1
    Thank you for the clear explanation. You really lighten up my day :) – pandora Jan 12 '18 at 6:33
2

The sed script prints the first line of input and then prints a line of * characters of exactly the same width as the first line.

1{p;s/./*/g}
  • 1 is an address matching the first line of input only. i.e. the sed commands in the {} block apply only to the first line.
  • p - print the input without change
  • ; is a command separator, in this case separating the p command and the s/./*/g command.
  • s/./*/g - change every character to a *, and print the result.

Any remaining input (lines 2+) are printed unmodified if they exist as per sed's default behaviour (if used without the -n option)

The whole command:

column -t -s : <(echo Name:Age:Gender:Height:Weight) test.txt | sed "1{p;s/./*/g}"

uses column -t -s : to tabulate input consisting of the echoed string "Name:Age:Gender:Height:Weight" and the contents of file.txt. column's output is piped to sed to produce the line of ******s separating the header from the rest of the output.

  • omg. Thank you for the clear explanation. – pandora Jan 12 '18 at 6:29
  • . is a regular expression that matches any character. BTW, "field" is not an applicable term here. s/./*/g is a command s (substitute) that changes . (any character) to * (a literal asterisk character). The g on the end makes it a "global" replacement, so all characters that match the regexp . will be substituted. – cas Jan 12 '18 at 6:34

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.