I need to concatenate two variables to create a filename that has an underscore. Lets call my variables $FILENAME and $EXTENSION where filename is read from a file.



I have tried the following without success:


I always get some kind of weird output. Usually the underscore first.

I need it to be

echo $NAME
  • 2
    Probably your script contains carriage return characters. – Stéphane Chazelas Aug 27 '13 at 20:17
  • Yep that was it. :D – Rhyuk Aug 27 '13 at 20:33

You can use something like this:


This works as well:

  • 1
    The problem was the file where I was reading NAMEs from. Windows formatting... Your solution worked great after that. Thanks – Rhyuk Aug 27 '13 at 20:29
  • 2
    The first command mangles the value of the variable and the use of a command substitution and echo cannot possibly help. The second command was already given in the question. – Gilles 'SO- stop being evil' Aug 27 '13 at 23:17
  • 1
    Yes! I love it. This was fücking annoying me for the longest while. Thanks for the solution! – racl101 Oct 10 '15 at 19:54
  • This works for me. When I use '$FILENAME_$EXTENSION', only values of '$EXTENSION' appears. Could you please help explain why this happens? Thanks – Lei Hao Aug 15 at 8:41



are all fine, so would be:


(but certainly not NAME=$(echo ${FILENAME}_${EXTENSION}) as it uses echo and the split+glob operator).


would have been the same as:


as _ (contrary to \ or ') is a valid character in a variable name.

Your problem is that you had lines delimited with CRLF instead of just LF, which meant that those variable content ended in CR character.

The CR character, when written to a terminal tells the terminal to move the cursor to the beginning of the line. So Hello<CR>_WORLD.TXT<CR> when sent to a terminal would show as _WORLD.TXT (overriding the Hello).


I've switched to using the ${FILENAME}_${EXTENSION} syntax mentioned above.

I used to use $() when I needed to concatenate two variables with an underscore. For example, $YYYYMMDD$()_$HHMMSS to generate a filename containing a timestamp in the format of YYYYMMDD_HHMMSS. The middle $() returns nothing and breaks the two variables apart.

I've used time to measure the assignments using the $YYYYMMDD$()_$HHMMSS method and some of those above, and they've all reported 0ms on the old server I'm using this on. Performance doesn't seem to be an issue.

  • That doesn't answer the question. You should raise a new question for that. $() does fork a subshell in a few shells (some optimise it away) . It's also hijacking an operator for something it's not been designed for. ${x}_y is what you want. – Stéphane Chazelas Oct 30 '17 at 16:49
  • What do you mean, “That doesn’t answer the question.”?   It’s not a great answer, but it is an answer. – Scott Oct 30 '17 at 19:06
  • Edited to remove ambiguity. – user208145 Nov 1 '17 at 0:09

You should use variables in lowercase (it is the best practice).
So, I'll use filename and extension instead of FILENAME and EXTENSION

As you say that “filename is read from a file”, I'll assume that the script is:

read -r filename  <file.txt

And that you want to concatenate both variables $filename and $extension with an underscore _.
The examples you provide (except: no double \) do work correctly here:


As some other:


So, your problem is not with how the variables are stick together, but with the contents of the vars. It seems reasonable to think that this:

read -r filename  <file.txt

will read a trailing carriage return \r if reading from a windows file.

One simple solution (for ksh,bash,zsh) is to remove all control characters from the read variable:


This could be simulated with a value that has a carriage return:

$ filename=$'Hello\r'
$ echo "starting<$filename>end"
>endting<Hello                       ### note the return to the start of line.
$ echo "starting<${filename//[[:cntrl:]]/}>end"

Or, replacing the value of filename:

$ filename="${filename//[[:cntrl:]]/}"
$ echo "start<$filename>end"


So this:


Will get the correct value for name even if the other vars contain control characters.


Stéphane Chazelas’s answer,


is, of course, the best answer, and should be the accepted answer.  But, in the spirit of user208145’s answer, here’s an alternative:


You can also use this: separate the separate vars with spaces and concatenate them using tr to remove the space.

# concatenation work is done here 
$(echo "$TableNameToCreate _my_other_Info " | tr -d " ")

echo "Var to create with var pasted as prefix to suffix
    $(echo "$TableNameToCreate _my_other_Info " | tr -d " ") 
. "

# create a new var
NewVar=$(echo "$TableNameToCreate _my_other_Info " | tr -d " ")

echo "$NewVar"
  • (1) This question has already been answered more than adequately.  (2) What does your answer do if the existing variable has special characters in it? – G-Man Says 'Reinstate Monica' Oct 30 '17 at 17:21
  • This does not address the issue with the variable containing a carriage return. It also removes spaces, something that may not be wanted. – Kusalananda Oct 30 '17 at 17:59
  • @Kusalananda: Actually, unless I’m overlooking something, this handles carriage return just fine; spaces are the only problem. – G-Man Says 'Reinstate Monica' Oct 30 '17 at 19:36

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.