I have this file I want to add a line with and I seem to have a problem when using alt characters.
Here's the script piece:
#testfile.txt
Main
├────►projectA
└────►projectC
sed "/├────►projectA/a ├────►projectB/" testfile.txt
sed doesn't seem to find the "├────►projectA" portion. Grep won't even find it.
grep "├────►projectA" testfile.txt
grep returns nothing.
So how can you make it found so I can add my line below it?
Edit: I found my problem. I was using the wrong character in my sed command. This script is on a different system so I had to make an example off the top of my head.
I'm trying to add spaces as well after the /a but it trims it. Is there a way to preserve the spaces?
(e.g. "[5 spaces] ├────►projectB")
I can't add spaces to the above line because stakoverflow formatting trims it as well. So I say [5 spaces] to represent the amount of whitespace.
It seems to me like you're trying to use sed to append a literal string (i,.e. a string containing any characters) but sed doesn't understand literal strings, only regular expressions and backreference-enabled replacements, see Is it possible to escape regex metacharacters reliably with sed. You should instead be using a tool like awk that understands literal strings.
Is this all you want to do?
$ awk '{print} index($0,"├────►projectA"){print "├────►projectB"}' file
#testfile.txt
Main
├────►projectA
├────►projectB
└────►projectC
If you want 5 leading blanks, just add 5 leading blanks to the string in the print statement:
$ awk '{print} index($0,"├────►projectA"){print " ├────►projectB"}' file
#testfile.txt
Main
├────►projectA
├────►projectB
└────►projectC
If you just want to duplicate whatever indent the preceding line has, here's a modified version of your input file:
$ cat file
#testfile.txt
Main
├────►projectA
└────►projectC
and here's how to print the new line with whatever indent (blanks and/or tabs) the preceding line uses:
$ awk '{print} s=index($0,"├────►projectA"){print substr($0,1,s-1) "├────►projectB"}' file
#testfile.txt
Main
├────►projectA
├────►projectB
└────►projectC
This might work for you (GNU sed):
sed '/^├────►projectA$/{p;s/A/B/}' file
Match on ────►projectA, print the line then substitute B for A.
Use the sed command l0 to see each lines representation in octal and ascii.
Thus this is the same as above:
sed '/^\o342\o224\o234\o342\o224\o200\o342\o224\o200\o342\o224\o200\o342\o224\o200\o342\o226\o272projectA$/{p;s/A/B/}' file
To add 5 spaces to front of such a line, use:
sed '/^├────►projectA$/{p;s/^/ /;s/A/B/}' file
Or you may prefer:
sed '/^\(├────►project\)A$/{p;s/ \1B/}' file
Related
I'm trying to write a script that replaces text in a text file between two patterns "opt/" and "/".
An example of the data in the file is:
daw9udiwa9diuoawdj098awd89a0909w opt/TEXTTOREPLACE/app-data/version.txt
wdalkwhjf8aufwaoif98fawfojaw98f8 opt/TEXTTOREPLACE/app-data/package.txt
awdhaw9d8yawdf8uaw9f8uwafhiuhawf opt/TEXTTOREPLACE/bin/somefile/somefile
wdalkwhjf8aufwaoif98fawfojaw98f8 opt/TEXTTOREPLACE/bin/someapp/somefile
I've looked at using the 'sed' command, but the pattern matching is confusing me.
I have tried:
sed -e 's!\/[^\/]*\/!\/CHANGE TO ME\/!'
This works - but I would like to add in the "opt" at the beginning to minimise errors
So I tried the following with no luck
sed -e 's!opt\/[^opt\/]*\/!opt\/CHANGE TO ME\/!'
I will be using a $VAR to replace text
so for example
VAR=CHANGED
sed -e 's!opt\/[^opt\/]*\/!opt\/$VAR\/!'
output:
daw9udiwa9diuoawdj098awd89a0909w opt/CHANGED/app-data/version.txt
wdalkwhjf8aufwaoif98fawfojaw98f8 opt/CHANGED/app-data/package.txt
awdhaw9d8yawdf8uaw9f8uwafhiuhawf opt/CHANGED/bin/somefile/somefile
wdalkwhjf8aufwaoif98fawfojaw98f8 opt/CHANGED/bin/someapp/somefile
help appreciated.
Thanks
A few issues with the current sed code:
to expand OS variables the sed script must be wrapped in double quotes
the use of ! as a delimiter may cause issues with some shells and/or shell configurations (eg, ! is a common shorthand for accessing the command line history)
escaping the / is only needed when the / also serves as the sed script delimiter (ie, no need to escape / if using a different delimiter)
One sed idea that addresses these issues:
sed -e "s|opt/[^/]*|opt/$VAR|" input.txt
Where:
opt/[^/]* - match on the string opt/ plus all characters that are not a \
opt/$VAR - replace with the string opt/ plus the contents of the OS VAR variable
This generates:
daw9udiwa9diuoawdj098awd89a0909w opt/CHANGED/app-data/version.txt
wdalkwhjf8aufwaoif98fawfojaw98f8 opt/CHANGED/app-data/package.txt
awdhaw9d8yawdf8uaw9f8uwafhiuhawf opt/CHANGED/bin/somefile/somefile
wdalkwhjf8aufwaoif98fawfojaw98f8 opt/CHANGED/bin/someapp/somefile
If you are open to using awk rather than sed, the following may work for you:
$ awk -v rep="CHANGED" -F/ 'BEGIN{OFS="/"} {$2=rep; print}' file1
daw9udiwa9diuoawdj098awd89a0909w opt/CHANGED/app-data/version.txt
wdalkwhjf8aufwaoif98fawfojaw98f8 opt/CHANGED/app-data/package.txt
awdhaw9d8yawdf8uaw9f8uwafhiuhawf opt/CHANGED/bin/somefile/somefile
wdalkwhjf8aufwaoif98fawfojaw98f8 opt/CHANGED/bin/someapp/somefile
Split each line on the forward slash character and replace the second field with your desired replacement text. Then format the output with the output field separator (OFS) set as a forward slash.
I have string like this:
20.07.2010|Berlin|id 100|bd-22.10.94|Marry Scott Robinson|msc#gmail.com
I need to replace whitespaces only between "Marry Scott Robinson" with "|". So to have bd-22.10.94|Marry|Scott|Robinson|
There many of such rows, so problem is in replace whitespace only between "bd-" and vertical line after name.
I'll assume that the name is always on the fifth column :
awk 'BEGIN{FS=OFS="|"}{gsub(/ /,OFS,$5)}1' file
If it is not the case, you can do :
awk 'BEGIN{FS=OFS="|"}{for(i=1;i<=NF;i++){if($i ~ /bd-/){break}};gsub(/ /,OFS,$(i+1))}1' file
Returns :
20.07.2010|Berlin|id 100|bd-22.10.94|Marry|Scott|Robinson|msc#gmail.com
Perl to the rescue!
perl -lne '($before, $change, $after) = /(.*\|bd-.*?\|)(.*?)(\|.*)/;
print $before, $change =~ s/ /|/gr, $after' -- file
-n reads the input line by line, running the code for each line
-l removes newlines from input and adds them to output
the first line populates three variables by values captured from the line. $before contains verything up to the first | after bd-; $change contains what follows up to the next |, and $after contains the rest.
s/ /|/gr replaces spaces by pipes (/g for "all of them") and returns (/r) the result.
This might work for you (GNU sed):
sed 's/[^|]*/\n&\n/5;:a;s/\(\n[^\n ]*\) /\1\|/;ta;s/\n//g' file
Sometimes to fix a problem we must erect scaffolding, then fix the original problem and finally remove the scaffolding.
Here we need to isolate the field by surrounding it by newlines.
Remove the spaces between the newlines by looping until failure.
Finally, remove the scaffolding i.e. the introduced newlines.
Another perl version:
$ perl -F'\|' -ne '$F[4] =~ tr/ /|/; print join("|", #F)' foo.txt
20.07.2010|Berlin|id 100|bd-22.10.94|Marry|Scott|Robinson|msc#gmail.com
Same basic idea as Corentin's first awk example. Split each line into columns based on |, replace spaces in the 5th one with |'s, print the re-joined lines.
I want to use command to edit the specific line of a file instead of using vi. This is the thing. If there is a # starting with the line, then replace the # to make it uncomment. Otherwise, add the # to make it comment. I'd like to use sed or awk. But it won't work as expected.
This is the file.
what are you doing now?
what are you gonna do? stab me?
this is interesting.
This is a test.
go big
don't be rude.
For example, I just want to add the # at the beginning of the the line 4 This is a test if it doesn't start with #. And if it starts with #, then remove the #.
I've already tried via sed & gawk (awk)
gawk -i inplace '$1!="#" {print "#",$0;next};{print substr($0,3,length-1)}' file
sed -i /test/s/^#// file # make it uncomment
sed -i /test/s/^/#/ file # make it comment
I don't know how to use if else to make sed work. I could only make it with a single command, then use another regex to make the opposite.
Using gawk, it works as the main line. But it will mess the rest of the code up.
This might work for you (GNU sed):
sed '4{s/^/#/;s/^##//}' file
On line 4 prepend a # to the line and if there 2 #'s remove them.
Could also be written:
sed '4s/^/#/;4s/^##//' file
This will remove # from the start of line 4 or add it if it wasn't already there:
sed -i '4s/^#/\n/; 4s/^[^\n]/#&/; 4s/^\n//' File
The above assume GNU sed. If you have BSD/MacOS sed, some minor changes will be required.
When sed reads a new line, the one thing that we know for sure about the new line is that it does not contain \n. (If it did, it would be two lines, not one.) Using this knowledge, the script works by:
s/^#/\n/
If the fourth line starts with #, replace # with \n. (The \n serves as a notice that the line had originally been commented out.)
4s/^[^\n]/#&/
If the fourth line now starts with anything other than \n (meaning that it was not originally commented), put a # in front.
4s/^\n//
If the fourth line now starts with \n, remove it.
Alternative: Modifying lines that contain test
To comment/uncomment lines that contain test:
sed '/test/{s/^#/\n/; s/^[^\n]/#&/; s/^\n//}' File
Alternative: using awk
The exact same logic can be applied using awk. If we want to comment/uncomment line 4:
awk 'NR==4 {sub(/^#/, "\n"); sub(/^[^\n]/, "#&"); sub(/^\n/, "")} 1' File
If we want to comment/uncomment any line containing test:
awk '/test/ {sub(/^#/, "\n"); sub(/^[^\n]/, "#&"); sub(/^\n/, "")} 1' File
Alternative: using sed but without newlines
To comment/uncomment any line containing test:
sed '/test/{s/^#//; t; s/^/#/; }' File
How it works:
s/^#//; t
If the line begins with #, then remove it.
t tells sed that, if the substitution succeeded, then it should skip the rest of the commands.
s/^/#/
If we get to this command, that means that the substitution did not succeed (meaning the line was not originally commented out), so we insert #.
If you end up on a system with a sed that doesn't support in-place editing, you can fall back to its uncle ed:
ed -s file 2>/dev/null <<EOF
4 s/^/#/
s/^##//
w
q
EOF
(Standard error is redirected to /dev/null because in ed, unlike sed, it's an error if s doesn't replace anything and a question mark is thus printed to standard error.)
$ awk 'NR==4{$0=(sub(/^#/,"") ? "" : "#") $0} 1' file
what are you doing now?
what are you gonna do? stab me?
this is interesting.
#This is a test.
go big
don't be rude.
$ awk 'NR==4{$0=(sub(/^#/,"") ? "" : "#") $0} 1' file |
awk 'NR==4{$0=(sub(/^#/,"") ? "" : "#") $0} 1'
what are you doing now?
what are you gonna do? stab me?
this is interesting.
This is a test.
go big
don't be rude.
I have a comma delimited file like so
info,someinfo,moreinfo,123,
I want to remove the final comma in each line.
I have tried many variations of:
sed -i 's/.$//' filename
The above command seems to be the prevailing opinion of the internet, but it is not changing my file in any way.
if every line contains , as last letter you can remove like this
str="info,someinfo,moreinfo,123,"
echo ${str::-1}
Output:
info,someinfo,moreinfo,123
Hope this helps you
You may have trailing spaces after the last comma. You could use this command instead so that it handles the optional spaces well:
sed -i -E 's/,[[:space:]]*$//' file
I am not sure if your intention is to remove the last character, regardless of whether it is a comma or not.
If it is a DOS \r\n line terminated file, remove the \r also:
$ cat file
info,someinfo,moreinfo,123,
$ unix2dos file
unix2dos: converting file file to DOS format...
$ sed 's/.\r$//' file
info,someinfo,moreinfo,123
In awk redefining record separator RS to conditionally accept the \r. It will be removed in output:
$ awk 'BEGIN{RS="\r?\n"}{sub(/.$/,"")}1' file
info,someinfo,moreinfo,123
If you are using GNU awk, you can add the \r (conditionally) to the output with ORS=RT; before the print, ie:
$ awk '
BEGIN { RS="\r?\n" } # set the record separator to accept \r if needed
{
ORS=RT # set the used separator as the output separator
sub(/.$/,"") # remove last char
}1' file # output
info,someinfo,moreinfo,123
Can you try this one:
sed 's/\(.\)$//' file_name > filename_output
Just redirecting the output to a new file filename_output.
The command removes the last character regardless of what it is. If you want to remove the last character only if it is a comma, try
sed -i 's/,$//' filename
This will leave the other lines alone, as the regex doesn't match.
(The -i option is not properly portable; on BSD and thus also MacOS, you will need -i ''.)
I have a text file that looks like this:
0,16777215
16807368,16807368
621357328,621357328
621357403,621357403
1380962773,1380962773
1768589474,1768589474
Is there a way to use sed to make sure that each line ONLY has numbers and one comma? If a line is missing the comma, contains letters, is blank, has spaces, etc. - then I want to delete it.
With sed:
sed -nr '/^[0-9]+,[0-9]+$/p' File
To edit the file in-place:
sed -nri '/^[0-9]+,[0-9]+$/p' File
A portable solution:
sed -nE '/^[0-9]+,[0-9]+$/p' File
sed -e '/^[0-9]\+,[0-9]\+$/ !d' file
The address is a regular expression. If the line does not match the regular expression, the d (delete) command is applied. (The exclamation mark (!) inverts the condition.)