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.

I have a file cities like this:

[1598] San Diego, US (inactive)
[4517] St Louis, US (inactive)
[6346] Orlando, US (inactive)

I want to cut out the city names, so that I have:

San Diego
St Louis
Orlando

This is the best I could come up with:

cut -d ',' -f1 cities | cut -d ']' -f2

But that still leaves me with a space before the names. Is there a cut like command that I can use that accept delimiters of several characters so I can cut on ]?

share|improve this question
7  
+1 for the subject alone. :) –  mattdm Apr 22 '11 at 13:57
1  
tr is useful for deleting characters you don't want. –  ultrasawblade Apr 22 '11 at 14:50
    
If you try the code in people's answers, you will see three different outputs. This suggests your question was not 100% clear. Does "cut out" mean remove or select? Do you want the (inactive) status or not? Please provide sample output. –  Mikel Apr 23 '11 at 3:42
    
@Mikel - Considering I'm using cut to cut things out and you can see the intent of the failed example I have, it should be fairly clear in the context. I will provide sample out though to clear it up further. :) –  Kit Sunde Apr 23 '11 at 4:25
1  
@Kit Sunde: With the sample output, it's certainly understandable. The title is cute. "cut out" makes me think of what happens when you press Ctrl+X, which is why I suggested the change, but it's your question. Downvoting would be silly when it's just a simple disagreement. –  Mikel Apr 23 '11 at 7:13
show 3 more comments

4 Answers

up vote 14 down vote accepted

Awk (also check Awk Info) is beautiful with that sort of question. Try:

awk -F'[],] *' '{print $2}' cities

This defines a field separator -F as [],] * - which means one occurence of either a closing square bracket or a comma, followed by zero or any number of spaces. Of course you can change that to suit any requirement. Read up on regular expressions.

Once the line is split, you can do what you want with the split result. Here, I decided to print out the second field only with print $2. Note that it is important to use single quotes around the awk instructions otherwise $2 gets substituted by the shell.

share|improve this answer
2  
] is not an angle bracket. Angle brackets are <>. [] are "square brackets", or just "brackets". –  cjm Apr 22 '11 at 18:54
    
I think you need to escape that closing bracket, unless I actually do need to go read up on my regular expressions. –  Kit Sunde Apr 22 '11 at 18:58
    
@cjm - Maybe he's German: news.ycombinator.com/item?id=1181243 :) –  Kit Sunde Apr 22 '11 at 19:01
1  
@cjm, sorry I meant to say square bracket, typed a little too fast. @Kit, I am not German. You do not want to escape the inner closing bracket (it would serve no purpose), but it must be the first character in the range. –  asoundmove Apr 22 '11 at 19:20
add comment

For more complex parsing, you should use sed(1):

sed -e 's/\[[0-9]\+\] \([^,]\+\),.*/\1/' cities

Or using -r to simplify the regular expression, as suggested by pepoluan:

sed -re 's/\[[0-9]+\] ([^,]+),.*/\1/' cities
share|improve this answer
2  
+1. you can also use -r to prevent escaping advanced regex chars, greatly simplifying the regex pattern –  pepoluan Apr 22 '11 at 15:27
    
@pepoluan Thanks. Added it to the answer. –  Juliano Apr 22 '11 at 18:04
add comment

You can modify the last cut in your pipeline to this:

cut -d ' ' -f2-

The above means that field separator is whitespace, and we want to select all fields starting from the second. The complete sequence becomes:

cut -d ',' -f1 cities | cut -d ' ' -f2-
share|improve this answer
add comment

I normally use Perl when things get too hard for sed and grep.

There are a number of ways you could write it in Perl. For example, you might prefer it to be fast, or you might prefer it to handle slight unexpected problems in the input (e.g. two spaces where one was expected).

One obvious way (assumes id is numeric, city is alphabetic, status is alphabetic):

while (<>) {
    if (/^\[\d+\] (\w+(?: \w+)*), \w+ \(\w*\)$/) {
        my $city = $1;
        print "$city\n";
    }
}

Or slower but more permissive (does more backtracking):

while (<>) {
    if (/^.*\]\s+(.*),.*$/) {
        my $city = $1;
        print "$city\n";
    }
}

Or faster (field stops at first occurrence of closing bracket):

while (<>) {
    if (/^\[[^]]*\] ([^,]*), \S+ \([^)]*\)$/) {
        my $city = $1;
        print "$city\n";
    }
}

From the command line rather than a script, you could use the -n option, which basically adds the while (<>) { BLOCK } loop:

perl -ne '/^\[[^]]*\] ([^,]*), \S+ \([^)]*\)$/ and print $1, "\n";' cities

or if you want the usage to resemble cut, you can use the -F option, which is similar to awk's -F option, for example:

perl -a -n -F'/[],]\s+/' -e 'print $F[1], "\n"' cities

This way obviously assumes that no field will contain any of the delimiters.

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.