What does single parenthesis do here? - bash

I encountered this code:
file=$(<filename)
This reads the file from the filename.
My question is how does this work?
I read from this post:
How to use double or single brackets, parentheses, curly braces
It tells me, single parentheses can function as:
- Sub bash execution
- Array construction
But in the case above, I do not know how this explains.
Besides this question, I want to know that why when I do echo $file, the file content concatenate into one line?

$(...) performs command substitution; the command inside is read and the output from stdout is returned to the script.
<... is redirection; the contents of the file are read and fed into stdin of the process.
Putting the two together results in an implicit cat, connecting the stdin of the redirection to the stdout of the command substitution, reading the contents of the file into the script.

You must surround your variable into double quotes, else it will be expanded into command line arguments that will be passed to echo.
If you surround it into double quotes variable will be passed as single argument and echo will display it correctly.
echo "$file"

echo $file
will give you a concatenated output.
Try
echo "$file"
this will give you an output in multiple lines.

Related

How to make bash script take file names with spaces?

I have a bash script like this:
myfiles=("file\ with\ spaces.csv")
for file_name in "${myfiles[#]}"
do
echo "removing first line of file $file_name"
echo "first line is `head -1 $file_name`"
echo "\n"
done
but it does not recognize the spaces for some reason, even though I enclosed it in double quotes "":
head: cannot open ‘file\\’ for reading: No such file or directory
How do I fix this?
You need double quotes inside the backticks. The outer set isn't sufficient.
echo "first line is `head -1 "$file_name"`"
Also, do not put backslashes in the file name, since it's already quoted. Quotes or backslashes, but not both.
myfiles=("file with spaces.csv")
myfiles=(file\ with\ spaces.csv)
To expand on #JohnKugelman's answer:
Quoting takes a bit of getting used to in Bash. As a simple rule use single quotes for static strings with no special characters, double quotes for strings with variables, and $'' quoting for strings with special characters.
There's a separate quoting context inside every command substitution.
$() is a clearer way to establish a command substitution, because it can be nested much easier.
Consequently you'd typically write myfiles=('file with spaces.csv') and echo "first line is $(head -1 "$file_name")".

Output ${projSRC} to file in Bash script

I am writing a Bash script that creates a CMakeLists.txt file for a project.
The problem arises at this portion:
echo "file(GLOB projSRC src/*.cpp)" >> CMakeLists.txt
After that, I need the program to output ${SOURCES} into CMakeLists.txt
I don't mean a variable in the script named SOURCES, I mean it should actually write the plaintext ${SOURCES}.
What I mean is, the final file should look this like:
arbitrary_command(target PROJECT sources ${SOURCES})
and not like:
arbitrary_command(target PROJECT sources [insert however bash messes it up here])
How can I do this in my Bash script?
Use single quotes, not double quotes, for a literal string:
echo 'file(GLOB ${projSRC} src/*.cpp)' >> CMakeLists.txt
That said, you might consider using a heredoc (or even a quoted heredoc) in this case, to write the entire file as one command:
cat >CMakeLists.txt <<'EOF'
everything here will be emitted to the file exactly as written
${projSRC}, etc
even over multiple lines
EOF
...or, if you want some substitutions, an unquoted heredoc (that is, one where the sigil -- EOF in these examples -- isn't quoted at the start):
foo="this"
cat >CMakeLists.txt <<EOF
here, parameter expansions will be honored, like ${foo}
but can still be quoted: \${foo}
EOF
You can also have multiple commands writing output to a single redirection, to avoid paying the cost of opening your output file more than once:
foo=this
{
echo "Here's a line, which is expanded due to double quotes: ${foo}"
echo 'Here is another line, with no expansion due to single quotes: ${foo}'
} >CMakeLists.txt
May be I don't understand your question...
But
echo \${SOURCES}
will print
${SOURCES}
for you.

printing the ampersand

I have a bash script that takes a url with variables and writes it to a file, problem is the ampersand is interfering and being interpreted as a command / control character.
In this situation the string cannot be escaped BEFORE being passed to the script and I have yet to find any way to do this.
if [ $1 ] ; then
url=$1
printf %q "$url" > "/somepath/somefile"
fi
with $1 being for example localhost?x=1&y=2&z=3
What get's printed is only the part before the first ampersand: "localhost?x=1"
I have also tried echo instead of printf but it's exactly the same ??
Your script is fine, but you need to invoke the script with a quoted parameter:
./myscript.sh "localhost?x=1&y=2&z=3"
There is no problem with echo nor print. The problem is that when you run the script, it starts those 2 jobs in background. For more information you can check: http://hacktux.com/bash/ampersand.
You can simply start script with 'localhost?x=1&y=2&z=3' in apostrophes, so bash will not treat ampersand as operator but just as normal character.
Quote things. Replace all $1s with "$1"s. And quote argument when you actually invoke your script.

Bash - demostrating the order of substitution

I'm trying to demonstrate the order of substitution; mainly that variable substitution, command substitution and globbing occurs in order one after the other. I executed the following command and I do not get the expected output.
bash-4.1$ a=file*
bash-4.1$ ls $(echo $($(b=$a)))
I expect the output to list all files names beginning with "file", but instead it outputs the list of all files in the directory. Any idea why?
The $(...) command substitution returns the output of the command, which is blank for an assignment. So you simply end up running ls.
Parameter and arithmetic expansion, and command substitution are evaluated at the same time, leftmost-innermost to right. Assignments, null and empty expansions, and redirects are all valid simple commands. The assignment is lost to the subshell, and the arguments to echo expand to nothing. echo outputs a newline, but the command substitution strips it, and ls gets no args. Also, if you were expecting a=file* to do something more than assign a literal string, pathname expansion doesn't occur in assignments.
See: http://wiki.bash-hackers.org/syntax/grammar/parser_exec
And a challenge question when you figure all that out. What will be the value of x? (shows more expansion order and some small Bash quirks.)
declare -i x=0
x+=${x:-1} let x+=2 $((x+=4)) {x}<&$((x+=8,0))

place a multi-line output inside a variable

I'm writing a script in bash and I want it to execute a command and to handle each line separately. for example:
LINES=$(df)
echo $LINES
it will return all the output converting new lines with spaces.
example:
if the output was supposed to be:
1
2
3
then I would get
1 2 3
how can I place the output of a command into a variable allowing new lines to still be new lines so when I print the variable i will get proper output?
Generally in bash $v is asking for trouble in most cases. Almost always what you really mean is "$v" in double quotes:
LINES="$(df)"
echo "$LINES"
No, it will not. The $(something) only strips trailing newlines.
The expansion in argument to echo splits on whitespace and than echo concatenates separate arguments with space. To preserve the whitespace, you need to quote again:
echo "$LINES"
Note, that the assignment does not need to be quoted; result of expansion is not word-split in assignment to variable and in argument to case. But it can be quoted and it's easier to just learn to just always put the quotes in.

Resources