Bash curly expansion with space in the string - bash

I would like to use curly expansion to save some typing.
My desire expansion is:
-e uncore_imc0/cas_count_read/ -e uncore_imc1/cas_count_read/ -e uncore_imc2/cas_count_read/ -e uncore_imc3/cas_count_read/ -e uncore_imc4/cas_count_read/ -e uncore_imc5/cas_count_read/ -e uncore_imc6/cas_count_read/ -e uncore_imc7/cas_count_read/
I've tried:
-e uncore_imc{0..7}/cas_count_read/
but this only expand to (with -e only in the beginning)
-e uncore_imc0/cas_count_read/ uncore_imc1/cas_count_read/ uncore_imc2/cas_count_read/ uncore_imc3/cas_count_read/ uncore_imc4/cas_count_read/ uncore_imc5/cas_count_read/ uncore_imc6/cas_count_read/ uncore_imc7/cas_count_read/
If I tried:
{-e, uncore_imc{0..7}/cas_count_read/}
or
"-e uncore_imc{0..7}/cas_count_read/"
Neither would expand.

You can use printf:
printf -- '-e uncore_imc%d/cas_count_read/ ' {0..7}; echo
-e uncore_imc0/cas_count_read/ -e uncore_imc1/cas_count_read/ -e uncore_imc2/cas_count_read/ -e uncore_imc3/cas_count_read/ -e uncore_imc4/cas_count_read/ -e uncore_imc5/cas_count_read/ -e uncore_imc6/cas_count_read/ -e uncore_imc7/cas_count_read/
You can also store this expansion in a variable like this:
printf -v arg -- '-e uncore_imc%d/cas_count_read/ ' {0..7}

If this is really for a script, don't use brace expansion. You only have to write the code once, so readability should be a higher priority. Create an array instead:
opts=()
for((i=0; i < 8; i++)); do
opts+=(-e "uncore_imc$i/cas_count_read/")
done
someCommand "${opts[#]}"

Related

Use a variable as replacement in bash sed command

I am using the sed command on Ubuntu to replace content.
This initial command comes from here.
sed -i '$ s/$/ /replacement/' "$DIR./result/doc.md"
However, as you can see, I have a slash in the replacement. The slash causes the command to throw:
sed: -e expression #1, char 9: unknown option to `s'
Moreover, my replacement is stored in a variable.
So the following will not work because of the slash:
sed -i "$ s/$/ $1/" "$DIR./result/doc.md"
As stated here and in duplicate, I should use another delimiter. If I try with #:
sed -i "$ s#$# $1#" "$DIR./result/doc.md"
It gives the error:
sed: -e expression #1, char 42: unterminated `s' command
My question is:
How can I use a variable in this command as well as other delimiter than / ?
Don't use sed here; perl and awk allow more robust approaches.
sed doesn't allow variables to be passed out-of-band from code, so they always need to be escaped. Use a language without that limitation, and you have code that always works, no matter what characters your data contains.
The Short Answer: Using perl
The below is taken from BashFAQ #21:
inplace_replace() {
local search=$1; shift; local replace=$1; shift
in="$search" out="$replace" perl -pi -e 's/\Q$ENV{"in"}/$ENV{"out"}/g' "$#"
}
inplace_replace '#' "replacement" "$DIR/result/doc.md"
The Longer Answer: Using awk
...or, using awk to do a streaming replacement, and a shell function to make that file replacement instead:
# usage as in: echo "in should instead be out" | gsub_literal "in" "out"
gsub_literal() {
local search=$1 replace=$2
awk -v s="${search//\\/\\\\}" -v r="${rep//\\/\\\\}" 'BEGIN {l=length(s)} {o="";while (i=index($0, s)) {o=o substr($0,1,i-1) r; $0=substr($0,i+l)} print o $0}'
}
# usage as in: inplace_replace "in" "out" /path/to/file1 /path/to/file2 ...
inplace_replace() {
local search=$1 replace=$2 retval=0; shift; shift
for file; do
tempfile=$(mktemp "$file.XXXXXX") || { retval |= $?; continue; }
if gsub_literal "$search" "$replace" <"$file" >"$tempfile"; then
mv -- "$tempfile" "$file" || (( retval |= $? ))
else
rm -f -- "$tempfile" || (( retval |= $? ))
fi
done
}
TL;DR:
Try:
sed -i '$ s#$# '"$1"'#' "$DIR./result/doc.md"
Long version:
Let's start with your original code:
sed -i '$ s/$/ /replacement/' "$DIR./result/doc.md"
And let's compare it to the code you referenced:
sed -i '$ s/$/abc/' file.txt
We can see that they don't exactly match up. I see that you've correctly made this substitution:
file.txt --> "$DIR./result/doc.md"
That looks fine (although I do have my doubts about the . after $DIR ). However, the other substitution doesn't look great:
abc --> /replacement
You actually introduced another delimeter /. However, if we replace the delimiters with '#' we get this:
sed -i '$ s#$# /replacement#' "$DIR./result/doc.md"
I think that the above is perfectly valid in sed/bash. The $# will not be replaced by the shell because it is single quoted. The $DIR variable will be interpolated by the shell because it is double quoted.
Looking at one of your attempts:
sed -i "$ s#$# $1#" "$DIR./result/doc.md"
You will have problems due to the shell interpolation of $# in the double quotes. Let's correct that by replacing with single quotes (but leaving $1 unquoted):
sed -i '$ s#$# '"$1"'#' "$DIR./result/doc.md"
Notice the '"$1"'. I had to surround $1 with '' to basically unescape the surrounding single quotes. But then I surrounded the $1 with double quotes so we could protect the string from white spaces.
Use shell parameter expansion to add escapes to the slashes in the variable:
$ cat file
foo
bar
baz
$ set -- ' /repl'
$ sed "s/$/$1/" file
sed: 1: "s/$/ /repl/": bad flag in substitute command: 'r'
$ sed "s/$/${1//\//\\\/}/" file
foo /repl
bar /repl
baz /repl
That is a monstrosity of leaning toothpicks, but it serves to transform this:
sed "s/$/ /repl/"
into
sed "s/$/ \/repl/"
The same technique can be used for whatever you choose as the sed s/// delimiter.

Problem Converting Morse Code to Text using sed

I am kinda new to bash scripting so bear with me.
I have a task to convert text to morse code and vice versa from a given .txt file.
I successfully did the first part but when i try to run the second script to translate from morse to text i get this error:
sed: can't read s/.-/A/g: No such file or directory
S
My original code:
#!/bin/bash
sed 's/.-/A/g' -e 's/-.../B/g' -e 's/-.-./C/g' -e 's/-../D/g' -e 's/./E/g' -e 's/..-./F/g' -e 's/-
-./G/g' -e 's/..../H/g' -e 's/../I/g' -e 's/.---/J/g' -e 's/-.-/K/g' -e 's/.-../L/g' -e 's/--/M/g' -e
's/-./N/g' -e 's/---/O/g' -e 's/.--./P/g' -e 's/--.-/Q/g' -e 's/.-./R/g' -e 's/.../S/g' -e 's/-/T/g'
-e 's/..-/U/g' -e 's/...-/V/g' -e 's/.--/W/g' -e 's/-..-/X/g' -e 's/-.--/Y/g' -e 's/--../Z/g' -e
's/...../1/g' -e 's/....-/2/g' -e 's/...--/3/g' -e 's/....-/4/g' -e 's/...../5/g' -e 's/-..../6/g' -e
's/--.../7/g' -e 's/---../8/g' -e 's/----./9/g' -e 's/-----/0/g' Morse_Text.txt
The Morse_Text.txt file:
-- --- .-. … . -.- --- -.-. .
You have to escape (prepend a \) the . characters, as they have a special meaning in regular expressions.
Like this sed 's/\.-/A/g
Have a look at https://en.wikipedia.org/wiki/Regular_expression
Also you are missing the first -e after your sed command:
░ tamasgal#greybox.local:~
░ 16:37:45 > echo '... --- ...'| sed -e 's/\.\.\./S/g' -e 's/---/O/g'
S O S
Btw. you will hit more issues due to the fact sed works, it will replace parts of other morse codes during the stages.
You should consider including a leading space or new line after each group of morse command.
Here is an example in stages (decoding SOS9):
░ tamasgal#greybox.local:~
░ 16:43:47 > echo '... --- ... ----.'| sed -e 's/\.\.\./S/g'
S --- S ----.
░ tamasgal#greybox.local:~
░ 16:43:56 > echo '... --- ... ----.'| sed -e 's/\.\.\./S/g' -e 's/---/O/g'
S O S O-.
░ tamasgal#greybox.local:~
░ 16:44:00 > echo '... --- ... ----.'| sed -e 's/\.\.\./S/g' -e 's/---/O/g' -e 's/----\./9/g'
S O S O-.
One solution is to include a space or end of line character ($) to each group, like this:
░ tamasgal#greybox.local:~
░ 16:44:02 > echo '... --- ... ----. '| sed -e 's/\.\.\.[ $]/S/g' -e 's/---[ $]/O/g' -e 's/----\.[ $]/9/g'
SOS9
Btw. this is by far an ugly solution, but I'll not do your homework ;)
You have to make sure that the largest matches are replaced first.
It is convenient to store the commands in morse.sed like this:
s/-----/0/g
s/\.\.\.\.\./5/g
s/\.\.\.\.\./1/g
s/\.\.\.\.-/4/g
s/\.\.\.\.-/2/g
s/\.\.\.--/3/g
s/-\.\.\.\./6/g
s/--\.\.\./7/g
s/---\.\./8/g
s/----\./9/g
s/\.\.\.\./H/g
s/\.\.\.-/V/g
s/\.\.-\./F/g
s/\.-\.\./L/g
s/\.--\./P/g
s/\.---/J/g
s/-\.\.\./B/g
s/-\.\.-/X/g
s/-\.-\./C/g
s/-\.--/Y/g
s/--\.\./Z/g
s/--\.-/Q/g
s/\.\.\./S/g
s/\.\.-/U/g
s/\.-\./R/g
s/\.--/W/g
s/-\.\./D/g
s/-\.-/K/g
s/--\./G/g
s/---/O/g
s/\.\./I/g
s/\.-/A/g
s/-\./N/g
s/--/M/g
s/\./E/g
s/-/T/g
Now you can use
sed -Ef morse.sed Morse_Text.txt

in bash linux how to echo escaped text as output of a command

I want to print a text example
'(]\{&}$"\n
the escaped version I have is this:
"'(]\\{&}$\"\\n"
I tried the following:
cat $CLIPBOARD_HISTORY_FILE | sed "$2!d" | sed 's/^.\(.*\).$/\1/'
cat $CLIPBOARD_HISTORY_FILE | sed "$2!d" | sed 's/^.\(.*\).$/\1/' | eval 'stdin=$(cat); echo "$stdin"'
VAR1=$(cat $CLIPBOARD_HISTORY_FILE | sed "$2!d" | sed 's/^.\(.*\).$/\1/')
VAR2="'(]\\{&}\$\"\\n"
VAR3=$VAR1
echo "1 '(]\\{&}\$\"\\n"
echo "2 $VAR1"
echo "3 $VAR2"
echo "4 $VAR3"
echo -e "5 $VAR1"
echo -e "6 $VAR2"
echo -e "7 $VAR3"
$
'(]\\{&}$\"\\n
'(]\\{&}$\"\\n
1 '(]\{&}$"\n
2 '(]\\{&}$\"\\n
3 '(]\{&}$"\n
4 '(]\\{&}$\"\\n
5 '(]\{&}$\"\n
6 '(]\{&}$"
7 '(]\{&}$\"\n
echoing the text directly works, but not if it comes from a command.... what am I not seeing or understanding?
Thanks for the help!
In general, it's best to enclose material in single quotes rather than double quotes; then you only have to worry about single quotes. Thus:
$ x="'"'(]\{&}$"\n'
$ printf "%s\n" "$x"
'(]\{&}$"\n
$ printf "%s\n" "$x" | sed -e "s/'/'\\\\''/g" -e "s/^/'/" -e "s/$/'/"
''\''(]\{&}$"\n'
$
The use of printf is important; it doesn't futz with its data, unlike echo.
The '\'' sequence is crucial; it stops the current single quoted string, outputs a single quote and then restarts the single quoted string. That output is 'sub-optimal'; the initial '' could be left out (and similarly the final '' could be left out if the data ends with a single quote):
$ printf "%s\n" "$x" | sed -e "s/'/'\\\\''/g" -e "s/^/'/" -e "s/$/'/" -e "s/^''//" -e "s/''$//"
\''(]\{&}$"\n'
$
If you really must have double quotes around the data, rather than single quotes, then you have to escape more ($`\" need protection), but the concept is similar.

Appending '-e' to a string in a bash script

I'm trying to write a bash script that iterates over the arguments and builds a string like the following:
Usage:
./myScript a b c d
Expected output:
-e "a" -e "b" -e "c" -e "d"
The script looks like the following:
#!/bin/bash
pattern=""
for arg in "$#" do
pattern=$pattern" -e \"$arg\""
done
echo $pattern
The actual output misses the first -e, i.e., the output is:
"a" -e "b" -e "c" -e "d"
What am I doing wrong? What is the correct way to append -e?
You are doing nothing wrong. It is just that echo takes -e as an argument *.
$ pattern='-e asdf -e ghjk'
$ echo $pattern
asdf -e ghjk
If you quote the variable it works as expected.
$ echo "$pattern"
-e asdf -e ghjk
* man echo
-e enable interpretation of backslash escapes

Substitution with sed + bash function

my question seems to be general, but i can't find any answers.
In sed command, how can you replace the substitution pattern by a value returned by a simple bash function.
For instance, I created the following function :
function parseDates(){
#Some process here with $1 (the pattern found)
return "dateParsed;
}
and the folowing sed command :
myCatFile=`sed -e "s/[0-3][0-9]\/[0-1][0-9]\/[0-9][0-9]/& parseDates &\}/p" myfile`
I found that the caracter '&' represents the current pattern found, i'd like it to be passed to my bash function and the whole pattern to be substituted by the pattern found +dateParsed.
Does anybody have an idea ?
Thanks
you can use the "e" option in sed command like this:
cat t.sh
myecho() {
echo ">>hello,$1<<"
}
export -f myecho
sed -e "s/.*/myecho &/e" <<END
ni
END
you can see the result without "e":
cat t.sh
myecho() {
echo ">>hello,$1<<"
}
export -f myecho
sed -e "s/.*/myecho &/" <<END
ni
END
Agree with Glenn Jackman.
If you want to use bash function in sed, something like this :
sed -rn 's/^([[:digit:].]+)/`date -d #&`/p' file |
while read -r line; do
eval echo "$line"
done
My file here begins with a unix timestamp (e.g. 1362407133.936).
Bash function inside sed (maybe for other purposes):
multi_stdin(){ #Makes function accepet variable or stdin (via pipe)
[[ -n "$1" ]] && echo "$*" || cat -
}
sans_accent(){
multi_stdin "$#" | sed '
y/àáâãäåèéêëìíîïòóôõöùúûü/aaaaaaeeeeiiiiooooouuuu/
y/ÀÁÂÃÄÅÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜ/AAAAAAEEEEIIIIOOOOOUUUU/
y/çÇñÑߢÐð£Øø§µÝý¥¹²³ªº/cCnNBcDdLOoSuYyY123ao/
'
}
eval $(echo "Rogério Madureira" | sed -n 's#.*#echo & | sans_accent#p')
or
eval $(echo "Rogério Madureira" | sed -n 's#.*#sans_accent &#p')
Rogerio
And if you need to keep the output into a variable:
VAR=$( eval $(echo "Rogério Madureira" | sed -n 's#.*#echo & | desacentua#p') )
echo "$VAR"
do it step by step. (also you could use an alternate delimiter , such as "|" instead of "/"
function parseDates(){
#Some process here with $1 (the pattern found)
return "dateParsed;
}
value=$(parseDates)
sed -n "s|[0-3][0-9]/[0-1][0-9]/[0-9][0-9]|& $value &|p" myfile
Note the use of double quotes instead of single quotes, so that $value can be interpolated
I'd like to know if there's a way to do this too. However, for this particular problem you don't need it. If you surround the different components of the date with ()s, you can back reference them with \1 \2 etc and reformat however you want.
For instance, let's reverse 03/04/1973:
echo 03/04/1973 | sed -e 's/\([0-9][0-9]\)\/\([0-9][0-9]\)\/\([0-9][0-9][0-9][0-9]\)/\3\/\2\/\1/g'
sed -e 's#[0-3][0-9]/[0-1][0-9]/[0-9][0-9]#& $(parseDates &)#' myfile |
while read -r line; do
eval echo "$line"
done
You can glue together a sed-command by ending a single-quoted section, and reopening it again.
sed -n 's|[0-3][0-9]/[0-1][0-9]/[0-9][0-9]|& '$(parseDates)' &|p' datefile
However, in contrast to other examples, a function in bash can't return strings, only put them out:
function parseDates(){
# Some process here with $1 (the pattern found)
echo dateParsed
}

Resources