I am trying to modify the variables of one shell script, using another script. This is what I have so far:
script1.sh
#!/bin/bash
var=123.45.67.890
script2.sh
#!/bin/bash
currVar=000.00.00.000
. /./script1.sh
var=$currVar
I understand that I am not modifying Script 1 here, but simply temporarily modifying var. How can I modify this var in script 1, via script 2?
Solution
. /./script1.sh
echo $var | sed "s/$var/$currVar/g" /./script1.sh > "temp.txt" && mv temp.txt /./script1.sh
Just use sed in 2nd script (script2.sh) as
currVar="000.00.00.000"
sed -r -i.bak "s/var=([[:graph:]]+)/var=$currVar/" script1.sh
var=000.00.00.000
where [[:graph:]] is a character class for [[:alnum:]] & [[:punct:]] to match values for var with printable characters/meta-characters.
Since you mentioned it is a proper IP address, use a proper regEx as
sed -r "s/(\b[0-9]{1,3}\.){3}[0-9]{1,3}\b/$currVar/" script1.sh
var=000.00.00.000
(\b[0-9]{1,3}\.){3}[0-9]{1,3} implies match 3 groups consisting of digits from 0-9, which each group could have from 1-3 digits each, preceded by a dot . and the 4th group also the same as the last. Remember the each group I am mentioning represents an IP octet
Normally, variables in scripts have local scope. If you export the variable, you can extend this scope to include any child processes. However, it looks like you might want to use the modified value when script1.sh runs. If that is the case, you can use the new var value as an input to script1.sh when you run it.
if [[ -z "$1" ]];
then
var=$1
else
var=123.45.67.890
This will check if you gave any parameters when you ran script1.sh, and if you did, then it should set your var equal to this value instead of the default IP.
Related
I want to make a few configuration files (for homeassistant) that are very similar to each other. I am aiming to use a template file as the base and put in a few substitution strings at the top of the file and use a bash script to read the substitutions and run sed with the applicable strings.
i.e.
# substitutions
# room = living_room
# switch = hallway_motion
# delay = 3
automations:
foo......
.........
entity_id: $switch
When I run the script it will look for any line beginning with a # that has a word (key) and then an = and another word (maybe string) (value) and replace anywhere that key with a $ in front is in the rest of the file.
Like what is done by esphome. https://esphome.io/guides/configuration-types.html#substitutions
I am getting stuck at finding the "keys" in the file. How can I script this so it can find all the "keys" recursively?
Or is there something that does this, or something similar, out there already?
You can do this with sed in two stages. The first stage will generate a second stage sed script to fill in your template. I'd make a small adjustment to your syntax and recommend that you require curly braces around your variable name. In other words, write your variable expansions like this:
# foo = bar
myentry: ${foo}
This makes it easier to avoid pitfalls when you have one variable name that's a prefix of another (e.g., foo and foobar).
#!/bin/bash
in="$1"
stage2=$(mktemp)
trap 'rm -f "$stage2"' EXIT
sed -n -e 's,^# \([[:alnum:]_]\+\) = \([^#]\+\),s#\${\1}#\2#g,p' "$in" > "$stage2"
sed -f "$stage2" "$in"
Provide a filename as the first argument, and it will print the filled out template on stdout.
This example code is pretty strict about white space on variable definition lines, but that can obviously be adjusted to your liking.
I am using tcsh (contract required, cannot change to bash etc), but am having a problem building up a command based on various conditions for different pieces.
Some names changed to protect the innocent...
If new or old program name, is really chosen earlier on by a preprocessor, and is hardcoded by the time this shell script gets run:
set myCMDline = newProgName
set myCMDlineTmpFile = "/tmp/myCMDlineTmpScriptFile.csh"
set bsubQname = "typical"
set bsubResources = "span[hosts=1]"
set myCMDline = "bsub -q $bsubQname -n 8 -R \"$bsubResources\" $myCMDline"
($myCMDline)
Now, I have tried several variations of the above, all not working for some reason or another. The closest I think I get is a complaint about mismatched double-quotes, even when backspacing them.
When I do an echo of $myCMDline, then that looks OK, but the execution of same must somehow be different...
set bsubResources = '"span[hosts=1]"' #double-quotes inside, single-quotes outside
set myCMDline = "bsub -q $bsubQname -n 8 -R $bsubResources $myCMDline"
.
set bsubResources = "span[hosts=1]" #double-quotes inside, single-quotes outside
set myCMDline = 'bsub -q $bsubQname -n 8 -R "$bsubResources" $myCMDline'
.
set bsubResources = "span[hosts=1]" #double-quotes inside, single-quotes outside
set myCMDline = "bsub -q $bsubQname -n 8 -R '$bsubResources' $myCMDline"
etc.
I have also tried dumping to a separate temp script file to source, but that contains the $variable names, not resolved equivalents as I would prefer, as I am doing set, not setenv, and prefer not to put these into shell vars.
First I could not echo the "#!/bin/csh -f" line, it seems to try and execute that rather than echo redirected into the temp script file, and dies.
rm -f $myCMDlineTmpFile
echo "#!/bin/csh -f > $myCMDlineTmpFile
echo "$myCMDline" >> $myCMDlineTmpFile
($myCMDlineTmpFile)
Then I tried multi-line echo, which is where I am seeing the local variable names go into the file rather than their contents:
/bin/cat > $myCMDlineTmpFile <<EOF
#!/bin/csh -f
$myCMDline
EOF
source $myCMDlineTmpFile
And then I am trying to instead use eval:
eval `echo "$myCMDline &" `
with and without the backticks etc, but complains about unknown variables for the queue name, resources etc.
Adding this echo always looks like what I want to be the commandline, between the >>> and <<<
echo "DEBUG - myCMDline= >>>$myCMDline<<<"
Please help me solve this puzzle...
set myCMDline = "bsub -q $bsubQname -n 8 -R \"$bsubResources\" $myCMDline"
($myCMDline)
This won't work because csh considers this as a single string, so it treats the whole string as one big program name. You have to define an array instead:
set myCMDline = (bsub -q $bsubQname -n 8 -R "$bsubResources" $myCMDline:gaq)
($myCMDline:gaq)
Explanation: The :gaq is a substitution quotes all strings in the list and keeps each list element intact. This is quite similar to "$#" in bash.
This is documented in History Substitution
g Apply the following modifier once to each word.
a (+) Apply the following modifier as many times as possible to a single word. `a' and `g' can be used together to apply a modifier globally. In the current implementation, using the `a' and `s' modifiers together can lead to an infinite loop. For example, `:as/f/ff/' will never terminate. This behavior might change in the future.
q Quote the substituted words, preventing further substitutions.
This is relevant due to the text in variable substitution:
The `:' modifiers described under History substitution, except for `:p', can be applied to the substitutions above. More than one may be used. (+) Braces may be needed to insulate a variable substitution from a literal colon just as with History substitution (q.v.); any modifiers must appear within the braces.
I need to take specific variables from one script and use them in a different script.
Example:
Original script:
VARA=4 # Some description of VARA
VARB=6 # Some description of VARB
SOMEOTHERVAR="Foo"
/call/to/some/program
I want to write a second script that needs VARA and VARB, but not SOMEOTHERVAR or the call to the program.
I can already do:
eval $(grep 'VARA=' origscript.sh)
eval $(grep 'VARB=' origscript.sh)
This seems to work, but when I want to do both, like this, it only sets the first:
eval $(grep 'VAR[AB]=' origscript.sh)
because it seems to concatenate the two lines that grep returns. (Which probably means that the comments save the first assignments.)
Put quotes around it, so that the newlines in the output of grep will not be turned into spaces.
eval "$(grep 'VAR[AB]=' origscript.sh)"
I seem to be not able to get this right
When i run this code I need a variable for the filename later on. How should I do thi?
#!/bin/bash
foo="../../../data/audio/serval-data/wav-16bit-16khz/ytdl/balanced_train/vidzyGjrJfE_rg.wav"
echo $foo
echo "${foo%.*}" | cut -d "/" -f10;
# fid=vidzyGjrJfE_rg
I want to use new variable fid to have value "vidzyGjrJfE_rg"
You can use a couple of iterations of shell builtins for this (see #melpomene's answer) but FYI that's exactly what basename exists to do:
$ foo="../../../data/audio/serval-data/wav-16bit-16khz/ytdl/balanced_train/vidzyGjrJfE_rg.wav"
$ fid=$(basename "$foo" '.wav')
$ echo "$fid"
vidzyGjrJfE_rg
You can do it like this:
fid="${foo##*/}"
fid="${fid%.*}"
Just as % removes a matching suffix from a variable, # removes a prefix (and ## removes the longest matching prefix). See https://www.gnu.org/software/bash/manual/bashref.html#Shell-Parameter-Expansion.
I am trying to extract the number of lines from a file, and then use it in a variable. However, it keeps passing the file name, not the number of lines. I read through this question, but the examples are not working.
for i in $BASE/$TEMPLATE_DIR-$i$PRUNING_METHOD/*.txt
do
NUM_LINES=$(wc -l < $i)
echo $NUM_LINES
UPLOAD_CMD="sh sshpass_setup_verification.sh $EXP_ID-$i$PRUNING_METHOD__$NUM_LINES__features";
echo "$UPLOAD_CMD"
break 1;
done
Prints:
15 #the correct number of lines
sh sshpass_setup_verification.sh di-60sec-max/TemplateUser1.txt #where TemplateUser1.txt is the name of the file
Should print:
15
sh sshpass_setup_verification.sh di-60sec-max__15__features
A summary of what people are telling you in the comments:
for i in "${BASE}/${TEMPLATE_DIR}-${i}${PRUNING_METHOD}"/*.txt
do
num_lines=$(wc -l < "$i")
echo "$num_lines"
upload_cmd="sh sshpass_setup_verification.sh ${EXP_ID}-${i}${PRUNING_METHOD}__${num_lines}__features"
echo "$upload_cmd"
break
done
The key thing here is using double quotes around your parameter expansions and curly braces to disambiguate in situations where characters such as _ could be interpreted as part of a variable name but shouldn't. I've added them in several places where they aren't strictly needed but they do no harm.
I've also changed your variable names to lowercase. This will help you one day when you decide to have a variable called PATH and suddenly all your commands stop working.