6

I have rbenv (ruby version manager) installed on machine and it works like that:

$ rbenv local
2.3.1

Writing to stdout the local version of my ruby. I want to rescue this version and declare it in a variable to reuse in another occasion.

$ declare -r RUBY_DEFINED_VERSION=$(rbenv local)
$ echo Using ruby version $RUBY_DEFINED_VERSION
Using ruby version 2.3.1

It works!

But I don't want to use a subshell to do the work (using $() or ``). I want to use the same shell and I don't want to create a tmp file to do the work.

Is there a way to do this?

Note: declare -r is not mandatory, it can be a simple var=FOOBAR.

  • if you wanna call rbenv in current shell ; then you need at laest a named pip ; cmd > fifo; var="<fifo" ! – Jonah Jan 3 '17 at 14:17
  • Any reason for not wanting to use $(...) or temporary file? – Kusalananda Jan 3 '17 at 14:24
  • 1
    @Kusalananda The rbenv local changes some variables and I want to use these variables. The shell script will run on various projects and I can't trust in /tmp or permissions. Some machines I just can write on /var/tmp. – dgmike Jan 3 '17 at 14:28
  • Can't you just parse the .ruby-version file in the current directory? BTW, I can not find anything that says rbenv local changes anything. It's supposed to only report the local version according to github.com/rbenv/rbenv#rbenv-local – Kusalananda Jan 3 '17 at 14:36
  • @Kusalananda in case of rvm (the shell script will prevent the rbenv and rvm) some variables are set, like RUBY_VERSION. But I think I can use cat .ruby-version. :-) – dgmike Jan 3 '17 at 14:57
6

There is a hack, but I think it just make sense if you need it in a loop.

you can open a cat coproc like this: coproc CAT { cat; }

This will start a cat command in background, and set two environment variables: CAT_PID and CAT. The CAT variable is an array with STDOUT and STDIN (in this order) file descriptor (pipes) used by cat.

So, you can execute anything writing the output to &${CAT[1]} that represents the STDIN, and use the builtin command read to set your variable reading from ${CAT[0]} that is the STDOUT of cat.

Here a sample:

coproc CAT { cat; }
echo 123 >&${CAT[1]}
read myvar <&${CAT[0]}

To test:

echo $myvar
123

Don't forget to stop the cat after use it. You can do it by by killing the process.

kill $CAT_PID

This makes a great difference in performance tuning.

  • 1
    IFS= read -r -d $'\0' myvar to read null-delimited data – Niklas Holm Aug 29 '18 at 15:47
1

With bash you can also do it like this :

read a < <(echo hello)
echo "$a"

Or like this :

shopt -s lastpipe
echo hello | read a
shopt -u lastpipe
echo "$a"

But you still have to launch a sub-process which will run ruby, so I don't really understand what you are trying to avoid...

  • I really don't understand why this answer was downvoted. – ton Mar 15 '18 at 14:44
  • what is a? is that a variable? – Alexander Mills Dec 22 '18 at 4:55
  • read a, where a in this case is a variable where the read command you put the content readed. The command read will create or replace this variable and you can put any non-reserved word here, If you do not want to choose a name, you can omit it and the default variable name is REPLY, so you can read the content from $REPLY, use help read for more details. – ton Jan 18 at 16:45
  • Your first method has a subshell, breaking variable assignment. So read a < <(foo=bar; echo baz); echo "$a:$foo" outputs baz: instead of baz:bar. Your second method doesn't change $a's value. Maybe shopt -u lastpipe is broken for me? – Mark Haferkamp Aug 7 at 23:57
1

If on Linux, with bash you could do:

{
  rbenv local > /dev/fd/3
  IFS= read -rd '' -u 3 variable
} 3<<< ''

That does use a temp file like every here-document or here-string, though that's hidden to you.

If rbenv outputs less data than can fit in a pipe without blocking (typically 64KiB), still on Linux and Linux only, you can use a pipe instead of the temp file with:

{
  rbenv local > /dev/fd/3
  IFS= read -rd '' -u 3 variable
} 3< <(:)

With ksh93, use the form of command substitution that doesn't start a subshell:

variable=${
  rbenv local
}

Beware that contrary to the IFS= read -rd '', that removes the trailing newline characters in the output (like all command substitutions).

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.