6

I was working on a Bash script to help partition a hard drive correctly, and I came across a strange problem where I had to append a number to a variable. It took me a while to get the outcome right since I'm not very experienced with Bash but I managed to make something temporary.

Here is an example of what I was building:

#!/bin/bash

devName="/dev/sda"
devTarget=$devName\3

echo "$devTarget"

The variable devName was /dev/sda, but I also needed a variable set for the 3rd partition later on in the script so I used the \ symbol, which let me add the number 3 to the /dev/sda to make the output /dev/sda3

Even though the \ symbol worked, I was just wondering if this is the right way to do something like this. I used this because in Python it's used to ignore the following character for quotations and such, but I wanted to do the opposite in this situation, so surprisingly it worked. If this isn't the best way to go about adding to variables, could someone please show an example of the best way to do this in Bash.

4 Answers 4

9

The safe way to interpolate a variable into a string is to use ${var_name}. For example:

#!/bin/bash

devName="/dev/sda"
devTarget="${devName}3"

echo "$devTarget"

This way Bash has no doubts of what the variable to interpolate is.

Btw, interesting how devTarget=$devName\3 works. I'm not sure why.

10
  • I knew there was a better way lol. I have no idea how it works either, one of the strange mysteries of programming haha. Thanks for the clarification @tomasz
    – user270900
    Commented Jan 20, 2018 at 6:38
  • 1
    @KyleCurtis I'm not sure if it's better. Definitely it's more popular. Maybe someone knowledgable explains the mystery of \3. It must be something with the order of expansions and escape removing, I guess?
    – user147505
    Commented Jan 20, 2018 at 6:42
  • It seems more logical as far as syntax. I'll add more to the question to see if anyone knows more about it.
    – user270900
    Commented Jan 20, 2018 at 6:49
  • 1
    @KyleCurtis The main use of \ is escaping (whitespace, $, whatever else should be escaped). I'd change the title of this question to something more about \. Possibly "Backslash in variable substitution".
    – user147505
    Commented Jan 20, 2018 at 7:00
  • 3
    @KyleCurtis \3, it's not a surprised thing, if you don't want 3 be as part of your variableName devName3 you should have escape it to print itself as alone like you do \\ to print \ and since \ is special character in shell so it interpret as its meaning apart of variable name. Commented Jan 20, 2018 at 7:05
5

$x\b works in the same way that $x"b" or "$x"b work, the quotes end the variable name. If for no other reason, I think this happens because the quotes are still there when the variable is expanded and none of \"' is valid in variable names.

The standard text says that:

The order of word expansion shall be as follows:

1. ..., parameter expansion (see Parameter Expansion), ...

4. Quote removal (see Quote Removal) shall always be performed last.

And "quote removal" is the step that actually removes the quote characters.

But, as @tomasz stated in their answer, putting braces around the variable name is the usual way to do it, so "${x}b", or "${devName}3". You almost always want to put the quotes around an expansion, see When is double-quoting necessary?

(Though, I've wondered if there might be other ways to mark a quoted string within a program, other than actually leaving the quote characters in place.)

0
3

What your script shows is that you're writing the variable plus number to output. Easy enough that can be achieved with printf and formatted string:

$ devName="/dev/sda"
$ printf "%s%d\n" "$devName" 3
/dev/sda3

But as you mentioned that you need to use a new variable later in the script, we can also make use of -v flag in bash's version of printf:

$ printf -v devTarget "%s%d" "$devName" 3
$ echo "$devTarget"
/dev/sda3

This approach won't work with POSIX printf, and doesn't work in other shells (at least ksh and mksh don't have -v flag for printf, not sure about zhsh). So we could work around this via command substitution:

$ devTarget=$(printf "%s3" "$devName")

However,the accepted answer would probably be simpler and more portable.

2
  • Thanks for the examples to help clarify the differences. I will keep that in mind the next time I come across a problem like this one.
    – user270900
    Commented Jan 20, 2018 at 16:33
  • @KyleCurtis Glad I could help :) Commented Jan 20, 2018 at 17:59
1

As tomasz pointed out, the proper way to specify exactly which characters constitute the variable name is to use braces, like ${devName}3.

Bash allows you to omit the braces as a convenience, but in doing so you lose the ability to be fully explicit about what variable you want to reference. The way bash resolves this is by taking the longest possible valid variable name that starts after the dollar sign. Variable names start with a letter or underscore and can contain any number of letters, numbers, or underscores, but no other characters. So in your example, $devName\3, bash starts with the d and looks for the longest run of letter/number/underscore characters it can find, which is devName. It takes that to be the name of the variable to use. Similarly, something like $here-is-a-long-string-of-words would use the variable named here; it'd be like ${here}-is-a-long-string-of-words.

After that bash sees \3. The backslash is a form of quoting, in the sense that it causes the following character to be interpreted normally, without any special meaning. But 3 doesn't have any special meaning anyway, so the backslash is redundant. This sequence winds up being equivalent to just 3 by itself.

You must log in to answer this question.