Variable Expansion when using variable within filename [duplicate] - bash

This question already has answers here:
How to echo "$x_$y" in Bash script?
(4 answers)
When do we need curly braces around shell variables?
(7 answers)
Closed 3 years ago.
When using Bash I want to WGET multiple files from a server so I write a script with a For-loop that increments a counter to match the numbering of the files.
But I want to include the title of the file AND the number in which order the file appears (the "ID" of the file). So the the file has a URI of "example.com/files/hello_world.txt", with the ID of 42 and the title is "Hello World" when WGET it, the downloaded file should have the name "42_Hello_World.txt".
I tried the following code:
#! /bin/bash
# Init
index=42
title="Hello World"
# Replace blanks with underscore
title=${title/ /_}
# Concat fileName
fileName="$index_$title.txt"
echo $fileName
but the output is just "Hello_World.txt". When I change the order of $title and $index the output is "42.txt"
Can someone explain to me why this happens and how to solve it?
tl;dr
When using two or more variables in bash when evaluating a string only the last variable is "expanded". The first one is ignored. WHY???

_ is a valid character for an identifier, so $index_$title.txt is interpreted as the concatenation of two parameter expansions, $index_ and $title. To explicitly delimit the parameter name, use the full ${...} form:
fileName=${index}_$title.txt
The braces are not necessary for $title, because the following . cannot be interpreted as part of a parameter name (though the braces are certainly permitted: ${index}_${title}.txt).
Since index_ is not defined, $index_ expands to the empty string.

Yes. The explanation is the the _ character is a valid character in a variable name, so that your expressions are expanding the (undefined) variables $index_ and $title_ as empty strings. (The . is not a valid name character, so it terminates the 2nd name automatically.) Do this instead:
$ fileName="${index}_$title.txt"
$ echo $fileName
42_Hello_World.txt
$ echo "${title}_$index.txt"
Hello_World_42.txt

Could you please try following. This change should provide you your expected results. It is simple your variable "$index_$title.txt" is considered as you are concatenating 2 variables (index_ and title) so its better to quote _ like --> "_" and tell shell that it is a string.
index="42"
str="Hello World"
# Replace blanks with underscore
title=${str/ /_}
# Concat fileName
fileName=$index"_"$title".txt"
echo $fileName
In this nice url, you could see the last example of VALID variables(_ is there in the list):
https://bash.cyberciti.biz/guide/Rules_for_Naming_variable_name

The _ in the filename is not helping. _ is a valid variable character, and bash thinks that you want a variable called $index_ followed by $title, which isn't what you want. You can either:
Change the underscore character to an invalid variable name
Change to filename=$title"_"$index".txt" or
put brackets around $index
Hope this helps!
EDIT: You already have an answer here! How to echo "$x_$y" in Bash script?

Related

What does the #*$ in a shell script's string interpolation do? [duplicate]

This question already has answers here:
What is the meaning of the ${0##...} syntax with variable, braces and hash character in bash?
(4 answers)
Closed 11 months ago.
I found the following code in a shell script, but I am unsure of what the test condition is evaluating for
if test "${SOME_VAR#*$from asdf/qwer}" != "$SOME_VAR"; then
echo "##zxcv[message text='some text.' status='NORMAL']";
fi
The combination #*$ does not mean anything special. There are three special symbols and each of them has its own meaning in this context.
${SOME_VAR#abc} is a parameter expansion. Its value is the value of $SOME_VAR with the shortest prefix that matches abc removed.
In your example, abc is *${from} asdf/qwer. That means anything * followed by the value of variable $from (which is dynamic and replaced when the expression is evaluated), followed by a space and followed by asdf/qwer.
All in all, if the value of $SOME_VAR starts with a string that ends in ${from} asdf/qwer then everything before and including asdf/qwer is removed and the resulting value is passed as the first argument to test.
Type man bash in your terminal to read the documentation of bash or read it online.

SH - Replace words in text value by variables [duplicate]

This question already has answers here:
Bash expand variable in a variable
(5 answers)
Difference between single and double quotes in Bash
(7 answers)
Closed 1 year ago.
I have a problem that I am not able to solve, i need replace values of text variable in sh by the value of local variables.
Example:
~/Documents$ VERSION=1.0.0
~/Documents$ TEST='This is a test, VERSION: $VERSION'
~/Documents$ echo $TEST
This is a test, VERSION: $VERSION
I would like to transform the TEST string so that it uses the values ​​of the local variables.
I know I could change the ' ' to " " and that would work, but in my problem I get a literal string so I can't just do that.
Edit:
I am trying to convert a literal string into a string with replacement of values ​​by local variables in words beginning with "$", I could do this with a simple regex but I am looking for a better way, I believe that you should achieve this using only simple SH commands.
If TEST must be a literal string, you can use parameter substitution. These are bash docs, but it also works in sh:
${var/Pattern/Replacement}:
First match of Pattern, within var replaced with Replacement.
If Replacement is omitted, then the first match of Pattern is replaced by nothing, that is, deleted.
${var//Pattern/Replacement}: Global replacement. All matches of Pattern, within var replaced with Replacement.
For your given example, it would be ${TEST/\$VERSION/$VERSION}:
$ VERSION=1.0.0
$ TEST='This is a test, VERSION: $VERSION'
$ echo "${TEST/\$VERSION/$VERSION}"
This is a test, VERSION: 1.0.0
The first dollar sign is escaped so the Pattern is \$VERSION which becomes the literal string "$VERSION". Then its Replacement is $VERSION which gets interpolated as "1.0.0".

bash script whole file reading into a variable with newline [duplicate]

This question already has answers here:
I just assigned a variable, but echo $variable shows something else
(7 answers)
Closed 7 years ago.
i want to read whole file in a variable.
for example my file is.
file name : Q.txt
My name is Naeem Rehmat.
I am a student of FAST university.
Now i am in 4th semester.
I am learning bash script.
I have this problem.
code:
text=$(cat Q.txt)
echo $text
out put should be like this:
My name is Naeem Rehmat.
I am a student of FAST university.
Now i am in 4th semester.
I am learning bash script.
I have this problem.
Presumably the problem is that your whitespace is incorrect. Use double quotes:
echo "$text"
When you write echo $text without quotes, bash evaluates the string and performs what is known as "field splitting" or "word splitting" before generating the command. To simplify the case, suppose text is the string foo bar. Bash splits that into two "words" and passes "foo" as the first argument to echo, and "bar" as the second. If you use quotes, bash passes only one argument to echo, and that argument contains multiple spaces which echo will print.
Note that it is probably good style to also use quotes in the assignment of text (ie, text="$(cat Q.txt)"), although it is not necessary since field splitting does not occur in a variable assignment.

shell expansion when special character [] and * in the expression

i have a question about the shell expansion when [] occur in the expression If i create a folder named test[12],then under this folder i have two files test1 test2 so when i using
echo test\[12\]/*
the output is
test[12]/test1 test[12]/test2
but when i use
echo test[12]/*
the output is
test[12]/*
what's the trick here, i know the [] is a pair of special characters, but i can not tell why the file expansion is depends on the []!
When using echo test\[12\]/*, it will literally match like test[12]/files.
When using echo test[12]/*, it will match like test1/files, test2/file.
When using [] bash treated as a character class so the expansion is made and worked as special meaning.
Test it with by creating directory test1,test2 with file inside.
It will given the matched result.
Try to use echo test[12]/* instead of echo "test[12]/*.
When using echo "test[12]/*" it will literally print the content directly.

How does bash know where my variable names end?

Given:
myvar=Hello
echo $myvar -> Shows Hello (fine so far)
echo $myvar#world -> shows Hello#world (why? I thought it would complain that here is no such variable called myvar#world)
echo ${myvar#world} -> shows just Hello (again, why?)
The second case splits up into three parts:
[echo] [$myvar][#world]
1 2 3
Part 1 is the command, part 2 is a parameter, and part 3 is a string literal. The parameter stops on r since the # can't be part of the variable name (#'s are not allowed in variable names.)
The shell parser will recognise the start of a parameter name by the $, and the end by any character which cannot be part of the variable name. Normally only letters, numbers and underscores are allowed in a variable name, anything else will tell the shell that you're finished specifying the name of the variable.
All of these will print out $myvar followed by six literal characters:
echo $myvar world
echo $myvar?world
echo $myvar#world
If you want to put characters which can be part of a parameter directly after the parameter, you can include braces around the parameter name, like this:
myvar=hello
echo ${myvar}world
which prints out:
helloworld
Your third case is substring removal, except without a match. To get it to do something interesting, try this instead:
myvar="Hello World"
echo ${myvar#Hello }
which just prints World.
variables cannot contain a "#" so the shell knows its not part of a variable.
The construct ${myvar#world} actually is a string manipulator explained below:
# is actuially a string modifier that will remove the first part of the string matching "world". Since there is no string matching world in myvar is just echos back "hello"

Resources