1
f1="filename1";
i=1;
c=f$i
echo $c

What shell command should I use so that echo $c returns "filename1" as the output?

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441

4 Answers4

2

Use variable indirection.

#!/bin/bash
f1="filename1";
i=1;
c=f$i
echo ${!c}  

It works in bash ( GNU bash, version 4.1.2(1)-release ). I have not tried in other shells.

Ashwani
  • 1,938
  • 11
  • 15
  • Works in bash 4.3.30 and sh 4.3 too, although not in zsh 5.0.7. – jdehesa Oct 13 '14 at 10:47
  • 2
    @jdehesa Note that it only works in `sh` because `/bin/sh` is a link to `bash` on your machine. `${!var}` is a `bash` extension and not available in POSIX shell. Incidentally, the `zsh` equivalent is `${(P)c}`. – chepner Oct 14 '14 at 12:39
1

You can use eval to "nest" variable substitutions.

f1="filename1";
i=1;
eval c=\${f$i}
echo $c
jdehesa
  • 58,456
  • 7
  • 77
  • 121
  • 1
    http://google-styleguide.googlecode.com/svn/trunk/shell.xml states use should avoid eval. This example is safe, but do not use eval with user-input. – Walter A Oct 13 '14 at 11:28
  • Unless you need to support this in POSIX `sh`, use your shell's syntax for indirect parameter expansion where possible. – chepner Oct 14 '14 at 12:40
  • @WalterA It's fine to use `eval` with user input, assuming it's properly quoted. [Here's a function to do that](https://stackoverflow.com/a/52538533/5353461) – Tom Hale Sep 29 '18 at 05:20
  • @TomHale Nice function in the link. However, that function won't work in POSIX /bin/sh (OP asks for `#!/bin/sh (POSIX sh)`). – Walter A Sep 29 '18 at 09:09
  • Granted, that function is `{ba,z}sh` code. You could [POSIXify the `printf %q`](https://stackoverflow.com/a/29824428/5353461) with `printf '%s\n' "$1" | sed "s/'/'\\\\''/g; 1 s/^/'/; $ s/$/'/"`. – Tom Hale Sep 29 '18 at 11:37
0

Reusable function

# Expand the variable named by $1 into its value. Works in both {ba,z}sh
# eg: a=HOME $(var_expand $a) == /home/me
var_expand() {
  if [ -z "${1-}" ] || [ $# -ne 1 ]; then
    printf 'var_expand: expected one argument\n' >&2;
    return 1;
  fi
  eval printf '%s' "\"\${$1?}\""
}

It warns when given:

  • Null input
  • More than one argument
  • A variable name which isn't set
Tom Hale
  • 40,825
  • 36
  • 187
  • 242
-1

Avoid intensive operations like eval. Use an associative array.

#!/bin/bash
typeset -A c
c[f1]=filename1
i=1
echo ${c[f$i]}
Josiah DeWitt
  • 1,594
  • 13
  • 15
  • `eval` is dangerous when using unsanitised input, but in what way is it intensive? – Tom Hale Sep 28 '18 at 12:38
  • 1
    Eval will add iterative complexity to your code. I'm referring to the processing speed of eval relative to using array assignment. I can't speak to the actual internals of eval, but there must be an overhead due to preprocessing of strings assumingly due to string manipulation, concatenation, character escapes, quoting and parsing, etc. It's also well know that string manipulation costs grow with the length of the string. In my tests eval is over twice the cost of doing associative array element assignment. Specifically 7.693:20.664 in a comparison test iterating both methods 1M times. – Josiah DeWitt Sep 29 '18 at 00:01
  • 1
    This will not work with `sh`, which was the subject of this question. – mattalxndr Jul 17 '21 at 22:04