7

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

L=`ls`

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?

Alex B
  • 4,478
  • 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 07 '10 at 02:23
  • 1
    @phunehehe, sorry, you have to push (pushd <dir>) some directories on the stack to have more than one line. – Alex B Sep 07 '10 at 03: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 07 '10 at 03:43
  • Autojump may do what you need or at least have ideas worth stealing: http://github.com/joelthelion/autojump/wiki – msw Sep 07 '10 at 03: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 07 '10 at 03:52
  • Your example works fine for me. After using pushd, $L contains multiple lines. – Dennis Williamson Sep 07 '10 at 17:46
  • @Dennis, yes, I've figured out what my problem was, see comment to accepted answer. – Alex B Sep 08 '10 at 11:04

2 Answers2

7

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
/home/msw

You could get a function to help out:

$ function passdirs() { bash ndirs.sh `dirs -l -p`; }
$ cat > ndirs.sh
echo $#
echo "$@"
$ passdirs
8
/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.

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

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
L=`export`
echo "$L"

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

But not alias, which gives you nothing.

phunehehe
  • 20,240
  • 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 07 '10 at 03:31