1

I have to grab the first two lines, the lines 43 and 44, and the last 2 lines from a file in one conduct of commands. Is there away to print those while only using head, tail and pipe commands AND without special operators like && or ;? All I could think of is this

(cat cool | head -n 2) | (tail -n +43 | head -n 2) | (tail -n  2)

but it has cat... AND another option is

(head -n 2 < cool) | (tail -n +43 < cool | head -n 2) | (tail -n 2 < cool)

but for some reason it only shows the last line

3
  • 2
    "Is there away to print those while only using head, tail and pipe commands" - yes there is. Given the artificial constraints this sounds like homework, so please edit your question to show your own attempts Commented Oct 17, 2023 at 15:46
  • I believe you misunderstood the (homework?) question. In my opinion this is less complicated than that, you are just supposed to provide THREE separate commands: one for the first two lines, one for the lines 43 and 44 and finally the last 2 lines. There are solutions, though, to do the three operations in one single command.
    – xhienne
    Commented Oct 17, 2023 at 16:01
  • 2
    I forgot to add, but our teacher said to try and only use one command that's why I did it in one haha. The thing is I can only find a way to combine three commands using && or ; which are prohibited for the question... That's why I'm really confused. I'll edit my post to make it more precise!
    – cow
    Commented Oct 17, 2023 at 16:07

2 Answers 2

1

cat cool | head is an example of UUOC (Useless use of cat). Let's examine your second bit of code:

(head -n 2 < cool) | (tail -n +43 < cool | head -n 2) | (tail -n 2 < cool)

What should strike you is that | command < cool redirects stdin twice. You have to choose either the result of the previous piped commands, or the file cool. Both is not an option.

Now back to your problem. The solution with one single file read is to:

  • Read the first 42 lines, display only the first two and discard the 40 lines that follow. This translates to head -n 42 | head -n 2.
  • Display the next two lines (lines 43 and 44). This translates to head -n 2.
  • Display the last two lines. This translates to tail -n 2.

Now put all these together:

( head -n 42 | head -n 2; head -n 2; tail -n 2 ) < cool

Alternative, similar reasoning:

( head -n 2; head -n 42 | tail -n 2; tail -n 2 ) < cool
2
  • @Chris Hmmm, head and tail should operate on lines, not blocks. What system are you using? Does adding stdbuf -iL before each head and tail change anything?
    – xhienne
    Commented Oct 17, 2023 at 16:31
  • 2
    it depends on the implementation of head if it takes care to read only the lines it needs to read (i.e. seek back when reading from a file, or abysmally slowly reading a byte at a time when reading from a pipe). GNU and Busybox seemed to seek the file, but I wouldn't be surprised if some implementation didn't.
    – ilkkachu
    Commented Oct 17, 2023 at 16:39
0

Since the constraint is to use only head and tail, I don't believe it's possible to answer the requirement with a single command:

# First two lines of the file "cool"
head -n2 cool

# Lines 43 and 44
head -n44 cool | tail -n2

# Last 2 lines
tail -n2 cool

You can crash these together as a single line of three commands, but it's not "one command" as stated in your question:

head -n2 cool; head -n44 cool | tail -n2; tail -n2 cool

You may want to refresh your understanding of the head and tail commands by using the installed documentation: see man head and man tail.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .