2
\$\begingroup\$

I want to make a list of the last words on each line of a file that looks like this:

casper@casper-PC ~/scripts -> cat /tmp/foo
update PTMM_ARCHIVE.FASTTRACK_USER set user_name = 'monohajoxx' where user_name = 'monohajo'
update PTMM_ARCHIVE.FASTTRACK_USER set user_name = 'wuemxx' where user_name = 'wuem'
update PTMM_ARCHIVE.FASTTRACK_USER set user_name = 'taraziemxx' where user_name = 'taraziem'
update PTMM_ARCHIVE.FASTTRACK_USER set user_name = 'mullankexx' where user_name = 'mullanke'
update PTMM_ARCHIVE.FASTTRACK_USER set user_name = 'fernanjaxx' where user_name = 'fernanja'casper@casper-PC ~/scripts ->

As you can see, the file lacks a newline at the end: 'fernanja'casper@casper-PC ~/scripts ->. I hate that when the prompt gets mixed in with the data -or the output. It's a minor point, but it makes the text on the terminal jumbled and confusing.

Also, I use tr all the time to fix my one-liners. I am looking for another way to print out the names in a horizontal line instead of a vertical list. I echo'd a new line but it printed it out literally.

09:38 PM Tue Dec 01$ awk '{print $NF}' /tmp/foo | tr -d "'" | tr "\n" ", \s" ; echo  "\n"
monohajo,wuem,taraziem,mullanke,fernanja,\n
walt@walt-PC ~/scripts ->
09:55 PM Tue Dec 01$

I do not want this:

monohajo
wuem
taraziem
mullanke
fernanja

I want this:

monohajo, wuem, taraziem, mullanke, fernanja

These are minor points, but when you do this 20 or thirty times a day, it gets tiring. It would be great just to do this in pure bash, with a while loop. Instead of vertical lists, I need horizontal lists which can be cut and pasted into emails, so I can tell bosses that I performed updates on this list of people.

\$\endgroup\$
  • 1
    \$\begingroup\$ Asking to change the behaviour is off topic. Please see help center. \$\endgroup\$ – Heslacher Dec 2 '15 at 6:54
  • \$\begingroup\$ @Heslacher I don't think the question is asking for a change in behaviour. It's just awkwardly phrased to appear that way. \$\endgroup\$ – 200_success Dec 2 '15 at 7:11
2
\$\begingroup\$

Critique

First, note that you are misusing tr when you do tr "\n" ", \s". In tr string1 string2, string1 is supposed to be at least as long as string2, as each character in string1 is translated into the corresponding character in string2. (If string1 is longer than string2, the last character found in string2 is duplicated until string1 is exhausted.) Therefore, tr "\n" ", \s" is equivalent to tr "\n" ",", which produces a comma-separated output with no spaces — in contradiction to your sample output.

The dangling ; echo "\n" at the end is awkward. You're now running two pipelines whose outputs happen to appear one after the other, rather than producing one output stream. That means that the output merely looks right on screen. You can't pipe or redirect the whole thing, though, unless you run the two pipelines in a subshell, like ( awk … | tr -d … | tr … ; echo "\n" ).

Suggested solution

awk '{ print $NF }' /tmp/foo | xargs echo will get you mostly there. By using xargs, you automatically get unquoting behaviour. (The way xargs handles quoting is not quite the same as the way SQL handles quoting, but in most cases, it's probably close enough.) The echo command will produce one line of output, ending with a newline.

The only thing remaining is to insert a comma before each space. For that, I would use | sed -e 's/ /, /g'. (If you just want commas and no spaces, you can use tr ' ' , instead.)

In summary, do

awk '{ print $NF }' /tmp/foo | xargs echo | sed -e 's/ /, /g'
\$\endgroup\$
  • \$\begingroup\$ very good answer !!!!!!! good use of xargs !!!!! \$\endgroup\$ – capser Dec 2 '15 at 7:38
0
\$\begingroup\$

There is a much better way to achieve what you want, using only Awk:

  • Given the last field ($NF)
  • Remove the quotes using gsub
  • If it's not the first line, print a comma and space (your required format)
  • Print the last field, without printing a newline, using printf instead of print
  • At the end, print a newline character (your required format)

Like this:

awk '{ gsub("'\''", "", $NF); if (NR > 1) printf ", "; printf $NF } END { print "" }' /tmp/foo

This approach is better, as there is a single awk processes, instead of awk + tr + tr + echo as in the original post. It's also a straightforward, clean logic. (I suppose the key point here was printing in Awk without printing a newline.)

\$\endgroup\$

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.