I want to do substitution using sed command, but also this should not get applied to specific range of lines matching some pattern/word.
File has below lines:
startline1
top
nice
good
bad
nice
nice
verybad
Extremely bad
bad
nice
endline1
nice
Trail from my end:
sed -i '/startline1/,/endline1/ s/nice/decent/'
The substitute must happen between startline1 and endline1
But must exclude the update between bad and verybad words
With the above command I get result as below:
startline1
top
decent
good
bad
decent
decent
verybad
Extremely bad
bad
decent
endline1
nice
This will do a replacement between only startline1 and endline1, which is as expected.
But now I don't want the update to happen between bad and verybad.
Expected Result:
startline1
top
decent
good
bad
nice
nice
verybad
Extremely bad
bad
decent
endline1
nice
Any suggestion on this would be of help.
Could you please try following based on your shown samples. Tested successfully in link
https://ideone.com/zFl8LK
awk '
/^bad$/{
bad_found=1
}
/^verybad$/{
bad_found=""
}
/endline1/{
found=""
}
/startline1/{
found=1
}
found && !bad_found{
sub(/nice/,"decent")
}
1
' Input_file
Explanation: Firstly checking condition if line starts with bad set bad_found then checking if line starts with verybad then unset bad_found there
checking condition if a line contains endline1 then unset found(found variable work is explained further in explanation). Then checking condition if line contains startline1 then setting variable found here. Then checking condition if found is set AND variable bad_found is NOT SET then substitute nice with decent in that line(where found makes sure that we are boundaries of startling to endline). Finally mentioning 1 will print all lines.
Note: in case your Input_file's lines may have more than 1 occurrence of nice then change sub to gsub in above code.
This might work for you (GNU sed):
sed '/\n/bb;/^start/{:a;N;/^end/M!ba;:b;/^bad.*verybad/h;x;/./{/^verybad/z;s/[^\n]*\n//;x;P;D};x;s/^nice/decent/;P;D}' file
The lines between start and end are gathered up and then processed a line at a time.
If the pattern space contains multiple lines i.e. the lines have already been gathered up, a flag is set in the hold space by copying the current pattern space if the first of these lines contains bad and there follows a line that contains verybad.
If the hold space is not empty and first line is verybad the hold space is emptied, thus flipping the s/nice/decent/ on.
N.B. The solution uses the control flow that the D command resumes processing the current multi line until the pattern space is empty.
If more that one nice occurs on a line use the idiom:
`:c;s/^\([^\n]*\)nice/\1decent/;tc`
Related
I need to indent some math stuff in the \details section of my .Rd documentation to enhance its readability. I am using mathjaxr. Is there any way to indent without installing roxygen2 or similar?
The math stuff is inline, so simply setting to display using \mjdeqn won't solve this.
I seem to have a reasonable "cheating" work around for indenting the first line using mathjaxr, at least for the PDF and HTML output.
We need to do two things:
Use the mathjax/LaTeX phantom command. phantom works by making a box of the size necessary to type-set whatever its argument is, but without actually type-setting anything in the box. For my purposes, if I want to indent, say, about 2 characters wide, I would start the line with a \mjeqn{\phantom{22}}{ } and following with my actual text, possibly including actual mathy bits. If I want an indent of, say, roughly 4 characters wide, I might use \mjeqn{\phantom{2222}}{ }.
Because mathjaxr has a problem with tacking on unsolicited new lines when starting a line with mjeqn, we need to prefix the use of phantom in 1 above with an empty bit of something non-mathjaxr-ish like \emph{}.
Putting it all together, I can indent by about 2 characters using something like this:
\emph{}\mjeqn{\phantom{22}}Here beginneth mine indented line…
I need to explore whether the { } business actually indents for ASCII output, or whether I might accomplish that using or some such.
For example, I have many HTML tabs to style, they use different classes, and will have different backgrounds. Background images files have names corresponding to class names.
The way I found to do it is yank:
.tab.home {
background: ...home.jpg...
}
then paste, then :s/home/about.
This is to be repeated for a few times. I found that & can be used to repeat last substitute, but only for the same target string. What is the quickest way to repeat a substitute with different target string?
Alternatively, probably there are more efficient ways to do such a thing?
I had a quick play with some vim macro magic and came up with the following idea... I apologise for the length. I thought it best to explain the steps..
First, place the text block you want to repeat into a register (I picked register z), so with the cursor at the beginning of the .tab line I pressed "z3Y (select reg z and yank 3 lines).
Then I entered the series of VIM commands I wanted into the buffer as )"zp:.,%s/home/. (Just press i and type the commands)
This translate to;
) go the end of the current '{}' block,
"zp paste a copy of the text in register z,
.,%s/home/ which has two tricks.
The .,% ensures the substitution applies to everything from the start of the .tab to the end of the closing }, and,
The command is incomplete (ie, does not have a at the end), so vim will prompt me to complete the command.
Note that while %s/// will perform a substitution across every line of the file, it is important to realise that % is an alias for range 1,$. Using 1,% as a range, causes the % to be used as the 'jump to matching parenthesis' operator, resulting in a range from the current line to the end of the % match. (which in this example, is the closing brace in the block)
Then, after placing the cursor on the ) at the beginning of the line, I typed "qy$ which means yank all characters to the end of the line into register q.
This is important, because simply yanking the line with Y will include a carriage return in the register, and will cause the macro to fail.
I then executed the content of register q with #q and I was prompted to complete the s/home/ on the command line.
After typing the replacement text and pressing enter, the pasted block (from register z) appeared in the buffer with the substitutions already applied.
At this point you can repeat the last #qby simple typing ##. You don't even need to move the cursor down to the end of the block because the ) at the start of the macro does that for you.
This effectively reduces the process of yanking the original text, inserting it, and executing two manual replace commands into a simple ##.
You can safely delete the macro string from your edit buffer when done.
This is incredibly vim-ish, and might waste a bit of time getting it right, but it could save you even more when you do.
Vim macro's might be the trick you are looking for.
From the manual, I found :s//new-replacement. Seemed to be too much typing.
Looking for a better answer.
I have the following in my .zshrc:
setopt PROMPT_SUBST
precmd(){
echo""
LEFT="$fg[cyan]$USERNAME#$HOST $fg[green]$PWD"
RIGHT="$fg[yellow]$(date +%I:%M\ %P)"
RIGHTWIDTH=$(($COLUMNS-${#LEFT}))
echo $LEFT${(l:$RIGHTWIDTH:)RIGHT}
}
PROMPT="$ "
This gives me the following screenshot
The time part on the right is always not going all the way to the edge of the terminal, even when resized. I think this is due to the $(date +%I:%M\ %P) Anyone know how to fix this?
EDIT: Zoomed in screenshot
While your idea is commendable, the problem you suffer from is that your LEFT and RIGHT contains ANSI escape sequences (for colors), which should be zero-width characters, but are nevertheless counted toward the length of a string if you naively use $#name, or ${(l:expr:)name}.
Also, as a matter of style, you're better off using Zsh's builtin prompt expansion, which wraps a lot of common things people may want to see in their prompts in short percent escape sequences. In particular, there are builtin sequences for colors, so you don't need to rely on nonstandard $fg[blah].
Below is an approximate of your prompt written in my preferred coding style... Not exactly, I made everything super verbose so as to be understandable (hopefully). The lengths of left and right preprompts are calculated after stripping the escape sequences for colors and performing prompt expansion, which gives the correct display length (I can't possibly whip that up in minutes; I ripped the expression off pure).
precmd(){
local preprompt_left="%F{cyan}%n#%m %F{green}%~"
local preprompt_right="%F{yellow}%D{%I:%M %p}%f"
local preprompt_left_length=${#${(S%%)preprompt_left//(\%([KF1]|)\{*\}|\%[Bbkf])}}
local preprompt_right_length=${#${(S%%)preprompt_right//(\%([KF1]|)\{*\}|\%[Bbkf])}}
local num_filler_spaces=$((COLUMNS - preprompt_left_length - preprompt_right_length))
print -Pr $'\n'"$preprompt_left${(l:$num_filler_spaces:)}$preprompt_right"
}
PROMPT="$ "
Edit: In some terminal emulators, printing exactly $COLUMN characters might wrap the line. In that case, replace the appropriate line with
local num_filler_spaces=$((COLUMNS - preprompt_left_length - preprompt_right_length - 1))
End of edit.
This is very customizable, because you can put almost anything in preprompt_left and preprompt_right and still get the correct lengths — just remember to use prompt escape sequence for zero width sequences, e.g., %F{}%f for colors, %B%b for bold, etc. Again, read the docs on prompt expansion: http://zsh.sourceforge.net/Doc/Release/Prompt-Expansion.html.
Note: You might notice that %D{%I:%M %p} expands to things like 11:35 PM. That's because I would like to use %P to get pm, but not every implementation of strftime supports %P. Worst case scenario: if you really want lowercase but %P is not supported, use your original command subsitution $(date +'%I:%M %P').
Also, I'm using %~ instead of %/, so you'll get ~/Desktop instead of /c/Users/johndoe/Desktop. Some like it, some don't. However, as I said, this is easily customizable.
I am trying to learn text processing units in Unix through hackerrank.com's practice problems.
One of the problems is this: https://www.hackerrank.com/challenges/text-processing-cut-4
But I don't understand how to take inputs from standard output in unix, so I'm having trouble submitting my answer. Can you please let me know how to get started with taking inputs? Thanks
In the question we don't have to take input, we have to just display the first four characters from each line of text.
solution:
cut -c1-4
i know that this is an old question, but I'll answer anyway, so everyone can have a response, if they need it.
As you can read on the man page, the synopsis is cut [OPTION]... [FILE]..., so the FILE argument is optional (because it's surrounded by square brackets).
In addition:
With no FILE, or when FILE is -, read standard input.
So if you simply call cut command withoud any file specification, it will read from STDIN.
I am using ed a unix line editor and the book i'm reading says to type 1,$p
(also works in vim)
after trial and error I figured the first value means the line number but whats the purpose to $p? from what i can tell is the 1 goes to the beginning of the line and $p goes to the EOF and displays to me everything it picked up. is this true or am i way off?
The 1,$ part is a range. The comma separates the beginning and end of the range. In this case, 1 (line 1) is the beginning, and $ (EOF) is the end. The p means print, which is the command the range is being given to, and yes.. it displays to you what is in that range.
In vim you can look at :help :range and :help :print to find out more about how this works. These types of ranges are also used by sed and other editors.
They probably used the 1,$ terminology in the tutorial to be explicit, but note that you can also use % as its equivalent. Thus, %p will also print all the lines in the file.