4

I have been reading through the study guides for the LPIC-1.

echo "This is a sentence. " !#:* !#:1->text3

I'm having trouble understanding how the above line of code repeats the echo command multiple times. I know that it is using a feature of bash's history but I can't find any documentation on !#:* or !#:1. Could someone explain this for me?

  • If i was to guess the first command, echo "This is a statement. " is an echo statement, and the !#:* and !#:1 repeats it. The >text3 directs them to a text file created – ryekayo Aug 11 '14 at 15:58
  • refer to the bash manual: gnu.org/software/bash/manual/bashref.html#History-Interaction – glenn jackman Aug 11 '14 at 16:06
  • @Braiam: Because the question isn't about a bash script -- history doesn't work in scripts (unless you jump through hoops to enable it) – G-Man Aug 22 '14 at 15:43
11

Yes, this is using history.  !# is a history event designator that refers to the entire command line typed so far.  :* is a word (range) designator that refers to all of the words, except the 0th.  So, after you have typed echo "This is a sentence. ", then !#:* expands to "This is a sentence.  ".  And x-y (where x and y are integers) is a word (range) designator that refers to word number x through word number y.  If y is omitted (x-), this is interpreted to mean word number x through the second to last word.  So, after your “entire command line typed so far” stands as

echo "This is a sentence. " "This is a sentence. "

then !#:1- expands to "This is a sentence. ", because each of the quoted "This is a sentence. " strings counts as one word, and so !#:1- is equivalent to !#:1 (just word number 1).  So you end up with

echo "This is a sentence. " "This is a sentence. " "This is a sentence. " >text3

The fact that the - and the > appear together in the command is just a confusion; they don’t interact.  And the fact that “This is a sentence.” is quoted obscures what is going on; if you said

echo This is a sentence. !#:* !#:1-

it would expand to

echo This is a sentence. This is a sentence. !#:1-

and thence to

echo This is a sentence. This is a sentence. This is a sentence. This is a

(because !#:1- expands to word number 1 through the second to last word.)

  • 2
    @Gnouc: As I said halfway through my answer, the command from the question (echo "This is a sentence. " !#:* !#:1->text3) expands as echo "This is a sentence. " "This is a sentence. " "This is a sentence. " >text3. I then gave another example: echo This is a sentence. !#:* !#:1- (without quotes) expands as echo This is a sentence. This is a sentence. This is a sentence. This is a. – G-Man Aug 11 '14 at 16:37
  • Oh, sorry, mis-reading. – cuonglm Aug 11 '14 at 17:02
  • Thank you for your answer. This clears things up quite a bit. Quoting the sentence did add to the confusion. Thank you. – chthonous Aug 11 '14 at 17:36
4

A good documentation about bash history cheat sheet by Peter Krumins you can found here.

Since you understood !#, so we just explain :* and :1.

  • *: refer to all word but the zeroth.
  • x-: refer to the words from x to the second to last word.

So your command:

echo "This is a sentence. " !#:* !#:1-
  • !#:* get all but the zeroth, so it get "This is a sentence. ", your command becomes echo "This is a sentence. " This is a sentence. ".

  • !#:1- get word from 1 to second to last word, meaning "This is a sentence. " again, your command becomes echo "This is a sentence. " "This is a sentence. " "This is a sentence. "

  • Thanks for the link to the cheat sheet. It looks very helpful. – chthonous Aug 11 '14 at 17:35
3

As @glennjackman points out, this is all covered in the bash manual. Search the man page for HISTORY EXPANSION.

  • !# is an event designator meaning "the entire command line typed so far". In your example that's echo "This is a sentence. ", including a trailing space after the final double quotation mark.

  • : separates the event designator (!#) from the word designator (*).

  • * is a word designator meaning "all of the words in the event except the zeroth"

So echo "This is a sentence. " !#:* expands to

echo "This is a sentence. " "This is a sentence. " $

(the dollar sign is mine, indicating end of white space) which echoes

This is a sentence.  This is a sentence. $

to the terminal (again, the dollar sign is mine, indicating end of white space).

The second event designator:word designator pair (!#:1-) functions in a similar manner (but bear in mind it has to deal with the fact that the command line has been expanded considerably now that the first designator pair has been evaluated).

  • Thanks. The search terms a quite helpful. It's hard to know what things are called when just starting out. – chthonous Aug 11 '14 at 17:37
  • 2
    @chthonous, if you know it's about history, in info bash, type i (to bring the index), enter hist and press Tab twice to bring up the list of related topics. – Stéphane Chazelas Aug 12 '14 at 9:42
  • @StéphaneChazelas great tip! – chthonous Aug 12 '14 at 12:44
1

I have experimented with !#:* and !#:1. I could not find many sources online for this but I am sure that you introduce an echo "Sample text" and then you can use !#:* !#:1 !#:2->text and the !#:* will store the string contents in there temporarily. I am not sure if it is a kind of conditional statement as I was unable to find much resources on it, but it seems this is useful when you are attempting to code the same thing and want to save keystrokes. I have tried this in bash:

echo "test" !#:* !#:1 !#:2 !#:3 !#:4->test1

This is what I get in test1:

test test test test test test

Pretty nifty tool.

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.