Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Just wondering why this is not working

#!/bin/bash 

ls /bin
ls !$

I expect to run ls /bin twice, but the second one raises errors as !$ was not interpreted

Did I miss something, or !$ only work in command line?

I couldn't find relevant part in man bash (on mac)

share|improve this question
8  
Although there is a solution is this really the best way to achieve this in a script? history is disabled by default for a reason when you run non interactively - a long script is going to spam the .bash_history file. Not saying it wasn't worth asking this, but just that if you're thinking of using this in a script, is this really the best way? – flungo Feb 5 at 22:54
up vote 25 down vote accepted

History and history expansion are disabled by default when the shell run non-interactively.

You need:

#!/bin/bash 

set -o history
set -o histexpand

ls /bin
ls !$

or:

SHELLOPTS=history:histexpand bash script.sh

it will affect all bash instances that script.sh may run.

share|improve this answer
1  
+1 for SHELLOPTS – warl0ck Feb 5 at 16:41
8  
careful with SHELLOPTS, it will affect that bash that runs script.sh, but also all the other bash instances that script.sh may eventually run (like other bash scripts...). – Stéphane Chazelas Feb 5 at 22:55
    
And it won't affect any other bash instance that runs your script. – Blacklight Shining Feb 6 at 0:31
    
I think this answer would improve if @StéphaneChazelas's comment is edited into it. – Oliphaunt Feb 6 at 16:37

The sane thing to do would be

ls /bin
ls $_

or

set ls /bin
$*
$*

or

c='ls /bin'
$c
$c

Caveats: it's worth remembering that each of these comes with some pitfalls. The $_ solution only captures the last single argument: so ls foo bar will leave $_ containing just bar. The one using set will override the arguments ($1, $2, etc). And all of these as written will work, but when generalized to more complex commands (where escaping and whitespace matter), you could run into some difficulties. For example: ls 'foo bar' (where the single pathname argument foo bar contains two or more spaces, or any other whitespace characters) is not going to behave correctly in either of these examples. Proper escaping (possibly combined with an eval command), or using "$@" instead of $*, might be needed to get around those cases.

share|improve this answer
1  
+1 for an answer that is portable and doesn't abuse a bashism intended to make interactive use easier. (As an aside, bash's desire to interactively expand exclamation marks in the default setup is something I find counterintuitive as a user of other shells, and counterproductive because it always screws me when I'm trying to do some complicated shell command). – mtraceur Feb 6 at 5:49
    
Although as written at this moment, I was hesitant to give it the +1 due to the issues beginner shell scripters will likely have when trying to generalize it to more complex commands. I suggested an edit to at least add a caveat paragraph explaining the possible pitfalls thereof. – mtraceur Feb 6 at 6:03
    
@mtraceur: it's not portable, only work in bash, zsh, ksh (if two command are not in the same line). Work in dash, mksh only when interactive – cuonglm Feb 6 at 6:43
    
@cuongim: Sorry, I was perhaps too carelessly general. The $_ way is not portable, you are right. The set approach works in non-interactive dash, and with the ${1+"$@"} (plus zsh global alias) trick should be general, although I vaguely remember that set has a history of not being perfectly portable with some (old?) shells. The define-a-variable-holding-the-command-and-then-eval-it approach, especially using proper escaping and an actual eval command, is completely generally portable as far as I know. – mtraceur Feb 6 at 6:51

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.