how do I escape ' in sed? - sed

I do not understand what I am doing wrong here:
$ cat fixnames.sh
#!/bin/sh
for i in *mp3
do
j=`echo $i | sed -e's/ /_/g'`
j=`echo $j | sed -e's/_(...)_/_/g'`
j=`echo $j | sed -e's/\'//g'`
echo $j
done
$ ./fixnames.sh
./fixnames.sh: 1: Syntax error: Unterminated quoted string
I guess the line /bin/sh does not like is ...
j=`echo $j | sed -e's/\'//g'`
... so how am I suppose to remove ' ?

It's shell not sed giving trouble. You can't escape single quotes in a single-quoted string.
for i in *mp3
do
j=$(echo $i | sed -e 's/ /_/g' -e 's/_(...)_/_/g' -e "s/'//g")
echo $j
done
In this context, it is sufficient to use double quotes around the expression. Other times, you need to be more careful with the double quotes (stray $ need escaping, etc), or you use the canonical sequence '\'' to embed a single quote in a single quoted string:
-e 's/'\''//g'
The '\'' sequence stops the current single quoted string, inserts an escaped single quote (effectively just a single quote), and resumes the single quoted string.
Note that I combined the 3 invocations of sed into one; I like the -e option but many people would use semicolons to separate the three commands. Also note the use of $(...) in preference to back-quotes.

Related

Use sed to replace every character by itself followed by $n times a char?

I'm trying to run the command below to replace every char in DECEMBER by itself followed by $n question marks. I tried both escaping {$n} like so {$n} and leaving it as is. Yet my output just keeps being D?{$n}E?{$n}... Is it just not possible to do this with a sed?
How should i got about this.
echo 'DECEMBER' > a.txt
sed -i "s%\(.\)%\1\(?\){$n}%g" a.txt
cat a.txt
This might work for you (GNU sed):
n=5
sed -E ':a;s/[^\n]/&\n/g;x;s/^/x/;/x{'"$n"'}/{z;x;y/\n/?/;b};x;ba' file
Append a newline to each non-newline character in a line $n times then replace all newlines by the intended character ?.
N.B. The newline is chosen as the initial substitute character as it is not possible for it to be within a line (sed uses newlines to separate lines) and if the final substitution character already exists within the current line, the substitutions are correct.
Range (also, interval or limiting quantifiers), like {3} / {3,} / {3,6}, are part of regex, and not replacement patterns.
You can use
sed -i "s/./&$(for i in {1..7}; do echo -n '?'; done)/g" a.txt
See the online demo:
#!/bin/bash
sed "s/./&$(for i in {1..7}; do echo -n '?'; done)/g" <<< "DECEMBER"
# => D???????E???????C???????E???????M???????B???????E???????R???????
Here, . matches any char, and & in the replacement pattern puts it back and $(for i in {1..7}; do echo -n '?'; done) adds seven question marks right after it.
This one-liner should do the trick:
sed 's/./&'$(printf '%*s' "$n" '' | tr ' ' '?')'/g' a.txt
with the assumption that $n expands to a positive integer and the command is executed in a POSIX shell.
Efficiently using any awk in any shell on every Unix box after setting n=2:
$ awk -v n="$n" '
BEGIN {
new = sprintf("%*s",n,"")
gsub(/./,"?",new)
}
{
gsub(/./,"&"new)
print
}
' a.txt
D??E??C??E??M??B??E??R??
To make the changes "inplace" use GNU awk with -i inplace just like GNU sed has -i.
Caveat - if the character you want to use in the replacement text is & then you'd need to use gsub(/./,"\\\\\\&",new) in the BEGIN section to make it is treated as literal instead of a backreference metachar. You'd have that issue and more (e.g. handling \1 or /) with any sed solution and any solution that uses double quotes around the script would have more issues with handling $s and the solutions that have a shell script expanding unquoted would have even more issues with globbing chars.

sed replace using string containing backslashes

I need to replace text in a file with a Windows-style directory path containing backslash (REVERSE SOLIDUS) characters. I am already using an alternative expression delimiter. The backslashes appear to be treated as escape characters.
How can I keep the backslashes in the output?
$ echo DIR=foobar | sed -e "s#DIR=.*#$(cygpath -w $(pwd))#"
C:gwin64homelit
The desired output is:
C:\cygwin64\home\lit
You'll have to escape metacharacters in sed replacement pattern. Fortunately, there are only three of those: &, \, and a delimiter / (see this question and this). In your case, since you're using # for delimiter, you'll have to escape # instead of /.
You can create a helper shell function (like here):
escapeSubst() { sed 's/[&#\]/\\&/g'; }
and then pass your string through it before giving it to sed, like this:
$ echo DIR=foobar | sed -e "s#DIR=.*#$(cygpath -w $(pwd) | escapeSubst)#"
C:\cygwin64\home\lit

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

How to escape characters in sed

I'm trying to remove some text from multiple files using sed. This is the text I'm trying to delete:
\once override TupletBracket #'stencil = ##f
I've tried this line in sed but I can't get it to work:
sed -i '' -e 's/\\once \\override TupletBracket #'stencil = ##f//g' *ily
I've tried escaping the # symbols, the ' and the = but still no joy. Could anyone please point me in the right direction?
I think it's better to use single quotes here rather than double quotes to avoid the extra \s and other possible expansions (e.g. variables). Where you want a literal single quote, you close the quotation, add \', and then start a new quotation for the remainder.
$ cat in
before \once override TupletBracket #'stencil = ##f after
$ sed 's/\\once override TupletBracket #'\''stencil = ##f//g' in
before after
you can't use ' directly inside sed command that is quoted using '. Use a double quotes instead and to match \ you'll need to use \\\ to have \\ i.e \.
$ sed "s/\\\once override TupletBracket #'stencil = ##f//g"
\once override TupletBracket #'stencil = ##f
hello \once override TupletBracket #'stencil = ##f xyz
hello xyz
$
# and = are not RE metacharacters nor do they have any other special meaning to sed within a regexp (= does outside of a regexp) unless the regexp is delimited with one of them so there's no reason to escape them in your script. ' only has significance if the whole script is delimited with 's since in shell no script that's delimited by a given character can include that character. So here's your choices:
$ echo "seab'cd" | sed 's/b'\''c/foo/'
seafood
$ echo "seab'cd" | sed "s/b'c/foo/"
seafood
Note that if you use the second (double quotes) version then you're allowing shell variables to expand inside the script and would require double-backslashes to escape chars.
I expected using the octal representation of a ' (i.e. \047) would work too like it does in awk:
$ echo "seab'cd" | awk '{sub(/b\047c/,"foo")}1'
seafood
but it didn't:
$ echo "seab'cd" | sed 's/b\047c/foo/'
seab'cd
and I suspect that's because sed is treating \0 as a backreference. It does work with the hex representation:
$ echo "seab'cd" | sed 's/b\x27c/foo/'
seafood
but that's dangerous and should be avoided (see http://awk.freeshell.org/PrintASingleQuote).

Escaping single quotes

I want to replace the double quotes in the sed command in the following example with single quotes.
set new_string to do shell script "echo " & quoted form of list_string & " | sed -e 's/$/\"/' -e 's/^/\"/' -e 's/^/+/'"
However if I replace the double quotes with single quotes I get an error, is there a way to escape single quotes?
I'm no sed ninja, so any hints on how to go about this is highly appreciated.
if you want to replace " with ' using sed:
sed 's/"/\x27/g' yourFile
\x27 - single
\x22 - double
it could make code looks cleaner, and with less escape.
see the test:
kent$ cat quote.tmp
""""""
kent$ sed 's/"/\x27/g' quote.tmp
''''''
fYou had a quotation fault. Just to replace double quotes for single quotes, this is enough
set list_string to "This program said: \"Hello World!\""
set new_string to do shell script "/bin/echo -n " & quoted form of list_string & " | sed -e 's/\"/'\\''/g'"
Explaining 's/\"/'\''/g'
The \\ and \" is needed in the applescript environment and will be in the shell just \ and ". So what's entering the shell is 's/"/'\''/g'. Then what's with all the quotes? A very common mistake is thinking that quotations on the command line works the same as in programming. A single quote turns substitution on or off. So the first single quote turns substitution off which mean the next characters will be interpreted as text and has no special meanings (including the escape character). So to escape a single quote we'll need to turn the substitution on, then we can escape a single quote and turn the substitution off again.
You need to be careful about which quotes are being parsed by sed and which are being parsed by the environment invoking sed. Normal invocations of sed come from shell scripts, but (based on your tag) it appears that you're calling it from an AppleScript.
From a shell script you would say
| sed -e 's/$/'\''/' -e 's/^/'\''/' -e 's/^/+/'
But I don't know if sh-style escaping rules are in effect for you or whether you need to additionally escape the \