Building a pattern out of position constraints
◈ 9 cardsTurning a plain-English selection rule into a working command line, one character position at a time.
Patterns are built, not guessed
The previous lessons were about reading a pattern. Writing one is the same skill run backwards, and there is a method: translate the English into a list of position constraints, write one pattern element per constraint in order, and test after each element. The last step is the one people skip, and it is the one that catches the mistakes.
Worked example — the assignment question, built in five steps
The requirement, in the register the paper uses: display the type of every file in ~/personal whose name starts with b, c, A or T and whose fourth character is neither a digit nor a letter.
First decompose it. There are four constraints and one non-constraint:
- position 1 is one of
b,c,A,T; - positions 2 and 3 are unconstrained but must exist;
- position 4 is not a digit and not a letter (either case);
- positions 5 onward are unconstrained and may be absent;
- and the command must report file types, which is
file, notls.
Now build it, testing against the same directory after each step. Use echo while you are building — it shows you the selection with nothing at risk.
Step 1 — the first position. [bcAT] is one bracket for one position. It is not four separate patterns, and it is not [b,c,A,T]; a comma there would add a fifth member to the set.
echo ~/personal/[bcAT]*
Step 2 — positions 2 and 3. Two question marks, because each one demands exactly one character. Writing * here would be wrong twice over: it would allow zero characters, and it would let the fourth-position constraint slide anywhere in the name.
echo ~/personal/[bcAT]??*
Step 3 — the negated fourth position. "Neither a digit nor a letter" is one bracket containing the union of everything you are excluding: [^0-9a-zA-Z]. Both letter cases must appear — leaving out A-Z would be a real bug, because [^0-9a-z] still admits a name whose fourth character is an uppercase letter — barTop would slip through, and its fourth character is very much a letter.
echo ~/personal/[bcAT]??[^0-9a-zA-Z]*
Step 4 — the tail. The trailing * allows any remainder including none, so a four-character name like Ax9_ still qualifies. Leave it off and you require the name to be exactly four characters long, which the question never asked for.
Step 5 — the command. File types come from file, which reads each file's contents and classifies it. ls -l reports the kind of entry (regular, directory, symbolic link) in its first column, which is a different question. The finished line:
file ~/personal/[bcAT]??[^0-9a-zA-Z]*
Write [!0-9a-zA-Z] instead if you want the POSIX-portable spelling; both are accepted, and naming the alternative in an exam answer is free marks.
A second one, on your own terms
Select chapter followed by one of the digits 1, 2, 6, 8 or 9, with either a .eps or a .prn extension. The digit set is [12689] — five members, no commas, no ranges, because 1, 2, 6, 8, 9 is not a contiguous run. The extension is the awkward part: a bracket cannot hold a three-character alternative, so either accept two patterns on one command line —
lpr chapter[12689].eps chapter[12689].prn
— or, in bash, use a brace expansion, which is a different mechanism entirely and expands even when nothing matches: chapter[12689].{eps,prn}. On a plain POSIX shell, the two-pattern form is the portable answer, and it is the one to write on paper.
Test the pattern, not your reading of it
The habit that separates a correct answer from a plausible one: build with echo, check the survivors against the English, and only then substitute the real command. A glob you have not run is a guess.