How to print literal string "$1" in bash script? - bash

I want to print string called "$1". But when I do this with echo it prints string which equals to "$1" variable. How can I print "$1" just like string?
for example:
set -- "output" # this sets $1 to be "output"
echo $1 # ==> output
But I want this:
echo $1 # ==> $1

You have to escape the $ to have it shown:
$ echo "\$1"
or, as noted by JeremyP in comments, just use single quotes so that the value of $1 does not get expanded:
$ echo '$1'

You need to either:
Enclose the variable in SINGLE quotes: echo '$1'
Escape the $ sign: echo "\$1"

Related

How to remove the character before the pattern found? (Unix)

I have a variable:
$var="-- ---comment- --
abcd;"
I want to remove every character that is between "--" comment "--"
Expected output:
$var="abcd;"
What I am trying in test.sh
#!/bin/bash
var="----comment---
abcd;"
test=$(sed 's/^--.*--/' $var)
echo $test
It is not working. I get the following error:
command not found
Trying to replace the pattern that you don't expect in output with nothing, something like this
test=$(echo $var | sed 's/^--.*--//g')
Should work.
In bash:
$ cat foo.sh
var="----comment---
abcd;"
echo "$var" # this outputs the original var
echo "${var/--* /}" # this output replacing everything between -- and " "
$ bash foo.sh
----comment---
abcd;
abcd;
You can set the output to a variable by
$ var="${var/--* /}"

Bash variable substitution and strings

Let's say I have two variables:
a="AAA"
b="BBB"
I read a string from a file. This string is the following:
str='$a $b'
How to create a new string from the first one that substitutes the variables?
newstr="AAA BBB"
bash variable indirection whithout eval:
Well, as eval is evil, we may try to make this whithout them, by using indirection in variable names.
a="AAA"
b="BBB"
str='$a $b'
newstr=()
for cnt in $str ;do
[ "${cnt:0:1}" == '$' ] && cnt=${cnt:1} && cnt=${!cnt}
newstr+=($cnt)
done
newstr="${newstr[*]}"
echo $newstr
AAA BBB
Another try:
var1="Hello"
var2="2015"
str='$var1 world! Happy new year $var2'
newstr=()
for cnt in $str ;do
[ "${cnt:0:1}" == '$' ] && cnt=${cnt:1} && cnt=${!cnt}
newstr+=($cnt)
done
newstr="${newstr[*]}"
echo $newstr
Hello world! Happy new year 2015
Addendum As correctly pointed by #EtanReisner's comment, if your string do contain some * or other glob expendable stings, you may have to use set -f to prevent bad things:
cd /bin
var1="Hello"
var2="star"
var3="*"
str='$var1 this string contain a $var2 as $var3 *'
newstr=()
for cnt in $str ;do
[ "${cnt:0:1}" == '$' ] && cnt=${cnt:1} && cnt=${!cnt};
newstr+=("$cnt");
done;
newstr="${newstr[*]}"
echo "$newstr"
Hello this string contain a star as * bash bunzip2 busybox....zmore znew
echo ${#newstr}
1239
Note: I've added " at newstr+=("$cnt"); to prevent glob expansion, but set -f seem required...
newstr=()
set -f
for cnt in $str ;do
[ "${cnt:0:1}" == '$' ] && cnt=${cnt:1} && cnt=${!cnt}
newstr+=("$cnt")
done
set +f
newstr="${newstr[*]}"
echo "$newstr"
Hello this string contain a star as * *
Nota 2: This is far away from a perfect solution. For sample if string do contain ponctuation, this won't work again... Example:
str='$var1, this string contain a $var2 as $var3: *'
with same variables as previous run will render:
' this string contain a star as *' because ${!var1,} and ${!var3:} don't exist.
... and if $str do contain special chars:
As #godblessfq asked:
If str contains a line break, how do I do the substitution and preserve the newline in the output?
So this is not robust as every indirected variable must be first, last or space separated from all special chars!
str=$'$var1 world!\n... 2nd line...'
var1=Hello
newstr=()
set -f
IFS=' ' read -d$'\377' -ra array <<<"$str"
for cnt in "${array[#]}";do
[ "${cnt:0:1}" == '$' ] && cnt=${cnt:1} && cnt=${!cnt}
newstr+=("$cnt")
done
set +f
newstr="${newstr[*]}"
echo "$newstr"
Hello world!
... 2nd line...
As <<< inline string add a trailing newline, last echo command could be written:
echo "${newstr%$'\n'}"
The easiest solution is to use eval:
eval echo "$str"
To assign it to a variable, use command substitution:
replaced=$(eval echo "$str")
Disclaimer: I only discovered perl an hour ago. But this seems to work robustly, whatever special characters you throw at it:
newstr=$(a2="$a" b2="$b" perl -pe 's/\$a\b/$ENV{a2}/g; s/\$b\b/$ENV{b2}/g' <(echo -e "$str"))
Test:
a='A*A\nA'
b='B*B\nB'
str='$a $aa * \n $b $bb'
newstr=$(a2="$a" b2="$b" perl -pe 's/\$a\b/$ENV{a2}/g; s/\$b\b/$ENV{b2}/g' <(echo -e "$str"))
echo -e "$newstr"
Output:
A*A
A $aa *
B*B
B $bb
I'd use awk solution with awk-variables. This will allow passing a text containing special chars and subsitute any placeholder with it.
a workaround to recognize $ would be using [\x24]:
awk -v a="$a" -v b="$b" '{gsub("[\x24]a",a);gsub("[\x24]b",b); print}' <<< $str
here
-v defines variable a="$a"
[x24] is ASCII for $, so [x24]a equal to $a
gsub(x,y) - replaces x with y

How can I get the length of all arguments given to a function in bash?

I'd like to get the length of the string that I get when i use "$*" in my function.
I tried:
echo ${#"$*"}
and
echo ${#"*"}
both gave me a bad substitution error.
I don't think you can do it in a single command. However, this seems to work:
#!/bin/bash
a="$#"
echo "${#a}"
Using a temporary variable is the only one basic way, and you need to unset IFS or set it to empty string to prevent spaces in between. And use $* not $# for it would give you spaces in between:
IFS= eval "__=\"\$*\""
echo "${#__}"
Another way is to loop through all strings:
L=0; for __; do (( L += ${#__} )); done
echo "$L"
You can use one of the following.
expr length "$*"
echo "$*" | awk '{print length}'
$# holds the number of positional parameters passed to the function.
Try it:
#!/bin/bash
t() {
echo "num args=$#"
echo "all $*"
}
t "$#"

assinging "-n" string to a variable doesn't work

$ OPTION="-n"
$ echo $OPTION
$
Nothing happens. I expected this.
$ OPTION="-n"
$ echo $OPTION
-n
$
Why is this?
-n is a parameter to echo, which means the trailing newline is suppressed. The fact that there's no empty line between $ echo $OPTION and the following $ means that $OPTION is properly set to -n.
If you put something else in front of $OPTION, the echo will work as you expect it to. echo only interprets words at the beginning of the command as options. As soon as it finds a non-option word ("OPTION", in this case), all words that follow are treated as literals, and not parsed as options to echo.
$ echo OPTION is set to $OPTION
OPTION is set to -n
$
Remember that the shell expands environment variables before the command is executed. Thus:
option="-n"
echo $option
becomes
echo -n ""
With the value of $option being interpreted as a parameter for the echo command. If you were using Kornshell (which is 95% similar to BASH), you could have used the builtin print command instead:
option="-n"
print -- "$option"
Unfortunately, BASH doesn't have the print command, and using the double dash in the BASH echo command will print out a double dash -- not what you want.
Instead, you'll have to use the printf command which is a bit slower than echo:
option="-n"
printf -- "$option\n" #Must include the \n to make a new line!
Of course, if you had this, you'd be in trouble:
option="%d"
printf -- "$option\n"
To get around that:
option="%d"
printf "%s\n", "$option"
By the way, you have the same problems with test:
option="-n"
if [ "$option" -eq "-n" ] #Won't work!
This is why you'll see people do this:
if [ "x$option" -eq "x-n" ] #Will work
To get the desired result, you could do this:
$ OUTPUT='-n'
$ echo -en ${OUTPUT}\\n

Passing a string with spaces as a function argument in Bash

I'm writing a Bash script where I need to pass a string containing spaces to a function in my Bash script.
For example:
#!/bin/bash
myFunction
{
echo $1
echo $2
echo $3
}
myFunction "firstString" "second string with spaces" "thirdString"
When run, the output I'd expect is:
firstString
second string with spaces
thirdString
However, what's actually output is:
firstString
second
string
Is there a way to pass a string with spaces as a single argument to a function in Bash?
You should add quotes and also, your function declaration is wrong.
myFunction()
{
echo "$1"
echo "$2"
echo "$3"
}
And like the others, it works for me as well.
Another solution to the issue above is to set each string to a variable, call the function with variables denoted by a literal dollar sign \$. Then in the function use eval to read the variable and output as expected.
#!/usr/bin/ksh
myFunction()
{
eval string1="$1"
eval string2="$2"
eval string3="$3"
echo "string1 = ${string1}"
echo "string2 = ${string2}"
echo "string3 = ${string3}"
}
var1="firstString"
var2="second string with spaces"
var3="thirdString"
myFunction "\${var1}" "\${var2}" "\${var3}"
exit 0
Output is then:
string1 = firstString
string2 = second string with spaces
string3 = thirdString
In trying to solve a similar problem to this, I was running into the issue of UNIX thinking my variables were space delimeted. I was trying to pass a pipe delimited string to a function using awk to set a series of variables later used to create a report. I initially tried the solution posted by ghostdog74 but could not get it to work as not all of my parameters were being passed in quotes. After adding double-quotes to each parameter it then began to function as expected.
Below is the before state of my code and fully functioning after state.
Before - Non Functioning Code
#!/usr/bin/ksh
#*******************************************************************************
# Setup Function To Extract Each Field For The Error Report
#*******************************************************************************
getField(){
detailedString="$1"
fieldNumber=$2
# Retrieves Column ${fieldNumber} From The Pipe Delimited ${detailedString}
# And Strips Leading And Trailing Spaces
echo ${detailedString} | awk -F '|' -v VAR=${fieldNumber} '{ print $VAR }' | sed 's/^[ \t]*//;s/[ \t]*$//'
}
while read LINE
do
var1="$LINE"
# Below Does Not Work Since There Are Not Quotes Around The 3
iputId=$(getField "${var1}" 3)
done<${someFile}
exit 0
After - Functioning Code
#!/usr/bin/ksh
#*******************************************************************************
# Setup Function To Extract Each Field For The Report
#*******************************************************************************
getField(){
detailedString="$1"
fieldNumber=$2
# Retrieves Column ${fieldNumber} From The Pipe Delimited ${detailedString}
# And Strips Leading And Trailing Spaces
echo ${detailedString} | awk -F '|' -v VAR=${fieldNumber} '{ print $VAR }' | sed 's/^[ \t]*//;s/[ \t]*$//'
}
while read LINE
do
var1="$LINE"
# Below Now Works As There Are Quotes Around The 3
iputId=$(getField "${var1}" "3")
done<${someFile}
exit 0
A more dynamic way would be:
function myFunction {
for i in "$*"; do echo "$i"; done;
}
The simplest solution to this problem is that you just need to use \" for space separated arguments when running a shell script:
#!/bin/bash
myFunction() {
echo $1
echo $2
echo $3
}
myFunction "firstString" "\"Hello World\"" "thirdString"
Your definition of myFunction is wrong. It should be:
myFunction()
{
# same as before
}
or:
function myFunction
{
# same as before
}
Anyway, it looks fine and works fine for me on Bash 3.2.48.
Simple solution that worked for me -- quoted $#
Test(){
set -x
grep "$#" /etc/hosts
set +x
}
Test -i "3 rb"
+ grep -i '3 rb' /etc/hosts
I could verify the actual grep command (thanks to set -x).
You could have an extension of this problem in case of your initial text was set into a string type variable, for example:
function status(){
if [ $1 != "stopped" ]; then
artist="ABC";
track="CDE";
album="DEF";
status_message="The current track is $track at $album by $artist";
echo $status_message;
read_status $1 "$status_message";
fi
}
function read_status(){
if [ $1 != "playing" ]; then
echo $2
fi
}
In this case if you don't pass the status_message variable forward as string (surrounded by "") it will be split in a mount of different arguments.
"$variable": The current track is CDE at DEF by ABC
$variable: The
I had the same kind of problem and in fact the problem was not the function nor the function call, but what I passed as arguments to the function.
The function was called from the body of the script - the 'main' - so I passed "st1 a b" "st2 c d" "st3 e f" from the command line and passed it over to the function using myFunction $*
The $* causes the problem as it expands into a set of characters which will be interpreted in the call to the function using whitespace as a delimiter.
The solution was to change the call to the function in explicit argument handling from the 'main' towards the function: the call would then be myFunction "$1" "$2" "$3" which will preserve the whitespace inside strings as the quotes will delimit the arguments...
So if a parameter can contain spaces, it should be handled explicitly throughout all calls of functions.
As this may be the reason for long searches to problems, it may be wise never to use $* to pass arguments...

Resources