EDIT: Incorporate a better solution

I broke my own bash limits this week.

We were given the following challenge in a forum:

Given a file (ASCII text) called input.txt, which contains 
the following string IN A SINGLE LINE:


 - Extract the first 4 characters of the string,
 - followed by the 2 characters at positions 56 and 57 
   (counting from 1),
 - and then the last 2 characters of the string. 
For a total of 8 characters to be represented in the 
standard output: DirtyBit
Do this in one line strictly, using only the 'head' and 
'tail' commands. Pipes are allowed. The following are not:
'if', 'while', 'for', ';', '&', '&&', '${}', '||', 

Can you solve it? I think I did!, but not with the best solution.

I came up with a solution that was sending only one sentence to bash, but it was using multi-line editing mode and thus was rejected.

My second solution, I am pretty sure 100% valid, uses temporary files to be able to construct the output.

head -c 4 input.txt > /tmp/dirt.txt | tail -c +56 input.txt | head -c 2 > /tmp/yB.txt | tail -c 3 input.txt > /tmp/it.txt | tail -q /tmp/dir.txt /tmp/yB.txt /tmp/it.txt

You can see it working:

[gvisoc@vao Downloads]$ head -c 4 input.txt > /tmp/dirt.txt | tail -c +56 input.txt | head -c 2 > /tmp/yB.txt | tail -c 3 input.txt > /tmp/it.txt | tail -q /tmp/dir.txt /tmp/yB.txt /tmp/it.txt
[gvisoc@vao Downloads]$

As the explanation can be spoiling the fun, it’s also hidden.

So the main issue is that the solution to the problem needs to do parallel processing of the same starting string, hence you need to fork your process into two or three streams, and then put together everything: I did that with temporary files.

Aside from that, a bit of checking the manual pages and an understanding on how pipes work for those commands when there are parameters and arguments in the invocations did the trick.

You can check out my answer to the challenge in the forum here, and also you can consider signing up for that forum at https://www.linux.org –it’s a great site.

EDIT: A day after posting my solution and the post you’re reading, there were better answers posted in the forum thread.

It’s based on the use of a trailing dash to tell a program to get the rest of the command line arguments from the standard input, that is, the output of the command that was piping onto it. While it’s not a shell construct and it depends on the program itself, it appears to be quite common.

I didn’t knew about these at all. I will surely look for them in the man pages and use them where available!

tail -c 3 input.txt | head -qc 57 input.txt - | tail -c 5 | head -qc 4 input.txt -

You can see it working:

[gvisoc@vao Downloads]$ tail -c 3 input.txt | head -qc 57 input.txt - | tail -c 5 | head -qc 4 input.txt -
DirtyBit[gvisoc@vao Downloads]$

There’s always something new to learn from someone with more experience.


Comment on this post on Mastodon, or write to the email at the bottom of the page