Here we have 2 vars in bash shell
ID="ABC"
ID_STRING="Here is the [${ID}]"
Is there any approach that we could dynamic replace the var ${ID} to "ABC" in the ID_STRING and echo to concole?
If I understand the question correctly the ID_STRING variable contains the literal string ${ID} when it is echoed to the screen and isn't assigned the way you show in the question - assigning it the way you have it in your question using double quotes means the ID_STRING variable never actually contains the literal string ${ID} because with double quotes the variable replacement is done as it is assigned. So first: to get the literal string ${ID} into ID_STRING you need to use single quotes. And second: you need to reevaluate ID_STRING as it is echoed. I do it like this (the $ is the bash prompt):
$ ID=ABC
$ echo $ID
ABC
$ ID_STRING='Here is the [${ID}]'
$ echo $ID_STRING
Here is the [${ID}]
$ eval echo $ID_STRING
Here is the [ABC]
$ echo $ID, $ID_STRING, `eval echo $ID_STRING`
ABC, Here is the [${ID}], Here is the [ABC]
You you want to replace ${ID} from string:
ID="ABC"
sed 's/\${ID}/'$ID'/' filename
Output:
ID_STRING="Here is the "[ABC]"
If you want to use that value of ID while assigning the string to ID_STRING, you can use:
ID="ABC"
ID_STRING="Here is the [$ID]"
Now:
echo $ID_STRING
gives
Here is the [ABC]
sed -i -e 's/old/new/g' file
Is basically what you need, to output the changes have a look here.
Related
Suppose I have a strings which consist of sequences of commands:
s1="c1|c2|c3>c4"
s2="c1>c2|c3|c4"
I want to split these strings into two strings. The first string will be the first command c1. The second string will be the remaining commands (and pipes and redirects).
I can easily get the first command:
echo ${s1%%[>|]*}
c1
echo ${s2%%[>|]*}
c1
But getting the second string isn't so straightforward:
What I get:
echo ${s1#*[>|]}
c2|c3>c4
echo ${s2#*[>|]}
c2|c3>c4
What I want:
|c2|c3>c4
>c2|c3>c4
To summarize, if I have a compound command, where one command is piped or redirected to other commands, I want to split off the first command into one string, and the remaining commands into another string. including all pipes and redirects.
Capture the "c1" part in a variable, then, from the start of the original string, remove the captured text:
$ cmd=${s1%%[>|]*}; echo "$cmd"; echo "${s1#"$cmd"}"
c1
|c2|c3>c4
$ cmd=${s2%%[>|]*}; echo "$cmd"; echo "${s2#"$cmd"}"
c1
>c2|c3|c4
This even works when the string contains glob characters:
$ s2="c1*>c2|c3|c4"
$ cmd=${s2%%[>|]*}; echo "$cmd"; echo "${s2#"$cmd"}"
c1*
>c2|c3|c4
I am using Bourne Shell. Need to confirm if my understanding of following is correct?
$ echo $SHELL
/bin/bash
$ VAR="NJ:NY:PA" <-- declare an array with semicolon as separator?
$ echo ${VAR#*} <-- show entire array without separator?
NJ:NY:PA
$ echo ${VAR#*:*} <-- show array after first separator?
NY:PA
$ echo ${VAR#*:*:*} <-- show string after two separator
PA
${var#pattern} is a parameter expansion that expands to the value of $var with the shortest possible match for pattern removed from the front of the string.
Thus, ${VAR#*:} removes everything up and including to the first :; ${VAR#*:*:} removes everything up to and including the second :.
The trailing *s on the end of the expansions given in the question don't have any use, and should be avoided: There's no reason whatsoever to use ${var#*:*:*} instead of ${var#*:*:} -- since these match the smallest amount of text possible, and * is allowed to expand to 0 characters, the final * matches and removes nothing.
If what you really want is an array, you might consider using a real array instead.
# read contents of string VAR into an array of states
IFS=: read -r -a states <<<"$VAR"
echo "${states[0]}" # will echo NJ
echo "${states[1]}" # will echo NY
echo "${#states[#]}" # count states; will emit 3
...which also gives you the ability to write:
printf ' - %s\n' "${states[#]}" # put *all* state names into an argument list
I thought my bash-fu was strong enough but apparently it isn't. I can't seem to figure this out. I would like to do something like this:
var="XXXX This is a line"
word_to_replace="XXXX"
# ...do something
echo "Done:${var}"
Done: This is a line
Basically I want to quickly replace all characters in a word with spaces, preferably in one step. Note, if it makes things easier var currently will be at the start of the string although it may have leading spaces (which would need to be retained).
In python I would possibly do this:
>>> var="XXXX This is a line"
>>> word_to_replace="XXXX"
>>> var=var.replace(word_to_replace, ' '*len(word_to_replace))
>>> print("Done:%s" % var)
Done: This is a line
Here's one way you could do it, using a combination of shell parameter expansion and the sed command.
$ var="XXXX This is a line"
$ word_to_replace="XXXX"
$ replacement=${word_to_replace//?/ }
$ sed "s/$word_to_replace/$replacement/" <<<"$var"
This is a line
? matches any character and ${var//find/replace} does a global substitution, so the variable $replacement has the same length as $word_to_replace, but is composed solely of spaces.
You can save the result to a variable in the usual way:
new_var=$(sed "s/$word_to_replace/$replacement/" <<<"$var")
In plain Bash:
If we know the word to be replaced:
$ line=" foo and some"
$ word=foo
$ spaces=$(printf "%*s" ${#word} "")
$ echo "${line/$word/$spaces}"
and some
If we don't, we could pick the string apart to find the leading word, but this gets a bit ugly:
xxx() {
shopt -s extglob # for *( )
local line=$1
local indent=${line%%[^ ]*} # the leading spaces
line=${line##*( )} # remove the leading spaces
local tail=${line#* } # part after first space
local head=${line%% *} # part before first space...
echo "$indent${head//?/ } $tail" # replace and put back together
}
$ xxx " word on a line"
on a line
That also fails if there is only one word on the line, head and tail both get set to that word, we'd need to check for if there is a space and handle the two cases separately.
Using sed:
#!/usr/bin/env sh
word_to_replace="XXXX"
var="$word_to_replace This is a line"
echo "Done: $var"
word_to_replace=$(echo "$word_to_replace" | sed 's,., ,g')
var="$word_to_replace This is a line"
echo "Done: $var"
I use GNU Awk:
echo "$title" | gawk '{gsub(/./, "*"); print}'
This replaces each character with an asterisk.
EDIT. Consolidated answer:
$ export text="FOO hello"
$ export sub="FOO"
$ export space=${sub//?/ }
$ echo "${text//$sub/$space}"
hello
Given a list of paths separated by a single space:
/home/me/src/test /home/me/src/vendor/a /home/me/src/vendor/b
I want to remove the prefix /home/me/src/ so that the result is:
test vendor/a vendor/b
For a single path I would do: ${PATH#/home/me/src/} but how do I apply it to this series?
You can use // to replace all occurrences of substring. Replace it with null string to remove them.
$ path="/home/me/src/test /home/me/src/vendor/a /home/me/src/vendor/b"
$ echo ${path//\/home\/me\/src\/}
test vendor/a vendor/b
Reference: ${parameter/pattern/string} in Bash reference manual
Using shell parameter expansion doesn't seem to be the solution for this, since it would remove everything up to / from a given point is useful, as nu11p01n73R's answer reveals.
For clarity, I would use sed with the syntax sed 's#pattern#replacement#g':
$ str="/home/me/src/test /home/me/src/vendor/a /home/me/src/vendor/b"
$ sed 's#/home/me/src/##g' <<< "$str"
test vendor/a vendor/b
Like always a grep solution from my side :
echo 'your string' | grep -Po '^/([^ /]*/)+\K.+'
Please note that the above regex do this for any string like /x/y/z/test ... But if you are interested only in replacing /home/me/src/, try the following :
echo 'your string' | grep -Po '^/home/me/src/\K.+' --color
I have two questions .
I have found following code line in script : IFS=${IFS#??}
I would like to understand what it is exactly doing ?
When I am trying to perform something in every place from directory like eg.:
$1 = home/user/bin/etc/something...
so I need to change IFS to "/" and then proceed this in for loop like
while [ -e "$1" ]; do
for F in `$1`
#do something
done
shift
done
Is that the correct way ?
${var#??} is a shell parameter expansion. It tries to match the beginning of $var with the pattern written after #. If it does, it returns the variable $var with that part removed. Since ? matches any character, this means that ${var#??} removes the first two chars from the var $var.
$ var="hello"
$ echo ${var#??}
llo
So with IFS=${IFS#??} you are resetting IFS to its value after removing its two first chars.
To loop through the words in a /-delimited string, you can store the splitted string into an array and then loop through it:
$ IFS="/" read -r -a myarray <<< "home/user/bin/etc/something"
$ for w in "${array[#]}"; do echo "-- $w"; done
-- home
-- user
-- bin
-- etc
-- something