Normally, bash is able to assign multi-line output of a command:


This works from both interactive shell and scripts. But it appears the output of a builtin can't be captured in a variable:

L=`dirs -l -p`

This works in the interactive shell, but in the script only first line end up in the variable, probably due to different treatment of builtins by bash.

Why does it happen, and how can I do what I need?

  • dirs -l -p gives me /home/phunehehe, and running L=`dirs -l -p`; echo $L always gives me /home/phunehehe. Why are you using it to test "multiline output"? – phunehehe Sep 7 '10 at 2:23
  • 1
    @phunehehe, sorry, you have to push (pushd <dir>) some directories on the stack to have more than one line. – Alex B Sep 7 '10 at 3:38
  • Is it the dirs command that you specifically need in your script? If so, you'll need to better explain what you need as dirs (and $DIRSTACK) are treated specially in non-interactive shells. Bash in general tries to make built-ins behave like external commands, except in cases where there is affirmative reason not to. – msw Sep 7 '10 at 3:43
  • Autojump may do what you need or at least have ideas worth stealing: github.com/joelthelion/autojump/wiki – msw Sep 7 '10 at 3:48
  • @msw I specifically need dirs output, since I am writing a bash function to cd based on regex (since I am already using pushd/popd, I'd like to keep it integrated with directory stack). But I think your remark warrants a separate answer if you can explain why bash treats dirs differently. – Alex B Sep 7 '10 at 3:52

There seems to be some ambiguity in the way that you worded your question. You said:

but in the script only first line end up in the variable

and then your comments suggest that you are writing a shell function - not a script.

I suspect you know that scripts are useless for changing directories, since any cd within a script does not propagate out to the superior shell. If indeed you are trying to develop your directory changer in a script, you'll have a tough time as the directory stack is not inherited by subordinate shells:

$ dirs -l
/home/msw /home/msw/Ubuntu One /home/msw /usr/bin /usr/local /bin /usr/local
$ cat > dirs.sh
dirs -l 
$ bash dirs.sh

You could get a function to help out:

$ function passdirs() { bash ndirs.sh `dirs -l -p`; }
$ cat > ndirs.sh
echo $#
echo "$@"
$ passdirs
/home/msw /usr/local /usr/bin /bin /usr/local /usr/bin /bin /home/msw

but, of course you'd need the function to also effect the cd in the current shell in which case wrapping and unwrapping arguments through the command line is a quoting chore so you are best just doing it all in the function

$ function cd_pattern() {
   cd $(dirs -l -p | grep --max-count=1 -e "$1") ; dirs
$ dirs
~ /usr/local /bin /usr/share/doc/evince ~/Ubuntu One
$ cd_pattern v..c
/usr/share/doc/evince /usr/local /bin /usr/share/doc/evince ~/Ubuntu One

which also switched to using $(...) as a synonym for backticks to reduce quoting confusion.

  • Bah, of course! I was writing a function for ~/.bashrc, but was testing it... drum roll... in a script. – Alex B Sep 7 '10 at 5:46

The output of commands can vary depending on whether it is run interactively or within a script. For example, "multiline output" goes fine with export:

#! /bin/bash
echo "$L"

, which gives you all the output just the same as when you run it interactively.

But not alias, which gives you nothing.

  • 1
    Aliases are intentionally disabled in non-interactive scripts: "aliases are not expanded when the shell is not interactive, unless the expand_aliases shell option is set". Which you should probably never do as it would be a great vector for exploits. – msw Sep 7 '10 at 3:31

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.