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 Answers4

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.

ilkkachu
  • 138,973
  • 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 –  Jan 20 '18 at 06: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? –  Jan 20 '18 at 06:42
  • It seems more logical as far as syntax. I'll add more to the question to see if anyone knows more about it. –  Jan 20 '18 at 06: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". –  Jan 20 '18 at 07: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. – αғsнιη Jan 20 '18 at 07:05
  • @KyleCurtis Notice the double quotes in ikkachu's update to my answer. That's the way to go... :-) –  Jan 20 '18 at 07:23
  • @αғsнιη is right, this is no big mystery. Just a variable concatenated with a string. Bash is quite patient in that regard, you could even mix and match, i.e. if you add variable="special" and change devTarget=$devName/my/$variable/partition you'll get /dev/sda/my/special/partition. You just need the / to distinguish the end of the variable name and the beginning of the string. You don't even need to double-quote the end-result. If you don't want the /just use \\. – Robert Riedl Jan 20 '18 at 18:04
  • @tomasz As far as explanation, Bash interprets parameter names as ending with the first character that isn't legal in a name. That's why the \3 isn't considered part of the name. Then the backslash causes the next character to be interpreted literally, without any special meaning, but 3 doesn't have a special meaning anyway so \3 is just equivalent to 3. You could edit the answer to mention this if you like. – David Z Jan 20 '18 at 22:44
  • @DavidZ Post your own answer. I think it's worth it. You would be able to answer any question should they happen to appear. –  Jan 21 '18 at 04:02
  • @tomasz OK, I didn't think that was worth an independent answer but I'll put one up if you like. – David Z Jan 22 '18 at 06:27
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.)

ilkkachu
  • 138,973
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.

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.

David Z
  • 912