5

I've got text with whitespace (including a newline) that I need to replace. I'd like to use sed, but AIX doesn't seem to support the answers to sed newline questions I found. The version of bash I'm using is "GNU bash, version 3.00.16(1)-release (powerpc-ibm-aix5.1)"

If I run a command that outputs a bunch of pairs of lines of text like:

alias:  aliasname
                10:00:00:00:00:00:00:00

What is the best way to make it one line for each? If I use ssh user@system command | tr '\n' ' ', it replaces both newlines, and I need to keep the last one or else the output goes into a single line.

Edit: what I've tried so far is:

  • | sed -e ':1' -e 'N' -e '$!b1' -e 's/\n/ /g' which replaces all the newlines, putting the entire output stream into a single line.
  • | while read i; do printf "%s " $i; done; echo which also replaces all the newlines
  • | tr -d '\n' and | tr -d '\n'; echo, which still replace all newlines, however the echo adds one back at the last command.
Basil
  • 153
  • Is there any sort of pattern to what appears on the lines? Like, is the first line always alias: aliasname or something? – evilsoup Oct 31 '14 at 18:41

4 Answers4

5

Easyest

ssh user@system command | tr '\n' ' ';echo

Or if you want sed

ssh user@system command | sed 'N;s/\n\s\+/ /'
Costas
  • 14,916
  • I tried this and it didn't replace the newline. – Basil Oct 30 '14 at 15:57
  • @Basil Show the exact line by ssh user@system command | cat -A - – Costas Oct 30 '14 at 16:17
  • cat: Not a recognized flag: A

    Version of bash is: GNU bash, version 3.00.16(1)-release (powerpc-ibm-aix5.1)

    – Basil Oct 30 '14 at 18:32
  • @Basil OK, you haven't GNU utilites. What you can receive on ssh user@system command | hd – Costas Oct 30 '14 at 18:44
  • "hd"? That's not installed... what's it supposed to do? I googled around and only found stuff that didn't seem relevant. – Basil Oct 30 '14 at 20:36
  • @Basil hd (hexdump) show all chars in hex, octal, ascii etc codes. May be you have od? – Costas Oct 30 '14 at 20:41
  • @Costas od is POSIX, which is all you can count on on AIX. hd is a BSD utility. – Gilles 'SO- stop being evil' Oct 30 '14 at 23:38
  • 1
    Thanks guys! This is a really useful tool I learned about :) The output of an od -a shows me the characters ht lf ht ht between the lines I need to replace. The output of a straight od is 005011 004465 030072 030066. – Basil Oct 31 '14 at 16:29
  • I should clarify- that tr worked, but it puts all the lines into a single massive line. I edited the question, realizing that I hadn't explained the stream well- I'm getting an arbitrary number of pairs of lines that I'd like to turn into one line each. If this were linux, I'd use sed to replace the particular whitespace pattern between lines in a pair. – Basil Oct 31 '14 at 17:08
  • The question talks about tr '\n' ' ' and s/\n/ /g, so the OP obviously wants to replace newlines with spaces on a one-to-one basis — so why does your second answer replace a newline only if it is followed (at the beginning of the next line) with one or more whitespace characters, and then delete those character(s)? – G-Man Says 'Reinstate Monica' Mar 10 '18 at 05:09
4

Try this:

sed -e ':1' -e 'N' -e '$!b1' -e 's/\n/ /g'
cuonglm
  • 153,898
4

Here's a pure bash approach that will delete the first newline of every pair:

command | 
  while IFS=$'\n' read i; do 
    let c++; [ "$(expr $c % 2)" -eq "0" ] && echo "$i" || printf "%s " "$i"; 
  done

If you don't need to keep the whitespace unchanged, you can leave out the IFS:

command | 
  while read i; do 
    let c++; [ "$(expr $c % 2)" -eq "0" ] && echo "$i" || printf "%s " "$i"; 
  done

The same idea using perl:

command | perl -ne 'chomp;$. % 2 == 0 ? print "$_\n" : print "$_"' 
terdon
  • 242,166
  • Thanks, like you said in chat: ugly, but functional and portable ;) – Basil Oct 31 '14 at 17:30
  • (1) IFS="\n" doesn’t work.  (Perhaps you meant IFS=$'\n'? But why not just IFS=?)  IFS="\n" seems to be equivalent to IFS=n.  If a line of input contains exactly one n, and it is the last character on the line, it will be stripped.  (Try your name, for example.)  (2) Your script fails in the pathological case that the last line of the input doesn’t end with a newline.  (3) Seriously?  You’re using expr?  In a loop? – G-Man Says 'Reinstate Monica' Mar 10 '18 at 04:57
  • @G-Man (1) you're absolutely right, no idea what I was thinking. It does actually produce the expected output with the OP's example, which is why I didn't realize at the time. I wanted \n to leave other whitespace unchanged. (2) yes, but this shouldn't be done in the shell in the first place. I was going through a shell phase at the time and was interested in shell-only solutions. Silly though. (3) Why not? it's slow and inefficient, yes (see (2)), but if you're going for slow and inefficient (shell) anyway, why not? – terdon Mar 10 '18 at 14:30
0

It probably best to do this with sed; see Why is using a shell loop to process text considered bad practice?  For example, this minor variation of Costas’s second (sed) answer:

sed 'N; s/\n/ /'

does a pretty good job of answering the question: N to append the next line of input into the pattern space, and then s/\n/ / to do the substitution.  (And then these commands repeat for every two lines of input.)

But, if you want to do it entirely in the shell, this variation of terdon’s answer:

while IFS= read -r line1
do
    IFS= read -r line2; printf '%s %s\n' "$line1" "$line2"
done

works without running any external program(s).  The logic is (IMHO) simple: while not at the end of the input, read a line, and then read another line, and then output the two strings, separated by spaces, on one line.

The above code has a problem with an edge case: if the input is an even number of well-formed lines followed by some characters not terminated with a newline, it discards the final (partial) line.  (terdon’s answer has the same issue, but not just for even numbers of lines.)  This is because read returns failure when it reads a partial line, even though it does put the data into the variable.  So the fix is this:

while IFS= read -r line1  ||  [ "$line1" != "" ]
do
    IFS= read -r line2; printf '%s %s\n' "$line1" "$line2"
done

which keeps going until read fails and sets the variable to an empty string.