3

How do I sort files or directories by part of their name, specifically the element contained in the parenthesis or separated by delimiter?

I am after two separate solutions one for directories with names structured like this:

Badger Bodger (2001)
Charlie Fisher's (1989)

And files in the following format:

Could Be A Title.2001.prop.ext1
Another Potential Title.1989.prop.ext2

In both cases the directories or files with 1989 should come before 2001. For the directories, the element to sort by is in brackets. For the files, its the first four digit number after the delimiter ..

I'm using bash running on Debian 8.0. Looking for Command line solutions please. Short scripts if written in bash acceptable.

1

Consider these files:

$ ls  --quoting-style=c -1 *.*
"Another Potential Title.1989.prop.ext2"
"being there.2000.prop.ext3"
"Could Be A Title.2001.prop.ext1"
"Yet Another Potential Title.1989.prop.ext2"

This sorts on the year:

$ ls  --quoting-style=c *.* | sort -t. -k2n
"Another Potential Title.1989.prop.ext2"
"Yet Another Potential Title.1989.prop.ext2"
"being there.2000.prop.ext3"
"Could Be A Title.2001.prop.ext1"

For your directories, a similar approach works:

$ ls --quoting-style=c -d */ | sort -t'(' -k2n
"Charlie Fisher's (1989)/"
"Badger Bodger (2001)/"

Because of the option --quoting-style=c, this approach will work even with file names with newlines or other difficult characters. If your are sure that your file names do not contain newlines, you may omit this option.

How it works

sort can break up input lines into fields. The -t option sets the field separator. For the files, the field separator is a . while for directories a field separator of ( is used. The -k option determines which field is sorted. For both cases above, we ask sort to sort numerically on the second field.

1

I’ll assume that you are sure that none of your filenames contain newlines, or that you have some way of dealing with that possibility.  First, manipulate your filenames with sed:

% command_to_list_filenames | sed 's/.*\.\([0-9][0-9][0-9][0-9]\)\..*/\1.&/'
2001.Could Be A Title.2001.prop.ext1
1989.Another Potential Title.1989.prop.ext2
%

The s (substitute) command in the sed command treats each line as a sequence of the following:

  • Any number of any character (.*),
  • An actual period (\.),
  • Four digits ([0-9][0-9][0-9][0-9]),
  • Another actual period (\.), and
  • Another character sequence (.*).

Note that the year ([0-9][0-9][0-9][0-9]) is enclosed between \( and \), forming a group.  The substitute command then replaces the string with

  • The grouped characters, i.e., the year (\1),
  • A period (.), and
  • The entire input line (&).

Then it’s a simple matter of sorting the lines by the year (which now appears at the beginning of the line) and stripping off the year:

% command_to_list_filenames | sed 's/.*\.\([0-9][0-9][0-9][0-9]\)\..*/\1.&/' | sort
1989.Another Potential Title.1989.prop.ext2
2001.Could Be A Title.2001.prop.ext1
% command_to_list_filenames | sed 's/.*\.\([0-9][0-9][0-9][0-9]\)\..*/\1.&/' | sort |
                                                            sed 's/^[0-9][0-9][0-9][0-9].//'
Another Potential Title.1989.prop.ext2
Could Be A Title.2001.prop.ext1
%

The sed command can be trivially modified to handle the other pattern.  And sed can take input from a file instead of a pipe:

% sed 's/.*(\([0-9][0-9][0-9][0-9]\)).*/\1.&/' list_of_directory_names | sort |
                                                            sed 's/^[0-9][0-9][0-9][0-9].//'
Charlie Fisher's (1989)
Badger Bodger (2001)
%

Note that this works fine with text that contains periods or parentheses, e.g.,

Dr. Strangelove.1964.foo
Mrs. Doubtfire.1993.bar

so long as they are not followed by four-digit numbers.

You can leave off the second .* if you want:

% command_to_list_filenames | sed 's/.*\.\([0-9][0-9][0-9][0-9]\)\./\1.&/'
2001.Could Be A Title.2001.prop.ext1
1989.Another Potential Title.1989.prop.ext2
%
  • Wow, a powerful approach. Had a quick try and did deliver. – fswings Apr 30 '15 at 11:31

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.