Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Please consider this snippet:

X=$(grep -m1 'some-pattern' some-file | sed -n 's/.* //p')

I want to put last word in a variable if some pattern condition is matched for lines in arbitrary text file

My problem is that variable X has CR or LF or CRLF at the end, depending on source file, which I want to get rid of, as it interferes with later operation I intend to do.
I even tried something like:

X=$(grep -m1 'some-pattern' some-file | sed -n 's/.* \([A-Za-z]\+\)/\1/p')

thus expecting sed output to be limited on [A-Za-z]+ but there are still this nuisance bytes inside X variable.

How can I get rid of it, without using too much code like see what bytes are at the end with xxd then cut it and similar complications?

share|improve this question
add comment

5 Answers

up vote 5 down vote accepted

It seems like awk would be a better choice for your needs, as these problems don't exist due to the fact that it can use fields and records:

x=$(awk '/some-pattern/ { sub(/\r$/, "") ; printf("%s", $NF) ; exit }' some-file)

The substitution avoids your issue with CRLF line endings.

sub(/\r$/, "") removes the trailing CR, if it exists. As awk treats \n as the record (line) separator, you do not need to strip it, as it isn't in the data being looked at.

printf("%s", $NF) prints the final field ($NF) with no trailing newline (print and some other awk functions append a newline by default).

exit happens after the first two actions -- this is the equivalent of m1 in your grep command line. This ensures that awk exits after the execution of the previous two commands -- and since these commands are issued upon a match, and awk evaluates data in a FIFO manner, this will only print the first match.

share|improve this answer
    
Thanks, it looks elegant but unfortunately CRLF is still inside X –  zetah Mar 3 '12 at 1:23
    
:) Now it doesn't look elegant any more and it is still no good –  zetah Mar 3 '12 at 1:26
    
@zetah - There won't be a CR, but there will be an LF. I had a hard time understanding what you want from the question, hopefully my edit does what you want. –  Chris Down Mar 3 '12 at 1:27
    
OK, this time it does it good - output last word in a line if that line satisfies some pattern condition - don't know, maybe it's clear to me because I have this problem, and then hard to explain as non-native english speaker. Anyhow, I'll wait a bit more if someone addresses this with grep/sed solution instead awk (which I don't understand), and if not I'll use it. Thanks –  zetah Mar 3 '12 at 1:40
    
@zetah - I'll add an explanation so you can understand it better, one second. –  Chris Down Mar 3 '12 at 1:41
show 1 more comment

The `` or $() will remove the newline from the end, but to do this programatically, use tr.

grep -m1 'some-pattern' some-file | sed -n 's/.* //p' | tr -d '\012\015'

This will remove the carriage return and/or the newline from the string.

What might be the problem is how you then output the result. For example, by default, echo adds a newline. You may want to use echo -n or printf.

share|improve this answer
    
This will also remove carriage returns that may occur throughout the string, which might not be desired. –  Chris Down Mar 3 '12 at 2:07
    
Yes, while it is possible to have a carriage return embedded within a single line, it is extremely rare. The -m1 will ensure that there is only one line output, which in all likelihood, would have the carriage return at the end. –  Arcege Mar 3 '12 at 2:23
    
ah tr... interesting, works both on LF and CRLF files. I would think \010\013 for some reason, and also \f\r works correctly. About the result: I don't actually put the output in variable but as variable enclosed in $() in pattern for grep match - some pipe | grep -o " $(...) ". Thanks for comments –  zetah Mar 3 '12 at 2:37
add comment

I prefer this way

grep -m1 'some-pattern' some-file | sed -n 's/.* //p' | tr -d '\n'
share|improve this answer
add comment

This works for me:

grep -m1 'some-pattern' some-file | sed -n 's/.* //p' | tr -d "\n" | tr -d "\r"
share|improve this answer
add comment

Why not simply let sed do the [\r\f] cleanup:

# using Bash's $'string' idiom (that decodes ANSI C escape sequences)
# cf. http://wiki.bash-hackers.org/syntax/quoting#ansi_c_like_strings
- X="$(grep -m1 'some-pattern' some-file | sed -n 's/.* //p')"
+ X="$(grep -m1 'some-pattern' some-file | sed -n -e $'s/[\r\f]*$//' -e 's/.* //p')"

Your second approach lacks a final regex to catch the trailing CR, \r.

# sample code to remove trailing \r with sed
# cf. http://en.wikipedia.org/wiki/Regular_expression#POSIX_character_classes
printf 'a b c\r' | sed -n 's/^.* \([[:alpha:]]\{1,\}\)/\1/p' | od -c
printf 'a b c\r' | sed -n 's/^.* \([[:alpha:]]\{1,\}\)[[:space:]]*/\1/p' | od -c

# keeps trailing space after c
printf 'a b c \r' | sed -n 's/^.* \([[:alpha:] ]\{1,\}\)[[:space:]]*/\1/p' | od -b
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.