String concatenation in bash 3.2.57 (macOS) - string

My goal is to concatenate two strings.
Here's a copy-paste of my bash script:
str1="Hello"
str2="World"
str3=$str1$str2
echo $str3
The expected output is HelloWorld but I'm getting World instead.
It works fine when I run it in the terminal.
Here's the output when I run cat -v on my script:
str1="Hello"^M
str2="World"^M
str3=$str1$str2^M
echo $str3^M
Am I missing something?

That's probably because you have a carriage return (\r) at the end of $str1. I'm getting the same output with the following:
#!/bin/bash
str1="Hello"$'\r'
str2="World"
str3=$str1$str2
echo $str3
It often happens when you create the script on a MSWin machine.
It prints Hello, but then \r moves the cursor back to the beginning of the line, and overwrites the Hello with World.
You can verify it by running it through a hex dump or cat -v.

Related

Bash when i try to apped to a string its overwrite

i run the following function in (git-)bash under windows:
function config_get_container_values() {
local project_name=$1
local container_name=$2
#local container_name="gitea"
echo "###"
buildcmd="jq -r \".containers[]."
echo "$buildcmd"
buildcmd="${buildcmd}${container_name}"
echo "$buildcmd"
buildcmd="${buildcmd}foobar"
echo "$buildcmd"
echo "###"
}
The output of this is the following. Whyever, after using the variable to extend the string, he starts to overwrite $buildcmd. I tried this also with everything in one line as well with the append command (=+). Everytime the same result.
###
jq -r ".containers[].
jq -r ".containers[].gitea
foobar".containers[].gitea
###
The really strange thing is: When i enable the line local container_name="gitea" everything works as expected. The output is:
###
jq -r ".containers[].
jq -r ".containers[].gitea
jq -r ".containers[].giteafoobar
###
When i put this all into a news file, its also works as expected. So i think something goes wrong in the thousands of line before calling this function. Any idea, what could be cause of this behavior?
Regards
Dave
This is not how you should build up the command, DOS line endings aside. Use --arg to pass the name into the filter as a variable. For example,
config_get_container_values() {
local project_name=$1
local container_name=$2
jq -r --arg n "$container_name " '.containers[][$n+"foobar"]'
}
config_get_container foo gitea < some.json
If the function is invoked with
config_get_container_values proj gitea
it produces the "expected" output. If it is invoked with
config_get_container_values proj $'gitea\r'
it produces output that looks like the first output example. $'gitea\r' expands to a string that consists of 'gitea' followed by a Carriage return (CR) character.
One possible cause of the problem is that the container name (gitea) was read from a file that had Windows/DOS line endings (CR-LF). Problems like that are common. See the first question ("Check whether your script or data has DOS style end-of-line characters") in the "Before asking about problematic code" section of the Stack Overflow 'bash' Info page.

How do you append a string built with interpolation of vars and STDIN to a file?

Can someone fix this for me.
It should copy a version log file to backup after moving to a repo directory
Then it automatically appends line given as input to the log file with some formatting.
That's it.
Assume existence of log file and test directory.
#!/bin/bash
cd ~/Git/test
cp versionlog.MD .versionlog.MD.old
LOGDATE="$(date --utc +%m-%d-%Y)"
read -p "MSG > " VHMSG |
VHENTRY="- **${LOGDATE}** | ${VHMSG}"
cat ${VHENTRY} >> versionlog.MD
shell output
virufac#box:~/Git/test$ ~/.logvh.sh
MSG > testing script
EOF
EOL]
EOL
e
E
CTRL + C to get out of stuck in reading lines of input
virufac#box:~/Git/test$ cat versionlog.MD
directly outputs the markdown
# Version Log
## version 0.0.1 established 01-22-2020
*Working Towards Working Mission 1 Demo in 0.1 *
- **01-22-2020** | discovered faker.Faker and deprecated old namelessgen
EOF
EOL]
EOL
e
E
I finally got it to save the damned input lines to the file instead of just echoing the command I wanted to enter on the screen and not executing it. But... why isn't it adding the lines built from the VHENTRY variable... and why doesn't it stop reading after one line sometimes and this time not. You could see I was trying to do something to tell it to stop reading the input.
After some realizing a thing I had done in the script was by accident... I tried to fix it and saw that the | at the end of the read command was seemingly the only reason the script did any of what it did save to the file in the first place.
I would have done this in python3 if I had know this script wouldn't be the simplest thing I had ever done. Now I just have to know how you do it after all the time spent on it so that I can remember never to think a shell script will save time again.
Use printf to write a string to a file. cat tries to read from a file named in the argument list. And when the argument is - it means to read from standard input until EOF. So your script is hanging because it's waiting for you to type all the input.
Don't put quotes around the path when it starts with ~, as the quotes make it a literal instead of expanding to the home directory.
Get rid of | at the end of the read line. read doesn't write anything to stdout, so there's nothing to pipe to the following command.
There isn't really any need for the VHENTRY variable, you can do that formatting in the printf argument.
#!/bin/bash
cd ~/Git/test
cp versionlog.MD .versionlog.MD.old
LOGDATE="$(date --utc +%m-%d-%Y)"
read -p "MSG > " VHMSG
printf -- '- **%s** | %s\n' "${LOGDATE}" "$VHMSG" >> versionlog.MD

Linux .sh auto set local variables empty when echo

./run.sh:
1. cp=warmonger-1.0.0.jar
2. cmmd="java -server -D64 -Xms200m -Xmx200m
-Dlog4j.configurationFile=$WARMONGER_HOME/etc/log4j2.xml
-classpath $cp warmonger.agent.WarmongerAgentApp"
3. echo $cmmd
execute results:
dataq.agent.DataqAgentApp -Xmx200m
-Dlog4j.configurationFile=/warmonger/etc/log4j2.xml
-classpath warmonger-1.0.0.jar
"warmonger.agent.WarmongerAgentApp" not appear.
I means if remove echo, java will be throw an exception: Couldn't find main class
You won't see $cp when you echo $cmmd, because the shell substitutes the value of the cp parameter (warmonger-1.0.0.jar) in the assignment to cmmd.
You can escape the dollar sign, or use single quotes if you don't want the shell to expand the parameter.
Your shell script is CR-LF terminated (DOS/Windows ends of lines). Thus, from bash point of view, the cp variable contains warmonger-1.0.0.jar<CR> (notice the trailing <CR>).
When you echo the content of the cp variable, <CR> is echoed too which puts the cursor at the beginning of the line (CR = carriage return). echo then prints the remaining of the arguments at the beginning of the line.
You can see it in your output:
"java -server -D64 -Xms200m -Xmx200m" is overwritten with "warmonger.agent.WarmongerAgentApp"
which, in turn, is overwritten by some other command output ("dataq.agent.Dataq")
Solution: turn your DOS/Windows text file into a UNIX one. See this answer.

Printing a "Hello World" in bourne shell without directly using white space char

I'm currently trying to solve a tricky/silly challenge and i've come to a dead end.
The challenge is basically to form a one-liner /bin/sh compatible command line
which essentially outputs "Hello World" without directly typing White space or Tab characters in the command itself.
for example something like -
echo Hello World
would be invalid since we used white space twice in the command line.
Any ideas?
Assuming that IFS by default is set to space:
# echo${IFS}a${IFS}b
a b
Tested on Solaris 10 sh.
Cheating a little, but it gives the correct effect (superficially) in bash:
PS1=hello$'\x20'world$'\n'"$PS1"
for example,
$ PS1=hello$'\x20'world$'\n'"$PS1"
hello world
$
The problem is that it will print hello world after every command in future :-)

How to show line number when executing bash script

I have a test script which has a lot of commands and will generate lots of output, I use set -x or set -v and set -e, so the script would stop when error occurs. However, it's still rather difficult for me to locate which line did the execution stop in order to locate the problem.
Is there a method which can output the line number of the script before each line is executed?
Or output the line number before the command exhibition generated by set -x?
Or any method which can deal with my script line location problem would be a great help.
Thanks.
You mention that you're already using -x. The variable PS4 denotes the value is the prompt printed before the command line is echoed when the -x option is set and defaults to : followed by space.
You can change PS4 to emit the LINENO (The line number in the script or shell function currently executing).
For example, if your script reads:
$ cat script
foo=10
echo ${foo}
echo $((2 + 2))
Executing it thus would print line numbers:
$ PS4='Line ${LINENO}: ' bash -x script
Line 1: foo=10
Line 2: echo 10
10
Line 3: echo 4
4
http://wiki.bash-hackers.org/scripting/debuggingtips gives the ultimate PS4 that would output everything you will possibly need for tracing:
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
In Bash, $LINENO contains the line number where the script currently executing.
If you need to know the line number where the function was called, try $BASH_LINENO. Note that this variable is an array.
For example:
#!/bin/bash
function log() {
echo "LINENO: ${LINENO}"
echo "BASH_LINENO: ${BASH_LINENO[*]}"
}
function foo() {
log "$#"
}
foo "$#"
See here for details of Bash variables.
PS4 with value $LINENO is what you need,
E.g. Following script (myScript.sh):
#!/bin/bash -xv
PS4='${LINENO}: '
echo "Hello"
echo "World"
Output would be:
./myScript.sh
+echo Hello
3 : Hello
+echo World
4 : World
Workaround for shells without LINENO
In a fairly sophisticated script I wouldn't like to see all line numbers; rather I would like to be in control of the output.
Define a function
echo_line_no () {
grep -n "$1" $0 | sed "s/echo_line_no//"
# grep the line(s) containing input $1 with line numbers
# replace the function name with nothing
} # echo_line_no
Use it with quotes like
echo_line_no "this is a simple comment with a line number"
Output is
16 "this is a simple comment with a line number"
if the number of this line in the source file is 16.
This basically answers the question How to show line number when executing bash script for users of ash or other shells without LINENO.
Anything more to add?
Sure. Why do you need this? How do you work with this? What can you do with this? Is this simple approach really sufficient or useful? Why do you want to tinker with this at all?
Want to know more? Read reflections on debugging
Simple (but powerful) solution: Place echo around the code you think that causes the problem and move the echo line by line until the messages does not appear anymore on screen - because the script has stop because of an error before.
Even more powerful solution: Install bashdb the bash debugger and debug the script line by line
If you're using $LINENO within a function, it will cache the first occurrence. Instead use ${BASH_LINENO[0]}

Resources