bash: How to add space in string? - string

I have a string like this:
string="aaa-bbb"
But I want to add space before char '-', so I want this:
aaa -bbb
I tried a lot of things, but I can't add space there. I tried with echo $string | tr '-' ' -', and some other stuff, but it didn't work...
I have Linux Mint: GNU bash, version 4.3.8(1)

No need to call sed, use string substitution native in BASH:
$ foo="abc-def-ghi"
$ echo "${foo//-/ -}"
abc -def -ghi
Note the two slashes after the variable name: the first slash replaces the first occurrence, where two slashes replace every occurrence.

Bash has builtin string substitution.
$ string="aaa-bbb"
$ result="${string/-/ -}"
$ echo "$result"
aaa -bbb
Alternatively, you can use sed or perl:
$ string="aaa-bbb"
$ result=$(sed 's/-/ -/' <<< $string)
$ echo "$result"
aaa -bbb
$ result=$(perl -pe 's/-/ -/' <<< $string)
$ echo "$result"
aaa -bbb

Give a try to this:
printf "%s\n" "${string}" | sed 's/-/ -/g'
It looks for - and replace it with - (space hyphen)

You are asking the shell to echo an un-quoted variable $string.
When that happens, spaces inside variables are used to split the string:
$ string="a -b -c"
$ printf '<%s>\n' $string
<a>
<-b>
<-c>
The variable does contain the spaces, just that you are not seeing it correctly.
Quote your expansions
$ printf '<%s>\n' "$string"
<a -b -c>
To get your variable changed from - to - there are many solutions:
sed: string="$(echo "$string" | sed 's/-/ -/g')"; echo "$string"
bash: string="${string//-/ -}; echo "$string"

tr can only substitute one character at a time. what you're looking for is sed:
echo "$string" | sed 's/-/ -/'

Related

Extract substring after a character

I'm trying to extract substring after the last period (dot).
examples below.
echo "filename..txt" should return "txt"
echo "filename.txt." should return ""
echo "filename" should return ""
echo "filename.xml" should return "xml"
I tried below. but works only if the character(dot) exists once. But my filename may have (dot) for 0 or more times.
echo "filename.txt" | cut -d "." -f2
Let's use awk!
awk -F"." '{print (NF>1)? $NF : ""}' file
This sets field separator to . and prints the last one. But if there is none, it prints an empty string.
Test
$ cat file
filename..txt
filename.txt.
filename
filename.xml
$ awk -F"." '{print (NF>1)? $NF : ""}' file
txt
xml
One can make this portable (so it's not Linux-only), avoiding an ERE dependency, with the following:
$ sed -ne 's/.*\.//p' <<< "file..txt"
txt
$ sed -ne 's/.*\.//p' <<< "file.txt."
$ sed -ne 's/.*\.//p' <<< "file"
$ sed -ne 's/.*\.//p' <<< "file.xml"
xml
Note that for testing purposes, I'm using a "here-string" in bash. If your shell is not bash, use whatever your shell uses to feed data to sed.
The important bit here is the use of sed's -n option, which tells it not to print anything by default, combined with the substitute command's explicit p flag, which tells sed to print only upon a successful substitution, which obviously requires a dot to be included in the pattern.
With this solution, the difference between "file.txt." and "file" is that the former returns the input line replaced with null (so you may still get a newline depending on your usage), whereas the latter returns nothing, as sed is not instructed to print, as no . is included in the input. The end result may well be the same, of course:
$ printf "#%s#\n" $(sed -ne 's/.*\.//p' <<< "file.txt.")
##
$ printf "#%s#\n" $(sed -ne 's/.*\.//p' <<< "file")
##
Simple to do with awk:
awk -F"." '{ print $NF }'
What this does: With dot as a delimiter, extract the last field from the input.
Use sed in 2 steps: first remove string without a dot and than remove up to the last dot:
sed -e 's/^[^.]*$//' -e 's/.*\.//'
Test:
for s in file.txt.. file.txt. file.txt filename file.xml; do
echo "$s -> $(echo "$s" | sed -e 's/^[^.]*$//' -e 's/.*\.//')"
done
Testresult:
file.txt.. ->
file.txt. ->
file.txt -> txt
filename ->
file.xml -> xml
Actually the answer of #ghoti is roughly the same, just a bit shorter (better).
This solution can be used by other readers who wants to do something like this in another language.

Best way to swap first 4 chars with last 4 chars of string?

What's the way to swap first 4 chars with last 4 chars of string?
e.g. I have the string 20140613, I'd like to convert that to 06132014.
$ f=20140613
$ g=${f#????}${f%????}
$ echo $g
06132014
For dealing with longer strings something like the following is needed. (With inspiration from konsolebox's answer.)
echo ${f:(-4)}${f:4:${#f} - 8}${f:0:4}
Using pure BASH regex:
s='20140613'
[[ "$s" =~ ^(.*)([[:digit:]]{4})$ ]] && echo "${BASH_REMATCH[2]}${BASH_REMATCH[1]}"
06132014
Simply use substring expansion:
$ STRING=20140613
$ echo "${STRING:(-4)}${STRING:0:4}"
06132014
See Parameter Expansion.
Using date which is optimized for such kind of conversion:
$ str="20140613"
$ date +"%m%d%Y" -d "$str"
06132014
When you have to convert dates, no need to look so far ;)
Using sed:
STRING="20140613"
STRING=$(echo $STRING | sed 's/\(....\)\(.*\)/\2\1/')
Or using awk:
echo 20140613 | awk '{print substr($0,5,7) substr($0,1,4)}'
Test:
~$ echo 20140613 | awk '{print substr($0,5,7) substr($0,1,4)}'
>> 06132014
Through sed,
$ echo 20140613 | sed 's/^\(.\{4\}\)\(.\{4\}\)$/\2\1/g'
06132014
Through perl,
$ echo 20140613 | perl -pe 's/^(.{4})(.{4})$/\2\1/g'
06132014
With GNU Coreutils:
input=20140613
output=$(echo $input | fold -w4 | tac | tr -d \\n)
If you also need the last line feed, you can replace tr -d \\n with printf %s%s\\n or just append && echo to the command.
With perl
for str in 11112222 1111xxxx2222 111222
do
echo -n "$str -> "
echo "$str" | perl -ple 's/^(.{4})(.*)(.{4})$/\3\2\1/'
done
produces:
11112222 -> 22221111
1111xxxx2222 -> 2222xxxx1111
111222 -> 111222

Bash Shell - Return substring after second occurrence of certain character

I need to return everything after a delimeter I decide but still don't fully know how to use sed.
What I need to do is:
$ echo "ABC DE,FG_HI J,123.XYZ-A1,DD/MM/YYYY HH24:MI:SS,,," \
| sed <some regexp>
For this example the return should be (substring)everything after the second comma:
123.XYZ-A1,DD/MM/YYYY HH24:MI:SS,,,
I can do this with cut like this:
echo "ABC DE,FG_HI J,123.XYZ-A1,DD/MM/YYYY HH24:MI:SS,,," | cut -d',' -f 2-
but I've been told cut is slower than sed...
Can some guru who has them (and wants to... :) ) give me a few minutes of his time and advice me please?
Thanks!
Leo
In my experience cut is always faster than sed.
To do what you want with sed you could use a non-matching group:
echo 'ABC DE,FG_HI J,123.XYZ-A1,DD/MM/YYYY HH24:MI:SS,,,' |
sed -r 's/([^,]*,){2}//'
This removes the first two fields (if the fields do not contain commas themselves) by removing non-comma characters [^,] followed by a comma twice {2}.
Output:
123.XYZ-A1,DD/MM/YYYY HH24:MI:SS,,,
You could also try doing the extraction in bash without spawning an external process at all:
$ [[ 'ABC DE,FG_HI J,123.XYZ-A1,DD/MM/YYYY HH24:MI:SS,,,' =~ [^,]*,[^,]*,(.*) ]]
$ echo "${BASH_REMATCH[#]}"
123.XYZ-A1,DD/MM/YYYY HH24:MI:SS,,,
or
$ FOO='ABC DE,FG_HI J,123.XYZ-A1,DD/MM/YYYY HH24:MI:SS,,,'
$ echo ${FOO/+([^,]),+([^,]),}
or
$ IFS=, read -a FOO <<< 'ABC DE,FG_HI J,123.XYZ-A1,DD/MM/YYYY HH24:MI:SS,,,'
$ echo ${FOO[#]:2}
(Assuming this is for a one-off match, not iterating over the contents of a file.)
This method is by find the index of second occurrence of a character and using bash substring to get the required result
input="ABC DE,FG_HI J,123.XYZ-A1,DD/MM/YYYY HH24:MI:SS,,,"
index=$(($(echo $input| grep -aob '/' | grep -oE '[0-9]+' | awk 'NR==2') + 1))
result=${input:$index}

Sed: delete a line in multiline variable

Is there simple equivalent of
sed -i '/str/d' /file
but for multiline variable?
Or I can use only following
var=`echo "$var" | sed "s/str//"`
Use
var=$(echo "$var" | sed '/str/d')
The quotes around $var in the subcommand are important for interpolating the newline characters. Otherwise $var would all be on one line.
You don't need sed, echo, and a pipe just to manipulate a string in bash:
$ echo "$var"
foo
str
bar
$ var="${var//str
}"
$ echo "$var"
foo
bar
man bash.

How to removing leading section in bash

How can I remove parts of a string up to a certain character?
Ex.) If I have the string testFile.txt.1 and testFile.txt.12345 how can I remove the 1 and 12345?
EDIT: I meant to remove and throw away the first part of a string up to a certain character and keep the end of it.
using just bash facilities
$ s=testFile.txt.1
$ echo ${s%.*}
testFile.txt
$ s=testFile.txt.12345
$ echo ${s%.*}
testFile.txt
to remove before leading zero
$ echo ${s#*.}
txt.12345
Other method, you can split your string up using IFS
$ s=testFile.txt.12345
$ IFS="."
$ set -- $s
$ echo $1
testFile
$ echo $2
txt
$ echo $3
12345
If you just want to remove a known suffix from a filename, you can use basename:
basename testFile.txt.1 .1
basename testFile.txt.12345 .12345
You could use sed to do a search and replace. The <<< operator will pass in a string on stdin.
$ sed 's/\.[0-9]*$//' <<< "testFile.txt.1"
testFile.txt
$ sed 's/\.[0-9]*$//' <<< "testFile.txt.12345"
testFile.txt
The question is a bit unclear - the example provided may mean you want to remove all #s, or remove the part after the last ".", or remove the part after the first "1", or even remove all charcters after character 13. Please clarify.
If you mean that you want to remove first N characters in a string (e.g. "up to a character # 13"), do echo testFile.txt.1 | cut -c14-. To retain the chars 1-13, on the other hand, do echo testFile.txt.1 | cut -c1-13
If you mean that you want to remove the beginning characters until the first occurence of a specific character (in your example that seems to be "1"), do echo testFile.txt.1 | perl -e 's/^[^1]*//;'. To remove everything AFTER the first "1", do echo testFile.txt.1 | perl -e 's/1.*$//;'
If you want to remove all the #s, do echo testFile.txt.1 | perl -e 's/\d//g;' or without Perl, echo testFile.txt.1 | tr -d "[0-9]"
If you want to remove everything after the last ".", do echo testFile.txt.1 | perl -e 's/\.[^.]+/./;'
# remove string prefix up to the first digit
var='testFile.txt.12345'
var='test1File.txt.12345'
var='testFile.txt.1'
var='testFile.txt'
echo "${var#"${var%%[[:digit:]]*}"}"

Resources