3

So, I wrote a simple script to convert sh-style export key=value statements to csh-style setenv key value for docker-machine env.

#!/bin/sh
docker-machine env | sed -e 's/export/setenv/' -e 's/=/ /' -e '$d'
echo '# eval `docker-machine env`'

and it produces the following output

setenv DOCKER_TLS_VERIFY "1"
setenv DOCKER_HOST "tcp://<ipv4 address>:<port>"
setenv DOCKER_CERT_PATH "<HOME>/.docker/machine/machines/default"
setenv DOCKER_MACHINE_NAME "default"
# Run this command to configure your shell:
# eval `docker-machine env`

In my .tcshrc I've bound this script to the alias docker-machine-env-csh.

I can source the output of this script using a temporary variable just fine

% docker-machine-env-csh >! /tmp/csh && source /tmp/csh

However, I don't seem to be able to directly eval the result of this alias

% eval `docker-machine-env-csh`
setenv: Too many arguments.

Or assign it to a variable in a way that preserves newlines.

% set a = `docker-machine-env-csh`
% printf "%s\n" "$a"
setenv ... setenv ... setenv ...

Although, weirdly printf "%s\n" `docker-machine-env-csh` seems to insert a newline between every token.

% printf "%s\n" `docker-machine-env-csh`
setenv
...
...
setenv
...
...

How do I preserve newlines in tcsh command substitution?

Greg Nisbet
  • 3,076

1 Answers1

9

There are plenty of good reasons why it's recommended not to use csh or tcsh for scripting. That's one of those.

To get the output of some command verbatim into a shell variable in tcsh, you need something like:

set temp = "`(some command; echo .) | paste -d . - /dev/null`"
set var = ""
set nl = '\
'
foreach i ($temp:q)
  set var = $var:q$i:r:q$nl:q
end
set var = $var:r:q

Don't forget to use $var:q syntax to expand the variable as in:

printf '<%s>\n' $var:q

The POSIX sh equivalent would be:

var=$(some command; echo .)
var=${var%.}

Now, if your docker-machine-env-csh command did output the information on one line only, or at least with each line terminated with a ; and without comments so that when joined with spaces they still are valid csh code (like applications such as dircolors or ssh-agent do when they want their output to be passed to csh's eval), then you could do:

set var = "`docker-machine-env-csh`"
printf '%s\n' "$var"
# or printf '%s\n' $var:q
# or printf '%s\n' "$var:q"

With set var = `docker-machine-env-csh`, tcsh splits on blanks (and creates an array variable), while with set var = "`docker-machine-env-csh`", it splits on newline only (still an array variable, but if the output has only one line, that array has only one element).

Here you can use "$var" in place of $var:q because $var doesn't contain newline characters. "$var" would join the array elements with space while $var:q would preserve them (here there's only one element, so it makes no difference). If those array elements may contain newlines and you wanted to join them with space, you'd use "$var:q".

So maybe you can modify your script like:

#! /bin/sh -
docker-machine env | sed '
  s/export/setenv/
  s/=/ /
  /^#/!s/$/;/
  $d'
echo '# eval "`docker-machine env`"'