0

How to extract one row if it contains specific characters such as "|" and put it on the first row?

For example:


varl="This is the first row (I might be specific)
...
This is the ith row (I might be specific)
...
this row contains I|am|sepcific|
...
This is the last row (I might be specific)
"

The output should look like:


varl="this row contains I|am|sepcific|
This is the first row (I might be specific)
...
This is the ith row (I might be specific)
...
...
This is the last row (I might be specific)
"

The specific row could be present in any row.

What I did is a bit messy and didn't get the right result:

new_varl=`echo "$varl" | grep '|'`\n`echo "$varl" | grep -v '|' `

But the result is :

new_varl="this row contains I|am|sepcific|\nThis is the first row (I might be specific)
...
This is the ith row (I might be specific)
...
...
This is the last row (I might be specific)"

\n didn't work as a carriage return

Jiao
  • 375

4 Answers4

1

\n is not a Carriage Return, it's a linefeed. Carriage Return is \r and a newline is either \n or \r\n or \n\r depending on your platform. On Unix it's \n.

You can't grep for a linefeed in text that's separated by linefeeds. grep is applying the regexp to 1 line at a time and by definition a line is separated from other lines by linefeeds and so cannot contain a linefeed.

Using any awk:

awk '/\|/{ print; next } { tail=tail $0 ORS } END{ printf "%s", tail }'

e.g.:

$ echo "$varl"
This is the first row (I might be specific)
...
This is the ith row (I might be specific)
...
this row contains I|am|sepcific|
...
This is the last row (I might be specific)

$ varl=$( echo "$varl" | awk '/\|/{ print; next } { tail=tail $0 ORS } END{ printf "%s", tail }' )

$ echo "$varl"
this row contains I|am|sepcific|
This is the first row (I might be specific)
...
This is the ith row (I might be specific)
...
...
This is the last row (I might be specific)
Ed Morton
  • 31,617
1

The issue with your command, besides being too complicated (a long pipeline and nested back-quotes), is that it tries to use n as a command and that it uses echo which, under some circumstances, may modify the data it's outputting.

This replaces the value in varl with all the lines that contains | followed by all lines that does not contain |:

varl=$( grep    -F '|' <<<"$varl"
        grep -v -F '|' <<<"$varl" )

or

varl=$( grep -F '|' <<<"$varl"; grep -v -F '|' <<<"$varl" )
  • The -F option forces grep to treat the pattern expression as a string (not as a regular expression).
  • The <<<"..." thing is a non-standard feature of bash and some other shells called a "here-string". It feeds the given string to the command on the command's standard input stream. As a standard alternative, you should pipe the input from printf '%s\n' "$varl", but not echo "$varl" (see Why is printf better than echo?).
  • Two commands that should be run unconditionally and in sequence may be separated with a literal newline, or by ;.

Looking again at your attempted command (which, by the way, can't have produced the output that you show), it strikes me that you may have tried to use \n as a separator between the two echo+grep pipelines. As shown above, use ; instead, or a literal newline.

Kusalananda
  • 333,661
-1
new_varl=`echo "$varl" | grep '|'`$'\n'`echo "$varl" | grep -v '|' `
Jiao
  • 375
-2

Put it in quotes.

new_varl=`echo "$varl" | grep '|'`"\n"`echo "$varl" | grep -v '|' `
echo "$new_varl"

For tasks like this, it is better to work with intermediate variables:

var_s=`echo "$varl" | grep '|'`
var_ns=`echo "$varl" | grep -v "$var_s"`
new_varl="$var_s\n$var_ns"

Easier to read and maintain. It is a matter of taste of course, but that particular error in the one-liner become very easy to notice and solve.

White Owl
  • 5,129