sed not working in read while loop - bash

I am using read to make some file in a while loop. The code is:
while read var val
do
sed -i 's/$var/$val/g' Hhh300_4L_gen.sh
echo $var $val
done < "Hhh300_4L_config.txt"
Where in Hhh300_4L_config.txt, there is a line, for instance,
PROCESSNAME Hhh;
and in Hhh300_4L_gen.sh, there is one element: PROCESSNAME. So if it works, PROCESSNAME in Hhh300_4L_gen.sh should be replaced by Hhh. But it doesn't. However the output of echo prints correctly.

Variables are not expanded inside single quotes. If you want var and val to expand you need double quotes (and make sure they don't have the sed separator, here / in them):
sed -i "s/$var/$val/g" Hhh300_4L_gen.sh
though if you're modifying a shell script (as I'm guessing might be from the .sh) there are probably better ways to do it, like having your .txt file store things as var=val instead of with white space separating them, then just source it from the script.

Related

Concatenating a string inside a while loop in bash

I am trying to write a program in bash which takes as input argument a file name and then takes every line of that file and appends it to a string. At the end, I want to print this string. This is what I wrote (consider that the program name is concat.sh):
#!/bin/bash
FILE=$1
STR=""
cat $FILE | while read USER; do
STR="${STR}${USER}"
done
echo "$STR"
And I have the file stuff.txt, which has the following:
a
b
c
And after I run ./concat.sh stuff.txt I get the empty string. I tried multiple variations of that concatenation, with space, without space (like above), with newline, etc. Still doesn't work. If I try to simply print each line, it works. So if I simply add the line echo "$USER" inside the loop I get the correct output, i.e., a, b, c (each on a different line). But that string concatenation still doesn't work. It doesn't work even if I stop using that $USER variable in the concatenation. So if I do something like STR="${STR} abc" the characters abc are surprisingly not concatenated to STR. But if I take this outside of the while loop it actually works (so if I comment the while loop and simply do STR="${STR} abc" I will get abc in the string STR). I am a newbie in bash and this looks like really weird behaviour to me and have no idea what is going on/how to fix it.
Just do not use cat - ie. do not use pipe. And do not use USER.
while read var; do
str="${str}${var}"
done < "$file"
Do not use upper case variables in your scripts. USER is variable set by bash to the name of current user.
Check scripts with http://shellcheck.net
Read https://mywiki.wooledge.org/BashFAQ/024
Read https://mywiki.wooledge.org/BashFAQ/001
Do not uselessly use cat.
In bash you can also use str+="$var".
Quote variable expansions.

How do I use sed to store a variable?

I am trying to use sed to use as input for a variable. The user will choose from a list of files that have numbers before each to identify individual files. Then they choose a number corresponding to a name. I need to get the name of that file. My code is:
for entry in *; do
((i++))
echo "$i) $entry: "
done
echo What file # do you want to choose?:
read filenum
fileName=$(./myscript.sh | sed -n "${filenum}p")
echo $fileName ###this is to see if anything goes into fileName. nothing is ever output
echo What do you want to do with $fileName?
Ideally I would use () instead of the backtick but I can't seem to figure out how. I've looked at the links below, but can't get those ideas to work. I believe a problem may be that I am trying to include the filenum variable inside my sed.
https://www.linuxquestions.org/questions/linux-newbie-8/storing-output-of-sed-in-a-variable-in-shell-script-499997/
Store output of sed into a variable
Don't put backticks around $filenum. That will try to execute the contents of $filenum as a command. Put variables inside double quotes.
And if you do want to nest a backtick expression inside another set of backticks, you have to escape them. That's where $() becomes useful -- they nest without any hassle.
When you use sed -n, you need to use the p command to print the lines that you want to show in the output.
fileName=$(sed -n "${filenum}p" myscript.sh)
This will put the contents of line $filenum of myscript.sh in the variable.
If you actually wanted to execute myscript.sh and print the selected line of its output, you need to pipe to sed:
fileName=$(./myscript.sh | sed -n "${filenum}p")

Bash remove every occurrence after first in string

I'm trying to remove everything after a specific_string in a path string in Bash. I've tried using sed to no avail so far.
variable="specific_string"
input_string="/path/to/some/specific_string/specific_string.something/specific_string.something-else"
output=$(sed 's/$variable//' $input_string)
Output should be "/path/to/some/specific_string/"
Would be better if I didn't have to use commands such as sed!
The Problems
There are many problems
Variables are not evaluated inside single quotes. 's/$variable//' will be treated as a literal string, which does not contain specific_string
sed can modify text from files or STDIN, but not text given via parameters. With sed 's/...//' $input_string the /path/to/some/specific_string/.../file is opened and its content is read, instead of the path itself.
s/string// deletes only string, not the words afterwards.
Also remember to double quote your variables. cmd $variable is dangerous if the variable contains spaces. cmd "$variable" is safe.
Sed Solution
output="$(sed "s/$variable.*/$variable/" <<< "$input_string")"
GNU Grep Solution
output="$(grep -Po "^.*?$variable" <<< "$input_string")"
Pure Bash Solution
output="${input_string%%$variable*}$variable"
If you want to remove everything after "specific_string" it will remove the "/" also as it does with the following example:
output=$(echo $input_string|sed "s/${variable}.*$/${variable}/")
try with simple sed:
variable="specific_string"
input_string="/path/to/some/specific_string/specific_string.something/specific_string.something-else"
output=$(echo "$input_string" | sed "s/\(.*$variable\/\).*/\1/")
Output of variable output will be as follows.
echo $output
/path/to/some/specific_string/

I want to concat a character before and after a word in bash

I have declared a variable in a file like this:
export psw=text
And I would like to concat the ' single character before and after the value of the variable. I mean, I want to replace the value for something like this:
export psw='text'
How can I get that done?
I want to do it though a command. I don't want to do it manually.
Simple and easy with Perl
perl -i -lpe 's/\=(\w+)$/='"'\\1'"'/' your-file
-i save in-palce
output
export psw='text'
how it works
s/.../ this part matches want you wnat
/.../ this part is for substitution that part that you have matched already.
So in the first step you match =(\w+)$
and equal sign and a word and it should be end of the line. Okay after that you change this part to
/='"'\\1'"'
that means put an equal singe and a single quote that what that matched by match group operator () and then another single quote.
So it matches: =text
then substitute it with ='text'
-i is for save the result
-p is for printing to the screen + a while loop
-l put a new line
-e a temporary program.
Just play with it without -i and then you little by little realize how it works.
NOTE
'"' is just for escape the single quote in bash.
\\1 as well this one
This sed command will do your job:
sed -i.bak -E "s/(export[ \t]+[[:alnum:]]+=)([^']+)/\1'\2'/" file
The above expression would only add the single quotes when they are not there.
if you prefer the echo approach, you could also do it this way
pwd=text
export psw=$(echo "\'$pwd\'")
\ are needed to escape the single quotes so it'll be part of the string and you assign the output of echo of you variable with added quotes to itself

bash grep variable as pattern

I don't usually work in bash but grep could be a really fast solution in this case. I have read a lot of questions on grep and variable assignment in bash yet I do not see the error. I have tried several flavours of double quotes around $pattern, used `...`` or $(...) but nothing worked.
So here's what I try to do:
I have two files. The first contains several names. Each of them I want to use as a pattern for grep in order to search them in another file. Therefore I loop through the lines of the first file and assign the name to the variable pattern.
This step works as the variable is printed out properly.
But somehow grep does not recognize/interpret the variable. When I substitute "$pattern" with an actual name everything is fine as well. Therefore I don't think the variable assignment has a problem but the interpretation of "$pattern" as the string it should represent.
Any help is greatly appreciated!
#!/bin/bash
while IFS='' read -r line || [[ -n $line ]]; do
a=( $line )
pattern="${a[2]}"
echo "Text read from file: $pattern"
var=$(grep "$pattern" 9606.protein.aliases.v10.txt)
echo "Matched Line in Alias is: $var"
done < "$1"
> bash match_Uniprot_StringDB.sh ~/Chromatin_Computation/.../KDM.protein.tb
output:
Text read from file: "UBE2B"
Matched Line in Alias is:
Text read from file: "UTY"
Matched Line in Alias is:
EDIT
The solution drvtiny suggested works. It is necessary to get rid of the double quotes to match the string. Adding the following lines makes the script work.
pattern="${pattern#\"}"
pattern="${pattern%\"}"
Please, look at "-f FILE" option in man grep.
I advise that this option do exactly what you need without any bash loops or such other "hacks" :)
And yes, according to the output of your code, you read pattern including double quotes literally. In other words, you read from file ~/Chromatin_Computation/.../KDM.protein.tb this string:
"UBE2B"
But not
UBE2B
as you probably expect.
Maybe you need to remove double quotes on the boundaries of your $pattern?
Try to do this after reading pattern:
pattern=${pattern#\"}
pattern=${pattern%\"}

Resources