How to evaluate hostname? - sed

I need to insert: -name api#$hostname at the top of test_file
How would I evaluate hostname ?
I tried:
sed -i '1s/^/-name api#$hostname \n/' test_file
But the result was:
-name api#$hostname
Any idea?

Variables aren't expanded in single quotes. Try double quotes:
sed -i "1s/^/-name api#$hostname \n/" test_file
Check also the i (insert) command, it's easier than s/// for these simple situations.
sed -i "1i-name api#$hostname" test_file
You need to assign a value to the variable first:
hostname=$(hostname)

Related

Escaping a variable with special characters within sed - comment and uncomment an arbitrary line of source code

I need to comment out a line in a crontab file through a script, so it contains directories, spaces and symbols. This specific line is stored in a variable and I am starting to get mixed up on how to escape the variable. Since the line changes on a regular basis I dont want any escaping in there. I don't want to simply add # in front of it, since I also need to switch it around and replace the line again with the original without the #.
So the goal is to replace $line with #$line (comment) with the possibility to do it the other way around (uncomment).
So I have a variable:
line="* * * hello/this/line & /still/this/line"
This is a line that occurs in a file, file.txt. Wich needs to get comment out.
First try:
sed -i "s/^${line}/#${line}/" file.txt
Second try:
sed -i 's|'${line}'|'"#${line}"'|g' file.txt
choroba's helpful answer shows an effective solution using perl.
sed solution
If you want to use sed, you must use a separate sed command just to escape the $line variable value, because sed has no built-in way to escape strings for use as literals in a regex context:
lineEscaped=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<<"$line") # escape $line for use in regex
sed -i "s/^$lineEscaped\$/#&/" file.txt # Note the \$ to escape the end-of-line anchor $
With BSD/macOS sed, use -i '' instead of just -i for in-place updating without backup.
And the reverse (un-commenting):
sed -i "s/^#\($lineEscaped\)\$/\1/" file.txt
See this answer of mine for an explanation of the sed command used for escaping, which should work with any input string.
Also note how variable $lineEscaped is only referenced once, in the regex portion of the s command, whereas the substitution-string portion simply references what the regex matched (which avoids the need to escape the variable again, using different rules):
& in the substitution string represents the entire match, and \1 the first capture group (parenthesized subexpression, \(...\)).
For simplicity, the second sed command uses double quotes in order to embed the value of shell variable $lineEscaped in the sed script, but it is generally preferable to use single-quoted scripts so as to avoid confusion between what the shell interprets up front vs. what sed ends up seeing.
For instance, $ is special to both the shell and sed, and in the above script the end-of-line anchor $ in the sed regex must therefore be escaped as \$ to prevent the shell from interpreting it.
One way to avoid confusion is to selectively splice double-quoted shell-variable references into the otherwise single-quoted script:
sed -i 's/^'"$lineEscaped"'$/#&/' file.txt
awk solution
awk offers literal string matching, which obviates the need for escaping:
awk -v line="$line" '$0 == line { $0 = "#" $0 } 1' file.txt > $$.tmp && mv $$.tmp file.txt
If you have GNU Awk v4.1+, you can use -i inplace for in-place updating.
And the reverse (un-commenting):
awk -v line="#$line" '$0 == line { $0 = substr($0, 2) } 1' file.txt > $$.tmp &&
mv $$.tmp file.txt
Perl has ways to do the quoting/escaping for you:
line=$line perl -i~ -pe '$regex = quotemeta $ENV{line}; s/^$regex/#$ENV{line}/' -- input.txt

sed does not print line when using address format

sed does not print line 1 to line 545.The result of the following command is empty resultfile.txt. Can someone say what should be the correct command?
RESULT=545;sed -n '1, $RESULT p' Configuration.txt > resultfile.txt
The above is not a sed issue, but rather a shell issue: you used single quotes, which inhibit variable expansion.
$ echo '$PWD'
$PWD
$ echo $PWD
/tmp
$ echo "$PWD"
/tmp
Either no quotes at all, or double quotes, allows the shell to expand variables. (No quotes at all means the shell expands everything; double quotes inhibit globbing, redirections, and such.)
Use double quotes so that $RESULT is expanded:
RESULT=545;sed -n "1, $RESULT p" Configuration.txt > resultfile.txt
By the way, the following would be simpler:
head -$RESULT Configuration.txt > resultfile.txt
And, if your file is big, this will quit as soon as you reach line 545 and be more efficient:
sed ${RESULT}q Configuration.txt > resultfile.txt

sed replacing content with star

I can use sed from tcsh like this:
set a = `echo $a | sed -e 's_old_new_'`
Everything is fine, but when I want to do this:
set a = `echo $a | sed -e 's_old_*new_'`
I can see "set: No match." . How I can escape this star?
I don't know much about tcsh but few experiments suggest the set assigning a variable attempts to expand right side *. Here is something that may help:
set a="`echo '2e2' | sed -e 's_e_*_'`"
echo "$a"
2*2
echo $a
echo: No match.
So double quote around back quotes and it will work.
set a = "`echo $a | sed -e 's_old_new_'`"
A command substitution (` or $(..)) that is not enclosed in double quotes is subject to filename expansion (aka 'globbing') and word splitting.
Normally, a variable assignment would suppress filename expansion and word splitting of the RHS (even without double quotes), but apparently not filename expansion in the case of command substitution.
Here's a test I ran for reference purposes:
$ touch randomfile
$ a="*file"
$ var_expand=$a
$ echo "$var_expand"
*file
$
$ cmd_subst=$(echo '*file')
$ echo "$cmd_subst"
randomfile
So I guess it's good practice to always double quote the command substitution when you are assigning to variable.
safe="$(cmd)"
Note: This is tested in Bash but I think tcsh exhibits a similar behavior in this respect.

Shell variables in sed script [duplicate]

This question already has answers here:
sed substitution with Bash variables
(6 answers)
Closed 24 days ago.
The sed command works as expected at the command prompt, but does not work in a shell script.
new_db_name=`echo "$new_db_name" | sed 's/$replace_string/$replace_with/'`
Why is that, and how can I fix it?
Use double quotes for the sed expression.
new_db_name=$(echo "$new_db_name" | sed "s/$replace_string/$replace_with/")
If you use bash, this should work:
new_db_name=${new_db_name/$replace_string/$replace_with}
This worked for me in using env arguments.
export a=foo
export b=bar
echo a/b | sed 's/a/'$b'/'
bar/b
Guys: I used the following to pass bash variables to a function in a bash script using sed. I.e., I passed bash variables to a sed command.
#!/bin/bash
function solveOffendingKey(){
echo "We will delete the offending key in file: $2, in line: $1"
sleep 5
eval "sed -i '$1d' $2"
}
line='4'
file=~/ivan/known_hosts
solveOffendingKey $number $file
Kind regards!
depending on how your variables are initialized, you are better off using brackets:
new_db_name=`echo "$new_db_name" | sed "s/${replace_string}`/${replace_with}/"
Maybe I'm missing something but new_db_name=echo "$new_db_name" doesn't make sense here. $new_db_name is empty so you're echoing a null result, and then the output of the sed command. To capture stdout as a variable, backticks aren't recommended anymore. Capture output surrounded by $().
new_db_name=$(sed "s/${replace_string}/${replace_with}/")
Take the following example:
replace_string="replace_me"
replace_with=$(cat replace_file.txt | grep "replacement_line:" | awk FS" '{print $1}')
Where replace_file.txt could look something like:
old_string: something_old
I like cats
replacement_line: "shiny_new_db"
Just having the variable in the sed expresion $replace_with won't work. bash doesn't have enough context to escape the variable expression. ${replace_with} tells bash to explicitly use the contents of the command issued by the variable.

How do I push `sed` matches to the shell call in the replacement pattern?

I need to replace several URLs in a text file with some content dependent on the URL itself. Let's say for simplicity it's the first line of the document at the URL.
What I'm trying is this:
sed "s/^URL=\(.*\)/TITLE=$(curl -s \1 | head -n 1)/" file.txt
This doesn't work, since \1 is not set. However, the shell is getting called. Can I somehow push the sed match variables to that subprocess?
The accept answer is just plain wrong. Proof:
Make an executable script foo.sh:
#! /bin/bash
echo $* 1>&2
Now run it:
$ echo foo | sed -e "s/\\(foo\\)/$(./foo.sh \\1)/"
\1
$
The $(...) is expanded before sed is run.
So you are trying to call an external command from inside the replacement pattern of a sed substitution. I dont' think it can be done, the $... inside a pattern just allows you to use an already existent (constant) shell variable.
I'd go with Perl, see the /e option in the search-replace operator (s/.../.../e).
UPDATE: I was wrong, sed plays nicely with the shell, and it allows you do to that. But, then, the backlash in \1 should be escaped. Try instead:
sed "s/^URL=\(.*\)/TITLE=$(curl -s \\1 | head -n 1)/" file.txt
Try this:
sed "s/^URL=\(.*\)/\1/" file.txt | while read url; do sed "s#URL=\($url\)#TITLE=$(curl -s $url | head -n 1)#" file.txt; done
If there are duplicate URLs in the original file, then there will be n^2 of them in the output. The # as a delimiter depends on the URLs not including that character.
Late reply, but making sure people don't get thrown off by the answers here -- this can be done in gnu sed using the e command. The following, for example, decrements a number at the beginning of a line:
echo "444 foo" | sed "s/\([0-9]*\)\(.*\)/expr \1 - 1 | tr -d '\n'; echo \"\2\";/e"
will produce:
443 foo